summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/ir/type-updating.cpp9
-rw-r--r--src/passes/SignatureRefining.cpp5
-rw-r--r--src/passes/TypeRefining.cpp5
-rw-r--r--test/lit/passes/signature-refining-isorecursive.wast207
-rw-r--r--test/lit/passes/type-refining-isorecursive.wast141
5 files changed, 363 insertions, 4 deletions
diff --git a/src/ir/type-updating.cpp b/src/ir/type-updating.cpp
index b9bbf7a03..b08cca295 100644
--- a/src/ir/type-updating.cpp
+++ b/src/ir/type-updating.cpp
@@ -32,6 +32,15 @@ void GlobalTypeRewriter::update() {
}
typeBuilder.grow(indexedTypes.types.size());
+ // All the input types are distinct, so we need to make sure the output types
+ // are distinct as well. Further, the new types may have more recursions than
+ // the original types, so the old recursion groups may not be sufficient any
+ // more. Both of these problems are solved by putting all the new types into a
+ // single large recursion group.
+ // TODO: When we properly analyze which types are external and which are
+ // internal to the module, only optimize internal types.
+ typeBuilder.createRecGroup(0, typeBuilder.size());
+
// Create the temporary heap types.
for (Index i = 0; i < indexedTypes.types.size(); i++) {
auto type = indexedTypes.types[i];
diff --git a/src/passes/SignatureRefining.cpp b/src/passes/SignatureRefining.cpp
index 8a8c45872..11df83f4d 100644
--- a/src/passes/SignatureRefining.cpp
+++ b/src/passes/SignatureRefining.cpp
@@ -51,8 +51,9 @@ struct SignatureRefining : public Pass {
if (!module->features.hasGC()) {
return;
}
- if (getTypeSystem() != TypeSystem::Nominal) {
- Fatal() << "SignatureRefining requires nominal typing";
+ if (getTypeSystem() != TypeSystem::Nominal &&
+ getTypeSystem() != TypeSystem::Isorecursive) {
+ Fatal() << "SignatureRefining requires nominal/hybrid typing";
}
if (!module->tables.empty()) {
diff --git a/src/passes/TypeRefining.cpp b/src/passes/TypeRefining.cpp
index c755c1af6..258feae32 100644
--- a/src/passes/TypeRefining.cpp
+++ b/src/passes/TypeRefining.cpp
@@ -81,8 +81,9 @@ struct TypeRefining : public Pass {
if (!module->features.hasGC()) {
return;
}
- if (getTypeSystem() != TypeSystem::Nominal) {
- Fatal() << "TypeRefining requires nominal typing";
+ if (getTypeSystem() != TypeSystem::Nominal &&
+ getTypeSystem() != TypeSystem::Isorecursive) {
+ Fatal() << "TypeRefining requires nominal/hybrid typing";
}
// Find and analyze struct operations inside each function.
diff --git a/test/lit/passes/signature-refining-isorecursive.wast b/test/lit/passes/signature-refining-isorecursive.wast
new file mode 100644
index 000000000..fbf02bf90
--- /dev/null
+++ b/test/lit/passes/signature-refining-isorecursive.wast
@@ -0,0 +1,207 @@
+;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.
+;; RUN: foreach %s %t wasm-opt --hybrid --signature-refining -all -S -o - | filecheck %s
+
+(module
+ ;; The signature should be refined to a single self-referential type.
+
+ ;; CHECK: (type $refined (func_subtype (param (ref $refined)) (result (ref $refined)) func))
+ (type $refined (func (param (ref $refined)) (result (ref $refined))))
+
+ ;; CHECK: (elem declare func $foo)
+
+ ;; CHECK: (func $foo (type $refined) (param $0 (ref $refined)) (result (ref $refined))
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (call $foo
+ ;; CHECK-NEXT: (ref.func $foo)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.func $foo)
+ ;; CHECK-NEXT: )
+ (func $foo (param funcref) (result funcref)
+ (drop
+ (call $foo
+ (ref.func $foo)
+ )
+ )
+ (ref.func $foo)
+ )
+)
+
+(module
+ ;; The signatures should be refined to a pair of mutually self-referential types.
+
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $0 (func_subtype (param i32 (ref $0)) (result (ref $1)) func))
+ (type $0 (func (param i32 funcref) (result funcref)))
+ ;; CHECK: (type $1 (func_subtype (param f32 (ref $1)) (result (ref $0)) func))
+ (type $1 (func (param f32 funcref) (result funcref)))
+
+
+ ;; CHECK: (elem declare func $bar $foo)
+
+ ;; CHECK: (func $foo (type $0) (param $0 i32) (param $1 (ref $0)) (result (ref $1))
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (call $foo
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: (ref.func $foo)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.func $bar)
+ ;; CHECK-NEXT: )
+ (func $foo (type $0) (param i32 funcref) (result funcref)
+ (drop
+ (call $foo
+ (i32.const 0)
+ (ref.func $foo)
+ )
+ )
+ (ref.func $bar)
+ )
+
+ ;; CHECK: (func $bar (type $1) (param $0 f32) (param $1 (ref $1)) (result (ref $0))
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (call $bar
+ ;; CHECK-NEXT: (f32.const 0)
+ ;; CHECK-NEXT: (ref.func $bar)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.func $foo)
+ ;; CHECK-NEXT: )
+ (func $bar (type $1) (param f32 funcref) (result funcref)
+ (drop
+ (call $bar
+ (f32.const 0)
+ (ref.func $bar)
+ )
+ )
+ (ref.func $foo)
+ )
+)
+
+(module
+ ;; The signatures start out as separate types, so they must remain separate
+ ;; even though they are refined to the same structure.
+
+ (rec
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $0 (func_subtype (param (ref $0)) func))
+ (type $0 (func (param funcref)))
+ ;; CHECK: (type $1 (func_subtype (param (ref $0)) func))
+ (type $1 (func (param funcref)))
+ )
+
+ ;; CHECK: (elem declare func $foo)
+
+ ;; CHECK: (func $foo (type $0) (param $0 (ref $0))
+ ;; CHECK-NEXT: (call $foo
+ ;; CHECK-NEXT: (ref.func $foo)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $foo (type $0) (param funcref)
+ (call $foo
+ (ref.func $foo)
+ )
+ )
+
+ ;; CHECK: (func $bar (type $1) (param $0 (ref $0))
+ ;; CHECK-NEXT: (call $bar
+ ;; CHECK-NEXT: (ref.func $foo)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $bar (type $1) (param funcref)
+ (call $bar
+ (ref.func $foo)
+ )
+ )
+)
+
+(module
+ ;; The signatures should be refined to a pair of mutually recursive types and
+ ;; another type that refers to them.
+
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $1 (func_subtype (param f32 (ref $1)) (result (ref $0)) func))
+
+ ;; CHECK: (type $0 (func_subtype (param i32 (ref $0)) (result (ref $1)) func))
+ (type $0 (func (param i32 funcref) (result funcref)))
+
+ (type $1 (func (param f32 funcref) (result funcref)))
+
+ ;; CHECK: (type $2 (func_subtype (param (ref $0)) (result (ref $1)) func))
+ (type $2 (func (param funcref) (result funcref)))
+
+
+ ;; CHECK: (elem declare func $bar $foo)
+
+ ;; CHECK: (func $foo (type $0) (param $0 i32) (param $1 (ref $0)) (result (ref $1))
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (call $foo
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: (ref.func $foo)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.func $bar)
+ ;; CHECK-NEXT: )
+ (func $foo (param i32 funcref) (result funcref)
+ (drop
+ (call $foo
+ (i32.const 0)
+ (ref.func $foo)
+ )
+ )
+ (ref.func $bar)
+ )
+
+ ;; CHECK: (func $baz (type $2) (param $0 (ref $0)) (result (ref $1))
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (call $quux
+ ;; CHECK-NEXT: (ref.func $foo)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.func $bar)
+ ;; CHECK-NEXT: )
+ (func $baz (param funcref) (result funcref)
+ (drop
+ (call $quux
+ (ref.func $foo)
+ )
+ )
+ (ref.func $bar)
+ )
+
+ ;; CHECK: (func $bar (type $1) (param $0 f32) (param $1 (ref $1)) (result (ref $0))
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (call $bar
+ ;; CHECK-NEXT: (f32.const 0)
+ ;; CHECK-NEXT: (ref.func $bar)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.func $foo)
+ ;; CHECK-NEXT: )
+ (func $bar (param f32 funcref) (result funcref)
+ (drop
+ (call $bar
+ (f32.const 0)
+ (ref.func $bar)
+ )
+ )
+ (ref.func $foo)
+ )
+
+ ;; CHECK: (func $quux (type $2) (param $0 (ref $0)) (result (ref $1))
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (call $baz
+ ;; CHECK-NEXT: (ref.func $foo)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.func $bar)
+ ;; CHECK-NEXT: )
+ (func $quux (param funcref) (result funcref)
+ (drop
+ (call $baz
+ (ref.func $foo)
+ )
+ )
+ (ref.func $bar)
+ )
+)
diff --git a/test/lit/passes/type-refining-isorecursive.wast b/test/lit/passes/type-refining-isorecursive.wast
new file mode 100644
index 000000000..eac3ba4b2
--- /dev/null
+++ b/test/lit/passes/type-refining-isorecursive.wast
@@ -0,0 +1,141 @@
+;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.
+;; RUN: foreach %s %t wasm-opt --hybrid --type-refining -all -S -o - | filecheck %s
+
+(module
+ ;; The types should be refined to a set of three mutually recursive types.
+
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $0 (struct_subtype (field anyref) (field (ref $1)) data))
+ (type $0 (struct_subtype (ref null any) anyref data))
+ ;; CHECK: (type $1 (struct_subtype (field eqref) (field (ref $2)) data))
+ (type $1 (struct_subtype (ref null eq) anyref data))
+ ;; CHECK: (type $2 (struct_subtype (field (ref null i31)) (field (ref $0)) data))
+ (type $2 (struct_subtype (ref null i31) anyref data))
+
+ ;; CHECK: (type $ref|$0|_ref|$1|_ref|$2|_=>_none (func_subtype (param (ref $0) (ref $1) (ref $2)) func))
+
+ ;; CHECK: (func $foo (type $ref|$0|_ref|$1|_ref|$2|_=>_none) (param $x (ref $0)) (param $y (ref $1)) (param $z (ref $2))
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $0
+ ;; CHECK-NEXT: (ref.null any)
+ ;; CHECK-NEXT: (local.get $y)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $1
+ ;; CHECK-NEXT: (ref.null eq)
+ ;; CHECK-NEXT: (local.get $z)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $2
+ ;; CHECK-NEXT: (ref.null i31)
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $foo (param $x (ref $0)) (param $y (ref $1)) (param $z (ref $2))
+ (drop
+ (struct.new $0
+ (ref.null any)
+ (local.get $y)
+ )
+ )
+ (drop
+ (struct.new $1
+ (ref.null eq)
+ (local.get $z)
+ )
+ )
+ (drop
+ (struct.new $2
+ (ref.null i31)
+ (local.get $x)
+ )
+ )
+ )
+)
+
+(module
+ ;; The types will all be mutually recursive because they all reference and are
+ ;; referenced by $all, but now we need to worry about ordering supertypes
+ ;; correctly.
+
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $all (struct_subtype (field i32) (field (ref $0)) (field (ref $1)) (field (ref $2)) data))
+ (type $all (struct_subtype i32 anyref anyref anyref data))
+
+ ;; CHECK: (type $0 (struct_subtype (field anyref) (field (ref null $all)) (field (ref $0)) data))
+ (type $0 (struct_subtype (ref null any) anyref anyref data))
+ ;; CHECK: (type $1 (struct_subtype (field eqref) (field (ref null $all)) (field (ref $0)) $0))
+ (type $1 (struct_subtype (ref null eq) anyref anyref $0))
+ ;; CHECK: (type $2 (struct_subtype (field (ref null i31)) (field (ref null $all)) (field (ref $0)) $1))
+ (type $2 (struct_subtype (ref null i31) anyref anyref $1))
+
+ ;; CHECK: (type $ref|$0|_ref|$1|_ref|$2|_=>_none (func_subtype (param (ref $0) (ref $1) (ref $2)) func))
+
+ ;; CHECK: (func $foo (type $ref|$0|_ref|$1|_ref|$2|_=>_none) (param $x (ref $0)) (param $y (ref $1)) (param $z (ref $2))
+ ;; CHECK-NEXT: (local $all (ref null $all))
+ ;; CHECK-NEXT: (local.set $all
+ ;; CHECK-NEXT: (struct.new $all
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: (local.get $y)
+ ;; CHECK-NEXT: (local.get $z)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $0
+ ;; CHECK-NEXT: (ref.null any)
+ ;; CHECK-NEXT: (local.get $all)
+ ;; CHECK-NEXT: (local.get $y)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $1
+ ;; CHECK-NEXT: (ref.null eq)
+ ;; CHECK-NEXT: (local.get $all)
+ ;; CHECK-NEXT: (local.get $z)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $2
+ ;; CHECK-NEXT: (ref.null i31)
+ ;; CHECK-NEXT: (local.get $all)
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $foo (param $x (ref $0)) (param $y (ref $1)) (param $z (ref $2))
+ (local $all (ref null $all))
+ (local.set $all
+ (struct.new $all
+ (i32.const 0)
+ (local.get $x)
+ (local.get $y)
+ (local.get $z)
+ )
+ )
+ (drop
+ (struct.new $0
+ (ref.null any)
+ (local.get $all)
+ (local.get $y)
+ )
+ )
+ (drop
+ (struct.new $1
+ (ref.null eq)
+ (local.get $all)
+ (local.get $z)
+ )
+ )
+ (drop
+ (struct.new $2
+ (ref.null i31)
+ (local.get $all)
+ (local.get $x)
+ )
+ )
+ )
+)