/* -*- 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): */ // PPCCalls.h // // Scott M. Silver // #ifndef _H_PPCCALLS #define _H_PPCCALLS #include "PPC601AppleMacOSEmitter.h" #include "AssembledInstructions.h" template class Call : public PPCInstructionXY { public: static inline bool hasReturnValue(DataNode& inDataNode); // determines whether Call Primitive has return value static inline uint8 numberOfArguments(DataNode& inDataNode); // determines number of real arguments (not including store) Call(DataNode* inDataNode, Pool& inPool, uint8 inRegisterArguments, bool inHasReturnValue, PPCEmitter& inEmitter, void* inFunc = NULL); virtual void formatToMemory(void* inStart, Uint32 inOffset, MdFormatter& inFormatter); virtual size_t getFormattedSize(MdFormatter& /*inFormatter*/) { return (8); } virtual InstructionFlags getFlags() const { return (ifCall); } protected: int16 mTOCOffset; // offset in accumulator TOC TVector* mCalleeAddress; // addres of cuntion #ifdef DEBUG public: virtual void printPretty(FILE* f) { fprintf(f, "call %X", mCalleeAddress); } #endif }; template class CallS : public Call { public: inline CallS(DataNode* inDataNode, Pool& inPool, uint8 inRegisterArguments, bool inHasReturnValue, PPCEmitter& inEmitter, void* inFunc) : Call(inDataNode, inPool, inRegisterArguments, inHasReturnValue, inEmitter, inFunc) { } }; #ifdef MANUAL_TEMPLATES template class Call; template class Call; template class Call; template class Call; template class Call; #endif typedef CallS CallS_V; typedef CallS CallS_; typedef CallS CallS_C; typedef Call Call_; // Dynamically dispatched call class CallD_ : public Call { public: inline CallD_(DataNode* inDataNode, Pool& inPool, uint8 inRegisterArguments, bool inHasReturnValue, PPCEmitter& inEmitter) : Call(inDataNode, inPool, inRegisterArguments, inHasReturnValue, inEmitter) { } void formatToMemory(void* inStart, Uint32 /*inOffset*/, MdFormatter& inFormatter); }; template bool Call:: hasReturnValue(DataNode& inDataNode) { bool hasReturnValue = (inDataNode.getOutgoingEdgesBegin() + tHasOutgoingStore < inDataNode.getOutgoingEdgesEnd()); return (hasReturnValue); } template uint8 Call:: numberOfArguments(DataNode& inDataNode) { DataConsumer* firstArg; DataConsumer* lastArg; assert(!(tHasFunctionAddress && !tHasIncomingStore)); // no such primitive firstArg = inDataNode.getInputsBegin() + tHasFunctionAddress + tHasIncomingStore; lastArg = inDataNode.getInputsEnd(); return (lastArg - firstArg); } // -> mem regarg1 regarg2 regarg3 regarg3 // <- mem [returnval] template Call:: Call(DataNode* inDataNode, Pool& inPool, uint8 inRegisterArguments, bool /*inHasReturnValue*/, PPCEmitter& inEmitter, void* inFunc) : PPCInstructionXY(inDataNode, inPool, inRegisterArguments + tHasIncomingStore + tIsDynamic, 1 + tHasOutgoingStore) { if (!tIsDynamic) inEmitter.mCrossTOCPtrGlCount++; const DoublyLinkedList& projectionConsumers = inDataNode->getConsumers(); DoublyLinkedList::iterator curProjectionEdge = projectionConsumers.begin(); DataNode* returnValProducer; // outgoing store 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(); if (returnValProducer) { VirtualRegister* returnValVR; // the Call defines a temporary register, which is precolored to // the appropriate return register returnValVR = &inEmitter.defineTemporary(*this, 1); switch (returnValProducer->getKind()) { case vkInt: case vkAddr: { returnValVR->preColorRegister(kR3Color); // now create a "buffer" copy between the precolored return register // and make the Copy define the return value edge from the pkCall Copy_I& copyInsn = *new(inPool) Copy_I(inDataNode, inPool); inEmitter.useTemporaryVR(copyInsn, *returnValVR, 0); inEmitter.defineProducer(*returnValProducer, copyInsn, 0); break; } case vkLong: assert(false); break; case vkFloat: case vkDouble: assert(false); break; default: assert(false); } } else getInstructionDefineBegin()[1].kind = udNone; // zero out the unused outgoing edge DataConsumer* firstArgument; DataConsumer* curArgument; // incoming store if (tHasIncomingStore) inEmitter.useProducer(inDataNode->nthInputVariable(0), *this, 0); // -> mem firstArgument = inDataNode->getInputsBegin() + tHasFunctionAddress + tHasIncomingStore; if (tHasFunctionAddress) { assert(inFunc == NULL); // programmer error to specify a function address if this Call has a function address if (tIsDynamic) inEmitter.useProducer(inDataNode->nthInputVariable(1), *this, 1); // -> incoming address else mCalleeAddress = (TVector*) addressFunction(PrimConst::cast(inDataNode->nthInputVariable(1)).value.a); } else mCalleeAddress = (TVector*) inFunc; inEmitter.mAccumulatorTOC.addData(&mCalleeAddress, sizeof(TVector), mTOCOffset); // move all arguments <= 8 words into registers, handle most of the stack passed arguments later uint8 curFixedArg = 0; // number of words passes as fixed arguments uint8 curFloatArg = 0; // number of words passed as float arguments uint8 curTotalArgNumber = tHasIncomingStore + tIsDynamic; // use number, which starts at 1 after the store, if it exists for (curArgument = firstArgument; curArgument < inDataNode->getInputsEnd(); curArgument++, curTotalArgNumber++) { VirtualRegister* vr; switch (curArgument->getKind()) { case vkInt: case vkAddr: if (curFixedArg <= 8) { if (curArgument->isConstant()) vr = &inEmitter.genLoadConstant_I(*inDataNode, curArgument->getConstant().i); else { Copy_I& copyInsn = *new(inPool) Copy_I(inDataNode, inPool); inEmitter.useProducer(curArgument->getVariable(), copyInsn, 0); vr = &inEmitter.defineTemporary(copyInsn, 0); } inEmitter.useTemporaryVR(*this, *vr, curTotalArgNumber); vr->preColorRegister(curFixedArg); } else { StD_FixedDestRegister& storeParam = *new(inPool) StD_FixedDestRegister(inDataNode, inPool, dfLwz, 24 + (curFixedArg + curFloatArg) * 4); if (curArgument->isConstant()) { VirtualRegister& vr = inEmitter.genLoadConstant_I(*inDataNode, curArgument->getConstant().i); inEmitter.useTemporaryVR(storeParam, vr, 1); } else inEmitter.useProducer(curArgument->getVariable(), storeParam, 1); // have the store use the incoming store edge, and define a temporary outgoing edge // make the call depend on this temporary outgoing store edge inEmitter.useProducer(inDataNode->nthInputVariable(0), storeParam, 0); inEmitter.useTemporaryOrder(*this, inEmitter.defineTemporaryOrder(storeParam, 0), curTotalArgNumber); } curFixedArg++; break; case vkFloat: case vkDouble: if (curFloatArg <= 13) vr->preColorRegister(curFloatArg++); else assert(false); break; case vkLong: assert(false); break; default: assert(false); } } // keep track of maximum words used inEmitter.mMaxArgWords = PR_MAX(inEmitter.mMaxArgWords, curFixedArg + curFloatArg); } template void Call:: formatToMemory(void* inStart, Uint32 /*inOffset*/, MdFormatter& inFormatter) { assert(!tIsDynamic); uint32 *curPC = curPC = (uint32 *) inStart; int32 branchOffset; bool sameTOC = (mCalleeAddress->toc == inFormatter.mRealTOC->mGlobalPtr); bool isStub = true; // callee is stub if (!isStub && sameTOC && (uint32) mCalleeAddress->functionPtr < 0x3FFFFFF) { *curPC++ = kBla | ( (uint32) mCalleeAddress->functionPtr & 0x03FFFFFF); *curPC++ = kNop; } else if (!isStub && sameTOC && canBePCRelative(inStart, (void*) mCalleeAddress, branchOffset)) { *curPC++ = kBl | (branchOffset & 0x03FFFFFF); *curPC++ = kNop; } else { // we can or the offset right into the bl instruction (because aa and lk nicely use the last 2 bits) *curPC++ = kBl | (((uint32) inFormatter.mNextFreePostMethod - (uint32)inStart) & 0x03FFFFFF); inFormatter.mNextFreePostMethod = (uint32*) formatCrossTocPtrGl(inFormatter.mNextFreePostMethod, mTOCOffset + inFormatter.mRealTOCOffset); *curPC++ = makeDForm(32, 2, 1, 20); // stw rtoc, 20(sp) } } inline void CallD_:: formatToMemory(void* inStart, Uint32 /*inOffset*/, MdFormatter& /*inFormatter*/) { uint32 *curPC = (uint32 *) inStart; *curPC++ = kBl | (((uint32) getDynamicPtrGl(inStart, udToRegisterNumber(getInstructionUseBegin()[1])) - (uint32) inStart) & 0x03FFFFFF); *curPC++ = makeDForm(32, 2, 1, 20); // stw rtoc, 20(sp) } #endif // _H_PPCCALLS