Skip to content

Commit

Permalink
[wasm-reduce] Reduce struct.new arguments away when possible (#7118)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
kripken authored Dec 30, 2024
1 parent 6ddacde commit 5611a52
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 1 deletion.
21 changes: 20 additions & 1 deletion src/tools/wasm-reduce.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)) {
Expand Down
28 changes: 28 additions & 0 deletions test/reduce/gc.wast
Original file line number Diff line number Diff line change
@@ -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)
)
)
)

16 changes: 16 additions & 0 deletions test/reduce/gc.wast.txt
Original file line number Diff line number Diff line change
@@ -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)
)
)
)

0 comments on commit 5611a52

Please sign in to comment.