Skip to content

Commit b88bedb

Browse files
authored
[Strings] string.concat (#4777)
1 parent 19a4375 commit b88bedb

20 files changed

+113
-3
lines changed

scripts/gen-s-parser.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -621,6 +621,7 @@
621621
("string.measure_wtf16", "makeStringMeasure(s, StringMeasureWTF16)"),
622622
("string.encode_wtf8", "makeStringEncode(s, StringEncodeWTF8)"),
623623
("string.encode_wtf16", "makeStringEncode(s, StringEncodeWTF16)"),
624+
("string.concat", "makeStringConcat(s)"),
624625
]
625626

626627

src/gen-s-parser.inc

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3129,9 +3129,17 @@ switch (op[0]) {
31293129
switch (op[3]) {
31303130
case 'i': {
31313131
switch (op[7]) {
3132-
case 'c':
3133-
if (strcmp(op, "string.const") == 0) { return makeStringConst(s); }
3134-
goto parse_error;
3132+
case 'c': {
3133+
switch (op[10]) {
3134+
case 'c':
3135+
if (strcmp(op, "string.concat") == 0) { return makeStringConcat(s); }
3136+
goto parse_error;
3137+
case 's':
3138+
if (strcmp(op, "string.const") == 0) { return makeStringConst(s); }
3139+
goto parse_error;
3140+
default: goto parse_error;
3141+
}
3142+
}
31353143
case 'e': {
31363144
switch (op[17]) {
31373145
case '1':

src/ir/ReFinalize.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ void ReFinalize::visitStringNew(StringNew* curr) { curr->finalize(); }
176176
void ReFinalize::visitStringConst(StringConst* curr) { curr->finalize(); }
177177
void ReFinalize::visitStringMeasure(StringMeasure* curr) { curr->finalize(); }
178178
void ReFinalize::visitStringEncode(StringEncode* curr) { curr->finalize(); }
179+
void ReFinalize::visitStringConcat(StringConcat* curr) { curr->finalize(); }
179180

180181
void ReFinalize::visitFunction(Function* curr) {
181182
// we may have changed the body from unreachable to none, which might be bad

src/ir/cost.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,9 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, CostType> {
681681
CostType visitStringEncode(StringEncode* curr) {
682682
return 6 + visit(curr->ref) + visit(curr->ptr);
683683
}
684+
CostType visitStringConcat(StringConcat* curr) {
685+
return 10 + visit(curr->left) + visit(curr->right);
686+
}
684687

685688
private:
686689
CostType nullCheckCost(Expression* ref) {

src/ir/effects.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -742,6 +742,10 @@ class EffectAnalyzer {
742742
// traps when ref is null or we write out of bounds.
743743
parent.implicitTrap = true;
744744
}
745+
void visitStringConcat(StringConcat* curr) {
746+
// traps when an input is null.
747+
parent.implicitTrap = true;
748+
}
745749
};
746750

747751
public:

src/ir/possible-contents.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -689,6 +689,10 @@ struct InfoCollector
689689
// TODO: optimize when possible
690690
addRoot(curr);
691691
}
692+
void visitStringConcat(StringConcat* curr) {
693+
// TODO: optimize when possible
694+
addRoot(curr);
695+
}
692696

693697
// TODO: Model which throws can go to which catches. For now, anything thrown
694698
// is sent to the location of that tag, and any catch of that tag can

src/passes/Print.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2272,6 +2272,9 @@ struct PrintExpressionContents
22722272
WASM_UNREACHABLE("invalid string.encode*");
22732273
}
22742274
}
2275+
void visitStringConcat(StringConcat* curr) {
2276+
printMedium(o, "string.concat");
2277+
}
22752278
};
22762279

22772280
// Prints an expression in s-expr format, including both the

src/wasm-binary.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1144,6 +1144,7 @@ enum ASTNodes {
11441144
StringMeasureWTF16 = 0x85,
11451145
StringEncodeWTF8 = 0x86,
11461146
StringEncodeWTF16 = 0x87,
1147+
StringConcat = 0x88,
11471148
};
11481149

11491150
enum MemoryAccess {
@@ -1728,6 +1729,7 @@ class WasmBinaryBuilder {
17281729
bool maybeVisitStringConst(Expression*& out, uint32_t code);
17291730
bool maybeVisitStringMeasure(Expression*& out, uint32_t code);
17301731
bool maybeVisitStringEncode(Expression*& out, uint32_t code);
1732+
bool maybeVisitStringConcat(Expression*& out, uint32_t code);
17311733
void visitSelect(Select* curr, uint8_t code);
17321734
void visitReturn(Return* curr);
17331735
void visitMemorySize(MemorySize* curr);

src/wasm-builder.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1020,6 +1020,13 @@ class Builder {
10201020
ret->finalize();
10211021
return ret;
10221022
}
1023+
StringConcat* makeStringConcat(Expression* left, Expression* right) {
1024+
auto* ret = wasm.allocator.alloc<StringConcat>();
1025+
ret->left = left;
1026+
ret->right = right;
1027+
ret->finalize();
1028+
return ret;
1029+
}
10231030

10241031
// Additional helpers
10251032

src/wasm-delegations-fields.def

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -742,6 +742,13 @@ switch (DELEGATE_ID) {
742742
DELEGATE_END(StringEncode);
743743
break;
744744
}
745+
case Expression::Id::StringConcatId: {
746+
DELEGATE_START(StringConcat);
747+
DELEGATE_FIELD_CHILD(StringConcat, right);
748+
DELEGATE_FIELD_CHILD(StringConcat, left);
749+
DELEGATE_END(StringConcat);
750+
break;
751+
}
745752
}
746753

747754
#undef DELEGATE_ID

src/wasm-delegations.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,5 +89,6 @@ DELEGATE(StringNew);
8989
DELEGATE(StringConst);
9090
DELEGATE(StringMeasure);
9191
DELEGATE(StringEncode);
92+
DELEGATE(StringConcat);
9293

9394
#undef DELEGATE

src/wasm-interpreter.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1967,6 +1967,9 @@ class ExpressionRunner : public OverriddenVisitor<SubType, Flow> {
19671967
Flow visitStringEncode(StringEncode* curr) {
19681968
WASM_UNREACHABLE("unimplemented string.encode");
19691969
}
1970+
Flow visitStringConcat(StringConcat* curr) {
1971+
WASM_UNREACHABLE("unimplemented string.concat");
1972+
}
19701973

19711974
virtual void trap(const char* why) { WASM_UNREACHABLE("unimp"); }
19721975

src/wasm-s-parser.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,7 @@ class SExpressionWasmBuilder {
307307
Expression* makeStringConst(Element& s);
308308
Expression* makeStringMeasure(Element& s, StringMeasureOp op);
309309
Expression* makeStringEncode(Element& s, StringEncodeOp op);
310+
Expression* makeStringConcat(Element& s);
310311

311312
// Helper functions
312313
Type parseOptionalResultType(Element& s, Index& i);

src/wasm.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -701,6 +701,7 @@ class Expression {
701701
StringConstId,
702702
StringMeasureId,
703703
StringEncodeId,
704+
StringConcatId,
704705
NumExpressionIds
705706
};
706707
Id _id;
@@ -1714,6 +1715,16 @@ class StringEncode : public SpecificExpression<Expression::StringEncodeId> {
17141715
void finalize();
17151716
};
17161717

1718+
class StringConcat : public SpecificExpression<Expression::StringConcatId> {
1719+
public:
1720+
StringConcat(MixedArena& allocator) {}
1721+
1722+
Expression* left;
1723+
Expression* right;
1724+
1725+
void finalize();
1726+
};
1727+
17171728
// Globals
17181729

17191730
struct Named {

src/wasm/wasm-binary.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3930,6 +3930,9 @@ BinaryConsts::ASTNodes WasmBinaryBuilder::readExpression(Expression*& curr) {
39303930
if (maybeVisitStringEncode(curr, opcode)) {
39313931
break;
39323932
}
3933+
if (maybeVisitStringConcat(curr, opcode)) {
3934+
break;
3935+
}
39333936
if (opcode == BinaryConsts::RefIsFunc ||
39343937
opcode == BinaryConsts::RefIsData ||
39353938
opcode == BinaryConsts::RefIsI31) {
@@ -7220,6 +7223,17 @@ bool WasmBinaryBuilder::maybeVisitStringEncode(Expression*& out,
72207223
return true;
72217224
}
72227225

7226+
bool WasmBinaryBuilder::maybeVisitStringConcat(Expression*& out,
7227+
uint32_t code) {
7228+
if (code != BinaryConsts::StringConcat) {
7229+
return false;
7230+
}
7231+
auto* right = popNonVoidExpression();
7232+
auto* left = popNonVoidExpression();
7233+
out = Builder(wasm).makeStringConcat(left, right);
7234+
return true;
7235+
}
7236+
72237237
void WasmBinaryBuilder::visitRefAs(RefAs* curr, uint8_t code) {
72247238
BYN_TRACE("zz node: RefAs\n");
72257239
switch (code) {

src/wasm/wasm-s-parser.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2990,6 +2990,11 @@ Expression* SExpressionWasmBuilder::makeStringEncode(Element& s,
29902990
op, parseExpression(s[i]), parseExpression(s[i + 1]));
29912991
}
29922992

2993+
Expression* SExpressionWasmBuilder::makeStringConcat(Element& s) {
2994+
return Builder(wasm).makeStringConcat(parseExpression(s[1]),
2995+
parseExpression(s[2]));
2996+
}
2997+
29932998
// converts an s-expression string representing binary data into an output
29942999
// sequence of raw bytes this appends to data, which may already contain
29953000
// content.

src/wasm/wasm-stack.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2300,6 +2300,10 @@ void BinaryInstWriter::visitStringEncode(StringEncode* curr) {
23002300
}
23012301
}
23022302

2303+
void BinaryInstWriter::visitStringConcat(StringConcat* curr) {
2304+
o << int8_t(BinaryConsts::GCPrefix) << U32LEB(BinaryConsts::StringConcat);
2305+
}
2306+
23032307
void BinaryInstWriter::emitScopeEnd(Expression* curr) {
23042308
assert(!breakStack.empty());
23052309
breakStack.pop_back();

src/wasm/wasm.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1200,6 +1200,14 @@ void StringEncode::finalize() {
12001200
}
12011201
}
12021202

1203+
void StringConcat::finalize() {
1204+
if (left->type == Type::unreachable || right->type == Type::unreachable) {
1205+
type = Type::unreachable;
1206+
} else {
1207+
type = Type(HeapType::string, NonNullable);
1208+
}
1209+
}
1210+
12031211
size_t Function::getNumParams() { return getParams().size(); }
12041212

12051213
size_t Function::getNumVars() { return vars.size(); }

src/wasm2js.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2319,6 +2319,10 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m,
23192319
unimplemented(curr);
23202320
WASM_UNREACHABLE("unimp");
23212321
}
2322+
Ref visitStringConcat(StringConcat* curr) {
2323+
unimplemented(curr);
2324+
WASM_UNREACHABLE("unimp");
2325+
}
23222326
Ref visitRefAs(RefAs* curr) {
23232327
unimplemented(curr);
23242328
WASM_UNREACHABLE("unimp");

test/lit/strings.wast

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
;; CHECK: (type $none_=>_none (func))
1313

14+
;; CHECK: (type $ref?|string|_ref?|string|_=>_none (func (param stringref stringref)))
15+
1416
;; CHECK: (global $string-const stringref (string.const "string in a global"))
1517
(global $string-const stringref (string.const "string in a global"))
1618

@@ -185,4 +187,21 @@
185187
)
186188
)
187189
)
190+
191+
;; CHECK: (func $string.concat (param $a stringref) (param $b stringref)
192+
;; CHECK-NEXT: (local.set $a
193+
;; CHECK-NEXT: (string.concat
194+
;; CHECK-NEXT: (local.get $a)
195+
;; CHECK-NEXT: (local.get $b)
196+
;; CHECK-NEXT: )
197+
;; CHECK-NEXT: )
198+
;; CHECK-NEXT: )
199+
(func $string.concat (param $a stringref) (param $b stringref)
200+
(local.set $a ;; validate the output is a stringref
201+
(string.concat
202+
(local.get $a)
203+
(local.get $b)
204+
)
205+
)
206+
)
188207
)

0 commit comments

Comments
 (0)