diff --git a/ChangeLog b/ChangeLog index 2abf9b0..bcde86a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,86 @@ +Sun Sep 28 05:07:30 2008 Minero Aoki + + * implement PIC generation. + + * lib/Makefile: should generate PIC for libcbc.o. + + * net/loveruby/cflat/compiler/Compiler.java: pass + CodeGeneratorOptions to CodeGenerator. + + * net/loveruby/cflat/compiler/Options.java: new options -fpic, + -fPIC. + + * net/loveruby/cflat/compiler/CodeGenerator.java: use position + independent address for global variables, common symbols, + defined/undefined functions and string constants. + + * net/loveruby/cflat/compiler/CodeGeneratorOptions.java: new + class. + + * net/loveruby/cflat/asm/ELFConstants.java: new interface to + define ELF-related constants. + + * net/loveruby/cflat/asm/Assembler.java: new method #hidden. + + * net/loveruby/cflat/asm/Assembler.java: new overload + #section(s,s,s,s,s). + + * net/loveruby/cflat/asm/IndirectMemoryReference.java: offset may + not long, but Literal. + + * net/loveruby/cflat/asm/Literal.java: new method #isZero. + + * net/loveruby/cflat/asm/IntegerLiteral.java: ditto. + + * net/loveruby/cflat/asm/LabelRef.java: ditto. + + * net/loveruby/cflat/ast/ExprNode.java: new method + #shouldEvaluatedToAddres. + + * net/loveruby/cflat/ast/VariableNode.java: override it. + + * net/loveruby/cflat/ast/CastNode.java: override it. + + * net/loveruby/cflat/ast/Entity.java: new method #cannotLoad, to + implement #shouldEvaluatedToAddress. + + * net/loveruby/cflat/ast/Variable.java: override it. + + * net/loveruby/cflat/ast/Function.java: override it. + + * net/loveruby/cflat/ast/ExprNode.java: rename method: #AsmLiteral + -> #AsmValue because it now returns ImmediateValue instead of + Literal. + + * net/loveruby/cflat/ast/CastNode.java: override it. + + * net/loveruby/cflat/ast/LiteralNode.java: rename method also. + + * net/loveruby/cflat/ast/IntegerLiteralNode.java: ditto. + + * net/loveruby/cflat/ast/StringLiteralNode.java: ditto. + + * net/loveruby/cflat/ast/Variable.java: holds memref and address + here, not in subclasses. + + * net/loveruby/cflat/ast/DefinedVariable.java: ditto. + + * net/loveruby/cflat/ast/UndefinedVariable.java: ditto. + + * net/loveruby/cflat/ast/LiteralNode.java: new method #memref. + + * net/loveruby/cflat/ast/IntegerLiteralNode.java: implement it. + + * net/loveruby/cflat/ast/StringLiteralNode.java: implement it. + + * net/loveruby/cflat/ast/ConstantEntry.java: holds memref and + address. + + * net/loveruby/cflat/ast/AST.java: new method #allFunctions, to + access undefined functions from code generator. + + * net/loveruby/cflat/ast/ToplevelScope.java: ditto. + Wed Sep 24 01:10:36 2008 Minero Aoki * net/loveruby/cflat/compiler/CodeGenerator.java: remove unused diff --git a/lib/Makefile b/lib/Makefile index 77ec7b1..05b2815 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -1,5 +1,5 @@ CBC = ../bin/cbc -CBFLAGS = -O +CBFLAGS = -O -fPIC TARGET = libcbc.a OBJS = libcbc.o diff --git a/net/loveruby/cflat/asm/Assembler.java b/net/loveruby/cflat/asm/Assembler.java index 465ae86..6509340 100644 --- a/net/loveruby/cflat/asm/Assembler.java +++ b/net/loveruby/cflat/asm/Assembler.java @@ -119,6 +119,10 @@ public class Assembler { directive("\t.section\t" + name); } + public void _section(String name, String flags, String type, String group, String linkage) { + directive("\t.section\t" + name + "," + flags + "," + type + "," + group + "," + linkage); + } + public void _globl(String sym) { directive(".globl " + sym); } @@ -127,6 +131,10 @@ public class Assembler { directive(".local " + sym); } + public void _hidden(String sym) { + directive("\t.hidden\t" + sym); + } + public void _comm(String sym, long size, long alignment) { directive("\t.comm\t" + sym + "," + size + "," + alignment); } @@ -160,7 +168,7 @@ public class Assembler { } public void _long(Label sym) { - directive(".long\t" + sym.toString()); + directive(".long\t" + sym); } public void _quad(long n) { @@ -168,7 +176,7 @@ public class Assembler { } public void _quad(Label sym) { - directive(".quad\t" + sym.toString()); + directive(".quad\t" + sym); } public void _string(String str) { diff --git a/net/loveruby/cflat/asm/ELFConstants.java b/net/loveruby/cflat/asm/ELFConstants.java new file mode 100644 index 0000000..ba00895 --- /dev/null +++ b/net/loveruby/cflat/asm/ELFConstants.java @@ -0,0 +1,21 @@ +package net.loveruby.cflat.asm; + +public interface ELFConstants { + // Flags + static public final String SectionFlag_allocatable = "a"; + static public final String SectionFlag_writable = "w"; + static public final String SectionFlag_executable = "x"; + static public final String SectionFlag_sectiongroup = "G"; + static public final String SectionFlag_strings = "S"; + static public final String SectionFlag_threadlocalstorage = "T"; + + // argument of "G" flag + static public final String Linkage_linkonce = "comdat"; + + // Types + static public final String SectionType_bits = "@progbits"; + static public final String SectionType_nobits = "@nobits"; + static public final String SectionType_note = "@note"; + + static public final String SymbolType_function = "@function"; +} diff --git a/net/loveruby/cflat/asm/IndirectMemoryReference.java b/net/loveruby/cflat/asm/IndirectMemoryReference.java index d332bb9..1ce4bab 100644 --- a/net/loveruby/cflat/asm/IndirectMemoryReference.java +++ b/net/loveruby/cflat/asm/IndirectMemoryReference.java @@ -1,23 +1,29 @@ package net.loveruby.cflat.asm; public class IndirectMemoryReference extends MemoryReference { - protected long offset; + protected Literal offset; protected Register base; protected boolean fixed; public IndirectMemoryReference(Register base) { - this.offset = 0; + this.offset = new IntegerLiteral(0); this.base = base; this.fixed = false; } public IndirectMemoryReference(long offset, Register base) { - this.offset = offset; + this.offset = new IntegerLiteral(offset); this.base = base; this.fixed = true; } - public long offset() { + public IndirectMemoryReference(Label offset, Register base) { + this.offset = new LabelRef(offset); + this.base = base; + this.fixed = true; + } + + public Literal offset() { return offset; } @@ -25,7 +31,7 @@ public class IndirectMemoryReference extends MemoryReference { if (fixed) { throw new Error("must not happen: fixed = true"); } - this.offset = realOffset; + this.offset = new IntegerLiteral(realOffset); this.fixed = true; } @@ -41,6 +47,7 @@ public class IndirectMemoryReference extends MemoryReference { if (! fixed) { throw new Error("must not happen: writing unfixed variable"); } - return (offset == 0 ? "" : "" + offset) + "(" + base.toSource() + ")"; + return (offset.isZero() ? "" : offset.toSource()) + + "(" + base.toSource() + ")"; } } diff --git a/net/loveruby/cflat/asm/IntegerLiteral.java b/net/loveruby/cflat/asm/IntegerLiteral.java index 6526af0..068d66e 100644 --- a/net/loveruby/cflat/asm/IntegerLiteral.java +++ b/net/loveruby/cflat/asm/IntegerLiteral.java @@ -17,6 +17,10 @@ public class IntegerLiteral extends Literal { return this.value; } + public boolean isZero() { + return value == 0; + } + public IntegerLiteral integerLiteral() { return this; } diff --git a/net/loveruby/cflat/asm/LabelRef.java b/net/loveruby/cflat/asm/LabelRef.java index cb39e55..68548dc 100644 --- a/net/loveruby/cflat/asm/LabelRef.java +++ b/net/loveruby/cflat/asm/LabelRef.java @@ -15,6 +15,10 @@ public class LabelRef extends Literal { return label.name(); } + public boolean isZero() { + return false; + } + public void collectStatistics(AsmStatistics stats) { stats.labelUsed(label); } diff --git a/net/loveruby/cflat/asm/Literal.java b/net/loveruby/cflat/asm/Literal.java index 06271b1..5630c09 100644 --- a/net/loveruby/cflat/asm/Literal.java +++ b/net/loveruby/cflat/asm/Literal.java @@ -3,4 +3,5 @@ package net.loveruby.cflat.asm; abstract public class Literal { abstract public String toSource(); abstract public void collectStatistics(AsmStatistics stats); + abstract public boolean isZero(); } diff --git a/net/loveruby/cflat/ast/AST.java b/net/loveruby/cflat/ast/AST.java index 1da0ef7..a542013 100644 --- a/net/loveruby/cflat/ast/AST.java +++ b/net/loveruby/cflat/ast/AST.java @@ -71,10 +71,21 @@ public class AST extends Node { return declarations.defuns().iterator(); } + public Iterator allFunctions() { + List result = new ArrayList(); + result.addAll(declarations.defuns()); + result.addAll(declarations.funcdecls()); + return result.iterator(); + } + public ToplevelScope scope() { return scope; } + public Iterator allGlobalVariables() { + return scope.allGlobalVariables().iterator(); + } + public Iterator globalVariables() { return scope.globalVariables().iterator(); } diff --git a/net/loveruby/cflat/ast/CastNode.java b/net/loveruby/cflat/ast/CastNode.java index 64d7305..ba0a421 100644 --- a/net/loveruby/cflat/ast/CastNode.java +++ b/net/loveruby/cflat/ast/CastNode.java @@ -27,12 +27,16 @@ public class CastNode extends ExprNode { return expr; } + public boolean shouldEvaluatedToAddress() { + return expr.shouldEvaluatedToAddress(); + } + public boolean isConstant() { return expr.isConstant(); } - public Literal asmLiteral() { - return expr.asmLiteral(); + public ImmediateValue asmValue() { + return expr.asmValue(); } public boolean isEffectiveCast() { diff --git a/net/loveruby/cflat/ast/ConstantEntry.java b/net/loveruby/cflat/ast/ConstantEntry.java index 3875557..ac01810 100644 --- a/net/loveruby/cflat/ast/ConstantEntry.java +++ b/net/loveruby/cflat/ast/ConstantEntry.java @@ -1,17 +1,16 @@ package net.loveruby.cflat.ast; -import net.loveruby.cflat.asm.AsmOperand; -import net.loveruby.cflat.asm.Label; +import net.loveruby.cflat.asm.*; -public class ConstantEntry -{ +public class ConstantEntry { protected long id; protected String value; protected Label label; + protected MemoryReference memref; + protected ImmediateValue address; public ConstantEntry(long i, String val) { id = i; value = val; - label = new Label(".LC" + id); } public long id() { @@ -22,7 +21,37 @@ public class ConstantEntry return value; } + public String symbol() { + return this.label.toString(); + } + + public void setLabel(Label label) { + this.label = label; + } + public Label label() { - return label; + if (this.label == null) { + throw new Error("must not happen: label == null"); + } + return this.label; + } + + public void setMemref(MemoryReference mem) { + this.memref = mem; + } + + public MemoryReference memref() { + if (this.memref == null) { + throw new Error("must not happen: memref == null"); + } + return this.memref; + } + + public void setAddress(ImmediateValue imm) { + this.address = imm; + } + + public ImmediateValue address() { + return this.address; } } diff --git a/net/loveruby/cflat/ast/DefinedVariable.java b/net/loveruby/cflat/ast/DefinedVariable.java index a6ad72c..650a104 100644 --- a/net/loveruby/cflat/ast/DefinedVariable.java +++ b/net/loveruby/cflat/ast/DefinedVariable.java @@ -3,8 +3,8 @@ import net.loveruby.cflat.type.*; import net.loveruby.cflat.asm.*; public class DefinedVariable extends Variable { + protected long sequence; protected ExprNode initializer; - protected MemoryReference memref; public DefinedVariable(boolean priv, TypeNode type, String name, ExprNode init) { @@ -17,6 +17,14 @@ public class DefinedVariable extends Variable { return true; } + public void setSequence(long seq) { + this.sequence = seq; + } + + public String symbol() { + return (sequence < 0) ? name : (name + "." + sequence); + } + public boolean hasInitializer() { return (initializer != null); } @@ -33,21 +41,6 @@ public class DefinedVariable extends Variable { this.initializer = expr; } - public void setMemref(MemoryReference mem) { - this.memref = mem; - } - - public MemoryReference memref() { - if (memref == null) { - throw new Error("unresolved variable address"); - } - return memref; - } - - public AsmOperand address() { - return null; - } - protected void _dump(Dumper d) { d.printMember("name", name); d.printMember("isPrivate", isPrivate); diff --git a/net/loveruby/cflat/ast/Entity.java b/net/loveruby/cflat/ast/Entity.java index 9dcc84a..2cd3506 100644 --- a/net/loveruby/cflat/ast/Entity.java +++ b/net/loveruby/cflat/ast/Entity.java @@ -19,6 +19,10 @@ abstract public class Entity extends Node { return name; } + public String symbol() { + return name(); + } + abstract public boolean isDefined(); abstract public boolean isInitialized(); @@ -52,6 +56,8 @@ abstract public class Entity extends Node { return (nRefered > 0); } + abstract public boolean cannotLoad(); + abstract public MemoryReference memref(); abstract public AsmOperand address(); diff --git a/net/loveruby/cflat/ast/ExprNode.java b/net/loveruby/cflat/ast/ExprNode.java index 63bc33e..ba5ad88 100644 --- a/net/loveruby/cflat/ast/ExprNode.java +++ b/net/loveruby/cflat/ast/ExprNode.java @@ -28,14 +28,6 @@ abstract public class ExprNode extends Node { } } - public boolean isConstant() { - return false; - } - - public Literal asmLiteral() { - throw new Error("ExprNode#asmLiteral called"); - } - public boolean isAssignable() { return false; } @@ -44,6 +36,18 @@ abstract public class ExprNode extends Node { return false; } + public boolean shouldEvaluatedToAddress() { + return false; + } + + public boolean isConstant() { + return false; + } + + public ImmediateValue asmValue() { + throw new Error("ExprNode#asmValue called"); + } + public boolean isConstantAddress() { return false; } diff --git a/net/loveruby/cflat/ast/Function.java b/net/loveruby/cflat/ast/Function.java index fdafddf..e6a3be3 100644 --- a/net/loveruby/cflat/ast/Function.java +++ b/net/loveruby/cflat/ast/Function.java @@ -4,6 +4,9 @@ import net.loveruby.cflat.asm.*; import java.util.*; abstract public class Function extends Entity { + protected String symbol; + protected AsmOperand address; + public Function(boolean priv, TypeNode t, String name) { super(priv, t, name); } @@ -25,16 +28,41 @@ abstract public class Function extends Entity { return returnType().isVoid(); } + public boolean cannotLoad() { + return true; + } + + public void setSymbol(String sym) { + if (this.symbol != null) { + throw new Error("must not happen: Function#symbol was set again"); + } + this.symbol = sym; + } + + public String symbol() { + if (this.symbol == null) { + throw new Error("must not happen: Function#symbol called but null"); + } + return this.symbol; + } + public MemoryReference memref() { return null; } + public void setAddress(AsmOperand addr) { + this.address = addr; + } + public AsmOperand address() { - return new ImmediateValue(label()); + if (address == null) { + throw new Error("must not happen: Function.address == null"); + } + return this.address; } public Label label() { // FIXME: should cache - return new Label(name); + return new Label(symbol()); } } diff --git a/net/loveruby/cflat/ast/IntegerLiteralNode.java b/net/loveruby/cflat/ast/IntegerLiteralNode.java index 451b26e..9f07b34 100644 --- a/net/loveruby/cflat/ast/IntegerLiteralNode.java +++ b/net/loveruby/cflat/ast/IntegerLiteralNode.java @@ -1,7 +1,6 @@ package net.loveruby.cflat.ast; import net.loveruby.cflat.type.*; -import net.loveruby.cflat.asm.Literal; -import net.loveruby.cflat.asm.IntegerLiteral; +import net.loveruby.cflat.asm.*; public class IntegerLiteralNode extends LiteralNode { protected long value; @@ -15,8 +14,12 @@ public class IntegerLiteralNode extends LiteralNode { return value; } - public Literal asmLiteral() { - return new IntegerLiteral(value); + public ImmediateValue asmValue() { + return new ImmediateValue(new IntegerLiteral(value)); + } + + public MemoryReference memref() { + throw new Error("must not happen: IntegerLiteralNode#memref"); } protected void _dump(Dumper d) { diff --git a/net/loveruby/cflat/ast/LiteralNode.java b/net/loveruby/cflat/ast/LiteralNode.java index e92fde6..645ca35 100644 --- a/net/loveruby/cflat/ast/LiteralNode.java +++ b/net/loveruby/cflat/ast/LiteralNode.java @@ -1,6 +1,6 @@ package net.loveruby.cflat.ast; import net.loveruby.cflat.type.*; -import net.loveruby.cflat.asm.Literal; +import net.loveruby.cflat.asm.*; abstract public class LiteralNode extends ExprNode { protected Location location; @@ -28,5 +28,6 @@ abstract public class LiteralNode extends ExprNode { return true; } - abstract public Literal asmLiteral(); + abstract public ImmediateValue asmValue(); + abstract public MemoryReference memref(); } diff --git a/net/loveruby/cflat/ast/StringLiteralNode.java b/net/loveruby/cflat/ast/StringLiteralNode.java index e4e0e45..397fd8f 100644 --- a/net/loveruby/cflat/ast/StringLiteralNode.java +++ b/net/loveruby/cflat/ast/StringLiteralNode.java @@ -5,6 +5,8 @@ import net.loveruby.cflat.asm.*; public class StringLiteralNode extends LiteralNode { protected String value; protected ConstantEntry entry; + protected ImmediateValue asmValue; + protected MemoryReference memref; public StringLiteralNode(Location loc, TypeRef ref, String value) { super(loc, ref); @@ -24,8 +26,16 @@ public class StringLiteralNode extends LiteralNode { return entry.label(); } - public Literal asmLiteral() { - return new LabelRef(label()); + public MemoryReference memref() { + return entry.memref(); + } + + public AsmOperand address() { + return entry.address(); + } + + public ImmediateValue asmValue() { + return entry.address(); } protected long id() { diff --git a/net/loveruby/cflat/ast/ToplevelScope.java b/net/loveruby/cflat/ast/ToplevelScope.java index fc33815..a34d033 100644 --- a/net/loveruby/cflat/ast/ToplevelScope.java +++ b/net/loveruby/cflat/ast/ToplevelScope.java @@ -51,6 +51,28 @@ public class ToplevelScope extends Scope { return staticLocalVariables; } + /** Returns a list of all global variables. + * "All global variable" means: + * + * * has global scope + * * defined or undefined + * * public or private + */ + public List allGlobalVariables() { + List result = new ArrayList(); + List src = new ArrayList(); + src.addAll(entities.values()); + src.addAll(staticLocalVariables()); + Iterator ents = src.iterator(); + while (ents.hasNext()) { + Object ent = ents.next(); + if (ent instanceof Variable) { + result.add(ent); + } + } + return result; + } + /** Returns the list of global variables. * A global variable is a variable which has * global scope and is initialized. */ diff --git a/net/loveruby/cflat/ast/UndefinedVariable.java b/net/loveruby/cflat/ast/UndefinedVariable.java index dd57a6c..ab320b0 100644 --- a/net/loveruby/cflat/ast/UndefinedVariable.java +++ b/net/loveruby/cflat/ast/UndefinedVariable.java @@ -11,22 +11,6 @@ public class UndefinedVariable extends Variable { public boolean isPrivate() { return false; } public boolean isInitialized() { return false; } - public String symbol() { - return name(); - } - - public void setMemref(MemoryReference mem) { - throw new Error("UndefinedVariable#Memref"); - } - - public MemoryReference memref() { - return new DirectMemoryReference(new Label(symbol())); - } - - public AsmOperand address() { - return new ImmediateValue(new Label(symbol())); - } - protected void _dump(Dumper d) { d.printMember("name", name); d.printMember("isPrivate", isPrivate()); diff --git a/net/loveruby/cflat/ast/Variable.java b/net/loveruby/cflat/ast/Variable.java index 66201b1..f44abe8 100644 --- a/net/loveruby/cflat/ast/Variable.java +++ b/net/loveruby/cflat/ast/Variable.java @@ -3,20 +3,38 @@ import net.loveruby.cflat.type.*; import net.loveruby.cflat.asm.*; abstract public class Variable extends Entity { - protected long sequence; + protected MemoryReference memref; + protected MemoryReference address; public Variable(boolean priv, TypeNode type, String name) { super(priv, type, name); - sequence = -1; } - public void setSequence(long seq) { - this.sequence = seq; + public boolean cannotLoad() { + return type().isAllocatedArray(); } - public String symbol() { - return (sequence < 0) ? name : (name + "." + sequence); + public void setMemref(MemoryReference mem) { + this.memref = mem; } - abstract public void setMemref(MemoryReference addr); + public MemoryReference memref() { + checkAddress(); + return memref; + } + + public void setAddress(MemoryReference mem) { + this.address = mem; + } + + public AsmOperand address() { + checkAddress(); + return address; + } + + protected void checkAddress() { + if (memref == null && address == null) { + throw new Error("address did not resolved: " + name); + } + } } diff --git a/net/loveruby/cflat/ast/VariableNode.java b/net/loveruby/cflat/ast/VariableNode.java index 3979ba0..89d74d4 100644 --- a/net/loveruby/cflat/ast/VariableNode.java +++ b/net/loveruby/cflat/ast/VariableNode.java @@ -48,6 +48,10 @@ public class VariableNode extends ExprNode { return entity().isParameter(); } + public boolean shouldEvaluatedToAddress() { + return entity().cannotLoad(); + } + public boolean isConstantAddress() { return true; } diff --git a/net/loveruby/cflat/compiler/CodeGenerator.java b/net/loveruby/cflat/compiler/CodeGenerator.java index 9a2ef66..dacf307 100644 --- a/net/loveruby/cflat/compiler/CodeGenerator.java +++ b/net/loveruby/cflat/compiler/CodeGenerator.java @@ -4,22 +4,20 @@ import net.loveruby.cflat.type.*; import net.loveruby.cflat.asm.*; import java.util.*; -public class CodeGenerator extends Visitor implements ASTLHSVisitor { +public class CodeGenerator + extends Visitor implements ASTLHSVisitor, ELFConstants { // #@@range/ctor{ - protected AsmOptimizer optimizer; + protected CodeGeneratorOptions options; protected ErrorHandler errorHandler; - protected boolean verboseAsm; protected LinkedList asStack; protected Assembler as; protected TypeTable typeTable; protected DefinedFunction currentFunction; - public CodeGenerator(AsmOptimizer optimizer, - ErrorHandler errorHandler, - boolean verboseAsm) { - this.optimizer = optimizer; + public CodeGenerator(CodeGeneratorOptions options, + ErrorHandler errorHandler) { + this.options = options; this.errorHandler = errorHandler; - this.verboseAsm = verboseAsm; this.asStack = new LinkedList(); } // #@@} @@ -29,8 +27,9 @@ public class CodeGenerator extends Visitor implements ASTLHSVisitor { public String generate(AST ast) { this.typeTable = ast.typeTable(); pushAssembler(); - allocateGlobalVariables(ast.globalVariables()); - allocateCommonSymbols(ast.commonSymbols()); + resolveConstants(ast.constantTable()); + resolveGlobalVariables(ast.allGlobalVariables()); + resolveFunctions(ast.allFunctions()); compileAST(ast); return popAssembler().toSource(); } @@ -66,48 +65,100 @@ public class CodeGenerator extends Visitor implements ASTLHSVisitor { } // .bss compileCommonSymbols(ast.commonSymbols()); + // others + if (options.isPICRequired()) { + PICThunk(GOTBaseReg()); + } } // #@@} + protected void resolveConstants(ConstantTable table) { + Iterator ents = table.entries(); + while (ents.hasNext()) { + ConstantEntry ent = (ConstantEntry)ents.next(); + ent.setLabel(new Label(".LC" + ent.id())); + if (options.isPICRequired()) { + Label offset = new Label(localGOTSymbol(ent.symbol())); + ent.setMemref(mem(offset, GOTBaseReg())); + } + else { + ent.setMemref(mem(ent.label())); + ent.setAddress(imm(ent.label())); + } + } + } + /** * Sets memory reference for... * * public global variables * * private global variables - * * static local variables - */ - // #@@range/allocateGlobalVariable - protected void allocateGlobalVariables(Iterator vars) { - while (vars.hasNext()) { - Variable var = (Variable)vars.next(); - var.setMemref(globalVariableAddress(var.symbol())); - } - } - // #@@} - - /** - * Sets address for... * * public common symbols * * private common symbols + * * static local variables */ - // #@@range/allocateCommonSymbols - protected void allocateCommonSymbols(Iterator comms) { - while (comms.hasNext()) { - Variable var = (Variable)comms.next(); - var.setMemref(commonSymbolAddress(var.symbol())); + // #@@range/resolveGlobalVariables + protected void resolveGlobalVariables(Iterator vars) { + while (vars.hasNext()) { + Variable var = (Variable)vars.next(); + resolveGlobalVariable(var); } } // #@@} - /** Linux/IA-32 dependent */ - // FIXME: PIC - protected MemoryReference globalVariableAddress(String sym) { - return new DirectMemoryReference(new Label(csymbol(sym))); + protected void resolveGlobalVariable(Variable var) { + String sym = var.symbol(); + MemoryReference mem; + if (options.isPICRequired()) { + if (var.isPrivate()) { + mem = mem(new Label(localGOTSymbol(sym)), GOTBaseReg()); + var.setMemref(mem); + } + else { + mem = mem(new Label(globalGOTSymbol(sym)), GOTBaseReg()); + var.setAddress(mem); + } + } + else { + mem = mem(new Label(csymbol(sym))); + var.setMemref(mem); + } } - /** Linux/IA-32 dependent */ - // FIXME: PIC - protected MemoryReference commonSymbolAddress(String sym) { - return new DirectMemoryReference(new Label(csymbol(sym))); + protected void resolveFunctions(Iterator funcs) { + while (funcs.hasNext()) { + Function func = (Function)funcs.next(); + func.setSymbol(functionSymbol(func)); + func.setAddress(functionAddress(func)); + } + } + + protected String functionSymbol(Function func) { + if (func.isDefined()) { + return csymbol(func.name()); + } + else { + if (options.isPICRequired()) { + return PLTSymbol(tmpsymbol(func.name())); + } + else { + return tmpsymbol(func.name()); + } + } + } + + protected AsmOperand functionAddress(Function func) { + if (options.isPICRequired()) { + String name = func.name(); + if (func.isPrivate()) { + return mem(new Label(localGOTSymbol(name)), GOTBaseReg()); + } + else { + return mem(new Label(globalGOTSymbol(name)), GOTBaseReg()); + } + } + else { + return imm(func.label()); + } } /** Generates static variable entries */ @@ -188,6 +239,84 @@ public class CodeGenerator extends Visitor implements ASTLHSVisitor { } // #@@} + // #@@range/tmpsymbol{ + // platform dependent + protected String tmpsymbol(String sym) { + return sym; + } + // #@@} + + // #@@range/csymbol{ + // platform dependent + protected String csymbol(String sym) { + return sym; + } + // #@@} + + // + // PIC related constants and codes + // + + static protected final String GOTName = "_GLOBAL_OFFSET_TABLE_"; + + protected void loadGOTBaseAddress(Register reg) { + call(PICThunkName(reg)); + add(imm(new Label(GOTName)), reg); + } + + protected String PICThunkName(Register reg) { + return "__i686.get_pc_thunk." + reg.baseName(); + } + + protected Register GOTBaseReg() { + return reg("bx"); + } + + protected String globalGOTSymbol(String base) { + return base + "@GOT"; + } + + protected String localGOTSymbol(String base) { + return base + "@GOTOFF"; + } + + protected String PLTSymbol(String base) { + return base + "@PLT"; + } + + static protected final String + PICThunkSectionFlags = SectionFlag_allocatable + + SectionFlag_executable + + SectionFlag_sectiongroup; + + protected void PICThunk(Register reg) { + // ELF section declaration; format: + // + // .section NAME, FLAGS, TYPE, flag_arguments + // + // FLAGS, TYPE, flag_arguments are optional. + // For "M" flag (a member of a section group), + // format is: + // + // .section NAME, "...M", TYPE, section_group_name, linkage + // + _section(".text" + "." + PICThunkName(reg), + "\"" + PICThunkSectionFlags + "\"", + SectionType_bits, // This section contains data + PICThunkName(reg), // The name of section group + Linkage_linkonce); // Only 1 copy should be generated + _globl(PICThunkName(reg)); + _hidden(PICThunkName(reg)); + _type(PICThunkName(reg), SymbolType_function); + label(PICThunkName(reg)); + mov(mem(sp()), reg); // fetch saved EIP to the GOT base register + ret(); + } + + // + // Compile Function + // + /** Compiles all functions and generates .text section. */ // #@@range/compileFunctions{ protected void compileFunctions(Iterator funcs) { @@ -222,6 +351,9 @@ public class CodeGenerator extends Visitor implements ASTLHSVisitor { long lvarBytes = allocateLocalVariables(func.body().scope(), saveRegs.size()); prologue(func, saveRegs, lvarBytes); + if (options.isPICRequired() && stats.doesRegisterUsed(GOTBaseReg())) { + loadGOTBaseAddress(GOTBaseReg()); + } as.addAll(bodyAsms); epilogue(func, saveRegs, lvarBytes); } @@ -232,7 +364,7 @@ public class CodeGenerator extends Visitor implements ASTLHSVisitor { compile(func.body()); label(epilogueLabel(func)); currentFunction = null; - return optimizer.optimize(popAssembler().assemblies()); + return options.optimizer().optimize(popAssembler().assemblies()); } protected List reduceLabels(List assemblies, AsmStatistics stats) { @@ -252,32 +384,18 @@ public class CodeGenerator extends Visitor implements ASTLHSVisitor { // #@@range/compile{ protected void compile(Node n) { - if (verboseAsm) { + if (options.isVerboseAsm()) { comment(n.getClass().getSimpleName() + " {"); as.indentComment(); } n.accept(this); - if (verboseAsm) { + if (options.isVerboseAsm()) { as.unindentComment(); comment("}"); } } // #@@} - // #@@range/tmpsymbol{ - // platform dependent - protected String tmpsymbol(String sym) { - return sym; - } - // #@@} - - // #@@range/csymbol{ - // platform dependent - protected String csymbol(String sym) { - return sym; - } - // #@@} - protected List usedCalleeSavedRegisters(AsmStatistics stats) { List result = new ArrayList(); Iterator regs = calleeSavedRegisters().iterator(); @@ -348,7 +466,7 @@ public class CodeGenerator extends Visitor implements ASTLHSVisitor { mov(sp(), bp()); saveRegisters(saveRegs); extendStack(lvarBytes); - if (verboseAsm) { + if (options.isVerboseAsm()) { Iterator vars = func.localVariables(); while (vars.hasNext()) { DefinedVariable var = (DefinedVariable)vars.next(); @@ -481,34 +599,32 @@ public class CodeGenerator extends Visitor implements ASTLHSVisitor { } } - /** cdecl call - * - * * all arguments are on stack - * * rollback stack by caller + /** + * Implements cdecl function call: + * * All arguments are on stack. + * * Rewind stack by caller. */ public void visit(FuncallNode node) { - ListIterator it = node.finalArg(); - while (it.hasPrevious()) { - ExprNode arg = (ExprNode)it.previous(); + // compile function arguments from right to left. + ListIterator args = node.finalArg(); + while (args.hasPrevious()) { + ExprNode arg = (ExprNode)args.previous(); compile(arg); push(reg("ax")); } + // call if (node.isStaticCall()) { - if (node.function().isDefined()) { - call(csymbol(node.function().name())); - } - else { - call(tmpsymbol(node.function().name())); - } + // call via function name + call(node.function().symbol()); } - else { // function call via pointer + else { + // call via pointer compile(node.expr()); callAbsolute(reg("ax")); } - if (node.numArgs() > 0) { - // >4 bytes arguments are not supported. - shrinkStack(node.numArgs() * stackWordSize); - } + // rewind stack + // >4 bytes arguments are not supported. + shrinkStack(node.numArgs() * stackWordSize); } public void visit(ReturnNode node) { @@ -538,7 +654,7 @@ public class CodeGenerator extends Visitor implements ASTLHSVisitor { } protected void compileStmt(Node node) { - if (verboseAsm) { + if (options.isVerboseAsm()) { comment(node.location().numberedLine()); } compile(node); @@ -683,7 +799,7 @@ public class CodeGenerator extends Visitor implements ASTLHSVisitor { AsmOperand right = null; if (!doesRequireRegister(node.operator()) && node.right().isConstant()){ compile(node.left()); - right = imm(node.right().asmLiteral()); + right = node.right().asmValue(); } else if (node.right().isConstantAddress()) { compile(node.left()); @@ -927,7 +1043,7 @@ public class CodeGenerator extends Visitor implements ASTLHSVisitor { compileLHS(node.lhs()); mov(reg("ax"), reg("cx")); load(node.type(), mem(reg("cx")), reg("ax")); - AsmOperand rhs = imm(node.rhs().asmLiteral()); + AsmOperand rhs = node.rhs().asmValue(); compileBinaryOp(node.operator(), node.type(), rhs); save(node.type(), reg("ax"), mem(reg("cx"))); } @@ -981,12 +1097,12 @@ public class CodeGenerator extends Visitor implements ASTLHSVisitor { } protected void compileLHS(Node node) { - if (verboseAsm) { + if (options.isVerboseAsm()) { comment("compileLHS: " + node.getClass().getSimpleName() + " {"); as.indentComment(); } node.acceptLHS(this); - if (verboseAsm) { + if (options.isVerboseAsm()) { as.unindentComment(); comment("compileLHS: }"); } @@ -1044,7 +1160,15 @@ public class CodeGenerator extends Visitor implements ASTLHSVisitor { * before calling this method. */ protected void loadConstant(ExprNode node, Register reg) { - mov(imm(node.asmLiteral()), reg); + if (node.asmValue() != null) { + mov(node.asmValue(), reg); + } + else if (node.memref() != null) { + lea(node.memref(), reg); + } + else { + throw new Error("must not happen: constant has no asm value"); + } } /** @@ -1052,13 +1176,14 @@ public class CodeGenerator extends Visitor implements ASTLHSVisitor { * by #isConstantAddress before calling this method. */ protected void loadVariable(ExprNode node, Register dest) { - if (node.type().isAllocatedArray()) { - // int[4] a; a implies &a + if (node.shouldEvaluatedToAddress()) { + // "int[4] a; a" implies &a + // "x = puts" implies &puts loadVariableAddress(node, dest); } else if (node.memref() == null) { - // "puts" equivalent to "&ptr" mov(node.address(), dest); + load(node.type(), mem(dest), dest); } else { // regular variable @@ -1097,6 +1222,10 @@ public class CodeGenerator extends Visitor implements ASTLHSVisitor { return new Register(name); } + protected DirectMemoryReference mem(Label label) { + return new DirectMemoryReference(label); + } + protected IndirectMemoryReference mem(Register reg) { return new IndirectMemoryReference(0, reg); } @@ -1105,6 +1234,10 @@ public class CodeGenerator extends Visitor implements ASTLHSVisitor { return new IndirectMemoryReference(offset, reg); } + protected IndirectMemoryReference mem(Label offset, Register reg) { + return new IndirectMemoryReference(offset, reg); + } + protected ImmediateValue imm(long n) { return new ImmediateValue(n); } @@ -1148,8 +1281,10 @@ public class CodeGenerator extends Visitor implements ASTLHSVisitor { public void _text() { as._text(); } public void _data() { as._data(); } public void _section(String name) { as._section(name); } + public void _section(String name, String flags, String type, String group, String linkage) { as._section(name, flags, type, group, linkage); } public void _globl(String sym) { as._globl(sym); } public void _local(String sym) { as._local(sym); } + public void _hidden(String sym) { as._hidden(sym); } public void _comm(String sym, long sz, long a) { as._comm(sym, sz, a); } public void _align(long n) { as._align(n); } public void _type(String sym, String type) { as._type(sym, type); } diff --git a/net/loveruby/cflat/compiler/CodeGeneratorOptions.java b/net/loveruby/cflat/compiler/CodeGeneratorOptions.java new file mode 100644 index 0000000..87cae52 --- /dev/null +++ b/net/loveruby/cflat/compiler/CodeGeneratorOptions.java @@ -0,0 +1,48 @@ +package net.loveruby.cflat.compiler; +import net.loveruby.cflat.asm.*; +import java.util.*; + +class CodeGeneratorOptions { + protected int optimizeLevel; + protected boolean generatePIC; + protected boolean verboseAsm; + + public CodeGeneratorOptions() { + optimizeLevel = 0; + generatePIC = false; + verboseAsm = false; + } + + public void setOptimizationLevel(int level) { + this.optimizeLevel = level; + } + + public AsmOptimizer optimizer() { + if (optimizeLevel > 0) { + return PeepholeOptimizer.defaultSet(); + } + else { + return new NullOptimizer(); + } + } + + class NullOptimizer implements AsmOptimizer { + public List optimize(List assemblies) { return assemblies; } + } + + public void generateVerboseAsm() { + this.verboseAsm = true; + } + + public boolean isVerboseAsm() { + return verboseAsm; + } + + public void generatePIC() { + this.generatePIC = true; + } + + public boolean isPICRequired() { + return generatePIC; + } +} diff --git a/net/loveruby/cflat/compiler/Compiler.java b/net/loveruby/cflat/compiler/Compiler.java index 4d3cf48..105b98a 100644 --- a/net/loveruby/cflat/compiler/Compiler.java +++ b/net/loveruby/cflat/compiler/Compiler.java @@ -185,9 +185,7 @@ public class Compiler { } protected String generateAssembly(AST ast, Options opts) { - CodeGenerator gen = new CodeGenerator(opts.optimizer(), - errorHandler, - opts.isVerboseAsm()); + CodeGenerator gen = new CodeGenerator(opts.genOptions(), errorHandler); return gen.generate(ast); } diff --git a/net/loveruby/cflat/compiler/Options.java b/net/loveruby/cflat/compiler/Options.java index 1da2c3d..d9eabd3 100644 --- a/net/loveruby/cflat/compiler/Options.java +++ b/net/loveruby/cflat/compiler/Options.java @@ -13,8 +13,7 @@ class Options { protected String outputFileName; protected boolean verbose; protected boolean debugParser; - protected int optimizeLevel = 0; - protected boolean verboseAsm = false; + protected CodeGeneratorOptions genOptions; protected List asOptions; // List protected List ldArgs; // List protected boolean noStartFiles = false; @@ -23,6 +22,7 @@ class Options { public Options(TypeTable typeTable, LibraryLoader loader) { this.typeTable = typeTable; this.loader = loader; + this.genOptions = new CodeGeneratorOptions(); this.asOptions = new ArrayList(); this.ldArgs = new ArrayList(); } @@ -114,21 +114,8 @@ class Options { return this.debugParser; } - public AsmOptimizer optimizer() { - if (optimizeLevel == 0) { - return new NullAsmOptimizer(); - } - else { - return PeepholeOptimizer.defaultSet(); - } - } - - class NullAsmOptimizer implements AsmOptimizer { - public List optimize(List assemblies) { return assemblies; } - } - - public boolean isVerboseAsm() { - return verboseAsm; + public CodeGeneratorOptions genOptions() { + return genOptions; } // List @@ -189,23 +176,24 @@ class Options { else if (arg.startsWith("-o")) { outputFileName = getOptArg(arg, args); } - // FIXME: PIC - //else if (arg.equals("-fpic")) - //else if (arg.equals("-fPIC")) - // -shared ?? + else if (arg.equals("-fpic")) { + genOptions.generatePIC(); + } + else if (arg.equals("-fPIC")) { + genOptions.generatePIC(); + } // FIXME: PIE //else if (arg.equals("-fpie")) //else if (arg.equals("-fPIE")) - //else if (arg.equals("-pie")) else if (arg.startsWith("-O")) { String type = arg.substring(2); if (! type.matches("^([0123s]|)$")) { parseError("unknown optimization switch: " + arg); } - optimizeLevel = type.equals("0") ? 0 : 1; + genOptions.setOptimizationLevel(type.equals("0") ? 0 : 1); } else if (arg.equals("--verbose-asm")) { - verboseAsm = true; + genOptions.generateVerboseAsm(); } else if (arg.startsWith("-Wa,")) { asOptions.addAll(parseCommaSeparatedOptions(arg)); @@ -217,8 +205,10 @@ class Options { ldArgs.add(new LdOption(arg)); } else if (arg.equals("-shared")) { + // FIXME: ?? any more work is required?? ldArgs.add(new LdOption(arg)); } + //else if (arg.equals("-pie")) else if (arg.startsWith("-L")) { ldArgs.add(new LdOption("-L" + getOptArg(arg, args))); } @@ -329,30 +319,30 @@ class Options { public void printUsage(PrintStream out) { out.println("Usage: cbc [options] file..."); - out.println("Generic Options:"); - out.println(" --check-syntax Checks syntax."); - out.println(" --dump-tokens Parses source file and dumps tokens."); - out.println(" --dump-ast Parses source file and dumps AST."); - out.println(" --dump-semantic Checks semantics and dumps AST."); + out.println("Global Options:"); + out.println(" --check-syntax Checks syntax and quit."); + out.println(" --dump-tokens Dumps tokens and quit."); + out.println(" --dump-ast Dumps AST and quit."); + out.println(" --dump-semantic Dumps AST after semantic check and quit."); // --dump-reference is hidden option - out.println(" -S Generates an assembly source."); - out.println(" --dump-asm Dumps an assembly source."); - out.println(" -c Generates an object file."); + out.println(" -S Generates an assembly source and quit."); + out.println(" --dump-asm Prints an assembly source and quit."); + out.println(" -c Generates an object file and quit."); out.println(" -o PATH Places output in file PATH."); - out.println(" -v Verbose mode."); - out.println(" --version Shows compiler version."); + out.println(" -v Turn on verbose mode."); + out.println(" --version Shows compiler version and quit."); out.println(" --help Prints this message and quit."); out.println(""); out.println("Parser Options:"); + out.println(" -I PATH Adds PATH as import file directory."); out.println(" --debug-parser Dumps parsing process."); out.println(""); - out.println("Compiler Options:"); - out.println(" -I PATH Adds PATH as import file directory."); + out.println("Code Generator Options:"); out.println(" -O Enables optimization."); out.println(" -O1, -O2, -O3 Equivalent to -O."); out.println(" -Os Equivalent to -O."); out.println(" -O0 Disables optimization (default)."); - //out.println(" -fPIC Generates PIC assembly."); + out.println(" -fPIC Generates PIC assembly."); //out.println(" -fPIE Generates PIE assembly."); out.println(" --verbose-asm Generate assembly with verbose comments."); out.println(""); @@ -363,8 +353,8 @@ class Options { out.println("Linker Options:"); out.println(" -l LIB Links the library LIB."); out.println(" -L PATH Adds PATH as library directory."); - //out.println(" -shared Linkes with shared library (default)."); - //out.println(" -static Linkes with static library."); + out.println(" -shared Generates shared library rather than executable."); + out.println(" -static Linkes only with static libraries."); //out.println(" -pie Generates PIE."); out.println(" -nostartfiles Do not link startup files."); out.println(" -nodefaultlibs Do not link default libraries."); diff --git a/test/test_cbc.sh b/test/test_cbc.sh index 929aada..e121c7c 100644 --- a/test/test_cbc.sh +++ b/test/test_cbc.sh @@ -322,6 +322,10 @@ assert_out() { assert_compile_success "$1.cb" && assert_stdout "$msg" "$@" && assert_compile_success -O "$1.cb" && + assert_stdout "$msg" "$@" && + assert_compile_success -fPIC "$1.cb" && + assert_stdout "$msg" "$@" && + assert_compile_success -O -fPIC "$1.cb" && assert_stdout "$msg" "$@" }