/* -*- 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 Call:: 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& projectionConsumers = inDataNode->getConsumers(); DoublyLinkedList::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); } }