Skip to content

Commit 739371e

Browse files
committed
[GR-77075] Add instruction rewriting support for stack values.
PullRequest: graal/25163
2 parents 49385e1 + 0d0f607 commit 739371e

8 files changed

Lines changed: 1144 additions & 305 deletions

File tree

truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/basic_interpreter/InstructionRewritingTest.java

Lines changed: 293 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
import static org.junit.Assert.fail;
4646
import static org.junit.Assume.assumeTrue;
4747

48+
import java.util.ArrayList;
4849
import java.util.List;
4950

5051
import static com.oracle.truffle.api.bytecode.test.AbstractInstructionTest.assertInstructions;
@@ -287,6 +288,248 @@ public void testLoadStackValuePop() {
287288
assertEquals(42L, node.getCallTarget().call());
288289
}
289290

291+
@Test
292+
public void testStackValueReturnCleanup1() {
293+
assertStackValueReturnCleanup(1);
294+
}
295+
296+
@Test
297+
public void testStackValueReturnCleanup3() {
298+
assertStackValueReturnCleanup(3);
299+
}
300+
301+
@Test
302+
public void testStackValueReturnCleanup8() {
303+
assertStackValueReturnCleanup(8);
304+
}
305+
306+
@Test
307+
public void testStackValueOperandFull() {
308+
BasicInterpreter node = parseNode("stackValueOperandFull", (BasicInterpreterBuilder b) -> {
309+
b.beginRoot();
310+
311+
BytecodeLocal result = b.createLocal();
312+
b.beginStoreLocal(result);
313+
b.beginBlock();
314+
StackValue value = emitBindStackValue(b, 42L);
315+
b.beginAlwaysBoxOperation();
316+
b.emitLoadStackValue(value);
317+
b.endAlwaysBoxOperation();
318+
b.endBlock();
319+
b.endStoreLocal();
320+
321+
b.beginReturn();
322+
b.emitLoadLocal(result);
323+
b.endReturn();
324+
b.endRoot();
325+
});
326+
327+
if (run.hasInstructionRewriting()) {
328+
assertInstructions(node,
329+
"load.constant",
330+
"c.AlwaysBoxOperation",
331+
"store.local",
332+
"load.local",
333+
"return");
334+
} else {
335+
assertInstructions(node,
336+
"load.constant",
337+
"dup",
338+
"c.AlwaysBoxOperation",
339+
"store.stackvalue",
340+
"store.local",
341+
"load.local",
342+
"return");
343+
}
344+
assertEquals(42L, node.getCallTarget().call());
345+
}
346+
347+
@Test
348+
public void testStackValueOperandSuffix() {
349+
BasicInterpreter node = parseNode("stackValueOperandSuffix", (BasicInterpreterBuilder b) -> {
350+
b.beginRoot();
351+
352+
BytecodeLocal result = b.createLocal();
353+
b.beginStoreLocal(result);
354+
b.beginBlock();
355+
emitBindStackValue(b, 100L);
356+
emitBindStackValue(b, 101L);
357+
StackValue value = emitBindStackValue(b, 42L);
358+
b.beginAlwaysBoxOperation();
359+
b.emitLoadStackValue(value);
360+
b.endAlwaysBoxOperation();
361+
b.endBlock();
362+
b.endStoreLocal();
363+
364+
b.beginReturn();
365+
b.emitLoadLocal(result);
366+
b.endReturn();
367+
b.endRoot();
368+
});
369+
370+
if (run.hasInstructionRewriting()) {
371+
assertInstructions(node,
372+
"load.constant",
373+
"load.constant",
374+
"load.constant",
375+
"c.AlwaysBoxOperation",
376+
"store.stackvalue",
377+
"pop",
378+
"store.local",
379+
"load.local",
380+
"return");
381+
} else {
382+
assertInstructions(node,
383+
"load.constant",
384+
"load.constant",
385+
"load.constant",
386+
"dup",
387+
"c.AlwaysBoxOperation",
388+
"store.stackvalue",
389+
"pop",
390+
"pop",
391+
"store.local",
392+
"load.local",
393+
"return");
394+
}
395+
assertEquals(42L, node.getCallTarget().call());
396+
}
397+
398+
@Test
399+
public void testStackValueOperandVoidFull() {
400+
BasicInterpreter node = parseNode("stackValueOperandVoidFull", (BasicInterpreterBuilder b) -> {
401+
b.beginRoot();
402+
403+
b.beginBlock();
404+
StackValue receiver = emitBindStackValueArgument(b, 0);
405+
StackValue value = emitBindStackValue(b, 42L);
406+
b.beginAppenderOperation();
407+
b.emitLoadStackValue(receiver);
408+
b.emitLoadStackValue(value);
409+
b.endAppenderOperation();
410+
b.endBlock();
411+
412+
b.beginReturn();
413+
b.emitLoadArgument(0);
414+
b.endReturn();
415+
b.endRoot();
416+
});
417+
418+
if (run.hasInstructionRewriting()) {
419+
assertInstructions(node,
420+
"load.argument",
421+
"load.constant",
422+
"c.AppenderOperation",
423+
"load.argument",
424+
"return");
425+
} else {
426+
assertInstructions(node,
427+
"load.argument",
428+
"load.constant",
429+
"load.stackvalue",
430+
"load.stackvalue",
431+
"c.AppenderOperation",
432+
"pop",
433+
"pop",
434+
"load.argument",
435+
"return");
436+
}
437+
ArrayList<Object> list = new ArrayList<>();
438+
assertEquals(list, node.getCallTarget().call(list));
439+
assertEquals(List.of(42L), list);
440+
}
441+
442+
@Test
443+
public void testStackValueOperandVoidSuffix() {
444+
BasicInterpreter node = parseNode("stackValueOperandVoidSuffix", (BasicInterpreterBuilder b) -> {
445+
b.beginRoot();
446+
447+
b.beginBlock();
448+
emitBindStackValue(b, 100L);
449+
StackValue receiver = emitBindStackValueArgument(b, 0);
450+
StackValue value = emitBindStackValue(b, 42L);
451+
b.beginAppenderOperation();
452+
b.emitLoadStackValue(receiver);
453+
b.emitLoadStackValue(value);
454+
b.endAppenderOperation();
455+
b.endBlock();
456+
457+
b.beginReturn();
458+
b.emitLoadArgument(0);
459+
b.endReturn();
460+
b.endRoot();
461+
});
462+
463+
if (run.hasInstructionRewriting()) {
464+
assertInstructions(node,
465+
"load.constant",
466+
"load.argument",
467+
"load.constant",
468+
"c.AppenderOperation",
469+
"pop",
470+
"load.argument",
471+
"return");
472+
} else {
473+
assertInstructions(node,
474+
"load.constant",
475+
"load.argument",
476+
"load.constant",
477+
"load.stackvalue",
478+
"load.stackvalue",
479+
"c.AppenderOperation",
480+
"pop",
481+
"pop",
482+
"pop",
483+
"load.argument",
484+
"return");
485+
}
486+
ArrayList<Object> list = new ArrayList<>();
487+
assertEquals(list, node.getCallTarget().call(list));
488+
assertEquals(List.of(42L), list);
489+
}
490+
491+
@Test
492+
public void testStackValueConsumerWithImmediate() {
493+
// Test that the consuming instruction's immediates are copied when rewriting.
494+
BasicInterpreter node = parseNode("stackValueConsumerWithImmediate", (BasicInterpreterBuilder b) -> {
495+
b.beginRoot();
496+
497+
BytecodeLocal result = b.createLocal();
498+
b.beginStoreLocal(result);
499+
b.beginBlock();
500+
StackValue value = emitBindStackValue(b, 2L);
501+
b.beginAddConstantOperation(40L);
502+
b.emitLoadStackValue(value);
503+
b.endAddConstantOperation();
504+
b.endBlock();
505+
b.endStoreLocal();
506+
507+
b.beginReturn();
508+
b.emitLoadLocal(result);
509+
b.endReturn();
510+
b.endRoot();
511+
});
512+
513+
if (run.hasInstructionRewriting()) {
514+
assertInstructions(node,
515+
"load.constant",
516+
"c.AddConstantOperation",
517+
"store.local",
518+
"load.local",
519+
"return");
520+
} else {
521+
assertInstructions(node,
522+
"load.constant",
523+
"dup",
524+
"c.AddConstantOperation",
525+
"store.stackvalue",
526+
"store.local",
527+
"load.local",
528+
"return");
529+
}
530+
assertEquals(42L, node.getCallTarget().call());
531+
}
532+
290533
@Test
291534
public void testClearLocalDuplicate() {
292535
BasicInterpreter node = parseNode("clearLocalDuplicate", (BasicInterpreterBuilder b) -> {
@@ -321,6 +564,56 @@ public void testClearLocalDuplicate() {
321564
assertEquals(42L, node.getCallTarget().call());
322565
}
323566

567+
private static <T extends BasicInterpreterBuilder> StackValue emitBindStackValue(T b, long value) {
568+
b.beginBindStackValue();
569+
b.emitLoadConstant(value);
570+
return b.endBindStackValue();
571+
}
572+
573+
private static <T extends BasicInterpreterBuilder> StackValue emitBindStackValueArgument(T b, int argumentIndex) {
574+
b.beginBindStackValue();
575+
b.emitLoadArgument(argumentIndex);
576+
return b.endBindStackValue();
577+
}
578+
579+
private void assertStackValueReturnCleanup(int numStackValues) {
580+
BasicInterpreter node = parseNode("stackValueReturnCleanup" + numStackValues, (BasicInterpreterBuilder b) -> {
581+
b.beginRoot();
582+
583+
b.beginReturn();
584+
b.beginBlock();
585+
for (int i = 0; i < numStackValues; i++) {
586+
emitBindStackValue(b, 100L + i);
587+
}
588+
b.emitLoadConstant(42L);
589+
b.endBlock();
590+
b.endReturn();
591+
592+
b.endRoot();
593+
});
594+
595+
assertInstructions(node, stackValueReturnCleanupInstructions(numStackValues, run.hasInstructionRewriting()));
596+
assertEquals(42L, node.getCallTarget().call());
597+
}
598+
599+
private static String[] stackValueReturnCleanupInstructions(int numStackValues, boolean rewritten) {
600+
int cleanupInstructionCount = rewritten ? 0 : numStackValues;
601+
String[] result = new String[numStackValues + 1 + cleanupInstructionCount + 1];
602+
int index = 0;
603+
for (int i = 0; i < numStackValues + 1; i++) {
604+
result[index++] = "load.constant";
605+
}
606+
if (!rewritten) {
607+
result[index++] = "store.stackvalue";
608+
for (int i = 1; i < numStackValues; i++) {
609+
result[index++] = "pop";
610+
}
611+
}
612+
result[index++] = "return";
613+
assertEquals(result.length, index);
614+
return result;
615+
}
616+
324617
@Test
325618
public void testRewriteAcrossBlockStart() {
326619
assumeTrue(run.hasInstructionRewriting());

0 commit comments

Comments
 (0)