diff options
-rw-r--r-- | src/ir/properties.cpp | 8 | ||||
-rw-r--r-- | src/ir/properties.h | 15 | ||||
-rw-r--r-- | test/lit/passes/type-ssa.wast | 83 |
3 files changed, 103 insertions, 3 deletions
diff --git a/src/ir/properties.cpp b/src/ir/properties.cpp index 4ade8aa10..b564b3bf1 100644 --- a/src/ir/properties.cpp +++ b/src/ir/properties.cpp @@ -36,6 +36,8 @@ bool isGenerative(Expression* curr, FeatureSet features) { return scanner.generative; } +// Checks an expression in a shallow manner (i.e., does not check children) as +// to whether it is valid in a wasm constant expression. static bool isValidInConstantExpression(Module& wasm, Expression* expr) { if (isSingleConstantExpression(expr) || expr->is<StructNew>() || expr->is<ArrayNew>() || expr->is<ArrayNewFixed>() || expr->is<I31New>() || @@ -43,6 +45,12 @@ static bool isValidInConstantExpression(Module& wasm, Expression* expr) { return true; } + if (auto* refAs = expr->dynCast<RefAs>()) { + if (refAs->op == ExternExternalize || refAs->op == ExternInternalize) { + return true; + } + } + if (auto* get = expr->dynCast<GlobalGet>()) { auto* g = wasm.getGlobalOrNull(get->name); // This is called from the validator, so we have to handle non-existent diff --git a/src/ir/properties.h b/src/ir/properties.h index 13eea6ba2..d47cf774b 100644 --- a/src/ir/properties.h +++ b/src/ir/properties.h @@ -82,10 +82,13 @@ inline bool isNamedControlFlow(Expression* curr) { // runtime will be equal as well. TODO: combine this with // isValidInConstantExpression or find better names(#4845) inline bool isSingleConstantExpression(const Expression* curr) { + if (auto* refAs = curr->dynCast<RefAs>()) { + if (refAs->op == ExternExternalize || refAs->op == ExternInternalize) { + return isSingleConstantExpression(refAs->value); + } + } return curr->is<Const>() || curr->is<RefNull>() || curr->is<RefFunc>() || - curr->is<StringConst>() || - (curr->is<RefAs>() && (curr->cast<RefAs>()->op == ExternExternalize || - curr->cast<RefAs>()->op == ExternInternalize)); + curr->is<StringConst>(); } inline bool isConstantExpression(const Expression* curr) { @@ -120,6 +123,12 @@ inline Literal getLiteral(const Expression* curr) { } } else if (auto* s = curr->dynCast<StringConst>()) { return Literal(s->string.toString()); + } else if (auto* r = curr->dynCast<RefAs>()) { + if (r->op == ExternExternalize) { + return getLiteral(r->value).externalize(); + } else if (r->op == ExternInternalize) { + return getLiteral(r->value).internalize(); + } } WASM_UNREACHABLE("non-constant expression"); } diff --git a/test/lit/passes/type-ssa.wast b/test/lit/passes/type-ssa.wast index ddfe8c089..323ebb704 100644 --- a/test/lit/passes/type-ssa.wast +++ b/test/lit/passes/type-ssa.wast @@ -449,3 +449,86 @@ ) ) ) + +;; Test that we do not error on externalized/internalized data. As we process +;; the fields of $struct we check if they are constants that we can handle, and +;; we should not hit any asserts while doing so. After that, we will decide not +;; to optimize $struct, since the global contains a struct.new (which we cannot +;; turn into a simple Literal). (We do optimize $empty and generate $empty$1, +;; but that is not important here.) +(module + ;; CHECK: (type $empty (struct )) + ;; NOMNL: (type $empty (struct )) + (type $empty (struct)) + + ;; CHECK: (type $empty$1 (struct_subtype $empty)) + + ;; CHECK: (type $anyref_=>_none (func (param anyref))) + + ;; CHECK: (type $struct (struct (field externref) (field anyref) (field externref))) + ;; NOMNL: (type $empty$1 (struct_subtype $empty)) + + ;; NOMNL: (type $anyref_=>_none (func (param anyref))) + + ;; NOMNL: (type $struct (struct (field externref) (field anyref) (field externref))) + (type $struct (struct externref anyref externref)) + + ;; CHECK: (global $g (mut anyref) (struct.new_default $empty$1)) + ;; NOMNL: (global $g (mut anyref) (struct.new_default $empty$1)) + (global $g (mut anyref) (struct.new $empty)) + + ;; CHECK: (func $0 (type $anyref_=>_none) (param $param anyref) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $struct + ;; CHECK-NEXT: (extern.externalize + ;; CHECK-NEXT: (global.get $g) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (extern.internalize + ;; CHECK-NEXT: (extern.externalize + ;; CHECK-NEXT: (global.get $g) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (extern.externalize + ;; CHECK-NEXT: (local.get $param) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; NOMNL: (func $0 (type $anyref_=>_none) (param $param anyref) + ;; NOMNL-NEXT: (drop + ;; NOMNL-NEXT: (struct.new $struct + ;; NOMNL-NEXT: (extern.externalize + ;; NOMNL-NEXT: (global.get $g) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: (extern.internalize + ;; NOMNL-NEXT: (extern.externalize + ;; NOMNL-NEXT: (global.get $g) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: (extern.externalize + ;; NOMNL-NEXT: (local.get $param) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: ) + (func $0 (param $param anyref) + (drop + (struct.new $struct + ;; An externalized global. + (extern.externalize + (global.get $g) + ) + ;; An externalized and then internalized global. + (extern.internalize + (extern.externalize + (global.get $g) + ) + ) + ;; An externalized parameter. + (extern.externalize + (local.get $param) + ) + ) + ) + ) +) |