- use the OP_PARAM and OP_RESULT opcodes to build the function signature

This commit is contained in:
Magnus Norddahl 2018-11-27 19:56:39 +01:00
parent 5e4e9e2c2b
commit 3202c86ea7
2 changed files with 92 additions and 51 deletions

View file

@ -324,7 +324,7 @@ void JitCompiler::EmitNativeCall(VMNativeFunction *target)
} }
asmjit::CBNode *cursorBefore = cc.getCursor(); asmjit::CBNode *cursorBefore = cc.getCursor();
auto call = cc.call(imm_ptr(target->DirectNativeCall), CreateFuncSignature(target)); auto call = cc.call(imm_ptr(target->DirectNativeCall), CreateFuncSignature());
call->setInlineComment(target->PrintableName.GetChars()); call->setInlineComment(target->PrintableName.GetChars());
asmjit::CBNode *cursorAfter = cc.getCursor(); asmjit::CBNode *cursorAfter = cc.getCursor();
cc.setCursor(cursorBefore); cc.setCursor(cursorBefore);
@ -460,81 +460,122 @@ void JitCompiler::EmitNativeCall(VMNativeFunction *target)
ParamOpcodes.Clear(); ParamOpcodes.Clear();
} }
asmjit::FuncSignature JitCompiler::CreateFuncSignature(VMFunction *func) asmjit::FuncSignature JitCompiler::CreateFuncSignature()
{ {
using namespace asmjit; using namespace asmjit;
TArray<uint8_t> args; TArray<uint8_t> args;
FString key; FString key;
for (unsigned int i = 0; i < func->Proto->ArgumentTypes.Size(); i++)
// First add parameters as args to the signature
for (unsigned int i = 0; i < ParamOpcodes.Size(); i++)
{ {
const PType *type = func->Proto->ArgumentTypes[i]; if (ParamOpcodes[i]->op == OP_PARAMI)
if (func->ArgFlags.Size() && func->ArgFlags[i] & (VARF_Out | VARF_Ref))
{
args.Push(TypeIdOf<void*>::kTypeId);
key += "v";
}
else if (type == TypeVector2)
{
args.Push(TypeIdOf<double>::kTypeId);
args.Push(TypeIdOf<double>::kTypeId);
key += "ff";
}
else if (type == TypeVector3)
{
args.Push(TypeIdOf<double>::kTypeId);
args.Push(TypeIdOf<double>::kTypeId);
args.Push(TypeIdOf<double>::kTypeId);
key += "fff";
}
else if (type == TypeFloat64)
{
args.Push(TypeIdOf<double>::kTypeId);
key += "f";
}
else if (type == TypeString)
{
args.Push(TypeIdOf<void*>::kTypeId);
key += "s";
}
else if (type->isIntCompatible())
{ {
args.Push(TypeIdOf<int>::kTypeId); args.Push(TypeIdOf<int>::kTypeId);
key += "i"; key += "i";
} }
else else // OP_PARAM
{ {
args.Push(TypeIdOf<void*>::kTypeId); int bc = ParamOpcodes[i]->i16u;
key += "v"; switch (ParamOpcodes[i]->a)
{
case REGT_NIL:
case REGT_POINTER:
case REGT_POINTER | REGT_KONST:
case REGT_STRING | REGT_ADDROF:
case REGT_INT | REGT_ADDROF:
case REGT_POINTER | REGT_ADDROF:
case REGT_FLOAT | REGT_ADDROF:
args.Push(TypeIdOf<void*>::kTypeId);
key += "v";
break;
case REGT_INT:
case REGT_INT | REGT_KONST:
args.Push(TypeIdOf<int>::kTypeId);
key += "i";
break;
case REGT_STRING:
case REGT_STRING | REGT_KONST:
args.Push(TypeIdOf<void*>::kTypeId);
key += "s";
break;
case REGT_FLOAT:
case REGT_FLOAT | REGT_KONST:
args.Push(TypeIdOf<double>::kTypeId);
key += "f";
break;
case REGT_FLOAT | REGT_MULTIREG2:
args.Push(TypeIdOf<double>::kTypeId);
args.Push(TypeIdOf<double>::kTypeId);
key += "ff";
break;
case REGT_FLOAT | REGT_MULTIREG3:
args.Push(TypeIdOf<double>::kTypeId);
args.Push(TypeIdOf<double>::kTypeId);
args.Push(TypeIdOf<double>::kTypeId);
key += "fff";
break;
default:
I_FatalError("Unknown REGT value passed to EmitPARAM\n");
break;
}
} }
} }
const VMOP *retval = pc + 1;
int numret = C;
uint32_t rettype = TypeIdOf<void>::kTypeId; uint32_t rettype = TypeIdOf<void>::kTypeId;
if (func->Proto->ReturnTypes.Size() > 0)
// Check if first return value can be placed in the function's real return value slot
int startret = 1;
if (numret > 0)
{ {
const PType *type = func->Proto->ReturnTypes[0]; if (retval[0].op != OP_RESULT)
if (type == TypeFloat64)
{ {
rettype = TypeIdOf<double>::kTypeId; I_FatalError("Expected OP_RESULT to follow OP_CALL\n");
key += "rf";
} }
else if (type == TypeString)
{ int type = retval[0].b;
rettype = TypeIdOf<void*>::kTypeId; switch (type)
key += "rs";
}
else if (type->isIntCompatible())
{ {
case REGT_INT:
rettype = TypeIdOf<int>::kTypeId; rettype = TypeIdOf<int>::kTypeId;
key += "ri"; key += "ri";
} break;
else case REGT_FLOAT:
{ rettype = TypeIdOf<double>::kTypeId;
key += "rf";
break;
case REGT_STRING:
rettype = TypeIdOf<void*>::kTypeId;
key += "rs";
break;
case REGT_POINTER:
rettype = TypeIdOf<void*>::kTypeId; rettype = TypeIdOf<void*>::kTypeId;
key += "rv"; key += "rv";
break;
default:
startret = 0;
break;
} }
} }
// Add any additional return values as function arguments
for (int i = startret; i < numret; ++i)
{
if (retval[i].op != OP_RESULT)
{
I_FatalError("Expected OP_RESULT to follow OP_CALL\n");
}
args.Push(TypeIdOf<void*>::kTypeId);
key += "v";
}
// FuncSignature only keeps a pointer to its args array. Store a copy of each args array variant. // FuncSignature only keeps a pointer to its args array. Store a copy of each args array variant.
static std::map<FString, std::unique_ptr<TArray<uint8_t>>> argsCache; static std::map<FString, std::unique_ptr<TArray<uint8_t>>> argsCache;
std::unique_ptr<TArray<uint8_t>> &cachedArgs = argsCache[key]; std::unique_ptr<TArray<uint8_t>> &cachedArgs = argsCache[key];

View file

@ -38,7 +38,7 @@ private:
#include "vmops.h" #include "vmops.h"
#undef xx #undef xx
static asmjit::FuncSignature CreateFuncSignature(VMFunction *sfunc); asmjit::FuncSignature CreateFuncSignature();
void Setup(); void Setup();
void CreateRegisters(); void CreateRegisters();