Skip to content

Commit 4d22d33

Browse files
committed
Instruction encoding overhaul: store bytecode in byte array; support byte/short/int width immediates
1 parent 1b735a4 commit 4d22d33

File tree

14 files changed

+1130
-534
lines changed

14 files changed

+1130
-534
lines changed

truffle/src/com.oracle.truffle.api.benchmark/src/com/oracle/truffle/api/benchmark/bytecode/ManualBytecodeInterpreter.java

Lines changed: 37 additions & 37 deletions
Large diffs are not rendered by default.

truffle/src/com.oracle.truffle.api.benchmark/src/com/oracle/truffle/api/benchmark/bytecode/SimpleBytecodeBenchmark.java

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,15 @@ public class SimpleBytecodeBenchmark extends TruffleBenchmark {
299299

300300
private Context context;
301301

302+
private static byte[] toByteArray(short[] arr) {
303+
byte[] result = new byte[arr.length * 2];
304+
for (int i = 0; i < arr.length; i++) {
305+
result[i * 2] = (byte) (arr[i] >>> 8);
306+
result[i * 2 + 1] = (byte) arr[i];
307+
}
308+
return result;
309+
}
310+
302311
/**
303312
* The code is equivalent to:
304313
*
@@ -351,31 +360,31 @@ public class SimpleBytecodeBenchmark extends TruffleBenchmark {
351360
BenchmarkLanguage.registerName(NAME_MANUAL_BCI, lang -> {
352361
FrameDescriptor.Builder b = FrameDescriptor.newBuilder(3);
353362
b.addSlots(8, FrameSlotKind.Illegal);
354-
ManualBytecodeInterpreter node = new ManualBytecodeInterpreter(lang, b.build(), BYTECODE);
363+
ManualBytecodeInterpreter node = new ManualBytecodeInterpreter(lang, b.build(), toByteArray(BYTECODE));
355364
return node.getCallTarget();
356365
});
357366
BenchmarkLanguage.registerName(NAME_MANUAL_BCI_NO_BE, lang -> {
358367
FrameDescriptor.Builder b = FrameDescriptor.newBuilder(3);
359368
b.addSlots(8, FrameSlotKind.Illegal);
360-
ManualBytecodeInterpreterWithoutBE node = new ManualBytecodeInterpreterWithoutBE(lang, b.build(), BYTECODE);
369+
ManualBytecodeInterpreterWithoutBE node = new ManualBytecodeInterpreterWithoutBE(lang, b.build(), toByteArray(BYTECODE));
361370
return node.getCallTarget();
362371
});
363372
BenchmarkLanguage.registerName(NAME_MANUAL_BCI_UNSAFE, lang -> {
364373
FrameDescriptor.Builder b = FrameDescriptor.newBuilder(3);
365374
b.addSlots(8, FrameSlotKind.Illegal);
366-
ManualUnsafeBytecodeInterpreter node = new ManualUnsafeBytecodeInterpreter(lang, b.build(), BYTECODE);
375+
ManualUnsafeBytecodeInterpreter node = new ManualUnsafeBytecodeInterpreter(lang, b.build(), toByteArray(BYTECODE));
367376
return node.getCallTarget();
368377
});
369378
BenchmarkLanguage.registerName(NAME_MANUAL_NODED_UNSAFE, lang -> {
370379
FrameDescriptor.Builder b = FrameDescriptor.newBuilder(3);
371380
b.addSlots(8, FrameSlotKind.Illegal);
372-
ManualUnsafeNodedInterpreter node = new ManualUnsafeNodedInterpreter(lang, b.build(), BC_SHORT, OBJ_SHORT, NODE_SHORT);
381+
ManualUnsafeNodedInterpreter node = new ManualUnsafeNodedInterpreter(lang, b.build(), toByteArray(BC_SHORT), OBJ_SHORT, NODE_SHORT);
373382
return node.getCallTarget();
374383
});
375384
BenchmarkLanguage.registerName(NAME_MANUAL_NODED_UNSAFE_NO_BE, lang -> {
376385
FrameDescriptor.Builder b = FrameDescriptor.newBuilder(3);
377386
b.addSlots(8, FrameSlotKind.Illegal);
378-
ManualUnsafeNodedInterpreterWithoutBE node = new ManualUnsafeNodedInterpreterWithoutBE(lang, b.build(), BC_SHORT, OBJ_SHORT, NODE_SHORT);
387+
ManualUnsafeNodedInterpreterWithoutBE node = new ManualUnsafeNodedInterpreterWithoutBE(lang, b.build(), toByteArray(BC_SHORT), OBJ_SHORT, NODE_SHORT);
379388
return node.getCallTarget();
380389
});
381390
BenchmarkLanguage.registerName(NAME_AST, lang -> {
Lines changed: 273 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* The Universal Permissive License (UPL), Version 1.0
6+
*
7+
* Subject to the condition set forth below, permission is hereby granted to any
8+
* person obtaining a copy of this software, associated documentation and/or
9+
* data (collectively the "Software"), free of charge and under any and all
10+
* copyright rights in the Software, and any and all patent rights owned or
11+
* freely licensable by each licensor hereunder covering either (i) the
12+
* unmodified Software as contributed to or provided by such licensor, or (ii)
13+
* the Larger Works (as defined below), to deal in both
14+
*
15+
* (a) the Software, and
16+
*
17+
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
18+
* one is included with the Software each a "Larger Work" to which the Software
19+
* is contributed by such licensors),
20+
*
21+
* without restriction, including without limitation the rights to copy, create
22+
* derivative works of, display, perform, and distribute the Software and make,
23+
* use, sell, offer for sale, import, export, have made, and have sold the
24+
* Software and the Larger Work(s), and to sublicense the foregoing rights on
25+
* either these or other terms.
26+
*
27+
* This license is subject to the following condition:
28+
*
29+
* The above copyright notice and either this complete permission notice or at a
30+
* minimum a reference to the UPL must be included in all copies or substantial
31+
* portions of the Software.
32+
*
33+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
39+
* SOFTWARE.
40+
*/
41+
package com.oracle.truffle.api.bytecode.test;
42+
43+
import static org.junit.Assert.assertEquals;
44+
import static org.junit.Assert.assertThrows;
45+
import static org.junit.Assert.assertTrue;
46+
import static org.junit.Assert.fail;
47+
import static org.junit.Assume.assumeFalse;
48+
49+
import java.util.List;
50+
51+
import org.junit.Before;
52+
import org.junit.Test;
53+
import org.junit.runner.RunWith;
54+
import org.junit.runners.Parameterized;
55+
import org.junit.runners.Parameterized.Parameter;
56+
import org.junit.runners.Parameterized.Parameters;
57+
58+
import com.oracle.truffle.api.bytecode.BytecodeBuilder;
59+
import com.oracle.truffle.api.bytecode.BytecodeDSLAccess;
60+
import com.oracle.truffle.api.bytecode.BytecodeParser;
61+
import com.oracle.truffle.api.bytecode.BytecodeRootNode;
62+
import com.oracle.truffle.api.bytecode.BytecodeRootNodes;
63+
import com.oracle.truffle.api.nodes.RootNode;
64+
65+
@RunWith(Parameterized.class)
66+
public class BytecodeDSLAccessTest {
67+
@Parameters(name = "{0}")
68+
public static List<Boolean> getParameters() {
69+
return List.of(false, true);
70+
}
71+
72+
@Parameter(0) public boolean isUnsafe;
73+
74+
public BytecodeDSLAccess access;
75+
76+
@Before
77+
public void setUp() {
78+
access = BytecodeDSLAccess.lookup(AccessToken.PUBLIC_TOKEN, isUnsafe);
79+
}
80+
81+
private static final byte[] BYTE_INPUTS = new byte[]{0, -1, 1, Byte.MAX_VALUE, Byte.MIN_VALUE};
82+
private static final short[] SHORT_INPUTS = new short[]{0, -1, 1, Short.MAX_VALUE, Short.MIN_VALUE};
83+
84+
private static final int[] INT_INPUTS = new int[]{0, -1, 1, Integer.MAX_VALUE, Integer.MIN_VALUE};
85+
86+
@Test
87+
public void testByte() {
88+
byte[] arr = new byte[BYTE_INPUTS.length];
89+
for (int i = 0; i < BYTE_INPUTS.length; i++) {
90+
access.writeByte(arr, i, BYTE_INPUTS[i]);
91+
}
92+
for (int i = 0; i < BYTE_INPUTS.length; i++) {
93+
assertEquals(BYTE_INPUTS[i], access.readByte(arr, i));
94+
}
95+
96+
if (!isUnsafe) {
97+
assertThrows(ArrayIndexOutOfBoundsException.class, () -> access.writeByte(arr, -1, (byte) 42));
98+
assertThrows(ArrayIndexOutOfBoundsException.class, () -> access.writeByte(arr, arr.length, (byte) 42));
99+
assertThrows(ArrayIndexOutOfBoundsException.class, () -> access.readByte(arr, -1));
100+
assertThrows(ArrayIndexOutOfBoundsException.class, () -> access.readByte(arr, arr.length));
101+
}
102+
}
103+
104+
@Test
105+
public void testShort() {
106+
byte[] arr = new byte[SHORT_INPUTS.length * 2];
107+
for (int i = 0; i < SHORT_INPUTS.length; i++) {
108+
access.writeShort(arr, i * 2, SHORT_INPUTS[i]);
109+
}
110+
for (int i = 0; i < SHORT_INPUTS.length; i++) {
111+
assertEquals(SHORT_INPUTS[i], access.readShort(arr, i * 2));
112+
}
113+
114+
if (!isUnsafe) {
115+
assertThrows(ArrayIndexOutOfBoundsException.class, () -> access.writeShort(arr, -1, (short) 42));
116+
assertThrows(ArrayIndexOutOfBoundsException.class, () -> access.writeShort(arr, arr.length, (short) 42));
117+
assertThrows(ArrayIndexOutOfBoundsException.class, () -> access.readShort(arr, -1));
118+
assertThrows(ArrayIndexOutOfBoundsException.class, () -> access.readShort(arr, arr.length));
119+
}
120+
}
121+
122+
@Test
123+
public void testInt() {
124+
byte[] arr = new byte[INT_INPUTS.length * 4];
125+
for (int i = 0; i < INT_INPUTS.length; i++) {
126+
access.writeInt(arr, i * 4, INT_INPUTS[i]);
127+
}
128+
for (int i = 0; i < INT_INPUTS.length; i++) {
129+
assertEquals(INT_INPUTS[i], access.readInt(arr, i * 4));
130+
}
131+
132+
if (!isUnsafe) {
133+
assertThrows(ArrayIndexOutOfBoundsException.class, () -> access.writeInt(arr, -1, 42));
134+
assertThrows(ArrayIndexOutOfBoundsException.class, () -> access.writeInt(arr, arr.length, 42));
135+
assertThrows(ArrayIndexOutOfBoundsException.class, () -> access.readInt(arr, -1));
136+
assertThrows(ArrayIndexOutOfBoundsException.class, () -> access.readInt(arr, arr.length));
137+
}
138+
}
139+
140+
@Test
141+
public void testCombined() {
142+
byte[] arr = new byte[4 + 2 + 1 + 2 + 4];
143+
int i = 0;
144+
access.writeInt(arr, i, -42);
145+
i += 4;
146+
access.writeShort(arr, i, (short) 123);
147+
i += 2;
148+
access.writeByte(arr, i, (byte) -30);
149+
i += 1;
150+
access.writeShort(arr, i, (short) -60000);
151+
i += 2;
152+
access.writeInt(arr, i, 1000000000);
153+
154+
i = 0;
155+
assertEquals(-42, access.readInt(arr, i));
156+
i += 4;
157+
assertEquals((short) 123, access.readShort(arr, i));
158+
i += 2;
159+
assertEquals((byte) -30, access.readByte(arr, i));
160+
i += 1;
161+
assertEquals((short) -60000, access.readShort(arr, i));
162+
i += 2;
163+
assertEquals(1000000000, access.readInt(arr, i));
164+
}
165+
166+
@Test
167+
public void testObject() {
168+
String[] arr = new String[4];
169+
access.writeObject(arr, 0, "the");
170+
access.writeObject(arr, 1, "quick");
171+
access.writeObject(arr, 2, "brown");
172+
access.writeObject(arr, 3, "fox");
173+
assertEquals("the", access.readObject(arr, 0));
174+
assertEquals("quick", access.readObject(arr, 1));
175+
assertEquals("brown", access.readObject(arr, 2));
176+
assertEquals("fox", access.readObject(arr, 3));
177+
178+
if (!isUnsafe) {
179+
assertThrows(ArrayIndexOutOfBoundsException.class, () -> access.writeObject(arr, -1, "jumps"));
180+
assertThrows(ArrayIndexOutOfBoundsException.class, () -> access.writeObject(arr, arr.length, "jumps"));
181+
assertThrows(ArrayIndexOutOfBoundsException.class, () -> access.readObject(arr, -1));
182+
assertThrows(ArrayIndexOutOfBoundsException.class, () -> access.readObject(arr, arr.length));
183+
assertThrows(ArrayStoreException.class, () -> access.writeObject(arr, 0, new Object()));
184+
}
185+
}
186+
187+
@Test
188+
public void testCast() {
189+
Object foo = "foo";
190+
String fooString = doCast(foo, String.class);
191+
assertTrue(fooString.contentEquals("foo"));
192+
193+
foo = new Object();
194+
if (!isUnsafe) {
195+
try {
196+
doCast(foo, String.class);
197+
fail("Expected ClassCastException");
198+
} catch (ClassCastException ex) {
199+
// pass
200+
}
201+
} else {
202+
doCast(foo, String.class); // no failure (unchecked cast)
203+
try {
204+
fooString = doCast(foo, String.class); // failure (assignment uses checkcast)
205+
fail("Expected ClassCastException");
206+
} catch (ClassCastException ex) {
207+
// pass
208+
}
209+
}
210+
}
211+
212+
private <T> T doCast(Object o, Class<T> clazz) {
213+
return access.cast(o, clazz);
214+
}
215+
216+
/**
217+
* The safe accessor has different implementations for reading numbers depending on the platform
218+
* byte order. In order to test both implementations regardless of the platform, we manually
219+
* test the static helpers.
220+
*/
221+
222+
@Test
223+
public void testBigEndianNumbers() {
224+
assumeFalse(isUnsafe);
225+
226+
byte[] arr = new byte[SHORT_INPUTS.length * 2];
227+
for (int i = 0; i < SHORT_INPUTS.length; i++) {
228+
BytecodeDSLAccess.SafeImpl.writeShortBigEndian(arr, i * 2, SHORT_INPUTS[i]);
229+
}
230+
for (int i = 0; i < SHORT_INPUTS.length; i++) {
231+
assertEquals(SHORT_INPUTS[i], BytecodeDSLAccess.SafeImpl.readShortBigEndian(arr, i * 2));
232+
}
233+
234+
arr = new byte[INT_INPUTS.length * 4];
235+
for (int i = 0; i < INT_INPUTS.length; i++) {
236+
BytecodeDSLAccess.SafeImpl.writeIntBigEndian(arr, i * 4, INT_INPUTS[i]);
237+
}
238+
for (int i = 0; i < INT_INPUTS.length; i++) {
239+
assertEquals(INT_INPUTS[i], BytecodeDSLAccess.SafeImpl.readIntBigEndian(arr, i * 4));
240+
}
241+
}
242+
243+
@Test
244+
public void testLittleEndianNumbers() {
245+
assumeFalse(isUnsafe);
246+
247+
byte[] arr = new byte[SHORT_INPUTS.length * 2];
248+
for (int i = 0; i < SHORT_INPUTS.length; i++) {
249+
BytecodeDSLAccess.SafeImpl.writeShortLittleEndian(arr, i * 2, SHORT_INPUTS[i]);
250+
}
251+
for (int i = 0; i < SHORT_INPUTS.length; i++) {
252+
assertEquals(SHORT_INPUTS[i], BytecodeDSLAccess.SafeImpl.readShortLittleEndian(arr, i * 2));
253+
}
254+
255+
arr = new byte[INT_INPUTS.length * 4];
256+
for (int i = 0; i < INT_INPUTS.length; i++) {
257+
BytecodeDSLAccess.SafeImpl.writeIntLittleEndian(arr, i * 4, INT_INPUTS[i]);
258+
}
259+
for (int i = 0; i < INT_INPUTS.length; i++) {
260+
assertEquals(INT_INPUTS[i], BytecodeDSLAccess.SafeImpl.readIntLittleEndian(arr, i * 4));
261+
}
262+
}
263+
264+
abstract static class AccessToken<T extends RootNode & BytecodeRootNode> extends BytecodeRootNodes<T> {
265+
266+
protected AccessToken(BytecodeParser<? extends BytecodeBuilder> parse) {
267+
super(parse);
268+
}
269+
270+
static final Object PUBLIC_TOKEN = TOKEN;
271+
272+
}
273+
}

0 commit comments

Comments
 (0)