WebAssembly / binaryen

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

wasm-opt crash: "Fatal: Unsupported instruction for Flatten: BrOn" #7053

Open osa1 opened 3 weeks ago

osa1 commented 3 weeks ago

wasm-opt crashes when I try to optimize the Wasm file in the zip file (attached):

$ wasm-opt -g --all-features --closed-world --traps-never-happen --type-unfinalizing -Os --type-ssa --gufa -Os --type-merging -O4 --type-finalizing test.wasm -o test.opt.wasm
Fatal: Unsupported instruction for Flatten: BrOn
Fatal: Unsupported instruction for Flatten: BrOn

I'm trying with the current main branch (320867a7c):

$ wasm-opt --version
wasm-opt version 120 (version_117-528-g320867a7c)

Update: it also happens with wasm-opt version 119 (93883fde36ac158fd415dcd6dbd387dcfd928d3c).

test.wasm.zip

kripken commented 3 weeks ago

Looks like this is https://github.com/WebAssembly/binaryen/issues/6989#issuecomment-2397723891 - we don't yet support -O4 on all instructions, including BrOn.

Was there a specific reason you wanted that instead of -O3?

osa1 commented 2 weeks ago

Was there a specific reason you wanted that instead of -O3?

-O3 is unable to hoist the field load inside the loop in this function:

(func $foo1 (;299;) (param $var0 (ref $OneByteString)) (result i64)
  (local $var1 i64)
  (local $var2 i64)
  (local $var3 (ref $Array<WasmI8>))
  loop $label0  
    local.get $var1
    local.get $var0
    struct.get $OneByteString $field2
    local.tee $var3                 
    array.len
    i64.extend_i32_u
    i64.lt_s
    if
      local.get $var3
      local.get $var1
      i32.wrap_i64
      array.get_u $Array<WasmI8>
      i64.extend_i32_u
      i64.eqz
      if
        local.get $var3            
        i32.const 1000000000
        array.get_u $Array<WasmI8> 
        i64.extend_i32_u
        local.get $var2
        i64.add
        local.set $var2
      end
      local.get $var1
      i64.const 1
      i64.add
      local.set $var1
      br $label0
    end
  end $label0
  local.get $var2)

Here var0 never changes, and the field field2 is immutable, so the struct.get could be hoisted outside of the loop and the field would be loaded only once, instead of in each iteration. I wanted to see if -O4 does this.

kripken commented 2 weeks ago

Ah, then -O4 does not do that either. We do have an licm pass, but it is disabled by default in all modes (since it tends to increase code size, and VMs do licm themselves anyhow). But you can experiment with it by adding --licm