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

[stringrefs] Implement string.encode_wtf16

Bug: v8:12868
Change-Id: Icdf4a04d55c59613e305ec8258485fd69b57e90a
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3702258Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Commit-Queue: Andy Wingo <wingo@igalia.com>
Cr-Commit-Position: refs/heads/main@{#81119}
parent a541dbcb
......@@ -47,6 +47,8 @@ extern runtime WasmStringMeasureUtf8(Context, String): Number;
extern runtime WasmStringMeasureWtf8(Context, String): Number;
extern runtime WasmStringEncodeWtf8(
Context, WasmInstanceObject, Smi, Smi, String, Number): JSAny;
extern runtime WasmStringEncodeWtf16(
Context, WasmInstanceObject, Smi, String, Number, Smi, Smi): JSAny;
}
namespace unsafe {
......@@ -820,6 +822,13 @@ builtin WasmStringEncodeWtf8(
LoadContextFromInstance(instance), instance, memory, policy, string,
WasmUint32ToNumber(offset));
}
builtin WasmStringEncodeWtf16(
string: String, offset: uint32, memory: Smi): JSAny {
const instance = LoadInstanceFromFrame();
tail runtime::WasmStringEncodeWtf16(
LoadContextFromInstance(instance), instance, memory, string,
WasmUint32ToNumber(offset), SmiConstant(0), SmiFromInt32(string.length));
}
transitioning builtin WasmStringViewWtf16GetCodeUnit(
string: String, offset: uint32): uint32 {
try {
......
......@@ -5799,6 +5799,16 @@ Node* WasmGraphBuilder::StringEncodeWtf8(uint32_t memory,
gasm_->SmiConstant(policy));
}
Node* WasmGraphBuilder::StringEncodeWtf16(uint32_t memory, Node* string,
CheckForNull null_check, Node* offset,
wasm::WasmCodePosition position) {
if (null_check == kWithNullCheck) {
string = AssertNotNull(string, position);
}
return gasm_->CallBuiltin(Builtin::kWasmStringEncodeWtf16, Operator::kNoDeopt,
string, offset, gasm_->SmiConstant(memory));
}
Node* WasmGraphBuilder::StringViewWtf16GetCodeUnit(
Node* string, CheckForNull null_check, Node* offset,
wasm::WasmCodePosition position) {
......
......@@ -550,6 +550,9 @@ class WasmGraphBuilder {
Node* StringEncodeWtf8(uint32_t memory, wasm::StringRefWtf8Policy policy,
Node* string, CheckForNull null_check, Node* offset,
wasm::WasmCodePosition position);
Node* StringEncodeWtf16(uint32_t memory, Node* string,
CheckForNull null_check, Node* offset,
wasm::WasmCodePosition position);
Node* StringViewWtf16GetCodeUnit(Node* string, CheckForNull null_check,
Node* offset,
wasm::WasmCodePosition position);
......
......@@ -1067,5 +1067,46 @@ RUNTIME_FUNCTION(Runtime_WasmStringEncodeWtf8) {
return Smi::zero(); // Unused.
}
RUNTIME_FUNCTION(Runtime_WasmStringEncodeWtf16) {
ClearThreadInWasmScope flag_scope(isolate);
DCHECK_EQ(6, args.length());
HandleScope scope(isolate);
Handle<WasmInstanceObject> instance = args.at<WasmInstanceObject>(0);
uint32_t memory = args.positive_smi_value_at(1);
Handle<String> string = args.at<String>(2);
uint32_t offset = NumberToUint32(args[3]);
uint32_t start = args.positive_smi_value_at(4);
uint32_t length = args.positive_smi_value_at(5);
DCHECK_EQ(memory, 0);
USE(memory);
DCHECK(base::IsInBounds<uint32_t>(start, length, string->length()));
size_t mem_size = instance->memory_size();
static_assert(String::kMaxLength <=
(std::numeric_limits<size_t>::max() / sizeof(base::uc16)));
if (!base::IsInBounds<size_t>(offset, length * sizeof(base::uc16),
mem_size)) {
return ThrowWasmError(isolate, MessageTemplate::kWasmTrapMemOutOfBounds);
}
if (offset & 1) {
return ThrowWasmError(isolate, MessageTemplate::kWasmTrapUnalignedAccess);
}
#if defined(V8_TARGET_LITTLE_ENDIAN)
uint16_t* dst =
reinterpret_cast<uint16_t*>(instance->memory_start() + offset);
String::WriteToFlat(*string, dst, start, length);
#elif defined(V8_TARGET_BIG_ENDIAN)
// TODO(12868): The host is big-endian but we need to write the string
// contents as little-endian.
UNIMPLEMENTED();
#else
#error Unknown endianness
#endif
return Smi::zero(); // Unused.
}
} // namespace internal
} // namespace v8
......@@ -617,7 +617,8 @@ namespace internal {
F(WasmStringConst, 2, 1) \
F(WasmStringMeasureUtf8, 1, 1) \
F(WasmStringMeasureWtf8, 1, 1) \
F(WasmStringEncodeWtf8, 5, 1)
F(WasmStringEncodeWtf8, 5, 1) \
F(WasmStringEncodeWtf16, 6, 1)
#define FOR_EACH_INTRINSIC_WASM_TEST(F, I) \
F(DeserializeWasmModule, 2, 1) \
......
......@@ -6168,8 +6168,32 @@ class LiftoffCompiler {
void StringEncodeWtf16(FullDecoder* decoder,
const MemoryIndexImmediate<validate>& imm,
const Value& str, const Value& address) {
UNIMPLEMENTED();
const Value& str, const Value& offset) {
LiftoffRegList pinned;
LiftoffAssembler::VarState& offset_var =
__ cache_state()->stack_state.end()[-1];
LiftoffRegister string_reg = pinned.set(
__ LoadToRegister(__ cache_state()->stack_state.end()[-2], pinned));
MaybeEmitNullCheck(decoder, string_reg.gp(), pinned, str.type);
LiftoffAssembler::VarState string_var(kRef, string_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::kWasmStringEncodeWtf16,
MakeSig::Params(kRef, kI32, kSmiKind),
{
string_var,
offset_var,
memory_var,
},
decoder->position());
__ DropValues(2);
RegisterDebugSideTableEntry(decoder, DebugSideTableBuilder::kDidSpill);
}
void StringConcat(FullDecoder* decoder, const Value& head, const Value& tail,
......
......@@ -1441,8 +1441,9 @@ class WasmGraphBuildingInterface {
void StringEncodeWtf16(FullDecoder* decoder,
const MemoryIndexImmediate<validate>& imm,
const Value& str, const Value& address) {
UNIMPLEMENTED();
const Value& str, const Value& offset) {
builder_->StringEncodeWtf16(imm.index, str.node, NullCheckFor(str.type),
offset.node, decoder->position());
}
void StringConcat(FullDecoder* decoder, const Value& head, const Value& tail,
......
......@@ -129,6 +129,7 @@ struct WasmModule;
V(WasmStringMeasureUtf8) \
V(WasmStringMeasureWtf8) \
V(WasmStringEncodeWtf8) \
V(WasmStringEncodeWtf16) \
V(WasmStringViewWtf16GetCodeUnit) \
V(WasmStringViewWtf16Slice)
......
......@@ -350,6 +350,87 @@ function HasIsolatedSurrogate(str) {
}
})();
(function TestStringEncodeWtf16() {
let builder = new WasmModuleBuilder();
builder.addMemory(1, undefined, true /* exported */, false);
builder.addFunction("encode_wtf16", kSig_v_wi)
.exportFunc()
.addBody([
kExprLocalGet, 0,
kExprLocalGet, 1,
kGCPrefix, kExprStringEncodeWtf16, 0,
]);
builder.addFunction("encode_null", kSig_v_v)
.exportFunc()
.addBody([
kExprRefNull, kStringRefCode,
kExprI32Const, 42,
kGCPrefix, kExprStringEncodeWtf16, 0,
]);
let instance = builder.instantiate();
let memory = new Uint8Array(instance.exports.memory.buffer);
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));
}
for (let str of interestingStrings) {
let wtf16 = encodeWtf16LE(str);
let offset = memory.length - wtf16.length;
instance.exports.encode_wtf16(str, offset);
checkMemory(offset, wtf16);
clearMemory(offset, offset + wtf16.length);
}
for (let str of interestingStrings) {
let wtf16 = encodeWtf16LE(str);
let offset = 0;
instance.exports.encode_wtf16(str, offset);
checkMemory(offset, wtf16);
clearMemory(offset, offset + wtf16.length);
}
assertThrows(() => instance.exports.encode_null(),
WebAssembly.RuntimeError, "dereferencing a null pointer");
checkMemory(memory.length - 10, []);
for (let str of interestingStrings) {
let offset = 1;
assertThrows(() => instance.exports.encode_wtf16(str, offset),
WebAssembly.RuntimeError,
"operation does not support unaligned accesses");
}
for (let str of interestingStrings) {
let wtf16 = encodeWtf16LE(str);
let offset = memory.length - wtf16.length + 2;
assertThrows(() => instance.exports.encode_wtf16(str, offset),
WebAssembly.RuntimeError, "memory access out of bounds");
checkMemory(offset - 2, []);
}
})();
(function TestStringViewWtf16() {
let builder = new WasmModuleBuilder();
......
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