mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-05 08:35:26 +00:00
2671 lines
87 KiB
C++
2671 lines
87 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
*
|
|
* The contents of this file are subject to the Netscape Public
|
|
* License Version 1.1 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS
|
|
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
|
* implied. See the License for the specific language governing
|
|
* rights and limitations under the License.
|
|
*
|
|
* The Original Code is mozilla.org code.
|
|
*
|
|
* The Initial Developer of the Original Code is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All
|
|
* Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*/
|
|
|
|
#include "BytecodeTranslator.h"
|
|
#include "PrimitiveBuilders.h"
|
|
#include "MemoryAccess.h"
|
|
#include "FieldOrMethod.h"
|
|
|
|
static const UnaryBuilder *const unaryBuilders[] =
|
|
{
|
|
&builder_Neg_II, // bcINeg
|
|
&builder_Neg_LL, // bcLNeg
|
|
&builder_Neg_FF, // bcFNeg
|
|
&builder_Neg_DD, // bcDNeg
|
|
0, // bcIShl
|
|
0, // bcLShl
|
|
0, // bcIShr
|
|
0, // bcLShr
|
|
0, // bcIUShr
|
|
0, // bcLUShr
|
|
0, // bcIAnd
|
|
0, // bcLAnd
|
|
0, // bcIOr
|
|
0, // bcLOr
|
|
0, // bcIXor
|
|
0, // bcLXor
|
|
0, // bcIInc
|
|
&builder_Conv_IL, // bcI2L
|
|
&builder_Conv_IF, // bcI2F
|
|
&builder_Conv_ID, // bcI2D
|
|
&builder_Conv_LI, // bcL2I
|
|
&builder_Conv_LF, // bcL2F
|
|
&builder_Conv_LD, // bcL2D
|
|
&builder_Conv_FI, // bcF2I
|
|
&builder_Conv_FL, // bcF2L
|
|
&builder_Conv_FD, // bcF2D
|
|
&builder_Conv_DI, // bcD2I
|
|
&builder_Conv_DL, // bcD2L
|
|
&builder_Conv_DF, // bcD2F
|
|
&builder_ExtB_II, // bcInt2Byte
|
|
&builder_ExtC_II, // bcInt2Char
|
|
&builder_ExtS_II // bcInt2Short
|
|
};
|
|
|
|
static const BinaryBuilder *const binaryBuilders[] =
|
|
{
|
|
&builder_AddCoal_III, // bcIAdd
|
|
&builder_AddCoal_LLL, // bcLAdd
|
|
&builder_Add_FFF, // bcFAdd
|
|
&builder_Add_DDD, // bcDAdd
|
|
&builder_SubCoal_III, // bcISub
|
|
&builder_SubCoal_LLL, // bcLSub
|
|
&builder_Sub_FFF, // bcFSub
|
|
&builder_Sub_DDD, // bcDSub
|
|
&builder_Mul_III, // bcIMul
|
|
&builder_Mul_LLL, // bcLMul
|
|
&builder_Mul_FFF, // bcFMul
|
|
&builder_Mul_DDD, // bcDMul
|
|
&builder_Div_III, // bcIDiv
|
|
&builder_Div_LLL, // bcLDiv
|
|
&builder_Div_FFF, // bcFDiv
|
|
&builder_Div_DDD, // bcDDiv
|
|
&builder_Mod_III, // bcIRem
|
|
&builder_Mod_LLL, // bcLRem
|
|
&builder_Rem_FFF, // bcFRem
|
|
&builder_Rem_DDD, // bcDRem
|
|
0, // bcINeg
|
|
0, // bcLNeg
|
|
0, // bcFNeg
|
|
0, // bcDNeg
|
|
&builder_Shl_III, // bcIShl
|
|
&builder_Shl_LIL, // bcLShl
|
|
&builder_Shr_III, // bcIShr
|
|
&builder_Shr_LIL, // bcLShr
|
|
&builder_UShr_III, // bcIUShr
|
|
&builder_UShr_LIL, // bcLUShr
|
|
&builder_And_III, // bcIAnd
|
|
&builder_And_LLL, // bcLAnd
|
|
&builder_Or_III, // bcIOr
|
|
&builder_Or_LLL, // bcLOr
|
|
&builder_Xor_III, // bcIXor
|
|
&builder_Xor_LLL, // bcLXor
|
|
0, // bcIInc
|
|
0, // bcI2L
|
|
0, // bcI2F
|
|
0, // bcI2D
|
|
0, // bcL2I
|
|
0, // bcL2F
|
|
0, // bcL2D
|
|
0, // bcF2I
|
|
0, // bcF2L
|
|
0, // bcF2D
|
|
0, // bcD2I
|
|
0, // bcD2L
|
|
0, // bcD2F
|
|
0, // bcInt2Byte
|
|
0, // bcInt2Char
|
|
0, // bcInt2Short
|
|
&builder_Cmp3_LLI, // bcLCmp
|
|
&builder_Cmp3L_FFI, // bcFCmpL
|
|
&builder_Cmp3G_FFI, // bcFCmpG
|
|
&builder_Cmp3L_DDI, // bcDCmpL
|
|
&builder_Cmp3G_DDI // bcDCmpG
|
|
};
|
|
|
|
static const ComparisonBuilder<Int32> *const unaryComparisonBuilders[] =
|
|
{
|
|
&builder_CmpEq_IIC, // bcIfEq
|
|
&builder_CmpEq_IIC, // bcIfNe
|
|
&builder_Cmp_IIC, // bcIfLt
|
|
&builder_Cmp_IIC, // bcIfGe
|
|
&builder_Cmp_IIC, // bcIfGt
|
|
&builder_Cmp_IIC // bcIfLe
|
|
};
|
|
|
|
static const BinaryBuilder *const binaryComparisonBuilders[] =
|
|
{
|
|
&builder_CmpEq_IIC, // bcIf_ICmpEq
|
|
&builder_CmpEq_IIC, // bcIf_ICmpNe
|
|
&builder_Cmp_IIC, // bcIf_ICmpLt
|
|
&builder_Cmp_IIC, // bcIf_ICmpGe
|
|
&builder_Cmp_IIC, // bcIf_ICmpGt
|
|
&builder_Cmp_IIC, // bcIf_ICmpLe
|
|
&builder_CmpUEq_AAC, // bcIf_ACmpEq
|
|
&builder_CmpUEq_AAC // bcIf_ACmpNe
|
|
};
|
|
|
|
|
|
static const Condition2 bytecodeConditionals[] =
|
|
{
|
|
condEq, // bcIfEq
|
|
condNe, // bcIfNe
|
|
condLt, // bcIfLt
|
|
condGe, // bcIfGe
|
|
condGt, // bcIfGt
|
|
condLe, // bcIfLe
|
|
condEq, // bcIf_ICmpEq
|
|
condNe, // bcIf_ICmpNe
|
|
condLt, // bcIf_ICmpLt
|
|
condGe, // bcIf_ICmpGe
|
|
condGt, // bcIf_ICmpGt
|
|
condLe, // bcIf_ICmpLe
|
|
condEq, // bcIf_ACmpEq
|
|
condNe, // bcIf_ACmpNe
|
|
cond0, // bcGoto
|
|
cond0, // bcJsr
|
|
cond0, // bcRet
|
|
cond0, // bcTableSwitch
|
|
cond0, // bcLookupSwitch
|
|
cond0, // bcIReturn
|
|
cond0, // bcLReturn
|
|
cond0, // bcFReturn
|
|
cond0, // bcDReturn
|
|
cond0, // bcAReturn
|
|
cond0, // bcReturn
|
|
cond0, // bcGetStatic
|
|
cond0, // bcPutStatic
|
|
cond0, // bcGetField
|
|
cond0, // bcPutField
|
|
cond0, // bcInvokeVirtual
|
|
cond0, // bcInvokeSpecial
|
|
cond0, // bcInvokeStatic
|
|
cond0, // bcInvokeInterface
|
|
cond0, // bc_unused_
|
|
cond0, // bcNew
|
|
cond0, // bcNewArray
|
|
cond0, // bcANewArray
|
|
cond0, // bcArrayLength
|
|
cond0, // bcAThrow
|
|
cond0, // bcCheckCast
|
|
cond0, // bcInstanceOf
|
|
cond0, // bcMonitorEnter
|
|
cond0, // bcMonitorExit
|
|
cond0, // bcWide
|
|
cond0, // bcMultiANewArray
|
|
condEq, // bcIfNull
|
|
condNe // bcIfNonnull
|
|
};
|
|
|
|
|
|
const TypeKind arrayAccessTypeKinds[] =
|
|
{
|
|
tkInt, // bcIALoad, bcIAStore
|
|
tkLong, // bcLALoad, bcLAStore
|
|
tkFloat, // bcFALoad, bcFAStore
|
|
tkDouble, // bcDALoad, bcDAStore
|
|
tkObject, // bcAALoad, bcAAStore
|
|
tkByte, // bcBALoad, bcBAStore
|
|
tkChar, // bcCALoad, bcCAStore
|
|
tkShort // bcSALoad, bcSAStore
|
|
};
|
|
|
|
|
|
const LoadBuilder *const loadBuilders[] =
|
|
{
|
|
0, // tkVoid
|
|
&builder_LdU_AB, // tkBoolean
|
|
&builder_LdU_AB, // tkUByte
|
|
&builder_LdS_AB, // tkByte
|
|
&builder_LdU_AH, // tkChar
|
|
&builder_LdS_AH, // tkShort
|
|
&builder_Ld_AI, // tkInt
|
|
&builder_Ld_AL, // tkLong
|
|
&builder_Ld_AF, // tkFloat
|
|
&builder_Ld_AD, // tkDouble
|
|
&builder_Ld_AA, // tkObject
|
|
&builder_Ld_AA, // tkSpecial
|
|
&builder_Ld_AA, // tkArray
|
|
&builder_Ld_AA // tkInterface
|
|
};
|
|
|
|
|
|
const StoreBuilder *const storeBuilders[] =
|
|
{
|
|
0, // tkVoid
|
|
&builder_St_AB, // tkBoolean
|
|
&builder_St_AB, // tkUByte
|
|
&builder_St_AB, // tkByte
|
|
&builder_St_AH, // tkChar
|
|
&builder_St_AH, // tkShort
|
|
&builder_St_AI, // tkInt
|
|
&builder_St_AL, // tkLong
|
|
&builder_St_AF, // tkFloat
|
|
&builder_St_AD, // tkDouble
|
|
&builder_St_AA, // tkObject
|
|
&builder_St_AA, // tkSpecial
|
|
&builder_St_AA, // tkArray
|
|
&builder_St_AA // tkInterface
|
|
};
|
|
|
|
|
|
static const SysCall *const newArrayCalls[natLimit - natMin] =
|
|
{
|
|
&scNewBooleanArray, // natBoolean
|
|
&scNewCharArray, // natChar
|
|
&scNewFloatArray, // natFloat
|
|
&scNewDoubleArray, // natDouble
|
|
&scNewByteArray, // natByte
|
|
&scNewShortArray, // natShort
|
|
&scNewIntArray, // natInt
|
|
&scNewLongArray // natLong
|
|
};
|
|
|
|
|
|
static const SysCall *const typeToArrayCalls[nPrimitiveTypeKinds] =
|
|
{
|
|
NULL, // tkVoid
|
|
&scNewBooleanArray, // tkBoolean
|
|
&scNewByteArray, // tkUByte
|
|
&scNewByteArray, // tkByte
|
|
&scNewCharArray, // tkChar
|
|
&scNewShortArray, // tkShort
|
|
&scNewIntArray, // tkInt
|
|
&scNewLongArray, // tkLong
|
|
&scNewFloatArray, // tkFloat
|
|
&scNewDoubleArray // tkDouble
|
|
};
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// BytecodeTranslator::BlockTranslator
|
|
|
|
|
|
//
|
|
// Create a new, empty control node to hold the rest of this BytecodeBlock and set cn
|
|
// to be that control node. Make edge e be the one predecessor (so far) of the
|
|
// new control node.
|
|
//
|
|
void BytecodeTranslator::BlockTranslator::appendControlNode(ControlEdge &e)
|
|
{
|
|
ControlNode &nextControlNode = getControlGraph().newControlNode();
|
|
nextControlNode.addPredecessor(e);
|
|
cn = &nextControlNode;
|
|
}
|
|
|
|
|
|
//
|
|
// Designate this ControlNode to be an Exc, Throw, or AExc node, depending on the given
|
|
// ControlKind. This node will have zero (Throw) or one (Exc or AExc) normal successors
|
|
// (whose targets are not initialized -- the caller will have to do that) followed by as
|
|
// many exceptional successors as there are possible handlers for an exception of class
|
|
// exceptionClass thrown from within this BytecodeBlock. The exceptional successor edges are
|
|
// fully initialized, as is the ExceptionExtra node's handlerFilters array.
|
|
//
|
|
// If the ControlNode will be an Exc or Throw node, tryPrimitive must be a primitive
|
|
// located inside this node that can throw an exception; if the ControlNode will be an
|
|
// AExc node, tryPrimitive must be nil.
|
|
//
|
|
// translationEnv's stack is ignored.
|
|
// This routine assumes ownership of translationEnv if canOwnEnv is true (thereby sometimes eliminating
|
|
// an unnecessary copy).
|
|
//
|
|
void BytecodeTranslator::BlockTranslator::genException(ControlKind ck, Primitive *tryPrimitive, const Class &exceptionClass,
|
|
bool canOwnEnv) const
|
|
{
|
|
assert(cn && hasExceptionOutgoingEdges(ck));
|
|
Uint32 nNormalSuccessors = ck != ckThrow;
|
|
Uint32 maxNHandlers = block.handlersEnd - block.successorsEnd + 1;
|
|
const Class **filters = new(primitivePool) const Class *[maxNHandlers];
|
|
ControlEdge *successors = new(primitivePool) ControlEdge[nNormalSuccessors + maxNHandlers];
|
|
|
|
// Copy the list of handlers from the BytecodeBlock to the ExceptionExtra.
|
|
// Remove any handlers that can't apply.
|
|
const Class **srcFilter = block.exceptionClasses;
|
|
const Class **dstFilter = filters;
|
|
BasicBlock **srcSuccessor = block.successorsEnd;
|
|
ControlEdge *dstSuccessor = successors + nNormalSuccessors;
|
|
BasicBlock *successor;
|
|
while (--maxNHandlers) {
|
|
const Class *filter = *srcFilter++;
|
|
successor = *srcSuccessor++;
|
|
|
|
assert(filter->implements(Standard::get(cThrowable)));
|
|
if (filter == &Standard::get(cThrowable))
|
|
goto haveCatchAll;
|
|
if (exceptionClass.implements(*filter) || filter->implements(exceptionClass)) {
|
|
*dstFilter++ = filter;
|
|
bt.linkPredecessor(*successor, *dstSuccessor++, getEnv(), false);
|
|
}
|
|
}
|
|
// We haven't reached a catch-all handler, so direct remaining exceptions to
|
|
// the End block, which indicates that they are propagated out of this function.
|
|
successor = bt.bytecodeGraph.endBlock;
|
|
|
|
haveCatchAll:
|
|
// Fill in the catch-all handler.
|
|
*dstFilter++ = &Standard::get(cThrowable);
|
|
bt.linkPredecessor(*successor, *dstSuccessor, getEnv(), canOwnEnv);
|
|
|
|
cn->setControlException(ck, dstFilter - filters, filters, tryPrimitive, successors);
|
|
}
|
|
|
|
|
|
//
|
|
// Generate an unconditional throw of the given exception, which is guaranteed not to
|
|
// have a null value. The exception's class must be a subclass of exceptionClass, which
|
|
// must be a subclass of Throwable; exceptionClass is not checked for integrity.
|
|
//
|
|
// Set cn to nil to indicate that no more control nodes should be generated.
|
|
//
|
|
// This routine assumes ownership of translationEnv if canOwnEnv is true (thereby sometimes eliminating
|
|
// an unnecessary copy).
|
|
//
|
|
void BytecodeTranslator::BlockTranslator::genThrowCommon(const VariableOrConstant &exception, const Class &exceptionClass,
|
|
bool canOwnEnv)
|
|
{
|
|
// asharma - fix me
|
|
Primitive &prim = makeSysCall(scThrow, 0, 1, &exception, 0, 0, *cn, 0);
|
|
genException(ckThrow, &prim, exceptionClass, canOwnEnv);
|
|
cn = 0;
|
|
}
|
|
|
|
|
|
//
|
|
// Generate an unconditional throw of the given exception, which is a system-defined
|
|
// instance of Throwable or one of its subclasses; exceptionObject is not checked for
|
|
// integrity. Set cn to nil to indicate that no more control nodes should be generated.
|
|
//
|
|
// This routine assumes ownership of translationEnv if canOwnEnv is true (thereby sometimes eliminating
|
|
// an unnecessary copy).
|
|
//
|
|
void BytecodeTranslator::BlockTranslator::genSimpleThrow(cobj exceptionObject, bool canOwnEnv)
|
|
{
|
|
VariableOrConstant exception;
|
|
exception.setConstant(objectAddress(exceptionObject));
|
|
genThrowCommon(exception, exceptionObject->getClass(), canOwnEnv);
|
|
}
|
|
|
|
|
|
//
|
|
// Generate an exc node that indicates that prim might throw an exception
|
|
// of the given class or one of its subclasses; exceptionClass must be Throwable
|
|
// or one of its subclasses and is not checked for integrity.
|
|
// prim is the primitive inside cn that can throw exceptions. There must be no
|
|
// other primitives in cn that rely on prim's outgoing data edge, if any.
|
|
//
|
|
// Set cn to a control node to use for the next portion of this BytecodeBlock;
|
|
// that control node is always empty.
|
|
//
|
|
// This routine does not assume ownership of translationEnv.
|
|
//
|
|
void BytecodeTranslator::BlockTranslator::genPossibleThrow(Primitive &prim, const Class &exceptionClass)
|
|
{
|
|
genException(ckExc, &prim, exceptionClass, false);
|
|
appendControlNode(cn->getNormalSuccessor());
|
|
}
|
|
|
|
|
|
//
|
|
// Make cn into a basic block control node. Allocate its ControlExtra record
|
|
// and link it to the successor BasicBlock.
|
|
// Set cn to nil to indicate that no more control nodes should be generated.
|
|
//
|
|
// If the basic block would be empty and have only one predecessor, eliminate it
|
|
// and instead link the successor to the predecessor.
|
|
//
|
|
void BytecodeTranslator::BlockTranslator::createBasicBlock(BasicBlock &successor)
|
|
{
|
|
ControlEdge *edge;
|
|
|
|
if (cn->empty() && cn->getPredecessors().lengthIs(1)) {
|
|
// Eliminate this basic block and recycle its ControlNode.
|
|
edge = &cn->getPredecessors().first();
|
|
assert(block.getGeneratedControlNodes());
|
|
if (block.getFirstControlNode() == cn)
|
|
block.getFirstControlNode() = 0;
|
|
edge->clearTarget(); // Clear cn's predecessor linked list.
|
|
getControlGraph().recycle(*cn);
|
|
} else {
|
|
cn->setControlBlock();
|
|
edge = &cn->getNormalSuccessor();
|
|
}
|
|
bt.linkPredecessor(successor, *edge, *translationEnv, true);
|
|
cn = 0;
|
|
}
|
|
|
|
|
|
//
|
|
// Make cn into an IF control node. Allocate its ControlExtra record and
|
|
// link it to the successor BasicBlocks. c contains the condition variable.
|
|
// If c satisfies cond2 (or reverse(cond2) if reverseCond2 is true), the IF
|
|
// will branch to the true successor; otherwise, it will branch to the false
|
|
// successor. On exit set cn to nil.
|
|
//
|
|
void BytecodeTranslator::BlockTranslator::createIf(Condition2 cond2,
|
|
bool reverseCond2,
|
|
VariableOrConstant &c,
|
|
BasicBlock &successorFalse,
|
|
BasicBlock &successorTrue,
|
|
Uint32 bci)
|
|
{
|
|
if (reverseCond2)
|
|
cond2 = reverse(cond2);
|
|
|
|
bool constResult;
|
|
DataNode *cp = partialComputeComparison(cond2, c, constResult);
|
|
if (cp) {
|
|
// The value of the selector is not known at compile time.
|
|
cn->setControlIf(cond2, *cp, bci);
|
|
bt.linkPredecessor(successorTrue, cn->getTrueSuccessor(), *translationEnv, false);
|
|
bt.linkPredecessor(successorFalse, cn->getFalseSuccessor(), *translationEnv, true);
|
|
cn = 0;
|
|
} else
|
|
// The comparison result is known at compile time.
|
|
createBasicBlock(constResult ? successorTrue : successorFalse);
|
|
}
|
|
|
|
|
|
//
|
|
// Same as createIf except that if the selector is false, continue processing this
|
|
// BytecodeBlock using the new control node in cn. If the selector is true, go to
|
|
// successorTrue as in createIf.
|
|
//
|
|
// Set cn to a control node to use for the next portion of this BytecodeBlock or
|
|
// nil if the selector is known to always be true.
|
|
//
|
|
void
|
|
BytecodeTranslator::BlockTranslator::createContinuedIf(Condition2 cond2,
|
|
bool reverseCond2,
|
|
VariableOrConstant &c,
|
|
BasicBlock &successorTrue,
|
|
Uint32 bci)
|
|
{
|
|
if (reverseCond2)
|
|
cond2 = reverse(cond2);
|
|
|
|
bool constResult;
|
|
DataNode *cp = partialComputeComparison(cond2, c, constResult);
|
|
if (cp) {
|
|
// The value of the selector is not known at compile time.
|
|
cn->setControlIf(cond2, *cp, bci);
|
|
bt.linkPredecessor(successorTrue, cn->getTrueSuccessor(), *translationEnv, false);
|
|
appendControlNode(cn->getFalseSuccessor());
|
|
} else if (constResult)
|
|
// The condition is always true.
|
|
createBasicBlock(successorTrue);
|
|
}
|
|
|
|
|
|
//
|
|
// Create control and data flow nodes to make sure that arg (which must have
|
|
// pointer kind) is non-null.
|
|
//
|
|
// Set cn to a control node to use for the next portion of this BytecodeBlock or
|
|
// nil if the rest of this BytecodeBlock is dead.
|
|
//
|
|
void BytecodeTranslator::BlockTranslator::genNullGuard(const VariableOrConstant &arg, Uint32 bci)
|
|
{
|
|
Primitive *prim;
|
|
|
|
switch (makeChkNull(arg, prim, cn, bci)) {
|
|
case chkAlwaysOK:
|
|
break;
|
|
case chkNeverOK:
|
|
genSimpleThrow(&Standard::get(oNullPointerException), true);
|
|
break;
|
|
default:
|
|
genPossibleThrow(*prim, Standard::get(cNullPointerException));
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Create control and data flow nodes for a tableswitch bytecode.
|
|
// bc points immediately after the tableswitch opcode.
|
|
//
|
|
// Set cn to nil to indicate that no more control nodes should be generated.
|
|
//
|
|
void BytecodeTranslator::BlockTranslator::genTableSwitch(const bytecode *bc,
|
|
Uint32 bci)
|
|
{
|
|
VariableOrConstant arg1;
|
|
VariableOrConstant arg2;
|
|
VariableOrConstant arg3;
|
|
TranslationEnv &env = *translationEnv;
|
|
|
|
env.pop().extract(arg1, bci);
|
|
bc = BytecodeGraph::switchAlign(bc) + 4; // Skip past padding and default target.
|
|
Int32 low = readBigSWord(bc);
|
|
Int32 high = readBigSWord(bc + 4);
|
|
bc += 8;
|
|
assert(low <= high);
|
|
Uint32 nCases = high - low + 1;
|
|
bc += nCases * 4;
|
|
assert(bc == block.bytecodesEnd);
|
|
assert(block.nSuccessors() == nCases + 1);
|
|
|
|
// Do the range check; jump to the default case if out of range.
|
|
builder_AddCoal_III.specializeArgConst(arg1, -low, arg2, cn);
|
|
Primitive *prim = builder_CmpU_IIC.specializeArgConst(arg2, (Int32)nCases, arg3, cn);
|
|
createContinuedIf(condGe, prim != 0, arg3, block.getSuccessor(0), bci);
|
|
|
|
BasicBlock **successorsEnd = block.successorsEnd;
|
|
BasicBlock **nextSuccessor = block.successorsBegin + 1;
|
|
if (cn)
|
|
if (arg2.isConstant())
|
|
// The switch argument is a constant, so go directly to the corresponding target.
|
|
createBasicBlock(block.getSuccessor(1 + arg2.getConstant().i - low));
|
|
else {
|
|
// The value of the switch argument is not known at compile time.
|
|
cn->setControlSwitch(arg2.getVariable(), nCases, bci);
|
|
ControlEdge *ce = cn->getSwitchSuccessors();
|
|
while (nextSuccessor != successorsEnd)
|
|
bt.linkPredecessor(**nextSuccessor++, *ce++, env, false);
|
|
cn = 0;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Create control and data flow nodes for a lookupswitch bytecode.
|
|
// bc points immediately after the lookupswitch opcode.
|
|
//
|
|
// Set cn to nil to indicate that no more control nodes should be generated.
|
|
//
|
|
void BytecodeTranslator::BlockTranslator::genLookupSwitch(const bytecode *bc,
|
|
Uint32 bci)
|
|
{
|
|
VariableOrConstant arg;
|
|
|
|
translationEnv->pop().extract(arg, bci);
|
|
bc = BytecodeGraph::switchAlign(bc) + 4; // Skip past padding and default target.
|
|
Uint32 nPairs = readBigUWord(bc);
|
|
bc += 4;
|
|
assert(block.nSuccessors() == nPairs + 1);
|
|
if (nPairs == 0)
|
|
createBasicBlock(block.getSuccessor(0));
|
|
else {
|
|
BasicBlock **nextSuccessor = block.successorsBegin + 1;
|
|
while (true) {
|
|
VariableOrConstant result;
|
|
Int32 match = readBigSWord(bc);
|
|
bc += 8;
|
|
Primitive *prim = builder_CmpEq_IIC.specializeArgConst(arg, match, result, cn);
|
|
nPairs--;
|
|
if (nPairs) {
|
|
createContinuedIf(condEq, prim != 0, result,
|
|
**nextSuccessor++, bci);
|
|
if (!cn) {
|
|
// We're done early.
|
|
bc += nPairs * 8;
|
|
break;
|
|
}
|
|
} else {
|
|
createIf(condEq, prim != 0, result, block.getSuccessor(0),
|
|
**nextSuccessor, bci);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
assert(bc == block.bytecodesEnd);
|
|
}
|
|
|
|
|
|
//
|
|
// Create data flow nodes to read the type of an object. The object pointer should
|
|
// already have been checked to make sure it is not null.
|
|
//
|
|
inline void
|
|
BytecodeTranslator::BlockTranslator::genReadObjectType(const VariableOrConstant &object, VariableOrConstant &type, Uint32 bci) const {
|
|
assert(objectTypeOffset == 0);
|
|
builder_Ld_AA.makeLoad(0, object, type, false, true, cn, bci);
|
|
}
|
|
|
|
|
|
//
|
|
// Create data flow nodes for a getstatic bytecode.
|
|
// cpi is the constant table index of the static variable entry.
|
|
//
|
|
void BytecodeTranslator::BlockTranslator::genGetStatic(ConstantPoolIndex cpi, Uint32 bci) const
|
|
{
|
|
ValueKind vk;
|
|
addr address;
|
|
bool isVolatile;
|
|
bool isConstant;
|
|
TypeKind tk = classFileSummary.lookupStaticField(cpi, vk, address, isVolatile, isConstant);
|
|
TranslationEnv &env = *translationEnv;
|
|
|
|
DataNode *memory = &env.memory().extract(bci);
|
|
VariableOrConstant result;
|
|
loadBuilders[tk]->makeLoad(&memory, address, result, isVolatile,
|
|
isConstant, cn, bci);
|
|
env.memory().define(*memory);
|
|
env.push1or2(isDoublewordKind(vk)).define(result, cn, envPool);
|
|
}
|
|
|
|
|
|
//
|
|
// Create data flow nodes for a putstatic bytecode.
|
|
// cpi is the constant table index of the static variable entry.
|
|
//
|
|
void BytecodeTranslator::BlockTranslator::genPutStatic(ConstantPoolIndex cpi,
|
|
Uint32 bci) const
|
|
{
|
|
ValueKind vk;
|
|
addr address;
|
|
bool isVolatile;
|
|
bool isConstant;
|
|
TypeKind tk = classFileSummary.lookupStaticField(cpi, vk, address, isVolatile, isConstant);
|
|
TranslationEnv &env = *translationEnv;
|
|
|
|
if (isConstant)
|
|
verifyError(VerifyError::writeToConst);
|
|
VariableOrConstant a;
|
|
a.setConstant(address);
|
|
VariableOrConstant value;
|
|
env.pop1or2(isDoublewordKind(vk)).extract(value, bci);
|
|
Primitive &memoryOut = storeBuilders[tk]->makeStore(env.memory().extract(bci), a, value,
|
|
isVolatile, cn, bci);
|
|
env.memory().define(memoryOut);
|
|
}
|
|
|
|
|
|
//
|
|
// Create control and data flow nodes for a getfield bytecode.
|
|
// cpi is the constant table index of the field entry.
|
|
//
|
|
// Set cn to a control node to use for the next statement of this BytecodeBlock or
|
|
// nil if the rest of this BytecodeBlock is dead.
|
|
//
|
|
void BytecodeTranslator::BlockTranslator::genGetField(ConstantPoolIndex cpi,
|
|
Uint32 bci)
|
|
{
|
|
ValueKind vk;
|
|
Uint32 offset;
|
|
bool isVolatile;
|
|
bool isConstant;
|
|
TypeKind tk = classFileSummary.lookupInstanceField(cpi, vk, offset, isVolatile, isConstant);
|
|
TranslationEnv &env = *translationEnv;
|
|
|
|
VariableOrConstant objectAddr;
|
|
env.pop().extract(objectAddr, bci);
|
|
genNullGuard(objectAddr, bci);
|
|
if (cn) {
|
|
// Get the address of the field.
|
|
VariableOrConstant fieldAddr;
|
|
builder_Add_AIA.specializeArgConst(objectAddr, (Int32)offset, fieldAddr, cn);
|
|
|
|
// Emit the load instruction.
|
|
DataNode *memory = &env.memory().extract(bci);
|
|
VariableOrConstant result;
|
|
loadBuilders[tk]->makeLoad(&memory, fieldAddr, result, isVolatile, isConstant, cn, bci);
|
|
env.memory().define(*memory);
|
|
env.push1or2(isDoublewordKind(vk)).define(result, cn, envPool);
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Create control and data flow nodes for a putfield bytecode.
|
|
// cpi is the constant table index of the field entry.
|
|
//
|
|
// Set cn to a control node to use for the next statement of this BytecodeBlock or
|
|
// nil if the rest of this BytecodeBlock is dead.
|
|
//
|
|
void BytecodeTranslator::BlockTranslator::genPutField(ConstantPoolIndex cpi,
|
|
Uint32 bci)
|
|
{
|
|
ValueKind vk;
|
|
Uint32 offset;
|
|
bool isVolatile;
|
|
bool isConstant;
|
|
TypeKind tk = classFileSummary.lookupInstanceField(cpi, vk, offset, isVolatile, isConstant);
|
|
TranslationEnv &env = *translationEnv;
|
|
|
|
if (isConstant)
|
|
verifyError(VerifyError::writeToConst);
|
|
VariableOrConstant value;
|
|
VariableOrConstant objectAddr;
|
|
env.pop1or2(isDoublewordKind(vk)).extract(value, bci);
|
|
env.pop().extract(objectAddr, bci);
|
|
genNullGuard(objectAddr, bci);
|
|
if (cn) {
|
|
// Get the address of the field.
|
|
VariableOrConstant fieldAddr;
|
|
builder_Add_AIA.specializeArgConst(objectAddr, (Int32)offset, fieldAddr, cn);
|
|
|
|
// Emit the store instruction.
|
|
Primitive &memoryOut =
|
|
storeBuilders[tk]->makeStore(env.memory().extract(bci), fieldAddr,
|
|
value, isVolatile, cn, bci);
|
|
env.memory().define(memoryOut);
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Create control and data flow nodes to read the length of an array.
|
|
// Make sure that arrayAddr isn't nil and return the array length in arrayLength.
|
|
//
|
|
// Set cn to a control node to use for the next portion of this BytecodeBlock or
|
|
// nil if the rest of this BytecodeBlock is dead.
|
|
//
|
|
void BytecodeTranslator::BlockTranslator::genReadArrayLength(const VariableOrConstant &arrayAddr, VariableOrConstant &arrayLength, Uint32 bci)
|
|
{
|
|
genNullGuard(arrayAddr, bci);
|
|
if (cn) {
|
|
// Get the address of the field.
|
|
VariableOrConstant fieldAddr;
|
|
builder_Add_AIA.specializeArgConst(arrayAddr, arrayLengthOffset, fieldAddr, cn);
|
|
|
|
// Emit the load instruction.
|
|
builder_Ld_AI.makeLoad(0, fieldAddr, arrayLength, false, true, cn, bci);
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Create control and data flow nodes to make sure that the object can be
|
|
// written into the object array, which should already have been checked to
|
|
// make sure it is not null.
|
|
//
|
|
// Set cn to a control node to use for the next portion of this BytecodeBlock or
|
|
// nil if the rest of this BytecodeBlock is dead.
|
|
//
|
|
void BytecodeTranslator::BlockTranslator::genArrayCastGuard(const VariableOrConstant &array, const VariableOrConstant &object)
|
|
{
|
|
assert(object.hasKind(vkAddr) && array.hasKind(vkAddr));
|
|
// We can write nil into any object array so we don't bother issuing a check
|
|
// if we know that we're writing a nil.
|
|
if (!object.isConstant() || !object.getConstant().a) {
|
|
// Do we know the actual types of both the object and the array?
|
|
Type *objectType = object.getDynamicType();
|
|
if (objectType) {
|
|
Type *arrayType = array.getDynamicType();
|
|
if (arrayType) {
|
|
// Yes. Perform the check statically.
|
|
assert(arrayType->typeKind == tkArray);
|
|
if (!implements(*objectType, static_cast<Array *>(arrayType)->componentType))
|
|
genSimpleThrow(&Standard::get(oArrayStoreException), true);
|
|
return;
|
|
}
|
|
}
|
|
|
|
VariableOrConstant args[2];
|
|
args[0] = array;
|
|
args[1] = object;
|
|
// asharma - fix me
|
|
Primitive &prim = makeSysCall(scCheckArrayStore, 0, 2, args, 0, 0, *cn, 0);
|
|
genPossibleThrow(prim, Standard::get(cArrayStoreException));
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Create control and data flow nodes for an array read or write bytecode.
|
|
// If write is true, the access is a write; otherwise it's a read.
|
|
//
|
|
// Set cn to a control node to use for the next portion of this BytecodeBlock or
|
|
// nil if the rest of this BytecodeBlock is dead.
|
|
//
|
|
void BytecodeTranslator::BlockTranslator::genArrayEltAccess(TypeKind tk, bool
|
|
write, Uint32 bci)
|
|
{
|
|
VariableOrConstant value;
|
|
VariableOrConstant index;
|
|
VariableOrConstant length; // Array length
|
|
VariableOrConstant arrayAddrAndOffset[2]; // [0] is array address; [1] is index times element size
|
|
TranslationEnv &env = *translationEnv;
|
|
|
|
if (write)
|
|
env.pop1or2(isDoublewordKind(tk)).extract(value, bci);
|
|
env.pop().extract(index, bci);
|
|
env.pop().extract(arrayAddrAndOffset[0], bci);
|
|
genReadArrayLength(arrayAddrAndOffset[0], length, bci);
|
|
|
|
if (cn) {
|
|
// Check bounds
|
|
Primitive *prim;
|
|
CheckResult chkRes = makeLimit(index, length, prim, cn);
|
|
if (chkRes == chkNeverOK)
|
|
genSimpleThrow(&Standard::get(oArrayIndexOutOfBoundsException), true);
|
|
else {
|
|
if (chkRes != chkAlwaysOK)
|
|
genPossibleThrow(*prim, Standard::get(cArrayIndexOutOfBoundsException));
|
|
|
|
// Check cast if needed
|
|
assert(isPrimitiveKind(tk) || tk == tkObject);
|
|
if (write && tk == tkObject)
|
|
genArrayCastGuard(arrayAddrAndOffset[0], value);
|
|
|
|
if (cn) {
|
|
// Get the address of the element.
|
|
VariableOrConstant elementAddr;
|
|
builder_Shl_III.specializeArgConst(index, getLgTypeKindSize(tk), arrayAddrAndOffset[1], cn);
|
|
builder_Add_AIA.makeBinaryGeneric(arrayAddrAndOffset, elementAddr, cn);
|
|
builder_AddCoal_AIA.specializeArgConst(elementAddr, arrayEltsOffset(tk), elementAddr, cn);
|
|
|
|
DataNode *memory = &env.memory().extract(bci);
|
|
if (write)
|
|
// Emit the store instruction.
|
|
env.memory().define(storeBuilders[tk]->makeStore(*memory, elementAddr, value, false, cn, bci));
|
|
else {
|
|
// Emit the load instruction.
|
|
loadBuilders[tk]->makeLoad(&memory, elementAddr, value, false, false, cn, bci);
|
|
env.push1or2(isDoublewordKind(tk)).define(value, cn, envPool);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Create data flow nodes to check if a reference with class pointed
|
|
// at by the type argument is assignable to another reference of type
|
|
// targetInterface, which is either an interface type or the type of
|
|
// an array of interfaces. type should be a variable, not a constant.
|
|
//
|
|
// results should be an array of two VariableOrConstants. On exit
|
|
// results[0] and results[1] will be initialized to two integer variables
|
|
// that, if equal, indicate that the interface check succeeds and,
|
|
// if not equal, indicate that the interface check fails.
|
|
//
|
|
void BytecodeTranslator::BlockTranslator::genCheckInterfaceAssignability(VariableOrConstant &type, const Type &targetInterface,
|
|
VariableOrConstant *results, Uint32 bci) const
|
|
{
|
|
assert(cn && type.isVariable());
|
|
|
|
// Get the address of the class-specific interface assignability table used
|
|
// to determine whether an instance of that class can be assigned to a
|
|
// given interface (or interface array) type.
|
|
VariableOrConstant vPointerToAssignableTableAddr;
|
|
builder_Add_AIA.specializeArgConst(type, offsetof(Type, interfaceAssignableTable), vPointerToAssignableTableAddr, cn);
|
|
VariableOrConstant vAssignableTableAddr;
|
|
builder_Ld_AA.makeLoad(0, vPointerToAssignableTableAddr, vAssignableTableAddr, false, true, cn, bci);
|
|
|
|
// Compute the address of the interface assignability table entry
|
|
// corresponding to the given interface.
|
|
Uint32 interfaceNumber = targetInterface.getInterfaceNumber();
|
|
VariableOrConstant vAssignableTableEntryAddr;
|
|
builder_Add_AIA.specializeArgConst(vAssignableTableAddr, interfaceNumber * sizeof(Uint16), vAssignableTableEntryAddr, cn);
|
|
|
|
// Load the selected table entry, which will contain a value that we must match against
|
|
VariableOrConstant &vAssignableValue = results[0];
|
|
VariableOrConstant &vMatchValue = results[1];
|
|
builder_LdU_AH.makeLoad(0, vAssignableTableEntryAddr, vAssignableValue, false, true, cn, bci);
|
|
|
|
// Load the class-specific match value for comparison with the value found in the table
|
|
VariableOrConstant vAddrOfAssignableMatchValue;
|
|
builder_Add_AIA.specializeVarConst(type.getVariable(), offsetof(Type, assignableMatchValue), vAddrOfAssignableMatchValue, cn);
|
|
builder_LdU_AH.makeLoad(0, vAddrOfAssignableMatchValue, vMatchValue, false, true, cn, bci);
|
|
}
|
|
|
|
|
|
//
|
|
// Create control and data flow nodes for a checkcast bytecode that checks
|
|
// that object arg is a member of type t. Type t is guaranteed not to be Object,
|
|
// and arg is guaranteed to be nonnull.
|
|
//
|
|
// Set cn to a new, empty node to use for the next portion of this BytecodeBlock or
|
|
// to nil if an exception is always thrown.
|
|
//
|
|
void BytecodeTranslator::BlockTranslator::genCheckCastNonNull(const Type &t, const VariableOrConstant &arg, Uint32 bci)
|
|
{
|
|
assert(!isPrimitiveKind(t.typeKind));
|
|
|
|
VariableOrConstant type;
|
|
genReadObjectType(arg, type, bci);
|
|
|
|
if (type.isConstant()) {
|
|
// We determined the type of arg at compile time. Do nothing if it a subtype of t;
|
|
// otherwise, always throw ClassCastException.
|
|
if (!implements(addressAsType(type.getConstant().a), t))
|
|
genSimpleThrow(&Standard::get(oClassCastException), true);
|
|
} else {
|
|
DataNode &dp = type.getVariable();
|
|
Primitive *prim;
|
|
|
|
if (t.final)
|
|
// We're testing against a final type. Emit a simple equality test
|
|
// that ensures that arg is the same as the given final type.
|
|
prim = makeChkCastA(dp, t, cn);
|
|
else if ((t.typeKind == tkObject) ||
|
|
((t.typeKind == tkArray) && (asArray(t).getElementType().typeKind != tkInterface))) {
|
|
|
|
// We're testing against an object or an array type that is not an array of
|
|
// interfaces. Emit a vtable-based test that passes the given type and its subtypes.
|
|
// The given type and each of its subtypes will have a pointer to type t
|
|
// in the ventry at index nVTableSlots-1.
|
|
|
|
// Make sure that we have a sufficient number of vEntries.
|
|
VariableOrConstant nVEntriesAddr;
|
|
builder_Add_AIA.specializeVarConst(dp, offsetof(Type, nVTableSlots), nVEntriesAddr, cn);
|
|
VariableOrConstant nVEntries;
|
|
builder_Ld_AI.makeLoad(0, nVEntriesAddr, nVEntries, false, true, cn, bci);
|
|
Primitive *limPrim;
|
|
CheckResult chkRes = makeLimCast(nVEntries, t.nVTableSlots, limPrim, cn);
|
|
if (chkRes == chkNeverOK) {
|
|
genSimpleThrow(&Standard::get(oClassCastException), true);
|
|
return;
|
|
}
|
|
if (chkRes != chkAlwaysOK)
|
|
genPossibleThrow(*limPrim, Standard::get(cClassCastException));
|
|
|
|
// Get the value of the vEntry at index nVTableSlots-1.
|
|
VariableOrConstant vEntryAddr;
|
|
builder_Add_AIA.specializeVarConst(dp, vTableIndexToOffset(t.nVTableSlots - 1), vEntryAddr, cn);
|
|
VariableOrConstant vEntryValue;
|
|
builder_Ld_AA.makeLoad(0, vEntryAddr, vEntryValue, false, true, cn, bci);
|
|
|
|
// Emit a test to make sure that the vEntry value is the type we desire.
|
|
prim = makeChkCastA(vEntryValue.getVariable(), t, cn);
|
|
} else {
|
|
// We're testing against an interface or an array whose element type is an interface.
|
|
assert((t.typeKind == tkInterface) || ((t.typeKind == tkArray) && (asArray(t).getElementType().typeKind == tkInterface)));
|
|
|
|
// Generate a runtime assignability check
|
|
VariableOrConstant cmpArgs[2];
|
|
genCheckInterfaceAssignability(type, t, cmpArgs, bci);
|
|
prim = makeChkCastI(cmpArgs[0].getVariable(), cmpArgs[1].getVariable(), cn);
|
|
}
|
|
genPossibleThrow(*prim, Standard::get(cClassCastException));
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Create control and data flow nodes for a checkcast bytecode that checks
|
|
// that object arg satisfies the type represented by cpi.
|
|
//
|
|
// Set cn to a control node to use for the next portion of this BytecodeBlock or
|
|
// nil if the checkcast always throws an exception.
|
|
//
|
|
void BytecodeTranslator::BlockTranslator::genCheckCast(ConstantPoolIndex cpi, const VariableOrConstant &arg, Uint32 bci)
|
|
{
|
|
const Type &t = classFileSummary.lookupType(cpi);
|
|
|
|
// A cast to the type Object always succeeds.
|
|
if (&t != &Standard::get(cObject)) {
|
|
// Try to statically check whether the object is null.
|
|
bool constResult;
|
|
VariableOrConstant cond;
|
|
builder_CmpUEq_AAC.specializeArgConst(arg, nullAddr, cond, cn);
|
|
DataNode *cp = partialComputeComparison(condEq, cond, constResult);
|
|
if (cp) {
|
|
// We have to check whether the object is null at runtime.
|
|
ControlNode *cnIf = cn;
|
|
cnIf->setControlIf(condEq, *cp, bci);
|
|
|
|
// Create a new ControlNode to hold the check code.
|
|
appendControlNode(cnIf->getFalseSuccessor());
|
|
genCheckCastNonNull(t, arg, bci);
|
|
if (!cn)
|
|
cn = &getControlGraph().newControlNode();
|
|
|
|
// Attach the true arm of the if to the latest control node, which should
|
|
// be empty at this point.
|
|
assert(cn->empty());
|
|
cn->addPredecessor(cnIf->getTrueSuccessor());
|
|
} else
|
|
// The object is either always or never null. If it's always null, don't
|
|
// bother emitting any code.
|
|
if (!constResult)
|
|
// The object is never null.
|
|
genCheckCastNonNull(t, arg, bci);
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Create control and data flow nodes for an instanceof bytecode that checks
|
|
// whether object arg is a member of type t and returns the int result (1 if arg is
|
|
// a member of type t, 0 if not) in result. Type t is guaranteed not to be Object,
|
|
// and arg is guaranteed to be nonnull.
|
|
// cnFalse is either nil or a pointer to a control node that will store false in the
|
|
// result.
|
|
//
|
|
// Set cn to a control node to use for the next portion of this BytecodeBlock.
|
|
// The outgoing cn will never be nil.
|
|
//
|
|
void BytecodeTranslator::BlockTranslator::genInstanceOfNonNull(const Type &t, const VariableOrConstant &arg, VariableOrConstant &result, ControlNode *cnFalse, Uint32 bci)
|
|
{
|
|
assert(!isPrimitiveKind(t.typeKind));
|
|
|
|
VariableOrConstant type;
|
|
genReadObjectType(arg, type, bci);
|
|
|
|
if (type.isConstant())
|
|
// We determined the type of arg at compile time.
|
|
// Return the constant 0 or 1, as appropriate.
|
|
result.setConstant((Int32)implements(addressAsType(type.getConstant().a), t));
|
|
|
|
else {
|
|
DataNode &dp = type.getVariable();
|
|
VariableOrConstant args[2];
|
|
args[0].setVariable(dp);
|
|
args[1].setConstant(objectAddress(&t));
|
|
|
|
if (t.final)
|
|
// We're testing against a final type. Emit a simple equality test
|
|
// that checks whether arg is the same as the given final type.
|
|
builder_Eq_AAI.makeBinaryGeneric(args, result, cn);
|
|
|
|
else if ((t.typeKind == tkObject) ||
|
|
((t.typeKind == tkArray) && (asArray(t).getElementType().typeKind != tkInterface))) {
|
|
|
|
// We're testing against an object or an array type where the elements
|
|
// of the array are not interfaces.
|
|
// Emit a vtable-based test that passes the given type and its subtypes.
|
|
// The given type and each of its subtypes will have a pointer to type t
|
|
// in the ventry at index nVTableSlots-1.
|
|
|
|
// Make sure that we have a sufficient number of vEntries.
|
|
VariableOrConstant nVEntriesAddr;
|
|
builder_Add_AIA.specializeVarConst(dp, offsetof(Type, nVTableSlots), nVEntriesAddr, cn);
|
|
VariableOrConstant nVEntries;
|
|
builder_Ld_AI.makeLoad(0, nVEntriesAddr, nVEntries, false, true, cn, bci);
|
|
|
|
// Emit the comparison and if node.
|
|
VariableOrConstant argCmp;
|
|
Primitive *reverseCmp = builder_Cmp_IIC.specializeArgConst(nVEntries, t.nVTableSlots, argCmp, cn);
|
|
Condition2 cond2 = reverseCmp ? condGt : condLt;
|
|
bool constResult;
|
|
DataNode *cp = partialComputeComparison(cond2, argCmp, constResult);
|
|
bool needJoin = false;
|
|
if (!cp) {
|
|
// The result of the if is known at compile time.
|
|
// (This should not be possible, because we've already
|
|
// tested for a type known at compile-time, above, but
|
|
// this code might be purposeful in the future.)
|
|
if (constResult) {
|
|
// The if is always true, so the result is always false.
|
|
result.setConstant((Int32)0);
|
|
return;
|
|
}
|
|
// The if is always false, so don't emit the if.
|
|
} else {
|
|
ControlNode *cnIf = cn;
|
|
cnIf->setControlIf(cond2, *cp, bci);
|
|
|
|
// Create a new control node for the false branch of the if and store it in cn.
|
|
appendControlNode(cnIf->getFalseSuccessor());
|
|
if (!cnFalse) {
|
|
cnFalse = &getControlGraph().newControlNode();
|
|
needJoin = true;
|
|
}
|
|
cnFalse->addPredecessor(cnIf->getTrueSuccessor());
|
|
}
|
|
|
|
// Get the value of the vEntry at index nVTableSlots-1.
|
|
VariableOrConstant vEntryAddr;
|
|
builder_Add_AIA.specializeVarConst(dp, vTableIndexToOffset(t.nVTableSlots - 1), vEntryAddr, cn);
|
|
builder_Ld_AA.makeLoad(0, vEntryAddr, args[0], false, true, cn, bci);
|
|
|
|
// Emit a test to make sure that the vEntry value is the type we desire.
|
|
builder_Eq_AAI.makeBinaryGeneric(args, result, cn);
|
|
|
|
// If we created our own control node for the true branch of the if, we must now join it
|
|
// with the control flow leaving the false branch.
|
|
if (needJoin) {
|
|
ControlNode *predecessors[2];
|
|
args[0] = result;
|
|
args[1].setConstant((Int32)0);
|
|
predecessors[0] = cn;
|
|
predecessors[1] = cnFalse;
|
|
cn = &joinControlFlows(2, predecessors, getControlGraph());
|
|
joinDataFlows(2, *cn, args, result);
|
|
}
|
|
|
|
} else {
|
|
// We're testing against an interface or an array whose element type is an interface.
|
|
assert((t.typeKind == tkInterface) || ((t.typeKind == tkArray) && (asArray(t).getElementType().typeKind == tkInterface)));
|
|
|
|
VariableOrConstant cmpArgs[2];
|
|
genCheckInterfaceAssignability(type, asInterface(t), cmpArgs, bci);
|
|
builder_Eq_III.makeBinaryGeneric(cmpArgs, result, cn);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Create control and data flow nodes for an instanceof bytecode that checks
|
|
// whether object arg is a member of the type represented by cpi. However,
|
|
// unlike checkcast, nil is not considered to be a member of any type.
|
|
// The result VariableOrConstant gets an int result that is 1 if arg is a member
|
|
// of the type or 0 if not.
|
|
//
|
|
// Set cn to a control node to use for the next portion of this BytecodeBlock.
|
|
// The outgoing cn will never be nil.
|
|
//
|
|
void BytecodeTranslator::BlockTranslator::genInstanceOf(ConstantPoolIndex cpi, const VariableOrConstant &arg, VariableOrConstant &result, Uint32 bci)
|
|
{
|
|
const Type &t = classFileSummary.lookupType(cpi);
|
|
|
|
if (&t == &Standard::get(cObject))
|
|
// "x instanceof Object" succeeds if and only if x is non-nil.
|
|
builder_Ne0_AI.makeUnaryGeneric(arg, result, cn);
|
|
else {
|
|
// Try to statically check whether the object is null.
|
|
bool constResult;
|
|
VariableOrConstant cond;
|
|
builder_CmpUEq_AAC.specializeArgConst(arg, nullAddr, cond, cn);
|
|
DataNode *cp = partialComputeComparison(condEq, cond, constResult);
|
|
if (cp) {
|
|
// We have to check whether the object is null at runtime.
|
|
ControlNode *cnIf = cn;
|
|
cnIf->setControlIf(condEq, *cp, bci);
|
|
|
|
// Create a new ControlNode to hold the instanceof code for the null case.
|
|
VariableOrConstant args[2];
|
|
ControlNode *predecessors[2];
|
|
appendControlNode(cnIf->getTrueSuccessor());
|
|
args[1].setConstant((Int32)0);
|
|
predecessors[1] = cn;
|
|
|
|
// Create a new ControlNode to hold the instanceof code for the non-null case.
|
|
appendControlNode(cnIf->getFalseSuccessor());
|
|
genInstanceOfNonNull(t, arg, args[0], predecessors[1], bci);
|
|
predecessors[0] = cn;
|
|
|
|
// Merge the outputs of the above two ControlNodes.
|
|
cn = &joinControlFlows(2, predecessors, getControlGraph());
|
|
joinDataFlows(2, *cn, args, result);
|
|
} else
|
|
// The object is either always or never null.
|
|
if (constResult)
|
|
// The object is always null.
|
|
result.setConstant((Int32)0);
|
|
else
|
|
// The object is never null.
|
|
genInstanceOfNonNull(t, arg, result, 0, bci);
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Create control and data flow nodes for a system call that receives
|
|
// a memory argument together with the given arguments and outputs a new
|
|
// memory state together with one one-word result and can throw any exception.
|
|
// Push the result onto the environment.
|
|
//
|
|
// Set cn to a control node to use for the next portion of this BytecodeBlock.
|
|
//
|
|
void BytecodeTranslator::BlockTranslator::genNewCommon(const SysCall &sysCall, uint nArgs, const VariableOrConstant *args, Uint32 bci)
|
|
{
|
|
VariableOrConstant result;
|
|
TranslationEnv &env = *translationEnv;
|
|
DataNode *memory = &env.memory().extract(bci);
|
|
Primitive &prim = makeSysCall(sysCall, &memory, nArgs, args, 1, &result, *cn, bci);
|
|
env.memory().define(*memory);
|
|
env.push().define(result.getVariable()); // OK because result cannot be a constant here.
|
|
genPossibleThrow(prim, Standard::get(sysCall.exceptionClass));
|
|
}
|
|
|
|
|
|
//
|
|
// Create code to allocate a temporary array for storing all the dimensions
|
|
// of a multi-dimensional array, which is pushed onto the environment.
|
|
//
|
|
// Set cn to a control node to use for the next portion of this BytecodeBlock or
|
|
// nil if the rest of this BytecodeBlock is dead.
|
|
//
|
|
void BytecodeTranslator::BlockTranslator::genTempArrayOfDim(Int32 dim, Uint32 bci)
|
|
{
|
|
VariableOrConstant args[2], arg, *dimargs;
|
|
Int32 i;
|
|
TranslationEnv &env = *translationEnv;
|
|
|
|
// Pull all the dimensions out of the environment.
|
|
dimargs = new(primitivePool) VariableOrConstant[dim];
|
|
for (i=dim-1; i>=0; i--) {
|
|
env.pop().extract(dimargs[i], bci);
|
|
// check for negative dimension here
|
|
assert(dimargs[i].hasKind(vkInt));
|
|
if (dimargs[i].isConstant() && dimargs[i].getConstant().i < 0) {
|
|
genSimpleThrow(&Standard::get(oNegativeArraySizeException), true);
|
|
return;
|
|
}
|
|
}
|
|
args[0].setConstant(dim); // the number of ints to allocate
|
|
genNewCommon(*typeToArrayCalls[tkInt], 1, args, bci);
|
|
env.pop().extract(args[0], bci); // array now stored in args[0]
|
|
// Now loop through dimensions and generate code to fill the array.
|
|
// We need to do the following:
|
|
// for each argument (picked from the environment in reverse order),
|
|
// store it in the array using the code for iastore
|
|
// (genArrayEltAccess(tkInt, 1)).
|
|
// We proceed by pushing the array, followed by i, and dimargs[i].
|
|
// Then, generate a call to genArrayEltAccess().
|
|
// Return the resulting control node.
|
|
for (i=0; i<dim; i++) {
|
|
env.push().define(args[0], cn, envPool); // arr. WASTE!
|
|
arg.setConstant(i);
|
|
env.push().define(arg, cn, envPool); // i.
|
|
env.push().define(dimargs[i], cn, envPool); // ith dimension
|
|
genArrayEltAccess(tkInt, 1, bci);
|
|
}
|
|
env.push().define(args[0], cn, envPool);
|
|
}
|
|
|
|
|
|
//
|
|
// Create control and data flow nodes for a monitor call that receives a memory
|
|
// argument together with the given argument (the pointer to the object being
|
|
// acquired or released) and outputs a new memory state. Set the primitive's slot
|
|
// to unknown for now.
|
|
//
|
|
Primitive &BytecodeTranslator::BlockTranslator::genMonitor(PrimitiveOperation op, const VariableOrConstant &arg, Uint32 bci) const
|
|
{
|
|
assert(cn);
|
|
TranslationEnv &env = *translationEnv;
|
|
Primitive &prim = makeMonitorPrimitive(op, env.memory().extract(bci), arg, PrimMonitor::unknownSlot, cn, bci);
|
|
env.memory().define(prim);
|
|
return prim;
|
|
}
|
|
|
|
|
|
//
|
|
// Generate an unconditional throw of the given exception. If the exception is null,
|
|
// throw NullPointerException instead. Set cn to nil to indicate that no more control
|
|
// nodes should be generated.
|
|
//
|
|
// This routine assumes ownership of translationEnv.
|
|
//
|
|
void BytecodeTranslator::BlockTranslator::genThrow(const VariableOrConstant &exception)
|
|
{
|
|
// asharma - fix me
|
|
genNullGuard(exception, 0);
|
|
if (cn)
|
|
// We don't know the class of the exception object here, so conservatively assume Throwable.
|
|
// A more detailed dataflow analysis might narrow down the possibilities.
|
|
genThrowCommon(exception, Standard::get(cThrowable), true);
|
|
}
|
|
|
|
|
|
//
|
|
// Generate a null check for the receiver of a virtual or interface call.
|
|
// Return the receiver.
|
|
//
|
|
// Set cn to a control node to use for the next portion of this BytecodeBlock or
|
|
// nil if the rest of this BytecodeBlock is dead because the receiver is always null.
|
|
//
|
|
void BytecodeTranslator::BlockTranslator::genReceiverNullCheck(const Signature &sig, VariableOrConstant &receiver, Uint32 bci)
|
|
{
|
|
translationEnv->stackNth(sig.nArgumentSlots()).extract(receiver, bci);
|
|
genNullGuard(receiver, bci);
|
|
}
|
|
|
|
|
|
//
|
|
// Generate code to perform a virtual method lookup. The receiver object
|
|
// is the "this" object and is guaranteed to be nonnull. vIndex is the index
|
|
// (not offset!) of the virtual table entry.
|
|
// Store the looked-up function address in functionAddr.
|
|
//
|
|
void BytecodeTranslator::BlockTranslator::genVirtualLookup(const VariableOrConstant &receiver, Uint32 vIndex,
|
|
VariableOrConstant &functionAddr, Uint32 bci) const
|
|
{
|
|
VariableOrConstant type;
|
|
genReadObjectType(receiver, type, bci);
|
|
|
|
// Get the address of the vEntry.
|
|
VariableOrConstant vEntryAddr;
|
|
builder_Add_AIA.specializeArgConst(type, vTableIndexToOffset(vIndex), vEntryAddr, cn);
|
|
|
|
// Emit the load instruction.
|
|
builder_Ld_AA.makeLoad(0, vEntryAddr, functionAddr, false, true, cn, bci);
|
|
}
|
|
|
|
|
|
//
|
|
// Generate code to perform an interface method lookup. The receiver object
|
|
// is the "this" object and is guaranteed to be nonnull. vIndex is the index
|
|
// (not offset!) of the virtual table entry.
|
|
// Store the looked-up function address in functionAddr.
|
|
//
|
|
void BytecodeTranslator::BlockTranslator::genInterfaceLookup(const VariableOrConstant &receiver,
|
|
Uint32 interfaceNumber,
|
|
Uint32 vIndex,
|
|
const Method *interfaceMethod,
|
|
VariableOrConstant &functionAddr,
|
|
Uint32 bci) const
|
|
{
|
|
VariableOrConstant type;
|
|
genReadObjectType(receiver, type, bci);
|
|
|
|
// Check for a receiver object of type known at compile-time.
|
|
// In this case, we can statically determine the method within the
|
|
// receiver's type that implements the interface method.
|
|
if (type.isConstant()) {
|
|
Type &receiverType = addressAsType(type.getConstant().a);
|
|
ClassFileSummary *receiverSummary = NULL;
|
|
|
|
if (receiverType.typeKind == tkObject) {
|
|
receiverSummary = asClass(receiverType).summary;
|
|
} else {
|
|
assert(receiverType.typeKind == tkArray);
|
|
/* Arrays never have interface methods */
|
|
verifyError(VerifyError::noSuchMethod);
|
|
}
|
|
|
|
// Get the class method that implements the given interface method
|
|
addr a;
|
|
Uint32 vIndex;
|
|
const Method *method;
|
|
method = receiverSummary->lookupImplementationOfInterfaceMethod(interfaceMethod, vIndex, a);
|
|
if (!method)
|
|
verifyError(VerifyError::noSuchMethod);
|
|
|
|
// Generate normal virtual function dispatch or set function address if it's
|
|
// known at compile-time.
|
|
if (method)
|
|
functionAddr.setConstant(a);
|
|
else
|
|
genVirtualLookup(receiver, vIndex, functionAddr, bci);
|
|
|
|
} else {
|
|
|
|
VariableOrConstant vVars[2];
|
|
VariableOrConstant &vSubVTableOffset = vVars[1];
|
|
VariableOrConstant &vEntryOffset = vVars[0];
|
|
|
|
// Get the address of the class-specific interface table used to locate
|
|
// the sub-vtable dedicated to the given interface within the class' vtable
|
|
VariableOrConstant vPointerToInterfaceTableAddr;
|
|
builder_Add_AIA.specializeArgConst(type, offsetof(Type, interfaceVIndexTable),
|
|
vPointerToInterfaceTableAddr, cn);
|
|
VariableOrConstant vInterfaceTableAddr;
|
|
builder_Ld_AA.makeLoad(0, vPointerToInterfaceTableAddr, vInterfaceTableAddr, false, true, cn, bci);
|
|
|
|
// Compute the address of the interface table entry corresponding to the
|
|
// given interface.
|
|
VariableOrConstant vInterfaceTableEntryAddr;
|
|
assert(sizeof(VTableOffset) == 4);
|
|
builder_Add_AIA.specializeArgConst(vInterfaceTableAddr, interfaceNumber * sizeof(VTableOffset),
|
|
vInterfaceTableEntryAddr, cn);
|
|
|
|
// Compute the offset into the class's VTable which corresponds to the base
|
|
// of the sub-vtable for this interface
|
|
builder_Ld_AI.makeLoad(0, vInterfaceTableEntryAddr, vSubVTableOffset, false, true, cn, bci);
|
|
|
|
// Get the offset of the vEntry within the interface's sub-vtable.
|
|
builder_Add_AIA.specializeArgConst(type, vIndex * sizeof(VTableEntry), vEntryOffset, cn);
|
|
|
|
// Compute the address of the vtable entry
|
|
VariableOrConstant vVTableEntryAddr;
|
|
builder_Add_AIA.makeBinaryGeneric(vVars, vVTableEntryAddr, cn);
|
|
|
|
// Emit the load instruction to get the function's address from the vtable entry.
|
|
builder_Ld_AA.makeLoad(0, vVTableEntryAddr, functionAddr, false, true, cn, bci);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Generate a function call. opcode should be one of the four opcodes
|
|
// invokevirtual, invokespecial, invokestatic, or invokeinterface.
|
|
// cpi is the constant pool index of the method.
|
|
// If opcode is bcInvokeInterface, nArgs is the number of arguments.
|
|
//
|
|
// Set cn to a control node to use for the next portion of this BytecodeBlock or
|
|
// nil if the rest of this BytecodeBlock is dead.
|
|
//
|
|
void BytecodeTranslator::BlockTranslator::genInvoke(bytecode opcode, ConstantPoolIndex cpi, uint nArgs, Uint32 bci)
|
|
{
|
|
Signature sig;
|
|
VariableOrConstant receiver;
|
|
VariableOrConstant function;
|
|
const Method *method;
|
|
TranslationEnv &env = *translationEnv;
|
|
|
|
function.setConstant(nullAddr);
|
|
switch (opcode) {
|
|
case bcInvokeVirtual:
|
|
{
|
|
Uint32 vIndex;
|
|
method = classFileSummary.lookupVirtualMethod(cpi, sig, vIndex, function.getConstant().a);
|
|
genReceiverNullCheck(sig, receiver, bci);
|
|
if (!cn)
|
|
return;
|
|
if (!method)
|
|
genVirtualLookup(receiver, vIndex, function, bci);
|
|
}
|
|
break;
|
|
case bcInvokeSpecial:
|
|
{
|
|
bool isInit;
|
|
method = &classFileSummary.lookupSpecialMethod(cpi, sig, isInit, function.getConstant().a);
|
|
genReceiverNullCheck(sig, receiver, bci);
|
|
if (!cn)
|
|
return;
|
|
}
|
|
break;
|
|
case bcInvokeStatic:
|
|
method = &classFileSummary.lookupStaticMethod(cpi, sig, function.getConstant().a);
|
|
break;
|
|
case bcInvokeInterface:
|
|
{
|
|
Uint32 vIndex, interfaceNumber;
|
|
method = classFileSummary.lookupInterfaceMethod(cpi, sig, vIndex, interfaceNumber,
|
|
function.getConstant().a, nArgs);
|
|
genReceiverNullCheck(sig, receiver, bci);
|
|
if (!cn)
|
|
return;
|
|
assert(!method);
|
|
genInterfaceLookup(receiver, interfaceNumber, vIndex, method, function, bci);
|
|
}
|
|
break;
|
|
default:
|
|
trespass("Bad invoke opcode");
|
|
return;
|
|
}
|
|
|
|
VariableOrConstant result;
|
|
VariableOrConstant args20[20]; // Hold arguments here if there are fewer than 20.
|
|
VariableOrConstant *args = args20;
|
|
uint nArguments = sig.nArguments;
|
|
if (nArguments > 20)
|
|
args = new(envPool) VariableOrConstant[nArguments];
|
|
|
|
// Pop the arguments off the environment stack.
|
|
const Type **argumentType = sig.argumentTypes + nArguments;
|
|
VariableOrConstant *arg = args + nArguments;
|
|
while (arg != args)
|
|
env.pop1or2(isDoublewordKind((*--argumentType)->typeKind)).extract(*--arg, bci);
|
|
|
|
// Create the call primitive.
|
|
DataNode *memory = &env.memory().extract(bci);
|
|
Primitive &prim = makeCall(sig, memory, function, method, args, &result, *cn, bci);
|
|
env.memory().define(*memory);
|
|
|
|
// Push the result onto the environment stack.
|
|
if (sig.getNResults())
|
|
env.push1or2(isDoublewordKind(result.getKind())).define(result, cn, envPool);
|
|
genPossibleThrow(prim, Standard::get(cThrowable));
|
|
}
|
|
|
|
|
|
//
|
|
// Create control and data flow nodes for the primitives inside this BytecodeBlock.
|
|
// Use and modify translationEnv to keep track of the locals' and stack temporaries' values.
|
|
//
|
|
// Set cn to nil when done.
|
|
//
|
|
void BytecodeTranslator::BlockTranslator::genLivePrimitives()
|
|
{
|
|
BytecodeGraph &bg = bt.bytecodeGraph;
|
|
TranslationEnv &env = *translationEnv;
|
|
const AttributeCode *code = (AttributeCode *)
|
|
bg.method.getMethodInfo().getAttribute("Code");
|
|
AttributeLocalVariableTable *localVariableTable =
|
|
(AttributeLocalVariableTable *) code->getAttribute("LocalVariableTable");
|
|
const bytecode *const bytecodesEnd = block.bytecodesEnd;
|
|
const bytecode *bc = block.bytecodesBegin;
|
|
|
|
while (bc != bytecodesEnd) {
|
|
assert(bc < bytecodesEnd);
|
|
bytecode opcode = *bc++;
|
|
ValueKind vk;
|
|
Value v;
|
|
Uint32 i;
|
|
ConstantPoolIndex cpi;
|
|
Int32 j;
|
|
TranslationBinding *tb;
|
|
VariableOrConstant args[4];
|
|
VariableOrConstant result;
|
|
Primitive *prim;
|
|
const Type *cl;
|
|
IntervalMapping *mapping;
|
|
Uint32 bci = bc - bg.bytecodesBegin;
|
|
|
|
switch (opcode) {
|
|
|
|
case bcNop:
|
|
break;
|
|
|
|
case bcAConst_Null:
|
|
env.push().definePtr(nullAddr, cn, envPool);
|
|
break;
|
|
case bcIConst_m1:
|
|
case bcIConst_0:
|
|
case bcIConst_1:
|
|
case bcIConst_2:
|
|
case bcIConst_3:
|
|
case bcIConst_4:
|
|
case bcIConst_5:
|
|
env.push().defineInt(opcode - bcIConst_0, cn, envPool);
|
|
break;
|
|
case bcLConst_0:
|
|
env.push2().defineLong(0, cn, envPool);
|
|
break;
|
|
case bcLConst_1:
|
|
env.push2().defineLong(1, cn, envPool);
|
|
break;
|
|
case bcFConst_0:
|
|
env.push().defineFloat(0.0, cn, envPool);
|
|
break;
|
|
case bcFConst_1:
|
|
env.push().defineFloat(1.0, cn, envPool);
|
|
break;
|
|
case bcFConst_2:
|
|
env.push().defineFloat(2.0, cn, envPool);
|
|
break;
|
|
case bcDConst_0:
|
|
env.push2().defineDouble(0.0, cn, envPool);
|
|
break;
|
|
case bcDConst_1:
|
|
env.push2().defineDouble(1.0, cn, envPool);
|
|
break;
|
|
case bcBIPush:
|
|
env.push().defineInt(*(Int8 *)bc, cn, envPool);
|
|
bc++;
|
|
break;
|
|
case bcSIPush:
|
|
env.push().defineInt(readBigSHalfwordUnaligned(bc), cn, envPool);
|
|
bc += 2;
|
|
break;
|
|
case bcLdc:
|
|
cpi = *(Uint8 *)bc;
|
|
bc++;
|
|
goto pushConst1;
|
|
case bcLdc_W:
|
|
cpi = readBigUHalfwordUnaligned(bc);
|
|
bc += 2;
|
|
pushConst1:
|
|
vk = classFileSummary.lookupConstant(cpi, v);
|
|
assert(isWordKind(vk));
|
|
env.push().define(vk, v, cn, envPool);
|
|
break;
|
|
case bcLdc2_W:
|
|
vk = classFileSummary.lookupConstant(readBigUHalfwordUnaligned(bc), v);
|
|
bc += 2;
|
|
assert(isDoublewordKind(vk));
|
|
env.push2().define(vk, v, cn, envPool);
|
|
break;
|
|
|
|
case bcILoad:
|
|
case bcFLoad:
|
|
case bcALoad:
|
|
i = *(Uint8 *)bc;
|
|
bc++;
|
|
pushLoad1:
|
|
env.push() = env.local(i);
|
|
break;
|
|
case bcLLoad:
|
|
case bcDLoad:
|
|
i = *(Uint8 *)bc;
|
|
bc++;
|
|
pushLoad2:
|
|
assert(env.local(i+1).isSecondWord());
|
|
env.push2() = env.local(i);
|
|
break;
|
|
case bcILoad_0:
|
|
case bcILoad_1:
|
|
case bcILoad_2:
|
|
case bcILoad_3:
|
|
i = opcode - bcILoad_0;
|
|
goto pushLoad1;
|
|
case bcLLoad_0:
|
|
case bcLLoad_1:
|
|
case bcLLoad_2:
|
|
case bcLLoad_3:
|
|
i = opcode - bcLLoad_0;
|
|
goto pushLoad2;
|
|
case bcFLoad_0:
|
|
case bcFLoad_1:
|
|
case bcFLoad_2:
|
|
case bcFLoad_3:
|
|
i = opcode - bcFLoad_0;
|
|
goto pushLoad1;
|
|
case bcDLoad_0:
|
|
case bcDLoad_1:
|
|
case bcDLoad_2:
|
|
case bcDLoad_3:
|
|
i = opcode - bcDLoad_0;
|
|
goto pushLoad2;
|
|
case bcALoad_0:
|
|
case bcALoad_1:
|
|
case bcALoad_2:
|
|
case bcALoad_3:
|
|
i = opcode - bcALoad_0;
|
|
goto pushLoad1;
|
|
|
|
case bcIALoad:
|
|
case bcLALoad:
|
|
case bcFALoad:
|
|
case bcDALoad:
|
|
case bcAALoad:
|
|
case bcBALoad:
|
|
case bcCALoad:
|
|
case bcSALoad:
|
|
genArrayEltAccess(arrayAccessTypeKinds[opcode - bcIALoad], false, bci);
|
|
goto checkDeadRestOfBlock;
|
|
|
|
case bcIStore:
|
|
case bcFStore:
|
|
case bcAStore:
|
|
i = *(Uint8 *)bc;
|
|
bc++;
|
|
popStore1:
|
|
// asharma - label this one
|
|
if (localVariableTable) {
|
|
DoublyLinkedList<IntervalMapping> &mappings =
|
|
localVariableTable->getEntry(i)->mappings;
|
|
IntervalMapping *prev;
|
|
|
|
mapping = new IntervalMapping(); // Fix me!
|
|
mapping->setStart((Uint32) (bc - bg.bytecodesBegin));
|
|
if (!mappings.empty()) {
|
|
prev = &mappings.get(mappings.end());
|
|
prev->setEnd((Uint32) (bc - bg.bytecodesBegin));
|
|
}
|
|
mappings.addLast(*mapping);
|
|
tb = (TranslationBinding *) &env.stackNth(1);
|
|
if (tb->isDataEdge()) {
|
|
tb->extract(bci).mapping = mapping;
|
|
}
|
|
}
|
|
env.local(i) = env.pop();
|
|
break;
|
|
case bcLStore:
|
|
case bcDStore:
|
|
i = *(Uint8 *)bc;
|
|
bc++;
|
|
popStore2:
|
|
// asharma label this one
|
|
env.local(i) = env.pop2();
|
|
env.local(i+1).defineSecondWord();
|
|
break;
|
|
case bcIStore_0:
|
|
case bcIStore_1:
|
|
case bcIStore_2:
|
|
case bcIStore_3:
|
|
i = opcode - bcIStore_0;
|
|
goto popStore1;
|
|
case bcLStore_0:
|
|
case bcLStore_1:
|
|
case bcLStore_2:
|
|
case bcLStore_3:
|
|
i = opcode - bcLStore_0;
|
|
goto popStore2;
|
|
case bcFStore_0:
|
|
case bcFStore_1:
|
|
case bcFStore_2:
|
|
case bcFStore_3:
|
|
i = opcode - bcFStore_0;
|
|
goto popStore1;
|
|
case bcDStore_0:
|
|
case bcDStore_1:
|
|
case bcDStore_2:
|
|
case bcDStore_3:
|
|
i = opcode - bcDStore_0;
|
|
goto popStore2;
|
|
case bcAStore_0:
|
|
case bcAStore_1:
|
|
case bcAStore_2:
|
|
case bcAStore_3:
|
|
i = opcode - bcAStore_0;
|
|
goto popStore1;
|
|
|
|
case bcIAStore:
|
|
case bcLAStore:
|
|
case bcFAStore:
|
|
case bcDAStore:
|
|
case bcAAStore:
|
|
case bcBAStore:
|
|
case bcCAStore:
|
|
case bcSAStore:
|
|
genArrayEltAccess(arrayAccessTypeKinds[opcode - bcIAStore], true, bci);
|
|
goto checkDeadRestOfBlock;
|
|
|
|
case bcPop:
|
|
env.drop(1);
|
|
break;
|
|
case bcPop2:
|
|
env.drop(2);
|
|
break;
|
|
case bcDup:
|
|
tb = &env.stackNth(1);
|
|
env.push() = *tb;
|
|
break;
|
|
case bcDup_x1:
|
|
env.raise(1);
|
|
tb = env.stackTopN(3);
|
|
tb[-1] = tb[-2];
|
|
tb[-2] = tb[-3];
|
|
tb[-3] = tb[-1];
|
|
break;
|
|
case bcDup_x2:
|
|
env.raise(1);
|
|
tb = env.stackTopN(4);
|
|
tb[-1] = tb[-2];
|
|
tb[-2] = tb[-3];
|
|
tb[-3] = tb[-4];
|
|
tb[-4] = tb[-1];
|
|
break;
|
|
case bcDup2:
|
|
env.raise(2);
|
|
tb = env.stackTopN(4);
|
|
tb[-1] = tb[-3];
|
|
tb[-2] = tb[-4];
|
|
break;
|
|
case bcDup2_x1:
|
|
env.raise(2);
|
|
tb = env.stackTopN(5);
|
|
tb[-1] = tb[-3];
|
|
tb[-2] = tb[-4];
|
|
tb[-3] = tb[-5];
|
|
tb[-4] = tb[-1];
|
|
tb[-5] = tb[-2];
|
|
break;
|
|
case bcDup2_x2:
|
|
env.raise(2);
|
|
tb = env.stackTopN(6);
|
|
tb[-1] = tb[-3];
|
|
tb[-2] = tb[-4];
|
|
tb[-3] = tb[-5];
|
|
tb[-4] = tb[-6];
|
|
tb[-5] = tb[-1];
|
|
tb[-6] = tb[-2];
|
|
break;
|
|
case bcSwap:
|
|
{
|
|
TranslationBinding tempBinding;
|
|
tb = env.stackTopN(2);
|
|
tempBinding = tb[-1];
|
|
tb[-1] = tb[-2];
|
|
tb[-2] = tempBinding;
|
|
}
|
|
break;
|
|
|
|
case bcIAdd:
|
|
case bcFAdd:
|
|
case bcISub:
|
|
case bcFSub:
|
|
case bcIMul:
|
|
case bcFMul:
|
|
case bcFDiv:
|
|
case bcFRem:
|
|
case bcIShl:
|
|
case bcIShr:
|
|
case bcIUShr:
|
|
case bcIAnd:
|
|
case bcIOr:
|
|
case bcIXor:
|
|
case bcFCmpL:
|
|
case bcFCmpG:
|
|
env.pop().extract(args[1], bci);
|
|
tb = env.stackTopN(1) - 1;
|
|
genGenericBinary:
|
|
tb->extract(args[0], bci);
|
|
genExtractedBinary:
|
|
binaryBuilders[opcode - bcIAdd]->makeBinaryGeneric(args, result, cn);
|
|
tb->define(result, cn, envPool);
|
|
break;
|
|
case bcLAdd:
|
|
case bcDAdd:
|
|
case bcLSub:
|
|
case bcDSub:
|
|
case bcLMul:
|
|
case bcDMul:
|
|
case bcDDiv:
|
|
case bcDRem:
|
|
case bcLAnd:
|
|
case bcLOr:
|
|
case bcLXor:
|
|
env.pop2().extract(args[1], bci);
|
|
genLongShift:
|
|
tb = env.stackTopN(2) - 2;
|
|
assert(tb[1].isSecondWord());
|
|
goto genGenericBinary;
|
|
case bcLShl:
|
|
case bcLShr:
|
|
case bcLUShr:
|
|
env.pop().extract(args[1], bci);
|
|
goto genLongShift;
|
|
case bcLCmp:
|
|
case bcDCmpL:
|
|
case bcDCmpG:
|
|
env.pop2().extract(args[1], bci);
|
|
env.pop2().extract(args[0], bci);
|
|
tb = &env.push();
|
|
goto genExtractedBinary;
|
|
|
|
case bcIDiv:
|
|
case bcIRem:
|
|
env.pop().extract(args[1], bci);
|
|
tb = env.stackTopN(1) - 1;
|
|
assert(args[1].hasKind(vkInt));
|
|
if (args[1].isConstant() && args[1].getConstant().i == 0) {
|
|
// If the second argument is always zero, this operation becomes an unconditional throw.
|
|
genSimpleThrow(&Standard::get(oArithmeticException), true);
|
|
// The rest of this BytecodeBlock is dead.
|
|
return;
|
|
}
|
|
genIntegerDivRem:
|
|
tb->extract(args[0], bci);
|
|
prim = binaryBuilders[opcode - bcIAdd]->makeBinaryGeneric(args, result, cn);
|
|
tb->define(result, cn, envPool);
|
|
if (prim && prim->canRaiseException())
|
|
genPossibleThrow(*prim, Standard::get(cArithmeticException));
|
|
break;
|
|
case bcLDiv:
|
|
case bcLRem:
|
|
env.pop2().extract(args[1], bci);
|
|
tb = env.stackTopN(2) - 2;
|
|
assert(tb[1].isSecondWord());
|
|
assert(args[1].hasKind(vkLong));
|
|
if (args[1].isConstant() && args[1].getConstant().l == 0) {
|
|
// If the second argument is always zero, this operation becomes an unconditional throw.
|
|
genSimpleThrow(&Standard::get(oArithmeticException), true);
|
|
// The rest of this BytecodeBlock is dead.
|
|
return;
|
|
}
|
|
goto genIntegerDivRem;
|
|
|
|
case bcINeg:
|
|
case bcFNeg:
|
|
case bcI2F:
|
|
case bcF2I:
|
|
case bcInt2Byte:
|
|
case bcInt2Char:
|
|
case bcInt2Short:
|
|
tb = env.stackTopN(1) - 1;
|
|
genGenericUnary:
|
|
tb->extract(args[0], bci);
|
|
genExtractedUnary:
|
|
unaryBuilders[opcode - bcINeg]->makeUnaryGeneric(args[0], result, cn);
|
|
tb->define(result, cn, envPool);
|
|
break;
|
|
case bcLNeg:
|
|
case bcDNeg:
|
|
case bcL2D:
|
|
case bcD2L:
|
|
tb = env.stackTopN(2) - 2;
|
|
assert(tb[1].isSecondWord());
|
|
goto genGenericUnary;
|
|
case bcI2L:
|
|
case bcI2D:
|
|
case bcF2L:
|
|
case bcF2D:
|
|
env.pop().extract(args[0], bci);
|
|
tb = &env.push2();
|
|
goto genExtractedUnary;
|
|
case bcL2I:
|
|
case bcL2F:
|
|
case bcD2I:
|
|
case bcD2F:
|
|
env.pop2().extract(args[0], bci);
|
|
tb = &env.push();
|
|
goto genExtractedUnary;
|
|
|
|
case bcIInc:
|
|
i = *(Uint8 *)bc;
|
|
bc++;
|
|
j = *(Int8 *)bc;
|
|
bc++;
|
|
incLocal:
|
|
// asharma label this one
|
|
env.local(i).extract(args[0], bci);
|
|
builder_AddCoal_III.specializeArgConst(args[0], j, result, cn);
|
|
env.local(i).define(result, cn, envPool);
|
|
break;
|
|
|
|
case bcIfEq:
|
|
case bcIfNe:
|
|
case bcIfLt:
|
|
case bcIfGe:
|
|
case bcIfGt:
|
|
case bcIfLe:
|
|
env.pop().extract(args[0], bci);
|
|
prim = unaryComparisonBuilders[opcode - bcIfEq]->specializeArgConst(args[0], 0, result, cn);
|
|
genIf:
|
|
// We should have exactly two successors.
|
|
createIf(bytecodeConditionals[opcode - bcIfEq], prim != 0, result, block.getSuccessor(0), block.getSuccessor(1), bci);
|
|
// We should be at the end of the basic block.
|
|
assert(bc + 2 == bytecodesEnd);
|
|
return;
|
|
case bcIf_ICmpEq:
|
|
case bcIf_ICmpNe:
|
|
case bcIf_ICmpLt:
|
|
case bcIf_ICmpGe:
|
|
case bcIf_ICmpGt:
|
|
case bcIf_ICmpLe:
|
|
case bcIf_ACmpEq:
|
|
case bcIf_ACmpNe:
|
|
env.pop().extract(args[1], bci);
|
|
env.pop().extract(args[0], bci);
|
|
prim = binaryComparisonBuilders[opcode - bcIf_ICmpEq]->makeBinaryGeneric(args, result, cn);
|
|
goto genIf;
|
|
|
|
case bcGoto_W:
|
|
bc += 2;
|
|
case bcGoto:
|
|
bc += 2;
|
|
// We should be at the end of the basic block.
|
|
assert(bc == bytecodesEnd);
|
|
break;
|
|
|
|
case bcJsr_W:
|
|
bc += 2;
|
|
case bcJsr:
|
|
bc += 2;
|
|
env.push().clear(); // Push the return address, which is not used since we eliminated all ret's already.
|
|
// We should be at the end of the basic block.
|
|
assert(bc == bytecodesEnd);
|
|
break;
|
|
|
|
case bcTableSwitch:
|
|
genTableSwitch(bc, bci);
|
|
return;
|
|
case bcLookupSwitch:
|
|
genLookupSwitch(bc, bci);
|
|
return;
|
|
|
|
case bcLReturn:
|
|
case bcDReturn:
|
|
env.pop2().extract(args[0], bci);
|
|
goto genValueReturn;
|
|
case bcIReturn:
|
|
case bcFReturn:
|
|
case bcAReturn:
|
|
env.pop().extract(args[0], bci);
|
|
genValueReturn:
|
|
vk = args[0].getKind();
|
|
cn->setControlReturn(1, &vk, bci);
|
|
cn->getReturnExtra()[0].getInput() = args[0];
|
|
goto genReturn;
|
|
case bcReturn:
|
|
cn->setControlReturn(0, 0, bci);
|
|
genReturn:
|
|
assert(block.nSuccessors() == 1);
|
|
bt.linkPredecessor(*block.successorsBegin[0], cn->getReturnSuccessor(), env, true);
|
|
cn = 0;
|
|
assert(bc == bytecodesEnd);
|
|
return;
|
|
|
|
case bcGetStatic:
|
|
genGetStatic(readBigUHalfwordUnaligned(bc), bci);
|
|
bc += 2;
|
|
break;
|
|
case bcPutStatic:
|
|
genPutStatic(readBigUHalfwordUnaligned(bc), bci);
|
|
bc += 2;
|
|
break;
|
|
case bcGetField:
|
|
genGetField(readBigUHalfwordUnaligned(bc), bci);
|
|
add2CheckDeadRestOfBlock:
|
|
bc += 2;
|
|
// Exit if the rest of this BytecodeBlock is dead.
|
|
checkDeadRestOfBlock:
|
|
if (!cn) {
|
|
// The rest of this BytecodeBlock is dead.
|
|
assert(bc < bytecodesEnd);
|
|
return;
|
|
}
|
|
break;
|
|
case bcPutField:
|
|
genPutField(readBigUHalfwordUnaligned(bc), bci);
|
|
goto add2CheckDeadRestOfBlock;
|
|
|
|
case bcInvokeVirtual:
|
|
case bcInvokeSpecial:
|
|
case bcInvokeStatic:
|
|
genInvoke(opcode, readBigUHalfwordUnaligned(bc), 0, bci);
|
|
goto add2CheckDeadRestOfBlock;
|
|
case bcInvokeInterface:
|
|
genInvoke(opcode, readBigUHalfwordUnaligned(bc), *(Uint8 *)(bc + 2), bci);
|
|
bc += 4;
|
|
goto checkDeadRestOfBlock;
|
|
|
|
case bcNew:
|
|
args[0].setConstant(objectAddress(&classFileSummary.lookupClass(readBigUHalfwordUnaligned(bc))));
|
|
genNewCommon(scNew, 1, args, bci);
|
|
goto add2CheckDeadRestOfBlock;
|
|
|
|
case bcNewArray:
|
|
i = *(Uint8 *)bc;
|
|
bc++;
|
|
i -= natMin;
|
|
if (i >= natLimit - natMin)
|
|
verifyError(VerifyError::badNewArrayType);
|
|
env.pop().extract(args[0], bci);
|
|
genNewCommon(*newArrayCalls[i], 1, args, bci);
|
|
goto checkDeadRestOfBlock;
|
|
|
|
case bcANewArray:
|
|
args[0].setConstant(objectAddress(&classFileSummary.lookupType(readBigUHalfwordUnaligned(bc))));
|
|
env.pop().extract(args[1], bci);
|
|
genNewCommon(scNewObjectArray, 2, args, bci);
|
|
goto add2CheckDeadRestOfBlock;
|
|
|
|
case bcMultiANewArray:
|
|
cl = &classFileSummary.lookupType(readBigUHalfwordUnaligned(bc));
|
|
args[0].setConstant(objectAddress(cl)); // array type argument
|
|
i = *(Uint8 *)(bc+2); // dimension
|
|
bc += 3;
|
|
switch (i) {
|
|
case 1:
|
|
cl = &asArray(*cl).getElementType();
|
|
if (isPrimitiveKind(cl->typeKind)) { // converted to a base type
|
|
env.pop().extract(args[0], bci); // number of elements
|
|
genNewCommon(*typeToArrayCalls[cl->typeKind], 1, args, bci);
|
|
}
|
|
else { // converted to a class type
|
|
env.pop().extract(args[1], bci); // number of elements
|
|
genNewCommon(scNewObjectArray, 2, args, bci);
|
|
}
|
|
break;
|
|
case 2:
|
|
env.pop().extract(args[2], bci); // first dimension
|
|
env.pop().extract(args[1], bci); // second dimension
|
|
genNewCommon(scNew2DArray, 3, args, bci);
|
|
break;
|
|
case 3:
|
|
env.pop().extract(args[3], bci); // as above
|
|
env.pop().extract(args[2], bci);
|
|
env.pop().extract(args[1], bci);
|
|
genNewCommon(scNew3DArray, 4, args, bci);
|
|
break;
|
|
default:
|
|
// i>=4
|
|
// generate temporary array to store all the dimensions in, and
|
|
// push it onto the environment
|
|
genTempArrayOfDim(i, bci);
|
|
if (!cn)
|
|
return; // temporary array construction will throw exception
|
|
env.pop().extract(args[1], bci);
|
|
genNewCommon(scNewNDArray, 2, args, bci);
|
|
break;
|
|
}
|
|
goto checkDeadRestOfBlock;
|
|
|
|
case bcArrayLength:
|
|
env.pop().extract(args[0], bci);
|
|
genReadArrayLength(args[0], result, bci);
|
|
if (!cn)
|
|
return; // The rest of this BytecodeBlock is dead.
|
|
env.push().define(result, cn, envPool);
|
|
break;
|
|
|
|
case bcAThrow:
|
|
env.pop().extract(args[0], bci);
|
|
genThrow(args[0]);
|
|
// The rest of this BytecodeBlock is dead, and we should be at the end of it.
|
|
assert(bc == bytecodesEnd);
|
|
return;
|
|
|
|
case bcCheckCast:
|
|
env.stackNth(1).extract(args[0], bci);
|
|
genCheckCast(readBigUHalfwordUnaligned(bc), args[0], bci);
|
|
goto add2CheckDeadRestOfBlock;
|
|
|
|
case bcInstanceOf:
|
|
env.pop().extract(args[0], bci);
|
|
genInstanceOf(readBigUHalfwordUnaligned(bc), args[0], result, bci);
|
|
assert(cn); // instanceof can't throw exceptions.
|
|
bc += 2;
|
|
env.push().define(result, cn, envPool);
|
|
break;
|
|
|
|
case bcMonitorEnter:
|
|
env.pop().extract(args[0], bci);
|
|
genNullGuard(args[0], bci);
|
|
if (cn)
|
|
genPossibleThrow(genMonitor(poMEnter, args[0], bci), Standard::get(cError));
|
|
else {
|
|
// The rest of this BytecodeBlock is dead.
|
|
assert(bc < bytecodesEnd);
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case bcMonitorExit:
|
|
env.pop().extract(args[0], bci);
|
|
genMonitor(poMExit, args[0], bci);
|
|
break;
|
|
|
|
case bcWide:
|
|
opcode = *bc++;
|
|
i = readBigUHalfwordUnaligned(bc);
|
|
bc += 2;
|
|
switch (opcode) {
|
|
case bcILoad:
|
|
case bcFLoad:
|
|
case bcALoad:
|
|
goto pushLoad1;
|
|
case bcLLoad:
|
|
case bcDLoad:
|
|
goto pushLoad2;
|
|
|
|
case bcIStore:
|
|
case bcFStore:
|
|
case bcAStore:
|
|
goto popStore1;
|
|
case bcLStore:
|
|
case bcDStore:
|
|
goto popStore2;
|
|
|
|
case bcIInc:
|
|
j = readBigSHalfwordUnaligned(bc);
|
|
bc += 2;
|
|
goto incLocal;
|
|
|
|
// We should not see these here; they should have been taken out by now.
|
|
// case bcRet:
|
|
default:
|
|
verifyError(VerifyError::badBytecode);
|
|
}
|
|
break;
|
|
|
|
case bcIfNull:
|
|
case bcIfNonnull:
|
|
env.pop().extract(args[0], bci);
|
|
prim = builder_CmpUEq_AAC.specializeArgConst(args[0], nullAddr, result, cn);
|
|
goto genIf;
|
|
|
|
case bcBreakpoint:
|
|
// Ignore breakpoints.
|
|
break;
|
|
|
|
// We should not see these here; they should have been taken out by now.
|
|
// case bcRet:
|
|
default:
|
|
verifyError(VerifyError::badBytecode);
|
|
}
|
|
}
|
|
|
|
// We fell off the last opcode of this basic block of bytecodes.
|
|
// We should have exactly one successor.
|
|
assert(block.nSuccessors() == 1);
|
|
createBasicBlock(block.getSuccessor(0));
|
|
}
|
|
|
|
|
|
//
|
|
// Adjust this BytecodeBlock's local environment to allow primitives to be generated
|
|
// for this BytecodeBlock before all of its predecessors have been generated.
|
|
// This means that local and stack variable bindings may have to be replaced by
|
|
// phantom phi nodes. [This is inefficient, so this code should be revised to
|
|
// do a prepass figuring out exactly which phi nodes are needed.]
|
|
//
|
|
// Also create an aexc header node for the cycle. Set cn to the ControlNode to use for
|
|
// the rest of this BytecodeBlock.
|
|
//
|
|
void BytecodeTranslator::BlockTranslator::genCycleHeader()
|
|
{
|
|
translationEnv->anticipate(block);
|
|
genException(ckAExc, 0, Standard::get(cThreadDeath), false);
|
|
appendControlNode(cn->getNormalSuccessor());
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// BytecodeTranslator
|
|
|
|
|
|
//
|
|
// If stackNormalization is either sn0, sn1, or sn2, pop all except the top
|
|
// zero, one, or two words from env, modifying it in place.
|
|
//
|
|
void BytecodeTranslator::normalizeEnv(TranslationEnv &env, BasicBlock::StackNormalization stackNormalization)
|
|
{
|
|
TranslationBinding tempBinding;
|
|
|
|
switch (stackNormalization) {
|
|
case BasicBlock::snNoChange:
|
|
return;
|
|
case BasicBlock::sn0:
|
|
break;
|
|
case BasicBlock::sn1:
|
|
tempBinding = env.pop();
|
|
break;
|
|
case BasicBlock::sn2:
|
|
tempBinding = env.pop2();
|
|
break;
|
|
}
|
|
env.dropAll();
|
|
switch (stackNormalization) {
|
|
case BasicBlock::sn0:
|
|
break;
|
|
case BasicBlock::sn1:
|
|
env.push() = tempBinding;
|
|
break;
|
|
case BasicBlock::sn2:
|
|
env.push2() = tempBinding;
|
|
break;
|
|
default:
|
|
trespass("Bad stackNormalization");
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Take notice that the control node(s) generated from BasicBlock block will have
|
|
// another predecessor, as given by controlEdge. The controlEdge should be allocated
|
|
// out of the control graph's memory pool.
|
|
// Merge the BasicBlock's environment with predecessorEnv, adding phi nodes or
|
|
// phantom phi nodes as needed. If canOwnEnv is true, the BasicBlock can assume
|
|
// ownership of predecessorEnv (thereby sometimes eliminating an unnecessary copy).
|
|
//
|
|
void BytecodeTranslator::linkPredecessor(BasicBlock &block, ControlEdge &controlEdge, TranslationEnv &predecessorEnv, bool canOwnEnv)
|
|
{
|
|
BasicBlock::StackNormalization stackNormalization = block.stackNormalization;
|
|
if (stackNormalization != BasicBlock::snNoChange && predecessorEnv.getSP() != (Uint32)(stackNormalization-BasicBlock::sn0))
|
|
// We have to normalize the stack inside predecessorEnv.
|
|
if (canOwnEnv)
|
|
normalizeEnv(predecessorEnv, stackNormalization);
|
|
else {
|
|
TranslationEnv envCopy(predecessorEnv);
|
|
normalizeEnv(envCopy, stackNormalization);
|
|
linkPredecessor(block, controlEdge, envCopy, true);
|
|
return;
|
|
}
|
|
|
|
ControlNode *first = block.getFirstControlNode();
|
|
assert(block.getGeneratedControlNodes() == (first != 0));
|
|
if (first) {
|
|
// Only aexc nodes can be targets of backward edges.
|
|
assert(block.hasIncomingBackEdges && first->hasControlKind(ckAExc));
|
|
first->disengagePhis();
|
|
first->addPredecessor(controlEdge);
|
|
} else
|
|
block.getPredecessors().addLast(controlEdge);
|
|
|
|
if (block.getEnvInInitialized())
|
|
block.getTranslationEnvIn().meet(predecessorEnv, block.hasKind(BasicBlock::bbEnd), block);
|
|
else {
|
|
if (canOwnEnv)
|
|
block.getTranslationEnvIn().move(predecessorEnv);
|
|
else
|
|
block.getTranslationEnvIn() = predecessorEnv;
|
|
block.getEnvInInitialized() = true;
|
|
}
|
|
#ifdef DEBUG
|
|
if (first)
|
|
first->reengagePhis();
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Call finishedOnePredecessor on every successor.
|
|
//
|
|
void BytecodeTranslator::finishedOutgoingEdges(BasicBlock &block)
|
|
{
|
|
BasicBlock **handlersEnd = block.handlersEnd;
|
|
for (BasicBlock **bp = block.successorsBegin; bp != handlersEnd; bp++)
|
|
finishedOnePredecessor(**bp);
|
|
}
|
|
|
|
|
|
//
|
|
// Create a new ControlNode for BasicBlock block.
|
|
// BasicBlock block must not be dead.
|
|
// Return the ControlNode to use for the rest of BasicBlock block.
|
|
// If mergeBlock is true and there is only one predecessor node that is a Block node,
|
|
// continue to use that Block node instead of making a new ControlNode.
|
|
//
|
|
ControlNode *BytecodeTranslator::createControlNode(BasicBlock &block, bool mergeBlock) const
|
|
{
|
|
assert(!block.getGeneratedControlNodes()); // We shouldn't have generated anything for this node yet.
|
|
|
|
if (mergeBlock && block.nPredecessors == block.getNSeenPredecessors() && block.getPredecessors().lengthIs(1)) {
|
|
ControlNode &predNode = DoublyLinkedList<ControlEdge>::get(block.getPredecessors().begin()).getSource();
|
|
if (predNode.hasControlKind(ckBlock)) {
|
|
// This node has only one predecessor and it was a ckBlock node, so
|
|
// merge this node and its predecessor now.
|
|
predNode.unsetControlKind();
|
|
#ifdef DEBUG
|
|
block.getGeneratedControlNodes() = true;
|
|
#endif
|
|
return &predNode;
|
|
}
|
|
}
|
|
|
|
ControlNode *cn = &controlGraph.newControlNode();
|
|
block.getFirstControlNode() = cn;
|
|
cn->movePredecessors(block.getPredecessors());
|
|
#ifdef DEBUG
|
|
block.getGeneratedControlNodes() = true;
|
|
#endif
|
|
return cn;
|
|
}
|
|
|
|
|
|
//
|
|
// Create control and data flow nodes out of the EndBlock. genPrimitives must
|
|
// have been called on every other BasicBlock before calling this.
|
|
//
|
|
void BytecodeTranslator::genPrimitives(EndBlock &block) const
|
|
{
|
|
// We should have seen all other blocks by now.
|
|
assert(block.getNSeenPredecessors() == block.nPredecessors && !block.hasIncomingBackEdges);
|
|
// The end block is never dead; either normal control flow or exceptions can always reach it.
|
|
assert(block.getEnvInInitialized());
|
|
|
|
assert(!block.getGeneratedControlNodes()); // We shouldn't have generated anything for this node yet.
|
|
ControlNode &endNode = controlGraph.getEndNode();
|
|
block.getFirstControlNode() = &endNode;
|
|
block.getFirstControlNode()->movePredecessors(block.getPredecessors());
|
|
#ifdef DEBUG
|
|
block.getGeneratedControlNodes() = true;
|
|
#endif
|
|
|
|
// Initialize the finalMemory in the EndNode.
|
|
// asharma - fix me
|
|
endNode.getEndExtra().finalMemory.getInput().setVariable(block.getTranslationEnvIn().memory().extract(0));
|
|
}
|
|
|
|
|
|
//
|
|
// Create control and data flow nodes out of the CatchBlock.
|
|
//
|
|
void BytecodeTranslator::genPrimitives(CatchBlock &block) const
|
|
{
|
|
// CatchBlocks cannot be targets of backward control edges.
|
|
assert(block.getNSeenPredecessors() == block.nPredecessors);
|
|
if (block.getEnvInInitialized()) {
|
|
// This isn't a dead CatchBlock if some predecessor already jumps here.
|
|
assert(block.getNSeenPredecessors());
|
|
assert(block.nSuccessors() == 1);
|
|
ControlNode *cn = createControlNode(block, false);
|
|
// asharma fix me!
|
|
block.getTranslationEnvIn().push().define(cn->setControlCatch(0));
|
|
linkPredecessor(block.getHandler(), cn->getNormalSuccessor(), block.getTranslationEnvIn(), true);
|
|
}
|
|
|
|
// Adjust the nPredecessors counts of the successor BasicBlock.
|
|
finishedOutgoingEdges(block);
|
|
}
|
|
|
|
|
|
//
|
|
// Create control and data flow nodes out of the BytecodeBlock.
|
|
//
|
|
void BytecodeTranslator::genPrimitives(BytecodeBlock &block) const
|
|
{
|
|
assert(block.getNSeenPredecessors() <= block.nPredecessors);
|
|
if (block.getNSeenPredecessors() != block.nPredecessors || block.getEnvInInitialized()) {
|
|
// This isn't a dead BytecodeBlock if either some predecessor already jumps
|
|
// here or we haven't examined all of the predecessor BasicBlocks yet.
|
|
assert(block.getNSeenPredecessors());
|
|
BlockTranslator translator(*this, block.getTranslationEnvIn(), block, createControlNode(block, true));
|
|
|
|
if (block.getNSeenPredecessors() == block.nPredecessors)
|
|
// We've seen all the predecessors already. Modify envIn directly -- we won't
|
|
// need it any more for merging additional predecessors.
|
|
translator.genLivePrimitives();
|
|
else {
|
|
// If this block has unseen predecessors in the depth-first order,
|
|
// then this block must be the header of a cycle. Use a copy of envIn to
|
|
// process this block's primitives.
|
|
assert(block.hasIncomingBackEdges);
|
|
translator.genCycleHeader();
|
|
TranslationEnv cycleEnv(block.getTranslationEnvIn());
|
|
translator.setEnv(cycleEnv);
|
|
translator.genLivePrimitives();
|
|
// cycleEnv must be live up to this point.
|
|
}
|
|
assert(translator.translatorDone());
|
|
}
|
|
|
|
// Adjust the nPredecessors counts of successor BasicBlocks.
|
|
finishedOutgoingEdges(block);
|
|
}
|
|
|
|
|
|
//
|
|
// Create control and data flow nodes out of this BytecodeGraph.
|
|
//
|
|
void BytecodeTranslator::genRawPrimitiveGraph() const
|
|
{
|
|
BasicBlock **blockArray = bytecodeGraph.getDFSList();
|
|
for (Uint32 i = bytecodeGraph.getDFSListLength() - 1; i; i--) {
|
|
BasicBlock *block = *blockArray++;
|
|
assert(block->hasKind(BasicBlock::bbBytecode) || block->hasKind(BasicBlock::bbCatch));
|
|
if (block->hasKind(BasicBlock::bbBytecode))
|
|
genPrimitives(*static_cast<BytecodeBlock *>(block));
|
|
else
|
|
genPrimitives(*static_cast<CatchBlock *>(block));
|
|
}
|
|
assert((*blockArray)->hasKind(BasicBlock::bbEnd));
|
|
genPrimitives(*static_cast<EndBlock *>(*blockArray));
|
|
}
|
|
|
|
|
|
//
|
|
// Add a synchronization primitive to acquire the syncHolder object's lock on
|
|
// method entry. Return the exc control node that contains the MEnter primitive.
|
|
//
|
|
static ControlNode &addEnterNode(ControlGraph &cg, VariableOrConstant &syncHolder)
|
|
{
|
|
Pool &pool = cg.pool;
|
|
DoublyLinkedList<ControlEdge>::iterator where;
|
|
ControlNode &beginNode = cg.getBeginNode();
|
|
ControlNode &endNode = cg.getEndNode();
|
|
ControlNode *secondNode;
|
|
DataNode &beginMemory = beginNode.getBeginExtra().initialMemory;
|
|
|
|
// Insert a new exc node (called enterNode) immediately after the begin node.
|
|
// The exc node's normal outgoing edge will go to the node (secondNode) that used to
|
|
// be the successor of the begin node. The exc node will also have one outgoing exception
|
|
// edge that goes directly to the end node.
|
|
ControlNode &enterNode = insertControlNodeAfter(beginNode, secondNode, where); // Disengages secondNode's phi nodes.
|
|
const Class **filter = new(pool) const Class *;
|
|
filter[0] = &Standard::get(cThrowable);
|
|
ControlEdge *enterSuccessors = new(pool) ControlEdge[2];
|
|
// asharma - fix me
|
|
Primitive &primEnter = makeMonitorPrimitive(poMEnter, beginMemory, syncHolder, 0, &enterNode, 0);
|
|
enterNode.setControlException(ckExc, 1, filter, &primEnter, enterSuccessors);
|
|
|
|
// Link the enterNode to its normal successor.
|
|
secondNode->addPredecessor(enterSuccessors[0], where);
|
|
secondNode->reengagePhis();
|
|
|
|
// Link the enterNode's exception edge to the end node,
|
|
// adjusting the end node's memory phi node as necessary.
|
|
VariableOrConstant memoryOutVOC;
|
|
memoryOutVOC.setVariable(primEnter);
|
|
endNode.disengagePhis();
|
|
endNode.addPredecessor(enterSuccessors[1]);
|
|
addDataFlow(endNode, endNode.getEndExtra().finalMemory.getInput(), memoryOutVOC);
|
|
endNode.reengagePhis();
|
|
|
|
// Replace the function's incoming memory edge by the memory edge (primEnter) generated by the MEnter
|
|
// everywhere except in the MEnter itself.
|
|
const DoublyLinkedList<DataConsumer> &beginMemoryConsumers = beginMemory.getConsumers();
|
|
for (DoublyLinkedList<DataConsumer>::iterator i = beginMemoryConsumers.begin(); !beginMemoryConsumers.done(i);) {
|
|
DataConsumer &c = beginMemoryConsumers.get(i);
|
|
i = beginMemoryConsumers.advance(i);
|
|
if (&c.getNode() != &primEnter) {
|
|
c.clearVariable();
|
|
c.setVariable(primEnter);
|
|
}
|
|
}
|
|
return enterNode;
|
|
}
|
|
|
|
|
|
//
|
|
// Add a synchronization primitive to release the syncHolder object's lock on
|
|
// normal method exit via the return node if there is one.
|
|
//
|
|
static void addNormalExitNode(ControlGraph &cg, VariableOrConstant &syncHolder)
|
|
{
|
|
ControlNode *returnNode = cg.getReturnNode();
|
|
if (returnNode) {
|
|
ControlNode &endNode = cg.getEndNode();
|
|
ControlEdge &returnEdge = returnNode->getReturnSuccessor();
|
|
|
|
// Add an MExit primitive to the return node, consuming the return node's
|
|
// current outgoing memory edge and producing a new memory edge.
|
|
DataConsumer &finalMemory = endNode.getEndExtra().finalMemory.getInput();
|
|
DataNode &returnMemory = followVariableBack(finalMemory, endNode, returnEdge).getVariable();
|
|
// asharma - fix me
|
|
Primitive &returnMemoryOut = makeMonitorPrimitive(poMExit, returnMemory, syncHolder, 0, returnNode, 0);
|
|
|
|
// Fix up the end node's memory phi node (creating one if needed) to receive the
|
|
// memory edge outgoing from our new MonitorExit primitive when control comes to the end
|
|
// node from the return node.
|
|
VariableOrConstant returnMemoryOutVOC;
|
|
returnMemoryOutVOC.setVariable(returnMemoryOut);
|
|
changeDataFlow(endNode, returnEdge, finalMemory, returnMemoryOutVOC);
|
|
}
|
|
}
|
|
|
|
|
|
class ExceptionEdgeIteratee: public Function1<bool, ControlEdge &>
|
|
{
|
|
ControlNode *enterNode;
|
|
|
|
public:
|
|
explicit ExceptionEdgeIteratee(ControlNode *enterNode): enterNode(enterNode) {}
|
|
|
|
bool operator()(ControlEdge &e);
|
|
};
|
|
|
|
|
|
//
|
|
// Edge e is an edge pointing to the end node in a synchronized method.
|
|
// Return true if that method's synchronization lock should be released if
|
|
// control follows edge e.
|
|
// Given our implementation of addEnterNode and addNormalExitNode, this
|
|
// method returns false for edges originating in:
|
|
// the return node,
|
|
// the node at the beginning of the method that acquires the monitor, and
|
|
// the node just before the return node that releases the monitor on normal exit.
|
|
// For all other edges this method returns true.
|
|
//
|
|
bool ExceptionEdgeIteratee::operator()(ControlEdge &e)
|
|
{
|
|
ControlNode &source = e.getSource();
|
|
return !(source.hasControlKind(ckReturn) || &source == enterNode);
|
|
}
|
|
|
|
|
|
//
|
|
// Add a synchronization primitive to release the syncHolder object's lock on
|
|
// exceptional method exit.
|
|
//
|
|
static void addExceptionalExitNode(ControlGraph &cg, VariableOrConstant &syncHolder, ControlNode &enterNode, Uint32 bci)
|
|
{
|
|
ExceptionEdgeIteratee eei(&enterNode);
|
|
ControlNode &endNode = cg.getEndNode();
|
|
ControlNode *exitCatchNode = insertControlNodeBefore(endNode, eei); // Disengages endNode's phi nodes if returns non-nil.
|
|
if (exitCatchNode) {
|
|
// We have at least one exceptional method exit path that doesn't come from
|
|
// the (already inserted) MEnter exc node.
|
|
|
|
// Make the newly inserted node into a catch node that intercepts all exceptions
|
|
// thrown out of this method except those coming from our new MEnter exc node.
|
|
// Connect this catch node's outgoing edge directly to the end node for now.
|
|
PrimCatch &primCatch = exitCatchNode->setControlCatch(bci);
|
|
ControlEdge &exitCatchSuccessorEdge = exitCatchNode->getNormalSuccessor();
|
|
endNode.addPredecessor(exitCatchSuccessorEdge);
|
|
endNode.reengagePhis();
|
|
|
|
// exitMemoryIn will be the memory input to the MonitorExit primitive we're
|
|
// about to insert.
|
|
DataConsumer &finalMemory = endNode.getEndExtra().finalMemory.getInput();
|
|
DataNode &exitMemoryIn = followVariableBack(finalMemory, endNode, exitCatchSuccessorEdge).getVariable();
|
|
|
|
// Insert a new throw node (called exitNode) immediately after the catch node.
|
|
// The throw node will have an MExit primitive followed by a throw primitive that
|
|
// rethrows the caught exception. The throw node will have one outgoing exception
|
|
// edge that goes directly to the end node.
|
|
ControlNode *successor;
|
|
DoublyLinkedList<ControlEdge>::iterator where;
|
|
ControlNode &exitNode = insertControlNodeAfter(*exitCatchNode, successor, where); // Disengages endNode's phi nodes.
|
|
assert(successor == &endNode);
|
|
// asharma - fix me
|
|
Primitive &exitMemoryOut = makeMonitorPrimitive(poMExit, exitMemoryIn, syncHolder, 0, &exitNode, 0);
|
|
|
|
// Initialize the throw primitive to rethrow the caught exception.
|
|
Pool &pool = cg.pool;
|
|
const Class **filter = new(pool) const Class *;
|
|
filter[0] = &Standard::get(cThrowable);
|
|
ControlEdge *throwSuccessors = new(pool) ControlEdge;
|
|
VariableOrConstant primCatchVOC;
|
|
primCatchVOC.setVariable(primCatch);
|
|
Primitive &throwPrim = makeSysCall(scThrow, 0, 1, &primCatchVOC, 0, 0, exitNode, bci);
|
|
exitNode.setControlException(ckThrow, 1, filter, &throwPrim, throwSuccessors);
|
|
|
|
// Link the throw node's outgoing edge to the end node at location where,
|
|
// adjusting the end node's memory phi node as necessary.
|
|
VariableOrConstant exitMemoryOutVOC;
|
|
exitMemoryOutVOC.setVariable(exitMemoryOut);
|
|
endNode.addPredecessor(throwSuccessors[0], where);
|
|
endNode.reengagePhis();
|
|
changeDataFlow(endNode, throwSuccessors[0], finalMemory, exitMemoryOutVOC);
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Add synchronization primitives to acquire the syncHolder object's lock on method
|
|
// entry and release that lock on normal or exceptional method exit. The ControlGraph
|
|
// should already have been generated for this method.
|
|
//
|
|
void BytecodeTranslator::addSynchronization(VariableOrConstant &syncHolder, Uint32 bci) const
|
|
{
|
|
ControlGraph &cg = controlGraph;
|
|
ControlNode &enterNode = addEnterNode(cg, syncHolder);
|
|
addNormalExitNode(cg, syncHolder);
|
|
addExceptionalExitNode(cg, syncHolder, enterNode, bci);
|
|
}
|
|
|
|
|
|
//
|
|
// Create a ControlGraph with data flow nodes out of this BytecodeGraph.
|
|
// Return the newly allocated ControlGraph or nil if an illegal bytecode was found.
|
|
// The BytecodeGraph must be complete and depthFirstSearch must have been called
|
|
// before calling this function; the usual way to do these is to call divideIntoBlocks.
|
|
//
|
|
ControlGraph *BytecodeTranslator::genPrimitiveGraph(BytecodeGraph &bytecodeGraph, Pool &primitivePool, Pool &tempPool)
|
|
{
|
|
ControlGraph &cg = *new(primitivePool)
|
|
ControlGraph(primitivePool, bytecodeGraph.nArguments, bytecodeGraph.argumentKinds, bytecodeGraph.isInstanceMethod, 1 /***** FIX ME *****/);
|
|
|
|
BytecodeTranslator translator(bytecodeGraph, cg, tempPool);
|
|
BasicBlock **block = bytecodeGraph.getDFSList();
|
|
BasicBlock **dfsListEnd = block + bytecodeGraph.getDFSListLength();
|
|
while (block != dfsListEnd)
|
|
(*block++)->initTranslation(translator);
|
|
|
|
BytecodeBlock *beginBlock = bytecodeGraph.beginBlock;
|
|
if (beginBlock) {
|
|
// Set up the locals and arguments
|
|
TranslationEnv env(translator);
|
|
|
|
env.init();
|
|
ControlNode::BeginExtra &beginExtra = cg.getBeginNode().getBeginExtra();
|
|
env.memory().define(beginExtra.initialMemory);
|
|
uint nArgs = bytecodeGraph.nArguments;
|
|
uint slotOffset = 0;
|
|
const ValueKind *ak = bytecodeGraph.argumentKinds;
|
|
for (uint n = 0; n != nArgs; n++) {
|
|
env.local(slotOffset++).define(beginExtra[n]);
|
|
if (isDoublewordKind(*ak++))
|
|
env.local(slotOffset++).defineSecondWord();
|
|
}
|
|
translator.linkPredecessor(*beginBlock, cg.getBeginNode().getNormalSuccessor(), env, true);
|
|
translator.finishedOnePredecessor(*beginBlock);
|
|
}
|
|
|
|
translator.genRawPrimitiveGraph();
|
|
if (bytecodeGraph.isSynchronized) {
|
|
VariableOrConstant sync;
|
|
if (bytecodeGraph.isInstanceMethod)
|
|
sync.setVariable(cg.getBeginNode().getBeginExtra()[0]);
|
|
else
|
|
sync.setConstant(objectAddress(bytecodeGraph.classFileSummary.getThisClass()));
|
|
// asharma fix me!
|
|
translator.addSynchronization(sync, 0);
|
|
}
|
|
return &cg;
|
|
}
|
|
|