-
Notifications
You must be signed in to change notification settings - Fork 837
LocalCSE: Optimize idempotent functions #8383
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
Changes from all commits
ae128ee
634e41f
ba578e8
3563a82
8b02e69
86d2770
cbd55d6
ef597d3
967d497
c03fc30
963524c
e683ccb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| ;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. | ||
|
|
||
| ;; RUN: wasm-opt -all %s -S -o - | filecheck %s | ||
| ;; RUN: wasm-opt -all --roundtrip %s -S -o - | filecheck %s | ||
|
|
||
| ;; Test text and binary handling of @binaryen.idempotent. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Worth testing the call site annotation as well?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, it is actually a little tedious to check it (need to be careful to check the right one, and remember the value), so I was thinking to just not optimize it, but forgot to add a comment with a TODO. I added one now.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Makes sense for CSE, but we could still test for the round tripping, right?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh right, added. |
||
|
|
||
| (module | ||
| ;; CHECK: (type $0 (func)) | ||
|
|
||
| ;; CHECK: (@binaryen.idempotent) | ||
| ;; CHECK-NEXT: (func $func-annotation (type $0) | ||
| ;; CHECK-NEXT: (drop | ||
| ;; CHECK-NEXT: (i32.const 0) | ||
| ;; CHECK-NEXT: ) | ||
| ;; CHECK-NEXT: ) | ||
| (@binaryen.idempotent) | ||
| (func $func-annotation | ||
| (drop | ||
| (i32.const 0) | ||
| ) | ||
| ) | ||
|
|
||
| ;; CHECK: (func $call (type $0) | ||
| ;; CHECK-NEXT: (@binaryen.idempotent) | ||
| ;; CHECK-NEXT: (call $call) | ||
| ;; CHECK-NEXT: ) | ||
| (func $call | ||
| (@binaryen.idempotent) | ||
| (call $call) | ||
| ) | ||
| ) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,279 @@ | ||
| ;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. | ||
| ;; NOTE: This test was ported using port_passes_tests_to_lit.py and could be cleaned up. | ||
|
|
||
| ;; RUN: foreach %s %t wasm-opt --local-cse -all -S -o - | filecheck %s | ||
|
|
||
| (module | ||
| ;; CHECK: (type $0 (func)) | ||
|
|
||
| ;; CHECK: (type $1 (func (param i32) (result i32))) | ||
|
|
||
| ;; CHECK: (import "a" "b" (func $import (type $0))) | ||
| (import "a" "b" (func $import)) | ||
|
|
||
| ;; CHECK: (global $mutable (mut i32) (i32.const 42)) | ||
| (global $mutable (mut i32) (i32.const 42)) | ||
|
|
||
| ;; CHECK: (global $immutable i32 (i32.const 1337)) | ||
| (global $immutable i32 (i32.const 1337)) | ||
|
|
||
| ;; CHECK: (@binaryen.idempotent) | ||
| ;; CHECK-NEXT: (func $idempotent (type $1) (param $0 i32) (result i32) | ||
| ;; CHECK-NEXT: (call $import) | ||
| ;; CHECK-NEXT: (i32.const 42) | ||
| ;; CHECK-NEXT: ) | ||
| (@binaryen.idempotent) | ||
| (func $idempotent (param i32) (result i32) | ||
| ;; This function has side effects, but is marked idempotent. | ||
| (call $import) | ||
| (i32.const 42) | ||
| ) | ||
|
|
||
| ;; CHECK: (func $potent (type $1) (param $0 i32) (result i32) | ||
| ;; CHECK-NEXT: (call $import) | ||
| ;; CHECK-NEXT: (i32.const 1337) | ||
| ;; CHECK-NEXT: ) | ||
| (func $potent (param i32) (result i32) | ||
| ;; As above, but not marked as idempotent. | ||
| (call $import) | ||
| (i32.const 1337) | ||
| ) | ||
|
|
||
| ;; CHECK: (func $yes (type $0) | ||
| ;; CHECK-NEXT: (local $0 i32) | ||
| ;; CHECK-NEXT: (drop | ||
| ;; CHECK-NEXT: (local.tee $0 | ||
| ;; CHECK-NEXT: (call $idempotent | ||
| ;; CHECK-NEXT: (i32.const 10) | ||
| ;; CHECK-NEXT: ) | ||
| ;; CHECK-NEXT: ) | ||
| ;; CHECK-NEXT: ) | ||
| ;; CHECK-NEXT: (drop | ||
| ;; CHECK-NEXT: (local.get $0) | ||
| ;; CHECK-NEXT: ) | ||
| ;; CHECK-NEXT: ) | ||
| (func $yes | ||
| ;; We can optimize here. | ||
| (drop | ||
| (call $idempotent | ||
| (i32.const 10) | ||
| ) | ||
| ) | ||
| (drop | ||
| (call $idempotent | ||
| (i32.const 10) | ||
| ) | ||
| ) | ||
| ) | ||
|
|
||
| ;; CHECK: (func $no (type $0) | ||
| ;; CHECK-NEXT: (drop | ||
| ;; CHECK-NEXT: (call $potent | ||
| ;; CHECK-NEXT: (i32.const 10) | ||
| ;; CHECK-NEXT: ) | ||
| ;; CHECK-NEXT: ) | ||
| ;; CHECK-NEXT: (drop | ||
| ;; CHECK-NEXT: (call $potent | ||
| ;; CHECK-NEXT: (i32.const 10) | ||
| ;; CHECK-NEXT: ) | ||
| ;; CHECK-NEXT: ) | ||
| ;; CHECK-NEXT: ) | ||
| (func $no | ||
| ;; Without idempotency we cannot optimize. | ||
| (drop | ||
| (call $potent | ||
| (i32.const 10) | ||
| ) | ||
| ) | ||
| (drop | ||
| (call $potent | ||
| (i32.const 10) | ||
| ) | ||
| ) | ||
| ) | ||
|
|
||
| ;; CHECK: (func $different-input (type $0) | ||
| ;; CHECK-NEXT: (drop | ||
| ;; CHECK-NEXT: (call $idempotent | ||
| ;; CHECK-NEXT: (i32.const 10) | ||
| ;; CHECK-NEXT: ) | ||
| ;; CHECK-NEXT: ) | ||
| ;; CHECK-NEXT: (drop | ||
| ;; CHECK-NEXT: (call $idempotent | ||
| ;; CHECK-NEXT: (i32.const 20) | ||
| ;; CHECK-NEXT: ) | ||
| ;; CHECK-NEXT: ) | ||
| ;; CHECK-NEXT: ) | ||
| (func $different-input | ||
| ;; We cannot optimize here. | ||
| (drop | ||
| (call $idempotent | ||
| (i32.const 10) | ||
| ) | ||
| ) | ||
| (drop | ||
| (call $idempotent | ||
| (i32.const 20) | ||
| ) | ||
| ) | ||
| ) | ||
|
|
||
| ;; CHECK: (func $idem-effects (type $0) | ||
| ;; CHECK-NEXT: (drop | ||
| ;; CHECK-NEXT: (call $idempotent | ||
| ;; CHECK-NEXT: (global.get $mutable) | ||
| ;; CHECK-NEXT: ) | ||
| ;; CHECK-NEXT: ) | ||
| ;; CHECK-NEXT: (drop | ||
| ;; CHECK-NEXT: (call $idempotent | ||
| ;; CHECK-NEXT: (global.get $mutable) | ||
| ;; CHECK-NEXT: ) | ||
| ;; CHECK-NEXT: ) | ||
| ;; CHECK-NEXT: ) | ||
| (func $idem-effects | ||
| ;; An idempotent function still has effects on the first call. Those effects | ||
| ;; can invalidate the global.get here. | ||
| (drop | ||
| (call $idempotent | ||
| (global.get $mutable) | ||
| ) | ||
| ) | ||
| (drop | ||
| (call $idempotent | ||
| (global.get $mutable) | ||
| ) | ||
| ) | ||
| ) | ||
|
|
||
| ;; CHECK: (func $idem-effects-immutable (type $0) | ||
| ;; CHECK-NEXT: (local $0 i32) | ||
| ;; CHECK-NEXT: (drop | ||
| ;; CHECK-NEXT: (local.tee $0 | ||
| ;; CHECK-NEXT: (call $idempotent | ||
| ;; CHECK-NEXT: (global.get $immutable) | ||
| ;; CHECK-NEXT: ) | ||
| ;; CHECK-NEXT: ) | ||
| ;; CHECK-NEXT: ) | ||
| ;; CHECK-NEXT: (drop | ||
| ;; CHECK-NEXT: (local.get $0) | ||
| ;; CHECK-NEXT: ) | ||
| ;; CHECK-NEXT: ) | ||
| (func $idem-effects-immutable | ||
| ;; But here we read an immutable value, so we can optimize. | ||
| (drop | ||
| (call $idempotent | ||
| (global.get $immutable) | ||
| ) | ||
| ) | ||
| (drop | ||
| (call $idempotent | ||
| (global.get $immutable) | ||
| ) | ||
| ) | ||
| ) | ||
|
|
||
| ;; CHECK: (func $idem-effects-interact (type $0) | ||
| ;; CHECK-NEXT: (drop | ||
| ;; CHECK-NEXT: (i32.add | ||
| ;; CHECK-NEXT: (global.get $mutable) | ||
| ;; CHECK-NEXT: (global.get $mutable) | ||
| ;; CHECK-NEXT: ) | ||
| ;; CHECK-NEXT: ) | ||
| ;; CHECK-NEXT: (drop | ||
| ;; CHECK-NEXT: (call $idempotent | ||
| ;; CHECK-NEXT: (global.get $immutable) | ||
| ;; CHECK-NEXT: ) | ||
| ;; CHECK-NEXT: ) | ||
| ;; CHECK-NEXT: (drop | ||
| ;; CHECK-NEXT: (i32.add | ||
| ;; CHECK-NEXT: (global.get $mutable) | ||
| ;; CHECK-NEXT: (global.get $mutable) | ||
| ;; CHECK-NEXT: ) | ||
| ;; CHECK-NEXT: ) | ||
| ;; CHECK-NEXT: (drop | ||
| ;; CHECK-NEXT: (call $idempotent | ||
| ;; CHECK-NEXT: (global.get $immutable) | ||
| ;; CHECK-NEXT: ) | ||
| ;; CHECK-NEXT: ) | ||
| ;; CHECK-NEXT: ) | ||
| (func $idem-effects-interact | ||
| ;; An idempotent function still has effects on the first call. That | ||
| ;; prevents optimizing the i32.add, as the first call might alter $mutable. | ||
| (drop | ||
| (i32.add | ||
| (global.get $mutable) | ||
| (global.get $mutable) | ||
| ) | ||
| ) | ||
| (drop | ||
| (call $idempotent | ||
| (global.get $immutable) | ||
| ) | ||
| ) | ||
| (drop | ||
| (i32.add | ||
| (global.get $mutable) | ||
| (global.get $mutable) | ||
| ) | ||
| ) | ||
| (drop | ||
| (call $idempotent | ||
| (global.get $immutable) | ||
| ) | ||
| ) | ||
| ) | ||
|
|
||
| ;; CHECK: (func $idem-effects-interact-2 (type $0) | ||
| ;; CHECK-NEXT: (drop | ||
| ;; CHECK-NEXT: (call $idempotent | ||
| ;; CHECK-NEXT: (global.get $immutable) | ||
| ;; CHECK-NEXT: ) | ||
| ;; CHECK-NEXT: ) | ||
| ;; CHECK-NEXT: (drop | ||
| ;; CHECK-NEXT: (i32.add | ||
| ;; CHECK-NEXT: (global.get $mutable) | ||
| ;; CHECK-NEXT: (global.get $mutable) | ||
| ;; CHECK-NEXT: ) | ||
| ;; CHECK-NEXT: ) | ||
| ;; CHECK-NEXT: (drop | ||
| ;; CHECK-NEXT: (call $idempotent | ||
| ;; CHECK-NEXT: (global.get $immutable) | ||
| ;; CHECK-NEXT: ) | ||
| ;; CHECK-NEXT: ) | ||
| ;; CHECK-NEXT: (drop | ||
| ;; CHECK-NEXT: (i32.add | ||
| ;; CHECK-NEXT: (global.get $mutable) | ||
| ;; CHECK-NEXT: (global.get $mutable) | ||
| ;; CHECK-NEXT: ) | ||
| ;; CHECK-NEXT: ) | ||
| ;; CHECK-NEXT: ) | ||
| (func $idem-effects-interact-2 | ||
| ;; As above, but interleaved differently. The first i32.add is now between | ||
| ;; the two idempotent calls. Here we could optimize, but do not atm, as the | ||
| ;; mutable reads invalidate the first call (only the reverse is true, but | ||
| ;; our check is symmetrical atm), and the second call - which we fail to | ||
| ;; optimize out - invalidates the later mutable reads. TODO | ||
| (drop | ||
| (call $idempotent | ||
| (global.get $immutable) | ||
| ) | ||
| ) | ||
| (drop | ||
| (i32.add | ||
| (global.get $mutable) | ||
| (global.get $mutable) | ||
| ) | ||
| ) | ||
| (drop | ||
| (call $idempotent | ||
| (global.get $immutable) | ||
| ) | ||
| ) | ||
| (drop | ||
| (i32.add | ||
| (global.get $mutable) | ||
| (global.get $mutable) | ||
| ) | ||
| ) | ||
| ) | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't we support annotations on the call sites as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(see above, sorry for the confusion)