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

863 lines
22 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 "Primitives.h"
#include "ControlNodes.h"
#include "SysCalls.h"
#include "FieldOrMethod.h"
#include "DebugUtils.h"
#include "PrimitiveOperations.cpp"
const DataNode VariableOrConstant::constSources[nValueKinds] =
{
vkVoid,
vkInt,
vkLong,
vkFloat,
vkDouble,
vkAddr,
vkCond,
vkMemory,
vkTuple
};
// ----------------------------------------------------------------------------
// VariableOrConstant
//
// Return true if the given VariableOrConstant is equal to this VariableOrConstant.
// Two VariableOrConstants are equal if either they are equal constants or
// the same Producer.
//
bool VariableOrConstant::operator==(const VariableOrConstant &poc2) const
{
return source == poc2.source && (isVariable() || value.eq(getKind(), poc2.value));
}
//
// Return true if the VariableOrConstant is guaranteed to be nonzero.
//
bool VariableOrConstant::isAlwaysNonzero() const
{
return isConstant() ? value.isNonzero(source->getKind()) : getVariable().isAlwaysNonzero();
}
// ----------------------------------------------------------------------------
// DataConsumer
//
// Set this DataConsumer to the variable or constant contained inside p.
// This DataConsumer should not have been previously initialized.
//
void DataConsumer::operator=(const VariableOrConstant &p)
{
assert(!(source && source->isVariable()));
if (p.isConstant())
setConstant(p.getKind(), p.getConstant());
else
setVariable(p.getVariable());
}
//
// Destructively move the src DataConsumer (which must have been initialized)
// to this DataConsumer, which must be uninitialized. The src DataConsumer is
// unlinked from any linked lists to which it belonged and left uninitialized.
//
void DataConsumer::move(DataConsumer &src)
{
assert(!(source && source->isVariable()));
source = src.source;
node = src.node;
#ifdef DEBUG
src.source = 0;
src.node = 0;
if (isVariable())
links.init();
#endif
if (isConstant())
value = src.value;
else
links.substitute(src.links);
}
#ifdef DEBUG_LOG
//
// Print a reference to this DataConsumer for debugging purposes.
// Return the number of characters printed.
//
int DataConsumer::printRef(LogModuleObject &f) const
{
if (isConstant())
return getConstant().print(f, getKind());
else
return getVariable().printRef(f);
}
#endif
// ----------------------------------------------------------------------------
// DataNode
#ifdef DEBUG_LOG
//
// Print a reference to this DataNode for debugging purposes.
// Return the number of characters printed.
//
int DataNode::printRef(LogModuleObject &f) const
{
int a = UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("%c%c", constant ? 'c' : isAlwaysNonzero() ? 'w' : 'v', valueKindShortName(kind)));
if (producerNumber != (Uint32)-1)
return a + UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("%u", producerNumber));
else
return a + UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("%p", this));
}
#endif
// ----------------------------------------------------------------------------
Uint16 gCurLineNumber = 0;
const PrimitiveFormat categoryFormats[nPrimitiveCategories] =
{
pfNone, // pcNone
pfPhi, // pcPhi
pfConst, // pcConst
pfDebug, // pcDebug
pfControl, // pcIfCond
pfControl, // pcSwitch
pfCatch, // pcCatch
pfProj, // pcProj
pfArg, // pcArg
pfResult, // pcResult
pfBinaryGeneric, // pcGeneric
pfBinaryGeneric, // pcDivMod
pfBinaryGeneric, // pcShift
pfBinaryGeneric, // pcExt
pfBinaryGeneric, // pcFGeneric
pfUnaryGeneric, // pcConv
pfUnaryGeneric, // pcFConv
pfBinaryGeneric, // pcCmp
pfBinaryGeneric, // pcFCmp
pfUnaryGeneric, // pcCond
pfUnaryGeneric, // pcCond3
pfUnaryGeneric, // pcCheck1
pfBinaryGeneric, // pcCheck2
pfLd, // pcLd
pfSt, // pcSt
pfMonitor, // pcMonitor
pfSync, // pcSync
pfSysCall, // pcSysCall
pfCall // pcCall
};
#ifdef DEBUG_LOG
static const char primitiveFormatNames[nPrimitiveFormats][16] =
{
"None", // pfNone
"Phi", // pfPhi
"Const", // pfConst
"Debug", // pfDebug
"Control", // pfControl
"Catch", // pfCatch
"Proj", // pfProj
"Arg", // pfArg
"Result", // pfResult
"UnaryGeneric", // pfUnaryGeneric
"BinaryGeneric", // pfBinaryGeneric
"Ld", // pfLd
"St", // pfSt
"Monitor", // pfMonitor
"Sync", // pfSync
"SysCall", // pfSysCall
"Call" // pfCall
};
static const char primitiveCategoryNames[nPrimitiveCategories][16] =
{
"None", // pcNone
"Phi", // pcPhi
"Const", // pcConst
"Debug", // pcDebug
"IfCond", // pcIfCond
"Switch", // pcSwitch
"Catch", // pcCatch
"Proj", // pcProj
"Arg", // pcArg
"Result", // pcResult
"Generic", // pcGeneric
"DivMod", // pcDivMod
"Shift", // pcShift
"Ext", // pcExt
"FGeneric", // pcFGeneric
"Conv", // pcConv
"FConv", // pcFConv
"Cmp", // pcCmp
"FCmp", // pcFCmp
"Cond", // pcCond
"Cond3", // pcCond3
"Check1", // pcCheck1
"Check2", // pcCheck2
"Ld", // pcLd
"St", // pcSt
"Monitor", // pcMonitor
"Sync", // pcSync
"SysCall", // pcSysCall
"Call" // pcCall
};
//
// Print the name of the PrimitiveFormat onto the output stream f.
// Return the number of characters printed.
//
int print(LogModuleObject &f, PrimitiveFormat pf)
{
return UT_OBJECTLOG(f, PR_LOG_ALWAYS, ((uint)pf >= nPrimitiveFormats ? "????" : primitiveFormatNames[pf]));
}
//
// Print the name of the PrimitiveCategory onto the output stream f.
// Return the number of characters printed.
//
int print(LogModuleObject &f, PrimitiveCategory pc)
{
return UT_OBJECTLOG(f, PR_LOG_ALWAYS, ((uint)pc >= nPrimitiveCategories ? "????" : primitiveCategoryNames[pc]));
}
//
// Print the name of the PrimitiveOperation onto the output stream f.
// Return the number of characters printed.
//
int print(LogModuleObject &f, PrimitiveOperation pk)
{
return UT_OBJECTLOG(f, PR_LOG_ALWAYS, ((uint)pk >= nPrimitiveOperations ? "????" : primitiveOperationNames[pk]));
}
#endif
//
// If the comparison always returns a constant, return nil and store the (true or
// false) result of the comparison in result. Otherwise, return the DataNode
// obtained from c.
//
DataNode *partialComputeComparison(Condition2 cond2, VariableOrConstant c, bool &result)
{
assert(c.hasKind(vkCond));
if (c.isConstant()) {
result = applyCondition(cond2, c.getConstant().c);
return 0;
}
// Optimize the cases in which we know that cond cannot be cEq.
DataNode &cp = c.getVariable();
if (cp.isAlwaysNonzero())
switch (cond2) {
case condEq:
result = false;
return 0;
case condNe:
result = true;
return 0;
default:;
}
return &cp;
}
// ----------------------------------------------------------------------------
// DataNode
//
// Destroy this DataNode, which must have been initialized. This DataNode's
// consumers are destroyed as well.
// Don't call this directly; call PhiNode::destroy or Primitive::destroy instead.
//
void DataNode::destroy()
{
DataConsumer *begin = inputsBegin;
DataConsumer *end = inputsEnd;
while (begin != end)
begin++->destroy();
#ifdef DEBUG
kind = vkVoid;
constant = false;
operation = poNone;
category = pcNone;
alwaysNonzero = false;
flags = templates[poNone].flags;
container = 0;
inputsBegin = 0;
inputsEnd = (DataConsumer *)(-1);
#endif
}
//
// Returns true if data node is a signed compare, false if unsigned compare
// NB must be a compare (checked by assertion)
// (Used by x86 emitter)
//
bool DataNode::isSignedCompare() const
{
bool result;
switch(getOperation())
{
case poCmp_I: // comparison is signed
result = true;
break;
case poCmpU_I: // comparison is unsigned
case poCmpU_A:
result = false;
break;
default:
assert(false);
}
return result;
}
//
// Assign a unique producerNumber to this DataNode if it produces a value.
// The first number assigned will be base; return the last number assigned + 1.
//
Uint32 DataNode::assignProducerNumbers(Uint32 base)
{
if (producesVariable())
producerNumber = base++;
return base;
}
#ifdef DEBUG_LOG
//
// Print the outgoing edge, if any, of a DataNode along with the := sign.
//
void DataNode::printOutgoing(LogModuleObject &f) const
{
if (producesVariable()) {
printRef(f);
UT_OBJECTLOG(f, PR_LOG_ALWAYS, (" := "));
}
}
//
// Print the incoming edges of a DataNode preceded by some white space.
//
void DataNode::printIncoming(LogModuleObject &f) const
{
if (inputsBegin != inputsEnd) {
UT_OBJECTLOG(f, PR_LOG_ALWAYS, (" "));
bool printSeparator = false;
for (DataConsumer *dc = inputsBegin; dc != inputsEnd; dc++) {
if (printSeparator)
UT_OBJECTLOG(f, PR_LOG_ALWAYS, (", "));
printSeparator = true;
assert(&dc->getNode() == this);
dc->printRef(f);
}
}
}
//
// Print a DataNode for debugging purposes.
// f should be at the beginning of a line.
//
void DataNode::printPretty(LogModuleObject &f, int margin) const
{
printMargin(f, margin);
ControlNode *c = getContainer();
if (c && c->hasControlKind() && hasTryPrimitive(c->getControlKind()))
if (this == c->getTryExtra().tryPrimitive)
UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("Exc "));
printOutgoing(f);
print(f, getOperation());
printIncoming(f);
UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("\n"));
}
#endif
// ----------------------------------------------------------------------------
// PhiNode
//
// Construct a new phi node. nExpectedInputs is an estimate of the number
// of incoming edges that the phi node will have. The estimate does not have to
// be accurate, but performance will suffer if it is too low.
// The phi node starts with no incoming edges. The caller must explicitly add
// each incoming edge in the same order as the order in which the predecessors of
// the PhiNode's container are listed.
//
PhiNode::PhiNode(Uint32 nExpectedInputs, ValueKind kind, Pool &pool):
DataNode((PrimitiveOperation)(poPhi_I + kind - vkInt))
{
assert(hasFormat(pfPhi));
DataConsumer *edges = new(pool) DataConsumer[nExpectedInputs];
inputsBegin = edges;
inputsEnd = edges;
inputsLimit = edges + nExpectedInputs;
}
//
// Destroy this PhiNode, which must have been initialized. This PhiNode's
// consumers are destroyed and the PhiNode is removed from its container.
//
void PhiNode::destroy()
{
remove();
DataNode::destroy();
}
//
// Add a new input to the phi node. The inputs must be added in the same
// order as the order of the predecessors of the PhiNode's container.
// Allocate edges from the given pool.
//
void PhiNode::addInput(DataNode &producer, Pool &pool)
{
DataConsumer *input = inputsEnd;
if (input == inputsLimit) {
Uint32 newNEdges = (input - inputsBegin) * 2;
if (newNEdges < 4)
newNEdges = 4;
DataConsumer *newInputsBegin = new(pool) DataConsumer[newNEdges];
input = move(inputsBegin, input, newInputsBegin);
inputsBegin = newInputsBegin;
inputsLimit = newInputsBegin + newNEdges;
}
input->setNode(*this);
input->setVariable(producer);
inputsEnd = input + 1;
}
//
// Call f once on each of this phi node's inputs in order. Remove from this phi
// node the inputs for which f returns true. This phi node remains a phi node
// (is not optimized out) even if all or all but one of its inputs are removed.
// The function f also gets each input's corresponding predecessor.
//
// CAUTION: Function f should not cache the DataConsumer reference it obtains
// because that DataConsumer can be destroyed. If it wants to cache the DataConsumer,
// it should make a copy.
//
void PhiNode::removeSomeInputs(Function2<bool, DataConsumer &, ControlEdge &> &f)
{
const DoublyLinkedList<ControlEdge> &predecessors = getContainer()->getPredecessors();
assert(predecessors.lengthIs(nInputs()));
DoublyLinkedList<ControlEdge>::iterator i = predecessors.begin();
DataConsumer *srcInput = inputsBegin;
DataConsumer *dstInput = srcInput;
DataConsumer *end = inputsEnd;
while (srcInput != end) {
if (f(*srcInput, predecessors.get(i)))
srcInput->destroy();
else {
if (srcInput != dstInput)
dstInput->move(*srcInput);
dstInput++;
}
srcInput++;
i = predecessors.advance(i);
}
inputsEnd = dstInput;
}
// ----------------------------------------------------------------------------
// Primitive
//
// Destroy this Primitive, which must have been initialized. This Primitive's
// consumers are destroyed and the Primitive is removed from its container.
//
void Primitive::destroy()
{
remove();
DataNode::destroy();
}
// ----------------------------------------------------------------------------
// PrimConst
//
// Create a PrimConst with the given value.
//
PrimConst::PrimConst(ValueKind vk, const Value &value, Uint32 bci):
Primitive0((PrimitiveOperation)(poConst_I + vk - vkInt), bci),
value(value)
{
assert(isRegOrMemKind(vk) && hasFormat(pfConst));
setAlwaysNonzero(value.isNonzero(vk));
}
#ifdef DEBUG_LOG
void PrimConst::printIncoming(LogModuleObject &f) const
{
UT_OBJECTLOG(f, PR_LOG_ALWAYS, (" "));
value.print(f, getKind());
}
#endif
// ----------------------------------------------------------------------------
// PrimProj
//
// Create a PrimProj with the given operation.
// If alwaysNonzero is true, the output is known to never be zero.
// The caller then has to connect the input to the data node that produces the tuple.
//
PrimProj::PrimProj(PrimitiveOperation op, TupleComponent component, bool
alwaysNonzero, Uint32 bci):
Primitive1(op, bci),
component(component)
{
assert(hasFormat(pfProj));
setAlwaysNonzero(alwaysNonzero);
}
//
// Create a PrimProj with the given value kind, which must be a storable ValueKind.
// If alwaysNonzero is true, the output is known to never be zero.
// The caller then has to connect the input to the data node that produces the tuple.
//
PrimProj::PrimProj(ValueKind vk, TupleComponent component, bool
alwaysNonzero, Uint32 bci):
Primitive1((PrimitiveOperation)(poProj_I + vk - vkInt), bci),
component(component)
{
assert(isStorableKind(vk));
setAlwaysNonzero(alwaysNonzero);
}
#ifdef DEBUG_LOG
void PrimProj::printIncoming(LogModuleObject &f) const
{
Primitive1::printIncoming(f);
UT_OBJECTLOG(f, PR_LOG_ALWAYS, (", component%d", component));
}
#endif
// ----------------------------------------------------------------------------
// PrimArg
//
// Create a PrimArg. The caller must subsequently call initOutgoingEdge on it.
//
PrimArg::PrimArg(): Primitive0(poArg_M, 0) // initOutgoingEdge will change the operation as appropriate.
{
assert(hasFormat(pfArg));
}
//
// Set the PrimArg's kind and flag that states whether the PrimArg might
// ever be zero or null.
//
void PrimArg::initOutgoingEdge(ValueKind vk, bool alwaysNonzero)
{
assert(isStorableKind(vk) || isMemoryKind(vk));
setOperation((PrimitiveOperation)(poArg_I + vk - vkInt));
setAlwaysNonzero(alwaysNonzero);
}
#ifdef DEBUG_LOG
void PrimArg::printIncoming(LogModuleObject &f) const
{
UT_OBJECTLOG(f, PR_LOG_ALWAYS, (" arg"));
ControlNode *container = getContainer();
if (container && container->hasControlKind()) {
ControlNode::BeginExtra &beginExtra = container->getBeginExtra();
if (this == &beginExtra.initialMemory)
UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("MEM"));
else {
for (uint n = 0; n != beginExtra.nArguments; n++)
if (this == &beginExtra[n]) {
UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("%d", n));
return;
}
UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("??"));
}
}
}
#endif
// ----------------------------------------------------------------------------
// PrimResult
//
// Create a PrimResult.
//
PrimResult::PrimResult(Uint32 bci, ValueKind vk)
: Primitive1((PrimitiveOperation)(poResult_I + vk - vkInt), bci)
{
assert((isStorableKind(vk) || isMemoryKind(vk)) && hasFormat(pfResult));
}
#ifdef DEBUG_LOG
void PrimResult::printOutgoing(LogModuleObject &f) const
{
UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("res"));
ControlNode *container = getContainer();
if (container && container->hasControlKind())
if (container->hasControlKind(ckEnd) && this == &container->getEndExtra().finalMemory)
UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("MEM"));
else {
ControlNode::ReturnExtra &returnExtra = container->getReturnExtra();
uint n;
for (n = 0; n != returnExtra.nResults; n++)
if (this == &returnExtra[n]) {
UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("%d", n));
break;
}
if (n == returnExtra.nResults)
UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("??"));
}
UT_OBJECTLOG(f, PR_LOG_ALWAYS, (" := "));
}
#endif
// ----------------------------------------------------------------------------
// PrimControl
//
// Create a PrimControl node. op should be poSwitch or one of the poIf... kinds.
// The caller then has to connect the input to another data flow node.
//
PrimControl::PrimControl(PrimitiveOperation op, Uint32 bci)
: Primitive1(op, bci)
{
assert(hasFormat(pfControl));
}
// ----------------------------------------------------------------------------
// PrimCatch
//
// Create a PrimCatch.
//
PrimCatch::PrimCatch(Uint32 bci): Primitive0(poCatch, bci)
{
assert(hasFormat(pfCatch));
setAlwaysNonzero(true);
}
// ----------------------------------------------------------------------------
// PrimUnaryGeneric
//
// Create a generic one-argument, one-result primitive with the given PrimitiveOperation.
// If alwaysNonzero is true, the output is known to never be zero.
// The caller then has to connect the input to another data flow node.
//
PrimUnaryGeneric::PrimUnaryGeneric(PrimitiveOperation op, Uint32 bci, bool alwaysNonzero)
: Primitive1(op, bci)
{
assert(hasFormat(pfUnaryGeneric));
setAlwaysNonzero(alwaysNonzero);
}
// ----------------------------------------------------------------------------
// PrimBinaryGeneric
//
// Create a generic two-argument, one-result primitive with the given PrimitiveOperation.
// If alwaysNonzero is true, the output is known to never be zero.
// The caller then has to connect the inputs to other data flow nodes.
//
PrimBinaryGeneric::PrimBinaryGeneric(PrimitiveOperation op, Uint32 bci,
bool alwaysNonzero)
: Primitive2(op, bci)
{
assert(hasFormat(pfBinaryGeneric));
setAlwaysNonzero(alwaysNonzero);
}
// ----------------------------------------------------------------------------
// PrimLoad
//
// Create a PrimLoad with the given PrimitiveOperation.
// The caller then has to connect the inputs to other data flow nodes.
//
PrimLoad::PrimLoad(PrimitiveOperation op, bool hasOutgoingMemory, Uint32 bci)
: Primitive2(op, bci), hasOutgoingMemory(hasOutgoingMemory)
{
assert(hasFormat(pfLd));
}
// ----------------------------------------------------------------------------
// PrimStore
//
// Create a PrimStore with the given PrimitiveOperation.
// The caller then has to connect the inputs to other data flow nodes.
//
PrimStore::PrimStore(PrimitiveOperation op, Uint32 bci) : Primitive(op, bci)
{
assert(hasFormat(pfSt));
inputsBegin = inputs;
inputsEnd = inputs + 3;
inputs[0].setNode(*this);
inputs[1].setNode(*this);
inputs[2].setNode(*this);
setAlwaysNonzero(true);
}
// ----------------------------------------------------------------------------
// PrimMonitor
//
// Create a monitor primitive with the given PrimitiveOperation (which should be
// poMEnter or poMExit). slot is the index of the stack slot for the saved subheader.
//
PrimMonitor::PrimMonitor(PrimitiveOperation op, Uint32 slot, Uint32 bci)
: Primitive2(op, bci), slot(slot)
{
assert(hasFormat(pfMonitor));
setAlwaysNonzero(true);
}
// ----------------------------------------------------------------------------
// PrimSync
//
// Create a PrimSync.
//
PrimSync::PrimSync(Uint32 bci): Primitive0(poSync, bci)
{
assert(hasFormat(pfSync));
setAlwaysNonzero(true);
}
// ----------------------------------------------------------------------------
// PrimSysCall
//
// Create a system call primitive for the given system call. The incoming edges
// get allocated from the given pool and connected.
// The caller then has to connect the inputs to other data flow nodes.
//
PrimSysCall::PrimSysCall(const SysCall &sysCall, Pool &pool, Uint32 bci)
: Primitive(sysCall.operation, bci), sysCall(sysCall)
{
assert(hasFormat(pfSysCall));
uint i = sysCall.nInputs;
DataConsumer *inputs = new(pool) DataConsumer[i];
inputsBegin = inputs;
while (i--)
inputs++->setNode(*this);
inputsEnd = inputs;
}
#ifdef DEBUG_LOG
void PrimSysCall::printIncoming(LogModuleObject &f) const
{
UT_OBJECTLOG(f, PR_LOG_ALWAYS, (" %s", sysCall.name));
Primitive::printIncoming(f);
}
#endif
// ----------------------------------------------------------------------------
// PrimCall
//
// Create a call primitive for the given call. The incoming edges
// get allocated from the given pool and connected.
// The caller then has to connect the inputs to other data flow nodes.
// nArguments and nResults do not include the implicit memory edges or the
// edge carrying the address of the called function.
//
// If the Method object is known for the function being called, method should point to it;
// if not, method must be nil.
//
PrimCall::PrimCall(PrimitiveOperation op, const Method *method,
uint nArguments, uint nResults, Pool &pool, Uint32 bci)
: Primitive(op, bci), nArguments(nArguments), nResults(nResults), method(method)
{
assert(hasFormat(pfCall));
nArguments += 2;
DataConsumer *inputs = new(pool) DataConsumer[nArguments];
inputsBegin = inputs;
while (nArguments--)
inputs++->setNode(*this);
inputsEnd = inputs;
}
#ifdef DEBUG_LOG
void PrimCall::printIncoming(LogModuleObject &f) const
{
if (method) {
UT_OBJECTLOG(f, PR_LOG_ALWAYS, (" ["));
method->printRef(f);
UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("]"));
}
Primitive::printIncoming(f);
}
#endif