dmose%mozilla.org 2ccab49e6d updating license boilerplate to xPL v1.1
git-svn-id: svn://10.0.0.236/trunk@52526 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-02 06:38:29 +00:00

211 lines
7.9 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):
*/
//
// x86StdCall.h - StdCall calling convention
//
#include "x86Instruction.h"
// Utility function to push one function-call argument on the stack
void pushCallArg(DataConsumer &curArgument, DataNode* usesNode, InstructionDefine*& orderingDependency, Pool& inPool, x86Emitter& inEmitter);
// -> mem regarg1 regarg2 regarg3 regarg3
// <- mem [returnval]
// inHasReturnValue is deprecated, and is ignored
template<bool tHasIncomingStore, bool tHasOutgoingStore, bool tHasFunctionAddress, bool tIsDynamic>
Call<tHasIncomingStore, tHasOutgoingStore, tHasFunctionAddress, tIsDynamic>::
Call( DataNode* inDataNode,
Pool& inPool,
Uint8 inRegisterArguments,
bool /*inHasReturnValue*/,
x86Emitter& inEmitter,
void (*inFunc)(),
DataNode* inUseDataNode) :
InsnUseXDefineYFromPool(inDataNode, inPool, ((inRegisterArguments != 0) ? 1 : 0) + tHasIncomingStore + tIsDynamic, 3)
// add extra use for ordering edge (see below) if more than one argument
{
DataNode* returnValProducer; // node whose outgoing edge is the return value
if (inDataNode->hasKind(vkTuple))
{
// if called with a Primitive with kind vkTuple, we know we will have to deal with a projection edge
const DoublyLinkedList<DataConsumer>& projectionConsumers = inDataNode->getConsumers();
DoublyLinkedList<DataConsumer>::iterator curProjectionEdge = projectionConsumers.begin();
// grab the outgoing store (if there is one)
// set up returnValProducer to point to the return value producer (if it exists)
// one of the two consumers of the projection node must be the memory node, the other must be the Return value node
if (tHasOutgoingStore)
{
DataNode& projectionA = projectionConsumers.get(curProjectionEdge).getNode();
DataNode* projectionB = (projectionConsumers.done(curProjectionEdge = projectionConsumers.advance(curProjectionEdge))) ? 0 : &projectionConsumers.get(curProjectionEdge).getNode();
if (projectionA.hasKind(vkMemory))
{
inEmitter.defineProducer(projectionA, *this, 0); // -> mem
returnValProducer = projectionB;
}
else
{
assert(projectionB); // projectionB must be the memory producer
inEmitter.defineProducer(*projectionB, *this, 0); // -> mem
returnValProducer = &projectionA;
}
}
else
returnValProducer = (projectionConsumers.done(curProjectionEdge = projectionConsumers.advance(curProjectionEdge))) ? 0 : &projectionConsumers.get(curProjectionEdge).getNode();
}
else
{
// non-call Primitive treated as an out-of-line call
assert(!tHasFunctionAddress && !tIsDynamic);
// since outgoing stores are handled separately and are not
// a real return type, we make the return value code ignore
// them
if (tHasOutgoingStore)
{
assert(inDataNode->hasKind(vkMemory));
inEmitter.defineProducer(*inDataNode, *this, 0); //-> mem
returnValProducer = NULL;
}
else
returnValProducer = inDataNode;
}
// pre-color (and buffer) the return value
if (returnValProducer)
{
switch (returnValProducer->getKind())
{
case vkInt:
case vkAddr:
{
VirtualRegister* returnValVR;
// the Call defines a temporary register, which is precolored to
// the appropriate return register
returnValVR = &inEmitter.defineTemporary(*this, 1);
returnValVR->preColorRegister(x86GPRToColor[EAX]);
// now create a "buffer" copy between the precolored return register
// and make the Copy define the return value edge from the poCall
InsnDoubleOpDir& copyInsn = newCopyInstruction(*inDataNode, inPool);
inEmitter.useTemporaryVR(copyInsn, *returnValVR, 0);
inEmitter.defineProducer(*returnValProducer, copyInsn, 0);
// fiX-ME none-out unused return value argument
break;
}
case vkLong:
{
// results returned in eax (low) and edx (high)
VirtualRegister* lowVR;
VirtualRegister* highVR;
lowVR = &inEmitter.defineTemporary(*this, 1);
highVR = &inEmitter.defineTemporary(*this, 2);
lowVR->preColorRegister(x86GPRToColor[EAX]);
highVR->preColorRegister(x86GPRToColor[EDX]);
InsnDoubleOpDir& copyHiInsn = newCopyInstruction(*inDataNode, inPool);
inEmitter.useTemporaryVR(copyHiInsn, *highVR, 0);
inEmitter.defineProducer(*returnValProducer, copyHiInsn, 0, vrcInteger, vidHigh);
InsnDoubleOpDir& copyLowInsn = newCopyInstruction(*inDataNode, inPool);
inEmitter.useTemporaryVR(copyLowInsn, *lowVR, 0);
inEmitter.defineProducer(*returnValProducer, copyLowInsn, 0, vrcInteger, vidLow);
}
break;
case vkFloat:
inEmitter.emit_CallReturnF(*this, *inDataNode, *returnValProducer);
break;
case vkDouble:
inEmitter.emit_CallReturnD(*this, *inDataNode, *returnValProducer);
break;
default:
assert(false);
break;
}
}
else if (!tHasOutgoingStore)
inDataNode->setInstructionRoot(this); // if no outgoing edges (no return value or memory edge, we need to hook this on)
DataConsumer* firstArgument;
DataConsumer* curArgument;
// uses may begin at some passed in node, instead of the node from which the call originates
DataNode* usesNode = (inUseDataNode) ? inUseDataNode : inDataNode;
// incoming store
if (tHasIncomingStore)
inEmitter.useProducer(usesNode->nthInputVariable(0), *this, tIsDynamic + (inRegisterArguments > 0)); // -> mem
firstArgument = usesNode->getInputsBegin() + tHasFunctionAddress + tHasIncomingStore;
// grab the callee address (it might not be a static call)
if (tHasFunctionAddress)
{
assert(inFunc == NULL); // programmer error to specify a function address if this Call has a function address
if (tIsDynamic)
inEmitter.useProducer(usesNode->nthInputVariable(1), *this, 0); // -> incoming address
else
mCalleeAddress = (Uint32) nthInputConstantUint32(*usesNode, 1);
}
else
mCalleeAddress = (Uint32) inFunc;
// start at last argument and push arguments from right to left
curArgument = usesNode->getInputsEnd() - 1; // arguments array is last + 1
// BASIC IDEA:
//
// We are creating a bunch of pushes followed by a call instruction
// Since pushes are clearly ordered operations we need an edge to maintain that ordering.
// We use an order edge defined by the first push, and which is used and by each
// subsequent push. This ends in a use by the Call (*this).
//
// In the end we will have somethinglike Call -> Arg0 -> Arg1 -> ... -> ArgN-1 -> ArgN
// where Call is the call instruction and the Arg's are pushes.
//
// The scheduler will walk up the use chain and do the last arg first and then come
// back down the chain.
InstructionDefine* orderingDependency = NULL; // maintains "thread" to order the pushes of the arguments
if(curArgument >= firstArgument)
{
// Generate pushes for the arguments. Order them with Order Edges.
// Note x86 arguments are pushed on the stack from right to left.
for (; curArgument >= firstArgument; curArgument--)
pushCallArg(*curArgument, usesNode, orderingDependency, inPool, inEmitter);
// this is the final order edge which is attached to the Call instruction
inEmitter.useTemporaryOrder(*this, *orderingDependency, tIsDynamic);
}
}