summaryrefslogtreecommitdiff
path: root/packaging/0025-Remove-relocations-for-MethodTable-s-vtable-1st-leve.patch
diff options
context:
space:
mode:
Diffstat (limited to 'packaging/0025-Remove-relocations-for-MethodTable-s-vtable-1st-leve.patch')
-rw-r--r--packaging/0025-Remove-relocations-for-MethodTable-s-vtable-1st-leve.patch901
1 files changed, 901 insertions, 0 deletions
diff --git a/packaging/0025-Remove-relocations-for-MethodTable-s-vtable-1st-leve.patch b/packaging/0025-Remove-relocations-for-MethodTable-s-vtable-1st-leve.patch
new file mode 100644
index 0000000..97f2ebb
--- /dev/null
+++ b/packaging/0025-Remove-relocations-for-MethodTable-s-vtable-1st-leve.patch
@@ -0,0 +1,901 @@
+From 3bf76a8bc479dd90c9f2f75d8941445c2a5ea2b2 Mon Sep 17 00:00:00 2001
+From: Gleb Balykov <g.balykov@samsung.com>
+Date: Fri, 23 Jun 2017 15:48:48 +0300
+Subject: [PATCH 25/32] Remove relocations for MethodTable's
+ vtable-1st-level-indirection
+
+FIX: fix No.5, rebased
+---
+ .../superpmi/superpmi-shared/icorjitinfoimpl.h | 3 +-
+ src/ToolBox/superpmi/superpmi-shared/lwmlist.h | 2 +-
+ .../superpmi/superpmi-shared/methodcontext.cpp | 16 +++--
+ .../superpmi/superpmi-shared/methodcontext.h | 14 +++-
+ .../superpmi-shim-collector/icorjitinfo.cpp | 7 +-
+ .../superpmi/superpmi-shim-counter/icorjitinfo.cpp | 5 +-
+ .../superpmi/superpmi-shim-simple/icorjitinfo.cpp | 5 +-
+ src/ToolBox/superpmi/superpmi/icorjitinfo.cpp | 5 +-
+ src/debug/daccess/nidump.cpp | 9 +--
+ src/inc/corinfo.h | 3 +-
+ src/jit/ICorJitInfo_API_wrapper.hpp | 5 +-
+ src/jit/codegenlegacy.cpp | 76 ++++++++++++++++++----
+ src/jit/emitarm.cpp | 10 +++
+ src/jit/lower.cpp | 46 ++++++++++---
+ src/jit/lower.h | 6 ++
+ src/jit/morph.cpp | 20 +++++-
+ src/vm/array.cpp | 4 +-
+ src/vm/generics.cpp | 4 +-
+ src/vm/jitinterface.cpp | 6 +-
+ src/vm/jitinterface.h | 4 +-
+ src/vm/methodtable.cpp | 15 +++--
+ src/vm/methodtable.h | 19 ++++--
+ src/vm/methodtable.inl | 14 ++--
+ src/vm/methodtablebuilder.cpp | 12 ++--
+ src/zap/zapinfo.cpp | 7 +-
+ src/zap/zapinfo.h | 3 +-
+ 26 files changed, 238 insertions(+), 82 deletions(-)
+
+diff --git a/src/ToolBox/superpmi/superpmi-shared/icorjitinfoimpl.h b/src/ToolBox/superpmi/superpmi-shared/icorjitinfoimpl.h
+index 19feffa..44b81aa 100644
+--- a/src/ToolBox/superpmi/superpmi-shared/icorjitinfoimpl.h
++++ b/src/ToolBox/superpmi/superpmi-shared/icorjitinfoimpl.h
+@@ -109,7 +109,8 @@ CORINFO_MODULE_HANDLE getMethodModule(CORINFO_METHOD_HANDLE method);
+ // vtable of it's owning class or interface.
+ void getMethodVTableOffset(CORINFO_METHOD_HANDLE method, /* IN */
+ unsigned* offsetOfIndirection, /* OUT */
+- unsigned* offsetAfterIndirection /* OUT */
++ unsigned* offsetAfterIndirection,/* OUT */
++ unsigned* isRelative /* OUT */
+ );
+
+ // Find the virtual method in implementingClass that overrides virtualMethod.
+diff --git a/src/ToolBox/superpmi/superpmi-shared/lwmlist.h b/src/ToolBox/superpmi/superpmi-shared/lwmlist.h
+index 6e5f016..6b33681 100644
+--- a/src/ToolBox/superpmi/superpmi-shared/lwmlist.h
++++ b/src/ToolBox/superpmi/superpmi-shared/lwmlist.h
+@@ -102,7 +102,7 @@ LWM(GetMethodInfo, DWORDLONG, Agnostic_GetMethodInfo)
+ LWM(GetMethodName, DLD, DD)
+ LWM(GetMethodSig, DLDL, Agnostic_CORINFO_SIG_INFO)
+ LWM(GetMethodSync, DWORDLONG, DLDL)
+-LWM(GetMethodVTableOffset, DWORDLONG, DD)
++LWM(GetMethodVTableOffset, DWORDLONG, DDD)
+ LWM(GetNewArrHelper, DWORDLONG, DWORD)
+ LWM(GetNewHelper, Agnostic_GetNewHelper, DWORD)
+ LWM(GetParentType, DWORDLONG, DWORDLONG)
+diff --git a/src/ToolBox/superpmi/superpmi-shared/methodcontext.cpp b/src/ToolBox/superpmi/superpmi-shared/methodcontext.cpp
+index 4406b85..f4130e9 100644
+--- a/src/ToolBox/superpmi/superpmi-shared/methodcontext.cpp
++++ b/src/ToolBox/superpmi/superpmi-shared/methodcontext.cpp
+@@ -3382,26 +3382,29 @@ void MethodContext::repGetEHinfo(CORINFO_METHOD_HANDLE ftn, unsigned EHnumber, C
+
+ void MethodContext::recGetMethodVTableOffset(CORINFO_METHOD_HANDLE method,
+ unsigned* offsetOfIndirection,
+- unsigned* offsetAfterIndirection)
++ unsigned* offsetAfterIndirection,
++ unsigned* isRelative)
+ {
+ if (GetMethodVTableOffset == nullptr)
+- GetMethodVTableOffset = new LightWeightMap<DWORDLONG, DD>();
++ GetMethodVTableOffset = new LightWeightMap<DWORDLONG, DDD>();
+
+- DD value;
++ DDD value;
+ value.A = (DWORD)*offsetOfIndirection;
+ value.B = (DWORD)*offsetAfterIndirection;
++ value.C = (DWORD)*isRelative;
+ GetMethodVTableOffset->Add((DWORDLONG)method, value);
+ DEBUG_REC(dmpGetMethodVTableOffset((DWORDLONG)method, value));
+ }
+-void MethodContext::dmpGetMethodVTableOffset(DWORDLONG key, DD value)
++void MethodContext::dmpGetMethodVTableOffset(DWORDLONG key, DDD value)
+ {
+ printf("GetMethodVTableOffset key ftn-%016llX, value offi-%u, offa-%u", key, value.A, value.B);
+ }
+ void MethodContext::repGetMethodVTableOffset(CORINFO_METHOD_HANDLE method,
+ unsigned* offsetOfIndirection,
+- unsigned* offsetAfterIndirection)
++ unsigned* offsetAfterIndirection,
++ unsigned* isRelative)
+ {
+- DD value;
++ DDD value;
+
+ AssertCodeMsg(GetMethodVTableOffset != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for %016llX",
+ (DWORDLONG)method);
+@@ -3411,6 +3414,7 @@ void MethodContext::repGetMethodVTableOffset(CORINFO_METHOD_HANDLE method,
+
+ *offsetOfIndirection = (unsigned)value.A;
+ *offsetAfterIndirection = (unsigned)value.B;
++ *isRelative = (unsigned)value.C;
+ DEBUG_REP(dmpGetMethodVTableOffset((DWORDLONG)method, value));
+ }
+
+diff --git a/src/ToolBox/superpmi/superpmi-shared/methodcontext.h b/src/ToolBox/superpmi/superpmi-shared/methodcontext.h
+index 53227e4..a8612b5 100644
+--- a/src/ToolBox/superpmi/superpmi-shared/methodcontext.h
++++ b/src/ToolBox/superpmi/superpmi-shared/methodcontext.h
+@@ -206,6 +206,12 @@ public:
+ DWORD A;
+ DWORD B;
+ };
++ struct DDD
++ {
++ DWORD A;
++ DWORD B;
++ DWORD C;
++ };
+ struct Agnostic_CanTailCall
+ {
+ DWORDLONG callerHnd;
+@@ -774,11 +780,13 @@ public:
+
+ void recGetMethodVTableOffset(CORINFO_METHOD_HANDLE method,
+ unsigned* offsetOfIndirection,
+- unsigned* offsetAfterIndirection);
+- void dmpGetMethodVTableOffset(DWORDLONG key, DD value);
++ unsigned* offsetAfterIndirection,
++ unsigned* isRelative);
++ void dmpGetMethodVTableOffset(DWORDLONG key, DDD value);
+ void repGetMethodVTableOffset(CORINFO_METHOD_HANDLE method,
+ unsigned* offsetOfIndirection,
+- unsigned* offsetAfterIndirection);
++ unsigned* offsetAfterIndirection,
++ unsigned* isRelative);
+
+ void recResolveVirtualMethod(CORINFO_METHOD_HANDLE virtMethod,
+ CORINFO_CLASS_HANDLE implClass,
+diff --git a/src/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp b/src/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp
+index 4741cf1..1f81883 100644
+--- a/src/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp
++++ b/src/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp
+@@ -214,12 +214,13 @@ CORINFO_MODULE_HANDLE interceptor_ICJI::getMethodModule(CORINFO_METHOD_HANDLE me
+ // vtable of it's owning class or interface.
+ void interceptor_ICJI::getMethodVTableOffset(CORINFO_METHOD_HANDLE method, /* IN */
+ unsigned* offsetOfIndirection, /* OUT */
+- unsigned* offsetAfterIndirection /* OUT */
++ unsigned* offsetAfterIndirection,/* OUT */
++ unsigned* isRelative /* OUT */
+ )
+ {
+ mc->cr->AddCall("getMethodVTableOffset");
+- original_ICorJitInfo->getMethodVTableOffset(method, offsetOfIndirection, offsetAfterIndirection);
+- mc->recGetMethodVTableOffset(method, offsetOfIndirection, offsetAfterIndirection);
++ original_ICorJitInfo->getMethodVTableOffset(method, offsetOfIndirection, offsetAfterIndirection, isRelative);
++ mc->recGetMethodVTableOffset(method, offsetOfIndirection, offsetAfterIndirection, isRelative);
+ }
+
+ // Find the virtual method in implementingClass that overrides virtualMethod.
+diff --git a/src/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp b/src/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp
+index 1d45229..5c2e784 100644
+--- a/src/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp
++++ b/src/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp
+@@ -145,11 +145,12 @@ CORINFO_MODULE_HANDLE interceptor_ICJI::getMethodModule(CORINFO_METHOD_HANDLE me
+ // vtable of it's owning class or interface.
+ void interceptor_ICJI::getMethodVTableOffset(CORINFO_METHOD_HANDLE method, /* IN */
+ unsigned* offsetOfIndirection, /* OUT */
+- unsigned* offsetAfterIndirection /* OUT */
++ unsigned* offsetAfterIndirection,/* OUT */
++ unsigned* isRelative /* OUT */
+ )
+ {
+ mcs->AddCall("getMethodVTableOffset");
+- original_ICorJitInfo->getMethodVTableOffset(method, offsetOfIndirection, offsetAfterIndirection);
++ original_ICorJitInfo->getMethodVTableOffset(method, offsetOfIndirection, offsetAfterIndirection, isRelative);
+ }
+
+ // Find the virtual method in implementingClass that overrides virtualMethod.
+diff --git a/src/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp b/src/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp
+index aca7536..df223f4 100644
+--- a/src/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp
++++ b/src/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp
+@@ -134,10 +134,11 @@ CORINFO_MODULE_HANDLE interceptor_ICJI::getMethodModule(CORINFO_METHOD_HANDLE me
+ // vtable of it's owning class or interface.
+ void interceptor_ICJI::getMethodVTableOffset(CORINFO_METHOD_HANDLE method, /* IN */
+ unsigned* offsetOfIndirection, /* OUT */
+- unsigned* offsetAfterIndirection /* OUT */
++ unsigned* offsetAfterIndirection,/* OUT */
++ unsigned* isRelative /* OUT */
+ )
+ {
+- original_ICorJitInfo->getMethodVTableOffset(method, offsetOfIndirection, offsetAfterIndirection);
++ original_ICorJitInfo->getMethodVTableOffset(method, offsetOfIndirection, offsetAfterIndirection, isRelative);
+ }
+
+ // Find the virtual method in implementingClass that overrides virtualMethod.
+diff --git a/src/ToolBox/superpmi/superpmi/icorjitinfo.cpp b/src/ToolBox/superpmi/superpmi/icorjitinfo.cpp
+index 59ad3c5..dc73a75 100644
+--- a/src/ToolBox/superpmi/superpmi/icorjitinfo.cpp
++++ b/src/ToolBox/superpmi/superpmi/icorjitinfo.cpp
+@@ -165,11 +165,12 @@ CORINFO_MODULE_HANDLE MyICJI::getMethodModule(CORINFO_METHOD_HANDLE method)
+ // vtable of it's owning class or interface.
+ void MyICJI::getMethodVTableOffset(CORINFO_METHOD_HANDLE method, /* IN */
+ unsigned* offsetOfIndirection, /* OUT */
+- unsigned* offsetAfterIndirection /* OUT */
++ unsigned* offsetAfterIndirection,/* OUT */
++ unsigned* isRelative /* OUT */
+ )
+ {
+ jitInstance->mc->cr->AddCall("getMethodVTableOffset");
+- jitInstance->mc->repGetMethodVTableOffset(method, offsetOfIndirection, offsetAfterIndirection);
++ jitInstance->mc->repGetMethodVTableOffset(method, offsetOfIndirection, offsetAfterIndirection, isRelative);
+ }
+
+ // Find the virtual method in implementingClass that overrides virtualMethod.
+diff --git a/src/debug/daccess/nidump.cpp b/src/debug/daccess/nidump.cpp
+index d43e9f9..2732c9e 100644
+--- a/src/debug/daccess/nidump.cpp
++++ b/src/debug/daccess/nidump.cpp
+@@ -7230,9 +7230,9 @@ NativeImageDumper::DumpMethodTable( PTR_MethodTable mt, const char * name,
+ {
+ m_display->StartStructureWithOffset("Vtable",
+ mt->GetVtableOffset(),
+- mt->GetNumVtableIndirections() * sizeof(PTR_PCODE),
++ mt->GetNumVtableIndirections() * sizeof(MethodTable::VTableIndir_t),
+ DataPtrToDisplay(PTR_TO_TADDR(mt) + mt->GetVtableOffset()),
+- mt->GetNumVtableIndirections() * sizeof(PTR_PCODE));
++ mt->GetNumVtableIndirections() * sizeof(MethodTable::VTableIndir_t));
+
+
+ MethodTable::VtableIndirectionSlotIterator itIndirect = mt->IterateVtableIndirectionSlots();
+@@ -7251,7 +7251,8 @@ NativeImageDumper::DumpMethodTable( PTR_MethodTable mt, const char * name,
+ {
+ DisplayStartElement( "Slot", ALWAYS );
+ DisplayWriteElementInt( "Index", i, ALWAYS );
+- PTR_PCODE tgt = mt->GetVtableIndirections()[i];
++ TADDR base = dac_cast<TADDR>(&(mt->GetVtableIndirections()[i]));
++ PTR_PCODE tgt = MethodTable::VTableIndir_t::GetValueMaybeNullAtPtr(base);
+ DisplayWriteElementPointer( "Pointer",
+ DataPtrToDisplay(dac_cast<TADDR>(tgt)),
+ ALWAYS );
+@@ -7287,7 +7288,7 @@ NativeImageDumper::DumpMethodTable( PTR_MethodTable mt, const char * name,
+ else
+ {
+ CoverageRead( PTR_TO_TADDR(mt) + mt->GetVtableOffset(),
+- mt->GetNumVtableIndirections() * sizeof(PTR_PCODE) );
++ mt->GetNumVtableIndirections() * sizeof(MethodTable::VTableIndir_t) );
+
+ if (mt->HasNonVirtualSlotsArray())
+ {
+diff --git a/src/inc/corinfo.h b/src/inc/corinfo.h
+index 58fcdf4..2b1d3a9 100644
+--- a/src/inc/corinfo.h
++++ b/src/inc/corinfo.h
+@@ -2067,7 +2067,8 @@ public:
+ virtual void getMethodVTableOffset (
+ CORINFO_METHOD_HANDLE method, /* IN */
+ unsigned* offsetOfIndirection, /* OUT */
+- unsigned* offsetAfterIndirection /* OUT */
++ unsigned* offsetAfterIndirection, /* OUT */
++ unsigned* isRelative /* OUT */
+ ) = 0;
+
+ // Find the virtual method in implementingClass that overrides virtualMethod,
+diff --git a/src/jit/ICorJitInfo_API_wrapper.hpp b/src/jit/ICorJitInfo_API_wrapper.hpp
+index a3ad211..8e0d1df 100644
+--- a/src/jit/ICorJitInfo_API_wrapper.hpp
++++ b/src/jit/ICorJitInfo_API_wrapper.hpp
+@@ -122,10 +122,11 @@ CORINFO_MODULE_HANDLE WrapICorJitInfo::getMethodModule(
+ void WrapICorJitInfo::getMethodVTableOffset(
+ CORINFO_METHOD_HANDLE method, /* IN */
+ unsigned* offsetOfIndirection, /* OUT */
+- unsigned* offsetAfterIndirection /* OUT */)
++ unsigned* offsetAfterIndirection, /* OUT */
++ unsigned* isRelative /* OUT */)
+ {
+ API_ENTER(getMethodVTableOffset);
+- wrapHnd->getMethodVTableOffset(method, offsetOfIndirection, offsetAfterIndirection);
++ wrapHnd->getMethodVTableOffset(method, offsetOfIndirection, offsetAfterIndirection, isRelative);
+ API_LEAVE(getMethodVTableOffset);
+ }
+
+diff --git a/src/jit/codegenlegacy.cpp b/src/jit/codegenlegacy.cpp
+index 178be54..a925c97 100644
+--- a/src/jit/codegenlegacy.cpp
++++ b/src/jit/codegenlegacy.cpp
+@@ -18886,35 +18886,68 @@ regMaskTP CodeGen::genCodeForCall(GenTreeCall* call, bool valUsed)
+ // stub dispatching is off or this is not a virtual call (could be a tailcall)
+ {
+ regNumber vptrReg;
++ regNumber vptrReg1;
++ regMaskTP vptrMask1;
+ unsigned vtabOffsOfIndirection;
+ unsigned vtabOffsAfterIndirection;
++ unsigned isRelative;
+
+ noway_assert(callType == CT_USER_FUNC);
+
++ /* Get hold of the vtable offset (note: this might be expensive) */
++
++ compiler->info.compCompHnd->getMethodVTableOffset(call->gtCallMethHnd, &vtabOffsOfIndirection,
++ &vtabOffsAfterIndirection, &isRelative);
++
+ vptrReg =
+ regSet.rsGrabReg(RBM_ALLINT); // Grab an available register to use for the CALL indirection
+ vptrMask = genRegMask(vptrReg);
+
++ if (isRelative)
++ {
++ vptrReg1 = regSet.rsGrabReg(RBM_ALLINT & ~vptrMask);
++ vptrMask1 = genRegMask(vptrReg1);
++ }
++
+ /* The register no longer holds a live pointer value */
+ gcInfo.gcMarkRegSetNpt(vptrMask);
+
++ if (isRelative)
++ {
++ gcInfo.gcMarkRegSetNpt(vptrMask1);
++ }
++
+ // MOV vptrReg, [REG_CALL_THIS + offs]
+ getEmitter()->emitIns_R_AR(ins_Load(TYP_I_IMPL), EA_PTRSIZE, vptrReg, genGetThisArgReg(call),
+ VPTR_OFFS);
+ regTracker.rsTrackRegTrash(vptrReg);
+
+- noway_assert(vptrMask & ~call->gtCallRegUsedMask);
+-
+- /* Get hold of the vtable offset (note: this might be expensive) */
++ if (isRelative)
++ {
++ regTracker.rsTrackRegTrash(vptrReg1);
++ }
+
+- compiler->info.compCompHnd->getMethodVTableOffset(call->gtCallMethHnd, &vtabOffsOfIndirection,
+- &vtabOffsAfterIndirection);
++ noway_assert(vptrMask & ~call->gtCallRegUsedMask);
+
+ /* Get the appropriate vtable chunk */
+
+ /* The register no longer holds a live pointer value */
+ gcInfo.gcMarkRegSetNpt(vptrMask);
+
++ /* Get the appropriate vtable chunk */
++
++ if (isRelative)
++ {
++#if defined(_TARGET_ARM_)
++ unsigned offset = vtabOffsOfIndirection + vtabOffsAfterIndirection;
++
++ // ADD vptrReg1, REG_CALL_IND_SCRATCH, vtabOffsOfIndirection + vtabOffsAfterIndirection
++ getEmitter()->emitIns_R_R_I(INS_add, EA_PTRSIZE, vptrReg1, vptrReg, offset);
++#else
++ _ASSERTE(false);
++#endif
++ }
++
+ // MOV vptrReg, [REG_CALL_IND_SCRATCH + vtabOffsOfIndirection]
+ getEmitter()->emitIns_R_AR(ins_Load(TYP_I_IMPL), EA_PTRSIZE, vptrReg, vptrReg,
+ vtabOffsOfIndirection);
+@@ -18923,16 +18956,36 @@ regMaskTP CodeGen::genCodeForCall(GenTreeCall* call, bool valUsed)
+
+ if (fTailCall)
+ {
+- /* Load the function address: "[vptrReg+vtabOffs] -> reg_intret" */
+-
+- getEmitter()->emitIns_R_AR(ins_Load(TYP_I_IMPL), EA_PTRSIZE, REG_TAILCALL_ADDR, vptrReg,
+- vtabOffsAfterIndirection);
++ if (isRelative)
++ {
++#if defined(_TARGET_ARM_)
++ /* Load the function address: "[vptrReg1 + vptrReg] -> reg_intret" */
++ getEmitter()->emitIns_R_ARR(ins_Load(TYP_I_IMPL), EA_PTRSIZE, REG_TAILCALL_ADDR, vptrReg1,
++ vptrReg, 0);
++#else
++ _ASSERTE(false);
++#endif
++ }
++ else
++ {
++ /* Load the function address: "[vptrReg+vtabOffs] -> reg_intret" */
++ getEmitter()->emitIns_R_AR(ins_Load(TYP_I_IMPL), EA_PTRSIZE, REG_TAILCALL_ADDR, vptrReg,
++ vtabOffsAfterIndirection);
++ }
+ }
+ else
+ {
+ #if CPU_LOAD_STORE_ARCH
+- getEmitter()->emitIns_R_AR(ins_Load(TYP_I_IMPL), EA_PTRSIZE, vptrReg, vptrReg,
+- vtabOffsAfterIndirection);
++ if (isRelative)
++ {
++ getEmitter()->emitIns_R_ARR(ins_Load(TYP_I_IMPL), EA_PTRSIZE, vptrReg, vptrReg1, vptrReg,
++ 0);
++ }
++ else
++ {
++ getEmitter()->emitIns_R_AR(ins_Load(TYP_I_IMPL), EA_PTRSIZE, vptrReg, vptrReg,
++ vtabOffsAfterIndirection);
++ }
+
+ getEmitter()->emitIns_Call(emitter::EC_INDIR_R, call->gtCallMethHnd,
+ INDEBUG_LDISASM_COMMA(sigInfo) NULL, // addr
+@@ -18940,6 +18993,7 @@ regMaskTP CodeGen::genCodeForCall(GenTreeCall* call, bool valUsed)
+ gcInfo.gcRegByrefSetCur, ilOffset,
+ vptrReg); // ireg
+ #else
++ _ASSERTE(!isRelative);
+ getEmitter()->emitIns_Call(emitter::EC_FUNC_VIRTUAL, call->gtCallMethHnd,
+ INDEBUG_LDISASM_COMMA(sigInfo) NULL, // addr
+ args, retSize, gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur,
+diff --git a/src/jit/emitarm.cpp b/src/jit/emitarm.cpp
+index 9ec8e07..e765af7 100644
+--- a/src/jit/emitarm.cpp
++++ b/src/jit/emitarm.cpp
+@@ -2446,6 +2446,16 @@ void emitter::emitIns_R_R_I(instruction ins,
+ fmt = IF_T2_M0;
+ sf = INS_FLAGS_NOT_SET;
+ }
++ else if (insDoesNotSetFlags(flags) && reg1 != REG_SP && reg1 != REG_PC)
++ {
++ // movw,movt reg1, imm
++ codeGen->instGen_Set_Reg_To_Imm(attr, reg1, imm);
++
++ // ins reg1, reg2
++ emitIns_R_R(ins, attr, reg1, reg2);
++
++ return;
++ }
+ else
+ {
+ assert(!"Instruction cannot be encoded");
+diff --git a/src/jit/lower.cpp b/src/jit/lower.cpp
+index 9cab5ee..d154f68 100644
+--- a/src/jit/lower.cpp
++++ b/src/jit/lower.cpp
+@@ -3401,6 +3401,13 @@ GenTree* Lowering::LowerVirtualVtableCall(GenTreeCall* call)
+ // We'll introduce another use of this local so increase its ref count.
+ comp->lvaTable[lclNum].incRefCnts(comp->compCurBB->getBBWeight(comp), comp);
+
++ // Get hold of the vtable offset (note: this might be expensive)
++ unsigned vtabOffsOfIndirection;
++ unsigned vtabOffsAfterIndirection;
++ unsigned isRelative;
++ comp->info.compCompHnd->getMethodVTableOffset(call->gtCallMethHnd, &vtabOffsOfIndirection,
++ &vtabOffsAfterIndirection, &isRelative);
++
+ // If the thisPtr is a local field, then construct a local field type node
+ GenTree* local;
+ if (thisPtr->isLclField())
+@@ -3416,19 +3423,40 @@ GenTree* Lowering::LowerVirtualVtableCall(GenTreeCall* call)
+ // pointer to virtual table = [REG_CALL_THIS + offs]
+ GenTree* result = Ind(Offset(local, VPTR_OFFS));
+
+- // Get hold of the vtable offset (note: this might be expensive)
+- unsigned vtabOffsOfIndirection;
+- unsigned vtabOffsAfterIndirection;
+- comp->info.compCompHnd->getMethodVTableOffset(call->gtCallMethHnd, &vtabOffsOfIndirection,
+- &vtabOffsAfterIndirection);
+-
+ // Get the appropriate vtable chunk
+- // result = [REG_CALL_IND_SCRATCH + vtabOffsOfIndirection]
+- result = Ind(Offset(result, vtabOffsOfIndirection));
++ if (isRelative)
++ {
++ unsigned lclNumTmp = comp->lvaGrabTemp(true DEBUGARG("lclNumTmp"));
++
++ comp->lvaTable[lclNumTmp].incRefCnts(comp->compCurBB->getBBWeight(comp), comp);
++ GenTree* lclvNodeStore = comp->gtNewTempAssign(lclNumTmp, result);
++
++ LIR::Range range = LIR::SeqTree(comp, lclvNodeStore);
++ JITDUMP("results of lowering call interm:\n");
++ DISPRANGE(range);
++ BlockRange().InsertBefore(call, std::move(range));
++
++ GenTree* tmpTree = comp->gtNewLclvNode(lclNumTmp, result->TypeGet());
++ tmpTree = Offset(tmpTree, vtabOffsOfIndirection);
++
++ tmpTree = comp->gtNewOperNode(GT_IND, TYP_I_IMPL, tmpTree, false);
++ GenTree* offs = comp->gtNewIconNode(vtabOffsOfIndirection + vtabOffsAfterIndirection, TYP_INT);
++ result = comp->gtNewOperNode(GT_ADD, TYP_I_IMPL, comp->gtNewLclvNode(lclNumTmp, result->TypeGet()), offs);
++
++ result = Ind(OffsetByIndex(result, tmpTree));
++ }
++ else
++ {
++ // result = [REG_CALL_IND_SCRATCH + vtabOffsOfIndirection]
++ result = Ind(Offset(result, vtabOffsOfIndirection));
++ }
+
+ // Load the function address
+ // result = [reg+vtabOffs]
+- result = Ind(Offset(result, vtabOffsAfterIndirection));
++ if (!isRelative)
++ {
++ result = Ind(Offset(result, vtabOffsAfterIndirection));
++ }
+
+ return result;
+ }
+diff --git a/src/jit/lower.h b/src/jit/lower.h
+index 5a55d2d..92d9cfe 100644
+--- a/src/jit/lower.h
++++ b/src/jit/lower.h
+@@ -120,6 +120,12 @@ private:
+ return new (comp, GT_LEA) GenTreeAddrMode(resultType, base, nullptr, 0, offset);
+ }
+
++ GenTree* OffsetByIndex(GenTree* base, GenTree* index)
++ {
++ var_types resultType = (base->TypeGet() == TYP_REF) ? TYP_BYREF : base->TypeGet();
++ return new (comp, GT_LEA) GenTreeAddrMode(resultType, base, index, 0, 0);
++ }
++
+ // returns true if the tree can use the read-modify-write memory instruction form
+ bool isRMWRegOper(GenTreePtr tree);
+
+diff --git a/src/jit/morph.cpp b/src/jit/morph.cpp
+index 3475889..c5d1ff2 100644
+--- a/src/jit/morph.cpp
++++ b/src/jit/morph.cpp
+@@ -7116,13 +7116,29 @@ void Compiler::fgMorphTailCall(GenTreeCall* call)
+
+ unsigned vtabOffsOfIndirection;
+ unsigned vtabOffsAfterIndirection;
+- info.compCompHnd->getMethodVTableOffset(call->gtCallMethHnd, &vtabOffsOfIndirection, &vtabOffsAfterIndirection);
++ unsigned isRelative;
++ info.compCompHnd->getMethodVTableOffset(call->gtCallMethHnd, &vtabOffsOfIndirection, &vtabOffsAfterIndirection,
++ &isRelative);
+
+ /* Get the appropriate vtable chunk */
+
+- add = gtNewOperNode(GT_ADD, TYP_I_IMPL, vtbl, gtNewIconNode(vtabOffsOfIndirection, TYP_I_IMPL));
++ add = gtNewOperNode(GT_ADD, TYP_I_IMPL, vtbl, gtNewIconNode(vtabOffsOfIndirection, TYP_I_IMPL));
++
++ GenTreePtr indOffTree;
++
++ if (isRelative)
++ {
++ indOffTree = impCloneExpr(add, &add, NO_CLASS_HANDLE, (unsigned)CHECK_SPILL_ALL,
++ nullptr DEBUGARG("virtual table call"));
++ }
++
+ vtbl = gtNewOperNode(GT_IND, TYP_I_IMPL, add);
+
++ if (isRelative)
++ {
++ vtbl = gtNewOperNode(GT_ADD, TYP_I_IMPL, vtbl, indOffTree);
++ }
++
+ /* Now the appropriate vtable slot */
+
+ add = gtNewOperNode(GT_ADD, TYP_I_IMPL, vtbl, gtNewIconNode(vtabOffsAfterIndirection, TYP_I_IMPL));
+diff --git a/src/vm/array.cpp b/src/vm/array.cpp
+index 3f5a8aa..3a33aff 100644
+--- a/src/vm/array.cpp
++++ b/src/vm/array.cpp
+@@ -310,7 +310,7 @@ MethodTable* Module::CreateArrayMethodTable(TypeHandle elemTypeHnd, CorElementTy
+ DWORD numNonVirtualSlots = numCtors + 3; // 3 for the proper rank Get, Set, Address
+
+ size_t cbMT = sizeof(MethodTable);
+- cbMT += MethodTable::GetNumVtableIndirections(numVirtuals) * sizeof(PTR_PCODE);
++ cbMT += MethodTable::GetNumVtableIndirections(numVirtuals) * sizeof(MethodTable::VTableIndir_t);
+
+ // GC info
+ size_t cbCGCDescData = 0;
+@@ -539,7 +539,7 @@ MethodTable* Module::CreateArrayMethodTable(TypeHandle elemTypeHnd, CorElementTy
+ if (canShareVtableChunks)
+ {
+ // Share the parent chunk
+- it.SetIndirectionSlot(pParentClass->GetVtableIndirections()[it.GetIndex()]);
++ it.SetIndirectionSlot(pParentClass->GetVtableIndirections()[it.GetIndex()].GetValueMaybeNull());
+ }
+ else
+ {
+diff --git a/src/vm/generics.cpp b/src/vm/generics.cpp
+index 650caef..b110184 100644
+--- a/src/vm/generics.cpp
++++ b/src/vm/generics.cpp
+@@ -255,7 +255,7 @@ ClassLoader::CreateTypeHandleForNonCanonicalGenericInstantiation(
+
+ // Bytes are required for the vtable itself
+ S_SIZE_T safe_cbMT = S_SIZE_T( cbGC ) + S_SIZE_T( sizeof(MethodTable) );
+- safe_cbMT += MethodTable::GetNumVtableIndirections(cSlots) * sizeof(PTR_PCODE);
++ safe_cbMT += MethodTable::GetNumVtableIndirections(cSlots) * sizeof(MethodTable::VTableIndir_t);
+ if (safe_cbMT.IsOverflow())
+ {
+ ThrowHR(COR_E_OVERFLOW);
+@@ -440,7 +440,7 @@ ClassLoader::CreateTypeHandleForNonCanonicalGenericInstantiation(
+ if (canShareVtableChunks)
+ {
+ // Share the canonical chunk
+- it.SetIndirectionSlot(pOldMT->GetVtableIndirections()[it.GetIndex()]);
++ it.SetIndirectionSlot(pOldMT->GetVtableIndirections()[it.GetIndex()].GetValueMaybeNull());
+ }
+ else
+ {
+diff --git a/src/vm/jitinterface.cpp b/src/vm/jitinterface.cpp
+index e0adf87..72f4131 100644
+--- a/src/vm/jitinterface.cpp
++++ b/src/vm/jitinterface.cpp
+@@ -8724,7 +8724,8 @@ CONTRACTL {
+ /*********************************************************************/
+ void CEEInfo::getMethodVTableOffset (CORINFO_METHOD_HANDLE methodHnd,
+ unsigned * pOffsetOfIndirection,
+- unsigned * pOffsetAfterIndirection)
++ unsigned * pOffsetAfterIndirection,
++ unsigned * isRelative)
+ {
+ CONTRACTL {
+ SO_TOLERANT;
+@@ -8745,8 +8746,9 @@ void CEEInfo::getMethodVTableOffset (CORINFO_METHOD_HANDLE methodHnd,
+ // better be in the vtable
+ _ASSERTE(method->GetSlot() < method->GetMethodTable()->GetNumVirtuals());
+
+- *pOffsetOfIndirection = MethodTable::GetVtableOffset() + MethodTable::GetIndexOfVtableIndirection(method->GetSlot()) * sizeof(PTR_PCODE);
++ *pOffsetOfIndirection = MethodTable::GetVtableOffset() + MethodTable::GetIndexOfVtableIndirection(method->GetSlot()) * sizeof(MethodTable::VTableIndir_t);
+ *pOffsetAfterIndirection = MethodTable::GetIndexAfterVtableIndirection(method->GetSlot()) * sizeof(PCODE);
++ *isRelative = MethodTable::VTableIndir_t::isRelative ? 1 : 0;
+
+ EE_TO_JIT_TRANSITION_LEAF();
+ }
+diff --git a/src/vm/jitinterface.h b/src/vm/jitinterface.h
+index d67cfc5..cf1097c 100644
+--- a/src/vm/jitinterface.h
++++ b/src/vm/jitinterface.h
+@@ -727,8 +727,8 @@ public:
+ void getMethodVTableOffset (
+ CORINFO_METHOD_HANDLE methodHnd,
+ unsigned * pOffsetOfIndirection,
+- unsigned * pOffsetAfterIndirection
+- );
++ unsigned * pOffsetAfterIndirection,
++ unsigned * isRelative);
+
+ CORINFO_METHOD_HANDLE resolveVirtualMethod(
+ CORINFO_METHOD_HANDLE virtualMethod,
+diff --git a/src/vm/methodtable.cpp b/src/vm/methodtable.cpp
+index 4c1746e..75db911 100644
+--- a/src/vm/methodtable.cpp
++++ b/src/vm/methodtable.cpp
+@@ -4915,7 +4915,14 @@ void MethodTable::Fixup(DataImage *image)
+ VtableIndirectionSlotIterator it = IterateVtableIndirectionSlots();
+ while (it.Next())
+ {
+- image->FixupPointerField(this, it.GetOffsetFromMethodTable());
++ if (VTableIndir_t::isRelative)
++ {
++ image->FixupRelativePointerField(this, it.GetOffsetFromMethodTable());
++ }
++ else
++ {
++ image->FixupPointerField(this, it.GetOffsetFromMethodTable());
++ }
+ }
+ }
+
+@@ -4936,7 +4943,7 @@ void MethodTable::Fixup(DataImage *image)
+ {
+ // Virtual slots live in chunks pointed to by vtable indirections
+
+- slotBase = (PVOID) GetVtableIndirections()[GetIndexOfVtableIndirection(slotNumber)];
++ slotBase = (PVOID) GetVtableIndirections()[GetIndexOfVtableIndirection(slotNumber)].GetValueMaybeNull();
+ slotOffset = GetIndexAfterVtableIndirection(slotNumber) * sizeof(PCODE);
+ }
+ else if (HasSingleNonVirtualSlot())
+@@ -9414,13 +9421,13 @@ void MethodTable::SetSlot(UINT32 slotNumber, PCODE slotCode)
+
+ if (!IsCanonicalMethodTable())
+ {
+- if (GetVtableIndirections()[indirectionIndex] == GetCanonicalMethodTable()->GetVtableIndirections()[indirectionIndex])
++ if (GetVtableIndirections()[indirectionIndex].GetValueMaybeNull() == GetCanonicalMethodTable()->GetVtableIndirections()[indirectionIndex].GetValueMaybeNull())
+ fSharedVtableChunk = TRUE;
+ }
+
+ if (slotNumber < GetNumParentVirtuals())
+ {
+- if (GetVtableIndirections()[indirectionIndex] == GetParentMethodTable()->GetVtableIndirections()[indirectionIndex])
++ if (GetVtableIndirections()[indirectionIndex].GetValueMaybeNull() == GetParentMethodTable()->GetVtableIndirections()[indirectionIndex].GetValueMaybeNull())
+ fSharedVtableChunk = TRUE;
+ }
+
+diff --git a/src/vm/methodtable.h b/src/vm/methodtable.h
+index 81a9186..8c15d2e 100644
+--- a/src/vm/methodtable.h
++++ b/src/vm/methodtable.h
+@@ -1514,7 +1514,10 @@ public:
+
+ CONSISTENCY_CHECK(slotNum < GetNumVirtuals());
+ // Virtual slots live in chunks pointed to by vtable indirections
+- return *(GetVtableIndirections()[GetIndexOfVtableIndirection(slotNum)] + GetIndexAfterVtableIndirection(slotNum));
++
++ DWORD index = GetIndexOfVtableIndirection(slotNum);
++ TADDR base = dac_cast<TADDR>(&(GetVtableIndirections()[index]));
++ return *(VTableIndir_t::GetValueMaybeNullAtPtr(base) + GetIndexAfterVtableIndirection(slotNum));
+ }
+
+ PTR_PCODE GetSlotPtrRaw(UINT32 slotNum)
+@@ -1526,7 +1529,9 @@ public:
+ if (slotNum < GetNumVirtuals())
+ {
+ // Virtual slots live in chunks pointed to by vtable indirections
+- return GetVtableIndirections()[GetIndexOfVtableIndirection(slotNum)] + GetIndexAfterVtableIndirection(slotNum);
++ DWORD index = GetIndexOfVtableIndirection(slotNum);
++ TADDR base = dac_cast<TADDR>(&(GetVtableIndirections()[index]));
++ return VTableIndir_t::GetValueMaybeNullAtPtr(base) + GetIndexAfterVtableIndirection(slotNum);
+ }
+ else if (HasSingleNonVirtualSlot())
+ {
+@@ -1610,12 +1615,18 @@ public:
+ #define VTABLE_SLOTS_PER_CHUNK 8
+ #define VTABLE_SLOTS_PER_CHUNK_LOG2 3
+
++#if defined(PLATFORM_UNIX) && defined(_TARGET_ARM_)
++ typedef RelativePointer<PTR_PCODE> VTableIndir_t;
++#else
++ typedef PlainPointer<PTR_PCODE> VTableIndir_t;
++#endif
++
+ static DWORD GetIndexOfVtableIndirection(DWORD slotNum);
+ static DWORD GetStartSlotForVtableIndirection(UINT32 indirectionIndex, DWORD wNumVirtuals);
+ static DWORD GetEndSlotForVtableIndirection(UINT32 indirectionIndex, DWORD wNumVirtuals);
+ static UINT32 GetIndexAfterVtableIndirection(UINT32 slotNum);
+ static DWORD GetNumVtableIndirections(DWORD wNumVirtuals);
+- PTR_PTR_PCODE GetVtableIndirections();
++ DPTR(VTableIndir_t) GetVtableIndirections();
+ DWORD GetNumVtableIndirections();
+
+ class VtableIndirectionSlotIterator
+@@ -1623,7 +1634,7 @@ public:
+ friend class MethodTable;
+
+ private:
+- PTR_PTR_PCODE m_pSlot;
++ DPTR(VTableIndir_t) m_pSlot;
+ DWORD m_i;
+ DWORD m_count;
+ PTR_MethodTable m_pMT;
+diff --git a/src/vm/methodtable.inl b/src/vm/methodtable.inl
+index b69513d..0d0acda 100644
+--- a/src/vm/methodtable.inl
++++ b/src/vm/methodtable.inl
+@@ -887,10 +887,10 @@ inline DWORD MethodTable::GetNumVtableIndirections(DWORD wNumVirtuals)
+ }
+
+ //==========================================================================================
+-inline PTR_PTR_PCODE MethodTable::GetVtableIndirections()
++inline DPTR(MethodTable::VTableIndir_t) MethodTable::GetVtableIndirections()
+ {
+ LIMITED_METHOD_DAC_CONTRACT;
+- return dac_cast<PTR_PTR_PCODE>(dac_cast<TADDR>(this) + sizeof(MethodTable));
++ return dac_cast<DPTR(VTableIndir_t)>(dac_cast<TADDR>(this) + sizeof(MethodTable));
+ }
+
+ //==========================================================================================
+@@ -952,7 +952,7 @@ inline DWORD MethodTable::VtableIndirectionSlotIterator::GetOffsetFromMethodTabl
+ WRAPPER_NO_CONTRACT;
+ PRECONDITION(m_i != (DWORD) -1 && m_i < m_count);
+
+- return GetVtableOffset() + sizeof(PTR_PCODE) * m_i;
++ return GetVtableOffset() + sizeof(VTableIndir_t) * m_i;
+ }
+
+ //==========================================================================================
+@@ -961,7 +961,7 @@ inline PTR_PCODE MethodTable::VtableIndirectionSlotIterator::GetIndirectionSlot(
+ LIMITED_METHOD_DAC_CONTRACT;
+ PRECONDITION(m_i != (DWORD) -1 && m_i < m_count);
+
+- return *m_pSlot;
++ return m_pSlot->GetValueMaybeNull(dac_cast<TADDR>(m_pSlot));
+ }
+
+ //==========================================================================================
+@@ -969,7 +969,7 @@ inline PTR_PCODE MethodTable::VtableIndirectionSlotIterator::GetIndirectionSlot(
+ inline void MethodTable::VtableIndirectionSlotIterator::SetIndirectionSlot(PTR_PCODE pChunk)
+ {
+ LIMITED_METHOD_CONTRACT;
+- *m_pSlot = pChunk;
++ m_pSlot->SetValueMaybeNull(pChunk);
+ }
+ #endif
+
+@@ -1355,7 +1355,7 @@ FORCEINLINE TADDR MethodTable::GetMultipurposeSlotPtr(WFLAGS2_ENUM flag, const B
+ DWORD offset = offsets[GetFlag((WFLAGS2_ENUM)(flag - 1))];
+
+ if (offset >= sizeof(MethodTable)) {
+- offset += GetNumVtableIndirections() * sizeof(PTR_PCODE);
++ offset += GetNumVtableIndirections() * sizeof(VTableIndir_t);
+ }
+
+ return dac_cast<TADDR>(this) + offset;
+@@ -1370,7 +1370,7 @@ FORCEINLINE DWORD MethodTable::GetOffsetOfOptionalMember(OptionalMemberId id)
+
+ DWORD offset = c_OptionalMembersStartOffsets[GetFlag(enum_flag_MultipurposeSlotsMask)];
+
+- offset += GetNumVtableIndirections() * sizeof(PTR_PCODE);
++ offset += GetNumVtableIndirections() * sizeof(VTableIndir_t);
+
+ #undef METHODTABLE_OPTIONAL_MEMBER
+ #define METHODTABLE_OPTIONAL_MEMBER(NAME, TYPE, GETTER) \
+diff --git a/src/vm/methodtablebuilder.cpp b/src/vm/methodtablebuilder.cpp
+index 792a19c..970166d 100644
+--- a/src/vm/methodtablebuilder.cpp
++++ b/src/vm/methodtablebuilder.cpp
+@@ -9111,7 +9111,7 @@ void MethodTableBuilder::CopyExactParentSlots(MethodTable *pMT, MethodTable *pAp
+ //
+ // Non-canonical method tables either share everything or nothing so it is sufficient to check
+ // just the first indirection to detect sharing.
+- if (pMT->GetVtableIndirections()[0] != pCanonMT->GetVtableIndirections()[0])
++ if (pMT->GetVtableIndirections()[0].GetValueMaybeNull() != pCanonMT->GetVtableIndirections()[0].GetValueMaybeNull())
+ {
+ for (DWORD i = 0; i < nParentVirtuals; i++)
+ {
+@@ -9138,7 +9138,7 @@ void MethodTableBuilder::CopyExactParentSlots(MethodTable *pMT, MethodTable *pAp
+ // We need to re-inherit this slot from the exact parent.
+
+ DWORD indirectionIndex = MethodTable::GetIndexOfVtableIndirection(i);
+- if (pMT->GetVtableIndirections()[indirectionIndex] == pApproxParentMT->GetVtableIndirections()[indirectionIndex])
++ if (pMT->GetVtableIndirections()[indirectionIndex].GetValueMaybeNull() == pApproxParentMT->GetVtableIndirections()[indirectionIndex].GetValueMaybeNull())
+ {
+ // The slot lives in a chunk shared from the approximate parent MT
+ // If so, we need to change to share the chunk from the exact parent MT
+@@ -9149,7 +9149,7 @@ void MethodTableBuilder::CopyExactParentSlots(MethodTable *pMT, MethodTable *pAp
+ _ASSERTE(MethodTable::CanShareVtableChunksFrom(pParentMT, pMT->GetLoaderModule()));
+ #endif
+
+- pMT->GetVtableIndirections()[indirectionIndex] = pParentMT->GetVtableIndirections()[indirectionIndex];
++ pMT->GetVtableIndirections()[indirectionIndex].SetValueMaybeNull(pParentMT->GetVtableIndirections()[indirectionIndex].GetValueMaybeNull());
+
+ i = MethodTable::GetEndSlotForVtableIndirection(indirectionIndex, nParentVirtuals) - 1;
+ continue;
+@@ -10006,7 +10006,7 @@ MethodTable * MethodTableBuilder::AllocateNewMT(Module *pLoaderModule,
+ S_SIZE_T cbTotalSize = S_SIZE_T(dwGCSize) + S_SIZE_T(sizeof(MethodTable));
+
+ // vtable
+- cbTotalSize += MethodTable::GetNumVtableIndirections(dwVirtuals) * sizeof(PTR_PCODE);
++ cbTotalSize += MethodTable::GetNumVtableIndirections(dwVirtuals) * sizeof(MethodTable::VTableIndir_t);
+
+
+ DWORD dwMultipurposeSlotsMask = 0;
+@@ -10155,7 +10155,7 @@ MethodTable * MethodTableBuilder::AllocateNewMT(Module *pLoaderModule,
+ {
+ // Share the parent chunk
+ _ASSERTE(it.GetEndSlot() <= pMTParent->GetNumVirtuals());
+- it.SetIndirectionSlot(pMTParent->GetVtableIndirections()[it.GetIndex()]);
++ it.SetIndirectionSlot(pMTParent->GetVtableIndirections()[it.GetIndex()].GetValueMaybeNull());
+ }
+ else
+ {
+@@ -10711,7 +10711,7 @@ MethodTableBuilder::SetupMethodTable2(
+ // with code:MethodDesc::SetStableEntryPointInterlocked.
+ //
+ DWORD indirectionIndex = MethodTable::GetIndexOfVtableIndirection(iCurSlot);
+- if (GetParentMethodTable()->GetVtableIndirections()[indirectionIndex] != pMT->GetVtableIndirections()[indirectionIndex])
++ if (GetParentMethodTable()->GetVtableIndirections()[indirectionIndex].GetValueMaybeNull() != pMT->GetVtableIndirections()[indirectionIndex].GetValueMaybeNull())
+ pMT->SetSlot(iCurSlot, pMD->GetMethodEntryPoint());
+ }
+ else
+diff --git a/src/zap/zapinfo.cpp b/src/zap/zapinfo.cpp
+index e94dea6..507cc25 100644
+--- a/src/zap/zapinfo.cpp
++++ b/src/zap/zapinfo.cpp
+@@ -3708,10 +3708,11 @@ CORINFO_MODULE_HANDLE ZapInfo::getMethodModule(CORINFO_METHOD_HANDLE method)
+ }
+
+ void ZapInfo::getMethodVTableOffset(CORINFO_METHOD_HANDLE method,
+- unsigned * pOffsetOfIndirection,
+- unsigned * pOffsetAfterIndirection)
++ unsigned * pOffsetOfIndirection,
++ unsigned * pOffsetAfterIndirection,
++ unsigned * isRelative)
+ {
+- m_pEEJitInfo->getMethodVTableOffset(method, pOffsetOfIndirection, pOffsetAfterIndirection);
++ m_pEEJitInfo->getMethodVTableOffset(method, pOffsetOfIndirection, pOffsetAfterIndirection, isRelative);
+ }
+
+ CORINFO_METHOD_HANDLE ZapInfo::resolveVirtualMethod(
+diff --git a/src/zap/zapinfo.h b/src/zap/zapinfo.h
+index 6e83657..65c1ddd 100644
+--- a/src/zap/zapinfo.h
++++ b/src/zap/zapinfo.h
+@@ -663,7 +663,8 @@ public:
+
+ void getMethodVTableOffset(CORINFO_METHOD_HANDLE method,
+ unsigned * pOffsetOfIndirection,
+- unsigned * pOffsetAfterIndirection);
++ unsigned * pOffsetAfterIndirection,
++ unsigned * isRelative);
+
+ CORINFO_METHOD_HANDLE resolveVirtualMethod(
+ CORINFO_METHOD_HANDLE virtualMethod,
+--
+2.7.4
+