diff options
author | Alexander Soldatov <soldatov.a@samsung.com> | 2018-06-09 20:56:03 +0300 |
---|---|---|
committer | Alexander Soldatov <soldatov.a@samsung.com> | 2018-06-09 20:56:03 +0300 |
commit | 52d9b3a052cdf2e486879396e03be44474d3db74 (patch) | |
tree | ed97947d8f78a6de9d85703d793feae7ea909db4 | |
parent | 2258e6eda8ce5e1fa97dc4d7b6d4c231285cba94 (diff) | |
download | coreclr-52d9b3a052cdf2e486879396e03be44474d3db74.tar.gz coreclr-52d9b3a052cdf2e486879396e03be44474d3db74.tar.bz2 coreclr-52d9b3a052cdf2e486879396e03be44474d3db74.zip |
Optimizations for 2nd level of Vtable + different fixes for JIT and runtimesubmit/tizen_4.0_base/20180609.183627accepted/tizen/4.0/base/20180615.171456
- Fix for Memory Profiler on x86 emulator
- Remove relocations for second-level indirection of Vtable
- PEImageLayout: flush instruction cache only for pages with relocations
- Improved collected delegate diagnostic
- Different fixes for stability (correct shootdown, correct signal handling,
fixed value type box optimization, fix to defer removing statements
during opt CSE)
Change-Id: Ic6186a717c8c2b5ceb2c9e48fb5216f3b5a415d9
34 files changed, 5140 insertions, 1 deletions
diff --git a/packaging/0001-Improve-UMEntryThunkCode-Poison-method.patch b/packaging/0001-Improve-UMEntryThunkCode-Poison-method.patch new file mode 100644 index 0000000000..49525b0b59 --- /dev/null +++ b/packaging/0001-Improve-UMEntryThunkCode-Poison-method.patch @@ -0,0 +1,175 @@ +From ce9c04e177ad80997aa0824aaa112ef51f9d0a3b Mon Sep 17 00:00:00 2001 +From: Konstantin Baladurin <k.baladurin@partner.samsung.com> +Date: Wed, 10 Jan 2018 18:26:01 +0300 +Subject: [PATCH 01/47] Improve UMEntryThunkCode::Poison method. + +Improve UMEntryThunkCode::Poison to produce diagnostic message +when collected delegate was called. +--- + src/vm/amd64/cgenamd64.cpp | 13 ++++++++++++- + src/vm/arm/stubs.cpp | 14 ++++++++++++-- + src/vm/arm64/stubs.cpp | 9 +++++++++ + src/vm/dllimportcallback.cpp | 36 ++++++++++++++++++++++++++++++++++++ + src/vm/dllimportcallback.h | 2 ++ + src/vm/i386/cgenx86.cpp | 7 ++++++- + 6 files changed, 77 insertions(+), 4 deletions(-) + +diff --git a/src/vm/amd64/cgenamd64.cpp b/src/vm/amd64/cgenamd64.cpp +index 20dca22..1b20b32 100644 +--- a/src/vm/amd64/cgenamd64.cpp ++++ b/src/vm/amd64/cgenamd64.cpp +@@ -680,7 +680,18 @@ void UMEntryThunkCode::Poison() + } + CONTRACTL_END; + +- m_movR10[0] = X86_INSTR_INT3; ++ m_execstub = (BYTE *)UMEntryThunk::ReportViolation; ++ ++ m_movR10[0] = REX_PREFIX_BASE | REX_OPERAND_SIZE_64BIT; ++#ifdef _WIN32 ++ // mov rcx, pUMEntryThunk // 48 b9 xx xx xx xx xx xx xx xx ++ m_movR10[1] = 0xB9; ++#else ++ // mov rdi, pUMEntryThunk // 48 bf xx xx xx xx xx xx xx xx ++ m_movR10[1] = 0xBF; ++#endif ++ ++ ClrFlushInstructionCache(&m_movR10[0], &m_jmpRAX[3]-&m_movR10[0]); + } + + UMEntryThunk* UMEntryThunk::Decode(LPVOID pCallback) +diff --git a/src/vm/arm/stubs.cpp b/src/vm/arm/stubs.cpp +index 70cc900..a52f0c8 100644 +--- a/src/vm/arm/stubs.cpp ++++ b/src/vm/arm/stubs.cpp +@@ -2523,12 +2523,22 @@ void UMEntryThunkCode::Encode(BYTE* pTargetCode, void* pvSecretParam) + FlushInstructionCache(GetCurrentProcess(),&m_code,sizeof(m_code)); + } + ++#ifndef DACCESS_COMPILE ++ + void UMEntryThunkCode::Poison() + { +- // Insert 'udf 0xff' at the entry point +- m_code[0] = 0xdeff; ++ m_pTargetCode = (TADDR)UMEntryThunk::ReportViolation; ++ ++ // ldr r0, [pc + 8] ++ m_code[0] = 0x4802; ++ // nop ++ m_code[1] = 0xbf00; ++ ++ ClrFlushInstructionCache(&m_code,sizeof(m_code)); + } + ++#endif // DACCESS_COMPILE ++ + ///////////////////////////// UNIMPLEMENTED ////////////////////////////////// + + #ifndef DACCESS_COMPILE +diff --git a/src/vm/arm64/stubs.cpp b/src/vm/arm64/stubs.cpp +index df2124d..a0d90de 100644 +--- a/src/vm/arm64/stubs.cpp ++++ b/src/vm/arm64/stubs.cpp +@@ -1244,11 +1244,20 @@ void UMEntryThunkCode::Encode(BYTE* pTargetCode, void* pvSecretParam) + FlushInstructionCache(GetCurrentProcess(),&m_code,sizeof(m_code)); + } + ++#ifndef DACCESS_COMPILE ++ + void UMEntryThunkCode::Poison() + { ++ m_pTargetCode = (TADDR)UMEntryThunk::ReportViolation; ++ ++ // ldp x16, x0, [x12] ++ m_code[1] = 0xa9400190; + ++ ClrFlushInstructionCache(&m_code,sizeof(m_code)); + } + ++#endif // DACCESS_COMPILE ++ + #ifdef PROFILING_SUPPORTED + #include "proftoeeinterfaceimpl.h" + +diff --git a/src/vm/dllimportcallback.cpp b/src/vm/dllimportcallback.cpp +index 8684c12..c3e6a4e 100644 +--- a/src/vm/dllimportcallback.cpp ++++ b/src/vm/dllimportcallback.cpp +@@ -1167,6 +1167,42 @@ VOID UMEntryThunk::FreeUMEntryThunk(UMEntryThunk* p) + + #endif // CROSSGEN_COMPILE + ++//------------------------------------------------------------------------- ++// This function is used to report error when we call collected delegate. ++// But memory that was allocated for thunk can be reused, due to it this ++// function will not be called in all cases of the collected delegate call, ++// also it may crash while trying to report the problem. ++//------------------------------------------------------------------------- ++VOID __fastcall UMEntryThunk::ReportViolation(UMEntryThunk* pEntryThunk) ++{ ++ CONTRACTL ++ { ++ THROWS; ++ GC_TRIGGERS; ++ MODE_COOPERATIVE; ++ PRECONDITION(CheckPointer(pEntryThunk)); ++ } ++ CONTRACTL_END; ++ ++ MethodDesc* pMethodDesc = pEntryThunk->GetMethod(); ++ ++ SString namespaceOrClassName; ++ SString methodName; ++ SString moduleName; ++ ++ pMethodDesc->GetMethodInfoNoSig(namespaceOrClassName, methodName); ++ moduleName.SetUTF8(pMethodDesc->GetModule()->GetSimpleName()); ++ ++ SString message; ++ ++ message.Printf(W("A callback was made on a garbage collected delegate of type '%s!%s::%s'."), ++ moduleName.GetUnicode(), ++ namespaceOrClassName.GetUnicode(), ++ methodName.GetUnicode()); ++ ++ EEPOLICY_HANDLE_FATAL_ERROR_WITH_MESSAGE(COR_E_FAILFAST, message.GetUnicode()); ++} ++ + UMThunkMarshInfo::~UMThunkMarshInfo() + { + CONTRACTL +diff --git a/src/vm/dllimportcallback.h b/src/vm/dllimportcallback.h +index e79c5f0..5838e49 100644 +--- a/src/vm/dllimportcallback.h ++++ b/src/vm/dllimportcallback.h +@@ -511,6 +511,8 @@ public: + } + #endif + ++ static VOID __fastcall ReportViolation(UMEntryThunk* p); ++ + private: + // The start of the managed code. + // if m_pObjectHandle is non-NULL, this field is still set to help with diagnostic of call on collected delegate crashes +diff --git a/src/vm/i386/cgenx86.cpp b/src/vm/i386/cgenx86.cpp +index b4277db..8db4073 100644 +--- a/src/vm/i386/cgenx86.cpp ++++ b/src/vm/i386/cgenx86.cpp +@@ -1614,7 +1614,12 @@ void UMEntryThunkCode::Poison() + { + LIMITED_METHOD_CONTRACT; + +- m_movEAX = X86_INSTR_INT3; ++ m_execstub = (BYTE*) ((BYTE*)UMEntryThunk::ReportViolation - (4+((BYTE*)&m_execstub))); ++ ++ // mov ecx, imm32 ++ m_movEAX = 0xb9; ++ ++ ClrFlushInstructionCache(GetEntryPoint(),sizeof(UMEntryThunkCode)); + } + + UMEntryThunk* UMEntryThunk::Decode(LPVOID pCallback) +-- +2.7.4 + diff --git a/packaging/0002-UMEntryThunk-store-freed-thunks-into-FIFO-free-list.patch b/packaging/0002-UMEntryThunk-store-freed-thunks-into-FIFO-free-list.patch new file mode 100644 index 0000000000..8ee6d76e31 --- /dev/null +++ b/packaging/0002-UMEntryThunk-store-freed-thunks-into-FIFO-free-list.patch @@ -0,0 +1,158 @@ +From 51ffa6ba338dded7be2a0147a8e477d28b35b159 Mon Sep 17 00:00:00 2001 +From: Konstantin Baladurin <k.baladurin@partner.samsung.com> +Date: Fri, 12 Jan 2018 18:55:10 +0300 +Subject: [PATCH 02/47] UMEntryThunk: store freed thunks into FIFO free list + +Use free list to delay reusing deleted thunks. It improves +collected delegate calls diagnostic. +--- + src/vm/dllimportcallback.cpp | 87 ++++++++++++++++++++++++++++++++++++++++++-- + src/vm/dllimportcallback.h | 10 ++++- + 2 files changed, 91 insertions(+), 6 deletions(-) + +diff --git a/src/vm/dllimportcallback.cpp b/src/vm/dllimportcallback.cpp +index c3e6a4e..fe03f47 100644 +--- a/src/vm/dllimportcallback.cpp ++++ b/src/vm/dllimportcallback.cpp +@@ -33,6 +33,79 @@ struct UM2MThunk_Args + int argLen; + }; + ++class UMEntryThunkFreeList ++{ ++public: ++ UMEntryThunkFreeList(size_t threshold) : ++ m_threshold(threshold), ++ m_count(0), ++ m_pHead(NULL), ++ m_pTail(NULL) ++ { ++ WRAPPER_NO_CONTRACT; ++ ++ m_crst.Init(CrstLeafLock, CRST_UNSAFE_ANYMODE); ++ } ++ ++ UMEntryThunk *GetUMEntryThunk() ++ { ++ WRAPPER_NO_CONTRACT; ++ ++ if (m_count < m_threshold) ++ return NULL; ++ ++ CrstHolder ch(&m_crst); ++ ++ UMEntryThunk *pThunk = m_pHead; ++ ++ if (pThunk == NULL) ++ return NULL; ++ ++ m_pHead = m_pHead->m_pNextFreeThunk; ++ --m_count; ++ ++ return pThunk; ++ } ++ ++ void AddToList(UMEntryThunk *pThunk) ++ { ++ CONTRACTL ++ { ++ NOTHROW; ++ } ++ CONTRACTL_END; ++ ++ CrstHolder ch(&m_crst); ++ ++ if (m_pHead == NULL) ++ { ++ m_pHead = pThunk; ++ m_pTail = pThunk; ++ } ++ else ++ { ++ m_pTail->m_pNextFreeThunk = pThunk; ++ m_pTail = pThunk; ++ } ++ ++ pThunk->m_pNextFreeThunk = NULL; ++ ++ ++m_count; ++ } ++ ++private: ++ // Used to delay reusing freed thunks ++ size_t m_threshold; ++ size_t m_count; ++ UMEntryThunk *m_pHead; ++ UMEntryThunk *m_pTail; ++ CrstStatic m_crst; ++}; ++ ++#define DEFAULT_THUNK_FREE_LIST_THRESHOLD 64 ++ ++static UMEntryThunkFreeList s_thunkFreeList(DEFAULT_THUNK_FREE_LIST_THRESHOLD); ++ + EXTERN_C void STDCALL UM2MThunk_WrapperHelper(void *pThunkArgs, + int argLen, + void *pAddr, +@@ -1111,20 +1184,26 @@ UMEntryThunk* UMEntryThunk::CreateUMEntryThunk() + + UMEntryThunk * p; + +- // On the phone, use loader heap to save memory commit of regular executable heap +- p = (UMEntryThunk *)(void *)SystemDomain::GetGlobalLoaderAllocator()->GetExecutableHeap()->AllocMem(S_SIZE_T(sizeof(UMEntryThunk))); ++ p = s_thunkFreeList.GetUMEntryThunk(); ++ ++ if (p == NULL) ++ p = (UMEntryThunk *)(void *)SystemDomain::GetGlobalLoaderAllocator()->GetExecutableHeap()->AllocMem(S_SIZE_T(sizeof(UMEntryThunk))); + + RETURN p; + } + + void UMEntryThunk::Terminate() + { +- WRAPPER_NO_CONTRACT; ++ CONTRACTL ++ { ++ NOTHROW; ++ } ++ CONTRACTL_END; + + _ASSERTE(!SystemDomain::GetGlobalLoaderAllocator()->GetExecutableHeap()->IsZeroInit()); + m_code.Poison(); + +- SystemDomain::GetGlobalLoaderAllocator()->GetExecutableHeap()->BackoutMem(this, sizeof(UMEntryThunk)); ++ s_thunkFreeList.AddToList(this); + } + + VOID UMEntryThunk::FreeUMEntryThunk(UMEntryThunk* p) +diff --git a/src/vm/dllimportcallback.h b/src/vm/dllimportcallback.h +index 5838e49..d820a76 100644 +--- a/src/vm/dllimportcallback.h ++++ b/src/vm/dllimportcallback.h +@@ -250,6 +250,7 @@ class UMEntryThunk + { + friend class CheckAsmOffsets; + friend class NDirectStubLinker; ++ friend class UMEntryThunkFreeList; + + private: + #ifdef _DEBUG +@@ -526,8 +527,13 @@ private: + // Field is NULL for a static method. + OBJECTHANDLE m_pObjectHandle; + +- // Pointer to the shared structure containing everything else +- PTR_UMThunkMarshInfo m_pUMThunkMarshInfo; ++ union ++ { ++ // Pointer to the shared structure containing everything else ++ PTR_UMThunkMarshInfo m_pUMThunkMarshInfo; ++ // Pointer to the next UMEntryThunk in the free list. Used when it is freed. ++ UMEntryThunk *m_pNextFreeThunk; ++ }; + + ADID m_dwDomainId; // appdomain of module (cached for fast access) + #ifdef _DEBUG +-- +2.7.4 + diff --git a/packaging/0003-dllimportcallback-remove-code-for-CallbackOnCollecte.patch b/packaging/0003-dllimportcallback-remove-code-for-CallbackOnCollecte.patch new file mode 100644 index 0000000000..f7b650569f --- /dev/null +++ b/packaging/0003-dllimportcallback-remove-code-for-CallbackOnCollecte.patch @@ -0,0 +1,153 @@ +From 7244ba7107daea201faac0b6c71b545d77fbcb57 Mon Sep 17 00:00:00 2001 +From: Konstantin Baladurin <k.baladurin@partner.samsung.com> +Date: Fri, 12 Jan 2018 11:46:48 +0300 +Subject: [PATCH 03/47] dllimportcallback: remove code for + CallbackOnCollectedDelegate MDA + +--- + src/vm/dllimportcallback.cpp | 71 -------------------------------------------- + src/vm/dllimportcallback.h | 19 ------------ + 2 files changed, 90 deletions(-) + +diff --git a/src/vm/dllimportcallback.cpp b/src/vm/dllimportcallback.cpp +index fe03f47..8623d46 100644 +--- a/src/vm/dllimportcallback.cpp ++++ b/src/vm/dllimportcallback.cpp +@@ -112,34 +112,6 @@ EXTERN_C void STDCALL UM2MThunk_WrapperHelper(void *pThunkArgs, + UMEntryThunk *pEntryThunk, + Thread *pThread); + +-#ifdef MDA_SUPPORTED +-EXTERN_C void __fastcall CallbackOnCollectedDelegateHelper(UMEntryThunk *pEntryThunk) +-{ +- CONTRACTL +- { +- THROWS; +- GC_TRIGGERS; +- MODE_COOPERATIVE; +- SO_TOLERANT; +- PRECONDITION(CheckPointer(pEntryThunk)); +- } +- CONTRACTL_END; +- +- MdaCallbackOnCollectedDelegate* pProbe = MDA_GET_ASSISTANT(CallbackOnCollectedDelegate); +- +- // This MDA must be active if we generated a call to CallbackOnCollectedDelegateHelper +- _ASSERTE(pProbe); +- +- if (pEntryThunk->IsCollected()) +- { +- INSTALL_UNWIND_AND_CONTINUE_HANDLER; +- pProbe->ReportViolation(pEntryThunk->GetMethod()); +- COMPlusThrow(kNullReferenceException); +- UNINSTALL_UNWIND_AND_CONTINUE_HANDLER; +- } +-} +-#endif // MDA_SUPPORTED +- + // This is used as target of callback from DoADCallBack. It sets up the environment and effectively + // calls back into the thunk that needed to switch ADs. + void UM2MThunk_Wrapper(LPVOID ptr) // UM2MThunk_Args +@@ -412,25 +384,6 @@ VOID UMEntryThunk::CompileUMThunkWorker(UMThunkStubInfo *pInfo, + // would deadlock). + pcpusl->EmitLabel(pDoADCallBackStartLabel); + +- +-#ifdef MDA_SUPPORTED +- if ((pInfo->m_wFlags & umtmlSkipStub) && !(pInfo->m_wFlags & umtmlIsStatic) && +- MDA_GET_ASSISTANT(CallbackOnCollectedDelegate)) +- { +- // save registers +- pcpusl->X86EmitPushReg(kEAXentryThunk); +- pcpusl->X86EmitPushReg(kECXthread); +- +- // CallbackOnCollectedDelegateHelper is a fast call +- pcpusl->X86EmitMovRegReg(kECX, kEAXentryThunk); +- pcpusl->X86EmitCall(pcpusl->NewExternalCodeLabel((LPVOID)CallbackOnCollectedDelegateHelper), 0); +- +- // restore registers +- pcpusl->X86EmitPopReg(kECXthread); +- pcpusl->X86EmitPopReg(kEAXentryThunk); +- } +-#endif +- + // save the thread pointer + pcpusl->X86EmitPushReg(kECXthread); + +@@ -1217,30 +1170,6 @@ VOID UMEntryThunk::FreeUMEntryThunk(UMEntryThunk* p) + } + CONTRACTL_END; + +-#ifdef MDA_SUPPORTED +- MdaCallbackOnCollectedDelegate* pProbe = MDA_GET_ASSISTANT(CallbackOnCollectedDelegate); +- if (pProbe) +- { +- if (p->GetObjectHandle()) +- { +- DestroyLongWeakHandle(p->GetObjectHandle()); +- p->m_pObjectHandle = NULL; +- +- // We are intentionally not reseting m_pManagedTarget here so that +- // it is available for diagnostics of call on collected delegate crashes. +- } +- else +- { +- p->m_pManagedTarget = NULL; +- } +- +- // Add this to the array of delegates to be cleaned up. +- pProbe->AddToList(p); +- +- return; +- } +-#endif +- + p->Terminate(); + } + +diff --git a/src/vm/dllimportcallback.h b/src/vm/dllimportcallback.h +index d820a76..555b737 100644 +--- a/src/vm/dllimportcallback.h ++++ b/src/vm/dllimportcallback.h +@@ -424,13 +424,7 @@ public: + MODE_ANY; + SUPPORTS_DAC; + PRECONDITION(m_state == kRunTimeInited || m_state == kLoadTimeInited); +-#ifdef MDA_SUPPORTED +- // We can return NULL here if the CollectedDelegate probe is on because +- // a collected delegate will have set this field to NULL. +- POSTCONDITION(g_pDebugInterface->ThisIsHelperThread() || MDA_GET_ASSISTANT(CallbackOnCollectedDelegate) || CheckPointer(RETVAL)); +-#else + POSTCONDITION(CheckPointer(RETVAL)); +-#endif + } + CONTRACT_END; + +@@ -503,15 +497,6 @@ public: + + static UMEntryThunk* Decode(LPVOID pCallback); + +-#ifdef MDA_SUPPORTED +- BOOL IsCollected() const +- { +- LIMITED_METHOD_CONTRACT; +- _ASSERTE(m_pMD != NULL && m_pMD->IsEEImpl()); +- return m_pObjectHandle == NULL; +- } +-#endif +- + static VOID __fastcall ReportViolation(UMEntryThunk* p); + + private: +@@ -613,8 +598,4 @@ EXTERN_C void UMThunkStub(void); + void STDCALL LogUMTransition(UMEntryThunk* thunk); + #endif + +-#ifdef MDA_SUPPORTED +-EXTERN_C void __fastcall CallbackOnCollectedDelegateHelper(UMEntryThunk *pEntryThunk); +-#endif // MDA_SUPPORTED +- + #endif //__dllimportcallback_h__ +-- +2.7.4 + diff --git a/packaging/0004-LoaderHeap-remove-LHF_ZEROINIT-option.patch b/packaging/0004-LoaderHeap-remove-LHF_ZEROINIT-option.patch new file mode 100644 index 0000000000..f104f98e8b --- /dev/null +++ b/packaging/0004-LoaderHeap-remove-LHF_ZEROINIT-option.patch @@ -0,0 +1,178 @@ +From 81116227de9850ea4ef4a3aefa911bdea6127d07 Mon Sep 17 00:00:00 2001 +From: Konstantin Baladurin <k.baladurin@partner.samsung.com> +Date: Fri, 12 Jan 2018 19:11:05 +0300 +Subject: [PATCH 04/47] LoaderHeap: remove LHF_ZEROINIT option. + +This option was used for UMEntryThunkCode::Poison. Now we use own free list +to store freed thunks and don't return allocated memory to the LoaderHeap. +So reused thunks are always uninitialized. +--- + src/inc/loaderheap.h | 17 +++++------------ + src/utilcode/loaderheap.cpp | 19 ++++--------------- + src/vm/dllimportcallback.cpp | 1 - + src/vm/loaderallocator.cpp | 3 +-- + 4 files changed, 10 insertions(+), 30 deletions(-) + +diff --git a/src/inc/loaderheap.h b/src/inc/loaderheap.h +index 4333505..5bdbe8c 100644 +--- a/src/inc/loaderheap.h ++++ b/src/inc/loaderheap.h +@@ -288,8 +288,7 @@ protected: + SIZE_T dwReservedRegionSize, + size_t *pPrivatePerfCounter_LoaderBytes = NULL, + RangeList *pRangeList = NULL, +- BOOL fMakeExecutable = FALSE, +- BOOL fZeroInit = TRUE); ++ BOOL fMakeExecutable = FALSE); + + ~UnlockedLoaderHeap(); + #endif +@@ -400,8 +399,6 @@ public: + } + + BOOL IsExecutable(); +- BOOL IsZeroInit(); +- + + public: + #ifdef _DEBUG +@@ -446,16 +443,14 @@ public: + DWORD dwCommitBlockSize, + size_t *pPrivatePerfCounter_LoaderBytes = NULL, + RangeList *pRangeList = NULL, +- BOOL fMakeExecutable = FALSE, +- BOOL fZeroInit = TRUE ++ BOOL fMakeExecutable = FALSE + ) + : UnlockedLoaderHeap(dwReserveBlockSize, + dwCommitBlockSize, + NULL, 0, + pPrivatePerfCounter_LoaderBytes, + pRangeList, +- fMakeExecutable, +- fZeroInit) ++ fMakeExecutable) + { + WRAPPER_NO_CONTRACT; + m_CriticalSection = NULL; +@@ -470,8 +465,7 @@ public: + SIZE_T dwReservedRegionSize, + size_t *pPrivatePerfCounter_LoaderBytes = NULL, + RangeList *pRangeList = NULL, +- BOOL fMakeExecutable = FALSE, +- BOOL fZeroInit = TRUE ++ BOOL fMakeExecutable = FALSE + ) + : UnlockedLoaderHeap(dwReserveBlockSize, + dwCommitBlockSize, +@@ -479,8 +473,7 @@ public: + dwReservedRegionSize, + pPrivatePerfCounter_LoaderBytes, + pRangeList, +- fMakeExecutable, +- fZeroInit) ++ fMakeExecutable) + { + WRAPPER_NO_CONTRACT; + m_CriticalSection = NULL; +diff --git a/src/utilcode/loaderheap.cpp b/src/utilcode/loaderheap.cpp +index 21aa150..4033c86 100644 +--- a/src/utilcode/loaderheap.cpp ++++ b/src/utilcode/loaderheap.cpp +@@ -11,7 +11,6 @@ + #include "eventtracebase.h" + + #define LHF_EXECUTABLE 0x1 +-#define LHF_ZEROINIT 0x2 + + #ifndef DACCESS_COMPILE + +@@ -906,8 +905,7 @@ UnlockedLoaderHeap::UnlockedLoaderHeap(DWORD dwReserveBlockSize, + SIZE_T dwReservedRegionSize, + size_t *pPrivatePerfCounter_LoaderBytes, + RangeList *pRangeList, +- BOOL fMakeExecutable, +- BOOL fZeroInit) ++ BOOL fMakeExecutable) + { + CONTRACTL + { +@@ -946,9 +944,6 @@ UnlockedLoaderHeap::UnlockedLoaderHeap(DWORD dwReserveBlockSize, + m_Options = 0; + if (fMakeExecutable) + m_Options |= LHF_EXECUTABLE; +- if (fZeroInit) +- m_Options |= LHF_ZEROINIT; +- + m_pFirstFreeBlock = NULL; + + if (dwReservedRegionAddress != NULL && dwReservedRegionSize > 0) +@@ -1356,7 +1351,7 @@ again: + // Don't fill the memory we allocated - it is assumed to be zeroed - fill the memory after it + memset(pAllocatedBytes + dwRequestedSize, 0xEE, LOADER_HEAP_DEBUG_BOUNDARY); + #endif +- if ((dwRequestedSize > 0) && (m_Options & LHF_ZEROINIT)) ++ if (dwRequestedSize > 0) + { + _ASSERTE_MSG(pAllocatedBytes[0] == 0 && memcmp(pAllocatedBytes, pAllocatedBytes + 1, dwRequestedSize - 1) == 0, + "LoaderHeap must return zero-initialized memory"); +@@ -1534,8 +1529,7 @@ void UnlockedLoaderHeap::UnlockedBackoutMem(void *pMem, + { + // Cool. This was the last block allocated. We can just undo the allocation instead + // of going to the freelist. +- if (m_Options & LHF_ZEROINIT) +- memset(pMem, 0x00, dwSize); // Fill freed region with 0 ++ memset(pMem, 0x00, dwSize); // Fill freed region with 0 + m_pAllocPtr = (BYTE*)pMem; + } + else +@@ -1653,7 +1647,7 @@ void *UnlockedLoaderHeap::UnlockedAllocAlignedMem_NoThrow(size_t dwRequestedSiz + memset(pAllocatedBytes + dwRequestedSize, 0xee, LOADER_HEAP_DEBUG_BOUNDARY); + #endif + +- if ((dwRequestedSize != 0) && (m_Options & LHF_ZEROINIT)) ++ if (dwRequestedSize != 0) + { + _ASSERTE_MSG(pAllocatedBytes[0] == 0 && memcmp(pAllocatedBytes, pAllocatedBytes + 1, dwRequestedSize - 1) == 0, + "LoaderHeap must return zero-initialized memory"); +@@ -1778,11 +1772,6 @@ BOOL UnlockedLoaderHeap::IsExecutable() + return (m_Options & LHF_EXECUTABLE); + } + +-BOOL UnlockedLoaderHeap::IsZeroInit() +-{ +- return (m_Options & LHF_ZEROINIT); +-} +- + #ifdef DACCESS_COMPILE + + void UnlockedLoaderHeap::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) +diff --git a/src/vm/dllimportcallback.cpp b/src/vm/dllimportcallback.cpp +index 8623d46..2becba5 100644 +--- a/src/vm/dllimportcallback.cpp ++++ b/src/vm/dllimportcallback.cpp +@@ -1153,7 +1153,6 @@ void UMEntryThunk::Terminate() + } + CONTRACTL_END; + +- _ASSERTE(!SystemDomain::GetGlobalLoaderAllocator()->GetExecutableHeap()->IsZeroInit()); + m_code.Poison(); + + s_thunkFreeList.AddToList(this); +diff --git a/src/vm/loaderallocator.cpp b/src/vm/loaderallocator.cpp +index 5a3f8f5..2264dc1 100644 +--- a/src/vm/loaderallocator.cpp ++++ b/src/vm/loaderallocator.cpp +@@ -1005,8 +1005,7 @@ void LoaderAllocator::Init(BaseDomain *pDomain, BYTE *pExecutableHeapMemory) + dwExecutableHeapReserveSize, + LOADERHEAP_PROFILE_COUNTER, + NULL, +- TRUE /* Make heap executable */, +- FALSE /* Disable zero-initialization (needed by UMEntryThunkCode::Poison) */ ++ TRUE /* Make heap executable */ + ); + initReservedMem += dwExecutableHeapReserveSize; + } +-- +2.7.4 + diff --git a/packaging/0019-JIT-Fix-value-type-box-optimization.patch b/packaging/0019-JIT-Fix-value-type-box-optimization.patch new file mode 100644 index 0000000000..db31e3ff8f --- /dev/null +++ b/packaging/0019-JIT-Fix-value-type-box-optimization.patch @@ -0,0 +1,1215 @@ +From dc2e9cc4c21e0cf565dbb1b4d4f029157779ddcc Mon Sep 17 00:00:00 2001 +From: Andy Ayers <andya@microsoft.com> +Date: Sat, 22 Jul 2017 09:16:29 -0700 +Subject: [PATCH 19/47] JIT: Fix value type box optimization + +Boxing a value type produces a non-null result. If the result of the box is +only used to feed a compare against null, the jit tries to optimize the box +away entirely since the result of the comparison is known. Such idiomatic +expressions arise fairly often in generics instantiated over value types. + +In the current implementation the box expands into two parts. The first is +an upstream statement to allocate a boxed object and assign a reference to +the boxed object to a local var known as the "box temp". The second is an +expression tree whose value is the box temp that also contains an an +encapsulated copy from the value being boxed to the payload section of the +boxed object. The box node also contains a pointer back to the first +statement (more on this later). + +In the examples being discussed here this second tree is a child of a compare +node whose other child is a null pointer. When the optimization fires, the +upstream allocation statement is located via the pointer in the box node and +removed, and the entire compare is replaced with a constant 0 or 1 as +appropriate. Unfortunately the encapsulated copy in the box subtree may +include side effects that should be preserved, and so this transformation is +unsafe. + +Note that the copy subtree as a whole will always contain side effects, since +the copy is storing values into the heap, and that copy now will not happen. +But the side effects that happen when producing the value to box must remain. + +In the initial example from #12949 the side effects in question were +introduced by the jit's optimizer to capure a CSE definition. #13016 gives +several other examples where the side effects are present in the initial user +code. For instance the value being boxed might come from an array, in which +case the encapsulated copy in the box expression tree would contain the array +null check and bounds check. So removing the entire tree can alter behavior. + +This fix attempts to carefully preserve the important side effects by +reworking how a box is imported. The copy is now moved out from under the box +into a second upstream statement. The box itself is then just a trivial +side-effect-free reference to the box temp. To ensure proper ordering of side +effects the jit spills the evaluation stack before appending the copy +statement. + +When the optimization fires the jit removes the upstream heap allocation +as before, as well as the now-trivial compare tree. It analyzes the source +side of the upstream copy. If it is side effect free, the copy is removed +entirely. If not, the jit modifies the copy into a minimal load of the +boxed value, and this load should reproduce the necessary side effects. + +The optimization is only performed when the tree shape of the copy matches +expected patterns. + +There are some expected cases where the tree won't match, for instance if the +optimization is invoked while the jit is inlining. Because this optimization +runs at several points the jit can catch these cases once inlining completes. +There is one case that is not handled that could be -- if the assignment part +of the copy is itself a subtree of a comma. This doesn't happen often. + +The optimization is now also extended to handle the case where the comparision +operation is `cgt.un`. This doesn't catch any new cases but causes the +optimization to happen earlier, typically during importation, which should +reduce jit time slightly. + +Generally the split of the box into two upstream statements reduces code size, +especially when the box expression is incorporated into a larger tree -- for +example a call. However in some cases where the value being boxed comes from +an array, preserving the array bounds check now causes loop cloning to kick +in and increase code size. Hence the overall size impact on the jit-diff set is +essentially zero. + +Added a number of new test cases showing the variety of situations that must +be handled and the need to spill before appending the copy statement. + +Fixes #12949. +--- + src/jit/gentree.cpp | 167 +++++++++++++++++++-- + src/jit/gentree.h | 13 +- + src/jit/importer.cpp | 15 +- + .../JitBlue/GitHub_12949/GitHub_12949_1.cs | 51 +++++++ + .../JitBlue/GitHub_12949/GitHub_12949_1.csproj | 43 ++++++ + .../JitBlue/GitHub_12949/GitHub_12949_2.cs | 43 ++++++ + .../JitBlue/GitHub_12949/GitHub_12949_2.csproj | 43 ++++++ + .../JitBlue/GitHub_12949/GitHub_12949_3.cs | 41 +++++ + .../JitBlue/GitHub_12949/GitHub_12949_3.csproj | 43 ++++++ + .../JitBlue/GitHub_12949/GitHub_12949_4.cs | 41 +++++ + .../JitBlue/GitHub_12949/GitHub_12949_4.csproj | 43 ++++++ + .../JitBlue/GitHub_12949/GitHub_12949_5.cs | 46 ++++++ + .../JitBlue/GitHub_12949/GitHub_12949_5.csproj | 43 ++++++ + .../JitBlue/GitHub_12949/GitHub_12949_6.cs | 60 ++++++++ + .../JitBlue/GitHub_12949/GitHub_12949_6.csproj | 43 ++++++ + .../JitBlue/GitHub_12949/GitHub_12949_7.cs | 58 +++++++ + .../JitBlue/GitHub_12949/GitHub_12949_7.csproj | 43 ++++++ + .../JitBlue/GitHub_12949/GitHub_12949_8.cs | 42 ++++++ + .../JitBlue/GitHub_12949/GitHub_12949_8.csproj | 43 ++++++ + 19 files changed, 902 insertions(+), 19 deletions(-) + create mode 100644 tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_1.cs + create mode 100644 tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_1.csproj + create mode 100644 tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_2.cs + create mode 100644 tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_2.csproj + create mode 100644 tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_3.cs + create mode 100644 tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_3.csproj + create mode 100644 tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_4.cs + create mode 100644 tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_4.csproj + create mode 100644 tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_5.cs + create mode 100644 tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_5.csproj + create mode 100644 tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_6.cs + create mode 100644 tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_6.csproj + create mode 100644 tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_7.cs + create mode 100644 tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_7.csproj + create mode 100644 tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_8.cs + create mode 100644 tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_8.csproj + +diff --git a/src/jit/gentree.cpp b/src/jit/gentree.cpp +index 25e9e10..3736f7a 100644 +--- a/src/jit/gentree.cpp ++++ b/src/jit/gentree.cpp +@@ -8050,7 +8050,8 @@ GenTreePtr Compiler::gtCloneExpr( + + case GT_BOX: + copy = new (this, GT_BOX) +- GenTreeBox(tree->TypeGet(), tree->gtOp.gtOp1, tree->gtBox.gtAsgStmtWhenInlinedBoxValue); ++ GenTreeBox(tree->TypeGet(), tree->gtOp.gtOp1, tree->gtBox.gtAsgStmtWhenInlinedBoxValue, ++ tree->gtBox.gtCopyStmtWhenInlinedBoxValue); + break; + + case GT_INTRINSIC: +@@ -12459,32 +12460,168 @@ GenTreePtr Compiler::gtFoldExprSpecial(GenTreePtr tree) + + switch (oper) + { +- + case GT_EQ: + case GT_NE: ++ case GT_GT: + // Optimize boxed value classes; these are always false. This IL is + // generated when a generic value is tested against null: + // <T> ... foo(T x) { ... if ((object)x == null) ... + if (val == 0 && op->IsBoxedValue()) + { +- // Change the assignment node so we don't generate any code for it. ++ // The tree under the box must be side effect free ++ // since we drop it if we optimize the compare. ++ assert(!gtTreeHasSideEffects(op->gtBox.gtOp.gtOp1, GTF_SIDE_EFFECT)); + ++ // grab related parts for the optimization + GenTreePtr asgStmt = op->gtBox.gtAsgStmtWhenInlinedBoxValue; + assert(asgStmt->gtOper == GT_STMT); +- GenTreePtr asg = asgStmt->gtStmt.gtStmtExpr; +- assert(asg->gtOper == GT_ASG); ++ GenTreePtr copyStmt = op->gtBox.gtCopyStmtWhenInlinedBoxValue; ++ assert(copyStmt->gtOper == GT_STMT); + #ifdef DEBUG + if (verbose) + { +- printf("Bashing "); +- printTreeID(asg); +- printf(" to NOP as part of dead box operation\n"); ++ printf("\nAttempting to optimize BOX(valueType) %s null\n", GenTree::OpName(oper)); + gtDispTree(tree); ++ printf("\nWith assign\n"); ++ gtDispTree(asgStmt); ++ printf("\nAnd copy\n"); ++ gtDispTree(copyStmt); + } + #endif ++ ++ // We don't expect GT_GT with signed compares, and we ++ // can't predict the result if we do see it, since the ++ // boxed object addr could have its high bit set. ++ if ((oper == GT_GT) && !tree->IsUnsigned()) ++ { ++ JITDUMP(" bailing; unexpected signed compare via GT_GT\n"); ++ goto FAIL; ++ } ++ ++ // If we don't recognize the form of the assign, bail. ++ GenTreePtr asg = asgStmt->gtStmt.gtStmtExpr; ++ if (asg->gtOper != GT_ASG) ++ { ++ JITDUMP(" bailing; unexpected assignment op %s\n", GenTree::OpName(asg->gtOper)); ++ goto FAIL; ++ } ++ ++ // If we don't recognize the form of the copy, bail. ++ GenTree* copy = copyStmt->gtStmt.gtStmtExpr; ++ if (copy->gtOper != GT_ASG) ++ { ++ // GT_RET_EXPR is a tolerable temporary failure. ++ // The jit will revisit this optimization after ++ // inlining is done. ++ if (copy->gtOper == GT_RET_EXPR) ++ { ++ JITDUMP(" bailing; must wait for replacement of copy %s\n", GenTree::OpName(copy->gtOper)); ++ } ++ else ++ { ++ // Anything else is a missed case we should ++ // figure out how to handle. One known case ++ // is GT_COMMAs enclosing the GT_ASG we are ++ // looking for. ++ JITDUMP(" bailing; unexpected copy op %s\n", GenTree::OpName(copy->gtOper)); ++ } ++ goto FAIL; ++ } ++ ++ // If the copy is a struct copy, make sure we know how to isolate ++ // any source side effects. ++ GenTreePtr copySrc = copy->gtOp.gtOp2; ++ ++ // If the copy source is from a pending inline, wait for it to resolve. ++ if (copySrc->gtOper == GT_RET_EXPR) ++ { ++ JITDUMP(" bailing; must wait for replacement of copy source %s\n", ++ GenTree::OpName(copySrc->gtOper)); ++ goto FAIL; ++ } ++ ++ bool hasSrcSideEffect = false; ++ bool isStructCopy = false; ++ ++ if (gtTreeHasSideEffects(copySrc, GTF_SIDE_EFFECT)) ++ { ++ hasSrcSideEffect = true; ++ ++ if (copySrc->gtType == TYP_STRUCT) ++ { ++ isStructCopy = true; ++ ++ if ((copySrc->gtOper != GT_OBJ) && (copySrc->gtOper != GT_IND) && (copySrc->gtOper != GT_FIELD)) ++ { ++ // We don't know how to handle other cases, yet. ++ JITDUMP(" bailing; unexpected copy source struct op with side effect %s\n", ++ GenTree::OpName(copySrc->gtOper)); ++ goto FAIL; ++ } ++ } ++ } ++ ++ // Proceed with the optimization ++ // ++ // Change the assignment expression to a NOP. ++ JITDUMP("\nBashing NEWOBJ [%06u] to NOP\n", dspTreeID(asg)); + asg->gtBashToNOP(); + +- op = gtNewIconNode(oper == GT_NE); ++ // Change the copy expression so it preserves key ++ // source side effects. ++ JITDUMP("\nBashing COPY [%06u]", dspTreeID(copy)); ++ ++ if (!hasSrcSideEffect) ++ { ++ // If there were no copy source side effects just bash ++ // the copy to a NOP. ++ copy->gtBashToNOP(); ++ JITDUMP(" to NOP\n"); ++ } ++ else if (!isStructCopy) ++ { ++ // For scalar types, go ahead and produce the ++ // value as the copy is fairly cheap and likely ++ // the optimizer can trim things down to just the ++ // minimal side effect parts. ++ copyStmt->gtStmt.gtStmtExpr = copySrc; ++ JITDUMP(" to scalar read via [%06u]\n", dspTreeID(copySrc)); ++ } ++ else ++ { ++ // For struct types read the first byte of the ++ // source struct; there's no need to read the ++ // entire thing, and no place to put it. ++ assert(copySrc->gtOper == GT_OBJ || copySrc->gtOper == GT_IND || copySrc->gtOper == GT_FIELD); ++ copySrc->ChangeOper(GT_IND); ++ copySrc->gtType = TYP_BYTE; ++ copyStmt->gtStmt.gtStmtExpr = copySrc; ++ JITDUMP(" to read first byte of struct via modified [%06u]\n", dspTreeID(copySrc)); ++ } ++ ++ // Set up the result of the compare. ++ int compareResult = 0; ++ if (oper == GT_GT) ++ { ++ // GT_GT(null, box) == false ++ // GT_GT(box, null) == true ++ compareResult = (op1 == op); ++ } ++ else if (oper == GT_EQ) ++ { ++ // GT_EQ(box, null) == false ++ // GT_EQ(null, box) == false ++ compareResult = 0; ++ } ++ else ++ { ++ assert(oper == GT_NE); ++ // GT_NE(box, null) == true ++ // GT_NE(null, box) == true ++ compareResult = 1; ++ } ++ op = gtNewIconNode(compareResult); ++ + if (fgGlobalMorph) + { + if (!fgIsInlining()) +@@ -12497,9 +12634,15 @@ GenTreePtr Compiler::gtFoldExprSpecial(GenTreePtr tree) + op->gtNext = tree->gtNext; + op->gtPrev = tree->gtPrev; + } +- fgSetStmtSeq(asgStmt); ++ ++ if (fgStmtListThreaded) ++ { ++ fgSetStmtSeq(asgStmt); ++ fgSetStmtSeq(copyStmt); ++ } + return op; + } ++ + break; + + case GT_ADD: +@@ -12667,7 +12810,9 @@ GenTreePtr Compiler::gtFoldExprSpecial(GenTreePtr tree) + break; + } + +- /* The node is not foldable */ ++/* The node is not foldable */ ++ ++FAIL: + + return tree; + +diff --git a/src/jit/gentree.h b/src/jit/gentree.h +index 1833a39..41f65f0 100644 +--- a/src/jit/gentree.h ++++ b/src/jit/gentree.h +@@ -2848,9 +2848,16 @@ struct GenTreeBox : public GenTreeUnOp + // This is the statement that contains the assignment tree when the node is an inlined GT_BOX on a value + // type + GenTreePtr gtAsgStmtWhenInlinedBoxValue; +- +- GenTreeBox(var_types type, GenTreePtr boxOp, GenTreePtr asgStmtWhenInlinedBoxValue) +- : GenTreeUnOp(GT_BOX, type, boxOp), gtAsgStmtWhenInlinedBoxValue(asgStmtWhenInlinedBoxValue) ++ // And this is the statement that copies from the value being boxed to the box payload ++ GenTreePtr gtCopyStmtWhenInlinedBoxValue; ++ ++ GenTreeBox(var_types type, ++ GenTreePtr boxOp, ++ GenTreePtr asgStmtWhenInlinedBoxValue, ++ GenTreePtr copyStmtWhenInlinedBoxValue) ++ : GenTreeUnOp(GT_BOX, type, boxOp) ++ , gtAsgStmtWhenInlinedBoxValue(asgStmtWhenInlinedBoxValue) ++ , gtCopyStmtWhenInlinedBoxValue(copyStmtWhenInlinedBoxValue) + { + } + #if DEBUGGABLE_GENTREE +diff --git a/src/jit/importer.cpp b/src/jit/importer.cpp +index 62f1c13..b4ee1d0 100644 +--- a/src/jit/importer.cpp ++++ b/src/jit/importer.cpp +@@ -5225,7 +5225,7 @@ void Compiler::impImportAndPushBox(CORINFO_RESOLVED_TOKEN* pResolvedToken) + gtNewArgList(op2)); + } + +- /* Remember that this basic block contains 'new' of an array */ ++ /* Remember that this basic block contains 'new' of an object */ + compCurBB->bbFlags |= BBF_HAS_NEWOBJ; + + GenTreePtr asg = gtNewTempAssign(impBoxTemp, op1); +@@ -5267,11 +5267,16 @@ void Compiler::impImportAndPushBox(CORINFO_RESOLVED_TOKEN* pResolvedToken) + op1 = gtNewAssignNode(gtNewOperNode(GT_IND, lclTyp, op1), exprToBox); + } + +- op2 = gtNewLclvNode(impBoxTemp, TYP_REF); +- op1 = gtNewOperNode(GT_COMMA, TYP_REF, op1, op2); ++ // Spill eval stack to flush out any pending side effects. ++ impSpillSideEffects(true, (unsigned)CHECK_SPILL_ALL DEBUGARG("impImportAndPushBox")); + +- // Record that this is a "box" node. +- op1 = new (this, GT_BOX) GenTreeBox(TYP_REF, op1, asgStmt); ++ // Set up this copy as a second assignment. ++ GenTreePtr copyStmt = impAppendTree(op1, (unsigned)CHECK_SPILL_NONE, impCurStmtOffs); ++ ++ op1 = gtNewLclvNode(impBoxTemp, TYP_REF); ++ ++ // Record that this is a "box" node and keep track of the matching parts. ++ op1 = new (this, GT_BOX) GenTreeBox(TYP_REF, op1, asgStmt, copyStmt); + + // If it is a value class, mark the "box" node. We can use this information + // to optimise several cases: +diff --git a/tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_1.cs b/tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_1.cs +new file mode 100644 +index 0000000..c436802 +--- /dev/null ++++ b/tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_1.cs +@@ -0,0 +1,51 @@ ++// Licensed to the .NET Foundation under one or more agreements. ++// The .NET Foundation licenses this file to you under the MIT license. ++// See the LICENSE file in the project root for more information. ++ ++using System; ++ ++public struct S<K> ++{ ++ public int x; ++ public int y; ++ public K val; ++} ++ ++public class X<K,V> ++{ ++ public X(K k) ++ { ++ a = new S<K>[2]; ++ a[1].val = k; ++ a[1].x = 3; ++ a[1].y = 4; ++ } ++ ++ public void Assert(bool b) ++ { ++ if (!b) throw new Exception("bad!"); ++ } ++ ++ public int Test() ++ { ++ int r = 0; ++ for (int i = 0; i < a.Length; i++) ++ { ++ Assert(a[i].val != null); ++ r += a[i].val.GetHashCode(); ++ } ++ return r; ++ } ++ ++ S<K>[] a; ++} ++ ++class B ++{ ++ public static int Main() ++ { ++ var a = new X<int, string>(11); ++ int z = a.Test(); ++ return (z == 11 ? 100 : 0); ++ } ++} +diff --git a/tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_1.csproj b/tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_1.csproj +new file mode 100644 +index 0000000..5cd573d +--- /dev/null ++++ b/tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_1.csproj +@@ -0,0 +1,43 @@ ++<?xml version="1.0" encoding="utf-8"?> ++<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> ++ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" /> ++ <PropertyGroup> ++ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> ++ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> ++ <AssemblyName>$(MSBuildProjectName)</AssemblyName> ++ <SchemaVersion>2.0</SchemaVersion> ++ <ProjectGuid>{7B521917-193E-48BB-86C6-FE013F3DFF35}</ProjectGuid> ++ <OutputType>Exe</OutputType> ++ <AppDesignerFolder>Properties</AppDesignerFolder> ++ <FileAlignment>512</FileAlignment> ++ <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> ++ <ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\11.0\UITestExtensionPackages</ReferencePath> ++ <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir> ++ ++ <NuGetPackageImportStamp>7a9bfb7d</NuGetPackageImportStamp> ++ </PropertyGroup> ++ <!-- Default configurations to help VS understand the configurations --> ++ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> ++ </PropertyGroup> ++ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> ++ </PropertyGroup> ++ <ItemGroup> ++ <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies"> ++ <Visible>False</Visible> ++ </CodeAnalysisDependentAssemblyPaths> ++ </ItemGroup> ++ <PropertyGroup> ++ <DebugType></DebugType> ++ <Optimize>True</Optimize> ++ <AllowUnsafeBlocks>True</AllowUnsafeBlocks> ++ </PropertyGroup> ++ <ItemGroup> ++ <Compile Include="$(MSBuildProjectName).cs" /> ++ </ItemGroup> ++ <ItemGroup> ++ <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" /> ++ </ItemGroup> ++ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" /> ++ <PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' "> ++ </PropertyGroup> ++</Project> +diff --git a/tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_2.cs b/tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_2.cs +new file mode 100644 +index 0000000..bbdc67d +--- /dev/null ++++ b/tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_2.cs +@@ -0,0 +1,43 @@ ++// Licensed to the .NET Foundation under one or more agreements. ++// The .NET Foundation licenses this file to you under the MIT license. ++// See the LICENSE file in the project root for more information. ++ ++using System; ++using System.Runtime.CompilerServices; ++ ++public class X<K> ++{ ++ public X(K k1) ++ { ++ k = k1; ++ } ++ ++ [MethodImpl(MethodImplOptions.NoInlining)] ++ public K Get() ++ { ++ Console.WriteLine("Get called"); ++ count++; ++ return k; ++ } ++ ++ public bool Test() ++ { ++ bool x = Get() != null; ++ bool y = (count == 1); ++ return x && y; ++ } ++ ++ K k; ++ int count; ++} ++ ++class B ++{ ++ public static int Main() ++ { ++ var a = new X<int>(11); ++ bool result = a.Test(); ++ Console.WriteLine("Passed: {0}", result); ++ return result ? 100 : 0; ++ } ++} +diff --git a/tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_2.csproj b/tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_2.csproj +new file mode 100644 +index 0000000..5cd573d +--- /dev/null ++++ b/tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_2.csproj +@@ -0,0 +1,43 @@ ++<?xml version="1.0" encoding="utf-8"?> ++<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> ++ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" /> ++ <PropertyGroup> ++ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> ++ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> ++ <AssemblyName>$(MSBuildProjectName)</AssemblyName> ++ <SchemaVersion>2.0</SchemaVersion> ++ <ProjectGuid>{7B521917-193E-48BB-86C6-FE013F3DFF35}</ProjectGuid> ++ <OutputType>Exe</OutputType> ++ <AppDesignerFolder>Properties</AppDesignerFolder> ++ <FileAlignment>512</FileAlignment> ++ <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> ++ <ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\11.0\UITestExtensionPackages</ReferencePath> ++ <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir> ++ ++ <NuGetPackageImportStamp>7a9bfb7d</NuGetPackageImportStamp> ++ </PropertyGroup> ++ <!-- Default configurations to help VS understand the configurations --> ++ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> ++ </PropertyGroup> ++ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> ++ </PropertyGroup> ++ <ItemGroup> ++ <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies"> ++ <Visible>False</Visible> ++ </CodeAnalysisDependentAssemblyPaths> ++ </ItemGroup> ++ <PropertyGroup> ++ <DebugType></DebugType> ++ <Optimize>True</Optimize> ++ <AllowUnsafeBlocks>True</AllowUnsafeBlocks> ++ </PropertyGroup> ++ <ItemGroup> ++ <Compile Include="$(MSBuildProjectName).cs" /> ++ </ItemGroup> ++ <ItemGroup> ++ <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" /> ++ </ItemGroup> ++ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" /> ++ <PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' "> ++ </PropertyGroup> ++</Project> +diff --git a/tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_3.cs b/tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_3.cs +new file mode 100644 +index 0000000..b7247c1 +--- /dev/null ++++ b/tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_3.cs +@@ -0,0 +1,41 @@ ++// Licensed to the .NET Foundation under one or more agreements. ++// The .NET Foundation licenses this file to you under the MIT license. ++// See the LICENSE file in the project root for more information. ++ ++using System; ++using System.Runtime.CompilerServices; ++ ++public class X<K> ++{ ++ public X(K k1) ++ { ++ k = k1; ++ } ++ ++ [MethodImpl(MethodImplOptions.NoInlining)] ++ public static bool Test(X<K> a) ++ { ++ return (a.k != null); ++ } ++ ++ public K k; ++} ++ ++class B ++{ ++ public static int Main() ++ { ++ X<int> a = null; ++ bool result = false; ++ try ++ { ++ X<int>.Test(a); ++ } ++ catch (Exception) ++ { ++ result = true; ++ } ++ Console.WriteLine("Passed: {0}", result); ++ return result ? 100 : 0; ++ } ++} +diff --git a/tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_3.csproj b/tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_3.csproj +new file mode 100644 +index 0000000..5cd573d +--- /dev/null ++++ b/tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_3.csproj +@@ -0,0 +1,43 @@ ++<?xml version="1.0" encoding="utf-8"?> ++<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> ++ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" /> ++ <PropertyGroup> ++ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> ++ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> ++ <AssemblyName>$(MSBuildProjectName)</AssemblyName> ++ <SchemaVersion>2.0</SchemaVersion> ++ <ProjectGuid>{7B521917-193E-48BB-86C6-FE013F3DFF35}</ProjectGuid> ++ <OutputType>Exe</OutputType> ++ <AppDesignerFolder>Properties</AppDesignerFolder> ++ <FileAlignment>512</FileAlignment> ++ <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> ++ <ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\11.0\UITestExtensionPackages</ReferencePath> ++ <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir> ++ ++ <NuGetPackageImportStamp>7a9bfb7d</NuGetPackageImportStamp> ++ </PropertyGroup> ++ <!-- Default configurations to help VS understand the configurations --> ++ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> ++ </PropertyGroup> ++ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> ++ </PropertyGroup> ++ <ItemGroup> ++ <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies"> ++ <Visible>False</Visible> ++ </CodeAnalysisDependentAssemblyPaths> ++ </ItemGroup> ++ <PropertyGroup> ++ <DebugType></DebugType> ++ <Optimize>True</Optimize> ++ <AllowUnsafeBlocks>True</AllowUnsafeBlocks> ++ </PropertyGroup> ++ <ItemGroup> ++ <Compile Include="$(MSBuildProjectName).cs" /> ++ </ItemGroup> ++ <ItemGroup> ++ <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" /> ++ </ItemGroup> ++ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" /> ++ <PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' "> ++ </PropertyGroup> ++</Project> +diff --git a/tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_4.cs b/tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_4.cs +new file mode 100644 +index 0000000..126cdb9 +--- /dev/null ++++ b/tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_4.cs +@@ -0,0 +1,41 @@ ++// Licensed to the .NET Foundation under one or more agreements. ++// The .NET Foundation licenses this file to you under the MIT license. ++// See the LICENSE file in the project root for more information. ++ ++using System; ++using System.Runtime.CompilerServices; ++ ++public class X<K> ++{ ++ public X(K k1) ++ { ++ k = k1; ++ } ++ ++ [MethodImpl(MethodImplOptions.NoInlining)] ++ public static bool Test(X<K> a) ++ { ++ return (a.k == null); ++ } ++ ++ public K k; ++} ++ ++class B ++{ ++ public static int Main() ++ { ++ X<int> a = null; ++ bool result = false; ++ try ++ { ++ X<int>.Test(a); ++ } ++ catch (Exception) ++ { ++ result = true; ++ } ++ Console.WriteLine("Passed: {0}", result); ++ return result ? 100 : 0; ++ } ++} +diff --git a/tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_4.csproj b/tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_4.csproj +new file mode 100644 +index 0000000..5cd573d +--- /dev/null ++++ b/tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_4.csproj +@@ -0,0 +1,43 @@ ++<?xml version="1.0" encoding="utf-8"?> ++<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> ++ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" /> ++ <PropertyGroup> ++ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> ++ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> ++ <AssemblyName>$(MSBuildProjectName)</AssemblyName> ++ <SchemaVersion>2.0</SchemaVersion> ++ <ProjectGuid>{7B521917-193E-48BB-86C6-FE013F3DFF35}</ProjectGuid> ++ <OutputType>Exe</OutputType> ++ <AppDesignerFolder>Properties</AppDesignerFolder> ++ <FileAlignment>512</FileAlignment> ++ <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> ++ <ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\11.0\UITestExtensionPackages</ReferencePath> ++ <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir> ++ ++ <NuGetPackageImportStamp>7a9bfb7d</NuGetPackageImportStamp> ++ </PropertyGroup> ++ <!-- Default configurations to help VS understand the configurations --> ++ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> ++ </PropertyGroup> ++ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> ++ </PropertyGroup> ++ <ItemGroup> ++ <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies"> ++ <Visible>False</Visible> ++ </CodeAnalysisDependentAssemblyPaths> ++ </ItemGroup> ++ <PropertyGroup> ++ <DebugType></DebugType> ++ <Optimize>True</Optimize> ++ <AllowUnsafeBlocks>True</AllowUnsafeBlocks> ++ </PropertyGroup> ++ <ItemGroup> ++ <Compile Include="$(MSBuildProjectName).cs" /> ++ </ItemGroup> ++ <ItemGroup> ++ <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" /> ++ </ItemGroup> ++ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" /> ++ <PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' "> ++ </PropertyGroup> ++</Project> +diff --git a/tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_5.cs b/tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_5.cs +new file mode 100644 +index 0000000..95a8945 +--- /dev/null ++++ b/tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_5.cs +@@ -0,0 +1,46 @@ ++// Licensed to the .NET Foundation under one or more agreements. ++// The .NET Foundation licenses this file to you under the MIT license. ++// See the LICENSE file in the project root for more information. ++ ++using System; ++using System.Runtime.CompilerServices; ++ ++struct R ++{ ++ int a; ++} ++ ++public class X<K> ++{ ++ public X(K k1) ++ { ++ k = k1; ++ } ++ ++ [MethodImpl(MethodImplOptions.NoInlining)] ++ public static bool Test(X<K> a) ++ { ++ return (a.k != null); ++ } ++ ++ public K k; ++} ++ ++class B ++{ ++ public static int Main() ++ { ++ X<R> a = null; ++ bool result = false; ++ try ++ { ++ X<R>.Test(a); ++ } ++ catch (Exception) ++ { ++ result = true; ++ } ++ Console.WriteLine("Passed: {0}", result); ++ return result ? 100 : 0; ++ } ++} +diff --git a/tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_5.csproj b/tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_5.csproj +new file mode 100644 +index 0000000..5cd573d +--- /dev/null ++++ b/tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_5.csproj +@@ -0,0 +1,43 @@ ++<?xml version="1.0" encoding="utf-8"?> ++<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> ++ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" /> ++ <PropertyGroup> ++ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> ++ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> ++ <AssemblyName>$(MSBuildProjectName)</AssemblyName> ++ <SchemaVersion>2.0</SchemaVersion> ++ <ProjectGuid>{7B521917-193E-48BB-86C6-FE013F3DFF35}</ProjectGuid> ++ <OutputType>Exe</OutputType> ++ <AppDesignerFolder>Properties</AppDesignerFolder> ++ <FileAlignment>512</FileAlignment> ++ <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> ++ <ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\11.0\UITestExtensionPackages</ReferencePath> ++ <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir> ++ ++ <NuGetPackageImportStamp>7a9bfb7d</NuGetPackageImportStamp> ++ </PropertyGroup> ++ <!-- Default configurations to help VS understand the configurations --> ++ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> ++ </PropertyGroup> ++ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> ++ </PropertyGroup> ++ <ItemGroup> ++ <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies"> ++ <Visible>False</Visible> ++ </CodeAnalysisDependentAssemblyPaths> ++ </ItemGroup> ++ <PropertyGroup> ++ <DebugType></DebugType> ++ <Optimize>True</Optimize> ++ <AllowUnsafeBlocks>True</AllowUnsafeBlocks> ++ </PropertyGroup> ++ <ItemGroup> ++ <Compile Include="$(MSBuildProjectName).cs" /> ++ </ItemGroup> ++ <ItemGroup> ++ <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" /> ++ </ItemGroup> ++ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" /> ++ <PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' "> ++ </PropertyGroup> ++</Project> +diff --git a/tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_6.cs b/tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_6.cs +new file mode 100644 +index 0000000..1371dbb +--- /dev/null ++++ b/tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_6.cs +@@ -0,0 +1,60 @@ ++// Licensed to the .NET Foundation under one or more agreements. ++// The .NET Foundation licenses this file to you under the MIT license. ++// See the LICENSE file in the project root for more information. ++ ++using System; ++using System.Runtime.CompilerServices; ++ ++public interface IGet ++{ ++ int Get(); ++} ++ ++public struct R : IGet ++{ ++ public double d; ++ public int a; ++ ++ public int Get() { return a; } ++} ++ ++public class X<K> where K: IGet ++{ ++ public X(K r) ++ { ++ a = new K[2]; ++ a[0] = r; ++ } ++ ++ public void Assert(bool b) ++ { ++ if (!b) throw new Exception("bad!"); ++ } ++ ++ public int Test() ++ { ++ int r = 0; ++ for (int i = 0; i < a.Length; i++) ++ { ++ Assert(a[i] != null); ++ r += a[i].Get(); ++ } ++ return r; ++ } ++ ++ K[] a; ++} ++ ++class B ++{ ++ public static int Main() ++ { ++ var r = new R(); ++ r.a = 3; ++ var a = new X<R>(r); ++ int result = a.Test(); ++ bool passed = result == 3; ++ Console.WriteLine("Passed: {0}", passed); ++ return passed ? 100 : 0; ++ } ++} +diff --git a/tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_6.csproj b/tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_6.csproj +new file mode 100644 +index 0000000..5cd573d +--- /dev/null ++++ b/tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_6.csproj +@@ -0,0 +1,43 @@ ++<?xml version="1.0" encoding="utf-8"?> ++<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> ++ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" /> ++ <PropertyGroup> ++ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> ++ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> ++ <AssemblyName>$(MSBuildProjectName)</AssemblyName> ++ <SchemaVersion>2.0</SchemaVersion> ++ <ProjectGuid>{7B521917-193E-48BB-86C6-FE013F3DFF35}</ProjectGuid> ++ <OutputType>Exe</OutputType> ++ <AppDesignerFolder>Properties</AppDesignerFolder> ++ <FileAlignment>512</FileAlignment> ++ <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> ++ <ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\11.0\UITestExtensionPackages</ReferencePath> ++ <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir> ++ ++ <NuGetPackageImportStamp>7a9bfb7d</NuGetPackageImportStamp> ++ </PropertyGroup> ++ <!-- Default configurations to help VS understand the configurations --> ++ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> ++ </PropertyGroup> ++ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> ++ </PropertyGroup> ++ <ItemGroup> ++ <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies"> ++ <Visible>False</Visible> ++ </CodeAnalysisDependentAssemblyPaths> ++ </ItemGroup> ++ <PropertyGroup> ++ <DebugType></DebugType> ++ <Optimize>True</Optimize> ++ <AllowUnsafeBlocks>True</AllowUnsafeBlocks> ++ </PropertyGroup> ++ <ItemGroup> ++ <Compile Include="$(MSBuildProjectName).cs" /> ++ </ItemGroup> ++ <ItemGroup> ++ <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" /> ++ </ItemGroup> ++ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" /> ++ <PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' "> ++ </PropertyGroup> ++</Project> +diff --git a/tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_7.cs b/tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_7.cs +new file mode 100644 +index 0000000..3ba6cd2 +--- /dev/null ++++ b/tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_7.cs +@@ -0,0 +1,58 @@ ++// Licensed to the .NET Foundation under one or more agreements. ++// The .NET Foundation licenses this file to you under the MIT license. ++// See the LICENSE file in the project root for more information. ++ ++using System; ++ ++public struct V ++{ ++ public V(int x) ++ { ++ Token = x; ++ } ++ ++ public int Token; ++} ++ ++class M ++{ ++ static int F(int x, object a) ++ { ++ int result = 0; ++ ++ if (a is V) ++ { ++ int token = ((V)a).Token; ++ Console.WriteLine("F: Token is {0}", token); ++ result = x + token; ++ } ++ ++ return result; ++ } ++ ++ static int G(object a, int x) ++ { ++ return F(x, a); ++ } ++ ++ static int Trouble(ref V v) ++ { ++ Console.WriteLine("T: Token is {0}", v.Token); ++ int result = v.Token; ++ v.Token++; ++ return result; ++ } ++ ++ public static int Main() ++ { ++ // Ensure we get right order of side effects from boxes ++ // now that we are splitting them into multiple statments. ++ V v1 = new V(11); ++ int result1 = F(Trouble(ref v1), v1); ++ V v2 = new V(11); ++ int result2 = G(v2, Trouble(ref v2)); ++ Console.WriteLine("Result1 = {0}; Result2 = {1}", result1, result2); ++ return result1 + result2 + 55; ++ } ++} ++ +diff --git a/tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_7.csproj b/tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_7.csproj +new file mode 100644 +index 0000000..5cd573d +--- /dev/null ++++ b/tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_7.csproj +@@ -0,0 +1,43 @@ ++<?xml version="1.0" encoding="utf-8"?> ++<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> ++ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" /> ++ <PropertyGroup> ++ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> ++ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> ++ <AssemblyName>$(MSBuildProjectName)</AssemblyName> ++ <SchemaVersion>2.0</SchemaVersion> ++ <ProjectGuid>{7B521917-193E-48BB-86C6-FE013F3DFF35}</ProjectGuid> ++ <OutputType>Exe</OutputType> ++ <AppDesignerFolder>Properties</AppDesignerFolder> ++ <FileAlignment>512</FileAlignment> ++ <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> ++ <ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\11.0\UITestExtensionPackages</ReferencePath> ++ <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir> ++ ++ <NuGetPackageImportStamp>7a9bfb7d</NuGetPackageImportStamp> ++ </PropertyGroup> ++ <!-- Default configurations to help VS understand the configurations --> ++ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> ++ </PropertyGroup> ++ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> ++ </PropertyGroup> ++ <ItemGroup> ++ <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies"> ++ <Visible>False</Visible> ++ </CodeAnalysisDependentAssemblyPaths> ++ </ItemGroup> ++ <PropertyGroup> ++ <DebugType></DebugType> ++ <Optimize>True</Optimize> ++ <AllowUnsafeBlocks>True</AllowUnsafeBlocks> ++ </PropertyGroup> ++ <ItemGroup> ++ <Compile Include="$(MSBuildProjectName).cs" /> ++ </ItemGroup> ++ <ItemGroup> ++ <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" /> ++ </ItemGroup> ++ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" /> ++ <PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' "> ++ </PropertyGroup> ++</Project> +diff --git a/tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_8.cs b/tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_8.cs +new file mode 100644 +index 0000000..d5daa43 +--- /dev/null ++++ b/tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_8.cs +@@ -0,0 +1,42 @@ ++// Licensed to the .NET Foundation under one or more agreements. ++// The .NET Foundation licenses this file to you under the MIT license. ++// See the LICENSE file in the project root for more information. ++ ++using System; ++using System.Runtime.CompilerServices; ++using System.Numerics; ++ ++public class X<K> ++{ ++ public X(K k1) ++ { ++ k = k1; ++ } ++ ++ [MethodImpl(MethodImplOptions.NoInlining)] ++ public static bool Test(X<K> a) ++ { ++ return (a.k != null); ++ } ++ ++ public K k; ++} ++ ++class B ++{ ++ public static int Main() ++ { ++ X<Vector3> a = null; ++ bool result = false; ++ try ++ { ++ X<Vector3>.Test(a); ++ } ++ catch (Exception) ++ { ++ result = true; ++ } ++ Console.WriteLine("Passed: {0}", result); ++ return result ? 100 : 0; ++ } ++} +diff --git a/tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_8.csproj b/tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_8.csproj +new file mode 100644 +index 0000000..5cd573d +--- /dev/null ++++ b/tests/src/JIT/Regression/JitBlue/GitHub_12949/GitHub_12949_8.csproj +@@ -0,0 +1,43 @@ ++<?xml version="1.0" encoding="utf-8"?> ++<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> ++ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" /> ++ <PropertyGroup> ++ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> ++ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> ++ <AssemblyName>$(MSBuildProjectName)</AssemblyName> ++ <SchemaVersion>2.0</SchemaVersion> ++ <ProjectGuid>{7B521917-193E-48BB-86C6-FE013F3DFF35}</ProjectGuid> ++ <OutputType>Exe</OutputType> ++ <AppDesignerFolder>Properties</AppDesignerFolder> ++ <FileAlignment>512</FileAlignment> ++ <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> ++ <ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\11.0\UITestExtensionPackages</ReferencePath> ++ <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir> ++ ++ <NuGetPackageImportStamp>7a9bfb7d</NuGetPackageImportStamp> ++ </PropertyGroup> ++ <!-- Default configurations to help VS understand the configurations --> ++ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> ++ </PropertyGroup> ++ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> ++ </PropertyGroup> ++ <ItemGroup> ++ <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies"> ++ <Visible>False</Visible> ++ </CodeAnalysisDependentAssemblyPaths> ++ </ItemGroup> ++ <PropertyGroup> ++ <DebugType></DebugType> ++ <Optimize>True</Optimize> ++ <AllowUnsafeBlocks>True</AllowUnsafeBlocks> ++ </PropertyGroup> ++ <ItemGroup> ++ <Compile Include="$(MSBuildProjectName).cs" /> ++ </ItemGroup> ++ <ItemGroup> ++ <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" /> ++ </ItemGroup> ++ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" /> ++ <PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' "> ++ </PropertyGroup> ++</Project> +-- +2.7.4 + diff --git a/packaging/0020-JIT-port-fix-to-defer-removing-statements-during-opt.patch b/packaging/0020-JIT-port-fix-to-defer-removing-statements-during-opt.patch new file mode 100644 index 0000000000..db0d267dc2 --- /dev/null +++ b/packaging/0020-JIT-port-fix-to-defer-removing-statements-during-opt.patch @@ -0,0 +1,132 @@ +From 240ffdf43611fda5d22672f56be5760803d1a011 Mon Sep 17 00:00:00 2001 +From: Andy Ayers <andya@microsoft.com> +Date: Mon, 4 Dec 2017 11:22:26 -0800 +Subject: [PATCH 20/47] JIT: port fix to defer removing statements during opt + CSE to release/2.0.0 + +Port of #15323. +Related issue #15319. +--- + src/jit/morph.cpp | 8 +++- + .../JitBlue/GitHub_15319/GitHub_15319.cs | 44 ++++++++++++++++++++++ + .../JitBlue/GitHub_15319/GitHub_15319.csproj | 38 +++++++++++++++++++ + 3 files changed, 89 insertions(+), 1 deletion(-) + create mode 100644 tests/src/JIT/Regression/JitBlue/GitHub_15319/GitHub_15319.cs + create mode 100644 tests/src/JIT/Regression/JitBlue/GitHub_15319/GitHub_15319.csproj + +diff --git a/src/jit/morph.cpp b/src/jit/morph.cpp +index 79b3fef..993ce53 100644 +--- a/src/jit/morph.cpp ++++ b/src/jit/morph.cpp +@@ -15695,7 +15695,13 @@ bool Compiler::fgMorphBlockStmt(BasicBlock* block, GenTreeStmt* stmt DEBUGARG(co + } + + // Can the entire tree be removed? +- bool removedStmt = fgCheckRemoveStmt(block, stmt); ++ bool removedStmt = false; ++ ++ // Defer removing statements during CSE so we don't inadvertently remove any CSE defs. ++ if (!optValnumCSE_phase) ++ { ++ removedStmt = fgCheckRemoveStmt(block, stmt); ++ } + + // Or this is the last statement of a conditional branch that was just folded? + if (!removedStmt && (stmt->getNextStmt() == nullptr) && !fgRemoveRestOfBlock) +diff --git a/tests/src/JIT/Regression/JitBlue/GitHub_15319/GitHub_15319.cs b/tests/src/JIT/Regression/JitBlue/GitHub_15319/GitHub_15319.cs +new file mode 100644 +index 0000000..af7419f +--- /dev/null ++++ b/tests/src/JIT/Regression/JitBlue/GitHub_15319/GitHub_15319.cs +@@ -0,0 +1,44 @@ ++// Licensed to the .NET Foundation under one or more agreements. ++// The .NET Foundation licenses this file to you under the MIT license. ++// See the LICENSE file in the project root for more information. ++ ++using System; ++using System.Linq; ++ ++// Bug where interacting CSEs of N - Old.Length and Old.Length ++// were not handled properly in optCSE ++ ++class P ++{ ++ private static int Main(string[] args) ++ { ++ var ar = new double[] ++ { ++ 100 ++ }; ++ ++ FillTo1(ref ar, 5); ++ Console.WriteLine(string.Join(",", ar.Select(a => a.ToString()).ToArray())); ++ return (int)ar[4]; ++ } ++ ++ public static void FillTo1(ref double[] dd, int N) ++ { ++ if (dd.Length >= N) ++ return; ++ ++ double[] Old = dd; ++ double d = double.NaN; ++ if (Old.Length > 0) ++ d = Old[0]; ++ ++ dd = new double[N]; ++ ++ for (int i = 0; i < Old.Length; i++) ++ { ++ dd[N - Old.Length + i] = Old[i]; ++ } ++ for (int i = 0; i < N - Old.Length; i++) ++ dd[i] = d; ++ } ++} +diff --git a/tests/src/JIT/Regression/JitBlue/GitHub_15319/GitHub_15319.csproj b/tests/src/JIT/Regression/JitBlue/GitHub_15319/GitHub_15319.csproj +new file mode 100644 +index 0000000..c3a87c1 +--- /dev/null ++++ b/tests/src/JIT/Regression/JitBlue/GitHub_15319/GitHub_15319.csproj +@@ -0,0 +1,38 @@ ++<?xml version="1.0" encoding="utf-8"?> ++<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> ++ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" /> ++ <PropertyGroup> ++ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> ++ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> ++ <AssemblyName>$(MSBuildProjectName)</AssemblyName> ++ <SchemaVersion>2.0</SchemaVersion> ++ <ProjectGuid>{95DFC527-4DC1-495E-97D7-E94EE1F7140D}</ProjectGuid> ++ <OutputType>Exe</OutputType> ++ <AppDesignerFolder>Properties</AppDesignerFolder> ++ <FileAlignment>512</FileAlignment> ++ <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> ++ <ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\11.0\UITestExtensionPackages</ReferencePath> ++ <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir> ++ <NuGetPackageImportStamp>7a9bfb7d</NuGetPackageImportStamp> ++ </PropertyGroup> ++ <!-- Default configurations to help VS understand the configurations --> ++ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "></PropertyGroup> ++ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "></PropertyGroup> ++ <ItemGroup> ++ <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies"> ++ <Visible>False</Visible> ++ </CodeAnalysisDependentAssemblyPaths> ++ </ItemGroup> ++ <PropertyGroup> ++ <DebugType></DebugType> ++ <Optimize>True</Optimize> ++ </PropertyGroup> ++ <ItemGroup> ++ <Compile Include="GitHub_15319.cs" /> ++ </ItemGroup> ++ <ItemGroup> ++ <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" /> ++ </ItemGroup> ++ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" /> ++ <PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' "></PropertyGroup> ++</Project> +-- +2.7.4 + diff --git a/packaging/0021-Revert-ExecuteHandlerOnOriginalStack-handle-case-whe.patch b/packaging/0021-Revert-ExecuteHandlerOnOriginalStack-handle-case-whe.patch new file mode 100644 index 0000000000..57e06008d2 --- /dev/null +++ b/packaging/0021-Revert-ExecuteHandlerOnOriginalStack-handle-case-whe.patch @@ -0,0 +1,143 @@ +From 911fcce99ad77f5bb18de8141a3b80f9f2823041 Mon Sep 17 00:00:00 2001 +From: Konstantin Baladurin <k.baladurin@partner.samsung.com> +Date: Wed, 30 May 2018 19:23:03 +0300 +Subject: [PATCH 21/47] Revert "ExecuteHandlerOnOriginalStack: handle case when + it is called on original stack." + +This reverts commit a8465f60548ed4b10f13f5ace8ea99e07fc2aeba. +--- + src/pal/src/arch/amd64/signalhandlerhelper.cpp | 20 ++------------------ + src/pal/src/arch/arm/signalhandlerhelper.cpp | 18 +----------------- + src/pal/src/arch/arm64/signalhandlerhelper.cpp | 19 +------------------ + src/pal/src/arch/i386/signalhandlerhelper.cpp | 18 +----------------- + 4 files changed, 5 insertions(+), 70 deletions(-) + +diff --git a/src/pal/src/arch/amd64/signalhandlerhelper.cpp b/src/pal/src/arch/amd64/signalhandlerhelper.cpp +index 803479c..8789f5a 100644 +--- a/src/pal/src/arch/amd64/signalhandlerhelper.cpp ++++ b/src/pal/src/arch/amd64/signalhandlerhelper.cpp +@@ -27,23 +27,7 @@ Parameters : + void ExecuteHandlerOnOriginalStack(int code, siginfo_t *siginfo, void *context, SignalHandlerWorkerReturnPoint* returnPoint) + { + ucontext_t *ucontext = (ucontext_t *)context; +- size_t faultSp; +- +- // check whether this function is called on alternate stack or not. In the second case we already +- // on original stack and we should use faultSp from this frame otherwise stackframe of the caller +- // function will be corrupted. +- char fakeStackFrame[128 /* redzone */ + 16 /* aligment */ + 4 * sizeof(size_t) /* registers */]; +- stack_t oss; +- int st = sigaltstack(NULL, &oss); +- if ((st == 0) && ((oss.ss_flags == SS_DISABLE) || +- ((size_t)oss.ss_sp > (size_t)&faultSp) || (((size_t)oss.ss_sp + oss.ss_size) < (size_t)&faultSp))) +- { +- faultSp = (size_t)&fakeStackFrame[sizeof(fakeStackFrame)]; +- } +- else +- { +- faultSp = (size_t)MCREG_Rsp(ucontext->uc_mcontext); +- } ++ size_t faultSp = (size_t)MCREG_Rsp(ucontext->uc_mcontext); + + _ASSERTE(IS_ALIGNED(faultSp, 8)); + +@@ -74,7 +58,7 @@ void ExecuteHandlerOnOriginalStack(int code, siginfo_t *siginfo, void *context, + // We don't care about the other registers state since the stack unwinding restores + // them for the target frame directly from the signal context. + context2.Rsp = (size_t)sp; +- context2.Rbx = (size_t)MCREG_Rsp(ucontext->uc_mcontext); ++ context2.Rbx = (size_t)faultSp; + context2.Rbp = (size_t)fp; + context2.Rip = (size_t)signal_handler_worker; + context2.Rdi = code; +diff --git a/src/pal/src/arch/arm/signalhandlerhelper.cpp b/src/pal/src/arch/arm/signalhandlerhelper.cpp +index fbf33e3..3936204 100644 +--- a/src/pal/src/arch/arm/signalhandlerhelper.cpp ++++ b/src/pal/src/arch/arm/signalhandlerhelper.cpp +@@ -27,23 +27,7 @@ Parameters : + void ExecuteHandlerOnOriginalStack(int code, siginfo_t *siginfo, void *context, SignalHandlerWorkerReturnPoint* returnPoint) + { + ucontext_t *ucontext = (ucontext_t *)context; +- size_t faultSp; +- +- // check whether this function is called on alternate stack or not. In the second case we already +- // on original stack and we should use faultSp from this frame otherwise stackframe of the caller +- // function will be corrupted. +- char fakeStackFrame[8 /* redzone */ + 8 /* aligment */ + sizeof(ucontext->uc_mcontext) + 8 /* registers */]; +- stack_t oss; +- int st = sigaltstack(NULL, &oss); +- if ((st == 0) && ((oss.ss_flags == SS_DISABLE) || +- ((size_t)oss.ss_sp > (size_t)&faultSp) || (((size_t)oss.ss_sp + oss.ss_size) < (size_t)&faultSp))) +- { +- faultSp = (size_t)&fakeStackFrame[sizeof(fakeStackFrame)]; +- } +- else +- { +- faultSp = (size_t)MCREG_Sp(ucontext->uc_mcontext); +- } ++ size_t faultSp = (size_t)MCREG_Sp(ucontext->uc_mcontext); + + _ASSERTE(IS_ALIGNED(faultSp, 4)); + +diff --git a/src/pal/src/arch/arm64/signalhandlerhelper.cpp b/src/pal/src/arch/arm64/signalhandlerhelper.cpp +index 958b8af..c35c629 100644 +--- a/src/pal/src/arch/arm64/signalhandlerhelper.cpp ++++ b/src/pal/src/arch/arm64/signalhandlerhelper.cpp +@@ -27,24 +27,7 @@ Parameters : + void ExecuteHandlerOnOriginalStack(int code, siginfo_t *siginfo, void *context, SignalHandlerWorkerReturnPoint* returnPoint) + { + ucontext_t *ucontext = (ucontext_t *)context; +- size_t faultSp; +- +- // check whether this function is called on alternate stack or not. In the second case we already +- // on original stack and we should use faultSp from this frame otherwise stackframe of the caller +- // function will be corrupted. +- char fakeStackFrame[128 /* redzone */ + 16 /* aligment */ + 3 * sizeof(size_t) /* registers */]; +- stack_t oss; +- int st = sigaltstack(NULL, &oss); +- if ((st == 0) && ((oss.ss_flags == SS_DISABLE) || +- ((size_t)oss.ss_sp > (size_t)&faultSp) || (((size_t)oss.ss_sp + oss.ss_size) < (size_t)&faultSp))) +- { +- faultSp = (size_t)&fakeStackFrame[sizeof(fakeStackFrame)]; +- } +- else +- { +- faultSp = (size_t)MCREG_Sp(ucontext->uc_mcontext); +- } +- ++ size_t faultSp = (size_t)MCREG_Sp(ucontext->uc_mcontext); + _ASSERTE(IS_ALIGNED(faultSp, 8)); + + size_t fakeFrameReturnAddress; +diff --git a/src/pal/src/arch/i386/signalhandlerhelper.cpp b/src/pal/src/arch/i386/signalhandlerhelper.cpp +index d534f96..a7d418a 100644 +--- a/src/pal/src/arch/i386/signalhandlerhelper.cpp ++++ b/src/pal/src/arch/i386/signalhandlerhelper.cpp +@@ -27,23 +27,7 @@ Parameters : + void ExecuteHandlerOnOriginalStack(int code, siginfo_t *siginfo, void *context, SignalHandlerWorkerReturnPoint* returnPoint) + { + ucontext_t *ucontext = (ucontext_t *)context; +- size_t faultSp; +- +- // check whether this function is called on alternate stack or not. In the second case we already +- // on original stack and we should use faultSp from this frame otherwise stackframe of the caller +- // function will be corrupted. +- char fakeStackFrame[16 /* aligment */ + 10 * sizeof(size_t) /* registers */]; +- stack_t oss; +- int st = sigaltstack(NULL, &oss); +- if ((st == 0) && ((oss.ss_flags == SS_DISABLE) || +- ((size_t)oss.ss_sp > (size_t)&faultSp) || (((size_t)oss.ss_sp + oss.ss_size) < (size_t)&faultSp))) +- { +- faultSp = (size_t)&fakeStackFrame[sizeof(fakeStackFrame)]; +- } +- else +- { +- faultSp = (size_t)MCREG_Esp(ucontext->uc_mcontext); +- } ++ size_t faultSp = (size_t)MCREG_Esp(ucontext->uc_mcontext); + + _ASSERTE(IS_ALIGNED(faultSp, 4)); + +-- +2.7.4 + diff --git a/packaging/0022-CatchHardwareExceptionHolder-use-GetCurrentPalThread.patch b/packaging/0022-CatchHardwareExceptionHolder-use-GetCurrentPalThread.patch new file mode 100644 index 0000000000..5431ee0c0a --- /dev/null +++ b/packaging/0022-CatchHardwareExceptionHolder-use-GetCurrentPalThread.patch @@ -0,0 +1,35 @@ +From 7c577d168dfc4b76516437c3220a5ccbd8607e37 Mon Sep 17 00:00:00 2001 +From: Konstantin Baladurin <k.baladurin@partner.samsung.com> +Date: Tue, 13 Feb 2018 15:56:58 +0300 +Subject: [PATCH 22/47] CatchHardwareExceptionHolder: use GetCurrentPalThread + instead of InternalGetCurrentThread in IsEnabled method. + +InternalGetCurrentThread tries to create pal thread if it doesn't +exist for the current thread. It's unnecessary because in this case +there are no hardware exception handlers for such thread. + +Also CatchHardwareExceptionHolder::IsEnable is called from signal +handlers and during pal thread creation non-async-signal-safe +function are called. +--- + src/pal/src/exception/seh.cpp | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/pal/src/exception/seh.cpp b/src/pal/src/exception/seh.cpp +index 2d1c182..0574fad 100644 +--- a/src/pal/src/exception/seh.cpp ++++ b/src/pal/src/exception/seh.cpp +@@ -374,8 +374,8 @@ CatchHardwareExceptionHolder::~CatchHardwareExceptionHolder() + + bool CatchHardwareExceptionHolder::IsEnabled() + { +- CPalThread *pThread = InternalGetCurrentThread(); +- return pThread->IsHardwareExceptionsEnabled(); ++ CPalThread *pThread = GetCurrentPalThread(); ++ return pThread ? pThread->IsHardwareExceptionsEnabled() : false; + } + + /*++ +-- +2.7.4 + diff --git a/packaging/0023-vm-threads-change-tls-model-for-gCurrentThreadInfo-v.patch b/packaging/0023-vm-threads-change-tls-model-for-gCurrentThreadInfo-v.patch new file mode 100644 index 0000000000..9976ee1fb9 --- /dev/null +++ b/packaging/0023-vm-threads-change-tls-model-for-gCurrentThreadInfo-v.patch @@ -0,0 +1,29 @@ +From df179b357e12c57b5e0caa8a1427a344bdae812f Mon Sep 17 00:00:00 2001 +From: Konstantin Baladurin <k.baladurin@partner.samsung.com> +Date: Tue, 13 Feb 2018 16:14:37 +0300 +Subject: [PATCH 23/47] vm/threads: change tls model for gCurrentThreadInfo + variable + +We should use initial-exec tls model to avoid memory allocations +during first access to this variable because it may ocuur in +signal handlers. +--- + src/vm/threads.inl | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/vm/threads.inl b/src/vm/threads.inl +index 26682ec..0105955 100644 +--- a/src/vm/threads.inl ++++ b/src/vm/threads.inl +@@ -27,7 +27,7 @@ + #ifndef __llvm__ + EXTERN_C __declspec(thread) ThreadLocalInfo gCurrentThreadInfo; + #else // !__llvm__ +-EXTERN_C __thread ThreadLocalInfo gCurrentThreadInfo; ++EXTERN_C __thread ThreadLocalInfo gCurrentThreadInfo __attribute__ ((tls_model("initial-exec"))); + #endif // !__llvm__ + + EXTERN_C inline Thread* STDCALL GetThread() +-- +2.7.4 + diff --git a/packaging/0024-sigsegv_handler-handle-case-when-it-is-called-on-ori.patch b/packaging/0024-sigsegv_handler-handle-case-when-it-is-called-on-ori.patch new file mode 100644 index 0000000000..08678dd416 --- /dev/null +++ b/packaging/0024-sigsegv_handler-handle-case-when-it-is-called-on-ori.patch @@ -0,0 +1,71 @@ +From 8b1bff743895942364a26df78ad1dd2f14760f28 Mon Sep 17 00:00:00 2001 +From: Konstantin Baladurin <k.baladurin@partner.samsung.com> +Date: Tue, 13 Feb 2018 16:27:01 +0300 +Subject: [PATCH 24/47] sigsegv_handler: handle case when it is called on + original stack + +If sigsegv_handler is called on original stack (for example, if segmentation +fault occurs in native application's thread that hasn't alternate signal stack) +we should call common_signal_handler directly othersize sigsegv_handler's +stackframe will be corrupted. +--- + src/pal/src/exception/signal.cpp | 38 ++++++++++++++++++++++++++------------ + 1 file changed, 26 insertions(+), 12 deletions(-) + +diff --git a/src/pal/src/exception/signal.cpp b/src/pal/src/exception/signal.cpp +index f795b81..bf48619 100644 +--- a/src/pal/src/exception/signal.cpp ++++ b/src/pal/src/exception/signal.cpp +@@ -472,23 +472,37 @@ static void sigsegv_handler(int code, siginfo_t *siginfo, void *context) + + // Establish a return point in case the common_signal_handler returns + +- volatile bool contextInitialization = true; ++ if (GetCurrentPalThread()) ++ { ++ volatile bool contextInitialization = true; + +- SignalHandlerWorkerReturnPoint returnPoint; +- RtlCaptureContext(&returnPoint.context); ++ void *ptr = alloca(sizeof(SignalHandlerWorkerReturnPoint) + alignof(SignalHandlerWorkerReturnPoint) - 1); ++ SignalHandlerWorkerReturnPoint *pReturnPoint = (SignalHandlerWorkerReturnPoint *)ALIGN_UP(ptr, alignof(SignalHandlerWorkerReturnPoint)); ++ RtlCaptureContext(&pReturnPoint->context); + +- // When the signal handler worker completes, it uses setcontext to return to this point ++ // When the signal handler worker completes, it uses setcontext to return to this point + +- if (contextInitialization) +- { +- contextInitialization = false; +- ExecuteHandlerOnOriginalStack(code, siginfo, context, &returnPoint); +- _ASSERTE(FALSE); // The ExecuteHandlerOnOriginalStack should never return ++ if (contextInitialization) ++ { ++ contextInitialization = false; ++ ExecuteHandlerOnOriginalStack(code, siginfo, context, pReturnPoint); ++ _ASSERTE(FALSE); // The ExecuteHandlerOnOriginalStack should never return ++ } ++ ++ if (pReturnPoint->returnFromHandler) ++ { ++ return; ++ } + } +- +- if (returnPoint.returnFromHandler) ++ else + { +- return; ++ // If thread isn't created by coreclr and has alternate signal stack GetCurrentPalThread() will return NULL too. ++ // But since in this case we don't handle hardware exceptions (IsSafeToHandleHardwareException returns false) ++ // we can call common_signal_handler on the alternate stack. ++ if (common_signal_handler(code, siginfo, context, 2, (size_t)0, (size_t)siginfo->si_addr)) ++ { ++ return; ++ } + } + } + +-- +2.7.4 + diff --git a/packaging/0025-Revert-TLS-model-change-of-the-gCurrentThreadInfo.patch b/packaging/0025-Revert-TLS-model-change-of-the-gCurrentThreadInfo.patch new file mode 100644 index 0000000000..38d416720f --- /dev/null +++ b/packaging/0025-Revert-TLS-model-change-of-the-gCurrentThreadInfo.patch @@ -0,0 +1,28 @@ +From 459e054044ede83560f9295bce5556f64b00e5c2 Mon Sep 17 00:00:00 2001 +From: Jan Vorlicek <janvorli@microsoft.com> +Date: Wed, 21 Feb 2018 02:02:30 +0100 +Subject: [PATCH 25/47] Revert TLS model change of the gCurrentThreadInfo + +This change causes crashes due to incorrect TLS variable initialization +on Alpine Linux. The initial-exec model that the variable was modified +to use recently cannot be safely used in shared libraries. +--- + src/vm/threads.inl | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/vm/threads.inl b/src/vm/threads.inl +index 0105955..26682ec 100644 +--- a/src/vm/threads.inl ++++ b/src/vm/threads.inl +@@ -27,7 +27,7 @@ + #ifndef __llvm__ + EXTERN_C __declspec(thread) ThreadLocalInfo gCurrentThreadInfo; + #else // !__llvm__ +-EXTERN_C __thread ThreadLocalInfo gCurrentThreadInfo __attribute__ ((tls_model("initial-exec"))); ++EXTERN_C __thread ThreadLocalInfo gCurrentThreadInfo; + #endif // !__llvm__ + + EXTERN_C inline Thread* STDCALL GetThread() +-- +2.7.4 + diff --git a/packaging/0026-Prevent-memory-allocation-in-signal-handler.patch b/packaging/0026-Prevent-memory-allocation-in-signal-handler.patch new file mode 100644 index 0000000000..b3617fa8c3 --- /dev/null +++ b/packaging/0026-Prevent-memory-allocation-in-signal-handler.patch @@ -0,0 +1,68 @@ +From 97b80457b091641dd6fb366038b8e362b1409a58 Mon Sep 17 00:00:00 2001 +From: Mikhail Labiuk <m.labiuk@samsung.com> +Date: Tue, 13 Feb 2018 15:02:12 +0300 +Subject: [PATCH 26/47] Prevent memory allocation in signal handler + +If the signal occurs when heap being inconsistent we should not +use heap. We should call signal-safe functions only from signal handler. + +fix https://github.com/dotnet/coreclr/issues/16338 +--- + src/pal/src/exception/seh-unwind.cpp | 6 ++++-- + src/pal/src/exception/signal.cpp | 2 +- + src/pal/src/include/pal/seh.hpp | 3 ++- + 3 files changed, 7 insertions(+), 4 deletions(-) + +diff --git a/src/pal/src/exception/seh-unwind.cpp b/src/pal/src/exception/seh-unwind.cpp +index 7746bbb..eba2c80 100644 +--- a/src/pal/src/exception/seh-unwind.cpp ++++ b/src/pal/src/exception/seh-unwind.cpp +@@ -620,12 +620,14 @@ Function: + Parameters: + exceptionRecord - output pointer to the allocated exception record + contextRecord - output pointer to the allocated context record ++ allocationProhibited - input flag to avoid memory allocation in critical situations + --*/ + VOID +-AllocateExceptionRecords(EXCEPTION_RECORD** exceptionRecord, CONTEXT** contextRecord) ++AllocateExceptionRecords(EXCEPTION_RECORD** exceptionRecord, CONTEXT** contextRecord, BOOL allocationProhibited) + { + ExceptionRecords* records; +- if (posix_memalign((void**)&records, alignof(ExceptionRecords), sizeof(ExceptionRecords)) != 0) ++ if (allocationProhibited || ++ (posix_memalign((void**)&records, alignof(ExceptionRecords), sizeof(ExceptionRecords)) != 0) ) + { + size_t bitmap; + size_t newBitmap; +diff --git a/src/pal/src/exception/signal.cpp b/src/pal/src/exception/signal.cpp +index bf48619..90da207 100644 +--- a/src/pal/src/exception/signal.cpp ++++ b/src/pal/src/exception/signal.cpp +@@ -808,7 +808,7 @@ static bool common_signal_handler(int code, siginfo_t *siginfo, void *sigcontext + ucontext = (native_context_t *)sigcontext; + g_common_signal_handler_context_locvar_offset = (int)((char*)&signalContextRecord - (char*)__builtin_frame_address(0)); + +- AllocateExceptionRecords(&exceptionRecord, &contextRecord); ++ AllocateExceptionRecords(&exceptionRecord, &contextRecord, true); + + exceptionRecord->ExceptionCode = CONTEXTGetExceptionCodeForSignal(siginfo, ucontext); + exceptionRecord->ExceptionFlags = EXCEPTION_IS_SIGNAL; +diff --git a/src/pal/src/include/pal/seh.hpp b/src/pal/src/include/pal/seh.hpp +index 3ac93d6..5edc214 100644 +--- a/src/pal/src/include/pal/seh.hpp ++++ b/src/pal/src/include/pal/seh.hpp +@@ -84,9 +84,10 @@ Function: + Parameters: + exceptionRecord - output pointer to the allocated Windows exception record + contextRecord - output pointer to the allocated Windows context record ++ allocationProhibited - input flag to avoid memory allocation in critical situations + --*/ + VOID +-AllocateExceptionRecords(EXCEPTION_RECORD** exceptionRecord, CONTEXT** contextRecord); ++AllocateExceptionRecords(EXCEPTION_RECORD** exceptionRecord, CONTEXT** contextRecord, BOOL allocationProhibited=false); + + #if !HAVE_MACH_EXCEPTIONS + // TODO: Implement for Mach exceptions. Not in CoreCLR surface area. +-- +2.7.4 + diff --git a/packaging/0027-Revert-Prevent-memory-allocation-in-signal-handler.patch b/packaging/0027-Revert-Prevent-memory-allocation-in-signal-handler.patch new file mode 100644 index 0000000000..2d88908ac5 --- /dev/null +++ b/packaging/0027-Revert-Prevent-memory-allocation-in-signal-handler.patch @@ -0,0 +1,65 @@ +From a01acb182005e85a766fe05bdc5ce81c78b49586 Mon Sep 17 00:00:00 2001 +From: Mikhail Labiuk <m.labiuk@samsung.com> +Date: Mon, 19 Feb 2018 19:08:50 +0300 +Subject: [PATCH 27/47] Revert "Prevent memory allocation in signal handler" + +This reverts commit fa6087f0c2856215e7141259b56e75b46746f74d. +--- + src/pal/src/exception/seh-unwind.cpp | 6 ++---- + src/pal/src/exception/signal.cpp | 2 +- + src/pal/src/include/pal/seh.hpp | 3 +-- + 3 files changed, 4 insertions(+), 7 deletions(-) + +diff --git a/src/pal/src/exception/seh-unwind.cpp b/src/pal/src/exception/seh-unwind.cpp +index eba2c80..7746bbb 100644 +--- a/src/pal/src/exception/seh-unwind.cpp ++++ b/src/pal/src/exception/seh-unwind.cpp +@@ -620,14 +620,12 @@ Function: + Parameters: + exceptionRecord - output pointer to the allocated exception record + contextRecord - output pointer to the allocated context record +- allocationProhibited - input flag to avoid memory allocation in critical situations + --*/ + VOID +-AllocateExceptionRecords(EXCEPTION_RECORD** exceptionRecord, CONTEXT** contextRecord, BOOL allocationProhibited) ++AllocateExceptionRecords(EXCEPTION_RECORD** exceptionRecord, CONTEXT** contextRecord) + { + ExceptionRecords* records; +- if (allocationProhibited || +- (posix_memalign((void**)&records, alignof(ExceptionRecords), sizeof(ExceptionRecords)) != 0) ) ++ if (posix_memalign((void**)&records, alignof(ExceptionRecords), sizeof(ExceptionRecords)) != 0) + { + size_t bitmap; + size_t newBitmap; +diff --git a/src/pal/src/exception/signal.cpp b/src/pal/src/exception/signal.cpp +index 90da207..bf48619 100644 +--- a/src/pal/src/exception/signal.cpp ++++ b/src/pal/src/exception/signal.cpp +@@ -808,7 +808,7 @@ static bool common_signal_handler(int code, siginfo_t *siginfo, void *sigcontext + ucontext = (native_context_t *)sigcontext; + g_common_signal_handler_context_locvar_offset = (int)((char*)&signalContextRecord - (char*)__builtin_frame_address(0)); + +- AllocateExceptionRecords(&exceptionRecord, &contextRecord, true); ++ AllocateExceptionRecords(&exceptionRecord, &contextRecord); + + exceptionRecord->ExceptionCode = CONTEXTGetExceptionCodeForSignal(siginfo, ucontext); + exceptionRecord->ExceptionFlags = EXCEPTION_IS_SIGNAL; +diff --git a/src/pal/src/include/pal/seh.hpp b/src/pal/src/include/pal/seh.hpp +index 5edc214..3ac93d6 100644 +--- a/src/pal/src/include/pal/seh.hpp ++++ b/src/pal/src/include/pal/seh.hpp +@@ -84,10 +84,9 @@ Function: + Parameters: + exceptionRecord - output pointer to the allocated Windows exception record + contextRecord - output pointer to the allocated Windows context record +- allocationProhibited - input flag to avoid memory allocation in critical situations + --*/ + VOID +-AllocateExceptionRecords(EXCEPTION_RECORD** exceptionRecord, CONTEXT** contextRecord, BOOL allocationProhibited=false); ++AllocateExceptionRecords(EXCEPTION_RECORD** exceptionRecord, CONTEXT** contextRecord); + + #if !HAVE_MACH_EXCEPTIONS + // TODO: Implement for Mach exceptions. Not in CoreCLR surface area. +-- +2.7.4 + diff --git a/packaging/0028-Do-not-allocate-exception-for-signal-from-non-manage.patch b/packaging/0028-Do-not-allocate-exception-for-signal-from-non-manage.patch new file mode 100644 index 0000000000..e313dab764 --- /dev/null +++ b/packaging/0028-Do-not-allocate-exception-for-signal-from-non-manage.patch @@ -0,0 +1,174 @@ +From 88c5af565882264c129816bdba94fed25c83f2de Mon Sep 17 00:00:00 2001 +From: Mikhail Labiuk <m.labiuk@samsung.com> +Date: Mon, 19 Feb 2018 18:56:15 +0300 +Subject: [PATCH 28/47] Do not allocate exception for signal from non managed + code + +If the signal occurs in not managed code we cannot use heap. +We should call signal-safe functions only from signal handler. + +Create exception object on stack for checking source of signal. +If signal is from managed code we can use memory allocation to create +persistent exception on heap as copy of volatile exception on stack. + +If signal from unmanaged code we do nothing and call base signal handler. + +fix https://github.com/dotnet/coreclr/issues/16338 +--- + src/pal/inc/pal.h | 5 ++++- + src/pal/src/exception/seh.cpp | 23 +++++++++++++++++++++++ + src/pal/src/exception/signal.cpp | 30 +++++++++++++++--------------- + 3 files changed, 42 insertions(+), 16 deletions(-) + +diff --git a/src/pal/inc/pal.h b/src/pal/inc/pal.h +index 0a00b67..e3bfa40 100644 +--- a/src/pal/inc/pal.h ++++ b/src/pal/inc/pal.h +@@ -3888,6 +3888,8 @@ PAL_BindResources(IN LPCSTR lpDomain); + + #define EXCEPTION_IS_SIGNAL 0x100 + ++#define EXCEPTION_ON_STACK 0x400 ++ + #define EXCEPTION_MAXIMUM_PARAMETERS 15 + + // Index in the ExceptionInformation array where we will keep the reference +@@ -5814,7 +5816,8 @@ private: + + void FreeRecords() + { +- if (ExceptionPointers.ExceptionRecord != NULL) ++ if (ExceptionPointers.ExceptionRecord != NULL && ++ ! (ExceptionPointers.ExceptionRecord->ExceptionFlags | EXCEPTION_ON_STACK) ) + { + PAL_FreeExceptionRecords(ExceptionPointers.ExceptionRecord, ExceptionPointers.ContextRecord); + ExceptionPointers.ExceptionRecord = NULL; +diff --git a/src/pal/src/exception/seh.cpp b/src/pal/src/exception/seh.cpp +index 0574fad..a7d4ad9 100644 +--- a/src/pal/src/exception/seh.cpp ++++ b/src/pal/src/exception/seh.cpp +@@ -232,6 +232,23 @@ void ThrowExceptionHelper(PAL_SEHException* ex) + throw std::move(*ex); + } + ++static PAL_SEHException copyPAL_SEHException(PAL_SEHException* src) ++{ ++ CONTEXT* contextRecord = src->GetContextRecord(); ++ EXCEPTION_RECORD* exceptionRecord = src->GetExceptionRecord(); ++ ++ CONTEXT* contextRecordCopy; ++ EXCEPTION_RECORD* exceptionRecordCopy; ++ AllocateExceptionRecords(&exceptionRecordCopy, &contextRecordCopy); ++ ++ *exceptionRecordCopy = *exceptionRecord; ++ exceptionRecordCopy->ExceptionFlags &= ~EXCEPTION_ON_STACK; ++ *contextRecordCopy = *contextRecord; ++ return PAL_SEHException(exceptionRecordCopy, contextRecordCopy); ++} ++ ++ ++ + /*++ + Function: + SEHProcessException +@@ -279,6 +296,9 @@ SEHProcessException(PAL_SEHException* exception) + PROCAbort(); + } + } ++ ++ if(exceptionRecord->ExceptionFlags | EXCEPTION_ON_STACK) ++ *exception = copyPAL_SEHException(exception); + + if (g_hardwareExceptionHandler(exception)) + { +@@ -292,6 +312,9 @@ SEHProcessException(PAL_SEHException* exception) + + if (CatchHardwareExceptionHolder::IsEnabled()) + { ++ if(exceptionRecord->ExceptionFlags | EXCEPTION_ON_STACK) ++ *exception = copyPAL_SEHException(exception); ++ + PAL_ThrowExceptionFromContext(exception->GetContextRecord(), exception); + } + } +diff --git a/src/pal/src/exception/signal.cpp b/src/pal/src/exception/signal.cpp +index bf48619..d42ba38 100644 +--- a/src/pal/src/exception/signal.cpp ++++ b/src/pal/src/exception/signal.cpp +@@ -801,32 +801,32 @@ static bool common_signal_handler(int code, siginfo_t *siginfo, void *sigcontext + { + sigset_t signal_set; + CONTEXT signalContextRecord; +- CONTEXT *contextRecord; +- EXCEPTION_RECORD *exceptionRecord; ++ CONTEXT contextRecord; ++ EXCEPTION_RECORD exceptionRecord; + native_context_t *ucontext; + + ucontext = (native_context_t *)sigcontext; + g_common_signal_handler_context_locvar_offset = (int)((char*)&signalContextRecord - (char*)__builtin_frame_address(0)); + +- AllocateExceptionRecords(&exceptionRecord, &contextRecord); ++ //AllocateExceptionRecords(&exceptionRecord, &contextRecord); + +- exceptionRecord->ExceptionCode = CONTEXTGetExceptionCodeForSignal(siginfo, ucontext); +- exceptionRecord->ExceptionFlags = EXCEPTION_IS_SIGNAL; +- exceptionRecord->ExceptionRecord = NULL; +- exceptionRecord->ExceptionAddress = GetNativeContextPC(ucontext); +- exceptionRecord->NumberParameters = numParams; ++ exceptionRecord.ExceptionCode = CONTEXTGetExceptionCodeForSignal(siginfo, ucontext); ++ exceptionRecord.ExceptionFlags = EXCEPTION_IS_SIGNAL | EXCEPTION_ON_STACK; ++ exceptionRecord.ExceptionRecord = NULL; ++ exceptionRecord.ExceptionAddress = GetNativeContextPC(ucontext); ++ exceptionRecord.NumberParameters = numParams; + + va_list params; + va_start(params, numParams); + + for (int i = 0; i < numParams; i++) + { +- exceptionRecord->ExceptionInformation[i] = va_arg(params, size_t); ++ exceptionRecord.ExceptionInformation[i] = va_arg(params, size_t); + } + + // Pre-populate context with data from current frame, because ucontext doesn't have some data (e.g. SS register) + // which is required for restoring context +- RtlCaptureContext(contextRecord); ++ RtlCaptureContext(&contextRecord); + + ULONG contextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT; + +@@ -837,7 +837,7 @@ static bool common_signal_handler(int code, siginfo_t *siginfo, void *sigcontext + // Fill context record with required information. from pal.h: + // On non-Win32 platforms, the CONTEXT pointer in the + // PEXCEPTION_POINTERS will contain at least the CONTEXT_CONTROL registers. +- CONTEXTFromNativeContext(ucontext, contextRecord, contextFlags); ++ CONTEXTFromNativeContext(ucontext, &contextRecord, contextFlags); + + /* Unmask signal so we can receive it again */ + sigemptyset(&signal_set); +@@ -848,17 +848,17 @@ static bool common_signal_handler(int code, siginfo_t *siginfo, void *sigcontext + ASSERT("pthread_sigmask failed; error number is %d\n", sigmaskRet); + } + +- contextRecord->ContextFlags |= CONTEXT_EXCEPTION_ACTIVE; ++ contextRecord.ContextFlags |= CONTEXT_EXCEPTION_ACTIVE; + +- memcpy_s(&signalContextRecord, sizeof(CONTEXT), contextRecord, sizeof(CONTEXT)); ++ memcpy_s(&signalContextRecord, sizeof(CONTEXT), &contextRecord, sizeof(CONTEXT)); + + // The exception object takes ownership of the exceptionRecord and contextRecord +- PAL_SEHException exception(exceptionRecord, contextRecord); ++ PAL_SEHException exception(&exceptionRecord, &contextRecord); + + if (SEHProcessException(&exception)) + { + // Exception handling may have modified the context, so update it. +- CONTEXTToNativeContext(contextRecord, ucontext); ++ CONTEXTToNativeContext(&contextRecord, ucontext); + return true; + } + +-- +2.7.4 + diff --git a/packaging/0029-Move-exception-allocation-to-PAL_SEHException.patch b/packaging/0029-Move-exception-allocation-to-PAL_SEHException.patch new file mode 100644 index 0000000000..67e9f9e5e4 --- /dev/null +++ b/packaging/0029-Move-exception-allocation-to-PAL_SEHException.patch @@ -0,0 +1,179 @@ +From d7ea540ec5ffa7e44627b61f0cfa480341ec64ec Mon Sep 17 00:00:00 2001 +From: Mikhail Labiuk <m.labiuk@samsung.com> +Date: Tue, 20 Feb 2018 14:26:35 +0300 +Subject: [PATCH 29/47] Move exception allocation to PAL_SEHException + +PAL_SEHException::EnsureExceptionRecordsOnHeap() moves exception record +to heap if needed. + +fix https://github.com/dotnet/coreclr/issues/16338 +--- + src/pal/inc/pal.h | 35 ++++++++++++++++++++++++++++++----- + src/pal/src/exception/seh.cpp | 25 ++----------------------- + src/pal/src/exception/signal.cpp | 6 ++---- + 3 files changed, 34 insertions(+), 32 deletions(-) + +diff --git a/src/pal/inc/pal.h b/src/pal/inc/pal.h +index e3bfa40..e241219 100644 +--- a/src/pal/inc/pal.h ++++ b/src/pal/inc/pal.h +@@ -3888,8 +3888,6 @@ PAL_BindResources(IN LPCSTR lpDomain); + + #define EXCEPTION_IS_SIGNAL 0x100 + +-#define EXCEPTION_ON_STACK 0x400 +- + #define EXCEPTION_MAXIMUM_PARAMETERS 15 + + // Index in the ExceptionInformation array where we will keep the reference +@@ -5796,6 +5794,11 @@ PAL_FreeExceptionRecords( + IN EXCEPTION_RECORD *exceptionRecord, + IN CONTEXT *contextRecord); + ++VOID ++AllocateExceptionRecords( ++ EXCEPTION_RECORD** exceptionRecord, ++ CONTEXT** contextRecord); ++ + #define EXCEPTION_CONTINUE_SEARCH 0 + #define EXCEPTION_EXECUTE_HANDLER 1 + #define EXCEPTION_CONTINUE_EXECUTION -1 +@@ -5810,14 +5813,14 @@ private: + ExceptionPointers.ExceptionRecord = ex.ExceptionPointers.ExceptionRecord; + ExceptionPointers.ContextRecord = ex.ExceptionPointers.ContextRecord; + TargetFrameSp = ex.TargetFrameSp; ++ RecordsOnStack = ex.RecordsOnStack; + + ex.Clear(); + } + + void FreeRecords() + { +- if (ExceptionPointers.ExceptionRecord != NULL && +- ! (ExceptionPointers.ExceptionRecord->ExceptionFlags | EXCEPTION_ON_STACK) ) ++ if (ExceptionPointers.ExceptionRecord != NULL && !RecordsOnStack ) + { + PAL_FreeExceptionRecords(ExceptionPointers.ExceptionRecord, ExceptionPointers.ContextRecord); + ExceptionPointers.ExceptionRecord = NULL; +@@ -5829,12 +5832,14 @@ public: + EXCEPTION_POINTERS ExceptionPointers; + // Target frame stack pointer set before the 2nd pass. + SIZE_T TargetFrameSp; ++ bool RecordsOnStack; + +- PAL_SEHException(EXCEPTION_RECORD *pExceptionRecord, CONTEXT *pContextRecord) ++ PAL_SEHException(EXCEPTION_RECORD *pExceptionRecord, CONTEXT *pContextRecord, bool onStack = false) + { + ExceptionPointers.ExceptionRecord = pExceptionRecord; + ExceptionPointers.ContextRecord = pContextRecord; + TargetFrameSp = NoTargetFrameSp; ++ RecordsOnStack = onStack; + } + + PAL_SEHException() +@@ -5870,6 +5875,26 @@ public: + ExceptionPointers.ExceptionRecord = NULL; + ExceptionPointers.ContextRecord = NULL; + TargetFrameSp = NoTargetFrameSp; ++ RecordsOnStack = false; ++ } ++ ++ void EnsureExceptionRecordsOnHeap() ++ { ++ if( !RecordsOnStack || ExceptionPointers.ExceptionRecord == NULL) ++ { ++ return; ++ } ++ ++ CONTEXT* contextRecordCopy; ++ EXCEPTION_RECORD* exceptionRecordCopy; ++ AllocateExceptionRecords(&exceptionRecordCopy, &contextRecordCopy); ++ ++ *exceptionRecordCopy = *ExceptionPointers.ExceptionRecord; ++ ExceptionPointers.ExceptionRecord = exceptionRecordCopy; ++ *contextRecordCopy = *ExceptionPointers.ContextRecord; ++ ExceptionPointers.ContextRecord = contextRecordCopy; ++ ++ RecordsOnStack = false; + } + + CONTEXT* GetContextRecord() +diff --git a/src/pal/src/exception/seh.cpp b/src/pal/src/exception/seh.cpp +index a7d4ad9..27766f2 100644 +--- a/src/pal/src/exception/seh.cpp ++++ b/src/pal/src/exception/seh.cpp +@@ -232,23 +232,6 @@ void ThrowExceptionHelper(PAL_SEHException* ex) + throw std::move(*ex); + } + +-static PAL_SEHException copyPAL_SEHException(PAL_SEHException* src) +-{ +- CONTEXT* contextRecord = src->GetContextRecord(); +- EXCEPTION_RECORD* exceptionRecord = src->GetExceptionRecord(); +- +- CONTEXT* contextRecordCopy; +- EXCEPTION_RECORD* exceptionRecordCopy; +- AllocateExceptionRecords(&exceptionRecordCopy, &contextRecordCopy); +- +- *exceptionRecordCopy = *exceptionRecord; +- exceptionRecordCopy->ExceptionFlags &= ~EXCEPTION_ON_STACK; +- *contextRecordCopy = *contextRecord; +- return PAL_SEHException(exceptionRecordCopy, contextRecordCopy); +-} +- +- +- + /*++ + Function: + SEHProcessException +@@ -296,10 +279,8 @@ SEHProcessException(PAL_SEHException* exception) + PROCAbort(); + } + } +- +- if(exceptionRecord->ExceptionFlags | EXCEPTION_ON_STACK) +- *exception = copyPAL_SEHException(exception); + ++ exception->EnsureExceptionRecordsOnHeap(); + if (g_hardwareExceptionHandler(exception)) + { + // The exception happened in managed code and the execution should continue. +@@ -312,9 +293,7 @@ SEHProcessException(PAL_SEHException* exception) + + if (CatchHardwareExceptionHolder::IsEnabled()) + { +- if(exceptionRecord->ExceptionFlags | EXCEPTION_ON_STACK) +- *exception = copyPAL_SEHException(exception); +- ++ exception->EnsureExceptionRecordsOnHeap(); + PAL_ThrowExceptionFromContext(exception->GetContextRecord(), exception); + } + } +diff --git a/src/pal/src/exception/signal.cpp b/src/pal/src/exception/signal.cpp +index d42ba38..3b4bec8 100644 +--- a/src/pal/src/exception/signal.cpp ++++ b/src/pal/src/exception/signal.cpp +@@ -808,10 +808,8 @@ static bool common_signal_handler(int code, siginfo_t *siginfo, void *sigcontext + ucontext = (native_context_t *)sigcontext; + g_common_signal_handler_context_locvar_offset = (int)((char*)&signalContextRecord - (char*)__builtin_frame_address(0)); + +- //AllocateExceptionRecords(&exceptionRecord, &contextRecord); +- + exceptionRecord.ExceptionCode = CONTEXTGetExceptionCodeForSignal(siginfo, ucontext); +- exceptionRecord.ExceptionFlags = EXCEPTION_IS_SIGNAL | EXCEPTION_ON_STACK; ++ exceptionRecord.ExceptionFlags = EXCEPTION_IS_SIGNAL; + exceptionRecord.ExceptionRecord = NULL; + exceptionRecord.ExceptionAddress = GetNativeContextPC(ucontext); + exceptionRecord.NumberParameters = numParams; +@@ -853,7 +851,7 @@ static bool common_signal_handler(int code, siginfo_t *siginfo, void *sigcontext + memcpy_s(&signalContextRecord, sizeof(CONTEXT), &contextRecord, sizeof(CONTEXT)); + + // The exception object takes ownership of the exceptionRecord and contextRecord +- PAL_SEHException exception(&exceptionRecord, &contextRecord); ++ PAL_SEHException exception(&exceptionRecord, &contextRecord, true); + + if (SEHProcessException(&exception)) + { +-- +2.7.4 + diff --git a/packaging/0030-Remove-exception-records-allocation-from-pal.h.patch b/packaging/0030-Remove-exception-records-allocation-from-pal.h.patch new file mode 100644 index 0000000000..cf099e3a6e --- /dev/null +++ b/packaging/0030-Remove-exception-records-allocation-from-pal.h.patch @@ -0,0 +1,116 @@ +From 318d7a2dff13634670ef005c4af8115e5c780eb3 Mon Sep 17 00:00:00 2001 +From: Mikhail Labiuk <m.labiuk@samsung.com> +Date: Tue, 20 Feb 2018 16:59:27 +0300 +Subject: [PATCH 30/47] Remove exception records allocation from pal.h + +--- + src/pal/inc/pal.h | 24 ------------------------ + src/pal/src/exception/seh.cpp | 36 ++++++++++++++++++++++++++++++++++-- + 2 files changed, 34 insertions(+), 26 deletions(-) + +diff --git a/src/pal/inc/pal.h b/src/pal/inc/pal.h +index e241219..199ab94 100644 +--- a/src/pal/inc/pal.h ++++ b/src/pal/inc/pal.h +@@ -5794,11 +5794,6 @@ PAL_FreeExceptionRecords( + IN EXCEPTION_RECORD *exceptionRecord, + IN CONTEXT *contextRecord); + +-VOID +-AllocateExceptionRecords( +- EXCEPTION_RECORD** exceptionRecord, +- CONTEXT** contextRecord); +- + #define EXCEPTION_CONTINUE_SEARCH 0 + #define EXCEPTION_EXECUTE_HANDLER 1 + #define EXCEPTION_CONTINUE_EXECUTION -1 +@@ -5878,25 +5873,6 @@ public: + RecordsOnStack = false; + } + +- void EnsureExceptionRecordsOnHeap() +- { +- if( !RecordsOnStack || ExceptionPointers.ExceptionRecord == NULL) +- { +- return; +- } +- +- CONTEXT* contextRecordCopy; +- EXCEPTION_RECORD* exceptionRecordCopy; +- AllocateExceptionRecords(&exceptionRecordCopy, &contextRecordCopy); +- +- *exceptionRecordCopy = *ExceptionPointers.ExceptionRecord; +- ExceptionPointers.ExceptionRecord = exceptionRecordCopy; +- *contextRecordCopy = *ExceptionPointers.ContextRecord; +- ExceptionPointers.ContextRecord = contextRecordCopy; +- +- RecordsOnStack = false; +- } +- + CONTEXT* GetContextRecord() + { + return ExceptionPointers.ContextRecord; +diff --git a/src/pal/src/exception/seh.cpp b/src/pal/src/exception/seh.cpp +index 27766f2..8477fd6 100644 +--- a/src/pal/src/exception/seh.cpp ++++ b/src/pal/src/exception/seh.cpp +@@ -234,6 +234,38 @@ void ThrowExceptionHelper(PAL_SEHException* ex) + + /*++ + Function: ++ EnsureExceptionRecordsOnHeap ++ ++ Helper function to move records from stack to heap. ++ ++Parameters: ++ PAL_SEHException* exception ++--*/ ++static void EnsureExceptionRecordsOnHeap(PAL_SEHException* exception) ++{ ++ if( !exception->RecordsOnStack || ++ exception->ExceptionPointers.ExceptionRecord == NULL ) ++ { ++ return; ++ } ++ ++ CONTEXT* contextRecord = exception->ExceptionPointers.ContextRecord; ++ EXCEPTION_RECORD* exceptionRecord = exception->ExceptionPointers.ExceptionRecord; ++ ++ CONTEXT* contextRecordCopy; ++ EXCEPTION_RECORD* exceptionRecordCopy; ++ AllocateExceptionRecords(&exceptionRecordCopy, &contextRecordCopy); ++ ++ *exceptionRecordCopy = *exceptionRecord; ++ *contextRecordCopy = *contextRecord; ++ ++ exception->ExceptionPointers.ExceptionRecord = exceptionRecordCopy; ++ exception->ExceptionPointers.ContextRecord = contextRecordCopy; ++ exception->RecordsOnStack = false; ++} ++ ++/*++ ++Function: + SEHProcessException + + Send the PAL exception to any handler registered. +@@ -280,7 +312,7 @@ SEHProcessException(PAL_SEHException* exception) + } + } + +- exception->EnsureExceptionRecordsOnHeap(); ++ EnsureExceptionRecordsOnHeap(exception); + if (g_hardwareExceptionHandler(exception)) + { + // The exception happened in managed code and the execution should continue. +@@ -293,7 +325,7 @@ SEHProcessException(PAL_SEHException* exception) + + if (CatchHardwareExceptionHolder::IsEnabled()) + { +- exception->EnsureExceptionRecordsOnHeap(); ++ EnsureExceptionRecordsOnHeap(exception); + PAL_ThrowExceptionFromContext(exception->GetContextRecord(), exception); + } + } +-- +2.7.4 + diff --git a/packaging/0031-Fix-preventing-memory-allocation-in-signal-handler.patch b/packaging/0031-Fix-preventing-memory-allocation-in-signal-handler.patch new file mode 100644 index 0000000000..8520d2608d --- /dev/null +++ b/packaging/0031-Fix-preventing-memory-allocation-in-signal-handler.patch @@ -0,0 +1,74 @@ +From 53c2a4bb7536cb6273e5ba58ee15f007978ba742 Mon Sep 17 00:00:00 2001 +From: Jan Vorlicek <janvorli@microsoft.com> +Date: Thu, 22 Feb 2018 01:48:43 +0100 +Subject: [PATCH 31/47] Fix preventing memory allocation in signal handler + +There was a subtle bug. When the hardware exception handler returns back +to the signal handler, the exception's CONTEXT record may contain +modified registers and so the changes need to be propagated back to the +signal context. But the recent change #16384 was restoring the signal +context from the originally grabbed context instead of the one that's +pointed to by the exception, which is different. + +I have also added a little optimization - the contextRecord that was +added is not needed, since the signalContextRecord can be used as the +initial context record for the exception. So we can save the +contextRecord and also copying to the signalContextRecord from it. +--- + src/pal/src/exception/signal.cpp | 13 +++++-------- + 1 file changed, 5 insertions(+), 8 deletions(-) + +diff --git a/src/pal/src/exception/signal.cpp b/src/pal/src/exception/signal.cpp +index 3b4bec8..430cd05 100644 +--- a/src/pal/src/exception/signal.cpp ++++ b/src/pal/src/exception/signal.cpp +@@ -801,7 +801,6 @@ static bool common_signal_handler(int code, siginfo_t *siginfo, void *sigcontext + { + sigset_t signal_set; + CONTEXT signalContextRecord; +- CONTEXT contextRecord; + EXCEPTION_RECORD exceptionRecord; + native_context_t *ucontext; + +@@ -824,7 +823,7 @@ static bool common_signal_handler(int code, siginfo_t *siginfo, void *sigcontext + + // Pre-populate context with data from current frame, because ucontext doesn't have some data (e.g. SS register) + // which is required for restoring context +- RtlCaptureContext(&contextRecord); ++ RtlCaptureContext(&signalContextRecord); + + ULONG contextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT; + +@@ -835,7 +834,7 @@ static bool common_signal_handler(int code, siginfo_t *siginfo, void *sigcontext + // Fill context record with required information. from pal.h: + // On non-Win32 platforms, the CONTEXT pointer in the + // PEXCEPTION_POINTERS will contain at least the CONTEXT_CONTROL registers. +- CONTEXTFromNativeContext(ucontext, &contextRecord, contextFlags); ++ CONTEXTFromNativeContext(ucontext, &signalContextRecord, contextFlags); + + /* Unmask signal so we can receive it again */ + sigemptyset(&signal_set); +@@ -846,17 +845,15 @@ static bool common_signal_handler(int code, siginfo_t *siginfo, void *sigcontext + ASSERT("pthread_sigmask failed; error number is %d\n", sigmaskRet); + } + +- contextRecord.ContextFlags |= CONTEXT_EXCEPTION_ACTIVE; +- +- memcpy_s(&signalContextRecord, sizeof(CONTEXT), &contextRecord, sizeof(CONTEXT)); ++ signalContextRecord.ContextFlags |= CONTEXT_EXCEPTION_ACTIVE; + + // The exception object takes ownership of the exceptionRecord and contextRecord +- PAL_SEHException exception(&exceptionRecord, &contextRecord, true); ++ PAL_SEHException exception(&exceptionRecord, &signalContextRecord, true); + + if (SEHProcessException(&exception)) + { + // Exception handling may have modified the context, so update it. +- CONTEXTToNativeContext(&contextRecord, ucontext); ++ CONTEXTToNativeContext(exception.ExceptionPointers.ContextRecord, ucontext); + return true; + } + +-- +2.7.4 + diff --git a/packaging/0032-Fix-Use-of-EventPipeConfiguration-After-it-has-Been-.patch b/packaging/0032-Fix-Use-of-EventPipeConfiguration-After-it-has-Been-.patch new file mode 100644 index 0000000000..79ad1e0bd1 --- /dev/null +++ b/packaging/0032-Fix-Use-of-EventPipeConfiguration-After-it-has-Been-.patch @@ -0,0 +1,53 @@ +From 10f02e5641055b97eca8964dda85542885fa753b Mon Sep 17 00:00:00 2001 +From: Konstantin Baladurin <k.baladurin@partner.samsung.com> +Date: Wed, 30 May 2018 19:34:11 +0300 +Subject: [PATCH 32/47] Fix Use of EventPipeConfiguration After it has Been + Freed on Shutdown + +Port #16704 for release/2.0.0 +--- + src/vm/eventpipe.cpp | 26 ++++++++++++++++---------- + 1 file changed, 16 insertions(+), 10 deletions(-) + +diff --git a/src/vm/eventpipe.cpp b/src/vm/eventpipe.cpp +index 50909a1..5ef3ec7 100644 +--- a/src/vm/eventpipe.cpp ++++ b/src/vm/eventpipe.cpp +@@ -91,18 +91,24 @@ void EventPipe::Shutdown() + } + CONTRACTL_END; + ++ // Mark tracing as no longer initialized. ++ s_tracingInitialized = false; ++ + Disable(); + +- if(s_pConfig != NULL) +- { +- delete(s_pConfig); +- s_pConfig = NULL; +- } +- if(s_pBufferManager != NULL) +- { +- delete(s_pBufferManager); +- s_pBufferManager = NULL; +- } ++ // Save pointers to the configuration and buffer manager. ++ EventPipeConfiguration *pConfig = s_pConfig; ++ EventPipeBufferManager *pBufferManager = s_pBufferManager; ++ ++ // Set the static pointers to NULL so that the rest of the EventPipe knows that they are no longer available. ++ // Flush process write buffers to make sure other threads can see the change. ++ s_pConfig = NULL; ++ s_pBufferManager = NULL; ++ FlushProcessWriteBuffers(); ++ ++ // Free the configuration and buffer manager. ++ delete(pConfig); ++ delete(pBufferManager); + } + + void EventPipe::Enable( +-- +2.7.4 + diff --git a/packaging/0033-Revert-clear-cache-after-NI-reloc.patch b/packaging/0033-Revert-clear-cache-after-NI-reloc.patch new file mode 100644 index 0000000000..b0e06f776c --- /dev/null +++ b/packaging/0033-Revert-clear-cache-after-NI-reloc.patch @@ -0,0 +1,26 @@ +From 8badee4404fe637bd9a8625785cd86fd3e310590 Mon Sep 17 00:00:00 2001 +From: Konstantin Baladurin <k.baladurin@partner.samsung.com> +Date: Fri, 1 Jun 2018 17:54:18 +0300 +Subject: [PATCH 33/47] Revert "clear cache after NI reloc" + +This reverts commit 2427902ddfff7dd868b79d95865afee5c3a257c4. +--- + src/vm/peimagelayout.cpp | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/src/vm/peimagelayout.cpp b/src/vm/peimagelayout.cpp +index 5dc0e64..34ba4d8 100644 +--- a/src/vm/peimagelayout.cpp ++++ b/src/vm/peimagelayout.cpp +@@ -227,8 +227,6 @@ void PEImageLayout::ApplyBaseRelocations() + #ifdef _TARGET_ARM_ + case IMAGE_REL_BASED_THUMB_MOV32: + PutThumb2Mov32((UINT16 *)address, GetThumb2Mov32((UINT16 *)address) + delta); +- +- ClrFlushInstructionCache(address, 8); + break; + #endif + +-- +2.7.4 + diff --git a/packaging/0034-PEImageLayout-clear-instruction-cache-after-relocati.patch b/packaging/0034-PEImageLayout-clear-instruction-cache-after-relocati.patch new file mode 100644 index 0000000000..297d62797f --- /dev/null +++ b/packaging/0034-PEImageLayout-clear-instruction-cache-after-relocati.patch @@ -0,0 +1,81 @@ +From 3aa9a0340557631f60b1ded4d5c629cf381092aa Mon Sep 17 00:00:00 2001 +From: Konstantin Baladurin <k.baladurin@partner.samsung.com> +Date: Fri, 30 Mar 2018 10:18:04 +0300 +Subject: [PATCH 34/47] PEImageLayout: clear instruction cache after + relocations + +It fixes crashes on arm when using AOT images. +--- + src/vm/peimagelayout.cpp | 22 ++++++++++++++++++++++ + 1 file changed, 22 insertions(+) + +diff --git a/src/vm/peimagelayout.cpp b/src/vm/peimagelayout.cpp +index 34ba4d8..4bfbe40 100644 +--- a/src/vm/peimagelayout.cpp ++++ b/src/vm/peimagelayout.cpp +@@ -150,6 +150,8 @@ void PEImageLayout::ApplyBaseRelocations() + SIZE_T cbWriteableRegion = 0; + DWORD dwOldProtection = 0; + ++ BOOL bRelocDone = FALSE; ++ + COUNT_T dirPos = 0; + while (dirPos < dirSize) + { +@@ -176,10 +178,20 @@ void PEImageLayout::ApplyBaseRelocations() + // Restore the protection + if (dwOldProtection != 0) + { ++ BOOL bExecRegion = (dwOldProtection & (PAGE_EXECUTE | PAGE_EXECUTE_READ | ++ PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY)) != 0; ++ + if (!ClrVirtualProtect(pWriteableRegion, cbWriteableRegion, + dwOldProtection, &dwOldProtection)) + ThrowLastError(); + ++ if (bRelocDone && bExecRegion) ++ { ++ ClrFlushInstructionCache(pWriteableRegion, cbWriteableRegion); ++ } ++ ++ bRelocDone = FALSE; ++ + dwOldProtection = 0; + } + +@@ -222,11 +234,13 @@ void PEImageLayout::ApplyBaseRelocations() + { + case IMAGE_REL_BASED_PTR: + *(TADDR *)address += delta; ++ bRelocDone = TRUE; + break; + + #ifdef _TARGET_ARM_ + case IMAGE_REL_BASED_THUMB_MOV32: + PutThumb2Mov32((UINT16 *)address, GetThumb2Mov32((UINT16 *)address) + delta); ++ bRelocDone = TRUE; + break; + #endif + +@@ -245,10 +259,18 @@ void PEImageLayout::ApplyBaseRelocations() + + if (dwOldProtection != 0) + { ++ BOOL bExecRegion = (dwOldProtection & (PAGE_EXECUTE | PAGE_EXECUTE_READ | ++ PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY)) != 0; ++ + // Restore the protection + if (!ClrVirtualProtect(pWriteableRegion, cbWriteableRegion, + dwOldProtection, &dwOldProtection)) + ThrowLastError(); ++ ++ if (bRelocDone && bExecRegion) ++ { ++ ClrFlushInstructionCache(pWriteableRegion, cbWriteableRegion); ++ } + } + } + #endif // FEATURE_PREJIT +-- +2.7.4 + diff --git a/packaging/0035-PEImageLayout-flush-instruction-cache-only-for-pages.patch b/packaging/0035-PEImageLayout-flush-instruction-cache-only-for-pages.patch new file mode 100644 index 0000000000..e986952668 --- /dev/null +++ b/packaging/0035-PEImageLayout-flush-instruction-cache-only-for-pages.patch @@ -0,0 +1,117 @@ +From c5539d864c61476446542bee351af3a125118ef3 Mon Sep 17 00:00:00 2001 +From: Konstantin Baladurin <k.baladurin@partner.samsung.com> +Date: Mon, 14 May 2018 12:35:09 +0300 +Subject: [PATCH 35/47] PEImageLayout: flush instruction cache only for pages + with relocs. + +We need to flush instruction cache only for pages that have relocations +instead of full sections because otherwise application's shared clean +memory is increased in some cases on Linux. +--- + src/vm/peimagelayout.cpp | 46 +++++++++++++++++++++++++++++----------------- + 1 file changed, 29 insertions(+), 17 deletions(-) + +diff --git a/src/vm/peimagelayout.cpp b/src/vm/peimagelayout.cpp +index 4bfbe40..5e0243b 100644 +--- a/src/vm/peimagelayout.cpp ++++ b/src/vm/peimagelayout.cpp +@@ -150,7 +150,10 @@ void PEImageLayout::ApplyBaseRelocations() + SIZE_T cbWriteableRegion = 0; + DWORD dwOldProtection = 0; + +- BOOL bRelocDone = FALSE; ++ BYTE * pFlushRegion = NULL; ++ SIZE_T cbFlushRegion = 0; ++ // The page size of PE file relocs is always 4096 bytes ++ const SIZE_T cbPageSize = 4096; + + COUNT_T dirPos = 0; + while (dirPos < dirSize) +@@ -185,13 +188,6 @@ void PEImageLayout::ApplyBaseRelocations() + dwOldProtection, &dwOldProtection)) + ThrowLastError(); + +- if (bRelocDone && bExecRegion) +- { +- ClrFlushInstructionCache(pWriteableRegion, cbWriteableRegion); +- } +- +- bRelocDone = FALSE; +- + dwOldProtection = 0; + } + +@@ -224,6 +220,7 @@ void PEImageLayout::ApplyBaseRelocations() + } + } + ++ BYTE* pEndAddressToFlush = NULL; + for (COUNT_T fixupIndex = 0; fixupIndex < fixupsCount; fixupIndex++) + { + USHORT fixup = VAL16(fixups[fixupIndex]); +@@ -234,13 +231,13 @@ void PEImageLayout::ApplyBaseRelocations() + { + case IMAGE_REL_BASED_PTR: + *(TADDR *)address += delta; +- bRelocDone = TRUE; ++ pEndAddressToFlush = max(pEndAddressToFlush, address + sizeof(TADDR)); + break; + + #ifdef _TARGET_ARM_ + case IMAGE_REL_BASED_THUMB_MOV32: + PutThumb2Mov32((UINT16 *)address, GetThumb2Mov32((UINT16 *)address) + delta); +- bRelocDone = TRUE; ++ pEndAddressToFlush = max(pEndAddressToFlush, address + 8); + break; + #endif + +@@ -253,24 +250,39 @@ void PEImageLayout::ApplyBaseRelocations() + } + } + ++ BOOL bExecRegion = (dwOldProtection & (PAGE_EXECUTE | PAGE_EXECUTE_READ | ++ PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY)) != 0; ++ ++ if (bExecRegion && pEndAddressToFlush != NULL) ++ { ++ // If the current page is not next to the pending region to flush, flush the current pending region and start a new one ++ if (pageAddress >= pFlushRegion + cbFlushRegion + cbPageSize || pageAddress < pFlushRegion) ++ { ++ if (pFlushRegion != NULL) ++ { ++ ClrFlushInstructionCache(pFlushRegion, cbFlushRegion); ++ } ++ pFlushRegion = pageAddress; ++ } ++ ++ cbFlushRegion = pEndAddressToFlush - pFlushRegion; ++ } ++ + dirPos += fixupsSize; + } + _ASSERTE(dirSize == dirPos); + + if (dwOldProtection != 0) + { +- BOOL bExecRegion = (dwOldProtection & (PAGE_EXECUTE | PAGE_EXECUTE_READ | +- PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY)) != 0; +- + // Restore the protection + if (!ClrVirtualProtect(pWriteableRegion, cbWriteableRegion, + dwOldProtection, &dwOldProtection)) + ThrowLastError(); ++ } + +- if (bRelocDone && bExecRegion) +- { +- ClrFlushInstructionCache(pWriteableRegion, cbWriteableRegion); +- } ++ if (pFlushRegion != NULL) ++ { ++ ClrFlushInstructionCache(pFlushRegion, cbFlushRegion); + } + } + #endif // FEATURE_PREJIT +-- +2.7.4 + diff --git a/packaging/0036-Separate-sections-READONLY_VCHUNKS-and-READONLY_DICT.patch b/packaging/0036-Separate-sections-READONLY_VCHUNKS-and-READONLY_DICT.patch new file mode 100644 index 0000000000..de86c9ba41 --- /dev/null +++ b/packaging/0036-Separate-sections-READONLY_VCHUNKS-and-READONLY_DICT.patch @@ -0,0 +1,59 @@ +From 305bac1e1c272a45ed0682b8bfa871b6099496ae Mon Sep 17 00:00:00 2001 +From: Gleb Balykov <g.balykov@samsung.com> +Date: Wed, 28 Feb 2018 16:28:00 +0300 +Subject: [PATCH 36/47] Separate sections READONLY_VCHUNKS and + READONLY_DICTIONARY + +--- + src/inc/corcompile.h | 3 ++- + src/vm/dataimage.cpp | 4 +++- + src/zap/zapimage.cpp | 3 ++- + 3 files changed, 7 insertions(+), 3 deletions(-) + +diff --git a/src/inc/corcompile.h b/src/inc/corcompile.h +index 17fdfcb..1d1f0e4 100644 +--- a/src/inc/corcompile.h ++++ b/src/inc/corcompile.h +@@ -1329,7 +1329,8 @@ class ICorCompilePreloader + CORCOMPILE_SECTION(READONLY_HOT) \ + CORCOMPILE_SECTION(READONLY_WARM) \ + CORCOMPILE_SECTION(READONLY_COLD) \ +- CORCOMPILE_SECTION(READONLY_VCHUNKS_AND_DICTIONARY) \ ++ CORCOMPILE_SECTION(READONLY_VCHUNKS) \ ++ CORCOMPILE_SECTION(READONLY_DICTIONARY) \ + CORCOMPILE_SECTION(CLASS_COLD) \ + CORCOMPILE_SECTION(CROSS_DOMAIN_INFO) \ + CORCOMPILE_SECTION(METHOD_PRECODE_COLD) \ +diff --git a/src/vm/dataimage.cpp b/src/vm/dataimage.cpp +index 4e276fe..854f214 100644 +--- a/src/vm/dataimage.cpp ++++ b/src/vm/dataimage.cpp +@@ -749,8 +749,10 @@ FORCEINLINE static CorCompileSection GetSectionForNodeType(ZapNodeType type) + return CORCOMPILE_SECTION_READONLY_WARM; + + case NodeTypeForItemKind(DataImage::ITEM_DICTIONARY): ++ return CORCOMPILE_SECTION_READONLY_DICTIONARY; ++ + case NodeTypeForItemKind(DataImage::ITEM_VTABLE_CHUNK): +- return CORCOMPILE_SECTION_READONLY_VCHUNKS_AND_DICTIONARY; ++ return CORCOMPILE_SECTION_READONLY_VCHUNKS; + + // SECTION_CLASS_COLD + case NodeTypeForItemKind(DataImage::ITEM_PARAM_TYPEDESC): +diff --git a/src/zap/zapimage.cpp b/src/zap/zapimage.cpp +index 4c26946..4c1838f 100644 +--- a/src/zap/zapimage.cpp ++++ b/src/zap/zapimage.cpp +@@ -572,7 +572,8 @@ void ZapImage::AllocateVirtualSections() + #endif // defined(WIN64EXCEPTIONS) + + m_pPreloadSections[CORCOMPILE_SECTION_READONLY_WARM] = NewVirtualSection(pTextSection, IBCProfiledSection | WarmRange | ReadonlySection, sizeof(TADDR)); +- m_pPreloadSections[CORCOMPILE_SECTION_READONLY_VCHUNKS_AND_DICTIONARY] = NewVirtualSection(pTextSection, IBCProfiledSection | WarmRange | ReadonlySection, sizeof(TADDR)); ++ m_pPreloadSections[CORCOMPILE_SECTION_READONLY_VCHUNKS] = NewVirtualSection(pTextSection, IBCProfiledSection | WarmRange | ReadonlySection, sizeof(TADDR)); ++ m_pPreloadSections[CORCOMPILE_SECTION_READONLY_DICTIONARY] = NewVirtualSection(pTextSection, IBCProfiledSection | WarmRange | ReadonlySection, sizeof(TADDR)); + + // + // GC Info for methods which were not touched in profiling +-- +2.7.4 + diff --git a/packaging/0037-Remove-relocations-for-second-level-indirection-of-V.patch b/packaging/0037-Remove-relocations-for-second-level-indirection-of-V.patch new file mode 100644 index 0000000000..10b9f79891 --- /dev/null +++ b/packaging/0037-Remove-relocations-for-second-level-indirection-of-V.patch @@ -0,0 +1,1134 @@ +From 3170d91efb07798e9562a172de2355e6b3851cff Mon Sep 17 00:00:00 2001 +From: Gleb Balykov <g.balykov@samsung.com> +Date: Thu, 22 Mar 2018 21:14:16 +0300 +Subject: [PATCH 37/47] Remove relocations for second-level indirection of + Vtable in case FEATURE_NGEN_RELOCS_OPTIMIZATIONS is enabled. + +Introduce FEATURE_NGEN_RELOCS_OPTIMIZATIONS, under which NGEN specific relocations optimizations are enabled +--- + clrdefinitions.cmake | 3 ++ + src/debug/daccess/nidump.cpp | 43 ++++++++++++++++------ + src/debug/daccess/nidump.h | 9 +++-- + src/inc/corinfo.h | 14 ++++--- + src/inc/fixuppointer.h | 33 +++++++++++++++++ + src/inc/stdmacros.h | 2 + + src/jit/codegencommon.cpp | 23 ++++++++++++ + src/jit/codegenlegacy.cpp | 83 ++++++++++++++++++++++++++++++++++++----- + src/jit/importer.cpp | 7 ++-- + src/jit/lower.cpp | 51 ++++++++++++++++++++----- + src/jit/lower.h | 6 +++ + src/jit/morph.cpp | 17 +++++++-- + src/vm/arm/stubs.cpp | 26 +++++++++++++ + src/vm/array.cpp | 4 +- + src/vm/generics.cpp | 4 +- + src/vm/jitinterface.cpp | 16 ++++++-- + src/vm/method.cpp | 86 +++++++++++++++++++++++++++++++++++++------ + src/vm/method.hpp | 11 +++++- + src/vm/methodtable.cpp | 27 +++++++++++--- + src/vm/methodtable.h | 39 ++++++++++++-------- + src/vm/methodtable.inl | 4 +- + src/vm/methodtablebuilder.cpp | 4 +- + 22 files changed, 424 insertions(+), 88 deletions(-) + +diff --git a/clrdefinitions.cmake b/clrdefinitions.cmake +index 6db2b24..8ba01c5 100644 +--- a/clrdefinitions.cmake ++++ b/clrdefinitions.cmake +@@ -171,6 +171,9 @@ add_definitions(-DFEATURE_STRONGNAME_MIGRATION) + if (CLR_CMAKE_PLATFORM_UNIX OR CLR_CMAKE_TARGET_ARCH_ARM64) + add_definitions(-DFEATURE_STUBS_AS_IL) + endif () ++if (FEATURE_NGEN_RELOCS_OPTIMIZATIONS) ++ add_definitions(-DFEATURE_NGEN_RELOCS_OPTIMIZATIONS) ++endif(FEATURE_NGEN_RELOCS_OPTIMIZATIONS) + add_definitions(-DFEATURE_SVR_GC) + add_definitions(-DFEATURE_SYMDIFF) + if (CLR_CMAKE_PLATFORM_ARCH_AMD64) +diff --git a/src/debug/daccess/nidump.cpp b/src/debug/daccess/nidump.cpp +index 18bef97..2dde08f 100644 +--- a/src/debug/daccess/nidump.cpp ++++ b/src/debug/daccess/nidump.cpp +@@ -5973,7 +5973,9 @@ void NativeImageDumper::DumpTypes(PTR_Module module) + + for (COUNT_T i = 0; i < slotChunkCount; ++i) + { +- DumpMethodTableSlotChunk(m_discoveredSlotChunks[i].addr, m_discoveredSlotChunks[i].nSlots); ++ DumpMethodTableSlotChunk(m_discoveredSlotChunks[i].addr, ++ m_discoveredSlotChunks[i].nSlots, ++ m_discoveredSlotChunks[i].isRelative); + } + } + DisplayEndArray( "Total MethodTableSlotChunks", METHODTABLES ); +@@ -7239,8 +7241,9 @@ NativeImageDumper::DumpMethodTable( PTR_MethodTable mt, const char * name, + while (itIndirect.Next()) + { + SlotChunk sc; +- sc.addr = itIndirect.GetIndirectionSlot(); ++ sc.addr = dac_cast<TADDR>(itIndirect.GetIndirectionSlot()); + sc.nSlots = (WORD)itIndirect.GetNumSlots(); ++ sc.isRelative = MethodTable::VTableIndir2_t::isRelative; + m_discoveredSlotChunks.AppendEx(sc); + } + +@@ -7252,7 +7255,7 @@ NativeImageDumper::DumpMethodTable( PTR_MethodTable mt, const char * name, + DisplayStartElement( "Slot", ALWAYS ); + DisplayWriteElementInt( "Index", i, ALWAYS ); + TADDR base = dac_cast<TADDR>(&(mt->GetVtableIndirections()[i])); +- PTR_PCODE tgt = MethodTable::VTableIndir_t::GetValueMaybeNullAtPtr(base); ++ DPTR(MethodTable::VTableIndir2_t) tgt = MethodTable::VTableIndir_t::GetValueMaybeNullAtPtr(base); + DisplayWriteElementPointer( "Pointer", + DataPtrToDisplay(dac_cast<TADDR>(tgt)), + ALWAYS ); +@@ -7274,8 +7277,9 @@ NativeImageDumper::DumpMethodTable( PTR_MethodTable mt, const char * name, + DisplayEndElement( ALWAYS ); //Slot + + SlotChunk sc; +- sc.addr = tgt; ++ sc.addr = dac_cast<TADDR>(tgt); + sc.nSlots = (mt->GetNumVtableSlots() - mt->GetNumVirtuals()); ++ sc.isRelative = false; + m_discoveredSlotChunks.AppendEx(sc); + } + else if (mt->HasSingleNonVirtualSlot()) +@@ -7411,25 +7415,42 @@ NativeImageDumper::DumpMethodTable( PTR_MethodTable mt, const char * name, + #endif + + void +-NativeImageDumper::DumpMethodTableSlotChunk( PTR_PCODE slotChunk, COUNT_T numSlots ) ++NativeImageDumper::DumpMethodTableSlotChunk( TADDR slotChunk, COUNT_T numSlots, bool isRelative ) + { + IF_OPT( METHODTABLES ) + { +- DisplayStartStructure( "MethodTableSlotChunk", DPtrToPreferredAddr(slotChunk), numSlots * sizeof(PCODE), +- METHODTABLES ); ++ COUNT_T slotsSize; ++ if (isRelative) ++ { ++ slotsSize = numSlots * sizeof(RelativePointer<PCODE>); ++ } ++ else ++ { ++ slotsSize = numSlots * sizeof(PCODE); ++ } ++ DisplayStartStructure( "MethodTableSlotChunk", DataPtrToDisplay(slotChunk), slotsSize, METHODTABLES ); + + IF_OPT(VERBOSE_TYPES) + { + DisplayStartList( W("[%-4s]: %s (%s)"), ALWAYS ); + for( unsigned i = 0; i < numSlots; ++i ) + { +- DumpSlot(i, slotChunk[i]); ++ PCODE target; ++ if (isRelative) ++ { ++ target = RelativePointer<PCODE>::GetValueMaybeNullAtPtr(slotChunk + i * sizeof(RelativePointer<PCODE>)); ++ } ++ else ++ { ++ target = dac_cast<PTR_PCODE>(slotChunk)[i]; ++ } ++ ++ DumpSlot(i, target); + } + DisplayEndList( ALWAYS ); //Slot list + } + else +- CoverageRead( PTR_TO_TADDR(slotChunk), +- numSlots * sizeof(PCODE) ); ++ CoverageRead( slotChunk, slotsSize ); + DisplayEndStructure(ALWAYS); //Slot chunk + } + } +@@ -7802,7 +7823,7 @@ void NativeImageDumper::DumpMethodDesc( PTR_MethodDesc md, PTR_Module module ) + } + if ( md->HasNonVtableSlot() ) + { +- DisplayWriteElementInt( "Slot", (DWORD)(PTR_TO_TADDR(md->GetAddrOfSlot()) - PTR_TO_TADDR(md)), ALWAYS); ++ DisplayWriteElementInt( "Slot", (DWORD)(md->GetAddrOfSlot() - PTR_TO_TADDR(md)), ALWAYS); + } + if (md->HasNativeCodeSlot()) + { +diff --git a/src/debug/daccess/nidump.h b/src/debug/daccess/nidump.h +index d14eb89..f0c8e7f 100644 +--- a/src/debug/daccess/nidump.h ++++ b/src/debug/daccess/nidump.h +@@ -195,7 +195,7 @@ public: + PTR_Module module ); + + #ifndef STUB_DISPATCH_ALL +- void DumpMethodTableSlotChunk( PTR_PCODE slotChunk, COUNT_T size ); ++ void DumpMethodTableSlotChunk( TADDR slotChunk, COUNT_T size, bool ); + #endif + + void DumpSlot( unsigned index, PCODE tgt ); +@@ -479,6 +479,8 @@ private: + template<typename T> + TADDR DPtrToPreferredAddr( T ptr ); + ++ TADDR DPtrToPreferredAddr( TADDR tptr ); ++ + void DumpAssemblySignature(CORCOMPILE_ASSEMBLY_SIGNATURE & assemblySignature); + + SIZE_T CountFields( PTR_MethodTable mt ); +@@ -501,12 +503,13 @@ private: + + struct SlotChunk + { +- PTR_PCODE addr; ++ TADDR addr; + WORD nSlots; ++ bool isRelative; + + inline bool operator==(const SlotChunk& sc) const + { +- return (addr == sc.addr) && (nSlots == sc.nSlots); ++ return (addr == sc.addr) && (nSlots == sc.nSlots) && (isRelative == sc.isRelative); + } + + inline bool operator<(const SlotChunk& sc) const +diff --git a/src/inc/corinfo.h b/src/inc/corinfo.h +index 63ade7f..1a6843c 100644 +--- a/src/inc/corinfo.h ++++ b/src/inc/corinfo.h +@@ -970,8 +970,9 @@ enum CorInfoIntrinsics + enum InfoAccessType + { + IAT_VALUE, // The info value is directly available +- IAT_PVALUE, // The value needs to be accessed via an indirection +- IAT_PPVALUE // The value needs to be accessed via a double indirection ++ IAT_PVALUE, // The value needs to be accessed via an indirection ++ IAT_RELPVALUE, // The value needs to be accessed via a relative indirection ++ IAT_PPVALUE // The value needs to be accessed via a double indirection + }; + + enum CorInfoGCType +@@ -1236,6 +1237,7 @@ struct CORINFO_METHOD_INFO + // Constant Lookups are either: + // IAT_VALUE: immediate (relocatable) values, + // IAT_PVALUE: immediate values access via an indirection through an immediate (relocatable) address ++// IAT_RELPVALUE: immediate values access via a relative indirection through an immediate offset + // IAT_PPVALUE: immediate values access via a double indirection through an immediate (relocatable) address + // + // Runtime Lookups +@@ -1261,9 +1263,10 @@ struct CORINFO_CONST_LOOKUP + // If the handle is obtained at compile-time, then this handle is the "exact" handle (class, method, or field) + // Otherwise, it's a representative... + // If accessType is +- // IAT_VALUE --> "handle" stores the real handle or "addr " stores the computed address +- // IAT_PVALUE --> "addr" stores a pointer to a location which will hold the real handle +- // IAT_PPVALUE --> "addr" stores a double indirection to a location which will hold the real handle ++ // IAT_VALUE --> "handle" stores the real handle or "addr " stores the computed address ++ // IAT_PVALUE --> "addr" stores a pointer to a location which will hold the real handle ++ // IAT_RELPVALUE --> "addr" stores a relative pointer to a location which will hold the real handle ++ // IAT_PPVALUE --> "addr" stores a double indirection to a location which will hold the real handle + + InfoAccessType accessType; + union +@@ -1354,6 +1357,7 @@ struct CORINFO_LOOKUP + // Otherwise, it's a representative... If accessType is + // IAT_VALUE --> "handle" stores the real handle or "addr " stores the computed address + // IAT_PVALUE --> "addr" stores a pointer to a location which will hold the real handle ++ // IAT_RELPVALUE --> "addr" stores a relative pointer to a location which will hold the real handle + // IAT_PPVALUE --> "addr" stores a double indirection to a location which will hold the real handle + CORINFO_CONST_LOOKUP constLookup; + }; +diff --git a/src/inc/fixuppointer.h b/src/inc/fixuppointer.h +index 5a897e4..abed1f9 100644 +--- a/src/inc/fixuppointer.h ++++ b/src/inc/fixuppointer.h +@@ -156,6 +156,26 @@ public: + } + #endif // DACCESS_COMPILE + ++ static TADDR GetRelativeMaybeNull(TADDR base, TADDR addr) ++ { ++ LIMITED_METHOD_DAC_CONTRACT; ++ if (addr == NULL) ++ { ++ return NULL; ++ } ++ else ++ { ++ return addr - base; ++ } ++ } ++ ++ static TADDR GetRelative(TADDR base, TADDR addr) ++ { ++ LIMITED_METHOD_DAC_CONTRACT; ++ PRECONDITION(addr != NULL); ++ return addr - base; ++ } ++ + private: + #ifndef DACCESS_COMPILE + Volatile<TADDR> m_delta; +@@ -721,6 +741,19 @@ public: + } + #endif + ++ static TADDR GetRelativeMaybeNull(TADDR base, TADDR addr) ++ { ++ LIMITED_METHOD_DAC_CONTRACT; ++ return addr; ++ } ++ ++ static TADDR GetRelative(TADDR base, TADDR addr) ++ { ++ LIMITED_METHOD_DAC_CONTRACT; ++ PRECONDITION(addr != NULL); ++ return addr; ++ } ++ + private: + TADDR m_ptr; + }; +diff --git a/src/inc/stdmacros.h b/src/inc/stdmacros.h +index 3ec8bec..6cef412 100644 +--- a/src/inc/stdmacros.h ++++ b/src/inc/stdmacros.h +@@ -87,12 +87,14 @@ + #ifdef _TARGET_ARM_ + #define ARM_FIRST_ARG(x) x , + #define ARM_ARG(x) , x ++#define ARM_ARG_OR_ZERO(x) , x + #define ARM_ONLY(x) x + #define NOT_ARM(x) + #define NOT_ARM_ARG(x) + #else + #define ARM_FIRST_ARG(x) + #define ARM_ARG(x) ++#define ARM_ARG_OR_ZERO(x) , 0 + #define ARM_ONLY(x) + #define NOT_ARM(x) x + #define NOT_ARM_ARG(x) , x +diff --git a/src/jit/codegencommon.cpp b/src/jit/codegencommon.cpp +index 9613e4d..51cc777 100644 +--- a/src/jit/codegencommon.cpp ++++ b/src/jit/codegencommon.cpp +@@ -9495,6 +9495,29 @@ void CodeGen::genFnEpilog(BasicBlock* block) + regTracker.rsTrackRegTrash(indCallReg); + } + break; ++ ++ case IAT_RELPVALUE: ++ { ++ // Load the address into a register, load relative indirect and call through a register ++ // We have to use R12 since we assume the argument registers are in use ++ callType = emitter::EC_INDIR_R; ++ indCallReg = REG_R12; ++ addr = NULL; ++ ++ regNumber vptrReg1 = REG_R11; ++ regMaskTP vptrReg1Mask = genRegMask(vptrReg1); ++ inst_IV(INS_push, (int)vptrReg1Mask); ++ ++ instGen_Set_Reg_To_Imm(EA_HANDLE_CNS_RELOC, indCallReg, (ssize_t)addrInfo.addr); ++ getEmitter()->emitIns_R_R(INS_mov, EA_PTRSIZE, vptrReg1, indCallReg); ++ getEmitter()->emitIns_R_R_I(INS_ldr, EA_PTRSIZE, indCallReg, indCallReg, 0); ++ getEmitter()->emitIns_R_R(INS_add, EA_PTRSIZE, indCallReg, vptrReg1); ++ ++ inst_IV(INS_pop, (int)vptrReg1Mask); ++ ++ regTracker.rsTrackRegTrash(indCallReg); ++ break; ++ } + + case IAT_PPVALUE: + default: +diff --git a/src/jit/codegenlegacy.cpp b/src/jit/codegenlegacy.cpp +index 53c8f8d..aec5bba 100644 +--- a/src/jit/codegenlegacy.cpp ++++ b/src/jit/codegenlegacy.cpp +@@ -18958,13 +18958,9 @@ regMaskTP CodeGen::genCodeForCall(GenTreeCall* call, bool valUsed) + { + 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 +- unreached(); +-#endif ++ getEmitter()->emitIns_R_R_R(INS_add, EA_PTRSIZE, vptrReg1, vptrReg1, vptrReg); ++ getEmitter()->emitIns_R_AR(ins_Load(TYP_I_IMPL), EA_PTRSIZE, vptrReg, vptrReg1, 0); ++ getEmitter()->emitIns_R_R_R(INS_add, EA_PTRSIZE, REG_TAILCALL_ADDR, vptrReg1, vptrReg); + } + else + { +@@ -18978,8 +18974,9 @@ regMaskTP CodeGen::genCodeForCall(GenTreeCall* call, bool valUsed) + #if CPU_LOAD_STORE_ARCH + if (isRelative) + { +- getEmitter()->emitIns_R_ARR(ins_Load(TYP_I_IMPL), EA_PTRSIZE, vptrReg, vptrReg1, vptrReg, +- 0); ++ getEmitter()->emitIns_R_R_R(INS_add, EA_PTRSIZE, vptrReg1, vptrReg1, vptrReg); ++ getEmitter()->emitIns_R_AR(ins_Load(TYP_I_IMPL), EA_PTRSIZE, vptrReg, vptrReg1, 0); ++ getEmitter()->emitIns_R_R_R(INS_add, EA_PTRSIZE, vptrReg, vptrReg1, vptrReg); + } + else + { +@@ -19387,6 +19384,34 @@ regMaskTP CodeGen::genCodeForCall(GenTreeCall* call, bool valUsed) + #endif + break; + ++ case IAT_RELPVALUE: ++ //------------------------------------------------------ ++ // Non-virtual direct calls to addresses accessed by ++ // a single relative indirection. ++ // ++ // For tailcalls we place the target address in REG_TAILCALL_ADDR ++ CLANG_FORMAT_COMMENT_ANCHOR; ++ ++#if CPU_LOAD_STORE_ARCH ++ { ++ regNumber indReg = REG_TAILCALL_ADDR; ++ regNumber vptrReg1 = regSet.rsGrabReg(RBM_ALLINT & ~genRegMask(indReg)); ++ regMaskTP vptrMask1 = genRegMask(vptrReg1); ++ ++ gcInfo.gcMarkRegSetNpt(vptrMask1); ++ regTracker.rsTrackRegTrash(vptrReg1); ++ ++ instGen_Set_Reg_To_Imm(EA_HANDLE_CNS_RELOC, indReg, (ssize_t)addr); ++ getEmitter()->emitIns_R_R(INS_mov, EA_PTRSIZE, vptrReg1, indReg); ++ getEmitter()->emitIns_R_R_I(INS_ldr, EA_4BYTE, indReg, indReg, 0); ++ getEmitter()->emitIns_R_R(INS_add, EA_4BYTE, indReg, vptrReg1); ++ regTracker.rsTrackRegTrash(indReg); ++ } ++#else ++ unreached(); ++#endif ++ break; ++ + default: + noway_assert(!"Bad accessType"); + break; +@@ -19530,6 +19555,46 @@ regMaskTP CodeGen::genCodeForCall(GenTreeCall* call, bool valUsed) + } + break; + ++ case IAT_RELPVALUE: ++ { ++ //------------------------------------------------------ ++ // Non-virtual direct calls to addresses accessed by ++ // a single relative indirection. ++ // ++ ++ // Load the address into a register, load indirect and call through a register ++ CLANG_FORMAT_COMMENT_ANCHOR; ++ ++ regMaskTP indCallMask = RBM_ALLINT; ++ ++ // Grab an available register to use for the CALL indirection ++ indCallReg = regSet.rsGrabReg(indCallMask); ++ ++ regNumber vptrReg1 = regSet.rsGrabReg(RBM_ALLINT & ~genRegMask(indCallReg)); ++ regMaskTP vptrMask1 = genRegMask(vptrReg1); ++ ++ gcInfo.gcMarkRegSetNpt(vptrMask1); ++ regTracker.rsTrackRegTrash(vptrReg1); ++ ++ instGen_Set_Reg_To_Imm(EA_HANDLE_CNS_RELOC, indCallReg, (ssize_t)addr); ++ getEmitter()->emitIns_R_R(INS_mov, EA_PTRSIZE, vptrReg1, indCallReg); ++ getEmitter()->emitIns_R_R_I(INS_ldr, EA_PTRSIZE, indCallReg, indCallReg, 0); ++ getEmitter()->emitIns_R_R(INS_add, EA_PTRSIZE, indCallReg, vptrReg1); ++ regTracker.rsTrackRegTrash(indCallReg); ++ ++ emitCallType = emitter::EC_INDIR_R; ++ addr = NULL; ++ ++ getEmitter()->emitIns_Call(emitCallType, methHnd, INDEBUG_LDISASM_COMMA(sigInfo) addr, args, ++ retSize, gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, ++ gcInfo.gcRegByrefSetCur, ilOffset, ++ indCallReg, // ireg ++ REG_NA, 0, 0, // xreg, xmul, disp ++ false, /* isJump */ ++ emitter::emitNoGChelper(helperNum)); ++ } ++ break; ++ + default: + noway_assert(!"Bad accessType"); + break; +diff --git a/src/jit/importer.cpp b/src/jit/importer.cpp +index b4ee1d0..e9dca90 100644 +--- a/src/jit/importer.cpp ++++ b/src/jit/importer.cpp +@@ -1768,7 +1768,7 @@ GenTreePtr Compiler::impLookupToTree(CORINFO_RESOLVED_TOKEN* pResolvedToken, + + CORINFO_GENERIC_HANDLE handle = nullptr; + void* pIndirection = nullptr; +- assert(pLookup->constLookup.accessType != IAT_PPVALUE); ++ assert(pLookup->constLookup.accessType != IAT_PPVALUE && pLookup->constLookup.accessType != IAT_RELPVALUE); + + if (pLookup->constLookup.accessType == IAT_VALUE) + { +@@ -1803,7 +1803,7 @@ GenTreePtr Compiler::impReadyToRunLookupToTree(CORINFO_CONST_LOOKUP* pLookup, + { + CORINFO_GENERIC_HANDLE handle = nullptr; + void* pIndirection = nullptr; +- assert(pLookup->accessType != IAT_PPVALUE); ++ assert(pLookup->accessType != IAT_PPVALUE && pLookup->accessType != IAT_RELPVALUE); + + if (pLookup->accessType == IAT_VALUE) + { +@@ -6828,7 +6828,8 @@ var_types Compiler::impImportCall(OPCODE opcode, + call = gtNewCallNode(CT_USER_FUNC, callInfo->hMethod, callRetTyp, nullptr, ilOffset); + call->gtCall.gtStubCallStubAddr = callInfo->stubLookup.constLookup.addr; + call->gtFlags |= GTF_CALL_VIRT_STUB; +- assert(callInfo->stubLookup.constLookup.accessType != IAT_PPVALUE); ++ assert(callInfo->stubLookup.constLookup.accessType != IAT_PPVALUE && ++ callInfo->stubLookup.constLookup.accessType != IAT_RELPVALUE); + if (callInfo->stubLookup.constLookup.accessType == IAT_PVALUE) + { + call->gtCall.gtCallMoreFlags |= GTF_CALL_M_VIRTSTUB_REL_INDIRECT; +diff --git a/src/jit/lower.cpp b/src/jit/lower.cpp +index c06dcb6..5c0cf69 100644 +--- a/src/jit/lower.cpp ++++ b/src/jit/lower.cpp +@@ -2567,6 +2567,16 @@ GenTree* Lowering::LowerDirectCall(GenTreeCall* call) + result = Ind(Ind(result)); + break; + ++ case IAT_RELPVALUE: ++ { ++ // Non-virtual direct calls to addresses accessed by ++ // a single relative indirection. ++ GenTree* cellAddr = AddrGen(addr); ++ GenTree* indir = Ind(cellAddr); ++ result = comp->gtNewOperNode(GT_ADD, TYP_I_IMPL, indir, AddrGen(addr)); ++ break; ++ } ++ + default: + noway_assert(!"Bad accessType"); + break; +@@ -3339,6 +3349,9 @@ GenTree* Lowering::LowerNonvirtPinvokeCall(GenTreeCall* call) + case IAT_PPVALUE: + result = Ind(Ind(AddrGen(addr))); + break; ++ ++ case IAT_RELPVALUE: ++ unreached(); + } + } + +@@ -3433,19 +3446,24 @@ GenTree* Lowering::LowerVirtualVtableCall(GenTreeCall* call) + // + // Save relative offset to tmp (vtab is virtual table pointer, vtabOffsOfIndirection is offset of + // vtable-1st-level-indirection): +- // tmp = [vtab + vtabOffsOfIndirection] ++ // tmp = vtab + // + // Save address of method to result (vtabOffsAfterIndirection is offset of vtable-2nd-level-indirection): +- // result = [vtab + vtabOffsOfIndirection + vtabOffsAfterIndirection + tmp] ++ // result = [tmp + vtabOffsOfIndirection + vtabOffsAfterIndirection + [tmp + vtabOffsOfIndirection]] ++ // ++ // ++ // If relative pointers are also in second level indirection, additional temporary is used: ++ // tmp1 = vtab ++ // tmp2 = tmp1 + vtabOffsOfIndirection + vtabOffsAfterIndirection + [tmp1 + vtabOffsOfIndirection] ++ // result = tmp2 + [tmp2] ++ // + 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("result of obtaining pointer to virtual table:\n"); +- DISPRANGE(range); +- BlockRange().InsertBefore(call, std::move(range)); ++ unsigned lclNumTmp2 = comp->lvaGrabTemp(true DEBUGARG("lclNumTmp2")); ++ comp->lvaTable[lclNumTmp2].incRefCnts(comp->compCurBB->getBBWeight(comp), comp); ++ ++ GenTree* lclvNodeStore = comp->gtNewTempAssign(lclNumTmp, result); + + GenTree* tmpTree = comp->gtNewLclvNode(lclNumTmp, result->TypeGet()); + tmpTree = Offset(tmpTree, vtabOffsOfIndirection); +@@ -3454,7 +3472,22 @@ GenTree* Lowering::LowerVirtualVtableCall(GenTreeCall* call) + 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)); ++ GenTree* base = OffsetByIndexWithScale(result, tmpTree, 1); ++ GenTree* lclvNodeStore2 = comp->gtNewTempAssign(lclNumTmp2, base); ++ ++ LIR::Range range = LIR::SeqTree(comp, lclvNodeStore); ++ JITDUMP("result of obtaining pointer to virtual table:\n"); ++ DISPRANGE(range); ++ BlockRange().InsertBefore(call, std::move(range)); ++ ++ LIR::Range range2 = LIR::SeqTree(comp, lclvNodeStore2); ++ JITDUMP("result of obtaining pointer to virtual table 2nd level indirection:\n"); ++ DISPRANGE(range2); ++ BlockRange().InsertAfter(lclvNodeStore, std::move(range2)); ++ ++ result = Ind(comp->gtNewLclvNode(lclNumTmp2, result->TypeGet())); ++ result = ++ comp->gtNewOperNode(GT_ADD, TYP_I_IMPL, result, comp->gtNewLclvNode(lclNumTmp2, result->TypeGet())); + } + else + { +diff --git a/src/jit/lower.h b/src/jit/lower.h +index 92d9cfe..af80fdd 100644 +--- a/src/jit/lower.h ++++ b/src/jit/lower.h +@@ -128,6 +128,12 @@ private: + + // returns true if the tree can use the read-modify-write memory instruction form + bool isRMWRegOper(GenTreePtr tree); ++ ++ GenTree* OffsetByIndexWithScale(GenTree* base, GenTree* index, unsigned scale) ++ { ++ var_types resultType = (base->TypeGet() == TYP_REF) ? TYP_BYREF : base->TypeGet(); ++ return new (comp, GT_LEA) GenTreeAddrMode(resultType, base, index, scale, 0); ++ } + + // return true if this call target is within range of a pc-rel call on the machine + bool IsCallTargetInRange(void* addr); +diff --git a/src/jit/morph.cpp b/src/jit/morph.cpp +index 993ce53..3f57191 100644 +--- a/src/jit/morph.cpp ++++ b/src/jit/morph.cpp +@@ -7124,7 +7124,7 @@ void Compiler::fgMorphTailCall(GenTreeCall* call) + + add = gtNewOperNode(GT_ADD, TYP_I_IMPL, vtbl, gtNewIconNode(vtabOffsOfIndirection, TYP_I_IMPL)); + +- GenTreePtr indOffTree; ++ GenTreePtr indOffTree = nullptr; + + if (isRelative) + { +@@ -7141,8 +7141,19 @@ void Compiler::fgMorphTailCall(GenTreeCall* call) + + /* Now the appropriate vtable slot */ + +- add = gtNewOperNode(GT_ADD, TYP_I_IMPL, vtbl, gtNewIconNode(vtabOffsAfterIndirection, TYP_I_IMPL)); +- vtbl = gtNewOperNode(GT_IND, TYP_I_IMPL, add); ++ add = gtNewOperNode(GT_ADD, TYP_I_IMPL, vtbl, gtNewIconNode(vtabOffsAfterIndirection, TYP_I_IMPL)); ++ ++ if (isRelative) ++ { ++ indOffTree = impCloneExpr(add, &add, NO_CLASS_HANDLE, (unsigned)CHECK_SPILL_ALL, ++ nullptr DEBUGARG("virtual table call 2")); ++ vtbl = gtNewOperNode(GT_IND, TYP_I_IMPL, add); ++ vtbl = gtNewOperNode(GT_ADD, TYP_I_IMPL, vtbl, indOffTree); ++ } ++ else ++ { ++ vtbl = gtNewOperNode(GT_IND, TYP_I_IMPL, add); ++ } + + // Switch this to a plain indirect call + call->gtFlags &= ~GTF_CALL_VIRT_KIND_MASK; +diff --git a/src/vm/arm/stubs.cpp b/src/vm/arm/stubs.cpp +index a52f0c8..ec0a1e7 100644 +--- a/src/vm/arm/stubs.cpp ++++ b/src/vm/arm/stubs.cpp +@@ -1732,8 +1732,34 @@ void StubLinkerCPU::ThumbEmitCallManagedMethod(MethodDesc *pMD, bool fTailcall) + // mov r12, #slotaddress + ThumbEmitMovConstant(ThumbReg(12), (TADDR)pMD->GetAddrOfSlot()); + ++ bool isRelative = MethodTable::VTableIndir2_t::isRelative ++ && pMD->IsVtableSlot(); ++ if (isRelative) ++ { ++#if defined(FEATURE_NGEN_RELOCS_OPTIMIZATIONS) ++ // push r11 ++ ThumbEmitPush(ThumbReg(11).Mask()); ++ // mov r11, r12 ++ ThumbEmitMovRegReg(ThumbReg(11), ThumbReg(12)); ++#else ++ _ASSERTE(false); ++#endif ++ } ++ + // ldr r12, [r12] + ThumbEmitLoadRegIndirect(ThumbReg(12), ThumbReg(12), 0); ++ ++ if (isRelative) ++ { ++#if defined(FEATURE_NGEN_RELOCS_OPTIMIZATIONS) ++ // add r12, r11 ++ ThumbEmitAddReg(ThumbReg(12), ThumbReg(11)); ++ // pop r11 ++ ThumbEmitPop(ThumbReg(11).Mask()); ++#else ++ _ASSERTE(false); ++#endif ++ } + } + + if (fTailcall) +diff --git a/src/vm/array.cpp b/src/vm/array.cpp +index 3a33aff..ac0e2c6 100644 +--- a/src/vm/array.cpp ++++ b/src/vm/array.cpp +@@ -374,7 +374,7 @@ MethodTable* Module::CreateArrayMethodTable(TypeHandle elemTypeHnd, CorElementTy + // If none, we need to allocate space for the slots + if (!canShareVtableChunks) + { +- cbMT += numVirtuals * sizeof(PCODE); ++ cbMT += numVirtuals * sizeof(MethodTable::VTableIndir2_t); + } + + // Canonical methodtable has an array of non virtual slots pointed to by the optional member +@@ -544,7 +544,7 @@ MethodTable* Module::CreateArrayMethodTable(TypeHandle elemTypeHnd, CorElementTy + else + { + // Use the locally allocated chunk +- it.SetIndirectionSlot((PTR_PCODE)(pMemory+cbArrayClass+offsetOfUnsharedVtableChunks)); ++ it.SetIndirectionSlot((MethodTable::VTableIndir2_t *)(pMemory+cbArrayClass+offsetOfUnsharedVtableChunks)); + offsetOfUnsharedVtableChunks += it.GetSize(); + } + } +diff --git a/src/vm/generics.cpp b/src/vm/generics.cpp +index 61a1de5..d45578b 100644 +--- a/src/vm/generics.cpp ++++ b/src/vm/generics.cpp +@@ -324,7 +324,7 @@ ClassLoader::CreateTypeHandleForNonCanonicalGenericInstantiation( + // If none, we need to allocate space for the slots + if (!canShareVtableChunks) + { +- allocSize += S_SIZE_T( cSlots ) * S_SIZE_T( sizeof(PCODE) ); ++ allocSize += S_SIZE_T( cSlots ) * S_SIZE_T( sizeof(MethodTable::VTableIndir2_t) ); + } + + if (allocSize.IsOverflow()) +@@ -446,7 +446,7 @@ ClassLoader::CreateTypeHandleForNonCanonicalGenericInstantiation( + else + { + // Use the locally allocated chunk +- it.SetIndirectionSlot((PTR_PCODE)(pMemory+offsetOfUnsharedVtableChunks)); ++ it.SetIndirectionSlot((MethodTable::VTableIndir2_t *)(pMemory+offsetOfUnsharedVtableChunks)); + offsetOfUnsharedVtableChunks += it.GetSize(); + } + } +diff --git a/src/vm/jitinterface.cpp b/src/vm/jitinterface.cpp +index 9cefd10..9e5d625 100644 +--- a/src/vm/jitinterface.cpp ++++ b/src/vm/jitinterface.cpp +@@ -8747,8 +8747,9 @@ void CEEInfo::getMethodVTableOffset (CORINFO_METHOD_HANDLE methodHnd, + _ASSERTE(method->GetSlot() < method->GetMethodTable()->GetNumVirtuals()); + + *pOffsetOfIndirection = MethodTable::GetVtableOffset() + MethodTable::GetIndexOfVtableIndirection(method->GetSlot()) * sizeof(MethodTable::VTableIndir_t); +- *pOffsetAfterIndirection = MethodTable::GetIndexAfterVtableIndirection(method->GetSlot()) * sizeof(PCODE); ++ *pOffsetAfterIndirection = MethodTable::GetIndexAfterVtableIndirection(method->GetSlot()) * sizeof(MethodTable::VTableIndir2_t); + *isRelative = MethodTable::VTableIndir_t::isRelative ? 1 : 0; ++ _ASSERTE(MethodTable::VTableIndir_t::isRelative == MethodTable::VTableIndir2_t::isRelative); + + EE_TO_JIT_TRANSITION_LEAF(); + } +@@ -8945,8 +8946,17 @@ void CEEInfo::getFunctionEntryPoint(CORINFO_METHOD_HANDLE ftnHnd, + + _ASSERTE((accessFlags & CORINFO_ACCESS_THIS) || !ftn->IsRemotingInterceptedViaVirtualDispatch()); + +- ret = ftn->GetAddrOfSlot(); +- accessType = IAT_PVALUE; ++ ret = (void *)ftn->GetAddrOfSlot(); ++ ++ if (MethodTable::VTableIndir2_t::isRelative ++ && ftn->IsVtableSlot()) ++ { ++ accessType = IAT_RELPVALUE; ++ } ++ else ++ { ++ accessType = IAT_PVALUE; ++ } + } + + +diff --git a/src/vm/method.cpp b/src/vm/method.cpp +index 6bd49fb..36d51fd 100644 +--- a/src/vm/method.cpp ++++ b/src/vm/method.cpp +@@ -568,7 +568,7 @@ PCODE MethodDesc::GetMethodEntryPoint() + return GetMethodTable_NoLogging()->GetSlot(GetSlot()); + } + +-PTR_PCODE MethodDesc::GetAddrOfSlot() ++TADDR MethodDesc::GetAddrOfSlot() + { + CONTRACTL + { +@@ -589,7 +589,7 @@ PTR_PCODE MethodDesc::GetAddrOfSlot() + + SIZE_T size = GetBaseSize(); + +- return PTR_PCODE(dac_cast<TADDR>(this) + size); ++ return dac_cast<TADDR>(this) + size; + } + + _ASSERTE(GetMethodTable()->IsCanonicalMethodTable()); +@@ -2489,7 +2489,15 @@ void MethodDesc::Reset() + + InterlockedUpdateFlags2(enum_flag2_HasStableEntryPoint | enum_flag2_HasPrecode, FALSE); + +- *GetAddrOfSlot() = GetTemporaryEntryPoint(); ++ TADDR slot = GetAddrOfSlot(); ++ if (IsVtableSlot()) ++ { ++ ((MethodTable::VTableIndir2_t *) slot)->SetValue(GetTemporaryEntryPoint()); ++ } ++ else ++ { ++ *((PCODE *) slot) = GetTemporaryEntryPoint(); ++ } + } + + if (HasNativeCodeSlot()) +@@ -5008,9 +5016,19 @@ void MethodDesc::SetTemporaryEntryPoint(LoaderAllocator *pLoaderAllocator, Alloc + + GetMethodDescChunk()->EnsureTemporaryEntryPointsCreated(pLoaderAllocator, pamTracker); + +- PTR_PCODE pSlot = GetAddrOfSlot(); +- _ASSERTE(*pSlot == NULL); +- *pSlot = GetTemporaryEntryPoint(); ++ TADDR slot = GetAddrOfSlot(); ++ if (IsVtableSlot()) ++ { ++ MethodTable::VTableIndir2_t *slotPtr = ((MethodTable::VTableIndir2_t *) slot); ++ _ASSERTE(slotPtr->IsNull()); ++ slotPtr->SetValue(GetTemporaryEntryPoint()); ++ } ++ else ++ { ++ PCODE *slotPtr = (PCODE *) slot; ++ _ASSERTE(*slotPtr == NULL); ++ *slotPtr = GetTemporaryEntryPoint(); ++ } + + if (RequiresStableEntryPoint()) + { +@@ -5073,7 +5091,7 @@ Precode* MethodDesc::GetOrCreatePrecode() + return GetPrecode(); + } + +- PTR_PCODE pSlot = GetAddrOfSlot(); ++ TADDR pSlot = GetAddrOfSlot(); + PCODE tempEntry = GetTemporaryEntryPoint(); + + PrecodeType requiredType = GetPrecodeType(); +@@ -5093,14 +5111,40 @@ Precode* MethodDesc::GetOrCreatePrecode() + + AllocMemTracker amt; + Precode* pPrecode = Precode::Allocate(requiredType, this, GetLoaderAllocator(), &amt); +- if (FastInterlockCompareExchangePointer(EnsureWritablePages(pSlot), pPrecode->GetEntryPoint(), tempEntry) == tempEntry) ++ PCODE newVal; ++ PCODE oldVal; ++ TADDR *slotAddr; ++ ++ if (IsVtableSlot()) ++ { ++ newVal = MethodTable::VTableIndir2_t::GetRelative(pSlot, pPrecode->GetEntryPoint()); ++ oldVal = MethodTable::VTableIndir2_t::GetRelative(pSlot, tempEntry); ++ slotAddr = (TADDR *) EnsureWritablePages((MethodTable::VTableIndir2_t *) pSlot); ++ } ++ else ++ { ++ newVal = pPrecode->GetEntryPoint(); ++ oldVal = tempEntry; ++ slotAddr = (TADDR *) EnsureWritablePages((PCODE *) pSlot); ++ } ++ ++ if (FastInterlockCompareExchangePointer(slotAddr, (TADDR) newVal, (TADDR) oldVal) == oldVal) + amt.SuppressRelease(); + } + + // Set the flags atomically + InterlockedUpdateFlags2(enum_flag2_HasStableEntryPoint | enum_flag2_HasPrecode, TRUE); + +- return Precode::GetPrecodeFromEntryPoint(*pSlot); ++ PCODE addr; ++ if (IsVtableSlot()) ++ { ++ addr = ((MethodTable::VTableIndir2_t *)pSlot)->GetValue(); ++ } ++ else ++ { ++ addr = *((PCODE *)pSlot); ++ } ++ return Precode::GetPrecodeFromEntryPoint(addr); + } + + //******************************************************************************* +@@ -5182,10 +5226,28 @@ BOOL MethodDesc::SetStableEntryPointInterlocked(PCODE addr) + _ASSERTE(!HasPrecode()); + + PCODE pExpected = GetTemporaryEntryPoint(); +- PTR_PCODE pSlot = GetAddrOfSlot(); +- EnsureWritablePages(pSlot); ++ TADDR pSlot = GetAddrOfSlot(); + +- BOOL fResult = FastInterlockCompareExchangePointer(pSlot, addr, pExpected) == pExpected; ++ BOOL fResult; ++ ++ TADDR *slotAddr; ++ PCODE newVal; ++ PCODE oldVal; ++ ++ if (IsVtableSlot()) ++ { ++ newVal = MethodTable::VTableIndir2_t::GetRelative(pSlot, addr); ++ oldVal = MethodTable::VTableIndir2_t::GetRelative(pSlot, pExpected); ++ slotAddr = (TADDR *) EnsureWritablePages((MethodTable::VTableIndir2_t *) pSlot); ++ } ++ else ++ { ++ newVal = addr; ++ oldVal = pExpected; ++ slotAddr = (TADDR *) EnsureWritablePages((PCODE *) pSlot); ++ } ++ ++ fResult = FastInterlockCompareExchangePointer(slotAddr, (TADDR) newVal, (TADDR) oldVal) == oldVal; + + InterlockedUpdateFlags2(enum_flag2_HasStableEntryPoint, TRUE); + +diff --git a/src/vm/method.hpp b/src/vm/method.hpp +index 9023a1b..92fbdee 100644 +--- a/src/vm/method.hpp ++++ b/src/vm/method.hpp +@@ -1203,7 +1203,16 @@ public: + } + } + +- PTR_PCODE GetAddrOfSlot(); ++ inline BOOL IsVirtualSlot() ++ { ++ return GetSlot() < GetMethodTable()->GetNumVirtuals(); ++ } ++ inline BOOL IsVtableSlot() ++ { ++ return IsVirtualSlot() && !HasNonVtableSlot(); ++ } ++ ++ TADDR GetAddrOfSlot(); + + PTR_MethodDesc GetDeclMethodDesc(UINT32 slotNumber); + +diff --git a/src/vm/methodtable.cpp b/src/vm/methodtable.cpp +index b097406..eeffc96 100644 +--- a/src/vm/methodtable.cpp ++++ b/src/vm/methodtable.cpp +@@ -4267,7 +4267,8 @@ void MethodTable::Save(DataImage *image, DWORD profilingFlags) + { + if (!image->IsStored(it.GetIndirectionSlot())) + { +- if (CanInternVtableChunk(image, it)) ++ if (!MethodTable::VTableIndir2_t::isRelative ++ && CanInternVtableChunk(image, it)) + image->StoreInternedStructure(it.GetIndirectionSlot(), it.GetSize(), DataImage::ITEM_VTABLE_CHUNK); + else + image->StoreStructure(it.GetIndirectionSlot(), it.GetSize(), DataImage::ITEM_VTABLE_CHUNK); +@@ -4955,7 +4956,7 @@ void MethodTable::Fixup(DataImage *image) + // Virtual slots live in chunks pointed to by vtable indirections + + slotBase = (PVOID) GetVtableIndirections()[GetIndexOfVtableIndirection(slotNumber)].GetValueMaybeNull(); +- slotOffset = GetIndexAfterVtableIndirection(slotNumber) * sizeof(PCODE); ++ slotOffset = GetIndexAfterVtableIndirection(slotNumber) * sizeof(MethodTable::VTableIndir2_t); + } + else if (HasSingleNonVirtualSlot()) + { +@@ -4982,7 +4983,7 @@ void MethodTable::Fixup(DataImage *image) + if (pMD->GetMethodTable() == this) + { + ZapRelocationType relocType; +- if (slotNumber >= GetNumVirtuals()) ++ if (slotNumber >= GetNumVirtuals() || MethodTable::VTableIndir2_t::isRelative) + relocType = IMAGE_REL_BASED_RelativePointer; + else + relocType = IMAGE_REL_BASED_PTR; +@@ -5005,9 +5006,15 @@ void MethodTable::Fixup(DataImage *image) + _ASSERTE(pSourceMT->GetMethodDescForSlot(slotNumber) == pMD); + #endif + ++ ZapRelocationType relocType; ++ if (MethodTable::VTableIndir2_t::isRelative) ++ relocType = IMAGE_REL_BASED_RELPTR; ++ else ++ relocType = IMAGE_REL_BASED_PTR; ++ + if (image->CanEagerBindToMethodDesc(pMD) && pMD->GetLoaderModule() == pZapModule) + { +- pMD->FixupSlot(image, slotBase, slotOffset); ++ pMD->FixupSlot(image, slotBase, slotOffset, relocType); + } + else + { +@@ -5016,7 +5023,7 @@ void MethodTable::Fixup(DataImage *image) + ZapNode * importThunk = image->GetVirtualImportThunk(pMD->GetMethodTable(), pMD, slotNumber); + // On ARM, make sure that the address to the virtual thunk that we write into the + // vtable "chunk" has the Thumb bit set. +- image->FixupFieldToNode(slotBase, slotOffset, importThunk ARM_ARG(THUMB_CODE)); ++ image->FixupFieldToNode(slotBase, slotOffset, importThunk ARM_ARG_OR_ZERO(THUMB_CODE), relocType); + } + else + { +@@ -9463,7 +9470,15 @@ void MethodTable::SetSlot(UINT32 slotNumber, PCODE slotCode) + _ASSERTE(IsThumbCode(slotCode)); + #endif + +- *GetSlotPtrRaw(slotNumber) = slotCode; ++ TADDR slot = GetSlotPtrRaw(slotNumber); ++ if (slotNumber < GetNumVirtuals()) ++ { ++ ((MethodTable::VTableIndir2_t *) slot)->SetValueMaybeNull(slotCode); ++ } ++ else ++ { ++ *((PCODE *)slot) = slotCode; ++ } + } + + //========================================================================================== +diff --git a/src/vm/methodtable.h b/src/vm/methodtable.h +index 9dc24d8..5cc6f45 100644 +--- a/src/vm/methodtable.h ++++ b/src/vm/methodtable.h +@@ -1497,13 +1497,18 @@ public: + WRAPPER_NO_CONTRACT; + STATIC_CONTRACT_SO_TOLERANT; + CONSISTENCY_CHECK(slotNumber < GetNumVtableSlots()); +- PTR_PCODE pSlot = GetSlotPtrRaw(slotNumber); +- if (IsZapped() && slotNumber >= GetNumVirtuals()) ++ ++ TADDR pSlot = GetSlotPtrRaw(slotNumber); ++ if (slotNumber < GetNumVirtuals()) ++ { ++ return VTableIndir2_t::GetValueMaybeNullAtPtr(pSlot); ++ } ++ else if (IsZapped() && slotNumber >= GetNumVirtuals()) + { + // Non-virtual slots in NGened images are relative pointers +- return RelativePointer<PCODE>::GetValueAtPtr(dac_cast<TADDR>(pSlot)); ++ return RelativePointer<PCODE>::GetValueAtPtr(pSlot); + } +- return *pSlot; ++ return *dac_cast<PTR_PCODE>(pSlot); + } + + // Special-case for when we know that the slot number corresponds +@@ -1517,10 +1522,11 @@ public: + + DWORD index = GetIndexOfVtableIndirection(slotNum); + TADDR base = dac_cast<TADDR>(&(GetVtableIndirections()[index])); +- return *(VTableIndir_t::GetValueMaybeNullAtPtr(base) + GetIndexAfterVtableIndirection(slotNum)); ++ DPTR(VTableIndir2_t) baseAfterInd = VTableIndir_t::GetValueMaybeNullAtPtr(base) + GetIndexAfterVtableIndirection(slotNum); ++ return VTableIndir2_t::GetValueMaybeNullAtPtr(dac_cast<TADDR>(baseAfterInd)); + } + +- PTR_PCODE GetSlotPtrRaw(UINT32 slotNum) ++ TADDR GetSlotPtrRaw(UINT32 slotNum) + { + WRAPPER_NO_CONTRACT; + STATIC_CONTRACT_SO_TOLERANT; +@@ -1531,25 +1537,26 @@ public: + // Virtual slots live in chunks pointed to by vtable indirections + DWORD index = GetIndexOfVtableIndirection(slotNum); + TADDR base = dac_cast<TADDR>(&(GetVtableIndirections()[index])); +- return VTableIndir_t::GetValueMaybeNullAtPtr(base) + GetIndexAfterVtableIndirection(slotNum); ++ DPTR(VTableIndir2_t) baseAfterInd = VTableIndir_t::GetValueMaybeNullAtPtr(base) + GetIndexAfterVtableIndirection(slotNum); ++ return dac_cast<TADDR>(baseAfterInd); + } + else if (HasSingleNonVirtualSlot()) + { + // Non-virtual slots < GetNumVtableSlots live in a single chunk pointed to by an optional member, + // except when there is only one in which case it lives in the optional member itself + _ASSERTE(slotNum == GetNumVirtuals()); +- return dac_cast<PTR_PCODE>(GetNonVirtualSlotsPtr()); ++ return GetNonVirtualSlotsPtr(); + } + else + { + // Non-virtual slots < GetNumVtableSlots live in a single chunk pointed to by an optional member + _ASSERTE(HasNonVirtualSlotsArray()); + g_IBCLogger.LogMethodTableNonVirtualSlotsAccess(this); +- return GetNonVirtualSlotsArray() + (slotNum - GetNumVirtuals()); ++ return dac_cast<TADDR>(GetNonVirtualSlotsArray() + (slotNum - GetNumVirtuals())); + } + } + +- PTR_PCODE GetSlotPtr(UINT32 slotNum) ++ TADDR GetSlotPtr(UINT32 slotNum) + { + WRAPPER_NO_CONTRACT; + STATIC_CONTRACT_SO_TOLERANT; +@@ -1615,10 +1622,12 @@ 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; ++#if defined(PLATFORM_UNIX) && defined(_TARGET_ARM_) && defined(FEATURE_NGEN_RELOCS_OPTIMIZATIONS) ++ typedef RelativePointer<PCODE> VTableIndir2_t; ++ typedef RelativePointer<DPTR(VTableIndir2_t)> VTableIndir_t; + #else +- typedef PlainPointer<PTR_PCODE> VTableIndir_t; ++ typedef PlainPointer<PCODE> VTableIndir2_t; ++ typedef PlainPointer<DPTR(VTableIndir2_t)> VTableIndir_t; + #endif + + static DWORD GetIndexOfVtableIndirection(DWORD slotNum); +@@ -1647,10 +1656,10 @@ public: + BOOL Finished(); + DWORD GetIndex(); + DWORD GetOffsetFromMethodTable(); +- PTR_PCODE GetIndirectionSlot(); ++ DPTR(VTableIndir2_t) GetIndirectionSlot(); + + #ifndef DACCESS_COMPILE +- void SetIndirectionSlot(PTR_PCODE pChunk); ++ void SetIndirectionSlot(DPTR(VTableIndir2_t) pChunk); + #endif + + DWORD GetStartSlot(); +diff --git a/src/vm/methodtable.inl b/src/vm/methodtable.inl +index 0d0acda..4fe3d83 100644 +--- a/src/vm/methodtable.inl ++++ b/src/vm/methodtable.inl +@@ -956,7 +956,7 @@ inline DWORD MethodTable::VtableIndirectionSlotIterator::GetOffsetFromMethodTabl + } + + //========================================================================================== +-inline PTR_PCODE MethodTable::VtableIndirectionSlotIterator::GetIndirectionSlot() ++inline DPTR(MethodTable::VTableIndir2_t) MethodTable::VtableIndirectionSlotIterator::GetIndirectionSlot() + { + LIMITED_METHOD_DAC_CONTRACT; + PRECONDITION(m_i != (DWORD) -1 && m_i < m_count); +@@ -966,7 +966,7 @@ inline PTR_PCODE MethodTable::VtableIndirectionSlotIterator::GetIndirectionSlot( + + //========================================================================================== + #ifndef DACCESS_COMPILE +-inline void MethodTable::VtableIndirectionSlotIterator::SetIndirectionSlot(PTR_PCODE pChunk) ++inline void MethodTable::VtableIndirectionSlotIterator::SetIndirectionSlot(DPTR(MethodTable::VTableIndir2_t) pChunk) + { + LIMITED_METHOD_CONTRACT; + m_pSlot->SetValueMaybeNull(pChunk); +diff --git a/src/vm/methodtablebuilder.cpp b/src/vm/methodtablebuilder.cpp +index 970166d..67e12bc 100644 +--- a/src/vm/methodtablebuilder.cpp ++++ b/src/vm/methodtablebuilder.cpp +@@ -10160,7 +10160,7 @@ MethodTable * MethodTableBuilder::AllocateNewMT(Module *pLoaderModule, + else + { + // Use the locally allocated chunk +- it.SetIndirectionSlot((PTR_PCODE)(pData+dwCurrentUnsharedSlotOffset)); ++ it.SetIndirectionSlot((MethodTable::VTableIndir2_t *)(pData+dwCurrentUnsharedSlotOffset)); + dwCurrentUnsharedSlotOffset += it.GetSize(); + } + } +@@ -10726,7 +10726,7 @@ MethodTableBuilder::SetupMethodTable2( + + if (pMD->HasNonVtableSlot()) + { +- *pMD->GetAddrOfSlot() = addr; ++ *((PCODE *)pMD->GetAddrOfSlot()) = addr; + } + else + { +-- +2.7.4 + diff --git a/packaging/0038-Replace-PLATFORM_UNIX-_TARGET_ARM_-for-NGEN-relocati.patch b/packaging/0038-Replace-PLATFORM_UNIX-_TARGET_ARM_-for-NGEN-relocati.patch new file mode 100644 index 0000000000..08f45e3734 --- /dev/null +++ b/packaging/0038-Replace-PLATFORM_UNIX-_TARGET_ARM_-for-NGEN-relocati.patch @@ -0,0 +1,112 @@ +From dc692676bb385477d702f173c04e9ff5af015126 Mon Sep 17 00:00:00 2001 +From: Gleb Balykov <g.balykov@samsung.com> +Date: Fri, 20 Apr 2018 11:54:18 +0300 +Subject: [PATCH 38/47] Replace PLATFORM_UNIX && _TARGET_ARM_ for NGEN + relocations optimizations with FEATURE_NGEN_RELOCS_OPTIMIZATIONS + +--- + src/vm/method.hpp | 4 ++-- + src/vm/methodtable.h | 16 ++++++++-------- + 2 files changed, 10 insertions(+), 10 deletions(-) + +diff --git a/src/vm/method.hpp b/src/vm/method.hpp +index 92fbdee..d60984c 100644 +--- a/src/vm/method.hpp ++++ b/src/vm/method.hpp +@@ -2599,7 +2599,7 @@ public: + }; + + // The writeable part of the methoddesc. +-#if defined(PLATFORM_UNIX) && defined(_TARGET_ARM_) ++#if defined(FEATURE_NGEN_RELOCS_OPTIMIZATIONS) + RelativePointer<PTR_NDirectWriteableData> m_pWriteableData; + #else + PlainPointer<PTR_NDirectWriteableData> m_pWriteableData; +@@ -3415,7 +3415,7 @@ public: // <TODO>make private: JITinterface.cpp accesses through this </TODO> + // + // For generic method definitions that are not the typical method definition (e.g. C<int>.m<U>) + // this field is null; to obtain the instantiation use LoadMethodInstantiation +-#if defined(PLATFORM_UNIX) && defined(_TARGET_ARM_) ++#if defined(FEATURE_NGEN_RELOCS_OPTIMIZATIONS) + RelativePointer<PTR_Dictionary> m_pPerInstInfo; //SHARED + #else + PlainPointer<PTR_Dictionary> m_pPerInstInfo; //SHARED +diff --git a/src/vm/methodtable.h b/src/vm/methodtable.h +index 5cc6f45..32b4ca4 100644 +--- a/src/vm/methodtable.h ++++ b/src/vm/methodtable.h +@@ -112,7 +112,7 @@ struct InterfaceInfo_t + #endif + + // Method table of the interface +-#if defined(PLATFORM_UNIX) && defined(_TARGET_ARM_) ++#if defined(FEATURE_NGEN_RELOCS_OPTIMIZATIONS) + RelativeFixupPointer<PTR_MethodTable> m_pMethodTable; + #else + FixupPointer<PTR_MethodTable> m_pMethodTable; +@@ -1622,7 +1622,7 @@ public: + #define VTABLE_SLOTS_PER_CHUNK 8 + #define VTABLE_SLOTS_PER_CHUNK_LOG2 3 + +-#if defined(PLATFORM_UNIX) && defined(_TARGET_ARM_) && defined(FEATURE_NGEN_RELOCS_OPTIMIZATIONS) ++#if defined(FEATURE_NGEN_RELOCS_OPTIMIZATIONS) + typedef RelativePointer<PCODE> VTableIndir2_t; + typedef RelativePointer<DPTR(VTableIndir2_t)> VTableIndir_t; + #else +@@ -2136,7 +2136,7 @@ public: + // THE METHOD TABLE PARENT (SUPERCLASS/BASE CLASS) + // + +-#if defined(PLATFORM_UNIX) && defined(_TARGET_ARM_) ++#if defined(FEATURE_NGEN_RELOCS_OPTIMIZATIONS) + #define PARENT_MT_FIXUP_OFFSET (-FIXUP_POINTER_INDIRECTION) + typedef RelativeFixupPointer<PTR_MethodTable> ParentMT_t; + #else +@@ -2168,7 +2168,7 @@ public: + inline static PTR_VOID GetParentMethodTableOrIndirection(PTR_VOID pMT) + { + WRAPPER_NO_CONTRACT; +-#if defined(PLATFORM_UNIX) && defined(_TARGET_ARM_) ++#if defined(FEATURE_NGEN_RELOCS_OPTIMIZATIONS) + PTR_MethodTable pMethodTable = dac_cast<PTR_MethodTable>(pMT); + PTR_MethodTable pParentMT = ReadPointerMaybeNull((MethodTable*) pMethodTable, &MethodTable::m_pParentMethodTable); + return dac_cast<PTR_VOID>(pParentMT); +@@ -3066,7 +3066,7 @@ public: + // must have a dictionary entry. On the other hand, for instantiations shared with Dict<string,double> the opposite holds. + // + +-#if defined(PLATFORM_UNIX) && defined(_TARGET_ARM_) ++#if defined(FEATURE_NGEN_RELOCS_OPTIMIZATIONS) + typedef RelativePointer<PTR_Dictionary> PerInstInfoElem_t; + typedef RelativePointer<DPTR(PerInstInfoElem_t)> PerInstInfo_t; + #else +@@ -4151,7 +4151,7 @@ private: + + RelativePointer<PTR_Module> m_pLoaderModule; // LoaderModule. It is equal to the ZapModule in ngened images + +-#if defined(PLATFORM_UNIX) && defined(_TARGET_ARM_) ++#if defined(FEATURE_NGEN_RELOCS_OPTIMIZATIONS) + RelativePointer<PTR_MethodTableWriteableData> m_pWriteableData; + #else + PlainPointer<PTR_MethodTableWriteableData> m_pWriteableData; +@@ -4167,7 +4167,7 @@ private: + static const TADDR UNION_MASK = 3; + + union { +-#if defined(PLATFORM_UNIX) && defined(_TARGET_ARM_) ++#if defined(FEATURE_NGEN_RELOCS_OPTIMIZATIONS) + RelativePointer<DPTR(EEClass)> m_pEEClass; + RelativePointer<TADDR> m_pCanonMT; + #else +@@ -4202,7 +4202,7 @@ private: + public: + union + { +-#if defined(PLATFORM_UNIX) && defined(_TARGET_ARM_) ++#if defined(FEATURE_NGEN_RELOCS_OPTIMIZATIONS) + RelativePointer<PTR_InterfaceInfo> m_pInterfaceMap; + #else + PlainPointer<PTR_InterfaceInfo> m_pInterfaceMap; +-- +2.7.4 + diff --git a/packaging/0039-Fix-formatting.patch b/packaging/0039-Fix-formatting.patch new file mode 100644 index 0000000000..57fceb0646 --- /dev/null +++ b/packaging/0039-Fix-formatting.patch @@ -0,0 +1,41 @@ +From 2d85d36b77315341add87724be5a04869cba5693 Mon Sep 17 00:00:00 2001 +From: Gleb Balykov <g.balykov@samsung.com> +Date: Fri, 20 Apr 2018 17:39:02 +0300 +Subject: [PATCH 39/47] Fix formatting + +--- + src/jit/lower.cpp | 4 ++-- + src/jit/morph.cpp | 2 +- + 2 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/src/jit/lower.cpp b/src/jit/lower.cpp +index 5c0cf69..4897107 100644 +--- a/src/jit/lower.cpp ++++ b/src/jit/lower.cpp +@@ -3472,8 +3472,8 @@ GenTree* Lowering::LowerVirtualVtableCall(GenTreeCall* call) + GenTree* offs = comp->gtNewIconNode(vtabOffsOfIndirection + vtabOffsAfterIndirection, TYP_INT); + result = comp->gtNewOperNode(GT_ADD, TYP_I_IMPL, comp->gtNewLclvNode(lclNumTmp, result->TypeGet()), offs); + +- GenTree* base = OffsetByIndexWithScale(result, tmpTree, 1); +- GenTree* lclvNodeStore2 = comp->gtNewTempAssign(lclNumTmp2, base); ++ GenTree* base = OffsetByIndexWithScale(result, tmpTree, 1); ++ GenTree* lclvNodeStore2 = comp->gtNewTempAssign(lclNumTmp2, base); + + LIR::Range range = LIR::SeqTree(comp, lclvNodeStore); + JITDUMP("result of obtaining pointer to virtual table:\n"); +diff --git a/src/jit/morph.cpp b/src/jit/morph.cpp +index 3f57191..3207fd4 100644 +--- a/src/jit/morph.cpp ++++ b/src/jit/morph.cpp +@@ -7145,7 +7145,7 @@ void Compiler::fgMorphTailCall(GenTreeCall* call) + + if (isRelative) + { +- indOffTree = impCloneExpr(add, &add, NO_CLASS_HANDLE, (unsigned)CHECK_SPILL_ALL, ++ GenTree* indOffTree = impCloneExpr(add, &add, NO_CLASS_HANDLE, (unsigned)CHECK_SPILL_ALL, + nullptr DEBUGARG("virtual table call 2")); + vtbl = gtNewOperNode(GT_IND, TYP_I_IMPL, add); + vtbl = gtNewOperNode(GT_ADD, TYP_I_IMPL, vtbl, indOffTree); +-- +2.7.4 + diff --git a/packaging/0040-Update-GUID.patch b/packaging/0040-Update-GUID.patch new file mode 100644 index 0000000000..d3a97bffc7 --- /dev/null +++ b/packaging/0040-Update-GUID.patch @@ -0,0 +1,33 @@ +From 843b1817c7ebea7c999f7327c6d4425d31f62615 Mon Sep 17 00:00:00 2001 +From: Gleb Balykov <g.balykov@samsung.com> +Date: Mon, 23 Apr 2018 20:57:13 +0300 +Subject: [PATCH 40/47] Update GUID + +--- + src/inc/corinfo.h | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +diff --git a/src/inc/corinfo.h b/src/inc/corinfo.h +index 1a6843c..76a7a7c 100644 +--- a/src/inc/corinfo.h ++++ b/src/inc/corinfo.h +@@ -213,11 +213,11 @@ TODO: Talk about initializing strutures before use + #define SELECTANY extern __declspec(selectany) + #endif + +-SELECTANY const GUID JITEEVersionIdentifier = { /* 5a1cfc89-a84a-4642-b01d-ead88e60c1ee */ +- 0x5a1cfc89, +- 0xa84a, +- 0x4642, +- { 0xb0, 0x1d, 0xea, 0xd8, 0x8e, 0x60, 0xc1, 0xee } ++SELECTANY const GUID JITEEVersionIdentifier = { /* 02d3cb99-cc44-463e-b47d-c94a90daf271 */ ++ 0x02d3cb99, ++ 0xcc44, ++ 0x463e, ++ {0xb4, 0x7d, 0xc9, 0x4a, 0x90, 0xda, 0xf2, 0x71} + }; + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// +-- +2.7.4 + diff --git a/packaging/0041-Move-IAT_RELPVALUE-to-the-end-of-enum-InfoAccessType.patch b/packaging/0041-Move-IAT_RELPVALUE-to-the-end-of-enum-InfoAccessType.patch new file mode 100644 index 0000000000..3421c5edf6 --- /dev/null +++ b/packaging/0041-Move-IAT_RELPVALUE-to-the-end-of-enum-InfoAccessType.patch @@ -0,0 +1,27 @@ +From aac7c8a3ed3c9670bacfa80a0c0c71df12a5d997 Mon Sep 17 00:00:00 2001 +From: Gleb Balykov <g.balykov@samsung.com> +Date: Thu, 10 May 2018 16:21:22 +0300 +Subject: [PATCH 41/47] Move IAT_RELPVALUE to the end of enum InfoAccessType + +--- + src/inc/corinfo.h | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/inc/corinfo.h b/src/inc/corinfo.h +index 76a7a7c..32fce05 100644 +--- a/src/inc/corinfo.h ++++ b/src/inc/corinfo.h +@@ -971,8 +971,8 @@ enum InfoAccessType + { + IAT_VALUE, // The info value is directly available + IAT_PVALUE, // The value needs to be accessed via an indirection +- IAT_RELPVALUE, // The value needs to be accessed via a relative indirection +- IAT_PPVALUE // The value needs to be accessed via a double indirection ++ IAT_PPVALUE, // The value needs to be accessed via a double indirection ++ IAT_RELPVALUE // The value needs to be accessed via a relative indirection + }; + + enum CorInfoGCType +-- +2.7.4 + diff --git a/packaging/0042-Add-FEATURE_NGEN_RELOCS_OPTIMIZATIONS-true-as-defaul.patch b/packaging/0042-Add-FEATURE_NGEN_RELOCS_OPTIMIZATIONS-true-as-defaul.patch new file mode 100644 index 0000000000..e4c57fdc09 --- /dev/null +++ b/packaging/0042-Add-FEATURE_NGEN_RELOCS_OPTIMIZATIONS-true-as-defaul.patch @@ -0,0 +1,22 @@ +From 3cf6e8b85d478470088313be6d31bc1f20387205 Mon Sep 17 00:00:00 2001 +From: Gleb Balykov <g.balykov@samsung.com> +Date: Fri, 1 Jun 2018 20:44:46 +0300 +Subject: [PATCH 42/47] Add FEATURE_NGEN_RELOCS_OPTIMIZATIONS=true as default + value + +--- + clrdefinitions.cmake | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/clrdefinitions.cmake b/clrdefinitions.cmake +index 8ba01c5..76acb50 100644 +--- a/clrdefinitions.cmake ++++ b/clrdefinitions.cmake +@@ -201,3 +201,4 @@ add_definitions(-DFEATURE_WINMD_RESILIENT) + add_definitions(-D_SECURE_SCL=0) + add_definitions(-DUNICODE) + add_definitions(-D_UNICODE) ++add_definitions(-DFEATURE_NGEN_RELOCS_OPTIMIZATIONS) +-- +2.7.4 + diff --git a/packaging/0043-Fix-setup-of-FEATURE_NGEN_RELOCS_OPTIMIZATIONS-for-U.patch b/packaging/0043-Fix-setup-of-FEATURE_NGEN_RELOCS_OPTIMIZATIONS-for-U.patch new file mode 100644 index 0000000000..9be1c44be7 --- /dev/null +++ b/packaging/0043-Fix-setup-of-FEATURE_NGEN_RELOCS_OPTIMIZATIONS-for-U.patch @@ -0,0 +1,25 @@ +From 8d26c1c7ca45337363284baeeb80f5c381b46b13 Mon Sep 17 00:00:00 2001 +From: Gleb Balykov <g.balykov@samsung.com> +Date: Tue, 5 Jun 2018 17:43:41 +0300 +Subject: [PATCH 43/47] Fix setup of FEATURE_NGEN_RELOCS_OPTIMIZATIONS for Unix + ARM only + +--- + clrdefinitions.cmake | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/clrdefinitions.cmake b/clrdefinitions.cmake +index 76acb50..1fb80a2 100644 +--- a/clrdefinitions.cmake ++++ b/clrdefinitions.cmake +@@ -201,4 +201,6 @@ add_definitions(-DFEATURE_WINMD_RESILIENT) + add_definitions(-D_SECURE_SCL=0) + add_definitions(-DUNICODE) + add_definitions(-D_UNICODE) +-add_definitions(-DFEATURE_NGEN_RELOCS_OPTIMIZATIONS) ++if (CLR_CMAKE_PLATFORM_UNIX AND CLR_CMAKE_TARGET_ARCH_ARM) ++ add_definitions(-DFEATURE_NGEN_RELOCS_OPTIMIZATIONS) ++endif() +-- +2.7.4 + diff --git a/packaging/0044-Replace-push-pop-of-R11-in-stubs-with.patch b/packaging/0044-Replace-push-pop-of-R11-in-stubs-with.patch new file mode 100644 index 0000000000..6c37a505cf --- /dev/null +++ b/packaging/0044-Replace-push-pop-of-R11-in-stubs-with.patch @@ -0,0 +1,182 @@ +From 1c31fc93e3a1acc7c0671837e0c25fe389a8415f Mon Sep 17 00:00:00 2001 +From: Gleb Balykov <g.balykov@samsung.com> +Date: Thu, 7 Jun 2018 17:51:53 +0300 +Subject: [PATCH 44/47] Replace push/pop of R11 in stubs with - str/ldr of R4 + in space reserved in epilog for non-tail calls - usage of R4 with + hybrid-tail calls (same as for EmitShuffleThunk) + +--- + src/vm/arm/stubs.cpp | 98 +++++++++++++++++++++++++++++++++++++++++----------- + 1 file changed, 77 insertions(+), 21 deletions(-) + +diff --git a/src/vm/arm/stubs.cpp b/src/vm/arm/stubs.cpp +index ec0a1e7..950dc7d 100644 +--- a/src/vm/arm/stubs.cpp ++++ b/src/vm/arm/stubs.cpp +@@ -1721,6 +1721,13 @@ VOID StubLinkerCPU::EmitShuffleThunk(ShuffleEntry *pShuffleEntryArray) + + void StubLinkerCPU::ThumbEmitCallManagedMethod(MethodDesc *pMD, bool fTailcall) + { ++ bool isRelative = MethodTable::VTableIndir2_t::isRelative ++ && pMD->IsVtableSlot(); ++ ++#ifndef FEATURE_NGEN_RELOCS_OPTIMIZATIONS ++ _ASSERTE(!isRelative); ++#endif ++ + // Use direct call if possible. + if (pMD->HasStableEntryPoint()) + { +@@ -1732,18 +1739,16 @@ void StubLinkerCPU::ThumbEmitCallManagedMethod(MethodDesc *pMD, bool fTailcall) + // mov r12, #slotaddress + ThumbEmitMovConstant(ThumbReg(12), (TADDR)pMD->GetAddrOfSlot()); + +- bool isRelative = MethodTable::VTableIndir2_t::isRelative +- && pMD->IsVtableSlot(); + if (isRelative) + { +-#if defined(FEATURE_NGEN_RELOCS_OPTIMIZATIONS) +- // push r11 +- ThumbEmitPush(ThumbReg(11).Mask()); +- // mov r11, r12 +- ThumbEmitMovRegReg(ThumbReg(11), ThumbReg(12)); +-#else +- _ASSERTE(false); +-#endif ++ if (!fTailcall) ++ { ++ // str r4, [sp, 0] ++ ThumbEmitStoreRegIndirect(ThumbReg(4), thumbRegSp, 0); ++ } ++ ++ // mov r4, r12 ++ ThumbEmitMovRegReg(ThumbReg(4), ThumbReg(12)); + } + + // ldr r12, [r12] +@@ -1751,21 +1756,30 @@ void StubLinkerCPU::ThumbEmitCallManagedMethod(MethodDesc *pMD, bool fTailcall) + + if (isRelative) + { +-#if defined(FEATURE_NGEN_RELOCS_OPTIMIZATIONS) +- // add r12, r11 +- ThumbEmitAddReg(ThumbReg(12), ThumbReg(11)); +- // pop r11 +- ThumbEmitPop(ThumbReg(11).Mask()); +-#else +- _ASSERTE(false); +-#endif ++ // add r12, r4 ++ ThumbEmitAddReg(ThumbReg(12), ThumbReg(4)); ++ ++ if (!fTailcall) ++ { ++ // ldr r4, [sp, 0] ++ ThumbEmitLoadRegIndirect(ThumbReg(4), thumbRegSp, 0); ++ } + } + } + + if (fTailcall) + { +- // bx r12 +- ThumbEmitJumpRegister(ThumbReg(12)); ++ if (!isRelative) ++ { ++ // bx r12 ++ ThumbEmitJumpRegister(ThumbReg(12)); ++ } ++ else ++ { ++ // Replace LR with R12 on stack: hybrid-tail call, same as for EmitShuffleThunk ++ // str r12, [sp, 4] ++ ThumbEmitStoreRegIndirect(ThumbReg(12), thumbRegSp, 4); ++ } + } + else + { +@@ -1902,6 +1916,13 @@ void StubLinkerCPU::ThumbEmitCallWithGenericInstantiationParameter(MethodDesc *p + } + } + ++ bool isRelative = MethodTable::VTableIndir2_t::isRelative ++ && pMD->IsVtableSlot(); ++ ++#ifndef FEATURE_NGEN_RELOCS_OPTIMIZATIONS ++ _ASSERTE(!isRelative); ++#endif ++ + // Update descriptor count to the actual number used. + cArgDescriptors = idxCurrentDesc; + +@@ -1994,7 +2015,17 @@ void StubLinkerCPU::ThumbEmitCallWithGenericInstantiationParameter(MethodDesc *p + } + + // Emit a tail call to the target method. ++ if (isRelative) ++ { ++ ThumbEmitProlog(1, 0, FALSE); ++ } ++ + ThumbEmitCallManagedMethod(pMD, true); ++ ++ if (isRelative) ++ { ++ ThumbEmitEpilog(); ++ } + } + else + { +@@ -2003,7 +2034,9 @@ void StubLinkerCPU::ThumbEmitCallWithGenericInstantiationParameter(MethodDesc *p + // Calculate the size of the new stack frame: + // + // +------------+ +- // SP -> | | <-+ ++ // SP -> | | <-- Space for helper arg, if isRelative is true ++ // +------------+ ++ // | | <-+ + // : : | Outgoing arguments + // | | <-+ + // +------------+ +@@ -2034,6 +2067,12 @@ void StubLinkerCPU::ThumbEmitCallWithGenericInstantiationParameter(MethodDesc *p + DWORD cbStackArgs = (pLastArg->m_idxDst + 1) * 4; + DWORD cbStackFrame = cbStackArgs + sizeof(GSCookie) + sizeof(StubHelperFrame); + cbStackFrame = ALIGN_UP(cbStackFrame, 8); ++ ++ if (isRelative) ++ { ++ cbStackFrame += 4; ++ } ++ + DWORD cbStackFrameWithoutSavedRegs = cbStackFrame - (13 * 4); // r0-r11,lr + + // Prolog: +@@ -2242,8 +2281,25 @@ void StubLinkerCPU::EmitUnboxMethodStub(MethodDesc *pMD) + // add r0, #4 + ThumbEmitIncrement(ThumbReg(0), 4); + ++ bool isRelative = MethodTable::VTableIndir2_t::isRelative ++ && pMD->IsVtableSlot(); ++ ++#ifndef FEATURE_NGEN_RELOCS_OPTIMIZATIONS ++ _ASSERTE(!isRelative); ++#endif ++ ++ if (isRelative) ++ { ++ ThumbEmitProlog(1, 0, FALSE); ++ } ++ + // Tail call the real target. + ThumbEmitCallManagedMethod(pMD, true /* tail call */); ++ ++ if (isRelative) ++ { ++ ThumbEmitEpilog(); ++ } + } + } + +-- +2.7.4 + diff --git a/packaging/0045-Replace-push-pop-of-R11-for-function-epilog-with-usa.patch b/packaging/0045-Replace-push-pop-of-R11-for-function-epilog-with-usa.patch new file mode 100644 index 0000000000..314cd00eba --- /dev/null +++ b/packaging/0045-Replace-push-pop-of-R11-for-function-epilog-with-usa.patch @@ -0,0 +1,93 @@ +From 777191a4bb6197f29d04889698c167aec4b49d39 Mon Sep 17 00:00:00 2001 +From: Gleb Balykov <g.balykov@samsung.com> +Date: Fri, 8 Jun 2018 19:16:11 +0300 +Subject: [PATCH 45/47] Replace push/pop of R11 for function epilog with usage + of LR as helper register right before its restore from stack + +--- + src/jit/codegencommon.cpp | 52 +++++++++++++++++++++++++++++++++++++---------- + 1 file changed, 41 insertions(+), 11 deletions(-) + +diff --git a/src/jit/codegencommon.cpp b/src/jit/codegencommon.cpp +index 51cc777..3084582 100644 +--- a/src/jit/codegencommon.cpp ++++ b/src/jit/codegencommon.cpp +@@ -9433,6 +9433,43 @@ void CodeGen::genFnEpilog(BasicBlock* block) + unwindStarted = true; + } + ++#ifdef FEATURE_NGEN_RELOCS_OPTIMIZATIONS ++ if (jmpEpilog) ++ { ++ // In case of FEATURE_NGEN_RELOCS_OPTIMIZATIONS and IAT_RELPVALUE jump at the end is done using ++ // relative indirection, so, additional helper register is required. ++ // We use LR just before it is going to be restored from stack, i.e. ++ // ++ // movw r12, laddr ++ // movt r12, haddr ++ // mov lr, r12 ++ // ldr r12, [r12] ++ // add r12, r12, lr ++ // pop {lr} ++ // ... ++ // bx r12 ++ ++ GenTree* jmpNode = block->lastNode(); ++ ++ noway_assert(jmpNode->gtOper == GT_JMP); ++ ++ CORINFO_METHOD_HANDLE methHnd = (CORINFO_METHOD_HANDLE)jmpNode->gtVal.gtVal1; ++ CORINFO_CONST_LOOKUP addrInfo; ++ compiler->info.compCompHnd->getFunctionEntryPoint(methHnd, &addrInfo); ++ ++ if (addrInfo.accessType == IAT_RELPVALUE) ++ { ++ regNumber indCallReg = REG_R12; ++ regNumber vptrReg1 = REG_LR; ++ ++ instGen_Set_Reg_To_Imm(EA_HANDLE_CNS_RELOC, indCallReg, (ssize_t)addrInfo.addr); ++ getEmitter()->emitIns_R_R(INS_mov, EA_PTRSIZE, vptrReg1, indCallReg); ++ getEmitter()->emitIns_R_R_I(INS_ldr, EA_PTRSIZE, indCallReg, indCallReg, 0); ++ getEmitter()->emitIns_R_R(INS_add, EA_PTRSIZE, indCallReg, vptrReg1); ++ } ++ } ++#endif // FEATURE_NGEN_RELOCS_OPTIMIZATIONS ++ + genPopCalleeSavedRegisters(jmpEpilog); + + if (regSet.rsMaskPreSpillRegs(true) != RBM_NONE) +@@ -9497,27 +9534,20 @@ void CodeGen::genFnEpilog(BasicBlock* block) + break; + + case IAT_RELPVALUE: ++#ifdef FEATURE_NGEN_RELOCS_OPTIMIZATIONS + { + // Load the address into a register, load relative indirect and call through a register + // We have to use R12 since we assume the argument registers are in use ++ // LR is used as helper register right before it is restored from stack, thus, ++ // all relative address calculations are performed before LR is restored. + callType = emitter::EC_INDIR_R; + indCallReg = REG_R12; + addr = NULL; + +- regNumber vptrReg1 = REG_R11; +- regMaskTP vptrReg1Mask = genRegMask(vptrReg1); +- inst_IV(INS_push, (int)vptrReg1Mask); +- +- instGen_Set_Reg_To_Imm(EA_HANDLE_CNS_RELOC, indCallReg, (ssize_t)addrInfo.addr); +- getEmitter()->emitIns_R_R(INS_mov, EA_PTRSIZE, vptrReg1, indCallReg); +- getEmitter()->emitIns_R_R_I(INS_ldr, EA_PTRSIZE, indCallReg, indCallReg, 0); +- getEmitter()->emitIns_R_R(INS_add, EA_PTRSIZE, indCallReg, vptrReg1); +- +- inst_IV(INS_pop, (int)vptrReg1Mask); +- + regTracker.rsTrackRegTrash(indCallReg); + break; + } ++#endif // FEATURE_NGEN_RELOCS_OPTIMIZATIONS + + case IAT_PPVALUE: + default: +-- +2.7.4 + diff --git a/packaging/0046-Remove-ifdef.patch b/packaging/0046-Remove-ifdef.patch new file mode 100644 index 0000000000..2208fea075 --- /dev/null +++ b/packaging/0046-Remove-ifdef.patch @@ -0,0 +1,32 @@ +From 9cc43901915b2fb5eface2fc9b733544b52e4ee0 Mon Sep 17 00:00:00 2001 +From: Gleb Balykov <g.balykov@samsung.com> +Date: Fri, 8 Jun 2018 20:00:03 +0300 +Subject: [PATCH 46/47] Remove ifdef + +--- + src/jit/codegencommon.cpp | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/src/jit/codegencommon.cpp b/src/jit/codegencommon.cpp +index 3084582..fce9a0c 100644 +--- a/src/jit/codegencommon.cpp ++++ b/src/jit/codegencommon.cpp +@@ -9534,7 +9534,6 @@ void CodeGen::genFnEpilog(BasicBlock* block) + break; + + case IAT_RELPVALUE: +-#ifdef FEATURE_NGEN_RELOCS_OPTIMIZATIONS + { + // Load the address into a register, load relative indirect and call through a register + // We have to use R12 since we assume the argument registers are in use +@@ -9547,7 +9546,6 @@ void CodeGen::genFnEpilog(BasicBlock* block) + regTracker.rsTrackRegTrash(indCallReg); + break; + } +-#endif // FEATURE_NGEN_RELOCS_OPTIMIZATIONS + + case IAT_PPVALUE: + default: +-- +2.7.4 + diff --git a/packaging/0047-Launching-the-Memory-Profiler-on-x86-emulator-may-le.patch b/packaging/0047-Launching-the-Memory-Profiler-on-x86-emulator-may-le.patch new file mode 100644 index 0000000000..bdbbc7bfc3 --- /dev/null +++ b/packaging/0047-Launching-the-Memory-Profiler-on-x86-emulator-may-le.patch @@ -0,0 +1,43 @@ +From 2f6cf609fe91270a416219c95e7f512108f34185 Mon Sep 17 00:00:00 2001 +From: Sergey Ignatov <sergign60@mail.ru> +Date: Fri, 8 Jun 2018 19:50:47 +0300 +Subject: [PATCH 47/47] Launching the Memory Profiler on x86 emulator may lead + to crash in coreclr (xmm bug) + +--- + src/pal/src/CMakeLists.txt | 2 ++ + src/utilcode/CMakeLists.txt | 5 +++++ + 2 files changed, 7 insertions(+) + +diff --git a/src/pal/src/CMakeLists.txt b/src/pal/src/CMakeLists.txt +index b8a9fe9..2f877ac 100644 +--- a/src/pal/src/CMakeLists.txt ++++ b/src/pal/src/CMakeLists.txt +@@ -91,6 +91,8 @@ elseif(PAL_CMAKE_PLATFORM_ARCH_ARM64) + elseif(PAL_CMAKE_PLATFORM_ARCH_I386) + add_definitions(-DBIT32=1) + set(PAL_ARCH_SOURCES_DIR i386) ++ # Workaround to avoid generating sse insts for profiler ++ add_compile_options(-mno-sse -mno-avx) + endif() + + if(PAL_CMAKE_PLATFORM_ARCH_AMD64 AND CMAKE_SYSTEM_NAME STREQUAL Linux AND NOT CLR_CMAKE_PLATFORM_ALPINE_LINUX) +diff --git a/src/utilcode/CMakeLists.txt b/src/utilcode/CMakeLists.txt +index dfe830d..90cb584 100644 +--- a/src/utilcode/CMakeLists.txt ++++ b/src/utilcode/CMakeLists.txt +@@ -126,6 +126,11 @@ if(CLR_CMAKE_PLATFORM_UNIX) + add_compile_options(-fPIC) + endif(CLR_CMAKE_PLATFORM_UNIX) + ++if(CLR_CMAKE_PLATFORM_ARCH_I386) ++ # Workaround to avoid generating sse insts for profiler ++ add_compile_options(-mno-sse -mno-avx) ++endif(CLR_CMAKE_PLATFORM_ARCH_I386) ++ + add_subdirectory(dac) + add_subdirectory(dyncrt) + add_subdirectory(staticnohost) +-- +2.7.4 + diff --git a/packaging/coreclr.spec b/packaging/coreclr.spec index e92c3fcc54..35bce027b6 100644 --- a/packaging/coreclr.spec +++ b/packaging/coreclr.spec @@ -23,7 +23,7 @@ Source1000: downloaded_files.tar.gz Source1001: %{name}.manifest Source1002: libicu.tar.gz Source1003: dep_libs.tar.gz -# Gbp-Ignore-Patches: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 +# Gbp-Ignore-Patches: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 Patch0: 0001-Add-project.assets.json-files.patch Patch1: 0001-ARM-Linux-Support-unaligned-struct-read-write-11290.patch Patch2: 0002-x86-Linux-Thread-safe-UMThunkMarshInfo-RunTimeInit-1.patch @@ -116,6 +116,39 @@ Patch88: 0011-Enable-thread-abort-reraise-loop-prevention.patch Patch89: 0012-16-byte-Stack-Aligned-StubDispatchFixupStub.patch Patch90: 0013-Fix-EECodeManager-GetAmbientSP-on-x86-Linux.patch Patch91: 0014-Fix-OOPStackUnwinderX86-Unwind-crash-when-Eip-is-inv.patch +Patch92: 0001-Improve-UMEntryThunkCode-Poison-method.patch +Patch93: 0002-UMEntryThunk-store-freed-thunks-into-FIFO-free-list.patch +Patch94: 0003-dllimportcallback-remove-code-for-CallbackOnCollecte.patch +Patch95: 0004-LoaderHeap-remove-LHF_ZEROINIT-option.patch +Patch96: 0019-JIT-Fix-value-type-box-optimization.patch +Patch97: 0020-JIT-port-fix-to-defer-removing-statements-during-opt.patch +Patch98: 0021-Revert-ExecuteHandlerOnOriginalStack-handle-case-whe.patch +Patch99: 0022-CatchHardwareExceptionHolder-use-GetCurrentPalThread.patch +Patch100: 0023-vm-threads-change-tls-model-for-gCurrentThreadInfo-v.patch +Patch101: 0024-sigsegv_handler-handle-case-when-it-is-called-on-ori.patch +Patch102: 0025-Revert-TLS-model-change-of-the-gCurrentThreadInfo.patch +Patch103: 0026-Prevent-memory-allocation-in-signal-handler.patch +Patch104: 0027-Revert-Prevent-memory-allocation-in-signal-handler.patch +Patch105: 0028-Do-not-allocate-exception-for-signal-from-non-manage.patch +Patch106: 0029-Move-exception-allocation-to-PAL_SEHException.patch +Patch107: 0030-Remove-exception-records-allocation-from-pal.h.patch +Patch108: 0031-Fix-preventing-memory-allocation-in-signal-handler.patch +Patch109: 0032-Fix-Use-of-EventPipeConfiguration-After-it-has-Been-.patch +Patch110: 0033-Revert-clear-cache-after-NI-reloc.patch +Patch111: 0034-PEImageLayout-clear-instruction-cache-after-relocati.patch +Patch112: 0035-PEImageLayout-flush-instruction-cache-only-for-pages.patch +Patch113: 0036-Separate-sections-READONLY_VCHUNKS-and-READONLY_DICT.patch +Patch114: 0037-Remove-relocations-for-second-level-indirection-of-V.patch +Patch115: 0038-Replace-PLATFORM_UNIX-_TARGET_ARM_-for-NGEN-relocati.patch +Patch116: 0039-Fix-formatting.patch +Patch117: 0040-Update-GUID.patch +Patch118: 0041-Move-IAT_RELPVALUE-to-the-end-of-enum-InfoAccessType.patch +Patch119: 0042-Add-FEATURE_NGEN_RELOCS_OPTIMIZATIONS-true-as-defaul.patch +Patch120: 0043-Fix-setup-of-FEATURE_NGEN_RELOCS_OPTIMIZATIONS-for-U.patch +Patch121: 0044-Replace-push-pop-of-R11-in-stubs-with.patch +Patch122: 0045-Replace-push-pop-of-R11-for-function-epilog-with-usa.patch +Patch123: 0046-Remove-ifdef.patch +Patch124: 0047-Launching-the-Memory-Profiler-on-x86-emulator-may-le.patch ExcludeArch: aarch64 @@ -309,6 +342,39 @@ cp %{SOURCE1001} . %patch89 -p1 %patch90 -p1 %patch91 -p1 +%patch92 -p1 +%patch93 -p1 +%patch94 -p1 +%patch95 -p1 +%patch96 -p1 +%patch97 -p1 +%patch98 -p1 +%patch99 -p1 +%patch100 -p1 +%patch101 -p1 +%patch102 -p1 +%patch103 -p1 +%patch104 -p1 +%patch105 -p1 +%patch106 -p1 +%patch107 -p1 +%patch108 -p1 +%patch109 -p1 +%patch110 -p1 +%patch111 -p1 +%patch112 -p1 +%patch113 -p1 +%patch114 -p1 +%patch115 -p1 +%patch116 -p1 +%patch117 -p1 +%patch118 -p1 +%patch119 -p1 +%patch120 -p1 +%patch121 -p1 +%patch122 -p1 +%patch123 -p1 +%patch124 -p1 %if 0%{skipmscorlib} |