Skip to content

Commit 525870c

Browse files
authored
GlobalRefining: Do not refine mutable exported globals (#7007)
A mutable exported global might be shared with another module which writes to it using the current type, which is unsafe and the type system does not allow, so do not refine there.
1 parent 686c76b commit 525870c

File tree

2 files changed

+40
-27
lines changed

2 files changed

+40
-27
lines changed

src/passes/GlobalRefining.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,16 @@ struct GlobalRefining : public Pass {
6767
// In closed world we cannot change the types of exports, as we might change
6868
// from a public type to a private that would cause a validation error.
6969
// TODO We could refine to a type that is still public, however.
70+
//
71+
// We are also limited in open world: in that mode we must assume that
72+
// another module might import our exported globals with the current type
73+
// (that type is a contract between them), and in such a case the type of
74+
// mutable globals must match precisely (the same rule as for mutable struct
75+
// fields in subtypes - the types must match exactly, or else a write in
76+
// one place could store a type considered in valid in another place).
7077
std::unordered_set<Name> unoptimizable;
71-
if (getPassOptions().closedWorld) {
72-
for (auto* global : ExportUtils::getExportedGlobals(*module)) {
78+
for (auto* global : ExportUtils::getExportedGlobals(*module)) {
79+
if (getPassOptions().closedWorld || global->mutable_) {
7380
unoptimizable.insert(global->name);
7481
}
7582
}

test/lit/passes/global-refining.wast

Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -169,31 +169,6 @@
169169
)
170170
)
171171

172-
;; We can refine here, but as it is an export we only do so in open world.
173-
(module
174-
;; CHECK: (type $0 (func))
175-
176-
;; CHECK: (global $func-init (mut (ref $0)) (ref.func $foo))
177-
;; CLOSD: (type $0 (func))
178-
179-
;; CLOSD: (global $func-init (mut funcref) (ref.func $foo))
180-
(global $func-init (mut funcref) (ref.func $foo))
181-
182-
;; CHECK: (export "global" (global $func-init))
183-
;; CLOSD: (export "global" (global $func-init))
184-
(export "global" (global $func-init))
185-
186-
;; CHECK: (func $foo (type $0)
187-
;; CHECK-NEXT: (nop)
188-
;; CHECK-NEXT: )
189-
;; CLOSD: (func $foo (type $0)
190-
;; CLOSD-NEXT: (nop)
191-
;; CLOSD-NEXT: )
192-
(func $foo
193-
(nop)
194-
)
195-
)
196-
197172
;; We can refine $a, after which we should update the global.get in the other
198173
;; global, or else we'd error on validation.
199174
;; TODO: we could optimize further here and refine the type of the global $b.
@@ -221,3 +196,34 @@
221196
(func $func (type $sub)
222197
)
223198
)
199+
200+
;; Test all combinations of being exported and being mutable.
201+
;;
202+
;; Mutability limits our ability to optimize in open world: mutable globals that
203+
;; are exported cannot be refined, as they might be modified in another module
204+
;; using the old type. In closed world, however, we can optimize both globals
205+
;; here, as mutability is not a concern. As a result, we can refine the
206+
;; (ref null func) to nullfuncref only when not exported, and if exported, then
207+
;; only when immutable in open world.
208+
(module
209+
;; CHECK: (global $mut (mut nullfuncref) (ref.null nofunc))
210+
;; CLOSD: (global $mut (mut nullfuncref) (ref.null nofunc))
211+
(global $mut (mut (ref null func)) (ref.null nofunc))
212+
;; CHECK: (global $imm nullfuncref (ref.null nofunc))
213+
;; CLOSD: (global $imm nullfuncref (ref.null nofunc))
214+
(global $imm (ref null func) (ref.null nofunc))
215+
;; CHECK: (global $mut-exp (mut funcref) (ref.null nofunc))
216+
;; CLOSD: (global $mut-exp (mut funcref) (ref.null nofunc))
217+
(global $mut-exp (mut (ref null func)) (ref.null nofunc))
218+
;; CHECK: (global $imm-exp nullfuncref (ref.null nofunc))
219+
;; CLOSD: (global $imm-exp funcref (ref.null nofunc))
220+
(global $imm-exp (ref null func) (ref.null nofunc))
221+
222+
;; CHECK: (export "mut-exp" (global $mut-exp))
223+
;; CLOSD: (export "mut-exp" (global $mut-exp))
224+
(export "mut-exp" (global $mut-exp))
225+
;; CHECK: (export "imm-exp" (global $imm-exp))
226+
;; CLOSD: (export "imm-exp" (global $imm-exp))
227+
(export "imm-exp" (global $imm-exp))
228+
)
229+

0 commit comments

Comments
 (0)