001/*
002 * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.
008 *
009 * This code is distributed in the hope that it will be useful, but WITHOUT
010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
011 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
012 * version 2 for more details (a copy is included in the LICENSE file that
013 * accompanied this code).
014 *
015 * You should have received a copy of the GNU General Public License version
016 * 2 along with this work; if not, write to the Free Software Foundation,
017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
018 *
019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
020 * or visit www.oracle.com if you need additional information or have any
021 * questions.
022 */
023package com.oracle.graal.truffle;
024
025import java.lang.reflect.*;
026import java.util.*;
027
028import sun.misc.*;
029
030import com.oracle.truffle.api.*;
031import com.oracle.truffle.api.frame.*;
032
033/**
034 * More efficient implementation of the Truffle frame that has no safety checks for frame accesses
035 * and therefore is much faster. Should not be used during debugging as potential misuses of the
036 * frame object would show up very late and would be hard to identify.
037 */
038public final class FrameWithoutBoxing implements VirtualFrame, MaterializedFrame {
039    private final FrameDescriptor descriptor;
040    private final Object[] arguments;
041    private Object[] locals;
042    private long[] primitiveLocals;
043    private byte[] tags;
044    public static final byte OBJECT_TAG = 0;
045    public static final byte ILLEGAL_TAG = 1;
046    public static final byte LONG_TAG = 2;
047    public static final byte INT_TAG = 3;
048    public static final byte DOUBLE_TAG = 4;
049    public static final byte FLOAT_TAG = 5;
050    public static final byte BOOLEAN_TAG = 6;
051    public static final byte BYTE_TAG = 7;
052
053    static {
054        assert OBJECT_TAG == FrameSlotKind.Object.tag;
055        assert ILLEGAL_TAG == FrameSlotKind.Illegal.tag;
056        assert LONG_TAG == FrameSlotKind.Long.tag;
057        assert INT_TAG == FrameSlotKind.Int.tag;
058        assert DOUBLE_TAG == FrameSlotKind.Double.tag;
059        assert FLOAT_TAG == FrameSlotKind.Float.tag;
060        assert BOOLEAN_TAG == FrameSlotKind.Boolean.tag;
061        assert BYTE_TAG == FrameSlotKind.Byte.tag;
062    }
063
064    public FrameWithoutBoxing(FrameDescriptor descriptor, Object[] arguments) {
065        this.descriptor = descriptor;
066        this.arguments = arguments;
067        int size = descriptor.getSize();
068        this.locals = new Object[size];
069        Object defaultValue = descriptor.getDefaultValue();
070        if (defaultValue != null) {
071            Arrays.fill(locals, defaultValue);
072        }
073        this.primitiveLocals = new long[size];
074        this.tags = new byte[size];
075    }
076
077    @Override
078    public Object[] getArguments() {
079        return unsafeCast(arguments, Object[].class, true, true);
080    }
081
082    @Override
083    public MaterializedFrame materialize() {
084        return this;
085    }
086
087    @Override
088    public Object getObject(FrameSlot slot) throws FrameSlotTypeException {
089        int slotIndex = slot.getIndex();
090        verifyGet(slotIndex, OBJECT_TAG);
091        return getObjectUnsafe(slotIndex, slot);
092    }
093
094    private Object[] getLocals() {
095        return unsafeCast(locals, Object[].class, true, true);
096    }
097
098    private long[] getPrimitiveLocals() {
099        return unsafeCast(this.primitiveLocals, long[].class, true, true);
100    }
101
102    private byte[] getTags() {
103        return unsafeCast(tags, byte[].class, true, true);
104    }
105
106    private Object getObjectUnsafe(int slotIndex, FrameSlot slot) {
107        boolean condition = this.getTags()[slotIndex] == OBJECT_TAG;
108        return unsafeGetObject(getLocals(), Unsafe.ARRAY_OBJECT_BASE_OFFSET + slotIndex * (long) Unsafe.ARRAY_OBJECT_INDEX_SCALE, condition, slot);
109    }
110
111    @Override
112    public void setObject(FrameSlot slot, Object value) {
113        int slotIndex = slot.getIndex();
114        verifySet(slotIndex, OBJECT_TAG);
115        setObjectUnsafe(slotIndex, slot, value);
116    }
117
118    private void setObjectUnsafe(int slotIndex, FrameSlot slot, Object value) {
119        unsafePutObject(getLocals(), Unsafe.ARRAY_OBJECT_BASE_OFFSET + slotIndex * (long) Unsafe.ARRAY_OBJECT_INDEX_SCALE, value, slot);
120    }
121
122    @Override
123    public byte getByte(FrameSlot slot) throws FrameSlotTypeException {
124        int slotIndex = slot.getIndex();
125        verifyGet(slotIndex, BYTE_TAG);
126        return getByteUnsafe(slotIndex, slot);
127    }
128
129    private byte getByteUnsafe(int slotIndex, FrameSlot slot) {
130        long offset = getPrimitiveOffset(slotIndex);
131        boolean condition = this.getTags()[slotIndex] == BYTE_TAG;
132        return (byte) unsafeGetInt(getPrimitiveLocals(), offset, condition, slot);
133    }
134
135    @Override
136    public void setByte(FrameSlot slot, byte value) {
137        int slotIndex = slot.getIndex();
138        verifySet(slotIndex, BYTE_TAG);
139        setByteUnsafe(slotIndex, slot, value);
140    }
141
142    private void setByteUnsafe(int slotIndex, FrameSlot slot, byte value) {
143        long offset = getPrimitiveOffset(slotIndex);
144        unsafePutInt(getPrimitiveLocals(), offset, value, slot);
145    }
146
147    @Override
148    public boolean getBoolean(FrameSlot slot) throws FrameSlotTypeException {
149        int slotIndex = slot.getIndex();
150        verifyGet(slotIndex, BOOLEAN_TAG);
151        return getBooleanUnsafe(slotIndex, slot);
152    }
153
154    private boolean getBooleanUnsafe(int slotIndex, FrameSlot slot) {
155        long offset = getPrimitiveOffset(slotIndex);
156        boolean condition = this.getTags()[slotIndex] == BOOLEAN_TAG;
157        return unsafeGetInt(getPrimitiveLocals(), offset, condition, slot) != 0;
158    }
159
160    @Override
161    public void setBoolean(FrameSlot slot, boolean value) {
162        int slotIndex = slot.getIndex();
163        verifySet(slotIndex, BOOLEAN_TAG);
164        setBooleanUnsafe(slotIndex, slot, value);
165    }
166
167    private void setBooleanUnsafe(int slotIndex, FrameSlot slot, boolean value) {
168        long offset = getPrimitiveOffset(slotIndex);
169        unsafePutInt(getPrimitiveLocals(), offset, value ? 1 : 0, slot);
170    }
171
172    @Override
173    public float getFloat(FrameSlot slot) throws FrameSlotTypeException {
174        int slotIndex = slot.getIndex();
175        verifyGet(slotIndex, FLOAT_TAG);
176        return getFloatUnsafe(slotIndex, slot);
177    }
178
179    private float getFloatUnsafe(int slotIndex, FrameSlot slot) {
180        long offset = getPrimitiveOffset(slotIndex);
181        boolean condition = this.getTags()[slotIndex] == FLOAT_TAG;
182        return unsafeGetFloat(getPrimitiveLocals(), offset, condition, slot);
183    }
184
185    @Override
186    public void setFloat(FrameSlot slot, float value) {
187        int slotIndex = slot.getIndex();
188        verifySet(slotIndex, FLOAT_TAG);
189        setFloatUnsafe(slotIndex, slot, value);
190    }
191
192    private void setFloatUnsafe(int slotIndex, FrameSlot slot, float value) {
193        long offset = getPrimitiveOffset(slotIndex);
194        unsafePutFloat(getPrimitiveLocals(), offset, value, slot);
195    }
196
197    @Override
198    public long getLong(FrameSlot slot) throws FrameSlotTypeException {
199        int slotIndex = slot.getIndex();
200        verifyGet(slotIndex, LONG_TAG);
201        return getLongUnsafe(slotIndex, slot);
202    }
203
204    private long getLongUnsafe(int slotIndex, FrameSlot slot) {
205        long offset = getPrimitiveOffset(slotIndex);
206        boolean condition = this.getTags()[slotIndex] == LONG_TAG;
207        return unsafeGetLong(getPrimitiveLocals(), offset, condition, slot);
208    }
209
210    @Override
211    public void setLong(FrameSlot slot, long value) {
212        int slotIndex = slot.getIndex();
213        verifySet(slotIndex, LONG_TAG);
214        setLongUnsafe(slotIndex, slot, value);
215    }
216
217    private void setLongUnsafe(int slotIndex, FrameSlot slot, long value) {
218        long offset = getPrimitiveOffset(slotIndex);
219        unsafePutLong(getPrimitiveLocals(), offset, value, slot);
220    }
221
222    @Override
223    public int getInt(FrameSlot slot) throws FrameSlotTypeException {
224        int slotIndex = slot.getIndex();
225        verifyGet(slotIndex, INT_TAG);
226        return getIntUnsafe(slotIndex, slot);
227    }
228
229    private int getIntUnsafe(int slotIndex, FrameSlot slot) {
230        long offset = getPrimitiveOffset(slotIndex);
231        boolean condition = this.getTags()[slot.getIndex()] == INT_TAG;
232        return unsafeGetInt(getPrimitiveLocals(), offset, condition, slot);
233    }
234
235    @Override
236    public void setInt(FrameSlot slot, int value) {
237        int slotIndex = slot.getIndex();
238        verifySet(slotIndex, INT_TAG);
239        setIntUnsafe(slotIndex, slot, value);
240    }
241
242    private void setIntUnsafe(int slotIndex, FrameSlot slot, int value) {
243        long offset = getPrimitiveOffset(slotIndex);
244        unsafePutInt(getPrimitiveLocals(), offset, value, slot);
245    }
246
247    @Override
248    public double getDouble(FrameSlot slot) throws FrameSlotTypeException {
249        int slotIndex = slot.getIndex();
250        verifyGet(slotIndex, DOUBLE_TAG);
251        return getDoubleUnsafe(slotIndex, slot);
252    }
253
254    private double getDoubleUnsafe(int slotIndex, FrameSlot slot) {
255        long offset = getPrimitiveOffset(slotIndex);
256        boolean condition = this.getTags()[slotIndex] == DOUBLE_TAG;
257        return unsafeGetDouble(getPrimitiveLocals(), offset, condition, slot);
258    }
259
260    @Override
261    public void setDouble(FrameSlot slot, double value) {
262        int slotIndex = slot.getIndex();
263        verifySet(slotIndex, DOUBLE_TAG);
264        setDoubleUnsafe(slotIndex, slot, value);
265    }
266
267    private void setDoubleUnsafe(int slotIndex, FrameSlot slot, double value) {
268        long offset = getPrimitiveOffset(slotIndex);
269        unsafePutDouble(getPrimitiveLocals(), offset, value, slot);
270    }
271
272    @Override
273    public FrameDescriptor getFrameDescriptor() {
274        return this.descriptor;
275    }
276
277    private void verifySet(int slotIndex, byte tag) {
278        checkSlotIndex(slotIndex);
279        getTags()[slotIndex] = tag;
280    }
281
282    private void verifyGet(int slotIndex, byte tag) throws FrameSlotTypeException {
283        checkSlotIndex(slotIndex);
284        if (getTags()[slotIndex] != tag) {
285            CompilerDirectives.transferToInterpreter();
286            throw new FrameSlotTypeException();
287        }
288    }
289
290    private void checkSlotIndex(int slotIndex) {
291        if (CompilerDirectives.inInterpreter() && slotIndex >= getTags().length) {
292            if (!resize()) {
293                throw new IllegalArgumentException(String.format("The frame slot '%s' is not known by the frame descriptor.", slotIndex));
294            }
295        }
296    }
297
298    private static long getPrimitiveOffset(int slotIndex) {
299        return Unsafe.ARRAY_LONG_BASE_OFFSET + slotIndex * (long) Unsafe.ARRAY_LONG_INDEX_SCALE;
300    }
301
302    @Override
303    public Object getValue(FrameSlot slot) {
304        int slotIndex = slot.getIndex();
305        if (CompilerDirectives.inInterpreter() && slotIndex >= getTags().length) {
306            CompilerDirectives.transferToInterpreter();
307            resize();
308        }
309        byte tag = getTags()[slotIndex];
310        if (tag == BOOLEAN_TAG) {
311            return getBooleanUnsafe(slotIndex, slot);
312        } else if (tag == BYTE_TAG) {
313            return getByteUnsafe(slotIndex, slot);
314        } else if (tag == INT_TAG) {
315            return getIntUnsafe(slotIndex, slot);
316        } else if (tag == DOUBLE_TAG) {
317            return getDoubleUnsafe(slotIndex, slot);
318        } else if (tag == LONG_TAG) {
319            return getLongUnsafe(slotIndex, slot);
320        } else if (tag == FLOAT_TAG) {
321            return getFloatUnsafe(slotIndex, slot);
322        } else {
323            assert tag == OBJECT_TAG;
324            return getObjectUnsafe(slotIndex, slot);
325        }
326    }
327
328    private boolean resize() {
329        int oldSize = tags.length;
330        int newSize = descriptor.getSize();
331        if (newSize > oldSize) {
332            locals = Arrays.copyOf(locals, newSize);
333            Arrays.fill(locals, oldSize, newSize, descriptor.getDefaultValue());
334            primitiveLocals = Arrays.copyOf(primitiveLocals, newSize);
335            tags = Arrays.copyOf(tags, newSize);
336            return true;
337        }
338        return false;
339    }
340
341    private byte getTag(FrameSlot slot) {
342        int slotIndex = slot.getIndex();
343        if (slotIndex >= getTags().length) {
344            CompilerDirectives.transferToInterpreter();
345            resize();
346        }
347        return getTags()[slotIndex];
348    }
349
350    @Override
351    public boolean isObject(FrameSlot slot) {
352        return getTag(slot) == OBJECT_TAG;
353    }
354
355    @Override
356    public boolean isByte(FrameSlot slot) {
357        return getTag(slot) == BYTE_TAG;
358    }
359
360    @Override
361    public boolean isBoolean(FrameSlot slot) {
362        return getTag(slot) == BOOLEAN_TAG;
363    }
364
365    @Override
366    public boolean isInt(FrameSlot slot) {
367        return getTag(slot) == INT_TAG;
368    }
369
370    @Override
371    public boolean isLong(FrameSlot slot) {
372        return getTag(slot) == LONG_TAG;
373    }
374
375    @Override
376    public boolean isFloat(FrameSlot slot) {
377        return getTag(slot) == FLOAT_TAG;
378    }
379
380    @Override
381    public boolean isDouble(FrameSlot slot) {
382        return getTag(slot) == DOUBLE_TAG;
383    }
384
385    @SuppressWarnings({"unchecked", "unused"})
386    static <T> T unsafeCast(Object value, Class<T> type, boolean condition, boolean nonNull) {
387        return (T) value;
388    }
389
390    @SuppressWarnings("unused")
391    static int unsafeGetInt(Object receiver, long offset, boolean condition, Object locationIdentity) {
392        return UNSAFE.getInt(receiver, offset);
393    }
394
395    @SuppressWarnings("unused")
396    static long unsafeGetLong(Object receiver, long offset, boolean condition, Object locationIdentity) {
397        return UNSAFE.getLong(receiver, offset);
398    }
399
400    @SuppressWarnings("unused")
401    static float unsafeGetFloat(Object receiver, long offset, boolean condition, Object locationIdentity) {
402        return UNSAFE.getFloat(receiver, offset);
403    }
404
405    @SuppressWarnings("unused")
406    static double unsafeGetDouble(Object receiver, long offset, boolean condition, Object locationIdentity) {
407        return UNSAFE.getDouble(receiver, offset);
408    }
409
410    @SuppressWarnings("unused")
411    static Object unsafeGetObject(Object receiver, long offset, boolean condition, Object locationIdentity) {
412        return UNSAFE.getObject(receiver, offset);
413    }
414
415    @SuppressWarnings("unused")
416    static void unsafePutInt(Object receiver, long offset, int value, Object locationIdentity) {
417        UNSAFE.putInt(receiver, offset, value);
418    }
419
420    @SuppressWarnings("unused")
421    static void unsafePutLong(Object receiver, long offset, long value, Object locationIdentity) {
422        UNSAFE.putLong(receiver, offset, value);
423    }
424
425    @SuppressWarnings("unused")
426    static void unsafePutFloat(Object receiver, long offset, float value, Object locationIdentity) {
427        UNSAFE.putFloat(receiver, offset, value);
428    }
429
430    @SuppressWarnings("unused")
431    static void unsafePutDouble(Object receiver, long offset, double value, Object locationIdentity) {
432        UNSAFE.putDouble(receiver, offset, value);
433    }
434
435    @SuppressWarnings("unused")
436    static void unsafePutObject(Object receiver, long offset, Object value, Object locationIdentity) {
437        UNSAFE.putObject(receiver, offset, value);
438    }
439
440    private static final Unsafe UNSAFE = getUnsafe();
441
442    private static Unsafe getUnsafe() {
443        try {
444            return Unsafe.getUnsafe();
445        } catch (SecurityException e) {
446        }
447        try {
448            Field theUnsafeInstance = Unsafe.class.getDeclaredField("theUnsafe");
449            theUnsafeInstance.setAccessible(true);
450            return (Unsafe) theUnsafeInstance.get(Unsafe.class);
451        } catch (Exception e) {
452            throw new RuntimeException("exception while trying to get Unsafe.theUnsafe via reflection:", e);
453        }
454    }
455}