Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MergeBlocks: Optimize all dropped blocks #6984

Merged
merged 12 commits into from
Oct 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/passes/MergeBlocks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,15 @@ struct MergeBlocks
optimizeBlock(curr, getModule(), getPassOptions(), branchInfo);
}

void visitDrop(Drop* curr) {
Copy link
Member

@aheejin aheejin Oct 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the difference between the cases handled by this and this code in optimizeBlock?

if (auto* drop = list[i]->dynCast<Drop>()) {

Can't all cases be handled by this new visitDrop?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code in optimizeBlock runs on the children of a block, and we do need to run that more than once, unlike visitDrop: Each time we make a signficant change to a block, it's possible that merging with children of the block is possible, so we call optimizeBlock on such occasions.

On the other hand, for things whose parent isn't a block, the new visitDrop is enough, since the parent can't be merged with it, so we can just do that once.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I was completely wrong here! You were right, we can remove this code. It looks like changes in the parent never lead to new improvements for the child, so we can just do the visitDrop part once.

I'll do some fuzzing to verify this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, actually that was wrong, it turns out. There is at least one case where we do end up improving a child based on the parents, which I added a testcase for now (after reverting the last commit). What seems to happen is that after we optimize the parent there, we then look at the child and see that it can be removed with its name. (This was not noticed in the test suite since we run --remove-unused-names.)

Logically I still think it makes sense that we only need the call from visitDrop, but that would require changes to the pass, and I'm not sure it's worth a large refactoring.

if (auto* block = curr->value->dynCast<Block>()) {
if (optimizeDroppedBlock(
curr, block, *getModule(), getPassOptions(), branchInfo)) {
replaceCurrent(block);
}
}
}

// given
// (curr
// (block=child
Expand Down
11 changes: 5 additions & 6 deletions test/lit/passes/merge-blocks.wast
Original file line number Diff line number Diff line change
Expand Up @@ -405,17 +405,16 @@
)

;; CHECK: (func $toplevel (type $4)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block $label (result i32)
;; CHECK-NEXT: (br $label
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: )
;; CHECK-NEXT: (block $label
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: )
;; CHECK-NEXT: (br $label)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $toplevel
;; Test we can remove a block value even when the drop is at the toplevel of
;; a function. This is not done yet TODO
;; a function.
(drop
(block $label (result i32)
(br $label
Expand Down
28 changes: 28 additions & 0 deletions test/lit/passes/merge-blocks_names.wast
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited.
;; RUN: wasm-opt %s --merge-blocks -all -S -o - | filecheck %s
;;
;; Similar to merge-blocks.wast, but without --remove-unused-names. This tests
;; the pass entirely by itself.

(module
;; CHECK: (func $nested-dropped-blocks (type $0)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $nested-dropped-blocks
;; Fully removing unneeded blocks here requires multiple operations to happen.
;; Specifically, we remove all the outer blocks first, and only then get to
;; the inner named block, which we can then infer is not needed either.
(block
(drop
(block (result i32)
(block $named (result i32)
(i32.const 42)
)
)
)
)
)
)

7 changes: 1 addition & 6 deletions test/lit/passes/monomorphize-drop.wast
Original file line number Diff line number Diff line change
Expand Up @@ -672,12 +672,7 @@

;; CAREFUL: (func $return-normal_4 (type $1)
;; CAREFUL-NEXT: (drop
;; CAREFUL-NEXT: (block
;; CAREFUL-NEXT: (drop
;; CAREFUL-NEXT: (call $import)
;; CAREFUL-NEXT: )
;; CAREFUL-NEXT: (return)
;; CAREFUL-NEXT: )
;; CAREFUL-NEXT: (call $import)
;; CAREFUL-NEXT: )
;; CAREFUL-NEXT: )

Expand Down
10 changes: 3 additions & 7 deletions test/passes/Oz_fuzz-exec_all-features.txt
Original file line number Diff line number Diff line change
Expand Up @@ -183,14 +183,10 @@
(nop)
)
(func $br-on_non_null-2 (type $void_func)
(drop
(block
(call $log
(i32.const 1)
)
(unreachable)
)
(call $log
(i32.const 1)
)
(unreachable)
)
(func $cast-on-func (type $void_func)
(call $log
Expand Down
29 changes: 10 additions & 19 deletions test/passes/merge-blocks.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@
)
)
(func $drop-block-br
(drop
(block $x (result i32)
(br $x
(i32.const 1)
)
(block $x
(drop
(i32.const 1)
)
(br $x)
(drop
(i32.const 0)
)
)
Expand Down Expand Up @@ -77,15 +78,9 @@
)
)
(func $drop-block-squared-iloop
(drop
(block $label$0 (result i32)
(drop
(block $label$1
(loop $label$2
(br $label$2)
)
)
)
(block $label$0
(loop $label$2
(br $label$2)
)
)
)
Expand All @@ -109,11 +104,7 @@
(func $loop-block-drop-block-return
(loop $label$4
(block $label$5
(drop
(block $label$6 (result i32)
(return)
)
)
(return)
)
)
)
Expand Down
64 changes: 20 additions & 44 deletions test/passes/remove-unused-names_merge-blocks_all-features.txt
Original file line number Diff line number Diff line change
Expand Up @@ -286,15 +286,11 @@
(i32.const 30)
)
(drop
(block
(drop
(i32.const 10)
)
(i32.add
(unreachable)
(i32.const 20)
)
)
(i32.const 10)
)
(i32.add
(unreachable)
(i32.const 20)
)
(drop
(i32.const 20)
Expand Down Expand Up @@ -822,40 +818,30 @@
)
(func $drop-unreachable (type $2) (result i32)
(local $0 i32)
(drop
(block (result i32)
(unreachable)
)
)
(unreachable)
(unreachable)
)
(func $concrete_finale_in_unreachable (type $5) (result f64)
(drop
(block (result f64)
(unreachable)
(block
(unreachable)
(drop
(f64.const 6.322092475576799e-96)
)
)
(f64.const -1)
)
(func $dont-move-unreachable (type $3)
(loop $label$0
(br $label$0)
(drop
(block (result i32)
(br $label$0)
(i32.const 1)
)
(i32.const 1)
)
)
)
(func $dont-move-unreachable-last (type $3)
(loop $label$0
(drop
(block (result i32)
(call $dont-move-unreachable-last)
(br $label$0)
)
)
(call $dont-move-unreachable-last)
(br $label$0)
)
)
(func $move-around-unreachable-in-middle (type $3)
Expand All @@ -876,16 +862,10 @@
)
(func $drop-unreachable-block-with-concrete-final (type $3)
(drop
(block (result i32)
(drop
(block
(drop
(return)
)
)
)
(i32.const -452)
)
(return)
)
(drop
(i32.const -452)
)
)
(func $merging-with-unreachable-in-middle (type $2) (result i32)
Expand All @@ -901,13 +881,9 @@
)
(func $remove-br-after-unreachable (type $3)
(block $label$9
(drop
(block
(block
(return)
(br $label$9)
)
)
(block
(return)
(br $label$9)
)
)
)
Expand Down
Loading