Commit f03814f8 authored by Andy Wingo's avatar Andy Wingo Committed by V8 LUCI CQ

[stringrefs] Implement stringview_wtf16.slice

Bug: v8:12868
Change-Id: Ib92cbf7eae8e4b7bc76bc918011be747254d931f
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3700075
Commit-Queue: Andy Wingo <wingo@igalia.com>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/main@{#81087}
parent f1ecc259
......@@ -823,4 +823,12 @@ transitioning builtin WasmStringViewWtf16GetCodeUnit(
unreachable;
}
}
transitioning builtin WasmStringViewWtf16Slice(
string: String, start: uint32, end: uint32): String {
const length = Unsigned(string.length);
if (start >= length) return kEmptyString;
if (end < start) return kEmptyString;
const clampedEnd = Convert<uintptr>(end < length ? end + 1 : length);
return string::SubString(string, Convert<uintptr>(start), clampedEnd);
}
}
......@@ -5796,6 +5796,17 @@ Node* WasmGraphBuilder::StringViewWtf16GetCodeUnit(
Operator::kNoDeopt, string, offset);
}
Node* WasmGraphBuilder::StringViewWtf16Slice(Node* string,
CheckForNull null_check,
Node* start, Node* end,
wasm::WasmCodePosition position) {
if (null_check == kWithNullCheck) {
string = AssertNotNull(string, position);
}
return gasm_->CallBuiltin(Builtin::kWasmStringViewWtf16Slice,
Operator::kNoDeopt, string, start, end);
}
// 1 bit V8 Smi tag, 31 bits V8 Smi shift, 1 bit i31ref high-bit truncation.
constexpr int kI31To32BitSmiShift = 33;
......
......@@ -550,6 +550,8 @@ class WasmGraphBuilder {
Node* StringViewWtf16GetCodeUnit(Node* string, CheckForNull null_check,
Node* offset,
wasm::WasmCodePosition position);
Node* StringViewWtf16Slice(Node* string, CheckForNull null_check, Node* start,
Node* end, wasm::WasmCodePosition position);
Node* IsNull(Node* object);
Node* TypeGuard(Node* value, wasm::ValueType type);
......
......@@ -6208,7 +6208,27 @@ class LiftoffCompiler {
void StringViewWtf16Slice(FullDecoder* decoder, const Value& view,
const Value& start, const Value& end,
Value* result) {
UNIMPLEMENTED();
LiftoffRegList pinned;
LiftoffRegister end_reg = pinned.set(__ PopToRegister(pinned));
LiftoffRegister start_reg = pinned.set(__ PopToRegister(pinned));
LiftoffRegister view_reg = pinned.set(__ PopToRegister(pinned));
MaybeEmitNullCheck(decoder, view_reg.gp(), pinned, view.type);
LiftoffAssembler::VarState view_var(kRef, view_reg, 0);
LiftoffAssembler::VarState start_var(kI32, start_reg, 0);
LiftoffAssembler::VarState end_var(kI32, end_reg, 0);
CallRuntimeStub(WasmCode::kWasmStringViewWtf16Slice,
MakeSig::Returns(kRef).Params(kRef, kI32, kI32),
{
view_var,
start_var,
end_var,
},
decoder->position());
RegisterDebugSideTableEntry(decoder, DebugSideTableBuilder::kDidSpill);
LiftoffRegister result_reg(kReturnRegister0);
__ PushRegister(kRef, result_reg);
}
void StringAsIter(FullDecoder* decoder, const Value& str, Value* result) {
......
......@@ -1493,7 +1493,9 @@ class WasmGraphBuildingInterface {
void StringViewWtf16Slice(FullDecoder* decoder, const Value& view,
const Value& start, const Value& end,
Value* result) {
UNIMPLEMENTED();
result->node = builder_->StringViewWtf16Slice(
view.node, NullCheckFor(view.type), start.node, end.node,
decoder->position());
}
void StringAsIter(FullDecoder* decoder, const Value& str, Value* result) {
......
......@@ -128,7 +128,8 @@ struct WasmModule;
V(WasmStringConst) \
V(WasmStringMeasureUtf8) \
V(WasmStringMeasureWtf8) \
V(WasmStringViewWtf16GetCodeUnit)
V(WasmStringViewWtf16GetCodeUnit) \
V(WasmStringViewWtf16Slice)
// Sorted, disjoint and non-overlapping memory regions. A region is of the
// form [start, end). So there's no [start, end), [end, other_end),
......
......@@ -10,6 +10,8 @@ let kSig_w_ii = makeSig([kWasmI32, kWasmI32], [kWasmStringRef]);
let kSig_w_v = makeSig([], [kWasmStringRef]);
let kSig_i_w = makeSig([kWasmStringRef], [kWasmI32]);
let kSig_i_wi = makeSig([kWasmStringRef, kWasmI32], [kWasmI32]);
let kSig_w_wii = makeSig([kWasmStringRef, kWasmI32, kWasmI32],
[kWasmStringRef]);
function encodeWtf8(str) {
// String iterator coalesces surrogate pairs.
......@@ -278,6 +280,25 @@ function makeWtf16TestDataSegment() {
kGCPrefix, kExprStringViewWtf16GetCodeunit
]);
builder.addFunction("string_view_wtf16_slice", kSig_w_wii)
.exportFunc()
.addBody([
kExprLocalGet, 0,
kGCPrefix, kExprStringAsWtf16,
kExprLocalGet, 1,
kExprLocalGet, 2,
kGCPrefix, kExprStringViewWtf16Slice
]);
builder.addFunction("string_view_wtf16_slice_null", kSig_w_v)
.exportFunc()
.addBody([
kExprRefNull, kStringViewWtf16Code,
kExprI32Const, 0,
kExprI32Const, 0,
kGCPrefix, kExprStringViewWtf16Slice
]);
let instance = builder.instantiate();
for (let str of interestingStrings) {
assertEquals(str.length, instance.exports.string_view_wtf16_length(str));
......@@ -285,12 +306,22 @@ function makeWtf16TestDataSegment() {
assertEquals(str.charCodeAt(i),
instance.exports.string_view_wtf16_get_codeunit(str, i));
}
assertEquals(str, instance.exports.string_view_wtf16_slice(str, 0, -1));
}
assertEquals("f", instance.exports.string_view_wtf16_slice("foo", 0, 0));
assertEquals("fo", instance.exports.string_view_wtf16_slice("foo", 0, 1));
assertEquals("foo", instance.exports.string_view_wtf16_slice("foo", 0, 2));
assertEquals("oo", instance.exports.string_view_wtf16_slice("foo", 1, 2));
assertEquals("oo", instance.exports.string_view_wtf16_slice("foo", 1, 100));
assertEquals("", instance.exports.string_view_wtf16_slice("foo", 1, 0));
assertThrows(() => instance.exports.string_view_wtf16_length_null(),
WebAssembly.RuntimeError, "dereferencing a null pointer");
assertThrows(() => instance.exports.string_view_wtf16_get_codeunit_null(),
WebAssembly.RuntimeError, "dereferencing a null pointer");
assertThrows(() => instance.exports.string_view_wtf16_get_codeunit("", 0),
WebAssembly.RuntimeError, "string offset out of bounds");
assertThrows(() => instance.exports.string_view_wtf16_slice_null(),
WebAssembly.RuntimeError, "dereferencing a null pointer");
})();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment