Skip to content

Commit 8778512

Browse files
authored
[Custom Descriptors] Heap2Local: Handle definitely-failing descriptor casts (#7919)
A definitely-failing ref.cast_desc is marked as non-escaping. We need to handle that, like in normal casts, by trapping rather than allowing the allocation to flow onward.
1 parent 17b024a commit 8778512

File tree

2 files changed

+70
-11
lines changed

2 files changed

+70
-11
lines changed

src/passes/Heap2Local.cpp

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -890,17 +890,28 @@ struct Struct2Local : PostWalker<Struct2Local> {
890890
}
891891
} else {
892892
assert(allocIsCastRef);
893-
// The cast succeeds iff the optimized allocation's descriptor is the
894-
// same as the given descriptor and traps otherwise.
895-
auto type = allocation->desc->type;
896-
replaceCurrent(builder.blockify(
897-
builder.makeDrop(curr->ref),
898-
builder.makeIf(
899-
builder.makeRefEq(
900-
curr->desc,
901-
builder.makeLocalGet(localIndexes[fields.size()], type)),
902-
builder.makeRefNull(allocation->type.getHeapType()),
903-
builder.makeUnreachable())));
893+
if (!Type::isSubType(allocation->type, curr->type)) {
894+
// The cast fails, so it must trap. We mark such failing casts as
895+
// fully consuming their inputs, so we cannot just emit the explicit
896+
// descriptor equality check below because it would appear to be able
897+
// to propagate the optimized allocation on to the parent (as a null
898+
// value, which might not validate).
899+
replaceCurrent(builder.blockify(builder.makeDrop(curr->ref),
900+
builder.makeDrop(curr->desc),
901+
builder.makeUnreachable()));
902+
} else {
903+
// The cast succeeds iff the optimized allocation's descriptor is the
904+
// same as the given descriptor and traps otherwise.
905+
auto type = allocation->desc->type;
906+
replaceCurrent(builder.blockify(
907+
builder.makeDrop(curr->ref),
908+
builder.makeIf(
909+
builder.makeRefEq(
910+
curr->desc,
911+
builder.makeLocalGet(localIndexes[fields.size()], type)),
912+
builder.makeRefNull(allocation->type.getHeapType()),
913+
builder.makeUnreachable())));
914+
}
904915
}
905916
} else {
906917
// We know this RefCast receives our allocation, so we can see whether it

test/lit/passes/heap2local-desc.wast

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -898,3 +898,51 @@
898898
)
899899
)
900900

901+
;; A definitely-failing descriptor cast. We have two pairs of descriptor/
902+
;; describee, and create an $A2 that we try to cast to the unrelated $A.
903+
(module
904+
(rec
905+
;; CHECK: (rec
906+
;; CHECK-NEXT: (type $A (descriptor $B (struct)))
907+
(type $A (descriptor $B (struct)))
908+
;; CHECK: (type $B (describes $A (struct)))
909+
(type $B (describes $A (struct)))
910+
911+
;; CHECK: (type $A2 (descriptor $B2 (struct)))
912+
(type $A2 (descriptor $B2 (struct)))
913+
;; CHECK: (type $B2 (describes $A2 (struct)))
914+
(type $B2 (describes $A2 (struct)))
915+
)
916+
917+
;; CHECK: (type $4 (func (result (ref $A))))
918+
919+
;; CHECK: (func $A (type $4) (result (ref $A))
920+
;; CHECK-NEXT: (local $0 (ref (exact $B2)))
921+
;; CHECK-NEXT: (local $1 (ref (exact $B2)))
922+
;; CHECK-NEXT: (drop
923+
;; CHECK-NEXT: (block (result nullref)
924+
;; CHECK-NEXT: (local.set $1
925+
;; CHECK-NEXT: (struct.new_default $B2)
926+
;; CHECK-NEXT: )
927+
;; CHECK-NEXT: (local.set $0
928+
;; CHECK-NEXT: (local.get $1)
929+
;; CHECK-NEXT: )
930+
;; CHECK-NEXT: (ref.null none)
931+
;; CHECK-NEXT: )
932+
;; CHECK-NEXT: )
933+
;; CHECK-NEXT: (drop
934+
;; CHECK-NEXT: (block (result nullref)
935+
;; CHECK-NEXT: (ref.null none)
936+
;; CHECK-NEXT: )
937+
;; CHECK-NEXT: )
938+
;; CHECK-NEXT: (unreachable)
939+
;; CHECK-NEXT: )
940+
(func $A (result (ref $A))
941+
(ref.cast_desc (ref $A)
942+
(struct.new_default $A2
943+
(struct.new_default $B2)
944+
)
945+
(struct.new_default $B)
946+
)
947+
)
948+
)

0 commit comments

Comments
 (0)