diff options
Diffstat (limited to 'test/lit')
-rw-r--r-- | test/lit/help/wasm-opt.test | 6 | ||||
-rw-r--r-- | test/lit/help/wasm2js.test | 6 | ||||
-rw-r--r-- | test/lit/passes/monomorphize.wast | 590 |
3 files changed, 602 insertions, 0 deletions
diff --git a/test/lit/help/wasm-opt.test b/test/lit/help/wasm-opt.test index 281411cca..4fd50abcc 100644 --- a/test/lit/help/wasm-opt.test +++ b/test/lit/help/wasm-opt.test @@ -273,6 +273,12 @@ ;; CHECK-NEXT: --mod-asyncify-never-unwind apply the assumption that ;; CHECK-NEXT: asyncify never unwinds ;; CHECK-NEXT: +;; CHECK-NEXT: --monomorphize creates specialized versions of +;; CHECK-NEXT: functions +;; CHECK-NEXT: +;; CHECK-NEXT: --monomorphize-always creates specialized versions of +;; CHECK-NEXT: functions (even if unhelpful) +;; CHECK-NEXT: ;; CHECK-NEXT: --multi-memory-lowering combines multiple memories into ;; CHECK-NEXT: a single memory ;; CHECK-NEXT: diff --git a/test/lit/help/wasm2js.test b/test/lit/help/wasm2js.test index f190c8fed..7a782c872 100644 --- a/test/lit/help/wasm2js.test +++ b/test/lit/help/wasm2js.test @@ -232,6 +232,12 @@ ;; CHECK-NEXT: --mod-asyncify-never-unwind apply the assumption that ;; CHECK-NEXT: asyncify never unwinds ;; CHECK-NEXT: +;; CHECK-NEXT: --monomorphize creates specialized versions of +;; CHECK-NEXT: functions +;; CHECK-NEXT: +;; CHECK-NEXT: --monomorphize-always creates specialized versions of +;; CHECK-NEXT: functions (even if unhelpful) +;; CHECK-NEXT: ;; CHECK-NEXT: --multi-memory-lowering combines multiple memories into ;; CHECK-NEXT: a single memory ;; CHECK-NEXT: diff --git a/test/lit/passes/monomorphize.wast b/test/lit/passes/monomorphize.wast new file mode 100644 index 000000000..1cd219d08 --- /dev/null +++ b/test/lit/passes/monomorphize.wast @@ -0,0 +1,590 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. + +;; Test in both "always" mode, which always monomorphizes, and in "careful" +;; mode which does it only when it appears to actually help. + +;; RUN: foreach %s %t wasm-opt --nominal --monomorphize-always -all -S -o - | filecheck %s --check-prefix ALWAYS +;; RUN: foreach %s %t wasm-opt --nominal --monomorphize -all -S -o - | filecheck %s --check-prefix CAREFUL + +(module + ;; ALWAYS: (type $A (struct_subtype data)) + ;; CAREFUL: (type $A (struct_subtype data)) + (type $A (struct_subtype data)) + ;; ALWAYS: (type $B (struct_subtype $A)) + ;; CAREFUL: (type $B (struct_subtype $A)) + (type $B (struct_subtype $A)) + + ;; ALWAYS: (type $ref|$A|_=>_none (func_subtype (param (ref $A)) func)) + + ;; ALWAYS: (type $none_=>_none (func_subtype func)) + + ;; ALWAYS: (type $ref|$B|_=>_none (func_subtype (param (ref $B)) func)) + + ;; ALWAYS: (import "a" "b" (func $import (param (ref $A)))) + ;; CAREFUL: (type $ref|$A|_=>_none (func_subtype (param (ref $A)) func)) + + ;; CAREFUL: (type $none_=>_none (func_subtype func)) + + ;; CAREFUL: (import "a" "b" (func $import (param (ref $A)))) + (import "a" "b" (func $import (param (ref $A)))) + + ;; ALWAYS: (func $calls (type $none_=>_none) + ;; ALWAYS-NEXT: (call $refinable + ;; ALWAYS-NEXT: (struct.new_default $A) + ;; ALWAYS-NEXT: ) + ;; ALWAYS-NEXT: (call $refinable + ;; ALWAYS-NEXT: (struct.new_default $A) + ;; ALWAYS-NEXT: ) + ;; ALWAYS-NEXT: (call $refinable_0 + ;; ALWAYS-NEXT: (struct.new_default $B) + ;; ALWAYS-NEXT: ) + ;; ALWAYS-NEXT: (call $refinable_0 + ;; ALWAYS-NEXT: (struct.new_default $B) + ;; ALWAYS-NEXT: ) + ;; ALWAYS-NEXT: ) + ;; CAREFUL: (func $calls (type $none_=>_none) + ;; CAREFUL-NEXT: (call $refinable + ;; CAREFUL-NEXT: (struct.new_default $A) + ;; CAREFUL-NEXT: ) + ;; CAREFUL-NEXT: (call $refinable + ;; CAREFUL-NEXT: (struct.new_default $A) + ;; CAREFUL-NEXT: ) + ;; CAREFUL-NEXT: (call $refinable + ;; CAREFUL-NEXT: (struct.new_default $B) + ;; CAREFUL-NEXT: ) + ;; CAREFUL-NEXT: (call $refinable + ;; CAREFUL-NEXT: (struct.new_default $B) + ;; CAREFUL-NEXT: ) + ;; CAREFUL-NEXT: ) + (func $calls + ;; Two calls with $A, two with $B. The calls to $B should both go to the + ;; same new function which has a refined parameter of $B. + ;; + ;; However, in CAREFUL mode we won't do that, as there is no helpful + ;; improvement in the target functions even with the refined types. + (call $refinable + (struct.new $A) + ) + (call $refinable + (struct.new $A) + ) + (call $refinable + (struct.new $B) + ) + (call $refinable + (struct.new $B) + ) + ) + + ;; ALWAYS: (func $call-import (type $none_=>_none) + ;; ALWAYS-NEXT: (call $import + ;; ALWAYS-NEXT: (struct.new_default $B) + ;; ALWAYS-NEXT: ) + ;; ALWAYS-NEXT: ) + ;; CAREFUL: (func $call-import (type $none_=>_none) + ;; CAREFUL-NEXT: (call $import + ;; CAREFUL-NEXT: (struct.new_default $B) + ;; CAREFUL-NEXT: ) + ;; CAREFUL-NEXT: ) + (func $call-import + ;; Calls to imports are left as they are. + (call $import + (struct.new $B) + ) + ) + + ;; ALWAYS: (func $refinable (type $ref|$A|_=>_none) (param $ref (ref $A)) + ;; ALWAYS-NEXT: (drop + ;; ALWAYS-NEXT: (local.get $ref) + ;; ALWAYS-NEXT: ) + ;; ALWAYS-NEXT: ) + ;; CAREFUL: (func $refinable (type $ref|$A|_=>_none) (param $0 (ref $A)) + ;; CAREFUL-NEXT: (nop) + ;; CAREFUL-NEXT: ) + (func $refinable (param $ref (ref $A)) + ;; Helper function for the above. Use the parameter to see we update types + ;; etc when we make a refined version of the function (if we didn't, + ;; validation would error). + ;; + ;; In CAREFUL mode we optimize to check if refined types help, which has the + ;; side effect of optimizing the body of this function into a nop. + (drop + (local.get $ref) + ) + ) +) + + +;; ALWAYS: (func $refinable_0 (type $ref|$B|_=>_none) (param $ref (ref $B)) +;; ALWAYS-NEXT: (drop +;; ALWAYS-NEXT: (local.get $ref) +;; ALWAYS-NEXT: ) +;; ALWAYS-NEXT: ) +(module + ;; As above, but now the refinable function uses the local in a way that + ;; requires a fixup. + + ;; ALWAYS: (type $A (struct_subtype data)) + ;; CAREFUL: (type $none_=>_none (func_subtype func)) + + ;; CAREFUL: (type $A (struct_subtype data)) + (type $A (struct_subtype data)) + ;; ALWAYS: (type $B (struct_subtype $A)) + ;; CAREFUL: (type $B (struct_subtype $A)) + (type $B (struct_subtype $A)) + + + + ;; ALWAYS: (type $none_=>_none (func_subtype func)) + + ;; ALWAYS: (type $ref|$A|_=>_none (func_subtype (param (ref $A)) func)) + + ;; ALWAYS: (type $ref|$B|_=>_none (func_subtype (param (ref $B)) func)) + + ;; ALWAYS: (func $calls (type $none_=>_none) + ;; ALWAYS-NEXT: (call $refinable_0 + ;; ALWAYS-NEXT: (struct.new_default $B) + ;; ALWAYS-NEXT: ) + ;; ALWAYS-NEXT: ) + ;; CAREFUL: (type $ref|$A|_=>_none (func_subtype (param (ref $A)) func)) + + ;; CAREFUL: (func $calls (type $none_=>_none) + ;; CAREFUL-NEXT: (call $refinable + ;; CAREFUL-NEXT: (struct.new_default $B) + ;; CAREFUL-NEXT: ) + ;; CAREFUL-NEXT: ) + (func $calls + (call $refinable + (struct.new $B) + ) + ) + + ;; ALWAYS: (func $refinable (type $ref|$A|_=>_none) (param $ref (ref $A)) + ;; ALWAYS-NEXT: (local $unref (ref $A)) + ;; ALWAYS-NEXT: (local.set $unref + ;; ALWAYS-NEXT: (local.get $ref) + ;; ALWAYS-NEXT: ) + ;; ALWAYS-NEXT: (local.set $ref + ;; ALWAYS-NEXT: (local.get $unref) + ;; ALWAYS-NEXT: ) + ;; ALWAYS-NEXT: ) + ;; CAREFUL: (func $refinable (type $ref|$A|_=>_none) (param $0 (ref $A)) + ;; CAREFUL-NEXT: (nop) + ;; CAREFUL-NEXT: ) + (func $refinable (param $ref (ref $A)) + (local $unref (ref $A)) + (local.set $unref + (local.get $ref) + ) + ;; If we refine $ref then this set will be invalid - we'd be setting a less- + ;; refined type into a local/param that is more refined. We should fix this + ;; up by using a temp local. + (local.set $ref + (local.get $unref) + ) + ) +) + + +;; ALWAYS: (func $refinable_0 (type $ref|$B|_=>_none) (param $ref (ref $B)) +;; ALWAYS-NEXT: (local $unref (ref $A)) +;; ALWAYS-NEXT: (local $2 (ref $A)) +;; ALWAYS-NEXT: (local.set $2 +;; ALWAYS-NEXT: (local.get $ref) +;; ALWAYS-NEXT: ) +;; ALWAYS-NEXT: (block +;; ALWAYS-NEXT: (local.set $unref +;; ALWAYS-NEXT: (local.get $2) +;; ALWAYS-NEXT: ) +;; ALWAYS-NEXT: (local.set $2 +;; ALWAYS-NEXT: (local.get $unref) +;; ALWAYS-NEXT: ) +;; ALWAYS-NEXT: ) +;; ALWAYS-NEXT: ) +(module + ;; Multiple refinings of the same function, and of different functions. + + ;; ALWAYS: (type $A (struct_subtype data)) + ;; CAREFUL: (type $none_=>_none (func_subtype func)) + + ;; CAREFUL: (type $A (struct_subtype data)) + (type $A (struct_subtype data)) + ;; ALWAYS: (type $B (struct_subtype $A)) + ;; CAREFUL: (type $B (struct_subtype $A)) + (type $B (struct_subtype $A)) + + ;; ALWAYS: (type $none_=>_none (func_subtype func)) + + ;; ALWAYS: (type $C (struct_subtype $B)) + ;; CAREFUL: (type $ref|$A|_=>_none (func_subtype (param (ref $A)) func)) + + ;; CAREFUL: (type $C (struct_subtype $B)) + (type $C (struct_subtype $B)) + + ;; ALWAYS: (type $ref|$A|_=>_none (func_subtype (param (ref $A)) func)) + + ;; ALWAYS: (type $ref|$B|_=>_none (func_subtype (param (ref $B)) func)) + + ;; ALWAYS: (type $ref|$C|_=>_none (func_subtype (param (ref $C)) func)) + + ;; ALWAYS: (func $calls1 (type $none_=>_none) + ;; ALWAYS-NEXT: (call $refinable1 + ;; ALWAYS-NEXT: (struct.new_default $A) + ;; ALWAYS-NEXT: ) + ;; ALWAYS-NEXT: (call $refinable1_0 + ;; ALWAYS-NEXT: (struct.new_default $B) + ;; ALWAYS-NEXT: ) + ;; ALWAYS-NEXT: ) + ;; CAREFUL: (func $calls1 (type $none_=>_none) + ;; CAREFUL-NEXT: (call $refinable1 + ;; CAREFUL-NEXT: (struct.new_default $A) + ;; CAREFUL-NEXT: ) + ;; CAREFUL-NEXT: (call $refinable1 + ;; CAREFUL-NEXT: (struct.new_default $B) + ;; CAREFUL-NEXT: ) + ;; CAREFUL-NEXT: ) + (func $calls1 + (call $refinable1 + (struct.new $A) + ) + (call $refinable1 + (struct.new $B) + ) + ) + + ;; ALWAYS: (func $calls2 (type $none_=>_none) + ;; ALWAYS-NEXT: (call $refinable1_1 + ;; ALWAYS-NEXT: (struct.new_default $C) + ;; ALWAYS-NEXT: ) + ;; ALWAYS-NEXT: (call $refinable2_0 + ;; ALWAYS-NEXT: (struct.new_default $B) + ;; ALWAYS-NEXT: ) + ;; ALWAYS-NEXT: ) + ;; CAREFUL: (func $calls2 (type $none_=>_none) + ;; CAREFUL-NEXT: (call $refinable1 + ;; CAREFUL-NEXT: (struct.new_default $C) + ;; CAREFUL-NEXT: ) + ;; CAREFUL-NEXT: (call $refinable2 + ;; CAREFUL-NEXT: (struct.new_default $B) + ;; CAREFUL-NEXT: ) + ;; CAREFUL-NEXT: ) + (func $calls2 + (call $refinable1 + (struct.new $C) + ) + (call $refinable2 + (struct.new $B) + ) + ) + + ;; ALWAYS: (func $refinable1 (type $ref|$A|_=>_none) (param $ref (ref $A)) + ;; ALWAYS-NEXT: (drop + ;; ALWAYS-NEXT: (local.get $ref) + ;; ALWAYS-NEXT: ) + ;; ALWAYS-NEXT: ) + ;; CAREFUL: (func $refinable1 (type $ref|$A|_=>_none) (param $0 (ref $A)) + ;; CAREFUL-NEXT: (nop) + ;; CAREFUL-NEXT: ) + (func $refinable1 (param $ref (ref $A)) + (drop + (local.get $ref) + ) + ) + + ;; ALWAYS: (func $refinable2 (type $ref|$A|_=>_none) (param $ref (ref $A)) + ;; ALWAYS-NEXT: (drop + ;; ALWAYS-NEXT: (local.get $ref) + ;; ALWAYS-NEXT: ) + ;; ALWAYS-NEXT: ) + ;; CAREFUL: (func $refinable2 (type $ref|$A|_=>_none) (param $0 (ref $A)) + ;; CAREFUL-NEXT: (nop) + ;; CAREFUL-NEXT: ) + (func $refinable2 (param $ref (ref $A)) + (drop + (local.get $ref) + ) + ) +) + +;; ALWAYS: (func $refinable1_0 (type $ref|$B|_=>_none) (param $ref (ref $B)) +;; ALWAYS-NEXT: (drop +;; ALWAYS-NEXT: (local.get $ref) +;; ALWAYS-NEXT: ) +;; ALWAYS-NEXT: ) + +;; ALWAYS: (func $refinable1_1 (type $ref|$C|_=>_none) (param $ref (ref $C)) +;; ALWAYS-NEXT: (drop +;; ALWAYS-NEXT: (local.get $ref) +;; ALWAYS-NEXT: ) +;; ALWAYS-NEXT: ) + +;; ALWAYS: (func $refinable2_0 (type $ref|$B|_=>_none) (param $ref (ref $B)) +;; ALWAYS-NEXT: (drop +;; ALWAYS-NEXT: (local.get $ref) +;; ALWAYS-NEXT: ) +;; ALWAYS-NEXT: ) +(module + ;; A case where even CAREFUL mode will monomorphize, as it helps the target + ;; function get optimized better. + + ;; ALWAYS: (type $A (struct_subtype data)) + ;; CAREFUL: (type $A (struct_subtype data)) + (type $A (struct_subtype data)) + + ;; ALWAYS: (type $B (struct_subtype $A)) + ;; CAREFUL: (type $B (struct_subtype $A)) + (type $B (struct_subtype $A)) + + ;; ALWAYS: (type $ref|$B|_=>_none (func_subtype (param (ref $B)) func)) + + ;; ALWAYS: (type $none_=>_none (func_subtype func)) + + ;; ALWAYS: (type $ref|$A|_=>_none (func_subtype (param (ref $A)) func)) + + ;; ALWAYS: (import "a" "b" (func $import (param (ref $B)))) + + ;; ALWAYS: (global $global (mut i32) (i32.const 1)) + ;; CAREFUL: (type $ref|$B|_=>_none (func_subtype (param (ref $B)) func)) + + ;; CAREFUL: (type $none_=>_none (func_subtype func)) + + ;; CAREFUL: (type $ref|$A|_=>_none (func_subtype (param (ref $A)) func)) + + ;; CAREFUL: (import "a" "b" (func $import (param (ref $B)))) + + ;; CAREFUL: (global $global (mut i32) (i32.const 1)) + (global $global (mut i32) (i32.const 1)) + + (import "a" "b" (func $import (param (ref $B)))) + + ;; ALWAYS: (func $calls (type $none_=>_none) + ;; ALWAYS-NEXT: (call $refinable + ;; ALWAYS-NEXT: (struct.new_default $A) + ;; ALWAYS-NEXT: ) + ;; ALWAYS-NEXT: (call $refinable + ;; ALWAYS-NEXT: (struct.new_default $A) + ;; ALWAYS-NEXT: ) + ;; ALWAYS-NEXT: (call $refinable_0 + ;; ALWAYS-NEXT: (struct.new_default $B) + ;; ALWAYS-NEXT: ) + ;; ALWAYS-NEXT: (call $refinable_0 + ;; ALWAYS-NEXT: (struct.new_default $B) + ;; ALWAYS-NEXT: ) + ;; ALWAYS-NEXT: ) + ;; CAREFUL: (func $calls (type $none_=>_none) + ;; CAREFUL-NEXT: (call $refinable + ;; CAREFUL-NEXT: (struct.new_default $A) + ;; CAREFUL-NEXT: ) + ;; CAREFUL-NEXT: (call $refinable + ;; CAREFUL-NEXT: (struct.new_default $A) + ;; CAREFUL-NEXT: ) + ;; CAREFUL-NEXT: (call $refinable_0 + ;; CAREFUL-NEXT: (struct.new_default $B) + ;; CAREFUL-NEXT: ) + ;; CAREFUL-NEXT: (call $refinable_0 + ;; CAREFUL-NEXT: (struct.new_default $B) + ;; CAREFUL-NEXT: ) + ;; CAREFUL-NEXT: ) + (func $calls + ;; The calls sending $B will switch to calling a refined version, as the + ;; refined version is better, even in CAREFUL mode. + (call $refinable + (struct.new $A) + ) + (call $refinable + (struct.new $A) + ) + (call $refinable + (struct.new $B) + ) + (call $refinable + (struct.new $B) + ) + ) + + ;; ALWAYS: (func $refinable (type $ref|$A|_=>_none) (param $ref (ref $A)) + ;; ALWAYS-NEXT: (local $x (ref $A)) + ;; ALWAYS-NEXT: (call $import + ;; ALWAYS-NEXT: (ref.cast_static $B + ;; ALWAYS-NEXT: (local.get $ref) + ;; ALWAYS-NEXT: ) + ;; ALWAYS-NEXT: ) + ;; ALWAYS-NEXT: (local.set $x + ;; ALWAYS-NEXT: (select (result (ref $A)) + ;; ALWAYS-NEXT: (local.get $ref) + ;; ALWAYS-NEXT: (struct.new_default $B) + ;; ALWAYS-NEXT: (global.get $global) + ;; ALWAYS-NEXT: ) + ;; ALWAYS-NEXT: ) + ;; ALWAYS-NEXT: (call $import + ;; ALWAYS-NEXT: (ref.cast_static $B + ;; ALWAYS-NEXT: (local.get $x) + ;; ALWAYS-NEXT: ) + ;; ALWAYS-NEXT: ) + ;; ALWAYS-NEXT: (call $import + ;; ALWAYS-NEXT: (ref.cast_static $B + ;; ALWAYS-NEXT: (local.get $x) + ;; ALWAYS-NEXT: ) + ;; ALWAYS-NEXT: ) + ;; ALWAYS-NEXT: (call $import + ;; ALWAYS-NEXT: (ref.cast_static $B + ;; ALWAYS-NEXT: (local.get $ref) + ;; ALWAYS-NEXT: ) + ;; ALWAYS-NEXT: ) + ;; ALWAYS-NEXT: ) + ;; CAREFUL: (func $refinable (type $ref|$A|_=>_none) (param $0 (ref $A)) + ;; CAREFUL-NEXT: (local $1 (ref $A)) + ;; CAREFUL-NEXT: (call $import + ;; CAREFUL-NEXT: (ref.cast_static $B + ;; CAREFUL-NEXT: (local.get $0) + ;; CAREFUL-NEXT: ) + ;; CAREFUL-NEXT: ) + ;; CAREFUL-NEXT: (call $import + ;; CAREFUL-NEXT: (ref.cast_static $B + ;; CAREFUL-NEXT: (local.tee $1 + ;; CAREFUL-NEXT: (select (result (ref $A)) + ;; CAREFUL-NEXT: (local.get $0) + ;; CAREFUL-NEXT: (struct.new_default $B) + ;; CAREFUL-NEXT: (global.get $global) + ;; CAREFUL-NEXT: ) + ;; CAREFUL-NEXT: ) + ;; CAREFUL-NEXT: ) + ;; CAREFUL-NEXT: ) + ;; CAREFUL-NEXT: (call $import + ;; CAREFUL-NEXT: (ref.cast_static $B + ;; CAREFUL-NEXT: (local.get $1) + ;; CAREFUL-NEXT: ) + ;; CAREFUL-NEXT: ) + ;; CAREFUL-NEXT: (call $import + ;; CAREFUL-NEXT: (ref.cast_static $B + ;; CAREFUL-NEXT: (local.get $0) + ;; CAREFUL-NEXT: ) + ;; CAREFUL-NEXT: ) + ;; CAREFUL-NEXT: ) + (func $refinable (param $ref (ref $A)) + (local $x (ref $A)) + ;; The refined version of this function will not have the cast, since + ;; optimizations manage to remove it using the more refined type. + ;; + ;; (That is the case in CAREFUL mode, which optimizes; in ALWAYS mode the + ;; cast will remain since we monomorphize without bothering to optimize and + ;; see if there is any benefit.) + (call $import + (ref.cast_static $B + (local.get $ref) + ) + ) + ;; Also copy the param into a local. The local should get refined to $B in + ;; the refined function in CAREFUL mode. + (local.set $x + ;; Use a select here so optimizations don't just merge $x and $ref. + (select (result (ref $A)) + (local.get $ref) + (struct.new $B) + (global.get $global) + ) + ) + (call $import + (ref.cast_static $B + (local.get $x) + ) + ) + (call $import + (ref.cast_static $B + (local.get $x) + ) + ) + ;; Another use of $ref, also to avoid opts merging $x and $ref. + (call $import + (ref.cast_static $B + (local.get $ref) + ) + ) + ) +) + +;; ALWAYS: (func $refinable_0 (type $ref|$B|_=>_none) (param $ref (ref $B)) +;; ALWAYS-NEXT: (local $x (ref $A)) +;; ALWAYS-NEXT: (call $import +;; ALWAYS-NEXT: (ref.cast_static $B +;; ALWAYS-NEXT: (local.get $ref) +;; ALWAYS-NEXT: ) +;; ALWAYS-NEXT: ) +;; ALWAYS-NEXT: (local.set $x +;; ALWAYS-NEXT: (select (result (ref $B)) +;; ALWAYS-NEXT: (local.get $ref) +;; ALWAYS-NEXT: (struct.new_default $B) +;; ALWAYS-NEXT: (global.get $global) +;; ALWAYS-NEXT: ) +;; ALWAYS-NEXT: ) +;; ALWAYS-NEXT: (call $import +;; ALWAYS-NEXT: (ref.cast_static $B +;; ALWAYS-NEXT: (local.get $x) +;; ALWAYS-NEXT: ) +;; ALWAYS-NEXT: ) +;; ALWAYS-NEXT: (call $import +;; ALWAYS-NEXT: (ref.cast_static $B +;; ALWAYS-NEXT: (local.get $x) +;; ALWAYS-NEXT: ) +;; ALWAYS-NEXT: ) +;; ALWAYS-NEXT: (call $import +;; ALWAYS-NEXT: (ref.cast_static $B +;; ALWAYS-NEXT: (local.get $ref) +;; ALWAYS-NEXT: ) +;; ALWAYS-NEXT: ) +;; ALWAYS-NEXT: ) + +;; CAREFUL: (func $refinable_0 (type $ref|$B|_=>_none) (param $0 (ref $B)) +;; CAREFUL-NEXT: (local $1 (ref $B)) +;; CAREFUL-NEXT: (call $import +;; CAREFUL-NEXT: (local.get $0) +;; CAREFUL-NEXT: ) +;; CAREFUL-NEXT: (call $import +;; CAREFUL-NEXT: (local.tee $1 +;; CAREFUL-NEXT: (select (result (ref $B)) +;; CAREFUL-NEXT: (local.get $0) +;; CAREFUL-NEXT: (struct.new_default $B) +;; CAREFUL-NEXT: (global.get $global) +;; CAREFUL-NEXT: ) +;; CAREFUL-NEXT: ) +;; CAREFUL-NEXT: ) +;; CAREFUL-NEXT: (call $import +;; CAREFUL-NEXT: (local.get $1) +;; CAREFUL-NEXT: ) +;; CAREFUL-NEXT: (call $import +;; CAREFUL-NEXT: (local.get $0) +;; CAREFUL-NEXT: ) +;; CAREFUL-NEXT: ) +(module + ;; Test that we avoid recursive calls. + + ;; ALWAYS: (type $ref|$A|_=>_none (func_subtype (param (ref $A)) func)) + + ;; ALWAYS: (type $A (struct_subtype data)) + ;; CAREFUL: (type $ref|$A|_=>_none (func_subtype (param (ref $A)) func)) + + ;; CAREFUL: (type $A (struct_subtype data)) + (type $A (struct_subtype data)) + ;; ALWAYS: (type $B (struct_subtype $A)) + ;; CAREFUL: (type $B (struct_subtype $A)) + (type $B (struct_subtype $A)) + + + ;; ALWAYS: (func $calls (type $ref|$A|_=>_none) (param $ref (ref $A)) + ;; ALWAYS-NEXT: (call $calls + ;; ALWAYS-NEXT: (struct.new_default $B) + ;; ALWAYS-NEXT: ) + ;; ALWAYS-NEXT: ) + ;; CAREFUL: (func $calls (type $ref|$A|_=>_none) (param $ref (ref $A)) + ;; CAREFUL-NEXT: (call $calls + ;; CAREFUL-NEXT: (struct.new_default $B) + ;; CAREFUL-NEXT: ) + ;; CAREFUL-NEXT: ) + (func $calls (param $ref (ref $A)) + ;; We should change nothing in this recursive call, even though we are + ;; sending a more refined type, so we could try to monomorphize in theory. + (call $calls + (struct.new $B) + ) + ) +) |