summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/passes/OptimizeInstructions.cpp23
-rw-r--r--test/lit/passes/optimize-instructions-gc-extern.wast39
2 files changed, 55 insertions, 7 deletions
diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp
index fff18b925..64d59ca1d 100644
--- a/src/passes/OptimizeInstructions.cpp
+++ b/src/passes/OptimizeInstructions.cpp
@@ -2248,8 +2248,27 @@ struct OptimizeInstructions
}
if (curr->op == ExternConvertAny || curr->op == AnyConvertExtern) {
- // We can't optimize these. Even removing a non-null cast is not valid as
- // they allow nulls to filter through, unlike other RefAs*.
+ // These pass nulls through, and we can reorder them with null traps:
+ //
+ // (any.convert_extern/extern.convert_any (ref.as_non_null.. ))
+ // =>
+ // (ref.as_non_null (any.convert_extern/extern.convert_any ..))
+ //
+ // By moving the RefAsNonNull outside, it may reach a position where it
+ // can be optimized (e.g. if the parent traps anyhow). And,
+ // ExternConvertAny/AnyConvertExtern cannot be folded with anything, so
+ // there is no harm to moving them inside.
+ if (auto* refAsChild = curr->value->dynCast<RefAs>()) {
+ if (refAsChild->op == RefAsNonNull) {
+ // Reorder and fix up the types.
+ curr->value = refAsChild->value;
+ curr->finalize();
+ refAsChild->value = curr;
+ refAsChild->finalize();
+ replaceCurrent(refAsChild);
+ }
+ }
+ // TODO: optimize away ExternConvertAny of AnyConvertExtern, etc.
return;
}
diff --git a/test/lit/passes/optimize-instructions-gc-extern.wast b/test/lit/passes/optimize-instructions-gc-extern.wast
index 658b2b115..067efb0ec 100644
--- a/test/lit/passes/optimize-instructions-gc-extern.wast
+++ b/test/lit/passes/optimize-instructions-gc-extern.wast
@@ -3,6 +3,9 @@
;; RUN: | filecheck %s
(module
+ ;; CHECK: (type $array (array (mut i8)))
+ (type $array (array (mut i8)))
+
;; CHECK: (func $extern.convert_any (type $0) (param $x anyref) (param $y externref)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (extern.convert_any
@@ -10,8 +13,8 @@
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (extern.convert_any
- ;; CHECK-NEXT: (ref.as_non_null
+ ;; CHECK-NEXT: (ref.as_non_null
+ ;; CHECK-NEXT: (extern.convert_any
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
@@ -22,20 +25,22 @@
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (any.convert_extern
- ;; CHECK-NEXT: (ref.as_non_null
+ ;; CHECK-NEXT: (ref.as_non_null
+ ;; CHECK-NEXT: (any.convert_extern
;; CHECK-NEXT: (local.get $y)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
- (func $extern.convert_any (export "ext") (param $x (ref null any)) (param $y (ref null extern))
+ (func $extern.convert_any (param $x (ref null any)) (param $y (ref null extern))
;; We should not change anything here, and also not hit an internal error.
(drop
(extern.convert_any
(local.get $x)
)
)
+ ;; We can reorder the externalize with the ref.as_non_null, which sometimes
+ ;; helps later optimizations, see below.
(drop
(extern.convert_any
(ref.as_non_null
@@ -43,6 +48,7 @@
)
)
)
+ ;; As the above two cases, but for internalize.
(drop
(any.convert_extern
(local.get $y)
@@ -56,4 +62,27 @@
)
)
)
+
+ ;; CHECK: (func $convert.optimize.parent (type $1) (param $ext externref)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (ref.cast (ref $array)
+ ;; CHECK-NEXT: (any.convert_extern
+ ;; CHECK-NEXT: (local.get $ext)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $convert.optimize.parent (param $ext externref)
+ ;; The ref.cast can fold in the ref.as_non_null, after it is moved
+ ;; outside of the any.convert_extern.
+ (drop
+ (ref.cast (ref null $array)
+ (any.convert_extern
+ (ref.as_non_null
+ (local.get $ext)
+ )
+ )
+ )
+ )
+ )
)