Skip to content

Commit bc36c02

Browse files
authoredOct 18, 2024
Remove closed world validation checks (#7019)
These were added to avoid common problems with closed world mode, but in practice they are causing more harm than good, forcing users to work around them. In the meantime (until #6965), remove this validation to unblock current toolchain makers. Fix GlobalTypeOptimization and AbstractTypeRefining on issues that this uncovers: without this validation, it is possible to run them on more wasm files than before, hence these were not previously detected. They are bundled in this PR because their tests cannot validate before this PR.
1 parent 679c26f commit bc36c02

8 files changed

+113
-183
lines changed
 

‎src/pass.h

-8
Original file line numberDiff line numberDiff line change
@@ -212,14 +212,6 @@ struct PassOptions {
212212
// but we also want to keep types of things on the boundary unchanged. For
213213
// example, we should not change an exported function's signature, as the
214214
// outside may need that type to properly call the export.
215-
//
216-
// * Since the goal of closedWorld is to optimize types aggressively but
217-
// types on the module boundary cannot be changed, we assume the producer
218-
// has made a mistake and we consider it a validation error if any user
219-
// defined types besides the types of imported or exported functions
220-
// themselves appear on the module boundary. For example, no user defined
221-
// struct type may be a parameter or result of an exported function. This
222-
// error may be relaxed or made more configurable in the future.
223215
bool closedWorld = false;
224216
// Whether to try to preserve debug info through, which are special calls.
225217
bool debugInfo = false;

‎src/passes/AbstractTypeRefining.cpp

+10
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,16 @@ struct AbstractTypeRefining : public Pass {
106106
}
107107
}
108108

109+
// Assume all public types are created, which makes them non-abstract and
110+
// hence ignored below.
111+
// TODO: In principle we could assume such types are not created outside the
112+
// module, given closed world, but we'd also need to make sure that
113+
// we don't need to make any changes to public types that refer to
114+
// them.
115+
for (auto type : ModuleUtils::getPublicHeapTypes(*module)) {
116+
createdTypes.insert(type);
117+
}
118+
109119
SubTypes subTypes(*module);
110120

111121
// Compute createdTypesOrSubTypes by starting with the created types and

‎src/passes/GlobalTypeOptimization.cpp

+14
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,20 @@ struct GlobalTypeOptimization : public Pass {
198198
continue;
199199
}
200200

201+
// The propagation analysis ensures we update immutability in all
202+
// supers and subs in concert, but it does not take into account
203+
// visibility, so do that here: we can only become immutable if the
204+
// parent can as well.
205+
auto super = type.getDeclaredSuperType();
206+
if (super && !canBecomeImmutable.count(*super)) {
207+
// No entry in canBecomeImmutable means nothing in the parent can
208+
// become immutable. We don't need to check the specific field index,
209+
// because visibility affects them all equally (i.e., if it is public
210+
// then no field can be changed, and if it is private then this field
211+
// can be changed, and perhaps more).
212+
continue;
213+
}
214+
201215
// No set exists. Mark it as something we can make immutable.
202216
auto& vec = canBecomeImmutable[type];
203217
vec.resize(i + 1);

‎src/wasm/wasm-validator.cpp

+1-50
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@ struct ValidationInfo {
6363
bool validateWeb;
6464
bool validateGlobally;
6565
bool quiet;
66-
bool closedWorld;
6766

6867
std::atomic<bool> valid;
6968

@@ -4135,46 +4134,6 @@ static void validateFeatures(Module& module, ValidationInfo& info) {
41354134
}
41364135
}
41374136

4138-
static void validateClosedWorldInterface(Module& module, ValidationInfo& info) {
4139-
// Error if there are any publicly exposed heap types beyond the types of
4140-
// publicly exposed functions. Note that we must include all types in the rec
4141-
// groups that are used, as if a type if public then all types in its rec
4142-
// group are as well.
4143-
std::unordered_set<RecGroup> publicRecGroups;
4144-
ModuleUtils::iterImportedFunctions(module, [&](Function* func) {
4145-
publicRecGroups.insert(func->type.getRecGroup());
4146-
});
4147-
for (auto& ex : module.exports) {
4148-
if (ex->kind == ExternalKind::Function) {
4149-
publicRecGroups.insert(module.getFunction(ex->value)->type.getRecGroup());
4150-
}
4151-
}
4152-
4153-
std::unordered_set<HeapType> publicTypes;
4154-
for (auto& group : publicRecGroups) {
4155-
for (auto type : group) {
4156-
publicTypes.insert(type);
4157-
}
4158-
}
4159-
4160-
// Ignorable public types are public, but we can ignore them for purposes of
4161-
// erroring here: It is always ok that they are public.
4162-
auto ignorable = getIgnorablePublicTypes();
4163-
4164-
for (auto type : ModuleUtils::getPublicHeapTypes(module)) {
4165-
if (!publicTypes.count(type) && !ignorable.count(type)) {
4166-
auto name = type.toString();
4167-
if (auto it = module.typeNames.find(type); it != module.typeNames.end()) {
4168-
name = it->second.name.toString();
4169-
}
4170-
info.fail("publicly exposed type disallowed with a closed world: $" +
4171-
name,
4172-
type,
4173-
nullptr);
4174-
}
4175-
}
4176-
}
4177-
41784137
// TODO: If we want the validator to be part of libwasm rather than libpasses,
41794138
// then Using PassRunner::getPassDebug causes a circular dependence. We should
41804139
// fix that, perhaps by moving some of the pass infrastructure into libsupport.
@@ -4183,7 +4142,6 @@ bool WasmValidator::validate(Module& module, Flags flags) {
41834142
info.validateWeb = (flags & Web) != 0;
41844143
info.validateGlobally = (flags & Globally) != 0;
41854144
info.quiet = (flags & Quiet) != 0;
4186-
info.closedWorld = (flags & ClosedWorld) != 0;
41874145

41884146
// Parallel function validation.
41894147
PassRunner runner(&module);
@@ -4210,9 +4168,6 @@ bool WasmValidator::validate(Module& module, Flags flags) {
42104168
validateStart(module, info);
42114169
validateModuleMaps(module, info);
42124170
validateFeatures(module, info);
4213-
if (info.closedWorld) {
4214-
validateClosedWorldInterface(module, info);
4215-
}
42164171
}
42174172

42184173
// Validate additional internal IR details when in pass-debug mode.
@@ -4231,11 +4186,7 @@ bool WasmValidator::validate(Module& module, Flags flags) {
42314186
}
42324187

42334188
bool WasmValidator::validate(Module& module, const PassOptions& options) {
4234-
Flags flags = options.validateGlobally ? Globally : Minimal;
4235-
if (options.closedWorld) {
4236-
flags |= ClosedWorld;
4237-
}
4238-
return validate(module, flags);
4189+
return validate(module, options.validateGlobally ? Globally : Minimal);
42394190
}
42404191

42414192
bool WasmValidator::validate(Function* func, Module& module, Flags flags) {

‎test/lit/passes/abstract-type-refining.wast

+52
Original file line numberDiff line numberDiff line change
@@ -1304,3 +1304,55 @@
13041304
)
13051305
)
13061306
)
1307+
1308+
;; $A is never created, but $B is, so all appearances of $A, like in the cast
1309+
;; and the struct field, could be replaced by $B, except that $A is a public type,
1310+
;; so might be created outside the module.
1311+
(module
1312+
(rec
1313+
;; YESTNH: (rec
1314+
;; YESTNH-NEXT: (type $A (sub (struct (field (ref null $A)))))
1315+
;; NO_TNH: (rec
1316+
;; NO_TNH-NEXT: (type $A (sub (struct (field (ref null $A)))))
1317+
(type $A (sub (struct (field (ref null $A)))))
1318+
1319+
;; YESTNH: (type $B (sub $A (struct (field (ref null $A)))))
1320+
;; NO_TNH: (type $B (sub $A (struct (field (ref null $A)))))
1321+
(type $B (sub $A (struct (field (ref null $A)))))
1322+
)
1323+
1324+
;; YESTNH: (type $2 (func (param anyref)))
1325+
1326+
;; YESTNH: (global $global (ref $B) (struct.new_default $B))
1327+
;; NO_TNH: (type $2 (func (param anyref)))
1328+
1329+
;; NO_TNH: (global $global (ref $B) (struct.new_default $B))
1330+
(global $global (ref $B) (struct.new_default $B))
1331+
1332+
;; YESTNH: (export "global" (global $global))
1333+
;; NO_TNH: (export "global" (global $global))
1334+
(export "global" (global $global))
1335+
1336+
;; YESTNH: (func $new (type $2) (param $x anyref)
1337+
;; YESTNH-NEXT: (drop
1338+
;; YESTNH-NEXT: (ref.cast (ref $A)
1339+
;; YESTNH-NEXT: (local.get $x)
1340+
;; YESTNH-NEXT: )
1341+
;; YESTNH-NEXT: )
1342+
;; YESTNH-NEXT: )
1343+
;; NO_TNH: (func $new (type $2) (param $x anyref)
1344+
;; NO_TNH-NEXT: (drop
1345+
;; NO_TNH-NEXT: (ref.cast (ref $A)
1346+
;; NO_TNH-NEXT: (local.get $x)
1347+
;; NO_TNH-NEXT: )
1348+
;; NO_TNH-NEXT: )
1349+
;; NO_TNH-NEXT: )
1350+
(func $new (param $x anyref)
1351+
(drop
1352+
(ref.cast (ref $A)
1353+
(local.get $x)
1354+
)
1355+
)
1356+
)
1357+
)
1358+

‎test/lit/passes/gto-mutability.wast

+36
Original file line numberDiff line numberDiff line change
@@ -652,3 +652,39 @@
652652
)
653653
)
654654
)
655+
656+
;; The parent is public, which prevents us from making any field immutable in
657+
;; the child.
658+
(module
659+
;; CHECK: (type $parent (sub (struct (field (mut i32)))))
660+
(type $parent (sub (struct (field (mut i32)))))
661+
;; CHECK: (type $1 (func))
662+
663+
;; CHECK: (type $child (sub $parent (struct (field (mut i32)))))
664+
(type $child (sub $parent (struct (field (mut i32)))))
665+
666+
;; CHECK: (global $global (ref $parent) (struct.new $parent
667+
;; CHECK-NEXT: (i32.const 0)
668+
;; CHECK-NEXT: ))
669+
(global $global (ref $parent) (struct.new $parent
670+
(i32.const 0)
671+
))
672+
673+
;; Make the parent public by exporting the global.
674+
;; CHECK: (export "global" (global $global))
675+
(export "global" (global $global))
676+
677+
;; CHECK: (func $func (type $1)
678+
;; CHECK-NEXT: (drop
679+
;; CHECK-NEXT: (struct.new_default $child)
680+
;; CHECK-NEXT: )
681+
;; CHECK-NEXT: )
682+
(func $func
683+
;; Create the child so the type is used. No sets to the fields exist, so
684+
;; in theory all fields could be immutable.
685+
(drop
686+
(struct.new_default $child)
687+
)
688+
)
689+
)
690+

‎test/lit/validation/closed-world-interface-intrinsics.wast

-46
This file was deleted.

‎test/lit/validation/closed-world-interface.wast

-79
This file was deleted.

0 commit comments

Comments
 (0)
Failed to load comments.