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 a57fc2b..ccb8b09 100644 --- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java +++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java @@ -111,10 +111,10 @@ public void deleteInstruction(Instruction instruction) { instructions.remove(instruction); } public void addSuccessor(BasicBlock successor) { - assert successors.contains(successor) == false; - successors.add(successor); - assert successor.predecessors.contains(this) == false; - successor.predecessors.add(this); + if (!successors.contains(successor)) { + successors.add(successor); + successor.predecessors.add(this); + } } public void removeSuccessor(BasicBlock successor) { successors.remove(successor); diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java index 2eaf92b..0d577d2 100644 --- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java +++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java @@ -7,9 +7,7 @@ import com.compilerprogramming.ezlang.types.Type; import com.compilerprogramming.ezlang.types.TypeDictionary; -import java.util.ArrayList; -import java.util.BitSet; -import java.util.List; +import java.util.*; public class CompiledFunction { @@ -27,6 +25,7 @@ public class CompiledFunction { public boolean isSSA; public boolean hasLiveness; + private final IncrementalSSA issa; /** * We essentially do a form of abstract interpretation as we generate @@ -38,10 +37,11 @@ public class CompiledFunction { */ private List virtualStack = new ArrayList<>(); - public CompiledFunction(Symbol.FunctionTypeSymbol functionSymbol, TypeDictionary typeDictionary) { + public CompiledFunction(Symbol.FunctionTypeSymbol functionSymbol, TypeDictionary typeDictionary, EnumSet options) { AST.FuncDecl funcDecl = (AST.FuncDecl) functionSymbol.functionDecl; this.functionType = (Type.TypeFunction) functionSymbol.type; this.registerPool = new RegisterPool(); + this.issa = (options != null && options.contains(Options.ISSA)) ? new IncrementalSSABraun(this) : new NoopIncrementalSSA(); setVirtualRegisters(funcDecl.scope); this.BID = 0; this.entry = this.currentBlock = createBlock(); @@ -52,18 +52,22 @@ public CompiledFunction(Symbol.FunctionTypeSymbol functionSymbol, TypeDictionary generateArgInstructions(funcDecl.scope); compileStatement(funcDecl.block); exitBlockIfNeeded(); + issa.finish(options); this.frameSlots = registerPool.numRegisters(); } - + public CompiledFunction(Symbol.FunctionTypeSymbol functionSymbol, TypeDictionary typeDictionary) { + this(functionSymbol,typeDictionary,null); + } public CompiledFunction(Type.TypeFunction functionType, TypeDictionary typeDictionary) { this.functionType = (Type.TypeFunction) functionType; this.registerPool = new RegisterPool(); - this.BID = 0; + this.issa = new NoopIncrementalSSA(); this.BID = 0; this.entry = this.currentBlock = createBlock(); this.exit = createBlock(); this.currentBreakTarget = null; this.currentContinueTarget = null; this.typeDictionary = typeDictionary; + issa.finish(null); this.frameSlots = registerPool.numRegisters(); } @@ -71,7 +75,7 @@ private void generateArgInstructions(Scope scope) { if (scope.isFunctionParameterScope) { for (Symbol symbol: scope.getLocalSymbols()) { if (symbol instanceof Symbol.ParameterSymbol parameterSymbol) { - code(new Instruction.ArgInstruction(new Operand.LocalRegisterOperand(registerPool.getReg(parameterSymbol.regNumber)))); + codeArg(new Operand.LocalRegisterOperand(registerPool.getReg(parameterSymbol.regNumber), parameterSymbol)); } } } @@ -122,7 +126,7 @@ private void compileReturn(AST.ReturnStmt returnStmt) { if (isIndexed) codeIndexedLoad(); if (virtualStack.size() == 1) - code(new Instruction.Ret(pop())); + codeReturn(pop()); else if (virtualStack.size() > 1) throw new CompilerException("Virtual stack has more than one item at return"); } @@ -179,7 +183,7 @@ private void compileAssign(AST.AssignStmt assignStmt) { codeIndexedStore(); else if (assignStmt.lhs instanceof AST.NameExpr symbolExpr) { Symbol.VarSymbol varSymbol = (Symbol.VarSymbol) symbolExpr.symbol; - code(new Instruction.Move(pop(), new Operand.LocalRegisterOperand(registerPool.getReg(varSymbol.regNumber)))); + codeMove(pop(), new Operand.LocalRegisterOperand(registerPool.getReg(varSymbol.regNumber), varSymbol)); } else throw new CompilerException("Invalid assignment expression: " + assignStmt.lhs); @@ -196,34 +200,37 @@ private void compileExprStmt(AST.ExprStmt exprStmt) { private void compileContinue(AST.ContinueStmt continueStmt) { if (currentContinueTarget == null) throw new CompilerException("No continue target found"); + assert !issa.isSealed(currentContinueTarget); jumpTo(currentContinueTarget); } private void compileBreak(AST.BreakStmt breakStmt) { if (currentBreakTarget == null) throw new CompilerException("No break target found"); + assert !issa.isSealed(currentBreakTarget); jumpTo(currentBreakTarget); } private void compileWhile(AST.WhileStmt whileStmt) { - BasicBlock loopBlock = createLoopHead(); + BasicBlock loopHead = createLoopHead(); BasicBlock bodyBlock = createBlock(); BasicBlock exitBlock = createBlock(); BasicBlock savedBreakTarget = currentBreakTarget; BasicBlock savedContinueTarget = currentContinueTarget; currentBreakTarget = exitBlock; - currentContinueTarget = loopBlock; - startBlock(loopBlock); + currentContinueTarget = loopHead; + startBlock(loopHead); // ISSA cannot seal until all back edges done boolean indexed = compileExpr(whileStmt.condition); if (indexed) codeIndexedLoad(); - code(new Instruction.ConditionalBranch(currentBlock, pop(), bodyBlock, exitBlock)); + codeCBR(currentBlock, pop(), bodyBlock, exitBlock); assert vstackEmpty(); - startBlock(bodyBlock); + startSealedBlock(bodyBlock); // ISSA If we seal this here fib test fails, wrong code generated, why? compileStatement(whileStmt.stmt); if (!isBlockTerminated(currentBlock)) - jumpTo(loopBlock); - startBlock(exitBlock); + jumpTo(loopHead); + issa.sealBlock(loopHead); + startSealedBlock(exitBlock); currentContinueTarget = savedContinueTarget; currentBreakTarget = savedBreakTarget; } @@ -235,6 +242,7 @@ private boolean isBlockTerminated(BasicBlock block) { public void jumpTo(BasicBlock block) { assert !isBlockTerminated(currentBlock); + assert !issa.isSealed(block); currentBlock.add(new Instruction.Jump(block)); currentBlock.addSuccessor(block); } @@ -245,28 +253,32 @@ public void startBlock(BasicBlock block) { } currentBlock = block; } - + public void startSealedBlock(BasicBlock block) { + startBlock(block); + assert !issa.isSealed(currentBlock); + issa.sealBlock(currentBlock); + } private void compileIf(AST.IfElseStmt ifElseStmt) { - BasicBlock ifBlock = createBlock(); + BasicBlock thenBlock = createBlock(); boolean needElse = ifElseStmt.elseStmt != null; BasicBlock elseBlock = needElse ? createBlock() : null; BasicBlock exitBlock = createBlock(); boolean indexed = compileExpr(ifElseStmt.condition); if (indexed) codeIndexedLoad(); - code(new Instruction.ConditionalBranch(currentBlock, pop(), ifBlock, needElse ? elseBlock : exitBlock)); + codeCBR(currentBlock, pop(), thenBlock, needElse ? elseBlock : exitBlock); assert vstackEmpty(); - startBlock(ifBlock); + startSealedBlock(thenBlock); // ISSA seal immediately compileStatement(ifElseStmt.ifStmt); if (!isBlockTerminated(currentBlock)) jumpTo(exitBlock); if (elseBlock != null) { - startBlock(elseBlock); + startSealedBlock(elseBlock); // ISSA seal immediately compileStatement(ifElseStmt.elseStmt); if (!isBlockTerminated(currentBlock)) jumpTo(exitBlock); } - startBlock(exitBlock); + startSealedBlock(exitBlock); // ISSA seal immediately } private void compileLet(AST.VarStmt letStmt) { @@ -274,7 +286,7 @@ private void compileLet(AST.VarStmt letStmt) { boolean indexed = compileExpr(letStmt.expr); if (indexed) codeIndexedLoad(); - code(new Instruction.Move(pop(), new Operand.LocalRegisterOperand(registerPool.getReg(letStmt.symbol.regNumber)))); + codeMove(pop(), new Operand.LocalRegisterOperand(registerPool.getReg(letStmt.symbol.regNumber), letStmt.symbol)); } } @@ -328,7 +340,7 @@ private boolean compileCallExpr(AST.CallExpr callExpr) { if (!(arg instanceof Operand.TempRegisterOperand) ) { var origArg = pop(); arg = createTemp(origArg.type); - code(new Instruction.Move(origArg, arg)); + codeMove(origArg, arg); } args.add((Operand.RegisterOperand) arg); } @@ -339,9 +351,8 @@ private boolean compileCallExpr(AST.CallExpr callExpr) { if (callExpr.callee.type instanceof Type.TypeFunction tf && !(tf.returnType instanceof Type.TypeVoid)) { ret = createTemp(tf.returnType); - //assert ret.regnum-maxLocalReg == returnStackPos; } - code(new Instruction.Call(returnStackPos, ret, calleeType, args.toArray(new Operand.RegisterOperand[args.size()]))); + codeCall(returnStackPos, ret, calleeType, args.toArray(new Operand.RegisterOperand[args.size()])); return false; } @@ -419,7 +430,7 @@ private boolean compileSymbolExpr(AST.NameExpr symbolExpr) { pushOperand(new Operand.LocalFunctionOperand(functionType)); else { Symbol.VarSymbol varSymbol = (Symbol.VarSymbol) symbolExpr.symbol; - pushLocal(registerPool.getReg(varSymbol.regNumber)); + pushLocal(registerPool.getReg(varSymbol.regNumber), varSymbol); } return false; } @@ -433,28 +444,27 @@ private boolean codeBoolean(AST.BinaryExpr binaryExpr) { if (indexed) codeIndexedLoad(); if (isAnd) { - code(new Instruction.ConditionalBranch(currentBlock, pop(), l1, l2)); + codeCBR(currentBlock, pop(), l1, l2); } else { - code(new Instruction.ConditionalBranch(currentBlock, pop(), l2, l1)); + codeCBR(currentBlock, pop(), l2, l1); } - startBlock(l1); + startSealedBlock(l1); // ISSA seal immediately compileExpr(binaryExpr.expr2); var temp = ensureTemp(); jumpTo(l3); - startBlock(l2); + startSealedBlock(l2); // ISSA seal immediately // Below we must write to the same temp - code(new Instruction.Move(new Operand.ConstantOperand(isAnd ? 0 : 1, typeDictionary.INT), temp)); + codeMove(new Operand.ConstantOperand(isAnd ? 0 : 1, typeDictionary.INT), temp); jumpTo(l3); - startBlock(l3); + startSealedBlock(l3); // ISSA seal immediately // leave temp on virtual stack return false; } - private boolean compileBinaryExpr(AST.BinaryExpr binaryExpr) { String opCode = binaryExpr.op.str; if (opCode.equals("&&") || - opCode.equals("||")) { + opCode.equals("||")) { return codeBoolean(binaryExpr); } boolean indexed = compileExpr(binaryExpr.expr1); @@ -466,7 +476,7 @@ private boolean compileBinaryExpr(AST.BinaryExpr binaryExpr) { Operand right = pop(); Operand left = pop(); if (left instanceof Operand.NullConstantOperand && - right instanceof Operand.NullConstantOperand) { + right instanceof Operand.NullConstantOperand) { long value = 0; switch (opCode) { case "==": value = 1; break; @@ -496,7 +506,7 @@ else if (left instanceof Operand.ConstantOperand leftconstant && } else { var temp = createTemp(binaryExpr.type); - code(new Instruction.Binary(opCode, temp, left, right)); + codeBinary(opCode, temp, left, right); } return false; } @@ -518,7 +528,7 @@ private boolean compileUnaryExpr(AST.UnaryExpr unaryExpr) { } else { var temp = createTemp(unaryExpr.type); - code(new Instruction.Unary(opCode, temp, top)); + codeUnary(opCode, temp, top); } return false; } @@ -559,7 +569,7 @@ else if (operand instanceof Operand.RegisterOperand registerOperand) private Operand.TempRegisterOperand createTempAndMove(Operand src) { Type type = typeOfOperand(src); var temp = createTemp(type); - code(new Instruction.Move(src, temp)); + codeMove(src, temp); return temp; } @@ -576,8 +586,8 @@ private Operand.RegisterOperand ensureTemp() { } else throw new CompilerException("Cannot convert to temporary register"); } - private void pushLocal(Register reg) { - pushOperand(new Operand.LocalRegisterOperand(reg)); + private void pushLocal(Register reg, Symbol.VarSymbol varSymbol) { + pushOperand(new Operand.LocalRegisterOperand(reg, varSymbol)); } private void pushOperand(Operand operand) { @@ -596,13 +606,13 @@ private Operand.TempRegisterOperand codeIndexedLoad() { Operand indexed = pop(); var temp = createTemp(indexed.type); if (indexed instanceof Operand.LoadIndexedOperand loadIndexedOperand) { - code(new Instruction.ArrayLoad(loadIndexedOperand, temp)); + codeArrayLoad(loadIndexedOperand, temp); } else if (indexed instanceof Operand.LoadFieldOperand loadFieldOperand) { - code(new Instruction.GetField(loadFieldOperand, temp)); + codeGetField(loadFieldOperand, temp); } else - code(new Instruction.Move(indexed, temp)); + codeMove(indexed, temp); return temp; } @@ -610,30 +620,158 @@ private void codeIndexedStore() { Operand value = pop(); Operand indexed = pop(); if (indexed instanceof Operand.LoadIndexedOperand loadIndexedOperand) { - code(new Instruction.ArrayStore(value, loadIndexedOperand)); + codeArrayStore(value, loadIndexedOperand); } else if (indexed instanceof Operand.LoadFieldOperand loadFieldOperand) { - code(new Instruction.SetField(value, loadFieldOperand)); + codeSetField(value, loadFieldOperand); } else - code(new Instruction.Move(value, indexed)); + codeMove(value, indexed); } private void codeNew(Type type) { - var temp = createTemp(type); - if (type instanceof Type.TypeArray typeArray) { - code(new Instruction.NewArray(typeArray, temp)); - } - else if (type instanceof Type.TypeStruct typeStruct) { - code(new Instruction.NewStruct(typeStruct, temp)); - } + if (type instanceof Type.TypeArray typeArray) + codeNewArray(typeArray); + else if (type instanceof Type.TypeStruct typeStruct) + codeNewStruct(typeStruct); else throw new CompilerException("Unexpected type: " + type); } + private void codeNewArray(Type.TypeArray typeArray) { + var temp = createTemp(typeArray); + var target = (Operand.RegisterOperand) issa.write(temp); + var insn = new Instruction.NewArray(typeArray, target); + issa.recordDef(target, insn); + code(insn); + } + + private void codeNewStruct(Type.TypeStruct typeStruct) { + var temp = createTemp(typeStruct); + var target = (Operand.RegisterOperand) issa.write(temp); + var insn = new Instruction.NewStruct(typeStruct, target); + issa.recordDef(target, insn); + code(insn); + } + private void codeStoreAppend() { - var operand = pop(); - code(new Instruction.AStoreAppend((Operand.RegisterOperand) top(), operand)); + var operand = issa.read(pop()); + Operand.RegisterOperand arrayOperand = (Operand.RegisterOperand) issa.read(top()); + var insn = new Instruction.AStoreAppend(arrayOperand, operand); + issa.recordUse(arrayOperand, insn); + issa.recordUse(operand, insn); + code(insn); + } + + private void codeArg(Operand.LocalRegisterOperand target) { + var newtarget = (Operand.RegisterOperand) issa.write(target); + var insn = new Instruction.ArgInstruction(newtarget); + issa.recordDef(newtarget, insn); + code(insn); + } + + private void codeMove(Operand srcOperand, Operand destOperand) { + srcOperand = issa.read(srcOperand); + destOperand = issa.write(destOperand); + var insn = new Instruction.Move(srcOperand, destOperand); + issa.recordDef(destOperand, insn); + issa.recordUse(srcOperand, insn); + code(insn); + } + + private void codeReturn(Operand resultOperand) { + resultOperand = issa.read(resultOperand); + var insn = new Instruction.Ret(resultOperand); + issa.recordUse(resultOperand, insn); + code(insn); + } + + private void codeCBR(BasicBlock block, Operand condition, BasicBlock trueBlock, BasicBlock falseBlock) { + condition = issa.read(condition); + var insn = new Instruction.ConditionalBranch(block, condition, trueBlock, falseBlock); + assert !issa.isSealed(trueBlock); + assert !issa.isSealed(falseBlock); + block.addSuccessor(trueBlock); + block.addSuccessor(falseBlock); + issa.recordUse(condition, insn); + code(insn); + } + + private void codeCall(int newBase, + Operand.RegisterOperand targetOperand, + Type.TypeFunction calleeType, + Operand.RegisterOperand ...arguments) { + if (targetOperand != null) + targetOperand = (Operand.RegisterOperand) issa.write(targetOperand); + Operand.RegisterOperand args[] = new Operand.RegisterOperand[arguments.length]; + for (int i = 0; i < args.length; i++) { + args[i] = (Operand.RegisterOperand) issa.read(arguments[i]); + } + var insn = new Instruction.Call(newBase, targetOperand, calleeType, args); + for (int i = 0; i < args.length; i++) { + issa.recordUse(args[i], insn); + } + if (targetOperand != null) + issa.recordDef(targetOperand, insn); + code(insn); + } + + private void codeUnary(String opCode, Operand.RegisterOperand result, Operand operand) { + operand = issa.read(operand); + result = (Operand.RegisterOperand) issa.write(result); + var insn = new Instruction.Unary(opCode, result, operand); + issa.recordDef(result, insn); + issa.recordUse(operand, insn); + code(insn); + } + + private void codeBinary(String opCode, Operand.RegisterOperand result, Operand left, Operand right) { + left = issa.read(left); + right = issa.read(right); + result = (Operand.RegisterOperand) issa.write(result); + var insn = new Instruction.Binary(opCode, result, left, right); + issa.recordDef(result, insn); + issa.recordUse(left, insn); + issa.recordUse(right, insn); + code(insn); + } + + private void codeArrayLoad(Operand.LoadIndexedOperand loadIndexedOperand, Operand.RegisterOperand target) { + loadIndexedOperand = new Operand.LoadIndexedOperand(issa.read(loadIndexedOperand.arrayOperand), issa.read(loadIndexedOperand.indexOperand)); + target = (Operand.RegisterOperand) issa.write(target); + var insn = new Instruction.ArrayLoad(loadIndexedOperand, target); + issa.recordDef(target, insn); + issa.recordUse(loadIndexedOperand.arrayOperand, insn); + issa.recordUse(loadIndexedOperand.indexOperand, insn); + code(insn); + } + + private void codeGetField(Operand.LoadFieldOperand loadFieldOperand, Operand.RegisterOperand target) { + loadFieldOperand = new Operand.LoadFieldOperand(issa.read(loadFieldOperand.structOperand), loadFieldOperand.fieldName, loadFieldOperand.fieldIndex); + target = (Operand.RegisterOperand) issa.write(target); + var insn = new Instruction.GetField(loadFieldOperand, target); + issa.recordDef(target, insn); + issa.recordUse(loadFieldOperand.structOperand, insn); + code(insn); + } + + private void codeArrayStore(Operand value, Operand.LoadIndexedOperand loadIndexedOperand) { + loadIndexedOperand = new Operand.LoadIndexedOperand(issa.read(loadIndexedOperand.arrayOperand), issa.read(loadIndexedOperand.indexOperand)); + value = issa.read(value); + var insn = new Instruction.ArrayStore(value, loadIndexedOperand); + issa.recordUse(loadIndexedOperand.arrayOperand, insn); + issa.recordUse(loadIndexedOperand.indexOperand, insn); + issa.recordUse(value, insn); + code(insn); + } + + private void codeSetField(Operand value, Operand.LoadFieldOperand loadFieldOperand) { + loadFieldOperand = new Operand.LoadFieldOperand(issa.read(loadFieldOperand.structOperand), loadFieldOperand.fieldName, loadFieldOperand.fieldIndex); + value = issa.read(value); + var insn = new Instruction.SetField(value, loadFieldOperand); + issa.recordUse(loadFieldOperand.structOperand, insn); + issa.recordUse(value, insn); + code(insn); } private boolean vstackEmpty() { @@ -677,4 +815,307 @@ public StringBuilder toDot(StringBuilder sb, boolean verbose) { sb.append("}\n"); return sb; } + + interface IncrementalSSA { + Operand read(Operand operand); + Operand write(Operand operand); + void recordUse(Operand operand, Instruction instruction); + void recordDef(Register reg, Instruction instruction); + void recordDef(Operand operand, Instruction instruction); + void sealBlock(BasicBlock block); + boolean isSealed(BasicBlock block); + void finish(EnumSet options); + } + + /** + * Support for AST to SSA IR using Braun's method. + * See Simple and Efficient Construction of Static Single Assignment Form, 2013 + * Matthias Braun, Sebastian Buchwald, Sebastian Hack, Roland Leißa + */ + static final class IncrementalSSABraun implements IncrementalSSA { + + CompiledFunction function; + + // For each unique variable (variable with same name in different scopes must be distinct) + // a mapping is maintained for the name to SSA value (virtual register) in each block. + // we could add this mapping to the BB itself but it seems nicer to keep it separate + // at least for now. + // Note that we use the nonSSAId as proxy for variable name + // because this id is unique for non-SSA variables, but for SSA versions this refers + // back to the original ID + Map> currentDef = new HashMap<>(); + // Flags blocks that are completed in terms of instruction generation + BitSet sealedBlocks = new BitSet(); + // Tracks Phis that are not finalized because the basic block is not yet sealed + Map> incompletePhis = new HashMap<>(); + + // Not explicitly stated in the paper but implicit in the algo is + // the availability of Def-use chains. We have to main this incrementally as we + // generate code - used when eliminating trivial phis + Map ssaDefUses = new HashMap<>(); + + // This is not part of the spec, it is just an implementation detail + // We pre-assign registers to local declared vars, but then the SSA part + // creates new ones. However, the first time a variable is versioned we could just use the + // original regnum. + // This set tracks whether a version is being created first time + // It also helps us maintain version numbers similar to traditional SSA + Map versioned = new HashMap<>(); + + private IncrementalSSABraun(CompiledFunction function) { + this.function = function; + } + + /** + * Associates a new definition (value) to a variable name within a basic block + */ + private void writeVariable(Register variable, BasicBlock block, Register value) { + currentDef.computeIfAbsent(variable.nonSSAId(), k -> new HashMap<>()).put(block, value); + } + + /** + * Looks up the current SSA value (virtual register) associated with a name, inside a block. + * If no mapping is found, processing depends on status of the block. + * @see #readVariableRecursive(Register, BasicBlock) + */ + private Register readVariable(Register variable, BasicBlock block) { + Map defs = currentDef.get(variable.nonSSAId()); + if (defs != null && defs.containsKey(block)) { + // local value numbering + return defs.get(block); + } + // global value numbering + return readVariableRecursive(variable, block); + } + + /** + * Called when a block does not have a mapping for a variable. + * If the block is still under construction, then a Phi is inserted into the + * block - and the phi is marked as incomplete. + * If block is constructed, then we look at predecessors for definitions; + * in case of more than 1 predecessor a phi is created with input + * obtained recursively via each predecessor block. + * In case of 1 predecessor the value is read recursively from that predecessor. + */ + private Register readVariableRecursive(Register variable, BasicBlock block) { + Register val; + if (!isSealed(block)) { + // incomplete CFG + val = makeVersion(variable); + Instruction.Phi phi = makePhi(val, block); + incompletePhis.computeIfAbsent(block, k -> new HashMap<>()).put(variable, phi); + } + else if (block.predecessors.size() == 1) { + // Optimize the common case of one predecessor: No phi needed + val = readVariable(variable, block.predecessors.get(0)); + } + else { + // Break potential cycles with operandless phis + val = makeVersion(variable); + Instruction.Phi phi = makePhi(val, block); + writeVariable(variable, block, val); + val = addPhiOperands(variable,phi); + } + writeVariable(variable, block, val); + return val; + } + + private Instruction.Phi makePhi(Register val, BasicBlock block) { + Instruction.Phi phi = new Instruction.Phi(val, new ArrayList<>()); + recordDef(val, phi); + block.add(0, phi); + return phi; + } + + /** + * Populate the members of a phi instruction + */ + private Register addPhiOperands(Register variable, Instruction.Phi phi) { + assert phi.numInputs() == 0; + // Determine operands from predecessors + for (BasicBlock pred: phi.block.predecessors) { + phi.addInput(readVariable(variable,pred)); + } + return tryRemovingPhi(phi); + } + + private Register tryRemovingPhi(Instruction.Phi phi) { + Register same = null; + // Check if phi has distinct inputs + for (int i = 0; i < phi.numInputs(); i++) { + if (!phi.isRegisterInput(i)) + // Cannot happen? + throw new IllegalStateException(); + var use = phi.inputAsRegister(i); + if (use.equals(same) || use.equals(phi.value())) + continue; // Unique value or self reference + if (same != null) + // More than 1 distinct value, so keep phi + return phi.value(); + same = use; + } + if (same == null) { + // phi is unreachable or in the start block + // Paper suggests we create an Undef, but we throw an exception + // same = function.registerPool.newReg("Undef", null); + throw new CompilerException("Undefined value for phi " + phi.value()); + } + // remember uses except phi + var users = getUsesExcept(phi); + // remove all uses of phi to same and remove phi + replacePhiValueAndUsers(phi, same); + phi.block.deleteInstruction(phi); + // try to recursively remove all phi users, which might have become trivial + for (var use: users) { + if (use instanceof Instruction.Phi phiuser) + tryRemovingPhi(phiuser); + } + return same; + } + + /** + * Reroute all uses of phi to new value + */ + private void replacePhiValueAndUsers(Instruction.Phi phi, Register newValue) { + var oldDefUseChain = ssaDefUses.get(phi.value()); + var newDefUseChain = ssaDefUses.get(newValue); + if (newDefUseChain == null) { + // Can be null because this may be existing def + newDefUseChain = SSAEdges.addDef(ssaDefUses, newValue, phi); + } + if (oldDefUseChain != null) { + for (Instruction instruction: oldDefUseChain.useList) { + if (instruction instanceof Instruction.Phi somePhi) { + somePhi.replaceInput(phi.value(), newValue); + } + else { + instruction.replaceUse(phi.value(), newValue); + } + } + // Users of phi old value become users of the new value + newDefUseChain.useList.addAll(oldDefUseChain.useList); + oldDefUseChain.useList.clear(); + // FIXME remove old def from def-use chains + } + } + + private List getUsesExcept(Instruction.Phi phi) { + var oldDefUseChain = ssaDefUses.get(phi.value()); + if (oldDefUseChain == null) { + return new ArrayList<>(); + } + var useList = new ArrayList<>(oldDefUseChain.useList); + useList.remove(phi); + return useList; + } + + @Override + public Operand read(Operand operand) { + // We have to consider temps too because of boolean expressions + // where temps are not SSA + if (operand instanceof Operand.RegisterOperand localRegisterOperand) { + var reg = readVariable(localRegisterOperand.reg, function.currentBlock); + operand = new Operand.RegisterOperand(reg); + } + return operand; + } + @Override + public Operand write(Operand operand) { + // We have to consider temps too because of boolean expressions + // where temps are not SSA + if (operand instanceof Operand.RegisterOperand localRegisterOperand) { + var variable = localRegisterOperand.reg; + Register newValue = makeVersion(variable); + writeVariable(variable, function.currentBlock, newValue); + operand = new Operand.RegisterOperand(newValue); + } + return operand; + } + + private Register makeVersion(Register variable) { + Register newValue; + Integer version = versioned.get(variable.nonSSAId()); + // Avoid creating a new value first time because we already + // have a pre-created register we can use + if (version == null) { + newValue = variable; + versioned.put(variable.nonSSAId(), 1); + } + else { + versioned.put(variable.nonSSAId(), version + 1); + newValue = function.registerPool.ssaReg(variable, version); + } + return newValue; + } + + @Override + public void recordUse(Operand operand, Instruction instruction) { + if (operand instanceof Operand.RegisterOperand registerOperand) { + SSAEdges.recordUse(ssaDefUses, instruction, registerOperand.reg); + } + } + @Override + public void recordDef(Register reg, Instruction instruction) { + SSAEdges.recordDef(ssaDefUses, reg, instruction); + } + @Override + public void recordDef(Operand operand, Instruction instruction) { + if (operand instanceof Operand.RegisterOperand registerOperand) { + SSAEdges.recordDef(ssaDefUses, registerOperand.reg, instruction); + } + } + @Override + public void sealBlock(BasicBlock block) { + if (isSealed(block)) + return; + var pendingPhis = incompletePhis.remove(block); + if (pendingPhis != null) { + for (var variable : pendingPhis.keySet()) { + addPhiOperands(variable, pendingPhis.get(variable)); + } + } + sealedBlocks.set(block.bid); + } + @Override + public boolean isSealed(BasicBlock block) { + return sealedBlocks.get(block.bid); + } + @Override + public void finish(EnumSet options) { + function.isSSA = true; + if (options != null && options.contains(Options.DUMP_SSA_IR)) { + function.dumpIR(false, "Post SSA IR"); + } + } + } + + static final class NoopIncrementalSSA implements IncrementalSSA { + @Override + public Operand read(Operand operand) { + return operand; + } + @Override + public Operand write(Operand operand) { + return operand; + } + @Override + public void recordUse(Operand operand, Instruction instruction) { + } + @Override + public void recordDef(Register reg, Instruction instruction) { + } + @Override + public void recordDef(Operand operand, Instruction instruction) { + } + @Override + public void sealBlock(BasicBlock block) { + } + @Override + public boolean isSealed(BasicBlock block) { + return false; + } + @Override + public void finish(EnumSet options) { + } + } } diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java index 4c24bb6..b15a157 100644 --- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java +++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java @@ -16,7 +16,7 @@ private void compile(TypeDictionary typeDictionary, EnumSet options) { for (Symbol symbol: typeDictionary.getLocalSymbols()) { if (symbol instanceof Symbol.FunctionTypeSymbol functionSymbol) { Type.TypeFunction functionType = (Type.TypeFunction) functionSymbol.type; - var function = new CompiledFunction(functionSymbol, typeDictionary); + var function = new CompiledFunction(functionSymbol, typeDictionary, options); if (options.contains(Options.DUMP_INITIAL_IR)) function.dumpIR(false, "Initial IR"); functionType.code = function; diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java index 1651c1c..7bb016b 100644 --- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java +++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java @@ -290,8 +290,6 @@ public ConditionalBranch(BasicBlock currentBlock, Operand condition, BasicBlock super(I_CBR, (Operand.RegisterOperand) null, condition); this.trueBlock = trueBlock; this.falseBlock = falseBlock; - currentBlock.addSuccessor(trueBlock); - currentBlock.addSuccessor(falseBlock); } public Operand condition() { return uses[0]; } @Override @@ -426,6 +424,22 @@ public StringBuilder toStr(StringBuilder sb) { sb.append(")"); return sb; } + public void addInput(Register register) { + var newUses = new Operand[uses.length + 1]; + System.arraycopy(uses, 0, newUses, 0, uses.length); + newUses[newUses.length-1] = new Operand.RegisterOperand(register); + this.uses = newUses; + } + public void replaceInput(Register oldReg, Register newReg) { + for (int i = 0; i < numInputs(); i++) { + if (isRegisterInput(i)) { + Register in = inputAsRegister(i); + if (in.equals(oldReg)) { + replaceInput(i, newReg); + } + } + } + } } public static class ArgInstruction extends Instruction { 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 57a691e..8ff84e4 100644 --- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Operand.java +++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Operand.java @@ -1,5 +1,6 @@ package com.compilerprogramming.ezlang.compiler; +import com.compilerprogramming.ezlang.types.Symbol; import com.compilerprogramming.ezlang.types.Type; public class Operand { @@ -35,7 +36,7 @@ protected RegisterOperand(Register reg) { if (reg == null) throw new NullPointerException(); } - public int frameSlot() { return reg.nonSSAId(); } + public int frameSlot() { return reg.frameSlot(); } public RegisterOperand copy(Register register) { return new RegisterOperand(register); @@ -48,12 +49,14 @@ public String toString() { } public static class LocalRegisterOperand extends RegisterOperand { - public LocalRegisterOperand(Register reg) { + Symbol.VarSymbol variable; + public LocalRegisterOperand(Register reg, Symbol.VarSymbol variable) { super(reg); + this.variable = variable; } @Override public RegisterOperand copy(Register register) { - return new LocalRegisterOperand(register); + return new LocalRegisterOperand(register, variable); } } diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Optimizer.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Optimizer.java index c9e21d8..1856e74 100644 --- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Optimizer.java +++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Optimizer.java @@ -6,7 +6,8 @@ public class Optimizer { public void optimize(CompiledFunction function, EnumSet options) { if (options.contains(Options.OPTIMIZE)) { - new EnterSSA(function, options); + if (!function.isSSA) + new EnterSSA(function, options); if (options.contains(Options.SCCP)) { new SparseConditionalConstantPropagation().constantPropagation(function).apply(options); if (new ConstantComparisonPropagation(function).apply(options)) { diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Options.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Options.java index 56280e2..b49d9b5 100644 --- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Options.java +++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Options.java @@ -3,6 +3,7 @@ import java.util.EnumSet; public enum Options { + ISSA, // Incremental SSA OPTIMIZE, SCCP, CCP, // constant comparison propagation @@ -22,5 +23,6 @@ public enum Options { public static final EnumSet NONE = EnumSet.noneOf(Options.class); public static final EnumSet OPT = EnumSet.of(Options.OPTIMIZE,Options.SCCP,Options.CCP,Options.REGALLOC); - public static final EnumSet OPT_VERBOSE = EnumSet.allOf(Options.class); + public static final EnumSet VERBOSE = EnumSet.range(DUMP_INITIAL_IR, DUMP_POST_CHAITIN_IR); + public static final EnumSet OPT_VERBOSE = EnumSet.range(OPTIMIZE, DUMP_POST_CHAITIN_IR); } 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 6ad3258..3376e61 100644 --- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Register.java +++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Register.java @@ -83,11 +83,13 @@ public String name() { * During SSA form this is not valid for registers that are instances of SSARegister. */ public int nonSSAId() { + //assert frameSlot >= 0; // assert inteferes with verbose display return frameSlot; } public void updateSlot(int slot) { this.frameSlot = slot; } + public int frameSlot() { return frameSlot; } /** * An SSA Register retains a reference to the original @@ -106,4 +108,6 @@ public int nonSSAId() { return originalRegNumber; } } + + } 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 5cb9e93..88469af 100644 --- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/RegisterPool.java +++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/RegisterPool.java @@ -49,7 +49,7 @@ public int numRegisters() { } public void toStr(StringBuilder sb) { for (Register reg : registers) { - sb.append("Reg #").append(reg.id).append(" ").append(reg.name()).append("\n"); + sb.append("Reg #").append(reg.id).append(" ").append(reg.name()).append(" ").append(reg.nonSSAId()).append("\n"); } } } diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SSAEdges.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SSAEdges.java index e6fcdcb..4e81e91 100644 --- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SSAEdges.java +++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SSAEdges.java @@ -83,12 +83,16 @@ private static void recordUses(CompiledFunction function, Map private static void recordUses(Map defUseChains, Register[] inputs, BasicBlock block, Instruction instruction) { for (Register register : inputs) { - SSADef def = defUseChains.get(register); - def.useList.add(instruction); + recordUse(defUseChains, instruction, register); } } - private static void recordDef(Map defUseChains, Register value, Instruction instruction) { + public static void recordUse(Map defUseChains, Instruction instruction, Register register) { + SSADef def = defUseChains.get(register); + def.useList.add(instruction); + } + + public static void recordDef(Map defUseChains, Register value, Instruction instruction) { if (defUseChains.containsKey(value)) throw new CompilerException("Register already defined, invalid multiple definition in SSA"); defUseChains.put(value, new SSADef(instruction)); diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestIncrementalSSA.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestIncrementalSSA.java new file mode 100644 index 0000000..864cb42 --- /dev/null +++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestIncrementalSSA.java @@ -0,0 +1,132 @@ +package com.compilerprogramming.ezlang.compiler; + +import org.junit.Test; + +import java.util.EnumSet; + +public class TestIncrementalSSA { + String compileSrc(String src) { + var compiler = new Compiler(); + var typeDict = compiler.compileSrc(src, EnumSet.of(Options.ISSA,Options.DUMP_SSA_IR)); + return compiler.dumpIR(typeDict,true); + } + + @Test + public void test1() { + String src = """ + func foo(d: Int) { + var a = 42; + var b = a; + var c = a + b; + a = c + 23; + c = a + d; + } + """; + String result = compileSrc(src); + System.out.println(result); + } + + @Test + public void test2() { + String src = """ + func foo(d: Int)->Int { + var a = 42 + if (d) + { + a = a + 1 + } + else + { + a = a - 1 + } + return a + } + """; + String result = compileSrc(src); + System.out.println(result); + } + + @Test + public void test3() { + String src = """ + func factorial(num: Int)->Int { + var result = 1 + while (num > 1) + { + result = result * num + num = num - 1 + } + return result + } + """; + String result = compileSrc(src); + System.out.println(result); + } + + + @Test + public void test4() { + String src = """ + func print(a: Int, b: Int, c:Int, d:Int) {} + func example14_66(p: Int, q: Int, r: Int, s: Int, t: Int) { + var i = 1 + var j = 1 + var k = 1 + var l = 1 + while (1) { + if (p) { + j = i + if (q) { + l = 2 + } + else { + l = 3 + } + k = k + 1 + } + else { + k = k + 2 + } + print(i,j,k,l) + while (1) { + if (r) { + l = l + 4 + } + if (!s) + break + } + i = i + 6 + if (!t) + break + } + } + """; + String result = compileSrc(src); + System.out.println(result); + } + + @Test + public void test5() { + String src = """ + func fib(n: Int)->Int { + var i: Int; + var temp: Int; + var f1=1; + var f2=1; + i=n; + while( i>1 ){ + temp = f1+f2; + f1=f2; + f2=temp; + i=i-1; + } + return f2; + } + func foo()->Int { + return fib(10); + } + """; + String result = compileSrc(src); + System.out.println(result); + } +} \ No newline at end of file diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestInterferenceGraph.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestInterferenceGraph.java index e5a4299..8018cba 100644 --- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestInterferenceGraph.java +++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestInterferenceGraph.java @@ -11,7 +11,8 @@ public class TestInterferenceGraph { private CompiledFunction buildTest1() { TypeDictionary typeDictionary = new TypeDictionary(); Type.TypeFunction functionType = new Type.TypeFunction("foo"); - functionType.addArg(new Symbol.ParameterSymbol("a", typeDictionary.INT)); + var argSymbol = new Symbol.ParameterSymbol("a", typeDictionary.INT); + functionType.addArg(argSymbol); functionType.setReturnType(typeDictionary.INT); CompiledFunction function = new CompiledFunction(functionType, typeDictionary); RegisterPool regPool = function.registerPool; @@ -19,7 +20,7 @@ private CompiledFunction buildTest1() { Register b = regPool.newReg("b", typeDictionary.INT); Register c = regPool.newReg("c", typeDictionary.INT); Register d = regPool.newReg("d", typeDictionary.INT); - function.code(new Instruction.ArgInstruction(new Operand.LocalRegisterOperand(a))); + function.code(new Instruction.ArgInstruction(new Operand.LocalRegisterOperand(a, argSymbol))); function.code(new Instruction.Binary( "+", new Operand.RegisterOperand(a), @@ -100,6 +101,8 @@ private CompiledFunction buildTest2() { function.currentBlock, new Operand.RegisterOperand(a), b1, b2)); + function.currentBlock.addSuccessor(b1); + function.currentBlock.addSuccessor(b2); function.startBlock(b1); function.code(new Instruction.Move( new Operand.ConstantOperand(2, typeDictionary.INT), diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestLiveness.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestLiveness.java index 8e632d7..7a324b2 100644 --- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestLiveness.java +++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestLiveness.java @@ -39,19 +39,19 @@ func foo() { String output = Compiler.dumpIR(typeDict, true); Assert.assertEquals(""" func print(n: Int) -Reg #0 n +Reg #0 n 0 L0: arg n goto L1 L1: func foo() -Reg #0 i -Reg #1 s -Reg #2 %t2 -Reg #3 %t3 -Reg #4 %t4 -Reg #5 %t5 -Reg #6 %t6 +Reg #0 i 0 +Reg #1 s 1 +Reg #2 %t2 2 +Reg #3 %t3 3 +Reg #4 %t4 4 +Reg #5 %t5 5 +Reg #6 %t6 6 L0: i = 1 s = 1 @@ -159,13 +159,13 @@ func foo(a: Int, b: Int) { String output = Compiler.dumpIR(typeDict, true); Assert.assertEquals(""" func foo(a: Int,b: Int) -Reg #0 a -Reg #1 b -Reg #2 %t2 -Reg #3 %t3 -Reg #4 %t4 -Reg #5 %t5 -Reg #6 %t6 +Reg #0 a 0 +Reg #1 b 1 +Reg #2 %t2 2 +Reg #3 %t3 3 +Reg #4 %t4 4 +Reg #5 %t5 5 +Reg #6 %t6 6 L0: arg a arg b @@ -264,6 +264,8 @@ static CompiledFunction buildTest3() { function.currentBlock, new Operand.RegisterOperand(i), b2, b3)); + function.currentBlock.addSuccessor(b2); + function.currentBlock.addSuccessor(b3); function.startBlock(b2); function.code(new Instruction.Move( new Operand.ConstantOperand(0, typeDictionary.INT), @@ -284,6 +286,8 @@ static CompiledFunction buildTest3() { function.currentBlock, new Operand.RegisterOperand(i), b1, b4)); + function.currentBlock.addSuccessor(b1); + function.currentBlock.addSuccessor(b4); function.startBlock(b4); function.code(new Instruction.Ret(new Operand.RegisterOperand(s))); function.startBlock(function.exit); @@ -301,8 +305,8 @@ public void test3() { String actual = function.toStr(new StringBuilder(), true).toString(); Assert.assertEquals(""" func foo()->Int -Reg #0 i -Reg #1 s +Reg #0 i 0 +Reg #1 s 1 L0: i = 1 goto L2 @@ -366,11 +370,11 @@ public void testSwapProblem() { String actual = function.toStr(new StringBuilder(), true).toString(); Assert.assertEquals(""" func foo(p: Int) -Reg #0 p -Reg #1 a1 -Reg #2 a2 -Reg #3 b1 -Reg #4 b2 +Reg #0 p 0 +Reg #1 a1 1 +Reg #2 a2 2 +Reg #3 b1 3 +Reg #4 b2 4 L0: arg p a1 = 42 @@ -409,10 +413,10 @@ public void testLostCopyProblem() { String actual = function.toStr(new StringBuilder(), true).toString(); Assert.assertEquals(""" func foo(p: Int)->Int -Reg #0 p -Reg #1 x1 -Reg #2 x3 -Reg #3 x2 +Reg #0 p 0 +Reg #1 x1 1 +Reg #2 x3 2 +Reg #3 x2 3 L0: arg p x1 = 1 @@ -466,7 +470,7 @@ func foo()->Int Assert.assertEquals(""" Pre-SSA func foo()->Int -Reg #0 %t0 +Reg #0 %t0 0 L0: if 1 goto L2 else goto L3 #PHIDEFS = {} @@ -511,10 +515,10 @@ func foo()->Int #LIVEOUT = {0} Post-SSA func foo()->Int -Reg #0 %t0 -Reg #1 %t0_0 -Reg #2 %t0_1 -Reg #3 %t0_2 +Reg #0 %t0 0 +Reg #1 %t0_0 0 +Reg #2 %t0_1 0 +Reg #3 %t0_2 0 L0: if 1 goto L2 else goto L3 #PHIDEFS = {} diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java index 1c4fc5b..2a91a12 100644 --- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java +++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java @@ -638,13 +638,14 @@ func bar(arg: Int)->Int { static CompiledFunction buildLostCopyTest() { TypeDictionary typeDictionary = new TypeDictionary(); Type.TypeFunction functionType = new Type.TypeFunction("foo"); - functionType.addArg(new Symbol.ParameterSymbol("p", typeDictionary.INT)); + var argSymbol = new Symbol.ParameterSymbol("p", typeDictionary.INT); + functionType.addArg(argSymbol); functionType.setReturnType(typeDictionary.INT); CompiledFunction function = new CompiledFunction(functionType, typeDictionary); RegisterPool regPool = function.registerPool; Register p = regPool.newReg("p", typeDictionary.INT); Register x1 = regPool.newReg("x1", typeDictionary.INT); - function.code(new Instruction.ArgInstruction(new Operand.LocalRegisterOperand(p))); + function.code(new Instruction.ArgInstruction(new Operand.LocalRegisterOperand(p, argSymbol))); function.code(new Instruction.Move( new Operand.ConstantOperand(1, typeDictionary.INT), new Operand.RegisterOperand(x1))); @@ -659,6 +660,8 @@ static CompiledFunction buildLostCopyTest() { new Operand.ConstantOperand(1, typeDictionary.INT))); function.code(new Instruction.ConditionalBranch(B2, new Operand.RegisterOperand(p), B2, function.exit)); + function.currentBlock.addSuccessor(B2); + function.currentBlock.addSuccessor(function.exit); function.startBlock(function.exit); function.code(new Instruction.Ret(new Operand.RegisterOperand(x2))); function.isSSA = true; @@ -706,7 +709,8 @@ public void testLostCopyProblem() { static CompiledFunction buildSwapTest() { TypeDictionary typeDictionary = new TypeDictionary(); Type.TypeFunction functionType = new Type.TypeFunction("foo"); - functionType.addArg(new Symbol.ParameterSymbol("p", typeDictionary.INT)); + var argSymbol = new Symbol.ParameterSymbol("p", typeDictionary.INT); + functionType.addArg(argSymbol); functionType.setReturnType(typeDictionary.VOID); CompiledFunction function = new CompiledFunction(functionType, typeDictionary); RegisterPool regPool = function.registerPool; @@ -715,7 +719,7 @@ static CompiledFunction buildSwapTest() { Register a2 = regPool.newReg("a2", typeDictionary.INT); Register b1 = regPool.newReg("b1", typeDictionary.INT); Register b2 = regPool.newReg("b2", typeDictionary.INT); - function.code(new Instruction.ArgInstruction(new Operand.LocalRegisterOperand(p))); + function.code(new Instruction.ArgInstruction(new Operand.LocalRegisterOperand(p, argSymbol))); function.code(new Instruction.Move( new Operand.ConstantOperand(42, typeDictionary.INT), new Operand.RegisterOperand(a1))); @@ -728,6 +732,8 @@ static CompiledFunction buildSwapTest() { function.code(new Instruction.Phi(b2, Arrays.asList(b1, a2))); function.code(new Instruction.ConditionalBranch(B2, new Operand.RegisterOperand(p), B2, function.exit)); + function.currentBlock.addSuccessor(B2); + function.currentBlock.addSuccessor(function.exit); function.startBlock(function.exit); function.isSSA = true; return function; diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java b/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java index 4f8c0f0..fcfe0f5 100644 --- a/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java +++ b/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java @@ -13,6 +13,7 @@ Value compileAndRun(String src, String mainFunction) { return compileAndRun(src, mainFunction, Options.NONE); } Value compileAndRun(String src, String mainFunction, EnumSet options) { + //options.add(Options.ISSA); var compiler = new Compiler(); var typeDict = compiler.compileSrc(src, options); var compiled = compiler.dumpIR(typeDict);