diff options
-rwxr-xr-x | scripts/gen-s-parser.py | 2 | ||||
-rw-r--r-- | src/gen-s-parser.inc | 76 | ||||
-rw-r--r-- | src/ir/cost.h | 2 | ||||
-rw-r--r-- | src/ir/effects.h | 5 | ||||
-rw-r--r-- | src/passes/Print.cpp | 12 | ||||
-rw-r--r-- | src/wasm-binary.h | 2 | ||||
-rw-r--r-- | src/wasm-delegations-fields.def | 2 | ||||
-rw-r--r-- | src/wasm.h | 14 | ||||
-rw-r--r-- | src/wasm/wasm-binary.cpp | 21 | ||||
-rw-r--r-- | src/wasm/wasm-s-parser.cpp | 18 | ||||
-rw-r--r-- | src/wasm/wasm-stack.cpp | 15 | ||||
-rw-r--r-- | src/wasm/wasm.cpp | 3 | ||||
-rw-r--r-- | test/lit/strings.wast | 52 |
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) + ) + ) + ) ) |