318 lines
11 KiB
C++
318 lines
11 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):
|
|
*/
|
|
// PPCCalls.h
|
|
//
|
|
// Scott M. Silver
|
|
//
|
|
|
|
#ifndef _H_PPCCALLS
|
|
#define _H_PPCCALLS
|
|
|
|
#include "PPC601AppleMacOSEmitter.h"
|
|
#include "AssembledInstructions.h"
|
|
|
|
template<bool tHasIncomingStore, bool tHasOutgoingStore, bool tHasFunctionAddress, bool tIsDynamic>
|
|
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<bool tHasIncomingStore, bool tHasOutgoingStore>
|
|
class CallS :
|
|
public Call<tHasIncomingStore, tHasOutgoingStore, false, false>
|
|
{
|
|
public:
|
|
inline CallS(DataNode* inDataNode, Pool& inPool, uint8 inRegisterArguments, bool inHasReturnValue, PPCEmitter& inEmitter, void* inFunc) :
|
|
Call<tHasIncomingStore, tHasOutgoingStore, false, false>(inDataNode, inPool, inRegisterArguments, inHasReturnValue, inEmitter, inFunc) { }
|
|
};
|
|
|
|
#ifdef MANUAL_TEMPLATES
|
|
template class Call<true, true, false, false>;
|
|
template class Call<true, false, false, false>;
|
|
template class Call<false, false, false, false>;
|
|
template class Call<true, true, true, false>;
|
|
template class Call<true, true, true, true>;
|
|
#endif
|
|
|
|
typedef CallS<true, true> CallS_V;
|
|
typedef CallS<true, false> CallS_;
|
|
typedef CallS<false, false> CallS_C;
|
|
typedef Call<true, true, true, false> Call_;
|
|
|
|
|
|
// Dynamically dispatched call
|
|
class CallD_ :
|
|
public Call<true, true, true, true>
|
|
{
|
|
public:
|
|
inline CallD_(DataNode* inDataNode, Pool& inPool, uint8 inRegisterArguments, bool inHasReturnValue, PPCEmitter& inEmitter) :
|
|
Call<true, true, true, true>(inDataNode, inPool, inRegisterArguments, inHasReturnValue, inEmitter) { }
|
|
|
|
void formatToMemory(void* inStart, Uint32 /*inOffset*/, MdFormatter& inFormatter);
|
|
};
|
|
|
|
|
|
template<bool tHasIncomingStore, bool tHasOutgoingStore, bool tHasFunctionAddress, bool tIsDynamic> bool
|
|
Call<tHasIncomingStore, tHasOutgoingStore, tHasFunctionAddress, tIsDynamic>::
|
|
hasReturnValue(DataNode& inDataNode)
|
|
{
|
|
bool hasReturnValue = (inDataNode.getOutgoingEdgesBegin() + tHasOutgoingStore < inDataNode.getOutgoingEdgesEnd());
|
|
|
|
return (hasReturnValue);
|
|
}
|
|
|
|
template<bool tHasIncomingStore, bool tHasOutgoingStore, bool tHasFunctionAddress, bool tIsDynamic> uint8
|
|
Call<tHasIncomingStore, tHasOutgoingStore,tHasFunctionAddress, tIsDynamic>::
|
|
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<bool tHasIncomingStore, bool tHasOutgoingStore, bool tHasFunctionAddress, bool tIsDynamic>
|
|
Call<tHasIncomingStore, tHasOutgoingStore, tHasFunctionAddress, tIsDynamic>::
|
|
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<DataConsumer>& projectionConsumers = inDataNode->getConsumers();
|
|
DoublyLinkedList<DataConsumer>::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<kStackRegister>& storeParam = *new(inPool) StD_FixedDestRegister<kStackRegister>(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<bool tHasIncomingStore, bool tHasOutgoingStore, bool tHasFunctionAddress, bool tIsDynamic>
|
|
void Call<tHasIncomingStore, tHasOutgoingStore,tHasFunctionAddress, tIsDynamic>::
|
|
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
|