gecko-dev/ef/Compiler/PrimitiveGraph/ControlNodes.h

513 lines
20 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.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#ifndef CONTROLNODES_H
#define CONTROLNODES_H
#include "Instruction.h"
#include "ClassWorld.h"
#include "Multiset.h"
#include "FastBitSet.h"
#include "CheckedUnion.h"
class ControlGraph;
class ControlEdge: public DoublyLinkedEntry<ControlEdge> // Links to other ControlEdges that go to the same target
{
ControlNode *source; // The node that owns this outgoing control edge (in DEBUG versions nil if not explicitly set)
ControlNode *target; // The node to which control goes from this node (in DEBUG versions nil if not explicitly set)
public:
bool longFlag; // States whether the edge is a short or long edge (for ControlNodeScheduler).
#ifdef DEBUG
ControlEdge(): source(0), target(0) {}
#endif
ControlNode &getSource() const {assert(source); return *source;}
ControlNode &getTarget() const {assert(target); return *target;}
void setSource(ControlNode &n) {assert(!source); source = &n;} // For use by ControlNode only.
void setTarget(ControlNode &n) {assert(!target); target = &n;} // For use by ControlNode only.
DoublyLinkedList<ControlEdge>::iterator clearTarget();
void substituteTarget(ControlEdge &src);
#ifdef DEBUG
bool hasTarget() const {return target != 0;} // Only works in DEBUG versions!
#endif
};
enum ControlKind // NormalIncoming? NormalOutgoing? OneNormalOutgoing?
{ // ExceptionIncoming? ExceptionOutgoing?
ckNone, // no no no no no
ckBegin, // no no yes no yes
ckEnd, // no yes no no no
ckBlock, // yes no yes no yes
ckIf, // yes no yes no no
ckSwitch, // yes no yes no no
ckExc, // yes no yes yes yes
ckThrow, // yes no no yes no
ckAExc, // yes no yes yes yes
ckCatch, // no yes yes no yes
ckReturn // yes no no no no
};
const uint nControlKinds = ckReturn + 1;
struct ControlKindProperties
{
bool normalInputs BOOL_8; // Does this control node kind allow normal forward or backward incoming edges?
bool exceptionInputs BOOL_8; // Does this control node kind allow exception incoming edges?
bool normalOutgoingEdges BOOL_8; // Does this control node kind allow normal forward or backward outgoing edges?
bool exceptionOutgoingEdges BOOL_8; // Does this control node kind allow exception outgoing edges?
bool oneNormalOutgoingEdge BOOL_8; // Does this control node kind have exactly one forward or backward outgoing edge?
};
extern const ControlKindProperties controlKindProperties[nControlKinds];
inline bool hasNormalInputs(ControlKind ck) {return controlKindProperties[ck].normalInputs;}
inline bool hasExceptionInputs(ControlKind ck) {return controlKindProperties[ck].exceptionInputs;}
inline bool hasNormalOutgoingEdges(ControlKind ck) {return controlKindProperties[ck].normalOutgoingEdges;}
inline bool hasExceptionOutgoingEdges(ControlKind ck) {return controlKindProperties[ck].exceptionOutgoingEdges;}
inline bool hasOneNormalOutgoingEdge(ControlKind ck) {return controlKindProperties[ck].oneNormalOutgoingEdge;}
inline bool hasTryPrimitive(ControlKind ck) {return ck == ckExc || ck == ckThrow;}
#ifdef DEBUG_LOG
int print(LogModuleObject &f, ControlKind ck);
#endif
class ControlNode: public DoublyLinkedEntry<ControlNode> // Links to other ControlNodes in the same graph
{
public:
struct BeginExtra
{
PrimArg initialMemory; // Initial memory value for this method
const uint nArguments; // Number of incoming arguments
private:
PrimArg *const arguments; // Array of nArguments incoming arguments
public:
BeginExtra(ControlNode &controlNode, Pool &pool, uint nArguments,
const ValueKind *argumentKinds, bool hasSelfArgument,
Uint32 bci);
PrimArg &operator[](uint n) const {assert(n < nArguments); return arguments[n];}
};
struct EndExtra
{
PrimResult finalMemory; // Final memory value for this method
EndExtra(Uint32 bci) : finalMemory(bci) {};
};
struct ExceptionExtra
{
Uint32 nHandlers; // The last nHandlers successors go to exception handlers. nHandlers is always
// greater than zero, and every exception must be handled (possibly just by
// jumping to the control End node if no other handler applies).
const Class **handlerFilters; // Array of nHandlers exception classes that are handled by the exception handlers
// guarding this control node. These are in the same order as the last nHandlers
// successors. An exception thrown inside this ControlNode will be passed to
}; // the first matching handler.
struct TryExtra: ExceptionExtra
{
Primitive *tryPrimitive; // The primitive in this control node that can cause exceptions
explicit TryExtra(Primitive *tryPrimitive): tryPrimitive(tryPrimitive) {}
};
struct CatchExtra
{
PrimCatch cause; // Exception that was thrown here
CatchExtra(Uint32 bci) : cause(bci) {};
};
struct ReturnExtra
{
const uint nResults; // Number of results
private:
PrimResult *const results; // Array of nResults variables containing the results
public:
ReturnExtra(ControlNode &controlNode, Pool &pool, uint nResults,
const ValueKind *resultKinds, Uint32 bci);
PrimResult &operator[](uint n) const {assert(n < nResults); return results[n];}
};
private:
DoublyLinkedList<ControlEdge> predecessors; // List of possible predecessors of this control node
ControlEdge *successorsBegin; // Array of edges to successors (in DEBUG versions nil if not explicitly set)
ControlEdge *successorsEnd; // Last element + 1 (in DEBUG versions nil if not explicitly set)
DoublyLinkedList<PhiNode> phiNodes; // List of phi nodes inside this control node
DoublyLinkedList<Primitive> primitives; // List of primitives inside this control node
DoublyLinkedList<Instruction> instructions; // List of scheduled instructions inside this control node
ControlKind controlKind ENUM_8; // The kind of this control node (in DEBUG or DEBUG_LOG versions ckNone if not explicitly set)
bool noRecycle BOOL_8; // True if this node should not be recycled
#ifdef DEBUG // If zero, the phi nodes correspond to the predecessors list. If nonzero,
Uint32 phisDisengaged; // set to number of nested disengagePhis calls while working on the predecessor
#endif // or phi lists.
union { // Kind-specific data for:
BeginExtra *beginExtra; // ckBegin
EndExtra *endExtra; // ckEnd
PrimControl *controlPrimExtra; // ckIf, ckSwitch
ReturnExtra *returnExtra; // ckReturn
ExceptionExtra *exceptionExtra; // ckExc (TryExtra *), ckThrow (TryExtra *), ckAExc
CatchExtra *catchExtra; // ckCatch
};
public:
ControlGraph &controlGraph; // Graph that contains this control node
Uint32 generation; // General-purpose variable used for marking nodes while traversing the graph
private:
static Uint32 nextGeneration; // Generation value to be used for next graph traversal; each graph traversal
// should increment this by one.
public:
enum {unmarked = -3, unvisited = -2, unnumbered = -1};
Int32 dfsNum; // Control number assigned by depth-first search
Uint32 loopDepth; // Loop nesting depth
Uint32 loopId; // Temp variable for loop search.
// ********* Fields below will be moved to RegisterAllocationTemps
Uint32 schedulePos;
Flt32 nVisited; // Approx. the number of time this node is visited (10^loopDepth)
FastBitSet liveAtBegin; // Virtual registers live at begin
FastBitSet liveAtEnd; // Virtual registers live at end
bool hasNewLiveAtEnd; // if true get the new interferences.
bool liveAtBeginIsValid; // if true liveAtBegin has been updated.
// ********* Fields above will be moved to RegisterAllocationTemps
// ********* Fields below will be moved to FormatterTemps
bool workingNode; // True if this node is seen as a working node for the ControlNodeScheduler.
Uint32 nativeOffset;
// ********* Fields above will be moved to FormatterTemps
private:
// Temporary fields for matching monitorenters with monitorexits
struct MonitorAnalysisTemps
{
SimpleMultiset<DataConsumer *> *monitorsHeld; // Multiset of objects whose monitors are currently held; nil if not known yet
};
// Temporary fields for register allocation
struct RegisterAllocationTemps
{
Uint32 schedulePos;
Flt32 nVisited; // Approx. the number of time this node is visited (10^loopDepth)
FastBitSet liveAtBegin; // Virtual registers live at begin
FastBitSet liveAtEnd; // Virtual registers live at end
bool hasNewLiveAtEnd; // if true get the new interferences.
bool liveAtBeginIsValid; // if true liveAtBegin has been updated.
};
// Temporary fields for scheduling and formatting code
struct FormatterTemps
{
bool workingNode; // True if this node is seen as a working node for the ControlNodeScheduler.
Uint32 nativeOffset;
};
CheckedUnion3(MonitorAnalysisTemps, RegisterAllocationTemps, FormatterTemps) temps;
explicit ControlNode(ControlGraph &cg); // Call ControlGraph::newControlNode instead to create a ControlNode
void recycle();
public:
Pool &getPrimitivePool() const;
#ifdef DEBUG
bool getPhisDisengaged() const {return phisDisengaged || phiNodes.empty();}
bool phisConsistent() const;
#endif
void disengagePhis() {assert(phisConsistent()); DEBUG_ONLY(phisDisengaged++);}
void reengagePhis() {assert(phisDisengaged); DEBUG_ONLY(phisDisengaged--); assert(phisConsistent());}
const DoublyLinkedList<ControlEdge> &getPredecessors() const {return predecessors;}
void addPredecessor(ControlEdge &e) {assert(phisDisengaged || phiNodes.empty()); e.setTarget(*this); predecessors.addLast(e);}
void addPredecessor(ControlEdge &e, DoublyLinkedList<ControlEdge>::iterator where);
void movePredecessors(DoublyLinkedList<ControlEdge> &src);
ControlEdge *getSuccessorsBegin() const {assert(successorsBegin); return successorsBegin;}
ControlEdge *getSuccessorsEnd() const {assert(successorsEnd); return successorsEnd;}
Uint32 nSuccessors() const {assert(successorsBegin && successorsEnd); return successorsEnd - successorsBegin;}
Uint32 nNormalSuccessors() const;
ControlEdge &nthSuccessor(Uint32 n) const {assert(n < nSuccessors()); return successorsBegin[n];}
ControlEdge &getNormalSuccessor() const;
ControlEdge &getFalseSuccessor() const;
ControlEdge &getTrueSuccessor() const;
ControlEdge *getSwitchSuccessors() const;
ControlEdge &getReturnSuccessor() const;
void setSuccessors(ControlEdge *newSuccessorsBegin, ControlEdge *newSuccessorsEnd);
private:
void setOneSuccessor();
void setNSuccessors(Uint32 n);
void setNSuccessors(Uint32 n, ControlEdge *successors);
public:
DoublyLinkedList<PhiNode> &getPhiNodes() {assert(!phisDisengaged); return phiNodes;}
void addPhiNode(PhiNode &p) {assert(phisDisengaged || p.nInputs() == predecessors.length()); p.setContainer(this); phiNodes.addLast(p);}
void movePhiNode(PhiNode &p);
DoublyLinkedList<Primitive> &getPrimitives() {return primitives;}
void appendPrimitive(Primitive &p) {p.setContainer(this); primitives.addLast(p);}
void prependPrimitive(Primitive &p) {p.setContainer(this); primitives.addFirst(p);}
void movePrimitiveToBack(Primitive &p);
void movePrimitiveToFront(Primitive &p);
bool hasControlKind(ControlKind ck) const {assert(controlKind != ckNone); return controlKind == ck;}
ControlKind getControlKind() const {assert(controlKind != ckNone); return controlKind;}
void unsetControlKind();
void clearControlKind();
DoublyLinkedList<ControlEdge>::iterator clearControlKindOne();
void setControlBegin(uint nArguments, const ValueKind *argumentKinds,
bool hasSelfArgument, Uint32 bci);
void setControlEnd(Uint32 bci);
void setControlBlock();
void setControlIf(Condition2 conditional, DataNode &condition, Uint32 bci);
void setControlSwitch(DataNode &selector, Uint32 nCases, Uint32 bci);
void setControlException(ControlKind ck, Uint32 nHandlers, const Class **handlerFilters, Primitive *tryPrimitive,
ControlEdge *successors);
PrimCatch &setControlCatch(Uint32 bci);
void setControlReturn(uint nResults, const ValueKind *resultKinds,
Uint32 bci);
BeginExtra &getBeginExtra() const {assert(hasControlKind(ckBegin)); return *beginExtra;}
EndExtra &getEndExtra() const {assert(hasControlKind(ckEnd)); return *endExtra;}
PrimControl &getControlPrimExtra() const {assert(hasControlKind(ckIf) || hasControlKind(ckSwitch)); return *controlPrimExtra;}
ExceptionExtra &getExceptionExtra() const {assert(hasExceptionOutgoingEdges(getControlKind())); return *exceptionExtra;}
TryExtra &getTryExtra() const {assert(hasTryPrimitive(getControlKind())); return *static_cast<TryExtra *>(exceptionExtra);}
CatchExtra &getCatchExtra() const {assert(hasControlKind(ckCatch)); return *catchExtra;}
ReturnExtra &getReturnExtra() const {assert(hasControlKind(ckReturn)); return *returnExtra;}
bool empty() const {assert(!phisDisengaged); return !noRecycle && primitives.empty() && phiNodes.empty();}
void inhibitRecycling() {noRecycle = true;}
InstructionList &getInstructions() {return instructions;}
void addScheduledInstruction(Instruction& i) {instructions.addLast(i);}
bool haveScheduledInstruction(Instruction& i) {return instructions.exists(i);}
void setNativeOffset(Uint32 offset) { nativeOffset = offset; }
Uint32 getNativeOffset() { return nativeOffset; }
static Uint32 getNextGeneration() {return nextGeneration++;}
#ifdef DEBUG
void printScheduledInstructions(LogModuleObject &f);
void validate(FastBitSet **reachingSet); // perform consistency checks on self
#endif
#ifdef DEBUG_LOG
bool hasControlKind() const {return controlKind != ckNone;} // Only use for debug logging code! Doesn't work in release builds.
int printRef(LogModuleObject &f) const;
void printPretty(LogModuleObject &f, int margin) const;
#endif
friend class ControlGraph; // ControlGraph can call the ControlNode constructor and recycle
};
// --- INLINES ----------------------------------------------------------------
//
// Unlink this edge from the target to which it is linked.
// Return a location suitable for relinking another edge to the target
// in place of this edge without disturbing the order of the target's
// predecessors (changing that order would disrupt the target's phi nodes).
//
inline DoublyLinkedList<ControlEdge>::iterator ControlEdge::clearTarget()
{
assert(target && target->getPhisDisengaged());
#ifdef DEBUG
target = 0;
#endif
DoublyLinkedList<ControlEdge>::iterator where = prev;
remove();
return where;
}
//
// Unlink src from the target to which it is linked and link this edge to that target
// in src's place. This edge must not currently have a target.
//
// This is essentially equivalent to, but faster than:
//
// ControlNode &target = src.getTarget();
// target.disengagePhis();
// DoublyLinkedList<ControlEdge>::iterator where = src.clearTarget();
// target.addPredecessor(*this, where);
// target.reengagePhis();
//
inline void ControlEdge::substituteTarget(ControlEdge &src)
{
assert(!target && src.target);
substitute(src);
target = src.target;
#ifdef DEBUG
src.target = 0;
#endif
}
//
// Bring the ControlNode back to the state it's in immediately after it's constructed.
// The given ControlNode must satisfy the empty() method and have no predecessors,
// instructions, control kind, or generation.
//
inline void ControlNode::recycle()
{
assert(!noRecycle && controlKind == ckNone && predecessors.empty() && empty() && instructions.empty() && generation == 0);
}
//
// Return the normal outgoing edge of this node. This node must have exactly one
// normal outgoing edge.
//
inline ControlEdge &ControlNode::getNormalSuccessor() const
{
assert(hasOneNormalOutgoingEdge(getControlKind()) && successorsBegin);
return successorsBegin[0];
}
//
// Return the false outgoing edge of this if node.
//
inline ControlEdge &ControlNode::getFalseSuccessor() const
{
assert(hasControlKind(ckIf) && successorsBegin);
return successorsBegin[0];
}
//
// Return the true outgoing edge of this if node.
//
inline ControlEdge &ControlNode::getTrueSuccessor() const
{
assert(hasControlKind(ckIf) && successorsBegin);
return successorsBegin[1];
}
//
// Return the array of nSuccessors() outgoing edges of this switch node.
//
inline ControlEdge *ControlNode::getSwitchSuccessors() const
{
assert(hasControlKind(ckSwitch) && successorsBegin);
return successorsBegin;
}
//
// Return the outgoing edge of this return node.
//
inline ControlEdge &ControlNode::getReturnSuccessor() const
{
assert(hasControlKind(ckReturn) && successorsBegin);
return successorsBegin[0];
}
//
// Add a new predecessor to this control node in the specified location.
// The node will be added to this control node's list of predecessors after the
// where predecessor, which should be the result of a clearTarget or
// clearControlKindOne call on this node. The where predecessor can be this
// node's root, in which case the new control node will become the first predecessor.
//
inline void ControlNode::addPredecessor(ControlEdge &e, DoublyLinkedList<ControlEdge>::iterator where)
{
assert(phisDisengaged || phiNodes.empty());
e.setTarget(*this);
predecessors.insertAfter(e, where);
}
//
// Designate the successors array for this control node. This can be done only
// once afer the control node is created and once after it each time it is
// reset with clearControlKind or one of its cousins.
//
inline void ControlNode::setSuccessors(ControlEdge *newSuccessorsBegin, ControlEdge *newSuccessorsEnd)
{
assert(!successorsBegin && !successorsEnd && newSuccessorsBegin && newSuccessorsEnd &&
newSuccessorsEnd >= newSuccessorsBegin);
successorsBegin = newSuccessorsBegin;
successorsEnd = newSuccessorsEnd;
}
//
// Move the phi node so that it becomes the last phi node in this control node.
// The phi node must have been part of some control node (not necessarily this one).
//
inline void ControlNode::movePhiNode(PhiNode &p)
{
assert(phisDisengaged || p.nInputs() == predecessors.length());
p.changeContainer(this);
phiNodes.addLast(p);
}
//
// Move the primitive so that it becomes the last primitive in this control node.
// The primitive must have been part of some control node (not necessarily this one).
//
inline void ControlNode::movePrimitiveToBack(Primitive &p)
{
p.changeContainer(this);
primitives.addLast(p);
}
//
// Move the primitive so that it becomes the first primitive in this control node.
// The primitive must have been part of some control node (not necessarily this one).
//
inline void ControlNode::movePrimitiveToFront(Primitive &p)
{
p.changeContainer(this);
primitives.addFirst(p);
}
//
// Unset the control node's kind so that the node can get a new kind.
// The primitives and phi nodes remain attached to this control node, but
// the control kind-specific data disappears.
// Unlike clearControlKind, the successor ControlEdges' targets must not
// have been initialized prior to calling this method.
//
inline void ControlNode::unsetControlKind()
{
#if defined(DEBUG) || defined(DEBUG_LOG)
controlKind = ckNone;
#endif
#ifdef DEBUG
for (ControlEdge *e = successorsBegin; e != successorsEnd; e++)
assert(!e->hasTarget());
successorsBegin = 0;
successorsEnd = 0;
#endif
}
#endif