From 975b64801e6565c762830524e44202052ea9f70b Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Thu, 2 Apr 2026 10:18:10 -0700 Subject: [PATCH 01/15] Fix minor issues and setup for actually loading code as R2R --- src/coreclr/inc/webcildecoder.h | 21 ++- src/coreclr/utilcode/webcildecoder.cpp | 170 +++++++++++++++++++++++-- src/coreclr/vm/peimagelayout.cpp | 2 + src/coreclr/vm/peimagelayout.inl | 6 +- 4 files changed, 178 insertions(+), 21 deletions(-) diff --git a/src/coreclr/inc/webcildecoder.h b/src/coreclr/inc/webcildecoder.h index a83e5d33875aca..d81ac86bc5f92c 100644 --- a/src/coreclr/inc/webcildecoder.h +++ b/src/coreclr/inc/webcildecoder.h @@ -197,15 +197,20 @@ class WebcilDecoder void *GetNativeEntryPoint() const { return NULL; } // ------------------------------------------------------------ - // R2R — not supported for Webcil + // R2R // ------------------------------------------------------------ - BOOL HasReadyToRunHeader() const { return FALSE; } + BOOL HasReadyToRunHeader() const; BOOL IsComponentAssembly() const { return FALSE; } - READYTORUN_HEADER *GetReadyToRunHeader() const { return NULL; } - BOOL IsNativeMachineFormat() const { return FALSE; } - PTR_CVOID GetNativeManifestMetadata(COUNT_T *pSize = NULL) const; + READYTORUN_HEADER *GetReadyToRunHeader() const; + BOOL IsNativeMachineFormat() const { return TRUE; } + PTR_CVOID GetNativeManifestMetadata(COUNT_T *pSize) const; + CHECK CheckDirectory(IMAGE_DATA_DIRECTORY *pDir, int forbiddenFlags = 0, IsNullOK ok = NULL_NOT_OK) const; + TADDR GetDirectoryData(IMAGE_DATA_DIRECTORY *pDir) const; +private: + READYTORUN_HEADER *FindReadyToRunHeader() const; +public: // ------------------------------------------------------------ // RVA operations (remaining private) // ------------------------------------------------------------ @@ -274,12 +279,14 @@ class WebcilDecoder // Instance members TADDR m_base; COUNT_T m_size; - BOOL m_hasContents; - BOOL m_relocated = FALSE; + bool m_relocated = FALSE; + bool m_hasContents; + mutable bool m_hasNoReadyToRunHeader; const WebcilHeader *m_pHeader; const WebcilSectionHeader *m_sections; mutable IMAGE_COR20_HEADER *m_pCorHeader; + mutable PTR_READYTORUN_HEADER m_pReadyToRunHeader; }; #endif // FEATURE_WEBCIL diff --git a/src/coreclr/utilcode/webcildecoder.cpp b/src/coreclr/utilcode/webcildecoder.cpp index 475801ab4be5a4..9ee69521d3b675 100644 --- a/src/coreclr/utilcode/webcildecoder.cpp +++ b/src/coreclr/utilcode/webcildecoder.cpp @@ -34,10 +34,12 @@ bool WebcilDecoder::DetectWebcilFormat(const void* data, COUNT_T size) WebcilDecoder::WebcilDecoder() : m_base(0), m_size(0), - m_hasContents(FALSE), + m_hasContents(false), + m_hasNoReadyToRunHeader(false), m_pHeader(NULL), m_sections(NULL), - m_pCorHeader(NULL) + m_pCorHeader(NULL), + m_pReadyToRunHeader(NULL) { LIMITED_METHOD_CONTRACT; } @@ -82,7 +84,7 @@ void WebcilDecoder::Reset() LIMITED_METHOD_CONTRACT; m_base = 0; m_size = 0; - m_hasContents = FALSE; + m_hasContents = false; m_pHeader = NULL; m_sections = NULL; m_pCorHeader = NULL; @@ -470,14 +472,6 @@ ULONG WebcilDecoder::GetEntryPointToken() const // R2R / native manifest metadata // ------------------------------------------------------------ -PTR_CVOID WebcilDecoder::GetNativeManifestMetadata(COUNT_T *pSize) const -{ - LIMITED_METHOD_CONTRACT; - if (pSize != NULL) - *pSize = 0; - return NULL; -} - // ------------------------------------------------------------ // RVA operations // ------------------------------------------------------------ @@ -916,6 +910,160 @@ CHECK WebcilDecoder::CheckILMethod(RVA rva) return CorDecoderHelpers::CheckILMethod(*this, rva); } +BOOL WebcilDecoder::HasReadyToRunHeader() const +{ + CONTRACTL + { + INSTANCE_CHECK; + NOTHROW; + GC_NOTRIGGER; + CANNOT_TAKE_LOCK; + SUPPORTS_DAC; + } + CONTRACTL_END; + + if (m_hasNoReadyToRunHeader) + return FALSE; + + if (m_pReadyToRunHeader != NULL) + return TRUE; + + return FindReadyToRunHeader() != NULL; +} + +READYTORUN_HEADER * WebcilDecoder::GetReadyToRunHeader() const +{ + CONTRACT(READYTORUN_HEADER *) + { + INSTANCE_CHECK; + PRECONDITION(CheckNTHeaders()); + PRECONDITION(HasCorHeader()); + PRECONDITION(HasReadyToRunHeader()); + NOTHROW; + GC_NOTRIGGER; + POSTCONDITION(CheckPointer(RETVAL)); + SUPPORTS_DAC; + CANNOT_TAKE_LOCK; + } + CONTRACT_END; + + if (m_pReadyToRunHeader != NULL) + RETURN m_pReadyToRunHeader; + + RETURN FindReadyToRunHeader(); +} + +PTR_CVOID WebcilDecoder::GetNativeManifestMetadata(COUNT_T *pSize) const +{ + CONTRACT(PTR_CVOID) + { + INSTANCE_CHECK; + PRECONDITION(HasReadyToRunHeader()); + POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); // TBD - may not store metadata for IJW + NOTHROW; + GC_NOTRIGGER; + } + CONTRACT_END; + + IMAGE_DATA_DIRECTORY *pDir = NULL; + { + READYTORUN_HEADER * pHeader = GetReadyToRunHeader(); + + PTR_READYTORUN_SECTION pSections = dac_cast(dac_cast(pHeader) + sizeof(READYTORUN_HEADER)); + for (DWORD i = 0; i < pHeader->CoreHeader.NumberOfSections; i++) + { + // Verify that section types are sorted + _ASSERTE(i == 0 || (pSections[i - 1].Type < pSections[i].Type)); + + READYTORUN_SECTION * pSection = pSections + i; + if (pSection->Type == ReadyToRunSectionType::ManifestMetadata) + { + // Set pDir to the address of the manifest metadata section + pDir = &pSection->Section; + break; + } + } + + // ReadyToRun file without large version bubble support doesn't have the ManifestMetadata + if (pDir == NULL) + { + if (pSize != NULL) + { + *pSize = 0; + } + + RETURN NULL; + } + } + + if (pSize != NULL) + *pSize = VAL32(pDir->Size); + + RETURN dac_cast(GetDirectoryData(pDir)); +} + +READYTORUN_HEADER * WebcilDecoder::FindReadyToRunHeader() const +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + SUPPORTS_DAC; + } + CONTRACTL_END; + + IMAGE_DATA_DIRECTORY *pDir = &GetCorHeader()->ManagedNativeHeader; + + if (VAL32(pDir->Size) >= sizeof(READYTORUN_HEADER) && CheckDirectory(pDir)) + { + PTR_READYTORUN_HEADER pHeader = PTR_READYTORUN_HEADER((TADDR)GetDirectoryData(pDir)); + if (pHeader->Signature == READYTORUN_SIGNATURE) + { + m_pReadyToRunHeader = pHeader; + return pHeader; + } + } + + m_hasNoReadyToRunHeader = true; + return NULL; +} + +TADDR WebcilDecoder::GetDirectoryData(IMAGE_DATA_DIRECTORY *pDir) const +{ + CONTRACT(TADDR) + { + INSTANCE_CHECK; + PRECONDITION(CheckNTHeaders()); + PRECONDITION(CheckDirectory(pDir, 0, NULL_OK)); + NOTHROW; + GC_NOTRIGGER; + SUPPORTS_DAC; + POSTCONDITION(CheckPointer((void *)RETVAL, NULL_OK)); + CANNOT_TAKE_LOCK; + } + CONTRACT_END; + + RETURN GetRvaData(VAL32(pDir->VirtualAddress)); +} + +CHECK WebcilDecoder::CheckDirectory(IMAGE_DATA_DIRECTORY *pDir, int forbiddenFlags, IsNullOK ok) const +{ + CONTRACT_CHECK + { + INSTANCE_CHECK; + PRECONDITION(CheckNTHeaders()); + PRECONDITION(CheckPointer(pDir)); + NOTHROW; + GC_NOTRIGGER; + SUPPORTS_DAC; + } + CONTRACT_CHECK_END; + + CHECK(CheckRva(VAL32(pDir->VirtualAddress), VAL32(pDir->Size), forbiddenFlags, ok)); + + CHECK_OK; +} + // ------------------------------------------------------------ // DAC support // ------------------------------------------------------------ diff --git a/src/coreclr/vm/peimagelayout.cpp b/src/coreclr/vm/peimagelayout.cpp index 5442b4d17e9cfe..5ebbf7a04802f9 100644 --- a/src/coreclr/vm/peimagelayout.cpp +++ b/src/coreclr/vm/peimagelayout.cpp @@ -210,6 +210,8 @@ void PEImageLayout::InitDecoders(void* data, COUNT_T size) { m_format = FORMAT_WEBCIL; m_webcilDecoder.Init(data, size); + if (HasBaseRelocations()) + ApplyBaseRelocations(true); m_peDecoder.Init(data, size); // Initialize base/size/flags for cDAC } else diff --git a/src/coreclr/vm/peimagelayout.inl b/src/coreclr/vm/peimagelayout.inl index bd0d1a8c2b5f28..4810c7ebf1a68c 100644 --- a/src/coreclr/vm/peimagelayout.inl +++ b/src/coreclr/vm/peimagelayout.inl @@ -579,7 +579,7 @@ inline IMAGE_COR_VTABLEFIXUP *PEImageLayout::GetVTableFixups(COUNT_T *pCount) co inline BOOL PEImageLayout::IsNativeMachineFormat() const { WRAPPER_NO_CONTRACT; - PE_OR_WEBCIL(IsNativeMachineFormat(), FALSE) + PE_OR_WEBCIL(IsNativeMachineFormat(), TRUE) } inline BOOL PEImageLayout::IsI386() const @@ -634,13 +634,13 @@ inline BOOL PEImageLayout::IsComponentAssembly() const inline BOOL PEImageLayout::HasReadyToRunHeader() const { LIMITED_METHOD_DAC_CONTRACT; - PE_OR_WEBCIL(HasReadyToRunHeader(), FALSE) + DECODER_DISPATCH(HasReadyToRunHeader()) } inline READYTORUN_HEADER *PEImageLayout::GetReadyToRunHeader() const { WRAPPER_NO_CONTRACT; - PE_OR_WEBCIL(GetReadyToRunHeader(), NULL) + DECODER_DISPATCH(GetReadyToRunHeader()) } inline BOOL PEImageLayout::HasNativeEntryPoint() const From 8abcd707bb4c213d7bddefc45c275c27ee5c3d92 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Thu, 2 Apr 2026 12:04:33 -0700 Subject: [PATCH 02/15] Hook up GetTableBaseOffset --- src/coreclr/inc/webcildecoder.h | 6 ++++++ src/coreclr/utilcode/webcildecoder.cpp | 2 +- src/coreclr/vm/peimagelayout.cpp | 2 +- src/coreclr/vm/peimagelayout.h | 4 +--- src/coreclr/vm/peimagelayout.inl | 15 ++++++++++++--- 5 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/coreclr/inc/webcildecoder.h b/src/coreclr/inc/webcildecoder.h index d81ac86bc5f92c..4082b0506bac13 100644 --- a/src/coreclr/inc/webcildecoder.h +++ b/src/coreclr/inc/webcildecoder.h @@ -208,6 +208,12 @@ class WebcilDecoder CHECK CheckDirectory(IMAGE_DATA_DIRECTORY *pDir, int forbiddenFlags = 0, IsNullOK ok = NULL_NOT_OK) const; TADDR GetDirectoryData(IMAGE_DATA_DIRECTORY *pDir) const; + SSIZE_T GetTableBaseOffset() const + { + if (m_pHeader == NULL) + return 0; + return m_pHeader->VersionMajor >= WEBCIL_VERSION_MAJOR_1 ? ((const WebcilHeader_1 *)m_pHeader)->TableBase : 0; + } private: READYTORUN_HEADER *FindReadyToRunHeader() const; public: diff --git a/src/coreclr/utilcode/webcildecoder.cpp b/src/coreclr/utilcode/webcildecoder.cpp index 9ee69521d3b675..e4a1011eb0a706 100644 --- a/src/coreclr/utilcode/webcildecoder.cpp +++ b/src/coreclr/utilcode/webcildecoder.cpp @@ -858,7 +858,7 @@ TADDR WebcilDecoder::GetDirectoryEntryData(int entry, COUNT_T *pSize) const return (TADDR)0; } - const WebcilSectionHeader *sections = (const WebcilSectionHeader *)(m_base + sizeof(WebcilHeader)); + const WebcilSectionHeader *sections = m_sections; const WebcilSectionHeader *relocSection = §ions[sectionIndex]; if (pSize != NULL) diff --git a/src/coreclr/vm/peimagelayout.cpp b/src/coreclr/vm/peimagelayout.cpp index 5ebbf7a04802f9..8a8497b476a5d4 100644 --- a/src/coreclr/vm/peimagelayout.cpp +++ b/src/coreclr/vm/peimagelayout.cpp @@ -247,7 +247,7 @@ void PEImageLayout::ApplyBaseRelocations(bool relocationMustWriteCopy) SSIZE_T delta = (SIZE_T) GetBase() - (SIZE_T) GetPreferredBase(); #ifdef FEATURE_WEBCIL - SSIZE_T tableBaseDelta = m_tableBaseOffset; + SSIZE_T tableBaseDelta = GetTableBaseOffset(); #endif // FEATURE_WEBCIL // Nothing to do - image is loaded at preferred base and no table base offset diff --git a/src/coreclr/vm/peimagelayout.h b/src/coreclr/vm/peimagelayout.h index 542ec86216569b..89c66cc4b63d30 100644 --- a/src/coreclr/vm/peimagelayout.h +++ b/src/coreclr/vm/peimagelayout.h @@ -77,9 +77,7 @@ class PEImageLayout void ApplyBaseRelocations(bool relocationMustWriteCopy); #ifdef FEATURE_WEBCIL -// TODO-WASM: These can be removed very soon, and we can fetch this from the webcil header itself - void SetTableBaseOffset(SSIZE_T tableBaseOffset) { m_tableBaseOffset = tableBaseOffset; } - SSIZE_T GetTableBaseOffset() const { return m_tableBaseOffset; } + SSIZE_T GetTableBaseOffset() const; #endif // ------------------------------------------------------------ diff --git a/src/coreclr/vm/peimagelayout.inl b/src/coreclr/vm/peimagelayout.inl index 4810c7ebf1a68c..83703fde9220ec 100644 --- a/src/coreclr/vm/peimagelayout.inl +++ b/src/coreclr/vm/peimagelayout.inl @@ -65,9 +65,6 @@ inline PEImageLayout::~PEImageLayout() inline PEImageLayout::PEImageLayout() : m_refCount(1) , m_format(FORMAT_PE) -#ifdef FEATURE_WEBCIL - , m_tableBaseOffset(0) -#endif , m_pOwner(NULL) { LIMITED_METHOD_CONTRACT; @@ -279,6 +276,18 @@ inline BOOL PEImageLayout::HasBaseRelocations() const DECODER_DISPATCH(HasBaseRelocations()) } +#ifdef FEATURE_WEBCIL +inline SSIZE_T PEImageLayout::GetTableBaseOffset() const +{ + WRAPPER_NO_CONTRACT; + if (IsWebcilFormat()) + { + return m_webcilDecoder.GetTableBaseOffset(); + } + return 0; +} +#endif + inline const void *PEImageLayout::GetPreferredBase() const { WRAPPER_NO_CONTRACT; From da04889c505f7cc7016a1e9471d8c01a06dca0f7 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Fri, 3 Apr 2026 13:47:58 -0700 Subject: [PATCH 03/15] Next steps towards actually running code. At this point we can get to the point of jumping into the r2r code, and then it fails with a function type mismatch since portable entrypoints are not quite correctly hooked up --- .../ReadyToRun/RuntimeFunctionsTableNode.cs | 17 ++++++++++++----- src/coreclr/vm/readytoruninfo.cpp | 11 +++++++++++ 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/RuntimeFunctionsTableNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/RuntimeFunctionsTableNode.cs index bd2f062fc59be4..063caae405075c 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/RuntimeFunctionsTableNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/RuntimeFunctionsTableNode.cs @@ -124,12 +124,19 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) symbol = method; } - runtimeFunctionsBuilder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_ADDR32NB, delta: frameInfo.StartOffset + _nodeFactory.Target.CodeDelta); - if (!relocsOnly && _nodeFactory.Target.Architecture == TargetArchitecture.X64) + if (_nodeFactory.Target.Architecture == TargetArchitecture.Wasm32) { - // On Amd64, the 2nd word contains the EndOffset of the runtime function - Debug.Assert(frameInfo.StartOffset != frameInfo.EndOffset); - runtimeFunctionsBuilder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_ADDR32NB, delta: frameInfo.EndOffset); + runtimeFunctionsBuilder.EmitReloc(symbol, RelocType.WASM_TABLE_INDEX_I32, frameIndex); + } + else + { + runtimeFunctionsBuilder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_ADDR32NB, delta: frameInfo.StartOffset + _nodeFactory.Target.CodeDelta); + if (!relocsOnly && _nodeFactory.Target.Architecture == TargetArchitecture.X64) + { + // On Amd64, the 2nd word contains the EndOffset of the runtime function + Debug.Assert(frameInfo.StartOffset != frameInfo.EndOffset); + runtimeFunctionsBuilder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_ADDR32NB, delta: frameInfo.EndOffset); + } } runtimeFunctionsBuilder.EmitReloc(factory.RuntimeFunctionsGCInfo, RelocType.IMAGE_REL_BASED_ADDR32NB, funcletOffsets[frameIndex]); runtimeFunctionIndex++; diff --git a/src/coreclr/vm/readytoruninfo.cpp b/src/coreclr/vm/readytoruninfo.cpp index 6a75a8b1530e6b..e31dc6ca8563d7 100644 --- a/src/coreclr/vm/readytoruninfo.cpp +++ b/src/coreclr/vm/readytoruninfo.cpp @@ -1353,7 +1353,18 @@ PCODE ReadyToRunInfo::GetEntryPoint(MethodDesc * pMD, PrepareCodeConfig* pConfig } _ASSERTE(id < m_nRuntimeFunctions); +#ifndef FEATURE_PORTABLE_ENTRYPOINTS pEntryPoint = dac_cast(GetImage()->GetBase()) + m_pRuntimeFunctions[id].BeginAddress; +#else + // When we have portable entrypoints enable, the R2R image contains actual entrypoints. +#ifdef FEATURE_TIERED_COMPILATION +#error "Portable entry points are not currently supported with tiered compilation, as the interaction between the two is not yet fully worked out." +#endif + PCODE actualEntryPoint; + actualEntryPoint = m_pRuntimeFunctions[id].BeginAddress; + pEntryPoint = pMD->GetTemporaryEntryPoint(); + PortableEntryPoint::SetActualCode(pEntryPoint, actualEntryPoint); +#endif m_pCompositeInfo->SetMethodDescForEntryPointInNativeImage(pEntryPoint, pMD); #ifdef PROFILING_SUPPORTED From ba987552f8687556fdc4f1b8d283384ce6ff7ed4 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Tue, 7 Apr 2026 15:52:08 -0700 Subject: [PATCH 04/15] Adjust PortableEntryPoint logic to actually handle having R2R native entrypoints - This includes some initial work where there is a predefined set of new thunks generated into the coreclr codebase. I expect that we'll build some sort of hardcoded list here for invokes to/from R2R code, and then have R2R supplement the list with extra cases. - Adjust PortableEntryPoint calling convention to match the WASM version --- src/coreclr/runtime/portable/AllocFast.cpp | 4 +- src/coreclr/vm/fcall.h | 75 +++++++ src/coreclr/vm/frames.cpp | 11 +- src/coreclr/vm/frames.h | 28 ++- src/coreclr/vm/gccover.cpp | 2 +- src/coreclr/vm/ilstubcache.cpp | 2 +- src/coreclr/vm/interpexec.cpp | 68 +++++-- src/coreclr/vm/jithelpers.cpp | 6 +- src/coreclr/vm/method.cpp | 23 ++- src/coreclr/vm/precode_portable.cpp | 20 +- src/coreclr/vm/precode_portable.hpp | 23 +++ src/coreclr/vm/prestub.cpp | 35 ++++ .../vm/wasm/callhelpers-interp-to-managed.cpp | 42 ++++ src/coreclr/vm/wasm/helpers.cpp | 190 +++++++++++++++++- .../coreclr/InterpToNativeGenerator.cs | 43 +++- .../coreclr/ManagedToNativeGenerator.cs | 17 +- 16 files changed, 531 insertions(+), 58 deletions(-) diff --git a/src/coreclr/runtime/portable/AllocFast.cpp b/src/coreclr/runtime/portable/AllocFast.cpp index 83b5e3f0b4e9fa..3fdff4403ad994 100644 --- a/src/coreclr/runtime/portable/AllocFast.cpp +++ b/src/coreclr/runtime/portable/AllocFast.cpp @@ -140,7 +140,9 @@ EXTERN_C FCDECL2(Object*, RhpNewArrayFast, MethodTable* pMT, INT_PTR size) EXTERN_C FCDECL2(Object*, RhpNewPtrArrayFast, MethodTable* pMT, INT_PTR size) { WRAPPER_NO_CONTRACT; - return RhpNewArrayFast(pMT, size); + // Since we know the implementation of RhpNewArrayFast is native we don't need to actually + // pass a stack poitner or portable entry point context, so we can just specify 0 for those parameters. + return RhpNewArrayFast(0 /* stack_pointer */, pMT, size, 0 /* portableEntryPointContext */); } EXTERN_C FCDECL1(Object*, RhpNewFast, MethodTable* pMT) diff --git a/src/coreclr/vm/fcall.h b/src/coreclr/vm/fcall.h index f592ada170894c..9e0126eb17ca4d 100644 --- a/src/coreclr/vm/fcall.h +++ b/src/coreclr/vm/fcall.h @@ -159,6 +159,38 @@ #endif // !SWIZZLE_REGARG_ORDER +#elif defined(TARGET_WASM) + +#define FCDECL0(rettype, funcname) rettype F_CALL_CONV funcname(uintptr_t stack_pointer, int32_t portableEntryPointContext) +#define FCDECL1(rettype, funcname, a1) rettype F_CALL_CONV funcname(uintptr_t stack_pointer, a1, int32_t portableEntryPointContext) +#define FCDECL1_V(rettype, funcname, a1) rettype F_CALL_CONV funcname(uintptr_t stack_pointer, a1, int32_t portableEntryPointContext) +#define FCDECL2(rettype, funcname, a1, a2) rettype F_CALL_CONV funcname(uintptr_t stack_pointer, a1, a2, int32_t portableEntryPointContext) +#define FCDECL2VA(rettype, funcname, a1, a2) rettype F_CALL_CONV funcname(uintptr_t stack_pointer, a1, a2, int32_t portableEntryPointContext, ...) +#define FCDECL2_VV(rettype, funcname, a1, a2) rettype F_CALL_CONV funcname(uintptr_t stack_pointer, a1, a2, int32_t portableEntryPointContext) +#define FCDECL2_VI(rettype, funcname, a1, a2) rettype F_CALL_CONV funcname(uintptr_t stack_pointer, a1, a2, int32_t portableEntryPointContext) +#define FCDECL2_IV(rettype, funcname, a1, a2) rettype F_CALL_CONV funcname(uintptr_t stack_pointer, a1, a2, int32_t portableEntryPointContext) +#define FCDECL3(rettype, funcname, a1, a2, a3) rettype F_CALL_CONV funcname(uintptr_t stack_pointer, a1, a2, a3, int32_t portableEntryPointContext) +#define FCDECL3_IIV(rettype, funcname, a1, a2, a3) rettype F_CALL_CONV funcname(uintptr_t stack_pointer, a1, a2, a3, int32_t portableEntryPointContext) +#define FCDECL3_VII(rettype, funcname, a1, a2, a3) rettype F_CALL_CONV funcname(uintptr_t stack_pointer, a1, a2, a3, int32_t portableEntryPointContext) +#define FCDECL3_IVV(rettype, funcname, a1, a2, a3) rettype F_CALL_CONV funcname(uintptr_t stack_pointer, a1, a2, a3, int32_t portableEntryPointContext) +#define FCDECL3_IVI(rettype, funcname, a1, a2, a3) rettype F_CALL_CONV funcname(uintptr_t stack_pointer, a1, a2, a3, int32_t portableEntryPointContext) +#define FCDECL3_VVI(rettype, funcname, a1, a2, a3) rettype F_CALL_CONV funcname(uintptr_t stack_pointer, a1, a2, a3, int32_t portableEntryPointContext) +#define FCDECL3_VVV(rettype, funcname, a1, a2, a3) rettype F_CALL_CONV funcname(uintptr_t stack_pointer, a1, a2, a3, int32_t portableEntryPointContext) +#define FCDECL4(rettype, funcname, a1, a2, a3, a4) rettype F_CALL_CONV funcname(uintptr_t stack_pointer, a1, a2, a3, a4, int32_t portableEntryPointContext) +#define FCDECL5(rettype, funcname, a1, a2, a3, a4, a5) rettype F_CALL_CONV funcname(uintptr_t stack_pointer, a1, a2, a3, a4, a5, int32_t portableEntryPointContext) +#define FCDECL6(rettype, funcname, a1, a2, a3, a4, a5, a6) rettype F_CALL_CONV funcname(uintptr_t stack_pointer, a1, a2, a3, a4, a5, a6, int32_t portableEntryPointContext) +#define FCDECL7(rettype, funcname, a1, a2, a3, a4, a5, a6, a7) rettype F_CALL_CONV funcname(uintptr_t stack_pointer, a1, a2, a3, a4, a5, a6, a7, int32_t portableEntryPointContext) +#define FCDECL8(rettype, funcname, a1, a2, a3, a4, a5, a6, a7, a8) rettype F_CALL_CONV funcname(uintptr_t stack_pointer, a1, a2, a3, a4, a5, a6, a7, a8, int32_t portableEntryPointContext) +#define FCDECL9(rettype, funcname, a1, a2, a3, a4, a5, a6, a7, a8, a9) rettype F_CALL_CONV funcname(uintptr_t stack_pointer, a1, a2, a3, a4, a5, a6, a7, a8, a9, int32_t portableEntryPointContext) +#define FCDECL10(rettype,funcname, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) rettype F_CALL_CONV funcname(uintptr_t stack_pointer, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, int32_t portableEntryPointContext) +#define FCDECL11(rettype,funcname, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11) rettype F_CALL_CONV funcname(uintptr_t stack_pointer, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, int32_t portableEntryPointContext) +#define FCDECL12(rettype,funcname, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12) rettype F_CALL_CONV funcname(uintptr_t stack_pointer, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, int32_t portableEntryPointContext) +#define FCDECL13(rettype,funcname, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13) rettype F_CALL_CONV funcname(uintptr_t stack_pointer, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, int32_t portableEntryPointContext) +#define FCDECL14(rettype,funcname, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14) rettype F_CALL_CONV funcname(uintptr_t stack_pointer, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, int32_t portableEntryPointContext) + +#define FCDECL5_IVI(rettype, funcname, a1, a2, a3, a4, a5) rettype F_CALL_CONV funcname(uintptr_t stack_pointer, a1, a2, a3, a4, a5, int32_t portableEntryPointContext) +#define FCDECL5_VII(rettype, funcname, a1, a2, a3, a4, a5) rettype F_CALL_CONV funcname(uintptr_t stack_pointer, a1, a2, a3, a4, a5, int32_t portableEntryPointContext) + #else // !SWIZZLE_STKARG_ORDER #define FCDECL0(rettype, funcname) rettype F_CALL_CONV funcname() @@ -366,6 +398,37 @@ struct FCSigCheck { #endif // !SWIZZLE_REGARG_ORDER +#elif defined(TARGET_WASM) +#define FCIMPL0(rettype, funcname) rettype funcname(uintptr_t stack_pointer, int32_t portableEntryPointContext) { FCIMPL_PROLOG(funcname) +#define FCIMPL1(rettype, funcname, a1) rettype funcname(uintptr_t stack_pointer, a1, int32_t portableEntryPointContext) { FCIMPL_PROLOG(funcname) +#define FCIMPL1_V(rettype, funcname, a1) rettype funcname(uintptr_t stack_pointer, a1, int32_t portableEntryPointContext) { FCIMPL_PROLOG(funcname) +#define FCIMPL2(rettype, funcname, a1, a2) rettype funcname(uintptr_t stack_pointer, a1, a2, int32_t portableEntryPointContext) { FCIMPL_PROLOG(funcname) +#define FCIMPL2VA(rettype, funcname, a1, a2) rettype funcname(uintptr_t stack_pointer, a1, a2, int32_t portableEntryPointContext, ...) { FCIMPL_PROLOG(funcname) +#define FCIMPL2_VV(rettype, funcname, a1, a2) rettype funcname(uintptr_t stack_pointer, a1, a2, int32_t portableEntryPointContext) { FCIMPL_PROLOG(funcname) +#define FCIMPL2_VI(rettype, funcname, a1, a2) rettype funcname(uintptr_t stack_pointer, a1, a2, int32_t portableEntryPointContext) { FCIMPL_PROLOG(funcname) +#define FCIMPL2_IV(rettype, funcname, a1, a2) rettype funcname(uintptr_t stack_pointer, a1, a2, int32_t portableEntryPointContext) { FCIMPL_PROLOG(funcname) +#define FCIMPL3(rettype, funcname, a1, a2, a3) rettype funcname(uintptr_t stack_pointer, a1, a2, a3, int32_t portableEntryPointContext) { FCIMPL_PROLOG(funcname) +#define FCIMPL3_IIV(rettype, funcname, a1, a2, a3) rettype funcname(uintptr_t stack_pointer, a1, a2, a3, int32_t portableEntryPointContext) { FCIMPL_PROLOG(funcname) +#define FCIMPL3_IVV(rettype, funcname, a1, a2, a3) rettype funcname(uintptr_t stack_pointer, a1, a2, a3, int32_t portableEntryPointContext) { FCIMPL_PROLOG(funcname) +#define FCIMPL3_VII(rettype, funcname, a1, a2, a3) rettype funcname(uintptr_t stack_pointer, a1, a2, a3, int32_t portableEntryPointContext) { FCIMPL_PROLOG(funcname) +#define FCIMPL3_IVI(rettype, funcname, a1, a2, a3) rettype funcname(uintptr_t stack_pointer, a1, a2, a3, int32_t portableEntryPointContext) { FCIMPL_PROLOG(funcname) +#define FCIMPL3_VVI(rettype, funcname, a1, a2, a3) rettype funcname(uintptr_t stack_pointer, a1, a2, a3, int32_t portableEntryPointContext) { FCIMPL_PROLOG(funcname) +#define FCIMPL3_VVV(rettype, funcname, a1, a2, a3) rettype funcname(uintptr_t stack_pointer, a1, a2, a3, int32_t portableEntryPointContext) { FCIMPL_PROLOG(funcname) +#define FCIMPL4(rettype, funcname, a1, a2, a3, a4) rettype funcname(uintptr_t stack_pointer, a1, a2, a3, a4, int32_t portableEntryPointContext) { FCIMPL_PROLOG(funcname) +#define FCIMPL5(rettype, funcname, a1, a2, a3, a4, a5) rettype funcname(uintptr_t stack_pointer, a1, a2, a3, a4, a5, int32_t portableEntryPointContext) { FCIMPL_PROLOG(funcname) +#define FCIMPL6(rettype, funcname, a1, a2, a3, a4, a5, a6) rettype funcname(uintptr_t stack_pointer, a1, a2, a3, a4, a5, a6, int32_t portableEntryPointContext) { FCIMPL_PROLOG(funcname) +#define FCIMPL7(rettype, funcname, a1, a2, a3, a4, a5, a6, a7) rettype funcname(uintptr_t stack_pointer, a1, a2, a3, a4, a5, a6, a7, int32_t portableEntryPointContext) { FCIMPL_PROLOG(funcname) +#define FCIMPL8(rettype, funcname, a1, a2, a3, a4, a5, a6, a7, a8) rettype funcname(uintptr_t stack_pointer, a1, a2, a3, a4, a5, a6, a7, a8, int32_t portableEntryPointContext) { FCIMPL_PROLOG(funcname) +#define FCIMPL9(rettype, funcname, a1, a2, a3, a4, a5, a6, a7, a8, a9) rettype funcname(uintptr_t stack_pointer, a1, a2, a3, a4, a5, a6, a7, a8, a9, int32_t portableEntryPointContext) { FCIMPL_PROLOG(funcname) +#define FCIMPL10(rettype,funcname, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) rettype funcname(uintptr_t stack_pointer, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, int32_t portableEntryPointContext) { FCIMPL_PROLOG(funcname) +#define FCIMPL11(rettype,funcname, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11) rettype funcname(uintptr_t stack_pointer, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, int32_t portableEntryPointContext) { FCIMPL_PROLOG(funcname) +#define FCIMPL12(rettype,funcname, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12) rettype funcname(uintptr_t stack_pointer, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, int32_t portableEntryPointContext) { FCIMPL_PROLOG(funcname) +#define FCIMPL13(rettype,funcname, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13) rettype funcname(uintptr_t stack_pointer, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, int32_t portableEntryPointContext) { FCIMPL_PROLOG(funcname) +#define FCIMPL14(rettype,funcname, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14) rettype funcname(uintptr_t stack_pointer, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, int32_t portableEntryPointContext) { FCIMPL_PROLOG(funcname) + +#define FCIMPL5_IVI(rettype, funcname, a1, a2, a3, a4, a5) rettype funcname(uintptr_t stack_pointer, a1, a2, a3, a4, a5, int32_t portableEntryPointContext) { FCIMPL_PROLOG(funcname) +#define FCIMPL5_VII(rettype, funcname, a1, a2, a3, a4, a5) rettype funcname(uintptr_t stack_pointer, a1, a2, a3, a4, a5, int32_t portableEntryPointContext) { FCIMPL_PROLOG(funcname) + #else // SWIZZLE_STKARG_ORDER #define FCIMPL0(rettype, funcname) rettype funcname() { FCIMPL_PROLOG(funcname) @@ -435,6 +498,18 @@ struct FCSigCheck { #define HCIMPL3(rettype, funcname, a1, a2, a3) rettype F_CALL_CONV funcname(a1, a2, a3) { HCIMPL_PROLOG(funcname) #define HCIMPL3_RAW(rettype, funcname, a1, a2, a3) rettype F_CALL_CONV funcname(a1, a2, a3) { #endif // !SWIZZLE_REGARG_ORDER +#elif defined(TARGET_WASM) + +#define HCIMPL0(rettype, funcname) rettype F_CALL_CONV funcname(uintptr_t stack_pointer, int32_t portableEntryPointContext) { HCIMPL_PROLOG(funcname) +#define HCIMPL1(rettype, funcname, a1) rettype F_CALL_CONV funcname(uintptr_t stack_pointer, a1, int32_t portableEntryPointContext) { HCIMPL_PROLOG(funcname) +#define HCIMPL1_RAW(rettype, funcname, a1) rettype F_CALL_CONV funcname(uintptr_t stack_pointer, a1, int32_t portableEntryPointContext) { +#define HCIMPL1_V(rettype, funcname, a1) rettype F_CALL_CONV funcname(uintptr_t stack_pointer, a1, int32_t portableEntryPointContext) { HCIMPL_PROLOG(funcname) +#define HCIMPL2(rettype, funcname, a1, a2) rettype F_CALL_CONV funcname(uintptr_t stack_pointer, a1, a2, int32_t portableEntryPointContext) { HCIMPL_PROLOG(funcname) +#define HCIMPL2_RAW(rettype, funcname, a1, a2) rettype F_CALL_CONV funcname(uintptr_t stack_pointer, a1, a2, int32_t portableEntryPointContext) { +#define HCIMPL2_VV(rettype, funcname, a1, a2) rettype F_CALL_CONV funcname(uintptr_t stack_pointer, a1, a2, int32_t portableEntryPointContext) { HCIMPL_PROLOG(funcname) +#define HCIMPL3(rettype, funcname, a1, a2, a3) rettype F_CALL_CONV funcname(uintptr_t stack_pointer, a1, a2, a3, int32_t portableEntryPointContext) { HCIMPL_PROLOG(funcname) +#define HCIMPL3_RAW(rettype, funcname, a1, a2, a3) rettype F_CALL_CONV funcname(uintptr_t stack_pointer, a1, a2, a3, int32_t portableEntryPointContext) { + #else // SWIZZLE_STKARG_ORDER #define HCIMPL0(rettype, funcname) rettype F_CALL_CONV funcname() { HCIMPL_PROLOG(funcname) diff --git a/src/coreclr/vm/frames.cpp b/src/coreclr/vm/frames.cpp index 7f08469e8c3b4f..18045f0b7ac46c 100644 --- a/src/coreclr/vm/frames.cpp +++ b/src/coreclr/vm/frames.cpp @@ -1114,7 +1114,7 @@ void DynamicHelperFrame::GcScanRoots_Impl(promote_func *fn, ScanContext* sc) //-------------------------------------------------------------------- // This constructor pushes a new GCFrame on the frame chain. //-------------------------------------------------------------------- -GCFrame::GCFrame(Thread *pThread, OBJECTREF *pObjRefs, UINT numObjRefs, BOOL maybeInterior) +GCFrame::GCFrame(Thread *pThread, OBJECTREF *pObjRefs, UINT numObjRefs, UINT gcFlags) { CONTRACTL { @@ -1130,7 +1130,7 @@ GCFrame::GCFrame(Thread *pThread, OBJECTREF *pObjRefs, UINT numObjRefs, BOOL may #endif #ifdef USE_CHECKED_OBJECTREFS - if (!maybeInterior) { + if (!gcFlags) { UINT i; for(i = 0; i < numObjRefs; i++) Thread::ObjectRefProtected(&pObjRefs[i]); @@ -1159,7 +1159,7 @@ GCFrame::GCFrame(Thread *pThread, OBJECTREF *pObjRefs, UINT numObjRefs, BOOL may m_pObjRefs = pObjRefs; m_numObjRefs = numObjRefs; - m_MaybeInterior = maybeInterior; + m_gcFlags = gcFlags; Push(pThread); } @@ -1314,9 +1314,10 @@ void GCFrame::GcScanRoots(promote_func *fn, ScanContext* sc) for (UINT i = 0; i < m_numObjRefs; i++) { auto fromAddress = OBJECTREF_TO_UNCHECKED_OBJECTREF(m_pObjRefs[i]); - if (m_MaybeInterior) + if (m_gcFlags != 0) { - PromoteCarefully(fn, pRefs + i, sc, GC_CALL_INTERIOR | CHECK_APP_DOMAIN); + _ASSERTE(m_gcFlags & GC_CALL_INTERIOR); + PromoteCarefully(fn, pRefs + i, sc, m_gcFlags | CHECK_APP_DOMAIN); } else { diff --git a/src/coreclr/vm/frames.h b/src/coreclr/vm/frames.h index 949081086455eb..cf62c6d5dca211 100644 --- a/src/coreclr/vm/frames.h +++ b/src/coreclr/vm/frames.h @@ -1602,13 +1602,13 @@ class GCFrame //-------------------------------------------------------------------- // This constructor pushes a new GCFrame on the GC frame chain. //-------------------------------------------------------------------- - GCFrame(OBJECTREF *pObjRefs, UINT numObjRefs, BOOL maybeInterior) - : GCFrame(GetThread(), pObjRefs, numObjRefs, maybeInterior) + GCFrame(OBJECTREF *pObjRefs, UINT numObjRefs, UINT gcFlags) + : GCFrame(GetThread(), pObjRefs, numObjRefs, gcFlags) { WRAPPER_NO_CONTRACT; } - GCFrame(Thread *pThread, OBJECTREF *pObjRefs, UINT numObjRefs, BOOL maybeInterior); + GCFrame(Thread *pThread, OBJECTREF *pObjRefs, UINT numObjRefs, UINT gcFlags); ~GCFrame(); // Push and pop this frame from the thread's stack. @@ -1663,7 +1663,7 @@ class GCFrame PTR_Thread m_pCurThread; PTR_OBJECTREF m_pObjRefs; UINT m_numObjRefs; - BOOL m_MaybeInterior; + UINT m_gcFlags; #ifdef FEATURE_INTERPRETER PTR_VOID m_osStackLocation; #endif @@ -2383,7 +2383,7 @@ class InterpreterFrame : public FramedMethodFrame GCFrame __gcframe( \ (OBJECTREF*)&(ObjRefStruct), \ sizeof(ObjRefStruct)/sizeof(OBJECTREF), \ - FALSE); \ + 0); \ { #define GCPROTECT_BEGIN_THREAD(pThread, ObjRefStruct) do { \ @@ -2391,14 +2391,14 @@ class InterpreterFrame : public FramedMethodFrame pThread, \ (OBJECTREF*)&(ObjRefStruct), \ sizeof(ObjRefStruct)/sizeof(OBJECTREF), \ - FALSE); \ + 0); \ { #define GCPROTECT_ARRAY_BEGIN(ObjRefArray,cnt) do { \ GCFrame __gcframe( \ (OBJECTREF*)&(ObjRefArray), \ cnt * sizeof(ObjRefArray) / sizeof(OBJECTREF), \ - FALSE); \ + 0); \ { #define GCPROTECT_BEGININTERIOR(ObjRefStruct) do { \ @@ -2408,16 +2408,26 @@ class InterpreterFrame : public FramedMethodFrame GCFrame __gcframe( \ (OBJECTREF*)&(ObjRefStruct), \ subjectSize/sizeof(OBJECTREF), \ - TRUE); \ + GC_CALL_INTERIOR); \ { #define GCPROTECT_BEGININTERIOR_ARRAY(ObjRefArray,cnt) do { \ GCFrame __gcframe( \ (OBJECTREF*)&(ObjRefArray), \ cnt, \ - TRUE); \ + GC_CALL_INTERIOR); \ { +// If FEATURE_INTERPRETER is set, the GC side of conservative reporting is enabled, so it is possible to report GC pointers conservatively +#ifdef FEATURE_INTERPRETER + +#define GCPROTECT_BEGINCONSERVATIVE_ARRAY(ObjRefArray,cnt) do { \ + GCFrame __gcframe( \ + (OBJECTREF*)&(ObjRefArray), \ + cnt, \ + GC_CALL_INTERIOR | GC_CALL_PINNED); \ + { +#endif // FEATURE_INTERPRETER #define GCPROTECT_END() \ } \ diff --git a/src/coreclr/vm/gccover.cpp b/src/coreclr/vm/gccover.cpp index 249c3b9c21910f..64f22359891a57 100644 --- a/src/coreclr/vm/gccover.cpp +++ b/src/coreclr/vm/gccover.cpp @@ -878,7 +878,7 @@ void DoGcStress (PCONTEXT regs, NativeCodeVersion nativeCodeVersion) } _ASSERTE(sizeof(OBJECTREF) == sizeof(DWORD_PTR)); - GCFrame gcFrame(pThread, (OBJECTREF*)protRegs, 2, TRUE); + GCFrame gcFrame(pThread, (OBJECTREF*)protRegs, 2, GC_CALL_INTERIOR); MethodDesc *pMD = nativeCodeVersion.GetMethodDesc(); LOG((LF_GCROOTS, LL_EVERYTHING, "GCCOVER: Doing GC at method %s::%s offset 0x%x\n", diff --git a/src/coreclr/vm/ilstubcache.cpp b/src/coreclr/vm/ilstubcache.cpp index d8ae897fbc43f1..9f2370ac1fdd33 100644 --- a/src/coreclr/vm/ilstubcache.cpp +++ b/src/coreclr/vm/ilstubcache.cpp @@ -175,7 +175,6 @@ MethodDesc* ILStubCache::CreateNewMethodDesc(LoaderHeap* pCreationHeap, MethodTa // the no metadata part of the method desc pMD->m_pszMethodName = (PTR_CUTF8)"IL_STUB"; pMD->InitializeFlags(DynamicMethodDesc::FlagPublic | DynamicMethodDesc::FlagIsILStub); - pMD->SetTemporaryEntryPoint(pamTracker); if (isAsync) { @@ -213,6 +212,7 @@ MethodDesc* ILStubCache::CreateNewMethodDesc(LoaderHeap* pCreationHeap, MethodTa pMD->SetFlags(DynamicMethodDesc::FlagStatic); pMD->SetStatic(); } + pMD->SetTemporaryEntryPoint(pamTracker); pMD->m_pResolver = (ILStubResolver*)pamTracker->Track(pCreationHeap->AllocMem(S_SIZE_T(sizeof(ILStubResolver)))); #ifdef _DEBUG diff --git a/src/coreclr/vm/interpexec.cpp b/src/coreclr/vm/interpexec.cpp index 3f6cc68519a608..10f146c37b213e 100644 --- a/src/coreclr/vm/interpexec.cpp +++ b/src/coreclr/vm/interpexec.cpp @@ -624,6 +624,30 @@ void DBG_PrintInterpreterStack() } #endif // _DEBUG +#ifdef FEATURE_PORTABLE_ENTRYPOINTS +#define PORTABLE_ENTRYPOINT_PARAM , 0 +#ifdef TARGET_WASM +#define PORTABLE_ENTRYPOINT_STACK_ARG emscripten_stack_get_current(), +typedef void* (*HELPER_FTN_P_P)(uintptr_t, void*, int32_t); +typedef void* (*HELPER_FTN_BOX_UNBOX)(uintptr_t, MethodTable*, void*, int32_t); +typedef Object* (*HELPER_FTN_NEWARR)(uintptr_t, MethodTable*, intptr_t, int32_t); +typedef void* (*HELPER_FTN_P_PP)(uintptr_t, void*, void*, int32_t); +typedef void (*HELPER_FTN_V_PPP)(uintptr_t, void*, void*, void*, int32_t); +typedef void* (*HELPER_FTN_P_PPIP)(uintptr_t, void*, void*, int32_t, void*, int32_t); +typedef void (*HELPER_FTN_V_PP)(uintptr_t, void*, void*, int32_t); +#else // TARGET_WASM +#define PORTABLE_ENTRYPOINT_STACK_ARG +typedef void* (*HELPER_FTN_P_P)(void*, int32_t); +typedef void* (*HELPER_FTN_BOX_UNBOX)(MethodTable*, void*, int32_t); +typedef Object* (*HELPER_FTN_NEWARR)(MethodTable*, intptr_t, int32_t); +typedef void* (*HELPER_FTN_P_PP)(void*, void*, int32_t); +typedef void (*HELPER_FTN_V_PPP)(void*, void*, void*, int32_t); +typedef void* (*HELPER_FTN_P_PPIP)(void*, void*, int32_t, void*, int32_t); +typedef void (*HELPER_FTN_V_PP)(void*, void*, int32_t); +#endif // TARGET_WASM +#else // FEATURE_PORTABLE_ENTRYPOINTS +#define PORTABLE_ENTRYPOINT_STACK_ARG +#define PORTABLE_ENTRYPOINT_PARAM typedef void* (*HELPER_FTN_P_P)(void*); typedef void* (*HELPER_FTN_BOX_UNBOX)(MethodTable*, void*); typedef Object* (*HELPER_FTN_NEWARR)(MethodTable*, intptr_t); @@ -631,6 +655,7 @@ typedef void* (*HELPER_FTN_P_PP)(void*, void*); typedef void (*HELPER_FTN_V_PPP)(void*, void*, void*); typedef void* (*HELPER_FTN_P_PPIP)(void*, void*, int32_t, void*); typedef void (*HELPER_FTN_V_PP)(void*, void*); +#endif // FEATURE_PORTABLE_ENTRYPOINTS InterpThreadContext::InterpThreadContext() { @@ -745,7 +770,7 @@ template static THelper GetPossiblyIndirectHelper(const Inter } #ifdef FEATURE_PORTABLE_ENTRYPOINTS - if (!PortableEntryPoint::HasNativeEntryPoint((PCODE)addr)) + if (PortableEntryPoint::PrefersInterpreterEntryPoint((PCODE)addr) || !PortableEntryPoint::HasNativeEntryPoint((PCODE)addr)) { _ASSERTE(pILTargetMethod != NULL); *pILTargetMethod = PortableEntryPoint::GetMethodDesc((PCODE)addr); @@ -2594,7 +2619,6 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr ip += 4; break; } - case INTOP_CALL_HELPER_P_P: { void* helperArg = pMethod->pDataItems[ip[3]]; @@ -2615,7 +2639,7 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr } _ASSERTE(helperFtn != NULL); - LOCAL_VAR(ip[1], void*) = helperFtn(helperArg); + LOCAL_VAR(ip[1], void*) = helperFtn(PORTABLE_ENTRYPOINT_STACK_ARG helperArg PORTABLE_ENTRYPOINT_PARAM); ip += 4; break; } @@ -2639,7 +2663,7 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr } _ASSERTE(helperFtn != NULL); - LOCAL_VAR(ip[1], void*) = helperFtn(helperArg); + LOCAL_VAR(ip[1], void*) = helperFtn(PORTABLE_ENTRYPOINT_STACK_ARG helperArg PORTABLE_ENTRYPOINT_PARAM); ip += 4; break; } @@ -2666,7 +2690,7 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr } _ASSERTE(helperFtn != NULL); - LOCAL_VAR(ip[1], void*) = helperFtn(helperArg1, helperArg2); + LOCAL_VAR(ip[1], void*) = helperFtn(PORTABLE_ENTRYPOINT_STACK_ARG helperArg1, helperArg2 PORTABLE_ENTRYPOINT_PARAM); ip += 5; break; } @@ -2693,7 +2717,7 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr } _ASSERTE(helperFtn != NULL); - LOCAL_VAR(ip[1], void*) = helperFtn(helperArg1, helperArg2); + LOCAL_VAR(ip[1], void*) = helperFtn(PORTABLE_ENTRYPOINT_STACK_ARG helperArg1, helperArg2 PORTABLE_ENTRYPOINT_PARAM); ip += 5; break; } @@ -2719,7 +2743,7 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr } _ASSERTE(helperFtn != NULL); - LOCAL_VAR(ip[1], void*) = helperFtn(helperArg); + LOCAL_VAR(ip[1], void*) = helperFtn(PORTABLE_ENTRYPOINT_STACK_ARG helperArg PORTABLE_ENTRYPOINT_PARAM); ip += 5; break; } @@ -2747,7 +2771,7 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr } _ASSERTE(helperFtn != NULL); - LOCAL_VAR(ip[1], void*) = helperFtn(helperArg1, helperArg2); + LOCAL_VAR(ip[1], void*) = helperFtn(PORTABLE_ENTRYPOINT_STACK_ARG helperArg1, helperArg2 PORTABLE_ENTRYPOINT_PARAM); ip += 6; break; } @@ -2775,7 +2799,7 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr } _ASSERTE(helperFtn != NULL); - LOCAL_VAR(ip[1], void*) = helperFtn(helperArg1, helperArg2); + LOCAL_VAR(ip[1], void*) = helperFtn(PORTABLE_ENTRYPOINT_STACK_ARG helperArg1, helperArg2 PORTABLE_ENTRYPOINT_PARAM); ip += 6; break; } @@ -2801,7 +2825,7 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr goto CALL_INTERP_METHOD; } - LOCAL_VAR(ip[1], void*) = helperFtn(helperArg1, helperArg2); + LOCAL_VAR(ip[1], void*) = helperFtn(PORTABLE_ENTRYPOINT_STACK_ARG helperArg1, helperArg2 PORTABLE_ENTRYPOINT_PARAM); ip += 5; break; } @@ -2831,7 +2855,7 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr } _ASSERTE(helperFtn != NULL); - helperFtn(helperArg1, helperArg2, helperArg3); + helperFtn(PORTABLE_ENTRYPOINT_STACK_ARG helperArg1, helperArg2, helperArg3 PORTABLE_ENTRYPOINT_PARAM); ip += 6; break; } @@ -2860,7 +2884,7 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr } _ASSERTE(helperFtn != NULL); - helperFtn(helperArg1, helperArg2, helperArg3); + helperFtn(PORTABLE_ENTRYPOINT_STACK_ARG helperArg1, helperArg2, helperArg3 PORTABLE_ENTRYPOINT_PARAM); ip += 5; break; } @@ -2887,7 +2911,7 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr } _ASSERTE(helperFtn != NULL); - helperFtn(helperArg1, helperArg2); + helperFtn(PORTABLE_ENTRYPOINT_STACK_ARG helperArg1, helperArg2 PORTABLE_ENTRYPOINT_PARAM); ip += 4; break; } @@ -2912,7 +2936,7 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr } _ASSERTE(helperFtn != NULL); - helperFtn(helperArg1, helperArg2); + helperFtn(PORTABLE_ENTRYPOINT_STACK_ARG helperArg1, helperArg2 PORTABLE_ENTRYPOINT_PARAM); ip += 4; break; } @@ -2940,7 +2964,7 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr } _ASSERTE(helperFtn != NULL); - helperFtn(helperArg1, helperArg2, helperArg3); + helperFtn(PORTABLE_ENTRYPOINT_STACK_ARG helperArg1, helperArg2, helperArg3 PORTABLE_ENTRYPOINT_PARAM); ip += 5; break; } @@ -3046,7 +3070,7 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr // through CALL_INTERP_METHOD. It is a small optimization and also necessary // for correctness for Newobj allocator helpers where the MethodDesc does not // represent the actual entrypoint. - if (!PortableEntryPoint::HasNativeEntryPoint(calliFunctionPointer)) + if (PortableEntryPoint::PrefersInterpreterEntryPoint(calliFunctionPointer) || !PortableEntryPoint::HasNativeEntryPoint(calliFunctionPointer)) goto CALL_INTERP_METHOD; MetaSig sig(targetMethod); @@ -3491,7 +3515,7 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr } // private static ref byte Unbox(MethodTable* toTypeHnd, object obj) - LOCAL_VAR(dreg, void*) = helper(pMT, src); + LOCAL_VAR(dreg, void*) = helper(PORTABLE_ENTRYPOINT_STACK_ARG pMT, src PORTABLE_ENTRYPOINT_PARAM); ip += 5; break; @@ -3534,7 +3558,7 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr } // private static ref byte Unbox(MethodTable* toTypeHnd, object obj) - LOCAL_VAR(dreg, void*) = helper(pMTBoxedObj, src); + LOCAL_VAR(dreg, void*) = helper(PORTABLE_ENTRYPOINT_STACK_ARG pMTBoxedObj, src PORTABLE_ENTRYPOINT_PARAM); ip += 6; break; @@ -3558,7 +3582,7 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr MethodTable* arrayClsHnd = (MethodTable*)pMethod->pDataItems[ip[3]]; HELPER_FTN_NEWARR helper = GetPossiblyIndirectHelper(pMethod, ip[4]); - Object* arr = helper(arrayClsHnd, (intptr_t)length); + Object* arr = helper(PORTABLE_ENTRYPOINT_STACK_ARG arrayClsHnd, (intptr_t)length PORTABLE_ENTRYPOINT_PARAM); LOCAL_VAR(ip[1], OBJECTREF) = ObjectToOBJECTREF(arr); ip += 5; @@ -3573,7 +3597,7 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr HELPER_FTN_NEWARR helper = GetPossiblyIndirectHelper(pMethod, ip[4]); - Object* arr = helper(arrayClsHnd, (intptr_t)length); + Object* arr = helper(PORTABLE_ENTRYPOINT_STACK_ARG arrayClsHnd, (intptr_t)length PORTABLE_ENTRYPOINT_PARAM); LOCAL_VAR(ip[1], OBJECTREF) = ObjectToOBJECTREF(arr); ip += 6; @@ -4211,12 +4235,12 @@ do \ { uintptr_t context = LOCAL_VAR(ip[2], uintptr_t); ip += ipAdjust; - *pDest = ObjectToOBJECTREF((Object*)helperFtnGeneric(OBJECTREFToObject(chainedContinuation), pContinuationType, pAsyncSuspendData->keepAliveOffset, (void*)context)); + *pDest = ObjectToOBJECTREF((Object*)helperFtnGeneric(PORTABLE_ENTRYPOINT_STACK_ARG OBJECTREFToObject(chainedContinuation), pContinuationType, pAsyncSuspendData->keepAliveOffset, (void*)context PORTABLE_ENTRYPOINT_PARAM)); } else { ip += ipAdjust; - *pDest = ObjectToOBJECTREF((Object*)helperFtn(OBJECTREFToObject(chainedContinuation), pContinuationType)); + *pDest = ObjectToOBJECTREF((Object*)helperFtn(PORTABLE_ENTRYPOINT_STACK_ARG OBJECTREFToObject(chainedContinuation), pContinuationType PORTABLE_ENTRYPOINT_PARAM)); } break; } diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp index 328c99bb96516d..1cd01e110f63c0 100644 --- a/src/coreclr/vm/jithelpers.cpp +++ b/src/coreclr/vm/jithelpers.cpp @@ -858,21 +858,21 @@ HCIMPLEND HCIMPL1(void, IL_Throw, Object* obj) { FCALL_CONTRACT; - IL_Throw_Impl(obj, NULL); + IL_Throw_Impl(0, obj, NULL, 0); } HCIMPLEND HCIMPL0(void, IL_Rethrow) { FCALL_CONTRACT; - IL_Rethrow_Impl(NULL); + IL_Rethrow_Impl(0, NULL, 0); } HCIMPLEND HCIMPL1(void, IL_ThrowExact, Object* obj) { FCALL_CONTRACT; - IL_ThrowExact_Impl(obj, NULL); + IL_ThrowExact_Impl(0, obj, NULL, 0); } HCIMPLEND #endif // TARGET_WASM diff --git a/src/coreclr/vm/method.cpp b/src/coreclr/vm/method.cpp index 9295fb3d38e534..15e8e39179d659 100644 --- a/src/coreclr/vm/method.cpp +++ b/src/coreclr/vm/method.cpp @@ -2830,6 +2830,10 @@ void MethodDesc::EnsureTemporaryEntryPoint() } } +#ifdef FEATURE_PORTABLE_ENTRYPOINTS +void* GetPortableEntryPointToInterpreterThunk(MethodDesc *pMD); +#endif + void MethodDesc::EnsureTemporaryEntryPointCore(AllocMemTracker *pamTracker) { CONTRACTL @@ -2852,7 +2856,24 @@ void MethodDesc::EnsureTemporaryEntryPointCore(AllocMemTracker *pamTracker) #ifdef FEATURE_PORTABLE_ENTRYPOINTS PortableEntryPoint* portableEntryPoint = (PortableEntryPoint*)pamTrackerPrecode->Track( GetLoaderAllocator()->GetHighFrequencyHeap()->AllocMem(S_SIZE_T{ sizeof(PortableEntryPoint) })); - portableEntryPoint->Init(this); + + // WASM-TODO! This only handling the R2R to interpreter case for well known signatures. + // Eventually we will need to handle arbitrary signatures by looking in the loaded list of R2R modules + // as well as recording when we couldn't find something, in case another R2R module might be loaded + // later which has an R2R to interpreter stub for that given signature. + void* pPortableEntryPointToInterpreter = GetPortableEntryPointToInterpreterThunk(this); + + if (pPortableEntryPointToInterpreter != nullptr) + { + portableEntryPoint->Init(pPortableEntryPointToInterpreter, this); + } + else + { + portableEntryPoint->Init(this); + } + // If we find actual code, we will remove this flag, but we want to prefer the interpreter entry point + // until then to allow helpers to work for methods that haven't tried to get an entry point yet. + portableEntryPoint->SetPrefersInterpreterEntryPoint(); entryPoint = (PCODE)portableEntryPoint; #else // !FEATURE_PORTABLE_ENTRYPOINTS diff --git a/src/coreclr/vm/precode_portable.cpp b/src/coreclr/vm/precode_portable.cpp index 25ba620324cc60..6e159d4b28d8d1 100644 --- a/src/coreclr/vm/precode_portable.cpp +++ b/src/coreclr/vm/precode_portable.cpp @@ -43,8 +43,16 @@ void PortableEntryPoint::SetActualCode(PCODE addr, PCODE actualCode) PortableEntryPoint* portableEntryPoint = ToPortableEntryPoint(addr); _ASSERTE_ALL_BUILDS(actualCode != (PCODE)NULL); - // This is a lock free write. It can either be NULL or was already set to the same value. - _ASSERTE(!portableEntryPoint->HasNativeCode() || portableEntryPoint->_pActualCode == (void*)PCODEToPINSTR(actualCode)); + // This is a lock free write. It can either be NULL, was already set to the same value, or be the interpreter entrypoint. + _ASSERTE(!portableEntryPoint->HasNativeCode() || portableEntryPoint->_pActualCode == (void*)PCODEToPINSTR(actualCode) || portableEntryPoint->PrefersInterpreterEntryPoint()); + + if (portableEntryPoint->PrefersInterpreterEntryPoint()) + { + // We can "upgrade" a portable entrypoint from the interpreter entry point to the actual code. If we do so + // we need to clear the PrefersInterpreterEntryPoint flag to allow future callers to use the actual code. + portableEntryPoint->ClearPrefersInterpreterEntryPoint(); + } + portableEntryPoint->_pActualCode = (void*)PCODEToPINSTR(actualCode); } @@ -76,6 +84,13 @@ void PortableEntryPoint::SetInterpreterData(PCODE addr, PCODE interpreterData) portableEntryPoint->_pInterpreterData = (void*)PCODEToPINSTR(interpreterData); } +bool PortableEntryPoint::PrefersInterpreterEntryPoint(PCODE addr) +{ + LIMITED_METHOD_CONTRACT; + PortableEntryPoint* portableEntryPoint = ToPortableEntryPoint(addr); + return portableEntryPoint->PrefersInterpreterEntryPoint(); +} + #ifdef _DEBUG bool PortableEntryPoint::IsValid() const { @@ -119,7 +134,6 @@ void PortableEntryPoint::Init(void* nativeEntryPoint) void PortableEntryPoint::Init(void* nativeEntryPoint, MethodDesc* pMD) { LIMITED_METHOD_CONTRACT; - _ASSERTE(nativeEntryPoint != NULL); _ASSERTE(pMD != NULL); _pActualCode = nativeEntryPoint; _pMD = pMD; diff --git a/src/coreclr/vm/precode_portable.hpp b/src/coreclr/vm/precode_portable.hpp index c7d35c442bc013..2240a4d024b6a1 100644 --- a/src/coreclr/vm/precode_portable.hpp +++ b/src/coreclr/vm/precode_portable.hpp @@ -21,6 +21,7 @@ class PortableEntryPoint final static MethodDesc* GetMethodDesc(PCODE addr); static void* GetInterpreterData(PCODE addr); static void SetInterpreterData(PCODE addr, PCODE interpreterData); + static bool PrefersInterpreterEntryPoint(PCODE addr); private: Volatile _pActualCode; @@ -32,6 +33,7 @@ class PortableEntryPoint final kNone = 0, kUnmanagedCallersOnly_Has = 0x1, kUnmanagedCallersOnly_Checked = 0x2, + kPrefersInterpreterEntryPoint = 0x4, }; Volatile _flags; @@ -78,6 +80,27 @@ class PortableEntryPoint final // pActualCode is a managed calling convention -> interpreter executor call stub in this case. return _pInterpreterData != nullptr && _pActualCode != nullptr; } + + bool PrefersInterpreterEntryPoint() const + { + LIMITED_METHOD_CONTRACT; + _ASSERTE(IsValid()); + return (_flags & kPrefersInterpreterEntryPoint) != 0; + } + + void SetPrefersInterpreterEntryPoint() + { + LIMITED_METHOD_CONTRACT; + _ASSERTE(IsValid()); + _flags = (_flags | kPrefersInterpreterEntryPoint); // TODO, use interlock operation here + } + + void ClearPrefersInterpreterEntryPoint() + { + LIMITED_METHOD_CONTRACT; + _ASSERTE(IsValid()); + _flags = (_flags & ~kPrefersInterpreterEntryPoint); // TODO, use interlock operation here + } friend struct ::cdac_data; }; template<> diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index 7fcaae1f738923..df60056e13390e 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -2078,6 +2078,41 @@ void ExecuteInterpretedMethodWithArgs(TADDR targetIp, int8_t* args, size_t argSi (void)ExecuteInterpretedMethod(&block, (TADDR)targetIp, retBuff); } +#ifdef FEATURE_PORTABLE_ENTRYPOINTS +PCODE ExecuteInterpretedMethodWithArgs_PortableEntryPoint_Worker(PCODE portableEntrypoint, int8_t* args, size_t argsSize) +{ + MethodDesc* pMethod = PortableEntryPoint::GetMethodDesc(portableEntrypoint); + InterpByteCodeStart* targetIp = pMethod->GetInterpreterCode(); + if (targetIp == NULL) + { + GCPROTECT_BEGINCONSERVATIVE_ARRAY(args, (UINT)(argsSize/sizeof(OBJECTREF))); + GCX_PREEMP(); + (void)pMethod->DoPrestub(NULL /* MethodTable */, CallerGCMode::Coop); + targetIp = pMethod->GetInterpreterCode(); + GCPROTECT_END(); + } + + _ASSERTE((PCODE)targetIp == (PCODE)PortableEntryPoint::GetInterpreterData(portableEntrypoint)); + + return (PCODE)targetIp; +} + +void ExecuteInterpretedMethodWithArgs_PortableEntryPoint(PCODE portableEntrypoint, int8_t* args, size_t argsSize, void* retBuff) +{ + PCODE targetIp; + + if (!PortableEntryPoint::HasInterpreterData(portableEntrypoint)) + { + targetIp = ExecuteInterpretedMethodWithArgs_PortableEntryPoint_Worker(portableEntrypoint, args, argsSize); + } + else + { + targetIp = (PCODE)PortableEntryPoint::GetInterpreterData(portableEntrypoint); + } + ExecuteInterpretedMethodWithArgs((TADDR)targetIp, args, argsSize, retBuff, (PCODE)&ExecuteInterpretedMethodWithArgs_PortableEntryPoint); +} +#endif // FEATURE_PORTABLE_ENTRYPOINTS + extern "C" void ExecuteInterpretedMethodFromUnmanaged(MethodDesc* pMD, int8_t* args, size_t argSize, int8_t* ret, PCODE callerIp) { _ASSERTE(pMD != NULL); diff --git a/src/coreclr/vm/wasm/callhelpers-interp-to-managed.cpp b/src/coreclr/vm/wasm/callhelpers-interp-to-managed.cpp index e3cb2fbcbab2c3..a36b5b9eb07430 100644 --- a/src/coreclr/vm/wasm/callhelpers-interp-to-managed.cpp +++ b/src/coreclr/vm/wasm/callhelpers-interp-to-managed.cpp @@ -139,6 +139,12 @@ namespace *((int32_t*)pRet) = (*fptr)(ARG_I32(0), ARG_I32(1), ARG_I32(2), ARG_IND(3), ARG_I32(4)); } + static void CallFunc_I32_I32_I32_RetI32_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) + { + int32_t (*fptr)(uintptr_t, int32_t, int32_t, int32_t, PCODE) = (int32_t (*)(uintptr_t, int32_t, int32_t, int32_t, PCODE))pcode; + *((int32_t*)pRet) = (*fptr)(emscripten_stack_get_current(), ARG_I32(0), ARG_I32(1), ARG_I32(2), pPortableEntryPointContext); + } + static void CallFunc_I32_I32_I64_RetI32(PCODE pcode, int8_t* pArgs, int8_t* pRet) { int32_t (*fptr)(int32_t, int32_t, int64_t) = (int32_t (*)(int32_t, int32_t, int64_t))pcode; @@ -157,6 +163,12 @@ namespace *((int32_t*)pRet) = (*fptr)(ARG_I32(0), ARG_I32(1), ARG_IND(2), ARG_I32(3), ARG_I32(4), ARG_IND(5)); } + static void CallFunc_I32_I32_RetI32_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) + { + int32_t (*fptr)(uintptr_t, int32_t, int32_t, PCODE) = (int32_t (*)(uintptr_t, int32_t, int32_t, PCODE))pcode; + *((int32_t*)pRet) = (*fptr)(emscripten_stack_get_current(), ARG_I32(0), ARG_I32(1), pPortableEntryPointContext); + } + static void CallFunc_I32_I64_RetI32(PCODE pcode, int8_t* pArgs, int8_t* pRet) { int32_t (*fptr)(int32_t, int64_t) = (int32_t (*)(int32_t, int64_t))pcode; @@ -199,6 +211,12 @@ namespace *((int32_t*)pRet) = (*fptr)(ARG_I32(0), ARG_IND(1), ARG_I32(2), ARG_I32(3), ARG_I32(4)); } + static void CallFunc_I32_RetI32_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) + { + int32_t (*fptr)(uintptr_t, int32_t, PCODE) = (int32_t (*)(uintptr_t, int32_t, PCODE))pcode; + *((int32_t*)pRet) = (*fptr)(emscripten_stack_get_current(), ARG_I32(0), pPortableEntryPointContext); + } + static void CallFunc_I64_I32_I64_I32_RetI32(PCODE pcode, int8_t* pArgs, int8_t* pRet) { int32_t (*fptr)(int64_t, int32_t, int64_t, int32_t) = (int32_t (*)(int64_t, int32_t, int64_t, int32_t))pcode; @@ -277,6 +295,12 @@ namespace *((int32_t*)pRet) = (*fptr)(ARG_IND(0), ARG_IND(1), ARG_IND(2)); } + static void CallFunc_Void_RetI32_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) + { + int32_t (*fptr)(uintptr_t, PCODE) = (int32_t (*)(uintptr_t, PCODE))pcode; + *((int32_t*)pRet) = (*fptr)(emscripten_stack_get_current(), pPortableEntryPointContext); + } + static void CallFunc_Void_RetI64(PCODE pcode, int8_t* pArgs, int8_t* pRet) { int64_t (*fptr)() = (int64_t (*)())pcode; @@ -403,12 +427,24 @@ namespace (*fptr)(ARG_I32(0), ARG_I32(1), ARG_IND(2), ARG_IND(3), ARG_I32(4), ARG_I32(5)); } + static void CallFunc_I32_I32_RetVoid_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) + { + void (*fptr)(uintptr_t, int32_t, int32_t, PCODE) = (void (*)(uintptr_t, int32_t, int32_t, PCODE))pcode; + (*fptr)(emscripten_stack_get_current(), ARG_I32(0), ARG_I32(1), pPortableEntryPointContext); + } + static void CallFunc_I32_IND_I32_RetVoid(PCODE pcode, int8_t* pArgs, int8_t* pRet) { void (*fptr)(int32_t, int32_t, int32_t) = (void (*)(int32_t, int32_t, int32_t))pcode; (*fptr)(ARG_I32(0), ARG_IND(1), ARG_I32(2)); } + static void CallFunc_I32_RetVoid_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) + { + void (*fptr)(uintptr_t, int32_t, PCODE) = (void (*)(uintptr_t, int32_t, PCODE))pcode; + (*fptr)(emscripten_stack_get_current(), ARG_I32(0), pPortableEntryPointContext); + } + static void CallFunc_I64_RetVoid(PCODE pcode, int8_t* pArgs, int8_t* pRet) { void (*fptr)(int64_t) = (void (*)(int64_t))pcode; @@ -497,9 +533,11 @@ const StringToWasmSigThunk g_wasmThunks[] = { { "iiiiiiiiiiiiiii", (void*)&CallFunc_I32_I32_I32_I32_I32_I32_I32_I32_I32_I32_I32_I32_I32_I32_RetI32 }, { "iiiil", (void*)&CallFunc_I32_I32_I32_I64_RetI32 }, { "iiiini", (void*)&CallFunc_I32_I32_I32_IND_I32_RetI32 }, + { "iiiip", (void*)&CallFunc_I32_I32_I32_RetI32_PE }, { "iiil", (void*)&CallFunc_I32_I32_I64_RetI32 }, { "iiinii", (void*)&CallFunc_I32_I32_IND_I32_I32_RetI32 }, { "iiiniin", (void*)&CallFunc_I32_I32_IND_I32_I32_IND_RetI32 }, + { "iiip", (void*)&CallFunc_I32_I32_RetI32_PE }, { "iil", (void*)&CallFunc_I32_I64_RetI32 }, { "iili", (void*)&CallFunc_I32_I64_I32_RetI32 }, { "iiliiil", (void*)&CallFunc_I32_I64_I32_I32_I32_I64_RetI32 }, @@ -507,6 +545,7 @@ const StringToWasmSigThunk g_wasmThunks[] = { { "iilli", (void*)&CallFunc_I32_I64_I64_I32_RetI32 }, { "iini", (void*)&CallFunc_I32_IND_I32_RetI32 }, { "iiniii", (void*)&CallFunc_I32_IND_I32_I32_I32_RetI32 }, + { "iip", (void*)&CallFunc_I32_RetI32_PE }, { "ilili", (void*)&CallFunc_I64_I32_I64_I32_RetI32 }, { "in", (void*)&CallFunc_IND_RetI32 }, { "ini", (void*)&CallFunc_IND_I32_RetI32 }, @@ -520,6 +559,7 @@ const StringToWasmSigThunk g_wasmThunks[] = { { "innii", (void*)&CallFunc_IND_IND_I32_I32_RetI32 }, { "innin", (void*)&CallFunc_IND_IND_I32_IND_RetI32 }, { "innn", (void*)&CallFunc_IND_IND_IND_RetI32 }, + { "ip", (void*)&CallFunc_Void_RetI32_PE }, { "l", (void*)&CallFunc_Void_RetI64 }, { "li", (void*)&CallFunc_I32_RetI64 }, { "liii", (void*)&CallFunc_I32_I32_I32_RetI64 }, @@ -541,7 +581,9 @@ const StringToWasmSigThunk g_wasmThunks[] = { { "viiinni", (void*)&CallFunc_I32_I32_I32_IND_IND_I32_RetVoid }, { "viinni", (void*)&CallFunc_I32_I32_IND_IND_I32_RetVoid }, { "viinnii", (void*)&CallFunc_I32_I32_IND_IND_I32_I32_RetVoid }, + { "viip", (void*)&CallFunc_I32_I32_RetVoid_PE }, { "vini", (void*)&CallFunc_I32_IND_I32_RetVoid }, + { "vip", (void*)&CallFunc_I32_RetVoid_PE }, { "vl", (void*)&CallFunc_I64_RetVoid }, { "vn", (void*)&CallFunc_IND_RetVoid }, { "vni", (void*)&CallFunc_IND_I32_RetVoid }, diff --git a/src/coreclr/vm/wasm/helpers.cpp b/src/coreclr/vm/wasm/helpers.cpp index afb6d2f739a8b8..5cd406885bb780 100644 --- a/src/coreclr/vm/wasm/helpers.cpp +++ b/src/coreclr/vm/wasm/helpers.cpp @@ -6,6 +6,113 @@ #include #include "callhelpers.hpp" #include "shash.h" +#include "callingconvention.h" +#include "cgensys.h" +#include "readytorun.h" + +void ExecuteInterpretedMethodWithArgs_PortableEntryPoint(PCODE portableEntrypoint, int8_t* args, size_t argsSize, void* retBuff); +// ------------------------------------------------- +// Logic that will eventually mostly be pregenerated for R2R to interpreter code +// ------------------------------------------------- +namespace +{ + static void CallInterpreter_I32_RetVoid(uintptr_t stackArg, int32_t arg0, PCODE portableEntrypoint) + { + int64_t args[1] = { (int64_t)arg0 }; + + void * result = NULL; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, (int8_t*)args, sizeof(args), (int8_t*)&result); + return; + } + static int32_t CallInterpreter_RetI32(uintptr_t stackArg, PCODE portableEntrypoint) + { + void * result = NULL; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, nullptr, 0, (int8_t*)&result); + return (int32_t)result; + } + static int32_t CallInterpreter_I32_RetI32(uintptr_t stackArg, int32_t arg0, PCODE portableEntrypoint) + { + int64_t args[1] = { (int64_t)arg0 }; + + void * result = NULL; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, (int8_t*)args, sizeof(args), (int8_t*)&result); + return (int32_t)result; + } + static int32_t CallInterpreter_I32_I32_RetI32(uintptr_t stackArg, int32_t arg0, int32_t arg1, PCODE portableEntrypoint) + { + int64_t args[2] = { (int64_t)arg0, (int64_t)arg1 }; + + void * result = NULL; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, (int8_t*)args, sizeof(args), (int8_t*)&result); + return (int32_t)result; + } + static int32_t CallInterpreter_I32_I32_I32_RetI32(uintptr_t stackArg, int32_t arg0, int32_t arg1, int32_t arg2, PCODE portableEntrypoint) + { + int64_t args[3] = { (int64_t)arg0, (int64_t)arg1, (int64_t)arg2 }; + + void * result = NULL; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, (int8_t*)args, sizeof(args), (int8_t*)&result); + return (int32_t)result; + } + static int32_t CallInterpreter_I32_I32_I32_I32_RetI32(uintptr_t stackArg, int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3,PCODE portableEntrypoint) + { + int64_t args[4] = { (int64_t)arg0, (int64_t)arg1, (int64_t)arg2, (int64_t)arg3 }; + + void * result = NULL; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, (int8_t*)args, sizeof(args), (int8_t*)&result); + return (int32_t)result; + } + static int32_t CallInterpreter_I32_I32_I32_I32_I32_RetI32(uintptr_t stackArg, int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4, PCODE portableEntrypoint) + { + int64_t args[5] = { (int64_t)arg0, (int64_t)arg1, (int64_t)arg2, (int64_t)arg3, (int64_t)arg4 }; + + void * result = NULL; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, (int8_t*)args, sizeof(args), (int8_t*)&result); + return (int32_t)result; + } + static int32_t CallInterpreter_I32_I32_I32_I32_I32_I32_RetI32(uintptr_t stackArg, int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4, int32_t arg5, PCODE portableEntrypoint) + { + int64_t args[6] = { (int64_t)arg0, (int64_t)arg1, (int64_t)arg2, (int64_t)arg3, (int64_t)arg4, (int64_t)arg5 }; + + void * result = NULL; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, (int8_t*)args, sizeof(args), (int8_t*)&result); + return (int32_t)result; + } + static int32_t CallInterpreter_I32_I32_I32_I32_I32_I32_I32_RetI32(uintptr_t stackArg, int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4, int32_t arg5, int32_t arg6, PCODE portableEntrypoint) + { + int64_t args[7] = { (int64_t)arg0, (int64_t)arg1, (int64_t)arg2, (int64_t)arg3, (int64_t)arg4, (int64_t)arg5, (int64_t)arg6 }; + + void * result = NULL; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, (int8_t*)args, sizeof(args), (int8_t*)&result); + return (int32_t)result; + } + static int32_t CallInterpreter_I32_I32_I32_I32_I32_I32_I32_I32_RetI32(uintptr_t stackArg, int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4, int32_t arg5, int32_t arg6, int32_t arg7, PCODE portableEntrypoint) + { + int64_t args[8] = { (int64_t)arg0, (int64_t)arg1, (int64_t)arg2, (int64_t)arg3, (int64_t)arg4, (int64_t)arg5, (int64_t)arg6, (int64_t)arg7 }; + + void * result = NULL; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, (int8_t*)args, sizeof(args), (int8_t*)&result); + return (int32_t)result; + } +} + +const StringToWasmSigThunk g_wasmPortableEntryPointThunks[] = { + { "vip", (void*)&CallInterpreter_I32_RetVoid }, + { "ip", (void*)&CallInterpreter_RetI32 }, + { "iip", (void*)&CallInterpreter_I32_RetI32 }, + { "iiip", (void*)&CallInterpreter_I32_I32_RetI32 }, + { "iiiip", (void*)&CallInterpreter_I32_I32_I32_RetI32 }, + { "iiiiip", (void*)&CallInterpreter_I32_I32_I32_I32_RetI32 }, + { "iiiiiip", (void*)&CallInterpreter_I32_I32_I32_I32_I32_RetI32 }, + { "iiiiiiip", (void*)&CallInterpreter_I32_I32_I32_I32_I32_I32_RetI32 }, + { "iiiiiiiip", (void*)&CallInterpreter_I32_I32_I32_I32_I32_I32_I32_RetI32 }, + { "iiiiiiiiip", (void*)&CallInterpreter_I32_I32_I32_I32_I32_I32_I32_I32_RetI32 }, +}; + +const size_t g_wasmPortableEntryPointThunksCount = sizeof(g_wasmPortableEntryPointThunks) / sizeof(g_wasmPortableEntryPointThunks[0]); +// ------------------------------------------------- +// END Logic that will eventually mostly be pregenerated for R2R to interpreter code END +// ------------------------------------------------- extern "C" void STDCALL CallCountingStubCode() { @@ -422,9 +529,8 @@ void InvokeCalliStub(CalliStubParam* pParam) _ASSERTE(pParam->ftn != (PCODE)NULL); _ASSERTE(pParam->cookie != NULL); - // WASM-TODO: Reconcile calling conventions for managed calli. PCODE actualFtn = (PCODE)PortableEntryPoint::GetActualCode(pParam->ftn); - ((void(*)(PCODE, int8_t*, int8_t*))pParam->cookie)(actualFtn, pParam->pArgs, pParam->pRet); + ((void(*)(PCODE, int8_t*, int8_t*, PCODE))pParam->cookie)(actualFtn, pParam->pArgs, pParam->pRet, pParam->ftn); } void InvokeUnmanagedCalli(PCODE ftn, void *cookie, int8_t *pArgs, int8_t *pRet) @@ -555,6 +661,15 @@ namespace keyBuffer[pos++] = GetTypeCode(ConvertibleTo(argType, sig, false /* isReturn */)); } + // Add the portable entrypoint parameter + if (sig.GetCallingConvention() == IMAGE_CEE_CS_CALLCONV_DEFAULT) + { + if (pos >= maxSize) + return false; + + keyBuffer[pos++] = 'p'; + } + if (pos >= maxSize) return false; @@ -572,6 +687,7 @@ namespace typedef MapSHash> StringToWasmSigThunkHash; static StringToWasmSigThunkHash* thunkCache = nullptr; + static StringToWasmSigThunkHash* portableEntrypointThunkCache = nullptr; void* LookupThunk(const char* key) { @@ -598,13 +714,37 @@ namespace return success ? thunk : nullptr; } + void* LookupPortableEntryPointThunk(const char* key) + { + StringToWasmSigThunkHash* table = VolatileLoad(&portableEntrypointThunkCache); + if (table == nullptr) + { + StringToWasmSigThunkHash* newTable = new StringToWasmSigThunkHash(); + newTable->Reallocate(g_wasmPortableEntryPointThunksCount * StringToWasmSigThunkHash::s_density_factor_denominator / StringToWasmSigThunkHash::s_density_factor_numerator + 1); + for (size_t i = 0; i < g_wasmPortableEntryPointThunksCount; i++) + { + newTable->Add(g_wasmPortableEntryPointThunks[i].key, g_wasmPortableEntryPointThunks[i].value); + } + + if (InterlockedCompareExchangeT(&portableEntrypointThunkCache, newTable, nullptr) != nullptr) + { + // Another thread won the race, discard ours + delete newTable; + } + table = thunkCache; + } + + void* thunk; + bool success = table->Lookup(key, &thunk); + return success ? thunk : nullptr; + } + // This is a simple signature computation routine for signatures currently supported in the wasm environment. void* ComputeCalliSigThunk(MetaSig& sig) { STANDARD_VM_CONTRACT; _ASSERTE(sizeof(int32_t) == sizeof(void*)); - // Ensure an unmanaged calling convention. BYTE callConv = sig.GetCallingConvention(); switch (callConv) { @@ -618,7 +758,7 @@ namespace return NULL; } - uint32_t keyBufferLen = sig.NumFixedArgs() + (sig.HasThis() ? 1 : 0) + 2; + uint32_t keyBufferLen = sig.NumFixedArgs() + (sig.HasThis() ? 1 : 0) + 2 + ((callConv == IMAGE_CEE_CS_CALLCONV_DEFAULT) ? 1 : 0); char* keyBuffer = (char*)alloca(keyBufferLen); if (!GetSignatureKey(sig, keyBuffer, keyBufferLen)) return NULL; @@ -631,6 +771,33 @@ namespace return thunk; } + void* ComputePortableEntryPointToInterpreterThunk(MetaSig& sig) + { + STANDARD_VM_CONTRACT; + _ASSERTE(sizeof(int32_t) == sizeof(void*)); + + BYTE callConv = sig.GetCallingConvention(); + switch (callConv) + { + // Only allowed for default calling convention since that's the only one currently supported for portable entry points, but we may want to support more in the future. + case IMAGE_CEE_CS_CALLCONV_DEFAULT: + break; + default: + return NULL; + } + uint32_t keyBufferLen = sig.NumFixedArgs() + (sig.HasThis() ? 1 : 0) + 2 + 1; // +1 for the 'p' suffix to indicate portable entry point + char* keyBuffer = (char*)alloca(keyBufferLen); + if (!GetSignatureKey(sig, keyBuffer, keyBufferLen)) + return NULL; + + void* thunk = LookupPortableEntryPointThunk(keyBuffer); +#ifdef _DEBUG + if (thunk == NULL) + printf("WASM R2R to interpreter call missing for key: %s\n", keyBuffer); +#endif + return thunk; + } + ULONG GetHashCode(MethodDesc* pMD, SString &strSource) { _ASSERTE(pMD != nullptr); @@ -740,6 +907,21 @@ void* GetCookieForCalliSig(MetaSig metaSig, MethodDesc *pContextMD) return thunk; } +void* GetPortableEntryPointToInterpreterThunk(MethodDesc *pMD) +{ + STANDARD_VM_CONTRACT; + + if (pMD->ContainsGenericVariables()) + { + return NULL; + } + + MetaSig sig(pMD); + void* thunk = ComputePortableEntryPointToInterpreterThunk(sig); + + return thunk; +} + void* GetUnmanagedCallersOnlyThunk(MethodDesc* pMD) { STANDARD_VM_CONTRACT; diff --git a/src/tasks/WasmAppBuilder/coreclr/InterpToNativeGenerator.cs b/src/tasks/WasmAppBuilder/coreclr/InterpToNativeGenerator.cs index 6bf40b396ec20b..682fb018ec01ac 100644 --- a/src/tasks/WasmAppBuilder/coreclr/InterpToNativeGenerator.cs +++ b/src/tasks/WasmAppBuilder/coreclr/InterpToNativeGenerator.cs @@ -48,11 +48,11 @@ private static string SignatureToArguments(string signature) return string.Join(", ", signature.Skip(1).Select(static c => SignatureMapper.CharToNativeType(c))); } - private static string CallFuncName(IEnumerable args, string result) + private static string CallFuncName(IEnumerable args, string result, bool isPortableEntryPointCall) { var paramTypes = args.Any() ? args.Join("_", (p, i) => SignatureMapper.CharToNameType(p)).ToString() : "Void"; - return $"CallFunc_{paramTypes}_Ret{result}"; + return $"CallFunc_{paramTypes}_Ret{result}{(isPortableEntryPointCall ? "_PE" : "")}"; } private static void Emit(StreamWriter w, IEnumerable cookies) @@ -86,20 +86,33 @@ private static void Emit(StreamWriter w, IEnumerable cookies) { """); - foreach (var signature in signatures) + foreach (var signatureValue in signatures) { + string signature = signatureValue; try { var result = Result(signature); + bool isPortableEntryPointCall = IsPortableEntryPointCall(signature); + if (isPortableEntryPointCall) + { + // Portable entrypoints have an extra hidden parameter for the portable entrypoint context, so we need to adjust the signature and result accordingly for the call function generation + signature = signature.Substring(0, signature.Length - 1); + } var args = Args(signature); var portabilityAssert = signature[0] == 'n' ? "PORTABILITY_ASSERT(\"Indirect struct return is not yet implemented.\");\n " : ""; + + var portableEntryPointComma = signature.Length > 1 ? ", " : ""; + var portableEntrypointDeclaration = isPortableEntryPointCall ? portableEntryPointComma + "PCODE" : ""; + var portableEntrypointParam = isPortableEntryPointCall ? portableEntryPointComma + "pPortableEntryPointContext" : ""; + var portableEntrypointStackDeclaration = isPortableEntryPointCall ? "uintptr_t, " : ""; + var portableEntrypointStackParam = isPortableEntryPointCall ? "emscripten_stack_get_current(), " : ""; w.Write( $$""" - static void {{CallFuncName(args, SignatureMapper.CharToNameType(signature[0]))}}(PCODE pcode, int8_t* pArgs, int8_t* pRet) + static void {{CallFuncName(args, SignatureMapper.CharToNameType(signature[0]), isPortableEntryPointCall)}}(PCODE pcode, int8_t* pArgs, int8_t* pRet{{(isPortableEntryPointCall ? ", PCODE pPortableEntryPointContext" : "")}}) { - {{result.nativeType}} (*fptr)({{args.Join(", ", (p, i) => SignatureMapper.CharToNativeType(p))}}) = ({{result.nativeType}} (*)({{args.Join(", ", (p, i) => SignatureMapper.CharToNativeType(p))}}))pcode; - {{portabilityAssert}}{{(result.isVoid ? "" : "*" + "((" + result.nativeType + "*)pRet) = ")}}(*fptr)({{args.Join(", ", (p, i) => $"{SignatureMapper.CharToArgType(p)}({i})")}}); + {{result.nativeType}} (*fptr)({{portableEntrypointStackDeclaration}}{{args.Join(", ", (p, i) => SignatureMapper.CharToNativeType(p))}}{{portableEntrypointDeclaration}}) = ({{result.nativeType}} (*)({{portableEntrypointStackDeclaration}}{{args.Join(", ", (p, i) => SignatureMapper.CharToNativeType(p))}}{{portableEntrypointDeclaration}}))pcode; + {{portabilityAssert}}{{(result.isVoid ? "" : "*" + "((" + result.nativeType + "*)pRet) = ")}}(*fptr)({{portableEntrypointStackParam}}{{args.Join(", ", (p, i) => $"{SignatureMapper.CharToArgType(p)}({i})")}}{{portableEntrypointParam}}); } """); @@ -116,7 +129,14 @@ private static void Emit(StreamWriter w, IEnumerable cookies) const StringToWasmSigThunk g_wasmThunks[] = { {{signatures.Join($",{w.NewLine}", signature => - $" {{ \"{signature}\", (void*)&{CallFuncName(Args(signature), SignatureMapper.CharToNameType(signature[0]))} }}")}} + { + string initialSignature = signature; + bool isPortableEntryPointCall = IsPortableEntryPointCall(signature); + if (isPortableEntryPointCall) + signature = signature.Substring(0, signature.Length - 1); + return $" {{ \"{initialSignature}\", (void*)&{CallFuncName(Args(signature), SignatureMapper.CharToNameType(signature[0]), isPortableEntryPointCall)} }}"; + } + )}} }; const size_t g_wasmThunksCount = sizeof(g_wasmThunks) / sizeof(g_wasmThunks[0]); @@ -131,5 +151,14 @@ static IEnumerable Args(string signature) static (bool isVoid, string nativeType) Result(string signature) => new(SignatureMapper.IsVoidSignature(signature), SignatureMapper.CharToNativeType(signature[0])); + + static bool IsPortableEntryPointCall(string signature) + { +#if NETFRAMEWORK + return signature.EndsWith("p"); +#else + return signature.EndsWith('p'); +#endif + } } } diff --git a/src/tasks/WasmAppBuilder/coreclr/ManagedToNativeGenerator.cs b/src/tasks/WasmAppBuilder/coreclr/ManagedToNativeGenerator.cs index b1722487c6a277..f27c230383f859 100644 --- a/src/tasks/WasmAppBuilder/coreclr/ManagedToNativeGenerator.cs +++ b/src/tasks/WasmAppBuilder/coreclr/ManagedToNativeGenerator.cs @@ -85,15 +85,30 @@ private void ExecuteInternal(LogAdapter log) icall.ScanAssembly(asm); } + // All Signatures required for FCalls should be in this list, but we may also want to include pregenerated signatures for commonly used shapes used by + // R2R code to reduce duplication in generated R2R binaries. The signatures should be in the form of a string where the first character represents the + // return type and the following characters represent the argument types. The type characters should match those used by the SignatureMapper.CharToNativeType method. + string [] pregeneratedInterpreterToNativeSignatures = + { + "ip", + "iip", + "iiip", + "iiiip", + "vip", + "viip", + }; + IEnumerable cookies = Enumerable.Concat( pinvoke.Generate(PInvokeModules, PInvokeOutputPath, ReversePInvokeOutputPath), icall.Generate(IcallOutputPath)); + cookies = Enumerable.Concat(cookies, pregeneratedInterpreterToNativeSignatures); + var m2n = new InterpToNativeGenerator(log); m2n.Generate(cookies, InterpToNativeOutputPath); if (!string.IsNullOrEmpty(CacheFilePath)) - File.WriteAllLines(CacheFilePath, PInvokeModules); + File.WriteAllLines(CacheFilePath, PInvokeModules, Encoding.UTF8); List fileWritesList = new() { PInvokeOutputPath, InterpToNativeOutputPath }; if (!string.IsNullOrEmpty(IcallOutputPath)) From 7404f9cfb29490a1e4db45a04789b306483c23bb Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Tue, 7 Apr 2026 15:52:53 -0700 Subject: [PATCH 05/15] Add codegen changes to make calls through PortableEntryPoints stored into locations in the R2R file work - Notably, a new level of indirection is needed --- src/coreclr/jit/codegenwasm.cpp | 2 ++ src/coreclr/jit/lower.cpp | 4 ++++ src/coreclr/jit/morph.cpp | 6 +++++- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/coreclr/jit/codegenwasm.cpp b/src/coreclr/jit/codegenwasm.cpp index 81c9ee12127884..cbeb8ea0bbbbe7 100644 --- a/src/coreclr/jit/codegenwasm.cpp +++ b/src/coreclr/jit/codegenwasm.cpp @@ -2521,6 +2521,7 @@ void CodeGen::genEmitHelperCall(unsigned helper, int argSize, emitAttr retSize, // Push PEP onto the stack because we are calling a managed helper that expects it as the last parameter. assert(helperFunction.accessType == IAT_PVALUE); GetEmitter()->emitAddressConstant(helperFunction.addr); + GetEmitter()->emitIns_I(INS_i32_load, EA_PTRSIZE, 0); } if (params.callType == EC_INDIR_R) @@ -2529,6 +2530,7 @@ void CodeGen::genEmitHelperCall(unsigned helper, int argSize, emitAttr retSize, assert(helperFunction.accessType == IAT_PVALUE); GetEmitter()->emitAddressConstant(helperFunction.addr); GetEmitter()->emitIns_I(INS_i32_load, EA_PTRSIZE, 0); + GetEmitter()->emitIns_I(INS_i32_load, EA_PTRSIZE, 0); } genEmitCallWithCurrentGC(params); diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 7ca6c624c65f76..495c8c00bad90a 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -6409,6 +6409,10 @@ GenTree* Lowering::LowerDirectCall(GenTreeCall* call) cellAddr->AsIntCon()->gtTargetHandle = (size_t)call->gtCallMethHnd; #endif GenTree* indir = Ind(cellAddr); +#ifdef TARGET_WASM + indir = Ind(indir); // WebAssembly "function pointers" are actually PortableEntryPoint pointers, and + // actually dispatching to them requires an additional level of indirection. +#endif result = indir; } break; diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index e2a1f11b3d0c1a..27b2f262d28f19 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -1767,7 +1767,11 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call size_t addrValue = (size_t)call->gtEntryPoint.addr; GenTree* indirectCellAddress = comp->gtNewIconHandleNode(addrValue, GTF_ICON_FTN_ADDR); INDEBUG(indirectCellAddress->AsIntCon()->gtTargetHandle = (size_t)call->gtCallMethHnd); - +#if defined(TARGET_WASM) + // On WASM, the address in the R2R table is actually the address of something that should + // be treated as a PortableEntryPoint. To actually dispatch, we need to indirect once more. + indirectCellAddress = comp->gtNewOperNode(GT_IND, TYP_I_IMPL, indirectCellAddress); +#endif #ifdef TARGET_ARM // TODO-ARM: We currently do not properly kill this register in LSRA // (see getKillSetForCall which does so only for VSD calls). From 3b0dd6be4064b9b794986cdf30e4c7ce2565d7a9 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Tue, 7 Apr 2026 15:57:01 -0700 Subject: [PATCH 06/15] Add new WASM_MEMORY_ADDR_REL_LEB reloc - This is used to make loads/stores more efficient. loads/stores have an offset baked into them, so instead of global.get 1 i32.const add i32.load We can do global.get 1 i32.load offset= Overall this saves 3 bytes of space per load from an RVA relative address --- src/coreclr/inc/corinfo.h | 2 + .../Compiler/DependencyAnalysis/Relocation.cs | 5 + .../Compiler/ObjectWriter/WasmInstructions.cs | 145 ++++++++++++------ .../Compiler/ObjectWriter/WasmObjectWriter.cs | 16 ++ .../tools/Common/JitInterface/CorInfoImpl.cs | 1 + .../tools/Common/JitInterface/CorInfoTypes.cs | 2 + 6 files changed, 128 insertions(+), 43 deletions(-) diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index 319aaeb23d82cb..5ba772bcebd970 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -928,6 +928,8 @@ enum class CorInfoReloc // e.g. in R2R scenarios as an offset from __image_base WASM_TYPE_INDEX_LEB, // Wasm: a type index encoded as a 5-byte varuint32, e.g. the type immediate in a call_indirect. WASM_GLOBAL_INDEX_LEB, // Wasm: a global index encoded as a 5-byte varuint32, e.g. the index immediate in a get_global. + WASM_MEMORY_ADDR_REL_LEB, // Wasm: a relative linear memory index encoded as a 5-byte varint32. Used as the immediate argument of a load or store instruction, + // e.g. in R2R scenarios as an offset from __image_base }; enum CorInfoGCType diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs index e42de9d295a037..e716729a547d49 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs @@ -59,6 +59,8 @@ public enum RelocType WASM_TABLE_INDEX_I32 = 0x207, // Wasm: a table index encoded as a 4-byte uint32, e.g. for storing the "address" of a function into linear memory WASM_TABLE_INDEX_I64 = 0x208, // Wasm: a table index encoded as a 8-byte uint64, e.g. for storing the "address" of a function into linear memory + WASM_MEMORY_ADDR_REL_LEB = 0x209, // Wasm: a relative linear memory index encoded as a 5-byte varint32. Used as the immediate argument of a load or store instruction, + // e.g. in R2R scenarios as an offset from __image_base // // Relocation operators related to TLS access @@ -656,6 +658,7 @@ public static unsafe void WriteValue(RelocType relocType, void* location, long v case RelocType.WASM_GLOBAL_INDEX_LEB: case RelocType.WASM_FUNCTION_INDEX_LEB: case RelocType.WASM_MEMORY_ADDR_LEB: + case RelocType.WASM_MEMORY_ADDR_REL_LEB: DwarfHelper.WritePaddedULEB128(new Span((byte*)location, WASM_PADDED_RELOC_SIZE_32), checked((ulong)value)); return; @@ -710,6 +713,7 @@ public static int GetSize(RelocType relocType) RelocType.WASM_GLOBAL_INDEX_LEB => WASM_PADDED_RELOC_SIZE_32, RelocType.WASM_MEMORY_ADDR_LEB => WASM_PADDED_RELOC_SIZE_32, RelocType.WASM_MEMORY_ADDR_SLEB => WASM_PADDED_RELOC_SIZE_32, + RelocType.WASM_MEMORY_ADDR_REL_LEB => WASM_PADDED_RELOC_SIZE_32, RelocType.WASM_MEMORY_ADDR_REL_SLEB => WASM_PADDED_RELOC_SIZE_32, RelocType.WASM_TABLE_INDEX_I32 => 4, RelocType.WASM_TABLE_INDEX_I64 => 8, @@ -783,6 +787,7 @@ public static unsafe long ReadValue(RelocType relocType, void* location) return 0; case RelocType.WASM_MEMORY_ADDR_LEB: + case RelocType.WASM_MEMORY_ADDR_REL_LEB: return checked((long)DwarfHelper.ReadULEB128(new ReadOnlySpan(location, WASM_PADDED_RELOC_SIZE_32))); case RelocType.WASM_MEMORY_ADDR_SLEB: diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmInstructions.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmInstructions.cs index cef0e10580c797..87043cd0a02f00 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmInstructions.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmInstructions.cs @@ -295,12 +295,81 @@ public static void OffsetRelocationsByOffset(Span buffer, int offset } } - class WasmMemoryArgInstruction : WasmExpr + struct WasmEncodableULong : IWasmEncodable + { + private ulong _value; + public WasmEncodableULong(ulong value) + { + _value = value; + } + public int EncodeSize() + { + return (int)DwarfHelper.SizeOfULEB128(_value); + } + public int Encode(Span buffer) + { + return DwarfHelper.WriteULEB128(buffer, _value); + } + public int EncodeRelocationCount() => 0; + public int EncodeRelocations(Span buffer) => 0; + } + + struct WasmEncodableSymbol : IWasmEncodable + { + private ISymbolNode _symbol; + private RelocType _relocType; + + public WasmEncodableSymbol(ISymbolNode symbol, RelocType relocType) + { + _symbol = symbol; + _relocType = relocType; + } + + public int EncodeSize() + { + return Relocation.GetSize(_relocType); + } + + public int Encode(Span buffer) + { + // The actual value is not encoded into the buffer, instead a relocation is emitted for the symbol + int relocSize = Relocation.GetSize(_relocType); + switch (_relocType) + { + case RelocType.WASM_FUNCTION_INDEX_LEB: + case RelocType.WASM_MEMORY_ADDR_LEB: + case RelocType.WASM_MEMORY_ADDR_REL_LEB: + case RelocType.WASM_TYPE_INDEX_LEB: + case RelocType.WASM_GLOBAL_INDEX_LEB: + DwarfHelper.WritePaddedULEB128(buffer, 0); + break; + + case RelocType.WASM_TABLE_INDEX_SLEB: + case RelocType.WASM_MEMORY_ADDR_REL_SLEB: + DwarfHelper.WritePaddedSLEB128(buffer, 0); + break; + + default: + throw new Exception($"Unknown WASM reloc type : {_relocType}"); + } + return relocSize; + } + + public int EncodeRelocationCount() => 1; + + public int EncodeRelocations(Span buffer) + { + buffer[0] = new Relocation(_relocType, 0, _symbol); + return 1; + } + } + + class WasmMemoryArgInstruction : WasmExpr where TOffset : IWasmEncodable { readonly uint _align; - readonly ulong _offset; + readonly TOffset _offset; - public WasmMemoryArgInstruction(WasmExprKind kind, uint align, ulong offset) : base(kind) + public WasmMemoryArgInstruction(WasmExprKind kind, uint align, TOffset offset) : base(kind) { switch (align) { @@ -317,7 +386,7 @@ public WasmMemoryArgInstruction(WasmExprKind kind, uint align, ulong offset) : b public override int EncodeSize() { - uint valSize = DwarfHelper.SizeOfULEB128(_align) + DwarfHelper.SizeOfULEB128(_offset); + int valSize = (int)DwarfHelper.SizeOfULEB128(_align) + _offset.EncodeSize(); return base.EncodeSize() + (int)valSize; } @@ -325,9 +394,17 @@ public override int Encode(Span buffer) { int pos = base.Encode(buffer); pos += DwarfHelper.WriteULEB128(buffer.Slice(pos), _align); - pos += DwarfHelper.WriteULEB128(buffer.Slice(pos), _offset); + pos += _offset.Encode(buffer.Slice(pos)); return pos; } + public override int EncodeRelocationCount() => _offset.EncodeRelocationCount(); + public override int EncodeRelocations(Span buffer) + { + int relocsEncoded = _offset.EncodeRelocations(buffer); + if (relocsEncoded > 0) + WasmExpr.OffsetRelocationsByOffset(buffer.Slice(0, relocsEncoded), base.EncodeSize() + (int)DwarfHelper.SizeOfULEB128(_align)); + return relocsEncoded; + } } // Represents a constant expression (e.g., (i32.const )) @@ -399,46 +476,27 @@ public override int EncodeRelocations(Span buffer) sealed class WasmLEBConstantReloc : WasmExpr { - readonly ISymbolNode _symbol; - readonly RelocType _relocType; + readonly WasmEncodableSymbol _symbol; public WasmLEBConstantReloc(WasmExprKind kind, ISymbolNode symbol, RelocType relocType) : base(kind) { - _symbol = symbol; - _relocType = relocType; + _symbol = new WasmEncodableSymbol(symbol, relocType); } - public override int EncodeSize() => base.EncodeSize() + Relocation.GetSize(_relocType); + public override int EncodeSize() => base.EncodeSize() + _symbol.EncodeSize(); public override int Encode(Span buffer) { int pos = base.Encode(buffer); - int relocSize = Relocation.GetSize(_relocType); - switch (_relocType) - { - case RelocType.WASM_FUNCTION_INDEX_LEB: - case RelocType.WASM_MEMORY_ADDR_LEB: - case RelocType.WASM_TYPE_INDEX_LEB: - case RelocType.WASM_GLOBAL_INDEX_LEB: - DwarfHelper.WritePaddedULEB128(buffer.Slice(pos, relocSize), 0); - break; - - case RelocType.WASM_TABLE_INDEX_SLEB: - case RelocType.WASM_MEMORY_ADDR_REL_SLEB: - DwarfHelper.WritePaddedSLEB128(buffer.Slice(pos, relocSize), 0); - break; - - default: - throw new Exception($"Unknown WASM reloc type : {_relocType}"); - } - - pos += relocSize; + pos += _symbol.Encode(buffer.Slice(pos)); return pos; } - public override int EncodeRelocationCount() => 1; + public override int EncodeRelocationCount() => _symbol.EncodeRelocationCount(); public override int EncodeRelocations(Span buffer) { - buffer[0] = new Relocation(_relocType, base.EncodeSize(), _symbol); - return 1; + int relocsEncoded = _symbol.EncodeRelocations(buffer); + if (relocsEncoded > 0) + WasmExpr.OffsetRelocationsByOffset(buffer.Slice(0, relocsEncoded), base.EncodeSize()); + return relocsEncoded; } } @@ -694,32 +752,33 @@ public static WasmExpr ConstRVA(ISymbolNode symbolNode) public static WasmExpr Add => new WasmBinaryExpr(WasmExprKind.I32Add); public static WasmExpr Sub => new WasmBinaryExpr(WasmExprKind.I32Sub); public static WasmExpr Ge_s => new WasmBinaryExpr(WasmExprKind.I32Ge_s); - public static WasmExpr Load(ulong offset) => new WasmMemoryArgInstruction(WasmExprKind.I32Load, 4, offset); - public static WasmExpr Store(ulong offset) => new WasmMemoryArgInstruction(WasmExprKind.I32Store, 4, offset); + public static WasmExpr Load(ulong offset) => new WasmMemoryArgInstruction(WasmExprKind.I32Load, 4, new WasmEncodableULong(offset)); + public static WasmExpr LoadWithRVAOffset(ISymbolNode symbolNode) => new WasmMemoryArgInstruction(WasmExprKind.I32Load, 4, new WasmEncodableSymbol(symbolNode, RelocType.WASM_MEMORY_ADDR_REL_LEB)); + public static WasmExpr Store(ulong offset) => new WasmMemoryArgInstruction(WasmExprKind.I32Store, 4, new WasmEncodableULong(offset)); } static class I64 { - public static WasmExpr Load(ulong offset) => new WasmMemoryArgInstruction(WasmExprKind.I64Load, 8, offset); - public static WasmExpr Store(ulong offset) => new WasmMemoryArgInstruction(WasmExprKind.I64Store, 8, offset); + public static WasmExpr Load(ulong offset) => new WasmMemoryArgInstruction(WasmExprKind.I64Load, 8, new WasmEncodableULong(offset)); + public static WasmExpr Store(ulong offset) => new WasmMemoryArgInstruction(WasmExprKind.I64Store, 8, new WasmEncodableULong(offset)); } static class F32 { - public static WasmExpr Load(ulong offset) => new WasmMemoryArgInstruction(WasmExprKind.F32Load, 4, offset); - public static WasmExpr Store(ulong offset) => new WasmMemoryArgInstruction(WasmExprKind.F32Store, 4, offset); + public static WasmExpr Load(ulong offset) => new WasmMemoryArgInstruction(WasmExprKind.F32Load, 4, new WasmEncodableULong(offset)); + public static WasmExpr Store(ulong offset) => new WasmMemoryArgInstruction(WasmExprKind.F32Store, 4, new WasmEncodableULong(offset)); } static class F64 { - public static WasmExpr Load(ulong offset) => new WasmMemoryArgInstruction(WasmExprKind.F64Load, 8, offset); - public static WasmExpr Store(ulong offset) => new WasmMemoryArgInstruction(WasmExprKind.F64Store, 8, offset); + public static WasmExpr Load(ulong offset) => new WasmMemoryArgInstruction(WasmExprKind.F64Load, 8, new WasmEncodableULong(offset)); + public static WasmExpr Store(ulong offset) => new WasmMemoryArgInstruction(WasmExprKind.F64Store, 8, new WasmEncodableULong(offset)); } static class V128 { - public static WasmExpr Load(ulong offset) => new WasmMemoryArgInstruction(WasmExprKind.V128Load, 16, offset); - public static WasmExpr Store(ulong offset) => new WasmMemoryArgInstruction(WasmExprKind.V128Store, 16, offset); + public static WasmExpr Load(ulong offset) => new WasmMemoryArgInstruction(WasmExprKind.V128Load, 16, new WasmEncodableULong(offset)); + public static WasmExpr Store(ulong offset) => new WasmMemoryArgInstruction(WasmExprKind.V128Store, 16, new WasmEncodableULong(offset)); } static class Memory diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs index bceb16c096e174..6fab037c6cf504 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs @@ -961,6 +961,22 @@ private unsafe void ResolveRelocations(int sectionIndex, MemoryStream sectionStr Relocation.WriteValue(reloc.Type, pData, virtualSymbolImageOffset + addend); break; } + case RelocType.WASM_MEMORY_ADDR_REL_LEB: + { + // These relocs should be for cases of the form: + // global.get __image_base + // i32.load + // So, the relocated address value should always represent an offset relative to image base. + // This offset should ALWAYS be equal to the actual offset from image base at runtime, due to Webcil's + // flag mapping + if (symbolWebcilSection is null) + { + throw new InvalidDataException(); + } + + Relocation.WriteValue(reloc.Type, pData, virtualSymbolImageOffset + addend); + break; + } case RelocType.WASM_TABLE_INDEX_I32: case RelocType.WASM_TABLE_INDEX_I64: case RelocType.WASM_TABLE_INDEX_SLEB: diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 1654ad2613be04..0af041b078149e 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -4250,6 +4250,7 @@ private RelocType GetRelocType(CorInfoReloc reloc) CorInfoReloc.WASM_MEMORY_ADDR_LEB => RelocType.WASM_MEMORY_ADDR_LEB, CorInfoReloc.WASM_MEMORY_ADDR_SLEB => RelocType.WASM_MEMORY_ADDR_SLEB, CorInfoReloc.WASM_MEMORY_ADDR_REL_SLEB => RelocType.WASM_MEMORY_ADDR_REL_SLEB, + CorInfoReloc.WASM_MEMORY_ADDR_REL_LEB => RelocType.WASM_MEMORY_ADDR_REL_LEB, CorInfoReloc.WASM_TYPE_INDEX_LEB => RelocType.WASM_TYPE_INDEX_LEB, CorInfoReloc.WASM_GLOBAL_INDEX_LEB => RelocType.WASM_GLOBAL_INDEX_LEB, _ => throw new ArgumentException("Unsupported relocation type: " + reloc), diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index 9969f75ef2db0b..e84795d2e3f4a6 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -518,6 +518,8 @@ public enum CorInfoReloc // e.g. in R2R scenarios, encoding an offset from __image_base WASM_TYPE_INDEX_LEB, // Wasm: a type index encoded as a 5-byte varuint32, e.g. the type immediate in a call_indirect. WASM_GLOBAL_INDEX_LEB, // Wasm: a global index encoded as a 5-byte varuint32, e.g. the index immediate in a get_global. + WASM_MEMORY_ADDR_REL_LEB, // Wasm: a relative linear memory index encoded as a 5-byte varuint32. Used as the immediate argument of a load or store instruction, + // e.g. in R2R scenarios, encoding an offset from __image_base } public enum CorInfoGCType From 58383b18df90644c792534d9956833b1548978b9 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Tue, 7 Apr 2026 16:07:46 -0700 Subject: [PATCH 07/15] Rework WasmImportThunk in terms of the ArgIterator, and adjust ArgIterator to match the ArgIterator behavior in the runtime - Notably, the ArgIterator in the runtime follows interpreter semantics, so this logic adjusts the way we stash memory into the stack to match the interpreter behavior - And the WasmImportThunk code now utilizes the ArgIterator to do its layout. - We may need to reconcile behavior differences between the interpreter/RyuJIT abi, but they are fairly close, so this is probably reasonable to do for now. Alternatively, we can remove the whole GCRefMap infra in favor of a small bit of conservative reporting, and a completely different format. --- .../ReadyToRun/ArgIterator.cs | 6 +- .../ReadyToRun/GCRefMapBuilder.cs | 30 +++-- .../ReadyToRun/TransitionBlock.cs | 8 +- .../ReadyToRun/WasmImportThunk.cs | 118 ++++++------------ 4 files changed, 65 insertions(+), 97 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs index 1d565590cd56ba..2d96dd41a66ac1 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs @@ -868,7 +868,7 @@ public int GetNextOffset() break; case TargetArchitecture.Wasm32: - _wasmOfsStack = numRegistersUsed * _transitionBlock.PointerSize; + _wasmOfsStack = numRegistersUsed * 8; break; case TargetArchitecture.ARM: @@ -1107,8 +1107,8 @@ public int GetNextOffset() break; case WasmValueType.I32: case WasmValueType.F32: - cbArg = 4; - align = 4; + cbArg = 8; + align = 8; break; case WasmValueType.V128: cbArg = 16; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/GCRefMapBuilder.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/GCRefMapBuilder.cs index fc12f268d28092..a97b2f2823a215 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/GCRefMapBuilder.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/GCRefMapBuilder.cs @@ -66,16 +66,14 @@ public GCRefMapBuilder(TargetDetails target, bool relocsOnly) _transitionBlock = TransitionBlock.FromTarget(target); } - public void GetCallRefMap(MethodDesc method, bool isUnboxingStub) + internal static (ArgIterator, TransitionBlock) BuildArgIterator(MethodSignature signature, TypeSystemContext context, bool methodRequiresInstArg = false, bool isUnboxingStub = false, bool methodIsArrayAddressMethod = false, bool methodIsStringConstructor = false, bool methodIsAsyncCall = false) { - TransitionBlock transitionBlock = TransitionBlock.FromTarget(method.Context.Target); - - MethodSignature signature = method.Signature; + TransitionBlock transitionBlock = TransitionBlock.FromTarget(context.Target); bool hasThis = (signature.Flags & MethodSignatureFlags.Static) == 0; // This pointer is omitted for string constructors - bool fCtorOfVariableSizedObject = hasThis && method.OwningType.IsString && method.IsConstructor; + bool fCtorOfVariableSizedObject = hasThis && methodIsStringConstructor; if (fCtorOfVariableSizedObject) hasThis = false; @@ -87,16 +85,16 @@ public void GetCallRefMap(MethodDesc method, bool isUnboxingStub) parameterTypes[parameterIndex] = new TypeHandle(signature[parameterIndex]); } CallingConventions callingConventions = (hasThis ? CallingConventions.ManagedInstance : CallingConventions.ManagedStatic); - bool hasParamType = method.RequiresInstArg() && !isUnboxingStub; + bool hasParamType = methodRequiresInstArg && !isUnboxingStub; // On X86 the Array address method doesn't use IL stubs, and instead has a custom calling convention - if ((method.Context.Target.Architecture == TargetArchitecture.X86) && - method.IsArrayAddressMethod()) + if ((context.Target.Architecture == TargetArchitecture.X86) && + methodIsArrayAddressMethod) { hasParamType = true; } - bool hasAsyncContinuation = method.IsAsyncCall(); + bool hasAsyncContinuation = methodIsAsyncCall; // We shouldn't be compiling unboxing stubs for async methods yet. Debug.Assert(hasAsyncContinuation ? !isUnboxingStub : true); @@ -107,7 +105,7 @@ public void GetCallRefMap(MethodDesc method, bool isUnboxingStub) ArgIteratorData argIteratorData = new ArgIteratorData(hasThis, isVarArg, parameterTypes, returnType); ArgIterator argit = new ArgIterator( - method.Context, + context, argIteratorData, callingConventions, hasParamType, @@ -117,6 +115,18 @@ public void GetCallRefMap(MethodDesc method, bool isUnboxingStub) skipFirstArg, extraObjectFirstArg); + return (argit, transitionBlock); + } + + public void GetCallRefMap(MethodDesc method, bool isUnboxingStub) + { + (ArgIterator argit, TransitionBlock transitionBlock) = BuildArgIterator(method.Signature, method.Context, + methodRequiresInstArg: method.RequiresInstArg(), + isUnboxingStub: isUnboxingStub, + methodIsArrayAddressMethod: method.IsArrayAddressMethod(), + methodIsStringConstructor: method.OwningType.IsString && method.IsConstructor, + methodIsAsyncCall: method.IsAsyncCall()); + int nStackBytes = argit.SizeOfFrameArgumentArray(); // Allocate a fake stack diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TransitionBlock.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TransitionBlock.cs index a19658e7e9e98a..83e807b0a4e5b9 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TransitionBlock.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TransitionBlock.cs @@ -760,9 +760,9 @@ private class Wasm32TransitionBlock : TransitionBlock public override int NumCalleeSavedRegisters => 0; - public override int SizeOfTransitionBlock => 0; + public override int SizeOfTransitionBlock => 8; - public override int OffsetOfArgumentRegisters => 0; + public override int OffsetOfArgumentRegisters => 8; public override int OffsetOfFloatArgumentRegisters => 0; @@ -770,7 +770,7 @@ private class Wasm32TransitionBlock : TransitionBlock public override int EnregisteredReturnTypeIntegerMaxSize => 0; - public override int GetRetBuffArgOffset(bool hasThis) => hasThis ? 4 : 0; + public override int GetRetBuffArgOffset(bool hasThis) => hasThis ? 8 : 0; public override bool IsArgPassedByRef(TypeHandle th) { @@ -779,7 +779,7 @@ public override bool IsArgPassedByRef(TypeHandle th) public override int StackElemSize(int parmSize, bool isValueType, bool isFloatHfa) { - int stackSlotSize = 4; + int stackSlotSize = 8; return ALIGN_UP(parmSize, stackSlotSize); } } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/WasmImportThunk.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/WasmImportThunk.cs index 3c88c3801031ff..5fcb9ffa464d6e 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/WasmImportThunk.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/WasmImportThunk.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Reflection.Metadata; namespace ILCompiler.DependencyAnalysis.ReadyToRun { @@ -109,7 +110,7 @@ public override int CompareToImpl(ISortableNode other, CompilerComparer comparer return comparer.Compare(_helperCell, otherNode._helperCell); } - static CorInfoWasmType[] _helperTypeParams = new CorInfoWasmType[] { CorInfoWasmType.CORINFO_WASM_TYPE_I32, CorInfoWasmType.CORINFO_WASM_TYPE_I32, CorInfoWasmType.CORINFO_WASM_TYPE_I32, CorInfoWasmType.CORINFO_WASM_TYPE_I32 }; + static CorInfoWasmType[] _helperTypeParams = new CorInfoWasmType[] { CorInfoWasmType.CORINFO_WASM_TYPE_I32, CorInfoWasmType.CORINFO_WASM_TYPE_I32, CorInfoWasmType.CORINFO_WASM_TYPE_I32, CorInfoWasmType.CORINFO_WASM_TYPE_I32, CorInfoWasmType.CORINFO_WASM_TYPE_I32 }; protected override void EmitCode(NodeFactory factory, ref Wasm.WasmEmitter instructionEncoder, bool relocsOnly) { @@ -122,38 +123,28 @@ protected override void EmitCode(NodeFactory factory, ref Wasm.WasmEmitter instr ISymbolNode helperTypeIndex = factory.WasmTypeNode(_helperTypeParams); - // The arguments are $sp, ARG0-ARGN, PortableEntrypointThunk. - // The general logic is... - // Compute stack offset needed. - int currentOffset = 0; + MethodSignature methodSignature = WasmLowering.RaiseSignature(_typeNode.Type, _context); + (ArgIterator argit, TransitionBlock transitionBlock) = GCRefMapBuilder.BuildArgIterator(methodSignature, _context); - for (int i = 1; i < _typeNode.Type.Params.Types.Length - 1; i++) - { - WasmValueType type = _typeNode.Type.Params.Types[i]; - switch (type) - { - case WasmValueType.I32: - case WasmValueType.F32: - currentOffset = AlignmentHelper.AlignUp(currentOffset, 4); - currentOffset += 4; - break; - case WasmValueType.I64: - case WasmValueType.F64: - currentOffset = AlignmentHelper.AlignUp(currentOffset, 8); - currentOffset += 8; - break; - case WasmValueType.V128: - currentOffset = AlignmentHelper.AlignUp(currentOffset, 16); - currentOffset += 16; - break; + int[] offsets = new int[methodSignature.Length]; + Debug.Assert(offsets.Length == _typeNode.Type.Params.Types.Length - 2); - default: - throw new System.Exception("Unexpected wasm type arg"); - } + int argIndex = 0; + int argOffset; + while ((argOffset = argit.GetNextOffset()) != TransitionBlock.InvalidOffset) + { + offsets[argIndex] = argOffset; + argIndex++; } + argit.Reset(); + + // The arguments are $sp, ARG0-ARGN, PortableEntrypointThunk. + // The general logic is... + // Compute stack offset needed. + // Align stack to 16 byte boundaries - int sizeOfStoredLocals = AlignmentHelper.AlignUp(currentOffset, 16); + int sizeOfStoredLocals = AlignmentHelper.AlignUp(argit.SizeOfFrameArgumentArray(), 16) + transitionBlock.SizeOfTransitionBlock; List expressions = new List(); // local.get 0 @@ -178,44 +169,28 @@ protected override void EmitCode(NodeFactory factory, ref Wasm.WasmEmitter instr // } // In the calling convention, the first arg is the sp arg, and the last is the portable entrypoint arg. Each of those are treated specially - currentOffset = 0; for (int i = 1; i < _typeNode.Type.Params.Types.Length - 1; i++) { expressions.Add(Local.Get(0)); expressions.Add(Local.Get(i)); WasmValueType type = _typeNode.Type.Params.Types[i]; + int currentOffset = offsets[i - 1]; switch (type) { case WasmValueType.I32: + expressions.Add(I32.Store((ulong)currentOffset)); + break; case WasmValueType.F32: - currentOffset = AlignmentHelper.AlignUp(currentOffset, 4); - if (type == WasmValueType.I32) - { - expressions.Add(I32.Store((ulong)currentOffset)); - } - else - { - expressions.Add(F32.Store((ulong)currentOffset)); - } - currentOffset += 4; + expressions.Add(F32.Store((ulong)currentOffset)); break; case WasmValueType.I64: + expressions.Add(I64.Store((ulong)currentOffset)); + break; case WasmValueType.F64: - currentOffset = AlignmentHelper.AlignUp(currentOffset, 8); - if (type == WasmValueType.I64) - { - expressions.Add(I64.Store((ulong)currentOffset)); - } - else - { - expressions.Add(F64.Store((ulong)currentOffset)); - } - currentOffset += 8; + expressions.Add(F64.Store((ulong)currentOffset)); break; case WasmValueType.V128: - currentOffset = AlignmentHelper.AlignUp(currentOffset, 16); expressions.Add(V128.Store((ulong)currentOffset)); - currentOffset += 16; break; default: @@ -231,15 +206,14 @@ protected override void EmitCode(NodeFactory factory, ref Wasm.WasmEmitter instr expressions.Add(Local.Get(portableEntrypointLocalIndex)); // The address of the portable entrypoint is passed as the second expressions.Add(Global.Get(WasmObjectWriter.ImageBaseGlobalIndex)); // The module base address is passed as the third argument + // Pass the RVA of the Module fixup as the fourth argument + // i32.const (RVA of Module fixup) + expressions.Add(I32.ConstRVA(factory.ModuleImport)); + // Load the helper function address and dispatch // global.get {module base} expressions.Add(Global.Get(WasmObjectWriter.ImageBaseGlobalIndex)); // Module base used to load the helper function address - // i32.const (RVA of R2RHelperID) - expressions.Add(I32.ConstRVA(_helperCell)); - // i32.add - expressions.Add(I32.Add); - // i32.load 0 - expressions.Add(I32.Load(0)); + expressions.Add(I32.LoadWithRVAOffset(_helperCell)); // Load the helper call function pointer from the helper cell, using a load with an RVA offset so that the helper cell can be left as a zero in the R2R image and fixed up at runtime without needing a relocation // call_indirect (i32, i32, i32, i32) (returns i32) expressions.Add(ControlFlow.CallIndirect(helperTypeIndex, 0)); @@ -262,43 +236,27 @@ protected override void EmitCode(NodeFactory factory, ref Wasm.WasmEmitter instr // local.set (i+1) // } // In the calling convention, the first arg is the sp arg, and the last is the portable entrypoint arg. Each of those are treated specially - currentOffset = 0; for (int i = 1; i < _typeNode.Type.Params.Types.Length - 1; i++) { expressions.Add(Local.Get(0)); WasmValueType type = _typeNode.Type.Params.Types[i]; + int currentOffset = offsets[i - 1]; switch (type) { case WasmValueType.I32: + expressions.Add(I32.Load((ulong)currentOffset)); + break; case WasmValueType.F32: - currentOffset = AlignmentHelper.AlignUp(currentOffset, 4); - if (type == WasmValueType.I32) - { - expressions.Add(I32.Load((ulong)currentOffset)); - } - else - { - expressions.Add(F32.Load((ulong)currentOffset)); - } - currentOffset += 4; + expressions.Add(F32.Load((ulong)currentOffset)); break; case WasmValueType.I64: + expressions.Add(I64.Load((ulong)currentOffset)); + break; case WasmValueType.F64: - currentOffset = AlignmentHelper.AlignUp(currentOffset, 8); - if (type == WasmValueType.I64) - { - expressions.Add(I64.Load((ulong)currentOffset)); - } - else - { - expressions.Add(F64.Load((ulong)currentOffset)); - } - currentOffset += 8; + expressions.Add(F64.Load((ulong)currentOffset)); break; case WasmValueType.V128: - currentOffset = AlignmentHelper.AlignUp(currentOffset, 16); expressions.Add(V128.Load((ulong)currentOffset)); - currentOffset += 16; break; default: From bc0a16fd4bd2afb05b3286d1f8413bebc5ba5d3b Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Tue, 7 Apr 2026 16:08:16 -0700 Subject: [PATCH 08/15] Updates to the VM to implement the VM side of delay loading --- src/coreclr/vm/cgensys.h | 6 ++++++ src/coreclr/vm/precode_portable.cpp | 2 +- src/coreclr/vm/wasm/helpers.cpp | 5 +++-- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/coreclr/vm/cgensys.h b/src/coreclr/vm/cgensys.h index 59c451b0a90ba7..92636fc0cdc2ec 100644 --- a/src/coreclr/vm/cgensys.h +++ b/src/coreclr/vm/cgensys.h @@ -60,7 +60,13 @@ extern "C" void STDCALL VirtualMethodFixupStub(void); extern "C" void STDCALL VirtualMethodFixupPatchLabel(void); #ifdef FEATURE_READYTORUN +#ifdef TARGET_WASM +// Wasm requires the signatures to be identical since the implementation is actually in C++ +struct READYTORUN_IMPORT_THUNK_PORTABLE_ENTRYPOINT; +extern "C" PCODE STDCALL DelayLoad_MethodCall(TransitionBlock* pTransitionBlock, READYTORUN_IMPORT_THUNK_PORTABLE_ENTRYPOINT* pImportThunkEntry, uint8_t *moduleBase, int32_t rvaOfModuleFixup); +#else extern "C" void STDCALL DelayLoad_MethodCall(); +#endif extern "C" void STDCALL DelayLoad_Helper(); extern "C" void STDCALL DelayLoad_Helper_Obj(); diff --git a/src/coreclr/vm/precode_portable.cpp b/src/coreclr/vm/precode_portable.cpp index 6e159d4b28d8d1..5f7d8508f48c11 100644 --- a/src/coreclr/vm/precode_portable.cpp +++ b/src/coreclr/vm/precode_portable.cpp @@ -237,7 +237,7 @@ void FlushCacheForDynamicMappedStub(void* code, SIZE_T size) BOOL DoesSlotCallPrestub(PCODE pCode) { LIMITED_METHOD_CONTRACT; - _ASSERTE(!"DoesSlotCallPrestub is not supported with Portable EntryPoints"); + /* On WASM slots never directly call the prestub*/ return FALSE; } diff --git a/src/coreclr/vm/wasm/helpers.cpp b/src/coreclr/vm/wasm/helpers.cpp index 5cd406885bb780..aa16240fb0d0ad 100644 --- a/src/coreclr/vm/wasm/helpers.cpp +++ b/src/coreclr/vm/wasm/helpers.cpp @@ -150,9 +150,10 @@ extern "C" void STDMETHODCALLTYPE JIT_ProfilerEnterLeaveTailcallStub(UINT_PTR Pr PORTABILITY_ASSERT("JIT_ProfilerEnterLeaveTailcallStub is not implemented on wasm"); } -extern "C" void STDCALL DelayLoad_MethodCall() +extern "C" PCODE STDCALL DelayLoad_MethodCall(TransitionBlock* pTransitionBlock, READYTORUN_IMPORT_THUNK_PORTABLE_ENTRYPOINT* pImportThunkEntry, uint8_t *moduleBase, int32_t rvaOfModuleFixup) { - PORTABILITY_ASSERT("DelayLoad_MethodCall is not implemented on wasm"); + Module** ppModule = (Module**)(moduleBase + rvaOfModuleFixup); + return ExternalMethodFixupWorker(pTransitionBlock, (TADDR)(moduleBase + pImportThunkEntry->RelocOffset), -1, *ppModule); } extern "C" void STDCALL DelayLoad_Helper() From 5ad841af30eff5a93622f02d2a0061231204e4c6 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Wed, 8 Apr 2026 14:11:31 -0700 Subject: [PATCH 09/15] More fixes! --- .../ReadyToRun/WasmImportThunk.cs | 9 ++++- src/coreclr/vm/peimagelayout.cpp | 14 ++++++++ src/coreclr/vm/precode_portable.cpp | 2 +- src/coreclr/vm/wasm/helpers.cpp | 36 ++++++++++++++++++- 4 files changed, 58 insertions(+), 3 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/WasmImportThunk.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/WasmImportThunk.cs index 5fcb9ffa464d6e..735f25d158df9c 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/WasmImportThunk.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/WasmImportThunk.cs @@ -116,6 +116,7 @@ protected override void EmitCode(NodeFactory factory, ref Wasm.WasmEmitter instr { Debug.Assert(_thunkKind == ImportThunkKind.DelayLoadHelper); Debug.Assert(!instructionEncoder.Is64Bit); // We currently only support 32-bit, and the thunk logic is currently tied to that assumption + int firstNonParamLocalIndex = _typeNode.Type.Params.Types.Length; // WASM-TODO! This is NOT an efficient way to implement this thunk. Currently it writes all the arguments to the stack, not just the ones which need to be saved for GC purposes. // At some point we'll want to only write the arguments which need GC tracking, and skip the save/restore for other arguments. This will require changes to code @@ -156,6 +157,9 @@ protected override void EmitCode(NodeFactory factory, ref Wasm.WasmEmitter instr // local.tee 0 expressions.Add(Local.Tee(0)); + expressions.Add(Global.Get(WasmObjectWriter.StackPointerGlobalIndex)); // Get the current value of the stack pointer global + expressions.Add(Local.Set(firstNonParamLocalIndex)); + // global.set {stack pointer global} // This is a callout from managed to native, we need to set the global stack pointer so that C++ code will work expressions.Add(Global.Set(0)); @@ -227,6 +231,9 @@ protected override void EmitCode(NodeFactory factory, ref Wasm.WasmEmitter instr expressions.Add(I32.Const(sizeOfStoredLocals)); // i32.add expressions.Add(I32.Add); + + expressions.Add(Local.Get(firstNonParamLocalIndex)); + expressions.Add(Global.Set(WasmObjectWriter.StackPointerGlobalIndex)); // Set the current value of the stack pointer global // // ; Setup normal args // for (int i = 0; i < N; i++) @@ -276,7 +283,7 @@ protected override void EmitCode(NodeFactory factory, ref Wasm.WasmEmitter instr expressions.Add(ControlFlow.CallIndirect(_typeNode, 0)); // Encode as a complete function body - instructionEncoder.FunctionBody = new WasmFunctionBody(_typeNode.Type, expressions.ToArray()); + instructionEncoder.FunctionBody = new WasmFunctionBody(_typeNode.Type, new WasmValueType[]{WasmValueType.I32}, expressions.ToArray()); } protected override void EmitCode(NodeFactory factory, ref X64.X64Emitter instructionEncoder, bool relocsOnly) { throw new NotSupportedException(); } diff --git a/src/coreclr/vm/peimagelayout.cpp b/src/coreclr/vm/peimagelayout.cpp index 8a8497b476a5d4..3e53bdf525b212 100644 --- a/src/coreclr/vm/peimagelayout.cpp +++ b/src/coreclr/vm/peimagelayout.cpp @@ -277,12 +277,26 @@ void PEImageLayout::ApplyBaseRelocations(bool relocationMustWriteCopy) const SIZE_T cbPageSize = 4096; COUNT_T dirPos = 0; +#ifdef TARGET_WASM + // WASM will padd out the reloc size to the next 16 byte boundary, so we need to validate we can safely read the IMAGE_BASE_RELOCATION struct before processing each entry. + while (dirPos < (dirSize - sizeof(IMAGE_BASE_RELOCATION))) +#else while (dirPos < dirSize) +#endif { PIMAGE_BASE_RELOCATION r = (PIMAGE_BASE_RELOCATION)(dir + dirPos); COUNT_T fixupsSize = VAL32(r->SizeOfBlock); +#ifdef TARGET_WASM + if (fixupsSize == 0) + { + // Since WASM will pad the reloc block to the next 16 byte boundary with 0's we need to allow for a SizeOfBlock being zero. + // This can only happen for the last block in the relocation list, so we can break here instead of continue. + break; + } +#endif + USHORT *fixups = (USHORT *) (r + 1); _ASSERTE(fixupsSize > sizeof(IMAGE_BASE_RELOCATION)); diff --git a/src/coreclr/vm/precode_portable.cpp b/src/coreclr/vm/precode_portable.cpp index 5f7d8508f48c11..62783359aef8d5 100644 --- a/src/coreclr/vm/precode_portable.cpp +++ b/src/coreclr/vm/precode_portable.cpp @@ -17,7 +17,7 @@ bool PortableEntryPoint::HasNativeEntryPoint(PCODE addr) { LIMITED_METHOD_CONTRACT; PortableEntryPoint* portableEntryPoint = ToPortableEntryPoint(addr); - return portableEntryPoint->HasNativeCode(); + return portableEntryPoint->HasNativeCode() && !portableEntryPoint->PrefersInterpreterEntryPoint(); } bool PortableEntryPoint::HasInterpreterData(PCODE addr) diff --git a/src/coreclr/vm/wasm/helpers.cpp b/src/coreclr/vm/wasm/helpers.cpp index aa16240fb0d0ad..0a32732add2e2e 100644 --- a/src/coreclr/vm/wasm/helpers.cpp +++ b/src/coreclr/vm/wasm/helpers.cpp @@ -16,6 +16,12 @@ void ExecuteInterpretedMethodWithArgs_PortableEntryPoint(PCODE portableEntrypoin // ------------------------------------------------- namespace { + static void CallInterpreter_RetVoid(uintptr_t stackArg, PCODE portableEntrypoint) + { + void * result = NULL; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, nullptr, 0, (int8_t*)&result); + return; + } static void CallInterpreter_I32_RetVoid(uintptr_t stackArg, int32_t arg0, PCODE portableEntrypoint) { int64_t args[1] = { (int64_t)arg0 }; @@ -24,6 +30,30 @@ namespace ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, (int8_t*)args, sizeof(args), (int8_t*)&result); return; } + static void CallInterpreter_I32_I32_RetVoid(uintptr_t stackArg, int32_t arg0, int32_t arg1, PCODE portableEntrypoint) + { + int64_t args[2] = { (int64_t)arg0, (int64_t)arg1 }; + + void * result = NULL; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, (int8_t*)args, sizeof(args), (int8_t*)&result); + return; + } + static void CallInterpreter_I32_I32_I32_RetVoid(uintptr_t stackArg, int32_t arg0, int32_t arg1, int32_t arg2, PCODE portableEntrypoint) + { + int64_t args[3] = { (int64_t)arg0, (int64_t)arg1, (int64_t)arg2 }; + + void * result = NULL; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, (int8_t*)args, sizeof(args), (int8_t*)&result); + return; + } + static void CallInterpreter_I32_I32_I32_I32_RetVoid(uintptr_t stackArg, int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, PCODE portableEntrypoint) + { + int64_t args[4] = { (int64_t)arg0, (int64_t)arg1, (int64_t)arg2, (int64_t)arg3 }; + + void * result = NULL; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, (int8_t*)args, sizeof(args), (int8_t*)&result); + return; + } static int32_t CallInterpreter_RetI32(uintptr_t stackArg, PCODE portableEntrypoint) { void * result = NULL; @@ -97,7 +127,11 @@ namespace } const StringToWasmSigThunk g_wasmPortableEntryPointThunks[] = { + { "vp", (void*)&CallInterpreter_RetVoid }, { "vip", (void*)&CallInterpreter_I32_RetVoid }, + { "viip", (void*)&CallInterpreter_I32_I32_RetVoid }, + { "viiip", (void*)&CallInterpreter_I32_I32_I32_RetVoid }, + { "viiiip", (void*)&CallInterpreter_I32_I32_I32_I32_RetVoid }, { "ip", (void*)&CallInterpreter_RetI32 }, { "iip", (void*)&CallInterpreter_I32_RetI32 }, { "iiip", (void*)&CallInterpreter_I32_I32_RetI32 }, @@ -732,7 +766,7 @@ namespace // Another thread won the race, discard ours delete newTable; } - table = thunkCache; + table = portableEntrypointThunkCache; } void* thunk; From 7aa0e6ce2e162860dc7d5ae835243a8b7763228b Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Thu, 9 Apr 2026 16:39:43 -0700 Subject: [PATCH 10/15] Convince R2R functions that they can call OTHER R2R functions --- src/coreclr/vm/interpexec.h | 2 + src/coreclr/vm/peimagelayout.cpp | 2 +- src/coreclr/vm/prestub.cpp | 29 ++++++++-- src/coreclr/vm/wasm/helpers.cpp | 96 +++++++++++++++++++++++++++----- 4 files changed, 107 insertions(+), 22 deletions(-) diff --git a/src/coreclr/vm/interpexec.h b/src/coreclr/vm/interpexec.h index 48c4c2c1a6f8ff..9f234145069595 100644 --- a/src/coreclr/vm/interpexec.h +++ b/src/coreclr/vm/interpexec.h @@ -130,6 +130,8 @@ struct ManagedMethodParam Object** pContinuationRet; }; +void InvokeManagedMethod(ManagedMethodParam *pParam); + struct CalliStubParam { PCODE ftn; diff --git a/src/coreclr/vm/peimagelayout.cpp b/src/coreclr/vm/peimagelayout.cpp index 3e53bdf525b212..8ef26225d8777f 100644 --- a/src/coreclr/vm/peimagelayout.cpp +++ b/src/coreclr/vm/peimagelayout.cpp @@ -431,7 +431,7 @@ void PEImageLayout::ApplyBaseRelocations(bool relocationMustWriteCopy) dirPos += fixupsSize; } - _ASSERTE(dirSize == dirPos); + _ASSERTE(dirSize == dirPos || !IsPEFormat()); if (dwOldProtection != 0) { diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index df60056e13390e..8aeeb22e1649e0 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -2079,7 +2079,15 @@ void ExecuteInterpretedMethodWithArgs(TADDR targetIp, int8_t* args, size_t argSi } #ifdef FEATURE_PORTABLE_ENTRYPOINTS -PCODE ExecuteInterpretedMethodWithArgs_PortableEntryPoint_Worker(PCODE portableEntrypoint, int8_t* args, size_t argsSize) +// In this case, we're using this entrypoint like the prestub. +// We need to run DoPrestub to have the runtime either compile the interpreter code, or find the R2R implementation +// then we need to dispatch onwards to the correct target. +// Continuing on from here for interpreter targets is straightforward, but for R2R targets we need to dispatch back +// to WebAssembly code. To avoid needing all of the R2R to interpreter thunks have logic for tail-calling onto more +// R2R functions, we utilize the InvokeManagedMethod path which will utilize an Interpreter to R2R thunk for this call. +void ExecuteInterpretedMethodWithArgs_PortableEntryPoint(PCODE portableEntrypoint, int8_t* args, size_t argsSize, int8_t* retBuff); + +void ExecuteInterpretedMethodWithArgs_PortableEntryPoint_Complex(PCODE portableEntrypoint, int8_t* args, size_t argsSize, int8_t* retBuff) { MethodDesc* pMethod = PortableEntryPoint::GetMethodDesc(portableEntrypoint); InterpByteCodeStart* targetIp = pMethod->GetInterpreterCode(); @@ -2089,27 +2097,36 @@ PCODE ExecuteInterpretedMethodWithArgs_PortableEntryPoint_Worker(PCODE portableE GCX_PREEMP(); (void)pMethod->DoPrestub(NULL /* MethodTable */, CallerGCMode::Coop); targetIp = pMethod->GetInterpreterCode(); + + if (targetIp == NULL) + { + _ASSERTE(!PortableEntryPoint::PrefersInterpreterEntryPoint(portableEntrypoint)); + ManagedMethodParam param = { pMethod, args, retBuff, (PCODE)targetIp, nullptr /* WASM-TODO, handle RuntimeAsync */}; + return InvokeManagedMethod(¶m); + } + GCPROTECT_END(); } _ASSERTE((PCODE)targetIp == (PCODE)PortableEntryPoint::GetInterpreterData(portableEntrypoint)); - - return (PCODE)targetIp; + ExecuteInterpretedMethodWithArgs((TADDR)targetIp, args, argsSize, retBuff, (PCODE)&ExecuteInterpretedMethodWithArgs_PortableEntryPoint); + return; } -void ExecuteInterpretedMethodWithArgs_PortableEntryPoint(PCODE portableEntrypoint, int8_t* args, size_t argsSize, void* retBuff) +void ExecuteInterpretedMethodWithArgs_PortableEntryPoint(PCODE portableEntrypoint, int8_t* args, size_t argsSize, int8_t* retBuff) { PCODE targetIp; if (!PortableEntryPoint::HasInterpreterData(portableEntrypoint)) { - targetIp = ExecuteInterpretedMethodWithArgs_PortableEntryPoint_Worker(portableEntrypoint, args, argsSize); + // In this case, we're using this entrypoint like the prestub. + ExecuteInterpretedMethodWithArgs_PortableEntryPoint_Complex(portableEntrypoint, args, argsSize, retBuff); } else { targetIp = (PCODE)PortableEntryPoint::GetInterpreterData(portableEntrypoint); + ExecuteInterpretedMethodWithArgs((TADDR)targetIp, args, argsSize, retBuff, (PCODE)&ExecuteInterpretedMethodWithArgs_PortableEntryPoint); } - ExecuteInterpretedMethodWithArgs((TADDR)targetIp, args, argsSize, retBuff, (PCODE)&ExecuteInterpretedMethodWithArgs_PortableEntryPoint); } #endif // FEATURE_PORTABLE_ENTRYPOINTS diff --git a/src/coreclr/vm/wasm/helpers.cpp b/src/coreclr/vm/wasm/helpers.cpp index 0a32732add2e2e..e068d57ed4adde 100644 --- a/src/coreclr/vm/wasm/helpers.cpp +++ b/src/coreclr/vm/wasm/helpers.cpp @@ -10,19 +10,85 @@ #include "cgensys.h" #include "readytorun.h" -void ExecuteInterpretedMethodWithArgs_PortableEntryPoint(PCODE portableEntrypoint, int8_t* args, size_t argsSize, void* retBuff); +void ExecuteInterpretedMethodWithArgs_PortableEntryPoint(PCODE portableEntrypoint, int8_t* args, size_t argsSize, int8_t* retBuff); + +#define WASM_WRAPPER_FUNC_INITIAL \ +{ \ + asm ("local.get 0\n" /* Capture stackArg onto the stack*/ \ + "global.get __stack_pointer\n" /* Get current value of stack global */ \ + "local.set 0\n" /* Store current stack global into stackArg local */ \ + "global.set __stack_pointer\n" /* Set stack global to the initial value of stackArg, which is the current stack pointer for the interpreter call */ + +#define WASM_WRAPPER_FUNC_EPILOG(_method) \ + "call %0\n" /* Call the actual implementation function*/ \ + "local.get 0\n" /* Load the original stack pointer from stack Arg local */ \ + "global.set __stack_pointer\n" /* Restore the original stack pointer to the stack global */ \ + "return" :: "i" (_method ## _IMPL)); \ +} + +#define WASM_WRAPPER_FUNC_0(_rettype, _method) __attribute__((naked)) _rettype _method(uintptr_t stackArg) WASM_WRAPPER_FUNC_INITIAL WASM_WRAPPER_FUNC_EPILOG(_method) +#define WASM_WRAPPER_FUNC_1(_rettype, _method, a) __attribute__((naked)) _rettype _method(uintptr_t stackArg, a) WASM_WRAPPER_FUNC_INITIAL "local.get 1\n" WASM_WRAPPER_FUNC_EPILOG(_method) +#define WASM_WRAPPER_FUNC_2(_rettype, _method, a, b) __attribute__((naked)) _rettype _method(uintptr_t stackArg, a, b) WASM_WRAPPER_FUNC_INITIAL "local.get 1\nlocal.get 2\n" WASM_WRAPPER_FUNC_EPILOG(_method) +#define WASM_WRAPPER_FUNC_3(_rettype, _method, a, b, c) __attribute__((naked)) _rettype _method(uintptr_t stackArg, a, b, c) WASM_WRAPPER_FUNC_INITIAL "local.get 1\nlocal.get 2\nlocal.get 3\n" WASM_WRAPPER_FUNC_EPILOG(_method) +#define WASM_WRAPPER_FUNC_4(_rettype, _method, a, b, c, d) __attribute__((naked)) _rettype _method(uintptr_t stackArg, a, b, c, d) WASM_WRAPPER_FUNC_INITIAL "local.get 1\nlocal.get 2\nlocal.get 3\nlocal.get 4\n" WASM_WRAPPER_FUNC_EPILOG(_method) +#define WASM_WRAPPER_FUNC_5(_rettype, _method, a, b, c, d, e) __attribute__((naked)) _rettype _method(uintptr_t stackArg, a, b, c, d, e) WASM_WRAPPER_FUNC_INITIAL "local.get 1\nlocal.get 2\nlocal.get 3\nlocal.get 4\nlocal.get 5\n" WASM_WRAPPER_FUNC_EPILOG(_method) +#define WASM_WRAPPER_FUNC_6(_rettype, _method, a, b, c, d, e, f) __attribute__((naked)) _rettype _method(uintptr_t stackArg, a, b, c, d, e, f) WASM_WRAPPER_FUNC_INITIAL "local.get 1\nlocal.get 2\nlocal.get 3\nlocal.get 4\nlocal.get 5\nlocal.get 6\n" WASM_WRAPPER_FUNC_EPILOG(_method) +#define WASM_WRAPPER_FUNC_7(_rettype, _method, a, b, c, d, e, f, g) __attribute__((naked)) _rettype _method(uintptr_t stackArg, a, b, c, d, e, f, g) WASM_WRAPPER_FUNC_INITIAL "local.get 1\nlocal.get 2\nlocal.get 3\nlocal.get 4\nlocal.get 5\nlocal.get 6\nlocal.get 7\n" WASM_WRAPPER_FUNC_EPILOG(_method) +#define WASM_WRAPPER_FUNC_8(_rettype, _method, a, b, c, d, e, f, g, h) __attribute__((naked)) _rettype _method(uintptr_t stackArg, a, b, c, d, e, f, g, h) WASM_WRAPPER_FUNC_INITIAL "local.get 1\nlocal.get 2\nlocal.get 3\nlocal.get 4\nlocal.get 5\nlocal.get 6\nlocal.get 7\nlocal.get 8\n" WASM_WRAPPER_FUNC_EPILOG(_method) +#define WASM_WRAPPER_FUNC_9(_rettype, _method, a, b, c, d, e, f, g, h, i) __attribute__((naked)) _rettype _method(uintptr_t stackArg, a, b, c, d, e, f, g, h, i) WASM_WRAPPER_FUNC_INITIAL "local.get 1\nlocal.get 2\nlocal.get 3\nlocal.get 4\nlocal.get 5\nlocal.get 6\nlocal.get 7\nlocal.get 8\nlocal.get 9\n" WASM_WRAPPER_FUNC_EPILOG(_method) + +#define WASM_CALLABLE_FUNC_0(_rettype, _method) _rettype _method ## _IMPL (); \ + WASM_WRAPPER_FUNC_0(_rettype, _method) \ + _rettype _method ## _IMPL () + +#define WASM_CALLABLE_FUNC_1(_rettype, _method, a) _rettype _method ## _IMPL (a); \ + WASM_WRAPPER_FUNC_1(_rettype, _method, a) \ + _rettype _method ## _IMPL (a) + +#define WASM_CALLABLE_FUNC_2(_rettype, _method, a, b) _rettype _method ## _IMPL (a, b); \ + WASM_WRAPPER_FUNC_2(_rettype, _method, a, b) \ + _rettype _method ## _IMPL (a, b) + +#define WASM_CALLABLE_FUNC_3(_rettype, _method, a, b, c) _rettype _method ## _IMPL (a, b, c); \ + WASM_WRAPPER_FUNC_3(_rettype, _method, a, b, c) \ + _rettype _method ## _IMPL (a, b, c) + +#define WASM_CALLABLE_FUNC_4(_rettype, _method, a, b, c, d) _rettype _method ## _IMPL (a, b, c, d); \ + WASM_WRAPPER_FUNC_4(_rettype, _method, a, b, c, d) \ + _rettype _method ## _IMPL (a, b, c, d) + +#define WASM_CALLABLE_FUNC_5(_rettype, _method, a, b, c, d, e) _rettype _method ## _IMPL (a, b, c, d, e); \ + WASM_WRAPPER_FUNC_5(_rettype, _method, a, b, c, d, e) \ + _rettype _method ## _IMPL (a, b, c, d, e) + +#define WASM_CALLABLE_FUNC_6(_rettype, _method, a, b, c, d, e, f) _rettype _method ## _IMPL (a, b, c, d, e, f); \ + WASM_WRAPPER_FUNC_6(_rettype, _method, a, b, c, d, e, f) \ + _rettype _method ## _IMPL (a, b, c, d, e, f) + +#define WASM_CALLABLE_FUNC_7(_rettype, _method, a, b, c, d, e, f, g) _rettype _method ## _IMPL (a, b, c, d, e, f, g); \ + WASM_WRAPPER_FUNC_7(_rettype, _method, a, b, c, d, e, f, g) \ + _rettype _method ## _IMPL (a, b, c, d, e, f, g) + +#define WASM_CALLABLE_FUNC_8(_rettype, _method, a, b, c, d, e, f, g, h) _rettype _method ## _IMPL (a, b, c, d, e, f, g, h); \ + WASM_WRAPPER_FUNC_8(_rettype, _method, a, b, c, d, e, f, g, h) \ + _rettype _method ## _IMPL (a, b, c, d, e, f, g, h) + +#define WASM_CALLABLE_FUNC_9(_rettype, _method, a, b, c, d, e, f, g, h, i) _rettype _method ## _IMPL (a, b, c, d, e, f, g, h, i); \ + WASM_WRAPPER_FUNC_9(_rettype, _method, a, b, c, d, e, f, g, h, i) \ + _rettype _method ## _IMPL (a, b, c, d, e, f, g, h, i) + // ------------------------------------------------- // Logic that will eventually mostly be pregenerated for R2R to interpreter code // ------------------------------------------------- namespace { - static void CallInterpreter_RetVoid(uintptr_t stackArg, PCODE portableEntrypoint) + WASM_CALLABLE_FUNC_1(void, CallInterpreter_RetVoid, PCODE portableEntrypoint) { void * result = NULL; ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, nullptr, 0, (int8_t*)&result); return; } - static void CallInterpreter_I32_RetVoid(uintptr_t stackArg, int32_t arg0, PCODE portableEntrypoint) + WASM_CALLABLE_FUNC_2(void, CallInterpreter_I32_RetVoid, int32_t arg0, PCODE portableEntrypoint) { int64_t args[1] = { (int64_t)arg0 }; @@ -30,7 +96,7 @@ namespace ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, (int8_t*)args, sizeof(args), (int8_t*)&result); return; } - static void CallInterpreter_I32_I32_RetVoid(uintptr_t stackArg, int32_t arg0, int32_t arg1, PCODE portableEntrypoint) + WASM_CALLABLE_FUNC_3(void, CallInterpreter_I32_I32_RetVoid, int32_t arg0, int32_t arg1, PCODE portableEntrypoint) { int64_t args[2] = { (int64_t)arg0, (int64_t)arg1 }; @@ -38,7 +104,7 @@ namespace ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, (int8_t*)args, sizeof(args), (int8_t*)&result); return; } - static void CallInterpreter_I32_I32_I32_RetVoid(uintptr_t stackArg, int32_t arg0, int32_t arg1, int32_t arg2, PCODE portableEntrypoint) + WASM_CALLABLE_FUNC_4(void, CallInterpreter_I32_I32_I32_RetVoid, int32_t arg0, int32_t arg1, int32_t arg2, PCODE portableEntrypoint) { int64_t args[3] = { (int64_t)arg0, (int64_t)arg1, (int64_t)arg2 }; @@ -46,7 +112,7 @@ namespace ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, (int8_t*)args, sizeof(args), (int8_t*)&result); return; } - static void CallInterpreter_I32_I32_I32_I32_RetVoid(uintptr_t stackArg, int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, PCODE portableEntrypoint) + WASM_CALLABLE_FUNC_5(void, CallInterpreter_I32_I32_I32_I32_RetVoid, int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, PCODE portableEntrypoint) { int64_t args[4] = { (int64_t)arg0, (int64_t)arg1, (int64_t)arg2, (int64_t)arg3 }; @@ -54,13 +120,13 @@ namespace ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, (int8_t*)args, sizeof(args), (int8_t*)&result); return; } - static int32_t CallInterpreter_RetI32(uintptr_t stackArg, PCODE portableEntrypoint) + WASM_CALLABLE_FUNC_1(int32_t, CallInterpreter_RetI32, PCODE portableEntrypoint) { void * result = NULL; ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, nullptr, 0, (int8_t*)&result); return (int32_t)result; } - static int32_t CallInterpreter_I32_RetI32(uintptr_t stackArg, int32_t arg0, PCODE portableEntrypoint) + WASM_CALLABLE_FUNC_2(int32_t, CallInterpreter_I32_RetI32, int32_t arg0, PCODE portableEntrypoint) { int64_t args[1] = { (int64_t)arg0 }; @@ -68,7 +134,7 @@ namespace ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, (int8_t*)args, sizeof(args), (int8_t*)&result); return (int32_t)result; } - static int32_t CallInterpreter_I32_I32_RetI32(uintptr_t stackArg, int32_t arg0, int32_t arg1, PCODE portableEntrypoint) + WASM_CALLABLE_FUNC_3(int32_t, CallInterpreter_I32_I32_RetI32, int32_t arg0, int32_t arg1, PCODE portableEntrypoint) { int64_t args[2] = { (int64_t)arg0, (int64_t)arg1 }; @@ -76,7 +142,7 @@ namespace ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, (int8_t*)args, sizeof(args), (int8_t*)&result); return (int32_t)result; } - static int32_t CallInterpreter_I32_I32_I32_RetI32(uintptr_t stackArg, int32_t arg0, int32_t arg1, int32_t arg2, PCODE portableEntrypoint) + WASM_CALLABLE_FUNC_4(int32_t, CallInterpreter_I32_I32_I32_RetI32, int32_t arg0, int32_t arg1, int32_t arg2, PCODE portableEntrypoint) { int64_t args[3] = { (int64_t)arg0, (int64_t)arg1, (int64_t)arg2 }; @@ -84,7 +150,7 @@ namespace ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, (int8_t*)args, sizeof(args), (int8_t*)&result); return (int32_t)result; } - static int32_t CallInterpreter_I32_I32_I32_I32_RetI32(uintptr_t stackArg, int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3,PCODE portableEntrypoint) + WASM_CALLABLE_FUNC_5(int32_t, CallInterpreter_I32_I32_I32_I32_RetI32, int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, PCODE portableEntrypoint) { int64_t args[4] = { (int64_t)arg0, (int64_t)arg1, (int64_t)arg2, (int64_t)arg3 }; @@ -92,7 +158,7 @@ namespace ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, (int8_t*)args, sizeof(args), (int8_t*)&result); return (int32_t)result; } - static int32_t CallInterpreter_I32_I32_I32_I32_I32_RetI32(uintptr_t stackArg, int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4, PCODE portableEntrypoint) + WASM_CALLABLE_FUNC_6(int32_t, CallInterpreter_I32_I32_I32_I32_I32_RetI32, int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4, PCODE portableEntrypoint) { int64_t args[5] = { (int64_t)arg0, (int64_t)arg1, (int64_t)arg2, (int64_t)arg3, (int64_t)arg4 }; @@ -100,7 +166,7 @@ namespace ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, (int8_t*)args, sizeof(args), (int8_t*)&result); return (int32_t)result; } - static int32_t CallInterpreter_I32_I32_I32_I32_I32_I32_RetI32(uintptr_t stackArg, int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4, int32_t arg5, PCODE portableEntrypoint) + WASM_CALLABLE_FUNC_7(int32_t, CallInterpreter_I32_I32_I32_I32_I32_I32_RetI32, int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4, int32_t arg5, PCODE portableEntrypoint) { int64_t args[6] = { (int64_t)arg0, (int64_t)arg1, (int64_t)arg2, (int64_t)arg3, (int64_t)arg4, (int64_t)arg5 }; @@ -108,7 +174,7 @@ namespace ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, (int8_t*)args, sizeof(args), (int8_t*)&result); return (int32_t)result; } - static int32_t CallInterpreter_I32_I32_I32_I32_I32_I32_I32_RetI32(uintptr_t stackArg, int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4, int32_t arg5, int32_t arg6, PCODE portableEntrypoint) + WASM_CALLABLE_FUNC_8(int32_t, CallInterpreter_I32_I32_I32_I32_I32_I32_I32_RetI32, int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4, int32_t arg5, int32_t arg6, PCODE portableEntrypoint) { int64_t args[7] = { (int64_t)arg0, (int64_t)arg1, (int64_t)arg2, (int64_t)arg3, (int64_t)arg4, (int64_t)arg5, (int64_t)arg6 }; @@ -116,7 +182,7 @@ namespace ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, (int8_t*)args, sizeof(args), (int8_t*)&result); return (int32_t)result; } - static int32_t CallInterpreter_I32_I32_I32_I32_I32_I32_I32_I32_RetI32(uintptr_t stackArg, int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4, int32_t arg5, int32_t arg6, int32_t arg7, PCODE portableEntrypoint) + WASM_CALLABLE_FUNC_9(int32_t, CallInterpreter_I32_I32_I32_I32_I32_I32_I32_I32_RetI32, int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4, int32_t arg5, int32_t arg6, int32_t arg7, PCODE portableEntrypoint) { int64_t args[8] = { (int64_t)arg0, (int64_t)arg1, (int64_t)arg2, (int64_t)arg3, (int64_t)arg4, (int64_t)arg5, (int64_t)arg6, (int64_t)arg7 }; From 53e5837468eda2177c89d928d305650012d9e1dd Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Fri, 10 Apr 2026 11:24:38 -0700 Subject: [PATCH 11/15] Add better error checking to libCorerun.js --- src/coreclr/hosts/corerun/wasm/libCorerun.js | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/coreclr/hosts/corerun/wasm/libCorerun.js b/src/coreclr/hosts/corerun/wasm/libCorerun.js index a47e145144f663..333a456c4948fd 100644 --- a/src/coreclr/hosts/corerun/wasm/libCorerun.js +++ b/src/coreclr/hosts/corerun/wasm/libCorerun.js @@ -178,7 +178,15 @@ function libCoreRunFactory() { } catch (e) { return false; } - const wasmModule = new WebAssembly.Module(wasmBytes); + let wasmModule; + try { + wasmModule = new WebAssembly.Module(wasmBytes); + } catch (e) { + const errormessage = e instanceof Error ? e.message : String(e); + console.error("Failed to compile WebAssembly module for Webcil image:", {wasmPath, errormessage}); + return false; + } + const tableStartIndex = wasmTable.length; var payloadSize = 0; @@ -195,7 +203,14 @@ function libCoreRunFactory() { console.error("Webcil payload size is 0; cannot load image"); return false; } - wasmTable.grow(tableSize); + + try { + wasmTable.grow(tableSize); + } catch (e) { + const errormessage = e instanceof Error ? e.message : String(e); + console.error("Failed to grow WebAssembly table for Webcil image:", {wasmPath, errormessage}); + return false; + } var payloadPtr = 0; var wasmInstance; From 05a3c278b560aaedd84c52f3565ca410958c5c43 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Fri, 10 Apr 2026 11:25:07 -0700 Subject: [PATCH 12/15] Implement correct signature mapping for PortableEntryPoints which are FCalls --- .../vm/wasm/callhelpers-interp-to-managed.cpp | 161 ++++++++++++------ .../coreclr/InternalCallSignatureCollector.cs | 61 +++++++ .../coreclr/ManagedToNativeGenerator.cs | 29 ++-- .../WasmAppBuilder/coreclr/SignatureMapper.cs | 7 +- 4 files changed, 183 insertions(+), 75 deletions(-) create mode 100644 src/tasks/WasmAppBuilder/coreclr/InternalCallSignatureCollector.cs diff --git a/src/coreclr/vm/wasm/callhelpers-interp-to-managed.cpp b/src/coreclr/vm/wasm/callhelpers-interp-to-managed.cpp index a36b5b9eb07430..0dd59a9b8c34bb 100644 --- a/src/coreclr/vm/wasm/callhelpers-interp-to-managed.cpp +++ b/src/coreclr/vm/wasm/callhelpers-interp-to-managed.cpp @@ -19,28 +19,28 @@ namespace { - static void CallFunc_F64_RetF64(PCODE pcode, int8_t* pArgs, int8_t* pRet) + static void CallFunc_F64_F64_F64_RetF64_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) { - double (*fptr)(double) = (double (*)(double))pcode; - *((double*)pRet) = (*fptr)(ARG_F64(0)); + double (*fptr)(uintptr_t, double, double, double, PCODE) = (double (*)(uintptr_t, double, double, double, PCODE))pcode; + *((double*)pRet) = (*fptr)(emscripten_stack_get_current(), ARG_F64(0), ARG_F64(1), ARG_F64(2), pPortableEntryPointContext); } - static void CallFunc_F64_F64_RetF64(PCODE pcode, int8_t* pArgs, int8_t* pRet) + static void CallFunc_F64_F64_RetF64_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) { - double (*fptr)(double, double) = (double (*)(double, double))pcode; - *((double*)pRet) = (*fptr)(ARG_F64(0), ARG_F64(1)); + double (*fptr)(uintptr_t, double, double, PCODE) = (double (*)(uintptr_t, double, double, PCODE))pcode; + *((double*)pRet) = (*fptr)(emscripten_stack_get_current(), ARG_F64(0), ARG_F64(1), pPortableEntryPointContext); } - static void CallFunc_F64_F64_F64_RetF64(PCODE pcode, int8_t* pArgs, int8_t* pRet) + static void CallFunc_F64_I32_RetF64_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) { - double (*fptr)(double, double, double) = (double (*)(double, double, double))pcode; - *((double*)pRet) = (*fptr)(ARG_F64(0), ARG_F64(1), ARG_F64(2)); + double (*fptr)(uintptr_t, double, int32_t, PCODE) = (double (*)(uintptr_t, double, int32_t, PCODE))pcode; + *((double*)pRet) = (*fptr)(emscripten_stack_get_current(), ARG_F64(0), ARG_I32(1), pPortableEntryPointContext); } - static void CallFunc_F64_I32_RetF64(PCODE pcode, int8_t* pArgs, int8_t* pRet) + static void CallFunc_F64_RetF64_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) { - double (*fptr)(double, int32_t) = (double (*)(double, int32_t))pcode; - *((double*)pRet) = (*fptr)(ARG_F64(0), ARG_I32(1)); + double (*fptr)(uintptr_t, double, PCODE) = (double (*)(uintptr_t, double, PCODE))pcode; + *((double*)pRet) = (*fptr)(emscripten_stack_get_current(), ARG_F64(0), pPortableEntryPointContext); } static void CallFunc_I32_RetF64(PCODE pcode, int8_t* pArgs, int8_t* pRet) @@ -49,28 +49,28 @@ namespace *((double*)pRet) = (*fptr)(ARG_I32(0)); } - static void CallFunc_F32_RetF32(PCODE pcode, int8_t* pArgs, int8_t* pRet) + static void CallFunc_F32_F32_F32_RetF32_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) { - float (*fptr)(float) = (float (*)(float))pcode; - *((float*)pRet) = (*fptr)(ARG_F32(0)); + float (*fptr)(uintptr_t, float, float, float, PCODE) = (float (*)(uintptr_t, float, float, float, PCODE))pcode; + *((float*)pRet) = (*fptr)(emscripten_stack_get_current(), ARG_F32(0), ARG_F32(1), ARG_F32(2), pPortableEntryPointContext); } - static void CallFunc_F32_F32_RetF32(PCODE pcode, int8_t* pArgs, int8_t* pRet) + static void CallFunc_F32_F32_RetF32_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) { - float (*fptr)(float, float) = (float (*)(float, float))pcode; - *((float*)pRet) = (*fptr)(ARG_F32(0), ARG_F32(1)); + float (*fptr)(uintptr_t, float, float, PCODE) = (float (*)(uintptr_t, float, float, PCODE))pcode; + *((float*)pRet) = (*fptr)(emscripten_stack_get_current(), ARG_F32(0), ARG_F32(1), pPortableEntryPointContext); } - static void CallFunc_F32_F32_F32_RetF32(PCODE pcode, int8_t* pArgs, int8_t* pRet) + static void CallFunc_F32_I32_RetF32_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) { - float (*fptr)(float, float, float) = (float (*)(float, float, float))pcode; - *((float*)pRet) = (*fptr)(ARG_F32(0), ARG_F32(1), ARG_F32(2)); + float (*fptr)(uintptr_t, float, int32_t, PCODE) = (float (*)(uintptr_t, float, int32_t, PCODE))pcode; + *((float*)pRet) = (*fptr)(emscripten_stack_get_current(), ARG_F32(0), ARG_I32(1), pPortableEntryPointContext); } - static void CallFunc_F32_I32_RetF32(PCODE pcode, int8_t* pArgs, int8_t* pRet) + static void CallFunc_F32_RetF32_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) { - float (*fptr)(float, int32_t) = (float (*)(float, int32_t))pcode; - *((float*)pRet) = (*fptr)(ARG_F32(0), ARG_I32(1)); + float (*fptr)(uintptr_t, float, PCODE) = (float (*)(uintptr_t, float, PCODE))pcode; + *((float*)pRet) = (*fptr)(emscripten_stack_get_current(), ARG_F32(0), pPortableEntryPointContext); } static void CallFunc_Void_RetI32(PCODE pcode, int8_t* pArgs, int8_t* pRet) @@ -127,6 +127,24 @@ namespace *((int32_t*)pRet) = (*fptr)(ARG_I32(0), ARG_I32(1), ARG_I32(2), ARG_I32(3), ARG_I32(4), ARG_I32(5), ARG_I32(6), ARG_I32(7), ARG_I32(8), ARG_I32(9), ARG_I32(10), ARG_I32(11), ARG_I32(12), ARG_I32(13)); } + static void CallFunc_I32_I32_I32_I32_I32_I32_RetI32_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) + { + int32_t (*fptr)(uintptr_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, PCODE) = (int32_t (*)(uintptr_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, PCODE))pcode; + *((int32_t*)pRet) = (*fptr)(emscripten_stack_get_current(), ARG_I32(0), ARG_I32(1), ARG_I32(2), ARG_I32(3), ARG_I32(4), ARG_I32(5), pPortableEntryPointContext); + } + + static void CallFunc_I32_I32_I32_I32_I32_RetI32_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) + { + int32_t (*fptr)(uintptr_t, int32_t, int32_t, int32_t, int32_t, int32_t, PCODE) = (int32_t (*)(uintptr_t, int32_t, int32_t, int32_t, int32_t, int32_t, PCODE))pcode; + *((int32_t*)pRet) = (*fptr)(emscripten_stack_get_current(), ARG_I32(0), ARG_I32(1), ARG_I32(2), ARG_I32(3), ARG_I32(4), pPortableEntryPointContext); + } + + static void CallFunc_I32_I32_I32_I32_RetI32_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) + { + int32_t (*fptr)(uintptr_t, int32_t, int32_t, int32_t, int32_t, PCODE) = (int32_t (*)(uintptr_t, int32_t, int32_t, int32_t, int32_t, PCODE))pcode; + *((int32_t*)pRet) = (*fptr)(emscripten_stack_get_current(), ARG_I32(0), ARG_I32(1), ARG_I32(2), ARG_I32(3), pPortableEntryPointContext); + } + static void CallFunc_I32_I32_I32_I64_RetI32(PCODE pcode, int8_t* pArgs, int8_t* pRet) { int32_t (*fptr)(int32_t, int32_t, int32_t, int64_t) = (int32_t (*)(int32_t, int32_t, int32_t, int64_t))pcode; @@ -325,28 +343,40 @@ namespace *((int64_t*)pRet) = (*fptr)(ARG_I32(0), ARG_I32(1), ARG_I32(2), ARG_I64(3)); } - static void CallFunc_I32_I64_RetI64(PCODE pcode, int8_t* pArgs, int8_t* pRet) - { - int64_t (*fptr)(int32_t, int64_t) = (int64_t (*)(int32_t, int64_t))pcode; - *((int64_t*)pRet) = (*fptr)(ARG_I32(0), ARG_I64(1)); - } - static void CallFunc_I32_I64_I32_RetI64(PCODE pcode, int8_t* pArgs, int8_t* pRet) { int64_t (*fptr)(int32_t, int64_t, int32_t) = (int64_t (*)(int32_t, int64_t, int32_t))pcode; *((int64_t*)pRet) = (*fptr)(ARG_I32(0), ARG_I64(1), ARG_I32(2)); } - static void CallFunc_I32_I64_I64_RetI64(PCODE pcode, int8_t* pArgs, int8_t* pRet) + static void CallFunc_I32_I64_I64_RetI64_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) { - int64_t (*fptr)(int32_t, int64_t, int64_t) = (int64_t (*)(int32_t, int64_t, int64_t))pcode; - *((int64_t*)pRet) = (*fptr)(ARG_I32(0), ARG_I64(1), ARG_I64(2)); + int64_t (*fptr)(uintptr_t, int32_t, int64_t, int64_t, PCODE) = (int64_t (*)(uintptr_t, int32_t, int64_t, int64_t, PCODE))pcode; + *((int64_t*)pRet) = (*fptr)(emscripten_stack_get_current(), ARG_I32(0), ARG_I64(1), ARG_I64(2), pPortableEntryPointContext); } - static void CallFunc_I64_I64_RetI64(PCODE pcode, int8_t* pArgs, int8_t* pRet) + static void CallFunc_I32_I64_RetI64_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) { - int64_t (*fptr)(int64_t, int64_t) = (int64_t (*)(int64_t, int64_t))pcode; - *((int64_t*)pRet) = (*fptr)(ARG_I64(0), ARG_I64(1)); + int64_t (*fptr)(uintptr_t, int32_t, int64_t, PCODE) = (int64_t (*)(uintptr_t, int32_t, int64_t, PCODE))pcode; + *((int64_t*)pRet) = (*fptr)(emscripten_stack_get_current(), ARG_I32(0), ARG_I64(1), pPortableEntryPointContext); + } + + static void CallFunc_I32_RetI64_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) + { + int64_t (*fptr)(uintptr_t, int32_t, PCODE) = (int64_t (*)(uintptr_t, int32_t, PCODE))pcode; + *((int64_t*)pRet) = (*fptr)(emscripten_stack_get_current(), ARG_I32(0), pPortableEntryPointContext); + } + + static void CallFunc_I64_I64_RetI64_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) + { + int64_t (*fptr)(uintptr_t, int64_t, int64_t, PCODE) = (int64_t (*)(uintptr_t, int64_t, int64_t, PCODE))pcode; + *((int64_t*)pRet) = (*fptr)(emscripten_stack_get_current(), ARG_I64(0), ARG_I64(1), pPortableEntryPointContext); + } + + static void CallFunc_Void_RetI64_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) + { + int64_t (*fptr)(uintptr_t, PCODE) = (int64_t (*)(uintptr_t, PCODE))pcode; + *((int64_t*)pRet) = (*fptr)(emscripten_stack_get_current(), pPortableEntryPointContext); } static void CallFunc_Void_RetVoid(PCODE pcode, int8_t* pArgs, int8_t* pRet) @@ -355,16 +385,16 @@ namespace (*fptr)(); } - static void CallFunc_F64_I32_I32_RetVoid(PCODE pcode, int8_t* pArgs, int8_t* pRet) + static void CallFunc_F64_I32_I32_RetVoid_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) { - void (*fptr)(double, int32_t, int32_t) = (void (*)(double, int32_t, int32_t))pcode; - (*fptr)(ARG_F64(0), ARG_I32(1), ARG_I32(2)); + void (*fptr)(uintptr_t, double, int32_t, int32_t, PCODE) = (void (*)(uintptr_t, double, int32_t, int32_t, PCODE))pcode; + (*fptr)(emscripten_stack_get_current(), ARG_F64(0), ARG_I32(1), ARG_I32(2), pPortableEntryPointContext); } - static void CallFunc_F32_I32_I32_RetVoid(PCODE pcode, int8_t* pArgs, int8_t* pRet) + static void CallFunc_F32_I32_I32_RetVoid_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) { - void (*fptr)(float, int32_t, int32_t) = (void (*)(float, int32_t, int32_t))pcode; - (*fptr)(ARG_F32(0), ARG_I32(1), ARG_I32(2)); + void (*fptr)(uintptr_t, float, int32_t, int32_t, PCODE) = (void (*)(uintptr_t, float, int32_t, int32_t, PCODE))pcode; + (*fptr)(emscripten_stack_get_current(), ARG_F32(0), ARG_I32(1), ARG_I32(2), pPortableEntryPointContext); } static void CallFunc_I32_RetVoid(PCODE pcode, int8_t* pArgs, int8_t* pRet) @@ -415,6 +445,12 @@ namespace (*fptr)(ARG_I32(0), ARG_I32(1), ARG_I32(2), ARG_IND(3), ARG_IND(4), ARG_I32(5)); } + static void CallFunc_I32_I32_I32_RetVoid_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) + { + void (*fptr)(uintptr_t, int32_t, int32_t, int32_t, PCODE) = (void (*)(uintptr_t, int32_t, int32_t, int32_t, PCODE))pcode; + (*fptr)(emscripten_stack_get_current(), ARG_I32(0), ARG_I32(1), ARG_I32(2), pPortableEntryPointContext); + } + static void CallFunc_I32_I32_IND_IND_I32_RetVoid(PCODE pcode, int8_t* pArgs, int8_t* pRet) { void (*fptr)(int32_t, int32_t, int32_t, int32_t, int32_t) = (void (*)(int32_t, int32_t, int32_t, int32_t, int32_t))pcode; @@ -510,18 +546,24 @@ namespace void (*fptr)(int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t) = (void (*)(int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t))pcode; (*fptr)(ARG_IND(0), ARG_IND(1), ARG_I32(2), ARG_I32(3), ARG_I32(4), ARG_I32(5), ARG_I32(6)); } + + static void CallFunc_Void_RetVoid_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) + { + void (*fptr)(uintptr_t, PCODE) = (void (*)(uintptr_t, PCODE))pcode; + (*fptr)(emscripten_stack_get_current(), pPortableEntryPointContext); + } } const StringToWasmSigThunk g_wasmThunks[] = { - { "dd", (void*)&CallFunc_F64_RetF64 }, - { "ddd", (void*)&CallFunc_F64_F64_RetF64 }, - { "dddd", (void*)&CallFunc_F64_F64_F64_RetF64 }, - { "ddi", (void*)&CallFunc_F64_I32_RetF64 }, + { "ddddp", (void*)&CallFunc_F64_F64_F64_RetF64_PE }, + { "dddp", (void*)&CallFunc_F64_F64_RetF64_PE }, + { "ddip", (void*)&CallFunc_F64_I32_RetF64_PE }, + { "ddp", (void*)&CallFunc_F64_RetF64_PE }, { "di", (void*)&CallFunc_I32_RetF64 }, - { "ff", (void*)&CallFunc_F32_RetF32 }, - { "fff", (void*)&CallFunc_F32_F32_RetF32 }, - { "ffff", (void*)&CallFunc_F32_F32_F32_RetF32 }, - { "ffi", (void*)&CallFunc_F32_I32_RetF32 }, + { "ffffp", (void*)&CallFunc_F32_F32_F32_RetF32_PE }, + { "fffp", (void*)&CallFunc_F32_F32_RetF32_PE }, + { "ffip", (void*)&CallFunc_F32_I32_RetF32_PE }, + { "ffp", (void*)&CallFunc_F32_RetF32_PE }, { "i", (void*)&CallFunc_Void_RetI32 }, { "ii", (void*)&CallFunc_I32_RetI32 }, { "iii", (void*)&CallFunc_I32_I32_RetI32 }, @@ -531,6 +573,9 @@ const StringToWasmSigThunk g_wasmThunks[] = { { "iiiiiii", (void*)&CallFunc_I32_I32_I32_I32_I32_I32_RetI32 }, { "iiiiiiii", (void*)&CallFunc_I32_I32_I32_I32_I32_I32_I32_RetI32 }, { "iiiiiiiiiiiiiii", (void*)&CallFunc_I32_I32_I32_I32_I32_I32_I32_I32_I32_I32_I32_I32_I32_I32_RetI32 }, + { "iiiiiiip", (void*)&CallFunc_I32_I32_I32_I32_I32_I32_RetI32_PE }, + { "iiiiiip", (void*)&CallFunc_I32_I32_I32_I32_I32_RetI32_PE }, + { "iiiiip", (void*)&CallFunc_I32_I32_I32_I32_RetI32_PE }, { "iiiil", (void*)&CallFunc_I32_I32_I32_I64_RetI32 }, { "iiiini", (void*)&CallFunc_I32_I32_I32_IND_I32_RetI32 }, { "iiiip", (void*)&CallFunc_I32_I32_I32_RetI32_PE }, @@ -564,13 +609,15 @@ const StringToWasmSigThunk g_wasmThunks[] = { { "li", (void*)&CallFunc_I32_RetI64 }, { "liii", (void*)&CallFunc_I32_I32_I32_RetI64 }, { "liiil", (void*)&CallFunc_I32_I32_I32_I64_RetI64 }, - { "lil", (void*)&CallFunc_I32_I64_RetI64 }, { "lili", (void*)&CallFunc_I32_I64_I32_RetI64 }, - { "lill", (void*)&CallFunc_I32_I64_I64_RetI64 }, - { "lll", (void*)&CallFunc_I64_I64_RetI64 }, + { "lillp", (void*)&CallFunc_I32_I64_I64_RetI64_PE }, + { "lilp", (void*)&CallFunc_I32_I64_RetI64_PE }, + { "lip", (void*)&CallFunc_I32_RetI64_PE }, + { "lllp", (void*)&CallFunc_I64_I64_RetI64_PE }, + { "lp", (void*)&CallFunc_Void_RetI64_PE }, { "v", (void*)&CallFunc_Void_RetVoid }, - { "vdii", (void*)&CallFunc_F64_I32_I32_RetVoid }, - { "vfii", (void*)&CallFunc_F32_I32_I32_RetVoid }, + { "vdiip", (void*)&CallFunc_F64_I32_I32_RetVoid_PE }, + { "vfiip", (void*)&CallFunc_F32_I32_I32_RetVoid_PE }, { "vi", (void*)&CallFunc_I32_RetVoid }, { "vii", (void*)&CallFunc_I32_I32_RetVoid }, { "viii", (void*)&CallFunc_I32_I32_I32_RetVoid }, @@ -579,6 +626,7 @@ const StringToWasmSigThunk g_wasmThunks[] = { { "viiiiii", (void*)&CallFunc_I32_I32_I32_I32_I32_I32_RetVoid }, { "viiinn", (void*)&CallFunc_I32_I32_I32_IND_IND_RetVoid }, { "viiinni", (void*)&CallFunc_I32_I32_I32_IND_IND_I32_RetVoid }, + { "viiip", (void*)&CallFunc_I32_I32_I32_RetVoid_PE }, { "viinni", (void*)&CallFunc_I32_I32_IND_IND_I32_RetVoid }, { "viinnii", (void*)&CallFunc_I32_I32_IND_IND_I32_I32_RetVoid }, { "viip", (void*)&CallFunc_I32_I32_RetVoid_PE }, @@ -594,7 +642,8 @@ const StringToWasmSigThunk g_wasmThunks[] = { { "vniiiiii", (void*)&CallFunc_IND_I32_I32_I32_I32_I32_I32_RetVoid }, { "vniiiiiiiiiii", (void*)&CallFunc_IND_I32_I32_I32_I32_I32_I32_I32_I32_I32_I32_I32_RetVoid }, { "vnn", (void*)&CallFunc_IND_IND_RetVoid }, - { "vnniiiii", (void*)&CallFunc_IND_IND_I32_I32_I32_I32_I32_RetVoid } + { "vnniiiii", (void*)&CallFunc_IND_IND_I32_I32_I32_I32_I32_RetVoid }, + { "vp", (void*)&CallFunc_Void_RetVoid_PE } }; const size_t g_wasmThunksCount = sizeof(g_wasmThunks) / sizeof(g_wasmThunks[0]); diff --git a/src/tasks/WasmAppBuilder/coreclr/InternalCallSignatureCollector.cs b/src/tasks/WasmAppBuilder/coreclr/InternalCallSignatureCollector.cs new file mode 100644 index 00000000000000..75e022e2434e77 --- /dev/null +++ b/src/tasks/WasmAppBuilder/coreclr/InternalCallSignatureCollector.cs @@ -0,0 +1,61 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Reflection; +using Microsoft.Build.Framework; +using WasmAppBuilder; + +namespace Microsoft.WebAssembly.Build.Tasks; + +// +// Scans assemblies for methods marked with MethodImplAttributes.InternalCall +// and generates portable entry point signatures for the interpreter-to-native thunks. +// +internal sealed class InternalCallSignatureCollector +{ + private readonly HashSet _signatures = new(); + private readonly LogAdapter _log; + + public InternalCallSignatureCollector(LogAdapter log) => _log = log; + + public void ScanAssembly(Assembly asm) + { + foreach (Type type in asm.GetTypes()) + ScanType(type); + } + + public IEnumerable GetSignatures() => _signatures; + + private void ScanType(Type type) + { + foreach (var method in type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance)) + { + if ((method.GetMethodImplementationFlags() & MethodImplAttributes.InternalCall) == 0) + continue; + + try + { + string? signature = SignatureMapper.MethodToSignature(method, _log, includeThis: true); + if (signature is null) + { + _log.Warning("WASM0001", $"Could not generate signature for InternalCall method '{type.FullName}::{method.Name}'"); + continue; + } + + signature += "p"; + + if (_signatures.Add(signature)) + _log.LogMessage(MessageImportance.Low, $"Adding InternalCall signature {signature} for method '{type.FullName}.{method.Name}'"); + } + catch (Exception ex) when (ex is not LogAsErrorException) + { + _log.Warning("WASM0001", $"Could not get signature for InternalCall method '{type.FullName}::{method.Name}' because '{ex.Message}'"); + } + } + + foreach (var nestedType in type.GetNestedTypes()) + ScanType(nestedType); + } +} diff --git a/src/tasks/WasmAppBuilder/coreclr/ManagedToNativeGenerator.cs b/src/tasks/WasmAppBuilder/coreclr/ManagedToNativeGenerator.cs index f27c230383f859..39162c75a22ed5 100644 --- a/src/tasks/WasmAppBuilder/coreclr/ManagedToNativeGenerator.cs +++ b/src/tasks/WasmAppBuilder/coreclr/ManagedToNativeGenerator.cs @@ -19,10 +19,6 @@ public class ManagedToNativeGenerator : Task [Required] public string[] Assemblies { get; set; } = Array.Empty(); - public string? RuntimeIcallTableFile { get; set; } - - public string? IcallOutputPath { get; set; } - [Required, NotNull] public string[]? PInvokeModules { get; set; } @@ -73,22 +69,23 @@ private void ExecuteInternal(LogAdapter log) Dictionary _symbolNameFixups = new(); List managedAssemblies = FilterOutUnmanagedBinaries(Assemblies); var pinvoke = new PInvokeTableGenerator(FixupSymbolName, log, IsLibraryMode); - var icall = new IcallTableGenerator(RuntimeIcallTableFile, FixupSymbolName, log); + var internalCallCollector = new InternalCallSignatureCollector(log); var resolver = new PathAssemblyResolver(managedAssemblies); using var mlc = new MetadataLoadContext(resolver, "System.Private.CoreLib"); foreach (string asmPath in managedAssemblies) { - log.LogMessage(MessageImportance.Low, $"Loading {asmPath} to scan for pinvokes, and icalls"); + log.LogMessage(MessageImportance.Low, $"Loading {asmPath} to scan for pinvokes and InternalCall methods"); Assembly asm = mlc.LoadFromAssemblyPath(asmPath); pinvoke.ScanAssembly(asm); - icall.ScanAssembly(asm); + internalCallCollector.ScanAssembly(asm); } - // All Signatures required for FCalls should be in this list, but we may also want to include pregenerated signatures for commonly used shapes used by - // R2R code to reduce duplication in generated R2R binaries. The signatures should be in the form of a string where the first character represents the - // return type and the following characters represent the argument types. The type characters should match those used by the SignatureMapper.CharToNativeType method. - string [] pregeneratedInterpreterToNativeSignatures = + // Pregenerated signatures for commonly used shapes used by R2R code to reduce duplication in generated R2R binaries. + // The signatures should be in the form of a string where the first character represents the return type and the + // following characters represent the argument types. The type characters should match those used by the + // SignatureMapper.CharToNativeType method. + string[] pregeneratedInterpreterToNativeSignatures = { "ip", "iip", @@ -98,11 +95,9 @@ private void ExecuteInternal(LogAdapter log) "viip", }; - IEnumerable cookies = Enumerable.Concat( - pinvoke.Generate(PInvokeModules, PInvokeOutputPath, ReversePInvokeOutputPath), - icall.Generate(IcallOutputPath)); - - cookies = Enumerable.Concat(cookies, pregeneratedInterpreterToNativeSignatures); + IEnumerable cookies = pinvoke.Generate(PInvokeModules, PInvokeOutputPath, ReversePInvokeOutputPath); + cookies = cookies.Concat(internalCallCollector.GetSignatures()); + cookies = cookies.Concat(pregeneratedInterpreterToNativeSignatures); var m2n = new InterpToNativeGenerator(log); m2n.Generate(cookies, InterpToNativeOutputPath); @@ -111,8 +106,6 @@ private void ExecuteInternal(LogAdapter log) File.WriteAllLines(CacheFilePath, PInvokeModules, Encoding.UTF8); List fileWritesList = new() { PInvokeOutputPath, InterpToNativeOutputPath }; - if (!string.IsNullOrEmpty(IcallOutputPath)) - fileWritesList.Add(IcallOutputPath); if (!string.IsNullOrEmpty(CacheFilePath)) fileWritesList.Add(CacheFilePath); diff --git a/src/tasks/WasmAppBuilder/coreclr/SignatureMapper.cs b/src/tasks/WasmAppBuilder/coreclr/SignatureMapper.cs index a5a5bcb2d4993f..efff2d8bd121d4 100644 --- a/src/tasks/WasmAppBuilder/coreclr/SignatureMapper.cs +++ b/src/tasks/WasmAppBuilder/coreclr/SignatureMapper.cs @@ -84,7 +84,7 @@ internal static class SignatureMapper return c; } - public static string? MethodToSignature(MethodInfo method, LogAdapter log) + public static string? MethodToSignature(MethodInfo method, LogAdapter log, bool includeThis = false) { string? result = TypeToChar(method.ReturnType, log, out bool resultIsByRef)?.ToString(); if (result == null) @@ -97,6 +97,11 @@ internal static class SignatureMapper result = "n"; } + if (includeThis && !method.IsStatic) + { + result += 'i'; + } + foreach (var parameter in method.GetParameters()) { char? parameterChar = TypeToChar(parameter.ParameterType, log, out _); From 8ec4ea6015f3bb1f922e3fa9556017e2e4e6c02a Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Fri, 10 Apr 2026 13:18:16 -0700 Subject: [PATCH 13/15] Fix missing call helper --- src/coreclr/vm/wasm/callhelpers-reverse.cpp | 253 +++++++++++++------- 1 file changed, 169 insertions(+), 84 deletions(-) diff --git a/src/coreclr/vm/wasm/callhelpers-reverse.cpp b/src/coreclr/vm/wasm/callhelpers-reverse.cpp index 0d20019d2f13c1..25ccba61ded632 100644 --- a/src/coreclr/vm/wasm/callhelpers-reverse.cpp +++ b/src/coreclr/vm/wasm/callhelpers-reverse.cpp @@ -105,6 +105,19 @@ extern "C" void SystemInteropJS_CallDelegate(void * arg0) Call_System_Runtime_InteropServices_JavaScript_System_Runtime_InteropServices_JavaScript_JavaScriptExports_CallDelegate_I32_RetVoid(arg0); } +static MethodDesc* MD_System_Private_CoreLib_System_Environment_CallEntryPoint_I32_I32_I32_I32_I32_RetVoid = nullptr; +static void Call_System_Private_CoreLib_System_Environment_CallEntryPoint_I32_I32_I32_I32_I32_RetVoid(void * arg0, void * arg1, void * arg2, int32_t arg3, void * arg4) +{ + int64_t args[5] = { (int64_t)arg0, (int64_t)arg1, (int64_t)arg2, (int64_t)arg3, (int64_t)arg4 }; + + // Lazy lookup of MethodDesc for the function export scenario. + if (!MD_System_Private_CoreLib_System_Environment_CallEntryPoint_I32_I32_I32_I32_I32_RetVoid) + { + LookupUnmanagedCallersOnlyMethodByName("System.Environment, System.Private.CoreLib", "CallEntryPoint", &MD_System_Private_CoreLib_System_Environment_CallEntryPoint_I32_I32_I32_I32_I32_RetVoid); + } + ExecuteInterpretedMethodFromUnmanaged(MD_System_Private_CoreLib_System_Environment_CallEntryPoint_I32_I32_I32_I32_I32_RetVoid, (int8_t*)args, sizeof(args), nullptr, (PCODE)&Call_System_Private_CoreLib_System_Environment_CallEntryPoint_I32_I32_I32_I32_I32_RetVoid); +} + static MethodDesc* MD_System_Runtime_InteropServices_JavaScript_System_Runtime_InteropServices_JavaScript_JavaScriptExports_CallJSExport_I32_I32_RetVoid = nullptr; static void Call_System_Runtime_InteropServices_JavaScript_System_Runtime_InteropServices_JavaScript_JavaScriptExports_CallJSExport_I32_I32_RetVoid(int32_t arg0, void * arg1) { @@ -206,22 +219,6 @@ static void Call_System_Private_CoreLib_System_GC_ConfigCallback_I32_I32_I32_I32 ExecuteInterpretedMethodFromUnmanaged(MD_System_Private_CoreLib_System_GC_ConfigCallback_I32_I32_I32_I32_I64_RetVoid, (int8_t*)args, sizeof(args), nullptr, (PCODE)&Call_System_Private_CoreLib_System_GC_ConfigCallback_I32_I32_I32_I32_I64_RetVoid); } -static MethodDesc* MD_System_Private_CoreLib_System_GC_RunFinalizers_I32_RetU = nullptr; -static uint32_t Call_System_Private_CoreLib_System_GC_RunFinalizers_I32_RetU() -{ - int64_t args[0] = { }; - - // Lazy lookup of MethodDesc for the function export scenario. - if (!MD_System_Private_CoreLib_System_GC_RunFinalizers_I32_RetU) - { - LookupUnmanagedCallersOnlyMethodByName("System.GC, System.Private.CoreLib", "RunFinalizers", &MD_System_Private_CoreLib_System_GC_RunFinalizers_I32_RetU); - } - - uint32_t result; - ExecuteInterpretedMethodFromUnmanaged(MD_System_Private_CoreLib_System_GC_RunFinalizers_I32_RetU, (int8_t*)args, sizeof(args), (int8_t*)&result, (PCODE)&Call_System_Private_CoreLib_System_GC_RunFinalizers_I32_RetU); - return result; -} - static MethodDesc* MD_System_Private_CoreLib_System_StubHelpers_MngdRefCustomMarshaler_ConvertContentsToManaged_I32_I32_I32_I32_RetVoid = nullptr; static void Call_System_Private_CoreLib_System_StubHelpers_MngdRefCustomMarshaler_ConvertContentsToManaged_I32_I32_I32_I32_RetVoid(void * arg0, void * arg1, void * arg2, void * arg3) { @@ -394,6 +391,22 @@ static void Call_System_Private_CoreLib_System_Globalization_CalendarData_EnumCa ExecuteInterpretedMethodFromUnmanaged(MD_System_Private_CoreLib_System_Globalization_CalendarData_EnumCalendarInfoCallback_I32_I32_RetVoid, (int8_t*)args, sizeof(args), nullptr, (PCODE)&Call_System_Private_CoreLib_System_Globalization_CalendarData_EnumCalendarInfoCallback_I32_I32_RetVoid); } +static MethodDesc* MD_System_Private_CoreLib_System_Environment_ExecuteInDefaultAppDomain_I32_I32_I32_RetI32 = nullptr; +static int32_t Call_System_Private_CoreLib_System_Environment_ExecuteInDefaultAppDomain_I32_I32_I32_RetI32(void * arg0, void * arg1, void * arg2) +{ + int64_t args[3] = { (int64_t)arg0, (int64_t)arg1, (int64_t)arg2 }; + + // Lazy lookup of MethodDesc for the function export scenario. + if (!MD_System_Private_CoreLib_System_Environment_ExecuteInDefaultAppDomain_I32_I32_I32_RetI32) + { + LookupUnmanagedCallersOnlyMethodByName("System.Environment, System.Private.CoreLib", "ExecuteInDefaultAppDomain", &MD_System_Private_CoreLib_System_Environment_ExecuteInDefaultAppDomain_I32_I32_I32_RetI32); + } + + int32_t result; + ExecuteInterpretedMethodFromUnmanaged(MD_System_Private_CoreLib_System_Environment_ExecuteInDefaultAppDomain_I32_I32_I32_RetI32, (int8_t*)args, sizeof(args), (int8_t*)&result, (PCODE)&Call_System_Private_CoreLib_System_Environment_ExecuteInDefaultAppDomain_I32_I32_I32_RetI32); + return result; +} + static MethodDesc* MD_System_Private_CoreLib_System_Resolver_GetCodeInfo_I32_I32_I32_I32_I32_I32_RetVoid = nullptr; static void Call_System_Private_CoreLib_System_Resolver_GetCodeInfo_I32_I32_I32_I32_I32_I32_RetVoid(void * arg0, void * arg1, void * arg2, void * arg3, void * arg4, void * arg5) { @@ -519,19 +532,6 @@ static void Call_System_Private_CoreLib_System_Environment_GetResourceString_I32 ExecuteInterpretedMethodFromUnmanaged(MD_System_Private_CoreLib_System_Environment_GetResourceString_I32_I32_I32_RetVoid, (int8_t*)args, sizeof(args), nullptr, (PCODE)&Call_System_Private_CoreLib_System_Environment_GetResourceString_I32_I32_I32_RetVoid); } -static MethodDesc* MD_System_Private_CoreLib_System_Environment_CallEntryPoint_I32_I32_I32_I32_I32_RetVoid = nullptr; -static void Call_System_Private_CoreLib_System_Environment_CallEntryPoint_I32_I32_I32_I32_I32_RetVoid(void * arg0, void * arg1, void * arg2, int32_t arg3, void * arg4) -{ - int64_t args[5] = { (int64_t)arg0, (int64_t)arg1, (int64_t)arg2, (int64_t)arg3, (int64_t)arg4 }; - - // Lazy lookup of MethodDesc for the function export scenario. - if (!MD_System_Private_CoreLib_System_Environment_CallEntryPoint_I32_I32_I32_I32_I32_RetVoid) - { - LookupUnmanagedCallersOnlyMethodByName("System.Environment, System.Private.CoreLib", "CallEntryPoint", &MD_System_Private_CoreLib_System_Environment_CallEntryPoint_I32_I32_I32_I32_I32_RetVoid); - } - ExecuteInterpretedMethodFromUnmanaged(MD_System_Private_CoreLib_System_Environment_CallEntryPoint_I32_I32_I32_I32_I32_RetVoid, (int8_t*)args, sizeof(args), nullptr, (PCODE)&Call_System_Private_CoreLib_System_Environment_CallEntryPoint_I32_I32_I32_I32_I32_RetVoid); -} - static MethodDesc* MD_System_Private_CoreLib_System_Resolver_GetStringLiteral_I32_I32_I32_I32_RetVoid = nullptr; static void Call_System_Private_CoreLib_System_Resolver_GetStringLiteral_I32_I32_I32_I32_RetVoid(void * arg0, int32_t arg1, void * arg2, void * arg3) { @@ -636,6 +636,32 @@ static void Call_System_Private_CoreLib_System_Runtime_InteropServices_DynamicIn ExecuteInterpretedMethodFromUnmanaged(MD_System_Private_CoreLib_System_Runtime_InteropServices_DynamicInterfaceCastableHelpers_IsInterfaceImplemented_I32_I32_I32_I32_I32_RetVoid, (int8_t*)args, sizeof(args), nullptr, (PCODE)&Call_System_Private_CoreLib_System_Runtime_InteropServices_DynamicInterfaceCastableHelpers_IsInterfaceImplemented_I32_I32_I32_I32_I32_RetVoid); } +static MethodDesc* MD_System_Private_CoreLib_System_StubHelpers_StubHelpers_LayoutTypeConvertToManaged_I32_I32_I32_RetVoid = nullptr; +static void Call_System_Private_CoreLib_System_StubHelpers_StubHelpers_LayoutTypeConvertToManaged_I32_I32_I32_RetVoid(void * arg0, void * arg1, void * arg2) +{ + int64_t args[3] = { (int64_t)arg0, (int64_t)arg1, (int64_t)arg2 }; + + // Lazy lookup of MethodDesc for the function export scenario. + if (!MD_System_Private_CoreLib_System_StubHelpers_StubHelpers_LayoutTypeConvertToManaged_I32_I32_I32_RetVoid) + { + LookupUnmanagedCallersOnlyMethodByName("System.StubHelpers.StubHelpers, System.Private.CoreLib", "LayoutTypeConvertToManaged", &MD_System_Private_CoreLib_System_StubHelpers_StubHelpers_LayoutTypeConvertToManaged_I32_I32_I32_RetVoid); + } + ExecuteInterpretedMethodFromUnmanaged(MD_System_Private_CoreLib_System_StubHelpers_StubHelpers_LayoutTypeConvertToManaged_I32_I32_I32_RetVoid, (int8_t*)args, sizeof(args), nullptr, (PCODE)&Call_System_Private_CoreLib_System_StubHelpers_StubHelpers_LayoutTypeConvertToManaged_I32_I32_I32_RetVoid); +} + +static MethodDesc* MD_System_Private_CoreLib_System_StubHelpers_StubHelpers_LayoutTypeConvertToUnmanaged_I32_I32_I32_RetVoid = nullptr; +static void Call_System_Private_CoreLib_System_StubHelpers_StubHelpers_LayoutTypeConvertToUnmanaged_I32_I32_I32_RetVoid(void * arg0, void * arg1, void * arg2) +{ + int64_t args[3] = { (int64_t)arg0, (int64_t)arg1, (int64_t)arg2 }; + + // Lazy lookup of MethodDesc for the function export scenario. + if (!MD_System_Private_CoreLib_System_StubHelpers_StubHelpers_LayoutTypeConvertToUnmanaged_I32_I32_I32_RetVoid) + { + LookupUnmanagedCallersOnlyMethodByName("System.StubHelpers.StubHelpers, System.Private.CoreLib", "LayoutTypeConvertToUnmanaged", &MD_System_Private_CoreLib_System_StubHelpers_StubHelpers_LayoutTypeConvertToUnmanaged_I32_I32_I32_RetVoid); + } + ExecuteInterpretedMethodFromUnmanaged(MD_System_Private_CoreLib_System_StubHelpers_StubHelpers_LayoutTypeConvertToUnmanaged_I32_I32_I32_RetVoid, (int8_t*)args, sizeof(args), nullptr, (PCODE)&Call_System_Private_CoreLib_System_StubHelpers_StubHelpers_LayoutTypeConvertToUnmanaged_I32_I32_I32_RetVoid); +} + static MethodDesc* MD_System_Private_CoreLib_Internal_Runtime_InteropServices_ComponentActivator_LoadAssembly_I32_I32_I32_RetI32 = nullptr; static int32_t Call_System_Private_CoreLib_Internal_Runtime_InteropServices_ComponentActivator_LoadAssembly_I32_I32_I32_RetI32(void * arg0, void * arg1, void * arg2) { @@ -777,6 +803,45 @@ static int32_t Call_System_Private_CoreLib_System_Runtime_InteropServices_TypeMa return result; } +static MethodDesc* MD_System_Private_CoreLib_System_StubHelpers_StubHelpers_NonBlittableStructureArrayConvertToManaged_I32_I32_I32_I32_I32_RetVoid = nullptr; +static void Call_System_Private_CoreLib_System_StubHelpers_StubHelpers_NonBlittableStructureArrayConvertToManaged_I32_I32_I32_I32_I32_RetVoid(void * arg0, void * arg1, void * arg2, int32_t arg3, void * arg4) +{ + int64_t args[5] = { (int64_t)arg0, (int64_t)arg1, (int64_t)arg2, (int64_t)arg3, (int64_t)arg4 }; + + // Lazy lookup of MethodDesc for the function export scenario. + if (!MD_System_Private_CoreLib_System_StubHelpers_StubHelpers_NonBlittableStructureArrayConvertToManaged_I32_I32_I32_I32_I32_RetVoid) + { + LookupUnmanagedCallersOnlyMethodByName("System.StubHelpers.StubHelpers, System.Private.CoreLib", "NonBlittableStructureArrayConvertToManaged", &MD_System_Private_CoreLib_System_StubHelpers_StubHelpers_NonBlittableStructureArrayConvertToManaged_I32_I32_I32_I32_I32_RetVoid); + } + ExecuteInterpretedMethodFromUnmanaged(MD_System_Private_CoreLib_System_StubHelpers_StubHelpers_NonBlittableStructureArrayConvertToManaged_I32_I32_I32_I32_I32_RetVoid, (int8_t*)args, sizeof(args), nullptr, (PCODE)&Call_System_Private_CoreLib_System_StubHelpers_StubHelpers_NonBlittableStructureArrayConvertToManaged_I32_I32_I32_I32_I32_RetVoid); +} + +static MethodDesc* MD_System_Private_CoreLib_System_StubHelpers_StubHelpers_NonBlittableStructureArrayConvertToUnmanaged_I32_I32_I32_I32_I32_RetVoid = nullptr; +static void Call_System_Private_CoreLib_System_StubHelpers_StubHelpers_NonBlittableStructureArrayConvertToUnmanaged_I32_I32_I32_I32_I32_RetVoid(void * arg0, void * arg1, void * arg2, int32_t arg3, void * arg4) +{ + int64_t args[5] = { (int64_t)arg0, (int64_t)arg1, (int64_t)arg2, (int64_t)arg3, (int64_t)arg4 }; + + // Lazy lookup of MethodDesc for the function export scenario. + if (!MD_System_Private_CoreLib_System_StubHelpers_StubHelpers_NonBlittableStructureArrayConvertToUnmanaged_I32_I32_I32_I32_I32_RetVoid) + { + LookupUnmanagedCallersOnlyMethodByName("System.StubHelpers.StubHelpers, System.Private.CoreLib", "NonBlittableStructureArrayConvertToUnmanaged", &MD_System_Private_CoreLib_System_StubHelpers_StubHelpers_NonBlittableStructureArrayConvertToUnmanaged_I32_I32_I32_I32_I32_RetVoid); + } + ExecuteInterpretedMethodFromUnmanaged(MD_System_Private_CoreLib_System_StubHelpers_StubHelpers_NonBlittableStructureArrayConvertToUnmanaged_I32_I32_I32_I32_I32_RetVoid, (int8_t*)args, sizeof(args), nullptr, (PCODE)&Call_System_Private_CoreLib_System_StubHelpers_StubHelpers_NonBlittableStructureArrayConvertToUnmanaged_I32_I32_I32_I32_I32_RetVoid); +} + +static MethodDesc* MD_System_Private_CoreLib_System_StubHelpers_StubHelpers_NonBlittableStructureArrayFree_I32_I32_I32_I32_I32_RetVoid = nullptr; +static void Call_System_Private_CoreLib_System_StubHelpers_StubHelpers_NonBlittableStructureArrayFree_I32_I32_I32_I32_I32_RetVoid(void * arg0, void * arg1, void * arg2, int32_t arg3, void * arg4) +{ + int64_t args[5] = { (int64_t)arg0, (int64_t)arg1, (int64_t)arg2, (int64_t)arg3, (int64_t)arg4 }; + + // Lazy lookup of MethodDesc for the function export scenario. + if (!MD_System_Private_CoreLib_System_StubHelpers_StubHelpers_NonBlittableStructureArrayFree_I32_I32_I32_I32_I32_RetVoid) + { + LookupUnmanagedCallersOnlyMethodByName("System.StubHelpers.StubHelpers, System.Private.CoreLib", "NonBlittableStructureArrayFree", &MD_System_Private_CoreLib_System_StubHelpers_StubHelpers_NonBlittableStructureArrayFree_I32_I32_I32_I32_I32_RetVoid); + } + ExecuteInterpretedMethodFromUnmanaged(MD_System_Private_CoreLib_System_StubHelpers_StubHelpers_NonBlittableStructureArrayFree_I32_I32_I32_I32_I32_RetVoid, (int8_t*)args, sizeof(args), nullptr, (PCODE)&Call_System_Private_CoreLib_System_StubHelpers_StubHelpers_NonBlittableStructureArrayFree_I32_I32_I32_I32_I32_RetVoid); +} + static MethodDesc* MD_System_Private_CoreLib_System_Runtime_Loader_AssemblyLoadContext_OnAssemblyLoad_I32_I32_RetVoid = nullptr; static void Call_System_Private_CoreLib_System_Runtime_Loader_AssemblyLoadContext_OnAssemblyLoad_I32_I32_RetVoid(void * arg0, void * arg1) { @@ -855,19 +920,6 @@ static void Call_System_Private_CoreLib_System_Threading_Thread_OnThreadExited_I ExecuteInterpretedMethodFromUnmanaged(MD_System_Private_CoreLib_System_Threading_Thread_OnThreadExited_I32_I32_RetVoid, (int8_t*)args, sizeof(args), nullptr, (PCODE)&Call_System_Private_CoreLib_System_Threading_Thread_OnThreadExited_I32_I32_RetVoid); } -static MethodDesc* MD_System_Private_CoreLib_System_Threading_Thread_StartCallback_I32_I32_RetVoid = nullptr; -static void Call_System_Private_CoreLib_System_Threading_Thread_StartCallback_I32_I32_RetVoid(void * arg0) -{ - int64_t args[1] = { (int64_t)arg0 }; - - // Lazy lookup of MethodDesc for the function export scenario. - if (!MD_System_Private_CoreLib_System_Threading_Thread_StartCallback_I32_I32_RetVoid) - { - LookupUnmanagedCallersOnlyMethodByName("System.Threading.Thread, System.Private.CoreLib", "StartCallback", &MD_System_Private_CoreLib_System_Threading_Thread_StartCallback_I32_I32_RetVoid); - } - ExecuteInterpretedMethodFromUnmanaged(MD_System_Private_CoreLib_System_Threading_Thread_StartCallback_I32_I32_RetVoid, (int8_t*)args, sizeof(args), nullptr, (PCODE)&Call_System_Private_CoreLib_System_Threading_Thread_StartCallback_I32_I32_RetVoid); -} - static MethodDesc* MD_System_Private_CoreLib_System_Runtime_Loader_AssemblyLoadContext_OnTypeResolve_I32_I32_I32_I32_RetVoid = nullptr; static void Call_System_Private_CoreLib_System_Runtime_Loader_AssemblyLoadContext_OnTypeResolve_I32_I32_I32_I32_RetVoid(void * arg0, void * arg1, void * arg2, void * arg3) { @@ -925,45 +977,6 @@ extern "C" void SystemInteropJS_ReleaseJSOwnedObjectByGCHandle(void * arg0) Call_System_Runtime_InteropServices_JavaScript_System_Runtime_InteropServices_JavaScript_JavaScriptExports_ReleaseJSOwnedObjectByGCHandle_I32_RetVoid(arg0); } -static MethodDesc* MD_System_Private_CoreLib_System_Runtime_EH_RhRethrow_I32_I32_RetVoid = nullptr; -static void Call_System_Private_CoreLib_System_Runtime_EH_RhRethrow_I32_I32_RetVoid(void * arg0, void * arg1) -{ - int64_t args[2] = { (int64_t)arg0, (int64_t)arg1 }; - - // Lazy lookup of MethodDesc for the function export scenario. - if (!MD_System_Private_CoreLib_System_Runtime_EH_RhRethrow_I32_I32_RetVoid) - { - LookupUnmanagedCallersOnlyMethodByName("System.Runtime.EH, System.Private.CoreLib", "RhRethrow", &MD_System_Private_CoreLib_System_Runtime_EH_RhRethrow_I32_I32_RetVoid); - } - ExecuteInterpretedMethodFromUnmanaged(MD_System_Private_CoreLib_System_Runtime_EH_RhRethrow_I32_I32_RetVoid, (int8_t*)args, sizeof(args), nullptr, (PCODE)&Call_System_Private_CoreLib_System_Runtime_EH_RhRethrow_I32_I32_RetVoid); -} - -static MethodDesc* MD_System_Private_CoreLib_System_Runtime_EH_RhThrowEx_I32_I32_RetVoid = nullptr; -static void Call_System_Private_CoreLib_System_Runtime_EH_RhThrowEx_I32_I32_RetVoid(void * arg0, void * arg1) -{ - int64_t args[2] = { (int64_t)arg0, (int64_t)arg1 }; - - // Lazy lookup of MethodDesc for the function export scenario. - if (!MD_System_Private_CoreLib_System_Runtime_EH_RhThrowEx_I32_I32_RetVoid) - { - LookupUnmanagedCallersOnlyMethodByName("System.Runtime.EH, System.Private.CoreLib", "RhThrowEx", &MD_System_Private_CoreLib_System_Runtime_EH_RhThrowEx_I32_I32_RetVoid); - } - ExecuteInterpretedMethodFromUnmanaged(MD_System_Private_CoreLib_System_Runtime_EH_RhThrowEx_I32_I32_RetVoid, (int8_t*)args, sizeof(args), nullptr, (PCODE)&Call_System_Private_CoreLib_System_Runtime_EH_RhThrowEx_I32_I32_RetVoid); -} - -static MethodDesc* MD_System_Private_CoreLib_System_Runtime_EH_RhThrowHwEx_I32_I32_RetVoid = nullptr; -static void Call_System_Private_CoreLib_System_Runtime_EH_RhThrowHwEx_I32_I32_RetVoid(int32_t arg0, void * arg1) -{ - int64_t args[2] = { (int64_t)arg0, (int64_t)arg1 }; - - // Lazy lookup of MethodDesc for the function export scenario. - if (!MD_System_Private_CoreLib_System_Runtime_EH_RhThrowHwEx_I32_I32_RetVoid) - { - LookupUnmanagedCallersOnlyMethodByName("System.Runtime.EH, System.Private.CoreLib", "RhThrowHwEx", &MD_System_Private_CoreLib_System_Runtime_EH_RhThrowHwEx_I32_I32_RetVoid); - } - ExecuteInterpretedMethodFromUnmanaged(MD_System_Private_CoreLib_System_Runtime_EH_RhThrowHwEx_I32_I32_RetVoid, (int8_t*)args, sizeof(args), nullptr, (PCODE)&Call_System_Private_CoreLib_System_Runtime_EH_RhThrowHwEx_I32_I32_RetVoid); -} - static MethodDesc* MD_System_Private_CoreLib_System_Runtime_Loader_AssemblyLoadContext_Resolve_I32_I32_I32_I32_RetVoid = nullptr; static void Call_System_Private_CoreLib_System_Runtime_Loader_AssemblyLoadContext_Resolve_I32_I32_I32_I32_RetVoid(void * arg0, void * arg1, void * arg2, void * arg3) { @@ -1061,6 +1074,59 @@ static void Call_System_Private_CoreLib_System_Runtime_Loader_AssemblyLoadContex ExecuteInterpretedMethodFromUnmanaged(MD_System_Private_CoreLib_System_Runtime_Loader_AssemblyLoadContext_ResolveUsingEvent_I32_I32_I32_I32_RetVoid, (int8_t*)args, sizeof(args), nullptr, (PCODE)&Call_System_Private_CoreLib_System_Runtime_Loader_AssemblyLoadContext_ResolveUsingEvent_I32_I32_I32_I32_RetVoid); } +static MethodDesc* MD_System_Private_CoreLib_System_Runtime_EH_RhRethrow_I32_I32_RetVoid = nullptr; +static void Call_System_Private_CoreLib_System_Runtime_EH_RhRethrow_I32_I32_RetVoid(void * arg0, void * arg1) +{ + int64_t args[2] = { (int64_t)arg0, (int64_t)arg1 }; + + // Lazy lookup of MethodDesc for the function export scenario. + if (!MD_System_Private_CoreLib_System_Runtime_EH_RhRethrow_I32_I32_RetVoid) + { + LookupUnmanagedCallersOnlyMethodByName("System.Runtime.EH, System.Private.CoreLib", "RhRethrow", &MD_System_Private_CoreLib_System_Runtime_EH_RhRethrow_I32_I32_RetVoid); + } + ExecuteInterpretedMethodFromUnmanaged(MD_System_Private_CoreLib_System_Runtime_EH_RhRethrow_I32_I32_RetVoid, (int8_t*)args, sizeof(args), nullptr, (PCODE)&Call_System_Private_CoreLib_System_Runtime_EH_RhRethrow_I32_I32_RetVoid); +} + +static MethodDesc* MD_System_Private_CoreLib_System_Runtime_EH_RhThrowEx_I32_I32_RetVoid = nullptr; +static void Call_System_Private_CoreLib_System_Runtime_EH_RhThrowEx_I32_I32_RetVoid(void * arg0, void * arg1) +{ + int64_t args[2] = { (int64_t)arg0, (int64_t)arg1 }; + + // Lazy lookup of MethodDesc for the function export scenario. + if (!MD_System_Private_CoreLib_System_Runtime_EH_RhThrowEx_I32_I32_RetVoid) + { + LookupUnmanagedCallersOnlyMethodByName("System.Runtime.EH, System.Private.CoreLib", "RhThrowEx", &MD_System_Private_CoreLib_System_Runtime_EH_RhThrowEx_I32_I32_RetVoid); + } + ExecuteInterpretedMethodFromUnmanaged(MD_System_Private_CoreLib_System_Runtime_EH_RhThrowEx_I32_I32_RetVoid, (int8_t*)args, sizeof(args), nullptr, (PCODE)&Call_System_Private_CoreLib_System_Runtime_EH_RhThrowEx_I32_I32_RetVoid); +} + +static MethodDesc* MD_System_Private_CoreLib_System_Runtime_EH_RhThrowHwEx_I32_I32_RetVoid = nullptr; +static void Call_System_Private_CoreLib_System_Runtime_EH_RhThrowHwEx_I32_I32_RetVoid(uint32_t arg0, void * arg1) +{ + int64_t args[2] = { (int64_t)arg0, (int64_t)arg1 }; + + // Lazy lookup of MethodDesc for the function export scenario. + if (!MD_System_Private_CoreLib_System_Runtime_EH_RhThrowHwEx_I32_I32_RetVoid) + { + LookupUnmanagedCallersOnlyMethodByName("System.Runtime.EH, System.Private.CoreLib", "RhThrowHwEx", &MD_System_Private_CoreLib_System_Runtime_EH_RhThrowHwEx_I32_I32_RetVoid); + } + ExecuteInterpretedMethodFromUnmanaged(MD_System_Private_CoreLib_System_Runtime_EH_RhThrowHwEx_I32_I32_RetVoid, (int8_t*)args, sizeof(args), nullptr, (PCODE)&Call_System_Private_CoreLib_System_Runtime_EH_RhThrowHwEx_I32_I32_RetVoid); +} + +static MethodDesc* MD_System_Private_CoreLib_System_GC_RunFinalizers_Void_RetI32 = nullptr; +static uint32_t Call_System_Private_CoreLib_System_GC_RunFinalizers_Void_RetI32() +{ + // Lazy lookup of MethodDesc for the function export scenario. + if (!MD_System_Private_CoreLib_System_GC_RunFinalizers_Void_RetI32) + { + LookupUnmanagedCallersOnlyMethodByName("System.GC, System.Private.CoreLib", "RunFinalizers", &MD_System_Private_CoreLib_System_GC_RunFinalizers_Void_RetI32); + } + + uint32_t result; + ExecuteInterpretedMethodFromUnmanaged(MD_System_Private_CoreLib_System_GC_RunFinalizers_Void_RetI32, nullptr, 0, (int8_t*)&result, (PCODE)&Call_System_Private_CoreLib_System_GC_RunFinalizers_Void_RetI32); + return result; +} + static MethodDesc* MD_System_Private_CoreLib_System_AppContext_Setup_I32_I32_I32_I32_RetVoid = nullptr; static void Call_System_Private_CoreLib_System_AppContext_Setup_I32_I32_I32_I32_RetVoid(void * arg0, void * arg1, int32_t arg2, void * arg3) { @@ -1087,6 +1153,19 @@ static void Call_System_Private_CoreLib_System_Runtime_Loader_AssemblyLoadContex ExecuteInterpretedMethodFromUnmanaged(MD_System_Private_CoreLib_System_Runtime_Loader_AssemblyLoadContext_StartAssemblyLoad_I32_I32_I32_RetVoid, (int8_t*)args, sizeof(args), nullptr, (PCODE)&Call_System_Private_CoreLib_System_Runtime_Loader_AssemblyLoadContext_StartAssemblyLoad_I32_I32_I32_RetVoid); } +static MethodDesc* MD_System_Private_CoreLib_System_Threading_Thread_StartCallback_I32_RetVoid = nullptr; +static void Call_System_Private_CoreLib_System_Threading_Thread_StartCallback_I32_RetVoid(void * arg0) +{ + int64_t args[1] = { (int64_t)arg0 }; + + // Lazy lookup of MethodDesc for the function export scenario. + if (!MD_System_Private_CoreLib_System_Threading_Thread_StartCallback_I32_RetVoid) + { + LookupUnmanagedCallersOnlyMethodByName("System.Threading.Thread, System.Private.CoreLib", "StartCallback", &MD_System_Private_CoreLib_System_Threading_Thread_StartCallback_I32_RetVoid); + } + ExecuteInterpretedMethodFromUnmanaged(MD_System_Private_CoreLib_System_Threading_Thread_StartCallback_I32_RetVoid, (int8_t*)args, sizeof(args), nullptr, (PCODE)&Call_System_Private_CoreLib_System_Threading_Thread_StartCallback_I32_RetVoid); +} + static MethodDesc* MD_System_Private_CoreLib_System_Runtime_Loader_AssemblyLoadContext_StopAssemblyLoad_I32_I32_RetVoid = nullptr; static void Call_System_Private_CoreLib_System_Runtime_Loader_AssemblyLoadContext_StopAssemblyLoad_I32_I32_RetVoid(void * arg0, void * arg1) { @@ -1124,6 +1203,7 @@ const ReverseThunkMapEntry g_ReverseThunks[] = { 1361004804, "CallClassConstructor#3:System.Private.CoreLib:System.Runtime.CompilerServices:InitHelpers", { &MD_System_Private_CoreLib_System_Runtime_CompilerServices_InitHelpers_CallClassConstructor_I32_I32_I32_RetVoid, (void*)&Call_System_Private_CoreLib_System_Runtime_CompilerServices_InitHelpers_CallClassConstructor_I32_I32_I32_RetVoid } }, { 3858976135, "CallDefaultConstructor#3:System.Private.CoreLib:System.Runtime.CompilerServices:RuntimeHelpers", { &MD_System_Private_CoreLib_System_Runtime_CompilerServices_RuntimeHelpers_CallDefaultConstructor_I32_I32_I32_RetVoid, (void*)&Call_System_Private_CoreLib_System_Runtime_CompilerServices_RuntimeHelpers_CallDefaultConstructor_I32_I32_I32_RetVoid } }, { 2601830388, "CallDelegate#1:System.Runtime.InteropServices.JavaScript:System.Runtime.InteropServices.JavaScript:JavaScriptExports", { &MD_System_Runtime_InteropServices_JavaScript_System_Runtime_InteropServices_JavaScript_JavaScriptExports_CallDelegate_I32_RetVoid, (void*)&Call_System_Runtime_InteropServices_JavaScript_System_Runtime_InteropServices_JavaScript_JavaScriptExports_CallDelegate_I32_RetVoid } }, + { 3962535319, "CallEntryPoint#5:System.Private.CoreLib:System:Environment", { &MD_System_Private_CoreLib_System_Environment_CallEntryPoint_I32_I32_I32_I32_I32_RetVoid, (void*)&Call_System_Private_CoreLib_System_Environment_CallEntryPoint_I32_I32_I32_I32_I32_RetVoid } }, { 433365813, "CallJSExport#2:System.Runtime.InteropServices.JavaScript:System.Runtime.InteropServices.JavaScript:JavaScriptExports", { &MD_System_Runtime_InteropServices_JavaScript_System_Runtime_InteropServices_JavaScript_JavaScriptExports_CallJSExport_I32_I32_RetVoid, (void*)&Call_System_Runtime_InteropServices_JavaScript_System_Runtime_InteropServices_JavaScript_JavaScriptExports_CallJSExport_I32_I32_RetVoid } }, { 1821934012, "CallStartupHook#2:System.Private.CoreLib:System:StartupHookProvider", { &MD_System_Private_CoreLib_System_StartupHookProvider_CallStartupHook_I32_I32_RetVoid, (void*)&Call_System_Private_CoreLib_System_StartupHookProvider_CallStartupHook_I32_I32_RetVoid } }, { 2915047114, "CallToString#3:System.Private.CoreLib:System.Runtime.CompilerServices:RuntimeHelpers", { &MD_System_Private_CoreLib_System_Runtime_CompilerServices_RuntimeHelpers_CallToString_I32_I32_I32_RetVoid, (void*)&Call_System_Private_CoreLib_System_Runtime_CompilerServices_RuntimeHelpers_CallToString_I32_I32_I32_RetVoid } }, @@ -1144,6 +1224,7 @@ const ReverseThunkMapEntry g_ReverseThunks[] = { 271519467, "CreateTargetInvocationException#3:System.Private.CoreLib:System:Exception", { &MD_System_Private_CoreLib_System_Exception_CreateTargetInvocationException_I32_I32_I32_RetVoid, (void*)&Call_System_Private_CoreLib_System_Exception_CreateTargetInvocationException_I32_I32_I32_RetVoid } }, { 3064803797, "CreateTypeInitializationException#4:System.Private.CoreLib:System:Exception", { &MD_System_Private_CoreLib_System_Exception_CreateTypeInitializationException_I32_I32_I32_I32_RetVoid, (void*)&Call_System_Private_CoreLib_System_Exception_CreateTypeInitializationException_I32_I32_I32_I32_RetVoid } }, { 1196551088, "EnumCalendarInfoCallback#2:System.Private.CoreLib:System.Globalization:CalendarData", { &MD_System_Private_CoreLib_System_Globalization_CalendarData_EnumCalendarInfoCallback_I32_I32_RetVoid, (void*)&Call_System_Private_CoreLib_System_Globalization_CalendarData_EnumCalendarInfoCallback_I32_I32_RetVoid } }, + { 2401666169, "ExecuteInDefaultAppDomain#3:System.Private.CoreLib:System:Environment", { &MD_System_Private_CoreLib_System_Environment_ExecuteInDefaultAppDomain_I32_I32_I32_RetI32, (void*)&Call_System_Private_CoreLib_System_Environment_ExecuteInDefaultAppDomain_I32_I32_I32_RetI32 } }, { 2605868264, "GetCodeInfo#6:System.Private.CoreLib:System:Resolver", { &MD_System_Private_CoreLib_System_Resolver_GetCodeInfo_I32_I32_I32_I32_I32_I32_RetVoid, (void*)&Call_System_Private_CoreLib_System_Resolver_GetCodeInfo_I32_I32_I32_I32_I32_I32_RetVoid } }, { 3084636701, "GetCustomMarshalerInstance#5:System.Private.CoreLib:System.StubHelpers:MngdRefCustomMarshaler", { &MD_System_Private_CoreLib_System_StubHelpers_MngdRefCustomMarshaler_GetCustomMarshalerInstance_I32_I32_I32_I32_I32_RetVoid, (void*)&Call_System_Private_CoreLib_System_StubHelpers_MngdRefCustomMarshaler_GetCustomMarshalerInstance_I32_I32_I32_I32_I32_RetVoid } }, { 1641343147, "GetEHInfo#5:System.Private.CoreLib:System:Resolver", { &MD_System_Private_CoreLib_System_Resolver_GetEHInfo_I32_I32_I32_I32_I32_RetVoid, (void*)&Call_System_Private_CoreLib_System_Resolver_GetEHInfo_I32_I32_I32_I32_I32_RetVoid } }, @@ -1152,7 +1233,6 @@ const ReverseThunkMapEntry g_ReverseThunks[] = { 4101188193, "GetJitContext#4:System.Private.CoreLib:System:Resolver", { &MD_System_Private_CoreLib_System_Resolver_GetJitContext_I32_I32_I32_I32_RetVoid, (void*)&Call_System_Private_CoreLib_System_Resolver_GetJitContext_I32_I32_I32_I32_RetVoid } }, { 2512220404, "GetLocalsSignature#3:System.Private.CoreLib:System:Resolver", { &MD_System_Private_CoreLib_System_Resolver_GetLocalsSignature_I32_I32_I32_RetVoid, (void*)&Call_System_Private_CoreLib_System_Resolver_GetLocalsSignature_I32_I32_I32_RetVoid } }, { 1081971317, "GetManagedStackTrace#1:System.Runtime.InteropServices.JavaScript:System.Runtime.InteropServices.JavaScript:JavaScriptExports", { &MD_System_Runtime_InteropServices_JavaScript_System_Runtime_InteropServices_JavaScript_JavaScriptExports_GetManagedStackTrace_I32_RetVoid, (void*)&Call_System_Runtime_InteropServices_JavaScript_System_Runtime_InteropServices_JavaScript_JavaScriptExports_GetManagedStackTrace_I32_RetVoid } }, - { 3962535319, "CallEntryPoint#5:System.Private.CoreLib:System:Environment", { &MD_System_Private_CoreLib_System_Environment_CallEntryPoint_I32_I32_I32_I32_I32_RetVoid, (void*)&Call_System_Private_CoreLib_System_Environment_CallEntryPoint_I32_I32_I32_I32_I32_RetVoid } }, { 1275372322, "GetResourceString#3:System.Private.CoreLib:System:Environment", { &MD_System_Private_CoreLib_System_Environment_GetResourceString_I32_I32_I32_RetVoid, (void*)&Call_System_Private_CoreLib_System_Environment_GetResourceString_I32_I32_I32_RetVoid } }, { 831291767, "GetStringLiteral#4:System.Private.CoreLib:System:Resolver", { &MD_System_Private_CoreLib_System_Resolver_GetStringLiteral_I32_I32_I32_I32_RetVoid, (void*)&Call_System_Private_CoreLib_System_Resolver_GetStringLiteral_I32_I32_I32_I32_RetVoid } }, { 3370125186, "GetTypeHelper#7:System.Private.CoreLib:System.Reflection:TypeNameResolver", { &MD_System_Private_CoreLib_System_Reflection_TypeNameResolver_GetTypeHelper_I32_I32_I32_I32_I32_I32_I32_RetVoid, (void*)&Call_System_Private_CoreLib_System_Reflection_TypeNameResolver_GetTypeHelper_I32_I32_I32_I32_I32_I32_I32_RetVoid } }, @@ -1162,6 +1242,8 @@ const ReverseThunkMapEntry g_ReverseThunks[] = { 266659693, "InitializeForMonitor#4:System.Private.CoreLib:System.Threading:Lock", { &MD_System_Private_CoreLib_System_Threading_Lock_InitializeForMonitor_I32_I32_I32_I32_RetVoid, (void*)&Call_System_Private_CoreLib_System_Threading_Lock_InitializeForMonitor_I32_I32_I32_I32_RetVoid } }, { 288803216, "InternalPreserveStackTrace#2:System.Private.CoreLib:System:Exception", { &MD_System_Private_CoreLib_System_Exception_InternalPreserveStackTrace_I32_I32_RetVoid, (void*)&Call_System_Private_CoreLib_System_Exception_InternalPreserveStackTrace_I32_I32_RetVoid } }, { 3290644746, "IsInterfaceImplemented#5:System.Private.CoreLib:System.Runtime.InteropServices:DynamicInterfaceCastableHelpers", { &MD_System_Private_CoreLib_System_Runtime_InteropServices_DynamicInterfaceCastableHelpers_IsInterfaceImplemented_I32_I32_I32_I32_I32_RetVoid, (void*)&Call_System_Private_CoreLib_System_Runtime_InteropServices_DynamicInterfaceCastableHelpers_IsInterfaceImplemented_I32_I32_I32_I32_I32_RetVoid } }, + { 1577711579, "LayoutTypeConvertToManaged#3:System.Private.CoreLib:System.StubHelpers:StubHelpers", { &MD_System_Private_CoreLib_System_StubHelpers_StubHelpers_LayoutTypeConvertToManaged_I32_I32_I32_RetVoid, (void*)&Call_System_Private_CoreLib_System_StubHelpers_StubHelpers_LayoutTypeConvertToManaged_I32_I32_I32_RetVoid } }, + { 2780693056, "LayoutTypeConvertToUnmanaged#3:System.Private.CoreLib:System.StubHelpers:StubHelpers", { &MD_System_Private_CoreLib_System_StubHelpers_StubHelpers_LayoutTypeConvertToUnmanaged_I32_I32_I32_RetVoid, (void*)&Call_System_Private_CoreLib_System_StubHelpers_StubHelpers_LayoutTypeConvertToUnmanaged_I32_I32_I32_RetVoid } }, { 3422156547, "LoadAssembly#3:System.Private.CoreLib:Internal.Runtime.InteropServices:ComponentActivator", { &MD_System_Private_CoreLib_Internal_Runtime_InteropServices_ComponentActivator_LoadAssembly_I32_I32_I32_RetI32, (void*)&Call_System_Private_CoreLib_Internal_Runtime_InteropServices_ComponentActivator_LoadAssembly_I32_I32_I32_RetI32 } }, { 542185314, "LoadAssemblyAndGetFunctionPointer#6:System.Private.CoreLib:Internal.Runtime.InteropServices:ComponentActivator", { &MD_System_Private_CoreLib_Internal_Runtime_InteropServices_ComponentActivator_LoadAssemblyAndGetFunctionPointer_I32_I32_I32_I32_I32_I32_RetI32, (void*)&Call_System_Private_CoreLib_Internal_Runtime_InteropServices_ComponentActivator_LoadAssemblyAndGetFunctionPointer_I32_I32_I32_I32_I32_I32_RetI32 } }, { 3765950975, "LoadAssemblyBytes#6:System.Private.CoreLib:Internal.Runtime.InteropServices:ComponentActivator", { &MD_System_Private_CoreLib_Internal_Runtime_InteropServices_ComponentActivator_LoadAssemblyBytes_I32_I32_I32_I32_I32_I32_RetI32, (void*)&Call_System_Private_CoreLib_Internal_Runtime_InteropServices_ComponentActivator_LoadAssemblyBytes_I32_I32_I32_I32_I32_I32_RetI32 } }, @@ -1171,6 +1253,9 @@ const ReverseThunkMapEntry g_ReverseThunks[] = { 3515006989, "NewPrecachedExternalTypeMap#1:System.Private.CoreLib:System.Runtime.InteropServices:TypeMapLazyDictionary", { &MD_System_Private_CoreLib_System_Runtime_InteropServices_TypeMapLazyDictionary_NewPrecachedExternalTypeMap_I32_RetI32, (void*)&Call_System_Private_CoreLib_System_Runtime_InteropServices_TypeMapLazyDictionary_NewPrecachedExternalTypeMap_I32_RetI32 } }, { 1731038108, "NewPrecachedProxyTypeMap#1:System.Private.CoreLib:System.Runtime.InteropServices:TypeMapLazyDictionary", { &MD_System_Private_CoreLib_System_Runtime_InteropServices_TypeMapLazyDictionary_NewPrecachedProxyTypeMap_I32_RetI32, (void*)&Call_System_Private_CoreLib_System_Runtime_InteropServices_TypeMapLazyDictionary_NewPrecachedProxyTypeMap_I32_RetI32 } }, { 3327247096, "NewProxyTypeEntry#2:System.Private.CoreLib:System.Runtime.InteropServices:TypeMapLazyDictionary", { &MD_System_Private_CoreLib_System_Runtime_InteropServices_TypeMapLazyDictionary_NewProxyTypeEntry_I32_I32_RetI32, (void*)&Call_System_Private_CoreLib_System_Runtime_InteropServices_TypeMapLazyDictionary_NewProxyTypeEntry_I32_I32_RetI32 } }, + { 3248038929, "NonBlittableStructureArrayConvertToManaged#5:System.Private.CoreLib:System.StubHelpers:StubHelpers", { &MD_System_Private_CoreLib_System_StubHelpers_StubHelpers_NonBlittableStructureArrayConvertToManaged_I32_I32_I32_I32_I32_RetVoid, (void*)&Call_System_Private_CoreLib_System_StubHelpers_StubHelpers_NonBlittableStructureArrayConvertToManaged_I32_I32_I32_I32_I32_RetVoid } }, + { 373411722, "NonBlittableStructureArrayConvertToUnmanaged#5:System.Private.CoreLib:System.StubHelpers:StubHelpers", { &MD_System_Private_CoreLib_System_StubHelpers_StubHelpers_NonBlittableStructureArrayConvertToUnmanaged_I32_I32_I32_I32_I32_RetVoid, (void*)&Call_System_Private_CoreLib_System_StubHelpers_StubHelpers_NonBlittableStructureArrayConvertToUnmanaged_I32_I32_I32_I32_I32_RetVoid } }, + { 2704509804, "NonBlittableStructureArrayFree#5:System.Private.CoreLib:System.StubHelpers:StubHelpers", { &MD_System_Private_CoreLib_System_StubHelpers_StubHelpers_NonBlittableStructureArrayFree_I32_I32_I32_I32_I32_RetVoid, (void*)&Call_System_Private_CoreLib_System_StubHelpers_StubHelpers_NonBlittableStructureArrayFree_I32_I32_I32_I32_I32_RetVoid } }, { 3837429452, "OnAssemblyLoad#2:System.Private.CoreLib:System.Runtime.Loader:AssemblyLoadContext", { &MD_System_Private_CoreLib_System_Runtime_Loader_AssemblyLoadContext_OnAssemblyLoad_I32_I32_RetVoid, (void*)&Call_System_Private_CoreLib_System_Runtime_Loader_AssemblyLoadContext_OnAssemblyLoad_I32_I32_RetVoid } }, { 1632250712, "OnAssemblyResolve#4:System.Private.CoreLib:System.Runtime.Loader:AssemblyLoadContext", { &MD_System_Private_CoreLib_System_Runtime_Loader_AssemblyLoadContext_OnAssemblyResolve_I32_I32_I32_I32_RetVoid, (void*)&Call_System_Private_CoreLib_System_Runtime_Loader_AssemblyLoadContext_OnAssemblyResolve_I32_I32_I32_I32_RetVoid } }, { 3308959471, "OnFirstChanceException#2:System.Private.CoreLib:System:AppContext", { &MD_System_Private_CoreLib_System_AppContext_OnFirstChanceException_I32_I32_RetVoid, (void*)&Call_System_Private_CoreLib_System_AppContext_OnFirstChanceException_I32_I32_RetVoid } }, @@ -1191,10 +1276,10 @@ const ReverseThunkMapEntry g_ReverseThunks[] = { 2883735131, "RhRethrow#2:System.Private.CoreLib:System.Runtime:EH", { &MD_System_Private_CoreLib_System_Runtime_EH_RhRethrow_I32_I32_RetVoid, (void*)&Call_System_Private_CoreLib_System_Runtime_EH_RhRethrow_I32_I32_RetVoid } }, { 3929107505, "RhThrowEx#2:System.Private.CoreLib:System.Runtime:EH", { &MD_System_Private_CoreLib_System_Runtime_EH_RhThrowEx_I32_I32_RetVoid, (void*)&Call_System_Private_CoreLib_System_Runtime_EH_RhThrowEx_I32_I32_RetVoid } }, { 504238190, "RhThrowHwEx#2:System.Private.CoreLib:System.Runtime:EH", { &MD_System_Private_CoreLib_System_Runtime_EH_RhThrowHwEx_I32_I32_RetVoid, (void*)&Call_System_Private_CoreLib_System_Runtime_EH_RhThrowHwEx_I32_I32_RetVoid } }, - { 4273572779, "RunFinalizers#0:System.Private.CoreLib:System:GC", { &MD_System_Private_CoreLib_System_GC_RunFinalizers_I32_RetU, (void*)&Call_System_Private_CoreLib_System_GC_RunFinalizers_I32_RetU } }, + { 4273572779, "RunFinalizers#0:System.Private.CoreLib:System:GC", { &MD_System_Private_CoreLib_System_GC_RunFinalizers_Void_RetI32, (void*)&Call_System_Private_CoreLib_System_GC_RunFinalizers_Void_RetI32 } }, { 1963568864, "Setup#4:System.Private.CoreLib:System:AppContext", { &MD_System_Private_CoreLib_System_AppContext_Setup_I32_I32_I32_I32_RetVoid, (void*)&Call_System_Private_CoreLib_System_AppContext_Setup_I32_I32_I32_I32_RetVoid } }, { 1343309100, "StartAssemblyLoad#3:System.Private.CoreLib:System.Runtime.Loader:AssemblyLoadContext", { &MD_System_Private_CoreLib_System_Runtime_Loader_AssemblyLoadContext_StartAssemblyLoad_I32_I32_I32_RetVoid, (void*)&Call_System_Private_CoreLib_System_Runtime_Loader_AssemblyLoadContext_StartAssemblyLoad_I32_I32_I32_RetVoid } }, - { 3372184251, "StartCallback#1:System.Private.CoreLib:System.Threading:Thread", { &MD_System_Private_CoreLib_System_Threading_Thread_StartCallback_I32_I32_RetVoid, (void*)&Call_System_Private_CoreLib_System_Threading_Thread_StartCallback_I32_I32_RetVoid } }, + { 3372184251, "StartCallback#1:System.Private.CoreLib:System.Threading:Thread", { &MD_System_Private_CoreLib_System_Threading_Thread_StartCallback_I32_RetVoid, (void*)&Call_System_Private_CoreLib_System_Threading_Thread_StartCallback_I32_RetVoid } }, { 3495913109, "StopAssemblyLoad#2:System.Private.CoreLib:System.Runtime.Loader:AssemblyLoadContext", { &MD_System_Private_CoreLib_System_Runtime_Loader_AssemblyLoadContext_StopAssemblyLoad_I32_I32_RetVoid, (void*)&Call_System_Private_CoreLib_System_Runtime_Loader_AssemblyLoadContext_StopAssemblyLoad_I32_I32_RetVoid } }, { 167179540, "TimerHandler#0:System.Private.CoreLib:System.Threading:TimerQueue", { &MD_System_Private_CoreLib_System_Threading_TimerQueue_TimerHandler_Void_RetVoid, (void*)&Call_System_Private_CoreLib_System_Threading_TimerQueue_TimerHandler_Void_RetVoid } } }; From ca1d6f4791d8eb328a0cfa9e8dcb06d9290dea76 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Fri, 10 Apr 2026 14:23:51 -0700 Subject: [PATCH 14/15] Update src/coreclr/runtime/portable/AllocFast.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/coreclr/runtime/portable/AllocFast.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/runtime/portable/AllocFast.cpp b/src/coreclr/runtime/portable/AllocFast.cpp index 3fdff4403ad994..ad7094f3be2404 100644 --- a/src/coreclr/runtime/portable/AllocFast.cpp +++ b/src/coreclr/runtime/portable/AllocFast.cpp @@ -141,7 +141,7 @@ EXTERN_C FCDECL2(Object*, RhpNewPtrArrayFast, MethodTable* pMT, INT_PTR size) { WRAPPER_NO_CONTRACT; // Since we know the implementation of RhpNewArrayFast is native we don't need to actually - // pass a stack poitner or portable entry point context, so we can just specify 0 for those parameters. + // pass a stack pointer or portable entry point context, so we can just specify 0 for those parameters. return RhpNewArrayFast(0 /* stack_pointer */, pMT, size, 0 /* portableEntryPointContext */); } From 6264831f8d4788cd6f624efa4675a0b984499922 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Fri, 10 Apr 2026 16:46:23 -0700 Subject: [PATCH 15/15] - Update interp to managed helpers to pass a stack pointer which informs the stack walker to stop walking - Put the callers frame pointer into the TransitionBlock. The current implementation drops it into the m_ReturnAddress field, which is a bit dodgy, but we can fix this later when we actually work on the stack walker - Replace use of conservative gc in the ExecuteInterpretedMethodWithArgs_PortableEntryPoint_Complex function in favor of reusing the PrestubMethodFrame which I believe is appropriate to the situation --- src/coreclr/vm/callhelpers.h | 7 + src/coreclr/vm/frames.h | 11 - src/coreclr/vm/prestub.cpp | 63 +++-- .../vm/wasm/callhelpers-interp-to-managed.cpp | 182 +++++++------ src/coreclr/vm/wasm/helpers.cpp | 251 +++++++++++++----- .../coreclr/InterpToNativeGenerator.cs | 8 +- 6 files changed, 354 insertions(+), 168 deletions(-) diff --git a/src/coreclr/vm/callhelpers.h b/src/coreclr/vm/callhelpers.h index d853823023a073..4779606d3aa57c 100644 --- a/src/coreclr/vm/callhelpers.h +++ b/src/coreclr/vm/callhelpers.h @@ -9,6 +9,13 @@ #ifndef __CALLHELPERS_H__ #define __CALLHELPERS_H__ +#ifdef TARGET_WASM +// A sentinel value to indicate to the stack walker that this frame is NOT R2R generated managed code, +// and it should look for the next Frame in the Frame chain to make further progress. +#define TERMINATE_R2R_STACK_WALK 1 + +#endif + struct CallDescrData { // Input arguments diff --git a/src/coreclr/vm/frames.h b/src/coreclr/vm/frames.h index cf62c6d5dca211..85d87cd2ec0a08 100644 --- a/src/coreclr/vm/frames.h +++ b/src/coreclr/vm/frames.h @@ -2418,17 +2418,6 @@ class InterpreterFrame : public FramedMethodFrame GC_CALL_INTERIOR); \ { -// If FEATURE_INTERPRETER is set, the GC side of conservative reporting is enabled, so it is possible to report GC pointers conservatively -#ifdef FEATURE_INTERPRETER - -#define GCPROTECT_BEGINCONSERVATIVE_ARRAY(ObjRefArray,cnt) do { \ - GCFrame __gcframe( \ - (OBJECTREF*)&(ObjRefArray), \ - cnt, \ - GC_CALL_INTERIOR | GC_CALL_PINNED); \ - { -#endif // FEATURE_INTERPRETER - #define GCPROTECT_END() \ } \ } while(0) diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index 8aeeb22e1649e0..3be3cb3f1a68a2 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -2085,47 +2085,80 @@ void ExecuteInterpretedMethodWithArgs(TADDR targetIp, int8_t* args, size_t argSi // Continuing on from here for interpreter targets is straightforward, but for R2R targets we need to dispatch back // to WebAssembly code. To avoid needing all of the R2R to interpreter thunks have logic for tail-calling onto more // R2R functions, we utilize the InvokeManagedMethod path which will utilize an Interpreter to R2R thunk for this call. -void ExecuteInterpretedMethodWithArgs_PortableEntryPoint(PCODE portableEntrypoint, int8_t* args, size_t argsSize, int8_t* retBuff); +void ExecuteInterpretedMethodWithArgs_PortableEntryPoint(PCODE portableEntrypoint, TransitionBlock* block, size_t argsSize, int8_t* retBuff); -void ExecuteInterpretedMethodWithArgs_PortableEntryPoint_Complex(PCODE portableEntrypoint, int8_t* args, size_t argsSize, int8_t* retBuff) +void ExecuteInterpretedMethodWithArgs_PortableEntryPoint_Complex(PCODE portableEntrypoint, TransitionBlock* block, size_t argsSize, int8_t* retBuff) { + int8_t* args = (int8_t*)(block + 1); MethodDesc* pMethod = PortableEntryPoint::GetMethodDesc(portableEntrypoint); InterpByteCodeStart* targetIp = pMethod->GetInterpreterCode(); if (targetIp == NULL) { - GCPROTECT_BEGINCONSERVATIVE_ARRAY(args, (UINT)(argsSize/sizeof(OBJECTREF))); - GCX_PREEMP(); - (void)pMethod->DoPrestub(NULL /* MethodTable */, CallerGCMode::Coop); - targetIp = pMethod->GetInterpreterCode(); + MAKE_CURRENT_THREAD_AVAILABLE_EX(GetThread()); + + PrestubMethodFrame frame(block, pMethod); + PrestubMethodFrame* pPFrame = &frame; + + bool finishedPrestubPortion = false; + + pPFrame->Push(CURRENT_THREAD); + + EX_TRY + { + INSTALL_MANAGED_EXCEPTION_DISPATCHER; + INSTALL_UNWIND_AND_CONTINUE_HANDLER; + + { + GCX_PREEMP(); + (void)pMethod->DoPrestub(NULL /* MethodTable */, CallerGCMode::Coop); + targetIp = pMethod->GetInterpreterCode(); + } - if (targetIp == NULL) + finishedPrestubPortion = true; + if (targetIp == NULL) + { + _ASSERTE(!PortableEntryPoint::PrefersInterpreterEntryPoint(portableEntrypoint)); + ManagedMethodParam param = { pMethod, args, retBuff, (PCODE)targetIp, nullptr /* WASM-TODO, handle RuntimeAsync */}; + return InvokeManagedMethod(¶m); + } + + UNINSTALL_UNWIND_AND_CONTINUE_HANDLER; + UNINSTALL_MANAGED_EXCEPTION_DISPATCHER; + } + EX_CATCH { - _ASSERTE(!PortableEntryPoint::PrefersInterpreterEntryPoint(portableEntrypoint)); - ManagedMethodParam param = { pMethod, args, retBuff, (PCODE)targetIp, nullptr /* WASM-TODO, handle RuntimeAsync */}; - return InvokeManagedMethod(¶m); + OBJECTHANDLE ohThrowable = CURRENT_THREAD->LastThrownObjectHandle(); + _ASSERTE(ohThrowable); + if (finishedPrestubPortion) + { + StackTraceInfo::AppendElement(ohThrowable, 0, (UINT_PTR)block, pMethod, NULL); + } + EX_RETHROW; } + EX_END_CATCH - GCPROTECT_END(); + pPFrame->Pop(CURRENT_THREAD); } _ASSERTE((PCODE)targetIp == (PCODE)PortableEntryPoint::GetInterpreterData(portableEntrypoint)); - ExecuteInterpretedMethodWithArgs((TADDR)targetIp, args, argsSize, retBuff, (PCODE)&ExecuteInterpretedMethodWithArgs_PortableEntryPoint); + ExecuteInterpretedMethodWithArgs((TADDR)targetIp, args, argsSize, retBuff, (PCODE)block->m_ReturnAddress); return; } -void ExecuteInterpretedMethodWithArgs_PortableEntryPoint(PCODE portableEntrypoint, int8_t* args, size_t argsSize, int8_t* retBuff) +void ExecuteInterpretedMethodWithArgs_PortableEntryPoint(PCODE portableEntrypoint, TransitionBlock* block, size_t argsSize, int8_t* retBuff) { PCODE targetIp; if (!PortableEntryPoint::HasInterpreterData(portableEntrypoint)) { // In this case, we're using this entrypoint like the prestub. - ExecuteInterpretedMethodWithArgs_PortableEntryPoint_Complex(portableEntrypoint, args, argsSize, retBuff); + ExecuteInterpretedMethodWithArgs_PortableEntryPoint_Complex(portableEntrypoint, block, argsSize, retBuff); } else { targetIp = (PCODE)PortableEntryPoint::GetInterpreterData(portableEntrypoint); - ExecuteInterpretedMethodWithArgs((TADDR)targetIp, args, argsSize, retBuff, (PCODE)&ExecuteInterpretedMethodWithArgs_PortableEntryPoint); + int8_t* args = (int8_t*)(block + 1); + ExecuteInterpretedMethodWithArgs((TADDR)targetIp, args, argsSize, retBuff, (PCODE)block->m_ReturnAddress); } } #endif // FEATURE_PORTABLE_ENTRYPOINTS diff --git a/src/coreclr/vm/wasm/callhelpers-interp-to-managed.cpp b/src/coreclr/vm/wasm/callhelpers-interp-to-managed.cpp index 0dd59a9b8c34bb..ec197216bfdfe8 100644 --- a/src/coreclr/vm/wasm/callhelpers-interp-to-managed.cpp +++ b/src/coreclr/vm/wasm/callhelpers-interp-to-managed.cpp @@ -19,28 +19,32 @@ namespace { - static void CallFunc_F64_F64_F64_RetF64_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) + NOINLINE static void CallFunc_F64_F64_F64_RetF64_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) { - double (*fptr)(uintptr_t, double, double, double, PCODE) = (double (*)(uintptr_t, double, double, double, PCODE))pcode; - *((double*)pRet) = (*fptr)(emscripten_stack_get_current(), ARG_F64(0), ARG_F64(1), ARG_F64(2), pPortableEntryPointContext); + int framePointer = TERMINATE_R2R_STACK_WALK; + double (*fptr)(int*, double, double, double, PCODE) = (double (*)(int*, double, double, double, PCODE))pcode; + *((double*)pRet) = (*fptr)(&framePointer, ARG_F64(0), ARG_F64(1), ARG_F64(2), pPortableEntryPointContext); } - static void CallFunc_F64_F64_RetF64_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) + NOINLINE static void CallFunc_F64_F64_RetF64_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) { - double (*fptr)(uintptr_t, double, double, PCODE) = (double (*)(uintptr_t, double, double, PCODE))pcode; - *((double*)pRet) = (*fptr)(emscripten_stack_get_current(), ARG_F64(0), ARG_F64(1), pPortableEntryPointContext); + int framePointer = TERMINATE_R2R_STACK_WALK; + double (*fptr)(int*, double, double, PCODE) = (double (*)(int*, double, double, PCODE))pcode; + *((double*)pRet) = (*fptr)(&framePointer, ARG_F64(0), ARG_F64(1), pPortableEntryPointContext); } - static void CallFunc_F64_I32_RetF64_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) + NOINLINE static void CallFunc_F64_I32_RetF64_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) { - double (*fptr)(uintptr_t, double, int32_t, PCODE) = (double (*)(uintptr_t, double, int32_t, PCODE))pcode; - *((double*)pRet) = (*fptr)(emscripten_stack_get_current(), ARG_F64(0), ARG_I32(1), pPortableEntryPointContext); + int framePointer = TERMINATE_R2R_STACK_WALK; + double (*fptr)(int*, double, int32_t, PCODE) = (double (*)(int*, double, int32_t, PCODE))pcode; + *((double*)pRet) = (*fptr)(&framePointer, ARG_F64(0), ARG_I32(1), pPortableEntryPointContext); } - static void CallFunc_F64_RetF64_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) + NOINLINE static void CallFunc_F64_RetF64_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) { - double (*fptr)(uintptr_t, double, PCODE) = (double (*)(uintptr_t, double, PCODE))pcode; - *((double*)pRet) = (*fptr)(emscripten_stack_get_current(), ARG_F64(0), pPortableEntryPointContext); + int framePointer = TERMINATE_R2R_STACK_WALK; + double (*fptr)(int*, double, PCODE) = (double (*)(int*, double, PCODE))pcode; + *((double*)pRet) = (*fptr)(&framePointer, ARG_F64(0), pPortableEntryPointContext); } static void CallFunc_I32_RetF64(PCODE pcode, int8_t* pArgs, int8_t* pRet) @@ -49,28 +53,32 @@ namespace *((double*)pRet) = (*fptr)(ARG_I32(0)); } - static void CallFunc_F32_F32_F32_RetF32_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) + NOINLINE static void CallFunc_F32_F32_F32_RetF32_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) { - float (*fptr)(uintptr_t, float, float, float, PCODE) = (float (*)(uintptr_t, float, float, float, PCODE))pcode; - *((float*)pRet) = (*fptr)(emscripten_stack_get_current(), ARG_F32(0), ARG_F32(1), ARG_F32(2), pPortableEntryPointContext); + int framePointer = TERMINATE_R2R_STACK_WALK; + float (*fptr)(int*, float, float, float, PCODE) = (float (*)(int*, float, float, float, PCODE))pcode; + *((float*)pRet) = (*fptr)(&framePointer, ARG_F32(0), ARG_F32(1), ARG_F32(2), pPortableEntryPointContext); } - static void CallFunc_F32_F32_RetF32_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) + NOINLINE static void CallFunc_F32_F32_RetF32_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) { - float (*fptr)(uintptr_t, float, float, PCODE) = (float (*)(uintptr_t, float, float, PCODE))pcode; - *((float*)pRet) = (*fptr)(emscripten_stack_get_current(), ARG_F32(0), ARG_F32(1), pPortableEntryPointContext); + int framePointer = TERMINATE_R2R_STACK_WALK; + float (*fptr)(int*, float, float, PCODE) = (float (*)(int*, float, float, PCODE))pcode; + *((float*)pRet) = (*fptr)(&framePointer, ARG_F32(0), ARG_F32(1), pPortableEntryPointContext); } - static void CallFunc_F32_I32_RetF32_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) + NOINLINE static void CallFunc_F32_I32_RetF32_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) { - float (*fptr)(uintptr_t, float, int32_t, PCODE) = (float (*)(uintptr_t, float, int32_t, PCODE))pcode; - *((float*)pRet) = (*fptr)(emscripten_stack_get_current(), ARG_F32(0), ARG_I32(1), pPortableEntryPointContext); + int framePointer = TERMINATE_R2R_STACK_WALK; + float (*fptr)(int*, float, int32_t, PCODE) = (float (*)(int*, float, int32_t, PCODE))pcode; + *((float*)pRet) = (*fptr)(&framePointer, ARG_F32(0), ARG_I32(1), pPortableEntryPointContext); } - static void CallFunc_F32_RetF32_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) + NOINLINE static void CallFunc_F32_RetF32_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) { - float (*fptr)(uintptr_t, float, PCODE) = (float (*)(uintptr_t, float, PCODE))pcode; - *((float*)pRet) = (*fptr)(emscripten_stack_get_current(), ARG_F32(0), pPortableEntryPointContext); + int framePointer = TERMINATE_R2R_STACK_WALK; + float (*fptr)(int*, float, PCODE) = (float (*)(int*, float, PCODE))pcode; + *((float*)pRet) = (*fptr)(&framePointer, ARG_F32(0), pPortableEntryPointContext); } static void CallFunc_Void_RetI32(PCODE pcode, int8_t* pArgs, int8_t* pRet) @@ -127,22 +135,25 @@ namespace *((int32_t*)pRet) = (*fptr)(ARG_I32(0), ARG_I32(1), ARG_I32(2), ARG_I32(3), ARG_I32(4), ARG_I32(5), ARG_I32(6), ARG_I32(7), ARG_I32(8), ARG_I32(9), ARG_I32(10), ARG_I32(11), ARG_I32(12), ARG_I32(13)); } - static void CallFunc_I32_I32_I32_I32_I32_I32_RetI32_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) + NOINLINE static void CallFunc_I32_I32_I32_I32_I32_I32_RetI32_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) { - int32_t (*fptr)(uintptr_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, PCODE) = (int32_t (*)(uintptr_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, PCODE))pcode; - *((int32_t*)pRet) = (*fptr)(emscripten_stack_get_current(), ARG_I32(0), ARG_I32(1), ARG_I32(2), ARG_I32(3), ARG_I32(4), ARG_I32(5), pPortableEntryPointContext); + int framePointer = TERMINATE_R2R_STACK_WALK; + int32_t (*fptr)(int*, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, PCODE) = (int32_t (*)(int*, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, PCODE))pcode; + *((int32_t*)pRet) = (*fptr)(&framePointer, ARG_I32(0), ARG_I32(1), ARG_I32(2), ARG_I32(3), ARG_I32(4), ARG_I32(5), pPortableEntryPointContext); } - static void CallFunc_I32_I32_I32_I32_I32_RetI32_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) + NOINLINE static void CallFunc_I32_I32_I32_I32_I32_RetI32_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) { - int32_t (*fptr)(uintptr_t, int32_t, int32_t, int32_t, int32_t, int32_t, PCODE) = (int32_t (*)(uintptr_t, int32_t, int32_t, int32_t, int32_t, int32_t, PCODE))pcode; - *((int32_t*)pRet) = (*fptr)(emscripten_stack_get_current(), ARG_I32(0), ARG_I32(1), ARG_I32(2), ARG_I32(3), ARG_I32(4), pPortableEntryPointContext); + int framePointer = TERMINATE_R2R_STACK_WALK; + int32_t (*fptr)(int*, int32_t, int32_t, int32_t, int32_t, int32_t, PCODE) = (int32_t (*)(int*, int32_t, int32_t, int32_t, int32_t, int32_t, PCODE))pcode; + *((int32_t*)pRet) = (*fptr)(&framePointer, ARG_I32(0), ARG_I32(1), ARG_I32(2), ARG_I32(3), ARG_I32(4), pPortableEntryPointContext); } - static void CallFunc_I32_I32_I32_I32_RetI32_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) + NOINLINE static void CallFunc_I32_I32_I32_I32_RetI32_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) { - int32_t (*fptr)(uintptr_t, int32_t, int32_t, int32_t, int32_t, PCODE) = (int32_t (*)(uintptr_t, int32_t, int32_t, int32_t, int32_t, PCODE))pcode; - *((int32_t*)pRet) = (*fptr)(emscripten_stack_get_current(), ARG_I32(0), ARG_I32(1), ARG_I32(2), ARG_I32(3), pPortableEntryPointContext); + int framePointer = TERMINATE_R2R_STACK_WALK; + int32_t (*fptr)(int*, int32_t, int32_t, int32_t, int32_t, PCODE) = (int32_t (*)(int*, int32_t, int32_t, int32_t, int32_t, PCODE))pcode; + *((int32_t*)pRet) = (*fptr)(&framePointer, ARG_I32(0), ARG_I32(1), ARG_I32(2), ARG_I32(3), pPortableEntryPointContext); } static void CallFunc_I32_I32_I32_I64_RetI32(PCODE pcode, int8_t* pArgs, int8_t* pRet) @@ -157,10 +168,11 @@ namespace *((int32_t*)pRet) = (*fptr)(ARG_I32(0), ARG_I32(1), ARG_I32(2), ARG_IND(3), ARG_I32(4)); } - static void CallFunc_I32_I32_I32_RetI32_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) + NOINLINE static void CallFunc_I32_I32_I32_RetI32_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) { - int32_t (*fptr)(uintptr_t, int32_t, int32_t, int32_t, PCODE) = (int32_t (*)(uintptr_t, int32_t, int32_t, int32_t, PCODE))pcode; - *((int32_t*)pRet) = (*fptr)(emscripten_stack_get_current(), ARG_I32(0), ARG_I32(1), ARG_I32(2), pPortableEntryPointContext); + int framePointer = TERMINATE_R2R_STACK_WALK; + int32_t (*fptr)(int*, int32_t, int32_t, int32_t, PCODE) = (int32_t (*)(int*, int32_t, int32_t, int32_t, PCODE))pcode; + *((int32_t*)pRet) = (*fptr)(&framePointer, ARG_I32(0), ARG_I32(1), ARG_I32(2), pPortableEntryPointContext); } static void CallFunc_I32_I32_I64_RetI32(PCODE pcode, int8_t* pArgs, int8_t* pRet) @@ -181,10 +193,11 @@ namespace *((int32_t*)pRet) = (*fptr)(ARG_I32(0), ARG_I32(1), ARG_IND(2), ARG_I32(3), ARG_I32(4), ARG_IND(5)); } - static void CallFunc_I32_I32_RetI32_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) + NOINLINE static void CallFunc_I32_I32_RetI32_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) { - int32_t (*fptr)(uintptr_t, int32_t, int32_t, PCODE) = (int32_t (*)(uintptr_t, int32_t, int32_t, PCODE))pcode; - *((int32_t*)pRet) = (*fptr)(emscripten_stack_get_current(), ARG_I32(0), ARG_I32(1), pPortableEntryPointContext); + int framePointer = TERMINATE_R2R_STACK_WALK; + int32_t (*fptr)(int*, int32_t, int32_t, PCODE) = (int32_t (*)(int*, int32_t, int32_t, PCODE))pcode; + *((int32_t*)pRet) = (*fptr)(&framePointer, ARG_I32(0), ARG_I32(1), pPortableEntryPointContext); } static void CallFunc_I32_I64_RetI32(PCODE pcode, int8_t* pArgs, int8_t* pRet) @@ -229,10 +242,11 @@ namespace *((int32_t*)pRet) = (*fptr)(ARG_I32(0), ARG_IND(1), ARG_I32(2), ARG_I32(3), ARG_I32(4)); } - static void CallFunc_I32_RetI32_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) + NOINLINE static void CallFunc_I32_RetI32_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) { - int32_t (*fptr)(uintptr_t, int32_t, PCODE) = (int32_t (*)(uintptr_t, int32_t, PCODE))pcode; - *((int32_t*)pRet) = (*fptr)(emscripten_stack_get_current(), ARG_I32(0), pPortableEntryPointContext); + int framePointer = TERMINATE_R2R_STACK_WALK; + int32_t (*fptr)(int*, int32_t, PCODE) = (int32_t (*)(int*, int32_t, PCODE))pcode; + *((int32_t*)pRet) = (*fptr)(&framePointer, ARG_I32(0), pPortableEntryPointContext); } static void CallFunc_I64_I32_I64_I32_RetI32(PCODE pcode, int8_t* pArgs, int8_t* pRet) @@ -313,10 +327,11 @@ namespace *((int32_t*)pRet) = (*fptr)(ARG_IND(0), ARG_IND(1), ARG_IND(2)); } - static void CallFunc_Void_RetI32_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) + NOINLINE static void CallFunc_Void_RetI32_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) { - int32_t (*fptr)(uintptr_t, PCODE) = (int32_t (*)(uintptr_t, PCODE))pcode; - *((int32_t*)pRet) = (*fptr)(emscripten_stack_get_current(), pPortableEntryPointContext); + int framePointer = TERMINATE_R2R_STACK_WALK; + int32_t (*fptr)(int*, PCODE) = (int32_t (*)(int*, PCODE))pcode; + *((int32_t*)pRet) = (*fptr)(&framePointer, pPortableEntryPointContext); } static void CallFunc_Void_RetI64(PCODE pcode, int8_t* pArgs, int8_t* pRet) @@ -349,34 +364,39 @@ namespace *((int64_t*)pRet) = (*fptr)(ARG_I32(0), ARG_I64(1), ARG_I32(2)); } - static void CallFunc_I32_I64_I64_RetI64_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) + NOINLINE static void CallFunc_I32_I64_I64_RetI64_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) { - int64_t (*fptr)(uintptr_t, int32_t, int64_t, int64_t, PCODE) = (int64_t (*)(uintptr_t, int32_t, int64_t, int64_t, PCODE))pcode; - *((int64_t*)pRet) = (*fptr)(emscripten_stack_get_current(), ARG_I32(0), ARG_I64(1), ARG_I64(2), pPortableEntryPointContext); + int framePointer = TERMINATE_R2R_STACK_WALK; + int64_t (*fptr)(int*, int32_t, int64_t, int64_t, PCODE) = (int64_t (*)(int*, int32_t, int64_t, int64_t, PCODE))pcode; + *((int64_t*)pRet) = (*fptr)(&framePointer, ARG_I32(0), ARG_I64(1), ARG_I64(2), pPortableEntryPointContext); } - static void CallFunc_I32_I64_RetI64_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) + NOINLINE static void CallFunc_I32_I64_RetI64_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) { - int64_t (*fptr)(uintptr_t, int32_t, int64_t, PCODE) = (int64_t (*)(uintptr_t, int32_t, int64_t, PCODE))pcode; - *((int64_t*)pRet) = (*fptr)(emscripten_stack_get_current(), ARG_I32(0), ARG_I64(1), pPortableEntryPointContext); + int framePointer = TERMINATE_R2R_STACK_WALK; + int64_t (*fptr)(int*, int32_t, int64_t, PCODE) = (int64_t (*)(int*, int32_t, int64_t, PCODE))pcode; + *((int64_t*)pRet) = (*fptr)(&framePointer, ARG_I32(0), ARG_I64(1), pPortableEntryPointContext); } - static void CallFunc_I32_RetI64_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) + NOINLINE static void CallFunc_I32_RetI64_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) { - int64_t (*fptr)(uintptr_t, int32_t, PCODE) = (int64_t (*)(uintptr_t, int32_t, PCODE))pcode; - *((int64_t*)pRet) = (*fptr)(emscripten_stack_get_current(), ARG_I32(0), pPortableEntryPointContext); + int framePointer = TERMINATE_R2R_STACK_WALK; + int64_t (*fptr)(int*, int32_t, PCODE) = (int64_t (*)(int*, int32_t, PCODE))pcode; + *((int64_t*)pRet) = (*fptr)(&framePointer, ARG_I32(0), pPortableEntryPointContext); } - static void CallFunc_I64_I64_RetI64_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) + NOINLINE static void CallFunc_I64_I64_RetI64_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) { - int64_t (*fptr)(uintptr_t, int64_t, int64_t, PCODE) = (int64_t (*)(uintptr_t, int64_t, int64_t, PCODE))pcode; - *((int64_t*)pRet) = (*fptr)(emscripten_stack_get_current(), ARG_I64(0), ARG_I64(1), pPortableEntryPointContext); + int framePointer = TERMINATE_R2R_STACK_WALK; + int64_t (*fptr)(int*, int64_t, int64_t, PCODE) = (int64_t (*)(int*, int64_t, int64_t, PCODE))pcode; + *((int64_t*)pRet) = (*fptr)(&framePointer, ARG_I64(0), ARG_I64(1), pPortableEntryPointContext); } - static void CallFunc_Void_RetI64_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) + NOINLINE static void CallFunc_Void_RetI64_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) { - int64_t (*fptr)(uintptr_t, PCODE) = (int64_t (*)(uintptr_t, PCODE))pcode; - *((int64_t*)pRet) = (*fptr)(emscripten_stack_get_current(), pPortableEntryPointContext); + int framePointer = TERMINATE_R2R_STACK_WALK; + int64_t (*fptr)(int*, PCODE) = (int64_t (*)(int*, PCODE))pcode; + *((int64_t*)pRet) = (*fptr)(&framePointer, pPortableEntryPointContext); } static void CallFunc_Void_RetVoid(PCODE pcode, int8_t* pArgs, int8_t* pRet) @@ -385,16 +405,18 @@ namespace (*fptr)(); } - static void CallFunc_F64_I32_I32_RetVoid_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) + NOINLINE static void CallFunc_F64_I32_I32_RetVoid_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) { - void (*fptr)(uintptr_t, double, int32_t, int32_t, PCODE) = (void (*)(uintptr_t, double, int32_t, int32_t, PCODE))pcode; - (*fptr)(emscripten_stack_get_current(), ARG_F64(0), ARG_I32(1), ARG_I32(2), pPortableEntryPointContext); + int framePointer = TERMINATE_R2R_STACK_WALK; + void (*fptr)(int*, double, int32_t, int32_t, PCODE) = (void (*)(int*, double, int32_t, int32_t, PCODE))pcode; + (*fptr)(&framePointer, ARG_F64(0), ARG_I32(1), ARG_I32(2), pPortableEntryPointContext); } - static void CallFunc_F32_I32_I32_RetVoid_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) + NOINLINE static void CallFunc_F32_I32_I32_RetVoid_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) { - void (*fptr)(uintptr_t, float, int32_t, int32_t, PCODE) = (void (*)(uintptr_t, float, int32_t, int32_t, PCODE))pcode; - (*fptr)(emscripten_stack_get_current(), ARG_F32(0), ARG_I32(1), ARG_I32(2), pPortableEntryPointContext); + int framePointer = TERMINATE_R2R_STACK_WALK; + void (*fptr)(int*, float, int32_t, int32_t, PCODE) = (void (*)(int*, float, int32_t, int32_t, PCODE))pcode; + (*fptr)(&framePointer, ARG_F32(0), ARG_I32(1), ARG_I32(2), pPortableEntryPointContext); } static void CallFunc_I32_RetVoid(PCODE pcode, int8_t* pArgs, int8_t* pRet) @@ -445,10 +467,11 @@ namespace (*fptr)(ARG_I32(0), ARG_I32(1), ARG_I32(2), ARG_IND(3), ARG_IND(4), ARG_I32(5)); } - static void CallFunc_I32_I32_I32_RetVoid_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) + NOINLINE static void CallFunc_I32_I32_I32_RetVoid_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) { - void (*fptr)(uintptr_t, int32_t, int32_t, int32_t, PCODE) = (void (*)(uintptr_t, int32_t, int32_t, int32_t, PCODE))pcode; - (*fptr)(emscripten_stack_get_current(), ARG_I32(0), ARG_I32(1), ARG_I32(2), pPortableEntryPointContext); + int framePointer = TERMINATE_R2R_STACK_WALK; + void (*fptr)(int*, int32_t, int32_t, int32_t, PCODE) = (void (*)(int*, int32_t, int32_t, int32_t, PCODE))pcode; + (*fptr)(&framePointer, ARG_I32(0), ARG_I32(1), ARG_I32(2), pPortableEntryPointContext); } static void CallFunc_I32_I32_IND_IND_I32_RetVoid(PCODE pcode, int8_t* pArgs, int8_t* pRet) @@ -463,10 +486,11 @@ namespace (*fptr)(ARG_I32(0), ARG_I32(1), ARG_IND(2), ARG_IND(3), ARG_I32(4), ARG_I32(5)); } - static void CallFunc_I32_I32_RetVoid_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) + NOINLINE static void CallFunc_I32_I32_RetVoid_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) { - void (*fptr)(uintptr_t, int32_t, int32_t, PCODE) = (void (*)(uintptr_t, int32_t, int32_t, PCODE))pcode; - (*fptr)(emscripten_stack_get_current(), ARG_I32(0), ARG_I32(1), pPortableEntryPointContext); + int framePointer = TERMINATE_R2R_STACK_WALK; + void (*fptr)(int*, int32_t, int32_t, PCODE) = (void (*)(int*, int32_t, int32_t, PCODE))pcode; + (*fptr)(&framePointer, ARG_I32(0), ARG_I32(1), pPortableEntryPointContext); } static void CallFunc_I32_IND_I32_RetVoid(PCODE pcode, int8_t* pArgs, int8_t* pRet) @@ -475,10 +499,11 @@ namespace (*fptr)(ARG_I32(0), ARG_IND(1), ARG_I32(2)); } - static void CallFunc_I32_RetVoid_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) + NOINLINE static void CallFunc_I32_RetVoid_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) { - void (*fptr)(uintptr_t, int32_t, PCODE) = (void (*)(uintptr_t, int32_t, PCODE))pcode; - (*fptr)(emscripten_stack_get_current(), ARG_I32(0), pPortableEntryPointContext); + int framePointer = TERMINATE_R2R_STACK_WALK; + void (*fptr)(int*, int32_t, PCODE) = (void (*)(int*, int32_t, PCODE))pcode; + (*fptr)(&framePointer, ARG_I32(0), pPortableEntryPointContext); } static void CallFunc_I64_RetVoid(PCODE pcode, int8_t* pArgs, int8_t* pRet) @@ -547,10 +572,11 @@ namespace (*fptr)(ARG_IND(0), ARG_IND(1), ARG_I32(2), ARG_I32(3), ARG_I32(4), ARG_I32(5), ARG_I32(6)); } - static void CallFunc_Void_RetVoid_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) + NOINLINE static void CallFunc_Void_RetVoid_PE(PCODE pcode, int8_t* pArgs, int8_t* pRet, PCODE pPortableEntryPointContext) { - void (*fptr)(uintptr_t, PCODE) = (void (*)(uintptr_t, PCODE))pcode; - (*fptr)(emscripten_stack_get_current(), pPortableEntryPointContext); + int framePointer = TERMINATE_R2R_STACK_WALK; + void (*fptr)(int*, PCODE) = (void (*)(int*, PCODE))pcode; + (*fptr)(&framePointer, pPortableEntryPointContext); } } diff --git a/src/coreclr/vm/wasm/helpers.cpp b/src/coreclr/vm/wasm/helpers.cpp index e068d57ed4adde..fafcd1483a57ee 100644 --- a/src/coreclr/vm/wasm/helpers.cpp +++ b/src/coreclr/vm/wasm/helpers.cpp @@ -10,14 +10,15 @@ #include "cgensys.h" #include "readytorun.h" -void ExecuteInterpretedMethodWithArgs_PortableEntryPoint(PCODE portableEntrypoint, int8_t* args, size_t argsSize, int8_t* retBuff); +void ExecuteInterpretedMethodWithArgs_PortableEntryPoint(PCODE portableEntrypoint, TransitionBlock* block, size_t argsSize, int8_t* retBuff); #define WASM_WRAPPER_FUNC_INITIAL \ { \ - asm ("local.get 0\n" /* Capture stackArg onto the stack*/ \ + asm ("local.get 0\n" /* Capture callersFramePointer onto the stack for calling _IMPL function*/ \ + "local.get 0\n" /* Capture callersFramePointer onto the stack for setting the __stack_pointer */ \ "global.get __stack_pointer\n" /* Get current value of stack global */ \ - "local.set 0\n" /* Store current stack global into stackArg local */ \ - "global.set __stack_pointer\n" /* Set stack global to the initial value of stackArg, which is the current stack pointer for the interpreter call */ + "local.set 0\n" /* Store current stack global into callersFramePointer local */ \ + "global.set __stack_pointer\n" /* Set stack global to the initial value of callersFramePointer, which is the current stack pointer for the interpreter call */ #define WASM_WRAPPER_FUNC_EPILOG(_method) \ "call %0\n" /* Call the actual implementation function*/ \ @@ -26,56 +27,56 @@ void ExecuteInterpretedMethodWithArgs_PortableEntryPoint(PCODE portableEntrypoin "return" :: "i" (_method ## _IMPL)); \ } -#define WASM_WRAPPER_FUNC_0(_rettype, _method) __attribute__((naked)) _rettype _method(uintptr_t stackArg) WASM_WRAPPER_FUNC_INITIAL WASM_WRAPPER_FUNC_EPILOG(_method) -#define WASM_WRAPPER_FUNC_1(_rettype, _method, a) __attribute__((naked)) _rettype _method(uintptr_t stackArg, a) WASM_WRAPPER_FUNC_INITIAL "local.get 1\n" WASM_WRAPPER_FUNC_EPILOG(_method) -#define WASM_WRAPPER_FUNC_2(_rettype, _method, a, b) __attribute__((naked)) _rettype _method(uintptr_t stackArg, a, b) WASM_WRAPPER_FUNC_INITIAL "local.get 1\nlocal.get 2\n" WASM_WRAPPER_FUNC_EPILOG(_method) -#define WASM_WRAPPER_FUNC_3(_rettype, _method, a, b, c) __attribute__((naked)) _rettype _method(uintptr_t stackArg, a, b, c) WASM_WRAPPER_FUNC_INITIAL "local.get 1\nlocal.get 2\nlocal.get 3\n" WASM_WRAPPER_FUNC_EPILOG(_method) -#define WASM_WRAPPER_FUNC_4(_rettype, _method, a, b, c, d) __attribute__((naked)) _rettype _method(uintptr_t stackArg, a, b, c, d) WASM_WRAPPER_FUNC_INITIAL "local.get 1\nlocal.get 2\nlocal.get 3\nlocal.get 4\n" WASM_WRAPPER_FUNC_EPILOG(_method) -#define WASM_WRAPPER_FUNC_5(_rettype, _method, a, b, c, d, e) __attribute__((naked)) _rettype _method(uintptr_t stackArg, a, b, c, d, e) WASM_WRAPPER_FUNC_INITIAL "local.get 1\nlocal.get 2\nlocal.get 3\nlocal.get 4\nlocal.get 5\n" WASM_WRAPPER_FUNC_EPILOG(_method) -#define WASM_WRAPPER_FUNC_6(_rettype, _method, a, b, c, d, e, f) __attribute__((naked)) _rettype _method(uintptr_t stackArg, a, b, c, d, e, f) WASM_WRAPPER_FUNC_INITIAL "local.get 1\nlocal.get 2\nlocal.get 3\nlocal.get 4\nlocal.get 5\nlocal.get 6\n" WASM_WRAPPER_FUNC_EPILOG(_method) -#define WASM_WRAPPER_FUNC_7(_rettype, _method, a, b, c, d, e, f, g) __attribute__((naked)) _rettype _method(uintptr_t stackArg, a, b, c, d, e, f, g) WASM_WRAPPER_FUNC_INITIAL "local.get 1\nlocal.get 2\nlocal.get 3\nlocal.get 4\nlocal.get 5\nlocal.get 6\nlocal.get 7\n" WASM_WRAPPER_FUNC_EPILOG(_method) -#define WASM_WRAPPER_FUNC_8(_rettype, _method, a, b, c, d, e, f, g, h) __attribute__((naked)) _rettype _method(uintptr_t stackArg, a, b, c, d, e, f, g, h) WASM_WRAPPER_FUNC_INITIAL "local.get 1\nlocal.get 2\nlocal.get 3\nlocal.get 4\nlocal.get 5\nlocal.get 6\nlocal.get 7\nlocal.get 8\n" WASM_WRAPPER_FUNC_EPILOG(_method) -#define WASM_WRAPPER_FUNC_9(_rettype, _method, a, b, c, d, e, f, g, h, i) __attribute__((naked)) _rettype _method(uintptr_t stackArg, a, b, c, d, e, f, g, h, i) WASM_WRAPPER_FUNC_INITIAL "local.get 1\nlocal.get 2\nlocal.get 3\nlocal.get 4\nlocal.get 5\nlocal.get 6\nlocal.get 7\nlocal.get 8\nlocal.get 9\n" WASM_WRAPPER_FUNC_EPILOG(_method) +#define WASM_WRAPPER_FUNC_0(_rettype, _method) __attribute__((naked)) _rettype _method(uintptr_t callersFramePointer) WASM_WRAPPER_FUNC_INITIAL WASM_WRAPPER_FUNC_EPILOG(_method) +#define WASM_WRAPPER_FUNC_1(_rettype, _method, a) __attribute__((naked)) _rettype _method(uintptr_t callersFramePointer, a) WASM_WRAPPER_FUNC_INITIAL "local.get 1\n" WASM_WRAPPER_FUNC_EPILOG(_method) +#define WASM_WRAPPER_FUNC_2(_rettype, _method, a, b) __attribute__((naked)) _rettype _method(uintptr_t callersFramePointer, a, b) WASM_WRAPPER_FUNC_INITIAL "local.get 1\nlocal.get 2\n" WASM_WRAPPER_FUNC_EPILOG(_method) +#define WASM_WRAPPER_FUNC_3(_rettype, _method, a, b, c) __attribute__((naked)) _rettype _method(uintptr_t callersFramePointer, a, b, c) WASM_WRAPPER_FUNC_INITIAL "local.get 1\nlocal.get 2\nlocal.get 3\n" WASM_WRAPPER_FUNC_EPILOG(_method) +#define WASM_WRAPPER_FUNC_4(_rettype, _method, a, b, c, d) __attribute__((naked)) _rettype _method(uintptr_t callersFramePointer, a, b, c, d) WASM_WRAPPER_FUNC_INITIAL "local.get 1\nlocal.get 2\nlocal.get 3\nlocal.get 4\n" WASM_WRAPPER_FUNC_EPILOG(_method) +#define WASM_WRAPPER_FUNC_5(_rettype, _method, a, b, c, d, e) __attribute__((naked)) _rettype _method(uintptr_t callersFramePointer, a, b, c, d, e) WASM_WRAPPER_FUNC_INITIAL "local.get 1\nlocal.get 2\nlocal.get 3\nlocal.get 4\nlocal.get 5\n" WASM_WRAPPER_FUNC_EPILOG(_method) +#define WASM_WRAPPER_FUNC_6(_rettype, _method, a, b, c, d, e, f) __attribute__((naked)) _rettype _method(uintptr_t callersFramePointer, a, b, c, d, e, f) WASM_WRAPPER_FUNC_INITIAL "local.get 1\nlocal.get 2\nlocal.get 3\nlocal.get 4\nlocal.get 5\nlocal.get 6\n" WASM_WRAPPER_FUNC_EPILOG(_method) +#define WASM_WRAPPER_FUNC_7(_rettype, _method, a, b, c, d, e, f, g) __attribute__((naked)) _rettype _method(uintptr_t callersFramePointer, a, b, c, d, e, f, g) WASM_WRAPPER_FUNC_INITIAL "local.get 1\nlocal.get 2\nlocal.get 3\nlocal.get 4\nlocal.get 5\nlocal.get 6\nlocal.get 7\n" WASM_WRAPPER_FUNC_EPILOG(_method) +#define WASM_WRAPPER_FUNC_8(_rettype, _method, a, b, c, d, e, f, g, h) __attribute__((naked)) _rettype _method(uintptr_t callersFramePointer, a, b, c, d, e, f, g, h) WASM_WRAPPER_FUNC_INITIAL "local.get 1\nlocal.get 2\nlocal.get 3\nlocal.get 4\nlocal.get 5\nlocal.get 6\nlocal.get 7\nlocal.get 8\n" WASM_WRAPPER_FUNC_EPILOG(_method) +#define WASM_WRAPPER_FUNC_9(_rettype, _method, a, b, c, d, e, f, g, h, i) __attribute__((naked)) _rettype _method(uintptr_t callersFramePointer, a, b, c, d, e, f, g, h, i) WASM_WRAPPER_FUNC_INITIAL "local.get 1\nlocal.get 2\nlocal.get 3\nlocal.get 4\nlocal.get 5\nlocal.get 6\nlocal.get 7\nlocal.get 8\nlocal.get 9\n" WASM_WRAPPER_FUNC_EPILOG(_method) -#define WASM_CALLABLE_FUNC_0(_rettype, _method) _rettype _method ## _IMPL (); \ +#define WASM_CALLABLE_FUNC_0(_rettype, _method) _rettype _method ## _IMPL (uintptr_t callersFramePointer); \ WASM_WRAPPER_FUNC_0(_rettype, _method) \ - _rettype _method ## _IMPL () + _rettype _method ## _IMPL (uintptr_t callersFramePointer) -#define WASM_CALLABLE_FUNC_1(_rettype, _method, a) _rettype _method ## _IMPL (a); \ +#define WASM_CALLABLE_FUNC_1(_rettype, _method, a) _rettype _method ## _IMPL (uintptr_t callersFramePointer, a); \ WASM_WRAPPER_FUNC_1(_rettype, _method, a) \ - _rettype _method ## _IMPL (a) + _rettype _method ## _IMPL (uintptr_t callersFramePointer, a) -#define WASM_CALLABLE_FUNC_2(_rettype, _method, a, b) _rettype _method ## _IMPL (a, b); \ +#define WASM_CALLABLE_FUNC_2(_rettype, _method, a, b) _rettype _method ## _IMPL (uintptr_t callersFramePointer, a, b); \ WASM_WRAPPER_FUNC_2(_rettype, _method, a, b) \ - _rettype _method ## _IMPL (a, b) + _rettype _method ## _IMPL (uintptr_t callersFramePointer, a, b) -#define WASM_CALLABLE_FUNC_3(_rettype, _method, a, b, c) _rettype _method ## _IMPL (a, b, c); \ +#define WASM_CALLABLE_FUNC_3(_rettype, _method, a, b, c) _rettype _method ## _IMPL (uintptr_t callersFramePointer, a, b, c); \ WASM_WRAPPER_FUNC_3(_rettype, _method, a, b, c) \ - _rettype _method ## _IMPL (a, b, c) + _rettype _method ## _IMPL (uintptr_t callersFramePointer, a, b, c) -#define WASM_CALLABLE_FUNC_4(_rettype, _method, a, b, c, d) _rettype _method ## _IMPL (a, b, c, d); \ +#define WASM_CALLABLE_FUNC_4(_rettype, _method, a, b, c, d) _rettype _method ## _IMPL (uintptr_t callersFramePointer, a, b, c, d); \ WASM_WRAPPER_FUNC_4(_rettype, _method, a, b, c, d) \ - _rettype _method ## _IMPL (a, b, c, d) + _rettype _method ## _IMPL (uintptr_t callersFramePointer, a, b, c, d) -#define WASM_CALLABLE_FUNC_5(_rettype, _method, a, b, c, d, e) _rettype _method ## _IMPL (a, b, c, d, e); \ +#define WASM_CALLABLE_FUNC_5(_rettype, _method, a, b, c, d, e) _rettype _method ## _IMPL (uintptr_t callersFramePointer, a, b, c, d, e); \ WASM_WRAPPER_FUNC_5(_rettype, _method, a, b, c, d, e) \ - _rettype _method ## _IMPL (a, b, c, d, e) + _rettype _method ## _IMPL (uintptr_t callersFramePointer, a, b, c, d, e) -#define WASM_CALLABLE_FUNC_6(_rettype, _method, a, b, c, d, e, f) _rettype _method ## _IMPL (a, b, c, d, e, f); \ +#define WASM_CALLABLE_FUNC_6(_rettype, _method, a, b, c, d, e, f) _rettype _method ## _IMPL (uintptr_t callersFramePointer, a, b, c, d, e, f); \ WASM_WRAPPER_FUNC_6(_rettype, _method, a, b, c, d, e, f) \ - _rettype _method ## _IMPL (a, b, c, d, e, f) + _rettype _method ## _IMPL (uintptr_t callersFramePointer, a, b, c, d, e, f) -#define WASM_CALLABLE_FUNC_7(_rettype, _method, a, b, c, d, e, f, g) _rettype _method ## _IMPL (a, b, c, d, e, f, g); \ +#define WASM_CALLABLE_FUNC_7(_rettype, _method, a, b, c, d, e, f, g) _rettype _method ## _IMPL (uintptr_t callersFramePointer, a, b, c, d, e, f, g); \ WASM_WRAPPER_FUNC_7(_rettype, _method, a, b, c, d, e, f, g) \ - _rettype _method ## _IMPL (a, b, c, d, e, f, g) + _rettype _method ## _IMPL (uintptr_t callersFramePointer, a, b, c, d, e, f, g) -#define WASM_CALLABLE_FUNC_8(_rettype, _method, a, b, c, d, e, f, g, h) _rettype _method ## _IMPL (a, b, c, d, e, f, g, h); \ +#define WASM_CALLABLE_FUNC_8(_rettype, _method, a, b, c, d, e, f, g, h) _rettype _method ## _IMPL (uintptr_t callersFramePointer, a, b, c, d, e, f, g, h); \ WASM_WRAPPER_FUNC_8(_rettype, _method, a, b, c, d, e, f, g, h) \ - _rettype _method ## _IMPL (a, b, c, d, e, f, g, h) + _rettype _method ## _IMPL (uintptr_t callersFramePointer, a, b, c, d, e, f, g, h) -#define WASM_CALLABLE_FUNC_9(_rettype, _method, a, b, c, d, e, f, g, h, i) _rettype _method ## _IMPL (a, b, c, d, e, f, g, h, i); \ +#define WASM_CALLABLE_FUNC_9(_rettype, _method, a, b, c, d, e, f, g, h, i) _rettype _method ## _IMPL (uintptr_t callersFramePointer, a, b, c, d, e, f, g, h, i); \ WASM_WRAPPER_FUNC_9(_rettype, _method, a, b, c, d, e, f, g, h, i) \ - _rettype _method ## _IMPL (a, b, c, d, e, f, g, h, i) + _rettype _method ## _IMPL (uintptr_t callersFramePointer, a, b, c, d, e, f, g, h, i) // ------------------------------------------------- // Logic that will eventually mostly be pregenerated for R2R to interpreter code @@ -84,110 +85,240 @@ namespace { WASM_CALLABLE_FUNC_1(void, CallInterpreter_RetVoid, PCODE portableEntrypoint) { + struct + { + TransitionBlock block; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = callersFramePointer; void * result = NULL; - ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, nullptr, 0, (int8_t*)&result); + + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, 0, (int8_t*)&result); return; } WASM_CALLABLE_FUNC_2(void, CallInterpreter_I32_RetVoid, int32_t arg0, PCODE portableEntrypoint) { - int64_t args[1] = { (int64_t)arg0 }; + struct + { + TransitionBlock block; + int64_t args[1]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = callersFramePointer; + transitionBlock.args[0] = (int64_t)arg0; + static_assert(offsetof(decltype(transitionBlock), args) == sizeof(TransitionBlock), "Args array must be at a TransitionBlock offset from the start of the block"); void * result = NULL; - ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, (int8_t*)args, sizeof(args), (int8_t*)&result); + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); return; } WASM_CALLABLE_FUNC_3(void, CallInterpreter_I32_I32_RetVoid, int32_t arg0, int32_t arg1, PCODE portableEntrypoint) { - int64_t args[2] = { (int64_t)arg0, (int64_t)arg1 }; + struct + { + TransitionBlock block; + int64_t args[2]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = callersFramePointer; + transitionBlock.args[0] = (int64_t)arg0; + transitionBlock.args[1] = (int64_t)arg1; + static_assert(offsetof(decltype(transitionBlock), args) == sizeof(TransitionBlock), "Args array must be at a TransitionBlock offset from the start of the block"); void * result = NULL; - ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, (int8_t*)args, sizeof(args), (int8_t*)&result); + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); return; } WASM_CALLABLE_FUNC_4(void, CallInterpreter_I32_I32_I32_RetVoid, int32_t arg0, int32_t arg1, int32_t arg2, PCODE portableEntrypoint) { - int64_t args[3] = { (int64_t)arg0, (int64_t)arg1, (int64_t)arg2 }; + struct + { + TransitionBlock block; + int64_t args[3]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = callersFramePointer; + transitionBlock.args[0] = (int64_t)arg0; + transitionBlock.args[1] = (int64_t)arg1; + transitionBlock.args[2] = (int64_t)arg2; + static_assert(offsetof(decltype(transitionBlock), args) == sizeof(TransitionBlock), "Args array must be at a TransitionBlock offset from the start of the block"); + void * result = NULL; - ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, (int8_t*)args, sizeof(args), (int8_t*)&result); + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); return; } WASM_CALLABLE_FUNC_5(void, CallInterpreter_I32_I32_I32_I32_RetVoid, int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, PCODE portableEntrypoint) { - int64_t args[4] = { (int64_t)arg0, (int64_t)arg1, (int64_t)arg2, (int64_t)arg3 }; + struct + { + TransitionBlock block; + int64_t args[4]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = callersFramePointer; + transitionBlock.args[0] = (int64_t)arg0; + transitionBlock.args[1] = (int64_t)arg1; + transitionBlock.args[2] = (int64_t)arg2; + transitionBlock.args[3] = (int64_t)arg3; + static_assert(offsetof(decltype(transitionBlock), args) == sizeof(TransitionBlock), "Args array must be at a TransitionBlock offset from the start of the block"); void * result = NULL; - ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, (int8_t*)args, sizeof(args), (int8_t*)&result); + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); return; } WASM_CALLABLE_FUNC_1(int32_t, CallInterpreter_RetI32, PCODE portableEntrypoint) { + struct + { + TransitionBlock block; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = callersFramePointer; void * result = NULL; - ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, nullptr, 0, (int8_t*)&result); + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, 0, (int8_t*)&result); return (int32_t)result; } WASM_CALLABLE_FUNC_2(int32_t, CallInterpreter_I32_RetI32, int32_t arg0, PCODE portableEntrypoint) { - int64_t args[1] = { (int64_t)arg0 }; + struct + { + TransitionBlock block; + int64_t args[1]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = callersFramePointer; + transitionBlock.args[0] = (int64_t)arg0; + static_assert(offsetof(decltype(transitionBlock), args) == sizeof(TransitionBlock), "Args array must be at a TransitionBlock offset from the start of the block"); void * result = NULL; - ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, (int8_t*)args, sizeof(args), (int8_t*)&result); + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); return (int32_t)result; } WASM_CALLABLE_FUNC_3(int32_t, CallInterpreter_I32_I32_RetI32, int32_t arg0, int32_t arg1, PCODE portableEntrypoint) { - int64_t args[2] = { (int64_t)arg0, (int64_t)arg1 }; + struct + { + TransitionBlock block; + int64_t args[2]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = callersFramePointer; + transitionBlock.args[0] = (int64_t)arg0; + transitionBlock.args[1] = (int64_t)arg1; + static_assert(offsetof(decltype(transitionBlock), args) == sizeof(TransitionBlock), "Args array must be at a TransitionBlock offset from the start of the block"); void * result = NULL; - ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, (int8_t*)args, sizeof(args), (int8_t*)&result); + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); return (int32_t)result; } WASM_CALLABLE_FUNC_4(int32_t, CallInterpreter_I32_I32_I32_RetI32, int32_t arg0, int32_t arg1, int32_t arg2, PCODE portableEntrypoint) { - int64_t args[3] = { (int64_t)arg0, (int64_t)arg1, (int64_t)arg2 }; + struct + { + TransitionBlock block; + int64_t args[3]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = callersFramePointer; + transitionBlock.args[0] = (int64_t)arg0; + transitionBlock.args[1] = (int64_t)arg1; + transitionBlock.args[2] = (int64_t)arg2; + static_assert(offsetof(decltype(transitionBlock), args) == sizeof(TransitionBlock), "Args array must be at a TransitionBlock offset from the start of the block"); void * result = NULL; - ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, (int8_t*)args, sizeof(args), (int8_t*)&result); + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); return (int32_t)result; } WASM_CALLABLE_FUNC_5(int32_t, CallInterpreter_I32_I32_I32_I32_RetI32, int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, PCODE portableEntrypoint) { - int64_t args[4] = { (int64_t)arg0, (int64_t)arg1, (int64_t)arg2, (int64_t)arg3 }; + struct + { + TransitionBlock block; + int64_t args[4]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = callersFramePointer; + transitionBlock.args[0] = (int64_t)arg0; + transitionBlock.args[1] = (int64_t)arg1; + transitionBlock.args[2] = (int64_t)arg2; + transitionBlock.args[3] = (int64_t)arg3; + static_assert(offsetof(decltype(transitionBlock), args) == sizeof(TransitionBlock), "Args array must be at a TransitionBlock offset from the start of the block"); void * result = NULL; - ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, (int8_t*)args, sizeof(args), (int8_t*)&result); + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); return (int32_t)result; } WASM_CALLABLE_FUNC_6(int32_t, CallInterpreter_I32_I32_I32_I32_I32_RetI32, int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4, PCODE portableEntrypoint) { - int64_t args[5] = { (int64_t)arg0, (int64_t)arg1, (int64_t)arg2, (int64_t)arg3, (int64_t)arg4 }; + struct + { + TransitionBlock block; + int64_t args[5]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = callersFramePointer; + transitionBlock.args[0] = (int64_t)arg0; + transitionBlock.args[1] = (int64_t)arg1; + transitionBlock.args[2] = (int64_t)arg2; + transitionBlock.args[3] = (int64_t)arg3; + transitionBlock.args[4] = (int64_t)arg4; + static_assert(offsetof(decltype(transitionBlock), args) == sizeof(TransitionBlock), "Args array must be at a TransitionBlock offset from the start of the block"); void * result = NULL; - ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, (int8_t*)args, sizeof(args), (int8_t*)&result); + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); return (int32_t)result; } WASM_CALLABLE_FUNC_7(int32_t, CallInterpreter_I32_I32_I32_I32_I32_I32_RetI32, int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4, int32_t arg5, PCODE portableEntrypoint) { - int64_t args[6] = { (int64_t)arg0, (int64_t)arg1, (int64_t)arg2, (int64_t)arg3, (int64_t)arg4, (int64_t)arg5 }; + struct + { + TransitionBlock block; + int64_t args[6]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = callersFramePointer; + transitionBlock.args[0] = (int64_t)arg0; + transitionBlock.args[1] = (int64_t)arg1; + transitionBlock.args[2] = (int64_t)arg2; + transitionBlock.args[3] = (int64_t)arg3; + transitionBlock.args[4] = (int64_t)arg4; + transitionBlock.args[5] = (int64_t)arg5; + static_assert(offsetof(decltype(transitionBlock), args) == sizeof(TransitionBlock), "Args array must be at a TransitionBlock offset from the start of the block"); void * result = NULL; - ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, (int8_t*)args, sizeof(args), (int8_t*)&result); + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); return (int32_t)result; } WASM_CALLABLE_FUNC_8(int32_t, CallInterpreter_I32_I32_I32_I32_I32_I32_I32_RetI32, int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4, int32_t arg5, int32_t arg6, PCODE portableEntrypoint) { - int64_t args[7] = { (int64_t)arg0, (int64_t)arg1, (int64_t)arg2, (int64_t)arg3, (int64_t)arg4, (int64_t)arg5, (int64_t)arg6 }; + struct + { + TransitionBlock block; + int64_t args[7]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = callersFramePointer; + transitionBlock.args[0] = (int64_t)arg0; + transitionBlock.args[1] = (int64_t)arg1; + transitionBlock.args[2] = (int64_t)arg2; + transitionBlock.args[3] = (int64_t)arg3; + transitionBlock.args[4] = (int64_t)arg4; + transitionBlock.args[5] = (int64_t)arg5; + transitionBlock.args[6] = (int64_t)arg6; + static_assert(offsetof(decltype(transitionBlock), args) == sizeof(TransitionBlock), "Args array must be at a TransitionBlock offset from the start of the block"); void * result = NULL; - ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, (int8_t*)args, sizeof(args), (int8_t*)&result); + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); return (int32_t)result; } WASM_CALLABLE_FUNC_9(int32_t, CallInterpreter_I32_I32_I32_I32_I32_I32_I32_I32_RetI32, int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4, int32_t arg5, int32_t arg6, int32_t arg7, PCODE portableEntrypoint) { - int64_t args[8] = { (int64_t)arg0, (int64_t)arg1, (int64_t)arg2, (int64_t)arg3, (int64_t)arg4, (int64_t)arg5, (int64_t)arg6, (int64_t)arg7 }; + struct + { + TransitionBlock block; + int64_t args[8]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = callersFramePointer; + transitionBlock.args[0] = (int64_t)arg0; + transitionBlock.args[1] = (int64_t)arg1; + transitionBlock.args[2] = (int64_t)arg2; + transitionBlock.args[3] = (int64_t)arg3; + transitionBlock.args[4] = (int64_t)arg4; + transitionBlock.args[5] = (int64_t)arg5; + transitionBlock.args[6] = (int64_t)arg6; + transitionBlock.args[7] = (int64_t)arg7; + static_assert(offsetof(decltype(transitionBlock), args) == sizeof(TransitionBlock), "Args array must be at a TransitionBlock offset from the start of the block"); void * result = NULL; - ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, (int8_t*)args, sizeof(args), (int8_t*)&result); + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); return (int32_t)result; } } diff --git a/src/tasks/WasmAppBuilder/coreclr/InterpToNativeGenerator.cs b/src/tasks/WasmAppBuilder/coreclr/InterpToNativeGenerator.cs index 682fb018ec01ac..51120f0254a18c 100644 --- a/src/tasks/WasmAppBuilder/coreclr/InterpToNativeGenerator.cs +++ b/src/tasks/WasmAppBuilder/coreclr/InterpToNativeGenerator.cs @@ -104,13 +104,13 @@ private static void Emit(StreamWriter w, IEnumerable cookies) var portableEntryPointComma = signature.Length > 1 ? ", " : ""; var portableEntrypointDeclaration = isPortableEntryPointCall ? portableEntryPointComma + "PCODE" : ""; var portableEntrypointParam = isPortableEntryPointCall ? portableEntryPointComma + "pPortableEntryPointContext" : ""; - var portableEntrypointStackDeclaration = isPortableEntryPointCall ? "uintptr_t, " : ""; - var portableEntrypointStackParam = isPortableEntryPointCall ? "emscripten_stack_get_current(), " : ""; + var portableEntrypointStackDeclaration = isPortableEntryPointCall ? "int*, " : ""; + var portableEntrypointStackParam = isPortableEntryPointCall ? "&framePointer, " : ""; w.Write( $$""" - static void {{CallFuncName(args, SignatureMapper.CharToNameType(signature[0]), isPortableEntryPointCall)}}(PCODE pcode, int8_t* pArgs, int8_t* pRet{{(isPortableEntryPointCall ? ", PCODE pPortableEntryPointContext" : "")}}) - { + {{(isPortableEntryPointCall ? "NOINLINE " : "")}}static void {{CallFuncName(args, SignatureMapper.CharToNameType(signature[0]), isPortableEntryPointCall)}}(PCODE pcode, int8_t* pArgs, int8_t* pRet{{(isPortableEntryPointCall ? ", PCODE pPortableEntryPointContext" : "")}}) + {{{(isPortableEntryPointCall ? "\n int framePointer = TERMINATE_R2R_STACK_WALK;" : "")}} {{result.nativeType}} (*fptr)({{portableEntrypointStackDeclaration}}{{args.Join(", ", (p, i) => SignatureMapper.CharToNativeType(p))}}{{portableEntrypointDeclaration}}) = ({{result.nativeType}} (*)({{portableEntrypointStackDeclaration}}{{args.Join(", ", (p, i) => SignatureMapper.CharToNativeType(p))}}{{portableEntrypointDeclaration}}))pcode; {{portabilityAssert}}{{(result.isVoid ? "" : "*" + "((" + result.nativeType + "*)pRet) = ")}}(*fptr)({{portableEntrypointStackParam}}{{args.Join(", ", (p, i) => $"{SignatureMapper.CharToArgType(p)}({i})")}}{{portableEntrypointParam}}); }