diff --git a/PLAN.md b/PLAN.md new file mode 100644 index 00000000..98da12da --- /dev/null +++ b/PLAN.md @@ -0,0 +1,219 @@ +# Polygen Android Full Implementation Plan + +This document outlines the steps needed to complete full Android support for Polygen. The current fix (PR #132) makes the Android build succeed, but the module throws `UnsupportedOperationException` at runtime because the C++ WASM runtime isn't wired up yet. + +## Current State + +### What Works +- Android new architecture build compiles successfully +- Codegen generates correct `NativePolygenSpec` in `com.callstack.polygen` +- `PolygenModule.kt` implements all abstract methods from the spec +- Module registers correctly with React Native's TurboModule system + +### What Doesn't Work +- All methods throw `UnsupportedOperationException` at runtime +- No JNI bridge to the C++ `ReactNativePolygen` implementation +- CMake native build is disabled + +## Architecture Options + +### Option A: Pure C++ TurboModule (Recommended) + +Register the C++ `ReactNativePolygen` class directly, matching how iOS works. + +**Pros:** +- Consistent with iOS implementation +- No duplicate code in Kotlin +- Direct JSI access, better performance +- The C++ implementation already exists and works + +**Cons:** +- Requires understanding React Native's C++ TurboModule registration on Android +- More complex CMake setup + +### Option B: Kotlin Bridge with JNI + +Keep the Kotlin `PolygenModule` and add JNI methods that call into C++. + +**Pros:** +- Standard Android pattern +- Easier to debug Kotlin layer + +**Cons:** +- Duplicates method definitions in Kotlin and C++ +- Additional JNI marshalling overhead +- More code to maintain + +## Implementation Steps (Option A - Recommended) + +### Phase 1: CMake Setup + +1. **Update `android/CMakeLists.txt`** to compile the C++ ReactNativePolygen module: + ```cmake + cmake_minimum_required(VERSION 3.13) + project(Polygen) + + set(CMAKE_CXX_STANDARD 17) + + # Find React Native packages + find_package(ReactAndroid REQUIRED CONFIG) + find_package(fbjni REQUIRED CONFIG) + + # Collect all C++ sources + file(GLOB_RECURSE POLYGEN_SOURCES + "../cpp/ReactNativePolygen/*.cpp" + "../cpp/ReactNativePolygen/*.c" + "../cpp/wasm-rt/*.c" + "src/main/jni/OnLoad.cpp" + ) + + # Create shared library + add_library(polygen SHARED ${POLYGEN_SOURCES}) + + target_include_directories(polygen PRIVATE + ../cpp + ../cpp/ReactNativePolygen + ../cpp/wasm-rt + ) + + target_link_libraries(polygen + ReactAndroid::jsi + ReactAndroid::react_nativemodule_core + ReactAndroid::turbomodulejsijni + fbjni::fbjni + ) + ``` + +2. **Re-enable native build in `build.gradle`**: + ```gradle + externalNativeBuild { + cmake { + path "CMakeLists.txt" + } + } + ``` + +### Phase 2: C++ Module Registration + +3. **Create `android/src/main/jni/OnLoad.cpp`**: + ```cpp + #include + #include + #include + + JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) { + return facebook::jni::initialize(vm, [] { + facebook::react::registerCxxModuleToGlobalModuleMap( + std::string(facebook::react::ReactNativePolygen::kModuleName), + [](std::shared_ptr jsInvoker) { + return std::make_shared(jsInvoker); + } + ); + }); + } + ``` + +4. **Update `PolygenPackage.kt`** to mark as CxxModule: + ```kotlin + moduleInfos[PolygenModule.NAME] = ReactModuleInfo( + PolygenModule.NAME, + PolygenModule.NAME, + false, // canOverrideExistingModule + false, // needsEagerInit + false, // hasConstants + true, // isCxxModule <-- Change to true + isTurboModule + ) + ``` + +5. **Load native library to trigger `JNI_OnLoad`** in `PolygenPackage.kt`: + ```kotlin + import com.facebook.soloader.SoLoader + + class PolygenPackage : TurboReactPackage() { + companion object { + init { + SoLoader.loadLibrary("polygen") + } + } + // ... rest of package + } + ``` + This ensures the native library is loaded when the package class is initialized, + which triggers `JNI_OnLoad` and registers the C++ TurboModule. + +6. **Remove or simplify `PolygenModule.kt`** since the C++ module handles everything. + +### Phase 3: Codegen Integration + +7. **Update `react-native.config.js`** to point to the CMake file: + ```js + android: { + cmakeListsPath: 'android/CMakeLists.txt', + } + ``` + +8. **Ensure codegen header paths are correct** in CMakeLists.txt: + ```cmake + target_include_directories(polygen PRIVATE + ${CMAKE_CURRENT_BINARY_DIR}/generated/source/codegen/jni + ) + ``` + +### Phase 4: Testing & Validation + +9. **Test with example app**: + ```bash + cd apps/example + yarn android + ``` + +10. **Verify all WASM operations work**: + - Module loading + - Memory allocation + - Global variables + - Table operations + - Function calls + +### Phase 5: Documentation + +11. **Update README.md** with Android setup instructions +12. **Add Android-specific troubleshooting** to docs + +## Files to Modify + +| File | Action | +|------|--------| +| `android/CMakeLists.txt` | Rewrite with proper C++ compilation | +| `android/build.gradle` | Re-enable externalNativeBuild | +| `android/src/main/jni/OnLoad.cpp` | Create for C++ module registration | +| `android/src/main/java/.../PolygenPackage.kt` | Set isCxxModule=true, add SoLoader.loadLibrary("polygen") | +| `android/src/main/java/.../PolygenModule.kt` | Remove or simplify | +| `react-native.config.js` | Add cmakeListsPath back | +| `cpp/ReactNativePolygen/ReactNativePolygen.h` | Ensure Android compatibility | + +## Dependencies + +- React Native 0.75+ (for proper new architecture support) +- NDK 21+ (already configured) +- CMake 3.13+ + +## Risks & Mitigations + +| Risk | Mitigation | +|------|------------| +| C++ code may have iOS-specific assumptions | Review and test all code paths on Android | +| CMake configuration complexity | Start with minimal config, add features incrementally | +| Memory management differences | Use shared_ptr consistently, test for leaks | +| Build time increase | Consider prebuilt binaries for release | + +## Timeline Estimate + +Not provided - depends on developer availability and familiarity with React Native internals. + +## References + +- [React Native TurboModules](https://reactnative.dev/docs/the-new-architecture/pillars-turbomodules) +- [C++ TurboModules](https://reactnative.dev/docs/the-new-architecture/cxx-cxxturbomodules) +- [Polygen Issue #132](https://github.com/callstackincubator/polygen/issues/132) +- [Polygen Issue #73](https://github.com/callstackincubator/polygen/issues/73) diff --git a/TODO.md b/TODO.md new file mode 100644 index 00000000..0277d5b8 --- /dev/null +++ b/TODO.md @@ -0,0 +1,80 @@ +# TODO - Polygen Android C++ Integration + +## Completed +- [x] Review existing C++ implementation structure (iteration 1) +- [x] Review iOS TurboModule registration pattern (iteration 1) +- [x] Phase 1: Rewrite CMakeLists.txt for C++ compilation (iteration 1) +- [x] Phase 1: Re-enable externalNativeBuild in build.gradle (iteration 1) +- [x] Phase 2: Create OnLoad.cpp for JNI_OnLoad registration (iteration 1) +- [x] Phase 2: Update PolygenPackage.kt with SoLoader.loadLibrary (iteration 1) +- [x] Phase 2: Set isCxxModule=true in ReactModuleInfo (iteration 1) +- [x] Phase 3: Add cmakeListsPath to react-native.config.js (iteration 1) +- [x] Fix C++ code to support Android codegen headers (iteration 2) + - Added #include to w2c.h + - Created ModuleBagStub.cpp for getModuleBag() function + - Added RNPolygenSpecJSI-generated.cpp to JNI sources + - Fixed GLOB to GLOB_RECURSE to include utils/*.cpp + - Added ReactAndroid::folly_runtime to link libraries +- [x] Phase 4: Verify Android build succeeds (iteration 2) + - libpolygen.so successfully built for arm64-v8a + - JNI_OnLoad symbol present +- [x] Phase 4: Test example app on Android emulator (iteration 3) + - App launches successfully via ADB + - UI visible and interactive (BottomTabs Example screen) + - Navigation works (Import Validation screen) + - C++ TurboModule registration working (no UnsatisfiedLinkError) + - "Bridgeless mode is enabled" confirms new architecture active +- [x] Phase 5: Android CMake integration infrastructure + - Created android-cmake.ts plugin in polygen-codegen + - Plugin generates CMakeLists.txt in .polygen-out/host/ + - Modified polygen CMakeLists.txt to detect and include generated code + - Passes POLYGEN_APP_ROOT from Gradle to find app's .polygen-out/host/ + - Generated code now compiling with correct include paths + +## In Progress +- [ ] Phase 5: Fix wasm-rt API mismatch + - Generated C code calls `wasm_rt_allocate_memory` with 5 arguments + - Bundled wasm-rt.h defines function with 4 parameters + - This is a pre-existing wasm2c version mismatch, not Android-specific + +## Blocked +- Pre-existing wasm2c API mismatch blocks module compilation + - The wasm2c tool generates code for a newer wasm-rt API than what's bundled + - This affects both iOS and Android builds + - Requires updating the bundled wasm-rt or regenerating code with matching wasm2c version + +## Notes +- App package: `com.callstack.polygen.example` +- Main activity: `com.microsoft.reacttestapp.MainActivity` +- libpolygen.so loaded successfully (verified in logcat) +- No UnsatisfiedLinkError or TurboModule registration errors +- iOS codegen generates: `RNPolygenSpecJSI.h` with class `NativePolygenCxxSpecJSI` +- Android codegen also generates: `RNPolygenSpecJSI.h` with same class name + +## Module Loading Architecture + +### How It Works +1. `polygen generate` creates native C/C++ code in `node_modules/.polygen-out/host/` +2. It generates a `loader.cpp` with `getModuleBag()` containing all precompiled modules +3. The polygen library has `ModuleBagStub.cpp` returning an empty bag (fallback) +4. For modules to load, the app must link generated code instead of the stub + +### iOS vs Android +- **iOS**: `ReactNativeWebAssemblyHost.podspec` auto-links generated code via CocoaPods +- **Android**: No equivalent mechanism yet - requires manual CMake integration + +### Current Behavior +- C++ TurboModule registration: ✅ Working +- Module compile errors (expected): "module was not precompiled" +- This is correct behavior when generated code isn't linked + +## Verification Summary +PR scope (C++ TurboModule wiring) verified: +1. ✅ Android example app launches successfully on emulator +2. ✅ App UI is visible and interactive +3. ✅ libpolygen.so loads without UnsatisfiedLinkError +4. ✅ C++ TurboModule registered via JNI_OnLoad +5. ✅ Module API accessible from JavaScript (returns expected errors for unlinked modules) + +Future work needed: +- Android equivalent of ReactNativeWebAssemblyHost.podspec for linking generated code diff --git a/apps/example/android/build.gradle b/apps/example/android/build.gradle index bd85d0f3..70dfea05 100644 --- a/apps/example/android/build.gradle +++ b/apps/example/android/build.gradle @@ -43,6 +43,13 @@ allprojects { } -react { - bundleCommand = "webpack-bundle" +// Configure react-native in app subproject after plugin is applied +subprojects { + afterEvaluate { project -> + if (project.plugins.hasPlugin("com.facebook.react")) { + project.react { + bundleCommand = "webpack-bundle" + } + } + } } diff --git a/apps/example/android/gradle.properties b/apps/example/android/gradle.properties index 6b9e8dcd..490e2deb 100644 --- a/apps/example/android/gradle.properties +++ b/apps/example/android/gradle.properties @@ -40,7 +40,7 @@ reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64 # to write custom TurboModules/Fabric components OR use libraries that # are providing them. # Note that this is incompatible with web debugging. -#newArchEnabled=true +newArchEnabled=true #bridgelessEnabled=true # Uncomment the line below to build React Native from source. diff --git a/packages/codegen/assets/wasm-rt/wasm-rt-exceptions.c b/packages/codegen/assets/wasm-rt/wasm-rt-exceptions.c index 27d6ea08..73e97201 100644 --- a/packages/codegen/assets/wasm-rt/wasm-rt-exceptions.c +++ b/packages/codegen/assets/wasm-rt/wasm-rt-exceptions.c @@ -31,38 +31,43 @@ static WASM_RT_THREAD_LOCAL wasm_rt_jmp_buf* g_unwind_target; void wasm_rt_load_exception(const wasm_rt_tag_t tag, uint32_t size, const void* values) { - if (size > MAX_EXCEPTION_SIZE) { - wasm_rt_trap(WASM_RT_TRAP_EXHAUSTION); - } + if (size > MAX_EXCEPTION_SIZE) { + wasm_rt_trap(WASM_RT_TRAP_EXHAUSTION); + } - g_active_exception_tag = tag; - g_active_exception_size = size; + g_active_exception_tag = tag; + g_active_exception_size = size; - if (size) { - memcpy(g_active_exception, values, size); - } + if (size) { + memcpy(g_active_exception, values, size); + } } WASM_RT_NO_RETURN void wasm_rt_throw(void) { - WASM_RT_LONGJMP(*g_unwind_target, WASM_RT_TRAP_UNCAUGHT_EXCEPTION); + WASM_RT_LONGJMP(*g_unwind_target, WASM_RT_TRAP_UNCAUGHT_EXCEPTION); } WASM_RT_UNWIND_TARGET* wasm_rt_get_unwind_target(void) { - return g_unwind_target; + return g_unwind_target; } void wasm_rt_set_unwind_target(WASM_RT_UNWIND_TARGET* target) { - g_unwind_target = target; + g_unwind_target = target; } wasm_rt_tag_t wasm_rt_exception_tag(void) { - return g_active_exception_tag; + return g_active_exception_tag; } uint32_t wasm_rt_exception_size(void) { - return g_active_exception_size; + return g_active_exception_size; } void* wasm_rt_exception(void) { - return g_active_exception; -} \ No newline at end of file + return g_active_exception; +} + +// Include table operations for exnref +#define WASM_RT_TABLE_OPS_EXNREF +#include "wasm-rt-impl-tableops.inc" +#undef WASM_RT_TABLE_OPS_EXNREF diff --git a/packages/codegen/assets/wasm-rt/wasm-rt-exceptions.h b/packages/codegen/assets/wasm-rt/wasm-rt-exceptions.h index 25f98cde..9e95c196 100644 --- a/packages/codegen/assets/wasm-rt/wasm-rt-exceptions.h +++ b/packages/codegen/assets/wasm-rt/wasm-rt-exceptions.h @@ -70,8 +70,71 @@ uint32_t wasm_rt_exception_size(void); */ void* wasm_rt_exception(void); +/** + * The maximum size of an exception. + */ +#define WASM_EXN_MAX_SIZE 256 + +/** + * An exception instance (the runtime representation of a function). + * These can be stored in tables of type exnref, or used as values. + */ +typedef struct { + /** The exceptions's tag. */ + wasm_rt_tag_t tag; + /** The size of the exception. */ + uint32_t size; + /** + * The actual contents of the exception are stored inline. + */ + char data[WASM_EXN_MAX_SIZE]; +} wasm_rt_exnref_t; + +/** Default (null) value of an exnref */ +#define wasm_rt_exnref_null_value ((wasm_rt_exnref_t){NULL, 0, {0}}) + +/** A Table of type exnref. */ +typedef struct { + /** The table element data, with an element count of `size`. */ + wasm_rt_exnref_t* data; + /** + * The maximum element count of this Table object. If there is no maximum, + * `max_size` is 0xffffffffu (i.e. UINT32_MAX). + */ + uint32_t max_size; + /** The current element count of the table. */ + uint32_t size; +} wasm_rt_exnref_table_t; + +/** + * Initialize an exnref Table object with an element count of `elements` and a + * maximum size of `max_elements`. + * + * ``` + * wasm_rt_exnref_table_t my_table; + * // 5 elements and a maximum of 10 elements. + * wasm_rt_allocate_exnref_table(&my_table, 5, 10); + * ``` + */ +void wasm_rt_allocate_exnref_table(wasm_rt_exnref_table_t*, + uint32_t elements, + uint32_t max_elements); + +/** Free an exnref Table object. */ +void wasm_rt_free_exnref_table(wasm_rt_exnref_table_t*); + +/** + * Grow a Table object by `delta` elements (giving the new elements the value + * `init`), and return the previous element count. If this new element count is + * greater than the maximum element count, the grow fails and 0xffffffffu + * (UINT32_MAX) is returned instead. + */ +uint32_t wasm_rt_grow_exnref_table(wasm_rt_exnref_table_t*, + uint32_t delta, + wasm_rt_exnref_t init); + #ifdef __cplusplus } #endif -#endif \ No newline at end of file +#endif diff --git a/packages/codegen/assets/wasm-rt/wasm-rt-impl-tableops.inc b/packages/codegen/assets/wasm-rt/wasm-rt-impl-tableops.inc index e53cfa6f..76193028 100644 --- a/packages/codegen/assets/wasm-rt/wasm-rt-impl-tableops.inc +++ b/packages/codegen/assets/wasm-rt/wasm-rt-impl-tableops.inc @@ -18,23 +18,32 @@ // funcref or externref. For this, the file must be included after defining // either WASM_RT_TABLE_OPS_FUNCREF or WASM_RT_TABLE_OPS_EXTERNREF -#if defined(WASM_RT_TABLE_OPS_FUNCREF) && defined(WASM_RT_TABLE_OPS_EXTERNREF) +#if defined(WASM_RT_TABLE_OPS_FUNCREF) + \ + defined(WASM_RT_TABLE_OPS_EXTERNREF) + \ + defined(WASM_RT_TABLE_OPS_EXNREF) > \ + 1 #error \ - "Expected only one of { WASM_RT_TABLE_OPS_FUNCREF, WASM_RT_TABLE_OPS_EXTERNREF } to be defined" -#elif !defined(WASM_RT_TABLE_OPS_FUNCREF) && \ - !defined(WASM_RT_TABLE_OPS_EXTERNREF) + "Expected only one of { WASM_RT_TABLE_OPS_FUNCREF, WASM_RT_TABLE_OPS_EXTERNREF, WASM_RT_TABLE_OPS_EXNREF } to be defined" +#elif defined(WASM_RT_TABLE_OPS_FUNCREF) + \ + defined(WASM_RT_TABLE_OPS_EXTERNREF) + \ + defined(WASM_RT_TABLE_OPS_EXNREF) < \ + 1 #error \ - "Expected one of { WASM_RT_TABLE_OPS_FUNCREF, WASM_RT_TABLE_OPS_EXTERNREF } to be defined" + "Expected one of { WASM_RT_TABLE_OPS_FUNCREF, WASM_RT_TABLE_OPS_EXTERNREF, WASM_RT_TABLE_OPS_EXNREF } to be defined" #endif -#ifdef WASM_RT_TABLE_OPS_FUNCREF +#if defined(WASM_RT_TABLE_OPS_FUNCREF) #define WASM_RT_TABLE_TYPE wasm_rt_funcref_table_t #define WASM_RT_TABLE_ELEMENT_TYPE wasm_rt_funcref_t #define WASM_RT_TABLE_APINAME(name) name##_funcref_table -#else +#elif defined(WASM_RT_TABLE_OPS_EXTERNREF) #define WASM_RT_TABLE_TYPE wasm_rt_externref_table_t #define WASM_RT_TABLE_ELEMENT_TYPE wasm_rt_externref_t #define WASM_RT_TABLE_APINAME(name) name##_externref_table +#elif defined(WASM_RT_TABLE_OPS_EXNREF) +#define WASM_RT_TABLE_TYPE wasm_rt_exnref_table_t +#define WASM_RT_TABLE_ELEMENT_TYPE wasm_rt_exnref_t +#define WASM_RT_TABLE_APINAME(name) name##_exnref_table #endif void WASM_RT_TABLE_APINAME(wasm_rt_allocate)(WASM_RT_TABLE_TYPE* table, diff --git a/packages/codegen/assets/wasm-rt/wasm-rt-impl.c b/packages/codegen/assets/wasm-rt/wasm-rt-impl.c index 46d90fa5..0cf0723a 100644 --- a/packages/codegen/assets/wasm-rt/wasm-rt-impl.c +++ b/packages/codegen/assets/wasm-rt/wasm-rt-impl.c @@ -49,17 +49,22 @@ static void* g_sig_handler_handle = 0; #endif #endif -#if WASM_RT_USE_SEGUE || WASM_RT_ALLOW_SEGUE -// Currently Segue is used only for linux +#if WASM_RT_USE_SEGUE +bool wasm_rt_fsgsbase_inst_supported = false; +#ifdef __linux__ #include #ifdef __GLIBC__ #include #endif -bool wasm_rt_fsgsbase_inst_supported = false; - #include // For ARCH_SET_GS #include // For SYS_arch_prctl #include // For syscall +#ifndef HWCAP2_FSGSBASE +#define HWCAP2_FSGSBASE (1 << 1) +#endif +#elif defined(__FreeBSD__) +#include // For amd64_set_gsbase etc. +#endif #endif #if WASM_RT_SEGUE_FREE_SEGMENT @@ -73,23 +78,25 @@ WASM_RT_THREAD_LOCAL uint32_t wasm_rt_saved_call_stack_depth; static WASM_RT_THREAD_LOCAL void* g_alt_stack = NULL; #endif +#ifndef WASM_RT_TRAP_HANDLER WASM_RT_THREAD_LOCAL wasm_rt_jmp_buf g_wasm_rt_jmp_buf; +#endif #ifdef WASM_RT_TRAP_HANDLER extern void WASM_RT_TRAP_HANDLER(wasm_rt_trap_t code); #endif void wasm_rt_trap(wasm_rt_trap_t code) { - assert(code != WASM_RT_TRAP_NONE); + assert(code != WASM_RT_TRAP_NONE); #if WASM_RT_STACK_DEPTH_COUNT - wasm_rt_call_stack_depth = wasm_rt_saved_call_stack_depth; + wasm_rt_call_stack_depth = wasm_rt_saved_call_stack_depth; #endif #ifdef WASM_RT_TRAP_HANDLER - WASM_RT_TRAP_HANDLER(code); + WASM_RT_TRAP_HANDLER(code); wasm_rt_unreachable(); #else - WASM_RT_LONGJMP(g_wasm_rt_jmp_buf, code); + WASM_RT_LONGJMP(g_wasm_rt_jmp_buf, code); #endif } @@ -121,179 +128,228 @@ static void os_cleanup_signal_handler(void) { #if WASM_RT_INSTALL_SIGNAL_HANDLER static void os_signal_handler(int sig, siginfo_t* si, void* unused) { - if (si->si_code == SEGV_ACCERR) { - wasm_rt_trap(WASM_RT_TRAP_OOB); - } else { - wasm_rt_trap(WASM_RT_TRAP_EXHAUSTION); - } + if (si->si_code == SEGV_ACCERR) { + wasm_rt_trap(WASM_RT_TRAP_OOB); + } else { + wasm_rt_trap(WASM_RT_TRAP_EXHAUSTION); + } } static void os_install_signal_handler(void) { - struct sigaction sa; - memset(&sa, '\0', sizeof(sa)); - sa.sa_flags = SA_SIGINFO; + struct sigaction sa; + memset(&sa, '\0', sizeof(sa)); + sa.sa_flags = SA_SIGINFO; #if WASM_RT_STACK_EXHAUSTION_HANDLER - sa.sa_flags |= SA_ONSTACK; + sa.sa_flags |= SA_ONSTACK; #endif - sigemptyset(&sa.sa_mask); - sa.sa_sigaction = os_signal_handler; + sigemptyset(&sa.sa_mask); + sa.sa_sigaction = os_signal_handler; - /* Install SIGSEGV and SIGBUS handlers, since macOS seems to use SIGBUS. */ - if (sigaction(SIGSEGV, &sa, NULL) != 0 || sigaction(SIGBUS, &sa, NULL) != 0) { - perror("sigaction failed"); - abort(); - } + /* Install SIGSEGV and SIGBUS handlers, since macOS seems to use SIGBUS. */ + if (sigaction(SIGSEGV, &sa, NULL) != 0 || sigaction(SIGBUS, &sa, NULL) != 0) { + perror("sigaction failed"); + abort(); + } } static void os_cleanup_signal_handler(void) { - /* Undo what was done in os_install_signal_handler */ - struct sigaction sa; - memset(&sa, '\0', sizeof(sa)); - sa.sa_handler = SIG_DFL; - if (sigaction(SIGSEGV, &sa, NULL) != 0 || sigaction(SIGBUS, &sa, NULL)) { - perror("sigaction failed"); - abort(); - } + /* Undo what was done in os_install_signal_handler */ + struct sigaction sa; + memset(&sa, '\0', sizeof(sa)); + sa.sa_handler = SIG_DFL; + if (sigaction(SIGSEGV, &sa, NULL) != 0 || sigaction(SIGBUS, &sa, NULL)) { + perror("sigaction failed"); + abort(); + } } #endif #if WASM_RT_STACK_EXHAUSTION_HANDLER static bool os_has_altstack_installed() { - /* check for altstack already in place */ - stack_t ss; - if (sigaltstack(NULL, &ss) != 0) { - perror("sigaltstack failed"); - abort(); - } + /* check for altstack already in place */ + stack_t ss; + if (sigaltstack(NULL, &ss) != 0) { + perror("os_has_altstack_installed: sigaltstack failed"); + abort(); + } - return !(ss.ss_flags & SS_DISABLE); + return !(ss.ss_flags & SS_DISABLE); } /* These routines set up an altstack to handle SIGSEGV from stack overflow. */ static void os_allocate_and_install_altstack(void) { - /* verify altstack not already allocated */ - assert(!g_alt_stack && - "wasm-rt error: tried to re-allocate thread-local alternate stack"); - - /* We could check and warn if an altstack is already installed, but some - * sanitizers install their own altstack, so this warning would fire - * spuriously and break the test outputs. */ - - /* allocate altstack */ - g_alt_stack = malloc(SIGSTKSZ); - if (g_alt_stack == NULL) { - perror("malloc failed"); - abort(); - } + /* verify altstack not already allocated */ + assert(!g_alt_stack && + "wasm-rt error: tried to re-allocate thread-local alternate stack"); + + /* We could check and warn if an altstack is already installed, but some + * sanitizers install their own altstack, so this warning would fire + * spuriously and break the test outputs. */ + + /* allocate altstack */ + g_alt_stack = malloc(SIGSTKSZ); + if (g_alt_stack == NULL) { + perror("malloc failed"); + abort(); + } - /* install altstack */ - stack_t ss; - ss.ss_sp = g_alt_stack; - ss.ss_flags = 0; - ss.ss_size = SIGSTKSZ; - if (sigaltstack(&ss, NULL) != 0) { - perror("sigaltstack failed"); - abort(); - } + /* install altstack */ + stack_t ss; + ss.ss_sp = g_alt_stack; + ss.ss_flags = 0; + ss.ss_size = SIGSTKSZ; + if (sigaltstack(&ss, NULL) != 0) { + perror("os_allocate_and_install_altstack: sigaltstack failed"); + abort(); + } } static void os_disable_and_deallocate_altstack(void) { - /* in debug build, verify altstack allocated */ - assert(g_alt_stack && - "wasm-rt error: thread-local alternate stack not allocated"); - - /* verify altstack was still in place */ - stack_t ss; - if (sigaltstack(NULL, &ss) != 0) { - perror("sigaltstack failed"); - abort(); - } + /* in debug build, verify altstack allocated */ + assert(g_alt_stack && + "wasm-rt error: thread-local alternate stack not allocated"); + + /* verify altstack was still in place */ + stack_t ss; + if (sigaltstack(NULL, &ss) != 0) { + perror("os_disable_and_deallocate_altstack: sigaltstack failed"); + abort(); + } - if ((!g_alt_stack) || (ss.ss_flags & SS_DISABLE) || - (ss.ss_sp != g_alt_stack) || (ss.ss_size != SIGSTKSZ)) { - DEBUG_PRINTF( - "wasm-rt warning: alternate stack was modified unexpectedly\n"); - return; - } + if ((!g_alt_stack) || (ss.ss_flags & SS_DISABLE) || + (ss.ss_sp != g_alt_stack) || (ss.ss_size != SIGSTKSZ)) { + DEBUG_PRINTF( + "wasm-rt warning: alternate stack was modified unexpectedly\n"); + return; + } - /* disable and free */ - ss.ss_flags = SS_DISABLE; - if (sigaltstack(&ss, NULL) != 0) { - perror("sigaltstack failed"); - abort(); - } - assert(!os_has_altstack_installed()); - free(g_alt_stack); - g_alt_stack = NULL; + assert(!(ss.ss_flags & SS_ONSTACK) && + "attempt to deallocate altstack while in use"); + + /* disable and free */ + ss.ss_flags = SS_DISABLE; + if (sigaltstack(&ss, NULL) != 0) { + perror("sigaltstack disable failed"); + abort(); + } + assert(!os_has_altstack_installed()); + free(g_alt_stack); + g_alt_stack = NULL; } #endif #endif +#if WASM_RT_USE_SEGUE && defined(__FreeBSD__) +static void call_cpuid(uint64_t* rax, + uint64_t* rbx, + uint64_t* rcx, + uint64_t* rdx) { + __asm__ volatile( + "cpuid" + : "=a"(*rax), "=b"(*rbx), "=c"(*rcx), "=d"(*rdx) // output operands + : "a"(*rax), "c"(*rcx) // input operands + ); +} +#endif + void wasm_rt_init(void) { - wasm_rt_init_thread(); + wasm_rt_init_thread(); #if WASM_RT_INSTALL_SIGNAL_HANDLER - if (!g_signal_handler_installed) { - g_signal_handler_installed = true; - os_install_signal_handler(); - } + if (!g_signal_handler_installed) { + g_signal_handler_installed = true; + os_install_signal_handler(); + } #endif -#if WASM_RT_USE_SEGUE || WASM_RT_ALLOW_SEGUE - #if defined(__GLIBC__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 18 +#if WASM_RT_USE_SEGUE +#if defined(__linux__) && defined(__GLIBC__) && __GLIBC__ >= 2 && \ + __GLIBC_MINOR__ >= 18 // Check for support for userspace wrgsbase instructions unsigned long val = getauxval(AT_HWCAP2); - wasm_rt_fsgsbase_inst_supported = val & (1 << 1); + wasm_rt_fsgsbase_inst_supported = val & HWCAP2_FSGSBASE; +#elif defined(__FreeBSD__) + // FreeBSD enables fsgsbase on the newer kernels if the hardware supports it. + // We just need to check if the hardware supports it by querying the correct + // cpuid leaf. + uint64_t rax, rbx, rcx, rdx; + rax = 0; + call_cpuid(&rax, &rbx, &rcx, &rdx); + + if (rax > 7) { + rax = 7; + rcx = 0; + call_cpuid(&rax, &rbx, &rcx, &rdx); + if (rbx & 0x1) { + wasm_rt_fsgsbase_inst_supported = true; + } + } #endif #endif - assert(wasm_rt_is_initialized()); + assert(wasm_rt_is_initialized()); } bool wasm_rt_is_initialized(void) { #if WASM_RT_STACK_EXHAUSTION_HANDLER - if (!os_has_altstack_installed()) { - return false; - } + if (!os_has_altstack_installed()) { + return false; + } #endif -#if WASM_RT_INSTALL_SIGNAL_HANDLER && !WASM_RT_SHARED_WEAK - return g_signal_handler_installed; +#if WASM_RT_INSTALL_SIGNAL_HANDLER + return g_signal_handler_installed; #else - return true; + return true; #endif } void wasm_rt_free(void) { - assert(wasm_rt_is_initialized()); + assert(wasm_rt_is_initialized()); #if WASM_RT_INSTALL_SIGNAL_HANDLER - os_cleanup_signal_handler(); - g_signal_handler_installed = false; + os_cleanup_signal_handler(); + g_signal_handler_installed = false; #endif - wasm_rt_free_thread(); + wasm_rt_free_thread(); } void wasm_rt_init_thread(void) { #if WASM_RT_STACK_EXHAUSTION_HANDLER - os_allocate_and_install_altstack(); + os_allocate_and_install_altstack(); #endif } void wasm_rt_free_thread(void) { #if WASM_RT_STACK_EXHAUSTION_HANDLER - os_disable_and_deallocate_altstack(); + os_disable_and_deallocate_altstack(); #endif } -#if WASM_RT_USE_SEGUE || WASM_RT_ALLOW_SEGUE +#if WASM_RT_USE_SEGUE void wasm_rt_syscall_set_segue_base(void* base) { - if (syscall(SYS_arch_prctl, ARCH_SET_GS, base) != 0) { + int error_code = 0; +#ifdef __linux__ + error_code = syscall(SYS_arch_prctl, ARCH_SET_GS, base); +#elif defined(__FreeBSD__) + error_code = amd64_set_gsbase(base); +#else +#error "Unknown platform" +#endif + if (error_code != 0) { perror("wasm_rt_syscall_set_segue_base error"); abort(); } } void* wasm_rt_syscall_get_segue_base() { void* base; - if (syscall(SYS_arch_prctl, ARCH_GET_GS, &base) != 0) { + int error_code = 0; +#ifdef __linux__ + error_code = syscall(SYS_arch_prctl, ARCH_GET_GS, &base); +#elif defined(__FreeBSD__) + error_code = amd64_get_gsbase(&base); +#else +#error "Unknown platform" +#endif + if (error_code != 0) { perror("wasm_rt_syscall_get_segue_base error"); abort(); } @@ -312,32 +368,34 @@ void* wasm_rt_syscall_get_segue_base() { #undef WASM_RT_TABLE_OPS_EXTERNREF const char* wasm_rt_strerror(wasm_rt_trap_t trap) { - switch (trap) { - case WASM_RT_TRAP_NONE: - return "No error"; - case WASM_RT_TRAP_OOB: + switch (trap) { + case WASM_RT_TRAP_NONE: + return "No error"; + case WASM_RT_TRAP_OOB: #if WASM_RT_MERGED_OOB_AND_EXHAUSTION_TRAPS - return "Out-of-bounds access in linear memory or a table, or call stack " - "exhausted"; + return "Out-of-bounds access in linear memory or a table, or call stack " + "exhausted"; #else - return "Out-of-bounds access in linear memory or a table"; + return "Out-of-bounds access in linear memory or a table"; case WASM_RT_TRAP_EXHAUSTION: return "Call stack exhausted"; #endif - case WASM_RT_TRAP_INT_OVERFLOW: - return "Integer overflow on divide or truncation"; - case WASM_RT_TRAP_DIV_BY_ZERO: - return "Integer divide by zero"; - case WASM_RT_TRAP_INVALID_CONVERSION: - return "Conversion from NaN to integer"; - case WASM_RT_TRAP_UNREACHABLE: - return "Unreachable instruction executed"; - case WASM_RT_TRAP_CALL_INDIRECT: - return "Invalid call_indirect or return_call_indirect"; - case WASM_RT_TRAP_UNCAUGHT_EXCEPTION: - return "Uncaught exception"; - case WASM_RT_TRAP_UNALIGNED: - return "Unaligned atomic memory access"; - } - return "invalid trap code"; -} \ No newline at end of file + case WASM_RT_TRAP_INT_OVERFLOW: + return "Integer overflow on divide or truncation"; + case WASM_RT_TRAP_DIV_BY_ZERO: + return "Integer divide by zero"; + case WASM_RT_TRAP_INVALID_CONVERSION: + return "Conversion from NaN to integer"; + case WASM_RT_TRAP_UNREACHABLE: + return "Unreachable instruction executed"; + case WASM_RT_TRAP_CALL_INDIRECT: + return "Invalid call_indirect or return_call_indirect"; + case WASM_RT_TRAP_UNCAUGHT_EXCEPTION: + return "Uncaught exception"; + case WASM_RT_TRAP_UNALIGNED: + return "Unaligned atomic memory access"; + case WASM_RT_TRAP_NULL_REF: + return "Null reference"; + } + return "invalid trap code"; +} diff --git a/packages/codegen/assets/wasm-rt/wasm-rt-impl.h b/packages/codegen/assets/wasm-rt/wasm-rt-impl.h index da70752d..ed46b097 100644 --- a/packages/codegen/assets/wasm-rt/wasm-rt-impl.h +++ b/packages/codegen/assets/wasm-rt/wasm-rt-impl.h @@ -14,662 +14,55 @@ * limitations under the License. */ -#ifndef WASM_RT_H_ -#define WASM_RT_H_ +#ifndef WASM_RT_IMPL_H_ +#define WASM_RT_IMPL_H_ -#include -#include -#include -#include -#include +#include "wasm-rt.h" -#ifdef __cplusplus -extern "C" { -#endif - -#ifndef __has_builtin -#define __has_builtin(x) 0 /** Compatibility with non-clang compilers. */ -#endif - -#if __has_builtin(__builtin_expect) -#define UNLIKELY(x) __builtin_expect(!!(x), 0) -#define LIKELY(x) __builtin_expect(!!(x), 1) -#else -#define UNLIKELY(x) (x) -#define LIKELY(x) (x) -#endif - -#if __has_builtin(__builtin_memcpy) -#define wasm_rt_memcpy __builtin_memcpy -#else -#define wasm_rt_memcpy memcpy -#endif - -#if __has_builtin(__builtin_unreachable) -#define wasm_rt_unreachable __builtin_unreachable -#else -#define wasm_rt_unreachable abort -#endif - -#ifdef __STDC_VERSION__ -#if __STDC_VERSION__ >= 201112L -#define WASM_RT_C11_AVAILABLE -#endif -#endif - -/** - * Many devices don't implement the C11 threads.h. We use CriticalSection APIs - * for Windows and pthreads on other platforms where threads are not available. - */ -#ifdef WASM_RT_C11_AVAILABLE - -#if defined(_WIN32) +#ifdef _WIN32 #include -#define WASM_RT_MUTEX CRITICAL_SECTION -#define WASM_RT_USE_CRITICALSECTION 1 -#elif defined(__APPLE__) || defined(__STDC_NO_THREADS__) -#include -#define WASM_RT_MUTEX pthread_mutex_t -#define WASM_RT_USE_PTHREADS 1 -#else -#include -#define WASM_RT_MUTEX mtx_t -#define WASM_RT_USE_C11THREADS 1 -#endif - -#endif - -#ifdef _MSC_VER -#define WASM_RT_THREAD_LOCAL __declspec(thread) -#elif defined(WASM_RT_C11_AVAILABLE) -#define WASM_RT_THREAD_LOCAL _Thread_local -#else -#define WASM_RT_THREAD_LOCAL -#endif - -/** - * If enabled, perform additional sanity checks in the generated wasm2c code and - * wasm2c runtime. This is useful to enable on debug builds. - */ -#ifndef WASM_RT_SANITY_CHECKS -#define WASM_RT_SANITY_CHECKS 0 -#endif - -/** - * Backward compatibility: Convert the previously exposed - * WASM_RT_MEMCHECK_SIGNAL_HANDLER macro to the ALLOCATION and CHECK macros that - * are now used. - */ -#if defined(WASM_RT_MEMCHECK_SIGNAL_HANDLER) - -#if WASM_RT_MEMCHECK_SIGNAL_HANDLER -#define WASM_RT_USE_MMAP 1 -#define WASM_RT_MEMCHECK_GUARD_PAGES 1 -#else -#define WASM_RT_USE_MMAP 0 -#define WASM_RT_MEMCHECK_BOUNDS_CHECK 1 -#endif - -#warning \ - "WASM_RT_MEMCHECK_SIGNAL_HANDLER has been deprecated in favor of WASM_RT_USE_MMAP and WASM_RT_MEMORY_CHECK_* macros" -#endif - -/** - * Specify if we use OR mmap/mprotect (+ Windows equivalents) OR malloc/realloc - * for the Wasm memory allocation and growth. mmap/mprotect guarantees memory - * will grow without being moved, while malloc ensures the virtual memory is - * consumed only as needed, but may relocate the memory to handle memory - * fragmentation. - * - * This defaults to malloc on 32-bit platforms or if memory64 support is needed. - * It defaults to mmap on 64-bit platforms assuming memory64 support is not - * needed (so we can use the guard based range checks below). - */ -#ifndef WASM_RT_USE_MMAP -#if UINTPTR_MAX > 0xffffffff && !SUPPORT_MEMORY64 -#define WASM_RT_USE_MMAP 1 -#else -#define WASM_RT_USE_MMAP 0 -#endif -#endif - -/** - * Set the range checking strategy for Wasm memories. - * - * GUARD_PAGES: memory accesses rely on unmapped pages/guard pages to trap - * out-of-bound accesses. - * - * BOUNDS_CHECK: memory accesses are checked with explicit bounds checks. - * - * This defaults to GUARD_PAGES as this is the fasest option, iff the - * requirements of GUARD_PAGES --- 64-bit platforms, MMAP allocation strategy, - * no 64-bit memories, no big-endian --- are met. This falls back to BOUNDS - * otherwise. - */ - -/** Check if Guard checks are supported */ -#if UINTPTR_MAX > 0xffffffff && WASM_RT_USE_MMAP && !SUPPORT_MEMORY64 && \ - !WABT_BIG_ENDIAN -#define WASM_RT_GUARD_PAGES_SUPPORTED 1 -#else -#define WASM_RT_GUARD_PAGES_SUPPORTED 0 -#endif - -/** Specify defaults for memory checks if unspecified */ -#if !defined(WASM_RT_MEMCHECK_GUARD_PAGES) && \ - !defined(WASM_RT_MEMCHECK_BOUNDS_CHECK) -#if WASM_RT_GUARD_PAGES_SUPPORTED -#define WASM_RT_MEMCHECK_GUARD_PAGES 1 -#else -#define WASM_RT_MEMCHECK_BOUNDS_CHECK 1 -#endif -#endif - -/** Ensure the macros are defined */ -#ifndef WASM_RT_MEMCHECK_GUARD_PAGES -#define WASM_RT_MEMCHECK_GUARD_PAGES 0 -#endif -#ifndef WASM_RT_MEMCHECK_BOUNDS_CHECK -#define WASM_RT_MEMCHECK_BOUNDS_CHECK 0 -#endif - -/** Sanity check the use of guard pages */ -#if WASM_RT_MEMCHECK_GUARD_PAGES && !WASM_RT_GUARD_PAGES_SUPPORTED -#error \ - "WASM_RT_MEMCHECK_GUARD_PAGES not supported on this platform/configuration" -#endif - -#if WASM_RT_MEMCHECK_GUARD_PAGES && WASM_RT_MEMCHECK_BOUNDS_CHECK -#error \ - "Cannot use both WASM_RT_MEMCHECK_GUARD_PAGES and WASM_RT_MEMCHECK_BOUNDS_CHECK" - -#elif !WASM_RT_MEMCHECK_GUARD_PAGES && !WASM_RT_MEMCHECK_BOUNDS_CHECK -#error \ - "Must choose at least one from WASM_RT_MEMCHECK_GUARD_PAGES and WASM_RT_MEMCHECK_BOUNDS_CHECK" -#endif - -/** - * Some configurations above require the Wasm runtime to install a signal - * handler. However, this can be explicitly disallowed by the host using - * WASM_RT_SKIP_SIGNAL_RECOVERY. In this case, when the wasm code encounters an - * OOB access, it may either trap or abort. - */ -#ifndef WASM_RT_SKIP_SIGNAL_RECOVERY -#define WASM_RT_SKIP_SIGNAL_RECOVERY 0 -#endif - -#if WASM_RT_MEMCHECK_GUARD_PAGES && !WASM_RT_SKIP_SIGNAL_RECOVERY -#define WASM_RT_INSTALL_SIGNAL_HANDLER 1 -#else -#define WASM_RT_INSTALL_SIGNAL_HANDLER 0 -#endif - -#ifndef WASM_RT_SHARED_WEAK -#define WASM_RT_SHARED_WEAK 0 -#endif - -/** - * This macro, if defined to 1 (i.e., allows the "segue" optimization), allows - * Wasm2c to use segment registers to speedup access to the linear heap. Note - * that even if allowed in this way, the segment registers would only be used if - * Wasm2c output is compiled for a suitable architecture and OS and the produces - * C file is compiled by supported compilers. The extact restrictions are listed - * in detail in src/template/wasm2c.declarations.c - */ -#ifndef WASM_RT_ALLOW_SEGUE -#define WASM_RT_ALLOW_SEGUE 0 -#endif - -/** - * The segue optimization restores x86 segments to their old values when exiting - * wasm2c code. If WASM_RT_SEGUE_FREE_SEGMENT is defined, wasm2c assumes it has - * exclusive use of the segment and optimizes performance in two ways. First, it - * does not restore the "old" value of the segment during exits. Second, wasm2c - * only sets the segment register if the value has changed since the last time - * it was set. - */ -#ifndef WASM_RT_SEGUE_FREE_SEGMENT -#define WASM_RT_SEGUE_FREE_SEGMENT 0 -#endif - -/** - * This macro, if defined, allows the embedder to disable all stack exhaustion - * checks. This a non conformant configuration, i.e., this does not respect - * Wasm's specification, and may compromise security. Use with caution. - */ -#ifndef WASM_RT_NONCONFORMING_UNCHECKED_STACK_EXHAUSTION -#define WASM_RT_NONCONFORMING_UNCHECKED_STACK_EXHAUSTION 0 -#endif - -/** - * We need to detect and trap stack overflows. If we use a signal handler on - * POSIX systems, this can detect call stack overflows. On windows, or platforms - * without a signal handler, we use stack depth counting. - */ -#if !defined(WASM_RT_STACK_DEPTH_COUNT) && \ - !defined(WASM_RT_STACK_EXHAUSTION_HANDLER) && \ - !WASM_RT_NONCONFORMING_UNCHECKED_STACK_EXHAUSTION - -#if WASM_RT_INSTALL_SIGNAL_HANDLER && !defined(_WIN32) -#define WASM_RT_STACK_EXHAUSTION_HANDLER 1 -#else -#define WASM_RT_STACK_DEPTH_COUNT 1 -#endif - -#endif - -/** Ensure the stack macros are defined */ -#ifndef WASM_RT_STACK_DEPTH_COUNT -#define WASM_RT_STACK_DEPTH_COUNT 0 -#endif -#ifndef WASM_RT_STACK_EXHAUSTION_HANDLER -#define WASM_RT_STACK_EXHAUSTION_HANDLER 0 -#endif - -#if WASM_RT_NONCONFORMING_UNCHECKED_STACK_EXHAUSTION - -#if (WASM_RT_STACK_EXHAUSTION_HANDLER + WASM_RT_STACK_DEPTH_COUNT) != 0 -#error \ - "Cannot specify WASM_RT_NONCONFORMING_UNCHECKED_STACK_EXHAUSTION along with WASM_RT_STACK_EXHAUSTION_HANDLER or WASM_RT_STACK_DEPTH_COUNT" -#endif - -#else - -#if (WASM_RT_STACK_EXHAUSTION_HANDLER + WASM_RT_STACK_DEPTH_COUNT) > 1 -#error \ - "Cannot specify multiple options from WASM_RT_STACK_EXHAUSTION_HANDLER , WASM_RT_STACK_DEPTH_COUNT" -#elif (WASM_RT_STACK_EXHAUSTION_HANDLER + WASM_RT_STACK_DEPTH_COUNT) == 0 -#error \ - "Must specify one of WASM_RT_STACK_EXHAUSTION_HANDLER , WASM_RT_STACK_DEPTH_COUNT" #endif +#ifdef __cplusplus +extern "C" { #endif -#if WASM_RT_STACK_EXHAUSTION_HANDLER && !WASM_RT_INSTALL_SIGNAL_HANDLER -#error \ - "WASM_RT_STACK_EXHAUSTION_HANDLER can only be used if WASM_RT_INSTALL_SIGNAL_HANDLER is enabled" +#ifndef WASM_RT_TRAP_HANDLER +/** A setjmp buffer used for handling traps. */ +extern WASM_RT_THREAD_LOCAL wasm_rt_jmp_buf g_wasm_rt_jmp_buf; #endif #if WASM_RT_STACK_DEPTH_COUNT -/** - * When the signal handler cannot be used to detect stack overflows, stack depth - * is limited explicitly. The maximum stack depth before trapping can be - * configured by defining this symbol before including wasm-rt when building the - * generated c files, for example: - * - * ``` - * cc -c -DWASM_RT_MAX_CALL_STACK_DEPTH=100 my_module.c -o my_module.o - * ``` - */ -#ifndef WASM_RT_MAX_CALL_STACK_DEPTH -#define WASM_RT_MAX_CALL_STACK_DEPTH 500 -#endif - -/** Current call stack depth. */ -extern WASM_RT_THREAD_LOCAL uint32_t wasm_rt_call_stack_depth; - -#endif - -#if WASM_RT_USE_SEGUE || WASM_RT_ALLOW_SEGUE -/** - * The segue optimization uses x86 segments to point to a linear memory. If - * used, the runtime must query whether it can use the fast userspace wrgsbase - * instructions or whether it must invoke syscalls to set the segment base, - * depending on the supported CPU features. The result of this query is saved in - * this variable. - */ -extern bool wasm_rt_fsgsbase_inst_supported; -/** - * If fast userspace wrgsbase instructions don't exist, the runtime most provide - * a function that invokes the OS' underlying syscall to set the segment base. - */ -void wasm_rt_syscall_set_segue_base(void* base); -/** - * If fast userspace rdgsbase instructions don't exist, the runtime most provide - * a function that invokes the OS' underlying syscall to get the segment base. - */ -void* wasm_rt_syscall_get_segue_base(); -/** - * If WASM_RT_SEGUE_FREE_SEGMENT is defined, we must only set the segment - * register if it was changed since the last time it was set. The last value set - * on the segment register is stored in this variable. - */ -#if WASM_RT_SEGUE_FREE_SEGMENT -extern WASM_RT_THREAD_LOCAL void* wasm_rt_last_segment_val; -#endif -#endif - -#if defined(_MSC_VER) -#define WASM_RT_NO_RETURN __declspec(noreturn) +/** Saved call stack depth that will be restored in case a trap occurs. */ +extern WASM_RT_THREAD_LOCAL uint32_t wasm_rt_saved_call_stack_depth; +#define WASM_RT_SAVE_STACK_DEPTH() \ + wasm_rt_saved_call_stack_depth = wasm_rt_call_stack_depth #else -#define WASM_RT_NO_RETURN __attribute__((noreturn)) +#define WASM_RT_SAVE_STACK_DEPTH() (void)0 #endif -#if defined(__APPLE__) && WASM_RT_STACK_EXHAUSTION_HANDLER -#define WASM_RT_MERGED_OOB_AND_EXHAUSTION_TRAPS 1 -#else -#define WASM_RT_MERGED_OOB_AND_EXHAUSTION_TRAPS 0 -#endif - -/** Reason a trap occurred. Provide this to `wasm_rt_trap`. */ -typedef enum { - WASM_RT_TRAP_NONE, /** No error. */ - WASM_RT_TRAP_OOB, /** Out-of-bounds access in linear memory or a table. */ - WASM_RT_TRAP_INT_OVERFLOW, /** Integer overflow on divide or truncation. */ - WASM_RT_TRAP_DIV_BY_ZERO, /** Integer divide by zero. */ - WASM_RT_TRAP_INVALID_CONVERSION, /** Conversion from NaN to integer. */ - WASM_RT_TRAP_UNREACHABLE, /** Unreachable instruction executed. */ - WASM_RT_TRAP_CALL_INDIRECT, /** Invalid call_indirect, for any reason. */ - WASM_RT_TRAP_UNCAUGHT_EXCEPTION, /** Exception thrown and not caught. */ - WASM_RT_TRAP_UNALIGNED, /** Unaligned atomic instruction executed. */ -#if WASM_RT_MERGED_OOB_AND_EXHAUSTION_TRAPS - WASM_RT_TRAP_EXHAUSTION = WASM_RT_TRAP_OOB, -#else - WASM_RT_TRAP_EXHAUSTION, /** Call stack exhausted. */ -#endif -} wasm_rt_trap_t; - -/** Value types. Used to define function signatures. */ -typedef enum { - WASM_RT_I32, - WASM_RT_I64, - WASM_RT_F32, - WASM_RT_F64, - WASM_RT_V128, - WASM_RT_FUNCREF, - WASM_RT_EXTERNREF, -} wasm_rt_type_t; - -/** - * A generic function pointer type, both for Wasm functions (`code`) - * and host functions (`hostcode`). All function pointers are stored - * in this canonical form, but must be cast to their proper signature - * to call. - */ -typedef void (*wasm_rt_function_ptr_t)(void); - /** - * A pointer to a "tail-callee" function, called by a tail-call - * trampoline or by another tail-callee function. (The definition uses a - * single-member struct to allow a recursive definition.) - */ -typedef struct wasm_rt_tailcallee_t { - void (*fn)(void** instance_ptr, - void* tail_call_stack, - struct wasm_rt_tailcallee_t* next); -} wasm_rt_tailcallee_t; - -/** - * The type of a function (an arbitrary number of param and result types). - * This is represented as an opaque 256-bit ID. - */ -typedef const char* wasm_rt_func_type_t; - -/** - * A function instance (the runtime representation of a function). - * These can be stored in tables of type funcref, or used as values. - */ -typedef struct { - /** The function's type. */ - wasm_rt_func_type_t func_type; - /** - * The function. The embedder must know the actual C signature of the function - * and cast to it before calling. - */ - wasm_rt_function_ptr_t func; - /** An alternate version of the function to be used when tail-called. */ - wasm_rt_tailcallee_t func_tailcallee; - /** - * A function instance is a closure of the function over an instance - * of the originating module. The module_instance element will be passed into - * the function at runtime. - */ - void* module_instance; -} wasm_rt_funcref_t; - -/** Default (null) value of a funcref */ -#define wasm_rt_funcref_null_value \ - ((wasm_rt_funcref_t){NULL, NULL, {NULL}, NULL}) - -/** The type of an external reference (opaque to WebAssembly). */ -typedef void* wasm_rt_externref_t; - -/** Default (null) value of an externref */ -#define wasm_rt_externref_null_value ((wasm_rt_externref_t){NULL}) - -/** A Memory object. */ -typedef struct { - /** The linear memory data, with a byte length of `size`. */ - uint8_t* data; - /** The current page count for this Memory object. */ - uint64_t pages; - /** - * The maximum page count for this Memory object. If there is no maximum, - * `max_pages` is 0xffffffffu (i.e. UINT32_MAX). - */ - uint64_t max_pages; - /** The current size of the linear memory, in bytes. */ - uint64_t size; - /** Is this memory indexed by u64 (as opposed to default u32) */ - bool is64; -} wasm_rt_memory_t; - -#ifdef WASM_RT_C11_AVAILABLE -/** A shared Memory object. */ -typedef struct { - /** - * The linear memory data, with a byte length of `size`. The memory is marked - * atomic as it is shared and may have to be accessed with different memory - * orders --- sequential when being accessed atomically, relaxed otherwise. - * Unfortunately, the C standard does not state what happens if there are - * overlaps in two memory accesses which have a memory order, e.g., an - * atomic32 being read from the same location an atomic64 is read. One way to - * prevent optimizations from assuming non-overlapping behavior as typically - * done in C is to mark the memory as volatile. Thus the memory is atomic and - * volatile. - */ - _Atomic volatile uint8_t* data; - /** The current page count for this Memory object. */ - uint64_t pages; - /** - * The maximum page count for this Memory object. If there is no maximum, - * `max_pages` is 0xffffffffu (i.e. UINT32_MAX). - */ - uint64_t max_pages; - /** The current size of the linear memory, in bytes. */ - uint64_t size; - /** Is this memory indexed by u64 (as opposed to default u32) */ - bool is64; - /** Lock used to ensure operations such as memory grow are threadsafe */ - WASM_RT_MUTEX mem_lock; -} wasm_rt_shared_memory_t; -#endif - -/** A Table of type funcref. */ -typedef struct { - /** The table element data, with an element count of `size`. */ - wasm_rt_funcref_t* data; - /** - * The maximum element count of this Table object. If there is no maximum, - * `max_size` is 0xffffffffu (i.e. UINT32_MAX). - */ - uint32_t max_size; - /** The current element count of the table. */ - uint32_t size; -} wasm_rt_funcref_table_t; - -/** A Table of type externref. */ -typedef struct { - /** The table element data, with an element count of `size`. */ - wasm_rt_externref_t* data; - /** - * The maximum element count of this Table object. If there is no maximum, - * `max_size` is 0xffffffffu (i.e. UINT32_MAX). - */ - uint32_t max_size; - /** The current element count of the table. */ - uint32_t size; -} wasm_rt_externref_table_t; - -/** Initialize the runtime. */ -void wasm_rt_init(void); - -/** Is the runtime initialized? */ -bool wasm_rt_is_initialized(void); - -/** Free the runtime's state. */ -void wasm_rt_free(void); - -/* - * Initialize the multithreaded runtime for a given thread. Must be - * called by each thread (other than the one that called wasm_rt_init) - * before initializing a Wasm module or calling an exported - * function. - */ -void wasm_rt_init_thread(void); - -/* - * Free the individual thread's state. - */ -void wasm_rt_free_thread(void); - -/** A hardened jmp_buf that allows checking for initialization before use */ -typedef struct { - /** Is the jmp buf intialized? */ - bool initialized; - /** jmp_buf contents */ - jmp_buf buffer; -} wasm_rt_jmp_buf; - -#ifndef _WIN32 -#define WASM_RT_SETJMP_SETBUF(buf) sigsetjmp(buf, 1) -#else -#define WASM_RT_SETJMP_SETBUF(buf) setjmp(buf) -#endif - -#define WASM_RT_SETJMP(buf) \ - ((buf).initialized = true, WASM_RT_SETJMP_SETBUF((buf).buffer)) - -#ifndef _WIN32 -#define WASM_RT_LONGJMP_UNCHECKED(buf, val) siglongjmp(buf, val) -#else -#define WASM_RT_LONGJMP_UNCHECKED(buf, val) longjmp(buf, val) -#endif - -#define WASM_RT_LONGJMP(buf, val) \ - /** Abort on failure as this may be called in the trap handler */ \ - if (!((buf).initialized)) \ - abort(); \ - (buf).initialized = false; \ - WASM_RT_LONGJMP_UNCHECKED((buf).buffer, val) - -/** - * Stop execution immediately and jump back to the call to `wasm_rt_impl_try`. - * The result of `wasm_rt_impl_try` will be the provided trap reason. - * - * This is typically called by the generated code, and not the embedder. - */ -WASM_RT_NO_RETURN void wasm_rt_trap(wasm_rt_trap_t); - -/** Return a human readable error string based on a trap type. */ -const char* wasm_rt_strerror(wasm_rt_trap_t trap); - -#define wasm_rt_try(target) WASM_RT_SETJMP(target) - -/** - * Initialize a Memory object with an initial page size of `initial_pages` and - * a maximum page size of `max_pages`, indexed with an i32 or i64. - * - * ``` - * wasm_rt_memory_t my_memory; - * // 1 initial page (65536 bytes), and a maximum of 2 pages, - * // indexed with an i32 - * wasm_rt_allocate_memory(&my_memory, 1, 2, false); - * ``` - */ -void wasm_rt_allocate_memory(wasm_rt_memory_t*, - uint64_t initial_pages, - uint64_t max_pages, - bool is64); - -/** - * Grow a Memory object by `pages`, and return the previous page count. If - * this new page count is greater than the maximum page count, the grow fails - * and 0xffffffffu (UINT32_MAX) is returned instead. + * Convenience macro to use before calling a wasm function. On first execution + * it will return `WASM_RT_TRAP_NONE` (i.e. 0). If the function traps, it will + * jump back and return the trap that occurred. * - * ``` - * wasm_rt_memory_t my_memory; - * ... - * // Grow memory by 10 pages. - * uint32_t old_page_size = wasm_rt_grow_memory(&my_memory, 10); - * if (old_page_size == UINT32_MAX) { - * // Failed to grow memory. - * } - * ``` - */ -uint64_t wasm_rt_grow_memory(wasm_rt_memory_t*, uint64_t pages); - -/** Free a Memory object. */ -void wasm_rt_free_memory(wasm_rt_memory_t*); - -#ifdef WASM_RT_C11_AVAILABLE -/** Shared memory version of wasm_rt_allocate_memory */ -void wasm_rt_allocate_memory_shared(wasm_rt_shared_memory_t*, - uint64_t initial_pages, - uint64_t max_pages, - bool is64); - -/** Shared memory version of wasm_rt_grow_memory */ -uint64_t wasm_rt_grow_memory_shared(wasm_rt_shared_memory_t*, uint64_t pages); - -/** Shared memory version of wasm_rt_free_memory */ -void wasm_rt_free_memory_shared(wasm_rt_shared_memory_t*); -#endif - -/** - * Initialize a funcref Table object with an element count of `elements` and a - * maximum size of `max_elements`. + * ``` + * wasm_rt_trap_t code = wasm_rt_impl_try(); + * if (code != 0) { + * printf("A trap occurred with code: %d\n", code); + * ... + * } * - * ``` - * wasm_rt_funcref_table_t my_table; - * // 5 elements and a maximum of 10 elements. - * wasm_rt_allocate_funcref_table(&my_table, 5, 10); - * ``` - */ -void wasm_rt_allocate_funcref_table(wasm_rt_funcref_table_t*, - uint32_t elements, - uint32_t max_elements); - -/** Free a funcref Table object. */ -void wasm_rt_free_funcref_table(wasm_rt_funcref_table_t*); - -/** - * Initialize an externref Table object with an element count - * of `elements` and a maximum size of `max_elements`. - * Usage as per wasm_rt_allocate_funcref_table. - */ -void wasm_rt_allocate_externref_table(wasm_rt_externref_table_t*, - uint32_t elements, - uint32_t max_elements); - -/** Free an externref Table object. */ -void wasm_rt_free_externref_table(wasm_rt_externref_table_t*); - -/** - * Grow a Table object by `delta` elements (giving the new elements the value - * `init`), and return the previous element count. If this new element count is - * greater than the maximum element count, the grow fails and 0xffffffffu - * (UINT32_MAX) is returned instead. + * // Call the potentially-trapping function. + * my_wasm_func(); + * ``` */ -uint32_t wasm_rt_grow_funcref_table(wasm_rt_funcref_table_t*, - uint32_t delta, - wasm_rt_funcref_t init); -uint32_t wasm_rt_grow_externref_table(wasm_rt_externref_table_t*, - uint32_t delta, - wasm_rt_externref_t init); +#define wasm_rt_impl_try() \ + (WASM_RT_SAVE_STACK_DEPTH(), wasm_rt_set_unwind_target(&g_wasm_rt_jmp_buf), \ + WASM_RT_SETJMP(g_wasm_rt_jmp_buf)) #ifdef __cplusplus } #endif -#endif /* WASM_RT_H_ */ \ No newline at end of file +#endif /* WASM_RT_IMPL_H_ */ diff --git a/packages/codegen/assets/wasm-rt/wasm-rt-mem-impl-helper.inc b/packages/codegen/assets/wasm-rt/wasm-rt-mem-impl-helper.inc index 2c2f9b75..63d10bc5 100644 --- a/packages/codegen/assets/wasm-rt/wasm-rt-mem-impl-helper.inc +++ b/packages/codegen/assets/wasm-rt/wasm-rt-mem-impl-helper.inc @@ -47,50 +47,94 @@ #define MEMORY_API_NAME(name) name##_shared #define MEMORY_CELL_TYPE _Atomic volatile uint8_t* -#if WASM_RT_USE_C11THREADS -#define MEMORY_LOCK_VAR_INIT(name) C11_MEMORY_LOCK_VAR_INIT(name) -#define MEMORY_LOCK_AQUIRE(name) C11_MEMORY_LOCK_AQUIRE(name) -#define MEMORY_LOCK_RELEASE(name) C11_MEMORY_LOCK_RELEASE(name) +#if WASM_RT_USE_CRITICALSECTION +#define MEMORY_LOCK_VAR_INIT(name) WIN_MEMORY_LOCK_VAR_INIT(name) +#define MEMORY_LOCK_AQUIRE(name) WIN_MEMORY_LOCK_AQUIRE(name) +#define MEMORY_LOCK_RELEASE(name) WIN_MEMORY_LOCK_RELEASE(name) #elif WASM_RT_USE_PTHREADS #define MEMORY_LOCK_VAR_INIT(name) PTHREAD_MEMORY_LOCK_VAR_INIT(name) #define MEMORY_LOCK_AQUIRE(name) PTHREAD_MEMORY_LOCK_AQUIRE(name) #define MEMORY_LOCK_RELEASE(name) PTHREAD_MEMORY_LOCK_RELEASE(name) -#elif WASM_RT_USE_CRITICALSECTION -#define MEMORY_LOCK_VAR_INIT(name) WIN_MEMORY_LOCK_VAR_INIT(name) -#define MEMORY_LOCK_AQUIRE(name) WIN_MEMORY_LOCK_AQUIRE(name) -#define MEMORY_LOCK_RELEASE(name) WIN_MEMORY_LOCK_RELEASE(name) #endif #endif +bool MEMORY_API_NAME(wasm_rt_memory_is_default32)(const MEMORY_TYPE* memory) { + return memory->page_size == WASM_DEFAULT_PAGE_SIZE && !memory->is64; +} + void MEMORY_API_NAME(wasm_rt_allocate_memory)(MEMORY_TYPE* memory, uint64_t initial_pages, uint64_t max_pages, - bool is64) { - uint64_t byte_length = initial_pages * WASM_PAGE_SIZE; + bool is64, + uint32_t page_size) { + uint64_t byte_length = initial_pages * page_size; memory->size = byte_length; memory->pages = initial_pages; memory->max_pages = max_pages; memory->is64 = is64; + memory->page_size = page_size; MEMORY_LOCK_VAR_INIT(memory->mem_lock); #if WASM_RT_USE_MMAP - const uint64_t mmap_size = - get_alloc_size_for_mmap(memory->max_pages, memory->is64); - void* addr = os_mmap(mmap_size); - if (!addr) { - os_print_last_error("os_mmap failed."); - abort(); + if (MEMORY_API_NAME(wasm_rt_memory_is_default32)(memory)) { + const uint64_t mmap_size = + get_alloc_size_for_mmap_default32(memory->max_pages); + uint8_t* addr = os_mmap(mmap_size); + if (!addr) { + os_print_last_error("os_mmap failed."); + abort(); + } + uint8_t* data_end = addr + mmap_size; +#if !WABT_BIG_ENDIAN + int ret = os_mprotect(addr, byte_length); +#else + int ret = os_mprotect(data_end - byte_length, byte_length); +#endif + if (ret != 0) { + os_print_last_error("os_mprotect failed."); + abort(); + } + memory->data = (MEMORY_CELL_TYPE)addr; + memory->data_end = (MEMORY_CELL_TYPE)data_end; + return; } - int ret = os_mprotect(addr, byte_length); - if (ret != 0) { - os_print_last_error("os_mprotect failed."); - abort(); +#endif + + memory->data = (MEMORY_CELL_TYPE)calloc(byte_length, 1); + memory->data_end = memory->data + byte_length; +} + +// Returns 0 on success +static int MEMORY_API_NAME(expand_data_allocation)(MEMORY_TYPE* memory, + uint64_t old_size, + uint64_t new_size, + uint64_t delta_size) { +#if WASM_RT_USE_MMAP + if (MEMORY_API_NAME(wasm_rt_memory_is_default32)(memory)) { +#if !WABT_BIG_ENDIAN + return os_mprotect((void*)(memory->data + old_size), delta_size); +#else + return os_mprotect((void*)(memory->data_end - old_size - delta_size), + delta_size); +#endif + } +#endif + + uint8_t* new_data = realloc((void*)memory->data, new_size); + if (new_data == NULL) { + return -1; } - memory->data = addr; +#if !WABT_BIG_ENDIAN + memset((void*)(new_data + old_size), 0, delta_size); #else - memory->data = calloc(byte_length, 1); + memmove((void*)(new_data + new_size - old_size), (void*)new_data, old_size); + memset((void*)new_data, 0, delta_size); #endif + + memory->data = (MEMORY_CELL_TYPE)new_data; + memory->data_end = memory->data + new_size; + return 0; } static uint64_t MEMORY_API_NAME(grow_memory_impl)(MEMORY_TYPE* memory, @@ -103,31 +147,18 @@ static uint64_t MEMORY_API_NAME(grow_memory_impl)(MEMORY_TYPE* memory, if (new_pages < old_pages || new_pages > memory->max_pages) { return (uint64_t)-1; } - uint64_t old_size = old_pages * WASM_PAGE_SIZE; - uint64_t new_size = new_pages * WASM_PAGE_SIZE; - uint64_t delta_size = delta * WASM_PAGE_SIZE; -#if WASM_RT_USE_MMAP - MEMORY_CELL_TYPE new_data = memory->data; - int ret = os_mprotect((void*)(new_data + old_size), delta_size); - if (ret != 0) { - return (uint64_t)-1; - } -#else - MEMORY_CELL_TYPE new_data = realloc((void*)memory->data, new_size); - if (new_data == NULL) { + uint64_t old_size = old_pages * memory->page_size; + uint64_t new_size = new_pages * memory->page_size; + uint64_t delta_size = delta * memory->page_size; + + int err = MEMORY_API_NAME(expand_data_allocation)(memory, old_size, new_size, + delta_size); + if (err != 0) { return (uint64_t)-1; } -#if !WABT_BIG_ENDIAN - memset((void*)(new_data + old_size), 0, delta_size); -#endif -#endif -#if WABT_BIG_ENDIAN - memmove((void*)(new_data + new_size - old_size), (void*)new_data, old_size); - memset((void*)new_data, 0, delta_size); -#endif + memory->pages = new_pages; memory->size = new_size; - memory->data = new_data; return old_pages; } @@ -146,12 +177,14 @@ uint64_t MEMORY_API_NAME(wasm_rt_grow_memory)(MEMORY_TYPE* memory, void MEMORY_API_NAME(wasm_rt_free_memory)(MEMORY_TYPE* memory) { #if WASM_RT_USE_MMAP - const uint64_t mmap_size = - get_alloc_size_for_mmap(memory->max_pages, memory->is64); - os_munmap((void*)memory->data, mmap_size); // ignore error -#else - free((void*)memory->data); + if (MEMORY_API_NAME(wasm_rt_memory_is_default32)(memory)) { + const uint64_t mmap_size = + get_alloc_size_for_mmap_default32(memory->max_pages); + os_munmap((void*)memory->data, mmap_size); // ignore error + return; + } #endif + free((void*)memory->data); } #undef MEMORY_LOCK_RELEASE diff --git a/packages/codegen/assets/wasm-rt/wasm-rt-mem-impl.c b/packages/codegen/assets/wasm-rt/wasm-rt-mem-impl.c index b8a383ec..953c92db 100644 --- a/packages/codegen/assets/wasm-rt/wasm-rt-mem-impl.c +++ b/packages/codegen/assets/wasm-rt/wasm-rt-mem-impl.c @@ -25,28 +25,10 @@ #include #endif -#define WASM_PAGE_SIZE 65536 - #ifdef WASM_RT_GROW_FAILED_HANDLER extern void WASM_RT_GROW_FAILED_HANDLER(); #endif -#define C11_MEMORY_LOCK_VAR_INIT(name) \ - if (mtx_init(&(name), mtx_plain) != thrd_success) { \ - fprintf(stderr, "Lock init failed\n"); \ - abort(); \ - } -#define C11_MEMORY_LOCK_AQUIRE(name) \ - if (mtx_lock(&(name)) != thrd_success) { \ - fprintf(stderr, "Lock acquire failed\n"); \ - abort(); \ - } -#define C11_MEMORY_LOCK_RELEASE(name) \ - if (mtx_unlock(&(name)) != thrd_success) { \ - fprintf(stderr, "Lock release failed\n"); \ - abort(); \ - } - #define PTHREAD_MEMORY_LOCK_VAR_INIT(name) \ if (pthread_mutex_init(&(name), NULL) != 0) { \ fprintf(stderr, "Lock init failed\n"); \ @@ -112,39 +94,41 @@ static void os_print_last_error(const char* msg) { } } -#else +#else /* !_WIN32 */ static void* os_mmap(size_t size) { - int map_prot = PROT_NONE; - int map_flags = MAP_ANONYMOUS | MAP_PRIVATE; - uint8_t* addr = mmap(NULL, size, map_prot, map_flags, -1, 0); - if (addr == MAP_FAILED) - return NULL; - return addr; + int map_prot = PROT_NONE; + int map_flags = MAP_ANONYMOUS | MAP_PRIVATE; + uint8_t* addr = mmap(NULL, size, map_prot, map_flags, -1, 0); + if (addr == MAP_FAILED) + return NULL; + return addr; } static int os_munmap(void* addr, size_t size) { - return munmap(addr, size); + return munmap(addr, size); } static int os_mprotect(void* addr, size_t size) { - return mprotect(addr, size, PROT_READ | PROT_WRITE); + if (size == 0) { + return 0; + } + return mprotect(addr, size, PROT_READ | PROT_WRITE); } static void os_print_last_error(const char* msg) { - perror(msg); + perror(msg); } -#endif +#endif /* _WIN32 */ -static uint64_t get_alloc_size_for_mmap(uint64_t max_pages, bool is64) { - assert(!is64 && "memory64 is not yet compatible with WASM_RT_USE_MMAP"); +static uint64_t get_alloc_size_for_mmap_default32(uint64_t max_pages) { #if WASM_RT_MEMCHECK_GUARD_PAGES - /* Reserve 8GiB. */ - const uint64_t max_size = 0x200000000ul; - return max_size; + /* Reserve 8GiB. */ + const uint64_t max_size = 0x200000000ul; + return max_size; #else - if (max_pages != 0) { - const uint64_t max_size = max_pages * WASM_PAGE_SIZE; + if (max_pages != 0) { + const uint64_t max_size = max_pages * WASM_DEFAULT_PAGE_SIZE; return max_size; } @@ -154,7 +138,7 @@ static uint64_t get_alloc_size_for_mmap(uint64_t max_pages, bool is64) { #endif } -#endif +#endif /* WASM_RT_USE_MMAP */ // Include operations for memory #define WASM_RT_MEM_OPS @@ -175,4 +159,3 @@ static uint64_t get_alloc_size_for_mmap(uint64_t max_pages, bool is64) { #undef WIN_MEMORY_LOCK_VAR_INIT #undef WIN_MEMORY_LOCK_AQUIRE #undef WIN_MEMORY_LOCK_RELEASE -#undef WASM_PAGE_SIZE \ No newline at end of file diff --git a/packages/codegen/assets/wasm-rt/wasm-rt.h b/packages/codegen/assets/wasm-rt/wasm-rt.h index 23ea4f3a..bce33ce8 100644 --- a/packages/codegen/assets/wasm-rt/wasm-rt.h +++ b/packages/codegen/assets/wasm-rt/wasm-rt.h @@ -67,22 +67,21 @@ extern "C" { #include #define WASM_RT_MUTEX CRITICAL_SECTION #define WASM_RT_USE_CRITICALSECTION 1 -#elif defined(__APPLE__) || defined(__STDC_NO_THREADS__) +#else #include #define WASM_RT_MUTEX pthread_mutex_t #define WASM_RT_USE_PTHREADS 1 -#else -#include -#define WASM_RT_MUTEX mtx_t -#define WASM_RT_USE_C11THREADS 1 #endif #endif -#ifdef _MSC_VER -#define WASM_RT_THREAD_LOCAL __declspec(thread) -#elif defined(WASM_RT_C11_AVAILABLE) +#ifdef WASM_RT_C11_AVAILABLE #define WASM_RT_THREAD_LOCAL _Thread_local +#elif defined(_MSC_VER) +#define WASM_RT_THREAD_LOCAL __declspec(thread) +#elif (defined(__GNUC__) || defined(__clang__)) && !defined(__APPLE__) +// Disabled on Apple systems due to sporadic test failures. +#define WASM_RT_THREAD_LOCAL __thread #else #define WASM_RT_THREAD_LOCAL #endif @@ -126,7 +125,7 @@ extern "C" { * needed (so we can use the guard based range checks below). */ #ifndef WASM_RT_USE_MMAP -#if UINTPTR_MAX > 0xffffffff && !SUPPORT_MEMORY64 +#if UINTPTR_MAX > 0xffffffff #define WASM_RT_USE_MMAP 1 #else #define WASM_RT_USE_MMAP 0 @@ -141,15 +140,13 @@ extern "C" { * * BOUNDS_CHECK: memory accesses are checked with explicit bounds checks. * - * This defaults to GUARD_PAGES as this is the fasest option, iff the + * This defaults to GUARD_PAGES as this is the fastest option, iff the * requirements of GUARD_PAGES --- 64-bit platforms, MMAP allocation strategy, - * no 64-bit memories, no big-endian --- are met. This falls back to BOUNDS - * otherwise. + * no 64-bit memories --- are met. This falls back to BOUNDS otherwise. */ /** Check if Guard checks are supported */ -#if UINTPTR_MAX > 0xffffffff && WASM_RT_USE_MMAP && !SUPPORT_MEMORY64 && \ - !WABT_BIG_ENDIAN +#if UINTPTR_MAX > 0xffffffff && WASM_RT_USE_MMAP #define WASM_RT_GUARD_PAGES_SUPPORTED 1 #else #define WASM_RT_GUARD_PAGES_SUPPORTED 0 @@ -228,6 +225,34 @@ extern "C" { #define WASM_RT_SEGUE_FREE_SEGMENT 0 #endif +#ifndef WASM_RT_USE_SEGUE +// Memory functions can use the segue optimization if allowed. The segue +// optimization uses x86 segments to point to a linear memory. We use this +// optimization when: +// +// (1) Segue is allowed using WASM_RT_ALLOW_SEGUE +// (2) on x86_64 without WABT_BIG_ENDIAN enabled +// (3) the compiler supports: intrinsics for (rd|wr)gsbase, "address namespaces" +// for accessing pointers, and supports memcpy on pointers with custom +// "address namespaces". GCC does not support the memcpy requirement, so +// this leaves only clang (version 9 or later) for now. +// (4) The OS provides a way to query if (rd|wr)gsbase is allowed by the kernel +// or the implementation has to use a syscall for this. +// (5) The OS doesn't replace the segment register on context switch which +// eliminates windows for now +// +// While more OS can be supported in the future, we only support linux for now +#if WASM_RT_ALLOW_SEGUE && !WABT_BIG_ENDIAN && \ + (defined(__x86_64__) || defined(_M_X64)) && __clang__ && \ + (__clang_major__ >= 9) && __has_builtin(__builtin_ia32_wrgsbase64) && \ + !defined(_WIN32) && !defined(__ANDROID__) && \ + (defined(__linux__) || defined(__FreeBSD__)) +#define WASM_RT_USE_SEGUE 1 +#else +#define WASM_RT_USE_SEGUE 0 +#endif +#endif + /** * This macro, if defined, allows the embedder to disable all stack exhaustion * checks. This a non conformant configuration, i.e., this does not respect @@ -240,13 +265,15 @@ extern "C" { /** * We need to detect and trap stack overflows. If we use a signal handler on * POSIX systems, this can detect call stack overflows. On windows, or platforms - * without a signal handler, we use stack depth counting. + * without a signal handler, we use stack depth counting. The s390x big endian + * platform additionally seems to have issues with stack guard pages, so we play + * it safe and use stack counting on big endian platforms. */ #if !defined(WASM_RT_STACK_DEPTH_COUNT) && \ !defined(WASM_RT_STACK_EXHAUSTION_HANDLER) && \ !WASM_RT_NONCONFORMING_UNCHECKED_STACK_EXHAUSTION -#if WASM_RT_INSTALL_SIGNAL_HANDLER && !defined(_WIN32) +#if WASM_RT_INSTALL_SIGNAL_HANDLER && !defined(_WIN32) && !WABT_BIG_ENDIAN #define WASM_RT_STACK_EXHAUSTION_HANDLER 1 #else #define WASM_RT_STACK_DEPTH_COUNT 1 @@ -306,7 +333,7 @@ extern WASM_RT_THREAD_LOCAL uint32_t wasm_rt_call_stack_depth; #endif -#if WASM_RT_USE_SEGUE || WASM_RT_ALLOW_SEGUE +#if WASM_RT_USE_SEGUE /** * The segue optimization uses x86 segments to point to a linear memory. If * used, the runtime must query whether it can use the fast userspace wrgsbase @@ -349,31 +376,33 @@ extern WASM_RT_THREAD_LOCAL void* wasm_rt_last_segment_val; /** Reason a trap occurred. Provide this to `wasm_rt_trap`. */ typedef enum { - WASM_RT_TRAP_NONE, /** No error. */ - WASM_RT_TRAP_OOB, /** Out-of-bounds access in linear memory or a table. */ - WASM_RT_TRAP_INT_OVERFLOW, /** Integer overflow on divide or truncation. */ - WASM_RT_TRAP_DIV_BY_ZERO, /** Integer divide by zero. */ - WASM_RT_TRAP_INVALID_CONVERSION, /** Conversion from NaN to integer. */ - WASM_RT_TRAP_UNREACHABLE, /** Unreachable instruction executed. */ - WASM_RT_TRAP_CALL_INDIRECT, /** Invalid call_indirect, for any reason. */ - WASM_RT_TRAP_UNCAUGHT_EXCEPTION, /** Exception thrown and not caught. */ - WASM_RT_TRAP_UNALIGNED, /** Unaligned atomic instruction executed. */ + WASM_RT_TRAP_NONE, /** No error. */ + WASM_RT_TRAP_OOB, /** Out-of-bounds access in linear memory or a table. */ + WASM_RT_TRAP_INT_OVERFLOW, /** Integer overflow on divide or truncation. */ + WASM_RT_TRAP_DIV_BY_ZERO, /** Integer divide by zero. */ + WASM_RT_TRAP_INVALID_CONVERSION, /** Conversion from NaN to integer. */ + WASM_RT_TRAP_UNREACHABLE, /** Unreachable instruction executed. */ + WASM_RT_TRAP_CALL_INDIRECT, /** Invalid call_indirect, for any reason. */ + WASM_RT_TRAP_NULL_REF, /** Null reference. */ + WASM_RT_TRAP_UNCAUGHT_EXCEPTION, /** Exception thrown and not caught. */ + WASM_RT_TRAP_UNALIGNED, /** Unaligned atomic instruction executed. */ #if WASM_RT_MERGED_OOB_AND_EXHAUSTION_TRAPS - WASM_RT_TRAP_EXHAUSTION = WASM_RT_TRAP_OOB, + WASM_RT_TRAP_EXHAUSTION = WASM_RT_TRAP_OOB, #else - WASM_RT_TRAP_EXHAUSTION, /** Call stack exhausted. */ + WASM_RT_TRAP_EXHAUSTION, /** Call stack exhausted. */ #endif } wasm_rt_trap_t; /** Value types. Used to define function signatures. */ typedef enum { - WASM_RT_I32, - WASM_RT_I64, - WASM_RT_F32, - WASM_RT_F64, - WASM_RT_V128, - WASM_RT_FUNCREF, - WASM_RT_EXTERNREF, + WASM_RT_I32, + WASM_RT_I64, + WASM_RT_F32, + WASM_RT_F64, + WASM_RT_V128, + WASM_RT_FUNCREF, + WASM_RT_EXTERNREF, + WASM_RT_EXNREF, } wasm_rt_type_t; /** @@ -390,9 +419,9 @@ typedef void (*wasm_rt_function_ptr_t)(void); * single-member struct to allow a recursive definition.) */ typedef struct wasm_rt_tailcallee_t { - void (*fn)(void** instance_ptr, - void* tail_call_stack, - struct wasm_rt_tailcallee_t* next); + void (*fn)(void** instance_ptr, + void* tail_call_stack, + struct wasm_rt_tailcallee_t* next); } wasm_rt_tailcallee_t; /** @@ -406,21 +435,21 @@ typedef const char* wasm_rt_func_type_t; * These can be stored in tables of type funcref, or used as values. */ typedef struct { - /** The function's type. */ - wasm_rt_func_type_t func_type; - /** - * The function. The embedder must know the actual C signature of the function - * and cast to it before calling. - */ - wasm_rt_function_ptr_t func; - /** An alternate version of the function to be used when tail-called. */ - wasm_rt_tailcallee_t func_tailcallee; - /** - * A function instance is a closure of the function over an instance - * of the originating module. The module_instance element will be passed into - * the function at runtime. - */ - void* module_instance; + /** The function's type. */ + wasm_rt_func_type_t func_type; + /** + * The function. The embedder must know the actual C signature of the function + * and cast to it before calling. + */ + wasm_rt_function_ptr_t func; + /** An alternate version of the function to be used when tail-called. */ + wasm_rt_tailcallee_t func_tailcallee; + /** + * A function instance is a closure of the function over an instance + * of the originating module. The module_instance element will be passed into + * the function at runtime. + */ + void* module_instance; } wasm_rt_funcref_t; /** Default (null) value of a funcref */ @@ -435,19 +464,21 @@ typedef void* wasm_rt_externref_t; /** A Memory object. */ typedef struct { - /** The linear memory data, with a byte length of `size`. */ - uint8_t* data; - /** The current page count for this Memory object. */ - uint64_t pages; - /** - * The maximum page count for this Memory object. If there is no maximum, - * `max_pages` is 0xffffffffu (i.e. UINT32_MAX). - */ - uint64_t max_pages; - /** The current size of the linear memory, in bytes. */ - uint64_t size; - /** Is this memory indexed by u64 (as opposed to default u32) */ - bool is64; + /** The linear memory data, with a byte length of `size`. */ + uint8_t* data; + /** The location after the the reserved space for the linear memory data. */ + uint8_t* data_end; + /** The page size for this Memory object + (always 64 KiB without the custom-page-sizes feature) */ + uint32_t page_size; + /** The current page count for this Memory object. */ + uint64_t pages; + /** The maximum page count for this Memory object. */ + uint64_t max_pages; + /** The current size of the linear memory, in bytes. */ + uint64_t size; + /** Is this memory indexed by u64 (as opposed to default u32) */ + bool is64; } wasm_rt_memory_t; #ifdef WASM_RT_C11_AVAILABLE @@ -465,12 +496,15 @@ typedef struct { * volatile. */ _Atomic volatile uint8_t* data; + /** The location one byte after the reserved space for the linear memory data. + * This includes any reserved pages that are not yet allocated. */ + _Atomic volatile uint8_t* data_end; + /** The page size for this Memory object + (always 64 KiB without the custom-page-sizes feature) */ + uint32_t page_size; /** The current page count for this Memory object. */ uint64_t pages; - /** - * The maximum page count for this Memory object. If there is no maximum, - * `max_pages` is 0xffffffffu (i.e. UINT32_MAX). - */ + /* The maximum page count for this Memory object. */ uint64_t max_pages; /** The current size of the linear memory, in bytes. */ uint64_t size; @@ -483,28 +517,28 @@ typedef struct { /** A Table of type funcref. */ typedef struct { - /** The table element data, with an element count of `size`. */ - wasm_rt_funcref_t* data; - /** - * The maximum element count of this Table object. If there is no maximum, - * `max_size` is 0xffffffffu (i.e. UINT32_MAX). - */ - uint32_t max_size; - /** The current element count of the table. */ - uint32_t size; + /** The table element data, with an element count of `size`. */ + wasm_rt_funcref_t* data; + /** + * The maximum element count of this Table object. If there is no maximum, + * `max_size` is 0xffffffffu (i.e. UINT32_MAX). + */ + uint32_t max_size; + /** The current element count of the table. */ + uint32_t size; } wasm_rt_funcref_table_t; /** A Table of type externref. */ typedef struct { - /** The table element data, with an element count of `size`. */ - wasm_rt_externref_t* data; - /** - * The maximum element count of this Table object. If there is no maximum, - * `max_size` is 0xffffffffu (i.e. UINT32_MAX). - */ - uint32_t max_size; - /** The current element count of the table. */ - uint32_t size; + /** The table element data, with an element count of `size`. */ + wasm_rt_externref_t* data; + /** + * The maximum element count of this Table object. If there is no maximum, + * `max_size` is 0xffffffffu (i.e. UINT32_MAX). + */ + uint32_t max_size; + /** The current element count of the table. */ + uint32_t size; } wasm_rt_externref_table_t; /** Initialize the runtime. */ @@ -531,10 +565,10 @@ void wasm_rt_free_thread(void); /** A hardened jmp_buf that allows checking for initialization before use */ typedef struct { - /** Is the jmp buf intialized? */ - bool initialized; - /** jmp_buf contents */ - jmp_buf buffer; + /** Is the jmp buf intialized? */ + bool initialized; + /** jmp_buf contents */ + jmp_buf buffer; } wasm_rt_jmp_buf; #ifndef _WIN32 @@ -572,6 +606,9 @@ const char* wasm_rt_strerror(wasm_rt_trap_t trap); #define wasm_rt_try(target) WASM_RT_SETJMP(target) +/** WebAssembly's default page size (64 KiB) */ +#define WASM_DEFAULT_PAGE_SIZE 65536 + /** * Initialize a Memory object with an initial page size of `initial_pages` and * a maximum page size of `max_pages`, indexed with an i32 or i64. @@ -580,13 +617,14 @@ const char* wasm_rt_strerror(wasm_rt_trap_t trap); * wasm_rt_memory_t my_memory; * // 1 initial page (65536 bytes), and a maximum of 2 pages, * // indexed with an i32 - * wasm_rt_allocate_memory(&my_memory, 1, 2, false); + * wasm_rt_allocate_memory(&my_memory, 1, 2, false, WASM_DEFAULT_PAGE_SIZE); * ``` */ void wasm_rt_allocate_memory(wasm_rt_memory_t*, uint64_t initial_pages, uint64_t max_pages, - bool is64); + bool is64, + uint32_t page_size); /** * Grow a Memory object by `pages`, and return the previous page count. If @@ -613,7 +651,8 @@ void wasm_rt_free_memory(wasm_rt_memory_t*); void wasm_rt_allocate_memory_shared(wasm_rt_shared_memory_t*, uint64_t initial_pages, uint64_t max_pages, - bool is64); + bool is64, + uint32_t page_size); /** Shared memory version of wasm_rt_grow_memory */ uint64_t wasm_rt_grow_memory_shared(wasm_rt_shared_memory_t*, uint64_t pages); @@ -668,4 +707,4 @@ uint32_t wasm_rt_grow_externref_table(wasm_rt_externref_table_t*, } #endif -#endif /* WASM_RT_H_ */ \ No newline at end of file +#endif /* WASM_RT_H_ */ diff --git a/packages/codegen/src/codegen-pipeline.ts b/packages/codegen/src/codegen-pipeline.ts index 1ab4dffd..1157c3f0 100644 --- a/packages/codegen/src/codegen-pipeline.ts +++ b/packages/codegen/src/codegen-pipeline.ts @@ -1,3 +1,4 @@ +import { androidCmake } from './pipeline/react-native/android-cmake.js'; import { cocoapods } from './pipeline/react-native/cocoapods.js'; import { metroResolver } from './pipeline/react-native/metro.js'; import { reactNativeTurboModule } from './pipeline/react-native/turbomodule.js'; @@ -5,6 +6,7 @@ import { embedWasmRuntime } from './pipeline/wasm2c-runtime.js'; import type { Plugin } from './plugin.js'; export const DEFAULT_PLUGINS: Plugin[] = [ + androidCmake(), cocoapods(), metroResolver(), reactNativeTurboModule(), diff --git a/packages/codegen/src/pipeline/react-native/android-cmake.ts b/packages/codegen/src/pipeline/react-native/android-cmake.ts new file mode 100644 index 00000000..4bc770cd --- /dev/null +++ b/packages/codegen/src/pipeline/react-native/android-cmake.ts @@ -0,0 +1,87 @@ +import stripIndent from 'strip-indent'; +import type { Plugin } from '../../plugin.js'; + +/** + * Plugin that generates CMakeLists.txt for Android to link generated native code. + * + * This is the Android equivalent of the iOS CocoaPods integration. + * It generates a CMakeLists.txt that the polygen library can include via add_subdirectory. + */ +export function androidCmake(): Plugin { + return { + name: 'core/android-cmake', + title: 'Android CMake Integration', + + async hostProjectGenerated({ projectOutput }): Promise { + await projectOutput.writeAllTo({ + 'CMakeLists.txt': buildCMakeListsSource(), + }); + }, + }; +} + +/** + * Builds the CMakeLists.txt for Android generated code. + * + * This CMakeLists.txt: + * 1. Collects all generated C/C++ sources (loader.cpp, module files, wasm-rt runtime) + * 2. Defines a static library target that can be linked into libpolygen.so + * 3. Sets up include directories for the polygen library headers + */ +function buildCMakeListsSource(): string { + return stripIndent( + ` + # THIS FILE WAS GENERATED BY \`polygen\` + # DO NOT EDIT THIS FILE. ANY CHANGES WILL BE LOST. + + cmake_minimum_required(VERSION 3.13) + + # Marker that this is polygen-generated code + set(POLYGEN_GENERATED_HOST ON PARENT_SCOPE) + + # Collect all C++ sources from generated modules + file(GLOB_RECURSE POLYGEN_GENERATED_CPP_SOURCES + "\${CMAKE_CURRENT_SOURCE_DIR}/*.cpp" + ) + + # Collect all C sources (wasm-rt runtime and generated module code) + file(GLOB_RECURSE POLYGEN_GENERATED_C_SOURCES + "\${CMAKE_CURRENT_SOURCE_DIR}/*.c" + ) + + # Create an object library for generated code + # This allows the sources to be compiled and linked into libpolygen.so + add_library(polygen_generated OBJECT + \${POLYGEN_GENERATED_CPP_SOURCES} + \${POLYGEN_GENERATED_C_SOURCES} + ) + + # Include directories: + # - CMAKE_CURRENT_SOURCE_DIR: the generated code root (for loader.cpp, imports, etc.) + # - wasm-rt: the wasm-rt runtime headers (wasm-rt.h, etc.) + # - POLYGEN_INCLUDE_DIR: the polygen library headers (set by parent CMakeLists.txt) + # - POLYGEN_INCLUDE_DIR/ReactNativePolygen: for StaticLibraryModule.h, etc. + # - POLYGEN_INCLUDE_DIR/ReactNativePolygen/WebAssembly: for Module.h (included by StaticLibraryModule.h) + target_include_directories(polygen_generated PRIVATE + \${CMAKE_CURRENT_SOURCE_DIR} + \${CMAKE_CURRENT_SOURCE_DIR}/wasm-rt + \${POLYGEN_INCLUDE_DIR} + \${POLYGEN_INCLUDE_DIR}/ReactNativePolygen + \${POLYGEN_INCLUDE_DIR}/ReactNativePolygen/WebAssembly + ) + + # Link against ReactAndroid::jsi to get JSI headers + # This is found via prefab in the parent CMakeLists.txt + find_package(ReactAndroid REQUIRED CONFIG) + target_link_libraries(polygen_generated PRIVATE ReactAndroid::jsi) + + # Compiler flags matching polygen library + target_compile_options(polygen_generated PRIVATE + -fexceptions + -frtti + -O2 + -fstack-protector-all + ) + ` + ).trimStart(); +} diff --git a/packages/codegen/src/templates/library/static-lib.ts b/packages/codegen/src/templates/library/static-lib.ts index a2c2a3be..cd3e0287 100644 --- a/packages/codegen/src/templates/library/static-lib.ts +++ b/packages/codegen/src/templates/library/static-lib.ts @@ -55,7 +55,11 @@ export function buildStaticLibrarySource(module: W2CGeneratedModule) { ]) ); - const moduleVar = new cpp.VariableBuilder('moduleInfo') + // Use module-specific names to avoid duplicate symbol errors when linking multiple modules + const moduleInfoVarName = `${module.generatedClassName}ModuleInfo`; + const moduleSharedVarName = `${module.generatedClassName}ModuleSharedInfo`; + + const moduleVar = new cpp.VariableBuilder(moduleInfoVarName) .withType(moduleType) .withInitializer( (i) => @@ -71,7 +75,7 @@ export function buildStaticLibrarySource(module: W2CGeneratedModule) { true ); - const moduleSharedVar = new cpp.VariableBuilder('moduleSharedInfo') + const moduleSharedVar = new cpp.VariableBuilder(moduleSharedVarName) .withType(abstractModuleType.asSharedPtr()) .withInitializer((i) => i.symbol(moduleVar.name).addressOf()); diff --git a/packages/codegen/src/wasm2c/wasm2c.ts b/packages/codegen/src/wasm2c/wasm2c.ts index 91371de1..52fba547 100644 --- a/packages/codegen/src/wasm2c/wasm2c.ts +++ b/packages/codegen/src/wasm2c/wasm2c.ts @@ -16,10 +16,11 @@ export class Wasm2cError extends Error { } function assertVersion(output: string) { - if (!output.startsWith('1.0.36')) { + // Support both 1.0.36 and 1.0.39 (bundled wasm-rt updated to 1.0.39 API) + if (!output.startsWith('1.0.36') && !output.startsWith('1.0.39')) { const version = output.split(' ')[0]; throw new Wasm2cError( - `Unsupported wasm2c version: ${version}. Please use version 1.0.36.` + `Unsupported wasm2c version: ${version}. Please use version 1.0.36 or 1.0.39.` ); } } diff --git a/packages/polygen/android/CMakeLists.txt b/packages/polygen/android/CMakeLists.txt index f387bcfd..0e7b5c81 100644 --- a/packages/polygen/android/CMakeLists.txt +++ b/packages/polygen/android/CMakeLists.txt @@ -1,15 +1,143 @@ -cmake_minimum_required(VERSION 3.4.1) -project(Wasm) +cmake_minimum_required(VERSION 3.13) +project(Polygen) -set (CMAKE_VERBOSE_MAKEFILE ON) -set (CMAKE_CXX_STANDARD 14) +set(CMAKE_VERBOSE_MAKEFILE ON) +set(CMAKE_CXX_STANDARD 20) -add_library(react-native-wasm SHARED - ../cpp/react-native-wasm.cpp - cpp-adapter.cpp +# Find react-native directory +# When building as part of an app, this will find the react-native installation +if(NOT DEFINED REACT_ANDROID_DIR) + # Search upward for node_modules/react-native + set(SEARCH_DIR ${CMAKE_SOURCE_DIR}) + while(NOT EXISTS "${SEARCH_DIR}/node_modules/react-native/ReactAndroid") + get_filename_component(PARENT_DIR ${SEARCH_DIR} DIRECTORY) + if(PARENT_DIR STREQUAL SEARCH_DIR) + message(FATAL_ERROR "Could not find react-native in node_modules") + endif() + set(SEARCH_DIR ${PARENT_DIR}) + endwhile() + set(REACT_ANDROID_DIR "${SEARCH_DIR}/node_modules/react-native/ReactAndroid") + set(APP_NODE_MODULES_DIR "${SEARCH_DIR}/node_modules") +endif() + +# Include folly flags for proper compilation +include(${REACT_ANDROID_DIR}/cmake-utils/folly-flags.cmake) + +# Find React Native packages via prefab +find_package(ReactAndroid REQUIRED CONFIG) +find_package(fbjni REQUIRED CONFIG) + +# Check for polygen-generated code in .polygen-out/host/ +# This is the Android equivalent of iOS's ReactNativeWebAssemblyHost.podspec auto-linking +set(POLYGEN_HAS_GENERATED_CODE OFF) +set(POLYGEN_GENERATED_DIR "") + +# POLYGEN_APP_ROOT is passed from Gradle with the app's project directory +# This allows us to find the app's node_modules/.polygen-out/host/ even in monorepo setups +if(DEFINED POLYGEN_APP_ROOT AND EXISTS "${POLYGEN_APP_ROOT}/node_modules/.polygen-out/host/CMakeLists.txt") + set(POLYGEN_GENERATED_DIR "${POLYGEN_APP_ROOT}/node_modules/.polygen-out/host") + set(POLYGEN_HAS_GENERATED_CODE ON) +elseif(EXISTS "${APP_NODE_MODULES_DIR}/.polygen-out/host/CMakeLists.txt") + # Fallback: try the node_modules found during react-native search + set(POLYGEN_GENERATED_DIR "${APP_NODE_MODULES_DIR}/.polygen-out/host") + set(POLYGEN_HAS_GENERATED_CODE ON) +endif() + +if(POLYGEN_HAS_GENERATED_CODE) + message(STATUS "Found polygen-generated code at ${POLYGEN_GENERATED_DIR}") + # Set include directory for generated code to find polygen headers + set(POLYGEN_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../cpp") + add_subdirectory(${POLYGEN_GENERATED_DIR} ${CMAKE_BINARY_DIR}/polygen-generated) +else() + message(STATUS "No polygen-generated code found, using stub (run 'polygen generate' in your app)") +endif() + +# Collect all C++ sources from ReactNativePolygen (including subdirectories) +file(GLOB_RECURSE POLYGEN_CPP_SOURCES + "../cpp/ReactNativePolygen/*.cpp" +) + +# Collect all C sources from wasm-rt runtime (only if not using generated code) +# Generated code includes its own wasm-rt copy +if(NOT POLYGEN_HAS_GENERATED_CODE) + file(GLOB WASM_RT_C_SOURCES + "../cpp/wasm-rt/*.c" + ) +else() + set(WASM_RT_C_SOURCES "") +endif() + +# JNI registration sources +set(JNI_SOURCES + "src/main/jni/OnLoad.cpp" +) + +# Only include stub if we don't have generated code +# Generated code provides getModuleBag() via loader.cpp +if(NOT POLYGEN_HAS_GENERATED_CODE) + list(APPEND JNI_SOURCES "src/main/jni/ModuleBagStub.cpp") +endif() + +# Codegen-generated sources (only available when new architecture is enabled and codegen has run) +set(CODEGEN_JSI_SOURCE "${CMAKE_SOURCE_DIR}/build/generated/source/codegen/jni/react/renderer/components/RNPolygenSpec/RNPolygenSpecJSI-generated.cpp") +if(EXISTS "${CODEGEN_JSI_SOURCE}") + list(APPEND JNI_SOURCES "${CODEGEN_JSI_SOURCE}") +else() + message(STATUS "Codegen source not found at ${CODEGEN_JSI_SOURCE}, skipping (run with newArchEnabled=true after codegen)") +endif() + +# Create shared library +add_library(polygen SHARED + ${POLYGEN_CPP_SOURCES} + ${WASM_RT_C_SOURCES} + ${JNI_SOURCES} +) + +# Link generated code object library if available +if(POLYGEN_HAS_GENERATED_CODE) + target_sources(polygen PRIVATE $) +endif() + +# Include directories for our sources +target_include_directories(polygen PRIVATE + ../cpp + ../cpp/ReactNativePolygen + ../cpp/wasm-rt +) + +# Include generated code directory if available +if(POLYGEN_HAS_GENERATED_CODE) + target_include_directories(polygen PRIVATE + ${POLYGEN_GENERATED_DIR} + ) +endif() + +# Include directories for codegen-generated headers (only if they exist) +set(CODEGEN_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/build/generated/source/codegen/jni") +if(EXISTS "${CODEGEN_INCLUDE_DIR}") + target_include_directories(polygen PRIVATE + ${CODEGEN_INCLUDE_DIR} + ${CODEGEN_INCLUDE_DIR}/react/renderer/components/RNPolygenSpec + ) +endif() + +# Link against React Native and fbjni +target_link_libraries(polygen + ReactAndroid::jsi + ReactAndroid::react_nativemodule_core + ReactAndroid::turbomodulejsijni + ReactAndroid::reactnativejni + ReactAndroid::folly_runtime + fbjni::fbjni ) -# Specifies a path to native header files. -include_directories( - ../cpp +# Compiler flags +target_compile_options(polygen PRIVATE + -Wall + -fexceptions + -frtti + -O2 + -fstack-protector-all + -DLOG_TAG=\"Polygen\" + ${folly_FLAGS} ) diff --git a/packages/polygen/android/build.gradle b/packages/polygen/android/build.gradle index 434349c3..8d6b86de 100644 --- a/packages/polygen/android/build.gradle +++ b/packages/polygen/android/build.gradle @@ -1,6 +1,6 @@ buildscript { // Buildscript is evaluated before everything else so we can't use getExtOrDefault - def kotlin_version = rootProject.ext.has("kotlinVersion") ? rootProject.ext.get("kotlinVersion") : project.properties["Wasm_kotlinVersion"] + def kotlin_version = rootProject.ext.has("kotlinVersion") ? rootProject.ext.get("kotlinVersion") : project.properties["Polygen_kotlinVersion"] repositories { google() @@ -31,11 +31,11 @@ if (isNewArchitectureEnabled()) { } def getExtOrDefault(name) { - return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["Wasm_" + name] + return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["Polygen_" + name] } def getExtOrIntegerDefault(name) { - return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["Wasm_" + name]).toInteger() + return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["Polygen_" + name]).toInteger() } def supportsNamespace() { @@ -49,7 +49,7 @@ def supportsNamespace() { android { if (supportsNamespace()) { - namespace "com.wasm" + namespace "com.callstack.polygen" sourceSets { main { @@ -66,22 +66,52 @@ android { targetSdkVersion getExtOrIntegerDefault("targetSdkVersion") buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString() - externalNativeBuild { - cmake { - cppFlags "-O2 -frtti -fexceptions -Wall -fstack-protector-all" - abiFilters (*reactNativeArchitectures()) + // C++ TurboModule build is only enabled for new architecture + // Old architecture uses the Java/Kotlin stub that throws UnsupportedOperationException + if (isNewArchitectureEnabled()) { + externalNativeBuild { + cmake { + cppFlags "-O2 -frtti -fexceptions -Wall -fstack-protector-all" + abiFilters(*reactNativeArchitectures()) + // Pass app root directory to CMake so it can find .polygen-out/host/ + // rootProject.projectDir points to the app's android directory + arguments "-DANDROID_STL=c++_shared", + "-DPOLYGEN_APP_ROOT=${rootProject.projectDir.parent}" + } } } } - externalNativeBuild { - cmake { - path "CMakeLists.txt" + // Only configure CMake when new architecture is enabled + // The C++ sources require codegen-generated headers that only exist with new arch + if (isNewArchitectureEnabled()) { + externalNativeBuild { + cmake { + path "CMakeLists.txt" + } } } buildFeatures { buildConfig true + prefab isNewArchitectureEnabled() + } + + if (isNewArchitectureEnabled()) { + packagingOptions { + // Exclude React Native native libs from this library's AAR + // They will be provided by the app's react-native dependency + excludes += [ + "**/libreactnativejni.so", + "**/libfbjni.so", + "**/libjsi.so", + "**/libfolly_runtime.so", + "**/libglog.so", + "**/libhermes.so", + "**/libreact_*.so", + "**/libturbomodulejsijni.so" + ] + } } buildTypes { @@ -133,7 +163,7 @@ dependencies { if (isNewArchitectureEnabled()) { react { jsRootDir = file("../src/") - libraryName = "Wasm" - codegenJavaPackageName = "com.wasm" + libraryName = "RNPolygenSpec" + codegenJavaPackageName = "com.callstack.polygen" } } diff --git a/packages/polygen/android/cpp-adapter.cpp b/packages/polygen/android/cpp-adapter.cpp deleted file mode 100644 index ae7a8961..00000000 --- a/packages/polygen/android/cpp-adapter.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include -#include "react-native-wasm.h" - -extern "C" -JNIEXPORT jdouble JNICALL -Java_com_wasm_WasmModule_nativeMultiply(JNIEnv *env, jclass type, jdouble a, jdouble b) { - return wasm::multiply(a, b); -} diff --git a/packages/polygen/android/gradle.properties b/packages/polygen/android/gradle.properties index fd3ac6da..4058993b 100644 --- a/packages/polygen/android/gradle.properties +++ b/packages/polygen/android/gradle.properties @@ -1,5 +1,5 @@ -Wasm_kotlinVersion=1.7.0 -Wasm_minSdkVersion=21 -Wasm_targetSdkVersion=31 -Wasm_compileSdkVersion=31 -Wasm_ndkversion=21.4.7075529 +Polygen_kotlinVersion=1.7.0 +Polygen_minSdkVersion=21 +Polygen_targetSdkVersion=31 +Polygen_compileSdkVersion=31 +Polygen_ndkversion=21.4.7075529 diff --git a/packages/polygen/android/src/main/AndroidManifest.xml b/packages/polygen/android/src/main/AndroidManifest.xml index 5f6380e4..328cba90 100644 --- a/packages/polygen/android/src/main/AndroidManifest.xml +++ b/packages/polygen/android/src/main/AndroidManifest.xml @@ -1,3 +1,3 @@ + package="com.callstack.polygen"> diff --git a/packages/polygen/android/src/main/java/com/callstack/polygen/PolygenModule.kt b/packages/polygen/android/src/main/java/com/callstack/polygen/PolygenModule.kt new file mode 100644 index 00000000..456980c6 --- /dev/null +++ b/packages/polygen/android/src/main/java/com/callstack/polygen/PolygenModule.kt @@ -0,0 +1,88 @@ +package com.callstack.polygen + +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.bridge.ReadableMap +import com.facebook.react.bridge.WritableMap +import com.facebook.react.bridge.WritableNativeMap +import com.facebook.react.module.annotations.ReactModule + +@ReactModule(name = PolygenModule.NAME) +class PolygenModule internal constructor(context: ReactApplicationContext) : + PolygenSpec(context) { + + override fun getName(): String { + return NAME + } + + override fun copyNativeHandle(holder: ReadableMap, from: ReadableMap): Boolean { + throw UnsupportedOperationException("Polygen Android implementation is not yet complete. See https://github.com/callstackincubator/polygen/issues/132") + } + + override fun loadModule(holder: ReadableMap, moduleData: ReadableMap): WritableMap { + throw UnsupportedOperationException("Polygen Android implementation is not yet complete. See https://github.com/callstackincubator/polygen/issues/132") + } + + override fun unloadModule(module: ReadableMap) { + throw UnsupportedOperationException("Polygen Android implementation is not yet complete. See https://github.com/callstackincubator/polygen/issues/132") + } + + override fun getModuleMetadata(module: ReadableMap): WritableMap { + throw UnsupportedOperationException("Polygen Android implementation is not yet complete. See https://github.com/callstackincubator/polygen/issues/132") + } + + override fun createModuleInstance(holder: ReadableMap, mod: ReadableMap, importObject: ReadableMap) { + throw UnsupportedOperationException("Polygen Android implementation is not yet complete. See https://github.com/callstackincubator/polygen/issues/132") + } + + override fun destroyModuleInstance(instance: ReadableMap) { + throw UnsupportedOperationException("Polygen Android implementation is not yet complete. See https://github.com/callstackincubator/polygen/issues/132") + } + + override fun createMemory(holder: ReadableMap, initial: Double, maximum: Double?) { + throw UnsupportedOperationException("Polygen Android implementation is not yet complete. See https://github.com/callstackincubator/polygen/issues/132") + } + + override fun getMemoryBuffer(instance: ReadableMap): WritableMap { + throw UnsupportedOperationException("Polygen Android implementation is not yet complete. See https://github.com/callstackincubator/polygen/issues/132") + } + + override fun growMemory(instance: ReadableMap, delta: Double) { + throw UnsupportedOperationException("Polygen Android implementation is not yet complete. See https://github.com/callstackincubator/polygen/issues/132") + } + + override fun createGlobal(holder: ReadableMap, descriptor: ReadableMap, initialValue: Double) { + throw UnsupportedOperationException("Polygen Android implementation is not yet complete. See https://github.com/callstackincubator/polygen/issues/132") + } + + override fun getGlobalValue(instance: ReadableMap): Double { + throw UnsupportedOperationException("Polygen Android implementation is not yet complete. See https://github.com/callstackincubator/polygen/issues/132") + } + + override fun setGlobalValue(instance: ReadableMap, newValue: Double) { + throw UnsupportedOperationException("Polygen Android implementation is not yet complete. See https://github.com/callstackincubator/polygen/issues/132") + } + + override fun createTable(holder: ReadableMap, descriptor: ReadableMap, initial: ReadableMap?) { + throw UnsupportedOperationException("Polygen Android implementation is not yet complete. See https://github.com/callstackincubator/polygen/issues/132") + } + + override fun growTable(instance: ReadableMap, delta: Double) { + throw UnsupportedOperationException("Polygen Android implementation is not yet complete. See https://github.com/callstackincubator/polygen/issues/132") + } + + override fun getTableElement(instance: ReadableMap, index: Double): WritableMap { + throw UnsupportedOperationException("Polygen Android implementation is not yet complete. See https://github.com/callstackincubator/polygen/issues/132") + } + + override fun setTableElement(instance: ReadableMap, index: Double, value: ReadableMap) { + throw UnsupportedOperationException("Polygen Android implementation is not yet complete. See https://github.com/callstackincubator/polygen/issues/132") + } + + override fun getTableSize(instance: ReadableMap): Double { + throw UnsupportedOperationException("Polygen Android implementation is not yet complete. See https://github.com/callstackincubator/polygen/issues/132") + } + + companion object { + const val NAME = "Polygen" + } +} diff --git a/packages/polygen/android/src/main/java/com/callstack/polygen/PolygenPackage.kt b/packages/polygen/android/src/main/java/com/callstack/polygen/PolygenPackage.kt new file mode 100644 index 00000000..19d91e8c --- /dev/null +++ b/packages/polygen/android/src/main/java/com/callstack/polygen/PolygenPackage.kt @@ -0,0 +1,50 @@ +package com.callstack.polygen + +import com.facebook.react.TurboReactPackage +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.bridge.NativeModule +import com.facebook.react.module.model.ReactModuleInfoProvider +import com.facebook.react.module.model.ReactModuleInfo +import com.facebook.soloader.SoLoader + +class PolygenPackage : TurboReactPackage() { + companion object { + init { + // Only load native library when new architecture is enabled + // Old architecture uses Java/Kotlin stub and no native library is built + if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { + SoLoader.loadLibrary("polygen") + } + } + } + override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? { + // For new architecture with C++ TurboModule, return null to let the system + // use the C++ module registered via JNI_OnLoad in OnLoad.cpp + if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { + return null + } + // Old architecture fallback (throws UnsupportedOperationException) + return if (name == PolygenModule.NAME) { + PolygenModule(reactContext) + } else { + null + } + } + + override fun getReactModuleInfoProvider(): ReactModuleInfoProvider { + return ReactModuleInfoProvider { + val moduleInfos: MutableMap = HashMap() + val isNewArch: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED + moduleInfos[PolygenModule.NAME] = ReactModuleInfo( + PolygenModule.NAME, + PolygenModule.NAME, + false, // canOverrideExistingModule + false, // needsEagerInit + false, // hasConstants + isNewArch, // isCxxModule - only true for new arch C++ TurboModule + isNewArch // isTurboModule + ) + moduleInfos + } + } +} diff --git a/packages/polygen/android/src/main/java/com/wasm/WasmModule.kt b/packages/polygen/android/src/main/java/com/wasm/WasmModule.kt deleted file mode 100644 index 614d38e5..00000000 --- a/packages/polygen/android/src/main/java/com/wasm/WasmModule.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.wasm - -import com.facebook.react.bridge.ReactApplicationContext -import com.facebook.react.bridge.ReactMethod -import com.facebook.react.bridge.Promise - -class WasmModule internal constructor(context: ReactApplicationContext) : - WasmSpec(context) { - - override fun getName(): String { - return NAME - } - - // Example method - // See https://reactnative.dev/docs/native-modules-android - @ReactMethod - override fun multiply(a: Double, b: Double, promise: Promise) { - promise.resolve(a * b) - } - - companion object { - const val NAME = "Wasm" - } -} diff --git a/packages/polygen/android/src/main/java/com/wasm/WasmPackage.kt b/packages/polygen/android/src/main/java/com/wasm/WasmPackage.kt deleted file mode 100644 index f758789e..00000000 --- a/packages/polygen/android/src/main/java/com/wasm/WasmPackage.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.wasm - -import com.facebook.react.TurboReactPackage -import com.facebook.react.bridge.ReactApplicationContext -import com.facebook.react.bridge.NativeModule -import com.facebook.react.module.model.ReactModuleInfoProvider -import com.facebook.react.module.model.ReactModuleInfo -import java.util.HashMap - -class WasmPackage : TurboReactPackage() { - override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? { - return if (name == WasmModule.NAME) { - WasmModule(reactContext) - } else { - null - } - } - - override fun getReactModuleInfoProvider(): ReactModuleInfoProvider { - return ReactModuleInfoProvider { - val moduleInfos: MutableMap = HashMap() - val isTurboModule: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED - moduleInfos[WasmModule.NAME] = ReactModuleInfo( - WasmModule.NAME, - WasmModule.NAME, - false, // canOverrideExistingModule - false, // needsEagerInit - true, // hasConstants - false, // isCxxModule - isTurboModule // isTurboModule - ) - moduleInfos - } - } -} diff --git a/packages/polygen/android/src/main/jni/ModuleBagStub.cpp b/packages/polygen/android/src/main/jni/ModuleBagStub.cpp new file mode 100644 index 00000000..2d61a85e --- /dev/null +++ b/packages/polygen/android/src/main/jni/ModuleBagStub.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) callstack.io. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +/** + * Stub implementation of getModuleBag for runtime-only usage. + * + * When using Polygen with AOT-compiled WASM modules, the bundler + * will generate this function with the actual module registry. + * For runtime-only usage (no pre-compiled modules), we provide + * an empty registry. + */ + +#include + +namespace callstack::polygen::generated { + +const ModuleBag& getModuleBag() { + static ModuleBag emptyBag{}; + return emptyBag; +} + +} // namespace callstack::polygen::generated diff --git a/packages/polygen/android/src/main/jni/OnLoad.cpp b/packages/polygen/android/src/main/jni/OnLoad.cpp new file mode 100644 index 00000000..58749839 --- /dev/null +++ b/packages/polygen/android/src/main/jni/OnLoad.cpp @@ -0,0 +1,21 @@ +/* + * Copyright (c) callstack.io. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include +#include +#include + +JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) { + return facebook::jni::initialize(vm, [] { + facebook::react::registerCxxModuleToGlobalModuleMap( + std::string(facebook::react::ReactNativePolygen::kModuleName), + [](std::shared_ptr jsInvoker) { + return std::make_shared(jsInvoker); + } + ); + }); +} diff --git a/packages/polygen/android/src/newarch/PolygenSpec.kt b/packages/polygen/android/src/newarch/PolygenSpec.kt new file mode 100644 index 00000000..ab51a7eb --- /dev/null +++ b/packages/polygen/android/src/newarch/PolygenSpec.kt @@ -0,0 +1,6 @@ +package com.callstack.polygen + +import com.facebook.react.bridge.ReactApplicationContext + +abstract class PolygenSpec internal constructor(context: ReactApplicationContext) : + NativePolygenSpec(context) diff --git a/packages/polygen/android/src/newarch/WasmSpec.kt b/packages/polygen/android/src/newarch/WasmSpec.kt deleted file mode 100644 index 674c1d1e..00000000 --- a/packages/polygen/android/src/newarch/WasmSpec.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.wasm - -import com.facebook.react.bridge.ReactApplicationContext - -abstract class WasmSpec internal constructor(context: ReactApplicationContext) : - NativeWasmSpec(context) { -} diff --git a/packages/polygen/android/src/oldarch/PolygenSpec.kt b/packages/polygen/android/src/oldarch/PolygenSpec.kt new file mode 100644 index 00000000..1253143f --- /dev/null +++ b/packages/polygen/android/src/oldarch/PolygenSpec.kt @@ -0,0 +1,62 @@ +package com.callstack.polygen + +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.bridge.ReactContextBaseJavaModule +import com.facebook.react.bridge.ReactMethod +import com.facebook.react.bridge.ReadableMap +import com.facebook.react.bridge.WritableMap + +abstract class PolygenSpec internal constructor(context: ReactApplicationContext) : + ReactContextBaseJavaModule(context) { + + @ReactMethod(isBlockingSynchronousMethod = true) + abstract fun copyNativeHandle(holder: ReadableMap, from: ReadableMap): Boolean + + @ReactMethod(isBlockingSynchronousMethod = true) + abstract fun loadModule(holder: ReadableMap, moduleData: ReadableMap): WritableMap + + @ReactMethod + abstract fun unloadModule(module: ReadableMap) + + @ReactMethod(isBlockingSynchronousMethod = true) + abstract fun getModuleMetadata(module: ReadableMap): WritableMap + + @ReactMethod + abstract fun createModuleInstance(holder: ReadableMap, mod: ReadableMap, importObject: ReadableMap) + + @ReactMethod + abstract fun destroyModuleInstance(instance: ReadableMap) + + @ReactMethod + abstract fun createMemory(holder: ReadableMap, initial: Double, maximum: Double?) + + @ReactMethod(isBlockingSynchronousMethod = true) + abstract fun getMemoryBuffer(instance: ReadableMap): WritableMap + + @ReactMethod + abstract fun growMemory(instance: ReadableMap, delta: Double) + + @ReactMethod + abstract fun createGlobal(holder: ReadableMap, descriptor: ReadableMap, initialValue: Double) + + @ReactMethod(isBlockingSynchronousMethod = true) + abstract fun getGlobalValue(instance: ReadableMap): Double + + @ReactMethod + abstract fun setGlobalValue(instance: ReadableMap, newValue: Double) + + @ReactMethod + abstract fun createTable(holder: ReadableMap, descriptor: ReadableMap, initial: ReadableMap?) + + @ReactMethod + abstract fun growTable(instance: ReadableMap, delta: Double) + + @ReactMethod(isBlockingSynchronousMethod = true) + abstract fun getTableElement(instance: ReadableMap, index: Double): WritableMap + + @ReactMethod + abstract fun setTableElement(instance: ReadableMap, index: Double, value: ReadableMap) + + @ReactMethod(isBlockingSynchronousMethod = true) + abstract fun getTableSize(instance: ReadableMap): Double +} diff --git a/packages/polygen/android/src/oldarch/WasmSpec.kt b/packages/polygen/android/src/oldarch/WasmSpec.kt deleted file mode 100644 index 3af0bd3b..00000000 --- a/packages/polygen/android/src/oldarch/WasmSpec.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.wasm - -import com.facebook.react.bridge.ReactApplicationContext -import com.facebook.react.bridge.ReactContextBaseJavaModule -import com.facebook.react.bridge.Promise - -abstract class WasmSpec internal constructor(context: ReactApplicationContext) : - ReactContextBaseJavaModule(context) { - - abstract fun multiply(a: Double, b: Double, promise: Promise) -} diff --git a/packages/polygen/cpp/ReactNativePolygen/WebAssembly/Memory.h b/packages/polygen/cpp/ReactNativePolygen/WebAssembly/Memory.h index 328c1752..59bf2b43 100644 --- a/packages/polygen/cpp/ReactNativePolygen/WebAssembly/Memory.h +++ b/packages/polygen/cpp/ReactNativePolygen/WebAssembly/Memory.h @@ -15,9 +15,9 @@ class Memory: public facebook::jsi::NativeState, public facebook::jsi::MutableBu public: explicit Memory(wasm_rt_memory_t* memory): memory_(memory) {} - Memory(uint64_t initial, uint64_t maximum, bool is64 = false) { + Memory(uint64_t initial, uint64_t maximum, bool is64 = false, uint32_t pageSize = WASM_DEFAULT_PAGE_SIZE) { this->memory_ = &this->ownedMemory_; - wasm_rt_allocate_memory(this->memory_, initial, maximum, is64); + wasm_rt_allocate_memory(this->memory_, initial, maximum, is64, pageSize); } virtual ~Memory() { diff --git a/packages/polygen/cpp/ReactNativePolygen/w2c.h b/packages/polygen/cpp/ReactNativePolygen/w2c.h index 345ac638..2fbd83ef 100644 --- a/packages/polygen/cpp/ReactNativePolygen/w2c.h +++ b/packages/polygen/cpp/ReactNativePolygen/w2c.h @@ -8,6 +8,7 @@ #include #include +#include namespace callstack::polygen { diff --git a/packages/polygen/cpp/wasm-rt/wasm-rt-exceptions.c b/packages/polygen/cpp/wasm-rt/wasm-rt-exceptions.c index 27d6ea08..73e97201 100644 --- a/packages/polygen/cpp/wasm-rt/wasm-rt-exceptions.c +++ b/packages/polygen/cpp/wasm-rt/wasm-rt-exceptions.c @@ -31,38 +31,43 @@ static WASM_RT_THREAD_LOCAL wasm_rt_jmp_buf* g_unwind_target; void wasm_rt_load_exception(const wasm_rt_tag_t tag, uint32_t size, const void* values) { - if (size > MAX_EXCEPTION_SIZE) { - wasm_rt_trap(WASM_RT_TRAP_EXHAUSTION); - } + if (size > MAX_EXCEPTION_SIZE) { + wasm_rt_trap(WASM_RT_TRAP_EXHAUSTION); + } - g_active_exception_tag = tag; - g_active_exception_size = size; + g_active_exception_tag = tag; + g_active_exception_size = size; - if (size) { - memcpy(g_active_exception, values, size); - } + if (size) { + memcpy(g_active_exception, values, size); + } } WASM_RT_NO_RETURN void wasm_rt_throw(void) { - WASM_RT_LONGJMP(*g_unwind_target, WASM_RT_TRAP_UNCAUGHT_EXCEPTION); + WASM_RT_LONGJMP(*g_unwind_target, WASM_RT_TRAP_UNCAUGHT_EXCEPTION); } WASM_RT_UNWIND_TARGET* wasm_rt_get_unwind_target(void) { - return g_unwind_target; + return g_unwind_target; } void wasm_rt_set_unwind_target(WASM_RT_UNWIND_TARGET* target) { - g_unwind_target = target; + g_unwind_target = target; } wasm_rt_tag_t wasm_rt_exception_tag(void) { - return g_active_exception_tag; + return g_active_exception_tag; } uint32_t wasm_rt_exception_size(void) { - return g_active_exception_size; + return g_active_exception_size; } void* wasm_rt_exception(void) { - return g_active_exception; -} \ No newline at end of file + return g_active_exception; +} + +// Include table operations for exnref +#define WASM_RT_TABLE_OPS_EXNREF +#include "wasm-rt-impl-tableops.inc" +#undef WASM_RT_TABLE_OPS_EXNREF diff --git a/packages/polygen/cpp/wasm-rt/wasm-rt-exceptions.h b/packages/polygen/cpp/wasm-rt/wasm-rt-exceptions.h index 25f98cde..9e95c196 100644 --- a/packages/polygen/cpp/wasm-rt/wasm-rt-exceptions.h +++ b/packages/polygen/cpp/wasm-rt/wasm-rt-exceptions.h @@ -70,8 +70,71 @@ uint32_t wasm_rt_exception_size(void); */ void* wasm_rt_exception(void); +/** + * The maximum size of an exception. + */ +#define WASM_EXN_MAX_SIZE 256 + +/** + * An exception instance (the runtime representation of a function). + * These can be stored in tables of type exnref, or used as values. + */ +typedef struct { + /** The exceptions's tag. */ + wasm_rt_tag_t tag; + /** The size of the exception. */ + uint32_t size; + /** + * The actual contents of the exception are stored inline. + */ + char data[WASM_EXN_MAX_SIZE]; +} wasm_rt_exnref_t; + +/** Default (null) value of an exnref */ +#define wasm_rt_exnref_null_value ((wasm_rt_exnref_t){NULL, 0, {0}}) + +/** A Table of type exnref. */ +typedef struct { + /** The table element data, with an element count of `size`. */ + wasm_rt_exnref_t* data; + /** + * The maximum element count of this Table object. If there is no maximum, + * `max_size` is 0xffffffffu (i.e. UINT32_MAX). + */ + uint32_t max_size; + /** The current element count of the table. */ + uint32_t size; +} wasm_rt_exnref_table_t; + +/** + * Initialize an exnref Table object with an element count of `elements` and a + * maximum size of `max_elements`. + * + * ``` + * wasm_rt_exnref_table_t my_table; + * // 5 elements and a maximum of 10 elements. + * wasm_rt_allocate_exnref_table(&my_table, 5, 10); + * ``` + */ +void wasm_rt_allocate_exnref_table(wasm_rt_exnref_table_t*, + uint32_t elements, + uint32_t max_elements); + +/** Free an exnref Table object. */ +void wasm_rt_free_exnref_table(wasm_rt_exnref_table_t*); + +/** + * Grow a Table object by `delta` elements (giving the new elements the value + * `init`), and return the previous element count. If this new element count is + * greater than the maximum element count, the grow fails and 0xffffffffu + * (UINT32_MAX) is returned instead. + */ +uint32_t wasm_rt_grow_exnref_table(wasm_rt_exnref_table_t*, + uint32_t delta, + wasm_rt_exnref_t init); + #ifdef __cplusplus } #endif -#endif \ No newline at end of file +#endif diff --git a/packages/polygen/cpp/wasm-rt/wasm-rt-impl-tableops.inc b/packages/polygen/cpp/wasm-rt/wasm-rt-impl-tableops.inc index 6031a2b7..76193028 100644 --- a/packages/polygen/cpp/wasm-rt/wasm-rt-impl-tableops.inc +++ b/packages/polygen/cpp/wasm-rt/wasm-rt-impl-tableops.inc @@ -18,23 +18,32 @@ // funcref or externref. For this, the file must be included after defining // either WASM_RT_TABLE_OPS_FUNCREF or WASM_RT_TABLE_OPS_EXTERNREF -#if defined(WASM_RT_TABLE_OPS_FUNCREF) && defined(WASM_RT_TABLE_OPS_EXTERNREF) +#if defined(WASM_RT_TABLE_OPS_FUNCREF) + \ + defined(WASM_RT_TABLE_OPS_EXTERNREF) + \ + defined(WASM_RT_TABLE_OPS_EXNREF) > \ + 1 #error \ - "Expected only one of { WASM_RT_TABLE_OPS_FUNCREF, WASM_RT_TABLE_OPS_EXTERNREF } to be defined" -#elif !defined(WASM_RT_TABLE_OPS_FUNCREF) && \ - !defined(WASM_RT_TABLE_OPS_EXTERNREF) + "Expected only one of { WASM_RT_TABLE_OPS_FUNCREF, WASM_RT_TABLE_OPS_EXTERNREF, WASM_RT_TABLE_OPS_EXNREF } to be defined" +#elif defined(WASM_RT_TABLE_OPS_FUNCREF) + \ + defined(WASM_RT_TABLE_OPS_EXTERNREF) + \ + defined(WASM_RT_TABLE_OPS_EXNREF) < \ + 1 #error \ - "Expected one of { WASM_RT_TABLE_OPS_FUNCREF, WASM_RT_TABLE_OPS_EXTERNREF } to be defined" + "Expected one of { WASM_RT_TABLE_OPS_FUNCREF, WASM_RT_TABLE_OPS_EXTERNREF, WASM_RT_TABLE_OPS_EXNREF } to be defined" #endif -#ifdef WASM_RT_TABLE_OPS_FUNCREF +#if defined(WASM_RT_TABLE_OPS_FUNCREF) #define WASM_RT_TABLE_TYPE wasm_rt_funcref_table_t #define WASM_RT_TABLE_ELEMENT_TYPE wasm_rt_funcref_t #define WASM_RT_TABLE_APINAME(name) name##_funcref_table -#else +#elif defined(WASM_RT_TABLE_OPS_EXTERNREF) #define WASM_RT_TABLE_TYPE wasm_rt_externref_table_t #define WASM_RT_TABLE_ELEMENT_TYPE wasm_rt_externref_t #define WASM_RT_TABLE_APINAME(name) name##_externref_table +#elif defined(WASM_RT_TABLE_OPS_EXNREF) +#define WASM_RT_TABLE_TYPE wasm_rt_exnref_table_t +#define WASM_RT_TABLE_ELEMENT_TYPE wasm_rt_exnref_t +#define WASM_RT_TABLE_APINAME(name) name##_exnref_table #endif void WASM_RT_TABLE_APINAME(wasm_rt_allocate)(WASM_RT_TABLE_TYPE* table, @@ -75,4 +84,4 @@ uint32_t WASM_RT_TABLE_APINAME(wasm_rt_grow)(WASM_RT_TABLE_TYPE* table, #undef WASM_RT_TABLE_APINAME #undef WASM_RT_TABLE_ELEMENT_TYPE -#undef WASM_RT_TABLE_TYPE \ No newline at end of file +#undef WASM_RT_TABLE_TYPE diff --git a/packages/polygen/cpp/wasm-rt/wasm-rt-impl.c b/packages/polygen/cpp/wasm-rt/wasm-rt-impl.c index 91523d79..0cf0723a 100644 --- a/packages/polygen/cpp/wasm-rt/wasm-rt-impl.c +++ b/packages/polygen/cpp/wasm-rt/wasm-rt-impl.c @@ -42,11 +42,6 @@ #define DEBUG_PRINTF(...) #endif -/** - * Polygen customisation - */ -#define WASM_RT_TRAP_HANDLER polygen_trap_handler - #if WASM_RT_INSTALL_SIGNAL_HANDLER static bool g_signal_handler_installed = false; #ifdef _WIN32 @@ -54,17 +49,22 @@ static void* g_sig_handler_handle = 0; #endif #endif -#if WASM_RT_USE_SEGUE || WASM_RT_ALLOW_SEGUE -// Currently Segue is used only for linux +#if WASM_RT_USE_SEGUE +bool wasm_rt_fsgsbase_inst_supported = false; +#ifdef __linux__ #include #ifdef __GLIBC__ #include #endif -bool wasm_rt_fsgsbase_inst_supported = false; - #include // For ARCH_SET_GS #include // For SYS_arch_prctl #include // For syscall +#ifndef HWCAP2_FSGSBASE +#define HWCAP2_FSGSBASE (1 << 1) +#endif +#elif defined(__FreeBSD__) +#include // For amd64_set_gsbase etc. +#endif #endif #if WASM_RT_SEGUE_FREE_SEGMENT @@ -78,23 +78,25 @@ WASM_RT_THREAD_LOCAL uint32_t wasm_rt_saved_call_stack_depth; static WASM_RT_THREAD_LOCAL void* g_alt_stack = NULL; #endif +#ifndef WASM_RT_TRAP_HANDLER WASM_RT_THREAD_LOCAL wasm_rt_jmp_buf g_wasm_rt_jmp_buf; +#endif #ifdef WASM_RT_TRAP_HANDLER extern void WASM_RT_TRAP_HANDLER(wasm_rt_trap_t code); #endif void wasm_rt_trap(wasm_rt_trap_t code) { - assert(code != WASM_RT_TRAP_NONE); + assert(code != WASM_RT_TRAP_NONE); #if WASM_RT_STACK_DEPTH_COUNT - wasm_rt_call_stack_depth = wasm_rt_saved_call_stack_depth; + wasm_rt_call_stack_depth = wasm_rt_saved_call_stack_depth; #endif #ifdef WASM_RT_TRAP_HANDLER - WASM_RT_TRAP_HANDLER(code); + WASM_RT_TRAP_HANDLER(code); wasm_rt_unreachable(); #else - WASM_RT_LONGJMP(g_wasm_rt_jmp_buf, code); + WASM_RT_LONGJMP(g_wasm_rt_jmp_buf, code); #endif } @@ -126,179 +128,228 @@ static void os_cleanup_signal_handler(void) { #if WASM_RT_INSTALL_SIGNAL_HANDLER static void os_signal_handler(int sig, siginfo_t* si, void* unused) { - if (si->si_code == SEGV_ACCERR) { - wasm_rt_trap(WASM_RT_TRAP_OOB); - } else { - wasm_rt_trap(WASM_RT_TRAP_EXHAUSTION); - } + if (si->si_code == SEGV_ACCERR) { + wasm_rt_trap(WASM_RT_TRAP_OOB); + } else { + wasm_rt_trap(WASM_RT_TRAP_EXHAUSTION); + } } static void os_install_signal_handler(void) { - struct sigaction sa; - memset(&sa, '\0', sizeof(sa)); - sa.sa_flags = SA_SIGINFO; + struct sigaction sa; + memset(&sa, '\0', sizeof(sa)); + sa.sa_flags = SA_SIGINFO; #if WASM_RT_STACK_EXHAUSTION_HANDLER - sa.sa_flags |= SA_ONSTACK; + sa.sa_flags |= SA_ONSTACK; #endif - sigemptyset(&sa.sa_mask); - sa.sa_sigaction = os_signal_handler; + sigemptyset(&sa.sa_mask); + sa.sa_sigaction = os_signal_handler; - /* Install SIGSEGV and SIGBUS handlers, since macOS seems to use SIGBUS. */ - if (sigaction(SIGSEGV, &sa, NULL) != 0 || sigaction(SIGBUS, &sa, NULL) != 0) { - perror("sigaction failed"); - abort(); - } + /* Install SIGSEGV and SIGBUS handlers, since macOS seems to use SIGBUS. */ + if (sigaction(SIGSEGV, &sa, NULL) != 0 || sigaction(SIGBUS, &sa, NULL) != 0) { + perror("sigaction failed"); + abort(); + } } static void os_cleanup_signal_handler(void) { - /* Undo what was done in os_install_signal_handler */ - struct sigaction sa; - memset(&sa, '\0', sizeof(sa)); - sa.sa_handler = SIG_DFL; - if (sigaction(SIGSEGV, &sa, NULL) != 0 || sigaction(SIGBUS, &sa, NULL)) { - perror("sigaction failed"); - abort(); - } + /* Undo what was done in os_install_signal_handler */ + struct sigaction sa; + memset(&sa, '\0', sizeof(sa)); + sa.sa_handler = SIG_DFL; + if (sigaction(SIGSEGV, &sa, NULL) != 0 || sigaction(SIGBUS, &sa, NULL)) { + perror("sigaction failed"); + abort(); + } } #endif #if WASM_RT_STACK_EXHAUSTION_HANDLER static bool os_has_altstack_installed() { - /* check for altstack already in place */ - stack_t ss; - if (sigaltstack(NULL, &ss) != 0) { - perror("sigaltstack failed"); - abort(); - } + /* check for altstack already in place */ + stack_t ss; + if (sigaltstack(NULL, &ss) != 0) { + perror("os_has_altstack_installed: sigaltstack failed"); + abort(); + } - return !(ss.ss_flags & SS_DISABLE); + return !(ss.ss_flags & SS_DISABLE); } /* These routines set up an altstack to handle SIGSEGV from stack overflow. */ static void os_allocate_and_install_altstack(void) { - /* verify altstack not already allocated */ - assert(!g_alt_stack && - "wasm-rt error: tried to re-allocate thread-local alternate stack"); - - /* We could check and warn if an altstack is already installed, but some - * sanitizers install their own altstack, so this warning would fire - * spuriously and break the test outputs. */ - - /* allocate altstack */ - g_alt_stack = malloc(SIGSTKSZ); - if (g_alt_stack == NULL) { - perror("malloc failed"); - abort(); - } + /* verify altstack not already allocated */ + assert(!g_alt_stack && + "wasm-rt error: tried to re-allocate thread-local alternate stack"); + + /* We could check and warn if an altstack is already installed, but some + * sanitizers install their own altstack, so this warning would fire + * spuriously and break the test outputs. */ + + /* allocate altstack */ + g_alt_stack = malloc(SIGSTKSZ); + if (g_alt_stack == NULL) { + perror("malloc failed"); + abort(); + } - /* install altstack */ - stack_t ss; - ss.ss_sp = g_alt_stack; - ss.ss_flags = 0; - ss.ss_size = SIGSTKSZ; - if (sigaltstack(&ss, NULL) != 0) { - perror("sigaltstack failed"); - abort(); - } + /* install altstack */ + stack_t ss; + ss.ss_sp = g_alt_stack; + ss.ss_flags = 0; + ss.ss_size = SIGSTKSZ; + if (sigaltstack(&ss, NULL) != 0) { + perror("os_allocate_and_install_altstack: sigaltstack failed"); + abort(); + } } static void os_disable_and_deallocate_altstack(void) { - /* in debug build, verify altstack allocated */ - assert(g_alt_stack && - "wasm-rt error: thread-local alternate stack not allocated"); - - /* verify altstack was still in place */ - stack_t ss; - if (sigaltstack(NULL, &ss) != 0) { - perror("sigaltstack failed"); - abort(); - } + /* in debug build, verify altstack allocated */ + assert(g_alt_stack && + "wasm-rt error: thread-local alternate stack not allocated"); + + /* verify altstack was still in place */ + stack_t ss; + if (sigaltstack(NULL, &ss) != 0) { + perror("os_disable_and_deallocate_altstack: sigaltstack failed"); + abort(); + } - if ((!g_alt_stack) || (ss.ss_flags & SS_DISABLE) || - (ss.ss_sp != g_alt_stack) || (ss.ss_size != SIGSTKSZ)) { - DEBUG_PRINTF( - "wasm-rt warning: alternate stack was modified unexpectedly\n"); - return; - } + if ((!g_alt_stack) || (ss.ss_flags & SS_DISABLE) || + (ss.ss_sp != g_alt_stack) || (ss.ss_size != SIGSTKSZ)) { + DEBUG_PRINTF( + "wasm-rt warning: alternate stack was modified unexpectedly\n"); + return; + } - /* disable and free */ - ss.ss_flags = SS_DISABLE; - if (sigaltstack(&ss, NULL) != 0) { - perror("sigaltstack failed"); - abort(); - } - assert(!os_has_altstack_installed()); - free(g_alt_stack); - g_alt_stack = NULL; + assert(!(ss.ss_flags & SS_ONSTACK) && + "attempt to deallocate altstack while in use"); + + /* disable and free */ + ss.ss_flags = SS_DISABLE; + if (sigaltstack(&ss, NULL) != 0) { + perror("sigaltstack disable failed"); + abort(); + } + assert(!os_has_altstack_installed()); + free(g_alt_stack); + g_alt_stack = NULL; } #endif #endif +#if WASM_RT_USE_SEGUE && defined(__FreeBSD__) +static void call_cpuid(uint64_t* rax, + uint64_t* rbx, + uint64_t* rcx, + uint64_t* rdx) { + __asm__ volatile( + "cpuid" + : "=a"(*rax), "=b"(*rbx), "=c"(*rcx), "=d"(*rdx) // output operands + : "a"(*rax), "c"(*rcx) // input operands + ); +} +#endif + void wasm_rt_init(void) { - wasm_rt_init_thread(); + wasm_rt_init_thread(); #if WASM_RT_INSTALL_SIGNAL_HANDLER - if (!g_signal_handler_installed) { - g_signal_handler_installed = true; - os_install_signal_handler(); - } + if (!g_signal_handler_installed) { + g_signal_handler_installed = true; + os_install_signal_handler(); + } #endif -#if WASM_RT_USE_SEGUE || WASM_RT_ALLOW_SEGUE - #if defined(__GLIBC__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 18 +#if WASM_RT_USE_SEGUE +#if defined(__linux__) && defined(__GLIBC__) && __GLIBC__ >= 2 && \ + __GLIBC_MINOR__ >= 18 // Check for support for userspace wrgsbase instructions unsigned long val = getauxval(AT_HWCAP2); - wasm_rt_fsgsbase_inst_supported = val & (1 << 1); + wasm_rt_fsgsbase_inst_supported = val & HWCAP2_FSGSBASE; +#elif defined(__FreeBSD__) + // FreeBSD enables fsgsbase on the newer kernels if the hardware supports it. + // We just need to check if the hardware supports it by querying the correct + // cpuid leaf. + uint64_t rax, rbx, rcx, rdx; + rax = 0; + call_cpuid(&rax, &rbx, &rcx, &rdx); + + if (rax > 7) { + rax = 7; + rcx = 0; + call_cpuid(&rax, &rbx, &rcx, &rdx); + if (rbx & 0x1) { + wasm_rt_fsgsbase_inst_supported = true; + } + } #endif #endif - assert(wasm_rt_is_initialized()); + assert(wasm_rt_is_initialized()); } bool wasm_rt_is_initialized(void) { #if WASM_RT_STACK_EXHAUSTION_HANDLER - if (!os_has_altstack_installed()) { - return false; - } + if (!os_has_altstack_installed()) { + return false; + } #endif -#if WASM_RT_INSTALL_SIGNAL_HANDLER && !WASM_RT_SHARED_WEAK - return g_signal_handler_installed; +#if WASM_RT_INSTALL_SIGNAL_HANDLER + return g_signal_handler_installed; #else - return true; + return true; #endif } void wasm_rt_free(void) { - assert(wasm_rt_is_initialized()); + assert(wasm_rt_is_initialized()); #if WASM_RT_INSTALL_SIGNAL_HANDLER - os_cleanup_signal_handler(); - g_signal_handler_installed = false; + os_cleanup_signal_handler(); + g_signal_handler_installed = false; #endif - wasm_rt_free_thread(); + wasm_rt_free_thread(); } void wasm_rt_init_thread(void) { #if WASM_RT_STACK_EXHAUSTION_HANDLER - os_allocate_and_install_altstack(); + os_allocate_and_install_altstack(); #endif } void wasm_rt_free_thread(void) { #if WASM_RT_STACK_EXHAUSTION_HANDLER - os_disable_and_deallocate_altstack(); + os_disable_and_deallocate_altstack(); #endif } -#if WASM_RT_USE_SEGUE || WASM_RT_ALLOW_SEGUE +#if WASM_RT_USE_SEGUE void wasm_rt_syscall_set_segue_base(void* base) { - if (syscall(SYS_arch_prctl, ARCH_SET_GS, base) != 0) { + int error_code = 0; +#ifdef __linux__ + error_code = syscall(SYS_arch_prctl, ARCH_SET_GS, base); +#elif defined(__FreeBSD__) + error_code = amd64_set_gsbase(base); +#else +#error "Unknown platform" +#endif + if (error_code != 0) { perror("wasm_rt_syscall_set_segue_base error"); abort(); } } void* wasm_rt_syscall_get_segue_base() { void* base; - if (syscall(SYS_arch_prctl, ARCH_GET_GS, &base) != 0) { + int error_code = 0; +#ifdef __linux__ + error_code = syscall(SYS_arch_prctl, ARCH_GET_GS, &base); +#elif defined(__FreeBSD__) + error_code = amd64_get_gsbase(&base); +#else +#error "Unknown platform" +#endif + if (error_code != 0) { perror("wasm_rt_syscall_get_segue_base error"); abort(); } @@ -317,32 +368,34 @@ void* wasm_rt_syscall_get_segue_base() { #undef WASM_RT_TABLE_OPS_EXTERNREF const char* wasm_rt_strerror(wasm_rt_trap_t trap) { - switch (trap) { - case WASM_RT_TRAP_NONE: - return "No error"; - case WASM_RT_TRAP_OOB: + switch (trap) { + case WASM_RT_TRAP_NONE: + return "No error"; + case WASM_RT_TRAP_OOB: #if WASM_RT_MERGED_OOB_AND_EXHAUSTION_TRAPS - return "Out-of-bounds access in linear memory or a table, or call stack " - "exhausted"; + return "Out-of-bounds access in linear memory or a table, or call stack " + "exhausted"; #else - return "Out-of-bounds access in linear memory or a table"; + return "Out-of-bounds access in linear memory or a table"; case WASM_RT_TRAP_EXHAUSTION: return "Call stack exhausted"; #endif - case WASM_RT_TRAP_INT_OVERFLOW: - return "Integer overflow on divide or truncation"; - case WASM_RT_TRAP_DIV_BY_ZERO: - return "Integer divide by zero"; - case WASM_RT_TRAP_INVALID_CONVERSION: - return "Conversion from NaN to integer"; - case WASM_RT_TRAP_UNREACHABLE: - return "Unreachable instruction executed"; - case WASM_RT_TRAP_CALL_INDIRECT: - return "Invalid call_indirect or return_call_indirect"; - case WASM_RT_TRAP_UNCAUGHT_EXCEPTION: - return "Uncaught exception"; - case WASM_RT_TRAP_UNALIGNED: - return "Unaligned atomic memory access"; - } - return "invalid trap code"; + case WASM_RT_TRAP_INT_OVERFLOW: + return "Integer overflow on divide or truncation"; + case WASM_RT_TRAP_DIV_BY_ZERO: + return "Integer divide by zero"; + case WASM_RT_TRAP_INVALID_CONVERSION: + return "Conversion from NaN to integer"; + case WASM_RT_TRAP_UNREACHABLE: + return "Unreachable instruction executed"; + case WASM_RT_TRAP_CALL_INDIRECT: + return "Invalid call_indirect or return_call_indirect"; + case WASM_RT_TRAP_UNCAUGHT_EXCEPTION: + return "Uncaught exception"; + case WASM_RT_TRAP_UNALIGNED: + return "Unaligned atomic memory access"; + case WASM_RT_TRAP_NULL_REF: + return "Null reference"; + } + return "invalid trap code"; } diff --git a/packages/polygen/cpp/wasm-rt/wasm-rt-impl.h b/packages/polygen/cpp/wasm-rt/wasm-rt-impl.h index da70752d..ed46b097 100644 --- a/packages/polygen/cpp/wasm-rt/wasm-rt-impl.h +++ b/packages/polygen/cpp/wasm-rt/wasm-rt-impl.h @@ -14,662 +14,55 @@ * limitations under the License. */ -#ifndef WASM_RT_H_ -#define WASM_RT_H_ +#ifndef WASM_RT_IMPL_H_ +#define WASM_RT_IMPL_H_ -#include -#include -#include -#include -#include +#include "wasm-rt.h" -#ifdef __cplusplus -extern "C" { -#endif - -#ifndef __has_builtin -#define __has_builtin(x) 0 /** Compatibility with non-clang compilers. */ -#endif - -#if __has_builtin(__builtin_expect) -#define UNLIKELY(x) __builtin_expect(!!(x), 0) -#define LIKELY(x) __builtin_expect(!!(x), 1) -#else -#define UNLIKELY(x) (x) -#define LIKELY(x) (x) -#endif - -#if __has_builtin(__builtin_memcpy) -#define wasm_rt_memcpy __builtin_memcpy -#else -#define wasm_rt_memcpy memcpy -#endif - -#if __has_builtin(__builtin_unreachable) -#define wasm_rt_unreachable __builtin_unreachable -#else -#define wasm_rt_unreachable abort -#endif - -#ifdef __STDC_VERSION__ -#if __STDC_VERSION__ >= 201112L -#define WASM_RT_C11_AVAILABLE -#endif -#endif - -/** - * Many devices don't implement the C11 threads.h. We use CriticalSection APIs - * for Windows and pthreads on other platforms where threads are not available. - */ -#ifdef WASM_RT_C11_AVAILABLE - -#if defined(_WIN32) +#ifdef _WIN32 #include -#define WASM_RT_MUTEX CRITICAL_SECTION -#define WASM_RT_USE_CRITICALSECTION 1 -#elif defined(__APPLE__) || defined(__STDC_NO_THREADS__) -#include -#define WASM_RT_MUTEX pthread_mutex_t -#define WASM_RT_USE_PTHREADS 1 -#else -#include -#define WASM_RT_MUTEX mtx_t -#define WASM_RT_USE_C11THREADS 1 -#endif - -#endif - -#ifdef _MSC_VER -#define WASM_RT_THREAD_LOCAL __declspec(thread) -#elif defined(WASM_RT_C11_AVAILABLE) -#define WASM_RT_THREAD_LOCAL _Thread_local -#else -#define WASM_RT_THREAD_LOCAL -#endif - -/** - * If enabled, perform additional sanity checks in the generated wasm2c code and - * wasm2c runtime. This is useful to enable on debug builds. - */ -#ifndef WASM_RT_SANITY_CHECKS -#define WASM_RT_SANITY_CHECKS 0 -#endif - -/** - * Backward compatibility: Convert the previously exposed - * WASM_RT_MEMCHECK_SIGNAL_HANDLER macro to the ALLOCATION and CHECK macros that - * are now used. - */ -#if defined(WASM_RT_MEMCHECK_SIGNAL_HANDLER) - -#if WASM_RT_MEMCHECK_SIGNAL_HANDLER -#define WASM_RT_USE_MMAP 1 -#define WASM_RT_MEMCHECK_GUARD_PAGES 1 -#else -#define WASM_RT_USE_MMAP 0 -#define WASM_RT_MEMCHECK_BOUNDS_CHECK 1 -#endif - -#warning \ - "WASM_RT_MEMCHECK_SIGNAL_HANDLER has been deprecated in favor of WASM_RT_USE_MMAP and WASM_RT_MEMORY_CHECK_* macros" -#endif - -/** - * Specify if we use OR mmap/mprotect (+ Windows equivalents) OR malloc/realloc - * for the Wasm memory allocation and growth. mmap/mprotect guarantees memory - * will grow without being moved, while malloc ensures the virtual memory is - * consumed only as needed, but may relocate the memory to handle memory - * fragmentation. - * - * This defaults to malloc on 32-bit platforms or if memory64 support is needed. - * It defaults to mmap on 64-bit platforms assuming memory64 support is not - * needed (so we can use the guard based range checks below). - */ -#ifndef WASM_RT_USE_MMAP -#if UINTPTR_MAX > 0xffffffff && !SUPPORT_MEMORY64 -#define WASM_RT_USE_MMAP 1 -#else -#define WASM_RT_USE_MMAP 0 -#endif -#endif - -/** - * Set the range checking strategy for Wasm memories. - * - * GUARD_PAGES: memory accesses rely on unmapped pages/guard pages to trap - * out-of-bound accesses. - * - * BOUNDS_CHECK: memory accesses are checked with explicit bounds checks. - * - * This defaults to GUARD_PAGES as this is the fasest option, iff the - * requirements of GUARD_PAGES --- 64-bit platforms, MMAP allocation strategy, - * no 64-bit memories, no big-endian --- are met. This falls back to BOUNDS - * otherwise. - */ - -/** Check if Guard checks are supported */ -#if UINTPTR_MAX > 0xffffffff && WASM_RT_USE_MMAP && !SUPPORT_MEMORY64 && \ - !WABT_BIG_ENDIAN -#define WASM_RT_GUARD_PAGES_SUPPORTED 1 -#else -#define WASM_RT_GUARD_PAGES_SUPPORTED 0 -#endif - -/** Specify defaults for memory checks if unspecified */ -#if !defined(WASM_RT_MEMCHECK_GUARD_PAGES) && \ - !defined(WASM_RT_MEMCHECK_BOUNDS_CHECK) -#if WASM_RT_GUARD_PAGES_SUPPORTED -#define WASM_RT_MEMCHECK_GUARD_PAGES 1 -#else -#define WASM_RT_MEMCHECK_BOUNDS_CHECK 1 -#endif -#endif - -/** Ensure the macros are defined */ -#ifndef WASM_RT_MEMCHECK_GUARD_PAGES -#define WASM_RT_MEMCHECK_GUARD_PAGES 0 -#endif -#ifndef WASM_RT_MEMCHECK_BOUNDS_CHECK -#define WASM_RT_MEMCHECK_BOUNDS_CHECK 0 -#endif - -/** Sanity check the use of guard pages */ -#if WASM_RT_MEMCHECK_GUARD_PAGES && !WASM_RT_GUARD_PAGES_SUPPORTED -#error \ - "WASM_RT_MEMCHECK_GUARD_PAGES not supported on this platform/configuration" -#endif - -#if WASM_RT_MEMCHECK_GUARD_PAGES && WASM_RT_MEMCHECK_BOUNDS_CHECK -#error \ - "Cannot use both WASM_RT_MEMCHECK_GUARD_PAGES and WASM_RT_MEMCHECK_BOUNDS_CHECK" - -#elif !WASM_RT_MEMCHECK_GUARD_PAGES && !WASM_RT_MEMCHECK_BOUNDS_CHECK -#error \ - "Must choose at least one from WASM_RT_MEMCHECK_GUARD_PAGES and WASM_RT_MEMCHECK_BOUNDS_CHECK" -#endif - -/** - * Some configurations above require the Wasm runtime to install a signal - * handler. However, this can be explicitly disallowed by the host using - * WASM_RT_SKIP_SIGNAL_RECOVERY. In this case, when the wasm code encounters an - * OOB access, it may either trap or abort. - */ -#ifndef WASM_RT_SKIP_SIGNAL_RECOVERY -#define WASM_RT_SKIP_SIGNAL_RECOVERY 0 -#endif - -#if WASM_RT_MEMCHECK_GUARD_PAGES && !WASM_RT_SKIP_SIGNAL_RECOVERY -#define WASM_RT_INSTALL_SIGNAL_HANDLER 1 -#else -#define WASM_RT_INSTALL_SIGNAL_HANDLER 0 -#endif - -#ifndef WASM_RT_SHARED_WEAK -#define WASM_RT_SHARED_WEAK 0 -#endif - -/** - * This macro, if defined to 1 (i.e., allows the "segue" optimization), allows - * Wasm2c to use segment registers to speedup access to the linear heap. Note - * that even if allowed in this way, the segment registers would only be used if - * Wasm2c output is compiled for a suitable architecture and OS and the produces - * C file is compiled by supported compilers. The extact restrictions are listed - * in detail in src/template/wasm2c.declarations.c - */ -#ifndef WASM_RT_ALLOW_SEGUE -#define WASM_RT_ALLOW_SEGUE 0 -#endif - -/** - * The segue optimization restores x86 segments to their old values when exiting - * wasm2c code. If WASM_RT_SEGUE_FREE_SEGMENT is defined, wasm2c assumes it has - * exclusive use of the segment and optimizes performance in two ways. First, it - * does not restore the "old" value of the segment during exits. Second, wasm2c - * only sets the segment register if the value has changed since the last time - * it was set. - */ -#ifndef WASM_RT_SEGUE_FREE_SEGMENT -#define WASM_RT_SEGUE_FREE_SEGMENT 0 -#endif - -/** - * This macro, if defined, allows the embedder to disable all stack exhaustion - * checks. This a non conformant configuration, i.e., this does not respect - * Wasm's specification, and may compromise security. Use with caution. - */ -#ifndef WASM_RT_NONCONFORMING_UNCHECKED_STACK_EXHAUSTION -#define WASM_RT_NONCONFORMING_UNCHECKED_STACK_EXHAUSTION 0 -#endif - -/** - * We need to detect and trap stack overflows. If we use a signal handler on - * POSIX systems, this can detect call stack overflows. On windows, or platforms - * without a signal handler, we use stack depth counting. - */ -#if !defined(WASM_RT_STACK_DEPTH_COUNT) && \ - !defined(WASM_RT_STACK_EXHAUSTION_HANDLER) && \ - !WASM_RT_NONCONFORMING_UNCHECKED_STACK_EXHAUSTION - -#if WASM_RT_INSTALL_SIGNAL_HANDLER && !defined(_WIN32) -#define WASM_RT_STACK_EXHAUSTION_HANDLER 1 -#else -#define WASM_RT_STACK_DEPTH_COUNT 1 -#endif - -#endif - -/** Ensure the stack macros are defined */ -#ifndef WASM_RT_STACK_DEPTH_COUNT -#define WASM_RT_STACK_DEPTH_COUNT 0 -#endif -#ifndef WASM_RT_STACK_EXHAUSTION_HANDLER -#define WASM_RT_STACK_EXHAUSTION_HANDLER 0 -#endif - -#if WASM_RT_NONCONFORMING_UNCHECKED_STACK_EXHAUSTION - -#if (WASM_RT_STACK_EXHAUSTION_HANDLER + WASM_RT_STACK_DEPTH_COUNT) != 0 -#error \ - "Cannot specify WASM_RT_NONCONFORMING_UNCHECKED_STACK_EXHAUSTION along with WASM_RT_STACK_EXHAUSTION_HANDLER or WASM_RT_STACK_DEPTH_COUNT" -#endif - -#else - -#if (WASM_RT_STACK_EXHAUSTION_HANDLER + WASM_RT_STACK_DEPTH_COUNT) > 1 -#error \ - "Cannot specify multiple options from WASM_RT_STACK_EXHAUSTION_HANDLER , WASM_RT_STACK_DEPTH_COUNT" -#elif (WASM_RT_STACK_EXHAUSTION_HANDLER + WASM_RT_STACK_DEPTH_COUNT) == 0 -#error \ - "Must specify one of WASM_RT_STACK_EXHAUSTION_HANDLER , WASM_RT_STACK_DEPTH_COUNT" #endif +#ifdef __cplusplus +extern "C" { #endif -#if WASM_RT_STACK_EXHAUSTION_HANDLER && !WASM_RT_INSTALL_SIGNAL_HANDLER -#error \ - "WASM_RT_STACK_EXHAUSTION_HANDLER can only be used if WASM_RT_INSTALL_SIGNAL_HANDLER is enabled" +#ifndef WASM_RT_TRAP_HANDLER +/** A setjmp buffer used for handling traps. */ +extern WASM_RT_THREAD_LOCAL wasm_rt_jmp_buf g_wasm_rt_jmp_buf; #endif #if WASM_RT_STACK_DEPTH_COUNT -/** - * When the signal handler cannot be used to detect stack overflows, stack depth - * is limited explicitly. The maximum stack depth before trapping can be - * configured by defining this symbol before including wasm-rt when building the - * generated c files, for example: - * - * ``` - * cc -c -DWASM_RT_MAX_CALL_STACK_DEPTH=100 my_module.c -o my_module.o - * ``` - */ -#ifndef WASM_RT_MAX_CALL_STACK_DEPTH -#define WASM_RT_MAX_CALL_STACK_DEPTH 500 -#endif - -/** Current call stack depth. */ -extern WASM_RT_THREAD_LOCAL uint32_t wasm_rt_call_stack_depth; - -#endif - -#if WASM_RT_USE_SEGUE || WASM_RT_ALLOW_SEGUE -/** - * The segue optimization uses x86 segments to point to a linear memory. If - * used, the runtime must query whether it can use the fast userspace wrgsbase - * instructions or whether it must invoke syscalls to set the segment base, - * depending on the supported CPU features. The result of this query is saved in - * this variable. - */ -extern bool wasm_rt_fsgsbase_inst_supported; -/** - * If fast userspace wrgsbase instructions don't exist, the runtime most provide - * a function that invokes the OS' underlying syscall to set the segment base. - */ -void wasm_rt_syscall_set_segue_base(void* base); -/** - * If fast userspace rdgsbase instructions don't exist, the runtime most provide - * a function that invokes the OS' underlying syscall to get the segment base. - */ -void* wasm_rt_syscall_get_segue_base(); -/** - * If WASM_RT_SEGUE_FREE_SEGMENT is defined, we must only set the segment - * register if it was changed since the last time it was set. The last value set - * on the segment register is stored in this variable. - */ -#if WASM_RT_SEGUE_FREE_SEGMENT -extern WASM_RT_THREAD_LOCAL void* wasm_rt_last_segment_val; -#endif -#endif - -#if defined(_MSC_VER) -#define WASM_RT_NO_RETURN __declspec(noreturn) +/** Saved call stack depth that will be restored in case a trap occurs. */ +extern WASM_RT_THREAD_LOCAL uint32_t wasm_rt_saved_call_stack_depth; +#define WASM_RT_SAVE_STACK_DEPTH() \ + wasm_rt_saved_call_stack_depth = wasm_rt_call_stack_depth #else -#define WASM_RT_NO_RETURN __attribute__((noreturn)) +#define WASM_RT_SAVE_STACK_DEPTH() (void)0 #endif -#if defined(__APPLE__) && WASM_RT_STACK_EXHAUSTION_HANDLER -#define WASM_RT_MERGED_OOB_AND_EXHAUSTION_TRAPS 1 -#else -#define WASM_RT_MERGED_OOB_AND_EXHAUSTION_TRAPS 0 -#endif - -/** Reason a trap occurred. Provide this to `wasm_rt_trap`. */ -typedef enum { - WASM_RT_TRAP_NONE, /** No error. */ - WASM_RT_TRAP_OOB, /** Out-of-bounds access in linear memory or a table. */ - WASM_RT_TRAP_INT_OVERFLOW, /** Integer overflow on divide or truncation. */ - WASM_RT_TRAP_DIV_BY_ZERO, /** Integer divide by zero. */ - WASM_RT_TRAP_INVALID_CONVERSION, /** Conversion from NaN to integer. */ - WASM_RT_TRAP_UNREACHABLE, /** Unreachable instruction executed. */ - WASM_RT_TRAP_CALL_INDIRECT, /** Invalid call_indirect, for any reason. */ - WASM_RT_TRAP_UNCAUGHT_EXCEPTION, /** Exception thrown and not caught. */ - WASM_RT_TRAP_UNALIGNED, /** Unaligned atomic instruction executed. */ -#if WASM_RT_MERGED_OOB_AND_EXHAUSTION_TRAPS - WASM_RT_TRAP_EXHAUSTION = WASM_RT_TRAP_OOB, -#else - WASM_RT_TRAP_EXHAUSTION, /** Call stack exhausted. */ -#endif -} wasm_rt_trap_t; - -/** Value types. Used to define function signatures. */ -typedef enum { - WASM_RT_I32, - WASM_RT_I64, - WASM_RT_F32, - WASM_RT_F64, - WASM_RT_V128, - WASM_RT_FUNCREF, - WASM_RT_EXTERNREF, -} wasm_rt_type_t; - -/** - * A generic function pointer type, both for Wasm functions (`code`) - * and host functions (`hostcode`). All function pointers are stored - * in this canonical form, but must be cast to their proper signature - * to call. - */ -typedef void (*wasm_rt_function_ptr_t)(void); - /** - * A pointer to a "tail-callee" function, called by a tail-call - * trampoline or by another tail-callee function. (The definition uses a - * single-member struct to allow a recursive definition.) - */ -typedef struct wasm_rt_tailcallee_t { - void (*fn)(void** instance_ptr, - void* tail_call_stack, - struct wasm_rt_tailcallee_t* next); -} wasm_rt_tailcallee_t; - -/** - * The type of a function (an arbitrary number of param and result types). - * This is represented as an opaque 256-bit ID. - */ -typedef const char* wasm_rt_func_type_t; - -/** - * A function instance (the runtime representation of a function). - * These can be stored in tables of type funcref, or used as values. - */ -typedef struct { - /** The function's type. */ - wasm_rt_func_type_t func_type; - /** - * The function. The embedder must know the actual C signature of the function - * and cast to it before calling. - */ - wasm_rt_function_ptr_t func; - /** An alternate version of the function to be used when tail-called. */ - wasm_rt_tailcallee_t func_tailcallee; - /** - * A function instance is a closure of the function over an instance - * of the originating module. The module_instance element will be passed into - * the function at runtime. - */ - void* module_instance; -} wasm_rt_funcref_t; - -/** Default (null) value of a funcref */ -#define wasm_rt_funcref_null_value \ - ((wasm_rt_funcref_t){NULL, NULL, {NULL}, NULL}) - -/** The type of an external reference (opaque to WebAssembly). */ -typedef void* wasm_rt_externref_t; - -/** Default (null) value of an externref */ -#define wasm_rt_externref_null_value ((wasm_rt_externref_t){NULL}) - -/** A Memory object. */ -typedef struct { - /** The linear memory data, with a byte length of `size`. */ - uint8_t* data; - /** The current page count for this Memory object. */ - uint64_t pages; - /** - * The maximum page count for this Memory object. If there is no maximum, - * `max_pages` is 0xffffffffu (i.e. UINT32_MAX). - */ - uint64_t max_pages; - /** The current size of the linear memory, in bytes. */ - uint64_t size; - /** Is this memory indexed by u64 (as opposed to default u32) */ - bool is64; -} wasm_rt_memory_t; - -#ifdef WASM_RT_C11_AVAILABLE -/** A shared Memory object. */ -typedef struct { - /** - * The linear memory data, with a byte length of `size`. The memory is marked - * atomic as it is shared and may have to be accessed with different memory - * orders --- sequential when being accessed atomically, relaxed otherwise. - * Unfortunately, the C standard does not state what happens if there are - * overlaps in two memory accesses which have a memory order, e.g., an - * atomic32 being read from the same location an atomic64 is read. One way to - * prevent optimizations from assuming non-overlapping behavior as typically - * done in C is to mark the memory as volatile. Thus the memory is atomic and - * volatile. - */ - _Atomic volatile uint8_t* data; - /** The current page count for this Memory object. */ - uint64_t pages; - /** - * The maximum page count for this Memory object. If there is no maximum, - * `max_pages` is 0xffffffffu (i.e. UINT32_MAX). - */ - uint64_t max_pages; - /** The current size of the linear memory, in bytes. */ - uint64_t size; - /** Is this memory indexed by u64 (as opposed to default u32) */ - bool is64; - /** Lock used to ensure operations such as memory grow are threadsafe */ - WASM_RT_MUTEX mem_lock; -} wasm_rt_shared_memory_t; -#endif - -/** A Table of type funcref. */ -typedef struct { - /** The table element data, with an element count of `size`. */ - wasm_rt_funcref_t* data; - /** - * The maximum element count of this Table object. If there is no maximum, - * `max_size` is 0xffffffffu (i.e. UINT32_MAX). - */ - uint32_t max_size; - /** The current element count of the table. */ - uint32_t size; -} wasm_rt_funcref_table_t; - -/** A Table of type externref. */ -typedef struct { - /** The table element data, with an element count of `size`. */ - wasm_rt_externref_t* data; - /** - * The maximum element count of this Table object. If there is no maximum, - * `max_size` is 0xffffffffu (i.e. UINT32_MAX). - */ - uint32_t max_size; - /** The current element count of the table. */ - uint32_t size; -} wasm_rt_externref_table_t; - -/** Initialize the runtime. */ -void wasm_rt_init(void); - -/** Is the runtime initialized? */ -bool wasm_rt_is_initialized(void); - -/** Free the runtime's state. */ -void wasm_rt_free(void); - -/* - * Initialize the multithreaded runtime for a given thread. Must be - * called by each thread (other than the one that called wasm_rt_init) - * before initializing a Wasm module or calling an exported - * function. - */ -void wasm_rt_init_thread(void); - -/* - * Free the individual thread's state. - */ -void wasm_rt_free_thread(void); - -/** A hardened jmp_buf that allows checking for initialization before use */ -typedef struct { - /** Is the jmp buf intialized? */ - bool initialized; - /** jmp_buf contents */ - jmp_buf buffer; -} wasm_rt_jmp_buf; - -#ifndef _WIN32 -#define WASM_RT_SETJMP_SETBUF(buf) sigsetjmp(buf, 1) -#else -#define WASM_RT_SETJMP_SETBUF(buf) setjmp(buf) -#endif - -#define WASM_RT_SETJMP(buf) \ - ((buf).initialized = true, WASM_RT_SETJMP_SETBUF((buf).buffer)) - -#ifndef _WIN32 -#define WASM_RT_LONGJMP_UNCHECKED(buf, val) siglongjmp(buf, val) -#else -#define WASM_RT_LONGJMP_UNCHECKED(buf, val) longjmp(buf, val) -#endif - -#define WASM_RT_LONGJMP(buf, val) \ - /** Abort on failure as this may be called in the trap handler */ \ - if (!((buf).initialized)) \ - abort(); \ - (buf).initialized = false; \ - WASM_RT_LONGJMP_UNCHECKED((buf).buffer, val) - -/** - * Stop execution immediately and jump back to the call to `wasm_rt_impl_try`. - * The result of `wasm_rt_impl_try` will be the provided trap reason. - * - * This is typically called by the generated code, and not the embedder. - */ -WASM_RT_NO_RETURN void wasm_rt_trap(wasm_rt_trap_t); - -/** Return a human readable error string based on a trap type. */ -const char* wasm_rt_strerror(wasm_rt_trap_t trap); - -#define wasm_rt_try(target) WASM_RT_SETJMP(target) - -/** - * Initialize a Memory object with an initial page size of `initial_pages` and - * a maximum page size of `max_pages`, indexed with an i32 or i64. - * - * ``` - * wasm_rt_memory_t my_memory; - * // 1 initial page (65536 bytes), and a maximum of 2 pages, - * // indexed with an i32 - * wasm_rt_allocate_memory(&my_memory, 1, 2, false); - * ``` - */ -void wasm_rt_allocate_memory(wasm_rt_memory_t*, - uint64_t initial_pages, - uint64_t max_pages, - bool is64); - -/** - * Grow a Memory object by `pages`, and return the previous page count. If - * this new page count is greater than the maximum page count, the grow fails - * and 0xffffffffu (UINT32_MAX) is returned instead. + * Convenience macro to use before calling a wasm function. On first execution + * it will return `WASM_RT_TRAP_NONE` (i.e. 0). If the function traps, it will + * jump back and return the trap that occurred. * - * ``` - * wasm_rt_memory_t my_memory; - * ... - * // Grow memory by 10 pages. - * uint32_t old_page_size = wasm_rt_grow_memory(&my_memory, 10); - * if (old_page_size == UINT32_MAX) { - * // Failed to grow memory. - * } - * ``` - */ -uint64_t wasm_rt_grow_memory(wasm_rt_memory_t*, uint64_t pages); - -/** Free a Memory object. */ -void wasm_rt_free_memory(wasm_rt_memory_t*); - -#ifdef WASM_RT_C11_AVAILABLE -/** Shared memory version of wasm_rt_allocate_memory */ -void wasm_rt_allocate_memory_shared(wasm_rt_shared_memory_t*, - uint64_t initial_pages, - uint64_t max_pages, - bool is64); - -/** Shared memory version of wasm_rt_grow_memory */ -uint64_t wasm_rt_grow_memory_shared(wasm_rt_shared_memory_t*, uint64_t pages); - -/** Shared memory version of wasm_rt_free_memory */ -void wasm_rt_free_memory_shared(wasm_rt_shared_memory_t*); -#endif - -/** - * Initialize a funcref Table object with an element count of `elements` and a - * maximum size of `max_elements`. + * ``` + * wasm_rt_trap_t code = wasm_rt_impl_try(); + * if (code != 0) { + * printf("A trap occurred with code: %d\n", code); + * ... + * } * - * ``` - * wasm_rt_funcref_table_t my_table; - * // 5 elements and a maximum of 10 elements. - * wasm_rt_allocate_funcref_table(&my_table, 5, 10); - * ``` - */ -void wasm_rt_allocate_funcref_table(wasm_rt_funcref_table_t*, - uint32_t elements, - uint32_t max_elements); - -/** Free a funcref Table object. */ -void wasm_rt_free_funcref_table(wasm_rt_funcref_table_t*); - -/** - * Initialize an externref Table object with an element count - * of `elements` and a maximum size of `max_elements`. - * Usage as per wasm_rt_allocate_funcref_table. - */ -void wasm_rt_allocate_externref_table(wasm_rt_externref_table_t*, - uint32_t elements, - uint32_t max_elements); - -/** Free an externref Table object. */ -void wasm_rt_free_externref_table(wasm_rt_externref_table_t*); - -/** - * Grow a Table object by `delta` elements (giving the new elements the value - * `init`), and return the previous element count. If this new element count is - * greater than the maximum element count, the grow fails and 0xffffffffu - * (UINT32_MAX) is returned instead. + * // Call the potentially-trapping function. + * my_wasm_func(); + * ``` */ -uint32_t wasm_rt_grow_funcref_table(wasm_rt_funcref_table_t*, - uint32_t delta, - wasm_rt_funcref_t init); -uint32_t wasm_rt_grow_externref_table(wasm_rt_externref_table_t*, - uint32_t delta, - wasm_rt_externref_t init); +#define wasm_rt_impl_try() \ + (WASM_RT_SAVE_STACK_DEPTH(), wasm_rt_set_unwind_target(&g_wasm_rt_jmp_buf), \ + WASM_RT_SETJMP(g_wasm_rt_jmp_buf)) #ifdef __cplusplus } #endif -#endif /* WASM_RT_H_ */ \ No newline at end of file +#endif /* WASM_RT_IMPL_H_ */ diff --git a/packages/polygen/cpp/wasm-rt/wasm-rt-mem-impl-helper.inc b/packages/polygen/cpp/wasm-rt/wasm-rt-mem-impl-helper.inc index 3edda63c..63d10bc5 100644 --- a/packages/polygen/cpp/wasm-rt/wasm-rt-mem-impl-helper.inc +++ b/packages/polygen/cpp/wasm-rt/wasm-rt-mem-impl-helper.inc @@ -47,50 +47,94 @@ #define MEMORY_API_NAME(name) name##_shared #define MEMORY_CELL_TYPE _Atomic volatile uint8_t* -#if WASM_RT_USE_C11THREADS -#define MEMORY_LOCK_VAR_INIT(name) C11_MEMORY_LOCK_VAR_INIT(name) -#define MEMORY_LOCK_AQUIRE(name) C11_MEMORY_LOCK_AQUIRE(name) -#define MEMORY_LOCK_RELEASE(name) C11_MEMORY_LOCK_RELEASE(name) +#if WASM_RT_USE_CRITICALSECTION +#define MEMORY_LOCK_VAR_INIT(name) WIN_MEMORY_LOCK_VAR_INIT(name) +#define MEMORY_LOCK_AQUIRE(name) WIN_MEMORY_LOCK_AQUIRE(name) +#define MEMORY_LOCK_RELEASE(name) WIN_MEMORY_LOCK_RELEASE(name) #elif WASM_RT_USE_PTHREADS #define MEMORY_LOCK_VAR_INIT(name) PTHREAD_MEMORY_LOCK_VAR_INIT(name) #define MEMORY_LOCK_AQUIRE(name) PTHREAD_MEMORY_LOCK_AQUIRE(name) #define MEMORY_LOCK_RELEASE(name) PTHREAD_MEMORY_LOCK_RELEASE(name) -#elif WASM_RT_USE_CRITICALSECTION -#define MEMORY_LOCK_VAR_INIT(name) WIN_MEMORY_LOCK_VAR_INIT(name) -#define MEMORY_LOCK_AQUIRE(name) WIN_MEMORY_LOCK_AQUIRE(name) -#define MEMORY_LOCK_RELEASE(name) WIN_MEMORY_LOCK_RELEASE(name) #endif #endif +bool MEMORY_API_NAME(wasm_rt_memory_is_default32)(const MEMORY_TYPE* memory) { + return memory->page_size == WASM_DEFAULT_PAGE_SIZE && !memory->is64; +} + void MEMORY_API_NAME(wasm_rt_allocate_memory)(MEMORY_TYPE* memory, uint64_t initial_pages, uint64_t max_pages, - bool is64) { - uint64_t byte_length = initial_pages * WASM_PAGE_SIZE; + bool is64, + uint32_t page_size) { + uint64_t byte_length = initial_pages * page_size; memory->size = byte_length; memory->pages = initial_pages; memory->max_pages = max_pages; memory->is64 = is64; + memory->page_size = page_size; MEMORY_LOCK_VAR_INIT(memory->mem_lock); #if WASM_RT_USE_MMAP - const uint64_t mmap_size = - get_alloc_size_for_mmap(memory->max_pages, memory->is64); - void* addr = os_mmap(mmap_size); - if (!addr) { - os_print_last_error("os_mmap failed."); - abort(); + if (MEMORY_API_NAME(wasm_rt_memory_is_default32)(memory)) { + const uint64_t mmap_size = + get_alloc_size_for_mmap_default32(memory->max_pages); + uint8_t* addr = os_mmap(mmap_size); + if (!addr) { + os_print_last_error("os_mmap failed."); + abort(); + } + uint8_t* data_end = addr + mmap_size; +#if !WABT_BIG_ENDIAN + int ret = os_mprotect(addr, byte_length); +#else + int ret = os_mprotect(data_end - byte_length, byte_length); +#endif + if (ret != 0) { + os_print_last_error("os_mprotect failed."); + abort(); + } + memory->data = (MEMORY_CELL_TYPE)addr; + memory->data_end = (MEMORY_CELL_TYPE)data_end; + return; + } +#endif + + memory->data = (MEMORY_CELL_TYPE)calloc(byte_length, 1); + memory->data_end = memory->data + byte_length; +} + +// Returns 0 on success +static int MEMORY_API_NAME(expand_data_allocation)(MEMORY_TYPE* memory, + uint64_t old_size, + uint64_t new_size, + uint64_t delta_size) { +#if WASM_RT_USE_MMAP + if (MEMORY_API_NAME(wasm_rt_memory_is_default32)(memory)) { +#if !WABT_BIG_ENDIAN + return os_mprotect((void*)(memory->data + old_size), delta_size); +#else + return os_mprotect((void*)(memory->data_end - old_size - delta_size), + delta_size); +#endif } - int ret = os_mprotect(addr, byte_length); - if (ret != 0) { - os_print_last_error("os_mprotect failed."); - abort(); +#endif + + uint8_t* new_data = realloc((void*)memory->data, new_size); + if (new_data == NULL) { + return -1; } - memory->data = addr; +#if !WABT_BIG_ENDIAN + memset((void*)(new_data + old_size), 0, delta_size); #else - memory->data = calloc(byte_length, 1); + memmove((void*)(new_data + new_size - old_size), (void*)new_data, old_size); + memset((void*)new_data, 0, delta_size); #endif + + memory->data = (MEMORY_CELL_TYPE)new_data; + memory->data_end = memory->data + new_size; + return 0; } static uint64_t MEMORY_API_NAME(grow_memory_impl)(MEMORY_TYPE* memory, @@ -103,31 +147,18 @@ static uint64_t MEMORY_API_NAME(grow_memory_impl)(MEMORY_TYPE* memory, if (new_pages < old_pages || new_pages > memory->max_pages) { return (uint64_t)-1; } - uint64_t old_size = old_pages * WASM_PAGE_SIZE; - uint64_t new_size = new_pages * WASM_PAGE_SIZE; - uint64_t delta_size = delta * WASM_PAGE_SIZE; -#if WASM_RT_USE_MMAP - MEMORY_CELL_TYPE new_data = memory->data; - int ret = os_mprotect((void*)(new_data + old_size), delta_size); - if (ret != 0) { - return (uint64_t)-1; - } -#else - MEMORY_CELL_TYPE new_data = realloc((void*)memory->data, new_size); - if (new_data == NULL) { + uint64_t old_size = old_pages * memory->page_size; + uint64_t new_size = new_pages * memory->page_size; + uint64_t delta_size = delta * memory->page_size; + + int err = MEMORY_API_NAME(expand_data_allocation)(memory, old_size, new_size, + delta_size); + if (err != 0) { return (uint64_t)-1; } -#if !WABT_BIG_ENDIAN - memset((void*)(new_data + old_size), 0, delta_size); -#endif -#endif -#if WABT_BIG_ENDIAN - memmove((void*)(new_data + new_size - old_size), (void*)new_data, old_size); - memset((void*)new_data, 0, delta_size); -#endif + memory->pages = new_pages; memory->size = new_size; - memory->data = new_data; return old_pages; } @@ -146,12 +177,14 @@ uint64_t MEMORY_API_NAME(wasm_rt_grow_memory)(MEMORY_TYPE* memory, void MEMORY_API_NAME(wasm_rt_free_memory)(MEMORY_TYPE* memory) { #if WASM_RT_USE_MMAP - const uint64_t mmap_size = - get_alloc_size_for_mmap(memory->max_pages, memory->is64); - os_munmap((void*)memory->data, mmap_size); // ignore error -#else - free((void*)memory->data); + if (MEMORY_API_NAME(wasm_rt_memory_is_default32)(memory)) { + const uint64_t mmap_size = + get_alloc_size_for_mmap_default32(memory->max_pages); + os_munmap((void*)memory->data, mmap_size); // ignore error + return; + } #endif + free((void*)memory->data); } #undef MEMORY_LOCK_RELEASE @@ -161,4 +194,4 @@ void MEMORY_API_NAME(wasm_rt_free_memory)(MEMORY_TYPE* memory) { #undef MEMORY_API_NAME #undef MEMORY_TYPE -#endif \ No newline at end of file +#endif diff --git a/packages/polygen/cpp/wasm-rt/wasm-rt-mem-impl.c b/packages/polygen/cpp/wasm-rt/wasm-rt-mem-impl.c index b8a383ec..953c92db 100644 --- a/packages/polygen/cpp/wasm-rt/wasm-rt-mem-impl.c +++ b/packages/polygen/cpp/wasm-rt/wasm-rt-mem-impl.c @@ -25,28 +25,10 @@ #include #endif -#define WASM_PAGE_SIZE 65536 - #ifdef WASM_RT_GROW_FAILED_HANDLER extern void WASM_RT_GROW_FAILED_HANDLER(); #endif -#define C11_MEMORY_LOCK_VAR_INIT(name) \ - if (mtx_init(&(name), mtx_plain) != thrd_success) { \ - fprintf(stderr, "Lock init failed\n"); \ - abort(); \ - } -#define C11_MEMORY_LOCK_AQUIRE(name) \ - if (mtx_lock(&(name)) != thrd_success) { \ - fprintf(stderr, "Lock acquire failed\n"); \ - abort(); \ - } -#define C11_MEMORY_LOCK_RELEASE(name) \ - if (mtx_unlock(&(name)) != thrd_success) { \ - fprintf(stderr, "Lock release failed\n"); \ - abort(); \ - } - #define PTHREAD_MEMORY_LOCK_VAR_INIT(name) \ if (pthread_mutex_init(&(name), NULL) != 0) { \ fprintf(stderr, "Lock init failed\n"); \ @@ -112,39 +94,41 @@ static void os_print_last_error(const char* msg) { } } -#else +#else /* !_WIN32 */ static void* os_mmap(size_t size) { - int map_prot = PROT_NONE; - int map_flags = MAP_ANONYMOUS | MAP_PRIVATE; - uint8_t* addr = mmap(NULL, size, map_prot, map_flags, -1, 0); - if (addr == MAP_FAILED) - return NULL; - return addr; + int map_prot = PROT_NONE; + int map_flags = MAP_ANONYMOUS | MAP_PRIVATE; + uint8_t* addr = mmap(NULL, size, map_prot, map_flags, -1, 0); + if (addr == MAP_FAILED) + return NULL; + return addr; } static int os_munmap(void* addr, size_t size) { - return munmap(addr, size); + return munmap(addr, size); } static int os_mprotect(void* addr, size_t size) { - return mprotect(addr, size, PROT_READ | PROT_WRITE); + if (size == 0) { + return 0; + } + return mprotect(addr, size, PROT_READ | PROT_WRITE); } static void os_print_last_error(const char* msg) { - perror(msg); + perror(msg); } -#endif +#endif /* _WIN32 */ -static uint64_t get_alloc_size_for_mmap(uint64_t max_pages, bool is64) { - assert(!is64 && "memory64 is not yet compatible with WASM_RT_USE_MMAP"); +static uint64_t get_alloc_size_for_mmap_default32(uint64_t max_pages) { #if WASM_RT_MEMCHECK_GUARD_PAGES - /* Reserve 8GiB. */ - const uint64_t max_size = 0x200000000ul; - return max_size; + /* Reserve 8GiB. */ + const uint64_t max_size = 0x200000000ul; + return max_size; #else - if (max_pages != 0) { - const uint64_t max_size = max_pages * WASM_PAGE_SIZE; + if (max_pages != 0) { + const uint64_t max_size = max_pages * WASM_DEFAULT_PAGE_SIZE; return max_size; } @@ -154,7 +138,7 @@ static uint64_t get_alloc_size_for_mmap(uint64_t max_pages, bool is64) { #endif } -#endif +#endif /* WASM_RT_USE_MMAP */ // Include operations for memory #define WASM_RT_MEM_OPS @@ -175,4 +159,3 @@ static uint64_t get_alloc_size_for_mmap(uint64_t max_pages, bool is64) { #undef WIN_MEMORY_LOCK_VAR_INIT #undef WIN_MEMORY_LOCK_AQUIRE #undef WIN_MEMORY_LOCK_RELEASE -#undef WASM_PAGE_SIZE \ No newline at end of file diff --git a/packages/polygen/cpp/wasm-rt/wasm-rt.h b/packages/polygen/cpp/wasm-rt/wasm-rt.h index d15aeac7..bce33ce8 100644 --- a/packages/polygen/cpp/wasm-rt/wasm-rt.h +++ b/packages/polygen/cpp/wasm-rt/wasm-rt.h @@ -32,13 +32,8 @@ extern "C" { #endif #if __has_builtin(__builtin_expect) -#ifndef UNLIKELY #define UNLIKELY(x) __builtin_expect(!!(x), 0) -#endif - -#ifndef LIKELY #define LIKELY(x) __builtin_expect(!!(x), 1) -#endif #else #define UNLIKELY(x) (x) #define LIKELY(x) (x) @@ -72,22 +67,21 @@ extern "C" { #include #define WASM_RT_MUTEX CRITICAL_SECTION #define WASM_RT_USE_CRITICALSECTION 1 -#elif defined(__APPLE__) || defined(__STDC_NO_THREADS__) +#else #include #define WASM_RT_MUTEX pthread_mutex_t #define WASM_RT_USE_PTHREADS 1 -#else -#include -#define WASM_RT_MUTEX mtx_t -#define WASM_RT_USE_C11THREADS 1 #endif #endif -#ifdef _MSC_VER -#define WASM_RT_THREAD_LOCAL __declspec(thread) -#elif defined(WASM_RT_C11_AVAILABLE) +#ifdef WASM_RT_C11_AVAILABLE #define WASM_RT_THREAD_LOCAL _Thread_local +#elif defined(_MSC_VER) +#define WASM_RT_THREAD_LOCAL __declspec(thread) +#elif (defined(__GNUC__) || defined(__clang__)) && !defined(__APPLE__) +// Disabled on Apple systems due to sporadic test failures. +#define WASM_RT_THREAD_LOCAL __thread #else #define WASM_RT_THREAD_LOCAL #endif @@ -131,7 +125,7 @@ extern "C" { * needed (so we can use the guard based range checks below). */ #ifndef WASM_RT_USE_MMAP -#if UINTPTR_MAX > 0xffffffff && !SUPPORT_MEMORY64 +#if UINTPTR_MAX > 0xffffffff #define WASM_RT_USE_MMAP 1 #else #define WASM_RT_USE_MMAP 0 @@ -146,15 +140,13 @@ extern "C" { * * BOUNDS_CHECK: memory accesses are checked with explicit bounds checks. * - * This defaults to GUARD_PAGES as this is the fasest option, iff the + * This defaults to GUARD_PAGES as this is the fastest option, iff the * requirements of GUARD_PAGES --- 64-bit platforms, MMAP allocation strategy, - * no 64-bit memories, no big-endian --- are met. This falls back to BOUNDS - * otherwise. + * no 64-bit memories --- are met. This falls back to BOUNDS otherwise. */ /** Check if Guard checks are supported */ -#if UINTPTR_MAX > 0xffffffff && WASM_RT_USE_MMAP && !SUPPORT_MEMORY64 && \ - !WABT_BIG_ENDIAN +#if UINTPTR_MAX > 0xffffffff && WASM_RT_USE_MMAP #define WASM_RT_GUARD_PAGES_SUPPORTED 1 #else #define WASM_RT_GUARD_PAGES_SUPPORTED 0 @@ -233,6 +225,34 @@ extern "C" { #define WASM_RT_SEGUE_FREE_SEGMENT 0 #endif +#ifndef WASM_RT_USE_SEGUE +// Memory functions can use the segue optimization if allowed. The segue +// optimization uses x86 segments to point to a linear memory. We use this +// optimization when: +// +// (1) Segue is allowed using WASM_RT_ALLOW_SEGUE +// (2) on x86_64 without WABT_BIG_ENDIAN enabled +// (3) the compiler supports: intrinsics for (rd|wr)gsbase, "address namespaces" +// for accessing pointers, and supports memcpy on pointers with custom +// "address namespaces". GCC does not support the memcpy requirement, so +// this leaves only clang (version 9 or later) for now. +// (4) The OS provides a way to query if (rd|wr)gsbase is allowed by the kernel +// or the implementation has to use a syscall for this. +// (5) The OS doesn't replace the segment register on context switch which +// eliminates windows for now +// +// While more OS can be supported in the future, we only support linux for now +#if WASM_RT_ALLOW_SEGUE && !WABT_BIG_ENDIAN && \ + (defined(__x86_64__) || defined(_M_X64)) && __clang__ && \ + (__clang_major__ >= 9) && __has_builtin(__builtin_ia32_wrgsbase64) && \ + !defined(_WIN32) && !defined(__ANDROID__) && \ + (defined(__linux__) || defined(__FreeBSD__)) +#define WASM_RT_USE_SEGUE 1 +#else +#define WASM_RT_USE_SEGUE 0 +#endif +#endif + /** * This macro, if defined, allows the embedder to disable all stack exhaustion * checks. This a non conformant configuration, i.e., this does not respect @@ -245,13 +265,15 @@ extern "C" { /** * We need to detect and trap stack overflows. If we use a signal handler on * POSIX systems, this can detect call stack overflows. On windows, or platforms - * without a signal handler, we use stack depth counting. + * without a signal handler, we use stack depth counting. The s390x big endian + * platform additionally seems to have issues with stack guard pages, so we play + * it safe and use stack counting on big endian platforms. */ #if !defined(WASM_RT_STACK_DEPTH_COUNT) && \ !defined(WASM_RT_STACK_EXHAUSTION_HANDLER) && \ !WASM_RT_NONCONFORMING_UNCHECKED_STACK_EXHAUSTION -#if WASM_RT_INSTALL_SIGNAL_HANDLER && !defined(_WIN32) +#if WASM_RT_INSTALL_SIGNAL_HANDLER && !defined(_WIN32) && !WABT_BIG_ENDIAN #define WASM_RT_STACK_EXHAUSTION_HANDLER 1 #else #define WASM_RT_STACK_DEPTH_COUNT 1 @@ -311,7 +333,7 @@ extern WASM_RT_THREAD_LOCAL uint32_t wasm_rt_call_stack_depth; #endif -#if WASM_RT_USE_SEGUE || WASM_RT_ALLOW_SEGUE +#if WASM_RT_USE_SEGUE /** * The segue optimization uses x86 segments to point to a linear memory. If * used, the runtime must query whether it can use the fast userspace wrgsbase @@ -354,31 +376,33 @@ extern WASM_RT_THREAD_LOCAL void* wasm_rt_last_segment_val; /** Reason a trap occurred. Provide this to `wasm_rt_trap`. */ typedef enum { - WASM_RT_TRAP_NONE, /** No error. */ - WASM_RT_TRAP_OOB, /** Out-of-bounds access in linear memory or a table. */ - WASM_RT_TRAP_INT_OVERFLOW, /** Integer overflow on divide or truncation. */ - WASM_RT_TRAP_DIV_BY_ZERO, /** Integer divide by zero. */ - WASM_RT_TRAP_INVALID_CONVERSION, /** Conversion from NaN to integer. */ - WASM_RT_TRAP_UNREACHABLE, /** Unreachable instruction executed. */ - WASM_RT_TRAP_CALL_INDIRECT, /** Invalid call_indirect, for any reason. */ - WASM_RT_TRAP_UNCAUGHT_EXCEPTION, /** Exception thrown and not caught. */ - WASM_RT_TRAP_UNALIGNED, /** Unaligned atomic instruction executed. */ + WASM_RT_TRAP_NONE, /** No error. */ + WASM_RT_TRAP_OOB, /** Out-of-bounds access in linear memory or a table. */ + WASM_RT_TRAP_INT_OVERFLOW, /** Integer overflow on divide or truncation. */ + WASM_RT_TRAP_DIV_BY_ZERO, /** Integer divide by zero. */ + WASM_RT_TRAP_INVALID_CONVERSION, /** Conversion from NaN to integer. */ + WASM_RT_TRAP_UNREACHABLE, /** Unreachable instruction executed. */ + WASM_RT_TRAP_CALL_INDIRECT, /** Invalid call_indirect, for any reason. */ + WASM_RT_TRAP_NULL_REF, /** Null reference. */ + WASM_RT_TRAP_UNCAUGHT_EXCEPTION, /** Exception thrown and not caught. */ + WASM_RT_TRAP_UNALIGNED, /** Unaligned atomic instruction executed. */ #if WASM_RT_MERGED_OOB_AND_EXHAUSTION_TRAPS - WASM_RT_TRAP_EXHAUSTION = WASM_RT_TRAP_OOB, + WASM_RT_TRAP_EXHAUSTION = WASM_RT_TRAP_OOB, #else - WASM_RT_TRAP_EXHAUSTION, /** Call stack exhausted. */ + WASM_RT_TRAP_EXHAUSTION, /** Call stack exhausted. */ #endif } wasm_rt_trap_t; /** Value types. Used to define function signatures. */ typedef enum { - WASM_RT_I32, - WASM_RT_I64, - WASM_RT_F32, - WASM_RT_F64, - WASM_RT_V128, - WASM_RT_FUNCREF, - WASM_RT_EXTERNREF, + WASM_RT_I32, + WASM_RT_I64, + WASM_RT_F32, + WASM_RT_F64, + WASM_RT_V128, + WASM_RT_FUNCREF, + WASM_RT_EXTERNREF, + WASM_RT_EXNREF, } wasm_rt_type_t; /** @@ -395,9 +419,9 @@ typedef void (*wasm_rt_function_ptr_t)(void); * single-member struct to allow a recursive definition.) */ typedef struct wasm_rt_tailcallee_t { - void (*fn)(void** instance_ptr, - void* tail_call_stack, - struct wasm_rt_tailcallee_t* next); + void (*fn)(void** instance_ptr, + void* tail_call_stack, + struct wasm_rt_tailcallee_t* next); } wasm_rt_tailcallee_t; /** @@ -411,21 +435,21 @@ typedef const char* wasm_rt_func_type_t; * These can be stored in tables of type funcref, or used as values. */ typedef struct { - /** The function's type. */ - wasm_rt_func_type_t func_type; - /** - * The function. The embedder must know the actual C signature of the function - * and cast to it before calling. - */ - wasm_rt_function_ptr_t func; - /** An alternate version of the function to be used when tail-called. */ - wasm_rt_tailcallee_t func_tailcallee; - /** - * A function instance is a closure of the function over an instance - * of the originating module. The module_instance element will be passed into - * the function at runtime. - */ - void* module_instance; + /** The function's type. */ + wasm_rt_func_type_t func_type; + /** + * The function. The embedder must know the actual C signature of the function + * and cast to it before calling. + */ + wasm_rt_function_ptr_t func; + /** An alternate version of the function to be used when tail-called. */ + wasm_rt_tailcallee_t func_tailcallee; + /** + * A function instance is a closure of the function over an instance + * of the originating module. The module_instance element will be passed into + * the function at runtime. + */ + void* module_instance; } wasm_rt_funcref_t; /** Default (null) value of a funcref */ @@ -440,19 +464,21 @@ typedef void* wasm_rt_externref_t; /** A Memory object. */ typedef struct { - /** The linear memory data, with a byte length of `size`. */ - uint8_t* data; - /** The current page count for this Memory object. */ - uint64_t pages; - /** - * The maximum page count for this Memory object. If there is no maximum, - * `max_pages` is 0xffffffffu (i.e. UINT32_MAX). - */ - uint64_t max_pages; - /** The current size of the linear memory, in bytes. */ - uint64_t size; - /** Is this memory indexed by u64 (as opposed to default u32) */ - bool is64; + /** The linear memory data, with a byte length of `size`. */ + uint8_t* data; + /** The location after the the reserved space for the linear memory data. */ + uint8_t* data_end; + /** The page size for this Memory object + (always 64 KiB without the custom-page-sizes feature) */ + uint32_t page_size; + /** The current page count for this Memory object. */ + uint64_t pages; + /** The maximum page count for this Memory object. */ + uint64_t max_pages; + /** The current size of the linear memory, in bytes. */ + uint64_t size; + /** Is this memory indexed by u64 (as opposed to default u32) */ + bool is64; } wasm_rt_memory_t; #ifdef WASM_RT_C11_AVAILABLE @@ -470,12 +496,15 @@ typedef struct { * volatile. */ _Atomic volatile uint8_t* data; + /** The location one byte after the reserved space for the linear memory data. + * This includes any reserved pages that are not yet allocated. */ + _Atomic volatile uint8_t* data_end; + /** The page size for this Memory object + (always 64 KiB without the custom-page-sizes feature) */ + uint32_t page_size; /** The current page count for this Memory object. */ uint64_t pages; - /** - * The maximum page count for this Memory object. If there is no maximum, - * `max_pages` is 0xffffffffu (i.e. UINT32_MAX). - */ + /* The maximum page count for this Memory object. */ uint64_t max_pages; /** The current size of the linear memory, in bytes. */ uint64_t size; @@ -488,28 +517,28 @@ typedef struct { /** A Table of type funcref. */ typedef struct { - /** The table element data, with an element count of `size`. */ - wasm_rt_funcref_t* data; - /** - * The maximum element count of this Table object. If there is no maximum, - * `max_size` is 0xffffffffu (i.e. UINT32_MAX). - */ - uint32_t max_size; - /** The current element count of the table. */ - uint32_t size; + /** The table element data, with an element count of `size`. */ + wasm_rt_funcref_t* data; + /** + * The maximum element count of this Table object. If there is no maximum, + * `max_size` is 0xffffffffu (i.e. UINT32_MAX). + */ + uint32_t max_size; + /** The current element count of the table. */ + uint32_t size; } wasm_rt_funcref_table_t; /** A Table of type externref. */ typedef struct { - /** The table element data, with an element count of `size`. */ - wasm_rt_externref_t* data; - /** - * The maximum element count of this Table object. If there is no maximum, - * `max_size` is 0xffffffffu (i.e. UINT32_MAX). - */ - uint32_t max_size; - /** The current element count of the table. */ - uint32_t size; + /** The table element data, with an element count of `size`. */ + wasm_rt_externref_t* data; + /** + * The maximum element count of this Table object. If there is no maximum, + * `max_size` is 0xffffffffu (i.e. UINT32_MAX). + */ + uint32_t max_size; + /** The current element count of the table. */ + uint32_t size; } wasm_rt_externref_table_t; /** Initialize the runtime. */ @@ -536,10 +565,10 @@ void wasm_rt_free_thread(void); /** A hardened jmp_buf that allows checking for initialization before use */ typedef struct { - /** Is the jmp buf intialized? */ - bool initialized; - /** jmp_buf contents */ - jmp_buf buffer; + /** Is the jmp buf intialized? */ + bool initialized; + /** jmp_buf contents */ + jmp_buf buffer; } wasm_rt_jmp_buf; #ifndef _WIN32 @@ -577,6 +606,9 @@ const char* wasm_rt_strerror(wasm_rt_trap_t trap); #define wasm_rt_try(target) WASM_RT_SETJMP(target) +/** WebAssembly's default page size (64 KiB) */ +#define WASM_DEFAULT_PAGE_SIZE 65536 + /** * Initialize a Memory object with an initial page size of `initial_pages` and * a maximum page size of `max_pages`, indexed with an i32 or i64. @@ -585,13 +617,14 @@ const char* wasm_rt_strerror(wasm_rt_trap_t trap); * wasm_rt_memory_t my_memory; * // 1 initial page (65536 bytes), and a maximum of 2 pages, * // indexed with an i32 - * wasm_rt_allocate_memory(&my_memory, 1, 2, false); + * wasm_rt_allocate_memory(&my_memory, 1, 2, false, WASM_DEFAULT_PAGE_SIZE); * ``` */ void wasm_rt_allocate_memory(wasm_rt_memory_t*, uint64_t initial_pages, uint64_t max_pages, - bool is64); + bool is64, + uint32_t page_size); /** * Grow a Memory object by `pages`, and return the previous page count. If @@ -618,7 +651,8 @@ void wasm_rt_free_memory(wasm_rt_memory_t*); void wasm_rt_allocate_memory_shared(wasm_rt_shared_memory_t*, uint64_t initial_pages, uint64_t max_pages, - bool is64); + bool is64, + uint32_t page_size); /** Shared memory version of wasm_rt_grow_memory */ uint64_t wasm_rt_grow_memory_shared(wasm_rt_shared_memory_t*, uint64_t pages); diff --git a/packages/polygen/react-native.config.js b/packages/polygen/react-native.config.js index a8ff2d51..9ee35596 100644 --- a/packages/polygen/react-native.config.js +++ b/packages/polygen/react-native.config.js @@ -8,7 +8,9 @@ module.exports = { dependency: { platforms: { android: { - cmakeListsPath: 'build/generated/source/polygen/jni/CMakeLists.txt', + packageImportPath: 'import com.callstack.polygen.PolygenPackage;', + packageInstance: 'new PolygenPackage()', + cmakeListsPath: 'android/CMakeLists.txt', }, ios: {}, },