From d51bfac8baa49dc6a3f8604a6236fb826c82f21c Mon Sep 17 00:00:00 2001 From: liujiahui Date: Wed, 3 Jun 2026 16:01:01 +0800 Subject: [PATCH] deps: V8: cherry-pick c4d06ba586f Origin commit message: [loong64][compiler] Extend Word64Select instruction functionality Change-Id: Iba762777642d2d2d3aa904f9afc1e9005139992e Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/7801520 Reviewed-by: Zhao Jiazhong Commit-Queue: Liu Yu Reviewed-by: Darius Mercadier Auto-Submit: Liu Yu Cr-Commit-Position: refs/heads/main@{#107619} Refs: v8/v8@c4d06ba --- common.gypi | 2 +- .../loong64/macro-assembler-loong64.cc | 15 ++ .../codegen/loong64/macro-assembler-loong64.h | 4 + .../backend/loong64/code-generator-loong64.cc | 181 ++++++++++--- .../loong64/instruction-selector-loong64.cc | 32 ++- deps/v8/test/cctest/BUILD.gn | 4 + .../test-run-machops-select-loong64.cc | 250 ++++++++++++++++++ 7 files changed, 443 insertions(+), 45 deletions(-) create mode 100644 deps/v8/test/cctest/compiler/test-run-machops-select-loong64.cc diff --git a/common.gypi b/common.gypi index 26c2ef3675fd0e..36b4b1138dbfd2 100644 --- a/common.gypi +++ b/common.gypi @@ -40,7 +40,7 @@ # Reset this number to 0 on major V8 upgrades. # Increment by one for each non-official patch applied to deps/v8. - 'v8_embedder_string': '-node.20', + 'v8_embedder_string': '-node.21', ##### V8 defaults for Node.js ##### diff --git a/deps/v8/src/codegen/loong64/macro-assembler-loong64.cc b/deps/v8/src/codegen/loong64/macro-assembler-loong64.cc index a8c8e961fc5013..71f521e8a3ff54 100644 --- a/deps/v8/src/codegen/loong64/macro-assembler-loong64.cc +++ b/deps/v8/src/codegen/loong64/macro-assembler-loong64.cc @@ -3140,6 +3140,21 @@ void MacroAssembler::TruncateDoubleToI(Isolate* isolate, Zone* zone, bind(&done); } +void MacroAssembler::SelectWord(Register result, Register cond, Register v_true, + Register v_false) { + if (v_false == zero_reg) { + maskeqz(result, v_true, cond); + } else if (v_true == zero_reg) { + masknez(result, v_false, cond); + } else { + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + maskeqz(scratch, v_true, cond); + masknez(result, v_false, cond); + or_(result, scratch, result); + } +} + void MacroAssembler::CompareWord(Condition cond, Register dst, Register lhs, const Operand& rhs) { switch (cond) { diff --git a/deps/v8/src/codegen/loong64/macro-assembler-loong64.h b/deps/v8/src/codegen/loong64/macro-assembler-loong64.h index be7502a5b5b833..b6be8e7781d531 100644 --- a/deps/v8/src/codegen/loong64/macro-assembler-loong64.h +++ b/deps/v8/src/codegen/loong64/macro-assembler-loong64.h @@ -140,6 +140,10 @@ class V8_EXPORT_PRIVATE MacroAssembler : public MacroAssemblerBase { // Print a message to stdout and abort execution. void Abort(AbortReason msg); + // Select v_true if cond is non-zero, otherwise select v_false. + void SelectWord(Register result, Register cond, Register v_true, + Register v_false); + void CompareWord(Condition cond, Register dst, Register lhs, const Operand& rhs); void Branch(Label* label, bool need_link = false); 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 57262ede9d331e..a0974e320ed3eb 100644 --- a/deps/v8/src/compiler/backend/loong64/code-generator-loong64.cc +++ b/deps/v8/src/compiler/backend/loong64/code-generator-loong64.cc @@ -1363,10 +1363,14 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction( case kLoong64Sub_d: __ Sub_d(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); break; - case kLoong64SubOvf_d: + case kLoong64SubOvf_d: { + UseScratchRegisterScope temps(masm()); + DCHECK(temps.hasAvailable()); + temps.Exclude(t8); __ SubOverflow_d(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1), t8); break; + } case kLoong64Mul_w: __ Mul_w(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); break; @@ -4591,7 +4595,7 @@ void CodeGenerator::AssembleArchBoolean(Instruction* instr, } return; } else { - PrintF("AssembleArchBranch Unimplemented arch_opcode is : %d\n", + PrintF("AssembleArchBoolean Unimplemented arch_opcode is : %d\n", instr->arch_opcode()); TRACE("UNIMPLEMENTED code_generator_loong64: %s at line %d\n", __FUNCTION__, __LINE__); @@ -4646,56 +4650,49 @@ void CodeGenerator::AssembleArchSelect(Instruction* instr, size_t output_index = instr->OutputCount() - 1; // We don't know how many inputs were consumed by the condition, so we have to // calculate the indices of the last two inputs. - DCHECK_GE(instr->InputCount(), 4); size_t true_value_index = instr->InputCount() - 2; size_t false_value_index = instr->InputCount() - 1; + Register result = i.OutputRegister(output_index); + Register v_true = i.InputOrZeroRegister(true_value_index); + Register v_false = i.InputOrZeroRegister(false_value_index); + + DCHECK( + LocationOperand::cast(instr->OutputAt(output_index))->representation() == + MachineRepresentation::kWord64); if (instr->arch_opcode() == kLoong64Tst) { + DCHECK_GE(instr->InputCount(), 4); Condition cc = FlagsConditionToConditionTst(condition); - Register result = i.OutputRegister(output_index); - Register v_true = i.InputOrZeroRegister(true_value_index); - Register v_false = i.InputOrZeroRegister(false_value_index); - if (v_true == zero_reg || v_false == zero_reg) { - if (v_true == zero_reg) { - v_true = v_false; - cc = NegateCondition(cc); - } - if (cc == eq) - __ masknez(result, v_true, t8); - else - __ maskeqz(result, v_true, t8); - } else if (result == v_true || result == v_false) { - if (result == v_false) { - v_false = v_true; - cc = NegateCondition(cc); - } - Label done; - __ Branch(&done, cc, t8, Operand(0)); - __ Move(result, v_false); - __ bind(&done); - } else { - UseScratchRegisterScope temps(masm()); - Register scratch = temps.Acquire(); - if (cc == eq) { - Register temp = v_true; - v_true = v_false; - v_false = temp; - } - __ maskeqz(scratch, v_true, t8); - __ masknez(result, v_false, t8); - __ or_(result, scratch, result); + if (cc == eq) { + Register temp = v_true; + v_true = v_false; + v_false = temp; } + __ SelectWord(result, t8, v_true, v_false); UseScratchRegisterScope temps(masm()); temps.Include(t8); return; } else if (instr->arch_opcode() == kLoong64Cmp64 || - instr->arch_opcode() == kLoong64Cmp32) { + instr->arch_opcode() == kLoong64Cmp32 || + instr->arch_opcode() == kArchStackPointerGreaterThan) { Condition cc = FlagsConditionToConditionCmp(condition); - Register left = i.InputRegister(0); - Operand right = i.InputOperand(1); - Register result = i.OutputRegister(output_index); - Register v_true = i.InputOrZeroRegister(true_value_index); - Register v_false = i.InputOrZeroRegister(false_value_index); + Register left = no_reg; + Operand right = Operand(0); + if (instr->arch_opcode() == kArchStackPointerGreaterThan) { + DCHECK_GE(instr->InputCount(), 3); + DCHECK((cc == ls) || (cc == hi)); + left = sp; + right = i.InputOperand(0); + uint32_t offset; + if (ShouldApplyOffsetToStackCheck(instr, &offset)) { + left = i.TempRegister(1); + __ Sub_d(left, sp, offset); + } + } else { + DCHECK_GE(instr->InputCount(), 4); + left = i.InputRegister(0); + right = i.InputOperand(1); + } if (v_true == zero_reg || v_false == zero_reg) { if (v_true == zero_reg) { v_true = v_false; @@ -4725,6 +4722,108 @@ void CodeGenerator::AssembleArchSelect(Instruction* instr, __ bind(&done); } return; + } else if (instr->arch_opcode() == kLoong64Add_d || + instr->arch_opcode() == kLoong64Sub_d) { + DCHECK_GE(instr->InputCount(), 4); + Condition cc = FlagsConditionToConditionOvf(condition); + if (cc == eq) { + Register temp = v_true; + v_true = v_false; + v_false = temp; + } + UseScratchRegisterScope temps(masm()); + Register scratch1 = temps.Acquire(); + Register scratch2 = temps.Acquire(); + __ srai_d(scratch1, i.OutputRegister(), 32); + __ srai_w(scratch2, i.OutputRegister(), 31); + if (v_false == zero_reg) { + __ xor_(scratch1, scratch1, scratch2); + __ maskeqz(result, v_true, scratch1); + } else if (v_true == zero_reg) { + __ xor_(scratch1, scratch1, scratch2); + __ masknez(result, v_false, scratch1); + } else if (result == v_true || result == v_false) { + if (result == v_false) { + v_false = v_true; + cc = NegateCondition(cc); + } + Label done; + __ Branch(&done, cc, scratch2, Operand(scratch1)); + __ Move(result, v_false); + __ bind(&done); + } else { + Label true_label, done; + __ Branch(&true_label, cc, scratch2, Operand(scratch1)); + __ Move(result, v_false); + __ Branch(&done); + __ bind(&true_label); + __ Move(result, v_true); + __ bind(&done); + } + return; + } else if (instr->arch_opcode() == kLoong64AddOvf_d || + instr->arch_opcode() == kLoong64SubOvf_d) { + DCHECK_GE(instr->InputCount(), 4); + // Overflow occurs if overflow register is negative + Condition cc = lt; + if (condition == kNotOverflow) { + Register temp = v_true; + v_true = v_false; + v_false = temp; + } + if (v_false == zero_reg) { + __ slt(t8, t8, zero_reg); + __ maskeqz(result, v_true, t8); + } else if (v_true == zero_reg) { + __ slt(t8, t8, zero_reg); + __ masknez(result, v_false, t8); + } else if (result == v_true || result == v_false) { + if (result == v_false) { + v_false = v_true; + cc = NegateCondition(cc); + } + Label done; + __ Branch(&done, cc, t8, Operand(zero_reg)); + __ Move(result, v_false); + __ bind(&done); + } else { + Label true_label, done; + __ Branch(&true_label, cc, t8, Operand(zero_reg)); + __ Move(result, v_false); + __ Branch(&done); + __ bind(&true_label); + __ Move(result, v_true); + __ bind(&done); + } + UseScratchRegisterScope temps(masm()); + temps.Include(t8); + } else if (instr->arch_opcode() == kLoong64MulOvf_w || + instr->arch_opcode() == kLoong64MulOvf_d) { + DCHECK_GE(instr->InputCount(), 4); + Condition cc = FlagsConditionToConditionOvf(condition); + if (cc == eq) { + Register temp = v_true; + v_true = v_false; + v_false = temp; + } + __ SelectWord(result, t8, v_true, v_false); + UseScratchRegisterScope temps(masm()); + temps.Include(t8); + return; + } else if (instr->arch_opcode() == kLoong64Float32Cmp || + instr->arch_opcode() == kLoong64Float64Cmp) { + bool predicate; + FlagsConditionToConditionCmpFPU(&predicate, condition); + UseScratchRegisterScope temps(masm()); + Register scratch = temps.Acquire(); + if (!predicate) { + Register temp = v_true; + v_true = v_false; + v_false = temp; + } + __ movcf2gr(scratch, FCC0); + __ SelectWord(result, scratch, v_true, v_false); + return; } else { PrintF("AssembleArchSelect Unimplemented arch_opcode is : %d\n", instr->arch_opcode()); diff --git a/deps/v8/src/compiler/backend/loong64/instruction-selector-loong64.cc b/deps/v8/src/compiler/backend/loong64/instruction-selector-loong64.cc index 4126871d4527fd..05ed6dae4f0b2f 100644 --- a/deps/v8/src/compiler/backend/loong64/instruction-selector-loong64.cc +++ b/deps/v8/src/compiler/backend/loong64/instruction-selector-loong64.cc @@ -48,6 +48,20 @@ class Loong64OperandGenerator final : public OperandGenerator { return UseRegister(node); } + InstructionOperand UseRegisterAtEndOrImmediateZero(OpIndex node) { + if (const ConstantOp* constant = + selector()->Get(node).TryCast()) { + if ((constant->IsIntegral() && constant->integral() == 0) || + (constant->kind == ConstantOp::Kind::kFloat32 && + constant->float32().get_bits() == 0) || + (constant->kind == ConstantOp::Kind::kFloat64 && + constant->float64().get_bits() == 0)) { + return UseImmediate(node); + } + } + return UseRegisterAtEnd(node); + } + bool IsIntegerConstant(OpIndex node) { int64_t unused; return selector()->MatchSignedIntegralConstant(node, &unused); @@ -307,7 +321,7 @@ static void VisitBinop(InstructionSelector* selector, turboshaft::OpIndex node, InstructionCode reverse_opcode, FlagsContinuation* cont) { Loong64OperandGenerator g(selector); - InstructionOperand inputs[2]; + InstructionOperand inputs[4]; size_t input_count = 0; InstructionOperand outputs[1]; size_t output_count = 0; @@ -331,6 +345,13 @@ static void VisitBinop(InstructionSelector* selector, turboshaft::OpIndex node, inputs[input_count++] = g.UseOperand(right_node, opcode); } + if (cont->IsSelect()) { + inputs[input_count++] = + g.UseRegisterAtEndOrImmediateZero(cont->true_value()); + inputs[input_count++] = + g.UseRegisterAtEndOrImmediateZero(cont->false_value()); + } + outputs[output_count++] = g.DefineAsRegister(node); DCHECK_NE(0u, input_count); @@ -2169,9 +2190,14 @@ void InstructionSelector::VisitStackPointerGreaterThan( ? OperandGenerator::kUniqueRegister : OperandGenerator::kRegister; - InstructionOperand inputs[] = {g.UseRegisterWithMode(value, register_mode)}; - static constexpr int input_count = arraysize(inputs); + InstructionOperand inputs[3]; + int input_count = 0; + inputs[input_count++] = g.UseRegisterWithMode(value, register_mode); + if (cont->IsSelect()) { + inputs[input_count++] = g.UseRegisterOrImmediateZero(cont->true_value()); + inputs[input_count++] = g.UseRegisterOrImmediateZero(cont->false_value()); + } EmitWithContinuation(opcode, output_count, outputs, input_count, inputs, temp_count, temps, cont); } diff --git a/deps/v8/test/cctest/BUILD.gn b/deps/v8/test/cctest/BUILD.gn index dd759917f78482..52faa5ccbcafd2 100644 --- a/deps/v8/test/cctest/BUILD.gn +++ b/deps/v8/test/cctest/BUILD.gn @@ -233,6 +233,10 @@ v8_source_set("cctest_sources") { if (is_win) { sources += [ "test-stack-unwinding-win64.cc" ] } + } else if (v8_current_cpu == "loong64") { + if (v8_enable_turbofan) { + sources += [ "compiler/test-run-machops-select-loong64.cc" ] + } } if (v8_use_perfetto) { diff --git a/deps/v8/test/cctest/compiler/test-run-machops-select-loong64.cc b/deps/v8/test/cctest/compiler/test-run-machops-select-loong64.cc new file mode 100644 index 00000000000000..946b2173507e70 --- /dev/null +++ b/deps/v8/test/cctest/compiler/test-run-machops-select-loong64.cc @@ -0,0 +1,250 @@ +// Copyright 2026 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. + +#include +#include +#include + +#include "src/base/bits.h" +#include "src/base/ieee754.h" +#include "src/base/numerics/safe_conversions.h" +#include "src/base/overflowing-math.h" +#include "src/base/utils/random-number-generator.h" +#include "src/builtins/builtins.h" +#include "src/common/ptr-compr-inl.h" +#include "src/objects/objects-inl.h" +#include "src/utils/boxed-float.h" +#include "src/utils/utils.h" +#include "test/cctest/cctest.h" +#include "test/cctest/compiler/codegen-tester.h" +#include "test/common/flag-utils.h" +#include "test/common/value-helper.h" + +namespace v8 { +namespace internal { +namespace compiler { + +#define WORD_COMPARE(Cond, Type, TYPE, SIGN, v_true, v_false) \ + { \ + BufferedRawMachineAssemblerTester m(MachineType::Type(), \ + MachineType::Type()); \ + if (!m.machine()->Word64Select().IsSupported()) { \ + return; \ + } \ + Node* cmp = m.Cond(m.Parameter(0), m.Parameter(1)); \ + m.Return(m.Word64Select(cmp, m.Int64Constant(v_true), \ + m.Int64Constant(v_false))); \ + FOR_##TYPE##_INPUTS(i) { \ + FOR_##TYPE##_INPUTS(j) { \ + CHECK_EQ(m.Call(i, j), (i SIGN j) ? v_true : v_false); \ + } \ + } \ + } + +TEST(RunSelectWord32Compare) { + for (int64_t k = -50; k < 50; k++) { + int64_t v_true = k % 5, v_false = k % 11; + WORD_COMPARE(Word32Equal, Int32, INT32, ==, v_true, v_false) + WORD_COMPARE(Int32LessThan, Int32, INT32, <, v_true, v_false) + WORD_COMPARE(Int32LessThanOrEqual, Int32, INT32, <=, v_true, v_false) + WORD_COMPARE(Uint32LessThan, Uint32, UINT32, <, v_true, v_false) + WORD_COMPARE(Uint32LessThanOrEqual, Uint32, UINT32, <=, v_true, v_false) + } +} + +TEST(RunSelectWord64Compare) { + for (int64_t k = -50; k < 50; k++) { + int64_t v_true = k % 5, v_false = k % 11; + WORD_COMPARE(Word64Equal, Int64, INT64, ==, v_true, v_false) + WORD_COMPARE(Int64LessThan, Int64, INT64, <, v_true, v_false) + WORD_COMPARE(Int64LessThanOrEqual, Int64, INT64, <=, v_true, v_false) + WORD_COMPARE(Uint64LessThan, Uint64, UINT64, <, v_true, v_false) + WORD_COMPARE(Uint64LessThanOrEqual, Uint64, UINT64, <=, v_true, v_false) + } +} + +TEST(RunSelectFloat32Compare) { + for (int64_t k = -50; k < 50; k++) { + int64_t v_true = k % 5, v_false = k % 11; + WORD_COMPARE(Float32Equal, Float32, FLOAT32, ==, v_true, v_false) + WORD_COMPARE(Float32LessThan, Float32, FLOAT32, <, v_true, v_false) + WORD_COMPARE(Float32LessThanOrEqual, Float32, FLOAT32, <=, v_true, v_false) + } +} + +TEST(RunSelectFloat64Compare) { + for (int64_t k = -50; k < 50; k++) { + int64_t v_true = k % 5, v_false = k % 11; + WORD_COMPARE(Float64Equal, Float64, FLOAT64, ==, v_true, v_false) + WORD_COMPARE(Float64LessThan, Float64, FLOAT64, <, v_true, v_false) + WORD_COMPARE(Float64LessThanOrEqual, Float64, FLOAT64, <=, v_true, v_false) + } +} + +#undef WORD_COMPARE + +TEST(RunSelectStackPointerGreaterThan) { + for (int64_t k = -50; k < 50; k++) { + int64_t v_true = k % 5, v_false = k % 11; + BufferedRawMachineAssemblerTester m(MachineType::Uint64()); + if (!m.machine()->Word64Select().IsSupported()) { + return; + } + v8::RegisterState state; +#if defined(USE_SIMULATOR) + SimulatorHelper simulator_helper; + if (!simulator_helper.Init(CcTest::isolate())) return; + simulator_helper.FillRegisters(&state); + uint64_t sp = reinterpret_cast(state.sp); +#else + uint64_t sp = reinterpret_cast(&state); +#endif + Node* cmp = m.StackPointerGreaterThan(m.Parameter(0)); + m.Return( + m.Word64Select(cmp, m.Int64Constant(v_true), m.Int64Constant(v_false))); + FOR_UINT64_INPUTS(i) { CHECK_EQ(m.Call(i), sp > i ? v_true : v_false); } + } +} + +TEST(RunSelectWord32AddOvf) { + for (int64_t k = -50; k < 50; k++) { + int64_t v_true = k % 5, v_false = k % 11; + BufferedRawMachineAssemblerTester m(MachineType::Int32(), + MachineType::Int32()); + if (!m.machine()->Word64Select().IsSupported()) { + return; + } + Node* cal = m.Int32AddWithOverflow(m.Parameter(0), m.Parameter(1)); + Node* ovf = m.Projection(1, cal); + m.Return( + m.Word64Select(ovf, m.Int64Constant(v_true), m.Int64Constant(v_false))); + int result; + FOR_INT32_INPUTS(i) { + FOR_INT32_INPUTS(j) { + CHECK_EQ(m.Call(i, j), base::bits::SignedAddOverflow32(i, j, &result) + ? v_true + : v_false); + } + } + } +} + +TEST(RunSelectWord32SubOvf) { + for (int64_t k = -50; k < 50; k++) { + int64_t v_true = k % 5, v_false = k % 11; + BufferedRawMachineAssemblerTester m(MachineType::Int32(), + MachineType::Int32()); + if (!m.machine()->Word64Select().IsSupported()) { + return; + } + Node* cal = m.Int32SubWithOverflow(m.Parameter(0), m.Parameter(1)); + Node* ovf = m.Projection(1, cal); + m.Return( + m.Word64Select(ovf, m.Int64Constant(v_true), m.Int64Constant(v_false))); + int result; + FOR_INT32_INPUTS(i) { + FOR_INT32_INPUTS(j) { + CHECK_EQ(m.Call(i, j), base::bits::SignedSubOverflow32(i, j, &result) + ? v_true + : v_false); + } + } + } +} + +TEST(RunSelectWord32MulOvf) { + for (int64_t k = -50; k < 50; k++) { + int64_t v_true = k % 5, v_false = k % 11; + BufferedRawMachineAssemblerTester m(MachineType::Int32(), + MachineType::Int32()); + if (!m.machine()->Word64Select().IsSupported()) { + return; + } + Node* cal = m.Int32MulWithOverflow(m.Parameter(0), m.Parameter(1)); + Node* ovf = m.Projection(1, cal); + m.Return( + m.Word64Select(ovf, m.Int64Constant(v_true), m.Int64Constant(v_false))); + int result; + FOR_INT32_INPUTS(i) { + FOR_INT32_INPUTS(j) { + CHECK_EQ(m.Call(i, j), base::bits::SignedMulOverflow32(i, j, &result) + ? v_true + : v_false); + } + } + } +} + +TEST(RunSelectWord64AddOvf) { + for (int64_t k = -50; k < 50; k++) { + int64_t v_true = k % 5, v_false = k % 11; + BufferedRawMachineAssemblerTester m(MachineType::Int64(), + MachineType::Int64()); + if (!m.machine()->Word64Select().IsSupported()) { + return; + } + Node* cal = m.Int64AddWithOverflow(m.Parameter(0), m.Parameter(1)); + Node* ovf = m.Projection(1, cal); + m.Return( + m.Word64Select(ovf, m.Int64Constant(v_true), m.Int64Constant(v_false))); + int64_t result; + FOR_INT64_INPUTS(i) { + FOR_INT64_INPUTS(j) { + CHECK_EQ(m.Call(i, j), base::bits::SignedAddOverflow64(i, j, &result) + ? v_true + : v_false); + } + } + } +} + +TEST(RunSelectWord64SubOvf) { + for (int64_t k = -50; k < 50; k++) { + int64_t v_true = k % 5, v_false = k % 11; + BufferedRawMachineAssemblerTester m(MachineType::Int64(), + MachineType::Int64()); + if (!m.machine()->Word64Select().IsSupported()) { + return; + } + Node* cal = m.Int64SubWithOverflow(m.Parameter(0), m.Parameter(1)); + Node* ovf = m.Projection(1, cal); + m.Return( + m.Word64Select(ovf, m.Int64Constant(v_true), m.Int64Constant(v_false))); + int64_t result; + FOR_INT64_INPUTS(i) { + FOR_INT64_INPUTS(j) { + CHECK_EQ(m.Call(i, j), base::bits::SignedSubOverflow64(i, j, &result) + ? v_true + : v_false); + } + } + } +} + +TEST(RunSelectWord64MulOvf) { + for (int64_t k = -50; k < 50; k++) { + int64_t v_true = k % 5, v_false = k % 11; + BufferedRawMachineAssemblerTester m(MachineType::Int64(), + MachineType::Int64()); + if (!m.machine()->Word64Select().IsSupported()) { + return; + } + Node* cal = m.Int64MulWithOverflow(m.Parameter(0), m.Parameter(1)); + Node* ovf = m.Projection(1, cal); + m.Return( + m.Word64Select(ovf, m.Int64Constant(v_true), m.Int64Constant(v_false))); + int64_t result; + FOR_INT64_INPUTS(i) { + FOR_INT64_INPUTS(j) { + CHECK_EQ(m.Call(i, j), base::bits::SignedMulOverflow64(i, j, &result) + ? v_true + : v_false); + } + } + } +} + +} // namespace compiler +} // namespace internal +} // namespace v8