Skip to content

Commit c326e66

Browse files
authored
[Custom Descriptors] Handle inexact ref.get_desc in AbstractTypeRefining (WebAssembly#7965)
In that case we need to consider subtypes.
1 parent a76b8ad commit c326e66

File tree

2 files changed

+143
-6
lines changed

2 files changed

+143
-6
lines changed

src/passes/AbstractTypeRefining.cpp

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -395,19 +395,32 @@ struct AbstractTypeRefining : public Pass {
395395
}
396396

397397
// If we optimize away a descriptor type, then we must fix up any
398-
// ref.get_desc of it, as ReFinalize would fix us up to return it. This
399-
// can only happen when no such descriptor is created, which means the
400-
// instruction will never be reached (no struct with such a descriptor was
401-
// created).
398+
// ref.get_desc of it, as ReFinalize would fix us up to return it.
402399
void visitRefGetDesc(RefGetDesc* curr) {
403400
auto optimized = getOptimized(curr->type);
404401
if (!optimized) {
405402
return;
406403
}
407404

405+
// We are optimizing the descriptor to a subtype.
406+
auto subDescriptor = *optimized;
407+
408408
Builder builder(*getModule());
409-
replaceCurrent(builder.makeSequence(builder.makeDrop(curr->ref),
410-
builder.makeUnreachable()));
409+
if (curr->type.isExact() || subDescriptor == HeapTypes::none) {
410+
// This is exact, so we can ignore subtypes, or there is no subtype to
411+
// optimize to. In this case it must trap.
412+
replaceCurrent(builder.makeSequence(builder.makeDrop(curr->ref),
413+
builder.makeUnreachable()));
414+
} else {
415+
// The descriptor is abstract, but it has a subtype that is not
416+
// (which we want to optimize to). We know this will only succeed if
417+
// we receive the subtype of the described type, so that that
418+
// descriptor is fetched. Add a cast so that we validate.
419+
auto subDescribed = subDescriptor.getDescribedType();
420+
assert(subDescribed);
421+
curr->ref =
422+
builder.makeRefCast(curr->ref, curr->ref->type.with(*subDescribed));
423+
}
411424
}
412425

413426
void visitBrOn(BrOn* curr) {

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

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1038,6 +1038,8 @@
10381038

10391039
;; YESTNH: (type $2 (func (result (ref none))))
10401040

1041+
;; YESTNH: (type $3 (func (param (ref $A)) (result (ref none))))
1042+
10411043
;; YESTNH: (func $test (type $2) (result (ref none))
10421044
;; YESTNH-NEXT: (drop
10431045
;; YESTNH-NEXT: (struct.new_default $A
@@ -1048,6 +1050,8 @@
10481050
;; YESTNH-NEXT: )
10491051
;; NO_TNH: (type $2 (func (result (ref none))))
10501052

1053+
;; NO_TNH: (type $3 (func (param (ref $A)) (result (ref none))))
1054+
10511055
;; NO_TNH: (func $test (type $2) (result (ref none))
10521056
;; NO_TNH-NEXT: (drop
10531057
;; NO_TNH-NEXT: (struct.new_default $A
@@ -1063,5 +1067,125 @@
10631067
)
10641068
)
10651069
)
1070+
1071+
;; YESTNH: (func $inexact (type $3) (param $inexact (ref $A)) (result (ref none))
1072+
;; YESTNH-NEXT: (drop
1073+
;; YESTNH-NEXT: (local.get $inexact)
1074+
;; YESTNH-NEXT: )
1075+
;; YESTNH-NEXT: (unreachable)
1076+
;; YESTNH-NEXT: )
1077+
;; NO_TNH: (func $inexact (type $3) (param $inexact (ref $A)) (result (ref none))
1078+
;; NO_TNH-NEXT: (drop
1079+
;; NO_TNH-NEXT: (local.get $inexact)
1080+
;; NO_TNH-NEXT: )
1081+
;; NO_TNH-NEXT: (unreachable)
1082+
;; NO_TNH-NEXT: )
1083+
(func $inexact (param $inexact (ref $A)) (result (ref $B))
1084+
;; As above, but now the input type is inexact. There are no subtypes, so
1085+
;; we still optimize.
1086+
(ref.get_desc $A
1087+
(local.get $inexact)
1088+
)
1089+
)
1090+
)
1091+
1092+
(module
1093+
;; As above, but now there are subtypes $A.sub, $B.sub.
1094+
(rec
1095+
;; YESTNH: (rec
1096+
;; YESTNH-NEXT: (type $A (sub (descriptor $B (struct))))
1097+
;; NO_TNH: (rec
1098+
;; NO_TNH-NEXT: (type $A (sub (descriptor $B (struct))))
1099+
(type $A (sub (descriptor $B (struct))))
1100+
;; YESTNH: (type $B (sub (describes $A (struct))))
1101+
;; NO_TNH: (type $B (sub (describes $A (struct))))
1102+
(type $B (sub (describes $A (struct))))
1103+
1104+
;; YESTNH: (type $A.sub (sub $A (descriptor $B.sub (struct))))
1105+
;; NO_TNH: (type $A.sub (sub $A (descriptor $B.sub (struct))))
1106+
(type $A.sub (sub $A (descriptor $B.sub (struct))))
1107+
;; YESTNH: (type $B.sub (sub $B (describes $A.sub (struct))))
1108+
;; NO_TNH: (type $B.sub (sub $B (describes $A.sub (struct))))
1109+
(type $B.sub (sub $B (describes $A.sub (struct))))
1110+
)
1111+
1112+
;; YESTNH: (type $4 (func))
1113+
1114+
;; YESTNH: (type $5 (func (result (ref (exact $B.sub)))))
1115+
1116+
;; YESTNH: (type $6 (func (param (ref $A)) (result (ref $B.sub))))
1117+
1118+
;; YESTNH: (func $create (type $4)
1119+
;; YESTNH-NEXT: (drop
1120+
;; YESTNH-NEXT: (struct.new_default $A.sub
1121+
;; YESTNH-NEXT: (struct.new_default $B.sub)
1122+
;; YESTNH-NEXT: )
1123+
;; YESTNH-NEXT: )
1124+
;; YESTNH-NEXT: )
1125+
;; NO_TNH: (type $4 (func))
1126+
1127+
;; NO_TNH: (type $5 (func (result (ref (exact $B)))))
1128+
1129+
;; NO_TNH: (type $6 (func (param (ref $A)) (result (ref $B))))
1130+
1131+
;; NO_TNH: (func $create (type $4)
1132+
;; NO_TNH-NEXT: (drop
1133+
;; NO_TNH-NEXT: (struct.new_default $A.sub
1134+
;; NO_TNH-NEXT: (struct.new_default $B.sub)
1135+
;; NO_TNH-NEXT: )
1136+
;; NO_TNH-NEXT: )
1137+
;; NO_TNH-NEXT: )
1138+
(func $create
1139+
;; Make the subtypes not abstract.
1140+
(drop
1141+
(struct.new_default $A.sub
1142+
(struct.new_default $B.sub)
1143+
)
1144+
)
1145+
)
1146+
1147+
;; YESTNH: (func $test (type $5) (result (ref (exact $B.sub)))
1148+
;; YESTNH-NEXT: (drop
1149+
;; YESTNH-NEXT: (struct.new_default $A
1150+
;; YESTNH-NEXT: (ref.null none)
1151+
;; YESTNH-NEXT: )
1152+
;; YESTNH-NEXT: )
1153+
;; YESTNH-NEXT: (unreachable)
1154+
;; YESTNH-NEXT: )
1155+
;; NO_TNH: (func $test (type $5) (result (ref (exact $B)))
1156+
;; NO_TNH-NEXT: (ref.get_desc $A
1157+
;; NO_TNH-NEXT: (struct.new_default $A
1158+
;; NO_TNH-NEXT: (ref.null none)
1159+
;; NO_TNH-NEXT: )
1160+
;; NO_TNH-NEXT: )
1161+
;; NO_TNH-NEXT: )
1162+
(func $test (result (ref (exact $B)))
1163+
;; We can still optimize here, thanks to exactness (when TNH).
1164+
(ref.get_desc $A
1165+
(struct.new_default $A
1166+
(ref.null none)
1167+
)
1168+
)
1169+
)
1170+
1171+
;; YESTNH: (func $inexact (type $6) (param $inexact (ref $A)) (result (ref $B.sub))
1172+
;; YESTNH-NEXT: (ref.get_desc $A.sub
1173+
;; YESTNH-NEXT: (ref.cast (ref $A.sub)
1174+
;; YESTNH-NEXT: (local.get $inexact)
1175+
;; YESTNH-NEXT: )
1176+
;; YESTNH-NEXT: )
1177+
;; YESTNH-NEXT: )
1178+
;; NO_TNH: (func $inexact (type $6) (param $inexact (ref $A)) (result (ref $B))
1179+
;; NO_TNH-NEXT: (ref.get_desc $A
1180+
;; NO_TNH-NEXT: (local.get $inexact)
1181+
;; NO_TNH-NEXT: )
1182+
;; NO_TNH-NEXT: )
1183+
(func $inexact (param $inexact (ref $A)) (result (ref $B))
1184+
;; We do not optimize to an unreachable here, as the inexact reference may
1185+
;; contain an $A.sub. We optimize to that, using a cast (in TNH).
1186+
(ref.get_desc $A
1187+
(local.get $inexact)
1188+
)
1189+
)
10661190
)
10671191

0 commit comments

Comments
 (0)