cbc/net/loveruby/cflat/compiler/CodeGenerator.java

810 lines
24 KiB
Java

package net.loveruby.cflat.compiler;
import net.loveruby.cflat.ast.*;
import net.loveruby.cflat.type.*;
import net.loveruby.cflat.asm.*;
import java.util.*;
public class CodeGenerator extends Visitor {
static public String generate(AST ast, TypeTable typeTable,
ErrorHandler errorHandler) {
CodeGenerator gen = new CodeGenerator(new Assembler(), errorHandler);
return gen.generateAssembly(ast, typeTable);
}
protected Assembler as;
protected DefinedFunction currentFunction;
protected TypeTable typeTable;
protected ErrorHandler errorHandler;
public CodeGenerator(Assembler as, ErrorHandler errorHandler) {
this.as = as;
this.errorHandler = errorHandler;
}
static public void p(String s) { System.err.println(s); }
/** Compiles "ast" and generates assembly code. */
public String generateAssembly(AST ast, TypeTable typeTable) {
typeTable = typeTable;
allocateGlobalVariables(ast.globalVariables());
allocateCommonSymbols(ast.commonSymbols());
as._file(ast.fileName());
// .data
compileGlobalVariables(ast.globalVariables());
if (!ast.constantTable().isEmpty()) {
compileConstants(ast.constantTable());
}
// .text
if (ast.functionDefined()) {
compileFunctions(ast.functions());
}
// .bss
compileCommonSymbols(ast.commonSymbols());
return as.string();
}
/**
* Sets address for...
* * public global variables
* * private global variables
* * static local variables
*/
protected void allocateGlobalVariables(Iterator vars) {
while (vars.hasNext()) {
Variable var = (Variable)vars.next();
var.setAddress(globalVariableAddress(var.symbol()));
}
}
/**
* Sets address for...
* * public common symbols
* * private common symbols
*/
protected void allocateCommonSymbols(Iterator comms) {
while (comms.hasNext()) {
Variable var = (Variable)comms.next();
var.setAddress(commonSymbolAddress(var.symbol()));
}
}
/** Linux/IA-32 dependent */
// FIXME: PIC
protected AsmEntity globalVariableAddress(String sym) {
return new Label(csymbol(sym));
}
/** Linux/IA-32 dependent */
// FIXME: PIC
protected AsmEntity commonSymbolAddress(String sym) {
return new Label(csymbol(sym));
}
/** Generates static variable entries */
protected void compileGlobalVariables(Iterator vars) {
as._data();
while (vars.hasNext()) {
DefinedVariable var = (DefinedVariable)vars.next();
dataEntry(var);
}
}
/** Generates initialized entries */
protected void dataEntry(DefinedVariable ent) {
if (!ent.isPrivate()) {
as._globl(csymbol(ent.symbol()));
}
as._align(ent.allocSize());
as._type(csymbol(ent.symbol()), "@object");
as._size(csymbol(ent.symbol()), ent.allocSize());
as.label(csymbol(ent.symbol()));
compileImmediate(ent.type(), ent.initializer());
}
/** Generates immediate values for .data section */
protected void compileImmediate(Type type, ExprNode n) {
// FIXME: support other constants
IntegerLiteralNode expr = (IntegerLiteralNode)n;
switch ((int)type.allocSize()) {
case 1: as._byte(expr.value()); break;
case 2: as._value(expr.value()); break;
case 4: as._long(expr.value()); break;
case 8: as._quad(expr.value()); break;
default:
throw new Error("entry size is not 1,2,4,8");
}
}
/** Generates BSS entries */
protected void compileCommonSymbols(Iterator ents) {
while (ents.hasNext()) {
Variable ent = (Variable)ents.next();
if (ent.isPrivate()) {
as._local(csymbol(ent.symbol()));
}
as._comm(csymbol(ent.symbol()), ent.allocSize(), ent.alignment());
}
}
/** Generates .rodata entry (constant strings) */
protected void compileConstants(ConstantTable table) {
as._section(".rodata");
Iterator ents = table.entries();
while (ents.hasNext()) {
ConstantEntry ent = (ConstantEntry)ents.next();
as.label(ent.label());
as._string(ent.value());
}
}
/** Compiles all functions and generates .text section. */
protected void compileFunctions(Iterator funcs) {
as._text();
while (funcs.hasNext()) {
DefinedFunction func = (DefinedFunction)funcs.next();
compileFunction(func);
}
}
/** Compiles a function. */
protected void compileFunction(DefinedFunction func) {
currentFunction = func;
String symbol = csymbol(func.name());
as._globl(symbol);
as._type(symbol, "@function");
as.label(symbol);
prologue(func);
allocateParameters(func);
allocateLocalVariables(func);
compile(func.body());
epilogue(func);
as._size(symbol, ".-" + symbol);
}
protected void compile(Node n) {
n.accept(this);
}
// platform dependent
protected String tmpsymbol(String sym) {
return sym;
}
// platform dependent
protected String csymbol(String sym) {
return sym;
}
protected void prologue(DefinedFunction func) {
as.pushq(bp());
as.movq(sp(), bp());
}
protected void epilogue(DefinedFunction func) {
as.label(epilogueLabel(func));
as.movq(bp(), sp());
as.popq(bp());
as.ret();
}
protected void jmpEpilogue() {
as.jmp(new Label(epilogueLabel(currentFunction)));
}
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)
* lvar 3
* --------------------- ebp-(4*2)
* lvar 2
* --------------------- ebp-(4*1)
* lvar 1
* ======================= 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 int stackDirection = -1; // stack grows lower
static final protected long stackWordSize = 4;
// 1 for return address, 1 for saved bp.
static final protected long paramStartOffset = 2;
static final protected long usedStackWords = 0;
protected void allocateParameters(DefinedFunction func) {
Iterator vars = func.parameters();
long i = paramStartOffset;
while (vars.hasNext()) {
Parameter var = (Parameter)vars.next();
var.setAddress(lvarAddressByWord(i));
i++;
}
}
protected CompositeAddress lvarAddressByWord(long offset) {
return new CompositeAddress(offset * stackWordSize, bp());
}
protected void allocateLocalVariables(DefinedFunction func) {
Iterator vars = func.localVariables();
long len = usedStackWords * stackWordSize;
while (vars.hasNext()) {
DefinedVariable var = (DefinedVariable)vars.next();
if (stackDirection < 0) {
len = align(len + var.allocSize(), stackWordSize);
var.setAddress(new CompositeAddress(-len, bp()));
}
else {
var.setAddress(new CompositeAddress(len, bp()));
len = align(len + var.allocSize(), stackWordSize);
}
}
if (len != 0) {
extendStack(len);
}
}
protected void extendStack(long len) {
as.addq(imm(stackDirection * len), sp());
}
protected void shrinkStack(long len) {
as.subq(imm(stackDirection * len), sp());
}
protected long align(long n, long alignment) {
return (n + alignment - 1) / alignment * alignment;
}
/** cdecl call
*
* * all arguments are on stack
* * rollback stack by caller
*/
public void visit(FuncallNode node) {
ListIterator it = node.finalArg();
while (it.hasPrevious()) {
ExprNode arg = (ExprNode)it.previous();
compile(arg);
as.pushq(reg("ax"));
}
//if (node.function().isVararg()) {
// ...
//}
if (node.isStaticCall()) {
if (node.function().isDefined()) {
as.call(csymbol(node.function().name()));
}
else {
as.call(tmpsymbol(node.function().name()));
}
}
else { // funcall via pointer
// FIXME
compile(node.expr());
as.ptrcall(reg("ax"));
}
if (node.numArgs() > 0) {
// FIXME: >4 size arguments are not supported.
shrinkStack(node.numArgs() * stackWordSize);
}
}
public void visit(ReturnNode node) {
if (node.expr() != null) {
compile(node.expr());
}
jmpEpilogue();
}
//
// Statements
//
public void visit(BlockNode node) {
Iterator vars = node.scope().variables();
while (vars.hasNext()) {
DefinedVariable var = (DefinedVariable)vars.next();
if (var.initializer() != null) {
compile(var.initializer());
saveWords(var.type(), "ax", var.address());
}
}
Iterator stmts = node.stmts();
while (stmts.hasNext()) {
compileStmt((Node)stmts.next());
}
}
// needed?
protected void compileStmt(Node node) {
compile(node);
}
private void testCond(Type t, String regname) {
as.test(t, reg(regname, t), reg(regname, t));
}
public void visit(IfNode node) {
compile(node.cond());
testCond(node.cond().type(), "ax");
if (node.elseBody() != null) {
as.jz(node.elseLabel());
compileStmt(node.thenBody());
as.jmp(node.endLabel());
as.label(node.elseLabel());
compileStmt(node.elseBody());
as.label(node.endLabel());
}
else {
as.jz(node.endLabel());
compileStmt(node.thenBody());
as.label(node.endLabel());
}
}
public void visit(CondExprNode node) {
compile(node.cond());
testCond(node.cond().type(), "ax");
as.jz(node.elseLabel());
compile(node.thenExpr());
as.jmp(node.endLabel());
as.label(node.elseLabel());
compile(node.elseExpr());
as.label(node.endLabel());
}
public void visit(SwitchNode node) {
compile(node.cond());
Type t = typeTable.signedInt();
Iterator cases = node.cases();
while (cases.hasNext()) {
CaseNode caseNode = (CaseNode)cases.next();
Iterator values = caseNode.values();
while (values.hasNext()) {
as.movq(imm(caseValue((Node)values.next())), reg("cx"));
as.cmp(t, reg("cx", t), reg("ax", t));
as.je(caseNode.beginLabel());
}
}
cases = node.cases();
while (cases.hasNext()) {
compile((CaseNode)cases.next());
}
}
protected long caseValue(Node node) {
if (!(node instanceof IntegerLiteralNode)) {
// FIXME: use exception
throw new Error("case accepts only integer literal");
}
return ((IntegerLiteralNode)node).value();
}
public void visit(CaseNode node) {
as.label(node.beginLabel());
compile(node.body());
}
public void visit(LogicalAndNode node) {
compile(node.left());
testCond(node.left().type(), "ax");
as.jz(node.endLabel());
compile(node.right());
as.label(node.endLabel());
}
public void visit(LogicalOrNode node) {
compile(node.left());
testCond(node.left().type(), "ax");
as.jnz(node.endLabel());
compile(node.right());
as.label(node.endLabel());
}
public void visit(WhileNode node) {
as.label(node.begLabel());
compile(node.cond());
testCond(node.cond().type(), "ax");
as.jz(node.endLabel());
compileStmt(node.body());
as.jmp(node.begLabel());
as.label(node.endLabel());
}
public void visit(DoWhileNode node) {
as.label(node.begLabel());
compileStmt(node.body());
as.label(node.continueLabel());
compile(node.cond());
testCond(node.cond().type(), "ax");
as.jnz(node.begLabel());
as.label(node.endLabel());
}
public void visit(ForNode node) {
compileStmt(node.init());
as.label(node.begLabel());
compile(node.cond());
testCond(node.cond().type(), "ax");
as.jz(node.endLabel());
compileStmt(node.body());
as.label(node.continueLabel());
compileStmt(node.incr());
as.jmp(node.begLabel());
as.label(node.endLabel());
}
public void visit(BreakNode node) {
as.jmp(node.targetLabel());
}
public void visit(ContinueNode node) {
as.jmp(node.targetLabel());
}
public void visit(LabelNode node) {
as.label(node.label());
compileStmt(node.stmt());
}
public void visit(GotoNode node) {
as.jmp(node.targetLabel());
}
//
// Expressions
//
public void visit(BinaryOpNode node) {
compile(node.right());
as.pushq(reg("ax"));
compile(node.left());
as.popq(reg("cx"));
compileBinaryOp(node.operator(), node.type());
}
protected void compileBinaryOp(String op, Type t) {
if (op.equals("+")) {
as.add(t, reg("cx", t), reg("ax", t));
}
else if (op.equals("-")) {
as.sub(t, reg("cx", t), reg("ax", t));
}
else if (op.equals("*")) {
as.imul(t, reg("cx", t), reg("ax", t));
}
else if (op.equals("/")) {
as.movq(imm(0), reg("dx"));
as.idiv(t, reg("cx", t));
}
else if (op.equals("%")) {
as.movq(imm(0), reg("dx"));
as.idiv(t, reg("cx", t));
as.movq(reg("dx"), reg("ax"));
}
else if (op.equals("&")) {
as.and(t, reg("cx", t), reg("ax", t));
}
else if (op.equals("|")) {
as.or(t, reg("cx", t), reg("ax", t));
}
else if (op.equals("^")) {
as.xor(t, reg("cx", t), reg("ax", t));
}
else if (op.equals(">>")) {
as.sar(t, cl(), reg("ax", t));
}
else if (op.equals("<<")) {
as.sal(t, cl(), reg("ax", t));
}
else {
// Comparison operators
as.cmp(t, reg("cx", t), reg("ax", t));
if (op.equals("==")) as.sete (al());
else if (op.equals("!=")) as.setne(al());
else if (op.equals(">")) as.setg (al());
else if (op.equals(">=")) as.setge(al());
else if (op.equals("<")) as.setl (al());
else if (op.equals("<=")) as.setle(al());
else {
throw new Error("unknown binary operator: " + op);
}
as.movzb(t, al(), reg("ax", t));
}
}
public void visit(UnaryOpNode node) {
compile(node.expr());
if (node.operator().equals("+")) {
;
}
else if (node.operator().equals("-")) {
as.neg(node.expr().type(), reg("ax", node.expr().type()));
}
else if (node.operator().equals("~")) {
as.not(node.expr().type(), reg("ax", node.expr().type()));
}
else if (node.operator().equals("!")) {
testCond(node.expr().type(), "ax");
as.sete(al());
as.movzbl(al(), reg("ax"));
}
}
public void visit(PrefixOpNode node) {
compileIncDec(node.operator(), node.expr());
loadWords(node.expr().type(), node.expr().address(), "ax");
}
public void visit(SuffixOpNode node) {
loadWords(node.expr().type(), node.expr().address(), "ax");
compileIncDec(node.operator(), node.expr());
}
protected void compileIncDec(String op, ExprNode e) {
if (op.equals("++")) {
if (e.type().isInteger()) {
as.inc(e.type(), e.address());
}
else {
as.addq(imm(e.type().size()), e.address());
}
}
else if (op.equals("--")) {
if (e.type().isInteger()) {
as.dec(e.type(), e.address());
}
else {
as.subq(imm(e.type().size()), e.address());
}
}
else {
throw new Error("unknown unary operator: " + op);
}
}
public void visit(CastNode node) {
// FIXME: insert cast op here
compile(node.expr());
}
public void visit(VariableNode node) {
if (node.type().isAllocatedArray()) {
as.leaq(node.address(), reg("ax"));
}
else {
loadWords(node.type(), node.address(), "ax");
}
}
public void visit(IntegerLiteralNode node) {
as.mov(node.type(), imm(node.value()), reg("ax", node.type()));
}
public void visit(StringLiteralNode node) {
loadWords(node.type(), imm(node.label()), "ax");
}
//
// Assignable expressions
//
public void visit(AssignNode node) {
if (node.lhs().isConstantAddress()) {
compile(node.rhs());
saveWords(node.type(), "ax", node.lhs().address());
}
else {
compile(node.rhs());
as.pushq(reg("ax"));
compileLHS(node.lhs());
as.popq(reg("ax"));
saveWords(node.type(), "ax", addr(PTRREG));
}
}
public void visit(OpAssignNode node) {
compile(node.rhs());
as.movq(reg("ax"), reg("cx"));
loadWords(node.type(), node.lhs().address(), "ax");
compileBinaryOp(node.operator(), node.type());
saveWords(node.type(), "ax", node.lhs().address());
}
public void visit(ArefNode node) {
if (node.expr().type().isPointerAlike()) {
compile(node.expr());
as.pushq(reg("ax"));
}
else {
compileLHS(node.expr());
as.pushq(reg(PTRREG));
}
compile(node.index());
as.imulq(imm(node.type().size()), reg("ax"));
as.popq(reg(PTRREG));
as.addq(reg("ax"), reg(PTRREG));
loadWords(node.type(), addr(PTRREG), "ax");
}
public void visit(MemberNode node) {
compileLHS(node.expr());
loadWords(node.type(), addr2(node.offset(), PTRREG), "ax");
}
public void visit(PtrMemberNode node) {
compileLHS(node.expr());
loadWords(node.type(), addr(PTRREG), PTRREG);
loadWords(node.type(), addr2(node.offset(), PTRREG), "ax");
}
public void visit(DereferenceNode node) {
compile(node.expr());
loadWords(node.type(), addr("ax"), "ax");
}
public void visit(AddressNode node) {
compileLHS(node.expr());
as.movq(reg(PTRREG), reg("ax"));
}
static final String PTRREG = "bx";
protected void compileLHS(Node node) {
as.comment("compileLHS: " + node.getClass().getName() + " {");
if (node instanceof VariableNode) {
// FIXME: support static variables
VariableNode n = (VariableNode)node;
as.leaq(n.address(), reg(PTRREG));
}
else if (node instanceof ArefNode) {
ArefNode n = (ArefNode)node;
as.pushq(reg("ax"));
compile(n.index());
as.imulq(imm(n.type().size()), reg("ax"));
as.pushq(reg("ax"));
if (n.expr().type().isPointerAlike()) {
compile(n.expr());
as.movq(reg("ax"), reg(PTRREG));
}
else {
compileLHS(n.expr());
}
as.popq(reg("cx"));
as.addq(reg("cx"), reg(PTRREG));
as.popq(reg("ax"));
}
else if (node instanceof MemberNode) {
MemberNode n = (MemberNode)node;
compileLHS(n.expr());
as.addq(imm(n.offset()), reg(PTRREG));
}
else if (node instanceof DereferenceNode) {
DereferenceNode n = (DereferenceNode)node;
as.pushq(reg("ax"));
compile(n.expr());
as.movq(reg("ax"), reg(PTRREG));
as.popq(reg("ax"));
}
else if (node instanceof PtrMemberNode) {
PtrMemberNode n = (PtrMemberNode)node;
as.pushq(reg("ax"));
compile(n.expr());
as.addq(imm(n.offset()), reg("ax"));
as.movq(reg("ax"), reg(PTRREG));
as.popq(reg("ax"));
}
else if (node instanceof PrefixOpNode) {
PrefixOpNode n = (PrefixOpNode)node;
compileLHS(n.expr());
if (n.operator().equals("++")) {
as.addq(imm(n.expr().type().size()), addr(PTRREG));
as.addq(imm(n.expr().type().size()), reg(PTRREG));
}
else {
as.subq(imm(n.expr().type().size()), addr(PTRREG));
as.subq(imm(n.expr().type().size()), reg(PTRREG));
}
}
else if (node instanceof SuffixOpNode) {
SuffixOpNode n = (SuffixOpNode)node;
compileLHS(n.expr());
if (n.operator().equals("++")) {
as.addq(imm(n.expr().type().size()), reg(PTRREG));
}
else {
as.subq(imm(n.expr().type().size()), reg(PTRREG));
}
}
else if (node instanceof CastNode) {
CastNode n = (CastNode)node;
compileLHS(n.expr());
// FIXME: cast here
}
else {
throw new Error("wrong type for compileLHS: " + node.getClass().getName());
}
as.comment("compileLHS: }");
}
/*
* x86 assembly DSL
*/
protected Register bp() { return reg("bp"); }
protected Register sp() { return reg("sp"); }
protected Register al() { return new Register(1, "ax"); }
protected Register cl() { return new Register(1, "cx"); }
protected Register reg(String name, Type type) {
return Register.forType(type, name);
}
protected Register reg(String name) {
return Register.widestRegister(name);
}
protected SimpleAddress addr(String regname) {
return new SimpleAddress(Register.widestRegister(regname));
}
protected CompositeAddress addr2(long offset, String regname) {
return new CompositeAddress(offset, Register.widestRegister(regname));
}
protected ImmediateValue imm(long n) {
return new ImmediateValue(n);
}
protected Reference imm(Label label) {
return new Reference(label);
}
protected void loadWords(Type type, AsmEntity addr, String reg) {
switch ((int)type.size()) {
case 1:
if (type.isSigned()) { // signed char
as.movsbl(addr, reg(reg));
} else { // unsigned char
as.movzbl(addr, reg(reg));
}
break;
case 2:
if (type.isSigned()) { // signed short
as.movswl(addr, reg(reg));
} else { // unsigned short
as.movzwl(addr, reg(reg));
}
break;
default: // int, long, long_long
as.mov(type, addr, reg(reg, type));
break;
}
}
protected void saveWords(Type type, String reg, AsmEntity addr) {
as.mov(type, reg(reg, type), addr);
}
}