Skip to content

Commit ca3302b

Browse files
authored
[GC] Ignore public types in SignaturePruning (WebAssembly#7018)
Similar to WebAssembly#7017 . As with that PR, this reduces some optimizations that were valid, as we tried to do something complex here and refine types in a public rec group when it seemed safe to do so, but our analysis was incomplete. The testcase here shows how another operation can end up causing a dependency that breaks things, if another type that uses one that we modify is public. To be safe, ignore all public types. In the future perhaps we can find a good way to handle "almost-private" types in public rec groups, in closed world.
1 parent 0b882b9 commit ca3302b

File tree

2 files changed

+78
-29
lines changed

2 files changed

+78
-29
lines changed

src/passes/SignaturePruning.cpp

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -162,11 +162,10 @@ struct SignaturePruning : public Pass {
162162
sigFuncs[func->type].push_back(func);
163163
}
164164

165-
// Exported functions cannot be modified.
166-
for (auto& exp : module->exports) {
167-
if (exp->kind == ExternalKind::Function) {
168-
auto* func = module->getFunction(exp->value);
169-
allInfo[func->type].optimizable = false;
165+
// Find the public types, which cannot be modified.
166+
for (auto type : ModuleUtils::getPublicHeapTypes(*module)) {
167+
if (type.isFunction()) {
168+
allInfo[type].optimizable = false;
170169
}
171170
}
172171

@@ -291,16 +290,8 @@ struct SignaturePruning : public Pass {
291290
}
292291
}
293292

294-
// Rewrite the types. We pass in all the types we intend to modify as being
295-
// "additional private types" because we have proven above that they are
296-
// safe to modify, even if they are technically public (e.g. they may be in
297-
// a singleton big rec group that is public because one member is public).
298-
std::vector<HeapType> additionalPrivateTypes;
299-
for (auto& [type, sig] : newSignatures) {
300-
additionalPrivateTypes.push_back(type);
301-
}
302-
GlobalTypeRewriter::updateSignatures(
303-
newSignatures, *module, additionalPrivateTypes);
293+
// Rewrite the types.
294+
GlobalTypeRewriter::updateSignatures(newSignatures, *module);
304295

305296
if (callTargetsToLocalize.empty()) {
306297
return false;

test/lit/passes/signature-pruning.wast

Lines changed: 72 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1154,24 +1154,22 @@
11541154

11551155
;; $exported is exported. The entire rec group becomes exported as well, which
11561156
;; causes $unused-param's type to be public, which means we cannot normally
1157-
;; modify it. However, in closed world we allow such changes, and we can remove
1158-
;; the unused param there. What happens is that we keep the original public rec
1159-
;; group as-is, and add a new rec group for private types, put the pruned type
1160-
;; there, and use that pruned type on $unused-param.
1157+
;; modify it. However, in closed world we could allow such changes, by keeping
1158+
;; the original public rec group as-is, and add a new rec group for private
1159+
;; types, put the pruned type there, and use that pruned type on $unused-param.
1160+
;; That can work here, but not in the testcase after us. For now, we also do not
1161+
;; optimize here, as figuring out when it is safe is very difficult, and may
1162+
;; need a new design TODO
11611163
(module
11621164
(rec
1163-
;; CHECK: (rec
1164-
;; CHECK-NEXT: (type $0 (func))
1165-
1166-
;; CHECK: (type $much (func))
1167-
11681165
;; CHECK: (rec
11691166
;; CHECK-NEXT: (type $none (func))
11701167
(type $none (func))
1168+
;; CHECK: (type $much (func (param i32)))
11711169
(type $much (func (param i32)))
11721170
)
11731171

1174-
;; CHECK: (type $much_0 (func (param i32)))
1172+
;; CHECK: (type $2 (func))
11751173

11761174
;; CHECK: (export "exported" (func $exported))
11771175

@@ -1181,23 +1179,83 @@
11811179
(func $exported (export "exported") (type $none)
11821180
)
11831181

1184-
;; CHECK: (func $unused-param (type $much)
1185-
;; CHECK-NEXT: (local $0 i32)
1186-
;; CHECK-NEXT: (local.set $0
1182+
;; CHECK: (func $unused-param (type $much) (param $param i32)
1183+
;; CHECK-NEXT: (nop)
1184+
;; CHECK-NEXT: )
1185+
(func $unused-param (type $much) (param $param i32)
1186+
)
1187+
1188+
;; CHECK: (func $caller (type $2)
1189+
;; CHECK-NEXT: (call $unused-param
11871190
;; CHECK-NEXT: (i32.const 0)
11881191
;; CHECK-NEXT: )
1192+
;; CHECK-NEXT: )
1193+
(func $caller
1194+
(call $unused-param
1195+
(i32.const 0)
1196+
)
1197+
)
1198+
)
1199+
1200+
;; As the previous testcase, but add another use of the type we want to prune,
1201+
;; in a struct.new. The struct type is public, so we cannot modify it and
1202+
;; replace the reference to the function type with the pruned version.
1203+
(module
1204+
(rec
1205+
;; CHECK: (type $0 (func))
1206+
1207+
;; CHECK: (rec
1208+
;; CHECK-NEXT: (type $none (func))
1209+
(type $none (func))
1210+
;; CHECK: (type $much (func (param i32)))
1211+
(type $much (func (param i32)))
1212+
1213+
;; CHECK: (type $struct (struct (field (ref $much))))
1214+
(type $struct (struct (field (ref $much))))
1215+
)
1216+
1217+
;; CHECK: (elem declare func $unused-param)
1218+
1219+
;; CHECK: (export "exported" (func $exported))
1220+
1221+
;; CHECK: (func $exported (type $none)
1222+
;; CHECK-NEXT: (nop)
1223+
;; CHECK-NEXT: )
1224+
(func $exported (export "exported") (type $none)
1225+
;; This makes the rec group public.
1226+
)
1227+
1228+
;; CHECK: (func $unused-param (type $much) (param $param i32)
11891229
;; CHECK-NEXT: (nop)
11901230
;; CHECK-NEXT: )
11911231
(func $unused-param (type $much) (param $param i32)
11921232
)
11931233

11941234
;; CHECK: (func $caller (type $0)
1195-
;; CHECK-NEXT: (call $unused-param)
1235+
;; CHECK-NEXT: (call $unused-param
1236+
;; CHECK-NEXT: (i32.const 0)
1237+
;; CHECK-NEXT: )
11961238
;; CHECK-NEXT: )
11971239
(func $caller
11981240
(call $unused-param
11991241
(i32.const 0)
12001242
)
12011243
)
1244+
1245+
;; CHECK: (func $struct.new (type $0)
1246+
;; CHECK-NEXT: (drop
1247+
;; CHECK-NEXT: (struct.new $struct
1248+
;; CHECK-NEXT: (ref.func $unused-param)
1249+
;; CHECK-NEXT: )
1250+
;; CHECK-NEXT: )
1251+
;; CHECK-NEXT: )
1252+
(func $struct.new
1253+
;; This struct.new causes the problem mentioned above.
1254+
(drop
1255+
(struct.new $struct
1256+
(ref.func $unused-param)
1257+
)
1258+
)
1259+
)
12021260
)
12031261

0 commit comments

Comments
 (0)