/* * 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.runtime; import org.mmtk.harness.lang.Env; import org.mmtk.harness.lang.Trace; import org.mmtk.harness.lang.Trace.Item; import org.mmtk.harness.lang.compiler.CompiledMethod; import org.mmtk.harness.lang.pcode.CallNormalOp; import org.mmtk.harness.lang.pcode.PseudoOp; import org.mmtk.harness.lang.pcode.ReturnOp; public final class PcodeInterpreter { /** The environment (stack and associated structures) for this thread. */ private final Env env; /** The code for the current method */ private PseudoOp[] code; /** Program counter (index into the <code>code</code> array) */ private int pc = 0; /** nesting level for method calls. Return from level 0 exits the thread */ private int nesting = 0; /** * Create a pcode interpreter for a given environment/method pair. This will * in general be a thread of execution within a script, either main() or a spawned * process. * @param env * @param method */ public PcodeInterpreter(Env env, CompiledMethod method) { this.env = env; code = method.getCodeArray(); pushFrame(method); } /** * Execute the method, with given values for its parameters. * @param params */ public void exec(Value...params) { setActualParams(params); while (true) { PseudoOp op = code[pc++]; try { Trace.trace(Item.EVAL,"%-4d %4d: %s",nesting,pc,op); op.exec(env); if (op.affectsControlFlow()) { env.gcSafePoint(); if (op.isBranch()) { /* * Branch */ if (op.isTaken(env)) { pc = op.getBranchTarget(); } } else { if (op.isCall()) { methodCall((CallNormalOp)op); } else if (op.isReturn()) { if (nesting == 0) { break; } methodReturn((ReturnOp)op); } } } } catch (RuntimeException e) { System.err.printf("Runtime exception encountered at line %d, column %d%n", op.getLine(),op.getColumn()); stackTrace(op); throw e; } } } /** * Return from a method call * @param retOp The return pseudo-op */ private void methodReturn(ReturnOp retOp) { StackFrame calleeFrame = env.top(); env.pop(); StackFrame callerFrame = env.top(); if (retOp.hasOperand()) { callerFrame.setResult(retOp.getOperand(calleeFrame)); } nesting--; pc = callerFrame.getSavedPc(); code = callerFrame.getSavedMethod(); } /** * Perform a method call * @param callOp The call pseudo-op */ private void methodCall(CallNormalOp callOp) { CompiledMethod callee = callOp.getMethod(); StackFrame callerFrame = env.top(); saveContext(callOp, callerFrame); pushFrame(callee); setActualParams(callOp.getOperandValues(callerFrame)); code = callee.getCodeArray(); pc = 0; nesting++; } /** * Push a new stack frame for a given method * @param callee */ private void pushFrame(CompiledMethod callee) { env.push(callee.formatStackFrame()); } /** * Save context in the caller's stack frame * @param callOp * @param frame */ private void saveContext(PseudoOp callOp, StackFrame frame) { frame.savePc(pc); frame.saveMethod(code); if (callOp.hasResult()) { frame.setResultSlot(callOp.getResult()); } else { frame.clearResultSlot(); } } /** * Initialize the actual parameters in a method call. Assumes that * the callee stack frame has already been pushed on the stack. * @param actuals */ private void setActualParams(Value[] actuals) { StackFrame calleeFrame = env.top(); for (int i=0; i < actuals.length; i++) { calleeFrame.set(i,actuals[i]); } } /** * Print a (script) stack trace * @param op The current op at top of stack. */ private void stackTrace(PseudoOp op) { saveContext(op,env.top()); for (StackFrame frame : env.stack()) { op = frame.getSavedMethod()[frame.getSavedPc()-1]; System.err.println(op.getSourceLocation("at ")); } } }