123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330 |
- /*
- AngelCode Scripting Library
- Copyright (c) 2020-2021 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_arm64.cpp
- //
- // These functions handle the actual calling of system functions on the arm64 platform
- //
- // Written by Max Waine in July 2020, based on as_callfunc_arm.cpp
- //
- #include "as_config.h"
- #ifndef AS_MAX_PORTABILITY
- #ifdef AS_ARM64
- #include "as_callfunc.h"
- #include "as_scriptengine.h"
- #include "as_texts.h"
- #include "as_tokendef.h"
- #include "as_context.h"
- // ARM64 targets use has no software floating-point ABI, it's all hardware (or totally disabled)
- #define HFA_RET_REGISTERS 4 // s0-s3/d0-d3
- #define GP_ARG_REGISTERS 8 // x0-x7
- #define FLOAT_ARG_REGISTERS 8 // v0-v7
- BEGIN_AS_NAMESPACE
- // x0-7: Argument registers (pass params or return results. OK as volatile local variables)
- // x8: Indirect result register (e.g. address of large returned struct)
- // x9-15: Volatile local variable registers
- // x16-17: Intra-procedure-call temporary registers
- // x18: Platform register (reserved for use of platform ABIs)
- // x19-29: Non-volatile variable registers (must be saved and restored if modified)
- // x29: Frame pointer register
- // x30: Link register (where to return to)
- extern "C" void GetHFAReturnDouble(asQWORD *out1, asQWORD *out2, asQWORD returnSize);
- extern "C" void GetHFAReturnFloat(asQWORD *out1, asQWORD *out2, asQWORD returnSize);
- extern "C" asQWORD CallARM64RetInMemory(
- const asQWORD *gpRegArgs, asQWORD numGPRegArgs,
- const asQWORD *floatRegArgs, asQWORD numFloatRegArgs,
- const asQWORD *stackArgs, asQWORD numStackArgs,
- void *retPointer, asFUNCTION_t func
- );
- extern "C" double CallARM64Double(
- const asQWORD *gpRegArgs, asQWORD numGPRegArgs,
- const asQWORD *floatRegArgs, asQWORD numFloatRegArgs,
- const asQWORD *stackArgs, asQWORD numStackArgs,
- asFUNCTION_t func
- );
- extern "C" float CallARM64Float(
- const asQWORD *gpRegArgs, asQWORD numGPRegArgs,
- const asQWORD *floatRegArgs, asQWORD numFloatRegArgs,
- const asQWORD *stackArgs, asQWORD numStackArgs,
- asFUNCTION_t func
- );
- extern "C" asQWORD CallARM64(
- const asQWORD *gpRegArgs, asQWORD numGPRegArgs,
- const asQWORD *floatRegArgs, asQWORD numFloatRegArgs,
- const asQWORD *stackArgs, asQWORD numStackArgs,
- asFUNCTION_t func
- );
- extern "C" asQWORD CallARM64Ret128(
- const asQWORD *gpRegArgs, asQWORD numGPRegArgs,
- const asQWORD *floatRegArgs, asQWORD numFloatRegArgs,
- const asQWORD *stackArgs, asQWORD numStackArgs,
- asQWORD *higherQWORD, asFUNCTION_t func
- );
- //
- // If it's possible to fit in registers,
- // there may not be enough float register space even if true is returned
- //
- static inline bool IsRegisterHFA(const asCDataType &type)
- {
- const asCTypeInfo *const typeInfo = type.GetTypeInfo();
- if( typeInfo == nullptr ||
- (typeInfo->flags & asOBJ_APP_CLASS_ALLFLOATS) == 0 ||
- type.IsObjectHandle() || type.IsReference() )
- return false;
- const bool doubles = (typeInfo->flags & asOBJ_APP_CLASS_ALIGN8) != 0;
- const int maxAllowedSize = doubles ? sizeof(double) * HFA_RET_REGISTERS : sizeof(float) * HFA_RET_REGISTERS;
- return type.GetSizeInMemoryBytes() <= maxAllowedSize;
- }
- //
- // If it's possible to fit it in registers,
- // if true is returned there is enough space to fit
- //
- static inline bool IsRegisterHFAParameter(const asCDataType &type, const asQWORD numFloatRegArgs)
- {
- if( !IsRegisterHFA(type) )
- return false;
- const bool doubles = (type.GetTypeInfo()->flags & asOBJ_APP_CLASS_ALIGN8) != 0;
- const int registersUsed = type.GetSizeInMemoryDWords() / (doubles ? sizeof(double) : sizeof(float));
- return numFloatRegArgs + registersUsed <= FLOAT_ARG_REGISTERS;
- }
- asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &retQW2, void *secondObject)
- {
- 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;
- asQWORD retQW = 0;
- asQWORD gpRegArgs[GP_ARG_REGISTERS];
- asQWORD floatRegArgs[FLOAT_ARG_REGISTERS];
- asQWORD stackArgs[64]; // It's how many x64 users can have
- asQWORD numGPRegArgs = 0;
- asQWORD numFloatRegArgs = 0;
- asQWORD numStackArgs = 0;
- asFUNCTION_t *vftable;
- // Optimization to avoid check 12 values (all ICC_ that contains THISCALL)
- if( (callConv >= ICC_THISCALL && callConv <= ICC_VIRTUAL_THISCALL_RETURNINMEM) ||
- (callConv >= ICC_THISCALL_OBJLAST && callConv <= ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM) )
- {
- // Add the object pointer as the first parameter
- gpRegArgs[numGPRegArgs++] = (asQWORD)obj;
- }
- if( callConv == ICC_CDECL_OBJFIRST || callConv == ICC_CDECL_OBJFIRST_RETURNINMEM )
- {
- // Add the object pointer as the first parameter
- gpRegArgs[numGPRegArgs++] = (asQWORD)obj;
- }
- else if( callConv == ICC_THISCALL_OBJFIRST || callConv == ICC_THISCALL_OBJFIRST_RETURNINMEM ||
- callConv == ICC_VIRTUAL_THISCALL_OBJFIRST || callConv == ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM )
- {
- // Add the object pointer as the first parameter
- gpRegArgs[numGPRegArgs++] = (asQWORD)secondObject;
- }
- if( 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 )
- {
- // Get virtual function table from the object pointer
- vftable = *(asFUNCTION_t**)obj;
- func = vftable[FuncPtrToUInt(func)/sizeof(void*)];
- }
- asUINT argsPos = 0;
- for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ )
- {
- const asCDataType &parmType = descr->parameterTypes[n];
- const asCTypeInfo *const parmTypeInfo = parmType.GetTypeInfo();
- if( parmType.IsObject() && !parmType.IsObjectHandle() && !parmType.IsReference() )
- {
- const asUINT parmDWords = parmType.GetSizeInMemoryDWords();
- const asUINT parmQWords = (parmDWords >> 1) + (parmDWords & 1);
- const bool passedAsPointer = parmQWords <= 2;
- const bool fitsInRegisters = passedAsPointer ? (numGPRegArgs < GP_ARG_REGISTERS) : (numGPRegArgs + parmQWords <= GP_ARG_REGISTERS);
- asQWORD *const argsArray = fitsInRegisters ? gpRegArgs : stackArgs;
- asQWORD &numArgs = fitsInRegisters ? numGPRegArgs : numStackArgs;
- if( (parmTypeInfo->flags & COMPLEX_MASK) )
- {
- argsArray[numArgs++] = *(asQWORD*)&args[argsPos];
- argsPos += AS_PTR_SIZE;
- }
- else if( IsRegisterHFAParameter(parmType, numFloatRegArgs) )
- {
- if( (parmTypeInfo->flags & asOBJ_APP_CLASS_ALIGN8) != 0 )
- {
- const asQWORD *const contents = *(asQWORD**)&args[argsPos];
- for( asUINT i = 0; i < parmQWords; i++ )
- floatRegArgs[numFloatRegArgs++] = *(asQWORD*)&contents[i];
- }
- else
- {
- const asDWORD *const contents = *(asDWORD**)&args[argsPos];
- for( asUINT i = 0; i < parmDWords; i++ )
- floatRegArgs[numFloatRegArgs++] = *(asQWORD*)&contents[i];
- }
- engine->CallFree(*(char**)(args+argsPos));
- argsPos += AS_PTR_SIZE;
- }
- else
- {
- // Copy the object's memory to the buffer
- memcpy(&argsArray[numArgs], *(void**)(args+argsPos), parmType.GetSizeInMemoryBytes());
- // Delete the original memory
- engine->CallFree(*(char**)(args+argsPos));
- argsPos += AS_PTR_SIZE;
- numArgs += parmQWords;
- }
- }
- else if( parmType.IsFloatType() && !parmType.IsReference() )
- {
- if( numFloatRegArgs >= FLOAT_ARG_REGISTERS )
- stackArgs[numStackArgs++] = args[argsPos];
- else
- floatRegArgs[numFloatRegArgs++] = args[argsPos];
- argsPos++;
- }
- else if( parmType.IsDoubleType() && !parmType.IsReference() )
- {
- if( numFloatRegArgs >= FLOAT_ARG_REGISTERS )
- stackArgs[numStackArgs++] = *(asQWORD*)&args[argsPos];
- else
- floatRegArgs[numFloatRegArgs++] = *(asQWORD*)&args[argsPos];
- argsPos += 2;
- }
- else
- {
- // Copy the value directly
- const asUINT parmDWords = parmType.GetSizeOnStackDWords();
- const asUINT parmQWords = (parmDWords >> 1) + (parmDWords & 1);
- const bool fitsInRegisters = numGPRegArgs + parmQWords <= GP_ARG_REGISTERS;
- asQWORD *const argsArray = fitsInRegisters ? gpRegArgs : stackArgs;
- asQWORD &numArgs = fitsInRegisters ? numGPRegArgs : numStackArgs;
- memcpy(&argsArray[numArgs], (void*)(args+argsPos), parmDWords * 4);
- argsPos += parmDWords;
- numArgs += parmQWords;
- }
- }
- if( callConv == ICC_CDECL_OBJLAST || callConv == ICC_CDECL_OBJLAST_RETURNINMEM )
- {
- // Add the object pointer as the last parameter
- if( numGPRegArgs < GP_ARG_REGISTERS )
- gpRegArgs[numGPRegArgs++] = (asQWORD)obj;
- else
- stackArgs[numStackArgs++] = (asQWORD)obj;
- }
- else if( callConv == ICC_THISCALL_OBJLAST || callConv == ICC_THISCALL_OBJLAST_RETURNINMEM ||
- callConv == ICC_VIRTUAL_THISCALL_OBJLAST || callConv == ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM )
- {
- // Add the object pointer as the last parameter
- if( numGPRegArgs < GP_ARG_REGISTERS )
- gpRegArgs[numGPRegArgs++] = (asQWORD)secondObject;
- else
- stackArgs[numStackArgs++] = (asQWORD)secondObject;
- }
- if( IsRegisterHFA(retType) && !(retTypeInfo->flags & COMPLEX_MASK) )
- {
- // This is to deal with HFAs (Homogeneous Floating-point Aggregates):
- // ARM64 will place all-float composite types (of equal precision)
- // with <= 4 members in the float return registers
- const int structSize = retType.GetSizeInMemoryBytes();
- CallARM64(gpRegArgs, numGPRegArgs, floatRegArgs, numFloatRegArgs, stackArgs, numStackArgs, func);
- if( (retTypeInfo->flags & asOBJ_APP_CLASS_ALIGN8) != 0 )
- {
- if( structSize <= sizeof(double) * 2 )
- GetHFAReturnDouble(&retQW, &retQW2, structSize);
- else
- GetHFAReturnDouble((asQWORD*)retPointer, ((asQWORD*)retPointer) + 1, structSize);
- }
- else
- GetHFAReturnFloat(&retQW, &retQW2, structSize);
- }
- else if( sysFunc->hostReturnFloat )
- {
- if( sysFunc->hostReturnSize == 1 )
- *(float*)&retQW = CallARM64Float(gpRegArgs, numGPRegArgs, floatRegArgs, numFloatRegArgs, stackArgs, numStackArgs, func);
- else
- *(double*)&retQW = CallARM64Double(gpRegArgs, numGPRegArgs, floatRegArgs, numFloatRegArgs, stackArgs, numStackArgs, func);
- }
- else if( sysFunc->hostReturnInMemory )
- retQW = CallARM64RetInMemory(gpRegArgs, numGPRegArgs, floatRegArgs, numFloatRegArgs, stackArgs, numStackArgs, retPointer, func);
- else
- {
- if( retType.GetSizeInMemoryBytes() > sizeof(asQWORD) )
- retQW = CallARM64Ret128(gpRegArgs, numGPRegArgs, floatRegArgs, numFloatRegArgs, stackArgs, numStackArgs, &retQW2, func);
- else
- retQW = CallARM64(gpRegArgs, numGPRegArgs, floatRegArgs, numFloatRegArgs, stackArgs, numStackArgs, func);
- }
- return retQW;
- }
- END_AS_NAMESPACE
- #endif // AS_ARM64
- #endif // AS_MAX_PORTABILITY
|