mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-07 12:15:51 +00:00
521 lines
16 KiB
C++
521 lines
16 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 "BytecodeGraph.h"
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// TranslationCommonEnv
|
|
|
|
|
|
//
|
|
// Create a new, empty phi node in the block's first control node.
|
|
// nExpectedInputs is the estimated number of inputs that the phi node will
|
|
// eventually have; however, there is no guarantee that the actual number of
|
|
// inputs won't be higher or lower.
|
|
// This method disengages the phi nodes in the block's first control node
|
|
// and must be balanced by a call to finishPhiNode.
|
|
//
|
|
PhiNode *TranslationCommonEnv::genEmptyPhiNode(const BasicBlock &block, ValueKind kind, Uint32 nExpectedInputs) const
|
|
{
|
|
PhiNode *phiNode = new(primitivePool) PhiNode(nExpectedInputs, kind, primitivePool);
|
|
ControlNode *cn = block.getFirstControlNode();
|
|
assert(cn);
|
|
cn->disengagePhis();
|
|
cn->addPhiNode(*phiNode);
|
|
return phiNode;
|
|
}
|
|
|
|
|
|
//
|
|
// Balance a call to genEmptyPhiNode. The caller should have set up the
|
|
// (formerly) empty phi node's inputs by now.
|
|
//
|
|
inline void TranslationCommonEnv::finishPhiNode(const BasicBlock &block) const
|
|
{
|
|
ControlNode *cn = block.getFirstControlNode();
|
|
assert(cn);
|
|
cn->reengagePhis();
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// TranslationBinding
|
|
|
|
|
|
//
|
|
// Create the real constant primitive node for this Constant. Allocate the
|
|
// primitive node out of the given pool.
|
|
//
|
|
void TranslationBinding::Constant::genRealNode(Pool &primitivePool, Uint32 bci)
|
|
{
|
|
assert(!producerExists);
|
|
|
|
PrimConst *primConst = new(primitivePool) PrimConst(kind, value, bci);
|
|
placeForDataNode->appendPrimitive(*primConst);
|
|
producerExists = true;
|
|
dataNode = primConst;
|
|
}
|
|
|
|
|
|
//
|
|
// Create a new PhantomPhi record with room for size arguments. Ouf of these,
|
|
// set the first nInitialArgs (which may be zero) to initialArgs, and set the
|
|
// next argument to arg. Clearly, nInitialArgs must be less than size.
|
|
// container is the BasicBlock in which this phantom phi node is defined.
|
|
// If alwaysNonzero is true, the value of the PhantomPhi is known to never be
|
|
// zero.
|
|
// Allocate needed storage from pool.
|
|
//
|
|
TranslationBinding::PhantomPhi::PhantomPhi(ValueKind kind, Uint32 size, Uint32 nInitialArgs, const TranslationBinding &initialArgs,
|
|
const TranslationBinding &arg, const BasicBlock &container, bool alwaysNonzero, Pool &pool):
|
|
container(container),
|
|
nArgs(nInitialArgs + 1),
|
|
realPhiExists(false),
|
|
kind(kind),
|
|
alwaysNonzero(alwaysNonzero)
|
|
{
|
|
TranslationBinding *a = new(pool) TranslationBinding[size];
|
|
args = a;
|
|
while (nInitialArgs--)
|
|
*a++ = initialArgs;
|
|
*a = arg;
|
|
}
|
|
|
|
|
|
//
|
|
// Return true if the value of this PhantomPhi is known to never be zero.
|
|
//
|
|
bool TranslationBinding::PhantomPhi::isAlwaysNonzero() const
|
|
{
|
|
return realPhiExists ? realPhi->isAlwaysNonzero() : alwaysNonzero;
|
|
}
|
|
|
|
|
|
//
|
|
// Inform this PhantomPhi about whether its value is never zero.
|
|
//
|
|
void TranslationBinding::PhantomPhi::setAlwaysNonzero(bool nz)
|
|
{
|
|
if (realPhiExists)
|
|
realPhi->setAlwaysNonzero(nz);
|
|
else
|
|
alwaysNonzero = nz;
|
|
}
|
|
|
|
|
|
//
|
|
// Append a new argument arg to this PhantomPhi's array. If expand is true,
|
|
// the array first needs to be physically expanded to size newSize (which is
|
|
// guaranteed to hold all of its elements).
|
|
//
|
|
void TranslationBinding::PhantomPhi::append(bool expand, Uint32 newSize,
|
|
const TranslationBinding &arg,
|
|
Uint32 bci)
|
|
{
|
|
if (realPhiExists) {
|
|
Pool &primitivePool = container.getTranslationEnvIn().getCommonEnv().primitivePool;
|
|
realPhi->addInput(arg.extract(primitivePool, bci), primitivePool);
|
|
} else {
|
|
Uint32 n = nArgs;
|
|
if (expand) {
|
|
TranslationBinding *newArgs = new(container.getTranslationEnvIn().envPool()) TranslationBinding[newSize];
|
|
copy(args, args + n, newArgs);
|
|
args = newArgs;
|
|
assert(newSize > n);
|
|
}
|
|
args[n] = arg;
|
|
nArgs = n + 1;
|
|
if (alwaysNonzero)
|
|
alwaysNonzero = arg.isAlwaysNonzero();
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Create the real phi node for this PhantomPhi.
|
|
//
|
|
void TranslationBinding::PhantomPhi::genRealPhi(Uint32 bci)
|
|
{
|
|
assert(!realPhiExists);
|
|
TranslationBinding *a = args;
|
|
const TranslationEnv &env = container.getTranslationEnvIn();
|
|
const TranslationCommonEnv &commonEnv = env.getCommonEnv();
|
|
Pool &primitivePool = commonEnv.primitivePool;
|
|
Uint32 n = getNArgs();
|
|
|
|
// Carefully create a new phi node before calling extract on the arguments
|
|
// because one of the arguments could refer back to this phi node.
|
|
realPhi = commonEnv.genEmptyPhiNode(container, kind, env.getPhiSize());
|
|
realPhiExists = true;
|
|
while (n--)
|
|
realPhi->addInput(a++->extract(primitivePool, bci), primitivePool);
|
|
commonEnv.finishPhiNode(container);
|
|
}
|
|
|
|
|
|
//
|
|
// Return the kind of value represented by this binding.
|
|
//
|
|
ValueKind TranslationBinding::getKind() const
|
|
{
|
|
switch (category) {
|
|
case tbConstant:
|
|
return constant->kind;
|
|
case tbDataEdge:
|
|
return dataNode->getKind();
|
|
case tbPhantomPhi:
|
|
case tbConstantPhi:
|
|
return phantomPhi->kind;
|
|
default:
|
|
return vkVoid;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Return true if the value of this binding is known to never be zero.
|
|
//
|
|
bool TranslationBinding::isAlwaysNonzero() const
|
|
{
|
|
switch (category) {
|
|
case tbConstant:
|
|
return constant->isNonzero();
|
|
case tbDataEdge:
|
|
return dataNode->isAlwaysNonzero();
|
|
case tbPhantomPhi:
|
|
case tbConstantPhi:
|
|
return phantomPhi->isAlwaysNonzero();
|
|
default:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Bind this TranslationBinding to represent the given int constant.
|
|
// If a primitive to explicitly generate the constant is needed, it will
|
|
// be placed in the given ControlNode.
|
|
// Allocate needed storage from pool.
|
|
//
|
|
void TranslationBinding::defineInt(Int32 v, ControlNode *placeForDataNode, Pool &pool)
|
|
{
|
|
category = tbConstant;
|
|
Constant *c = new(pool) Constant(vkInt, placeForDataNode);
|
|
c->value.i = v;
|
|
constant = c;
|
|
}
|
|
|
|
|
|
//
|
|
// Bind this TranslationBinding to represent the first word of the given long constant.
|
|
// If a primitive to explicitly generate the constant is needed, it will
|
|
// be placed in the given ControlNode.
|
|
// Allocate needed storage from pool.
|
|
//
|
|
void TranslationBinding::defineLong(Int64 v, ControlNode *placeForDataNode, Pool &pool)
|
|
{
|
|
category = tbConstant;
|
|
Constant *c = new(pool) Constant(vkLong, placeForDataNode);
|
|
c->value.l = v;
|
|
constant = c;
|
|
}
|
|
|
|
|
|
//
|
|
// Bind this TranslationBinding to represent the given float constant.
|
|
// If a primitive to explicitly generate the constant is needed, it will
|
|
// be placed in the given ControlNode.
|
|
// Allocate needed storage from pool.
|
|
//
|
|
void TranslationBinding::defineFloat(Flt32 v, ControlNode *placeForDataNode, Pool &pool)
|
|
{
|
|
category = tbConstant;
|
|
Constant *c = new(pool) Constant(vkFloat, placeForDataNode);
|
|
c->value.f = v;
|
|
constant = c;
|
|
}
|
|
|
|
|
|
//
|
|
// Bind this TranslationBinding to represent the first word of the given double constant.
|
|
// If a primitive to explicitly generate the constant is needed, it will
|
|
// be placed in the given ControlNode.
|
|
// Allocate needed storage from pool.
|
|
//
|
|
void TranslationBinding::defineDouble(Flt64 v, ControlNode *placeForDataNode, Pool &pool)
|
|
{
|
|
category = tbConstant;
|
|
Constant *c = new(pool) Constant(vkDouble, placeForDataNode);
|
|
c->value.d = v;
|
|
constant = c;
|
|
}
|
|
|
|
|
|
//
|
|
// Bind this TranslationBinding to represent the given pointer constant.
|
|
// If a primitive to explicitly generate the constant is needed, it will
|
|
// be placed in the given ControlNode.
|
|
// Allocate needed storage from pool.
|
|
//
|
|
void TranslationBinding::definePtr(addr v, ControlNode *placeForDataNode, Pool &pool)
|
|
{
|
|
category = tbConstant;
|
|
Constant *c = new(pool) Constant(vkAddr, placeForDataNode);
|
|
c->value.a = v;
|
|
constant = c;
|
|
}
|
|
|
|
|
|
//
|
|
// Bind this TranslationBinding to represent the constant (or first word of the constant
|
|
// if it takes two words).
|
|
// If a primitive to explicitly generate the constant is needed, it will
|
|
// be placed in the given ControlNode.
|
|
// Allocate needed environment storage from pool.
|
|
//
|
|
void TranslationBinding::define(ValueKind kind, const Value &v, ControlNode *placeForDataNode, Pool &pool)
|
|
{
|
|
category = tbConstant;
|
|
Constant *c = new(pool) Constant(kind, placeForDataNode);
|
|
c->value = v;
|
|
constant = c;
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// TranslationEnv
|
|
|
|
|
|
#ifdef DEBUG
|
|
//
|
|
// Assert that none of the PhantomPhi or ConstantPhi nodes referring to the current container
|
|
// are shared.
|
|
//
|
|
void TranslationEnv::assertNoSharedPhantomPhis(const BasicBlock &container)
|
|
{
|
|
TranslationBinding *dst = bindingsBegin();
|
|
TranslationBinding *dstEnd = bindingsEnd();
|
|
for (; dst != dstEnd; dst++)
|
|
if (dst->category == TranslationBinding::tbPhantomPhi || dst->category == TranslationBinding::tbConstantPhi)
|
|
dst->phantomPhi->duplicate = false;
|
|
for (dst = bindingsBegin(); dst != dstEnd; dst++)
|
|
if (dst->category == TranslationBinding::tbPhantomPhi || dst->category == TranslationBinding::tbConstantPhi) {
|
|
TranslationBinding::PhantomPhi *phi = dst->phantomPhi;
|
|
assert(&phi->getContainer() != &container || !phi->duplicate);
|
|
phi->duplicate = true;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
//
|
|
// Intersect the given TranslationEnv (which must have been initialized)
|
|
// into this TranslationEnv, which must also be initialized.
|
|
// This environment must be part of the given container.
|
|
// If memoryOnly is true, only intersect the memory binding.
|
|
//
|
|
void TranslationEnv::meet(const TranslationEnv &env, bool memoryOnly, const BasicBlock &container)
|
|
{
|
|
#ifdef DEBUG
|
|
assert(this == &container.getTranslationEnvIn());
|
|
assert(compatible(env));
|
|
assertNoSharedPhantomPhis(container);
|
|
#endif
|
|
TranslationBinding *dst = bindingsBegin();
|
|
TranslationBinding *dstEnd = memoryOnly ? bindingsMemoryEnd() : bindingsEnd();
|
|
const TranslationBinding *src = env.bindingsBegin();
|
|
Pool &pool = commonEnv.envPool;
|
|
Uint32 oldPhiOffset = phiOffset;
|
|
Uint32 newPhiSize = phiSize;
|
|
bool expandThis = false;
|
|
if (oldPhiOffset >= newPhiSize) {
|
|
newPhiSize = oldPhiOffset*2 + 1;
|
|
expandThis = true;
|
|
}
|
|
|
|
while (dst != dstEnd) {
|
|
TranslationBinding::Category cDst = dst->category;
|
|
TranslationBinding::Category cSrc = src->category;
|
|
ValueKind kind;
|
|
|
|
if (cSrc == TranslationBinding::tbNone) {
|
|
assert(!anticipated || cDst == TranslationBinding::tbNone);
|
|
dst->clear();
|
|
} else
|
|
switch (cDst) {
|
|
|
|
case TranslationBinding::tbNone:
|
|
break;
|
|
|
|
case TranslationBinding::tbSecondWord:
|
|
if (cSrc != TranslationBinding::tbSecondWord) {
|
|
assert(!anticipated);
|
|
dst->clear();
|
|
}
|
|
break;
|
|
|
|
case TranslationBinding::tbConstantPhi:
|
|
{
|
|
TranslationBinding::ConstantPhi *phi = dst->constantPhi;
|
|
kind = phi->kind;
|
|
if (src->getKind() != kind) {
|
|
assert(!anticipated);
|
|
dst->clear();
|
|
break;
|
|
}
|
|
const TranslationBinding::Constant *srcConstant = src->constantValue();
|
|
bool constantMatches = srcConstant && *phi->constant == *srcConstant;
|
|
|
|
if (&phi->getContainer() == &container)
|
|
if (constantMatches)
|
|
// asharma - fix this!
|
|
phi->append(expandThis, newPhiSize, *src, 0);
|
|
else {
|
|
// The constant doesn't match, so turn this into a regular phantom phi node.
|
|
assert(!anticipated);
|
|
dst->category = TranslationBinding::tbPhantomPhi;
|
|
goto phantomPhi;
|
|
}
|
|
else
|
|
// Don't merge if both source and destination are the same and they don't refer to
|
|
// this container.
|
|
if (cSrc != TranslationBinding::tbConstantPhi || src->constantPhi != phi)
|
|
if (constantMatches) {
|
|
assert(!anticipated);
|
|
dst->define(new(pool) TranslationBinding::ConstantPhi(kind, newPhiSize, oldPhiOffset,
|
|
*dst, *src, container, srcConstant, pool));
|
|
} else
|
|
goto createPhi;
|
|
}
|
|
break;
|
|
|
|
case TranslationBinding::tbPhantomPhi:
|
|
if (src->getKind() != dst->phantomPhi->kind) {
|
|
assert(!anticipated);
|
|
dst->clear();
|
|
break;
|
|
}
|
|
phantomPhi:
|
|
{
|
|
TranslationBinding::PhantomPhi *phi = dst->phantomPhi;
|
|
|
|
if (&phi->getContainer() == &container) {
|
|
// Use existing phantom phi node in this node
|
|
assert(!phi->isAlwaysNonzero() || src->isAlwaysNonzero() || !anticipated);
|
|
// asharma - fix this!
|
|
phi->append(expandThis, newPhiSize, *src, 0);
|
|
break;
|
|
}
|
|
|
|
// Don't merge if both source and destination are the same and they don't refer to
|
|
// this container.
|
|
if (cSrc != TranslationBinding::tbPhantomPhi || src->phantomPhi != phi) {
|
|
kind = phi->kind;
|
|
goto createPhi;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case TranslationBinding::tbDataEdge:
|
|
if (cSrc != TranslationBinding::tbDataEdge || src->dataNode != dst->dataNode) {
|
|
kind = dst->dataNode->getKind();
|
|
if (src->getKind() != kind) {
|
|
assert(!anticipated);
|
|
dst->clear();
|
|
break;
|
|
}
|
|
createPhi:
|
|
assert(!anticipated);
|
|
dst->define(new(pool) TranslationBinding::PhantomPhi(kind, newPhiSize, oldPhiOffset, *dst, *src, container,
|
|
dst->isAlwaysNonzero() && src->isAlwaysNonzero(), pool));
|
|
}
|
|
break;
|
|
|
|
case TranslationBinding::tbConstant:
|
|
if (cSrc != TranslationBinding::tbConstant || src->constant != dst->constant) {
|
|
kind = dst->constant->kind;
|
|
if (src->getKind() != kind) {
|
|
assert(!anticipated);
|
|
dst->clear();
|
|
break;
|
|
}
|
|
const TranslationBinding::Constant *srcConstant = src->constantValue();
|
|
if (srcConstant && *dst->constant == *srcConstant) {
|
|
dst->define(new(pool) TranslationBinding::ConstantPhi(kind, newPhiSize, oldPhiOffset,
|
|
*dst, *src, container, srcConstant, pool));
|
|
break;
|
|
}
|
|
goto createPhi;
|
|
}
|
|
}
|
|
src++;
|
|
dst++;
|
|
}
|
|
phiOffset = oldPhiOffset + 1;
|
|
phiSize = newPhiSize;
|
|
}
|
|
|
|
|
|
//
|
|
// Add phantom phi nodes to all variables in this local environment because we'd like
|
|
// to use this environment without knowing all of its predecessors yet.
|
|
// This environment must be part of the given container.
|
|
//
|
|
void TranslationEnv::anticipate(const BasicBlock &container)
|
|
{
|
|
#ifdef DEBUG
|
|
assert(this == &container.getTranslationEnvIn());
|
|
assertNoSharedPhantomPhis(container);
|
|
assert(!anticipated);
|
|
anticipated = true;
|
|
#endif
|
|
TranslationBinding *dst = bindingsBegin();
|
|
TranslationBinding *dstEnd = bindingsEnd();
|
|
Pool &pool = commonEnv.envPool;
|
|
|
|
while (dst != dstEnd) {
|
|
switch (dst->category) {
|
|
case TranslationBinding::tbNone:
|
|
case TranslationBinding::tbSecondWord:
|
|
break;
|
|
case TranslationBinding::tbConstantPhi:
|
|
if (&dst->constantPhi->getContainer() != &container)
|
|
goto createPhi;
|
|
dst->constantPhi->setAlwaysNonzero(false);
|
|
dst->category = TranslationBinding::tbPhantomPhi;
|
|
break;
|
|
case TranslationBinding::tbPhantomPhi:
|
|
if (&dst->phantomPhi->getContainer() == &container)
|
|
break; // We already have an appropriate phi node.
|
|
// Fall into default case because we need to create a new phi node; the current
|
|
// value was a phi node from another control node.
|
|
default:
|
|
createPhi:
|
|
assert(phiOffset > 0 && phiSize >= phiOffset);
|
|
dst->define(new(pool) TranslationBinding::PhantomPhi(dst->getKind(), phiSize, phiOffset-1,
|
|
*dst, *dst, container, false, pool));
|
|
break;
|
|
}
|
|
dst++;
|
|
}
|
|
}
|