/* -*- 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 "ControlGraph.h" #include "DebugUtils.h" const ControlKindProperties controlKindProperties[nControlKinds] = {// Incoming--- Outgoing---------- // Norml? Exc? Norml? Exc? One? {false, false, false, false, false}, // ckNone {false, false, true, false, true}, // ckBegin {false, true, false, false, false}, // ckEnd {true, false, true, false, true}, // ckBlock {true, false, true, false, false}, // ckIf {true, false, true, false, false}, // ckSwitch {true, false, true, true, true}, // ckExc {true, false, false, true, false}, // ckThrow {true, false, true, true, true}, // ckAExc {false, true, true, false, true}, // ckCatch {true, false, false, false, false} // ckReturn }; #ifdef DEBUG_LOG const char controlKindNames[][8] = { "????", // ckNone "Begin", // ckBegin "End", // ckEnd "Block", // ckBlock "If", // ckIf "Switch", // ckSwitch "Exc", // ckExc "Throw", // ckThrow "AExc", // ckAExc "Catch", // ckCatch "Return" // ckReturn }; // // Print the name of the ControlKind onto the output stream f. // Return the number of characters printed. // int print(LogModuleObject &f, ControlKind ck) { return UT_OBJECTLOG(f, PR_LOG_ALWAYS, ((uint)ck > ckReturn ? "????" : controlKindNames[ck])); } #endif // ---------------------------------------------------------------------------- // ControlNode::BeginExtra // // Initialize a new BeginExtra record with nArguments incoming arguments whose // kinds are given by argumentKinds. If the method isn't static, the this // parameter must be listed as an incoming argument in the argumentKinds array // as well and hasSelfArgument must be set to true; otherwise hasSelfArgument // should be false. // Allocate additional storage for the arguments array from the given pool. // ControlNode::BeginExtra::BeginExtra(ControlNode &controlNode, Pool &pool, uint nArguments, const ValueKind *argumentKinds, bool hasSelfArgument, Uint32 bci) : nArguments(nArguments), arguments(nArguments ? new(pool) PrimArg[nArguments] : 0) { initialMemory.initOutgoingEdge(vkMemory, true); controlNode.appendPrimitive(initialMemory); PrimArg *a = arguments; bool isThis = hasSelfArgument; for (uint i = 0; i != nArguments; i++) { // Set the bytecode index. This can't be done in the constructor // because initializers can't be specified for an array of objects // allocated through the new operator a->setBytecodeIndex(bci); a->initOutgoingEdge(*argumentKinds++, isThis); isThis = false; controlNode.appendPrimitive(*a); a++; } } // ---------------------------------------------------------------------------- // ControlNode::ReturnExtra // // Initialize a new ReturnExtra record with nResults results whose kinds are // given in the resultKinds array. // Allocate additional storage for the results array from the given pool. // ControlNode::ReturnExtra::ReturnExtra(ControlNode &controlNode, Pool &pool, uint nResults, const ValueKind *resultKinds, Uint32 bci) : nResults(nResults), results((PrimResult * const)pool.allocate(nResults * sizeof(PrimResult))) { uint i; PrimResult *r = results; for (i = 0; i != nResults; i++) { new (r) PrimResult(bci); r->setResultKind(*resultKinds++); controlNode.appendPrimitive(*r++); } } // ---------------------------------------------------------------------------- // ControlNode Uint32 ControlNode::nextGeneration = 1; #ifdef DEBUG // // Return true if each phi node in this ControlNode has the same number of // inputs as this ControlNode has predecessors. // If phisDisengaged is nonzero, always return true. // bool ControlNode::phisConsistent() const { if (phisDisengaged) return true; Uint32 nPredecessors = predecessors.length(); for (DoublyLinkedList::iterator i = phiNodes.begin(); !phiNodes.done(i); i = phiNodes.advance(i)) if (phiNodes.get(i).nInputs() != nPredecessors) return false; return true; } #endif // // Return the number of successor edges that are not exception or return edges. // Multiple edges to the same node are counted multiple times. // Uint32 ControlNode::nNormalSuccessors() const { switch (controlKind) { case ckEnd: case ckThrow: case ckReturn: return 0; case ckBegin: case ckBlock: case ckExc: case ckAExc: case ckCatch: return 1; case ckIf: return 2; case ckSwitch: return nSuccessors(); case ckNone:; } trespass("Bad control node"); return 0; } // // Allocate exactly one successor for this ControlNode. // void ControlNode::setOneSuccessor() { ControlEdge *successor = new(getPrimitivePool()) ControlEdge; successor->setSource(*this); setSuccessors(successor, successor + 1); } // // Allocate n successors for this ControlNode. // void ControlNode::setNSuccessors(Uint32 n) { ControlEdge *successors = new(getPrimitivePool()) ControlEdge[n]; ControlEdge *successors2 = successors; while (n--) successors2++->setSource(*this); setSuccessors(successors, successors2); } // // Use n preallocated successors for this ControlNode. // Make the successors's sources point to this ControlNode. // inline void ControlNode::setNSuccessors(Uint32 n, ControlEdge *successors) { setSuccessors(successors, successors + n); while (n--) successors++->setSource(*this); } // // 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. // The successor ControlEdges' targets must have been initialized prior // to calling this method; this method clears them. // void ControlNode::clearControlKind() { #if defined(DEBUG) || defined(DEBUG_LOG) controlKind = ckNone; #endif for (ControlEdge *e = successorsBegin; e != successorsEnd; e++) e->clearTarget(); #ifdef DEBUG successorsBegin = 0; successorsEnd = 0; #endif } // // 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. // The successor ControlEdges' targets must have been initialized prior // to calling this method; this method clears them. // // Return a location suitable for relinking another edge to the control // node's normal outgoing edge's target in place of the existing normal // outgoing edge without disturbing the order of that target's // predecessors (changing that order would disrupt the target's phi nodes). // This control node must have exactly one normal outgoing edge. // DoublyLinkedList::iterator ControlNode::clearControlKindOne() { assert(hasOneNormalOutgoingEdge(getControlKind()) && successorsBegin); #if defined(DEBUG) || defined(DEBUG_LOG) controlKind = ckNone; #endif ControlEdge *e = successorsBegin; DoublyLinkedList::iterator where = e->clearTarget(); while (++e != successorsEnd) e->clearTarget(); #ifdef DEBUG successorsBegin = 0; successorsEnd = 0; #endif return where; } // // Designate the ControlNode to be a Begin node with nArguments incoming arguments // whose kinds are given by argumentKinds. If the method isn't static, the this // parameter must be listed as an incoming argument in the argumentKinds array // as well and hasSelfArgument must be set to true; otherwise hasSelfArgument // should be false. // Afterwards the caller must initialize the successor ControlEdge's target. // void ControlNode::setControlBegin(uint nArguments, const ValueKind *argumentKinds, bool hasSelfArgument, Uint32 bci) { assert(controlKind == ckNone); setOneSuccessor(); Pool &pool = getPrimitivePool(); beginExtra = new(pool) BeginExtra(*this, pool, nArguments, argumentKinds, hasSelfArgument, bci); controlKind = ckBegin; } // // Designate the ControlNode to be an End node. // The caller must subsequently initialize the finalMemory's incoming edge. // void ControlNode::setControlEnd(Uint32 bci) { assert(controlKind == ckNone); ControlEdge e; setSuccessors(&e, &e); // No successors. endExtra = new(getPrimitivePool()) EndExtra(bci); appendPrimitive(endExtra->finalMemory); controlKind = ckEnd; } // // Designate the ControlNode to be a Block node. // Afterwards the caller must initialize the successor ControlEdge's target. // void ControlNode::setControlBlock() { assert(controlKind == ckNone); setOneSuccessor(); controlKind = ckBlock; } // // Designate the ControlNode to be an If node that tests the given // conditional. condition must be a DataNode that generates a condition // value. Afterwards the caller must initialize the two successor // ControlEdges' targets. // void ControlNode::setControlIf(Condition2 conditional, DataNode &condition, Uint32 bci) { assert(controlKind == ckNone && condition.hasKind(vkCond)); setNSuccessors(2); PrimControl *pc = new(getPrimitivePool()) PrimControl(condition2ToIfCond(conditional), bci); pc->getInput().setVariable(condition); appendPrimitive(*pc); controlPrimExtra = pc; controlKind = ckIf; } // // Designate the ControlNode to be a Switch node with nCases cases. selector // must be a DataNode that generates an int value. Afterwards the caller // must initialize all the successor ControlEdges' targets. // void ControlNode::setControlSwitch(DataNode &selector, Uint32 nCases, Uint32 bci) { assert(controlKind == ckNone && selector.hasKind(vkInt) && nCases > 0); setNSuccessors(nCases); PrimControl *pc = new(getPrimitivePool()) PrimControl(poSwitch, bci); pc->getInput().setVariable(selector); appendPrimitive(*pc); controlPrimExtra = pc; controlKind = ckSwitch; } // // Designate the ControlNode to be an Exc, Throw, or AExc node, depending on // the given ControlKind. The node will have nHandlers exception handlers // with that many filter classes listed in the handlerFilters array. The // exception classes are considered in order when dispatching an exception, // so their order is significant. If the ControlNode will be an Exc or Throw // node, tryPrimitive must be a primitive located inside this node that can // throw an exception; if the ControlNode will be an AExc node, tryPrimitive // must be nil. successors must be a preallocated array of nHandlers or // nHandlers+1 successor edges, including the edge referring to the normal // successor for Exc and AExc nodes. // // The caller must initialize all the successor ControlEdges' targets either // before or after calling this method. // void ControlNode::setControlException(ControlKind ck, Uint32 nHandlers, const Class **handlerFilters, Primitive *tryPrimitive, ControlEdge *successors) { assert(controlKind == ckNone && hasExceptionOutgoingEdges(ck) && nHandlers > 0 && (tryPrimitive == 0) == (ck == ckAExc)); bool hasNormalExit = ck != ckThrow; setNSuccessors(hasNormalExit + nHandlers, successors); ExceptionExtra *ee; if (tryPrimitive) ee = new(getPrimitivePool()) TryExtra(tryPrimitive); else ee = new(getPrimitivePool()) ExceptionExtra; ee->nHandlers = nHandlers; ee->handlerFilters = handlerFilters; exceptionExtra = ee; controlKind = ck; } // // Designate the ControlNode to be a Catch node. Return the node's PrimCatch // primitive. Afterwards the caller must initialize the successor // ControlEdge's target. // PrimCatch & ControlNode::setControlCatch(Uint32 bci) { assert(controlKind == ckNone); setOneSuccessor(); catchExtra = new(getPrimitivePool()) CatchExtra(bci); PrimCatch &cause = catchExtra->cause; prependPrimitive(cause); controlKind = ckCatch; return cause; } // // Designate the ControlNode to be a Return node with nResults results whose // kinds are given in the resultKinds array. Afterwards the caller must // initialize the successor ControlEdge's target to point to the End node and // call getReturnExtra()[...].getInput() = ... for each result // void ControlNode::setControlReturn(uint nResults, const ValueKind *resultKinds, Uint32 bci) { assert(controlKind == ckNone); setOneSuccessor(); Pool &pool = getPrimitivePool(); returnExtra = new(pool) ReturnExtra(*this, pool, nResults, resultKinds, bci); controlKind = ckReturn; controlGraph.setReturnNode(*this); } // // Move the predecessors from the given list to this ControlNode. // This ControlNode must have no current predecessors. // On return, the src list will be made empty because the ControlEdges can // be linked into only one DoublyLinkedList at a time. // void ControlNode::movePredecessors(DoublyLinkedList &src) { assert(phisDisengaged || phiNodes.empty()); predecessors.move(src); for (DoublyLinkedList::iterator i = predecessors.begin(); !predecessors.done(i); i = predecessors.advance(i)) predecessors.get(i).setTarget(*this); } #ifdef DEBUG // // Temporary routine to print out the list of instructions which are valid after scheduling. // void ControlNode::printScheduledInstructions(LogModuleObject &f) { for(InstructionList::iterator i = instructions.begin(); !instructions.done(i); i = instructions.advance(i)) { instructions.get(i).getPrimitive()->printPretty(f, 2); UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("\t\t")); instructions.get(i).printDebug(f); UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("\n")); } } #endif #ifdef DEBUG_LOG // // Print a reference to this ControlNode for debugging purposes. // Return the number of characters printed. // int ControlNode::printRef(LogModuleObject &f) const { if (controlGraph.dfsListIsValid() && dfsNum >= 0) return UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("N%d", dfsNum)); else return UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("N%p", this)); } // // Print a ControlNode for debugging purposes. // f should be at the beginning of a line. // void ControlNode::printPretty(LogModuleObject &f, int margin) const { printMargin(f, margin); if (controlKind != ckNone) { print(f, controlKind); UT_OBJECTLOG(f, PR_LOG_ALWAYS, (" ")); } UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("Node ")); printRef(f); UT_OBJECTLOG(f, PR_LOG_ALWAYS, (" (%p):\n", this)); printMargin(f, margin); UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("Predecessors: ")); bool printSeparator = false; for (DoublyLinkedList::iterator i = predecessors.begin(); !predecessors.done(i); i = predecessors.advance(i)) { ControlEdge &e = predecessors.get(i); if (printSeparator) UT_OBJECTLOG(f, PR_LOG_ALWAYS, (", ")); printSeparator = true; assert(&e.getTarget() == this); e.getSource().printRef(f); } UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("\n")); if (!phiNodes.empty()) { printMargin(f, margin); UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("Phi nodes:\n")); for (DoublyLinkedList::iterator i = phiNodes.begin(); !phiNodes.done(i); i = phiNodes.advance(i)) { PhiNode &n = phiNodes.get(i); assert(n.getContainer() == this); n.printPretty(f, margin + 4); } } if (!primitives.empty()) { printMargin(f, margin); UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("Primitives:\n")); for (DoublyLinkedList::iterator i = primitives.begin(); !primitives.done(i); i = primitives.advance(i)) { Primitive &n = primitives.get(i); assert(n.getContainer() == this); n.printPretty(f, margin + 4); } } if (successorsBegin && successorsEnd) { ControlEdge *e = successorsBegin; ControlEdge *eEnd = e + nNormalSuccessors() + hasControlKind(ckReturn); if (e != eEnd) { printMargin(f, margin); UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("Successors: ")); bool printSeparator = false; while (e != eEnd) { if (printSeparator) UT_OBJECTLOG(f, PR_LOG_ALWAYS, (", ")); printSeparator = true; assert(&e->getSource() == this); e->getTarget().printRef(f); e++; } UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("\n")); } if (e != successorsEnd) { ExceptionExtra *ee = &getExceptionExtra(); assert((Uint32)(successorsEnd - e) == ee->nHandlers); printMargin(f, margin); UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("Exception handlers: ")); bool printSeparator = false; const Class **exceptionClass = ee->handlerFilters; while (e != successorsEnd) { if (printSeparator) printMargin(f, margin + 20); printSeparator = true; assert(&e->getSource() == this); (*exceptionClass++)->printRef(f); UT_OBJECTLOG(f, PR_LOG_ALWAYS, (" -> ")); e->getTarget().printRef(f); UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("\n")); e++; } } } UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("\n")); } #endif