// Copyright 2014 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/codegen/external-reference.h"
#include "src/objects/objects-inl.h"
#include "test/cctest/cctest.h"
#include "test/cctest/compiler/codegen-tester.h"
#include "test/cctest/compiler/value-helper.h"

#if V8_ENABLE_WEBASSEMBLY
#include "src/wasm/wasm-external-refs.h"
#endif  // V8_ENABLE_WEBASSEMBLY

namespace v8 {
namespace internal {
namespace compiler {

template <typename InType, typename OutType, typename Iterable>
void TestExternalReference_ConvertOp(
    BufferedRawMachineAssemblerTester<int32_t>* m, ExternalReference ref,
    void (*wrapper)(Address), Iterable inputs) {
  constexpr size_t kBufferSize = std::max(sizeof(InType), sizeof(OutType));
  uint8_t buffer[kBufferSize] = {0};
  Address buffer_addr = reinterpret_cast<Address>(buffer);

  Node* function = m->ExternalConstant(ref);
  m->CallCFunction(
      function, MachineType::Pointer(),
      std::make_pair(MachineType::Pointer(), m->PointerConstant(buffer)));
  m->Return(m->Int32Constant(4356));

  for (InType input : inputs) {
    WriteUnalignedValue<InType>(buffer_addr, input);

    CHECK_EQ(4356, m->Call());
    OutType output = ReadUnalignedValue<OutType>(buffer_addr);

    WriteUnalignedValue<InType>(buffer_addr, input);
    wrapper(buffer_addr);
    OutType expected_output = ReadUnalignedValue<OutType>(buffer_addr);

    CHECK_EQ(expected_output, output);
  }
}

template <typename InType, typename OutType, typename Iterable>
void TestExternalReference_ConvertOpWithOutputAndReturn(
    BufferedRawMachineAssemblerTester<int32_t>* m, ExternalReference ref,
    int32_t (*wrapper)(Address), Iterable inputs) {
  constexpr size_t kBufferSize = std::max(sizeof(InType), sizeof(OutType));
  uint8_t buffer[kBufferSize] = {0};
  Address buffer_addr = reinterpret_cast<Address>(buffer);

  Node* function = m->ExternalConstant(ref);
  m->Return(m->CallCFunction(
      function, MachineType::Int32(),
      std::make_pair(MachineType::Pointer(), m->PointerConstant(buffer))));

  for (InType input : inputs) {
    WriteUnalignedValue<InType>(buffer_addr, input);

    int32_t ret = m->Call();
    OutType output = ReadUnalignedValue<OutType>(buffer_addr);

    WriteUnalignedValue<InType>(buffer_addr, input);
    int32_t expected_ret = wrapper(buffer_addr);
    OutType expected_output = ReadUnalignedValue<OutType>(buffer_addr);

    CHECK_EQ(expected_ret, ret);
    CHECK_EQ(expected_output, output);
  }
}

template <typename InType, typename OutType, typename Iterable>
void TestExternalReference_ConvertOpWithReturn(
    BufferedRawMachineAssemblerTester<OutType>* m, ExternalReference ref,
    OutType (*wrapper)(Address), Iterable inputs) {
  constexpr size_t kBufferSize = sizeof(InType);
  uint8_t buffer[kBufferSize] = {0};
  Address buffer_addr = reinterpret_cast<Address>(buffer);

  Node* function = m->ExternalConstant(ref);
  m->Return(m->CallCFunction(
      function, MachineType::Int32(),
      std::make_pair(MachineType::Pointer(), m->PointerConstant(buffer))));

  for (InType input : inputs) {
    WriteUnalignedValue<InType>(buffer_addr, input);

    OutType ret = m->Call();

    WriteUnalignedValue<InType>(buffer_addr, input);
    OutType expected_ret = wrapper(buffer_addr);

    CHECK_EQ(expected_ret, ret);
  }
}

template <typename Type>
bool isnan(Type value) {
  return false;
}
template <>
bool isnan<float>(float value) {
  return std::isnan(value);
}
template <>
bool isnan<double>(double value) {
  return std::isnan(value);
}

template <typename Type, typename Iterable>
void TestExternalReference_UnOp(BufferedRawMachineAssemblerTester<int32_t>* m,
                                ExternalReference ref, void (*wrapper)(Address),
                                Iterable inputs) {
  constexpr size_t kBufferSize = sizeof(Type);
  uint8_t buffer[kBufferSize] = {0};
  Address buffer_addr = reinterpret_cast<Address>(buffer);

  Node* function = m->ExternalConstant(ref);
  m->CallCFunction(
      function, MachineType::Int32(),
      std::make_pair(MachineType::Pointer(), m->PointerConstant(buffer)));
  m->Return(m->Int32Constant(4356));

  for (Type input : inputs) {
    WriteUnalignedValue<Type>(buffer_addr, input);
    CHECK_EQ(4356, m->Call());
    Type output = ReadUnalignedValue<Type>(buffer_addr);

    WriteUnalignedValue<Type>(buffer_addr, input);
    wrapper(buffer_addr);
    Type expected_output = ReadUnalignedValue<Type>(buffer_addr);

    if (isnan(expected_output) && isnan(output)) continue;
    CHECK_EQ(expected_output, output);
  }
}

template <typename Type, typename Iterable>
void TestExternalReference_BinOp(BufferedRawMachineAssemblerTester<int32_t>* m,
                                 ExternalReference ref,
                                 void (*wrapper)(Address), Iterable inputs) {
  constexpr size_t kBufferSize = 2 * sizeof(Type);
  uint8_t buffer[kBufferSize] = {0};
  Address buffer_addr = reinterpret_cast<Address>(buffer);

  Node* function = m->ExternalConstant(ref);
  m->CallCFunction(
      function, MachineType::Int32(),
      std::make_pair(MachineType::Pointer(), m->PointerConstant(buffer)));
  m->Return(m->Int32Constant(4356));

  for (Type input1 : inputs) {
    for (Type input2 : inputs) {
      WriteUnalignedValue<Type>(buffer_addr, input1);
      WriteUnalignedValue<Type>(buffer_addr + sizeof(Type), input2);
      CHECK_EQ(4356, m->Call());
      Type output = ReadUnalignedValue<Type>(buffer_addr);

      WriteUnalignedValue<Type>(buffer_addr, input1);
      WriteUnalignedValue<Type>(buffer_addr + sizeof(Type), input2);
      wrapper(buffer_addr);
      Type expected_output = ReadUnalignedValue<Type>(buffer_addr);

      if (isnan(expected_output) && isnan(output)) continue;
      CHECK_EQ(expected_output, output);
    }
  }
}

template <typename Type, typename Iterable>
void TestExternalReference_BinOpWithReturn(
    BufferedRawMachineAssemblerTester<int32_t>* m, ExternalReference ref,
    int32_t (*wrapper)(Address), Iterable inputs) {
  constexpr size_t kBufferSize = 2 * sizeof(Type);
  uint8_t buffer[kBufferSize] = {0};
  Address buffer_addr = reinterpret_cast<Address>(buffer);

  Node* function = m->ExternalConstant(ref);
  m->Return(m->CallCFunction(
      function, MachineType::Int32(),
      std::make_pair(MachineType::Pointer(), m->PointerConstant(buffer))));

  for (Type input1 : inputs) {
    for (Type input2 : inputs) {
      WriteUnalignedValue<Type>(buffer_addr, input1);
      WriteUnalignedValue<Type>(buffer_addr + sizeof(Type), input2);
      int32_t ret = m->Call();
      Type output = ReadUnalignedValue<Type>(buffer_addr);

      WriteUnalignedValue<Type>(buffer_addr, input1);
      WriteUnalignedValue<Type>(buffer_addr + sizeof(Type), input2);
      int32_t expected_ret = wrapper(buffer_addr);
      Type expected_output = ReadUnalignedValue<Type>(buffer_addr);

      CHECK_EQ(expected_ret, ret);
      if (isnan(expected_output) && isnan(output)) continue;
      CHECK_EQ(expected_output, output);
    }
  }
}

#if V8_ENABLE_WEBASSEMBLY
TEST(RunCallF32Trunc) {
  BufferedRawMachineAssemblerTester<int32_t> m;
  ExternalReference ref = ExternalReference::wasm_f32_trunc();
  TestExternalReference_UnOp<float>(&m, ref, wasm::f32_trunc_wrapper,
                                    ValueHelper::float32_vector());
}

TEST(RunCallF32Floor) {
  BufferedRawMachineAssemblerTester<int32_t> m;
  ExternalReference ref = ExternalReference::wasm_f32_floor();
  TestExternalReference_UnOp<float>(&m, ref, wasm::f32_floor_wrapper,
                                    ValueHelper::float32_vector());
}

TEST(RunCallF32Ceil) {
  BufferedRawMachineAssemblerTester<int32_t> m;
  ExternalReference ref = ExternalReference::wasm_f32_ceil();
  TestExternalReference_UnOp<float>(&m, ref, wasm::f32_ceil_wrapper,
                                    ValueHelper::float32_vector());
}

TEST(RunCallF32RoundTiesEven) {
  BufferedRawMachineAssemblerTester<int32_t> m;
  ExternalReference ref = ExternalReference::wasm_f32_nearest_int();
  TestExternalReference_UnOp<float>(&m, ref, wasm::f32_nearest_int_wrapper,
                                    ValueHelper::float32_vector());
}

TEST(RunCallF64Trunc) {
  BufferedRawMachineAssemblerTester<int32_t> m;
  ExternalReference ref = ExternalReference::wasm_f64_trunc();
  TestExternalReference_UnOp<double>(&m, ref, wasm::f64_trunc_wrapper,
                                     ValueHelper::float64_vector());
}

TEST(RunCallF64Floor) {
  BufferedRawMachineAssemblerTester<int32_t> m;
  ExternalReference ref = ExternalReference::wasm_f64_floor();
  TestExternalReference_UnOp<double>(&m, ref, wasm::f64_floor_wrapper,
                                     ValueHelper::float64_vector());
}

TEST(RunCallF64Ceil) {
  BufferedRawMachineAssemblerTester<int32_t> m;
  ExternalReference ref = ExternalReference::wasm_f64_ceil();
  TestExternalReference_UnOp<double>(&m, ref, wasm::f64_ceil_wrapper,
                                     ValueHelper::float64_vector());
}

TEST(RunCallF64RoundTiesEven) {
  BufferedRawMachineAssemblerTester<int32_t> m;
  ExternalReference ref = ExternalReference::wasm_f64_nearest_int();
  TestExternalReference_UnOp<double>(&m, ref, wasm::f64_nearest_int_wrapper,
                                     ValueHelper::float64_vector());
}

TEST(RunCallInt64ToFloat32) {
  BufferedRawMachineAssemblerTester<int32_t> m;
  ExternalReference ref = ExternalReference::wasm_int64_to_float32();
  TestExternalReference_ConvertOp<int64_t, float>(
      &m, ref, wasm::int64_to_float32_wrapper, ValueHelper::int64_vector());
}

TEST(RunCallUint64ToFloat32) {
  BufferedRawMachineAssemblerTester<int32_t> m;
  ExternalReference ref = ExternalReference::wasm_uint64_to_float32();
  TestExternalReference_ConvertOp<uint64_t, float>(
      &m, ref, wasm::uint64_to_float32_wrapper, ValueHelper::uint64_vector());
}

TEST(RunCallInt64ToFloat64) {
  BufferedRawMachineAssemblerTester<int32_t> m;
  ExternalReference ref = ExternalReference::wasm_int64_to_float64();
  TestExternalReference_ConvertOp<int64_t, double>(
      &m, ref, wasm::int64_to_float64_wrapper, ValueHelper::int64_vector());
}

TEST(RunCallUint64ToFloat64) {
  BufferedRawMachineAssemblerTester<int32_t> m;
  ExternalReference ref = ExternalReference::wasm_uint64_to_float64();
  TestExternalReference_ConvertOp<uint64_t, double>(
      &m, ref, wasm::uint64_to_float64_wrapper, ValueHelper::uint64_vector());
}

TEST(RunCallFloat32ToInt64) {
  BufferedRawMachineAssemblerTester<int32_t> m;
  ExternalReference ref = ExternalReference::wasm_float32_to_int64();
  TestExternalReference_ConvertOpWithOutputAndReturn<float, int64_t>(
      &m, ref, wasm::float32_to_int64_wrapper, ValueHelper::float32_vector());
}

TEST(RunCallFloat32ToUint64) {
  BufferedRawMachineAssemblerTester<int32_t> m;
  ExternalReference ref = ExternalReference::wasm_float32_to_uint64();
  TestExternalReference_ConvertOpWithOutputAndReturn<float, uint64_t>(
      &m, ref, wasm::float32_to_uint64_wrapper, ValueHelper::float32_vector());
}

TEST(RunCallFloat64ToInt64) {
  BufferedRawMachineAssemblerTester<int32_t> m;
  ExternalReference ref = ExternalReference::wasm_float64_to_int64();
  TestExternalReference_ConvertOpWithOutputAndReturn<double, int64_t>(
      &m, ref, wasm::float64_to_int64_wrapper, ValueHelper::float64_vector());
}

TEST(RunCallFloat64ToUint64) {
  BufferedRawMachineAssemblerTester<int32_t> m;
  ExternalReference ref = ExternalReference::wasm_float64_to_uint64();
  TestExternalReference_ConvertOpWithOutputAndReturn<double, uint64_t>(
      &m, ref, wasm::float64_to_uint64_wrapper, ValueHelper::float64_vector());
}

TEST(RunCallInt64Div) {
  BufferedRawMachineAssemblerTester<int32_t> m;
  ExternalReference ref = ExternalReference::wasm_int64_div();
  TestExternalReference_BinOpWithReturn<int64_t>(
      &m, ref, wasm::int64_div_wrapper, ValueHelper::int64_vector());
}

TEST(RunCallInt64Mod) {
  BufferedRawMachineAssemblerTester<int32_t> m;
  ExternalReference ref = ExternalReference::wasm_int64_mod();
  TestExternalReference_BinOpWithReturn<int64_t>(
      &m, ref, wasm::int64_mod_wrapper, ValueHelper::int64_vector());
}

TEST(RunCallUint64Div) {
  BufferedRawMachineAssemblerTester<int32_t> m;
  ExternalReference ref = ExternalReference::wasm_uint64_div();
  TestExternalReference_BinOpWithReturn<uint64_t>(
      &m, ref, wasm::uint64_div_wrapper, ValueHelper::uint64_vector());
}

TEST(RunCallUint64Mod) {
  BufferedRawMachineAssemblerTester<int32_t> m;
  ExternalReference ref = ExternalReference::wasm_uint64_mod();
  TestExternalReference_BinOpWithReturn<uint64_t>(
      &m, ref, wasm::uint64_mod_wrapper, ValueHelper::uint64_vector());
}

TEST(RunCallWord32Ctz) {
  BufferedRawMachineAssemblerTester<uint32_t> m;
  ExternalReference ref = ExternalReference::wasm_word32_ctz();
  TestExternalReference_ConvertOpWithReturn<int32_t, uint32_t>(
      &m, ref, wasm::word32_ctz_wrapper, ValueHelper::int32_vector());
}

TEST(RunCallWord64Ctz) {
  BufferedRawMachineAssemblerTester<uint32_t> m;
  ExternalReference ref = ExternalReference::wasm_word64_ctz();
  TestExternalReference_ConvertOpWithReturn<int64_t, uint32_t>(
      &m, ref, wasm::word64_ctz_wrapper, ValueHelper::int64_vector());
}

TEST(RunCallWord32Popcnt) {
  BufferedRawMachineAssemblerTester<uint32_t> m;
  ExternalReference ref = ExternalReference::wasm_word32_popcnt();
  TestExternalReference_ConvertOpWithReturn<uint32_t, uint32_t>(
      &m, ref, wasm::word32_popcnt_wrapper, ValueHelper::int32_vector());
}

TEST(RunCallWord64Popcnt) {
  BufferedRawMachineAssemblerTester<uint32_t> m;
  ExternalReference ref = ExternalReference::wasm_word64_popcnt();
  TestExternalReference_ConvertOpWithReturn<int64_t, uint32_t>(
      &m, ref, wasm::word64_popcnt_wrapper, ValueHelper::int64_vector());
}

TEST(RunCallFloat64Pow) {
  BufferedRawMachineAssemblerTester<int32_t> m;
  ExternalReference ref = ExternalReference::wasm_float64_pow();
  TestExternalReference_BinOp<double>(&m, ref, wasm::float64_pow_wrapper,
                                      ValueHelper::float64_vector());
}
#endif  // V8_ENABLE_WEBASSEMBLY

#ifdef V8_ENABLE_FP_PARAMS_IN_C_LINKAGE

template <typename T>
MachineType MachineTypeForCType() {
  UNREACHABLE();
}

template <>
MachineType MachineTypeForCType<int32_t>() {
  return MachineType::Int32();
}

template <>
MachineType MachineTypeForCType<double>() {
  return MachineType::Float64();
}

template <>
MachineType MachineTypeForCType<float>() {
  return MachineType::Float32();
}

#define SIGNATURE_TYPES_END(TYPE, IDX, VALUE) MachineTypeForCType<TYPE>()
#define SIGNATURE_TYPES(TYPE, IDX, VALUE) SIGNATURE_TYPES_END(TYPE, IDX, VALUE),

#define PARAM_PAIRS_END(TYPE, IDX, VALUE) \
  std::make_pair(MachineTypeForCType<TYPE>(), m.Parameter(IDX))

#define PARAM_PAIRS(TYPE, IDX, VALUE) PARAM_PAIRS_END(TYPE, IDX, VALUE),

#define CALL_ARGS_END(TYPE, IDX, VALUE) static_cast<TYPE>(VALUE)
#define CALL_ARGS(TYPE, IDX, VALUE) CALL_ARGS_END(TYPE, IDX, VALUE),

#define COMPARE_ARG_I_END(TYPE, IDX, VALUE) (arg##IDX == VALUE)
#define COMPARE_ARG_I(TYPE, IDX, VALUE) COMPARE_ARG_I_END(TYPE, IDX, VALUE)&&

#define SIGNATURE_TEST(NAME, SIGNATURE, FUNC)                             \
  TEST(NAME) {                                                            \
    RawMachineAssemblerTester<int64_t> m(SIGNATURE(SIGNATURE_TYPES));     \
                                                                          \
    Address func_address = FUNCTION_ADDR(&FUNC);                          \
    ExternalReference::Type dummy_type = ExternalReference::BUILTIN_CALL; \
    ApiFunction func(func_address);                                       \
    ExternalReference ref = ExternalReference::Create(&func, dummy_type); \
                                                                          \
    Node* function = m.ExternalConstant(ref);                             \
    m.Return(m.CallCFunction(function, MachineType::Int64(),              \
                             SIGNATURE(PARAM_PAIRS)));                    \
                                                                          \
    int64_t c = m.Call(SIGNATURE(CALL_ARGS));                             \
    CHECK_EQ(c, 42);                                                      \
  }

#define MIXED_SIGNATURE_SIMPLE(V) \
  V(int32_t, 0, 0)                \
  V(double, 1, 1.5)               \
  V##_END(int32_t, 2, 2)

int64_t test_api_func_simple(int32_t arg0, double arg1, int32_t arg2) {
  CHECK(MIXED_SIGNATURE_SIMPLE(COMPARE_ARG_I));
  return 42;
}

SIGNATURE_TEST(RunCallWithMixedSignatureSimple, MIXED_SIGNATURE_SIMPLE,
               test_api_func_simple)

#define MIXED_SIGNATURE(V) \
  V(int32_t, 0, 0)         \
  V(double, 1, 1.5)        \
  V(int32_t, 2, 2)         \
  V(double, 3, 3.5)        \
  V(int32_t, 4, 4)         \
  V(double, 5, 5.5)        \
  V(int32_t, 6, 6)         \
  V(double, 7, 7.5)        \
  V(int32_t, 8, 8)         \
  V(double, 9, 9.5)        \
  V##_END(int32_t, 10, 10)

int64_t test_api_func(int32_t arg0, double arg1, int32_t arg2, double arg3,
                      int32_t arg4, double arg5, int32_t arg6, double arg7,
                      int32_t arg8, double arg9, int32_t arg10) {
  CHECK(MIXED_SIGNATURE(COMPARE_ARG_I));
  return 42;
}

SIGNATURE_TEST(RunCallWithMixedSignature, MIXED_SIGNATURE, test_api_func)

#define MIXED_SIGNATURE_DOUBLE_INT(V) \
  V(double, 0, 0.5)                   \
  V(double, 1, 1.5)                   \
  V(double, 2, 2.5)                   \
  V(double, 3, 3.5)                   \
  V(double, 4, 4.5)                   \
  V(double, 5, 5.5)                   \
  V(double, 6, 6.5)                   \
  V(double, 7, 7.5)                   \
  V(double, 8, 8.5)                   \
  V(double, 9, 9.5)                   \
  V(int32_t, 10, 10)                  \
  V(int32_t, 11, 11)                  \
  V(int32_t, 12, 12)                  \
  V(int32_t, 13, 13)                  \
  V(int32_t, 14, 14)                  \
  V(int32_t, 15, 15)                  \
  V(int32_t, 16, 16)                  \
  V(int32_t, 17, 17)                  \
  V(int32_t, 18, 18)                  \
  V##_END(int32_t, 19, 19)

int64_t func_mixed_double_int(double arg0, double arg1, double arg2,
                              double arg3, double arg4, double arg5,
                              double arg6, double arg7, double arg8,
                              double arg9, int32_t arg10, int32_t arg11,
                              int32_t arg12, int32_t arg13, int32_t arg14,
                              int32_t arg15, int32_t arg16, int32_t arg17,
                              int32_t arg18, int32_t arg19) {
  CHECK(MIXED_SIGNATURE_DOUBLE_INT(COMPARE_ARG_I));
  return 42;
}

SIGNATURE_TEST(RunCallWithMixedSignatureDoubleInt, MIXED_SIGNATURE_DOUBLE_INT,
               func_mixed_double_int)

#define MIXED_SIGNATURE_INT_DOUBLE(V) \
  V(int32_t, 0, 0)                    \
  V(int32_t, 1, 1)                    \
  V(int32_t, 2, 2)                    \
  V(int32_t, 3, 3)                    \
  V(int32_t, 4, 4)                    \
  V(int32_t, 5, 5)                    \
  V(int32_t, 6, 6)                    \
  V(int32_t, 7, 7)                    \
  V(int32_t, 8, 8)                    \
  V(int32_t, 9, 9)                    \
  V(double, 10, 10.5)                 \
  V(double, 11, 11.5)                 \
  V(double, 12, 12.5)                 \
  V(double, 13, 13.5)                 \
  V(double, 14, 14.5)                 \
  V(double, 15, 15.5)                 \
  V(double, 16, 16.5)                 \
  V(double, 17, 17.5)                 \
  V(double, 18, 18.5)                 \
  V##_END(double, 19, 19.5)

int64_t func_mixed_int_double(int32_t arg0, int32_t arg1, int32_t arg2,
                              int32_t arg3, int32_t arg4, int32_t arg5,
                              int32_t arg6, int32_t arg7, int32_t arg8,
                              int32_t arg9, double arg10, double arg11,
                              double arg12, double arg13, double arg14,
                              double arg15, double arg16, double arg17,
                              double arg18, double arg19) {
  CHECK(MIXED_SIGNATURE_INT_DOUBLE(COMPARE_ARG_I));
  return 42;
}

SIGNATURE_TEST(RunCallWithMixedSignatureIntDouble, MIXED_SIGNATURE_INT_DOUBLE,
               func_mixed_int_double)

#define MIXED_SIGNATURE_INT_DOUBLE_ALT(V) \
  V(int32_t, 0, 0)                        \
  V(double, 1, 1.5)                       \
  V(int32_t, 2, 2)                        \
  V(double, 3, 3.5)                       \
  V(int32_t, 4, 4)                        \
  V(double, 5, 5.5)                       \
  V(int32_t, 6, 6)                        \
  V(double, 7, 7.5)                       \
  V(int32_t, 8, 8)                        \
  V(double, 9, 9.5)                       \
  V(int32_t, 10, 10)                      \
  V(double, 11, 11.5)                     \
  V(int32_t, 12, 12)                      \
  V(double, 13, 13.5)                     \
  V(int32_t, 14, 14)                      \
  V(double, 15, 15.5)                     \
  V(int32_t, 16, 16)                      \
  V(double, 17, 17.5)                     \
  V(int32_t, 18, 18)                      \
  V##_END(double, 19, 19.5)

int64_t func_mixed_int_double_alt(int32_t arg0, double arg1, int32_t arg2,
                                  double arg3, int32_t arg4, double arg5,
                                  int32_t arg6, double arg7, int32_t arg8,
                                  double arg9, int32_t arg10, double arg11,
                                  int32_t arg12, double arg13, int32_t arg14,
                                  double arg15, int32_t arg16, double arg17,
                                  int32_t arg18, double arg19) {
  CHECK(MIXED_SIGNATURE_INT_DOUBLE_ALT(COMPARE_ARG_I));
  return 42;
}

SIGNATURE_TEST(RunCallWithMixedSignatureIntDoubleAlt,
               MIXED_SIGNATURE_INT_DOUBLE_ALT, func_mixed_int_double_alt)

#define SIGNATURE_ONLY_DOUBLE(V) \
  V(double, 0, 0.5)              \
  V(double, 1, 1.5)              \
  V(double, 2, 2.5)              \
  V(double, 3, 3.5)              \
  V(double, 4, 4.5)              \
  V(double, 5, 5.5)              \
  V(double, 6, 6.5)              \
  V(double, 7, 7.5)              \
  V(double, 8, 8.5)              \
  V##_END(double, 9, 9.5)

int64_t func_only_double(double arg0, double arg1, double arg2, double arg3,
                         double arg4, double arg5, double arg6, double arg7,
                         double arg8, double arg9) {
  CHECK(SIGNATURE_ONLY_DOUBLE(COMPARE_ARG_I));
  return 42;
}

SIGNATURE_TEST(RunCallWithSignatureOnlyDouble, SIGNATURE_ONLY_DOUBLE,
               func_only_double)

#define SIGNATURE_ONLY_DOUBLE_20(V) \
  V(double, 0, 0.5)                 \
  V(double, 1, 1.5)                 \
  V(double, 2, 2.5)                 \
  V(double, 3, 3.5)                 \
  V(double, 4, 4.5)                 \
  V(double, 5, 5.5)                 \
  V(double, 6, 6.5)                 \
  V(double, 7, 7.5)                 \
  V(double, 8, 8.5)                 \
  V(double, 9, 9.5)                 \
  V(double, 10, 10.5)               \
  V(double, 11, 11.5)               \
  V(double, 12, 12.5)               \
  V(double, 13, 13.5)               \
  V(double, 14, 14.5)               \
  V(double, 15, 15.5)               \
  V(double, 16, 16.5)               \
  V(double, 17, 17.5)               \
  V(double, 18, 18.5)               \
  V##_END(double, 19, 19.5)

int64_t func_only_double_20(double arg0, double arg1, double arg2, double arg3,
                            double arg4, double arg5, double arg6, double arg7,
                            double arg8, double arg9, double arg10,
                            double arg11, double arg12, double arg13,
                            double arg14, double arg15, double arg16,
                            double arg17, double arg18, double arg19) {
  CHECK(SIGNATURE_ONLY_DOUBLE_20(COMPARE_ARG_I));
  return 42;
}

SIGNATURE_TEST(RunCallWithSignatureOnlyDouble20, SIGNATURE_ONLY_DOUBLE_20,
               func_only_double_20)

#define SIGNATURE_ONLY_INT(V) \
  V(int32_t, 0, 0)            \
  V(int32_t, 1, 1)            \
  V(int32_t, 2, 2)            \
  V(int32_t, 3, 3)            \
  V(int32_t, 4, 4)            \
  V(int32_t, 5, 5)            \
  V(int32_t, 6, 6)            \
  V(int32_t, 7, 7)            \
  V(int32_t, 8, 8)            \
  V##_END(int32_t, 9, 9)

int64_t func_only_int(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3,
                      int32_t arg4, int32_t arg5, int32_t arg6, int32_t arg7,
                      int32_t arg8, int32_t arg9) {
  CHECK(SIGNATURE_ONLY_INT(COMPARE_ARG_I));
  return 42;
}

SIGNATURE_TEST(RunCallWithSignatureOnlyInt, SIGNATURE_ONLY_INT, func_only_int)

#define SIGNATURE_ONLY_INT_20(V) \
  V(int32_t, 0, 0)               \
  V(int32_t, 1, 1)               \
  V(int32_t, 2, 2)               \
  V(int32_t, 3, 3)               \
  V(int32_t, 4, 4)               \
  V(int32_t, 5, 5)               \
  V(int32_t, 6, 6)               \
  V(int32_t, 7, 7)               \
  V(int32_t, 8, 8)               \
  V(int32_t, 9, 9)               \
  V(int32_t, 10, 10)             \
  V(int32_t, 11, 11)             \
  V(int32_t, 12, 12)             \
  V(int32_t, 13, 13)             \
  V(int32_t, 14, 14)             \
  V(int32_t, 15, 15)             \
  V(int32_t, 16, 16)             \
  V(int32_t, 17, 17)             \
  V(int32_t, 18, 18)             \
  V##_END(int32_t, 19, 19)

int64_t func_only_int_20(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3,
                         int32_t arg4, int32_t arg5, int32_t arg6, int32_t arg7,
                         int32_t arg8, int32_t arg9, int32_t arg10,
                         int32_t arg11, int32_t arg12, int32_t arg13,
                         int32_t arg14, int32_t arg15, int32_t arg16,
                         int32_t arg17, int32_t arg18, int32_t arg19) {
  CHECK(SIGNATURE_ONLY_INT_20(COMPARE_ARG_I));
  return 42;
}

SIGNATURE_TEST(RunCallWithSignatureOnlyInt20, SIGNATURE_ONLY_INT_20,
               func_only_int_20)

#define MIXED_SIGNATURE_SIMPLE_FLOAT(V) \
  V(int32_t, 0, 0)                      \
  V(float, 1, 1.5)                      \
  V##_END(int32_t, 2, 2)

int64_t test_api_func_simple_float(int32_t arg0, float arg1, int32_t arg2) {
  CHECK(MIXED_SIGNATURE_SIMPLE_FLOAT(COMPARE_ARG_I));
  return 42;
}

SIGNATURE_TEST(RunCallWithMixedSignatureSimpleFloat,
               MIXED_SIGNATURE_SIMPLE_FLOAT, test_api_func_simple_float)

#define MIXED_SIGNATURE_FLOAT(V) \
  V(int32_t, 0, 0)               \
  V(float, 1, 1.5)               \
  V(int32_t, 2, 2)               \
  V(float, 3, 3.5)               \
  V(int32_t, 4, 4)               \
  V(float, 5, 5.5)               \
  V(int32_t, 6, 6)               \
  V(float, 7, 7.5)               \
  V(int32_t, 8, 8)               \
  V(float, 9, 9.5)               \
  V##_END(int32_t, 10, 10)

int64_t test_api_func_float(int32_t arg0, float arg1, int32_t arg2, float arg3,
                            int32_t arg4, float arg5, int32_t arg6, float arg7,
                            int32_t arg8, float arg9, int32_t arg10) {
  CHECK(MIXED_SIGNATURE_FLOAT(COMPARE_ARG_I));
  return 42;
}

SIGNATURE_TEST(RunCallWithMixedSignatureFloat, MIXED_SIGNATURE_FLOAT,
               test_api_func_float)

#define MIXED_SIGNATURE_INT_FLOAT_ALT(V) \
  V(int32_t, 0, 0)                       \
  V(float, 1, 1.5)                       \
  V(int32_t, 2, 2)                       \
  V(float, 3, 3.5)                       \
  V(int32_t, 4, 4)                       \
  V(float, 5, 5.5)                       \
  V(int32_t, 6, 6)                       \
  V(float, 7, 7.5)                       \
  V(int32_t, 8, 8)                       \
  V(float, 9, 9.5)                       \
  V(int32_t, 10, 10)                     \
  V(float, 11, 11.5)                     \
  V(int32_t, 12, 12)                     \
  V(float, 13, 13.5)                     \
  V(int32_t, 14, 14)                     \
  V(float, 15, 15.5)                     \
  V(int32_t, 16, 16)                     \
  V(float, 17, 17.5)                     \
  V(int32_t, 18, 18)                     \
  V##_END(float, 19, 19.5)

int64_t func_mixed_int_float_alt(int32_t arg0, float arg1, int32_t arg2,
                                 float arg3, int32_t arg4, float arg5,
                                 int32_t arg6, float arg7, int32_t arg8,
                                 float arg9, int32_t arg10, float arg11,
                                 int32_t arg12, float arg13, int32_t arg14,
                                 float arg15, int32_t arg16, float arg17,
                                 int32_t arg18, float arg19) {
  CHECK(MIXED_SIGNATURE_INT_FLOAT_ALT(COMPARE_ARG_I));
  return 42;
}

SIGNATURE_TEST(RunCallWithMixedSignatureIntFloatAlt,
               MIXED_SIGNATURE_INT_FLOAT_ALT, func_mixed_int_float_alt)

#define SIGNATURE_ONLY_FLOAT_20(V) \
  V(float, 0, 0.5)                 \
  V(float, 1, 1.5)                 \
  V(float, 2, 2.5)                 \
  V(float, 3, 3.5)                 \
  V(float, 4, 4.5)                 \
  V(float, 5, 5.5)                 \
  V(float, 6, 6.5)                 \
  V(float, 7, 7.5)                 \
  V(float, 8, 8.5)                 \
  V(float, 9, 9.5)                 \
  V(float, 10, 10.5)               \
  V(float, 11, 11.5)               \
  V(float, 12, 12.5)               \
  V(float, 13, 13.5)               \
  V(float, 14, 14.5)               \
  V(float, 15, 15.5)               \
  V(float, 16, 16.5)               \
  V(float, 17, 17.5)               \
  V(float, 18, 18.5)               \
  V##_END(float, 19, 19.5)

int64_t func_only_float_20(float arg0, float arg1, float arg2, float arg3,
                           float arg4, float arg5, float arg6, float arg7,
                           float arg8, float arg9, float arg10, float arg11,
                           float arg12, float arg13, float arg14, float arg15,
                           float arg16, float arg17, float arg18, float arg19) {
  CHECK(SIGNATURE_ONLY_FLOAT_20(COMPARE_ARG_I));
  return 42;
}

SIGNATURE_TEST(RunCallWithSignatureOnlyFloat20, SIGNATURE_ONLY_FLOAT_20,
               func_only_float_20)

#define MIXED_SIGNATURE_FLOAT_INT(V) \
  V(float, 0, 0.5)                   \
  V(float, 1, 1.5)                   \
  V(float, 2, 2.5)                   \
  V(float, 3, 3.5)                   \
  V(float, 4, 4.5)                   \
  V(float, 5, 5.5)                   \
  V(float, 6, 6.5)                   \
  V(float, 7, 7.5)                   \
  V(float, 8, 8.5)                   \
  V(float, 9, 9.5)                   \
  V(int32_t, 10, 10)                 \
  V(int32_t, 11, 11)                 \
  V(int32_t, 12, 12)                 \
  V(int32_t, 13, 13)                 \
  V(int32_t, 14, 14)                 \
  V(int32_t, 15, 15)                 \
  V(int32_t, 16, 16)                 \
  V(int32_t, 17, 17)                 \
  V(int32_t, 18, 18)                 \
  V##_END(int32_t, 19, 19)

int64_t func_mixed_float_int(float arg0, float arg1, float arg2, float arg3,
                             float arg4, float arg5, float arg6, float arg7,
                             float arg8, float arg9, int32_t arg10,
                             int32_t arg11, int32_t arg12, int32_t arg13,
                             int32_t arg14, int32_t arg15, int32_t arg16,
                             int32_t arg17, int32_t arg18, int32_t arg19) {
  CHECK(MIXED_SIGNATURE_FLOAT_INT(COMPARE_ARG_I));
  return 42;
}

SIGNATURE_TEST(RunCallWithMixedSignatureFloatInt, MIXED_SIGNATURE_FLOAT_INT,
               func_mixed_float_int)

#define MIXED_SIGNATURE_INT_FLOAT(V) \
  V(int32_t, 0, 0)                   \
  V(int32_t, 1, 1)                   \
  V(int32_t, 2, 2)                   \
  V(int32_t, 3, 3)                   \
  V(int32_t, 4, 4)                   \
  V(int32_t, 5, 5)                   \
  V(int32_t, 6, 6)                   \
  V(int32_t, 7, 7)                   \
  V(int32_t, 8, 8)                   \
  V(int32_t, 9, 9)                   \
  V(float, 10, 10.5)                 \
  V(float, 11, 11.5)                 \
  V(float, 12, 12.5)                 \
  V(float, 13, 13.5)                 \
  V(float, 14, 14.5)                 \
  V(float, 15, 15.5)                 \
  V(float, 16, 16.5)                 \
  V(float, 17, 17.5)                 \
  V(float, 18, 18.5)                 \
  V##_END(float, 19, 19.5)

int64_t func_mixed_int_float(int32_t arg0, int32_t arg1, int32_t arg2,
                             int32_t arg3, int32_t arg4, int32_t arg5,
                             int32_t arg6, int32_t arg7, int32_t arg8,
                             int32_t arg9, float arg10, float arg11,
                             float arg12, float arg13, float arg14, float arg15,
                             float arg16, float arg17, float arg18,
                             float arg19) {
  CHECK(MIXED_SIGNATURE_INT_FLOAT(COMPARE_ARG_I));
  return 42;
}

SIGNATURE_TEST(RunCallWithMixedSignatureIntFloat, MIXED_SIGNATURE_INT_FLOAT,
               func_mixed_int_float)

#define MIXED_SIGNATURE_FLOAT_DOUBLE(V) \
  V(float, 0, 0.5)                      \
  V(float, 1, 1.5)                      \
  V(float, 2, 2.5)                      \
  V(float, 3, 3.5)                      \
  V(float, 4, 4.5)                      \
  V(float, 5, 5.5)                      \
  V(float, 6, 6.5)                      \
  V(float, 7, 7.5)                      \
  V(float, 8, 8.5)                      \
  V(float, 9, 9.5)                      \
  V(double, 10, 10.7)                   \
  V(double, 11, 11.7)                   \
  V(double, 12, 12.7)                   \
  V(double, 13, 13.7)                   \
  V(double, 14, 14.7)                   \
  V(double, 15, 15.7)                   \
  V(double, 16, 16.7)                   \
  V(double, 17, 17.7)                   \
  V(double, 18, 18.7)                   \
  V##_END(double, 19, 19.7)

int64_t func_mixed_float_double(float arg0, float arg1, float arg2, float arg3,
                                float arg4, float arg5, float arg6, float arg7,
                                float arg8, float arg9, double arg10,
                                double arg11, double arg12, double arg13,
                                double arg14, double arg15, double arg16,
                                double arg17, double arg18, double arg19) {
  CHECK(MIXED_SIGNATURE_FLOAT_DOUBLE(COMPARE_ARG_I));
  return 42;
}

SIGNATURE_TEST(RunCallWithMixedSignatureFloatDouble,
               MIXED_SIGNATURE_FLOAT_DOUBLE, func_mixed_float_double)

#define MIXED_SIGNATURE_DOUBLE_FLOAT(V) \
  V(double, 0, 0.7)                     \
  V(double, 1, 1.7)                     \
  V(double, 2, 2.7)                     \
  V(double, 3, 3.7)                     \
  V(double, 4, 4.7)                     \
  V(double, 5, 5.7)                     \
  V(double, 6, 6.7)                     \
  V(double, 7, 7.7)                     \
  V(double, 8, 8.7)                     \
  V(double, 9, 9.7)                     \
  V(float, 10, 10.5)                    \
  V(float, 11, 11.5)                    \
  V(float, 12, 12.5)                    \
  V(float, 13, 13.5)                    \
  V(float, 14, 14.5)                    \
  V(float, 15, 15.5)                    \
  V(float, 16, 16.5)                    \
  V(float, 17, 17.5)                    \
  V(float, 18, 18.5)                    \
  V##_END(float, 19, 19.5)

int64_t func_mixed_double_float(double arg0, double arg1, double arg2,
                                double arg3, double arg4, double arg5,
                                double arg6, double arg7, double arg8,
                                double arg9, float arg10, float arg11,
                                float arg12, float arg13, float arg14,
                                float arg15, float arg16, float arg17,
                                float arg18, float arg19) {
  CHECK(MIXED_SIGNATURE_DOUBLE_FLOAT(COMPARE_ARG_I));
  return 42;
}

SIGNATURE_TEST(RunCallWithMixedSignatureDoubleFloat,
               MIXED_SIGNATURE_DOUBLE_FLOAT, func_mixed_double_float)

#endif  // V8_ENABLE_FP_PARAMS_IN_C_LINKAGE

}  // namespace compiler
}  // namespace internal
}  // namespace v8
