- refactored the scope check for VirtualScope calls.

It now uses a dedicated opcode instead of piggybacking on OP_CALL and it passes data that is closer to the VM. Symbols should be avoided at this level.
It also will skip the scope instruction if the code generator detects that both calling function and the self pointer type have the same scope, this assumes that subclasses cannot flip between UI and Play.
This commit is contained in:
Christoph Oelckers 2017-03-07 22:17:48 +01:00
parent 0c686c593b
commit e2f3a09dd0
5 changed files with 33 additions and 35 deletions

View file

@ -8792,20 +8792,27 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build)
ExpEmit selfemit;
if (Function->Variants[0].Flags & VARF_Method)
{
#if 0
// [ZZ]
if (Function->Variants[0].Implementation && Function->Variants[0].Implementation->BarrierSide == FScopeBarrier::Side_Virtual)
{
// pass this even before Self, because otherwise we can't silently advance the arguments.
// this is not even implicit arguments.
build->Emit(OP_PARAM, 0, REGT_POINTER | REGT_KONST, build->GetConstantAddress(Function, ATAG_OBJECT));
build->Emit(OP_PARAM, 0, REGT_POINTER | REGT_KONST, build->GetConstantAddress(CallingFunction, ATAG_OBJECT));
count += 2;
}
#endif
assert(Self != nullptr);
selfemit = Self->Emit(build);
assert((selfemit.RegType == REGT_POINTER) || (selfemit.Fixed && selfemit.Target));
int innerside = FScopeBarrier::SideFromFlags(Function->Variants[0].Flags);
if (innerside == FScopeBarrier::Side_Virtual)
{
auto selfside = FScopeBarrier::SideFromObjectFlags(static_cast<PPointer*>(Self->ValueType)->PointedType->ObjectFlags);
int outerside = FScopeBarrier::SideFromFlags(CallingFunction->Variants[0].Flags);
if (outerside == FScopeBarrier::Side_Virtual)
outerside = FScopeBarrier::SideFromObjectFlags(CallingFunction->OwningClass->ObjectFlags);
if (selfside != outerside && (selfside == FScopeBarrier::Side_Play || selfside == FScopeBarrier::Side_UI)) // if the self pointer and the calling functions have the same scope the check here is not needed.
{
// Check the self object against the calling function's flags at run time
build->Emit(OP_SCOPE, selfemit.RegNum, outerside + 1, build->GetConstantAddress(vmfunc, ATAG_OBJECT));
}
}
if (selfemit.Fixed && selfemit.Target)
{
// Address of a local variable.
@ -8870,6 +8877,7 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build)
{
selfemit.Free(build);
ExpEmit funcreg(build, REGT_POINTER);
build->Emit(OP_VTBL, funcreg.RegNum, selfemit.RegNum, vmfunc->VirtualIndex);
if (EmitTail)
{ // Tail call

View file

@ -179,16 +179,10 @@ void FScopeBarrier::ValidateNew(PClass* cls, int outerside)
if ((outerside != innerside) && (innerside != FScopeBarrier::Side_PlainData)) // "cannot construct ui class ... from data context"
ThrowAbortException(X_OTHER, "Cannot construct %s class %s from %s context", FScopeBarrier::StringFromSide(innerside), cls->TypeName.GetChars(), FScopeBarrier::StringFromSide(outerside));
}
// this can be imported in vmexec.h
void FScopeBarrier::ValidateCall(PFunction* calledfunc, PFunction* callingfunc, PClass* selftype)
void FScopeBarrier::ValidateCall(PClass* selftype, VMFunction *calledfunc, int outerside)
{
// [ZZ] anonymous blocks have 0 variants, so give them Side_Virtual.
int outerside = callingfunc->Variants.Size() ? FScopeBarrier::SideFromFlags(callingfunc->Variants[0].Flags) : FScopeBarrier::Side_Virtual;
if (outerside == FScopeBarrier::Side_Virtual)
outerside = FScopeBarrier::SideFromObjectFlags(callingfunc->OwningClass->ObjectFlags);
int innerside = FScopeBarrier::SideFromFlags(calledfunc->Variants[0].Flags);
if (innerside == FScopeBarrier::Side_Virtual)
innerside = FScopeBarrier::SideFromObjectFlags(selftype->ObjectFlags);
int innerside = FScopeBarrier::SideFromObjectFlags(selftype->ObjectFlags);
if ((outerside != innerside) && (innerside != FScopeBarrier::Side_PlainData))
ThrowAbortException(X_OTHER, "Cannot call %s function %s from %s context", FScopeBarrier::StringFromSide(innerside), calledfunc->SymbolName.GetChars(), FScopeBarrier::StringFromSide(outerside));
ThrowAbortException(X_OTHER, "Cannot call %s function %s from %s context", FScopeBarrier::StringFromSide(innerside), calledfunc->PrintableName.GetChars(), FScopeBarrier::StringFromSide(outerside));
}

View file

@ -54,6 +54,7 @@ struct FScopeBarrier
// this is called from vmexec.h
static void ValidateNew(PClass* cls, int scope);
static void ValidateCall(PFunction* calledfunc, PFunction* callingfunc, PClass* selftype);
static void ValidateCall(PClass* selftype, VMFunction *calledfunc, int outerside);
};

View file

@ -648,6 +648,13 @@ begin:
reg.a[a] = p->Virtuals[C];
}
NEXTOP;
OP(SCOPE) :
{
ASSERTA(a); ASSERTA(C);
FScopeBarrier::ValidateCall(((DObject*)a)->GetClass(), (VMFunction*)C, B);
}
NEXTOP;
OP(CALL_K):
ASSERTKA(a);
assert(konstatag[a] == ATAG_OBJECT);
@ -665,19 +672,6 @@ begin:
int numret;
b = B;
#if 0
// [ZZ] hax!
if (call->BarrierSide == 3) // :( - this is Side_Virtual. Side_Virtual should receive special arguments.
{
PFunction* calledfunc = (PFunction*)(reg.param + f->NumParam - b)[0].a;
PFunction* callingfunc = (PFunction*)(reg.param + f->NumParam - b)[1].a;
DObject* dobj = (DObject*)(reg.param + f->NumParam - b)[2].a; // this is the self pointer. it should be in, since Side_Virtual functions are always non-static methods.
PClass* selftype = dobj->GetClass();
FScopeBarrier::ValidateCall(calledfunc, callingfunc, selftype);
b -= 2;
}
#endif
FillReturns(reg, f, returns, pc+1, C);
if (call->VarFlags & VARF_Native)
{

View file

@ -106,6 +106,7 @@ xx(PARAMI, parami, I24, NOP, 0, 0), // push immediate, signed integer for func
xx(CALL, call, RPI8I8, NOP, 0, 0), // Call function pkA with parameter count B and expected result count C
xx(CALL_K, call, KPI8I8, CALL, 1, REGT_POINTER),
xx(VTBL, vtbl, RPRPI8, NOP, 0, 0), // dereferences a virtual method table.
xx(SCOPE, scope, RPI8, NOP, 0, 0), // Scope check at runtime.
xx(TAIL, tail, RPI8, NOP, 0, 0), // Call+Ret in a single instruction
xx(TAIL_K, tail, KPI8, TAIL, 1, REGT_POINTER),
xx(RESULT, result, __BCP, NOP, 0, 0), // Result should go in register encoded in BC (in caller, after CALL)