|
27 | 27 | import static com.oracle.truffle.api.bytecode.test.basic_interpreter.AbstractBasicInterpreterTest.createNodes; |
28 | 28 | import static com.oracle.truffle.api.bytecode.test.basic_interpreter.AbstractBasicInterpreterTest.parseNode; |
29 | 29 | import static org.junit.Assert.assertEquals; |
| 30 | +import static org.junit.Assert.assertNotNull; |
30 | 31 | import static org.junit.Assert.assertNull; |
31 | 32 | import static org.junit.Assert.fail; |
32 | 33 | import static org.junit.Assume.assumeTrue; |
|
44 | 45 | import org.junit.runners.Parameterized.Parameters; |
45 | 46 |
|
46 | 47 | import com.oracle.truffle.api.bytecode.BytecodeConfig; |
| 48 | +import com.oracle.truffle.api.bytecode.BytecodeFrame; |
47 | 49 | import com.oracle.truffle.api.bytecode.BytecodeLocal; |
48 | 50 | import com.oracle.truffle.api.bytecode.BytecodeLocation; |
49 | 51 | import com.oracle.truffle.api.bytecode.BytecodeNode; |
50 | 52 | import com.oracle.truffle.api.bytecode.BytecodeParser; |
51 | 53 | import com.oracle.truffle.api.bytecode.BytecodeRootNodes; |
| 54 | +import com.oracle.truffle.api.bytecode.BytecodeTier; |
52 | 55 | import com.oracle.truffle.api.bytecode.ContinuationResult; |
53 | 56 | import com.oracle.truffle.api.bytecode.test.BytecodeDSLTestLanguage; |
54 | 57 | import com.oracle.truffle.api.bytecode.test.basic_interpreter.AbstractBasicInterpreterTest; |
55 | 58 | import com.oracle.truffle.api.bytecode.test.basic_interpreter.AbstractBasicInterpreterTest.TestRun; |
56 | 59 | import com.oracle.truffle.api.bytecode.test.basic_interpreter.BasicInterpreter; |
57 | 60 | import com.oracle.truffle.api.bytecode.test.basic_interpreter.BasicInterpreterBuilder; |
58 | 61 | import com.oracle.truffle.api.bytecode.test.basic_interpreter.BasicInterpreterBuilder.BytecodeVariant; |
| 62 | +import com.oracle.truffle.api.frame.FrameInstance; |
59 | 63 | import com.oracle.truffle.api.frame.FrameSlotKind; |
60 | 64 | import com.oracle.truffle.api.frame.VirtualFrame; |
61 | 65 | import com.oracle.truffle.api.instrumentation.ExecutionEventNode; |
@@ -957,6 +961,176 @@ public void testTagInstrumentation() { |
957 | 961 | assertCompiled(target); |
958 | 962 | } |
959 | 963 |
|
| 964 | + @Test |
| 965 | + public void testCaptureFrame() { |
| 966 | + BytecodeRootNodes<BasicInterpreter> rootNodes = createNodes(run, BytecodeDSLTestLanguage.REF.get(null), BytecodeConfig.DEFAULT, b -> { |
| 967 | + b.beginRoot(); |
| 968 | + b.beginReturn(); |
| 969 | + b.beginCaptureFrame(); |
| 970 | + b.emitLoadArgument(0); |
| 971 | + b.emitLoadArgument(1); |
| 972 | + b.endCaptureFrame(); |
| 973 | + b.endReturn(); |
| 974 | + BasicInterpreter callee = b.endRoot(); |
| 975 | + callee.setName("callee"); |
| 976 | + |
| 977 | + b.beginRoot(); |
| 978 | + BytecodeLocal x = b.createLocal(); |
| 979 | + b.beginStoreLocal(x); |
| 980 | + b.emitLoadConstant(123); |
| 981 | + b.endStoreLocal(); |
| 982 | + b.beginInvoke(); |
| 983 | + b.emitLoadConstant(callee); |
| 984 | + b.emitLoadArgument(0); |
| 985 | + b.emitLoadArgument(1); |
| 986 | + b.endInvoke(); |
| 987 | + b.endRoot().setName("caller"); |
| 988 | + }); |
| 989 | + BasicInterpreter caller = rootNodes.getNode(1); |
| 990 | + |
| 991 | + OptimizedCallTarget target = (OptimizedCallTarget) caller.getCallTarget(); |
| 992 | + |
| 993 | + // The callee frame (the top of the stack) should never be accessible. |
| 994 | + assertNull(target.call(0, FrameInstance.FrameAccess.READ_ONLY)); |
| 995 | + |
| 996 | + // In the interpreter the caller frame should always be accessible. |
| 997 | + assertNotCompiled(target); |
| 998 | + checkCallerBytecodeFrame((BytecodeFrame) target.call(1, FrameInstance.FrameAccess.READ_ONLY), false); |
| 999 | + assertNotCompiled(target); |
| 1000 | + checkCallerBytecodeFrame((BytecodeFrame) target.call(1, FrameInstance.FrameAccess.READ_WRITE), false); |
| 1001 | + assertNotCompiled(target); |
| 1002 | + checkCallerBytecodeFrame((BytecodeFrame) target.call(1, FrameInstance.FrameAccess.MATERIALIZE), false); |
| 1003 | + |
| 1004 | + // Force transition to cached. |
| 1005 | + caller.getBytecodeNode().setUncachedThreshold(0); |
| 1006 | + target.call(0, FrameInstance.FrameAccess.READ_ONLY); |
| 1007 | + assertEquals(BytecodeTier.CACHED, caller.getBytecodeNode().getTier()); |
| 1008 | + |
| 1009 | + // In compiled code the caller frame should always be accessible, but may be a copy. |
| 1010 | + // Requesting the frame should not invalidate compiled code. |
| 1011 | + target.compile(true); |
| 1012 | + assertCompiled(target); |
| 1013 | + checkCallerBytecodeFrame((BytecodeFrame) target.call(1, FrameInstance.FrameAccess.READ_ONLY), true); |
| 1014 | + assertCompiled(target); |
| 1015 | + checkCallerBytecodeFrame((BytecodeFrame) target.call(1, FrameInstance.FrameAccess.READ_WRITE), false); |
| 1016 | + assertCompiled(target); |
| 1017 | + checkCallerBytecodeFrame((BytecodeFrame) target.call(1, FrameInstance.FrameAccess.MATERIALIZE), false); |
| 1018 | + assertCompiled(target); |
| 1019 | + } |
| 1020 | + |
| 1021 | + @Test |
| 1022 | + public void testCaptureNonVirtualFrame() { |
| 1023 | + BytecodeRootNodes<BasicInterpreter> rootNodes = createNodes(run, BytecodeDSLTestLanguage.REF.get(null), BytecodeConfig.DEFAULT, b -> { |
| 1024 | + b.beginRoot(); |
| 1025 | + b.beginReturn(); |
| 1026 | + b.beginCaptureNonVirtualFrame(); |
| 1027 | + b.emitLoadArgument(0); |
| 1028 | + b.endCaptureNonVirtualFrame(); |
| 1029 | + b.endReturn(); |
| 1030 | + BasicInterpreter callee = b.endRoot(); |
| 1031 | + callee.setName("callee"); |
| 1032 | + |
| 1033 | + b.beginRoot(); |
| 1034 | + BytecodeLocal x = b.createLocal(); |
| 1035 | + b.beginStoreLocal(x); |
| 1036 | + b.emitLoadConstant(123); |
| 1037 | + b.endStoreLocal(); |
| 1038 | + b.beginInvoke(); |
| 1039 | + b.emitLoadConstant(callee); |
| 1040 | + b.emitLoadArgument(0); |
| 1041 | + b.endInvoke(); |
| 1042 | + b.endRoot().setName("caller"); |
| 1043 | + }); |
| 1044 | + BasicInterpreter caller = rootNodes.getNode(1); |
| 1045 | + |
| 1046 | + OptimizedCallTarget target = (OptimizedCallTarget) caller.getCallTarget(); |
| 1047 | + |
| 1048 | + // The callee frame (the top of the stack) should never be accessible. |
| 1049 | + assertNull(target.call(0)); |
| 1050 | + |
| 1051 | + // In the interpreter the non-virtual caller frame should be accessible. |
| 1052 | + assertNotCompiled(target); |
| 1053 | + BytecodeFrame nonVirtualFrame = (BytecodeFrame) target.call(1); |
| 1054 | + assertNotCompiled(target); |
| 1055 | + checkCallerBytecodeFrame(nonVirtualFrame, false); |
| 1056 | + |
| 1057 | + // Force transition to cached. |
| 1058 | + caller.getBytecodeNode().setUncachedThreshold(0); |
| 1059 | + target.call(0); |
| 1060 | + assertEquals(BytecodeTier.CACHED, caller.getBytecodeNode().getTier()); |
| 1061 | + |
| 1062 | + // In compiled code the non-virtual caller frame should be inaccessible. |
| 1063 | + target.compile(true); |
| 1064 | + assertCompiled(target); |
| 1065 | + assertNull(target.call(1)); |
| 1066 | + assertCompiled(target); |
| 1067 | + } |
| 1068 | + |
| 1069 | + @Test |
| 1070 | + public void testCaptureNonVirtualFrameAfterMaterialization() { |
| 1071 | + BytecodeRootNodes<BasicInterpreter> rootNodes = createNodes(run, BytecodeDSLTestLanguage.REF.get(null), BytecodeConfig.DEFAULT, b -> { |
| 1072 | + b.beginRoot(); |
| 1073 | + b.beginReturn(); |
| 1074 | + b.beginCaptureNonVirtualFrame(); |
| 1075 | + b.emitLoadArgument(0); |
| 1076 | + b.endCaptureNonVirtualFrame(); |
| 1077 | + b.endReturn(); |
| 1078 | + BasicInterpreter callee = b.endRoot(); |
| 1079 | + callee.setName("callee"); |
| 1080 | + |
| 1081 | + b.beginRoot(); |
| 1082 | + BytecodeLocal x = b.createLocal(); |
| 1083 | + b.beginStoreLocal(x); |
| 1084 | + b.emitLoadConstant(123); |
| 1085 | + b.endStoreLocal(); |
| 1086 | + |
| 1087 | + b.beginBlackhole(); |
| 1088 | + b.emitMaterializeFrame(); // force materialize frame. |
| 1089 | + b.endBlackhole(); |
| 1090 | + |
| 1091 | + b.beginInvoke(); |
| 1092 | + b.emitLoadConstant(callee); |
| 1093 | + b.emitLoadArgument(0); |
| 1094 | + b.endInvoke(); |
| 1095 | + b.endRoot().setName("caller"); |
| 1096 | + }); |
| 1097 | + BasicInterpreter caller = rootNodes.getNode(1); |
| 1098 | + |
| 1099 | + OptimizedCallTarget target = (OptimizedCallTarget) caller.getCallTarget(); |
| 1100 | + |
| 1101 | + // The callee frame (the top of the stack) should never be accessible. |
| 1102 | + assertNull(target.call(0)); |
| 1103 | + |
| 1104 | + // In the interpreter the non-virtual caller frame should be accessible. |
| 1105 | + assertNotCompiled(target); |
| 1106 | + BytecodeFrame nonVirtualFrame = (BytecodeFrame) target.call(1); |
| 1107 | + assertNotCompiled(target); |
| 1108 | + checkCallerBytecodeFrame(nonVirtualFrame, false); |
| 1109 | + |
| 1110 | + // Force transition to cached. |
| 1111 | + caller.getBytecodeNode().setUncachedThreshold(0); |
| 1112 | + target.call(0); |
| 1113 | + assertEquals(BytecodeTier.CACHED, caller.getBytecodeNode().getTier()); |
| 1114 | + |
| 1115 | + // In compiled code the frame should be accessible because it was materialized already. |
| 1116 | + target.compile(true); |
| 1117 | + assertCompiled(target); |
| 1118 | + nonVirtualFrame = (BytecodeFrame) target.call(1); |
| 1119 | + checkCallerBytecodeFrame(nonVirtualFrame, false); |
| 1120 | + assertCompiled(target); |
| 1121 | + } |
| 1122 | + |
| 1123 | + private void checkCallerBytecodeFrame(BytecodeFrame bytecodeFrame, boolean isCopy) { |
| 1124 | + assertNotNull(bytecodeFrame); |
| 1125 | + assertEquals(1, bytecodeFrame.getLocalCount()); |
| 1126 | + if (isCopy || AbstractBasicInterpreterTest.hasRootScoping(run.interpreterClass())) { |
| 1127 | + assertEquals(123, bytecodeFrame.getLocalValue(0)); |
| 1128 | + } else { |
| 1129 | + // the local gets cleared on exit. |
| 1130 | + assertEquals(AbstractBasicInterpreterTest.getDefaultLocalValue(run.interpreterClass()), bytecodeFrame.getLocalValue(0)); |
| 1131 | + } |
| 1132 | + } |
| 1133 | + |
960 | 1134 | @TruffleInstrument.Registration(id = BytecodeDSLCompilationTestInstrumentation.ID, services = Instrumenter.class) |
961 | 1135 | public static class BytecodeDSLCompilationTestInstrumentation extends TruffleInstrument { |
962 | 1136 |
|
|
0 commit comments