/
redgpu
/
ezEngine
Обзор
Документация
Войти
/
redgpu
/
ezEngine
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
dev
Code/ThirdParty/AngelScript/source/as_callfunc_riscv64.cpp
322 строки
12 KB
Jan Krassnigg
Added AngelScript Thirdparty library (#1477)
01 фев 2025, 15:50
Не верифицирован
01 фев 2025, 15:50
f838b31
Код
Авторство
О чём код?
/* 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