/*
* 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.hir2lir;
import org.jikesrvm.VM;
import org.jikesrvm.classloader.RVMArray;
import org.jikesrvm.classloader.RVMClass;
import org.jikesrvm.classloader.DynamicTypeCheck;
import org.jikesrvm.classloader.RVMMethod;
import org.jikesrvm.classloader.RVMType;
import org.jikesrvm.classloader.TypeReference;
import org.jikesrvm.compilers.opt.OptimizingCompilerException;
import org.jikesrvm.compilers.opt.ir.ALoad;
import org.jikesrvm.compilers.opt.ir.Binary;
import org.jikesrvm.compilers.opt.ir.BooleanCmp;
import org.jikesrvm.compilers.opt.ir.Call;
import org.jikesrvm.compilers.opt.ir.Goto;
import org.jikesrvm.compilers.opt.ir.IfCmp;
import org.jikesrvm.compilers.opt.ir.IfCmp2;
import org.jikesrvm.compilers.opt.ir.InstanceOf;
import org.jikesrvm.compilers.opt.ir.Load;
import org.jikesrvm.compilers.opt.ir.Move;
import org.jikesrvm.compilers.opt.ir.BasicBlock;
import org.jikesrvm.compilers.opt.ir.IR;
import org.jikesrvm.compilers.opt.ir.Instruction;
import static org.jikesrvm.compilers.opt.ir.Operators.ARRAYLENGTH;
import static org.jikesrvm.compilers.opt.ir.Operators.BBEND;
import static org.jikesrvm.compilers.opt.ir.Operators.BOOLEAN_CMP_ADDR;
import static org.jikesrvm.compilers.opt.ir.Operators.CALL;
import static org.jikesrvm.compilers.opt.ir.Operators.GET_ARRAY_ELEMENT_TIB_FROM_TIB;
import static org.jikesrvm.compilers.opt.ir.Operators.GET_DOES_IMPLEMENT_FROM_TIB;
import static org.jikesrvm.compilers.opt.ir.Operators.GET_SUPERCLASS_IDS_FROM_TIB;
import static org.jikesrvm.compilers.opt.ir.Operators.GET_TYPE_FROM_TIB;
import static org.jikesrvm.compilers.opt.ir.Operators.GOTO;
import static org.jikesrvm.compilers.opt.ir.Operators.GUARD_COMBINE;
import static org.jikesrvm.compilers.opt.ir.Operators.GUARD_MOVE;
import static org.jikesrvm.compilers.opt.ir.Operators.INT_2ADDRZerExt;
import static org.jikesrvm.compilers.opt.ir.Operators.INT_AND;
import static org.jikesrvm.compilers.opt.ir.Operators.INT_IFCMP;
import static org.jikesrvm.compilers.opt.ir.Operators.INT_IFCMP2;
import static org.jikesrvm.compilers.opt.ir.Operators.INT_LOAD;
import static org.jikesrvm.compilers.opt.ir.Operators.INT_MOVE;
import static org.jikesrvm.compilers.opt.ir.Operators.INT_SHL;
import static org.jikesrvm.compilers.opt.ir.Operators.REF_IFCMP;
import static org.jikesrvm.compilers.opt.ir.Operators.REF_MOVE;
import static org.jikesrvm.compilers.opt.ir.Operators.TRAP;
import static org.jikesrvm.compilers.opt.ir.Operators.USHORT_ALOAD;
import static org.jikesrvm.compilers.opt.ir.Operators.USHORT_LOAD;
import org.jikesrvm.compilers.opt.ir.StoreCheck;
import org.jikesrvm.compilers.opt.ir.Trap;
import org.jikesrvm.compilers.opt.ir.TypeCheck;
import org.jikesrvm.compilers.opt.ir.operand.BranchProfileOperand;
import org.jikesrvm.compilers.opt.ir.operand.ConditionOperand;
import org.jikesrvm.compilers.opt.ir.operand.IntConstantOperand;
import org.jikesrvm.compilers.opt.ir.operand.LocationOperand;
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.RegisterOperand;
import org.jikesrvm.compilers.opt.ir.operand.TrapCodeOperand;
import org.jikesrvm.compilers.opt.ir.operand.TrueGuardOperand;
import org.jikesrvm.runtime.Entrypoints;
import org.vmmagic.unboxed.Address;
import org.vmmagic.unboxed.Offset;
/**
* Expansion of Dynamic Type Checking operations.
*
* @see DynamicTypeCheck
*/
abstract class DynamicTypeCheckExpansion extends ConvertToLowLevelIR {
/**
* Expand an instanceof instruction into the LIR sequence that implements
* the dynamic type check. Ref may contain a null ptr at runtime.
*
* @param s an INSTANCEOF or INSTANCEOF_UNRESOLVED instruction to expand
* @param ir the enclosing IR
* @return the last Instruction in the generated LIR sequence.
*/
static Instruction instanceOf(Instruction s, IR ir) {
RegisterOperand result = InstanceOf.getClearResult(s);
TypeReference LHStype = InstanceOf.getType(s).getTypeRef();
Operand ref = InstanceOf.getClearRef(s);
Instruction next = s.nextInstructionInCodeOrder();
if (next.operator() == INT_IFCMP &&
IfCmp.getVal1(next) instanceof RegisterOperand &&
result.similar(IfCmp.getVal1(next))) {
// The result of instanceof is being consumed by a conditional branch.
// Optimize this case by generating a branching type check
// instead of producing a value.
// TODO: This is really not safe: suppose the if is NOT the
// only use of the result of the instanceof.
// The way to fix this is to add ifInstanceOf and ifNotInstanceOf
// operators to the IR and have Simple transform
// instanceof, intIfCmp based on the U/D chains.
// See defect 2114.
Operand val2 = IfCmp.getVal2(next);
if (VM.VerifyAssertions) VM._assert(val2.isIntConstant());
int ival2 = ((IntConstantOperand) val2).value;
ConditionOperand cond = IfCmp.getCond(next);
boolean branchCondition =
(((ival2 == 0) && (cond.isNOT_EQUAL() || cond.isLESS_EQUAL())) ||
((ival2 == 1) && (cond.isEQUAL() || cond.isGREATER_EQUAL())));
BasicBlock branchBB = next.getBranchTarget();
RegisterOperand oldGuard = IfCmp.getGuardResult(next);
next.remove();
BasicBlock fallThroughBB = fallThroughBB(s, ir);
BasicBlock falseBranch = branchCondition ? fallThroughBB : branchBB;
BasicBlock trueBranch = branchCondition ? branchBB : fallThroughBB;
BranchProfileOperand bp = IfCmp.getClearBranchProfile(next);
if (branchCondition) bp = bp.flip();
Instruction nullComp =
IfCmp.create(REF_IFCMP,
oldGuard.copyRO(),
ref.copy(),
new NullConstantOperand(),
ConditionOperand.EQUAL(),
falseBranch.makeJumpTarget(),
BranchProfileOperand.unlikely());
s.insertBefore(nullComp);
BasicBlock myBlock = s.getBasicBlock();
BasicBlock instanceOfBlock = myBlock.splitNodeAt(nullComp, ir);
myBlock.insertOut(instanceOfBlock);
myBlock.insertOut(falseBranch);
ir.cfg.linkInCodeOrder(myBlock, instanceOfBlock);
Operand RHStib = getTIB(s, ir, ref, oldGuard.copyRO());
return generateBranchingTypeCheck(s, ir, ref.copy(), LHStype, RHStib, trueBranch, falseBranch, oldGuard.copy().asRegister(), bp);
} else {
// Not a branching pattern
RegisterOperand guard = ir.regpool.makeTempValidation();
BasicBlock instanceOfBlock = s.getBasicBlock().segregateInstruction(s, ir);
BasicBlock prevBB = instanceOfBlock.prevBasicBlockInCodeOrder();
BasicBlock nextBB = instanceOfBlock.nextBasicBlockInCodeOrder();
BasicBlock nullCaseBB = instanceOfBlock.createSubBlock(s.getBytecodeIndex(), ir, .01f);
prevBB.appendInstruction(IfCmp.create(REF_IFCMP,
guard,
ref.copy(),
new NullConstantOperand(),
ConditionOperand.EQUAL(),
nullCaseBB.makeJumpTarget(),
BranchProfileOperand.unlikely()));
nullCaseBB.appendInstruction(Move.create(INT_MOVE, result.copyD2D(), IC(0)));
nullCaseBB.appendInstruction(Goto.create(GOTO, nextBB.makeJumpTarget()));
// Stitch together the CFG; add nullCaseBB to the end of code array.
prevBB.insertOut(nullCaseBB);
nullCaseBB.insertOut(nextBB);
ir.cfg.addLastInCodeOrder(nullCaseBB);
Operand RHStib = getTIB(s, ir, ref, guard.copyD2U());
return generateValueProducingTypeCheck(s, ir, ref.copy(), LHStype, RHStib, result);
}
}
/**
* Expand an instanceof instruction into the LIR sequence that implements
* the dynamic type check. Ref is known to never contain a null ptr at
* runtime.
*
* @param s an INSTANCEOF_NOTNULL instruction to expand
* @param ir the enclosing IR
* @return the last Instruction in the generated LIR sequence.
*/
static Instruction instanceOfNotNull(Instruction s, IR ir) {
RegisterOperand result = InstanceOf.getClearResult(s);
TypeReference LHStype = InstanceOf.getType(s).getTypeRef();
Operand ref = InstanceOf.getClearRef(s);
Operand guard = InstanceOf.getClearGuard(s);
Instruction next = s.nextInstructionInCodeOrder();
if (next.operator() == INT_IFCMP &&
IfCmp.getVal1(next) instanceof RegisterOperand &&
result.similar(IfCmp.getVal1(next))) {
// The result of instanceof is being consumed by a conditional branch.
// Optimize this case by generating a branching type
// check instead of producing a value.
Operand val2 = IfCmp.getVal2(next);
if (VM.VerifyAssertions) {
VM._assert(val2.isIntConstant());
}
int ival2 = ((IntConstantOperand) val2).value;
ConditionOperand cond = IfCmp.getCond(next);
boolean branchCondition =
(((ival2 == 0) && (cond.isNOT_EQUAL() || cond.isLESS_EQUAL())) ||
((ival2 == 1) && (cond.isEQUAL() || cond.isGREATER_EQUAL())));
BasicBlock branchBB = next.getBranchTarget();
RegisterOperand oldGuard = IfCmp.getGuardResult(next);
next.remove();
BasicBlock fallThroughBB = fallThroughBB(s, ir);
Operand RHStib = getTIB(s, ir, ref, guard);
if (branchCondition) {
return generateBranchingTypeCheck(s,
ir,
ref.copy(),
LHStype,
RHStib,
branchBB,
fallThroughBB,
oldGuard.copyRO(),
IfCmp.getClearBranchProfile(next).flip());
} else {
return generateBranchingTypeCheck(s,
ir,
ref.copy(),
LHStype,
RHStib,
fallThroughBB,
branchBB,
oldGuard.copyRO(),
IfCmp.getClearBranchProfile(next));
}
} else {
// Not a branching pattern
Operand RHStib = getTIB(s, ir, ref, guard);
return generateValueProducingTypeCheck(s, ir, ref.copy(), LHStype, RHStib, result);
}
}
/**
* Expand a checkcast instruction into the LIR sequence that implements the
* dynamic type check, raising a ClassCastException when the type check
* fails. Ref may contain a null ptr at runtime.
*
* @param s a CHECKCAST or CHECKCAST_UNRESOLVED instruction to expand
* @param ir the enclosing IR
* @return the last Instruction in the generated LIR sequence.
*/
static Instruction checkcast(Instruction s, IR ir) {
Operand ref = TypeCheck.getClearRef(s);
TypeReference LHStype = TypeCheck.getType(s).getTypeRef();
RegisterOperand guard = ir.regpool.makeTempValidation();
Instruction nullCond =
IfCmp.create(REF_IFCMP,
guard,
ref.copy(),
new NullConstantOperand(),
ConditionOperand.EQUAL(),
null,
// KLUDGE...we haven't created the block yet!
new BranchProfileOperand());
s.insertBefore(nullCond);
BasicBlock myBlock = s.getBasicBlock();
BasicBlock failBlock = myBlock.createSubBlock(s.getBytecodeIndex(), ir, .0001f);
BasicBlock instanceOfBlock = myBlock.splitNodeAt(nullCond, ir);
BasicBlock succBlock = instanceOfBlock.splitNodeAt(s, ir);
succBlock.firstInstruction().insertAfter(Move.create(REF_MOVE, TypeCheck.getClearResult(s), ref.copy()));
IfCmp.setTarget(nullCond, succBlock.makeJumpTarget()); // fixup KLUDGE
myBlock.insertOut(instanceOfBlock);
myBlock.insertOut(succBlock);
instanceOfBlock.insertOut(failBlock);
instanceOfBlock.insertOut(succBlock);
ir.cfg.linkInCodeOrder(myBlock, instanceOfBlock);
ir.cfg.linkInCodeOrder(instanceOfBlock, succBlock);
ir.cfg.addLastInCodeOrder(failBlock);
Instruction raiseError = Trap.create(TRAP, null, TrapCodeOperand.CheckCast());
raiseError.copyPosition(s);
failBlock.appendInstruction(raiseError);
Operand RHStib = getTIB(s, ir, ref, guard.copyD2U());
return generateBranchingTypeCheck(s,
ir,
ref.copy(),
LHStype,
RHStib,
succBlock,
failBlock,
guard.copyRO(),
BranchProfileOperand.never());
}
/**
* Expand a checkcast instruction into the LIR sequence that implements the
* dynamic type check, raising a ClassCastException when the type check
* fails. Ref is known to never contain a null ptr at runtime.
*
* @param s a CHECKCAST_NOTNULL instruction to expand
* @param ir the enclosing IR
* @return the last Instruction in the generated LIR sequence.
*/
static Instruction checkcastNotNull(Instruction s, IR ir) {
Operand ref = TypeCheck.getClearRef(s);
TypeReference LHStype = TypeCheck.getType(s).getTypeRef();
Operand guard = TypeCheck.getClearGuard(s);
BasicBlock myBlock = s.getBasicBlock();
BasicBlock failBlock = myBlock.createSubBlock(s.getBytecodeIndex(), ir, .0001f);
BasicBlock succBlock = myBlock.splitNodeAt(s, ir);
succBlock.firstInstruction().insertAfter(Move.create(REF_MOVE, TypeCheck.getClearResult(s), ref.copy()));
myBlock.insertOut(failBlock);
myBlock.insertOut(succBlock);
ir.cfg.linkInCodeOrder(myBlock, succBlock);
ir.cfg.addLastInCodeOrder(failBlock);
Instruction raiseError = Trap.create(TRAP, null, TrapCodeOperand.CheckCast());
raiseError.copyPosition(s);
failBlock.appendInstruction(raiseError);
Operand RHStib = getTIB(s, ir, ref, guard);
return generateBranchingTypeCheck(s,
ir,
ref.copy(),
LHStype,
RHStib,
succBlock,
failBlock,
ir.regpool.makeTempValidation(),
BranchProfileOperand.never());
}
/**
* Expand a checkcastInterface instruction into the LIR sequence that
* implements the dynamic type check, raising an IncompataibleClassChangeError
* if the type check fails.
* Ref is known to never contain a null ptr at runtime.
*
* @param s a MUST_IMPLEMENT_INTERFACE instruction to expand
* @param ir the enclosing IR
* @return the last Instruction in the generated LIR sequence.
*/
static Instruction mustImplementInterface(Instruction s, IR ir) {
Operand ref = TypeCheck.getClearRef(s);
RVMClass LHSClass = (RVMClass) TypeCheck.getType(s).getVMType();
if (VM.VerifyAssertions) VM._assert(LHSClass != null, "Should be resolvable...");
int interfaceIndex = LHSClass.getDoesImplementIndex();
int interfaceMask = LHSClass.getDoesImplementBitMask();
Operand guard = TypeCheck.getClearGuard(s);
BasicBlock myBlock = s.getBasicBlock();
BasicBlock failBlock = myBlock.createSubBlock(s.getBytecodeIndex(), ir, .0001f);
BasicBlock succBlock = myBlock.splitNodeAt(s, ir);
succBlock.firstInstruction().insertAfter(Move.create(REF_MOVE, TypeCheck.getClearResult(s), ref.copy()));
myBlock.insertOut(failBlock);
myBlock.insertOut(succBlock);
ir.cfg.linkInCodeOrder(myBlock, succBlock);
ir.cfg.addLastInCodeOrder(failBlock);
Instruction raiseError = Trap.create(TRAP, null, TrapCodeOperand.MustImplement());
raiseError.copyPosition(s);
failBlock.appendInstruction(raiseError);
Operand RHStib = getTIB(s, ir, ref, guard);
RegisterOperand doesImpl = InsertUnary(s, ir, GET_DOES_IMPLEMENT_FROM_TIB, TypeReference.IntArray, RHStib);
if (DynamicTypeCheck.MIN_DOES_IMPLEMENT_SIZE <= interfaceIndex) {
RegisterOperand doesImplLength =
InsertGuardedUnary(s, ir, ARRAYLENGTH, TypeReference.Int, doesImpl.copyD2U(), TG());
Instruction lengthCheck =
IfCmp.create(INT_IFCMP,
ir.regpool.makeTempValidation(),
doesImplLength,
IC(interfaceIndex),
ConditionOperand.LESS_EQUAL(),
failBlock.makeJumpTarget(),
BranchProfileOperand.never());
s.insertBefore(lengthCheck);
myBlock.splitNodeWithLinksAt(lengthCheck, ir);
myBlock.insertOut(failBlock); // required due to splitNode!
}
RegisterOperand entry =
InsertLoadOffset(s,
ir,
INT_LOAD,
TypeReference.Int,
doesImpl,
Offset.fromIntZeroExtend(interfaceIndex << 2),
new LocationOperand(TypeReference.Int),
TG());
RegisterOperand bit = insertBinary(s, ir, INT_AND, TypeReference.Int, entry, IC(interfaceMask));
IfCmp.mutate(s,
INT_IFCMP,
ir.regpool.makeTempValidation(),
bit,
IC(0),
ConditionOperand.EQUAL(),
failBlock.makeJumpTarget(),
BranchProfileOperand.never());
return s;
}
/**
* Expand an object array store check into the LIR sequence that
* implements it.
*
* @param s an OBJARRAY_STORE_CHECK instruction to expand
* @param ir the enclosing IR
* @param couldBeNull is it possible that the element being stored is null?
* @return the last Instruction in the generated LIR sequence.
*/
static Instruction arrayStoreCheck(Instruction s, IR ir, boolean couldBeNull) {
RegisterOperand guardResult = StoreCheck.getGuardResult(s);
Operand arrayRef = StoreCheck.getClearRef(s);
Operand elemRef = StoreCheck.getClearVal(s);
Operand guard = StoreCheck.getClearGuard(s);
if (elemRef instanceof NullConstantOperand) {
Instruction continueAt = s.prevInstructionInCodeOrder();
s.remove();
return continueAt;
}
BasicBlock myBlock = s.getBasicBlock();
BasicBlock contBlock = myBlock.splitNodeAt(s, ir);
BasicBlock trapBlock = myBlock.createSubBlock(s.getBytecodeIndex(), ir, .0001f);
BasicBlock curBlock = myBlock;
Move.mutate(s, GUARD_MOVE, guardResult, new TrueGuardOperand());
// Set up a block with a trap instruction that we can jump to if the
// store check fails
Instruction trap = Trap.create(TRAP, null, TrapCodeOperand.StoreCheck());
trap.copyPosition(s);
trapBlock.appendInstruction(trap);
ir.cfg.addLastInCodeOrder(trapBlock);
Operand rhsGuard = guard;
if (couldBeNull) {
// if rhs is null, then the checkcast succeeds
rhsGuard = ir.regpool.makeTempValidation();
contBlock.prependInstruction(Binary.create(GUARD_COMBINE,
guardResult.copyRO(),
guardResult.copyRO(),
rhsGuard.copy()));
curBlock.appendInstruction(IfCmp.create(REF_IFCMP,
rhsGuard.asRegister(),
elemRef,
new NullConstantOperand(),
ConditionOperand.EQUAL(),
contBlock.makeJumpTarget(),
new BranchProfileOperand()));
curBlock.insertOut(contBlock);
curBlock = advanceBlock(s.getBytecodeIndex(), curBlock, ir);
}
// Find out what we think the compile time type of the lhs is.
// Based on this, we can do one of several things:
// (1) If the compile time element type is a final proper class, then a
// TIB comparision of the runtime elemRef type and the
// compile time element type is definitive.
// (2) If the compile time type is known to be the declared type,
// then inject a short-circuit test to see if the
// runtime lhs type is the same as the compile-time lhs type.
// (3) If the compile time element type is a proper class other than
// java.lang.Object, then a subclass test of the runtime LHS elem type
// and the runtime elemRef type is definitive. Note: we must exclude
// java.lang.Object because if the compile time element type is
// java.lang.Object, then the runtime-element type might actually be
// an interface (ie not a proper class), and we won't be testing the right thing!
// If we think the compile time type is JavaLangObjectType then
// we lost type information due to unloaded classes causing
// imprecise meets. This should only happen once in a blue moon,
// so don't bother trying anything clever when it does.
RVMType compType = arrayRef.getType().peekType();
if (compType != null && !compType.isJavaLangObjectType()) {
// optionally (1) from above
if (compType.getDimensionality() == 1) {
RVMClass etc = (RVMClass) compType.asArray().getElementType();
if (etc.isResolved() && etc.isFinal()) {
if (VM.VerifyAssertions) VM._assert(!etc.isInterface());
Operand rhsTIB = getTIB(curBlock.lastInstruction(), ir, elemRef.copy(), rhsGuard.copy());
Operand etTIB = getTIB(curBlock.lastInstruction(), ir, etc);
curBlock.appendInstruction(IfCmp.create(REF_IFCMP,
guardResult.copyRO(),
rhsTIB,
etTIB,
ConditionOperand.NOT_EQUAL(),
trapBlock.makeJumpTarget(),
BranchProfileOperand.never()));
curBlock.insertOut(trapBlock);
curBlock.insertOut(contBlock);
ir.cfg.linkInCodeOrder(curBlock, contBlock);
return curBlock.lastInstruction();
}
}
// optionally (2) from above
Operand lhsTIB = getTIB(curBlock.lastInstruction(), ir, arrayRef, guard);
if (((arrayRef instanceof RegisterOperand) && ((RegisterOperand) arrayRef).isDeclaredType()) ||
compType == RVMType.JavaLangObjectArrayType) {
Operand declTIB = getTIB(curBlock.lastInstruction(), ir, compType);
curBlock.appendInstruction(IfCmp.create(REF_IFCMP,
guardResult.copyRO(),
declTIB,
lhsTIB,
ConditionOperand.EQUAL(),
contBlock.makeJumpTarget(),
new BranchProfileOperand()));
curBlock.insertOut(contBlock);
curBlock = advanceBlock(s.getBytecodeIndex(), curBlock, ir);
}
// On our way to doing (3) from above attempt another short-circuit.
// If lhsElemTIB == rhsTIB, then we are done.
Operand rhsTIB = getTIB(curBlock.lastInstruction(), ir, elemRef.copy(), rhsGuard.copy());
RegisterOperand lhsElemTIB =
InsertUnary(curBlock.lastInstruction(),
ir,
GET_ARRAY_ELEMENT_TIB_FROM_TIB,
TypeReference.TIB,
lhsTIB.copy());
curBlock.appendInstruction(IfCmp.create(REF_IFCMP,
guardResult.copyRO(),
rhsTIB,
lhsElemTIB,
ConditionOperand.EQUAL(),
contBlock.makeJumpTarget(),
new BranchProfileOperand()));
curBlock.insertOut(contBlock);
curBlock = advanceBlock(s.getBytecodeIndex(), curBlock, ir);
// Optionally (3) from above
if (compType.getDimensionality() == 1) {
RVMClass etc = (RVMClass) compType.asArray().getElementType();
if (etc.isResolved() && !etc.isInterface() && !etc.isJavaLangObjectType()) {
RegisterOperand lhsElemType =
InsertUnary(curBlock.lastInstruction(),
ir,
GET_TYPE_FROM_TIB,
TypeReference.Type,
lhsElemTIB.copyU2U());
RegisterOperand rhsSuperclassIds =
InsertUnary(curBlock.lastInstruction(),
ir,
GET_SUPERCLASS_IDS_FROM_TIB,
TypeReference.ShortArray,
rhsTIB.copy());
RegisterOperand lhsElemDepth =
getField(curBlock.lastInstruction(), ir, lhsElemType, Entrypoints.depthField, TG());
RegisterOperand rhsSuperclassIdsLength =
InsertGuardedUnary(curBlock.lastInstruction(),
ir,
ARRAYLENGTH,
TypeReference.Int,
rhsSuperclassIds.copyD2U(),
TG());
curBlock.appendInstruction(IfCmp.create(INT_IFCMP,
guardResult.copyRO(),
lhsElemDepth,
rhsSuperclassIdsLength,
ConditionOperand.GREATER_EQUAL(),
trapBlock.makeJumpTarget(),
BranchProfileOperand.never()));
curBlock.insertOut(trapBlock);
curBlock = advanceBlock(s.getBytecodeIndex(), curBlock, ir);
RegisterOperand lhsElemId =
getField(curBlock.lastInstruction(), ir, lhsElemType.copyD2U(), Entrypoints.idField, TG());
RegisterOperand refCandidate = ir.regpool.makeTemp(TypeReference.Short);
LocationOperand loc = new LocationOperand(TypeReference.Short);
if (LOWER_ARRAY_ACCESS) {
RegisterOperand lhsDepthOffset =
insertBinary(curBlock.lastInstruction(),
ir,
INT_SHL,
TypeReference.Int,
lhsElemDepth.copyD2U(),
IC(1));
lhsDepthOffset =
InsertUnary(curBlock.lastInstruction(),
ir,
INT_2ADDRZerExt,
TypeReference.Offset,
lhsDepthOffset.copy());
curBlock.appendInstruction(Load.create(USHORT_LOAD,
refCandidate,
rhsSuperclassIds,
lhsDepthOffset,
loc,
TG()));
} else {
curBlock.appendInstruction(ALoad.create(USHORT_ALOAD,
refCandidate,
rhsSuperclassIds,
lhsElemDepth.copyRO(),
loc,
TG()));
}
curBlock.appendInstruction(IfCmp.create(INT_IFCMP,
guardResult.copyRO(),
refCandidate.copyD2U(),
lhsElemId,
ConditionOperand.NOT_EQUAL(),
trapBlock.makeJumpTarget(),
BranchProfileOperand.never()));
curBlock.insertOut(trapBlock);
curBlock.insertOut(contBlock);
ir.cfg.linkInCodeOrder(curBlock, contBlock);
return curBlock.lastInstruction();
}
}
}
// Call RuntimeEntrypoints.checkstore.
RVMMethod target = Entrypoints.checkstoreMethod;
Instruction call =
Call.create2(CALL,
null,
AC(target.getOffset()),
MethodOperand.STATIC(target),
rhsGuard.copy(),
arrayRef.copy(),
elemRef.copy());
call.copyPosition(s);
curBlock.appendInstruction(call);
curBlock.insertOut(contBlock);
ir.cfg.linkInCodeOrder(curBlock, contBlock);
return callHelper(call, ir);
}
/**
* Generate a value-producing dynamic type check.
* This routine assumes that the CFG and code order are
* already correctly established.
* This routine must either remove s or mutuate it.
*
* @param s The Instruction that is to be replaced by
* a value producing type check
* @param ir The IR containing the instruction to be expanded.
* @param RHSobj The RegisterOperand containing the rhs object.
* @param LHStype The RVMType to be tested against.
* @param RHStib The Operand containing the TIB of the rhs.
* @param result The RegisterOperand that the result of dynamic
* type check is to be stored in.
* @return the opt instruction immediately before the
* instruction to continue expansion.
*/
private static Instruction generateValueProducingTypeCheck(Instruction s, IR ir, Operand RHSobj,
TypeReference LHStype, Operand RHStib,
RegisterOperand result) {
// Is LHStype a class?
if (LHStype.isClassType()) {
RVMClass LHSclass = (RVMClass) LHStype.peekType();
if (LHSclass != null && LHSclass.isResolved()) {
// Cases 4, 5, and 6 of DynamicTypeCheck: LHSclass is a
// resolved class or interface
if (LHSclass.isInterface()) {
// A resolved interface (case 4)
int interfaceIndex = LHSclass.getDoesImplementIndex();
int interfaceMask = LHSclass.getDoesImplementBitMask();
RegisterOperand doesImpl =
InsertUnary(s, ir, GET_DOES_IMPLEMENT_FROM_TIB, TypeReference.IntArray, RHStib);
RegisterOperand entry =
InsertLoadOffset(s,
ir,
INT_LOAD,
TypeReference.Int,
doesImpl,
Offset.fromIntZeroExtend(interfaceIndex << 2),
new LocationOperand(TypeReference.Int),
TG());
RegisterOperand bit = insertBinary(s, ir, INT_AND, TypeReference.Int, entry, IC(interfaceMask));
//save to use the cheaper ADDR version of BOOLEAN_CMP
s.insertBefore(BooleanCmp.create(BOOLEAN_CMP_ADDR,
result,
bit,
AC(Address.zero()),
ConditionOperand.NOT_EQUAL(),
new BranchProfileOperand()));
if (DynamicTypeCheck.MIN_DOES_IMPLEMENT_SIZE <= interfaceIndex) {
RegisterOperand doesImplLength =
InsertGuardedUnary(s, ir, ARRAYLENGTH, TypeReference.Int, doesImpl.copy(), TG());
RegisterOperand boundscheck = ir.regpool.makeTempInt();
//save to use the cheaper ADDR version of BOOLEAN_CMP
s.insertBefore(BooleanCmp.create(BOOLEAN_CMP_ADDR,
boundscheck,
doesImplLength,
AC(Address.fromIntSignExtend(interfaceIndex)),
ConditionOperand.GREATER(),
new BranchProfileOperand()));
s.insertBefore(Binary.create(INT_AND, result.copyD2D(), result.copyD2U(), boundscheck.copyD2U()));
}
Instruction continueAt = s.prevInstructionInCodeOrder();
s.remove();
return continueAt;
} else {
// A resolved class (cases 5 and 6 in DynamicTypeCheck)
if (LHSclass.isFinal()) {
// For a final class, we can do a PTR compare of
// rhsTIB and the TIB of the class
Operand classTIB = getTIB(s, ir, LHSclass);
BooleanCmp.mutate(s,
BOOLEAN_CMP_ADDR,
result,
RHStib,
classTIB,
ConditionOperand.EQUAL(),
new BranchProfileOperand());
return s.prevInstructionInCodeOrder();
} else {
// Do the full blown case 5 or 6 typecheck.
int LHSDepth = LHSclass.getTypeDepth();
int LHSId = LHSclass.getId();
RegisterOperand superclassIds =
InsertUnary(s, ir, GET_SUPERCLASS_IDS_FROM_TIB, TypeReference.ShortArray, RHStib);
RegisterOperand refCandidate =
InsertLoadOffset(s,
ir,
USHORT_LOAD,
TypeReference.Short,
superclassIds,
Offset.fromIntZeroExtend(LHSDepth << 1),
new LocationOperand(TypeReference.Short),
TG());
//save to use the cheaper ADDR version of BOOLEAN_CMP
s.insertBefore(BooleanCmp.create(BOOLEAN_CMP_ADDR,
result,
refCandidate,
AC(Address.fromIntZeroExtend(LHSId)),
ConditionOperand.EQUAL(),
new BranchProfileOperand()));
if (DynamicTypeCheck.MIN_SUPERCLASS_IDS_SIZE <= LHSDepth) {
RegisterOperand superclassIdsLength =
InsertGuardedUnary(s, ir, ARRAYLENGTH, TypeReference.Int, superclassIds.copyD2U(), TG());
RegisterOperand boundscheck = ir.regpool.makeTempInt();
//save to use the cheaper ADDR version of BOOLEAN_CMP
s.insertBefore(BooleanCmp.create(BOOLEAN_CMP_ADDR,
boundscheck,
superclassIdsLength,
AC(Address.fromIntSignExtend(LHSDepth)),
ConditionOperand.GREATER(),
new BranchProfileOperand()));
s.insertBefore(Binary.create(INT_AND, result.copyD2D(), result.copyD2U(), boundscheck.copyD2U()));
}
Instruction continueAt = s.prevInstructionInCodeOrder();
s.remove();
return continueAt;
}
}
} else {
// A non-resolved class or interface.
// We expect these to be extremely uncommon in opt code in AOS.
// Mutate s into a call to RuntimeEntrypoints.instanceOf
RVMMethod target = Entrypoints.instanceOfMethod;
Call.mutate2(s,
CALL,
result,
AC(target.getOffset()),
MethodOperand.STATIC(target),
RHSobj,
IC(LHStype.getId()));
return callHelper(s, ir);
}
}
if (LHStype.isArrayType()) {
// Case 2 of DynamicTypeCheck: LHS is an array.
RVMArray LHSArray = (RVMArray) LHStype.peekType();
if (LHSArray != null) {
RVMType innermostElementType = LHSArray.getInnermostElementType();
if (innermostElementType.isPrimitiveType() || innermostElementType.isUnboxedType() ||
(innermostElementType.asClass().isResolved() && innermostElementType.asClass().isFinal())) {
// [^k of primitive or [^k of final class. Just like final classes,
// a PTR compare of rhsTIB and the TIB of the class gives the answer.
Operand classTIB = getTIB(s, ir, LHSArray);
BooleanCmp.mutate(s,
BOOLEAN_CMP_ADDR,
result,
RHStib,
classTIB,
ConditionOperand.EQUAL(),
new BranchProfileOperand());
return s;
}
}
// We're going to have to branch anyways, so reduce to a branching case
// and do the real work there.
return convertToBranchingTypeCheck(s, ir, RHSobj, LHStype, RHStib, result);
}
OptimizingCompilerException.UNREACHABLE();
return null;
}
/**
* Generate wrapper around branching type check to get a
* value producing type check.
* @param s The Instruction that is to be replaced by
* a value producing type check
* @param ir The IR containing the instruction to be expanded.
* @param RHSobj The RegisterOperand containing the rhs object.
* @param LHStype The TypeReference to be tested against.
* @param RHStib The Operand containing the TIB of the rhs.
* @param result The RegisterOperand that the result of dynamic
* @return the opt instruction immediately before the instruction to
* continue expansion.
*/
private static Instruction convertToBranchingTypeCheck(Instruction s, IR ir, Operand RHSobj,
TypeReference LHStype, Operand RHStib,
RegisterOperand result) {
BasicBlock myBlock = s.getBasicBlock();
BasicBlock contBlock = myBlock.splitNodeAt(s, ir);
int subBlockStart = s.getBytecodeIndex();
BasicBlock trueBlock = myBlock.createSubBlock(subBlockStart, ir);
BasicBlock falseBlock = myBlock.createSubBlock(subBlockStart, ir);
myBlock.insertOut(trueBlock);
myBlock.insertOut(falseBlock);
trueBlock.insertOut(contBlock);
falseBlock.insertOut(contBlock);
ir.cfg.linkInCodeOrder(myBlock, trueBlock);
ir.cfg.linkInCodeOrder(trueBlock, falseBlock);
ir.cfg.linkInCodeOrder(falseBlock, contBlock);
trueBlock.appendInstruction(Move.create(INT_MOVE, result, IC(1)));
trueBlock.appendInstruction(Goto.create(GOTO, contBlock.makeJumpTarget()));
falseBlock.appendInstruction(Move.create(INT_MOVE, result.copyD2D(), IC(0)));
return generateBranchingTypeCheck(s,
ir,
RHSobj,
LHStype,
RHStib,
trueBlock,
falseBlock,
ir.regpool.makeTempValidation(),
new BranchProfileOperand());
}
/**
* Generate a branching dynamic type check.
* This routine assumes that the CFG and code order are already
* correctly established.
* This routine must either remove s or mutate it.
*
* @param s The Instruction that is to be replaced by a
* branching type check
* @param ir The IR containing the instruction to be expanded.
* @param RHSobj The RegisterOperand containing the rhs object.
* @param LHStype The TypeReference to be tested against.
* @param RHStib The Operand containing the TIB of the rhs.
* @param trueBlock The BasicBlock to continue at if the typecheck
* evaluates to true
* @param falseBlock The BasicBlock to continue at if the typecheck
* evaluates to false.
* @param oldGuard A suitable guard operand (not necessarily related
* the instruction that is to be replaced).
* @param falseProb The probability that typecheck will branch to the falseBlock
* @return the opt instruction immediately before the instruction to
* continue expansion.
*/
private static Instruction generateBranchingTypeCheck(Instruction s, IR ir, Operand RHSobj,
TypeReference LHStype, Operand RHStib,
BasicBlock trueBlock, BasicBlock falseBlock,
RegisterOperand oldGuard,
BranchProfileOperand falseProb) {
Instruction continueAt = Goto.create(GOTO, trueBlock.makeJumpTarget());
continueAt.copyPosition(s);
s.insertBefore(continueAt);
s.remove();
if (LHStype.isClassType()) {
RVMClass LHSclass = (RVMClass) LHStype.peekType();
if (LHSclass != null && LHSclass.isResolved()) {
// Cases 4, 5, and 6 of DynamicTypeCheck: LHSclass is a resolved
// class or interface
if (LHSclass.isInterface()) {
// A resolved interface (case 4)
int interfaceIndex = LHSclass.getDoesImplementIndex();
int interfaceMask = LHSclass.getDoesImplementBitMask();
RegisterOperand doesImpl =
InsertUnary(continueAt, ir, GET_DOES_IMPLEMENT_FROM_TIB, TypeReference.IntArray, RHStib);
if (DynamicTypeCheck.MIN_DOES_IMPLEMENT_SIZE <= interfaceIndex) {
RegisterOperand doesImplLength =
InsertGuardedUnary(continueAt, ir, ARRAYLENGTH, TypeReference.Int, doesImpl.copyD2U(), TG());
Instruction lengthCheck =
IfCmp.create(INT_IFCMP,
oldGuard,
doesImplLength,
IC(interfaceIndex),
ConditionOperand.LESS_EQUAL(),
falseBlock.makeJumpTarget(),
BranchProfileOperand.unlikely());
if (oldGuard != null) {
oldGuard = oldGuard.copyD2D();
}
continueAt.insertBefore(lengthCheck);
BasicBlock oldBlock = continueAt.getBasicBlock();
oldBlock.splitNodeWithLinksAt(lengthCheck, ir);
oldBlock.insertOut(falseBlock); // required due to splitNode!
}
RegisterOperand entry =
InsertLoadOffset(continueAt,
ir,
INT_LOAD,
TypeReference.Int,
doesImpl,
Offset.fromIntZeroExtend(interfaceIndex << 2),
new LocationOperand(TypeReference.Int),
TG());
RegisterOperand bit =
insertBinary(continueAt, ir, INT_AND, TypeReference.Int, entry, IC(interfaceMask));
continueAt.insertBefore(IfCmp.create(INT_IFCMP,
oldGuard,
bit,
IC(0),
ConditionOperand.EQUAL(),
falseBlock.makeJumpTarget(),
falseProb));
return continueAt;
} else {
// A resolved class (cases 5 and 6 in DynamicTypeCheck)
if (LHSclass.isFinal()) {
// For a final class, we can do a PTR compare of
// rhsTIB and the TIB of the class
Operand classTIB = getTIB(continueAt, ir, LHSclass);
continueAt.insertBefore(IfCmp.create(REF_IFCMP,
oldGuard,
RHStib,
classTIB,
ConditionOperand.NOT_EQUAL(),
falseBlock.makeJumpTarget(),
falseProb));
return continueAt;
} else {
// Do the full blown case 5 or 6 typecheck.
int LHSDepth = LHSclass.getTypeDepth();
int LHSId = LHSclass.getId();
RegisterOperand superclassIds =
InsertUnary(continueAt, ir, GET_SUPERCLASS_IDS_FROM_TIB, TypeReference.ShortArray, RHStib);
if (DynamicTypeCheck.MIN_SUPERCLASS_IDS_SIZE <= LHSDepth) {
RegisterOperand superclassIdsLength =
InsertGuardedUnary(continueAt, ir, ARRAYLENGTH, TypeReference.Int, superclassIds.copyD2U(), TG());
Instruction lengthCheck =
IfCmp.create(INT_IFCMP,
oldGuard,
superclassIdsLength,
IC(LHSDepth),
ConditionOperand.LESS(),
falseBlock.makeJumpTarget(),
BranchProfileOperand.unlikely());
if (oldGuard != null) {
oldGuard = oldGuard.copyD2D();
}
continueAt.insertBefore(lengthCheck);
BasicBlock oldBlock = continueAt.getBasicBlock();
oldBlock.splitNodeWithLinksAt(lengthCheck, ir);
oldBlock.insertOut(falseBlock); // required due to splitNode!
}
RegisterOperand refCandidate =
InsertLoadOffset(continueAt,
ir,
USHORT_LOAD,
TypeReference.Short,
superclassIds,
Offset.fromIntZeroExtend(LHSDepth << 1),
new LocationOperand(TypeReference.Short),
TG());
continueAt.insertBefore(IfCmp.create(INT_IFCMP,
oldGuard,
refCandidate,
IC(LHSId),
ConditionOperand.NOT_EQUAL(),
falseBlock.makeJumpTarget(),
falseProb));
return continueAt;
}
}
} else {
// A non-resolved class or interface. Case 3 of DynamicTypeCheck
// Branch on the result of a call to
// RuntimeEntrypoints.instance
RegisterOperand result = ir.regpool.makeTempInt();
RVMMethod target = Entrypoints.instanceOfMethod;
Instruction call =
Call.create2(CALL,
result,
AC(target.getOffset()),
MethodOperand.STATIC(target),
RHSobj,
IC(LHStype.getId()));
call.copyPosition(continueAt);
continueAt.insertBefore(call);
call = callHelper(call, ir);
continueAt.insertBefore(IfCmp.create(INT_IFCMP,
oldGuard,
result.copyD2U(),
IC(0),
ConditionOperand.EQUAL(),
falseBlock.makeJumpTarget(),
falseProb));
return continueAt;
}
}
if (LHStype.isArrayType()) {
// Case 2 of DynamicTypeCheck: LHS is an array.
RVMArray LHSArray = (RVMArray) LHStype.peekType();
if (LHSArray != null) {
Operand classTIB = getTIB(continueAt, ir, LHSArray);
RVMType innermostElementType = LHSArray.getInnermostElementType();
if (innermostElementType.isPrimitiveType() || innermostElementType.isUnboxedType() ||
(innermostElementType.asClass().isResolved() && innermostElementType.asClass().isFinal())) {
// [^k of primitive or [^k of final class. Just like final classes,
// a PTR compare of rhsTIB and the TIB of the class gives the answer.
continueAt.insertBefore(IfCmp.create(REF_IFCMP,
oldGuard,
RHStib,
classTIB,
ConditionOperand.NOT_EQUAL(),
falseBlock.makeJumpTarget(),
falseProb));
return continueAt;
}
// TODO: branch probability calculation is somewhat bogus for this case.
Instruction shortcircuit =
IfCmp.create(REF_IFCMP,
oldGuard,
RHStib,
classTIB,
ConditionOperand.EQUAL(),
trueBlock.makeJumpTarget(),
new BranchProfileOperand());
if (oldGuard != null) {
oldGuard = oldGuard.copyD2D();
}
continueAt.insertBefore(shortcircuit);
BasicBlock myBlock = shortcircuit.getBasicBlock();
BasicBlock mainBlock = myBlock.splitNodeWithLinksAt(shortcircuit, ir);
myBlock.insertOut(trueBlock); // must come after the splitNodeAt
RegisterOperand rhsType =
InsertUnary(continueAt, ir, GET_TYPE_FROM_TIB, TypeReference.Type, RHStib.copy());
if (innermostElementType.isJavaLangObjectType()) {
IntConstantOperand lhsDimension = IC(LHStype.getDimensionality());
RegisterOperand rhsDimension = getField(continueAt, ir, rhsType, Entrypoints.dimensionField);
Instruction dimTest =
IfCmp2.create(INT_IFCMP2,
oldGuard,
rhsDimension,
lhsDimension,
ConditionOperand.GREATER(),
trueBlock.makeJumpTarget(),
((BranchProfileOperand) falseProb.copy()).flip(),
ConditionOperand.LESS(),
falseBlock.makeJumpTarget(),
(BranchProfileOperand) falseProb.copy());
if (oldGuard != null) {
oldGuard = oldGuard.copyD2D();
}
continueAt.insertBefore(dimTest);
//BasicBlock testBlock =
mainBlock.splitNodeWithLinksAt(dimTest, ir);
mainBlock.insertOut(trueBlock);
mainBlock.insertOut(falseBlock);
RegisterOperand rhsInnermostElementTypeDimension =
getField(continueAt, ir, rhsType.copyU2U(), Entrypoints.innermostElementTypeDimensionField);
continueAt.insertBefore(IfCmp.create(INT_IFCMP,
oldGuard,
rhsInnermostElementTypeDimension,
IC(0),
ConditionOperand.NOT_EQUAL(),
falseBlock.makeJumpTarget(),
falseProb));
return continueAt;
}
}
// Not a case we want to handle inline
RVMMethod target = Entrypoints.instanceOfMethod;
RegisterOperand callResult = ir.regpool.makeTempInt();
Instruction call =
Call.create2(CALL,
callResult,
AC(target.getOffset()),
MethodOperand.STATIC(target),
RHSobj,
IC(LHStype.getId()));
call.copyPosition(continueAt);
continueAt.insertBefore(call);
call = callHelper(call, ir);
continueAt.insertBefore(IfCmp.create(INT_IFCMP,
oldGuard,
callResult.copyD2U(),
IC(0),
ConditionOperand.EQUAL(),
falseBlock.makeJumpTarget(),
falseProb));
return continueAt;
}
OptimizingCompilerException.UNREACHABLE();
return null;
}
// helper routine.
// s is a conditional branch; Make it the last instruction in its block
// if it isn't already and return the fallthrough block.
private static BasicBlock fallThroughBB(Instruction s, IR ir) {
Instruction next = s.nextInstructionInCodeOrder();
if (next.operator() == BBEND) {
return next.getBasicBlock().nextBasicBlockInCodeOrder();
} else if (next.operator() == GOTO) {
BasicBlock target = next.getBranchTarget();
next.remove();
return target;
} else {
BasicBlock myBlock = s.getBasicBlock();
BasicBlock succBlock = myBlock.splitNodeAt(s, ir);
myBlock.insertOut(succBlock);
ir.cfg.linkInCodeOrder(myBlock, succBlock);
return succBlock;
}
}
private static BasicBlock advanceBlock(int bcIndex, BasicBlock curBlock, IR ir) {
BasicBlock newBlock = curBlock.createSubBlock(bcIndex, ir);
curBlock.insertOut(newBlock);
ir.cfg.linkInCodeOrder(curBlock, newBlock);
return newBlock;
}
}