mirror of
https://github.com/ZDoom/qzdoom-gpl.git
synced 2024-11-26 22:01:13 +00:00
- 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:
parent
0c686c593b
commit
e2f3a09dd0
5 changed files with 33 additions and 35 deletions
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
}
|
|
@ -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);
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue