Skip to content
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
1 change: 0 additions & 1 deletion scripts/fuzz_opt.py
Original file line number Diff line number Diff line change
Expand Up @@ -2393,7 +2393,6 @@ def write_commands(commands, filename):
("--abstract-type-refining",),
("--cfp",),
("--cfp-reftest",),
("--gsi",),
("--type-ssa",),
("--type-merging",)}

Expand Down
18 changes: 15 additions & 3 deletions src/passes/GlobalStructInference.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
* limitations under the License.
*/

//
// GlobalStructInference: Analyze struct usage globally, in particular, structs
// created (perhaps only) in globals.
//
// Finds types which are only created in assignments to immutable globals. For
// such types we can replace a struct.get with a global.get when there is a
Expand Down Expand Up @@ -47,7 +50,7 @@
//
// This also optimizes some related things - reads from structs created in
// globals - that benefit from the infrastructure here (see unnesting, below),
// even without this type-based approach.
// even without this type-based approach, and even in open world.
//
// TODO: Only do the case with a select when shrinkLevel == 0?
//
Expand Down Expand Up @@ -81,17 +84,24 @@ struct GlobalStructInference : public Pass {
//
// We will remove unoptimizable types from here, so in practice, if a type is
// optimizable it will have an entry here, and not if not.
//
// This is filled in when in closed world. In open world, we cannot do such
// type-based inference, and this remains empty.
std::unordered_map<HeapType, std::vector<Name>> typeGlobals;

void run(Module* module) override {
if (!module->features.hasGC()) {
return;
}

if (!getPassOptions().closedWorld) {
Fatal() << "GSI requires --closed-world";
if (getPassOptions().closedWorld) {
analyzeClosedWorld(module);
}

optimize(module);
}

void analyzeClosedWorld(Module* module) {
// First, find all the information we need. We need to know which struct
// types are created in functions, because we will not be able to optimize
// those.
Expand Down Expand Up @@ -213,7 +223,9 @@ struct GlobalStructInference : public Pass {
for (auto& [type, globals] : typeGlobals) {
std::sort(globals.begin(), globals.end());
}
}

void optimize(Module* module) {
// We are looking for the case where we can pick between two values using a
// single comparison. More than two values, or more than a single
// comparison, lead to tradeoffs that may not be worth it.
Expand Down
4 changes: 3 additions & 1 deletion src/passes/pass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -769,7 +769,9 @@ void PassRunner::addDefaultGlobalOptimizationPrePasses() {
} else {
addIfNoDWARFIssues("cfp");
}
addIfNoDWARFIssues("gsi");
}
addIfNoDWARFIssues("gsi");
Copy link
Contributor

Choose a reason for hiding this comment

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

dbc: Will this also be enabled in -Os (from the options.optimizeLevel >= 2) condition on line 749 it seems this is for -O2 and -O3)?

At least the part of gsi that will preform this global.get + struct.get optimization will lead to lower code size and would therefore make sense to run in -Os.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, this runs in -Os. -Os is equivalent to -O2 plus setting the shrink level to 1,

static constexpr const int OS_OPTIMIZE_LEVEL = 2;

Copy link
Contributor

Choose a reason for hiding this comment

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

Great 👍

if (options.closedWorld) {
addIfNoDWARFIssues("abstract-type-refining");
addIfNoDWARFIssues("unsubtyping");
}
Expand Down
17 changes: 15 additions & 2 deletions test/lit/passes/gsi-nontype.wast
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.
;; RUN: foreach %s %t wasm-opt --gsi -all --closed-world -S -o - | filecheck %s

;; Non-type-based optimizations in --gsi
;; RUN: foreach %s %t wasm-opt --gsi -all -S -o - | filecheck %s
;; RUN: foreach %s %t wasm-opt --gsi --closed-world -all -S -o - | filecheck %s

;; Non-type-based optimizations in --gsi, which work in open world too.

;; Create an immutable vtable in an immutable global, which we can optimize
;; with.
(module
;; CHECK: (type $vtable (struct (field funcref)))
(type $vtable (struct funcref))


;; CHECK: (type $1 (func))

;; CHECK: (import "a" "b" (global $imported funcref))
Expand Down Expand Up @@ -43,6 +46,7 @@
;; CHECK: (type $vtable (struct (field funcref)))
(type $vtable (struct funcref))


;; CHECK: (type $1 (func))

;; CHECK: (import "a" "b" (global $imported funcref))
Expand Down Expand Up @@ -79,6 +83,7 @@
;; CHECK: (type $vtable (struct (field funcref)))
(type $vtable (struct funcref))


;; CHECK: (type $1 (func))

;; CHECK: (import "a" "b" (global $imported funcref))
Expand Down Expand Up @@ -109,11 +114,13 @@
;; CHECK: (type $table (struct (field anyref)))
(type $table (struct anyref))


;; CHECK: (type $1 (func))

;; CHECK: (import "a" "b" (global $imported funcref))
(import "a" "b" (global $imported funcref))


;; CHECK: (global $table.unnested.0 (ref (exact $table)) (struct.new_default $table))

;; CHECK: (global $table (ref $table) (struct.new $table
Expand Down Expand Up @@ -149,6 +156,7 @@
(type $desc (sub (describes $struct (struct))))
)


;; CHECK: (type $2 (func))

;; CHECK: (import "a" "b" (global $imported (ref (exact $desc))))
Expand Down Expand Up @@ -183,6 +191,7 @@
;; CHECK: (type $vtable (struct (field funcref)))
(type $vtable (struct funcref))


;; CHECK: (type $1 (func))

;; CHECK: (import "a" "b" (global $imported (ref $vtable)))
Expand All @@ -209,6 +218,7 @@
;; CHECK: (type $A (struct (field i8)))
(type $A (struct (field i8)))


;; CHECK: (type $1 (func))

;; CHECK: (global $global (ref $A) (struct.new $A
Expand Down Expand Up @@ -246,6 +256,7 @@
(type $A.desc (sub (describes $A (struct))))
)


;; CHECK: (type $2 (func (result (ref $A.desc))))

;; CHECK: (global $global (ref $A) (struct.new_default $A
Expand Down Expand Up @@ -279,6 +290,8 @@
(type $inner (sub (struct (field (ref $func)))))
)



;; CHECK: (type $3 (func (result anyref)))

;; CHECK: (global $global.unnested.0 (ref (exact $inner)) (struct.new $inner
Expand Down
8 changes: 7 additions & 1 deletion test/unit/test_passes.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,20 @@ def test_O2(self):
'signature-refining',
'gto',
'cfp',
'gsi',
]
for pass_ in CLOSED_WORLD_PASSES:
if closed_world:
self.assertIn(pass_, passes)
else:
self.assertNotIn(pass_, passes)

# some passes run in open world too
OPEN_WORLD_PASSES = [
'gsi',
]
for pass_ in OPEN_WORLD_PASSES:
self.assertIn(pass_, passes)

def test_O3_O1(self):
# When we run something like -O3 -O1 we should run -O3 followed by -O1
# (and not -O1 -O1, which would be the case if the last commandline
Expand Down
Loading