gecko-dev/ef/Compiler/FrontEnd/BytecodeVerifier.cpp
1999-11-02 06:38:29 +00:00

1370 lines
45 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 "BytecodeVerifier.h"
#include "FieldOrMethod.h"
#include "MemoryAccess.h"
#include "GraphUtils.h"
static const VerificationEnv::BindingKind localAccessKinds[] =
{
VerificationEnv::bkInt, // bcILoad, bcIStore
VerificationEnv::bkLong, // bcLLoad, bcLStore
VerificationEnv::bkFloat, // bcFLoad, bcFStore
VerificationEnv::bkDouble, // bcDLoad, bcDStore
VerificationEnv::bkAddr, // bcALoad, bcAStore
VerificationEnv::bkInt, // bcILoad_0, bcIStore_0
VerificationEnv::bkInt, // bcILoad_1, bcIStore_1
VerificationEnv::bkInt, // bcILoad_2, bcIStore_2
VerificationEnv::bkInt, // bcILoad_3, bcIStore_3
VerificationEnv::bkLong, // bcLLoad_0, bcLStore_0
VerificationEnv::bkLong, // bcLLoad_1, bcLStore_1
VerificationEnv::bkLong, // bcLLoad_2, bcLStore_2
VerificationEnv::bkLong, // bcLLoad_3, bcLStore_3
VerificationEnv::bkFloat, // bcFLoad_0, bcFStore_0
VerificationEnv::bkFloat, // bcFLoad_1, bcFStore_1
VerificationEnv::bkFloat, // bcFLoad_2, bcFStore_2
VerificationEnv::bkFloat, // bcFLoad_3, bcFStore_3
VerificationEnv::bkDouble, // bcDLoad_0, bcDStore_0
VerificationEnv::bkDouble, // bcDLoad_1, bcDStore_1
VerificationEnv::bkDouble, // bcDLoad_2, bcDStore_2
VerificationEnv::bkDouble, // bcDLoad_3, bcDStore_3
VerificationEnv::bkAddr, // bcALoad_0, bcAStore_0
VerificationEnv::bkAddr, // bcALoad_1, bcAStore_1
VerificationEnv::bkAddr, // bcALoad_2, bcAStore_2
VerificationEnv::bkAddr // bcALoad_3, bcAStore_3
};
static const VerificationEnv::BindingKind arrayAccessKinds[] =
{
VerificationEnv::bkInt, // bcIALoad, bcIAStore, bcIReturn
VerificationEnv::bkLong, // bcLALoad, bcLAStore, bcLReturn
VerificationEnv::bkFloat, // bcFALoad, bcFAStore, bcFReturn
VerificationEnv::bkDouble, // bcDALoad, bcDAStore, bcDReturn
VerificationEnv::bkAddr, // bcAALoad, bcAAStore, bcAReturn
VerificationEnv::bkInt, // bcBALoad, bcBAStore
VerificationEnv::bkInt, // bcCALoad, bcCAStore
VerificationEnv::bkInt // bcSALoad, bcSAStore
};
static const VerificationEnv::BindingKind bytecodeSignatures[][3] =
{ // Argument 1 Argument 2 Result
{VerificationEnv::bkInt, VerificationEnv::bkInt, VerificationEnv::bkInt}, // bcIAdd
{VerificationEnv::bkLong, VerificationEnv::bkLong, VerificationEnv::bkLong}, // bcLAdd
{VerificationEnv::bkFloat, VerificationEnv::bkFloat, VerificationEnv::bkFloat}, // bcFAdd
{VerificationEnv::bkDouble, VerificationEnv::bkDouble, VerificationEnv::bkDouble}, // bcDAdd
{VerificationEnv::bkInt, VerificationEnv::bkInt, VerificationEnv::bkInt}, // bcISub
{VerificationEnv::bkLong, VerificationEnv::bkLong, VerificationEnv::bkLong}, // bcLSub
{VerificationEnv::bkFloat, VerificationEnv::bkFloat, VerificationEnv::bkFloat}, // bcFSub
{VerificationEnv::bkDouble, VerificationEnv::bkDouble, VerificationEnv::bkDouble}, // bcDSub
{VerificationEnv::bkInt, VerificationEnv::bkInt, VerificationEnv::bkInt}, // bcIMul
{VerificationEnv::bkLong, VerificationEnv::bkLong, VerificationEnv::bkLong}, // bcLMul
{VerificationEnv::bkFloat, VerificationEnv::bkFloat, VerificationEnv::bkFloat}, // bcFMul
{VerificationEnv::bkDouble, VerificationEnv::bkDouble, VerificationEnv::bkDouble}, // bcDMul
{VerificationEnv::bkInt, VerificationEnv::bkInt, VerificationEnv::bkInt}, // bcIDiv
{VerificationEnv::bkLong, VerificationEnv::bkLong, VerificationEnv::bkLong}, // bcLDiv
{VerificationEnv::bkFloat, VerificationEnv::bkFloat, VerificationEnv::bkFloat}, // bcFDiv
{VerificationEnv::bkDouble, VerificationEnv::bkDouble, VerificationEnv::bkDouble}, // bcDDiv
{VerificationEnv::bkInt, VerificationEnv::bkInt, VerificationEnv::bkInt}, // bcIRem
{VerificationEnv::bkLong, VerificationEnv::bkLong, VerificationEnv::bkLong}, // bcLRem
{VerificationEnv::bkFloat, VerificationEnv::bkFloat, VerificationEnv::bkFloat}, // bcFRem
{VerificationEnv::bkDouble, VerificationEnv::bkDouble, VerificationEnv::bkDouble}, // bcDRem
{VerificationEnv::bkInt, VerificationEnv::bkVoid, VerificationEnv::bkInt}, // bcINeg
{VerificationEnv::bkLong, VerificationEnv::bkVoid, VerificationEnv::bkLong}, // bcLNeg
{VerificationEnv::bkFloat, VerificationEnv::bkVoid, VerificationEnv::bkFloat}, // bcFNeg
{VerificationEnv::bkDouble, VerificationEnv::bkVoid, VerificationEnv::bkDouble}, // bcDNeg
{VerificationEnv::bkInt, VerificationEnv::bkInt, VerificationEnv::bkInt}, // bcIShl
{VerificationEnv::bkLong, VerificationEnv::bkInt, VerificationEnv::bkLong}, // bcLShl
{VerificationEnv::bkInt, VerificationEnv::bkInt, VerificationEnv::bkInt}, // bcIShr
{VerificationEnv::bkLong, VerificationEnv::bkInt, VerificationEnv::bkLong}, // bcLShr
{VerificationEnv::bkInt, VerificationEnv::bkInt, VerificationEnv::bkInt}, // bcIUShr
{VerificationEnv::bkLong, VerificationEnv::bkInt, VerificationEnv::bkLong}, // bcLUShr
{VerificationEnv::bkInt, VerificationEnv::bkInt, VerificationEnv::bkInt}, // bcIAnd
{VerificationEnv::bkLong, VerificationEnv::bkLong, VerificationEnv::bkLong}, // bcLAnd
{VerificationEnv::bkInt, VerificationEnv::bkInt, VerificationEnv::bkInt}, // bcIOr
{VerificationEnv::bkLong, VerificationEnv::bkLong, VerificationEnv::bkLong}, // bcLOr
{VerificationEnv::bkInt, VerificationEnv::bkInt, VerificationEnv::bkInt}, // bcIXor
{VerificationEnv::bkLong, VerificationEnv::bkLong, VerificationEnv::bkLong}, // bcLXor
{VerificationEnv::bkVoid, VerificationEnv::bkVoid, VerificationEnv::bkVoid}, // bcIInc
{VerificationEnv::bkInt, VerificationEnv::bkVoid, VerificationEnv::bkLong}, // bcI2L
{VerificationEnv::bkInt, VerificationEnv::bkVoid, VerificationEnv::bkFloat}, // bcI2F
{VerificationEnv::bkInt, VerificationEnv::bkVoid, VerificationEnv::bkDouble}, // bcI2D
{VerificationEnv::bkLong, VerificationEnv::bkVoid, VerificationEnv::bkInt}, // bcL2I
{VerificationEnv::bkLong, VerificationEnv::bkVoid, VerificationEnv::bkFloat}, // bcL2F
{VerificationEnv::bkLong, VerificationEnv::bkVoid, VerificationEnv::bkDouble}, // bcL2D
{VerificationEnv::bkFloat, VerificationEnv::bkVoid, VerificationEnv::bkInt}, // bcF2I
{VerificationEnv::bkFloat, VerificationEnv::bkVoid, VerificationEnv::bkLong}, // bcF2L
{VerificationEnv::bkFloat, VerificationEnv::bkVoid, VerificationEnv::bkDouble}, // bcF2D
{VerificationEnv::bkDouble, VerificationEnv::bkVoid, VerificationEnv::bkInt}, // bcD2I
{VerificationEnv::bkDouble, VerificationEnv::bkVoid, VerificationEnv::bkLong}, // bcD2L
{VerificationEnv::bkDouble, VerificationEnv::bkVoid, VerificationEnv::bkFloat}, // bcD2F
{VerificationEnv::bkInt, VerificationEnv::bkVoid, VerificationEnv::bkInt}, // bcInt2Byte
{VerificationEnv::bkInt, VerificationEnv::bkVoid, VerificationEnv::bkInt}, // bcInt2Char
{VerificationEnv::bkInt, VerificationEnv::bkVoid, VerificationEnv::bkInt}, // bcInt2Short
{VerificationEnv::bkLong, VerificationEnv::bkLong, VerificationEnv::bkInt}, // bcLCmp
{VerificationEnv::bkFloat, VerificationEnv::bkFloat, VerificationEnv::bkInt}, // bcFCmpL
{VerificationEnv::bkFloat, VerificationEnv::bkFloat, VerificationEnv::bkInt}, // bcFCmpG
{VerificationEnv::bkDouble, VerificationEnv::bkDouble, VerificationEnv::bkInt}, // bcDCmpL
{VerificationEnv::bkDouble, VerificationEnv::bkDouble, VerificationEnv::bkInt} // bcDCmpG
};
// ----------------------------------------------------------------------------
// SubroutineCallSiteList
//
// Return true if this Association is less than a2 in the "dictionary" ordering with
// the subroutine being the major key and callSite being the minor key. A nil callSite
// is considered to be less than ony other callSite.
//
bool SubroutineCallSiteList::Association::operator<(const Association &a2) const
{
// According to ANSI C++, we can't compare subroutine addresses directly because
// they are not elements of the same array. Instead, we compare their bytecodesBegin
// values, which do point into the same array.
const bytecode *begin1 = subroutine.bytecodesBegin;
const bytecode *begin2 = a2.subroutine.bytecodesBegin;
return begin1 < begin2 ||
begin1 == begin2 && a2.callSite && (!callSite || callSite->bytecodesBegin < a2.callSite->bytecodesBegin);
}
//
// Create an Iterator that will return all known call sites for the given subroutine.
//
SubroutineCallSiteList::Iterator::Iterator(SubroutineCallSiteList &csList, BytecodeBlock &subroutine):
subroutine(subroutine)
{
Association a(subroutine);
Node *n = csList.tree.findAfter(a);
if (n && &n->getKey().subroutine != &subroutine)
n = 0;
node = n;
}
//
// Advance the iterator to the next call site.
//
void SubroutineCallSiteList::Iterator::operator++()
{
assert(node);
Node *n = node->next();
if (n && &n->getKey().subroutine != &subroutine)
n = 0;
node = n;
}
//
// Add callSite to the list of subroutine's call sites if it is not there already.
// Allocate additional storage from pool if needed.
//
void SubroutineCallSiteList::addAssociation(BytecodeBlock &subroutine, BytecodeBlock &callSite, Pool &pool)
{
Association a(subroutine, callSite);
Node *where;
bool right;
if (!tree.find(a, where, right))
tree.attach(*new(pool) Node(a), where, right);
}
//
// Remove callSite to the list of subroutine's call sites. callSite must have been
// previously added (and not yet removed) to the list of subroutine's call sites.
//
void SubroutineCallSiteList::removeAssociation(BytecodeBlock &subroutine, BytecodeBlock &callSite)
{
Association a(subroutine, callSite);
Node *n = tree.find(a);
assert(n);
tree.remove(*n);
}
//
// Return a callSite and its subroutine of one association present in this
// SubroutineCallSiteList. At the same time remove that association from this
// SubroutineCallSiteList. Also return a flag that is true if this was the only
// association for this subroutine.
// If this SubroutineCallSiteList is already empty, return nil.
//
BytecodeBlock *SubroutineCallSiteList::popAssociation(BytecodeBlock *&subroutine, bool &onlyOneCallSite)
{
Node *n = tree.firstNode();
if (!n) {
subroutine = 0;
return 0;
}
subroutine = &n->getKey().subroutine;
BytecodeBlock *callSite = n->getKey().callSite;
Node *n2 = n->next();
onlyOneCallSite = !(n2 && &n2->getKey().subroutine == subroutine);
tree.remove(*n);
return callSite;
}
// ----------------------------------------------------------------------------
// BytecodeVerifier
//
// Initialize the BytecodeVerifier and set up all BasicBlocks in the bytecode
// graph for verification.
//
BytecodeVerifier::BytecodeVerifier(BytecodeGraph &bytecodeGraph, Pool &envPool):
VerificationEnv::Common(envPool, 0, bytecodeGraph.nLocals, bytecodeGraph.stackSize),
bytecodeGraph(bytecodeGraph),
classFileSummary(bytecodeGraph.classFileSummary)
{
BasicBlock **block = bytecodeGraph.getDFSList();
BasicBlock **dfsListEnd = block + bytecodeGraph.getDFSListLength();
while (block != dfsListEnd)
(*block++)->initVerification(*this);
}
//
// 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 BytecodeVerifier::normalizeEnv(VerificationEnv &env, BasicBlock::StackNormalization stackNormalization)
{
VerificationEnv::Binding tempBinding1;
VerificationEnv::Binding tempBinding2;
switch (stackNormalization) {
case BasicBlock::snNoChange:
return;
case BasicBlock::sn0:
break;
case BasicBlock::sn1:
tempBinding1 = env.pop1();
break;
case BasicBlock::sn2:
env.pop2(tempBinding1, tempBinding2);
break;
}
env.dropAll();
switch (stackNormalization) {
case BasicBlock::sn0:
break;
case BasicBlock::sn1:
env.push1(tempBinding1);
break;
case BasicBlock::sn2:
env.push2(tempBinding1, tempBinding2);
break;
default:
trespass("Bad stackNormalization");
}
}
//
// Intersect block's current incoming environment with predecessorEnv, updating the
// block's incoming environment. Return true if both conditions below are true:
// the block's incoming environment changed, and
// the block's generation is already equal to the given generation.
// When this method returns true, a block already examined on this pass through the
// depth-first list has changed, so another pass is needed.
//
// Set this block's recompute flag if its incoming environment changed.
//
// If canOwnEnv is true, the BasicBlock can assume ownership of predecessorEnv
// (thereby sometimes eliminating an unnecessary copy).
//
// Throw a verification error if the environments cannot be intersected.
//
bool BytecodeVerifier::predecessorChanged(BasicBlock &block, VerificationEnv &predecessorEnv, Uint32 generation, bool canOwnEnv)
{
assert(predecessorEnv.live());
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 {
VerificationEnv envCopy(predecessorEnv);
normalizeEnv(envCopy, stackNormalization);
return predecessorChanged(block, envCopy, generation, true);
}
bool changed = block.getGeneration() == generation;
if (block.getVerificationEnvIn().live())
if (block.getVerificationEnvIn().meet(predecessorEnv))
block.getRecompute() = true;
else
changed = false;
else {
block.getRecompute() = true;
if (canOwnEnv)
block.getVerificationEnvIn().move(predecessorEnv);
else
block.getVerificationEnvIn() = predecessorEnv;
}
return changed;
}
//
// Call predecessorChanged on every BasicBlock between blocksBegin (inclusive)
// and blocksEnd (exclusive). Return true if any predecessorChanged call returned
// true. Of course, canOwnEnv applies only to the last call to predecessorChanged.
//
bool BytecodeVerifier::predecessorChanged(BasicBlock **blocksBegin, BasicBlock **blocksEnd, VerificationEnv &predecessorEnv,
Uint32 generation, bool canOwnEnv)
{
bool changed = false;
while (blocksBegin != blocksEnd) {
BasicBlock *block = *blocksBegin++;
changed |= predecessorChanged(*block, predecessorEnv, generation, blocksBegin == blocksEnd && canOwnEnv);
}
return changed;
}
//
// Compute the verificationEnvIn, subroutineHeader, and subroutineRet fields of
// the block. Verify the block's stack and type discpiline. Return true if
// calling predecessorChanged on at least one of this block's successors returned true.
//
bool BytecodeVerifier::propagateDataflow(CatchBlock &block, Uint32 generation)
{
VerificationEnv env(block.getVerificationEnvIn());
env.push1(VerificationEnv::bkAddr);
return predecessorChanged(block.getHandler(), env, generation, true);
}
//
// Compute the verificationEnvIn, subroutineHeader, and subroutineRet fields of
// the block. Verify the block's stack and type discipline. Return true if
// calling predecessorChanged on at least one of this block's successors returned true.
//
bool BytecodeVerifier::propagateDataflow(BytecodeBlock &block, Uint32 generation)
{
VerificationEnv env(block.getVerificationEnvIn());
// Follow exception handlers from this block at the beginning of this block and
// after each change of a local variable.
bool changed = predecessorChanged(block.successorsEnd, block.handlersEnd, env, generation, false);
const bytecode *const bytecodesEnd = block.bytecodesEnd;
const bytecode *bc = block.bytecodesBegin;
assert(bc != bytecodesEnd);
while (bc != bytecodesEnd) {
assert(bc < bytecodesEnd);
bytecode opcode = *bc++;
ValueKind vk;
TypeKind tk;
Value v;
Uint32 i;
ConstantPoolIndex cpi;
const VerificationEnv::Binding *b;
const VerificationEnv::BindingKind *bk;
VerificationEnv::Binding b1;
VerificationEnv::Binding b2;
VerificationEnv::Binding b3;
VerificationEnv::Binding b4;
Signature sig;
Uint32 offset;
Uint32 interfaceNumber;
addr address;
bool isVolatile;
bool isConstant;
bool isInit;
BytecodeBlock *sub;
switch (opcode) {
case bcNop:
break;
case bcAConst_Null:
env.push1(VerificationEnv::bkAddr);
break;
case bcIConst_m1:
case bcIConst_0:
case bcIConst_1:
case bcIConst_2:
case bcIConst_3:
case bcIConst_4:
case bcIConst_5:
pushInt:
env.push1(VerificationEnv::bkInt);
break;
case bcLConst_0:
case bcLConst_1:
env.push2(VerificationEnv::bkLong);
break;
case bcFConst_0:
case bcFConst_1:
case bcFConst_2:
env.push1(VerificationEnv::bkFloat);
break;
case bcDConst_0:
case bcDConst_1:
env.push2(VerificationEnv::bkDouble);
break;
case bcBIPush:
bc++;
goto pushInt;
case bcSIPush:
bc += 2;
goto pushInt;
case bcLdc:
cpi = *(Uint8 *)bc;
bc++;
goto pushConst1;
case bcLdc_W:
cpi = readBigUHalfwordUnaligned(bc);
bc += 2;
pushConst1:
vk = classFileSummary.lookupConstant(cpi, v);
if (!isWordKind(vk))
verifyError(VerifyError::badType);
env.push1(VerificationEnv::valueKindToBindingKind(vk));
break;
case bcLdc2_W:
vk = classFileSummary.lookupConstant(readBigUHalfwordUnaligned(bc), v);
bc += 2;
if (!isDoublewordKind(vk))
verifyError(VerifyError::badType);
env.push2(VerificationEnv::valueKindToBindingKind(vk));
break;
case bcILoad:
case bcFLoad:
case bcALoad:
i = *(Uint8 *)bc;
bc++;
pushLoad1:
b = &env.getLocal1(i);
if (!b->hasKind(localAccessKinds[opcode - bcILoad]))
verifyError(VerifyError::badType);
env.push1(*b);
break;
case bcLLoad:
case bcDLoad:
i = *(Uint8 *)bc;
bc++;
pushLoad2:
b = &env.getLocal2(i);
if (!b->hasKind(localAccessKinds[opcode - bcILoad]))
verifyError(VerifyError::badType);
env.push2(*b);
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:
env.pop1(VerificationEnv::bkInt);
env.pop1(VerificationEnv::bkAddr);
env.push1or2(arrayAccessKinds[opcode - bcIALoad]);
break;
case bcIStore:
case bcFStore:
i = *(Uint8 *)bc;
bc++;
popStore1:
env.setLocal1(i, env.pop1(localAccessKinds[opcode - bcIStore]));
recheckHandlers:
// We changed the local environment, so propagate the environment to the
// exception handlers again.
changed |= predecessorChanged(block.successorsEnd, block.handlersEnd, env, generation, false);
break;
case bcAStore:
i = *(Uint8 *)bc;
bc++;
popAStore1:
b = &env.pop1();
if (!(b->hasKind(VerificationEnv::bkAddr) || b->hasKind(VerificationEnv::bkReturn)))
verifyError(VerifyError::badType);
env.setLocal1(i, *b);
goto recheckHandlers;
case bcLStore:
case bcDStore:
i = *(Uint8 *)bc;
bc++;
popStore2:
env.setLocal2(i, env.pop2(localAccessKinds[opcode - bcIStore]));
goto recheckHandlers;
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 popAStore1;
case bcIAStore:
case bcLAStore:
case bcFAStore:
case bcDAStore:
case bcAAStore:
case bcBAStore:
case bcCAStore:
case bcSAStore:
env.pop1or2(arrayAccessKinds[opcode - bcIAStore]);
env.pop1(VerificationEnv::bkInt);
env.pop1(VerificationEnv::bkAddr);
break;
case bcPop:
env.pop1();
break;
case bcPop2:
env.pop2(b1, b2);
break;
case bcDup:
b1 = env.pop1();
env.push1(b1);
env.push1(b1);
break;
case bcDup_x1:
b1 = env.pop1();
b2 = env.pop1();
env.push1(b1);
env.push1(b2);
env.push1(b1);
break;
case bcDup_x2:
b1 = env.pop1();
env.pop2(b2, b3);
env.push1(b1);
env.push2(b2, b3);
env.push1(b1);
break;
case bcDup2:
env.pop2(b1, b2);
env.push2(b1, b2);
env.push2(b1, b2);
break;
case bcDup2_x1:
env.pop2(b1, b2);
b3 = env.pop1();
env.push2(b1, b2);
env.push1(b3);
env.push2(b1, b2);
break;
case bcDup2_x2:
env.pop2(b1, b2);
env.pop2(b3, b4);
env.push2(b1, b2);
env.push2(b3, b4);
env.push2(b1, b2);
break;
case bcSwap:
b1 = env.pop1();
b2 = env.pop1();
env.push1(b1);
env.push1(b2);
break;
case bcIAdd:
case bcLAdd:
case bcFAdd:
case bcDAdd:
case bcISub:
case bcLSub:
case bcFSub:
case bcDSub:
case bcIMul:
case bcLMul:
case bcFMul:
case bcDMul:
case bcIDiv:
case bcLDiv:
case bcFDiv:
case bcDDiv:
case bcIRem:
case bcLRem:
case bcFRem:
case bcDRem:
case bcIShl:
case bcLShl:
case bcIShr:
case bcLShr:
case bcIUShr:
case bcLUShr:
case bcIAnd:
case bcLAnd:
case bcIOr:
case bcLOr:
case bcIXor:
case bcLXor:
case bcLCmp:
case bcFCmpL:
case bcFCmpG:
case bcDCmpL:
case bcDCmpG:
bk = bytecodeSignatures[opcode - bcIAdd];
env.pop1or2(bk[1]);
goto popPushUnary;
case bcINeg:
case bcLNeg:
case bcFNeg:
case bcDNeg:
case bcI2L:
case bcI2F:
case bcI2D:
case bcL2I:
case bcL2F:
case bcL2D:
case bcF2I:
case bcF2L:
case bcF2D:
case bcD2I:
case bcD2L:
case bcD2F:
case bcInt2Byte:
case bcInt2Char:
case bcInt2Short:
bk = bytecodeSignatures[opcode - bcIAdd];
popPushUnary:
env.pop1or2(bk[0]);
env.push1or2(bk[2]);
break;
case bcIInc:
i = *(Uint8 *)bc;
bc += 2;
incLocal:
if (!env.getLocal1(i).hasKind(VerificationEnv::bkInt))
verifyError(VerifyError::badType);
break;
case bcIf_ICmpEq:
case bcIf_ICmpNe:
case bcIf_ICmpLt:
case bcIf_ICmpGe:
case bcIf_ICmpGt:
case bcIf_ICmpLe:
env.pop1(VerificationEnv::bkInt);
case bcIfEq:
case bcIfNe:
case bcIfLt:
case bcIfGe:
case bcIfGt:
case bcIfLe:
env.pop1(VerificationEnv::bkInt);
bc += 2;
// We should be at the end of the basic block.
assert(bc == bytecodesEnd);
break;
case bcIf_ACmpEq:
case bcIf_ACmpNe:
env.pop1(VerificationEnv::bkAddr);
case bcIfNull:
case bcIfNonnull:
env.pop1(VerificationEnv::bkAddr);
bc += 2;
// We should be at the end of the basic block.
assert(bc == bytecodesEnd);
break;
case bcGoto_W:
bc += 2;
case bcGoto:
bc += 2;
// We should be at the end of the basic block.
assert(bc == bytecodesEnd);
break;
case bcTableSwitch:
case bcLookupSwitch:
env.pop1(VerificationEnv::bkInt);
bc = bytecodesEnd;
break;
case bcIReturn:
case bcLReturn:
case bcFReturn:
case bcDReturn:
case bcAReturn:
env.pop1or2(arrayAccessKinds[opcode - bcIReturn]);
case bcReturn:
// We should be at the end of the basic block.
assert(bc == bytecodesEnd);
break;
case bcGetStatic:
env.push1or2(VerificationEnv::typeKindToBindingKind(
classFileSummary.lookupStaticField(readBigUHalfwordUnaligned(bc), vk, address, isVolatile, isConstant)));
bc += 2;
break;
case bcPutStatic:
env.pop1or2(VerificationEnv::typeKindToBindingKind(
classFileSummary.lookupStaticField(readBigUHalfwordUnaligned(bc), vk, address, isVolatile, isConstant)));
bc += 2;
break;
case bcGetField:
env.pop1(VerificationEnv::bkAddr);
env.push1or2(VerificationEnv::typeKindToBindingKind(
classFileSummary.lookupInstanceField(readBigUHalfwordUnaligned(bc), vk, offset, isVolatile, isConstant)));
bc += 2;
break;
case bcPutField:
env.pop1or2(VerificationEnv::typeKindToBindingKind(
classFileSummary.lookupInstanceField(readBigUHalfwordUnaligned(bc), vk, offset, isVolatile, isConstant)));
env.pop1(VerificationEnv::bkAddr);
bc += 2;
break;
case bcInvokeVirtual:
classFileSummary.lookupVirtualMethod(readBigUHalfwordUnaligned(bc), sig, offset, address);
popPushInvoke:
bc += 2;
i = sig.nArguments;
while (i--)
env.pop1or2(VerificationEnv::typeKindToBindingKind(sig.argumentTypes[i]->typeKind));
tk = sig.resultType->typeKind;
if (tk != tkVoid)
env.push1or2(VerificationEnv::typeKindToBindingKind(tk));
break;
case bcInvokeSpecial:
classFileSummary.lookupSpecialMethod(readBigUHalfwordUnaligned(bc), sig, isInit, address);
goto popPushInvoke;
case bcInvokeStatic:
classFileSummary.lookupStaticMethod(readBigUHalfwordUnaligned(bc), sig, address);
goto popPushInvoke;
case bcInvokeInterface:
classFileSummary.lookupInterfaceMethod(readBigUHalfwordUnaligned(bc), sig, offset, interfaceNumber, address, *(Uint8 *)(bc + 2));
bc += 2;
goto popPushInvoke;
case bcNew:
classFileSummary.lookupClass(readBigUHalfwordUnaligned(bc));
bc += 2;
env.push1(VerificationEnv::bkAddr);
break;
case bcNewArray:
i = *(Uint8 *)bc;
bc++;
i -= natMin;
if (i >= natLimit - natMin)
verifyError(VerifyError::badNewArrayType);
env.pop1(VerificationEnv::bkInt);
env.push1(VerificationEnv::bkAddr);
break;
case bcANewArray:
classFileSummary.lookupType(readBigUHalfwordUnaligned(bc));
bc += 2;
env.pop1(VerificationEnv::bkInt);
env.push1(VerificationEnv::bkAddr);
break;
case bcMultiANewArray:
classFileSummary.lookupType(readBigUHalfwordUnaligned(bc));
i = *(Uint8 *)(bc+2); // Number of dimensions
bc += 3;
if (i == 0)
verifyError(VerifyError::badNewArrayType);
while (i--)
env.pop1(VerificationEnv::bkInt);
env.push1(VerificationEnv::bkAddr);
break;
case bcArrayLength:
env.pop1(VerificationEnv::bkAddr);
env.push1(VerificationEnv::bkInt);
break;
case bcAThrow:
env.pop1(VerificationEnv::bkAddr);
// The rest of this BytecodeBlock is dead, and we should be at the end of it.
assert(bc == bytecodesEnd);
break;
case bcCheckCast:
classFileSummary.lookupType(readBigUHalfwordUnaligned(bc));
bc += 2;
env.pop1(VerificationEnv::bkAddr);
env.push1(VerificationEnv::bkAddr);
break;
case bcInstanceOf:
classFileSummary.lookupType(readBigUHalfwordUnaligned(bc));
bc += 2;
env.pop1(VerificationEnv::bkAddr);
env.push1(VerificationEnv::bkInt);
break;
case bcMonitorEnter:
case bcMonitorExit:
env.pop1(VerificationEnv::bkAddr);
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:
goto popStore1;
case bcLStore:
case bcDStore:
goto popStore2;
case bcAStore:
goto popAStore1;
case bcIInc:
bc += 2;
goto incLocal;
case bcRet:
goto handleRet;
default:
verifyError(VerifyError::badBytecode);
}
break;
case bcBreakpoint:
// Ignore breakpoints.
break;
case bcJsr_W:
bc += 2;
case bcJsr:
bc += 2;
// We have two successors to a jsr bytecode's basic block. The first is the
// code that follows the jsr bytecode, while the second is the beginning of the
// subroutine.
// At this point we do not propagate the environment to the code that follows
// the jst bytecode because we don't know the state of the registers, and, besides,
// the subroutine might not return at all. Instead, we only propagate the environment
// into the subroutine and rely on ret to propagate it to the instructions after
// all jsr's that could have called that subroutine.
assert(block.hasSubkind(BytecodeBlock::skJsr));
sub = &block.getSuccessor(0);
env.enterSubroutine(sub);
b1.setReturn(sub);
env.push1(b1);
subroutineCallSiteList.addAssociation(*sub, block, envPool);
// We should be at the end of the basic block.
assert(bc == bytecodesEnd);
changed |= predecessorChanged(*sub, env, generation, true);
return changed;
case bcRet:
i = *(Uint8 *)bc;
bc++;
handleRet:
b = &env.getLocal1(i);
if (!b->hasKind(VerificationEnv::bkReturn))
verifyError(VerifyError::badType);
sub = b->getSubroutine();
// We have to figure out the successors dynamically using the subroutine's address.
assert(block.hasSubkind(BytecodeBlock::skRet) && block.nSuccessors() == 0);
{
// This can be an assert instead of an if/verifyError because if we really did have
// two jsr's going to this ret, the return address local's BindingKind would become
// bkVoid instead of bkReturn and we would catch this above.
assert(!block.getSubroutineHeader() || block.getSubroutineHeader() == sub);
block.getSubroutineHeader() = sub;
BytecodeBlock *oldRet = sub->getSubroutineRet();
if (oldRet && oldRet != &block)
verifyError(VerifyError::multipleRet);
sub->getSubroutineRet() = &block;
for (SubroutineCallSiteList::Iterator iter(subroutineCallSiteList, *sub); iter.more(); ++iter) {
BytecodeBlock &succ = *iter;
assert(succ.hasSubkind(BytecodeBlock::skJsr));
VerificationEnv succEnv(env);
succEnv.exitSubroutine(sub, succ.getVerificationEnvIn());
changed |= predecessorChanged(succ.getSuccessor(1), succEnv, generation, true);
}
}
// We should be at the end of the basic block.
assert(bc == bytecodesEnd);
return changed;
default:
verifyError(VerifyError::badBytecode);
}
}
// Propagate dataflow information to all non-exceptional successors.
changed |= predecessorChanged(block.successorsBegin, block.successorsEnd, env, generation, true);
return changed;
}
//
// Compute the verificationEnvIn, subroutineHeader, and subroutineRet fields of
// each block in the bytecode graph. This will verify the method's stack and
// type discpiline and match each ret (and wide ret) bytecode in the method with
// the entry address of the subroutine to which it corresponds.
// Set all retReachable fields of Context structures in the verification environments
// to false.
//
// depthFirstSearch should have been called on entry to this function. The
// subroutineHeader and subroutineRet fields of each bytecode graph block should
// be nil on entry.
//
// This function uses the generation and recompute fields of bytecode graph blocks
// as temporaries. It expects all recompute flags to be false on entry, and it
// leaves them that way on exit.
//
void BytecodeVerifier::computeDataflow()
{
Pool bindingPool;
setBindingPool(bindingPool);
BasicBlock **dfsList = bytecodeGraph.getDFSList();
BasicBlock **dfsListEnd = dfsList + bytecodeGraph.getDFSListLength() - 1;
assert(bytecodeGraph.getDFSListLength() > 0);
Uint32 generation = 0;
// Set up the locals and arguments
VerificationEnv env(*this);
env.initLive();
uint nArgs = bytecodeGraph.nArguments;
uint slotOffset = 0;
const ValueKind *ak = bytecodeGraph.argumentKinds;
for (uint n = 0; n != nArgs; n++) {
VerificationEnv::Binding b;
b.setKind(VerificationEnv::valueKindToBindingKind(*ak++));
if (b.isOneWord())
env.setLocal1(slotOffset++, b);
else {
env.setLocal2(slotOffset, b);
slotOffset += 2;
}
}
bool changed = predecessorChanged(*bytecodeGraph.beginBlock, env, generation, true);
while (changed) {
generation++;
changed = false;
BasicBlock **blockPtr = dfsList;
while (blockPtr != dfsListEnd) {
BasicBlock *block = *blockPtr++;
block->getGeneration() = generation;
if (block->getRecompute()) {
block->getRecompute() = false;
assert(block->getVerificationEnvIn().live());
assert(block->hasKind(BasicBlock::bbBytecode) || block->hasKind(BasicBlock::bbCatch));
if (block->hasKind(BasicBlock::bbBytecode))
changed |= propagateDataflow(*static_cast<BytecodeBlock *>(block), generation);
else
changed |= propagateDataflow(*static_cast<CatchBlock *>(block), generation);
}
}
}
assert((*dfsListEnd)->hasKind(BasicBlock::bbEnd));
(*dfsListEnd)->getRecompute() = false;
#ifdef DEBUG
// There shouldn't be any blocks left to recompute.
for (BasicBlock **blockPtr = dfsList; blockPtr != dfsListEnd+1; blockPtr++)
assert(!(*blockPtr)->getRecompute());
#endif
clearBindingPool();
}
//
// Do two things:
//
// 1. Compute the retReachable fields of Context structures in the verification environments
// in each block in the bytecode graph. The rest of the environments should have been
// computed by computeDataflow.
//
// Let B be a block with one or more Contexts in its verification environment and let
// C be one of those Contexts and S be C's subroutine. On output C's retReachable will
// be set if S's ret can be reached from B without going through S's jsr again.
// If C's retReachable is not set, we can consider B to not really be a part of S
// because there is no way to reach S's ret from B; instead, we consider S to have
// exited via a jump before it got to B.
//
// 2. Convert each BytecodeBlocks with a jsr's for which there is no ret into a
// skJsrNoRet-subkind block. That block will have one successor instead of two because
// the subroutine it calls cannot return.
//
// This function uses the generation fields of bytecode graph blocks as temporaries.
//
void BytecodeVerifier::computeRetReachables()
{
BasicBlock **dfsList = bytecodeGraph.getDFSList();
BasicBlock **dfsListEnd = dfsList + bytecodeGraph.getDFSListLength();
bool changed = false;
BasicBlock **blockPtr = dfsList;
while (blockPtr != dfsListEnd) {
BasicBlock *block = *blockPtr++;
block->getGeneration() = 0;
if (block->hasKind(BasicBlock::bbBytecode) && block->getVerificationEnvIn().live()) {
BytecodeBlock::Subkind subkind = static_cast<BytecodeBlock *>(block)->subkind;
if (subkind == BytecodeBlock::skRet) {
block->getVerificationEnvIn().setRetReachable(block->getSubroutineHeader());
block->getGeneration() = 1;
changed = true;
} else if (subkind == BytecodeBlock::skJsr) {
assert(block->nSuccessors() == 2);
if (!block->getSuccessor(0).getSubroutineRet()) {
static_cast<BytecodeBlock *>(block)->transformToJsrNoRet();
subroutineCallSiteList.removeAssociation(block->getSuccessor(0), *static_cast<BytecodeBlock *>(block));
}
}
}
}
dfsListEnd--; // Ignore the end block from now on.
Uint32 generation = 1;
while (changed) {
Uint32 oldGeneration = generation;
generation++;
changed = false;
// We're doing a backward analysis, so traverse the DFS list in reverse order.
// This isn't quite as good as computing the DFS of the reverse graph, but it works;
// figuring out the real DFS of the reverse graph would require reversing the graph's
// edges and be quite messy.
blockPtr = dfsListEnd;
while (blockPtr != dfsList) {
BasicBlock *block = *--blockPtr;
// For each live block propagate retReachable information backward from successors
// (including exceptional successors) that have changed in either this or the previous
// generation. Set the generation of any blocks that change as a result to the current
// generation so that they can be propagated further.
if (block->getVerificationEnvIn().live()) {
BasicBlock **succPtr = block->successorsBegin;
BasicBlock **succEnd = block->handlersEnd;
BytecodeBlock *sub = 0;
if (block->hasKind(BasicBlock::bbBytecode) && static_cast<BytecodeBlock *>(block)->hasSubkind(BytecodeBlock::skJsr))
sub = &block->getSuccessor(0);
while (succPtr != succEnd) {
BasicBlock *succ = *succPtr++;
if (succ->getGeneration() >= oldGeneration)
if (block->getVerificationEnvIn().mergeRetReachables(succ->getVerificationEnvIn(), sub)) {
changed = true;
block->getGeneration() = generation;
}
sub = 0;
}
}
}
}
}
struct DuplicateHelper
{
typedef BasicBlock *Successor;
typedef BasicBlock *NodeRef;
const Uint32 generation; // currentGeneration from BytecodeVerifier::duplicateSubroutines
explicit DuplicateHelper(Uint32 generation): generation(generation) {}
static Successor *getSuccessorsBegin(NodeRef n) {return n->successorsBegin;}
static Successor *getSuccessorsEnd(NodeRef n) {return n->handlersEnd;}
static NodeRef getNodeRef(Successor s) {return s;}
};
struct DuplicatePass1Helper: DuplicateHelper
{
BytecodeGraph &bytecodeGraph; // The BytecodeGraph which we're examining
BytecodeBlock *subroutine; // Header of the subroutine whose body we're marking
DuplicatePass1Helper(BytecodeGraph &bytecodeGraph, BytecodeBlock *subroutine, Uint32 generation):
DuplicateHelper(generation), bytecodeGraph(bytecodeGraph), subroutine(subroutine) {}
bool isMarked(NodeRef n) {return n->getGeneration() == generation || !n->getVerificationEnvIn().isRetReachable(subroutine);}
void setMarked(NodeRef n);
};
//
// Make a clone of block n and store a pointer to that clone in n's clone field.
// Make the clone's successors and handlers point to the same blocks to which n's
// successors and handlers point.
// Also set n's generation to the current generation.
//
void DuplicatePass1Helper::setMarked(BasicBlock *n)
{
assert(n->getGeneration() != generation);
n->getGeneration() = generation;
BasicBlock *clone;
Pool &bytecodeGraphPool = bytecodeGraph.bytecodeGraphPool;
if (n->hasKind(BasicBlock::bbBytecode))
clone = new(bytecodeGraphPool) BytecodeBlock(bytecodeGraph, *static_cast<BytecodeBlock *>(n));
else {
assert(n->hasKind(BasicBlock::bbCatch));
clone = new(bytecodeGraphPool) CatchBlock(bytecodeGraph, *static_cast<CatchBlock *>(n));
}
n->getClone() = clone;
}
struct DuplicatePass2Helper: DuplicateHelper, Function1<BytecodeBlock *, BytecodeBlock *>
{
SubroutineCallSiteList &subroutineCallSiteList; // Reference to BytecodeVerifier::subroutineCallSiteList
Pool &envPool; // Reference to BytecodeVerifier::envPool
DuplicatePass2Helper(SubroutineCallSiteList &subroutineCallSiteList, Pool &envPool, Uint32 generation):
DuplicateHelper(generation), subroutineCallSiteList(subroutineCallSiteList), envPool(envPool) {}
bool isMarked(NodeRef n) {return n->getGeneration() != generation;}
void setMarked(NodeRef n);
BytecodeBlock *operator()(BytecodeBlock *arg);
};
//
// This block has an already created clone saved in its clone field.
// For each of this block's successors and catch handlers s, if s points to
// a block in this subroutine (which we can tell by checking whether s's generation
// is greater than or equal to this DuplicatePass2Helper's generation), replace s
// by s's clone.
// If this block has subkind skJsr, update the subroutineCallSiteList to also include
// this block's clone, which is a newly created call site.
//
// Set this block's generation to this DuplicatePass2Helper's generation plus one to
// mark this block so that setMarked is not called on it again.
//
void DuplicatePass2Helper::setMarked(BasicBlock *n)
{
assert(n->getGeneration() == generation);
n->getGeneration() = generation + 1;
BasicBlock *const clone = n->getClone();
assert(clone);
// Fix up the VerificationTemps.
clone->initVerification(*n, *this);
// Fix up the successor and handler pointers.
BasicBlock **successor = clone->successorsBegin;
BasicBlock **limit = clone->handlersEnd;
while (successor != limit) {
BasicBlock *s = *successor;
assert(s);
if (s->getGeneration() >= generation)
*successor = s->getClone();
successor++;
}
if (clone->hasKind(BasicBlock::bbBytecode)) {
BytecodeBlock *const bytecodeClone = static_cast<BytecodeBlock *>(clone);
// Fix up the catchBlock pointer. If it's non-nil, it must point inside this subroutine.
CatchBlock *c = bytecodeClone->catchBlock;
if (c) {
assert(c->getGeneration() >= generation);
bytecodeClone->catchBlock = static_cast<CatchBlock *>(c->getClone());
}
// If clone has subkind skJsr, update the subroutineCallSiteList.
if (bytecodeClone->hasSubkind(BytecodeBlock::skJsr)) {
subroutineCallSiteList.addAssociation(bytecodeClone->getSuccessor(0), *bytecodeClone, envPool);
}
} else
assert(clone->hasKind(BasicBlock::bbCatch));
}
//
// If arg points inside the subroutine currently being duplicated, return its clone;
// if arg is nil or points outside that subroutine, return it unchanged.
//
BytecodeBlock *DuplicatePass2Helper::operator()(BytecodeBlock *arg)
{
if (arg && arg->getGeneration() >= generation)
arg = static_cast<BytecodeBlock *>(arg->getClone());
return arg;
}
//
// Duplicate each subroutine called by jsr's inside skJsr-kind (not skJsrNoRet-kind) blocks.
// This function relies on the bytecode graph being set up by computeRetReachables.
// Furthermore, no blocks should have been added to the graph since its last DFS search.
// On exit the graph will contain no unforwarded ret's and every jsr will be inside a
// skJsrNoRet-kind block.
//
// This function uses the generation and clone fields of bytecode graph blocks as temporaries.
//
void BytecodeVerifier::duplicateSubroutines()
{
Pool searchPool;
Uint32 nGraphBlocks = bytecodeGraph.getDFSListLength(); // Upper bound on the current number of blocks in the graph
SearchStackEntry<BasicBlock *> *searchStack = 0;
Uint32 searchStackSize = 0;
BasicBlock **block = bytecodeGraph.getDFSList();
BasicBlock **dfsListEnd = block + nGraphBlocks;
while (block != dfsListEnd)
(*block++)->getGeneration() = 0;
Uint32 currentGeneration = 2;
BytecodeBlock *callSite;
BytecodeBlock *subroutine;
bool onlyOneCallSite;
while ((callSite = subroutineCallSiteList.popAssociation(subroutine, onlyOneCallSite)) != 0) {
BytecodeBlock *subroutineRet = subroutine->getSubroutineRet();
assert(subroutineRet && callSite->hasSubkind(BytecodeBlock::skJsr));
if (!onlyOneCallSite) {
// There is more than one jsr remaining to this subroutine, so we copy the entire
// subroutine.
if (nGraphBlocks > searchStackSize) {
searchStackSize = nGraphBlocks * 2;
searchStack = new(searchPool) SearchStackEntry<BasicBlock *>[searchStackSize];
}
{
// Make a clone of every BasicBlock in the subroutine and store a pointer to that
// clone in the original BasicBlock's clone field. The clones' successors will still
// point to the original blocks; we'll take care of that later.
// To recognize which blocks we've cloned already we set the generation field of
// every visited block in the subroutine to currentGeneration.
DuplicatePass1Helper pass1Helper(bytecodeGraph, subroutine, currentGeneration);
graphSimpleSearch(pass1Helper, static_cast<BasicBlock *>(subroutine), searchStackSize, searchStack);
}
// Now change all successor and catch block pointers in the cloned blocks point to
// the clones of their successors/catch blocks if there are any. Leave pointers to
// blocks outside the subroutine as they are. Change the generation of each block
// whose clone we transform this way to currentGeneration+1 to mark it so we don't
// transform it twice.
DuplicatePass2Helper pass2Helper(subroutineCallSiteList, envPool, currentGeneration);
graphSimpleSearch(pass2Helper, static_cast<BasicBlock *>(subroutine), searchStackSize, searchStack);
// Change the subroutine pointer of our call site.
assert(subroutine->getGeneration() >= currentGeneration && callSite->nSuccessors() == 2);
callSite->successorsBegin[0] = subroutine->getClone();
// Set up subroutineRet to point to the clone of the original ret.
assert(subroutineRet->getGeneration() >= currentGeneration);
subroutineRet = static_cast<BytecodeBlock *>(subroutineRet->getClone());
// Advance the generation.
currentGeneration += 2;
// Throw an error if we went through 2^32 generations so far (unlikely, but it's better to be safe!).
if (currentGeneration < 2)
verifyError(VerifyError::jsrNestingError);
}
// At this point we've already copied the subroutine if needed. Now we
// just forward its ret to the successor of the jsr.
subroutineRet->transformToForward(callSite->transformToJsrNoRet());
}
}
//
// Inline all jsr-ret subroutines in this BytecodeGraph, eliminating all
// jsr and ret bytecodes. depthFirstSearch should have been called on entry
// to this function, and it needs to be called again after this function exits.
//
void BytecodeVerifier::inlineSubroutines(BytecodeGraph &bytecodeGraph, Pool &tempPool)
{
BytecodeVerifier verifier(bytecodeGraph, tempPool);
verifier.computeDataflow();
verifier.computeRetReachables();
verifier.duplicateSubroutines();
bytecodeGraph.invalidateDFSList();
}