Skip to content

Add directize-skip-type-mismatch pass argument#8475

Open
thiblahute wants to merge 1 commit intoWebAssembly:mainfrom
thiblahute:directize-skip-type-mismatch
Open

Add directize-skip-type-mismatch pass argument#8475
thiblahute wants to merge 1 commit intoWebAssembly:mainfrom
thiblahute:directize-skip-type-mismatch

Conversation

@thiblahute
Copy link

When fpcast-emu will run later in the pipeline, directize should not replace type-mismatched call_indirect with unreachable. The new --pass-arg=directize-skip-type-mismatch tells directize to return Unknown instead of Trap for these cases, leaving the call_indirect intact for fpcast-emu to handle.

Without this, emscripten's pass pipeline (-O2 then fpcast-emu) causes directize to kill type-mismatched calls before fpcast-emu can fix them, resulting in RuntimeError: unreachable at runtime. This affects any C code using -sEMULATE_FUNCTION_POINTER_CASTS that calls functions through cast function pointers (e.g. GLib's g_list_free_full casting GDestroyNotify to GFunc).

When fpcast-emu will run later in the pipeline, directize should not
replace type-mismatched call_indirect with unreachable. The new
--pass-arg=directize-skip-type-mismatch tells directize to return
Unknown instead of Trap for these cases, leaving the call_indirect
intact for fpcast-emu to handle.

Without this, emscripten's pass pipeline (-O2 then fpcast-emu) causes
directize to kill type-mismatched calls before fpcast-emu can fix them,
resulting in RuntimeError: unreachable at runtime. This affects any C
code using -sEMULATE_FUNCTION_POINTER_CASTS that calls functions through
cast function pointers (e.g. GLib's g_list_free_full casting
GDestroyNotify to GFunc).
@kripken
Copy link
Member

kripken commented Mar 17, 2026

Interesting problem, thanks for the PR!

Thinking about this, two other options might be

  • The user can disable Directize entirely, if they know they need this kind of fixup, using --skip-pass=directize. That would avoid adding a flag for this rare case, and the cost of disabling the entire pass might be low?
  • The user can reorder the operations, first letting fpcast-emu handle these calls, and then running the optimizer and Directize. I'm not actually sure how this would affect performance. (edit: I mean that this might actually help)

In both cases we might need to measure the impact.

Thoughts?

thiblahute added a commit to thiblahute/emscripten that referenced this pull request Mar 18, 2026
Move the `--fpcast-emu` binaryen pass earlier in the pass pipeline so it
runs before -O2. Previously, directize (part of -O2) would see
type-mismatched call_indirect entries and replace them with unreachable
before fpcast-emu had a chance to rewrite the table entries with
matching types. This caused crashes for common C idioms like calling a
1-arg function through a 2-arg function pointer (e.g. GLib's
`g_list_free_full` pattern).

This reordering also improves performance: on a real-world GStreamer
test binary, the reordered pipeline produces a 1.8%
smaller wasm output (5.30MB vs 5.40MB) and seems to run almost ~2x faster
(not sure why).

Also move the fpcast-emu block outside `if optimizing:` so it runs
even without -O2 in the link flags as this is not an optimization
pass in practice.

Also add a regression test.

See WebAssembly/binaryen#8475
thiblahute added a commit to thiblahute/emscripten that referenced this pull request Mar 18, 2026
Move the `--fpcast-emu` binaryen pass earlier in the pass pipeline so it
runs before -O2. Previously, directize (part of -O2) would see
type-mismatched call_indirect entries and replace them with unreachable
before fpcast-emu had a chance to rewrite the table entries with
matching types. This caused crashes for common C idioms like calling a
1-arg function through a 2-arg function pointer (e.g. GLib's
`g_list_free_full` pattern).

This reordering also improves performance: on a real-world GStreamer
test binary, the reordered pipeline produces a 1.8%
smaller wasm output (5.30MB vs 5.40MB) and seems to run almost ~2x faster
(not sure why).

Also move the fpcast-emu block outside `if optimizing:` so it runs
even without -O2 in the link flags as this is not an optimization
pass in practice.

Also add a regression test.

See WebAssembly/binaryen#8475
thiblahute added a commit to thiblahute/emscripten that referenced this pull request Mar 18, 2026
Move the `--fpcast-emu` binaryen pass earlier in the pass pipeline so it
runs before -O2. Previously, directize (part of -O2) would see
type-mismatched call_indirect entries and replace them with unreachable
before fpcast-emu had a chance to rewrite the table entries with
matching types. This caused crashes for common C idioms like calling a
1-arg function through a 2-arg function pointer (e.g. GLib's
`g_list_free_full` pattern).

This reordering also improves performance: on a real-world GStreamer
test binary, the reordered pipeline produces a 1.8%
smaller wasm output (5.30MB vs 5.40MB) and seems to run almost ~2x faster
(not sure why).

Also move the fpcast-emu block outside `if optimizing:` so it runs
even without -O2 in the link flags as this is not an optimization
pass in practice.

Also add a regression test.

See WebAssembly/binaryen#8475
kripken pushed a commit to emscripten-core/emscripten that referenced this pull request Mar 18, 2026
Move EMULATE_FUNCTION_POINTER_CASTS's pass earlier in the pipeline so it
runs before -O2. Previously, directize (part of -O2) would see
type-mismatched call_indirect entries and replace them with unreachable
before fpcast-emu had a chance to rewrite the table entries with
matching types. This caused crashes for common C idioms like calling a
1-arg function through a 2-arg function pointer (e.g. GLib's
`g_list_free_full` pattern).

This reordering also improves performance, on a real-world GStreamer
test binary, the reordered pipeline produces a 1.8% smaller wasm output
(5.30MB vs 5.40MB) and seems to run almost ~2x faster.

Also move the fpcast-emu block outside `if optimizing:` so it runs even
without -O2 in the link flags as this is not an optimization pass in
practice.

Also add a regression test.

See WebAssembly/binaryen#8475
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants