summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlon Zakai <azakai@google.com>2024-12-30 13:14:59 -0800
committerGitHub <noreply@github.com>2024-12-30 13:14:59 -0800
commit5611a528b2a3bcd37aedd863c6058f99095c9a35 (patch)
tree8b387e92b32756029137f2bc7fe56b29e7de6a13
parent6ddacde514af7cc546caa07fede4baa3e429c33c (diff)
downloadbinaryen-5611a528b2a3bcd37aedd863c6058f99095c9a35.tar.gz
binaryen-5611a528b2a3bcd37aedd863c6058f99095c9a35.tar.bz2
binaryen-5611a528b2a3bcd37aedd863c6058f99095c9a35.zip
[wasm-reduce] Reduce struct.new arguments away when possible (#7118)HEADmain
If all the fields of a struct.new are defaultable, see if replacing it with a struct.new_default preserves the behavior, and reduce that way if so. Also add a missing --closed-world to the --remove-unused-types invocation. Without that, it was erroring and not working, which I noticed when testing this. The test also checks that.
-rw-r--r--src/tools/wasm-reduce.cpp21
-rw-r--r--test/reduce/gc.wast28
-rw-r--r--test/reduce/gc.wast.txt16
3 files changed, 64 insertions, 1 deletions
diff --git a/src/tools/wasm-reduce.cpp b/src/tools/wasm-reduce.cpp
index 3b9229462..6613c8899 100644
--- a/src/tools/wasm-reduce.cpp
+++ b/src/tools/wasm-reduce.cpp
@@ -304,7 +304,7 @@ struct Reducer
"--simplify-globals",
"--simplify-locals --vacuum",
"--strip",
- "--remove-unused-types",
+ "--remove-unused-types --closed-world",
"--vacuum"};
auto oldSize = file_size(working);
bool more = true;
@@ -627,6 +627,25 @@ struct Reducer
// children (which would recreate the current state).
return;
}
+ } else if (auto* structNew = curr->dynCast<StructNew>()) {
+ // If all the fields are defaultable, try to replace this with a
+ // struct.new_with_default.
+ if (!structNew->isWithDefault() && structNew->type != Type::unreachable) {
+ auto& fields = structNew->type.getHeapType().getStruct().fields;
+ if (std::all_of(fields.begin(), fields.end(), [&](auto& field) {
+ return field.type.isDefaultable();
+ })) {
+ ExpressionList operands(getModule()->allocator);
+ operands.swap(structNew->operands);
+ assert(structNew->isWithDefault());
+ if (tryToReplaceCurrent(structNew)) {
+ return;
+ } else {
+ structNew->operands.swap(operands);
+ assert(!structNew->isWithDefault());
+ }
+ }
+ }
}
// Finally, try to replace with a child.
for (auto* child : ChildIterator(curr)) {
diff --git a/test/reduce/gc.wast b/test/reduce/gc.wast
new file mode 100644
index 000000000..98d1cd075
--- /dev/null
+++ b/test/reduce/gc.wast
@@ -0,0 +1,28 @@
+(module
+ (rec
+ (type $A (struct (field (mut i32)) (field funcref)))
+ ;; This type can be optimized away.
+ (type $unused (struct))
+ )
+
+ (global $A (ref null $A) (struct.new $A
+ ;; These particular values are not used, and can be removed, leaving the
+ ;; struct.new as struct.new_default.
+ (i32.const 0)
+ (ref.func $use-global)
+ ))
+
+ (func $use-global (export "use-global") (result i32)
+ ;; This function stores 42 in the global struct, then reads and returns
+ ;; that. We don't manage to optimize away anything in this function, which
+ ;; only serves to keep alive the type and the global for the above testing.
+ (struct.set $A 0
+ (global.get $A)
+ (i32.const 42)
+ )
+ (struct.get $A 0
+ (global.get $A)
+ )
+ )
+)
+
diff --git a/test/reduce/gc.wast.txt b/test/reduce/gc.wast.txt
new file mode 100644
index 000000000..3af5287ce
--- /dev/null
+++ b/test/reduce/gc.wast.txt
@@ -0,0 +1,16 @@
+(module
+ (type $0 (struct (field (mut i32)) (field funcref)))
+ (type $1 (func (result i32)))
+ (global $global$0 (ref null $0) (struct.new_default $0))
+ (export "use-global" (func $0))
+ (func $0 (result i32)
+ (struct.set $0 0
+ (global.get $global$0)
+ (i32.const 42)
+ )
+ (struct.get $0 0
+ (global.get $global$0)
+ )
+ )
+)
+