mirror of
https://github.com/ZDoom/gzdoom.git
synced 2024-11-29 23:33:00 +00:00
- made OP_NEW a builtin function instead of an opcode.
The code was present 3 times due to the JIt, and this is not something that benefits from being a real opcode, even in the interpreted case.
This commit is contained in:
parent
9506b0e337
commit
80427b72e9
6 changed files with 64 additions and 113 deletions
|
@ -7,6 +7,7 @@ xx(Super)
|
|||
xx(Object)
|
||||
xx(Actor)
|
||||
xx(Class)
|
||||
xx(Thinker)
|
||||
|
||||
xx(Untranslated)
|
||||
|
||||
|
@ -343,6 +344,7 @@ xx(RandomPick)
|
|||
xx(FRandomPick)
|
||||
xx(SetRandomSeed)
|
||||
xx(BuiltinRandomSeed)
|
||||
xx(BuiltinNew)
|
||||
xx(GetClass)
|
||||
xx(GetParentClass)
|
||||
xx(GetClassName)
|
||||
|
|
|
@ -5138,9 +5138,54 @@ FxExpression *FxNew::Resolve(FCompileContext &ctx)
|
|||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
// The CVAR is for finding places where thinkers are created.
|
||||
// Those will require code changes in ZScript 4.0.
|
||||
//
|
||||
//==========================================================================
|
||||
CVAR(Bool, vm_warnthinkercreation, false, 0)
|
||||
|
||||
static DObject *BuiltinNew(PClass *cls, int outerside, int backwardscompatible)
|
||||
{
|
||||
if (cls->ConstructNative == nullptr)
|
||||
{
|
||||
ThrowAbortException(X_OTHER, "Class %s requires native construction", cls->TypeName.GetChars());
|
||||
return nullptr;
|
||||
}
|
||||
if (cls->bAbstract)
|
||||
{
|
||||
ThrowAbortException(X_OTHER, "Cannot instantiate abstract class %s", cls->TypeName.GetChars());
|
||||
return nullptr;
|
||||
}
|
||||
// Creating actors here must be outright prohibited,
|
||||
if (cls->IsDescendantOf(NAME_Actor))
|
||||
{
|
||||
ThrowAbortException(X_OTHER, "Cannot create actors with 'new'");
|
||||
return nullptr;
|
||||
}
|
||||
if (vm_warnthinkercreation && cls->IsDescendantOf(NAME_Thinker))
|
||||
{
|
||||
// This must output a diagnostic warning
|
||||
//ThrowAbortException(X_OTHER, "Cannot create actors with 'new'");
|
||||
//return nullptr;
|
||||
}
|
||||
// [ZZ] validate readonly and between scope construction
|
||||
if (outerside) FScopeBarrier::ValidateNew(cls, outerside - 1);
|
||||
auto object = cls->CreateNew();
|
||||
if (backwardscompatible && object->IsKindOf(NAME_Thinker))
|
||||
{
|
||||
// Todo: Link thinker to current primary level.
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(DObject, BuiltinNew, BuiltinNew)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_CLASS(cls, DObject);
|
||||
PARAM_INT(outerside);
|
||||
PARAM_INT(compatible);
|
||||
ACTION_RETURN_OBJECT(BuiltinNew(cls, outerside, compatible));
|
||||
}
|
||||
|
||||
ExpEmit FxNew::Emit(VMFunctionBuilder *build)
|
||||
{
|
||||
|
@ -5148,18 +5193,27 @@ ExpEmit FxNew::Emit(VMFunctionBuilder *build)
|
|||
from.Free(build);
|
||||
ExpEmit to(build, REGT_POINTER);
|
||||
|
||||
// Call DecoRandom to generate a random number.
|
||||
VMFunction *callfunc;
|
||||
auto sym = FindBuiltinFunction(NAME_BuiltinNew);
|
||||
|
||||
assert(sym);
|
||||
callfunc = sym->Variants[0].Implementation;
|
||||
|
||||
FunctionCallEmitter emitters(callfunc);
|
||||
|
||||
int outerside = -1;
|
||||
if (!from.Konst)
|
||||
{
|
||||
int outerside = FScopeBarrier::SideFromFlags(CallingFunction->Variants[0].Flags);
|
||||
if (outerside == FScopeBarrier::Side_Virtual)
|
||||
outerside = FScopeBarrier::SideFromObjectFlags(CallingFunction->OwningClass->ScopeFlags);
|
||||
build->Emit(OP_NEW, to.RegNum, from.RegNum, outerside+1); // +1 to ensure it's not 0
|
||||
}
|
||||
else
|
||||
{
|
||||
build->Emit(OP_NEW_K, to.RegNum, from.RegNum);
|
||||
}
|
||||
return to;
|
||||
emitters.AddParameter(from, false);
|
||||
emitters.AddParameterIntConst(outerside);
|
||||
emitters.AddParameterIntConst(1); // Todo: 1 only if version < 4.0.0
|
||||
emitters.AddReturn(REGT_POINTER);
|
||||
return emitters.EmitCall(build);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
|
|
@ -244,82 +244,6 @@ void JitCompiler::EmitRETI()
|
|||
}
|
||||
}
|
||||
|
||||
static DObject* CreateNew(PClass *cls, int c)
|
||||
{
|
||||
if (!cls->ConstructNative)
|
||||
{
|
||||
ThrowAbortException(X_OTHER, "Class %s requires native construction", cls->TypeName.GetChars());
|
||||
}
|
||||
else if (cls->bAbstract)
|
||||
{
|
||||
ThrowAbortException(X_OTHER, "Cannot instantiate abstract class %s", cls->TypeName.GetChars());
|
||||
}
|
||||
else if (cls->IsDescendantOf(NAME_Actor)) // Creating actors here must be outright prohibited
|
||||
{
|
||||
ThrowAbortException(X_OTHER, "Cannot create actors with 'new'");
|
||||
}
|
||||
|
||||
// [ZZ] validate readonly and between scope construction
|
||||
if (c) FScopeBarrier::ValidateNew(cls, c - 1);
|
||||
return cls->CreateNew();
|
||||
}
|
||||
|
||||
void JitCompiler::EmitNEW()
|
||||
{
|
||||
auto result = newResultIntPtr();
|
||||
auto call = CreateCall<DObject*, PClass*, int>(CreateNew);
|
||||
call->setRet(0, result);
|
||||
call->setArg(0, regA[B]);
|
||||
call->setArg(1, asmjit::Imm(C));
|
||||
|
||||
cc.mov(regA[A], result);
|
||||
}
|
||||
|
||||
static void ThrowNewK(PClass *cls, int c)
|
||||
{
|
||||
if (!cls->ConstructNative)
|
||||
{
|
||||
ThrowAbortException(X_OTHER, "Class %s requires native construction", cls->TypeName.GetChars());
|
||||
}
|
||||
else if (cls->bAbstract)
|
||||
{
|
||||
ThrowAbortException(X_OTHER, "Cannot instantiate abstract class %s", cls->TypeName.GetChars());
|
||||
}
|
||||
else // if (cls->IsDescendantOf(NAME_Actor)) // Creating actors here must be outright prohibited
|
||||
{
|
||||
ThrowAbortException(X_OTHER, "Cannot create actors with 'new'");
|
||||
}
|
||||
}
|
||||
|
||||
static DObject *CreateNewK(PClass *cls, int c)
|
||||
{
|
||||
if (c) FScopeBarrier::ValidateNew(cls, c - 1);
|
||||
return cls->CreateNew();
|
||||
}
|
||||
|
||||
void JitCompiler::EmitNEW_K()
|
||||
{
|
||||
PClass *cls = (PClass*)konsta[B].v;
|
||||
auto regcls = newTempIntPtr();
|
||||
cc.mov(regcls, asmjit::imm_ptr(cls));
|
||||
|
||||
if (!cls->ConstructNative || cls->bAbstract || cls->IsDescendantOf(NAME_Actor))
|
||||
{
|
||||
auto call = CreateCall<void, PClass*, int>(ThrowNewK);
|
||||
call->setArg(0, regcls);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto result = newResultIntPtr();
|
||||
auto call = CreateCall<DObject*, PClass*, int>(CreateNewK);
|
||||
call->setRet(0, result);
|
||||
call->setArg(0, regcls);
|
||||
call->setArg(1, asmjit::Imm(C));
|
||||
|
||||
cc.mov(regA[A], result);
|
||||
}
|
||||
}
|
||||
|
||||
void JitCompiler::EmitTHROW()
|
||||
{
|
||||
EmitThrowException(EVMAbortException(BC));
|
||||
|
|
|
@ -757,34 +757,6 @@ static int ExecScriptFunc(VMFrameStack *stack, VMReturn *ret, int numret)
|
|||
assert(0);
|
||||
NEXTOP;
|
||||
|
||||
OP(NEW_K):
|
||||
OP(NEW):
|
||||
{
|
||||
b = B;
|
||||
PClass *cls = (PClass*)(pc->op == OP_NEW ? reg.a[b] : konsta[b].v);
|
||||
if (cls->ConstructNative == nullptr)
|
||||
{
|
||||
ThrowAbortException(X_OTHER, "Class %s requires native construction", cls->TypeName.GetChars());
|
||||
return 0;
|
||||
}
|
||||
if (cls->bAbstract)
|
||||
{
|
||||
ThrowAbortException(X_OTHER, "Cannot instantiate abstract class %s", cls->TypeName.GetChars());
|
||||
return 0;
|
||||
}
|
||||
// Creating actors here must be outright prohibited,
|
||||
if (cls->IsDescendantOf(NAME_Actor))
|
||||
{
|
||||
ThrowAbortException(X_OTHER, "Cannot create actors with 'new'");
|
||||
return 0;
|
||||
}
|
||||
// [ZZ] validate readonly and between scope construction
|
||||
c = C;
|
||||
if (c) FScopeBarrier::ValidateNew(cls, c - 1);
|
||||
reg.a[a] = cls->CreateNew();
|
||||
NEXTOP;
|
||||
}
|
||||
|
||||
#if 0
|
||||
OP(TRY):
|
||||
assert(try_depth < MAX_TRY_DEPTH);
|
||||
|
|
|
@ -108,8 +108,6 @@ xx(SCOPE, scope, RPI8, NOP, 0, 0) // Scope check at runtime.
|
|||
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
|
||||
xx(NEW, new, RPRPI8, NOP, 0, 0)
|
||||
xx(NEW_K, new, RPKP, NOP, 0, 0)
|
||||
//xx(TRY, try, I24, NOP, 0, 0) // When an exception is thrown, start searching for a handler at pc + ABC
|
||||
//xx(UNTRY, untry, I8, NOP, 0, 0) // Pop A entries off the exception stack
|
||||
xx(THROW, throw, THROW, NOP, 0, 0) // A == 0: Throw exception object pB
|
||||
|
|
|
@ -385,6 +385,7 @@ class Object native
|
|||
native bool bDestroyed;
|
||||
|
||||
// These must be defined in some class, so that the compiler can find them. Object is just fine, as long as they are private to external code.
|
||||
private native static Object BuiltinNew(Class<Object> cls, int outerclass, int compatibility);
|
||||
private native static int BuiltinRandom(voidptr rng, int min, int max);
|
||||
private native static double BuiltinFRandom(voidptr rng, double min, double max);
|
||||
private native static int BuiltinRandom2(voidptr rng, int mask);
|
||||
|
|
Loading…
Reference in a new issue