From 637fce50532e907bae2e757e8b2c266182fadd1a Mon Sep 17 00:00:00 2001 From: Minero Aoki Date: Tue, 23 Sep 2008 13:50:54 +0000 Subject: [PATCH] * net/loveruby/cflat/compiler/CodeGenerator.java: apply jump elimination. * net/loveruby/cflat/asm/PeepholeOptimizer.java: new optimization method: jump elimination. * net/loveruby/cflat/asm/Assembler.java: refactoring: #string -> #toSource. * net/loveruby/cflat/asm/Assembler.java: refactoring: list -> assemblies. * net/loveruby/cflat/asm/Assembly.java: new method #isDirective and #isComment. * net/loveruby/cflat/asm/Directive.java: implement it. * net/loveruby/cflat/asm/AsmComment.java: implement it. * net/loveruby/cflat/asm/Label.java: implement it. * net/loveruby/cflat/asm/Label.java: new method #equals, #hashCode. * net/loveruby/cflat/asm/Instruction.java: add jump related methods to analyze jump instruction. * net/loveruby/cflat/asm/LabelRef.java: new method #label to allow extracting a Label. * net/loveruby/cflat/asm/Register.java: new method #equals, #hashCode. * net/loveruby/cflat/asm/AsmStatistics.java: use Object for map keys. * net/loveruby/cflat/asm/AsmStatistics.java: new method #doesRegisterUsed. * net/loveruby/cflat/utils/ClonableIterator.java: new utility. git-svn-id: file:///Users/aamine/c/gitwork/public/cbc/trunk@4050 1b9489fe-b721-0410-924e-b54b9192deb8 --- ChangeLog | 43 ++++++++ net/loveruby/cflat/asm/AsmComment.java | 4 + net/loveruby/cflat/asm/AsmStatistics.java | 20 ++-- net/loveruby/cflat/asm/Assembler.java | 36 +++---- net/loveruby/cflat/asm/Assembly.java | 8 ++ net/loveruby/cflat/asm/Directive.java | 4 + net/loveruby/cflat/asm/Instruction.java | 19 ++++ net/loveruby/cflat/asm/Label.java | 21 ++++ net/loveruby/cflat/asm/LabelRef.java | 4 + net/loveruby/cflat/asm/PeepholeOptimizer.java | 67 +++++++++++- net/loveruby/cflat/asm/Register.java | 18 +++- .../cflat/compiler/CodeGenerator.java | 101 ++++++++++-------- .../cflat/utils/ClonableIterator.java | 40 +++++++ 13 files changed, 315 insertions(+), 70 deletions(-) create mode 100644 net/loveruby/cflat/utils/ClonableIterator.java diff --git a/ChangeLog b/ChangeLog index 737bbc3..e2ebdb1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,46 @@ +Tue Sep 23 22:50:52 2008 Minero Aoki + + * net/loveruby/cflat/compiler/CodeGenerator.java: apply jump + elimination. + + * net/loveruby/cflat/asm/PeepholeOptimizer.java: new optimization + method: jump elimination. + + * net/loveruby/cflat/asm/Assembler.java: refactoring: #string -> + #toSource. + + * net/loveruby/cflat/asm/Assembler.java: refactoring: list -> + assemblies. + + * net/loveruby/cflat/asm/Assembly.java: new method #isDirective + and #isComment. + + * net/loveruby/cflat/asm/Directive.java: implement it. + + * net/loveruby/cflat/asm/AsmComment.java: implement it. + + * net/loveruby/cflat/asm/Label.java: implement it. + + * net/loveruby/cflat/asm/Label.java: new method #equals, + #hashCode. + + * net/loveruby/cflat/asm/Instruction.java: add jump related + methods to analyze jump instruction. + + * net/loveruby/cflat/asm/LabelRef.java: new method #label to allow + extracting a Label. + + * net/loveruby/cflat/asm/Register.java: new method #equals, + #hashCode. + + * net/loveruby/cflat/asm/AsmStatistics.java: use Object for map + keys. + + * net/loveruby/cflat/asm/AsmStatistics.java: new method + #doesRegisterUsed. + + * net/loveruby/cflat/utils/ClonableIterator.java: new utility. + Tue Sep 23 22:45:00 2008 Minero Aoki * lib/Makefile: parameterize. diff --git a/net/loveruby/cflat/asm/AsmComment.java b/net/loveruby/cflat/asm/AsmComment.java index c2f28b2..fe50646 100644 --- a/net/loveruby/cflat/asm/AsmComment.java +++ b/net/loveruby/cflat/asm/AsmComment.java @@ -13,6 +13,10 @@ public class AsmComment extends Assembly { this.indentLevel = indentLevel; } + public boolean isComment() { + return true; + } + public String toSource() { return "\t" + indent() + "# " + string; } diff --git a/net/loveruby/cflat/asm/AsmStatistics.java b/net/loveruby/cflat/asm/AsmStatistics.java index a0066c1..64a9e4e 100644 --- a/net/loveruby/cflat/asm/AsmStatistics.java +++ b/net/loveruby/cflat/asm/AsmStatistics.java @@ -20,12 +20,16 @@ public class AsmStatistics { insnUsage = new HashMap(); } - public int numRegisterUsage(String name) { - return fetchCount(registerUsage, name); + public boolean doesRegisterUsed(Register reg) { + return numRegisterUsage(reg) > 0; } - public void registerUsed(String name) { - incrementCount(registerUsage, name); + public int numRegisterUsage(Register reg) { + return fetchCount(registerUsage, reg); + } + + public void registerUsed(Register reg) { + incrementCount(registerUsage, reg); } public int numInstructionUsage(String insn) { @@ -36,8 +40,8 @@ public class AsmStatistics { incrementCount(insnUsage, insn); } - protected int fetchCount(Map m, String name) { - Integer n = (Integer)m.get(name); + protected int fetchCount(Map m, Object key) { + Integer n = (Integer)m.get(key); if (n == null) { return 0; } @@ -46,7 +50,7 @@ public class AsmStatistics { } } - protected void incrementCount(Map m, String name) { - m.put(name, new Integer(fetchCount(m, name) + 1)); + protected void incrementCount(Map m, Object key) { + m.put(key, new Integer(fetchCount(m, key) + 1)); } } diff --git a/net/loveruby/cflat/asm/Assembler.java b/net/loveruby/cflat/asm/Assembler.java index cb3b82c..465ae86 100644 --- a/net/loveruby/cflat/asm/Assembler.java +++ b/net/loveruby/cflat/asm/Assembler.java @@ -4,7 +4,7 @@ import net.loveruby.cflat.utils.*; import java.util.*; public class Assembler { - protected List list; // List + protected List assemblies; // List protected Type naturalType; protected int commentIndentLevel; @@ -13,24 +13,24 @@ public class Assembler { } public Assembler(Type naturalType) { - this.list = new ArrayList(); + this.assemblies = new ArrayList(); this.naturalType = naturalType; this.commentIndentLevel = 0; } public List assemblies() { - return this.list; + return this.assemblies; } public void addAll(List assemblies) { - this.list.addAll(assemblies); + this.assemblies.addAll(assemblies); } - public String string() { + public String toSource() { StringBuffer buf = new StringBuffer(); - Iterator i = list.iterator(); - while (i.hasNext()) { - Assembly asm = (Assembly)i.next(); + Iterator asms = assemblies.iterator(); + while (asms.hasNext()) { + Assembly asm = (Assembly)asms.next(); buf.append(asm.toSource()); buf.append("\n"); } @@ -38,7 +38,7 @@ public class Assembler { } public void comment(String str) { - list.add(new AsmComment(str, commentIndentLevel)); + assemblies.add(new AsmComment(str, commentIndentLevel)); } public void indentComment() { @@ -50,39 +50,39 @@ public class Assembler { } public void label(String sym) { - list.add(new Label(sym)); + assemblies.add(new Label(sym)); } public void label(Label label) { - list.add(label); + assemblies.add(label); } protected void directive(String direc) { - list.add(new Directive(direc)); + assemblies.add(new Directive(direc)); } protected void insn(String op) { - list.add(new Instruction(op)); + assemblies.add(new Instruction(op)); } protected void insn(String op, AsmOperand a) { - list.add(new Instruction(op, "", a)); + assemblies.add(new Instruction(op, "", a)); } protected void insn(String op, String suffix, AsmOperand a) { - list.add(new Instruction(op, suffix, a)); + assemblies.add(new Instruction(op, suffix, a)); } protected void insn(Type t, String op, AsmOperand a) { - list.add(new Instruction(op, typeSuffix(t), a)); + assemblies.add(new Instruction(op, typeSuffix(t), a)); } protected void insn(String op, String suffix, AsmOperand a, AsmOperand b) { - list.add(new Instruction(op, suffix, a, b)); + assemblies.add(new Instruction(op, suffix, a, b)); } protected void insn(Type t, String op, AsmOperand a, AsmOperand b) { - list.add(new Instruction(op, typeSuffix(t), a, b)); + assemblies.add(new Instruction(op, typeSuffix(t), a, b)); } protected String typeSuffix(Type t1, Type t2) { diff --git a/net/loveruby/cflat/asm/Assembly.java b/net/loveruby/cflat/asm/Assembly.java index d3f8314..98a0618 100644 --- a/net/loveruby/cflat/asm/Assembly.java +++ b/net/loveruby/cflat/asm/Assembly.java @@ -11,6 +11,14 @@ abstract public class Assembly { return false; } + public boolean isDirective() { + return false; + } + + public boolean isComment() { + return false; + } + public void collectStatistics(AsmStatistics stats) { // does nothing by default. } diff --git a/net/loveruby/cflat/asm/Directive.java b/net/loveruby/cflat/asm/Directive.java index 2c8d5b1..157a049 100644 --- a/net/loveruby/cflat/asm/Directive.java +++ b/net/loveruby/cflat/asm/Directive.java @@ -7,6 +7,10 @@ public class Directive extends Assembly { this.content = content; } + public boolean isDirective() { + return true; + } + public String toSource() { return this.content; } diff --git a/net/loveruby/cflat/asm/Instruction.java b/net/loveruby/cflat/asm/Instruction.java index cc1fc32..bdef1c4 100644 --- a/net/loveruby/cflat/asm/Instruction.java +++ b/net/loveruby/cflat/asm/Instruction.java @@ -41,6 +41,17 @@ public class Instruction extends Assembly { return this.mnemonic; } + public boolean isJumpInstruction() { + return mnemonic.equals("jmp") + || mnemonic.equals("jz") + || mnemonic.equals("jne") + || mnemonic.equals("je") + || mnemonic.equals("jne"); + } + + /** + * Returns the number of operands. + */ public int numOperands() { return this.operands.length; } @@ -53,6 +64,14 @@ public class Instruction extends Assembly { return this.operands[1]; } + /** + * Extract jump destination label from operands. + */ + public Label jmpDestination() { + DirectMemoryReference ref = (DirectMemoryReference)(operands[0]); + return ((LabelRef)ref.value()).label(); + } + public void collectStatistics(AsmStatistics stats) { stats.instructionUsed(mnemonic); for (int i = 0; i < operands.length; i++) { diff --git a/net/loveruby/cflat/asm/Label.java b/net/loveruby/cflat/asm/Label.java index 4488b57..dc635e6 100644 --- a/net/loveruby/cflat/asm/Label.java +++ b/net/loveruby/cflat/asm/Label.java @@ -14,6 +14,10 @@ public class Label extends Assembly { name = n; } + public boolean isLabel() { + return true; + } + public long seq() { return seq; } @@ -22,6 +26,23 @@ public class Label extends Assembly { return name; } + public boolean equals(Object other) { + if (other instanceof Label) { + return equals((Label)other); + } + else { + return false; + } + } + + public int hashCode() { + return name.hashCode(); + } + + public boolean equals(Label label) { + return name.equals(label.name()); + } + public String toSource() { return name + ":"; } diff --git a/net/loveruby/cflat/asm/LabelRef.java b/net/loveruby/cflat/asm/LabelRef.java index 7ff35b1..c2d89b0 100644 --- a/net/loveruby/cflat/asm/LabelRef.java +++ b/net/loveruby/cflat/asm/LabelRef.java @@ -7,6 +7,10 @@ public class LabelRef extends Literal { this.label = label; } + public Label label() { + return label; + } + public String symbol() { return label.name(); } diff --git a/net/loveruby/cflat/asm/PeepholeOptimizer.java b/net/loveruby/cflat/asm/PeepholeOptimizer.java index 1623f52..6510c9a 100644 --- a/net/loveruby/cflat/asm/PeepholeOptimizer.java +++ b/net/loveruby/cflat/asm/PeepholeOptimizer.java @@ -1,4 +1,5 @@ package net.loveruby.cflat.asm; +import net.loveruby.cflat.utils.ClonableIterator; import java.util.*; public class PeepholeOptimizer implements AsmOptimizer { @@ -8,6 +9,10 @@ public class PeepholeOptimizer implements AsmOptimizer { this.filters = defaultFilterSet(); } + public List optimize(List assemblies) { + return jumpElimination(insnOptimization(assemblies)); + } + // List protected List defaultFilterSet() { List set = new ArrayList(); @@ -123,7 +128,7 @@ public class PeepholeOptimizer implements AsmOptimizer { return new AnyRegisterPattern(); } - public List optimize(List assemblies) { + public List insnOptimization(List assemblies) { List result = new ArrayList(); Iterator asms = assemblies.iterator(); while (asms.hasNext()) { @@ -205,4 +210,64 @@ public class PeepholeOptimizer implements AsmOptimizer { interface InsnTemplate { abstract public Instruction apply(Instruction insn); } + + // + // jumpElimination + // + + public List jumpElimination(List assemblies) { + List result = new ArrayList(); + ClonableIterator asms = new ClonableIterator(assemblies); + while (asms.hasNext()) { + Assembly asm = (Assembly)asms.next(); + if (isUselessJump(asm, asms)) { + ; // remove useless jump + } + else { + result.add(asm); + } + } + return result; + } + + protected boolean isUselessJump(Assembly asm, ClonableIterator asms) { + if (! asm.isInstruction()) return false; + Instruction insn = (Instruction)asm; + if (! insn.isJumpInstruction()) return false; + return doesLabelFollows(asms.dup(), insn.jmpDestination()); + } + + /** + * Returns true if jmpDest is found in asms before any instruction + * or directives. For example, this method returns true if contents + * of asms are: + * + * if_end3: + * # comment + * jmpDest: + * mov + * mov + * add + */ + protected boolean doesLabelFollows(ClonableIterator asms, Label jmpDest) { + while (asms.hasNext()) { + Assembly asm = (Assembly)asms.next(); + if (asm.isLabel()) { + Label label = (Label)asm; + if (label.equals(jmpDest)) { + return true; + } + else { + continue; + } + } + else if (asm.isComment()) { + continue; + } + else { + return false; + } + } + return false; + } } diff --git a/net/loveruby/cflat/asm/Register.java b/net/loveruby/cflat/asm/Register.java index 30a2502..ccdf020 100644 --- a/net/loveruby/cflat/asm/Register.java +++ b/net/loveruby/cflat/asm/Register.java @@ -32,6 +32,22 @@ public class Register extends AsmOperand { return true; } + public boolean equals(Object other) { + if (!(other instanceof Register)) return false; + return equals((Register)other); + } + + public int hashCode() { + return name.hashCode(); + } + + /** + * size difference does NOT matter. + */ + public boolean equals(Register reg) { + return name.equals(reg.baseName()); + } + public String baseName() { return name; } @@ -62,7 +78,7 @@ public class Register extends AsmOperand { } public void collectStatistics(AsmStatistics stats) { - stats.registerUsed(name); + stats.registerUsed(this); } public String toSource() { diff --git a/net/loveruby/cflat/compiler/CodeGenerator.java b/net/loveruby/cflat/compiler/CodeGenerator.java index a26ce5c..c8c2522 100644 --- a/net/loveruby/cflat/compiler/CodeGenerator.java +++ b/net/loveruby/cflat/compiler/CodeGenerator.java @@ -9,7 +9,7 @@ public class CodeGenerator extends Visitor implements ASTLHSVisitor { protected AsmOptimizer optimizer; protected ErrorHandler errorHandler; protected boolean verboseAsm; - protected Assembler assembler; + protected LinkedList asStack; protected Assembler as; protected TypeTable typeTable; protected DefinedFunction currentFunction; @@ -20,6 +20,7 @@ public class CodeGenerator extends Visitor implements ASTLHSVisitor { this.optimizer = optimizer; this.errorHandler = errorHandler; this.verboseAsm = verboseAsm; + this.asStack = new LinkedList(); } // #@@} @@ -27,12 +28,26 @@ public class CodeGenerator extends Visitor implements ASTLHSVisitor { // #@@range/generate public String generate(AST ast) { this.typeTable = ast.typeTable(); - this.assembler = newAssembler(); - this.as = this.assembler; + pushAssembler(); allocateGlobalVariables(ast.globalVariables()); allocateCommonSymbols(ast.commonSymbols()); compileAST(ast); - return as.string(); + return popAssembler().toSource(); + } + + protected void pushAssembler() { + asStack.add(newAssembler()); + this.as = (Assembler)asStack.getLast(); + } + + protected Assembler popAssembler() { + Assembler poped = (Assembler)asStack.removeLast(); + this.as = asStack.isEmpty() ? null : (Assembler)asStack.getLast(); + return poped; + } + + protected Assembler newAssembler() { + return new Assembler(typeTable.unsignedLong()); } public void compileAST(AST ast) { @@ -54,10 +69,6 @@ public class CodeGenerator extends Visitor implements ASTLHSVisitor { } // #@@} - protected Assembler newAssembler() { - return new Assembler(typeTable.unsignedLong()); - } - /** * Sets memory reference for... * * public global variables @@ -189,37 +200,38 @@ public class CodeGenerator extends Visitor implements ASTLHSVisitor { /** Compiles a function. */ // #@@range/compileFunction{ protected void compileFunction(DefinedFunction func) { - long numSavedRegs = 0; // 1 for PIC allocateParameters(func); allocateLocalVariablesTemp(func.body().scope()); - List bodyAsms = compileFunctionBody(func); - AsmStatistics stats = AsmStatistics.collect(bodyAsms); - List usedCalleeSavedRegs = usedCalleeSavedRegs(stats); - long lvarBytes = allocateLocalVariables(func.body().scope(), - usedCalleeSavedRegs.size()); - String symbol = csymbol(func.name()); if (! func.isPrivate()) { _globl(symbol); } _type(symbol, "@function"); label(symbol); - prologue(func, usedCalleeSavedRegs, lvarBytes); - as.addAll(bodyAsms); - epilogue(func, usedCalleeSavedRegs, lvarBytes); + compileFunctionBody(func); _size(symbol, ".-" + symbol); } // #@@} - protected List compileFunctionBody(DefinedFunction func) { + protected void compileFunctionBody(DefinedFunction func) { + List bodyAsms = compileStmts(func); + AsmStatistics stats = AsmStatistics.collect(bodyAsms); + List saveRegs = usedCalleeSavedRegisters(stats); + long lvarBytes = allocateLocalVariables(func.body().scope(), + saveRegs.size()); + prologue(func, saveRegs, lvarBytes); + as.addAll(bodyAsms); + epilogue(func, saveRegs, lvarBytes); + } + + protected List compileStmts(DefinedFunction func) { + pushAssembler(); currentFunction = func; - this.as = newAssembler(); compile(func.body()); - List assemblies = this.as.assemblies(); - this.as = this.assembler; + label(epilogueLabel(func)); currentFunction = null; - return optimizer.optimize(assemblies); + return optimizer.optimize(popAssembler().assemblies()); } // #@@range/compile{ @@ -250,12 +262,12 @@ public class CodeGenerator extends Visitor implements ASTLHSVisitor { } // #@@} - protected List usedCalleeSavedRegs(AsmStatistics stats) { + protected List usedCalleeSavedRegisters(AsmStatistics stats) { List result = new ArrayList(); Iterator regs = calleeSavedRegisters().iterator(); while (regs.hasNext()) { Register reg = (Register)regs.next(); - if (stats.numRegisterUsage(reg.baseName()) > 0) { + if (stats.doesRegisterUsed(reg)) { result.add(reg); } } @@ -318,16 +330,8 @@ public class CodeGenerator extends Visitor implements ASTLHSVisitor { List saveRegs, long lvarBytes) { push(bp()); mov(sp(), bp()); - Iterator regs = saveRegs.iterator(); - while (regs.hasNext()) { - Register reg = (Register)regs.next(); - // bp is already saved - if (! reg.baseName().equals("bp")) { - push(reg); - } - } + saveRegisters(saveRegs); extendStack(lvarBytes); - if (verboseAsm) { Iterator vars = func.localVariables(); while (vars.hasNext()) { @@ -341,21 +345,34 @@ public class CodeGenerator extends Visitor implements ASTLHSVisitor { // #@@range/epilogue{ protected void epilogue(DefinedFunction func, List savedRegs, long lvarBytes) { - label(epilogueLabel(func)); shrinkStack(lvarBytes); - Iterator regs = savedRegs.iterator(); - while (regs.hasNext()) { - Register reg = (Register)regs.next(); - if (! reg.baseName().equals("bp")) { - pop(reg); - } - } + restoreRegisters(savedRegs); mov(bp(), sp()); pop(bp()); ret(); } // #@@} + protected void saveRegisters(List saveRegs) { + Iterator regs = saveRegs.iterator(); + while (regs.hasNext()) { + Register reg = (Register)regs.next(); + if (! reg.equals(bp())) { // bp is already saved. + push(reg); + } + } + } + + protected void restoreRegisters(List savedRegs) { + Iterator regs = savedRegs.iterator(); + while (regs.hasNext()) { + Register reg = (Register)regs.next(); + if (! reg.equals(bp())) { // bp is going to be restored. + pop(reg); + } + } + } + // #@@range/jmpEpilogue{ protected void jmpEpilogue() { jmp(new Label(epilogueLabel(currentFunction))); diff --git a/net/loveruby/cflat/utils/ClonableIterator.java b/net/loveruby/cflat/utils/ClonableIterator.java new file mode 100644 index 0000000..e2a8c45 --- /dev/null +++ b/net/loveruby/cflat/utils/ClonableIterator.java @@ -0,0 +1,40 @@ +package net.loveruby.cflat.utils; +import java.util.*; + +public class ClonableIterator implements Iterator { + protected List list; + protected int index; + + public ClonableIterator(List list) { + this(list, 0); + } + + protected ClonableIterator(List list, int index) { + this.list = list; + this.index = index; + } + + public Object clone() { + return new ClonableIterator(list, index); + } + + public ClonableIterator dup() { + return new ClonableIterator(list, index); + } + + public boolean hasNext() { + return index < list.size(); + } + + public Object next() { + return list.get(index++); + } + + public void remove() { + list.remove(index); + } + + public String toString() { + return "#"; + } +}