tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

testJitABIcalls.cpp (30885B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      3 */
      4 /* This Source Code Form is subject to the terms of the Mozilla Public
      5 * License, v. 2.0. If a copy of the MPL was not distributed with this
      6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      7 
      8 #include "mozilla/FloatingPoint.h"
      9 #include "mozilla/IntegerTypeTraits.h"
     10 
     11 #include <type_traits>
     12 
     13 #include "jit/ABIFunctions.h"
     14 #include "jit/IonAnalysis.h"
     15 #include "jit/Linker.h"
     16 #include "jit/MacroAssembler.h"
     17 #include "jit/MIRGenerator.h"
     18 #include "jit/MIRGraph.h"
     19 #include "jit/ValueNumbering.h"
     20 #include "jit/VMFunctions.h"
     21 #include "js/Value.h"
     22 
     23 #include "jsapi-tests/tests.h"
     24 #include "jsapi-tests/testsJit.h"
     25 
     26 #include "jit/ABIFunctionList-inl.h"
     27 #include "jit/MacroAssembler-inl.h"
     28 #include "jit/VMFunctionList-inl.h"
     29 
     30 using namespace js;
     31 using namespace js::jit;
     32 
     33 // This test case relies on VMFUNCTION_LIST, ABIFUNCTION_LIST,
     34 // ABIFUNCTION_AND_TYPE_LIST and ABIFUNCTIONSIG_LIST, to create a test case for
     35 // each function registered, in order to check if the arguments are properly
     36 // being interpreted after a call from the JIT.
     37 //
     38 // This test checks that the interpretation of the C++ compiler matches the
     39 // interpretation of the JIT. It works by generating a call to a function which
     40 // has the same signature as the tested function. The function being called
     41 // re-interprets the arguments' content to ensure that it matches the content
     42 // given as arguments by the JIT.
     43 //
     44 // These tests cases succeed if the content provided by the JIT matches the
     45 // content read by the C++ code. Otherwise, a failure implies that either the
     46 // MacroAssembler is not used properly, or that the code used by the JIT to
     47 // generate the function call does not match the ABI of the targeted system.
     48 
     49 // Convert the content of each macro list to a single and unique format which is
     50 // (Name, Type).
     51 #define ABIFUN_TO_ALLFUN(Fun) (#Fun, decltype(&Fun))
     52 #define ABIFUN_AND_SIG_TO_ALLFUN(Fun, Sig) (#Fun " as " #Sig, Sig)
     53 #define ABISIG_TO_ALLFUN(Sig) ("(none) as " #Sig, Sig)
     54 #define VMFUN_TO_ALLFUN(Name, Fun, Pop...) (#Fun, decltype(&Fun))
     55 
     56 #define APPLY(A, B) A B
     57 
     58 // Generate macro calls for all the lists which are used to allow, directly or
     59 // indirectly, calls performed with callWithABI.
     60 //
     61 // This macro will delegate to a different macro call based on the type of the
     62 // list the element is extracted from.
     63 #define ALL_FUNCTIONS(PREFIX)                                  \
     64  ABIFUNCTION_LIST(PREFIX##_ABIFUN_TO_ALLFUN)                  \
     65  ABIFUNCTION_AND_TYPE_LIST(PREFIX##_ABIFUN_AND_SIG_TO_ALLFUN) \
     66  ABIFUNCTIONSIG_LIST(PREFIX##_ABISIG_TO_ALLFUN)               \
     67  VMFUNCTION_LIST(PREFIX##_VMFUN_TO_ALLFUN)
     68 
     69 // sizeof(const T&) is not equal to sizeof(const T*), but references are passed
     70 // as pointers.
     71 //
     72 // "When applied to a reference or a reference type, the result is the size of
     73 // the referenced type." [expr.sizeof] (5.3.3.2)
     74 //
     75 // The following functions avoid this issue by wrapping the type in a structure
     76 // which will share the same property, even if the wrapped type is a reference.
     77 template <typename T>
     78 constexpr size_t ActualSizeOf() {
     79  struct Wrapper {
     80    T _unused;
     81  };
     82  return sizeof(Wrapper);
     83 }
     84 
     85 template <typename T>
     86 constexpr size_t ActualAlignOf() {
     87  struct Wrapper {
     88    T _unused;
     89  };
     90  return alignof(Wrapper);
     91 }
     92 
     93 // Given a type, return the integer type which has the same size.
     94 template <typename T>
     95 using IntTypeOf_t =
     96    typename mozilla::UnsignedStdintTypeForSize<ActualSizeOf<T>()>::Type;
     97 
     98 // Concatenate 2 std::integer_sequence, and return an std::integer_sequence with
     99 // the content of both parameters.
    100 template <typename Before, typename After>
    101 struct Concat;
    102 
    103 template <typename Int, Int... Before, Int... After>
    104 struct Concat<std::integer_sequence<Int, Before...>,
    105              std::integer_sequence<Int, After...>> {
    106  using type = std::integer_sequence<Int, Before..., After...>;
    107 };
    108 
    109 template <typename Before, typename After>
    110 using Concat_t = typename Concat<Before, After>::type;
    111 
    112 static_assert(std::is_same_v<Concat_t<std::integer_sequence<uint8_t, 1, 2>,
    113                                      std::integer_sequence<uint8_t, 3, 4>>,
    114                             std::integer_sequence<uint8_t, 1, 2, 3, 4>>);
    115 
    116 // Generate an std::integer_sequence of `N` elements, where each element is an
    117 // uint8_t integer with value `Value`.
    118 template <size_t N, uint8_t Value>
    119 constexpr auto CstSeq() {
    120  if constexpr (N == 0) {
    121    return std::integer_sequence<uint8_t>{};
    122  } else {
    123    return Concat_t<std::integer_sequence<uint8_t, Value>,
    124                    decltype(CstSeq<N - 1, Value>())>{};
    125  }
    126 }
    127 
    128 template <size_t N, uint8_t Value>
    129 using CstSeq_t = decltype(CstSeq<N, Value>());
    130 
    131 static_assert(
    132    std::is_same_v<CstSeq_t<4, 2>, std::integer_sequence<uint8_t, 2, 2, 2, 2>>);
    133 
    134 // Computes the number of bytes to add before a type in order to align it in
    135 // memory.
    136 constexpr size_t PadBytes(size_t size, size_t align) {
    137  return (align - (size % align)) % align;
    138 }
    139 
    140 // Request a minimum alignment for the values added to a buffer in order to
    141 // account for the read size used by the MoveOperand given as an argument of
    142 // passWithABI. The MoveOperand does not take into consideration the size of
    143 // the data being transfered, and might load a larger amount of data.
    144 //
    145 // This function ensures that the MoveOperand would read the 0x55 padding added
    146 // after each value, when it reads too much.
    147 constexpr size_t AtLeastSize() { return sizeof(uintptr_t); }
    148 
    149 // Returns the size which needs to be added in addition to the memory consumed
    150 // by the type, from which the size if given as argument.
    151 template <typename Type>
    152 constexpr size_t BackPadBytes() {
    153  return std::max(AtLeastSize(), ActualSizeOf<Type>()) - ActualSizeOf<Type>();
    154 }
    155 
    156 // Adds the padding and the reserved size for storing a value in a buffer which
    157 // can be read by a MoveOperand.
    158 template <typename Type>
    159 constexpr size_t PadSize(size_t size) {
    160  return PadBytes(size, ActualAlignOf<Type>()) + ActualSizeOf<Type>() +
    161         BackPadBytes<Type>();
    162 }
    163 
    164 // Generate an std::integer_sequence of 0:uint8_t elements of the size of the
    165 // padding needed to align a type in memory.
    166 template <size_t Align, size_t CurrSize>
    167 using PadSeq_t = decltype(CstSeq<PadBytes(CurrSize, Align), 0>());
    168 
    169 static_assert(std::is_same_v<PadSeq_t<4, 0>, std::integer_sequence<uint8_t>>);
    170 static_assert(
    171    std::is_same_v<PadSeq_t<4, 3>, std::integer_sequence<uint8_t, 0>>);
    172 static_assert(
    173    std::is_same_v<PadSeq_t<4, 2>, std::integer_sequence<uint8_t, 0, 0>>);
    174 static_assert(
    175    std::is_same_v<PadSeq_t<4, 1>, std::integer_sequence<uint8_t, 0, 0, 0>>);
    176 
    177 // Spread an integer value `Value` into a new std::integer_sequence of `N`
    178 // uint8_t elements, using Little Endian ordering of bytes.
    179 template <size_t N, uint64_t Value, uint8_t... Rest>
    180 constexpr auto FillLESeq() {
    181  if constexpr (N == 0) {
    182    return std::integer_sequence<uint8_t, Rest...>{};
    183  } else {
    184    return FillLESeq<N - 1, (Value >> 8), Rest..., uint8_t(Value & 0xff)>();
    185  }
    186 }
    187 
    188 template <size_t N, uint64_t Value>
    189 using FillSeq_t = decltype(FillLESeq<N, Value>());
    190 
    191 static_assert(std::is_same_v<FillSeq_t<4, 2>,
    192                             std::integer_sequence<uint8_t, 2, 0, 0, 0>>);
    193 
    194 // Given a list of template parameters, generate an std::integer_sequence of
    195 // size_t, where each element is 1 larger than the previous one. The generated
    196 // sequence starts at 0.
    197 template <typename... Args>
    198 using ArgsIndexes_t =
    199    std::make_integer_sequence<uint64_t, uint64_t(sizeof...(Args))>;
    200 
    201 static_assert(std::is_same_v<ArgsIndexes_t<uint8_t, uint64_t>,
    202                             std::integer_sequence<uint64_t, 0, 1>>);
    203 
    204 // Extract a single bit for each element of an std::integer_sequence. This is
    205 // used to work around some restrictions with providing boolean arguments,
    206 // which might be truncated to a single bit.
    207 template <size_t Bit, typename IntSeq>
    208 struct ExtractBit;
    209 
    210 template <size_t Bit, uint64_t... Values>
    211 struct ExtractBit<Bit, std::integer_sequence<uint64_t, Values...>> {
    212  using type = std::integer_sequence<uint64_t, (Values >> Bit) & 1 ...>;
    213 };
    214 
    215 // Generate an std::integer_sequence of indexes which are filtered for a single
    216 // bit, such that it can be used with boolean types.
    217 template <size_t Bit, typename... Args>
    218 using ArgsBitOfIndexes_t =
    219    typename ExtractBit<Bit, ArgsIndexes_t<Args...>>::type;
    220 
    221 static_assert(std::is_same_v<ArgsBitOfIndexes_t<0, int, int, int, int>,
    222                             std::integer_sequence<uint64_t, 0, 1, 0, 1>>);
    223 static_assert(std::is_same_v<ArgsBitOfIndexes_t<1, int, int, int, int>,
    224                             std::integer_sequence<uint64_t, 0, 0, 1, 1>>);
    225 
    226 // Compute the offset of each argument in a buffer produced by GenArgsBuffer,
    227 // this is used to fill the MoveOperand displacement field when loading value
    228 // out of the buffer produced by GenArgsBuffer.
    229 template <uint64_t Size, typename... Args>
    230 struct ArgsOffsets;
    231 
    232 template <uint64_t Size>
    233 struct ArgsOffsets<Size> {
    234  using type = std::integer_sequence<uint64_t>;
    235 };
    236 
    237 template <uint64_t Size, typename Arg, typename... Args>
    238 struct ArgsOffsets<Size, Arg, Args...> {
    239  using type =
    240      Concat_t<std::integer_sequence<
    241                   uint64_t, Size + PadBytes(Size, ActualAlignOf<Arg>())>,
    242               typename ArgsOffsets<Size + PadSize<Arg>(Size), Args...>::type>;
    243 };
    244 
    245 template <uint64_t Size, typename... Args>
    246 using ArgsOffsets_t = typename ArgsOffsets<Size, Args...>::type;
    247 
    248 // Not all 32bits architecture align uint64_t type on 8 bytes, so check the
    249 // validity of the stored content based on the alignment of the architecture.
    250 static_assert(ActualAlignOf<uint64_t>() != 8 ||
    251              std::is_same_v<ArgsOffsets_t<0, uint8_t, uint64_t, bool>,
    252                             std::integer_sequence<uint64_t, 0, 8, 16>>);
    253 
    254 static_assert(ActualAlignOf<uint64_t>() != 4 ||
    255              std::is_same_v<ArgsOffsets_t<0, uint8_t, uint64_t, bool>,
    256                             std::integer_sequence<uint64_t, 0, 4, 12>>);
    257 
    258 // Generate an std::integer_sequence containing the size of each argument in
    259 // memory.
    260 template <typename... Args>
    261 using ArgsSizes_t = std::integer_sequence<uint64_t, ActualSizeOf<Args>()...>;
    262 
    263 // Generate an std::integer_sequence containing values where all valid bits for
    264 // each type are set to 1.
    265 template <typename Type>
    266 constexpr uint64_t FillBits() {
    267  constexpr uint64_t topBit = uint64_t(1) << ((8 * ActualSizeOf<Type>()) - 1);
    268  if constexpr (std::is_same_v<Type, bool>) {
    269    return uint64_t(1);
    270  } else if constexpr (std::is_same_v<Type, double> ||
    271                       std::is_same_v<Type, float>) {
    272    // A NaN has all the bits of its exponent set to 1. The CPU / C++ does not
    273    // garantee keeping the payload of NaN values, when interpreted as floating
    274    // point, which could cause some random failures. This removes one bit from
    275    // the exponent, such that the floating point value is not converted to a
    276    // canonicalized NaN by the time we compare it.
    277    constexpr uint64_t lowExpBit =
    278        uint64_t(1) << mozilla::FloatingPoint<Type>::kExponentShift;
    279    return uint64_t((topBit - 1) + topBit - lowExpBit);
    280  } else {
    281    // Note: Keep parentheses to avoid unwanted overflow.
    282    return uint64_t((topBit - 1) + topBit);
    283  }
    284 }
    285 
    286 template <typename... Args>
    287 using ArgsFillBits_t = std::integer_sequence<uint64_t, FillBits<Args>()...>;
    288 
    289 // Given a type, return the ABIType used by passABIArg to know how to
    290 // interpret the value which are given as arguments.
    291 template <typename Type>
    292 constexpr ABIType TypeToABIType() {
    293  // We support the following C++ types in ABI calls.
    294  //
    295  // 1. Arithmetic types (integral or floating point)
    296  // 2. Pointer and reference types.
    297  // 3. Enum types.
    298  // 4. Class and union types.
    299  // 5. The void type
    300 
    301  if constexpr (std::is_integral_v<Type>) {
    302    // Integral types.
    303    if constexpr (sizeof(Type) <= sizeof(intptr_t)) {
    304      // Register-sized integer types are passed as |ABIType::General|,
    305      // including int32. |ABIType::Int32| is not used for int32!
    306      return ABIType::General;
    307    } else if constexpr (sizeof(Type) == sizeof(int64_t)) {
    308      return ABIType::Int64;
    309    }
    310  } else if constexpr (std::is_floating_point_v<Type>) {
    311    // Floating point types.
    312    if constexpr (std::is_same_v<Type, float>) {
    313      return ABIType::Float32;
    314    } else if constexpr (std::is_same_v<Type, double>) {
    315      return ABIType::Float64;
    316    }
    317  } else if constexpr (std::is_pointer_v<Type> || std::is_reference_v<Type>) {
    318    // Pointer and reference types.
    319    return ABIType::General;
    320  } else if constexpr (std::is_enum_v<Type>) {
    321    // Compute the ABIType from the underlying type.
    322    return TypeToABIType<std::underlying_type_t<Type>>();
    323  } else if constexpr (std::is_class_v<Type> || std::is_union_v<Type>) {
    324    // Class and union types are supported if they can be passed by-value in a
    325    // single register.
    326    //
    327    // Use trivially-copyable to test for by-value copyable types.
    328    if constexpr (std::is_trivially_copyable_v<Type>) {
    329      if constexpr (sizeof(Type) <= sizeof(intptr_t)) {
    330        // TODO: This is wrong for classes consisting of a single floating point
    331        // type. Using std::is_layout_compatible may help to support this use
    332        // case, but that requires C++20.
    333        return ABIType::General;
    334      }
    335    }
    336  } else if constexpr (std::is_void_v<Type>) {
    337    // Void type.
    338    //
    339    // The void type is represented using |ABIType::General|. |ABIType::Void| is
    340    // not used here!
    341    return ABIType::General;
    342  }
    343 }
    344 
    345 // Generate a sequence which contains the associated ABIType of each argument.
    346 // Note, a new class is defined because C++ header of clang are rejecting the
    347 // option of having an enumerated type as argument of std::integer_sequence.
    348 template <ABIType... Val>
    349 class ABITypeSequence {};
    350 
    351 template <typename... Args>
    352 using ArgsABITypes_t = ABITypeSequence<TypeToABIType<Args>()...>;
    353 
    354 // Generate an std::integer_sequence which corresponds to a buffer containing
    355 // values which are spread at the location where each arguments type would be
    356 // stored in a buffer.
    357 template <typename Buffer, typename Values, typename... Args>
    358 struct ArgsBuffer;
    359 
    360 template <uint8_t... Buffer, typename Arg, typename... Args, uint64_t Val,
    361          uint64_t... Values>
    362 struct ArgsBuffer<std::integer_sequence<uint8_t, Buffer...>,
    363                  std::integer_sequence<uint64_t, Val, Values...>, Arg,
    364                  Args...> {
    365  using type = typename ArgsBuffer<
    366      Concat_t<std::integer_sequence<uint8_t, Buffer...>,
    367               Concat_t<PadSeq_t<ActualAlignOf<Arg>(), sizeof...(Buffer)>,
    368                        Concat_t<FillSeq_t<ActualSizeOf<Arg>(), Val>,
    369                                 CstSeq_t<BackPadBytes<Arg>(), 0x55>>>>,
    370      std::integer_sequence<uint64_t, Values...>, Args...>::type;
    371 };
    372 
    373 template <typename Buffer>
    374 struct ArgsBuffer<Buffer, std::integer_sequence<uint64_t>> {
    375  using type = Buffer;
    376 };
    377 
    378 template <typename Values, typename... Args>
    379 using ArgsBuffer_t =
    380    typename ArgsBuffer<std::integer_sequence<uint8_t>, Values, Args...>::type;
    381 
    382 // NOTE: The representation of the boolean might be surprising in this test
    383 // case, see AtLeastSize function for an explanation.
    384 #ifdef JS_64BIT
    385 static_assert(sizeof(uintptr_t) == 8);
    386 static_assert(
    387    std::is_same_v<
    388        ArgsBuffer_t<std::integer_sequence<uint64_t, 42, 51>, uint64_t, bool>,
    389        std::integer_sequence<uint8_t, 42, 0, 0, 0, 0, 0, 0, 0, 51, 0x55, 0x55,
    390                              0x55, 0x55, 0x55, 0x55, 0x55>>);
    391 static_assert(
    392    std::is_same_v<
    393        ArgsBuffer_t<std::integer_sequence<uint64_t, 0xffffffff, 0xffffffff>,
    394                     uint8_t, uint16_t>,
    395        std::integer_sequence<uint8_t, 0xff, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
    396                              0x55, 0xff, 0xff, 0x55, 0x55, 0x55, 0x55, 0x55,
    397                              0x55>>);
    398 #else
    399 static_assert(sizeof(uintptr_t) == 4);
    400 static_assert(
    401    std::is_same_v<
    402        ArgsBuffer_t<std::integer_sequence<uint64_t, 42, 51>, uint64_t, bool>,
    403        std::integer_sequence<uint8_t, 42, 0, 0, 0, 0, 0, 0, 0, 51, 0x55, 0x55,
    404                              0x55>>);
    405 static_assert(
    406    std::is_same_v<
    407        ArgsBuffer_t<std::integer_sequence<uint64_t, 0xffffffff, 0xffffffff>,
    408                     uint8_t, uint16_t>,
    409        std::integer_sequence<uint8_t, 0xff, 0x55, 0x55, 0x55, 0xff, 0xff, 0x55,
    410                              0x55>>);
    411 #endif
    412 
    413 // Test used to check if any of the types given as template parameters are a
    414 // `bool`, which is a corner case where a raw integer might be truncated by the
    415 // C++ compiler.
    416 template <typename... Args>
    417 constexpr bool AnyBool_v = (std::is_same_v<Args, bool> || ...);
    418 
    419 // Instantiate an std::integer_sequence as a buffer which is readable and
    420 // addressable at runtime, for reading argument values from the generated code.
    421 template <typename Seq>
    422 struct InstanceSeq;
    423 
    424 template <typename Int, Int... Values>
    425 struct InstanceSeq<std::integer_sequence<Int, Values...>> {
    426  static constexpr Int table[sizeof...(Values)] = {Values...};
    427  static constexpr size_t size = sizeof...(Values);
    428 };
    429 
    430 // Instantiate a buffer for testing the position of arguments when calling a
    431 // function.
    432 template <typename... Args>
    433 using TestArgsPositions =
    434    InstanceSeq<ArgsBuffer_t<ArgsIndexes_t<Args...>, Args...>>;
    435 
    436 // Instantiate a buffer for testing the position of arguments, one bit at a
    437 // time, when calling a function.
    438 template <size_t Bit, typename... Args>
    439 using TestArgsBitOfPositions =
    440    InstanceSeq<ArgsBuffer_t<ArgsBitOfIndexes_t<Bit, Args...>, Args...>>;
    441 
    442 // Instantiate a buffer to check that the size of each argument is interpreted
    443 // correctly when calling a function.
    444 template <typename... Args>
    445 using TestArgsSizes = InstanceSeq<ArgsBuffer_t<ArgsSizes_t<Args...>, Args...>>;
    446 
    447 // Instantiate a buffer to check that all bits of each argument goes through.
    448 template <typename... Args>
    449 using TestArgsFillBits =
    450    InstanceSeq<ArgsBuffer_t<ArgsFillBits_t<Args...>, Args...>>;
    451 
    452 // ConvertToInt returns the raw value of any argument as an integer value which
    453 // can be compared with the expected values.
    454 template <typename Type>
    455 IntTypeOf_t<Type> ConvertToInt(Type v) {
    456  // Simplify working with types by casting the address of the value to the
    457  // equivalent `const void*`.
    458  auto address = reinterpret_cast<const void*>(&v);
    459  // Convert the `void*` to an integer pointer of the same size as the input
    460  // type, and return the raw value stored in the integer interpretation.
    461  static_assert(ActualSizeOf<Type>() == ActualSizeOf<IntTypeOf_t<Type>>());
    462  if constexpr (std::is_reference_v<Type>) {
    463    return reinterpret_cast<const IntTypeOf_t<Type>>(address);
    464  } else {
    465    return *reinterpret_cast<const IntTypeOf_t<Type>*>(address);
    466  }
    467 }
    468 
    469 // Attributes used to disable some parts of Undefined Behavior sanitizer. This
    470 // is needed to keep the signature identical to what is used in production,
    471 // instead of working around these limitations.
    472 //
    473 // * no_sanitize("enum"): Enumerated values given as arguments are checked to
    474 //     see if the value given as argument matches any of the enumerated values.
    475 //     The patterns used to check whether the values are correctly transmitted
    476 //     from the JIT to C++ might go beyond the set of enumerated values, and
    477 //     break this sanitizer check.
    478 #if defined(__clang__) && defined(__has_attribute) && \
    479    __has_attribute(no_sanitize)
    480 #  define NO_ARGS_CHECKS __attribute__((no_sanitize("enum")))
    481 #else
    482 #  define NO_ARGS_CHECKS
    483 #endif
    484 
    485 // Check if the raw values of arguments are equal to the numbers given in the
    486 // std::integer_sequence given as the first argument.
    487 template <typename... Args, typename Int, Int... Val>
    488 NO_ARGS_CHECKS bool CheckArgsEqual(JSAPIRuntimeTest* instance, int lineno,
    489                                   std::integer_sequence<Int, Val...>,
    490                                   Args... args) {
    491  return (instance->checkEqual(ConvertToInt<Args>(args), IntTypeOf_t<Args>(Val),
    492                               "ConvertToInt<Args>(args)",
    493                               "IntTypeOf_t<Args>(Val)", __FILE__, lineno) &&
    494          ...);
    495 }
    496 
    497 // Generate code to register the value of each argument of the called function.
    498 // Each argument's content is read from a buffer whose address is stored in the
    499 // `base` register. The offsets of arguments are given as a third argument
    500 // which is expected to be generated by `ArgsOffsets`. The ABIType types of
    501 // arguments are given as the fourth argument and are expected to be generated
    502 // by `ArgsABIType`.
    503 template <uint64_t... Off, ABIType... Type>
    504 static void passABIArgs(MacroAssembler& masm, Register base,
    505                        std::integer_sequence<uint64_t, Off...>,
    506                        ABITypeSequence<Type...>) {
    507  [[maybe_unused]] auto passABIArg = [&](uint64_t off, ABIType type) {
    508    if (type == ABIType::Int64) {
    509 #ifdef JS_64BIT
    510      masm.passABIArg(MoveOperand(base, size_t(off)), ABIType::General);
    511 #else
    512      masm.passABIArg(MoveOperand(base, size_t(off)), ABIType::General);
    513      masm.passABIArg(MoveOperand(base, size_t(off) + sizeof(intptr_t)),
    514                      ABIType::General);
    515 #endif
    516    } else {
    517      masm.passABIArg(MoveOperand(base, size_t(off)), type);
    518    }
    519  };
    520  (passABIArg(Off, Type), ...);
    521 }
    522 
    523 // For each function type given as a parameter, create a few functions with the
    524 // given type, to be called by the JIT code produced by `generateCalls`. These
    525 // functions report the result through the instance registered with the
    526 // `set_instance` function, as we cannot add extra arguments to these functions.
    527 template <typename Type>
    528 struct DefineCheckArgs;
    529 
    530 template <typename Res, typename... Args>
    531 struct DefineCheckArgs<Res (*)(Args...)> {
    532  void set_instance(JSAPIRuntimeTest* instance, bool* reportTo) {
    533    MOZ_ASSERT((!instance_) != (!instance));
    534    instance_ = instance;
    535    MOZ_ASSERT((!reportTo_) != (!reportTo));
    536    reportTo_ = reportTo;
    537  }
    538  static void report(bool value) { *reportTo_ = *reportTo_ && value; }
    539 
    540  // Check that arguments are interpreted in the same order at compile time and
    541  // at runtime.
    542  static NO_ARGS_CHECKS Res CheckArgsPositions(Args... args) {
    543    AutoUnsafeCallWithABI unsafe;
    544    using Indexes = std::index_sequence_for<Args...>;
    545    report(CheckArgsEqual<Args...>(instance_, __LINE__, Indexes(),
    546                                   std::forward<Args>(args)...));
    547    return Res();
    548  }
    549 
    550  // This is the same test as above, but some compilers might clean the boolean
    551  // values using `& 1` operations, which corrupt the operand index, thus to
    552  // properly check for the position of boolean operands, we have to check the
    553  // position of the boolean operand using a single bit at a time.
    554  static NO_ARGS_CHECKS Res CheckArgsBitOfPositions0(Args... args) {
    555    AutoUnsafeCallWithABI unsafe;
    556    using Indexes = ArgsBitOfIndexes_t<0, Args...>;
    557    report(CheckArgsEqual<Args...>(instance_, __LINE__, Indexes(),
    558                                   std::forward<Args>(args)...));
    559    return Res();
    560  }
    561 
    562  static NO_ARGS_CHECKS Res CheckArgsBitOfPositions1(Args... args) {
    563    AutoUnsafeCallWithABI unsafe;
    564    using Indexes = ArgsBitOfIndexes_t<1, Args...>;
    565    report(CheckArgsEqual<Args...>(instance_, __LINE__, Indexes(),
    566                                   std::forward<Args>(args)...));
    567    return Res();
    568  }
    569 
    570  static NO_ARGS_CHECKS Res CheckArgsBitOfPositions2(Args... args) {
    571    AutoUnsafeCallWithABI unsafe;
    572    using Indexes = ArgsBitOfIndexes_t<2, Args...>;
    573    report(CheckArgsEqual<Args...>(instance_, __LINE__, Indexes(),
    574                                   std::forward<Args>(args)...));
    575    return Res();
    576  }
    577 
    578  static NO_ARGS_CHECKS Res CheckArgsBitOfPositions3(Args... args) {
    579    AutoUnsafeCallWithABI unsafe;
    580    using Indexes = ArgsBitOfIndexes_t<3, Args...>;
    581    report(CheckArgsEqual<Args...>(instance_, __LINE__, Indexes(),
    582                                   std::forward<Args>(args)...));
    583    return Res();
    584  }
    585 
    586  // Check that the compile time and run time sizes of each argument are the
    587  // same.
    588  static NO_ARGS_CHECKS Res CheckArgsSizes(Args... args) {
    589    AutoUnsafeCallWithABI unsafe;
    590    using Sizes = ArgsSizes_t<Args...>;
    591    report(CheckArgsEqual<Args...>(instance_, __LINE__, Sizes(),
    592                                   std::forward<Args>(args)...));
    593    return Res();
    594  }
    595 
    596  // Check that the compile time and run time all bits of each argument are
    597  // correctly passed through.
    598  static NO_ARGS_CHECKS Res CheckArgsFillBits(Args... args) {
    599    AutoUnsafeCallWithABI unsafe;
    600    using FillBits = ArgsFillBits_t<Args...>;
    601    report(CheckArgsEqual<Args...>(instance_, __LINE__, FillBits(),
    602                                   std::forward<Args>(args)...));
    603    return Res();
    604  }
    605 
    606  using FunType = Res (*)(Args...);
    607  struct Test {
    608    const uint8_t* buffer;
    609    const size_t size;
    610    const FunType fun;
    611  };
    612 
    613  // Generate JIT code for calling the above test functions where each argument
    614  // is given a different raw value that can be compared by each called
    615  // function.
    616  void generateCalls(MacroAssembler& masm, Register base, Register setup) {
    617    using ArgsPositions = TestArgsPositions<Args...>;
    618    using ArgsBitOfPositions0 = TestArgsBitOfPositions<0, Args...>;
    619    using ArgsBitOfPositions1 = TestArgsBitOfPositions<1, Args...>;
    620    using ArgsBitOfPositions2 = TestArgsBitOfPositions<2, Args...>;
    621    using ArgsBitOfPositions3 = TestArgsBitOfPositions<3, Args...>;
    622    using ArgsSizes = TestArgsSizes<Args...>;
    623    using ArgsFillBits = TestArgsFillBits<Args...>;
    624    static const Test testsWithoutBoolArgs[3] = {
    625        {ArgsPositions::table, ArgsPositions::size, CheckArgsPositions},
    626        {ArgsSizes::table, ArgsSizes::size, CheckArgsSizes},
    627        {ArgsFillBits::table, ArgsFillBits::size, CheckArgsFillBits},
    628    };
    629    static const Test testsWithBoolArgs[6] = {
    630        {ArgsBitOfPositions0::table, ArgsBitOfPositions0::size,
    631         CheckArgsBitOfPositions0},
    632        {ArgsBitOfPositions1::table, ArgsBitOfPositions1::size,
    633         CheckArgsBitOfPositions1},
    634        {ArgsBitOfPositions2::table, ArgsBitOfPositions2::size,
    635         CheckArgsBitOfPositions2},
    636        {ArgsBitOfPositions3::table, ArgsBitOfPositions3::size,
    637         CheckArgsBitOfPositions3},
    638        {ArgsSizes::table, ArgsSizes::size, CheckArgsSizes},
    639        {ArgsFillBits::table, ArgsFillBits::size, CheckArgsFillBits},
    640    };
    641    const Test* tests = testsWithoutBoolArgs;
    642    size_t numTests = std::size(testsWithoutBoolArgs);
    643    if (AnyBool_v<Args...>) {
    644      tests = testsWithBoolArgs;
    645      numTests = std::size(testsWithBoolArgs);
    646    }
    647 
    648    for (size_t i = 0; i < numTests; i++) {
    649      const Test& test = tests[i];
    650      masm.movePtr(ImmPtr(test.buffer), base);
    651 
    652      masm.setupUnalignedABICall(setup);
    653      using Offsets = ArgsOffsets_t<0, Args...>;
    654      using ABITypes = ArgsABITypes_t<Args...>;
    655      passABIArgs(masm, base, Offsets(), ABITypes());
    656      masm.callWithABI(DynFn{JS_FUNC_TO_DATA_PTR(void*, test.fun)},
    657                       TypeToABIType<Res>(),
    658                       CheckUnsafeCallWithABI::DontCheckOther);
    659    }
    660  }
    661 
    662 private:
    663  // As we are checking specific function signature, we cannot add extra
    664  // parameters, thus we rely on static variables to pass the value of the
    665  // instance that we are testing.
    666  static JSAPIRuntimeTest* instance_;
    667  static bool* reportTo_;
    668 };
    669 
    670 template <typename Res, typename... Args>
    671 JSAPIRuntimeTest* DefineCheckArgs<Res (*)(Args...)>::instance_ = nullptr;
    672 
    673 template <typename Res, typename... Args>
    674 bool* DefineCheckArgs<Res (*)(Args...)>::reportTo_ = nullptr;
    675 
    676 // This is a child class of JSAPIRuntimeTest, which is used behind the scenes to
    677 // register test cases in jsapi-tests. Each instance of it creates a new test
    678 // case. This class is specialized with the type of the function to check, and
    679 // initialized with the name of the function with the given signature.
    680 //
    681 // When executed, it generates the JIT code to call functions with the same
    682 // signature and checks that the JIT interpretation of arguments location
    683 // matches the C++ interpretation. If it differs, the test case will fail.
    684 template <typename Sig>
    685 class JitABICall final : public JSAPIRuntimeTest, public DefineCheckArgs<Sig> {
    686 public:
    687  explicit JitABICall(const char* name) : name_(name) { reuseGlobal = true; }
    688  virtual const char* name() override { return name_; }
    689  virtual bool run(JS::HandleObject) override {
    690    bool result = true;
    691    this->set_instance(this, &result);
    692 
    693    TempAllocator temp(&cx->tempLifoAlloc());
    694    JitContext jcx(cx);
    695    StackMacroAssembler masm(cx, temp);
    696    AutoCreatedBy acb(masm, __func__);
    697    PrepareJit(masm);
    698 
    699    AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
    700    // Initialize the base register the same way this is done while reading
    701    // arguments in generateVMWrapper, in order to avoid MOZ_RELEASE_ASSERT in
    702    // the MoveResolver.
    703 #if defined(JS_CODEGEN_X86)
    704    Register base = regs.takeAny();
    705 #elif defined(JS_CODEGEN_X64)
    706    Register base = r10;
    707    regs.take(base);
    708 #elif defined(JS_CODEGEN_ARM)
    709    Register base = r5;
    710    regs.take(base);
    711 #elif defined(JS_CODEGEN_ARM64)
    712    Register base = r8;
    713    regs.take(base);
    714 #elif defined(JS_CODEGEN_MIPS64)
    715    Register base = t5;
    716    regs.take(base);
    717 #elif defined(JS_CODEGEN_LOONG64)
    718    Register base = t0;
    719    regs.take(base);
    720 #elif defined(JS_CODEGEN_RISCV64)
    721    Register base = t0;
    722    regs.take(base);
    723 #else
    724 #  error "Unknown architecture!"
    725 #endif
    726 
    727    Register setup = regs.takeAny();
    728 
    729    this->generateCalls(masm, base, setup);
    730 
    731    if (!ExecuteJit(cx, masm)) {
    732      return false;
    733    }
    734    this->set_instance(nullptr, nullptr);
    735    return result;
    736  };
    737 
    738 private:
    739  const char* name_;
    740 };
    741 
    742 // GCC warns when the signature does not have matching attributes (for example
    743 // [[nodiscard]]). Squelch this warning to avoid a GCC-only footgun.
    744 #if MOZ_IS_GCC
    745 #  pragma GCC diagnostic push
    746 #  pragma GCC diagnostic ignored "-Wignored-attributes"
    747 #endif
    748 
    749 // For each VMFunction and ABIFunction, create an instance of a JitABICall
    750 // class to register a jsapi-tests test case.
    751 #define TEST_INSTANCE(Name, Sig)                 \
    752  MOZ_RUNINIT static JitABICall<Sig> MOZ_CONCAT( \
    753      MOZ_CONCAT(cls_jitabicall, __COUNTER__),   \
    754      _instance)("JIT ABI for " Name);
    755 #define TEST_INSTANCE_ABIFUN_TO_ALLFUN(...) \
    756  APPLY(TEST_INSTANCE, ABIFUN_TO_ALLFUN(__VA_ARGS__))
    757 #define TEST_INSTANCE_ABIFUN_AND_SIG_TO_ALLFUN(...) \
    758  APPLY(TEST_INSTANCE, ABIFUN_AND_SIG_TO_ALLFUN(__VA_ARGS__))
    759 #define TEST_INSTANCE_ABISIG_TO_ALLFUN(...) \
    760  APPLY(TEST_INSTANCE, ABISIG_TO_ALLFUN(__VA_ARGS__))
    761 #define TEST_INSTANCE_VMFUN_TO_ALLFUN(...) \
    762  APPLY(TEST_INSTANCE, VMFUN_TO_ALLFUN(__VA_ARGS__))
    763 
    764 ALL_FUNCTIONS(TEST_INSTANCE)
    765 
    766 #if MOZ_IS_GCC
    767 #  pragma GCC diagnostic pop
    768 #endif