summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xscripts/gen-s-parser.py1
-rw-r--r--src/gen-s-parser.inc14
-rw-r--r--src/ir/ReFinalize.cpp1
-rw-r--r--src/ir/cost.h3
-rw-r--r--src/ir/effects.h4
-rw-r--r--src/ir/possible-contents.cpp4
-rw-r--r--src/passes/Print.cpp3
-rw-r--r--src/wasm-binary.h2
-rw-r--r--src/wasm-builder.h7
-rw-r--r--src/wasm-delegations-fields.def7
-rw-r--r--src/wasm-delegations.def1
-rw-r--r--src/wasm-interpreter.h3
-rw-r--r--src/wasm-s-parser.h1
-rw-r--r--src/wasm.h11
-rw-r--r--src/wasm/wasm-binary.cpp14
-rw-r--r--src/wasm/wasm-s-parser.cpp5
-rw-r--r--src/wasm/wasm-stack.cpp4
-rw-r--r--src/wasm/wasm.cpp8
-rw-r--r--src/wasm2js.h4
-rw-r--r--test/lit/strings.wast19
20 files changed, 113 insertions, 3 deletions
diff --git a/scripts/gen-s-parser.py b/scripts/gen-s-parser.py
index 1003c63f6..113eaeaa1 100755
--- a/scripts/gen-s-parser.py
+++ b/scripts/gen-s-parser.py
@@ -621,6 +621,7 @@ instructions = [
("string.measure_wtf16", "makeStringMeasure(s, StringMeasureWTF16)"),
("string.encode_wtf8", "makeStringEncode(s, StringEncodeWTF8)"),
("string.encode_wtf16", "makeStringEncode(s, StringEncodeWTF16)"),
+ ("string.concat", "makeStringConcat(s)"),
]
diff --git a/src/gen-s-parser.inc b/src/gen-s-parser.inc
index c3b6bcec1..be1ab85e7 100644
--- a/src/gen-s-parser.inc
+++ b/src/gen-s-parser.inc
@@ -3129,9 +3129,17 @@ switch (op[0]) {
switch (op[3]) {
case 'i': {
switch (op[7]) {
- case 'c':
- if (strcmp(op, "string.const") == 0) { return makeStringConst(s); }
- goto parse_error;
+ case 'c': {
+ switch (op[10]) {
+ case 'c':
+ if (strcmp(op, "string.concat") == 0) { return makeStringConcat(s); }
+ goto parse_error;
+ case 's':
+ if (strcmp(op, "string.const") == 0) { return makeStringConst(s); }
+ goto parse_error;
+ default: goto parse_error;
+ }
+ }
case 'e': {
switch (op[17]) {
case '1':
diff --git a/src/ir/ReFinalize.cpp b/src/ir/ReFinalize.cpp
index 8acffaa34..102d701b9 100644
--- a/src/ir/ReFinalize.cpp
+++ b/src/ir/ReFinalize.cpp
@@ -176,6 +176,7 @@ void ReFinalize::visitStringNew(StringNew* curr) { curr->finalize(); }
void ReFinalize::visitStringConst(StringConst* curr) { curr->finalize(); }
void ReFinalize::visitStringMeasure(StringMeasure* curr) { curr->finalize(); }
void ReFinalize::visitStringEncode(StringEncode* curr) { curr->finalize(); }
+void ReFinalize::visitStringConcat(StringConcat* curr) { curr->finalize(); }
void ReFinalize::visitFunction(Function* curr) {
// we may have changed the body from unreachable to none, which might be bad
diff --git a/src/ir/cost.h b/src/ir/cost.h
index afc091ffb..2dcf7c456 100644
--- a/src/ir/cost.h
+++ b/src/ir/cost.h
@@ -681,6 +681,9 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, CostType> {
CostType visitStringEncode(StringEncode* curr) {
return 6 + visit(curr->ref) + visit(curr->ptr);
}
+ CostType visitStringConcat(StringConcat* curr) {
+ return 10 + visit(curr->left) + visit(curr->right);
+ }
private:
CostType nullCheckCost(Expression* ref) {
diff --git a/src/ir/effects.h b/src/ir/effects.h
index 8e33f65bf..dbd1bb48f 100644
--- a/src/ir/effects.h
+++ b/src/ir/effects.h
@@ -742,6 +742,10 @@ private:
// traps when ref is null or we write out of bounds.
parent.implicitTrap = true;
}
+ void visitStringConcat(StringConcat* curr) {
+ // traps when an input is null.
+ parent.implicitTrap = true;
+ }
};
public:
diff --git a/src/ir/possible-contents.cpp b/src/ir/possible-contents.cpp
index c865585f1..21eadc50b 100644
--- a/src/ir/possible-contents.cpp
+++ b/src/ir/possible-contents.cpp
@@ -689,6 +689,10 @@ struct InfoCollector
// TODO: optimize when possible
addRoot(curr);
}
+ void visitStringConcat(StringConcat* curr) {
+ // TODO: optimize when possible
+ addRoot(curr);
+ }
// TODO: Model which throws can go to which catches. For now, anything thrown
// is sent to the location of that tag, and any catch of that tag can
diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp
index 49cade313..dc0214453 100644
--- a/src/passes/Print.cpp
+++ b/src/passes/Print.cpp
@@ -2272,6 +2272,9 @@ struct PrintExpressionContents
WASM_UNREACHABLE("invalid string.encode*");
}
}
+ void visitStringConcat(StringConcat* curr) {
+ printMedium(o, "string.concat");
+ }
};
// Prints an expression in s-expr format, including both the
diff --git a/src/wasm-binary.h b/src/wasm-binary.h
index 14658890e..6734d3bd6 100644
--- a/src/wasm-binary.h
+++ b/src/wasm-binary.h
@@ -1144,6 +1144,7 @@ enum ASTNodes {
StringMeasureWTF16 = 0x85,
StringEncodeWTF8 = 0x86,
StringEncodeWTF16 = 0x87,
+ StringConcat = 0x88,
};
enum MemoryAccess {
@@ -1728,6 +1729,7 @@ public:
bool maybeVisitStringConst(Expression*& out, uint32_t code);
bool maybeVisitStringMeasure(Expression*& out, uint32_t code);
bool maybeVisitStringEncode(Expression*& out, uint32_t code);
+ bool maybeVisitStringConcat(Expression*& out, uint32_t code);
void visitSelect(Select* curr, uint8_t code);
void visitReturn(Return* curr);
void visitMemorySize(MemorySize* curr);
diff --git a/src/wasm-builder.h b/src/wasm-builder.h
index 75ebcd0a3..b9bb2f7aa 100644
--- a/src/wasm-builder.h
+++ b/src/wasm-builder.h
@@ -1020,6 +1020,13 @@ public:
ret->finalize();
return ret;
}
+ StringConcat* makeStringConcat(Expression* left, Expression* right) {
+ auto* ret = wasm.allocator.alloc<StringConcat>();
+ ret->left = left;
+ ret->right = right;
+ ret->finalize();
+ return ret;
+ }
// Additional helpers
diff --git a/src/wasm-delegations-fields.def b/src/wasm-delegations-fields.def
index 7d9ffc37d..7f450b16d 100644
--- a/src/wasm-delegations-fields.def
+++ b/src/wasm-delegations-fields.def
@@ -742,6 +742,13 @@ switch (DELEGATE_ID) {
DELEGATE_END(StringEncode);
break;
}
+ case Expression::Id::StringConcatId: {
+ DELEGATE_START(StringConcat);
+ DELEGATE_FIELD_CHILD(StringConcat, right);
+ DELEGATE_FIELD_CHILD(StringConcat, left);
+ DELEGATE_END(StringConcat);
+ break;
+ }
}
#undef DELEGATE_ID
diff --git a/src/wasm-delegations.def b/src/wasm-delegations.def
index 5ee2c0a67..7802c63fb 100644
--- a/src/wasm-delegations.def
+++ b/src/wasm-delegations.def
@@ -89,5 +89,6 @@ DELEGATE(StringNew);
DELEGATE(StringConst);
DELEGATE(StringMeasure);
DELEGATE(StringEncode);
+DELEGATE(StringConcat);
#undef DELEGATE
diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h
index c1bd06e1c..05de81256 100644
--- a/src/wasm-interpreter.h
+++ b/src/wasm-interpreter.h
@@ -1967,6 +1967,9 @@ public:
Flow visitStringEncode(StringEncode* curr) {
WASM_UNREACHABLE("unimplemented string.encode");
}
+ Flow visitStringConcat(StringConcat* curr) {
+ WASM_UNREACHABLE("unimplemented string.concat");
+ }
virtual void trap(const char* why) { WASM_UNREACHABLE("unimp"); }
diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h
index 3ecde7633..9eb4a2085 100644
--- a/src/wasm-s-parser.h
+++ b/src/wasm-s-parser.h
@@ -307,6 +307,7 @@ private:
Expression* makeStringConst(Element& s);
Expression* makeStringMeasure(Element& s, StringMeasureOp op);
Expression* makeStringEncode(Element& s, StringEncodeOp op);
+ Expression* makeStringConcat(Element& s);
// Helper functions
Type parseOptionalResultType(Element& s, Index& i);
diff --git a/src/wasm.h b/src/wasm.h
index 1cb0b381b..957d2d368 100644
--- a/src/wasm.h
+++ b/src/wasm.h
@@ -701,6 +701,7 @@ public:
StringConstId,
StringMeasureId,
StringEncodeId,
+ StringConcatId,
NumExpressionIds
};
Id _id;
@@ -1714,6 +1715,16 @@ public:
void finalize();
};
+class StringConcat : public SpecificExpression<Expression::StringConcatId> {
+public:
+ StringConcat(MixedArena& allocator) {}
+
+ Expression* left;
+ Expression* right;
+
+ void finalize();
+};
+
// Globals
struct Named {
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp
index 429ff7bae..5aa5948fe 100644
--- a/src/wasm/wasm-binary.cpp
+++ b/src/wasm/wasm-binary.cpp
@@ -3930,6 +3930,9 @@ BinaryConsts::ASTNodes WasmBinaryBuilder::readExpression(Expression*& curr) {
if (maybeVisitStringEncode(curr, opcode)) {
break;
}
+ if (maybeVisitStringConcat(curr, opcode)) {
+ break;
+ }
if (opcode == BinaryConsts::RefIsFunc ||
opcode == BinaryConsts::RefIsData ||
opcode == BinaryConsts::RefIsI31) {
@@ -7220,6 +7223,17 @@ bool WasmBinaryBuilder::maybeVisitStringEncode(Expression*& out,
return true;
}
+bool WasmBinaryBuilder::maybeVisitStringConcat(Expression*& out,
+ uint32_t code) {
+ if (code != BinaryConsts::StringConcat) {
+ return false;
+ }
+ auto* right = popNonVoidExpression();
+ auto* left = popNonVoidExpression();
+ out = Builder(wasm).makeStringConcat(left, right);
+ return true;
+}
+
void WasmBinaryBuilder::visitRefAs(RefAs* curr, uint8_t code) {
BYN_TRACE("zz node: RefAs\n");
switch (code) {
diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp
index 68bd69104..fe52864d8 100644
--- a/src/wasm/wasm-s-parser.cpp
+++ b/src/wasm/wasm-s-parser.cpp
@@ -2990,6 +2990,11 @@ Expression* SExpressionWasmBuilder::makeStringEncode(Element& s,
op, parseExpression(s[i]), parseExpression(s[i + 1]));
}
+Expression* SExpressionWasmBuilder::makeStringConcat(Element& s) {
+ return Builder(wasm).makeStringConcat(parseExpression(s[1]),
+ parseExpression(s[2]));
+}
+
// converts an s-expression string representing binary data into an output
// sequence of raw bytes this appends to data, which may already contain
// content.
diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp
index 38c8f1661..6596cccc7 100644
--- a/src/wasm/wasm-stack.cpp
+++ b/src/wasm/wasm-stack.cpp
@@ -2300,6 +2300,10 @@ void BinaryInstWriter::visitStringEncode(StringEncode* curr) {
}
}
+void BinaryInstWriter::visitStringConcat(StringConcat* curr) {
+ o << int8_t(BinaryConsts::GCPrefix) << U32LEB(BinaryConsts::StringConcat);
+}
+
void BinaryInstWriter::emitScopeEnd(Expression* curr) {
assert(!breakStack.empty());
breakStack.pop_back();
diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp
index d4337a87d..d3b1c2151 100644
--- a/src/wasm/wasm.cpp
+++ b/src/wasm/wasm.cpp
@@ -1200,6 +1200,14 @@ void StringEncode::finalize() {
}
}
+void StringConcat::finalize() {
+ if (left->type == Type::unreachable || right->type == Type::unreachable) {
+ type = Type::unreachable;
+ } else {
+ type = Type(HeapType::string, NonNullable);
+ }
+}
+
size_t Function::getNumParams() { return getParams().size(); }
size_t Function::getNumVars() { return vars.size(); }
diff --git a/src/wasm2js.h b/src/wasm2js.h
index 6042e2f5b..c6b5cbb6c 100644
--- a/src/wasm2js.h
+++ b/src/wasm2js.h
@@ -2319,6 +2319,10 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m,
unimplemented(curr);
WASM_UNREACHABLE("unimp");
}
+ Ref visitStringConcat(StringConcat* curr) {
+ unimplemented(curr);
+ WASM_UNREACHABLE("unimp");
+ }
Ref visitRefAs(RefAs* curr) {
unimplemented(curr);
WASM_UNREACHABLE("unimp");
diff --git a/test/lit/strings.wast b/test/lit/strings.wast
index 5afdfdbcb..64974d005 100644
--- a/test/lit/strings.wast
+++ b/test/lit/strings.wast
@@ -11,6 +11,8 @@
;; CHECK: (type $none_=>_none (func))
+ ;; CHECK: (type $ref?|string|_ref?|string|_=>_none (func (param stringref stringref)))
+
;; CHECK: (global $string-const stringref (string.const "string in a global"))
(global $string-const stringref (string.const "string in a global"))
@@ -185,4 +187,21 @@
)
)
)
+
+ ;; CHECK: (func $string.concat (param $a stringref) (param $b stringref)
+ ;; CHECK-NEXT: (local.set $a
+ ;; CHECK-NEXT: (string.concat
+ ;; CHECK-NEXT: (local.get $a)
+ ;; CHECK-NEXT: (local.get $b)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $string.concat (param $a stringref) (param $b stringref)
+ (local.set $a ;; validate the output is a stringref
+ (string.concat
+ (local.get $a)
+ (local.get $b)
+ )
+ )
+ )
)