- make dynamic object casts a dedicated VM instruction instead of a builtin function.

This can see some heavy use in iterators where saving several hundreds of function calls can be achieved. In these cases, using a function to do the job will become a significant time waster.
This commit is contained in:
Christoph Oelckers 2016-12-05 14:52:34 +01:00
parent f722967abe
commit 092461ed34
6 changed files with 45 additions and 73 deletions

View file

@ -4324,58 +4324,32 @@ FxExpression *FxTypeCheck::Resolve(FCompileContext& ctx)
//
//==========================================================================
PPrototype *FxTypeCheck::ReturnProto()
ExpEmit FxTypeCheck::EmitCommon(VMFunctionBuilder *build)
{
EmitTail = true;
return FxExpression::ReturnProto();
ExpEmit castee = left->Emit(build);
ExpEmit casttype = right->Emit(build);
castee.Free(build);
casttype.Free(build);
ExpEmit ares(build, REGT_POINTER);
build->Emit(casttype.Konst ? OP_DYNCAST_K : OP_DYNCAST_R, ares.RegNum, castee.RegNum, casttype.RegNum);
return ares;
}
//==========================================================================
//
//
//
//==========================================================================
int BuiltinTypeCheck(VMValue *param, TArray<VMValue> &defaultparam, int numparam, VMReturn *ret, int numret)
{
assert(numparam == 2);
PARAM_POINTER_AT(0, obj, DObject);
PARAM_CLASS_AT(1, cls, DObject);
ACTION_RETURN_BOOL(obj && obj->IsKindOf(cls));
}
//==========================================================================
//
//
//
//==========================================================================
ExpEmit FxTypeCheck::Emit(VMFunctionBuilder *build)
{
EmitParameter(build, left, ScriptPosition);
EmitParameter(build, right, ScriptPosition);
ExpEmit ares = EmitCommon(build);
ares.Free(build);
ExpEmit bres(build, REGT_INT);
build->Emit(OP_CASTB, bres.RegNum, ares.RegNum, CASTB_A);
return bres;
}
PSymbol *sym = FindBuiltinFunction(NAME_BuiltinTypeCheck, BuiltinTypeCheck);
assert(sym->IsKindOf(RUNTIME_CLASS(PSymbolVMFunction)));
assert(((PSymbolVMFunction *)sym)->Function != nullptr);
auto callfunc = ((PSymbolVMFunction *)sym)->Function;
int opcode = (EmitTail ? OP_TAIL_K : OP_CALL_K);
build->Emit(opcode, build->GetConstantAddress(callfunc, ATAG_OBJECT), 2, 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;
void FxTypeCheck::EmitCompare(VMFunctionBuilder *build, bool invert, TArray<size_t> &patchspots_yes, TArray<size_t> &patchspots_no)
{
ExpEmit ares = EmitCommon(build);
ares.Free(build);
build->Emit(OP_EQA_K, !invert, ares.RegNum, build->GetConstantAddress(nullptr, ATAG_OBJECT));
patchspots_no.Push(build->Emit(OP_JMP, 0));
}
//==========================================================================
@ -4443,27 +4417,11 @@ FxExpression *FxDynamicCast::Resolve(FCompileContext& ctx)
ExpEmit FxDynamicCast::Emit(VMFunctionBuilder *build)
{
ExpEmit in = expr->Emit(build);
ExpEmit out = in.Fixed ? ExpEmit(build, in.RegType) : in;
ExpEmit check(build, REGT_INT);
assert(out.RegType == REGT_POINTER);
if (in.Fixed) build->Emit(OP_MOVEA, out.RegNum, in.RegNum);
build->Emit(OP_PARAM, 0, REGT_POINTER, in.RegNum);
build->Emit(OP_PARAM, 0, REGT_POINTER | REGT_KONST, build->GetConstantAddress(CastType, ATAG_OBJECT));
PSymbol *sym = FindBuiltinFunction(NAME_BuiltinTypeCheck, BuiltinTypeCheck);
assert(sym->IsKindOf(RUNTIME_CLASS(PSymbolVMFunction)));
assert(((PSymbolVMFunction *)sym)->Function != nullptr);
auto callfunc = ((PSymbolVMFunction *)sym)->Function;
build->Emit(OP_CALL_K, build->GetConstantAddress(callfunc, ATAG_OBJECT), 2, 1);
build->Emit(OP_RESULT, 0, REGT_INT, check.RegNum);
build->Emit(OP_EQ_K, 0, check.RegNum, build->GetConstantInt(0));
auto patch = build->Emit(OP_JMP, 0);
build->Emit(OP_LKP, out.RegNum, build->GetConstantAddress(nullptr, ATAG_OBJECT));
build->BackpatchToHere(patch);
return out;
ExpEmit castee = expr->Emit(build);
castee.Free(build);
ExpEmit ares(build, REGT_POINTER);
build->Emit(OP_DYNCAST_K, ares.RegNum, castee.RegNum, build->GetConstantAddress(CastType, ATAG_OBJECT));
return ares;
}
//==========================================================================
@ -9586,8 +9544,7 @@ int BuiltinNameToClass(VMValue *param, TArray<VMValue> &defaultparam, int numpar
if (!cls->IsDescendantOf(desttype))
{
// Let the caller check this. The message can be enabled for diagnostic purposes.
DPrintf(DMSG_SPAMMY, "class '%s' is not compatible with '%s'\n", clsname.GetChars(), desttype->TypeName.GetChars());
// Let the caller check this. Making this an error with a message is only taking away options from the user.
cls = nullptr;
}
ret->SetPointer(const_cast<PClass *>(cls), ATAG_OBJECT);

View file

@ -1078,9 +1078,10 @@ public:
FxTypeCheck(FxExpression*, FxExpression*);
~FxTypeCheck();
FxExpression *Resolve(FCompileContext&);
PPrototype *ReturnProto();
ExpEmit EmitCommon(VMFunctionBuilder *build);
ExpEmit Emit(VMFunctionBuilder *build);
void EmitCompare(VMFunctionBuilder *build, bool invert, TArray<size_t> &patchspots_yes, TArray<size_t> &patchspots_no);
};
//==========================================================================

View file

@ -414,6 +414,18 @@ begin:
reg.f[a+1] = reg.f[B+1];
reg.f[a+2] = reg.f[B+2];
NEXTOP;
OP(DYNCAST_R) :
ASSERTA(a); ASSERTA(B); ASSERTA(C);
b = B;
reg.a[a] = (reg.a[b] && ((DObject*)(reg.a[b]))->IsKindOf((PClass*)(reg.a[C]))) ? reg.a[b] : nullptr;
reg.atag[a] = ATAG_OBJECT;
NEXTOP;
OP(DYNCAST_K) :
ASSERTA(a); ASSERTA(B); ASSERTKA(C);
b = B;
reg.a[a] = (reg.a[b] && ((DObject*)(reg.a[b]))->IsKindOf((PClass*)(konsta[C].o))) ? reg.a[b] : nullptr;
reg.atag[a] = ATAG_OBJECT;
NEXTOP;
OP(CAST):
if (C == CAST_I2F)
{

View file

@ -84,6 +84,8 @@ xx(MOVEV2, mov2, RFRF, NOP, 0, 0), // fA = fB (2 elements)
xx(MOVEV3, mov3, RFRF, NOP, 0, 0), // fA = fB (3 elements)
xx(CAST, cast, CAST, NOP, 0, 0), // xA = xB, conversion specified by C
xx(CASTB, castb, CAST, NOP, 0, 0), // xA = !!xB, type specified by C
xx(DYNCAST_R, dyncast, RPRPRP, NOP, 0, 0), // aA = dyn_cast<aC>(aB);
xx(DYNCAST_K, dyncast, RPRPKP, NOP, 0, 0), // aA = dyn_cast<aKC>(aB);
// Control flow.
xx(TEST, test, RII16, NOP, 0, 0), // if (dA != BC) then pc++

View file

@ -193,7 +193,7 @@ extend class Actor
if (sv_killbossmonst)
{
int count; // Repeat until we have no more boss-spawned monsters.
ThinkerIterator it = ThinkerIterator.Create();
ThinkerIterator it = ThinkerIterator.Create("Actor");
do // (e.g. Pain Elementals can spawn more to kill upon death.)
{
Actor mo;

View file

@ -388,7 +388,7 @@ class Minotaur : Actor
// In case pain caused him to skip his fade in.
A_SetRenderStyle(1, STYLE_Normal);
MinotaurFriend mf = MinotaurFriend(self);
let mf = MinotaurFriend(self);
if (mf)
{
if (mf.StartTime >= 0 && (level.maptime - mf.StartTime) >= MAULATORTICS)
@ -501,7 +501,7 @@ class Minotaur : Actor
void A_MinotaurChase()
{
MinotaurFriend mf = MinotaurFriend(self);
let mf = MinotaurFriend(self);
if (!mf)
{
A_Chase();