211 lines
7.9 KiB
C++
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);
|
|
}
|
|
}
|