From 82efdc95287b6d56dfd62a5036e3268ac79cac90 Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Tue, 26 Aug 2025 16:25:10 -0700 Subject: [PATCH 01/15] Create FEATURE_PORTABLE_ENTRYPOINTS build flag. This new feature will be for portable method entry points. For example, platforms that are unable to allocation executable memory (that is, WASM). --- src/coreclr/clr.featuredefines.props | 6 ++++++ src/coreclr/clrdefinitions.cmake | 1 + src/coreclr/clrfeatures.cmake | 1 + 3 files changed, 8 insertions(+) diff --git a/src/coreclr/clr.featuredefines.props b/src/coreclr/clr.featuredefines.props index 81fb995c84a793..efa15c94415c8a 100644 --- a/src/coreclr/clr.featuredefines.props +++ b/src/coreclr/clr.featuredefines.props @@ -3,6 +3,7 @@ true true true + false true @@ -10,6 +11,10 @@ true + + true + + true true @@ -43,6 +48,7 @@ $(DefineConstants);FEATURE_TYPEEQUIVALENCE $(DefineConstants);FEATURE_EH_FUNCLETS $(DefineConstants);FEATURE_INTERPRETER + $(DefineConstants);FEATURE_PORTABLE_ENTRYPOINTS $(DefineConstants);PROFILING_SUPPORTED diff --git a/src/coreclr/clrdefinitions.cmake b/src/coreclr/clrdefinitions.cmake index 5dba165212f0d4..7ac60959b41e2b 100644 --- a/src/coreclr/clrdefinitions.cmake +++ b/src/coreclr/clrdefinitions.cmake @@ -118,6 +118,7 @@ if (CLR_CMAKE_TARGET_WIN32 AND (CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ endif (CLR_CMAKE_TARGET_WIN32 AND (CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_I386 OR CLR_CMAKE_TARGET_ARCH_ARM64)) add_compile_definitions($<${FEATURE_INTERPRETER}:FEATURE_INTERPRETER>) +add_compile_definitions($<${FEATURE_PORTABLE_ENTRYPOINTS}:FEATURE_PORTABLE_ENTRYPOINTS>) if (CLR_CMAKE_TARGET_WIN32) add_definitions(-DFEATURE_ISYM_READER) diff --git a/src/coreclr/clrfeatures.cmake b/src/coreclr/clrfeatures.cmake index 3b4fb203ee2c51..8a22b34c41415b 100644 --- a/src/coreclr/clrfeatures.cmake +++ b/src/coreclr/clrfeatures.cmake @@ -39,6 +39,7 @@ if(NOT DEFINED FEATURE_INTERPRETER) set(FEATURE_INTERPRETER 0) elseif(CLR_CMAKE_TARGET_ARCH_WASM) set(FEATURE_INTERPRETER 1) + set(FEATURE_PORTABLE_ENTRYPOINTS 1) else() if(CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_ARM64) set(FEATURE_INTERPRETER $,1,0>) From e6daead89b63bc35ee188622c0ace8b9cf19104e Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Wed, 27 Aug 2025 17:50:04 -0700 Subject: [PATCH 02/15] WASM uses PortableEntryPoint --- src/coreclr/interpreter/compiler.cpp | 6 +- src/coreclr/interpreter/interpretershared.h | 3 +- src/coreclr/vm/CMakeLists.txt | 2 + src/coreclr/vm/callhelpers.cpp | 10 +- src/coreclr/vm/callhelpers.h | 10 +- src/coreclr/vm/interpexec.cpp | 33 +++-- src/coreclr/vm/jitinterface.cpp | 10 ++ src/coreclr/vm/method.cpp | 38 ++++-- src/coreclr/vm/method.hpp | 6 + src/coreclr/vm/precode.cpp | 27 +--- src/coreclr/vm/precode.h | 33 ++--- src/coreclr/vm/precode_portable.cpp | 84 ++++++++++++ src/coreclr/vm/precode_portable.hpp | 134 ++++++++++++++++++++ src/coreclr/vm/prestub.cpp | 10 +- 14 files changed, 327 insertions(+), 79 deletions(-) create mode 100644 src/coreclr/vm/precode_portable.cpp create mode 100644 src/coreclr/vm/precode_portable.hpp diff --git a/src/coreclr/interpreter/compiler.cpp b/src/coreclr/interpreter/compiler.cpp index c93116c145ce14..93564ee61edaff 100644 --- a/src/coreclr/interpreter/compiler.cpp +++ b/src/coreclr/interpreter/compiler.cpp @@ -2233,7 +2233,7 @@ int32_t InterpCompiler::GetDataForHelperFtn(CorInfoHelpFunc ftn) static_assert(sizeof(InterpHelperData) == sizeof(int32_t), "InterpHelperData must be the same size as an int32_t"); - InterpHelperData result; + InterpHelperData result{}; result.accessType = ftnLookup.accessType; int32_t dataItemIndex = GetDataItemIndex(ftnLookup.addr); result.addressDataItemIndex = dataItemIndex; @@ -6577,8 +6577,8 @@ void InterpCompiler::PrintPointer(void* pointer) void InterpCompiler::PrintHelperFtn(int32_t _data) { - InterpHelperData data; - memcpy(&data, &_data, sizeof(int32_t)); + InterpHelperData data{}; + memcpy(&data, &_data, sizeof(_data)); void *helperAddr = GetDataItemAtIndex(data.addressDataItemIndex); PrintPointer(helperAddr); diff --git a/src/coreclr/interpreter/interpretershared.h b/src/coreclr/interpreter/interpretershared.h index aaa90045dd9f29..137077d6add124 100644 --- a/src/coreclr/interpreter/interpretershared.h +++ b/src/coreclr/interpreter/interpretershared.h @@ -30,7 +30,8 @@ struct InterpMethod InterpMethod *self; #endif CORINFO_METHOD_HANDLE methodHnd; - int32_t argsSize, allocaSize; + int32_t argsSize; + int32_t allocaSize; void** pDataItems; // This stub is used for calling the interpreted method from JITted/AOTed code CallStubHeader *pCallStub; diff --git a/src/coreclr/vm/CMakeLists.txt b/src/coreclr/vm/CMakeLists.txt index 37f1a76d4f73a3..53faa6b87aeee0 100644 --- a/src/coreclr/vm/CMakeLists.txt +++ b/src/coreclr/vm/CMakeLists.txt @@ -112,6 +112,7 @@ set(VM_SOURCES_DAC_AND_WKS_COMMON peimage.cpp perfmap.cpp pgo.cpp + precode_portable.cpp precode.cpp prestub.cpp readytorunstandalonemethodmetadata.cpp @@ -215,6 +216,7 @@ set(VM_HEADERS_DAC_AND_WKS_COMMON peimagelayout.inl perfmap.h pgo.h + precode_portable.hpp precode.h rejit.h rejit.inl diff --git a/src/coreclr/vm/callhelpers.cpp b/src/coreclr/vm/callhelpers.cpp index 4bf742d5078440..daf5df18ee75fc 100644 --- a/src/coreclr/vm/callhelpers.cpp +++ b/src/coreclr/vm/callhelpers.cpp @@ -180,11 +180,11 @@ void CopyReturnedFpStructFromRegisters(void* dest, UINT64 returnRegs[2], FpStruc #endif // TARGET_RISCV64 || TARGET_LOONGARCH64 // Helper for VM->managed calls with simple signatures. -void * DispatchCallSimple( - SIZE_T *pSrc, - DWORD numStackSlotsToCopy, - PCODE pTargetAddress, - DWORD dwDispatchCallSimpleFlags) +void* DispatchCallSimple( + SIZE_T *pSrc, + DWORD numStackSlotsToCopy, + PCODE pTargetAddress, + DWORD dwDispatchCallSimpleFlags) { CONTRACTL { diff --git a/src/coreclr/vm/callhelpers.h b/src/coreclr/vm/callhelpers.h index 12b4a8da1af464..91a249cbc7660b 100644 --- a/src/coreclr/vm/callhelpers.h +++ b/src/coreclr/vm/callhelpers.h @@ -74,11 +74,11 @@ void CallDescrWorkerWithHandler( BOOL fCriticalCall = FALSE); // Helper for VM->managed calls with simple signatures. -void * DispatchCallSimple( - SIZE_T *pSrc, - DWORD numStackSlotsToCopy, - PCODE pTargetAddress, - DWORD dwDispatchCallSimpleFlags); +void* DispatchCallSimple( + SIZE_T *pSrc, + DWORD numStackSlotsToCopy, + PCODE pTargetAddress, + DWORD dwDispatchCallSimpleFlags); #if defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) // Copy structs returned according to floating-point calling convention from 'returnRegs' containing struct fields diff --git a/src/coreclr/vm/interpexec.cpp b/src/coreclr/vm/interpexec.cpp index f0b5a81f641295..57ae30d32866e1 100644 --- a/src/coreclr/vm/interpexec.cpp +++ b/src/coreclr/vm/interpexec.cpp @@ -232,23 +232,36 @@ static OBJECTREF CreateMultiDimArray(MethodTable* arrayClass, int8_t* stack, int #define LOCAL_VAR(offset,type) (*LOCAL_VAR_ADDR(offset, type)) #define NULL_CHECK(o) do { if ((o) == NULL) { COMPlusThrow(kNullReferenceException); } } while (0) -template static THelper GetPossiblyIndirectHelper(const InterpMethod *pMethod, int32_t _data) +template static THelper GetPossiblyIndirectHelper(const InterpMethod* pMethod, int32_t _data, MethodDesc** pILTargetMethod = NULL) { - InterpHelperData data; - memcpy(&data, &_data, sizeof(int32_t)); + InterpHelperData data{}; + memcpy(&data, &_data, sizeof(_data)); - void *addr = pMethod->pDataItems[data.addressDataItemIndex]; - switch (data.accessType) { + void* addr = pMethod->pDataItems[data.addressDataItemIndex]; + switch (data.accessType) + { case IAT_VALUE: - return (THelper)addr; + break; case IAT_PVALUE: - return *(THelper *)addr; + addr = *(void**)addr; + break; case IAT_PPVALUE: - return **(THelper **)addr; + addr = **(void***)addr; + break; default: COMPlusThrowHR(COR_E_EXECUTIONENGINE); - return (THelper)nullptr; + break; } + +#ifdef FEATURE_PORTABLE_ENTRYPOINTS + if (!PortableEntryPoint::IsNativeEntryPoint(addr)) + { + *pILTargetMethod = PortableEntryPoint::GetMethodDesc(addr); + return NULL; // Return null to interpret this entrypoint + } +#endif // FEATURE_PORTABLE_ENTRYPOINTS + + return (THelper)addr; } // At present our behavior for float to int conversions is to perform a saturating conversion down to either 32 or 64 bits @@ -2053,7 +2066,7 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr pInterpreterFrame->SetTopInterpMethodContextFrame(pFrame); GCX_PREEMP(); // Attempt to setup the interpreter code for the target method. - if ((targetMethod->IsIL() || targetMethod->IsNoMetadata()) && !targetMethod->IsUnboxingStub()) + if (targetMethod->CanBeInterpreted()) { PrepareInitialCode(targetMethod); } diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 77f65cb7178463..0775ce797a5667 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -13351,10 +13351,20 @@ PCODE UnsafeJitFunction(PrepareCodeConfig* config, { sizeOfILCode = interpreterJitInfo.getMethodInfoInternal()->ILCodeSize; +#ifdef FEATURE_PORTABLE_ENTRYPOINTS + PCODE portableEntryPoint = ftn->GetTemporaryEntryPoint(); + _ASSERTE(portableEntryPoint != NULL); + PortableEntryPoint::SetInterpreterData((void*)PCODEToPINSTR(portableEntryPoint), ret); + ret = portableEntryPoint; + +#else // !FEATURE_PORTABLE_ENTRYPOINTS AllocMemTracker amt; InterpreterPrecode* pPrecode = Precode::AllocateInterpreterPrecode(ret, ftn->GetLoaderAllocator(), &amt); amt.SuppressRelease(); ret = PINSTRToPCODE(pPrecode->GetEntryPoint()); + +#endif // FEATURE_PORTABLE_ENTRYPOINTS + *isInterpreterCode = true; *isTier0 = interpreterJitInfo.getJitFlagsInternal()->IsSet(CORJIT_FLAGS::CORJIT_FLAG_TIER0); } diff --git a/src/coreclr/vm/method.cpp b/src/coreclr/vm/method.cpp index 769a5a0d720c95..0dccc4ce795074 100644 --- a/src/coreclr/vm/method.cpp +++ b/src/coreclr/vm/method.cpp @@ -2636,12 +2636,16 @@ MethodDesc* MethodDesc::GetMethodDescFromPrecode(PCODE addr, BOOL fSpeculative / MethodDesc* pMD = NULL; +#ifdef FEATURE_PORTABLE_ENTRYPOINTS + pMD = PortableEntryPoint::GetMethodDesc((void*)PCODEToPINSTR(addr)); + +#else // !FEATURE_PORTABLE_ENTRYPOINTS PTR_Precode pPrecode = Precode::GetPrecodeFromEntryPoint(addr, fSpeculative); _ASSERTE(fSpeculative || (pPrecode != NULL)); if (pPrecode != NULL) - { pMD = pPrecode->GetMethodDesc(fSpeculative); - } + +#endif // FEATURE_PORTABLE_ENTRYPOINTS RETURN(pMD); } @@ -2735,12 +2739,24 @@ void MethodDesc::EnsureTemporaryEntryPointCore(AllocMemTracker *pamTracker) PTR_PCODE pSlot = GetAddrOfSlot(); AllocMemTracker amt; - AllocMemTracker *pamTrackerPrecode = pamTracker != NULL ? pamTracker : &amt; + AllocMemTracker* pamTrackerPrecode = pamTracker != NULL ? pamTracker : &amt; + + PCODE entryPoint; +#ifdef FEATURE_PORTABLE_ENTRYPOINTS + PortableEntryPoint* portableEntryPoint = (PortableEntryPoint*)pamTrackerPrecode->Track( + GetLoaderAllocator()->GetHighFrequencyHeap()->AllocMem(S_SIZE_T{ sizeof(PortableEntryPoint) })); + portableEntryPoint->Init(this); + entryPoint = (PCODE)portableEntryPoint; + +#else // !FEATURE_PORTABLE_ENTRYPOINTS Precode* pPrecode = Precode::Allocate(GetPrecodeType(), this, GetLoaderAllocator(), pamTrackerPrecode); + entryPoint = pPrecode->GetEntryPoint(); + +#endif // FEATURE_PORTABLE_ENTRYPOINTS IfFailThrow(EnsureCodeDataExists(pamTracker)); - if (InterlockedCompareExchangeT(&m_codeData->TemporaryEntryPoint, pPrecode->GetEntryPoint(), (PCODE)NULL) == (PCODE)NULL) + if (InterlockedCompareExchangeT(&m_codeData->TemporaryEntryPoint, entryPoint, (PCODE)NULL) == (PCODE)NULL) amt.SuppressRelease(); // We only need to suppress the release if we are working with a MethodDesc which is not newly allocated PCODE tempEntryPoint = m_codeData->TemporaryEntryPoint; @@ -2827,12 +2843,14 @@ Precode* MethodDesc::GetOrCreatePrecode() #ifdef _DEBUG PTR_PCODE pSlot = GetAddrOfSlot(); + _ASSERTE(*pSlot != (PCODE)NULL); + _ASSERTE(*pSlot == tempEntry); +#ifndef FEATURE_PORTABLE_ENTRYPOINTS PrecodeType requiredType = GetPrecodeType(); PrecodeType availableType = Precode::GetPrecodeFromEntryPoint(tempEntry)->GetType(); _ASSERTE(requiredType == availableType); - _ASSERTE(*pSlot != (PCODE)NULL); - _ASSERTE(*pSlot == tempEntry); -#endif +#endif // !FEATURE_PORTABLE_ENTRYPOINTS +#endif // _DEBUG // Set the flags atomically InterlockedUpdateFlags3(enum_flag3_HasStableEntryPoint | enum_flag3_HasPrecode, TRUE); @@ -2845,10 +2863,14 @@ void MethodDesc::MarkPrecodeAsStableEntrypoint() #if _DEBUG PCODE tempEntry = GetTemporaryEntryPointIfExists(); _ASSERTE(tempEntry != (PCODE)NULL); +#ifdef FEATURE_PORTABLE_ENTRYPOINTS + _ASSERTE(PortableEntryPoint::GetMethodDesc((void*)PCODEToPINSTR(tempEntry)) == this); +#else // !FEATURE_PORTABLE_ENTRYPOINTS PrecodeType requiredType = GetPrecodeType(); PrecodeType availableType = Precode::GetPrecodeFromEntryPoint(tempEntry)->GetType(); _ASSERTE(requiredType == availableType); -#endif +#endif // FEATURE_PORTABLE_ENTRYPOINTS +#endif // _DEBUG _ASSERTE(!HasPrecode()); _ASSERTE(RequiresStableEntryPoint()); diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp index dc138254d93965..d94806440e5681 100644 --- a/src/coreclr/vm/method.hpp +++ b/src/coreclr/vm/method.hpp @@ -1808,6 +1808,12 @@ class MethodDesc LIMITED_METHOD_CONTRACT; VolatileStore(&m_interpreterCode, interpreterCode); } + + bool CanBeInterpreted() + { + WRAPPER_NO_CONTRACT; + return (IsIL() || IsNoMetadata()) && !IsUnboxingStub(); + } #endif // FEATURE_INTERPRETER #ifdef _DEBUG diff --git a/src/coreclr/vm/precode.cpp b/src/coreclr/vm/precode.cpp index d5d8cffdec6487..a4a3d85332f805 100644 --- a/src/coreclr/vm/precode.cpp +++ b/src/coreclr/vm/precode.cpp @@ -7,6 +7,7 @@ // Stub that runs before the actual native code // +#ifndef FEATURE_PORTABLE_ENTRYPOINTS #include "common.h" #include "dllimportcallback.h" @@ -134,9 +135,6 @@ MethodDesc* Precode::GetMethodDesc(BOOL fSpeculative /*= FALSE*/) TADDR pMD = (TADDR)NULL; PrecodeType precodeType = GetType(); -#ifdef TARGET_WASM - pMD = *(TADDR*)(m_data + OFFSETOF_PRECODE_MD); -#else switch (precodeType) { case PRECODE_STUB: @@ -169,7 +167,6 @@ MethodDesc* Precode::GetMethodDesc(BOOL fSpeculative /*= FALSE*/) default: break; } -#endif // TARGET_WASM if (pMD == (TADDR)NULL) { @@ -322,11 +319,8 @@ void Precode::Init(Precode* pPrecodeRX, PrecodeType t, MethodDesc* pMD, LoaderAl { LIMITED_METHOD_CONTRACT; -#ifdef TARGET_WASM - m_data[OFFSETOF_PRECODE_TYPE] = t; - *(TADDR*)(m_data + OFFSETOF_PRECODE_MD) = (TADDR)pMD; -#else - switch (t) { + switch (t) + { case PRECODE_STUB: ((StubPrecode*)this)->Init((StubPrecode*)pPrecodeRX, (TADDR)pMD, pLoaderAllocator); break; @@ -349,7 +343,6 @@ void Precode::Init(Precode* pPrecodeRX, PrecodeType t, MethodDesc* pMD, LoaderAl UnexpectedPrecodeType("Precode::Init", t); break; } -#endif _ASSERTE(IsValidType(GetType())); } @@ -571,8 +564,6 @@ void StubPrecode::StaticInitialize() } #undef ENUM_PAGE_SIZE -#elif defined(TARGET_WASM) - // StubPrecode is not implemented on WASM #else _ASSERTE((SIZE_T)((BYTE*)StubPrecodeCode_End - (BYTE*)StubPrecodeCode) <= StubPrecode::CodeSize); #endif @@ -587,8 +578,6 @@ void StubPrecode::StaticInitialize() void StubPrecode::GenerateCodePage(uint8_t* pageBase, uint8_t* pageBaseRX, size_t pageSize) { -#ifndef TARGET_WASM - int totalCodeSize = (int)(pageSize / StubPrecode::CodeSize) * StubPrecode::CodeSize; #ifdef TARGET_X86 for (int i = 0; i < totalCodeSize; i += StubPrecode::CodeSize) { @@ -610,7 +599,6 @@ void StubPrecode::GenerateCodePage(uint8_t* pageBase, uint8_t* pageBaseRX, size_ _ASSERTE(StubPrecode::IsStubPrecodeByASM_DAC((PCODE)(pageBaseRX + i))); } #endif // _DEBUG -#endif // TARGET_WASM } BOOL StubPrecode::IsStubPrecodeByASM(PCODE addr) @@ -725,8 +713,6 @@ void FixupPrecode::StaticInitialize() // This should fail if the template is used on a platform which doesn't support the supported page size for templates ThrowHR(COR_E_EXECUTIONENGINE); } -#elif defined(TARGET_WASM) - // FixupPrecode is not implemented on WASM #else _ASSERTE((SIZE_T)((BYTE*)FixupPrecodeCode_End - (BYTE*)FixupPrecodeCode) <= FixupPrecode::CodeSize); #endif @@ -740,7 +726,6 @@ void FixupPrecode::StaticInitialize() void FixupPrecode::GenerateDataPage(uint8_t* pageBase, size_t pageSize) { -#ifndef TARGET_WASM // Fill in the data page such that the target of the fixup precode starts as initialized to point // to the start of the precode itself, so that before the memory for the precode is initialized, // the precode is in a state where it will loop forever. @@ -759,13 +744,10 @@ void FixupPrecode::GenerateDataPage(uint8_t* pageBase, size_t pageSize) PCODE* ppTargetSlot = (PCODE*)(pageBase + i + offsetof(FixupPrecodeData, Target)); *ppTargetSlot = ((Precode*)(pageBase - pageSize + i))->GetEntryPoint(); } -#endif // !TARGET_WASM } void FixupPrecode::GenerateCodePage(uint8_t* pageBase, uint8_t* pageBaseRX, size_t pageSize) { -#ifndef TARGET_WASM - int totalCodeSize = (int)((pageSize / FixupPrecode::CodeSize) * FixupPrecode::CodeSize); #ifdef TARGET_X86 for (int i = 0; i < totalCodeSize; i += FixupPrecode::CodeSize) @@ -790,7 +772,6 @@ void FixupPrecode::GenerateCodePage(uint8_t* pageBase, uint8_t* pageBaseRX, size _ASSERTE(FixupPrecode::IsFixupPrecodeByASM_DAC((PCODE)(pageBaseRX + i))); } #endif // _DEBUG -#endif // !TARGET_WASM } BOOL FixupPrecode::IsFixupPrecodeByASM(PCODE addr) @@ -967,3 +948,5 @@ BOOL StubPrecode::IsStubPrecodeByASM(PCODE addr) return TRUE; } + +#endif // !FEATURE_PORTABLE_ENTRYPOINTS diff --git a/src/coreclr/vm/precode.h b/src/coreclr/vm/precode.h index a42539a264edf7..72984e1d7e6b93 100644 --- a/src/coreclr/vm/precode.h +++ b/src/coreclr/vm/precode.h @@ -9,6 +9,12 @@ #ifndef __PRECODE_H__ #define __PRECODE_H__ +#ifdef FEATURE_PORTABLE_ENTRYPOINTS + +#include "precode_portable.hpp" + +#else // !FEATURE_PORTABLE_ENTRYPOINTS + #define PRECODE_ALIGNMENT sizeof(void*) #if defined(TARGET_AMD64) @@ -17,8 +23,6 @@ #elif defined(TARGET_X86) -EXTERN_C VOID STDCALL PrecodeRemotingThunk(); - #define SIZEOF_PRECODE_BASE 8 #elif defined(TARGET_ARM64) @@ -37,12 +41,6 @@ EXTERN_C VOID STDCALL PrecodeRemotingThunk(); #define SIZEOF_PRECODE_BASE CODE_SIZE_ALIGN -#elif defined(TARGET_WASM) - -// on wasm we have "fake" precode, with precode type and MethodDesc information stored -#define SIZEOF_PRECODE_BASE 2*sizeof(void*) -#define OFFSETOF_PRECODE_TYPE 0 -#define OFFSETOF_PRECODE_MD 4 #endif // TARGET_AMD64 #ifndef DACCESS_COMPILE @@ -106,8 +104,6 @@ struct StubPrecode static const SIZE_T CodeSize = 24; #elif defined(TARGET_RISCV64) static const SIZE_T CodeSize = 24; -#elif defined(TARGET_WASM) - static const SIZE_T CodeSize = 3*sizeof(void*); #endif // TARGET_AMD64 BYTE m_code[CodeSize]; @@ -399,9 +395,6 @@ struct FixupPrecode #elif defined(TARGET_RISCV64) static const SIZE_T CodeSize = 32; static const int FixupCodeOffset = 10; -#elif defined(TARGET_WASM) - static const SIZE_T CodeSize = 2*sizeof(void*); - static const int FixupCodeOffset = 0; #endif // TARGET_AMD64 BYTE m_code[CodeSize]; @@ -548,10 +541,6 @@ inline BYTE StubPrecode::GetType() LIMITED_METHOD_DAC_CONTRACT; TADDR type = GetData()->Type; -#ifdef TARGET_WASM - return (BYTE)type; -#endif - // There are a limited number of valid bit patterns here. Restrict to those, so that the // speculative variant of GetPrecodeFromEntryPoint is more robust. Type is stored as a TADDR // so that a single byte matching is not enough to cause a false match. @@ -649,9 +638,7 @@ class Precode { { LIMITED_METHOD_CONTRACT; SUPPORTS_DAC; -#ifdef TARGET_WASM // WASM-TODO: we will not need this once we have real precode on Wasm - return (PrecodeType)m_data[OFFSETOF_PRECODE_TYPE]; -#endif + PrecodeType basicPrecodeType = PRECODE_INVALID; if (StubPrecode::IsStubPrecodeByASM(PINSTRToPCODE(dac_cast(this)))) { @@ -768,10 +755,6 @@ class Precode { fSpeculative = TRUE; #endif -#ifdef TARGET_WASM // WASM-TODO: we will not need this once we have real precode on Wasm - return (PTR_Precode)addr; -#endif - TADDR pInstr = PCODEToPINSTR(addr); // Always do consistency check in debug @@ -883,4 +866,6 @@ extern InterleavedLoaderHeapConfig s_stubPrecodeHeapConfig; extern InterleavedLoaderHeapConfig s_fixupStubPrecodeHeapConfig; #endif +#endif // FEATURE_PORTABLE_ENTRYPOINTS + #endif // __PRECODE_H__ diff --git a/src/coreclr/vm/precode_portable.cpp b/src/coreclr/vm/precode_portable.cpp new file mode 100644 index 00000000000000..97c410cfd46592 --- /dev/null +++ b/src/coreclr/vm/precode_portable.cpp @@ -0,0 +1,84 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// + +#ifdef FEATURE_PORTABLE_ENTRYPOINTS + +#include "common.h" +#include "precode_portable.hpp" + +#ifdef HOST_64BIT + #define CANARY_VALUE 0x1234567812345678 +#else // HOST_64BIT + #define CANARY_VALUE 0x12345678 +#endif // HOST_64BIT + +bool PortableEntryPoint::IsNativeEntryPoint(void* addr) +{ + STANDARD_VM_CONTRACT; + + return false; +} + +MethodDesc* PortableEntryPoint::GetMethodDesc(void* addr) +{ + STANDARD_VM_CONTRACT; + + PortableEntryPoint* portableEntryPoint = ToPortableEntryPoint(addr); + _ASSERTE(portableEntryPoint->_pMD != NULL); + return portableEntryPoint->_pMD; +} + +void* PortableEntryPoint::GetInterpreterData(void* addr) +{ + STANDARD_VM_CONTRACT; + + PortableEntryPoint* portableEntryPoint = ToPortableEntryPoint(addr); + _ASSERTE(portableEntryPoint->_pInterpreterData != NULL); + return portableEntryPoint->_pInterpreterData; +} + +void PortableEntryPoint::SetInterpreterData(void* addr, PCODE interpreterData) +{ + STANDARD_VM_CONTRACT; + + PortableEntryPoint* portableEntryPoint = ToPortableEntryPoint(addr); + _ASSERTE(portableEntryPoint->_pInterpreterData == NULL); + portableEntryPoint->_pInterpreterData = (void*)PCODEToPINSTR(interpreterData); +} + +PortableEntryPoint* PortableEntryPoint::ToPortableEntryPoint(void* addr) +{ + LIMITED_METHOD_CONTRACT; + _ASSERTE(addr != NULL); + + PortableEntryPoint* portableEntryPoint = (PortableEntryPoint*)addr; + _ASSERTE(portableEntryPoint->_canary == CANARY_VALUE); + return portableEntryPoint; +} + +void PortableEntryPoint::Init(MethodDesc* pMD) +{ + LIMITED_METHOD_CONTRACT; + INDEBUG(_canary = CANARY_VALUE); + _pMD = pMD; + _pInterpreterData = NULL; + _pActualCode = NULL; +} + +void FlushCacheForDynamicMappedStub(void* code, SIZE_T size) +{ + +} + +BOOL DoesSlotCallPrestub(PCODE pCode) +{ + return FALSE; +} + +InterleavedLoaderHeapConfig s_stubPrecodeHeapConfig; +#ifdef HAS_FIXUP_PRECODE +InterleavedLoaderHeapConfig s_fixupStubPrecodeHeapConfig; +#endif + +#endif // FEATURE_PORTABLE_ENTRYPOINTS diff --git a/src/coreclr/vm/precode_portable.hpp b/src/coreclr/vm/precode_portable.hpp new file mode 100644 index 00000000000000..501e594a042df5 --- /dev/null +++ b/src/coreclr/vm/precode_portable.hpp @@ -0,0 +1,134 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// + +#ifndef __PRECODE_PORTABLE_H__ +#define __PRECODE_PORTABLE_H__ + +#ifndef FEATURE_PORTABLE_ENTRYPOINTS +#error Requires FEATURE_PORTABLE_ENTRYPOINTS to be set +#endif // !FEATURE_PORTABLE_ENTRYPOINTS + +class PortableEntryPoint final +{ +public: // static + static bool IsNativeEntryPoint(void* addr); + static MethodDesc* GetMethodDesc(void* addr); + static void* GetInterpreterData(void* addr); + static void SetInterpreterData(void* addr, PCODE interpreterData); + +private: // static + static PortableEntryPoint* ToPortableEntryPoint(void* addr); + +private: + INDEBUG(size_t _canary); + void* _pActualCode; + MethodDesc* _pMD; + void* _pInterpreterData; + +public: + void Init(MethodDesc* pMD); +}; + +extern InterleavedLoaderHeapConfig s_stubPrecodeHeapConfig; +#ifdef HAS_FIXUP_PRECODE +extern InterleavedLoaderHeapConfig s_fixupStubPrecodeHeapConfig; +#endif + +enum PrecodeType +{ + PRECODE_INVALID = -100, + PRECODE_STUB, + PRECODE_UMENTRY_THUNK, + PRECODE_FIXUP, + PRECODE_PINVOKE_IMPORT, + PRECODE_THISPTR_RETBUF, +}; + +class StubPrecode +{ +public: // static + static const BYTE Type = PRECODE_STUB; + + static void StaticInitialize() { } + +public: + void Init(StubPrecode* pPrecodeRX, TADDR secretParam, LoaderAllocator *pLoaderAllocator = NULL, TADDR type = StubPrecode::Type, TADDR target = 0) { } + + void SetTargetUnconditional(TADDR target) { } + + TADDR GetSecretParam() const { return (TADDR)NULL; } + + MethodDesc* GetMethodDesc() { return NULL; } +}; + +class FixupPrecode final +{ +public: // static + static const int FixupCodeOffset = 0; + + static void StaticInitialize() { } + +public: + PCODE* GetTargetSlot() { return NULL; } + + MethodDesc* GetMethodDesc() { return NULL; } +}; + +class PInvokeImportPrecode final : StubPrecode +{ +public: + LPVOID GetEntrypoint() { return NULL; } +}; + +class UMEntryThunk; + +class Precode +{ +public: // static + static Precode* Allocate(PrecodeType t, MethodDesc* pMD, + LoaderAllocator *pLoaderAllocator, AllocMemTracker *pamTracker) + { + return NULL; + } + + static Precode* GetPrecodeFromEntryPoint(PCODE addr, BOOL fSpeculative = FALSE) + { + return NULL; + } + +public: + PrecodeType GetType() { return (PrecodeType)0; } + + UMEntryThunk* AsUMEntryThunk() { return NULL; } + + PInvokeImportPrecode* AsPInvokeImportPrecode() { return NULL; } + + MethodDesc* GetMethodDesc(BOOL fSpeculative = FALSE) { return NULL; } + + PCODE GetEntryPoint() { return (PCODE)NULL; } + + BOOL IsPointingToNativeCode(PCODE pNativeCode) { return FALSE; } + + void Reset() { } + + PCODE GetTarget() { return (PCODE)NULL; } + + void ResetTargetInterlocked() { } + + BOOL SetTargetInterlocked(PCODE target, BOOL fOnlyRedirectFromPrestub = TRUE) { return FALSE; } + + BOOL IsPointingToPrestub() { return FALSE; } + + BOOL IsPointingToPrestub(PCODE target) { return FALSE; } +}; + +void FlushCacheForDynamicMappedStub(void* code, SIZE_T size); +BOOL DoesSlotCallPrestub(PCODE pCode); + +struct PrecodeMachineDescriptor +{ + static void Init(PrecodeMachineDescriptor* dest) { } +}; + +#endif // __PRECODE_PORTABLE_H__ diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index 8ce695d3d50410..a08d73ac880df4 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -994,8 +994,16 @@ PCODE MethodDesc::JitCompileCodeLocked(PrepareCodeConfig* pConfig, COR_ILMETHOD_ #ifdef FEATURE_INTERPRETER if (*pIsInterpreterCode) { + InterpByteCodeStart* interpreterCode; +#ifdef FEATURE_PORTABLE_ENTRYPOINTS + interpreterCode = (InterpByteCodeStart*)PortableEntryPoint::GetInterpreterData((void*)PCODEToPINSTR(pCode)); + + +#else // !FEATURE_PORTABLE_ENTRYPOINTS InterpreterPrecode* pPrecode = InterpreterPrecode::FromEntryPoint(pCode); - InterpByteCodeStart* interpreterCode = dac_cast(pPrecode->GetData()->ByteCodeAddr); + interpreterCode = dac_cast(pPrecode->GetData()->ByteCodeAddr); +#endif // FEATURE_PORTABLE_ENTRYPOINTS + pConfig->GetMethodDesc()->SetInterpreterCode(interpreterCode); } #endif // FEATURE_INTERPRETER From 0df52d004605b439a278081c2f0d4bbc9ea776c9 Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Wed, 27 Aug 2025 22:47:35 -0700 Subject: [PATCH 03/15] Address updated to get thread on WASM. Change signatures to TADDR for PortableEntryPoint. Fix up NonVirtualEntry2MethodDesc. --- src/coreclr/vm/interpexec.cpp | 4 ++-- src/coreclr/vm/jitinterface.cpp | 2 +- src/coreclr/vm/method.cpp | 12 +++++++++--- src/coreclr/vm/precode_portable.cpp | 10 +++++----- src/coreclr/vm/precode_portable.hpp | 21 ++++++++++++++++----- src/coreclr/vm/prestub.cpp | 3 +-- src/coreclr/vm/util.hpp | 15 +++++++-------- 7 files changed, 41 insertions(+), 26 deletions(-) diff --git a/src/coreclr/vm/interpexec.cpp b/src/coreclr/vm/interpexec.cpp index 57ae30d32866e1..7376ac004513c6 100644 --- a/src/coreclr/vm/interpexec.cpp +++ b/src/coreclr/vm/interpexec.cpp @@ -254,9 +254,9 @@ template static THelper GetPossiblyIndirectHelper(const Inter } #ifdef FEATURE_PORTABLE_ENTRYPOINTS - if (!PortableEntryPoint::IsNativeEntryPoint(addr)) + if (!PortableEntryPoint::IsNativeEntryPoint((TADDR)addr)) { - *pILTargetMethod = PortableEntryPoint::GetMethodDesc(addr); + *pILTargetMethod = PortableEntryPoint::GetMethodDesc((TADDR)addr); return NULL; // Return null to interpret this entrypoint } #endif // FEATURE_PORTABLE_ENTRYPOINTS diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 0775ce797a5667..e04e6e4815680b 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -13354,7 +13354,7 @@ PCODE UnsafeJitFunction(PrepareCodeConfig* config, #ifdef FEATURE_PORTABLE_ENTRYPOINTS PCODE portableEntryPoint = ftn->GetTemporaryEntryPoint(); _ASSERTE(portableEntryPoint != NULL); - PortableEntryPoint::SetInterpreterData((void*)PCODEToPINSTR(portableEntryPoint), ret); + PortableEntryPoint::SetInterpreterData(PCODEToPINSTR(portableEntryPoint), ret); ret = portableEntryPoint; #else // !FEATURE_PORTABLE_ENTRYPOINTS diff --git a/src/coreclr/vm/method.cpp b/src/coreclr/vm/method.cpp index 0dccc4ce795074..9de10c80a7d8aa 100644 --- a/src/coreclr/vm/method.cpp +++ b/src/coreclr/vm/method.cpp @@ -2202,13 +2202,18 @@ PCODE MethodDesc::GetCallTarget(OBJECTREF* pThisObj, TypeHandle ownerType) MethodDesc* NonVirtualEntry2MethodDesc(PCODE entryPoint) { - CONTRACTL { + CONTRACTL + { NOTHROW; GC_NOTRIGGER; MODE_ANY; } CONTRACTL_END +#ifdef FEATURE_PORTABLE_ENTRYPOINTS + return PortableEntryPoint::GetMethodDesc(PCODEToPINSTR(entryPoint)); + +#else // FEATURE_PORTABLE_ENTRYPOINTS RangeSection* pRS = ExecutionManager::FindCodeRange(entryPoint, ExecutionManager::GetScanFlags()); if (pRS == NULL) { @@ -2242,6 +2247,7 @@ MethodDesc* NonVirtualEntry2MethodDesc(PCODE entryPoint) // We should never get here _ASSERTE(!"NonVirtualEntry2MethodDesc failed"); return NULL; +#endif // FEATURE_PORTABLE_ENTRYPOINTS } #ifndef DACCESS_COMPILE @@ -2637,7 +2643,7 @@ MethodDesc* MethodDesc::GetMethodDescFromPrecode(PCODE addr, BOOL fSpeculative / MethodDesc* pMD = NULL; #ifdef FEATURE_PORTABLE_ENTRYPOINTS - pMD = PortableEntryPoint::GetMethodDesc((void*)PCODEToPINSTR(addr)); + pMD = PortableEntryPoint::GetMethodDesc(PCODEToPINSTR(addr)); #else // !FEATURE_PORTABLE_ENTRYPOINTS PTR_Precode pPrecode = Precode::GetPrecodeFromEntryPoint(addr, fSpeculative); @@ -2864,7 +2870,7 @@ void MethodDesc::MarkPrecodeAsStableEntrypoint() PCODE tempEntry = GetTemporaryEntryPointIfExists(); _ASSERTE(tempEntry != (PCODE)NULL); #ifdef FEATURE_PORTABLE_ENTRYPOINTS - _ASSERTE(PortableEntryPoint::GetMethodDesc((void*)PCODEToPINSTR(tempEntry)) == this); + _ASSERTE(PortableEntryPoint::GetMethodDesc(PCODEToPINSTR(tempEntry)) == this); #else // !FEATURE_PORTABLE_ENTRYPOINTS PrecodeType requiredType = GetPrecodeType(); PrecodeType availableType = Precode::GetPrecodeFromEntryPoint(tempEntry)->GetType(); diff --git a/src/coreclr/vm/precode_portable.cpp b/src/coreclr/vm/precode_portable.cpp index 97c410cfd46592..98883727856cc9 100644 --- a/src/coreclr/vm/precode_portable.cpp +++ b/src/coreclr/vm/precode_portable.cpp @@ -13,14 +13,14 @@ #define CANARY_VALUE 0x12345678 #endif // HOST_64BIT -bool PortableEntryPoint::IsNativeEntryPoint(void* addr) +bool PortableEntryPoint::IsNativeEntryPoint(TADDR addr) { STANDARD_VM_CONTRACT; return false; } -MethodDesc* PortableEntryPoint::GetMethodDesc(void* addr) +MethodDesc* PortableEntryPoint::GetMethodDesc(TADDR addr) { STANDARD_VM_CONTRACT; @@ -29,7 +29,7 @@ MethodDesc* PortableEntryPoint::GetMethodDesc(void* addr) return portableEntryPoint->_pMD; } -void* PortableEntryPoint::GetInterpreterData(void* addr) +void* PortableEntryPoint::GetInterpreterData(TADDR addr) { STANDARD_VM_CONTRACT; @@ -38,7 +38,7 @@ void* PortableEntryPoint::GetInterpreterData(void* addr) return portableEntryPoint->_pInterpreterData; } -void PortableEntryPoint::SetInterpreterData(void* addr, PCODE interpreterData) +void PortableEntryPoint::SetInterpreterData(TADDR addr, PCODE interpreterData) { STANDARD_VM_CONTRACT; @@ -47,7 +47,7 @@ void PortableEntryPoint::SetInterpreterData(void* addr, PCODE interpreterData) portableEntryPoint->_pInterpreterData = (void*)PCODEToPINSTR(interpreterData); } -PortableEntryPoint* PortableEntryPoint::ToPortableEntryPoint(void* addr) +PortableEntryPoint* PortableEntryPoint::ToPortableEntryPoint(TADDR addr) { LIMITED_METHOD_CONTRACT; _ASSERTE(addr != NULL); diff --git a/src/coreclr/vm/precode_portable.hpp b/src/coreclr/vm/precode_portable.hpp index 501e594a042df5..e5d3ede8a6c12e 100644 --- a/src/coreclr/vm/precode_portable.hpp +++ b/src/coreclr/vm/precode_portable.hpp @@ -12,13 +12,13 @@ class PortableEntryPoint final { public: // static - static bool IsNativeEntryPoint(void* addr); - static MethodDesc* GetMethodDesc(void* addr); - static void* GetInterpreterData(void* addr); - static void SetInterpreterData(void* addr, PCODE interpreterData); + static bool IsNativeEntryPoint(TADDR addr); + static MethodDesc* GetMethodDesc(TADDR addr); + static void* GetInterpreterData(TADDR addr); + static void SetInterpreterData(TADDR addr, PCODE interpreterData); private: // static - static PortableEntryPoint* ToPortableEntryPoint(void* addr); + static PortableEntryPoint* ToPortableEntryPoint(TADDR addr); private: INDEBUG(size_t _canary); @@ -43,6 +43,7 @@ enum PrecodeType PRECODE_FIXUP, PRECODE_PINVOKE_IMPORT, PRECODE_THISPTR_RETBUF, + PRECODE_INTERPRETER, }; class StubPrecode @@ -55,6 +56,8 @@ class StubPrecode public: void Init(StubPrecode* pPrecodeRX, TADDR secretParam, LoaderAllocator *pLoaderAllocator = NULL, TADDR type = StubPrecode::Type, TADDR target = 0) { } + BYTE GetType() { return 0; } + void SetTargetUnconditional(TADDR target) { } TADDR GetSecretParam() const { return (TADDR)NULL; } @@ -62,6 +65,8 @@ class StubPrecode MethodDesc* GetMethodDesc() { return NULL; } }; +typedef DPTR(StubPrecode) PTR_StubPrecode; + class FixupPrecode final { public: // static @@ -104,6 +109,12 @@ class Precode PInvokeImportPrecode* AsPInvokeImportPrecode() { return NULL; } + StubPrecode* AsStubPrecode() { return NULL; } + +#ifdef HAS_FIXUP_PRECODE + FixupPrecode* AsFixupPrecode() { return NULL; } +#endif // HAS_FIXUP_PRECODE + MethodDesc* GetMethodDesc(BOOL fSpeculative = FALSE) { return NULL; } PCODE GetEntryPoint() { return (PCODE)NULL; } diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index a08d73ac880df4..0ef2ed8ff2fb29 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -996,8 +996,7 @@ PCODE MethodDesc::JitCompileCodeLocked(PrepareCodeConfig* pConfig, COR_ILMETHOD_ { InterpByteCodeStart* interpreterCode; #ifdef FEATURE_PORTABLE_ENTRYPOINTS - interpreterCode = (InterpByteCodeStart*)PortableEntryPoint::GetInterpreterData((void*)PCODEToPINSTR(pCode)); - + interpreterCode = (InterpByteCodeStart*)PortableEntryPoint::GetInterpreterData(PCODEToPINSTR(pCode)); #else // !FEATURE_PORTABLE_ENTRYPOINTS InterpreterPrecode* pPrecode = InterpreterPrecode::FromEntryPoint(pCode); diff --git a/src/coreclr/vm/util.hpp b/src/coreclr/vm/util.hpp index 61a948dfb6e232..a05526fadcf4de 100644 --- a/src/coreclr/vm/util.hpp +++ b/src/coreclr/vm/util.hpp @@ -427,11 +427,11 @@ extern LockOwner g_lockTrustMeIAmThreadSafe; class EEThreadId { private: - void *m_FiberPtrId; + SIZE_T m_FiberPtrId; public: #ifdef _DEBUG - EEThreadId() - : m_FiberPtrId(NULL) + static SIZE_T const UNKNOWN_ID = INVALID_POINTER_CD; + EEThreadId() : m_FiberPtrId(UNKNOWN_ID) { LIMITED_METHOD_CONTRACT; } @@ -441,28 +441,27 @@ class EEThreadId { WRAPPER_NO_CONTRACT; - m_FiberPtrId = ClrTeb::GetFiberPtrId(); + m_FiberPtrId = (SIZE_T)ClrTeb::GetFiberPtrId(); } bool IsCurrentThread() const { WRAPPER_NO_CONTRACT; - return (m_FiberPtrId == ClrTeb::GetFiberPtrId()); + return (m_FiberPtrId == (SIZE_T)ClrTeb::GetFiberPtrId()); } - #ifdef _DEBUG bool IsUnknown() const { LIMITED_METHOD_CONTRACT; - return m_FiberPtrId == NULL; + return m_FiberPtrId == UNKNOWN_ID; } #endif void Clear() { LIMITED_METHOD_CONTRACT; - m_FiberPtrId = NULL; + m_FiberPtrId = UNKNOWN_ID; } }; From 50a72879ffb281f1fac6165fc3c6c5edd5ffa8b0 Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Thu, 28 Aug 2025 09:10:01 -0700 Subject: [PATCH 04/15] Clean-up --- src/coreclr/vm/cdacplatformmetadata.cpp | 2 + src/coreclr/vm/ceemain.cpp | 2 + src/coreclr/vm/loaderallocator.cpp | 2 + src/coreclr/vm/method.hpp | 6 +- src/coreclr/vm/methodtablebuilder.cpp | 4 +- src/coreclr/vm/precode_portable.cpp | 136 +++++++++++++++++++++++- src/coreclr/vm/precode_portable.hpp | 63 ++++------- src/coreclr/vm/wasm/cgencpu.h | 6 -- 8 files changed, 164 insertions(+), 57 deletions(-) diff --git a/src/coreclr/vm/cdacplatformmetadata.cpp b/src/coreclr/vm/cdacplatformmetadata.cpp index e616d10c82afde..2434315a506dd7 100644 --- a/src/coreclr/vm/cdacplatformmetadata.cpp +++ b/src/coreclr/vm/cdacplatformmetadata.cpp @@ -20,7 +20,9 @@ void CDacPlatformMetadata::Init() void CDacPlatformMetadata::InitPrecodes() { +#ifndef FEATURE_PORTABLE_ENTRYPOINTS PrecodeMachineDescriptor::Init(&(&g_cdacPlatformMetadata)->precode); +#endif // !FEATURE_PORTABLE_ENTRYPOINTS } #endif // !DACCESS_COMPILE diff --git a/src/coreclr/vm/ceemain.cpp b/src/coreclr/vm/ceemain.cpp index 4be75dc9158969..a2b8ac7b858289 100644 --- a/src/coreclr/vm/ceemain.cpp +++ b/src/coreclr/vm/ceemain.cpp @@ -804,9 +804,11 @@ void EEStartupHelper() CoreLibBinder::Startup(); StubLinkerCPU::Init(); +#ifndef FEATURE_PORTABLE_ENTRYPOINTS StubPrecode::StaticInitialize(); FixupPrecode::StaticInitialize(); CDacPlatformMetadata::InitPrecodes(); +#endif // !FEATURE_PORTABLE_ENTRYPOINTS InitializeGarbageCollector(); diff --git a/src/coreclr/vm/loaderallocator.cpp b/src/coreclr/vm/loaderallocator.cpp index 58740770b2b644..e2d779d0e404cd 100644 --- a/src/coreclr/vm/loaderallocator.cpp +++ b/src/coreclr/vm/loaderallocator.cpp @@ -1219,9 +1219,11 @@ void LoaderAllocator::Init(BYTE *pExecutableHeapMemory) &s_stubPrecodeHeapConfig); #endif // defined(FEATURE_STUBPRECODE_DYNAMIC_HELPERS) && defined(FEATURE_READYTORUN) +#ifdef HAS_FIXUP_PRECODE m_pFixupPrecodeHeap = new (&m_FixupPrecodeHeapInstance) InterleavedLoaderHeap(&m_fixupPrecodeRangeList, false /* fUnlocked */, &s_fixupStubPrecodeHeapConfig); +#endif // HAS_FIXUP_PRECODE // Initialize the EE marshaling data to NULL. m_pMarshalingData = NULL; diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp index d94806440e5681..1cb19ffb6eec54 100644 --- a/src/coreclr/vm/method.hpp +++ b/src/coreclr/vm/method.hpp @@ -3227,7 +3227,11 @@ class PInvokeMethodDesc : public MethodDesc { LIMITED_METHOD_DAC_CONTRACT; +#ifdef HAS_PINVOKE_IMPORT_PRECODE return m_pImportThunkGlue; +#else + return &m_ImportThunkGlue; +#endif // HAS_PINVOKE_IMPORT_PRECODE } LPVOID GetPInvokeTarget() @@ -3247,7 +3251,7 @@ class PInvokeMethodDesc : public MethodDesc _ASSERTE(IsPInvoke()); - return (GetPInvokeTarget() == GetPInvokeImportThunkGlue()->GetEntrypoint()); + return (GetPInvokeTarget() == GetPInvokeImportThunkGlue()->GetEntryPoint()); } #endif // !DACCESS_COMPILE diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp index 1e8b5c87893c2c..f6b43b82d9d245 100644 --- a/src/coreclr/vm/methodtablebuilder.cpp +++ b/src/coreclr/vm/methodtablebuilder.cpp @@ -4311,7 +4311,7 @@ VOID MethodTableBuilder::InitializeFieldDescs(FieldDesc *pFieldDescList, fIsByValue = FALSE; // we're going to treat it as the underlying type now goto GOT_ELEMENT_TYPE; } - + // #FieldDescTypeMorph IF it is an enum, strip it down to its underlying type if (!fIsStatic && pByValueClass->IsEnum()) { @@ -6169,7 +6169,7 @@ MethodTableBuilder::InitMethodDesc( pNewNMD->SetIsEarlyBound(); } - pNewNMD->m_pPInvokeTarget = pNewNMD->GetPInvokeImportThunkGlue()->GetEntrypoint(); + pNewNMD->m_pPInvokeTarget = pNewNMD->GetPInvokeImportThunkGlue()->GetEntryPoint(); } break; diff --git a/src/coreclr/vm/precode_portable.cpp b/src/coreclr/vm/precode_portable.cpp index 98883727856cc9..b727d26c10fdf9 100644 --- a/src/coreclr/vm/precode_portable.cpp +++ b/src/coreclr/vm/precode_portable.cpp @@ -66,6 +66,137 @@ void PortableEntryPoint::Init(MethodDesc* pMD) _pActualCode = NULL; } +InterleavedLoaderHeapConfig s_stubPrecodeHeapConfig; + +void StubPrecode::Init(StubPrecode* pPrecodeRX, TADDR secretParam, LoaderAllocator *pLoaderAllocator, TADDR type, TADDR target) +{ + LIMITED_METHOD_CONTRACT; + _ASSERTE(!"StubPrecode::Init is not supported with Portable EntryPoints"); +} + +BYTE StubPrecode::GetType() +{ + LIMITED_METHOD_CONTRACT; + _ASSERTE(!"StubPrecode::GetType is not supported with Portable EntryPoints"); + return 0; +} + +void StubPrecode::SetTargetUnconditional(TADDR target) +{ + LIMITED_METHOD_CONTRACT; + _ASSERTE(!"StubPrecode::SetTargetUnconditional is not supported with Portable EntryPoints"); +} + +TADDR StubPrecode::GetSecretParam() const +{ + LIMITED_METHOD_CONTRACT; + _ASSERTE(!"StubPrecode::GetSecretParam is not supported with Portable EntryPoints"); + return (TADDR)NULL; +} + +MethodDesc* StubPrecode::GetMethodDesc() +{ + LIMITED_METHOD_CONTRACT; + _ASSERTE(!"StubPrecode::GetMethodDesc is not supported with Portable EntryPoints"); + return NULL; +} + +PCODE* FixupPrecode::GetTargetSlot() +{ + LIMITED_METHOD_CONTRACT; + _ASSERTE(!"FixupPrecode::GetTargetSlot is not supported with Portable EntryPoints"); + return NULL; +} + +MethodDesc* FixupPrecode::GetMethodDesc() +{ + LIMITED_METHOD_CONTRACT; + _ASSERTE(!"FixupPrecode::GetMethodDesc is not supported with Portable EntryPoints"); + return NULL; +} + +PrecodeType Precode::GetType() +{ + LIMITED_METHOD_CONTRACT; + _ASSERTE(!"Precode::GetType is not supported with Portable EntryPoints"); + return (PrecodeType)0; +} + +UMEntryThunk* Precode::AsUMEntryThunk() +{ + LIMITED_METHOD_CONTRACT; + _ASSERTE(!"Precode::AsUMEntryThunk is not supported with Portable EntryPoints"); + return NULL; +} + +StubPrecode* Precode::AsStubPrecode() +{ + LIMITED_METHOD_CONTRACT; + _ASSERTE(!"Precode::AsStubPrecode is not supported with Portable EntryPoints"); + return NULL; +} + +MethodDesc* Precode::GetMethodDesc(BOOL fSpeculative) +{ + LIMITED_METHOD_CONTRACT; + _ASSERTE(!"Precode::GetMethodDesc is not supported with Portable EntryPoints"); + return NULL; +} + +PCODE Precode::GetEntryPoint() +{ + LIMITED_METHOD_CONTRACT; + _ASSERTE(!"Precode::GetEntryPoint is not supported with Portable EntryPoints"); + return (PCODE)NULL; +} + +BOOL Precode::IsPointingToNativeCode(PCODE pNativeCode) +{ + LIMITED_METHOD_CONTRACT; + _ASSERTE(!"Precode::IsPointingToNativeCode is not supported with Portable EntryPoints"); + return FALSE; +} + +void Precode::Reset() +{ + LIMITED_METHOD_CONTRACT; + _ASSERTE(!"Precode::Reset is not supported with Portable EntryPoints"); +} + +PCODE Precode::GetTarget() +{ + LIMITED_METHOD_CONTRACT; + _ASSERTE(!"Precode::GetTarget is not supported with Portable EntryPoints"); + return (PCODE)NULL; +} + +void Precode::ResetTargetInterlocked() +{ + LIMITED_METHOD_CONTRACT; + _ASSERTE(!"Precode::ResetTargetInterlocked is not supported with Portable EntryPoints"); +} + +BOOL Precode::SetTargetInterlocked(PCODE target, BOOL fOnlyRedirectFromPrestub) +{ + LIMITED_METHOD_CONTRACT; + _ASSERTE(!"Precode::SetTargetInterlocked is not supported with Portable EntryPoints"); + return FALSE; +} + +BOOL Precode::IsPointingToPrestub() +{ + LIMITED_METHOD_CONTRACT; + _ASSERTE(!"Precode::IsPointingToPrestub is not supported with Portable EntryPoints"); + return FALSE; +} + +BOOL Precode::IsPointingToPrestub(PCODE target) +{ + LIMITED_METHOD_CONTRACT; + _ASSERTE(!"Precode::IsPointingToPrestub is not supported with Portable EntryPoints"); + return FALSE; +} + void FlushCacheForDynamicMappedStub(void* code, SIZE_T size) { @@ -76,9 +207,4 @@ BOOL DoesSlotCallPrestub(PCODE pCode) return FALSE; } -InterleavedLoaderHeapConfig s_stubPrecodeHeapConfig; -#ifdef HAS_FIXUP_PRECODE -InterleavedLoaderHeapConfig s_fixupStubPrecodeHeapConfig; -#endif - #endif // FEATURE_PORTABLE_ENTRYPOINTS diff --git a/src/coreclr/vm/precode_portable.hpp b/src/coreclr/vm/precode_portable.hpp index e5d3ede8a6c12e..ab9571b6835d7b 100644 --- a/src/coreclr/vm/precode_portable.hpp +++ b/src/coreclr/vm/precode_portable.hpp @@ -31,9 +31,6 @@ class PortableEntryPoint final }; extern InterleavedLoaderHeapConfig s_stubPrecodeHeapConfig; -#ifdef HAS_FIXUP_PRECODE -extern InterleavedLoaderHeapConfig s_fixupStubPrecodeHeapConfig; -#endif enum PrecodeType { @@ -42,7 +39,6 @@ enum PrecodeType PRECODE_UMENTRY_THUNK, PRECODE_FIXUP, PRECODE_PINVOKE_IMPORT, - PRECODE_THISPTR_RETBUF, PRECODE_INTERPRETER, }; @@ -51,18 +47,16 @@ class StubPrecode public: // static static const BYTE Type = PRECODE_STUB; - static void StaticInitialize() { } - public: - void Init(StubPrecode* pPrecodeRX, TADDR secretParam, LoaderAllocator *pLoaderAllocator = NULL, TADDR type = StubPrecode::Type, TADDR target = 0) { } + void Init(StubPrecode* pPrecodeRX, TADDR secretParam, LoaderAllocator *pLoaderAllocator = NULL, TADDR type = StubPrecode::Type, TADDR target = 0); - BYTE GetType() { return 0; } + BYTE GetType(); - void SetTargetUnconditional(TADDR target) { } + void SetTargetUnconditional(TADDR target); - TADDR GetSecretParam() const { return (TADDR)NULL; } + TADDR GetSecretParam() const; - MethodDesc* GetMethodDesc() { return NULL; } + MethodDesc* GetMethodDesc(); }; typedef DPTR(StubPrecode) PTR_StubPrecode; @@ -72,18 +66,10 @@ class FixupPrecode final public: // static static const int FixupCodeOffset = 0; - static void StaticInitialize() { } - public: - PCODE* GetTargetSlot() { return NULL; } + PCODE* GetTargetSlot(); - MethodDesc* GetMethodDesc() { return NULL; } -}; - -class PInvokeImportPrecode final : StubPrecode -{ -public: - LPVOID GetEntrypoint() { return NULL; } + MethodDesc* GetMethodDesc(); }; class UMEntryThunk; @@ -103,43 +89,34 @@ class Precode } public: - PrecodeType GetType() { return (PrecodeType)0; } - - UMEntryThunk* AsUMEntryThunk() { return NULL; } - - PInvokeImportPrecode* AsPInvokeImportPrecode() { return NULL; } + PrecodeType GetType(); - StubPrecode* AsStubPrecode() { return NULL; } + UMEntryThunk* AsUMEntryThunk(); -#ifdef HAS_FIXUP_PRECODE - FixupPrecode* AsFixupPrecode() { return NULL; } -#endif // HAS_FIXUP_PRECODE + StubPrecode* AsStubPrecode(); - MethodDesc* GetMethodDesc(BOOL fSpeculative = FALSE) { return NULL; } + MethodDesc* GetMethodDesc(BOOL fSpeculative = FALSE); - PCODE GetEntryPoint() { return (PCODE)NULL; } + PCODE GetEntryPoint(); - BOOL IsPointingToNativeCode(PCODE pNativeCode) { return FALSE; } + BOOL IsPointingToNativeCode(PCODE pNativeCode); - void Reset() { } + void Reset(); - PCODE GetTarget() { return (PCODE)NULL; } + PCODE GetTarget(); - void ResetTargetInterlocked() { } + void ResetTargetInterlocked(); - BOOL SetTargetInterlocked(PCODE target, BOOL fOnlyRedirectFromPrestub = TRUE) { return FALSE; } + BOOL SetTargetInterlocked(PCODE target, BOOL fOnlyRedirectFromPrestub = TRUE); - BOOL IsPointingToPrestub() { return FALSE; } + BOOL IsPointingToPrestub(); - BOOL IsPointingToPrestub(PCODE target) { return FALSE; } + BOOL IsPointingToPrestub(PCODE target); }; void FlushCacheForDynamicMappedStub(void* code, SIZE_T size); BOOL DoesSlotCallPrestub(PCODE pCode); -struct PrecodeMachineDescriptor -{ - static void Init(PrecodeMachineDescriptor* dest) { } -}; +class PrecodeMachineDescriptor { }; #endif // __PRECODE_PORTABLE_H__ diff --git a/src/coreclr/vm/wasm/cgencpu.h b/src/coreclr/vm/wasm/cgencpu.h index 975f892a17f1f1..e36a95297127b6 100644 --- a/src/coreclr/vm/wasm/cgencpu.h +++ b/src/coreclr/vm/wasm/cgencpu.h @@ -14,12 +14,6 @@ #define CODE_SIZE_ALIGN 4 #define LOG2SLOT LOG2_PTRSIZE -// looks like this is mandatory for now -#define HAS_PINVOKE_IMPORT_PRECODE 1 -#define HAS_FIXUP_PRECODE 1 -// ThisPtrRetBufPrecode one is necessary for closed delegates over static methods with return buffer -#define HAS_THISPTR_RETBUF_PRECODE 1 - #define BACK_TO_BACK_JUMP_ALLOCATE_SIZE 8 // # bytes to allocate for a back to back jump instruction //********************************************************************** From 41fa3373167870d28e2070faf0c6f6def17085b1 Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Thu, 28 Aug 2025 09:24:41 -0700 Subject: [PATCH 05/15] Add note to clr-abi for WASM. --- docs/design/coreclr/botr/clr-abi.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/design/coreclr/botr/clr-abi.md b/docs/design/coreclr/botr/clr-abi.md index af53e558712b5c..420f386066f0d6 100644 --- a/docs/design/coreclr/botr/clr-abi.md +++ b/docs/design/coreclr/botr/clr-abi.md @@ -537,6 +537,12 @@ The extra state created by the JIT for synchronized methods (lock taken flag) mu EnC is supported for adding and editing generic methods and methods on generic types and generic methods on non-generic types. +# WASM support + +The WASM target has restrictions that present unique difficulties for a JITed environment. One of those restrictions is the inability to allocate executable code at run-time. + +The Portable Entry Point feature is required for WASM to avoid the need for run-time executable code generation. On less restricted platforms, `Precode`s are used to represent entry points to pre-JITed methods or for function pointers to managed functions. Instead of a small executable block of code, we use the `PortableEntryPoint` type that acts as a container for the target method (for example, `MethodDesc`) and a pointer to the code to interpret. + # System V x86_64 support This section relates mostly to calling conventions on System V systems (such as Ubuntu Linux and Mac OS X). From 6e301220c4c0b16af91a980962e16c2d4035284e Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Thu, 28 Aug 2025 10:14:33 -0700 Subject: [PATCH 06/15] Create TargetsWasm MSBuild property. --- eng/OSArch.props | 4 ++++ src/coreclr/clr.featuredefines.props | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/eng/OSArch.props b/eng/OSArch.props index 22fb8b577fca90..4f30cb2e71d5be 100644 --- a/eng/OSArch.props +++ b/eng/OSArch.props @@ -33,6 +33,10 @@ $(TargetArchitecture) + + true + + <_ImportedOSArchProps>true diff --git a/src/coreclr/clr.featuredefines.props b/src/coreclr/clr.featuredefines.props index efa15c94415c8a..cde73ea5bc3bca 100644 --- a/src/coreclr/clr.featuredefines.props +++ b/src/coreclr/clr.featuredefines.props @@ -11,7 +11,7 @@ true - + true From 61a7721c95193fe6e0c08b29f0a6f241e3ba2b96 Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Thu, 28 Aug 2025 11:22:17 -0700 Subject: [PATCH 07/15] Disable FEATURE_REJIT and FEATURE_TIERED_COMPILATION for wasm. --- src/coreclr/clrdefinitions.cmake | 10 +++++++-- src/coreclr/clrfeatures.cmake | 2 ++ src/coreclr/vm/appdomain.hpp | 4 ---- src/coreclr/vm/ceemain.cpp | 2 ++ src/coreclr/vm/codeversion.cpp | 13 +++++++++-- src/coreclr/vm/codeversion.h | 3 ++- src/coreclr/vm/interpexec.cpp | 1 + src/coreclr/vm/jitinterface.cpp | 4 +++- src/coreclr/vm/method.cpp | 30 +++++++++++++++++++++++++ src/coreclr/vm/method.hpp | 7 ++++++ src/coreclr/vm/precode_portable.cpp | 33 +++++++++++++++++++++++++--- src/coreclr/vm/precode_portable.hpp | 17 ++++++-------- src/coreclr/vm/tieredcompilation.cpp | 16 +++++++++----- src/coreclr/vm/tieredcompilation.h | 5 +++-- 14 files changed, 116 insertions(+), 31 deletions(-) diff --git a/src/coreclr/clrdefinitions.cmake b/src/coreclr/clrdefinitions.cmake index 7ac60959b41e2b..e9e99388c57a74 100644 --- a/src/coreclr/clrdefinitions.cmake +++ b/src/coreclr/clrdefinitions.cmake @@ -161,7 +161,9 @@ add_definitions(-DFEATURE_READYTORUN) set(FEATURE_READYTORUN 1) -add_compile_definitions(FEATURE_REJIT) +if(FEATURE_REJIT) + add_compile_definitions(FEATURE_REJIT) +endif() if (CLR_CMAKE_HOST_UNIX AND CLR_CMAKE_TARGET_UNIX) add_definitions(-DFEATURE_REMOTE_PROC_MEM) @@ -175,7 +177,11 @@ if (NOT CLR_CMAKE_HOST_ANDROID) add_definitions(-DFEATURE_SVR_GC) endif(NOT CLR_CMAKE_HOST_ANDROID) add_definitions(-DFEATURE_SYMDIFF) -add_compile_definitions(FEATURE_TIERED_COMPILATION) + +if (FEATURE_TIERED_COMPILATION) + add_compile_definitions(FEATURE_TIERED_COMPILATION) +endif(FEATURE_TIERED_COMPILATION) + add_compile_definitions(FEATURE_PGO) if (CLR_CMAKE_TARGET_ARCH_AMD64) # Enable the AMD64 Unix struct passing JIT-EE interface for all AMD64 platforms, to enable altjit. diff --git a/src/coreclr/clrfeatures.cmake b/src/coreclr/clrfeatures.cmake index 8a22b34c41415b..1dbc058acc6f03 100644 --- a/src/coreclr/clrfeatures.cmake +++ b/src/coreclr/clrfeatures.cmake @@ -1,5 +1,7 @@ if (NOT CLR_CMAKE_TARGET_ARCH_WASM AND NOT CLR_CMAKE_TARGET_IOS AND NOT CLR_CMAKE_TARGET_TVOS AND NOT CLR_CMAKE_TARGET_MACCATALYST) set(FEATURE_JIT 1) + set(FEATURE_TIERED_COMPILATION 1) + set(FEATURE_REJIT 1) endif() if (CLR_CMAKE_TARGET_ARCH_WASM OR CLR_CMAKE_TARGET_IOS OR CLR_CMAKE_TARGET_TVOS OR CLR_CMAKE_TARGET_MACCATALYST) diff --git a/src/coreclr/vm/appdomain.hpp b/src/coreclr/vm/appdomain.hpp index 8a42c4d7cd213f..1f02ebbaac5a97 100644 --- a/src/coreclr/vm/appdomain.hpp +++ b/src/coreclr/vm/appdomain.hpp @@ -1549,8 +1549,6 @@ class AppDomain final #endif -#if defined(FEATURE_TIERED_COMPILATION) - public: TieredCompilationManager * GetTieredCompilationManager() { @@ -1561,8 +1559,6 @@ class AppDomain final private: TieredCompilationManager m_tieredCompilationManager; -#endif - friend struct cdac_data; }; // class AppDomain diff --git a/src/coreclr/vm/ceemain.cpp b/src/coreclr/vm/ceemain.cpp index a2b8ac7b858289..3515db9792064a 100644 --- a/src/coreclr/vm/ceemain.cpp +++ b/src/coreclr/vm/ceemain.cpp @@ -670,8 +670,10 @@ void EEStartupHelper() JITInlineTrackingMap::StaticInitialize(); MethodDescBackpatchInfoTracker::StaticInitialize(); CodeVersionManager::StaticInitialize(); +#ifdef FEATURE_TIERED_COMPILATION TieredCompilationManager::StaticInitialize(); CallCountingManager::StaticInitialize(); +#endif // FEATURE_TIERED_COMPILATION OnStackReplacementManager::StaticInitialize(); MethodTable::InitMethodDataCache(); diff --git a/src/coreclr/vm/codeversion.cpp b/src/coreclr/vm/codeversion.cpp index 9a2af2a97f79d0..42eae50fb5b5f9 100644 --- a/src/coreclr/vm/codeversion.cpp +++ b/src/coreclr/vm/codeversion.cpp @@ -322,11 +322,11 @@ MethodDescVersioningState* NativeCodeVersion::GetMethodDescVersioningState() } #endif -#ifdef FEATURE_TIERED_COMPILATION - NativeCodeVersion::OptimizationTier NativeCodeVersion::GetOptimizationTier() const { LIMITED_METHOD_DAC_CONTRACT; + +#ifdef FEATURE_TIERED_COMPILATION if (m_storageKind == StorageKind::Explicit) { return AsNode()->GetOptimizationTier(); @@ -335,15 +335,24 @@ NativeCodeVersion::OptimizationTier NativeCodeVersion::GetOptimizationTier() con { return TieredCompilationManager::GetInitialOptimizationTier(GetMethodDesc()); } +#else // !FEATURE_TIERED_COMPILATION + return OptimizationTierOptimized; +#endif // FEATURE_TIERED_COMPILATION } bool NativeCodeVersion::IsFinalTier() const { LIMITED_METHOD_DAC_CONTRACT; + +#ifdef FEATURE_TIERED_COMPILATION OptimizationTier tier = GetOptimizationTier(); return tier == OptimizationTier1 || tier == OptimizationTierOptimized; +#else // !FEATURE_TIERED_COMPILATION + return true; +#endif // FEATURE_TIERED_COMPILATION } +#ifdef FEATURE_TIERED_COMPILATION #ifndef DACCESS_COMPILE void NativeCodeVersion::SetOptimizationTier(OptimizationTier tier) { diff --git a/src/coreclr/vm/codeversion.h b/src/coreclr/vm/codeversion.h index 7735a1039d43c6..8e642b7fc572a4 100644 --- a/src/coreclr/vm/codeversion.h +++ b/src/coreclr/vm/codeversion.h @@ -81,9 +81,10 @@ class NativeCodeVersion OptimizationTier0Instrumented, OptimizationTier1Instrumented, }; -#ifdef FEATURE_TIERED_COMPILATION OptimizationTier GetOptimizationTier() const; bool IsFinalTier() const; + +#ifdef FEATURE_TIERED_COMPILATION #ifndef DACCESS_COMPILE void SetOptimizationTier(OptimizationTier tier); #endif diff --git a/src/coreclr/vm/interpexec.cpp b/src/coreclr/vm/interpexec.cpp index 7376ac004513c6..7ecd0cf70068ad 100644 --- a/src/coreclr/vm/interpexec.cpp +++ b/src/coreclr/vm/interpexec.cpp @@ -259,6 +259,7 @@ template static THelper GetPossiblyIndirectHelper(const Inter *pILTargetMethod = PortableEntryPoint::GetMethodDesc((TADDR)addr); return NULL; // Return null to interpret this entrypoint } + addr = PortableEntryPoint::GetActualCode((TADDR)addr); #endif // FEATURE_PORTABLE_ENTRYPOINTS return (THelper)addr; diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index e04e6e4815680b..13baa81958a369 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -6546,10 +6546,12 @@ DWORD CEEInfo::getMethodAttribsInternal (CORINFO_METHOD_HANDLE ftn) result |= CORINFO_FLG_DELEGATE_INVOKE; } +#ifdef FEATURE_TIERED_COMPILATION if (!g_pConfig->TieredCompilation_QuickJitForLoops()) { result |= CORINFO_FLG_DISABLE_TIER0_FOR_LOOPS; } +#endif // FEATURE_TIERED_COMPILATION return result; } @@ -13352,7 +13354,7 @@ PCODE UnsafeJitFunction(PrepareCodeConfig* config, sizeOfILCode = interpreterJitInfo.getMethodInfoInternal()->ILCodeSize; #ifdef FEATURE_PORTABLE_ENTRYPOINTS - PCODE portableEntryPoint = ftn->GetTemporaryEntryPoint(); + PCODE portableEntryPoint = ftn->GetPortableEntryPoint(); _ASSERTE(portableEntryPoint != NULL); PortableEntryPoint::SetInterpreterData(PCODEToPINSTR(portableEntryPoint), ret); ret = portableEntryPoint; diff --git a/src/coreclr/vm/method.cpp b/src/coreclr/vm/method.cpp index 9de10c80a7d8aa..96f3ddbcf0ddb7 100644 --- a/src/coreclr/vm/method.cpp +++ b/src/coreclr/vm/method.cpp @@ -2108,7 +2108,13 @@ PCODE MethodDesc::TryGetMultiCallableAddrOfCode(CORINFO_ACCESS_FLAGS accessFlags } if (RequiresStableEntryPoint() && !HasStableEntryPoint()) + { +#ifdef FEATURE_PORTABLE_ENTRYPOINTS + EnsurePortableEntryPoint(); +#else // !FEATURE_PORTABLE_ENTRYPOINTS GetOrCreatePrecode(); +#endif // FEATURE_PORTABLE_ENTRYPOINTS + } // We create stable entrypoints for these upfront if (IsWrapperStub() || IsEnCAddedMethod()) @@ -2146,9 +2152,11 @@ PCODE MethodDesc::TryGetMultiCallableAddrOfCode(CORINFO_ACCESS_FLAGS accessFlags return (PCODE)NULL; } +#ifndef FEATURE_PORTABLE_ENTRYPOINTS // Force the creation of the precode if we would eventually got one anyway if (MayHavePrecode()) return GetOrCreatePrecode()->GetEntryPoint(); +#endif // !FEATURE_PORTABLE_ENTRYPOINTS _ASSERTE(!RequiresStableEntryPoint()); @@ -2776,6 +2784,28 @@ void MethodDesc::EnsureTemporaryEntryPointCore(AllocMemTracker *pamTracker) } } +#ifdef FEATURE_PORTABLE_ENTRYPOINTS +void MethodDesc::EnsurePortableEntryPoint() +{ + WRAPPER_NO_CONTRACT; + + // The portable entry point is currently the same as the + // temporary entry point. + EnsureTemporaryEntryPoint(); + + SetStableEntryPointInterlocked(GetPortableEntryPoint()); +} + +PCODE MethodDesc::GetPortableEntryPoint() +{ + WRAPPER_NO_CONTRACT; + + // The portable entry point is currently the same as the + // temporary entry point. + return GetTemporaryEntryPoint(); +} +#endif // FEATURE_PORTABLE_ENTRYPOINTS + //******************************************************************************* void MethodDescChunk::DetermineAndSetIsEligibleForTieredCompilation() { diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp index 1cb19ffb6eec54..ef609ece84ad80 100644 --- a/src/coreclr/vm/method.hpp +++ b/src/coreclr/vm/method.hpp @@ -1574,6 +1574,13 @@ class MethodDesc // OR must be set to point to the same AllocMemTracker that controls allocation of the MethodDesc void EnsureTemporaryEntryPointCore(AllocMemTracker *pamTracker); +#ifdef FEATURE_PORTABLE_ENTRYPOINTS + // Ensure that the portable entrypoint is allocated, and the slot is filled + void EnsurePortableEntryPoint(); + + PCODE GetPortableEntryPoint(); +#endif // FEATURE_PORTABLE_ENTRYPOINTS + //******************************************************************************* // Returns the address of the native code. PCODE GetNativeCode(); diff --git a/src/coreclr/vm/precode_portable.cpp b/src/coreclr/vm/precode_portable.cpp index b727d26c10fdf9..317add07b551a5 100644 --- a/src/coreclr/vm/precode_portable.cpp +++ b/src/coreclr/vm/precode_portable.cpp @@ -20,6 +20,15 @@ bool PortableEntryPoint::IsNativeEntryPoint(TADDR addr) return false; } +void* PortableEntryPoint::GetActualCode(TADDR addr) +{ + STANDARD_VM_CONTRACT; + + PortableEntryPoint* portableEntryPoint = ToPortableEntryPoint(addr); + _ASSERTE(portableEntryPoint->_pActualCode != NULL); + return portableEntryPoint->_pActualCode; +} + MethodDesc* PortableEntryPoint::GetMethodDesc(TADDR addr) { STANDARD_VM_CONTRACT; @@ -60,10 +69,10 @@ PortableEntryPoint* PortableEntryPoint::ToPortableEntryPoint(TADDR addr) void PortableEntryPoint::Init(MethodDesc* pMD) { LIMITED_METHOD_CONTRACT; - INDEBUG(_canary = CANARY_VALUE); + _pActualCode = NULL; _pMD = pMD; _pInterpreterData = NULL; - _pActualCode = NULL; + INDEBUG(_canary = CANARY_VALUE); } InterleavedLoaderHeapConfig s_stubPrecodeHeapConfig; @@ -115,6 +124,21 @@ MethodDesc* FixupPrecode::GetMethodDesc() return NULL; } +Precode* Precode::Allocate(PrecodeType t, MethodDesc* pMD, + LoaderAllocator *pLoaderAllocator, AllocMemTracker *pamTracker) +{ + LIMITED_METHOD_CONTRACT; + _ASSERTE(!"Precode::Allocate is not supported with Portable EntryPoints"); + return NULL; +} + +Precode* Precode::GetPrecodeFromEntryPoint(PCODE addr, BOOL fSpeculative) +{ + LIMITED_METHOD_CONTRACT; + _ASSERTE(!"Precode::GetPrecodeFromEntryPoint is not supported with Portable EntryPoints"); + return NULL; +} + PrecodeType Precode::GetType() { LIMITED_METHOD_CONTRACT; @@ -199,11 +223,14 @@ BOOL Precode::IsPointingToPrestub(PCODE target) void FlushCacheForDynamicMappedStub(void* code, SIZE_T size) { - + LIMITED_METHOD_CONTRACT; + _ASSERTE(!"FlushCacheForDynamicMappedStub is not supported with Portable EntryPoints"); } BOOL DoesSlotCallPrestub(PCODE pCode) { + LIMITED_METHOD_CONTRACT; + _ASSERTE(!"DoesSlotCallPrestub is not supported with Portable EntryPoints"); return FALSE; } diff --git a/src/coreclr/vm/precode_portable.hpp b/src/coreclr/vm/precode_portable.hpp index ab9571b6835d7b..07aec0eff4f5c6 100644 --- a/src/coreclr/vm/precode_portable.hpp +++ b/src/coreclr/vm/precode_portable.hpp @@ -13,6 +13,7 @@ class PortableEntryPoint final { public: // static static bool IsNativeEntryPoint(TADDR addr); + static void* GetActualCode(TADDR addr); static MethodDesc* GetMethodDesc(TADDR addr); static void* GetInterpreterData(TADDR addr); static void SetInterpreterData(TADDR addr, PCODE interpreterData); @@ -21,11 +22,13 @@ class PortableEntryPoint final static PortableEntryPoint* ToPortableEntryPoint(TADDR addr); private: - INDEBUG(size_t _canary); void* _pActualCode; MethodDesc* _pMD; void* _pInterpreterData; + // We keep the canary value last to ensure a stable ABI across build flavors + INDEBUG(size_t _canary); + public: void Init(MethodDesc* pMD); }; @@ -78,15 +81,9 @@ class Precode { public: // static static Precode* Allocate(PrecodeType t, MethodDesc* pMD, - LoaderAllocator *pLoaderAllocator, AllocMemTracker *pamTracker) - { - return NULL; - } - - static Precode* GetPrecodeFromEntryPoint(PCODE addr, BOOL fSpeculative = FALSE) - { - return NULL; - } + LoaderAllocator *pLoaderAllocator, AllocMemTracker *pamTracker); + + static Precode* GetPrecodeFromEntryPoint(PCODE addr, BOOL fSpeculative = FALSE); public: PrecodeType GetType(); diff --git a/src/coreclr/vm/tieredcompilation.cpp b/src/coreclr/vm/tieredcompilation.cpp index 8f1b36eeac43ec..5a66f26ef328b3 100644 --- a/src/coreclr/vm/tieredcompilation.cpp +++ b/src/coreclr/vm/tieredcompilation.cpp @@ -130,6 +130,16 @@ NativeCodeVersion::OptimizationTier TieredCompilationManager::GetInitialOptimiza #endif } +bool TieredCompilationManager::IsTieringDelayActive() +{ + LIMITED_METHOD_CONTRACT; +#if defined(FEATURE_TIERED_COMPILATION) + return m_methodsPendingCountingForTier1 != nullptr; +#else + return false; +#endif // FEATURE_TIERED_COMPILATION +} + #if defined(FEATURE_TIERED_COMPILATION) && !defined(DACCESS_COMPILE) void TieredCompilationManager::HandleCallCountingForFirstCall(MethodDesc* pMethodDesc) @@ -574,12 +584,6 @@ void TieredCompilationManager::BackgroundWorkerStart() } } -bool TieredCompilationManager::IsTieringDelayActive() -{ - LIMITED_METHOD_CONTRACT; - return m_methodsPendingCountingForTier1 != nullptr; -} - bool TieredCompilationManager::TryDeactivateTieringDelay() { CONTRACTL diff --git a/src/coreclr/vm/tieredcompilation.h b/src/coreclr/vm/tieredcompilation.h index b39ef788a72e12..775240e8193a2c 100644 --- a/src/coreclr/vm/tieredcompilation.h +++ b/src/coreclr/vm/tieredcompilation.h @@ -37,6 +37,8 @@ class TieredCompilationManager public: static NativeCodeVersion::OptimizationTier GetInitialOptimizationTier(PTR_MethodDesc pMethodDesc); + bool IsTieringDelayActive(); + #ifdef FEATURE_TIERED_COMPILATION public: @@ -66,7 +68,6 @@ class TieredCompilationManager bool TryDeactivateTieringDelay(); public: - bool IsTieringDelayActive(); void AsyncCompleteCallCounting(); private: @@ -76,7 +77,7 @@ class TieredCompilationManager private: void OptimizeMethod(NativeCodeVersion nativeCodeVersion); HRESULT DeoptimizeMethodHelper(Module* pModule, mdMethodDef methodDef); - + NativeCodeVersion GetNextMethodToOptimize(); BOOL CompileCodeVersion(NativeCodeVersion nativeCodeVersion); void ActivateCodeVersion(NativeCodeVersion nativeCodeVersion); From bbd2b39beb80ea2daedcea75421010dc44f0271b Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Thu, 28 Aug 2025 11:26:24 -0700 Subject: [PATCH 08/15] Apply clr-abi edits. --- docs/design/coreclr/botr/clr-abi.md | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/docs/design/coreclr/botr/clr-abi.md b/docs/design/coreclr/botr/clr-abi.md index 420f386066f0d6..906bdbe3f47086 100644 --- a/docs/design/coreclr/botr/clr-abi.md +++ b/docs/design/coreclr/botr/clr-abi.md @@ -537,11 +537,21 @@ The extra state created by the JIT for synchronized methods (lock taken flag) mu EnC is supported for adding and editing generic methods and methods on generic types and generic methods on non-generic types. -# WASM support +# Portable entrypoints -The WASM target has restrictions that present unique difficulties for a JITed environment. One of those restrictions is the inability to allocate executable code at run-time. +On platforms that allow dynamic code generation, the runtime abstracts away execution strategies for dynamically loaded methods by allocating [`Precode`](method-descriptor.md#precode)s. The `Precode` is a small code fragment that is used as a temporary method entrypoint until the actual method code is acquired. `Precode`s are also used as part of the execution for methods that do not have regular JITed or AOT-compiled code, for example stubs or interpreted methods. `Precode`s allow native code to use the same native code calling convention irrespective of the execution strategy used by the target method. -The Portable Entry Point feature is required for WASM to avoid the need for run-time executable code generation. On less restricted platforms, `Precode`s are used to represent entry points to pre-JITed methods or for function pointers to managed functions. Instead of a small executable block of code, we use the `PortableEntryPoint` type that acts as a container for the target method (for example, `MethodDesc`) and a pointer to the code to interpret. +On platforms that do not allow dynamic code generation (Wasm), the runtime abstracts away execution strategies by allocating portable entrypoints for dynamically loaded methods. The `PortableEntryPoint` is a data structure that allows efficient transition to the desired execution strategy for the target method. When the runtime is configured to use portable entrypoints, the managed calling convention is modified as follows: + +- The native code to call is obtained by dereferencing the entrypoint + +- The entrypoint address is passed in as an extra last hidden argument. The extra hidden argument must be present in signatures of all methods. It is unused by the code of JITed or AOT-compiled methods. + +Pseudo code for a call with portable entrypoints: + +> `(*(void**)pfn)(arg0, arg1, ..., argN, pfn)` + +Portable entrypoints are used for Wasm with interpreter only currently. Note that portable entrypoints are unnecessary for Wasm with native AOT since native AOT does not support dynamic loading. # System V x86_64 support From 5edff8528b349b7cd89dac1d3ad1c657f87cb67e Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Thu, 28 Aug 2025 11:38:50 -0700 Subject: [PATCH 09/15] Style nits. --- src/coreclr/vm/eeconfig.h | 2 +- src/coreclr/vm/loaderallocator.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/vm/eeconfig.h b/src/coreclr/vm/eeconfig.h index 2300be8fba075e..9c008ae7647cc2 100644 --- a/src/coreclr/vm/eeconfig.h +++ b/src/coreclr/vm/eeconfig.h @@ -87,7 +87,7 @@ class EEConfig DWORD TieredCompilation_CallCountingDelayMs() const { LIMITED_METHOD_CONTRACT; return tieredCompilation_CallCountingDelayMs; } bool TieredCompilation_UseCallCountingStubs() const { LIMITED_METHOD_CONTRACT; return fTieredCompilation_UseCallCountingStubs; } DWORD TieredCompilation_DeleteCallCountingStubsAfter() const { LIMITED_METHOD_CONTRACT; return tieredCompilation_DeleteCallCountingStubsAfter; } -#endif +#endif // FEATURE_TIERED_COMPILATION #if defined(FEATURE_PGO) bool TieredPGO(void) const { LIMITED_METHOD_CONTRACT; return fTieredPGO; } diff --git a/src/coreclr/vm/loaderallocator.cpp b/src/coreclr/vm/loaderallocator.cpp index e2d779d0e404cd..f02cd07af6ee6e 100644 --- a/src/coreclr/vm/loaderallocator.cpp +++ b/src/coreclr/vm/loaderallocator.cpp @@ -1244,7 +1244,7 @@ void LoaderAllocator::Init(BYTE *pExecutableHeapMemory) { m_callCountingManager = new CallCountingManager(); } -#endif +#endif // FEATURE_TIERED_COMPILATION } From 95bf40b286cbc03af48165be5abc9d77a6ade7ef Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Thu, 28 Aug 2025 11:47:33 -0700 Subject: [PATCH 10/15] Feedback --- src/coreclr/vm/method.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/coreclr/vm/method.cpp b/src/coreclr/vm/method.cpp index 96f3ddbcf0ddb7..32bd8222e8577f 100644 --- a/src/coreclr/vm/method.cpp +++ b/src/coreclr/vm/method.cpp @@ -2107,14 +2107,12 @@ PCODE MethodDesc::TryGetMultiCallableAddrOfCode(CORINFO_ACCESS_FLAGS accessFlags _ASSERTE((accessFlags & ~CORINFO_ACCESS_LDFTN) == 0); } - if (RequiresStableEntryPoint() && !HasStableEntryPoint()) - { #ifdef FEATURE_PORTABLE_ENTRYPOINTS - EnsurePortableEntryPoint(); + return GetPortableEntryPoint(); + #else // !FEATURE_PORTABLE_ENTRYPOINTS + if (RequiresStableEntryPoint() && !HasStableEntryPoint()) GetOrCreatePrecode(); -#endif // FEATURE_PORTABLE_ENTRYPOINTS - } // We create stable entrypoints for these upfront if (IsWrapperStub() || IsEnCAddedMethod()) @@ -2152,11 +2150,9 @@ PCODE MethodDesc::TryGetMultiCallableAddrOfCode(CORINFO_ACCESS_FLAGS accessFlags return (PCODE)NULL; } -#ifndef FEATURE_PORTABLE_ENTRYPOINTS // Force the creation of the precode if we would eventually got one anyway if (MayHavePrecode()) return GetOrCreatePrecode()->GetEntryPoint(); -#endif // !FEATURE_PORTABLE_ENTRYPOINTS _ASSERTE(!RequiresStableEntryPoint()); @@ -2176,6 +2172,7 @@ PCODE MethodDesc::TryGetMultiCallableAddrOfCode(CORINFO_ACCESS_FLAGS accessFlags // return GetTemporaryEntryPoint(); } +#endif // !FEATURE_PORTABLE_ENTRYPOINTS } //******************************************************************************* From 9f4b8a409a7cd05a64d9f098176ef91e14891e8a Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Thu, 28 Aug 2025 12:14:22 -0700 Subject: [PATCH 11/15] Fix issue with CMake Add back some accidentally removed code. --- src/coreclr/clrdefinitions.cmake | 4 +++- src/coreclr/vm/precode.cpp | 3 ++- src/coreclr/vm/precode.h | 6 +++--- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/coreclr/clrdefinitions.cmake b/src/coreclr/clrdefinitions.cmake index e9e99388c57a74..610a7fef055698 100644 --- a/src/coreclr/clrdefinitions.cmake +++ b/src/coreclr/clrdefinitions.cmake @@ -118,7 +118,9 @@ if (CLR_CMAKE_TARGET_WIN32 AND (CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ endif (CLR_CMAKE_TARGET_WIN32 AND (CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_I386 OR CLR_CMAKE_TARGET_ARCH_ARM64)) add_compile_definitions($<${FEATURE_INTERPRETER}:FEATURE_INTERPRETER>) -add_compile_definitions($<${FEATURE_PORTABLE_ENTRYPOINTS}:FEATURE_PORTABLE_ENTRYPOINTS>) +if (FEATURE_PORTABLE_ENTRYPOINTS) + add_compile_definitions(FEATURE_PORTABLE_ENTRYPOINTS) +endif() if (CLR_CMAKE_TARGET_WIN32) add_definitions(-DFEATURE_ISYM_READER) diff --git a/src/coreclr/vm/precode.cpp b/src/coreclr/vm/precode.cpp index a4a3d85332f805..eb682e07d17004 100644 --- a/src/coreclr/vm/precode.cpp +++ b/src/coreclr/vm/precode.cpp @@ -578,6 +578,7 @@ void StubPrecode::StaticInitialize() void StubPrecode::GenerateCodePage(uint8_t* pageBase, uint8_t* pageBaseRX, size_t pageSize) { + int totalCodeSize = (int)(pageSize / StubPrecode::CodeSize) * StubPrecode::CodeSize; #ifdef TARGET_X86 for (int i = 0; i < totalCodeSize; i += StubPrecode::CodeSize) { @@ -748,8 +749,8 @@ void FixupPrecode::GenerateDataPage(uint8_t* pageBase, size_t pageSize) void FixupPrecode::GenerateCodePage(uint8_t* pageBase, uint8_t* pageBaseRX, size_t pageSize) { + int totalCodeSize = (int)((pageSize / FixupPrecode::CodeSize) * FixupPrecode::CodeSize); #ifdef TARGET_X86 - for (int i = 0; i < totalCodeSize; i += FixupPrecode::CodeSize) { memcpy(pageBase + i, (const void*)FixupPrecodeCode, FixupPrecode::CodeSize); diff --git a/src/coreclr/vm/precode.h b/src/coreclr/vm/precode.h index 72984e1d7e6b93..1118faf86dfa05 100644 --- a/src/coreclr/vm/precode.h +++ b/src/coreclr/vm/precode.h @@ -86,7 +86,7 @@ typedef DPTR(class UMEntryThunk) PTR_UMEntryThunk; struct InterpreterPrecode; typedef DPTR(InterpreterPrecode) PTR_InterpreterPrecode; -#endif +#endif // FEATURE_INTERPRETER // Regular precode struct StubPrecode @@ -215,7 +215,7 @@ struct PInvokeImportPrecode : StubPrecode void Init(PInvokeImportPrecode* pPrecodeRX, MethodDesc* pMD, LoaderAllocator *pLoaderAllocator); - LPVOID GetEntrypoint() + LPVOID GetEntryPoint() { LIMITED_METHOD_CONTRACT; return (LPVOID)PINSTRToPCODE(dac_cast(this)); @@ -250,7 +250,7 @@ struct ThisPtrRetBufPrecode : StubPrecode return dac_cast(StubPrecode::GetData()->SecretParam); } - LPVOID GetEntrypoint() + LPVOID GetEntryPoint() { LIMITED_METHOD_CONTRACT; return (LPVOID)PINSTRToPCODE(dac_cast(this)); From f0416154b9c558d16f95912f0c475a4bd1693bfe Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Thu, 28 Aug 2025 12:34:44 -0700 Subject: [PATCH 12/15] Fix build break. --- src/coreclr/vm/util.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/vm/util.hpp b/src/coreclr/vm/util.hpp index a05526fadcf4de..087a7398012a72 100644 --- a/src/coreclr/vm/util.hpp +++ b/src/coreclr/vm/util.hpp @@ -427,10 +427,10 @@ extern LockOwner g_lockTrustMeIAmThreadSafe; class EEThreadId { private: + static SIZE_T const UNKNOWN_ID = INVALID_POINTER_CD; SIZE_T m_FiberPtrId; public: #ifdef _DEBUG - static SIZE_T const UNKNOWN_ID = INVALID_POINTER_CD; EEThreadId() : m_FiberPtrId(UNKNOWN_ID) { LIMITED_METHOD_CONTRACT; From f85091e83de3d8e77c2a5e51af249789cdd17ea9 Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Thu, 28 Aug 2025 12:36:54 -0700 Subject: [PATCH 13/15] Address copilot feedback --- src/coreclr/vm/interpexec.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/coreclr/vm/interpexec.cpp b/src/coreclr/vm/interpexec.cpp index 7ecd0cf70068ad..4caf16881bea3e 100644 --- a/src/coreclr/vm/interpexec.cpp +++ b/src/coreclr/vm/interpexec.cpp @@ -256,6 +256,7 @@ template static THelper GetPossiblyIndirectHelper(const Inter #ifdef FEATURE_PORTABLE_ENTRYPOINTS if (!PortableEntryPoint::IsNativeEntryPoint((TADDR)addr)) { + _ASSERTE(pILTargetMethod != NULL); *pILTargetMethod = PortableEntryPoint::GetMethodDesc((TADDR)addr); return NULL; // Return null to interpret this entrypoint } From 7c8b82568d2c029d04050f0cc61b1c6852024d13 Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Thu, 28 Aug 2025 15:41:45 -0700 Subject: [PATCH 14/15] Feedback --- src/coreclr/vm/codeversion.cpp | 30 +++++++++++++----------------- src/coreclr/vm/codeversion.h | 5 +++-- src/coreclr/vm/jitinterface.cpp | 12 ++++++++---- 3 files changed, 24 insertions(+), 23 deletions(-) diff --git a/src/coreclr/vm/codeversion.cpp b/src/coreclr/vm/codeversion.cpp index 42eae50fb5b5f9..9db4ea522b0fbc 100644 --- a/src/coreclr/vm/codeversion.cpp +++ b/src/coreclr/vm/codeversion.cpp @@ -322,11 +322,23 @@ MethodDescVersioningState* NativeCodeVersion::GetMethodDescVersioningState() } #endif -NativeCodeVersion::OptimizationTier NativeCodeVersion::GetOptimizationTier() const +bool NativeCodeVersion::IsFinalTier() const { LIMITED_METHOD_DAC_CONTRACT; #ifdef FEATURE_TIERED_COMPILATION + OptimizationTier tier = GetOptimizationTier(); + return tier == OptimizationTier1 || tier == OptimizationTierOptimized; +#else // !FEATURE_TIERED_COMPILATION + return true; +#endif // FEATURE_TIERED_COMPILATION +} + +#ifdef FEATURE_TIERED_COMPILATION +NativeCodeVersion::OptimizationTier NativeCodeVersion::GetOptimizationTier() const +{ + LIMITED_METHOD_DAC_CONTRACT; + if (m_storageKind == StorageKind::Explicit) { return AsNode()->GetOptimizationTier(); @@ -335,24 +347,8 @@ NativeCodeVersion::OptimizationTier NativeCodeVersion::GetOptimizationTier() con { return TieredCompilationManager::GetInitialOptimizationTier(GetMethodDesc()); } -#else // !FEATURE_TIERED_COMPILATION - return OptimizationTierOptimized; -#endif // FEATURE_TIERED_COMPILATION -} - -bool NativeCodeVersion::IsFinalTier() const -{ - LIMITED_METHOD_DAC_CONTRACT; - -#ifdef FEATURE_TIERED_COMPILATION - OptimizationTier tier = GetOptimizationTier(); - return tier == OptimizationTier1 || tier == OptimizationTierOptimized; -#else // !FEATURE_TIERED_COMPILATION - return true; -#endif // FEATURE_TIERED_COMPILATION } -#ifdef FEATURE_TIERED_COMPILATION #ifndef DACCESS_COMPILE void NativeCodeVersion::SetOptimizationTier(OptimizationTier tier) { diff --git a/src/coreclr/vm/codeversion.h b/src/coreclr/vm/codeversion.h index 8e642b7fc572a4..84019d9e346aca 100644 --- a/src/coreclr/vm/codeversion.h +++ b/src/coreclr/vm/codeversion.h @@ -71,6 +71,8 @@ class NativeCodeVersion BOOL SetNativeCodeInterlocked(PCODE pCode, PCODE pExpected = 0); #endif + bool IsFinalTier() const; + // NOTE: Don't change existing values to avoid breaking changes in event tracing enum OptimizationTier { @@ -81,10 +83,9 @@ class NativeCodeVersion OptimizationTier0Instrumented, OptimizationTier1Instrumented, }; +#ifdef FEATURE_TIERED_COMPILATION OptimizationTier GetOptimizationTier() const; - bool IsFinalTier() const; -#ifdef FEATURE_TIERED_COMPILATION #ifndef DACCESS_COMPILE void SetOptimizationTier(OptimizationTier tier); #endif diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 13baa81958a369..b2d2b11543c2f9 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -14581,6 +14581,7 @@ CORINFO_METHOD_HANDLE CEEJitInfo::getAsyncResumptionStub() numArgs++; #endif +#ifdef FEATURE_TIERED_COMPILATION // Resumption stubs are uniquely coupled to the code version (since the // continuation is), so we need to make sure we always keep calling the // same version here. @@ -14595,11 +14596,12 @@ CORINFO_METHOD_HANDLE CEEJitInfo::getAsyncResumptionStub() // so through information stored in the continuation). _ASSERTE(m_pPatchpointInfoFromRuntime != NULL); pCode->EmitLDC((DWORD_PTR)m_pPatchpointInfoFromRuntime->GetTier0EntryPoint()); -#else +#else // !FEATURE_ON_STACK_REPLACEMENT _ASSERTE(!"Unexpected optimization tier with OSR disabled"); -#endif +#endif // FEATURE_ON_STACK_REPLACEMENT } else +#endif // FEATURE_TIERED_COMPILATION { { m_finalCodeAddressSlot = (PCODE*)amTracker.Track(m_pMethodBeingCompiled->GetLoaderAllocator()->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(PCODE)))); @@ -14737,7 +14739,8 @@ CORINFO_METHOD_HANDLE CEEJitInfo::getAsyncResumptionStub() amTracker.SuppressRelease(); - const char* optimizationTierName = nullptr; + const char* optimizationTierName = "UnknownTier"; +#ifdef FEATURE_TIERED_COMPILATION switch (ncv.GetOptimizationTier()) { case NativeCodeVersion::OptimizationTier0: optimizationTierName = "Tier0"; break; @@ -14746,8 +14749,9 @@ CORINFO_METHOD_HANDLE CEEJitInfo::getAsyncResumptionStub() case NativeCodeVersion::OptimizationTierOptimized: optimizationTierName = "Optimized"; break; case NativeCodeVersion::OptimizationTier0Instrumented: optimizationTierName = "Tier0Instrumented"; break; case NativeCodeVersion::OptimizationTier1Instrumented: optimizationTierName = "Tier1Instrumented"; break; - default: optimizationTierName = "UnknownTier"; break; + default: break; } +#endif // FEATURE_TIERED_COMPILATION char name[256]; int numWritten = sprintf_s(name, ARRAY_SIZE(name), "IL_STUB_AsyncResume_%s_%s", m_pMethodBeingCompiled->GetName(), optimizationTierName); From 1449a53b2cd615aa5d055ff138cb1aeecc76b5ff Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Thu, 28 Aug 2025 15:45:18 -0700 Subject: [PATCH 15/15] Remove helper function. --- src/coreclr/vm/interpexec.cpp | 2 +- src/coreclr/vm/method.hpp | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/coreclr/vm/interpexec.cpp b/src/coreclr/vm/interpexec.cpp index 4caf16881bea3e..bc504bab76272e 100644 --- a/src/coreclr/vm/interpexec.cpp +++ b/src/coreclr/vm/interpexec.cpp @@ -2068,7 +2068,7 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr pInterpreterFrame->SetTopInterpMethodContextFrame(pFrame); GCX_PREEMP(); // Attempt to setup the interpreter code for the target method. - if (targetMethod->CanBeInterpreted()) + if ((targetMethod->IsIL() || targetMethod->IsNoMetadata()) && !targetMethod->IsUnboxingStub()) { PrepareInitialCode(targetMethod); } diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp index ef609ece84ad80..9bd1f5889e6fa3 100644 --- a/src/coreclr/vm/method.hpp +++ b/src/coreclr/vm/method.hpp @@ -1815,12 +1815,6 @@ class MethodDesc LIMITED_METHOD_CONTRACT; VolatileStore(&m_interpreterCode, interpreterCode); } - - bool CanBeInterpreted() - { - WRAPPER_NO_CONTRACT; - return (IsIL() || IsNoMetadata()) && !IsUnboxingStub(); - } #endif // FEATURE_INTERPRETER #ifdef _DEBUG