[stringrefs] Implement string.concat
Bug: v8:12868 Change-Id: I7f5487dc01b006caef582f792e007d3a3cabb324 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3702435 Commit-Queue: Andy Wingo <wingo@igalia.com> Reviewed-by: Jakob Kummerow <jkummerow@chromium.org> Cr-Commit-Position: refs/heads/main@{#81151}
This commit is contained in:
parent
2eea0da9f1
commit
163ed720c7
@ -829,6 +829,10 @@ builtin WasmStringEncodeWtf16(
|
||||
LoadContextFromInstance(instance), instance, memory, string,
|
||||
WasmUint32ToNumber(offset), SmiConstant(0), SmiFromInt32(string.length));
|
||||
}
|
||||
builtin WasmStringConcat(a: String, b: String): String {
|
||||
const context = LoadContextFromFrame();
|
||||
return a + b;
|
||||
}
|
||||
transitioning builtin WasmStringViewWtf16GetCodeUnit(
|
||||
string: String, offset: uint32): uint32 {
|
||||
try {
|
||||
|
@ -5809,6 +5809,15 @@ Node* WasmGraphBuilder::StringEncodeWtf16(uint32_t memory, Node* string,
|
||||
string, offset, gasm_->SmiConstant(memory));
|
||||
}
|
||||
|
||||
Node* WasmGraphBuilder::StringConcat(Node* head, CheckForNull head_null_check,
|
||||
Node* tail, CheckForNull tail_null_check,
|
||||
wasm::WasmCodePosition position) {
|
||||
if (head_null_check == kWithNullCheck) head = AssertNotNull(head, position);
|
||||
if (tail_null_check == kWithNullCheck) tail = AssertNotNull(tail, position);
|
||||
return gasm_->CallBuiltin(Builtin::kWasmStringConcat, Operator::kNoDeopt,
|
||||
head, tail);
|
||||
}
|
||||
|
||||
Node* WasmGraphBuilder::StringViewWtf16GetCodeUnit(
|
||||
Node* string, CheckForNull null_check, Node* offset,
|
||||
wasm::WasmCodePosition position) {
|
||||
|
@ -553,6 +553,9 @@ class WasmGraphBuilder {
|
||||
Node* StringEncodeWtf16(uint32_t memory, Node* string,
|
||||
CheckForNull null_check, Node* offset,
|
||||
wasm::WasmCodePosition position);
|
||||
Node* StringConcat(Node* head, CheckForNull head_null_check, Node* tail,
|
||||
CheckForNull tail_null_check,
|
||||
wasm::WasmCodePosition position);
|
||||
Node* StringViewWtf16GetCodeUnit(Node* string, CheckForNull null_check,
|
||||
Node* offset,
|
||||
wasm::WasmCodePosition position);
|
||||
|
@ -6198,7 +6198,27 @@ class LiftoffCompiler {
|
||||
|
||||
void StringConcat(FullDecoder* decoder, const Value& head, const Value& tail,
|
||||
Value* result) {
|
||||
UNIMPLEMENTED();
|
||||
LiftoffRegList pinned;
|
||||
|
||||
LiftoffRegister tail_reg = pinned.set(__ PopToRegister(pinned));
|
||||
MaybeEmitNullCheck(decoder, tail_reg.gp(), pinned, tail.type);
|
||||
LiftoffAssembler::VarState tail_var(kRef, tail_reg, 0);
|
||||
|
||||
LiftoffRegister head_reg = pinned.set(__ PopToRegister(pinned));
|
||||
MaybeEmitNullCheck(decoder, head_reg.gp(), pinned, head.type);
|
||||
LiftoffAssembler::VarState head_var(kRef, head_reg, 0);
|
||||
|
||||
CallRuntimeStub(WasmCode::kWasmStringConcat,
|
||||
MakeSig::Returns(kRef).Params(kRef, kRef),
|
||||
{
|
||||
head_var,
|
||||
tail_var,
|
||||
},
|
||||
decoder->position());
|
||||
RegisterDebugSideTableEntry(decoder, DebugSideTableBuilder::kDidSpill);
|
||||
|
||||
LiftoffRegister result_reg(kReturnRegister0);
|
||||
__ PushRegister(kRef, result_reg);
|
||||
}
|
||||
|
||||
void StringEq(FullDecoder* decoder, const Value& a, const Value& b,
|
||||
|
@ -1448,7 +1448,9 @@ class WasmGraphBuildingInterface {
|
||||
|
||||
void StringConcat(FullDecoder* decoder, const Value& head, const Value& tail,
|
||||
Value* result) {
|
||||
UNIMPLEMENTED();
|
||||
result->node =
|
||||
builder_->StringConcat(head.node, NullCheckFor(head.type), tail.node,
|
||||
NullCheckFor(tail.type), decoder->position());
|
||||
}
|
||||
|
||||
void StringEq(FullDecoder* decoder, const Value& a, const Value& b,
|
||||
|
@ -130,6 +130,7 @@ struct WasmModule;
|
||||
V(WasmStringMeasureWtf8) \
|
||||
V(WasmStringEncodeWtf8) \
|
||||
V(WasmStringEncodeWtf16) \
|
||||
V(WasmStringConcat) \
|
||||
V(WasmStringViewWtf16GetCodeUnit) \
|
||||
V(WasmStringViewWtf16Encode) \
|
||||
V(WasmStringViewWtf16Slice)
|
||||
|
@ -15,6 +15,8 @@ let kSig_w_wii = makeSig([kWasmStringRef, kWasmI32, kWasmI32],
|
||||
let kSig_v_wi = makeSig([kWasmStringRef, kWasmI32], []);
|
||||
let kSig_v_wiii = makeSig([kWasmStringRef, kWasmI32, kWasmI32, kWasmI32],
|
||||
[]);
|
||||
let kSig_w_ww = makeSig([kWasmStringRef, kWasmStringRef], [kWasmStringRef]);
|
||||
let kSig_w_w = makeSig([kWasmStringRef], [kWasmStringRef]);
|
||||
|
||||
function encodeWtf8(str) {
|
||||
// String iterator coalesces surrogate pairs.
|
||||
@ -433,6 +435,46 @@ function HasIsolatedSurrogate(str) {
|
||||
}
|
||||
})();
|
||||
|
||||
(function TestStringConcat() {
|
||||
let builder = new WasmModuleBuilder();
|
||||
|
||||
builder.addFunction("concat", kSig_w_ww)
|
||||
.exportFunc()
|
||||
.addBody([
|
||||
kExprLocalGet, 0,
|
||||
kExprLocalGet, 1,
|
||||
kGCPrefix, kExprStringConcat
|
||||
]);
|
||||
|
||||
builder.addFunction("concat_null_head", kSig_w_w)
|
||||
.exportFunc()
|
||||
.addBody([
|
||||
kExprRefNull, kStringRefCode,
|
||||
kExprLocalGet, 0,
|
||||
kGCPrefix, kExprStringConcat
|
||||
]);
|
||||
builder.addFunction("concat_null_tail", kSig_w_w)
|
||||
.exportFunc()
|
||||
.addBody([
|
||||
kExprLocalGet, 0,
|
||||
kExprRefNull, kStringRefCode,
|
||||
kGCPrefix, kExprStringConcat
|
||||
]);
|
||||
|
||||
let instance = builder.instantiate();
|
||||
|
||||
for (let head of interestingStrings) {
|
||||
for (let tail of interestingStrings) {
|
||||
assertEquals(head + tail, instance.exports.concat(head, tail));
|
||||
}
|
||||
}
|
||||
|
||||
assertThrows(() => instance.exports.concat_null_head("hey"),
|
||||
WebAssembly.RuntimeError, "dereferencing a null pointer");
|
||||
assertThrows(() => instance.exports.concat_null_tail("hey"),
|
||||
WebAssembly.RuntimeError, "dereferencing a null pointer");
|
||||
})();
|
||||
|
||||
(function TestStringViewWtf16() {
|
||||
let builder = new WasmModuleBuilder();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user