implements PR-06.2
This commit is contained in:
parent
0ec5693b0d
commit
bc98d5a3d4
@ -57,13 +57,13 @@ to concrete positive/negative test evidence and current status.
|
||||
| G20-11.3 | Source attribution MUST be preserved when source-actionable. | `LowerToIRVMPipelineStageTest#runMustAttachSourceAttributionForLoweringFailure`; `LowerToIRVMServiceTest#lowerMustMapHostAndIntrinsicCallsites` | `LowerToIRVMServiceTest#lowerMustRejectMissingCallee` | pass | Stage-level failure now carries explicit `file/start/end` attribution for lowering errors. |
|
||||
| G21-5 | `OptimizeIRVM` MUST NOT be skipped in canonical pipeline order. | `BuilderPipelineServiceOrderTest#canonicalOrderMustContainOptimizeBetweenLowerAndEmit` | N/A | pass | Canonical stage order is enforced. |
|
||||
| G21-6.1 | Optimize input MUST satisfy lowering obligations/profile/structural validity. | `OptimizeIRVMPipelineStageTest#runMustAcceptSupportedNonDefaultVmProfile` | `OptimizeIRVMPipelineStageTest#runMustRejectUnsupportedVmProfile` | pass | Input validation occurs before pass execution. |
|
||||
| G21-6.2 | Optimize output MUST preserve semantics/contracts and remain emission-valid. | `OptimizeIRVMServiceTest#optimizeDefaultPassesMustRemoveUnreachableInstructions`; `EmitBytecodePipelineStageTest#runMustEmitBytecodeWhenPreconditionsAreSatisfied` | `OptimizeIRVMServiceTest#optimizeMustRejectPassThatMutatesVmProfile` | partial | Full semantic-equivalence fixture corpus still incremental. |
|
||||
| G21-7.1 | Optimization passes MUST preserve observable semantics. | `OptimizeIRVMServiceTest#normalizeRedundantJumpTargetsPassMustCollapseJumpChain` | N/A | partial | Proof level is fixture-based, not formal equivalence. |
|
||||
| G21-6.2 | Optimize output MUST preserve semantics/contracts and remain emission-valid. | `OptimizeIRVMEquivalenceHarnessTest#optimizeOnOffMustPreserveObservableTraceForLoweredHostIntrinsicFixture`; `OptimizeIRVMEquivalenceHarnessTest#optimizeOnOffMustPreserveObservableTraceForConditionalJoinFixture`; `OptimizeIRVMEquivalenceHarnessTest#optimizeOnOffMustPreserveObservableTraceForSimpleLoopFixture`; `OptimizeIRVMEquivalenceHarnessTest#optimizeOnOffMustPreserveObservableTraceForLinearCallFixture`; `EmitBytecodePipelineStageTest#runMustEmitBytecodeWhenPreconditionsAreSatisfied` | `OptimizeIRVMServiceTest#optimizeMustRejectPassThatMutatesVmProfile` | pass | Equivalence harness now validates on/off semantics and emission validity over CFG corpus with host/intrinsic paths. |
|
||||
| G21-7.1 | Optimization passes MUST preserve observable semantics. | `OptimizeIRVMEquivalenceHarnessTest#optimizeOnOffMustPreserveObservableTraceForConditionalJoinFixture`; `OptimizeIRVMEquivalenceHarnessTest#optimizeOnOffMustPreserveObservableTraceForSimpleLoopFixture`; `OptimizeIRVMEquivalenceHarnessTest#optimizeOnOffMustPreserveObservableTraceForLinearCallFixture` | N/A | pass | Observable trace equivalence is asserted across branching, loops, and call graphs. |
|
||||
| G21-7.2 | Optimization passes MUST be deterministic for same input/profile. | `BackendSafetyGateSUTest#optimizeStageMustBeDeterministicForSameInputProgram` | N/A | pass | |
|
||||
| G21-7.3 | Optimization passes MUST preserve profile compatibility. | `OptimizeIRVMPipelineStageTest#runMustAcceptSupportedNonDefaultVmProfile` | `OptimizeIRVMServiceTest#optimizeMustRejectPassThatMutatesVmProfile` | pass | |
|
||||
| G21-7.4 | Optimization passes MUST preserve host-vs-intrinsic boundary classification. | `LowerToIRVMServiceTest#lowerMustMapHostAndIntrinsicCallsites`; `BackendGateIIntegrationTest#gateI_validIntrinsicPath` | N/A | pass | No pass rewrites operation kind domains. |
|
||||
| G21-7.5 | Optimization passes MUST preserve diagnostics/source-attribution hooks. | `LowerToIRVMServiceTest#lowerMustMapHostAndIntrinsicCallsites` (spans on emission ops) | N/A | partial | Needs targeted optimizer regression asserting span retention after rewrites. |
|
||||
| G21-9.1 | Validation MUST include optimized-vs-non-optimized equivalence fixtures. | `OptimizeIRVMServiceTest` baseline pass fixtures | N/A | partial | Additional corpus planned for broader control-flow patterns. |
|
||||
| G21-9.1 | Validation MUST include optimized-vs-non-optimized equivalence fixtures. | `OptimizeIRVMEquivalenceHarnessTest#optimizeOnOffMustPreserveObservableTraceForLoweredHostIntrinsicFixture`; `OptimizeIRVMEquivalenceHarnessTest#optimizeOnOffMustPreserveObservableTraceForConditionalJoinFixture`; `OptimizeIRVMEquivalenceHarnessTest#optimizeOnOffMustPreserveObservableTraceForSimpleLoopFixture`; `OptimizeIRVMEquivalenceHarnessTest#optimizeOnOffMustPreserveObservableTraceForLinearCallFixture` | N/A | pass | Dedicated opt on/off harness with reusable interpreter and deterministic trace assertions is in place. |
|
||||
| G21-9.2 | Validation MUST preserve known negative loader/verifier behavior. | `BackendSafetyGateSUTest#emitStageMustExposeMarshalingLinkageFailureDeterministically`; `BackendGateIIntegrationTest` rejection suite | N/A | pass | |
|
||||
| G21-9.3 | Validation MUST preserve deterministic artifact-level invariants. | `BackendSafetyGateSUTest#fullPipelineMustProduceDeterministicBytecodeForSameInput`; `BytecodeEmitterTest#emitMustRemainDeterministicAfterInterning` | N/A | pass | |
|
||||
| PBS13-12.0 | Executable backend handoff MUST satisfy addendum obligations. | `IRBackendExecutableContractTest` suite | N/A | pass | Row group `PBS13-12.x` details each obligation. |
|
||||
|
||||
@ -0,0 +1,422 @@
|
||||
package p.studio.compiler.backend.irvm;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import p.studio.compiler.backend.bytecode.BytecodeEmitter;
|
||||
import p.studio.compiler.backend.bytecode.BytecodePreloadVerifierService;
|
||||
import p.studio.compiler.backend.bytecode.BytecodeModule;
|
||||
import p.studio.compiler.models.IRBackend;
|
||||
import p.studio.compiler.models.IRBackendExecutableFunction;
|
||||
import p.studio.compiler.source.Span;
|
||||
import p.studio.compiler.source.identifiers.CallableId;
|
||||
import p.studio.compiler.source.identifiers.FileId;
|
||||
import p.studio.compiler.source.identifiers.IntrinsicId;
|
||||
import p.studio.compiler.source.tables.IntrinsicReference;
|
||||
import p.studio.utilities.structures.ReadOnlyList;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class OptimizeIRVMEquivalenceHarnessTest {
|
||||
|
||||
@Test
|
||||
void optimizeOnOffMustPreserveObservableTraceForLoweredHostIntrinsicFixture() {
|
||||
final var backend = IRBackend.builder()
|
||||
.executableFunctions(ReadOnlyList.from(
|
||||
fn("main", 1, ReadOnlyList.from(
|
||||
callHost("gfx", "draw_pixel", 1, 0, 0),
|
||||
callIntrinsic("core.color.pack", 1, 0, 0, 0),
|
||||
ret()))))
|
||||
.intrinsicPool(ReadOnlyList.from(new IntrinsicReference("core.color.pack", 1)))
|
||||
.build();
|
||||
|
||||
final var lowered = new LowerToIRVMService().lower(backend);
|
||||
assertEquivalentObservableBehavior(lowered);
|
||||
}
|
||||
|
||||
@Test
|
||||
void optimizeOnOffMustPreserveObservableTraceForConditionalJoinFixture() {
|
||||
final var program = singleFunctionProgram(
|
||||
"main",
|
||||
1,
|
||||
ReadOnlyList.from(
|
||||
new IRVMInstruction(IRVMOp.INTRINSIC, 901),
|
||||
new IRVMInstruction(IRVMOp.JMP_IF_FALSE, 24),
|
||||
new IRVMInstruction(IRVMOp.HOSTCALL, 0),
|
||||
new IRVMInstruction(IRVMOp.JMP, 30),
|
||||
new IRVMInstruction(IRVMOp.HOSTCALL, 0),
|
||||
new IRVMInstruction(IRVMOp.RET, null)),
|
||||
ReadOnlyList.from(
|
||||
BytecodeEmitter.Operation.intrinsic(901, 0, 1, null),
|
||||
BytecodeEmitter.Operation.jmpIfFalse(24, null),
|
||||
hostcallOp("gfx", "draw_true_branch", 1, 0, 0),
|
||||
BytecodeEmitter.Operation.jmp(30, null),
|
||||
hostcallOp("gfx", "draw_false_branch", 1, 0, 0),
|
||||
BytecodeEmitter.Operation.ret()));
|
||||
assertEquivalentObservableBehavior(program);
|
||||
}
|
||||
|
||||
@Test
|
||||
void optimizeOnOffMustPreserveObservableTraceForSimpleLoopFixture() {
|
||||
final var program = singleFunctionProgram(
|
||||
"main",
|
||||
1,
|
||||
ReadOnlyList.from(
|
||||
new IRVMInstruction(IRVMOp.INTRINSIC, 900),
|
||||
new IRVMInstruction(IRVMOp.JMP_IF_FALSE, 24),
|
||||
new IRVMInstruction(IRVMOp.HOSTCALL, 0),
|
||||
new IRVMInstruction(IRVMOp.JMP, 0),
|
||||
new IRVMInstruction(IRVMOp.RET, null)),
|
||||
ReadOnlyList.from(
|
||||
BytecodeEmitter.Operation.intrinsic(900, 0, 1, null),
|
||||
BytecodeEmitter.Operation.jmpIfFalse(24, null),
|
||||
hostcallOp("gfx", "loop_tick", 1, 0, 0),
|
||||
BytecodeEmitter.Operation.jmp(0, null),
|
||||
BytecodeEmitter.Operation.ret()));
|
||||
assertEquivalentObservableBehavior(program);
|
||||
}
|
||||
|
||||
@Test
|
||||
void optimizeOnOffMustPreserveObservableTraceForLinearCallFixture() {
|
||||
final var mainInstructions = ReadOnlyList.from(
|
||||
new IRVMInstruction(IRVMOp.CALL, 1),
|
||||
new IRVMInstruction(IRVMOp.HOSTCALL, 0),
|
||||
new IRVMInstruction(IRVMOp.RET, null));
|
||||
final var workerInstructions = ReadOnlyList.from(
|
||||
new IRVMInstruction(IRVMOp.HOSTCALL, 0),
|
||||
new IRVMInstruction(IRVMOp.RET, null));
|
||||
final var mainOps = ReadOnlyList.from(
|
||||
BytecodeEmitter.Operation.callFunc(1),
|
||||
hostcallOp("gfx", "draw_main", 1, 0, 0),
|
||||
BytecodeEmitter.Operation.ret());
|
||||
final var workerOps = ReadOnlyList.from(
|
||||
hostcallOp("gfx", "draw_worker", 1, 0, 0),
|
||||
BytecodeEmitter.Operation.ret());
|
||||
final var program = multiFunctionProgram(
|
||||
ReadOnlyList.from(
|
||||
irvmFunction("main", 0, mainInstructions),
|
||||
irvmFunction("worker", 0, workerInstructions)),
|
||||
ReadOnlyList.from(
|
||||
functionPlan("main", 0, mainOps),
|
||||
functionPlan("worker", 0, workerOps)));
|
||||
|
||||
assertEquivalentObservableBehavior(program);
|
||||
}
|
||||
|
||||
private void assertEquivalentObservableBehavior(final IRVMProgram input) {
|
||||
final var optimizedOff = new OptimizeIRVMService(new IRVMValidator(), List.of()).optimize(input);
|
||||
final var optimizedOn = new OptimizeIRVMService().optimize(input);
|
||||
final var optimizedOnAgain = new OptimizeIRVMService().optimize(input);
|
||||
|
||||
final var offResult = execute(optimizedOff);
|
||||
final var onResult = execute(optimizedOn);
|
||||
assertEquals(offResult, onResult);
|
||||
assertEquals(optimizedOn, optimizedOnAgain);
|
||||
assertFalse(optimizedOn.hasInternalOpcodes());
|
||||
assertFalse(optimizedOff.hasInternalOpcodes());
|
||||
|
||||
final var emitter = new BytecodeEmitter();
|
||||
final var verifier = new BytecodePreloadVerifierService();
|
||||
verifier.verify(emitter.emit(optimizedOff.coherentEmissionPlan()));
|
||||
verifier.verify(emitter.emit(optimizedOn.coherentEmissionPlan()));
|
||||
}
|
||||
|
||||
private ExecutionResult execute(final IRVMProgram program) {
|
||||
final var module = program.module();
|
||||
if (module.functions().isEmpty()) {
|
||||
return new ExecutionResult(ReadOnlyList.empty(), true, ReadOnlyList.empty());
|
||||
}
|
||||
|
||||
final var plan = program.coherentEmissionPlan();
|
||||
final var trace = new ArrayList<String>();
|
||||
final var stack = new ArrayDeque<Integer>();
|
||||
final var frames = new ArrayDeque<Frame>();
|
||||
final var indexByPcByFunction = new ArrayList<HashMap<Integer, Integer>>(module.functions().size());
|
||||
final var functionPlans = new ArrayList<BytecodeEmitter.FunctionPlan>(plan.functions().size());
|
||||
final var intrinsicInvocationCount = new HashMap<Integer, Integer>();
|
||||
for (final var function : module.functions()) {
|
||||
indexByPcByFunction.add(indexByPc(function.instructions()));
|
||||
}
|
||||
for (final var functionPlan : plan.functions()) {
|
||||
functionPlans.add(functionPlan);
|
||||
}
|
||||
frames.push(new Frame(0, 0));
|
||||
|
||||
var steps = 0;
|
||||
while (!frames.isEmpty() && steps < 8_000) {
|
||||
steps++;
|
||||
final var frame = frames.peek();
|
||||
final var function = module.functions().get(frame.functionIndex());
|
||||
final var functionPlan = functionPlans.get(frame.functionIndex());
|
||||
final var instructions = function.instructions();
|
||||
if (frame.instructionIndex() < 0 || frame.instructionIndex() >= instructions.size()) {
|
||||
throw new IllegalStateException("invalid pc index during interpretation");
|
||||
}
|
||||
final var instruction = instructions.get(frame.instructionIndex());
|
||||
final var operation = functionPlan.operations().get(frame.instructionIndex());
|
||||
final var op = instruction.op();
|
||||
if (op == IRVMOp.HALT) {
|
||||
trace.add("HALT");
|
||||
return new ExecutionResult(ReadOnlyList.wrap(trace), true, stackSnapshot(stack));
|
||||
} else if (op == IRVMOp.RET) {
|
||||
trace.add("RET:" + function.name());
|
||||
frames.pop();
|
||||
if (frames.isEmpty()) {
|
||||
trace.add("HALT");
|
||||
return new ExecutionResult(ReadOnlyList.wrap(trace), true, stackSnapshot(stack));
|
||||
}
|
||||
} else if (op == IRVMOp.CALL) {
|
||||
trace.add("CALL#" + instruction.immediate());
|
||||
frame.advance();
|
||||
frames.push(new Frame(instruction.immediate(), 0));
|
||||
} else if (op == IRVMOp.JMP) {
|
||||
frame.jump(indexByPcByFunction.get(frame.functionIndex()), instruction.immediate());
|
||||
} else if (op == IRVMOp.JMP_IF_TRUE) {
|
||||
final var value = stack.isEmpty() ? 0 : stack.pop();
|
||||
if (value != 0) {
|
||||
frame.jump(indexByPcByFunction.get(frame.functionIndex()), instruction.immediate());
|
||||
} else {
|
||||
frame.advance();
|
||||
}
|
||||
} else if (op == IRVMOp.JMP_IF_FALSE) {
|
||||
final var value = stack.isEmpty() ? 0 : stack.pop();
|
||||
if (value == 0) {
|
||||
frame.jump(indexByPcByFunction.get(frame.functionIndex()), instruction.immediate());
|
||||
} else {
|
||||
frame.advance();
|
||||
}
|
||||
} else if (op == IRVMOp.HOSTCALL) {
|
||||
final var hostDecl = operation.syscallDecl();
|
||||
if (hostDecl != null) {
|
||||
trace.add("HOSTCALL:" + hostDecl.module() + "." + hostDecl.name() + "@" + hostDecl.version());
|
||||
} else {
|
||||
trace.add("HOSTCALL");
|
||||
}
|
||||
applyOperationStackEffect(stack, operation);
|
||||
frame.advance();
|
||||
} else if (op == IRVMOp.INTRINSIC) {
|
||||
trace.add("INTRINSIC#" + instruction.immediate());
|
||||
applyOperationStackEffect(stack, operation);
|
||||
final var pushes = operation.expectedRetSlots() == null ? 0 : operation.expectedRetSlots();
|
||||
for (var i = 0; i < pushes; i++) {
|
||||
final var count = intrinsicInvocationCount.getOrDefault(instruction.immediate(), 0);
|
||||
intrinsicInvocationCount.put(instruction.immediate(), count + 1);
|
||||
stack.push(simulatedIntrinsicValue(instruction.immediate(), count));
|
||||
}
|
||||
frame.advance();
|
||||
} else {
|
||||
throw new IllegalStateException("unsupported op in equivalence harness: " + instruction.op().name());
|
||||
}
|
||||
}
|
||||
return new ExecutionResult(ReadOnlyList.wrap(trace), false, stackSnapshot(stack));
|
||||
}
|
||||
|
||||
private HashMap<Integer, Integer> indexByPc(final ReadOnlyList<IRVMInstruction> instructions) {
|
||||
final var map = new HashMap<Integer, Integer>();
|
||||
var pc = 0;
|
||||
for (var i = 0; i < instructions.size(); i++) {
|
||||
map.put(pc, i);
|
||||
pc += instructions.get(i).encodedSize();
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private void applyOperationStackEffect(
|
||||
final ArrayDeque<Integer> stack,
|
||||
final BytecodeEmitter.Operation operation) {
|
||||
final var pops = operation.expectedArgSlots() == null ? 0 : operation.expectedArgSlots();
|
||||
for (var i = 0; i < pops; i++) {
|
||||
if (stack.isEmpty()) {
|
||||
stack.push(0);
|
||||
}
|
||||
stack.pop();
|
||||
}
|
||||
}
|
||||
|
||||
private int simulatedIntrinsicValue(
|
||||
final int intrinsicId,
|
||||
final int invocationIndex) {
|
||||
if (intrinsicId == 900) {
|
||||
return invocationIndex == 0 ? 1 : 0;
|
||||
}
|
||||
if (intrinsicId == 901) {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
private ReadOnlyList<Integer> stackSnapshot(final ArrayDeque<Integer> stack) {
|
||||
return ReadOnlyList.wrap(new ArrayList<>(stack));
|
||||
}
|
||||
|
||||
private IRVMProgram singleFunctionProgram(
|
||||
final String name,
|
||||
final int maxStackSlots,
|
||||
final ReadOnlyList<IRVMInstruction> instructions,
|
||||
final ReadOnlyList<BytecodeEmitter.Operation> operations) {
|
||||
return multiFunctionProgram(
|
||||
ReadOnlyList.from(irvmFunction(name, maxStackSlots, instructions)),
|
||||
ReadOnlyList.from(functionPlan(name, maxStackSlots, operations)));
|
||||
}
|
||||
|
||||
private IRVMProgram multiFunctionProgram(
|
||||
final ReadOnlyList<IRVMFunction> functions,
|
||||
final ReadOnlyList<BytecodeEmitter.FunctionPlan> functionPlans) {
|
||||
return new IRVMProgram(
|
||||
new IRVMModule("core-v1", functions),
|
||||
new BytecodeEmitter.EmissionPlan(
|
||||
0,
|
||||
ReadOnlyList.empty(),
|
||||
ReadOnlyList.empty(),
|
||||
functionPlans));
|
||||
}
|
||||
|
||||
private IRVMFunction irvmFunction(
|
||||
final String name,
|
||||
final int maxStackSlots,
|
||||
final ReadOnlyList<IRVMInstruction> instructions) {
|
||||
return new IRVMFunction(
|
||||
name,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
maxStackSlots,
|
||||
instructions);
|
||||
}
|
||||
|
||||
private BytecodeEmitter.FunctionPlan functionPlan(
|
||||
final String name,
|
||||
final int maxStackSlots,
|
||||
final ReadOnlyList<BytecodeEmitter.Operation> operations) {
|
||||
return new BytecodeEmitter.FunctionPlan(
|
||||
name,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
maxStackSlots,
|
||||
operations);
|
||||
}
|
||||
|
||||
private BytecodeEmitter.Operation hostcallOp(
|
||||
final String module,
|
||||
final String name,
|
||||
final int version,
|
||||
final int argSlots,
|
||||
final int retSlots) {
|
||||
return BytecodeEmitter.Operation.hostcall(
|
||||
new BytecodeModule.SyscallDecl(module, name, version, argSlots, retSlots),
|
||||
argSlots,
|
||||
retSlots,
|
||||
null);
|
||||
}
|
||||
|
||||
private IRBackendExecutableFunction fn(
|
||||
final String name,
|
||||
final int callableId,
|
||||
final ReadOnlyList<IRBackendExecutableFunction.Instruction> instructions) {
|
||||
return new IRBackendExecutableFunction(
|
||||
new FileId(1),
|
||||
"app",
|
||||
name,
|
||||
new CallableId(callableId),
|
||||
0,
|
||||
10,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
2,
|
||||
instructions,
|
||||
Span.none());
|
||||
}
|
||||
|
||||
private IRBackendExecutableFunction.Instruction ret() {
|
||||
return new IRBackendExecutableFunction.Instruction(
|
||||
IRBackendExecutableFunction.InstructionKind.RET,
|
||||
"",
|
||||
"",
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
Span.none());
|
||||
}
|
||||
|
||||
private IRBackendExecutableFunction.Instruction callHost(
|
||||
final String module,
|
||||
final String name,
|
||||
final long version,
|
||||
final int argSlots,
|
||||
final int retSlots) {
|
||||
return new IRBackendExecutableFunction.Instruction(
|
||||
IRBackendExecutableFunction.InstructionKind.CALL_HOST,
|
||||
"",
|
||||
"",
|
||||
null,
|
||||
new IRBackendExecutableFunction.HostCallMetadata(module, name, version, argSlots, retSlots),
|
||||
null,
|
||||
Span.none());
|
||||
}
|
||||
|
||||
private IRBackendExecutableFunction.Instruction callIntrinsic(
|
||||
final String canonicalName,
|
||||
final long canonicalVersion,
|
||||
final int intrinsicId,
|
||||
final int argSlots,
|
||||
final int retSlots) {
|
||||
return new IRBackendExecutableFunction.Instruction(
|
||||
IRBackendExecutableFunction.InstructionKind.CALL_INTRINSIC,
|
||||
"",
|
||||
"",
|
||||
null,
|
||||
null,
|
||||
new IRBackendExecutableFunction.IntrinsicCallMetadata(canonicalName, canonicalVersion, new IntrinsicId(intrinsicId)),
|
||||
argSlots,
|
||||
retSlots,
|
||||
Span.none());
|
||||
}
|
||||
|
||||
private record ExecutionResult(
|
||||
ReadOnlyList<String> trace,
|
||||
boolean halted,
|
||||
ReadOnlyList<Integer> stack) {
|
||||
}
|
||||
|
||||
private static final class Frame {
|
||||
private final int functionIndex;
|
||||
private int instructionIndex;
|
||||
|
||||
private Frame(
|
||||
final int functionIndex,
|
||||
final int instructionIndex) {
|
||||
this.functionIndex = functionIndex;
|
||||
this.instructionIndex = instructionIndex;
|
||||
}
|
||||
|
||||
int functionIndex() {
|
||||
return functionIndex;
|
||||
}
|
||||
|
||||
int instructionIndex() {
|
||||
return instructionIndex;
|
||||
}
|
||||
|
||||
void advance() {
|
||||
instructionIndex++;
|
||||
}
|
||||
|
||||
void jump(
|
||||
final HashMap<Integer, Integer> indexByPc,
|
||||
final int targetPc) {
|
||||
final var targetIndex = indexByPc.get(targetPc);
|
||||
if (targetIndex == null) {
|
||||
throw new IllegalStateException("invalid jump target for harness");
|
||||
}
|
||||
instructionIndex = targetIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user