From ea6bedaeb1d8f62973f3eab4eec7350e0ce8022d Mon Sep 17 00:00:00 2001 From: Andy Wingo Date: Fri, 10 Jun 2022 16:10:21 +0200 Subject: [PATCH] [stringrefs] Implement stringview_wtf16.get_codeunit Bug: v8:12868 Change-Id: I5fb5dccb5ff6b691348a519253de338fa91e8be1 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3695269 Reviewed-by: Jakob Kummerow Commit-Queue: Andy Wingo Cr-Commit-Position: refs/heads/main@{#81082} --- src/builtins/base.tq | 1 + src/builtins/wasm.tq | 12 ++++++++++ src/common/message-template.h | 1 + src/compiler/wasm-compiler.cc | 10 ++++++++ src/compiler/wasm-compiler.h | 3 +++ src/wasm/baseline/liftoff-compiler.cc | 19 ++++++++++++++- src/wasm/graph-builder-interface.cc | 3 ++- src/wasm/wasm-code-manager.h | 3 ++- test/mjsunit/wasm/stringrefs-exec.js | 34 +++++++++++++++++++++++---- 9 files changed, 79 insertions(+), 7 deletions(-) diff --git a/src/builtins/base.tq b/src/builtins/base.tq index 78609f1e95..ee333b6f1c 100644 --- a/src/builtins/base.tq +++ b/src/builtins/base.tq @@ -434,6 +434,7 @@ extern enum MessageTemplate { kWasmTrapIllegalCast, kWasmTrapArrayOutOfBounds, kWasmTrapArrayTooLarge, + kWasmTrapStringOffsetOutOfBounds, kWeakRefsRegisterTargetAndHoldingsMustNotBeSame, kWeakRefsRegisterTargetMustBeObject, kWeakRefsUnregisterTokenMustBeObject, diff --git a/src/builtins/wasm.tq b/src/builtins/wasm.tq index 86a7ad942c..09beeeeee3 100644 --- a/src/builtins/wasm.tq +++ b/src/builtins/wasm.tq @@ -811,4 +811,16 @@ builtin WasmStringMeasureWtf8(string: String): int32 { const result = runtime::WasmStringMeasureWtf8(LoadContextFromFrame(), string); return Signed(ChangeNumberToUint32(result)); } +transitioning builtin WasmStringViewWtf16GetCodeUnit( + string: String, offset: uint32): uint32 { + try { + if (Unsigned(string.length) <= offset) goto OffsetOutOfRange; + const code: char16 = StringCharCodeAt(string, Convert(offset)); + return Convert(code); + } label OffsetOutOfRange deferred { + const error = MessageTemplate::kWasmTrapStringOffsetOutOfBounds; + runtime::ThrowWasmError(LoadContextFromFrame(), SmiConstant(error)); + unreachable; + } +} } diff --git a/src/common/message-template.h b/src/common/message-template.h index 0a5008a653..998818f861 100644 --- a/src/common/message-template.h +++ b/src/common/message-template.h @@ -645,6 +645,7 @@ namespace internal { T(WasmTrapArrayOutOfBounds, "array element access out of bounds") \ T(WasmTrapArrayTooLarge, "requested new array is too large") \ T(WasmTrapStringInvalidWtf8, "invalid WTF-8 string") \ + T(WasmTrapStringOffsetOutOfBounds, "string offset out of bounds") \ T(WasmExceptionError, "wasm exception") \ /* Asm.js validation related */ \ T(AsmJsInvalid, "Invalid asm.js: %") \ diff --git a/src/compiler/wasm-compiler.cc b/src/compiler/wasm-compiler.cc index b6e43f24c4..86a79acbb5 100644 --- a/src/compiler/wasm-compiler.cc +++ b/src/compiler/wasm-compiler.cc @@ -5786,6 +5786,16 @@ Node* WasmGraphBuilder::StringMeasureWtf16(Node* string, wasm::ObjectAccess::ToTagged(String::kLengthOffset)); } +Node* WasmGraphBuilder::StringViewWtf16GetCodeUnit( + Node* string, CheckForNull null_check, Node* offset, + wasm::WasmCodePosition position) { + if (null_check == kWithNullCheck) { + string = AssertNotNull(string, position); + } + return gasm_->CallBuiltin(Builtin::kWasmStringViewWtf16GetCodeUnit, + Operator::kNoDeopt, string, offset); +} + // 1 bit V8 Smi tag, 31 bits V8 Smi shift, 1 bit i31ref high-bit truncation. constexpr int kI31To32BitSmiShift = 33; diff --git a/src/compiler/wasm-compiler.h b/src/compiler/wasm-compiler.h index c9ee4dbd9f..a670c0ce64 100644 --- a/src/compiler/wasm-compiler.h +++ b/src/compiler/wasm-compiler.h @@ -547,6 +547,9 @@ class WasmGraphBuilder { wasm::WasmCodePosition position); Node* StringMeasureWtf16(Node* string, CheckForNull null_check, wasm::WasmCodePosition position); + Node* StringViewWtf16GetCodeUnit(Node* string, CheckForNull null_check, + Node* offset, + wasm::WasmCodePosition position); Node* IsNull(Node* object); Node* TypeGuard(Node* value, wasm::ValueType type); diff --git a/src/wasm/baseline/liftoff-compiler.cc b/src/wasm/baseline/liftoff-compiler.cc index 4f64a87df4..afbe4c762c 100644 --- a/src/wasm/baseline/liftoff-compiler.cc +++ b/src/wasm/baseline/liftoff-compiler.cc @@ -6178,7 +6178,24 @@ class LiftoffCompiler { void StringViewWtf16GetCodeUnit(FullDecoder* decoder, const Value& view, const Value& pos, Value* result) { - UNIMPLEMENTED(); + LiftoffRegList pinned; + LiftoffRegister pos_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 pos_var(kI32, pos_reg, 0); + + CallRuntimeStub(WasmCode::kWasmStringViewWtf16GetCodeUnit, + MakeSig::Returns(kI32).Params(kRef, kI32), + { + view_var, + pos_var, + }, + decoder->position()); + RegisterDebugSideTableEntry(decoder, DebugSideTableBuilder::kDidSpill); + + LiftoffRegister result_reg(kReturnRegister0); + __ PushRegister(kI32, result_reg); } void StringViewWtf16Encode(FullDecoder* decoder, diff --git a/src/wasm/graph-builder-interface.cc b/src/wasm/graph-builder-interface.cc index aaf0583088..558870b845 100644 --- a/src/wasm/graph-builder-interface.cc +++ b/src/wasm/graph-builder-interface.cc @@ -1479,7 +1479,8 @@ class WasmGraphBuildingInterface { void StringViewWtf16GetCodeUnit(FullDecoder* decoder, const Value& view, const Value& pos, Value* result) { - UNIMPLEMENTED(); + result->node = builder_->StringViewWtf16GetCodeUnit( + view.node, NullCheckFor(view.type), pos.node, decoder->position()); } void StringViewWtf16Encode(FullDecoder* decoder, diff --git a/src/wasm/wasm-code-manager.h b/src/wasm/wasm-code-manager.h index 43815b92cf..b4a7b2496f 100644 --- a/src/wasm/wasm-code-manager.h +++ b/src/wasm/wasm-code-manager.h @@ -127,7 +127,8 @@ struct WasmModule; V(WasmStringNewWtf16) \ V(WasmStringConst) \ V(WasmStringMeasureUtf8) \ - V(WasmStringMeasureWtf8) + V(WasmStringMeasureWtf8) \ + V(WasmStringViewWtf16GetCodeUnit) // Sorted, disjoint and non-overlapping memory regions. A region is of the // form [start, end). So there's no [start, end), [end, other_end), diff --git a/test/mjsunit/wasm/stringrefs-exec.js b/test/mjsunit/wasm/stringrefs-exec.js index b5ce00a91a..6e94bd4ad7 100644 --- a/test/mjsunit/wasm/stringrefs-exec.js +++ b/test/mjsunit/wasm/stringrefs-exec.js @@ -9,6 +9,7 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js"); 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]); function encodeWtf8(str) { // String iterator coalesces surrogate pairs. @@ -245,7 +246,7 @@ function makeWtf16TestDataSegment() { (function TestStringViewWtf16() { let builder = new WasmModuleBuilder(); - builder.addFunction("string_measure_wtf16", kSig_i_w) + builder.addFunction("string_view_wtf16_length", kSig_i_w) .exportFunc() .addBody([ kExprLocalGet, 0, @@ -253,18 +254,43 @@ function makeWtf16TestDataSegment() { kGCPrefix, kExprStringViewWtf16Length ]); - builder.addFunction("string_measure_wtf16_null", kSig_i_v) + builder.addFunction("string_view_wtf16_length_null", kSig_i_v) .exportFunc() .addBody([ kExprRefNull, kStringViewWtf16Code, kGCPrefix, kExprStringViewWtf16Length ]); + builder.addFunction("string_view_wtf16_get_codeunit", kSig_i_wi) + .exportFunc() + .addBody([ + kExprLocalGet, 0, + kGCPrefix, kExprStringAsWtf16, + kExprLocalGet, 1, + kGCPrefix, kExprStringViewWtf16GetCodeunit + ]); + + builder.addFunction("string_view_wtf16_get_codeunit_null", kSig_i_v) + .exportFunc() + .addBody([ + kExprRefNull, kStringViewWtf16Code, + kExprI32Const, 0, + kGCPrefix, kExprStringViewWtf16GetCodeunit + ]); + let instance = builder.instantiate(); for (let str of interestingStrings) { - assertEquals(str.length, instance.exports.string_measure_wtf16(str)); + assertEquals(str.length, instance.exports.string_view_wtf16_length(str)); + for (let i = 0; i < str.length; i++) { + assertEquals(str.charCodeAt(i), + instance.exports.string_view_wtf16_get_codeunit(str, i)); + } } - assertThrows(() => instance.exports.string_measure_wtf16_null(), + 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"); })();