Skip to content

Commit f23ae35

Browse files
authored
GlobalStructInference: Handle nested optimizable global.gets (#8019)
When we un-nest, temporarily there is a global.get of a global we have not yet created. Mark those so we don't read them and get confused.
1 parent f9864ac commit f23ae35

2 files changed

Lines changed: 77 additions & 8 deletions

File tree

src/passes/GlobalStructInference.cpp

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,12 @@ struct GlobalStructInference : public Pass {
309309

310310
bool refinalize = false;
311311

312+
// As we prepare to un-nest globals, we create global.gets of the global
313+
// that we will un-nest the content to. That global does not yet exist,
314+
// and we note such globals as we go so we ignore them (they are invalid
315+
// IR until the global is created, later in this pass).
316+
std::unordered_set<GlobalGet*> unnestingGlobalGets;
317+
312318
void visitStructGet(StructGet* curr) {
313319
optimize(curr, curr->ref, curr->index);
314320
}
@@ -347,14 +353,18 @@ struct GlobalStructInference : public Pass {
347353
// This is a read of an immutable field. See if it is a trivial case, of
348354
// a read from an immutable global.
349355
if (auto* get = ref->dynCast<GlobalGet>()) {
350-
auto* global = wasm.getGlobal(get->name);
351-
if (!global->mutable_ && !global->imported()) {
352-
if (auto* structNew = global->init->dynCast<StructNew>()) {
353-
auto value = readFromStructNew(structNew, fieldIndex, field);
354-
// We know the exact global being read here.
355-
value.globals.push_back(global->name);
356-
replaceCurrent(getReadValue(value, fieldIndex, field, curr));
357-
return;
356+
// The global.get must be valid, and not in the process of being
357+
// rewritten to point to a new un-nested global.
358+
if (!unnestingGlobalGets.count(get)) {
359+
auto* global = wasm.getGlobal(get->name);
360+
if (!global->mutable_ && !global->imported()) {
361+
if (auto* structNew = global->init->dynCast<StructNew>()) {
362+
auto value = readFromStructNew(structNew, fieldIndex, field);
363+
// We know the exact global being read here.
364+
value.globals.push_back(global->name);
365+
replaceCurrent(getReadValue(value, fieldIndex, field, curr));
366+
return;
367+
}
358368
}
359369
}
360370
}
@@ -541,6 +551,7 @@ struct GlobalStructInference : public Pass {
541551

542552
globalsToUnnest.emplace_back(
543553
GlobalToUnnest{value.globals[0], fieldIndex, get});
554+
unnestingGlobalGets.insert(get);
544555

545556
ret = get;
546557
}

test/lit/passes/gsi-nontype.wast

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,3 +267,61 @@
267267
)
268268
)
269269

270+
;; Nested struct.gets that seem optimizable.
271+
(module
272+
(rec
273+
;; CHECK: (rec
274+
;; CHECK-NEXT: (type $func (func (result anyref)))
275+
(type $func (func (result anyref)))
276+
;; CHECK: (type $outer (sub (struct (field (ref $inner)))))
277+
(type $outer (sub (struct (field (ref $inner)))))
278+
;; CHECK: (type $inner (sub (struct (field (ref $func)))))
279+
(type $inner (sub (struct (field (ref $func)))))
280+
)
281+
282+
;; CHECK: (type $3 (func (result anyref)))
283+
284+
;; CHECK: (global $global.unnested.0 (ref (exact $inner)) (struct.new $inner
285+
;; CHECK-NEXT: (ref.func $func)
286+
;; CHECK-NEXT: ))
287+
288+
;; CHECK: (global $global (ref $outer) (struct.new $outer
289+
;; CHECK-NEXT: (global.get $global.unnested.0)
290+
;; CHECK-NEXT: ))
291+
(global $global (ref $outer) (struct.new $outer
292+
(struct.new $inner
293+
(ref.func $func)
294+
)
295+
))
296+
297+
;; CHECK: (func $func (type $func) (result anyref)
298+
;; CHECK-NEXT: (unreachable)
299+
;; CHECK-NEXT: )
300+
(func $func (type $func) (result anyref)
301+
(unreachable)
302+
)
303+
304+
;; CHECK: (func $caller (type $3) (result anyref)
305+
;; CHECK-NEXT: (call_ref $func
306+
;; CHECK-NEXT: (struct.get $inner 0
307+
;; CHECK-NEXT: (global.get $global.unnested.0)
308+
;; CHECK-NEXT: )
309+
;; CHECK-NEXT: )
310+
;; CHECK-NEXT: )
311+
(func $caller (result anyref)
312+
(call_ref $func
313+
;; TODO: If we did two passes, we could optimize this one too.
314+
(struct.get $inner 0
315+
;; These two can be optimized, if we un-nest the global. When doing so we
316+
;; turn these into a global.get, with a global name that does not exist yet
317+
;; (we only create that global later in the pass). We must not think it is
318+
;; a complete global.get and try to optimize with it when we reach the
319+
;; parent struct.get.
320+
(struct.get $outer 0
321+
(global.get $global)
322+
)
323+
)
324+
)
325+
)
326+
)
327+

0 commit comments

Comments
 (0)