mirror of
https://github.com/ZDoom/qzdoom.git
synced 2025-03-17 08:21:28 +00:00
Merge branch 'asmjit' of https://github.com/coelckers/gzdoom
This commit is contained in:
commit
b59f267a8b
32 changed files with 649 additions and 703 deletions
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<AActor> 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<AActor> 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));
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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__
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<PSymbolVMFunction>(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<PSymbolConstNumeric*>(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<PObjectPointer*>(elementType)->PointedClass()->IsAncestorOf(static_cast<PObjectPointer*>(a->ValueType)->PointedClass()))
|
||||
// Null pointers are always valid.
|
||||
if (!a->isConstant() || static_cast<FxConstant*>(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<PObjectPointer*>(elementType)->PointedClass()->IsAncestorOf(static_cast<PObjectPointer*>(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<PClass *>(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<PClass *>(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));
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<ExpEmit> *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();
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "dobject.h"
|
||||
#include "vmintern.h"
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
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<std::function<int(VMFunctionBuilder *)>> emitters;
|
||||
unsigned numparams = 0;
|
||||
TArray<std::pair<int, int>> returns;
|
||||
TArray<uint8_t> 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<ExpEmit> *ReturnRegs = nullptr);
|
||||
void AddReturn(int regtype, int regcount = 1)
|
||||
{
|
||||
returns.Push({ regtype, regcount });
|
||||
}
|
||||
unsigned Count() const
|
||||
{
|
||||
return numparams;
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)));
|
||||
|
|
|
@ -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<VMFunction*, DObject*, int>(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<VMFunction*>(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<VMScriptFunction*>(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<int, VMFunction *, VMValue*, int, VMReturn*, int>());
|
||||
|
@ -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<int, VMFunction *, VMValue*, int, VMReturn*, int>());
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<VMFunction*, DObject*, int>(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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<const VMOP *> 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;
|
||||
|
|
|
@ -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<VMValue> DefaultArgs;
|
||||
const uint8_t *RegTypes = nullptr;
|
||||
TArray<TypedVMValue> 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<VMFunction *> 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 <x> at position <p>
|
||||
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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -697,7 +697,7 @@ static int ExecScriptFunc(VMFrameStack *stack, VMReturn *ret, int numret)
|
|||
try
|
||||
{
|
||||
VMCycles[0].Unclock();
|
||||
numret = static_cast<VMNativeFunction *>(call)->NativeCall(reg.param + f->NumParam - b, b, returns, C);
|
||||
numret = static_cast<VMNativeFunction *>(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<VMNativeFunction *>(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<VMScriptFunction *>(call);
|
||||
return sfunc->ScriptCall(sfunc, reg.param + f->NumParam - B, B, ret, numret);
|
||||
}
|
||||
}
|
||||
NEXTOP;
|
||||
OP(RET):
|
||||
if (B == REGT_NIL)
|
||||
{ // No return values
|
||||
|
|
|
@ -65,6 +65,42 @@ IMPLEMENT_CLASS(VMException, false, false)
|
|||
|
||||
TArray<VMFunction *> 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<VMNativeFunction *>(func)->NativeCall(params, numparams, returns, numret);
|
||||
numret = static_cast<VMNativeFunction *>(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<VMNativeFunction *>(func)->NativeCall(params, numparams, results, numresults);
|
||||
return static_cast<VMNativeFunction *>(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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -2287,7 +2287,7 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool
|
|||
TArray<PType *> rets(1);
|
||||
TArray<PType *> args;
|
||||
TArray<uint32_t> argflags;
|
||||
TArray<VMValue> argdefaults;
|
||||
TArray<TypedVMValue> argdefaults;
|
||||
TArray<FName> 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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -287,6 +287,7 @@ struct VMVa_List
|
|||
VMValue *args;
|
||||
int curindex;
|
||||
int numargs;
|
||||
const uint8_t *reginfo;
|
||||
};
|
||||
//
|
||||
// VIDEO
|
||||
|
|
|
@ -34,6 +34,7 @@ struct SectorPortal native play
|
|||
struct Vertex native play
|
||||
{
|
||||
native readonly Vector2 p;
|
||||
native int Index();
|
||||
}
|
||||
|
||||
struct Side native play
|
||||
|
|
Loading…
Reference in a new issue