diff --git a/src/compiler/wasm-compiler.cc b/src/compiler/wasm-compiler.cc index 3cb9600007..f549570573 100644 --- a/src/compiler/wasm-compiler.cc +++ b/src/compiler/wasm-compiler.cc @@ -5725,36 +5725,49 @@ void WasmGraphBuilder::ArrayCopy(Node* dst_array, Node* dst_index, gasm_->Bind(&skip); } +// General rules for operator properties for builtin calls: +// - Use kEliminatable if it can neither throw a catchable exception nor trap. +// - Use kNoDeopt | kNoThrow if it can trap (because in that case, eliminating +// it would avoid the trap and thereby observably change the code's behavior +// compared to its unoptimized version). +// - If you don't use kNoThrow (nor kEliminatable which implies it), then you +// must also set up control nodes for the throwing case, e.g. by using +// WasmGraphBuildingInterface::CheckForException(). + Node* WasmGraphBuilder::StringNewWtf8(uint32_t memory, unibrow::Utf8Variant variant, Node* offset, Node* size) { - return gasm_->CallBuiltin(Builtin::kWasmStringNewWtf8, Operator::kNoDeopt, - offset, size, gasm_->SmiConstant(memory), + return gasm_->CallBuiltin(Builtin::kWasmStringNewWtf8, + Operator::kNoDeopt | Operator::kNoThrow, offset, + size, gasm_->SmiConstant(memory), gasm_->SmiConstant(static_cast(variant))); } Node* WasmGraphBuilder::StringNewWtf8Array(unibrow::Utf8Variant variant, Node* array, Node* start, Node* end) { - return gasm_->CallBuiltin(Builtin::kWasmStringNewWtf8Array, - Operator::kNoDeopt, start, end, array, - gasm_->SmiConstant(static_cast(variant))); + return gasm_->CallBuiltin( + Builtin::kWasmStringNewWtf8Array, Operator::kNoDeopt | Operator::kNoThrow, + start, end, array, gasm_->SmiConstant(static_cast(variant))); } Node* WasmGraphBuilder::StringNewWtf16(uint32_t memory, Node* offset, Node* size) { - return gasm_->CallBuiltin(Builtin::kWasmStringNewWtf16, Operator::kNoDeopt, + return gasm_->CallBuiltin(Builtin::kWasmStringNewWtf16, + Operator::kNoDeopt | Operator::kNoThrow, gasm_->Uint32Constant(memory), offset, size); } Node* WasmGraphBuilder::StringNewWtf16Array(Node* array, Node* start, Node* end) { return gasm_->CallBuiltin(Builtin::kWasmStringNewWtf16Array, - Operator::kNoDeopt, array, start, end); + Operator::kNoDeopt | Operator::kNoThrow, array, + start, end); } Node* WasmGraphBuilder::StringConst(uint32_t index) { - return gasm_->CallBuiltin(Builtin::kWasmStringConst, Operator::kNoDeopt, + return gasm_->CallBuiltin(Builtin::kWasmStringConst, + Operator::kNoDeopt | Operator::kNoThrow, gasm_->Uint32Constant(index)); } @@ -5763,8 +5776,8 @@ Node* WasmGraphBuilder::StringMeasureUtf8(Node* string, CheckForNull null_check, if (null_check == kWithNullCheck) { string = AssertNotNull(string, position); } - return gasm_->CallBuiltin(Builtin::kWasmStringMeasureUtf8, Operator::kNoDeopt, - string); + return gasm_->CallBuiltin(Builtin::kWasmStringMeasureUtf8, + Operator::kEliminatable, string); } Node* WasmGraphBuilder::StringMeasureWtf8(Node* string, CheckForNull null_check, @@ -5772,8 +5785,8 @@ Node* WasmGraphBuilder::StringMeasureWtf8(Node* string, CheckForNull null_check, if (null_check == kWithNullCheck) { string = AssertNotNull(string, position); } - return gasm_->CallBuiltin(Builtin::kWasmStringMeasureWtf8, Operator::kNoDeopt, - string); + return gasm_->CallBuiltin(Builtin::kWasmStringMeasureWtf8, + Operator::kEliminatable, string); } Node* WasmGraphBuilder::StringMeasureWtf16(Node* string, @@ -5795,8 +5808,9 @@ Node* WasmGraphBuilder::StringEncodeWtf8(uint32_t memory, if (null_check == kWithNullCheck) { string = AssertNotNull(string, position); } - return gasm_->CallBuiltin(Builtin::kWasmStringEncodeWtf8, Operator::kNoDeopt, - string, offset, gasm_->SmiConstant(memory), + return gasm_->CallBuiltin(Builtin::kWasmStringEncodeWtf8, + Operator::kNoDeopt | Operator::kNoThrow, string, + offset, gasm_->SmiConstant(memory), gasm_->SmiConstant(static_cast(variant))); } @@ -5811,7 +5825,8 @@ Node* WasmGraphBuilder::StringEncodeWtf8Array( array = AssertNotNull(array, position); } return gasm_->CallBuiltin(Builtin::kWasmStringEncodeWtf8Array, - Operator::kNoDeopt, string, array, start, + Operator::kNoDeopt | Operator::kNoThrow, string, + array, start, gasm_->SmiConstant(static_cast(variant))); } @@ -5821,8 +5836,9 @@ Node* WasmGraphBuilder::StringEncodeWtf16(uint32_t memory, Node* string, if (null_check == kWithNullCheck) { string = AssertNotNull(string, position); } - return gasm_->CallBuiltin(Builtin::kWasmStringEncodeWtf16, Operator::kNoDeopt, - string, offset, gasm_->SmiConstant(memory)); + return gasm_->CallBuiltin(Builtin::kWasmStringEncodeWtf16, + Operator::kNoDeopt | Operator::kNoThrow, string, + offset, gasm_->SmiConstant(memory)); } Node* WasmGraphBuilder::StringEncodeWtf16Array( @@ -5836,7 +5852,8 @@ Node* WasmGraphBuilder::StringEncodeWtf16Array( array = AssertNotNull(array, position); } return gasm_->CallBuiltin(Builtin::kWasmStringEncodeWtf16Array, - Operator::kNoDeopt, string, array, start); + Operator::kNoDeopt | Operator::kNoThrow, string, + array, start); } Node* WasmGraphBuilder::StringConcat(Node* head, CheckForNull head_null_check, @@ -5845,7 +5862,8 @@ Node* WasmGraphBuilder::StringConcat(Node* head, CheckForNull head_null_check, if (head_null_check == kWithNullCheck) head = AssertNotNull(head, position); if (tail_null_check == kWithNullCheck) tail = AssertNotNull(tail, position); return gasm_->CallBuiltin( - Builtin::kStringAdd_CheckNone, Operator::kEliminatable, head, tail, + Builtin::kStringAdd_CheckNone, Operator::kNoDeopt | Operator::kNoThrow, + head, tail, LOAD_INSTANCE_FIELD(NativeContext, MachineType::TaggedPointer())); } @@ -5862,7 +5880,7 @@ Node* WasmGraphBuilder::StringEqual(Node* a, CheckForNull a_null_check, Node* b, gasm_->GotoIf(gasm_->IsNull(b), &done, Int32Constant(0)); } gasm_->Goto(&done, gasm_->CallBuiltin(Builtin::kWasmStringEqual, - Operator::kNoDeopt, a, b)); + Operator::kEliminatable, a, b)); gasm_->Bind(&done); return done.PhiAt(0); } @@ -5872,14 +5890,14 @@ Node* WasmGraphBuilder::StringIsUSVSequence(Node* str, CheckForNull null_check, if (null_check == kWithNullCheck) str = AssertNotNull(str, position); return gasm_->CallBuiltin(Builtin::kWasmStringIsUSVSequence, - Operator::kNoDeopt, str); + Operator::kEliminatable, str); } Node* WasmGraphBuilder::StringAsWtf8(Node* str, CheckForNull null_check, wasm::WasmCodePosition position) { if (null_check == kWithNullCheck) str = AssertNotNull(str, position); - return gasm_->CallBuiltin(Builtin::kWasmStringAsWtf8, Operator::kNoDeopt, + return gasm_->CallBuiltin(Builtin::kWasmStringAsWtf8, Operator::kEliminatable, str); } @@ -5890,7 +5908,7 @@ Node* WasmGraphBuilder::StringViewWtf8Advance(Node* view, if (null_check == kWithNullCheck) view = AssertNotNull(view, position); return gasm_->CallBuiltin(Builtin::kWasmStringViewWtf8Advance, - Operator::kNoDeopt, view, pos, bytes); + Operator::kEliminatable, view, pos, bytes); } void WasmGraphBuilder::StringViewWtf8Encode( @@ -5901,8 +5919,9 @@ void WasmGraphBuilder::StringViewWtf8Encode( view = AssertNotNull(view, position); } Node* pair = - gasm_->CallBuiltin(Builtin::kWasmStringViewWtf8Encode, Operator::kNoDeopt, - addr, pos, bytes, view, gasm_->SmiConstant(memory), + gasm_->CallBuiltin(Builtin::kWasmStringViewWtf8Encode, + Operator::kNoDeopt | Operator::kNoThrow, addr, pos, + bytes, view, gasm_->SmiConstant(memory), gasm_->SmiConstant(static_cast(variant))); *next_pos = gasm_->Projection(0, pair); *bytes_written = gasm_->Projection(1, pair); @@ -5915,7 +5934,7 @@ Node* WasmGraphBuilder::StringViewWtf8Slice(Node* view, CheckForNull null_check, view = AssertNotNull(view, position); } return gasm_->CallBuiltin(Builtin::kWasmStringViewWtf8Slice, - Operator::kNoDeopt, view, pos, bytes); + Operator::kEliminatable, view, pos, bytes); } Node* WasmGraphBuilder::StringViewWtf16GetCodeUnit( @@ -5925,7 +5944,8 @@ Node* WasmGraphBuilder::StringViewWtf16GetCodeUnit( string = AssertNotNull(string, position); } return gasm_->CallBuiltin(Builtin::kWasmStringViewWtf16GetCodeUnit, - Operator::kNoDeopt, string, offset); + Operator::kNoDeopt | Operator::kNoThrow, string, + offset); } Node* WasmGraphBuilder::StringViewWtf16Encode(uint32_t memory, Node* string, @@ -5937,8 +5957,9 @@ Node* WasmGraphBuilder::StringViewWtf16Encode(uint32_t memory, Node* string, string = AssertNotNull(string, position); } return gasm_->CallBuiltin(Builtin::kWasmStringViewWtf16Encode, - Operator::kNoDeopt, offset, start, codeunits, - string, gasm_->SmiConstant(memory)); + Operator::kNoDeopt | Operator::kNoThrow, offset, + start, codeunits, string, + gasm_->SmiConstant(memory)); } Node* WasmGraphBuilder::StringViewWtf16Slice(Node* string, @@ -5949,14 +5970,14 @@ Node* WasmGraphBuilder::StringViewWtf16Slice(Node* string, string = AssertNotNull(string, position); } return gasm_->CallBuiltin(Builtin::kWasmStringViewWtf16Slice, - Operator::kNoDeopt, string, start, end); + Operator::kEliminatable, string, start, end); } Node* WasmGraphBuilder::StringAsIter(Node* str, CheckForNull null_check, wasm::WasmCodePosition position) { if (null_check == kWithNullCheck) str = AssertNotNull(str, position); - return gasm_->CallBuiltin(Builtin::kWasmStringAsIter, Operator::kNoDeopt, + return gasm_->CallBuiltin(Builtin::kWasmStringAsIter, Operator::kEliminatable, str); } @@ -5965,7 +5986,7 @@ Node* WasmGraphBuilder::StringViewIterNext(Node* view, CheckForNull null_check, if (null_check == kWithNullCheck) view = AssertNotNull(view, position); return gasm_->CallBuiltin(Builtin::kWasmStringViewIterNext, - Operator::kNoDeopt, view); + Operator::kEliminatable, view); } Node* WasmGraphBuilder::StringViewIterAdvance(Node* view, @@ -5975,7 +5996,7 @@ Node* WasmGraphBuilder::StringViewIterAdvance(Node* view, if (null_check == kWithNullCheck) view = AssertNotNull(view, position); return gasm_->CallBuiltin(Builtin::kWasmStringViewIterAdvance, - Operator::kNoDeopt, view, codepoints); + Operator::kEliminatable, view, codepoints); } Node* WasmGraphBuilder::StringViewIterRewind(Node* view, @@ -5985,7 +6006,7 @@ Node* WasmGraphBuilder::StringViewIterRewind(Node* view, if (null_check == kWithNullCheck) view = AssertNotNull(view, position); return gasm_->CallBuiltin(Builtin::kWasmStringViewIterRewind, - Operator::kNoDeopt, view, codepoints); + Operator::kEliminatable, view, codepoints); } Node* WasmGraphBuilder::StringViewIterSlice(Node* view, CheckForNull null_check, @@ -5994,7 +6015,7 @@ Node* WasmGraphBuilder::StringViewIterSlice(Node* view, CheckForNull null_check, if (null_check == kWithNullCheck) view = AssertNotNull(view, position); return gasm_->CallBuiltin(Builtin::kWasmStringViewIterSlice, - Operator::kNoDeopt, view, codepoints); + Operator::kEliminatable, view, codepoints); } // 1 bit V8 Smi tag, 31 bits V8 Smi shift, 1 bit i31ref high-bit truncation. diff --git a/src/heap/factory.cc b/src/heap/factory.cc index 3e28d62ff2..d63ad09534 100644 --- a/src/heap/factory.cc +++ b/src/heap/factory.cc @@ -775,6 +775,11 @@ MaybeHandle NewStringFromUtf8Variant(Isolate* isolate, MaybeHandle Factory::NewStringFromUtf8( const base::Vector& string, unibrow::Utf8Variant utf8_variant, AllocationType allocation) { + if (string.size() > kMaxInt) { + // The Utf8Decode can't handle longer inputs, and we couldn't create + // strings from them anyway. + THROW_NEW_ERROR(isolate(), NewInvalidStringLengthError(), String); + } auto peek_bytes = [&]() -> base::Vector { return string; }; return NewStringFromUtf8Variant(isolate(), peek_bytes, utf8_variant, allocation); @@ -793,6 +798,8 @@ MaybeHandle Factory::NewStringFromUtf8( DCHECK_EQ(sizeof(uint8_t), array->type()->element_type().value_kind_size()); DCHECK_LE(start, end); DCHECK_LE(end, array->length()); + // {end - start} can never be more than what the Utf8Decoder can handle. + static_assert(WasmArray::MaxLength(sizeof(uint8_t)) <= kMaxInt); auto peek_bytes = [&]() -> base::Vector { const uint8_t* contents = reinterpret_cast(array->ElementAddress(0)); @@ -807,6 +814,8 @@ MaybeHandle Factory::NewStringFromUtf8( unibrow::Utf8Variant utf8_variant, AllocationType allocation) { DCHECK_LE(start, end); DCHECK_LE(end, array->length()); + // {end - start} can never be more than what the Utf8Decoder can handle. + static_assert(ByteArray::kMaxLength <= kMaxInt); auto peek_bytes = [&]() -> base::Vector { const uint8_t* contents = reinterpret_cast(array->GetDataStartAddress()); @@ -839,6 +848,8 @@ MaybeHandle Factory::NewStringFromUtf16(Handle array, DCHECK_EQ(sizeof(uint16_t), array->type()->element_type().value_kind_size()); DCHECK_LE(start, end); DCHECK_LE(end, array->length()); + // {end - start} can never be more than what the Utf8Decoder can handle. + static_assert(WasmArray::MaxLength(sizeof(uint16_t)) <= kMaxInt); auto peek_bytes = [&]() -> base::Vector { const uint16_t* contents = reinterpret_cast(array->ElementAddress(0)); diff --git a/src/runtime/runtime-internal.cc b/src/runtime/runtime-internal.cc index dad9a2f200..aafb9fe18f 100644 --- a/src/runtime/runtime-internal.cc +++ b/src/runtime/runtime-internal.cc @@ -19,8 +19,8 @@ #include "src/utils/ostreams.h" #if V8_ENABLE_WEBASSEMBLY -// TODO(jkummerow): Drop this when the "SaveAndClearThreadInWasmFlag" -// short-term mitigation is no longer needed. +// TODO(chromium:1236668): Drop this when the "SaveAndClearThreadInWasmFlag" +// approach is no longer needed. #include "src/trap-handler/trap-handler.h" #endif // V8_ENABLE_WEBASSEMBLY @@ -418,7 +418,7 @@ RUNTIME_FUNCTION(Runtime_BytecodeBudgetInterruptWithStackCheck_Maglev) { namespace { #if V8_ENABLE_WEBASSEMBLY -class SaveAndClearThreadInWasmFlag { +class V8_NODISCARD SaveAndClearThreadInWasmFlag { public: SaveAndClearThreadInWasmFlag() { if (trap_handler::IsTrapHandlerEnabled()) { @@ -460,10 +460,10 @@ RUNTIME_FUNCTION(Runtime_AllocateInYoungGeneration) { } #if V8_ENABLE_WEBASSEMBLY - // Short-term mitigation for crbug.com/1236668. When this is called from - // WasmGC code, clear the "thread in wasm" flag, which is important in case - // any GC needs to happen. - // TODO(jkummerow): Find a better fix, likely by replacing the global flag. + // When this is called from WasmGC code, clear the "thread in wasm" flag, + // which is important in case any GC needs to happen. + // TODO(chromium:1236668): Find a better fix, likely by replacing the global + // flag. SaveAndClearThreadInWasmFlag clear_wasm_flag; #endif // V8_ENABLE_WEBASSEMBLY diff --git a/src/runtime/runtime-strings.cc b/src/runtime/runtime-strings.cc index 621788f98c..1f2da1cd19 100644 --- a/src/runtime/runtime-strings.cc +++ b/src/runtime/runtime-strings.cc @@ -11,9 +11,46 @@ #include "src/objects/smi.h" #include "src/strings/string-builder-inl.h" +#if V8_ENABLE_WEBASSEMBLY +// TODO(chromium:1236668): Drop this when the "SaveAndClearThreadInWasmFlag" +// approach is no longer needed. +#include "src/trap-handler/trap-handler.h" +#endif // V8_ENABLE_WEBASSEMBLY + namespace v8 { namespace internal { +namespace { + +#if V8_ENABLE_WEBASSEMBLY +class V8_NODISCARD SaveAndClearThreadInWasmFlag { + public: + explicit SaveAndClearThreadInWasmFlag(Isolate* isolate) : isolate_(isolate) { + if (trap_handler::IsTrapHandlerEnabled()) { + if (trap_handler::IsThreadInWasm()) { + thread_was_in_wasm_ = true; + trap_handler::ClearThreadInWasm(); + } + } + } + ~SaveAndClearThreadInWasmFlag() { + if (thread_was_in_wasm_ && !isolate_->has_pending_exception()) { + trap_handler::SetThreadInWasm(); + } + } + + private: + bool thread_was_in_wasm_{false}; + Isolate* isolate_; +}; +#define CLEAR_THREAD_IN_WASM_SCOPE \ + SaveAndClearThreadInWasmFlag non_wasm_scope(isolate) +#else +#define CLEAR_THREAD_IN_WASM_SCOPE (void)0 +#endif // V8_ENABLE_WEBASSEMBLY + +} // namespace + RUNTIME_FUNCTION(Runtime_GetSubstitution) { HandleScope scope(isolate); DCHECK_EQ(5, args.length()); @@ -154,6 +191,8 @@ RUNTIME_FUNCTION(Runtime_StringSubstring) { } RUNTIME_FUNCTION(Runtime_StringAdd) { + // This is used by Wasm stringrefs. + CLEAR_THREAD_IN_WASM_SCOPE; HandleScope scope(isolate); DCHECK_EQ(2, args.length()); Handle str1 = args.at(0); diff --git a/src/runtime/runtime-wasm.cc b/src/runtime/runtime-wasm.cc index 034234f8be..e0a9c4e695 100644 --- a/src/runtime/runtime-wasm.cc +++ b/src/runtime/runtime-wasm.cc @@ -870,6 +870,27 @@ RUNTIME_FUNCTION(Runtime_WasmCreateResumePromise) { return *result; } +#define RETURN_RESULT_OR_TRAP(call) \ + do { \ + Handle result; \ + if (!(call).ToHandle(&result)) { \ + DCHECK(isolate->has_pending_exception()); \ + /* Mark any exception as uncatchable by Wasm. */ \ + Handle exception(JSObject::cast(isolate->pending_exception()), \ + isolate); \ + Handle uncatchable = \ + isolate->factory()->wasm_uncatchable_symbol(); \ + LookupIterator it(isolate, exception, uncatchable, LookupIterator::OWN); \ + if (!JSReceiver::HasProperty(&it).FromJust()) { \ + JSObject::AddProperty(isolate, exception, uncatchable, \ + isolate->factory()->true_value(), NONE); \ + } \ + return ReadOnlyRoots(isolate).exception(); \ + } \ + DCHECK(!isolate->has_pending_exception()); \ + return *result; \ + } while (false) + // Returns the new string if the operation succeeds. Otherwise throws an // exception and returns an empty result. RUNTIME_FUNCTION(Runtime_WasmStringNewWtf8) { @@ -896,8 +917,8 @@ RUNTIME_FUNCTION(Runtime_WasmStringNewWtf8) { const base::Vector bytes{instance.memory_start() + offset, size}; - RETURN_RESULT_OR_FAILURE( - isolate, isolate->factory()->NewStringFromUtf8(bytes, utf8_variant)); + RETURN_RESULT_OR_TRAP( + isolate->factory()->NewStringFromUtf8(bytes, utf8_variant)); } RUNTIME_FUNCTION(Runtime_WasmStringNewWtf8Array) { @@ -913,8 +934,8 @@ RUNTIME_FUNCTION(Runtime_WasmStringNewWtf8Array) { static_cast(unibrow::Utf8Variant::kLastUtf8Variant)); auto utf8_variant = static_cast(utf8_variant_value); - RETURN_RESULT_OR_FAILURE(isolate, isolate->factory()->NewStringFromUtf8( - array, start, end, utf8_variant)); + RETURN_RESULT_OR_TRAP( + isolate->factory()->NewStringFromUtf8(array, start, end, utf8_variant)); } RUNTIME_FUNCTION(Runtime_WasmStringNewWtf16) { @@ -940,10 +961,8 @@ RUNTIME_FUNCTION(Runtime_WasmStringNewWtf16) { const byte* bytes = instance.memory_start() + offset; const base::uc16* codeunits = reinterpret_cast(bytes); - // TODO(12868): Override any exception with an uncatchable-by-wasm trap. - RETURN_RESULT_OR_FAILURE(isolate, - isolate->factory()->NewStringFromTwoByteLittleEndian( - {codeunits, size_in_codeunits})); + RETURN_RESULT_OR_TRAP(isolate->factory()->NewStringFromTwoByteLittleEndian( + {codeunits, size_in_codeunits})); } RUNTIME_FUNCTION(Runtime_WasmStringNewWtf16Array) { @@ -954,9 +973,8 @@ RUNTIME_FUNCTION(Runtime_WasmStringNewWtf16Array) { uint32_t start = NumberToUint32(args[1]); uint32_t end = NumberToUint32(args[2]); - // TODO(12868): Override any exception with an uncatchable-by-wasm trap. - RETURN_RESULT_OR_FAILURE( - isolate, isolate->factory()->NewStringFromUtf16(array, start, end)); + RETURN_RESULT_OR_TRAP( + isolate->factory()->NewStringFromUtf16(array, start, end)); } // Returns the new string if the operation succeeds. Otherwise traps. @@ -1291,9 +1309,12 @@ RUNTIME_FUNCTION(Runtime_WasmStringViewWtf8Slice) { DCHECK_LT(start, end); DCHECK(base::IsInBounds(start, end - start, array->length())); - RETURN_RESULT_OR_FAILURE(isolate, - isolate->factory()->NewStringFromUtf8( - array, start, end, unibrow::Utf8Variant::kWtf8)); + // This can't throw because the result can't be too long if the input wasn't, + // and encoding failures are ruled out too because {start}/{end} are aligned. + return *isolate->factory() + ->NewStringFromUtf8(array, start, end, + unibrow::Utf8Variant::kWtf8) + .ToHandleChecked(); } } // namespace internal diff --git a/src/wasm/wasm-objects.h b/src/wasm/wasm-objects.h index ee7e93020b..22e208562c 100644 --- a/src/wasm/wasm-objects.h +++ b/src/wasm/wasm-objects.h @@ -1000,7 +1000,7 @@ class WasmArray : public TorqueGeneratedWasmArray { inline uint32_t element_offset(uint32_t index); inline Address ElementAddress(uint32_t index); - static int MaxLength(uint32_t element_size_bytes) { + static constexpr int MaxLength(uint32_t element_size_bytes) { // The total object size must fit into a Smi, for filler objects. To make // the behavior of Wasm programs independent from the Smi configuration, // we hard-code the smaller of the two supported ranges. diff --git a/test/mjsunit/wasm/stringrefs-regressions.js b/test/mjsunit/wasm/stringrefs-regressions.js new file mode 100644 index 0000000000..c12111f29d --- /dev/null +++ b/test/mjsunit/wasm/stringrefs-regressions.js @@ -0,0 +1,98 @@ +// Copyright 2022 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --experimental-wasm-stringref --allow-natives-syntax +// We just want speculative inlining, but the "stress" variant doesn't like +// that flag for some reason, so use the GC flag which implies it. +// Flags: --experimental-wasm-gc + +d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js"); + +let kSig_w_v = makeSig([], [kWasmStringRef]); +let kSig_w_i = makeSig([kWasmI32], [kWasmStringRef]); +let kSig_v_w = makeSig([kWasmStringRef], []); + +(function () { + let huge_builder = new WasmModuleBuilder(); + huge_builder.addMemory(65001, undefined, false, false); + + huge_builder.addFunction("huge", kSig_w_v).exportFunc().addBody([ + kExprI32Const, 0, // address + ...wasmI32Const(65000 * 65536), // bytes + ...GCInstr(kExprStringNewUtf8), 0 // memory index + ]); + + let callee = huge_builder.addFunction("callee", kSig_w_i).addBody([ + kExprI32Const, 0, // address + kExprLocalGet, 0, // bytes + ...GCInstr(kExprStringNewUtf8), 0 // memory index + ]); + + let caller = huge_builder.addFunction("caller", kSig_i_i).exportFunc() + .addBody([ + kExprTry, kWasmI32, + kExprLocalGet, 0, + kExprCallFunction, callee.index, + kExprDrop, + kExprI32Const, 1, + kExprCatchAll, + kExprI32Const, 0, + kExprEnd + ]); + + let instance; + try { + instance = huge_builder.instantiate(); + // On 64-bit platforms, expect to see this message. + console.log("Instantiation successful, proceeding."); + } catch (e) { + // 32-bit builds don't have enough virtual memory, that's OK. + assertInstanceof(e, RangeError); + assertMatches(/Cannot allocate Wasm memory for new instance/, e.message, + 'Error message'); + return; + } + + // Bug 1: The Utf8Decoder can't handle more than kMaxInt bytes as input. + assertThrows(() => instance.exports.huge(), RangeError); + + // Bug 2: Exceptions created by the JS-focused strings infrastructure must + // be marked as uncatchable by Wasm. + let f1 = instance.exports.caller; + assertThrows(() => f1(2147483647), RangeError); + + // Bug 3: Builtin calls that have neither a kNoThrow annotation nor exception- + // handling support make the Wasm inliner sad. + for (let i = 0; i < 20; i++) f1(10); + %WasmTierUpFunction(instance, caller.index); + f1(10); +})(); + +let builder = new WasmModuleBuilder(); + +let concat_body = []; +// This doubles the string 26 times, i.e. multiplies its length with a factor +// of ~65 million. +for (let i = 0; i < 26; i++) { + concat_body.push(...[ + kExprLocalGet, 0, kExprLocalGet, 0, + ...GCInstr(kExprStringConcat), + kExprLocalSet, 0 + ]); +} + +let concat = + builder.addFunction('concat', kSig_v_w).exportFunc().addBody(concat_body); + +let instance = builder.instantiate(); + +// Bug 4: Throwing in StringAdd must clear the "thread in wasm" bit. +let f2 = instance.exports.concat; +assertThrows(() => f2("1234567890")); // 650M characters is too much. + +// Bug 5: Operations that can trap must not be marked as kEliminatable, +// otherwise the trap may be eliminated. +for (let i = 0; i < 3; i++) f2("a"); // 65M characters is okay. +%WasmTierUpFunction(instance, concat.index); +assertThrows(() => f2("1234567890")); // Optimized code still traps.