/*
* This file is part of the Jikes RVM project (http://jikesrvm.org).
*
* This file is licensed to You under the Eclipse Public License (EPL);
* You may not use this file except in compliance with the License. You
* may obtain a copy of the License at
*
* http://www.opensource.org/licenses/eclipse-1.0.php
*
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership.
*/
package org.mmtk.harness.lang;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.mmtk.harness.Harness;
import org.mmtk.harness.lang.Trace.Item;
import org.mmtk.harness.lang.ast.*;
import org.mmtk.harness.lang.compiler.CompiledMethod;
import org.mmtk.harness.lang.compiler.CompiledMethodProxy;
import org.mmtk.harness.lang.compiler.CompiledMethodTable;
import org.mmtk.harness.lang.compiler.Register;
import org.mmtk.harness.lang.compiler.Temporary;
import org.mmtk.harness.lang.parser.MethodTable;
import org.mmtk.harness.lang.pcode.*;
import org.mmtk.harness.lang.runtime.ConstantPool;
import org.mmtk.harness.lang.type.Field;
import org.mmtk.harness.lang.type.Type;
import org.mmtk.harness.lang.type.UserType;
/**
* Compile a script to pcode.
*/
public final class Compiler extends Visitor {
static {
//Trace.enable(Item.COMPILER);
//Trace.enable(Item.EVAL);
}
private final CompiledMethod current;
private final CompiledMethodTable methodTable;
private final Temporary temps;
private Compiler(NormalMethod method, CompiledMethodTable table) {
this.current = new CompiledMethod(method);
this.methodTable = table;
this.temps = new Temporary(method.getDecls().size());
}
public static CompiledMethod compile(MethodTable methods) {
CompiledMethodTable table = new CompiledMethodTable();
for (NormalMethod m : methods.normalMethods()) {
Compiler compiler = new Compiler(m,table);
m.accept(compiler);
table.put(compiler.yield());
}
for (CompiledMethod cm : table) {
cm.resolveMethodReferences();
if (Harness.dumpPcode.getValue()) {
System.out.println(cm.getName()+"\n"+cm.toString());
}
}
return table.get("main");
}
/*
* Utility methods
*/
public CompiledMethod yield() {
current.setTemps(temps.size());
return current;
}
private void emit(PseudoOp op) {
Trace.trace(Item.COMPILER, "emitting %s", op);
current.append(op);
}
private int currentPc() {
return current.currentIndex();
}
private void freeTemps(List<Register> actuals) {
for (Register r : actuals) {
temps.release(r);
}
}
private List<Register> compileArgList(List<Expression> args) {
ArrayList<Register> actuals = new ArrayList<Register>(args.size());
for (Expression exp : args) {
actuals.add(compile(exp));
}
return actuals;
}
private CompiledMethod compiledMethodFor(Method method) {
CompiledMethod compiledMethod = methodTable.get(method.getName());
if (compiledMethod == null) {
compiledMethod = new CompiledMethodProxy((NormalMethod)method,methodTable);
}
return compiledMethod;
}
/**
* Visit a node and return its result as a Register
* @param ast
* @return
*/
private Register compile(AST ast) {
return (Register)ast.accept(this);
}
@Override
public Object visit(Alloc alloc) {
Register result = temps.acquire();
Register doubleAlign = ConstantPool.FALSE;
if (alloc.isTyped()) {
UserType type = alloc.getType();
if (alloc.numArgs() >= 2) {
doubleAlign = compile(alloc.getArg(1));
}
emit(new AllocUserOp(alloc,result,type,doubleAlign,alloc.getSite()));
temps.release(doubleAlign);
} else {
Register refCount = compile(alloc.getArg(0));
Register dataCount = compile(alloc.getArg(1));
if (alloc.numArgs() >= 3) {
doubleAlign = compile(alloc.getArg(2));
}
emit(new AllocOp(alloc,result,dataCount,refCount,doubleAlign,alloc.getSite()));
temps.release(dataCount,refCount,doubleAlign);
}
return result;
}
@Override
public Object visit(Assert ass) {
Register predicate = compile(ass.getPredicate());
Branch branch = new Branch(ass,predicate,true);
emit(branch);
temps.release(predicate);
ArrayList<Register> actuals = new ArrayList<Register>(ass.getOutputs().size());
for (Expression expr : ass.getOutputs()) {
actuals.add(compile(expr));
}
emit(new PrintOp(ass,actuals.toArray(new Register[0])));
freeTemps(actuals);
emit(new ExitOp(ass,ConstantPool.ONE));
branch.setBranchTarget(currentPc());
return null;
}
@Override
public Object visit(Assignment a) {
Trace.trace(Item.COMPILER, "Compiling %s", PrettyPrinter.format(a));
Register rhs = compile(a.getRhs());
emit(new StoreLocal(a,Register.createLocal(a.getSlot()),rhs));
temps.release(rhs);
return null;
}
@Override
public Object visit(BinaryExpression exp) {
Register lhs = compile(exp.getLhs());
Register rhs = compile(exp.getRhs());
Register result = temps.acquire();
emit(new BinaryOperation(exp,result,lhs,rhs,exp.getOperator()));
temps.release(rhs,lhs);
return result;
}
@Override
public Object visit(Call call) {
Method method = call.getMethod();
List<Register> actuals = compileArgList(call.getParams());
Register returnVal = method.getReturnType() == Type.VOID ?
Register.NULL : temps.acquire();
if (method instanceof IntrinsicMethod) {
emit(new CallIntrinsicOp(call,returnVal,(IntrinsicMethod)method,actuals));
} else if (method instanceof NormalMethod) {
CompiledMethod compiledMethod = compiledMethodFor(method);
emit(new CallNormalOp(call,returnVal,compiledMethod,actuals));
} else {
throw new RuntimeException("Unknown method class "+method.getClass().getCanonicalName());
}
freeTemps(actuals);
return returnVal;
}
/**
* Constant value. push an operand which fetches from the
* global constant pool
*/
@Override
public Object visit(Constant c) {
return ConstantPool.acquire(c.value);
}
@Override
public Object visit(Empty e) {
return null;
}
@Override
public Object visit(Expect exp) {
emit(new ExpectOp(exp,exp.getExpected()));
return null;
}
@Override
public Object visit(IfStatement conditional) {
Iterator<Statement> stmtIter = conditional.getStmts().iterator();
Branch branch = new Branch(conditional,Register.NULL,false);
Goto gotoExit = new Goto(conditional);
for (Expression cond : conditional.getConds()) {
branch.setBranchTarget(currentPc());
Register conditionReg = compile(cond);
branch = new Branch(conditional,conditionReg,false);
temps.release(conditionReg);
emit(branch);
stmtIter.next().accept(this);
emit(gotoExit);
}
branch.setBranchTarget(currentPc());
if (stmtIter.hasNext()) {
stmtIter.next().accept(this);
}
gotoExit.setBranchTarget(currentPc());
return null;
}
@Override
public Object visit(IntrinsicMethod method) {
throw new RuntimeException("You can't compile an intrinsic method!!!");
}
@Override
public Object visit(LoadField load) {
Register index = compile(load.getIndex());
Register object = Register.createLocal(load.getSlot());
Register result = temps.acquire();
emit(new LoadFieldOp(load,result,object,index,load.getFieldType()));
temps.release(index);
return result;
}
@Override
public Object visit(LoadNamedField load) {
UserType type = (UserType)load.getObjectSymbol().getType();
Field field = type.getField(load.getFieldName());
Register object = Register.createLocal(load.getSlot());
Register result = temps.acquire();
emit(new LoadFixedFieldOp(load,result,object,field.getOffset(),
load.getFieldName(),
field.getType().isObject() ? Type.OBJECT : Type.INT));
return result;
}
@Override
public Object visit(NormalMethod method) {
compile(method.getBody());
emit(new ReturnOp(method));
return null;
}
@Override
public Object visit(PrintStatement print) {
List<Expression> args = print.getArgs();
List<Register> actuals = compileArgList(args);
emit(new PrintOp(print,actuals));
freeTemps(actuals);
return null;
}
@Override
public Object visit(Return ret) {
if (ret.hasReturnValue()) {
emit(new ReturnOp(ret,compile(ret.getRhs())));
} else {
emit(new ReturnOp(ret));
}
return null;
}
@Override
public Object visit(Sequence ass) {
for (Statement stmt : ass) {
compile(stmt);
}
return null;
}
@Override
public Object visit(Spawn sp) {
List<Register> actuals = compileArgList(sp.getArgs());
emit(new SpawnOp(sp,compiledMethodFor(sp.getMethod()),actuals));
freeTemps(actuals);
return null;
}
@Override
public Object visit(StoreField store) {
Register index = compile(store.getIndex());
Register value = compile(store.getRhs());
Register object = Register.createLocal(store.getSlot());
emit(new StoreFieldOp(store,object,index,value,store.getFieldType()));
temps.release(index,value);
return null;
}
@Override
public Object visit(StoreNamedField store) {
Field field = store.getField();
Register value = compile(store.getRhs());
Register object = Register.createLocal(store.getSlot());
emit(new StoreFixedFieldOp(store,object,field.getOffset(),store.getFieldName(),value,
field.getType().isObject() ? Type.OBJECT : Type.INT));
temps.release(value);
return null;
}
@Override
public Object visit(TypeLiteral type) {
throw new UnsupportedOperationException();
}
@Override
public Object visit(UnaryExpression exp) {
Register operand = compile(exp.getOperand());
Register result = temps.acquire();
emit(new UnaryOperation(exp,result,operand,exp.getOperator()));
temps.release(operand);
return result;
}
/**
* Variable reference: push an operand which will fetch
* the appropriate stack frame slot.
*/
@Override
public Object visit(Variable var) {
return Register.createLocal(var.getSlot());
}
/**
* While statement. Compiled to:
* top: condition
* if false goto b
* body
* goto top:
* b:
*/
@Override
public Object visit(WhileStatement w) {
int top = currentPc();
Register cond = compile(w.getCond());
Branch branchToExit = new Branch(w,cond,false);
temps.release(cond);
emit(branchToExit);
compile(w.getBody());
emit(new Goto(w,top));
branchToExit.setBranchTarget(currentPc());
return null;
}
}