From e2f3a09dd0bd628212123d8b7f7306f7d8f94dd7 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 7 Mar 2017 22:17:48 +0100 Subject: [PATCH] - 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. --- src/scripting/backend/codegen.cpp | 30 ++++++++++++++++---------- src/scripting/backend/scopebarrier.cpp | 14 ++++-------- src/scripting/backend/scopebarrier.h | 3 ++- src/scripting/vm/vmexec.h | 20 ++++++----------- src/scripting/vm/vmops.h | 1 + 5 files changed, 33 insertions(+), 35 deletions(-) diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index addc07f4b..c0a69eb6e 100644 --- a/src/scripting/backend/codegen.cpp +++ b/src/scripting/backend/codegen.cpp @@ -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(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 diff --git a/src/scripting/backend/scopebarrier.cpp b/src/scripting/backend/scopebarrier.cpp index 3ba207fba..29caa2457 100644 --- a/src/scripting/backend/scopebarrier.cpp +++ b/src/scripting/backend/scopebarrier.cpp @@ -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)); } \ No newline at end of file diff --git a/src/scripting/backend/scopebarrier.h b/src/scripting/backend/scopebarrier.h index af3951a8d..da0bc6d1d 100644 --- a/src/scripting/backend/scopebarrier.h +++ b/src/scripting/backend/scopebarrier.h @@ -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); + }; diff --git a/src/scripting/vm/vmexec.h b/src/scripting/vm/vmexec.h index a822aef53..aec532c37 100644 --- a/src/scripting/vm/vmexec.h +++ b/src/scripting/vm/vmexec.h @@ -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) { diff --git a/src/scripting/vm/vmops.h b/src/scripting/vm/vmops.h index b9cba87d2..18a444677 100644 --- a/src/scripting/vm/vmops.h +++ b/src/scripting/vm/vmops.h @@ -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)