Description
Given the following code:
(module
(type $0 (func))
(type $1 (func (param i32 i32) (result i32)))
(type $2 (func (result i32)))
(type $3 (func (param i32) (result i32)))
(import "External" "external_function" (func $external_function (type $0)))
(func $_start (type $3) (param $0 i32) (result i32)
(local $1 i32) (local $2 i32)
i32.const 0
i32.load
local.set $2
local.get $1
i32.const 0
call $foo
local.set $1
local.get $1
local.get $2
i32.gt_u
if (result i32) ;; label = @1
call $external_function
i32.const 0
else
i32.const 1
end)
(func $foo (type $1) (param $0 i32) (param $1 i32) (result i32)
i32.const 0
i32.const 0
i32.store
i32.const 0)
(memory $0 258 258)
(export "_start" (func $_start)))
The wasm-opt (16dbac1) can eliminate the dead true branch by -all -O2
but cannot by -all -O3
.
Analysis
O2 produces the following if statement before inlining-optimizing
:
(i32.gt_u
(call $foo) ==> return const 0, and it has side effect
(local.get $0) ;; ==> which is (i32.load $0 (i32.const 0) )
)
After inline, the condition is deduced to be zero, thus the true branch is deleted (call $external_function
is in the true branch). (Of course it preserves the side-effect statements in the branch)
O3 produces the following if statement before inlining-optimizing
:
(i32.lt_u
(i32.load $0 (i32.const 0) )
(call $foo)
)
After inline, the condition is transformed to
(i32.lt_u
(i32.load $0 (i32.const 0) )
(block (result i32)
(i32.store $0
(i32.const 0)
(i32.const 0)
)
(i32.const 0)
)
)
As you can see, the condition is almost the same as O2 produced before inlining-optimizing
. However, wasm-opt
failed to deduced the condition to be zero after inline -- It should be.
The only difference is the O2 uses gt_u
and the O3 uses lt_u
. Maybe the reorder of the comparison operands affects the optimization? So weird. (I am not sure about that).
Overall, I think there is a missed optimization in the updateAfterInlining
-- wasm-opt may not handle constant propagation properly after inlining. (Similar to the #7454 )