/* -*- 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));
}



