Permalink
Browse files

- 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.
  • Loading branch information...
coelckers committed Jan 10, 2019
1 parent 9506b0e commit 80427b72e9c4ad7857486678bbeaa1cddf8e72cf
@@ -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,28 +5138,82 @@ 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)
{
ExpEmit from = val->Emit(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);

0 comments on commit 80427b7

Please sign in to comment.