summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/ir/properties.cpp8
-rw-r--r--src/ir/properties.h15
-rw-r--r--test/lit/passes/type-ssa.wast83
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)
+ )
+ )
+ )
+ )
+)