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

[stringrefs] Implement string.new_wtf16_array

See https://github.com/WebAssembly/stringref/issues/1.

Bug: v8:12868
Change-Id: Iea3940c48d5f47609ff7d32e366d2f41d2161372
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3734808Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Commit-Queue: Andy Wingo <wingo@igalia.com>
Cr-Commit-Position: refs/heads/main@{#81448}
parent 2699e0c0
......@@ -44,6 +44,7 @@ extern runtime WasmStringNewWtf8Array(
Context, Smi, WasmArray, Smi, Smi): String;
extern runtime WasmStringNewWtf16(
Context, WasmInstanceObject, Smi, Number, Number): String;
extern runtime WasmStringNewWtf16Array(Context, WasmArray, Smi, Smi): String;
extern runtime WasmStringConst(Context, WasmInstanceObject, Smi): String;
extern runtime WasmStringMeasureUtf8(Context, String): Number;
extern runtime WasmStringMeasureWtf8(Context, String): Number;
......@@ -827,6 +828,20 @@ builtin WasmStringNewWtf16(
LoadContextFromInstance(instance), instance, SmiFromUint32(memory),
WasmUint32ToNumber(offset), WasmUint32ToNumber(size));
}
builtin WasmStringNewWtf16Array(
array: WasmArray, start: uint32, end: uint32): String {
const context = LoadContextFromFrame();
try {
if (array.length < end) goto OffsetOutOfRange;
if (end < start) goto OffsetOutOfRange;
tail runtime::WasmStringNewWtf16Array(
context, array, SmiFromUint32(start), SmiFromUint32(end));
} label OffsetOutOfRange deferred {
const error = MessageTemplate::kWasmTrapArrayOutOfBounds;
runtime::ThrowWasmError(context, SmiConstant(error));
unreachable;
}
}
builtin WasmStringConst(index: uint32): String {
const instance = LoadInstanceFromFrame();
tail runtime::WasmStringConst(
......
......@@ -5775,6 +5775,12 @@ Node* WasmGraphBuilder::StringNewWtf16(uint32_t memory, Node* offset,
gasm_->Uint32Constant(memory), offset, size);
}
Node* WasmGraphBuilder::StringNewWtf16Array(Node* array, Node* start,
Node* end) {
return gasm_->CallBuiltin(Builtin::kWasmStringNewWtf16Array,
Operator::kNoDeopt, array, start, end);
}
Node* WasmGraphBuilder::StringConst(uint32_t index) {
return gasm_->CallBuiltin(Builtin::kWasmStringConst, Operator::kNoDeopt,
gasm_->Uint32Constant(index));
......
......@@ -540,6 +540,7 @@ class WasmGraphBuilder {
Node* StringNewWtf8Array(wasm::StringRefWtf8Policy policy, Node* array,
Node* start, Node* end);
Node* StringNewWtf16(uint32_t memory, Node* offset, Node* size);
Node* StringNewWtf16Array(Node* array, Node* start, Node* end);
Node* StringConst(uint32_t index);
Node* StringMeasureUtf8(Node* string, CheckForNull null_check,
wasm::WasmCodePosition position);
......
......@@ -963,6 +963,29 @@ RUNTIME_FUNCTION(Runtime_WasmStringNewWtf16) {
return *result;
}
RUNTIME_FUNCTION(Runtime_WasmStringNewWtf16Array) {
ClearThreadInWasmScope flag_scope(isolate);
DCHECK_EQ(3, args.length());
HandleScope scope(isolate);
Handle<WasmArray> array = args.at<WasmArray>(0);
uint32_t start = NumberToUint32(args[1]);
uint32_t end = NumberToUint32(args[2]);
DCHECK(!array->type()->element_type().is_reference());
DCHECK_EQ(sizeof(uint16_t), array->type()->element_type().value_kind_size());
const void* src = ArrayElementAddress(array, start, sizeof(uint16_t));
DCHECK_LE(start, end);
DCHECK_LE(end, array->length());
const base::uc16* codeunits = static_cast<const base::uc16*>(src);
size_t size_in_codeunits = end - start;
// TODO(12868): Override any exception with an uncatchable-by-wasm trap.
Handle<String> result;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, result,
isolate->factory()->NewStringFromTwoByte({codeunits, size_in_codeunits}));
return *result;
}
// Returns the new string if the operation succeeds. Otherwise traps.
RUNTIME_FUNCTION(Runtime_WasmStringConst) {
ClearThreadInWasmScope flag_scope(isolate);
......
......@@ -615,6 +615,7 @@ namespace internal {
F(WasmStringNewWtf8, 5, 1) \
F(WasmStringNewWtf8Array, 4, 1) \
F(WasmStringNewWtf16, 4, 1) \
F(WasmStringNewWtf16Array, 3, 1) \
F(WasmStringConst, 2, 1) \
F(WasmStringMeasureUtf8, 1, 1) \
F(WasmStringMeasureWtf8, 1, 1) \
......
......@@ -6280,7 +6280,26 @@ class LiftoffCompiler {
void StringNewWtf16Array(FullDecoder* decoder, const Value& array,
const Value& start, const Value& end,
Value* result) {
UNIMPLEMENTED();
LiftoffRegList pinned;
LiftoffRegister array_reg = pinned.set(
__ LoadToRegister(__ cache_state()->stack_state.end()[-3], pinned));
MaybeEmitNullCheck(decoder, array_reg.gp(), pinned, array.type);
LiftoffAssembler::VarState array_var(kRef, array_reg, 0);
CallRuntimeStub(WasmCode::kWasmStringNewWtf16Array,
MakeSig::Returns(kRef).Params(kRef, kI32, kI32),
{
array_var,
__ cache_state()->stack_state.end()[-2], // start
__ cache_state()->stack_state.end()[-1], // end
},
decoder->position());
__ cache_state()->stack_state.pop_back(3);
RegisterDebugSideTableEntry(decoder, DebugSideTableBuilder::kDidSpill);
LiftoffRegister result_reg(kReturnRegister0);
__ PushRegister(kRef, result_reg);
}
void StringConst(FullDecoder* decoder,
......
......@@ -1421,7 +1421,8 @@ class WasmGraphBuildingInterface {
void StringNewWtf16Array(FullDecoder* decoder, const Value& array,
const Value& start, const Value& end,
Value* result) {
UNIMPLEMENTED();
result->node =
builder_->StringNewWtf16Array(array.node, start.node, end.node);
}
void StringConst(FullDecoder* decoder,
......
......@@ -137,7 +137,8 @@ struct WasmModule;
V(WasmStringViewWtf16GetCodeUnit) \
V(WasmStringViewWtf16Encode) \
V(WasmStringViewWtf16Slice) \
V(WasmStringNewWtf8Array)
V(WasmStringNewWtf8Array) \
V(WasmStringNewWtf16Array)
// Sorted, disjoint and non-overlapping memory regions. A region is of the
// form [start, end). So there's no [start, end), [end, other_end),
......
......@@ -177,3 +177,83 @@ function makeWtf8TestDataSegment() {
"ascii".length + 1),
WebAssembly.RuntimeError, "array element access out of bounds");
})();
function encodeWtf16LE(str) {
// String iterator coalesces surrogate pairs.
let out = [];
for (let i = 0; i < str.length; i++) {
codeunit = str.charCodeAt(i);
out.push(codeunit & 0xff)
out.push(codeunit >> 8);
}
return out;
}
function makeWtf16TestDataSegment() {
let data = []
let valid = {};
for (let str of interestingStrings) {
valid[str] = { offset: data.length, length: str.length };
for (let byte of encodeWtf16LE(str)) {
data.push(byte);
}
}
return { valid, data: Uint8Array.from(data) };
};
(function TestStringNewWtf16Array() {
let builder = new WasmModuleBuilder();
let data = makeWtf16TestDataSegment();
let data_index = builder.addPassiveDataSegment(data.data);
let ascii_data_index =
builder.addPassiveDataSegment(Uint8Array.from(encodeWtf16LE("ascii")));
let i16_array = builder.addArray(kWasmI16, true);
let make_i16_array = builder.addFunction(
"make_i16_array", makeSig([], [wasmRefType(i16_array)]))
.addBody([
...wasmI32Const(0),
...wasmI32Const(data.data.length / 2),
kGCPrefix, kExprArrayNewDataStatic, i16_array, data_index
]).index;
builder.addFunction("new_wtf16", kSig_w_ii)
.exportFunc()
.addBody([
kExprCallFunction, make_i16_array,
kExprLocalGet, 0, kExprLocalGet, 1,
kGCPrefix, kExprStringNewWtf16Array
]);
builder.addFunction("bounds_check", kSig_w_ii)
.exportFunc()
.addBody([
...wasmI32Const(0),
...wasmI32Const("ascii".length),
kGCPrefix, kExprArrayNewDataStatic, i16_array, ascii_data_index,
kExprLocalGet, 0, kExprLocalGet, 1,
kGCPrefix, kExprStringNewWtf16Array
]);
let instance = builder.instantiate();
for (let [str, {offset, length}] of Object.entries(data.valid)) {
let start = offset / 2;
let end = start + length;
assertEquals(str, instance.exports.new_wtf16(start, end));
}
assertEquals("ascii", instance.exports.bounds_check(0, "ascii".length));
assertEquals("", instance.exports.bounds_check("ascii".length,
"ascii".length));
assertEquals("i", instance.exports.bounds_check("ascii".length - 1,
"ascii".length));
assertThrows(() => instance.exports.bounds_check(0, 100),
WebAssembly.RuntimeError, "array element access out of bounds");
assertThrows(() => instance.exports.bounds_check("ascii".length,
"ascii".length + 1),
WebAssembly.RuntimeError, "array element access out of bounds");
})();
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