- pass additional return values as the last args to a direct native call

This commit is contained in:
Magnus Norddahl 2018-11-27 20:43:35 +01:00
parent 3202c86ea7
commit 8860517bbd

View file

@ -403,36 +403,78 @@ void JitCompiler::EmitNativeCall(VMNativeFunction *target)
} }
} }
cc.setCursor(cursorAfter);
if (numparams != B) if (numparams != B)
I_FatalError("OP_CALL parameter count does not match the number of preceding OP_PARAM instructions\n"); I_FatalError("OP_CALL parameter count does not match the number of preceding OP_PARAM instructions\n");
int numret = C; // Note: the usage of newResultXX is intentional. Asmjit has a register allocation bug
if (numret > 1) // if the return virtual register is already allocated in an argument slot.
I_FatalError("Only one return parameter is supported for direct native calls\n");
if (numret == 1) const VMOP *retval = pc + 1;
int numret = C;
// Check if first return value was placed in the function's real return value slot
int startret = 1;
if (numret > 0)
{ {
const auto &retval = pc[1]; int type = retval[0].b;
if (retval.op != OP_RESULT) switch (type)
{ {
I_FatalError("Expected OP_RESULT to follow OP_CALL\n"); case REGT_INT:
case REGT_FLOAT:
case REGT_POINTER:
break;
default:
startret = 0;
break;
}
} }
int type = retval.b; // Pass return pointers as arguments
int regnum = retval.c; for (int i = startret; i < numret; ++i)
{
int type = retval[i].b;
int regnum = retval[i].c;
if (type & REGT_KONST) if (type & REGT_KONST)
{ {
I_FatalError("OP_RESULT with REGT_KONST is not allowed\n"); I_FatalError("OP_RESULT with REGT_KONST is not allowed\n");
} }
// Note: the usage of newResultXX is intentional. Asmjit has a register allocation bug CheckVMFrame();
// if the return virtual register is already allocated in an argument slot.
auto regPtr = newTempIntPtr();
switch (type & REGT_TYPE) switch (type & REGT_TYPE)
{ {
case REGT_INT:
cc.lea(regPtr, x86::ptr(vmframe, offsetD + (int)(regnum * sizeof(int32_t))));
break;
case REGT_FLOAT:
cc.lea(regPtr, x86::ptr(vmframe, offsetF + (int)(regnum * sizeof(double))));
break;
case REGT_STRING:
cc.lea(regPtr, x86::ptr(vmframe, offsetS + (int)(regnum * sizeof(FString))));
break;
case REGT_POINTER:
cc.lea(regPtr, x86::ptr(vmframe, offsetA + (int)(regnum * sizeof(void*))));
break;
default:
I_FatalError("Unknown OP_RESULT type encountered\n");
break;
}
cc.setArg(numparams + i - startret, regPtr);
}
cc.setCursor(cursorAfter);
if (startret == 1 && numret > 0)
{
int type = retval[0].b;
int regnum = retval[0].c;
switch (type)
{
case REGT_INT: case REGT_INT:
tmp = newResultInt32(); tmp = newResultInt32();
call->setRet(0, tmp); call->setRet(0, tmp);
@ -448,11 +490,40 @@ void JitCompiler::EmitNativeCall(VMNativeFunction *target)
call->setRet(0, tmp); call->setRet(0, tmp);
cc.mov(regA[regnum], tmp); cc.mov(regA[regnum], tmp);
break; break;
case REGT_STRING: }
}
// Move the result into virtual registers
for (int i = startret; i < numret; ++i)
{
int type = retval[i].b;
int regnum = retval[i].c;
switch (type)
{
case REGT_INT:
cc.mov(regD[regnum], asmjit::x86::dword_ptr(vmframe, offsetD + regnum * sizeof(int32_t)));
break;
case REGT_FLOAT:
cc.movsd(regF[regnum], asmjit::x86::qword_ptr(vmframe, offsetF + regnum * sizeof(double)));
break;
case REGT_FLOAT | REGT_MULTIREG2: case REGT_FLOAT | REGT_MULTIREG2:
cc.movsd(regF[regnum], asmjit::x86::qword_ptr(vmframe, offsetF + regnum * sizeof(double)));
cc.movsd(regF[regnum + 1], asmjit::x86::qword_ptr(vmframe, offsetF + (regnum + 1) * sizeof(double)));
break;
case REGT_FLOAT | REGT_MULTIREG3: case REGT_FLOAT | REGT_MULTIREG3:
cc.movsd(regF[regnum], asmjit::x86::qword_ptr(vmframe, offsetF + regnum * sizeof(double)));
cc.movsd(regF[regnum + 1], asmjit::x86::qword_ptr(vmframe, offsetF + (regnum + 1) * sizeof(double)));
cc.movsd(regF[regnum + 2], asmjit::x86::qword_ptr(vmframe, offsetF + (regnum + 2) * sizeof(double)));
break;
case REGT_STRING:
// We don't have to do anything in this case. String values are never moved to virtual registers.
break;
case REGT_POINTER:
cc.mov(regA[regnum], asmjit::x86::ptr(vmframe, offsetA + regnum * sizeof(void*)));
break;
default: default:
I_FatalError("Unsupported OP_RESULT type encountered in EmitNativeCall\n"); I_FatalError("Unknown OP_RESULT type encountered\n");
break; break;
} }
} }