1/*
2 * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23package org.graalvm.compiler.core.common.type;
24
25import org.graalvm.compiler.debug.GraalError;
26
27import jdk.vm.ci.code.CodeUtil;
28import jdk.vm.ci.meta.Assumptions;
29import jdk.vm.ci.meta.JavaConstant;
30import jdk.vm.ci.meta.JavaKind;
31import jdk.vm.ci.meta.JavaType;
32import jdk.vm.ci.meta.MetaAccessProvider;
33import jdk.vm.ci.meta.ResolvedJavaMethod;
34import jdk.vm.ci.meta.ResolvedJavaType;
35import jdk.vm.ci.meta.Signature;
36
37public class StampFactory {
38
39    /*
40     * The marker stamp for node intrinsics must be its own class, so that it is never equal() to a
41     * regular ObjectStamp.
42     */
43    static final class NodeIntrinsicStamp extends ObjectStamp {
44        protected static final Stamp SINGLETON = new NodeIntrinsicStamp();
45
46        private NodeIntrinsicStamp() {
47            super(null, false, false, false);
48        }
49
50        @Override
51        public int hashCode() {
52            return System.identityHashCode(this);
53        }
54
55        @Override
56        public boolean equals(Object obj) {
57            return this == obj;
58        }
59    }
60
61    // JaCoCo Exclude
62
63    private static final Stamp[] stampCache = new Stamp[JavaKind.values().length];
64    private static final Stamp[] emptyStampCache = new Stamp[JavaKind.values().length];
65    private static final Stamp objectStamp = new ObjectStamp(null, false, false, false);
66    private static final Stamp objectNonNullStamp = new ObjectStamp(null, false, true, false);
67    private static final Stamp objectAlwaysNullStamp = new ObjectStamp(null, false, false, true);
68    private static final Stamp positiveInt = forInteger(JavaKind.Int, 0, Integer.MAX_VALUE, 0, Integer.MAX_VALUE);
69    private static final Stamp booleanTrue = forInteger(JavaKind.Boolean, -1, -1, 1, 1);
70    private static final Stamp booleanFalse = forInteger(JavaKind.Boolean, 0, 0, 0, 0);
71    private static final Stamp rawPointer = new RawPointerStamp();
72
73    private static void setCache(JavaKind kind, Stamp stamp) {
74        stampCache[kind.ordinal()] = stamp;
75    }
76
77    private static void setIntCache(JavaKind kind) {
78        int bits = kind.getStackKind().getBitCount();
79        long mask;
80        if (kind.isUnsigned()) {
81            mask = CodeUtil.mask(kind.getBitCount());
82        } else {
83            mask = CodeUtil.mask(bits);
84        }
85        setCache(kind, new IntegerStamp(bits, kind.getMinValue(), kind.getMaxValue(), 0, mask));
86    }
87
88    private static void setFloatCache(JavaKind kind) {
89        setCache(kind, new FloatStamp(kind.getBitCount()));
90    }
91
92    static {
93        setIntCache(JavaKind.Boolean);
94        setIntCache(JavaKind.Byte);
95        setIntCache(JavaKind.Short);
96        setIntCache(JavaKind.Char);
97        setIntCache(JavaKind.Int);
98        setIntCache(JavaKind.Long);
99
100        setFloatCache(JavaKind.Float);
101        setFloatCache(JavaKind.Double);
102
103        setCache(JavaKind.Object, objectStamp);
104        setCache(JavaKind.Void, VoidStamp.getInstance());
105        setCache(JavaKind.Illegal, IllegalStamp.getInstance());
106
107        for (JavaKind k : JavaKind.values()) {
108            if (stampCache[k.ordinal()] != null) {
109                emptyStampCache[k.ordinal()] = stampCache[k.ordinal()].empty();
110            }
111        }
112    }
113
114    public static Stamp tautology() {
115        return booleanTrue;
116    }
117
118    public static Stamp contradiction() {
119        return booleanFalse;
120    }
121
122    /**
123     * Return a stamp for a Java kind, as it would be represented on the bytecode stack.
124     */
125    public static Stamp forKind(JavaKind kind) {
126        assert stampCache[kind.ordinal()] != null : "unexpected forKind(" + kind + ")";
127        return stampCache[kind.ordinal()];
128    }
129
130    /**
131     * Return the stamp for the {@code void} type. This will return a singleton instance than can be
132     * compared using {@code ==}.
133     */
134    public static Stamp forVoid() {
135        return VoidStamp.getInstance();
136    }
137
138    /**
139     * A stamp used only in the graph of intrinsics, e.g., snippets. It is then replaced by an
140     * actual stamp when the intrinsic is used, i.e., when the snippet template is instantiated.
141     */
142    public static Stamp forNodeIntrinsic() {
143        return NodeIntrinsicStamp.SINGLETON;
144    }
145
146    public static Stamp intValue() {
147        return forKind(JavaKind.Int);
148    }
149
150    public static Stamp positiveInt() {
151        return positiveInt;
152    }
153
154    public static Stamp empty(JavaKind kind) {
155        return emptyStampCache[kind.ordinal()];
156    }
157
158    public static IntegerStamp forInteger(JavaKind kind, long lowerBound, long upperBound, long downMask, long upMask) {
159        return new IntegerStamp(kind.getBitCount(), lowerBound, upperBound, downMask, upMask);
160    }
161
162    public static IntegerStamp forInteger(JavaKind kind, long lowerBound, long upperBound) {
163        return forInteger(kind.getBitCount(), lowerBound, upperBound);
164    }
165
166    /**
167     * Create a new stamp use {@code newLowerBound} and {@code newUpperBound} computing the
168     * appropriate {@link IntegerStamp#upMask} and {@link IntegerStamp#downMask} and incorporating
169     * any mask information from {@code maskStamp}.
170     *
171     * @param bits
172     * @param newLowerBound
173     * @param newUpperBound
174     * @param maskStamp
175     * @return a new stamp with the appropriate bounds and masks
176     */
177    public static IntegerStamp forIntegerWithMask(int bits, long newLowerBound, long newUpperBound, IntegerStamp maskStamp) {
178        IntegerStamp limit = StampFactory.forInteger(bits, newLowerBound, newUpperBound);
179        return new IntegerStamp(bits, newLowerBound, newUpperBound, limit.downMask() | maskStamp.downMask(), limit.upMask() & maskStamp.upMask());
180    }
181
182    public static IntegerStamp forIntegerWithMask(int bits, long newLowerBound, long newUpperBound, long newDownMask, long newUpMask) {
183        IntegerStamp limit = StampFactory.forInteger(bits, newLowerBound, newUpperBound);
184        return new IntegerStamp(bits, newLowerBound, newUpperBound, limit.downMask() | newDownMask, limit.upMask() & newUpMask);
185    }
186
187    public static IntegerStamp forInteger(int bits) {
188        return new IntegerStamp(bits, CodeUtil.minValue(bits), CodeUtil.maxValue(bits), 0, CodeUtil.mask(bits));
189    }
190
191    public static IntegerStamp forInteger(int bits, long lowerBound, long upperBound) {
192        long defaultMask = CodeUtil.mask(bits);
193        if (lowerBound == upperBound) {
194            return new IntegerStamp(bits, lowerBound, lowerBound, lowerBound & defaultMask, lowerBound & defaultMask);
195        }
196        final long downMask;
197        final long upMask;
198        if (lowerBound >= 0) {
199            int upperBoundLeadingZeros = Long.numberOfLeadingZeros(upperBound);
200            long differentBits = lowerBound ^ upperBound;
201            int sameBitCount = Long.numberOfLeadingZeros(differentBits << upperBoundLeadingZeros);
202
203            upMask = upperBound | -1L >>> (upperBoundLeadingZeros + sameBitCount);
204            downMask = upperBound & ~(-1L >>> (upperBoundLeadingZeros + sameBitCount));
205        } else {
206            if (upperBound >= 0) {
207                upMask = defaultMask;
208                downMask = 0;
209            } else {
210                int lowerBoundLeadingOnes = Long.numberOfLeadingZeros(~lowerBound);
211                long differentBits = lowerBound ^ upperBound;
212                int sameBitCount = Long.numberOfLeadingZeros(differentBits << lowerBoundLeadingOnes);
213
214                upMask = lowerBound | -1L >>> (lowerBoundLeadingOnes + sameBitCount) | ~(-1L >>> lowerBoundLeadingOnes);
215                downMask = lowerBound & ~(-1L >>> (lowerBoundLeadingOnes + sameBitCount)) | ~(-1L >>> lowerBoundLeadingOnes);
216            }
217        }
218        return new IntegerStamp(bits, lowerBound, upperBound, downMask & defaultMask, upMask & defaultMask);
219    }
220
221    public static FloatStamp forFloat(JavaKind kind, double lowerBound, double upperBound, boolean nonNaN) {
222        assert kind.isNumericFloat();
223        return new FloatStamp(kind.getBitCount(), lowerBound, upperBound, nonNaN);
224    }
225
226    public static Stamp forConstant(JavaConstant value) {
227        JavaKind kind = value.getJavaKind();
228        switch (kind) {
229            case Boolean:
230            case Byte:
231            case Char:
232            case Short:
233            case Int:
234            case Long:
235                long mask = value.asLong() & CodeUtil.mask(kind.getBitCount());
236                return forInteger(kind.getStackKind(), value.asLong(), value.asLong(), mask, mask);
237            case Float:
238                return forFloat(kind, value.asFloat(), value.asFloat(), !Float.isNaN(value.asFloat()));
239            case Double:
240                return forFloat(kind, value.asDouble(), value.asDouble(), !Double.isNaN(value.asDouble()));
241            case Illegal:
242                return forKind(JavaKind.Illegal);
243            case Object:
244                if (value.isNull()) {
245                    return alwaysNull();
246                } else {
247                    return objectNonNull();
248                }
249            default:
250                throw new GraalError("unexpected kind: %s", kind);
251        }
252    }
253
254    public static Stamp forConstant(JavaConstant value, MetaAccessProvider metaAccess) {
255        if (value.getJavaKind() == JavaKind.Object) {
256            ResolvedJavaType type = value.isNull() ? null : metaAccess.lookupJavaType(value);
257            return new ObjectStamp(type, value.isNonNull(), value.isNonNull(), value.isNull());
258        } else {
259            return forConstant(value);
260        }
261    }
262
263    public static Stamp object() {
264        return objectStamp;
265    }
266
267    public static Stamp objectNonNull() {
268        return objectNonNullStamp;
269    }
270
271    public static Stamp alwaysNull() {
272        return objectAlwaysNullStamp;
273    }
274
275    public static ObjectStamp object(TypeReference type) {
276        return object(type, false);
277    }
278
279    public static ObjectStamp objectNonNull(TypeReference type) {
280        return object(type, true);
281    }
282
283    public static ObjectStamp object(TypeReference type, boolean nonNull) {
284        if (type == null) {
285            return new ObjectStamp(null, false, nonNull, false);
286        } else {
287            return new ObjectStamp(type.getType(), type.isExact(), nonNull, false);
288        }
289    }
290
291    public static Stamp[] createParameterStamps(Assumptions assumptions, ResolvedJavaMethod method) {
292        Signature sig = method.getSignature();
293        Stamp[] result = new Stamp[sig.getParameterCount(!method.isStatic())];
294        int index = 0;
295
296        if (!method.isStatic()) {
297            result[index++] = StampFactory.objectNonNull(TypeReference.create(assumptions, method.getDeclaringClass()));
298        }
299
300        int max = sig.getParameterCount(false);
301        ResolvedJavaType accessingClass = method.getDeclaringClass();
302        for (int i = 0; i < max; i++) {
303            JavaType type = sig.getParameterType(i, accessingClass);
304            JavaKind kind = type.getJavaKind();
305            Stamp stamp;
306            if (kind == JavaKind.Object && type instanceof ResolvedJavaType) {
307                stamp = StampFactory.object(TypeReference.create(assumptions, (ResolvedJavaType) type));
308            } else {
309                stamp = StampFactory.forKind(kind);
310            }
311            result[index++] = stamp;
312        }
313
314        return result;
315    }
316
317    public static Stamp pointer() {
318        return rawPointer;
319    }
320
321    public static StampPair forDeclaredType(Assumptions assumptions, JavaType returnType, boolean nonNull) {
322        if (returnType.getJavaKind() == JavaKind.Object && returnType instanceof ResolvedJavaType) {
323            ResolvedJavaType resolvedJavaType = (ResolvedJavaType) returnType;
324            TypeReference reference = TypeReference.create(assumptions, resolvedJavaType);
325            if (resolvedJavaType.isInterface()) {
326                ResolvedJavaType implementor = resolvedJavaType.getSingleImplementor();
327                if (implementor != null && !resolvedJavaType.equals(implementor)) {
328                    TypeReference uncheckedType = TypeReference.createTrusted(assumptions, implementor);
329                    return StampPair.create(StampFactory.object(reference, nonNull), StampFactory.object(uncheckedType, nonNull));
330                }
331            }
332            return StampPair.createSingle(StampFactory.object(reference, nonNull));
333        } else {
334            return StampPair.createSingle(StampFactory.forKind(returnType.getJavaKind()));
335        }
336    }
337}
338