code:
/*
   AngelCode Scripting Library
   Copyright (c) 2003-2015 Andreas Jonsson

   This software is provided 'as-is', without any express or implied
   warranty. In no event will the authors be held liable for any
   damages arising from the use of this software.

   Permission is granted to anyone to use this software for any
   purpose, including commercial applications, and to alter it and
   redistribute it freely, subject to the following restrictions:

   1. The origin of this software must not be misrepresented; you
      must not claim that you wrote the original software. If you use
      this software in a product, an acknowledgment in the product
      documentation would be appreciated but is not required.

   2. Altered source versions must be plainly marked as such, and
      must not be misrepresented as being the original software.

   3. This notice may not be removed or altered from any source
      distribution.

   The original version of this library can be located at:
   http://www.angelcode.com/angelscript/

   Andreas Jonsson
   andreas@angelcode.com
*/

//
// as_callfunc_ppc.cpp
//
// These functions handle the actual calling of system functions
//
// This version is PPC specific
//

#include <stdio.h>

#include "as_config.h"

#ifndef AS_MAX_PORTABILITY
#ifdef AS_PPC

#include "as_callfunc.h"
#include "as_scriptengine.h"
#include "as_texts.h"
#include "as_tokendef.h"
#include "as_context.h"

#include <stdlib.h>

BEGIN_AS_NAMESPACE

// This part was originally written by Pecan Heber, June 2006, for
// use on MacOS X with 32bit PPC processor. He based the code on the
// code in as_callfunc_sh4.cpp

#define AS_PPC_MAX_ARGS 32

// The array used to send values to the correct places.
// Contains a byte of argTypes to indicate the register tYpe to load
// or zero if end of arguments
// The +1 is for when CallThis (object methods) is used
// Extra +1 when returning in memory
// Extra +1 in ppcArgsType to ensure zero end-of-args marker

// TODO: multithread: We need to remove these global variables for thread-safety

enum argTypes { ppcENDARG, ppcINTARG, ppcFLOATARG, ppcDOUBLEARG };
static asDWORD ppcArgs[2*AS_PPC_MAX_ARGS + 1 + 1];

// Using extern "C" because we use this symbol name in the assembly code
extern "C"
{
	static asBYTE ppcArgsType[2*AS_PPC_MAX_ARGS + 1 + 1 + 1];
}

// NOTE: these values are for PowerPC 32 bit.
#define PPC_LINKAGE_SIZE  (24)                                   // how big the PPC linkage area is in a stack frame
#define PPC_NUM_REGSTORE  (9)                                    // how many registers of the PPC we need to store/restore for ppcFunc()
#define PPC_REGSTORE_SIZE (4*PPC_NUM_REGSTORE)                   // how many bytes are required for register store/restore
#define EXTRA_STACK_SIZE  (PPC_LINKAGE_SIZE + PPC_REGSTORE_SIZE) // memory required, not including parameters, for the stack frame
#define PPC_STACK_SIZE(numParams)  (-( ( ((((numParams)<8)?8:(numParams))<<2) + EXTRA_STACK_SIZE + 15 ) & ~15 ))  // calculates the total stack size needed for ppcFunc64, must pad to 16bytes

// Loads all data into the correct places and calls the function.
// ppcArgsType is an array containing a byte type (enum argTypes) for each argument.
// stackArgSize is the size in bytes for how much data to put on the stack frame
extern "C" asQWORD ppcFunc(const asDWORD* argsPtr, int StackArgSize, asDWORD func);

asm(" .text\n"
	" .align 2\n"		  // align the code to 1 << 2 = 4 bytes
	" .globl _ppcFunc\n"
	"_ppcFunc:\n"

	// We're receiving the following parameters

	// r3 : argsPtr
	// r4 : StackArgSize
	// r5 : func

	// The following registers are used through out the function

	// r31 : the address of the label address, as reference for all other labels
	// r30 : temporary variable
	// r29 : arg list pointer
	// r28 : number of FPR registers used by the parameters
	// r27 : the function pointer that will be called
	// r26 : the location of the parameters for the call
	// r25 : arg type list pointer
	// r24 : temporary variable
	// r23 : number of GPR registers used by the parameters
	// r1  : this is stack pointer
	// r0  : temporary variable
	// f0  : temporary variable

	// We need to store some of the registers for restoral before returning to caller

	// lr - always stored in 8(r1) - this is the return address
	// cr - not required to be stored, but if it is, its place is in 4(r1) - this is the condition register
	// r1 - always stored in 0(r1) - this is the stack pointer
	// r11
	// r13 to r31
	// f14 to f31

	// Store register values and setup our stack frame
	" mflr  r0            \n"   // move the return address into r0
	" stw   r0, 8(r1)     \n"   // Store the return address on the stack
	" stmw  r23, -36(r1)  \n"   // Store registers r23 to r31 on the stack
	" stwux r1, r1, r4    \n"   // Increase the stack with the needed space and store the original value in the destination

	// Obtain an address that we'll use as our position of reference when obtaining addresses of other labels
	" bl    address         \n"
	"address:               \n"
	" mflr  r31             \n"

	// initial registers for the function
	" mr    r29, r3       \n"   // (r29) args list
	" mr    r27, r5       \n"   // load the function pointer to call.  func actually holds the pointer to our function
	" addi  r26, r1, 24   \n"   // setup the pointer to the parameter area to the function we're going to call
	" sub   r0, r0, r0    \n"   // zero out r0
	" mr    r23, r0       \n"   // zero out r23, which holds the number of used GPR registers
	" mr    r28, r0       \n"   // zero our r22, which holds the number of used float registers

	// load the global ppcArgsType which holds the types of arguments for each argument
	" addis r25, r31, ha16(_ppcArgsType - address) \n" // load the upper 16 bits of the address to r25
	" la    r25, lo16(_ppcArgsType - address)(r25) \n" // load the lower 16 bits of the address to r25
	" subi  r25, r25, 1    \n"   // since we increment r25 on its use, we'll pre-decrement it

	// loop through the arguments
	"ppcNextArg:           \n"
	" addi r25, r25, 1     \n"   // increment r25, our arg type pointer
	// switch based on the current argument type (0:end, 1:int, 2:float 3:double)
	" lbz  r24, 0(r25)     \n"   // load the current argument type (it's a byte)
	" mulli r24, r24, 4    \n"   // our jump table has 4 bytes per case (1 instruction)
	" addis r30, r31, ha16(ppcTypeSwitch - address) \n"   // load the address of the jump table for the switch
	" la    r30, lo16(ppcTypeSwitch - address)(r30) \n"

	" add    r0, r30, r24  \n"   // offset by our argument type
	" mtctr  r0            \n"   // load the jump address into CTR
	" bctr                 \n"   // jump into the jump table/switch
	" nop                  \n"

	// the jump table/switch based on the current argument type
	"ppcTypeSwitch:        \n"
	" b ppcArgsEnd         \n"
	" b ppcArgIsInteger    \n"
	" b ppcArgIsFloat      \n"
	" b ppcArgIsDouble     \n"

	// when we get here we have finished processing all the arguments
	// everything is ready to go to call the function
	"ppcArgsEnd:           \n"
	" mtctr r27            \n"   // the function pointer is stored in r27, load that into CTR
	" bctrl                \n"   // call the function.  We have to do it this way so that the LR gets the proper
	" nop                  \n"   // return value (the next instruction below).  So we have to branch from CTR instead of LR.

	// Restore registers and caller's stack frame, then return to caller
	" lwz   r1, 0(r1)      \n"   // restore the caller's stack pointer
	" lwz   r0, 8(r1)      \n"   // load in the caller's LR
	" mtlr  r0             \n"   // restore the caller's LR
	" lmw   r23, -36(r1)   \n"   // restore registers r23 to r31 from the stack
	" blr                  \n"   // return back to the caller
	" nop                  \n"

	// Integer argument (GPR register)
	"ppcArgIsInteger:             \n"
	" addis r30, r31, ha16(ppcLoadIntReg - address) \n" // load the address to the jump table for integer registers
	" la    r30, lo16(ppcLoadIntReg - address)(r30) \n"
	" mulli r0, r23, 8            \n"    // each item in the jump table is 2 instructions (8 bytes)
	" add   r0, r0, r30           \n"    // calculate ppcLoadIntReg[numUsedGPRRegs]
	" lwz   r30, 0(r29)           \n"    // load the next argument from the argument list into r30
	" cmpwi r23, 8                \n"    // we can only load GPR3 through GPR10 (8 registers)
	" bgt   ppcLoadIntRegUpd      \n"    // if we're beyond 8 GPR registers, we're in the stack, go there
	" mtctr r0                    \n"    // load the address of our ppcLoadIntReg jump table (we're below 8 GPR registers)
	" bctr                        \n"    // load the argument into a GPR register
	" nop                         \n"
	// jump table for GPR registers, for the first 8 GPR arguments
	"ppcLoadIntReg:               \n"
	" mr    r3, r30               \n"    // arg0 (to r3)
	" b     ppcLoadIntRegUpd      \n"
	" mr    r4, r30               \n"    // arg1 (to r4)
	" b     ppcLoadIntRegUpd      \n"
	" mr    r5, r30               \n"    // arg2 (to r5)
	" b     ppcLoadIntRegUpd      \n"
	" mr    r6, r30               \n"    // arg3 (to r6)
	" b     ppcLoadIntRegUpd      \n"
	" mr    r7, r30               \n"    // arg4 (to r7)
	" b     ppcLoadIntRegUpd      \n"
	" mr    r8, r30               \n"    // arg5 (to r8)
	" b     ppcLoadIntRegUpd      \n"
	" mr    r9, r30               \n"    // arg6 (to r9)
	" b     ppcLoadIntRegUpd      \n"
	" mr    r10, r30              \n"    // arg7 (to r10)
	" b     ppcLoadIntRegUpd      \n"
	// all GPR arguments still go on the stack
	"ppcLoadIntRegUpd:            \n"
	" stw   r30, 0(r26)           \n"    // store the argument into the next slot on the stack's argument list
	" addi  r23, r23, 1           \n"    // count a used GPR register
	" addi  r29, r29, 4           \n"    // move to the next argument on the list
	" addi  r26, r26, 4           \n"    // adjust our argument stack pointer for the next
	" b     ppcNextArg            \n"    // next argument

	// single Float argument
	"ppcArgIsFloat:\n"
	" addis r30, r31, ha16(ppcLoadFloatReg - address) \n" // get the base address of the float register jump table
	" la    r30, lo16(ppcLoadFloatReg - address)(r30) \n"
	" mulli r0, r28, 8           \n"     // each jump table entry is 8 bytes
	" add   r0, r0, r30          \n"     // calculate the offset to ppcLoadFloatReg[numUsedFloatReg]
	" lfs   f0, 0(r29)           \n"     // load the next argument as a float into f0
	" cmpwi r28, 13              \n"     // can't load more than 13 float/double registers
	" bgt   ppcLoadFloatRegUpd   \n"     // if we're beyond 13 registers, just fall to inserting into the stack
	" mtctr r0                   \n"     // jump into the float jump table
	" bctr                       \n"
	" nop                        \n"
	// jump table for float registers, for the first 13 float arguments
	"ppcLoadFloatReg:            \n"
	" fmr   f1, f0               \n"     // arg0 (f1)
	" b     ppcLoadFloatRegUpd   \n"
	" fmr   f2, f0               \n"     // arg1 (f2)
	" b     ppcLoadFloatRegUpd   \n"
	" fmr   f3, f0               \n"     // arg2 (f3)
	" b     ppcLoadFloatRegUpd   \n"
	" fmr   f4, f0               \n"     // arg3 (f4)
	" b     ppcLoadFloatRegUpd   \n"
	" fmr   f5, f0               \n"     // arg4 (f5)
	" b     ppcLoadFloatRegUpd   \n"
	" fmr   f6, f0               \n"     // arg5 (f6)
	" b     ppcLoadFloatRegUpd   \n"
	" fmr   f7, f0               \n"     // arg6 (f7)
	" b     ppcLoadFloatRegUpd   \n"
	" fmr   f8, f0               \n"     // arg7 (f8)
	" b     ppcLoadFloatRegUpd   \n"
	" fmr   f9, f0               \n"     // arg8 (f9)
	" b     ppcLoadFloatRegUpd   \n"
	" fmr   f10, f0              \n"     // arg9 (f10)
	" b     ppcLoadFloatRegUpd   \n"
	" fmr   f11, f0              \n"     // arg10 (f11)
	" b     ppcLoadFloatRegUpd   \n"
	" fmr   f12, f0              \n"     // arg11 (f12)
	" b     ppcLoadFloatRegUpd   \n"
	" fmr   f13, f0              \n"     // arg12 (f13)
	" b     ppcLoadFloatRegUpd   \n"
	" nop                        \n"
	// all float arguments still go on the stack
	"ppcLoadFloatRegUpd:         \n"
	" stfs  f0, 0(r26)           \n"     // store, as a single float, f0 (current argument) on to the stack argument list
	" addi  r23, r23, 1          \n"     // a float register eats up a GPR register
	" addi  r28, r28, 1          \n"     // ...and, of course, a float register
	" addi  r29, r29, 4          \n"     // move to the next argument in the list
	" addi  r26, r26, 4          \n"     // move to the next stack slot
	" b     ppcNextArg           \n"     // on to the next argument
	" nop                        \n"

	// double Float argument
	"ppcArgIsDouble:             \n"
	" addis r30, r31, ha16(ppcLoadDoubleReg - address) \n" // load the base address of the jump table for double registers
	" la    r30, lo16(ppcLoadDoubleReg - address)(r30) \n"
	" mulli r0, r28, 8           \n"     // each slot of the jump table is 8 bytes
	" add   r0, r0, r30          \n"     // calculate ppcLoadDoubleReg[numUsedFloatReg]
	" lfd   f0, 0(r29)           \n"     // load the next argument, as a double float, into f0
	" cmpwi r28, 13              \n"     // the first 13 floats must go into float registers also
	" bgt   ppcLoadDoubleRegUpd  \n"     // if we're beyond 13, then just put on to the stack
	" mtctr r0                   \n"     // we're under 13, first load our register
	" bctr                       \n"     // jump into the jump table
	" nop                        \n"
	// jump table for float registers, for the first 13 float arguments
	"ppcLoadDoubleReg:           \n"
	" fmr   f1, f0               \n"     // arg0 (f1)
	" b     ppcLoadDoubleRegUpd  \n"
	" fmr   f2, f0               \n"     // arg1 (f2)
	" b     ppcLoadDoubleRegUpd  \n"
	" fmr   f3, f0               \n"     // arg2 (f3)
	" b     ppcLoadDoubleRegUpd  \n"
	" fmr   f4, f0               \n"     // arg3 (f4)
	" b     ppcLoadDoubleRegUpd  \n"
	" fmr   f5, f0               \n"     // arg4 (f5)
	" b     ppcLoadDoubleRegUpd  \n"
	" fmr   f6, f0               \n"     // arg5 (f6)
	" b     ppcLoadDoubleRegUpd  \n"
	" fmr   f7, f0               \n"     // arg6 (f7)
	" b     ppcLoadDoubleRegUpd  \n"
	" fmr   f8, f0               \n"     // arg7 (f8)
	" b     ppcLoadDoubleRegUpd  \n"
	" fmr   f9, f0               \n"     // arg8 (f9)
	" b     ppcLoadDoubleRegUpd  \n"
	" fmr   f10, f0              \n"     // arg9 (f10)
	" b     ppcLoadDoubleRegUpd  \n"
	" fmr   f11, f0              \n"     // arg10 (f11)
	" b     ppcLoadDoubleRegUpd  \n"
	" fmr   f12, f0              \n"     // arg11 (f12)
	" b     ppcLoadDoubleRegUpd  \n"
	" fmr   f13, f0              \n"     // arg12 (f13)
	" b     ppcLoadDoubleRegUpd  \n"
	" nop                        \n"
	// all float arguments still go on the stack
	"ppcLoadDoubleRegUpd: \n"
	" stfd  f0, 0(r26)     \n"    // store f0, as a double, into the argument list on the stack
	" addi  r23, r23, 2   \n"     // a double float eats up two GPRs
	" addi  r28, r28, 1   \n"     // ...and, of course, a float
	" addi  r29, r29, 8   \n"     // increment to our next argument we need to process (8 bytes for the 64bit float)
	" addi  r26, r26, 8   \n"     // increment to the next slot on the argument list on the stack (8 bytes)
	" b     ppcNextArg    \n"     // on to the next argument
	" nop                 \n"
);

asDWORD GetReturnedFloat()
{
	asDWORD f;
	asm(" stfs f1, %0\n" : "=m"(f));
	return f;
}

asQWORD GetReturnedDouble()
{
	asQWORD f;
	asm(" stfd f1, %0\n" : "=m"(f));
	return f;
}

// puts the arguments in the correct place in the stack array. See comments above.
void stackArgs(const asDWORD *args, const asBYTE *argsType, int& numIntArgs, int& numFloatArgs, int& numDoubleArgs)
{
	int i;
	int argWordPos = numIntArgs + numFloatArgs + (numDoubleArgs*2);
	int typeOffset = numIntArgs + numFloatArgs + numDoubleArgs;

	int typeIndex;
	for( i = 0, typeIndex = 0; ; i++, typeIndex++ )
	{
		// store the type
		ppcArgsType[typeOffset++] = argsType[typeIndex];
		if( argsType[typeIndex] == ppcENDARG )
			break;

		switch( argsType[typeIndex] )
		{
		case ppcFLOATARG:
			// stow float
			ppcArgs[argWordPos] = args[i]; // it's just a bit copy
			numFloatArgs++;
			argWordPos++; //add one word
			break;

		case ppcDOUBLEARG:
			// stow double
			memcpy( &ppcArgs[argWordPos], &args[i], sizeof(double) ); // we have to do this because of alignment
			numDoubleArgs++;
			argWordPos+=2; //add two words
			i++;//doubles take up 2 argument slots
			break;

		case ppcINTARG:
			// stow register
			ppcArgs[argWordPos] = args[i];
			numIntArgs++;
			argWordPos++;
			break;
		}
	}

	// close off the argument list (if we have max args we won't close it off until here)
	ppcArgsType[typeOffset] = ppcENDARG;
}

static asQWORD CallCDeclFunction(const asDWORD* pArgs, const asBYTE *pArgsType, int argSize, asDWORD func, void *retInMemory)
{
	int baseArgCount = 0;
	if( retInMemory )
	{
		// the first argument is the 'return in memory' pointer
		ppcArgs[0]     = (asDWORD)retInMemory;
		ppcArgsType[0] = ppcINTARG;
		ppcArgsType[1] = ppcENDARG;
		baseArgCount   = 1;
	}

	// put the arguments in the correct places in the ppcArgs array
	int numTotalArgs = baseArgCount;
	if( argSize > 0 )
	{
		int intArgs = baseArgCount, floatArgs = 0, doubleArgs = 0;
		stackArgs( pArgs, pArgsType, intArgs, floatArgs, doubleArgs );
		numTotalArgs = intArgs + floatArgs + 2*doubleArgs; // doubles occupy two slots
	}
	else
	{
		// no arguments, cap the type list
		ppcArgsType[baseArgCount] = ppcENDARG;
	}

	// call the function with the arguments
	return ppcFunc( ppcArgs, PPC_STACK_SIZE(numTotalArgs), func );
}

// This function is identical to CallCDeclFunction, with the only difference that
// the value in the first parameter is the object (unless we are returning in memory)
static asQWORD CallThisCallFunction(const void *obj, const asDWORD* pArgs, const asBYTE *pArgsType, int argSize, asDWORD func, void *retInMemory )
{
	int baseArgCount = 0;
	if( retInMemory )
	{
		// the first argument is the 'return in memory' pointer
		ppcArgs[0]     = (asDWORD)retInMemory;
		ppcArgsType[0] = ppcINTARG;
		ppcArgsType[1] = ppcENDARG;
		baseArgCount = 1;
	}

	// the first argument is the 'this' of the object
	ppcArgs[baseArgCount]       = (asDWORD)obj;
	ppcArgsType[baseArgCount++] = ppcINTARG;
	ppcArgsType[baseArgCount]   = ppcENDARG;

	// put the arguments in the correct places in the ppcArgs array
	int numTotalArgs = baseArgCount;
	if( argSize > 0 )
	{
		int intArgs = baseArgCount, floatArgs = 0, doubleArgs = 0;
		stackArgs( pArgs, pArgsType, intArgs, floatArgs, doubleArgs );
		numTotalArgs = intArgs + floatArgs + 2*doubleArgs; // doubles occupy two slots
	}

	// call the function with the arguments
	return ppcFunc( ppcArgs, PPC_STACK_SIZE(numTotalArgs), func);
}

// This function is identical to CallCDeclFunction, with the only difference that
// the value in the last parameter is the object
// NOTE: on PPC the order for the args is reversed
static asQWORD CallThisCallFunction_objLast(const void *obj, const asDWORD* pArgs, const asBYTE *pArgsType, int argSize, asDWORD func, void *retInMemory)
{
	UNUSED_VAR(argSize);
	int baseArgCount = 0;
	if( retInMemory )
	{
		// the first argument is the 'return in memory' pointer
		ppcArgs[0]     = (asDWORD)retInMemory;
		ppcArgsType[0] = ppcINTARG;
		ppcArgsType[1] = ppcENDARG;
		baseArgCount = 1;
	}

	// stack any of the arguments
	int intArgs = baseArgCount, floatArgs = 0, doubleArgs = 0;
	stackArgs( pArgs, pArgsType, intArgs, floatArgs, doubleArgs );
	int numTotalArgs = intArgs + floatArgs + doubleArgs;

	// can we fit the object in at the end?
	if( numTotalArgs < AS_PPC_MAX_ARGS )
	{
		// put the object pointer at the end
		int argPos = intArgs + floatArgs + (doubleArgs * 2);
		ppcArgs[argPos]             = (asDWORD)obj;
		ppcArgsType[numTotalArgs++] = ppcINTARG;
		ppcArgsType[numTotalArgs]   = ppcENDARG;
	}

	// call the function with the arguments
	return ppcFunc( ppcArgs, PPC_STACK_SIZE(numTotalArgs), func );
}

asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &/*retQW2*/, void */*secondObject*/)
{
	// TODO: PPC does not yet support THISCALL_OBJFIRST/LAST

	// use a working array of types, we'll configure the final one in stackArgs
	asBYTE argsType[2*AS_PPC_MAX_ARGS + 1 + 1 + 1];
	memset( argsType, 0, sizeof(argsType));

	asCScriptEngine            *engine  = context->m_engine;
	asSSystemFunctionInterface *sysFunc = descr->sysFuncIntf;

	asQWORD  retQW           = 0;
	void    *func            = (void*)sysFunc->func;
	int      paramSize       = sysFunc->paramSize;
	asDWORD *vftable         = NULL;
	int      a, s;

	// convert the parameters that are < 4 bytes from little endian to big endian
	int argDwordOffset = 0;
	for( a = 0; a < (int)descr->parameterTypes.GetLength(); a++ )
	{
		int numBytes = descr->parameterTypes[a].GetSizeInMemoryBytes();
		if( numBytes >= 4 || descr->parameterTypes[a].IsReference() || descr->parameterTypes[a].IsObjectHandle() )
		{
			argDwordOffset += descr->parameterTypes[a].GetSizeOnStackDWords();
			continue;
		}

		// flip
		asASSERT( numBytes == 1 || numBytes == 2 );
		switch( numBytes )
		{
		case 1:
			{
				volatile asBYTE *bPtr = (asBYTE*)ARG_DW(args[argDwordOffset]);
				asBYTE t = bPtr[0];
				bPtr[0] = bPtr[3];
				bPtr[3] = t;
				t = bPtr[1];
				bPtr[1] = bPtr[2];
				bPtr[2] = t;
			}
			break;
		case 2:
			{
				volatile asWORD *wPtr = (asWORD*)ARG_DW(args[argDwordOffset]);
				asWORD t = wPtr[0];
				wPtr[0] = wPtr[1];
				wPtr[1] = t;
			}
			break;
		}
		argDwordOffset++;
	}

	// mark all float/double/int arguments
	if( !sysFunc->takesObjByVal )
	{
		for( s = 0, a = 0; s < (int)descr->parameterTypes.GetLength(); s++, a++ )
		{
			if( descr->parameterTypes[s].IsFloatType() && !descr->parameterTypes[s].IsReference() )
			{
				argsType[a] = ppcFLOATARG;
			}
			else if( descr->parameterTypes[s].IsDoubleType() && !descr->parameterTypes[s].IsReference() )
			{
				argsType[a] = ppcDOUBLEARG;
			}
			else
			{
				argsType[a] = ppcINTARG;
				if( descr->parameterTypes[s].GetSizeOnStackDWords() == 2 )
				{
					// Add an extra integer argument for the extra size
					a++;
					argsType[a] = ppcINTARG;
				}
			}
		}
	}

	asDWORD paramBuffer[64];
	if( sysFunc->takesObjByVal )
	{
		paramSize = 0;
		int spos = 0;
		int dpos = 1;

        int a = 0;
		for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ )
		{
			if( descr->parameterTypes[n].IsObject() && !descr->parameterTypes[n].IsObjectHandle() && !descr->parameterTypes[n].IsReference() )
			{
#ifdef COMPLEX_OBJS_PASSED_BY_REF
				if( descr->parameterTypes[n].GetTypeInfo()->flags & COMPLEX_MASK )
				{
					argsType[a++] = ppcINTARG;
					paramBuffer[dpos++] = args[spos++];
					paramSize++;
				}
				else
#endif
				{
					// TODO: Probably have to handle asOBJ_APP_FLOAT as a primitive

					// Copy the object's memory to the buffer
					memcpy( &paramBuffer[dpos], *(void**)(args+spos), descr->parameterTypes[n].GetSizeInMemoryBytes() );

					// Delete the original memory
					engine->CallFree(*(char**)(args+spos) );
					spos++;
					asUINT dwords = descr->parameterTypes[n].GetSizeInMemoryDWords();
					dpos += dwords;
					paramSize += dwords;
					for( asUINT i = 0; i < dwords; i++ )
						argsType[a++] = ppcINTARG;
				}
			}
			else
			{
				// Copy the value directly
				paramBuffer[dpos++] = args[spos++];
				if( descr->parameterTypes[n].IsFloatType() && !descr->parameterTypes[n].IsReference() )
					argsType[a++] = ppcFLOATARG;
				else if( descr->parameterTypes[n].IsDoubleType() && !descr->parameterTypes[n].IsReference() )
					argsType[a++] = ppcDOUBLEARG;
				else
					argsType[a++] = ppcINTARG;
				if( descr->parameterTypes[n].GetSizeOnStackDWords() > 1 )
				{
					paramBuffer[dpos++] = args[spos++];
					if( !descr->parameterTypes[n].IsDoubleType() ) // Double already knows it is 2 dwords
						argsType[a++] = ppcINTARG;
				}
				paramSize += descr->parameterTypes[n].GetSizeOnStackDWords();
			}
		}

		// Keep a free location at the beginning
		args = &paramBuffer[1];
	}

	int callConv = sysFunc->callConv;
	switch( callConv )
	{
	case ICC_CDECL:
	case ICC_CDECL_RETURNINMEM:
	case ICC_STDCALL:
	case ICC_STDCALL_RETURNINMEM:
		retQW = CallCDeclFunction( args, argsType, paramSize, (asDWORD)func, retPointer );
		break;
	case ICC_THISCALL:
	case ICC_THISCALL_RETURNINMEM:
		retQW = CallThisCallFunction(obj, args, argsType, paramSize, (asDWORD)func, retPointer );
		break;
	case ICC_VIRTUAL_THISCALL:
	case ICC_VIRTUAL_THISCALL_RETURNINMEM:
		// Get virtual function table from the object pointer
		vftable = *(asDWORD**)obj;
		retQW = CallThisCallFunction( obj, args, argsType, paramSize, vftable[asDWORD(func)>>2], retPointer );
		break;
	case ICC_CDECL_OBJLAST:
	case ICC_CDECL_OBJLAST_RETURNINMEM:
		retQW = CallThisCallFunction_objLast( obj, args, argsType, paramSize, (asDWORD)func, retPointer );
		break;
	case ICC_CDECL_OBJFIRST:
	case ICC_CDECL_OBJFIRST_RETURNINMEM:
		retQW = CallThisCallFunction( obj, args, argsType, paramSize, (asDWORD)func, retPointer );
		break;
	default:
		context->SetInternalException(TXT_INVALID_CALLING_CONVENTION);
	}

	// If the return is a float value we need to get the value from the FP register
	if( sysFunc->hostReturnFloat )
	{
		if( sysFunc->hostReturnSize == 1 )
			*(asDWORD*)&retQW = GetReturnedFloat();
		else
			retQW = GetReturnedDouble();
	}

	return retQW;
}

END_AS_NAMESPACE

#endif // AS_PPC
#endif // AS_MAX_PORTABILITY


code:
/*
   AngelCode Scripting Library
   Copyright (c) 2003-2016 Andreas Jonsson

   This software is provided 'as-is', without any express or implied
   warranty. In no event will the authors be held liable for any
   damages arising from the use of this software.

   Permission is granted to anyone to use this software for any
   purpose, including commercial applications, and to alter it and
   redistribute it freely, subject to the following restrictions:

   1. The origin of this software must not be misrepresented; you
      must not claim that you wrote the original software. If you use
      this software in a product, an acknowledgment in the product
      documentation would be appreciated but is not required.

   2. Altered source versions must be plainly marked as such, and
      must not be misrepresented as being the original software.

   3. This notice may not be removed or altered from any source
      distribution.

   The original version of this library can be located at:
   http://www.angelcode.com/angelscript/

   Andreas Jonsson
   andreas@angelcode.com
*/

//
// as_callfunc_ppc_64.cpp
//
// These functions handle the actual calling of system functions
//
// This version is 64 bit PPC specific
//

#include "as_config.h"

#ifndef AS_MAX_PORTABILITY
#ifdef AS_PPC_64
#if AS_PTR_SIZE == 2
// TODO: Add support for PPC 64bit platforms with 64bit pointers, for example Linux PPC64 (big endian) and PPC64 (little endian)
#error This code has not been prepared for PPC with 64bit pointers. Most likely the ABI is different
#else

#include "as_callfunc.h"
#include "as_scriptengine.h"
#include "as_texts.h"
#include "as_tokendef.h"
#include "as_context.h"

#include <stdio.h>
#include <stdlib.h>

#ifdef __SNC__
#include "ppu_asm_intrinsics.h"
#endif

BEGIN_AS_NAMESPACE

// This part was written and tested by Jeff Slutter
// from Reactor Zero, Abril, 2007, for PlayStation 3, which
// is a PowerPC 64bit based architecture. Even though it is
// 64bit it seems the pointer size is still 32bit.

// It still remains to be seen how well this code works
// on other PPC platforms, such as XBox 360, GameCube.

#define AS_PPC_MAX_ARGS 32

// The array used to send values to the correct places.
// Contains a byte of argTypes to indicate the register type to load
//    or zero if end of arguments
// The +1 is for when CallThis (object methods) is used
// Extra +1 when returning in memory
// Extra +1 in ppcArgsType to ensure zero end-of-args marker

// TODO: multithread: The global variables must be removed to make the code thread safe

extern "C"
{
	enum argTypes { ppcENDARG = 0, ppcINTARG = 1, ppcFLOATARG = 2, ppcDOUBLEARG = 3, ppcLONGARG = 4 };
	static asBYTE ppcArgsType[AS_PPC_MAX_ARGS + 1 + 1 + 1];
	static asDWORD ppcArgs[2*AS_PPC_MAX_ARGS + 1 + 1];
}

// NOTE: these values are for PowerPC 64 bit.  I'm sure things are different for PowerPC 32bit, but I don't have one.
//       I'm pretty sure that PPC 32bit sets up a stack frame slightly different (only 24 bytes for linkage area for instance)
#define PPC_LINKAGE_SIZE  (0x30)                                 // how big the PPC linkage area is in a stack frame
#define PPC_NUM_REGSTORE  (10)                                   // how many registers of the PPC we need to store/restore for ppcFunc64()
#define PPC_REGSTORE_SIZE (8*PPC_NUM_REGSTORE)                   // how many bytes are required for register store/restore
#define EXTRA_STACK_SIZE  (PPC_LINKAGE_SIZE + PPC_REGSTORE_SIZE) // memory required, not including parameters, for the stack frame
#define PPC_STACK_SIZE(numParams)  ( -(( ( (((numParams)<8)?8:(numParams))<<3) + EXTRA_STACK_SIZE + 15 ) & ~15) ) // calculates the total stack size needed for ppcFunc64, must pad to 16bytes

// This is PowerPC 64 bit specific
// Loads all data into the correct places and calls the function.
// ppcArgsType is an array containing a byte type (enum argTypes) for each argument.
// StackArgSizeInBytes is the size in bytes of the stack frame (takes into account linkage area, etc. must be multiple of 16)
extern "C" asQWORD ppcFunc64(const asDWORD* argsPtr, int StackArgSizeInBytes, asDWORD func);
asm(""
	".text\n"
	".align 4\n"
	".p2align 4,,15\n"
	".globl .ppcFunc64\n"
	".ppcFunc64:\n"

	// function prolog
	"std %r22, -0x08(%r1)\n"  // we need a register other than r0, to store the old stack pointer
	"mr    %r22, %r1\n"       // store the old stack pointer, for now (to make storing registers easier)
	"stdux %r1, %r1, %r4\n"   // atomically store and update the stack pointer for the new stack frame (in case of a signal/interrupt)
	"mflr %r0\n"              // get the caller's LR register
	"std %r0,  0x10(%r22)\n"  // store the caller's LR register
	"std %r23, -0x10(%r22)\n" //
	"std %r24, -0x18(%r22)\n" //
	"std %r25, -0x20(%r22)\n" //
	"std %r26, -0x28(%r22)\n" //
	"std %r27, -0x30(%r22)\n" //
	"std %r28, -0x38(%r22)\n" //
	"std %r29, -0x40(%r22)\n" //
	"std %r30, -0x48(%r22)\n" //
	"std %r31, -0x50(%r22)\n" //
	"std %r3, 0x30(%r22)\n"   // save our parameters
	"std %r4, 0x38(%r22)\n"   //
	"std %r5, 0x40(%r22)\n"   //
	"mr  %r31, %r1\n"         // functions tend to store the stack pointer here too

	// initial registers for the function
	"mr %r29, %r3\n"         // (r29) args list
	"lwz %r27, 0(%r5)\n"     // load the function pointer to call.  func actually holds the pointer to our function
	"addi %r26, %r1, 0x30\n" // setup the pointer to the parameter area to the function we're going to call
	"sub %r0,%r0,%r0\n"      // zero out r0
	"mr  %r23,%r0\n"         // zero out r23, which holds the number of used GPR registers
	"mr  %r22,%r0\n"         // zero our r22, which holds the number of used float registers

	// load the global ppcArgsType which holds the types of arguments for each argument
	"lis %r25, ppcArgsType@ha\n"       // load the upper 16 bits of the address to r25
	"addi %r25, %r25, ppcArgsType@l\n" // load the lower 16 bits of the address to r25
	"subi %r25, %r25, 1\n"             // since we increment r25 on its use, we'll pre-decrement it

	// loop through the arguments
	"ppcNextArg:\n"
	"addi %r25, %r25, 1\n"         // increment r25, our arg type pointer
	// switch based on the current argument type (0:end, 1:int, 2:float 3:double)
	"lbz %r24, 0(%r25)\n"          // load the current argument type (it's a byte)
	"mulli %r24, %r24, 4\n"        // our jump table has 4 bytes per case (1 instruction)
	"lis %r30, ppcTypeSwitch@ha\n" // load the address of the jump table for the switch
	"addi %r30, %r30, ppcTypeSwitch@l\n"
	"add %r0, %r30, %r24\n"        // offset by our argument type
	"mtctr %r0\n"                  // load the jump address into CTR
	"bctr\n"                       // jump into the jump table/switch
	"nop\n"
	// the jump table/switch based on the current argument type
	"ppcTypeSwitch:\n"
	"b ppcArgsEnd\n"
	"b ppcArgIsInteger\n"
	"b ppcArgIsFloat\n"
	"b ppcArgIsDouble\n"
	"b ppcArgIsLong\n"

	// when we get here we have finished processing all the arguments
	// everything is ready to go to call the function
	"ppcArgsEnd:\n"
	"mtctr %r27\n"        // the function pointer is stored in r27, load that into CTR
	"bctrl\n"             // call the function.  We have to do it this way so that the LR gets the proper
	"nop\n"               // return value (the next instruction below).  So we have to branch from CTR instead of LR.
	// when we get here, the function has returned, this is the function epilog
	"ld %r11,0x00(%r1)\n"    // load in the caller's stack pointer
	"ld %r0,0x10(%r11)\n"    // load in the caller's LR
	"mtlr %r0\n"             // restore the caller's LR
	"ld %r22, -0x08(%r11)\n" // load registers
	"ld %r23, -0x10(%r11)\n" //
	"ld %r24, -0x18(%r11)\n" //
	"ld %r25, -0x20(%r11)\n" //
	"ld %r26, -0x28(%r11)\n" //
	"ld %r27, -0x30(%r11)\n" //
	"ld %r28, -0x38(%r11)\n" //
	"ld %r29, -0x40(%r11)\n" //
	"ld %r30, -0x48(%r11)\n" //
	"ld %r31, -0x50(%r11)\n" //
	"mr %r1, %r11\n"         // restore the caller's SP
	"blr\n"                  // return back to the caller
	"nop\n"
	// Integer argument (GPR register)
	"ppcArgIsInteger:\n"
	"lis %r30,ppcLoadIntReg@ha\n"  // load the address to the jump table for integer registers
	"addi %r30, %r30, ppcLoadIntReg@l\n"
	"mulli %r0, %r23, 8\n"         // each item in the jump table is 2 instructions (8 bytes)
	"add %r0, %r0, %r30\n"         // calculate ppcLoadIntReg[numUsedGPRRegs]
	"lwz %r30,0(%r29)\n"           // load the next argument from the argument list into r30
	"cmpwi %r23, 8\n"              // we can only load GPR3 through GPR10 (8 registers)
	"bgt ppcLoadIntRegUpd\n"       // if we're beyond 8 GPR registers, we're in the stack, go there
	"mtctr %r0\n"                  // load the address of our ppcLoadIntReg jump table (we're below 8 GPR registers)
	"bctr\n"                       // load the argument into a GPR register
	"nop\n"
	// jump table for GPR registers, for the first 8 GPR arguments
	"ppcLoadIntReg:\n"
	"mr %r3,%r30\n"         // arg0 (to r3)
	"b ppcLoadIntRegUpd\n"
	"mr %r4,%r30\n"         // arg1 (to r4)
	"b ppcLoadIntRegUpd\n"
	"mr %r5,%r30\n"         // arg2 (to r5)
	"b ppcLoadIntRegUpd\n"
	"mr %r6,%r30\n"         // arg3 (to r6)
	"b ppcLoadIntRegUpd\n"
	"mr %r7,%r30\n"         // arg4 (to r7)
	"b ppcLoadIntRegUpd\n"
	"mr %r8,%r30\n"         // arg5 (to r8)
	"b ppcLoadIntRegUpd\n"
	"mr %r9,%r30\n"         // arg6 (to r9)
	"b ppcLoadIntRegUpd\n"
	"mr %r10,%r30\n"        // arg7 (to r10)
	"b ppcLoadIntRegUpd\n"

	// all GPR arguments still go on the stack
	"ppcLoadIntRegUpd:\n"
	"std  %r30,0(%r26)\n"   // store the argument into the next slot on the stack's argument list
	"addi %r23, %r23, 1\n"  // count a used GPR register
	"addi %r29, %r29, 4\n"  // move to the next argument on the list
	"addi %r26, %r26, 8\n"  // adjust our argument stack pointer for the next
	"b ppcNextArg\n"        // next argument

	// single Float argument
	"ppcArgIsFloat:\n"
	"lis %r30,ppcLoadFloatReg@ha\n"   // get the base address of the float register jump table
	"addi %r30, %r30, ppcLoadFloatReg@l\n"
	"mulli %r0, %r22 ,8\n"            // each jump table entry is 8 bytes
	"add %r0, %r0, %r30\n"            // calculate the offset to ppcLoadFloatReg[numUsedFloatReg]
	"lfs 0, 0(%r29)\n"                // load the next argument as a float into f0
	"cmpwi %r22, 13\n"                // can't load more than 13 float/double registers
	"bgt ppcLoadFloatRegUpd\n"        // if we're beyond 13 registers, just fall to inserting into the stack
	"mtctr %r0\n"                     // jump into the float jump table
	"bctr\n"
	"nop\n"
	// jump table for float registers, for the first 13 float arguments
	"ppcLoadFloatReg:\n"
	"fmr 1,0\n"                // arg0 (f1)
	"b ppcLoadFloatRegUpd\n"
	"fmr 2,0\n"                // arg1 (f2)
	"b ppcLoadFloatRegUpd\n"
	"fmr 3,0\n"                // arg2 (f3)
	"b ppcLoadFloatRegUpd\n"
	"fmr 4,0\n"                // arg3 (f4)
	"b ppcLoadFloatRegUpd\n"
	"fmr 5,0\n"                // arg4 (f5)
	"b ppcLoadFloatRegUpd\n"
	"fmr 6,0\n"                // arg5 (f6)
	"b ppcLoadFloatRegUpd\n"
	"fmr 7,0\n"                // arg6 (f7)
	"b ppcLoadFloatRegUpd\n"
	"fmr 8,0\n"                // arg7 (f8)
	"b ppcLoadFloatRegUpd\n"
	"fmr 9,0\n"                // arg8 (f9)
	"b ppcLoadFloatRegUpd\n"
	"fmr 10,0\n"               // arg9 (f10)
	"b ppcLoadFloatRegUpd\n"
	"fmr 11,0\n"               // arg10 (f11)
	"b ppcLoadFloatRegUpd\n"
	"fmr 12,0\n"               // arg11 (f12)
	"b ppcLoadFloatRegUpd\n"
	"fmr 13,0\n"               // arg12 (f13)
	"b ppcLoadFloatRegUpd\n"
	"nop\n"
	// all float arguments still go on the stack
	"ppcLoadFloatRegUpd:\n"
	"stfs 0, 0x04(%r26)\n"     // store, as a single float, f0 (current argument) on to the stack argument list
	"addi %r23, %r23, 1\n"     // a float register eats up a GPR register
	"addi %r22, %r22, 1\n"     // ...and, of course, a float register
	"addi %r29, %r29, 4\n"     // move to the next argument in the list
	"addi %r26, %r26, 8\n"     // move to the next stack slot
	"b ppcNextArg\n"           // on to the next argument
	"nop\n"
	// double Float argument
	"ppcArgIsDouble:\n"
	"lis %r30, ppcLoadDoubleReg@ha\n" // load the base address of the jump table for double registers
	"addi %r30, %r30, ppcLoadDoubleReg@l\n"
	"mulli %r0, %r22, 8\n"            // each slot of the jump table is 8 bytes
	"add %r0, %r0, %r30\n"            // calculate ppcLoadDoubleReg[numUsedFloatReg]
	"lfd 0, 0(%r29)\n"                // load the next argument, as a double float, into f0
	"cmpwi %r22,13\n"                 // the first 13 floats must go into float registers also
	"bgt ppcLoadDoubleRegUpd\n"       // if we're beyond 13, then just put on to the stack
	"mtctr %r0\n"                     // we're under 13, first load our register
	"bctr\n"                          // jump into the jump table
	"nop\n"
	// jump table for float registers, for the first 13 float arguments
	"ppcLoadDoubleReg:\n"
	"fmr 1,0\n"                // arg0 (f1)
	"b ppcLoadDoubleRegUpd\n"
	"fmr 2,0\n"                // arg1 (f2)
	"b ppcLoadDoubleRegUpd\n"
	"fmr 3,0\n"                // arg2 (f3)
	"b ppcLoadDoubleRegUpd\n"
	"fmr 4,0\n"                // arg3 (f4)
	"b ppcLoadDoubleRegUpd\n"
	"fmr 5,0\n"                // arg4 (f5)
	"b ppcLoadDoubleRegUpd\n"
	"fmr 6,0\n"                // arg5 (f6)
	"b ppcLoadDoubleRegUpd\n"
	"fmr 7,0\n"                // arg6 (f7)
	"b ppcLoadDoubleRegUpd\n"
	"fmr 8,0\n"                // arg7 (f8)
	"b ppcLoadDoubleRegUpd\n"
	"fmr 9,0\n"                // arg8 (f9)
	"b ppcLoadDoubleRegUpd\n"
	"fmr 10,0\n"               // arg9 (f10)
	"b ppcLoadDoubleRegUpd\n"
	"fmr 11,0\n"               // arg10 (f11)
	"b ppcLoadDoubleRegUpd\n"
	"fmr 12,0\n"               // arg11 (f12)
	"b ppcLoadDoubleRegUpd\n"
	"fmr 13,0\n"               // arg12 (f13)
	"b ppcLoadDoubleRegUpd\n"
	"nop\n"
	// all float arguments still go on the stack
	"ppcLoadDoubleRegUpd:\n"
	"stfd 0,0(%r26)\n"         // store f0, as a double, into the argument list on the stack
	"addi %r23, %r23, 1\n"     // a double float eats up one GPR
	"addi %r22, %r22, 1\n"     // ...and, of course, a float
	"addi %r29, %r29, 8\n"     // increment to our next argument we need to process (8 bytes for the 64bit float)
	"addi %r26, %r26, 8\n"     // increment to the next slot on the argument list on the stack (8 bytes)
	"b ppcNextArg\n"           // on to the next argument
	"nop\n"

	// Long (64 bit int) argument
	"ppcArgIsLong:\n"
	"lis %r30,ppcLoadLongReg@ha\n"  // load the address to the jump table for integer64
	"addi %r30, %r30, ppcLoadLongReg@l\n"
	"mulli %r0, %r23, 8\n"         // each item in the jump table is 2 instructions (8 bytes)
	"add %r0, %r0, %r30\n"         // calculate ppcLoadLongReg[numUsedGPRRegs]
	"ld  %r30,0(%r29)\n"           // load the next argument from the argument list into r30
	"cmpwi %r23, 8\n"              // we can only load GPR3 through GPR10 (8 registers)
	"bgt ppcLoadLongRegUpd\n"      // if we're beyond 8 GPR registers, we're in the stack, go there
	"mtctr %r0\n"                  // load the address of our ppcLoadLongReg jump table (we're below 8 GPR registers)
	"bctr\n"                       // load the argument into a GPR register
	"nop\n"
	// jump table for GPR registers, for the first 8 GPR arguments
	"ppcLoadLongReg:\n"
	"mr %r3,%r30\n"         // arg0 (to r3)
	"b ppcLoadLongRegUpd\n"
	"mr %r4,%r30\n"         // arg1 (to r4)
	"b ppcLoadLongRegUpd\n"
	"mr %r5,%r30\n"         // arg2 (to r5)
	"b ppcLoadLongRegUpd\n"
	"mr %r6,%r30\n"         // arg3 (to r6)
	"b ppcLoadLongRegUpd\n"
	"mr %r7,%r30\n"         // arg4 (to r7)
	"b ppcLoadLongRegUpd\n"
	"mr %r8,%r30\n"         // arg5 (to r8)
	"b ppcLoadLongRegUpd\n"
	"mr %r9,%r30\n"         // arg6 (to r9)
	"b ppcLoadLongRegUpd\n"
	"mr %r10,%r30\n"        // arg7 (to r10)
	"b ppcLoadLongRegUpd\n"

	// all GPR arguments still go on the stack
	"ppcLoadLongRegUpd:\n"
	"std  %r30,0(%r26)\n"   // store the argument into the next slot on the stack's argument list
	"addi %r23, %r23, 1\n"  // count a used GPR register
	"addi %r29, %r29, 8\n"  // move to the next argument on the list
	"addi %r26, %r26, 8\n"  // adjust our argument stack pointer for the next
	"b ppcNextArg\n"        // next argument
);

static asDWORD GetReturnedFloat(void)
{
	asDWORD f;
#ifdef __SNC__
	__stfs( __freg(1), 0, (void*)&f);
#else
	asm(" stfs 1, %0\n" : "=m"(f));
#endif
	return f;
}

static asQWORD GetReturnedDouble(void)
{
	asQWORD f;
#ifdef __SNC__
	__stfd( __freg(1), 0, (void*)&f);
#else
	asm(" stfd 1, %0\n" : "=m"(f));
#endif
	return f;
}

// puts the arguments in the correct place in the stack array. See comments above.
static void stackArgs( const asDWORD *args, const asBYTE *argsType, int &numIntArgs, int &numFloatArgs, int &numDoubleArgs, int &numLongArgs )
{
	// initialize our offset based on any already placed arguments
	int i;
	int argWordPos = numIntArgs + numFloatArgs + (numDoubleArgs*2) + (numLongArgs*2);
	int typeOffset = numIntArgs + numFloatArgs + numDoubleArgs + numLongArgs;

	int typeIndex;
	for( i = 0, typeIndex = 0; ; i++, typeIndex++ )
	{
		// store the type
		ppcArgsType[typeOffset++] = argsType[typeIndex];
		if( argsType[typeIndex] == ppcENDARG )
			break;

		switch( argsType[typeIndex] )
		{
		case ppcFLOATARG:
			{
				// stow float
				ppcArgs[argWordPos] = args[i]; // it's just a bit copy
				numFloatArgs++;
				argWordPos++; //add one word
			}
			break;

		case ppcDOUBLEARG:
			{
				// stow double
				memcpy( &ppcArgs[argWordPos], &args[i], sizeof(double) ); // we have to do this because of alignment
				numDoubleArgs++;
				argWordPos+=2; //add two words
				i++;//doubles take up 2 argument slots
			}
			break;

		case ppcINTARG:
			{
				// stow register
				ppcArgs[argWordPos] = args[i];
				numIntArgs++;
				argWordPos++;
			}
			break;

		case ppcLONGARG:
			{
				// stow long
				memcpy( &ppcArgs[argWordPos], &args[i], 8 ); // for alignment purposes, we use memcpy
				numLongArgs++;
				argWordPos += 2; // add two words
				i++; // longs take up 2 argument slots
			}
			break;
		}
	}

	// close off the argument list (if we have max args we won't close it off until here)
	ppcArgsType[typeOffset] = ppcENDARG;
}

static asQWORD CallCDeclFunction(const asDWORD* pArgs, const asBYTE *pArgsType, int argSize, asDWORD func, void *retInMemory)
{
	int baseArgCount = 0;
	if( retInMemory )
	{
		// the first argument is the 'return in memory' pointer
		ppcArgs[0]     = (asDWORD)retInMemory;
		ppcArgsType[0] = ppcINTARG;
		ppcArgsType[1] = ppcENDARG;
		baseArgCount   = 1;
	}

	// put the arguments in the correct places in the ppcArgs array
	int numTotalArgs = baseArgCount;
	if( argSize > 0 )
	{
		int intArgs = baseArgCount, floatArgs = 0, doubleArgs = 0, longArgs = 0;
		stackArgs( pArgs, pArgsType, intArgs, floatArgs, doubleArgs, longArgs );
		numTotalArgs = intArgs + floatArgs + doubleArgs + longArgs;
	}
	else
	{
		// no arguments, cap the type list
		ppcArgsType[baseArgCount] = ppcENDARG;
	}

	// call the function with the arguments
	return ppcFunc64( ppcArgs, PPC_STACK_SIZE(numTotalArgs), func );
}

// This function is identical to CallCDeclFunction, with the only difference that
// the value in the first parameter is the object (unless we are returning in memory)
static asQWORD CallThisCallFunction(const void *obj, const asDWORD* pArgs, const asBYTE *pArgsType, int argSize, asDWORD func, void *retInMemory )
{
	int baseArgCount = 0;
	if( retInMemory )
	{
		// the first argument is the 'return in memory' pointer
		ppcArgs[0]     = (asDWORD)retInMemory;
		ppcArgsType[0] = ppcINTARG;
		ppcArgsType[1] = ppcENDARG;
		baseArgCount   = 1;
	}

	// the first argument is the 'this' of the object
	ppcArgs[baseArgCount]       = (asDWORD)obj;
	ppcArgsType[baseArgCount++] = ppcINTARG;
	ppcArgsType[baseArgCount]   = ppcENDARG;

	// put the arguments in the correct places in the ppcArgs array
	int numTotalArgs = baseArgCount;
	if( argSize > 0 )
	{
		int intArgs = baseArgCount, floatArgs = 0, doubleArgs = 0, longArgs = 0;
		stackArgs( pArgs, pArgsType, intArgs, floatArgs, doubleArgs, longArgs );
		numTotalArgs = intArgs + floatArgs + doubleArgs + longArgs;
	}

	// call the function with the arguments
	return ppcFunc64( ppcArgs, PPC_STACK_SIZE(numTotalArgs), func);
}

// This function is identical to CallCDeclFunction, with the only difference that
// the value in the last parameter is the object
// NOTE: on PPC the order for the args is reversed
static asQWORD CallThisCallFunction_objLast(const void *obj, const asDWORD* pArgs, const asBYTE *pArgsType, int argSize, asDWORD func, void *retInMemory)
{
	UNUSED_VAR(argSize);
	int baseArgCount = 0;
	if( retInMemory )
	{
		// the first argument is the 'return in memory' pointer
		ppcArgs[0]     = (asDWORD)retInMemory;
		ppcArgsType[0] = ppcINTARG;
		ppcArgsType[1] = ppcENDARG;
		baseArgCount = 1;
	}

	// stack any of the arguments
	int intArgs = baseArgCount, floatArgs = 0, doubleArgs = 0, longArgs = 0;
	stackArgs( pArgs, pArgsType, intArgs, floatArgs, doubleArgs, longArgs );
	int numTotalArgs = intArgs + floatArgs + doubleArgs;

	// can we fit the object in at the end?
	if( numTotalArgs < AS_PPC_MAX_ARGS )
	{
		// put the object pointer at the end
		int argPos = intArgs + floatArgs + (doubleArgs * 2) + (longArgs *2);
		ppcArgs[argPos]             = (asDWORD)obj;
		ppcArgsType[numTotalArgs++] = ppcINTARG;
		ppcArgsType[numTotalArgs]   = ppcENDARG;
	}

	// call the function with the arguments
	return ppcFunc64( ppcArgs, PPC_STACK_SIZE(numTotalArgs), func );
}

// returns true if the given parameter is a 'variable argument'
inline bool IsVariableArgument( asCDataType type )
{
	return (type.GetTokenType() == ttQuestion) ? true : false;
}

asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &/*retQW2*/, void */*secondObject*/)
{
	// TODO: PPC 64 does not yet support THISCALL_OBJFIRST/LAST

	// use a working array of types, we'll configure the final one in stackArgs
	asBYTE argsType[AS_PPC_MAX_ARGS + 1 + 1 + 1];
	memset( argsType, 0, sizeof(argsType));

	asCScriptEngine *engine = context->m_engine;
	asSSystemFunctionInterface *sysFunc = descr->sysFuncIntf;

	int callConv = sysFunc->callConv;

	asQWORD  retQW           = 0;
	void    *func            = (void*)sysFunc->func;
	int      paramSize       = sysFunc->paramSize;
	asDWORD *vftable         = NULL;
	int      a;

	// convert the parameters that are < 4 bytes from little endian to big endian
	int argDwordOffset = 0;
	int totalArgumentCount = 0;

	for( a = 0; a < (int)descr->parameterTypes.GetLength(); ++a )
	{
		// get the size for the parameter
		int numBytes = descr->parameterTypes[a].GetSizeInMemoryBytes();
		++totalArgumentCount;

		// is this a variable argument?
		// for variable arguments, the typeID will always follow...but we know it is 4 bytes
		// so we can skip that parameter automatically.
		bool isVarArg = IsVariableArgument( descr->parameterTypes[a] );
		if( isVarArg )
		{
			++totalArgumentCount;
		}

		if( numBytes >= 4 || descr->parameterTypes[a].IsReference() || descr->parameterTypes[a].IsObjectHandle() )
		{
			// DWORD or larger parameter --- no flipping needed
			argDwordOffset += descr->parameterTypes[a].GetSizeOnStackDWords();
		}
		else
		{
			// flip
			asASSERT( numBytes == 1 || numBytes == 2 );
			switch( numBytes )
			{
			case 1:
				{
					volatile asBYTE *bPtr = (asBYTE*)ARG_DW(args[argDwordOffset]);
					asBYTE t = bPtr[0];
					bPtr[0] = bPtr[3];
					bPtr[3] = t;
					t = bPtr[1];
					bPtr[1] = bPtr[2];
					bPtr[2] = t;
				}
				break;
			case 2:
				{
					volatile asWORD *wPtr = (asWORD*)ARG_DW(args[argDwordOffset]);
					asWORD t = wPtr[0];
					wPtr[0] = wPtr[1];
					wPtr[1] = t;
				}
				break;
			}
			++argDwordOffset;
		}

		if( isVarArg )
		{
			// skip the implicit typeID
			++argDwordOffset;
		}
	}

	asASSERT( totalArgumentCount <= AS_PPC_MAX_ARGS );

	// mark all float/double/int arguments
	int argIndex = 0;
	for( a = 0; a < (int)descr->parameterTypes.GetLength(); ++a, ++argIndex )
	{
		// get the base type
		argsType[argIndex] = ppcINTARG;
		if( descr->parameterTypes[a].IsFloatType() && !descr->parameterTypes[a].IsReference() )
		{
			argsType[argIndex] = ppcFLOATARG;
		}
		if( descr->parameterTypes[a].IsDoubleType() && !descr->parameterTypes[a].IsReference() )
		{
			argsType[argIndex] = ppcDOUBLEARG;
		}
		if( descr->parameterTypes[a].GetSizeOnStackDWords() == 2 && !descr->parameterTypes[a].IsDoubleType() && !descr->parameterTypes[a].IsReference() )
		{
			argsType[argIndex] = ppcLONGARG;
		}

		// if it is a variable argument, account for the typeID
		if( IsVariableArgument(descr->parameterTypes[a]) )
		{
			// implicitly add another parameter (AFTER the parameter above), for the TypeID
			argsType[++argIndex] = ppcINTARG;
		}
	}
	asASSERT( argIndex == totalArgumentCount );

	asDWORD paramBuffer[64];
	if( sysFunc->takesObjByVal )
	{
		paramSize = 0;
		int spos = 0;
		int dpos = 1;

		for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ )
		{
			if( descr->parameterTypes[n].IsObject() && !descr->parameterTypes[n].IsObjectHandle() && !descr->parameterTypes[n].IsReference() &&
				!(descr->parameterTypes[n].GetTypeInfo()->flags & asOBJ_APP_ARRAY) )
			{
#ifdef COMPLEX_OBJS_PASSED_BY_REF
				if( descr->parameterTypes[n].GetTypeInfo()->flags & COMPLEX_MASK )
				{
					paramBuffer[dpos++] = args[spos++];
					++paramSize;
				}
				else
#endif
				{
					// NOTE: we may have to do endian flipping here

					// Copy the object's memory to the buffer
					memcpy( &paramBuffer[dpos], *(void**)(args+spos), descr->parameterTypes[n].GetSizeInMemoryBytes() );

					// Delete the original memory
					engine->CallFree( *(char**)(args+spos) );
					spos++;
					dpos += descr->parameterTypes[n].GetSizeInMemoryDWords();
					paramSize += descr->parameterTypes[n].GetSizeInMemoryDWords();
				}
			}
			else
			{
				// Copy the value directly
				paramBuffer[dpos++] = args[spos++];
				if( descr->parameterTypes[n].GetSizeOnStackDWords() > 1 )
				{
					paramBuffer[dpos++] = args[spos++];
				}
				paramSize += descr->parameterTypes[n].GetSizeOnStackDWords();
			}

			// if this was a variable argument parameter, then account for the implicit typeID
			if( IsVariableArgument( descr->parameterTypes[n] ) )
			{
				// the TypeID is just a DWORD
				paramBuffer[dpos++] = args[spos++];
				++paramSize;
			}
		}

		// Keep a free location at the beginning
		args = &paramBuffer[1];
	}

	// one last verification to make sure things are how we expect
	switch( callConv )
	{
	case ICC_CDECL:
	case ICC_CDECL_RETURNINMEM:
	case ICC_STDCALL:
	case ICC_STDCALL_RETURNINMEM:
		retQW = CallCDeclFunction( args, argsType, paramSize, (asDWORD)func, retPointer );
		break;
	case ICC_THISCALL:
	case ICC_THISCALL_RETURNINMEM:
		retQW = CallThisCallFunction(obj, args, argsType, paramSize, (asDWORD)func, retPointer );
		break;
	case ICC_VIRTUAL_THISCALL:
	case ICC_VIRTUAL_THISCALL_RETURNINMEM:
		// Get virtual function table from the object pointer
		vftable = *(asDWORD**)obj;
		retQW = CallThisCallFunction( obj, args, argsType, paramSize, vftable[asDWORD(func)>>2], retPointer );
		break;
	case ICC_CDECL_OBJLAST:
	case ICC_CDECL_OBJLAST_RETURNINMEM:
		retQW = CallThisCallFunction_objLast( obj, args, argsType, paramSize, (asDWORD)func, retPointer );
		break;
	case ICC_CDECL_OBJFIRST:
	case ICC_CDECL_OBJFIRST_RETURNINMEM:
		retQW = CallThisCallFunction( obj, args, argsType, paramSize, (asDWORD)func, retPointer );
		break;
	default:
		context->SetInternalException(TXT_INVALID_CALLING_CONVENTION);
	}

	if( sysFunc->hostReturnFloat )
	{
		// If the return is a float value we need to get the value from the FP register
		if( sysFunc->hostReturnSize == 1 )
			*(asDWORD*)&retQW = GetReturnedFloat();
		else
			retQW = GetReturnedDouble();
	}
	else if( sysFunc->hostReturnSize == 1 )
	{
		// Move the bits to the higher value to compensate for the adjustment that the caller does
		retQW <<= 32;
	}

	return retQW;
}

END_AS_NAMESPACE

#endif // AS_PTR_SIZE == 2
#endif // AS_PPC_64
#endif // AS_MAX_PORTABILITY

repo_name: Antidote/openrpgeditor

license: mit


code:
/*
   AngelCode Scripting Library
   Copyright (c) 2024 Andreas Jonsson

   This software is provided 'as-is', without any express or implied
   warranty. In no event will the authors be held liable for any
   damages arising from the use of this software.

   Permission is granted to anyone to use this software for any
   purpose, including commercial applications, and to alter it and
   redistribute it freely, subject to the following restrictions:

   1. The origin of this software must not be misrepresented; you
      must not claim that you wrote the original software. If you use
      this software in a product, an acknowledgment in the product
      documentation would be appreciated but is not required.

   2. Altered source versions must be plainly marked as such, and
      must not be misrepresented as being the original software.

   3. This notice may not be removed or altered from any source
      distribution.

   The original version of this library can be located at:
   http://www.angelcode.com/angelscript/

   Andreas Jonsson
   andreas@angelcode.com
*/

//
// as_callfunc_riscv64.cpp
//
// These functions handle the actual calling of system functions
// on the 64bit RISC-V call convention used for Linux
//
// ref: https://riscv.org/wp-content/uploads/2017/05/riscv-spec-v2.2.pdf
//

#include "as_config.h"

#ifndef AS_MAX_PORTABILITY
#ifdef AS_RISCV64

#include "as_callfunc.h"
#include "as_scriptengine.h"
#include "as_texts.h"
#include "as_tokendef.h"
#include "as_context.h"

BEGIN_AS_NAMESPACE

// retfloat == 0: the called function doesn't return a float value
// retfloat == 1: the called function returns a float/double value
// argValues is an array with all the values, the first 8 values will go to a0-a7 registers, the next 8 values will go to fa0-fa7 registers, and the remaining goes to the stack
// numRegularValues holds the number of regular values to put in a0-a7 registers
// numFloatValues hold the number of float values to put in fa0-fa7 registers
// numStackValues hold the number of values to push on the stack
struct asDBLQWORD { asQWORD qw1, qw2; };
extern "C" asDBLQWORD CallRiscVFunc(asFUNCTION_t func, int retfloat, asQWORD *argValues, int numRegularValues, int numFloatValues, int numStackValues);

// a0-a7 used for non-float values
// fa0-fa7 used for float values
// if more than 8 float values and there is space left in regular registers then those are used
// rest of the values are pushed on the stack
const asUINT maxRegularRegisters = 8;
const asUINT maxFloatRegisters = 8;
const asUINT maxValuesOnStack = 48 - maxRegularRegisters - maxFloatRegisters;

bool PushToFloatRegs(asQWORD val, asQWORD *argValues, asUINT &numFloatRegistersUsed, asUINT &numRegularRegistersUsed, asUINT &numStackValuesUsed)
{
	asQWORD* stackValues = argValues + maxRegularRegisters + maxFloatRegisters;

	if (numFloatRegistersUsed < maxFloatRegisters)
	{
		argValues[maxRegularRegisters + numFloatRegistersUsed] = val;
		numFloatRegistersUsed++;
	}
	else if (numRegularRegistersUsed < maxRegularRegisters)
	{
		argValues[numRegularRegistersUsed] = val;
		numRegularRegistersUsed++;
	}
	else if (numStackValuesUsed < maxValuesOnStack)
	{
		stackValues[numStackValuesUsed] = val;
		numStackValuesUsed++;
	}
	else
	{
		// Oops, we ran out of space in the argValues array!
		// TODO: This should be validated as the function is registered
		asASSERT(false);
		return false;
	}

	return true;
}

bool PushToRegularRegs(asQWORD val, asQWORD* argValues, asUINT& numRegularRegistersUsed, asUINT& numStackValuesUsed)
{
	asQWORD* stackValues = argValues + maxRegularRegisters + maxFloatRegisters;

	if (numRegularRegistersUsed < maxRegularRegisters)
	{
		argValues[numRegularRegistersUsed] = val;
		numRegularRegistersUsed++;
	}
	else if (numStackValuesUsed < maxValuesOnStack)
	{
		stackValues[numStackValuesUsed] = val;
		numStackValuesUsed++;
	}
	else
	{
		// Oops, we ran out of space in the argValues array!
		// TODO: This should be validated as the function is registered
		asASSERT(false);
		return false;
	}

	return true;
}

asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &retQW2, void *secondObj)
{
	asCScriptEngine *engine = context->m_engine;
	const asSSystemFunctionInterface *const sysFunc = descr->sysFuncIntf;
	const asCDataType &retType = descr->returnType;
	const asCTypeInfo *const retTypeInfo = retType.GetTypeInfo();
	asFUNCTION_t func = sysFunc->func;
	int callConv = sysFunc->callConv;

	// TODO: retrieve correct function pointer to call (e.g. from virtual function table, auxiliary pointer, etc)

	// Prepare the values that will be sent to the native function
	asQWORD argValues[maxRegularRegisters + maxFloatRegisters + maxValuesOnStack];
	asQWORD* stackValues = argValues + maxRegularRegisters + maxFloatRegisters;

	asUINT numRegularRegistersUsed = 0;
	asUINT numFloatRegistersUsed = 0;
	asUINT numStackValuesUsed = 0;

	// A function returning an object by value must give the
	// address of the memory to initialize as the first argument
	if (sysFunc->hostReturnInMemory)
	{
		// Set the return pointer as the first argument
		argValues[numRegularRegistersUsed++] = (asQWORD)retPointer;
	}

	// Determine the real function pointer in case of virtual method
	if (obj && (callConv == ICC_VIRTUAL_THISCALL ||
		callConv == ICC_VIRTUAL_THISCALL_RETURNINMEM ||
		callConv == ICC_VIRTUAL_THISCALL_OBJFIRST ||
		callConv == ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM ||
		callConv == ICC_VIRTUAL_THISCALL_OBJLAST ||
		callConv == ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM))
	{
		asFUNCTION_t* vftable = *((asFUNCTION_t**)obj);
		func = vftable[FuncPtrToUInt(func) / sizeof(void*)];
	}

	// Check if the object pointer must be added as the first argument
	if (callConv == ICC_CDECL_OBJFIRST ||
		callConv == ICC_CDECL_OBJFIRST_RETURNINMEM ||
		callConv == ICC_THISCALL ||
		callConv == ICC_VIRTUAL_THISCALL ||
		callConv == ICC_THISCALL_RETURNINMEM ||
		callConv == ICC_VIRTUAL_THISCALL_RETURNINMEM ||
		callConv == ICC_THISCALL_OBJLAST ||
		callConv == ICC_THISCALL_OBJLAST_RETURNINMEM ||
		callConv == ICC_VIRTUAL_THISCALL_OBJLAST ||
		callConv == ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM)
	{
		PushToRegularRegs((asPWORD)obj, argValues, numRegularRegistersUsed, numStackValuesUsed);
	}
	else if (callConv == ICC_THISCALL_OBJFIRST ||
		callConv == ICC_VIRTUAL_THISCALL_OBJFIRST ||
		callConv == ICC_THISCALL_OBJFIRST_RETURNINMEM ||
		callConv == ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM)
	{
		PushToRegularRegs((asPWORD)obj, argValues, numRegularRegistersUsed, numStackValuesUsed);
		PushToRegularRegs((asPWORD)secondObj, argValues, numRegularRegistersUsed, numStackValuesUsed);
	}

	asUINT argsPos = 0;
	for (asUINT n = 0; n < descr->parameterTypes.GetLength(); n++)
	{
		const asCDataType& parmType = descr->parameterTypes[n];
		const asUINT parmDWords = parmType.GetSizeOnStackDWords();

		if (parmType.IsReference() || parmType.IsObjectHandle() || parmType.IsIntegerType() || parmType.IsUnsignedType() || parmType.IsBooleanType() )
		{
			// pointers, integers, and booleans go to regular registers

			if (parmType.GetTokenType() == ttQuestion)
			{
				// Copy the reference and type id as two separate arguments
				PushToRegularRegs(*(asQWORD*)&args[argsPos], argValues, numRegularRegistersUsed, numStackValuesUsed);
				PushToRegularRegs((asQWORD)args[argsPos + AS_PTR_SIZE], argValues, numRegularRegistersUsed, numStackValuesUsed);
			}
			else
			{
				if (parmDWords == 1)
					PushToRegularRegs((asQWORD)args[argsPos], argValues, numRegularRegistersUsed, numStackValuesUsed);
				else
					PushToRegularRegs(*(asQWORD*)&args[argsPos], argValues, numRegularRegistersUsed, numStackValuesUsed);
			}
		}
		else if (parmType.IsFloatType() || parmType.IsDoubleType())
		{
			// floats and doubles goes to the float registers
			// if there are more float/double args than registers, and there are still regular registers available then use those
			if (parmDWords == 1)
				PushToFloatRegs(0xFFFFFFFF00000000ull | (asQWORD)args[argsPos], argValues, numFloatRegistersUsed, numRegularRegistersUsed, numStackValuesUsed);
			else
				PushToFloatRegs(*(asQWORD*)&args[argsPos], argValues, numFloatRegistersUsed, numRegularRegistersUsed, numStackValuesUsed);
		}
		else if (parmType.IsObject())
		{
			if (parmType.GetTypeInfo()->flags & COMPLEX_MASK)
			{
				// complex object types are passed by address
				PushToRegularRegs(*(asQWORD*)&args[argsPos], argValues, numRegularRegistersUsed, numStackValuesUsed);
			}
			else if ((parmType.GetTypeInfo()->flags & asOBJ_APP_CLASS_ALLFLOATS) && !(parmType.GetTypeInfo()->flags & asOBJ_APP_CLASS_UNION) &&
				((parmType.GetSizeInMemoryDWords() <= 2 && !(parmType.GetTypeInfo()->flags & asOBJ_APP_CLASS_ALIGN8)) ||
				 (parmType.GetSizeInMemoryDWords() <= 4 && (parmType.GetTypeInfo()->flags & asOBJ_APP_CLASS_ALIGN8))) )
			{
				// simple structs with 1 or 2 floats/doubles are loaded into into float registers
				if (!(parmType.GetTypeInfo()->flags & asOBJ_APP_CLASS_ALIGN8))
				{
					// Unpack the floats
					asQWORD arg1 = 0xFFFFFFFF00000000ull | **(asDWORD**)&args[argsPos];
					asQWORD arg2 = 0xFFFFFFFF00000000ull | *((*(asDWORD**)&args[argsPos])+1);
					PushToFloatRegs(arg1, argValues, numFloatRegistersUsed, numRegularRegistersUsed, numStackValuesUsed);
					PushToFloatRegs(arg2, argValues, numFloatRegistersUsed, numRegularRegistersUsed, numStackValuesUsed);
				}
				else
				{
					// Unpack the doubles
					asQWORD arg1 = **(asQWORD**)&args[argsPos];
					asQWORD arg2 = *((*(asQWORD**)&args[argsPos]) + 1);
					PushToFloatRegs(arg1, argValues, numFloatRegistersUsed, numRegularRegistersUsed, numStackValuesUsed);
					PushToFloatRegs(arg2, argValues, numFloatRegistersUsed, numRegularRegistersUsed, numStackValuesUsed);
				}

				// Delete the original memory
				engine->CallFree(*(void**)&args[argsPos]);
			}
			else
			{
				// simple object types are passed in registers
				// TODO: what if part of the structure fits in registers but not the other part? would part of the object be pushed on the stack?
				// TODO: what of large objects? are they passed by value in registers/stack? Or by reference?
				const asUINT sizeInMemoryDWords = parmType.GetSizeInMemoryDWords();
				const asUINT parmQWords = (sizeInMemoryDWords >> 1) + (sizeInMemoryDWords & 1);

				if ((maxRegularRegisters - numRegularRegistersUsed) > parmQWords)
				{
					if (sizeInMemoryDWords == 1)
						argValues[numRegularRegistersUsed] = (asQWORD) * *(asDWORD**)&args[argsPos];
					else
						memcpy(&argValues[numRegularRegistersUsed], *(void**)&args[argsPos], sizeInMemoryDWords * 4);
					numRegularRegistersUsed += parmQWords;
				}
				else if ((maxValuesOnStack - numStackValuesUsed) > parmQWords)
				{
					if (sizeInMemoryDWords == 1)
						stackValues[numStackValuesUsed] = (asQWORD) * *(asDWORD**)&args[argsPos];
					else
						memcpy(&stackValues[numStackValuesUsed], *(void**)&args[argsPos], sizeInMemoryDWords * 4);
					numStackValuesUsed += parmQWords;
				}
				else
				{
					// Oops, we ran out of space in the argValues array!
					// TODO: This should be validated as the function is registered
					asASSERT(false);
				}

				// Delete the original memory
				engine->CallFree(*(void**)&args[argsPos]);
			}
		}

		argsPos += parmDWords;
	}

	// Check if the object pointer must be added as the last argument
	if (callConv == ICC_CDECL_OBJLAST || callConv == ICC_CDECL_OBJLAST_RETURNINMEM)
	{
		PushToRegularRegs((asPWORD)obj, argValues, numRegularRegistersUsed, numStackValuesUsed);
	}
	else if (callConv == ICC_THISCALL_OBJLAST || callConv == ICC_THISCALL_OBJLAST_RETURNINMEM ||
		callConv == ICC_VIRTUAL_THISCALL_OBJLAST || callConv == ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM)
	{
		PushToRegularRegs((asPWORD)secondObj, argValues, numRegularRegistersUsed, numStackValuesUsed);
	}

	int retfloat = sysFunc->hostReturnFloat ? 1 : 0;

	// Integer values are returned in a0 and a1, allowing simple structures with up to 128bits to be returned in registers
	asDBLQWORD ret = CallRiscVFunc(func, retfloat, argValues, numRegularRegistersUsed, numFloatRegistersUsed, numStackValuesUsed);
	retQW2 = ret.qw2;

	// Special case for returning a struct with two floats. C++ will return this in fa0:fa1. These needs to be compacted into a single qword
	if (retfloat && retTypeInfo && !(retTypeInfo->flags & asOBJ_APP_CLASS_ALIGN8) && retTypeInfo->flags & asOBJ_APP_CLASS_ALLFLOATS)
	{
		ret.qw1 &= 0xFFFFFFFF;
		ret.qw1 |= (retQW2 << 32);
	}

	return ret.qw1;
}

END_AS_NAMESPACE

#endif // AS_RISCV64
#endif // AS_MAX_PORTABILITY


code:
//
//  AngelCode Scripting Library
//  Copyright (c) 2024 Andreas Jonsson
//
//  This software is provided 'as-is', without any express or implied
//  warranty. In no event will the authors be held liable for any
//  damages arising from the use of this software.
//
//  Permission is granted to anyone to use this software for any
//  purpose, including commercial applications, and to alter it and
//  redistribute it freely, subject to the following restrictions:
//
//  1. The origin of this software must not be misrepresented// you
//     must not claim that you wrote the original software. If you use
//     this software in a product, an acknowledgment in the product
//     documentation would be appreciated but is not required.
//
//  2. Altered source versions must be plainly marked as such, and
//     must not be misrepresented as being the original software.
//
//  3. This notice may not be removed or altered from any source
//     distribution.
//
//  The original version of this library can be located at:
//  http://www.angelcode.com/angelscript/
//
//  Andreas Jonsson
//  andreas@angelcode.com
//

// Assembly routines for the 64bit RISC-V call convention used for Linux

// Compile with GCC/GAS

#if !defined(AS_MAX_PORTABILITY)

#if defined(__riscv) && defined(__LP64__)

	.file "as_callfunc_riscv64_gcc.S"
	.option pic
	.attribute unaligned_access, 0
	.attribute stack_align, 16
	.text
	.align 1
	.globl CallRiscVFunc
	.type CallRiscVFunc, @function
CallRiscVFunc:
	.cfi_startproc

	// Setup call stack
	addi    sp,sp,-336        // reserve bytes on stack (aligned to 16bytes). 256 bytes (32*8) is reserved for values that will be pushed on the stack for the function call, the rest is for local backup of registers
	.cfi_def_cfa_offset 336
	sd      ra,328(sp)        // backup return address on stack, just below original stack frame pointer
	sd      s0,320(sp)        // backup frame pointer on stack, below return address
	.cfi_offset 1, -8
	.cfi_offset 8, -16
	addi    s0,sp,336         // load new frame pointer with the reserved space
	.cfi_def_cfa 8,0

	// Backup arguments on stack
	// TODO: skip the backup and move them into the temp registers directly
	sd      a0,-24(s0)        // store func arg on stack, below backup of s0
	sd      a1,-32(s0)        // store retfloat arg on stack
	sd      a2,-40(s0)        // store argValues arg on stack
	sd      a3,-48(s0)        // store numRegularValues arg on stack
	sd      a4,-56(s0)        // store numFloatValues arg on stack
	sd      a5,-64(s0)        // store numStackValues arg on stack

	ld      t1,-40(s0)        // load argValues arg into t1

	// Load regular values into regular registers
	// TODO: skip ahead to the number of generic args
	ld      a7,56(t1)         // a7 = argValues[7]
	ld      a6,48(t1)         // a6 = argValues[6]
	ld      a5,40(t1)         // a5 = argValues[5]
	ld      a4,32(t1)         // a4 = argValues[4]
	ld      a3,24(t1)         // a3 = argValues[3]
	ld      a2,16(t1)         // a2 = argValues[2]
	ld      a1,8(t1)          // a1 = argValues[1]
	ld      a0,0(t1)          // a0 = argValues[0]

	// Load float values into float registers
	// TODO: skip ahead to the number of float args
	fld     fa7,120(t1)       // fa7 = argValues[15]
	fld     fa6,112(t1)       // fa6 = argValues[14]
	fld     fa5,104(t1)       // fa5 = argValues[13]
	fld     fa4,96(t1)        // fa4 = argValues[12]
	fld     fa3,88(t1)        // fa3 = argValues[11]
	fld     fa2,80(t1)        // fa2 = argValues[10]
	fld     fa1,72(t1)        // fa1 = argValues[9]
	fld     fa0,64(t1)        // fa0 = argValues[8]

	// Push the remaining args on the stack
	addi    t1,t1,128         // t1 = &argValues[16]
	mv      t3,sp             // t3 = sp
	ld      t2,-64(s0)        // t2 = numStackValues
	beq     t2,x0,.L_nomore   // jump if t2 == 0
.L_next:
	addi    t2,t2,-1          // t2--
    ld      t5,0(t1)          // t5 value from argValues
	sd      t5,0(t3)          // store the value on the stack
	addi    t3,t3,8           // move t3 to the next slot on the stack
	addi    t1,t1,8           // move to the next value in argValues
	bne     t2,x0,.L_next     // reiterate if t2 is not zero
.L_nomore:

	// Call the function
	ld      t1,-24(s0)        // load func arg to t1
	jalr    t1                // call the function in func arg
	nop

	// If the function returns a float value, then retrieve that
	ld      a5,-32(s0)        // restore retfloat
	sext.w  a4,a5             // load lower 32bit word of retfloat into a4
	li      a5,1              // set a5 = 1
	bne     a4,a5,.L_nofloat  // jump if a4 != 1  TODO: rewrite to compare with x0 which is hardwired to 0 so there is no need to set a5
	fsd     fa0,-32(s0)       // store the raw returned 64bit float/double value on the stack (where retfloat was)
	ld      a0,-32(s0)        // load the raw 64bit value into a0 for return
	fsd     fa1,-32(s0)       // store the raw returned 64bit float/double value on the stack (where retfloat was)
	ld      a1,-32(s0)        // load the raw 64bit value into a1 for return
.L_nofloat:

	// Clean up call stack
	ld      ra,328(sp)        // restore return address from stack
	.cfi_restore 1
	ld      s0,320(sp)        // restore frame pointer from stack
	.cfi_restore 8
	.cfi_def_cfa 2, 336
	addi    sp,sp,336         // clear reserved space from stack
	.cfi_def_cfa_offset 0
	jr      ra

	.cfi_endproc
	.size CallRiscVFunc, .-CallRiscVFunc

#endif /* __riscv && __LP64__ */

#endif /* !AS_MAX_PORTABILITY */


code:
/*
   AngelCode Scripting Library
   Copyright (c) 2003-2015 Andreas Jonsson

   This software is provided 'as-is', without any express or implied
   warranty. In no event will the authors be held liable for any
   damages arising from the use of this software.

   Permission is granted to anyone to use this software for any
   purpose, including commercial applications, and to alter it and
   redistribute it freely, subject to the following restrictions:

   1. The origin of this software must not be misrepresented; you
      must not claim that you wrote the original software. If you use
      this software in a product, an acknowledgment in the product
      documentation would be appreciated but is not required.

   2. Altered source versions must be plainly marked as such, and
      must not be misrepresented as being the original software.

   3. This notice may not be removed or altered from any source
      distribution.

   The original version of this library can be located at:
   http://www.angelcode.com/angelscript/

   Andreas Jonsson
   andreas@angelcode.com
*/

//
// as_callfunc_sh4.cpp
//
// These functions handle the actual calling of system functions
//
// This version is SH4 specific and was originally written
// by Fredrik Ehnbom in May, 2004
// Later updated for angelscript 2.0.0 by Fredrik Ehnbom in Jan, 2005

// References:
// * http://www.renesas.com/avs/resource/japan/eng/pdf/mpumcu/e602156_sh4.pdf
// * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wcechp40/html/_callsh4_SH_4_Calling_Standard.asp

#include "as_config.h"

#ifndef AS_MAX_PORTABILITY
#ifdef AS_SH4

#include "as_callfunc.h"
#include "as_scriptengine.h"
#include "as_texts.h"
#include "as_tokendef.h"
#include "as_context.h"

#include <stdio.h>
#include <stdlib.h>

BEGIN_AS_NAMESPACE

#define AS_SH4_MAX_ARGS 32
// The array used to send values to the correct places.
// first 0-4 regular values to load into the r4-r7 registers
// then 0-8 float values to load into the fr4-fr11 registers
// then (AS_SH4_MAX_ARGS - 12) values to load onto the stack
// the +1 is for when CallThis (object methods) is used
// extra +1 when returning in memory
extern "C" {
static asDWORD sh4Args[AS_SH4_MAX_ARGS + 1 + 1];
}

// Loads all data into the correct places and calls the function.
// intArgSize is the size in bytes for how much data to put in int registers
// floatArgSize is the size in bytes for how much data to put in float registers
// stackArgSize is the size in bytes for how much data to put on the callstack
extern "C" asQWORD sh4Func(int intArgSize, int floatArgSize, int stackArgSize, asDWORD func);

asm(""
"	.align 4\n"
"	.global _sh4Func\n"
"_sh4Func:\n"
"	mov.l	r14,@-r15\n"
"	mov.l	r13,@-r15\n"
"	mov.l	r12,@-r15\n"
"	sts.l	pr,@-r15\n"		// must be saved since we call a subroutine
"	mov	r7, r14\n"		// func
"	mov	r6, r13\n"		// stackArgSize
"	mov.l	r5,@-r15\n"		// floatArgSize
"	mov.l	sh4Args,r0\n"
"	pref	@r0\n"
"	mov	r4, r1\n"		// intArgsize
"	mov	#33*4,r2\n"
"	extu.b	r2,r2\n"		// make unsigned (33*4 = 132 => 128)
"	mov.l	@(r0,r2), r2\n"		// r2 has adress for when returning in memory
"_sh4f_intarguments:\n"			// copy all the int arguments to the respective registers
"	mov	#4*2*2,r3\n"		// calculate how many bytes to skip
"	sub	r1,r3\n"
"	braf	r3\n"
"	add	#-4,r1\n"		// we are indexing the array backwards, so subtract one (delayed slot)
"	mov.l	@(r0,r1),r7\n"		// 4 arguments
"	add	#-4,r1\n"
"	mov.l	@(r0,r1),r6\n"		// 3 arguments
"	add	#-4,r1\n"
"	mov.l	@(r0,r1),r5\n"		// 2 arguments
"	add	#-4,r1\n"
"	mov.l	@(r0,r1),r4\n"		// 1 argument
"	nop\n"
"_sh4f_floatarguments:\n"		// copy all the float arguments to the respective registers
"	add	#4*4, r0\n"
"	mov.l	@r15+,r1\n"		// floatArgSize
"	mov	#8*2*2,r3\n"		// calculate how many bytes to skip
"	sub	r1,r3\n"
"	braf	r3\n"
"	add	#-4,r1\n"		// we are indexing the array backwards, so subtract one (delayed slot)
"	fmov.s	@(r0,r1),fr11\n"	// 8 arguments
"	add	#-4,r1\n"
"	fmov.s	@(r0,r1),fr10\n"	// 7 arguments
"	add	#-4,r1\n"
"	fmov.s	@(r0,r1),fr9\n"		// 6 arguments
"	add	#-4,r1\n"
"	fmov.s	@(r0,r1),fr8\n"		// 5 arguments
"	add	#-4,r1\n"
"	fmov.s	@(r0,r1),fr7\n"		// 4 arguments
"	add	#-4,r1\n"
"	fmov.s	@(r0,r1),fr6\n"		// 3 arguments
"	add	#-4,r1\n"
"	fmov.s	@(r0,r1),fr5\n"		// 2 arguments
"	add	#-4,r1\n"
"	fmov.s	@(r0,r1),fr4\n"		// 1 argument
"	nop\n"
"_sh4f_stackarguments:\n"		// copy all the stack argument onto the stack
"	add	#8*4, r0\n"
"	mov	r0, r1\n"
"	mov	#0, r0\n"		// init position counter (also used as a 0-check on the line after)
"	cmp/eq	r0, r13\n"
"	bt	_sh4f_functioncall\n"	// no arguments to push onto the stack
"	mov	r13, r3\n"		// stackArgSize
"	sub	r3,r15\n"		// "allocate" space on the stack
"	shlr2	r3\n"			// make into a counter
"_sh4f_stackloop:\n"
"	mov.l	@r1+, r12\n"
"	mov.l	r12, @(r0, r15)\n"
"	add	#4, r0\n"
"	dt	r3\n"
"	bf	_sh4f_stackloop\n"
"_sh4f_functioncall:\n"
"	jsr	@r14\n"			// no arguments
"	nop\n"
"	add	r13, r15\n"		// restore stack position
"	lds.l	@r15+,pr\n"
"	mov.l	@r15+, r12\n"
"	mov.l	@r15+, r13\n"
"	rts\n"
"	mov.l	@r15+, r14\n"		// delayed slot
"\n"
"	.align 4\n"
"sh4Args:\n"
"	.long	_sh4Args\n"
);

// puts the arguments in the correct place in the sh4Args-array. See comments above.
// This could be done better.
inline void splitArgs(const asDWORD *args, int argNum, int &numRegIntArgs, int &numRegFloatArgs, int &numRestArgs, int hostFlags) {
	int i;

	int argBit = 1;
	for (i = 0; i < argNum; i++) {
		if (hostFlags & argBit) {
			if (numRegFloatArgs < 12 - 4) {
				// put in float register
				sh4Args[4 + numRegFloatArgs] = args[i];
				numRegFloatArgs++;
			} else {
				// put in stack
				sh4Args[4 + 8 + numRestArgs] = args[i];
				numRestArgs++;
			}
		} else {
			if (numRegIntArgs < 8 - 4) {
				// put in int register
				sh4Args[numRegIntArgs] = args[i];
				numRegIntArgs++;
			} else {
				// put in stack
				sh4Args[4 + 8 + numRestArgs] = args[i];
				numRestArgs++;
			}
		}
		argBit <<= 1;
	}
}
asQWORD CallCDeclFunction(const asDWORD *args, int argSize, asDWORD func, int flags)
{
	int argNum = argSize >> 2;

	int intArgs = 0;
	int floatArgs = 0;
	int restArgs = 0;

	// put the arguments in the correct places in the sh4Args array
	if (argNum > 0)
		splitArgs(args, argNum, intArgs, floatArgs, restArgs, flags);

	return sh4Func(intArgs << 2, floatArgs << 2, restArgs << 2, func);
}

// This function is identical to CallCDeclFunction, with the only difference that
// the value in the first parameter is the object
asQWORD CallThisCallFunction(const void *obj, const asDWORD *args, int argSize, asDWORD func, int flags)
{
	int argNum = argSize >> 2;

	int intArgs = 1;
	int floatArgs = 0;
	int restArgs = 0;

	sh4Args[0] = (asDWORD) obj;

	// put the arguments in the correct places in the sh4Args array
	if (argNum >= 1)
		splitArgs(args, argNum, intArgs, floatArgs, restArgs, flags);

	return sh4Func(intArgs << 2, floatArgs << 2, restArgs << 2, func);
}
// This function is identical to CallCDeclFunction, with the only difference that
// the value in the last parameter is the object
asQWORD CallThisCallFunction_objLast(const void *obj, const asDWORD *args, int argSize, asDWORD func, int flags)
{
	int argNum = argSize >> 2;

	int intArgs = 0;
	int floatArgs = 0;
	int restArgs = 0;

	// put the arguments in the correct places in the sh4Args array
	if (argNum >= 1)
		splitArgs(args, argNum, intArgs, floatArgs, restArgs, flags);

	if (intArgs < 4) {
		sh4Args[intArgs] = (asDWORD) obj;
		intArgs++;
	} else {
		sh4Args[4 + 8 + restArgs] = (asDWORD) obj;
		restArgs++;
	}

	return sh4Func(intArgs << 2, floatArgs << 2, restArgs << 2, func);
}

asDWORD GetReturnedFloat()
{
	asDWORD f;

	asm("fmov.s	fr0, %0\n" : "=m"(f));

	return f;
}

// sizeof(double) == 4 with sh-elf-gcc (3.4.0) -m4
// so this isn't really used...
asQWORD GetReturnedDouble()
{
	asQWORD d;

	asm("fmov	dr0, %0\n" : "=m"(d));

	return d;
}

asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &/*retQW2*/, void */*secondObject*/)
{
	// TODO: SH4 does not yet support THISCALL_OBJFIRST/LAST

	asCScriptEngine *engine = context->m_engine;
	asSSystemFunctionInterface *sysFunc = descr->sysFuncIntf;
	int callConv = sysFunc->callConv;

	asQWORD retQW = 0;

	void    *func              = (void*)sysFunc->func;
	int      paramSize         = sysFunc->paramSize;
	asDWORD *vftable;

	if( descr->returnType.IsObject() && !descr->returnType.IsReference() && !descr->returnType.IsObjectHandle() )
	{
		sh4Args[AS_SH4_MAX_ARGS+1] = (asDWORD) retPointer;
	}

	asASSERT(descr->parameterTypes.GetLength() <= 32);

	// mark all float arguments
	int argBit = 1;
	int hostFlags = 0;
	int intArgs = 0;
	for( asUINT a = 0; a < descr->parameterTypes.GetLength(); a++ ) {
		if (descr->parameterTypes[a].IsFloatType()) {
			hostFlags |= argBit;
		} else intArgs++;
		argBit <<= 1;
	}

	asDWORD paramBuffer[64];
	if( sysFunc->takesObjByVal )
	{
		paramSize = 0;
		int spos = 0;
		int dpos = 1;
		for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ )
		{
			if( descr->parameterTypes[n].IsObject() && !descr->parameterTypes[n].IsObjectHandle() && !descr->parameterTypes[n].IsReference() )
			{
#ifdef COMPLEX_OBJS_PASSED_BY_REF
				if( descr->parameterTypes[n].GetTypeInfo()->flags & COMPLEX_MASK )
				{
					paramBuffer[dpos++] = args[spos++];
					paramSize++;
				}
				else
#endif
				{
					// Copy the object's memory to the buffer
					memcpy(&paramBuffer[dpos], *(void**)(args+spos), descr->parameterTypes[n].GetSizeInMemoryBytes());
					// Delete the original memory
					engine->CallFree(*(char**)(args+spos));
					spos++;
					dpos += descr->parameterTypes[n].GetSizeInMemoryDWords();
					paramSize += descr->parameterTypes[n].GetSizeInMemoryDWords();
				}
			}
			else
			{
				// Copy the value directly
				paramBuffer[dpos++] = args[spos++];
				if( descr->parameterTypes[n].GetSizeOnStackDWords() > 1 )
					paramBuffer[dpos++] = args[spos++];
				paramSize += descr->parameterTypes[n].GetSizeOnStackDWords();
			}
		}
		// Keep a free location at the beginning
		args = &paramBuffer[1];
	}

	switch( callConv )
	{
	case ICC_CDECL:
	case ICC_CDECL_RETURNINMEM:
	case ICC_STDCALL:
	case ICC_STDCALL_RETURNINMEM:
		retQW = CallCDeclFunction(args, paramSize<<2, (asDWORD)func, hostFlags);
		break;
	case ICC_THISCALL:
	case ICC_THISCALL_RETURNINMEM:
		retQW = CallThisCallFunction(obj, args, paramSize<<2, (asDWORD)func, hostFlags);
		break;
	case ICC_VIRTUAL_THISCALL:
	case ICC_VIRTUAL_THISCALL_RETURNINMEM:
		// Get virtual function table from the object pointer
		vftable = *(asDWORD**)obj;
		retQW = CallThisCallFunction(obj, args, paramSize<<2, vftable[asDWORD(func)>>2], hostFlags);
		break;
	case ICC_CDECL_OBJLAST:
	case ICC_CDECL_OBJLAST_RETURNINMEM:
		retQW = CallThisCallFunction_objLast(obj, args, paramSize<<2, (asDWORD)func, hostFlags);
		break;
	case ICC_CDECL_OBJFIRST:
	case ICC_CDECL_OBJFIRST_RETURNINMEM:
		retQW = CallThisCallFunction(obj, args, paramSize<<2, (asDWORD)func, hostFlags);
		break;
	default:
		context->SetInternalException(TXT_INVALID_CALLING_CONVENTION);
	}

	// If the return is a float value we need to get the value from the FP register
	if( sysFunc->hostReturnFloat )
	{
		if( sysFunc->hostReturnSize == 1 )
			*(asDWORD*)&retQW = GetReturnedFloat();
		else
			retQW = GetReturnedDouble();
	}

	return retQW;
}

END_AS_NAMESPACE

#endif // AS_SH4
#endif // AS_MAX_PORTABILITY


code:
/*
   AngelCode Scripting Library
   Copyright (c) 2003-2023 Andreas Jonsson

   This software is provided 'as-is', without any express or implied
   warranty. In no event will the authors be held liable for any
   damages arising from the use of this software.

   Permission is granted to anyone to use this software for any
   purpose, including commercial applications, and to alter it and
   redistribute it freely, subject to the following restrictions:

   1. The origin of this software must not be misrepresented; you
      must not claim that you wrote the original software. If you use
      this software in a product, an acknowledgment in the product
      documentation would be appreciated but is not required.

   2. Altered source versions must be plainly marked as such, and
      must not be misrepresented as being the original software.

   3. This notice may not be removed or altered from any source
      distribution.

   The original version of this library can be located at:
   http://www.angelcode.com/angelscript/

   Andreas Jonsson
   andreas@angelcode.com
*/

/*
 * Implements the AMD64 calling convention for gcc-based 64bit Unices
 *
 * Author: Ionut "gargltk" Leonte <ileonte@bitdefender.com>
 *
 * Initial author: niteice
 *
 * Added support for functor methods by Jordi Oliveras Rovira in April, 2014.
 */

// Useful references for the System V AMD64 ABI:
// http://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64/
// http://math-atlas.sourceforge.net/devel/assembly/abi_sysV_amd64.pdf

#include "as_config.h"

#ifndef AS_MAX_PORTABILITY
#ifdef AS_X64_GCC

#include "as_scriptengine.h"
#include "as_texts.h"
#include "as_context.h"

BEGIN_AS_NAMESPACE

enum argTypes { x64INTARG = 0, x64FLOATARG = 1 };
typedef asQWORD ( *funcptr_t )( void );

#define X64_MAX_ARGS             32
#define MAX_CALL_INT_REGISTERS    6
#define MAX_CALL_SSE_REGISTERS    8
#define X64_CALLSTACK_SIZE        ( X64_MAX_ARGS + MAX_CALL_SSE_REGISTERS + 3 )

// Note to self: Always remember to inform the used registers on the clobber line,
// so that the gcc optimizer doesn't try to use them for other things

static asQWORD __attribute__((noinline))
#ifndef __clang__
    // On GNUC this code doesn't work properly when optimized, so disable optimization for this function
    __attribute__((optimize(0)))
#endif
    X64_CallFunction(const asQWORD *args, int cnt, funcptr_t func, asQWORD &retQW2, bool returnFloat)
{
	// Need to flag the variable as volatile so the compiler doesn't optimize out the variable
	volatile asQWORD retQW1 = 0;

	// Reference: http://www.x86-64.org/documentation/abi.pdf

	__asm__ __volatile__ (

		"  movq %0, %%rcx \n"	// rcx = cnt
		"  movq %1, %%r10 \n"	// r10 = args
		"  movq %2, %%r11 \n"	// r11 = func

	// Backup stack pointer in R15 that is guaranteed to maintain its value over function calls
		"  movq %%rsp, %%r15 \n"
#if defined(__clang__) && defined(__OPTIMIZE__)
	// Make sure the stack unwind logic knows we've backed up the stack pointer in register r15
	// This should only be done if any optimization is done. If no optimization (-O0) is used,
	// then the compiler already backups the rsp before entering the inline assembler code
		" .cfi_def_cfa_register r15 \n"
#endif

	// Skip the first 128 bytes on the stack frame, called "red zone",
	// that might be used by the compiler to store temporary values
		"  sub $128, %%rsp \n"

	// Make sure the stack pointer will be aligned to 16 bytes when the function is called
		"  movq %%rcx, %%rdx \n"
		"  salq $3, %%rdx \n"
		"  movq %%rsp, %%rax \n"
		"  sub %%rdx, %%rax \n"
		"  and $15, %%rax \n"
		"  sub %%rax, %%rsp \n"

	// Push the stack parameters, i.e. the arguments that won't be loaded into registers
		"  movq %%rcx, %%rsi \n"
		"  testl %%esi, %%esi \n"
		"  jle endstack \n"
		"  subl $1, %%esi \n"
		"  xorl %%edx, %%edx \n"
		"  leaq 8(, %%rsi, 8), %%rcx \n"
		"loopstack: \n"
		"  movq 112(%%r10, %%rdx), %%rax \n"
		"  pushq %%rax \n"
		"  addq $8, %%rdx \n"
		"  cmpq %%rcx, %%rdx \n"
		"  jne loopstack \n"
		"endstack: \n"

	// Populate integer and floating point parameters
		"  movq %%r10, %%rax \n"
		"  mov     (%%rax), %%rdi \n"
		"  mov    8(%%rax), %%rsi \n"
		"  mov   16(%%rax), %%rdx \n"
		"  mov   24(%%rax), %%rcx \n"
		"  mov   32(%%rax), %%r8 \n"
		"  mov   40(%%rax), %%r9 \n"
		"  add   $48, %%rax \n"
		"  movsd   (%%rax), %%xmm0 \n"
		"  movsd  8(%%rax), %%xmm1 \n"
		"  movsd 16(%%rax), %%xmm2 \n"
		"  movsd 24(%%rax), %%xmm3 \n"
		"  movsd 32(%%rax), %%xmm4 \n"
		"  movsd 40(%%rax), %%xmm5 \n"
		"  movsd 48(%%rax), %%xmm6 \n"
		"  movsd 56(%%rax), %%xmm7 \n"

	// Call the function
		"  call *%%r11 \n"

	// Restore stack pointer
		"  mov %%r15, %%rsp \n"
#if defined(__clang__) && defined(__OPTIMIZE__)
	// Inform the stack unwind logic that the stack pointer has been restored
	// This should only be done if any optimization is done. If no optimization (-O0) is used,
	// then the compiler already backups the rsp before entering the inline assembler code
		" .cfi_def_cfa_register rsp \n"
#endif

	// Put return value in retQW1 and retQW2, using either RAX:RDX or XMM0:XMM1 depending on type of return value
		"  movl %5, %%ecx \n"
		"  testb %%cl, %%cl \n"
		"  je intret \n"
		"  lea %3, %%rax \n"
		"  movq %%xmm0, (%%rax) \n"
		"  lea %4, %%rdx \n"
		"  movq %%xmm1, (%%rdx) \n"
		"  jmp endcall \n"
		"intret: \n"
		"  movq %%rax, %3 \n"
		"  movq %%rdx, %4 \n"
		"endcall: \n"

		: : "g" ((asQWORD)cnt), "g" (args), "g" (func), "m" (retQW1), "m" (retQW2), "m" (returnFloat)
		: "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7",
		  "%rdi", "%rsi", "%rax", "%rdx", "%rcx", "%r8", "%r9", "%r10", "%r11", "%r15");

	return retQW1;
}

// returns true if the given parameter is a 'variable argument'
static inline bool IsVariableArgument( asCDataType type )
{
	return ( type.GetTokenType() == ttQuestion ) ? true : false;
}

asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &retQW2, void *secondObject)
{
	asCScriptEngine            *engine             = context->m_engine;
	asSSystemFunctionInterface *sysFunc            = descr->sysFuncIntf;
	int                         callConv           = sysFunc->callConv;
	asQWORD                     retQW              = 0;
	asDWORD                    *stack_pointer      = args;
	funcptr_t                  *vftable            = NULL;
	int                         totalArgumentCount = 0;
	int                         n                  = 0;
	int                         param_post         = 0;
	int                         argIndex           = 0;
	funcptr_t                   func               = (funcptr_t)sysFunc->func;

	if( sysFunc->hostReturnInMemory )
	{
		// The return is made in memory
		callConv++;
	}

#ifdef AS_NO_THISCALL_FUNCTOR_METHOD
	// Determine the real function pointer in case of virtual method
	if ( obj && ( callConv == ICC_VIRTUAL_THISCALL || callConv == ICC_VIRTUAL_THISCALL_RETURNINMEM ) )
#else
	if ( obj && ( callConv == ICC_VIRTUAL_THISCALL ||
		callConv == ICC_VIRTUAL_THISCALL_RETURNINMEM ||
		callConv == ICC_VIRTUAL_THISCALL_OBJFIRST ||
		callConv == ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM ||
		callConv == ICC_VIRTUAL_THISCALL_OBJLAST ||
		callConv == ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM) )
#endif
	{
		vftable = *((funcptr_t**)obj);
		func = vftable[FuncPtrToUInt(asFUNCTION_t(func)) >> 3];
	}

	// Determine the type of the arguments, and prepare the input array for the X64_CallFunction
	asQWORD  paramBuffer[X64_CALLSTACK_SIZE] = { 0 };
	asBYTE	 argsType[X64_CALLSTACK_SIZE] = { 0 };

	switch ( callConv )
	{
		case ICC_CDECL_RETURNINMEM:
		case ICC_STDCALL_RETURNINMEM:
		{
			paramBuffer[0] = (asPWORD)retPointer;
			argsType[0] = x64INTARG;

			argIndex = 1;

			break;
		}
#ifndef AS_NO_THISCALL_FUNCTOR_METHOD
		case ICC_THISCALL_OBJLAST:
		case ICC_VIRTUAL_THISCALL_OBJLAST:
			param_post = 2;
#endif
		case ICC_THISCALL:
		case ICC_VIRTUAL_THISCALL:
		case ICC_CDECL_OBJFIRST:
		{
			paramBuffer[0] = (asPWORD)obj;
			argsType[0] = x64INTARG;

			argIndex = 1;

			break;
		}
#ifndef AS_NO_THISCALL_FUNCTOR_METHOD
		case ICC_THISCALL_OBJLAST_RETURNINMEM:
		case ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM:
			param_post = 2;
#endif
		case ICC_THISCALL_RETURNINMEM:
		case ICC_VIRTUAL_THISCALL_RETURNINMEM:
		case ICC_CDECL_OBJFIRST_RETURNINMEM:
		{
			paramBuffer[0] = (asPWORD)retPointer;
			paramBuffer[1] = (asPWORD)obj;
			argsType[0] = x64INTARG;
			argsType[1] = x64INTARG;

			argIndex = 2;

			break;
		}
#ifndef AS_NO_THISCALL_FUNCTOR_METHOD
		case ICC_THISCALL_OBJFIRST:
		case ICC_VIRTUAL_THISCALL_OBJFIRST:
		{
			paramBuffer[0] = (asPWORD)obj;
			paramBuffer[1] = (asPWORD)secondObject;
			argsType[0] = x64INTARG;
			argsType[1] = x64INTARG;

			argIndex = 2;
			break;
		}
		case ICC_THISCALL_OBJFIRST_RETURNINMEM:
		case ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM:
		{
			paramBuffer[0] = (asPWORD)retPointer;
			paramBuffer[1] = (asPWORD)obj;
			paramBuffer[2] = (asPWORD)secondObject;
			argsType[0] = x64INTARG;
			argsType[1] = x64INTARG;
			argsType[2] = x64INTARG;

			argIndex = 3;
			break;
		}
#endif
		case ICC_CDECL_OBJLAST:
			param_post = 1;
			break;
		case ICC_CDECL_OBJLAST_RETURNINMEM:
		{
			paramBuffer[0] = (asPWORD)retPointer;
			argsType[0] = x64INTARG;

			argIndex = 1;
			param_post = 1;

			break;
		}
	}

	int argumentCount = ( int )descr->parameterTypes.GetLength();
	for( int a = 0; a < argumentCount; ++a )
	{
		const asCDataType &parmType = descr->parameterTypes[a];
		if( parmType.IsFloatType() && !parmType.IsReference() )
		{
			argsType[argIndex] = x64FLOATARG;
			memcpy(paramBuffer + argIndex, stack_pointer, sizeof(float));
			argIndex++;
			stack_pointer++;
		}
		else if( parmType.IsDoubleType() && !parmType.IsReference() )
		{
			argsType[argIndex] = x64FLOATARG;
			memcpy(paramBuffer + argIndex, stack_pointer, sizeof(double));
			argIndex++;
			stack_pointer += 2;
		}
		else if( IsVariableArgument( parmType ) )
		{
			// The variable args are really two, one pointer and one type id
			argsType[argIndex] = x64INTARG;
			argsType[argIndex+1] = x64INTARG;
			memcpy(paramBuffer + argIndex, stack_pointer, sizeof(void*));
			memcpy(paramBuffer + argIndex + 1, stack_pointer + 2, sizeof(asDWORD));
			argIndex += 2;
			stack_pointer += 3;
		}
		else if( parmType.IsPrimitive() ||
		         parmType.IsReference() ||
		         parmType.IsObjectHandle() )
		{
			argsType[argIndex] = x64INTARG;
			if( parmType.GetSizeOnStackDWords() == 1 )
			{
				memcpy(paramBuffer + argIndex, stack_pointer, sizeof(asDWORD));
				stack_pointer++;
			}
			else
			{
				memcpy(paramBuffer + argIndex, stack_pointer, sizeof(asQWORD));
				stack_pointer += 2;
			}
			argIndex++;
		}
		else
		{
			// An object is being passed by value
			if( (parmType.GetTypeInfo()->flags & COMPLEX_MASK) ||
			    parmType.GetSizeInMemoryDWords() > 4 )
			{
				// Copy the address of the object
				argsType[argIndex] = x64INTARG;
				memcpy(paramBuffer + argIndex, stack_pointer, sizeof(asQWORD));
				argIndex++;
			}
			else if( (parmType.GetTypeInfo()->flags & asOBJ_APP_CLASS_ALLINTS) ||
			         (parmType.GetTypeInfo()->flags & asOBJ_APP_PRIMITIVE) )
			{
				// Copy the value of the object
				if( parmType.GetSizeInMemoryDWords() > 2 )
				{
					argsType[argIndex] = x64INTARG;
					argsType[argIndex+1] = x64INTARG;
					memcpy(paramBuffer + argIndex, *(asDWORD**)stack_pointer, parmType.GetSizeInMemoryBytes());
					argIndex += 2;
				}
				else
				{
					argsType[argIndex] = x64INTARG;
					memcpy(paramBuffer + argIndex, *(asDWORD**)stack_pointer, parmType.GetSizeInMemoryBytes());
					argIndex++;
				}
				// Delete the original memory
				engine->CallFree(*(void**)stack_pointer);
			}
			else if( (parmType.GetTypeInfo()->flags & asOBJ_APP_CLASS_ALLFLOATS) ||
			         (parmType.GetTypeInfo()->flags & asOBJ_APP_FLOAT) )
			{
				// Copy the value of the object
				if( parmType.GetSizeInMemoryDWords() > 2 )
				{
					argsType[argIndex] = x64FLOATARG;
					argsType[argIndex+1] = x64FLOATARG;
					memcpy(paramBuffer + argIndex, *(asDWORD**)stack_pointer, parmType.GetSizeInMemoryBytes());
					argIndex += 2;
				}
				else
				{
					argsType[argIndex] = x64FLOATARG;
					memcpy(paramBuffer + argIndex, *(asDWORD**)stack_pointer, parmType.GetSizeInMemoryBytes());
					argIndex++;
				}
				// Delete the original memory
				engine->CallFree(*(void**)stack_pointer);
			}
			stack_pointer += 2;
		}
	}

	// For the CDECL_OBJ_LAST calling convention we need to add the object pointer as the last argument
	if( param_post )
	{
#ifdef AS_NO_THISCALL_FUNCTOR_METHOD
		paramBuffer[argIndex] = (asPWORD)obj;
#else
		paramBuffer[argIndex] = (asPWORD)(param_post > 1 ? secondObject : obj);
#endif
		argsType[argIndex] = x64INTARG;
		argIndex++;
	}

	totalArgumentCount = argIndex;

	/*
	 * Q: WTF is going on here !?
	 *
	 * A: The idea is to pre-arange the parameters so that X64_CallFunction() can do
	 * it's little magic which must work regardless of how the compiler decides to
	 * allocate registers. Basically:
	 * - the first MAX_CALL_INT_REGISTERS entries in tempBuff will
	 *   contain the values/types of the x64INTARG parameters - that is the ones who
	 *   go into the registers. If the function has less then MAX_CALL_INT_REGISTERS
	 *   integer parameters then the last entries will be set to 0
	 * - the next MAX_CALL_SSE_REGISTERS entries will contain the float/double arguments
	 *   that go into the floating point registers. If the function has less than
	 *   MAX_CALL_SSE_REGISTERS floating point parameters then the last entries will
	 *   be set to 0
	 * - index MAX_CALL_INT_REGISTERS + MAX_CALL_SSE_REGISTERS marks the start of the
	 *   parameters which will get passed on the stack. These are added to the array
	 *   in reverse order so that X64_CallFunction() can simply push them to the stack
	 *   without the need to perform further tests
	 */
	asQWORD tempBuff[X64_CALLSTACK_SIZE] = { 0 };
	asBYTE  argsSet[X64_CALLSTACK_SIZE]  = { 0 };
	int     used_int_regs   = 0;
	int     used_sse_regs   = 0;
	int     used_stack_args = 0;
	int     idx             = 0;
	for ( n = 0; ( n < totalArgumentCount ) && ( used_int_regs < MAX_CALL_INT_REGISTERS ); n++ )
	{
		if ( argsType[n] == x64INTARG )
		{
			argsSet[n] = 1;
			tempBuff[idx++] = paramBuffer[n];
			used_int_regs++;
		}
	}
	idx = MAX_CALL_INT_REGISTERS;
	for ( n = 0; ( n < totalArgumentCount ) && ( used_sse_regs < MAX_CALL_SSE_REGISTERS ); n++ )
	{
		if ( argsType[n] == x64FLOATARG )
		{
			argsSet[n] = 1;
			tempBuff[idx++] = paramBuffer[n];
			used_sse_regs++;
		}
	}
	idx = MAX_CALL_INT_REGISTERS + MAX_CALL_SSE_REGISTERS;
	for ( n = totalArgumentCount - 1; n >= 0; n-- )
	{
		if ( !argsSet[n] )
		{
			tempBuff[idx++] = paramBuffer[n];
			used_stack_args++;
		}
	}

	retQW = X64_CallFunction( tempBuff, used_stack_args, func, retQW2, sysFunc->hostReturnFloat );

	return retQW;
}

END_AS_NAMESPACE

#endif // AS_X64_GCC
#endif // AS_MAX_PORTABILITY