crowforkotlin opened issue #12229:
Subject: "Code for function is too large" error and 1.6GB Memory Spike during JIT compilation of a 781KB Kotlin/Wasm module
Environment:
- Wasmtime version: v40.0.0 (Release #12192)
- Host Machine (Build): Mac M2 (aarch64)
- Target Device (Runtime): Android (aarch64)
- Compiler: Kotlin/Wasm (WASI) via
compileProductionExecutableKotlinWasmWasiOptimize- Project Link: crowforkotlin/wasmline
- Source File: Plugin.kt
Description:
I am reporting aCompilation error: Code for function is too largethat occurs during the JIT loading phase on Android.Despite the generated
.wasmbinary being only 781.63 KB, the compilation fails when a single Kotlin function contains a large amount of logic/content. During the attempt to compile this module, I observed a massive RSS memory spike on the Android device, jumping from ~235 MB to over 1.6 GB in just 4 seconds, eventually resulting in the "function too large" error.Wasmtime Configuration:
The engine is configured specifically for Android compatibility to avoid signal conflicts with ART and minimize VSS overhead:/** * Creates and configures the Wasm Config object. * * Critical Android Settings: * 1. Signals-based traps DISABLED: To prevent conflicts with Android ART signal handlers (SIGSEGV). * 2. Memory Guard Size = 0: To prevent VSS (Virtual Set Size) OOM on 32-bit or limited devices. * 3. GC / Exceptions: Enabled for Kotlin/Wasm support. */ wasm_config_t *Engine::createConfig() { wasm_config_t *conf = wasm_config_new(); // Feature Flags for Kotlin/Wasm support wasmtime_config_wasm_gc_set(conf, true); wasmtime_config_wasm_function_references_set(conf, true); wasmtime_config_wasm_exceptions_set(conf, true); // Optimization: Disable SIMD if not strictly needed (improves compatibility) wasmtime_config_wasm_simd_set(conf, false); wasmtime_config_wasm_relaxed_simd_set(conf, false); // [CRITICAL] Disable signal handlers to avoid crash conflicts with Android Runtime (ART) wasmtime_config_signals_based_traps_set(conf, false); // [CRITICAL] Set guard pages to 0 to minimize Virtual Memory usage (prevents OOM) wasmtime_config_memory_guard_size_set(conf, 0); // Set max stack size (512KB is usually sufficient for mobile logic) wasmtime_config_max_wasm_stack_set(conf, 512 * 1024); // Compiler Optimization Strategy: Optimize for Speed and Binary Size wasmtime_config_cranelift_opt_level_set(conf, WASMTIME_OPT_LEVEL_SPEED_AND_SIZE); wasmtime_config_cranelift_debug_verifier_set(conf, false); return conf; }Reproduction Steps:
- Add a large amount of content/logic into the
wasmWasiMainfunction inPlugin.kt.- Build the wasm file:
./gradlew wasmtime-core:plugin:compileProductionExecutableKotlinWasmWasiOptimize.- The resulting
plugin.wasmis ~781 KB.- Load the module on an Android device using the configuration above.
Observed Behavior:
- Total Wasm Size: 781.63 KB (Small)
- Memory Usage: Spikes from 234MB to 1.6GB.
- Result:
Compilation error: Code for function is too large.Full Android Logs:
00:49:34.204 I [MotionEvent] ViewRootImpl windowName 'crow.wasmtime.wasmline/crow.mordecai.wasmline.MainActivity', { action=ACTION_DOWN, id[0]=0, pointerCount=1, eventTime=51746485, downTime=51746485, phoneEventTime=00:49:34.191 } moveCount:0 00:49:34.206 D getMiuiFreeformStackInfo mTmpFrames.miuiFreeFormStackInfo: null 00:49:34.217 I CreateGraphicsPipeline pipeline cache hit. elpased time: 0.32 ms. 00:49:34.218 E FrameInsert open fail: No such file or directory 00:49:34.230 I This is non sticky GC, maxfree is 33554432 minfree is 8388608 00:49:34.227 W type=1400 audit(0.0:2008818): avc: denied { getopt } for path="/dev/socket/usap_pool_primary" scontext=u:r:untrusted_app:s0:c190,c257,c512,c768 tcontext=u:r:zygote:s0 tclass=unix_stream_socket permissive=0 app=crow.wasmtime.wasmline 00:49:34.322 I [MotionEvent] ViewRootImpl windowName 'crow.wasmtime.wasmline/crow.mordecai.wasmline.MainActivity', { action=ACTION_UP, id[0]=0, pointerCount=1, eventTime=51746611, downTime=51746485, phoneEventTime=00:49:34.316 } moveCount:0 00:49:34.332 I ============================================== 00:49:34.337 I [Android] Wasm file : plugin.wasm || wasm file exits : true || wasm file path : /data/user/0/crow.wasmtime.wasmline/cache/plugin.wasm 00:49:34.338 I [Android] Cwasm cache file : plugin.cwasm || cache file exits : false || cache file path : /data/user/0/crow.wasmtime.wasmline/cache/plugin.cwasm 00:49:34.345 I [Wasmtime] Module --> Jit Compiling for /data/user/0/crow.wasmtime.wasmline/cache/plugin.wasm... 00:49:34.363 W type=1400 audit(0.0:2008819): avc: denied { read } for name="cpu.cfs_quota_us" dev="cgroup" ino=64 scontext=u:r:untrusted_app:s0:c190,c257,c512,c768 tcontext=u:object_r:cgroup:s0 tclass=file permissive=0 app=crow.wasmtime.wasmline 00:49:34.363 W type=1400 audit(0.0:2008820): avc: denied { read } for name="cpu.cfs_period_us" dev="cgroup" ino=65 scontext=u:r:untrusted_app:s0:c190,c257,c512,c768 tcontext=u:object_r:cgroup:s0 tclass=file permissive=0 app=crow.wasmtime.wasmline 00:49:34.363 W type=1400 audit(0.0:2008821): avc: denied { read } for name="cpu.cfs_quota_us" dev="cgroup" ino=10 scontext=u:r:untrusted_app:s0:c190,c257,c512,c768 tcontext=u:object_r:cgroup:s0 tclass=file permissive=0 app=crow.wasmtime.wasmline 00:49:34.363 W type=1400 audit(0.0:2008822): avc: denied { read } for name="cpu.cfs_period_us" dev="cgroup" ino=11 scontext=u:r:untrusted_app:s0:c190,c257,c512,c768 tcontext=u:object_r:cgroup:s0 tclass=file permissive=0 app=crow.wasmtime.wasmline 00:49:34.441 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 234952kb -> 342100kb 00:49:34.494 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 342100kb -> 428844kb 00:49:34.544 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 428844kb -> 541612kb 00:49:34.602 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 541612kb -> 657240kb 00:49:34.928 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 657240kb -> 860432kb 00:49:35.761 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 860432kb -> 959324kb 00:49:36.012 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 959324kb -> 1075892kb 00:49:36.184 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 1075892kb -> 1183820kb 00:49:37.835 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 1183820kb -> 1306888kb 00:49:38.482 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 1306888kb -> 1450064kb 00:49:38.639 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 1450064kb -> 1612484kb 00:49:38.700 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 1612484kb -> 1389664kb 00:49:38.700 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 1389664kb -> 1011816kb 00:49:38.702 E [Wasmtime] Module --> Error loading module /data/user/0/crow.wasmtime.wasmline/cache/plugin.wasm: Compilation error: Code for function is too large 00:49:38.708 I [Wasmline] Load failure, because native load return false, file path is : /data/user/0/crow.wasmtime.wasmline/cache/plugin.wasm 00:49:38.790 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 1011816kb -> 727648kbArtifacts:
Wasmline-wasmline-core-plugin.wasm.zip
Wasm File Size (781.63 KB):
Error Detail:
Success state (after reducing content):
Analysis & Questions:
- Why does a relatively small Wasm file (under 1MB) trigger a "Code too large" error? It seems the Kotlin/Wasm compiler is generating a single function with extremely high complexity.
- The memory usage spike to 1.6GB suggests that Cranelift might be hitting a worst-case scenario in its e-graph optimization or register allocation phase.
- Are there any specific Cranelift settings to mitigate this, or is this a fundamental limit on function complexity regardless of the total
.wasmfile size?
crowforkotlin added the bug label to Issue #12229.
crowforkotlin edited issue #12229:
Subject: "Code for function is too large" error and 1.6GB Memory Spike during JIT compilation of a 781KB Kotlin/Wasm module
Environment:
- Wasmtime version: v40.0.0 (Release #12192)
- Host Machine (Build): Mac M2 (aarch64)
- Target Device (Runtime): Android (aarch64)
- Compiler: Kotlin/Wasm (WASI) via
compileProductionExecutableKotlinWasmWasiOptimize- Project Link: crowforkotlin/wasmline
- Source File: Plugin.kt
Description:
I am reporting aCompilation error: Code for function is too largethat occurs during the JIT loading phase on Android.Despite the generated
.wasmbinary being only 781.63 KB, the compilation fails when a single Kotlin function contains a large amount of logic/content. During the attempt to compile this module, I observed a massive RSS memory spike on the Android device, jumping from ~235 MB to over 1.6 GB in just 4 seconds, eventually resulting in the "function too large" error.Wasmtime Configuration:
The engine is configured specifically for Android compatibility to avoid signal conflicts with ART and minimize VSS overhead:/** * Creates and configures the Wasm Config object. * * Critical Android Settings: * 1. Signals-based traps DISABLED: To prevent conflicts with Android ART signal handlers (SIGSEGV). * 2. Memory Guard Size = 0: To prevent VSS (Virtual Set Size) OOM on 32-bit or limited devices. * 3. GC / Exceptions: Enabled for Kotlin/Wasm support. */ wasm_config_t *Engine::createConfig() { wasm_config_t *conf = wasm_config_new(); // Feature Flags for Kotlin/Wasm support wasmtime_config_wasm_gc_set(conf, true); wasmtime_config_wasm_function_references_set(conf, true); wasmtime_config_wasm_exceptions_set(conf, true); // Optimization: Disable SIMD if not strictly needed (improves compatibility) wasmtime_config_wasm_simd_set(conf, false); wasmtime_config_wasm_relaxed_simd_set(conf, false); // [CRITICAL] Disable signal handlers to avoid crash conflicts with Android Runtime (ART) wasmtime_config_signals_based_traps_set(conf, false); // [CRITICAL] Set guard pages to 0 to minimize Virtual Memory usage (prevents OOM) wasmtime_config_memory_guard_size_set(conf, 0); // Set max stack size (512KB is usually sufficient for mobile logic) wasmtime_config_max_wasm_stack_set(conf, 512 * 1024); // Compiler Optimization Strategy: Optimize for Speed and Binary Size wasmtime_config_cranelift_opt_level_set(conf, WASMTIME_OPT_LEVEL_SPEED_AND_SIZE); wasmtime_config_cranelift_debug_verifier_set(conf, false); return conf; }Reproduction Steps:
- Add a large amount of content/logic into the
wasmWasiMainfunction inPlugin.kt.- Build the wasm file:
./gradlew wasmtime-core:plugin:compileProductionExecutableKotlinWasmWasiOptimize.- The resulting
plugin.wasmis ~781 KB.- Load the module on an Android device using the configuration above.
Observed Behavior:
- Total Wasm Size: 781.63 KB (Small)
- Memory Usage: Spikes from 234MB to 1.6GB.
- Result:
Compilation error: Code for function is too large.Full Android Logs:
00:49:34.204 I [MotionEvent] ViewRootImpl windowName 'crow.wasmtime.wasmline/crow.mordecai.wasmline.MainActivity', { action=ACTION_DOWN, id[0]=0, pointerCount=1, eventTime=51746485, downTime=51746485, phoneEventTime=00:49:34.191 } moveCount:0 00:49:34.206 D getMiuiFreeformStackInfo mTmpFrames.miuiFreeFormStackInfo: null 00:49:34.217 I CreateGraphicsPipeline pipeline cache hit. elpased time: 0.32 ms. 00:49:34.218 E FrameInsert open fail: No such file or directory 00:49:34.230 I This is non sticky GC, maxfree is 33554432 minfree is 8388608 00:49:34.227 W type=1400 audit(0.0:2008818): avc: denied { getopt } for path="/dev/socket/usap_pool_primary" scontext=u:r:untrusted_app:s0:c190,c257,c512,c768 tcontext=u:r:zygote:s0 tclass=unix_stream_socket permissive=0 app=crow.wasmtime.wasmline 00:49:34.322 I [MotionEvent] ViewRootImpl windowName 'crow.wasmtime.wasmline/crow.mordecai.wasmline.MainActivity', { action=ACTION_UP, id[0]=0, pointerCount=1, eventTime=51746611, downTime=51746485, phoneEventTime=00:49:34.316 } moveCount:0 00:49:34.332 I ============================================== 00:49:34.337 I [Android] Wasm file : plugin.wasm || wasm file exits : true || wasm file path : /data/user/0/crow.wasmtime.wasmline/cache/plugin.wasm 00:49:34.338 I [Android] Cwasm cache file : plugin.cwasm || cache file exits : false || cache file path : /data/user/0/crow.wasmtime.wasmline/cache/plugin.cwasm 00:49:34.345 I [Wasmtime] Module --> Jit Compiling for /data/user/0/crow.wasmtime.wasmline/cache/plugin.wasm... 00:49:34.363 W type=1400 audit(0.0:2008819): avc: denied { read } for name="cpu.cfs_quota_us" dev="cgroup" ino=64 scontext=u:r:untrusted_app:s0:c190,c257,c512,c768 tcontext=u:object_r:cgroup:s0 tclass=file permissive=0 app=crow.wasmtime.wasmline 00:49:34.363 W type=1400 audit(0.0:2008820): avc: denied { read } for name="cpu.cfs_period_us" dev="cgroup" ino=65 scontext=u:r:untrusted_app:s0:c190,c257,c512,c768 tcontext=u:object_r:cgroup:s0 tclass=file permissive=0 app=crow.wasmtime.wasmline 00:49:34.363 W type=1400 audit(0.0:2008821): avc: denied { read } for name="cpu.cfs_quota_us" dev="cgroup" ino=10 scontext=u:r:untrusted_app:s0:c190,c257,c512,c768 tcontext=u:object_r:cgroup:s0 tclass=file permissive=0 app=crow.wasmtime.wasmline 00:49:34.363 W type=1400 audit(0.0:2008822): avc: denied { read } for name="cpu.cfs_period_us" dev="cgroup" ino=11 scontext=u:r:untrusted_app:s0:c190,c257,c512,c768 tcontext=u:object_r:cgroup:s0 tclass=file permissive=0 app=crow.wasmtime.wasmline 00:49:34.441 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 234952kb -> 342100kb 00:49:34.494 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 342100kb -> 428844kb 00:49:34.544 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 428844kb -> 541612kb 00:49:34.602 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 541612kb -> 657240kb 00:49:34.928 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 657240kb -> 860432kb 00:49:35.761 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 860432kb -> 959324kb 00:49:36.012 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 959324kb -> 1075892kb 00:49:36.184 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 1075892kb -> 1183820kb 00:49:37.835 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 1183820kb -> 1306888kb 00:49:38.482 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 1306888kb -> 1450064kb 00:49:38.639 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 1450064kb -> 1612484kb 00:49:38.700 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 1612484kb -> 1389664kb 00:49:38.700 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 1389664kb -> 1011816kb 00:49:38.702 E [Wasmtime] Module --> Error loading module /data/user/0/crow.wasmtime.wasmline/cache/plugin.wasm: Compilation error: Code for function is too large 00:49:38.708 I [Wasmline] Load failure, because native load return false, file path is : /data/user/0/crow.wasmtime.wasmline/cache/plugin.wasm 00:49:38.790 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 1011816kb -> 727648kbArtifacts:
Wasmline-wasmline-core-plugin.wasm.zip
Wasm File Size (781.63 KB):
Error Detail:
Success state (after reducing content):
Analysis & Questions:
- Why does a relatively small Wasm file (under 1MB) trigger a "Code too large" error? It seems the Kotlin/Wasm compiler is generating a single function with extremely high complexity.
- The memory usage spike to 1.6GB suggests that Cranelift might be hitting a worst-case scenario in its e-graph optimization or register allocation phase.
- Are there any specific Cranelift settings to mitigate this, or is this a fundamental limit on function complexity regardless of the total
.wasmfile size?
crowforkotlin commented on issue #12229:
#11682 @cfallin
crowforkotlin edited issue #12229:
"Code for function is too large" error and 1.6GB Memory Spike during JIT compilation of a 781KB Kotlin/Wasm module
Environment:
- Wasmtime version: v40.0.0 (Release #12192)
- Host Machine (Build): Mac M2 (aarch64)
- Target Device (Runtime): Android (aarch64)
- Compiler: Kotlin/Wasm (WASI) via
compileProductionExecutableKotlinWasmWasiOptimize- Project Link: crowforkotlin/wasmline
- Source File: Plugin.kt
Description:
I am reporting aCompilation error: Code for function is too largethat occurs during the JIT loading phase on Android.Despite the generated
.wasmbinary being only 781.63 KB, the compilation fails when a single Kotlin function contains a large amount of logic/content. During the attempt to compile this module, I observed a massive RSS memory spike on the Android device, jumping from ~235 MB to over 1.6 GB in just 4 seconds, eventually resulting in the "function too large" error.Wasmtime Configuration:
The engine is configured specifically for Android compatibility to avoid signal conflicts with ART and minimize VSS overhead:/** * Creates and configures the Wasm Config object. * * Critical Android Settings: * 1. Signals-based traps DISABLED: To prevent conflicts with Android ART signal handlers (SIGSEGV). * 2. Memory Guard Size = 0: To prevent VSS (Virtual Set Size) OOM on 32-bit or limited devices. * 3. GC / Exceptions: Enabled for Kotlin/Wasm support. */ wasm_config_t *Engine::createConfig() { wasm_config_t *conf = wasm_config_new(); // Feature Flags for Kotlin/Wasm support wasmtime_config_wasm_gc_set(conf, true); wasmtime_config_wasm_function_references_set(conf, true); wasmtime_config_wasm_exceptions_set(conf, true); // Optimization: Disable SIMD if not strictly needed (improves compatibility) wasmtime_config_wasm_simd_set(conf, false); wasmtime_config_wasm_relaxed_simd_set(conf, false); // [CRITICAL] Disable signal handlers to avoid crash conflicts with Android Runtime (ART) wasmtime_config_signals_based_traps_set(conf, false); // [CRITICAL] Set guard pages to 0 to minimize Virtual Memory usage (prevents OOM) wasmtime_config_memory_guard_size_set(conf, 0); // Set max stack size (512KB is usually sufficient for mobile logic) wasmtime_config_max_wasm_stack_set(conf, 512 * 1024); // Compiler Optimization Strategy: Optimize for Speed and Binary Size wasmtime_config_cranelift_opt_level_set(conf, WASMTIME_OPT_LEVEL_SPEED_AND_SIZE); wasmtime_config_cranelift_debug_verifier_set(conf, false); return conf; }Reproduction Steps:
- Add a large amount of content/logic into the
wasmWasiMainfunction inPlugin.kt.- Build the wasm file:
./gradlew wasmtime-core:plugin:compileProductionExecutableKotlinWasmWasiOptimize.- The resulting
plugin.wasmis ~781 KB.- Load the module on an Android device using the configuration above.
Observed Behavior:
- Total Wasm Size: 781.63 KB (Small)
- Memory Usage: Spikes from 234MB to 1.6GB.
- Result:
Compilation error: Code for function is too large.Full Android Logs:
00:49:34.204 I [MotionEvent] ViewRootImpl windowName 'crow.wasmtime.wasmline/crow.mordecai.wasmline.MainActivity', { action=ACTION_DOWN, id[0]=0, pointerCount=1, eventTime=51746485, downTime=51746485, phoneEventTime=00:49:34.191 } moveCount:0 00:49:34.206 D getMiuiFreeformStackInfo mTmpFrames.miuiFreeFormStackInfo: null 00:49:34.217 I CreateGraphicsPipeline pipeline cache hit. elpased time: 0.32 ms. 00:49:34.218 E FrameInsert open fail: No such file or directory 00:49:34.230 I This is non sticky GC, maxfree is 33554432 minfree is 8388608 00:49:34.227 W type=1400 audit(0.0:2008818): avc: denied { getopt } for path="/dev/socket/usap_pool_primary" scontext=u:r:untrusted_app:s0:c190,c257,c512,c768 tcontext=u:r:zygote:s0 tclass=unix_stream_socket permissive=0 app=crow.wasmtime.wasmline 00:49:34.322 I [MotionEvent] ViewRootImpl windowName 'crow.wasmtime.wasmline/crow.mordecai.wasmline.MainActivity', { action=ACTION_UP, id[0]=0, pointerCount=1, eventTime=51746611, downTime=51746485, phoneEventTime=00:49:34.316 } moveCount:0 00:49:34.332 I ============================================== 00:49:34.337 I [Android] Wasm file : plugin.wasm || wasm file exits : true || wasm file path : /data/user/0/crow.wasmtime.wasmline/cache/plugin.wasm 00:49:34.338 I [Android] Cwasm cache file : plugin.cwasm || cache file exits : false || cache file path : /data/user/0/crow.wasmtime.wasmline/cache/plugin.cwasm 00:49:34.345 I [Wasmtime] Module --> Jit Compiling for /data/user/0/crow.wasmtime.wasmline/cache/plugin.wasm... 00:49:34.363 W type=1400 audit(0.0:2008819): avc: denied { read } for name="cpu.cfs_quota_us" dev="cgroup" ino=64 scontext=u:r:untrusted_app:s0:c190,c257,c512,c768 tcontext=u:object_r:cgroup:s0 tclass=file permissive=0 app=crow.wasmtime.wasmline 00:49:34.363 W type=1400 audit(0.0:2008820): avc: denied { read } for name="cpu.cfs_period_us" dev="cgroup" ino=65 scontext=u:r:untrusted_app:s0:c190,c257,c512,c768 tcontext=u:object_r:cgroup:s0 tclass=file permissive=0 app=crow.wasmtime.wasmline 00:49:34.363 W type=1400 audit(0.0:2008821): avc: denied { read } for name="cpu.cfs_quota_us" dev="cgroup" ino=10 scontext=u:r:untrusted_app:s0:c190,c257,c512,c768 tcontext=u:object_r:cgroup:s0 tclass=file permissive=0 app=crow.wasmtime.wasmline 00:49:34.363 W type=1400 audit(0.0:2008822): avc: denied { read } for name="cpu.cfs_period_us" dev="cgroup" ino=11 scontext=u:r:untrusted_app:s0:c190,c257,c512,c768 tcontext=u:object_r:cgroup:s0 tclass=file permissive=0 app=crow.wasmtime.wasmline 00:49:34.441 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 234952kb -> 342100kb 00:49:34.494 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 342100kb -> 428844kb 00:49:34.544 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 428844kb -> 541612kb 00:49:34.602 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 541612kb -> 657240kb 00:49:34.928 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 657240kb -> 860432kb 00:49:35.761 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 860432kb -> 959324kb 00:49:36.012 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 959324kb -> 1075892kb 00:49:36.184 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 1075892kb -> 1183820kb 00:49:37.835 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 1183820kb -> 1306888kb 00:49:38.482 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 1306888kb -> 1450064kb 00:49:38.639 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 1450064kb -> 1612484kb 00:49:38.700 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 1612484kb -> 1389664kb 00:49:38.700 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 1389664kb -> 1011816kb 00:49:38.702 E [Wasmtime] Module --> Error loading module /data/user/0/crow.wasmtime.wasmline/cache/plugin.wasm: Compilation error: Code for function is too large 00:49:38.708 I [Wasmline] Load failure, because native load return false, file path is : /data/user/0/crow.wasmtime.wasmline/cache/plugin.wasm 00:49:38.790 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 1011816kb -> 727648kbArtifacts:
Wasmline-wasmline-core-plugin.wasm.zip
Wasm File Size (781.63 KB):
Error Detail:
Success state (after reducing content):
Analysis & Questions:
- Why does a relatively small Wasm file (under 1MB) trigger a "Code too large" error? It seems the Kotlin/Wasm compiler is generating a single function with extremely high complexity.
- The memory usage spike to 1.6GB suggests that Cranelift might be hitting a worst-case scenario in its e-graph optimization or register allocation phase.
- Are there any specific Cranelift settings to mitigate this, or is this a fundamental limit on function complexity regardless of the total
.wasmfile size?
crowforkotlin commented on issue #12229:
<img width="1739" height="467" alt="Image" src="https://github.com/user-attachments/assets/54509e4b-a92b-4792-a6ac-13ac5f82e617" />
Wasmline-wasmline-core-plugin.wasm.zip
./wasmtime compile Wasmline-wasmline-core-plugin.wasm -o plugin.cwasm \
--target aarch64-linux-android \
-W gc=y \
-W function-references=y \
-W exceptions=y \
-W simd=n \
-W relaxed-simd=n \
-O static-memory-guard-size=0 \
-O dynamic-memory-guard-size=0 \
-O signals-based-traps=n \
-O opt-level=2 \
-C cranelift-debug-verifier=no
crowforkotlin edited a comment on issue #12229:
<img width="1739" height="467" alt="Image" src="https://github.com/user-attachments/assets/54509e4b-a92b-4792-a6ac-13ac5f82e617" />
Wasmline-wasmline-core-plugin.wasm.zip
./wasmtime compile Wasmline-wasmline-core-plugin.wasm -o plugin.cwasm \ --target aarch64-linux-android \ -W gc=y \ -W function-references=y \ -W exceptions=y \ -W simd=n \ -W relaxed-simd=n \ -O static-memory-guard-size=0 \ -O dynamic-memory-guard-size=0 \ -O signals-based-traps=n \ -O opt-level=2 \ -C cranelift-debug-verifier=no
crowforkotlin commented on issue #12229:
<img width="2860" height="676" alt="Image" src="https://github.com/user-attachments/assets/c1d23ee4-febb-4cfa-964a-b84c2fd47dd3" />
crowforkotlin edited issue #12229:
"Code for function is too large" error and 1.6GB Memory Spike during JIT compilation of a 781KB Kotlin/Wasm module
Environment:
- Wasmtime version: v40.0.0 (Release #12192)
- Host Machine (Build): Mac M2 (aarch64)
- Target Device (Runtime): Android (aarch64)
- Compiler: Kotlin/Wasm (WASI) via
compileProductionExecutableKotlinWasmWasiOptimize- Project Link: crowforkotlin/wasmline
- Source File: Plugin.kt
Description:
I am reporting aCompilation error: Code for function is too largethat occurs during the JIT loading phase on Android.Despite the generated
.wasmbinary being only 781.63 KB, the compilation fails when a single Kotlin function contains a large amount of logic/content. During the attempt to compile this module, I observed a massive RSS memory spike on the Android device, jumping from ~235 MB to over 1.6 GB in just 4 seconds, eventually resulting in the "function too large" error.Wasmtime Configuration:
The engine is configured specifically for Android compatibility to avoid signal conflicts with ART and minimize VSS overhead:/** * Creates and configures the Wasm Config object. * * Critical Android Settings: * 1. Signals-based traps DISABLED: To prevent conflicts with Android ART signal handlers (SIGSEGV). * 2. Memory Guard Size = 0: To prevent VSS (Virtual Set Size) OOM on 32-bit or limited devices. * 3. GC / Exceptions: Enabled for Kotlin/Wasm support. */ wasm_config_t *Engine::createConfig() { wasm_config_t *conf = wasm_config_new(); // Feature Flags for Kotlin/Wasm support wasmtime_config_wasm_gc_set(conf, true); wasmtime_config_wasm_function_references_set(conf, true); wasmtime_config_wasm_exceptions_set(conf, true); // Optimization: Disable SIMD if not strictly needed (improves compatibility) wasmtime_config_wasm_simd_set(conf, false); wasmtime_config_wasm_relaxed_simd_set(conf, false); // [CRITICAL] Disable signal handlers to avoid crash conflicts with Android Runtime (ART) wasmtime_config_signals_based_traps_set(conf, false); // [CRITICAL] Set guard pages to 0 to minimize Virtual Memory usage (prevents OOM) wasmtime_config_memory_guard_size_set(conf, 0); // Set max stack size (512KB is usually sufficient for mobile logic) wasmtime_config_max_wasm_stack_set(conf, 512 * 1024); // Compiler Optimization Strategy: Optimize for Speed and Binary Size wasmtime_config_cranelift_opt_level_set(conf, WASMTIME_OPT_LEVEL_SPEED_AND_SIZE); wasmtime_config_cranelift_debug_verifier_set(conf, false); return conf; }Reproduction Steps:
- Add a large amount of content/logic into the
wasmWasiMainfunction inPlugin.kt.- Build the wasm file:
./gradlew wasmtime-core:plugin:compileProductionExecutableKotlinWasmWasiOptimize.- The resulting
plugin.wasmis ~781 KB.- Load the module on an Android device using the configuration above.
Observed Behavior:
- Total Wasm Size: 781.63 KB (Small)
- Memory Usage: Spikes from 234MB to 1.6GB.
- Result:
Compilation error: Code for function is too large.Full Android Logs:
00:49:34.204 I [MotionEvent] ViewRootImpl windowName 'crow.wasmtime.wasmline/crow.mordecai.wasmline.MainActivity', { action=ACTION_DOWN, id[0]=0, pointerCount=1, eventTime=51746485, downTime=51746485, phoneEventTime=00:49:34.191 } moveCount:0 00:49:34.206 D getMiuiFreeformStackInfo mTmpFrames.miuiFreeFormStackInfo: null 00:49:34.217 I CreateGraphicsPipeline pipeline cache hit. elpased time: 0.32 ms. 00:49:34.218 E FrameInsert open fail: No such file or directory 00:49:34.230 I This is non sticky GC, maxfree is 33554432 minfree is 8388608 00:49:34.227 W type=1400 audit(0.0:2008818): avc: denied { getopt } for path="/dev/socket/usap_pool_primary" scontext=u:r:untrusted_app:s0:c190,c257,c512,c768 tcontext=u:r:zygote:s0 tclass=unix_stream_socket permissive=0 app=crow.wasmtime.wasmline 00:49:34.322 I [MotionEvent] ViewRootImpl windowName 'crow.wasmtime.wasmline/crow.mordecai.wasmline.MainActivity', { action=ACTION_UP, id[0]=0, pointerCount=1, eventTime=51746611, downTime=51746485, phoneEventTime=00:49:34.316 } moveCount:0 00:49:34.332 I ============================================== 00:49:34.337 I [Android] Wasm file : plugin.wasm || wasm file exits : true || wasm file path : /data/user/0/crow.wasmtime.wasmline/cache/plugin.wasm 00:49:34.338 I [Android] Cwasm cache file : plugin.cwasm || cache file exits : false || cache file path : /data/user/0/crow.wasmtime.wasmline/cache/plugin.cwasm 00:49:34.345 I [Wasmtime] Module --> Jit Compiling for /data/user/0/crow.wasmtime.wasmline/cache/plugin.wasm... 00:49:34.363 W type=1400 audit(0.0:2008819): avc: denied { read } for name="cpu.cfs_quota_us" dev="cgroup" ino=64 scontext=u:r:untrusted_app:s0:c190,c257,c512,c768 tcontext=u:object_r:cgroup:s0 tclass=file permissive=0 app=crow.wasmtime.wasmline 00:49:34.363 W type=1400 audit(0.0:2008820): avc: denied { read } for name="cpu.cfs_period_us" dev="cgroup" ino=65 scontext=u:r:untrusted_app:s0:c190,c257,c512,c768 tcontext=u:object_r:cgroup:s0 tclass=file permissive=0 app=crow.wasmtime.wasmline 00:49:34.363 W type=1400 audit(0.0:2008821): avc: denied { read } for name="cpu.cfs_quota_us" dev="cgroup" ino=10 scontext=u:r:untrusted_app:s0:c190,c257,c512,c768 tcontext=u:object_r:cgroup:s0 tclass=file permissive=0 app=crow.wasmtime.wasmline 00:49:34.363 W type=1400 audit(0.0:2008822): avc: denied { read } for name="cpu.cfs_period_us" dev="cgroup" ino=11 scontext=u:r:untrusted_app:s0:c190,c257,c512,c768 tcontext=u:object_r:cgroup:s0 tclass=file permissive=0 app=crow.wasmtime.wasmline 00:49:34.441 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 234952kb -> 342100kb 00:49:34.494 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 342100kb -> 428844kb 00:49:34.544 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 428844kb -> 541612kb 00:49:34.602 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 541612kb -> 657240kb 00:49:34.928 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 657240kb -> 860432kb 00:49:35.761 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 860432kb -> 959324kb 00:49:36.012 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 959324kb -> 1075892kb 00:49:36.184 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 1075892kb -> 1183820kb 00:49:37.835 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 1183820kb -> 1306888kb 00:49:38.482 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 1306888kb -> 1450064kb 00:49:38.639 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 1450064kb -> 1612484kb 00:49:38.700 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 1612484kb -> 1389664kb 00:49:38.700 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 1389664kb -> 1011816kb 00:49:38.702 E [Wasmtime] Module --> Error loading module /data/user/0/crow.wasmtime.wasmline/cache/plugin.wasm: Compilation error: Code for function is too large 00:49:38.708 I [Wasmline] Load failure, because native load return false, file path is : /data/user/0/crow.wasmtime.wasmline/cache/plugin.wasm 00:49:38.790 E ->pid:19641,processName:"crow.wasmtime.wasmline",Rss Memory Size Change 1011816kb -> 727648kbArtifacts:
Wasmline-wasmline-core-plugin.wasm.zip
Wasm File Size (781.63 KB):
Error Detail:
Success state (after reducing content):
Analysis & Questions:
- Why does a relatively small Wasm file (under 1MB) trigger a "Code too large" error? It seems the Kotlin/Wasm compiler is generating a single function with extremely high complexity.
- The memory usage spike to 1.6GB suggests that Cranelift might be hitting a worst-case scenario in its e-graph optimization or register allocation phase.
- Are there any specific Cranelift settings to mitigate this, or is this a fundamental limit on function complexity regardless of the total
.wasmfile size?
cfallin commented on issue #12229:
Just an FYI -- I am on PTO and then at a conference, so I will be able to investigate this starting Mon, Jan 12. (Thanks for the report!)
cfallin commented on issue #12229:
Actually, this was bothering me and I at least wanted to verify, so I double-checked with your provided Wasm module and the command
./wasmtime compile Wasmline-wasmline-core-plugin.wasm -o plugin.cwasm \ --target aarch64-linux-android \ -W gc=y \ -W function-references=y \ -W exceptions=y \ -W simd=n \ -W relaxed-simd=n \ -O static-memory-guard-size=0 \ -O dynamic-memory-guard-size=0 \ -O signals-based-traps=n \ -O opt-level=2 \ -C cranelift-debug-verifier=noHowever, both with a
wasmtimebuilt frommain(b5272a5f103053f5ada2a38d5302a8d1e2de442d) and from just before the 40.0 branch was cut (ac3358b1c9a1b4ca2794236966e47946d76606ea), running on macOS/aarch64 as you specify, I see the command complete just fine with minimal time taken, and definitely no "Code for function is too large" error -- e.g. fromtime,target/release/wasmtime compile Wasmline-wasmline-core-plugin.wasm -o -W 0.38s user 0.03s system 328% cpu 0.126 totalAm I missing some setting? I'm inclined to close as "cannot reproduce" if not but want to make sure I'm running exactly what you are.
crowforkotlin commented on issue #12229:
Actually, this was bothering me and I at least wanted to verify, so I double-checked with your provided Wasm module and the command实际上,这让我感到困扰,至少我想验证一下,所以我用你提供的 Wasm 模块和命令进行了双重检查。
./wasmtime compile Wasmline-wasmline-core-plugin.wasm -o plugin.cwasm \ --target aarch64-linux-android \ -W gc=y \ -W function-references=y \ -W exceptions=y \ -W simd=n \ -W relaxed-simd=n \ -O static-memory-guard-size=0 \ -O dynamic-memory-guard-size=0 \ -O signals-based-traps=n \ -O opt-level=2 \ -C cranelift-debug-verifier=noHowever, both with a
wasmtimebuilt frommain(b5272a5) and from just before the 40.0 branch was cut (ac3358b), running on macOS/aarch64 as you specify, I see the command complete just fine with minimal time taken, and definitely no "Code for function is too large" error -- e.g. fromtime,然而,无论使用从main()构建的wasmtime还是从 40.0 分支之前构建的wasmtime,在您指定的 macOS/aarch64 上运行时,命令都能正常完成,时间非常短,绝对没有“函数代码太长”的错误提示 - 例如,从time。
target/release/wasmtime compile Wasmline-wasmline-core-plugin.wasm -o -W 0.38s user 0.03s system 328% cpu 0.126 totalAm I missing some setting? I'm inclined to close as "cannot reproduce" if not but want to make sure I'm running exactly what you are.我是不是漏掉了什么设置?如果不是的话,我倾向于关闭为“无法重现”,但还是想确保我正在运行的是你所要求的。
The file download was incorrect. You used a WASM file generated from a small amount of code; it was my oversight for sending it to the wrong location. While the CWASM generated from that file works fine, using the larger WASM file (700KB+) to generate a CWASM triggers an exception. I’m sending you the latest version now for you to check.
crowforkotlin commented on issue #12229:
Actually, this was bothering me and I at least wanted to verify, so I double-checked with your provided Wasm module and the command实际上,这让我感到困扰,至少我想验证一下,所以我用你提供的 Wasm 模块和命令进行了双重检查。实际上,这让我感到困扰,至少我想验证一下,所以我用你提供的 Wasm 模块和命令进行了双重检查。
./wasmtime compile Wasmline-wasmline-core-plugin.wasm -o plugin.cwasm \ --target aarch64-linux-android \ -W gc=y \ -W function-references=y \ -W exceptions=y \ -W simd=n \ -W relaxed-simd=n \ -O static-memory-guard-size=0 \ -O dynamic-memory-guard-size=0 \ -O signals-based-traps=n \ -O opt-level=2 \ -C cranelift-debug-verifier=noHowever, both with a
wasmtimebuilt frommain(b5272a5) and from just before the 40.0 branch was cut (ac3358b), running on macOS/aarch64 as you specify, I see the command complete just fine with minimal time taken, and definitely no "Code for function is too large" error -- e.g. fromtime,然而,无论使用从main()构建的wasmtime还是从 40.0 分支之前构建的wasmtime,在您指定的 macOS/aarch64 上运行时,命令都能正常完成,时间非常短,绝对没有“函数代码太长”的错误提示 - 例如,从time。然而,无论使用从main()构建的wasmtime还是从 40.0 分支之前构建的wasmtime,在您指定的 macOS/aarch64 上运行时,命令都能正常完成,时间非常短,绝对没有“函数代码太长”的错误提示 - 例如,从time。
target/release/wasmtime compile Wasmline-wasmline-core-plugin.wasm -o -W 0.38s user 0.03s system 328% cpu 0.126 totalAm I missing some setting? I'm inclined to close as "cannot reproduce" if not but want to make sure I'm running exactly what you are.我是不是漏掉了什么设置?如果不是的话,我倾向于关闭为“无法重现”,但还是想确保我正在运行的是你所要求的。我是不是漏掉了什么设置?如果不是的话,我倾向于关闭为“无法重现”,但还是想确保我正在运行的是你所要求的。
The file download was incorrect. You used a WASM file generated from a small amount of code; it was my oversight for sending it to the wrong location. While the CWASM generated from that file works fine, using the larger WASM file (700KB+) to generate a CWASM triggers an exception. I’m sending you the latest version now for you to check.文件下载不正确。您使用了一个来自少量代码生成的 WASM 文件;这是我的疏忽,将它发送到错误的位置。虽然从该文件生成的 CWASM 文件正常工作,但使用更大的 WASM 文件(700KB+)生成 CWASM 会触发异常。我现在将为您发送最新版本。
The Kotlin/WASI build process uses Gradle tasks to generate WASM artifacts. The output ZIP includes 'kotlin' and 'optimize' folders, which seem to act as a size-optimization toggle similar to a release environment switch.[1] Yesterday, I inspected the WASM file with wasmtime-tools and discovered it contains over 22,000 global variables.[1] I realize this is not an ideal implementation, but I'm concerned that if a user implements extensive logic in the future, the resulting large file size might trigger exceptions during CWASM generation.
crowforkotlin deleted a comment on issue #12229:
Actually, this was bothering me and I at least wanted to verify, so I double-checked with your provided Wasm module and the command实际上,这让我感到困扰,至少我想验证一下,所以我用你提供的 Wasm 模块和命令进行了双重检查。
./wasmtime compile Wasmline-wasmline-core-plugin.wasm -o plugin.cwasm \ --target aarch64-linux-android \ -W gc=y \ -W function-references=y \ -W exceptions=y \ -W simd=n \ -W relaxed-simd=n \ -O static-memory-guard-size=0 \ -O dynamic-memory-guard-size=0 \ -O signals-based-traps=n \ -O opt-level=2 \ -C cranelift-debug-verifier=noHowever, both with a
wasmtimebuilt frommain(b5272a5) and from just before the 40.0 branch was cut (ac3358b), running on macOS/aarch64 as you specify, I see the command complete just fine with minimal time taken, and definitely no "Code for function is too large" error -- e.g. fromtime,然而,无论使用从main()构建的wasmtime还是从 40.0 分支之前构建的wasmtime,在您指定的 macOS/aarch64 上运行时,命令都能正常完成,时间非常短,绝对没有“函数代码太长”的错误提示 - 例如,从time。
target/release/wasmtime compile Wasmline-wasmline-core-plugin.wasm -o -W 0.38s user 0.03s system 328% cpu 0.126 totalAm I missing some setting? I'm inclined to close as "cannot reproduce" if not but want to make sure I'm running exactly what you are.我是不是漏掉了什么设置?如果不是的话,我倾向于关闭为“无法重现”,但还是想确保我正在运行的是你所要求的。
The file download was incorrect. You used a WASM file generated from a small amount of code; it was my oversight for sending it to the wrong location. While the CWASM generated from that file works fine, using the larger WASM file (700KB+) to generate a CWASM triggers an exception. I’m sending you the latest version now for you to check.
crowforkotlin edited a comment on issue #12229:
Actually, this was bothering me and I at least wanted to verify, so I double-checked with your provided Wasm module and the command实际上,这让我感到困扰,至少我想验证一下,所以我用你提供的 Wasm 模块和命令进行了双重检查。实际上,这让我感到困扰,至少我想验证一下,所以我用你提供的 Wasm 模块和命令进行了双重检查。
./wasmtime compile Wasmline-wasmline-core-plugin.wasm -o plugin.cwasm \ --target aarch64-linux-android \ -W gc=y \ -W function-references=y \ -W exceptions=y \ -W simd=n \ -W relaxed-simd=n \ -O static-memory-guard-size=0 \ -O dynamic-memory-guard-size=0 \ -O signals-based-traps=n \ -O opt-level=2 \ -C cranelift-debug-verifier=noHowever, both with a
wasmtimebuilt frommain(b5272a5) and from just before the 40.0 branch was cut (ac3358b), running on macOS/aarch64 as you specify, I see the command complete just fine with minimal time taken, and definitely no "Code for function is too large" error -- e.g. fromtime,然而,无论使用从main()构建的wasmtime还是从 40.0 分支之前构建的wasmtime,在您指定的 macOS/aarch64 上运行时,命令都能正常完成,时间非常短,绝对没有“函数代码太长”的错误提示 - 例如,从time。然而,无论使用从main()构建的wasmtime还是从 40.0 分支之前构建的wasmtime,在您指定的 macOS/aarch64 上运行时,命令都能正常完成,时间非常短,绝对没有“函数代码太长”的错误提示 - 例如,从time。
target/release/wasmtime compile Wasmline-wasmline-core-plugin.wasm -o -W 0.38s user 0.03s system 328% cpu 0.126 totalAm I missing some setting? I'm inclined to close as "cannot reproduce" if not but want to make sure I'm running exactly what you are.我是不是漏掉了什么设置?如果不是的话,我倾向于关闭为“无法重现”,但还是想确保我正在运行的是你所要求的。我是不是漏掉了什么设置?如果不是的话,我倾向于关闭为“无法重现”,但还是想确保我正在运行的是你所要求的。
The file download was incorrect. You used a WASM file generated from a small amount of code; it was my oversight for sending it to the wrong location. While the CWASM generated from that file works fine, using the larger WASM file (700KB+) to generate a CWASM triggers an exception. I’m sending you the latest version now for you to check.文件下载不正确。您使用了一个来自少量代码生成的 WASM 文件;这是我的疏忽,将它发送到错误的位置。虽然从该文件生成的 CWASM 文件正常工作,但使用更大的 WASM 文件(700KB+)生成 CWASM 会触发异常。我现在将为您发送最新版本。
The Kotlin/WASI build process uses Gradle tasks to generate WASM artifacts. The output ZIP includes 'kotlin' and 'optimize' folders, which seem to act as a size-optimization toggle similar to a release environment switch.[1] Yesterday, I inspected the WASM file with wasmtime-tools and discovered it contains over 22,000 global variables.[1] I realize this is not an ideal implementation, but I'm concerned that if a user implements extensive logic in the future, the resulting large file size might trigger exceptions during CWASM generation.
cfallin commented on issue #12229:
Thanks for that. A few stats from your
optimized/Wasmline-wasmline-core-plugin.wasmin your latest zip file, function 127 in the module:
- There are 4005519 (4 million) SSA values in the CLIF generated from that function body;
- There are 488081 (488k) basic blocks in that CLIF;
- There are 61796 Wasm bytecode operators in the function body;
- Those operators are distributed according to this histogram:
22433 global.get 16819 call 11238 i32.const 5612 global.set 5608 struct.new 20 local.get 12 struct.set 9 end 7 local.set 7 drop 6 ref.null 6 ref.is_null 6 local.tee 6 if 2 block 1 unreachable 1 try_table 1 throw_ref 1 br 1 array.new_dataSo, my thoughts:
- It's not surprising at all that feeding a CFG of 488k basic blocks into an optimizing compiler causes memory usage to grow, potentially to over a gigabyte. That's the nature of the beast: we build IR in memory and do passes over it. Just representing the IR itself, nevermind the analysis results, is going to take a large fraction of the usage you observed. Our optimizing compiler is not designed to have linear-time overhead (that would be impossible while still performing the kinds of optimizations we want to do), so any function this large is likely to blow up.
struct.newin particular is expanding to a lot of IR, because of its inlined fast-path. I don't think that's the wrong choice though -- it makes sense to make allocation fast for the usual program structure that has maybe a few allocation sites per function, rather than try to optimize for code size for the uncommon case of a single function with 5608struct.news (!).The immediate issue is that lowering runs out of VRegs, for which we allow only 2M (2^21), because of bitpacking for efficiency. We could in theory look at increasing that. But at least we are returning a clean error ("function too large") rather than panic'ing.
On a broader scale, I don't think Cranelift (or any optimizing backend) is going to meet your requirements/expectations if that is the code that you need to compile. I would thus recommend either:
- Using Winch, once it supports exceptions and GC (it currently does not support either); or
- Modifying your codegen to not produce such a function, e.g. by splitting it up, using table-driven setup, using initializer expressions for the globals if the graph is acyclic, or something else.
cc @fitzgen for any thoughts on
struct.new...
crowforkotlin commented on issue #12229:
- histogram
Hello, could you please share how you generated this histogram? I’d like to learn the method to help with my future debugging.
crowforkotlin deleted a comment on issue #12229:
- histogram
Hello, could you please share how you generated this histogram? I’d like to learn the method to help with my future debugging.
crowforkotlin commented on issue #12229:
Interestingly, after refining my Kotlin/Wasm (WASI) implementation, a 700KB Wasm file successfully compiled into a 28MB CWASM via JIT without triggering the "Code for function is too large" error. I suspect the Kotlin/Wasm compiler might be performing aggressive inlining, potentially collapsing numerous function implementations into a single entry point (e.g., the main function). If this aggressive inlining could be disabled or tuned, the issue might be resolved. For context, I have successfully tested the build and execution on an Android app.
Additionally, I have a question regarding Wasmtime: is there a hard limit of 2 million virtual registers? I’m trying to understand if this issue stems from the generation of approximately 4 million (4M) virtual register values.
I am planning to report this to the Kotlin team with the relevant details. While I am not entirely certain about the underlying mechanism, the issue is consistently reproducible.@cfallin
fitzgen commented on issue #12229:
struct.newin particular is expanding to a lot of IR, because of its inlined fast-path. I don't think that's the wrong choice though -- it makes sense to make allocation fast for the usual program structure that has maybe a few allocation sites per function, rather than try to optimize for code size for the uncommon case of a single function with 5608struct.news (!).We could possibly have a fuel counter for inlining the GC allocation fast path, and start deferring to out-of-line calls once the fuel is spent. This is nice because we already have out-of-line paths for allocation that we can reuse.
Except... the (default) DRC collector already does allocation out-of-line; it only does field initialization inline. I expect that it is this field initialization that is what is blowing up the CLIF size relative to the input Wasm size. The DRC collector's barriers are not small.
That said, we don't have any existing code path to do allocation and field initialization out of line, so adding such a path would be purely new maintenance burden.
Chris, did you check that this function doesn't blow any implementation limits? We should be able to compile anything within those limits in theory (even if it hits slow paths or whatever). Those limits should in theory be caught by
wasmparser's validation, but it is always possible we missed something... Looking into this a little more now.
fitzgen commented on issue #12229:
Chris, did you check that this function doesn't blow any implementation limits? We should be able to compile anything within those limits in theory (even if it hits slow paths or whatever). Those limits should in theory be caught by
wasmparser's validation, but it is always possible we missed something... Looking into this a little more now.I'm not seeing this break any limits, fwiw.
cfallin commented on issue #12229:
is there a hard limit of 2 million virtual registers? I’m trying to understand if this issue stems from the generation of approximately 4 million (4M) virtual register values.
That's correct. (And yes, this doesn't hit any implementation limits; there are "only" 61k Wasm instructions in the function, which is far below the ~7MB limit.)
I agree that "should be able to compile any function at all within limits without a 'too large' error" is a good goal to have, and is the ideal state to end up in. I also think that's a large project in general: not only the VReg limit (which we could raise today, at a ~few-percent compile time cost) but the blowup in the IR in general is likely going to lead to OOMs etc in worst-cases as long as we have a lot of inlined logic for some operators.
I wonder if it would make sense to have a "small operator footprint" mode where anything nontrivial (more than one or two CLIF operators) becomes a libcall? And then select this mode if the function is too large or contains too many of a certain kind of operator? I guess that's an eager form of your "fuel" idea; starting that way rather than switching partway through. Though fuel seems workable too.
For what it's worth, to ask the usual "what would SpiderMonkey do?" question, it seems that a tier-up heuristic that estimates compilation cost will effectively prevent optimizing compilation if the function body is "too large". So we're in kind of a unique position where the standard Wasm implementation limits are agreed upon by engines that can always fall back to baseline but we must always use the optimizing compiler on every function if we're set up to use it at all. So we're trying to solve a much harder problem (and one that the implementation limits are arguably not designed to guard against).
crowforkotlin edited a comment on issue #12229:
Actually, this was bothering me and I at least wanted to verify, so I double-checked with your provided Wasm module and the command
./wasmtime compile Wasmline-wasmline-core-plugin.wasm -o plugin.cwasm \ --target aarch64-linux-android \ -W gc=y \ -W function-references=y \ -W exceptions=y \ -W simd=n \ -W relaxed-simd=n \ -O static-memory-guard-size=0 \ -O dynamic-memory-guard-size=0 \ -O signals-based-traps=n \ -O opt-level=2 \ -C cranelift-debug-verifier=noHowever, both with a
wasmtimebuilt frommain(b5272a5) and from just before the 40.0 branch was cut (ac3358b), running on macOS/aarch64 as you specify, I see the command complete just fine with minimal time taken, and definitely no "Code for function is too large" error -- e.g. fromtime
target/release/wasmtime compile Wasmline-wasmline-core-plugin.wasm -o -W 0.38s user 0.03s system 328% cpu 0.126 totalAm I missing some setting? I'm inclined to close as "cannot reproduce" if not but want to make sure I'm running exactly what you are.
The file download was incorrect. You used a WASM file generated from a small amount of code; it was my oversight for sending it to the wrong location. While the CWASM generated from that file works fine, using the larger WASM file (700KB+) to generate a CWASM triggers an exception. I’m sending you the latest version now for you to check.
The Kotlin/WASI build process uses Gradle tasks to generate WASM artifacts. The output ZIP includes 'kotlin' and 'optimize' folders, which seem to act as a size-optimization toggle similar to a release environment switch.[1] Yesterday, I inspected the WASM file with wasmtime-tools and discovered it contains over 22,000 global variables.[1] I realize this is not an ideal implementation, but I'm concerned that if a user implements extensive logic in the future, the resulting large file size might trigger exceptions during CWASM generation.
alexcrichton added the wasm-proposal:gc label to Issue #12229.
crowforkotlin commented on issue #12229:
I have reported the upstream issue regarding the IR explosion to the Kotlin team (see KT-83497). While we await a fix on their end, I would like to highlight a critical bottleneck in Wasmtime's JIT compilation.
currently, the JIT behavior is unstable for our use case. I observing exponential memory growth when processing these large initialization functions, which rapidly leads to OOM errors. Consequently, I have been forced to migrate my project entirely to AOT compilation to maintain stability.
fitzgen commented on issue #12229:
@crowforkotlin The JIT and AOT compilation pipelines are identical, the only difference is that when JITing we then map the resulting code as executable into the current process, rather than writing it to disk or giving it to the caller as a
Vec<u8>. That is, if you observing exponential memory growth in one, then you should also see it in the other. Can you provide additional details for this new JIT vs AOT issue? Test case, steps to reproduce, etc. Thanks!
crowforkotlin commented on issue #12229:
@crowforkotlin The JIT and AOT compilation pipelines are identical, the only difference is that when JITing we then map the resulting code as executable into the current process, rather than writing it to disk or giving it to the caller as a
Vec<u8>. That is, if you observing exponential memory growth in one, then you should also see it in the other. Can you provide additional details for this new JIT vs AOT issue? Test case, steps to reproduce, etc. Thanks!Okay, I'll debug and output the details of AOT and JIT on Android when I have some free time in the next couple of days. If you need the sample, you might need to install Android Studio to run the wasmline-sample test cases on a real Android device or emulator. If needed, I can provide remote assistance or technical support to help with setup and running. The sample isn't very complete right now, mainly because I haven't had much time to implement some features. I've been busy researching the removal of features from the wasmtime library, cross-platform building, and stability testing on different platforms.
crowforkotlin edited a comment on issue #12229:
@crowforkotlin The JIT and AOT compilation pipelines are identical, the only difference is that when JITing we then map the resulting code as executable into the current process, rather than writing it to disk or giving it to the caller as a
Vec<u8>. That is, if you observing exponential memory growth in one, then you should also see it in the other. Can you provide additional details for this new JIT vs AOT issue? Test case, steps to reproduce, etc. Thanks!Okay, I'll debug and output the details of AOT and JIT on Android when I have some free time in the next couple of days. If you need the sample, you might need to install Android Studio to run the wasmline-sample test cases on a real Android device or emulator. If needed, I can provide remote assistance or technical support to help with setup and running. The sample isn't very complete right now, and building it might be tricky for users who aren't familiar with KMP., mainly because I haven't had much time to implement some features. I've been busy researching the removal of features from the wasmtime library, cross-platform building, and stability testing on different platforms.
cfallin commented on issue #12229:
@crowforkotlin thanks for noting this new issue -- for reference though I don't think we maintainers will be able to install Android Studio and build your project; it would be best if you can provide us with a Wasm or WAT and compilation flags such that this reproduces with
wasmtime compile. (For pure compilation issues, none of your other integration details should be relevant.)
crowforkotlin commented on issue #12229:
~/Dow/kotlin-2.3.2/p/optimized ❯ sh check.sh 18:04:12 Starting AOT compilation and resource monitoring... ---------------------------------------------------- Command being timed: "wasmtime compile ./wasmline-multiplatform-wasmline-sample-plugin.wasm -o ./plugin.cwasm --target aarch64-linux-android -W gc=y -W function-references=y -W exceptions=y -W simd=n -W relaxed-simd=n -O static-memory-guard-size=0 -O dynamic-memory-guard-size=0 -O signals-based-traps=n -O opt-level=2 -C cranelift-debug-verifier=n" User time (seconds): 0.00 System time (seconds): 0.00 Percent of CPU this job got: 0% Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.23 Average shared text size (kbytes): 0 Average unshared data size (kbytes): 0 Average stack size (kbytes): 0 Average total size (kbytes): 0 Maximum resident set size (kbytes): 5380 Average resident set size (kbytes): 0 Major (requiring I/O) page faults: 1411 Minor (reclaiming a frame) page faults: 0 Voluntary context switches: 0 Involuntary context switches: 0 Swaps: 0 File system inputs: 0 File system outputs: 0 Socket messages sent: 0 Socket messages received: 0 Signals delivered: 0 Page size (bytes): 65536 Exit status: 0 ---------------------------------------------------- Compilation successful: ./plugin.cwasm generated. ~/Dow/kotlin-2.3.2/p/optimized ❯ bat -P ./check.sh 18:04:14#!/bin/bash # Configuration SOURCE_WASM="./wasmline-multiplatform-wasmline-sample-plugin.wasm" OUTPUT_CWASM="./plugin.cwasm" TARGET="aarch64-linux-android" echo "Starting AOT compilation and resource monitoring..." echo "----------------------------------------------------" # Using /usr/bin/time for detailed resource usage report # The output will show Peak Memory (RSS) and CPU usage /usr/bin/time -v wasmtime compile "$SOURCE_WASM" -o "$OUTPUT_CWASM" \ --target "$TARGET" \ -W gc=y \ -W function-references=y \ -W exceptions=y \ -W simd=n \ -W relaxed-simd=n \ -O static-memory-guard-size=0 \ -O dynamic-memory-guard-size=0 \ -O signals-based-traps=n \ -O opt-level=2 \ -C cranelift-debug-verifier=n EXIT_CODE=$? echo "----------------------------------------------------" if [ $EXIT_CODE -eq 0 ]; then echo "Compilation successful: $OUTPUT_CWASM generated." else echo "Error: Compilation failed with exit code $EXIT_CODE." fihttps://github.com/user-attachments/assets/4a1f4326-4dc0-4952-a9a8-6cc2d4f5667b
- That's roughly the process. Honestly, it's not easy to observe this memory usage through the command line. However, it might take some time for me to refine the C++ sample, after which I can directly call the Wasmtime API to troubleshoot this issue. The specific location is here.
if (isJit) { LOGI("[Wasmtime] Module --> Jit Compiling for %s...", filePath.c_str()); error = wasmtime_module_new(engine, data.data(), data.size(), &module); } else { LOGI("[Wasmtime] Module --> Aot Deserializing for %s...", filePath.c_str()); error = wasmtime_module_deserialize(engine, data.data(), data.size(), &module); }
crowforkotlin edited a comment on issue #12229:
~/Dow/kotlin-2.3.2/p/optimized ❯ sh check.sh 18:04:12 Starting AOT compilation and resource monitoring... ---------------------------------------------------- Command being timed: "wasmtime compile ./wasmline-multiplatform-wasmline-sample-plugin.wasm -o ./plugin.cwasm --target aarch64-linux-android -W gc=y -W function-references=y -W exceptions=y -W simd=n -W relaxed-simd=n -O static-memory-guard-size=0 -O dynamic-memory-guard-size=0 -O signals-based-traps=n -O opt-level=2 -C cranelift-debug-verifier=n" User time (seconds): 0.00 System time (seconds): 0.00 Percent of CPU this job got: 0% Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.23 Average shared text size (kbytes): 0 Average unshared data size (kbytes): 0 Average stack size (kbytes): 0 Average total size (kbytes): 0 Maximum resident set size (kbytes): 5380 Average resident set size (kbytes): 0 Major (requiring I/O) page faults: 1411 Minor (reclaiming a frame) page faults: 0 Voluntary context switches: 0 Involuntary context switches: 0 Swaps: 0 File system inputs: 0 File system outputs: 0 Socket messages sent: 0 Socket messages received: 0 Signals delivered: 0 Page size (bytes): 65536 Exit status: 0 ---------------------------------------------------- Compilation successful: ./plugin.cwasm generated. ~/Dow/kotlin-2.3.2/p/optimized ❯ bat -P ./check.sh 18:04:14#!/bin/bash # Configuration SOURCE_WASM="./wasmline-multiplatform-wasmline-sample-plugin.wasm" OUTPUT_CWASM="./plugin.cwasm" TARGET="aarch64-linux-android" echo "Starting AOT compilation and resource monitoring..." echo "----------------------------------------------------" # Using /usr/bin/time for detailed resource usage report # The output will show Peak Memory (RSS) and CPU usage /usr/bin/time -v wasmtime compile "$SOURCE_WASM" -o "$OUTPUT_CWASM" \ --target "$TARGET" \ -W gc=y \ -W function-references=y \ -W exceptions=y \ -W simd=n \ -W relaxed-simd=n \ -O static-memory-guard-size=0 \ -O dynamic-memory-guard-size=0 \ -O signals-based-traps=n \ -O opt-level=2 \ -C cranelift-debug-verifier=n EXIT_CODE=$? echo "----------------------------------------------------" if [ $EXIT_CODE -eq 0 ]; then echo "Compilation successful: $OUTPUT_CWASM generated." else echo "Error: Compilation failed with exit code $EXIT_CODE." fihttps://github.com/user-attachments/assets/4a1f4326-4dc0-4952-a9a8-6cc2d4f5667b
- That's roughly the process. Honestly, it's not easy to observe this memory usage through the command line. However, it might take some time for me to refine the C++ sample, after which I can directly call the Wasmtime API to troubleshoot this issue. The specific location is here.
if (isJit) { LOGI("[Wasmtime] Module --> Jit Compiling for %s...", filePath.c_str()); error = wasmtime_module_new(engine, data.data(), data.size(), &module); } else { LOGI("[Wasmtime] Module --> Aot Deserializing for %s...", filePath.c_str()); error = wasmtime_module_deserialize(engine, data.data(), data.size(), &module); }
Last updated: Jan 09 2026 at 13:15 UTC