First reported upstream to rust-lang/rust - https://github.com/rust-lang/rust/issues/58548 - I've managed to reduce this a little more from the Rust issue report. It looks like during "WebAssembly Instruction Selection" on the `run_test` function a bug is generated. All the relevant files for this should be in https://gist.github.com/alexcrichton/f0098f0505ce1476a2bef944b03ff06e, notably: * lib.rs - https://gist.github.com/alexcrichton/f0098f0505ce1476a2bef944b03ff06e#file-lib-rs - the original Rust source used to generate this buggy program * foo.ll - https://gist.github.com/alexcrichton/f0098f0505ce1476a2bef944b03ff06e#file-foo-ll - the LLVM IR generated by rustc with LTO (to avoid references to the Rust standard library). * foo.js - https://gist.github.com/alexcrichton/f0098f0505ce1476a2bef944b03ff06e#file-foo-js - auxiliary JS for node.js to run the output wasm binary * build.sh - https://gist.github.com/alexcrichton/f0098f0505ce1476a2bef944b03ff06e#file-build-sh - the invocations of `llc` and `wasm-ld` used to produce a WebAssembly binary. Using `-opt-bisect-limit` I've found that the pass "WebAssembly Instruction Selection on function (run_test)" looks to be causing the issue here (different behavior in optimized vs non-optimized mode). This pass changes the final output wasm with this diff: https://gist.github.com/alexcrichton/5603d6a0e59d638e9ac74751d51f4cad but I unfortunately can't really make heads or tails of where the bug is. If any more info is needed from our end please just let me know!
Reduced reproduce case: define i32 @test(i32 %a) nounwind { %b = and i32 %a, 1 %c = icmp ne i32 %b, 0 %d = select i1 %c, i32 23, i32 0 ret i32 %d } Pre-selection SDAG: t0: ch = EntryToken t2: i32 = WebAssemblyISD::ARGUMENT TargetConstant:i32<0> t4: i32 = and t2, Constant:i32<1> t9: i32 = select t4, Constant:i32<23>, Constant:i32<0> t10: ch = WebAssemblyISD::RETURN t0, t9 Post-selection SDAG: t8: i32 = CONST_I32 TargetConstant:i32<23> t5: i32 = CONST_I32 TargetConstant:i32<0> t2: i32 = ARGUMENT_i32 TargetConstant:i32<0> t9: i32 = SELECT_I32 t8, t5, t2 t0: ch = EntryToken t10: ch = RETURN_I32 t9, t0 We can see that the "and" node has been lost. This is due to these two patterns in WebAssemblyInstrInteger.td: // The legalizer inserts an unnecessary `and 1` to make input conform // to getBooleanContents, which we can lower away. def : Pat<(select (i32 (and I32:$cond, 1)), I32:$lhs, I32:$rhs), (SELECT_I32 I32:$lhs, I32:$rhs, I32:$cond)>; def : Pat<(select (i32 (and I32:$cond, 1)), I64:$lhs, I64:$rhs), (SELECT_I64 I64:$lhs, I64:$rhs, I32:$cond)>; That comment isn't right :)
https://reviews.llvm.org/D58575
Thanks for the quick fix Nikita!
FYI, I filed https://bugs.llvm.org/show_bug.cgi?id=40847 to request merging this fix to the 8.0 branch.