This commit is contained in:
Rachael Alexanderson 2018-11-18 17:11:52 -05:00
commit b59f267a8b
32 changed files with 649 additions and 703 deletions

View file

@ -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;
}

View file

@ -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;

View file

@ -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)

View file

@ -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;
}

View file

@ -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;

View file

@ -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));
}
//===========================================================================

View file

@ -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;

View file

@ -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__

View file

@ -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)
{

View file

@ -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);

View file

@ -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);

View file

@ -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));
}

View file

@ -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;

View file

@ -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();
}

View file

@ -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;

View file

@ -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());
}

View file

@ -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;
}

View file

@ -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)));

View file

@ -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 &param = *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);
}
}

View file

@ -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

View file

@ -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);

View file

@ -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;

View file

@ -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);

View file

@ -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;
}
}

View file

@ -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

View file

@ -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;
}

View file

@ -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

View file

@ -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);

View file

@ -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;
}

View file

@ -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;

View file

@ -287,6 +287,7 @@ struct VMVa_List
VMValue *args;
int curindex;
int numargs;
const uint8_t *reginfo;
};
//
// VIDEO

View file

@ -34,6 +34,7 @@ struct SectorPortal native play
struct Vertex native play
{
native readonly Vector2 p;
native int Index();
}
struct Side native play