[stringrefs] Implement stringview_wtf16.encode
Bug: v8:12868 Change-Id: I9b7cbd3851b3819bcc2c32e273ddae16b9d812ca Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3702266 Reviewed-by: Jakob Kummerow <jkummerow@chromium.org> Commit-Queue: Andy Wingo <wingo@igalia.com> Cr-Commit-Position: refs/heads/main@{#81144}
This commit is contained in:
parent
a6b7f1f3f4
commit
9efa9e3c92
@ -841,6 +841,19 @@ transitioning builtin WasmStringViewWtf16GetCodeUnit(
|
||||
unreachable;
|
||||
}
|
||||
}
|
||||
builtin WasmStringViewWtf16Encode(
|
||||
offset: uint32, start: uint32, length: uint32, string: String,
|
||||
memory: Smi): JSAny {
|
||||
const instance = LoadInstanceFromFrame();
|
||||
const clampedStart =
|
||||
start < Unsigned(string.length) ? start : Unsigned(string.length);
|
||||
const maxLength = Unsigned(string.length) - clampedStart;
|
||||
const clampedLength = length < maxLength ? length : maxLength;
|
||||
tail runtime::WasmStringEncodeWtf16(
|
||||
LoadContextFromInstance(instance), instance, memory, string,
|
||||
WasmUint32ToNumber(offset), SmiFromUint32(clampedStart),
|
||||
SmiFromUint32(clampedLength));
|
||||
}
|
||||
transitioning builtin WasmStringViewWtf16Slice(
|
||||
string: String, start: uint32, end: uint32): String {
|
||||
const length = Unsigned(string.length);
|
||||
|
@ -5819,6 +5819,19 @@ Node* WasmGraphBuilder::StringViewWtf16GetCodeUnit(
|
||||
Operator::kNoDeopt, string, offset);
|
||||
}
|
||||
|
||||
Node* WasmGraphBuilder::StringViewWtf16Encode(uint32_t memory, Node* string,
|
||||
CheckForNull null_check,
|
||||
Node* offset, Node* start,
|
||||
Node* codeunits,
|
||||
wasm::WasmCodePosition position) {
|
||||
if (null_check == kWithNullCheck) {
|
||||
string = AssertNotNull(string, position);
|
||||
}
|
||||
return gasm_->CallBuiltin(Builtin::kWasmStringViewWtf16Encode,
|
||||
Operator::kNoDeopt, offset, start, codeunits,
|
||||
string, gasm_->SmiConstant(memory));
|
||||
}
|
||||
|
||||
Node* WasmGraphBuilder::StringViewWtf16Slice(Node* string,
|
||||
CheckForNull null_check,
|
||||
Node* start, Node* end,
|
||||
|
@ -556,6 +556,10 @@ class WasmGraphBuilder {
|
||||
Node* StringViewWtf16GetCodeUnit(Node* string, CheckForNull null_check,
|
||||
Node* offset,
|
||||
wasm::WasmCodePosition position);
|
||||
Node* StringViewWtf16Encode(uint32_t memory, Node* string,
|
||||
CheckForNull null_check, Node* offset,
|
||||
Node* start, Node* length,
|
||||
wasm::WasmCodePosition position);
|
||||
Node* StringViewWtf16Slice(Node* string, CheckForNull null_check, Node* start,
|
||||
Node* end, wasm::WasmCodePosition position);
|
||||
Node* IsNull(Node* object);
|
||||
|
@ -6254,9 +6254,39 @@ class LiftoffCompiler {
|
||||
|
||||
void StringViewWtf16Encode(FullDecoder* decoder,
|
||||
const MemoryIndexImmediate<validate>& imm,
|
||||
const Value& view, const Value& addr,
|
||||
const Value& view, const Value& offset,
|
||||
const Value& pos, const Value& codeunits) {
|
||||
UNIMPLEMENTED();
|
||||
LiftoffRegList pinned;
|
||||
|
||||
LiftoffAssembler::VarState& codeunits_var =
|
||||
__ cache_state()->stack_state.end()[-1];
|
||||
LiftoffAssembler::VarState& pos_var =
|
||||
__ cache_state()->stack_state.end()[-2];
|
||||
LiftoffAssembler::VarState& offset_var =
|
||||
__ cache_state()->stack_state.end()[-3];
|
||||
|
||||
LiftoffRegister view_reg = pinned.set(
|
||||
__ LoadToRegister(__ cache_state()->stack_state.end()[-4], pinned));
|
||||
MaybeEmitNullCheck(decoder, view_reg.gp(), pinned, view.type);
|
||||
LiftoffAssembler::VarState view_var(kRef, view_reg, 0);
|
||||
|
||||
LiftoffRegister memory_reg =
|
||||
pinned.set(__ GetUnusedRegister(kGpReg, pinned));
|
||||
LoadSmi(memory_reg, imm.index);
|
||||
LiftoffAssembler::VarState memory_var(kPointerKind, memory_reg, 0);
|
||||
|
||||
CallRuntimeStub(WasmCode::kWasmStringViewWtf16Encode,
|
||||
MakeSig::Params(kI32, kI32, kI32, kRef, kSmiKind),
|
||||
{
|
||||
offset_var,
|
||||
pos_var,
|
||||
codeunits_var,
|
||||
view_var,
|
||||
memory_var,
|
||||
},
|
||||
decoder->position());
|
||||
__ DropValues(4);
|
||||
RegisterDebugSideTableEntry(decoder, DebugSideTableBuilder::kDidSpill);
|
||||
}
|
||||
|
||||
void StringViewWtf16Slice(FullDecoder* decoder, const Value& view,
|
||||
|
@ -1488,9 +1488,11 @@ class WasmGraphBuildingInterface {
|
||||
|
||||
void StringViewWtf16Encode(FullDecoder* decoder,
|
||||
const MemoryIndexImmediate<validate>& imm,
|
||||
const Value& view, const Value& addr,
|
||||
const Value& view, const Value& offset,
|
||||
const Value& pos, const Value& codeunits) {
|
||||
UNIMPLEMENTED();
|
||||
builder_->StringViewWtf16Encode(
|
||||
imm.index, view.node, NullCheckFor(view.type), offset.node, pos.node,
|
||||
codeunits.node, decoder->position());
|
||||
}
|
||||
|
||||
void StringViewWtf16Slice(FullDecoder* decoder, const Value& view,
|
||||
|
@ -131,6 +131,7 @@ struct WasmModule;
|
||||
V(WasmStringEncodeWtf8) \
|
||||
V(WasmStringEncodeWtf16) \
|
||||
V(WasmStringViewWtf16GetCodeUnit) \
|
||||
V(WasmStringViewWtf16Encode) \
|
||||
V(WasmStringViewWtf16Slice)
|
||||
|
||||
// Sorted, disjoint and non-overlapping memory regions. A region is of the
|
||||
|
@ -13,6 +13,8 @@ let kSig_i_wi = makeSig([kWasmStringRef, kWasmI32], [kWasmI32]);
|
||||
let kSig_w_wii = makeSig([kWasmStringRef, kWasmI32, kWasmI32],
|
||||
[kWasmStringRef]);
|
||||
let kSig_v_wi = makeSig([kWasmStringRef, kWasmI32], []);
|
||||
let kSig_v_wiii = makeSig([kWasmStringRef, kWasmI32, kWasmI32, kWasmI32],
|
||||
[]);
|
||||
|
||||
function encodeWtf8(str) {
|
||||
// String iterator coalesces surrogate pairs.
|
||||
@ -434,7 +436,9 @@ function HasIsolatedSurrogate(str) {
|
||||
(function TestStringViewWtf16() {
|
||||
let builder = new WasmModuleBuilder();
|
||||
|
||||
builder.addFunction("string_view_wtf16_length", kSig_i_w)
|
||||
builder.addMemory(1, undefined, true /* exported */, false);
|
||||
|
||||
builder.addFunction("length", kSig_i_w)
|
||||
.exportFunc()
|
||||
.addBody([
|
||||
kExprLocalGet, 0,
|
||||
@ -442,14 +446,14 @@ function HasIsolatedSurrogate(str) {
|
||||
kGCPrefix, kExprStringViewWtf16Length
|
||||
]);
|
||||
|
||||
builder.addFunction("string_view_wtf16_length_null", kSig_i_v)
|
||||
builder.addFunction("length_null", kSig_i_v)
|
||||
.exportFunc()
|
||||
.addBody([
|
||||
kExprRefNull, kStringViewWtf16Code,
|
||||
kGCPrefix, kExprStringViewWtf16Length
|
||||
]);
|
||||
|
||||
builder.addFunction("string_view_wtf16_get_codeunit", kSig_i_wi)
|
||||
builder.addFunction("get_codeunit", kSig_i_wi)
|
||||
.exportFunc()
|
||||
.addBody([
|
||||
kExprLocalGet, 0,
|
||||
@ -458,7 +462,7 @@ function HasIsolatedSurrogate(str) {
|
||||
kGCPrefix, kExprStringViewWtf16GetCodeunit
|
||||
]);
|
||||
|
||||
builder.addFunction("string_view_wtf16_get_codeunit_null", kSig_i_v)
|
||||
builder.addFunction("get_codeunit_null", kSig_i_v)
|
||||
.exportFunc()
|
||||
.addBody([
|
||||
kExprRefNull, kStringViewWtf16Code,
|
||||
@ -466,7 +470,28 @@ function HasIsolatedSurrogate(str) {
|
||||
kGCPrefix, kExprStringViewWtf16GetCodeunit
|
||||
]);
|
||||
|
||||
builder.addFunction("string_view_wtf16_slice", kSig_w_wii)
|
||||
builder.addFunction("encode", kSig_v_wiii)
|
||||
.exportFunc()
|
||||
.addBody([
|
||||
kExprLocalGet, 0,
|
||||
kGCPrefix, kExprStringAsWtf16,
|
||||
kExprLocalGet, 1,
|
||||
kExprLocalGet, 2,
|
||||
kExprLocalGet, 3,
|
||||
kGCPrefix, kExprStringViewWtf16Encode, 0
|
||||
]);
|
||||
|
||||
builder.addFunction("encode_null", kSig_v_v)
|
||||
.exportFunc()
|
||||
.addBody([
|
||||
kExprRefNull, kStringViewWtf16Code,
|
||||
kExprI32Const, 0,
|
||||
kExprI32Const, 0,
|
||||
kExprI32Const, 0,
|
||||
kGCPrefix, kExprStringViewWtf16Encode, 0
|
||||
]);
|
||||
|
||||
builder.addFunction("slice", kSig_w_wii)
|
||||
.exportFunc()
|
||||
.addBody([
|
||||
kExprLocalGet, 0,
|
||||
@ -476,7 +501,7 @@ function HasIsolatedSurrogate(str) {
|
||||
kGCPrefix, kExprStringViewWtf16Slice
|
||||
]);
|
||||
|
||||
builder.addFunction("string_view_wtf16_slice_null", kSig_w_v)
|
||||
builder.addFunction("slice_null", kSig_w_v)
|
||||
.exportFunc()
|
||||
.addBody([
|
||||
kExprRefNull, kStringViewWtf16Code,
|
||||
@ -486,28 +511,88 @@ function HasIsolatedSurrogate(str) {
|
||||
]);
|
||||
|
||||
let instance = builder.instantiate();
|
||||
let memory = new Uint8Array(instance.exports.memory.buffer);
|
||||
for (let str of interestingStrings) {
|
||||
assertEquals(str.length, instance.exports.string_view_wtf16_length(str));
|
||||
assertEquals(str.length, instance.exports.length(str));
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
assertEquals(str.charCodeAt(i),
|
||||
instance.exports.string_view_wtf16_get_codeunit(str, i));
|
||||
instance.exports.get_codeunit(str, i));
|
||||
}
|
||||
assertEquals(str, instance.exports.string_view_wtf16_slice(str, 0, -1));
|
||||
assertEquals(str, instance.exports.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));
|
||||
function checkEncoding(str, slice, start, length) {
|
||||
let bytes = encodeWtf16LE(slice);
|
||||
function clearMemory(low, high) {
|
||||
for (let i = low; i < high; i++) {
|
||||
memory[i] = 0;
|
||||
}
|
||||
}
|
||||
function assertMemoryBytesZero(low, high) {
|
||||
for (let i = low; i < high; i++) {
|
||||
assertEquals(0, memory[i]);
|
||||
}
|
||||
}
|
||||
function checkMemory(offset, bytes) {
|
||||
let slop = 64;
|
||||
assertMemoryBytesZero(Math.max(0, offset - slop), offset);
|
||||
for (let i = 0; i < bytes.length; i++) {
|
||||
assertEquals(bytes[i], memory[offset + i]);
|
||||
}
|
||||
assertMemoryBytesZero(offset + bytes.length,
|
||||
Math.min(memory.length,
|
||||
offset + bytes.length + slop));
|
||||
}
|
||||
|
||||
assertThrows(() => instance.exports.string_view_wtf16_length_null(),
|
||||
for (let offset of [0, 42, memory.length - bytes.length]) {
|
||||
instance.exports.encode(str, offset, start, length);
|
||||
checkMemory(offset, bytes);
|
||||
clearMemory(offset, offset + bytes.length);
|
||||
}
|
||||
|
||||
assertThrows(() => instance.exports.encode(str, 1, start, length),
|
||||
WebAssembly.RuntimeError,
|
||||
"operation does not support unaligned accesses");
|
||||
assertThrows(
|
||||
() => instance.exports.encode(str, memory.length - bytes.length + 2,
|
||||
start, length),
|
||||
WebAssembly.RuntimeError, "memory access out of bounds");
|
||||
checkMemory(memory.length - bytes.length - 2, []);
|
||||
}
|
||||
checkEncoding("fox", "f", 0, 1);
|
||||
checkEncoding("fox", "fo", 0, 2);
|
||||
checkEncoding("fox", "fox", 0, 3);
|
||||
checkEncoding("fox", "fox", 0, 300);
|
||||
checkEncoding("fox", "", 1, 0);
|
||||
checkEncoding("fox", "o", 1, 1);
|
||||
checkEncoding("fox", "ox", 1, 2);
|
||||
checkEncoding("fox", "ox", 1, 200);
|
||||
checkEncoding("fox", "", 2, 0);
|
||||
checkEncoding("fox", "x", 2, 1);
|
||||
checkEncoding("fox", "x", 2, 2);
|
||||
checkEncoding("fox", "", 3, 0);
|
||||
checkEncoding("fox", "", 3, 1_000_000_000);
|
||||
checkEncoding("fox", "", 1_000_000_000, 1_000_000_000);
|
||||
checkEncoding("fox", "", 100, 100);
|
||||
// Bounds checks before alignment checks.
|
||||
assertThrows(() => instance.exports.encode("foo", memory.length - 1, 0, 3),
|
||||
WebAssembly.RuntimeError, "memory access out of bounds");
|
||||
|
||||
assertEquals("f", instance.exports.slice("foo", 0, 0));
|
||||
assertEquals("fo", instance.exports.slice("foo", 0, 1));
|
||||
assertEquals("foo", instance.exports.slice("foo", 0, 2));
|
||||
assertEquals("oo", instance.exports.slice("foo", 1, 2));
|
||||
assertEquals("oo", instance.exports.slice("foo", 1, 100));
|
||||
assertEquals("", instance.exports.slice("foo", 1, 0));
|
||||
|
||||
assertThrows(() => instance.exports.length_null(),
|
||||
WebAssembly.RuntimeError, "dereferencing a null pointer");
|
||||
assertThrows(() => instance.exports.string_view_wtf16_get_codeunit_null(),
|
||||
assertThrows(() => instance.exports.get_codeunit_null(),
|
||||
WebAssembly.RuntimeError, "dereferencing a null pointer");
|
||||
assertThrows(() => instance.exports.string_view_wtf16_get_codeunit("", 0),
|
||||
assertThrows(() => instance.exports.get_codeunit("", 0),
|
||||
WebAssembly.RuntimeError, "string offset out of bounds");
|
||||
assertThrows(() => instance.exports.string_view_wtf16_slice_null(),
|
||||
assertThrows(() => instance.exports.encode_null(),
|
||||
WebAssembly.RuntimeError, "dereferencing a null pointer");
|
||||
assertThrows(() => instance.exports.slice_null(),
|
||||
WebAssembly.RuntimeError, "dereferencing a null pointer");
|
||||
})();
|
||||
|
Loading…
Reference in New Issue
Block a user