/* -*- 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): 
 */
#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
