binaryen icon indicating copy to clipboard operation
binaryen copied to clipboard

RemoveUnusedBrs fails to optimize control flow based dead code

Open xuruiyang2002 opened this issue 8 months ago • 2 comments

Given the following code:

(module
  (import "env" "dead" (func $dead))
  (func $_start (param $0 i32)
    i32.const 0
    i32.const 0
    call $main)
  (func $main (param $0 i32) (param $1 i32)
    block  ;; label = @1
      i32.const 0
      i32.const 0
      i32.store
      i32.const 0
      i32.load
      local.set $1
      i32.const 1
      local.set $0
      local.get $1
      br_if 0 (;@1;)
      i32.const 0
      i32.const 0
      i32.store
      i32.const 1
      local.set $0
      local.get $0
      br_if 0 (;@1;)
      i32.const 0
      local.set $0
    end
    local.get $0
    if  ;; label = @1
      unreachable
    end
    call $dead
    call $dead
    call $dead
    )
  (memory 1)
  (export "_start" (func $_start)))

wasm-opt version: 2b989ae

O2 can eliminate the dead code because it identifies the statements of if unreachable:

 (func $_start (param $0 i32)
  (i32.store
   (i32.const 0)
   (i32.const 0)
  )
  (if
   (i32.eqz
    (i32.load
     (i32.const 0)
    )
   )
   (then
    (i32.store
     (i32.const 0)
     (i32.const 0)
    )
   )
  )
  (unreachable)
 )

O3 cannot:

 (func $_start (param $0 i32)
  (if
   (block $block (result i32)
    (i32.store
     (i32.const 0)
     (i32.const 0)
    )
    (drop
     (br_if $block
      (i32.const 1)
      (i32.load
       (i32.const 0)
      )
     )
    )
    (i32.store
     (i32.const 0)
     (i32.const 0)
    )
    (i32.const 1)
   )
   (then
    (unreachable)
   )
  )
  (call $dead)
  (call $dead)
  (call $dead)
 )

xuruiyang2002 avatar Jun 12 '25 07:06 xuruiyang2002

A missed optimization opportunity here

(block $block (result i32)
 ..
 (drop
  (br_if $block
   (value)
   (condition)
  )
 )
 ...
 (value)
)

=>

(block $block (result i32)
 ..
 (drop
  (condition)
 )
...
 (value)
)

Currently, optimization is limited because it only targets the last two instructions—specifically, assuming the last instruction is a value and the second-to-last is a drop containing a br_if with a value and condition

https://github.com/WebAssembly/binaryen/blob/2b989ae0e1d724c532ce7022b2a7085347b25d82/src/passes/RemoveUnusedBrs.cpp#L1293-L1299

xuruiyang2002 avatar Jun 12 '25 07:06 xuruiyang2002

A DFS traversal could solve? But, is the optimization worth it for this particular pattern? What do you think?

xuruiyang2002 avatar Jun 12 '25 07:06 xuruiyang2002