WebAssembly / binaryen

Optimizer and compiler/toolchain library for WebAssembly
Apache License 2.0
7.5k stars 745 forks source link

wasm-opt with --ignore-implicit-traps -Os breaks #2824

Open schellingb opened 4 years ago

schellingb commented 4 years ago

Optimizing the attached wasm file and loading it via the attached html file through a webserver results in memory access out of bounds errors being thrown in Chrome and Firefox.

This is the breaking command line: wasm-opt --ignore-implicit-traps -Os si.org.wasm -o si.wasm

Interestingly all these command lines result in a file that runs fine: wasm-opt -Os si.org.wasm -o si.wasm wasm-opt --ignore-implicit-traps -O3 si.org.wasm -o si.wasm wasm-opt --ignore-implicit-traps --duplicate-function-elimination --memory-packing --ssa-nomerge --dce --remove-unused-brs --remove-unused-names --optimize-instructions --pick-load-signs --precompute --code-pushing --simplify-locals-nostructure --vacuum --reorder-locals --remove-unused-brs --coalesce-locals --simplify-locals --vacuum --reorder-locals --coalesce-locals --reorder-locals --vacuum --code-folding --merge-blocks --remove-unused-brs --remove-unused-names --merge-blocks --precompute --optimize-instructions --rse --vacuum --dae-optimizing --inlining-optimizing --duplicate-function-elimination --duplicate-import-elimination --simplify-globals-optimizing --remove-unused-module-elements --directize --generate-stack-ir --optimize-stack-ir si.org.wasm -o si.wasm

The last one seems especially interesting. Adding -d to the command line shows a very similar process for -Os and the manual list of passes, with all [PassRunner] log entries matching up, yet after a million lines of debug output, things start to diverge. In the end -Os produces a 1540 bytes smaller (but broken) result.

The source is the imgui example from the Sokol headers sample repository (by @floooh, zlib license): https://github.com/floooh/sokol-samples/blob/master/html5/imgui-emsc.cc

Built not using Emscripten but directly via clang with an optimization level -O1. Building the wasm file in clang without any optimizations makes it not breakable with wasm-opt. Building it in clang with any -O1, -O2, -O3 or -Os results with a broken wasm after this wasm-opt.

Building through Emscripten and using its library_browser.js results in a fairly different wasm file which is not breakable with any wasm-opt options that I tried.

I tried building with -g and it ended up crashing in random places (changing one line of C code made it crash in a completely different place) so I have no idea what causes this.

si-wasm-opt-iit-Os-break.zip (I'm using wasm-opt version 92)

kripken commented 4 years ago

It might be interesting to use wasm-reduce to get a small testcase from this, to easily see exactly what's going on. Another option is to run the instrumentation passes --log-execution etc. to narrow it down.

However, --ignore-implict-traps is expected to have such issues. There appears to be an implicit trap here that can't be ignored. For example, if it does if (p < 1024) read(p) then that flag will assume the read never traps, which means it might decide to do it unconditionally.