summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/lit/passes/cfp.wast298
1 files changed, 298 insertions, 0 deletions
diff --git a/test/lit/passes/cfp.wast b/test/lit/passes/cfp.wast
index 99fd9d209..90eecf01b 100644
--- a/test/lit/passes/cfp.wast
+++ b/test/lit/passes/cfp.wast
@@ -1872,3 +1872,301 @@
)
)
)
+
+(module
+ ;; CHECK: (type $struct (struct_subtype (field i32) data))
+ (type $struct (struct i32))
+
+ ;; CHECK: (type $none_=>_none (func_subtype func))
+
+ ;; CHECK: (global $global i32 (i32.const 42))
+ (global $global i32 (i32.const 42))
+
+ ;; CHECK: (func $test
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $struct
+ ;; CHECK-NEXT: (global.get $global)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block (result i32)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (ref.as_non_null
+ ;; CHECK-NEXT: (ref.null $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (global.get $global)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $test
+ ;; An immutable global is the only thing written to this field, so we can
+ ;; propagate the value to the struct.get and replace it with a global.get.
+ (drop
+ (struct.new $struct
+ (global.get $global)
+ )
+ )
+ (drop
+ (struct.get $struct 0
+ (ref.null $struct)
+ )
+ )
+ )
+)
+
+(module
+ ;; CHECK: (type $struct (struct_subtype (field i32) data))
+ (type $struct (struct i32))
+
+ ;; CHECK: (type $none_=>_none (func_subtype func))
+
+ ;; CHECK: (global $global (mut i32) (i32.const 42))
+ (global $global (mut i32) (i32.const 42))
+
+ ;; CHECK: (func $test
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $struct
+ ;; CHECK-NEXT: (global.get $global)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.get $struct 0
+ ;; CHECK-NEXT: (ref.null $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $test
+ ;; As above, but the global is *not* immutable, so we cannot optimize.
+ (drop
+ (struct.new $struct
+ (global.get $global)
+ )
+ )
+ (drop
+ (struct.get $struct 0
+ (ref.null $struct)
+ )
+ )
+ )
+)
+
+(module
+ ;; CHECK: (type $struct (struct_subtype (field (mut i32)) data))
+ (type $struct (struct (mut i32)))
+
+ ;; CHECK: (type $none_=>_none (func_subtype func))
+
+ ;; CHECK: (global $global i32 (i32.const 42))
+ (global $global i32 (i32.const 42))
+
+ ;; CHECK: (func $test
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $struct
+ ;; CHECK-NEXT: (global.get $global)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (struct.set $struct 0
+ ;; CHECK-NEXT: (ref.null $struct)
+ ;; CHECK-NEXT: (global.get $global)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block (result i32)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (ref.as_non_null
+ ;; CHECK-NEXT: (ref.null $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (global.get $global)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $test
+ (drop
+ (struct.new $struct
+ (global.get $global)
+ )
+ )
+ ;; As above, but there is another set of the field. It is the same, though,
+ ;; so that is fine. Also, the struct's field is now mutable as well to allow
+ ;; that, and that also does not prevent optimization.
+ (struct.set $struct 0
+ (ref.null $struct)
+ (global.get $global)
+ )
+ (drop
+ (struct.get $struct 0
+ (ref.null $struct)
+ )
+ )
+ )
+)
+
+(module
+ ;; CHECK: (type $struct (struct_subtype (field (mut i32)) data))
+ (type $struct (struct (mut i32)))
+
+ ;; CHECK: (type $none_=>_none (func_subtype func))
+
+ ;; CHECK: (global $global i32 (i32.const 42))
+ (global $global i32 (i32.const 42))
+ ;; CHECK: (global $global-2 i32 (i32.const 1337))
+ (global $global-2 i32 (i32.const 1337))
+
+ ;; CHECK: (func $test
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $struct
+ ;; CHECK-NEXT: (global.get $global)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (struct.set $struct 0
+ ;; CHECK-NEXT: (ref.null $struct)
+ ;; CHECK-NEXT: (global.get $global-2)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.get $struct 0
+ ;; CHECK-NEXT: (ref.null $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $test
+ (drop
+ (struct.new $struct
+ (global.get $global)
+ )
+ )
+ ;; As above, but set a different global, which prevents optimization.
+ (struct.set $struct 0
+ (ref.null $struct)
+ (global.get $global-2)
+ )
+ (drop
+ (struct.get $struct 0
+ (ref.null $struct)
+ )
+ )
+ )
+)
+
+(module
+ ;; CHECK: (type $struct (struct_subtype (field (mut i32)) data))
+ (type $struct (struct (mut i32)))
+
+ ;; CHECK: (type $none_=>_none (func_subtype func))
+
+ ;; CHECK: (global $global i32 (i32.const 42))
+ (global $global i32 (i32.const 42))
+ ;; CHECK: (global $global-2 i32 (i32.const 1337))
+ (global $global-2 i32 (i32.const 1337))
+
+ ;; CHECK: (func $test
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $struct
+ ;; CHECK-NEXT: (global.get $global)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (struct.set $struct 0
+ ;; CHECK-NEXT: (ref.null $struct)
+ ;; CHECK-NEXT: (i32.const 1337)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.get $struct 0
+ ;; CHECK-NEXT: (ref.null $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $test
+ (drop
+ (struct.new $struct
+ (global.get $global)
+ )
+ )
+ ;; As above, but set a constant, which means we are mixing constants with
+ ;; globals, which prevents the optimization.
+ (struct.set $struct 0
+ (ref.null $struct)
+ (i32.const 1337)
+ )
+ (drop
+ (struct.get $struct 0
+ (ref.null $struct)
+ )
+ )
+ )
+)
+
+(module
+ ;; Test a global type other than i32. Arrays of structs are a realistic case
+ ;; as they are used to implement itables.
+
+ ;; CHECK: (type $vtable (struct_subtype (field funcref) data))
+ (type $vtable (struct funcref))
+
+ ;; CHECK: (type $itable (array_subtype (ref $vtable) data))
+ (type $itable (array (ref $vtable)))
+
+ ;; CHECK: (type $object (struct_subtype (field $itable (ref $itable)) data))
+ (type $object (struct (field $itable (ref $itable))))
+
+ ;; CHECK: (type $none_=>_funcref (func_subtype (result funcref) func))
+
+ ;; CHECK: (global $global (ref $itable) (array.init_static $itable
+ ;; CHECK-NEXT: (struct.new $vtable
+ ;; CHECK-NEXT: (ref.null func)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (struct.new $vtable
+ ;; CHECK-NEXT: (ref.func $test)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: ))
+ (global $global (ref $itable) (array.init_static $itable
+ (struct.new $vtable
+ (ref.null func)
+ )
+ (struct.new $vtable
+ (ref.func $test)
+ )
+ ))
+
+ ;; CHECK: (func $test (result funcref)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $object
+ ;; CHECK-NEXT: (global.get $global)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (struct.get $vtable 0
+ ;; CHECK-NEXT: (array.get $itable
+ ;; CHECK-NEXT: (block (result (ref $itable))
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (ref.as_non_null
+ ;; CHECK-NEXT: (ref.null $object)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (global.get $global)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $test (result funcref)
+ (drop
+ (struct.new $object
+ (global.get $global)
+ )
+ )
+ ;; Realistic usage of an itable: read an item from it, then a func from
+ ;; that, and return the value (all verifying that the types are correct
+ ;; after optimization). Note how after optimization everything is lined up
+ ;; so that precompute-propagate can infer from the global.get the specific
+ ;; object the array.get is on, allowing us to emit a constant value for the
+ ;; outer struct.get in principle.
+ (struct.get $vtable 0
+ (array.get $itable
+ (struct.get $object $itable
+ (ref.null $object)
+ )
+ (i32.const 1)
+ )
+ )
+ )
+)
+