summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xscripts/gen-s-parser.py2
-rw-r--r--src/gen-s-parser.inc76
-rw-r--r--src/ir/cost.h2
-rw-r--r--src/ir/effects.h5
-rw-r--r--src/passes/Print.cpp12
-rw-r--r--src/wasm-binary.h2
-rw-r--r--src/wasm-delegations-fields.def2
-rw-r--r--src/wasm.h14
-rw-r--r--src/wasm/wasm-binary.cpp21
-rw-r--r--src/wasm/wasm-s-parser.cpp18
-rw-r--r--src/wasm/wasm-stack.cpp15
-rw-r--r--src/wasm/wasm.cpp3
-rw-r--r--test/lit/strings.wast52
13 files changed, 196 insertions, 28 deletions
diff --git a/scripts/gen-s-parser.py b/scripts/gen-s-parser.py
index 3e6889ab9..dbe24cbda 100755
--- a/scripts/gen-s-parser.py
+++ b/scripts/gen-s-parser.py
@@ -616,6 +616,8 @@ instructions = [
("ref.as_i31", "makeRefAs(s, RefAsI31)"),
("string.new_wtf8", "makeStringNew(s, StringNewWTF8)"),
("string.new_wtf16", "makeStringNew(s, StringNewWTF16)"),
+ ("string.new_wtf8_array", "makeStringNew(s, StringNewWTF8Array)"),
+ ("string.new_wtf16_array", "makeStringNew(s, StringNewWTF16Array)"),
("string.const", "makeStringConst(s)"),
("string.measure_wtf8", "makeStringMeasure(s, StringMeasureWTF8)"),
("string.measure_wtf16", "makeStringMeasure(s, StringMeasureWTF16)"),
diff --git a/src/gen-s-parser.inc b/src/gen-s-parser.inc
index 1c3a24749..59eee1070 100644
--- a/src/gen-s-parser.inc
+++ b/src/gen-s-parser.inc
@@ -3196,12 +3196,28 @@ switch (op[0]) {
}
case 'n': {
switch (op[14]) {
- case '1':
- if (strcmp(op, "string.new_wtf16") == 0) { return makeStringNew(s, StringNewWTF16); }
- goto parse_error;
- case '8':
- if (strcmp(op, "string.new_wtf8") == 0) { return makeStringNew(s, StringNewWTF8); }
- goto parse_error;
+ case '1': {
+ switch (op[16]) {
+ case '\0':
+ if (strcmp(op, "string.new_wtf16") == 0) { return makeStringNew(s, StringNewWTF16); }
+ goto parse_error;
+ case '_':
+ if (strcmp(op, "string.new_wtf16_array") == 0) { return makeStringNew(s, StringNewWTF16Array); }
+ goto parse_error;
+ default: goto parse_error;
+ }
+ }
+ case '8': {
+ switch (op[15]) {
+ case '\0':
+ if (strcmp(op, "string.new_wtf8") == 0) { return makeStringNew(s, StringNewWTF8); }
+ goto parse_error;
+ case '_':
+ if (strcmp(op, "string.new_wtf8_array") == 0) { return makeStringNew(s, StringNewWTF8Array); }
+ goto parse_error;
+ default: goto parse_error;
+ }
+ }
default: goto parse_error;
}
}
@@ -8918,20 +8934,44 @@ switch (op[0]) {
}
case 'n': {
switch (op[14]) {
- case '1':
- if (op == "string.new_wtf16"sv) {
- auto ret = makeStringNew(ctx, in, StringNewWTF16);
- CHECK_ERR(ret);
- return *ret;
+ case '1': {
+ switch (op[16]) {
+ case '\0':
+ if (op == "string.new_wtf16"sv) {
+ auto ret = makeStringNew(ctx, in, StringNewWTF16);
+ CHECK_ERR(ret);
+ return *ret;
+ }
+ goto parse_error;
+ case '_':
+ if (op == "string.new_wtf16_array"sv) {
+ auto ret = makeStringNew(ctx, in, StringNewWTF16Array);
+ CHECK_ERR(ret);
+ return *ret;
+ }
+ goto parse_error;
+ default: goto parse_error;
}
- goto parse_error;
- case '8':
- if (op == "string.new_wtf8"sv) {
- auto ret = makeStringNew(ctx, in, StringNewWTF8);
- CHECK_ERR(ret);
- return *ret;
+ }
+ case '8': {
+ switch (op[15]) {
+ case '\0':
+ if (op == "string.new_wtf8"sv) {
+ auto ret = makeStringNew(ctx, in, StringNewWTF8);
+ CHECK_ERR(ret);
+ return *ret;
+ }
+ goto parse_error;
+ case '_':
+ if (op == "string.new_wtf8_array"sv) {
+ auto ret = makeStringNew(ctx, in, StringNewWTF8Array);
+ CHECK_ERR(ret);
+ return *ret;
+ }
+ goto parse_error;
+ default: goto parse_error;
}
- goto parse_error;
+ }
default: goto parse_error;
}
}
diff --git a/src/ir/cost.h b/src/ir/cost.h
index 8ebc6eb9b..b48b777e1 100644
--- a/src/ir/cost.h
+++ b/src/ir/cost.h
@@ -672,7 +672,7 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, CostType> {
}
CostType visitRefAs(RefAs* curr) { return 1 + visit(curr->value); }
CostType visitStringNew(StringNew* curr) {
- return 8 + visit(curr->ptr) + visit(curr->length);
+ return 8 + visit(curr->ptr) + maybeVisit(curr->length);
}
CostType visitStringConst(StringConst* curr) { return 4; }
CostType visitStringMeasure(StringMeasure* curr) {
diff --git a/src/ir/effects.h b/src/ir/effects.h
index 58480f0ff..43d4868d8 100644
--- a/src/ir/effects.h
+++ b/src/ir/effects.h
@@ -732,7 +732,10 @@ private:
// we keep the code here simpler, but it does mean another optimization
// cycle may be needed in some cases.
}
- void visitStringNew(StringNew* curr) {}
+ void visitStringNew(StringNew* curr) {
+ // traps when out of bounds in linear memory or ref is null
+ parent.implicitTrap = true;
+ }
void visitStringConst(StringConst* curr) {}
void visitStringMeasure(StringMeasure* curr) {
// traps when ref is null.
diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp
index 208833357..ca2985240 100644
--- a/src/passes/Print.cpp
+++ b/src/passes/Print.cpp
@@ -2233,6 +2233,18 @@ struct PrintExpressionContents
case StringNewWTF16:
printMedium(o, "string.new_wtf16");
break;
+ case StringNewUTF8Array:
+ printMedium(o, "string.new_wtf8_array utf8");
+ break;
+ case StringNewWTF8Array:
+ printMedium(o, "string.new_wtf8_array wtf8");
+ break;
+ case StringNewReplaceArray:
+ printMedium(o, "string.new_wtf8_array replace");
+ break;
+ case StringNewWTF16Array:
+ printMedium(o, "string.new_wtf16_array");
+ break;
default:
WASM_UNREACHABLE("invalid string.new*");
}
diff --git a/src/wasm-binary.h b/src/wasm-binary.h
index 23b7ace5a..8f44c6ecf 100644
--- a/src/wasm-binary.h
+++ b/src/wasm-binary.h
@@ -1159,6 +1159,8 @@ enum ASTNodes {
StringViewIterAdvance = 0xa2,
StringViewIterRewind = 0xa3,
StringViewIterSlice = 0xa4,
+ StringNewWTF8Array = 0xb0,
+ StringNewWTF16Array = 0xb1,
};
enum MemoryAccess {
diff --git a/src/wasm-delegations-fields.def b/src/wasm-delegations-fields.def
index 41dd7d660..a8dfcc750 100644
--- a/src/wasm-delegations-fields.def
+++ b/src/wasm-delegations-fields.def
@@ -716,7 +716,7 @@ switch (DELEGATE_ID) {
case Expression::Id::StringNewId: {
DELEGATE_START(StringNew);
DELEGATE_FIELD_INT(StringNew, op);
- DELEGATE_FIELD_CHILD(StringNew, length);
+ DELEGATE_FIELD_OPTIONAL_CHILD(StringNew, length);
DELEGATE_FIELD_CHILD(StringNew, ptr);
DELEGATE_END(StringNew);
break;
diff --git a/src/wasm.h b/src/wasm.h
index a2f361917..b5d51b84b 100644
--- a/src/wasm.h
+++ b/src/wasm.h
@@ -584,10 +584,16 @@ enum BrOnOp {
};
enum StringNewOp {
+ // Linear memory
StringNewUTF8,
StringNewWTF8,
StringNewReplace,
- StringNewWTF16
+ StringNewWTF16,
+ // GC
+ StringNewUTF8Array,
+ StringNewWTF8Array,
+ StringNewReplaceArray,
+ StringNewWTF16Array,
};
enum StringMeasureOp {
@@ -1700,8 +1706,12 @@ public:
StringNewOp op;
+ // In linear memory variations this is the pointer in linear memory. In the
+ // GC variations this is an Array.
Expression* ptr;
- Expression* length;
+
+ // Used only in linear memory variations.
+ Expression* length = nullptr;
void finalize();
};
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp
index 8108027d4..e2588422c 100644
--- a/src/wasm/wasm-binary.cpp
+++ b/src/wasm/wasm-binary.cpp
@@ -7157,6 +7157,7 @@ bool WasmBinaryBuilder::maybeVisitArrayCopy(Expression*& out, uint32_t code) {
bool WasmBinaryBuilder::maybeVisitStringNew(Expression*& out, uint32_t code) {
StringNewOp op;
+ Expression* length = nullptr;
if (code == BinaryConsts::StringNewWTF8) {
auto policy = getU32LEB();
switch (policy) {
@@ -7172,12 +7173,30 @@ bool WasmBinaryBuilder::maybeVisitStringNew(Expression*& out, uint32_t code) {
default:
throwError("bad policy for string.new");
}
+ length = popNonVoidExpression();
} else if (code == BinaryConsts::StringNewWTF16) {
op = StringNewWTF16;
+ length = popNonVoidExpression();
+ } else if (code == BinaryConsts::StringNewWTF8Array) {
+ auto policy = getU32LEB();
+ switch (policy) {
+ case BinaryConsts::StringPolicy::UTF8:
+ op = StringNewUTF8Array;
+ break;
+ case BinaryConsts::StringPolicy::WTF8:
+ op = StringNewWTF8Array;
+ break;
+ case BinaryConsts::StringPolicy::Replace:
+ op = StringNewReplaceArray;
+ break;
+ default:
+ throwError("bad policy for string.new");
+ }
+ } else if (code == BinaryConsts::StringNewWTF16Array) {
+ op = StringNewWTF16Array;
} else {
return false;
}
- auto* length = popNonVoidExpression();
auto* ptr = popNonVoidExpression();
out = Builder(wasm).makeStringNew(op, ptr, length);
return true;
diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp
index f3bafe3de..bfe09280d 100644
--- a/src/wasm/wasm-s-parser.cpp
+++ b/src/wasm/wasm-s-parser.cpp
@@ -2937,6 +2937,7 @@ Expression* SExpressionWasmBuilder::makeRefAs(Element& s, RefAsOp op) {
Expression* SExpressionWasmBuilder::makeStringNew(Element& s, StringNewOp op) {
size_t i = 1;
+ Expression* length = nullptr;
if (op == StringNewWTF8) {
const char* str = s[i++]->c_str();
if (strncmp(str, "utf8", 4) == 0) {
@@ -2948,9 +2949,22 @@ Expression* SExpressionWasmBuilder::makeStringNew(Element& s, StringNewOp op) {
} else {
throw ParseException("bad string.new op", s.line, s.col);
}
+ length = parseExpression(s[i + 1]);
+ } else if (op == StringNewWTF16) {
+ length = parseExpression(s[i + 1]);
+ } else if (op == StringNewWTF8Array) {
+ const char* str = s[i++]->c_str();
+ if (strncmp(str, "utf8", 4) == 0) {
+ op = StringNewUTF8Array;
+ } else if (strncmp(str, "wtf8", 4) == 0) {
+ op = StringNewWTF8Array;
+ } else if (strncmp(str, "replace", 7) == 0) {
+ op = StringNewReplaceArray;
+ } else {
+ throw ParseException("bad string.new op", s.line, s.col);
+ }
}
- return Builder(wasm).makeStringNew(
- op, parseExpression(s[i]), parseExpression(s[i + 1]));
+ return Builder(wasm).makeStringNew(op, parseExpression(s[i]), length);
}
Expression* SExpressionWasmBuilder::makeStringConst(Element& s) {
diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp
index 0e39862e7..28472aebd 100644
--- a/src/wasm/wasm-stack.cpp
+++ b/src/wasm/wasm-stack.cpp
@@ -2252,6 +2252,21 @@ void BinaryInstWriter::visitStringNew(StringNew* curr) {
case StringNewWTF16:
o << U32LEB(BinaryConsts::StringNewWTF16);
break;
+ case StringNewUTF8Array:
+ o << U32LEB(BinaryConsts::StringNewWTF8Array)
+ << U32LEB(BinaryConsts::StringPolicy::UTF8);
+ break;
+ case StringNewWTF8Array:
+ o << U32LEB(BinaryConsts::StringNewWTF8Array)
+ << U32LEB(BinaryConsts::StringPolicy::WTF8);
+ break;
+ case StringNewReplaceArray:
+ o << U32LEB(BinaryConsts::StringNewWTF8Array)
+ << U32LEB(BinaryConsts::StringPolicy::Replace);
+ break;
+ case StringNewWTF16Array:
+ o << U32LEB(BinaryConsts::StringNewWTF16Array);
+ break;
default:
WASM_UNREACHABLE("invalid string.new*");
}
diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp
index 0a3ca1929..eceb4585b 100644
--- a/src/wasm/wasm.cpp
+++ b/src/wasm/wasm.cpp
@@ -1175,7 +1175,8 @@ void RefAs::finalize() {
}
void StringNew::finalize() {
- if (ptr->type == Type::unreachable || length->type == Type::unreachable) {
+ if (ptr->type == Type::unreachable ||
+ (length && length->type == Type::unreachable)) {
type = Type::unreachable;
} else {
type = Type(HeapType::string, NonNullable);
diff --git a/test/lit/strings.wast b/test/lit/strings.wast
index 25ec73326..c807ac000 100644
--- a/test/lit/strings.wast
+++ b/test/lit/strings.wast
@@ -2,7 +2,7 @@
;; Check that string types are emitted properly in the binary format.
-;; RUN: foreach %s %t wasm-opt --enable-strings --enable-reference-types --roundtrip -S -o - | filecheck %s
+;; RUN: foreach %s %t wasm-opt --enable-strings --enable-reference-types --enable-gc --roundtrip -S -o - | filecheck %s
(module
;; CHECK: (type $ref?|string|_=>_none (func (param stringref)))
@@ -17,6 +17,11 @@
;; CHECK: (type $ref?|stringview_wtf16|_=>_none (func (param stringview_wtf16)))
+ ;; CHECK: (type $ref|$array|_=>_none (func (param (ref $array))))
+
+ ;; CHECK: (type $array (array i32))
+ (type $array (array_subtype i32 data))
+
;; CHECK: (global $string-const stringref (string.const "string in a global"))
(global $string-const stringref (string.const "string in a global"))
@@ -425,4 +430,49 @@
)
)
)
+
+ ;; CHECK: (func $string.new.gc (param $array (ref $array))
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (string.new_wtf8_array utf8
+ ;; CHECK-NEXT: (local.get $array)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (string.new_wtf8_array wtf8
+ ;; CHECK-NEXT: (local.get $array)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (string.new_wtf8_array replace
+ ;; CHECK-NEXT: (local.get $array)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (string.new_wtf16_array
+ ;; CHECK-NEXT: (local.get $array)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $string.new.gc (param $array (ref $array))
+ (drop
+ (string.new_wtf8_array utf8
+ (local.get $array)
+ )
+ )
+ (drop
+ (string.new_wtf8_array wtf8
+ (local.get $array)
+ )
+ )
+ (drop
+ (string.new_wtf8_array replace
+ (local.get $array)
+ )
+ )
+ (drop
+ (string.new_wtf16_array
+ (local.get $array)
+ )
+ )
+ )
)