summaryrefslogtreecommitdiff
path: root/packaging/0015-Partially-remove-relocations-from-Class-section-of-N.patch
diff options
context:
space:
mode:
Diffstat (limited to 'packaging/0015-Partially-remove-relocations-from-Class-section-of-N.patch')
-rw-r--r--packaging/0015-Partially-remove-relocations-from-Class-section-of-N.patch1105
1 files changed, 1105 insertions, 0 deletions
diff --git a/packaging/0015-Partially-remove-relocations-from-Class-section-of-N.patch b/packaging/0015-Partially-remove-relocations-from-Class-section-of-N.patch
new file mode 100644
index 0000000..69b7fb7
--- /dev/null
+++ b/packaging/0015-Partially-remove-relocations-from-Class-section-of-N.patch
@@ -0,0 +1,1105 @@
+From cb4f4da84d36ec631f69a1e9007035a62a6c1738 Mon Sep 17 00:00:00 2001
+From: Ruben Ayrapetyan <ruben-ayrapetyan@users.noreply.github.com>
+Date: Wed, 28 Jun 2017 09:16:01 +0300
+Subject: [PATCH 15/32] Partially remove relocations from Class section of
+ NGEN-ed images (#11962)
+
+* Remove relocations for ParamTypeDesc::m_TemplateMT.
+
+* Remove relocations for LayoutEEClass::m_LayoutInfo.m_pFieldMarshalers.
+
+* Prepare RelativeFixupPointer.
+
+* Remove relocations for FieldMarshaler::m_pFD and FieldMarshaler_*::m_*.
+---
+ src/debug/daccess/nidump.cpp | 8 +-
+ src/inc/fixuppointer.h | 31 ++++++-
+ src/vm/ceeload.cpp | 7 +-
+ src/vm/ceeload.h | 3 +-
+ src/vm/class.cpp | 8 +-
+ src/vm/class.h | 15 +++-
+ src/vm/fieldmarshaler.cpp | 19 ++--
+ src/vm/fieldmarshaler.h | 197 ++++++++++++++++++++++++++++++++++++------
+ src/vm/methodtablebuilder.cpp | 8 +-
+ src/vm/typedesc.cpp | 22 ++---
+ src/vm/typedesc.h | 23 +++--
+ src/vm/typedesc.inl | 2 +-
+ 12 files changed, 269 insertions(+), 74 deletions(-)
+
+diff --git a/src/debug/daccess/nidump.cpp b/src/debug/daccess/nidump.cpp
+index 42705a5..2ec5d9a 100644
+--- a/src/debug/daccess/nidump.cpp
++++ b/src/debug/daccess/nidump.cpp
+@@ -4767,7 +4767,7 @@ void NativeImageDumper::TraverseTypeHashEntry(void *pContext, PTR_EETypeHashEntr
+ * all that much harm here (bloats m_discoveredMTs though,
+ * but not by a huge amount.
+ */
+- PTR_MethodTable mt(ptd->m_TemplateMT.GetValue());
++ PTR_MethodTable mt(ptd->GetTemplateMethodTableInternal());
+ if (isInRange(PTR_TO_TADDR(mt)))
+ {
+ m_discoveredMTs.AppendEx(mt);
+@@ -6243,7 +6243,7 @@ void NativeImageDumper::TypeDescToString( PTR_TypeDesc td, SString& buf )
+ if( td->IsArray() )
+ {
+ //td->HasTypeParam() may also be true.
+- PTR_MethodTable mt = ptd->m_TemplateMT.GetValue();
++ PTR_MethodTable mt = ptd->GetTemplateMethodTableInternal();
+ _ASSERTE( PTR_TO_TADDR(mt) );
+ if( CORCOMPILE_IS_POINTER_TAGGED(PTR_TO_TADDR(mt)) )
+ {
+@@ -8493,7 +8493,7 @@ NativeImageDumper::DumpEEClassForMethodTable( PTR_MethodTable mt )
+ VERBOSE_TYPES );
+ DisplayWriteFieldInt( m_numCTMFields, eecli->m_numCTMFields,
+ EEClassLayoutInfo, VERBOSE_TYPES );
+- PTR_FieldMarshaler fmArray( TO_TADDR(eecli->m_pFieldMarshalers) );
++ PTR_FieldMarshaler fmArray = eecli->GetFieldMarshalers();
+ DisplayWriteFieldAddress( m_pFieldMarshalers,
+ DPtrToPreferredAddr(fmArray),
+ eecli->m_numCTMFields
+@@ -8840,7 +8840,7 @@ void NativeImageDumper::DumpTypeDesc( PTR_TypeDesc td )
+ {
+ PTR_ParamTypeDesc ptd(td);
+ DisplayStartVStructure( "ParamTypeDesc", TYPEDESCS );
+- WriteFieldMethodTable( m_TemplateMT, ptd->m_TemplateMT.GetValue(),
++ WriteFieldMethodTable( m_TemplateMT, ptd->GetTemplateMethodTableInternal(),
+ ParamTypeDesc, TYPEDESCS );
+ WriteFieldTypeHandle( m_Arg, ptd->m_Arg,
+ ParamTypeDesc, TYPEDESCS );
+diff --git a/src/inc/fixuppointer.h b/src/inc/fixuppointer.h
+index 38ae348..83ff20e 100644
+--- a/src/inc/fixuppointer.h
++++ b/src/inc/fixuppointer.h
+@@ -249,6 +249,15 @@ public:
+ static constexpr bool isRelative = true;
+ typedef PTR_TYPE type;
+
++#ifndef DACCESS_COMPILE
++ RelativeFixupPointer()
++ {
++ SetValueMaybeNull(NULL);
++ }
++#else // DACCESS_COMPILE
++ RelativeFixupPointer() =delete;
++#endif // DACCESS_COMPILE
++
+ // Implicit copy/move is not allowed
+ RelativeFixupPointer<PTR_TYPE>(const RelativeFixupPointer<PTR_TYPE> &) =delete;
+ RelativeFixupPointer<PTR_TYPE>(RelativeFixupPointer<PTR_TYPE> &&) =delete;
+@@ -273,6 +282,15 @@ public:
+ return FALSE;
+ }
+
++#ifndef DACCESS_COMPILE
++ FORCEINLINE BOOL IsTagged() const
++ {
++ LIMITED_METHOD_CONTRACT;
++ TADDR base = (TADDR) this;
++ return IsTagged(base);
++ }
++#endif // !DACCESS_COMPILE
++
+ // Returns value of the encoded pointer. Assumes that the pointer is not NULL.
+ FORCEINLINE PTR_TYPE GetValue(TADDR base) const
+ {
+@@ -343,7 +361,7 @@ public:
+ {
+ LIMITED_METHOD_CONTRACT;
+ PRECONDITION(addr != NULL);
+- m_delta = (TADDR)addr - (TADDR)this;
++ m_delta = dac_cast<TADDR>(addr) - (TADDR)this;
+ }
+
+ // Set encoded value of the pointer. The value can be NULL.
+@@ -353,7 +371,7 @@ public:
+ if (addr == NULL)
+ m_delta = NULL;
+ else
+- m_delta = (TADDR)addr - (TADDR)base;
++ m_delta = dac_cast<TADDR>(addr) - (TADDR)base;
+ }
+
+ // Set encoded value of the pointer. The value can be NULL.
+@@ -373,6 +391,15 @@ public:
+ return dac_cast<DPTR(PTR_TYPE)>(addr - FIXUP_POINTER_INDIRECTION);
+ }
+
++#ifndef DACCESS_COMPILE
++ PTR_TYPE * GetValuePtr() const
++ {
++ LIMITED_METHOD_CONTRACT;
++ TADDR base = (TADDR) this;
++ return GetValuePtr(base);
++ }
++#endif // !DACCESS_COMPILE
++
+ // Returns value of the encoded pointer. Assumes that the pointer is not NULL.
+ // Allows the value to be tagged.
+ FORCEINLINE TADDR GetValueMaybeTagged(TADDR base) const
+diff --git a/src/vm/ceeload.cpp b/src/vm/ceeload.cpp
+index f995343..cd40ad7 100644
+--- a/src/vm/ceeload.cpp
++++ b/src/vm/ceeload.cpp
+@@ -10749,7 +10749,7 @@ void Module::RestoreMethodDescPointer(RelativeFixupPointer<PTR_MethodDesc> * ppM
+ }
+
+ /*static*/
+-void Module::RestoreFieldDescPointer(FixupPointer<PTR_FieldDesc> * ppFD)
++void Module::RestoreFieldDescPointer(RelativeFixupPointer<PTR_FieldDesc> * ppFD)
+ {
+ CONTRACTL
+ {
+@@ -10759,6 +10759,9 @@ void Module::RestoreFieldDescPointer(FixupPointer<PTR_FieldDesc> * ppFD)
+ }
+ CONTRACTL_END;
+
++ if (!ppFD->IsTagged())
++ return;
++
+ PTR_FieldDesc * ppValue = ppFD->GetValuePtr();
+
+ // Ensure that the compiler won't fetch the value twice
+@@ -10770,7 +10773,7 @@ void Module::RestoreFieldDescPointer(FixupPointer<PTR_FieldDesc> * ppFD)
+ CONSISTENCY_CHECK((CORCOMPILE_UNTAG_TOKEN(fixup)>>32) == 0);
+ #endif
+
+- Module * pContainingModule = ExecutionManager::FindZapModule(dac_cast<TADDR>(ppValue));
++ Module * pContainingModule = ExecutionManager::FindZapModule((TADDR)ppValue);
+ PREFIX_ASSUME(pContainingModule != NULL);
+
+ RVA fixupRva = (RVA) CORCOMPILE_UNTAG_TOKEN(fixup);
+diff --git a/src/vm/ceeload.h b/src/vm/ceeload.h
+index dc21eec..fa61089 100644
+--- a/src/vm/ceeload.h
++++ b/src/vm/ceeload.h
+@@ -2904,8 +2904,7 @@ public:
+ static void RestoreMethodDescPointer(RelativeFixupPointer<PTR_MethodDesc> * ppMD,
+ Module *pContainingModule = NULL,
+ ClassLoadLevel level = CLASS_LOADED);
+-
+- static void RestoreFieldDescPointer(FixupPointer<PTR_FieldDesc> * ppFD);
++ static void RestoreFieldDescPointer(RelativeFixupPointer<PTR_FieldDesc> * ppFD);
+
+ static void RestoreModulePointer(RelativeFixupPointer<PTR_Module> * ppModule, Module *pContainingModule);
+
+diff --git a/src/vm/class.cpp b/src/vm/class.cpp
+index 0259b1e..6697b23 100644
+--- a/src/vm/class.cpp
++++ b/src/vm/class.cpp
+@@ -2818,13 +2818,13 @@ void EEClass::Save(DataImage *image, MethodTable *pMT)
+
+ if (pInfo->m_numCTMFields > 0)
+ {
+- ZapStoredStructure * pNode = image->StoreStructure(pInfo->m_pFieldMarshalers,
++ ZapStoredStructure * pNode = image->StoreStructure(pInfo->GetFieldMarshalers(),
+ pInfo->m_numCTMFields * MAXFIELDMARSHALERSIZE,
+ DataImage::ITEM_FIELD_MARSHALERS);
+
+ for (UINT iField = 0; iField < pInfo->m_numCTMFields; iField++)
+ {
+- FieldMarshaler *pFM = (FieldMarshaler*)((BYTE *)pInfo->m_pFieldMarshalers + iField * MAXFIELDMARSHALERSIZE);
++ FieldMarshaler *pFM = (FieldMarshaler*)((BYTE *)pInfo->GetFieldMarshalers() + iField * MAXFIELDMARSHALERSIZE);
+ pFM->Save(image);
+
+ if (iField > 0)
+@@ -3029,11 +3029,11 @@ void EEClass::Fixup(DataImage *image, MethodTable *pMT)
+
+ if (HasLayout())
+ {
+- image->FixupPointerField(this, offsetof(LayoutEEClass, m_LayoutInfo.m_pFieldMarshalers));
++ image->FixupRelativePointerField(this, offsetof(LayoutEEClass, m_LayoutInfo.m_pFieldMarshalers));
+
+ EEClassLayoutInfo *pInfo = &((LayoutEEClass*)this)->m_LayoutInfo;
+
+- FieldMarshaler *pFM = pInfo->m_pFieldMarshalers;
++ FieldMarshaler *pFM = pInfo->GetFieldMarshalers();
+ FieldMarshaler *pFMEnd = (FieldMarshaler*) ((BYTE *)pFM + pInfo->m_numCTMFields*MAXFIELDMARSHALERSIZE);
+ while (pFM < pFMEnd)
+ {
+diff --git a/src/vm/class.h b/src/vm/class.h
+index 13b2e50..1d5f9a2 100644
+--- a/src/vm/class.h
++++ b/src/vm/class.h
+@@ -110,6 +110,7 @@ class LoaderAllocator;
+ class ComCallWrapperTemplate;
+
+ typedef DPTR(DictionaryLayout) PTR_DictionaryLayout;
++typedef DPTR(FieldMarshaler) PTR_FieldMarshaler;
+
+
+ //---------------------------------------------------------------------------------
+@@ -440,7 +441,7 @@ class EEClassLayoutInfo
+ // An array of FieldMarshaler data blocks, used to drive call-time
+ // marshaling of NStruct reference parameters. The number of elements
+ // equals m_numCTMFields.
+- FieldMarshaler *m_pFieldMarshalers;
++ RelativePointer<PTR_FieldMarshaler> m_pFieldMarshalers;
+
+
+ public:
+@@ -469,12 +470,20 @@ class EEClassLayoutInfo
+ return m_numCTMFields;
+ }
+
+- FieldMarshaler *GetFieldMarshalers() const
++ PTR_FieldMarshaler GetFieldMarshalers() const
+ {
+ LIMITED_METHOD_CONTRACT;
+- return m_pFieldMarshalers;
++ return ReadPointerMaybeNull(this, &EEClassLayoutInfo::m_pFieldMarshalers);
+ }
+
++#ifndef DACCESS_COMPILE
++ void SetFieldMarshalers(FieldMarshaler *pFieldMarshallers)
++ {
++ LIMITED_METHOD_CONTRACT;
++ m_pFieldMarshalers.SetValueMaybeNull(pFieldMarshallers);
++ }
++#endif // DACCESS_COMPILE
++
+ BOOL IsBlittable() const
+ {
+ LIMITED_METHOD_CONTRACT;
+diff --git a/src/vm/fieldmarshaler.cpp b/src/vm/fieldmarshaler.cpp
+index 0de71b5..9415b94 100644
+--- a/src/vm/fieldmarshaler.cpp
++++ b/src/vm/fieldmarshaler.cpp
+@@ -1318,7 +1318,7 @@ VOID EEClassLayoutInfo::CollectLayoutFieldMetadataThrowing(
+ }
+
+ pEEClassLayoutInfoOut->m_numCTMFields = fHasNonTrivialParent ? pParentMT->GetLayoutInfo()->m_numCTMFields : 0;
+- pEEClassLayoutInfoOut->m_pFieldMarshalers = NULL;
++ pEEClassLayoutInfoOut->SetFieldMarshalers(NULL);
+ pEEClassLayoutInfoOut->SetIsBlittable(TRUE);
+ if (fHasNonTrivialParent)
+ pEEClassLayoutInfoOut->SetIsBlittable(pParentMT->IsBlittable());
+@@ -1599,7 +1599,7 @@ VOID EEClassLayoutInfo::CollectLayoutFieldMetadataThrowing(
+
+ if (pEEClassLayoutInfoOut->m_numCTMFields)
+ {
+- pEEClassLayoutInfoOut->m_pFieldMarshalers = (FieldMarshaler*)(pamTracker->Track(pAllocator->GetLowFrequencyHeap()->AllocMem(S_SIZE_T(MAXFIELDMARSHALERSIZE) * S_SIZE_T(pEEClassLayoutInfoOut->m_numCTMFields))));
++ pEEClassLayoutInfoOut->SetFieldMarshalers((FieldMarshaler*)(pamTracker->Track(pAllocator->GetLowFrequencyHeap()->AllocMem(S_SIZE_T(MAXFIELDMARSHALERSIZE) * S_SIZE_T(pEEClassLayoutInfoOut->m_numCTMFields)))));
+
+ // Bring in the parent's fieldmarshalers
+ if (fHasNonTrivialParent)
+@@ -1608,8 +1608,8 @@ VOID EEClassLayoutInfo::CollectLayoutFieldMetadataThrowing(
+ PREFAST_ASSUME(pParentLayoutInfo != NULL); // See if (fParentHasLayout) branch above
+
+ UINT numChildCTMFields = pEEClassLayoutInfoOut->m_numCTMFields - pParentLayoutInfo->m_numCTMFields;
+- memcpyNoGCRefs( ((BYTE*)pEEClassLayoutInfoOut->m_pFieldMarshalers) + MAXFIELDMARSHALERSIZE*numChildCTMFields,
+- pParentLayoutInfo->m_pFieldMarshalers,
++ memcpyNoGCRefs( ((BYTE*)pEEClassLayoutInfoOut->GetFieldMarshalers()) + MAXFIELDMARSHALERSIZE*numChildCTMFields,
++ pParentLayoutInfo->GetFieldMarshalers(),
+ MAXFIELDMARSHALERSIZE * (pParentLayoutInfo->m_numCTMFields) );
+ }
+
+@@ -3726,7 +3726,7 @@ VOID FieldMarshaler_SafeArray::UpdateNativeImpl(OBJECTREF* pCLRValue, LPVOID pNa
+ pSafeArray = (LPSAFEARRAY*)pNativeValue;
+
+ VARTYPE vt = m_vt;
+- MethodTable* pMT = m_pMT.GetValue();
++ MethodTable* pMT = m_pMT.GetValueMaybeNull();
+
+ GCPROTECT_BEGIN(pArray)
+ {
+@@ -3771,7 +3771,7 @@ VOID FieldMarshaler_SafeArray::UpdateCLRImpl(const VOID *pNativeValue, OBJECTREF
+ }
+
+ VARTYPE vt = m_vt;
+- MethodTable* pMT = m_pMT.GetValue();
++ MethodTable* pMT = m_pMT.GetValueMaybeNull();
+
+ // If we have an empty vartype, get it from the safearray vartype
+ if (vt == VT_EMPTY)
+@@ -4868,3 +4868,10 @@ IMPLEMENT_FieldMarshaler_METHOD(void, Restore,
+ (),
+ ,
+ ())
++
++#ifndef DACCESS_COMPILE
++IMPLEMENT_FieldMarshaler_METHOD(VOID, CopyTo,
++ (VOID *pDest, SIZE_T destSize) const,
++ ,
++ (pDest, destSize))
++#endif // !DACCESS_COMPILE
+diff --git a/src/vm/fieldmarshaler.h b/src/vm/fieldmarshaler.h
+index 287da41..f11c81b 100644
+--- a/src/vm/fieldmarshaler.h
++++ b/src/vm/fieldmarshaler.h
+@@ -253,6 +253,28 @@ VOID FmtValueTypeUpdateCLR(LPVOID pProtectedManagedData, MethodTable *pMT, BYTE
+ } \
+ ELEMENT_SIZE_IMPL(NativeSize, AlignmentReq)
+
++#define COPY_TO_IMPL_BASE_STRUCT_ONLY() \
++ VOID CopyToImpl(VOID *pDest, SIZE_T destSize) \
++ { \
++ static_assert(sizeof(*this) == sizeof(FieldMarshaler), \
++ "Please, implement CopyToImpl for correct copy of field values"); \
++ \
++ FieldMarshaler::CopyToImpl(pDest, destSize); \
++ }
++
++#define START_COPY_TO_IMPL(CLASS_NAME) \
++ VOID CopyToImpl(VOID *pDest, SIZE_T destSize) const \
++ { \
++ FieldMarshaler::CopyToImpl(pDest, destSize); \
++ \
++ CLASS_NAME *pDestFieldMarshaller = (std::remove_const<std::remove_pointer<decltype(this)>::type>::type *) pDest; \
++ _ASSERTE(sizeof(*pDestFieldMarshaller) <= destSize); \
++
++#define END_COPY_TO_IMPL(CLASS_NAME) \
++ static_assert(std::is_same<CLASS_NAME *, decltype(pDestFieldMarshaller)>::value, \
++ "Structure's name is required"); \
++ }
++
+
+ //=======================================================================
+ //
+@@ -278,6 +300,7 @@ public:
+ VOID ScalarUpdateCLR(const VOID *pNative, LPVOID pCLR) const;
+ VOID NestedValueClassUpdateNative(const VOID **ppProtectedCLR, SIZE_T startoffset, LPVOID pNative, OBJECTREF *ppCleanupWorkListOnStack) const;
+ VOID NestedValueClassUpdateCLR(const VOID *pNative, LPVOID *ppProtectedCLR, SIZE_T startoffset) const;
++ VOID CopyTo(VOID *pDest, SIZE_T destSize) const;
+ #ifdef FEATURE_PREJIT
+ void Save(DataImage *image);
+ void Fixup(DataImage *image);
+@@ -351,10 +374,21 @@ public:
+ #endif // FEATURE_PREJIT
+ }
+
++ void CopyToImpl(VOID *pDest, SIZE_T destSize) const
++ {
++ FieldMarshaler *pDestFieldMarshaller = (FieldMarshaler *) pDest;
++
++ _ASSERTE(sizeof(*pDestFieldMarshaller) <= destSize);
++
++ pDestFieldMarshaller->SetFieldDesc(GetFieldDesc());
++ pDestFieldMarshaller->SetExternalOffset(GetExternalOffset());
++ pDestFieldMarshaller->SetNStructFieldType(GetNStructFieldType());
++ }
++
+ void SetFieldDesc(FieldDesc* pFD)
+ {
+ LIMITED_METHOD_CONTRACT;
+- m_pFD.SetValue(pFD);
++ m_pFD.SetValueMaybeNull(pFD);
+ }
+
+ FieldDesc* GetFieldDesc() const
+@@ -369,7 +403,7 @@ public:
+ }
+ CONTRACT_END;
+
+- RETURN m_pFD.GetValue();
++ RETURN m_pFD.GetValueMaybeNull();
+ }
+
+ void SetExternalOffset(UINT32 dwExternalOffset)
+@@ -394,7 +428,7 @@ protected:
+ #endif
+ }
+
+- static inline void RestoreHelper(FixupPointer<PTR_MethodTable> *ppMT)
++ static inline void RestoreHelper(RelativeFixupPointer<PTR_MethodTable> *ppMT)
+ {
+ CONTRACTL
+ {
+@@ -414,7 +448,7 @@ protected:
+ }
+
+ #ifdef _DEBUG
+- static inline BOOL IsRestoredHelper(FixupPointer<PTR_MethodTable> pMT)
++ static inline BOOL IsRestoredHelper(const RelativeFixupPointer<PTR_MethodTable> &pMT)
+ {
+ WRAPPER_NO_CONTRACT;
+
+@@ -428,7 +462,7 @@ protected:
+ #endif // _DEBUG
+
+
+- FixupPointer<PTR_FieldDesc> m_pFD; // FieldDesc
++ RelativeFixupPointer<PTR_FieldDesc> m_pFD; // FieldDesc
+ UINT32 m_dwExternalOffset; // offset of field in the fixed portion
+ NStructFieldType m_nft;
+ };
+@@ -449,6 +483,7 @@ public:
+ VOID DestroyNativeImpl(LPVOID pNativeValue) const;
+
+ ELEMENT_SIZE_IMPL(sizeof(BSTR), sizeof(BSTR))
++ COPY_TO_IMPL_BASE_STRUCT_ONLY()
+ };
+
+ //=======================================================================
+@@ -462,6 +497,7 @@ public:
+ VOID DestroyNativeImpl(LPVOID pNativeValue) const;
+
+ ELEMENT_SIZE_IMPL(sizeof(HSTRING), sizeof(HSTRING))
++ COPY_TO_IMPL_BASE_STRUCT_ONLY()
+ };
+
+ //=======================================================================
+@@ -473,7 +509,7 @@ public:
+
+ FieldMarshaler_Nullable(MethodTable* pMT)
+ {
+- m_pNullableTypeMT.SetValue(pMT);
++ m_pNullableTypeMT.SetValueMaybeNull(pMT);
+ }
+
+ BOOL IsNullableMarshalerImpl() const
+@@ -526,6 +562,12 @@ public:
+ FieldMarshaler::RestoreImpl();
+ }
+
++ START_COPY_TO_IMPL(FieldMarshaler_Nullable)
++ {
++ pDestFieldMarshaller->m_pNullableTypeMT.SetValueMaybeNull(GetMethodTable());
++ }
++ END_COPY_TO_IMPL(FieldMarshaler_Nullable)
++
+ #ifdef _DEBUG
+ BOOL IsRestored() const
+ {
+@@ -550,7 +592,7 @@ public:
+ }
+
+ private:
+- FixupPointer<PTR_MethodTable> m_pNullableTypeMT;
++ RelativeFixupPointer<PTR_MethodTable> m_pNullableTypeMT;
+ };
+
+
+@@ -565,6 +607,7 @@ public:
+ VOID DestroyNativeImpl(LPVOID pNativeValue) const;
+
+ ELEMENT_SIZE_IMPL(sizeof(HSTRING), sizeof(HSTRING))
++ COPY_TO_IMPL_BASE_STRUCT_ONLY()
+ };
+
+ //=======================================================================
+@@ -578,6 +621,7 @@ public:
+ VOID UpdateCLRImpl(const VOID * pNativeValue, OBJECTREF * ppProtectedCLRValue, OBJECTREF * ppProtectedOldCLRValue) const;
+
+ ELEMENT_SIZE_IMPL(sizeof(HRESULT), sizeof(HRESULT))
++ COPY_TO_IMPL_BASE_STRUCT_ONLY()
+ };
+
+ #endif // FEATURE_COMINTEROP
+@@ -593,7 +637,7 @@ public:
+ FieldMarshaler_NestedLayoutClass(MethodTable *pMT)
+ {
+ WRAPPER_NO_CONTRACT;
+- m_pNestedMethodTable.SetValue(pMT);
++ m_pNestedMethodTable.SetValueMaybeNull(pMT);
+ }
+
+ VOID UpdateNativeImpl(OBJECTREF* pCLRValue, LPVOID pNativeValue, OBJECTREF *ppCleanupWorkListOnStack) const;
+@@ -629,6 +673,12 @@ public:
+ FieldMarshaler::RestoreImpl();
+ }
+
++ START_COPY_TO_IMPL(FieldMarshaler_NestedLayoutClass)
++ {
++ pDestFieldMarshaller->m_pNestedMethodTable.SetValueMaybeNull(GetMethodTable());
++ }
++ END_COPY_TO_IMPL(FieldMarshaler_NestedLayoutClass)
++
+ #ifdef _DEBUG
+ BOOL IsRestored() const
+ {
+@@ -649,12 +699,12 @@ public:
+ }
+ CONTRACTL_END;
+
+- return m_pNestedMethodTable.GetValue();
++ return m_pNestedMethodTable.GetValueMaybeNull();
+ }
+
+ private:
+ // MethodTable of nested FieldMarshaler.
+- FixupPointer<PTR_MethodTable> m_pNestedMethodTable;
++ RelativeFixupPointer<PTR_MethodTable> m_pNestedMethodTable;
+ };
+
+
+@@ -667,7 +717,7 @@ public:
+ FieldMarshaler_NestedValueClass(MethodTable *pMT)
+ {
+ WRAPPER_NO_CONTRACT;
+- m_pNestedMethodTable.SetValue(pMT);
++ m_pNestedMethodTable.SetValueMaybeNull(pMT);
+ }
+
+ BOOL IsNestedValueClassMarshalerImpl() const
+@@ -712,6 +762,12 @@ public:
+ FieldMarshaler::RestoreImpl();
+ }
+
++ START_COPY_TO_IMPL(FieldMarshaler_NestedValueClass)
++ {
++ pDestFieldMarshaller->m_pNestedMethodTable.SetValueMaybeNull(GetMethodTable());
++ }
++ END_COPY_TO_IMPL(FieldMarshaler_NestedValueClass)
++
+ #ifdef _DEBUG
+ BOOL IsRestored() const
+ {
+@@ -738,13 +794,13 @@ public:
+ }
+ CONTRACTL_END;
+
+- return m_pNestedMethodTable.GetValue();
++ return m_pNestedMethodTable.GetValueMaybeNull();
+ }
+
+
+ private:
+ // MethodTable of nested NStruct.
+- FixupPointer<PTR_MethodTable> m_pNestedMethodTable;
++ RelativeFixupPointer<PTR_MethodTable> m_pNestedMethodTable;
+ };
+
+
+@@ -760,6 +816,7 @@ public:
+ VOID DestroyNativeImpl(LPVOID pNativeValue) const;
+
+ ELEMENT_SIZE_IMPL(sizeof(LPWSTR), sizeof(LPWSTR))
++ COPY_TO_IMPL_BASE_STRUCT_ONLY()
+ };
+
+ //=======================================================================
+@@ -774,6 +831,7 @@ public:
+ VOID DestroyNativeImpl(LPVOID pNativeValue) const;
+
+ ELEMENT_SIZE_IMPL(sizeof(LPSTR), sizeof(LPSTR))
++ COPY_TO_IMPL_BASE_STRUCT_ONLY()
+ };
+
+ //=======================================================================
+@@ -806,6 +864,13 @@ public:
+ return m_ThrowOnUnmappableChar;
+ }
+
++ START_COPY_TO_IMPL(FieldMarshaler_StringAnsi)
++ {
++ pDestFieldMarshaller->m_BestFitMap = m_BestFitMap;
++ pDestFieldMarshaller->m_ThrowOnUnmappableChar = m_ThrowOnUnmappableChar;
++ }
++ END_COPY_TO_IMPL(FieldMarshaler_StringAnsi)
++
+ private:
+ bool m_BestFitMap:1;
+ bool m_ThrowOnUnmappableChar:1;
+@@ -829,6 +894,12 @@ public:
+ m_numchar = numChar;
+ }
+
++ START_COPY_TO_IMPL(FieldMarshaler_FixedStringUni)
++ {
++ pDestFieldMarshaller->m_numchar = m_numchar;
++ }
++ END_COPY_TO_IMPL(FieldMarshaler_FixedStringUni)
++
+ private:
+ // # of characters for fixed strings
+ UINT32 m_numchar;
+@@ -864,6 +935,14 @@ public:
+ return m_ThrowOnUnmappableChar;
+ }
+
++ START_COPY_TO_IMPL(FieldMarshaler_FixedStringAnsi)
++ {
++ pDestFieldMarshaller->m_numchar = m_numchar;
++ pDestFieldMarshaller->m_BestFitMap = m_BestFitMap;
++ pDestFieldMarshaller->m_ThrowOnUnmappableChar = m_ThrowOnUnmappableChar;
++ }
++ END_COPY_TO_IMPL(FieldMarshaler_FixedStringAnsi)
++
+ private:
+ // # of characters for fixed strings
+ UINT32 m_numchar;
+@@ -901,6 +980,14 @@ public:
+ return m_ThrowOnUnmappableChar;
+ }
+
++ START_COPY_TO_IMPL(FieldMarshaler_FixedCharArrayAnsi)
++ {
++ pDestFieldMarshaller->m_numElems = m_numElems;
++ pDestFieldMarshaller->m_BestFitMap = m_BestFitMap;
++ pDestFieldMarshaller->m_ThrowOnUnmappableChar = m_ThrowOnUnmappableChar;
++ }
++ END_COPY_TO_IMPL(FieldMarshaler_FixedCharArrayAnsi)
++
+ private:
+ // # of elements for fixedchararray
+ UINT32 m_numElems;
+@@ -980,6 +1067,16 @@ public:
+ FieldMarshaler::RestoreImpl();
+ }
+
++ START_COPY_TO_IMPL(FieldMarshaler_FixedArray)
++ {
++ pDestFieldMarshaller->m_arrayType.SetValueMaybeNull(m_arrayType.GetValueMaybeNull());
++ pDestFieldMarshaller->m_numElems = m_numElems;
++ pDestFieldMarshaller->m_vt = m_vt;
++ pDestFieldMarshaller->m_BestFitMap = m_BestFitMap;
++ pDestFieldMarshaller->m_ThrowOnUnmappableChar = m_ThrowOnUnmappableChar;
++ }
++ END_COPY_TO_IMPL(FieldMarshaler_FixedArray)
++
+ #ifdef _DEBUG
+ BOOL IsRestored() const
+ {
+@@ -994,7 +1091,7 @@ public:
+ #endif
+
+ private:
+- FixupPointer<TypeHandle> m_arrayType;
++ RelativeFixupPointer<TypeHandle> m_arrayType;
+ UINT32 m_numElems;
+ VARTYPE m_vt;
+ bool m_BestFitMap:1; // Note: deliberately use small bools to save on working set - this is the largest FieldMarshaler and dominates the cost of the FieldMarshaler array
+@@ -1020,7 +1117,7 @@ public:
+ {
+ WRAPPER_NO_CONTRACT;
+ m_vt = vt;
+- m_pMT.SetValue(pMT);
++ m_pMT.SetValueMaybeNull(pMT);
+ }
+
+ #ifdef FEATURE_PREJIT
+@@ -1049,6 +1146,13 @@ public:
+ FieldMarshaler::RestoreImpl();
+ }
+
++ START_COPY_TO_IMPL(FieldMarshaler_SafeArray)
++ {
++ pDestFieldMarshaller->m_pMT.SetValueMaybeNull(m_pMT.GetValueMaybeNull());
++ pDestFieldMarshaller->m_vt = m_vt;
++ }
++ END_COPY_TO_IMPL(FieldMarshaler_SafeArray)
++
+ #ifdef _DEBUG
+ BOOL IsRestored() const
+ {
+@@ -1079,7 +1183,7 @@ public:
+ }
+
+ private:
+- FixupPointer<PTR_MethodTable> m_pMT;
++ RelativeFixupPointer<PTR_MethodTable> m_pMT;
+ VARTYPE m_vt;
+ };
+ #endif //FEATURE_CLASSIC_COMINTEROP
+@@ -1094,7 +1198,7 @@ public:
+ FieldMarshaler_Delegate(MethodTable* pMT)
+ {
+ WRAPPER_NO_CONTRACT;
+- m_pNestedMethodTable.SetValue(pMT);
++ m_pNestedMethodTable.SetValueMaybeNull(pMT);
+ }
+
+ VOID UpdateNativeImpl(OBJECTREF* pCLRValue, LPVOID pNativeValue, OBJECTREF *ppCleanupWorkListOnStack) const;
+@@ -1128,6 +1232,12 @@ public:
+ FieldMarshaler::RestoreImpl();
+ }
+
++ START_COPY_TO_IMPL(FieldMarshaler_Delegate)
++ {
++ pDestFieldMarshaller->m_pNestedMethodTable.SetValueMaybeNull(m_pNestedMethodTable.GetValueMaybeNull());
++ }
++ END_COPY_TO_IMPL(FieldMarshaler_Delegate)
++
+ #ifdef _DEBUG
+ BOOL IsRestored() const
+ {
+@@ -1148,10 +1258,10 @@ public:
+ }
+ CONTRACTL_END;
+
+- return m_pNestedMethodTable.GetValue();
++ return m_pNestedMethodTable.GetValueMaybeNull();
+ }
+
+- FixupPointer<PTR_MethodTable> m_pNestedMethodTable;
++ RelativeFixupPointer<PTR_MethodTable> m_pNestedMethodTable;
+ };
+
+
+@@ -1168,6 +1278,7 @@ public:
+ VOID UpdateCLRImpl(const VOID *pNativeValue, OBJECTREF *ppProtectedCLRValue, OBJECTREF *ppProtectedOldCLRValue) const;
+
+ ELEMENT_SIZE_IMPL(sizeof(LPVOID), sizeof(LPVOID))
++ COPY_TO_IMPL_BASE_STRUCT_ONLY()
+ };
+
+
+@@ -1184,6 +1295,7 @@ public:
+ VOID UpdateCLRImpl(const VOID *pNativeValue, OBJECTREF *ppProtectedCLRValue, OBJECTREF *ppProtectedOldCLRValue) const;
+
+ ELEMENT_SIZE_IMPL(sizeof(LPVOID), sizeof(LPVOID))
++ COPY_TO_IMPL_BASE_STRUCT_ONLY()
+ };
+
+ #ifdef FEATURE_COMINTEROP
+@@ -1204,8 +1316,8 @@ public:
+ FieldMarshaler_Interface(MethodTable *pClassMT, MethodTable *pItfMT, DWORD dwFlags)
+ {
+ WRAPPER_NO_CONTRACT;
+- m_pClassMT.SetValue(pClassMT);
+- m_pItfMT.SetValue(pItfMT);
++ m_pClassMT.SetValueMaybeNull(pClassMT);
++ m_pItfMT.SetValueMaybeNull(pItfMT);
+ m_dwFlags = dwFlags;
+ }
+
+@@ -1237,6 +1349,14 @@ public:
+ FieldMarshaler::RestoreImpl();
+ }
+
++ START_COPY_TO_IMPL(FieldMarshaler_Interface)
++ {
++ pDestFieldMarshaller->m_pClassMT.SetValueMaybeNull(m_pClassMT.GetValueMaybeNull());
++ pDestFieldMarshaller->m_pItfMT.SetValueMaybeNull(m_pItfMT.GetValueMaybeNull());
++ pDestFieldMarshaller->m_dwFlags = m_dwFlags;
++ }
++ END_COPY_TO_IMPL(FieldMarshaler_Interface)
++
+ #ifdef _DEBUG
+ BOOL IsRestored() const
+ {
+@@ -1275,7 +1395,7 @@ public:
+ }
+ CONTRACTL_END;
+
+- return m_pClassMT.GetValue();
++ return m_pClassMT.GetValueMaybeNull();
+ }
+
+ MethodTable *GetInterfaceMethodTable() const
+@@ -1289,12 +1409,12 @@ public:
+ }
+ CONTRACTL_END;
+
+- return m_pItfMT.GetValue();
++ return m_pItfMT.GetValueMaybeNull();
+ }
+
+ private:
+- FixupPointer<PTR_MethodTable> m_pClassMT;
+- FixupPointer<PTR_MethodTable> m_pItfMT;
++ RelativeFixupPointer<PTR_MethodTable> m_pClassMT;
++ RelativeFixupPointer<PTR_MethodTable> m_pItfMT;
+ DWORD m_dwFlags;
+ };
+
+@@ -1328,6 +1448,7 @@ public:
+ VOID DestroyNativeImpl(LPVOID pNativeValue) const;
+
+ ELEMENT_SIZE_IMPL(sizeof(VARIANT), 8)
++ COPY_TO_IMPL_BASE_STRUCT_ONLY()
+ };
+
+ #endif // FEATURE_COMINTEROP
+@@ -1352,7 +1473,13 @@ public:
+ VOID ScalarUpdateCLRImpl(const VOID *pNative, LPVOID pCLR) const;
+
+ SCALAR_MARSHALER_IMPL(1, 1)
+-
++
++ START_COPY_TO_IMPL(FieldMarshaler_Illegal)
++ {
++ pDestFieldMarshaller->m_resIDWhy = m_resIDWhy;
++ }
++ END_COPY_TO_IMPL(FieldMarshaler_Illegal)
++
+ private:
+ UINT m_resIDWhy;
+ };
+@@ -1369,6 +1496,7 @@ public:
+ UNUSED_METHOD_IMPL(VOID UpdateCLRImpl(const VOID *pNativeValue, OBJECTREF *ppProtectedCLRValue, OBJECTREF *ppProtectedOldCLRValue) const)
+
+ SCALAR_MARSHALER_IMPL(1, 1)
++ COPY_TO_IMPL_BASE_STRUCT_ONLY()
+
+ VOID ScalarUpdateNativeImpl(LPVOID pCLR, LPVOID pNative) const
+ {
+@@ -1413,6 +1541,7 @@ public:
+ UNUSED_METHOD_IMPL(VOID UpdateCLRImpl(const VOID *pNativeValue, OBJECTREF *ppProtectedCLRValue, OBJECTREF *ppProtectedOldCLRValue) const)
+
+ SCALAR_MARSHALER_IMPL(2, 2)
++ COPY_TO_IMPL_BASE_STRUCT_ONLY()
+
+ VOID ScalarUpdateNativeImpl(LPVOID pCLR, LPVOID pNative) const
+ {
+@@ -1456,6 +1585,7 @@ public:
+ UNUSED_METHOD_IMPL(VOID UpdateCLRImpl(const VOID *pNativeValue, OBJECTREF *ppProtectedCLRValue, OBJECTREF *ppProtectedOldCLRValue) const)
+
+ SCALAR_MARSHALER_IMPL(4, 4)
++ COPY_TO_IMPL_BASE_STRUCT_ONLY()
+
+ VOID ScalarUpdateNativeImpl(LPVOID pCLR, LPVOID pNative) const
+ {
+@@ -1499,6 +1629,7 @@ public:
+ UNUSED_METHOD_IMPL(VOID UpdateCLRImpl(const VOID *pNativeValue, OBJECTREF *ppProtectedCLRValue, OBJECTREF *ppProtectedOldCLRValue) const)
+
+ SCALAR_MARSHALER_IMPL(8, 8)
++ COPY_TO_IMPL_BASE_STRUCT_ONLY()
+
+ VOID ScalarUpdateNativeImpl(LPVOID pCLR, LPVOID pNative) const
+ {
+@@ -1599,6 +1730,13 @@ public:
+ return m_ThrowOnUnmappableChar;
+ }
+
++ START_COPY_TO_IMPL(FieldMarshaler_Ansi)
++ {
++ pDestFieldMarshaller->m_BestFitMap = m_BestFitMap;
++ pDestFieldMarshaller->m_ThrowOnUnmappableChar = m_ThrowOnUnmappableChar;
++ }
++ END_COPY_TO_IMPL(FieldMarshaler_Ansi)
++
+ private:
+ bool m_BestFitMap:1;
+ bool m_ThrowOnUnmappableChar:1;
+@@ -1614,6 +1752,7 @@ public:
+ UNUSED_METHOD_IMPL(VOID UpdateCLRImpl(const VOID *pNativeValue, OBJECTREF *ppProtectedCLRValue, OBJECTREF *ppProtectedOldCLRValue) const)
+
+ SCALAR_MARSHALER_IMPL(sizeof(BOOL), sizeof(BOOL))
++ COPY_TO_IMPL_BASE_STRUCT_ONLY()
+
+ VOID ScalarUpdateNativeImpl(LPVOID pCLR, LPVOID pNative) const
+ {
+@@ -1661,6 +1800,7 @@ public:
+ UNUSED_METHOD_IMPL(VOID UpdateCLRImpl(const VOID *pNativeValue, OBJECTREF *ppProtectedCLRValue, OBJECTREF *ppProtectedOldCLRValue) const)
+
+ SCALAR_MARSHALER_IMPL(sizeof(VARIANT_BOOL), sizeof(VARIANT_BOOL))
++ COPY_TO_IMPL_BASE_STRUCT_ONLY()
+
+ VOID ScalarUpdateNativeImpl(LPVOID pCLR, LPVOID pNative) const
+ {
+@@ -1711,6 +1851,7 @@ public:
+ UNUSED_METHOD_IMPL(VOID UpdateCLRImpl(const VOID *pNativeValue, OBJECTREF *ppProtectedCLRValue, OBJECTREF *ppProtectedOldCLRValue) const)
+
+ SCALAR_MARSHALER_IMPL(1, 1)
++ COPY_TO_IMPL_BASE_STRUCT_ONLY()
+
+ VOID ScalarUpdateNativeImpl(LPVOID pCLR, LPVOID pNative) const
+ {
+@@ -1752,6 +1893,7 @@ public:
+ UNUSED_METHOD_IMPL(VOID UpdateCLRImpl(const VOID *pNativeValue, OBJECTREF *ppProtectedCLRValue, OBJECTREF *ppProtectedOldCLRValue) const)
+
+ SCALAR_MARSHALER_IMPL(sizeof(DECIMAL), 8);
++ COPY_TO_IMPL_BASE_STRUCT_ONLY()
+
+ VOID ScalarUpdateNativeImpl(LPVOID pCLR, LPVOID pNative) const
+ {
+@@ -1793,6 +1935,7 @@ public:
+ UNUSED_METHOD_IMPL(VOID UpdateCLRImpl(const VOID *pNativeValue, OBJECTREF *ppProtectedCLRValue, OBJECTREF *ppProtectedOldCLRValue) const)
+
+ SCALAR_MARSHALER_IMPL(sizeof(DATE), sizeof(DATE))
++ COPY_TO_IMPL_BASE_STRUCT_ONLY()
+
+ VOID ScalarUpdateNativeImpl(LPVOID pCLR, LPVOID pNative) const;
+ VOID ScalarUpdateCLRImpl(const VOID *pNative, LPVOID pCLR) const;
+@@ -1811,6 +1954,7 @@ public:
+ UNUSED_METHOD_IMPL(VOID UpdateCLRImpl(const VOID *pNativeValue, OBJECTREF *ppProtectedCLRValue, OBJECTREF *ppProtectedOldCLRValue) const)
+
+ SCALAR_MARSHALER_IMPL(sizeof(CURRENCY), sizeof(CURRENCY))
++ COPY_TO_IMPL_BASE_STRUCT_ONLY()
+
+ VOID ScalarUpdateNativeImpl(LPVOID pCLR, LPVOID pNative) const;
+ VOID ScalarUpdateCLRImpl(const VOID *pNative, LPVOID pCLR) const;
+@@ -1825,6 +1969,7 @@ public:
+ UNUSED_METHOD_IMPL(VOID UpdateCLRImpl(const VOID *pNativeValue, OBJECTREF *ppProtectedCLRValue, OBJECTREF *ppProtectedOldCLRValue) const)
+
+ SCALAR_MARSHALER_IMPL(sizeof(INT64), sizeof(INT64))
++ COPY_TO_IMPL_BASE_STRUCT_ONLY()
+
+ VOID ScalarUpdateNativeImpl(LPVOID pCLR, LPVOID pNative) const;
+ VOID ScalarUpdateCLRImpl(const VOID *pNative, LPVOID pCLR) const;
+diff --git a/src/vm/methodtablebuilder.cpp b/src/vm/methodtablebuilder.cpp
+index a1e9095..e8f3b9c 100644
+--- a/src/vm/methodtablebuilder.cpp
++++ b/src/vm/methodtablebuilder.cpp
+@@ -4206,11 +4206,11 @@ VOID MethodTableBuilder::InitializeFieldDescs(FieldDesc *pFieldDescList,
+ {
+ if (pwalk->m_MD == bmtMetaData->pFields[i])
+ {
+-
+ pLayoutFieldInfo = pwalk;
+- CopyMemory(pNextFieldMarshaler,
+- &(pwalk->m_FieldMarshaler),
+- MAXFIELDMARSHALERSIZE);
++
++ const FieldMarshaler *pSrcFieldMarshaler = (const FieldMarshaler *) &pwalk->m_FieldMarshaler;
++
++ pSrcFieldMarshaler->CopyTo(pNextFieldMarshaler, MAXFIELDMARSHALERSIZE);
+
+ pNextFieldMarshaler->SetFieldDesc(pFD);
+ pNextFieldMarshaler->SetExternalOffset(pwalk->m_offset);
+diff --git a/src/vm/typedesc.cpp b/src/vm/typedesc.cpp
+index 6718068..7da1c84 100644
+--- a/src/vm/typedesc.cpp
++++ b/src/vm/typedesc.cpp
+@@ -40,7 +40,7 @@ BOOL ParamTypeDesc::Verify() {
+ STATIC_CONTRACT_DEBUG_ONLY;
+ STATIC_CONTRACT_SUPPORTS_DAC;
+
+- _ASSERTE(m_TemplateMT.IsNull() || m_TemplateMT.GetValue()->SanityCheck());
++ _ASSERTE(m_TemplateMT.IsNull() || GetTemplateMethodTableInternal()->SanityCheck());
+ _ASSERTE(!GetTypeParam().IsNull());
+ BAD_FORMAT_NOTHROW_ASSERT(GetTypeParam().IsTypeDesc() || !GetTypeParam().AsMethodTable()->IsArray());
+ BAD_FORMAT_NOTHROW_ASSERT(CorTypeInfo::IsModifier_NoThrow(GetInternalCorElementType()) ||
+@@ -59,7 +59,7 @@ BOOL ArrayTypeDesc::Verify() {
+ STATIC_CONTRACT_SUPPORTS_DAC;
+
+ // m_TemplateMT == 0 may be null when building types involving TypeVarTypeDesc's
+- BAD_FORMAT_NOTHROW_ASSERT(m_TemplateMT.IsNull() || m_TemplateMT.GetValue()->IsArray());
++ BAD_FORMAT_NOTHROW_ASSERT(m_TemplateMT.IsNull() || GetTemplateMethodTable()->IsArray());
+ BAD_FORMAT_NOTHROW_ASSERT(CorTypeInfo::IsArray_NoThrow(GetInternalCorElementType()));
+ ParamTypeDesc::Verify();
+ return(true);
+@@ -844,7 +844,7 @@ OBJECTREF ParamTypeDesc::GetManagedClassObject()
+ if (OwnsTemplateMethodTable())
+ {
+ // Set the handle on template methodtable as well to make Object.GetType for arrays take the fast path
+- EnsureWritablePages(m_TemplateMT.GetValue()->GetWriteableDataForWrite())->m_hExposedClassObject = m_hExposedClassObject;
++ EnsureWritablePages(GetTemplateMethodTableInternal()->GetWriteableDataForWrite())->m_hExposedClassObject = m_hExposedClassObject;
+ }
+
+ // Log the TypeVarTypeDesc access
+@@ -1011,7 +1011,7 @@ void TypeDesc::DoFullyLoad(Generics::RecursionGraph *pVisited, ClassLoadLevel le
+ // Fully load the template method table
+ if (!pPTD->m_TemplateMT.IsNull())
+ {
+- pPTD->m_TemplateMT.GetValue()->DoFullyLoad(&newVisited, level, pPending, &fBailed, pInstContext);
++ pPTD->GetTemplateMethodTableInternal()->DoFullyLoad(&newVisited, level, pPending, &fBailed, pInstContext);
+ }
+ }
+
+@@ -1189,8 +1189,8 @@ void ParamTypeDesc::Save(DataImage *image)
+ if (OwnsTemplateMethodTable())
+ {
+ // This TypeDesc should be the only one saving this MT
+- _ASSERTE(!image->IsStored(m_TemplateMT.GetValue()));
+- Module::SaveMethodTable(image, m_TemplateMT.GetValue(), 0);
++ _ASSERTE(!image->IsStored(GetTemplateMethodTableInternal()));
++ Module::SaveMethodTable(image, GetTemplateMethodTableInternal(), 0);
+ }
+
+ }
+@@ -1219,8 +1219,8 @@ void ParamTypeDesc::Fixup(DataImage *image)
+ // TypeDesc and the MT are "tightly-knit") In other words if one is present in
+ // an NGEN image then then other will be, and if one is "used" at runtime then
+ // the other will be too.
+- image->FixupPointerField(this, offsetof(ParamTypeDesc, m_TemplateMT));
+- m_TemplateMT.GetValue()->Fixup(image);
++ image->FixupMethodTablePointer(this, &m_TemplateMT);
++ GetTemplateMethodTableInternal()->Fixup(image);
+ }
+ else
+ {
+@@ -1275,14 +1275,14 @@ BOOL ParamTypeDesc::ComputeNeedsRestore(DataImage *image, TypeHandleList *pVisit
+ {
+ if (OwnsTemplateMethodTable())
+ {
+- if (m_TemplateMT.GetValue()->ComputeNeedsRestore(image, pVisited))
++ if (GetTemplateMethodTableInternal()->ComputeNeedsRestore(image, pVisited))
+ {
+ res = TRUE;
+ }
+ }
+ else
+ {
+- if (!image->CanPrerestoreEagerBindToMethodTable(m_TemplateMT.GetValue(), pVisited))
++ if (!image->CanPrerestoreEagerBindToMethodTable(GetTemplateMethodTableInternal(), pVisited))
+ {
+ res = TRUE;
+ }
+@@ -2419,7 +2419,7 @@ ParamTypeDesc::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
+ SUPPORTS_DAC;
+ DAC_ENUM_DTHIS();
+
+- PTR_MethodTable pTemplateMT = m_TemplateMT.GetValue();
++ PTR_MethodTable pTemplateMT = GetTemplateMethodTableInternal();
+ if (pTemplateMT.IsValid())
+ {
+ pTemplateMT->EnumMemoryRegions(flags);
+diff --git a/src/vm/typedesc.h b/src/vm/typedesc.h
+index a8b1c25..3e8b0e6 100644
+--- a/src/vm/typedesc.h
++++ b/src/vm/typedesc.h
+@@ -254,7 +254,7 @@ public:
+
+ LIMITED_METHOD_CONTRACT;
+
+- m_TemplateMT.SetValue(pMT);
++ m_TemplateMT.SetValueMaybeNull(pMT);
+
+ // ParamTypeDescs start out life not fully loaded
+ m_typeAndFlags |= TypeDesc::enum_flag_IsNotFullyLoaded;
+@@ -323,8 +323,13 @@ public:
+ friend class ArrayOpLinker;
+ #endif
+ protected:
++ PTR_MethodTable GetTemplateMethodTableInternal() {
++ WRAPPER_NO_CONTRACT;
++ return ReadPointerMaybeNull(this, &ParamTypeDesc::m_TemplateMT);
++ }
++
+ // the m_typeAndFlags field in TypeDesc tell what kind of parameterized type we have
+- FixupPointer<PTR_MethodTable> m_TemplateMT; // The shared method table, some variants do not use this field (it is null)
++ RelativeFixupPointer<PTR_MethodTable> m_TemplateMT; // The shared method table, some variants do not use this field (it is null)
+ TypeHandle m_Arg; // The type that is being modified
+ LOADERHANDLE m_hExposedClassObject; // handle back to the internal reflection Type object
+ };
+@@ -380,8 +385,8 @@ public:
+ WRAPPER_NO_CONTRACT;
+
+ _ASSERTE(!m_TemplateMT.IsNull());
+- _ASSERTE(m_TemplateMT.GetValue()->IsArray());
+- _ASSERTE(m_TemplateMT.GetValue()->ParentEquals(g_pArrayClass));
++ _ASSERTE(GetTemplateMethodTableInternal()->IsArray());
++ _ASSERTE(GetTemplateMethodTableInternal()->ParentEquals(g_pArrayClass));
+
+ return g_pArrayClass;
+ }
+@@ -416,16 +421,16 @@ public:
+ void Fixup(DataImage *image);
+ #endif
+
+- MethodTable * GetTemplateMethodTable() {
++ PTR_MethodTable GetTemplateMethodTable() {
+ WRAPPER_NO_CONTRACT;
+- MethodTable * pTemplateMT = m_TemplateMT.GetValue();
+- _ASSERTE(pTemplateMT->IsArray());
+- return pTemplateMT;
++ PTR_MethodTable ptrTemplateMT = GetTemplateMethodTableInternal();
++ _ASSERTE(ptrTemplateMT->IsArray());
++ return ptrTemplateMT;
+ }
+
+ TADDR GetTemplateMethodTableMaybeTagged() {
+ WRAPPER_NO_CONTRACT;
+- return m_TemplateMT.GetValueMaybeTagged();
++ return m_TemplateMT.GetValueMaybeTagged(dac_cast<TADDR>(this) + offsetof(ArrayTypeDesc, m_TemplateMT));
+ }
+
+ #ifdef FEATURE_COMINTEROP
+diff --git a/src/vm/typedesc.inl b/src/vm/typedesc.inl
+index 4d7416e..312270e 100644
+--- a/src/vm/typedesc.inl
++++ b/src/vm/typedesc.inl
+@@ -31,7 +31,7 @@ inline PTR_MethodTable TypeDesc::GetMethodTable() {
+ if (GetInternalCorElementType() == ELEMENT_TYPE_VALUETYPE)
+ return dac_cast<PTR_MethodTable>(asParam->m_Arg.AsMethodTable());
+ else
+- return(asParam->m_TemplateMT.GetValue());
++ return(asParam->GetTemplateMethodTableInternal());
+ }
+
+ inline TypeHandle TypeDesc::GetTypeParam() {
+--
+2.7.4
+