Skip to content

Commit 2165afc

Browse files
authored
TypeRefiningGUFA: Avoid bottom continuation casts (#8513)
The pass already checked `isContinuation` and did not refine a field to such a type, if we know we need a cast to fix things up (we can't do it in such cases, as the type can't be cast). However, we did not handle a cast to the bottom type nullcontref. Avoid a cast there by emitting a null. That is, before we did ```wat (ref.cast nullcontref VALUE ) ``` and now we do ```wat (block (drop (VALUE)) (ref.null) ) ``` The null has the right type for where we are writing. For a non-nullable bottom type, emit an unreachable. As a drive by, remove ``` updater.runOnModuleCode(getPassRunner(), &wasm); ``` That code cannot work in module code anyhow (it emits casts, blocks, drops, etc.).
1 parent 40e9cfd commit 2165afc

File tree

3 files changed

+118
-30
lines changed

3 files changed

+118
-30
lines changed

src/passes/TypeRefining.cpp

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,8 @@ struct TypeRefining : public Pass {
474474

475475
TypeRewriter(wasm, *this).update();
476476

477+
// Refinalization fixes up types and makes them fit in the places we write
478+
// them to.
477479
ReFinalize().run(getPassRunner(), &wasm);
478480

479481
// After refinalizing, we may still have situations that do not validate.
@@ -514,9 +516,7 @@ struct TypeRefining : public Pass {
514516
for (Index i = 0; i < fields.size(); i++) {
515517
auto*& operand = curr->operands[i];
516518
auto fieldType = fields[i].type;
517-
if (!Type::isSubType(operand->type, fieldType)) {
518-
operand = Builder(*getModule()).makeRefCast(operand, fieldType);
519-
}
519+
operand = fixType(operand, fieldType);
520520
}
521521
}
522522

@@ -532,17 +532,45 @@ struct TypeRefining : public Pass {
532532
}
533533

534534
auto fieldType = type.getStruct().fields[curr->index].type;
535+
curr->value = fixType(curr->value, fieldType);
536+
}
537+
538+
bool refinalize = false;
539+
540+
// Fix up a given value so it fits into the type the location it is
541+
// written to.
542+
Expression* fixType(Expression* value, Type type) {
543+
if (Type::isSubType(value->type, type)) {
544+
return value;
545+
}
546+
// We cast to fix this up. An exception is a bottom type, which we can
547+
// handle by emitting a null (which works with types that cannot be
548+
// cast, like continuations; it also explicitly provides the value being
549+
// written, which other passes would do anyhow).
550+
Builder builder(*getModule());
551+
auto heapType = type.getHeapType();
552+
if (heapType.isBottom()) {
553+
auto* drop = builder.makeDrop(value);
554+
if (type.isNonNullable()) {
555+
// This will just trap. Make it trap, and update parents' types.
556+
refinalize = true;
557+
return builder.makeSequence(drop, builder.makeUnreachable());
558+
} else {
559+
return builder.makeSequence(drop, builder.makeRefNull(heapType));
560+
}
561+
}
562+
return builder.makeRefCast(value, type);
563+
}
535564

536-
if (!Type::isSubType(curr->value->type, fieldType)) {
537-
curr->value =
538-
Builder(*getModule()).makeRefCast(curr->value, fieldType);
565+
void visitFunction(Function* func) {
566+
if (refinalize) {
567+
ReFinalize().walkFunctionInModule(func, getModule());
539568
}
540569
}
541570
};
542571

543572
WriteUpdater updater;
544573
updater.run(getPassRunner(), &wasm);
545-
updater.runOnModuleCode(getPassRunner(), &wasm);
546574
}
547575
};
548576

test/lit/passes/type-refining-gufa.wast

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -611,3 +611,54 @@
611611
)
612612
)
613613

614+
;; When refining to nullcontref we cannot use a cast. Just emit a null.
615+
(module
616+
(rec
617+
;; NRML: (rec
618+
;; NRML-NEXT: (type $func (sub (func)))
619+
;; GUFA: (rec
620+
;; GUFA-NEXT: (type $func (sub (func)))
621+
(type $func (sub (func)))
622+
;; NRML: (type $cont (sub (cont $func)))
623+
;; GUFA: (type $cont (sub (cont $func)))
624+
(type $cont (sub (cont $func)))
625+
;; NRML: (type $struct (struct (field (ref null $cont))))
626+
;; GUFA: (type $struct (struct (field nullcontref)))
627+
(type $struct (struct (field (ref null $cont))))
628+
)
629+
630+
;; NRML: (type $3 (func))
631+
632+
;; NRML: (func $test (type $3)
633+
;; NRML-NEXT: (local $null (ref null $cont))
634+
;; NRML-NEXT: (drop
635+
;; NRML-NEXT: (struct.new $struct
636+
;; NRML-NEXT: (local.get $null)
637+
;; NRML-NEXT: )
638+
;; NRML-NEXT: )
639+
;; NRML-NEXT: )
640+
;; GUFA: (type $3 (func))
641+
642+
;; GUFA: (func $test (type $3)
643+
;; GUFA-NEXT: (local $null (ref null $cont))
644+
;; GUFA-NEXT: (drop
645+
;; GUFA-NEXT: (struct.new $struct
646+
;; GUFA-NEXT: (block (result nullcontref)
647+
;; GUFA-NEXT: (drop
648+
;; GUFA-NEXT: (local.get $null)
649+
;; GUFA-NEXT: )
650+
;; GUFA-NEXT: (ref.null nocont)
651+
;; GUFA-NEXT: )
652+
;; GUFA-NEXT: )
653+
;; GUFA-NEXT: )
654+
;; GUFA-NEXT: )
655+
(func $test
656+
(local $null (ref null $cont))
657+
(drop
658+
(struct.new $struct
659+
(local.get $null)
660+
)
661+
)
662+
)
663+
)
664+

test/lit/passes/type-refining.wast

Lines changed: 32 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1252,23 +1252,29 @@
12521252
(tag $tag)
12531253

12541254
;; CHECK: (func $struct.new (type $2) (param $extern externref) (result anyref)
1255-
;; CHECK-NEXT: (struct.new $A
1256-
;; CHECK-NEXT: (ref.cast (ref noextern)
1257-
;; CHECK-NEXT: (try (result externref)
1258-
;; CHECK-NEXT: (do
1259-
;; CHECK-NEXT: (struct.get $A 0
1260-
;; CHECK-NEXT: (struct.new $A
1261-
;; CHECK-NEXT: (ref.as_non_null
1262-
;; CHECK-NEXT: (ref.null noextern)
1255+
;; CHECK-NEXT: (block ;; (replaces unreachable StructNew we can't emit)
1256+
;; CHECK-NEXT: (drop
1257+
;; CHECK-NEXT: (block
1258+
;; CHECK-NEXT: (drop
1259+
;; CHECK-NEXT: (try (result externref)
1260+
;; CHECK-NEXT: (do
1261+
;; CHECK-NEXT: (struct.get $A 0
1262+
;; CHECK-NEXT: (struct.new $A
1263+
;; CHECK-NEXT: (ref.as_non_null
1264+
;; CHECK-NEXT: (ref.null noextern)
1265+
;; CHECK-NEXT: )
1266+
;; CHECK-NEXT: )
12631267
;; CHECK-NEXT: )
12641268
;; CHECK-NEXT: )
1269+
;; CHECK-NEXT: (catch $tag
1270+
;; CHECK-NEXT: (local.get $extern)
1271+
;; CHECK-NEXT: )
12651272
;; CHECK-NEXT: )
12661273
;; CHECK-NEXT: )
1267-
;; CHECK-NEXT: (catch $tag
1268-
;; CHECK-NEXT: (local.get $extern)
1269-
;; CHECK-NEXT: )
1274+
;; CHECK-NEXT: (unreachable)
12701275
;; CHECK-NEXT: )
12711276
;; CHECK-NEXT: )
1277+
;; CHECK-NEXT: (unreachable)
12721278
;; CHECK-NEXT: )
12731279
;; CHECK-NEXT: )
12741280
(func $struct.new (param $extern externref) (result anyref)
@@ -1282,8 +1288,8 @@
12821288
;; type than the body.
12831289
;;
12841290
;; In such situations we rely on other optimizations to improve things, like
1285-
;; getting rid of the catch in this case. In this pass we add a cast to get
1286-
;; things to validate, which should be removable by other passes later on.
1291+
;; getting rid of the catch in this case. In this pass we add an unreachable
1292+
;; for the uninhabitable type, which fixes validation.
12871293
(struct.new $A
12881294
(try (result externref)
12891295
(do
@@ -1305,21 +1311,24 @@
13051311
;; CHECK: (func $struct.set (type $3) (param $ref (ref $A)) (param $extern externref)
13061312
;; CHECK-NEXT: (struct.set $A 0
13071313
;; CHECK-NEXT: (local.get $ref)
1308-
;; CHECK-NEXT: (ref.cast (ref noextern)
1309-
;; CHECK-NEXT: (try (result externref)
1310-
;; CHECK-NEXT: (do
1311-
;; CHECK-NEXT: (struct.get $A 0
1312-
;; CHECK-NEXT: (struct.new $A
1313-
;; CHECK-NEXT: (ref.as_non_null
1314-
;; CHECK-NEXT: (ref.null noextern)
1314+
;; CHECK-NEXT: (block
1315+
;; CHECK-NEXT: (drop
1316+
;; CHECK-NEXT: (try (result externref)
1317+
;; CHECK-NEXT: (do
1318+
;; CHECK-NEXT: (struct.get $A 0
1319+
;; CHECK-NEXT: (struct.new $A
1320+
;; CHECK-NEXT: (ref.as_non_null
1321+
;; CHECK-NEXT: (ref.null noextern)
1322+
;; CHECK-NEXT: )
13151323
;; CHECK-NEXT: )
13161324
;; CHECK-NEXT: )
13171325
;; CHECK-NEXT: )
1318-
;; CHECK-NEXT: )
1319-
;; CHECK-NEXT: (catch $tag
1320-
;; CHECK-NEXT: (local.get $extern)
1326+
;; CHECK-NEXT: (catch $tag
1327+
;; CHECK-NEXT: (local.get $extern)
1328+
;; CHECK-NEXT: )
13211329
;; CHECK-NEXT: )
13221330
;; CHECK-NEXT: )
1331+
;; CHECK-NEXT: (unreachable)
13231332
;; CHECK-NEXT: )
13241333
;; CHECK-NEXT: )
13251334
;; CHECK-NEXT: )

0 commit comments

Comments
 (0)