From 60be01bbcd9988fbd1835280e08c051bd5e55a7e Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Thu, 7 Mar 2024 18:24:02 -0800 Subject: [PATCH] Fix printing of bulk array ops When the bulk array ops had unreachable or null array types, they were replaced with blocks, but not using the correct code that also prints all their children as dropped followed by an unreachable. This meant that the text output in those cases did not parse as a valid module. Fix the bug. A follow-up PR will simplify the code to prevent similar bugs from occurring in the future. --- src/passes/Print.cpp | 19 ++++ test/lit/passes/gufa-refs.wast | 17 +++- test/lit/passes/type-generalizing.wast | 126 ++++++++++++++++++------- test/lit/passes/unsubtyping.wast | 116 +++++++++++++++++------ 4 files changed, 212 insertions(+), 66 deletions(-) diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index b0c83c43715..af91a319451 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -289,6 +289,25 @@ struct PrintSExpression : public UnifiedExpressionVisitor { void visitArrayGet(ArrayGet* curr) { maybePrintUnreachableOrNullReplacement(curr, curr->ref->type); } + void visitArrayCopy(ArrayCopy* curr) { + if (curr->srcRef->type == Type::unreachable || + curr->destRef->type == Type::unreachable || + curr->srcRef->type.isNull() || curr->destRef->type.isNull()) { + maybePrintUnreachableOrNullReplacement(curr, Type::unreachable); + return; + } + visitExpression(curr); + } + void visitArrayFill(ArrayFill* curr) { + maybePrintUnreachableOrNullReplacement(curr, curr->ref->type); + } + void visitArrayInitData(ArrayInitData* curr) { + maybePrintUnreachableOrNullReplacement(curr, curr->ref->type); + } + void visitArrayInitElem(ArrayInitElem* curr) { + maybePrintUnreachableOrNullReplacement(curr, curr->ref->type); + } + // Module-level visitors void handleSignature(HeapType curr, Name name = Name()); void visitExport(Export* curr); diff --git a/test/lit/passes/gufa-refs.wast b/test/lit/passes/gufa-refs.wast index e6e5d0bde40..4be45e4ca18 100644 --- a/test/lit/passes/gufa-refs.wast +++ b/test/lit/passes/gufa-refs.wast @@ -5632,11 +5632,20 @@ (data $0 "") ;; CHECK: (func $test (type $0) - ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block ;; (replaces unreachable ArrayInitData we can't emit) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: (unreachable) - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $test diff --git a/test/lit/passes/type-generalizing.wast b/test/lit/passes/type-generalizing.wast index 2c1714a46d2..f927086dee3 100644 --- a/test/lit/passes/type-generalizing.wast +++ b/test/lit/passes/type-generalizing.wast @@ -1383,12 +1383,23 @@ ;; CHECK: (func $array-copy-impossible-dest (type $0) ;; CHECK-NEXT: (local $dest nullref) ;; CHECK-NEXT: (local $src anyref) - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (local.get $dest) - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: (local.get $src) - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (block ;; (replaces unreachable ArrayCopy we can't emit) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $dest) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $src) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $array-copy-impossible-dest @@ -1408,12 +1419,23 @@ ;; CHECK: (func $array-copy-impossible-src (type $0) ;; CHECK-NEXT: (local $dest anyref) ;; CHECK-NEXT: (local $src nullref) - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (local.get $dest) - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: (local.get $src) - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (block ;; (replaces unreachable ArrayCopy we can't emit) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $dest) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $src) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $array-copy-impossible-src @@ -1433,12 +1455,23 @@ ;; CHECK: (func $array-copy-impossible-both (type $0) ;; CHECK-NEXT: (local $dest nullref) ;; CHECK-NEXT: (local $src nullref) - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (local.get $dest) - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: (local.get $src) - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (block ;; (replaces unreachable ArrayCopy we can't emit) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $dest) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $src) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $array-copy-impossible-both @@ -1481,11 +1514,20 @@ ;; CHECK: (func $array-fill-impossible (type $0) ;; CHECK-NEXT: (local $ref nullref) ;; CHECK-NEXT: (local $val anyref) - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (local.get $ref) - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: (local.get $val) - ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (block ;; (replaces unreachable ArrayFill we can't emit) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $ref) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $val) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $array-fill-impossible @@ -1522,11 +1564,20 @@ ;; CHECK: (func $array-init-data-impossible (type $0) ;; CHECK-NEXT: (local $ref nullref) - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (local.get $ref) - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (block ;; (replaces unreachable ArrayInitData we can't emit) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $ref) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $array-init-data-impossible @@ -1562,11 +1613,20 @@ ;; CHECK: (func $array-init-elem-impossible (type $0) ;; CHECK-NEXT: (local $ref nullref) - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (local.get $ref) - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (block ;; (replaces unreachable ArrayInitElem we can't emit) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $ref) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $array-init-elem-impossible diff --git a/test/lit/passes/unsubtyping.wast b/test/lit/passes/unsubtyping.wast index 8408c2e7d95..204b76ef718 100644 --- a/test/lit/passes/unsubtyping.wast +++ b/test/lit/passes/unsubtyping.wast @@ -1441,19 +1441,41 @@ ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block ;; (replaces unreachable ArrayCopy we can't emit) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (array.new_fixed $sub-array 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: (unreachable) - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: (array.new_fixed $sub-array 0) - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (array.new_fixed $super-array 0) - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: (ref.null none) - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (block ;; (replaces unreachable ArrayCopy we can't emit) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (array.new_fixed $super-array 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $array-copy @@ -1503,17 +1525,35 @@ ;; CHECK-NEXT: (struct.new_default $sub) ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block ;; (replaces unreachable ArrayFill we can't emit) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new_default $sub) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: (unreachable) - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: (struct.new_default $sub) - ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (ref.null none) - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: (struct.new_default $sub) - ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (block ;; (replaces unreachable ArrayFill we can't emit) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new_default $sub) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $array-fill @@ -1564,17 +1604,35 @@ ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block ;; (replaces unreachable ArrayInitElem we can't emit) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: (unreachable) - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (ref.null none) - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (block ;; (replaces unreachable ArrayInitElem we can't emit) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $array-init-elem