From 81c84ec4e2fc7c36c5ebafb3f117697790f12c0c Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Sat, 29 Aug 2026 12:55:25 +0200 Subject: [PATCH 01/11] [wasm] Regenerate the browser P/Invoke table Catches the checked-in table up with main: dotnet/runtime#132274 removed the only managed caller of compressBound(), so the P/Invoke is no longer in the shipping System.IO.Compression, and ZipArchive now reaches the native RNG directly. --- src/coreclr/vm/wasm/browser/callhelpers-pinvoke.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/coreclr/vm/wasm/browser/callhelpers-pinvoke.cpp b/src/coreclr/vm/wasm/browser/callhelpers-pinvoke.cpp index 9dbd7f579c68e6..2248bb068804aa 100644 --- a/src/coreclr/vm/wasm/browser/callhelpers-pinvoke.cpp +++ b/src/coreclr/vm/wasm/browser/callhelpers-pinvoke.cpp @@ -11,7 +11,6 @@ #include extern "C" { - uint32_t CompressionNative_CompressBound (uint32_t); uint32_t CompressionNative_Crc32 (uint32_t, void *, int32_t); int32_t CompressionNative_Deflate (void *, int32_t); int32_t CompressionNative_DeflateEnd (void *); @@ -196,7 +195,6 @@ static const Entry s_libSystem_Globalization_Native [] = { }; static const Entry s_libSystem_IO_Compression_Native [] = { - DllImportEntry(CompressionNative_CompressBound) // System.IO.Compression DllImportEntry(CompressionNative_Crc32) // System.IO.Compression DllImportEntry(CompressionNative_Deflate) // System.IO.Compression, System.Net.WebSockets DllImportEntry(CompressionNative_DeflateEnd) // System.IO.Compression, System.Net.WebSockets @@ -236,7 +234,7 @@ static const Entry s_libSystem_Native [] = { DllImportEntry(SystemNative_Free) // System.Private.CoreLib DllImportEntry(SystemNative_FreeLibrary) // System.Private.CoreLib DllImportEntry(SystemNative_GetCpuUtilization) // System.Private.CoreLib - DllImportEntry(SystemNative_GetCryptographicallySecureRandomBytes) // System.Private.CoreLib, System.Security.Cryptography + DllImportEntry(SystemNative_GetCryptographicallySecureRandomBytes) // System.IO.Compression, System.Private.CoreLib, System.Security.Cryptography DllImportEntry(SystemNative_GetCwd) // System.Private.CoreLib DllImportEntry(SystemNative_GetDefaultSearchOrderPseudoHandle) // System.Private.CoreLib DllImportEntry(SystemNative_GetErrNo) // System.Private.CoreLib @@ -326,7 +324,7 @@ typedef struct PInvokeTable { static PInvokeTable s_PInvokeTables[] = { {"libSystem.Globalization.Native", s_libSystem_Globalization_Native, 34}, - {"libSystem.IO.Compression.Native", s_libSystem_IO_Compression_Native, 10}, + {"libSystem.IO.Compression.Native", s_libSystem_IO_Compression_Native, 9}, {"libSystem.Native", s_libSystem_Native, 94}, {"libSystem.Native.Browser", s_libSystem_Native_Browser, 1}, {"libSystem.Runtime.InteropServices.JavaScript.Native", s_libSystem_Runtime_InteropServices_JavaScript_Native, 6} From d879cbf9019401252131dfd428444f255fb8c479 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Sat, 29 Aug 2026 12:55:58 +0200 Subject: [PATCH 02/11] [wasm] Generate the R2R-to-interpreter thunk table The 'I' thunks that let R2R code call an interpreted method were hand-written in vm/wasm/helpers.cpp, so every new call shape needed a hand-authored thunk. Emit them from the WasmAppBuilder generator instead: 17 hand-written entries become 69 generated plus 1, and helpers.cpp loses ~330 lines. Fix two parameter transpositions that the struct-returning shapes hit as soon as the generator started emitting them. crossgen2 lays the wasm parameters out as (callersStackPointer, [this], [retBuf], args..., portableEntrypoint), reading the buffer back from retBufLocalIndex = 1 + (hasThis ? 1 : 0): - Returning the struct by value makes clang insert its own sret pointer at parameter 0, ahead of callersStackPointer. These thunks are now void with an explicit int8_t* retBuf. - retBuf was placed first unconditionally; for an instance method it follows 'this'. Neither is detectable at run time. The stack pointer, the return buffer, 'this' and every by-reference argument are all i32, so a transposed order still passes call_indirect type checking and instead writes the return value over the caller's frame pointer, surfacing later as an unrelated NullReferenceException or an out-of-bounds trap. The generated table is browser-only; wasi keeps the hand-written thunk. The one remaining hand-written entry is IS16l2ip, whose 'l2' argument (a 16-byte value passed across two i64 parameters) maps one signature token to several C parameters, which the generator cannot express yet. --- src/coreclr/vm/CMakeLists.txt | 8 + .../browser/callhelpers-interp-to-managed.cpp | 324 ++++ .../callhelpers-portable-entrypoints.cpp | 1441 +++++++++++++++++ src/coreclr/vm/wasm/callhelpers.hpp | 5 + src/coreclr/vm/wasm/helpers.cpp | 371 +---- .../WasmAppBuilder/WasmAppBuilder.csproj | 3 +- .../coreclr/InterpToNativeGenerator.cs | 174 +- .../coreclr/ManagedToNativeGenerator.cs | 20 +- .../PortableEntryPointThunkSignature.cs | 53 + 9 files changed, 2050 insertions(+), 349 deletions(-) create mode 100644 src/coreclr/vm/wasm/browser/callhelpers-portable-entrypoints.cpp create mode 100644 src/tasks/WasmAppBuilder/coreclr/PortableEntryPointThunkSignature.cs diff --git a/src/coreclr/vm/CMakeLists.txt b/src/coreclr/vm/CMakeLists.txt index bcc0a0d6e791cc..27444575de3b82 100644 --- a/src/coreclr/vm/CMakeLists.txt +++ b/src/coreclr/vm/CMakeLists.txt @@ -973,6 +973,14 @@ elseif(CLR_CMAKE_TARGET_ARCH_WASM) else() set(WASM_THUNK_SUBDIR browser) endif() + # Unlike the other generated call helpers, the portable entrypoint thunks are not regenerated + # per app, so they belong in the shipped static library rather than in cee_wks_gen. + # Only browser has a generated table today; wasi keeps using the hand-written thunks alone. + if (CLR_CMAKE_TARGET_BROWSER) + list(APPEND VM_SOURCES_WKS_ARCH + ${ARCH_SOURCES_DIR}/${WASM_THUNK_SUBDIR}/callhelpers-portable-entrypoints.cpp + ) + endif(CLR_CMAKE_TARGET_BROWSER) set(VM_SOURCES_WKS_GEN ${ARCH_SOURCES_DIR}/${WASM_THUNK_SUBDIR}/callhelpers-interp-to-managed.cpp ${ARCH_SOURCES_DIR}/${WASM_THUNK_SUBDIR}/callhelpers-reverse.cpp diff --git a/src/coreclr/vm/wasm/browser/callhelpers-interp-to-managed.cpp b/src/coreclr/vm/wasm/browser/callhelpers-interp-to-managed.cpp index dc7af3ffa4ea3a..e4ff4d11cb9403 100644 --- a/src/coreclr/vm/wasm/browser/callhelpers-interp-to-managed.cpp +++ b/src/coreclr/vm/wasm/browser/callhelpers-interp-to-managed.cpp @@ -17,9 +17,76 @@ #define ARG_I64(i) (*(int64_t*)ARG_ADDR(i)) #define ARG_F32(i) (*(float*)ARG_ADDR(i)) #define ARG_F64(i) (*(double*)ARG_ADDR(i)) +typedef struct { char d[1]; } wasm_ret_S1; +typedef struct { char d[8]; } wasm_ret_S8; +typedef struct { char d[12]; } wasm_ret_S12; +typedef struct { char d[16]; } wasm_ret_S16; namespace { + NOINLINE static void CallFunc_This_S12_I32_RetS12_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + wasm_ret_S12 (*fptr)(int*, int32_t, int32_t, int32_t, PCODE) = *(wasm_ret_S12 (**)(int*, int32_t, int32_t, int32_t, PCODE))(pPortableEntryPoint); + *((wasm_ret_S12*)pRet) = (*fptr)(&framePointer, ARG_I32(0), ARG_IND(1), ARG_I32(3), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_I32_S12_I32_RetS12_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + wasm_ret_S12 (*fptr)(int*, int32_t, int32_t, int32_t, int32_t, PCODE) = *(wasm_ret_S12 (**)(int*, int32_t, int32_t, int32_t, int32_t, PCODE))(pPortableEntryPoint); + *((wasm_ret_S12*)pRet) = (*fptr)(&framePointer, ARG_I32(0), ARG_I32(1), ARG_IND(2), ARG_I32(4), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_I32_I32_I32_RetS12_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + wasm_ret_S12 (*fptr)(int*, int32_t, int32_t, int32_t, int32_t, PCODE) = *(wasm_ret_S12 (**)(int*, int32_t, int32_t, int32_t, int32_t, PCODE))(pPortableEntryPoint); + *((wasm_ret_S12*)pRet) = (*fptr)(&framePointer, ARG_I32(0), ARG_I32(1), ARG_I32(2), ARG_I32(3), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_I32_I32_RetS12_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + wasm_ret_S12 (*fptr)(int*, int32_t, int32_t, int32_t, PCODE) = *(wasm_ret_S12 (**)(int*, int32_t, int32_t, int32_t, PCODE))(pPortableEntryPoint); + *((wasm_ret_S12*)pRet) = (*fptr)(&framePointer, ARG_I32(0), ARG_I32(1), ARG_I32(2), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_RetS16_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + wasm_ret_S16 (*fptr)(int*, int32_t, PCODE) = *(wasm_ret_S16 (**)(int*, int32_t, PCODE))(pPortableEntryPoint); + *((wasm_ret_S16*)pRet) = (*fptr)(&framePointer, ARG_I32(0), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_RetS1_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + wasm_ret_S1 (*fptr)(int*, int32_t, PCODE) = *(wasm_ret_S1 (**)(int*, int32_t, PCODE))(pPortableEntryPoint); + *((wasm_ret_S1*)pRet) = (*fptr)(&framePointer, ARG_I32(0), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_I32_I32_I32_RetS8_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + wasm_ret_S8 (*fptr)(int*, int32_t, int32_t, int32_t, int32_t, PCODE) = *(wasm_ret_S8 (**)(int*, int32_t, int32_t, int32_t, int32_t, PCODE))(pPortableEntryPoint); + *((wasm_ret_S8*)pRet) = (*fptr)(&framePointer, ARG_I32(0), ARG_I32(1), ARG_I32(2), ARG_I32(3), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_I32_RetS8_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + wasm_ret_S8 (*fptr)(int*, int32_t, int32_t, PCODE) = *(wasm_ret_S8 (**)(int*, int32_t, int32_t, PCODE))(pPortableEntryPoint); + *((wasm_ret_S8*)pRet) = (*fptr)(&framePointer, ARG_I32(0), ARG_I32(1), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_RetS8_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + wasm_ret_S8 (*fptr)(int*, int32_t, PCODE) = *(wasm_ret_S8 (**)(int*, int32_t, PCODE))(pPortableEntryPoint); + *((wasm_ret_S8*)pRet) = (*fptr)(&framePointer, ARG_I32(0), pPortableEntryPoint); + } + NOINLINE static void CallFunc_F64_F64_F64_RetF64_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) { alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; @@ -166,6 +233,97 @@ namespace *((int32_t*)pRet) = (*fptr)(ARG_IND(0), ARG_I32(1), ARG_I32(2), ARG_I32(3), ARG_I32(4), ARG_I32(5)); } + NOINLINE static void CallFunc_S8_RetI32_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + int32_t (*fptr)(int*, int32_t, PCODE) = *(int32_t (**)(int*, int32_t, PCODE))(pPortableEntryPoint); + *((int32_t*)pRet) = (*fptr)(&framePointer, ARG_IND(0), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_S12_I32_RetI32_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) 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))(pPortableEntryPoint); + *((int32_t*)pRet) = (*fptr)(&framePointer, ARG_I32(0), ARG_IND(1), ARG_I32(3), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_S12_RetI32_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + int32_t (*fptr)(int*, int32_t, int32_t, PCODE) = *(int32_t (**)(int*, int32_t, int32_t, PCODE))(pPortableEntryPoint); + *((int32_t*)pRet) = (*fptr)(&framePointer, ARG_I32(0), ARG_IND(1), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_S8_S8_RetI32_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) 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))(pPortableEntryPoint); + *((int32_t*)pRet) = (*fptr)(&framePointer, ARG_I32(0), ARG_IND(1), ARG_IND(2), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_S8_I32_S8_I32_RetI32_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) 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))(pPortableEntryPoint); + *((int32_t*)pRet) = (*fptr)(&framePointer, ARG_I32(0), ARG_IND(1), ARG_I32(2), ARG_IND(3), ARG_I32(4), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_S8_I32_I32_I32_RetI32_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) 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))(pPortableEntryPoint); + *((int32_t*)pRet) = (*fptr)(&framePointer, ARG_I32(0), ARG_IND(1), ARG_I32(2), ARG_I32(3), ARG_I32(4), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_S8_I32_RetI32_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) 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))(pPortableEntryPoint); + *((int32_t*)pRet) = (*fptr)(&framePointer, ARG_I32(0), ARG_IND(1), ARG_I32(2), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_S8_RetI32_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + int32_t (*fptr)(int*, int32_t, int32_t, PCODE) = *(int32_t (**)(int*, int32_t, int32_t, PCODE))(pPortableEntryPoint); + *((int32_t*)pRet) = (*fptr)(&framePointer, ARG_I32(0), ARG_IND(1), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_I32_S12_RetI32_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) 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))(pPortableEntryPoint); + *((int32_t*)pRet) = (*fptr)(&framePointer, ARG_I32(0), ARG_I32(1), ARG_IND(2), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_I32_S8_RetI32_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) 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))(pPortableEntryPoint); + *((int32_t*)pRet) = (*fptr)(&framePointer, ARG_I32(0), ARG_I32(1), ARG_IND(2), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_I32_I32_I32_I32_RetI32_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) 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))(pPortableEntryPoint); + *((int32_t*)pRet) = (*fptr)(&framePointer, ARG_I32(0), ARG_I32(1), ARG_I32(2), ARG_I32(3), ARG_I32(4), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_I32_I32_I32_RetI32_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) 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))(pPortableEntryPoint); + *((int32_t*)pRet) = (*fptr)(&framePointer, ARG_I32(0), ARG_I32(1), ARG_I32(2), ARG_I32(3), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_I32_I32_RetI32_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) 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))(pPortableEntryPoint); + *((int32_t*)pRet) = (*fptr)(&framePointer, ARG_I32(0), ARG_I32(1), ARG_I32(2), pPortableEntryPoint); + } + NOINLINE static void CallFunc_This_I32_RetI32_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) { alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; @@ -180,6 +338,13 @@ namespace *((int32_t*)pRet) = (*fptr)(&framePointer, ARG_I32(0), pPortableEntryPoint); } + NOINLINE static void CallFunc_F64_RetI32_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + int32_t (*fptr)(int*, double, PCODE) = *(int32_t (**)(int*, double, PCODE))(pPortableEntryPoint); + *((int32_t*)pRet) = (*fptr)(&framePointer, ARG_F64(0), pPortableEntryPoint); + } + static void CallFunc_I32_RetI32(PCODE pcode, int8_t* pArgs, int8_t* pRet) { int32_t (*fptr)(int32_t) = (int32_t (*)(int32_t))pcode; @@ -198,6 +363,13 @@ namespace *((int32_t*)pRet) = (*fptr)(ARG_I32(0), ARG_IND(1), ARG_I32(2), ARG_I32(3), ARG_I32(4)); } + NOINLINE static void CallFunc_I32_S8_RetI32_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + int32_t (*fptr)(int*, int32_t, int32_t, PCODE) = *(int32_t (**)(int*, int32_t, int32_t, PCODE))(pPortableEntryPoint); + *((int32_t*)pRet) = (*fptr)(&framePointer, ARG_I32(0), ARG_IND(1), pPortableEntryPoint); + } + static void CallFunc_I32_I32_RetI32(PCODE pcode, int8_t* pArgs, int8_t* pRet) { int32_t (*fptr)(int32_t, int32_t) = (int32_t (*)(int32_t, int32_t))pcode; @@ -258,6 +430,20 @@ 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)); } + NOINLINE static void CallFunc_I32_I32_I32_I32_I32_I32_I32_I32_RetI32_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + int32_t (*fptr)(int*, int32_t, int32_t, 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, int32_t, int32_t, PCODE))(pPortableEntryPoint); + *((int32_t*)pRet) = (*fptr)(&framePointer, ARG_I32(0), ARG_I32(1), ARG_I32(2), ARG_I32(3), ARG_I32(4), ARG_I32(5), ARG_I32(6), ARG_I32(7), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_I32_I32_I32_I32_I32_I32_I32_RetI32_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + int32_t (*fptr)(int*, int32_t, 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, int32_t, PCODE))(pPortableEntryPoint); + *((int32_t*)pRet) = (*fptr)(&framePointer, ARG_I32(0), ARG_I32(1), ARG_I32(2), ARG_I32(3), ARG_I32(4), ARG_I32(5), ARG_I32(6), pPortableEntryPoint); + } + NOINLINE static void CallFunc_I32_I32_I32_I32_I32_I32_RetI32_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) { alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; @@ -379,6 +565,13 @@ namespace *((int64_t*)pRet) = (*fptr)(); } + NOINLINE static void CallFunc_F64_RetI64_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + int64_t (*fptr)(int*, double, PCODE) = *(int64_t (**)(int*, double, PCODE))(pPortableEntryPoint); + *((int64_t*)pRet) = (*fptr)(&framePointer, ARG_F64(0), pPortableEntryPoint); + } + static void CallFunc_I32_RetI64(PCODE pcode, int8_t* pArgs, int8_t* pRet) { int64_t (*fptr)(int32_t) = (int64_t (*)(int32_t))pcode; @@ -510,6 +703,90 @@ namespace (*fptr)(ARG_IND(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)); } + NOINLINE static void CallFunc_This_S12_RetVoid_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + void (*fptr)(int*, int32_t, int32_t, PCODE) = *(void (**)(int*, int32_t, int32_t, PCODE))(pPortableEntryPoint); + (*fptr)(&framePointer, ARG_I32(0), ARG_IND(1), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_S32_RetVoid_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + void (*fptr)(int*, int32_t, int32_t, PCODE) = *(void (**)(int*, int32_t, int32_t, PCODE))(pPortableEntryPoint); + (*fptr)(&framePointer, ARG_I32(0), ARG_IND(1), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_S8_S8_RetVoid_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) 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))(pPortableEntryPoint); + (*fptr)(&framePointer, ARG_I32(0), ARG_IND(1), ARG_IND(2), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_S8_I32_RetVoid_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) 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))(pPortableEntryPoint); + (*fptr)(&framePointer, ARG_I32(0), ARG_IND(1), ARG_I32(2), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_S8_RetVoid_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + void (*fptr)(int*, int32_t, int32_t, PCODE) = *(void (**)(int*, int32_t, int32_t, PCODE))(pPortableEntryPoint); + (*fptr)(&framePointer, ARG_I32(0), ARG_IND(1), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_I32_S8_RetVoid_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) 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))(pPortableEntryPoint); + (*fptr)(&framePointer, ARG_I32(0), ARG_I32(1), ARG_IND(2), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_I32_I32_S8_S8_I32_I32_RetVoid_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + void (*fptr)(int*, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, PCODE) = *(void (**)(int*, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, PCODE))(pPortableEntryPoint); + (*fptr)(&framePointer, ARG_I32(0), ARG_I32(1), ARG_I32(2), ARG_IND(3), ARG_IND(4), ARG_I32(5), ARG_I32(6), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_I32_I32_I32_I32_I32_RetVoid_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + void (*fptr)(int*, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, PCODE) = *(void (**)(int*, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, PCODE))(pPortableEntryPoint); + (*fptr)(&framePointer, ARG_I32(0), ARG_I32(1), ARG_I32(2), ARG_I32(3), ARG_I32(4), ARG_I32(5), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_I32_I32_I32_I32_RetVoid_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + void (*fptr)(int*, int32_t, int32_t, int32_t, int32_t, int32_t, PCODE) = *(void (**)(int*, int32_t, int32_t, int32_t, int32_t, int32_t, PCODE))(pPortableEntryPoint); + (*fptr)(&framePointer, ARG_I32(0), ARG_I32(1), ARG_I32(2), ARG_I32(3), ARG_I32(4), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_I32_I32_I32_RetVoid_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + void (*fptr)(int*, int32_t, int32_t, int32_t, int32_t, PCODE) = *(void (**)(int*, int32_t, int32_t, int32_t, int32_t, PCODE))(pPortableEntryPoint); + (*fptr)(&framePointer, ARG_I32(0), ARG_I32(1), ARG_I32(2), ARG_I32(3), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_I32_I32_RetVoid_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) 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))(pPortableEntryPoint); + (*fptr)(&framePointer, ARG_I32(0), ARG_I32(1), ARG_I32(2), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_I32_RetVoid_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + void (*fptr)(int*, int32_t, int32_t, PCODE) = *(void (**)(int*, int32_t, int32_t, PCODE))(pPortableEntryPoint); + (*fptr)(&framePointer, ARG_I32(0), ARG_I32(1), pPortableEntryPoint); + } + NOINLINE static void CallFunc_This_RetVoid_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) { alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; @@ -627,6 +904,13 @@ namespace (*fptr)(ARG_I32(0), ARG_I32(1), ARG_I32(2), ARG_I32(3), ARG_I64(4)); } + NOINLINE static void CallFunc_I32_I32_I32_I32_RetVoid_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + void (*fptr)(int*, int32_t, int32_t, int32_t, int32_t, PCODE) = *(void (**)(int*, int32_t, int32_t, int32_t, int32_t, PCODE))(pPortableEntryPoint); + (*fptr)(&framePointer, ARG_I32(0), ARG_I32(1), ARG_I32(2), ARG_I32(3), pPortableEntryPoint); + } + NOINLINE static void CallFunc_I32_I32_I32_RetVoid_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) { alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; @@ -663,6 +947,15 @@ namespace } const StringToWasmSigThunk g_wasmThunks[] = { + { "MS12TS12ip", (void*)&CallFunc_This_S12_I32_RetS12_PE }, + { "MS12TiS12ip", (void*)&CallFunc_This_I32_S12_I32_RetS12_PE }, + { "MS12Tiiip", (void*)&CallFunc_This_I32_I32_I32_RetS12_PE }, + { "MS12Tiip", (void*)&CallFunc_This_I32_I32_RetS12_PE }, + { "MS16Tp", (void*)&CallFunc_This_RetS16_PE }, + { "MS1Tp", (void*)&CallFunc_This_RetS1_PE }, + { "MS8Tiiip", (void*)&CallFunc_This_I32_I32_I32_RetS8_PE }, + { "MS8Tip", (void*)&CallFunc_This_I32_RetS8_PE }, + { "MS8Tp", (void*)&CallFunc_This_RetS8_PE }, { "Mddddp", (void*)&CallFunc_F64_F64_F64_RetF64_PE }, { "Mdddp", (void*)&CallFunc_F64_F64_RetF64_PE }, { "Mddip", (void*)&CallFunc_F64_I32_RetF64_PE }, @@ -686,11 +979,26 @@ const StringToWasmSigThunk g_wasmThunks[] = { { "MiS8iii", (void*)&CallFunc_S8_I32_I32_I32_RetI32 }, { "MiS8iiii", (void*)&CallFunc_S8_I32_I32_I32_I32_RetI32 }, { "MiS8iiiii", (void*)&CallFunc_S8_I32_I32_I32_I32_I32_RetI32 }, + { "MiS8p", (void*)&CallFunc_S8_RetI32_PE }, + { "MiTS12ip", (void*)&CallFunc_This_S12_I32_RetI32_PE }, + { "MiTS12p", (void*)&CallFunc_This_S12_RetI32_PE }, + { "MiTS8S8p", (void*)&CallFunc_This_S8_S8_RetI32_PE }, + { "MiTS8iS8ip", (void*)&CallFunc_This_S8_I32_S8_I32_RetI32_PE }, + { "MiTS8iiip", (void*)&CallFunc_This_S8_I32_I32_I32_RetI32_PE }, + { "MiTS8ip", (void*)&CallFunc_This_S8_I32_RetI32_PE }, + { "MiTS8p", (void*)&CallFunc_This_S8_RetI32_PE }, + { "MiTiS12p", (void*)&CallFunc_This_I32_S12_RetI32_PE }, + { "MiTiS8p", (void*)&CallFunc_This_I32_S8_RetI32_PE }, + { "MiTiiiip", (void*)&CallFunc_This_I32_I32_I32_I32_RetI32_PE }, + { "MiTiiip", (void*)&CallFunc_This_I32_I32_I32_RetI32_PE }, + { "MiTiip", (void*)&CallFunc_This_I32_I32_RetI32_PE }, { "MiTip", (void*)&CallFunc_This_I32_RetI32_PE }, { "MiTp", (void*)&CallFunc_This_RetI32_PE }, + { "Midp", (void*)&CallFunc_F64_RetI32_PE }, { "Mii", (void*)&CallFunc_I32_RetI32 }, { "MiiS8i", (void*)&CallFunc_I32_S8_I32_RetI32 }, { "MiiS8iii", (void*)&CallFunc_I32_S8_I32_I32_I32_RetI32 }, + { "MiiS8p", (void*)&CallFunc_I32_S8_RetI32_PE }, { "Miii", (void*)&CallFunc_I32_I32_RetI32 }, { "MiiiS8iiS8", (void*)&CallFunc_I32_I32_S8_I32_I32_S8_RetI32 }, { "MiiiS8iiii", (void*)&CallFunc_I32_I32_S8_I32_I32_I32_I32_RetI32 }, @@ -701,6 +1009,8 @@ const StringToWasmSigThunk g_wasmThunks[] = { { "Miiiiiii", (void*)&CallFunc_I32_I32_I32_I32_I32_I32_RetI32 }, { "Miiiiiiii", (void*)&CallFunc_I32_I32_I32_I32_I32_I32_I32_RetI32 }, { "Miiiiiiiiiiiiiii", (void*)&CallFunc_I32_I32_I32_I32_I32_I32_I32_I32_I32_I32_I32_I32_I32_I32_RetI32 }, + { "Miiiiiiiiip", (void*)&CallFunc_I32_I32_I32_I32_I32_I32_I32_I32_RetI32_PE }, + { "Miiiiiiiip", (void*)&CallFunc_I32_I32_I32_I32_I32_I32_I32_RetI32_PE }, { "Miiiiiiip", (void*)&CallFunc_I32_I32_I32_I32_I32_I32_RetI32_PE }, { "Miiiiiip", (void*)&CallFunc_I32_I32_I32_I32_I32_RetI32_PE }, { "Miiiiip", (void*)&CallFunc_I32_I32_I32_I32_RetI32_PE }, @@ -720,6 +1030,7 @@ const StringToWasmSigThunk g_wasmThunks[] = { { "Milili", (void*)&CallFunc_I64_I32_I64_I32_RetI32 }, { "Mip", (void*)&CallFunc_Void_RetI32_PE }, { "Ml", (void*)&CallFunc_Void_RetI64 }, + { "Mldp", (void*)&CallFunc_F64_RetI64_PE }, { "Mli", (void*)&CallFunc_I32_RetI64 }, { "Mliii", (void*)&CallFunc_I32_I32_I32_RetI64 }, { "Mliiiii", (void*)&CallFunc_I32_I32_I32_I32_I32_RetI64 }, @@ -741,6 +1052,18 @@ const StringToWasmSigThunk g_wasmThunks[] = { { "MvS8iiiii", (void*)&CallFunc_S8_I32_I32_I32_I32_I32_RetVoid }, { "MvS8iiiiii", (void*)&CallFunc_S8_I32_I32_I32_I32_I32_I32_RetVoid }, { "MvS8iiiiiiiiiii", (void*)&CallFunc_S8_I32_I32_I32_I32_I32_I32_I32_I32_I32_I32_I32_RetVoid }, + { "MvTS12p", (void*)&CallFunc_This_S12_RetVoid_PE }, + { "MvTS32p", (void*)&CallFunc_This_S32_RetVoid_PE }, + { "MvTS8S8p", (void*)&CallFunc_This_S8_S8_RetVoid_PE }, + { "MvTS8ip", (void*)&CallFunc_This_S8_I32_RetVoid_PE }, + { "MvTS8p", (void*)&CallFunc_This_S8_RetVoid_PE }, + { "MvTiS8p", (void*)&CallFunc_This_I32_S8_RetVoid_PE }, + { "MvTiiS8S8iip", (void*)&CallFunc_This_I32_I32_S8_S8_I32_I32_RetVoid_PE }, + { "MvTiiiiip", (void*)&CallFunc_This_I32_I32_I32_I32_I32_RetVoid_PE }, + { "MvTiiiip", (void*)&CallFunc_This_I32_I32_I32_I32_RetVoid_PE }, + { "MvTiiip", (void*)&CallFunc_This_I32_I32_I32_RetVoid_PE }, + { "MvTiip", (void*)&CallFunc_This_I32_I32_RetVoid_PE }, + { "MvTip", (void*)&CallFunc_This_I32_RetVoid_PE }, { "MvTp", (void*)&CallFunc_This_RetVoid_PE }, { "Mvdddddddddii", (void*)&CallFunc_F64_F64_F64_F64_F64_F64_F64_F64_F64_I32_I32_RetVoid }, { "Mvdi", (void*)&CallFunc_F64_I32_RetVoid }, @@ -760,6 +1083,7 @@ const StringToWasmSigThunk g_wasmThunks[] = { { "Mviiiii", (void*)&CallFunc_I32_I32_I32_I32_I32_RetVoid }, { "Mviiiiii", (void*)&CallFunc_I32_I32_I32_I32_I32_I32_RetVoid }, { "Mviiiil", (void*)&CallFunc_I32_I32_I32_I32_I64_RetVoid }, + { "Mviiiip", (void*)&CallFunc_I32_I32_I32_I32_RetVoid_PE }, { "Mviiip", (void*)&CallFunc_I32_I32_I32_RetVoid_PE }, { "Mviip", (void*)&CallFunc_I32_I32_RetVoid_PE }, { "Mvip", (void*)&CallFunc_I32_RetVoid_PE }, diff --git a/src/coreclr/vm/wasm/browser/callhelpers-portable-entrypoints.cpp b/src/coreclr/vm/wasm/browser/callhelpers-portable-entrypoints.cpp new file mode 100644 index 00000000000000..cc2cf0af600c8e --- /dev/null +++ b/src/coreclr/vm/wasm/browser/callhelpers-portable-entrypoints.cpp @@ -0,0 +1,1441 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// + +// +// GENERATED FILE, DON'T EDIT +// Generated by coreclr InterpToNativeGenerator +// + +#include +#include +#include "callhelpers.hpp" + +void ExecuteInterpretedMethodWithArgs_PortableEntryPoint(PCODE portableEntrypoint, TransitionBlock* block, size_t argsSize, int8_t* retBuff); + +namespace +{ + FCDECL4(void, CallInterpreter_This_S12_I32_RetS12, int32_t, int8_t*, int8_t*, int32_t); + WASM_CALLABLE_FUNC_5(void, CallInterpreter_This_S12_I32_RetS12, int32_t arg0, int8_t* retBuf, int8_t* arg1, int32_t arg2, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[4]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + transitionBlock.args[0] = (int64_t)arg0; + memcpy(&transitionBlock.args[1], arg1, 12); + transitionBlock.args[3] = (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"); + + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), retBuf); + return; + } + + FCDECL5(void, CallInterpreter_This_I32_S12_I32_RetS12, int32_t, int8_t*, int32_t, int8_t*, int32_t); + WASM_CALLABLE_FUNC_6(void, CallInterpreter_This_I32_S12_I32_RetS12, int32_t arg0, int8_t* retBuf, int32_t arg1, int8_t* arg2, int32_t arg3, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[5]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + transitionBlock.args[0] = (int64_t)arg0; + transitionBlock.args[1] = (int64_t)arg1; + memcpy(&transitionBlock.args[2], arg2, 12); + transitionBlock.args[4] = (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"); + + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), retBuf); + return; + } + + FCDECL5(void, CallInterpreter_This_I32_I32_I32_RetS12, int32_t, int8_t*, int32_t, int32_t, int32_t); + WASM_CALLABLE_FUNC_6(void, CallInterpreter_This_I32_I32_I32_RetS12, int32_t arg0, int8_t* retBuf, int32_t arg1, int32_t arg2, int32_t arg3, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[4]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), retBuf); + return; + } + + FCDECL4(void, CallInterpreter_This_I32_I32_RetS12, int32_t, int8_t*, int32_t, int32_t); + WASM_CALLABLE_FUNC_5(void, CallInterpreter_This_I32_I32_RetS12, int32_t arg0, int8_t* retBuf, int32_t arg1, int32_t arg2, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[3]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), retBuf); + return; + } + + FCDECL2(void, CallInterpreter_This_RetS16, int32_t, int8_t*); + WASM_CALLABLE_FUNC_3(void, CallInterpreter_This_RetS16, int32_t arg0, int8_t* retBuf, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[1]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), retBuf); + return; + } + + FCDECL2(void, CallInterpreter_This_RetS1, int32_t, int8_t*); + WASM_CALLABLE_FUNC_3(void, CallInterpreter_This_RetS1, int32_t arg0, int8_t* retBuf, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[1]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), retBuf); + return; + } + + FCDECL5(void, CallInterpreter_This_I32_I32_I32_RetS8, int32_t, int8_t*, int32_t, int32_t, int32_t); + WASM_CALLABLE_FUNC_6(void, CallInterpreter_This_I32_I32_I32_RetS8, int32_t arg0, int8_t* retBuf, int32_t arg1, int32_t arg2, int32_t arg3, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[4]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), retBuf); + return; + } + + FCDECL3(void, CallInterpreter_This_I32_RetS8, int32_t, int8_t*, int32_t); + WASM_CALLABLE_FUNC_4(void, CallInterpreter_This_I32_RetS8, int32_t arg0, int8_t* retBuf, int32_t arg1, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[2]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), retBuf); + return; + } + + FCDECL2(void, CallInterpreter_This_RetS8, int32_t, int8_t*); + WASM_CALLABLE_FUNC_3(void, CallInterpreter_This_RetS8, int32_t arg0, int8_t* retBuf, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[1]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), retBuf); + return; + } + + FCDECL3(double, CallInterpreter_F64_F64_F64_RetF64, double, double, double); + WASM_CALLABLE_FUNC_4(double, CallInterpreter_F64_F64_F64_RetF64, double arg0, double arg1, double arg2, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[3]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + double result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL2(double, CallInterpreter_F64_F64_RetF64, double, double); + WASM_CALLABLE_FUNC_3(double, CallInterpreter_F64_F64_RetF64, double arg0, double arg1, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[2]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + double result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL2(double, CallInterpreter_F64_I32_RetF64, double, int32_t); + WASM_CALLABLE_FUNC_3(double, CallInterpreter_F64_I32_RetF64, double arg0, int32_t arg1, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[2]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + double result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL1(double, CallInterpreter_F64_RetF64, double); + WASM_CALLABLE_FUNC_2(double, CallInterpreter_F64_RetF64, double arg0, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[1]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + double result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL3(float, CallInterpreter_F32_F32_F32_RetF32, float, float, float); + WASM_CALLABLE_FUNC_4(float, CallInterpreter_F32_F32_F32_RetF32, float arg0, float arg1, float arg2, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[3]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + float result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL2(float, CallInterpreter_F32_F32_RetF32, float, float); + WASM_CALLABLE_FUNC_3(float, CallInterpreter_F32_F32_RetF32, float arg0, float arg1, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[2]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + float result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL2(float, CallInterpreter_F32_I32_RetF32, float, int32_t); + WASM_CALLABLE_FUNC_3(float, CallInterpreter_F32_I32_RetF32, float arg0, int32_t arg1, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[2]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + float result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL1(float, CallInterpreter_F32_RetF32, float); + WASM_CALLABLE_FUNC_2(float, CallInterpreter_F32_RetF32, float arg0, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[1]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + float result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL1(int32_t, CallInterpreter_S8_RetI32, int8_t*); + WASM_CALLABLE_FUNC_2(int32_t, CallInterpreter_S8_RetI32, int8_t* arg0, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[1]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + memcpy(&transitionBlock.args[0], arg0, 8); + static_assert(offsetof(decltype(transitionBlock), args) == sizeof(TransitionBlock), "Args array must be at a TransitionBlock offset from the start of the block"); + + int32_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL3(int32_t, CallInterpreter_This_S12_I32_RetI32, int32_t, int8_t*, int32_t); + WASM_CALLABLE_FUNC_4(int32_t, CallInterpreter_This_S12_I32_RetI32, int32_t arg0, int8_t* arg1, int32_t arg2, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[4]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + transitionBlock.args[0] = (int64_t)arg0; + memcpy(&transitionBlock.args[1], arg1, 12); + transitionBlock.args[3] = (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"); + + int32_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL2(int32_t, CallInterpreter_This_S12_RetI32, int32_t, int8_t*); + WASM_CALLABLE_FUNC_3(int32_t, CallInterpreter_This_S12_RetI32, int32_t arg0, int8_t* arg1, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[3]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + transitionBlock.args[0] = (int64_t)arg0; + memcpy(&transitionBlock.args[1], arg1, 12); + static_assert(offsetof(decltype(transitionBlock), args) == sizeof(TransitionBlock), "Args array must be at a TransitionBlock offset from the start of the block"); + + int32_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL3(int32_t, CallInterpreter_This_S8_S8_RetI32, int32_t, int8_t*, int8_t*); + WASM_CALLABLE_FUNC_4(int32_t, CallInterpreter_This_S8_S8_RetI32, int32_t arg0, int8_t* arg1, int8_t* arg2, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[3]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + transitionBlock.args[0] = (int64_t)arg0; + memcpy(&transitionBlock.args[1], arg1, 8); + memcpy(&transitionBlock.args[2], arg2, 8); + static_assert(offsetof(decltype(transitionBlock), args) == sizeof(TransitionBlock), "Args array must be at a TransitionBlock offset from the start of the block"); + + int32_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL5(int32_t, CallInterpreter_This_S8_I32_S8_I32_RetI32, int32_t, int8_t*, int32_t, int8_t*, int32_t); + WASM_CALLABLE_FUNC_6(int32_t, CallInterpreter_This_S8_I32_S8_I32_RetI32, int32_t arg0, int8_t* arg1, int32_t arg2, int8_t* arg3, int32_t arg4, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[5]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + transitionBlock.args[0] = (int64_t)arg0; + memcpy(&transitionBlock.args[1], arg1, 8); + transitionBlock.args[2] = (int64_t)arg2; + memcpy(&transitionBlock.args[3], arg3, 8); + 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"); + + int32_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL5(int32_t, CallInterpreter_This_S8_I32_I32_I32_RetI32, int32_t, int8_t*, int32_t, int32_t, int32_t); + WASM_CALLABLE_FUNC_6(int32_t, CallInterpreter_This_S8_I32_I32_I32_RetI32, int32_t arg0, int8_t* arg1, int32_t arg2, int32_t arg3, int32_t arg4, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[5]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + transitionBlock.args[0] = (int64_t)arg0; + memcpy(&transitionBlock.args[1], arg1, 8); + 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"); + + int32_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL3(int32_t, CallInterpreter_This_S8_I32_RetI32, int32_t, int8_t*, int32_t); + WASM_CALLABLE_FUNC_4(int32_t, CallInterpreter_This_S8_I32_RetI32, int32_t arg0, int8_t* arg1, int32_t arg2, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[3]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + transitionBlock.args[0] = (int64_t)arg0; + memcpy(&transitionBlock.args[1], arg1, 8); + 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"); + + int32_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL2(int32_t, CallInterpreter_This_S8_RetI32, int32_t, int8_t*); + WASM_CALLABLE_FUNC_3(int32_t, CallInterpreter_This_S8_RetI32, int32_t arg0, int8_t* arg1, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[2]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + transitionBlock.args[0] = (int64_t)arg0; + memcpy(&transitionBlock.args[1], arg1, 8); + static_assert(offsetof(decltype(transitionBlock), args) == sizeof(TransitionBlock), "Args array must be at a TransitionBlock offset from the start of the block"); + + int32_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL3(int32_t, CallInterpreter_This_I32_S12_RetI32, int32_t, int32_t, int8_t*); + WASM_CALLABLE_FUNC_4(int32_t, CallInterpreter_This_I32_S12_RetI32, int32_t arg0, int32_t arg1, int8_t* arg2, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[4]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + transitionBlock.args[0] = (int64_t)arg0; + transitionBlock.args[1] = (int64_t)arg1; + memcpy(&transitionBlock.args[2], arg2, 12); + static_assert(offsetof(decltype(transitionBlock), args) == sizeof(TransitionBlock), "Args array must be at a TransitionBlock offset from the start of the block"); + + int32_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL3(int32_t, CallInterpreter_This_I32_S8_RetI32, int32_t, int32_t, int8_t*); + WASM_CALLABLE_FUNC_4(int32_t, CallInterpreter_This_I32_S8_RetI32, int32_t arg0, int32_t arg1, int8_t* arg2, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[3]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + transitionBlock.args[0] = (int64_t)arg0; + transitionBlock.args[1] = (int64_t)arg1; + memcpy(&transitionBlock.args[2], arg2, 8); + static_assert(offsetof(decltype(transitionBlock), args) == sizeof(TransitionBlock), "Args array must be at a TransitionBlock offset from the start of the block"); + + int32_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL5(int32_t, CallInterpreter_This_I32_I32_I32_I32_RetI32, int32_t, int32_t, int32_t, int32_t, int32_t); + WASM_CALLABLE_FUNC_6(int32_t, CallInterpreter_This_I32_I32_I32_I32_RetI32, int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[5]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + int32_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL4(int32_t, CallInterpreter_This_I32_I32_I32_RetI32, int32_t, int32_t, int32_t, int32_t); + WASM_CALLABLE_FUNC_5(int32_t, CallInterpreter_This_I32_I32_I32_RetI32, int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[4]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + int32_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL3(int32_t, CallInterpreter_This_I32_I32_RetI32, int32_t, int32_t, int32_t); + WASM_CALLABLE_FUNC_4(int32_t, CallInterpreter_This_I32_I32_RetI32, int32_t arg0, int32_t arg1, int32_t arg2, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[3]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + int32_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL2(int32_t, CallInterpreter_This_I32_RetI32, int32_t, int32_t); + WASM_CALLABLE_FUNC_3(int32_t, CallInterpreter_This_I32_RetI32, int32_t arg0, int32_t arg1, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[2]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + int32_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL1(int32_t, CallInterpreter_This_RetI32, int32_t); + WASM_CALLABLE_FUNC_2(int32_t, CallInterpreter_This_RetI32, int32_t arg0, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[1]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + int32_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL1(int32_t, CallInterpreter_F64_RetI32, double); + WASM_CALLABLE_FUNC_2(int32_t, CallInterpreter_F64_RetI32, double arg0, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[1]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + int32_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL2(int32_t, CallInterpreter_I32_S8_RetI32, int32_t, int8_t*); + WASM_CALLABLE_FUNC_3(int32_t, CallInterpreter_I32_S8_RetI32, int32_t arg0, int8_t* arg1, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[2]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + transitionBlock.args[0] = (int64_t)arg0; + memcpy(&transitionBlock.args[1], arg1, 8); + static_assert(offsetof(decltype(transitionBlock), args) == sizeof(TransitionBlock), "Args array must be at a TransitionBlock offset from the start of the block"); + + int32_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL8(int32_t, CallInterpreter_I32_I32_I32_I32_I32_I32_I32_I32_RetI32, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t); + 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) + { + struct + { + TransitionBlock block; + int64_t args[8]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + int32_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL7(int32_t, CallInterpreter_I32_I32_I32_I32_I32_I32_I32_RetI32, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t); + 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) + { + struct + { + TransitionBlock block; + int64_t args[7]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + int32_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL6(int32_t, CallInterpreter_I32_I32_I32_I32_I32_I32_RetI32, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t); + 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) + { + struct + { + TransitionBlock block; + int64_t args[6]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + int32_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL5(int32_t, CallInterpreter_I32_I32_I32_I32_I32_RetI32, int32_t, int32_t, int32_t, int32_t, int32_t); + 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) + { + struct + { + TransitionBlock block; + int64_t args[5]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + int32_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL4(int32_t, CallInterpreter_I32_I32_I32_I32_RetI32, int32_t, int32_t, int32_t, int32_t); + 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) + { + struct + { + TransitionBlock block; + int64_t args[4]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + int32_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL3(int32_t, CallInterpreter_I32_I32_I32_RetI32, int32_t, int32_t, int32_t); + WASM_CALLABLE_FUNC_4(int32_t, CallInterpreter_I32_I32_I32_RetI32, int32_t arg0, int32_t arg1, int32_t arg2, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[3]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + int32_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL2(int32_t, CallInterpreter_I32_I32_RetI32, int32_t, int32_t); + WASM_CALLABLE_FUNC_3(int32_t, CallInterpreter_I32_I32_RetI32, int32_t arg0, int32_t arg1, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[2]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + int32_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL1(int32_t, CallInterpreter_I32_RetI32, int32_t); + WASM_CALLABLE_FUNC_2(int32_t, CallInterpreter_I32_RetI32, int32_t arg0, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[1]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + int32_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL0(int32_t, CallInterpreter_Void_RetI32); + WASM_CALLABLE_FUNC_1(int32_t, CallInterpreter_Void_RetI32, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + + int32_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, 0, (int8_t*)&result); + return result; + } + + FCDECL1(int64_t, CallInterpreter_F64_RetI64, double); + WASM_CALLABLE_FUNC_2(int64_t, CallInterpreter_F64_RetI64, double arg0, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[1]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + int64_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL3(int64_t, CallInterpreter_I32_I64_I64_RetI64, int32_t, int64_t, int64_t); + WASM_CALLABLE_FUNC_4(int64_t, CallInterpreter_I32_I64_I64_RetI64, int32_t arg0, int64_t arg1, int64_t arg2, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[3]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + int64_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL2(int64_t, CallInterpreter_I32_I64_RetI64, int32_t, int64_t); + WASM_CALLABLE_FUNC_3(int64_t, CallInterpreter_I32_I64_RetI64, int32_t arg0, int64_t arg1, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[2]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + int64_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL1(int64_t, CallInterpreter_I32_RetI64, int32_t); + WASM_CALLABLE_FUNC_2(int64_t, CallInterpreter_I32_RetI64, int32_t arg0, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[1]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + int64_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL2(int64_t, CallInterpreter_I64_I64_RetI64, int64_t, int64_t); + WASM_CALLABLE_FUNC_3(int64_t, CallInterpreter_I64_I64_RetI64, int64_t arg0, int64_t arg1, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[2]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + int64_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL0(int64_t, CallInterpreter_Void_RetI64); + WASM_CALLABLE_FUNC_1(int64_t, CallInterpreter_Void_RetI64, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + + int64_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, 0, (int8_t*)&result); + return result; + } + + FCDECL2(void, CallInterpreter_This_S12_RetVoid, int32_t, int8_t*); + WASM_CALLABLE_FUNC_3(void, CallInterpreter_This_S12_RetVoid, int32_t arg0, int8_t* arg1, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[3]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + transitionBlock.args[0] = (int64_t)arg0; + memcpy(&transitionBlock.args[1], arg1, 12); + 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, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return; + } + + FCDECL2(void, CallInterpreter_This_S32_RetVoid, int32_t, int8_t*); + WASM_CALLABLE_FUNC_3(void, CallInterpreter_This_S32_RetVoid, int32_t arg0, int8_t* arg1, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[5]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + transitionBlock.args[0] = (int64_t)arg0; + memcpy(&transitionBlock.args[1], arg1, 32); + 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, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return; + } + + FCDECL3(void, CallInterpreter_This_S8_S8_RetVoid, int32_t, int8_t*, int8_t*); + WASM_CALLABLE_FUNC_4(void, CallInterpreter_This_S8_S8_RetVoid, int32_t arg0, int8_t* arg1, int8_t* arg2, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[3]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + transitionBlock.args[0] = (int64_t)arg0; + memcpy(&transitionBlock.args[1], arg1, 8); + memcpy(&transitionBlock.args[2], arg2, 8); + 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, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return; + } + + FCDECL3(void, CallInterpreter_This_S8_I32_RetVoid, int32_t, int8_t*, int32_t); + WASM_CALLABLE_FUNC_4(void, CallInterpreter_This_S8_I32_RetVoid, int32_t arg0, int8_t* arg1, int32_t arg2, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[3]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + transitionBlock.args[0] = (int64_t)arg0; + memcpy(&transitionBlock.args[1], arg1, 8); + 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, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return; + } + + FCDECL2(void, CallInterpreter_This_S8_RetVoid, int32_t, int8_t*); + WASM_CALLABLE_FUNC_3(void, CallInterpreter_This_S8_RetVoid, int32_t arg0, int8_t* arg1, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[2]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + transitionBlock.args[0] = (int64_t)arg0; + memcpy(&transitionBlock.args[1], arg1, 8); + 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, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return; + } + + FCDECL3(void, CallInterpreter_This_I32_S8_RetVoid, int32_t, int32_t, int8_t*); + WASM_CALLABLE_FUNC_4(void, CallInterpreter_This_I32_S8_RetVoid, int32_t arg0, int32_t arg1, int8_t* arg2, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[3]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + transitionBlock.args[0] = (int64_t)arg0; + transitionBlock.args[1] = (int64_t)arg1; + memcpy(&transitionBlock.args[2], arg2, 8); + 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, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return; + } + + FCDECL7(void, CallInterpreter_This_I32_I32_S8_S8_I32_I32_RetVoid, int32_t, int32_t, int32_t, int8_t*, int8_t*, int32_t, int32_t); + WASM_CALLABLE_FUNC_8(void, CallInterpreter_This_I32_I32_S8_S8_I32_I32_RetVoid, int32_t arg0, int32_t arg1, int32_t arg2, int8_t* arg3, int8_t* arg4, int32_t arg5, int32_t arg6, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[7]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + transitionBlock.args[0] = (int64_t)arg0; + transitionBlock.args[1] = (int64_t)arg1; + transitionBlock.args[2] = (int64_t)arg2; + memcpy(&transitionBlock.args[3], arg3, 8); + memcpy(&transitionBlock.args[4], arg4, 8); + 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, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return; + } + + FCDECL6(void, CallInterpreter_This_I32_I32_I32_I32_I32_RetVoid, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t); + WASM_CALLABLE_FUNC_7(void, CallInterpreter_This_I32_I32_I32_I32_I32_RetVoid, int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4, int32_t arg5, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[6]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return; + } + + FCDECL5(void, CallInterpreter_This_I32_I32_I32_I32_RetVoid, int32_t, int32_t, int32_t, int32_t, int32_t); + WASM_CALLABLE_FUNC_6(void, CallInterpreter_This_I32_I32_I32_I32_RetVoid, int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[5]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return; + } + + FCDECL4(void, CallInterpreter_This_I32_I32_I32_RetVoid, int32_t, int32_t, int32_t, int32_t); + WASM_CALLABLE_FUNC_5(void, CallInterpreter_This_I32_I32_I32_RetVoid, int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[4]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return; + } + + FCDECL3(void, CallInterpreter_This_I32_I32_RetVoid, int32_t, int32_t, int32_t); + WASM_CALLABLE_FUNC_4(void, CallInterpreter_This_I32_I32_RetVoid, int32_t arg0, int32_t arg1, int32_t arg2, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[3]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return; + } + + FCDECL2(void, CallInterpreter_This_I32_RetVoid, int32_t, int32_t); + WASM_CALLABLE_FUNC_3(void, CallInterpreter_This_I32_RetVoid, int32_t arg0, int32_t arg1, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[2]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return; + } + + FCDECL1(void, CallInterpreter_This_RetVoid, int32_t); + WASM_CALLABLE_FUNC_2(void, CallInterpreter_This_RetVoid, int32_t arg0, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[1]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return; + } + + FCDECL3(void, CallInterpreter_F64_I32_I32_RetVoid, double, int32_t, int32_t); + WASM_CALLABLE_FUNC_4(void, CallInterpreter_F64_I32_I32_RetVoid, double arg0, int32_t arg1, int32_t arg2, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[3]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return; + } + + FCDECL3(void, CallInterpreter_F32_I32_I32_RetVoid, float, int32_t, int32_t); + WASM_CALLABLE_FUNC_4(void, CallInterpreter_F32_I32_I32_RetVoid, float arg0, int32_t arg1, int32_t arg2, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[3]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return; + } + + FCDECL4(void, CallInterpreter_I32_I32_I32_I32_RetVoid, int32_t, int32_t, int32_t, int32_t); + 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) + { + struct + { + TransitionBlock block; + int64_t args[4]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return; + } + + FCDECL3(void, CallInterpreter_I32_I32_I32_RetVoid, int32_t, int32_t, int32_t); + WASM_CALLABLE_FUNC_4(void, CallInterpreter_I32_I32_I32_RetVoid, int32_t arg0, int32_t arg1, int32_t arg2, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[3]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return; + } + + FCDECL2(void, CallInterpreter_I32_I32_RetVoid, int32_t, int32_t); + WASM_CALLABLE_FUNC_3(void, CallInterpreter_I32_I32_RetVoid, int32_t arg0, int32_t arg1, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[2]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return; + } + + FCDECL1(void, CallInterpreter_I32_RetVoid, int32_t); + WASM_CALLABLE_FUNC_2(void, CallInterpreter_I32_RetVoid, int32_t arg0, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[1]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return; + } + + FCDECL0(void, CallInterpreter_Void_RetVoid); + WASM_CALLABLE_FUNC_1(void, CallInterpreter_Void_RetVoid, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + + void * result = NULL; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, 0, (int8_t*)&result); + return; + } + +} + +const StringToWasmSigThunk g_wasmGeneratedPortableEntryPointThunks[] = { + { "IS12TS12ip", (void*)&CallInterpreter_This_S12_I32_RetS12 }, + { "IS12TiS12ip", (void*)&CallInterpreter_This_I32_S12_I32_RetS12 }, + { "IS12Tiiip", (void*)&CallInterpreter_This_I32_I32_I32_RetS12 }, + { "IS12Tiip", (void*)&CallInterpreter_This_I32_I32_RetS12 }, + { "IS16Tp", (void*)&CallInterpreter_This_RetS16 }, + { "IS1Tp", (void*)&CallInterpreter_This_RetS1 }, + { "IS8Tiiip", (void*)&CallInterpreter_This_I32_I32_I32_RetS8 }, + { "IS8Tip", (void*)&CallInterpreter_This_I32_RetS8 }, + { "IS8Tp", (void*)&CallInterpreter_This_RetS8 }, + { "Iddddp", (void*)&CallInterpreter_F64_F64_F64_RetF64 }, + { "Idddp", (void*)&CallInterpreter_F64_F64_RetF64 }, + { "Iddip", (void*)&CallInterpreter_F64_I32_RetF64 }, + { "Iddp", (void*)&CallInterpreter_F64_RetF64 }, + { "Iffffp", (void*)&CallInterpreter_F32_F32_F32_RetF32 }, + { "Ifffp", (void*)&CallInterpreter_F32_F32_RetF32 }, + { "Iffip", (void*)&CallInterpreter_F32_I32_RetF32 }, + { "Iffp", (void*)&CallInterpreter_F32_RetF32 }, + { "IiS8p", (void*)&CallInterpreter_S8_RetI32 }, + { "IiTS12ip", (void*)&CallInterpreter_This_S12_I32_RetI32 }, + { "IiTS12p", (void*)&CallInterpreter_This_S12_RetI32 }, + { "IiTS8S8p", (void*)&CallInterpreter_This_S8_S8_RetI32 }, + { "IiTS8iS8ip", (void*)&CallInterpreter_This_S8_I32_S8_I32_RetI32 }, + { "IiTS8iiip", (void*)&CallInterpreter_This_S8_I32_I32_I32_RetI32 }, + { "IiTS8ip", (void*)&CallInterpreter_This_S8_I32_RetI32 }, + { "IiTS8p", (void*)&CallInterpreter_This_S8_RetI32 }, + { "IiTiS12p", (void*)&CallInterpreter_This_I32_S12_RetI32 }, + { "IiTiS8p", (void*)&CallInterpreter_This_I32_S8_RetI32 }, + { "IiTiiiip", (void*)&CallInterpreter_This_I32_I32_I32_I32_RetI32 }, + { "IiTiiip", (void*)&CallInterpreter_This_I32_I32_I32_RetI32 }, + { "IiTiip", (void*)&CallInterpreter_This_I32_I32_RetI32 }, + { "IiTip", (void*)&CallInterpreter_This_I32_RetI32 }, + { "IiTp", (void*)&CallInterpreter_This_RetI32 }, + { "Iidp", (void*)&CallInterpreter_F64_RetI32 }, + { "IiiS8p", (void*)&CallInterpreter_I32_S8_RetI32 }, + { "Iiiiiiiiiip", (void*)&CallInterpreter_I32_I32_I32_I32_I32_I32_I32_I32_RetI32 }, + { "Iiiiiiiiip", (void*)&CallInterpreter_I32_I32_I32_I32_I32_I32_I32_RetI32 }, + { "Iiiiiiiip", (void*)&CallInterpreter_I32_I32_I32_I32_I32_I32_RetI32 }, + { "Iiiiiiip", (void*)&CallInterpreter_I32_I32_I32_I32_I32_RetI32 }, + { "Iiiiiip", (void*)&CallInterpreter_I32_I32_I32_I32_RetI32 }, + { "Iiiiip", (void*)&CallInterpreter_I32_I32_I32_RetI32 }, + { "Iiiip", (void*)&CallInterpreter_I32_I32_RetI32 }, + { "Iiip", (void*)&CallInterpreter_I32_RetI32 }, + { "Iip", (void*)&CallInterpreter_Void_RetI32 }, + { "Ildp", (void*)&CallInterpreter_F64_RetI64 }, + { "Ilillp", (void*)&CallInterpreter_I32_I64_I64_RetI64 }, + { "Ililp", (void*)&CallInterpreter_I32_I64_RetI64 }, + { "Ilip", (void*)&CallInterpreter_I32_RetI64 }, + { "Illlp", (void*)&CallInterpreter_I64_I64_RetI64 }, + { "Ilp", (void*)&CallInterpreter_Void_RetI64 }, + { "IvTS12p", (void*)&CallInterpreter_This_S12_RetVoid }, + { "IvTS32p", (void*)&CallInterpreter_This_S32_RetVoid }, + { "IvTS8S8p", (void*)&CallInterpreter_This_S8_S8_RetVoid }, + { "IvTS8ip", (void*)&CallInterpreter_This_S8_I32_RetVoid }, + { "IvTS8p", (void*)&CallInterpreter_This_S8_RetVoid }, + { "IvTiS8p", (void*)&CallInterpreter_This_I32_S8_RetVoid }, + { "IvTiiS8S8iip", (void*)&CallInterpreter_This_I32_I32_S8_S8_I32_I32_RetVoid }, + { "IvTiiiiip", (void*)&CallInterpreter_This_I32_I32_I32_I32_I32_RetVoid }, + { "IvTiiiip", (void*)&CallInterpreter_This_I32_I32_I32_I32_RetVoid }, + { "IvTiiip", (void*)&CallInterpreter_This_I32_I32_I32_RetVoid }, + { "IvTiip", (void*)&CallInterpreter_This_I32_I32_RetVoid }, + { "IvTip", (void*)&CallInterpreter_This_I32_RetVoid }, + { "IvTp", (void*)&CallInterpreter_This_RetVoid }, + { "Ivdiip", (void*)&CallInterpreter_F64_I32_I32_RetVoid }, + { "Ivfiip", (void*)&CallInterpreter_F32_I32_I32_RetVoid }, + { "Iviiiip", (void*)&CallInterpreter_I32_I32_I32_I32_RetVoid }, + { "Iviiip", (void*)&CallInterpreter_I32_I32_I32_RetVoid }, + { "Iviip", (void*)&CallInterpreter_I32_I32_RetVoid }, + { "Ivip", (void*)&CallInterpreter_I32_RetVoid }, + { "Ivp", (void*)&CallInterpreter_Void_RetVoid }, +}; + +const size_t g_wasmGeneratedPortableEntryPointThunksCount = sizeof(g_wasmGeneratedPortableEntryPointThunks) / sizeof(g_wasmGeneratedPortableEntryPointThunks[0]); diff --git a/src/coreclr/vm/wasm/callhelpers.hpp b/src/coreclr/vm/wasm/callhelpers.hpp index 6f60164ffa87c9..8c35140704e4f7 100644 --- a/src/coreclr/vm/wasm/callhelpers.hpp +++ b/src/coreclr/vm/wasm/callhelpers.hpp @@ -43,6 +43,11 @@ struct StringToWasmSigThunk extern const StringToWasmSigThunk g_wasmThunks[]; extern const size_t g_wasmThunksCount; +#ifdef TARGET_BROWSER +extern const StringToWasmSigThunk g_wasmGeneratedPortableEntryPointThunks[]; +extern const size_t g_wasmGeneratedPortableEntryPointThunksCount; +#endif + struct ReverseThunkMapValue { MethodDesc** Target; diff --git a/src/coreclr/vm/wasm/helpers.cpp b/src/coreclr/vm/wasm/helpers.cpp index 05ad28e3b94eea..a9500254e57a34 100644 --- a/src/coreclr/vm/wasm/helpers.cpp +++ b/src/coreclr/vm/wasm/helpers.cpp @@ -21,57 +21,13 @@ void ExecuteInterpretedMethodWithArgs_PortableEntryPoint(PCODE portableEntrypoin // ------------------------------------------------- namespace { - FCDECL0(void, CallInterpreter_RetVoid); - WASM_CALLABLE_FUNC_1(void, CallInterpreter_RetVoid, PCODE portableEntrypoint) - { - struct - { - TransitionBlock block; - } transitionBlock; - transitionBlock.block.m_ReturnAddress = 0; - transitionBlock.block.m_StackPointer = callersStackPointer; - void * result = NULL; - - ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, 0, (int8_t*)&result); - return; - } - FCDECL1(void, CallInterpreter_I32_RetVoid, int32_t); - WASM_CALLABLE_FUNC_2(void, CallInterpreter_I32_RetVoid, int32_t arg0, PCODE portableEntrypoint) - { - struct - { - TransitionBlock block; - int64_t args[1]; - } transitionBlock; - transitionBlock.block.m_ReturnAddress = 0; - transitionBlock.block.m_StackPointer = callersStackPointer; - 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, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); - return; - } - FCDECL2(void, CallInterpreter_I32_I32_RetVoid, int32_t, int32_t); - WASM_CALLABLE_FUNC_3(void, CallInterpreter_I32_I32_RetVoid, int32_t arg0, int32_t arg1, PCODE portableEntrypoint) - { - struct - { - TransitionBlock block; - int64_t args[2]; - } transitionBlock; - transitionBlock.block.m_ReturnAddress = 0; - transitionBlock.block.m_StackPointer = callersStackPointer; - 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, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); - return; - } - FCDECL3(void, CallInterpreter_I32_I32_I32_RetVoid, int32_t, int32_t, int32_t); - WASM_CALLABLE_FUNC_4(void, CallInterpreter_I32_I32_I32_RetVoid, int32_t arg0, int32_t arg1, int32_t arg2, PCODE portableEntrypoint) + // 'l2' is a 16-byte multi-slot argument (Int128/UInt128/Decimal128) passed by value in two i64 + // slots. A struct return is NOT a C return value here: crossgen2 passes the return buffer as an + // explicit parameter (after callersStackPointer, or after 'this' for an instance method), so the + // thunk must be void — returning a struct would make the compiler insert its own sret pointer at + // parameter 0 and shift everything. + FCDECL4(void, CallInterpreter_L2_I32_RetS16, int8_t*, int64_t, int64_t, int32_t); + WASM_CALLABLE_FUNC_5(void, CallInterpreter_L2_I32_RetS16, int8_t* retBuf, int64_t arg0Lo, int64_t arg0Hi, int32_t arg1, PCODE portableEntrypoint) { struct { @@ -80,285 +36,21 @@ namespace } transitionBlock; transitionBlock.block.m_ReturnAddress = 0; transitionBlock.block.m_StackPointer = callersStackPointer; - transitionBlock.args[0] = (int64_t)arg0; - transitionBlock.args[1] = (int64_t)arg1; - transitionBlock.args[2] = (int64_t)arg2; + transitionBlock.args[0] = arg0Lo; + transitionBlock.args[1] = arg0Hi; + transitionBlock.args[2] = (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, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); - return; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), retBuf); } - FCDECL4(void, CallInterpreter_I32_I32_I32_I32_RetVoid, int32_t, int32_t, int32_t, int32_t); - 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) - { - struct - { - TransitionBlock block; - int64_t args[4]; - } transitionBlock; - transitionBlock.block.m_ReturnAddress = 0; - transitionBlock.block.m_StackPointer = callersStackPointer; - 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, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); - return; - } - FCDECL0(int32_t, CallInterpreter_RetI32); - WASM_CALLABLE_FUNC_1(int32_t, CallInterpreter_RetI32, PCODE portableEntrypoint) - { - struct - { - TransitionBlock block; - } transitionBlock; - transitionBlock.block.m_ReturnAddress = 0; - transitionBlock.block.m_StackPointer = callersStackPointer; - void * result = NULL; - ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, 0, (int8_t*)&result); - return (int32_t)result; - } - FCDECL1(int32_t, CallInterpreter_I32_RetI32, int32_t); - WASM_CALLABLE_FUNC_2(int32_t, CallInterpreter_I32_RetI32, int32_t arg0, PCODE portableEntrypoint) - { - struct - { - TransitionBlock block; - int64_t args[1]; - } transitionBlock; - transitionBlock.block.m_ReturnAddress = 0; - transitionBlock.block.m_StackPointer = callersStackPointer; - 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, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); - return (int32_t)result; - } - FCDECL1(int32_t, CallInterpreter_D64_RetI32, double); - WASM_CALLABLE_FUNC_2(int32_t, CallInterpreter_D64_RetI32, double arg0, PCODE portableEntrypoint) - { - struct - { - TransitionBlock block; - double args[1]; - } transitionBlock; - transitionBlock.block.m_ReturnAddress = 0; - transitionBlock.block.m_StackPointer = callersStackPointer; - transitionBlock.args[0] = 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, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); - return (int32_t)result; - } - FCDECL1(int64_t, CallInterpreter_D64_RetI64, double); - WASM_CALLABLE_FUNC_2(int64_t, CallInterpreter_D64_RetI64, double arg0, PCODE portableEntrypoint) - { - struct - { - TransitionBlock block; - double args[1]; - } transitionBlock; - transitionBlock.block.m_ReturnAddress = 0; - transitionBlock.block.m_StackPointer = callersStackPointer; - transitionBlock.args[0] = arg0; - static_assert(offsetof(decltype(transitionBlock), args) == sizeof(TransitionBlock), "Args array must be at a TransitionBlock offset from the start of the block"); - - int64_t result = 0; - ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); - return result; - } - FCDECL2(int32_t, CallInterpreter_I32_I32_RetI32, int32_t, int32_t); - WASM_CALLABLE_FUNC_3(int32_t, CallInterpreter_I32_I32_RetI32, int32_t arg0, int32_t arg1, PCODE portableEntrypoint) - { - struct - { - TransitionBlock block; - int64_t args[2]; - } transitionBlock; - transitionBlock.block.m_ReturnAddress = 0; - transitionBlock.block.m_StackPointer = callersStackPointer; - 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, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); - return (int32_t)result; - } - FCDECL2(int32_t, CallInterpreter_I32_S8_RetI32, int32_t, int8_t*); - WASM_CALLABLE_FUNC_3(int32_t, CallInterpreter_I32_S8_RetI32, int32_t arg0, int8_t* arg1, PCODE portableEntrypoint) - { - struct - { - TransitionBlock block; - int64_t args[2]; - } transitionBlock; - transitionBlock.block.m_ReturnAddress = 0; - transitionBlock.block.m_StackPointer = callersStackPointer; - transitionBlock.args[0] = (int64_t)arg0; - memcpy(&transitionBlock.args[1], arg1, 8); - 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, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); - return (int32_t)result; - } - FCDECL3(int32_t, CallInterpreter_I32_I32_I32_RetI32, int32_t, int32_t, int32_t); - WASM_CALLABLE_FUNC_4(int32_t, CallInterpreter_I32_I32_I32_RetI32, int32_t arg0, int32_t arg1, int32_t arg2, PCODE portableEntrypoint) - { - struct - { - TransitionBlock block; - int64_t args[3]; - } transitionBlock; - transitionBlock.block.m_ReturnAddress = 0; - transitionBlock.block.m_StackPointer = callersStackPointer; - 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, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); - return (int32_t)result; - } - FCDECL4(int32_t, CallInterpreter_I32_I32_I32_I32_RetI32, int32_t, int32_t, int32_t, int32_t); - 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) - { - struct - { - TransitionBlock block; - int64_t args[4]; - } transitionBlock; - transitionBlock.block.m_ReturnAddress = 0; - transitionBlock.block.m_StackPointer = callersStackPointer; - 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, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); - return (int32_t)result; - } - FCDECL5(int32_t, CallInterpreter_I32_I32_I32_I32_I32_RetI32, int32_t, int32_t, int32_t, int32_t, int32_t); - 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) - { - struct - { - TransitionBlock block; - int64_t args[5]; - } transitionBlock; - transitionBlock.block.m_ReturnAddress = 0; - transitionBlock.block.m_StackPointer = callersStackPointer; - 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, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); - return (int32_t)result; - } - FCDECL6(int32_t, CallInterpreter_I32_I32_I32_I32_I32_I32_RetI32, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t); - 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) - { - struct - { - TransitionBlock block; - int64_t args[6]; - } transitionBlock; - transitionBlock.block.m_ReturnAddress = 0; - transitionBlock.block.m_StackPointer = callersStackPointer; - 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, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); - return (int32_t)result; - } - FCDECL7(int32_t, CallInterpreter_I32_I32_I32_I32_I32_I32_I32_RetI32, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t); - 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) - { - struct - { - TransitionBlock block; - int64_t args[7]; - } transitionBlock; - transitionBlock.block.m_ReturnAddress = 0; - transitionBlock.block.m_StackPointer = callersStackPointer; - 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, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); - return (int32_t)result; - } - FCDECL8(int32_t, CallInterpreter_I32_I32_I32_I32_I32_I32_I32_I32_RetI32, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t); - 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) - { - struct - { - TransitionBlock block; - int64_t args[8]; - } transitionBlock; - transitionBlock.block.m_ReturnAddress = 0; - transitionBlock.block.m_StackPointer = callersStackPointer; - 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, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); - return (int32_t)result; - } } const StringToWasmSigThunk g_wasmPortableEntryPointThunks[] = { - { "Ivp", (void*)&CallInterpreter_RetVoid }, - { "Ivip", (void*)&CallInterpreter_I32_RetVoid }, - { "Iviip", (void*)&CallInterpreter_I32_I32_RetVoid }, - { "Iviiip", (void*)&CallInterpreter_I32_I32_I32_RetVoid }, - { "Iviiiip", (void*)&CallInterpreter_I32_I32_I32_I32_RetVoid }, - { "Iip", (void*)&CallInterpreter_RetI32 }, - { "Iiip", (void*)&CallInterpreter_I32_RetI32 }, - { "Iiiip", (void*)&CallInterpreter_I32_I32_RetI32 }, - { "Iiiiip", (void*)&CallInterpreter_I32_I32_I32_RetI32 }, - { "Iiiiiip", (void*)&CallInterpreter_I32_I32_I32_I32_RetI32 }, - { "Iiiiiiip", (void*)&CallInterpreter_I32_I32_I32_I32_I32_RetI32 }, - { "Iiiiiiiip", (void*)&CallInterpreter_I32_I32_I32_I32_I32_I32_RetI32 }, - { "Iiiiiiiiip", (void*)&CallInterpreter_I32_I32_I32_I32_I32_I32_I32_RetI32 }, - { "Iiiiiiiiiip", (void*)&CallInterpreter_I32_I32_I32_I32_I32_I32_I32_I32_RetI32 }, - { "Iidp", (void*)&CallInterpreter_D64_RetI32 }, - { "Ildp", (void*)&CallInterpreter_D64_RetI64 }, - { "IiiS8p", (void*)&CallInterpreter_I32_S8_RetI32 } + // Every other shape is emitted by the WasmAppBuilder generator into + // callhelpers-portable-entrypoints.cpp; only the multi-slot 'l2' form, which the generator + // cannot express, is still written by hand. + { "IS16l2ip", (void*)&CallInterpreter_L2_I32_RetS16 }, }; const size_t g_wasmPortableEntryPointThunksCount = sizeof(g_wasmPortableEntryPointThunks) / sizeof(g_wasmPortableEntryPointThunks[0]); @@ -1486,10 +1178,10 @@ namespace } InterpreterCalliCookie thunk = LookupThunk(keyBuffer); -#ifdef _DEBUG if (thunk == NULL) - printf("WASM calli missing for key: %s\n", keyBuffer); -#endif + { + printf("WASM: no interpreter-to-R2R calli thunk for signature key '%s'. Add it to g_wasmThunks.\n", keyBuffer); + } return thunk; } @@ -1532,12 +1224,13 @@ namespace } void* thunk = LookupPortableEntryPointThunk(keyBuffer); -#ifdef _DEBUG if (thunk == NULL) { - LOG((LF_STUBS, LL_INFO100000, "WASM R2R to interpreter call missing for key: %s\n", keyBuffer)); + // Printed rather than only asserted: the caller's assert compiles out in release, where these + // gaps surface, and cannot carry the key. A miss leaves the entry point's table index 0 and + // traps later as "null function", far from here. + printf("WASM: no R2R-to-interpreter thunk for signature key '%s'. Add it to g_wasmPortableEntryPointThunks.\n", keyBuffer); } -#endif return thunk; } @@ -1652,11 +1345,21 @@ void InitializeWasmThunkCaches() { StringToWasmSigThunkHash* newTable = new StringToWasmSigThunkHash(); - newTable->Reallocate(g_wasmPortableEntryPointThunksCount * StringToWasmSigThunkHash::s_density_factor_denominator / StringToWasmSigThunkHash::s_density_factor_numerator + 1); + size_t total = g_wasmPortableEntryPointThunksCount; +#ifdef TARGET_BROWSER + total += g_wasmGeneratedPortableEntryPointThunksCount; +#endif + newTable->Reallocate(total * 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); } +#ifdef TARGET_BROWSER + for (size_t i = 0; i < g_wasmGeneratedPortableEntryPointThunksCount; i++) + { + newTable->Add(g_wasmGeneratedPortableEntryPointThunks[i].key, g_wasmGeneratedPortableEntryPointThunks[i].value); + } +#endif portableEntrypointThunkCache = newTable; } } @@ -1786,10 +1489,18 @@ void* GetPortableEntryPointToInterpreterThunk(MethodDesc *pMD) } thunk = LookupPortableEntryPointThunk(thunkKey); + if (thunk == NULL) + { + PORTABILITY_ASSERT("GetPortableEntryPointToInterpreterThunk: unknown thunk for string constructor"); + } } else { thunk = ComputePortableEntryPointToInterpreterThunk(sig); + if (thunk == NULL) + { + PORTABILITY_ASSERT("ComputePortableEntryPointToInterpreterThunk: unknown thunk signature"); + } } return thunk; diff --git a/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj b/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj index 32cac2b06a82d4..701a1d062af057 100644 --- a/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj +++ b/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj @@ -81,7 +81,8 @@ TargetOS="$(_RunGeneratorTargetOS)" PInvokeOutputPath="$(GeneratorOutputPath)callhelpers-pinvoke.cpp" ReversePInvokeOutputPath="$(GeneratorOutputPath)callhelpers-reverse.cpp" - InterpToNativeOutputPath="$(GeneratorOutputPath)callhelpers-interp-to-managed.cpp"> + InterpToNativeOutputPath="$(GeneratorOutputPath)callhelpers-interp-to-managed.cpp" + PortableEntryPointOutputPath="$(GeneratorOutputPath)callhelpers-portable-entrypoints.cpp"> diff --git a/src/tasks/WasmAppBuilder/coreclr/InterpToNativeGenerator.cs b/src/tasks/WasmAppBuilder/coreclr/InterpToNativeGenerator.cs index e5183182df8088..87d3d13f41a59c 100644 --- a/src/tasks/WasmAppBuilder/coreclr/InterpToNativeGenerator.cs +++ b/src/tasks/WasmAppBuilder/coreclr/InterpToNativeGenerator.cs @@ -57,6 +57,145 @@ private static string CallFuncName(IEnumerable args, string result, bool return $"CallFunc_{paramTypes}_Ret{result}{(isPortableEntryPointCall ? "_PE" : "")}"; } + /// + /// Emits the R2R-to-interpreter side of the portable entrypoint boundary: the thunks that + /// g_wasmThunks' _PE helpers call into. Struct returns are not emitted yet; their + /// wasm sret convention across this boundary is not settled, and no _PE helper exercises one. + /// + public void GeneratePortableEntryPoints(IEnumerable cookies, string outputPath) + { + using TempFileName tmpFileName = new(); + using (var w = File.CreateText(tmpFileName.Path)) + { + EmitPortableEntryPoints(w, cookies); + } + + if (Utils.CopyIfDifferent(tmpFileName.Path, outputPath, useHash: false)) + Log.LogMessage(MessageImportance.Low, $"Generating portable entrypoint thunks to '{outputPath}'."); + else + Log.LogMessage(MessageImportance.Low, $"Portable entrypoint thunks in {outputPath} are unchanged."); + } + + private static string CallInterpreterName(IEnumerable args, string result) + { + var paramTypes = args.Any() ? string.Join("_", args.Select(static t => SignatureMapper.TokenToNameType(t))) : "Void"; + + return $"CallInterpreter_{paramTypes}_Ret{result}"; + } + + private static bool IsStructToken(string token) => PortableEntryPointThunkSignature.IsStructToken(token); + + private static void EmitPortableEntryPoints(StreamWriter w, IEnumerable cookies) + { + var signatures = cookies.Distinct().OrderBy(c => c, StringComparer.Ordinal).ToArray(); + + w.Write( + """ + // Licensed to the .NET Foundation under one or more agreements. + // The .NET Foundation licenses this file to you under the MIT license. + // + + // + // GENERATED FILE, DON'T EDIT + // Generated by coreclr InterpToNativeGenerator + // + + #include + #include + #include "callhelpers.hpp" + + void ExecuteInterpretedMethodWithArgs_PortableEntryPoint(PCODE portableEntrypoint, TransitionBlock* block, size_t argsSize, int8_t* retBuff); + + namespace + { + + """); + + var emitted = new List<(string Key, string Name)>(); + + foreach (var signature in signatures) + { + var tokens = SignatureMapper.ParseSignatureTokens(signature); + if (!IsPortableEntryPointCall(tokens)) + continue; + + string returnToken = tokens[0]; + + tokens.RemoveAt(tokens.Count - 1); + RemoveAsyncCallMarker(tokens); + var args = Args(tokens); + + string retName = SignatureMapper.TokenToNameType(returnToken); + string name = CallInterpreterName(args, retName); + if (emitted.Any(e => e.Name == name)) + continue; + + // crossgen2 passes a struct return through an explicit buffer parameter rather than by value. + // Returning the struct by value instead would make the compiler insert its own sret pointer + // ahead of callersStackPointer, silently transposing the two. + bool isStructReturn = IsStructToken(returnToken); + string retType = isStructReturn ? "void" : SignatureMapper.TokenToNativeType(returnToken); + bool isVoid = isStructReturn || returnToken == "v"; + + var parameters = PortableEntryPointThunkSignature.GetDeclaredParameters(args, isStructReturn); + var declTypes = parameters.Select(static p => p.NativeType).ToList(); + var namedArgs = parameters.Select(static p => $"{p.NativeType} {p.Name}").ToList(); + + int slot = 0; + var stores = new List(); + for (int i = 0; i < args.Count; i++) + { + string t = args[i]; + if (t[0] == 'A') + slot = (slot + 1) & ~1; + + stores.Add(IsStructToken(t) + ? $" memcpy(&transitionBlock.args[{slot}], arg{i}, {SignatureMapper.GetStructSize(t)});" + : $" transitionBlock.args[{slot}] = (int64_t)arg{i};"); + + slot += SignatureMapper.TokenToSlotCount(t); + } + + string declArgs = declTypes.Count > 0 ? ", " + string.Join(", ", declTypes) : ""; + string sigArgs = namedArgs.Count > 0 ? string.Join(", ", namedArgs) + ", " : ""; + + w.WriteLine($" FCDECL{declTypes.Count}({retType}, {name}{declArgs});"); + w.WriteLine($" WASM_CALLABLE_FUNC_{declTypes.Count + 1}({retType}, {name}, {sigArgs}PCODE portableEntrypoint)"); + w.WriteLine(" {"); + w.WriteLine(" struct"); + w.WriteLine(" {"); + w.WriteLine(" TransitionBlock block;"); + if (slot > 0) + w.WriteLine($" int64_t args[{slot}];"); + w.WriteLine(" } transitionBlock;"); + w.WriteLine(" transitionBlock.block.m_ReturnAddress = 0;"); + w.WriteLine(" transitionBlock.block.m_StackPointer = callersStackPointer;"); + foreach (var store in stores) + w.WriteLine(store); + if (slot > 0) + w.WriteLine(" static_assert(offsetof(decltype(transitionBlock), args) == sizeof(TransitionBlock), \"Args array must be at a TransitionBlock offset from the start of the block\");"); + w.WriteLine(); + if (!isStructReturn) + w.WriteLine(isVoid ? " void * result = NULL;" : $" {retType} result = 0;"); + string retBuffArg = isStructReturn ? "retBuf" : "(int8_t*)&result"; + w.WriteLine($" ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, {(slot > 0 ? "sizeof(transitionBlock.args)" : "0")}, {retBuffArg});"); + w.WriteLine(isVoid ? " return;" : " return result;"); + w.WriteLine(" }"); + w.WriteLine(); + + emitted.Add(("I" + signature, name)); + } + + w.WriteLine("}"); + w.WriteLine(); + w.WriteLine("const StringToWasmSigThunk g_wasmGeneratedPortableEntryPointThunks[] = {"); + foreach (var (key, name) in emitted) + w.WriteLine($" {{ \"{key}\", (void*)&{name} }},"); + w.WriteLine("};"); + w.WriteLine(); + w.WriteLine("const size_t g_wasmGeneratedPortableEntryPointThunksCount = sizeof(g_wasmGeneratedPortableEntryPointThunks) / sizeof(g_wasmGeneratedPortableEntryPointThunks[0]);"); + } + private static void Emit(StreamWriter w, IEnumerable cookies) { // Use OrderBy because Order() is not available on .NET Framework @@ -173,11 +312,6 @@ private static void Emit(StreamWriter w, IEnumerable cookies) """); - static List Args(List tokens) - { - return tokens.Count > 1 ? tokens.GetRange(1, tokens.Count - 1) : new List(); - } - static List ArgsWithSlotOffsets(List args) { var result = new List(); @@ -204,19 +338,25 @@ static List ArgsWithSlotOffsets(List args) return new(returnToken == "v", SignatureMapper.TokenToNativeType(returnToken)); } - static bool IsPortableEntryPointCall(List tokens) - { - return tokens.Count > 0 && tokens[tokens.Count - 1] == "p"; - } + } - static bool RemoveAsyncCallMarker(List tokens) - { - int asyncMarkerIndex = tokens.IndexOf("a"); - if (asyncMarkerIndex < 0) - return false; + private static List Args(List tokens) + { + return tokens.Count > 1 ? tokens.GetRange(1, tokens.Count - 1) : new List(); + } - tokens.RemoveAt(asyncMarkerIndex); - return true; - } + private static bool IsPortableEntryPointCall(List tokens) + { + return tokens.Count > 0 && tokens[tokens.Count - 1] == "p"; + } + + private static bool RemoveAsyncCallMarker(List tokens) + { + int asyncMarkerIndex = tokens.IndexOf("a"); + if (asyncMarkerIndex < 0) + return false; + + tokens.RemoveAt(asyncMarkerIndex); + return true; } } diff --git a/src/tasks/WasmAppBuilder/coreclr/ManagedToNativeGenerator.cs b/src/tasks/WasmAppBuilder/coreclr/ManagedToNativeGenerator.cs index f033d8b39ca3ed..17370f83947085 100644 --- a/src/tasks/WasmAppBuilder/coreclr/ManagedToNativeGenerator.cs +++ b/src/tasks/WasmAppBuilder/coreclr/ManagedToNativeGenerator.cs @@ -31,6 +31,9 @@ public class ManagedToNativeGenerator : Task [Required, NotNull] public string? InterpToNativeOutputPath { get; set; } + + /// Set only by the in-tree RunGenerator target; app builds use the runtime's copy. + public string? PortableEntryPointOutputPath { get; set; } public string? CacheFilePath { get; set; } public bool IsLibraryMode { get; set; } @@ -117,7 +120,19 @@ private void ExecuteInternal(LogAdapter log) // 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 = Array.Empty(); // Currently none, but can be added here as needed in the future. + // The instance ('T') shapes below are the ones a Blazor app hits on startup. The static shapes on + // the last line were hand-written in vm/wasm/helpers.cpp before this generator emitted the table. + string[] pregeneratedInterpreterToNativeSignatures = new[] + { + "iTp", "iTip", "iTiip", "iTiiip", "iTiiiip", + "vTp", "vTip", "vTiip", "vTiiip", "vTiiiip", "vTiiiiip", + "iTS8p", "iTS8ip", "iTS8S8p", "iTS8iS8ip", "iTS8iiip", "iTiS8p", + "iTS12p", "iTS12ip", "iTiS12p", + "vTS8p", "vTS8ip", "vTS8S8p", "vTiS8p", "vTS12p", "vTS32p", "vTiiS8S8iip", + "S8Tp", "S8Tip", "S8Tiiip", "S12Tiip", "S12Tiiip", "S12TS12ip", "S16Tp", "S1Tp", + "S12TiS12ip", + "viiiip", "iiiiiiiip", "iiiiiiiiip", "idp", "ldp", "iiS8p", "iS8p", + }; IEnumerable cookies = pinvoke.Generate(PInvokeModules, IgnoredPInvokeModules, PInvokeOutputPath, ReversePInvokeOutputPath); cookies = cookies.Concat(internalCallCollector.GetSignatures()); @@ -126,6 +141,9 @@ private void ExecuteInternal(LogAdapter log) var m2n = new InterpToNativeGenerator(log); m2n.Generate(cookies, InterpToNativeOutputPath); + if (!string.IsNullOrEmpty(PortableEntryPointOutputPath)) + m2n.GeneratePortableEntryPoints(cookies, PortableEntryPointOutputPath); + if (!string.IsNullOrEmpty(CacheFilePath)) { IEnumerable cacheLines = PInvokeModules diff --git a/src/tasks/WasmAppBuilder/coreclr/PortableEntryPointThunkSignature.cs b/src/tasks/WasmAppBuilder/coreclr/PortableEntryPointThunkSignature.cs new file mode 100644 index 00000000000000..7d0b3e9c8fba73 --- /dev/null +++ b/src/tasks/WasmAppBuilder/coreclr/PortableEntryPointThunkSignature.cs @@ -0,0 +1,53 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; + +namespace Microsoft.WebAssembly.Build.Tasks.CoreClr; + +// Parameter layout of a generated R2R-to-interpreter thunk. Shared with the tests, which compare +// it against the wasm signature crossgen2 computes for the same key, so it must stay free of +// MSBuild dependencies. +internal static class PortableEntryPointThunkSignature +{ + internal readonly struct Parameter + { + public Parameter(string nativeType, string name) + { + NativeType = nativeType; + Name = name; + } + + public string NativeType { get; } + public string Name { get; } + } + + public static bool IsStructToken(string token) => token[0] is 'S' or 'A' && token.Length > 1; + + // A struct argument arrives as the address of its interpreter stack slot. + public static string DeclType(string token) + => IsStructToken(token) ? "int8_t*" : SignatureMapper.TokenToNativeType(token); + + /// + /// The thunk's declared C parameters, in the order crossgen2 passes them: + /// (callersStackPointer, [this], retBuf, args..., portableEntrypoint). The stack pointer comes + /// from the WASM_CALLABLE_FUNC macro and the entrypoint is appended by the caller, so neither + /// appears here. + /// + public static List GetDeclaredParameters(IReadOnlyList args, bool isStructReturn) + { + var parameters = new List(args.Count + 1); + for (int i = 0; i < args.Count; i++) + parameters.Add(new Parameter(DeclType(args[i]), $"arg{i}")); + + if (isStructReturn) + { + // The hidden return buffer follows the 'this' pointer rather than preceding it. + // See WasmR2RToInterpreterThunkNode.EmitCode: retBufLocalIndex = 1 + (hasThis ? 1 : 0). + int retBufIndex = args.Count > 0 && args[0] == "T" ? 1 : 0; + parameters.Insert(retBufIndex, new Parameter("int8_t*", "retBuf")); + } + + return parameters; + } +} From b7018fa331b780d225207f8cbc1b92ce99173372 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Sat, 29 Aug 2026 12:56:19 +0200 Subject: [PATCH 03/11] [wasm] Split the token half of SignatureMapper into its own file SignatureMapper mixes two things: reflection over scanned assemblies, which needs a LogAdapter and so drags in Microsoft.Build, and a pure mapping from signature tokens to native types, which needs nothing. Make it partial and move the pure half out, so a test can compile it directly instead of pulling MSBuild into a compiler test assembly. No behaviour change: the generator emits a byte-identical portable entrypoint table and an identical interp-to-managed table afterwards. --- .../coreclr/SignatureMapper.Tokens.cs | 142 ++++++++++++++++++ .../WasmAppBuilder/coreclr/SignatureMapper.cs | 131 +--------------- 2 files changed, 143 insertions(+), 130 deletions(-) create mode 100644 src/tasks/WasmAppBuilder/coreclr/SignatureMapper.Tokens.cs diff --git a/src/tasks/WasmAppBuilder/coreclr/SignatureMapper.Tokens.cs b/src/tasks/WasmAppBuilder/coreclr/SignatureMapper.Tokens.cs new file mode 100644 index 00000000000000..1c4e2e79ad4d78 --- /dev/null +++ b/src/tasks/WasmAppBuilder/coreclr/SignatureMapper.Tokens.cs @@ -0,0 +1,142 @@ +// 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; + +namespace Microsoft.WebAssembly.Build.Tasks.CoreClr; + +// The half of SignatureMapper that maps signature tokens to native types. It is kept free of +// MSBuild dependencies so that tests can compile it directly; the reflection-based half needs a +// LogAdapter and cannot be linked on its own. +internal static partial class SignatureMapper +{ + /// + /// Parses a signature string into individual tokens. + /// Single-char types produce one-char tokens; struct encodings produce multi-char tokens like + /// "S8" or "A32", and a multi-slot parameter produces a two-char token like "l2" or "V4". + /// The 'a' and 'p' suffixes are included as their own tokens. + /// + public static List ParseSignatureTokens(string signature) + { + var tokens = new List(); + int i = 0; + while (i < signature.Length) + { + if (signature[i] is 'S' or 'A') + { + int start = i; + i++; // skip 'S'/'A' + while (i < signature.Length && char.IsDigit(signature[i])) + i++; + tokens.Add(signature.Substring(start, i - start)); + } + else if (signature[i] is 'l' or 'V' && i + 1 < signature.Length && char.IsDigit(signature[i + 1])) + { + tokens.Add(signature.Substring(i, 2)); + i += 2; + } + else + { + tokens.Add(signature[i].ToString()); + i++; + } + } + + return tokens; + } + + /// + /// True for a token describing a type passed by value across several wasm parameters + /// ("l2", "V2", "V4"). Interop signatures do not use these today. + /// + private static bool IsMultiSlotToken(string token) + => token.Length == 2 && token[0] is 'l' or 'V' && char.IsDigit(token[1]); + + private static void RejectMultiSlotToken(string token) + { + if (IsMultiSlotToken(token)) + throw new NotSupportedException($"Multi-slot signature token '{token}' is not supported in interop thunks"); + } + + public static string TokenToNativeType(string token) + { + RejectMultiSlotToken(token); + return token[0] switch + { + 'v' => "void", + 'i' => "int32_t", + 'l' => "int64_t", + 'f' => "float", + 'd' => "double", + 'S' or 'A' => "int32_t", + 'T' => "int32_t", + 'p' => "PCODE", + _ => throw new InvalidSignatureCharException(token[0]) + }; + } + + public static string TokenToNameType(string token) + { + RejectMultiSlotToken(token); + return token[0] switch + { + 'v' => "Void", + 'i' => "I32", + 'l' => "I64", + 'f' => "F32", + 'd' => "F64", + 'S' or 'A' => token, + 'T' => "This", + 'p' => "PE", + _ => throw new InvalidSignatureCharException(token[0]) + }; + } + + public static string TokenToArgType(string token) + { + RejectMultiSlotToken(token); + return token[0] switch + { + 'i' => "ARG_I32", + 'l' => "ARG_I64", + 'f' => "ARG_F32", + 'd' => "ARG_F64", + 'S' or 'A' => "ARG_IND", + 'T' => "ARG_I32", + _ => throw new InvalidSignatureCharException(token[0]) + }; + } + + /// + /// Returns the number of INTERP_STACK_SLOT_SIZE slots consumed by a token. + /// Struct tokens consume max((size + 7) / 8, 1) slots; all others consume 1. + /// + public static int TokenToSlotCount(string token) + { + if (token[0] is not ('S' or 'A') || token.Length < 2) + return 1; + + int size = GetStructSize(token); + return Math.Max((size + 7) / 8, 1); + } + + internal static int GetStructSize(string token) + { + return int.Parse(token.Substring(1)); + } + + // Legacy single-char overloads — still used by consumers that don't encounter S tokens. + public static string CharToNativeType(char c) => TokenToNativeType(c.ToString()); + public static string CharToNameType(char c) => TokenToNameType(c.ToString()); + public static string CharToArgType(char c) => TokenToArgType(c.ToString()); + + public static bool IsVoidSignature(string signature) => signature[0] == 'v'; +} + +internal sealed class InvalidSignatureCharException : Exception +{ + public char Char { get; private set; } + + public InvalidSignatureCharException(char c) : base($"Can't handle signature '{c}'") => Char = c; +} diff --git a/src/tasks/WasmAppBuilder/coreclr/SignatureMapper.cs b/src/tasks/WasmAppBuilder/coreclr/SignatureMapper.cs index c1f07ec01067d9..395cd7dbb75049 100644 --- a/src/tasks/WasmAppBuilder/coreclr/SignatureMapper.cs +++ b/src/tasks/WasmAppBuilder/coreclr/SignatureMapper.cs @@ -13,7 +13,7 @@ namespace Microsoft.WebAssembly.Build.Tasks.CoreClr; // Computes Wasm signature strings from reflection metadata. // The signature string format is documented in docs/design/coreclr/botr/readytorun-format.md // (section "Wasm Signature String Encoding"). -internal static class SignatureMapper +internal static partial class SignatureMapper { // Hardcoded struct layouts for types that crossgen2 encodes as struct tokens. // The fully general case is handled by crossgen2's type system; these @@ -262,126 +262,6 @@ private static bool HasNumericElementType(Type t) return sb.ToString(); } - /// - /// Parses a signature string into individual tokens. - /// Single-char types produce one-char tokens; struct encodings produce multi-char tokens like - /// "S8" or "A32", and a multi-slot parameter produces a two-char token like "l2" or "V4". - /// The 'a' and 'p' suffixes are included as their own tokens. - /// - public static List ParseSignatureTokens(string signature) - { - var tokens = new List(); - int i = 0; - while (i < signature.Length) - { - if (signature[i] is 'S' or 'A') - { - int start = i; - i++; // skip 'S'/'A' - while (i < signature.Length && char.IsDigit(signature[i])) - i++; - tokens.Add(signature.Substring(start, i - start)); - } - else if (signature[i] is 'l' or 'V' && i + 1 < signature.Length && char.IsDigit(signature[i + 1])) - { - tokens.Add(signature.Substring(i, 2)); - i += 2; - } - else - { - tokens.Add(signature[i].ToString()); - i++; - } - } - - return tokens; - } - - /// - /// True for a token describing a type passed by value across several wasm parameters - /// ("l2", "V2", "V4"). Interop signatures do not use these today. - /// - private static bool IsMultiSlotToken(string token) - => token.Length == 2 && token[0] is 'l' or 'V' && char.IsDigit(token[1]); - - private static void RejectMultiSlotToken(string token) - { - if (IsMultiSlotToken(token)) - throw new NotSupportedException($"Multi-slot signature token '{token}' is not supported in interop thunks"); - } - - public static string TokenToNativeType(string token) - { - RejectMultiSlotToken(token); - return token[0] switch - { - 'v' => "void", - 'i' => "int32_t", - 'l' => "int64_t", - 'f' => "float", - 'd' => "double", - 'S' or 'A' => "int32_t", - 'T' => "int32_t", - 'p' => "PCODE", - _ => throw new InvalidSignatureCharException(token[0]) - }; - } - - public static string TokenToNameType(string token) - { - RejectMultiSlotToken(token); - return token[0] switch - { - 'v' => "Void", - 'i' => "I32", - 'l' => "I64", - 'f' => "F32", - 'd' => "F64", - 'S' or 'A' => token, - 'T' => "This", - 'p' => "PE", - _ => throw new InvalidSignatureCharException(token[0]) - }; - } - - public static string TokenToArgType(string token) - { - RejectMultiSlotToken(token); - return token[0] switch - { - 'i' => "ARG_I32", - 'l' => "ARG_I64", - 'f' => "ARG_F32", - 'd' => "ARG_F64", - 'S' or 'A' => "ARG_IND", - 'T' => "ARG_I32", - _ => throw new InvalidSignatureCharException(token[0]) - }; - } - - /// - /// Returns the number of INTERP_STACK_SLOT_SIZE slots consumed by a token. - /// Struct tokens consume max((size + 7) / 8, 1) slots; all others consume 1. - /// - public static int TokenToSlotCount(string token) - { - if (token[0] is not ('S' or 'A') || token.Length < 2) - return 1; - - int size = GetStructSize(token); - return Math.Max((size + 7) / 8, 1); - } - - internal static int GetStructSize(string token) - { - return int.Parse(token.Substring(1)); - } - - // Legacy single-char overloads — still used by consumers that don't encounter S tokens. - public static string CharToNativeType(char c) => TokenToNativeType(c.ToString()); - public static string CharToNameType(char c) => TokenToNameType(c.ToString()); - public static string CharToArgType(char c) => TokenToArgType(c.ToString()); - public static string TypeToNameType(Type t, LogAdapter log) { char? c = TypeToChar(t, log, out _); @@ -390,13 +270,4 @@ public static string TypeToNameType(Type t, LogAdapter log) return CharToNameType(c.Value); } - - public static bool IsVoidSignature(string signature) => signature[0] == 'v'; -} - -internal sealed class InvalidSignatureCharException : Exception -{ - public char Char { get; private set; } - - public InvalidSignatureCharException(char c) : base($"Can't handle signature '{c}'") => Char = c; } From 9f6b27ab6dc690b5c227377238c7a163fc01914b Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Sat, 29 Aug 2026 12:56:32 +0200 Subject: [PATCH 04/11] [wasm] Pin the generated thunks against crossgen2's lowering The R2R-to-interpreter thunks are written by the WasmAppBuilder generator but called by code crossgen2 emits, and nothing at run time can detect a disagreement, so test that the two agree. GeneratedThunkMatchesLoweredWasmSignature lowers a managed signature with crossgen2 and requires the generator to produce the same wasm parameter arity and types for the resulting key. That catches a missing hidden return buffer, which is what returning the struct by value produces, but it cannot catch two same-typed parameters being swapped: 'this' and retBuf are both i32, so a transposition leaves the type sequence identical. ThunkParametersFollowCrossgen2Order covers the positions separately, which is the only way that case is visible. Both were checked by reintroducing each bug: the transposition fails 3 cases in the ordering theory and none elsewhere, and dropping the return buffer fails 10. --- .../ILCompiler.ReadyToRun.Tests.csproj | 8 + .../WasmArgumentLayoutTests.cs | 191 ++++++++++++++++++ 2 files changed, 199 insertions(+) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj index 1c77956d8bbe6b..13811c03632ff8 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj @@ -37,6 +37,14 @@ + + + + + + ThunkShapes() + { + // (description, isStatic, expected signature key) + TheoryData data = new() + { + { "void(int)", true, "vip" }, + { "int(int)", false, "iTip" }, + { "long(double)", true, "ldp" }, + { "int(float, double, long)", false, "iTfdlp" }, + { "void()", false, "vTp" }, + // Struct returns: the shape both encoders previously disagreed on. + { "S8()", true, "S8p" }, + { "S8(int)", false, "S8Tip" }, + { "S16(long, int)", true, "S16lip" }, + { "S12(S12, int)", false, "S12TS12ip" }, + { "void(S8)", false, "vTS8p" }, + }; + + return data; + } + + /// + /// The R2R-to-interpreter thunks are written by the WasmAppBuilder generator but called by code + /// crossgen2 emits, so the two have to agree on the wasm signature behind every key. This checks + /// arity and types: a missing hidden return buffer — which is what returning the struct by value + /// produces, since the compiler then inserts its own pointer ahead of the stack pointer — or a + /// wrong scalar width shows up here. It cannot see two same-typed parameters swapped; + /// covers the order. + /// + [Theory] + [MemberData(nameof(ThunkShapes))] + public void GeneratedThunkMatchesLoweredWasmSignature(string description, bool isStatic, string expectedKey) + { + _output.WriteLine($"{description} => {expectedKey}"); + + ReadyToRunCompilerContext context = CreateWasmContext(); + WasmSignature lowered = WasmLowering.GetSignature(MakeThunkSignature(context, description, isStatic), WasmLowering.LoweringFlags.None); + + Assert.Equal(expectedKey, lowered.SignatureString); + Assert.Equal(lowered.FuncType.Params.Types.ToArray(), GetThunkWasmParameters(lowered.SignatureString)); + } + + /// + /// A generic context argument is an ordinary pointer slot that follows the return buffer, so it + /// encodes exactly like a leading int parameter and must lay out the same way. + /// + [Fact] + public void GenericContextArgumentFollowsTheReturnBuffer() + { + ReadyToRunCompilerContext context = CreateWasmContext(); + MethodSignature signature = new( + MethodSignatureFlags.None, + genericParameterCount: 0, + returnType: MakeBlobOfSize(context, 8), + parameters: Array.Empty()); + + WasmSignature lowered = WasmLowering.GetSignature(signature, WasmLowering.LoweringFlags.HasGenericContextArg); + + Assert.Equal("S8Tip", lowered.SignatureString); + Assert.Equal(lowered.FuncType.Params.Types.ToArray(), GetThunkWasmParameters(lowered.SignatureString)); + } + + public static TheoryData ThunkParameterOrder() + { + // Transcribed from WasmR2RToInterpreterThunkNode.EmitCode, which stores 'this' from the + // local after the stack pointer and then reads the buffer from + // retBufLocalIndex = 1 + (hasThis ? 1 : 0). A generic context is an ordinary slot that + // follows the buffer, so it is spelled like any other argument here. + return new TheoryData + { + { Array.Empty(), true, new[] { "retBuf" } }, + { new[] { "i" }, true, new[] { "retBuf", "arg0" } }, + { new[] { "T" }, true, new[] { "arg0", "retBuf" } }, + { new[] { "T", "i" }, true, new[] { "arg0", "retBuf", "arg1" } }, + { new[] { "T", "i", "S8" }, true, new[] { "arg0", "retBuf", "arg1", "arg2" } }, + { new[] { "T", "i" }, false, new[] { "arg0", "arg1" } }, + { new[] { "i", "l" }, false, new[] { "arg0", "arg1" } }, + }; + } + + /// + /// The transposition that shipped broken, and the reason it needs its own test: for an instance + /// method the hidden return buffer follows this, and both are i32, so getting the + /// order wrong leaves the wasm type sequence unchanged. Comparing lowered parameter types cannot + /// see it — only the positions can. + /// + [Theory] + [MemberData(nameof(ThunkParameterOrder))] + public void ThunkParametersFollowCrossgen2Order(string[] args, bool isStructReturn, string[] expectedNames) + { + Assert.Equal( + expectedNames, + PortableEntryPointThunkSignature.GetDeclaredParameters(args, isStructReturn).Select(static p => p.Name)); + } + + /// + /// Builds the signature named by in . + /// + private static MethodSignature MakeThunkSignature(ReadyToRunCompilerContext context, string description, bool isStatic) + { + TypeDesc Int32() => context.GetWellKnownType(WellKnownType.Int32); + + (TypeDesc Return, TypeDesc[] Parameters) shape = description switch + { + "void(int)" => (context.GetWellKnownType(WellKnownType.Void), new[] { Int32() }), + "int(int)" => (Int32(), new[] { Int32() }), + "long(double)" => (context.GetWellKnownType(WellKnownType.Int64), new[] { context.GetWellKnownType(WellKnownType.Double) }), + "int(float, double, long)" => (Int32(), new[] + { + context.GetWellKnownType(WellKnownType.Single), + context.GetWellKnownType(WellKnownType.Double), + context.GetWellKnownType(WellKnownType.Int64), + }), + "void()" => (context.GetWellKnownType(WellKnownType.Void), Array.Empty()), + "S8()" => (MakeBlobOfSize(context, 8), Array.Empty()), + "S8(int)" => (MakeBlobOfSize(context, 8), new[] { Int32() }), + "S16(long, int)" => (MakeBlobOfSize(context, 16), new[] { context.GetWellKnownType(WellKnownType.Int64), Int32() }), + "S12(S12, int)" => (MakeBlobOfSize(context, 12), new TypeDesc[] { MakeBlobOfSize(context, 12), Int32() }), + "void(S8)" => (context.GetWellKnownType(WellKnownType.Void), new TypeDesc[] { MakeBlobOfSize(context, 8) }), + _ => throw new ArgumentOutOfRangeException(nameof(description), description, null), + }; + + return new MethodSignature( + isStatic ? MethodSignatureFlags.Static : MethodSignatureFlags.None, + genericParameterCount: 0, + returnType: shape.Return, + parameters: shape.Parameters); + } + + /// + /// A multi-field struct of the given size. It needs more than one field: a single-field struct + /// is lowered to the field's own type and would never reach the S<N> encoding. + /// + private static DefType MakeBlobOfSize(ReadyToRunCompilerContext context, int size) + { + TypeDesc int32 = context.GetWellKnownType(WellKnownType.Int32); + TypeDesc int64 = context.GetWellKnownType(WellKnownType.Int64); + + DefType result = size switch + { + 8 => MakeValueTuple(context, int32, int32), + 12 => MakeValueTuple(context, int32, int32, int32), + 16 => MakeValueTuple(context, int64, int64), + _ => throw new ArgumentOutOfRangeException(nameof(size), size, null), + }; + + Assert.Equal(size, result.InstanceFieldSize.AsInt); + return result; + } + + /// + /// The wasm parameters of the thunk the generator emits for . + /// The stack pointer comes from the WASM_CALLABLE_FUNC macro and the portable entrypoint is + /// appended after the declared arguments, so neither is part of the generator's own list. + /// + private static List GetThunkWasmParameters(string signatureKey) + { + List tokens = SignatureMapper.ParseSignatureTokens(signatureKey); + string returnToken = tokens[0]; + + Assert.Equal("p", tokens[tokens.Count - 1]); + tokens.RemoveAt(tokens.Count - 1); + List args = tokens.GetRange(1, tokens.Count - 1); + + List parameters = new() { WasmValueType.I32 }; // callersStackPointer + foreach (PortableEntryPointThunkSignature.Parameter parameter in + PortableEntryPointThunkSignature.GetDeclaredParameters(args, PortableEntryPointThunkSignature.IsStructToken(returnToken))) + { + parameters.Add(NativeTypeToWasmType(parameter.NativeType)); + } + + parameters.Add(WasmValueType.I32); // portable entrypoint + return parameters; + } + + /// + /// Maps a C type the generator emits onto the wasm type clang gives it on wasm32. Reading the + /// generator's own type strings, rather than re-deriving them from the signature, is what makes + /// this a check of the emitted thunk instead of a restatement of the encoding rules. + /// + private static WasmValueType NativeTypeToWasmType(string nativeType) => nativeType switch + { + "int32_t" or "int8_t*" or "PCODE" => WasmValueType.I32, + "int64_t" => WasmValueType.I64, + "float" => WasmValueType.F32, + "double" => WasmValueType.F64, + _ => throw new ArgumentOutOfRangeException(nameof(nativeType), nativeType, null), + }; + private static DefType MakeValueTuple(ReadyToRunCompilerContext context, params TypeDesc[] fields) => ((MetadataType)context.SystemModule.GetType( "System"u8, System.Text.Encoding.UTF8.GetBytes($"ValueTuple`{fields.Length}"))) From 78f8cce056a361c556ea5cade852ebb480804ad3 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Sat, 29 Aug 2026 13:12:25 +0200 Subject: [PATCH 05/11] [wasm] Store float and double thunk arguments as themselves The generated portable entrypoint thunks wrote every argument through (int64_t), which converts a float or double to its integer value instead of storing its bits: 1.5 arrived as 1. The interpreter reads those slots back as ARG_F32/ARG_F64, so every floating point argument crossing an R2R to interpreter call was corrupt. This was a regression for Iidp and Ildp, whose hand-written thunks used a typed 'double args[1]' and stored the value correctly, and was wrong from the start for the float and double shapes the generator discovered on its own. The unit tests cannot see this: they compare parameter types and positions, not the stores. The runtime test added alongside covers it. --- .../callhelpers-portable-entrypoints.cpp | 36 +++++++++---------- .../coreclr/InterpToNativeGenerator.cs | 13 +++++-- 2 files changed, 28 insertions(+), 21 deletions(-) diff --git a/src/coreclr/vm/wasm/browser/callhelpers-portable-entrypoints.cpp b/src/coreclr/vm/wasm/browser/callhelpers-portable-entrypoints.cpp index cc2cf0af600c8e..1a55da8df19ed6 100644 --- a/src/coreclr/vm/wasm/browser/callhelpers-portable-entrypoints.cpp +++ b/src/coreclr/vm/wasm/browser/callhelpers-portable-entrypoints.cpp @@ -192,9 +192,9 @@ namespace } transitionBlock; transitionBlock.block.m_ReturnAddress = 0; transitionBlock.block.m_StackPointer = callersStackPointer; - transitionBlock.args[0] = (int64_t)arg0; - transitionBlock.args[1] = (int64_t)arg1; - transitionBlock.args[2] = (int64_t)arg2; + *(double*)&transitionBlock.args[0] = arg0; + *(double*)&transitionBlock.args[1] = arg1; + *(double*)&transitionBlock.args[2] = arg2; static_assert(offsetof(decltype(transitionBlock), args) == sizeof(TransitionBlock), "Args array must be at a TransitionBlock offset from the start of the block"); double result = 0; @@ -212,8 +212,8 @@ namespace } transitionBlock; transitionBlock.block.m_ReturnAddress = 0; transitionBlock.block.m_StackPointer = callersStackPointer; - transitionBlock.args[0] = (int64_t)arg0; - transitionBlock.args[1] = (int64_t)arg1; + *(double*)&transitionBlock.args[0] = arg0; + *(double*)&transitionBlock.args[1] = arg1; static_assert(offsetof(decltype(transitionBlock), args) == sizeof(TransitionBlock), "Args array must be at a TransitionBlock offset from the start of the block"); double result = 0; @@ -231,7 +231,7 @@ namespace } transitionBlock; transitionBlock.block.m_ReturnAddress = 0; transitionBlock.block.m_StackPointer = callersStackPointer; - transitionBlock.args[0] = (int64_t)arg0; + *(double*)&transitionBlock.args[0] = 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"); @@ -250,7 +250,7 @@ namespace } transitionBlock; transitionBlock.block.m_ReturnAddress = 0; transitionBlock.block.m_StackPointer = callersStackPointer; - transitionBlock.args[0] = (int64_t)arg0; + *(double*)&transitionBlock.args[0] = arg0; static_assert(offsetof(decltype(transitionBlock), args) == sizeof(TransitionBlock), "Args array must be at a TransitionBlock offset from the start of the block"); double result = 0; @@ -268,9 +268,9 @@ namespace } transitionBlock; transitionBlock.block.m_ReturnAddress = 0; transitionBlock.block.m_StackPointer = callersStackPointer; - transitionBlock.args[0] = (int64_t)arg0; - transitionBlock.args[1] = (int64_t)arg1; - transitionBlock.args[2] = (int64_t)arg2; + *(float*)&transitionBlock.args[0] = arg0; + *(float*)&transitionBlock.args[1] = arg1; + *(float*)&transitionBlock.args[2] = arg2; static_assert(offsetof(decltype(transitionBlock), args) == sizeof(TransitionBlock), "Args array must be at a TransitionBlock offset from the start of the block"); float result = 0; @@ -288,8 +288,8 @@ namespace } transitionBlock; transitionBlock.block.m_ReturnAddress = 0; transitionBlock.block.m_StackPointer = callersStackPointer; - transitionBlock.args[0] = (int64_t)arg0; - transitionBlock.args[1] = (int64_t)arg1; + *(float*)&transitionBlock.args[0] = arg0; + *(float*)&transitionBlock.args[1] = arg1; static_assert(offsetof(decltype(transitionBlock), args) == sizeof(TransitionBlock), "Args array must be at a TransitionBlock offset from the start of the block"); float result = 0; @@ -307,7 +307,7 @@ namespace } transitionBlock; transitionBlock.block.m_ReturnAddress = 0; transitionBlock.block.m_StackPointer = callersStackPointer; - transitionBlock.args[0] = (int64_t)arg0; + *(float*)&transitionBlock.args[0] = 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"); @@ -326,7 +326,7 @@ namespace } transitionBlock; transitionBlock.block.m_ReturnAddress = 0; transitionBlock.block.m_StackPointer = callersStackPointer; - transitionBlock.args[0] = (int64_t)arg0; + *(float*)&transitionBlock.args[0] = arg0; static_assert(offsetof(decltype(transitionBlock), args) == sizeof(TransitionBlock), "Args array must be at a TransitionBlock offset from the start of the block"); float result = 0; @@ -644,7 +644,7 @@ namespace } transitionBlock; transitionBlock.block.m_ReturnAddress = 0; transitionBlock.block.m_StackPointer = callersStackPointer; - transitionBlock.args[0] = (int64_t)arg0; + *(double*)&transitionBlock.args[0] = arg0; static_assert(offsetof(decltype(transitionBlock), args) == sizeof(TransitionBlock), "Args array must be at a TransitionBlock offset from the start of the block"); int32_t result = 0; @@ -868,7 +868,7 @@ namespace } transitionBlock; transitionBlock.block.m_ReturnAddress = 0; transitionBlock.block.m_StackPointer = callersStackPointer; - transitionBlock.args[0] = (int64_t)arg0; + *(double*)&transitionBlock.args[0] = arg0; static_assert(offsetof(decltype(transitionBlock), args) == sizeof(TransitionBlock), "Args array must be at a TransitionBlock offset from the start of the block"); int64_t result = 0; @@ -1241,7 +1241,7 @@ namespace } transitionBlock; transitionBlock.block.m_ReturnAddress = 0; transitionBlock.block.m_StackPointer = callersStackPointer; - transitionBlock.args[0] = (int64_t)arg0; + *(double*)&transitionBlock.args[0] = 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"); @@ -1261,7 +1261,7 @@ namespace } transitionBlock; transitionBlock.block.m_ReturnAddress = 0; transitionBlock.block.m_StackPointer = callersStackPointer; - transitionBlock.args[0] = (int64_t)arg0; + *(float*)&transitionBlock.args[0] = 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"); diff --git a/src/tasks/WasmAppBuilder/coreclr/InterpToNativeGenerator.cs b/src/tasks/WasmAppBuilder/coreclr/InterpToNativeGenerator.cs index 87d3d13f41a59c..26b6c30034160b 100644 --- a/src/tasks/WasmAppBuilder/coreclr/InterpToNativeGenerator.cs +++ b/src/tasks/WasmAppBuilder/coreclr/InterpToNativeGenerator.cs @@ -149,9 +149,16 @@ private static void EmitPortableEntryPoints(StreamWriter w, IEnumerable if (t[0] == 'A') slot = (slot + 1) & ~1; - stores.Add(IsStructToken(t) - ? $" memcpy(&transitionBlock.args[{slot}], arg{i}, {SignatureMapper.GetStructSize(t)});" - : $" transitionBlock.args[{slot}] = (int64_t)arg{i};"); + // A float or double occupies its slot as itself: casting to int64_t would convert the + // value rather than store its bits, and the interpreter reads the slot back through + // ARG_F32/ARG_F64. + stores.Add(t switch + { + _ when IsStructToken(t) => $" memcpy(&transitionBlock.args[{slot}], arg{i}, {SignatureMapper.GetStructSize(t)});", + "f" => $" *(float*)&transitionBlock.args[{slot}] = arg{i};", + "d" => $" *(double*)&transitionBlock.args[{slot}] = arg{i};", + _ => $" transitionBlock.args[{slot}] = (int64_t)arg{i};", + }); slot += SignatureMapper.TokenToSlotCount(t); } From e81d6f7bb9c90a25b1d8982cbdb4c42a91985006 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Sat, 29 Aug 2026 13:29:21 +0200 Subject: [PATCH 06/11] [wasm] Pass the return buffer explicitly on interpreter-to-R2R calls too The struct-returning shapes added to the pregenerated cookie list feed both generators, and only the R2R-to-interpreter half was corrected: the interpreter-to-R2R thunks still called through a pointer declared as returning the struct by value, so the compiler inserted its own sret pointer at parameter 0, ahead of the stack pointer, while the R2R callee expects (callersStackPointer, [this], retBuf, args..., portableEntrypoint). Every parameter involved is an i32, so the mismatch passed call_indirect type checking and corrupted memory instead. It showed up as an out-of-bounds access during EventSource start-up, far from the call, and it broke tests that have nothing to do with struct returns: WasmR2RStructAlignment passes on main, passes with the P/Invoke table regenerated, and failed once the thunk table was generated. Native callees keep the by-value form, which is what their own C ABI gives them. --- .../browser/callhelpers-interp-to-managed.cpp | 36 +++++++++---------- .../coreclr/InterpToNativeGenerator.cs | 24 +++++++++++-- 2 files changed, 39 insertions(+), 21 deletions(-) diff --git a/src/coreclr/vm/wasm/browser/callhelpers-interp-to-managed.cpp b/src/coreclr/vm/wasm/browser/callhelpers-interp-to-managed.cpp index e4ff4d11cb9403..43d3322345eb40 100644 --- a/src/coreclr/vm/wasm/browser/callhelpers-interp-to-managed.cpp +++ b/src/coreclr/vm/wasm/browser/callhelpers-interp-to-managed.cpp @@ -27,64 +27,64 @@ namespace NOINLINE static void CallFunc_This_S12_I32_RetS12_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) { alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; - wasm_ret_S12 (*fptr)(int*, int32_t, int32_t, int32_t, PCODE) = *(wasm_ret_S12 (**)(int*, int32_t, int32_t, int32_t, PCODE))(pPortableEntryPoint); - *((wasm_ret_S12*)pRet) = (*fptr)(&framePointer, ARG_I32(0), ARG_IND(1), ARG_I32(3), pPortableEntryPoint); + void (*fptr)(int*, int32_t, int8_t*, int32_t, int32_t, PCODE) = *(void (**)(int*, int32_t, int8_t*, int32_t, int32_t, PCODE))(pPortableEntryPoint); + (*fptr)(&framePointer, ARG_I32(0), pRet, ARG_IND(1), ARG_I32(3), pPortableEntryPoint); } NOINLINE static void CallFunc_This_I32_S12_I32_RetS12_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) { alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; - wasm_ret_S12 (*fptr)(int*, int32_t, int32_t, int32_t, int32_t, PCODE) = *(wasm_ret_S12 (**)(int*, int32_t, int32_t, int32_t, int32_t, PCODE))(pPortableEntryPoint); - *((wasm_ret_S12*)pRet) = (*fptr)(&framePointer, ARG_I32(0), ARG_I32(1), ARG_IND(2), ARG_I32(4), pPortableEntryPoint); + void (*fptr)(int*, int32_t, int8_t*, int32_t, int32_t, int32_t, PCODE) = *(void (**)(int*, int32_t, int8_t*, int32_t, int32_t, int32_t, PCODE))(pPortableEntryPoint); + (*fptr)(&framePointer, ARG_I32(0), pRet, ARG_I32(1), ARG_IND(2), ARG_I32(4), pPortableEntryPoint); } NOINLINE static void CallFunc_This_I32_I32_I32_RetS12_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) { alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; - wasm_ret_S12 (*fptr)(int*, int32_t, int32_t, int32_t, int32_t, PCODE) = *(wasm_ret_S12 (**)(int*, int32_t, int32_t, int32_t, int32_t, PCODE))(pPortableEntryPoint); - *((wasm_ret_S12*)pRet) = (*fptr)(&framePointer, ARG_I32(0), ARG_I32(1), ARG_I32(2), ARG_I32(3), pPortableEntryPoint); + void (*fptr)(int*, int32_t, int8_t*, int32_t, int32_t, int32_t, PCODE) = *(void (**)(int*, int32_t, int8_t*, int32_t, int32_t, int32_t, PCODE))(pPortableEntryPoint); + (*fptr)(&framePointer, ARG_I32(0), pRet, ARG_I32(1), ARG_I32(2), ARG_I32(3), pPortableEntryPoint); } NOINLINE static void CallFunc_This_I32_I32_RetS12_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) { alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; - wasm_ret_S12 (*fptr)(int*, int32_t, int32_t, int32_t, PCODE) = *(wasm_ret_S12 (**)(int*, int32_t, int32_t, int32_t, PCODE))(pPortableEntryPoint); - *((wasm_ret_S12*)pRet) = (*fptr)(&framePointer, ARG_I32(0), ARG_I32(1), ARG_I32(2), pPortableEntryPoint); + void (*fptr)(int*, int32_t, int8_t*, int32_t, int32_t, PCODE) = *(void (**)(int*, int32_t, int8_t*, int32_t, int32_t, PCODE))(pPortableEntryPoint); + (*fptr)(&framePointer, ARG_I32(0), pRet, ARG_I32(1), ARG_I32(2), pPortableEntryPoint); } NOINLINE static void CallFunc_This_RetS16_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) { alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; - wasm_ret_S16 (*fptr)(int*, int32_t, PCODE) = *(wasm_ret_S16 (**)(int*, int32_t, PCODE))(pPortableEntryPoint); - *((wasm_ret_S16*)pRet) = (*fptr)(&framePointer, ARG_I32(0), pPortableEntryPoint); + void (*fptr)(int*, int32_t, int8_t*, PCODE) = *(void (**)(int*, int32_t, int8_t*, PCODE))(pPortableEntryPoint); + (*fptr)(&framePointer, ARG_I32(0), pRet, pPortableEntryPoint); } NOINLINE static void CallFunc_This_RetS1_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) { alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; - wasm_ret_S1 (*fptr)(int*, int32_t, PCODE) = *(wasm_ret_S1 (**)(int*, int32_t, PCODE))(pPortableEntryPoint); - *((wasm_ret_S1*)pRet) = (*fptr)(&framePointer, ARG_I32(0), pPortableEntryPoint); + void (*fptr)(int*, int32_t, int8_t*, PCODE) = *(void (**)(int*, int32_t, int8_t*, PCODE))(pPortableEntryPoint); + (*fptr)(&framePointer, ARG_I32(0), pRet, pPortableEntryPoint); } NOINLINE static void CallFunc_This_I32_I32_I32_RetS8_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) { alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; - wasm_ret_S8 (*fptr)(int*, int32_t, int32_t, int32_t, int32_t, PCODE) = *(wasm_ret_S8 (**)(int*, int32_t, int32_t, int32_t, int32_t, PCODE))(pPortableEntryPoint); - *((wasm_ret_S8*)pRet) = (*fptr)(&framePointer, ARG_I32(0), ARG_I32(1), ARG_I32(2), ARG_I32(3), pPortableEntryPoint); + void (*fptr)(int*, int32_t, int8_t*, int32_t, int32_t, int32_t, PCODE) = *(void (**)(int*, int32_t, int8_t*, int32_t, int32_t, int32_t, PCODE))(pPortableEntryPoint); + (*fptr)(&framePointer, ARG_I32(0), pRet, ARG_I32(1), ARG_I32(2), ARG_I32(3), pPortableEntryPoint); } NOINLINE static void CallFunc_This_I32_RetS8_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) { alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; - wasm_ret_S8 (*fptr)(int*, int32_t, int32_t, PCODE) = *(wasm_ret_S8 (**)(int*, int32_t, int32_t, PCODE))(pPortableEntryPoint); - *((wasm_ret_S8*)pRet) = (*fptr)(&framePointer, ARG_I32(0), ARG_I32(1), pPortableEntryPoint); + void (*fptr)(int*, int32_t, int8_t*, int32_t, PCODE) = *(void (**)(int*, int32_t, int8_t*, int32_t, PCODE))(pPortableEntryPoint); + (*fptr)(&framePointer, ARG_I32(0), pRet, ARG_I32(1), pPortableEntryPoint); } NOINLINE static void CallFunc_This_RetS8_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) { alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; - wasm_ret_S8 (*fptr)(int*, int32_t, PCODE) = *(wasm_ret_S8 (**)(int*, int32_t, PCODE))(pPortableEntryPoint); - *((wasm_ret_S8*)pRet) = (*fptr)(&framePointer, ARG_I32(0), pPortableEntryPoint); + void (*fptr)(int*, int32_t, int8_t*, PCODE) = *(void (**)(int*, int32_t, int8_t*, PCODE))(pPortableEntryPoint); + (*fptr)(&framePointer, ARG_I32(0), pRet, pPortableEntryPoint); } NOINLINE static void CallFunc_F64_F64_F64_RetF64_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) diff --git a/src/tasks/WasmAppBuilder/coreclr/InterpToNativeGenerator.cs b/src/tasks/WasmAppBuilder/coreclr/InterpToNativeGenerator.cs index 26b6c30034160b..9b2f0178f918ba 100644 --- a/src/tasks/WasmAppBuilder/coreclr/InterpToNativeGenerator.cs +++ b/src/tasks/WasmAppBuilder/coreclr/InterpToNativeGenerator.cs @@ -274,7 +274,25 @@ private static void Emit(StreamWriter w, IEnumerable cookies) var args = Args(tokens); - var portableEntryPointComma = args.Count > 0 ? ", " : ""; + var argTypes = args.Select(static t => SignatureMapper.TokenToNativeType(t)).ToList(); + var argValues = ArgsWithSlotOffsets(args).ToList(); + + // R2R code takes a struct return through an explicit buffer that follows the stack + // pointer and 'this', so the pointer this calls through must spell it out. Declaring + // the call as returning the struct would make the compiler insert its own sret + // pointer at parameter 0 instead, ahead of the stack pointer. Native callees keep the + // by-value form, which is what their own C ABI gives them. + bool isStructReturn = isPortableEntryPointCall && IsStructToken(returnToken); + if (isStructReturn) + { + argTypes.Insert(args.Count > 0 && args[0] == "T" ? 1 : 0, "int8_t*"); + argValues.Insert(args.Count > 0 && args[0] == "T" ? 1 : 0, "pRet"); + } + + string fptrReturn = isStructReturn ? "void" : result.nativeType; + string assignResult = isStructReturn || result.isVoid ? "" : $"*(({result.nativeType}*)pRet) = "; + + var portableEntryPointComma = argTypes.Count > 0 ? ", " : ""; var portableEntrypointDeclaration = isPortableEntryPointCall ? portableEntryPointComma + "PCODE" : ""; var portableEntrypointParam = isPortableEntryPointCall ? portableEntryPointComma + "pPortableEntryPoint" : ""; var portableEntrypointStackDeclaration = isPortableEntryPointCall ? "int*, " : ""; @@ -285,8 +303,8 @@ private static void Emit(StreamWriter w, IEnumerable cookies) {{(isPortableEntryPointCall ? "NOINLINE " : "")}}static void {{CallFuncName(args, SignatureMapper.TokenToNameType(returnToken), isPortableEntryPointCall)}}(PCODE {{(isPortableEntryPointCall ? "pPortableEntryPoint" : "pcode")}}, int8_t* pArgs, int8_t* pRet) {{{(isPortableEntryPointCall ? "\n alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK;" : "")}} - {{result.nativeType}} (*fptr)({{portableEntrypointStackDeclaration}}{{string.Join(", ", args.Select(static t => SignatureMapper.TokenToNativeType(t)))}}{{portableEntrypointDeclaration}}) = {{portableEntrypointPointerRD}}({{result.nativeType}} ({{portableEntrypointPointerRD}}*)({{portableEntrypointStackDeclaration}}{{string.Join(", ", args.Select(static t => SignatureMapper.TokenToNativeType(t)))}}{{portableEntrypointDeclaration}})){{(isPortableEntryPointCall ? "(pPortableEntryPoint)" : "pcode")}}; - {{(result.isVoid ? "" : "*" + "((" + result.nativeType + "*)pRet) = ")}}(*fptr)({{portableEntrypointStackParam}}{{string.Join(", ", ArgsWithSlotOffsets(args))}}{{portableEntrypointParam}}); + {{fptrReturn}} (*fptr)({{portableEntrypointStackDeclaration}}{{string.Join(", ", argTypes)}}{{portableEntrypointDeclaration}}) = {{portableEntrypointPointerRD}}({{fptrReturn}} ({{portableEntrypointPointerRD}}*)({{portableEntrypointStackDeclaration}}{{string.Join(", ", argTypes)}}{{portableEntrypointDeclaration}})){{(isPortableEntryPointCall ? "(pPortableEntryPoint)" : "pcode")}}; + {{assignResult}}(*fptr)({{portableEntrypointStackParam}}{{string.Join(", ", argValues)}}{{portableEntrypointParam}}); } """); From f5c19a89bbcd7aa28da8fcf530652cdf1599bcc7 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Sat, 29 Aug 2026 13:29:35 +0200 Subject: [PATCH 07/11] [wasm] Add a runtime test for the R2R and interpreter transitions Methods marked BypassReadyToRun are skipped by crossgen2 and run interpreted while the rest of the assembly is compiled, so a single test assembly can put a thunk on a call in either direction. Cover the shapes the thunk table carries: struct returns of 8, 12 and 16 bytes from both instance and static methods, struct arguments, mixed float, double and long scalars, void, and an interpreted method calling back into compiled code. Every case checks a value rather than only that the call returned. Nothing here traps when it goes wrong: the stack pointer, the return buffer, 'this' and every by-reference argument are i32, so a thunk with its parameters in the wrong order still passes call_indirect type checking and quietly returns bad data. The callees are NoInlining so that an inlined callee cannot skip the transition and leave the test passing without exercising anything. This covers two bugs the unit tests structurally cannot reach, both found by running it: float and double arguments stored through an integer cast, and the interpreter-to-R2R struct return convention. --- .../WasmInterpreterTransitions.cs | 165 ++++++++++++++++++ .../WasmInterpreterTransitions.csproj | 14 ++ 2 files changed, 179 insertions(+) create mode 100644 src/tests/readytorun/wasm/WasmInterpreterTransitions/WasmInterpreterTransitions.cs create mode 100644 src/tests/readytorun/wasm/WasmInterpreterTransitions/WasmInterpreterTransitions.csproj diff --git a/src/tests/readytorun/wasm/WasmInterpreterTransitions/WasmInterpreterTransitions.cs b/src/tests/readytorun/wasm/WasmInterpreterTransitions/WasmInterpreterTransitions.cs new file mode 100644 index 00000000000000..b93ee83ed46229 --- /dev/null +++ b/src/tests/readytorun/wasm/WasmInterpreterTransitions/WasmInterpreterTransitions.cs @@ -0,0 +1,165 @@ +// 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.Runtime; +using System.Runtime.CompilerServices; +using Xunit; + +namespace System.Runtime +{ + [AttributeUsage(AttributeTargets.Method)] + internal sealed class BypassReadyToRunAttribute : Attribute + { + } +} + +/// +/// Exercises the thunks that carry a call between R2R code and the interpreter on wasm. Methods +/// marked are skipped by crossgen2 and so run interpreted, +/// while the rest of the assembly is compiled, which puts a thunk on every call between the two. +/// +/// Each case returns values chosen so that a wrong answer is visible. That matters more than usual +/// here: the stack pointer, the hidden return buffer, 'this' and every by-reference argument are +/// all i32, so a thunk whose parameters are in the wrong order still passes wasm's call_indirect +/// type check. It does not trap — it writes through the wrong pointer, and the only symptom is +/// data that quietly comes back wrong. +/// +public class WasmInterpreterTransitions +{ + private const int A = 0x11223344; + private const int B = 0x55667788; + private const int C = 0x1234567; + private const long Wide = 0x1122334455667788; + + public struct S8 + { + public int A; + public int B; + } + + public struct S12 + { + public int A; + public int B; + public int C; + } + + public struct S16 + { + public long A; + public long B; + } + + private readonly int _state = C; + + [Fact] + public static void TestEntryPoint() + { + WasmInterpreterTransitions self = new(); + + // R2R -> interpreted, struct returns. The return buffer follows 'this' for an instance + // method and the stack pointer for a static one, which is where the two forms differ. + S8 s8 = self.InterpretedInstanceReturnsS8(A); + Assert.Equal(A, s8.A); + Assert.Equal(C, s8.B); + + S8 staticS8 = InterpretedStaticReturnsS8(A); + Assert.Equal(A, staticS8.A); + Assert.Equal(B, staticS8.B); + + S16 s16 = self.InterpretedInstanceReturnsS16(); + Assert.Equal(Wide, s16.A); + Assert.Equal(C, s16.B); + + S16 staticS16 = InterpretedStaticReturnsS16(Wide, A); + Assert.Equal(Wide, staticS16.A); + Assert.Equal(A, staticS16.B); + + // A struct that is not a whole number of 8-byte slots, passed and returned. + S12 s12 = self.InterpretedInstanceRoundTripsS12(new S12 { A = A, B = B, C = C }, A); + Assert.Equal(B, s12.A); + Assert.Equal(C, s12.B); + Assert.Equal(A, s12.C); + + // R2R -> interpreted, scalar and void shapes. + Assert.Equal(A + C, self.InterpretedInstanceReturnsI32(A)); + Assert.Equal(Wide, InterpretedStaticReturnsI64(1.5)); + Assert.Equal(A + B + C, InterpretedStaticSumsFour(A, B, C, 0)); + Assert.Equal(A, self.InterpretedInstanceMixedScalars(1.5f, 2.5, Wide)); + + // A struct argument arrives as the address of its interpreter stack slot. + Assert.Equal(A + B, self.InterpretedInstanceTakesS8(new S8 { A = A, B = B })); + + s_sideEffect = 0; + self.InterpretedInstanceTakesS8ReturnsVoid(new S8 { A = A, B = B }); + Assert.Equal(A + B, s_sideEffect); + + // interpreted -> R2R, the opposite direction over the same shapes. + Assert.Equal(A + C, self.InterpretedCallsBackIntoR2R()); + + S16 fromInterpreter = self.InterpretedCallsR2RReturningS16(); + Assert.Equal(Wide, fromInterpreter.A); + Assert.Equal(C, fromInterpreter.B); + } + + private static int s_sideEffect; + + [BypassReadyToRun] + [MethodImpl(MethodImplOptions.NoInlining)] + private S8 InterpretedInstanceReturnsS8(int a) => new S8 { A = a, B = _state }; + + [BypassReadyToRun] + [MethodImpl(MethodImplOptions.NoInlining)] + private static S8 InterpretedStaticReturnsS8(int a) => new S8 { A = a, B = B }; + + [BypassReadyToRun] + [MethodImpl(MethodImplOptions.NoInlining)] + private S16 InterpretedInstanceReturnsS16() => new S16 { A = Wide, B = _state }; + + [BypassReadyToRun] + [MethodImpl(MethodImplOptions.NoInlining)] + private static S16 InterpretedStaticReturnsS16(long wide, int a) => new S16 { A = wide, B = a }; + + [BypassReadyToRun] + [MethodImpl(MethodImplOptions.NoInlining)] + private S12 InterpretedInstanceRoundTripsS12(S12 value, int a) => new S12 { A = value.B, B = value.C, C = a }; + + [BypassReadyToRun] + [MethodImpl(MethodImplOptions.NoInlining)] + private int InterpretedInstanceReturnsI32(int a) => a + _state; + + [BypassReadyToRun] + [MethodImpl(MethodImplOptions.NoInlining)] + private static long InterpretedStaticReturnsI64(double unused) => Wide; + + [BypassReadyToRun] + [MethodImpl(MethodImplOptions.NoInlining)] + private static int InterpretedStaticSumsFour(int a, int b, int c, int d) => a + b + c + d; + + [BypassReadyToRun] + [MethodImpl(MethodImplOptions.NoInlining)] + private int InterpretedInstanceMixedScalars(float f, double d, long l) => l == Wide && f == 1.5f && d == 2.5 ? A : 0; + + [BypassReadyToRun] + [MethodImpl(MethodImplOptions.NoInlining)] + private int InterpretedInstanceTakesS8(S8 value) => value.A + value.B; + + [BypassReadyToRun] + [MethodImpl(MethodImplOptions.NoInlining)] + private void InterpretedInstanceTakesS8ReturnsVoid(S8 value) => s_sideEffect = value.A + value.B; + + [BypassReadyToRun] + [MethodImpl(MethodImplOptions.NoInlining)] + private int InterpretedCallsBackIntoR2R() => R2RInstanceReturnsI32(A); + + [BypassReadyToRun] + [MethodImpl(MethodImplOptions.NoInlining)] + private S16 InterpretedCallsR2RReturningS16() => R2RInstanceReturnsS16(); + + [MethodImpl(MethodImplOptions.NoInlining)] + private int R2RInstanceReturnsI32(int a) => a + _state; + + [MethodImpl(MethodImplOptions.NoInlining)] + private S16 R2RInstanceReturnsS16() => new S16 { A = Wide, B = _state }; +} diff --git a/src/tests/readytorun/wasm/WasmInterpreterTransitions/WasmInterpreterTransitions.csproj b/src/tests/readytorun/wasm/WasmInterpreterTransitions/WasmInterpreterTransitions.csproj new file mode 100644 index 00000000000000..092b4f60c765b9 --- /dev/null +++ b/src/tests/readytorun/wasm/WasmInterpreterTransitions/WasmInterpreterTransitions.csproj @@ -0,0 +1,14 @@ + + + true + true + + true + true + + + + + From 3589d5c2ff90a62db777169c830139124a553fd7 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Sat, 29 Aug 2026 18:23:46 +0200 Subject: [PATCH 08/11] [wasm] Generate the multi-slot 'l2' thunk shape 'l2' is a 16-byte value (Int128, UInt128, Decimal128) passed by value across two i64 wasm parameters. SignatureMapper rejected the token outright, so IS16l2ip had to stay hand-written: one signature token maps to several C parameters, which the generator could not express. Expand a multi-slot token into one parameter per slot in both directions, as argLo and argHi, stored into consecutive transition block slots and read back through consecutive ARG_I64 accessors. TokenToNativeType and TokenToArgType still reject an unexpanded multi-slot token, so one cannot quietly collapse into a single parameter -- the shape every parameter bug in this area has taken. 'V2' and 'V4' now fail with a specific message instead: these thunks have no portable spelling for a v128 and nothing generates one today. The generated CallInterpreter_L2_I32_RetS16 is identical to the hand-written thunk it replaces, which was itself verified against the wasm crossgen2 emits. This empties the hand-written table, so it is removed. Browser is unaffected; every thunk it uses is generated. wasi has no generated table yet, so it now has no portable entrypoint thunks at all and a call needing one reports a missing key. wasi had 17 before this series and needs its own generated table, which requires a wasi testhost to scan. --- .../browser/callhelpers-interp-to-managed.cpp | 8 +++ .../callhelpers-portable-entrypoints.cpp | 20 +++++++ src/coreclr/vm/wasm/helpers.cpp | 52 +++---------------- .../coreclr/InterpToNativeGenerator.cs | 48 ++++++++++++++--- .../coreclr/ManagedToNativeGenerator.cs | 2 +- .../PortableEntryPointThunkSignature.cs | 19 ++++++- .../coreclr/SignatureMapper.Tokens.cs | 49 ++++++++++++++--- 7 files changed, 135 insertions(+), 63 deletions(-) diff --git a/src/coreclr/vm/wasm/browser/callhelpers-interp-to-managed.cpp b/src/coreclr/vm/wasm/browser/callhelpers-interp-to-managed.cpp index 43d3322345eb40..a82b48a6c00b7e 100644 --- a/src/coreclr/vm/wasm/browser/callhelpers-interp-to-managed.cpp +++ b/src/coreclr/vm/wasm/browser/callhelpers-interp-to-managed.cpp @@ -59,6 +59,13 @@ namespace (*fptr)(&framePointer, ARG_I32(0), pRet, pPortableEntryPoint); } + NOINLINE static void CallFunc_L2_I32_RetS16_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + void (*fptr)(int*, int8_t*, int64_t, int64_t, int32_t, PCODE) = *(void (**)(int*, int8_t*, int64_t, int64_t, int32_t, PCODE))(pPortableEntryPoint); + (*fptr)(&framePointer, pRet, ARG_I64(0), ARG_I64(1), ARG_I32(2), pPortableEntryPoint); + } + NOINLINE static void CallFunc_This_RetS1_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) { alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; @@ -952,6 +959,7 @@ const StringToWasmSigThunk g_wasmThunks[] = { { "MS12Tiiip", (void*)&CallFunc_This_I32_I32_I32_RetS12_PE }, { "MS12Tiip", (void*)&CallFunc_This_I32_I32_RetS12_PE }, { "MS16Tp", (void*)&CallFunc_This_RetS16_PE }, + { "MS16l2ip", (void*)&CallFunc_L2_I32_RetS16_PE }, { "MS1Tp", (void*)&CallFunc_This_RetS1_PE }, { "MS8Tiiip", (void*)&CallFunc_This_I32_I32_I32_RetS8_PE }, { "MS8Tip", (void*)&CallFunc_This_I32_RetS8_PE }, diff --git a/src/coreclr/vm/wasm/browser/callhelpers-portable-entrypoints.cpp b/src/coreclr/vm/wasm/browser/callhelpers-portable-entrypoints.cpp index 1a55da8df19ed6..cbe20c0ef987a0 100644 --- a/src/coreclr/vm/wasm/browser/callhelpers-portable-entrypoints.cpp +++ b/src/coreclr/vm/wasm/browser/callhelpers-portable-entrypoints.cpp @@ -110,6 +110,25 @@ namespace return; } + FCDECL4(void, CallInterpreter_L2_I32_RetS16, int8_t*, int64_t, int64_t, int32_t); + WASM_CALLABLE_FUNC_5(void, CallInterpreter_L2_I32_RetS16, int8_t* retBuf, int64_t arg0Lo, int64_t arg0Hi, int32_t arg1, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[3]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + transitionBlock.args[0] = arg0Lo; + transitionBlock.args[1] = arg0Hi; + transitionBlock.args[2] = (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"); + + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), retBuf); + return; + } + FCDECL2(void, CallInterpreter_This_RetS1, int32_t, int8_t*); WASM_CALLABLE_FUNC_3(void, CallInterpreter_This_RetS1, int32_t arg0, int8_t* retBuf, PCODE portableEntrypoint) { @@ -1372,6 +1391,7 @@ const StringToWasmSigThunk g_wasmGeneratedPortableEntryPointThunks[] = { { "IS12Tiiip", (void*)&CallInterpreter_This_I32_I32_I32_RetS12 }, { "IS12Tiip", (void*)&CallInterpreter_This_I32_I32_RetS12 }, { "IS16Tp", (void*)&CallInterpreter_This_RetS16 }, + { "IS16l2ip", (void*)&CallInterpreter_L2_I32_RetS16 }, { "IS1Tp", (void*)&CallInterpreter_This_RetS1 }, { "IS8Tiiip", (void*)&CallInterpreter_This_I32_I32_I32_RetS8 }, { "IS8Tip", (void*)&CallInterpreter_This_I32_RetS8 }, diff --git a/src/coreclr/vm/wasm/helpers.cpp b/src/coreclr/vm/wasm/helpers.cpp index a9500254e57a34..ec80c10724a537 100644 --- a/src/coreclr/vm/wasm/helpers.cpp +++ b/src/coreclr/vm/wasm/helpers.cpp @@ -17,45 +17,9 @@ void ExecuteInterpretedMethodWithArgs_PortableEntryPoint(PCODE portableEntrypoint, TransitionBlock* block, size_t argsSize, int8_t* retBuff); // ------------------------------------------------- -// Logic that will eventually mostly be pregenerated for R2R to interpreter code -// ------------------------------------------------- -namespace -{ - // 'l2' is a 16-byte multi-slot argument (Int128/UInt128/Decimal128) passed by value in two i64 - // slots. A struct return is NOT a C return value here: crossgen2 passes the return buffer as an - // explicit parameter (after callersStackPointer, or after 'this' for an instance method), so the - // thunk must be void — returning a struct would make the compiler insert its own sret pointer at - // parameter 0 and shift everything. - FCDECL4(void, CallInterpreter_L2_I32_RetS16, int8_t*, int64_t, int64_t, int32_t); - WASM_CALLABLE_FUNC_5(void, CallInterpreter_L2_I32_RetS16, int8_t* retBuf, int64_t arg0Lo, int64_t arg0Hi, int32_t arg1, PCODE portableEntrypoint) - { - struct - { - TransitionBlock block; - int64_t args[3]; - } transitionBlock; - transitionBlock.block.m_ReturnAddress = 0; - transitionBlock.block.m_StackPointer = callersStackPointer; - transitionBlock.args[0] = arg0Lo; - transitionBlock.args[1] = arg0Hi; - transitionBlock.args[2] = (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"); - - ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), retBuf); - } - -} - -const StringToWasmSigThunk g_wasmPortableEntryPointThunks[] = { - // Every other shape is emitted by the WasmAppBuilder generator into - // callhelpers-portable-entrypoints.cpp; only the multi-slot 'l2' form, which the generator - // cannot express, is still written by hand. - { "IS16l2ip", (void*)&CallInterpreter_L2_I32_RetS16 }, -}; - -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 +// The R2R to interpreter thunks are generated into callhelpers-portable-entrypoints.cpp by the +// WasmAppBuilder generator. Only browser has one; wasi has no generated table yet, so a call that +// needs a thunk there reports a missing key rather than finding one. // ------------------------------------------------- extern "C" void STDCALL CallCountingStubCode() @@ -1229,7 +1193,7 @@ namespace // Printed rather than only asserted: the caller's assert compiles out in release, where these // gaps surface, and cannot carry the key. A miss leaves the entry point's table index 0 and // traps later as "null function", far from here. - printf("WASM: no R2R-to-interpreter thunk for signature key '%s'. Add it to g_wasmPortableEntryPointThunks.\n", keyBuffer); + printf("WASM: no R2R-to-interpreter thunk for signature key '%s'. Add it to pregeneratedInterpreterToNativeSignatures in ManagedToNativeGenerator and regenerate.\n", keyBuffer); } return thunk; } @@ -1345,15 +1309,11 @@ void InitializeWasmThunkCaches() { StringToWasmSigThunkHash* newTable = new StringToWasmSigThunkHash(); - size_t total = g_wasmPortableEntryPointThunksCount; + size_t total = 0; #ifdef TARGET_BROWSER - total += g_wasmGeneratedPortableEntryPointThunksCount; + total = g_wasmGeneratedPortableEntryPointThunksCount; #endif newTable->Reallocate(total * 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); - } #ifdef TARGET_BROWSER for (size_t i = 0; i < g_wasmGeneratedPortableEntryPointThunksCount; i++) { diff --git a/src/tasks/WasmAppBuilder/coreclr/InterpToNativeGenerator.cs b/src/tasks/WasmAppBuilder/coreclr/InterpToNativeGenerator.cs index 9b2f0178f918ba..821214d6e6420b 100644 --- a/src/tasks/WasmAppBuilder/coreclr/InterpToNativeGenerator.cs +++ b/src/tasks/WasmAppBuilder/coreclr/InterpToNativeGenerator.cs @@ -152,13 +152,21 @@ private static void EmitPortableEntryPoints(StreamWriter w, IEnumerable // A float or double occupies its slot as itself: casting to int64_t would convert the // value rather than store its bits, and the interpreter reads the slot back through // ARG_F32/ARG_F64. - stores.Add(t switch + if (SignatureMapper.TryGetMultiSlotToken(t, out int multiSlotCount)) { - _ when IsStructToken(t) => $" memcpy(&transitionBlock.args[{slot}], arg{i}, {SignatureMapper.GetStructSize(t)});", - "f" => $" *(float*)&transitionBlock.args[{slot}] = arg{i};", - "d" => $" *(double*)&transitionBlock.args[{slot}] = arg{i};", - _ => $" transitionBlock.args[{slot}] = (int64_t)arg{i};", - }); + for (int s = 0; s < multiSlotCount; s++) + stores.Add($" transitionBlock.args[{slot + s}] = {PortableEntryPointThunkSignature.MultiSlotParameterName(i, s, multiSlotCount)};"); + } + else + { + stores.Add(t switch + { + _ when IsStructToken(t) => $" memcpy(&transitionBlock.args[{slot}], arg{i}, {SignatureMapper.GetStructSize(t)});", + "f" => $" *(float*)&transitionBlock.args[{slot}] = arg{i};", + "d" => $" *(double*)&transitionBlock.args[{slot}] = arg{i};", + _ => $" transitionBlock.args[{slot}] = (int64_t)arg{i};", + }); + } slot += SignatureMapper.TokenToSlotCount(t); } @@ -274,7 +282,21 @@ private static void Emit(StreamWriter w, IEnumerable cookies) var args = Args(tokens); - var argTypes = args.Select(static t => SignatureMapper.TokenToNativeType(t)).ToList(); + var argTypes = new List(); + foreach (var argToken in args) + { + if (SignatureMapper.TryGetMultiSlotToken(argToken, out int multiSlotCount)) + { + string slotType = SignatureMapper.MultiSlotElementNativeType(argToken); + for (int s = 0; s < multiSlotCount; s++) + argTypes.Add(slotType); + } + else + { + argTypes.Add(SignatureMapper.TokenToNativeType(argToken)); + } + } + var argValues = ArgsWithSlotOffsets(args).ToList(); // R2R code takes a struct return through an explicit buffer that follows the stack @@ -348,7 +370,17 @@ static List ArgsWithSlotOffsets(List args) slot = (slot + 1) & ~1; } - result.Add($"{SignatureMapper.TokenToArgType(token)}({slot})"); + if (SignatureMapper.TryGetMultiSlotToken(token, out int multiSlotCount)) + { + string slotArgType = SignatureMapper.MultiSlotElementArgType(token); + for (int s = 0; s < multiSlotCount; s++) + result.Add($"{slotArgType}({slot + s})"); + } + else + { + result.Add($"{SignatureMapper.TokenToArgType(token)}({slot})"); + } + slot += SignatureMapper.TokenToSlotCount(token); } diff --git a/src/tasks/WasmAppBuilder/coreclr/ManagedToNativeGenerator.cs b/src/tasks/WasmAppBuilder/coreclr/ManagedToNativeGenerator.cs index 17370f83947085..7e92fbfd71beb4 100644 --- a/src/tasks/WasmAppBuilder/coreclr/ManagedToNativeGenerator.cs +++ b/src/tasks/WasmAppBuilder/coreclr/ManagedToNativeGenerator.cs @@ -131,7 +131,7 @@ private void ExecuteInternal(LogAdapter log) "vTS8p", "vTS8ip", "vTS8S8p", "vTiS8p", "vTS12p", "vTS32p", "vTiiS8S8iip", "S8Tp", "S8Tip", "S8Tiiip", "S12Tiip", "S12Tiiip", "S12TS12ip", "S16Tp", "S1Tp", "S12TiS12ip", - "viiiip", "iiiiiiiip", "iiiiiiiiip", "idp", "ldp", "iiS8p", "iS8p", + "viiiip", "iiiiiiiip", "iiiiiiiiip", "idp", "ldp", "iiS8p", "iS8p", "S16l2ip", }; IEnumerable cookies = pinvoke.Generate(PInvokeModules, IgnoredPInvokeModules, PInvokeOutputPath, ReversePInvokeOutputPath); diff --git a/src/tasks/WasmAppBuilder/coreclr/PortableEntryPointThunkSignature.cs b/src/tasks/WasmAppBuilder/coreclr/PortableEntryPointThunkSignature.cs index 7d0b3e9c8fba73..2f70740df0807f 100644 --- a/src/tasks/WasmAppBuilder/coreclr/PortableEntryPointThunkSignature.cs +++ b/src/tasks/WasmAppBuilder/coreclr/PortableEntryPointThunkSignature.cs @@ -28,6 +28,12 @@ public Parameter(string nativeType, string name) public static string DeclType(string token) => IsStructToken(token) ? "int8_t*" : SignatureMapper.TokenToNativeType(token); + /// + /// The name of one slot of a multi-slot argument, which occupies several wasm parameters. + /// + public static string MultiSlotParameterName(int argIndex, int slot, int slotCount) + => slotCount == 2 ? $"arg{argIndex}{(slot == 0 ? "Lo" : "Hi")}" : $"arg{argIndex}_{slot}"; + /// /// The thunk's declared C parameters, in the order crossgen2 passes them: /// (callersStackPointer, [this], retBuf, args..., portableEntrypoint). The stack pointer comes @@ -38,7 +44,18 @@ public static List GetDeclaredParameters(IReadOnlyList args, { var parameters = new List(args.Count + 1); for (int i = 0; i < args.Count; i++) - parameters.Add(new Parameter(DeclType(args[i]), $"arg{i}")); + { + if (SignatureMapper.TryGetMultiSlotToken(args[i], out int slotCount)) + { + string slotType = SignatureMapper.MultiSlotElementNativeType(args[i]); + for (int slot = 0; slot < slotCount; slot++) + parameters.Add(new Parameter(slotType, MultiSlotParameterName(i, slot, slotCount))); + } + else + { + parameters.Add(new Parameter(DeclType(args[i]), $"arg{i}")); + } + } if (isStructReturn) { diff --git a/src/tasks/WasmAppBuilder/coreclr/SignatureMapper.Tokens.cs b/src/tasks/WasmAppBuilder/coreclr/SignatureMapper.Tokens.cs index 1c4e2e79ad4d78..a025fb04d702cb 100644 --- a/src/tasks/WasmAppBuilder/coreclr/SignatureMapper.Tokens.cs +++ b/src/tasks/WasmAppBuilder/coreclr/SignatureMapper.Tokens.cs @@ -47,16 +47,41 @@ public static List ParseSignatureTokens(string signature) } /// - /// True for a token describing a type passed by value across several wasm parameters - /// ("l2", "V2", "V4"). Interop signatures do not use these today. + /// True for a token describing a type passed by value across several wasm parameters ("l2", + /// "V2", "V4"), reporting how many it occupies. A caller has to expand it into one parameter per + /// slot; the single-type accessors below still reject it so an unexpanded token cannot slip + /// through as one parameter. /// - private static bool IsMultiSlotToken(string token) - => token.Length == 2 && token[0] is 'l' or 'V' && char.IsDigit(token[1]); + public static bool TryGetMultiSlotToken(string token, out int slotCount) + { + if (token.Length == 2 && token[0] is 'l' or 'V' && char.IsDigit(token[1])) + { + slotCount = token[1] - '0'; + return true; + } + + slotCount = 0; + return false; + } + + /// The type of one slot of a multi-slot token. + public static string MultiSlotElementNativeType(string token) => token[0] switch + { + 'l' => "int64_t", + _ => throw new NotSupportedException($"Multi-slot token '{token}' has no native type these thunks can spell") + }; + + /// The interpreter stack accessor for one slot of a multi-slot token. + public static string MultiSlotElementArgType(string token) => token[0] switch + { + 'l' => "ARG_I64", + _ => throw new NotSupportedException($"Multi-slot token '{token}' has no interpreter stack accessor") + }; private static void RejectMultiSlotToken(string token) { - if (IsMultiSlotToken(token)) - throw new NotSupportedException($"Multi-slot signature token '{token}' is not supported in interop thunks"); + if (TryGetMultiSlotToken(token, out _)) + throw new NotSupportedException($"Multi-slot signature token '{token}' must be expanded into one parameter per slot"); } public static string TokenToNativeType(string token) @@ -78,7 +103,9 @@ public static string TokenToNativeType(string token) public static string TokenToNameType(string token) { - RejectMultiSlotToken(token); + if (TryGetMultiSlotToken(token, out int multiSlotCount)) + return $"{char.ToUpperInvariant(token[0])}{multiSlotCount}"; + return token[0] switch { 'v' => "Void", @@ -114,6 +141,14 @@ public static string TokenToArgType(string token) /// public static int TokenToSlotCount(string token) { + if (TryGetMultiSlotToken(token, out int multiSlotCount)) + { + // An i64 slot is one interpreter slot; a v128 slot would be two, but nothing spells one yet. + return token[0] == 'l' + ? multiSlotCount + : throw new NotSupportedException($"Multi-slot token '{token}' has no known interpreter slot size"); + } + if (token[0] is not ('S' or 'A') || token.Length < 2) return 1; From a895b80de678069a93d940ccb68784c400783488 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Sat, 29 Aug 2026 19:21:45 +0200 Subject: [PATCH 09/11] [wasm] Generate the wasi call tables The wasi portable entrypoint table was left behind when the generator took over the browser one: wasi had 17 hand-written thunks on main, then 1, then none once the multi-slot shape removed the last of them. Generate wasi's table too, so it has the same 70 entries as browser, and drop the browser-only guards on the CMake source entry, the extern declarations and the cache population. The other wasi tables (interp-to-managed, pinvoke, reverse) are regenerated at the same time; they were stale against the current scan set. This restores wasi to the state it had before this series and no further. It does not make R2R work there: an R2R image is a wasm module that has to be instantiated at run time against the runtime's memory and indirect function table, which only the JavaScript host does (libCorerun.js, host/assets.ts). wasi has no equivalent, so its table stays latent until that exists. Generated but not compiled locally: 'build.cmd -os wasi -subset clr' fails on a Windows host because the cross-components build passes clang flags to cl.exe (D8021: invalid numeric argument '/Werror'), which predates this change. The table was produced from a managed-only 'clr.corelib+libs' build whose testhost matches browser's exactly -- 181 assemblies, no difference in either direction. CI's wasi leg is the first thing that will compile the file. --- src/coreclr/vm/CMakeLists.txt | 9 +- src/coreclr/vm/wasm/callhelpers.hpp | 2 - src/coreclr/vm/wasm/helpers.cpp | 11 +- .../wasi/callhelpers-interp-to-managed.cpp | 332 ++++ .../vm/wasm/wasi/callhelpers-pinvoke.cpp | 10 +- .../wasi/callhelpers-portable-entrypoints.cpp | 1461 +++++++++++++++++ 6 files changed, 1803 insertions(+), 22 deletions(-) create mode 100644 src/coreclr/vm/wasm/wasi/callhelpers-portable-entrypoints.cpp diff --git a/src/coreclr/vm/CMakeLists.txt b/src/coreclr/vm/CMakeLists.txt index 27444575de3b82..aa6b9d10ffaff7 100644 --- a/src/coreclr/vm/CMakeLists.txt +++ b/src/coreclr/vm/CMakeLists.txt @@ -975,12 +975,9 @@ elseif(CLR_CMAKE_TARGET_ARCH_WASM) endif() # Unlike the other generated call helpers, the portable entrypoint thunks are not regenerated # per app, so they belong in the shipped static library rather than in cee_wks_gen. - # Only browser has a generated table today; wasi keeps using the hand-written thunks alone. - if (CLR_CMAKE_TARGET_BROWSER) - list(APPEND VM_SOURCES_WKS_ARCH - ${ARCH_SOURCES_DIR}/${WASM_THUNK_SUBDIR}/callhelpers-portable-entrypoints.cpp - ) - endif(CLR_CMAKE_TARGET_BROWSER) + list(APPEND VM_SOURCES_WKS_ARCH + ${ARCH_SOURCES_DIR}/${WASM_THUNK_SUBDIR}/callhelpers-portable-entrypoints.cpp + ) set(VM_SOURCES_WKS_GEN ${ARCH_SOURCES_DIR}/${WASM_THUNK_SUBDIR}/callhelpers-interp-to-managed.cpp ${ARCH_SOURCES_DIR}/${WASM_THUNK_SUBDIR}/callhelpers-reverse.cpp diff --git a/src/coreclr/vm/wasm/callhelpers.hpp b/src/coreclr/vm/wasm/callhelpers.hpp index 8c35140704e4f7..ddd394cb3f2ec6 100644 --- a/src/coreclr/vm/wasm/callhelpers.hpp +++ b/src/coreclr/vm/wasm/callhelpers.hpp @@ -43,10 +43,8 @@ struct StringToWasmSigThunk extern const StringToWasmSigThunk g_wasmThunks[]; extern const size_t g_wasmThunksCount; -#ifdef TARGET_BROWSER extern const StringToWasmSigThunk g_wasmGeneratedPortableEntryPointThunks[]; extern const size_t g_wasmGeneratedPortableEntryPointThunksCount; -#endif struct ReverseThunkMapValue { diff --git a/src/coreclr/vm/wasm/helpers.cpp b/src/coreclr/vm/wasm/helpers.cpp index ec80c10724a537..77b31d3dacaf86 100644 --- a/src/coreclr/vm/wasm/helpers.cpp +++ b/src/coreclr/vm/wasm/helpers.cpp @@ -18,8 +18,7 @@ void ExecuteInterpretedMethodWithArgs_PortableEntryPoint(PCODE portableEntrypoin // ------------------------------------------------- // The R2R to interpreter thunks are generated into callhelpers-portable-entrypoints.cpp by the -// WasmAppBuilder generator. Only browser has one; wasi has no generated table yet, so a call that -// needs a thunk there reports a missing key rather than finding one. +// WasmAppBuilder generator, one table per target. // ------------------------------------------------- extern "C" void STDCALL CallCountingStubCode() @@ -1309,17 +1308,11 @@ void InitializeWasmThunkCaches() { StringToWasmSigThunkHash* newTable = new StringToWasmSigThunkHash(); - size_t total = 0; -#ifdef TARGET_BROWSER - total = g_wasmGeneratedPortableEntryPointThunksCount; -#endif - newTable->Reallocate(total * StringToWasmSigThunkHash::s_density_factor_denominator / StringToWasmSigThunkHash::s_density_factor_numerator + 1); -#ifdef TARGET_BROWSER + newTable->Reallocate(g_wasmGeneratedPortableEntryPointThunksCount * StringToWasmSigThunkHash::s_density_factor_denominator / StringToWasmSigThunkHash::s_density_factor_numerator + 1); for (size_t i = 0; i < g_wasmGeneratedPortableEntryPointThunksCount; i++) { newTable->Add(g_wasmGeneratedPortableEntryPointThunks[i].key, g_wasmGeneratedPortableEntryPointThunks[i].value); } -#endif portableEntrypointThunkCache = newTable; } } diff --git a/src/coreclr/vm/wasm/wasi/callhelpers-interp-to-managed.cpp b/src/coreclr/vm/wasm/wasi/callhelpers-interp-to-managed.cpp index 0033e59ea2469e..1f128e9aa9f94c 100644 --- a/src/coreclr/vm/wasm/wasi/callhelpers-interp-to-managed.cpp +++ b/src/coreclr/vm/wasm/wasi/callhelpers-interp-to-managed.cpp @@ -17,9 +17,83 @@ #define ARG_I64(i) (*(int64_t*)ARG_ADDR(i)) #define ARG_F32(i) (*(float*)ARG_ADDR(i)) #define ARG_F64(i) (*(double*)ARG_ADDR(i)) +typedef struct { char d[1]; } wasm_ret_S1; +typedef struct { char d[8]; } wasm_ret_S8; +typedef struct { char d[12]; } wasm_ret_S12; +typedef struct { char d[16]; } wasm_ret_S16; namespace { + NOINLINE static void CallFunc_This_S12_I32_RetS12_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + void (*fptr)(int*, int32_t, int8_t*, int32_t, int32_t, PCODE) = *(void (**)(int*, int32_t, int8_t*, int32_t, int32_t, PCODE))(pPortableEntryPoint); + (*fptr)(&framePointer, ARG_I32(0), pRet, ARG_IND(1), ARG_I32(3), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_I32_S12_I32_RetS12_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + void (*fptr)(int*, int32_t, int8_t*, int32_t, int32_t, int32_t, PCODE) = *(void (**)(int*, int32_t, int8_t*, int32_t, int32_t, int32_t, PCODE))(pPortableEntryPoint); + (*fptr)(&framePointer, ARG_I32(0), pRet, ARG_I32(1), ARG_IND(2), ARG_I32(4), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_I32_I32_I32_RetS12_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + void (*fptr)(int*, int32_t, int8_t*, int32_t, int32_t, int32_t, PCODE) = *(void (**)(int*, int32_t, int8_t*, int32_t, int32_t, int32_t, PCODE))(pPortableEntryPoint); + (*fptr)(&framePointer, ARG_I32(0), pRet, ARG_I32(1), ARG_I32(2), ARG_I32(3), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_I32_I32_RetS12_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + void (*fptr)(int*, int32_t, int8_t*, int32_t, int32_t, PCODE) = *(void (**)(int*, int32_t, int8_t*, int32_t, int32_t, PCODE))(pPortableEntryPoint); + (*fptr)(&framePointer, ARG_I32(0), pRet, ARG_I32(1), ARG_I32(2), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_RetS16_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + void (*fptr)(int*, int32_t, int8_t*, PCODE) = *(void (**)(int*, int32_t, int8_t*, PCODE))(pPortableEntryPoint); + (*fptr)(&framePointer, ARG_I32(0), pRet, pPortableEntryPoint); + } + + NOINLINE static void CallFunc_L2_I32_RetS16_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + void (*fptr)(int*, int8_t*, int64_t, int64_t, int32_t, PCODE) = *(void (**)(int*, int8_t*, int64_t, int64_t, int32_t, PCODE))(pPortableEntryPoint); + (*fptr)(&framePointer, pRet, ARG_I64(0), ARG_I64(1), ARG_I32(2), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_RetS1_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + void (*fptr)(int*, int32_t, int8_t*, PCODE) = *(void (**)(int*, int32_t, int8_t*, PCODE))(pPortableEntryPoint); + (*fptr)(&framePointer, ARG_I32(0), pRet, pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_I32_I32_I32_RetS8_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + void (*fptr)(int*, int32_t, int8_t*, int32_t, int32_t, int32_t, PCODE) = *(void (**)(int*, int32_t, int8_t*, int32_t, int32_t, int32_t, PCODE))(pPortableEntryPoint); + (*fptr)(&framePointer, ARG_I32(0), pRet, ARG_I32(1), ARG_I32(2), ARG_I32(3), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_I32_RetS8_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + void (*fptr)(int*, int32_t, int8_t*, int32_t, PCODE) = *(void (**)(int*, int32_t, int8_t*, int32_t, PCODE))(pPortableEntryPoint); + (*fptr)(&framePointer, ARG_I32(0), pRet, ARG_I32(1), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_RetS8_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + void (*fptr)(int*, int32_t, int8_t*, PCODE) = *(void (**)(int*, int32_t, int8_t*, PCODE))(pPortableEntryPoint); + (*fptr)(&framePointer, ARG_I32(0), pRet, pPortableEntryPoint); + } + NOINLINE static void CallFunc_F64_F64_F64_RetF64_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) { alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; @@ -166,6 +240,97 @@ namespace *((int32_t*)pRet) = (*fptr)(ARG_IND(0), ARG_I32(1), ARG_I32(2), ARG_I32(3), ARG_I32(4), ARG_I32(5)); } + NOINLINE static void CallFunc_S8_RetI32_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + int32_t (*fptr)(int*, int32_t, PCODE) = *(int32_t (**)(int*, int32_t, PCODE))(pPortableEntryPoint); + *((int32_t*)pRet) = (*fptr)(&framePointer, ARG_IND(0), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_S12_I32_RetI32_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) 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))(pPortableEntryPoint); + *((int32_t*)pRet) = (*fptr)(&framePointer, ARG_I32(0), ARG_IND(1), ARG_I32(3), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_S12_RetI32_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + int32_t (*fptr)(int*, int32_t, int32_t, PCODE) = *(int32_t (**)(int*, int32_t, int32_t, PCODE))(pPortableEntryPoint); + *((int32_t*)pRet) = (*fptr)(&framePointer, ARG_I32(0), ARG_IND(1), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_S8_S8_RetI32_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) 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))(pPortableEntryPoint); + *((int32_t*)pRet) = (*fptr)(&framePointer, ARG_I32(0), ARG_IND(1), ARG_IND(2), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_S8_I32_S8_I32_RetI32_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) 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))(pPortableEntryPoint); + *((int32_t*)pRet) = (*fptr)(&framePointer, ARG_I32(0), ARG_IND(1), ARG_I32(2), ARG_IND(3), ARG_I32(4), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_S8_I32_I32_I32_RetI32_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) 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))(pPortableEntryPoint); + *((int32_t*)pRet) = (*fptr)(&framePointer, ARG_I32(0), ARG_IND(1), ARG_I32(2), ARG_I32(3), ARG_I32(4), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_S8_I32_RetI32_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) 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))(pPortableEntryPoint); + *((int32_t*)pRet) = (*fptr)(&framePointer, ARG_I32(0), ARG_IND(1), ARG_I32(2), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_S8_RetI32_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + int32_t (*fptr)(int*, int32_t, int32_t, PCODE) = *(int32_t (**)(int*, int32_t, int32_t, PCODE))(pPortableEntryPoint); + *((int32_t*)pRet) = (*fptr)(&framePointer, ARG_I32(0), ARG_IND(1), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_I32_S12_RetI32_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) 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))(pPortableEntryPoint); + *((int32_t*)pRet) = (*fptr)(&framePointer, ARG_I32(0), ARG_I32(1), ARG_IND(2), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_I32_S8_RetI32_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) 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))(pPortableEntryPoint); + *((int32_t*)pRet) = (*fptr)(&framePointer, ARG_I32(0), ARG_I32(1), ARG_IND(2), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_I32_I32_I32_I32_RetI32_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) 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))(pPortableEntryPoint); + *((int32_t*)pRet) = (*fptr)(&framePointer, ARG_I32(0), ARG_I32(1), ARG_I32(2), ARG_I32(3), ARG_I32(4), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_I32_I32_I32_RetI32_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) 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))(pPortableEntryPoint); + *((int32_t*)pRet) = (*fptr)(&framePointer, ARG_I32(0), ARG_I32(1), ARG_I32(2), ARG_I32(3), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_I32_I32_RetI32_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) 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))(pPortableEntryPoint); + *((int32_t*)pRet) = (*fptr)(&framePointer, ARG_I32(0), ARG_I32(1), ARG_I32(2), pPortableEntryPoint); + } + NOINLINE static void CallFunc_This_I32_RetI32_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) { alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; @@ -180,6 +345,13 @@ namespace *((int32_t*)pRet) = (*fptr)(&framePointer, ARG_I32(0), pPortableEntryPoint); } + NOINLINE static void CallFunc_F64_RetI32_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + int32_t (*fptr)(int*, double, PCODE) = *(int32_t (**)(int*, double, PCODE))(pPortableEntryPoint); + *((int32_t*)pRet) = (*fptr)(&framePointer, ARG_F64(0), pPortableEntryPoint); + } + static void CallFunc_I32_RetI32(PCODE pcode, int8_t* pArgs, int8_t* pRet) { int32_t (*fptr)(int32_t) = (int32_t (*)(int32_t))pcode; @@ -198,6 +370,13 @@ namespace *((int32_t*)pRet) = (*fptr)(ARG_I32(0), ARG_IND(1), ARG_I32(2), ARG_I32(3), ARG_I32(4)); } + NOINLINE static void CallFunc_I32_S8_RetI32_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + int32_t (*fptr)(int*, int32_t, int32_t, PCODE) = *(int32_t (**)(int*, int32_t, int32_t, PCODE))(pPortableEntryPoint); + *((int32_t*)pRet) = (*fptr)(&framePointer, ARG_I32(0), ARG_IND(1), pPortableEntryPoint); + } + static void CallFunc_I32_I32_RetI32(PCODE pcode, int8_t* pArgs, int8_t* pRet) { int32_t (*fptr)(int32_t, int32_t) = (int32_t (*)(int32_t, int32_t))pcode; @@ -270,6 +449,20 @@ 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)); } + NOINLINE static void CallFunc_I32_I32_I32_I32_I32_I32_I32_I32_RetI32_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + int32_t (*fptr)(int*, int32_t, int32_t, 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, int32_t, int32_t, PCODE))(pPortableEntryPoint); + *((int32_t*)pRet) = (*fptr)(&framePointer, ARG_I32(0), ARG_I32(1), ARG_I32(2), ARG_I32(3), ARG_I32(4), ARG_I32(5), ARG_I32(6), ARG_I32(7), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_I32_I32_I32_I32_I32_I32_I32_RetI32_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + int32_t (*fptr)(int*, int32_t, 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, int32_t, PCODE))(pPortableEntryPoint); + *((int32_t*)pRet) = (*fptr)(&framePointer, ARG_I32(0), ARG_I32(1), ARG_I32(2), ARG_I32(3), ARG_I32(4), ARG_I32(5), ARG_I32(6), pPortableEntryPoint); + } + NOINLINE static void CallFunc_I32_I32_I32_I32_I32_I32_RetI32_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) { alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; @@ -385,6 +578,13 @@ namespace *((int64_t*)pRet) = (*fptr)(); } + NOINLINE static void CallFunc_F64_RetI64_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + int64_t (*fptr)(int*, double, PCODE) = *(int64_t (**)(int*, double, PCODE))(pPortableEntryPoint); + *((int64_t*)pRet) = (*fptr)(&framePointer, ARG_F64(0), pPortableEntryPoint); + } + static void CallFunc_I32_RetI64(PCODE pcode, int8_t* pArgs, int8_t* pRet) { int64_t (*fptr)(int32_t) = (int64_t (*)(int32_t))pcode; @@ -510,6 +710,90 @@ namespace (*fptr)(ARG_IND(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)); } + NOINLINE static void CallFunc_This_S12_RetVoid_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + void (*fptr)(int*, int32_t, int32_t, PCODE) = *(void (**)(int*, int32_t, int32_t, PCODE))(pPortableEntryPoint); + (*fptr)(&framePointer, ARG_I32(0), ARG_IND(1), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_S32_RetVoid_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + void (*fptr)(int*, int32_t, int32_t, PCODE) = *(void (**)(int*, int32_t, int32_t, PCODE))(pPortableEntryPoint); + (*fptr)(&framePointer, ARG_I32(0), ARG_IND(1), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_S8_S8_RetVoid_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) 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))(pPortableEntryPoint); + (*fptr)(&framePointer, ARG_I32(0), ARG_IND(1), ARG_IND(2), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_S8_I32_RetVoid_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) 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))(pPortableEntryPoint); + (*fptr)(&framePointer, ARG_I32(0), ARG_IND(1), ARG_I32(2), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_S8_RetVoid_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + void (*fptr)(int*, int32_t, int32_t, PCODE) = *(void (**)(int*, int32_t, int32_t, PCODE))(pPortableEntryPoint); + (*fptr)(&framePointer, ARG_I32(0), ARG_IND(1), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_I32_S8_RetVoid_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) 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))(pPortableEntryPoint); + (*fptr)(&framePointer, ARG_I32(0), ARG_I32(1), ARG_IND(2), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_I32_I32_S8_S8_I32_I32_RetVoid_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + void (*fptr)(int*, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, PCODE) = *(void (**)(int*, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, PCODE))(pPortableEntryPoint); + (*fptr)(&framePointer, ARG_I32(0), ARG_I32(1), ARG_I32(2), ARG_IND(3), ARG_IND(4), ARG_I32(5), ARG_I32(6), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_I32_I32_I32_I32_I32_RetVoid_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + void (*fptr)(int*, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, PCODE) = *(void (**)(int*, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, PCODE))(pPortableEntryPoint); + (*fptr)(&framePointer, ARG_I32(0), ARG_I32(1), ARG_I32(2), ARG_I32(3), ARG_I32(4), ARG_I32(5), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_I32_I32_I32_I32_RetVoid_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + void (*fptr)(int*, int32_t, int32_t, int32_t, int32_t, int32_t, PCODE) = *(void (**)(int*, int32_t, int32_t, int32_t, int32_t, int32_t, PCODE))(pPortableEntryPoint); + (*fptr)(&framePointer, ARG_I32(0), ARG_I32(1), ARG_I32(2), ARG_I32(3), ARG_I32(4), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_I32_I32_I32_RetVoid_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + void (*fptr)(int*, int32_t, int32_t, int32_t, int32_t, PCODE) = *(void (**)(int*, int32_t, int32_t, int32_t, int32_t, PCODE))(pPortableEntryPoint); + (*fptr)(&framePointer, ARG_I32(0), ARG_I32(1), ARG_I32(2), ARG_I32(3), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_I32_I32_RetVoid_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) 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))(pPortableEntryPoint); + (*fptr)(&framePointer, ARG_I32(0), ARG_I32(1), ARG_I32(2), pPortableEntryPoint); + } + + NOINLINE static void CallFunc_This_I32_RetVoid_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + void (*fptr)(int*, int32_t, int32_t, PCODE) = *(void (**)(int*, int32_t, int32_t, PCODE))(pPortableEntryPoint); + (*fptr)(&framePointer, ARG_I32(0), ARG_I32(1), pPortableEntryPoint); + } + NOINLINE static void CallFunc_This_RetVoid_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) { alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; @@ -597,6 +881,13 @@ namespace (*fptr)(ARG_I32(0), ARG_I32(1), ARG_I32(2), ARG_I32(3), ARG_I32(4), ARG_I32(5)); } + NOINLINE static void CallFunc_I32_I32_I32_I32_RetVoid_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) + { + alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; + void (*fptr)(int*, int32_t, int32_t, int32_t, int32_t, PCODE) = *(void (**)(int*, int32_t, int32_t, int32_t, int32_t, PCODE))(pPortableEntryPoint); + (*fptr)(&framePointer, ARG_I32(0), ARG_I32(1), ARG_I32(2), ARG_I32(3), pPortableEntryPoint); + } + NOINLINE static void CallFunc_I32_I32_I32_RetVoid_PE(PCODE pPortableEntryPoint, int8_t* pArgs, int8_t* pRet) { alignas(16) int framePointer = TERMINATE_R2R_STACK_WALK; @@ -639,6 +930,16 @@ namespace } const StringToWasmSigThunk g_wasmThunks[] = { + { "MS12TS12ip", (void*)&CallFunc_This_S12_I32_RetS12_PE }, + { "MS12TiS12ip", (void*)&CallFunc_This_I32_S12_I32_RetS12_PE }, + { "MS12Tiiip", (void*)&CallFunc_This_I32_I32_I32_RetS12_PE }, + { "MS12Tiip", (void*)&CallFunc_This_I32_I32_RetS12_PE }, + { "MS16Tp", (void*)&CallFunc_This_RetS16_PE }, + { "MS16l2ip", (void*)&CallFunc_L2_I32_RetS16_PE }, + { "MS1Tp", (void*)&CallFunc_This_RetS1_PE }, + { "MS8Tiiip", (void*)&CallFunc_This_I32_I32_I32_RetS8_PE }, + { "MS8Tip", (void*)&CallFunc_This_I32_RetS8_PE }, + { "MS8Tp", (void*)&CallFunc_This_RetS8_PE }, { "Mddddp", (void*)&CallFunc_F64_F64_F64_RetF64_PE }, { "Mdddp", (void*)&CallFunc_F64_F64_RetF64_PE }, { "Mddip", (void*)&CallFunc_F64_I32_RetF64_PE }, @@ -662,11 +963,26 @@ const StringToWasmSigThunk g_wasmThunks[] = { { "MiS8iii", (void*)&CallFunc_S8_I32_I32_I32_RetI32 }, { "MiS8iiii", (void*)&CallFunc_S8_I32_I32_I32_I32_RetI32 }, { "MiS8iiiii", (void*)&CallFunc_S8_I32_I32_I32_I32_I32_RetI32 }, + { "MiS8p", (void*)&CallFunc_S8_RetI32_PE }, + { "MiTS12ip", (void*)&CallFunc_This_S12_I32_RetI32_PE }, + { "MiTS12p", (void*)&CallFunc_This_S12_RetI32_PE }, + { "MiTS8S8p", (void*)&CallFunc_This_S8_S8_RetI32_PE }, + { "MiTS8iS8ip", (void*)&CallFunc_This_S8_I32_S8_I32_RetI32_PE }, + { "MiTS8iiip", (void*)&CallFunc_This_S8_I32_I32_I32_RetI32_PE }, + { "MiTS8ip", (void*)&CallFunc_This_S8_I32_RetI32_PE }, + { "MiTS8p", (void*)&CallFunc_This_S8_RetI32_PE }, + { "MiTiS12p", (void*)&CallFunc_This_I32_S12_RetI32_PE }, + { "MiTiS8p", (void*)&CallFunc_This_I32_S8_RetI32_PE }, + { "MiTiiiip", (void*)&CallFunc_This_I32_I32_I32_I32_RetI32_PE }, + { "MiTiiip", (void*)&CallFunc_This_I32_I32_I32_RetI32_PE }, + { "MiTiip", (void*)&CallFunc_This_I32_I32_RetI32_PE }, { "MiTip", (void*)&CallFunc_This_I32_RetI32_PE }, { "MiTp", (void*)&CallFunc_This_RetI32_PE }, + { "Midp", (void*)&CallFunc_F64_RetI32_PE }, { "Mii", (void*)&CallFunc_I32_RetI32 }, { "MiiS8i", (void*)&CallFunc_I32_S8_I32_RetI32 }, { "MiiS8iii", (void*)&CallFunc_I32_S8_I32_I32_I32_RetI32 }, + { "MiiS8p", (void*)&CallFunc_I32_S8_RetI32_PE }, { "Miii", (void*)&CallFunc_I32_I32_RetI32 }, { "MiiiS8iiS8", (void*)&CallFunc_I32_I32_S8_I32_I32_S8_RetI32 }, { "MiiiS8iiii", (void*)&CallFunc_I32_I32_S8_I32_I32_I32_I32_RetI32 }, @@ -679,6 +995,8 @@ const StringToWasmSigThunk g_wasmThunks[] = { { "Miiiiiiiii", (void*)&CallFunc_I32_I32_I32_I32_I32_I32_I32_I32_RetI32 }, { "Miiiiiiiiii", (void*)&CallFunc_I32_I32_I32_I32_I32_I32_I32_I32_I32_RetI32 }, { "Miiiiiiiiiiiiiii", (void*)&CallFunc_I32_I32_I32_I32_I32_I32_I32_I32_I32_I32_I32_I32_I32_I32_RetI32 }, + { "Miiiiiiiiip", (void*)&CallFunc_I32_I32_I32_I32_I32_I32_I32_I32_RetI32_PE }, + { "Miiiiiiiip", (void*)&CallFunc_I32_I32_I32_I32_I32_I32_I32_RetI32_PE }, { "Miiiiiiip", (void*)&CallFunc_I32_I32_I32_I32_I32_I32_RetI32_PE }, { "Miiiiiip", (void*)&CallFunc_I32_I32_I32_I32_I32_RetI32_PE }, { "Miiiiip", (void*)&CallFunc_I32_I32_I32_I32_RetI32_PE }, @@ -697,6 +1015,7 @@ const StringToWasmSigThunk g_wasmThunks[] = { { "Milili", (void*)&CallFunc_I64_I32_I64_I32_RetI32 }, { "Mip", (void*)&CallFunc_Void_RetI32_PE }, { "Ml", (void*)&CallFunc_Void_RetI64 }, + { "Mldp", (void*)&CallFunc_F64_RetI64_PE }, { "Mli", (void*)&CallFunc_I32_RetI64 }, { "Mliii", (void*)&CallFunc_I32_I32_I32_RetI64 }, { "Mliiil", (void*)&CallFunc_I32_I32_I32_I64_RetI64 }, @@ -717,6 +1036,18 @@ const StringToWasmSigThunk g_wasmThunks[] = { { "MvS8iiiii", (void*)&CallFunc_S8_I32_I32_I32_I32_I32_RetVoid }, { "MvS8iiiiii", (void*)&CallFunc_S8_I32_I32_I32_I32_I32_I32_RetVoid }, { "MvS8iiiiiiiiiii", (void*)&CallFunc_S8_I32_I32_I32_I32_I32_I32_I32_I32_I32_I32_I32_RetVoid }, + { "MvTS12p", (void*)&CallFunc_This_S12_RetVoid_PE }, + { "MvTS32p", (void*)&CallFunc_This_S32_RetVoid_PE }, + { "MvTS8S8p", (void*)&CallFunc_This_S8_S8_RetVoid_PE }, + { "MvTS8ip", (void*)&CallFunc_This_S8_I32_RetVoid_PE }, + { "MvTS8p", (void*)&CallFunc_This_S8_RetVoid_PE }, + { "MvTiS8p", (void*)&CallFunc_This_I32_S8_RetVoid_PE }, + { "MvTiiS8S8iip", (void*)&CallFunc_This_I32_I32_S8_S8_I32_I32_RetVoid_PE }, + { "MvTiiiiip", (void*)&CallFunc_This_I32_I32_I32_I32_I32_RetVoid_PE }, + { "MvTiiiip", (void*)&CallFunc_This_I32_I32_I32_I32_RetVoid_PE }, + { "MvTiiip", (void*)&CallFunc_This_I32_I32_I32_RetVoid_PE }, + { "MvTiip", (void*)&CallFunc_This_I32_I32_RetVoid_PE }, + { "MvTip", (void*)&CallFunc_This_I32_RetVoid_PE }, { "MvTp", (void*)&CallFunc_This_RetVoid_PE }, { "Mvdiip", (void*)&CallFunc_F64_I32_I32_RetVoid_PE }, { "Mvfiip", (void*)&CallFunc_F32_I32_I32_RetVoid_PE }, @@ -731,6 +1062,7 @@ const StringToWasmSigThunk g_wasmThunks[] = { { "Mviiii", (void*)&CallFunc_I32_I32_I32_I32_RetVoid }, { "Mviiiii", (void*)&CallFunc_I32_I32_I32_I32_I32_RetVoid }, { "Mviiiiii", (void*)&CallFunc_I32_I32_I32_I32_I32_I32_RetVoid }, + { "Mviiiip", (void*)&CallFunc_I32_I32_I32_I32_RetVoid_PE }, { "Mviiip", (void*)&CallFunc_I32_I32_I32_RetVoid_PE }, { "Mviip", (void*)&CallFunc_I32_I32_RetVoid_PE }, { "Mvili", (void*)&CallFunc_I32_I64_I32_RetVoid }, diff --git a/src/coreclr/vm/wasm/wasi/callhelpers-pinvoke.cpp b/src/coreclr/vm/wasm/wasi/callhelpers-pinvoke.cpp index e128a77b7bd640..493539a07f5216 100644 --- a/src/coreclr/vm/wasm/wasi/callhelpers-pinvoke.cpp +++ b/src/coreclr/vm/wasm/wasi/callhelpers-pinvoke.cpp @@ -11,7 +11,6 @@ #include extern "C" { - uint32_t CompressionNative_CompressBound (uint32_t); uint32_t CompressionNative_Crc32 (uint32_t, void *, int32_t); int32_t CompressionNative_Deflate (void *, int32_t); int32_t CompressionNative_DeflateEnd (void *); @@ -94,6 +93,7 @@ extern "C" { int32_t SystemNative_GetBytesAvailable (void *, void *); int32_t SystemNative_GetControlMessageBufferSize (int32_t, int32_t); double SystemNative_GetCpuUtilization (void *); + int32_t SystemNative_GetCryptographicallySecureRandomBytes (void *, int32_t); void * SystemNative_GetCwd (void *, int32_t); void * SystemNative_GetDefaultSearchOrderPseudoHandle (); int32_t SystemNative_GetErrNo (); @@ -290,7 +290,6 @@ static const Entry s_libSystem_Globalization_Native [] = { }; static const Entry s_libSystem_IO_Compression_Native [] = { - DllImportEntry(CompressionNative_CompressBound) // System.IO.Compression DllImportEntry(CompressionNative_Crc32) // System.IO.Compression DllImportEntry(CompressionNative_Deflate) // System.IO.Compression DllImportEntry(CompressionNative_DeflateEnd) // System.IO.Compression @@ -342,9 +341,10 @@ static const Entry s_libSystem_Native [] = { DllImportEntry(SystemNative_GetBytesAvailable) // System.Net.Sockets DllImportEntry(SystemNative_GetControlMessageBufferSize) // System.Net.Sockets DllImportEntry(SystemNative_GetCpuUtilization) // System.Private.CoreLib + DllImportEntry(SystemNative_GetCryptographicallySecureRandomBytes) // System.IO.Compression DllImportEntry(SystemNative_GetCwd) // System.Private.CoreLib DllImportEntry(SystemNative_GetDefaultSearchOrderPseudoHandle) // System.Private.CoreLib - DllImportEntry(SystemNative_GetErrNo) // System.Net.NameResolution, System.Private.CoreLib + DllImportEntry(SystemNative_GetErrNo) // System.Private.CoreLib DllImportEntry(SystemNative_GetHostEntryForName) // System.Net.NameResolution DllImportEntry(SystemNative_GetHostName) // System.Net.NameResolution DllImportEntry(SystemNative_GetIPv4Address) // System.Net.Primitives, System.Net.Sockets @@ -526,8 +526,8 @@ typedef struct PInvokeTable { static PInvokeTable s_PInvokeTables[] = { {"libSystem.Globalization.Native", s_libSystem_Globalization_Native, 34}, - {"libSystem.IO.Compression.Native", s_libSystem_IO_Compression_Native, 10}, - {"libSystem.Native", s_libSystem_Native, 147}, + {"libSystem.IO.Compression.Native", s_libSystem_IO_Compression_Native, 9}, + {"libSystem.Native", s_libSystem_Native, 148}, {"libSystem.Native.Browser", s_libSystem_Native_Browser, 0}, {"libSystem.Runtime.InteropServices.JavaScript.Native", s_libSystem_Runtime_InteropServices_JavaScript_Native, 0}, {"wasi:clocks/monotonic-clock@0.2.8", s_wasi_3A_clocks_2F_monotonic_clock_40_0_2_8, 4}, diff --git a/src/coreclr/vm/wasm/wasi/callhelpers-portable-entrypoints.cpp b/src/coreclr/vm/wasm/wasi/callhelpers-portable-entrypoints.cpp new file mode 100644 index 00000000000000..cbe20c0ef987a0 --- /dev/null +++ b/src/coreclr/vm/wasm/wasi/callhelpers-portable-entrypoints.cpp @@ -0,0 +1,1461 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// + +// +// GENERATED FILE, DON'T EDIT +// Generated by coreclr InterpToNativeGenerator +// + +#include +#include +#include "callhelpers.hpp" + +void ExecuteInterpretedMethodWithArgs_PortableEntryPoint(PCODE portableEntrypoint, TransitionBlock* block, size_t argsSize, int8_t* retBuff); + +namespace +{ + FCDECL4(void, CallInterpreter_This_S12_I32_RetS12, int32_t, int8_t*, int8_t*, int32_t); + WASM_CALLABLE_FUNC_5(void, CallInterpreter_This_S12_I32_RetS12, int32_t arg0, int8_t* retBuf, int8_t* arg1, int32_t arg2, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[4]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + transitionBlock.args[0] = (int64_t)arg0; + memcpy(&transitionBlock.args[1], arg1, 12); + transitionBlock.args[3] = (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"); + + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), retBuf); + return; + } + + FCDECL5(void, CallInterpreter_This_I32_S12_I32_RetS12, int32_t, int8_t*, int32_t, int8_t*, int32_t); + WASM_CALLABLE_FUNC_6(void, CallInterpreter_This_I32_S12_I32_RetS12, int32_t arg0, int8_t* retBuf, int32_t arg1, int8_t* arg2, int32_t arg3, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[5]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + transitionBlock.args[0] = (int64_t)arg0; + transitionBlock.args[1] = (int64_t)arg1; + memcpy(&transitionBlock.args[2], arg2, 12); + transitionBlock.args[4] = (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"); + + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), retBuf); + return; + } + + FCDECL5(void, CallInterpreter_This_I32_I32_I32_RetS12, int32_t, int8_t*, int32_t, int32_t, int32_t); + WASM_CALLABLE_FUNC_6(void, CallInterpreter_This_I32_I32_I32_RetS12, int32_t arg0, int8_t* retBuf, int32_t arg1, int32_t arg2, int32_t arg3, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[4]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), retBuf); + return; + } + + FCDECL4(void, CallInterpreter_This_I32_I32_RetS12, int32_t, int8_t*, int32_t, int32_t); + WASM_CALLABLE_FUNC_5(void, CallInterpreter_This_I32_I32_RetS12, int32_t arg0, int8_t* retBuf, int32_t arg1, int32_t arg2, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[3]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), retBuf); + return; + } + + FCDECL2(void, CallInterpreter_This_RetS16, int32_t, int8_t*); + WASM_CALLABLE_FUNC_3(void, CallInterpreter_This_RetS16, int32_t arg0, int8_t* retBuf, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[1]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), retBuf); + return; + } + + FCDECL4(void, CallInterpreter_L2_I32_RetS16, int8_t*, int64_t, int64_t, int32_t); + WASM_CALLABLE_FUNC_5(void, CallInterpreter_L2_I32_RetS16, int8_t* retBuf, int64_t arg0Lo, int64_t arg0Hi, int32_t arg1, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[3]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + transitionBlock.args[0] = arg0Lo; + transitionBlock.args[1] = arg0Hi; + transitionBlock.args[2] = (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"); + + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), retBuf); + return; + } + + FCDECL2(void, CallInterpreter_This_RetS1, int32_t, int8_t*); + WASM_CALLABLE_FUNC_3(void, CallInterpreter_This_RetS1, int32_t arg0, int8_t* retBuf, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[1]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), retBuf); + return; + } + + FCDECL5(void, CallInterpreter_This_I32_I32_I32_RetS8, int32_t, int8_t*, int32_t, int32_t, int32_t); + WASM_CALLABLE_FUNC_6(void, CallInterpreter_This_I32_I32_I32_RetS8, int32_t arg0, int8_t* retBuf, int32_t arg1, int32_t arg2, int32_t arg3, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[4]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), retBuf); + return; + } + + FCDECL3(void, CallInterpreter_This_I32_RetS8, int32_t, int8_t*, int32_t); + WASM_CALLABLE_FUNC_4(void, CallInterpreter_This_I32_RetS8, int32_t arg0, int8_t* retBuf, int32_t arg1, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[2]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), retBuf); + return; + } + + FCDECL2(void, CallInterpreter_This_RetS8, int32_t, int8_t*); + WASM_CALLABLE_FUNC_3(void, CallInterpreter_This_RetS8, int32_t arg0, int8_t* retBuf, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[1]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), retBuf); + return; + } + + FCDECL3(double, CallInterpreter_F64_F64_F64_RetF64, double, double, double); + WASM_CALLABLE_FUNC_4(double, CallInterpreter_F64_F64_F64_RetF64, double arg0, double arg1, double arg2, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[3]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + *(double*)&transitionBlock.args[0] = arg0; + *(double*)&transitionBlock.args[1] = arg1; + *(double*)&transitionBlock.args[2] = arg2; + static_assert(offsetof(decltype(transitionBlock), args) == sizeof(TransitionBlock), "Args array must be at a TransitionBlock offset from the start of the block"); + + double result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL2(double, CallInterpreter_F64_F64_RetF64, double, double); + WASM_CALLABLE_FUNC_3(double, CallInterpreter_F64_F64_RetF64, double arg0, double arg1, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[2]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + *(double*)&transitionBlock.args[0] = arg0; + *(double*)&transitionBlock.args[1] = arg1; + static_assert(offsetof(decltype(transitionBlock), args) == sizeof(TransitionBlock), "Args array must be at a TransitionBlock offset from the start of the block"); + + double result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL2(double, CallInterpreter_F64_I32_RetF64, double, int32_t); + WASM_CALLABLE_FUNC_3(double, CallInterpreter_F64_I32_RetF64, double arg0, int32_t arg1, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[2]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + *(double*)&transitionBlock.args[0] = 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"); + + double result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL1(double, CallInterpreter_F64_RetF64, double); + WASM_CALLABLE_FUNC_2(double, CallInterpreter_F64_RetF64, double arg0, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[1]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + *(double*)&transitionBlock.args[0] = arg0; + static_assert(offsetof(decltype(transitionBlock), args) == sizeof(TransitionBlock), "Args array must be at a TransitionBlock offset from the start of the block"); + + double result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL3(float, CallInterpreter_F32_F32_F32_RetF32, float, float, float); + WASM_CALLABLE_FUNC_4(float, CallInterpreter_F32_F32_F32_RetF32, float arg0, float arg1, float arg2, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[3]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + *(float*)&transitionBlock.args[0] = arg0; + *(float*)&transitionBlock.args[1] = arg1; + *(float*)&transitionBlock.args[2] = arg2; + static_assert(offsetof(decltype(transitionBlock), args) == sizeof(TransitionBlock), "Args array must be at a TransitionBlock offset from the start of the block"); + + float result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL2(float, CallInterpreter_F32_F32_RetF32, float, float); + WASM_CALLABLE_FUNC_3(float, CallInterpreter_F32_F32_RetF32, float arg0, float arg1, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[2]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + *(float*)&transitionBlock.args[0] = arg0; + *(float*)&transitionBlock.args[1] = arg1; + static_assert(offsetof(decltype(transitionBlock), args) == sizeof(TransitionBlock), "Args array must be at a TransitionBlock offset from the start of the block"); + + float result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL2(float, CallInterpreter_F32_I32_RetF32, float, int32_t); + WASM_CALLABLE_FUNC_3(float, CallInterpreter_F32_I32_RetF32, float arg0, int32_t arg1, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[2]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + *(float*)&transitionBlock.args[0] = 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"); + + float result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL1(float, CallInterpreter_F32_RetF32, float); + WASM_CALLABLE_FUNC_2(float, CallInterpreter_F32_RetF32, float arg0, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[1]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + *(float*)&transitionBlock.args[0] = arg0; + static_assert(offsetof(decltype(transitionBlock), args) == sizeof(TransitionBlock), "Args array must be at a TransitionBlock offset from the start of the block"); + + float result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL1(int32_t, CallInterpreter_S8_RetI32, int8_t*); + WASM_CALLABLE_FUNC_2(int32_t, CallInterpreter_S8_RetI32, int8_t* arg0, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[1]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + memcpy(&transitionBlock.args[0], arg0, 8); + static_assert(offsetof(decltype(transitionBlock), args) == sizeof(TransitionBlock), "Args array must be at a TransitionBlock offset from the start of the block"); + + int32_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL3(int32_t, CallInterpreter_This_S12_I32_RetI32, int32_t, int8_t*, int32_t); + WASM_CALLABLE_FUNC_4(int32_t, CallInterpreter_This_S12_I32_RetI32, int32_t arg0, int8_t* arg1, int32_t arg2, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[4]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + transitionBlock.args[0] = (int64_t)arg0; + memcpy(&transitionBlock.args[1], arg1, 12); + transitionBlock.args[3] = (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"); + + int32_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL2(int32_t, CallInterpreter_This_S12_RetI32, int32_t, int8_t*); + WASM_CALLABLE_FUNC_3(int32_t, CallInterpreter_This_S12_RetI32, int32_t arg0, int8_t* arg1, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[3]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + transitionBlock.args[0] = (int64_t)arg0; + memcpy(&transitionBlock.args[1], arg1, 12); + static_assert(offsetof(decltype(transitionBlock), args) == sizeof(TransitionBlock), "Args array must be at a TransitionBlock offset from the start of the block"); + + int32_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL3(int32_t, CallInterpreter_This_S8_S8_RetI32, int32_t, int8_t*, int8_t*); + WASM_CALLABLE_FUNC_4(int32_t, CallInterpreter_This_S8_S8_RetI32, int32_t arg0, int8_t* arg1, int8_t* arg2, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[3]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + transitionBlock.args[0] = (int64_t)arg0; + memcpy(&transitionBlock.args[1], arg1, 8); + memcpy(&transitionBlock.args[2], arg2, 8); + static_assert(offsetof(decltype(transitionBlock), args) == sizeof(TransitionBlock), "Args array must be at a TransitionBlock offset from the start of the block"); + + int32_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL5(int32_t, CallInterpreter_This_S8_I32_S8_I32_RetI32, int32_t, int8_t*, int32_t, int8_t*, int32_t); + WASM_CALLABLE_FUNC_6(int32_t, CallInterpreter_This_S8_I32_S8_I32_RetI32, int32_t arg0, int8_t* arg1, int32_t arg2, int8_t* arg3, int32_t arg4, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[5]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + transitionBlock.args[0] = (int64_t)arg0; + memcpy(&transitionBlock.args[1], arg1, 8); + transitionBlock.args[2] = (int64_t)arg2; + memcpy(&transitionBlock.args[3], arg3, 8); + 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"); + + int32_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL5(int32_t, CallInterpreter_This_S8_I32_I32_I32_RetI32, int32_t, int8_t*, int32_t, int32_t, int32_t); + WASM_CALLABLE_FUNC_6(int32_t, CallInterpreter_This_S8_I32_I32_I32_RetI32, int32_t arg0, int8_t* arg1, int32_t arg2, int32_t arg3, int32_t arg4, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[5]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + transitionBlock.args[0] = (int64_t)arg0; + memcpy(&transitionBlock.args[1], arg1, 8); + 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"); + + int32_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL3(int32_t, CallInterpreter_This_S8_I32_RetI32, int32_t, int8_t*, int32_t); + WASM_CALLABLE_FUNC_4(int32_t, CallInterpreter_This_S8_I32_RetI32, int32_t arg0, int8_t* arg1, int32_t arg2, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[3]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + transitionBlock.args[0] = (int64_t)arg0; + memcpy(&transitionBlock.args[1], arg1, 8); + 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"); + + int32_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL2(int32_t, CallInterpreter_This_S8_RetI32, int32_t, int8_t*); + WASM_CALLABLE_FUNC_3(int32_t, CallInterpreter_This_S8_RetI32, int32_t arg0, int8_t* arg1, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[2]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + transitionBlock.args[0] = (int64_t)arg0; + memcpy(&transitionBlock.args[1], arg1, 8); + static_assert(offsetof(decltype(transitionBlock), args) == sizeof(TransitionBlock), "Args array must be at a TransitionBlock offset from the start of the block"); + + int32_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL3(int32_t, CallInterpreter_This_I32_S12_RetI32, int32_t, int32_t, int8_t*); + WASM_CALLABLE_FUNC_4(int32_t, CallInterpreter_This_I32_S12_RetI32, int32_t arg0, int32_t arg1, int8_t* arg2, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[4]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + transitionBlock.args[0] = (int64_t)arg0; + transitionBlock.args[1] = (int64_t)arg1; + memcpy(&transitionBlock.args[2], arg2, 12); + static_assert(offsetof(decltype(transitionBlock), args) == sizeof(TransitionBlock), "Args array must be at a TransitionBlock offset from the start of the block"); + + int32_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL3(int32_t, CallInterpreter_This_I32_S8_RetI32, int32_t, int32_t, int8_t*); + WASM_CALLABLE_FUNC_4(int32_t, CallInterpreter_This_I32_S8_RetI32, int32_t arg0, int32_t arg1, int8_t* arg2, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[3]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + transitionBlock.args[0] = (int64_t)arg0; + transitionBlock.args[1] = (int64_t)arg1; + memcpy(&transitionBlock.args[2], arg2, 8); + static_assert(offsetof(decltype(transitionBlock), args) == sizeof(TransitionBlock), "Args array must be at a TransitionBlock offset from the start of the block"); + + int32_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL5(int32_t, CallInterpreter_This_I32_I32_I32_I32_RetI32, int32_t, int32_t, int32_t, int32_t, int32_t); + WASM_CALLABLE_FUNC_6(int32_t, CallInterpreter_This_I32_I32_I32_I32_RetI32, int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[5]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + int32_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL4(int32_t, CallInterpreter_This_I32_I32_I32_RetI32, int32_t, int32_t, int32_t, int32_t); + WASM_CALLABLE_FUNC_5(int32_t, CallInterpreter_This_I32_I32_I32_RetI32, int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[4]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + int32_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL3(int32_t, CallInterpreter_This_I32_I32_RetI32, int32_t, int32_t, int32_t); + WASM_CALLABLE_FUNC_4(int32_t, CallInterpreter_This_I32_I32_RetI32, int32_t arg0, int32_t arg1, int32_t arg2, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[3]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + int32_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL2(int32_t, CallInterpreter_This_I32_RetI32, int32_t, int32_t); + WASM_CALLABLE_FUNC_3(int32_t, CallInterpreter_This_I32_RetI32, int32_t arg0, int32_t arg1, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[2]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + int32_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL1(int32_t, CallInterpreter_This_RetI32, int32_t); + WASM_CALLABLE_FUNC_2(int32_t, CallInterpreter_This_RetI32, int32_t arg0, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[1]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + int32_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL1(int32_t, CallInterpreter_F64_RetI32, double); + WASM_CALLABLE_FUNC_2(int32_t, CallInterpreter_F64_RetI32, double arg0, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[1]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + *(double*)&transitionBlock.args[0] = arg0; + static_assert(offsetof(decltype(transitionBlock), args) == sizeof(TransitionBlock), "Args array must be at a TransitionBlock offset from the start of the block"); + + int32_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL2(int32_t, CallInterpreter_I32_S8_RetI32, int32_t, int8_t*); + WASM_CALLABLE_FUNC_3(int32_t, CallInterpreter_I32_S8_RetI32, int32_t arg0, int8_t* arg1, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[2]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + transitionBlock.args[0] = (int64_t)arg0; + memcpy(&transitionBlock.args[1], arg1, 8); + static_assert(offsetof(decltype(transitionBlock), args) == sizeof(TransitionBlock), "Args array must be at a TransitionBlock offset from the start of the block"); + + int32_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL8(int32_t, CallInterpreter_I32_I32_I32_I32_I32_I32_I32_I32_RetI32, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t); + 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) + { + struct + { + TransitionBlock block; + int64_t args[8]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + int32_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL7(int32_t, CallInterpreter_I32_I32_I32_I32_I32_I32_I32_RetI32, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t); + 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) + { + struct + { + TransitionBlock block; + int64_t args[7]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + int32_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL6(int32_t, CallInterpreter_I32_I32_I32_I32_I32_I32_RetI32, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t); + 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) + { + struct + { + TransitionBlock block; + int64_t args[6]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + int32_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL5(int32_t, CallInterpreter_I32_I32_I32_I32_I32_RetI32, int32_t, int32_t, int32_t, int32_t, int32_t); + 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) + { + struct + { + TransitionBlock block; + int64_t args[5]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + int32_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL4(int32_t, CallInterpreter_I32_I32_I32_I32_RetI32, int32_t, int32_t, int32_t, int32_t); + 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) + { + struct + { + TransitionBlock block; + int64_t args[4]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + int32_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL3(int32_t, CallInterpreter_I32_I32_I32_RetI32, int32_t, int32_t, int32_t); + WASM_CALLABLE_FUNC_4(int32_t, CallInterpreter_I32_I32_I32_RetI32, int32_t arg0, int32_t arg1, int32_t arg2, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[3]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + int32_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL2(int32_t, CallInterpreter_I32_I32_RetI32, int32_t, int32_t); + WASM_CALLABLE_FUNC_3(int32_t, CallInterpreter_I32_I32_RetI32, int32_t arg0, int32_t arg1, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[2]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + int32_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL1(int32_t, CallInterpreter_I32_RetI32, int32_t); + WASM_CALLABLE_FUNC_2(int32_t, CallInterpreter_I32_RetI32, int32_t arg0, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[1]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + int32_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL0(int32_t, CallInterpreter_Void_RetI32); + WASM_CALLABLE_FUNC_1(int32_t, CallInterpreter_Void_RetI32, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + + int32_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, 0, (int8_t*)&result); + return result; + } + + FCDECL1(int64_t, CallInterpreter_F64_RetI64, double); + WASM_CALLABLE_FUNC_2(int64_t, CallInterpreter_F64_RetI64, double arg0, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[1]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + *(double*)&transitionBlock.args[0] = arg0; + static_assert(offsetof(decltype(transitionBlock), args) == sizeof(TransitionBlock), "Args array must be at a TransitionBlock offset from the start of the block"); + + int64_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL3(int64_t, CallInterpreter_I32_I64_I64_RetI64, int32_t, int64_t, int64_t); + WASM_CALLABLE_FUNC_4(int64_t, CallInterpreter_I32_I64_I64_RetI64, int32_t arg0, int64_t arg1, int64_t arg2, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[3]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + int64_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL2(int64_t, CallInterpreter_I32_I64_RetI64, int32_t, int64_t); + WASM_CALLABLE_FUNC_3(int64_t, CallInterpreter_I32_I64_RetI64, int32_t arg0, int64_t arg1, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[2]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + int64_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL1(int64_t, CallInterpreter_I32_RetI64, int32_t); + WASM_CALLABLE_FUNC_2(int64_t, CallInterpreter_I32_RetI64, int32_t arg0, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[1]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + int64_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL2(int64_t, CallInterpreter_I64_I64_RetI64, int64_t, int64_t); + WASM_CALLABLE_FUNC_3(int64_t, CallInterpreter_I64_I64_RetI64, int64_t arg0, int64_t arg1, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[2]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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"); + + int64_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return result; + } + + FCDECL0(int64_t, CallInterpreter_Void_RetI64); + WASM_CALLABLE_FUNC_1(int64_t, CallInterpreter_Void_RetI64, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + + int64_t result = 0; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, 0, (int8_t*)&result); + return result; + } + + FCDECL2(void, CallInterpreter_This_S12_RetVoid, int32_t, int8_t*); + WASM_CALLABLE_FUNC_3(void, CallInterpreter_This_S12_RetVoid, int32_t arg0, int8_t* arg1, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[3]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + transitionBlock.args[0] = (int64_t)arg0; + memcpy(&transitionBlock.args[1], arg1, 12); + 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, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return; + } + + FCDECL2(void, CallInterpreter_This_S32_RetVoid, int32_t, int8_t*); + WASM_CALLABLE_FUNC_3(void, CallInterpreter_This_S32_RetVoid, int32_t arg0, int8_t* arg1, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[5]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + transitionBlock.args[0] = (int64_t)arg0; + memcpy(&transitionBlock.args[1], arg1, 32); + 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, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return; + } + + FCDECL3(void, CallInterpreter_This_S8_S8_RetVoid, int32_t, int8_t*, int8_t*); + WASM_CALLABLE_FUNC_4(void, CallInterpreter_This_S8_S8_RetVoid, int32_t arg0, int8_t* arg1, int8_t* arg2, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[3]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + transitionBlock.args[0] = (int64_t)arg0; + memcpy(&transitionBlock.args[1], arg1, 8); + memcpy(&transitionBlock.args[2], arg2, 8); + 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, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return; + } + + FCDECL3(void, CallInterpreter_This_S8_I32_RetVoid, int32_t, int8_t*, int32_t); + WASM_CALLABLE_FUNC_4(void, CallInterpreter_This_S8_I32_RetVoid, int32_t arg0, int8_t* arg1, int32_t arg2, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[3]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + transitionBlock.args[0] = (int64_t)arg0; + memcpy(&transitionBlock.args[1], arg1, 8); + 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, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return; + } + + FCDECL2(void, CallInterpreter_This_S8_RetVoid, int32_t, int8_t*); + WASM_CALLABLE_FUNC_3(void, CallInterpreter_This_S8_RetVoid, int32_t arg0, int8_t* arg1, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[2]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + transitionBlock.args[0] = (int64_t)arg0; + memcpy(&transitionBlock.args[1], arg1, 8); + 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, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return; + } + + FCDECL3(void, CallInterpreter_This_I32_S8_RetVoid, int32_t, int32_t, int8_t*); + WASM_CALLABLE_FUNC_4(void, CallInterpreter_This_I32_S8_RetVoid, int32_t arg0, int32_t arg1, int8_t* arg2, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[3]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + transitionBlock.args[0] = (int64_t)arg0; + transitionBlock.args[1] = (int64_t)arg1; + memcpy(&transitionBlock.args[2], arg2, 8); + 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, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return; + } + + FCDECL7(void, CallInterpreter_This_I32_I32_S8_S8_I32_I32_RetVoid, int32_t, int32_t, int32_t, int8_t*, int8_t*, int32_t, int32_t); + WASM_CALLABLE_FUNC_8(void, CallInterpreter_This_I32_I32_S8_S8_I32_I32_RetVoid, int32_t arg0, int32_t arg1, int32_t arg2, int8_t* arg3, int8_t* arg4, int32_t arg5, int32_t arg6, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[7]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + transitionBlock.args[0] = (int64_t)arg0; + transitionBlock.args[1] = (int64_t)arg1; + transitionBlock.args[2] = (int64_t)arg2; + memcpy(&transitionBlock.args[3], arg3, 8); + memcpy(&transitionBlock.args[4], arg4, 8); + 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, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return; + } + + FCDECL6(void, CallInterpreter_This_I32_I32_I32_I32_I32_RetVoid, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t); + WASM_CALLABLE_FUNC_7(void, CallInterpreter_This_I32_I32_I32_I32_I32_RetVoid, int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4, int32_t arg5, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[6]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return; + } + + FCDECL5(void, CallInterpreter_This_I32_I32_I32_I32_RetVoid, int32_t, int32_t, int32_t, int32_t, int32_t); + WASM_CALLABLE_FUNC_6(void, CallInterpreter_This_I32_I32_I32_I32_RetVoid, int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[5]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return; + } + + FCDECL4(void, CallInterpreter_This_I32_I32_I32_RetVoid, int32_t, int32_t, int32_t, int32_t); + WASM_CALLABLE_FUNC_5(void, CallInterpreter_This_I32_I32_I32_RetVoid, int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[4]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return; + } + + FCDECL3(void, CallInterpreter_This_I32_I32_RetVoid, int32_t, int32_t, int32_t); + WASM_CALLABLE_FUNC_4(void, CallInterpreter_This_I32_I32_RetVoid, int32_t arg0, int32_t arg1, int32_t arg2, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[3]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return; + } + + FCDECL2(void, CallInterpreter_This_I32_RetVoid, int32_t, int32_t); + WASM_CALLABLE_FUNC_3(void, CallInterpreter_This_I32_RetVoid, int32_t arg0, int32_t arg1, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[2]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return; + } + + FCDECL1(void, CallInterpreter_This_RetVoid, int32_t); + WASM_CALLABLE_FUNC_2(void, CallInterpreter_This_RetVoid, int32_t arg0, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[1]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return; + } + + FCDECL3(void, CallInterpreter_F64_I32_I32_RetVoid, double, int32_t, int32_t); + WASM_CALLABLE_FUNC_4(void, CallInterpreter_F64_I32_I32_RetVoid, double arg0, int32_t arg1, int32_t arg2, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[3]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + *(double*)&transitionBlock.args[0] = 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, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return; + } + + FCDECL3(void, CallInterpreter_F32_I32_I32_RetVoid, float, int32_t, int32_t); + WASM_CALLABLE_FUNC_4(void, CallInterpreter_F32_I32_I32_RetVoid, float arg0, int32_t arg1, int32_t arg2, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[3]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + *(float*)&transitionBlock.args[0] = 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, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return; + } + + FCDECL4(void, CallInterpreter_I32_I32_I32_I32_RetVoid, int32_t, int32_t, int32_t, int32_t); + 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) + { + struct + { + TransitionBlock block; + int64_t args[4]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return; + } + + FCDECL3(void, CallInterpreter_I32_I32_I32_RetVoid, int32_t, int32_t, int32_t); + WASM_CALLABLE_FUNC_4(void, CallInterpreter_I32_I32_I32_RetVoid, int32_t arg0, int32_t arg1, int32_t arg2, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[3]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return; + } + + FCDECL2(void, CallInterpreter_I32_I32_RetVoid, int32_t, int32_t); + WASM_CALLABLE_FUNC_3(void, CallInterpreter_I32_I32_RetVoid, int32_t arg0, int32_t arg1, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[2]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return; + } + + FCDECL1(void, CallInterpreter_I32_RetVoid, int32_t); + WASM_CALLABLE_FUNC_2(void, CallInterpreter_I32_RetVoid, int32_t arg0, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + int64_t args[1]; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + 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, &transitionBlock.block, sizeof(transitionBlock.args), (int8_t*)&result); + return; + } + + FCDECL0(void, CallInterpreter_Void_RetVoid); + WASM_CALLABLE_FUNC_1(void, CallInterpreter_Void_RetVoid, PCODE portableEntrypoint) + { + struct + { + TransitionBlock block; + } transitionBlock; + transitionBlock.block.m_ReturnAddress = 0; + transitionBlock.block.m_StackPointer = callersStackPointer; + + void * result = NULL; + ExecuteInterpretedMethodWithArgs_PortableEntryPoint(portableEntrypoint, &transitionBlock.block, 0, (int8_t*)&result); + return; + } + +} + +const StringToWasmSigThunk g_wasmGeneratedPortableEntryPointThunks[] = { + { "IS12TS12ip", (void*)&CallInterpreter_This_S12_I32_RetS12 }, + { "IS12TiS12ip", (void*)&CallInterpreter_This_I32_S12_I32_RetS12 }, + { "IS12Tiiip", (void*)&CallInterpreter_This_I32_I32_I32_RetS12 }, + { "IS12Tiip", (void*)&CallInterpreter_This_I32_I32_RetS12 }, + { "IS16Tp", (void*)&CallInterpreter_This_RetS16 }, + { "IS16l2ip", (void*)&CallInterpreter_L2_I32_RetS16 }, + { "IS1Tp", (void*)&CallInterpreter_This_RetS1 }, + { "IS8Tiiip", (void*)&CallInterpreter_This_I32_I32_I32_RetS8 }, + { "IS8Tip", (void*)&CallInterpreter_This_I32_RetS8 }, + { "IS8Tp", (void*)&CallInterpreter_This_RetS8 }, + { "Iddddp", (void*)&CallInterpreter_F64_F64_F64_RetF64 }, + { "Idddp", (void*)&CallInterpreter_F64_F64_RetF64 }, + { "Iddip", (void*)&CallInterpreter_F64_I32_RetF64 }, + { "Iddp", (void*)&CallInterpreter_F64_RetF64 }, + { "Iffffp", (void*)&CallInterpreter_F32_F32_F32_RetF32 }, + { "Ifffp", (void*)&CallInterpreter_F32_F32_RetF32 }, + { "Iffip", (void*)&CallInterpreter_F32_I32_RetF32 }, + { "Iffp", (void*)&CallInterpreter_F32_RetF32 }, + { "IiS8p", (void*)&CallInterpreter_S8_RetI32 }, + { "IiTS12ip", (void*)&CallInterpreter_This_S12_I32_RetI32 }, + { "IiTS12p", (void*)&CallInterpreter_This_S12_RetI32 }, + { "IiTS8S8p", (void*)&CallInterpreter_This_S8_S8_RetI32 }, + { "IiTS8iS8ip", (void*)&CallInterpreter_This_S8_I32_S8_I32_RetI32 }, + { "IiTS8iiip", (void*)&CallInterpreter_This_S8_I32_I32_I32_RetI32 }, + { "IiTS8ip", (void*)&CallInterpreter_This_S8_I32_RetI32 }, + { "IiTS8p", (void*)&CallInterpreter_This_S8_RetI32 }, + { "IiTiS12p", (void*)&CallInterpreter_This_I32_S12_RetI32 }, + { "IiTiS8p", (void*)&CallInterpreter_This_I32_S8_RetI32 }, + { "IiTiiiip", (void*)&CallInterpreter_This_I32_I32_I32_I32_RetI32 }, + { "IiTiiip", (void*)&CallInterpreter_This_I32_I32_I32_RetI32 }, + { "IiTiip", (void*)&CallInterpreter_This_I32_I32_RetI32 }, + { "IiTip", (void*)&CallInterpreter_This_I32_RetI32 }, + { "IiTp", (void*)&CallInterpreter_This_RetI32 }, + { "Iidp", (void*)&CallInterpreter_F64_RetI32 }, + { "IiiS8p", (void*)&CallInterpreter_I32_S8_RetI32 }, + { "Iiiiiiiiiip", (void*)&CallInterpreter_I32_I32_I32_I32_I32_I32_I32_I32_RetI32 }, + { "Iiiiiiiiip", (void*)&CallInterpreter_I32_I32_I32_I32_I32_I32_I32_RetI32 }, + { "Iiiiiiiip", (void*)&CallInterpreter_I32_I32_I32_I32_I32_I32_RetI32 }, + { "Iiiiiiip", (void*)&CallInterpreter_I32_I32_I32_I32_I32_RetI32 }, + { "Iiiiiip", (void*)&CallInterpreter_I32_I32_I32_I32_RetI32 }, + { "Iiiiip", (void*)&CallInterpreter_I32_I32_I32_RetI32 }, + { "Iiiip", (void*)&CallInterpreter_I32_I32_RetI32 }, + { "Iiip", (void*)&CallInterpreter_I32_RetI32 }, + { "Iip", (void*)&CallInterpreter_Void_RetI32 }, + { "Ildp", (void*)&CallInterpreter_F64_RetI64 }, + { "Ilillp", (void*)&CallInterpreter_I32_I64_I64_RetI64 }, + { "Ililp", (void*)&CallInterpreter_I32_I64_RetI64 }, + { "Ilip", (void*)&CallInterpreter_I32_RetI64 }, + { "Illlp", (void*)&CallInterpreter_I64_I64_RetI64 }, + { "Ilp", (void*)&CallInterpreter_Void_RetI64 }, + { "IvTS12p", (void*)&CallInterpreter_This_S12_RetVoid }, + { "IvTS32p", (void*)&CallInterpreter_This_S32_RetVoid }, + { "IvTS8S8p", (void*)&CallInterpreter_This_S8_S8_RetVoid }, + { "IvTS8ip", (void*)&CallInterpreter_This_S8_I32_RetVoid }, + { "IvTS8p", (void*)&CallInterpreter_This_S8_RetVoid }, + { "IvTiS8p", (void*)&CallInterpreter_This_I32_S8_RetVoid }, + { "IvTiiS8S8iip", (void*)&CallInterpreter_This_I32_I32_S8_S8_I32_I32_RetVoid }, + { "IvTiiiiip", (void*)&CallInterpreter_This_I32_I32_I32_I32_I32_RetVoid }, + { "IvTiiiip", (void*)&CallInterpreter_This_I32_I32_I32_I32_RetVoid }, + { "IvTiiip", (void*)&CallInterpreter_This_I32_I32_I32_RetVoid }, + { "IvTiip", (void*)&CallInterpreter_This_I32_I32_RetVoid }, + { "IvTip", (void*)&CallInterpreter_This_I32_RetVoid }, + { "IvTp", (void*)&CallInterpreter_This_RetVoid }, + { "Ivdiip", (void*)&CallInterpreter_F64_I32_I32_RetVoid }, + { "Ivfiip", (void*)&CallInterpreter_F32_I32_I32_RetVoid }, + { "Iviiiip", (void*)&CallInterpreter_I32_I32_I32_I32_RetVoid }, + { "Iviiip", (void*)&CallInterpreter_I32_I32_I32_RetVoid }, + { "Iviip", (void*)&CallInterpreter_I32_I32_RetVoid }, + { "Ivip", (void*)&CallInterpreter_I32_RetVoid }, + { "Ivp", (void*)&CallInterpreter_Void_RetVoid }, +}; + +const size_t g_wasmGeneratedPortableEntryPointThunksCount = sizeof(g_wasmGeneratedPortableEntryPointThunks) / sizeof(g_wasmGeneratedPortableEntryPointThunks[0]); From 0f6ff2fc28bf52c33b4b3c54bbb05ad186fdd1bf Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Sat, 29 Aug 2026 20:18:47 +0200 Subject: [PATCH 10/11] fix WASI build on windows --- src/coreclr/build-runtime.cmd | 3 +++ src/native/libs/build-native.cmd | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/coreclr/build-runtime.cmd b/src/coreclr/build-runtime.cmd index 4374e13face5ae..77788bbf6c21ff 100644 --- a/src/coreclr/build-runtime.cmd +++ b/src/coreclr/build-runtime.cmd @@ -281,6 +281,9 @@ if "%__TargetOS%"=="android" ( if "%__TargetOS%"=="browser" ( set __CrossTarget=1 ) +if "%__TargetOS%"=="wasi" ( + set __CrossTarget=1 +) if %__CrossTarget% EQU 0 ( call "%__RepoRootDir%\eng\native\version\copy_version_files.cmd" diff --git a/src/native/libs/build-native.cmd b/src/native/libs/build-native.cmd index 0369fc8db2e684..0c96c064b590aa 100644 --- a/src/native/libs/build-native.cmd +++ b/src/native/libs/build-native.cmd @@ -63,6 +63,9 @@ if "%__TargetOS%"=="android" ( if "%__TargetOS%"=="browser" ( set __CrossTarget=1 ) +if "%__TargetOS%"=="wasi" ( + set __CrossTarget=1 +) if %__CrossTarget% EQU 0 ( call "%__repoRoot%\eng\native\version\copy_version_files.cmd" From ea61390b894f6f3987468209a0577acdf9ba045d Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Sat, 29 Aug 2026 20:34:00 +0200 Subject: [PATCH 11/11] [wasm] Key CLR_CMAKE_HOST_WASI off the host OS, not the target OS Every other platform block in configureplatform.cmake keys on CLR_CMAKE_HOST_OS. The wasi one keyed on CLR_CMAKE_TARGET_OS, so a wasi cross-components build - which compiles host tools with MSVC on Windows - still got CLR_CMAKE_HOST_UNIX=1 and CLR_CMAKE_HOST_ARCH=wasm. That handed cl.exe the clang flags from configurecompiler.cmake, failing with D8021 on /Werror. --- eng/native/configureplatform.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eng/native/configureplatform.cmake b/eng/native/configureplatform.cmake index 2ccced093f6b38..a9ddb0d18aec41 100644 --- a/eng/native/configureplatform.cmake +++ b/eng/native/configureplatform.cmake @@ -221,10 +221,10 @@ if(CLR_CMAKE_HOST_OS STREQUAL emscripten) set(CLR_CMAKE_HOST_BROWSER 1) endif(CLR_CMAKE_HOST_OS STREQUAL emscripten) -if(CLR_CMAKE_TARGET_OS STREQUAL wasi) +if(CLR_CMAKE_HOST_OS STREQUAL wasi) set(CLR_CMAKE_HOST_WASI 1) set(CLR_CMAKE_HOST_UNIX 1) -endif(CLR_CMAKE_TARGET_OS STREQUAL wasi) +endif(CLR_CMAKE_HOST_OS STREQUAL wasi) #-------------------------------------------- # This repo builds two set of binaries