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 "jit/ABIFunctions.h"
12 #include "jit/IonAnalysis.h"
13 #include "jit/Linker.h"
14 #include "jit/MacroAssembler.h"
15 #include "jit/MIRGenerator.h"
16 #include "jit/MIRGraph.h"
17 #include "jit/ValueNumbering.h"
18 #include "jit/VMFunctions.h"
19 #include "js/Value.h"
20 
21 #include "jsapi-tests/tests.h"
22 #include "jsapi-tests/testsJit.h"
23 
24 #include "jit/ABIFunctionList-inl.h"
25 #include "jit/MacroAssembler-inl.h"
26 #include "jit/VMFunctionList-inl.h"
27 
28 using namespace js;
29 using namespace js::jit;
30 
31 // This test case relies on VMFUNCTION_LIST, TAIL_CALL_VMFUNCTION_LIST,
32 // ABIFUNCTION_LIST, ABIFUNCTION_AND_TYPE_LIST and ABIFUNCTIONSIG_LIST, to
33 // create a test case for each function registered, in order to check if the
34 // arguments are properly being interpreted after a call from the JIT.
35 //
36 // This test checks that the interpretation of the C++ compiler matches the
37 // interpretation of the JIT. It works by generating a call to a function which
38 // has the same signature as the tested function. The function being called
39 // re-interprets the arguments' content to ensure that it matches the content
40 // given as arguments by the JIT.
41 //
42 // These tests cases succeed if the content provided by the JIT matches the
43 // content read by the C++ code. Otherwise, a failure implies that either the
44 // MacroAssembler is not used properly, or that the code used by the JIT to
45 // generate the function call does not match the ABI of the targeted system.
46 
47 // Convert the content of each macro list to a single and unique format which is
48 // (Name, Type).
49 #define ABIFUN_TO_ALLFUN(Fun) (#Fun, decltype(&::Fun))
50 #define ABIFUN_AND_SIG_TO_ALLFUN(Fun, Sig) (#Fun " as " #Sig, Sig)
51 #define ABISIG_TO_ALLFUN(Sig) ("(none) as " #Sig, Sig)
52 #define VMFUN_TO_ALLFUN(Name, Fun) (#Fun, decltype(&::Fun))
53 #define TC_VMFUN_TO_ALLFUN(Name, Fun, Pop) (#Fun, decltype(&::Fun))
54 
55 #define APPLY(A, B) A B
56 
57 // Generate macro calls for all the lists which are used to allow, directly or
58 // indirectly, calls performed with callWithABI.
59 //
60 // This macro will delegate to a different macro call based on the type of the
61 // list the element is extracted from.
62 #define ALL_FUNCTIONS(PREFIX)                                  \
63   ABIFUNCTION_LIST(PREFIX##_ABIFUN_TO_ALLFUN)                  \
64   ABIFUNCTION_AND_TYPE_LIST(PREFIX##_ABIFUN_AND_SIG_TO_ALLFUN) \
65   ABIFUNCTIONSIG_LIST(PREFIX##_ABISIG_TO_ALLFUN)               \
66   VMFUNCTION_LIST(PREFIX##_VMFUN_TO_ALLFUN)                    \
67   TAIL_CALL_VMFUNCTION_LIST(PREFIX##_TC_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>
ActualSizeOf()78 constexpr size_t ActualSizeOf() {
79   struct Wrapper {
80     T _unused;
81   };
82   return sizeof(Wrapper);
83 }
84 
85 template <typename T>
ActualAlignOf()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>
CstSeq()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.
PadBytes(size_t size,size_t align)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.
AtLeastSize()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>
BackPadBytes()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>
PadSize(size_t size)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>
FillLESeq()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>
FillBits()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 MoveOp type used by passABIArg to know how to
290 // interpret the value which are given as arguments.
291 template <typename Type>
TypeToMoveOp()292 constexpr MoveOp::Type TypeToMoveOp() {
293   if constexpr (std::is_same_v<Type, float>) {
294     return MoveOp::FLOAT32;
295   } else if constexpr (std::is_same_v<Type, double>) {
296     return MoveOp::DOUBLE;
297   } else {
298     return MoveOp::GENERAL;
299   }
300 }
301 
302 // Generate a sequence which contains the associated MoveOp of each argument.
303 // Note, a new class is defined because C++ header of clang are rejecting the
304 // option of having an enumerated type as argument of std::integer_sequence.
305 template <MoveOp::Type... Val>
306 class MoveOpSequence {};
307 
308 template <typename... Args>
309 using ArgsMoveOps_t = MoveOpSequence<TypeToMoveOp<Args>()...>;
310 
311 // Generate an std::integer_sequence which corresponds to a buffer containing
312 // values which are spread at the location where each arguments type would be
313 // stored in a buffer.
314 template <typename Buffer, typename Values, typename... Args>
315 struct ArgsBuffer;
316 
317 template <uint8_t... Buffer, typename Arg, typename... Args, uint64_t Val,
318           uint64_t... Values>
319 struct ArgsBuffer<std::integer_sequence<uint8_t, Buffer...>,
320                   std::integer_sequence<uint64_t, Val, Values...>, Arg,
321                   Args...> {
322   using type = typename ArgsBuffer<
323       Concat_t<std::integer_sequence<uint8_t, Buffer...>,
324                Concat_t<PadSeq_t<ActualAlignOf<Arg>(), sizeof...(Buffer)>,
325                         Concat_t<FillSeq_t<ActualSizeOf<Arg>(), Val>,
326                                  CstSeq_t<BackPadBytes<Arg>(), 0x55>>>>,
327       std::integer_sequence<uint64_t, Values...>, Args...>::type;
328 };
329 
330 template <typename Buffer>
331 struct ArgsBuffer<Buffer, std::integer_sequence<uint64_t>> {
332   using type = Buffer;
333 };
334 
335 template <typename Values, typename... Args>
336 using ArgsBuffer_t =
337     typename ArgsBuffer<std::integer_sequence<uint8_t>, Values, Args...>::type;
338 
339 // NOTE: The representation of the boolean might be surprising in this test
340 // case, see AtLeastSize function for an explanation.
341 #ifdef JS_64BIT
342 static_assert(sizeof(uintptr_t) == 8);
343 static_assert(
344     std::is_same_v<
345         ArgsBuffer_t<std::integer_sequence<uint64_t, 42, 51>, uint64_t, bool>,
346         std::integer_sequence<uint8_t, 42, 0, 0, 0, 0, 0, 0, 0, 51, 0x55, 0x55,
347                               0x55, 0x55, 0x55, 0x55, 0x55>>);
348 static_assert(
349     std::is_same_v<
350         ArgsBuffer_t<std::integer_sequence<uint64_t, 0xffffffff, 0xffffffff>,
351                      uint8_t, uint16_t>,
352         std::integer_sequence<uint8_t, 0xff, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
353                               0x55, 0xff, 0xff, 0x55, 0x55, 0x55, 0x55, 0x55,
354                               0x55>>);
355 #else
356 static_assert(sizeof(uintptr_t) == 4);
357 static_assert(
358     std::is_same_v<
359         ArgsBuffer_t<std::integer_sequence<uint64_t, 42, 51>, uint64_t, bool>,
360         std::integer_sequence<uint8_t, 42, 0, 0, 0, 0, 0, 0, 0, 51, 0x55, 0x55,
361                               0x55>>);
362 static_assert(
363     std::is_same_v<
364         ArgsBuffer_t<std::integer_sequence<uint64_t, 0xffffffff, 0xffffffff>,
365                      uint8_t, uint16_t>,
366         std::integer_sequence<uint8_t, 0xff, 0x55, 0x55, 0x55, 0xff, 0xff, 0x55,
367                               0x55>>);
368 #endif
369 
370 // Test used to check if any of the types given as template parameters are a
371 // `bool`, which is a corner case where a raw integer might be truncated by the
372 // C++ compiler.
373 template <typename... Args>
374 constexpr bool AnyBool_v = (std::is_same_v<Args, bool> || ...);
375 
376 // Instantiate an std::integer_sequence as a buffer which is readable and
377 // addressable at runtime, for reading argument values from the generated code.
378 template <typename Seq>
379 struct InstanceSeq;
380 
381 template <typename Int, Int... Values>
382 struct InstanceSeq<std::integer_sequence<Int, Values...>> {
383   static constexpr Int table[sizeof...(Values)] = {Values...};
384   static constexpr size_t size = sizeof...(Values);
385 };
386 
387 // Instantiate a buffer for testing the position of arguments when calling a
388 // function.
389 template <typename... Args>
390 using TestArgsPositions =
391     InstanceSeq<ArgsBuffer_t<ArgsIndexes_t<Args...>, Args...>>;
392 
393 // Instantiate a buffer for testing the position of arguments, one bit at a
394 // time, when calling a function.
395 template <size_t Bit, typename... Args>
396 using TestArgsBitOfPositions =
397     InstanceSeq<ArgsBuffer_t<ArgsBitOfIndexes_t<Bit, Args...>, Args...>>;
398 
399 // Instantiate a buffer to check that the size of each argument is interpreted
400 // correctly when calling a function.
401 template <typename... Args>
402 using TestArgsSizes = InstanceSeq<ArgsBuffer_t<ArgsSizes_t<Args...>, Args...>>;
403 
404 // Instantiate a buffer to check that all bits of each argument goes through.
405 template <typename... Args>
406 using TestArgsFillBits =
407     InstanceSeq<ArgsBuffer_t<ArgsFillBits_t<Args...>, Args...>>;
408 
409 // ConvertToInt returns the raw value of any argument as an integer value which
410 // can be compared with the expected values.
411 template <typename Type>
ConvertToInt(Type v)412 IntTypeOf_t<Type> ConvertToInt(Type v) {
413   // Simplify working with types by casting the address of the value to the
414   // equivalent `const void*`.
415   auto address = reinterpret_cast<const void*>(&v);
416   // Convert the `void*` to an integer pointer of the same size as the input
417   // type, and return the raw value stored in the integer interpretation.
418   static_assert(ActualSizeOf<Type>() == ActualSizeOf<IntTypeOf_t<Type>>());
419   if constexpr (std::is_reference_v<Type>) {
420     return reinterpret_cast<const IntTypeOf_t<Type>>(address);
421   } else {
422     return *reinterpret_cast<const IntTypeOf_t<Type>*>(address);
423   }
424 }
425 
426 // Attributes used to disable some parts of Undefined Behavior sanitizer. This
427 // is needed to keep the signature identical to what is used in production,
428 // instead of working around these limitations.
429 //
430 // * no_sanitize("enum"): Enumerated values given as arguments are checked to
431 //     see if the value given as argument matches any of the enumerated values.
432 //     The patterns used to check whether the values are correctly transmitted
433 //     from the JIT to C++ might go beyond the set of enumerated values, and
434 //     break this sanitizer check.
435 #if defined(__clang__) && defined(__has_attribute) && \
436     __has_attribute(no_sanitize)
437 #  define NO_ARGS_CHECKS __attribute__((no_sanitize("enum")))
438 #else
439 #  define NO_ARGS_CHECKS
440 #endif
441 
442 // Check if the raw values of arguments are equal to the numbers given in the
443 // std::integer_sequence given as the first argument.
444 template <typename... Args, typename Int, Int... Val>
CheckArgsEqual(JSAPITest * instance,int lineno,std::integer_sequence<Int,Val...>,Args...args)445 NO_ARGS_CHECKS bool CheckArgsEqual(JSAPITest* instance, int lineno,
446                                    std::integer_sequence<Int, Val...>,
447                                    Args... args) {
448   return (instance->checkEqual(ConvertToInt<Args>(args), IntTypeOf_t<Args>(Val),
449                                "ConvertToInt<Args>(args)",
450                                "IntTypeOf_t<Args>(Val)", __FILE__, lineno) &&
451           ...);
452 }
453 
454 // Generate code to register the value of each argument of the called function.
455 // Each argument's content is read from a buffer whose address is stored in the
456 // `base` register. The offsets of arguments are given as a third argument
457 // which is expected to be generated by `ArgsOffsets`. The MoveOp types of
458 // arguments are given as the fourth argument and are expected to be generated
459 // by `ArgsMoveOp`.
460 template <uint64_t... Off, MoveOp::Type... Move>
passABIArgs(MacroAssembler & masm,Register base,std::integer_sequence<uint64_t,Off...>,MoveOpSequence<Move...>)461 static void passABIArgs(MacroAssembler& masm, Register base,
462                         std::integer_sequence<uint64_t, Off...>,
463                         MoveOpSequence<Move...>) {
464   (masm.passABIArg(MoveOperand(base, size_t(Off)), Move), ...);
465 }
466 
467 // For each function type given as a parameter, create a few functions with the
468 // given type, to be called by the JIT code produced by `generateCalls`. These
469 // functions report the result through the instance registered with the
470 // `set_instance` function, as we cannot add extra arguments to these functions.
471 template <typename Type>
472 struct DefineCheckArgs;
473 
474 template <typename Res, typename... Args>
475 struct DefineCheckArgs<Res (*)(Args...)> {
set_instanceDefineCheckArgs476   void set_instance(JSAPITest* instance, bool* reportTo) {
477     MOZ_ASSERT((!instance_) != (!instance));
478     instance_ = instance;
479     MOZ_ASSERT((!reportTo_) != (!reportTo));
480     reportTo_ = reportTo;
481   }
reportDefineCheckArgs482   static void report(bool value) { *reportTo_ = *reportTo_ && value; }
483 
484   // Check that arguments are interpreted in the same order at compile time and
485   // at runtime.
CheckArgsPositionsDefineCheckArgs486   static NO_ARGS_CHECKS Res CheckArgsPositions(Args... args) {
487     AutoUnsafeCallWithABI unsafe;
488     using Indexes = std::index_sequence_for<Args...>;
489     report(CheckArgsEqual<Args...>(instance_, __LINE__, Indexes(),
490                                    std::forward<Args>(args)...));
491     return Res();
492   }
493 
494   // This is the same test as above, but some compilers might clean the boolean
495   // values using `& 1` operations, which corrupt the operand index, thus to
496   // properly check for the position of boolean operands, we have to check the
497   // position of the boolean operand using a single bit at a time.
CheckArgsBitOfPositions0DefineCheckArgs498   static NO_ARGS_CHECKS Res CheckArgsBitOfPositions0(Args... args) {
499     AutoUnsafeCallWithABI unsafe;
500     using Indexes = ArgsBitOfIndexes_t<0, Args...>;
501     report(CheckArgsEqual<Args...>(instance_, __LINE__, Indexes(),
502                                    std::forward<Args>(args)...));
503     return Res();
504   }
505 
CheckArgsBitOfPositions1DefineCheckArgs506   static NO_ARGS_CHECKS Res CheckArgsBitOfPositions1(Args... args) {
507     AutoUnsafeCallWithABI unsafe;
508     using Indexes = ArgsBitOfIndexes_t<1, Args...>;
509     report(CheckArgsEqual<Args...>(instance_, __LINE__, Indexes(),
510                                    std::forward<Args>(args)...));
511     return Res();
512   }
513 
CheckArgsBitOfPositions2DefineCheckArgs514   static NO_ARGS_CHECKS Res CheckArgsBitOfPositions2(Args... args) {
515     AutoUnsafeCallWithABI unsafe;
516     using Indexes = ArgsBitOfIndexes_t<2, Args...>;
517     report(CheckArgsEqual<Args...>(instance_, __LINE__, Indexes(),
518                                    std::forward<Args>(args)...));
519     return Res();
520   }
521 
CheckArgsBitOfPositions3DefineCheckArgs522   static NO_ARGS_CHECKS Res CheckArgsBitOfPositions3(Args... args) {
523     AutoUnsafeCallWithABI unsafe;
524     using Indexes = ArgsBitOfIndexes_t<3, Args...>;
525     report(CheckArgsEqual<Args...>(instance_, __LINE__, Indexes(),
526                                    std::forward<Args>(args)...));
527     return Res();
528   }
529 
530   // Check that the compile time and run time sizes of each argument are the
531   // same.
CheckArgsSizesDefineCheckArgs532   static NO_ARGS_CHECKS Res CheckArgsSizes(Args... args) {
533     AutoUnsafeCallWithABI unsafe;
534     using Sizes = ArgsSizes_t<Args...>;
535     report(CheckArgsEqual<Args...>(instance_, __LINE__, Sizes(),
536                                    std::forward<Args>(args)...));
537     return Res();
538   }
539 
540   // Check that the compile time and run time all bits of each argument are
541   // correctly passed through.
CheckArgsFillBitsDefineCheckArgs542   static NO_ARGS_CHECKS Res CheckArgsFillBits(Args... args) {
543     AutoUnsafeCallWithABI unsafe;
544     using FillBits = ArgsFillBits_t<Args...>;
545     report(CheckArgsEqual<Args...>(instance_, __LINE__, FillBits(),
546                                    std::forward<Args>(args)...));
547     return Res();
548   }
549 
550   using FunType = Res (*)(Args...);
551   struct Test {
552     const uint8_t* buffer;
553     const size_t size;
554     const FunType fun;
555   };
556 
557   // Generate JIT code for calling the above test functions where each argument
558   // is given a different raw value that can be compared by each called
559   // function.
generateCallsDefineCheckArgs560   void generateCalls(MacroAssembler& masm, Register base, Register setup) {
561     using ArgsPositions = TestArgsPositions<Args...>;
562     using ArgsBitOfPositions0 = TestArgsBitOfPositions<0, Args...>;
563     using ArgsBitOfPositions1 = TestArgsBitOfPositions<1, Args...>;
564     using ArgsBitOfPositions2 = TestArgsBitOfPositions<2, Args...>;
565     using ArgsBitOfPositions3 = TestArgsBitOfPositions<3, Args...>;
566     using ArgsSizes = TestArgsSizes<Args...>;
567     using ArgsFillBits = TestArgsFillBits<Args...>;
568     static const Test testsWithoutBoolArgs[3] = {
569         {ArgsPositions::table, ArgsPositions::size, CheckArgsPositions},
570         {ArgsSizes::table, ArgsSizes::size, CheckArgsSizes},
571         {ArgsFillBits::table, ArgsFillBits::size, CheckArgsFillBits},
572     };
573     static const Test testsWithBoolArgs[6] = {
574         {ArgsBitOfPositions0::table, ArgsBitOfPositions0::size,
575          CheckArgsBitOfPositions0},
576         {ArgsBitOfPositions1::table, ArgsBitOfPositions1::size,
577          CheckArgsBitOfPositions1},
578         {ArgsBitOfPositions2::table, ArgsBitOfPositions2::size,
579          CheckArgsBitOfPositions2},
580         {ArgsBitOfPositions3::table, ArgsBitOfPositions3::size,
581          CheckArgsBitOfPositions3},
582         {ArgsSizes::table, ArgsSizes::size, CheckArgsSizes},
583         {ArgsFillBits::table, ArgsFillBits::size, CheckArgsFillBits},
584     };
585     const Test* tests = testsWithoutBoolArgs;
586     size_t numTests = sizeof(testsWithoutBoolArgs) / sizeof(Test);
587     if (AnyBool_v<Args...>) {
588       tests = testsWithBoolArgs;
589       numTests = sizeof(testsWithBoolArgs) / sizeof(Test);
590     }
591 
592     for (size_t i = 0; i < numTests; i++) {
593       const Test& test = tests[i];
594       masm.movePtr(ImmPtr(test.buffer), base);
595 
596       masm.setupUnalignedABICall(setup);
597       using Offsets = ArgsOffsets_t<0, Args...>;
598       using MoveOps = ArgsMoveOps_t<Args...>;
599       passABIArgs(masm, base, Offsets(), MoveOps());
600       masm.callWithABI(DynFn{JS_FUNC_TO_DATA_PTR(void*, test.fun)},
601                        TypeToMoveOp<Res>(),
602                        CheckUnsafeCallWithABI::DontCheckOther);
603     }
604   }
605 
606  private:
607   // As we are checking specific function signature, we cannot add extra
608   // parameters, thus we rely on static variables to pass the value of the
609   // instance that we are testing.
610   static JSAPITest* instance_;
611   static bool* reportTo_;
612 };
613 
614 template <typename Res, typename... Args>
615 JSAPITest* DefineCheckArgs<Res (*)(Args...)>::instance_ = nullptr;
616 
617 template <typename Res, typename... Args>
618 bool* DefineCheckArgs<Res (*)(Args...)>::reportTo_ = nullptr;
619 
620 // This is a child class of JSAPITest, which is used behind the scenes to
621 // register test cases in jsapi-tests. Each instance of it creates a new test
622 // case. This class is specialized with the type of the function to check, and
623 // initialized with the name of the function with the given signature.
624 //
625 // When executed, it generates the JIT code to call functions with the same
626 // signature and checks that the JIT interpretation of arguments location
627 // matches the C++ interpretation. If it differs, the test case will fail.
628 template <typename Sig>
629 class JitABICall final : public JSAPITest, public DefineCheckArgs<Sig> {
630  public:
JitABICall(const char * name)631   explicit JitABICall(const char* name) : name_(name) { reuseGlobal = true; }
name()632   virtual const char* name() override { return name_; }
run(JS::HandleObject)633   virtual bool run(JS::HandleObject) override {
634     bool result = true;
635     this->set_instance(this, &result);
636 
637     StackMacroAssembler masm(cx);
638     PrepareJit(masm);
639 
640     AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
641     // Initialize the base register the same way this is done while reading
642     // arguments in generateVMWrapper, in order to avoid MOZ_RELEASE_ASSERT in
643     // the MoveResolver.
644 #if defined(JS_CODEGEN_X86)
645     Register base = regs.takeAny();
646 #elif defined(JS_CODEGEN_X64)
647     Register base = r10;
648     regs.take(base);
649 #elif defined(JS_CODEGEN_ARM)
650     Register base = r5;
651     regs.take(base);
652 #elif defined(JS_CODEGEN_ARM64)
653     Register base = r8;
654     regs.take(base);
655 #elif defined(JS_CODEGEN_MIPS32)
656     Register base = t1;
657     regs.take(base);
658 #elif defined(JS_CODEGEN_MIPS64)
659     Register base = t1;
660     regs.take(base);
661 #else
662 #  error "Unknown architecture!"
663 #endif
664 
665     Register setup = regs.takeAny();
666 
667     this->generateCalls(masm, base, setup);
668 
669     if (!ExecuteJit(cx, masm)) {
670       return false;
671     }
672     this->set_instance(nullptr, nullptr);
673     return result;
674   };
675 
676  private:
677   const char* name_;
678 };
679 
680 // GCC warns when the signature does not have matching attributes (for example
681 // [[nodiscard]]). Squelch this warning to avoid a GCC-only footgun.
682 #if MOZ_IS_GCC
683 #  pragma GCC diagnostic push
684 #  pragma GCC diagnostic ignored "-Wignored-attributes"
685 #endif
686 
687 // For each VMFunction and ABIFunction, create an instance of a JitABICall
688 // class to register a jsapi-tests test case.
689 #define TEST_INSTANCE(Name, Sig)                                             \
690   static JitABICall<Sig> MOZ_CONCAT(MOZ_CONCAT(cls_jitabicall, __COUNTER__), \
691                                     _instance)("JIT ABI for " Name);
692 #define TEST_INSTANCE_ABIFUN_TO_ALLFUN(...) \
693   APPLY(TEST_INSTANCE, ABIFUN_TO_ALLFUN(__VA_ARGS__))
694 #define TEST_INSTANCE_ABIFUN_AND_SIG_TO_ALLFUN(...) \
695   APPLY(TEST_INSTANCE, ABIFUN_AND_SIG_TO_ALLFUN(__VA_ARGS__))
696 #define TEST_INSTANCE_ABISIG_TO_ALLFUN(...) \
697   APPLY(TEST_INSTANCE, ABISIG_TO_ALLFUN(__VA_ARGS__))
698 #define TEST_INSTANCE_VMFUN_TO_ALLFUN(...) \
699   APPLY(TEST_INSTANCE, VMFUN_TO_ALLFUN(__VA_ARGS__))
700 #define TEST_INSTANCE_TC_VMFUN_TO_ALLFUN(...) \
701   APPLY(TEST_INSTANCE, TC_VMFUN_TO_ALLFUN(__VA_ARGS__))
702 
703 ALL_FUNCTIONS(TEST_INSTANCE)
704 
705 #if MOZ_IS_GCC
706 #  pragma GCC diagnostic pop
707 #endif
708