From 35c9ef8bfbd9ce0477ab7b5ca70c1131a22b2841 Mon Sep 17 00:00:00 2001 From: dibyendumajumdar Date: Mon, 23 Dec 2024 22:09:42 +0000 Subject: [PATCH 1/4] Docs skeleton --- README.md | 14 +++++++------- lexer/README.md | 3 +++ optvm/README.md | 7 +++++++ parser/README.md | 3 +++ registervm/README.md | 6 ++++++ semantic/README.md | 3 +++ stackvm/README.md | 3 +++ types/README.md | 3 +++ 8 files changed, 35 insertions(+), 7 deletions(-) create mode 100644 lexer/README.md create mode 100644 optvm/README.md create mode 100644 parser/README.md create mode 100644 registervm/README.md create mode 100644 semantic/README.md create mode 100644 stackvm/README.md create mode 100644 types/README.md diff --git a/README.md b/README.md index 6307032..2c01d51 100644 --- a/README.md +++ b/README.md @@ -20,13 +20,13 @@ The language is intentionally very simple and is meant to have just enough funct The project is under development and subject to change. At this point in time, we have following initial implementations: -* lexer - a simple tokenizer -* parser - a recursive descent parser and AST -* types - the type definitions -* semantic - semantic analyzer -* stackvm - a bytecode compiler that generates stack IR (bytecode interpreter not yet available) -* registervm - a bytecode compiler that generates a linear register IR and a bytecode interpreter that can execute the IR -* optvm - WIP this will use an optimization pipeline, making usa of SSA transformation +* [lexer](./lexer/README.md) - a simple tokenizer +* [parser](./parser/README.md) - a recursive descent parser and AST +* [types](/types/README.md) - the type definitions +* [semantic](./semantic/README.md) - semantic analyzer +* [stackvm](./stackvm/README.md) - a compiler that generates IR for a stack based virtual machine +* [registervm](./registervm/README.md) - a compiler that generates a so called three-address IR and an interpreter that can execute the IR +* [optvm](./optvm/README.md) - an optimizing compiler (WIP) that supports SSA. ## How can you contribute? diff --git a/lexer/README.md b/lexer/README.md new file mode 100644 index 0000000..91eab56 --- /dev/null +++ b/lexer/README.md @@ -0,0 +1,3 @@ +# Lexer + +For now please checkout the code. Docs to follow. \ No newline at end of file diff --git a/optvm/README.md b/optvm/README.md new file mode 100644 index 0000000..df4d28e --- /dev/null +++ b/optvm/README.md @@ -0,0 +1,7 @@ +# Optimizing Compiler for Register VM + +This module implements various compiler optimization techniques such as: + +* Static Single Assignment +* Liveness Analysis +* WIP Graph Coloring Register Allocator (Chaitin) \ No newline at end of file diff --git a/parser/README.md b/parser/README.md new file mode 100644 index 0000000..7f2e5cb --- /dev/null +++ b/parser/README.md @@ -0,0 +1,3 @@ +# Parser + +For now please checkout the code. Docs to follow. diff --git a/registervm/README.md b/registervm/README.md new file mode 100644 index 0000000..00f2b58 --- /dev/null +++ b/registervm/README.md @@ -0,0 +1,6 @@ +# Intermediate Representation using Three Address Instructions + +Well, its not always three address instructions but the main idea is that instructions directly +address locations called Virtual Registers. + +For now please checkout the code. Docs to follow. \ No newline at end of file diff --git a/semantic/README.md b/semantic/README.md new file mode 100644 index 0000000..1ae2506 --- /dev/null +++ b/semantic/README.md @@ -0,0 +1,3 @@ +# Semantic Analyzer + +For now please checkout the code. Docs to follow. \ No newline at end of file diff --git a/stackvm/README.md b/stackvm/README.md new file mode 100644 index 0000000..db15555 --- /dev/null +++ b/stackvm/README.md @@ -0,0 +1,3 @@ +# Intermediate Representation using Stack + +Implements a compiler that generates IR for an abstract machine that uses a stack based execution model. \ No newline at end of file diff --git a/types/README.md b/types/README.md new file mode 100644 index 0000000..f80d353 --- /dev/null +++ b/types/README.md @@ -0,0 +1,3 @@ +# Type System + +For now please checkout the code. Docs to follow. \ No newline at end of file From 083941ad738272c5ae9c4d3e5f2066236c5b3f5d Mon Sep 17 00:00:00 2001 From: dibyendumajumdar Date: Mon, 23 Dec 2024 22:10:40 +0000 Subject: [PATCH 2/4] Docs skeleton --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 2c01d51..913da06 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ The project is under development and subject to change. At this point in time, w * [stackvm](./stackvm/README.md) - a compiler that generates IR for a stack based virtual machine * [registervm](./registervm/README.md) - a compiler that generates a so called three-address IR and an interpreter that can execute the IR * [optvm](./optvm/README.md) - an optimizing compiler (WIP) that supports SSA. +* seaofnodes - a compiler that will generate Sea of Nodes IR (planned) ## How can you contribute? From 1a21e95ae8039768462fcd6ac7aa0488dd0bee79 Mon Sep 17 00:00:00 2001 From: dibyendumajumdar Date: Sun, 22 Dec 2024 00:08:35 +0000 Subject: [PATCH 3/4] Chaitin Graph Coloring Register allocator - WIP coalesce step Chaitin Graph Coloring Register allocator - WIP coalesce step Bugfix Bugfix Phi and liveness issue Refactored Instruction so that we avoid calculating uses all over the place Some unit tests --- ...ChaitinGraphColoringRegisterAllocator.java | 57 ++ .../ezlang/compiler/CompiledFunction.java | 4 + .../ezlang/compiler/ExitSSA.java | 7 +- .../ezlang/compiler/Instruction.java | 643 +++++------------- .../ezlang/compiler/InterferenceGraph.java | 23 +- .../compiler/InterferenceGraphBuilder.java | 5 +- .../ezlang/compiler/Liveness.java | 7 +- .../ezlang/compiler/SSATransform.java | 16 +- .../ezlang/interpreter/Interpreter.java | 77 +-- .../compiler/TestChaitinRegAllocator.java | 21 + .../compiler/TestInterferenceGraph.java | 70 +- .../ezlang/compiler/TestLiveness.java | 67 ++ .../ezlang/compiler/TestSSATransform.java | 10 +- 13 files changed, 459 insertions(+), 548 deletions(-) create mode 100644 optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ChaitinGraphColoringRegisterAllocator.java create mode 100644 optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestChaitinRegAllocator.java diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ChaitinGraphColoringRegisterAllocator.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ChaitinGraphColoringRegisterAllocator.java new file mode 100644 index 0000000..8fc2bc7 --- /dev/null +++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ChaitinGraphColoringRegisterAllocator.java @@ -0,0 +1,57 @@ +package com.compilerprogramming.ezlang.compiler; + +import java.util.ArrayList; +import java.util.List; + +public class ChaitinGraphColoringRegisterAllocator { + + public ChaitinGraphColoringRegisterAllocator(CompiledFunction function) { + coalesce(function); + } + + private void coalesce(CompiledFunction function) { + boolean changed = true; + while (changed) { + var igraph = new InterferenceGraphBuilder().build(function); + changed = coalesceRegisters(function, igraph); + } + } + + private boolean coalesceRegisters(CompiledFunction function, InterferenceGraph igraph) { + boolean changed = false; + for (var block: function.getBlocks()) { + List instructionsToRemove = new ArrayList<>(); + for (int j = 0; j < block.instructions.size(); j++) { + Instruction i = block.instructions.get(j); + if (i instanceof Instruction.Move move + && move.from() instanceof Operand.RegisterOperand targetOperand) { + Register source = move.def(); + Register target = targetOperand.reg; + if (source.id != target.id && + !igraph.interfere(target.id, source.id)) { + igraph.rename(source.id, target.id); + rewriteInstructions(function, i, source, target); + instructionsToRemove.add(j); + changed = true; + } + } + } + for (var j: instructionsToRemove) { + block.instructions.set(j, new Instruction.NoOp()); + } + } + return changed; + } + + private void rewriteInstructions(CompiledFunction function, Instruction notNeeded, Register source, Register target) { + for (var block: function.getBlocks()) { + for (Instruction i: block.instructions) { + if (i == notNeeded) + continue; + if (i.definesVar() && source.id == i.def().id) + i.replaceDef(target); + i.replaceUse(source, target); + } + } + } +} 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 6c1170d..85dd365 100644 --- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java +++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java @@ -565,4 +565,8 @@ public void livenessAnalysis() { new Liveness(this); this.hasLiveness = true; } + + public List getBlocks() { + return BBHelper.findAllBlocks(entry); + } } diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA.java index fb9bb03..fefa4fb 100644 --- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA.java +++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA.java @@ -18,6 +18,7 @@ public ExitSSA(CompiledFunction function) { this.function = function; if (!function.isSSA) throw new IllegalStateException(); function.livenessAnalysis(); + System.out.println(function.toStr(new StringBuilder(), true)); tree = new DominatorTree(function.entry); initStack(); insertCopies(function.entry); @@ -35,9 +36,7 @@ private void insertCopies(BasicBlock block) { List pushed = new ArrayList<>(); for (Instruction i: block.instructions) { // replace all uses u with stacks[i] - if (i.usesVars()) { - replaceUses(i); - } + replaceUses(i); } scheduleCopies(block, pushed); for (BasicBlock c: block.dominatedChildren) { @@ -86,7 +85,7 @@ private void scheduleCopies(BasicBlock block, List pushed) { int j = SSATransform.whichPred(s, block); for (Instruction.Phi phi: s.phis()) { Register dst = phi.def(); - Register src = phi.inputs.get(j).reg; // jth operand of phi node + Register src = phi.input(j); // jth operand of phi node copySet.add(new CopyItem(src, dst)); map.put(src.id, src); map.put(dst.id, dst); 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 42bfdef..88de2d5 100644 --- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java +++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java @@ -8,96 +8,117 @@ public abstract class Instruction { - public boolean isTerminal() { - return false; + static final int I_NOOP = 0; + static final int I_MOVE = 1; + static final int I_RET = 2; + static final int I_UNARY = 3; + static final int I_BINARY = 4; + static final int I_BR = 5; + static final int I_CBR = 6; + static final int I_ARG = 7; + static final int I_CALL = 8; + static final int I_PHI = 9; + static final int I_NEW_ARRAY = 10; + static final int I_NEW_STRUCT = 11; + static final int I_ARRAY_STORE = 12; + static final int I_ARRAY_LOAD = 13; + static final int I_ARRAY_APPEND = 14; + static final int I_FIELD_GET = 15; + static final int I_FIELD_SET = 16; + + public final int opcode; + public Operand.RegisterOperand def; + public Operand[] uses; + + public Instruction(int opcode, Operand... uses) { + this.opcode = opcode; + this.def = null; + this.uses = uses; } + public Instruction(int opcode, Operand.RegisterOperand def, Operand... uses) { + this.opcode = opcode; + this.def = def; + this.uses = uses; + } + + public boolean isTerminal() { return false; } @Override public String toString() { return toStr(new StringBuilder()).toString(); } - public boolean definesVar() { return false; } - public Register def() { return null; } - - public boolean usesVars() { return false; } - public List uses() { return Collections.emptyList(); } - public void replaceDef(Register newReg) {} - public void replaceUses(Register[] newUses) {} - - public static class Move extends Instruction { - public final Operand from; - public final Operand to; - public Move(Operand from, Operand to) { - this.from = from; - this.to = to; - } + public boolean definesVar() { return def != null; } + public Register def() { return def != null ? def.reg: null; } - @Override - public boolean definesVar() { - return true; + public List uses() { + List useList = null; + for (int i = 0; i < uses.length; i++) { + Operand operand = uses[i]; + if (operand != null && operand instanceof Operand.RegisterOperand registerOperand) { + if (useList == null) useList = new ArrayList<>(); + useList.add(registerOperand.reg); + } } - - @Override - public Register def() { - if (to instanceof Operand.RegisterOperand registerOperand) - return registerOperand.reg; - throw new IllegalStateException(); + if (useList == null) useList = Collections.emptyList(); + return useList; + } + public void replaceDef(Register newReg) { + if (def == null) throw new IllegalStateException(); + def.replaceRegister(newReg); + } + public void replaceUses(Register[] newUses) { + int j = 0; + for (int i = 0; i < uses.length; i++) { + Operand use = uses[i]; + if (use != null && use instanceof Operand.RegisterOperand registerOperand) { + registerOperand.replaceRegister(newUses[j++]); + } } - - @Override - public void replaceDef(Register newReg) { - this.to.replaceRegister(newReg); + } + public boolean replaceUse(Register source, Register target) { + boolean replaced = false; + for (int i = 0; i < uses.length; i++) { + Operand operand = uses[i]; + if (operand != null && operand instanceof Operand.RegisterOperand registerOperand && registerOperand.reg.id == source.id) { + registerOperand.replaceRegister(target); + replaced = true; + } } + return replaced; + } - @Override - public boolean usesVars() { - return (from instanceof Operand.RegisterOperand); + public static class NoOp extends Instruction { + public NoOp() { + super(I_NOOP); } - @Override - public List uses() { - if (from instanceof Operand.RegisterOperand registerOperand) - return List.of(registerOperand.reg); - return super.uses(); + public StringBuilder toStr(StringBuilder sb) { + return sb.append("noop"); } + } - @Override - public void replaceUses(Register[] newUses) { - from.replaceRegister(newUses[0]); + public static class Move extends Instruction { + public Move(Operand from, Operand to) { + super(I_MOVE, (Operand.RegisterOperand) to, from); } - + public Operand from() { return uses[0]; } + public Operand.RegisterOperand to() { return def; } @Override public StringBuilder toStr(StringBuilder sb) { - return sb.append(to).append(" = ").append(from); + return sb.append(to()).append(" = ").append(from()); } } public static class NewArray extends Instruction { public final Type.TypeArray type; - public final Operand.RegisterOperand destOperand; public NewArray(Type.TypeArray type, Operand.RegisterOperand destOperand) { + super(I_NEW_ARRAY, destOperand); this.type = type; - this.destOperand = destOperand; } - - @Override - public boolean definesVar() { - return true; - } - - @Override - public Register def() { - return destOperand.reg; - } - - @Override - public void replaceDef(Register newReg) { - destOperand.replaceRegister(newReg); - } - + public Operand.RegisterOperand destOperand() { return def; } @Override public StringBuilder toStr(StringBuilder sb) { - return sb.append(destOperand) + return sb.append(def) .append(" = ") .append("New(") .append(type) @@ -107,29 +128,14 @@ public StringBuilder toStr(StringBuilder sb) { public static class NewStruct extends Instruction { public final Type.TypeStruct type; - public final Operand.RegisterOperand destOperand; public NewStruct(Type.TypeStruct type, Operand.RegisterOperand destOperand) { + super(I_NEW_STRUCT, destOperand); this.type = type; - this.destOperand = destOperand; } - @Override - public boolean definesVar() { - return true; - } - - @Override - public Register def() { - return destOperand.reg; - } - - @Override - public void replaceDef(Register newReg) { - destOperand.replaceRegister(newReg); - } - + public Operand.RegisterOperand destOperand() { return def; } @Override public StringBuilder toStr(StringBuilder sb) { - return sb.append(destOperand) + return sb.append(def) .append(" = ") .append("New(") .append(type) @@ -138,489 +144,177 @@ public StringBuilder toStr(StringBuilder sb) { } public static class ArrayLoad extends Instruction { - public final Operand arrayOperand; - public final Operand indexOperand; - public final Operand.RegisterOperand destOperand; public ArrayLoad(Operand.LoadIndexedOperand from, Operand.RegisterOperand to) { - arrayOperand = from.arrayOperand; - indexOperand = from.indexOperand; - destOperand = to; + super(I_ARRAY_LOAD, to, from.arrayOperand, from.indexOperand); } - @Override - public boolean definesVar() { - return true; - } - @Override - public Register def() { - return destOperand.reg; - } - @Override - public void replaceDef(Register newReg) { - destOperand.replaceRegister(newReg); - } - - @Override - public boolean usesVars() { - return true; - } - - @Override - public List uses() { - List usesList = new ArrayList<>(); - if (arrayOperand instanceof Operand.RegisterOperand registerOperand) - usesList.add(registerOperand.reg); - if (indexOperand instanceof Operand.RegisterOperand registerOperand) - usesList.add(registerOperand.reg); - return usesList; - } - - @Override - public void replaceUses(Register[] newUses) { - int i = 0; - if (arrayOperand instanceof Operand.RegisterOperand) { - arrayOperand.replaceRegister(newUses[i++]); - } - if (indexOperand instanceof Operand.RegisterOperand) { - indexOperand.replaceRegister(newUses[i]); - } - } - + public Operand arrayOperand() { return uses[0]; } + public Operand indexOperand() { return uses[1]; } + public Operand.RegisterOperand destOperand() { return def; } @Override public StringBuilder toStr(StringBuilder sb) { - return sb.append(destOperand) + return sb.append(destOperand()) .append(" = ") - .append(arrayOperand) + .append(arrayOperand()) .append("[") - .append(indexOperand) + .append(indexOperand()) .append("]"); } } public static class ArrayStore extends Instruction { - public final Operand arrayOperand; - public final Operand indexOperand; - public final Operand sourceOperand; public ArrayStore(Operand from, Operand.LoadIndexedOperand to) { - arrayOperand = to.arrayOperand; - indexOperand = to.indexOperand; - sourceOperand = from; + super(I_ARRAY_STORE, (Operand.RegisterOperand) null, to.arrayOperand, to.indexOperand, from); } - @Override - public boolean usesVars() { - return true; - } - - @Override - public List uses() { - List usesList = new ArrayList<>(); - if (arrayOperand instanceof Operand.RegisterOperand registerOperand) - usesList.add(registerOperand.reg); - if (indexOperand instanceof Operand.RegisterOperand registerOperand) - usesList.add(registerOperand.reg); - if (sourceOperand instanceof Operand.RegisterOperand registerOperand) - usesList.add(registerOperand.reg); - return usesList; - } - - @Override - public void replaceUses(Register[] newUses) { - int i = 0; - if (arrayOperand instanceof Operand.RegisterOperand) { - arrayOperand.replaceRegister(newUses[i++]); - } - if (indexOperand instanceof Operand.RegisterOperand) { - indexOperand.replaceRegister(newUses[i++]); - } - if (sourceOperand instanceof Operand.RegisterOperand) { - sourceOperand.replaceRegister(newUses[i]); - } - } - + public Operand arrayOperand() { return uses[0]; } + public Operand indexOperand() { return uses[1]; } + public Operand sourceOperand() { return uses[2]; } @Override public StringBuilder toStr(StringBuilder sb) { return sb - .append(arrayOperand) + .append(arrayOperand()) .append("[") - .append(indexOperand) + .append(indexOperand()) .append("] = ") - .append(sourceOperand); + .append(sourceOperand()); } } public static class GetField extends Instruction { - public final Operand structOperand; public final String fieldName; public final int fieldIndex; - public final Operand.RegisterOperand destOperand; - public GetField(Operand.LoadFieldOperand from, Operand.RegisterOperand to) - { - this.structOperand = from.structOperand; + public GetField(Operand.LoadFieldOperand from, Operand.RegisterOperand to) { + super(I_FIELD_GET, to, from.structOperand); this.fieldName = from.fieldName; this.fieldIndex = from.fieldIndex; - this.destOperand = to; } - @Override - public boolean definesVar() { - return true; - } - @Override - public Register def() { - return destOperand.reg; - } - - @Override - public void replaceDef(Register newReg) { - destOperand.replaceRegister(newReg); - } - - @Override - public boolean usesVars() { - return true; - } - @Override - public List uses() { - List usesList = new ArrayList<>(); - if (structOperand instanceof Operand.RegisterOperand registerOperand) - usesList.add(registerOperand.reg); - return usesList; - } - - @Override - public void replaceUses(Register[] newUses) { - if (newUses.length > 0) - structOperand.replaceRegister(newUses[0]); - } - + public Operand structOperand() { return uses[0]; } + public Operand.RegisterOperand destOperand() { return def; } @Override public StringBuilder toStr(StringBuilder sb) { - return sb.append(destOperand) + return sb.append(def) .append(" = ") - .append(structOperand) + .append(uses[0]) .append(".") .append(fieldName); } } public static class SetField extends Instruction { - public final Operand structOperand; public final String fieldName; public final int fieldIndex; - public final Operand sourceOperand; - public SetField(Operand from,Operand.LoadFieldOperand to) - { - this.structOperand = to.structOperand; + public SetField(Operand from,Operand.LoadFieldOperand to) { + super(I_FIELD_SET, (Operand.RegisterOperand) null, to.structOperand, from); this.fieldName = to.fieldName; this.fieldIndex = to.fieldIndex; - this.sourceOperand = from; - } - @Override - public boolean usesVars() { - return true; } - @Override - public List uses() { - List usesList = new ArrayList<>(); - if (structOperand instanceof Operand.RegisterOperand registerOperand) - usesList.add(registerOperand.reg); - if (sourceOperand instanceof Operand.RegisterOperand registerOperand) - usesList.add(registerOperand.reg); - return usesList; - } - @Override - public void replaceUses(Register[] newUses) { - int i = 0; - if (structOperand instanceof Operand.RegisterOperand) { - structOperand.replaceRegister(newUses[i++]); - } - if (sourceOperand instanceof Operand.RegisterOperand) { - sourceOperand.replaceRegister(newUses[i]); - } - } - + public Operand structOperand() { return uses[0]; } + public Operand sourceOperand() { return uses[1]; } @Override public StringBuilder toStr(StringBuilder sb) { return sb - .append(structOperand) + .append(structOperand()) .append(".") .append(fieldName) .append(" = ") - .append(sourceOperand); + .append(sourceOperand()); } } public static class Ret extends Instruction { - public final Operand value; public Ret(Operand value) { - this.value = value; - } - @Override - public boolean usesVars() { - return value != null && value instanceof Operand.RegisterOperand registerOperand; - } - @Override - public List uses() { - if (value != null && value instanceof Operand.RegisterOperand registerOperand) - return List.of(registerOperand.reg); - return Collections.emptyList(); - } - @Override - public void replaceUses(Register[] newUses) { - if (newUses.length > 0) - value.replaceRegister(newUses[0]); + super(I_RET, (Operand.RegisterOperand) null, value); } + public Operand value() { return uses[0]; } @Override public StringBuilder toStr(StringBuilder sb) { sb.append("ret"); - if (value != null) - sb.append(" ").append(value); + if (uses[0] != null) + sb.append(" ").append(value()); return sb; } } public static class Unary extends Instruction { public final String unop; - public final Operand.RegisterOperand result; - public final Operand operand; public Unary(String unop, Operand.RegisterOperand result, Operand operand) { + super(I_UNARY, result, operand); this.unop = unop; - this.result = result; - this.operand = operand; - } - - @Override - public boolean definesVar() { - return true; - } - - @Override - public Register def() { - return result.reg; - } - - @Override - public void replaceDef(Register newReg) { - result.replaceRegister(newReg); - } - - @Override - public boolean usesVars() { - return operand instanceof Operand.RegisterOperand registerOperand; - } - - @Override - public List uses() { - List usesList = new ArrayList<>(); - if (operand instanceof Operand.RegisterOperand registerOperand) { - usesList.add(registerOperand.reg); - } - return usesList; - } - - @Override - public void replaceUses(Register[] newUses) { - if (newUses.length > 0) - operand.replaceRegister(newUses[0]); } - + public Operand.RegisterOperand result() { return def; } + public Operand operand() { return uses[0]; } @Override public StringBuilder toStr(StringBuilder sb) { - return sb.append(result).append(" = ").append(unop).append(operand); + return sb.append(result()).append(" = ").append(unop).append(operand()); } } public static class Binary extends Instruction { public final String binOp; - public final Operand.RegisterOperand result; - public final Operand left; - public final Operand right; public Binary(String binop, Operand.RegisterOperand result, Operand left, Operand right) { + super(I_BINARY, result, left, right); this.binOp = binop; - this.result = result; - this.left = left; - this.right = right; - } - @Override - public boolean definesVar() { - return true; - } - - @Override - public Register def() { - return result.reg; } - - @Override - public void replaceDef(Register newReg) { - result.replaceRegister(newReg); - } - - @Override - public boolean usesVars() { - return left instanceof Operand.RegisterOperand || - right instanceof Operand.RegisterOperand; - } - @Override - public List uses() { - List usesList = new ArrayList<>(); - if (left instanceof Operand.RegisterOperand registerOperand) { - usesList.add(registerOperand.reg); - } - if (right instanceof Operand.RegisterOperand registerOperand) { - usesList.add(registerOperand.reg); - } - return usesList; - } - - @Override - public void replaceUses(Register[] newUses) { - int i = 0; - if (left instanceof Operand.RegisterOperand) { - left.replaceRegister(newUses[i++]); - } - if (right instanceof Operand.RegisterOperand) { - right.replaceRegister(newUses[i]); - } - } - + public Operand.RegisterOperand result() { return def; } + public Operand left() { return uses[0]; } + public Operand right() { return uses[1]; } @Override public StringBuilder toStr(StringBuilder sb) { - return sb.append(result).append(" = ").append(left).append(binOp).append(right); + return sb.append(def).append(" = ").append(uses[0]).append(binOp).append(uses[1]); } } public static class AStoreAppend extends Instruction { - public final Operand.RegisterOperand array; - public final Operand value; public AStoreAppend(Operand.RegisterOperand array, Operand value) { - this.array = array; - this.value = value; - } - @Override - public boolean usesVars() { - return true; - } - @Override - public List uses() { - List usesList = new ArrayList<>(); - usesList.add(array.reg); - if (value instanceof Operand.RegisterOperand registerOperand) { - usesList.add(registerOperand.reg); - } - return usesList; + super(I_ARRAY_APPEND, (Operand.RegisterOperand) null, array, value); } - @Override - public void replaceUses(Register[] newUses) { - array.replaceRegister(newUses[0]); - if (value instanceof Operand.RegisterOperand) - value.replaceRegister(newUses[1]); - } - + public Operand.RegisterOperand array() { return (Operand.RegisterOperand) uses[0]; } + public Operand value() { return uses[1]; } @Override public StringBuilder toStr(StringBuilder sb) { - return sb.append(array).append(".append(").append(value).append(")"); + return sb.append(uses[0]).append(".append(").append(uses[1]).append(")"); } } public static class ConditionalBranch extends Instruction { - public final Operand condition; public final BasicBlock trueBlock; public final BasicBlock falseBlock; public ConditionalBranch(BasicBlock currentBlock, Operand condition, BasicBlock trueBlock, BasicBlock falseBlock) { - this.condition = condition; + super(I_CBR, (Operand.RegisterOperand) null, condition); this.trueBlock = trueBlock; this.falseBlock = falseBlock; currentBlock.addSuccessor(trueBlock); currentBlock.addSuccessor(falseBlock); } - @Override - public boolean usesVars() { - return condition instanceof Operand.RegisterOperand; - } - @Override - public List uses() { - List usesList = new ArrayList<>(); - if (condition instanceof Operand.RegisterOperand registerOperand) { - usesList.add(registerOperand.reg); - } - return usesList; - } - - @Override - public void replaceUses(Register[] newUses) { - if (condition instanceof Operand.RegisterOperand) { - condition.replaceRegister(newUses[0]); - } - } - + public Operand condition() { return uses[0]; } @Override public boolean isTerminal() { return true; } @Override public StringBuilder toStr(StringBuilder sb) { - return sb.append("if ").append(condition).append(" goto L").append(trueBlock.bid).append(" else goto L").append(falseBlock.bid); + return sb.append("if ").append(condition()).append(" goto L").append(trueBlock.bid).append(" else goto L").append(falseBlock.bid); } } public static class Call extends Instruction { public final Type.TypeFunction callee; - public final Operand.RegisterOperand[] args; - public final Operand.RegisterOperand returnOperand; public final int newbase; public Call(int newbase, Operand.RegisterOperand returnOperand, Type.TypeFunction callee, Operand.RegisterOperand... args) { - this.returnOperand = returnOperand; + super(I_CALL, returnOperand, args); this.callee = callee; - this.args = args; this.newbase = newbase; } - - @Override - public boolean definesVar() { - return returnOperand != null; - } - - @Override - public Register def() { - return returnOperand != null ? returnOperand.reg : null; - } - - @Override - public void replaceDef(Register newReg) { - if (returnOperand != null) - returnOperand.replaceRegister(newReg); - } - - @Override - public boolean usesVars() { - return args != null && args.length > 0; - } - @Override - public List uses() { - List usesList = new ArrayList<>(); - for (Operand.RegisterOperand argOperand : args) { - usesList.add(argOperand.reg); - } - return usesList; - } - - @Override - public void replaceUses(Register[] newUses) { - if (args == null) - return; - for (int i = 0; i < args.length; i++) { - args[i].replaceRegister(newUses[i]); - } - } - + public Operand.RegisterOperand returnOperand() { return def; } + public Operand[] args() { return uses; } @Override public StringBuilder toStr(StringBuilder sb) { - if (returnOperand != null) { - sb.append(returnOperand).append(" = "); + if (def != null) { + sb.append(def).append(" = "); } sb.append("call ").append(callee); - if (args.length > 0) + if (uses.length > 0) sb.append(" params "); - for (int i = 0; i < args.length; i++) { + for (int i = 0; i < uses.length; i++) { if (i > 0) sb.append(", "); - sb.append(args[i]); + sb.append(uses[i]); } return sb; } @@ -629,6 +323,7 @@ public StringBuilder toStr(StringBuilder sb) { public static class Jump extends Instruction { public final BasicBlock jumpTo; public Jump(BasicBlock jumpTo) { + super(I_BR); this.jumpTo = jumpTo; } @Override @@ -641,36 +336,27 @@ public StringBuilder toStr(StringBuilder sb) { } } + /** + * Phi does not generate uses. + */ public static class Phi extends Instruction { - public Operand.RegisterOperand dest; - public final List inputs = new ArrayList<>(); + public final Register[] inputs; public Phi(Register dest, List inputs) { - this.dest = new Operand.RegisterOperand(dest); - for (Register input : inputs) { - this.inputs.add(new Operand.RegisterOperand(input)); - } - } - @Override - public boolean definesVar() { - return true; - } - @Override - public Register def() { - return dest.reg; - } - @Override - public void replaceDef(Register newReg) { - dest = new Operand.RegisterOperand(newReg); + super(I_PHI, new Operand.RegisterOperand(dest)); + this.inputs = inputs.toArray(new Register[inputs.size()]); } public void replaceInput(int i, Register newReg) { - inputs.set(i, new Operand.RegisterOperand(newReg)); + inputs[i] = newReg; + } + public Register input(int i) { + return inputs[i]; } @Override public StringBuilder toStr(StringBuilder sb) { - sb.append(dest).append(" = phi("); - for (int i = 0; i < inputs.size(); i++) { + sb.append(def).append(" = phi("); + for (int i = 0; i < inputs.length; i++) { if (i > 0) sb.append(", "); - sb.append(inputs.get(i)); + sb.append(inputs[i].name()); } sb.append(")"); return sb; @@ -678,28 +364,13 @@ public StringBuilder toStr(StringBuilder sb) { } public static class ArgInstruction extends Instruction { - Operand.RegisterOperand arg; - - @Override - public boolean definesVar() { - return true; - } - @Override - public Register def() { - return arg.reg; - } - - @Override - public void replaceDef(Register newReg) { - arg.replaceRegister(newReg); - } - public ArgInstruction(Operand.RegisterOperand arg) { - this.arg = arg; + super(I_ARG, arg); } + public Operand.RegisterOperand arg() { return def; } @Override public StringBuilder toStr(StringBuilder sb) { - return sb.append("arg ").append(arg); + return sb.append("arg ").append(arg()); } } diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/InterferenceGraph.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/InterferenceGraph.java index ede1f7a..441a264 100644 --- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/InterferenceGraph.java +++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/InterferenceGraph.java @@ -24,11 +24,32 @@ public void addEdge(Integer from, Integer to) { set2.add(from); } - public boolean containsEdge(Integer from, Integer to) { + public boolean interfere(Integer from, Integer to) { var set = edges.get(from); return set != null && set.contains(to); } + /** + * The source is replaced by target in the graph. + * All nodes that interfered with source are made to interfere with target. + */ + public void rename(Integer source, Integer target) { + // Move all interferences + var fromSet = edges.remove(source); + var toSet = edges.get(target); + toSet.addAll(fromSet); + // If any node interfered with from + // it should now interfere with to + for (var k: edges.keySet()) { + var set = edges.get(k); + if (set.contains(source)) { + set.remove(source); + if (k != target) + set.add(target); + } + } + } + public Set adjacents(Integer node) { return edges.get(node); } diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/InterferenceGraphBuilder.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/InterferenceGraphBuilder.java index bac880e..cc8946c 100644 --- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/InterferenceGraphBuilder.java +++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/InterferenceGraphBuilder.java @@ -17,15 +17,14 @@ public InterferenceGraph build(CompiledFunction function) { // end of the block. // Process each instruction in the block in reverse order for (var i: b.instructions.reversed()) { - if (i instanceof Instruction.Move || - i instanceof Instruction.Phi) { + if (i instanceof Instruction.Move) { // Move(copy) instructions are handled specially to avoid // adding an undesirable interference between the source and // destination (section 2.2.2 in Briggs thesis) // Engineering a Compiler: The copy operation does not // create an interference cause both values can occupy the // same register - // Same argument applies to phi. + // Same argument applies to phi. (Phi's do not generate uses) liveNow.remove(i.uses()); } if (i.definesVar()) { diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Liveness.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Liveness.java index d9c9382..b222727 100644 --- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Liveness.java +++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Liveness.java @@ -1,12 +1,17 @@ package com.compilerprogramming.ezlang.compiler; -import java.util.BitSet; import java.util.List; /** * Compute LiveOut for each Basic Block * Implementation is based on description in 'Engineering a Compiler' 2nd ed. * pages 446-447. + * + * It turns out that this dataflow implementation cannot correctly handle + * phis, because with phis, the inputs are live at the predecessor blocks. + * We have to look at alternative approaches when input is SSA form. + * Surprisingly even with this approach, the lost copy and swap problems + * appear to work correctly. */ public class Liveness { diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SSATransform.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SSATransform.java index 30a6e8f..c89cffc 100644 --- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SSATransform.java +++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SSATransform.java @@ -51,11 +51,9 @@ private void findGlobalVars() { for (BasicBlock block : blocks) { var varKill = new HashSet(); for (Instruction instruction: block.instructions) { - if (instruction.usesVars()) { - for (Register reg : instruction.uses()) { - if (!varKill.contains(reg.nonSSAId())) { - nonLocalNames[reg.nonSSAId()] = reg; - } + for (Register reg : instruction.uses()) { + if (!varKill.contains(reg.nonSSAId())) { + nonLocalNames[reg.nonSSAId()] = reg; } } if (instruction.definesVar()) { @@ -127,14 +125,14 @@ void search(BasicBlock block) { if (instruction instanceof Instruction.Phi) continue; // first replace x,y - if (instruction.usesVars()) { - var uses = instruction.uses(); + var uses = instruction.uses(); + if (!uses.isEmpty()) { Register[] newUses = new Register[uses.size()]; for (int i = 0; i < newUses.length; i++) { Register oldReg = uses.get(i); newUses[i] = stacks[oldReg.nonSSAId()].top(); - instruction.replaceUses(newUses); } + instruction.replaceUses(newUses); } // then replace v if (instruction.definesVar()) { @@ -146,7 +144,7 @@ void search(BasicBlock block) { for (BasicBlock s: block.successors) { int j = whichPred(s,block); for (Instruction.Phi phi: s.phis()) { - Register oldReg = phi.inputs.get(j).reg; + Register oldReg = phi.input(j); phi.replaceInput(j, stacks[oldReg.nonSSAId()].top()); } } 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 d98352e..a3a949e 100644 --- a/optvm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java +++ b/optvm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java @@ -45,10 +45,10 @@ public Value interpret(ExecutionStack execStack, Frame frame) { instruction = currentBlock.instructions.get(ip); switch (instruction) { case Instruction.Ret retInst -> { - if (retInst.value instanceof Operand.ConstantOperand constantOperand) { + if (retInst.value() instanceof Operand.ConstantOperand constantOperand) { execStack.stack[base] = new Value.IntegerValue(constantOperand.value); } - else if (retInst.value instanceof Operand.RegisterOperand registerOperand) { + else if (retInst.value() instanceof Operand.RegisterOperand registerOperand) { execStack.stack[base] = execStack.stack[base+registerOperand.slot()]; } else throw new IllegalStateException(); @@ -56,11 +56,11 @@ 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) { + if (moveInst.to() instanceof Operand.RegisterOperand toReg) { + if (moveInst.from() instanceof Operand.RegisterOperand fromReg) { execStack.stack[base + toReg.slot()] = execStack.stack[base + fromReg.slot()]; } - else if (moveInst.from instanceof Operand.ConstantOperand constantOperand) { + else if (moveInst.from() instanceof Operand.ConstantOperand constantOperand) { execStack.stack[base + toReg.slot()] = new Value.IntegerValue(constantOperand.value); } else throw new IllegalStateException(); @@ -75,7 +75,7 @@ else if (moveInst.from instanceof Operand.ConstantOperand constantOperand) { } case Instruction.ConditionalBranch cbrInst -> { boolean condition; - if (cbrInst.condition instanceof Operand.RegisterOperand registerOperand) { + if (cbrInst.condition() instanceof Operand.RegisterOperand registerOperand) { Value value = execStack.stack[base + registerOperand.slot()]; if (value instanceof Value.IntegerValue integerValue) { condition = integerValue.value != 0; @@ -84,7 +84,7 @@ else if (moveInst.from instanceof Operand.ConstantOperand constantOperand) { condition = value != null; } } - else if (cbrInst.condition instanceof Operand.ConstantOperand constantOperand) { + else if (cbrInst.condition() instanceof Operand.ConstantOperand constantOperand) { condition = constantOperand.value != 0; } else throw new IllegalStateException(); @@ -100,8 +100,9 @@ else if (cbrInst.condition instanceof Operand.ConstantOperand constantOperand) { // Copy args to new frame int baseReg = base+currentFunction.frameSize(); int reg = baseReg; - for (Operand.RegisterOperand arg: callInst.args) { - execStack.stack[base + reg] = execStack.stack[base + arg.slot()]; + for (Operand arg: callInst.args()) { + Operand.RegisterOperand param = (Operand.RegisterOperand) arg; + execStack.stack[base + reg] = execStack.stack[base + param.slot()]; reg += 1; } // Call function @@ -109,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().slot()] = 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; + Operand.RegisterOperand unaryOperand = (Operand.RegisterOperand) unaryInst.operand(); Value unaryValue = execStack.stack[base + unaryOperand.slot()]; 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().slot()] = 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().slot()] = new Value.IntegerValue(integerValue.value==0?1:0); break; default: throw new CompilerException("Invalid unary op"); } } @@ -130,14 +131,14 @@ else if (cbrInst.condition instanceof Operand.ConstantOperand constantOperand) { case Instruction.Binary binaryInst -> { long x, y; long value = 0; - if (binaryInst.left instanceof Operand.ConstantOperand constant) + if (binaryInst.left() instanceof Operand.ConstantOperand constant) x = constant.value; - else if (binaryInst.left instanceof Operand.RegisterOperand registerOperand) + else if (binaryInst.left() instanceof Operand.RegisterOperand registerOperand) x = ((Value.IntegerValue) execStack.stack[base + registerOperand.slot()]).value; else throw new IllegalStateException(); - if (binaryInst.right instanceof Operand.ConstantOperand constant) + if (binaryInst.right() instanceof Operand.ConstantOperand constant) y = constant.value; - else if (binaryInst.right instanceof Operand.RegisterOperand registerOperand) + else if (binaryInst.right() instanceof Operand.RegisterOperand registerOperand) y = ((Value.IntegerValue) execStack.stack[base + registerOperand.slot()]).value; else throw new IllegalStateException(); switch (binaryInst.binOp) { @@ -154,41 +155,41 @@ 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().slot()] = new Value.IntegerValue(value); } case Instruction.NewArray newArrayInst -> { - execStack.stack[base + newArrayInst.destOperand.slot()] = new Value.ArrayValue(newArrayInst.type); + execStack.stack[base + newArrayInst.destOperand().slot()] = 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().slot()] = new Value.StructValue(newStructInst.type); } case Instruction.AStoreAppend arrayAppendInst -> { - Value.ArrayValue arrayValue = (Value.ArrayValue) execStack.stack[base + arrayAppendInst.array.slot()]; - if (arrayAppendInst.value instanceof Operand.ConstantOperand constant) { + Value.ArrayValue arrayValue = (Value.ArrayValue) execStack.stack[base + arrayAppendInst.array().slot()]; + if (arrayAppendInst.value() instanceof Operand.ConstantOperand constant) { arrayValue.values.add(new Value.IntegerValue(constant.value)); } - else if (arrayAppendInst.value instanceof Operand.RegisterOperand registerOperand) { + else if (arrayAppendInst.value() instanceof Operand.RegisterOperand registerOperand) { arrayValue.values.add(execStack.stack[base + registerOperand.slot()]); } else throw new IllegalStateException(); } case Instruction.ArrayStore arrayStoreInst -> { - if (arrayStoreInst.arrayOperand instanceof Operand.RegisterOperand arrayOperand) { + if (arrayStoreInst.arrayOperand() instanceof Operand.RegisterOperand arrayOperand) { Value.ArrayValue arrayValue = (Value.ArrayValue) execStack.stack[base + arrayOperand.slot()]; int index = 0; - if (arrayStoreInst.indexOperand instanceof Operand.ConstantOperand constant) { + if (arrayStoreInst.indexOperand() instanceof Operand.ConstantOperand constant) { index = (int) constant.value; } - else if (arrayStoreInst.indexOperand instanceof Operand.RegisterOperand registerOperand) { + else if (arrayStoreInst.indexOperand() instanceof Operand.RegisterOperand registerOperand) { Value.IntegerValue indexValue = (Value.IntegerValue) execStack.stack[base + registerOperand.slot()]; index = (int) indexValue.value; } else throw new IllegalStateException(); Value value; - if (arrayStoreInst.sourceOperand instanceof Operand.ConstantOperand constantOperand) { + if (arrayStoreInst.sourceOperand() instanceof Operand.ConstantOperand constantOperand) { value = new Value.IntegerValue(constantOperand.value); } - else if (arrayStoreInst.sourceOperand instanceof Operand.RegisterOperand registerOperand) { + else if (arrayStoreInst.sourceOperand() instanceof Operand.RegisterOperand registerOperand) { value = execStack.stack[base + registerOperand.slot()]; } else throw new IllegalStateException(); @@ -196,27 +197,27 @@ else if (arrayStoreInst.sourceOperand instanceof Operand.RegisterOperand registe } else throw new IllegalStateException(); } case Instruction.ArrayLoad arrayLoadInst -> { - if (arrayLoadInst.arrayOperand instanceof Operand.RegisterOperand arrayOperand) { + if (arrayLoadInst.arrayOperand() instanceof Operand.RegisterOperand arrayOperand) { Value.ArrayValue arrayValue = (Value.ArrayValue) execStack.stack[base + arrayOperand.slot()]; - if (arrayLoadInst.indexOperand instanceof Operand.ConstantOperand constant) { - execStack.stack[base + arrayLoadInst.destOperand.slot()] = arrayValue.values.get((int) constant.value); + if (arrayLoadInst.indexOperand() instanceof Operand.ConstantOperand constant) { + execStack.stack[base + arrayLoadInst.destOperand().slot()] = arrayValue.values.get((int) constant.value); } - else if (arrayLoadInst.indexOperand instanceof Operand.RegisterOperand registerOperand) { + 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); + execStack.stack[base + arrayLoadInst.destOperand().slot()] = 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) { + if (setFieldInst.structOperand() instanceof Operand.RegisterOperand structOperand) { Value.StructValue structValue = (Value.StructValue) execStack.stack[base + structOperand.slot()]; int index = setFieldInst.fieldIndex; Value value; - if (setFieldInst.sourceOperand instanceof Operand.ConstantOperand constant) { + if (setFieldInst.sourceOperand() instanceof Operand.ConstantOperand constant) { value = new Value.IntegerValue(constant.value); } - else if (setFieldInst.sourceOperand instanceof Operand.RegisterOperand registerOperand) { + else if (setFieldInst.sourceOperand() instanceof Operand.RegisterOperand registerOperand) { value = execStack.stack[base + registerOperand.slot()]; } else throw new IllegalStateException(); @@ -224,10 +225,10 @@ else if (setFieldInst.sourceOperand instanceof Operand.RegisterOperand registerO } else throw new IllegalStateException(); } case Instruction.GetField getFieldInst -> { - if (getFieldInst.structOperand instanceof Operand.RegisterOperand structOperand) { + if (getFieldInst.structOperand() instanceof Operand.RegisterOperand structOperand) { Value.StructValue structValue = (Value.StructValue) execStack.stack[base + structOperand.slot()]; int index = getFieldInst.fieldIndex; - execStack.stack[base + getFieldInst.destOperand.slot()] = structValue.fields[index]; + execStack.stack[base + getFieldInst.destOperand().slot()] = structValue.fields[index]; } else throw new IllegalStateException(); } case Instruction.ArgInstruction argInst -> {} diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestChaitinRegAllocator.java b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestChaitinRegAllocator.java new file mode 100644 index 0000000..3c09ac0 --- /dev/null +++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestChaitinRegAllocator.java @@ -0,0 +1,21 @@ +package com.compilerprogramming.ezlang.compiler; + +import org.junit.Assert; +import org.junit.Test; + +public class TestChaitinRegAllocator { + + /* Test move does not interfere with uses */ + @Test + public void test4() { + CompiledFunction function = TestInterferenceGraph.buildTest4(); + var graph = new InterferenceGraphBuilder().build(function); + System.out.println(graph.generateDotOutput()); + var edges = graph.getEdges(); + Assert.assertEquals(2, edges.size()); + Assert.assertTrue(edges.contains(new InterferenceGraph.Edge(0, 1))); + Assert.assertTrue(edges.contains(new InterferenceGraph.Edge(0, 2))); + new ChaitinGraphColoringRegisterAllocator(function); + System.out.println(function.toStr(new StringBuilder(), true)); + } +} \ 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 674df1f..bf05f1f 100644 --- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestInterferenceGraph.java +++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestInterferenceGraph.java @@ -154,7 +154,7 @@ public void test3() { } /* Test move does not interfere with uses */ - private CompiledFunction buildTest4() { + public static CompiledFunction buildTest4() { TypeDictionary typeDictionary = new TypeDictionary(); Type.TypeFunction functionType = new Type.TypeFunction("foo"); functionType.setReturnType(typeDictionary.VOID); @@ -198,4 +198,72 @@ public void test4() { Assert.assertTrue(edges.contains(new InterferenceGraph.Edge(0, 1))); Assert.assertTrue(edges.contains(new InterferenceGraph.Edge(0, 2))); } + + @Test + public void test5() { + InterferenceGraph graph = new InterferenceGraph(); + graph.addEdge(1, 2); + Assert.assertTrue(graph.interfere(1, 2)); + Assert.assertTrue(graph.interfere(2, 1)); + Assert.assertTrue(graph.adjacents(1).contains(2)); + Assert.assertTrue(graph.adjacents(2).contains(1)); + } + + @Test + public void test6() { + InterferenceGraph graph = new InterferenceGraph(); + graph.addEdge(1, 2); + graph.addEdge(1, 3); + Assert.assertTrue(graph.interfere(1, 2)); + Assert.assertTrue(graph.interfere(2, 1)); + Assert.assertTrue(graph.interfere(1, 3)); + Assert.assertTrue(graph.interfere(3, 1)); + Assert.assertFalse(graph.interfere(2, 3)); + Assert.assertFalse(graph.interfere(3, 2)); + Assert.assertTrue(graph.adjacents(1).contains(2)); + Assert.assertTrue(graph.adjacents(1).contains(3)); + Assert.assertTrue(graph.adjacents(2).contains(1)); + Assert.assertTrue(graph.adjacents(3).contains(1)); + System.out.println(graph.generateDotOutput()); + graph.rename(2, 3); + System.out.println(graph.generateDotOutput()); + Assert.assertFalse(graph.interfere(1, 2)); + Assert.assertFalse(graph.interfere(2, 1)); + Assert.assertTrue(graph.interfere(1, 3)); + Assert.assertTrue(graph.interfere(3, 1)); + Assert.assertFalse(graph.interfere(2, 3)); + Assert.assertFalse(graph.interfere(3, 2)); + Assert.assertFalse(graph.adjacents(1).contains(2)); + Assert.assertTrue(graph.adjacents(1).contains(3)); + Assert.assertTrue(graph.adjacents(3).contains(1)); + } + + @Test + public void test7() { + InterferenceGraph graph = new InterferenceGraph(); + graph.addEdge(1, 2); + graph.addEdge(1, 3); + Assert.assertTrue(graph.interfere(1, 2)); + Assert.assertTrue(graph.interfere(2, 1)); + Assert.assertTrue(graph.interfere(1, 3)); + Assert.assertTrue(graph.interfere(3, 1)); + Assert.assertFalse(graph.interfere(2, 3)); + Assert.assertFalse(graph.interfere(3, 2)); + Assert.assertTrue(graph.adjacents(1).contains(2)); + Assert.assertTrue(graph.adjacents(1).contains(3)); + Assert.assertTrue(graph.adjacents(2).contains(1)); + Assert.assertTrue(graph.adjacents(3).contains(1)); + System.out.println(graph.generateDotOutput()); + graph.rename(1, 2); + System.out.println(graph.generateDotOutput()); + Assert.assertFalse(graph.interfere(1, 2)); + Assert.assertFalse(graph.interfere(2, 1)); + Assert.assertFalse(graph.interfere(1, 3)); + Assert.assertFalse(graph.interfere(3, 1)); + Assert.assertTrue(graph.interfere(2, 3)); + Assert.assertTrue(graph.interfere(3, 2)); + Assert.assertTrue(graph.adjacents(2).contains(3)); + Assert.assertTrue(graph.adjacents(3).contains(2)); + } + } 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 8096dae..6f9a0a4 100644 --- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestLiveness.java +++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestLiveness.java @@ -289,5 +289,72 @@ func foo()->Int """, actual); } + // See not on SSA and liveness in Liveness. + @Test + public void testSwapProblem() { + CompiledFunction function = TestSSATransform.buildSwapTest(); + function.livenessAnalysis(); + 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 +L0: + arg p + a1 = 42 + b1 = 24 + goto L2 + #UEVAR = {} + #VARKILL = {0, 1, 3} + #LIVEOUT = {0} +L2: + a2 = phi(a1, b2) + b2 = phi(b1, a2) + if p goto L2 else goto L1 + #UEVAR = {0} + #VARKILL = {2, 4} + #LIVEOUT = {0} +L1: + #UEVAR = {} + #VARKILL = {} + #LIVEOUT = {} +""", actual); + } + + @Test + public void testLostCopyProblem() { + CompiledFunction function = TestSSATransform.buildLostCopyTest(); + function.livenessAnalysis(); + 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 +L0: + arg p + x1 = 1 + goto L2 + #UEVAR = {} + #VARKILL = {0, 1} + #LIVEOUT = {0} +L2: + x2 = phi(x1, x3) + x3 = x2+1 + if p goto L2 else goto L1 + #UEVAR = {0} + #VARKILL = {2, 3} + #LIVEOUT = {0, 3} +L1: + ret x2 + #UEVAR = {3} + #VARKILL = {} + #LIVEOUT = {} +""", actual); + } } 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 a0e2825..cbe8cab 100644 --- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java +++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestSSATransform.java @@ -633,7 +633,7 @@ func bar(arg: Int)->Int { * This test case is based on the example snippet from Briggs paper * illustrating the lost copy problem. */ - private CompiledFunction buildLostCopyTest() { + static CompiledFunction buildLostCopyTest() { TypeDictionary typeDictionary = new TypeDictionary(); Type.TypeFunction functionType = new Type.TypeFunction("foo"); functionType.addArg(new Symbol.ParameterSymbol("p", typeDictionary.INT)); @@ -701,16 +701,16 @@ public void testLostCopyProblem() { * This test case is based on the example snippet from Briggs paper * illustrating the swap problem. */ - private CompiledFunction buildSwapTest() { + static CompiledFunction buildSwapTest() { TypeDictionary typeDictionary = new TypeDictionary(); Type.TypeFunction functionType = new Type.TypeFunction("foo"); functionType.addArg(new Symbol.ParameterSymbol("p", typeDictionary.INT)); + functionType.setReturnType(typeDictionary.VOID); CompiledFunction function = new CompiledFunction(functionType); RegisterPool regPool = function.registerPool; Register p = regPool.newReg("p", typeDictionary.INT); Register a1 = regPool.newReg("a1", typeDictionary.INT); Register a2 = regPool.newReg("a2", typeDictionary.INT); - Register a3 = regPool.newReg("a3", 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))); @@ -757,9 +757,9 @@ public void testSwapProblem() { b2 = b1 goto L2 L2: - a2_6 = a2 + a2_5 = a2 a2 = b2 - b2 = a2_6 + b2 = a2_5 if p goto L2 else goto L1 L1: """; From e77b0fa29057e7c9fba51507555c87850b02c352 Mon Sep 17 00:00:00 2001 From: dibyendumajumdar Date: Mon, 23 Dec 2024 21:54:21 +0000 Subject: [PATCH 4/4] #12 Update to Liveness calculation to handle phis correctly (hopefully). --- .../ezlang/compiler/BasicBlock.java | 18 +++- .../ezlang/compiler/ExitSSA.java | 4 +- .../ezlang/compiler/Instruction.java | 30 +++++- .../ezlang/compiler/Liveness.java | 71 ++++++++++--- .../ezlang/compiler/SSATransform.java | 4 +- .../ezlang/compiler/TestLiveness.java | 99 +++++++++++++++++-- 6 files changed, 197 insertions(+), 29 deletions(-) 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 91fe9ac..c34659f 100644 --- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java +++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java @@ -61,6 +61,19 @@ public class BasicBlock { * head of some block that is a successor of this block. */ LiveSet liveOut; + + /** + * Inputs to successor block's phi function + */ + LiveSet phiUses; + /** + * Phi definitions in this block + */ + LiveSet phiDefs; + /** + * Live in set + */ + LiveSet liveIn; // ----------------------- public BasicBlock(int bid, boolean loopHead) { @@ -92,7 +105,7 @@ public void addSuccessor(BasicBlock successor) { public void insertPhiFor(Register var) { for (Instruction i: instructions) { if (i instanceof Instruction.Phi phi) { - if (phi.def().nonSSAId() == var.nonSSAId()) + if (phi.value().nonSSAId() == var.nonSSAId()) // already added return; } @@ -124,8 +137,11 @@ public static StringBuilder toStr(StringBuilder sb, BasicBlock bb, BitSet visite n.toStr(sb).append("\n"); } if (dumpLiveness) { + if (bb.phiDefs != null) sb.append(" #PHIDEFS = ").append(bb.phiDefs.toString()).append("\n"); + if (bb.phiUses != null) sb.append(" #PHIUSES = ").append(bb.phiUses.toString()).append("\n"); if (bb.UEVar != null) sb.append(" #UEVAR = ").append(bb.UEVar.toString()).append("\n"); if (bb.varKill != null) sb.append(" #VARKILL = ").append(bb.varKill.toString()).append("\n"); + if (bb.liveIn != null) sb.append(" #LIVEIN = ").append(bb.liveIn.toString()).append("\n"); if (bb.liveOut != null) sb.append(" #LIVEOUT = ").append(bb.liveOut.toString()).append("\n"); } for (BasicBlock succ: bb.successors) { diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA.java index fefa4fb..ae64666 100644 --- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA.java +++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA.java @@ -84,7 +84,7 @@ private void scheduleCopies(BasicBlock block, List pushed) { for (BasicBlock s: block.successors) { int j = SSATransform.whichPred(s, block); for (Instruction.Phi phi: s.phis()) { - Register dst = phi.def(); + Register dst = phi.value(); Register src = phi.input(j); // jth operand of phi node copySet.add(new CopyItem(src, dst)); map.put(src.id, src); @@ -166,7 +166,7 @@ private void insertAfterPhi(BasicBlock bb, Register phiDef, Instruction newInst) for (int pos = 0; pos < bb.instructions.size(); pos++) { Instruction i = bb.instructions.get(pos); if (i instanceof Instruction.Phi phi) { - if (phi.def().id == phiDef.id) { + if (phi.value().id == phiDef.id) { insertionPos = pos+1; // After phi break; } 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 88de2d5..cd6166a 100644 --- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java +++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java @@ -337,12 +337,16 @@ public StringBuilder toStr(StringBuilder sb) { } /** - * Phi does not generate uses. + * Phi does not generate uses or defs directly, instead + * they are treated as a special case. + * To avoid bugs we do not use the def or uses. */ public static class Phi extends Instruction { + public Register value; public final Register[] inputs; - public Phi(Register dest, List inputs) { - super(I_PHI, new Operand.RegisterOperand(dest)); + public Phi(Register value, List inputs) { + super(I_PHI); + this.value = value; this.inputs = inputs.toArray(new Register[inputs.size()]); } public void replaceInput(int i, Register newReg) { @@ -352,8 +356,26 @@ public Register input(int i) { return inputs[i]; } @Override + public Register def() { + throw new UnsupportedOperationException(); + } + @Override + public void replaceDef(Register newReg) { + throw new UnsupportedOperationException(); + } + @Override + public boolean definesVar() { + return false; + } + public Register value() { + return value; + } + public void replaceValue(Register newReg) { + this.value = newReg; + } + @Override public StringBuilder toStr(StringBuilder sb) { - sb.append(def).append(" = phi("); + sb.append(value().name()).append(" = phi("); for (int i = 0; i < inputs.length; i++) { if (i > 0) sb.append(", "); sb.append(inputs[i].name()); diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Liveness.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Liveness.java index b222727..a125547 100644 --- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Liveness.java +++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/Liveness.java @@ -4,40 +4,81 @@ /** * Compute LiveOut for each Basic Block - * Implementation is based on description in 'Engineering a Compiler' 2nd ed. + * + * Original Implementation was based on description in 'Engineering a Compiler' 2nd ed. * pages 446-447. * - * It turns out that this dataflow implementation cannot correctly handle + * It turned out that this dataflow implementation cannot correctly handle * phis, because with phis, the inputs are live at the predecessor blocks. * We have to look at alternative approaches when input is SSA form. * Surprisingly even with this approach, the lost copy and swap problems - * appear to work correctly. + * appeared to work correctly. + * + * The new approach is based on formula described in + * Computing Liveness Sets for SSA-Form Programs + * Florian Brandner, Benoit Boissinot, Alain Darte, BenoƮt Dupont de Dinechin, Fabrice Rastello + * + * The implementation is the unoptimized simple one. + * However, we have a modification to ensure that if we see a block + * which loops to itself and has Phi cycles, then the Phi is only added to + * PhiDefs. */ public class Liveness { public Liveness(CompiledFunction function) { List blocks = BBHelper.findAllBlocks(function.entry); RegisterPool regPool = function.registerPool; - init(regPool, blocks); + initBlocks(regPool, blocks); + init(blocks); computeLiveness(blocks); function.hasLiveness = true; } - private void init(RegisterPool regPool, List blocks) { + private void initBlocks(RegisterPool regPool, List blocks) { int numRegisters = regPool.numRegisters(); for (BasicBlock block : blocks) { block.UEVar = new LiveSet(numRegisters); block.varKill = new LiveSet(numRegisters); block.liveOut = new LiveSet(numRegisters); + block.liveIn = new LiveSet(numRegisters); + block.phiUses = new LiveSet(numRegisters); + block.phiDefs = new LiveSet(numRegisters); + } + } + + private void init(List blocks) { + for (BasicBlock block : blocks) { + // We st up phiDefs first because when we + // look at phi uses we need to refer back here + // see comments on phi cycles below + for (Instruction instruction : block.instructions) { + if (instruction instanceof Instruction.Phi phi) { + block.phiDefs.add(phi.value()); + } + else break; + } for (Instruction instruction : block.instructions) { for (Register use : instruction.uses()) { if (!block.varKill.isMember(use)) block.UEVar.add(use); } - if (instruction.definesVar()) { + if (instruction.definesVar() && !(instruction instanceof Instruction.Phi)) { Register def = instruction.def(); block.varKill.add(def); } + if (instruction instanceof Instruction.Phi phi) { + for (int i = 0; i < block.predecessors.size(); i++) { + BasicBlock pred = block.predecessors.get(i); + Register use = phi.input(i); + // We can have a block referring it its own phis + // if there is loop back and there are cycles + // such as e.g. the swap copy problem + if (pred == block && + block.phiDefs.isMember(use)) + continue; + pred.phiUses.add(use); + } + } } } } @@ -53,17 +94,19 @@ private void computeLiveness(List blocks) { } } + // See 'Computing Liveness Sets for SSA-Form Programs' + // LiveIn(B) = PhiDefs(B) U UpwardExposed(B) U (LiveOut(B) \ Defs(B)) + // LiveOut(B) = U all S (LiveIn(S) \ PhiDefs(S)) U PhiUses(B) private boolean recomputeLiveOut(BasicBlock block) { LiveSet oldLiveOut = block.liveOut.dup(); - for (BasicBlock m: block.successors) { - LiveSet mLiveIn = m.liveOut.dup(); - // LiveOut(m) intersect not VarKill(m) - mLiveIn.intersectNot(m.varKill); - // UEVar(m) union (LiveOut(m) intersect not VarKill(m)) - mLiveIn.union(m.UEVar); - // LiveOut(block) =union (UEVar(m) union (LiveOut(m) intersect not VarKill(m))) - block.liveOut.union(mLiveIn); + LiveSet t = block.liveOut.dup().intersectNot(block.varKill); + block.liveIn.union(block.phiDefs).union(block.UEVar).union(t); + block.liveOut.clear(); + for (BasicBlock s: block.successors) { + t = s.liveIn.dup().intersectNot(s.phiDefs); + block.liveOut.union(t); } + block.liveOut.union(block.phiUses); return !oldLiveOut.equals(block.liveOut); } } diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SSATransform.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SSATransform.java index c89cffc..da802ea 100644 --- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SSATransform.java +++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SSATransform.java @@ -115,8 +115,8 @@ Register makeVersion(Register reg) { void search(BasicBlock block) { // Replace v = phi(...) with v_i = phi(...) for (Instruction.Phi phi: block.phis()) { - Register ssaReg = makeVersion(phi.def()); - phi.replaceDef(ssaReg); + Register ssaReg = makeVersion(phi.value()); + phi.replaceValue(ssaReg); } // for each instruction v = x op y // first replace x,y 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 6f9a0a4..b1b6222 100644 --- a/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestLiveness.java +++ b/optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestLiveness.java @@ -56,25 +56,37 @@ func foo() i = 1 s = 1 goto L2 + #PHIDEFS = {} + #PHIUSES = {} #UEVAR = {} #VARKILL = {0, 1} + #LIVEIN = {} #LIVEOUT = {0, 1} L2: if 1 goto L3 else goto L4 + #PHIDEFS = {} + #PHIUSES = {} #UEVAR = {} #VARKILL = {} + #LIVEIN = {0, 1} #LIVEOUT = {0, 1} L3: %t2 = i==5 if %t2 goto L5 else goto L6 + #PHIDEFS = {} + #PHIUSES = {} #UEVAR = {0} #VARKILL = {2} + #LIVEIN = {0, 1} #LIVEOUT = {0, 1} L5: s = 0 goto L6 + #PHIDEFS = {} + #PHIUSES = {} #UEVAR = {} #VARKILL = {1} + #LIVEIN = {0} #LIVEOUT = {0, 1} L6: %t3 = s+1 @@ -83,29 +95,44 @@ func foo() i = %t4 %t5 = i<10 if %t5 goto L7 else goto L8 + #PHIDEFS = {} + #PHIUSES = {} #UEVAR = {0, 1} #VARKILL = {0, 1, 3, 4, 5} + #LIVEIN = {0, 1} #LIVEOUT = {0, 1} L7: goto L2 + #PHIDEFS = {} + #PHIUSES = {} #UEVAR = {} #VARKILL = {} + #LIVEIN = {0, 1} #LIVEOUT = {0, 1} L8: goto L4 + #PHIDEFS = {} + #PHIUSES = {} #UEVAR = {} #VARKILL = {} + #LIVEIN = {1} #LIVEOUT = {1} L4: %t6 = s call print params %t6 goto L1 + #PHIDEFS = {} + #PHIUSES = {} #UEVAR = {1} #VARKILL = {6} + #LIVEIN = {1} #LIVEOUT = {} L1: + #PHIDEFS = {} + #PHIUSES = {} #UEVAR = {} #VARKILL = {} + #LIVEIN = {} #LIVEOUT = {} """, output); } @@ -143,20 +170,29 @@ func foo(a: Int,b: Int) arg a arg b goto L2 + #PHIDEFS = {} + #PHIUSES = {} #UEVAR = {} #VARKILL = {0, 1} + #LIVEIN = {} #LIVEOUT = {0, 1} L2: %t2 = b<10 if %t2 goto L3 else goto L4 + #PHIDEFS = {} + #PHIUSES = {} #UEVAR = {1} #VARKILL = {2} + #LIVEIN = {0, 1} #LIVEOUT = {0, 1} L3: %t3 = bInt L0: i = 1 goto L2 + #PHIDEFS = {} + #PHIUSES = {} #UEVAR = {} #VARKILL = {0} + #LIVEIN = {1} #LIVEOUT = {0, 1} L2: if i goto L3 else goto L4 + #PHIDEFS = {} + #PHIUSES = {} #UEVAR = {0} #VARKILL = {} + #LIVEIN = {0, 1} #LIVEOUT = {0, 1} L3: s = 0 goto L4 + #PHIDEFS = {} + #PHIUSES = {} #UEVAR = {} #VARKILL = {1} + #LIVEIN = {0} #LIVEOUT = {0, 1} L4: s = s+i i = i+1 if i goto L2 else goto L5 + #PHIDEFS = {} + #PHIUSES = {} #UEVAR = {0, 1} #VARKILL = {0, 1} + #LIVEIN = {0, 1} #LIVEOUT = {0, 1} L5: ret s goto L1 + #PHIDEFS = {} + #PHIUSES = {} #UEVAR = {1} #VARKILL = {} + #LIVEIN = {1} #LIVEOUT = {} L1: + #PHIDEFS = {} + #PHIUSES = {} #UEVAR = {} #VARKILL = {} + #LIVEIN = {} #LIVEOUT = {} """, actual); } @@ -307,19 +376,28 @@ func foo(p: Int) a1 = 42 b1 = 24 goto L2 + #PHIDEFS = {} + #PHIUSES = {1, 3} #UEVAR = {} #VARKILL = {0, 1, 3} - #LIVEOUT = {0} + #LIVEIN = {} + #LIVEOUT = {0, 1, 3} L2: a2 = phi(a1, b2) b2 = phi(b1, a2) if p goto L2 else goto L1 + #PHIDEFS = {2, 4} + #PHIUSES = {} #UEVAR = {0} - #VARKILL = {2, 4} + #VARKILL = {} + #LIVEIN = {0, 2, 4} #LIVEOUT = {0} L1: + #PHIDEFS = {} + #PHIUSES = {} #UEVAR = {} #VARKILL = {} + #LIVEIN = {} #LIVEOUT = {} """, actual); } @@ -339,20 +417,29 @@ func foo(p: Int)->Int arg p x1 = 1 goto L2 + #PHIDEFS = {} + #PHIUSES = {1} #UEVAR = {} #VARKILL = {0, 1} - #LIVEOUT = {0} + #LIVEIN = {} + #LIVEOUT = {0, 1} L2: x2 = phi(x1, x3) x3 = x2+1 if p goto L2 else goto L1 - #UEVAR = {0} - #VARKILL = {2, 3} - #LIVEOUT = {0, 3} + #PHIDEFS = {3} + #PHIUSES = {2} + #UEVAR = {0, 3} + #VARKILL = {2} + #LIVEIN = {0, 3} + #LIVEOUT = {0, 2, 3} L1: ret x2 + #PHIDEFS = {} + #PHIUSES = {} #UEVAR = {3} #VARKILL = {} + #LIVEIN = {3} #LIVEOUT = {} """, actual); }