- 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
commit 4126f8ce72
6 changed files with 64 additions and 113 deletions

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