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