[stringrefs] Implement stringview_iter.advance, stringview_iter.rewind

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

View File

@ -1091,4 +1091,46 @@ builtin WasmStringViewIterNext(view: WasmStringViewIter): int32 {
view.offset = offset + 1;
return Signed(Convert<uint32>(code));
}
builtin WasmStringViewIterAdvance(
view: WasmStringViewIter, codepoints: uint32): uint32 {
const string = view.string;
let offset = view.offset;
let advanced: uint32 = 0;
while (advanced < codepoints) {
if (offset == Unsigned(string.length)) break;
advanced = advanced + 1;
if (offset + 1 < Unsigned(string.length) &&
IsLeadSurrogate(StringCharCodeAt(string, Convert<uintptr>(offset))) &&
IsTrailSurrogate(
StringCharCodeAt(string, Convert<uintptr>(offset + 1)))) {
offset = offset + 2;
} else {
offset = offset + 1;
}
}
view.offset = offset;
return advanced;
}
builtin WasmStringViewIterRewind(
view: WasmStringViewIter, codepoints: uint32): uint32 {
const string = view.string;
let offset = view.offset;
let rewound: uint32 = 0;
if (string.length == 0) return 0;
while (rewound < codepoints) {
if (offset == 0) break;
rewound = rewound + 1;
if (offset >= 2 &&
IsTrailSurrogate(
StringCharCodeAt(string, Convert<uintptr>(offset - 1))) &&
IsLeadSurrogate(
StringCharCodeAt(string, Convert<uintptr>(offset - 2)))) {
offset = offset - 2;
} else {
offset = offset - 1;
}
}
view.offset = offset;
return rewound;
}
}

View File

@ -5987,6 +5987,26 @@ Node* WasmGraphBuilder::StringViewIterNext(Node* view, CheckForNull null_check,
Operator::kNoDeopt, view);
}
Node* WasmGraphBuilder::StringViewIterAdvance(Node* view,
CheckForNull null_check,
Node* codepoints,
wasm::WasmCodePosition position) {
if (null_check == kWithNullCheck) view = AssertNotNull(view, position);
return gasm_->CallBuiltin(Builtin::kWasmStringViewIterAdvance,
Operator::kNoDeopt, view, codepoints);
}
Node* WasmGraphBuilder::StringViewIterRewind(Node* view,
CheckForNull null_check,
Node* codepoints,
wasm::WasmCodePosition position) {
if (null_check == kWithNullCheck) view = AssertNotNull(view, position);
return gasm_->CallBuiltin(Builtin::kWasmStringViewIterRewind,
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

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

View File

@ -6868,12 +6868,52 @@ class LiftoffCompiler {
void StringViewIterAdvance(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::kWasmStringViewIterAdvance,
MakeSig::Returns(kI32).Params(kRef, kI32),
{
view_var,
codepoints_var,
},
decoder->position());
RegisterDebugSideTableEntry(decoder, DebugSideTableBuilder::kDidSpill);
LiftoffRegister result_reg(kReturnRegister0);
__ PushRegister(kI32, result_reg);
}
void StringViewIterRewind(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::kWasmStringViewIterRewind,
MakeSig::Returns(kI32).Params(kRef, kI32),
{
view_var,
codepoints_var,
},
decoder->position());
RegisterDebugSideTableEntry(decoder, DebugSideTableBuilder::kDidSpill);
LiftoffRegister result_reg(kReturnRegister0);
__ PushRegister(kI32, result_reg);
}
void StringViewIterSlice(FullDecoder* decoder, const Value& view,

View File

@ -1561,12 +1561,16 @@ class WasmGraphBuildingInterface {
void StringViewIterAdvance(FullDecoder* decoder, const Value& view,
const Value& codepoints, Value* result) {
UNIMPLEMENTED();
result->node =
builder_->StringViewIterAdvance(view.node, NullCheckFor(view.type),
codepoints.node, decoder->position());
}
void StringViewIterRewind(FullDecoder* decoder, const Value& view,
const Value& codepoints, Value* result) {
UNIMPLEMENTED();
result->node =
builder_->StringViewIterRewind(view.node, NullCheckFor(view.type),
codepoints.node, decoder->position());
}
void StringViewIterSlice(FullDecoder* decoder, const Value& view,

View File

@ -145,7 +145,9 @@ struct WasmModule;
V(WasmStringViewWtf8Encode) \
V(WasmStringViewWtf8Slice) \
V(WasmStringAsIter) \
V(WasmStringViewIterNext)
V(WasmStringViewIterNext) \
V(WasmStringViewIterAdvance) \
V(WasmStringViewIterRewind)
// 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

@ -1069,19 +1069,76 @@ function makeWtf16TestDataSegment() {
kGCPrefix, kExprStringViewIterNext
]);
builder.addFunction("advance", kSig_i_i)
.exportFunc()
.addBody([
kExprGlobalGet, global.index,
kExprLocalGet, 0,
kGCPrefix, kExprStringViewIterAdvance
]);
builder.addFunction("advance_null", kSig_i_v)
.exportFunc()
.addBody([
kExprRefNull, kStringViewIterCode,
kExprI32Const, 0,
kGCPrefix, kExprStringViewIterAdvance
]);
builder.addFunction("rewind", kSig_i_i)
.exportFunc()
.addBody([
kExprGlobalGet, global.index,
kExprLocalGet, 0,
kGCPrefix, kExprStringViewIterRewind
]);
builder.addFunction("rewind_null", kSig_i_v)
.exportFunc()
.addBody([
kExprRefNull, kStringViewIterCode,
kExprI32Const, 0,
kGCPrefix, kExprStringViewIterRewind
]);
let instance = builder.instantiate();
for (let str of interestingStrings) {
instance.exports.iterate(str);
let codepoints = [];
for (let codepoint of str) {
assertEquals(codepoint.codePointAt(0), instance.exports.next());
codepoints.push(codepoint.codePointAt(0));
}
instance.exports.iterate(str);
for (let codepoint of codepoints) {
assertEquals(codepoint, instance.exports.next());
}
assertEquals(-1, instance.exports.next());
assertEquals(-1, instance.exports.next());
for (let i = 1; i <= codepoints.length; i++) {
assertEquals(i, instance.exports.rewind(i));
assertEquals(codepoints[codepoints.length - i], instance.exports.next());
assertEquals(i - 1, instance.exports.advance(-1));
}
for (let i = 0; i < codepoints.length; i++) {
instance.exports.rewind(-1);
assertEquals(i, instance.exports.advance(i));
assertEquals(codepoints[i], instance.exports.next());
}
assertEquals(codepoints.length, instance.exports.rewind(-1));
assertEquals(0, instance.exports.rewind(-1));
assertEquals(codepoints.length, instance.exports.advance(-1));
assertEquals(0, instance.exports.advance(-1));
}
assertThrows(() => instance.exports.iterate_null(),
WebAssembly.RuntimeError, "dereferencing a null pointer");
assertThrows(() => instance.exports.next_null(),
WebAssembly.RuntimeError, "dereferencing a null pointer");
assertThrows(() => instance.exports.advance_null(),
WebAssembly.RuntimeError, "dereferencing a null pointer");
assertThrows(() => instance.exports.rewind_null(),
WebAssembly.RuntimeError, "dereferencing a null pointer");
})();