/* -*- 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++;
	}
}
