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

[stringrefs] Implement stringview_wtf8.slice

Bug: v8:12868
Change-Id: I93595dfc168b6e4702b67bdd7355a1f7c18caa46
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3757332Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Commit-Queue: Andy Wingo <wingo@igalia.com>
Cr-Commit-Position: refs/heads/main@{#81669}
parent d0a4c900
...@@ -57,6 +57,8 @@ extern runtime WasmStringEncodeWtf16( ...@@ -57,6 +57,8 @@ extern runtime WasmStringEncodeWtf16(
extern runtime WasmStringAsWtf8(Context, String): ByteArray; extern runtime WasmStringAsWtf8(Context, String): ByteArray;
extern runtime WasmStringViewWtf8Encode( extern runtime WasmStringViewWtf8Encode(
Context, WasmInstanceObject, Smi, ByteArray, Number, Number, Number): JSAny; Context, WasmInstanceObject, Smi, ByteArray, Number, Number, Number): JSAny;
extern runtime WasmStringViewWtf8Slice(
Context, ByteArray, Number, Number): String;
} }
namespace unsafe { namespace unsafe {
...@@ -1005,6 +1007,17 @@ builtin WasmStringViewWtf8Encode( ...@@ -1005,6 +1007,17 @@ builtin WasmStringViewWtf8Encode(
bytesWritten: Convert<uintptr>(end - start) bytesWritten: Convert<uintptr>(end - start)
}; };
} }
builtin WasmStringViewWtf8Slice(
view: ByteArray, start: uint32, end: uint32): String {
const start = WasmStringViewWtf8Advance(view, start, 0);
const end = WasmStringViewWtf8Advance(view, end, 0);
if (end <= start) return kEmptyString;
tail runtime::WasmStringViewWtf8Slice(
LoadContextFromFrame(), view, WasmUint32ToNumber(start),
WasmUint32ToNumber(end));
}
transitioning builtin WasmStringViewWtf16GetCodeUnit( transitioning builtin WasmStringViewWtf16GetCodeUnit(
string: String, offset: uint32): uint32 { string: String, offset: uint32): uint32 {
try { try {
......
...@@ -5927,6 +5927,16 @@ void WasmGraphBuilder::StringViewWtf8Encode( ...@@ -5927,6 +5927,16 @@ void WasmGraphBuilder::StringViewWtf8Encode(
*bytes_written = gasm_->Projection(1, pair); *bytes_written = gasm_->Projection(1, pair);
} }
Node* WasmGraphBuilder::StringViewWtf8Slice(Node* view, CheckForNull null_check,
Node* pos, Node* bytes,
wasm::WasmCodePosition position) {
if (null_check == kWithNullCheck) {
view = AssertNotNull(view, position);
}
return gasm_->CallBuiltin(Builtin::kWasmStringViewWtf8Slice,
Operator::kNoDeopt, view, pos, bytes);
}
Node* WasmGraphBuilder::StringViewWtf16GetCodeUnit( Node* WasmGraphBuilder::StringViewWtf16GetCodeUnit(
Node* string, CheckForNull null_check, Node* offset, Node* string, CheckForNull null_check, Node* offset,
wasm::WasmCodePosition position) { wasm::WasmCodePosition position) {
......
...@@ -576,6 +576,8 @@ class WasmGraphBuilder { ...@@ -576,6 +576,8 @@ class WasmGraphBuilder {
Node* pos, Node* bytes, Node** next_pos, Node* pos, Node* bytes, Node** next_pos,
Node** bytes_written, Node** bytes_written,
wasm::WasmCodePosition position); wasm::WasmCodePosition position);
Node* StringViewWtf8Slice(Node* view, CheckForNull null_check, Node* pos,
Node* bytes, wasm::WasmCodePosition position);
Node* StringViewWtf16GetCodeUnit(Node* string, CheckForNull null_check, Node* StringViewWtf16GetCodeUnit(Node* string, CheckForNull null_check,
Node* offset, Node* offset,
wasm::WasmCodePosition position); wasm::WasmCodePosition position);
......
...@@ -815,6 +815,20 @@ MaybeHandle<String> Factory::NewStringFromUtf8( ...@@ -815,6 +815,20 @@ MaybeHandle<String> Factory::NewStringFromUtf8(
allocation); allocation);
} }
MaybeHandle<String> Factory::NewStringFromUtf8(
Handle<ByteArray> array, uint32_t start, uint32_t end,
unibrow::Utf8Variant utf8_variant, AllocationType allocation) {
DCHECK_LE(start, end);
DCHECK_LE(end, array->length());
auto peek_bytes = [&]() -> base::Vector<const uint8_t> {
const uint8_t* contents =
reinterpret_cast<const uint8_t*>(array->GetDataStartAddress());
return {contents + start, end - start};
};
return NewStringFromUtf8Variant(isolate(), peek_bytes, utf8_variant,
allocation);
}
namespace { namespace {
struct Wtf16Decoder { struct Wtf16Decoder {
int length_; int length_;
......
...@@ -272,6 +272,11 @@ class V8_EXPORT_PRIVATE Factory : public FactoryBase<Factory> { ...@@ -272,6 +272,11 @@ class V8_EXPORT_PRIVATE Factory : public FactoryBase<Factory> {
unibrow::Utf8Variant utf8_variant, unibrow::Utf8Variant utf8_variant,
AllocationType allocation = AllocationType::kYoung); AllocationType allocation = AllocationType::kYoung);
V8_WARN_UNUSED_RESULT MaybeHandle<String> NewStringFromUtf8(
Handle<ByteArray> array, uint32_t start, uint32_t end,
unibrow::Utf8Variant utf8_variant,
AllocationType allocation = AllocationType::kYoung);
V8_WARN_UNUSED_RESULT MaybeHandle<String> NewStringFromUtf16( V8_WARN_UNUSED_RESULT MaybeHandle<String> NewStringFromUtf16(
Handle<WasmArray> array, uint32_t start, uint32_t end, Handle<WasmArray> array, uint32_t start, uint32_t end,
AllocationType allocation = AllocationType::kYoung); AllocationType allocation = AllocationType::kYoung);
......
...@@ -1284,5 +1284,21 @@ RUNTIME_FUNCTION(Runtime_WasmStringViewWtf8Encode) { ...@@ -1284,5 +1284,21 @@ RUNTIME_FUNCTION(Runtime_WasmStringViewWtf8Encode) {
return Smi(0); return Smi(0);
} }
RUNTIME_FUNCTION(Runtime_WasmStringViewWtf8Slice) {
ClearThreadInWasmScope flag_scope(isolate);
DCHECK_EQ(3, args.length());
HandleScope scope(isolate);
Handle<ByteArray> array(ByteArray::cast(args[0]), isolate);
uint32_t start = NumberToUint32(args[1]);
uint32_t end = NumberToUint32(args[2]);
DCHECK_LT(start, end);
DCHECK(base::IsInBounds<size_t>(start, end - start, array->length()));
RETURN_RESULT_OR_FAILURE(isolate,
isolate->factory()->NewStringFromUtf8(
array, start, end, unibrow::Utf8Variant::kWtf8));
}
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
...@@ -620,7 +620,8 @@ namespace internal { ...@@ -620,7 +620,8 @@ namespace internal {
F(WasmStringEncodeWtf16, 6, 1) \ F(WasmStringEncodeWtf16, 6, 1) \
F(WasmStringEncodeWtf8Array, 4, 1) \ F(WasmStringEncodeWtf8Array, 4, 1) \
F(WasmStringAsWtf8, 1, 1) \ F(WasmStringAsWtf8, 1, 1) \
F(WasmStringViewWtf8Encode, 6, 1) F(WasmStringViewWtf8Encode, 6, 1) \
F(WasmStringViewWtf8Slice, 3, 1)
#define FOR_EACH_INTRINSIC_WASM_TEST(F, I) \ #define FOR_EACH_INTRINSIC_WASM_TEST(F, I) \
F(DeserializeWasmModule, 2, 1) \ F(DeserializeWasmModule, 2, 1) \
......
...@@ -6710,7 +6710,31 @@ class LiftoffCompiler { ...@@ -6710,7 +6710,31 @@ class LiftoffCompiler {
void StringViewWtf8Slice(FullDecoder* decoder, const Value& view, void StringViewWtf8Slice(FullDecoder* decoder, const Value& view,
const Value& start, const Value& end, const Value& start, const Value& end,
Value* result) { Value* result) {
UNIMPLEMENTED(); LiftoffRegList pinned;
LiftoffAssembler::VarState& end_var =
__ cache_state()->stack_state.end()[-1];
LiftoffAssembler::VarState& start_var =
__ cache_state()->stack_state.end()[-2];
LiftoffRegister view_reg = pinned.set(
__ LoadToRegister(__ cache_state()->stack_state.end()[-3], pinned));
MaybeEmitNullCheck(decoder, view_reg.gp(), pinned, view.type);
LiftoffAssembler::VarState view_var(kRef, view_reg, 0);
CallRuntimeStub(WasmCode::kWasmStringViewWtf8Slice,
MakeSig::Returns(kRef).Params(kRef, kI32, kI32),
{
view_var,
start_var,
end_var,
},
decoder->position());
__ DropValues(3);
RegisterDebugSideTableEntry(decoder, DebugSideTableBuilder::kDidSpill);
LiftoffRegister result_reg(kReturnRegister0);
__ PushRegister(kRef, result_reg);
} }
void StringViewWtf16GetCodeUnit(FullDecoder* decoder, const Value& view, void StringViewWtf16GetCodeUnit(FullDecoder* decoder, const Value& view,
......
...@@ -1518,7 +1518,9 @@ class WasmGraphBuildingInterface { ...@@ -1518,7 +1518,9 @@ class WasmGraphBuildingInterface {
void StringViewWtf8Slice(FullDecoder* decoder, const Value& view, void StringViewWtf8Slice(FullDecoder* decoder, const Value& view,
const Value& start, const Value& end, const Value& start, const Value& end,
Value* result) { Value* result) {
UNIMPLEMENTED(); SetAndTypeNode(result, builder_->StringViewWtf8Slice(
view.node, NullCheckFor(view.type), start.node,
end.node, decoder->position()));
} }
void StringViewWtf16GetCodeUnit(FullDecoder* decoder, const Value& view, void StringViewWtf16GetCodeUnit(FullDecoder* decoder, const Value& view,
......
...@@ -142,7 +142,8 @@ struct WasmModule; ...@@ -142,7 +142,8 @@ struct WasmModule;
V(WasmStringEncodeWtf16Array) \ V(WasmStringEncodeWtf16Array) \
V(WasmStringAsWtf8) \ V(WasmStringAsWtf8) \
V(WasmStringViewWtf8Advance) \ V(WasmStringViewWtf8Advance) \
V(WasmStringViewWtf8Encode) V(WasmStringViewWtf8Encode) \
V(WasmStringViewWtf8Slice)
// Sorted, disjoint and non-overlapping memory regions. A region is of the // Sorted, disjoint and non-overlapping memory regions. A region is of the
// form [start, end). So there's no [start, end), [end, other_end), // form [start, end). So there's no [start, end), [end, other_end),
......
...@@ -871,6 +871,25 @@ function makeWtf16TestDataSegment() { ...@@ -871,6 +871,25 @@ function makeWtf16TestDataSegment() {
kExprDrop kExprDrop
]); ]);
builder.addFunction(`slice`, kSig_w_wii)
.exportFunc()
.addBody([
kExprLocalGet, 0,
kGCPrefix, kExprStringAsWtf8,
kExprLocalGet, 1,
kExprLocalGet, 2,
kGCPrefix, kExprStringViewWtf8Slice
]);
builder.addFunction("slice_null", kSig_v_v)
.exportFunc()
.addBody([
kExprRefNull, kStringViewWtf8Code,
kExprI32Const, 0,
kExprI32Const, 0,
kGCPrefix, kExprStringViewWtf8Slice,
kExprDrop
]);
function Wtf8StartsCodepoint(wtf8, offset) { function Wtf8StartsCodepoint(wtf8, offset) {
return (wtf8[offset] & 0xc0) != 0x80; return (wtf8[offset] & 0xc0) != 0x80;
} }
...@@ -994,8 +1013,22 @@ function makeWtf16TestDataSegment() { ...@@ -994,8 +1013,22 @@ function makeWtf16TestDataSegment() {
} }
} }
for (let str of interestingStrings) {
let wtf8 = encodeWtf8(str);
for (let start = 0; start <= wtf8.length; start++) {
for (let end = start; end <= wtf8.length; end++) {
let expected_slice = decodeWtf8(wtf8,
Wtf8PositionTreatment(wtf8, start),
Wtf8PositionTreatment(wtf8, end));
assertEquals(expected_slice, instance.exports.slice(str, start, end));
}
}
}
assertThrows(() => instance.exports.advance_null(), assertThrows(() => instance.exports.advance_null(),
WebAssembly.RuntimeError, "dereferencing a null pointer"); WebAssembly.RuntimeError, "dereferencing a null pointer");
assertThrows(() => instance.exports.encode_null(), assertThrows(() => instance.exports.encode_null(),
WebAssembly.RuntimeError, "dereferencing a null pointer"); WebAssembly.RuntimeError, "dereferencing a null pointer");
assertThrows(() => instance.exports.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