/*
* 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.jikesrvm.compilers.opt.bc2ir;
import static org.jikesrvm.classloader.BytecodeConstants.*;
import static org.jikesrvm.classloader.ClassLoaderConstants.*;
import static org.jikesrvm.compilers.opt.OptimizingCompilerException.opt_assert;
import static org.jikesrvm.compilers.opt.bc2ir.IRGenOptions.*;
import static org.jikesrvm.compilers.opt.driver.OptConstants.NO;
import static org.jikesrvm.compilers.opt.driver.OptConstants.RUNTIME_SERVICES_BCI;
import static org.jikesrvm.compilers.opt.driver.OptConstants.YES;
import static org.jikesrvm.compilers.opt.ir.Operators.*;
import static org.jikesrvm.osr.OSRConstants.*;
import java.util.ArrayList;
import java.util.Enumeration;
import org.jikesrvm.VM;
import org.jikesrvm.adaptive.controller.Controller;
import org.jikesrvm.classloader.BytecodeStream;
import org.jikesrvm.classloader.FieldReference;
import org.jikesrvm.classloader.MethodReference;
import org.jikesrvm.classloader.RVMClass;
import org.jikesrvm.classloader.RVMField;
import org.jikesrvm.classloader.RVMMethod;
import org.jikesrvm.classloader.RVMType;
import org.jikesrvm.classloader.TypeReference;
import org.jikesrvm.compilers.baseline.SwitchBranchProfile;
import org.jikesrvm.compilers.common.CompiledMethod;
import org.jikesrvm.compilers.common.CompiledMethods;
import org.jikesrvm.compilers.opt.ClassLoaderProxy;
import org.jikesrvm.compilers.opt.FieldAnalysis;
import org.jikesrvm.compilers.opt.OptimizingCompilerException;
import org.jikesrvm.compilers.opt.Simplifier;
import org.jikesrvm.compilers.opt.StaticFieldReader;
import org.jikesrvm.compilers.opt.driver.OptimizingCompiler;
import org.jikesrvm.compilers.opt.inlining.CompilationState;
import org.jikesrvm.compilers.opt.inlining.InlineDecision;
import org.jikesrvm.compilers.opt.inlining.InlineSequence;
import org.jikesrvm.compilers.opt.inlining.Inliner;
import org.jikesrvm.compilers.opt.ir.ALoad;
import org.jikesrvm.compilers.opt.ir.AStore;
import org.jikesrvm.compilers.opt.ir.Athrow;
import org.jikesrvm.compilers.opt.ir.BasicBlock;
import org.jikesrvm.compilers.opt.ir.Binary;
import org.jikesrvm.compilers.opt.ir.BoundsCheck;
import org.jikesrvm.compilers.opt.ir.CacheOp;
import org.jikesrvm.compilers.opt.ir.Call;
import org.jikesrvm.compilers.opt.ir.Empty;
import org.jikesrvm.compilers.opt.ir.ExceptionHandlerBasicBlock;
import org.jikesrvm.compilers.opt.ir.GetField;
import org.jikesrvm.compilers.opt.ir.GetStatic;
import org.jikesrvm.compilers.opt.ir.Goto;
import org.jikesrvm.compilers.opt.ir.GuardedBinary;
import org.jikesrvm.compilers.opt.ir.GuardedUnary;
import org.jikesrvm.compilers.opt.ir.IRTools;
import org.jikesrvm.compilers.opt.ir.IfCmp;
import org.jikesrvm.compilers.opt.ir.InstanceOf;
import org.jikesrvm.compilers.opt.ir.Instruction;
import org.jikesrvm.compilers.opt.ir.LookupSwitch;
import org.jikesrvm.compilers.opt.ir.MonitorOp;
import org.jikesrvm.compilers.opt.ir.Move;
import org.jikesrvm.compilers.opt.ir.Multianewarray;
import org.jikesrvm.compilers.opt.ir.New;
import org.jikesrvm.compilers.opt.ir.NewArray;
import org.jikesrvm.compilers.opt.ir.NullCheck;
import org.jikesrvm.compilers.opt.ir.Operator;
import org.jikesrvm.compilers.opt.ir.OsrBarrier;
import org.jikesrvm.compilers.opt.ir.OsrPoint;
import org.jikesrvm.compilers.opt.ir.PutField;
import org.jikesrvm.compilers.opt.ir.PutStatic;
import org.jikesrvm.compilers.opt.ir.Register;
import org.jikesrvm.compilers.opt.ir.ResultCarrier;
import org.jikesrvm.compilers.opt.ir.StoreCheck;
import org.jikesrvm.compilers.opt.ir.TableSwitch;
import org.jikesrvm.compilers.opt.ir.Trap;
import org.jikesrvm.compilers.opt.ir.TypeCheck;
import org.jikesrvm.compilers.opt.ir.Unary;
import org.jikesrvm.compilers.opt.ir.ZeroCheck;
import org.jikesrvm.compilers.opt.ir.operand.AddressConstantOperand;
import org.jikesrvm.compilers.opt.ir.operand.BranchOperand;
import org.jikesrvm.compilers.opt.ir.operand.BranchProfileOperand;
import org.jikesrvm.compilers.opt.ir.operand.ConditionOperand;
import org.jikesrvm.compilers.opt.ir.operand.ConstantOperand;
import org.jikesrvm.compilers.opt.ir.operand.DoubleConstantOperand;
import org.jikesrvm.compilers.opt.ir.operand.FloatConstantOperand;
import org.jikesrvm.compilers.opt.ir.operand.IntConstantOperand;
import org.jikesrvm.compilers.opt.ir.operand.LocationOperand;
import org.jikesrvm.compilers.opt.ir.operand.LongConstantOperand;
import org.jikesrvm.compilers.opt.ir.operand.MethodOperand;
import org.jikesrvm.compilers.opt.ir.operand.NullConstantOperand;
import org.jikesrvm.compilers.opt.ir.operand.Operand;
import org.jikesrvm.compilers.opt.ir.operand.OsrTypeInfoOperand;
import org.jikesrvm.compilers.opt.ir.operand.RegisterOperand;
import org.jikesrvm.compilers.opt.ir.operand.TrapCodeOperand;
import org.jikesrvm.compilers.opt.ir.operand.TrueGuardOperand;
import org.jikesrvm.compilers.opt.ir.operand.TypeOperand;
import org.jikesrvm.osr.ObjectHolder;
import org.jikesrvm.osr.bytecodes.InvokeStatic;
import org.jikesrvm.runtime.Entrypoints;
import org.jikesrvm.runtime.Magic;
import org.vmmagic.pragma.NoInline;
import org.vmmagic.unboxed.Address;
import org.vmmagic.unboxed.Offset;
/**
* This class translates from bytecode to HIR.
* <p>
* The only public entry point is BC2IR.generateHIR.
* generateHIR is passed an argument GenerationContext.
* The context is assumed to be "empty" but "initialized." Invoking
* generateHIR on a context results in it being "filled in" with the HIR
* for the method (and for any inlined methods) as specified by the
* state of the context.
* <p>
* The basic idea is to abstractly interpret the bytecode stream,
* translating it into a register-based IR along the way. At each program
* point BC2IR has an abstract stack and an abstract local variable array.
* Based on this, and on the bytecode, it can generate instructions.
* It also does a number of forward flow-sensitive dataflow analyses and
* optimistic optimizations in the process. There's lots of details in
* John Whaley's master thesis from MIT. However, one needs to be careful
* because this code has substantial diverged from the system described in
* his thesis.
* Some optimizations/features described in Johns's thesis are not implemented
* here. Some optimizations/features implemented here are not described
* in John's thesis.
* In particular this code takes a different approach to JSRs (inlining them),
* and has more advanced and effective implementation of the inlining
* transformation.
*
*
* @see IRGenOptions
* @see GenerationContext
* @see ConvertBCtoHIR
*/
public final class BC2IR {
/**
* Dummy slot.
* Used to deal with the fact the longs/doubles take
* two words of stack space/local space to represent.
* This field needs to be accessed by several of the IR classes,
* but is not intended to be referenced by general client code.
*/
public static final DummyStackSlot DUMMY = new DummyStackSlot();
/**
* Generate HIR as specified by the argument GenerationContext.
* As a result of calling this method, the cfg field of the generation
* context is populated with basic blocks and instructions.
* Additionally, other fields of the generation context will be modified
* to summarize what happened during IR generation.
* <p>
* This is the only external entry point to BC2IR.
* <p>
* Note: most clients should be calling methods in
* ConvertBCtoHIR or in Inliner rather than invoking
* BC2IR.generateHIR directly.
*
* @param context the generation context
*/
public static void generateHIR(GenerationContext context) {
new BC2IR(context).generateHIR();
}
//////////////////////////////////////////
// vvv Implementation details below vvv //
//////////////////////////////////////////
/**
* The generation context.
*/
private GenerationContext gc;
/**
* Bytecodes for the method being generated.
*/
private BytecodeStream bcodes;
// Fields to support generation of instructions/blocks
/**
* The set of BasicBlockLEs we are generating
*/
private BBSet blocks;
/**
* Bytecode index of current instruction.
*/
private int instrIndex;
// OSR field
private boolean osrGuardedInline = false;
/**
* OSR field: TODO rework this mechanism!
* adjustment of bcIndex of instructions because of
* specialized bytecode.
*/
private int bciAdjustment;
/**
* Last instruction generated (for ELIM_COPY_LOCALS)
*/
private Instruction lastInstr;
/**
* Does basic block end here?
*/
private boolean endOfBasicBlock;
/**
* Do we fall through to the next basic block?
*/
private boolean fallThrough;
/**
* Current BBLE.
*/
private BasicBlockLE currentBBLE;
/**
* Current simulated stack state.
*/
private OperandStack stack;
/**
* Current state of local variables.
*/
private Operand[] _localState;
/**
* Index of next basic block.
*/
private int runoff;
private Operand currentGuard;
/**
* Was something inlined?
*/
private boolean inlinedSomething;
/**
* OSR: used for PSEUDO_InvokeStatic to recover the type info
*/
private int param1, param2;
/**
* osr barrier needs type information of locals and stacks,
* it has to be created before a _callHelper.
* only when the call site is going to be inlined, the instruction
* is inserted before the call site.
*/
private Instruction lastOsrBarrier = null;
/**
* Debugging with method_to_print. Switch following 2
* to both be non-final. Set {@link #DBG_SELECTIVE} to true.
* {@link #DBG_SELECTED} will then be {@code true} when the method matches.
* You must also uncomment the assignment to DBG_SELECTIVE in
* {@link #start(GenerationContext)}.
*/
private static final boolean DBG_SELECTIVE = false;
static final boolean DBG_SELECTED = false;
//////////
// End of field declarations
//////////
/**
* Construct the BC2IR object for the generation context.
* After the constructor completes, we're ready to start generating
* HIR from bytecode 0 of context.method.
*
* @param context the context to generate HIR into
*/
private BC2IR(GenerationContext context) {
start(context);
for (int argIdx = 0, localIdx = 0; argIdx < context.getArguments().length;) {
TypeReference argType = context.getArguments()[argIdx].getType();
_localState[localIdx++] = context.getArguments()[argIdx++];
if (argType.isLongType() || argType.isDoubleType()) {
_localState[localIdx++] = DUMMY;
}
}
finish(context);
}
@NoInline
private void start(GenerationContext context) {
gc = context;
// To use the following you need to change the declarations
// above the constructor
if (DBG_SELECTIVE) {
VM.sysWrite("Whoops! you need to uncomment the assignment to DBG_SELECTED");
// DBG_SELECTED = gc.methodIsSelectedForDebuggingWithMethodToPrint();
}
if (context.getMethod().isForOsrSpecialization()) {
bcodes = context.getMethod().getOsrSynthesizedBytecodes();
} else {
bcodes = context.getMethod().getBytecodes();
}
// initialize the local state from context.arguments
_localState = new Operand[context.getMethod().getLocalWords()];
if (context.getMethod().isForOsrSpecialization()) {
this.bciAdjustment = context.getMethod().getOsrPrologueLength();
} else {
this.bciAdjustment = 0;
}
this.osrGuardedInline = VM.runningVM &&
context.getOptions().OSR_GUARDED_INLINING &&
!context.getMethod().isForOsrSpecialization() &&
OptimizingCompiler.getAppStarted() &&
(Controller.options != null) &&
Controller.options.ENABLE_RECOMPILATION;
}
private void finish(GenerationContext context) {
// Initialize simulated stack.
stack = new OperandStack(context.getMethod().getOperandWords());
// Initialize BBSet.
blocks = new BBSet(context, bcodes, _localState);
// Finish preparing to generate from bytecode 0
currentBBLE = blocks.getEntry();
gc.getPrologue().insertOut(currentBBLE.block);
if (DBG_CFG || DBG_SELECTED) {
db("Added CFG edge from " + gc.getPrologue() + " to " + currentBBLE.block);
}
runoff = currentBBLE.max;
}
/**
* Main generation loop.
*/
private void generateHIR() {
// Constructor initialized generation state to start
// generating from bytecode 0, so get the ball rolling.
if (DBG_BB || DBG_SELECTED) db("bbl: " + printBlocks());
generateFrom(0);
// While there are more blocks that need it, pick one and generate it.
for (currentBBLE = blocks.getNextEmptyBlock(currentBBLE); currentBBLE != null; currentBBLE =
blocks.getNextEmptyBlock(currentBBLE)) {
// Found a block. Set the generation state appropriately.
currentBBLE.clearSelfRegen();
runoff = Math.min(blocks.getNextBlockBytecodeIndex(currentBBLE), currentBBLE.max);
if (currentBBLE.stackState == null) {
stack.clear();
} else {
stack = currentBBLE.stackState.deepCopy();
}
_localState = currentBBLE.copyLocalState();
if (DBG_BB || DBG_SELECTED) db("bbl: " + printBlocks());
// Generate it!
generateFrom(currentBBLE.low);
}
// Construct initial code order, commit to recursive inlines,
// insert any synthetic blocks.
if (DBG_BB || DBG_SELECTED) db("doing final pass over basic blocks: " + printBlocks());
blocks.finalPass(inlinedSomething);
}
// pops the length off the stack
//
public Instruction generateAnewarray(TypeReference arrayTypeRef, TypeReference elementTypeRef) {
if (arrayTypeRef == null) {
if (VM.VerifyAssertions) opt_assert(elementTypeRef != null);
arrayTypeRef = elementTypeRef.getArrayTypeForElementType();
}
if (elementTypeRef == null) {
elementTypeRef = arrayTypeRef.getArrayElementType();
}
RegisterOperand t = gc.getTemps().makeTemp(arrayTypeRef);
t.setPreciseType();
markGuardlessNonNull(t);
// We can do early resolution of the array type if the element type
// is already initialized.
RVMType arrayType = arrayTypeRef.peekType();
Operator op;
TypeOperand arrayOp;
if ((arrayType != null) && (arrayType.isInitialized() || arrayType.isInBootImage())) {
op = NEWARRAY;
arrayOp = makeTypeOperand(arrayType);
t.setExtant();
} else {
RVMType elementType = elementTypeRef.peekType();
if ((elementType != null) && (elementType.isInitialized() || elementType.isInBootImage())) {
arrayType = arrayTypeRef.resolve();
arrayType.prepareForFirstUse();
op = NEWARRAY;
arrayOp = makeTypeOperand(arrayType);
t.setExtant();
} else {
op = NEWARRAY_UNRESOLVED;
arrayOp = makeTypeOperand(arrayTypeRef);
}
}
Instruction s = NewArray.create(op, t, arrayOp, popInt());
push(t.copyD2U());
rectifyStateWithErrorHandler();
rectifyStateWithExceptionHandler(TypeReference.JavaLangNegativeArraySizeException);
return s;
}
/**
* Generate instructions for a basic block.
* May discover other basic blocks that need to be generated along the way.
*
* @param fromIndex bytecode index to start from
*/
private void generateFrom(int fromIndex) {
if (DBG_BB || DBG_SELECTED) {
db("generating code into " + currentBBLE + " with runoff " + runoff);
}
currentBBLE.setGenerated();
endOfBasicBlock = fallThrough = false;
lastInstr = null;
bcodes.reset(fromIndex);
while (true) {
// Must keep currentBBLE.high up-to-date in case we try to jump into
// the middle of the block we're currently generating. Simply updating
// high once endsBasicBlock is true doesn't enable us to catch this case.
currentBBLE.high = instrIndex = bcodes.index();
int code = bcodes.nextInstruction();
if (DBG_BCPARSE) {
db("parsing " +
instrIndex +
" " +
code +
" : 0x" +
Integer.toHexString(code) +
" " +
JBC_name(code));
}
Instruction s = null;
lastOsrBarrier = null;
switch (code) {
case JBC_nop:
break;
case JBC_aconst_null:
push(new NullConstantOperand());
break;
case JBC_iconst_m1:
case JBC_iconst_0:
case JBC_iconst_1:
case JBC_iconst_2:
case JBC_iconst_3:
case JBC_iconst_4:
case JBC_iconst_5:
push(new IntConstantOperand(code - JBC_iconst_0));
break;
case JBC_lconst_0:
case JBC_lconst_1:
pushDual(new LongConstantOperand(code - JBC_lconst_0));
break;
case JBC_fconst_0:
push(new FloatConstantOperand(0.f));
break;
case JBC_fconst_1:
push(new FloatConstantOperand(1.f));
break;
case JBC_fconst_2:
push(new FloatConstantOperand(2.f));
break;
case JBC_dconst_0:
pushDual(new DoubleConstantOperand(0.));
break;
case JBC_dconst_1:
pushDual(new DoubleConstantOperand(1.));
break;
case JBC_bipush:
push(new IntConstantOperand(bcodes.getByteValue()));
break;
case JBC_sipush:
push(new IntConstantOperand(bcodes.getShortValue()));
break;
case JBC_ldc:
push(getConstantOperand(bcodes.getConstantIndex()));
break;
case JBC_ldc_w:
push(getConstantOperand(bcodes.getWideConstantIndex()));
break;
case JBC_ldc2_w:
pushDual(getConstantOperand(bcodes.getWideConstantIndex()));
break;
case JBC_iload:
s = do_iload(bcodes.getLocalNumber());
break;
case JBC_lload:
s = do_lload(bcodes.getLocalNumber());
break;
case JBC_fload:
s = do_fload(bcodes.getLocalNumber());
break;
case JBC_dload:
s = do_dload(bcodes.getLocalNumber());
break;
case JBC_aload:
s = do_aload(bcodes.getLocalNumber());
break;
case JBC_iload_0:
case JBC_iload_1:
case JBC_iload_2:
case JBC_iload_3:
s = do_iload(code - JBC_iload_0);
break;
case JBC_lload_0:
case JBC_lload_1:
case JBC_lload_2:
case JBC_lload_3:
s = do_lload(code - JBC_lload_0);
break;
case JBC_fload_0:
case JBC_fload_1:
case JBC_fload_2:
case JBC_fload_3:
s = do_fload(code - JBC_fload_0);
break;
case JBC_dload_0:
case JBC_dload_1:
case JBC_dload_2:
case JBC_dload_3:
s = do_dload(code - JBC_dload_0);
break;
case JBC_aload_0:
case JBC_aload_1:
case JBC_aload_2:
case JBC_aload_3:
s = do_aload(code - JBC_aload_0);
break;
case JBC_iaload: {
Operand index = popInt();
Operand ref = pop();
clearCurrentGuard();
if (do_NullCheck(ref) || do_BoundsCheck(ref, index)) {
break;
}
if (VM.VerifyAssertions) {
assertIsType(ref, TypeReference.IntArray);
}
s = _aloadHelper(INT_ALOAD, ref, index, TypeReference.Int);
}
break;
case JBC_laload: {
Operand index = popInt();
Operand ref = pop();
clearCurrentGuard();
if (do_NullCheck(ref) || do_BoundsCheck(ref, index)) {
break;
}
if (VM.VerifyAssertions) {
assertIsType(ref, TypeReference.LongArray);
}
s = _aloadHelper(LONG_ALOAD, ref, index, TypeReference.Long);
}
break;
case JBC_faload: {
Operand index = popInt();
Operand ref = pop();
clearCurrentGuard();
if (do_NullCheck(ref) || do_BoundsCheck(ref, index)) {
break;
}
if (VM.VerifyAssertions) {
assertIsType(ref, TypeReference.FloatArray);
}
s = _aloadHelper(FLOAT_ALOAD, ref, index, TypeReference.Float);
}
break;
case JBC_daload: {
Operand index = popInt();
Operand ref = pop();
clearCurrentGuard();
if (do_NullCheck(ref) || do_BoundsCheck(ref, index)) {
break;
}
if (VM.VerifyAssertions) {
assertIsType(ref, TypeReference.DoubleArray);
}
s = _aloadHelper(DOUBLE_ALOAD, ref, index, TypeReference.Double);
}
break;
case JBC_aaload: {
Operand index = popInt();
Operand ref = pop();
clearCurrentGuard();
if (do_NullCheck(ref) || do_BoundsCheck(ref, index)) {
break;
}
TypeReference type = getRefTypeOf(ref).getArrayElementType();
if (VM.VerifyAssertions) opt_assert(type.isReferenceType());
s = _aloadHelper(REF_ALOAD, ref, index, type);
}
break;
case JBC_baload: {
Operand index = popInt();
Operand ref = pop();
clearCurrentGuard();
if (do_NullCheck(ref) || do_BoundsCheck(ref, index)) {
break;
}
TypeReference type = getArrayTypeOf(ref);
if (VM.VerifyAssertions) {
opt_assert(type == TypeReference.ByteArray || type == TypeReference.BooleanArray);
}
if (type == TypeReference.ByteArray) {
s = _aloadHelper(BYTE_ALOAD, ref, index, TypeReference.Byte);
} else {
s = _aloadHelper(UBYTE_ALOAD, ref, index, TypeReference.Boolean);
}
}
break;
case JBC_caload: {
Operand index = popInt();
Operand ref = pop();
clearCurrentGuard();
if (do_NullCheck(ref) || do_BoundsCheck(ref, index)) {
break;
}
if (VM.VerifyAssertions) {
assertIsType(ref, TypeReference.CharArray);
}
s = _aloadHelper(USHORT_ALOAD, ref, index, TypeReference.Char);
}
break;
case JBC_saload: {
Operand index = popInt();
Operand ref = pop();
clearCurrentGuard();
if (do_NullCheck(ref) || do_BoundsCheck(ref, index)) {
break;
}
if (VM.VerifyAssertions) {
assertIsType(ref, TypeReference.ShortArray);
}
s = _aloadHelper(SHORT_ALOAD, ref, index, TypeReference.Short);
}
break;
case JBC_istore:
s = do_store(bcodes.getLocalNumber(), popInt());
break;
case JBC_lstore:
s = do_store(bcodes.getLocalNumber(), popLong());
break;
case JBC_fstore:
s = do_store(bcodes.getLocalNumber(), popFloat());
break;
case JBC_dstore:
s = do_store(bcodes.getLocalNumber(), popDouble());
break;
case JBC_astore:
s = do_astore(bcodes.getLocalNumber());
break;
case JBC_istore_0:
case JBC_istore_1:
case JBC_istore_2:
case JBC_istore_3:
s = do_store(code - JBC_istore_0, popInt());
break;
case JBC_lstore_0:
case JBC_lstore_1:
case JBC_lstore_2:
case JBC_lstore_3:
s = do_store(code - JBC_lstore_0, popLong());
break;
case JBC_fstore_0:
case JBC_fstore_1:
case JBC_fstore_2:
case JBC_fstore_3:
s = do_store(code - JBC_fstore_0, popFloat());
break;
case JBC_dstore_0:
case JBC_dstore_1:
case JBC_dstore_2:
case JBC_dstore_3:
s = do_store(code - JBC_dstore_0, popDouble());
break;
case JBC_astore_0:
case JBC_astore_1:
case JBC_astore_2:
case JBC_astore_3:
s = do_astore(code - JBC_astore_0);
break;
case JBC_iastore: {
Operand val = popInt();
Operand index = popInt();
Operand ref = pop();
clearCurrentGuard();
if (do_NullCheck(ref) || do_BoundsCheck(ref, index)) {
break;
}
if (VM.VerifyAssertions) {
assertIsType(ref, TypeReference.IntArray);
}
s =
AStore.create(INT_ASTORE,
val,
ref,
index,
new LocationOperand(TypeReference.Int),
getCurrentGuard());
}
break;
case JBC_lastore: {
Operand val = popLong();
Operand index = popInt();
Operand ref = pop();
clearCurrentGuard();
if (do_NullCheck(ref) || do_BoundsCheck(ref, index)) {
break;
}
if (VM.VerifyAssertions) {
assertIsType(ref, TypeReference.LongArray);
}
s =
AStore.create(LONG_ASTORE,
val,
ref,
index,
new LocationOperand(TypeReference.Long),
getCurrentGuard());
}
break;
case JBC_fastore: {
Operand val = popFloat();
Operand index = popInt();
Operand ref = pop();
clearCurrentGuard();
if (do_NullCheck(ref) || do_BoundsCheck(ref, index)) {
break;
}
if (VM.VerifyAssertions) {
assertIsType(ref, TypeReference.FloatArray);
}
s =
AStore.create(FLOAT_ASTORE,
val,
ref,
index,
new LocationOperand(TypeReference.Float),
getCurrentGuard());
}
break;
case JBC_dastore: {
Operand val = popDouble();
Operand index = popInt();
Operand ref = pop();
clearCurrentGuard();
if (do_NullCheck(ref) || do_BoundsCheck(ref, index)) {
break;
}
if (VM.VerifyAssertions) {
assertIsType(ref, TypeReference.DoubleArray);
}
s =
AStore.create(DOUBLE_ASTORE,
val,
ref,
index,
new LocationOperand(TypeReference.Double),
getCurrentGuard());
}
break;
case JBC_aastore: {
Operand val = pop();
Operand index = popInt();
Operand ref = pop();
clearCurrentGuard();
if (do_NullCheck(ref) || do_BoundsCheck(ref, index)) {
break;
}
TypeReference type = getRefTypeOf(ref).getArrayElementType();
if (VM.VerifyAssertions) opt_assert(type.isReferenceType());
if (do_CheckStore(ref, val, type)) {
break;
}
s = AStore.create(REF_ASTORE, val, ref, index, new LocationOperand(type), getCurrentGuard());
}
break;
case JBC_bastore: {
Operand val = popInt();
Operand index = popInt();
Operand ref = pop();
clearCurrentGuard();
if (do_NullCheck(ref) || do_BoundsCheck(ref, index)) {
break;
}
TypeReference type = getArrayTypeOf(ref);
if (VM.VerifyAssertions) {
opt_assert(type == TypeReference.ByteArray || type == TypeReference.BooleanArray);
}
if (type == TypeReference.ByteArray) {
type = TypeReference.Byte;
} else {
type = TypeReference.Boolean;
}
s = AStore.create(BYTE_ASTORE, val, ref, index, new LocationOperand(type), getCurrentGuard());
}
break;
case JBC_castore: {
Operand val = popInt();
Operand index = popInt();
Operand ref = pop();
clearCurrentGuard();
if (do_NullCheck(ref) || do_BoundsCheck(ref, index)) {
break;
}
if (VM.VerifyAssertions) {
assertIsType(ref, TypeReference.CharArray);
}
s =
AStore.create(SHORT_ASTORE,
val,
ref,
index,
new LocationOperand(TypeReference.Char),
getCurrentGuard());
}
break;
case JBC_sastore: {
Operand val = popInt();
Operand index = popInt();
Operand ref = pop();
clearCurrentGuard();
if (do_NullCheck(ref) || do_BoundsCheck(ref, index)) {
break;
}
if (VM.VerifyAssertions) {
assertIsType(ref, TypeReference.ShortArray);
}
s =
AStore.create(SHORT_ASTORE,
val,
ref,
index,
new LocationOperand(TypeReference.Short),
getCurrentGuard());
}
break;
case JBC_pop:
stack.pop();
break;
case JBC_pop2:
stack.pop2();
break;
case JBC_dup: {
Operand op1 = stack.pop();
stack.push(op1);
s = pushCopy(op1);
}
break;
case JBC_dup_x1: {
Operand op1 = stack.pop();
Operand op2 = stack.pop();
stack.push(op1);
stack.push(op2);
s = pushCopy(op1);
}
break;
case JBC_dup_x2: {
Operand op1 = stack.pop();
Operand op2 = stack.pop();
Operand op3 = stack.pop();
stack.push(op1);
stack.push(op3);
stack.push(op2);
s = pushCopy(op1);
}
break;
case JBC_dup2: {
Operand op1 = stack.pop();
Operand op2 = stack.pop();
stack.push(op2);
stack.push(op1);
s = pushCopy(op2);
if (s != null) {
appendInstruction(s);
s = null;
}
s = pushCopy(op1);
}
break;
case JBC_dup2_x1: {
Operand op1 = stack.pop();
Operand op2 = stack.pop();
Operand op3 = stack.pop();
stack.push(op2);
stack.push(op1);
stack.push(op3);
s = pushCopy(op2);
if (s != null) {
appendInstruction(s);
s = null;
}
s = pushCopy(op1);
}
break;
case JBC_dup2_x2: {
Operand op1 = stack.pop();
Operand op2 = stack.pop();
Operand op3 = stack.pop();
Operand op4 = stack.pop();
stack.push(op2);
stack.push(op1);
stack.push(op4);
stack.push(op3);
s = pushCopy(op2);
if (s != null) {
appendInstruction(s);
s = null;
}
s = pushCopy(op1);
}
break;
case JBC_swap: {
stack.swap();
}
break;
case JBC_iadd: {
Operand op2 = popInt();
Operand op1 = popInt();
s = _binaryHelper(INT_ADD, op1, op2, TypeReference.Int);
}
break;
case JBC_ladd: {
Operand op2 = popLong();
Operand op1 = popLong();
s = _binaryDualHelper(LONG_ADD, op1, op2, TypeReference.Long);
}
break;
case JBC_fadd: {
Operand op2 = popFloat();
Operand op1 = popFloat();
s = _binaryHelper(FLOAT_ADD, op1, op2, TypeReference.Float);
}
break;
case JBC_dadd: {
Operand op2 = popDouble();
Operand op1 = popDouble();
s = _binaryDualHelper(DOUBLE_ADD, op1, op2, TypeReference.Double);
}
break;
case JBC_isub: {
Operand op2 = popInt();
Operand op1 = popInt();
s = _binaryHelper(INT_SUB, op1, op2, TypeReference.Int);
}
break;
case JBC_lsub: {
Operand op2 = popLong();
Operand op1 = popLong();
s = _binaryDualHelper(LONG_SUB, op1, op2, TypeReference.Long);
}
break;
case JBC_fsub: {
Operand op2 = popFloat();
Operand op1 = popFloat();
s = _binaryHelper(FLOAT_SUB, op1, op2, TypeReference.Float);
}
break;
case JBC_dsub: {
Operand op2 = popDouble();
Operand op1 = popDouble();
s = _binaryDualHelper(DOUBLE_SUB, op1, op2, TypeReference.Double);
}
break;
case JBC_imul: {
Operand op2 = popInt();
Operand op1 = popInt();
s = _binaryHelper(INT_MUL, op1, op2, TypeReference.Int);
}
break;
case JBC_lmul: {
Operand op2 = popLong();
Operand op1 = popLong();
s = _binaryDualHelper(LONG_MUL, op1, op2, TypeReference.Long);
}
break;
case JBC_fmul: {
Operand op2 = popFloat();
Operand op1 = popFloat();
s = _binaryHelper(FLOAT_MUL, op1, op2, TypeReference.Float);
}
break;
case JBC_dmul: {
Operand op2 = popDouble();
Operand op1 = popDouble();
s = _binaryDualHelper(DOUBLE_MUL, op1, op2, TypeReference.Double);
}
break;
case JBC_idiv: {
clearCurrentGuard();
Operand op2 = popInt();
Operand op1 = popInt();
if (do_IntZeroCheck(op2)) {
break;
}
s = _guardedBinaryHelper(INT_DIV, op1, op2, getCurrentGuard(), TypeReference.Int);
}
break;
case JBC_ldiv: {
clearCurrentGuard();
Operand op2 = popLong();
Operand op1 = popLong();
if (do_LongZeroCheck(op2)) {
break;
}
s = _guardedBinaryDualHelper(LONG_DIV, op1, op2, getCurrentGuard(), TypeReference.Long);
}
break;
case JBC_fdiv: {
Operand op2 = popFloat();
Operand op1 = popFloat();
s = _binaryHelper(FLOAT_DIV, op1, op2, TypeReference.Float);
}
break;
case JBC_ddiv: {
Operand op2 = popDouble();
Operand op1 = popDouble();
s = _binaryDualHelper(DOUBLE_DIV, op1, op2, TypeReference.Double);
}
break;
case JBC_irem: {
clearCurrentGuard();
Operand op2 = popInt();
Operand op1 = popInt();
if (do_IntZeroCheck(op2)) {
break;
}
s = _guardedBinaryHelper(INT_REM, op1, op2, getCurrentGuard(), TypeReference.Int);
}
break;
case JBC_lrem: {
clearCurrentGuard();
Operand op2 = popLong();
Operand op1 = popLong();
if (do_LongZeroCheck(op2)) {
break;
}
s = _guardedBinaryDualHelper(LONG_REM, op1, op2, getCurrentGuard(), TypeReference.Long);
}
break;
case JBC_frem: {
Operand op2 = popFloat();
Operand op1 = popFloat();
s = _binaryHelper(FLOAT_REM, op1, op2, TypeReference.Float);
}
break;
case JBC_drem: {
Operand op2 = popDouble();
Operand op1 = popDouble();
s = _binaryDualHelper(DOUBLE_REM, op1, op2, TypeReference.Double);
}
break;
case JBC_ineg:
s = _unaryHelper(INT_NEG, popInt(), TypeReference.Int);
break;
case JBC_lneg:
s = _unaryDualHelper(LONG_NEG, popLong(), TypeReference.Long);
break;
case JBC_fneg:
s = _unaryHelper(FLOAT_NEG, popFloat(), TypeReference.Float);
break;
case JBC_dneg:
s = _unaryDualHelper(DOUBLE_NEG, popDouble(), TypeReference.Double);
break;
case JBC_ishl: {
Operand op2 = popShiftInt(false);
Operand op1 = popInt();
s = _binaryHelper(INT_SHL, op1, op2, TypeReference.Int);
}
break;
case JBC_lshl: {
Operand op2 = popShiftInt(true);
Operand op1 = popLong();
s = _binaryDualHelper(LONG_SHL, op1, op2, TypeReference.Long);
}
break;
case JBC_ishr: {
Operand op2 = popShiftInt(false);
Operand op1 = popInt();
s = _binaryHelper(INT_SHR, op1, op2, TypeReference.Int);
}
break;
case JBC_lshr: {
Operand op2 = popShiftInt(true);
Operand op1 = popLong();
s = _binaryDualHelper(LONG_SHR, op1, op2, TypeReference.Long);
}
break;
case JBC_iushr: {
Operand op2 = popShiftInt(false);
Operand op1 = popInt();
s = _binaryHelper(INT_USHR, op1, op2, TypeReference.Int);
}
break;
case JBC_lushr: {
Operand op2 = popShiftInt(true);
Operand op1 = popLong();
s = _binaryDualHelper(LONG_USHR, op1, op2, TypeReference.Long);
}
break;
case JBC_iand: {
Operand op2 = popInt();
Operand op1 = popInt();
s = _binaryHelper(INT_AND, op1, op2, TypeReference.Int);
}
break;
case JBC_land: {
Operand op2 = popLong();
Operand op1 = popLong();
s = _binaryDualHelper(LONG_AND, op1, op2, TypeReference.Long);
}
break;
case JBC_ior: {
Operand op2 = popInt();
Operand op1 = popInt();
s = _binaryHelper(INT_OR, op1, op2, TypeReference.Int);
}
break;
case JBC_lor: {
Operand op2 = popLong();
Operand op1 = popLong();
s = _binaryDualHelper(LONG_OR, op1, op2, TypeReference.Long);
}
break;
case JBC_ixor: {
Operand op2 = popInt();
Operand op1 = popInt();
s = _binaryHelper(INT_XOR, op1, op2, TypeReference.Int);
}
break;
case JBC_lxor: {
Operand op2 = popLong();
Operand op1 = popLong();
s = _binaryDualHelper(LONG_XOR, op1, op2, TypeReference.Long);
}
break;
case JBC_iinc: {
int index = bcodes.getLocalNumber();
s = do_iinc(index, bcodes.getIncrement());
}
break;
case JBC_i2l:
s = _unaryDualHelper(INT_2LONG, popInt(), TypeReference.Long);
break;
case JBC_i2f:
s = _unaryHelper(INT_2FLOAT, popInt(), TypeReference.Float);
break;
case JBC_i2d:
s = _unaryDualHelper(INT_2DOUBLE, popInt(), TypeReference.Double);
break;
case JBC_l2i:
s = _unaryHelper(LONG_2INT, popLong(), TypeReference.Int);
break;
case JBC_l2f:
s = _unaryHelper(LONG_2FLOAT, popLong(), TypeReference.Float);
break;
case JBC_l2d:
s = _unaryDualHelper(LONG_2DOUBLE, popLong(), TypeReference.Double);
break;
case JBC_f2i:
s = _unaryHelper(FLOAT_2INT, popFloat(), TypeReference.Int);
break;
case JBC_f2l:
s = _unaryDualHelper(FLOAT_2LONG, popFloat(), TypeReference.Long);
break;
case JBC_f2d:
s = _unaryDualHelper(FLOAT_2DOUBLE, popFloat(), TypeReference.Double);
break;
case JBC_d2i:
s = _unaryHelper(DOUBLE_2INT, popDouble(), TypeReference.Int);
break;
case JBC_d2l:
s = _unaryDualHelper(DOUBLE_2LONG, popDouble(), TypeReference.Long);
break;
case JBC_d2f:
s = _unaryHelper(DOUBLE_2FLOAT, popDouble(), TypeReference.Float);
break;
case JBC_int2byte:
s = _unaryHelper(INT_2BYTE, popInt(), TypeReference.Byte);
break;
case JBC_int2char:
s = _unaryHelper(INT_2USHORT, popInt(), TypeReference.Char);
break;
case JBC_int2short:
s = _unaryHelper(INT_2SHORT, popInt(), TypeReference.Short);
break;
case JBC_lcmp: {
Operand op2 = popLong();
Operand op1 = popLong();
s = _binaryHelper(LONG_CMP, op1, op2, TypeReference.Int);
}
break;
case JBC_fcmpl: {
Operand op2 = popFloat();
Operand op1 = popFloat();
s = _binaryHelper(FLOAT_CMPL, op1, op2, TypeReference.Int);
}
break;
case JBC_fcmpg: {
Operand op2 = popFloat();
Operand op1 = popFloat();
s = _binaryHelper(FLOAT_CMPG, op1, op2, TypeReference.Int);
}
break;
case JBC_dcmpl: {
Operand op2 = popDouble();
Operand op1 = popDouble();
s = _binaryHelper(DOUBLE_CMPL, op1, op2, TypeReference.Int);
}
break;
case JBC_dcmpg: {
Operand op2 = popDouble();
Operand op1 = popDouble();
s = _binaryHelper(DOUBLE_CMPG, op1, op2, TypeReference.Int);
}
break;
case JBC_ifeq:
s = _intIfHelper(ConditionOperand.EQUAL());
break;
case JBC_ifne:
s = _intIfHelper(ConditionOperand.NOT_EQUAL());
break;
case JBC_iflt:
s = _intIfHelper(ConditionOperand.LESS());
break;
case JBC_ifge:
s = _intIfHelper(ConditionOperand.GREATER_EQUAL());
break;
case JBC_ifgt:
s = _intIfHelper(ConditionOperand.GREATER());
break;
case JBC_ifle:
s = _intIfHelper(ConditionOperand.LESS_EQUAL());
break;
case JBC_if_icmpeq:
s = _intIfCmpHelper(ConditionOperand.EQUAL());
break;
case JBC_if_icmpne:
s = _intIfCmpHelper(ConditionOperand.NOT_EQUAL());
break;
case JBC_if_icmplt:
s = _intIfCmpHelper(ConditionOperand.LESS());
break;
case JBC_if_icmpge:
s = _intIfCmpHelper(ConditionOperand.GREATER_EQUAL());
break;
case JBC_if_icmpgt:
s = _intIfCmpHelper(ConditionOperand.GREATER());
break;
case JBC_if_icmple:
s = _intIfCmpHelper(ConditionOperand.LESS_EQUAL());
break;
case JBC_if_acmpeq:
s = _refIfCmpHelper(ConditionOperand.EQUAL());
break;
case JBC_if_acmpne:
s = _refIfCmpHelper(ConditionOperand.NOT_EQUAL());
break;
case JBC_goto: {
int offset = bcodes.getBranchOffset();
if (offset != 3) {
// skip generating frivolous goto's
s = _gotoHelper(offset);
}
}
break;
case JBC_jsr:
s = _jsrHelper(bcodes.getBranchOffset());
break;
case JBC_ret:
s = _retHelper(bcodes.getLocalNumber());
break;
case JBC_tableswitch: {
bcodes.alignSwitch();
Operand op0 = popInt();
int defaultoff = bcodes.getDefaultSwitchOffset();
int low = bcodes.getLowSwitchValue();
int high = bcodes.getHighSwitchValue();
int number = high - low + 1;
if (CF_TABLESWITCH && op0 instanceof IntConstantOperand) {
int v1 = ((IntConstantOperand) op0).value;
int match = bcodes.computeTableSwitchOffset(v1, low, high);
int offset = match == 0 ? defaultoff : match;
bcodes.skipTableSwitchOffsets(number);
if (DBG_CF) {
db("changed tableswitch to goto because index (" + v1 + ") is constant");
}
s = _gotoHelper(offset);
break;
}
s =
TableSwitch.create(TABLESWITCH,
op0,
null,
null,
new IntConstantOperand(low),
new IntConstantOperand(high),
generateTarget(defaultoff),
null,
number * 2);
for (int i = 0; i < number; ++i) {
TableSwitch.setTarget(s, i, generateTarget(bcodes.getTableSwitchOffset(i)));
}
bcodes.skipTableSwitchOffsets(number);
// Set branch probabilities
SwitchBranchProfile sp = gc.getSwitchProfile(instrIndex - bciAdjustment);
if (sp == null) {
float approxProb = 1.0f / (number + 1); // number targets + default
TableSwitch.setDefaultBranchProfile(s, new BranchProfileOperand(approxProb));
for (int i = 0; i < number; ++i) {
TableSwitch.setBranchProfile(s, i, new BranchProfileOperand(approxProb));
}
} else {
TableSwitch.setDefaultBranchProfile(s, new BranchProfileOperand(sp.getDefaultProbability()));
for (int i = 0; i < number; ++i) {
TableSwitch.setBranchProfile(s, i, new BranchProfileOperand(sp.getCaseProbability(i)));
}
}
}
break;
case JBC_lookupswitch: {
bcodes.alignSwitch();
Operand op0 = popInt();
int defaultoff = bcodes.getDefaultSwitchOffset();
int numpairs = bcodes.getSwitchLength();
if (numpairs == 0) {
s = _gotoHelper(defaultoff);
break;
}
if (CF_LOOKUPSWITCH && op0 instanceof IntConstantOperand) {
int v1 = ((IntConstantOperand) op0).value;
int match = bcodes.computeLookupSwitchOffset(v1, numpairs);
int offset = match == 0 ? defaultoff : match;
bcodes.skipLookupSwitchPairs(numpairs);
if (DBG_CF) {
db("changed lookupswitch to goto because index (" + v1 + ") is constant");
}
s = _gotoHelper(offset);
break;
}
// Construct switch
s = LookupSwitch.create(LOOKUPSWITCH, op0, null, null, generateTarget(defaultoff), null, numpairs * 3);
for (int i = 0; i < numpairs; ++i) {
LookupSwitch.setMatch(s, i, new IntConstantOperand(bcodes.getLookupSwitchValue(i)));
LookupSwitch.setTarget(s, i, generateTarget(bcodes.getLookupSwitchOffset(i)));
}
bcodes.skipLookupSwitchPairs(numpairs);
// Set branch probabilities
SwitchBranchProfile sp = gc.getSwitchProfile(instrIndex - bciAdjustment);
if (sp == null) {
float approxProb = 1.0f / (numpairs + 1); // num targets + default
LookupSwitch.setDefaultBranchProfile(s, new BranchProfileOperand(approxProb));
for (int i = 0; i < numpairs; ++i) {
LookupSwitch.setBranchProfile(s, i, new BranchProfileOperand(approxProb));
}
} else {
LookupSwitch.setDefaultBranchProfile(s, new BranchProfileOperand(sp.getDefaultProbability()));
for (int i = 0; i < numpairs; ++i) {
LookupSwitch.setBranchProfile(s, i, new BranchProfileOperand(sp.getCaseProbability(i)));
}
}
}
break;
case JBC_ireturn:
_returnHelper(INT_MOVE, popInt());
break;
case JBC_lreturn:
_returnHelper(LONG_MOVE, popLong());
break;
case JBC_freturn:
_returnHelper(FLOAT_MOVE, popFloat());
break;
case JBC_dreturn:
_returnHelper(DOUBLE_MOVE, popDouble());
break;
case JBC_areturn: {
Operand op0 = popRef();
if (VM.VerifyAssertions && !op0.isDefinitelyNull()) {
TypeReference retType = op0.getType();
assertIsAssignable(gc.getMethod().getReturnType(), retType);
}
_returnHelper(REF_MOVE, op0);
}
break;
case JBC_return:
_returnHelper(null, null);
break;
case JBC_getstatic: {
// field resolution
FieldReference ref = bcodes.getFieldReference();
boolean unresolved = ref.needsDynamicLink(bcodes.getMethod());
LocationOperand fieldOp = makeStaticFieldRef(ref);
Operand offsetOp;
TypeReference fieldType = ref.getFieldContentsType();
RegisterOperand t = gc.getTemps().makeTemp(fieldType);
if (unresolved) {
RegisterOperand offsetrop = gc.getTemps().makeTempOffset();
appendInstruction(Unary.create(RESOLVE_MEMBER, offsetrop.copyRO(), fieldOp.copy()));
offsetOp = offsetrop;
rectifyStateWithErrorHandler();
} else {
RVMField field = ref.peekResolvedField();
offsetOp = new AddressConstantOperand(field.getOffset());
// use results of field analysis to refine type of result
RVMType ft = fieldType.peekType();
if (ft != null && ft.isClassType()) {
TypeReference concreteType = FieldAnalysis.getConcreteType(field);
if (concreteType != null) {
if (concreteType == fieldType) {
t.setDeclaredType();
t.setPreciseType();
} else {
fieldType = concreteType;
t.setPreciseType(concreteType);
}
}
}
// optimization: if the field is final and either
// initialized or we're writing the bootimage and the field
// is from a suitable class, then get the value at compile
// time.
if (gc.getOptions().SIMPLIFY_CHASE_FINAL_FIELDS && field.isFinal()) {
RVMClass declaringClass = field.getDeclaringClass();
boolean initializedClassAtRuntime = VM.runningVM & declaringClass.isInitialized();
boolean fieldFromRVMInternalClassInBootImage = declaringClass.isInBootImage() &&
declaringClass.getDescriptor().isRVMDescriptor();
// We cannot assume that non-public fields from the host JVM's class library are present in
// the class library used by Jikes RVM: only public fields are part of the API.
boolean publicFieldInBootImage = declaringClass.isInBootImage() &&
field.isPublic();
if (initializedClassAtRuntime || fieldFromRVMInternalClassInBootImage ||
publicFieldInBootImage) {
try {
ConstantOperand rhs = StaticFieldReader.getStaticFieldValue(field);
// VM.sysWriteln("Replaced getstatic of " + field + " with " + rhs);
push(rhs, fieldType);
break;
} catch (NoSuchFieldException e) {
if (VM.runningVM) {
throw new Error("Unexpected exception", e);
} else {
// Field not found during bootstrap due to chasing a field
// only valid in the bootstrap JVM.
// Although we try to avoid most cases where this could happen, we cannot
// avoid all. For example, a NoSuchFieldException can occur when we're trying
// to optimize a public field from Jikes RVM's class library that's not present
// in the host JVM's class library (example: a field from an internal
// helper class).
}
}
}
} else if (field.isRuntimeFinal()) {
if (VM.VerifyAssertions) opt_assert(fieldType.isBooleanType());
boolean rhsBool = field.getRuntimeFinalValue();
push(new IntConstantOperand(rhsBool ? 1 : 0));
break;
}
}
s = GetStatic.create(GETSTATIC, t, offsetOp, fieldOp);
if (fieldOp.mayBeVolatile()) {
appendInstruction(s);
s = Empty.create(READ_CEILING);
}
push(t.copyD2U(), fieldType);
}
break;
case JBC_putstatic: {
// field resolution
FieldReference ref = bcodes.getFieldReference();
boolean unresolved = ref.needsDynamicLink(bcodes.getMethod());
LocationOperand fieldOp = makeStaticFieldRef(ref);
Operand offsetOp;
if (unresolved) {
RegisterOperand offsetrop = gc.getTemps().makeTempOffset();
appendInstruction(Unary.create(RESOLVE_MEMBER, offsetrop.copyRO(), fieldOp.copy()));
offsetOp = offsetrop;
rectifyStateWithErrorHandler();
} else {
RVMField field = ref.peekResolvedField();
offsetOp = new AddressConstantOperand(field.getOffset());
}
TypeReference fieldType = ref.getFieldContentsType();
Operand r = pop(fieldType);
if (fieldOp.mayBeVolatile()) {
appendInstruction(Empty.create(WRITE_FLOOR));
}
s = PutStatic.create(PUTSTATIC, r, offsetOp, fieldOp);
if (fieldOp.mayBeVolatile()) {
appendInstruction(s);
s = Empty.create(FENCE);
}
}
break;
case JBC_getfield: {
// field resolution
FieldReference ref = bcodes.getFieldReference();
boolean unresolved = ref.needsDynamicLink(bcodes.getMethod());
LocationOperand fieldOp = makeInstanceFieldRef(ref);
Operand offsetOp;
TypeReference fieldType = ref.getFieldContentsType();
RVMField field = null;
RegisterOperand t = gc.getTemps().makeTemp(fieldType);
if (unresolved) {
RegisterOperand offsetrop = gc.getTemps().makeTempOffset();
appendInstruction(Unary.create(RESOLVE_MEMBER, offsetrop.copyRO(), fieldOp.copy()));
offsetOp = offsetrop;
rectifyStateWithErrorHandler();
} else {
field = ref.peekResolvedField();
offsetOp = new AddressConstantOperand(field.getOffset());
// use results of field analysis to refine type.
RVMType ft = fieldType.peekType();
if (ft != null && ft.isClassType()) {
TypeReference concreteType = FieldAnalysis.getConcreteType(field);
if (concreteType != null) {
if (concreteType == fieldType) {
t.setDeclaredType();
t.setPreciseType();
} else {
fieldType = concreteType;
t.setType(concreteType);
t.setPreciseType();
}
}
}
}
Operand op1 = pop();
clearCurrentGuard();
if (do_NullCheck(op1)) {
break;
}
// optimization: if the field is final and referenced by a
// constant reference then get the value at compile time.
// NB avoid String fields
if (op1.isConstant() && field.isFinal()) {
try {
ConstantOperand rhs =
StaticFieldReader.getFieldValueAsConstant(field, op1.asObjectConstant().value);
push(rhs, fieldType);
break;
} catch (NoSuchFieldException e) {
if (VM.runningVM) { // this is unexpected
throw new Error("Unexpected exception", e);
} else {
// Field not found during bootstrap due to chasing a field
// only valid in the bootstrap JVM
}
}
}
s = GetField.create(GETFIELD, t, op1, offsetOp, fieldOp, getCurrentGuard());
if (fieldOp.mayBeVolatile()) {
appendInstruction(s);
s = Empty.create(READ_CEILING);
}
push(t.copyD2U(), fieldType);
}
break;
case JBC_putfield: {
// field resolution
FieldReference ref = bcodes.getFieldReference();
boolean unresolved = ref.needsDynamicLink(bcodes.getMethod());
LocationOperand fieldOp = makeInstanceFieldRef(ref);
TypeReference fieldType = ref.getFieldContentsType();
Operand offsetOp;
if (unresolved) {
RegisterOperand offsetrop = gc.getTemps().makeTempOffset();
appendInstruction(Unary.create(RESOLVE_MEMBER, offsetrop.copyRO(), fieldOp.copy()));
offsetOp = offsetrop;
rectifyStateWithErrorHandler();
} else {
RVMField field = ref.peekResolvedField();
offsetOp = new AddressConstantOperand(field.getOffset());
}
Operand val = pop(fieldType);
Operand obj = popRef();
clearCurrentGuard();
if (do_NullCheck(obj)) {
break;
}
if (fieldOp.mayBeVolatile()) {
appendInstruction(Empty.create(WRITE_FLOOR));
}
s = PutField.create(PUTFIELD, val, obj, offsetOp, fieldOp, getCurrentGuard());
if (fieldOp.mayBeVolatile()) {
appendInstruction(s);
s = Empty.create(FENCE);
}
}
break;
case JBC_invokevirtual: {
MethodReference ref = bcodes.getMethodReference();
// See if this is a magic method (Address, Word, etc.)
// If it is, generate the inline code and we are done.
if (ref.isMagic()) {
boolean generated = GenerateMagic.generateMagic(this, gc, ref);
if (generated) break; // all done.
}
/* just create an osr barrier right before _callHelper
* changes the states of locals and stacks.
*/
if (this.osrGuardedInline) {
lastOsrBarrier = _createOsrBarrier();
}
if (ref.isMiranda()) {
// An invokevirtual that is really an invokeinterface.
s = _callHelper(ref, MethodOperand.INTERFACE(ref, null));
if (s == null)
break;
Operand receiver = Call.getParam(s, 0);
RVMClass receiverType = (RVMClass) receiver.getType().peekType();
// null check on this parameter of call
clearCurrentGuard();
if (do_NullCheck(receiver)) {
// call will always raise null pointer exception
s = null;
break;
}
Call.setGuard(s, getCurrentGuard());
// Attempt to resolve the interface call to a particular virtual method.
// This is independent of whether or not the static type of the receiver is
// known to implement the interface and it is not that case that being able
// to prove one implies the other.
RVMMethod vmeth = null;
if (receiverType != null && receiverType.isInitialized() && !receiverType.isInterface()) {
vmeth = ClassLoaderProxy.lookupMethod(receiverType, ref);
}
if (vmeth != null) {
MethodReference vmethRef = vmeth.getMemberRef().asMethodReference();
MethodOperand mop = MethodOperand.VIRTUAL(vmethRef, vmeth);
if (receiver.isConstant() || (receiver.isRegister() && receiver.asRegister().isPreciseType())) {
mop.refine(vmeth, true);
}
Call.setMethod(s, mop);
boolean unresolved = vmethRef.needsDynamicLink(bcodes.getMethod());
if (unresolved) {
RegisterOperand offsetrop = gc.getTemps().makeTempOffset();
appendInstruction(Unary.create(RESOLVE_MEMBER, offsetrop.copyRO(), Call.getMethod(s).copy()));
Call.setAddress(s, offsetrop);
rectifyStateWithErrorHandler();
} else {
Call.setAddress(s, new AddressConstantOperand(vmeth.getOffset()));
}
// Attempt to inline virtualized call.
if (maybeInlineMethod(shouldInline(s,
receiver.isConstant() ||
(receiver.isRegister() && receiver.asRegister().isExtant()),
instrIndex - bciAdjustment), s)) {
return;
}
}
} else {
// A normal invokevirtual. Create call instruction.
boolean unresolved = ref.needsDynamicLink(bcodes.getMethod());
RVMMethod target = ref.peekResolvedMethod();
MethodOperand methOp = MethodOperand.VIRTUAL(ref, target);
s = _callHelper(ref, methOp);
if (s == null)
break;
// Handle possibility of dynamic linking.
// Must be done before null_check!
if (unresolved) {
RegisterOperand offsetrop = gc.getTemps().makeTempOffset();
appendInstruction(Unary.create(RESOLVE_MEMBER, offsetrop.copyRO(), Call.getMethod(s).copy()));
Call.setAddress(s, offsetrop);
rectifyStateWithErrorHandler();
} else {
if (VM.VerifyAssertions) opt_assert(target != null);
Call.setAddress(s, new AddressConstantOperand(target.getOffset()));
}
// null check receiver
Operand receiver = Call.getParam(s, 0);
clearCurrentGuard();
if (do_NullCheck(receiver)) {
// call will always raise null pointer exception
s = null;
break;
}
Call.setGuard(s, getCurrentGuard());
// Use compile time type of receiver to try reduce the number
// of targets.
// If we succeed, we'll update meth and s's method operand.
boolean isExtant = false;
boolean isPreciseType = false;
TypeReference tr = null;
if (receiver.isRegister()) {
RegisterOperand rop = receiver.asRegister();
isExtant = rop.isExtant();
isPreciseType = rop.isPreciseType();
tr = rop.getType();
} else {
isExtant = true;
isPreciseType = true;
tr = receiver.getType();
}
RVMType type = tr.peekType();
if (type != null && type.isResolved()) {
if (type.isClassType()) {
RVMMethod vmeth = target;
if (target == null || type != target.getDeclaringClass()) {
vmeth = ClassLoaderProxy.lookupMethod(type.asClass(), ref);
}
if (vmeth != null) {
methOp.refine(vmeth, isPreciseType || type.asClass().isFinal());
}
} else {
// Array: will always be calling the method defined in java.lang.Object
if (VM.VerifyAssertions) opt_assert(target != null, "Huh? Target method must already be resolved if receiver is array");
methOp.refine(target, true);
}
}
// Consider inlining it.
if (maybeInlineMethod(shouldInline(s, isExtant, instrIndex - bciAdjustment), s)) {
return;
}
}
// noninlined CALL must be treated as potential throw of anything
rectifyStateWithExceptionHandlers();
}
break;
case JBC_invokespecial: {
MethodReference ref = bcodes.getMethodReference();
RVMMethod target = ref.resolveInvokeSpecial();
/* just create an osr barrier right before _callHelper
* changes the states of locals and stacks.
*/
if (this.osrGuardedInline) {
lastOsrBarrier = _createOsrBarrier();
}
s = _callHelper(ref, MethodOperand.SPECIAL(ref, target));
if (s == null)
break;
// Handle possibility of dynamic linking. Must be done before null_check!
// NOTE: different definition of unresolved due to semantics of invokespecial.
if (target == null) {
RegisterOperand offsetrop = gc.getTemps().makeTempOffset();
appendInstruction(Unary.create(RESOLVE_MEMBER, offsetrop.copyRO(), Call.getMethod(s).copy()));
Call.setAddress(s, offsetrop);
rectifyStateWithErrorHandler();
} else {
Call.setAddress(s, new AddressConstantOperand(target.getOffset()));
}
// null check receiver
Operand receiver = Call.getParam(s, 0);
clearCurrentGuard();
if (do_NullCheck(receiver)) {
// call will always raise null pointer exception
s = null;
break;
}
Call.setGuard(s, getCurrentGuard());
// Consider inlining it.
if (maybeInlineMethod(shouldInline(s, false, instrIndex - bciAdjustment), s)) {
return;
}
// noninlined CALL must be treated as potential throw of anything
rectifyStateWithExceptionHandlers();
}
break;
case JBC_invokestatic: {
MethodReference ref = bcodes.getMethodReference();
// See if this is a magic method (Magic, Address, Word, etc.)
// If it is, generate the inline code and we are done.
if (ref.isMagic()) {
boolean generated = GenerateMagic.generateMagic(this, gc, ref);
if (generated) break;
}
// A non-magical invokestatic. Create call instruction.
boolean unresolved = ref.needsDynamicLink(bcodes.getMethod());
RVMMethod target = ref.peekResolvedMethod();
/* just create an osr barrier right before _callHelper
* changes the states of locals and stacks.
*/
if (this.osrGuardedInline) {
lastOsrBarrier = _createOsrBarrier();
}
s = _callHelper(ref, MethodOperand.STATIC(ref, target));
if (s == null)
break;
if (Call.conforms(s)) {
MethodOperand methOp = Call.getMethod(s);
if (methOp.getTarget() == target) {
// Handle possibility of dynamic linking.
if (unresolved) {
RegisterOperand offsetrop = gc.getTemps().makeTempOffset();
appendInstruction(Unary.create(RESOLVE_MEMBER, offsetrop.copyRO(), Call.getMethod(s).copy()));
Call.setAddress(s, offsetrop);
rectifyStateWithErrorHandler();
} else {
Call.setAddress(s, new AddressConstantOperand(target.getOffset()));
}
// Consider inlining it.
if (maybeInlineMethod(shouldInline(s, false, instrIndex - bciAdjustment), s)) {
return;
}
}
}
// noninlined CALL must be treated as potential throw of anything
rectifyStateWithExceptionHandlers();
}
break;
case JBC_invokeinterface: {
MethodReference ref = bcodes.getMethodReference();
bcodes.alignInvokeInterface();
RVMMethod resolvedMethod = null;
resolvedMethod = ref.peekInterfaceMethod();
/* just create an osr barrier right before _callHelper
* changes the states of locals and stacks.
*/
if (this.osrGuardedInline) {
lastOsrBarrier = _createOsrBarrier();
}
s = _callHelper(ref, MethodOperand.INTERFACE(ref, resolvedMethod));
if (s == null)
break;
Operand receiver = Call.getParam(s, 0);
RVMClass receiverType = (RVMClass) receiver.getType().peekType();
boolean requiresImplementsTest = VM.BuildForIMTInterfaceInvocation;
// Invokeinterface requires a dynamic type check
// to ensure that the receiver object actually
// implements the interface. This is necessary
// because the verifier does not detect incompatible class changes.
// Depending on the implementation of interface dispatching
// we are using, we may have to make this test explicit
// in the calling sequence if we can't prove at compile time
// that it is not needed.
if (requiresImplementsTest && resolvedMethod == null) {
// Sigh. Can't even resolve the reference to figure out what interface
// method we are trying to call. Therefore we must make generate a call
// to an out-of-line typechecking routine to handle it at runtime.
RVMMethod target = Entrypoints.unresolvedInvokeinterfaceImplementsTestMethod;
Instruction callCheck =
Call.create2(CALL,
null,
new AddressConstantOperand(target.getOffset()),
MethodOperand.STATIC(target),
new IntConstantOperand(ref.getId()),
receiver.copy());
if (gc.getOptions().H2L_NO_CALLEE_EXCEPTIONS) {
callCheck.markAsNonPEI();
}
appendInstruction(callCheck);
callCheck.setBytecodeIndex(RUNTIME_SERVICES_BCI);
requiresImplementsTest = false; // the above call subsumes the test
rectifyStateWithErrorHandler(); // Can raise incompatible class change error.
}
// null check on this parameter of call. Must be done after dynamic linking!
clearCurrentGuard();
if (do_NullCheck(receiver)) {
// call will always raise null pointer exception
s = null;
break;
}
Call.setGuard(s, getCurrentGuard());
if (requiresImplementsTest) {
// We know what interface method the program wants to invoke.
// Attempt to avoid inserting the type check by seeing if the
// known static type of the receiver implements the desired interface.
RVMType interfaceType = resolvedMethod.getDeclaringClass();
if (receiverType != null && receiverType.isResolved() && !receiverType.isInterface()) {
byte doesImplement =
ClassLoaderProxy.includesType(interfaceType.getTypeRef(), receiverType.getTypeRef());
requiresImplementsTest = doesImplement != YES;
}
}
// Attempt to resolve the interface call to a particular virtual method.
// This is independent of whether or not the static type of the receiver is
// known to implement the interface and it is not that case that being able
// to prove one implies the other.
RVMMethod vmeth = null;
if (receiverType != null && receiverType.isInitialized() && !receiverType.isInterface()) {
vmeth = ClassLoaderProxy.lookupMethod(receiverType, ref);
}
if (vmeth != null) {
MethodReference vmethRef = vmeth.getMemberRef().asMethodReference();
// We're going to virtualize the call. Must inject the
// DTC to ensure the receiver implements the interface if
// requiresImplementsTest is still true.
// Note that at this point requiresImplementsTest => resolvedMethod != null
if (requiresImplementsTest) {
RegisterOperand checkedReceiver = gc.getTemps().makeTemp(receiver);
appendInstruction(TypeCheck.create(MUST_IMPLEMENT_INTERFACE,
checkedReceiver,
receiver.copy(),
makeTypeOperand(resolvedMethod.getDeclaringClass()),
getCurrentGuard()));
checkedReceiver.refine(resolvedMethod.getDeclaringClass().getTypeRef());
Call.setParam(s, 0, checkedReceiver.copyRO());
receiver = checkedReceiver;
rectifyStateWithErrorHandler(); // Can raise incompatible class change error.
}
MethodOperand mop = MethodOperand.VIRTUAL(vmethRef, vmeth);
if (receiver.isConstant() || receiver.asRegister().isPreciseType()) {
mop.refine(vmeth, true);
}
Call.setMethod(s, mop);
boolean unresolved = vmethRef.needsDynamicLink(bcodes.getMethod());
if (unresolved) {
RegisterOperand offsetrop = gc.getTemps().makeTempOffset();
appendInstruction(Unary.create(RESOLVE_MEMBER, offsetrop.copyRO(), Call.getMethod(s).copy()));
Call.setAddress(s, offsetrop);
rectifyStateWithErrorHandler();
} else {
Call.setAddress(s, new AddressConstantOperand(vmeth.getOffset()));
}
// Attempt to inline virtualized call.
if (maybeInlineMethod(shouldInline(s,
receiver.isConstant() || receiver.asRegister().isExtant(),
instrIndex - bciAdjustment), s)) {
return;
}
} else {
// We can't virtualize the call;
// try to inline a predicted target for the interface invocation
// inline code will include DTC to ensure receiver implements the interface.
if (resolvedMethod != null &&
maybeInlineMethod(shouldInline(s, false, instrIndex - bciAdjustment), s)) {
return;
} else {
if (requiresImplementsTest) {
RegisterOperand checkedReceiver = gc.getTemps().makeTemp(receiver);
appendInstruction(TypeCheck.create(MUST_IMPLEMENT_INTERFACE,
checkedReceiver,
receiver.copy(),
makeTypeOperand(resolvedMethod.getDeclaringClass()),
getCurrentGuard()));
checkedReceiver.refine(resolvedMethod.getDeclaringClass().getTypeRef());
Call.setParam(s, 0, checkedReceiver.copyRO());
// don't have to rectify with error handlers; rectify call below subsumes.
}
}
}
// CALL must be treated as potential throw of anything
rectifyStateWithExceptionHandlers();
}
break;
case JBC_invokedynamic:
OptimizingCompilerException.UNREACHABLE();
break;
case JBC_new: {
TypeReference klass = bcodes.getTypeReference();
RegisterOperand t = gc.getTemps().makeTemp(klass);
t.setPreciseType();
markGuardlessNonNull(t);
Operator operator;
TypeOperand klassOp;
RVMClass klassType = (RVMClass) klass.peekType();
if (klassType != null && (klassType.isInitialized() || klassType.isInBootImage())) {
klassOp = makeTypeOperand(klassType);
operator = NEW;
t.setExtant();
} else {
operator = NEW_UNRESOLVED;
klassOp = makeTypeOperand(klass);
}
s = New.create(operator, t, klassOp);
push(t.copyD2U());
rectifyStateWithErrorHandler();
}
break;
case JBC_newarray: {
RVMType array = bcodes.getPrimitiveArrayType();
TypeOperand arrayOp = makeTypeOperand(array);
RegisterOperand t = gc.getTemps().makeTemp(array.getTypeRef());
t.setPreciseType();
t.setExtant();
markGuardlessNonNull(t);
s = NewArray.create(NEWARRAY, t, arrayOp, popInt());
push(t.copyD2U());
rectifyStateWithExceptionHandler(TypeReference.JavaLangNegativeArraySizeException);
}
break;
case JBC_anewarray: {
TypeReference elementTypeRef = bcodes.getTypeReference();
s = generateAnewarray(null, elementTypeRef);
}
break;
case JBC_arraylength: {
Operand op1 = pop();
clearCurrentGuard();
if (do_NullCheck(op1)) {
break;
}
if (VM.VerifyAssertions) {
opt_assert(getArrayTypeOf(op1).isArrayType());
}
RegisterOperand t = gc.getTemps().makeTempInt();
s = GuardedUnary.create(ARRAYLENGTH, t, op1, getCurrentGuard());
push(t.copyD2U());
}
break;
case JBC_athrow: {
Operand op0 = pop();
clearCurrentGuard();
if (do_NullCheck(op0)) {
break;
}
TypeReference type = getRefTypeOf(op0);
if (VM.VerifyAssertions) assertIsAssignable(TypeReference.JavaLangThrowable, type);
if (!gc.getMethod().isInterruptible()) {
// prevent code motion in or out of uninterruptible code sequence
appendInstruction(Empty.create(UNINT_END));
}
endOfBasicBlock = true;
BasicBlock definiteTarget = rectifyStateWithExceptionHandler(type, true);
if (definiteTarget != null) {
appendInstruction(CacheOp.create(SET_CAUGHT_EXCEPTION, op0));
s = Goto.create(GOTO, definiteTarget.makeJumpTarget());
definiteTarget.setExceptionHandlerWithNormalIn();
} else {
s = Athrow.create(ATHROW, op0);
}
}
break;
case JBC_checkcast: {
TypeReference typeRef = bcodes.getTypeReference();
boolean classLoading = couldCauseClassLoading(typeRef);
Operand op2 = pop();
if (typeRef.isWordLikeType()) {
op2 = op2.copy();
if (op2 instanceof RegisterOperand) {
((RegisterOperand) op2).setType(typeRef);
}
push(op2);
if (DBG_CF) db("skipped gen of checkcast to word type " + typeRef);
break;
}
if (VM.VerifyAssertions) opt_assert(op2.isRef());
if (CF_CHECKCAST && !classLoading) {
if (op2.isDefinitelyNull()) {
push(op2);
if (DBG_CF) db("skipped gen of null checkcast");
break;
}
TypeReference type = getRefTypeOf(op2); // non-null, null case above
byte typeTestResult = ClassLoaderProxy.includesType(typeRef, type);
if (typeTestResult == YES) {
push(op2);
if (DBG_CF) {
db("skipped gen of checkcast of " + op2 + " from " + typeRef + " to " + type);
}
break;
}
if (typeTestResult == NO) {
if (isNonNull(op2)) {
// Definite class cast exception
endOfBasicBlock = true;
appendInstruction(Trap.create(TRAP, gc.getTemps().makeTempValidation(), TrapCodeOperand.CheckCast()));
rectifyStateWithExceptionHandler(TypeReference.JavaLangClassCastException);
if (DBG_CF) db("Converted checkcast into unconditional trap");
break;
} else {
// At runtime either it is null and the checkcast succeeds or it is non-null
// and a class cast exception is raised
RegisterOperand refinedOp2 = gc.getTemps().makeTemp(op2);
s = TypeCheck.create(CHECKCAST, refinedOp2, op2.copy(), makeTypeOperand(typeRef.peekType()));
refinedOp2.refine(TypeReference.NULL_TYPE);
push(refinedOp2.copyRO());
rectifyStateWithExceptionHandler(TypeReference.JavaLangClassCastException);
if (DBG_CF) db("Narrowed type downstream of checkcast to NULL");
break;
}
}
}
RegisterOperand refinedOp2 = gc.getTemps().makeTemp(op2);
if (classLoading) {
s = TypeCheck.create(CHECKCAST_UNRESOLVED, refinedOp2, op2.copy(), makeTypeOperand(typeRef));
} else {
TypeOperand typeOp = makeTypeOperand(typeRef.peekType());
if (isNonNull(op2)) {
s = TypeCheck.create(CHECKCAST_NOTNULL, refinedOp2, op2.copy(), typeOp, copyGuardFromOperand(op2));
} else {
s = TypeCheck.create(CHECKCAST, refinedOp2, op2.copy(), typeOp);
}
}
refinedOp2.refine(typeRef);
push(refinedOp2.copyRO());
rectifyStateWithExceptionHandler(TypeReference.JavaLangClassCastException);
if (classLoading) rectifyStateWithErrorHandler();
}
break;
case JBC_instanceof: {
TypeReference typeRef = bcodes.getTypeReference();
boolean classLoading = couldCauseClassLoading(typeRef);
Operand op2 = pop();
if (VM.VerifyAssertions) opt_assert(op2.isRef());
if (CF_INSTANCEOF && !classLoading) {
if (op2.isDefinitelyNull()) {
push(new IntConstantOperand(0));
if (DBG_CF) db("skipped gen of null instanceof");
break;
}
TypeReference type = getRefTypeOf(op2); // non-null
int answer = ClassLoaderProxy.includesType(typeRef, type);
if (answer == YES && isNonNull(op2)) {
push(new IntConstantOperand(1));
if (DBG_CF) {
db(op2 + " instanceof " + typeRef + " is always true ");
}
break;
} else if (answer == NO) {
if (DBG_CF) {
db(op2 + " instanceof " + typeRef + " is always false ");
}
push(new IntConstantOperand(0));
break;
}
}
RegisterOperand t = gc.getTemps().makeTempInt();
if (classLoading) {
s = InstanceOf.create(INSTANCEOF_UNRESOLVED, t, makeTypeOperand(typeRef), op2);
} else {
TypeOperand typeOp = makeTypeOperand(typeRef.peekType());
if (isNonNull(op2)) {
s = InstanceOf.create(INSTANCEOF_NOTNULL, t, typeOp, op2, copyGuardFromOperand(op2));
} else {
s = InstanceOf.create(INSTANCEOF, t, typeOp, op2);
}
}
push(t.copyD2U());
if (classLoading) rectifyStateWithErrorHandler();
}
break;
case JBC_monitorenter: {
Operand op0 = pop();
clearCurrentGuard();
if (do_NullCheck(op0)) {
break;
}
if (VM.VerifyAssertions) opt_assert(op0.isRef());
s = MonitorOp.create(MONITORENTER, op0, getCurrentGuard());
}
break;
case JBC_monitorexit: {
Operand op0 = pop();
clearCurrentGuard();
if (do_NullCheck(op0)) {
break;
}
s = MonitorOp.create(MONITOREXIT, op0, getCurrentGuard());
rectifyStateWithExceptionHandler(TypeReference.JavaLangIllegalMonitorStateException);
}
break;
case JBC_wide: {
int widecode = bcodes.getWideOpcode();
int index = bcodes.getWideLocalNumber();
switch (widecode) {
case JBC_iload:
s = do_iload(index);
break;
case JBC_lload:
s = do_lload(index);
break;
case JBC_fload:
s = do_fload(index);
break;
case JBC_dload:
s = do_dload(index);
break;
case JBC_aload:
s = do_aload(index);
break;
case JBC_istore:
s = do_store(index, popInt());
break;
case JBC_lstore:
s = do_store(index, popLong());
break;
case JBC_fstore:
s = do_store(index, popFloat());
break;
case JBC_dstore:
s = do_store(index, popDouble());
break;
case JBC_astore:
s = do_astore(index);
break;
case JBC_iinc:
s = do_iinc(index, bcodes.getWideIncrement());
break;
case JBC_ret:
s = _retHelper(index);
break;
default:
OptimizingCompilerException.UNREACHABLE();
break;
}
}
break;
case JBC_multianewarray: {
TypeReference arrayType = bcodes.getTypeReference();
int dimensions = bcodes.getArrayDimension();
if (dimensions == 1) {
s = generateAnewarray(arrayType, null);
} else {
TypeOperand typeOp = makeTypeOperand(arrayType);
RegisterOperand result = gc.getTemps().makeTemp(arrayType);
markGuardlessNonNull(result);
result.setPreciseType();
TypeReference innermostElementTypeRef = arrayType.getInnermostElementType();
RVMType innermostElementType = innermostElementTypeRef.peekType();
if (innermostElementType != null && (innermostElementType.isInitialized() || innermostElementType.isInBootImage())) {
result.setExtant();
}
s = Multianewarray.create(NEWOBJMULTIARRAY, result, typeOp, dimensions);
for (int i = 0; i < dimensions; i++) {
Multianewarray.setDimension(s, dimensions - i - 1, popInt());
}
push(result.copyD2U());
rectifyStateWithErrorHandler();
rectifyStateWithExceptionHandler(TypeReference.JavaLangNegativeArraySizeException);
}
}
break;
case JBC_ifnull:
s = _refIfNullHelper(ConditionOperand.EQUAL());
break;
case JBC_ifnonnull:
s = _refIfNullHelper(ConditionOperand.NOT_EQUAL());
break;
case JBC_goto_w: {
int offset = bcodes.getWideBranchOffset();
if (offset != 5) {
// skip generating frivolous goto's
s = _gotoHelper(offset);
}
}
break;
case JBC_jsr_w:
s = _jsrHelper(bcodes.getWideBranchOffset());
break;
case JBC_impdep1: {
if (VM.BuildForAdaptiveSystem) {
int pseudo_opcode = bcodes.nextPseudoInstruction();
switch (pseudo_opcode) {
case PSEUDO_LoadIntConst: {
int value = bcodes.readIntConst();
if (VM.TraceOnStackReplacement) {
VM.sysWriteln("PSEUDO_LoadIntConst " + value);
}
push(new IntConstantOperand(value));
// used for PSEUDO_InvokeStatic to recover the type info
param1 = param2;
param2 = value;
break;
}
case PSEUDO_LoadLongConst: {
long value = bcodes.readLongConst();
if (VM.TraceOnStackReplacement) {
VM.sysWriteln("PSEUDO_LoadLongConst " + value);
}
pushDual(new LongConstantOperand(value));
break;
}
case PSEUDO_LoadWordConst: {
Address a =
(VM.BuildFor32Addr) ? Address.fromIntSignExtend(bcodes.readIntConst()) : Address.fromLong(bcodes.readLongConst());
push(new AddressConstantOperand(a));
if (VM.TraceOnStackReplacement) {
VM.sysWrite("PSEUDO_LoadWordConst 0x");
}
VM.sysWrite(a);
VM.sysWriteln();
break;
}
case PSEUDO_LoadFloatConst: {
int ibits = bcodes.readIntConst();
float value = Float.intBitsToFloat(ibits);
if (VM.TraceOnStackReplacement) {
VM.sysWriteln("PSEUDO_LoadFloatConst " + value);
}
push(new FloatConstantOperand(value, Offset.zero()));
break;
}
case PSEUDO_LoadDoubleConst: {
long lbits = bcodes.readLongConst();
double value = Magic.longBitsAsDouble(lbits);
if (VM.TraceOnStackReplacement) {
VM.sysWriteln("PSEUDO_LoadDoubleConst " + lbits);
}
pushDual(new DoubleConstantOperand(value, Offset.zero()));
break;
}
case PSEUDO_LoadRetAddrConst: {
int value = bcodes.readIntConst();
if (VM.TraceOnStackReplacement) {
VM.sysWriteln("PSEUDO_LoadRetAddrConst " + value);
}
push(new ReturnAddressOperand(value));
break;
}
case PSEUDO_InvokeStatic: {
/* pseudo invoke static for getRefAt and cleanRefAt, both must be resolved already */
int targetidx = bcodes.readIntConst();
RVMMethod meth = InvokeStatic.targetMethod(targetidx);
if (VM.TraceOnStackReplacement) {
VM.sysWriteln("PSEUDO_Invoke " + meth);
VM.sysWriteln();
}
s = _callHelper(meth.getMemberRef().asMethodReference(), MethodOperand.STATIC(meth));
if (s == null)
break;
Call.setAddress(s, new AddressConstantOperand(meth.getOffset()));
/* try to set the type of return register */
if (targetidx == GETREFAT) {
Object realObj = ObjectHolder.getRefAt(param1, param2);
if (VM.VerifyAssertions) opt_assert(realObj != null);
TypeReference klass = Magic.getObjectType(realObj).getTypeRef();
RegisterOperand op0 = gc.getTemps().makeTemp(klass);
Call.setResult(s, op0);
pop(); // pop the old one and push the new return type.
push(op0.copyD2U(), klass);
}
// CALL must be treated as potential throw of anything
rectifyStateWithExceptionHandlers();
break;
}
case PSEUDO_InvokeCompiledMethod: {
int cmid = bcodes.readIntConst();
int origBCIdx = bcodes.readIntConst(); // skip it
CompiledMethod cm = CompiledMethods.getCompiledMethod(cmid);
RVMMethod meth = cm.getMethod();
if (VM.TraceOnStackReplacement) {
VM.sysWriteln("PSEUDO_InvokeCompiledMethod " + meth);
VM.sysWriteln();
}
/* the bcIndex should be adjusted to the original */
s = _callHelper(meth.getMemberRef().asMethodReference(),
MethodOperand.COMPILED(meth, cm.getOsrJTOCoffset()));
if (s == null)
break;
// adjust the bcindex of s to the original bytecode's index
// it should be able to give the correct exception handling
s.adjustBytecodeIndex(bciAdjustment);
rectifyStateWithExceptionHandlers();
break;
}
case PSEUDO_ParamInitEnd: {
// indicates the place to insert method prologue and stack
// overflow checks.
// opt compiler should consider this too
break;
}
default:
if (VM.TraceOnStackReplacement) {
VM.sysWriteln("OSR Error, no such pseudo opcode : " + pseudo_opcode);
}
OptimizingCompilerException.UNREACHABLE();
break;
}
break;
} else {
OptimizingCompilerException.UNREACHABLE();
}
}
default:
OptimizingCompilerException.UNREACHABLE();
break;
}
if (s != null && !currentBBLE.isSelfRegen()) {
appendInstruction(s);
}
// check runoff
if (VM.VerifyAssertions) opt_assert(bcodes.index() <= runoff);
if (!endOfBasicBlock && bcodes.index() == runoff) {
if (DBG_BB || DBG_SELECTED) {
db("runoff occurred! current basic block: " + currentBBLE + ", runoff = " + runoff);
}
endOfBasicBlock = fallThrough = true;
}
if (endOfBasicBlock) {
if (currentBBLE.isSelfRegen()) {
// This block ended in a goto that jumped into the middle of it.
// Through away all out edges from this block, they're out of date
// because we're going to have to regenerate this block.
currentBBLE.block.deleteOut();
if (DBG_CFG || DBG_SELECTED) {
db("Deleted all out edges of " + currentBBLE.block);
}
return;
}
if (fallThrough) {
if (VM.VerifyAssertions) opt_assert(bcodes.index() < bcodes.length());
// Get/Create fallthrough BBLE and record it as
// currentBBLE's fallThrough.
currentBBLE.fallThrough = getOrCreateBlock(bcodes.index());
currentBBLE.block.insertOut(currentBBLE.fallThrough.block);
}
return;
}
}
}
Instruction _unaryHelper(Operator operator, Operand val, TypeReference type) {
RegisterOperand t = gc.getTemps().makeTemp(type);
Instruction s = Unary.create(operator, t, val);
Simplifier.DefUseEffect simp = Simplifier.simplify(true, gc.getTemps(), gc.getOptions(), s);
if ((simp == Simplifier.DefUseEffect.MOVE_FOLDED) || (simp == Simplifier.DefUseEffect.MOVE_REDUCED)) {
gc.getTemps().release(t);
push(Move.getClearVal(s));
return null;
} else {
push(t.copyD2U());
return s;
}
}
Instruction _unaryDualHelper(Operator operator, Operand val, TypeReference type) {
RegisterOperand t = gc.getTemps().makeTemp(type);
Instruction s = Unary.create(operator, t, val);
Simplifier.DefUseEffect simp = Simplifier.simplify(true, gc.getTemps(), gc.getOptions(), s);
if ((simp == Simplifier.DefUseEffect.MOVE_FOLDED) || (simp == Simplifier.DefUseEffect.MOVE_REDUCED)) {
gc.getTemps().release(t);
pushDual(Move.getClearVal(s));
return null;
} else {
pushDual(t.copyD2U());
return s;
}
}
public Instruction _binaryHelper(Operator operator, Operand op1, Operand op2,
TypeReference type) {
RegisterOperand t = gc.getTemps().makeTemp(type);
Instruction s = Binary.create(operator, t, op1, op2);
Simplifier.DefUseEffect simp = Simplifier.simplify(true, gc.getTemps(), gc.getOptions(), s);
if ((simp == Simplifier.DefUseEffect.MOVE_FOLDED) || (simp == Simplifier.DefUseEffect.MOVE_REDUCED)) {
gc.getTemps().release(t);
push(Move.getClearVal(s));
return null;
} else {
push(t.copyD2U());
return s;
}
}
private Instruction _guardedBinaryHelper(Operator operator, Operand op1, Operand op2,
Operand guard, TypeReference type) {
RegisterOperand t = gc.getTemps().makeTemp(type);
Instruction s = GuardedBinary.create(operator, t, op1, op2, guard);
Simplifier.DefUseEffect simp = Simplifier.simplify(true, gc.getTemps(), gc.getOptions(), s);
if ((simp == Simplifier.DefUseEffect.MOVE_FOLDED) || (simp == Simplifier.DefUseEffect.MOVE_REDUCED)) {
gc.getTemps().release(t);
push(Move.getClearVal(s));
return null;
} else {
push(t.copyD2U());
return s;
}
}
private Instruction _binaryDualHelper(Operator operator, Operand op1, Operand op2,
TypeReference type) {
RegisterOperand t = gc.getTemps().makeTemp(type);
Instruction s = Binary.create(operator, t, op1, op2);
Simplifier.DefUseEffect simp = Simplifier.simplify(true, gc.getTemps(), gc.getOptions(), s);
if ((simp == Simplifier.DefUseEffect.MOVE_FOLDED) || (simp == Simplifier.DefUseEffect.MOVE_REDUCED)) {
gc.getTemps().release(t);
pushDual(Move.getClearVal(s));
return null;
} else {
pushDual(t.copyD2U());
return s;
}
}
private Instruction _guardedBinaryDualHelper(Operator operator, Operand op1, Operand op2,
Operand guard, TypeReference type) {
RegisterOperand t = gc.getTemps().makeTemp(type);
Instruction s = GuardedBinary.create(operator, t, op1, op2, guard);
Simplifier.DefUseEffect simp = Simplifier.simplify(true, gc.getTemps(), gc.getOptions(), s);
if ((simp == Simplifier.DefUseEffect.MOVE_FOLDED) || (simp == Simplifier.DefUseEffect.MOVE_REDUCED)) {
gc.getTemps().release(t);
pushDual(Move.getClearVal(s));
return null;
} else {
pushDual(t.copyD2U());
return s;
}
}
private void setSourcePosition(Instruction s) {
s.setSourcePosition(instrIndex, gc.getInlineSequence());
}
Instruction _moveHelper(Operator operator, Operand val, TypeReference type) {
RegisterOperand t = gc.getTemps().makeTemp(type);
push(t.copyD2U());
Instruction s = Move.create(operator, t, val);
setSourcePosition(s);
return s;
}
private Instruction _moveDualHelper(Operator operator, Operand val, TypeReference type) {
RegisterOperand t = gc.getTemps().makeTemp(type);
pushDual(t.copyD2U());
Instruction s = Move.create(operator, t, val);
setSourcePosition(s);
return s;
}
public Instruction _aloadHelper(Operator operator, Operand ref, Operand index,
TypeReference type) {
RegisterOperand t = gc.getTemps().makeTemp(type);
t.setDeclaredType();
LocationOperand loc = new LocationOperand(type);
Instruction s = ALoad.create(operator, t, ref, index, loc, getCurrentGuard());
t = t.copyD2U();
if (type.isLongType() || type.isDoubleType()) {
pushDual(t);
} else {
push(t);
}
return s;
}
/**
* Pop method parameters off the expression stack.
* If a non-void return, then create a result operand and push it
* on the stack.
* Create the call instruction and initialize all its operands.
*
* @param meth the method to call
* @param methOp data about the method
*
* @return the newly created call instruction
*/
private Instruction _callHelper(MethodReference meth, MethodOperand methOp) {
int numHiddenParams = methOp.isStatic() ? 0 : 1;
TypeReference[] params = meth.getParameterTypes();
Instruction s = Call.create(CALL, null, null, null, null, params.length + numHiddenParams);
if (gc.getOptions().H2L_NO_CALLEE_EXCEPTIONS) {
s.markAsNonPEI();
}
for (int i = params.length - 1; i >= 0; i--) {
try {
Call.setParam(s, i + numHiddenParams, pop(params[i]));
} catch (OptimizingCompilerException.IllegalUpcast e) {
throw new Error("Illegal upcast creating call to " + meth + " from " + gc.getMethod() + " argument " + i, e);
}
}
if (numHiddenParams != 0) {
Operand ref = pop();
Call.setParam(s, 0, ref);
}
Call.setMethod(s, methOp);
setSourcePosition(s);
TypeReference rtype = meth.getReturnType();
if (rtype.isVoidType()) {
return s;
} else {
RegisterOperand t = gc.getTemps().makeTemp(rtype);
Call.setResult(s, t);
Simplifier.DefUseEffect simp = Simplifier.simplify(true, gc.getTemps(), gc.getOptions(), s);
if ((simp == Simplifier.DefUseEffect.MOVE_FOLDED) || (simp == Simplifier.DefUseEffect.MOVE_REDUCED)) {
gc.getTemps().release(t);
push(Move.getClearVal(s), rtype);
return null;
} else {
push(t.copyD2U(), rtype);
return s;
}
}
}
private void _returnHelper(Operator operator, Operand val) {
if (gc.getResultReg() != null) {
TypeReference returnType = val.getType();
RegisterOperand ret = new RegisterOperand(gc.getResultReg(), returnType);
boolean returningRegister = false;
if (val.isRegister()) {
returningRegister = true;
ret.setInheritableFlags(val.asRegister());
setGuardForRegOp(ret, copyGuardFromOperand(val));
}
appendInstruction(Move.create(operator, ret, val));
// pass analysis facts about val back to our caller
if (gc.getResult() == null) {
if (returningRegister) {
gc.setResult(ret.copyD2U());
} else {
gc.setResult(val.copy());
}
} else {
Operand meet = Operand.meet(gc.getResult(), val, gc.getResultReg());
// Return value can't be forced to bottom...violation of Java spec.
if (VM.VerifyAssertions) opt_assert(meet != null);
gc.setResult(meet);
}
}
if (gc.getMethod().isObjectInitializer() && gc.getMethod().getDeclaringClass().declaresFinalInstanceField()) {
/* JMM Compliance. Must insert StoreStore barrier before returning from constructor of class with final instance fields */
appendInstruction(Empty.create(WRITE_FLOOR));
}
appendInstruction(gc.getEpilogue().makeGOTO());
currentBBLE.block.insertOut(gc.getEpilogue());
if (DBG_CFG || DBG_SELECTED) {
db("Added CFG edge from " + currentBBLE.block + " to " + gc.getEpilogue());
}
endOfBasicBlock = true;
}
//// APPEND INSTRUCTION.
/**
* Append an instruction to the current basic block.
*
* @param s instruction to append
*/
public void appendInstruction(Instruction s) {
currentBBLE.block.appendInstruction(s);
setSourcePosition(s);
lastInstr = s;
if (DBG_INSTR || DBG_SELECTED) db("-> " + s.getBytecodeIndex() + ":\t" + s);
}
//// MAKE A FIELD REFERENCE.
/**
* Make a field reference operand referring to the given field with the
* given type.
*
* @param f desired field
* @return a new location operand
*/
private LocationOperand makeStaticFieldRef(FieldReference f) {
return new LocationOperand(f);
}
private LocationOperand makeInstanceFieldRef(FieldReference f) {
return new LocationOperand(f);
}
//// MAKE A TYPE REFERENCE.
/**
* Make a type operand that refers to the given type.
*
* @param type desired type
* @return a new type operand
*/
private TypeOperand makeTypeOperand(TypeReference type) {
if (VM.VerifyAssertions) opt_assert(type != null);
return new TypeOperand(type);
}
/**
* Make a type operand that refers to the given type.
*
* @param type desired type
* @return a new type operand
*/
private TypeOperand makeTypeOperand(RVMType type) {
if (VM.VerifyAssertions) opt_assert(type != null);
return new TypeOperand(type);
}
private boolean couldCauseClassLoading(TypeReference typeRef) {
RVMType type = typeRef.peekType();
if (type == null) return true;
if (type.isInitialized()) return false;
if (type.isArrayType()) return !type.isResolved();
if (type.isClassType() && type.asClass().isInBootImage()) return false;
return true;
}
/**
* Fetch the value of the next operand, a constant, from the bytecode
* stream.
* @param index constant pool index
* @return the value of a literal constant from the bytecode stream,
* encoding as a constant IR operand
*/
public Operand getConstantOperand(int index) {
byte desc = bcodes.getConstantType(index);
RVMClass declaringClass = bcodes.getDeclaringClass();
switch (desc) {
case CP_INT:
return ClassLoaderProxy.getIntFromConstantPool(declaringClass, index);
case CP_FLOAT:
return ClassLoaderProxy.getFloatFromConstantPool(declaringClass, index);
case CP_STRING:
return ClassLoaderProxy.getStringFromConstantPool(declaringClass, index);
case CP_LONG:
return ClassLoaderProxy.getLongFromConstantPool(declaringClass, index);
case CP_DOUBLE:
return ClassLoaderProxy.getDoubleFromConstantPool(declaringClass, index);
case CP_CLASS:
return ClassLoaderProxy.getClassFromConstantPool(declaringClass, index);
default:
if (VM.VerifyAssertions) {
String msg = "invalid literal type: 0x" + Integer.toHexString(desc);
opt_assert(VM.NOT_REACHED, msg);
}
return null;
}
}
//// LOAD LOCAL VARIABLE ONTO STACK.
/**
* Simulates a load from a given local variable of an int.
*
* @param index local variable number
* @return {@code null} if no instruction is necessary, a move instruction
* otherwise
*/
private Instruction do_iload(int index) {
Operand r = getLocal(index);
if (VM.VerifyAssertions) opt_assert(r.isIntLike());
if (LOCALS_ON_STACK) {
push(r);
return null;
} else {
return _moveHelper(INT_MOVE, r, TypeReference.Int);
}
}
/**
* Simulates a load from a given local variable of a float.
*
* @param index local variable number
* @return {@code null} if no instruction is necessary, a move instruction
* otherwise
*/
private Instruction do_fload(int index) {
Operand r = getLocal(index);
if (VM.VerifyAssertions) opt_assert(r.isFloat());
if (LOCALS_ON_STACK) {
push(r);
return null;
} else {
return _moveHelper(FLOAT_MOVE, r, TypeReference.Float);
}
}
/**
* Simulates a load from a given local variable of a reference.
*
* @param index local variable number
* @return {@code null} if no instruction is necessary, a move instruction
* otherwise
*/
private Instruction do_aload(int index) {
Operand r = getLocal(index);
if (VM.VerifyAssertions && !(r.isRef() || r.isAddress())) {
String msg = r + " not ref, but a " + r.getType();
opt_assert(VM.NOT_REACHED, msg);
}
if (LOCALS_ON_STACK) {
push(r);
return null;
} else {
return _moveHelper(REF_MOVE, r, r.getType());
}
}
/**
* Simulates a load from a given local variable of a long.
*
* @param index local variable number
* @return {@code null} if no instruction is necessary, a move instruction
* otherwise
*/
private Instruction do_lload(int index) {
Operand r = getLocalDual(index);
if (VM.VerifyAssertions) opt_assert(r.isLong());
if (LOCALS_ON_STACK) {
pushDual(r);
return null;
} else {
return _moveDualHelper(LONG_MOVE, r, TypeReference.Long);
}
}
/**
* Simulates a load from a given local variable of a double.
*
* @param index local variable number
* @return {@code null} if no instruction is necessary, a move instruction
* otherwise
*/
private Instruction do_dload(int index) {
Operand r = getLocalDual(index);
if (VM.VerifyAssertions) opt_assert(r.isDouble());
if (LOCALS_ON_STACK) {
pushDual(r);
return null;
} else {
return _moveDualHelper(DOUBLE_MOVE, r, TypeReference.Double);
}
}
//// INCREMENT A LOCAL VARIABLE.
/**
* Simulates the incrementing of a given int local variable.
*
* @param index local variable number
* @param amount amount to increment by
* @return the generated instruction, never {@code null}
*/
private Instruction do_iinc(int index, int amount) {
Operand r = getLocal(index);
if (VM.VerifyAssertions) opt_assert(r.isIntLike());
if (LOCALS_ON_STACK) {
replaceLocalsOnStack(index, TypeReference.Int);
}
RegisterOperand op0 = gc.makeLocal(index, TypeReference.Int);
if (r instanceof IntConstantOperand) {
// do constant folding.
int res = amount + ((IntConstantOperand) r).value;
IntConstantOperand val = new IntConstantOperand(res);
if (CP_IN_LOCALS) {
setLocal(index, val);
} else {
setLocal(index, op0);
}
Instruction s = Move.create(INT_MOVE, op0, val);
setSourcePosition(s);
return s;
}
setLocal(index, op0);
return Binary.create(INT_ADD, op0, r, new IntConstantOperand(amount));
}
//// POP FROM STACK AND STORE INTO LOCAL VARIABLE.
/**
* Simulates a store into a given local variable of an int/long/double/float
*
* @param index local variable number
* @param op1 the value to store
* @return {@code null} if no instruction is necessary, the generated
* instruction otherwise
*/
private Instruction do_store(int index, Operand op1) {
TypeReference type = op1.getType();
boolean Dual = (type.isLongType() || type.isDoubleType());
if (LOCALS_ON_STACK) {
replaceLocalsOnStack(index, type);
}
if (ELIM_COPY_LOCALS) {
if (op1 instanceof RegisterOperand) {
RegisterOperand rop1 = (RegisterOperand) op1;
Register r1 = rop1.getRegister();
if (lastInstr != null &&
ResultCarrier.conforms(lastInstr) &&
ResultCarrier.hasResult(lastInstr) &&
!r1.isLocal() &&
r1 == ResultCarrier.getResult(lastInstr).getRegister()) {
if (DBG_ELIMCOPY) db("eliminated copy " + op1 + " to" + index);
RegisterOperand newop0 = gc.makeLocal(index, rop1);
ResultCarrier.setResult(lastInstr, newop0);
if (Dual) {
setLocalDual(index, newop0);
} else {
setLocal(index, newop0);
}
gc.getTemps().release(rop1);
return null;
}
}
}
RegisterOperand op0 =
(op1 instanceof RegisterOperand) ? gc.makeLocal(index, (RegisterOperand) op1) : gc.makeLocal(index,
type);
Operand set = op0;
if (CP_IN_LOCALS) {
set = (op1 instanceof RegisterOperand) ? op0 : op1;
}
if (Dual) {
setLocalDual(index, set);
} else {
setLocal(index, set);
}
Instruction s = Move.create(IRTools.getMoveOp(type), op0, op1);
setSourcePosition(s);
return s;
}
/**
* Simulates a store into a given local variable of an object ref.
*
* @param index local variable number
* @return {@code null} if no instruction is necessary, the generated
* instruction otherwise
*/
private Instruction do_astore(int index) {
Operand op1 = pop();
if (op1 instanceof ReturnAddressOperand) {
setLocal(index, op1);
return null;
}
boolean doConstantProp = false;
if ((op1 instanceof NullConstantOperand) || (op1 instanceof AddressConstantOperand)) {
doConstantProp = true;
}
TypeReference type = op1.getType();
if (LOCALS_ON_STACK) {
replaceLocalsOnStack(index, type);
}
if (ELIM_COPY_LOCALS) {
if (op1 instanceof RegisterOperand) {
RegisterOperand rop1 = (RegisterOperand) op1;
Register r1 = rop1.getRegister();
if (lastInstr != null &&
ResultCarrier.conforms(lastInstr) &&
ResultCarrier.hasResult(lastInstr) &&
!r1.isLocal() &&
r1 == ResultCarrier.getResult(lastInstr).getRegister()) {
if (DBG_ELIMCOPY) {
db("eliminated copy " + op1 + " to " + index);
}
RegisterOperand newop0 = gc.makeLocal(index, rop1);
ResultCarrier.setResult(lastInstr, newop0);
setLocal(index, newop0);
gc.getTemps().release(rop1);
return null;
}
}
}
RegisterOperand op0;
if (op1 instanceof RegisterOperand) {
RegisterOperand rop1 = (RegisterOperand) op1;
op0 = gc.makeLocal(index, rop1);
if (hasGuard(rop1)) {
RegisterOperand g0 = gc.makeNullCheckGuard(op0.getRegister());
appendInstruction(Move.create(GUARD_MOVE, g0.copyRO(), copyGuardFromOperand(rop1)));
setGuardForRegOp(op0, g0);
}
} else {
op0 = gc.makeLocal(index, type);
}
if (CP_IN_LOCALS) {
setLocal(index, doConstantProp ? op1 : op0);
} else {
setLocal(index, op0);
}
Instruction s = Move.create(REF_MOVE, op0, op1);
setSourcePosition(s);
return s;
}
//// PUSH OPERAND ONTO THE STACK.
/**
* Push a single width operand (int, float, ref, ...) on the simulated stack.
*
* @param r operand to push
*/
public void push(Operand r) {
if (VM.VerifyAssertions) opt_assert(r.instruction == null);
stack.push(r);
}
/**
* Push a double width operand (long, double) on the simulated stack.
*
* @param r operand to push
*/
void pushDual(Operand r) {
if (VM.VerifyAssertions) opt_assert(r.instruction == null);
stack.push(DUMMY);
stack.push(r);
}
/**
* Push an operand of the specified type on the simulated stack.
*
* @param r operand to push
* @param type data type of operand
*/
void push(Operand r, TypeReference type) {
if (VM.VerifyAssertions) opt_assert(r.instruction == null);
if (type.isVoidType()) {
return;
}
if (type.isLongType() || type.isDoubleType()) {
pushDual(r);
} else {
push(r);
}
}
/**
* Pushes a copy of the given operand onto simulated stack.
*
* @param op1 operand to push
* @return always {@code null}
*/
private Instruction pushCopy(Operand op1) {
if (VM.VerifyAssertions) opt_assert(op1.instruction == null);
if (op1 instanceof RegisterOperand) {
RegisterOperand reg = (RegisterOperand) op1;
if (!reg.getRegister().isLocal()) {
lastInstr = null; // to prevent eliminating this temporary.
}
stack.push(reg.copy());
} else {
stack.push(op1.copy());
}
return null;
}
//// POP OPERAND FROM THE STACK.
/**
* Pops an operand from the stack. No type checking is performed.
* @return the popped operand
*/
Operand pop() {
return stack.pop();
}
/**
* Pops an int operand from the stack.
* @return the popped operand
*/
public Operand popInt() {
Operand r = pop();
if (VM.VerifyAssertions) opt_assert(r.isIntLike());
return r;
}
/**
* Pops a float operand from the stack.
* @return the popped operand
*/
Operand popFloat() {
Operand r = pop();
if (VM.VerifyAssertions) opt_assert(r.isFloat());
return r;
}
/**
* Pops a ref operand from the stack.
* @return the popped operand
*/
public Operand popRef() {
Operand r = pop();
if (VM.VerifyAssertions) opt_assert(r.isRef() || r.isAddress());
return r;
}
/**
* Pops an address operand from the stack.
* @return the popped operand
*/
public Operand popAddress() {
Operand r = pop();
if (VM.VerifyAssertions) opt_assert(r.isAddress());
return r;
}
/**
* Pops a long operand from the stack.
* @return the popped operand
*/
Operand popLong() {
Operand r = pop();
if (VM.VerifyAssertions) opt_assert(r.isLong());
popDummy();
return r;
}
/**
* Pops a double operand from the stack.
* @return the popped operand
*/
Operand popDouble() {
Operand r = pop();
if (VM.VerifyAssertions) opt_assert(r.isDouble());
popDummy();
return r;
}
/**
* Pops a dummy operand from the stack.
*/
void popDummy() {
Operand r = pop();
if (VM.VerifyAssertions) opt_assert(r == DUMMY);
}
/**
* Pops an operand of the given type from the stack.
* @param type the expected type of the operand
* @return the popped operand
*/
Operand pop(TypeReference type) {
Operand r = pop();
// Can't assert the following due to approximations by
// ClassLoaderProxy.findCommonSuperclass
// if (VM.VerifyAssertions) assertIsType(r, type);
// Avoid upcasts of magic types to regular j.l.Objects
// if (VM.VerifyAssertions && (type == TypeReference.JavaLangObject))
// opt_assert(!r.getType().isMagicType());
if (VM.VerifyAssertions) {
if ((type == TypeReference.JavaLangObject) &&
(r.getType().isMagicType()) &&
!gc.getMethod().getDeclaringClass().getTypeRef().isMagicType()) {
throw new OptimizingCompilerException.IllegalUpcast(r.getType());
}
}
if (type.isLongType() || type.isDoubleType()) {
popDummy();
}
return r;
}
/**
* Pop an int from the stack to be used in a shift. A shift only uses the
* bottom 5 or 6 bits of an int so the upper bits must be masked to conform
* with the semantics of xx_SHx. NB the opt compiler shift operators allow that
* {@code (x << 16) << 16 == x << 32}, which isn't true in the bytecode
* @param longShift is this a shift of a long
* @return the operand containing the amount to shift by
*/
private Operand popShiftInt(boolean longShift) {
Operand op = popInt();
if (op instanceof IntConstantOperand) {
int val = op.asIntConstant().value;
if (!longShift) {
if ((val > 0) && (val <= 31)) {
return op;
} else {
return new IntConstantOperand(val & 0x1F);
}
} else {
if ((val > 0) && (val <= 63)) {
return op;
} else {
return new IntConstantOperand(val & 0x3F);
}
}
} else {
Instruction s =
_binaryHelper(INT_AND, op, new IntConstantOperand(longShift ? 0x3F : 0x1f), TypeReference.Int);
if (s != null && !currentBBLE.isSelfRegen()) {
appendInstruction(s);
}
return popInt();
}
}
//// SUBROUTINES.
private Instruction _jsrHelper(int offset) {
// (1) notify the BBSet that we have reached a JSR bytecode.
// This enables the more complex JSR-aware implementation of
// BBSet.getOrCreateBlock.
blocks.seenJSR();
// (2) push return address on expression stack
push(new ReturnAddressOperand(bcodes.index()));
// (3) generate GOTO to subroutine body.
BranchOperand branch = generateTarget(offset);
return Goto.create(GOTO, branch);
}
private Instruction _retHelper(int var) {
// (1) consume the return address from the specified local variable
Operand local = getLocal(var);
ReturnAddressOperand ra = (ReturnAddressOperand) local;
setLocal(var, null); // must set local null before calling getOrCreateBlock!!
BasicBlockLE rb = getOrCreateBlock(ra.retIndex);
// (2) generate a GOTO to the return site.
currentBBLE.block.insertOut(rb.block);
endOfBasicBlock = true;
if (DBG_CFG || DBG_SELECTED) db("Added CFG edge from " + currentBBLE.block + " to " + rb.block);
return Goto.create(GOTO, rb.block.makeJumpTarget());
}
//// GET TYPE OF AN OPERAND.
/**
* Returns the data type of the given operand, assuming that the operand is
* an array reference. (and not a {@code null} constant.)
*
* @param op operand to get type of
* @return operand's data type
*/
public TypeReference getArrayTypeOf(Operand op) {
if (VM.VerifyAssertions) opt_assert(!op.isDefinitelyNull());
return op.getType();
}
/**
* Returns the data type of the given operand, assuming that the operand is
* a reference. (and not a {@code null} constant.)
*
* @param op operand to get type of
* @return operand's data type
*/
private TypeReference getRefTypeOf(Operand op) {
if (VM.VerifyAssertions) opt_assert(!op.isDefinitelyNull());
return op.getType();
}
//// HELPER FUNCTIONS FOR ASSERTION VERIFICATION
/**
* Assert that the given operand is of the given type, or of
* a subclass of the given type.
*
* @param op operand to check
* @param type expected type of operand
*/
public void assertIsType(Operand op, TypeReference type) {
if (VM.VerifyAssertions) {
if (op.isDefinitelyNull()) {
opt_assert(type.isReferenceType());
} else if (op.isIntLike()) {
opt_assert(type.isIntLikeType());
} else {
TypeReference type1 = op.getType();
if (ClassLoaderProxy.includesType(type, type1) == NO) {
String msg = op + ": " + type + " is not assignable with " + type1;
opt_assert(VM.NOT_REACHED, msg);
}
}
}
}
/**
* Assert that the given child type is a subclass of the given parent type.
*
* @param parentType parent type
* @param childType child type
*/
private void assertIsAssignable(TypeReference parentType, TypeReference childType) {
if (VM.VerifyAssertions) {
if (childType.isUnboxedType()) {
//TODO: This should be opt_assert(gc.method.getReturnType() == retType.isUnboxedType());
// but all word types are converted into addresses and thus the assertion fails. This should be fixed.
opt_assert(parentType.isUnboxedType());
} else {
// fudge to deal with conservative approximation
// in ClassLoaderProxy.findCommonSuperclass
if (childType != TypeReference.JavaLangObject) {
if (ClassLoaderProxy.includesType(parentType, childType) == NO) {
VM.sysWriteln("type reference equality " + (parentType == childType));
Enumeration<InlineSequence> callHierarchy = gc.getInlineSequence().enumerateFromRoot();
while (callHierarchy.hasMoreElements()) {
VM.sysWriteln(callHierarchy.nextElement().toString());
}
String msg = parentType + " not assignable with " + childType;
opt_assert(VM.NOT_REACHED, msg);
}
}
}
}
}
//// DEBUGGING.
/**
* Print a debug string to the sysWrite stream
*
* @param val string to print
*/
private void db(String val) {
VM.sysWriteln("IRGEN " + bcodes.getDeclaringClass() + "." + gc.getMethod().getName() + ":" + val);
}
/**
* @return a string representation of the current basic block set.
*/
private String printBlocks() {
StringBuilder res = new StringBuilder();
for (Enumeration<BasicBlockLE> e = blocks.contents(); e.hasMoreElements();) {
BasicBlockLE b = e.nextElement();
if (b == currentBBLE) {
res.append("*");
}
res.append(b.toString());
res.append(" ");
}
return res.toString();
}
//// GENERATE CHECK INSTRUCTIONS.
public static boolean isNonNull(Operand op) {
if (op instanceof RegisterOperand) {
RegisterOperand rop = (RegisterOperand) op;
if (VM.VerifyAssertions) {
opt_assert((rop.getGuard() == null) ||
(rop.getGuard() instanceof RegisterOperand) ||
(rop.getGuard() instanceof TrueGuardOperand));
}
return rop.getGuard() != null;
} else {
return op.isConstant();
}
}
public static boolean hasGuard(RegisterOperand rop) {
return rop.getGuard() != null;
}
public static boolean hasLessConservativeGuard(RegisterOperand rop1, RegisterOperand rop2) {
if (rop1.getGuard() == rop2.getGuard()) {
return false;
}
if (rop1.getGuard() instanceof Operand) {
if (rop2.getGuard() instanceof Operand) {
Operand op1 = rop1.getGuard();
Operand op2 = rop2.getGuard();
if (op2 instanceof TrueGuardOperand) {
// rop2 is top therefore rop1 can't be less conservative!
return false;
} else {
return !(op1.similar(op2));
}
} else {
return true;
}
} else {
// rop1 is bottom, therefore is most conservative guard possible
return false;
}
}
public void markGuardlessNonNull(RegisterOperand rop) {
RegisterOperand g = gc.makeNullCheckGuard(rop.getRegister());
appendInstruction(Move.create(GUARD_MOVE, g, new TrueGuardOperand()));
rop.setGuard(g.copy());
}
public static Operand copyGuardFromOperand(Operand op) {
if (op instanceof RegisterOperand) {
RegisterOperand rop = (RegisterOperand) op;
if (VM.VerifyAssertions) {
opt_assert((rop.getGuard() == null) ||
(rop.getGuard() instanceof RegisterOperand) ||
(rop.getGuard() instanceof TrueGuardOperand));
}
if (rop.getGuard() == null) {
return null;
} else {
return rop.getGuard().copy();
}
}
if (VM.VerifyAssertions) {
opt_assert(op.isConstant());
}
return new TrueGuardOperand();
}
public static void setGuardForRegOp(RegisterOperand rop, Operand guard) {
rop.setGuard(guard);
}
private void setCurrentGuard(Operand guard) {
if (currentGuard instanceof RegisterOperand) {
if (VM.VerifyAssertions) {
opt_assert(!(guard instanceof TrueGuardOperand));
}
// shouldn't happen given current generation --dave.
RegisterOperand combined = gc.getTemps().makeTempValidation();
appendInstruction(Binary.create(GUARD_COMBINE, combined, getCurrentGuard(), guard.copy()));
currentGuard = combined;
} else {
currentGuard = guard;
}
}
public void clearCurrentGuard() {
currentGuard = null;
}
public Operand getCurrentGuard() {
// This check is needed for when guards are (unsafely) turned off
if (currentGuard != null) {
return currentGuard.copy();
}
return null;
}
/**
* Generates a null-check instruction for the given operand.
* @param ref the reference to check for null
* @return {@code true} if an unconditional throw is generated, {@code false} otherwise
*/
public boolean do_NullCheck(Operand ref) {
if (gc.noNullChecks()) {
setCurrentGuard(new TrueGuardOperand());
return false;
}
if (ref.isDefinitelyNull()) {
if (DBG_CF) db("generating definite exception: null_check of definitely null");
endOfBasicBlock = true;
rectifyStateWithNullPtrExceptionHandler();
appendInstruction(Trap.create(TRAP, gc.getTemps().makeTempValidation(), TrapCodeOperand.NullPtr()));
return true;
}
if (ref instanceof RegisterOperand) {
RegisterOperand rop = (RegisterOperand) ref;
if (hasGuard(rop)) {
Operand guard = copyGuardFromOperand(rop);
setCurrentGuard(guard);
if (DBG_ELIMNULL) {
db("null check of " + ref + " is not necessary; guarded by " + guard);
}
return false;
}
// rop is possibly null, insert the null check,
// rectify with exception handler, update the guard state.
RegisterOperand guard = gc.makeNullCheckGuard(rop.getRegister());
appendInstruction(NullCheck.create(NULL_CHECK, guard, ref.copy()));
rectifyStateWithNullPtrExceptionHandler();
setCurrentGuard(guard);
setGuardForRegOp(rop, guard);
if (DBG_ELIMNULL) db(rop + " is guarded by " + guard);
// Now, try to leverage this null check by updating
// other unguarded (and thus potentially null)
// RegisterOperands representing the same Register.
if (rop.getRegister().isLocal()) {
// We want to learn that downstream of this nullcheck, other
// uses of this local variable will also be non-null.
// BUT, we MUST NOT just directly set the guard of the appropriate
// element of our locals array (operands in the local array
// may appear in previously generated instructions).
// Therefore we call getLocal (which internally makes a copy),
// mark the copy with the new guard
// and finally store the copy back into the local state.
int number = gc.getLocalNumberFor(rop.getRegister(), rop.getType());
if (number != -1) {
Operand loc = getLocal(number);
if (loc instanceof RegisterOperand) {
if (DBG_ELIMNULL) {
db("setting local #" + number + "(" + loc + ") to non-null");
}
setGuardForRegOp((RegisterOperand) loc, guard);
}
setLocal(number, loc);
}
}
// At least within this basic block we know that all subsequent uses
// of ref will be non null, since they are guarded by the null check
// instruction we just inserted. Update all RegisterOperands with
// this register currently on the expression stack appropriately.
// Stack rectification will ensure that we don't propagate this
// non-nullness to a use that is not dominated by the null check in
// the current basic block.
for (int i = stack.getSize() - 1; i >= 0; --i) {
Operand sop = stack.getFromTop(i);
if (sop instanceof RegisterOperand) {
RegisterOperand sreg = (RegisterOperand) sop;
if (sreg.getRegister() == rop.getRegister()) {
if (hasGuard(sreg)) {
if (DBG_ELIMNULL) {
db(sreg + " on stack already with guard " + copyGuardFromOperand(sreg));
}
} else {
if (DBG_ELIMNULL) {
db("setting " + sreg + " on stack to be guarded by " + guard);
}
setGuardForRegOp(sreg, guard);
}
}
}
}
return false;
} else {
// cannot be null becuase it's not in a register.
if (DBG_ELIMNULL) {
db("skipped generation of a null-check instruction for non-register " + ref);
}
setCurrentGuard(new TrueGuardOperand());
return false;
}
}
/**
* Generates a boundscheck instruction for the given operand and index.
* @param ref the array reference
* @param index the array index
* @return {@code true} if an unconditional throw is generated, {@code false} otherwise
*/
public boolean do_BoundsCheck(Operand ref, Operand index) {
// Unsafely eliminate all bounds checks
if (gc.noBoundsChecks()) {
return false;
}
RegisterOperand guard = gc.getTemps().makeTempValidation();
appendInstruction(BoundsCheck.create(BOUNDS_CHECK, guard, ref.copy(), index.copy(), getCurrentGuard()));
setCurrentGuard(guard);
rectifyStateWithArrayBoundsExceptionHandler();
return false;
}
/**
* Generates a check for 0 for the given operand
* @param div the value to check
* @return {@code true} if an unconditional trap is generated, {@code false} otherwise
*/
private boolean do_IntZeroCheck(Operand div) {
if (div instanceof IntConstantOperand) {
if (((IntConstantOperand) div).value == 0) {
endOfBasicBlock = true;
rectifyStateWithArithmeticExceptionHandler();
appendInstruction(Trap.create(TRAP, gc.getTemps().makeTempValidation(), TrapCodeOperand.DivByZero()));
return true;
} else {
if (DBG_CF) {
db("skipped gen of int_zero_check of " + div.asIntConstant().value);
}
setCurrentGuard(new TrueGuardOperand());
return false;
}
}
RegisterOperand guard = gc.getTemps().makeTempValidation();
appendInstruction(ZeroCheck.create(INT_ZERO_CHECK, guard, div.copy()));
setCurrentGuard(guard);
rectifyStateWithArithmeticExceptionHandler();
return false;
}
/**
* Generates a checks for 0 for the given operand
* @param div the value to check
* @return {@code true} if an unconditional trap is generated, {@code false} otherwise
*/
private boolean do_LongZeroCheck(Operand div) {
if (div instanceof LongConstantOperand) {
if (((LongConstantOperand) div).value == 0) {
endOfBasicBlock = true;
rectifyStateWithArithmeticExceptionHandler();
appendInstruction(Trap.create(TRAP, gc.getTemps().makeTempValidation(), TrapCodeOperand.DivByZero()));
return true;
} else {
if (DBG_CF) {
db("skipped gen of long_zero_check of " + div.asLongConstant().value);
}
setCurrentGuard(new TrueGuardOperand());
return false;
}
}
RegisterOperand guard = gc.getTemps().makeTempValidation();
appendInstruction(ZeroCheck.create(LONG_ZERO_CHECK, guard, div.copy()));
setCurrentGuard(guard);
rectifyStateWithArithmeticExceptionHandler();
return false;
}
/**
* Generate a storecheck for the given array and elem
* @param ref the array reference
* @param elem the element to be written to the array
* @param elemType the type of the array references elements
* @return {@code true} if an unconditional throw is generated, {@code false} otherwise
*/
private boolean do_CheckStore(Operand ref, Operand elem, TypeReference elemType) {
if (gc.noCheckStoreChecks()) return false;
if (CF_CHECKSTORE) {
// NOTE: BE WARY OF ADDITIONAL OPTIMZATIONS.
// ARRAY SUBTYPING IS SUBTLE (see JLS 10.10) --dave
if (elem.isDefinitelyNull()) {
if (DBG_TYPE) db("skipping checkstore of null constant");
return false;
}
if (elemType.isArrayType()) {
TypeReference elemType2 = elemType;
do {
elemType2 = elemType2.getArrayElementType();
} while (elemType2.isArrayType());
RVMType et2 = elemType2.peekType();
if (et2 != null) {
if (et2.isPrimitiveType() || et2.isUnboxedType() || ((RVMClass) et2).isFinal()) {
TypeReference myElemType = getRefTypeOf(elem);
if (myElemType == elemType) {
if (DBG_TYPE) {
db("eliminating checkstore to an array with a final element type " + elemType);
}
return false;
} else {
// run time check is still necessary
}
}
}
} else {
// elemType is class
RVMType et = elemType.peekType();
if (et != null && ((RVMClass) et).isFinal()) {
if (getRefTypeOf(elem) == elemType) {
if (DBG_TYPE) {
db("eliminating checkstore to an array with a final element type " + elemType);
}
return false;
} else {
// run time check is still necessary
}
}
}
}
RegisterOperand guard = gc.getTemps().makeTempValidation();
if (isNonNull(elem)) {
RegisterOperand newGuard = gc.getTemps().makeTempValidation();
appendInstruction(Binary.create(GUARD_COMBINE, newGuard, copyGuardFromOperand(elem), getCurrentGuard()));
appendInstruction(StoreCheck.create(OBJARRAY_STORE_CHECK_NOTNULL,
guard,
ref.copy(),
elem.copy(),
newGuard.copy()));
} else {
appendInstruction(StoreCheck.create(OBJARRAY_STORE_CHECK, guard, ref.copy(), elem.copy(), getCurrentGuard()));
}
setCurrentGuard(guard);
rectifyStateWithArrayStoreExceptionHandler();
return false;
}
//// GENERATE BRANCHING INSTRUCTIONS.
/**
* Gets or creates a block at the specified target.
* Rectifies current state with target state.
* Instructions to rectify state are appended to currentBBLE.
* If the target is between bcodes.index() and runoff, runoff is
* updated to be target.
*
* @param target target index
* @return a block, never {@code null}
*/
private BasicBlockLE getOrCreateBlock(int target) {
return getOrCreateBlock(target, currentBBLE, stack, _localState);
}
/**
* Get or create a block at the specified target.
* If simStack is non-{@code null}, rectifies stack state with target stack state.
* If simLocals is non-{@code null}, rectifies local state with target local state.
* Any instructions needed to rectify stack/local state are appended to from.
* If the target is between bcodes.index() and runoff, runoff is
* updated to be target.
*
* @param target target index
* @param from the block from which control is being transfered
* and to which stack rectification instructions are added.
* @param simStack stack state to rectify, or {@code null}
* @param simLocals local state to rectify, or {@code null}
* @return a block, never {@code null}
*/
private BasicBlockLE getOrCreateBlock(int target, BasicBlockLE from, OperandStack simStack, Operand[] simLocals) {
if ((target > bcodes.index()) && (target < runoff)) {
if (DBG_BB || DBG_SELECTED) db("updating runoff from " + runoff + " to " + target);
runoff = target;
}
return blocks.getOrCreateBlock(target, from, simStack, simLocals);
}
private BranchOperand generateTarget(int offset) {
BasicBlockLE targetbble = getOrCreateBlock(offset + instrIndex);
currentBBLE.block.insertOut(targetbble.block);
endOfBasicBlock = true;
if (DBG_CFG || DBG_SELECTED) {
db("Added CFG edge from " + currentBBLE.block + " to " + targetbble.block);
}
return targetbble.block.makeJumpTarget();
}
// GOTO
private Instruction _gotoHelper(int offset) {
return Goto.create(GOTO, generateTarget(offset));
}
// helper function for if?? bytecodes
private Instruction _intIfHelper(ConditionOperand cond) {
int offset = bcodes.getBranchOffset();
Operand op0 = popInt();
if (offset == 3) {
return null; // remove frivolous IFs
}
if (CF_INTIF && op0 instanceof IntConstantOperand) {
int c = cond.evaluate(((IntConstantOperand) op0).value, 0);
if (c == ConditionOperand.TRUE) {
if (DBG_CF) {
db(cond + ": changed branch to goto because predicate (" + op0 + ") is constant true");
}
return _gotoHelper(offset);
} else if (c == ConditionOperand.FALSE) {
if (DBG_CF) {
db(cond + ": eliminated branch because predicate (" + op0 + ") is constant false");
}
return null;
}
}
fallThrough = true;
if (!(op0 instanceof RegisterOperand)) {
if (DBG_CF) db("generated int_ifcmp of " + op0 + " with 0");
RegisterOperand guard = gc.getTemps().makeTempValidation();
return IfCmp.create(INT_IFCMP,
guard,
op0,
new IntConstantOperand(0),
cond,
generateTarget(offset),
gc.getConditionalBranchProfileOperand(instrIndex - bciAdjustment, offset < 0));
}
RegisterOperand val = (RegisterOperand) op0;
BranchOperand branch = null;
if (lastInstr != null) {
switch (lastInstr.getOpcode()) {
case INSTANCEOF_opcode:
case INSTANCEOF_UNRESOLVED_opcode: {
if (DBG_TYPE) db("last instruction was instanceof");
RegisterOperand res = InstanceOf.getResult(lastInstr);
if (DBG_TYPE) db("result was in " + res + ", we are checking " + val);
if (val.getRegister() != res.getRegister()) {
break; // not our value
}
Operand ref = InstanceOf.getRef(lastInstr);
// should've been constant folded anyway
if (!(ref instanceof RegisterOperand)) {
break;
}
RegisterOperand guard = null;
// Propagate types and non-nullness along the CFG edge where we
// know that refReg is an instanceof type2
RegisterOperand refReg = (RegisterOperand) ref;
TypeReference type2 = InstanceOf.getType(lastInstr).getTypeRef();
if (cond.isNOT_EQUAL()) {
// IS an instance of on the branch-taken edge
boolean generated = false;
if (refReg.getRegister().isLocal()) {
int locNum = gc.getLocalNumberFor(refReg.getRegister(), refReg.getType());
if (locNum != -1) {
Operand loc = getLocal(locNum);
if (loc instanceof RegisterOperand) {
if (DBG_TYPE) {
db(val +
" is from instanceof test, propagating new type of " +
refReg +
" (" +
type2 +
") to basic block at " +
offset);
}
RegisterOperand locr = (RegisterOperand) loc;
RegisterOperand tlocr = locr.copyU2U();
guard = gc.makeNullCheckGuard(tlocr.getRegister());
setGuardForRegOp(tlocr, guard.copyD2U());
tlocr.clearDeclaredType();
tlocr.clearPreciseType();
tlocr.setType(type2);
setLocal(locNum, tlocr);
branch = generateTarget(offset);
generated = true;
setLocal(locNum, locr);
}
}
}
if (!generated) {
branch = generateTarget(offset);
}
} else if (cond.isEQUAL()) {
// IS an instance of on the fallthrough edge.
branch = generateTarget(offset);
if (refReg.getRegister().isLocal()) {
int locNum = gc.getLocalNumberFor(refReg.getRegister(), refReg.getType());
if (locNum != -1) {
Operand loc = getLocal(locNum);
if (loc instanceof RegisterOperand) {
if (DBG_TYPE) {
db(val +
" is from instanceof test, propagating new type of " +
refReg +
" (" +
type2 +
") along fallthrough edge");
}
RegisterOperand locr = (RegisterOperand) loc;
guard = gc.makeNullCheckGuard(locr.getRegister());
setGuardForRegOp(locr, guard.copyD2U());
locr.clearDeclaredType();
locr.clearPreciseType();
locr.setType(type2);
setLocal(locNum, loc);
}
}
}
}
if (guard == null) {
guard = gc.getTemps().makeTempValidation();
}
return IfCmp.create(INT_IFCMP,
guard,
val,
new IntConstantOperand(0),
cond,
branch,
gc.getConditionalBranchProfileOperand(instrIndex - bciAdjustment, offset < 0));
}
case INSTANCEOF_NOTNULL_opcode: {
if (DBG_TYPE) db("last instruction was instanceof");
RegisterOperand res = InstanceOf.getResult(lastInstr);
if (DBG_TYPE) {
db("result was in " + res + ", we are checking " + val);
}
if (val.getRegister() != res.getRegister()) {
break; // not our value
}
Operand ref = InstanceOf.getRef(lastInstr);
// should've been constant folded anyway
if (!(ref instanceof RegisterOperand)) {
break;
}
// Propagate types along the CFG edge where we know that
// refReg is an instanceof type2
RegisterOperand refReg = (RegisterOperand) ref;
TypeReference type2 = InstanceOf.getType(lastInstr).getTypeRef();
if (cond.isNOT_EQUAL()) {
// IS an instance of on the branch-taken edge
boolean generated = false;
if (refReg.getRegister().isLocal()) {
int locNum = gc.getLocalNumberFor(refReg.getRegister(), refReg.getType());
if (locNum != -1) {
Operand loc = getLocal(locNum);
if (loc instanceof RegisterOperand) {
if (DBG_TYPE) {
db(val +
" is from instanceof test, propagating new type of " +
refReg +
" (" +
type2 +
") to basic block at " +
offset);
}
RegisterOperand locr = (RegisterOperand) loc;
RegisterOperand tlocr = locr.copyU2U();
tlocr.clearDeclaredType();
tlocr.clearPreciseType();
tlocr.setType(type2);
setLocal(locNum, tlocr);
branch = generateTarget(offset);
generated = true;
setLocal(locNum, locr);
}
}
}
if (!generated) {
branch = generateTarget(offset);
}
} else if (cond.isEQUAL()) {
// IS an instance of on the fallthrough edge.
branch = generateTarget(offset);
if (refReg.getRegister().isLocal()) {
int locNum = gc.getLocalNumberFor(refReg.getRegister(), refReg.getType());
if (locNum != -1) {
Operand loc = getLocal(locNum);
if (loc instanceof RegisterOperand) {
if (DBG_TYPE) {
db(val +
" is from instanceof test, propagating new type of " +
refReg +
" (" +
type2 +
") along fallthrough edge");
}
RegisterOperand locr = (RegisterOperand) loc;
locr.setType(type2);
locr.clearDeclaredType();
setLocal(locNum, loc);
}
}
}
}
RegisterOperand guard = gc.getTemps().makeTempValidation();
return IfCmp.create(INT_IFCMP,
guard,
val,
new IntConstantOperand(0),
cond,
branch,
gc.getConditionalBranchProfileOperand(instrIndex - bciAdjustment, offset < 0));
}
case DOUBLE_CMPG_opcode:
case DOUBLE_CMPL_opcode:
case FLOAT_CMPG_opcode:
case FLOAT_CMPL_opcode:
case LONG_CMP_opcode: {
RegisterOperand res = Binary.getResult(lastInstr);
if (val.getRegister() != res.getRegister()) {
break; // not our value
}
Operator operator = null;
switch (lastInstr.getOpcode()) {
case DOUBLE_CMPG_opcode:
cond.translateCMPG();
operator = DOUBLE_IFCMP;
break;
case DOUBLE_CMPL_opcode:
cond.translateCMPL();
operator = DOUBLE_IFCMP;
break;
case FLOAT_CMPG_opcode:
cond.translateCMPG();
operator = FLOAT_IFCMP;
break;
case FLOAT_CMPL_opcode:
cond.translateCMPL();
operator = FLOAT_IFCMP;
break;
case LONG_CMP_opcode:
operator = LONG_IFCMP;
break;
default:
OptimizingCompilerException.UNREACHABLE();
break;
}
Operand val1 = Binary.getClearVal1(lastInstr);
Operand val2 = Binary.getClearVal2(lastInstr);
if (!(val1 instanceof RegisterOperand)) {
// swap operands
Operand temp = val1;
val1 = val2;
val2 = temp;
cond = cond.flipOperands();
}
lastInstr.remove();
lastInstr = null;
branch = generateTarget(offset);
RegisterOperand guard = gc.getTemps().makeTempValidation();
return IfCmp.create(operator,
guard,
val1,
val2,
cond,
branch,
gc.getConditionalBranchProfileOperand(instrIndex - bciAdjustment, offset < 0));
}
default:
// Fall through and Insert INT_IFCMP
break;
}
}
branch = generateTarget(offset);
RegisterOperand guard = gc.getTemps().makeTempValidation();
return IfCmp.create(INT_IFCMP,
guard,
val,
new IntConstantOperand(0),
cond,
branch,
gc.getConditionalBranchProfileOperand(instrIndex - bciAdjustment, offset < 0));
}
// helper function for if_icmp?? bytecodes
private Instruction _intIfCmpHelper(ConditionOperand cond) {
int offset = bcodes.getBranchOffset();
Operand op1 = popInt();
Operand op0 = popInt();
if (offset == 3) {
return null; // remove frivolous INF_IFCMPs
}
if (!(op0 instanceof RegisterOperand)) {
// swap operands
Operand temp = op0;
op0 = op1;
op1 = temp;
cond = cond.flipOperands();
}
if (CF_INTIFCMP && (op0 instanceof IntConstantOperand) && (op1 instanceof IntConstantOperand)) {
int c = cond.evaluate(((IntConstantOperand) op0).value, ((IntConstantOperand) op1).value);
if (c == ConditionOperand.TRUE) {
if (DBG_CF) {
db(cond + ": changed branch to goto because predicate (" + op0 + ", " + op1 + ") is constant true");
}
return _gotoHelper(offset);
} else if (c == ConditionOperand.FALSE) {
if (DBG_CF) {
db(cond + ": eliminated branch because predicate (" + op0 + "," + op1 + ") is constant false");
}
return null;
}
}
fallThrough = true;
RegisterOperand guard = gc.getTemps().makeTempValidation();
return IfCmp.create(INT_IFCMP,
guard,
op0,
op1,
cond,
generateTarget(offset),
gc.getConditionalBranchProfileOperand(instrIndex - bciAdjustment, offset < 0));
}
// helper function for ifnull/ifnonnull bytecodes
private Instruction _refIfNullHelper(ConditionOperand cond) {
if (VM.VerifyAssertions) opt_assert(cond.isEQUAL() || cond.isNOT_EQUAL());
int offset = bcodes.getBranchOffset();
Operand op0 = popRef();
if (offset == 3) {
return null; // remove frivolous REF_IFs
}
if (CF_REFIF) {
if (op0.isDefinitelyNull()) {
if (cond.isEQUAL()) {
if (DBG_CF) {
db(cond + ": changed branch to goto because predicate is true");
}
return _gotoHelper(offset);
} else {
if (DBG_CF) {
db(cond + ": eliminated branch because predicate is false");
}
return null;
}
}
if (isNonNull(op0)) {
if (cond.isNOT_EQUAL()) {
if (DBG_CF) {
db(cond + ": changed branch to goto because predicate is true");
}
return _gotoHelper(offset);
} else {
if (DBG_CF) {
db(cond + ": eliminated branch because predicate is false");
}
return null;
}
}
}
RegisterOperand ref = (RegisterOperand) op0;
BranchOperand branch = null;
RegisterOperand guard = null;
// Check for incorrect null checks on unboxed types
if (ref.getType().isUnboxedType()) {
throw new OptimizingCompilerException("Detected incorrect null check of unboxed type in " +
gc.getMethod() + " at bytecode index " + instrIndex + " from class " +
gc.getMethod().getDeclaringClass() +
" . Use the methods provided on the unboxed types to do null checks!");
}
if (cond.isEQUAL()) {
branch = generateTarget(offset);
if (ref.getRegister().isLocal()) {
int locNum = gc.getLocalNumberFor(ref.getRegister(), ref.getType());
if (locNum != -1) {
Operand loc = getLocal(locNum);
if (loc instanceof RegisterOperand) {
RegisterOperand locr = (RegisterOperand) loc;
guard = gc.makeNullCheckGuard(locr.getRegister());
setGuardForRegOp(locr, guard.copyD2U());
setLocal(locNum, loc);
}
}
}
} else {
boolean generated = false;
if (ref.getRegister().isLocal()) {
int locNum = gc.getLocalNumberFor(ref.getRegister(), ref.getType());
if (locNum != -1) {
Operand loc = getLocal(locNum);
if (loc instanceof RegisterOperand) {
RegisterOperand locr = (RegisterOperand) loc;
RegisterOperand tlocr = locr.copyU2U();
guard = gc.makeNullCheckGuard(locr.getRegister());
setGuardForRegOp(tlocr, guard.copyD2U());
setLocal(locNum, tlocr);
branch = generateTarget(offset);
generated = true;
setLocal(locNum, locr);
}
}
}
if (!generated) {
branch = generateTarget(offset);
}
}
fallThrough = true;
if (guard == null) {
guard = gc.getTemps().makeTempValidation();
}
return IfCmp.create(REF_IFCMP,
guard,
ref,
new NullConstantOperand(),
cond,
branch,
gc.getConditionalBranchProfileOperand(instrIndex - bciAdjustment, offset < 0));
}
// helper function for if_acmp?? bytecodes
private Instruction _refIfCmpHelper(ConditionOperand cond) {
if (VM.VerifyAssertions) opt_assert(cond.isEQUAL() || cond.isNOT_EQUAL());
int offset = bcodes.getBranchOffset();
Operand op1 = popRef();
Operand op0 = popRef();
if (offset == 3) {
return null; // remove frivolous REF_IFCMPs
}
if (!(op0 instanceof RegisterOperand)) {
// swap operands
Operand temp = op0;
op0 = op1;
op1 = temp;
cond = cond.flipOperands();
}
if (CF_REFIFCMP && op0.isDefinitelyNull() && op1.isDefinitelyNull()) {
if (cond.isEQUAL()) {
if (DBG_CF) {
db(cond + ": changed branch to goto because predicate is true");
}
return _gotoHelper(offset);
} else {
if (DBG_CF) {
db(cond + ": eliminated branch because predicate is false");
}
return null;
}
}
// Check for incorrect comparison operators on unboxed types
if (op0.isRegister()) {
RegisterOperand op0Reg = op0.asRegister();
if (op0Reg.getType().isUnboxedType()) {
throw new OptimizingCompilerException("Detected incorrect comparison of unboxed types in " +
gc.getMethod() + " at bytecode index " + instrIndex + " from class " +
gc.getMethod().getDeclaringClass() +
" . Use the methods provided on the unboxed types to do comparisons!");
}
}
if (op1.isRegister()) {
RegisterOperand op1Reg = op1.asRegister();
if (op1Reg.getType().isUnboxedType()) {
throw new OptimizingCompilerException("Detected incorrect comparison of unboxed types in " +
gc.getMethod() + " at bytecode index " + instrIndex + " from class " +
gc.getMethod().getDeclaringClass() +
" . Use the methods provided on the unboxed types to do comparisons!");
}
}
fallThrough = true;
RegisterOperand guard = gc.getTemps().makeTempValidation();
return IfCmp.create(REF_IFCMP,
guard,
op0,
op1,
cond,
generateTarget(offset),
gc.getConditionalBranchProfileOperand(instrIndex - bciAdjustment, offset < 0));
}
////REPLACE LOCALS ON STACK.
//
/**
* Replaces copies of local {@code <#index,type>} with
* newly-generated temporaries, and generates the necessary move instructions.
* @param index the local's index
* @param type the local's type
*/
private void replaceLocalsOnStack(int index, TypeReference type) {
int i;
int size = stack.getSize();
for (i = 0; i < size; ++i) {
Operand op = stack.getFromTop(i);
if (gc.isLocal(op, index, type)) {
RegisterOperand lop = (RegisterOperand) op;
RegisterOperand t = gc.getTemps().makeTemp(lop);
Instruction s = Move.create(IRTools.getMoveOp(t.getType()), t, op);
stack.replaceFromTop(i, t.copyD2U());
setSourcePosition(s);
if (DBG_LOCAL || DBG_SELECTED) {
db("replacing local " + index + " at " + i + " from tos with " + t);
}
appendInstruction(s);
}
}
}
//////////
// EXCEPTION HANDLERS.
//////////
// Some common cases to make the code more readable...
private BasicBlock rectifyStateWithNullPtrExceptionHandler() {
return rectifyStateWithNullPtrExceptionHandler(false);
}
private BasicBlock rectifyStateWithArrayBoundsExceptionHandler() {
return rectifyStateWithArrayBoundsExceptionHandler(false);
}
private BasicBlock rectifyStateWithArithmeticExceptionHandler() {
return rectifyStateWithArithmeticExceptionHandler(false);
}
private BasicBlock rectifyStateWithArrayStoreExceptionHandler() {
return rectifyStateWithArrayStoreExceptionHandler(false);
}
private BasicBlock rectifyStateWithErrorHandler() {
return rectifyStateWithErrorHandler(false);
}
public void rectifyStateWithExceptionHandlers() {
rectifyStateWithExceptionHandlers(false);
}
public BasicBlock rectifyStateWithExceptionHandler(TypeReference exceptionType) {
return rectifyStateWithExceptionHandler(exceptionType, false);
}
private BasicBlock rectifyStateWithNullPtrExceptionHandler(boolean linkToExitIfUncaught) {
TypeReference et = TypeReference.JavaLangNullPointerException;
return rectifyStateWithExceptionHandler(et, linkToExitIfUncaught);
}
private BasicBlock rectifyStateWithArrayBoundsExceptionHandler(boolean linkToExitIfUncaught) {
TypeReference et = TypeReference.JavaLangArrayIndexOutOfBoundsException;
return rectifyStateWithExceptionHandler(et, linkToExitIfUncaught);
}
private BasicBlock rectifyStateWithArithmeticExceptionHandler(boolean linkToExitIfUncaught) {
TypeReference et = TypeReference.JavaLangArithmeticException;
return rectifyStateWithExceptionHandler(et, linkToExitIfUncaught);
}
private BasicBlock rectifyStateWithArrayStoreExceptionHandler(boolean linkToExitIfUncaught) {
TypeReference et = TypeReference.JavaLangArrayStoreException;
return rectifyStateWithExceptionHandler(et, linkToExitIfUncaught);
}
private BasicBlock rectifyStateWithErrorHandler(boolean linkToExitIfUncaught) {
TypeReference et = TypeReference.JavaLangError;
return rectifyStateWithExceptionHandler(et, linkToExitIfUncaught);
}
// If exactly 1 catch block is guarenteed to catch the exception,
// then we return it.
// Returning null means that no such block was found.
private BasicBlock rectifyStateWithExceptionHandler(TypeReference exceptionType,
boolean linkToExitIfUncaught) {
currentBBLE.block.setCanThrowExceptions();
int catchTargets = 0;
if (DBG_EX) db("\tchecking exceptions of " + currentBBLE.block);
if (currentBBLE.handlers != null) {
for (HandlerBlockLE xbble : currentBBLE.handlers) {
if (DBG_EX) db("\texception block " + xbble.entryBlock);
byte mustCatch = xbble.mustCatchException(exceptionType);
if (mustCatch != NO || xbble.mayCatchException(exceptionType) != NO) {
if (DBG_EX) {
db("PEI of type " + exceptionType + " could be caught by " + xbble + " rectifying locals");
}
catchTargets++;
blocks.rectifyLocals(_localState, xbble);
currentBBLE.block.insertOut(xbble.entryBlock);
if (DBG_CFG || DBG_SELECTED) {
db("Added CFG edge from " + currentBBLE.block + " to " + xbble.entryBlock);
}
}
if (mustCatch == YES) {
if (DBG_EX) {
db("\t" + xbble + " will defintely catch exceptions of type " + exceptionType);
}
if (DBG_EX && catchTargets == 1) {
db("\t and it is the only target");
}
return (catchTargets == 1) ? xbble.entryBlock : null;
}
}
}
// Now, consider the enclosing exception context.
// NOTE: Because the locals of the current method can't
// possibly matter to the locals of the enclosing method, it is
// sufficient to add a CFG edge (no need to rectify locals).
// It is the responsibility of the BC2IR object generating the
// caller method to ensure that the exposed handler blocks are
// generated if they are reachable from a callee.
// See maybeInlineMethod.
if (gc.getEnclosingHandlers() != null) {
for (Enumeration<BasicBlock> e = gc.getEnclosingHandlers().enumerator(); e.hasMoreElements();) {
ExceptionHandlerBasicBlock xbb = (ExceptionHandlerBasicBlock) e.nextElement();
byte mustCatch = xbb.mustCatchException(exceptionType);
if (mustCatch != NO || xbb.mayCatchException(exceptionType) != NO) {
if (DBG_EX) {
db("PEI of type " + exceptionType + " could be caught by enclosing handler " + xbb);
}
catchTargets++;
currentBBLE.block.insertOut(xbb);
if (DBG_CFG || DBG_SELECTED) {
db("Added CFG edge from " + currentBBLE.block + " to " + xbb);
}
}
if (mustCatch == YES) {
if (DBG_EX) {
db("\t" + xbb + " will defintely catch exceptions of type " + exceptionType);
}
if (DBG_EX && catchTargets == 1) {
db("\t and it is the only target");
}
return (catchTargets == 1) ? xbb : null;
}
}
}
// If we get to here, then we didn't find a handler block that
// is guarenteed to catch the exception. Therefore deal with the
// possibly uncaught exception.
currentBBLE.block.setMayThrowUncaughtException();
if (linkToExitIfUncaught) {
if (DBG_EX) {
db("added explicit edge from " + currentBBLE + " to outermost exit");
}
currentBBLE.block.insertOut(gc.getExit());
if (DBG_CFG || DBG_SELECTED) {
db("Added CFG edge from " + currentBBLE.block + " to exit");
}
}
return null;
}
/*
* Very similar to the above, but since we aren't told what might be thrown,
* we are forced to connect to every in scope handler and can't
* identify a definite target.
*
*/
private void rectifyStateWithExceptionHandlers(boolean linkToExitIfUncaught) {
currentBBLE.block.setCanThrowExceptions();
currentBBLE.block.setMayThrowUncaughtException();
if (linkToExitIfUncaught) {
if (DBG_EX) {
db("PEI of unknown type caused edge from " + currentBBLE + " to outermost exit");
}
currentBBLE.block.insertOut(gc.getExit());
if (DBG_CFG || DBG_SELECTED) {
db("Added CFG edge from " + currentBBLE.block + " to exit");
}
}
if (currentBBLE.handlers != null) {
for (HandlerBlockLE xbble : currentBBLE.handlers) {
if (DBG_EX) {
db("PEI of unknown type could be caught by " + xbble + " rectifying locals");
}
blocks.rectifyLocals(_localState, xbble);
currentBBLE.block.insertOut(xbble.entryBlock);
if (DBG_CFG || DBG_SELECTED) {
db("Added CFG edge from " + currentBBLE.block + " to " + xbble.entryBlock);
}
}
}
// Now, consider the enclosing exception context; ditto NOTE above.
if (gc.getEnclosingHandlers() != null) {
for (Enumeration<BasicBlock> e = gc.getEnclosingHandlers().enumerator(); e.hasMoreElements();) {
ExceptionHandlerBasicBlock xbb = (ExceptionHandlerBasicBlock) e.nextElement();
if (DBG_EX) {
db("PEI of unknown type could be caught by enclosing handler " + xbb);
}
currentBBLE.block.insertOut(xbb);
if (DBG_CFG || DBG_SELECTED) {
db("Added CFG edge from " + currentBBLE.block + " to " + xbb);
}
}
}
}
//////////
// INLINING support
//////////
/**
* Should we inline a call site?
*
* @param call the call instruction being considered for inlining
* @param isExtant is the receiver of a virtual method an extant object?
* @param realBCI the real bytecode index of the call instruction, not adjusted because of OSR
* @return the inline decision (which cannot be {@code null})
*/
private InlineDecision shouldInline(Instruction call, boolean isExtant, int realBCI) {
if (Call.getMethod(call).getTarget() == null) {
return InlineDecision.NO("Target method is null");
}
CompilationState state = new CompilationState(call, isExtant, gc.getOptions(), gc.getOriginalCompiledMethod(), realBCI);
InlineDecision d = gc.getInlinePlan().shouldInline(state);
return d;
}
/**
* Attempt to inline a method. This may fail.
*
* @param inlDec the inline decision for this call site
* @param callSite the call instruction we are attempting to inline
* @return {@code true} if inlining succeeded, {@code false} otherwise
*/
private boolean maybeInlineMethod(InlineDecision inlDec, Instruction callSite) {
if (inlDec.isNO()) {
return false;
}
// Insert OsrBarrier point before the callsite which is going to be
// inlined. The callee can find this barrier via its generation context.
// verify it
if (this.osrGuardedInline) {
if (VM.VerifyAssertions) opt_assert(lastOsrBarrier != null);
gc.saveOSRBarrierForInst(lastOsrBarrier, callSite);
}
// Execute the inline decision.
// NOTE: It is tempting to wrap the call to Inliner.execute in
// a try/catch block that suppresses MagicNotImplemented failures
// by "backing out" the attempted inlining of a method that contained
// an unimplemented magic. Unfortunately, this is somewhat hard to do
// cleanly, since exceptional control flow can inject control flow graph
// edges from inlinedContext to blocks in the enclosing caller CFG.
// These are not easy to find and remove because inlinedContext is not
// well-formed (the exception was thrown while generating the IR, in
// particular before calling finalPass, therefore the inlined CFG
// is not formed and finding all of its member blocks is somewhat awkward).
// We could write code to deal with this, but since in practice the
// opt compiler implements all but a few fringe magics, it is just fine
// to completely give up rather than take heroic measures here.
// In a few cases we do care about, we use NoInlinePragma to
// prevent the opt compiler from inlining a method that contains an
// unimplemented magic.
GenerationContext inlinedContext =
Inliner.execute(inlDec, gc, currentBBLE.block.exceptionHandlers(), callSite);
inlinedSomething = true;
// TODO: We're currently not keeping track if any of the
// enclosing exception handlers are actually reachable from
// this inlined callee.
// Therefore we simply force all of them to be generated wrt
// the state of the local variables in currentBBLE.
// This can result in generating unreachable handlers
// (if no PEI can reach them) and in generating suboptimal
// catch blocks (by merging in currentBBLE's local state
// into catch blocks that can't actually be reached from the inlined CFG).
// I strongly suspect it's not worth worrying about this.....
// dead code elimination should zap the unreachable handlers,
// and we shouldn't care too much about the
// optimization possibilities lost by the extra local rectification.
// Especially since the odds of currentBBLE actually having
// unreachable handler blocks is darn close to zero. --dave 9/21/99.
// NOTE: No need to add CFG edges (they were added as needed
// during generation of the callee)
if (currentBBLE.handlers != null) {
for (HandlerBlockLE handler : currentBBLE.handlers) {
blocks.rectifyLocals(_localState, handler);
}
}
if (inlinedContext.getEpilogue() != null) {
// Wrap a synthetic BBLE around GenerationContext.epilogue and
// pass it as from to getOrCreateBlock.
// This causes any compensation code inserted by getOrCreateBlock
// into the epilogue of the inlined method (see inlineTest7)
BasicBlockLE epilogueBBLE = new BasicBlockLE(0);
epilogueBBLE.block = inlinedContext.getEpilogue();
if (inlinedContext.getResult() != null) {
// If the call has a result, _callHelper allocated a new
// temp for it and pushed it onto the expression stack.
// But, since we successfully inlined the call and
// inlinedContext.epilogue != null,
// we can use inlinedContext.result to obtain better
// downstream information about the inlined callee's return value.
// Therefore we'll pop the old callSite.result off the stack
// and push result instead.
// NOTE: It's critical that we pop callSite.result
// _before_ we copy the stack state into epilogueBBLE!
// Otherwise we'll end up with bogus code in the inlined
// method's prologue due to stack saving!!!!
TypeReference resultType = Call.getResult(callSite).getType();
pop(resultType); // throw away callSite.result
}
blocks.rectifyStacks(currentBBLE.block, stack, epilogueBBLE);
if (inlinedContext.getResult() != null) {
TypeReference resultType = Call.getResult(callSite).getType();
push(inlinedContext.getResult(), resultType);
}
epilogueBBLE.copyIntoLocalState(_localState);
BasicBlockLE afterBBLE = blocks.getOrCreateBlock(bcodes.index(), epilogueBBLE, stack, _localState);
// Create the InliningBlockLE and initialize fallThrough links.
InliningBlockLE inlinedCallee = new InliningBlockLE(inlinedContext, epilogueBBLE);
currentBBLE.fallThrough = inlinedCallee;
currentBBLE.block.insertOut(inlinedCallee.gc.getCfg().firstInCodeOrder());
epilogueBBLE.fallThrough = afterBBLE;
epilogueBBLE.block.insertOut(epilogueBBLE.fallThrough.block);
} else {
// All exits from the callee were via throws.
// Therefore the next basic block is unreachable (unless
// there is a branch to it from somewhere else in the current method,
// which will naturally be handled when we generate the branch).
InliningBlockLE inlinedCallee = new InliningBlockLE(inlinedContext, null);
currentBBLE.fallThrough = inlinedCallee;
currentBBLE.block.insertOut(inlinedCallee.gc.getCfg().firstInCodeOrder());
}
endOfBasicBlock = true;
return true;
}
/* create an OSR Barrier instruction at the current position.
*/
private Instruction _createOsrBarrier() {
ArrayList<Operand> livevars = new ArrayList<Operand>();
/* for local variables, we have to use helper to make a register. */
/* ltypes and stypes should be the full length
* WARNING: what's the order of DUMMY and LONG?
*/
int localnum = _localState.length;
byte[] ltypes = new byte[localnum];
int num_llocals = 0;
for (int i = 0, n = _localState.length; i < n; i++) {
Operand op = _localState[i];
if ((op != null) && (op != DUMMY)) {
livevars.add(_loadLocalForOSR(op));
num_llocals++;
if (op instanceof ReturnAddressOperand) {
ltypes[i] = ReturnAddressTypeCode;
} else {
TypeReference typ = op.getType();
if (typ.isWordLikeType() || (typ == TypeReference.NULL_TYPE)) {
ltypes[i] = WordTypeCode;
} else {
ltypes[i] = typ.getName().parseForTypeCode();
}
}
} else {
ltypes[i] = VoidTypeCode;
}
}
int stacknum = stack.getSize();
byte[] stypes = new byte[stacknum];
/* the variable on stack can be used directly ? */
int num_lstacks = 0;
for (int i = 0, n = stack.getSize(); i < n; i++) {
Operand op = stack.getFromBottom(i);
if ((op != null) && (op != DUMMY)) {
if (op.isRegister()) {
livevars.add(op.asRegister().copyU2U());
} else {
livevars.add(op.copy());
}
num_lstacks++;
if (op instanceof ReturnAddressOperand) {
stypes[i] = ReturnAddressTypeCode;
} else {
TypeReference typ = op.getType();
if (typ.isWordLikeType() || (typ == TypeReference.NULL_TYPE)) {
stypes[i] = WordTypeCode;
} else {
/* for stack operand, reverse the order for long and double */
byte tcode = typ.getName().parseForTypeCode();
if ((tcode == LongTypeCode) || (tcode == DoubleTypeCode)) {
stypes[i - 1] = tcode;
stypes[i] = VoidTypeCode;
} else {
stypes[i] = tcode;
}
}
}
} else {
stypes[i] = VoidTypeCode;
}
}
Instruction barrier = OsrBarrier.create(OSR_BARRIER, null, // temporarily
num_llocals + num_lstacks);
for (int i = 0, n = livevars.size(); i < n; i++) {
Operand op = livevars.get(i);
if (op instanceof ReturnAddressOperand) {
int tgtpc = ((ReturnAddressOperand) op).retIndex - gc.getMethod().getOsrPrologueLength();
op = new IntConstantOperand(tgtpc);
} else if (op instanceof LongConstantOperand) {
op = _prepareLongConstant(op);
} else if (op instanceof DoubleConstantOperand) {
op = _prepareDoubleConstant(op);
}
if (VM.VerifyAssertions) opt_assert(op != null);
OsrBarrier.setElement(barrier, i, op);
}
// patch type info operand
OsrTypeInfoOperand typeinfo = new OsrTypeInfoOperand(ltypes, stypes);
OsrBarrier.setTypeInfo(barrier, typeinfo);
setSourcePosition(barrier);
return barrier;
}
/**
* special process for long/double constants
* @param op a long constant
* @return a new operand
*/
private Operand _prepareLongConstant(Operand op) {
/* for long and double constants, always move them to a register,
* therefor, BURS will split it in two registers.
*/
RegisterOperand t = gc.getTemps().makeTemp(op.getType());
appendInstruction(Move.create(LONG_MOVE, t, op));
return t.copyD2U();
}
/**
* special process for long/double constants
* @param op a double constant
* @return a new operand
*/
private Operand _prepareDoubleConstant(Operand op) {
/* for long and double constants, always move them to a register,
* therefor, BURS will split it in two registers.
*/
RegisterOperand t = gc.getTemps().makeTemp(op.getType());
appendInstruction(Move.create(DOUBLE_MOVE, t, op));
return t.copyD2U();
}
/**
* make a temporary register, and create a move instruction
* @param op the local variable.
* @return operand marked as use.
*/
private Operand _loadLocalForOSR(Operand op) {
/* return address is processed specially */
if (op instanceof ReturnAddressOperand) {
return op;
}
RegisterOperand t = gc.getTemps().makeTemp(op.getType());
byte tcode = op.getType().getName().parseForTypeCode();
Operator operator = null;
switch (tcode) {
case ClassTypeCode:
case ArrayTypeCode:
operator = REF_MOVE;
break;
case BooleanTypeCode:
case ByteTypeCode:
case ShortTypeCode:
case CharTypeCode:
case IntTypeCode:
operator = INT_MOVE;
break;
case LongTypeCode:
operator = LONG_MOVE;
break;
case FloatTypeCode:
operator = FLOAT_MOVE;
break;
case DoubleTypeCode:
operator = DOUBLE_MOVE;
break;
case VoidTypeCode:
return null;
}
appendInstruction(Move.create(operator, t, op.copy()));
return t.copyD2U();
}
/**
* Creates an OSR point instruction with its dependent OsrBarrier
* which provides type and variable information.
* The OsrPoint instruction is going to be refilled immediately
* after BC2IR, before any other optimizations.
*
* @param barrier the OSR barrier instruction
* @param gc context that has information about OSR
* @return the osr point instruction
*/
public static Instruction _osrHelper(Instruction barrier, GenerationContext gc) {
Instruction inst = OsrPoint.create(YIELDPOINT_OSR, null, // currently unknown
0); // currently unknown
gc.saveOSRBarrierForInst(barrier, inst);
return inst;
}
//// LOCAL STATE.
/**
* Gets the specified local variable. This can return an RegisterOperand
* which refers to the given local, or some other kind of operand (if the
* local variable is assumed to contain a particular value.)
*
* @param i local variable number
* @return a copy of the local variable
*/
private Operand getLocal(int i) {
Operand local = _localState[i];
if (DBG_LOCAL || DBG_SELECTED) db("getting local " + i + " for use: " + local);
return local.copy();
}
/**
* Gets the specified local variable (long, double). This can return an
* RegisterOperand which refers to the given local, or some other kind
* of operand (if the local variable is assumed to contain a given value.)
*
* @param i local variable number
* @return a copy of the local variable
*/
private Operand getLocalDual(int i) {
if (VM.VerifyAssertions) opt_assert(_localState[i + 1] == DUMMY);
Operand local = _localState[i];
if (DBG_LOCAL || DBG_SELECTED) db("getting local " + i + " for use: " + local);
return local.copy();
}
/**
* Set the specified local variable
*
* @param i local variable number
* @param op Operand to store in the local
*/
private void setLocal(int i, Operand op) {
if (DBG_LOCAL || DBG_SELECTED) db("setting local " + i + " with " + op);
_localState[i] = op;
}
/**
* Set the specified local variable
*
* @param i local variable number
* @param op Operand to store in the local
*/
private void setLocalDual(int i, Operand op) {
if (DBG_LOCAL || DBG_SELECTED) db("setting dual local " + i + " with " + op);
_localState[i] = op;
_localState[i + 1] = DUMMY;
}
/**
* Dummy stack slot
* @see BC2IR#DUMMY
*/
private static final class DummyStackSlot extends Operand {
@Override
public Operand copy() {
return this;
}
@Override
public boolean similar(Operand op) {
return (op instanceof DummyStackSlot);
}
@Override
public String toString() {
return "<DUMMY>";
}
}
}