From 629d329f2215c00994eb55f5c1e80cc58e39eddb Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 18 Nov 2018 07:43:03 +0100 Subject: [PATCH 01/17] - removed OP_TAIL. The amount of support code for this minor optimization was quite large and this stood in the way of streamlining the VM's calling convention, so it was preferable to remove it before moving on. --- src/d_dehacked.cpp | 3 +- src/scripting/backend/codegen.cpp | 114 ++-------------------------- src/scripting/backend/codegen.h | 9 --- src/scripting/backend/vmbuilder.cpp | 2 +- src/scripting/backend/vmdisasm.cpp | 3 +- src/scripting/vm/jit_call.cpp | 56 -------------- src/scripting/vm/jitintern.h | 3 - src/scripting/vm/vmexec.h | 39 ---------- src/scripting/vm/vmops.h | 2 - 9 files changed, 12 insertions(+), 219 deletions(-) diff --git a/src/d_dehacked.cpp b/src/d_dehacked.cpp index aaac4b879..9004dc555 100644 --- a/src/d_dehacked.cpp +++ b/src/d_dehacked.cpp @@ -784,7 +784,8 @@ void SetDehParams(FState *state, int codepointer) // Emit code for action parameters. MBFCodePointerFactories[codepointer](emitters, value1, value2); int count = emitters.EmitParameters(&buildit); - buildit.Emit(OP_TAIL_K, buildit.GetConstantAddress(sym->Variants[0].Implementation), count, 0); + buildit.Emit(OP_CALL_K, buildit.GetConstantAddress(sym->Variants[0].Implementation), count, 0); + buildit.Emit(OP_RET, RET_FINAL, REGT_NIL, 0); // Attach it to the state. VMScriptFunction *sfunc = new VMScriptFunction; buildit.MakeFunction(sfunc); diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index 26e2ccd99..74ab9a1c8 100644 --- a/src/scripting/backend/codegen.cpp +++ b/src/scripting/backend/codegen.cpp @@ -5449,18 +5449,6 @@ FxRandom::~FxRandom() // //========================================================================== -PPrototype *FxRandom::ReturnProto() -{ - EmitTail = true; - return FxExpression::ReturnProto(); -} - -//========================================================================== -// -// -// -//========================================================================== - FxExpression *FxRandom::Resolve(FCompileContext &ctx) { CHECKRESOLVED(); @@ -5506,23 +5494,13 @@ ExpEmit FxRandom::Emit(VMFunctionBuilder *build) assert(min && max); callfunc = ((PSymbolVMFunction *)sym)->Function; - if (build->FramePointer.Fixed) EmitTail = false; // do not tail call if the stack is in use - int opcode = (EmitTail ? OP_TAIL_K : OP_CALL_K); - EmitterArray emitters; emitters.AddParameterPointerConst(rng); emitters.AddParameter(build, min); emitters.AddParameter(build, max); int count = emitters.EmitParameters(build); - build->Emit(opcode, build->GetConstantAddress(callfunc), count, 1); - - if (EmitTail) - { - ExpEmit call; - call.Final = true; - return call; - } + build->Emit(OP_CALL_K, build->GetConstantAddress(callfunc), count, 1); ExpEmit out(build, REGT_INT); build->Emit(OP_RESULT, 0, REGT_INT, out.RegNum); @@ -5747,22 +5725,12 @@ ExpEmit FxFRandom::Emit(VMFunctionBuilder *build) assert(min && max); callfunc = ((PSymbolVMFunction *)sym)->Function; - if (build->FramePointer.Fixed) EmitTail = false; // do not tail call if the stack is in use - int opcode = (EmitTail ? OP_TAIL_K : OP_CALL_K); - EmitterArray emitters; emitters.AddParameterPointerConst(rng); emitters.AddParameter(build, min); emitters.AddParameter(build, max); int count = emitters.EmitParameters(build); - build->Emit(opcode, build->GetConstantAddress(callfunc), count, 1); - - if (EmitTail) - { - ExpEmit call; - call.Final = true; - return call; - } + build->Emit(OP_CALL_K, build->GetConstantAddress(callfunc), count, 1); ExpEmit out(build, REGT_FLOAT); build->Emit(OP_RESULT, 0, REGT_FLOAT, out.RegNum); @@ -5778,7 +5746,6 @@ ExpEmit FxFRandom::Emit(VMFunctionBuilder *build) FxRandom2::FxRandom2(FRandom *r, FxExpression *m, const FScriptPosition &pos, bool nowarn) : FxExpression(EFX_Random2, pos) { - EmitTail = false; rng = r; if (m) mask = new FxIntCast(m, nowarn); else mask = new FxConstant(-1, pos); @@ -5802,18 +5769,6 @@ FxRandom2::~FxRandom2() // //========================================================================== -PPrototype *FxRandom2::ReturnProto() -{ - EmitTail = true; - return FxExpression::ReturnProto(); -} - -//========================================================================== -// -// -// -//========================================================================== - FxExpression *FxRandom2::Resolve(FCompileContext &ctx) { CHECKRESOLVED(); @@ -5851,22 +5806,12 @@ ExpEmit FxRandom2::Emit(VMFunctionBuilder *build) assert(((PSymbolVMFunction *)sym)->Function != nullptr); callfunc = ((PSymbolVMFunction *)sym)->Function; - if (build->FramePointer.Fixed) EmitTail = false; // do not tail call if the stack is in use - int opcode = (EmitTail ? OP_TAIL_K : OP_CALL_K); - EmitterArray emitters; emitters.AddParameterPointerConst(rng); emitters.AddParameter(build, mask); int count = emitters.EmitParameters(build); - build->Emit(opcode, build->GetConstantAddress(callfunc), count, 1); - - if (EmitTail) - { - ExpEmit call; - call.Final = true; - return call; - } + build->Emit(OP_CALL_K, build->GetConstantAddress(callfunc), count, 1); ExpEmit out(build, REGT_INT); build->Emit(OP_RESULT, 0, REGT_INT, out.RegNum); @@ -5881,7 +5826,6 @@ ExpEmit FxRandom2::Emit(VMFunctionBuilder *build) FxRandomSeed::FxRandomSeed(FRandom * r, FxExpression *s, const FScriptPosition &pos, bool nowarn) : FxExpression(EFX_Random, pos) { - EmitTail = false; seed = new FxIntCast(s, nowarn); rng = r; ValueType = TypeVoid; @@ -5937,18 +5881,14 @@ ExpEmit FxRandomSeed::Emit(VMFunctionBuilder *build) assert(((PSymbolVMFunction *)sym)->Function != nullptr); callfunc = ((PSymbolVMFunction *)sym)->Function; - if (build->FramePointer.Fixed) EmitTail = false; // do not tail call if the stack is in use - int opcode = (EmitTail ? OP_TAIL_K : OP_CALL_K); - EmitterArray emitters; emitters.AddParameterPointerConst(rng); emitters.AddParameter(build, seed); int count = emitters.EmitParameters(build); - build->Emit(opcode, build->GetConstantAddress(callfunc), count, 0); + build->Emit(OP_CALL_K, build->GetConstantAddress(callfunc), count, 0); ExpEmit call; - if (EmitTail) call.Final = true; return call; } @@ -6636,7 +6576,6 @@ FxClassDefaults::FxClassDefaults(FxExpression *X, const FScriptPosition &pos) : FxExpression(EFX_ClassDefaults, pos) { obj = X; - EmitTail = false; } FxClassDefaults::~FxClassDefaults() @@ -8508,7 +8447,6 @@ FxActionSpecialCall::FxActionSpecialCall(FxExpression *self, int special, FArgum { ArgList.Push(new FxConstant(0, ScriptPosition)); } - EmitTail = false; } //========================================================================== @@ -8528,18 +8466,6 @@ FxActionSpecialCall::~FxActionSpecialCall() // //========================================================================== -PPrototype *FxActionSpecialCall::ReturnProto() -{ - EmitTail = true; - return FxExpression::ReturnProto(); -} - -//========================================================================== -// -// -// -//========================================================================== - FxExpression *FxActionSpecialCall::Resolve(FCompileContext& ctx) { CHECKRESOLVED(); @@ -8664,16 +8590,8 @@ ExpEmit FxActionSpecialCall::Emit(VMFunctionBuilder *build) ArgList.DeleteAndClear(); ArgList.ShrinkToFit(); - if (build->FramePointer.Fixed) EmitTail = false; // do not tail call if the stack is in use int count = emitters.EmitParameters(build); selfemit.Free(build); - if (EmitTail) - { - build->Emit(OP_TAIL_K, build->GetConstantAddress(callfunc), count, 0); - ExpEmit call; - call.Final = true; - return call; - } ExpEmit dest(build, REGT_INT); build->Emit(OP_CALL_K, build->GetConstantAddress(callfunc), count, 1); @@ -8717,7 +8635,7 @@ PPrototype *FxVMFunctionCall::ReturnProto() { if (hasStringArgs) return FxExpression::ReturnProto(); - EmitTail = true; + return Function->Variants[0].Proto; } @@ -9068,12 +8986,10 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build) assert(build->Registers[REGT_POINTER].GetMostUsed() >= build->NumImplicits); int count = 0; - if (build->FramePointer.Fixed) EmitTail = false; // do not tail call if the stack is in use - if (count == 1) { ExpEmit reg; - if (CheckEmitCast(build, EmitTail, reg)) + if (CheckEmitCast(build, false, reg)) { ArgList.DeleteAndClear(); ArgList.ShrinkToFit(); @@ -9166,14 +9082,7 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build) { int funcaddr = build->GetConstantAddress(vmfunc); // Emit the call - if (EmitTail) - { // Tail call - build->Emit(OP_TAIL_K, funcaddr, count, 0); - ExpEmit call; - call.Final = true; - return call; - } - else if (vmfunc->Proto->ReturnTypes.Size() > 0) + if (vmfunc->Proto->ReturnTypes.Size() > 0) { // Call, expecting one result build->Emit(OP_CALL_K, funcaddr, count, MAX(1, AssignCount)); goto handlereturns; @@ -9189,14 +9098,7 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build) ExpEmit funcreg(build, REGT_POINTER); build->Emit(OP_VTBL, funcreg.RegNum, selfemit.RegNum, vmfunc->VirtualIndex); - if (EmitTail) - { // Tail call - build->Emit(OP_TAIL, funcreg.RegNum, count, 0); - ExpEmit call; - call.Final = true; - return call; - } - else if (vmfunc->Proto->ReturnTypes.Size() > 0) + if (vmfunc->Proto->ReturnTypes.Size() > 0) { // Call, expecting one result build->Emit(OP_CALL, funcreg.RegNum, count, MAX(1, AssignCount)); goto handlereturns; diff --git a/src/scripting/backend/codegen.h b/src/scripting/backend/codegen.h index 9e93ec6f3..ba0bb4d41 100644 --- a/src/scripting/backend/codegen.h +++ b/src/scripting/backend/codegen.h @@ -404,7 +404,6 @@ public: class FxClassDefaults : public FxExpression { FxExpression *obj; - bool EmitTail; public: FxClassDefaults(FxExpression *, const FScriptPosition &); @@ -1257,7 +1256,6 @@ public: class FxRandom : public FxExpression { protected: - bool EmitTail = false; FRandom *rng; FxExpression *min, *max; @@ -1267,7 +1265,6 @@ public: FxRandom(FRandom *, FxExpression *mi, FxExpression *ma, const FScriptPosition &pos, bool nowarn); ~FxRandom(); FxExpression *Resolve(FCompileContext&); - PPrototype *ReturnProto(); ExpEmit Emit(VMFunctionBuilder *build); }; @@ -1313,7 +1310,6 @@ public: class FxRandom2 : public FxExpression { - bool EmitTail; FRandom * rng; FxExpression *mask; @@ -1322,7 +1318,6 @@ public: FxRandom2(FRandom *, FxExpression *m, const FScriptPosition &pos, bool nowarn); ~FxRandom2(); FxExpression *Resolve(FCompileContext&); - PPrototype *ReturnProto(); ExpEmit Emit(VMFunctionBuilder *build); }; @@ -1336,7 +1331,6 @@ public: class FxRandomSeed : public FxExpression { protected: - bool EmitTail; FRandom *rng; FxExpression *seed; @@ -1583,7 +1577,6 @@ public: class FxActionSpecialCall : public FxExpression { int Special; - bool EmitTail; FxExpression *Self; FArgumentList ArgList; @@ -1592,7 +1585,6 @@ public: FxActionSpecialCall(FxExpression *self, int special, FArgumentList &args, const FScriptPosition &pos); ~FxActionSpecialCall(); FxExpression *Resolve(FCompileContext&); - PPrototype *ReturnProto(); ExpEmit Emit(VMFunctionBuilder *build); }; @@ -1752,7 +1744,6 @@ class FxVMFunctionCall : public FxExpression { friend class FxMultiAssign; - bool EmitTail = false; bool NoVirtual; bool hasStringArgs = false; FxExpression *Self; diff --git a/src/scripting/backend/vmbuilder.cpp b/src/scripting/backend/vmbuilder.cpp index 6c16e1a3a..ecd8c2d29 100644 --- a/src/scripting/backend/vmbuilder.cpp +++ b/src/scripting/backend/vmbuilder.cpp @@ -606,7 +606,7 @@ size_t VMFunctionBuilder::Emit(int opcode, int opa, int opb, int opc) emit.Free(this); } - if (opcode == OP_CALL || opcode == OP_CALL_K || opcode == OP_TAIL || opcode == OP_TAIL_K) + if (opcode == OP_CALL || opcode == OP_CALL_K) { ParamChange(-opb); } diff --git a/src/scripting/backend/vmdisasm.cpp b/src/scripting/backend/vmdisasm.cpp index 53b8891c1..ebde2ef57 100644 --- a/src/scripting/backend/vmdisasm.cpp +++ b/src/scripting/backend/vmdisasm.cpp @@ -330,7 +330,6 @@ void VMDisasm(FILE *out, const VMOP *code, int codesize, const VMScriptFunction break; case OP_CALL_K: - case OP_TAIL_K: { callfunc = (VMFunction *)func->KonstA[code[i].a].o; col = printf_wrapper(out, "[%p],%d", callfunc, code[i].b); @@ -524,7 +523,7 @@ void VMDisasm(FILE *out, const VMOP *code, int codesize, const VMScriptFunction { printf_wrapper(out, ",%d\n", code[++i].i24); } - else if (code[i].op == OP_CALL_K || code[i].op == OP_TAIL_K) + else if (code[i].op == OP_CALL_K) { printf_wrapper(out, " [%s]\n", callfunc->PrintableName.GetChars()); } diff --git a/src/scripting/vm/jit_call.cpp b/src/scripting/vm/jit_call.cpp index f8130437b..15dc8b56e 100644 --- a/src/scripting/vm/jit_call.cpp +++ b/src/scripting/vm/jit_call.cpp @@ -132,18 +132,6 @@ void JitCompiler::EmitCALL_K() EmitDoCall(ptr); } -void JitCompiler::EmitTAIL() -{ - EmitDoTail(regA[A]); -} - -void JitCompiler::EmitTAIL_K() -{ - auto ptr = newTempIntPtr(); - cc.mov(ptr, asmjit::imm_ptr(konsta[A].o)); - EmitDoTail(ptr); -} - void JitCompiler::EmitDoCall(asmjit::X86Gp vmfunc) { using namespace asmjit; @@ -185,50 +173,6 @@ void JitCompiler::EmitScriptCall(asmjit::X86Gp vmfunc, asmjit::X86Gp paramsptr) call->setArg(4, Imm(C)); } -void JitCompiler::EmitDoTail(asmjit::X86Gp vmfunc) -{ - // Whereas the CALL instruction uses its third operand to specify how many return values - // it expects, TAIL ignores its third operand and uses whatever was passed to this Exec call. - - // Note: this is not a true tail call, but then again, it isn't in the vmexec implementation either.. - - using namespace asmjit; - - if (NumParam < B) - I_FatalError("OP_TAIL parameter count does not match the number of preceding OP_PARAM instructions"); - - StoreInOuts(B); // Is REGT_ADDROF even allowed for (true) tail calls? - - X86Gp paramsptr = newTempIntPtr(); - cc.lea(paramsptr, x86::ptr(vmframe, offsetParams + (int)((NumParam - B) * sizeof(VMValue)))); - - auto result = newResultInt32(); - - EmitScriptTailCall(vmfunc, result, paramsptr); - - EmitPopFrame(); - cc.ret(result); - - NumParam -= B; - ParamOpcodes.Resize(ParamOpcodes.Size() - B); -} - -void JitCompiler::EmitScriptTailCall(asmjit::X86Gp vmfunc, asmjit::X86Gp result, asmjit::X86Gp paramsptr) -{ - using namespace asmjit; - - auto scriptcall = newTempIntPtr(); - cc.mov(scriptcall, x86::ptr(vmfunc, offsetof(VMScriptFunction, ScriptCall))); - - auto call = cc.call(scriptcall, FuncSignature5()); - call->setRet(0, result); - call->setArg(0, vmfunc); - call->setArg(1, paramsptr); - call->setArg(2, Imm(B)); - call->setArg(3, ret); - call->setArg(4, numret); -} - void JitCompiler::StoreInOuts(int b) { using namespace asmjit; diff --git a/src/scripting/vm/jitintern.h b/src/scripting/vm/jitintern.h index a34488054..34099158a 100644 --- a/src/scripting/vm/jitintern.h +++ b/src/scripting/vm/jitintern.h @@ -55,9 +55,6 @@ private: void EmitDoCall(asmjit::X86Gp ptr); void EmitScriptCall(asmjit::X86Gp vmfunc, asmjit::X86Gp paramsptr); - void EmitDoTail(asmjit::X86Gp ptr); - void EmitScriptTailCall(asmjit::X86Gp vmfunc, asmjit::X86Gp result, asmjit::X86Gp paramsptr); - void StoreInOuts(int b); void LoadInOuts(int b); void LoadReturns(const VMOP *retval, int numret); diff --git a/src/scripting/vm/vmexec.h b/src/scripting/vm/vmexec.h index e7735c5cb..c910d61b9 100644 --- a/src/scripting/vm/vmexec.h +++ b/src/scripting/vm/vmexec.h @@ -718,45 +718,6 @@ static int ExecScriptFunc(VMFrameStack *stack, VMReturn *ret, int numret) pc += C; // Skip RESULTs } NEXTOP; - OP(TAIL_K): - ASSERTKA(a); - ptr = konsta[a].o; - goto Do_TAILCALL; - OP(TAIL): - ASSERTA(a); - ptr = reg.a[a]; - Do_TAILCALL: - // Whereas the CALL instruction uses its third operand to specify how many return values - // it expects, TAIL ignores its third operand and uses whatever was passed to this Exec call. - assert(B <= f->NumParam); - assert(C <= MAX_RETURNS); - { - VMFunction *call = (VMFunction *)ptr; - - if (call->VarFlags & VARF_Native) - { - try - { - VMCycles[0].Unclock(); - auto r = static_cast(call)->NativeCall(reg.param + f->NumParam - B, B, ret, numret); - VMCycles[0].Clock(); - return r; - } - catch (CVMAbortException &err) - { - err.MaybePrintMessage(); - err.stacktrace.AppendFormat("Called from %s\n", call->PrintableName.GetChars()); - // PrintParameters(reg.param + f->NumParam - B, B); - throw; - } - } - else - { // FIXME: Not a true tail call - auto sfunc = static_cast(call); - return sfunc->ScriptCall(sfunc, reg.param + f->NumParam - B, B, ret, numret); - } - } - NEXTOP; OP(RET): if (B == REGT_NIL) { // No return values diff --git a/src/scripting/vm/vmops.h b/src/scripting/vm/vmops.h index afa0dbfa9..a4e623e9b 100644 --- a/src/scripting/vm/vmops.h +++ b/src/scripting/vm/vmops.h @@ -105,8 +105,6 @@ xx(CALL, call, RPI8I8, NOP, 0, 0) // Call function pkA with parameter count B a xx(CALL_K, call, KPI8I8, CALL, 1, REGT_POINTER) xx(VTBL, vtbl, RPRPI8, NOP, 0, 0) // dereferences a virtual method table. xx(SCOPE, scope, RPI8, NOP, 0, 0) // Scope check at runtime. -xx(TAIL, tail, RPI8, NOP, 0, 0) // Call+Ret in a single instruction -xx(TAIL_K, tail, KPI8, TAIL, 1, REGT_POINTER) xx(RESULT, result, __BCP, NOP, 0, 0) // Result should go in register encoded in BC (in caller, after CALL) xx(RET, ret, I8BCP, NOP, 0, 0) // Copy value from register encoded in BC to return value A, possibly returning xx(RETI, reti, I8I16, NOP, 0, 0) // Copy immediate from BC to return value A, possibly returning From cbedcff5597064ea0c2e2e9cdb83861768679dc5 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 18 Nov 2018 08:29:41 +0100 Subject: [PATCH 02/17] - moved all handling for the simple calls into EmitterArray to have it in one place only. The main case of FxVmFunctionCall is not done yet, though. --- src/d_dehacked.cpp | 4 +- src/scripting/backend/codegen.cpp | 86 +++++++++++------------------ src/scripting/backend/vmbuilder.cpp | 13 +++++ src/scripting/backend/vmbuilder.h | 13 ++++- 4 files changed, 58 insertions(+), 58 deletions(-) diff --git a/src/d_dehacked.cpp b/src/d_dehacked.cpp index 9004dc555..0946b6328 100644 --- a/src/d_dehacked.cpp +++ b/src/d_dehacked.cpp @@ -783,8 +783,8 @@ void SetDehParams(FState *state, int codepointer) } // Emit code for action parameters. MBFCodePointerFactories[codepointer](emitters, value1, value2); - int count = emitters.EmitParameters(&buildit); - buildit.Emit(OP_CALL_K, buildit.GetConstantAddress(sym->Variants[0].Implementation), count, 0); + emitters.AddTarget(sym->Variants[0].Implementation); + emitters.EmitCall(&buildit); buildit.Emit(OP_RET, RET_FINAL, REGT_NIL, 0); // Attach it to the state. VMScriptFunction *sfunc = new VMScriptFunction; diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index 74ab9a1c8..bccef9061 100644 --- a/src/scripting/backend/codegen.cpp +++ b/src/scripting/backend/codegen.cpp @@ -5499,12 +5499,9 @@ ExpEmit FxRandom::Emit(VMFunctionBuilder *build) emitters.AddParameterPointerConst(rng); emitters.AddParameter(build, min); emitters.AddParameter(build, max); - int count = emitters.EmitParameters(build); - build->Emit(OP_CALL_K, build->GetConstantAddress(callfunc), count, 1); - - ExpEmit out(build, REGT_INT); - build->Emit(OP_RESULT, 0, REGT_INT, out.RegNum); - return out; + emitters.AddTarget(callfunc); + emitters.AddReturn(REGT_INT); + return emitters.EmitCall(build); } //========================================================================== @@ -5608,10 +5605,10 @@ ExpEmit FxRandomPick::Emit(VMFunctionBuilder *build) emitters.AddParameterPointerConst(rng); emitters.AddParameterIntConst(0); emitters.AddParameterIntConst(choices.Size() - 1); - int count = emitters.EmitParameters(build); - build->Emit(OP_CALL_K, build->GetConstantAddress(callfunc), count, 1); + emitters.AddTarget(callfunc); + emitters.AddReturn(REGT_INT); + auto resultreg = emitters.EmitCall(build); - ExpEmit resultreg(build, REGT_INT); build->Emit(OP_RESULT, 0, REGT_INT, resultreg.RegNum); build->Emit(OP_IJMP, resultreg.RegNum, choices.Size()); @@ -5729,12 +5726,9 @@ ExpEmit FxFRandom::Emit(VMFunctionBuilder *build) emitters.AddParameterPointerConst(rng); emitters.AddParameter(build, min); emitters.AddParameter(build, max); - int count = emitters.EmitParameters(build); - build->Emit(OP_CALL_K, build->GetConstantAddress(callfunc), count, 1); - - ExpEmit out(build, REGT_FLOAT); - build->Emit(OP_RESULT, 0, REGT_FLOAT, out.RegNum); - return out; + emitters.AddTarget(callfunc); + emitters.AddReturn(REGT_FLOAT); + return emitters.EmitCall(build); } //========================================================================== @@ -5810,12 +5804,9 @@ ExpEmit FxRandom2::Emit(VMFunctionBuilder *build) emitters.AddParameterPointerConst(rng); emitters.AddParameter(build, mask); - int count = emitters.EmitParameters(build); - build->Emit(OP_CALL_K, build->GetConstantAddress(callfunc), count, 1); - - ExpEmit out(build, REGT_INT); - build->Emit(OP_RESULT, 0, REGT_INT, out.RegNum); - return out; + emitters.AddTarget(callfunc); + emitters.AddReturn(REGT_INT); + return emitters.EmitCall(build); } //========================================================================== @@ -5885,11 +5876,8 @@ ExpEmit FxRandomSeed::Emit(VMFunctionBuilder *build) emitters.AddParameterPointerConst(rng); emitters.AddParameter(build, seed); - int count = emitters.EmitParameters(build); - build->Emit(OP_CALL_K, build->GetConstantAddress(callfunc), count, 0); - - ExpEmit call; - return call; + emitters.AddTarget(callfunc); + return emitters.EmitCall(build); } //========================================================================== @@ -8554,9 +8542,7 @@ ExpEmit FxActionSpecialCall::Emit(VMFunctionBuilder *build) EmitterArray emitters; emitters.AddParameterIntConst(abs(Special)); // pass special number - - ExpEmit selfemit(Self->Emit(build)); - emitters.AddParameterPointer(selfemit.RegNum, selfemit.Konst); + emitters.AddParameter(build, Self); for (; i < ArgList.Size(); ++i) { @@ -8590,13 +8576,9 @@ ExpEmit FxActionSpecialCall::Emit(VMFunctionBuilder *build) ArgList.DeleteAndClear(); ArgList.ShrinkToFit(); - int count = emitters.EmitParameters(build); - selfemit.Free(build); - - ExpEmit dest(build, REGT_INT); - build->Emit(OP_CALL_K, build->GetConstantAddress(callfunc), count, 1); - build->Emit(OP_RESULT, 0, REGT_INT, dest.RegNum); - return dest; + emitters.AddTarget(callfunc); + emitters.AddReturn(REGT_INT); + return emitters.EmitCall(build); } //========================================================================== @@ -10641,8 +10623,8 @@ ExpEmit FxReturnStatement::Emit(VMFunctionBuilder *build) build->Emit(OP_ADDA_RK, reg.RegNum, build->FramePointer.RegNum, build->GetConstantInt(build->ConstructedStructs[i]->StackOffset)); EmitterArray emitters; emitters.AddParameter(reg, false); - int count = emitters.EmitParameters(build); - build->Emit(OP_CALL_K, build->GetConstantAddress(pstr->mDestructor), count, 0); + emitters.AddTarget(pstr->mDestructor); + emitters.EmitCall(build); } // If we return nothing, use a regular RET opcode. @@ -10834,11 +10816,8 @@ ExpEmit FxClassTypeCast::Emit(VMFunctionBuilder *build) { return ExpEmit(build->GetConstantAddress(nullptr), REGT_POINTER, true); } - ExpEmit clsname = basex->Emit(build); - assert(!clsname.Konst); - ExpEmit dest(build, REGT_POINTER); EmitterArray emitters; - emitters.AddParameter(clsname, false); + emitters.AddParameter(build, basex); emitters.AddParameterPointerConst(const_cast(desttype)); // Call the BuiltinNameToClass function to convert from 'name' to class. @@ -10849,10 +10828,9 @@ ExpEmit FxClassTypeCast::Emit(VMFunctionBuilder *build) assert(((PSymbolVMFunction *)sym)->Function != nullptr); callfunc = ((PSymbolVMFunction *)sym)->Function; - int count = emitters.EmitParameters(build); - build->Emit(OP_CALL_K, build->GetConstantAddress(callfunc), count, 1); - build->Emit(OP_RESULT, 0, REGT_POINTER, dest.RegNum); - return dest; + emitters.AddTarget(callfunc); + emitters.AddReturn(REGT_POINTER); + return emitters.EmitCall(build); } //========================================================================== @@ -10958,11 +10936,9 @@ ExpEmit FxClassPtrCast::Emit(VMFunctionBuilder *build) assert(sym->IsKindOf(RUNTIME_CLASS(PSymbolVMFunction))); assert(((PSymbolVMFunction *)sym)->Function != nullptr); callfunc = ((PSymbolVMFunction *)sym)->Function; - ExpEmit dest(build, REGT_POINTER); - int count = emitters.EmitParameters(build); - build->Emit(OP_CALL_K, build->GetConstantAddress(callfunc), count, 1); - build->Emit(OP_RESULT, 0, REGT_POINTER, dest.RegNum); - return dest; + emitters.AddTarget(callfunc); + emitters.AddReturn(REGT_POINTER); + return emitters.EmitCall(build); } //========================================================================== @@ -11337,8 +11313,8 @@ ExpEmit FxLocalVariableDeclaration::Emit(VMFunctionBuilder *build) build->Emit(OP_ADDA_RK, reg.RegNum, build->FramePointer.RegNum, build->GetConstantInt(StackOffset)); EmitterArray emitters; emitters.AddParameter(reg, false); - int count = emitters.EmitParameters(build); - build->Emit(OP_CALL_K, build->GetConstantAddress(pstr->mConstructor), count, 0); + emitters.AddTarget(pstr->mConstructor); + emitters.EmitCall(build); } if (pstr->mDestructor != nullptr) build->ConstructedStructs.Push(this); } @@ -11364,8 +11340,8 @@ void FxLocalVariableDeclaration::Release(VMFunctionBuilder *build) build->Emit(OP_ADDA_RK, reg.RegNum, build->FramePointer.RegNum, build->GetConstantInt(StackOffset)); EmitterArray emitters; emitters.AddParameter(reg, false); - int count = emitters.EmitParameters(build); - build->Emit(OP_CALL_K, build->GetConstantAddress(pstr->mDestructor), count, 0); + emitters.AddTarget(pstr->mDestructor); + emitters.EmitCall(build); } build->ConstructedStructs.Delete(build->ConstructedStructs.Find(this)); } diff --git a/src/scripting/backend/vmbuilder.cpp b/src/scripting/backend/vmbuilder.cpp index ecd8c2d29..40d187390 100644 --- a/src/scripting/backend/vmbuilder.cpp +++ b/src/scripting/backend/vmbuilder.cpp @@ -1038,3 +1038,16 @@ int EmitterArray::EmitParameters(VMFunctionBuilder *build) assert(paramcount == numparams); return paramcount; } + + +ExpEmit EmitterArray::EmitCall(VMFunctionBuilder *build) +{ + int count = EmitParameters(build); + build->Emit(OP_CALL_K, build->GetConstantAddress(target), count, returns.Size()); + if (returns.Size() == 0) return ExpEmit(); + + ExpEmit out(build, returns[0]); + build->Emit(OP_RESULT, 0, returns[0], out.RegNum); + return out; +} + diff --git a/src/scripting/backend/vmbuilder.h b/src/scripting/backend/vmbuilder.h index eb28f20d9..2b019eee4 100644 --- a/src/scripting/backend/vmbuilder.h +++ b/src/scripting/backend/vmbuilder.h @@ -167,7 +167,9 @@ class EmitterArray { // std::function and TArray are not compatible so this has to use std::vector instead. std::vector> emitters; - unsigned numparams = 0; + TArray returns; + unsigned numparams = 0; // This counts the number of pushed elements, which can differ from the number of emitters with vectors. + VMFunction *target = nullptr; public: void AddParameter(VMFunctionBuilder *build, FxExpression *operand); @@ -178,6 +180,15 @@ public: void AddParameterIntConst(int konst); void AddParameterStringConst(const FString &konst); int EmitParameters(VMFunctionBuilder *build); + ExpEmit EmitCall(VMFunctionBuilder *build); + void AddReturn(int regtype) + { + returns.Push(regtype); + } + void AddTarget(VMFunction *func) + { + target = func; + } unsigned Count() const { return numparams; From fe0a341e0c2e193afdf699c6a93c8c66f048af1e Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 18 Nov 2018 10:02:31 +0100 Subject: [PATCH 03/17] - moved all code related to function calls into the helper class so that all future work on the calling convention is in one place only. --- src/d_dehacked.cpp | 22 ++++---- src/scripting/backend/codegen.cpp | 81 +++++++---------------------- src/scripting/backend/vmbuilder.cpp | 51 ++++++++++-------- src/scripting/backend/vmbuilder.h | 15 +++--- 4 files changed, 67 insertions(+), 102 deletions(-) diff --git a/src/d_dehacked.cpp b/src/d_dehacked.cpp index 0946b6328..44e675ed2 100644 --- a/src/d_dehacked.cpp +++ b/src/d_dehacked.cpp @@ -634,7 +634,7 @@ static int GetLine (void) // misc1 = vrange (arg +3), misc2 = hrange (arg+4) -static void CreateMushroomFunc(EmitterArray &emitters, int value1, int value2) +static void CreateMushroomFunc(FunctionCallEmitter &emitters, int value1, int value2) { // A_Mushroom emitters.AddParameterPointerConst(PClass::FindClass("FatShot")); // itemtype emitters.AddParameterIntConst(0); // numspawns @@ -644,7 +644,7 @@ static void CreateMushroomFunc(EmitterArray &emitters, int value1, int value2) } // misc1 = type (arg +0), misc2 = Z-pos (arg +2) -static void CreateSpawnFunc(EmitterArray &emitters, int value1, int value2) +static void CreateSpawnFunc(FunctionCallEmitter &emitters, int value1, int value2) { // A_SpawnItem if (InfoNames[value1-1] == nullptr) { @@ -659,13 +659,13 @@ static void CreateSpawnFunc(EmitterArray &emitters, int value1, int value2) // misc1 = angle (in degrees) (arg +0 but factor in current actor angle too) -static void CreateTurnFunc(EmitterArray &emitters, int value1, int value2) +static void CreateTurnFunc(FunctionCallEmitter &emitters, int value1, int value2) { // A_Turn emitters.AddParameterFloatConst(value1); // angle } // misc1 = angle (in degrees) (arg +0) -static void CreateFaceFunc(EmitterArray &emitters, int value1, int value2) +static void CreateFaceFunc(FunctionCallEmitter &emitters, int value1, int value2) { // A_FaceTarget emitters.AddParameterFloatConst(value1); // angle emitters.AddParameterIntConst(0); // flags @@ -673,7 +673,7 @@ static void CreateFaceFunc(EmitterArray &emitters, int value1, int value2) } // misc1 = damage, misc 2 = sound -static void CreateScratchFunc(EmitterArray &emitters, int value1, int value2) +static void CreateScratchFunc(FunctionCallEmitter &emitters, int value1, int value2) { // A_CustomMeleeAttack emitters.AddParameterIntConst(value1); // damage emitters.AddParameterIntConst(value2 ? (int)SoundMap[value2 - 1] : 0); // hit sound @@ -683,7 +683,7 @@ static void CreateScratchFunc(EmitterArray &emitters, int value1, int value2) } // misc1 = sound, misc2 = attenuation none (true) or normal (false) -static void CreatePlaySoundFunc(EmitterArray &emitters, int value1, int value2) +static void CreatePlaySoundFunc(FunctionCallEmitter &emitters, int value1, int value2) { // A_PlaySound emitters.AddParameterIntConst(value1 ? (int)SoundMap[value1 - 1] : 0); // soundid emitters.AddParameterIntConst(CHAN_BODY); // channel @@ -694,14 +694,14 @@ static void CreatePlaySoundFunc(EmitterArray &emitters, int value1, int value2) } // misc1 = state, misc2 = probability -static void CreateRandomJumpFunc(EmitterArray &emitters, int value1, int value2) +static void CreateRandomJumpFunc(FunctionCallEmitter &emitters, int value1, int value2) { // A_Jump emitters.AddParameterIntConst(value2); // maxchance emitters.AddParameterPointerConst(FindState(value1)); // jumpto } // misc1 = Boom linedef type, misc2 = sector tag -static void CreateLineEffectFunc(EmitterArray &emitters, int value1, int value2) +static void CreateLineEffectFunc(FunctionCallEmitter &emitters, int value1, int value2) { // A_LineEffect // This is the second MBF codepointer that couldn't be translated easily. // Calling P_TranslateLineDef() here was a simple matter, as was adding an @@ -713,7 +713,7 @@ static void CreateLineEffectFunc(EmitterArray &emitters, int value1, int value2) } // No misc, but it's basically A_Explode with an added effect -static void CreateNailBombFunc(EmitterArray &emitters, int value1, int value2) +static void CreateNailBombFunc(FunctionCallEmitter &emitters, int value1, int value2) { // A_Explode // This one does not actually have MBF-style parameters. But since // we're aliasing it to an extension of A_Explode... @@ -729,7 +729,7 @@ static void CreateNailBombFunc(EmitterArray &emitters, int value1, int value2) } // This array must be in sync with the Aliases array in DEHSUPP. -static void (*MBFCodePointerFactories[])(EmitterArray&, int, int) = +static void (*MBFCodePointerFactories[])(FunctionCallEmitter&, int, int) = { // Die and Detonate are not in this list because these codepointers have // no dehacked arguments and therefore do not need special handling. @@ -776,7 +776,7 @@ void SetDehParams(FState *state, int codepointer) // self, stateowner, state (all are pointers) buildit.Registers[REGT_POINTER].Get(numargs); // Emit code to pass the standard action function parameters. - EmitterArray emitters; + FunctionCallEmitter emitters; for (int i = 0; i < numargs; i++) { emitters.AddParameterPointer(i, false); diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index bccef9061..b746e322a 100644 --- a/src/scripting/backend/codegen.cpp +++ b/src/scripting/backend/codegen.cpp @@ -5494,7 +5494,7 @@ ExpEmit FxRandom::Emit(VMFunctionBuilder *build) assert(min && max); callfunc = ((PSymbolVMFunction *)sym)->Function; - EmitterArray emitters; + FunctionCallEmitter emitters; emitters.AddParameterPointerConst(rng); emitters.AddParameter(build, min); @@ -5601,7 +5601,7 @@ ExpEmit FxRandomPick::Emit(VMFunctionBuilder *build) assert(((PSymbolVMFunction *)sym)->Function != nullptr); callfunc = ((PSymbolVMFunction *)sym)->Function; - EmitterArray emitters; + FunctionCallEmitter emitters; emitters.AddParameterPointerConst(rng); emitters.AddParameterIntConst(0); emitters.AddParameterIntConst(choices.Size() - 1); @@ -5722,7 +5722,7 @@ ExpEmit FxFRandom::Emit(VMFunctionBuilder *build) assert(min && max); callfunc = ((PSymbolVMFunction *)sym)->Function; - EmitterArray emitters; + FunctionCallEmitter emitters; emitters.AddParameterPointerConst(rng); emitters.AddParameter(build, min); emitters.AddParameter(build, max); @@ -5800,7 +5800,7 @@ ExpEmit FxRandom2::Emit(VMFunctionBuilder *build) assert(((PSymbolVMFunction *)sym)->Function != nullptr); callfunc = ((PSymbolVMFunction *)sym)->Function; - EmitterArray emitters; + FunctionCallEmitter emitters; emitters.AddParameterPointerConst(rng); emitters.AddParameter(build, mask); @@ -5872,7 +5872,7 @@ ExpEmit FxRandomSeed::Emit(VMFunctionBuilder *build) assert(((PSymbolVMFunction *)sym)->Function != nullptr); callfunc = ((PSymbolVMFunction *)sym)->Function; - EmitterArray emitters; + FunctionCallEmitter emitters; emitters.AddParameterPointerConst(rng); emitters.AddParameter(build, seed); @@ -8539,7 +8539,7 @@ ExpEmit FxActionSpecialCall::Emit(VMFunctionBuilder *build) { unsigned i = 0; - EmitterArray emitters; + FunctionCallEmitter emitters; emitters.AddParameterIntConst(abs(Special)); // pass special number emitters.AddParameter(build, Self); @@ -8983,7 +8983,7 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build) bool staticcall = ((vmfunc->VarFlags & VARF_Final) || vmfunc->VirtualIndex == ~0u || NoVirtual); count = 0; - EmitterArray emitters; + FunctionCallEmitter emitters; // Emit code to pass implied parameters ExpEmit selfemit; if (Function->Variants[0].Flags & VARF_Method) @@ -9054,63 +9054,18 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build) break; } } - count = emitters.EmitParameters(build); - ArgList.DeleteAndClear(); ArgList.ShrinkToFit(); - // Get a constant register for this function - if (staticcall) - { - int funcaddr = build->GetConstantAddress(vmfunc); - // Emit the call - if (vmfunc->Proto->ReturnTypes.Size() > 0) - { // Call, expecting one result - build->Emit(OP_CALL_K, funcaddr, count, MAX(1, AssignCount)); - goto handlereturns; - } - else - { // Call, expecting no results - build->Emit(OP_CALL_K, funcaddr, count, 0); - return ExpEmit(); - } - } - else - { - ExpEmit funcreg(build, REGT_POINTER); + emitters.AddTarget(vmfunc, staticcall? -1 : selfemit.RegNum); + int resultcount = vmfunc->Proto->ReturnTypes.Size() == 0 ? 0 : MAX(AssignCount, 1); - build->Emit(OP_VTBL, funcreg.RegNum, selfemit.RegNum, vmfunc->VirtualIndex); - if (vmfunc->Proto->ReturnTypes.Size() > 0) - { // Call, expecting one result - build->Emit(OP_CALL, funcreg.RegNum, count, MAX(1, AssignCount)); - goto handlereturns; - } - else - { // Call, expecting no results - build->Emit(OP_CALL, funcreg.RegNum, count, 0); - return ExpEmit(); - } - } -handlereturns: - if (AssignCount == 0) + assert((unsigned)resultcount <= vmfunc->Proto->ReturnTypes.Size()); + for (int i = 0; i < resultcount; i++) { - // Regular call, will not write to ReturnRegs - ExpEmit reg(build, vmfunc->Proto->ReturnTypes[0]->GetRegType(), vmfunc->Proto->ReturnTypes[0]->GetRegCount()); - build->Emit(OP_RESULT, 0, EncodeRegType(reg), reg.RegNum); - return reg; + emitters.AddReturn(vmfunc->Proto->ReturnTypes[i]->GetRegType(), vmfunc->Proto->ReturnTypes[i]->GetRegCount()); } - else - { - // Multi-Assignment call, this must fill in the ReturnRegs array so that the multi-assignment operator can dispatch the return values. - assert((unsigned)AssignCount <= vmfunc->Proto->ReturnTypes.Size()); - for (int i = 0; i < AssignCount; i++) - { - ExpEmit reg(build, vmfunc->Proto->ReturnTypes[i]->GetRegType(), vmfunc->Proto->ReturnTypes[i]->GetRegCount()); - build->Emit(OP_RESULT, 0, EncodeRegType(reg), reg.RegNum); - ReturnRegs.Push(reg); - } - } - return ExpEmit(); + return emitters.EmitCall(build, resultcount > 1? &ReturnRegs : nullptr); } //========================================================================== @@ -10621,7 +10576,7 @@ ExpEmit FxReturnStatement::Emit(VMFunctionBuilder *build) assert(pstr->mDestructor != nullptr); ExpEmit reg(build, REGT_POINTER); build->Emit(OP_ADDA_RK, reg.RegNum, build->FramePointer.RegNum, build->GetConstantInt(build->ConstructedStructs[i]->StackOffset)); - EmitterArray emitters; + FunctionCallEmitter emitters; emitters.AddParameter(reg, false); emitters.AddTarget(pstr->mDestructor); emitters.EmitCall(build); @@ -10816,7 +10771,7 @@ ExpEmit FxClassTypeCast::Emit(VMFunctionBuilder *build) { return ExpEmit(build->GetConstantAddress(nullptr), REGT_POINTER, true); } - EmitterArray emitters; + FunctionCallEmitter emitters; emitters.AddParameter(build, basex); emitters.AddParameterPointerConst(const_cast(desttype)); @@ -10925,7 +10880,7 @@ ExpEmit FxClassPtrCast::Emit(VMFunctionBuilder *build) { ExpEmit clsname = basex->Emit(build); - EmitterArray emitters; + FunctionCallEmitter emitters; emitters.AddParameter(clsname, false); emitters.AddParameterPointerConst(desttype); @@ -11311,7 +11266,7 @@ ExpEmit FxLocalVariableDeclaration::Emit(VMFunctionBuilder *build) { ExpEmit reg(build, REGT_POINTER); build->Emit(OP_ADDA_RK, reg.RegNum, build->FramePointer.RegNum, build->GetConstantInt(StackOffset)); - EmitterArray emitters; + FunctionCallEmitter emitters; emitters.AddParameter(reg, false); emitters.AddTarget(pstr->mConstructor); emitters.EmitCall(build); @@ -11338,7 +11293,7 @@ void FxLocalVariableDeclaration::Release(VMFunctionBuilder *build) { ExpEmit reg(build, REGT_POINTER); build->Emit(OP_ADDA_RK, reg.RegNum, build->FramePointer.RegNum, build->GetConstantInt(StackOffset)); - EmitterArray emitters; + FunctionCallEmitter emitters; emitters.AddParameter(reg, false); emitters.AddTarget(pstr->mDestructor); emitters.EmitCall(build); diff --git a/src/scripting/backend/vmbuilder.cpp b/src/scripting/backend/vmbuilder.cpp index 40d187390..1aa5c09f7 100644 --- a/src/scripting/backend/vmbuilder.cpp +++ b/src/scripting/backend/vmbuilder.cpp @@ -931,7 +931,7 @@ void FFunctionBuildList::DumpJit() } -void EmitterArray::AddParameter(VMFunctionBuilder *build, FxExpression *operand) +void FunctionCallEmitter::AddParameter(VMFunctionBuilder *build, FxExpression *operand) { ExpEmit where = operand->Emit(build); @@ -958,7 +958,7 @@ void EmitterArray::AddParameter(VMFunctionBuilder *build, FxExpression *operand) }); } -void EmitterArray::AddParameter(ExpEmit &emit, bool reference) +void FunctionCallEmitter::AddParameter(ExpEmit &emit, bool reference) { numparams += emit.RegCount; emitters.push_back([=](VMFunctionBuilder *build) ->int @@ -970,7 +970,7 @@ void EmitterArray::AddParameter(ExpEmit &emit, bool reference) }); } -void EmitterArray::AddParameterPointerConst(void *konst) +void FunctionCallEmitter::AddParameterPointerConst(void *konst) { numparams++; emitters.push_back([=](VMFunctionBuilder *build) ->int @@ -980,7 +980,7 @@ void EmitterArray::AddParameterPointerConst(void *konst) }); } -void EmitterArray::AddParameterPointer(int index, bool konst) +void FunctionCallEmitter::AddParameterPointer(int index, bool konst) { numparams++; emitters.push_back([=](VMFunctionBuilder *build) ->int @@ -990,7 +990,7 @@ void EmitterArray::AddParameterPointer(int index, bool konst) }); } -void EmitterArray::AddParameterFloatConst(double konst) +void FunctionCallEmitter::AddParameterFloatConst(double konst) { numparams++; emitters.push_back([=](VMFunctionBuilder *build) ->int @@ -1000,7 +1000,7 @@ void EmitterArray::AddParameterFloatConst(double konst) }); } -void EmitterArray::AddParameterIntConst(int konst) +void FunctionCallEmitter::AddParameterIntConst(int konst) { numparams++; emitters.push_back([=](VMFunctionBuilder *build) ->int @@ -1018,7 +1018,7 @@ void EmitterArray::AddParameterIntConst(int konst) }); } -void EmitterArray::AddParameterStringConst(const FString &konst) +void FunctionCallEmitter::AddParameterStringConst(const FString &konst) { numparams++; emitters.push_back([=](VMFunctionBuilder *build) ->int @@ -1028,7 +1028,7 @@ void EmitterArray::AddParameterStringConst(const FString &konst) }); } -int EmitterArray::EmitParameters(VMFunctionBuilder *build) +ExpEmit FunctionCallEmitter::EmitCall(VMFunctionBuilder *build, TArray *ReturnRegs) { int paramcount = 0; for (auto &func : emitters) @@ -1036,18 +1036,27 @@ int EmitterArray::EmitParameters(VMFunctionBuilder *build) paramcount += func(build); } assert(paramcount == numparams); - return paramcount; -} - - -ExpEmit EmitterArray::EmitCall(VMFunctionBuilder *build) -{ - int count = EmitParameters(build); - build->Emit(OP_CALL_K, build->GetConstantAddress(target), count, returns.Size()); - if (returns.Size() == 0) return ExpEmit(); - - ExpEmit out(build, returns[0]); - build->Emit(OP_RESULT, 0, returns[0], out.RegNum); - return out; + + if (virtualselfreg == -1) + { + build->Emit(OP_CALL_K, build->GetConstantAddress(target), paramcount, returns.Size()); + } + else + { + ExpEmit funcreg(build, REGT_POINTER); + + build->Emit(OP_VTBL, funcreg.RegNum, virtualselfreg, target->VirtualIndex); + build->Emit(OP_CALL, funcreg.RegNum, paramcount, returns.Size()); + } + + assert(returns.Size() < 2 || ReturnRegs != nullptr); + for (unsigned i = 0; i < returns.Size(); i++) + { + ExpEmit reg(build, returns[i].first, returns[i].second); + build->Emit(OP_RESULT, 0, EncodeRegType(reg), reg.RegNum); + if (ReturnRegs) ReturnRegs->Push(reg); + else return reg; + } + return ExpEmit(); } diff --git a/src/scripting/backend/vmbuilder.h b/src/scripting/backend/vmbuilder.h index 2b019eee4..07ba89ad4 100644 --- a/src/scripting/backend/vmbuilder.h +++ b/src/scripting/backend/vmbuilder.h @@ -163,13 +163,14 @@ extern FFunctionBuildList FunctionBuildList; //========================================================================== extern int EncodeRegType(ExpEmit reg); -class EmitterArray +class FunctionCallEmitter { // std::function and TArray are not compatible so this has to use std::vector instead. std::vector> emitters; - TArray returns; + TArray> returns; unsigned numparams = 0; // This counts the number of pushed elements, which can differ from the number of emitters with vectors. VMFunction *target = nullptr; + int virtualselfreg = -1; public: void AddParameter(VMFunctionBuilder *build, FxExpression *operand); @@ -179,15 +180,15 @@ public: void AddParameterFloatConst(double konst); void AddParameterIntConst(int konst); void AddParameterStringConst(const FString &konst); - int EmitParameters(VMFunctionBuilder *build); - ExpEmit EmitCall(VMFunctionBuilder *build); - void AddReturn(int regtype) + ExpEmit EmitCall(VMFunctionBuilder *build, TArray *ReturnRegs = nullptr); + void AddReturn(int regtype, int regcount = 1) { - returns.Push(regtype); + returns.Push({ regtype, regcount }); } - void AddTarget(VMFunction *func) + void AddTarget(VMFunction *func, int virtreg = -1) { target = func; + virtualselfreg = virtreg; } unsigned Count() const { From 28db04b5014181ad34463f2d411993db25a83067 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 18 Nov 2018 10:09:29 +0100 Subject: [PATCH 04/17] - missed one OP_RESULT. --- src/scripting/backend/codegen.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index b746e322a..f58fd7719 100644 --- a/src/scripting/backend/codegen.cpp +++ b/src/scripting/backend/codegen.cpp @@ -5609,7 +5609,6 @@ ExpEmit FxRandomPick::Emit(VMFunctionBuilder *build) emitters.AddReturn(REGT_INT); auto resultreg = emitters.EmitCall(build); - build->Emit(OP_RESULT, 0, REGT_INT, resultreg.RegNum); build->Emit(OP_IJMP, resultreg.RegNum, choices.Size()); // Free the result register now. The simple code generation algorithm should From e4c238b6c7767f0ac60564c6e285784e421d9d52 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 18 Nov 2018 12:37:07 +0200 Subject: [PATCH 05/17] - fixed compilation of POSIX targets src/scripting/backend/vmbuilder.h:169:19: error: no member named 'function' in namespace 'std' --- src/scripting/backend/vmbuilder.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/scripting/backend/vmbuilder.h b/src/scripting/backend/vmbuilder.h index 07ba89ad4..7531e4eff 100644 --- a/src/scripting/backend/vmbuilder.h +++ b/src/scripting/backend/vmbuilder.h @@ -4,6 +4,7 @@ #include "dobject.h" #include "vmintern.h" #include +#include class VMFunctionBuilder; class FxExpression; From 4624c6e6a38ff209f09b1f1231cd0d8dd8e948a3 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 18 Nov 2018 12:48:50 +0200 Subject: [PATCH 06/17] - use custom offsetof() macro src/scripting/vm/jit_call.cpp:164:38: warning: offset of on non-standard-layout type 'VMScriptFunction' [-Winvalid-offsetof] src/scripting/vm/jit_load.cpp:87:50: warning: offset of on non-standard-layout type 'DObject' [-Winvalid-offsetof] src/scripting/vm/jit_load.cpp:96:50: warning: offset of on non-standard-layout type 'DObject' [-Winvalid-offsetof] src/scripting/vm/jit_load.cpp:257:53: warning: offset of on non-standard-layout type 'DObject' [-Winvalid-offsetof] --- src/scripting/vm/jit_call.cpp | 2 +- src/scripting/vm/jit_load.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/scripting/vm/jit_call.cpp b/src/scripting/vm/jit_call.cpp index 15dc8b56e..0c2d8115b 100644 --- a/src/scripting/vm/jit_call.cpp +++ b/src/scripting/vm/jit_call.cpp @@ -161,7 +161,7 @@ void JitCompiler::EmitScriptCall(asmjit::X86Gp vmfunc, asmjit::X86Gp paramsptr) using namespace asmjit; auto scriptcall = newTempIntPtr(); - cc.mov(scriptcall, x86::ptr(vmfunc, offsetof(VMScriptFunction, ScriptCall))); + cc.mov(scriptcall, x86::ptr(vmfunc, myoffsetof(VMScriptFunction, ScriptCall))); auto result = newResultInt32(); auto call = cc.call(scriptcall, FuncSignature5()); diff --git a/src/scripting/vm/jit_load.cpp b/src/scripting/vm/jit_load.cpp index 908ff144e..ab6dd24c9 100644 --- a/src/scripting/vm/jit_load.cpp +++ b/src/scripting/vm/jit_load.cpp @@ -84,8 +84,8 @@ void JitCompiler::EmitMETA() cc.test(regA[B], regA[B]); cc.je(label); - cc.mov(regA[A], asmjit::x86::qword_ptr(regA[B], offsetof(DObject, Class))); - cc.mov(regA[A], asmjit::x86::qword_ptr(regA[A], offsetof(PClass, Meta))); + cc.mov(regA[A], asmjit::x86::qword_ptr(regA[B], myoffsetof(DObject, Class))); + cc.mov(regA[A], asmjit::x86::qword_ptr(regA[A], myoffsetof(PClass, Meta))); } void JitCompiler::EmitCLSS() @@ -93,7 +93,7 @@ void JitCompiler::EmitCLSS() auto label = EmitThrowExceptionLabel(X_READ_NIL); cc.test(regA[B], regA[B]); cc.je(label); - cc.mov(regA[A], asmjit::x86::qword_ptr(regA[B], offsetof(DObject, Class))); + cc.mov(regA[A], asmjit::x86::qword_ptr(regA[B], myoffsetof(DObject, Class))); } #else @@ -254,7 +254,7 @@ void JitCompiler::EmitReadBarrier() cc.je(isnull); auto mask = newTempIntPtr(); - cc.mov(mask.r32(), asmjit::x86::dword_ptr(regA[A], offsetof(DObject, ObjectFlags))); + cc.mov(mask.r32(), asmjit::x86::dword_ptr(regA[A], myoffsetof(DObject, ObjectFlags))); cc.shl(mask, 63 - 5); // put OF_EuthanizeMe (1 << 5) in the highest bit cc.sar(mask, 63); // sign extend so all bits are set if OF_EuthanizeMe was set cc.not_(mask); From 486b7e0f3c869b58b61628dbf6d657150002a818 Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Sun, 18 Nov 2018 12:38:55 +0100 Subject: [PATCH 07/17] - delay emitting PARAM and VTBL instructions until CALL/CALL_K --- src/scripting/vm/jit_call.cpp | 262 +++++++++++++++++----------------- src/scripting/vm/jit_flow.cpp | 21 --- src/scripting/vm/jitintern.h | 5 +- 3 files changed, 135 insertions(+), 153 deletions(-) diff --git a/src/scripting/vm/jit_call.cpp b/src/scripting/vm/jit_call.cpp index 0c2d8115b..fbacb1f91 100644 --- a/src/scripting/vm/jit_call.cpp +++ b/src/scripting/vm/jit_call.cpp @@ -3,114 +3,12 @@ void JitCompiler::EmitPARAM() { - using namespace asmjit; - - int index = NumParam++; ParamOpcodes.Push(pc); - - X86Gp stackPtr, tmp; - X86Xmm tmp2; - - switch (A) - { - case REGT_NIL: - cc.mov(x86::ptr(vmframe, offsetParams + index * sizeof(VMValue) + offsetof(VMValue, a)), (int64_t)0); - cc.mov(x86::byte_ptr(vmframe, offsetParams + index * sizeof(VMValue) + offsetof(VMValue, Type)), (int)REGT_NIL); - break; - case REGT_INT: - cc.mov(x86::dword_ptr(vmframe, offsetParams + index * sizeof(VMValue) + offsetof(VMValue, i)), regD[BC]); - cc.mov(x86::byte_ptr(vmframe, offsetParams + index * sizeof(VMValue) + offsetof(VMValue, Type)), (int)REGT_INT); - break; - case REGT_INT | REGT_ADDROF: - stackPtr = newTempIntPtr(); - cc.lea(stackPtr, x86::ptr(vmframe, offsetD + (int)(BC * sizeof(int32_t)))); - cc.mov(x86::ptr(vmframe, offsetParams + index * sizeof(VMValue) + offsetof(VMValue, a)), stackPtr); - cc.mov(x86::byte_ptr(vmframe, offsetParams + index * sizeof(VMValue) + offsetof(VMValue, Type)), (int)REGT_POINTER); - break; - case REGT_INT | REGT_KONST: - cc.mov(x86::dword_ptr(vmframe, offsetParams + index * sizeof(VMValue) + offsetof(VMValue, i)), konstd[BC]); - cc.mov(x86::byte_ptr(vmframe, offsetParams + index * sizeof(VMValue) + offsetof(VMValue, Type)), (int)REGT_INT); - break; - case REGT_STRING: - cc.mov(x86::ptr(vmframe, offsetParams + index * sizeof(VMValue) + offsetof(VMValue, sp)), regS[BC]); - cc.mov(x86::byte_ptr(vmframe, offsetParams + index * sizeof(VMValue) + offsetof(VMValue, Type)), (int)REGT_STRING); - break; - case REGT_STRING | REGT_ADDROF: - cc.mov(x86::ptr(vmframe, offsetParams + index * sizeof(VMValue) + offsetof(VMValue, a)), regS[BC]); - cc.mov(x86::byte_ptr(vmframe, offsetParams + index * sizeof(VMValue) + offsetof(VMValue, Type)), (int)REGT_POINTER); - break; - case REGT_STRING | REGT_KONST: - tmp = newTempIntPtr(); - cc.mov(tmp, asmjit::imm_ptr(&konsts[BC])); - cc.mov(x86::ptr(vmframe, offsetParams + index * sizeof(VMValue) + offsetof(VMValue, sp)), tmp); - cc.mov(x86::byte_ptr(vmframe, offsetParams + index * sizeof(VMValue) + offsetof(VMValue, Type)), (int)REGT_STRING); - break; - case REGT_POINTER: - cc.mov(x86::ptr(vmframe, offsetParams + index * sizeof(VMValue) + offsetof(VMValue, a)), regA[BC]); - cc.mov(x86::byte_ptr(vmframe, offsetParams + index * sizeof(VMValue) + offsetof(VMValue, Type)), (int)REGT_POINTER); - break; - case REGT_POINTER | REGT_ADDROF: - stackPtr = newTempIntPtr(); - cc.lea(stackPtr, x86::ptr(vmframe, offsetA + (int)(BC * sizeof(void*)))); - cc.mov(x86::ptr(vmframe, offsetParams + index * sizeof(VMValue) + offsetof(VMValue, a)), stackPtr); - cc.mov(x86::byte_ptr(vmframe, offsetParams + index * sizeof(VMValue) + offsetof(VMValue, Type)), (int)REGT_POINTER); - break; - case REGT_POINTER | REGT_KONST: - tmp = newTempIntPtr(); - cc.mov(tmp, asmjit::imm_ptr(konsta[BC].v)); - cc.mov(x86::ptr(vmframe, offsetParams + index * sizeof(VMValue) + offsetof(VMValue, a)), tmp); - cc.mov(x86::byte_ptr(vmframe, offsetParams + index * sizeof(VMValue) + offsetof(VMValue, Type)), (int)REGT_POINTER); - break; - case REGT_FLOAT: - cc.movsd(x86::qword_ptr(vmframe, offsetParams + index * sizeof(VMValue) + offsetof(VMValue, f)), regF[BC]); - cc.mov(x86::byte_ptr(vmframe, offsetParams + index * sizeof(VMValue) + offsetof(VMValue, Type)), (int)REGT_FLOAT); - break; - case REGT_FLOAT | REGT_MULTIREG2: - cc.movsd(x86::qword_ptr(vmframe, offsetParams + index * sizeof(VMValue) + offsetof(VMValue, f)), regF[BC]); - cc.mov(x86::byte_ptr(vmframe, offsetParams + index * sizeof(VMValue) + offsetof(VMValue, Type)), (int)REGT_FLOAT); - index = NumParam++; - ParamOpcodes.Push(pc); - cc.movsd(x86::qword_ptr(vmframe, offsetParams + index * sizeof(VMValue) + offsetof(VMValue, f)), regF[BC + 1]); - cc.mov(x86::byte_ptr(vmframe, offsetParams + index * sizeof(VMValue) + offsetof(VMValue, Type)), (int)REGT_FLOAT); - break; - case REGT_FLOAT | REGT_MULTIREG3: - cc.movsd(x86::qword_ptr(vmframe, offsetParams + index * sizeof(VMValue) + offsetof(VMValue, f)), regF[BC]); - cc.mov(x86::byte_ptr(vmframe, offsetParams + index * sizeof(VMValue) + offsetof(VMValue, Type)), (int)REGT_FLOAT); - index = NumParam++; - ParamOpcodes.Push(pc); - cc.movsd(x86::qword_ptr(vmframe, offsetParams + index * sizeof(VMValue) + offsetof(VMValue, f)), regF[BC + 1]); - cc.mov(x86::byte_ptr(vmframe, offsetParams + index * sizeof(VMValue) + offsetof(VMValue, Type)), (int)REGT_FLOAT); - index = NumParam++; - ParamOpcodes.Push(pc); - cc.movsd(x86::qword_ptr(vmframe, offsetParams + index * sizeof(VMValue) + offsetof(VMValue, f)), regF[BC + 2]); - cc.mov(x86::byte_ptr(vmframe, offsetParams + index * sizeof(VMValue) + offsetof(VMValue, Type)), (int)REGT_FLOAT); - break; - case REGT_FLOAT | REGT_ADDROF: - stackPtr = newTempIntPtr(); - cc.lea(stackPtr, x86::ptr(vmframe, offsetF + (int)(BC * sizeof(double)))); - cc.mov(x86::ptr(vmframe, offsetParams + index * sizeof(VMValue) + offsetof(VMValue, a)), stackPtr); - cc.mov(x86::byte_ptr(vmframe, offsetParams + index * sizeof(VMValue) + offsetof(VMValue, Type)), (int)REGT_POINTER); - break; - case REGT_FLOAT | REGT_KONST: - tmp = newTempIntPtr(); - tmp2 = newTempXmmSd(); - cc.mov(tmp, asmjit::imm_ptr(konstf + BC)); - cc.movsd(tmp2, asmjit::x86::qword_ptr(tmp)); - cc.movsd(x86::qword_ptr(vmframe, offsetParams + index * sizeof(VMValue) + offsetof(VMValue, f)), tmp2); - cc.mov(x86::byte_ptr(vmframe, offsetParams + index * sizeof(VMValue) + offsetof(VMValue, Type)), (int)REGT_FLOAT); - break; - default: - I_FatalError("Unknown REGT value passed to EmitPARAM\n"); - break; - } } void JitCompiler::EmitPARAMI() { - int index = NumParam++; ParamOpcodes.Push(pc); - cc.mov(asmjit::x86::dword_ptr(vmframe, offsetParams + index * sizeof(VMValue) + offsetof(VMValue, i)), (int)ABCs); - cc.mov(asmjit::x86::byte_ptr(vmframe, offsetParams + index * sizeof(VMValue) + offsetof(VMValue, Type)), (int)REGT_INT); } void JitCompiler::EmitRESULT() @@ -120,6 +18,36 @@ void JitCompiler::EmitRESULT() // be executed. } +void JitCompiler::EmitVTBL() +{ + // This instruction is handled in the CALL/CALL_K instruction following it +} + +static VMFunction *GetVirtual(DObject *o, int c) +{ + auto p = o->GetClass(); + assert(c < (int)p->Virtuals.Size()); + return p->Virtuals[c]; +} + +void JitCompiler::EmitVtbl(const VMOP *op) +{ + int a = op->a; + int b = op->b; + int c = op->c; + + auto label = EmitThrowExceptionLabel(X_READ_NIL); + cc.test(regA[b], regA[b]); + cc.jz(label); + + auto result = newResultIntPtr(); + auto call = CreateCall(GetVirtual); + call->setRet(0, result); + call->setArg(0, regA[b]); + call->setArg(1, asmjit::Imm(c)); + cc.mov(regA[a], result); +} + void JitCompiler::EmitCALL() { EmitDoCall(regA[A]); @@ -136,22 +64,24 @@ void JitCompiler::EmitDoCall(asmjit::X86Gp vmfunc) { using namespace asmjit; - if (NumParam < B) + int numparams = StoreCallParams(); + if (numparams != B) I_FatalError("OP_CALL parameter count does not match the number of preceding OP_PARAM instructions"); - StoreInOuts(B); + if ((pc - 1)->op == OP_VTBL) + EmitVtbl(pc - 1); + FillReturns(pc + 1, C); X86Gp paramsptr = newTempIntPtr(); - cc.lea(paramsptr, x86::ptr(vmframe, offsetParams + (int)((NumParam - B) * sizeof(VMValue)))); + cc.lea(paramsptr, x86::ptr(vmframe, offsetParams)); EmitScriptCall(vmfunc, paramsptr); - LoadInOuts(B); + LoadInOuts(); LoadReturns(pc + 1, C); - NumParam -= B; - ParamOpcodes.Resize(ParamOpcodes.Size() - B); + ParamOpcodes.Clear(); pc += C; // Skip RESULTs } @@ -173,55 +103,127 @@ void JitCompiler::EmitScriptCall(asmjit::X86Gp vmfunc, asmjit::X86Gp paramsptr) call->setArg(4, Imm(C)); } -void JitCompiler::StoreInOuts(int b) +int JitCompiler::StoreCallParams() { using namespace asmjit; - for (unsigned int i = ParamOpcodes.Size() - b; i < ParamOpcodes.Size(); i++) + X86Gp stackPtr = newTempIntPtr(); + X86Gp tmp = newTempIntPtr(); + X86Xmm tmp2 = newTempXmmSd(); + + int numparams = 0; + for (unsigned int i = 0; i < ParamOpcodes.Size(); i++) { - asmjit::X86Gp stackPtr; - auto bc = ParamOpcodes[i]->i16u; + int slot = numparams++; + + if (ParamOpcodes[i]->op == OP_PARAMI) + { + int abcs = ParamOpcodes[i]->i24; + cc.mov(asmjit::x86::dword_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, i)), abcs); + cc.mov(asmjit::x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_INT); + continue; + } + + int bc = ParamOpcodes[i]->i16u; + switch (ParamOpcodes[i]->a) { + case REGT_NIL: + cc.mov(x86::ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, a)), (int64_t)0); + cc.mov(x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_NIL); + break; + case REGT_INT: + cc.mov(x86::dword_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, i)), regD[bc]); + cc.mov(x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_INT); + break; case REGT_INT | REGT_ADDROF: - stackPtr = newTempIntPtr(); cc.lea(stackPtr, x86::ptr(vmframe, offsetD + (int)(bc * sizeof(int32_t)))); cc.mov(x86::dword_ptr(stackPtr), regD[bc]); + cc.mov(x86::ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, a)), stackPtr); + cc.mov(x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_POINTER); + break; + case REGT_INT | REGT_KONST: + cc.mov(x86::dword_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, i)), konstd[bc]); + cc.mov(x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_INT); + break; + case REGT_STRING: + cc.mov(x86::ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, sp)), regS[bc]); + cc.mov(x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_STRING); break; case REGT_STRING | REGT_ADDROF: - // We don't have to do anything in this case. String values are never moved to virtual registers. + cc.mov(x86::ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, a)), regS[bc]); + cc.mov(x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_POINTER); + break; + case REGT_STRING | REGT_KONST: + cc.mov(tmp, asmjit::imm_ptr(&konsts[bc])); + cc.mov(x86::ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, sp)), tmp); + cc.mov(x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_STRING); + break; + case REGT_POINTER: + cc.mov(x86::ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, a)), regA[bc]); + cc.mov(x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_POINTER); break; case REGT_POINTER | REGT_ADDROF: - stackPtr = newTempIntPtr(); cc.lea(stackPtr, x86::ptr(vmframe, offsetA + (int)(bc * sizeof(void*)))); cc.mov(x86::ptr(stackPtr), regA[bc]); + cc.mov(x86::ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, a)), stackPtr); + cc.mov(x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_POINTER); + break; + case REGT_POINTER | REGT_KONST: + cc.mov(tmp, asmjit::imm_ptr(konsta[bc].v)); + cc.mov(x86::ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, a)), tmp); + cc.mov(x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_POINTER); + break; + case REGT_FLOAT: + cc.movsd(x86::qword_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, f)), regF[bc]); + cc.mov(x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_FLOAT); + break; + case REGT_FLOAT | REGT_MULTIREG2: + for (int j = 0; j < 2; j++) + { + cc.movsd(x86::qword_ptr(vmframe, offsetParams + (slot + j) * sizeof(VMValue) + myoffsetof(VMValue, f)), regF[bc + j]); + cc.mov(x86::byte_ptr(vmframe, offsetParams + (slot + j) * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_FLOAT); + } + numparams++; + break; + case REGT_FLOAT | REGT_MULTIREG3: + for (int j = 0; j < 3; j++) + { + cc.movsd(x86::qword_ptr(vmframe, offsetParams + (slot + j) * sizeof(VMValue) + myoffsetof(VMValue, f)), regF[bc + j]); + cc.mov(x86::byte_ptr(vmframe, offsetParams + (slot + j) * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_FLOAT); + } + numparams += 2; break; case REGT_FLOAT | REGT_ADDROF: - stackPtr = newTempIntPtr(); cc.lea(stackPtr, x86::ptr(vmframe, offsetF + (int)(bc * sizeof(double)))); - cc.movsd(x86::qword_ptr(stackPtr), regF[bc]); - // When passing the address to a float we don't know if the receiving function will treat it as float, vec2 or vec3. - if ((unsigned int)bc + 1 < regF.Size()) + for (int j = 0; j < 3; j++) { - cc.add(stackPtr, (int)sizeof(double)); - cc.movsd(x86::qword_ptr(stackPtr), regF[bc + 1]); - } - if ((unsigned int)bc + 2 < regF.Size()) - { - cc.add(stackPtr, (int)sizeof(double)); - cc.movsd(x86::qword_ptr(stackPtr), regF[bc + 2]); + if ((unsigned int)(bc + j) < regF.Size()) + cc.movsd(x86::qword_ptr(stackPtr, j * sizeof(double)), regF[bc + j]); } + cc.mov(x86::ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, a)), stackPtr); + cc.mov(x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_POINTER); break; + case REGT_FLOAT | REGT_KONST: + cc.mov(tmp, asmjit::imm_ptr(konstf + bc)); + cc.movsd(tmp2, asmjit::x86::qword_ptr(tmp)); + cc.movsd(x86::qword_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, f)), tmp2); + cc.mov(x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_FLOAT); + break; + default: + I_FatalError("Unknown REGT value passed to EmitPARAM\n"); break; } } + + return numparams; } -void JitCompiler::LoadInOuts(int b) +void JitCompiler::LoadInOuts() { - for (unsigned int i = ParamOpcodes.Size() - b; i < ParamOpcodes.Size(); i++) + for (unsigned int i = 0; i < ParamOpcodes.Size(); i++) { const VMOP ¶m = *ParamOpcodes[i]; if (param.op == OP_PARAM && (param.a & REGT_ADDROF)) @@ -321,7 +323,7 @@ void JitCompiler::FillReturns(const VMOP *retval, int numret) break; } - cc.mov(x86::ptr(callReturns, i * sizeof(VMReturn) + offsetof(VMReturn, Location)), regPtr); - cc.mov(x86::byte_ptr(callReturns, i * sizeof(VMReturn) + offsetof(VMReturn, RegType)), type); + cc.mov(x86::ptr(callReturns, i * sizeof(VMReturn) + myoffsetof(VMReturn, Location)), regPtr); + cc.mov(x86::byte_ptr(callReturns, i * sizeof(VMReturn) + myoffsetof(VMReturn, RegType)), type); } } diff --git a/src/scripting/vm/jit_flow.cpp b/src/scripting/vm/jit_flow.cpp index 8c89e36da..b0b66ce2a 100644 --- a/src/scripting/vm/jit_flow.cpp +++ b/src/scripting/vm/jit_flow.cpp @@ -45,27 +45,6 @@ void JitCompiler::EmitIJMP() EmitThrowException(X_OTHER); } -static VMFunction *GetVirtual(DObject *o, int c) -{ - auto p = o->GetClass(); - assert(c < (int)p->Virtuals.Size()); - return p->Virtuals[c]; -} - -void JitCompiler::EmitVTBL() -{ - auto label = EmitThrowExceptionLabel(X_READ_NIL); - cc.test(regA[B], regA[B]); - cc.jz(label); - - auto result = newResultIntPtr(); - auto call = CreateCall(GetVirtual); - call->setRet(0, result); - call->setArg(0, regA[B]); - call->setArg(1, asmjit::Imm(C)); - cc.mov(regA[A], result); -} - static void ValidateCall(DObject *o, VMFunction *f, int b) { try diff --git a/src/scripting/vm/jitintern.h b/src/scripting/vm/jitintern.h index 34099158a..3b6e6b3e8 100644 --- a/src/scripting/vm/jitintern.h +++ b/src/scripting/vm/jitintern.h @@ -54,9 +54,10 @@ private: void EmitDoCall(asmjit::X86Gp ptr); void EmitScriptCall(asmjit::X86Gp vmfunc, asmjit::X86Gp paramsptr); + void EmitVtbl(const VMOP *op); - void StoreInOuts(int b); - void LoadInOuts(int b); + int StoreCallParams(); + void LoadInOuts(); void LoadReturns(const VMOP *retval, int numret); void FillReturns(const VMOP *retval, int numret); void LoadCallResult(int type, int regnum, bool addrof); From e778f05b129cd17779c2211d3a3dfb25f227b28d Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Sun, 18 Nov 2018 12:59:53 +0100 Subject: [PATCH 08/17] - don't emit VMValue.Type information when we know the receiver isn't going to read it anyway --- src/scripting/vm/jit_call.cpp | 67 +++++++++++++++++++++++------------ src/scripting/vm/jitintern.h | 4 +-- 2 files changed, 47 insertions(+), 24 deletions(-) diff --git a/src/scripting/vm/jit_call.cpp b/src/scripting/vm/jit_call.cpp index fbacb1f91..56147c902 100644 --- a/src/scripting/vm/jit_call.cpp +++ b/src/scripting/vm/jit_call.cpp @@ -50,21 +50,28 @@ void JitCompiler::EmitVtbl(const VMOP *op) void JitCompiler::EmitCALL() { - EmitDoCall(regA[A]); + EmitDoCall(regA[A], nullptr); } void JitCompiler::EmitCALL_K() { auto ptr = newTempIntPtr(); - cc.mov(ptr, asmjit::imm_ptr(konsta[A].o)); - EmitDoCall(ptr); + cc.mov(ptr, asmjit::imm_ptr(konsta[A].v)); + EmitDoCall(ptr, static_cast(konsta[A].v)); } -void JitCompiler::EmitDoCall(asmjit::X86Gp vmfunc) +void JitCompiler::EmitDoCall(asmjit::X86Gp vmfunc, VMFunction *target) { using namespace asmjit; - int numparams = StoreCallParams(); + bool simpleFrameTarget = false; + if (target && (target->VarFlags & VARF_Native)) + { + VMScriptFunction *starget = static_cast(target); + simpleFrameTarget = starget->SpecialInits.Size() == 0 && starget->NumRegS == 0; + } + + int numparams = StoreCallParams(simpleFrameTarget); if (numparams != B) I_FatalError("OP_CALL parameter count does not match the number of preceding OP_PARAM instructions"); @@ -103,7 +110,7 @@ void JitCompiler::EmitScriptCall(asmjit::X86Gp vmfunc, asmjit::X86Gp paramsptr) call->setArg(4, Imm(C)); } -int JitCompiler::StoreCallParams() +int JitCompiler::StoreCallParams(bool simpleFrameTarget) { using namespace asmjit; @@ -120,7 +127,8 @@ int JitCompiler::StoreCallParams() { int abcs = ParamOpcodes[i]->i24; cc.mov(asmjit::x86::dword_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, i)), abcs); - cc.mov(asmjit::x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_INT); + if (!simpleFrameTarget) + cc.mov(asmjit::x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_INT); continue; } @@ -130,59 +138,71 @@ int JitCompiler::StoreCallParams() { case REGT_NIL: cc.mov(x86::ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, a)), (int64_t)0); - cc.mov(x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_NIL); + if (!simpleFrameTarget) + cc.mov(x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_NIL); break; case REGT_INT: cc.mov(x86::dword_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, i)), regD[bc]); - cc.mov(x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_INT); + if (!simpleFrameTarget) + cc.mov(x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_INT); break; case REGT_INT | REGT_ADDROF: cc.lea(stackPtr, x86::ptr(vmframe, offsetD + (int)(bc * sizeof(int32_t)))); cc.mov(x86::dword_ptr(stackPtr), regD[bc]); cc.mov(x86::ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, a)), stackPtr); - cc.mov(x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_POINTER); + if (!simpleFrameTarget) + cc.mov(x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_POINTER); break; case REGT_INT | REGT_KONST: cc.mov(x86::dword_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, i)), konstd[bc]); - cc.mov(x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_INT); + if (!simpleFrameTarget) + cc.mov(x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_INT); break; case REGT_STRING: cc.mov(x86::ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, sp)), regS[bc]); - cc.mov(x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_STRING); + if (!simpleFrameTarget) + cc.mov(x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_STRING); break; case REGT_STRING | REGT_ADDROF: cc.mov(x86::ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, a)), regS[bc]); - cc.mov(x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_POINTER); + if (!simpleFrameTarget) + cc.mov(x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_POINTER); break; case REGT_STRING | REGT_KONST: cc.mov(tmp, asmjit::imm_ptr(&konsts[bc])); cc.mov(x86::ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, sp)), tmp); - cc.mov(x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_STRING); + if (!simpleFrameTarget) + cc.mov(x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_STRING); break; case REGT_POINTER: cc.mov(x86::ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, a)), regA[bc]); - cc.mov(x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_POINTER); + if (!simpleFrameTarget) + cc.mov(x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_POINTER); break; case REGT_POINTER | REGT_ADDROF: cc.lea(stackPtr, x86::ptr(vmframe, offsetA + (int)(bc * sizeof(void*)))); cc.mov(x86::ptr(stackPtr), regA[bc]); cc.mov(x86::ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, a)), stackPtr); - cc.mov(x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_POINTER); + if (!simpleFrameTarget) + cc.mov(x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_POINTER); break; case REGT_POINTER | REGT_KONST: cc.mov(tmp, asmjit::imm_ptr(konsta[bc].v)); cc.mov(x86::ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, a)), tmp); - cc.mov(x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_POINTER); + if (!simpleFrameTarget) + cc.mov(x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_POINTER); break; case REGT_FLOAT: cc.movsd(x86::qword_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, f)), regF[bc]); - cc.mov(x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_FLOAT); + if (!simpleFrameTarget) + cc.mov(x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_FLOAT); break; case REGT_FLOAT | REGT_MULTIREG2: for (int j = 0; j < 2; j++) { cc.movsd(x86::qword_ptr(vmframe, offsetParams + (slot + j) * sizeof(VMValue) + myoffsetof(VMValue, f)), regF[bc + j]); - cc.mov(x86::byte_ptr(vmframe, offsetParams + (slot + j) * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_FLOAT); + if (!simpleFrameTarget) + cc.mov(x86::byte_ptr(vmframe, offsetParams + (slot + j) * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_FLOAT); } numparams++; break; @@ -190,7 +210,8 @@ int JitCompiler::StoreCallParams() for (int j = 0; j < 3; j++) { cc.movsd(x86::qword_ptr(vmframe, offsetParams + (slot + j) * sizeof(VMValue) + myoffsetof(VMValue, f)), regF[bc + j]); - cc.mov(x86::byte_ptr(vmframe, offsetParams + (slot + j) * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_FLOAT); + if (!simpleFrameTarget) + cc.mov(x86::byte_ptr(vmframe, offsetParams + (slot + j) * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_FLOAT); } numparams += 2; break; @@ -203,13 +224,15 @@ int JitCompiler::StoreCallParams() cc.movsd(x86::qword_ptr(stackPtr, j * sizeof(double)), regF[bc + j]); } cc.mov(x86::ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, a)), stackPtr); - cc.mov(x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_POINTER); + if (!simpleFrameTarget) + cc.mov(x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_POINTER); break; case REGT_FLOAT | REGT_KONST: cc.mov(tmp, asmjit::imm_ptr(konstf + bc)); cc.movsd(tmp2, asmjit::x86::qword_ptr(tmp)); cc.movsd(x86::qword_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, f)), tmp2); - cc.mov(x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_FLOAT); + if (!simpleFrameTarget) + cc.mov(x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_FLOAT); break; default: diff --git a/src/scripting/vm/jitintern.h b/src/scripting/vm/jitintern.h index 3b6e6b3e8..d417a100a 100644 --- a/src/scripting/vm/jitintern.h +++ b/src/scripting/vm/jitintern.h @@ -52,11 +52,11 @@ private: void EmitOpcode(); void EmitPopFrame(); - void EmitDoCall(asmjit::X86Gp ptr); + void EmitDoCall(asmjit::X86Gp ptr, VMFunction *target); void EmitScriptCall(asmjit::X86Gp vmfunc, asmjit::X86Gp paramsptr); void EmitVtbl(const VMOP *op); - int StoreCallParams(); + int StoreCallParams(bool simpleFrameTarget); void LoadInOuts(); void LoadReturns(const VMOP *retval, int numret); void FillReturns(const VMOP *retval, int numret); From 748dbec77af83bf3369d7f08fc5bfa02501b249d Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Sun, 18 Nov 2018 13:14:41 +0100 Subject: [PATCH 09/17] - improve dumpjit output slightly --- src/scripting/vm/jit.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/scripting/vm/jit.cpp b/src/scripting/vm/jit.cpp index 6c90b5af3..b8b7240a1 100644 --- a/src/scripting/vm/jit.cpp +++ b/src/scripting/vm/jit.cpp @@ -383,10 +383,13 @@ asmjit::CCFunc *JitCompiler::Codegen() int i = (int)(ptrdiff_t)(pc - sfunc->Code); op = pc->op; - FString lineinfo; - lineinfo.Format("; line %d: %02x%02x%02x%02x %s", sfunc->PCToLine(pc), pc->op, pc->a, pc->b, pc->c, OpNames[op]); - cc.comment("", 0); - cc.comment(lineinfo.GetChars(), lineinfo.Len()); + if (op != OP_PARAM && op != OP_PARAMI && op != OP_VTBL) + { + FString lineinfo; + lineinfo.Format("; line %d: %02x%02x%02x%02x %s", sfunc->PCToLine(pc), pc->op, pc->a, pc->b, pc->c, OpNames[op]); + cc.comment("", 0); + cc.comment(lineinfo.GetChars(), lineinfo.Len()); + } labels[i].cursor = cc.getCursor(); ResetTemp(); From a8a9ec98f3a8e37b160d3590db4496ec24d43ad9 Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Sun, 18 Nov 2018 13:49:19 +0100 Subject: [PATCH 10/17] - only allocate stack space for vmframe and call returns when we need them --- src/scripting/vm/jit.cpp | 42 +++++++++++++++++++++++++++++------ src/scripting/vm/jit_call.cpp | 8 ++++--- src/scripting/vm/jitintern.h | 13 +++++++++-- 3 files changed, 51 insertions(+), 12 deletions(-) diff --git a/src/scripting/vm/jit.cpp b/src/scripting/vm/jit.cpp index b8b7240a1..e05a4881f 100644 --- a/src/scripting/vm/jit.cpp +++ b/src/scripting/vm/jit.cpp @@ -436,6 +436,37 @@ void JitCompiler::BindLabels() cc.setCursor(cursor); } +void JitCompiler::CheckVMFrame() +{ + if (!vmframeAllocated) + { + auto cursor = cc.getCursor(); + cc.setCursor(vmframeCursor); + + auto vmstack = cc.newStack(sfunc->StackSize, 16, "vmstack"); + vmframe = cc.newIntPtr("vmframe"); + cc.lea(vmframe, vmstack); + + cc.setCursor(cursor); + vmframeAllocated = true; + } +} + +asmjit::X86Gp JitCompiler::GetCallReturns() +{ + if (!callReturnsAllocated) + { + auto cursor = cc.getCursor(); + cc.setCursor(callReturnsCursor); + auto stackalloc = cc.newStack(sizeof(VMReturn) * MAX_RETURNS, alignof(VMReturn), "stackalloc"); + callReturns = cc.newIntPtr("callReturns"); + cc.lea(callReturns, stackalloc); + cc.setCursor(cursor); + callReturnsAllocated = true; + } + return callReturns; +} + void JitCompiler::Setup() { using namespace asmjit; @@ -466,9 +497,7 @@ void JitCompiler::Setup() cc.setArg(3, ret); cc.setArg(4, numret); - auto stackalloc = cc.newStack(sizeof(VMReturn) * MAX_RETURNS, alignof(VMReturn), "stackalloc"); - callReturns = cc.newIntPtr("callReturns"); - cc.lea(callReturns, stackalloc); + callReturnsCursor = cc.getCursor(); konstd = sfunc->KonstD; konstf = sfunc->KonstF; @@ -492,8 +521,6 @@ void JitCompiler::SetupFrame() offsetD = offsetA + (int)(sfunc->NumRegA * sizeof(void*)); offsetExtra = (offsetD + (int)(sfunc->NumRegD * sizeof(int32_t)) + 15) & ~15; - vmframe = cc.newIntPtr("vmframe"); - if (sfunc->SpecialInits.Size() == 0 && sfunc->NumRegS == 0) { SetupSimpleFrame(); @@ -510,8 +537,7 @@ void JitCompiler::SetupSimpleFrame() // This is a simple frame with no constructors or destructors. Allocate it on the stack ourselves. - auto vmstack = cc.newStack(sfunc->StackSize, 16, "vmstack"); - cc.lea(vmframe, vmstack); + vmframeCursor = cc.getCursor(); int argsPos = 0; int regd = 0, regf = 0, rega = 0; @@ -592,8 +618,10 @@ void JitCompiler::SetupFullVMFrame() allocFrame->setArg(1, args); allocFrame->setArg(2, numargs); + vmframe = cc.newIntPtr("vmframe"); cc.mov(vmframe, x86::ptr(stack)); // stack->Blocks cc.mov(vmframe, x86::ptr(vmframe, VMFrameStack::OffsetLastFrame())); // Blocks->LastFrame + vmframeAllocated = true; for (int i = 0; i < sfunc->NumRegD; i++) cc.mov(regD[i], x86::dword_ptr(vmframe, offsetD + i * sizeof(int32_t))); diff --git a/src/scripting/vm/jit_call.cpp b/src/scripting/vm/jit_call.cpp index 56147c902..92f596693 100644 --- a/src/scripting/vm/jit_call.cpp +++ b/src/scripting/vm/jit_call.cpp @@ -71,6 +71,8 @@ void JitCompiler::EmitDoCall(asmjit::X86Gp vmfunc, VMFunction *target) simpleFrameTarget = starget->SpecialInits.Size() == 0 && starget->NumRegS == 0; } + CheckVMFrame(); + int numparams = StoreCallParams(simpleFrameTarget); if (numparams != B) I_FatalError("OP_CALL parameter count does not match the number of preceding OP_PARAM instructions"); @@ -106,7 +108,7 @@ void JitCompiler::EmitScriptCall(asmjit::X86Gp vmfunc, asmjit::X86Gp paramsptr) call->setArg(0, vmfunc); call->setArg(1, paramsptr); call->setArg(2, Imm(B)); - call->setArg(3, callReturns); + call->setArg(3, GetCallReturns()); call->setArg(4, Imm(C)); } @@ -346,7 +348,7 @@ void JitCompiler::FillReturns(const VMOP *retval, int numret) break; } - cc.mov(x86::ptr(callReturns, i * sizeof(VMReturn) + myoffsetof(VMReturn, Location)), regPtr); - cc.mov(x86::byte_ptr(callReturns, i * sizeof(VMReturn) + myoffsetof(VMReturn, RegType)), type); + cc.mov(x86::ptr(GetCallReturns(), i * sizeof(VMReturn) + myoffsetof(VMReturn, Location)), regPtr); + cc.mov(x86::byte_ptr(GetCallReturns(), i * sizeof(VMReturn) + myoffsetof(VMReturn, RegType)), type); } } diff --git a/src/scripting/vm/jitintern.h b/src/scripting/vm/jitintern.h index d417a100a..59004cc39 100644 --- a/src/scripting/vm/jitintern.h +++ b/src/scripting/vm/jitintern.h @@ -237,9 +237,18 @@ private: int offsetA; int offsetD; int offsetExtra; - asmjit::X86Gp vmframe; - int NumParam = 0; // Actually part of vmframe (f->NumParam), but nobody seems to read that? + TArray ParamOpcodes; + + void CheckVMFrame(); + asmjit::X86Gp GetCallReturns(); + + bool vmframeAllocated = false; + asmjit::CBNode *vmframeCursor = nullptr; + asmjit::X86Gp vmframe; + + bool callReturnsAllocated = false; + asmjit::CBNode *callReturnsCursor = nullptr; asmjit::X86Gp callReturns; const int *konstd; From 1ef772d01739d39b6735d23c3a15741ddffafd2e Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Sun, 18 Nov 2018 14:02:39 +0100 Subject: [PATCH 11/17] - add missing CheckVMFrame call --- src/scripting/vm/jit_load.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/scripting/vm/jit_load.cpp b/src/scripting/vm/jit_load.cpp index ab6dd24c9..a17ca196c 100644 --- a/src/scripting/vm/jit_load.cpp +++ b/src/scripting/vm/jit_load.cpp @@ -73,6 +73,7 @@ void JitCompiler::EmitLKP_R() void JitCompiler::EmitLFP() { + CheckVMFrame(); cc.lea(regA[A], asmjit::x86::ptr(vmframe, offsetExtra)); } From a8d4d45e89d69afacd14b9d726c59eb33443d2a5 Mon Sep 17 00:00:00 2001 From: Major Cooke Date: Sat, 17 Nov 2018 10:44:06 -0600 Subject: [PATCH 12/17] P_Thing_Raise fixes & cleanup - Transfer flags directly into the function and process inside instead of the action functions - Pass in raiser for all function calls --- src/p_actionfunctions.cpp | 20 +++++--------------- src/p_lnspec.cpp | 4 ++-- src/p_local.h | 8 +++++++- src/p_things.cpp | 6 +++--- 4 files changed, 17 insertions(+), 21 deletions(-) diff --git a/src/p_actionfunctions.cpp b/src/p_actionfunctions.cpp index 6e6e16500..ccc6d7358 100644 --- a/src/p_actionfunctions.cpp +++ b/src/p_actionfunctions.cpp @@ -4477,13 +4477,6 @@ DEFINE_ACTION_FUNCTION(AActor, A_ChangeCountFlags) return 0; } - -enum ERaise -{ - RF_TRANSFERFRIENDLINESS = 1, - RF_NOCHECKPOSITION = 2 -}; - //=========================================================================== // // A_RaiseMaster @@ -4494,10 +4487,9 @@ DEFINE_ACTION_FUNCTION(AActor, A_RaiseMaster) PARAM_SELF_PROLOGUE(AActor); PARAM_INT_DEF(flags); - bool copy = !!(flags & RF_TRANSFERFRIENDLINESS); if (self->master != NULL) { - P_Thing_Raise(self->master, copy ? self : NULL, (flags & RF_NOCHECKPOSITION)); + P_Thing_Raise(self->master, self, flags); } return 0; } @@ -4515,12 +4507,11 @@ DEFINE_ACTION_FUNCTION(AActor, A_RaiseChildren) TThinkerIterator it; AActor *mo; - bool copy = !!(flags & RF_TRANSFERFRIENDLINESS); while ((mo = it.Next()) != NULL) { if (mo->master == self) { - P_Thing_Raise(mo, copy ? self : NULL, (flags & RF_NOCHECKPOSITION)); + P_Thing_Raise(mo, self, flags); } } return 0; @@ -4539,14 +4530,13 @@ DEFINE_ACTION_FUNCTION(AActor, A_RaiseSiblings) TThinkerIterator it; AActor *mo; - bool copy = !!(flags & RF_TRANSFERFRIENDLINESS); if (self->master != NULL) { while ((mo = it.Next()) != NULL) { if (mo->master == self->master && mo != self) { - P_Thing_Raise(mo, copy ? self : NULL, (flags & RF_NOCHECKPOSITION)); + P_Thing_Raise(mo, self, flags); } } } @@ -4562,7 +4552,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_RaiseSelf) { PARAM_SELF_PROLOGUE(AActor); PARAM_INT_DEF(flags); - ACTION_RETURN_BOOL(P_Thing_Raise(self, NULL, (flags & RF_NOCHECKPOSITION))); + ACTION_RETURN_BOOL(P_Thing_Raise(self, self, flags)); } //=========================================================================== @@ -4576,7 +4566,7 @@ DEFINE_ACTION_FUNCTION(AActor, RaiseActor) PARAM_SELF_PROLOGUE(AActor); PARAM_OBJECT(other, AActor); PARAM_INT_DEF(flags); - ACTION_RETURN_BOOL(P_Thing_Raise(other, self, (flags & RF_NOCHECKPOSITION))); + ACTION_RETURN_BOOL(P_Thing_Raise(other, self, flags)); } //=========================================================================== diff --git a/src/p_lnspec.cpp b/src/p_lnspec.cpp index 2422dfe19..58ed5d1e0 100644 --- a/src/p_lnspec.cpp +++ b/src/p_lnspec.cpp @@ -1764,7 +1764,7 @@ FUNC(LS_Thing_Raise) if (arg0==0) { - ok = P_Thing_Raise (it,NULL, arg1); + ok = P_Thing_Raise (it, it, arg1); } else { @@ -1772,7 +1772,7 @@ FUNC(LS_Thing_Raise) while ( (target = iterator.Next ()) ) { - ok |= P_Thing_Raise(target,NULL, arg1); + ok |= P_Thing_Raise(target, target, arg1); } } return ok; diff --git a/src/p_local.h b/src/p_local.h index e9c2d0f2e..9b41253a9 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -160,7 +160,7 @@ bool P_Thing_Move (int tid, AActor *source, int mapspot, bool fog); int P_Thing_Damage (int tid, AActor *whofor0, int amount, FName type); void P_Thing_SetVelocity(AActor *actor, const DVector3 &vec, bool add, bool setbob); void P_RemoveThing(AActor * actor); -bool P_Thing_Raise(AActor *thing, AActor *raiser, int nocheck = false); +bool P_Thing_Raise(AActor *thing, AActor *raiser, int flags = 0); bool P_Thing_CanRaise(AActor *thing); bool P_CanResurrect(AActor *ththing, AActor *thing); PClassActor *P_GetSpawnableType(int spawnnum); @@ -475,4 +475,10 @@ enum ETexReplaceFlags void P_ReplaceTextures(const char *fromname, const char *toname, int flags); +enum ERaise +{ + RF_TRANSFERFRIENDLINESS = 1, + RF_NOCHECKPOSITION = 2 +}; + #endif // __P_LOCAL__ diff --git a/src/p_things.cpp b/src/p_things.cpp index 0cd046c33..4d055e8f6 100644 --- a/src/p_things.cpp +++ b/src/p_things.cpp @@ -432,7 +432,7 @@ void P_RemoveThing(AActor * actor) } -bool P_Thing_Raise(AActor *thing, AActor *raiser, int nocheck) +bool P_Thing_Raise(AActor *thing, AActor *raiser, int flags) { if (!thing) return false; @@ -455,7 +455,7 @@ bool P_Thing_Raise(AActor *thing, AActor *raiser, int nocheck) thing->flags |= MF_SOLID; thing->Height = info->Height; // [RH] Use real height thing->radius = info->radius; // [RH] Use real radius - if (!nocheck && !P_CheckPosition (thing, thing->Pos())) + if (!(flags & RF_NOCHECKPOSITION) && !P_CheckPosition (thing, thing->Pos())) { thing->flags = oldflags; thing->radius = oldradius; @@ -470,7 +470,7 @@ bool P_Thing_Raise(AActor *thing, AActor *raiser, int nocheck) thing->Revive(); - if (raiser != NULL) + if ((flags & RF_TRANSFERFRIENDLINESS) && raiser != nullptr) { // Let's copy the friendliness of the one who raised it. thing->CopyFriendliness(raiser, false); From a981737855bff7fc62c2aa1cc40a621f496a4bfc Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 18 Nov 2018 17:10:55 +0100 Subject: [PATCH 13/17] - generate register type info for the parameter lists of all functions. Currently used for loading parameters into registers. For checking parameters of native functions some more work is needed to get the info to the function. Currently it doesn't receive the function descriptor. --- src/d_dehacked.cpp | 2 ++ src/events.cpp | 4 +++- src/p_mobj.cpp | 15 ------------ src/p_sectors.cpp | 7 ------ src/scripting/backend/codegen.cpp | 27 ++++++++++++++-------- src/scripting/backend/vmbuilder.cpp | 1 + src/scripting/thingdef_data.cpp | 7 ++---- src/scripting/vm/vm.h | 11 ++++++++- src/scripting/vm/vmexec.cpp | 19 ++++++++------- src/scripting/vm/vmframe.cpp | 36 +++++++++++++++++++++++++++++ wadsrc/static/zscript/mapdata.txt | 1 + 11 files changed, 84 insertions(+), 46 deletions(-) diff --git a/src/d_dehacked.cpp b/src/d_dehacked.cpp index 44e675ed2..89ac12401 100644 --- a/src/d_dehacked.cpp +++ b/src/d_dehacked.cpp @@ -749,6 +749,7 @@ static void (*MBFCodePointerFactories[])(FunctionCallEmitter&, int, int) = void SetDehParams(FState *state, int codepointer) { + static uint8_t regts[] = { REGT_POINTER, REGT_POINTER, REGT_POINTER }; int value1 = state->GetMisc1(); int value2 = state->GetMisc2(); if (!(value1|value2)) return; @@ -788,6 +789,7 @@ void SetDehParams(FState *state, int codepointer) buildit.Emit(OP_RET, RET_FINAL, REGT_NIL, 0); // Attach it to the state. VMScriptFunction *sfunc = new VMScriptFunction; + sfunc->RegTypes = regts; // These functions are built after running the script compiler so they don't get this info. buildit.MakeFunction(sfunc); sfunc->NumArgs = numargs; sfunc->ImplicitArgs = numargs; diff --git a/src/events.cpp b/src/events.cpp index 8dd9baa96..6ae49f04c 100755 --- a/src/events.cpp +++ b/src/events.cpp @@ -688,7 +688,7 @@ DEFINE_EMPTY_HANDLER(DStaticEventHandler, WorldLineDamaged); DEFINE_EMPTY_HANDLER(DStaticEventHandler, WorldLightning) DEFINE_EMPTY_HANDLER(DStaticEventHandler, WorldTick) -DEFINE_EMPTY_HANDLER(DStaticEventHandler, RenderFrame) +//DEFINE_EMPTY_HANDLER(DStaticEventHandler, RenderFrame) DEFINE_EMPTY_HANDLER(DStaticEventHandler, RenderOverlay) DEFINE_EMPTY_HANDLER(DStaticEventHandler, PlayerEntered) @@ -972,6 +972,7 @@ static FRenderEvent E_SetupRenderEvent() void DStaticEventHandler::RenderFrame() { + /* This is intentionally and permanently disabled. IFVIRTUAL(DStaticEventHandler, RenderFrame) { // don't create excessive DObjects if not going to be processed anyway @@ -981,6 +982,7 @@ void DStaticEventHandler::RenderFrame() VMValue params[2] = { (DStaticEventHandler*)this, &e }; VMCall(func, params, 2, nullptr, 0); } + */ } void DStaticEventHandler::RenderOverlay(EHudState state) diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 1e4d412a2..ff48ab79d 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -4689,14 +4689,6 @@ void AActor::Tick () } } -DEFINE_ACTION_FUNCTION(AActor, Tick) -{ - PARAM_SELF_PROLOGUE(AActor); - self->Tick(); - return 0; -} - - //========================================================================== // // AActor :: CheckNoDelay @@ -8282,13 +8274,6 @@ DEFINE_ACTION_FUNCTION(AActor, SetDamage) return 0; } -DEFINE_ACTION_FUNCTION(AActor, GetDefaultByType) -{ - PARAM_PROLOGUE; - PARAM_CLASS(cls, AActor); - ACTION_RETURN_OBJECT(cls == nullptr? nullptr : GetDefaultByType(cls)); -} - // This combines all 3 variations of the internal function DEFINE_ACTION_FUNCTION(AActor, VelFromAngle) { diff --git a/src/p_sectors.cpp b/src/p_sectors.cpp index 1a95f051d..1ed2c78d6 100644 --- a/src/p_sectors.cpp +++ b/src/p_sectors.cpp @@ -1899,13 +1899,6 @@ DEFINE_ACTION_FUNCTION(_Sector, NextLowestFloorAt) ACTION_RETURN_INT(self->GetLightLevel()); } - DEFINE_ACTION_FUNCTION(_Sector, ClearSpecial) - { - PARAM_SELF_STRUCT_PROLOGUE(sector_t); - self->ClearSpecial(); - return 0; - } - DEFINE_ACTION_FUNCTION(_Sector, PortalBlocksView) { PARAM_SELF_STRUCT_PROLOGUE(sector_t); diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index f58fd7719..aba8102c1 100644 --- a/src/scripting/backend/codegen.cpp +++ b/src/scripting/backend/codegen.cpp @@ -259,7 +259,7 @@ void ExpEmit::Reuse(VMFunctionBuilder *build) // //========================================================================== -static PSymbol *FindBuiltinFunction(FName funcname, VMNativeFunction::NativeCallType func) +static PSymbol *FindBuiltinFunction(FName funcname, VMNativeFunction::NativeCallType func, const uint8_t *reginfo) { PSymbol *sym = Namespaces.GlobalNamespace->Symbols.FindSymbol(funcname, false); if (sym == nullptr) @@ -267,6 +267,7 @@ static PSymbol *FindBuiltinFunction(FName funcname, VMNativeFunction::NativeCall PSymbolVMFunction *symfunc = Create(funcname); VMNativeFunction *calldec = new VMNativeFunction(func, funcname); calldec->PrintableName = funcname.GetChars(); + calldec->RegTypes = reginfo; symfunc->Function = calldec; sym = symfunc; Namespaces.GlobalNamespace->Symbols.AddSymbol(sym); @@ -5487,7 +5488,8 @@ ExpEmit FxRandom::Emit(VMFunctionBuilder *build) { // Call DecoRandom to generate a random number. VMFunction *callfunc; - PSymbol *sym = FindBuiltinFunction(NAME_BuiltinRandom, BuiltinRandom); + static const uint8_t reginfo[] = { REGT_POINTER, REGT_INT, REGT_INT }; + PSymbol *sym = FindBuiltinFunction(NAME_BuiltinRandom, BuiltinRandom, reginfo); assert(sym->IsKindOf(RUNTIME_CLASS(PSymbolVMFunction))); assert(((PSymbolVMFunction *)sym)->Function != nullptr); @@ -5595,7 +5597,8 @@ ExpEmit FxRandomPick::Emit(VMFunctionBuilder *build) // Call BuiltinRandom to generate a random number. VMFunction *callfunc; - PSymbol *sym = FindBuiltinFunction(NAME_BuiltinRandom, BuiltinRandom); + static const uint8_t reginfo[] = { REGT_POINTER, REGT_INT, REGT_INT }; + PSymbol *sym = FindBuiltinFunction(NAME_BuiltinRandom, BuiltinRandom, reginfo); assert(sym->IsKindOf(RUNTIME_CLASS(PSymbolVMFunction))); assert(((PSymbolVMFunction *)sym)->Function != nullptr); @@ -5714,7 +5717,8 @@ ExpEmit FxFRandom::Emit(VMFunctionBuilder *build) { // Call the BuiltinFRandom function to generate a floating point random number.. VMFunction *callfunc; - PSymbol *sym = FindBuiltinFunction(NAME_BuiltinFRandom, BuiltinFRandom); + static uint8_t reginfo[] = { REGT_POINTER, REGT_FLOAT, REGT_FLOAT }; + PSymbol *sym = FindBuiltinFunction(NAME_BuiltinFRandom, BuiltinFRandom, reginfo); assert(sym->IsKindOf(RUNTIME_CLASS(PSymbolVMFunction))); assert(((PSymbolVMFunction *)sym)->Function != nullptr); @@ -5793,7 +5797,8 @@ ExpEmit FxRandom2::Emit(VMFunctionBuilder *build) { // Call the BuiltinRandom function to generate the random number. VMFunction *callfunc; - PSymbol *sym = FindBuiltinFunction(NAME_BuiltinRandom2, BuiltinRandom2); + static uint8_t reginfo[] = { REGT_POINTER, REGT_INT }; + PSymbol *sym = FindBuiltinFunction(NAME_BuiltinRandom2, BuiltinRandom2, reginfo); assert(sym->IsKindOf(RUNTIME_CLASS(PSymbolVMFunction))); assert(((PSymbolVMFunction *)sym)->Function != nullptr); @@ -5865,7 +5870,8 @@ ExpEmit FxRandomSeed::Emit(VMFunctionBuilder *build) { // Call DecoRandom to generate a random number. VMFunction *callfunc; - PSymbol *sym = FindBuiltinFunction(NAME_BuiltinRandomSeed, BuiltinRandomSeed); + static uint8_t reginfo[] = { REGT_POINTER, REGT_INT }; + PSymbol *sym = FindBuiltinFunction(NAME_BuiltinRandomSeed, BuiltinRandomSeed, reginfo); assert(sym->IsKindOf(RUNTIME_CLASS(PSymbolVMFunction))); assert(((PSymbolVMFunction *)sym)->Function != nullptr); @@ -8567,7 +8573,8 @@ ExpEmit FxActionSpecialCall::Emit(VMFunctionBuilder *build) } // Call the BuiltinCallLineSpecial function to perform the desired special. VMFunction *callfunc; - PSymbol *sym = FindBuiltinFunction(NAME_BuiltinCallLineSpecial, BuiltinCallLineSpecial); + static uint8_t reginfo[] = { REGT_INT, REGT_POINTER, REGT_INT, REGT_INT, REGT_INT, REGT_INT, REGT_INT }; + PSymbol *sym = FindBuiltinFunction(NAME_BuiltinCallLineSpecial, BuiltinCallLineSpecial, reginfo); assert(sym->IsKindOf(RUNTIME_CLASS(PSymbolVMFunction))); assert(((PSymbolVMFunction *)sym)->Function != nullptr); @@ -10776,7 +10783,8 @@ ExpEmit FxClassTypeCast::Emit(VMFunctionBuilder *build) // Call the BuiltinNameToClass function to convert from 'name' to class. VMFunction *callfunc; - PSymbol *sym = FindBuiltinFunction(NAME_BuiltinNameToClass, BuiltinNameToClass); + static uint8_t reginfo[] = { REGT_INT, REGT_POINTER }; + PSymbol *sym = FindBuiltinFunction(NAME_BuiltinNameToClass, BuiltinNameToClass, reginfo); assert(sym->IsKindOf(RUNTIME_CLASS(PSymbolVMFunction))); assert(((PSymbolVMFunction *)sym)->Function != nullptr); @@ -10885,7 +10893,8 @@ ExpEmit FxClassPtrCast::Emit(VMFunctionBuilder *build) emitters.AddParameterPointerConst(desttype); VMFunction *callfunc; - PSymbol *sym = FindBuiltinFunction(NAME_BuiltinClassCast, BuiltinClassCast); + static uint8_t reginfo[] = { REGT_POINTER, REGT_POINTER }; + PSymbol *sym = FindBuiltinFunction(NAME_BuiltinClassCast, BuiltinClassCast, reginfo); assert(sym->IsKindOf(RUNTIME_CLASS(PSymbolVMFunction))); assert(((PSymbolVMFunction *)sym)->Function != nullptr); diff --git a/src/scripting/backend/vmbuilder.cpp b/src/scripting/backend/vmbuilder.cpp index 1aa5c09f7..f11feab86 100644 --- a/src/scripting/backend/vmbuilder.cpp +++ b/src/scripting/backend/vmbuilder.cpp @@ -909,6 +909,7 @@ void FFunctionBuildList::Build() fprintf(dump, "\n*************************************************************************\n%i code bytes\n%i data bytes", codesize * 4, datasize); fclose(dump); } + VMFunction::CreateRegUseInfo(); FScriptPosition::StrictErrors = false; if (Args->CheckParm("-dumpjit")) DumpJit(); mItems.Clear(); diff --git a/src/scripting/thingdef_data.cpp b/src/scripting/thingdef_data.cpp index 6d62e7081..531af0701 100644 --- a/src/scripting/thingdef_data.cpp +++ b/src/scripting/thingdef_data.cpp @@ -943,6 +943,8 @@ void InitThingdef() auto fcp = NewStruct("FCheckPosition", nullptr); fcp->mConstructor = *FindFunction(fcp, "_Constructor")->VMPointer; fcp->mDestructor = *FindFunction(fcp, "_Destructor")->VMPointer; + static const uint8_t reguse[] = { REGT_POINTER }; + fcp->mConstructor->RegTypes = fcp->mDestructor->RegTypes = reguse; fcp->Size = sizeof(FCheckPosition); fcp->Align = alignof(FCheckPosition); @@ -991,11 +993,6 @@ void SynthesizeFlagFields() } } } -DEFINE_ACTION_FUNCTION(DObject, GameType) -{ - PARAM_PROLOGUE; - ACTION_RETURN_INT(gameinfo.gametype); -} DEFINE_ACTION_FUNCTION(DObject, BAM) { diff --git a/src/scripting/vm/vm.h b/src/scripting/vm/vm.h index ab1fdad71..f1266bd51 100644 --- a/src/scripting/vm/vm.h +++ b/src/scripting/vm/vm.h @@ -319,10 +319,11 @@ class VMFunction { public: bool Unsafe = false; - int VarFlags = 0; // [ZZ] this replaces 5+ bool fields uint8_t ImplicitArgs = 0; // either 0 for static, 1 for method or 3 for action + int VarFlags = 0; // [ZZ] this replaces 5+ bool fields unsigned VirtualIndex = ~0u; FName Name; + const uint8_t *RegTypes = nullptr; TArray DefaultArgs; FString PrintableName; // so that the VM can print meaningful info if something in this function goes wrong. @@ -352,8 +353,16 @@ public: } AllFunctions.Clear(); } + static void CreateRegUseInfo() + { + for (auto f : AllFunctions) + { + f->CreateRegUse(); + } + } static TArray AllFunctions; protected: + void CreateRegUse(); }; class VMNativeFunction : public VMFunction diff --git a/src/scripting/vm/vmexec.cpp b/src/scripting/vm/vmexec.cpp index 5b58180a8..3424b81d7 100644 --- a/src/scripting/vm/vmexec.cpp +++ b/src/scripting/vm/vmexec.cpp @@ -193,17 +193,20 @@ void VMFillParams(VMValue *params, VMFrame *callee, int numparam) const VMRegisters calleereg(callee); assert(calleefunc != NULL && !(calleefunc->VarFlags & VARF_Native)); - assert(numparam == calleefunc->NumArgs || ((int)calleefunc->DefaultArgs.Size() == calleefunc->NumArgs)); + assert(numparam == calleefunc->NumArgs); assert(REGT_INT == 0 && REGT_FLOAT == 1 && REGT_STRING == 2 && REGT_POINTER == 3); regd = regf = regs = rega = 0; - for (int i = 0; i < calleefunc->NumArgs; ++i) + const uint8_t *reginfo = calleefunc->RegTypes; + assert(reginfo != nullptr); + for (int i = 0; i < calleefunc->NumArgs; ++i, reginfo++) { - // get all actual parameters and fill the rest from the defaults. - VMValue &p = i < numparam? params[i] : calleefunc->DefaultArgs[i]; - if (p.Type < REGT_STRING) + // copy all parameters to the local registers. + VMValue &p = params[i]; + assert(*reginfo == p.Type); + if (*reginfo < REGT_STRING) { - if (p.Type == REGT_INT) + if (*reginfo == REGT_INT) { calleereg.d[regd++] = p.i; } @@ -212,13 +215,13 @@ void VMFillParams(VMValue *params, VMFrame *callee, int numparam) calleereg.f[regf++] = p.f; } } - else if (p.Type == REGT_STRING) + else if (*reginfo == REGT_STRING) { calleereg.s[regs++] = p.s(); } else { - assert(p.Type == REGT_POINTER); + assert(*reginfo == REGT_POINTER); calleereg.a[rega++] = p.a; } } diff --git a/src/scripting/vm/vmframe.cpp b/src/scripting/vm/vmframe.cpp index 0a69c8c2b..0f9da7383 100644 --- a/src/scripting/vm/vmframe.cpp +++ b/src/scripting/vm/vmframe.cpp @@ -65,6 +65,42 @@ IMPLEMENT_CLASS(VMException, false, false) TArray VMFunction::AllFunctions; +// Creates the register type list for a function. +// Native functions only need this to assert their parameters in debug mode, script functions use this to load their registers from the VMValues. +void VMFunction::CreateRegUse() +{ +#ifdef NDEBUG + if (VarFlags & VARF_Native) return; // we do not need this for native functions in release builds. +#endif + int count = 0; + if (!Proto) + { + if (RegTypes) return; + Printf(TEXTCOLOR_ORANGE "Function without prototype needs register info manually set: %s\n", PrintableName.GetChars()); + return; + } + assert(Proto->isPrototype()); + for (auto arg : Proto->ArgumentTypes) + { + count += arg? arg->GetRegCount() : 1; + } + uint8_t *regp; + RegTypes = regp = (uint8_t*)ClassDataAllocator.Alloc(count); + count = 0; + for (auto arg : Proto->ArgumentTypes) + { + if (arg == nullptr) + { + // Marker for start of varargs. + *regp++ = REGT_NIL; + } + else for (int i = 0; i < arg->GetRegCount(); i++) + { + *regp++ = arg->GetRegType(); + } + } +} + VMScriptFunction::VMScriptFunction(FName name) { Name = name; diff --git a/wadsrc/static/zscript/mapdata.txt b/wadsrc/static/zscript/mapdata.txt index 3440d4b24..9f58c0724 100644 --- a/wadsrc/static/zscript/mapdata.txt +++ b/wadsrc/static/zscript/mapdata.txt @@ -34,6 +34,7 @@ struct SectorPortal native play struct Vertex native play { native readonly Vector2 p; + native int Index(); } struct Side native play From a9ec819557ab855526ddf490af1f8052b2109a85 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 18 Nov 2018 19:31:13 +0100 Subject: [PATCH 14/17] - moved the type infomation entirely out of VMValue. For the varargs functions that used the Type field to validate their parameters, now a hidden additional argument is passed which contains a byte array with the type info for the current call's arguments. Since this is static per call location it can be better prepared once when the code is being compiled instead of being put in a runtime created array for each invocation. Everything else uses the per-function instance of the same data. The only thing that still needed the type field with a VMValue is the defaults array, so this uses a different struct type now to store its data. --- src/c_console.cpp | 5 +- src/d_dehacked.cpp | 3 +- src/info.cpp | 8 +- src/p_actionfunctions.cpp | 8 +- src/scripting/backend/codegen.cpp | 82 ++++++------- src/scripting/backend/codegen.h | 2 +- src/scripting/backend/vmbuilder.cpp | 27 ++++ src/scripting/backend/vmbuilder.h | 16 ++- src/scripting/thingdef_data.cpp | 32 +++-- src/scripting/vm/jit_call.cpp | 32 ----- src/scripting/vm/vm.h | 169 +++++++++++++++++++------- src/scripting/vm/vmexec.cpp | 1 - src/scripting/vm/vmexec.h | 2 +- src/scripting/vm/vmframe.cpp | 6 +- src/scripting/zscript/zcc_compile.cpp | 4 +- src/v_draw.cpp | 23 +++- src/v_text.cpp | 8 +- src/v_video.h | 1 + 18 files changed, 265 insertions(+), 164 deletions(-) diff --git a/src/c_console.cpp b/src/c_console.cpp index 115d2bc43..9a38ae1cf 100644 --- a/src/c_console.cpp +++ b/src/c_console.cpp @@ -62,7 +62,6 @@ #include "g_levellocals.h" #include "vm.h" -FString FStringFormat(VM_ARGS); // extern from thingdef_data.cpp #include "gi.h" @@ -1294,7 +1293,9 @@ DEFINE_ACTION_FUNCTION(_Console, HideConsole) DEFINE_ACTION_FUNCTION(_Console, Printf) { PARAM_PROLOGUE; - FString s = FStringFormat(param, numparam, ret, numret); + PARAM_VA_POINTER(va_reginfo) // Get the hidden type information array + + FString s = FStringFormat(VM_ARGS_NAMES); Printf("%s\n", s.GetChars()); return 0; } diff --git a/src/d_dehacked.cpp b/src/d_dehacked.cpp index 89ac12401..2adc83c92 100644 --- a/src/d_dehacked.cpp +++ b/src/d_dehacked.cpp @@ -777,14 +777,13 @@ void SetDehParams(FState *state, int codepointer) // self, stateowner, state (all are pointers) buildit.Registers[REGT_POINTER].Get(numargs); // Emit code to pass the standard action function parameters. - FunctionCallEmitter emitters; + FunctionCallEmitter emitters(sym->Variants[0].Implementation); for (int i = 0; i < numargs; i++) { emitters.AddParameterPointer(i, false); } // Emit code for action parameters. MBFCodePointerFactories[codepointer](emitters, value1, value2); - emitters.AddTarget(sym->Variants[0].Implementation); emitters.EmitCall(&buildit); buildit.Emit(OP_RET, RET_FINAL, REGT_NIL, 0); // Attach it to the state. diff --git a/src/info.cpp b/src/info.cpp index 385dc5bd4..cfa6b067b 100644 --- a/src/info.cpp +++ b/src/info.cpp @@ -181,7 +181,13 @@ bool FState::CallAction(AActor *self, AActor *stateowner, FStateParamInfo *info, if (ActionFunc->DefaultArgs.Size() > 0) { - auto index = actionParams.Append(ActionFunc->DefaultArgs); + auto defs = ActionFunc->DefaultArgs; + auto index = actionParams.Reserve(defs.Size()); + for (unsigned i = 0; i < defs.Size(); i++) + { + actionParams[i + index] = defs[i]; + } + if (ActionFunc->ImplicitArgs >= 1) { actionParams[index] = self; diff --git a/src/p_actionfunctions.cpp b/src/p_actionfunctions.cpp index 4c22eae16..3789fe7cd 100644 --- a/src/p_actionfunctions.cpp +++ b/src/p_actionfunctions.cpp @@ -175,7 +175,13 @@ bool AStateProvider::CallStateChain (AActor *actor, FState *state) if (state->ActionFunc->DefaultArgs.Size() > 0) { - auto index = actionParams.Append(state->ActionFunc->DefaultArgs); + auto defs = state->ActionFunc->DefaultArgs; + auto index = actionParams.Reserve(defs.Size()); + for (unsigned i = 0; i < defs.Size(); i++) + { + actionParams[i + index] = defs[i]; + } + if (state->ActionFunc->ImplicitArgs >= 1) { actionParams[index] = actor; diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index aba8102c1..b15cccea4 100644 --- a/src/scripting/backend/codegen.cpp +++ b/src/scripting/backend/codegen.cpp @@ -5471,7 +5471,7 @@ FxExpression *FxRandom::Resolve(FCompileContext &ctx) // //========================================================================== -int BuiltinRandom(VMValue *param, int numparam, VMReturn *ret, int numret) +int BuiltinRandom(VM_ARGS) { PARAM_PROLOGUE; PARAM_POINTER(rng, FRandom); @@ -5496,12 +5496,11 @@ ExpEmit FxRandom::Emit(VMFunctionBuilder *build) assert(min && max); callfunc = ((PSymbolVMFunction *)sym)->Function; - FunctionCallEmitter emitters; + FunctionCallEmitter emitters(callfunc); emitters.AddParameterPointerConst(rng); emitters.AddParameter(build, min); emitters.AddParameter(build, max); - emitters.AddTarget(callfunc); emitters.AddReturn(REGT_INT); return emitters.EmitCall(build); } @@ -5604,11 +5603,10 @@ ExpEmit FxRandomPick::Emit(VMFunctionBuilder *build) assert(((PSymbolVMFunction *)sym)->Function != nullptr); callfunc = ((PSymbolVMFunction *)sym)->Function; - FunctionCallEmitter emitters; + FunctionCallEmitter emitters(callfunc); emitters.AddParameterPointerConst(rng); emitters.AddParameterIntConst(0); emitters.AddParameterIntConst(choices.Size() - 1); - emitters.AddTarget(callfunc); emitters.AddReturn(REGT_INT); auto resultreg = emitters.EmitCall(build); @@ -5696,7 +5694,7 @@ FxFRandom::FxFRandom(FRandom *r, FxExpression *mi, FxExpression *ma, const FScri // //========================================================================== -int BuiltinFRandom(VMValue *param, int numparam, VMReturn *ret, int numret) +int BuiltinFRandom(VM_ARGS) { PARAM_PROLOGUE; PARAM_POINTER(rng, FRandom); @@ -5725,11 +5723,10 @@ ExpEmit FxFRandom::Emit(VMFunctionBuilder *build) assert(min && max); callfunc = ((PSymbolVMFunction *)sym)->Function; - FunctionCallEmitter emitters; + FunctionCallEmitter emitters(callfunc); emitters.AddParameterPointerConst(rng); emitters.AddParameter(build, min); emitters.AddParameter(build, max); - emitters.AddTarget(callfunc); emitters.AddReturn(REGT_FLOAT); return emitters.EmitCall(build); } @@ -5779,7 +5776,7 @@ FxExpression *FxRandom2::Resolve(FCompileContext &ctx) // //========================================================================== -int BuiltinRandom2(VMValue *param, int numparam, VMReturn *ret, int numret) +int BuiltinRandom2(VM_ARGS) { PARAM_PROLOGUE; PARAM_POINTER(rng, FRandom); @@ -5804,11 +5801,10 @@ ExpEmit FxRandom2::Emit(VMFunctionBuilder *build) assert(((PSymbolVMFunction *)sym)->Function != nullptr); callfunc = ((PSymbolVMFunction *)sym)->Function; - FunctionCallEmitter emitters; + FunctionCallEmitter emitters(callfunc); emitters.AddParameterPointerConst(rng); emitters.AddParameter(build, mask); - emitters.AddTarget(callfunc); emitters.AddReturn(REGT_INT); return emitters.EmitCall(build); } @@ -5857,7 +5853,7 @@ FxExpression *FxRandomSeed::Resolve(FCompileContext &ctx) // //========================================================================== -int BuiltinRandomSeed(VMValue *param, int numparam, VMReturn *ret, int numret) +int BuiltinRandomSeed(VM_ARGS) { PARAM_PROLOGUE; PARAM_POINTER(rng, FRandom) @@ -5877,11 +5873,9 @@ ExpEmit FxRandomSeed::Emit(VMFunctionBuilder *build) assert(((PSymbolVMFunction *)sym)->Function != nullptr); callfunc = ((PSymbolVMFunction *)sym)->Function; - FunctionCallEmitter emitters; - + FunctionCallEmitter emitters(callfunc); emitters.AddParameterPointerConst(rng); emitters.AddParameter(build, seed); - emitters.AddTarget(callfunc); return emitters.EmitCall(build); } @@ -6320,7 +6314,7 @@ FxExpression *FxMemberIdentifier::Resolve(FCompileContext& ctx) assert(sym->IsKindOf(RUNTIME_CLASS(PSymbolConstNumeric))); auto sn = static_cast(sym); - VMValue vmv; + TypedVMValue vmv; if (sn->ValueType->isIntCompatible()) vmv = sn->Value; else vmv = sn->Float; auto x = new FxConstant(sn->ValueType, vmv, ScriptPosition); @@ -8526,7 +8520,7 @@ FxExpression *FxActionSpecialCall::Resolve(FCompileContext& ctx) // //========================================================================== -int BuiltinCallLineSpecial(VMValue *param, int numparam, VMReturn *ret, int numret) +int BuiltinCallLineSpecial(VM_ARGS) { PARAM_PROLOGUE; PARAM_INT(special); @@ -8544,7 +8538,15 @@ ExpEmit FxActionSpecialCall::Emit(VMFunctionBuilder *build) { unsigned i = 0; - FunctionCallEmitter emitters; + // Call the BuiltinCallLineSpecial function to perform the desired special. + static uint8_t reginfo[] = { REGT_INT, REGT_POINTER, REGT_INT, REGT_INT, REGT_INT, REGT_INT, REGT_INT }; + PSymbol *sym = FindBuiltinFunction(NAME_BuiltinCallLineSpecial, BuiltinCallLineSpecial, reginfo); + + assert(sym->IsKindOf(RUNTIME_CLASS(PSymbolVMFunction))); + assert(((PSymbolVMFunction *)sym)->Function != nullptr); + VMFunction *callfunc = ((PSymbolVMFunction *)sym)->Function; + + FunctionCallEmitter emitters(callfunc); emitters.AddParameterIntConst(abs(Special)); // pass special number emitters.AddParameter(build, Self); @@ -8571,18 +8573,9 @@ ExpEmit FxActionSpecialCall::Emit(VMFunctionBuilder *build) } } } - // Call the BuiltinCallLineSpecial function to perform the desired special. - VMFunction *callfunc; - static uint8_t reginfo[] = { REGT_INT, REGT_POINTER, REGT_INT, REGT_INT, REGT_INT, REGT_INT, REGT_INT }; - PSymbol *sym = FindBuiltinFunction(NAME_BuiltinCallLineSpecial, BuiltinCallLineSpecial, reginfo); - - assert(sym->IsKindOf(RUNTIME_CLASS(PSymbolVMFunction))); - assert(((PSymbolVMFunction *)sym)->Function != nullptr); - callfunc = ((PSymbolVMFunction *)sym)->Function; ArgList.DeleteAndClear(); ArgList.ShrinkToFit(); - emitters.AddTarget(callfunc); emitters.AddReturn(REGT_INT); return emitters.EmitCall(build); } @@ -8989,7 +8982,7 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build) bool staticcall = ((vmfunc->VarFlags & VARF_Final) || vmfunc->VirtualIndex == ~0u || NoVirtual); count = 0; - FunctionCallEmitter emitters; + FunctionCallEmitter emitters(vmfunc); // Emit code to pass implied parameters ExpEmit selfemit; if (Function->Variants[0].Flags & VARF_Method) @@ -9063,7 +9056,7 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build) ArgList.DeleteAndClear(); ArgList.ShrinkToFit(); - emitters.AddTarget(vmfunc, staticcall? -1 : selfemit.RegNum); + if (!staticcall) emitters.SetVirtualReg(selfemit.RegNum); int resultcount = vmfunc->Proto->ReturnTypes.Size() == 0 ? 0 : MAX(AssignCount, 1); assert((unsigned)resultcount <= vmfunc->Proto->ReturnTypes.Size()); @@ -10582,9 +10575,8 @@ ExpEmit FxReturnStatement::Emit(VMFunctionBuilder *build) assert(pstr->mDestructor != nullptr); ExpEmit reg(build, REGT_POINTER); build->Emit(OP_ADDA_RK, reg.RegNum, build->FramePointer.RegNum, build->GetConstantInt(build->ConstructedStructs[i]->StackOffset)); - FunctionCallEmitter emitters; + FunctionCallEmitter emitters(pstr->mDestructor); emitters.AddParameter(reg, false); - emitters.AddTarget(pstr->mDestructor); emitters.EmitCall(build); } @@ -10752,7 +10744,7 @@ FxExpression *FxClassTypeCast::Resolve(FCompileContext &ctx) // //========================================================================== -int BuiltinNameToClass(VMValue *param, int numparam, VMReturn *ret, int numret) +int BuiltinNameToClass(VM_ARGS) { PARAM_PROLOGUE; PARAM_NAME(clsname); @@ -10777,9 +10769,6 @@ ExpEmit FxClassTypeCast::Emit(VMFunctionBuilder *build) { return ExpEmit(build->GetConstantAddress(nullptr), REGT_POINTER, true); } - FunctionCallEmitter emitters; - emitters.AddParameter(build, basex); - emitters.AddParameterPointerConst(const_cast(desttype)); // Call the BuiltinNameToClass function to convert from 'name' to class. VMFunction *callfunc; @@ -10790,7 +10779,9 @@ ExpEmit FxClassTypeCast::Emit(VMFunctionBuilder *build) assert(((PSymbolVMFunction *)sym)->Function != nullptr); callfunc = ((PSymbolVMFunction *)sym)->Function; - emitters.AddTarget(callfunc); + FunctionCallEmitter emitters(callfunc); + emitters.AddParameter(build, basex); + emitters.AddParameterPointerConst(const_cast(desttype)); emitters.AddReturn(REGT_POINTER); return emitters.EmitCall(build); } @@ -10875,7 +10866,7 @@ FxExpression *FxClassPtrCast::Resolve(FCompileContext &ctx) // //========================================================================== -int BuiltinClassCast(VMValue *param, int numparam, VMReturn *ret, int numret) +int BuiltinClassCast(VM_ARGS) { PARAM_PROLOGUE; PARAM_CLASS(from, DObject); @@ -10887,11 +10878,6 @@ ExpEmit FxClassPtrCast::Emit(VMFunctionBuilder *build) { ExpEmit clsname = basex->Emit(build); - FunctionCallEmitter emitters; - - emitters.AddParameter(clsname, false); - emitters.AddParameterPointerConst(desttype); - VMFunction *callfunc; static uint8_t reginfo[] = { REGT_POINTER, REGT_POINTER }; PSymbol *sym = FindBuiltinFunction(NAME_BuiltinClassCast, BuiltinClassCast, reginfo); @@ -10899,7 +10885,11 @@ ExpEmit FxClassPtrCast::Emit(VMFunctionBuilder *build) assert(sym->IsKindOf(RUNTIME_CLASS(PSymbolVMFunction))); assert(((PSymbolVMFunction *)sym)->Function != nullptr); callfunc = ((PSymbolVMFunction *)sym)->Function; - emitters.AddTarget(callfunc); + + FunctionCallEmitter emitters(callfunc); + emitters.AddParameter(clsname, false); + emitters.AddParameterPointerConst(desttype); + emitters.AddReturn(REGT_POINTER); return emitters.EmitCall(build); } @@ -11274,9 +11264,8 @@ ExpEmit FxLocalVariableDeclaration::Emit(VMFunctionBuilder *build) { ExpEmit reg(build, REGT_POINTER); build->Emit(OP_ADDA_RK, reg.RegNum, build->FramePointer.RegNum, build->GetConstantInt(StackOffset)); - FunctionCallEmitter emitters; + FunctionCallEmitter emitters(pstr->mConstructor); emitters.AddParameter(reg, false); - emitters.AddTarget(pstr->mConstructor); emitters.EmitCall(build); } if (pstr->mDestructor != nullptr) build->ConstructedStructs.Push(this); @@ -11301,9 +11290,8 @@ void FxLocalVariableDeclaration::Release(VMFunctionBuilder *build) { ExpEmit reg(build, REGT_POINTER); build->Emit(OP_ADDA_RK, reg.RegNum, build->FramePointer.RegNum, build->GetConstantInt(StackOffset)); - FunctionCallEmitter emitters; + FunctionCallEmitter emitters(pstr->mDestructor); emitters.AddParameter(reg, false); - emitters.AddTarget(pstr->mDestructor); emitters.EmitCall(build); } build->ConstructedStructs.Delete(build->ConstructedStructs.Find(this)); diff --git a/src/scripting/backend/codegen.h b/src/scripting/backend/codegen.h index ba0bb4d41..3876f9626 100644 --- a/src/scripting/backend/codegen.h +++ b/src/scripting/backend/codegen.h @@ -514,7 +514,7 @@ public: isresolved = true; } - FxConstant(PType *type, VMValue &vmval, const FScriptPosition &pos) : FxExpression(EFX_Constant, pos) + FxConstant(PType *type, TypedVMValue &vmval, const FScriptPosition &pos) : FxExpression(EFX_Constant, pos) { isresolved = true; switch (vmval.Type) diff --git a/src/scripting/backend/vmbuilder.cpp b/src/scripting/backend/vmbuilder.cpp index f11feab86..d1566730d 100644 --- a/src/scripting/backend/vmbuilder.cpp +++ b/src/scripting/backend/vmbuilder.cpp @@ -941,6 +941,8 @@ 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) + for (unsigned i = 0; i < where.RegCount; i++) reginfo.Push(where.RegType & REGT_TYPE); emitters.push_back([=](VMFunctionBuilder *build) -> int { @@ -962,6 +964,11 @@ void FunctionCallEmitter::AddParameter(VMFunctionBuilder *build, FxExpression *o void FunctionCallEmitter::AddParameter(ExpEmit &emit, bool reference) { numparams += emit.RegCount; + if (target->VarFlags & VARF_VarArg) + { + if (reference) reginfo.Push(REGT_POINTER); + else for (unsigned i = 0; i < emit.RegCount; i++) reginfo.Push(emit.RegType & REGT_TYPE); + } emitters.push_back([=](VMFunctionBuilder *build) ->int { build->Emit(OP_PARAM, emit.RegType + (reference * REGT_ADDROF), emit.RegNum); @@ -974,6 +981,8 @@ void FunctionCallEmitter::AddParameter(ExpEmit &emit, bool reference) void FunctionCallEmitter::AddParameterPointerConst(void *konst) { numparams++; + if (target->VarFlags & VARF_VarArg) + reginfo.Push(REGT_POINTER); emitters.push_back([=](VMFunctionBuilder *build) ->int { build->Emit(OP_PARAM, REGT_POINTER | REGT_KONST, build->GetConstantAddress(konst)); @@ -984,6 +993,8 @@ void FunctionCallEmitter::AddParameterPointerConst(void *konst) void FunctionCallEmitter::AddParameterPointer(int index, bool konst) { numparams++; + if (target->VarFlags & VARF_VarArg) + reginfo.Push(REGT_POINTER); emitters.push_back([=](VMFunctionBuilder *build) ->int { build->Emit(OP_PARAM, konst ? REGT_POINTER | REGT_KONST : REGT_POINTER, index); @@ -994,6 +1005,8 @@ void FunctionCallEmitter::AddParameterPointer(int index, bool konst) void FunctionCallEmitter::AddParameterFloatConst(double konst) { numparams++; + if (target->VarFlags & VARF_VarArg) + reginfo.Push(REGT_FLOAT); emitters.push_back([=](VMFunctionBuilder *build) ->int { build->Emit(OP_PARAM, REGT_FLOAT | REGT_KONST, build->GetConstantFloat(konst)); @@ -1004,6 +1017,8 @@ void FunctionCallEmitter::AddParameterFloatConst(double konst) void FunctionCallEmitter::AddParameterIntConst(int konst) { numparams++; + if (target->VarFlags & VARF_VarArg) + reginfo.Push(REGT_INT); emitters.push_back([=](VMFunctionBuilder *build) ->int { // Immediates for PARAMI must fit in 24 bits. @@ -1022,6 +1037,8 @@ void FunctionCallEmitter::AddParameterIntConst(int konst) void FunctionCallEmitter::AddParameterStringConst(const FString &konst) { numparams++; + if (target->VarFlags & VARF_VarArg) + reginfo.Push(REGT_STRING); emitters.push_back([=](VMFunctionBuilder *build) ->int { build->Emit(OP_PARAM, REGT_STRING | REGT_KONST, build->GetConstantString(konst)); @@ -1037,6 +1054,16 @@ ExpEmit FunctionCallEmitter::EmitCall(VMFunctionBuilder *build, TArray paramcount += func(build); } assert(paramcount == numparams); + if (target->VarFlags & VARF_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. + uint8_t *regbuffer = (uint8_t*)ClassDataAllocator.Alloc(reginfo.Size()); // Allocate in the arena so that the pointer does not need to be maintained. + memcpy(regbuffer, reginfo.Data(), reginfo.Size()); + build->Emit(OP_PARAM, REGT_POINTER | REGT_KONST, build->GetConstantAddress(regbuffer)); + paramcount++; + } + if (virtualselfreg == -1) { diff --git a/src/scripting/backend/vmbuilder.h b/src/scripting/backend/vmbuilder.h index 7531e4eff..28b6798d5 100644 --- a/src/scripting/backend/vmbuilder.h +++ b/src/scripting/backend/vmbuilder.h @@ -169,11 +169,22 @@ class FunctionCallEmitter // std::function and TArray are not compatible so this has to use std::vector instead. std::vector> emitters; TArray> returns; + TArray reginfo; unsigned numparams = 0; // This counts the number of pushed elements, which can differ from the number of emitters with vectors. VMFunction *target = nullptr; int virtualselfreg = -1; public: + FunctionCallEmitter(VMFunction *func) + { + target = func; + } + + void SetVirtualReg(int virtreg) + { + virtualselfreg = virtreg; + } + void AddParameter(VMFunctionBuilder *build, FxExpression *operand); void AddParameter(ExpEmit &emit, bool reference); void AddParameterPointerConst(void *konst); @@ -186,11 +197,6 @@ public: { returns.Push({ regtype, regcount }); } - void AddTarget(VMFunction *func, int virtreg = -1) - { - target = func; - virtualselfreg = virtreg; - } unsigned Count() const { return numparams; diff --git a/src/scripting/thingdef_data.cpp b/src/scripting/thingdef_data.cpp index 531af0701..019c10b3a 100644 --- a/src/scripting/thingdef_data.cpp +++ b/src/scripting/thingdef_data.cpp @@ -1020,10 +1020,16 @@ DEFINE_ACTION_FUNCTION(FStringStruct, Replace) return 0; } -FString FStringFormat(VM_ARGS) +FString FStringFormat(VM_ARGS, int offset) { - assert(param[0].Type == REGT_STRING); - FString fmtstring = param[0].s().GetChars(); + PARAM_VA_POINTER(va_reginfo) // Get the hidden type information array + assert(va_reginfo[offset] == REGT_STRING); + + FString fmtstring = param[offset].s().GetChars(); + + param += offset; + numparam -= offset; + va_reginfo += offset; // note: we don't need a real printf format parser. // enough to simply find the subtitution tokens and feed them to the real printf after checking types. @@ -1074,7 +1080,7 @@ FString FStringFormat(VM_ARGS) in_fmt = false; // fail if something was found, but it's not a string if (argnum >= numparam) ThrowAbortException(X_FORMAT_ERROR, "Not enough arguments for format."); - if (param[argnum].Type != REGT_STRING) ThrowAbortException(X_FORMAT_ERROR, "Expected a string for format %s.", fmt_current.GetChars()); + if (va_reginfo[argnum] != REGT_STRING) ThrowAbortException(X_FORMAT_ERROR, "Expected a string for format %s.", fmt_current.GetChars()); // append output.AppendFormat(fmt_current.GetChars(), param[argnum].s().GetChars()); if (!haveargnums) argnum = ++argauto; @@ -1090,7 +1096,7 @@ FString FStringFormat(VM_ARGS) in_fmt = false; // fail if something was found, but it's not a string if (argnum >= numparam) ThrowAbortException(X_FORMAT_ERROR, "Not enough arguments for format."); - if (param[argnum].Type != REGT_POINTER) ThrowAbortException(X_FORMAT_ERROR, "Expected a pointer for format %s.", fmt_current.GetChars()); + if (va_reginfo[argnum] != REGT_POINTER) ThrowAbortException(X_FORMAT_ERROR, "Expected a pointer for format %s.", fmt_current.GetChars()); // append output.AppendFormat(fmt_current.GetChars(), param[argnum].a); if (!haveargnums) argnum = ++argauto; @@ -1112,10 +1118,10 @@ FString FStringFormat(VM_ARGS) in_fmt = false; // fail if something was found, but it's not an int if (argnum >= numparam) ThrowAbortException(X_FORMAT_ERROR, "Not enough arguments for format."); - if (param[argnum].Type != REGT_INT && - param[argnum].Type != REGT_FLOAT) ThrowAbortException(X_FORMAT_ERROR, "Expected a numeric value for format %s.", fmt_current.GetChars()); + if (va_reginfo[argnum] != REGT_INT && + va_reginfo[argnum] != REGT_FLOAT) ThrowAbortException(X_FORMAT_ERROR, "Expected a numeric value for format %s.", fmt_current.GetChars()); // append - output.AppendFormat(fmt_current.GetChars(), param[argnum].ToInt()); + output.AppendFormat(fmt_current.GetChars(), param[argnum].ToInt(va_reginfo[argnum])); if (!haveargnums) argnum = ++argauto; else argnum = -1; break; @@ -1136,10 +1142,10 @@ FString FStringFormat(VM_ARGS) in_fmt = false; // fail if something was found, but it's not a float if (argnum >= numparam) ThrowAbortException(X_FORMAT_ERROR, "Not enough arguments for format."); - if (param[argnum].Type != REGT_INT && - param[argnum].Type != REGT_FLOAT) ThrowAbortException(X_FORMAT_ERROR, "Expected a numeric value for format %s.", fmt_current.GetChars()); + if (va_reginfo[argnum] != REGT_INT && + va_reginfo[argnum] != REGT_FLOAT) ThrowAbortException(X_FORMAT_ERROR, "Expected a numeric value for format %s.", fmt_current.GetChars()); // append - output.AppendFormat(fmt_current.GetChars(), param[argnum].ToDouble()); + output.AppendFormat(fmt_current.GetChars(), param[argnum].ToDouble(va_reginfo[argnum])); if (!haveargnums) argnum = ++argauto; else argnum = -1; break; @@ -1181,7 +1187,7 @@ FString FStringFormat(VM_ARGS) DEFINE_ACTION_FUNCTION(FStringStruct, Format) { PARAM_PROLOGUE; - FString s = FStringFormat(param, numparam, ret, numret); + FString s = FStringFormat(VM_ARGS_NAMES); ACTION_RETURN_STRING(s); } @@ -1189,7 +1195,7 @@ DEFINE_ACTION_FUNCTION(FStringStruct, AppendFormat) { PARAM_SELF_STRUCT_PROLOGUE(FString); // first parameter is the self pointer - FString s = FStringFormat(param+1, numparam-1, ret, numret); + FString s = FStringFormat(VM_ARGS_NAMES, 1); (*self) += s; return 0; } diff --git a/src/scripting/vm/jit_call.cpp b/src/scripting/vm/jit_call.cpp index 92f596693..444bc04b8 100644 --- a/src/scripting/vm/jit_call.cpp +++ b/src/scripting/vm/jit_call.cpp @@ -129,8 +129,6 @@ int JitCompiler::StoreCallParams(bool simpleFrameTarget) { int abcs = ParamOpcodes[i]->i24; cc.mov(asmjit::x86::dword_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, i)), abcs); - if (!simpleFrameTarget) - cc.mov(asmjit::x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_INT); continue; } @@ -140,71 +138,47 @@ int JitCompiler::StoreCallParams(bool simpleFrameTarget) { case REGT_NIL: cc.mov(x86::ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, a)), (int64_t)0); - if (!simpleFrameTarget) - cc.mov(x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_NIL); break; case REGT_INT: cc.mov(x86::dword_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, i)), regD[bc]); - if (!simpleFrameTarget) - cc.mov(x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_INT); break; case REGT_INT | REGT_ADDROF: cc.lea(stackPtr, x86::ptr(vmframe, offsetD + (int)(bc * sizeof(int32_t)))); cc.mov(x86::dword_ptr(stackPtr), regD[bc]); cc.mov(x86::ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, a)), stackPtr); - if (!simpleFrameTarget) - cc.mov(x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_POINTER); break; case REGT_INT | REGT_KONST: cc.mov(x86::dword_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, i)), konstd[bc]); - if (!simpleFrameTarget) - cc.mov(x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_INT); break; case REGT_STRING: cc.mov(x86::ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, sp)), regS[bc]); - if (!simpleFrameTarget) - cc.mov(x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_STRING); break; case REGT_STRING | REGT_ADDROF: cc.mov(x86::ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, a)), regS[bc]); - if (!simpleFrameTarget) - cc.mov(x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_POINTER); break; case REGT_STRING | REGT_KONST: cc.mov(tmp, asmjit::imm_ptr(&konsts[bc])); cc.mov(x86::ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, sp)), tmp); - if (!simpleFrameTarget) - cc.mov(x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_STRING); break; case REGT_POINTER: cc.mov(x86::ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, a)), regA[bc]); - if (!simpleFrameTarget) - cc.mov(x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_POINTER); break; case REGT_POINTER | REGT_ADDROF: cc.lea(stackPtr, x86::ptr(vmframe, offsetA + (int)(bc * sizeof(void*)))); cc.mov(x86::ptr(stackPtr), regA[bc]); cc.mov(x86::ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, a)), stackPtr); - if (!simpleFrameTarget) - cc.mov(x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_POINTER); break; case REGT_POINTER | REGT_KONST: cc.mov(tmp, asmjit::imm_ptr(konsta[bc].v)); cc.mov(x86::ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, a)), tmp); - if (!simpleFrameTarget) - cc.mov(x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_POINTER); break; case REGT_FLOAT: cc.movsd(x86::qword_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, f)), regF[bc]); - if (!simpleFrameTarget) - cc.mov(x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_FLOAT); break; case REGT_FLOAT | REGT_MULTIREG2: for (int j = 0; j < 2; j++) { cc.movsd(x86::qword_ptr(vmframe, offsetParams + (slot + j) * sizeof(VMValue) + myoffsetof(VMValue, f)), regF[bc + j]); - if (!simpleFrameTarget) - cc.mov(x86::byte_ptr(vmframe, offsetParams + (slot + j) * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_FLOAT); } numparams++; break; @@ -212,8 +186,6 @@ int JitCompiler::StoreCallParams(bool simpleFrameTarget) for (int j = 0; j < 3; j++) { cc.movsd(x86::qword_ptr(vmframe, offsetParams + (slot + j) * sizeof(VMValue) + myoffsetof(VMValue, f)), regF[bc + j]); - if (!simpleFrameTarget) - cc.mov(x86::byte_ptr(vmframe, offsetParams + (slot + j) * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_FLOAT); } numparams += 2; break; @@ -226,15 +198,11 @@ int JitCompiler::StoreCallParams(bool simpleFrameTarget) cc.movsd(x86::qword_ptr(stackPtr, j * sizeof(double)), regF[bc + j]); } cc.mov(x86::ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, a)), stackPtr); - if (!simpleFrameTarget) - cc.mov(x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_POINTER); break; case REGT_FLOAT | REGT_KONST: cc.mov(tmp, asmjit::imm_ptr(konstf + bc)); cc.movsd(tmp2, asmjit::x86::qword_ptr(tmp)); cc.movsd(x86::qword_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, f)), tmp2); - if (!simpleFrameTarget) - cc.mov(x86::byte_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, Type)), (int)REGT_FLOAT); break; default: diff --git a/src/scripting/vm/vm.h b/src/scripting/vm/vm.h index f1266bd51..3c856f103 100644 --- a/src/scripting/vm/vm.h +++ b/src/scripting/vm/vm.h @@ -196,8 +196,7 @@ struct VMReturn struct VMRegisters; - -struct VMValue +struct TypedVMValue { union { @@ -211,11 +210,90 @@ struct VMValue const FString &s() const { return *sp; } - VMValue() + TypedVMValue() { a = NULL; Type = REGT_NIL; } + TypedVMValue(const TypedVMValue &o) + { + biggest = o.biggest; + } + TypedVMValue(int v) + { + i = v; + Type = REGT_INT; + } + TypedVMValue(double v) + { + f = v; + Type = REGT_FLOAT; + } + + TypedVMValue(const FString *s) + { + sp = s; + Type = REGT_STRING; + } + TypedVMValue(DObject *v) + { + a = v; + Type = REGT_POINTER; + } + TypedVMValue(void *v) + { + a = v; + Type = REGT_POINTER; + } + TypedVMValue &operator=(const TypedVMValue &o) + { + biggest = o.biggest; + return *this; + } + TypedVMValue &operator=(int v) + { + i = v; + Type = REGT_INT; + return *this; + } + TypedVMValue &operator=(double v) + { + f = v; + Type = REGT_FLOAT; + return *this; + } + TypedVMValue &operator=(const FString *v) + { + sp = v; + Type = REGT_STRING; + return *this; + } + TypedVMValue &operator=(DObject *v) + { + a = v; + Type = REGT_POINTER; + return *this; + } +}; + + +struct VMValue +{ + union + { + int i; + void *a; + double f; + struct { int foo[2]; } biggest; + const FString *sp; + }; + + const FString &s() const { return *sp; } + + VMValue() + { + a = NULL; + } VMValue(const VMValue &o) { biggest = o.biggest; @@ -223,12 +301,10 @@ struct VMValue VMValue(int v) { i = v; - Type = REGT_INT; } VMValue(double v) { f = v; - Type = REGT_FLOAT; } VMValue(const char *s) = delete; VMValue(const FString &s) = delete; @@ -236,39 +312,38 @@ struct VMValue VMValue(const FString *s) { sp = s; - Type = REGT_STRING; } VMValue(DObject *v) { a = v; - Type = REGT_POINTER; } VMValue(void *v) { a = v; - Type = REGT_POINTER; } VMValue &operator=(const VMValue &o) { biggest = o.biggest; return *this; } + VMValue &operator=(const TypedVMValue &o) + { + memcpy(&biggest, &o.biggest, sizeof(biggest)); + return *this; + } VMValue &operator=(int v) { i = v; - Type = REGT_INT; return *this; } VMValue &operator=(double v) { f = v; - Type = REGT_FLOAT; return *this; } VMValue &operator=(const FString *v) { sp = v; - Type = REGT_STRING; return *this; } VMValue &operator=(const FString &v) = delete; @@ -276,10 +351,9 @@ struct VMValue VMValue &operator=(DObject *v) { a = v; - Type = REGT_POINTER; return *this; } - int ToInt() + int ToInt(int Type) { if (Type == REGT_INT) { @@ -296,7 +370,7 @@ struct VMValue // FIXME return 0; } - double ToDouble() + double ToDouble(int Type) { if (Type == REGT_FLOAT) { @@ -324,7 +398,7 @@ public: unsigned VirtualIndex = ~0u; FName Name; const uint8_t *RegTypes = nullptr; - TArray DefaultArgs; + TArray DefaultArgs; FString PrintableName; // so that the VM can print meaningful info if something in this function goes wrong. class PPrototype *Proto; @@ -365,10 +439,22 @@ protected: void CreateRegUse(); }; +// Use this in the prototype for a native function. + +#ifdef NDEBUG +#define VM_ARGS VMValue *param, int numparam, VMReturn *ret, int numret +#define VM_ARGS_NAMES param, numparam, ret, numret +#define VM_INVOKE(param, numparam, ret, numret, reginfo) (param), (numparam), (ret), (numret) +#else +#define VM_ARGS VMValue *param, int numparam, VMReturn *ret, int numret, const uint8_t *reginfo +#define VM_ARGS_NAMES param, numparam, ret, numret, reginfo +#define VM_INVOKE(param, numparam, ret, numret, reginfo) (param), (numparam), (ret), (numret), (reginfo) +#endif + class VMNativeFunction : public VMFunction { public: - typedef int (*NativeCallType)(VMValue *param, int numparam, VMReturn *ret, int numret); + typedef int (*NativeCallType)(VM_ARGS); // 8 is VARF_Native. I can't write VARF_Native because of circular references between this and dobject/dobjtype. VMNativeFunction() : NativeCall(NULL) { VarFlags = 8; ScriptCall = &VMNativeFunction::NativeScriptCall; } @@ -390,10 +476,6 @@ inline int VMCallAction(VMFunction *func, VMValue *params, int numparams, VMRetu return VMCall(func, params, numparams, results, numresults); } -// Use this in the prototype for a native function. -#define VM_ARGS VMValue *param, int numparam, VMReturn *ret, int numret -#define VM_ARGS_NAMES param, numparam, ret, numret - // Use these to collect the parameters in a native function. // variable name at position

void NullParam(const char *varname); @@ -404,31 +486,28 @@ bool AssertObject(void * ob); #define PARAM_NULLCHECK(ptr, var) (ptr == nullptr? NullParam(#var), ptr : ptr) -#define ASSERTINT(p) assert((p).Type == REGT_INT) -#define ASSERTFLOAT(p) assert((p).Type == REGT_FLOAT) -#define ASSERTSTRING(p) assert((p).Type == REGT_STRING) -#define ASSERTOBJECT(p) assert((p).Type == REGT_POINTER && AssertObject(p.a)) -#define ASSERTPOINTER(p) assert((p).Type == REGT_POINTER) +// This cannot assert because there is no info for varargs +#define PARAM_VA_POINTER(x) const uint8_t *x = (const uint8_t *)param[numparam-1].a; // For required parameters. -#define PARAM_INT_AT(p,x) assert((p) < numparam); assert(param[p].Type == REGT_INT); int x = param[p].i; -#define PARAM_UINT_AT(p,x) assert((p) < numparam); assert(param[p].Type == REGT_INT); unsigned x = param[p].i; -#define PARAM_BOOL_AT(p,x) assert((p) < numparam); assert(param[p].Type == REGT_INT); bool x = !!param[p].i; -#define PARAM_NAME_AT(p,x) assert((p) < numparam); assert(param[p].Type == REGT_INT); FName x = ENamedName(param[p].i); -#define PARAM_SOUND_AT(p,x) assert((p) < numparam); assert(param[p].Type == REGT_INT); FSoundID x = param[p].i; -#define PARAM_COLOR_AT(p,x) assert((p) < numparam); assert(param[p].Type == REGT_INT); PalEntry x; x.d = param[p].i; -#define PARAM_FLOAT_AT(p,x) assert((p) < numparam); assert(param[p].Type == REGT_FLOAT); double x = param[p].f; -#define PARAM_ANGLE_AT(p,x) assert((p) < numparam); assert(param[p].Type == REGT_FLOAT); DAngle x = param[p].f; -#define PARAM_STRING_AT(p,x) assert((p) < numparam); assert(param[p].Type == REGT_STRING); FString x = param[p].s(); -#define PARAM_STATE_AT(p,x) assert((p) < numparam); assert(param[p].Type == REGT_INT); FState *x = (FState *)StateLabels.GetState(param[p].i, self->GetClass()); -#define PARAM_STATE_ACTION_AT(p,x) assert((p) < numparam); assert(param[p].Type == REGT_INT); FState *x = (FState *)StateLabels.GetState(param[p].i, stateowner->GetClass()); -#define PARAM_POINTER_AT(p,x,type) assert((p) < numparam); assert(param[p].Type == REGT_POINTER); type *x = (type *)param[p].a; -#define PARAM_POINTERTYPE_AT(p,x,type) assert((p) < numparam); assert(param[p].Type == REGT_POINTER); type x = (type )param[p].a; -#define PARAM_OBJECT_AT(p,x,type) assert((p) < numparam); assert(param[p].Type == REGT_POINTER && AssertObject(param[p].a)); type *x = (type *)param[p].a; assert(x == NULL || x->IsKindOf(RUNTIME_CLASS(type))); -#define PARAM_CLASS_AT(p,x,base) assert((p) < numparam); assert(param[p].Type == REGT_POINTER); base::MetaClass *x = (base::MetaClass *)param[p].a; assert(x == NULL || x->IsDescendantOf(RUNTIME_CLASS(base))); -#define PARAM_POINTER_NOT_NULL_AT(p,x,type) assert((p) < numparam); assert(param[p].Type == REGT_POINTER); type *x = (type *)PARAM_NULLCHECK(param[p].a, #x); -#define PARAM_OBJECT_NOT_NULL_AT(p,x,type) assert((p) < numparam); assert(param[p].Type == REGT_POINTER && (AssertObject(param[p].a))); type *x = (type *)PARAM_NULLCHECK(param[p].a, #x); assert(x == NULL || x->IsKindOf(RUNTIME_CLASS(type))); -#define PARAM_CLASS_NOT_NULL_AT(p,x,base) assert((p) < numparam); assert(param[p].Type == REGT_POINTER); base::MetaClass *x = (base::MetaClass *)PARAM_NULLCHECK(param[p].a, #x); assert(x == NULL || x->IsDescendantOf(RUNTIME_CLASS(base))); +#define PARAM_INT_AT(p,x) assert((p) < numparam); assert(reginfo[p] == REGT_INT); int x = param[p].i; +#define PARAM_UINT_AT(p,x) assert((p) < numparam); assert(reginfo[p] == REGT_INT); unsigned x = param[p].i; +#define PARAM_BOOL_AT(p,x) assert((p) < numparam); assert(reginfo[p] == REGT_INT); bool x = !!param[p].i; +#define PARAM_NAME_AT(p,x) assert((p) < numparam); assert(reginfo[p] == REGT_INT); FName x = ENamedName(param[p].i); +#define PARAM_SOUND_AT(p,x) assert((p) < numparam); assert(reginfo[p] == REGT_INT); FSoundID x = param[p].i; +#define PARAM_COLOR_AT(p,x) assert((p) < numparam); assert(reginfo[p] == REGT_INT); PalEntry x; x.d = param[p].i; +#define PARAM_FLOAT_AT(p,x) assert((p) < numparam); assert(reginfo[p] == REGT_FLOAT); double x = param[p].f; +#define PARAM_ANGLE_AT(p,x) assert((p) < numparam); assert(reginfo[p] == REGT_FLOAT); DAngle x = param[p].f; +#define PARAM_STRING_AT(p,x) assert((p) < numparam); assert(reginfo[p] == REGT_STRING); FString x = param[p].s(); +#define PARAM_STATE_AT(p,x) assert((p) < numparam); assert(reginfo[p] == REGT_INT); FState *x = (FState *)StateLabels.GetState(param[p].i, self->GetClass()); +#define PARAM_STATE_ACTION_AT(p,x) assert((p) < numparam); assert(reginfo[p] == REGT_INT); FState *x = (FState *)StateLabels.GetState(param[p].i, stateowner->GetClass()); +#define PARAM_POINTER_AT(p,x,type) assert((p) < numparam); assert(reginfo[p] == REGT_POINTER); type *x = (type *)param[p].a; +#define PARAM_POINTERTYPE_AT(p,x,type) assert((p) < numparam); assert(reginfo[p] == REGT_POINTER); type x = (type )param[p].a; +#define PARAM_OBJECT_AT(p,x,type) assert((p) < numparam); assert(reginfo[p] == REGT_POINTER && AssertObject(param[p].a)); type *x = (type *)param[p].a; assert(x == NULL || x->IsKindOf(RUNTIME_CLASS(type))); +#define PARAM_CLASS_AT(p,x,base) assert((p) < numparam); assert(reginfo[p] == REGT_POINTER); base::MetaClass *x = (base::MetaClass *)param[p].a; assert(x == NULL || x->IsDescendantOf(RUNTIME_CLASS(base))); +#define PARAM_POINTER_NOT_NULL_AT(p,x,type) assert((p) < numparam); assert(reginfo[p] == REGT_POINTER); type *x = (type *)PARAM_NULLCHECK(param[p].a, #x); +#define PARAM_OBJECT_NOT_NULL_AT(p,x,type) assert((p) < numparam); assert(reginfo[p] == REGT_POINTER && (AssertObject(param[p].a))); type *x = (type *)PARAM_NULLCHECK(param[p].a, #x); assert(x == NULL || x->IsKindOf(RUNTIME_CLASS(type))); +#define PARAM_CLASS_NOT_NULL_AT(p,x,base) assert((p) < numparam); assert(reginfo[p] == REGT_POINTER); base::MetaClass *x = (base::MetaClass *)PARAM_NULLCHECK(param[p].a, #x); assert(x == NULL || x->IsDescendantOf(RUNTIME_CLASS(base))); // The above, but with an automatically increasing position index. @@ -454,7 +533,7 @@ bool AssertObject(void * ob); #define PARAM_OBJECT_NOT_NULL(x,type) ++paramnum; PARAM_OBJECT_NOT_NULL_AT(paramnum,x,type) #define PARAM_CLASS_NOT_NULL(x,base) ++paramnum; PARAM_CLASS_NOT_NULL_AT(paramnum,x,base) -typedef int(*actionf_p)(VMValue *param, int numparam, VMReturn *ret, int numret);/*(VM_ARGS)*/ +typedef int(*actionf_p)(VM_ARGS); struct FieldDesc { @@ -591,7 +670,7 @@ class PFunction; VMFunction *FindVMFunction(PClass *cls, const char *name); #define DECLARE_VMFUNC(cls, name) static VMFunction *name; if (name == nullptr) name = FindVMFunction(RUNTIME_CLASS(cls), #name); -FString FStringFormat(VM_ARGS); +FString FStringFormat(VM_ARGS, int offset = 0); unsigned GetVirtualIndex(PClass *cls, const char *funcname); diff --git a/src/scripting/vm/vmexec.cpp b/src/scripting/vm/vmexec.cpp index 3424b81d7..0d0e2125f 100644 --- a/src/scripting/vm/vmexec.cpp +++ b/src/scripting/vm/vmexec.cpp @@ -203,7 +203,6 @@ void VMFillParams(VMValue *params, VMFrame *callee, int numparam) { // copy all parameters to the local registers. VMValue &p = params[i]; - assert(*reginfo == p.Type); if (*reginfo < REGT_STRING) { if (*reginfo == REGT_INT) diff --git a/src/scripting/vm/vmexec.h b/src/scripting/vm/vmexec.h index c910d61b9..2a67daaec 100644 --- a/src/scripting/vm/vmexec.h +++ b/src/scripting/vm/vmexec.h @@ -697,7 +697,7 @@ static int ExecScriptFunc(VMFrameStack *stack, VMReturn *ret, int numret) try { VMCycles[0].Unclock(); - numret = static_cast(call)->NativeCall(reg.param + f->NumParam - b, b, returns, C); + numret = static_cast(call)->NativeCall(VM_INVOKE(reg.param + f->NumParam - b, b, returns, C, call->RegTypes)); VMCycles[0].Clock(); } catch (CVMAbortException &err) diff --git a/src/scripting/vm/vmframe.cpp b/src/scripting/vm/vmframe.cpp index 0f9da7383..e912cdad0 100644 --- a/src/scripting/vm/vmframe.cpp +++ b/src/scripting/vm/vmframe.cpp @@ -296,7 +296,7 @@ int VMNativeFunction::NativeScriptCall(VMFunction *func, VMValue *params, int nu try { VMCycles[0].Unclock(); - numret = static_cast(func)->NativeCall(params, numparams, returns, numret); + numret = static_cast(func)->NativeCall(VM_INVOKE(params, numparams, returns, numret, func->RegTypes)); VMCycles[0].Clock(); return numret; @@ -571,7 +571,7 @@ int VMCall(VMFunction *func, VMValue *params, int numparams, VMReturn *results, { if (func->VarFlags & VARF_Native) { - return static_cast(func)->NativeCall(params, numparams, results, numresults); + return static_cast(func)->NativeCall(VM_INVOKE(params, numparams, results, numresults, func->RegTypes)); } else { @@ -730,7 +730,7 @@ void ThrowAbortException(VMScriptFunction *sfunc, VMOP *line, EVMAbortException DEFINE_ACTION_FUNCTION(DObject, ThrowAbortException) { PARAM_PROLOGUE; - FString s = FStringFormat(param, numparam, ret, numret); + FString s = FStringFormat(VM_ARGS_NAMES); ThrowAbortException(X_OTHER, s.GetChars()); return 0; } diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index 0a06f7b7c..a5b8d875a 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -2287,7 +2287,7 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool TArray rets(1); TArray args; TArray argflags; - TArray argdefaults; + TArray argdefaults; TArray argnames; rets.Clear(); @@ -2495,7 +2495,7 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool do { int elementcount = 1; - VMValue vmval[3]; // default is REGT_NIL which means 'no default value' here. + TypedVMValue vmval[3]; // default is REGT_NIL which means 'no default value' here. if (p->Type != nullptr) { auto type = DetermineType(c->Type(), p, f->Name, p->Type, false, false); diff --git a/src/v_draw.cpp b/src/v_draw.cpp index ca9ff6640..c64fcedfd 100644 --- a/src/v_draw.cpp +++ b/src/v_draw.cpp @@ -171,10 +171,12 @@ DEFINE_ACTION_FUNCTION(_Screen, DrawTexture) PARAM_FLOAT(x); PARAM_FLOAT(y); + PARAM_VA_POINTER(va_reginfo) // Get the hidden type information array + if (!screen->HasBegun2D()) ThrowAbortException(X_OTHER, "Attempt to draw to screen outside a draw function"); FTexture *tex = animate ? TexMan(FSetTextureID(texid)) : TexMan[FSetTextureID(texid)]; - VMVa_List args = { param + 4, 0, numparam - 4 }; + VMVa_List args = { param + 4, 0, numparam - 5, va_reginfo }; screen->DrawTexture(tex, x, y, args); return 0; } @@ -225,10 +227,12 @@ DEFINE_ACTION_FUNCTION(_Screen, DrawShape) PARAM_BOOL(animate); PARAM_POINTER(shape, DShape2D); + PARAM_VA_POINTER(va_reginfo) // Get the hidden type information array + if (!screen->HasBegun2D()) ThrowAbortException(X_OTHER, "Attempt to draw to screen outside a draw function"); FTexture *tex = animate ? TexMan(FSetTextureID(texid)) : TexMan[FSetTextureID(texid)]; - VMVa_List args = { param + 3, 0, numparam - 3 }; + VMVa_List args = { param + 3, 0, numparam - 4, va_reginfo }; screen->DrawShape(tex, shape, args); return 0; @@ -418,7 +422,7 @@ int ListGetInt(VMVa_List &tags) { if (tags.curindex < tags.numargs) { - if (tags.args[tags.curindex].Type == REGT_INT) + if (tags.reginfo[tags.curindex] == REGT_INT) { return tags.args[tags.curindex++].i; } @@ -429,11 +433,18 @@ int ListGetInt(VMVa_List &tags) static inline double ListGetDouble(VMVa_List &tags) { - if (tags.curindex < tags.numargs && tags.args[tags.curindex].Type == REGT_FLOAT) + if (tags.curindex < tags.numargs) { - return tags.args[tags.curindex++].f; + if (tags.reginfo[tags.curindex] == REGT_FLOAT) + { + return tags.args[tags.curindex++].f; + } + if (tags.reginfo[tags.curindex] == REGT_INT) + { + return tags.args[tags.curindex++].i; + } + ThrowAbortException(X_OTHER, "Invalid parameter in draw function, float expected"); } - ThrowAbortException(X_OTHER, "Invalid parameter in draw function, float expected"); return 0; } diff --git a/src/v_text.cpp b/src/v_text.cpp index 7e4a44b89..fe535b598 100644 --- a/src/v_text.cpp +++ b/src/v_text.cpp @@ -191,8 +191,10 @@ DEFINE_ACTION_FUNCTION(_Screen, DrawChar) PARAM_FLOAT(y); PARAM_INT(chr); + PARAM_VA_POINTER(va_reginfo) // Get the hidden type information array + if (!screen->HasBegun2D()) ThrowAbortException(X_OTHER, "Attempt to draw to screen outside a draw function"); - VMVa_List args = { param + 5, 0, numparam - 5 }; + VMVa_List args = { param + 5, 0, numparam - 6, va_reginfo }; screen->DrawChar(font, cr, x, y, chr, args); return 0; } @@ -321,8 +323,10 @@ DEFINE_ACTION_FUNCTION(_Screen, DrawText) PARAM_FLOAT(y); PARAM_STRING(chr); + PARAM_VA_POINTER(va_reginfo) // Get the hidden type information array + if (!screen->HasBegun2D()) ThrowAbortException(X_OTHER, "Attempt to draw to screen outside a draw function"); - VMVa_List args = { param + 5, 0, numparam - 5 }; + VMVa_List args = { param + 5, 0, numparam - 6, va_reginfo }; const char *txt = chr[0] == '$' ? GStrings(&chr[1]) : chr.GetChars(); screen->DrawText(font, cr, x, y, txt, args); return 0; diff --git a/src/v_video.h b/src/v_video.h index 61185e1b3..71ac19e3d 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -287,6 +287,7 @@ struct VMVa_List VMValue *args; int curindex; int numargs; + const uint8_t *reginfo; }; // // VIDEO From 3aef8418d93ab34d16deade92cfee36fa61aff0e Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 18 Nov 2018 19:48:09 +0100 Subject: [PATCH 15/17] - fixed the type checks for object arrays. Null pointers must be allowed and non-object pointers which are not null must be explicitly checked for because the code could crash on them when performing a static_cast on an incorrect type. --- src/scripting/backend/codegen.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index 039a21dab..7f864bea4 100644 --- a/src/scripting/backend/codegen.cpp +++ b/src/scripting/backend/codegen.cpp @@ -8246,12 +8246,17 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx) } if (isDynArrayObj && ((MethodName == NAME_Push && idx == 0) || (MethodName == NAME_Insert && idx == 1))) { - // The DynArray_Obj declaration in dynarrays.txt doesn't support generics yet. Check the type here as if it did. - if (!static_cast(elementType)->PointedClass()->IsAncestorOf(static_cast(a->ValueType)->PointedClass())) + // Null pointers are always valid. + if (!a->isConstant() || static_cast(a)->GetValue().GetPointer() != nullptr) { - ScriptPosition.Message(MSG_ERROR, "Type mismatch in function argument"); - delete this; - return nullptr; + // The DynArray_Obj declaration in dynarrays.txt doesn't support generics yet. Check the type here as if it did. + if (!a->ValueType->isObjectPointer() || + !static_cast(elementType)->PointedClass()->IsAncestorOf(static_cast(a->ValueType)->PointedClass())) + { + ScriptPosition.Message(MSG_ERROR, "Type mismatch in function argument"); + delete this; + return nullptr; + } } } if (a->IsDynamicArray()) From 426ee2b78efac7a0978beda8a97fd53ff1d9bfdf Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 18 Nov 2018 20:03:06 +0100 Subject: [PATCH 16/17] - fixed: Ceiling render hack segments were inserted into the floor list and incorrectly rendered. --- src/hwrenderer/scene/hw_renderhacks.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hwrenderer/scene/hw_renderhacks.cpp b/src/hwrenderer/scene/hw_renderhacks.cpp index c9e34b062..6814fd5f5 100644 --- a/src/hwrenderer/scene/hw_renderhacks.cpp +++ b/src/hwrenderer/scene/hw_renderhacks.cpp @@ -763,12 +763,12 @@ void HWDrawInfo::PrepareUpperGap(seg_t * seg) CreateFloodPoly(&ws, vertices.first+4, ws.z2, fakebsector, true); gl_floodrendernode *node = NewFloodRenderNode(); - auto pNode = floodFloorSegs.CheckKey(fakebsector->sectornum); + auto pNode = floodCeilingSegs.CheckKey(fakebsector->sectornum); node->next = pNode? *pNode : nullptr; node->seg = seg; node->vertexindex = vertices.second; - floodFloorSegs[fakebsector->sectornum] = node; + floodCeilingSegs[fakebsector->sectornum] = node; } From 108874f3791bc00b92a309c501b2532da1953830 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 18 Nov 2018 21:18:15 +0100 Subject: [PATCH 17/17] - fixed the reginfo checks for the Draw... functions. They need to be offset by the same amount as the arguments so that the values are properly matched. --- src/v_draw.cpp | 4 ++-- src/v_text.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/v_draw.cpp b/src/v_draw.cpp index c64fcedfd..502e14a29 100644 --- a/src/v_draw.cpp +++ b/src/v_draw.cpp @@ -176,7 +176,7 @@ DEFINE_ACTION_FUNCTION(_Screen, DrawTexture) if (!screen->HasBegun2D()) ThrowAbortException(X_OTHER, "Attempt to draw to screen outside a draw function"); FTexture *tex = animate ? TexMan(FSetTextureID(texid)) : TexMan[FSetTextureID(texid)]; - VMVa_List args = { param + 4, 0, numparam - 5, va_reginfo }; + VMVa_List args = { param + 4, 0, numparam - 5, va_reginfo + 4 }; screen->DrawTexture(tex, x, y, args); return 0; } @@ -232,7 +232,7 @@ DEFINE_ACTION_FUNCTION(_Screen, DrawShape) if (!screen->HasBegun2D()) ThrowAbortException(X_OTHER, "Attempt to draw to screen outside a draw function"); FTexture *tex = animate ? TexMan(FSetTextureID(texid)) : TexMan[FSetTextureID(texid)]; - VMVa_List args = { param + 3, 0, numparam - 4, va_reginfo }; + VMVa_List args = { param + 3, 0, numparam - 4, va_reginfo + 3 }; screen->DrawShape(tex, shape, args); return 0; diff --git a/src/v_text.cpp b/src/v_text.cpp index fe535b598..2eae3e9f3 100644 --- a/src/v_text.cpp +++ b/src/v_text.cpp @@ -194,7 +194,7 @@ DEFINE_ACTION_FUNCTION(_Screen, DrawChar) PARAM_VA_POINTER(va_reginfo) // Get the hidden type information array if (!screen->HasBegun2D()) ThrowAbortException(X_OTHER, "Attempt to draw to screen outside a draw function"); - VMVa_List args = { param + 5, 0, numparam - 6, va_reginfo }; + VMVa_List args = { param + 5, 0, numparam - 6, va_reginfo + 5 }; screen->DrawChar(font, cr, x, y, chr, args); return 0; } @@ -326,7 +326,7 @@ DEFINE_ACTION_FUNCTION(_Screen, DrawText) PARAM_VA_POINTER(va_reginfo) // Get the hidden type information array if (!screen->HasBegun2D()) ThrowAbortException(X_OTHER, "Attempt to draw to screen outside a draw function"); - VMVa_List args = { param + 5, 0, numparam - 6, va_reginfo }; + VMVa_List args = { param + 5, 0, numparam - 6, va_reginfo + 5 }; const char *txt = chr[0] == '$' ? GStrings(&chr[1]) : chr.GetChars(); screen->DrawText(font, cr, x, y, txt, args); return 0;