[stringrefs] Implement stringview_iter.slice

Feature complete!

Bug: v8:12868
Change-Id: I7727071bdd062a6dae26206a65080f675ef7ee93
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3758226
Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
Commit-Queue: Andy Wingo <wingo@igalia.com>
Cr-Commit-Position: refs/heads/main@{#81694}
This commit is contained in:
Andy Wingo 2022-07-12 16:25:10 +02:00 committed by V8 LUCI CQ
parent 77425d0a3e
commit 74289c46bb
7 changed files with 93 additions and 3 deletions

View File

@ -1133,4 +1133,25 @@ builtin WasmStringViewIterRewind(
view.offset = offset;
return rewound;
}
builtin WasmStringViewIterSlice(
view: WasmStringViewIter, codepoints: uint32): String {
const string = view.string;
const start = view.offset;
let end = view.offset;
let advanced: uint32 = 0;
while (advanced < codepoints) {
if (end == Unsigned(string.length)) break;
advanced = advanced + 1;
if (end + 1 < Unsigned(string.length) &&
IsLeadSurrogate(StringCharCodeAt(string, Convert<uintptr>(end))) &&
IsTrailSurrogate(StringCharCodeAt(string, Convert<uintptr>(end + 1)))) {
end = end + 2;
} else {
end = end + 1;
}
}
return (start == end) ?
kEmptyString :
string::SubString(string, Convert<uintptr>(start), Convert<uintptr>(end));
}
}

View File

@ -6007,6 +6007,15 @@ Node* WasmGraphBuilder::StringViewIterRewind(Node* view,
Operator::kNoDeopt, view, codepoints);
}
Node* WasmGraphBuilder::StringViewIterSlice(Node* view, CheckForNull null_check,
Node* codepoints,
wasm::WasmCodePosition position) {
if (null_check == kWithNullCheck) view = AssertNotNull(view, position);
return gasm_->CallBuiltin(Builtin::kWasmStringViewIterSlice,
Operator::kNoDeopt, view, codepoints);
}
// 1 bit V8 Smi tag, 31 bits V8 Smi shift, 1 bit i31ref high-bit truncation.
constexpr int kI31To32BitSmiShift = 33;

View File

@ -596,6 +596,8 @@ class WasmGraphBuilder {
wasm::WasmCodePosition position);
Node* StringViewIterRewind(Node* view, CheckForNull null_check,
Node* codepoints, wasm::WasmCodePosition position);
Node* StringViewIterSlice(Node* view, CheckForNull null_check,
Node* codepoints, wasm::WasmCodePosition position);
Node* IsNull(Node* object);
Node* TypeGuard(Node* value, wasm::ValueType type);

View File

@ -6918,7 +6918,27 @@ class LiftoffCompiler {
void StringViewIterSlice(FullDecoder* decoder, const Value& view,
const Value& codepoints, Value* result) {
UNIMPLEMENTED();
LiftoffRegList pinned;
LiftoffAssembler::VarState& codepoints_var =
__ cache_state()->stack_state.end()[-1];
LiftoffRegister view_reg = pinned.set(
__ LoadToRegister(__ cache_state()->stack_state.end()[-2], pinned));
MaybeEmitNullCheck(decoder, view_reg.gp(), pinned, view.type);
LiftoffAssembler::VarState view_var(kRef, view_reg, 0);
CallRuntimeStub(WasmCode::kWasmStringViewIterSlice,
MakeSig::Returns(kRef).Params(kRef, kI32),
{
view_var,
codepoints_var,
},
decoder->position());
RegisterDebugSideTableEntry(decoder, DebugSideTableBuilder::kDidSpill);
LiftoffRegister result_reg(kReturnRegister0);
__ PushRegister(kRef, result_reg);
}
void Forward(FullDecoder* decoder, const Value& from, Value* to) {

View File

@ -1575,7 +1575,9 @@ class WasmGraphBuildingInterface {
void StringViewIterSlice(FullDecoder* decoder, const Value& view,
const Value& codepoints, Value* result) {
UNIMPLEMENTED();
SetAndTypeNode(result, builder_->StringViewIterSlice(
view.node, NullCheckFor(view.type),
codepoints.node, decoder->position()));
}
void Forward(FullDecoder* decoder, const Value& from, Value* to) {

View File

@ -147,7 +147,8 @@ struct WasmModule;
V(WasmStringAsIter) \
V(WasmStringViewIterNext) \
V(WasmStringViewIterAdvance) \
V(WasmStringViewIterRewind)
V(WasmStringViewIterRewind) \
V(WasmStringViewIterSlice)
// Sorted, disjoint and non-overlapping memory regions. A region is of the
// form [start, end). So there's no [start, end), [end, other_end),

View File

@ -17,6 +17,7 @@ let kSig_i_wiii = makeSig([kWasmStringRef, kWasmI32, kWasmI32, kWasmI32],
let kSig_ii_wiii = makeSig([kWasmStringRef, kWasmI32, kWasmI32, kWasmI32],
[kWasmI32, kWasmI32]);
let kSig_v_w = makeSig([kWasmStringRef], []);
let kSig_w_i = makeSig([kWasmI32], [kWasmStringRef]);
let kSig_w_wii = makeSig([kWasmStringRef, kWasmI32, kWasmI32],
[kWasmStringRef]);
let kSig_w_ww = makeSig([kWasmStringRef, kWasmStringRef], [kWasmStringRef]);
@ -1101,6 +1102,22 @@ function makeWtf16TestDataSegment() {
kGCPrefix, kExprStringViewIterRewind
]);
builder.addFunction("slice", kSig_w_i)
.exportFunc()
.addBody([
kExprGlobalGet, global.index,
kExprLocalGet, 0,
kGCPrefix, kExprStringViewIterSlice
]);
builder.addFunction("slice_null", kSig_w_i)
.exportFunc()
.addBody([
kExprRefNull, kStringViewIterCode,
kExprI32Const, 0,
kGCPrefix, kExprStringViewIterSlice
]);
let instance = builder.instantiate();
for (let str of interestingStrings) {
@ -1131,6 +1148,22 @@ function makeWtf16TestDataSegment() {
assertEquals(0, instance.exports.rewind(-1));
assertEquals(codepoints.length, instance.exports.advance(-1));
assertEquals(0, instance.exports.advance(-1));
for (let start = 0; start <= codepoints.length; start++) {
for (let end = start; end <= codepoints.length; end++) {
let expected_slice =
String.fromCodePoint(...codepoints.slice(start, end));
instance.exports.iterate(str);
assertEquals(start, instance.exports.advance(start));
assertEquals(expected_slice, instance.exports.slice(end - start));
}
}
instance.exports.iterate(str);
assertEquals(str, instance.exports.slice(codepoints.length));
assertEquals(str, instance.exports.slice(-1));
assertEquals("", instance.exports.slice(0));
assertEquals(codepoints.length, instance.exports.advance(-1));
assertEquals("", instance.exports.slice(-1));
}
assertThrows(() => instance.exports.iterate_null(),
@ -1141,4 +1174,6 @@ function makeWtf16TestDataSegment() {
WebAssembly.RuntimeError, "dereferencing a null pointer");
assertThrows(() => instance.exports.rewind_null(),
WebAssembly.RuntimeError, "dereferencing a null pointer");
assertThrows(() => instance.exports.slice_null(),
WebAssembly.RuntimeError, "dereferencing a null pointer");
})();