diff --git a/optvm/README.md b/optvm/README.md index 7d660f0..bae0927 100644 --- a/optvm/README.md +++ b/optvm/README.md @@ -13,32 +13,42 @@ a physical machine. Therefore, all our optimization passes will work on the inst ## Guide * [Register](src/main/java/com/compilerprogramming/ezlang/compiler/Register.java) - implements a virtual register. Virtual registers - have a name, type, and id - the id is unique, but name is not. Initially the compiler generates unique registers for every local + have a name, type, id, frameSlot - the id is unique, but the name is not. Initially the compiler generates unique registers for every local and temporary, the number of registers grows as we convert to SSA and back out of SSA. Finally, as we run the Chaitin register allocator - we shrink down the number of virtual registers to the minimum. + we shrink down the number of virtual registers to the minimum. When executing code, the abstract machine uses the frameSlot as the location + of the virtual register in the executing function's stack frame. Registers that do not have overlapping lifetimes can share the same + frameSlot. * [RegisterPool](src/main/java/com/compilerprogramming/ezlang/compiler/RegisterPool.java) - simple pool to allow us to find a register by its id, and to allocate new virtual registers. * [BasicBlock](src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java) - Defines our basic block - which contains instructions - that execute sequentially. A basic block ends with a branch. There are two distinguished basic blocks in every function: entry and exit. + that execute sequentially. A basic block ends with a branch. There are two distinguished basic blocks in every function: entry and exit. * [BBHelper](src/main/java/com/compilerprogramming/ezlang/compiler/BBHelper.java) - Some utilities that manipulate basic blocks. * [Operand](src/main/java/com/compilerprogramming/ezlang/compiler/Operand.java) - Operands in instructions. Both definitions and uses are treated as operands. Instruction can have at most one definition; but an instruction can have multiple use operands. Operands can hold registers or constants or pointers to basic blocks. -* [Instruction](src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java) - Instructions in basic blocks. +* [Instruction](src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java) - Instructions - sequential instructions reside in + basic blocks. Some instructions define variables (registers) and some use them. * [DominatorTree](src/main/java/com/compilerprogramming/ezlang/compiler/DominatorTree.java) - Calculates dominator tree and dominance frontiers. -* [LiveSet](src/main/java/com/compilerprogramming/ezlang/compiler/LiveSet.java) - Bitset used to track liveness of registers. -* [Liveness](src/main/java/com/compilerprogramming/ezlang/compiler/Liveness.java) - Liveness calculator, works for both SSA and non-SSA forms. +* [LiveSet](src/main/java/com/compilerprogramming/ezlang/compiler/LiveSet.java) - Bitset used to track liveness of registers. We exploit the fact that + each register has a unique integer ID and these ids are allocated in a sequential manner. +* [Liveness](src/main/java/com/compilerprogramming/ezlang/compiler/Liveness.java) - Liveness calculator, works for both SSA and non-SSA forms. Computes + liveness data per basic block - mainly live-out. Note that the interference graph builder starts here and computes instruction level liveness as necessary. * [EnterSSA](src/main/java/com/compilerprogramming/ezlang/compiler/EnterSSA.java) - Transforms into SSA, using algorithm by Preston Briggs. * [ExitSSA](src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA.java) - Exits SSA form, using algorithm by Preston Briggs. -* [LoopFinder](src/main/java/com/compilerprogramming/ezlang/compiler/LoopFinder.java) - Discovers loops. -* [LoopNest](src/main/java/com/compilerprogramming/ezlang/compiler/LoopNest.java) - Representation of loop nesting. +* [LoopFinder](src/main/java/com/compilerprogramming/ezlang/compiler/LoopFinder.java) - Discovers loops. (Not used yet) +* [LoopNest](src/main/java/com/compilerprogramming/ezlang/compiler/LoopNest.java) - Representation of loop nesting. (Not used yet) * [InterferenceGraph](src/main/java/com/compilerprogramming/ezlang/compiler/InterferenceGraph.java) - Representation of an Interference Graph required by the register allocator. -* [InterferenceGraphBuilder](src/main/java/com/compilerprogramming/ezlang/compiler/InterferenceGraphBuilder.java) - Constructs InteferenceGraph for a set - of basic bocks, using liveness information. +* [InterferenceGraphBuilder](src/main/java/com/compilerprogramming/ezlang/compiler/InterferenceGraphBuilder.java) - Constructs an InterferenceGraph for a set + of basic bocks, using basic block level liveness information as a starting point for calculating instruction level liveness. * [ChaitinGraphColoringRegisterAllocator](src/main/java/com/compilerprogramming/ezlang/compiler/ChaitinGraphColoringRegisterAllocator.java) - basic - Chaitin Graph Coloring Register Allocator - WIP. + Chaitin Graph Coloring Register Allocator. Since our target machine here is an abstract machine, we do not really needing spilling support + as we can size each function's stack frame to accommodate the number of registers needed such that each register is really a slot in the stack + frame. But we will eventually simulate an abstract machine with a limited set of registers and a separate stack frame. ## Compiler * [CompiledFunction](src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java) - builds and encapsulates the IR for a single function. +* [Compiler](src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java) - simple orchestrator of compilation tasks. +* [Optimizer](src/main/java/com/compilerprogramming/ezlang/compiler/Optimizer.java) - simple orchestrator of optimization steps. Currently + does not have optimization passes, but translates to SSA and out and then runs the graph coloring register allocator. \ No newline at end of file diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java index 09745e3..a7f9cf9 100644 --- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java +++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java @@ -47,13 +47,17 @@ public class BasicBlock { // Liveness computation /** * VarKill contains all the variables that are defined - * in the block. + * in the block. This is often called defs in literature, but + * we use the name in Engineering a Compiler. */ LiveSet varKill; /** * UEVar contains upward-exposed variables in the block, * i.e. those variables that are used in the block prior to * any redefinition in the block. + * + * This is often called uses in Literature but we use the + * name in Engineering a Compiler. */ LiveSet UEVar; /** @@ -168,7 +172,7 @@ public boolean equals(Object o) { @Override public int hashCode() { - return Objects.hash(bid); + return bid; } public String label() { diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ChaitinGraphColoringRegisterAllocator.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ChaitinGraphColoringRegisterAllocator.java index 4838eb0..47a03b9 100644 --- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ChaitinGraphColoringRegisterAllocator.java +++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ChaitinGraphColoringRegisterAllocator.java @@ -69,10 +69,14 @@ private Map preAssignArgsToColors(CompiledFunction function, S private void updateInstructions(CompiledFunction function, Map assignments) { var regPool = function.registerPool; + // First reset the slots of every register to -1 + for (int r = 0; r < regPool.numRegisters(); r++) + regPool.getReg(r).updateSlot(-1); + // Now set the slot to the color assigned by the graph coloring algo for (var entry : assignments.entrySet()) { int reg = entry.getKey(); - int slot = entry.getValue(); - regPool.getReg(reg).updateSlot(slot); + int color = entry.getValue(); + regPool.getReg(reg).updateSlot(color); } } diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/EnterSSA.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/EnterSSA.java index 57e30e5..54afbc3 100644 --- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/EnterSSA.java +++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/EnterSSA.java @@ -17,10 +17,19 @@ public class EnterSSA { /** * Non-local names are set of variables that are live * on entry to _some_ BasicBlock in the program. + * + * Note that a variable in the Literature is represented + * by a Register in our implementation */ Register[] nonLocalNames; BBSet[] blockSets; + /** + * Basic blocks in reverse post order + */ List blocks; + /** + * Used to generate SSA version for each variable (register) + */ int[] counters; VersionStack[] stacks; @@ -28,7 +37,7 @@ public EnterSSA(CompiledFunction bytecodeFunction) { this.function = bytecodeFunction; setupGlobals(); computeDomTreeAndDominanceFrontiers(); - this.blocks = domTree.blocks; + this.blocks = domTree.blocks; // the blocks are ordered reverse post order findNonLocalNames(); insertPhis(); renameVars(); diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Operand.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Operand.java index 3f8806c..e91531e 100644 --- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Operand.java +++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Operand.java @@ -27,7 +27,7 @@ public RegisterOperand(Register reg) { if (reg == null) throw new NullPointerException(); } - public int slot() { return reg.nonSSAId(); } + public int frameSlot() { return reg.nonSSAId(); } @Override public void replaceRegister(Register register) { @@ -57,15 +57,6 @@ public String toString() { } } - /** - * Represents the return register, which is the location where - * the caller will expect to see any return value. The VM must map - * this to appropriate location. - */ - public static class ReturnRegisterOperand extends RegisterOperand { - public ReturnRegisterOperand(Register reg) { super(reg); } - } - /** * Represents a temp register, maps to a location on the * virtual stack. Temps start at offset 0, but this is a relative @@ -110,17 +101,4 @@ public String toString() { return structOperand + "." + fieldName; } } - - public static class NewTypeOperand extends Operand { - public final Type type; - public NewTypeOperand(Type type) { - this.type = type; - } - - @Override - public String toString() { - return "New(" + type + ")"; - } - } - } diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Register.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Register.java index f9f8296..6ad3258 100644 --- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Register.java +++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Register.java @@ -3,31 +3,63 @@ import com.compilerprogramming.ezlang.types.Type; /** - * Virtual register represents values that are operands of the - * IR. + * Represents a Virtual Register in the abstract machine. + * + * Virtual registers are used as operands in the instruction set of the abstract machine. + * We try to meet several requirements. + * + * When executing instructions in the Interpreter, we require the notion of a frame slot - this + * is the location of the register within the function's frame. Each executing function maps its slots to + * an ExecutionStack, which is simply an array of Values. When the function executes, the location of a + * register on the function's frame is given by the Register's slot plus the base offset of the function. + * The base offset is the location where the function's first argument appears on the execution stack, and is + * determined at runtime based on the call stack. + * + * In addition to the frame slot - for the purpose of SSA, each register must have a unique name. We use a + * unique ID as a proxy for this name. During SSA transformation we assign new IDs as we rename variables, but + * we also maintain the original ID of the register because the SSA algorithm needs to be able to track variables + * by their original names. + * + * After SSA, during register allocation, we use graph coloring to assign slots to registers. We could rename + * all registers in the instruction set again, but using a slot instead allows us to maintain the previously + * generated name - this is mainly useful as we look for relations to the variables in the source program. + * + * @see RegisterPool + * @see ChaitinGraphColoringRegisterAllocator + * @see com.compilerprogramming.ezlang.compiler.Operand.RegisterOperand */ public class Register { /** - * Unique virtual ID + * Unique virtual ID always unique */ public final int id; /** * The base name - for local variables and function params this should be the name - * in the source program. For temps this is a made up name. - * Does not include ssa version + * in the source program. For temps this is a made up name. Name is suffixed with ssa version + * during SSA transformation. + * + * Not unique. */ private final String name; /** - * The type of a register + * The type of the register */ public final Type type; - private int slot; + /** + * The location of this register relative to the base + * of the executing function. Multiple registers may share the same + * frame slot because of different non-overlapping life times. + */ + private int frameSlot; public Register(int id, String name, Type type) { + this(id,name,type,id); // Initially frame slot is set to the unique ID + } + protected Register(int id, String name, Type type, int frameSlot) { this.id = id; this.name = name; this.type = type; - this.slot = id; + this.frameSlot = frameSlot; } @Override public boolean equals(Object o) { @@ -41,15 +73,20 @@ public boolean equals(Object o) { public int hashCode() { return id; } - public String name() { return name; } + + /** + * The nonSSAID is valid as a frame slot prior to SSA conversion, + * and following register assignment. + * During SSA form this is not valid for registers that are instances of SSARegister. + */ public int nonSSAId() { - return slot; + return frameSlot; } public void updateSlot(int slot) { - this.slot = slot; + this.frameSlot = slot; } /** @@ -60,7 +97,7 @@ public static class SSARegister extends Register { public final int ssaVersion; public final int originalRegNumber; public SSARegister(Register original, int id, int version) { - super(id, original.name+"_"+version, original.type); + super(id, original.name+"_"+version, original.type, -1); this.originalRegNumber = original.id; this.ssaVersion = version; } diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/RegisterPool.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/RegisterPool.java index 0b86738..5cb9e93 100644 --- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/RegisterPool.java +++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/RegisterPool.java @@ -38,7 +38,6 @@ public Register newTempReg(String baseName, Type type) { registers.add(reg); return reg; } - public Register.SSARegister ssaReg(Register original, int version) { var id = registers.size(); var reg = new Register.SSARegister(original, id, version); @@ -48,7 +47,6 @@ public Register.SSARegister ssaReg(Register original, int version) { public int numRegisters() { return registers.size(); } - public void toStr(StringBuilder sb) { for (Register reg : registers) { sb.append("Reg #").append(reg.id).append(" ").append(reg.name()).append("\n"); diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java b/optvm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java index a3a949e..a62375f 100644 --- a/optvm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java +++ b/optvm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java @@ -49,7 +49,7 @@ public Value interpret(ExecutionStack execStack, Frame frame) { execStack.stack[base] = new Value.IntegerValue(constantOperand.value); } else if (retInst.value() instanceof Operand.RegisterOperand registerOperand) { - execStack.stack[base] = execStack.stack[base+registerOperand.slot()]; + execStack.stack[base] = execStack.stack[base+registerOperand.frameSlot()]; } else throw new IllegalStateException(); returnValue = execStack.stack[base]; @@ -58,10 +58,10 @@ else if (retInst.value() instanceof Operand.RegisterOperand registerOperand) { case Instruction.Move moveInst -> { if (moveInst.to() instanceof Operand.RegisterOperand toReg) { if (moveInst.from() instanceof Operand.RegisterOperand fromReg) { - execStack.stack[base + toReg.slot()] = execStack.stack[base + fromReg.slot()]; + execStack.stack[base + toReg.frameSlot()] = execStack.stack[base + fromReg.frameSlot()]; } else if (moveInst.from() instanceof Operand.ConstantOperand constantOperand) { - execStack.stack[base + toReg.slot()] = new Value.IntegerValue(constantOperand.value); + execStack.stack[base + toReg.frameSlot()] = new Value.IntegerValue(constantOperand.value); } else throw new IllegalStateException(); } @@ -76,7 +76,7 @@ else if (moveInst.from() instanceof Operand.ConstantOperand constantOperand) { case Instruction.ConditionalBranch cbrInst -> { boolean condition; if (cbrInst.condition() instanceof Operand.RegisterOperand registerOperand) { - Value value = execStack.stack[base + registerOperand.slot()]; + Value value = execStack.stack[base + registerOperand.frameSlot()]; if (value instanceof Value.IntegerValue integerValue) { condition = integerValue.value != 0; } @@ -102,7 +102,7 @@ else if (cbrInst.condition() instanceof Operand.ConstantOperand constantOperand) int reg = baseReg; for (Operand arg: callInst.args()) { Operand.RegisterOperand param = (Operand.RegisterOperand) arg; - execStack.stack[base + reg] = execStack.stack[base + param.slot()]; + execStack.stack[base + reg] = execStack.stack[base + param.frameSlot()]; reg += 1; } // Call function @@ -110,18 +110,18 @@ else if (cbrInst.condition() instanceof Operand.ConstantOperand constantOperand) interpret(execStack, newFrame); // Copy return value in expected location if (!(callInst.callee.returnType instanceof Type.TypeVoid)) { - execStack.stack[base + callInst.returnOperand().slot()] = execStack.stack[baseReg]; + execStack.stack[base + callInst.returnOperand().frameSlot()] = execStack.stack[baseReg]; } } case Instruction.Unary unaryInst -> { // We don't expect constant here because we fold constants in unary expressions Operand.RegisterOperand unaryOperand = (Operand.RegisterOperand) unaryInst.operand(); - Value unaryValue = execStack.stack[base + unaryOperand.slot()]; + Value unaryValue = execStack.stack[base + unaryOperand.frameSlot()]; if (unaryValue instanceof Value.IntegerValue integerValue) { switch (unaryInst.unop) { - case "-": execStack.stack[base + unaryInst.result().slot()] = new Value.IntegerValue(-integerValue.value); break; + case "-": execStack.stack[base + unaryInst.result().frameSlot()] = new Value.IntegerValue(-integerValue.value); break; // Maybe below we should explicitly set Int - case "!": execStack.stack[base + unaryInst.result().slot()] = new Value.IntegerValue(integerValue.value==0?1:0); break; + case "!": execStack.stack[base + unaryInst.result().frameSlot()] = new Value.IntegerValue(integerValue.value==0?1:0); break; default: throw new CompilerException("Invalid unary op"); } } @@ -134,12 +134,12 @@ else if (cbrInst.condition() instanceof Operand.ConstantOperand constantOperand) if (binaryInst.left() instanceof Operand.ConstantOperand constant) x = constant.value; else if (binaryInst.left() instanceof Operand.RegisterOperand registerOperand) - x = ((Value.IntegerValue) execStack.stack[base + registerOperand.slot()]).value; + x = ((Value.IntegerValue) execStack.stack[base + registerOperand.frameSlot()]).value; else throw new IllegalStateException(); if (binaryInst.right() instanceof Operand.ConstantOperand constant) y = constant.value; else if (binaryInst.right() instanceof Operand.RegisterOperand registerOperand) - y = ((Value.IntegerValue) execStack.stack[base + registerOperand.slot()]).value; + y = ((Value.IntegerValue) execStack.stack[base + registerOperand.frameSlot()]).value; else throw new IllegalStateException(); switch (binaryInst.binOp) { case "+": value = x + y; break; @@ -155,33 +155,33 @@ else if (binaryInst.right() instanceof Operand.RegisterOperand registerOperand) case ">=": value = x <= y ? 1 : 0; break; default: throw new IllegalStateException(); } - execStack.stack[base + binaryInst.result().slot()] = new Value.IntegerValue(value); + execStack.stack[base + binaryInst.result().frameSlot()] = new Value.IntegerValue(value); } case Instruction.NewArray newArrayInst -> { - execStack.stack[base + newArrayInst.destOperand().slot()] = new Value.ArrayValue(newArrayInst.type); + execStack.stack[base + newArrayInst.destOperand().frameSlot()] = new Value.ArrayValue(newArrayInst.type); } case Instruction.NewStruct newStructInst -> { - execStack.stack[base + newStructInst.destOperand().slot()] = new Value.StructValue(newStructInst.type); + execStack.stack[base + newStructInst.destOperand().frameSlot()] = new Value.StructValue(newStructInst.type); } case Instruction.AStoreAppend arrayAppendInst -> { - Value.ArrayValue arrayValue = (Value.ArrayValue) execStack.stack[base + arrayAppendInst.array().slot()]; + Value.ArrayValue arrayValue = (Value.ArrayValue) execStack.stack[base + arrayAppendInst.array().frameSlot()]; if (arrayAppendInst.value() instanceof Operand.ConstantOperand constant) { arrayValue.values.add(new Value.IntegerValue(constant.value)); } else if (arrayAppendInst.value() instanceof Operand.RegisterOperand registerOperand) { - arrayValue.values.add(execStack.stack[base + registerOperand.slot()]); + arrayValue.values.add(execStack.stack[base + registerOperand.frameSlot()]); } else throw new IllegalStateException(); } case Instruction.ArrayStore arrayStoreInst -> { if (arrayStoreInst.arrayOperand() instanceof Operand.RegisterOperand arrayOperand) { - Value.ArrayValue arrayValue = (Value.ArrayValue) execStack.stack[base + arrayOperand.slot()]; + Value.ArrayValue arrayValue = (Value.ArrayValue) execStack.stack[base + arrayOperand.frameSlot()]; int index = 0; if (arrayStoreInst.indexOperand() instanceof Operand.ConstantOperand constant) { index = (int) constant.value; } else if (arrayStoreInst.indexOperand() instanceof Operand.RegisterOperand registerOperand) { - Value.IntegerValue indexValue = (Value.IntegerValue) execStack.stack[base + registerOperand.slot()]; + Value.IntegerValue indexValue = (Value.IntegerValue) execStack.stack[base + registerOperand.frameSlot()]; index = (int) indexValue.value; } else throw new IllegalStateException(); @@ -190,7 +190,7 @@ else if (arrayStoreInst.indexOperand() instanceof Operand.RegisterOperand regist value = new Value.IntegerValue(constantOperand.value); } else if (arrayStoreInst.sourceOperand() instanceof Operand.RegisterOperand registerOperand) { - value = execStack.stack[base + registerOperand.slot()]; + value = execStack.stack[base + registerOperand.frameSlot()]; } else throw new IllegalStateException(); arrayValue.values.set(index, value); @@ -198,27 +198,27 @@ else if (arrayStoreInst.sourceOperand() instanceof Operand.RegisterOperand regis } case Instruction.ArrayLoad arrayLoadInst -> { if (arrayLoadInst.arrayOperand() instanceof Operand.RegisterOperand arrayOperand) { - Value.ArrayValue arrayValue = (Value.ArrayValue) execStack.stack[base + arrayOperand.slot()]; + Value.ArrayValue arrayValue = (Value.ArrayValue) execStack.stack[base + arrayOperand.frameSlot()]; if (arrayLoadInst.indexOperand() instanceof Operand.ConstantOperand constant) { - execStack.stack[base + arrayLoadInst.destOperand().slot()] = arrayValue.values.get((int) constant.value); + execStack.stack[base + arrayLoadInst.destOperand().frameSlot()] = arrayValue.values.get((int) constant.value); } else if (arrayLoadInst.indexOperand() instanceof Operand.RegisterOperand registerOperand) { - Value.IntegerValue index = (Value.IntegerValue) execStack.stack[base + registerOperand.slot()]; - execStack.stack[base + arrayLoadInst.destOperand().slot()] = arrayValue.values.get((int) index.value); + Value.IntegerValue index = (Value.IntegerValue) execStack.stack[base + registerOperand.frameSlot()]; + execStack.stack[base + arrayLoadInst.destOperand().frameSlot()] = arrayValue.values.get((int) index.value); } else throw new IllegalStateException(); } else throw new IllegalStateException(); } case Instruction.SetField setFieldInst -> { if (setFieldInst.structOperand() instanceof Operand.RegisterOperand structOperand) { - Value.StructValue structValue = (Value.StructValue) execStack.stack[base + structOperand.slot()]; + Value.StructValue structValue = (Value.StructValue) execStack.stack[base + structOperand.frameSlot()]; int index = setFieldInst.fieldIndex; Value value; if (setFieldInst.sourceOperand() instanceof Operand.ConstantOperand constant) { value = new Value.IntegerValue(constant.value); } else if (setFieldInst.sourceOperand() instanceof Operand.RegisterOperand registerOperand) { - value = execStack.stack[base + registerOperand.slot()]; + value = execStack.stack[base + registerOperand.frameSlot()]; } else throw new IllegalStateException(); structValue.fields[index] = value; @@ -226,9 +226,9 @@ else if (setFieldInst.sourceOperand() instanceof Operand.RegisterOperand registe } case Instruction.GetField getFieldInst -> { if (getFieldInst.structOperand() instanceof Operand.RegisterOperand structOperand) { - Value.StructValue structValue = (Value.StructValue) execStack.stack[base + structOperand.slot()]; + Value.StructValue structValue = (Value.StructValue) execStack.stack[base + structOperand.frameSlot()]; int index = getFieldInst.fieldIndex; - execStack.stack[base + getFieldInst.destOperand().slot()] = structValue.fields[index]; + execStack.stack[base + getFieldInst.destOperand().frameSlot()] = structValue.fields[index]; } else throw new IllegalStateException(); } case Instruction.ArgInstruction argInst -> {}