/*
* This file is part of the Jikes RVM project (http://jikesrvm.org).
*
* This file is licensed to You under the Common Public License (CPL);
* 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/cpl1.0.php
*
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership.
*/
package org.jikesrvm.compilers.opt.ir;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import org.jikesrvm.ArchitectureSpecific.OPT_RegisterPool;
import org.jikesrvm.classloader.VM_Method;
import org.jikesrvm.classloader.VM_NormalMethod;
import org.jikesrvm.classloader.VM_Type;
import org.jikesrvm.classloader.VM_TypeReference;
import org.jikesrvm.compilers.baseline.VM_BranchProfile;
import org.jikesrvm.compilers.baseline.VM_BranchProfiles;
import org.jikesrvm.compilers.baseline.VM_ConditionalBranchProfile;
import org.jikesrvm.compilers.baseline.VM_EdgeCounts;
import org.jikesrvm.compilers.baseline.VM_SwitchBranchProfile;
import org.jikesrvm.compilers.common.VM_CompiledMethod;
import org.jikesrvm.compilers.opt.OPT_ClassLoaderProxy;
import org.jikesrvm.compilers.opt.OPT_InlineOracle;
import org.jikesrvm.compilers.opt.OPT_OptimizingCompilerException;
import org.jikesrvm.compilers.opt.OPT_Options;
import org.jikesrvm.runtime.VM_Entrypoints;
import org.jikesrvm.runtime.VM_Statics;
import org.vmmagic.unboxed.Offset;
/**
* Defines the context in which BC2IR will abstractly interpret
* a method's bytecodes and populate targetIR with instructions.
*
**/
public final class OPT_GenerationContext implements org.jikesrvm.compilers.opt.OPT_Constants, OPT_Operators {
//////////
// These fields are used to communicate information from its
// caller to OPT_BC2IR
//////////
/**
* The original method (root of the calling context tree)
*/
VM_NormalMethod original_method;
/**
* The compiled method assigned for this compilation of original_method
*/
VM_CompiledMethod original_cm;
/**
* The method to be generated
*/
VM_NormalMethod method;
/**
* The BranchProfile data for method, if available
*/
VM_BranchProfiles branchProfiles;
/**
* The options to control the generation
*/
public OPT_Options options;
/**
* The CFG object into which instructions should be generated.
*/
OPT_ControlFlowGraph cfg;
/**
* The register pool to be used during generation
*/
public OPT_RegisterPool temps;
/**
* The parameters which BC2IR should use to seed the local state
* for the entry basic block.
*/
OPT_Operand[] arguments;
/**
* The basic block into which BC2IR's caller will generate a "prologue."
* BC2IR will add a CFG edge from prologue to the block containing the
* instructions generated for bytecode 0, but it is its caller's
* responsibility to populate the prologue with instructions.
* All blocks generated by BC2IR will be injected by BC2IR.doFinalPass
* immediately
* after prologue in the code ordering
* (ie prologue can assume it will fallthrough
* to the first basic block in the ir generated for method.
*/
OPT_BasicBlock prologue;
/**
* The basic block into which BC2IR's caller will generate an epilogue.
* BC2IR will add CFG edges to this node, but it is its caller's
* responsibility to populate it with instructions.
* NOTE: After IR is generated one of two conditions will hold:
* <ul>
* <li> epilogue == cfg.lastInCodeOrder(): (if it is to be inlined,
* then the generated cfg
* is expecting to "fallthrough"
* to the next bblock)
* <li> epilogue == null: implies that there is no "normal" exit from
* the callee (all exits via throw)
* </ul>
* NOTE: BC2IR assumes that epilogue is a single basic block
* (ie it has no out edges)
*/
OPT_BasicBlock epilogue;
/**
* The exit node of the outermost CFG
* (only used by BC2IR for not-definitely caught athrows)
*/
OPT_BasicBlock exit;
/**
* A catch, unlock, and rethrow exception handler used for
* synchronized methods.
*/
OPT_BasicBlock unlockAndRethrow;
/**
* The OPT_Register to which BC2IR should assign the return value(s)
* of the method. It will be null when the method has a void return.
*/
OPT_Register resultReg;
/**
* The enclosing exception handlers (null if there are none).
*/
OPT_ExceptionHandlerBasicBlockBag enclosingHandlers;
/**
* Inlining context of the method to be generated
*/
public OPT_InlineSequence inlineSequence;
/**
* The OPT_InlineOracle to be consulted for all inlining decisions during
* the generation of this IR.
*/
OPT_InlineOracle inlinePlan;
//////////
// These fields are used to communicate information from BC2IR to its caller
//////////
/**
* Did BC2IR generate a reachable exception handler while generating
* the IR for this method
*/
boolean generatedExceptionHandlers;
/**
* Did BC2IR encounter a magic that requires us to allocate a stack frame?
*/
public boolean allocFrame;
/**
* Used to communicate the meet of the return values back to the caller
* Mainly useful when BC2IR is doing inlining....allows the caller
* BC2IR object
* to exploit knowledge the callee BC2IR object had about the result.
*/
OPT_Operand result;
//////////
// Main public methods
/////////
/**
* Use this constructor to create an outermost (non-inlined)
* OPT_GenerationContext.
*
* @param meth The VM_NormalMethod whose IR will be generated
* @param params The known types of the parameters to the method. For method specialization.
* @param cm The compiled method id to be used for this compilation
* @param opts The OPT_Options to be used for the generation
* @param ip The OPT_InlineOracle to be used for the generation
*/
OPT_GenerationContext(VM_NormalMethod meth, VM_TypeReference[] params, VM_CompiledMethod cm, OPT_Options opts, OPT_InlineOracle ip) {
original_method = meth;
original_cm = cm;
method = meth;
if (opts.frequencyCounters() || opts.inverseFrequencyCounters()) {
branchProfiles = VM_EdgeCounts.getBranchProfiles(meth);
}
options = opts;
inlinePlan = ip;
inlineSequence = new OPT_InlineSequence(meth);
// Create the CFG. Initially contains prologue, epilogue, and exit.
cfg = new OPT_ControlFlowGraph(0);
prologue = new OPT_BasicBlock(PROLOGUE_BLOCK_BCI, inlineSequence, cfg);
epilogue = new OPT_BasicBlock(EPILOGUE_BLOCK_BCI, inlineSequence, cfg);
cfg.addLastInCodeOrder(prologue);
cfg.addLastInCodeOrder(epilogue);
exit = cfg.exit();
epilogue.insertOut(exit);
// Create register pool, initialize arguments, resultReg.
temps = new OPT_RegisterPool(meth);
_ncGuards = new HashMap<OPT_Register, OPT_RegisterOperand>();
initLocalPool();
VM_TypeReference[] definedParams = meth.getParameterTypes();
if (params == null) params = definedParams;
int numParams = params.length;
int argIdx = 0;
int localNum = 0;
arguments = new OPT_Operand[method.isStatic() ? numParams : numParams + 1];
// Insert IR_PROLOGUE instruction. Loop below will fill in its operands
OPT_Instruction prologueInstr = Prologue.create(IR_PROLOGUE, arguments.length);
appendInstruction(prologue, prologueInstr, PROLOGUE_BCI);
if (!method.isStatic()) {
VM_TypeReference thisType = meth.getDeclaringClass().getTypeRef();
OPT_RegisterOperand thisOp = makeLocal(localNum, thisType);
// The this param of a virtual method is by definition non null
OPT_RegisterOperand guard = makeNullCheckGuard(thisOp.getRegister());
OPT_BC2IR.setGuard(thisOp, guard);
appendInstruction(prologue, Move.create(GUARD_MOVE, guard.copyRO(), new OPT_TrueGuardOperand()), PROLOGUE_BCI);
thisOp.setDeclaredType();
thisOp.setExtant();
arguments[0] = thisOp;
Prologue.setFormal(prologueInstr, 0, thisOp.copyU2D());
argIdx++;
localNum++;
}
for (int paramIdx = 0; paramIdx < numParams; paramIdx++) {
VM_TypeReference argType = params[paramIdx];
OPT_RegisterOperand argOp = makeLocal(localNum, argType);
argOp.setDeclaredType();
if (argType.isClassType()) {
argOp.setExtant();
}
arguments[argIdx] = argOp;
Prologue.setFormal(prologueInstr, argIdx, argOp.copyU2D());
argIdx++;
localNum++;
if (argType.isLongType() || argType.isDoubleType()) {
localNum++; // longs & doubles take two words of local space
}
}
VM_TypeReference returnType = meth.getReturnType();
if (returnType != VM_TypeReference.Void) {
resultReg = temps.makeTemp(returnType).getRegister();
}
enclosingHandlers = null;
completePrologue(true);
completeEpilogue(true);
completeExceptionHandlers(true);
}
/**
* Create a child generation context from parent & callerBB to
* generate IR for callsite.
* Make this 'static' to avoid confusing parent/child fields.
*
* @param parent the parent gc
* @param ebag the enclosing exception handlers (null if none)
* @param callee the callee method to be inlined
* (may _not_ be equal to Call.getMethod(callSite).method)
* @param callSite the Call instruction to be inlined.
* @return the child context
*/
static OPT_GenerationContext createChildContext(OPT_GenerationContext parent, OPT_ExceptionHandlerBasicBlockBag ebag,
VM_NormalMethod callee, OPT_Instruction callSite) {
OPT_GenerationContext child = new OPT_GenerationContext();
child.method = callee;
if (parent.options.frequencyCounters() || parent.options.inverseFrequencyCounters()) {
child.branchProfiles = VM_EdgeCounts.getBranchProfiles(callee);
}
child.original_method = parent.original_method;
child.original_cm = parent.original_cm;
// Some state gets directly copied to the child
child.options = parent.options;
child.temps = parent.temps;
child._ncGuards = parent._ncGuards;
child.exit = parent.exit;
child.inlinePlan = parent.inlinePlan;
// Now inherit state based on callSite
child.inlineSequence = new OPT_InlineSequence(child.method, callSite.position, callSite);
child.enclosingHandlers = ebag;
child.arguments = new OPT_Operand[Call.getNumberOfParams(callSite)];
for (int i = 0; i < child.arguments.length; i++) {
child.arguments[i] = Call.getParam(callSite, i).copy(); // copy instead
// of clearing in case inlining aborts.
}
if (Call.hasResult(callSite)) {
child.resultReg = Call.getResult(callSite).copyD2D().getRegister();
child.resultReg.setSpansBasicBlock(); // it will...
}
// Initialize the child CFG, prologue, and epilogue blocks
child.cfg = new OPT_ControlFlowGraph(parent.cfg.numberOfNodes());
child.prologue = new OPT_BasicBlock(PROLOGUE_BCI, child.inlineSequence, child.cfg);
child.prologue.exceptionHandlers = ebag;
child.epilogue = new OPT_BasicBlock(EPILOGUE_BCI, child.inlineSequence, child.cfg);
child.epilogue.exceptionHandlers = ebag;
child.cfg.addLastInCodeOrder(child.prologue);
child.cfg.addLastInCodeOrder(child.epilogue);
// Set up the local pool
child.initLocalPool();
// Insert moves from child.arguments to child's locals in prologue
VM_TypeReference[] params = child.method.getParameterTypes();
int numParams = params.length;
int argIdx = 0;
int localNum = 0;
if (!child.method.isStatic()) {
OPT_Operand receiver = child.arguments[argIdx++];
OPT_RegisterOperand local = null;
if (receiver.isRegister()) {
OPT_RegisterOperand objPtr = receiver.asRegister();
if (OPT_ClassLoaderProxy.includesType(child.method.getDeclaringClass().getTypeRef(), objPtr.getType()) != YES) {
// narrow type of actual to match formal static type implied by method
objPtr.setType(child.method.getDeclaringClass().getTypeRef());
objPtr.clearPreciseType(); // Can be precise but not assignable if enough classes aren't loaded
objPtr.setDeclaredType();
}
local = child.makeLocal(localNum++, objPtr);
child.arguments[0] = local; // Avoid confusion in BC2IR of callee
// when objPtr is a local in the caller.
} else if (receiver.isConstant()) {
local = child.makeLocal(localNum++, receiver.getType());
local.setPreciseType();
// Constants trivially non-null
OPT_RegisterOperand guard = child.makeNullCheckGuard(local.getRegister());
OPT_BC2IR.setGuard(local, guard);
child.prologue.appendInstruction(Move.create(GUARD_MOVE, guard.copyRO(), new OPT_TrueGuardOperand()));
} else {
OPT_OptimizingCompilerException.UNREACHABLE("Unexpected receiver operand");
}
OPT_Instruction s = Move.create(REF_MOVE, local, receiver);
s.bcIndex = PROLOGUE_BCI;
s.position = callSite.position;
child.prologue.appendInstruction(s);
}
for (int paramIdx = 0; paramIdx < numParams; paramIdx++, argIdx++) {
VM_TypeReference argType = params[paramIdx];
OPT_RegisterOperand formal;
OPT_Operand actual = child.arguments[argIdx];
if (actual.isRegister()) {
OPT_RegisterOperand rActual = actual.asRegister();
if (OPT_ClassLoaderProxy.includesType(argType, rActual.getType()) != YES) {
// narrow type of actual to match formal static type implied by method
rActual.clearPreciseType(); // Can be precise but not
// assignable if enough classes aren't loaded
rActual.setDeclaredType();
rActual.setType(argType);
}
formal = child.makeLocal(localNum++, rActual);
child.arguments[argIdx] = formal; // Avoid confusion in BC2IR of
// callee when arg is a local in the caller.
} else {
formal = child.makeLocal(localNum++, argType);
}
OPT_Instruction s = Move.create(OPT_IRTools.getMoveOp(argType), formal, actual);
s.bcIndex = PROLOGUE_BCI;
s.position = callSite.position;
child.prologue.appendInstruction(s);
if (argType.isLongType() || argType.isDoubleType()) {
localNum++; // longs and doubles take two local words
}
}
child.completePrologue(false);
child.completeEpilogue(false);
child.completeExceptionHandlers(false);
return child;
}
/**
* Only for internal use by OPT_Inliner (when inlining multiple targets)
* This is probably not the prettiest way to handle this, but it requires
* no changes to BC2IR's & OPT_Inliner's high level control logic.
*
* @param parent the parent gc
* @param ebag the enclosing exception handlers (null if none)
* @return the synthetic context
*/
static OPT_GenerationContext createSynthetic(OPT_GenerationContext parent, OPT_ExceptionHandlerBasicBlockBag ebag) {
// Create the CFG. Initially contains prologue and epilogue
OPT_GenerationContext child = new OPT_GenerationContext();
child.cfg = new OPT_ControlFlowGraph(-100000);
// It may be wrong to use the parent inline sequence as the
// position here, but it seems to work out. This is a synthetic
// context that is just used as a container for multiple inlined
// targets, so in the cases that I've observed where the prologue
// and epilogue don't disappear, it was correct to have the
// parent's position. -- Matt
child.prologue = new OPT_BasicBlock(PROLOGUE_BCI, parent.inlineSequence, parent.cfg);
child.prologue.exceptionHandlers = ebag;
child.epilogue = new OPT_BasicBlock(EPILOGUE_BCI, parent.inlineSequence, parent.cfg);
child.epilogue.exceptionHandlers = ebag;
child.cfg.addLastInCodeOrder(child.prologue);
child.cfg.addLastInCodeOrder(child.epilogue);
// All other fields are intentionally left null.
// We are only really using this context to transfer a synthetic CFG
// from the low-level OPT_Inliner.execute back to its caller.
// TODO: Rewrite OPT_GenerationContext to be a subclass of a root
// class that is just a CFG wrapper. Then, have an instance of this
// new parent
// class be the return value for the main entrypoints in OPT_Inliner
// and create an instance of the root class instead of OPT_GC when
// inlining multiple targets.
return child;
}
/**
* Use this to transfer state back from a child context back to its parent.
*
* @param parent the parent context that will receive the state
* @param child the child context from which the state will be taken
*/
public static void transferState(OPT_GenerationContext parent, OPT_GenerationContext child) {
parent.cfg.setNumberOfNodes(child.cfg.numberOfNodes());
if (child.generatedExceptionHandlers) {
parent.generatedExceptionHandlers = true;
}
if (child.allocFrame) {
parent.allocFrame = true;
}
}
///////////
// Local variables
///////////
// The registers to use for various types of locals.
// Note that "int" really means 32-bit gpr.
private OPT_Register[] intLocals;
private OPT_Register[] addressLocals;
private OPT_Register[] floatLocals;
private OPT_Register[] longLocals;
private OPT_Register[] doubleLocals;
private void initLocalPool() {
int numLocals = method.getLocalWords();
intLocals = new OPT_Register[numLocals];
addressLocals = new OPT_Register[numLocals];
floatLocals = new OPT_Register[numLocals];
longLocals = new OPT_Register[numLocals];
doubleLocals = new OPT_Register[numLocals];
}
private OPT_Register[] getPool(VM_TypeReference type) {
if (type == VM_TypeReference.Float) {
return floatLocals;
} else if (type == VM_TypeReference.Long) {
return longLocals;
} else if (type == VM_TypeReference.Double) {
return doubleLocals;
} else if (type.isReferenceType() || type.isWordType()) {
return addressLocals;
} else {
return intLocals;
}
}
/**
* Return the OPT_Register used to for local i of VM_TypeReference type
*/
public OPT_Register localReg(int i, VM_TypeReference type) {
OPT_Register[] pool = getPool(type);
if (pool[i] == null) {
pool[i] = temps.getReg(type);
pool[i].setLocal();
}
return pool[i];
}
/**
* Should null checks be generated?
*/
boolean noNullChecks() {
return options.NO_NULL_CHECK || method.hasNoNullCheckAnnotation();
}
/**
* Should bounds checks be generated?
*/
boolean noBoundsChecks() {
return options.NO_BOUNDS_CHECK || method.hasNoBoundsCheckAnnotation();
}
/**
* Make a register operand that refers to the given local variable number
* and has the given type.
*
* @param i local variable number
* @param type desired data type
*/
public OPT_RegisterOperand makeLocal(int i, VM_TypeReference type) {
return new OPT_RegisterOperand(localReg(i, type), type);
}
/**
* Make a register operand that refers to the given local variable number,
* and inherits its properties (type, flags) from props
*
* @param i local variable number
* @param props OPT_RegisterOperand to inherit flags from
*/
OPT_RegisterOperand makeLocal(int i, OPT_RegisterOperand props) {
OPT_RegisterOperand local = makeLocal(i, props.getType());
local.setInheritableFlags(props);
OPT_BC2IR.setGuard(local, OPT_BC2IR.getGuard(props));
return local;
}
/**
* Get the local number for a given register
*/
public int getLocalNumberFor(OPT_Register reg, VM_TypeReference type) {
OPT_Register[] pool = getPool(type);
for (int i = 0; i < pool.length; i++) {
if (pool[i] == reg) return i;
}
return -1;
}
/**
* Is the operand a particular bytecode local?
*/
public boolean isLocal(OPT_Operand op, int i, VM_TypeReference type) {
if (op instanceof OPT_RegisterOperand) {
if (getPool(type)[i] == ((OPT_RegisterOperand) op).getRegister()) return true;
}
return false;
}
///////////
// Validation operands (guards)
///////////
// For each register, we always use the same register as a validation operand.
// This helps us avoid needlessly losing information at CFG join points.
private HashMap<OPT_Register, OPT_RegisterOperand> _ncGuards;
/**
* Make a register operand to use as a null check guard for the
* given register.
*/
OPT_RegisterOperand makeNullCheckGuard(OPT_Register ref) {
OPT_RegisterOperand guard = _ncGuards.get(ref);
if (guard == null) {
guard = temps.makeTempValidation();
_ncGuards.put(ref, guard.copyRO());
} else {
guard = guard.copyRO();
}
return guard;
}
///////////
// Profile data
///////////
public OPT_BranchProfileOperand getConditionalBranchProfileOperand(int bcIndex, boolean backwards) {
float prob;
if (branchProfiles != null) {
VM_BranchProfile bp = branchProfiles.getEntry(bcIndex);
prob = ((VM_ConditionalBranchProfile) bp).getTakenProbability();
} else if (backwards) {
prob = 0.9f;
} else {
prob = 0.5f;
}
// experimental option: flip the probablity to see how bad things would be if
// we were completely wrong.
if (options.inverseFrequencyCounters()) {
prob = 1f - prob;
}
return new OPT_BranchProfileOperand(prob);
}
public VM_SwitchBranchProfile getSwitchProfile(int bcIndex) {
if (branchProfiles != null) {
VM_BranchProfile bp = branchProfiles.getEntry(bcIndex);
return (VM_SwitchBranchProfile) bp;
} else {
return null;
}
}
///////////
// Implementation
///////////
/**
* for internal use only (in createInlinedContext)
*/
private OPT_GenerationContext() {}
/**
* Fill in the rest of the method prologue.
* PRECONDITION: arguments & temps have been setup/initialized.
*/
private void completePrologue(boolean isOutermost) {
// Deal with Uninteruptible code.
if (!isOutermost && requiresUnintMarker()) {
OPT_Instruction s = Empty.create(UNINT_BEGIN);
appendInstruction(prologue, s, PROLOGUE_BCI);
}
// Deal with implicit monitorenter for synchronized methods.
// When working with the class writer do not expand static
// synchronization headers as there is no easy way to get at
// class object
// OSR: if this is a specialized method, no monitor enter at the beginging
// since it's the second time reenter
if (method.isForOsrSpecialization()) {
// do nothing
} else if (method.isSynchronized() && !options.MONITOR_NOP && !options.INVOKEE_THREAD_LOCAL) {
OPT_Operand lockObject = getLockObject();
OPT_Instruction s = MonitorOp.create(MONITORENTER, lockObject, new OPT_TrueGuardOperand());
appendInstruction(prologue, s, SYNCHRONIZED_MONITORENTER_BCI);
}
}
/**
* Fill in the rest of the method epilogue.
* PRECONDITION: arguments & temps have been setup/initialized.
*/
private void completeEpilogue(boolean isOutermost) {
// Deal with implicit monitorexit for synchronized methods.
if (method.isSynchronized() && !options.MONITOR_NOP && !options.INVOKEE_THREAD_LOCAL) {
OPT_Operand lockObject = getLockObject();
OPT_Instruction s = MonitorOp.create(MONITOREXIT, lockObject, new OPT_TrueGuardOperand());
appendInstruction(epilogue, s, SYNCHRONIZED_MONITOREXIT_BCI);
}
// Deal with Uninteruptible code.
if (!isOutermost && requiresUnintMarker()) {
OPT_Instruction s = Empty.create(UNINT_END);
appendInstruction(epilogue, s, EPILOGUE_BCI);
}
if (isOutermost) {
VM_TypeReference returnType = method.getReturnType();
OPT_Operand retVal = returnType.isVoidType() ? null : new OPT_RegisterOperand(resultReg, returnType);
OPT_Instruction s = Return.create(RETURN, retVal);
appendInstruction(epilogue, s, EPILOGUE_BCI);
}
}
/**
* If the method is synchronized then we wrap it in a
* synthetic exception handler that unlocks & rethrows
* PRECONDITION: cfg, arguments & temps have been setup/initialized.
*/
private void completeExceptionHandlers(boolean isOutermost) {
if (method.isSynchronized() && !options.MONITOR_NOP) {
OPT_ExceptionHandlerBasicBlock rethrow =
new OPT_ExceptionHandlerBasicBlock(SYNTH_CATCH_BCI,
inlineSequence,
new OPT_TypeOperand(VM_Type.JavaLangThrowableType),
cfg);
rethrow.exceptionHandlers = enclosingHandlers;
OPT_RegisterOperand ceo = temps.makeTemp(VM_TypeReference.JavaLangThrowable);
OPT_Instruction s = Nullary.create(GET_CAUGHT_EXCEPTION, ceo);
appendInstruction(rethrow, s, SYNTH_CATCH_BCI);
OPT_Operand lockObject = getLockObject();
VM_Method target = VM_Entrypoints.unlockAndThrowMethod;
OPT_MethodOperand methodOp = OPT_MethodOperand.STATIC(target);
methodOp.setIsNonReturningCall(true); // Used to keep cfg correct
s =
Call.create2(CALL,
null,
new OPT_AddressConstantOperand(target.getOffset()),
methodOp,
lockObject,
ceo.copyD2U());
appendInstruction(rethrow, s, RUNTIME_SERVICES_BCI);
cfg.insertBeforeInCodeOrder(epilogue, rethrow);
// May be overly conservative
// (if enclosed by another catch of Throwable...)
if (enclosingHandlers != null) {
for (OPT_BasicBlockEnumeration e = enclosingHandlers.enumerator(); e.hasMoreElements();) {
OPT_BasicBlock eh = e.next();
rethrow.insertOut(eh);
}
}
rethrow.setCanThrowExceptions();
rethrow.setMayThrowUncaughtException();
rethrow.insertOut(exit);
// save a reference to this block so we can discard it if unused.
unlockAndRethrow = rethrow;
OPT_ExceptionHandlerBasicBlock[] sh = new OPT_ExceptionHandlerBasicBlock[1];
sh[0] = rethrow;
enclosingHandlers = new OPT_ExceptionHandlerBasicBlockBag(sh, enclosingHandlers);
generatedExceptionHandlers = true;
}
}
/**
* Get the object for locking for synchronized methods.
* either the class object or the this ptr.
*/
private OPT_Operand getLockObject() {
if (method.isStatic()) {
Class<?> klass = method.getDeclaringClass().getClassForType();
Offset offs = Offset.fromIntSignExtend(VM_Statics.findOrCreateObjectLiteral(klass));
return new OPT_ClassConstantOperand(klass, offs);
} else {
return makeLocal(0, arguments[0].getType());
}
}
private void appendInstruction(OPT_BasicBlock b, OPT_Instruction s, int bcIndex) {
s.position = inlineSequence;
s.bcIndex = bcIndex;
b.appendInstruction(s);
}
private boolean requiresUnintMarker() {
if (method.isInterruptible()) return false;
// supress redundant markers by detecting when we're inlining
// one Uninterruptible method into another one.
for (OPT_InlineSequence p = inlineSequence.getCaller(); p != null; p = p.getCaller()) {
if (!p.getMethod().isInterruptible()) return false;
}
return true;
}
/**
* Make sure, the gc is still in sync with the IR, even if we applied some
* optimizations. This method should be called before hir2lir conversions
* which might trigger inlining.
*/
public void resync() {
//make sure the _ncGuards contain no dangling mappings
resync_ncGuards();
}
/**
* This method makes sure that _ncGuard only maps to registers that
* are actually in the IRs register pool.
*/
private void resync_ncGuards() {
HashSet<OPT_Register> regPool = new HashSet<OPT_Register>();
for (OPT_Register r = temps.getFirstSymbolicRegister(); r != null; r = r.next) {
regPool.add(r);
}
Iterator<Map.Entry<OPT_Register, OPT_RegisterOperand>> i = _ncGuards.entrySet().iterator();
while (i.hasNext()) {
Map.Entry<OPT_Register, OPT_RegisterOperand> entry = i.next();
if (!(regPool.contains(entry.getValue()))) i.remove();
}
}
/**
* Kill ncGuards, so we do not use outdated mappings unintendedly later on
*/
public void close() {
_ncGuards = null;
}
}