From 1668b7240359c0defe233f3230907e090cb43c10 Mon Sep 17 00:00:00 2001 From: Minero Aoki Date: Sat, 20 Sep 2008 18:04:20 +0000 Subject: [PATCH] * net/loveruby/cflat/compiler/CodeGenerator.java: fix local variables offset after compiling function body, to allow saving really-used callee-saved registers only. * net/loveruby/cflat/asm/IndirectMemoryReference.java: allow fixing offset out of consructor. * net/loveruby/cflat/asm/AsmStatistics.java: new class. * net/loveruby/cflat/asm/Assembly.java: support #collectStatistics. * net/loveruby/cflat/asm/Instruction.java: ditto. * net/loveruby/cflat/asm/AsmOperand.java: ditto. * net/loveruby/cflat/asm/Register.java: ditto. * net/loveruby/cflat/asm/AbsoluteAddress.java: ditto. * net/loveruby/cflat/asm/DirectMemoryReference.java: ditto. * net/loveruby/cflat/asm/ImmediateValue.java: ditto. * net/loveruby/cflat/ast/Scope.java: refactoring: rename method: #allChildren -> #allScopes. * net/loveruby/cflat/ast/Scope.java (allScopes): should include receiver scope itself. * net/loveruby/cflat/ast/LocalScope.java (allVariables): did not wrongly included current scope's variables. * net/loveruby/cflat/ast/LocalScope.java: refactoring: rename method: allVariables -> allLocalVariables. * net/loveruby/cflat/ast/DefinedFunction.java: ditto. git-svn-id: file:///Users/aamine/c/gitwork/public/cbc/trunk@4040 1b9489fe-b721-0410-924e-b54b9192deb8 --- ChangeLog | 40 +++++ net/loveruby/cflat/asm/AbsoluteAddress.java | 4 + net/loveruby/cflat/asm/AsmOperand.java | 2 + net/loveruby/cflat/asm/AsmStatistics.java | 52 ++++++ net/loveruby/cflat/asm/Assembly.java | 4 + .../cflat/asm/DirectMemoryReference.java | 4 + net/loveruby/cflat/asm/ImmediateValue.java | 4 + .../cflat/asm/IndirectMemoryReference.java | 21 ++- net/loveruby/cflat/asm/Instruction.java | 7 + net/loveruby/cflat/asm/Register.java | 4 + net/loveruby/cflat/ast/DefinedFunction.java | 2 +- net/loveruby/cflat/ast/DefinedVariable.java | 3 +- net/loveruby/cflat/ast/LocalScope.java | 47 +++-- net/loveruby/cflat/ast/Scope.java | 15 +- .../cflat/compiler/CodeGenerator.java | 166 +++++++++++++----- 15 files changed, 304 insertions(+), 71 deletions(-) create mode 100644 net/loveruby/cflat/asm/AsmStatistics.java diff --git a/ChangeLog b/ChangeLog index 5e19f1a..23e1f3f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,43 @@ +Sun Sep 21 03:04:16 2008 Minero Aoki + + * net/loveruby/cflat/compiler/CodeGenerator.java: fix local + variables offset after compiling function body, to allow saving + really-used callee-saved registers only. + + * net/loveruby/cflat/asm/IndirectMemoryReference.java: allow + fixing offset out of consructor. + + * net/loveruby/cflat/asm/AsmStatistics.java: new class. + + * net/loveruby/cflat/asm/Assembly.java: support + #collectStatistics. + + * net/loveruby/cflat/asm/Instruction.java: ditto. + + * net/loveruby/cflat/asm/AsmOperand.java: ditto. + + * net/loveruby/cflat/asm/Register.java: ditto. + + * net/loveruby/cflat/asm/AbsoluteAddress.java: ditto. + + * net/loveruby/cflat/asm/DirectMemoryReference.java: ditto. + + * net/loveruby/cflat/asm/ImmediateValue.java: ditto. + + * net/loveruby/cflat/ast/Scope.java: refactoring: rename method: + #allChildren -> #allScopes. + + * net/loveruby/cflat/ast/Scope.java (allScopes): should include + receiver scope itself. + + * net/loveruby/cflat/ast/LocalScope.java (allVariables): did not + wrongly included current scope's variables. + + * net/loveruby/cflat/ast/LocalScope.java: refactoring: rename + method: allVariables -> allLocalVariables. + + * net/loveruby/cflat/ast/DefinedFunction.java: ditto. + Sat Sep 20 23:48:31 2008 Minero Aoki * net/loveruby/cflat/compiler/Options.java: new option -O0, -O, diff --git a/net/loveruby/cflat/asm/AbsoluteAddress.java b/net/loveruby/cflat/asm/AbsoluteAddress.java index 6a0a864..aca42e6 100644 --- a/net/loveruby/cflat/asm/AbsoluteAddress.java +++ b/net/loveruby/cflat/asm/AbsoluteAddress.java @@ -11,6 +11,10 @@ public class AbsoluteAddress extends AsmOperand { return this.register; } + public void collectStatistics(AsmStatistics stats) { + register.collectStatistics(stats); + } + public String toSource() { return "*" + register.toSource(); } diff --git a/net/loveruby/cflat/asm/AsmOperand.java b/net/loveruby/cflat/asm/AsmOperand.java index dbddd7f..e6f7670 100644 --- a/net/loveruby/cflat/asm/AsmOperand.java +++ b/net/loveruby/cflat/asm/AsmOperand.java @@ -15,6 +15,8 @@ abstract public class AsmOperand implements OperandPattern { return null; } + abstract public void collectStatistics(AsmStatistics stats); + // default implementation public boolean match(AsmOperand operand) { return equals(operand); diff --git a/net/loveruby/cflat/asm/AsmStatistics.java b/net/loveruby/cflat/asm/AsmStatistics.java new file mode 100644 index 0000000..a0066c1 --- /dev/null +++ b/net/loveruby/cflat/asm/AsmStatistics.java @@ -0,0 +1,52 @@ +package net.loveruby.cflat.asm; +import java.util.*; + +public class AsmStatistics { + protected Map registerUsage; + protected Map insnUsage; + + static public AsmStatistics collect(List assemblies) { + AsmStatistics stats = new AsmStatistics(); + Iterator asms = assemblies.iterator(); + while (asms.hasNext()) { + Assembly asm = (Assembly)asms.next(); + asm.collectStatistics(stats); + } + return stats; + } + + public AsmStatistics() { + registerUsage = new HashMap(); + insnUsage = new HashMap(); + } + + public int numRegisterUsage(String name) { + return fetchCount(registerUsage, name); + } + + public void registerUsed(String name) { + incrementCount(registerUsage, name); + } + + public int numInstructionUsage(String insn) { + return fetchCount(insnUsage, insn); + } + + public void instructionUsed(String insn) { + incrementCount(insnUsage, insn); + } + + protected int fetchCount(Map m, String name) { + Integer n = (Integer)m.get(name); + if (n == null) { + return 0; + } + else { + return n.intValue(); + } + } + + protected void incrementCount(Map m, String name) { + m.put(name, new Integer(fetchCount(m, name) + 1)); + } +} diff --git a/net/loveruby/cflat/asm/Assembly.java b/net/loveruby/cflat/asm/Assembly.java index 23d9450..d3f8314 100644 --- a/net/loveruby/cflat/asm/Assembly.java +++ b/net/loveruby/cflat/asm/Assembly.java @@ -10,4 +10,8 @@ abstract public class Assembly { public boolean isLabel() { return false; } + + public void collectStatistics(AsmStatistics stats) { + // does nothing by default. + } } diff --git a/net/loveruby/cflat/asm/DirectMemoryReference.java b/net/loveruby/cflat/asm/DirectMemoryReference.java index 366a34a..01e7cb6 100644 --- a/net/loveruby/cflat/asm/DirectMemoryReference.java +++ b/net/loveruby/cflat/asm/DirectMemoryReference.java @@ -15,6 +15,10 @@ public class DirectMemoryReference extends MemoryReference { return this.value; } + public void collectStatistics(AsmStatistics stats) { + // does nothing + } + public String toSource() { return this.value.toSource(); } diff --git a/net/loveruby/cflat/asm/ImmediateValue.java b/net/loveruby/cflat/asm/ImmediateValue.java index 0eb51a9..fea624c 100644 --- a/net/loveruby/cflat/asm/ImmediateValue.java +++ b/net/loveruby/cflat/asm/ImmediateValue.java @@ -21,6 +21,10 @@ public class ImmediateValue extends AsmOperand { return this.expr; } + public void collectStatistics(AsmStatistics stats) { + // does nothing + } + public String toSource() { return "$" + expr.toSource(); } diff --git a/net/loveruby/cflat/asm/IndirectMemoryReference.java b/net/loveruby/cflat/asm/IndirectMemoryReference.java index 9b28040..d332bb9 100644 --- a/net/loveruby/cflat/asm/IndirectMemoryReference.java +++ b/net/loveruby/cflat/asm/IndirectMemoryReference.java @@ -3,25 +3,44 @@ package net.loveruby.cflat.asm; public class IndirectMemoryReference extends MemoryReference { protected long offset; protected Register base; + protected boolean fixed; public IndirectMemoryReference(Register base) { - this(0, base); + this.offset = 0; + this.base = base; + this.fixed = false; } public IndirectMemoryReference(long offset, Register base) { this.offset = offset; this.base = base; + this.fixed = true; } public long offset() { return offset; } + public void fixOffset(long realOffset) { + if (fixed) { + throw new Error("must not happen: fixed = true"); + } + this.offset = realOffset; + this.fixed = true; + } + public Register base() { return base; } + public void collectStatistics(AsmStatistics stats) { + base.collectStatistics(stats); + } + public String toSource() { + if (! fixed) { + throw new Error("must not happen: writing unfixed variable"); + } return (offset == 0 ? "" : "" + offset) + "(" + base.toSource() + ")"; } } diff --git a/net/loveruby/cflat/asm/Instruction.java b/net/loveruby/cflat/asm/Instruction.java index 30dcda4..cc1fc32 100644 --- a/net/loveruby/cflat/asm/Instruction.java +++ b/net/loveruby/cflat/asm/Instruction.java @@ -53,6 +53,13 @@ public class Instruction extends Assembly { return this.operands[1]; } + public void collectStatistics(AsmStatistics stats) { + stats.instructionUsed(mnemonic); + for (int i = 0; i < operands.length; i++) { + operands[i].collectStatistics(stats); + } + } + public String toSource() { StringBuffer buf = new StringBuffer(); buf.append("\t"); diff --git a/net/loveruby/cflat/asm/Register.java b/net/loveruby/cflat/asm/Register.java index 8e965b0..30a2502 100644 --- a/net/loveruby/cflat/asm/Register.java +++ b/net/loveruby/cflat/asm/Register.java @@ -61,6 +61,10 @@ public class Register extends AsmOperand { return false; } + public void collectStatistics(AsmStatistics stats) { + stats.registerUsed(name); + } + public String toSource() { return "%" + name(); } diff --git a/net/loveruby/cflat/ast/DefinedFunction.java b/net/loveruby/cflat/ast/DefinedFunction.java index 8064b1c..f44fe2a 100644 --- a/net/loveruby/cflat/ast/DefinedFunction.java +++ b/net/loveruby/cflat/ast/DefinedFunction.java @@ -44,7 +44,7 @@ public class DefinedFunction extends Function { * Does NOT include static local variables. */ public Iterator localVariables() { - return scope.allVariables(); + return scope.allLocalVariables(); } class JumpEntry { diff --git a/net/loveruby/cflat/ast/DefinedVariable.java b/net/loveruby/cflat/ast/DefinedVariable.java index 18998c4..a6ad72c 100644 --- a/net/loveruby/cflat/ast/DefinedVariable.java +++ b/net/loveruby/cflat/ast/DefinedVariable.java @@ -38,8 +38,9 @@ public class DefinedVariable extends Variable { } public MemoryReference memref() { - if (memref == null) + if (memref == null) { throw new Error("unresolved variable address"); + } return memref; } diff --git a/net/loveruby/cflat/ast/LocalScope.java b/net/loveruby/cflat/ast/LocalScope.java index 2ab646a..8e9f8dc 100644 --- a/net/loveruby/cflat/ast/LocalScope.java +++ b/net/loveruby/cflat/ast/LocalScope.java @@ -19,14 +19,21 @@ public class LocalScope extends Scope { return children.iterator(); } - // Returns local variables defined in this scope. - // Does includes all nested local variables. - // Does NOT include static local variables. + /** + * Returns local variables defined in this scope. + * Does NOT includes children's local variables. + * Does NOT include static local variables. + */ public Iterator variables() { - return variablesList().iterator(); + return lvarList().iterator(); } - protected List variablesList() { + /** + * Returns local variables defined in this scope. + * Does NOT includes children's local variables. + * Does NOT include static local variables. + */ + protected List lvarList() { List result = new ArrayList(); Iterator ents = entities.values().iterator(); while (ents.hasNext()) { @@ -41,9 +48,13 @@ public class LocalScope extends Scope { return result; } + /** + * Returns the number of all entities in this scope. + * Result includes the number of children's entities. + */ public long numAllEntities() { if (numAllEntities < 0) { - Iterator cs = allChildren(); + Iterator cs = allScopes().iterator(); long n = 0; while (cs.hasNext()) { Scope c = (Scope)cs.next(); @@ -54,26 +65,32 @@ public class LocalScope extends Scope { return numAllEntities; } - // Returns all function local variables defined in this scope. - // Does includes all nested local variables. - // Does NOT include static local variables. - public Iterator allVariables() { - return allEntities().iterator(); + /** + * Returns all local variables in this scope. + * The result DOES includes all nested local variables, + * while it does NOT include static local variables. + */ + public Iterator allLocalVariables() { + return allLocalVariablesList().iterator(); } - protected List allEntities() { + // List + protected List allLocalVariablesList() { List result = new ArrayList(); - Iterator scopes = allChildren(); + Iterator scopes = allScopes().iterator(); while (scopes.hasNext()) { LocalScope s = (LocalScope)scopes.next(); - result.addAll(s.variablesList()); + result.addAll(s.lvarList()); } return result; } + /** + * Returns all static local variables defined in this scope. + */ public List staticLocalVariables() { List result = new ArrayList(); - Iterator scopes = allChildren(); + Iterator scopes = allScopes().iterator(); while (scopes.hasNext()) { LocalScope s = (LocalScope)scopes.next(); Iterator vars = s.entities.values().iterator(); diff --git a/net/loveruby/cflat/ast/Scope.java b/net/loveruby/cflat/ast/Scope.java index b8c2ec0..0f59a7d 100644 --- a/net/loveruby/cflat/ast/Scope.java +++ b/net/loveruby/cflat/ast/Scope.java @@ -37,20 +37,19 @@ abstract public class Scope { children.add(s); } - // Returns a list of all child scopes. - // Does NOT include myself. - protected Iterator allChildren() { + // Returns a list of all child scopes including this scope. + protected List allScopes() { List result = new ArrayList(); - collectChildren(result); - return result.iterator(); + collectScope(result); + return result; } - protected void collectChildren(List buf) { + protected void collectScope(List buf) { + buf.add(this); Iterator cs = children.iterator(); while (cs.hasNext()) { Scope c = (Scope)cs.next(); - buf.add(c); - c.collectChildren(buf); + c.collectScope(buf); } } diff --git a/net/loveruby/cflat/compiler/CodeGenerator.java b/net/loveruby/cflat/compiler/CodeGenerator.java index 34ca828..600869d 100644 --- a/net/loveruby/cflat/compiler/CodeGenerator.java +++ b/net/loveruby/cflat/compiler/CodeGenerator.java @@ -187,28 +187,35 @@ public class CodeGenerator extends Visitor implements ASTLHSVisitor { protected void compileFunction(DefinedFunction func) { long numSavedRegs = 0; // 1 for PIC allocateParameters(func); - long lvarBytes = allocateLocalVariables(func, numSavedRegs); + 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()); - currentFunction = func; String symbol = csymbol(func.name()); if (! func.isPrivate()) { _globl(symbol); } _type(symbol, "@function"); label(symbol); - prologue(func, lvarBytes); - compileFunctionBody(func.body()); - epilogue(func, lvarBytes); + prologue(func, usedCalleeSavedRegs, lvarBytes); + as.addAll(bodyAsms); + epilogue(func, usedCalleeSavedRegs, lvarBytes); _size(symbol, ".-" + symbol); } // #@@} - protected void compileFunctionBody(Node body) { + protected List compileFunctionBody(DefinedFunction func) { + currentFunction = func; this.as = newAssembler(); - compile(body); + compile(func.body()); List assemblies = this.as.assemblies(); this.as = this.assembler; - this.as.addAll(optimizer.optimize(assemblies)); + currentFunction = null; + return optimizer.optimize(assemblies); } // #@@range/compile{ @@ -231,47 +238,45 @@ public class CodeGenerator extends Visitor implements ASTLHSVisitor { } // #@@} - // #@@range/prologue{ - protected void prologue(DefinedFunction func, long lvarBytes) { - push(bp()); - mov(sp(), bp()); - extendStack(lvarBytes); + protected List usedCalleeSavedRegs(AsmStatistics stats) { + List result = new ArrayList(); + Iterator regs = calleeSavedRegisters().iterator(); + while (regs.hasNext()) { + Register reg = (Register)regs.next(); + if (stats.numRegisterUsage(reg.baseName()) > 0) { + result.add(reg); + } + } + return result; } - // #@@} - // #@@range/epilogue{ - protected void epilogue(DefinedFunction func, long lvarBytes) { - label(epilogueLabel(func)); - shrinkStack(lvarBytes); - mov(bp(), sp()); - pop(bp()); - ret(); - } - // #@@} + protected List calleeSavedRegistersCache = null; - // #@@range/jmpEpilogue{ - protected void jmpEpilogue() { - jmp(new Label(epilogueLabel(currentFunction))); + protected List calleeSavedRegisters() { + if (calleeSavedRegistersCache == null) { + ArrayList regs = new ArrayList(); + regs.add(reg("bx")); + regs.add(reg("si")); + regs.add(reg("di")); + regs.add(reg("bp")); + calleeSavedRegistersCache = regs; + } + return calleeSavedRegistersCache; } - // #@@} - - // #@@range/epilogueLabel{ - protected String epilogueLabel(DefinedFunction func) { - return ".L" + func.name() + "$epilogue"; - } - // #@@} /* Standard IA-32 stack frame layout (after prologue) * * ======================= esp (stack top) * temporary * variables... - * --------------------- ebp-(4*3) + * --------------------- ebp-(4*4) * lvar 3 - * --------------------- ebp-(4*2) + * --------------------- ebp-(4*3) * lvar 2 - * --------------------- ebp-(4*1) + * --------------------- ebp-(4*2) * lvar 1 + * --------------------- ebp-(4*1) + * callee-saved register * ======================= ebp * saved ebp * --------------------- ebp+(4*1) @@ -296,6 +301,59 @@ public class CodeGenerator extends Visitor implements ASTLHSVisitor { // +1 for return address, +1 for saved bp static final protected long paramStartWord = 2; + // #@@range/prologue{ + protected void prologue(DefinedFunction func, + 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); + } + } + extendStack(lvarBytes); + + Iterator vars = func.localVariables(); + while (vars.hasNext()) { + DefinedVariable var = (DefinedVariable)vars.next(); + comment("mem " + var.memref().toSource() + ": " + var.name()); + } + } + // #@@} + + // #@@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); + } + } + mov(bp(), sp()); + pop(bp()); + ret(); + } + // #@@} + + // #@@range/jmpEpilogue{ + protected void jmpEpilogue() { + jmp(new Label(epilogueLabel(currentFunction))); + } + // #@@} + + // #@@range/epilogueLabel{ + protected String epilogueLabel(DefinedFunction func) { + return ".L" + func.name() + "$epilogue"; + } + // #@@} + protected void allocateParameters(DefinedFunction func) { Iterator vars = func.parameters(); long word = paramStartWord; @@ -311,12 +369,26 @@ public class CodeGenerator extends Visitor implements ASTLHSVisitor { } } - // Fixes addresses of local variables. - // Returns byte-length of the local variable area. - protected long allocateLocalVariables(DefinedFunction func, - long numSavedRegs) { - long initLen = numSavedRegs * stackWordSize; - long maxLen = allocateScope(func.body().scope(), initLen); + /** + * Allocates addresses of local variables, but offset is still + * not determined, assign unfixed IndirectMemoryReference. + */ + protected void allocateLocalVariablesTemp(LocalScope scope) { + Iterator vars = scope.allLocalVariables(); + while (vars.hasNext()) { + DefinedVariable var = (DefinedVariable)vars.next(); + var.setMemref(new IndirectMemoryReference(bp())); + } + } + + /** + * Fixes addresses of local variables. + * Returns byte-length of the local variable area. + * Note that numSavedRegs includes bp. + */ + protected long allocateLocalVariables(LocalScope scope, long numSavedRegs) { + long initLen = (numSavedRegs - 1) * stackWordSize; + long maxLen = allocateScope(scope, initLen); return maxLen - initLen; } @@ -327,10 +399,10 @@ public class CodeGenerator extends Visitor implements ASTLHSVisitor { DefinedVariable var = (DefinedVariable)vars.next(); if (stackGrowsLower) { len = Assembler.align(len + var.allocSize(), stackAlignment); - var.setMemref(mem(-len, bp())); + fixMemref((IndirectMemoryReference)var.memref(), -len); } else { - var.setMemref(mem(len, bp())); + fixMemref((IndirectMemoryReference)var.memref(), len); len = Assembler.align(len + var.allocSize(), stackAlignment); } } @@ -346,6 +418,10 @@ public class CodeGenerator extends Visitor implements ASTLHSVisitor { return maxLen; } + protected void fixMemref(IndirectMemoryReference memref, long offset) { + memref.fixOffset(offset); + } + protected void extendStack(long len) { if (len > 0) { add(imm(len * (stackGrowsLower ? -1 : 1)), sp()); @@ -886,7 +962,7 @@ public class CodeGenerator extends Visitor implements ASTLHSVisitor { } protected IndirectMemoryReference mem(Register reg) { - return new IndirectMemoryReference(reg); + return new IndirectMemoryReference(0, reg); } protected IndirectMemoryReference mem(long offset, Register reg) {