diff options
-rw-r--r-- | src/ir/type-updating.cpp | 9 | ||||
-rw-r--r-- | src/passes/SignatureRefining.cpp | 5 | ||||
-rw-r--r-- | src/passes/TypeRefining.cpp | 5 | ||||
-rw-r--r-- | test/lit/passes/signature-refining-isorecursive.wast | 207 | ||||
-rw-r--r-- | test/lit/passes/type-refining-isorecursive.wast | 141 |
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) + ) + ) + ) +) |