[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:
parent
994b64148e
commit
77425d0a3e
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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),
|
||||
|
@ -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");
|
||||
})();
|
||||
|
Loading…
Reference in New Issue
Block a user