deps: V8: backport c4d06ba586f3

Original 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 <zhaojiazhong-hf@loongson.cn>
    Commit-Queue: Liu Yu <liuyu@loongson.cn>
    Reviewed-by: Darius Mercadier <dmercadier@chromium.org>
    Auto-Submit: Liu Yu <liuyu@loongson.cn>
    Cr-Commit-Position: refs/heads/main@{#107619}

Refs: https://github.com/v8/v8/commit/c4d06ba586f33b8e5db81507b05e2cad9b7c07dc
Co-authored-by: liujiahui <liujiahui@loongson.cn>
PR-URL: https://github.com/nodejs/node/pull/63731
Fixes: https://github.com/nodejs/node/issues/63721
Reviewed-By: René <contact.9a5d6388@renegade334.me.uk>
Reviewed-By: Michaël Zasso <targos@protonmail.com>
This commit is contained in:
liujiahuiandliujiahui authored and GitHub committed 2026-07-24 09:57:00 +00:00
1 parent 6572cf54b4
commit 94e11b022d
7 files changed
+443 -45

No files matched your search

+1 -1
View File
@@ -42,7 +42,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.26',
'v8_embedder_string': '-node.27',
##### V8 defaults for Node.js #####
+15
View File
@@ -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) {
+4
View File
@@ -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);
+140 -41
View File
@@ -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());
@@ -48,6 +48,20 @@ class Loong64OperandGenerator final : public OperandGenerator {
return UseRegister(node);
}
InstructionOperand UseRegisterAtEndOrImmediateZero(OpIndex node) {
if (const ConstantOp* constant =
selector()->Get(node).TryCast<ConstantOp>()) {
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);
}
+4
View File
@@ -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) {
@@ -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 <cmath>
#include <functional>
#include <limits>
#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<int64_t> 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<int64_t> 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<uint64_t>(state.sp);
#else
uint64_t sp = reinterpret_cast<uint64_t>(&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<int64_t> 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<int64_t> 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<int64_t> 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<int64_t> 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<int64_t> 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<int64_t> 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