From 65431e37bc2b20cb8f0b2d73fbf0f2c205b763b6 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 14 Oct 2025 12:59:38 -0700 Subject: [PATCH 1/3] fix --- src/passes/GlobalRefining.cpp | 16 ++++++++++----- test/lit/passes/global-refining_no-cd.wast | 23 ++++++++++++++++++++++ 2 files changed, 34 insertions(+), 5 deletions(-) create mode 100644 test/lit/passes/global-refining_no-cd.wast diff --git a/src/passes/GlobalRefining.cpp b/src/passes/GlobalRefining.cpp index 4ef6252b5ac..17f987bf24a 100644 --- a/src/passes/GlobalRefining.cpp +++ b/src/passes/GlobalRefining.cpp @@ -22,6 +22,7 @@ #include "ir/find_all.h" #include "ir/lubs.h" #include "ir/module-utils.h" +#include "ir/public-type-validator.h" #include "ir/utils.h" #include "pass.h" #include "wasm-type.h" @@ -83,6 +84,8 @@ struct GlobalRefining : public Pass { bool optimized = false; + PublicTypeValidator publicTypeValidator(module->features); + for (auto& global : module->globals) { if (global->imported() || unoptimizable.count(global->name)) { continue; @@ -102,12 +105,15 @@ struct GlobalRefining : public Pass { auto oldType = global->type; auto newType = lub.getLUB(); - if (newType != oldType) { - // We found an improvement! - assert(Type::isSubType(newType, oldType)); - global->type = newType; - optimized = true; + if (newType == oldType || + !publicTypeValidator.isValidPublicType(newType)) { + continue; } + + // We found an improvement! + assert(Type::isSubType(newType, oldType)); + global->type = newType; + optimized = true; } if (!optimized) { diff --git a/test/lit/passes/global-refining_no-cd.wast b/test/lit/passes/global-refining_no-cd.wast new file mode 100644 index 00000000000..6b8d6f91950 --- /dev/null +++ b/test/lit/passes/global-refining_no-cd.wast @@ -0,0 +1,23 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. + +;; RUN: foreach %s %t wasm-opt --global-refining -all --disable-custom-descriptors -S -o - | filecheck %s + +;; We cannot refine the global's type, as then it would make the rec group +;; public, and that includes an exact type, which is invalid without custom +;; descriptors. +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $array (array i8)) + (type $array (array i8)) + ;; CHECK: (type $struct (struct (field (ref (exact $array))))) + (type $struct (struct (field (ref (exact $array))))) + ) + + ;; CHECK: (global $global (ref eq) (array.new_fixed $array 0)) + (global $global (ref eq) (array.new_fixed $array 0)) + + ;; CHECK: (export "global" (global $global)) + (export "global" (global $global)) +) + From f047e64d35d019717f47451e1ab650893b98921f Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 14 Oct 2025 13:10:41 -0700 Subject: [PATCH 2/3] fix --- src/passes/GlobalRefining.cpp | 13 +++++++++++-- test/lit/passes/global-refining_no-cd.wast | 7 ++++++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/passes/GlobalRefining.cpp b/src/passes/GlobalRefining.cpp index 17f987bf24a..c3679562bdf 100644 --- a/src/passes/GlobalRefining.cpp +++ b/src/passes/GlobalRefining.cpp @@ -76,7 +76,10 @@ struct GlobalRefining : public Pass { // fields in subtypes - the types must match exactly, or else a write in // one place could store a type considered in valid in another place). std::unordered_set unoptimizable; - for (auto* global : ExportUtils::getExportedGlobals(*module)) { + auto exportedGlobalsVec = ExportUtils::getExportedGlobals(*module); + std::unordered_set exportedGlobals(exportedGlobalsVec.begin(), + exportedGlobalsVec.end()); + for (auto* global : exportedGlobalsVec) { if (getPassOptions().closedWorld || global->mutable_) { unoptimizable.insert(global->name); } @@ -84,6 +87,7 @@ struct GlobalRefining : public Pass { bool optimized = false; + // We must avoid exporting invalid types. Note all exports to help there. PublicTypeValidator publicTypeValidator(module->features); for (auto& global : module->globals) { @@ -105,7 +109,12 @@ struct GlobalRefining : public Pass { auto oldType = global->type; auto newType = lub.getLUB(); - if (newType == oldType || + if (newType == oldType) { + continue; + } + + // Do not make invalid types public. + if (exportedGlobals.count(global.get()) && !publicTypeValidator.isValidPublicType(newType)) { continue; } diff --git a/test/lit/passes/global-refining_no-cd.wast b/test/lit/passes/global-refining_no-cd.wast index 6b8d6f91950..ff1a8590760 100644 --- a/test/lit/passes/global-refining_no-cd.wast +++ b/test/lit/passes/global-refining_no-cd.wast @@ -2,9 +2,11 @@ ;; RUN: foreach %s %t wasm-opt --global-refining -all --disable-custom-descriptors -S -o - | filecheck %s -;; We cannot refine the global's type, as then it would make the rec group +;; We cannot refine $global's type, as then it would make the rec group ;; public, and that includes an exact type, which is invalid without custom ;; descriptors. +;; +;; We can refine $unexported, though, as it makes nothing public. (module (rec ;; CHECK: (rec @@ -17,6 +19,9 @@ ;; CHECK: (global $global (ref eq) (array.new_fixed $array 0)) (global $global (ref eq) (array.new_fixed $array 0)) + ;; CHECK: (global $unexported (ref (exact $array)) (array.new_fixed $array 0)) + (global $unexported (ref eq) (array.new_fixed $array 0)) + ;; CHECK: (export "global" (global $global)) (export "global" (global $global)) ) From 25f9672a57b55e9145e048d278e30cdf15a4ac92 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 14 Oct 2025 13:11:24 -0700 Subject: [PATCH 3/3] fix --- src/passes/GlobalRefining.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/passes/GlobalRefining.cpp b/src/passes/GlobalRefining.cpp index c3679562bdf..1cb4aed7c29 100644 --- a/src/passes/GlobalRefining.cpp +++ b/src/passes/GlobalRefining.cpp @@ -86,8 +86,6 @@ struct GlobalRefining : public Pass { } bool optimized = false; - - // We must avoid exporting invalid types. Note all exports to help there. PublicTypeValidator publicTypeValidator(module->features); for (auto& global : module->globals) {