Skip to content

Commit 01e0eea

Browse files
authored
[GC] Add a TypeRefiningGUFA pass (#7433)
This variation of TypeRefining uses GUFA to determine what types to refine struct fields to. GUFA does a (slow) whole-program analysis which can infer things the normal pass cannot, e.g. refinements that contain cycles through things like locals or globals. This is mainly a proof of concept, as it is pretty slow to compute GUFA just for this, and while I see improvements on real-world code, they are minor. If we find that the benefits here are worth it, a larger refactoring could do this optimization in the existing GUFA pass (which already does the computation of the graph anyhow).
1 parent 862aeb9 commit 01e0eea

File tree

8 files changed

+374
-2
lines changed

8 files changed

+374
-2
lines changed

scripts/fuzz_opt.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2005,6 +2005,7 @@ def write_commands(commands, filename):
20052005
("--tuple-optimization",),
20062006
("--type-finalizing",),
20072007
("--type-refining",),
2008+
("--type-refining-gufa",),
20082009
("--type-merging",),
20092010
("--type-ssa",),
20102011
("--type-unfinalizing",),
@@ -2014,6 +2015,7 @@ def write_commands(commands, filename):
20142015

20152016
# TODO: Fix these passes so that they still work without --closed-world!
20162017
requires_closed_world = {("--type-refining",),
2018+
("--type-refining-gufa",),
20172019
("--signature-pruning",),
20182020
("--signature-refining",),
20192021
("--gto",),

src/passes/TypeRefining.cpp

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,15 @@
1818
// Apply more specific subtypes to type fields where possible, where all the
1919
// writes to that field in the entire program allow doing so.
2020
//
21+
// TODO: handle arrays and not just structs.
22+
//
23+
// The GUFA variant of this uses GUFA to infer types, which performs a (slow)
24+
// whole-program inference, rather than just scan struct/array operations by
25+
// themselves.
26+
//
2127

2228
#include "ir/lubs.h"
29+
#include "ir/possible-contents.h"
2330
#include "ir/struct-utils.h"
2431
#include "ir/type-updating.h"
2532
#include "ir/utils.h"
@@ -104,8 +111,16 @@ struct TypeRefining : public Pass {
104111
// Only affects GC type declarations and struct.gets.
105112
bool requiresNonNullableLocalFixups() override { return false; }
106113

114+
bool gufa;
115+
116+
TypeRefining(bool gufa) : gufa(gufa) {}
117+
118+
// The final information we inferred about struct usage, that we then use to
119+
// optimize.
107120
StructUtils::StructValuesMap<FieldInfo> finalInfos;
108121

122+
using Propagator = StructUtils::TypeHierarchyPropagator<FieldInfo>;
123+
109124
void run(Module* module) override {
110125
if (!module->features.hasGC()) {
111126
return;
@@ -115,6 +130,20 @@ struct TypeRefining : public Pass {
115130
Fatal() << "TypeRefining requires --closed-world";
116131
}
117132

133+
Propagator propagator(*module);
134+
135+
// Compute our main data structure, finalInfos, either normally or using
136+
// GUFA.
137+
if (!gufa) {
138+
computeFinalInfos(module, propagator);
139+
} else {
140+
computeFinalInfosGUFA(module, propagator);
141+
}
142+
143+
useFinalInfos(module, propagator);
144+
}
145+
146+
void computeFinalInfos(Module* module, Propagator& propagator) {
118147
// Find and analyze struct operations inside each function.
119148
StructUtils::FunctionStructValuesMap<FieldInfo> functionNewInfos(*module),
120149
functionSetGetInfos(*module);
@@ -132,14 +161,39 @@ struct TypeRefining : public Pass {
132161
// able to contain that type. Propagate things written using set to subtypes
133162
// as well, as the reference might be to a supertype if the field is present
134163
// there.
135-
StructUtils::TypeHierarchyPropagator<FieldInfo> propagator(*module);
136164
propagator.propagateToSuperTypes(combinedNewInfos);
137165
propagator.propagateToSuperAndSubTypes(combinedSetGetInfos);
138166

139167
// Combine everything together.
140168
combinedNewInfos.combineInto(finalInfos);
141169
combinedSetGetInfos.combineInto(finalInfos);
170+
}
171+
172+
void computeFinalInfosGUFA(Module* module, Propagator& propagator) {
173+
// Compute the oracle, then simply apply it.
174+
// TODO: Consider doing this in GUFA.cpp, where we already computed the
175+
// oracle. That would require refactoring out the rest of this pass to
176+
// a shared location. Alternatively, perhaps we can reuse the computed
177+
// oracle, but any pass that changes anything would need to invalidate
178+
// it...
179+
ContentOracle oracle(*module, getPassOptions());
180+
auto allTypes = ModuleUtils::collectHeapTypes(*module);
181+
for (auto type : allTypes) {
182+
if (type.isStruct()) {
183+
auto& fields = type.getStruct().fields;
184+
auto& infos = finalInfos[type];
185+
for (Index i = 0; i < fields.size(); i++) {
186+
auto gufaType = oracle.getContents(DataLocation{type, i}).getType();
187+
infos[i] = LUBFinder(gufaType);
188+
}
189+
}
190+
}
191+
192+
// Propagate to supertypes, so no field is less refined than its super.
193+
propagator.propagateToSuperTypes(finalInfos);
194+
}
142195

196+
void useFinalInfos(Module* module, Propagator& propagator) {
143197
// While we do the following work, see if we have anything to optimize, so
144198
// that we can avoid wasteful work later if not.
145199
bool canOptimize = false;
@@ -427,6 +481,7 @@ struct TypeRefining : public Pass {
427481

428482
} // anonymous namespace
429483

430-
Pass* createTypeRefiningPass() { return new TypeRefining(); }
484+
Pass* createTypeRefiningPass() { return new TypeRefining(false); }
485+
Pass* createTypeRefiningGUFAPass() { return new TypeRefining(true); }
431486

432487
} // namespace wasm

src/passes/pass.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,10 @@ void PassRegistry::registerPasses() {
214214
registerPass("type-refining",
215215
"apply more specific subtypes to type fields where possible",
216216
createTypeRefiningPass);
217+
registerPass("type-refining-gufa",
218+
"apply more specific subtypes to type fields where possible "
219+
"(using GUFA)",
220+
createTypeRefiningGUFAPass);
217221
registerPass(
218222
"heap2local", "replace GC allocations with locals", createHeap2LocalPass);
219223
registerPass("heap-store-optimization",

src/passes/passes.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ Pass* createTrapModeJS();
183183
Pass* createTupleOptimizationPass();
184184
Pass* createTypeGeneralizingPass();
185185
Pass* createTypeRefiningPass();
186+
Pass* createTypeRefiningGUFAPass();
186187
Pass* createTypeFinalizingPass();
187188
Pass* createTypeMergingPass();
188189
Pass* createTypeSSAPass();

test/lit/help/wasm-metadce.test

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,10 @@
554554
;; CHECK-NEXT: --type-refining apply more specific subtypes to
555555
;; CHECK-NEXT: type fields where possible
556556
;; CHECK-NEXT:
557+
;; CHECK-NEXT: --type-refining-gufa apply more specific subtypes to
558+
;; CHECK-NEXT: type fields where possible
559+
;; CHECK-NEXT: (using GUFA)
560+
;; CHECK-NEXT:
557561
;; CHECK-NEXT: --type-ssa create new nominal types to help
558562
;; CHECK-NEXT: other optimizations
559563
;; CHECK-NEXT:

test/lit/help/wasm-opt.test

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,10 @@
566566
;; CHECK-NEXT: --type-refining apply more specific subtypes to
567567
;; CHECK-NEXT: type fields where possible
568568
;; CHECK-NEXT:
569+
;; CHECK-NEXT: --type-refining-gufa apply more specific subtypes to
570+
;; CHECK-NEXT: type fields where possible
571+
;; CHECK-NEXT: (using GUFA)
572+
;; CHECK-NEXT:
569573
;; CHECK-NEXT: --type-ssa create new nominal types to help
570574
;; CHECK-NEXT: other optimizations
571575
;; CHECK-NEXT:

test/lit/help/wasm2js.test

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,10 @@
518518
;; CHECK-NEXT: --type-refining apply more specific subtypes to
519519
;; CHECK-NEXT: type fields where possible
520520
;; CHECK-NEXT:
521+
;; CHECK-NEXT: --type-refining-gufa apply more specific subtypes to
522+
;; CHECK-NEXT: type fields where possible
523+
;; CHECK-NEXT: (using GUFA)
524+
;; CHECK-NEXT:
521525
;; CHECK-NEXT: --type-ssa create new nominal types to help
522526
;; CHECK-NEXT: other optimizations
523527
;; CHECK-NEXT:

0 commit comments

Comments
 (0)