|
| 1 | +package com.compilerprogramming.ezlang.compiler; |
| 2 | + |
| 3 | +import java.util.*; |
| 4 | + |
| 5 | +/** |
| 6 | + * Converts from SSA form to non-SSA form. |
| 7 | + * Implementation is based on description in |
| 8 | + * 'Practical Improvements to the Construction and Destruction |
| 9 | + * of Static Single Assignment Form' by Preston Briggs. |
| 10 | + */ |
| 11 | +public class ExitSSA { |
| 12 | + |
| 13 | + CompiledFunction function; |
| 14 | + NameStack[] stacks; |
| 15 | + DominatorTree tree; |
| 16 | + |
| 17 | + public ExitSSA(CompiledFunction function) { |
| 18 | + this.function = function; |
| 19 | + if (!function.isSSA) throw new IllegalStateException(); |
| 20 | + if (!function.hasLiveness) { |
| 21 | + new Liveness().computeLiveness(function); |
| 22 | + } |
| 23 | + tree = new DominatorTree(function.entry); |
| 24 | + initStack(); |
| 25 | + insertCopies(function.entry); |
| 26 | + removePhis(); |
| 27 | + } |
| 28 | + |
| 29 | + private void removePhis() { |
| 30 | + for (BasicBlock block : tree.blocks) { |
| 31 | + block.instructions.removeIf(instruction -> instruction instanceof Instruction.Phi); |
| 32 | + } |
| 33 | + } |
| 34 | + |
| 35 | + /* Algorithm for iterating through blocks to perform phi replacement */ |
| 36 | + private void insertCopies(BasicBlock block) { |
| 37 | + List<Register> pushed = new ArrayList<>(); |
| 38 | + for (Instruction i: block.instructions) { |
| 39 | + // replace all uses u with stacks[i] |
| 40 | + if (i.usesVars()) { |
| 41 | + replaceUses(i); |
| 42 | + } |
| 43 | + } |
| 44 | + scheduleCopies(block, pushed); |
| 45 | + for (BasicBlock c: block.dominatedChildren) { |
| 46 | + insertCopies(c); |
| 47 | + } |
| 48 | + for (Register name: pushed) { |
| 49 | + stacks[name.id].pop(); |
| 50 | + } |
| 51 | + } |
| 52 | + |
| 53 | + /** |
| 54 | + * replace all uses u with stacks[i] |
| 55 | + */ |
| 56 | + private void replaceUses(Instruction i) { |
| 57 | + var oldUses = i.uses(); |
| 58 | + Register[] newUses = new Register[oldUses.size()]; |
| 59 | + for (int u = 0; u < oldUses.size(); u++) { |
| 60 | + Register use = oldUses.get(u); |
| 61 | + if (!stacks[use.id].isEmpty()) |
| 62 | + newUses[u] = stacks[use.id].top(); |
| 63 | + else |
| 64 | + newUses[u] = use; |
| 65 | + } |
| 66 | + i.replaceUses(newUses); |
| 67 | + } |
| 68 | + |
| 69 | + static class CopyItem { |
| 70 | + Register src; |
| 71 | + Register dest; |
| 72 | + boolean removed; |
| 73 | + |
| 74 | + public CopyItem(Register src, Register dest) { |
| 75 | + this.src = src; |
| 76 | + this.dest = dest; |
| 77 | + this.removed = false; |
| 78 | + } |
| 79 | + } |
| 80 | + |
| 81 | + private void scheduleCopies(BasicBlock block, List<Register> pushed) { |
| 82 | + /* Pass 1 - Initialize data structures */ |
| 83 | + /* In this pass we count the number of times a name is used by other phi-nodes */ |
| 84 | + List<CopyItem> copySet = new ArrayList<>(); |
| 85 | + Map<Integer, Register> map = new HashMap<>(); |
| 86 | + BitSet usedByAnother = new BitSet(function.registerPool.numRegisters()*2); |
| 87 | + for (BasicBlock s: block.successors) { |
| 88 | + int j = SSATransform.whichPred(s, block); |
| 89 | + for (Instruction.Phi phi: s.phis()) { |
| 90 | + Register dst = phi.def(); |
| 91 | + Register src = phi.inputs.get(j).reg; // jth operand of phi node |
| 92 | + copySet.add(new CopyItem(src, dst)); |
| 93 | + map.put(src.id, src); |
| 94 | + map.put(dst.id, dst); |
| 95 | + usedByAnother.set(src.id); |
| 96 | + } |
| 97 | + } |
| 98 | + |
| 99 | + /* Pass 2: setup up the worklist of initial copies */ |
| 100 | + /* In this pass we build a worklist of names that are not used in other phi nodes */ |
| 101 | + List<CopyItem> workList = new ArrayList<>(); |
| 102 | + for (CopyItem copyItem: copySet) { |
| 103 | + if (!usedByAnother.get(copyItem.dest.id)) { |
| 104 | + copyItem.removed = true; |
| 105 | + workList.add(copyItem); |
| 106 | + } |
| 107 | + } |
| 108 | + copySet.removeIf(copyItem -> copyItem.removed); |
| 109 | + |
| 110 | + /* Pass 3: iterate over the worklist, inserting copies */ |
| 111 | + /* Copy operations whose destinations are not used by other copy operations can be scheduled immediately */ |
| 112 | + /* Each time we insert a copy operation we add the source of that op to the worklist */ |
| 113 | + while (!workList.isEmpty() || !copySet.isEmpty()) { |
| 114 | + while (!workList.isEmpty()) { |
| 115 | + CopyItem copyItem = workList.remove(0); |
| 116 | + Register src = copyItem.src; |
| 117 | + Register dest = copyItem.dest; |
| 118 | + if (block.liveOut.get(dest.id)) { |
| 119 | + /* Insert a copy from dest to a new temp t at phi node defining dest */ |
| 120 | + Register t = insertCopy(block, dest); |
| 121 | + stacks[dest.id].push(t); |
| 122 | + pushed.add(t); |
| 123 | + } |
| 124 | + /* Insert a copy operation from map[src] to dst at end of BB */ |
| 125 | + appendCopy(block, map.get(src.id), dest); |
| 126 | + map.put(src.id, dest); |
| 127 | + /* If src is the name of a dest in copySet add item to worklist */ |
| 128 | + CopyItem item = isDest(copySet, src); |
| 129 | + if (item != null) { |
| 130 | + workList.add(item); |
| 131 | + } |
| 132 | + } |
| 133 | + if (!copySet.isEmpty()) { |
| 134 | + CopyItem copyItem = copySet.remove(0); |
| 135 | + /* Insert a copy from dst to new temp at the end of Block */ |
| 136 | + Register t = appendCopy(block, copyItem.dest); |
| 137 | + map.put(copyItem.dest.id, t); |
| 138 | + workList.add(copyItem); |
| 139 | + } |
| 140 | + } |
| 141 | + } |
| 142 | + |
| 143 | + private void insertAtEnd(BasicBlock bb, Instruction i) { |
| 144 | + assert bb.instructions.size() > 0; |
| 145 | + int pos = bb.instructions.size()-1; |
| 146 | + bb.instructions.add(pos, i); |
| 147 | + } |
| 148 | + |
| 149 | + private void insertAfterPhi(BasicBlock bb, Register phiDef, Instruction newInst) { |
| 150 | + assert bb.instructions.size() > 0; |
| 151 | + int pos = 0; |
| 152 | + while (pos < bb.instructions.size()) { |
| 153 | + Instruction i = bb.instructions.get(pos); |
| 154 | + if (i instanceof Instruction.Phi phi && |
| 155 | + phi.def().id == phiDef.id) { |
| 156 | + pos += 1; |
| 157 | + break; |
| 158 | + } |
| 159 | + } |
| 160 | + if (pos == bb.instructions.size()) { |
| 161 | + throw new IllegalStateException(); |
| 162 | + } |
| 163 | + bb.instructions.add(pos, newInst); |
| 164 | + } |
| 165 | + |
| 166 | + |
| 167 | + /* Insert a copy from dest to new temp at end of BB, and return temp */ |
| 168 | + private Register appendCopy(BasicBlock block, Register dest) { |
| 169 | + var temp = function.registerPool.newReg(dest.name(), dest.type); |
| 170 | + var inst = new Instruction.Move(new Operand.RegisterOperand(dest), new Operand.RegisterOperand(temp)); |
| 171 | + insertAtEnd(block, inst); |
| 172 | + return temp; |
| 173 | + } |
| 174 | + |
| 175 | + /* If src is the name of a dest in copySet return the item */ |
| 176 | + private CopyItem isDest(List<CopyItem> copySet, Register src) { |
| 177 | + for (CopyItem copyItem: copySet) { |
| 178 | + if (copyItem.dest.id == src.id) |
| 179 | + return copyItem; |
| 180 | + } |
| 181 | + return null; |
| 182 | + } |
| 183 | + |
| 184 | + /* Insert a copy from src to dst at end of BB */ |
| 185 | + private void appendCopy(BasicBlock block, Register src, Register dest) { |
| 186 | + var inst = new Instruction.Move(new Operand.RegisterOperand(src), new Operand.RegisterOperand(dest)); |
| 187 | + insertAtEnd(block, inst); |
| 188 | + } |
| 189 | + |
| 190 | + /* Insert a copy dest to a new temp at phi node defining dest, return temp */ |
| 191 | + private Register insertCopy(BasicBlock block, Register dst) { |
| 192 | + var temp = function.registerPool.newReg(dst.name(), dst.type); |
| 193 | + var inst = new Instruction.Move(new Operand.RegisterOperand(dst), new Operand.RegisterOperand(temp)); |
| 194 | + insertAfterPhi(block, dst, inst); |
| 195 | + return temp; |
| 196 | + } |
| 197 | + |
| 198 | + private void initStack() { |
| 199 | + stacks = new NameStack[function.registerPool.numRegisters()]; |
| 200 | + for (int i = 0; i < stacks.length; i++) |
| 201 | + stacks[i] = new NameStack(); |
| 202 | + } |
| 203 | + |
| 204 | + static class NameStack { |
| 205 | + List<Register> stack = new ArrayList<>(); |
| 206 | + void push(Register r) { stack.add(r); } |
| 207 | + Register top() { return stack.getLast(); } |
| 208 | + void pop() { stack.removeLast(); } |
| 209 | + boolean isEmpty() { return stack.isEmpty(); } |
| 210 | + } |
| 211 | +} |
0 commit comments