From cf5fe15eb8b086994555e2f894dc4de4da738da6 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 3 Nov 2025 13:53:50 -0800 Subject: [PATCH] fix --- src/passes/GlobalStructInference.cpp | 27 +++++++++---- test/lit/passes/gsi-nontype.wast | 58 ++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 8 deletions(-) diff --git a/src/passes/GlobalStructInference.cpp b/src/passes/GlobalStructInference.cpp index 824d3731dca..77badc2148e 100644 --- a/src/passes/GlobalStructInference.cpp +++ b/src/passes/GlobalStructInference.cpp @@ -309,6 +309,12 @@ struct GlobalStructInference : public Pass { bool refinalize = false; + // As we prepare to un-nest globals, we create global.gets of the global + // that we will un-nest the content to. That global does not yet exist, + // and we note such globals as we go so we ignore them (they are invalid + // IR until the global is created, later in this pass). + std::unordered_set unnestingGlobalGets; + void visitStructGet(StructGet* curr) { optimize(curr, curr->ref, curr->index); } @@ -347,14 +353,18 @@ struct GlobalStructInference : public Pass { // This is a read of an immutable field. See if it is a trivial case, of // a read from an immutable global. if (auto* get = ref->dynCast()) { - auto* global = wasm.getGlobal(get->name); - if (!global->mutable_ && !global->imported()) { - if (auto* structNew = global->init->dynCast()) { - auto value = readFromStructNew(structNew, fieldIndex, field); - // We know the exact global being read here. - value.globals.push_back(global->name); - replaceCurrent(getReadValue(value, fieldIndex, field, curr)); - return; + // The global.get must be valid, and not in the process of being + // rewritten to point to a new un-nested global. + if (!unnestingGlobalGets.count(get)) { + auto* global = wasm.getGlobal(get->name); + if (!global->mutable_ && !global->imported()) { + if (auto* structNew = global->init->dynCast()) { + auto value = readFromStructNew(structNew, fieldIndex, field); + // We know the exact global being read here. + value.globals.push_back(global->name); + replaceCurrent(getReadValue(value, fieldIndex, field, curr)); + return; + } } } } @@ -541,6 +551,7 @@ struct GlobalStructInference : public Pass { globalsToUnnest.emplace_back( GlobalToUnnest{value.globals[0], fieldIndex, get}); + unnestingGlobalGets.insert(get); ret = get; } diff --git a/test/lit/passes/gsi-nontype.wast b/test/lit/passes/gsi-nontype.wast index 01831b2bff3..4d0c158adb0 100644 --- a/test/lit/passes/gsi-nontype.wast +++ b/test/lit/passes/gsi-nontype.wast @@ -267,3 +267,61 @@ ) ) +;; Nested struct.gets that seem optimizable. +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $func (func (result anyref))) + (type $func (func (result anyref))) + ;; CHECK: (type $outer (sub (struct (field (ref $inner))))) + (type $outer (sub (struct (field (ref $inner))))) + ;; CHECK: (type $inner (sub (struct (field (ref $func))))) + (type $inner (sub (struct (field (ref $func))))) + ) + + ;; CHECK: (type $3 (func (result anyref))) + + ;; CHECK: (global $global.unnested.0 (ref (exact $inner)) (struct.new $inner + ;; CHECK-NEXT: (ref.func $func) + ;; CHECK-NEXT: )) + + ;; CHECK: (global $global (ref $outer) (struct.new $outer + ;; CHECK-NEXT: (global.get $global.unnested.0) + ;; CHECK-NEXT: )) + (global $global (ref $outer) (struct.new $outer + (struct.new $inner + (ref.func $func) + ) + )) + + ;; CHECK: (func $func (type $func) (result anyref) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + (func $func (type $func) (result anyref) + (unreachable) + ) + + ;; CHECK: (func $caller (type $3) (result anyref) + ;; CHECK-NEXT: (call_ref $func + ;; CHECK-NEXT: (struct.get $inner 0 + ;; CHECK-NEXT: (global.get $global.unnested.0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $caller (result anyref) + (call_ref $func + ;; TODO: If we did two passes, we could optimize this one too. + (struct.get $inner 0 + ;; These two can be optimized, if we un-nest the global. When doing so we + ;; turn these into a global.get, with a global name that does not exist yet + ;; (we only create that global later in the pass). We must not think it is + ;; a complete global.get and try to optimize with it when we reach the + ;; parent struct.get. + (struct.get $outer 0 + (global.get $global) + ) + ) + ) + ) +) +