Skip to content

Commit 8d395c0

Browse files
authored
Heap2Local: Ignore unreachable code (WebAssembly#7948)
Processing it may lead to us changing an unreachable to a concrete type, which is an error.
1 parent df6d943 commit 8d395c0

File tree

2 files changed

+97
-0
lines changed

2 files changed

+97
-0
lines changed

src/passes/Heap2Local.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -973,6 +973,15 @@ struct Struct2Local : PostWalker<Struct2Local> {
973973
return;
974974
}
975975

976+
if (curr->type == Type::unreachable) {
977+
// We must not modify unreachable code here, as we will replace it with a
978+
// local.get, which has a concrete type (another option could be to run
979+
// DCE and not only ReFinalize - DCE will propagate an unreachable out of
980+
// a concrete block, like we emit here - but we can just ignore such
981+
// code).
982+
return;
983+
}
984+
976985
auto& field = fields[curr->index];
977986
auto type = field.type;
978987
if (type != curr->type) {

test/lit/passes/heap2local-desc.wast

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1056,3 +1056,91 @@
10561056
)
10571057
)
10581058
)
1059+
1060+
(module
1061+
(rec
1062+
;; CHECK: (rec
1063+
;; CHECK-NEXT: (type $A (descriptor $B (struct (field v128))))
1064+
(type $A (descriptor $B (struct (field v128))))
1065+
;; CHECK: (type $B (describes $A (struct)))
1066+
(type $B (describes $A (struct)))
1067+
)
1068+
1069+
;; CHECK: (type $2 (func))
1070+
1071+
;; CHECK: (func $test (type $2)
1072+
;; CHECK-NEXT: (local $B (ref null $B))
1073+
;; CHECK-NEXT: (local $v v128)
1074+
;; CHECK-NEXT: (local $2 v128)
1075+
;; CHECK-NEXT: (local $3 (ref none))
1076+
;; CHECK-NEXT: (local $4 (ref none))
1077+
;; CHECK-NEXT: (drop
1078+
;; CHECK-NEXT: (block (result nullref)
1079+
;; CHECK-NEXT: (ref.null none)
1080+
;; CHECK-NEXT: )
1081+
;; CHECK-NEXT: )
1082+
;; CHECK-NEXT: (local.tee $v
1083+
;; CHECK-NEXT: (block ;; (replaces unreachable StructGet we can't emit)
1084+
;; CHECK-NEXT: (drop
1085+
;; CHECK-NEXT: (block
1086+
;; CHECK-NEXT: (drop
1087+
;; CHECK-NEXT: (block (result nullref)
1088+
;; CHECK-NEXT: (local.set $4
1089+
;; CHECK-NEXT: (ref.as_non_null
1090+
;; CHECK-NEXT: (ref.null none)
1091+
;; CHECK-NEXT: )
1092+
;; CHECK-NEXT: )
1093+
;; CHECK-NEXT: (local.set $2
1094+
;; CHECK-NEXT: (v128.const i32x4 0x00000000 0x00000000 0x00000000 0x00000000)
1095+
;; CHECK-NEXT: )
1096+
;; CHECK-NEXT: (local.set $3
1097+
;; CHECK-NEXT: (local.get $4)
1098+
;; CHECK-NEXT: )
1099+
;; CHECK-NEXT: (ref.null none)
1100+
;; CHECK-NEXT: )
1101+
;; CHECK-NEXT: )
1102+
;; CHECK-NEXT: (drop
1103+
;; CHECK-NEXT: (ref.null none)
1104+
;; CHECK-NEXT: )
1105+
;; CHECK-NEXT: (unreachable)
1106+
;; CHECK-NEXT: )
1107+
;; CHECK-NEXT: )
1108+
;; CHECK-NEXT: (unreachable)
1109+
;; CHECK-NEXT: )
1110+
;; CHECK-NEXT: )
1111+
;; CHECK-NEXT: )
1112+
(func $test
1113+
(local $B (ref null $B))
1114+
(local $v v128)
1115+
1116+
;; We can optimize a few times here. As we do so, the local.set becomes
1117+
;; unreachable, as its descriptor is null. Later work will replace the
1118+
;; nested struct.get there, which was unreachable, with a local.get of a
1119+
;; v128, a concrete type, causing an error as now the local.set is
1120+
;; unreachable but the child is not. To avoid this problem, we should not
1121+
;; modify unreachable code.
1122+
1123+
(drop
1124+
(ref.as_non_null
1125+
(local.tee $B
1126+
(struct.new_default $B)
1127+
)
1128+
)
1129+
)
1130+
(local.set $v
1131+
(struct.get $A 0
1132+
(ref.cast_desc (ref $A)
1133+
(struct.new_default $A
1134+
(ref.as_non_null
1135+
(ref.null none)
1136+
)
1137+
)
1138+
(ref.as_non_null
1139+
(local.get $B)
1140+
)
1141+
)
1142+
)
1143+
)
1144+
)
1145+
)
1146+

0 commit comments

Comments
 (0)