- 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:
Christoph Oelckers 2019-01-10 02:12:43 +01:00
parent 9506b0e337
commit 80427b72e9
6 changed files with 64 additions and 113 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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