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 7cd4455f0..aac8baeee 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. @@ -749,6 +749,7 @@ static void (*MBFCodePointerFactories[])(EmitterArray&, 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; @@ -776,17 +777,18 @@ 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(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); - int count = emitters.EmitParameters(&buildit); - buildit.Emit(OP_TAIL_K, buildit.GetConstantAddress(sym->Variants[0].Implementation), count, 0); + emitters.EmitCall(&buildit); + 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/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; } 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 94fe8c83f..6e0857c6e 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; @@ -4497,13 +4503,6 @@ DEFINE_ACTION_FUNCTION(AActor, A_ChangeCountFlags) return 0; } - -enum ERaise -{ - RF_TRANSFERFRIENDLINESS = 1, - RF_NOCHECKPOSITION = 2 -}; - //=========================================================================== // // A_RaiseMaster @@ -4514,10 +4513,9 @@ DEFINE_ACTION_FUNCTION(AActor, A_RaiseMaster) PARAM_SELF_PROLOGUE(AActor); PARAM_INT(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; } @@ -4535,12 +4533,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; @@ -4559,14 +4556,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); } } } @@ -4582,7 +4578,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_RaiseSelf) { PARAM_SELF_PROLOGUE(AActor); PARAM_INT(flags); - ACTION_RETURN_BOOL(P_Thing_Raise(self, NULL, (flags & RF_NOCHECKPOSITION))); + ACTION_RETURN_BOOL(P_Thing_Raise(self, self, flags)); } //=========================================================================== @@ -4596,7 +4592,7 @@ DEFINE_ACTION_FUNCTION(AActor, RaiseActor) PARAM_SELF_PROLOGUE(AActor); PARAM_OBJECT(other, AActor); PARAM_INT(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 2d48937e8..85d40081a 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_mobj.cpp b/src/p_mobj.cpp index af045be5b..b3eb29cf5 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 @@ -8285,13 +8277,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/p_things.cpp b/src/p_things.cpp index 568bfd2b8..8b38141d7 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); diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index 26e2ccd99..b13537762 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); @@ -5449,18 +5450,6 @@ FxRandom::~FxRandom() // //========================================================================== -PPrototype *FxRandom::ReturnProto() -{ - EmitTail = true; - return FxExpression::ReturnProto(); -} - -//========================================================================== -// -// -// -//========================================================================== - FxExpression *FxRandom::Resolve(FCompileContext &ctx) { CHECKRESOLVED(); @@ -5482,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); @@ -5499,34 +5488,21 @@ 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); 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; + FunctionCallEmitter emitters(callfunc); 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; - } - - ExpEmit out(build, REGT_INT); - build->Emit(OP_RESULT, 0, REGT_INT, out.RegNum); - return out; + emitters.AddReturn(REGT_INT); + return emitters.EmitCall(build); } //========================================================================== @@ -5620,21 +5596,20 @@ 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); callfunc = ((PSymbolVMFunction *)sym)->Function; - EmitterArray emitters; + FunctionCallEmitter emitters(callfunc); 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.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()); // Free the result register now. The simple code generation algorithm should @@ -5719,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); @@ -5740,33 +5715,20 @@ 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); 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; + FunctionCallEmitter emitters(callfunc); 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; - } - - ExpEmit out(build, REGT_FLOAT); - build->Emit(OP_RESULT, 0, REGT_FLOAT, out.RegNum); - return out; + emitters.AddReturn(REGT_FLOAT); + return emitters.EmitCall(build); } //========================================================================== @@ -5778,7 +5740,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 +5763,6 @@ FxRandom2::~FxRandom2() // //========================================================================== -PPrototype *FxRandom2::ReturnProto() -{ - EmitTail = true; - return FxExpression::ReturnProto(); -} - -//========================================================================== -// -// -// -//========================================================================== - FxExpression *FxRandom2::Resolve(FCompileContext &ctx) { CHECKRESOLVED(); @@ -5827,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); @@ -5845,32 +5794,19 @@ 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); 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; + FunctionCallEmitter emitters(callfunc); 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; - } - - ExpEmit out(build, REGT_INT); - build->Emit(OP_RESULT, 0, REGT_INT, out.RegNum); - return out; + emitters.AddReturn(REGT_INT); + return emitters.EmitCall(build); } //========================================================================== @@ -5881,7 +5817,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; @@ -5918,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) @@ -5931,25 +5866,17 @@ 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); 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; - + FunctionCallEmitter emitters(callfunc); emitters.AddParameterPointerConst(rng); emitters.AddParameter(build, seed); - int count = emitters.EmitParameters(build); - build->Emit(opcode, build->GetConstantAddress(callfunc), count, 0); - - ExpEmit call; - if (EmitTail) call.Final = true; - return call; + return emitters.EmitCall(build); } //========================================================================== @@ -6387,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); @@ -6636,7 +6563,6 @@ FxClassDefaults::FxClassDefaults(FxExpression *X, const FScriptPosition &pos) : FxExpression(EFX_ClassDefaults, pos) { obj = X; - EmitTail = false; } FxClassDefaults::~FxClassDefaults() @@ -8214,12 +8140,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()) @@ -8508,7 +8439,6 @@ FxActionSpecialCall::FxActionSpecialCall(FxExpression *self, int special, FArgum { ArgList.Push(new FxConstant(0, ScriptPosition)); } - EmitTail = false; } //========================================================================== @@ -8528,18 +8458,6 @@ FxActionSpecialCall::~FxActionSpecialCall() // //========================================================================== -PPrototype *FxActionSpecialCall::ReturnProto() -{ - EmitTail = true; - return FxExpression::ReturnProto(); -} - -//========================================================================== -// -// -// -//========================================================================== - FxExpression *FxActionSpecialCall::Resolve(FCompileContext& ctx) { CHECKRESOLVED(); @@ -8607,7 +8525,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); @@ -8625,12 +8543,18 @@ ExpEmit FxActionSpecialCall::Emit(VMFunctionBuilder *build) { unsigned i = 0; - EmitterArray 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 - - ExpEmit selfemit(Self->Emit(build)); - emitters.AddParameterPointer(selfemit.RegNum, selfemit.Konst); + emitters.AddParameter(build, Self); for (; i < ArgList.Size(); ++i) { @@ -8654,31 +8578,11 @@ ExpEmit FxActionSpecialCall::Emit(VMFunctionBuilder *build) } } } - // Call the BuiltinCallLineSpecial function to perform the desired special. - VMFunction *callfunc; - PSymbol *sym = FindBuiltinFunction(NAME_BuiltinCallLineSpecial, BuiltinCallLineSpecial); - - assert(sym->IsKindOf(RUNTIME_CLASS(PSymbolVMFunction))); - assert(((PSymbolVMFunction *)sym)->Function != nullptr); - callfunc = ((PSymbolVMFunction *)sym)->Function; 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); - build->Emit(OP_RESULT, 0, REGT_INT, dest.RegNum); - return dest; + emitters.AddReturn(REGT_INT); + return emitters.EmitCall(build); } //========================================================================== @@ -8717,7 +8621,7 @@ PPrototype *FxVMFunctionCall::ReturnProto() { if (hasStringArgs) return FxExpression::ReturnProto(); - EmitTail = true; + return Function->Variants[0].Proto; } @@ -9068,12 +8972,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(); @@ -9085,7 +8987,7 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build) bool staticcall = ((vmfunc->VarFlags & VARF_Final) || vmfunc->VirtualIndex == ~0u || NoVirtual); count = 0; - EmitterArray emitters; + FunctionCallEmitter emitters(vmfunc); // Emit code to pass implied parameters ExpEmit selfemit; if (Function->Variants[0].Flags & VARF_Method) @@ -9156,77 +9058,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 (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) - { // 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); + if (!staticcall) emitters.SetVirtualReg(selfemit.RegNum); + int resultcount = vmfunc->Proto->ReturnTypes.Size() == 0 ? 0 : MAX(AssignCount, 1); - 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) - { // 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); } //========================================================================== @@ -10737,10 +10580,9 @@ 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(pstr->mDestructor); emitters.AddParameter(reg, false); - int count = emitters.EmitParameters(build); - build->Emit(OP_CALL_K, build->GetConstantAddress(pstr->mDestructor), count, 0); + emitters.EmitCall(build); } // If we return nothing, use a regular RET opcode. @@ -10907,7 +10749,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); @@ -10932,25 +10774,21 @@ 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.AddParameterPointerConst(const_cast(desttype)); // 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); 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; + FunctionCallEmitter emitters(callfunc); + emitters.AddParameter(build, basex); + emitters.AddParameterPointerConst(const_cast(desttype)); + emitters.AddReturn(REGT_POINTER); + return emitters.EmitCall(build); } //========================================================================== @@ -11033,7 +10871,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); @@ -11045,22 +10883,20 @@ ExpEmit FxClassPtrCast::Emit(VMFunctionBuilder *build) { ExpEmit clsname = basex->Emit(build); - EmitterArray emitters; - - emitters.AddParameter(clsname, false); - 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); 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; + + FunctionCallEmitter emitters(callfunc); + emitters.AddParameter(clsname, false); + emitters.AddParameterPointerConst(desttype); + + emitters.AddReturn(REGT_POINTER); + return emitters.EmitCall(build); } //========================================================================== @@ -11433,10 +11269,9 @@ 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(pstr->mConstructor); emitters.AddParameter(reg, false); - int count = emitters.EmitParameters(build); - build->Emit(OP_CALL_K, build->GetConstantAddress(pstr->mConstructor), count, 0); + emitters.EmitCall(build); } if (pstr->mDestructor != nullptr) build->ConstructedStructs.Push(this); } @@ -11460,10 +11295,9 @@ 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(pstr->mDestructor); emitters.AddParameter(reg, false); - int count = emitters.EmitParameters(build); - build->Emit(OP_CALL_K, build->GetConstantAddress(pstr->mDestructor), count, 0); + 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 9e93ec6f3..3876f9626 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 &); @@ -515,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) @@ -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..d1566730d 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); } @@ -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(); @@ -931,7 +932,7 @@ void FFunctionBuildList::DumpJit() } -void EmitterArray::AddParameter(VMFunctionBuilder *build, FxExpression *operand) +void FunctionCallEmitter::AddParameter(VMFunctionBuilder *build, FxExpression *operand) { ExpEmit where = operand->Emit(build); @@ -940,6 +941,8 @@ void EmitterArray::AddParameter(VMFunctionBuilder *build, FxExpression *operand) 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 { @@ -958,9 +961,14 @@ void EmitterArray::AddParameter(VMFunctionBuilder *build, FxExpression *operand) }); } -void EmitterArray::AddParameter(ExpEmit &emit, bool reference) +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); @@ -970,9 +978,11 @@ void EmitterArray::AddParameter(ExpEmit &emit, bool reference) }); } -void EmitterArray::AddParameterPointerConst(void *konst) +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)); @@ -980,9 +990,11 @@ void EmitterArray::AddParameterPointerConst(void *konst) }); } -void EmitterArray::AddParameterPointer(int index, bool 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); @@ -990,9 +1002,11 @@ void EmitterArray::AddParameterPointer(int index, bool konst) }); } -void EmitterArray::AddParameterFloatConst(double 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)); @@ -1000,9 +1014,11 @@ void EmitterArray::AddParameterFloatConst(double konst) }); } -void EmitterArray::AddParameterIntConst(int 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. @@ -1018,9 +1034,11 @@ void EmitterArray::AddParameterIntConst(int konst) }); } -void EmitterArray::AddParameterStringConst(const FString &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)); @@ -1028,7 +1046,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,5 +1054,37 @@ int EmitterArray::EmitParameters(VMFunctionBuilder *build) paramcount += func(build); } assert(paramcount == numparams); - return paramcount; + 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) + { + 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 eb28f20d9..28b6798d5 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; @@ -163,13 +164,27 @@ 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; - unsigned numparams = 0; + 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); @@ -177,7 +192,11 @@ public: void AddParameterFloatConst(double konst); void AddParameterIntConst(int konst); void AddParameterStringConst(const FString &konst); - int EmitParameters(VMFunctionBuilder *build); + ExpEmit EmitCall(VMFunctionBuilder *build, TArray *ReturnRegs = nullptr); + void AddReturn(int regtype, int regcount = 1) + { + returns.Push({ regtype, regcount }); + } unsigned Count() const { return numparams; 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/thingdef_data.cpp b/src/scripting/thingdef_data.cpp index 6d62e7081..019c10b3a 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) { @@ -1023,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. @@ -1077,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; @@ -1093,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; @@ -1115,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; @@ -1139,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; @@ -1184,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); } @@ -1192,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.cpp b/src/scripting/vm/jit.cpp index 6c90b5af3..e05a4881f 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(); @@ -433,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; @@ -463,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; @@ -489,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(); @@ -507,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; @@ -589,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 f8130437b..444bc04b8 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,50 +18,79 @@ 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]); + 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::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) +void JitCompiler::EmitDoCall(asmjit::X86Gp vmfunc, VMFunction *target) { using namespace asmjit; - if (NumParam < B) + bool simpleFrameTarget = false; + if (target && (target->VarFlags & VARF_Native)) + { + VMScriptFunction *starget = static_cast(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"); - 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,7 +100,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()); @@ -181,103 +108,115 @@ 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)); } -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) +int JitCompiler::StoreCallParams(bool simpleFrameTarget) { using namespace asmjit; - auto scriptcall = newTempIntPtr(); - cc.mov(scriptcall, x86::ptr(vmfunc, offsetof(VMScriptFunction, ScriptCall))); + X86Gp stackPtr = newTempIntPtr(); + X86Gp tmp = newTempIntPtr(); + X86Xmm tmp2 = newTempXmmSd(); - 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; - - for (unsigned int i = ParamOpcodes.Size() - b; i < ParamOpcodes.Size(); i++) + 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); + 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); + break; + case REGT_INT: + cc.mov(x86::dword_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, i)), regD[bc]); + 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); + break; + case REGT_INT | REGT_KONST: + cc.mov(x86::dword_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, i)), konstd[bc]); + break; + case REGT_STRING: + cc.mov(x86::ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, sp)), regS[bc]); 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]); + 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); + break; + case REGT_POINTER: + cc.mov(x86::ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, a)), regA[bc]); 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); + 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); + break; + case REGT_FLOAT: + cc.movsd(x86::qword_ptr(vmframe, offsetParams + slot * sizeof(VMValue) + myoffsetof(VMValue, f)), regF[bc]); + 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]); + } + 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]); + } + 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); 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); + 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)) @@ -377,7 +316,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(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/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/jit_load.cpp b/src/scripting/vm/jit_load.cpp index 908ff144e..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)); } @@ -84,8 +85,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 +94,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 +255,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); diff --git a/src/scripting/vm/jitintern.h b/src/scripting/vm/jitintern.h index a34488054..59004cc39 100644 --- a/src/scripting/vm/jitintern.h +++ b/src/scripting/vm/jitintern.h @@ -52,14 +52,12 @@ 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); - void EmitDoTail(asmjit::X86Gp ptr); - void EmitScriptTailCall(asmjit::X86Gp vmfunc, asmjit::X86Gp result, asmjit::X86Gp paramsptr); - - void StoreInOuts(int b); - void LoadInOuts(int b); + int StoreCallParams(bool simpleFrameTarget); + void LoadInOuts(); void LoadReturns(const VMOP *retval, int numret); void FillReturns(const VMOP *retval, int numret); void LoadCallResult(int type, int regnum, bool addrof); @@ -239,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; diff --git a/src/scripting/vm/vm.h b/src/scripting/vm/vm.h index ab1fdad71..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) { @@ -319,11 +393,12 @@ 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; - TArray DefaultArgs; + const uint8_t *RegTypes = nullptr; + TArray DefaultArgs; FString PrintableName; // so that the VM can print meaningful info if something in this function goes wrong. class PPrototype *Proto; @@ -352,14 +427,34 @@ public: } AllFunctions.Clear(); } + static void CreateRegUseInfo() + { + for (auto f : AllFunctions) + { + f->CreateRegUse(); + } + } static TArray AllFunctions; 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; } @@ -381,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); @@ -395,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. @@ -445,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 { @@ -582,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 5b58180a8..0d0e2125f 100644 --- a/src/scripting/vm/vmexec.cpp +++ b/src/scripting/vm/vmexec.cpp @@ -193,17 +193,19 @@ 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]; + if (*reginfo < REGT_STRING) { - if (p.Type == REGT_INT) + if (*reginfo == REGT_INT) { calleereg.d[regd++] = p.i; } @@ -212,13 +214,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/vmexec.h b/src/scripting/vm/vmexec.h index e7735c5cb..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) @@ -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/vmframe.cpp b/src/scripting/vm/vmframe.cpp index 0a69c8c2b..e912cdad0 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; @@ -260,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; @@ -535,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 { @@ -694,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/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 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..502e14a29 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 + 4 }; 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 + 3 }; 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..2eae3e9f3 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 + 5 }; 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 + 5 }; 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 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