Relax structs
diff --git a/src/passes/RelaxNullability.cpp b/src/passes/RelaxNullability.cpp index 839a801..f19ae37 100644 --- a/src/passes/RelaxNullability.cpp +++ b/src/passes/RelaxNullability.cpp
@@ -41,6 +41,7 @@ #include "ir/child-typer.h" #include "ir/find_all.h" #include "ir/module-utils.h" +#include "ir/type-updating.h" #include "ir/utils.h" namespace wasm { @@ -199,6 +200,9 @@ fixupReturn(&func->body, getModule(), func); } + // void visitStructGet(StructGet* curr) { changed |= makeNullable(curr->type); + // } + void visitLocalGet(LocalGet* curr) { changed |= makeNullable(curr->type); } void visitLocalSet(LocalSet* curr) { @@ -213,6 +217,7 @@ void run(Module* module) override { auto referencedFuncs = ModuleUtils::getReferencedFuncs(*module); + relaxStructs(module, getPassOptions().worldMode); relaxFunctionParams(module, referencedFuncs); PassRunner runner(getPassRunner()); @@ -221,6 +226,35 @@ } private: + void relaxStructs(Module* module, WorldMode worldMode) { + using TypeMap = GlobalTypeRewriter::TypeMap; + auto privateTypes = ModuleUtils::getPrivateHeapTypes(*module, worldMode); + + TypeMap map; + for (HeapType old : privateTypes) { + if (old.isStruct()) { + bool changed = false; + + std::vector<Field> relaxedFields; + relaxedFields.reserve(old.getStruct().fields.size()); + for (auto field : old.getStruct().fields) { + changed |= makeNullable(field.type); + relaxedFields.push_back(field); + } + if (!changed) { + continue; + } + + HeapType rewritten(Struct{std::move(relaxedFields)}); + map.try_emplace(old, rewritten); + } + } + + GlobalTypeRewriter rewriter(*module, worldMode); + // TODO: does this handle public/private? + rewriter.mapTypes(map); + rewriter.mapTypeNamesAndIndices(map); + } void relaxFunctionParams(Module* module, const std::unordered_set<Function*>& referencedFuncs) {
diff --git a/test/lit/passes/relax-nullability-closed-world.wast b/test/lit/passes/relax-nullability-closed-world.wast new file mode 100644 index 0000000..1a7a613 --- /dev/null +++ b/test/lit/passes/relax-nullability-closed-world.wast
@@ -0,0 +1,65 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. + +;; RUN: foreach %s %t wasm-opt -all --closed-world --relax-nullability -S -o - | filecheck %s + +(module + ;; CHECK: (type $private-struct (struct (field (mut anyref)))) + (type $private-struct (struct (field (mut (ref any))))) + + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $public-struct (struct (field (mut (ref any))))) + (type $public-struct (struct (field (mut (ref any))))) + ;; CHECK: (type $brand (struct)) + (type $brand (struct)) + ) + + (func $public (import "" "") (param (ref $public-struct)) (result (ref $public-struct))) + + ;; CHECK: (type $3 (func (param (ref $public-struct)) (result (ref $public-struct)))) + + ;; CHECK: (type $4 (func (param anyref))) + + ;; CHECK: (import "" "" (func $public (type $3) (param (ref $public-struct)) (result (ref $public-struct)))) + + ;; CHECK: (func $private-is-relaxed (type $4) (param $ref anyref) + ;; CHECK-NEXT: (local $private-relaxed (ref null $private-struct)) + ;; CHECK-NEXT: (local $public-not-relaxed (ref null $public-struct)) + ;; CHECK-NEXT: (local.set $private-relaxed + ;; CHECK-NEXT: (struct.new $private-struct + ;; CHECK-NEXT: (local.get $ref) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $private-struct 0 + ;; CHECK-NEXT: (local.get $private-relaxed) + ;; CHECK-NEXT: (local.get $ref) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $public-not-relaxed + ;; CHECK-NEXT: (struct.new $public-struct + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $ref) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $public-struct 0 + ;; CHECK-NEXT: (local.get $public-not-relaxed) + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $ref) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $private-is-relaxed (param $ref (ref any)) + (local $private-relaxed (ref $private-struct)) + (local $public-not-relaxed (ref $public-struct)) + + ;; The ref in the private struct is relaxed to nullable. + ;; struct.new and struct.set don't need ref.as_non_null + (local.set $private-relaxed (struct.new $private-struct (local.get $ref))) + (struct.set $private-struct 0 (local.get $private-relaxed) (local.get $ref)) + + ;; The public struct is not relaxed and struct.new and struct.set require + ;; ref.as_non_null. + (local.set $public-not-relaxed (struct.new $public-struct (local.get $ref))) + (struct.set $public-struct 0 (local.get $public-not-relaxed) (local.get $ref)) + ) +)
diff --git a/test/lit/passes/relax-nullability.wast b/test/lit/passes/relax-nullability.wast index 2a409d5..001c6cb 100644 --- a/test/lit/passes/relax-nullability.wast +++ b/test/lit/passes/relax-nullability.wast
@@ -4,6 +4,22 @@ ;; Locals, params, and blocks are relaxed. (module + ;; CHECK: (type $func.0 (func (param (ref any) (ref $struct.0)) (result (ref any)))) + + ;; CHECK: (type $func.0 (func (result anyref))) + + ;; CHECK: (type $struct.0 (struct)) + + ;; CHECK: (type $func.0 (func (result (ref any)))) + + ;; CHECK: (type $func.0 (func (param i32) (result (ref any)))) + + ;; CHECK: (type $func.0 (func (param i32 (ref any) (ref any)) (result (ref any)))) + + ;; CHECK: (type $func.0 (func (param (ref any)) (result (ref any)))) + + ;; CHECK: (type $func.0 (func (param anyref))) + ;; CHECK: (type $struct (struct)) (type $struct (struct)) @@ -179,6 +195,10 @@ ;; call_ref/call_indirect. (module (type $struct (struct)) + ;; CHECK: (type $func.0 (func (param (ref any)))) + + ;; CHECK: (type $func.0 (func)) + ;; CHECK: (type $shared-sig (func (param (ref any)) (result (ref any)))) ;; CHECK: (type $1 (func (param anyref)))