diff --git a/deps/v8/include/v8-version.h b/deps/v8/include/v8-version.h index 1f55ca3c03cec0..eb1bdf134b8fbe 100644 --- a/deps/v8/include/v8-version.h +++ b/deps/v8/include/v8-version.h @@ -11,7 +11,7 @@ #define V8_MAJOR_VERSION 14 #define V8_MINOR_VERSION 2 #define V8_BUILD_NUMBER 231 -#define V8_PATCH_LEVEL 14 +#define V8_PATCH_LEVEL 16 // Use 1 for candidates and 0 otherwise. // (Boolean macro values are not supported by all preprocessors.) diff --git a/deps/v8/src/codegen/loong64/macro-assembler-loong64.cc b/deps/v8/src/codegen/loong64/macro-assembler-loong64.cc index bf3619d512d10a..7b039de8bc32dd 100644 --- a/deps/v8/src/codegen/loong64/macro-assembler-loong64.cc +++ b/deps/v8/src/codegen/loong64/macro-assembler-loong64.cc @@ -194,18 +194,34 @@ void MacroAssembler::PreCheckSkippedWriteBarrier(Register object, bind(¬_ok); } +void MacroAssembler::MaybeJumpIfReadOnlyOrSmallSmi(Register value, + Label* dest) { +#if V8_STATIC_ROOTS_BOOL + // Quick check for Read-only and small Smi values. + static_assert(StaticReadOnlyRoot::kLastAllocatedRoot < kRegularPageSize); + JumpIfUnsignedLessThan(value, kRegularPageSize, dest); +#endif // V8_STATIC_ROOTS_BOOL +} + // Clobbers object, dst, value, and ra, if (ra_status == kRAHasBeenSaved) // The register 'object' contains a heap object pointer. The heap object // tag is shifted away. void MacroAssembler::RecordWriteField(Register object, int offset, Register value, RAStatus ra_status, SaveFPRegsMode save_fp, - SmiCheck smi_check, SlotDescriptor slot) { + SmiCheck smi_check, + ReadOnlyCheck ro_check, + SlotDescriptor slot) { ASM_CODE_COMMENT(this); + DCHECK(!AreAliased(object, value)); // First, check if a write barrier is even needed. The tests below - // catch stores of Smis. + // catch stores of Smis and read-only objects. Label done; + if (ro_check == ReadOnlyCheck::kInline) { + MaybeJumpIfReadOnlyOrSmallSmi(value, &done); + } + // Skip barrier if writing a smi. if (smi_check == SmiCheck::kInline) { JumpIfSmi(value, &done); @@ -228,7 +244,7 @@ void MacroAssembler::RecordWriteField(Register object, int offset, } RecordWrite(object, Operand(offset - kHeapObjectTag), value, ra_status, - save_fp, SmiCheck::kOmit, slot); + save_fp, SmiCheck::kOmit, ReadOnlyCheck::kOmit, slot); bind(&done); } @@ -703,7 +719,7 @@ void MacroAssembler::MoveObjectAndSlot(Register dst_object, Register dst_slot, void MacroAssembler::RecordWrite(Register object, Operand offset, Register value, RAStatus ra_status, SaveFPRegsMode fp_mode, SmiCheck smi_check, - SlotDescriptor slot) { + ReadOnlyCheck ro_check, SlotDescriptor slot) { DCHECK(!AreAliased(object, value)); if (v8_flags.slow_debug_code) { @@ -726,9 +742,14 @@ void MacroAssembler::RecordWrite(Register object, Operand offset, } // First, check if a write barrier is even needed. The tests below - // catch stores of smis and stores into the young generation. + // catch stores of smis and read-only objects, as well as stores into the + // young generation. Label done; + if (ro_check == ReadOnlyCheck::kInline) { + MaybeJumpIfReadOnlyOrSmallSmi(value, &done); + } + if (smi_check == SmiCheck::kInline) { DCHECK_EQ(0, kSmiTag); JumpIfSmi(value, &done); @@ -5311,7 +5332,7 @@ void MacroAssembler::ReplaceClosureCodeWithOptimizedCode( FieldMemOperand(closure, JSFunction::kCodeOffset)); RecordWriteField(closure, JSFunction::kCodeOffset, optimized_code, kRAHasNotBeenSaved, SaveFPRegsMode::kIgnore, SmiCheck::kOmit, - SlotDescriptor::ForCodePointerSlot()); + ReadOnlyCheck::kOmit, SlotDescriptor::ForCodePointerSlot()); } // Read off the flags in the feedback vector and check if there diff --git a/deps/v8/src/codegen/loong64/macro-assembler-loong64.h b/deps/v8/src/codegen/loong64/macro-assembler-loong64.h index a2f57e5ce0e323..1206512296e39c 100644 --- a/deps/v8/src/codegen/loong64/macro-assembler-loong64.h +++ b/deps/v8/src/codegen/loong64/macro-assembler-loong64.h @@ -817,18 +817,20 @@ class V8_EXPORT_PRIVATE MacroAssembler : public MacroAssemblerBase { // Jump the register contains a smi. void JumpIfSmi(Register value, Label* smi_label); - void JumpIfEqual(Register a, int32_t b, Label* dest) { - UseScratchRegisterScope temps(this); - Register scratch = temps.Acquire(); - li(scratch, Operand(b)); - Branch(dest, eq, a, Operand(scratch)); + inline void JumpIf(Condition cond, Register x, int32_t y, Label* dest) { + Branch(dest, cond, x, Operand(y)); } - void JumpIfLessThan(Register a, int32_t b, Label* dest) { - UseScratchRegisterScope temps(this); - Register scratch = temps.Acquire(); - li(scratch, Operand(b)); - Branch(dest, lt, a, Operand(scratch)); + inline void JumpIfEqual(Register x, int32_t y, Label* dest) { + Branch(dest, eq, x, Operand(y)); + } + + inline void JumpIfLessThan(Register x, int32_t y, Label* dest) { + Branch(dest, lt, x, Operand(y)); + } + + inline void JumpIfUnsignedLessThan(Register x, int32_t y, Label* dest) { + Branch(dest, lo, x, Operand(y)); } // Push a standard frame, consisting of ra, fp, context and JS function. @@ -1058,6 +1060,10 @@ class V8_EXPORT_PRIVATE MacroAssembler : public MacroAssemblerBase { // --------------------------------------------------------------------------- // GC Support + // Performs a fast check for whether `value` is a read-only object or a small + // Smi. Only enabled in some configurations. + void MaybeJumpIfReadOnlyOrSmallSmi(Register value, Label* dest); + // Notify the garbage collector that we wrote a pointer into an object. // |object| is the object being stored into, |value| is the object being // stored. @@ -1066,6 +1072,7 @@ class V8_EXPORT_PRIVATE MacroAssembler : public MacroAssemblerBase { void RecordWriteField( Register object, int offset, Register value, RAStatus ra_status, SaveFPRegsMode save_fp, SmiCheck smi_check = SmiCheck::kInline, + ReadOnlyCheck ro_check = ReadOnlyCheck::kInline, SlotDescriptor slot = SlotDescriptor::ForDirectPointerSlot()); // For a given |object| notify the garbage collector that the slot at |offset| @@ -1073,6 +1080,7 @@ class V8_EXPORT_PRIVATE MacroAssembler : public MacroAssemblerBase { void RecordWrite( Register object, Operand offset, Register value, RAStatus ra_status, SaveFPRegsMode save_fp, SmiCheck smi_check = SmiCheck::kInline, + ReadOnlyCheck ro_check = ReadOnlyCheck::kInline, SlotDescriptor slot = SlotDescriptor::ForDirectPointerSlot()); // --------------------------------------------------------------------------- diff --git a/deps/v8/src/compiler/backend/arm64/instruction-selector-arm64.cc b/deps/v8/src/compiler/backend/arm64/instruction-selector-arm64.cc index b3e8582232fe91..9795d9f55779c8 100644 --- a/deps/v8/src/compiler/backend/arm64/instruction-selector-arm64.cc +++ b/deps/v8/src/compiler/backend/arm64/instruction-selector-arm64.cc @@ -5817,19 +5817,14 @@ void InstructionSelector::VisitI8x16Shuffle(OpIndex node) { int lane_size = kBitsPerByte * kSimd128Size / lanes; Emit(kArm64S128Dup | LaneSizeField::encode(lane_size), dup, g.UseRegister(dup_input), g.UseImmediate(dup_index)); - if (is_swizzle) { - Emit(shuffle_op, g.DefineAsRegister(node), g.UseRegister(input0), dup); - return; - } else { - // For non-swizzles, we first need to perform the shuffles with the two - // original inputs, into a temp register. - InstructionOperand temp = g.TempSimd128Register(); - Emit(shuffle_op, temp, g.UseRegister(input0), g.UseRegister(input1)); - // Then we need to move the dup result into the top 8 bytes. - Emit(kArm64S128MoveLane | LaneSizeField::encode(64), - g.DefineSameAsFirst(node), temp, dup, g.UseImmediate(1), - g.UseImmediate(1)); - } + // First need to perform the shuffles with the two original inputs, into a + // temp register. + InstructionOperand temp = g.TempSimd128Register(); + Emit(shuffle_op, temp, g.UseRegister(input0), g.UseRegister(input1)); + // Then we need to move the dup result into the top 8 bytes. + Emit(kArm64S128MoveLane | LaneSizeField::encode(64), + g.DefineSameAsFirst(node), temp, dup, g.UseImmediate(1), + g.UseImmediate(1)); }; std::array bottom_shuffle; diff --git a/deps/v8/src/compiler/backend/loong64/code-generator-loong64.cc b/deps/v8/src/compiler/backend/loong64/code-generator-loong64.cc index 0e14ed1aae7d19..2a0090f7a6237a 100644 --- a/deps/v8/src/compiler/backend/loong64/code-generator-loong64.cc +++ b/deps/v8/src/compiler/backend/loong64/code-generator-loong64.cc @@ -1068,6 +1068,7 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction( Register scratch = i.TempRegister(0); auto ool = zone()->New( this, object, value, scratch); + __ MaybeJumpIfReadOnlyOrSmallSmi(value, ool->exit()); __ JumpIfNotSmi(value, ool->entry()); __ bind(ool->exit()); @@ -1128,6 +1129,7 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction( Register scratch = i.TempRegister(1); auto ool = zone()->New( this, object, value, scratch); + __ MaybeJumpIfReadOnlyOrSmallSmi(value, ool->exit()); __ JumpIfNotSmi(value, ool->entry()); __ bind(ool->exit()); diff --git a/deps/v8/test/mjsunit/wasm/half-dup-shuffles.js b/deps/v8/test/mjsunit/wasm/half-dup-shuffles.js index 328eb71b1259fb..4c9cc74630195d 100644 --- a/deps/v8/test/mjsunit/wasm/half-dup-shuffles.js +++ b/deps/v8/test/mjsunit/wasm/half-dup-shuffles.js @@ -54,12 +54,14 @@ function Test(config) { (function SplatAndShuffleTest(config) { const dup_byte_0 = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ] + const dup_half_0 = [0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01 ] const dup_half_8 = [0x10, 0x11, 0x10, 0x11, 0x10, 0x11, 0x10, 0x11 ] const dup_word_1 = [0x04, 0x05, 0x06, 0x07, 0x04, 0x05, 0x06, 0x07 ] const even_bytes = [0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e ] const interleave_high_halves = [0x08, 0x09, 0x18, 0x19, 0x0a, 0x0b, 0x1a, 0x1b ] const transpose_odd_words = [0x04, 0x05, 0x06, 0x07, 0x14, 0x15, 0x16, 0x17 ] + const reverse_16x2 = [0x02, 0x03, 0x00, 0x01, 0x06, 0x07, 0x04, 0x05 ] const splat_and_shuffle_tests =[ { @@ -77,6 +79,11 @@ function Test(config) { dup_shuffle: dup_word_1, shuffle: transpose_odd_words, }, + { + name: "dup and reverse 16x2", + dup_shuffle: dup_half_0, + shuffle: reverse_16x2, + }, ]; for (const config of splat_and_shuffle_tests) { diff --git a/deps/v8/test/unittests/compiler/arm64/turboshaft-instruction-selector-arm64-unittest.cc b/deps/v8/test/unittests/compiler/arm64/turboshaft-instruction-selector-arm64-unittest.cc index d278985d2a24ce..9178eb2ad29e47 100644 --- a/deps/v8/test/unittests/compiler/arm64/turboshaft-instruction-selector-arm64-unittest.cc +++ b/deps/v8/test/unittests/compiler/arm64/turboshaft-instruction-selector-arm64-unittest.cc @@ -3627,7 +3627,7 @@ std::ostream& operator<<(std::ostream& os, const DupAndShuffleInst& inst) { const DupAndShuffleInst kDupAndShuffles[] = { {"Dup 0 and UnzipLeft", kArm64S128UnzipLeft, - 2, + 3, 0, 16, 0, @@ -3635,7 +3635,7 @@ const DupAndShuffleInst kDupAndShuffles[] = { {{0, 1, 4, 5, 8, 9, 12, 13, 0, 1, 0, 1, 0, 1, 0, 1}}}, {"Dup 1 and UnzipLeft", kArm64S128UnzipLeft, - 2, + 3, 0, 16, 1, @@ -3643,7 +3643,7 @@ const DupAndShuffleInst kDupAndShuffles[] = { {{0, 1, 4, 5, 8, 9, 12, 13, 2, 3, 2, 3, 2, 3, 2, 3}}}, {"Dup 0 and UnzipRight", kArm64S128UnzipRight, - 2, + 3, 0, 16, 0, @@ -3738,28 +3738,29 @@ TEST_P(TurboshaftInstructionSelectorDupAndShuffleTest, DupAndShuffle) { Stream s = m.Build(); EXPECT_EQ(inst.expected_num_insts, s.size()); - if (inst.expected_num_insts > 1) { + if (inst.expected_num_insts == 3) { + // The dup EXPECT_EQ(kArm64S128Dup, s[0]->arch_opcode()); + EXPECT_EQ(inst.lane_size, LaneSizeField::decode(s[0]->opcode())); EXPECT_EQ(s.ToVreg(s[0]->InputAt(0)), s.ToVreg(m.Parameter(inst.expected_param_index))); EXPECT_EQ(s.ToInt32(s[0]->InputAt(1)), inst.index); - EXPECT_EQ(inst.lane_size, LaneSizeField::decode(s[0]->opcode())); + // The shuffle EXPECT_EQ(inst.arch_opcode, s[1]->arch_opcode()); EXPECT_EQ(inst.lane_size, LaneSizeField::decode(s[1]->opcode())); - - if (inst.expected_num_insts == 3) { - EXPECT_EQ(s.ToVreg(s[1]->InputAt(0)), s.ToVreg(m.Parameter(0))); - EXPECT_EQ(s.ToVreg(s[1]->InputAt(1)), s.ToVreg(m.Parameter(1))); - EXPECT_EQ(kArm64S128MoveLane, s[2]->arch_opcode()); - EXPECT_EQ(1U, s[2]->OutputCount()); + EXPECT_EQ(s.ToVreg(s[1]->InputAt(0)), s.ToVreg(m.Parameter(0))); + if (inst.is_swizzle) { + EXPECT_EQ(s.ToVreg(s[1]->InputAt(1)), s.ToVreg(m.Parameter(0))); } else { - EXPECT_EQ(s.ToVreg(s[1]->InputAt(0)), - s.ToVreg(m.Parameter(inst.expected_param_index))); - EXPECT_EQ(s.ToVreg(s[1]->InputAt(1)), s.ToVreg(s[0]->Output())); - EXPECT_EQ(1U, s[1]->OutputCount()); + EXPECT_EQ(s.ToVreg(s[1]->InputAt(1)), s.ToVreg(m.Parameter(1))); } + + // Copy the top half of the dup into the result register. + EXPECT_EQ(kArm64S128MoveLane, s[2]->arch_opcode()); + EXPECT_EQ(1U, s[2]->OutputCount()); } else { + DCHECK_EQ(inst.expected_num_insts, 1); EXPECT_EQ(inst.arch_opcode, s[0]->arch_opcode()); } }