// Copyright 2024 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 "src/base/vector.h"
#include "src/objects/objects-inl.h"
#include "test/cctest/cctest.h"
#include "test/cctest/compiler/turboshaft-codegen-tester.h"
#include "test/common/value-helper.h"

namespace v8::internal::compiler::turboshaft {

namespace {

constexpr TurboshaftBinop kLogicOpcodes[] = {TurboshaftBinop::kWord32BitwiseAnd,
                                             TurboshaftBinop::kWord32BitwiseOr};
constexpr std::array kInt32CmpOpcodes = {
#ifdef V8_COMPRESS_POINTERS
    TurboshaftComparison::kTaggedEqual,
#endif  // V8_COMPRESS_POINTERS
    TurboshaftComparison::kWord32Equal,
    TurboshaftComparison::kInt32LessThan,
    TurboshaftComparison::kInt32LessThanOrEqual,
    TurboshaftComparison::kUint32LessThan,
    TurboshaftComparison::kUint32LessThanOrEqual};
#ifdef V8_COMPRESS_POINTERS
constexpr size_t kNumInt32Cmps = 6;
#else
constexpr size_t kNumInt32Cmps = 5;
#endif  // V8_COMPRESS_POINTERS

#if V8_TARGET_ARCH_64_BIT
constexpr std::array kInt64CmpOpcodes = {
#ifndef V8_COMPRESS_POINTERS
    TurboshaftComparison::kTaggedEqual,
#endif  // V8_COMPRESS_POINTERS
    TurboshaftComparison::kWord64Equal,
    TurboshaftComparison::kInt64LessThan,
    TurboshaftComparison::kInt64LessThanOrEqual,
    TurboshaftComparison::kUint64LessThan,
    TurboshaftComparison::kUint64LessThanOrEqual};
#ifdef V8_COMPRESS_POINTERS
constexpr size_t kNumInt64Cmps = 5;
#else
constexpr size_t kNumInt64Cmps = 6;
#endif  // V8_COMPRESS_POINTERS
#endif  // V8_TARGET_ARCH_64_BIT

#ifdef V8_ENABLE_FP_PARAMS_IN_C_LINKAGE
constexpr std::array kFloat32CmpOpcodes = {
    TurboshaftComparison::kFloat32Equal, TurboshaftComparison::kFloat32LessThan,
    TurboshaftComparison::kFloat32LessThanOrEqual};
#if V8_TARGET_ARCH_64_BIT
constexpr std::array kFloat64CmpOpcodes = {
    TurboshaftComparison::kFloat64Equal, TurboshaftComparison::kFloat64LessThan,
    TurboshaftComparison::kFloat64LessThanOrEqual};
#endif  // V8_TARGET_ARCH_64_BIT
#endif  // V8_ENABLE_FP_PARAMS_IN_C_LINKAGE

enum GraphShape { kBalanced, kUnbalanced };
enum InvertPattern {
  kNoInvert,
  kInvertCompare,
  kInvertLogic,
  kInvertCompareAndLogic,
  kInvertCompareDouble,
  kInvertLogicDouble
};
enum BranchPattern { kNone, kDirect, kEqualZero, kNotEqualZero };

constexpr GraphShape kGraphShapes[] = {kBalanced, kUnbalanced};
constexpr InvertPattern kInvertPatterns[] = {kNoInvert,
                                             kInvertCompare,
                                             kInvertLogic,
                                             kInvertCompareAndLogic,
                                             kInvertCompareDouble,
                                             kInvertLogicDouble};
constexpr BranchPattern kBranchPatterns[] = {kNone, kDirect, kEqualZero,
                                             kNotEqualZero};

// These are shorter versions of ValueHelper::uint32_vector() and
// ValueHelper::uint64_vector() (which are used by FOR_UINT32_INPUTS and
// FOR_UINT64_INPUTS).
static constexpr uint32_t uint32_test_array[] = {
    0x00000000, 0x00000001, 0xFFFFFFFF, 0x1B09788B, 0x00000005,
    0x00000008, 0x273A798E, 0x56123761, 0xFFFFFFFD, 0x001FFFFF,
    0x0007FFFF, 0x7FC00000, 0x7F876543};
static constexpr auto uint32_test_vector = base::VectorOf(uint32_test_array);
#ifdef V8_TARGET_ARCH_64_BIT
static constexpr uint64_t uint64_test_array[] = {
    0x00000000,         0x00000001,         0xFFFFFFFF,
    0x1B09788B,         0x00000008,         0xFFFFFFFFFFFFFFFF,
    0xFFFFFFFFFFFFFFFE, 0x0000000100000000, 0x1B09788B00000000,
    0x273A798E187937A3, 0xECE3AF835495A16B, 0x80000000EEEEEEEE,
    0x007FFFFFDDDDDDDD, 0x8000000000000000, 0x7FF8000000000000,
    0x7FF7654321FEDCBA};
static constexpr auto uint64_test_vector = base::VectorOf(uint64_test_array);
#endif  // V8_TARGET_ARCH_64_BIT
#ifdef V8_ENABLE_FP_PARAMS_IN_C_LINKAGE
static constexpr float float32_test_array[] = {
    -9223372036854775808.0,
    -1.45381e+10,
    -2147483904.0,
    -2147483648.0,
    -2147483520.0,
    -1.3956e+09,
    -1.0,
    -0.0,
    0.0,
    1.0,
    2147483648.0,
    2147483904.0,
    4294967296.0,
    std::numeric_limits<float>::infinity(),
    std::numeric_limits<float>::quiet_NaN(),
    -std::numeric_limits<float>::quiet_NaN()};
static constexpr auto float32_test_vector = base::VectorOf(float32_test_array);
#ifdef V8_TARGET_ARCH_64_BIT
static constexpr double float64_test_array[] = {
    -9223372036854775808.0,
    -1.0,
    -0.5,
    -0.0,
    0.0,
    3e-88,
    0.5,
    1.0,
    9223372036854775808.0,
    9223373136366403584.0,
    18446744073709551616.0,
    V8_INFINITY,
    -V8_INFINITY,
    std::numeric_limits<double>::quiet_NaN(),
    -std::numeric_limits<double>::quiet_NaN()};
static constexpr auto float64_test_vector = base::VectorOf(float64_test_array);
#endif  // V8_TARGET_ARCH_64_BIT
#endif  // V8_ENABLE_FP_PARAMS_IN_C_LINKAGE

// Given kGraphShapes and kInvertPatterns, defined above, the graphs produced by
// the test framework, with four compares, are illustrated below. In the cases
// where we insert a branch, this takes the final logic node as the input.

// kBalanced - kNoInvert
// a       b    c       d    a        b   c       d
// |       |    |       |    |        |   |       |
// |       |    |       |    |        |   |       |
// -> cmp <-    -> cmp <-    -> cmp <-    -> cmp <-
//     |            |            |            |
//     --> logic <--             --> logic <--
//           |                         |
//           ---------> logic <--------
//

// kBalanced - kInvertCompare
// a       b    c       d    a        b   c       d
// |       |    |       |    |        |   |       |
// |       |    |       |    |        |   |       |
// -> cmp <-    -> cmp <-    -> cmp <-    -> cmp <-
//     |            |            |            |
//    not           |           not           |
//     |            |            |            |
//     --> logic <--             --> logic <--
//           |                         |
//           |                         |
//           ---------> logic <--------

// kBalanced - kInvertCompareDouble
// a       b    c       d    a        b   c       d
// |       |    |       |    |        |   |       |
// |       |    |       |    |        |   |       |
// -> cmp <-    -> cmp <-    -> cmp <-    -> cmp <-
//     |            |            |            |
//    not           |           not           |
//     |            |            |            |
//    not           |           not           |
//     |            |            |            |
//     --> logic <--             --> logic <--
//           |                         |
//           |                         |
//           ---------> logic <--------

// kBalanced - kInvertLogic
// a       b    c       d    a        b   c       d
// |       |    |       |    |        |   |       |
// |       |    |       |    |        |   |       |
// -> cmp <-    -> cmp <-    -> cmp <-    -> cmp <-
//     |            |            |            |
//     --> logic <--             --> logic <--
//           |                         |
//          not                        |
//           ---------> logic <--------

// kBalanced - kInvertLogicDouble
// a       b    c       d    a        b   c       d
// |       |    |       |    |        |   |       |
// |       |    |       |    |        |   |       |
// -> cmp <-    -> cmp <-    -> cmp <-    -> cmp <-
//     |            |            |            |
//     --> logic <--             --> logic <--
//           |                         |
//          not                        |
//           |                         |
//          not                        |
//           ---------> logic <--------

// kBalanced - kInvertCompareAndLogic
// a       b    c       d    a        b   c       d
// |       |    |       |    |        |   |       |
// |       |    |       |    |        |   |       |
// -> cmp <-    -> cmp <-    -> cmp <-    -> cmp <-
//     |            |            |            |
//    not           |           not           |
//     |            |            |            |
//     --> logic <--             --> logic <--
//           |                         |
//          not                        |
//           ---------> logic <--------

// kUnbalanced - kNoInvert
// a       b    c       d    a        b   c       d
// |       |    |       |    |        |   |       |
// |       |    |       |    |        |   |       |
// -> cmp <-    -> cmp <-    -> cmp <-    -> cmp <-
//     |            |            |            |
//     --> logic <--             |            |
//           |                   |            |
//            --------> logic <--             |
//                        |                   |
//                         -----> logic <-----

// kUnbalanced - kInvertCompare
// a       b    c       d    a        b   c       d
// |       |    |       |    |        |   |       |
// |       |    |       |    |        |   |       |
// -> cmp <-    -> cmp <-    -> cmp <-    -> cmp <-
//     |            |            |            |
//    not           |           not           |
//     |            |            |            |
//     --> logic <--             |            |
//           |                   |            |
//            --------> logic <--             |
//                        |                   |
//                         -----> logic <-----

// kUnbalanced - kInvertCompareDouble
// a       b    c       d    a        b   c       d
// |       |    |       |    |        |   |       |
// |       |    |       |    |        |   |       |
// -> cmp <-    -> cmp <-    -> cmp <-    -> cmp <-
//     |            |            |            |
//    not           |           not           |
//     |            |            |            |
//    not           |           not           |
//     |            |            |            |
//     --> logic <--             |            |
//           |                   |            |
//            --------> logic <--             |
//                        |                   |
//                         -----> logic <-----

// kUnbalanced - kInvertLogic
// a       b    c       d    a        b   c       d
// |       |    |       |    |        |   |       |
// |       |    |       |    |        |   |       |
// -> cmp <-    -> cmp <-    -> cmp <-    -> cmp <-
//     |            |            |            |
//     --> logic <--             |            |
//           |                   |            |
//          not                  |            |
//            --------> logic <--             |
//                        |                   |
//                       not                  |
//                        |                   |
//                         -----> logic <-----

// kUnbalanced - kInvertLogicDouble
// a       b    c       d    a        b   c       d
// |       |    |       |    |        |   |       |
// |       |    |       |    |        |   |       |
// -> cmp <-    -> cmp <-    -> cmp <-    -> cmp <-
//     |            |            |            |
//     --> logic <--             |            |
//           |                   |            |
//          not                  |            |
//           |                   |            |
//          not                  |            |
//            --------> logic <--             |
//                        |                   |
//                       not                  |
//                        |                   |
//                       not                  |
//                        |                   |
//                         -----> logic <-----

// kUnbalanced - kInvertCompareAndLogic
// a       b    c       d    a        b   c       d
// |       |    |       |    |        |   |       |
// |       |    |       |    |        |   |       |
// -> cmp <-    -> cmp <-    -> cmp <-    -> cmp <-
//     |            |            |            |
//    not           |           not           |
//     |            |            |            |
//     --> logic <--             |            |
//           |                   |            |
//          not                  |            |
//            --------> logic <--             |
//                        |                   |
//                       not                  |
//                        |                   |
//                         -----> logic <-----

template <uint32_t NumLogic, typename CompareType>
class CombineCompares {
  static constexpr uint32_t NumInputs = 4;
  static constexpr uint32_t NumCompares = NumLogic + 1;
  static_assert(NumLogic > 0);

  // a       b    c       d    a        b       NumInputs = 4
  // |       |    |       |    |        |
  // |       |    |       |    |        |
  // -> cmp <-    -> cmp <-    -> cmp <-        NumCompares = 3
  //     |            |            |
  //     --> logic <--             |            ---------
  //           |                   |            NumLogic = 2
  //           ------> logic <-----             ---------

 public:
  CombineCompares(RawMachineAssemblerTester<uint32_t>& m, GraphShape shape,
                  InvertPattern invert_pattern, BranchPattern branch_pattern,
                  std::array<TurboshaftBinop, NumLogic> logic_ops,
                  std::array<TurboshaftComparison, NumCompares> compare_ops)
      : m_(m),
        graph_shape_(shape),
        invert_pattern_(invert_pattern),
        branch_pattern_(branch_pattern),
        logic_ops_(logic_ops),
        compare_ops_(compare_ops) {}

  void GenerateReturn(V<Word32> combine) {
    if (branch_pattern() == kNone) {
      m().Return(combine);
    } else {
      blocka_ = m().NewBlock();
      blockb_ = m().NewBlock();
      if (branch_pattern() == kDirect) {
        m().Branch(static_cast<V<Word32>>(combine), blocka(), blockb());
      } else if (branch_pattern() == kEqualZero) {
        m().Branch(m().Word32Equal(combine, m().Word32Constant(0)), blocka(),
                   blockb());
      } else {
        auto cond = static_cast<V<Word32>>(
            MakeNot(m().Word32Equal(combine, m().Word32Constant(0))));
        m().Branch(cond, blocka(), blockb());
      }
      m().Bind(blocka());
      m().Return(m().Word32Constant(1));
      m().Bind(blockb());
      m().Return(m().Word32Constant(0));
    }
  }

  V<Word32> MakeBinop(TurboshaftBinop op, V<Word32> lhs, V<Word32> rhs) {
    switch (op) {
      case TurboshaftBinop::kWord32BitwiseAnd:
        return m().Word32BitwiseAnd(lhs, rhs);
      case TurboshaftBinop::kWord32BitwiseOr:
        return m().Word32BitwiseOr(lhs, rhs);
      default:
        UNREACHABLE();
    }
  }

  V<Word32> MakeCompare(TurboshaftComparison op, OpIndex lhs, OpIndex rhs) {
    switch (op) {
      default:
        UNREACHABLE();
      case TurboshaftComparison::kTaggedEqual:
#ifdef V8_COMPRESS_POINTERS
        return m().Word32Equal(lhs, rhs);
#else
        return m().Word64Equal(lhs, rhs);
#endif  // V8_COMPRESS_POINTERS
      case TurboshaftComparison::kWord32Equal:
        return m().Word32Equal(lhs, rhs);
      case TurboshaftComparison::kInt32LessThan:
        return m().Int32LessThan(lhs, rhs);
      case TurboshaftComparison::kInt32LessThanOrEqual:
        return m().Int32LessThanOrEqual(lhs, rhs);
      case TurboshaftComparison::kUint32LessThan:
        return m().Uint32LessThan(lhs, rhs);
      case TurboshaftComparison::kUint32LessThanOrEqual:
        return m().Uint32LessThanOrEqual(lhs, rhs);
      case TurboshaftComparison::kWord64Equal:
        return m().Word64Equal(lhs, rhs);
      case TurboshaftComparison::kInt64LessThan:
        return m().Int64LessThan(lhs, rhs);
      case TurboshaftComparison::kInt64LessThanOrEqual:
        return m().Int64LessThanOrEqual(lhs, rhs);
      case TurboshaftComparison::kUint64LessThan:
        return m().Uint64LessThan(lhs, rhs);
      case TurboshaftComparison::kUint64LessThanOrEqual:
        return m().Uint64LessThanOrEqual(lhs, rhs);
      case TurboshaftComparison::kFloat32Equal:
        return m().Float32Equal(lhs, rhs);
      case TurboshaftComparison::kFloat32LessThan:
        return m().Float32LessThan(lhs, rhs);
      case TurboshaftComparison::kFloat32LessThanOrEqual:
        return m().Float32LessThanOrEqual(lhs, rhs);
      case TurboshaftComparison::kFloat64Equal:
        return m().Float64Equal(lhs, rhs);
      case TurboshaftComparison::kFloat64LessThan:
        return m().Float64LessThan(lhs, rhs);
      case TurboshaftComparison::kFloat64LessThanOrEqual:
        return m().Float64LessThanOrEqual(lhs, rhs);
    }
  }

  V<Word32> MakeNot(V<Word32> node) {
    return m().Word32Equal(node, m().Word32Constant(0));
  }

  V<Word32> MakeNotCompare(V<Word32> node) {
    V<Word32> inverted = MakeNot(node);
    if (invert_pattern() == kInvertCompareDouble) {
      return MakeNot(inverted);
    } else {
      return inverted;
    }
  }

  V<Word32> MakeNotLogic(V<Word32> node) {
    V<Word32> inverted = MakeNot(node);
    if (invert_pattern() == kInvertLogicDouble) {
      return MakeNot(inverted);
    } else {
      return inverted;
    }
  }

  bool ShouldDoubleInvert() const {
    return invert_pattern() == kInvertLogicDouble ||
           invert_pattern() == kInvertCompareDouble;
  }

  bool ShouldInvertLogic() const {
    return invert_pattern() == kInvertLogic ||
           invert_pattern() == kInvertCompareAndLogic ||
           invert_pattern() == kInvertLogicDouble;
  }

  bool ShouldInvertCompare() const {
    return invert_pattern() == kInvertCompare ||
           invert_pattern() == kInvertCompareAndLogic ||
           invert_pattern() == kInvertCompareDouble;
  }

  void BuildGraph(std::array<OpIndex, NumInputs>& inputs) {
    std::array<V<Word32>, NumCompares> compares;

    for (unsigned i = 0; i < NumCompares; ++i) {
      OpIndex a = inputs.at((2 * i) % NumInputs);
      OpIndex b = inputs.at((2 * i + 1) % NumInputs);
      V<Word32> cmp = MakeCompare(CompareOpcode(i), a, b);
      // Invert every other compare, starting with the first.
      if (ShouldInvertCompare() && (i % 1)) {
        compares[i] = MakeNotCompare(cmp);
      } else {
        compares[i] = cmp;
      }
    }

    V<Word32> first_combine =
        MakeBinop(LogicOpcode(0), compares[0], compares[1]);
    if (NumLogic == 1) {
      if (ShouldInvertLogic()) {
        return GenerateReturn(MakeNotLogic(first_combine));
      }
      return GenerateReturn(first_combine);
    }

    if (graph_shape() == kUnbalanced) {
      V<Word32> combine = first_combine;
      for (unsigned i = 1; i < NumLogic; ++i) {
        // Invert every other logic operation, beginning with the first.
        if (ShouldInvertLogic() && (i % 1)) {
          combine = MakeNotLogic(combine);
        }
        combine = MakeBinop(LogicOpcode(i), compares.at(i + 1), combine);
      }
      return GenerateReturn(combine);
    } else {
      constexpr uint32_t NumFirstLayerLogic = NumCompares / 2;
      std::array<V<Word32>, NumFirstLayerLogic> first_layer_logic{
          first_combine};
      for (unsigned i = 1; i < NumFirstLayerLogic; ++i) {
        first_layer_logic[i] = MakeBinop(LogicOpcode(i), compares.at(2 * i),
                                         compares.at(2 * i + 1));
      }
      V<Word32> combine = first_combine;
      // Invert every other first layer logic operation, beginning with the
      // first.
      if (ShouldInvertLogic()) {
        combine = MakeNotLogic(combine);
      }
      for (unsigned i = 1; i < NumFirstLayerLogic; ++i) {
        V<Word32> logic_node = first_layer_logic.at(i);
        if (ShouldInvertLogic() && !(i % 2)) {
          logic_node = MakeNotLogic(logic_node);
        }
        uint32_t logic_idx = NumFirstLayerLogic + i - 1;
        combine = MakeBinop(LogicOpcode(logic_idx), logic_node, combine);
      }
      GenerateReturn(combine);
    }
  }

  uint32_t ExpectedReturn(uint32_t combine) const {
    if (branch_pattern() == kNone) {
      return combine;
    } else if (branch_pattern() == kDirect) {
      return combine == 0 ? 0 : 1;
    } else if (branch_pattern() == kEqualZero) {
      return combine == 0 ? 1 : 0;
    } else {
      return combine != 0 ? 1 : 0;
    }
  }

  uint32_t InvertCompare(uint32_t v) const {
    return invert_pattern() == kInvertCompareDouble ? v : !v;
  }

  uint32_t InvertLogic(uint32_t v) const {
    return invert_pattern() == kInvertLogicDouble ? v : !v;
  }

  uint32_t Expected(std::array<CompareType, NumInputs>& inputs) {
    std::array<uint32_t, NumCompares> compare_results;
    for (unsigned i = 0; i < NumCompares; ++i) {
      CompareType cmp_lhs = inputs.at((2 * i) % NumInputs);
      CompareType cmp_rhs = inputs.at((2 * i + 1) % NumInputs);
      CompareWrapper cmpw = CompareWrapper(CompareOpcode(i));
      uint32_t cmp_res = EvalCompare(cmpw, cmp_lhs, cmp_rhs);
      // Invert every other compare, starting with the first.
      if (ShouldInvertCompare() && (i % 1)) {
        compare_results[i] = InvertCompare(cmp_res);
      } else {
        compare_results[i] = cmp_res;
      }
    }

    auto logicw = IntBinopWrapper<uint32_t>(LogicOpcode(0));
    uint32_t first_combine =
        logicw.eval(compare_results[0], compare_results[1]);
    if (NumLogic == 1) {
      if (ShouldInvertLogic()) {
        first_combine = InvertLogic(first_combine);
      }
      return ExpectedReturn(first_combine);
    }

    if (graph_shape() == kUnbalanced) {
      uint32_t combine = first_combine;
      for (unsigned i = 1; i < NumLogic; ++i) {
        // Invert every other logic operation, beginning with the first.
        if (ShouldInvertLogic() && (i % 1)) {
          combine = InvertLogic(combine);
        }
        logicw = IntBinopWrapper<uint32_t>(LogicOpcode(i));
        combine = logicw.eval(compare_results.at(i + 1), combine);
      }
      return ExpectedReturn(combine);
    } else {
      constexpr uint32_t NumFirstLayerLogic = NumCompares / 2;
      std::array<uint32_t, NumFirstLayerLogic> first_layer_logic{first_combine};
      for (unsigned i = 1; i < NumFirstLayerLogic; ++i) {
        logicw = IntBinopWrapper<uint32_t>(LogicOpcode(i));
        first_layer_logic[i] = logicw.eval(compare_results.at(2 * i),
                                           compare_results.at(2 * i + 1));
      }
      uint32_t combine = first_combine;
      // Invert every other first layer logic operation, beginning with the
      // first.
      if (ShouldInvertLogic()) {
        combine = InvertLogic(combine);
      }
      for (unsigned i = 1; i < NumFirstLayerLogic; ++i) {
        uint32_t logic_res = first_layer_logic.at(i);
        if (ShouldInvertLogic() && !(i % 2)) {
          logic_res = InvertLogic(logic_res);
        }
        uint32_t logic_idx = NumFirstLayerLogic + i - 1;
        logicw = IntBinopWrapper<uint32_t>(LogicOpcode(logic_idx));
        combine = logicw.eval(logic_res, combine);
      }
      return ExpectedReturn(combine);
    }
  }

  virtual uint32_t EvalCompare(CompareWrapper& cmpw, CompareType lhs,
                               CompareType rhs) const = 0;
  virtual OpIndex Zero() const = 0;
  virtual OpIndex One() const = 0;
  virtual OpIndex ThirtyTwo() const = 0;

  RawMachineAssemblerTester<uint32_t>& m() const { return m_; }
  GraphShape graph_shape() const { return graph_shape_; }
  InvertPattern invert_pattern() const { return invert_pattern_; }
  BranchPattern branch_pattern() const { return branch_pattern_; }
  TurboshaftBinop LogicOpcode(uint32_t i) const { return logic_ops_.at(i); }
  TurboshaftComparison CompareOpcode(uint32_t i) const {
    return compare_ops_.at(i);
  }
  Block* blocka() { return blocka_; }
  Block* blockb() { return blockb_; }

 private:
  RawMachineAssemblerTester<uint32_t>& m_;
  GraphShape graph_shape_;
  InvertPattern invert_pattern_;
  BranchPattern branch_pattern_;
  Block* blocka_;
  Block* blockb_;
  std::array<TurboshaftBinop, NumLogic> logic_ops_;
  std::array<TurboshaftComparison, NumCompares> compare_ops_;
};

template <uint32_t NumLogic>
class CombineCompareWord32 : public CombineCompares<NumLogic, uint32_t> {
 public:
  using CombineCompares<NumLogic, uint32_t>::CombineCompares;
  uint32_t EvalCompare(CompareWrapper& cmpw, uint32_t lhs,
                       uint32_t rhs) const override {
    return cmpw.Int32Compare(lhs, rhs);
  }
  OpIndex Zero() const override { return this->m().Word32Constant(0); }
  OpIndex One() const override { return this->m().Word32Constant(1); }
  OpIndex ThirtyTwo() const override { return this->m().Word32Constant(32); }
};

template <uint32_t NumLogic>
class CombineCompareWord64 : public CombineCompares<NumLogic, uint64_t> {
 public:
  using CombineCompares<NumLogic, uint64_t>::CombineCompares;
  uint32_t EvalCompare(CompareWrapper& cmpw, uint64_t lhs,
                       uint64_t rhs) const override {
    return cmpw.Int64Compare(lhs, rhs);
  }
  OpIndex Zero() const override {
    return this->m().Word64Constant(static_cast<uint64_t>(0));
  }
  OpIndex One() const override {
    return this->m().Word64Constant(static_cast<uint64_t>(1));
  }
  OpIndex ThirtyTwo() const override {
    return this->m().Word64Constant(static_cast<uint64_t>(32));
  }
};

template <uint32_t NumLogic>
class CombineCompareFloat32 : public CombineCompares<NumLogic, float> {
 public:
  using CombineCompares<NumLogic, float>::CombineCompares;
  uint32_t EvalCompare(CompareWrapper& cmpw, float lhs,
                       float rhs) const override {
    return cmpw.Float32Compare(lhs, rhs);
  }
  OpIndex Zero() const override { return this->m().Float32Constant(0.0); }
  OpIndex One() const override { return this->m().Float32Constant(1.0); }
  OpIndex ThirtyTwo() const override { return this->m().Float32Constant(32.0); }
};

template <uint32_t NumLogic>
class CombineCompareFloat64 : public CombineCompares<NumLogic, double> {
 public:
  using CombineCompares<NumLogic, double>::CombineCompares;
  uint32_t EvalCompare(CompareWrapper& cmpw, double lhs,
                       double rhs) const override {
    return cmpw.Float64Compare(lhs, rhs);
  }
  OpIndex Zero() const override { return this->m().Float64Constant(0.0); }
  OpIndex One() const override { return this->m().Float64Constant(1.0); }
  OpIndex ThirtyTwo() const override { return this->m().Float64Constant(32.0); }
};

template <typename Combiner, typename InputType, size_t NumCmps>
void CombineCompareLogic1(
    const std::array<TurboshaftComparison, NumCmps>& cmp_opcodes,
    MachineType (*input_type)(void),
    const base::Vector<const InputType>& input_vector) {
  constexpr GraphShape shape = kBalanced;
  for (auto cmp0 : cmp_opcodes) {
    for (auto cmp1 : cmp_opcodes) {
      for (auto logic : kLogicOpcodes) {
        for (auto invert_pattern : kInvertPatterns) {
          for (auto branch_pattern : kBranchPatterns) {
            RawMachineAssemblerTester<uint32_t> m(input_type(), input_type(),
                                                  input_type(), input_type());
            std::array logic_ops = {logic};
            std::array compare_ops = {cmp0, cmp1};
            Combiner gen(m, shape, invert_pattern, branch_pattern, logic_ops,
                         compare_ops);
            std::array inputs = {
                m.Parameter(0),
                m.Parameter(1),
                m.Parameter(2),
                m.Parameter(3),
            };
            gen.BuildGraph(inputs);

            for (auto a : input_vector) {
              for (auto b : input_vector) {
                std::array<InputType, 4> call_inputs{a, b, b, a};
                uint32_t expected = gen.Expected(call_inputs);
                uint32_t actual = m.Call(a, b, b, a);
                CHECK_EQ(expected, actual);
              }
            }
          }
        }
      }
    }
  }
}
TEST(CombineCompareWord32Logic1) {
  CombineCompareLogic1<CombineCompareWord32<1>, uint32_t, kNumInt32Cmps>(
      kInt32CmpOpcodes, MachineType::Uint32, uint32_test_vector);
}
#ifdef V8_ENABLE_FP_PARAMS_IN_C_LINKAGE
TEST(CombineCompareFloat32Logic1) {
  CombineCompareLogic1<CombineCompareFloat32<1>, float, 3>(
      kFloat32CmpOpcodes, MachineType::Float32, float32_test_vector);
}
#endif  // V8_ENABLE_FP_PARAMS_IN_C_LINKAGE
#if V8_TARGET_ARCH_64_BIT
TEST(CombineCompareWord64Logic1) {
  CombineCompareLogic1<CombineCompareWord64<1>, uint64_t, kNumInt64Cmps>(
      kInt64CmpOpcodes, MachineType::Uint64, uint64_test_vector);
}
#ifdef V8_ENABLE_FP_PARAMS_IN_C_LINKAGE
TEST(CombineCompareFloat64Logic1) {
  CombineCompareLogic1<CombineCompareFloat64<1>, double, 3>(
      kFloat64CmpOpcodes, MachineType::Float64, float64_test_vector);
}
#endif  // V8_ENABLE_FP_PARAMS_IN_C_LINKAGE
#endif  // V8_TARGET_ARCH_64_BIT

template <typename Combiner, typename InputType, size_t NumCmps>
void CombineCompareLogic2(
    const std::array<TurboshaftComparison, NumCmps>& cmp_opcodes,
    MachineType (*input_type)(void),
    const base::Vector<const InputType>& input_vector) {
  constexpr GraphShape shape = kUnbalanced;
  constexpr BranchPattern branch_pattern = kNone;
  auto cmp0 = cmp_opcodes[2];
  auto cmp1 = cmp_opcodes[1];
  auto cmp2 = cmp_opcodes[0];
  std::array compare_ops = {cmp0, cmp1, cmp2};
  for (auto logic0 : kLogicOpcodes) {
    for (auto logic1 : kLogicOpcodes) {
      for (auto invert_pattern : kInvertPatterns) {
        RawMachineAssemblerTester<uint32_t> m(input_type(), input_type(),
                                              input_type(), input_type());
        std::array logic_ops = {logic0, logic1};
        Combiner gen(m, shape, invert_pattern, branch_pattern, logic_ops,
                     compare_ops);
        std::array inputs = {
            m.Parameter(0),
            m.Parameter(1),
            m.Parameter(2),
            m.Parameter(3),
        };
        gen.BuildGraph(inputs);

        for (auto a : input_vector) {
          for (auto b : input_vector) {
            std::array<InputType, 4> call_inputs{a, b, b, a};
            uint32_t expected = gen.Expected(call_inputs);
            uint32_t actual = m.Call(a, b, b, a);
            CHECK_EQ(expected, actual);
          }
        }
      }
    }
  }
}
TEST(CombineCompareWord32Logic2) {
  CombineCompareLogic2<CombineCompareWord32<2>, uint32_t, kNumInt32Cmps>(
      kInt32CmpOpcodes, MachineType::Uint32, uint32_test_vector);
}
#ifdef V8_ENABLE_FP_PARAMS_IN_C_LINKAGE
TEST(CombineCompareFloat32Logic2) {
  CombineCompareLogic2<CombineCompareFloat32<2>, float, 3>(
      kFloat32CmpOpcodes, MachineType::Float32, float32_test_vector);
}
#endif  // V8_ENABLE_FP_PARAMS_IN_C_LINKAGE
#if V8_TARGET_ARCH_64_BIT
TEST(CombineCompareWord64Logic2) {
  CombineCompareLogic2<CombineCompareWord64<2>, uint64_t, kNumInt64Cmps>(
      kInt64CmpOpcodes, MachineType::Uint64, uint64_test_vector);
}
#ifdef V8_ENABLE_FP_PARAMS_IN_C_LINKAGE
TEST(CombineCompareFloat64Logic2) {
  CombineCompareLogic2<CombineCompareFloat64<2>, double, 3>(
      kFloat64CmpOpcodes, MachineType::Float64, float64_test_vector);
}
#endif  // V8_ENABLE_FP_PARAMS_IN_C_LINKAGE
#endif  // V8_TARGET_ARCH_64_BIT

template <typename Combiner, typename InputType, size_t NumCmps>
void CombineCompareLogic3Zero(
    const std::array<TurboshaftComparison, NumCmps>& cmp_opcodes,
    MachineType (*input_type)(void),
    const base::Vector<const InputType>& input_vector) {
  constexpr BranchPattern branch_pattern = kNone;
  auto cmp0 = cmp_opcodes[0];
  auto cmp1 = cmp_opcodes[1];
  auto cmp2 = cmp_opcodes[2];
  auto cmp3 = cmp_opcodes[3];
  std::array compare_ops = {cmp0, cmp1, cmp2, cmp3};
  for (auto logic0 : kLogicOpcodes) {
    for (auto logic1 : kLogicOpcodes) {
      for (auto logic2 : kLogicOpcodes) {
        for (auto shape : kGraphShapes) {
          for (auto invert_pattern : kInvertPatterns) {
            RawMachineAssemblerTester<uint32_t> m(input_type(), input_type(),
                                                  input_type(), input_type());
            std::array logic_ops = {logic0, logic1, logic2};
            Combiner gen(m, shape, invert_pattern, branch_pattern, logic_ops,
                         compare_ops);
            std::array inputs = {
                m.Parameter(0),
                m.Parameter(1),
                gen.Zero(),
                m.Parameter(3),
            };
            gen.BuildGraph(inputs);

            for (auto a : input_vector) {
              for (auto b : input_vector) {
                std::array<InputType, 4> call_inputs{a, b, 0, a};
                uint32_t expected = gen.Expected(call_inputs);
                uint32_t actual = m.Call(a, b, b, a);
                CHECK_EQ(expected, actual);
              }
            }
          }
        }
      }
    }
  }
}
TEST(CombineCompareWord32Logic3Zero) {
  CombineCompareLogic3Zero<CombineCompareWord32<3>, uint32_t, kNumInt32Cmps>(
      kInt32CmpOpcodes, MachineType::Uint32, uint32_test_vector);
}
#if V8_TARGET_ARCH_64_BIT
TEST(CombineCompareWord64Logic3Zero) {
  CombineCompareLogic3Zero<CombineCompareWord64<3>, uint64_t, kNumInt64Cmps>(
      kInt64CmpOpcodes, MachineType::Uint64, uint64_test_vector);
}
#endif

template <typename Combiner, typename InputType, size_t NumCmps>
void CombineCompareLogic3One(
    const std::array<TurboshaftComparison, NumCmps>& cmp_opcodes,
    MachineType (*input_type)(void),
    const base::Vector<const InputType>& input_vector) {
  constexpr BranchPattern branch_pattern = kNone;
  auto cmp0 = cmp_opcodes[4];
  auto cmp1 = cmp_opcodes[1];
  auto cmp2 = cmp_opcodes[2];
  auto cmp3 = cmp_opcodes[0];
  std::array compare_ops = {cmp0, cmp1, cmp2, cmp3};
  for (auto logic0 : kLogicOpcodes) {
    for (auto logic1 : kLogicOpcodes) {
      for (auto logic2 : kLogicOpcodes) {
        for (auto shape : kGraphShapes) {
          for (auto invert_pattern : kInvertPatterns) {
            RawMachineAssemblerTester<uint32_t> m(input_type(), input_type(),
                                                  input_type(), input_type());
            std::array logic_ops = {logic0, logic1, logic2};
            Combiner gen(m, shape, invert_pattern, branch_pattern, logic_ops,
                         compare_ops);
            std::array inputs = {
                gen.One(),
                m.Parameter(1),
                m.Parameter(2),
                m.Parameter(3),
            };
            gen.BuildGraph(inputs);

            for (auto a : input_vector) {
              for (auto b : input_vector) {
                std::array<InputType, 4> call_inputs{1, b, b, a};
                uint32_t expected = gen.Expected(call_inputs);
                uint32_t actual = m.Call(a, b, b, a);
                CHECK_EQ(expected, actual);
              }
            }
          }
        }
      }
    }
  }
}
TEST(CombineCompareWord32Logic3One) {
  CombineCompareLogic3One<CombineCompareWord32<3>, uint32_t, kNumInt32Cmps>(
      kInt32CmpOpcodes, MachineType::Uint32, uint32_test_vector);
}
#if V8_TARGET_ARCH_64_BIT
TEST(CombineCompareWord64Logic3One) {
  CombineCompareLogic3One<CombineCompareWord64<3>, uint64_t, kNumInt64Cmps>(
      kInt64CmpOpcodes, MachineType::Uint64, uint64_test_vector);
}
#endif

template <typename Combiner, typename InputType, size_t NumCmps>
void CombineCompareLogic3ThirtyTwo(
    const std::array<TurboshaftComparison, NumCmps>& cmp_opcodes,
    MachineType (*input_type)(void),
    const base::Vector<const InputType>& input_vector) {
  constexpr BranchPattern branch_pattern = kNone;
  auto cmp0 = cmp_opcodes[0];
  auto cmp1 = cmp_opcodes[3];
  auto cmp2 = cmp_opcodes[2];
  auto cmp3 = cmp_opcodes[4];
  std::array compare_ops = {cmp0, cmp1, cmp2, cmp3};
  for (auto logic0 : kLogicOpcodes) {
    for (auto logic1 : kLogicOpcodes) {
      for (auto logic2 : kLogicOpcodes) {
        for (auto shape : kGraphShapes) {
          for (auto invert_pattern : kInvertPatterns) {
            RawMachineAssemblerTester<uint32_t> m(input_type(), input_type(),
                                                  input_type(), input_type());
            std::array logic_ops = {logic0, logic1, logic2};
            Combiner gen(m, shape, invert_pattern, branch_pattern, logic_ops,
                         compare_ops);
            std::array inputs = {
                m.Parameter(0),
                gen.ThirtyTwo(),
                m.Parameter(2),
                m.Parameter(3),
            };
            gen.BuildGraph(inputs);

            for (auto a : input_vector) {
              for (auto b : input_vector) {
                std::array<InputType, 4> call_inputs{a, 32, b, a};
                uint32_t expected = gen.Expected(call_inputs);
                uint32_t actual = m.Call(a, b, b, a);
                CHECK_EQ(expected, actual);
              }
            }
          }
        }
      }
    }
  }
}
TEST(CombineCompareWord32Logic3ThirtyTwo) {
  CombineCompareLogic3ThirtyTwo<CombineCompareWord32<3>, uint32_t,
                                kNumInt32Cmps>(
      kInt32CmpOpcodes, MachineType::Uint32, uint32_test_vector);
}
#if V8_TARGET_ARCH_64_BIT
TEST(CombineCompareWord64Logic3ThirtyTwo) {
  CombineCompareLogic3ThirtyTwo<CombineCompareWord64<3>, uint64_t,
                                kNumInt64Cmps>(
      kInt64CmpOpcodes, MachineType::Uint64, uint64_test_vector);
}
#endif

constexpr uint32_t kMaxDepth = 4;
// a       b    b       a    a        b   b       a   a       b
// |       |    |       |    |        |   |       |   |       |
// |       |    |       |    |        |   |       |   |       |
// -> cmp <-    -> cmp <-    -> cmp <-    -> cmp <-   -> cmp <-
//     |            |            |            |           |
//     ---> and <---             |            |           |
//           |                   |            |           |
//            ---------> or <----             |           |
//                        |                   |           |
//                         ------> and <------            |
//                                  |                     |
//                                  --------> or <--------
TEST(CombineCompareMaxDepth) {
  constexpr GraphShape shape = kUnbalanced;
  constexpr BranchPattern branch_pattern = kNone;
  std::array logic_ops = {
      TurboshaftBinop::kWord32BitwiseAnd, TurboshaftBinop::kWord32BitwiseOr,
      TurboshaftBinop::kWord32BitwiseAnd, TurboshaftBinop::kWord32BitwiseOr};
  std::array compare_ops = {TurboshaftComparison::kWord32Equal,
                            TurboshaftComparison::kInt32LessThan,
                            TurboshaftComparison::kInt32LessThanOrEqual,
                            TurboshaftComparison::kUint32LessThan,
                            TurboshaftComparison::kUint32LessThanOrEqual};
  for (auto invert_pattern : kInvertPatterns) {
    RawMachineAssemblerTester<uint32_t> m(
        MachineType::Uint32(), MachineType::Uint32(), MachineType::Uint32(),
        MachineType::Uint32());
    CombineCompareWord32<kMaxDepth> gen(m, shape, invert_pattern,
                                        branch_pattern, logic_ops, compare_ops);
    std::array inputs = {
        m.Parameter(0),
        m.Parameter(1),
        m.Parameter(2),
        m.Parameter(3),
    };
    gen.BuildGraph(inputs);

    FOR_UINT32_INPUTS(a) {
      FOR_UINT32_INPUTS(b) {
        std::array call_inputs{a, b, b, a};
        uint32_t expected = gen.Expected(call_inputs);
        uint32_t actual = m.Call(a, b, b, a);
        CHECK_EQ(expected, actual);
      }
    }
  }
}

TEST(CombineCompareBranchesMaxDepth) {
  constexpr GraphShape shape = kUnbalanced;
  std::array logic_ops = {
      TurboshaftBinop::kWord32BitwiseAnd, TurboshaftBinop::kWord32BitwiseOr,
      TurboshaftBinop::kWord32BitwiseAnd, TurboshaftBinop::kWord32BitwiseOr};
  std::array compare_ops = {TurboshaftComparison::kWord32Equal,
                            TurboshaftComparison::kInt32LessThan,
                            TurboshaftComparison::kInt32LessThanOrEqual,
                            TurboshaftComparison::kUint32LessThan,
                            TurboshaftComparison::kUint32LessThanOrEqual};
  for (auto branch_pattern : kBranchPatterns) {
    for (auto invert_pattern : kInvertPatterns) {
      RawMachineAssemblerTester<uint32_t> m(
          MachineType::Uint32(), MachineType::Uint32(), MachineType::Uint32(),
          MachineType::Uint32());
      CombineCompareWord32<kMaxDepth> gen(
          m, shape, invert_pattern, branch_pattern, logic_ops, compare_ops);
      std::array inputs = {
          m.Parameter(0),
          m.Parameter(1),
          m.Parameter(2),
          m.Parameter(3),
      };
      gen.BuildGraph(inputs);

      FOR_UINT32_INPUTS(a) {
        FOR_UINT32_INPUTS(b) {
          std::array call_inputs{a, b, b, a};
          uint32_t expected = gen.Expected(call_inputs);
          uint32_t actual = m.Call(a, b, b, a);
          CHECK_EQ(expected, actual);
        }
      }
    }
  }
}

TEST(CombineCompareMaxDepthPlusOne) {
  std::array logic_ops = {
      TurboshaftBinop::kWord32BitwiseAnd, TurboshaftBinop::kWord32BitwiseOr,
      TurboshaftBinop::kWord32BitwiseAnd, TurboshaftBinop::kWord32BitwiseOr,
      TurboshaftBinop::kWord32BitwiseAnd};
  std::array compare_ops = {
      TurboshaftComparison::kWord32Equal,
      TurboshaftComparison::kInt32LessThan,
      TurboshaftComparison::kInt32LessThanOrEqual,
      TurboshaftComparison::kUint32LessThan,
      TurboshaftComparison::kUint32LessThanOrEqual,
      TurboshaftComparison::kWord32Equal,
  };
  constexpr BranchPattern branch_pattern = kNone;
  for (auto shape : kGraphShapes) {
    for (auto invert_pattern : kInvertPatterns) {
      RawMachineAssemblerTester<uint32_t> m(
          MachineType::Uint32(), MachineType::Uint32(), MachineType::Uint32(),
          MachineType::Uint32());
      CombineCompareWord32<kMaxDepth + 1> gen(
          m, shape, invert_pattern, branch_pattern, logic_ops, compare_ops);
      std::array inputs = {
          m.Parameter(0),
          m.Parameter(1),
          m.Parameter(2),
          m.Parameter(3),
      };
      gen.BuildGraph(inputs);

      FOR_UINT32_INPUTS(a) {
        FOR_UINT32_INPUTS(b) {
          std::array call_inputs{a, b, b, a};
          uint32_t expected = gen.Expected(call_inputs);
          uint32_t actual = m.Call(a, b, b, a);
          CHECK_EQ(expected, actual);
        }
      }
    }
  }
}

TEST(CombineCompareTwoLogicInputs) {
  // cmp cmp cmp cmp cmp cmp
  //  |   |   |   |   |   |
  //  logic   logic   logic
  //    |       |       |
  //     - cmp -        |
  //        |           |
  //         -- logic --
  auto run = [](uint32_t a, uint32_t b, uint32_t c, uint32_t d) {
    bool cmp1 = static_cast<int32_t>(a) < static_cast<int32_t>(b);
    bool cmp2 = static_cast<int32_t>(a) <= 1024;
    bool cmp3 = static_cast<int32_t>(c) < static_cast<int32_t>(d);
    bool cmp4 = static_cast<int32_t>(c) < 4096;
    bool cmp5 = a < d;
    bool cmp6 = b <= c;
    bool logic1 = cmp1 && cmp2;
    bool logic2 = cmp3 || cmp4;
    bool logic3 = cmp5 && cmp6;
    bool cmp7 = logic1 == logic2;
    return static_cast<uint32_t>(cmp7 || logic3);
  };

  RawMachineAssemblerTester<uint32_t> m(
      MachineType::Uint32(), MachineType::Uint32(), MachineType::Uint32(),
      MachineType::Uint32());

  V<Word32> cmp1 = m.Int32LessThan(m.Parameter(0), m.Parameter(1));
  V<Word32> cmp2 =
      m.Int32LessThanOrEqual(m.Parameter(0), m.Word32Constant(1024));
  V<Word32> cmp3 = m.Int32LessThan(m.Parameter(2), m.Parameter(3));
  V<Word32> cmp4 =
      m.Int32LessThanOrEqual(m.Parameter(2), m.Word32Constant(4096));
  V<Word32> cmp5 = m.Uint32LessThan(m.Parameter(0), m.Parameter(3));
  V<Word32> cmp6 = m.Uint32LessThanOrEqual(m.Parameter(1), m.Parameter(2));

  V<Word32> logic1 = m.Word32BitwiseAnd(cmp1, cmp2);
  V<Word32> logic2 = m.Word32BitwiseOr(cmp3, cmp4);
  V<Word32> logic3 = m.Word32BitwiseAnd(cmp5, cmp6);

  V<Word32> cmp7 = m.Word32Equal(logic1, logic2);

  m.Return(m.Word32BitwiseOr(cmp7, logic3));

  for (uint32_t a : uint32_test_vector) {
    for (uint32_t b : uint32_test_vector) {
      for (uint32_t c : uint32_test_vector) {
        for (uint32_t d : uint32_test_vector) {
          uint32_t result = m.Call(a, b, c, d);
          uint32_t expected = run(a, b, c, d);
          CHECK_EQ(result, expected);
        }
      }
    }
  }
}

#if (V8_TARGET_ARCH_64_BIT && V8_ENABLE_FP_PARAMS_IN_C_LINKAGE)
TEST(CombineCompareFloatAndIntMaxDepth) {
  auto run = [](uint32_t a, uint32_t b, float c, double d) {
    bool fcmp0 = 0.0f <= c;
    bool icmp0 = static_cast<int32_t>(b) >= static_cast<int32_t>(a);
    bool and0 = fcmp0 || icmp0;
    bool fcmp1 = c < -1.0f;
    bool or0 = and0 || fcmp1;
    bool icmp1 = b > a;
    bool and1 = or0 && icmp1;
    bool fcmp2 = 1.0 == d;
    return and1 || fcmp2;
  };

  RawMachineAssemblerTester<uint32_t> m(
      MachineType::Uint32(), MachineType::Uint32(), MachineType::Float32(),
      MachineType::Float64());
  {
    V<Word32> a = m.Parameter(0);
    V<Word32> b = m.Parameter(1);
    V<Float32> c = m.Parameter(2);
    V<Float64> d = m.Parameter(3);
    V<Word32> fcmp0 = m.Float32LessThanOrEqual(m.Float32Constant(0.0), c);
    V<Word32> icmp0 = m.Int32GreaterThanOrEqual(b, a);
    V<Word32> and0 = m.Word32BitwiseOr(fcmp0, icmp0);
    V<Word32> fcmp1 = m.Float32LessThan(c, m.Float32Constant(-1.0));
    V<Word32> or0 = m.Word32BitwiseOr(and0, fcmp1);
    V<Word32> icmp1 = m.Uint32GreaterThan(b, a);
    V<Word32> and1 = m.Word32BitwiseAnd(or0, icmp1);
    V<Word32> fcmp2 = m.Float64Equal(m.Float64Constant(1.0), d);
    m.Return(m.Word32BitwiseOr(and1, fcmp2));
  }

  for (uint32_t a : uint32_test_vector) {
    for (uint32_t b : uint32_test_vector) {
      for (float c : float32_test_vector) {
        for (double d : float64_test_vector) {
          uint32_t result = m.Call(a, b, c, d);
          uint32_t expected = run(a, b, c, d);
          CHECK_EQ(result, expected);
        }
      }
    }
  }
}
#endif  // (V8_TARGET_ARCH_64_BIT && V8_ENABLE_FP_PARAMS_IN_C_LINKAGE)

}  // end namespace

}  // namespace v8::internal::compiler::turboshaft
