Files
sousa-gecko/ef/Compiler/CodeGenerator/md/ppc/PPC601AppleMacOS_Support.cpp
T

476 lines
15 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):
*/
// PPC601AppleMacOS_Support.cpp.
//
// Scott M. Silver
//
// Support for the "Just-In-Time" part of the compiler
// Also a handful of routines for dealing with PowerPC linkage oddities (ptr gl, etc)
#include "NativeCodeCache.h"
#include "NativeFormatter.h"
#include "PPC601AppleMacOS_Support.h"
#include "NativeFormatter.h"
#include "AssembledInstructions.h"
#include "JavaObject.h"
// All volatile registers
struct VolatileRegisters
{
Uint32 r3; Uint32 r4; Uint32 r5; Uint32 r6;
Uint32 r7; Uint32 r8; Uint32 r9; Uint32 r10;
Flt64 fp0; Flt64 fp1; Flt64 fp2; Flt64 fp3;
Flt64 fp4; Flt64 fp5; Flt64 fp6; Flt64 fp7;
Flt64 fp8; Flt64 fp9; Flt64 fp10; Flt64 fp11;
Flt64 fp12; Flt64 fp13; Flt64 r31;
};
static void*
formatDynamicPtrGl(void* inStart, uint8 inRegister);
static void
formatCompileStubPtrGl(void* inStart);
asm static void
locate();
asm void
saveVolatiles(VolatileRegisters *vr);
asm void
restoreVolatiles(VolatileRegisters *vr);
const uint32 kDynamicPtrGlBytes = 20; // see formatDynamicPtrGl
// formatDynamicPtrGl
//
// Output some pointer glue through register inRegister
// beginning at inStart in memory. We require that
// the size of the buffer be at least kDynamicPtrGlBytes
// long. This kind of pointer glue is used for
// dispatching v-table based (dynamic) calls.
static void*
formatDynamicPtrGl(void* inStart, Uint8 inRegister)
{
Uint32* curPC = (Uint32*) inStart;
*curPC++ = makeDForm(36, 2, 1, 20); // stw rtoc, 20(sp)
*curPC++ = makeDForm(32, 0, inRegister, 0); // lwz r0, 0(inRegister)
*curPC++ = makeDForm(32, 2, inRegister, 4); // lwz rtoc, 4(inRegister);
*curPC++ = kMtctrR0; // mtctr r0
*curPC++ = kBctr; // bctr
return (curPC);
}
// formatCrossTocPtrGl
//
// Output cross-toc ptr gl beginning at inStart.
// Use inTOCOffset at the offset in the TOC to find
// the callee's TVector ptr. The buffer beginning at inStart
// must be at least kCrossTocPtrGlBytes long.
void*
formatCrossTocPtrGl(void* inStart, int16 inTOCOffset)
{
uint32* curPC = (uint32*) inStart;
*curPC++ = makeDForm(32, 12, 2, inTOCOffset); // lwz r12, offset(rtoc)
*curPC++ = makeDForm(36, 2, 1, 20); // stw rtoc, 20(sp)
*curPC++ = makeDForm(32, 0, 12, 0); // lwz r0, 0(r12)
*curPC++ = makeDForm(32, 2, 12, 4); // lwz rtoc, 4(r12);
*curPC++ = kMtctrR0; // mtctr r0
*curPC++ = kBctr; // bctr
return (curPC);
}
const uint32 kCompileStubPtrGlBytes = 12; // see formatCompileStubPtrGl
// formatCompileStubPtrGl
//
// Output a short stub beginng at inStart which loads
// the address of "locate" and jumps to it. The TOC must be set to
// the system's TOC before this routine is called (which is why a compile stub
// has a TVector with it's TOC set to the system TOC). inStart is
// assumbed to be a buffer of atleast kCompileStubPtrGlBytes.
static void
formatCompileStubPtrGl(void* inStart)
{
uint32* curPC = (uint32*) inStart;
// this is some cruft to locate the offset (from C) to locate's TVector
uint8* startTOC = ((uint8**) formatCompileStubPtrGl)[1]; // grab our TOC
uint8* compileStubFunction = (uint8*) locate; // grab &locate (which is ptr to TVector)
*curPC++ = makeDForm(32, 0, 2, compileStubFunction - startTOC); // lwz r0, locate(RTOC)
*curPC++ = kMtctrR0; // mtctr r0
*curPC++ = kBctr; // bctr
}
// DynamicPtrGlManager
//
// Manages a hunk of dynamic ptr gl
// FIX-ME synchronization
class DynamicPtrGlManager
{
public:
enum
{
kFirstPossibleRegister = 4, // since r3 must be the this ptr and can never be used for dynamic dispatching
kLastPossibleRegister = 31,
kTotalPtrGls = kLastPossibleRegister - kFirstPossibleRegister + 1
};
DynamicPtrGlManager()
{
void* allPtrGls = NativeCodeCache::getCache().acquireMemory(kTotalPtrGls * kDynamicPtrGlBytes);
mPtrGl = new void*[kTotalPtrGls];
void** curPtrGl = mPtrGl;
for (int curReg = kFirstPossibleRegister; curReg < kLastPossibleRegister + 1; curReg++)
{
*curPtrGl++ = allPtrGls;
allPtrGls = formatDynamicPtrGl(allPtrGls, curReg);
}
}
void *
getGl(void* DEBUG_ONLY(inFromWhere), Uint8 inWhichRegister)
{
#ifdef DEBUG
Int32 dummy;
#endif
assert(inWhichRegister >= kFirstPossibleRegister && inWhichRegister <= kLastPossibleRegister);
// FIX-ME allocate new ptr glues if this fails
assert(canBePCRelative(inFromWhere, mPtrGl[inWhichRegister - kFirstPossibleRegister], dummy));
return (mPtrGl[inWhichRegister - kFirstPossibleRegister]);
}
void** mPtrGl;
};
DynamicPtrGlManager sPtrGlueManager;
void* getDynamicPtrGl(void* inFromWhere, Uint8 inWhichRegister)
{
return (sPtrGlueManager.getGl(inFromWhere, inWhichRegister));
}
// getCompileStubPtrGl
//
// Locate a compile stub ptr gl withing pc-relative range of inFromWhere
// If one does not exist, create one. Assume that the newly created
// one will be in range. We also assume we will be reusing this often
// due to (hopefully) monotonically increasing addresses of new blocks
// of memory. FIX-ME?
//
// sMostRecentCompileStubPtrGl is maintained such that it is a single
// entry cache for the last CompileStubPtrGl we generated.
//
// A compileStubPtrGl is a sequence of instructions which jumps to
// the "locate" function which, in some way, locates/compiles/loads, etc
// a currently uncompiled method.
void* getCompileStubPtrGl(void* inFromWhere)
{
Int32 dummyOffset;
static void* sMostRecentCompileStubPtrGl = NULL; // This is a global!
// make a new ptr glue for the compile stub
if ((sMostRecentCompileStubPtrGl == NULL) || (!canBePCRelative(inFromWhere, sMostRecentCompileStubPtrGl, dummyOffset)))
{
void* newPtrGl = NativeCodeCache::getCache().acquireMemory(kCompileStubPtrGlBytes);
formatCompileStubPtrGl(newPtrGl);
sMostRecentCompileStubPtrGl = newPtrGl;
}
// assert that we actually succeeded in our goal, this really
// should result in programattic failure (abort, etc)
bool compileStubAssertion = canBePCRelative(inFromWhere, sMostRecentCompileStubPtrGl, dummyOffset);
assert(compileStubAssertion);
return (sMostRecentCompileStubPtrGl);
}
// _MD_createTVector
//
// Create a "transition vector" a two word entry containing a ptr to inFunctionMemory
// (the beginning of a compiled function) and a second word containing the environment
// or TOC pointer...currently we always use the system TOC. All other TVector's should
// be acquired from the Formatter objects.
void* _MD_createTVector(const MethodDescriptor& /*inMethodDescriptor*/, void* inFunctionMemory)
{
TVector* newTVector = (TVector*) NativeCodeCache::getCache().acquireMemory(sizeof(TVector));
newTVector->functionPtr = inFunctionMemory;
newTVector->toc = *((void**)_MD_createTVector + 1);
return (newTVector);
}
// BackPatchInfo
//
// little structure used to communicate between the assembly
// stub and the actual compilation and backpatching of the caller
struct BackPatchInfo
{
void** callerTOC; // caller's TOC
JavaObject* thisPtr; // callee this ptr (if a dynamic dispatch, otherwise undefined)
};
// backPatchMethod
//
// Cause the caller to call inMethodAddress next time the call instruction
// is encountered (instead of going through the stub). inLastPC is the next
// PC to be executed upon return from the intended callee (just after the call
// instruction. inUserData is assumed to point to a BackPatchInfo*. (above)
//
// The trick is, that this has to be atomic. That is, atomic from the standpoint
// that any thread entering the caller must not crash, but it would be ok for the
// thread to end up in the compile stub.
//
// For backpatching vtables, we call a routine on the method object which
// does all our dirty work. (It's non-trivial since the same method can occupy
// different positions in a vtable due to interfaces).
// We do the work for static dispatches. Our strategy is to try to use the cheapest
// call-like instruction. See the "General Idea" section at the top of this file
// for details of this strategy.
void* backPatchMethod(void* inMethodAddress, void* inLastPC, void* inUserData)
{
TVector* methodTVector = (TVector*) inMethodAddress;
Uint32* nopOrRestoreToc = (Uint32*) inLastPC;
BackPatchInfo* backPatchInfo = (BackPatchInfo*) inUserData;
// find bl
Uint32* blToPtrGlue = nopOrRestoreToc - 1;
assert ((*blToPtrGlue & kBl) == kBl);
// see where the destination of this bl -- it must be some kind of ptrgl
Int16 offsetToPtrGlue = (Int16) (*blToPtrGlue & 0x000FFFC);
Uint32* ptrGlue = (Uint32*) (((Uint8*) (nopOrRestoreToc - 1)) + offsetToPtrGlue);
if (*ptrGlue == makeDForm(36, 2, 1, 20)) // stw rtoc, 20(sp)
{
// dynamic call
Uint32* dynamicLookup = blToPtrGlue - 1;
assert ((*dynamicLookup & makeDForm(32, 0, 0, 0)) == makeDForm(32, 0, 0, 0)); // lwz r?, offset(rtoc)
Int16 vtableOffset = *dynamicLookup & 0xFFFF; // grab offset in table
// replace value in vtable
((void**)&backPatchInfo->thisPtr->type)[vtableOffset >> 2] = inMethodAddress;
}
else
{
// static call
assert ((*ptrGlue & makeDForm(32, 12, 2, 0)) == makeDForm(32, 12, 2, 0)); // lwz r12, offset(rtoc)
Int32 offsetToCallee;
bool sameTOC = (backPatchInfo->callerTOC == methodTVector->toc);
// first try an absolute branch
// then try a pc-relative branch
// last use the ptr glue (just change the entry in the TOC)
if (sameTOC && (Uint32) methodTVector->functionPtr < 0x3FFFFFF)
{
*blToPtrGlue = kBla | ( (Uint32) methodTVector->functionPtr & 0x03FFFFFF);
blToPtrGlue[1] = kNop;
}
else if (sameTOC && canBePCRelative(blToPtrGlue, methodTVector->functionPtr, offsetToCallee))
{
*blToPtrGlue = (kBl | offsetToCallee); // change bl to branch directly to callee
blToPtrGlue[1] = kNop; // don't restore toc, same toc
}
else
{
Int16 tocOffset = *ptrGlue & 0xFFFF; // grab offset in table
backPatchInfo->callerTOC[tocOffset] = inMethodAddress; // now change the ptr in the table
}
}
return (inMethodAddress);
}
// locate
//
// Assembly glue called by the locateStub to actually locate/compile/etc a
// method and back patch the caller. We save all volatiles (and
// of course save non-volatiles if we use them), fill in a BackPatchInfo
// object, and hand off control to compileAndBackPatchMethod. Upon return
// we restore volatiles, fix up the stack (so it appears we were at the
// callsite in the caller), and jump to the intended callee (returned by
// compileAndBackPatchMethod).
//
// Only one parameter is assumed, r11 = ptr to CacheEntry of method to be located.
// We chose r11 because it is volatile, but not used by any ptr-gl generated by
// the Metrowerks Compilers. We may have to do something slightly different for
// AIX. We may just push the cacheEntry on the stack in the stub, and fix up
// the stack in here.
//
// Currently we only "locate" methods by compilation
asm static void locate()
{
register CacheEntry* cacheEntry;
register void* oldLR;
register TVector* compiledMethod;
BackPatchInfo backPatchInfo;
VolatileRegisters volatileRegisters;
fralloc
mr cacheEntry, r11 // save r11 somewhere else, in case we end up in ptrgl somewhere
mflr oldLR
// save off volatiles
stw r3, backPatchInfo.thisPtr // r3 is saved in backPatchInfo (it is the thisPtr sometimes)
la r3, volatileRegisters // save rest of volatiles
bl saveVolatiles
// fill in the rest of backPatchInfo
lwz r5, 260(SP) // caller's RTOC (260 is a special constant, dependent on amt of stack space used in this function)
stw r5, backPatchInfo.callerTOC
// compileAndBackPatch
mr r3, cacheEntry
mr r4, oldLR
la r5, backPatchInfo
bl compileAndBackPatchMethod // r3 = TVector to actual method
mr compiledMethod, r3 // compiledMethod = TVector to actual method
// restore volatiles
la r3, volatileRegisters
bl restoreVolatiles
lwz r3, backPatchInfo.thisPtr // restore r3
// jmp to the real callee
lwz r0, compiledMethod->functionPtr // r0 = ptr to method
lwz RTOC, compiledMethod->toc // RTOC = toc of compiled method
mtctr r0 // ctr = ptr to method
frfree
bctr
}
// saveVolatiles
//
// Save all non-volatiles except r3 (because it is used for the incoming parameter)
asm void saveVolatiles(register VolatileRegisters *vr)
{
// stw r3, vr->r3
stfd fp0, vr->fp0
stw r4, vr->r4
stfd fp1, vr->fp1
stw r5, vr->r5
stfd fp2, vr->fp2
stw r6, vr->r6
stfd fp3, vr->fp3
stw r7, vr->r7
stfd fp4, vr->fp4
stw r8, vr->r8
stfd fp5, vr->fp5
stw r9, vr->r9
stfd fp6, vr->fp6
stw r10, vr->r10
stfd fp7, vr->fp7
stfd fp8, vr->fp8
stfd fp9, vr->fp9
stfd fp10, vr->fp10
stfd fp11, vr->fp11
stfd fp12, vr->fp12
stfd fp13, vr->fp13
blr
}
// restoreVolatiles
//
// Restore all non-volatiles except r3 (because it is used for the incoming parameter)
asm void restoreVolatiles(register VolatileRegisters *vr)
{
// lwz r3, vr->r3
lfd fp0, vr->fp0
lwz r4, vr->r4
lfd fp1, vr->fp1
lwz r5, vr->r5
lfd fp2, vr->fp2
lwz r6, vr->r6
lfd fp3, vr->fp3
lwz r7, vr->r7
lfd fp4, vr->fp4
lwz r8, vr->r8
lfd fp5, vr->fp5
lwz r9, vr->r9
lfd fp6, vr->fp6
lwz r10, vr->r10
lfd fp7, vr->fp7
lfd fp8, vr->fp8
lfd fp9, vr->fp9
lfd fp10, vr->fp10
lfd fp11, vr->fp11
lfd fp12, vr->fp12
lfd fp13, vr->fp13
blr
}
void *
generateNativeStub(NativeCodeCache& /*inCache*/, const CacheEntry& /*inCacheEntry*/, void * /*nativeFunction*/)
{
trespass("Not implemented");
return 0;
}
// generateCompileStub
//
// Genereate a kLocateStubBytes stub from memory acquired fromn inCache.
// The branches to some ptr-gl which jumps to the actual "locate" routine.
// It is assumed that there is already a reachable compileStubPtrGl, or one
// can be created by calling getCompileStubPtrGl.
extern void*
generateCompileStub(NativeCodeCache& inCache, const CacheEntry& inCacheEntry)
{
const size_t kLocateStubBytes = 12;
Uint32* stub = (Uint32*) inCache.acquireMemory(kLocateStubBytes);
Uint32* curPC = stub;
*curPC++ = makeDForm(15, 11, 0, ((Uint32) &inCacheEntry) >> 16); // addis r11, r0, hiword
*curPC++ = makeDForm(24, 11, 11, ((Uint32) &inCacheEntry) & 0xFFFF); // ori r11, r11, loword
*curPC = (kB | (((PRUptrdiff) getCompileStubPtrGl(curPC) - (PRUptrdiff) curPC) & 0x03FFFFFF)); // b compileStubPtrGl
return (_MD_createTVector(inCacheEntry.descriptor, stub));
}