Implement Static Function Pointers

This commit is contained in:
Ricardo Luís Vaz Silva 2023-02-10 09:44:32 -03:00 committed by Christoph Oelckers
commit e3704605d8
23 changed files with 876 additions and 51 deletions

View file

@ -949,6 +949,18 @@ void FFunctionBuildList::DumpJit(bool include_gzdoom_pk3)
#endif // HAVE_VM_JIT
}
FunctionCallEmitter::FunctionCallEmitter(VMFunction *func)
{
target = func;
is_vararg = target->VarFlags & VARF_VarArg;
}
FunctionCallEmitter::FunctionCallEmitter(class PFunctionPointer *func)
{
fnptr = func;
is_vararg = false; // function pointers cannot point to vararg functions
}
void FunctionCallEmitter::AddParameter(VMFunctionBuilder *build, FxExpression *operand)
{
@ -959,7 +971,7 @@ void FunctionCallEmitter::AddParameter(VMFunctionBuilder *build, FxExpression *o
operand->ScriptPosition.Message(MSG_ERROR, "Attempted to pass a non-value");
}
numparams += where.RegCount;
if (target->VarFlags & VARF_VarArg)
if (is_vararg)
for (unsigned i = 0; i < where.RegCount; i++) reginfo.Push(where.RegType & REGT_TYPE);
emitters.push_back([=](VMFunctionBuilder *build) -> int
@ -982,7 +994,7 @@ void FunctionCallEmitter::AddParameter(VMFunctionBuilder *build, FxExpression *o
void FunctionCallEmitter::AddParameter(ExpEmit &emit, bool reference)
{
numparams += emit.RegCount;
if (target->VarFlags & VARF_VarArg)
if (is_vararg)
{
if (reference) reginfo.Push(REGT_POINTER);
else for (unsigned i = 0; i < emit.RegCount; i++) reginfo.Push(emit.RegType & REGT_TYPE);
@ -999,7 +1011,7 @@ void FunctionCallEmitter::AddParameter(ExpEmit &emit, bool reference)
void FunctionCallEmitter::AddParameterPointerConst(void *konst)
{
numparams++;
if (target->VarFlags & VARF_VarArg)
if (is_vararg)
reginfo.Push(REGT_POINTER);
emitters.push_back([=](VMFunctionBuilder *build) ->int
{
@ -1011,7 +1023,7 @@ void FunctionCallEmitter::AddParameterPointerConst(void *konst)
void FunctionCallEmitter::AddParameterPointer(int index, bool konst)
{
numparams++;
if (target->VarFlags & VARF_VarArg)
if (is_vararg)
reginfo.Push(REGT_POINTER);
emitters.push_back([=](VMFunctionBuilder *build) ->int
{
@ -1023,7 +1035,7 @@ void FunctionCallEmitter::AddParameterPointer(int index, bool konst)
void FunctionCallEmitter::AddParameterFloatConst(double konst)
{
numparams++;
if (target->VarFlags & VARF_VarArg)
if (is_vararg)
reginfo.Push(REGT_FLOAT);
emitters.push_back([=](VMFunctionBuilder *build) ->int
{
@ -1035,7 +1047,7 @@ void FunctionCallEmitter::AddParameterFloatConst(double konst)
void FunctionCallEmitter::AddParameterIntConst(int konst)
{
numparams++;
if (target->VarFlags & VARF_VarArg)
if (is_vararg)
reginfo.Push(REGT_INT);
emitters.push_back([=](VMFunctionBuilder *build) ->int
{
@ -1055,7 +1067,7 @@ void FunctionCallEmitter::AddParameterIntConst(int konst)
void FunctionCallEmitter::AddParameterStringConst(const FString &konst)
{
numparams++;
if (target->VarFlags & VARF_VarArg)
if (is_vararg)
reginfo.Push(REGT_STRING);
emitters.push_back([=](VMFunctionBuilder *build) ->int
{
@ -1074,7 +1086,7 @@ ExpEmit FunctionCallEmitter::EmitCall(VMFunctionBuilder *build, TArray<ExpEmit>
paramcount += func(build);
}
assert(paramcount == numparams);
if (target->VarFlags & VARF_VarArg)
if (is_vararg)
{
// Pass a hidden type information parameter to vararg functions.
// It would really be nicer to actually pass real types but that'd require a far more complex interface on the compiler side than what we have.
@ -1084,9 +1096,24 @@ ExpEmit FunctionCallEmitter::EmitCall(VMFunctionBuilder *build, TArray<ExpEmit>
paramcount++;
}
if(fnptr)
{
ExpEmit reg(build, REGT_POINTER);
assert(fnptr->Scope != -1);
assert(fnptr->PointedType != TypeVoid);
// OP_LP , Load from memory. rA = *(rB + rkC)
// reg = &PFunction->Variants[0] -- PFunction::Variant*
build->Emit(OP_LP, reg.RegNum, virtualselfreg, build->GetConstantInt(offsetof(PFunction, Variants) + offsetof(FArray, Array)));
// reg = (&PFunction->Variants[0])->Implementation -- VMFunction*
build->Emit(OP_LP, reg.RegNum, reg.RegNum, build->GetConstantInt(offsetof(PFunction::Variant, Implementation)));
if (virtualselfreg == -1)
build->Emit(OP_CALL, reg.RegNum, paramcount, vm_jit? static_cast<PPrototype*>(fnptr->PointedType)->ReturnTypes.Size() : returns.Size());
reg.Free(build);
}
else if (virtualselfreg == -1)
{
build->Emit(OP_CALL_K, build->GetConstantAddress(target), paramcount, vm_jit ? target->Proto->ReturnTypes.Size() : returns.Size());
}
@ -1110,9 +1137,13 @@ ExpEmit FunctionCallEmitter::EmitCall(VMFunctionBuilder *build, TArray<ExpEmit>
}
if (vm_jit) // The JIT compiler needs this, but the VM interpreter does not.
{
for (unsigned i = returns.Size(); i < target->Proto->ReturnTypes.Size(); i++)
assert(!fnptr || fnptr->PointedType != TypeVoid);
PPrototype * proto = fnptr ? static_cast<PPrototype*>(fnptr->PointedType) : target->Proto;
for (unsigned i = returns.Size(); i < proto->ReturnTypes.Size(); i++)
{
ExpEmit reg(build, target->Proto->ReturnTypes[i]->RegType, target->Proto->ReturnTypes[i]->RegCount);
ExpEmit reg(build, proto->ReturnTypes[i]->RegType, proto->ReturnTypes[i]->RegCount);
build->Emit(OP_RESULT, 0, EncodeRegType(reg), reg.RegNum);
reg.Free(build);
}