diff --git a/ChangeLog b/ChangeLog index 98aa999..3df01ff 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +Mon Dec 15 00:05:16 2008 Minero Aoki + + * introduce virtual stack again, for only local variables and tmp + variables. Function arguments use true push & pop. + + * --verbose-asm prints stack frame layout in detail. + Sun Dec 14 21:04:56 2008 Minero Aoki * test: test alloca more. diff --git a/net/loveruby/cflat/asm/AsmOperand.java b/net/loveruby/cflat/asm/AsmOperand.java index 6d374d4..a21afc7 100644 --- a/net/loveruby/cflat/asm/AsmOperand.java +++ b/net/loveruby/cflat/asm/AsmOperand.java @@ -21,4 +21,8 @@ abstract public class AsmOperand implements OperandPattern { public boolean match(AsmOperand operand) { return equals(operand); } + + public void fixStackOffset(long diff) { + // does nothing by default + } } diff --git a/net/loveruby/cflat/asm/Assembler.java b/net/loveruby/cflat/asm/Assembler.java index 339732c..9ee5b8a 100644 --- a/net/loveruby/cflat/asm/Assembler.java +++ b/net/loveruby/cflat/asm/Assembler.java @@ -275,6 +275,11 @@ public class Assembler { mov(naturalType, src, dest); } + // for stack access + public void relocatableMov(AsmOperand src, AsmOperand dest) { + assemblies.add(new Instruction("mov", typeSuffix(naturalType), src, dest, true)); + } + public void mov(Type type, AsmOperand src, AsmOperand dest) { insn(type, "mov", src, dest); } diff --git a/net/loveruby/cflat/asm/Assembly.java b/net/loveruby/cflat/asm/Assembly.java index ee5986e..1f0d5dd 100644 --- a/net/loveruby/cflat/asm/Assembly.java +++ b/net/loveruby/cflat/asm/Assembly.java @@ -22,4 +22,8 @@ abstract public class Assembly { public void collectStatistics(AsmStatistics stats) { // does nothing by default. } + + public void fixStackOffset(long diff) { + // does nothing by default. + } } diff --git a/net/loveruby/cflat/asm/BaseSymbol.java b/net/loveruby/cflat/asm/BaseSymbol.java index ce53853..8b11c2f 100644 --- a/net/loveruby/cflat/asm/BaseSymbol.java +++ b/net/loveruby/cflat/asm/BaseSymbol.java @@ -8,4 +8,8 @@ abstract public class BaseSymbol implements Symbol { public void collectStatistics(AsmStatistics stats) { stats.symbolUsed(this); } + + public Literal plus(long n) { + throw new Error("must not happen: BaseSymbol.plus called"); + } } diff --git a/net/loveruby/cflat/asm/DirectMemoryReference.java b/net/loveruby/cflat/asm/DirectMemoryReference.java index a522007..6f9dfd6 100644 --- a/net/loveruby/cflat/asm/DirectMemoryReference.java +++ b/net/loveruby/cflat/asm/DirectMemoryReference.java @@ -22,4 +22,16 @@ public class DirectMemoryReference extends MemoryReference { public String toSource(SymbolTable table) { return this.value.toSource(table); } + + public int compareTo(MemoryReference mem) { + return -(mem.cmp(this)); + } + + protected int cmp(IndirectMemoryReference mem) { + return 1; + } + + protected int cmp(DirectMemoryReference mem) { + return value.compareTo(mem.value); + } } diff --git a/net/loveruby/cflat/asm/IndirectMemoryReference.java b/net/loveruby/cflat/asm/IndirectMemoryReference.java index 2283557..21a5828 100644 --- a/net/loveruby/cflat/asm/IndirectMemoryReference.java +++ b/net/loveruby/cflat/asm/IndirectMemoryReference.java @@ -43,6 +43,10 @@ public class IndirectMemoryReference extends MemoryReference { base.collectStatistics(stats); } + public void fixStackOffset(long diff) { + offset = offset.plus(diff); + } + public String toString() { return toSource(SymbolTable.dummy()); } @@ -54,4 +58,22 @@ public class IndirectMemoryReference extends MemoryReference { return (offset.isZero() ? "" : offset.toSource(table)) + "(" + base.toSource(table) + ")"; } + + public int compareTo(MemoryReference mem) { + return -(mem.cmp(this)); + } + + protected int cmp(DirectMemoryReference mem) { + return -1; + } + + protected int cmp(IndirectMemoryReference mem) { + int c = base.baseName().compareTo(mem.base.baseName()); + if (c != 0) { + return c; + } + else { + return offset.compareTo(mem.offset); + } + } } diff --git a/net/loveruby/cflat/asm/Instruction.java b/net/loveruby/cflat/asm/Instruction.java index ff244dd..7a1f42e 100644 --- a/net/loveruby/cflat/asm/Instruction.java +++ b/net/loveruby/cflat/asm/Instruction.java @@ -5,32 +5,41 @@ public class Instruction extends Assembly { protected String mnemonic; protected String suffix; protected AsmOperand[] operands; + protected boolean needRelocation; public Instruction(String mnemonic) { - this(mnemonic, "", new AsmOperand[0]); + this(mnemonic, "", new AsmOperand[0], false); } public Instruction(String mnemonic, String suffix, AsmOperand a1) { - this(mnemonic, suffix, new AsmOperand[] { a1 }); + this(mnemonic, suffix, new AsmOperand[] { a1 }, false); } public Instruction(String mnemonic, String suffix, AsmOperand a1, AsmOperand a2) { - this(mnemonic, suffix, new AsmOperand[] { a1, a2 }); + this(mnemonic, suffix, new AsmOperand[] { a1, a2 }, false); } - public Instruction(String mnemonic, String suffix, AsmOperand[] operands) { + public Instruction(String mnemonic, String suffix, + AsmOperand a1, AsmOperand a2, boolean reloc) { + this(mnemonic, suffix, new AsmOperand[] { a1, a2 }, reloc); + } + + public Instruction(String mnemonic, String suffix, AsmOperand[] operands, boolean reloc) { this.mnemonic = mnemonic; this.suffix = suffix; this.operands = operands; + this.needRelocation = reloc; } public Instruction build(String mnemonic, AsmOperand o1) { - return new Instruction(mnemonic, this.suffix, new AsmOperand[] { o1 }); + return new Instruction(mnemonic, this.suffix, + new AsmOperand[] { o1 }, needRelocation); } public Instruction build(String mnemonic, AsmOperand o1, AsmOperand o2) { - return new Instruction(mnemonic, this.suffix, new AsmOperand[] { o1, o2 }); + return new Instruction(mnemonic, this.suffix, + new AsmOperand[] { o1, o2 }, needRelocation); } public boolean isInstruction() { @@ -79,6 +88,13 @@ public class Instruction extends Assembly { } } + public void fixStackOffset(long diff) { + if (!needRelocation) return; + for (int i = 0; i < operands.length; i++) { + operands[i].fixStackOffset(diff); + } + } + public String toSource(SymbolTable table) { StringBuffer buf = new StringBuffer(); buf.append("\t"); diff --git a/net/loveruby/cflat/asm/IntegerLiteral.java b/net/loveruby/cflat/asm/IntegerLiteral.java index f9e3149..374db11 100644 --- a/net/loveruby/cflat/asm/IntegerLiteral.java +++ b/net/loveruby/cflat/asm/IntegerLiteral.java @@ -24,6 +24,10 @@ public class IntegerLiteral implements Literal { return value == 0; } + public IntegerLiteral plus(long diff) { + return new IntegerLiteral(value + diff); + } + public IntegerLiteral integerLiteral() { return this; } @@ -39,4 +43,28 @@ public class IntegerLiteral implements Literal { public void collectStatistics(AsmStatistics stats) { // does nothing } + + public String toString() { + return "$" + value; + } + + public int compareTo(Literal lit) { + return -(lit.cmp(this)); + } + + public int cmp(IntegerLiteral i) { + return new Long(value).compareTo(new Long(i.value)); + } + + public int cmp(NamedSymbol sym) { + return -1; + } + + public int cmp(UnnamedSymbol sym) { + return -1; + } + + public int cmp(SuffixedSymbol sym) { + return -1; + } } diff --git a/net/loveruby/cflat/asm/Literal.java b/net/loveruby/cflat/asm/Literal.java index 95836fe..ce1d29d 100644 --- a/net/loveruby/cflat/asm/Literal.java +++ b/net/loveruby/cflat/asm/Literal.java @@ -1,8 +1,13 @@ package net.loveruby.cflat.asm; -public interface Literal { +public interface Literal extends Comparable { public String toSource(); public String toSource(SymbolTable table); public void collectStatistics(AsmStatistics stats); public boolean isZero(); + public Literal plus(long diff); + public int cmp(IntegerLiteral i); + public int cmp(NamedSymbol sym); + public int cmp(UnnamedSymbol sym); + public int cmp(SuffixedSymbol sym); } diff --git a/net/loveruby/cflat/asm/MemoryReference.java b/net/loveruby/cflat/asm/MemoryReference.java index ae2a559..2bd1ceb 100644 --- a/net/loveruby/cflat/asm/MemoryReference.java +++ b/net/loveruby/cflat/asm/MemoryReference.java @@ -1,7 +1,11 @@ package net.loveruby.cflat.asm; -abstract public class MemoryReference extends AsmOperand { +abstract public class MemoryReference + extends AsmOperand implements Comparable { public boolean isMemoryReference() { return true; } + + abstract protected int cmp(DirectMemoryReference mem); + abstract protected int cmp(IndirectMemoryReference mem); } diff --git a/net/loveruby/cflat/asm/NamedSymbol.java b/net/loveruby/cflat/asm/NamedSymbol.java index 127462a..c639fd2 100644 --- a/net/loveruby/cflat/asm/NamedSymbol.java +++ b/net/loveruby/cflat/asm/NamedSymbol.java @@ -22,4 +22,24 @@ public class NamedSymbol extends BaseSymbol { public String toString() { return "#" + name; } + + public int compareTo(Literal lit) { + return -(lit.compareTo(this)); + } + + public int cmp(IntegerLiteral i) { + return 1; + } + + public int cmp(NamedSymbol sym) { + return name.compareTo(sym.name); + } + + public int cmp(UnnamedSymbol sym) { + return -1; + } + + public int cmp(SuffixedSymbol sym) { + return toString().compareTo(sym.toString()); + } } diff --git a/net/loveruby/cflat/asm/SuffixedSymbol.java b/net/loveruby/cflat/asm/SuffixedSymbol.java index a288beb..438deb9 100644 --- a/net/loveruby/cflat/asm/SuffixedSymbol.java +++ b/net/loveruby/cflat/asm/SuffixedSymbol.java @@ -17,6 +17,10 @@ public class SuffixedSymbol implements Symbol { base.collectStatistics(stats); } + public Literal plus(long n) { + throw new Error("must not happen: SuffixedSymbol.plus called"); + } + public String name() { return base.name(); } @@ -32,4 +36,24 @@ public class SuffixedSymbol implements Symbol { public String toString() { return base.toString() + suffix; } + + public int compareTo(Literal lit) { + return -(lit.compareTo(this)); + } + + public int cmp(IntegerLiteral i) { + return 1; + } + + public int cmp(NamedSymbol sym) { + return toString().compareTo(sym.toString()); + } + + public int cmp(UnnamedSymbol sym) { + return -1; + } + + public int cmp(SuffixedSymbol sym) { + return toString().compareTo(sym.toString()); + } } diff --git a/net/loveruby/cflat/asm/UnnamedSymbol.java b/net/loveruby/cflat/asm/UnnamedSymbol.java index c7c00b1..bc49a87 100644 --- a/net/loveruby/cflat/asm/UnnamedSymbol.java +++ b/net/loveruby/cflat/asm/UnnamedSymbol.java @@ -20,4 +20,24 @@ public class UnnamedSymbol extends BaseSymbol { public String toString() { return super.toString(); } + + public int compareTo(Literal lit) { + return -(lit.compareTo(this)); + } + + public int cmp(IntegerLiteral i) { + return 1; + } + + public int cmp(NamedSymbol sym) { + return 1; + } + + public int cmp(UnnamedSymbol sym) { + return toString().compareTo(sym.toString()); + } + + public int cmp(SuffixedSymbol sym) { + return 1; + } } diff --git a/net/loveruby/cflat/compiler/CodeGenerator.java b/net/loveruby/cflat/compiler/CodeGenerator.java index 9ebeae3..62a7c55 100644 --- a/net/loveruby/cflat/compiler/CodeGenerator.java +++ b/net/loveruby/cflat/compiler/CodeGenerator.java @@ -286,6 +286,51 @@ public class CodeGenerator // Compile Function // + /* Standard IA-32 stack frame layout (after prologue) + * + * ======================= esp #3 (stack top just before function call) + * next arg 1 + * --------------------- + * next arg 2 + * --------------------- + * next arg 3 + * --------------------- esp #2 (stack top after alloca call) + * alloca area + * --------------------- esp #1 (stack top just after prelude) + * temporary + * variables... + * --------------------- -16(%ebp) + * lvar 3 + * --------------------- -12(%ebp) + * lvar 2 + * --------------------- -8(%ebp) + * lvar 1 + * --------------------- -4(%ebp) + * callee-saved register + * ======================= 0(%ebp) + * saved ebp + * --------------------- 4(%ebp) + * return address + * --------------------- 8(%ebp) + * arg 1 + * --------------------- 12(%ebp) + * arg 2 + * --------------------- 16(%ebp) + * arg 3 + * ... + * ... + * ======================= stack bottom + */ + + /* + * Platform Dependent Stack Parameters + */ + static final protected boolean stackGrowsLower = true; + static final protected long stackWordSize = 4; + static final protected long stackAlignment = stackWordSize; + // +1 for return address, +1 for saved bp + static final protected long paramStartWord = 2; + /** Compiles a function. */ // #@@range/compileFunction{ protected void compileFunction(DefinedFunction func) { @@ -304,13 +349,25 @@ public class CodeGenerator // #@@} protected void compileFunctionBody(DefinedFunction func) { + initVirtualStack(); List bodyAsms = compileStmts(func); + long maxTmpBytes = maxTmpBytes(); AsmStatistics stats = AsmStatistics.collect(bodyAsms); bodyAsms = reduceLabels(bodyAsms, stats); List saveRegs = usedCalleeSavedRegistersWithoutBP(stats); - long lvarBytes = allocateLocalVariables(func.body().scope(), - saveRegs.size()); - prologue(func, saveRegs, lvarBytes); + long saveRegsBytes = saveRegs.size() * stackWordSize; + long lvarBytes = allocateLocalVariables( + func.body().scope(), saveRegsBytes); + fixTmpOffsets(bodyAsms, saveRegsBytes + lvarBytes); + + if (options.isVerboseAsm()) { + printStackFrameLayout( + saveRegsBytes, lvarBytes, maxTmpBytes, + func.localVariables()); + } + + initVirtualStack(); + prologue(func, saveRegs, saveRegsBytes + lvarBytes + maxTmpBytes); if (options.isPositionIndependent() && stats.doesRegisterUsed(GOTBaseReg())) { loadGOTBaseAddress(GOTBaseReg()); @@ -319,6 +376,46 @@ public class CodeGenerator epilogue(func, saveRegs, lvarBytes); } + protected void printStackFrameLayout( + long saveRegsBytes, long lvarBytes, long maxTmpBytes, + List lvars) { + List vars = new ArrayList(); + for (DefinedVariable var : lvars) { + vars.add(new MemInfo(var.memref(), var.name())); + } + vars.add(new MemInfo(mem(0, bp()), "return address")); + vars.add(new MemInfo(mem(4, bp()), "saved %ebp")); + if (saveRegsBytes > 0) { + vars.add(new MemInfo(mem(-saveRegsBytes, bp()), + "saved callee-saved registers (" + saveRegsBytes + " bytes)")); + } + if (maxTmpBytes > 0) { + long offset = -(saveRegsBytes + lvarBytes + maxTmpBytes); + vars.add(new MemInfo(mem(offset, bp()), + "tmp variables (" + maxTmpBytes + " bytes)")); + } + Collections.sort(vars, new Comparator() { + public int compare(MemInfo x, MemInfo y) { + return x.mem.compareTo(y.mem); + } + }); + comment("---- Stack Frame Layout -----------"); + for (MemInfo info : vars) { + comment(info.mem.toString() + ": " + info.name); + } + comment("-----------------------------------"); + } + + class MemInfo { + MemoryReference mem; + String name; + + MemInfo(MemoryReference mem, String name) { + this.mem = mem; + this.name = name; + } + } + protected List compileStmts(DefinedFunction func) { pushAssembler(); currentFunction = func; @@ -379,56 +476,14 @@ public class CodeGenerator return calleeSavedRegistersCache; } - /* Standard IA-32 stack frame layout (after prologue) - * - * ======================= esp (stack top) - * temporary - * variables... - * --------------------- ebp-(4*4) - * lvar 3 - * --------------------- ebp-(4*3) - * lvar 2 - * --------------------- ebp-(4*2) - * lvar 1 - * --------------------- ebp-(4*1) - * callee-saved register - * ======================= ebp - * saved ebp - * --------------------- ebp+(4*1) - * return address - * --------------------- ebp+(4*2) - * arg 1 - * --------------------- ebp+(4*3) - * arg 2 - * --------------------- ebp+(4*4) - * arg 3 - * ... - * ... - * ======================= stack bottom - */ - - /* - * Platform Dependent Stack Parameters - */ - static final protected boolean stackGrowsLower = true; - static final protected long stackWordSize = 4; - static final protected long stackAlignment = stackWordSize; - // +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()); + long frameSize) { + truePush(bp()); mov(sp(), bp()); saveRegisters(saveRegs); - extendStack(lvarBytes); - if (options.isVerboseAsm()) { - for (DefinedVariable var : func.localVariables()) { - comment("mem " + var.memref() + ": " + var.name()); - } - } + extendStack(frameSize); } // #@@} @@ -436,16 +491,9 @@ public class CodeGenerator protected void epilogue(DefinedFunction func, List savedRegs, long lvarBytes) { - //shrinkStack(lvarBytes); - if (! savedRegs.isEmpty()) { - long offset = stackGrowsLower - ? -1 * savedRegs.size() * stackWordSize - : (savedRegs.size() - 1) * stackWordSize; - lea(mem(offset, bp()), sp()); - restoreRegisters(savedRegs); - } + restoreRegisters(savedRegs); mov(bp(), sp()); - pop(bp()); + truePop(bp()); ret(); } // #@@} @@ -491,8 +539,7 @@ public class CodeGenerator * Returns byte-length of the local variable area. * Note that numSavedRegs includes bp. */ - protected long allocateLocalVariables(LocalScope scope, long numSavedRegs) { - long initLen = numSavedRegs * stackWordSize; + protected long allocateLocalVariables(LocalScope scope, long initLen) { long maxLen = allocateScope(scope, initLen); return maxLen - initLen; } @@ -525,13 +572,75 @@ public class CodeGenerator protected void extendStack(long len) { if (len > 0) { - add(imm(len * (stackGrowsLower ? -1 : 1)), sp()); + if (stackGrowsLower) { + sub(imm(len), sp()); + } + else { + add(imm(len), sp()); + } } } - protected void shrinkStack(long len) { + protected void rewindStack(long len) { if (len > 0) { - add(imm(len * (stackGrowsLower ? 1 : -1)), sp()); + if (stackGrowsLower) { + add(imm(len), sp()); + } + else { + sub(imm(len), sp()); + } + } + } + + protected long stackPointer; + protected long stackPointerMax; + + protected void initVirtualStack() { + stackPointer = 0; + stackPointerMax = stackPointer; + } + + protected long maxTmpBytes() { + return stackPointerMax; + } + + protected IndirectMemoryReference stackTop() { + if (stackGrowsLower) { + return mem(-stackPointer, bp()); + } + else { + return mem(stackPointer - stackWordSize, bp()); + } + } + + protected void push(Register reg) { + extendVirtualStack(stackWordSize); + as.relocatableMov(reg, stackTop()); + if (options.isVerboseAsm()) { + comment("push " + reg.name() + " -> " + stackTop()); + } + } + + protected void pop(Register reg) { + if (options.isVerboseAsm()) { + comment("pop " + reg.name() + " <- " + stackTop()); + } + as.relocatableMov(stackTop(), reg); + rewindVirtualStack(stackWordSize); + } + + protected void extendVirtualStack(long len) { + stackPointer += len; + stackPointerMax = Math.max(stackPointerMax, stackPointer); + } + + protected void rewindVirtualStack(long len) { + stackPointer -= len; + } + + protected void fixTmpOffsets(List asms, long offset) { + for (Assembly asm : asms) { + asm.fixStackOffset(offset * (stackGrowsLower ? -1 : 1)); } } @@ -545,7 +654,7 @@ public class CodeGenerator ListIterator args = node.finalArg(); while (args.hasPrevious()) { compile(args.previous()); - push(reg("ax")); + truePush(reg("ax")); } // call if (node.isStaticCall()) { @@ -559,7 +668,7 @@ public class CodeGenerator } // rewind stack // >4 bytes arguments are not supported. - shrinkStack(node.numArgs() * stackWordSize); + rewindStack(node.numArgs() * stackWordSize); } public void visit(ReturnNode node) { @@ -1250,8 +1359,8 @@ public class CodeGenerator public void setl(Register reg) { as.setl(reg); } public void setle(Register reg) { as.setle(reg); } public void test(Type type, Register a, Register b) { as.test(type, a, b); } - public void push(Register reg) { as.push(reg); } - public void pop(Register reg) { as.pop(reg); } + protected void truePush(Register reg) { as.push(reg); } + protected void truePop(Register reg) { as.pop(reg); } public void call(Symbol sym) { as.call(sym); } public void callAbsolute(Register reg) { as.callAbsolute(reg); } public void ret() { as.ret(); }