1/*
2 * Copyright (C) 2017-2018 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27
28#include "CCallHelpers.h"
29#include "CPU.h"
30#include "FPRInfo.h"
31#include "GPRInfo.h"
32#include "InitializeThreading.h"
33#include "LinkBuffer.h"
34#include "ProbeContext.h"
35#include "StackAlignment.h"
36#include <limits>
37#include <wtf/Compiler.h>
38#include <wtf/DataLog.h>
39#include <wtf/Function.h>
40#include <wtf/Lock.h>
41#include <wtf/NumberOfCores.h>
42#include <wtf/PtrTag.h>
43#include <wtf/Threading.h>
44#include <wtf/text/StringCommon.h>
45
46// We don't have a NO_RETURN_DUE_TO_EXIT, nor should we. That's ridiculous.
47static bool hiddenTruthBecauseNoReturnIsStupid() { return true; }
48
49static void usage()
50{
51 dataLog("Usage: testmasm [<filter>]\n");
52 if (hiddenTruthBecauseNoReturnIsStupid())
53 exit(1);
54}
55
56#if ENABLE(JIT)
57
58#if ENABLE(MASM_PROBE)
59namespace WTF {
60
61static void printInternal(PrintStream& out, void* value)
62{
63 out.printf("%p", value);
64}
65
66} // namespace WTF
67#endif // ENABLE(MASM_PROBE)
68
69namespace JSC {
70namespace Probe {
71
72JS_EXPORT_PRIVATE void* probeStateForContext(Probe::Context&);
73
74} // namespace Probe
75} // namespace JSC
76
77using namespace JSC;
78
79namespace {
80
81#if ENABLE(MASM_PROBE)
82using CPUState = Probe::CPUState;
83#endif
84
85Lock crashLock;
86
87typedef WTF::Function<void(CCallHelpers&)> Generator;
88
89template<typename T> T nextID(T id) { return static_cast<T>(id + 1); }
90
91#define TESTWORD64 0x0c0defefebeef000
92#define TESTWORD32 0x0beef000
93
94#define testWord32(x) (TESTWORD32 + static_cast<uint32_t>(x))
95#define testWord64(x) (TESTWORD64 + static_cast<uint64_t>(x))
96
97#if USE(JSVALUE64)
98#define testWord(x) testWord64(x)
99#else
100#define testWord(x) testWord32(x)
101#endif
102
103// Nothing fancy for now; we just use the existing WTF assertion machinery.
104#define CHECK_EQ(_actual, _expected) do { \
105 if ((_actual) == (_expected)) \
106 break; \
107 crashLock.lock(); \
108 dataLog("FAILED while testing " #_actual ": expected: ", _expected, ", actual: ", _actual, "\n"); \
109 WTFReportAssertionFailure(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, "CHECK_EQ("#_actual ", " #_expected ")"); \
110 CRASH(); \
111 } while (false)
112
113#define CHECK_NOT_EQ(_actual, _expected) do { \
114 if ((_actual) != (_expected)) \
115 break; \
116 crashLock.lock(); \
117 dataLog("FAILED while testing " #_actual ": expected not: ", _expected, ", actual: ", _actual, "\n"); \
118 WTFReportAssertionFailure(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, "CHECK_NOT_EQ("#_actual ", " #_expected ")"); \
119 CRASH(); \
120 } while (false)
121
122#if ENABLE(MASM_PROBE)
123bool isPC(MacroAssembler::RegisterID id)
124{
125#if CPU(ARM_THUMB2)
126 return id == ARMRegisters::pc;
127#else
128 UNUSED_PARAM(id);
129 return false;
130#endif
131}
132
133bool isSP(MacroAssembler::RegisterID id)
134{
135 return id == MacroAssembler::stackPointerRegister;
136}
137
138bool isFP(MacroAssembler::RegisterID id)
139{
140 return id == MacroAssembler::framePointerRegister;
141}
142
143bool isSpecialGPR(MacroAssembler::RegisterID id)
144{
145 if (isPC(id) || isSP(id) || isFP(id))
146 return true;
147#if CPU(ARM64)
148 if (id == ARM64Registers::x18)
149 return true;
150#elif CPU(MIPS)
151 if (id == MIPSRegisters::zero || id == MIPSRegisters::k0 || id == MIPSRegisters::k1)
152 return true;
153#endif
154 return false;
155}
156#endif // ENABLE(MASM_PROBE)
157
158MacroAssemblerCodeRef<JSEntryPtrTag> compile(Generator&& generate)
159{
160 CCallHelpers jit;
161 generate(jit);
162 LinkBuffer linkBuffer(jit, nullptr);
163 return FINALIZE_CODE(linkBuffer, JSEntryPtrTag, "testmasm compilation");
164}
165
166template<typename T, typename... Arguments>
167T invoke(const MacroAssemblerCodeRef<JSEntryPtrTag>& code, Arguments... arguments)
168{
169 void* executableAddress = untagCFunctionPtr<JSEntryPtrTag>(code.code().executableAddress());
170 T (*function)(Arguments...) = bitwise_cast<T(*)(Arguments...)>(executableAddress);
171 return function(arguments...);
172}
173
174template<typename T, typename... Arguments>
175T compileAndRun(Generator&& generator, Arguments... arguments)
176{
177 return invoke<T>(compile(WTFMove(generator)), arguments...);
178}
179
180void testSimple()
181{
182 CHECK_EQ(compileAndRun<int>([] (CCallHelpers& jit) {
183 jit.emitFunctionPrologue();
184 jit.move(CCallHelpers::TrustedImm32(42), GPRInfo::returnValueGPR);
185 jit.emitFunctionEpilogue();
186 jit.ret();
187 }), 42);
188}
189
190void testGetEffectiveAddress(size_t pointer, ptrdiff_t length, int32_t offset, CCallHelpers::Scale scale)
191{
192 CHECK_EQ(compileAndRun<size_t>([=] (CCallHelpers& jit) {
193 jit.emitFunctionPrologue();
194 jit.move(CCallHelpers::TrustedImmPtr(bitwise_cast<void*>(pointer)), GPRInfo::regT0);
195 jit.move(CCallHelpers::TrustedImmPtr(bitwise_cast<void*>(length)), GPRInfo::regT1);
196 jit.getEffectiveAddress(CCallHelpers::BaseIndex(GPRInfo::regT0, GPRInfo::regT1, scale, offset), GPRInfo::returnValueGPR);
197 jit.emitFunctionEpilogue();
198 jit.ret();
199 }), pointer + offset + (static_cast<size_t>(1) << static_cast<int>(scale)) * length);
200}
201
202// branchTruncateDoubleToInt32(), when encountering Infinity, -Infinity or a
203// Nan, should either yield 0 in dest or fail.
204void testBranchTruncateDoubleToInt32(double val, int32_t expected)
205{
206 const uint64_t valAsUInt = *reinterpret_cast<uint64_t*>(&val);
207#if CPU(BIG_ENDIAN)
208 const bool isBigEndian = true;
209#else
210 const bool isBigEndian = false;
211#endif
212 CHECK_EQ(compileAndRun<int>([&] (CCallHelpers& jit) {
213 jit.emitFunctionPrologue();
214 jit.subPtr(CCallHelpers::TrustedImm32(stackAlignmentBytes()), MacroAssembler::stackPointerRegister);
215 if (isBigEndian) {
216 jit.store32(CCallHelpers::TrustedImm32(valAsUInt >> 32),
217 MacroAssembler::stackPointerRegister);
218 jit.store32(CCallHelpers::TrustedImm32(valAsUInt & 0xffffffff),
219 MacroAssembler::Address(MacroAssembler::stackPointerRegister, 4));
220 } else {
221 jit.store32(CCallHelpers::TrustedImm32(valAsUInt & 0xffffffff),
222 MacroAssembler::stackPointerRegister);
223 jit.store32(CCallHelpers::TrustedImm32(valAsUInt >> 32),
224 MacroAssembler::Address(MacroAssembler::stackPointerRegister, 4));
225 }
226 jit.loadDouble(MacroAssembler::stackPointerRegister, FPRInfo::fpRegT0);
227
228 MacroAssembler::Jump done;
229 done = jit.branchTruncateDoubleToInt32(FPRInfo::fpRegT0, GPRInfo::returnValueGPR, MacroAssembler::BranchIfTruncateSuccessful);
230
231 jit.move(CCallHelpers::TrustedImm32(0), GPRInfo::returnValueGPR);
232
233 done.link(&jit);
234 jit.addPtr(CCallHelpers::TrustedImm32(stackAlignmentBytes()), MacroAssembler::stackPointerRegister);
235 jit.emitFunctionEpilogue();
236 jit.ret();
237 }), expected);
238}
239
240
241static Vector<double> doubleOperands()
242{
243 return Vector<double> {
244 0,
245 -0,
246 1,
247 -1,
248 42,
249 -42,
250 std::numeric_limits<double>::max(),
251 std::numeric_limits<double>::min(),
252 std::numeric_limits<double>::lowest(),
253 std::numeric_limits<double>::quiet_NaN(),
254 std::numeric_limits<double>::infinity(),
255 -std::numeric_limits<double>::infinity(),
256 };
257}
258
259
260#if CPU(X86) || CPU(X86_64) || CPU(ARM64)
261static Vector<float> floatOperands()
262{
263 return Vector<float> {
264 0,
265 -0,
266 1,
267 -1,
268 42,
269 -42,
270 std::numeric_limits<float>::max(),
271 std::numeric_limits<float>::min(),
272 std::numeric_limits<float>::lowest(),
273 std::numeric_limits<float>::quiet_NaN(),
274 std::numeric_limits<float>::infinity(),
275 -std::numeric_limits<float>::infinity(),
276 };
277}
278#endif
279
280static Vector<int32_t> int32Operands()
281{
282 return Vector<int32_t> {
283 0,
284 1,
285 -1,
286 2,
287 -2,
288 42,
289 -42,
290 64,
291 std::numeric_limits<int32_t>::max(),
292 std::numeric_limits<int32_t>::min(),
293 };
294}
295
296void testCompareDouble(MacroAssembler::DoubleCondition condition)
297{
298 double arg1 = 0;
299 double arg2 = 0;
300
301 auto compareDouble = compile([&, condition] (CCallHelpers& jit) {
302 jit.emitFunctionPrologue();
303
304 jit.loadDouble(CCallHelpers::TrustedImmPtr(&arg1), FPRInfo::fpRegT0);
305 jit.loadDouble(CCallHelpers::TrustedImmPtr(&arg2), FPRInfo::fpRegT1);
306 jit.move(CCallHelpers::TrustedImm32(-1), GPRInfo::returnValueGPR);
307 jit.compareDouble(condition, FPRInfo::fpRegT0, FPRInfo::fpRegT1, GPRInfo::returnValueGPR);
308
309 jit.emitFunctionEpilogue();
310 jit.ret();
311 });
312
313 auto compareDoubleGeneric = compile([&, condition] (CCallHelpers& jit) {
314 jit.emitFunctionPrologue();
315
316 jit.loadDouble(CCallHelpers::TrustedImmPtr(&arg1), FPRInfo::fpRegT0);
317 jit.loadDouble(CCallHelpers::TrustedImmPtr(&arg2), FPRInfo::fpRegT1);
318 jit.move(CCallHelpers::TrustedImm32(1), GPRInfo::returnValueGPR);
319 auto jump = jit.branchDouble(condition, FPRInfo::fpRegT0, FPRInfo::fpRegT1);
320 jit.move(CCallHelpers::TrustedImm32(0), GPRInfo::returnValueGPR);
321 jump.link(&jit);
322
323 jit.emitFunctionEpilogue();
324 jit.ret();
325 });
326
327 auto operands = doubleOperands();
328 for (auto a : operands) {
329 for (auto b : operands) {
330 arg1 = a;
331 arg2 = b;
332 CHECK_EQ(invoke<int>(compareDouble), invoke<int>(compareDoubleGeneric));
333 }
334 }
335}
336
337void testMul32WithImmediates()
338{
339 for (auto immediate : int32Operands()) {
340 auto mul = compile([=] (CCallHelpers& jit) {
341 jit.emitFunctionPrologue();
342
343 jit.mul32(CCallHelpers::TrustedImm32(immediate), GPRInfo::argumentGPR0, GPRInfo::returnValueGPR);
344
345 jit.emitFunctionEpilogue();
346 jit.ret();
347 });
348
349 for (auto value : int32Operands())
350 CHECK_EQ(invoke<int>(mul, value), immediate * value);
351 }
352}
353
354#if CPU(X86) || CPU(X86_64) || CPU(ARM64)
355void testCompareFloat(MacroAssembler::DoubleCondition condition)
356{
357 float arg1 = 0;
358 float arg2 = 0;
359
360 auto compareFloat = compile([&, condition] (CCallHelpers& jit) {
361 jit.emitFunctionPrologue();
362
363 jit.loadFloat(CCallHelpers::TrustedImmPtr(&arg1), FPRInfo::fpRegT0);
364 jit.loadFloat(CCallHelpers::TrustedImmPtr(&arg2), FPRInfo::fpRegT1);
365 jit.move(CCallHelpers::TrustedImm32(-1), GPRInfo::returnValueGPR);
366 jit.compareFloat(condition, FPRInfo::fpRegT0, FPRInfo::fpRegT1, GPRInfo::returnValueGPR);
367
368 jit.emitFunctionEpilogue();
369 jit.ret();
370 });
371
372 auto compareFloatGeneric = compile([&, condition] (CCallHelpers& jit) {
373 jit.emitFunctionPrologue();
374
375 jit.loadFloat(CCallHelpers::TrustedImmPtr(&arg1), FPRInfo::fpRegT0);
376 jit.loadFloat(CCallHelpers::TrustedImmPtr(&arg2), FPRInfo::fpRegT1);
377 jit.move(CCallHelpers::TrustedImm32(1), GPRInfo::returnValueGPR);
378 auto jump = jit.branchFloat(condition, FPRInfo::fpRegT0, FPRInfo::fpRegT1);
379 jit.move(CCallHelpers::TrustedImm32(0), GPRInfo::returnValueGPR);
380 jump.link(&jit);
381
382 jit.emitFunctionEpilogue();
383 jit.ret();
384 });
385
386 auto operands = floatOperands();
387 for (auto a : operands) {
388 for (auto b : operands) {
389 arg1 = a;
390 arg2 = b;
391 CHECK_EQ(invoke<int>(compareFloat), invoke<int>(compareFloatGeneric));
392 }
393 }
394}
395#endif
396
397#if ENABLE(MASM_PROBE)
398void testProbeReadsArgumentRegisters()
399{
400 bool probeWasCalled = false;
401 compileAndRun<void>([&] (CCallHelpers& jit) {
402 jit.emitFunctionPrologue();
403
404 jit.pushPair(GPRInfo::argumentGPR0, GPRInfo::argumentGPR1);
405 jit.pushPair(GPRInfo::argumentGPR2, GPRInfo::argumentGPR3);
406
407 jit.move(CCallHelpers::TrustedImm32(testWord32(0)), GPRInfo::argumentGPR0);
408 jit.convertInt32ToDouble(GPRInfo::argumentGPR0, FPRInfo::fpRegT0);
409 jit.move(CCallHelpers::TrustedImm32(testWord32(1)), GPRInfo::argumentGPR0);
410 jit.convertInt32ToDouble(GPRInfo::argumentGPR0, FPRInfo::fpRegT1);
411#if USE(JSVALUE64)
412 jit.move(CCallHelpers::TrustedImm64(testWord(0)), GPRInfo::argumentGPR0);
413 jit.move(CCallHelpers::TrustedImm64(testWord(1)), GPRInfo::argumentGPR1);
414 jit.move(CCallHelpers::TrustedImm64(testWord(2)), GPRInfo::argumentGPR2);
415 jit.move(CCallHelpers::TrustedImm64(testWord(3)), GPRInfo::argumentGPR3);
416#else
417 jit.move(CCallHelpers::TrustedImm32(testWord(0)), GPRInfo::argumentGPR0);
418 jit.move(CCallHelpers::TrustedImm32(testWord(1)), GPRInfo::argumentGPR1);
419 jit.move(CCallHelpers::TrustedImm32(testWord(2)), GPRInfo::argumentGPR2);
420 jit.move(CCallHelpers::TrustedImm32(testWord(3)), GPRInfo::argumentGPR3);
421#endif
422
423 jit.probe([&] (Probe::Context& context) {
424 auto& cpu = context.cpu;
425 probeWasCalled = true;
426 CHECK_EQ(cpu.gpr(GPRInfo::argumentGPR0), testWord(0));
427 CHECK_EQ(cpu.gpr(GPRInfo::argumentGPR1), testWord(1));
428 CHECK_EQ(cpu.gpr(GPRInfo::argumentGPR2), testWord(2));
429 CHECK_EQ(cpu.gpr(GPRInfo::argumentGPR3), testWord(3));
430
431 CHECK_EQ(cpu.fpr(FPRInfo::fpRegT0), testWord32(0));
432 CHECK_EQ(cpu.fpr(FPRInfo::fpRegT1), testWord32(1));
433 });
434
435 jit.popPair(GPRInfo::argumentGPR2, GPRInfo::argumentGPR3);
436 jit.popPair(GPRInfo::argumentGPR0, GPRInfo::argumentGPR1);
437
438 jit.emitFunctionEpilogue();
439 jit.ret();
440 });
441 CHECK_EQ(probeWasCalled, true);
442}
443
444void testProbeWritesArgumentRegisters()
445{
446 // This test relies on testProbeReadsArgumentRegisters() having already validated
447 // that we can read from argument registers. We'll use that ability to validate
448 // that our writes did take effect.
449 unsigned probeCallCount = 0;
450 compileAndRun<void>([&] (CCallHelpers& jit) {
451 jit.emitFunctionPrologue();
452
453 jit.pushPair(GPRInfo::argumentGPR0, GPRInfo::argumentGPR1);
454 jit.pushPair(GPRInfo::argumentGPR2, GPRInfo::argumentGPR3);
455
456 // Pre-initialize with non-expected values.
457#if USE(JSVALUE64)
458 jit.move(CCallHelpers::TrustedImm64(0), GPRInfo::argumentGPR0);
459 jit.move(CCallHelpers::TrustedImm64(0), GPRInfo::argumentGPR1);
460 jit.move(CCallHelpers::TrustedImm64(0), GPRInfo::argumentGPR2);
461 jit.move(CCallHelpers::TrustedImm64(0), GPRInfo::argumentGPR3);
462#else
463 jit.move(CCallHelpers::TrustedImm32(0), GPRInfo::argumentGPR0);
464 jit.move(CCallHelpers::TrustedImm32(0), GPRInfo::argumentGPR1);
465 jit.move(CCallHelpers::TrustedImm32(0), GPRInfo::argumentGPR2);
466 jit.move(CCallHelpers::TrustedImm32(0), GPRInfo::argumentGPR3);
467#endif
468 jit.convertInt32ToDouble(GPRInfo::argumentGPR0, FPRInfo::fpRegT0);
469 jit.convertInt32ToDouble(GPRInfo::argumentGPR0, FPRInfo::fpRegT1);
470
471 // Write expected values.
472 jit.probe([&] (Probe::Context& context) {
473 auto& cpu = context.cpu;
474 probeCallCount++;
475 cpu.gpr(GPRInfo::argumentGPR0) = testWord(0);
476 cpu.gpr(GPRInfo::argumentGPR1) = testWord(1);
477 cpu.gpr(GPRInfo::argumentGPR2) = testWord(2);
478 cpu.gpr(GPRInfo::argumentGPR3) = testWord(3);
479
480 cpu.fpr(FPRInfo::fpRegT0) = bitwise_cast<double>(testWord64(0));
481 cpu.fpr(FPRInfo::fpRegT1) = bitwise_cast<double>(testWord64(1));
482 });
483
484 // Validate that expected values were written.
485 jit.probe([&] (Probe::Context& context) {
486 auto& cpu = context.cpu;
487 probeCallCount++;
488 CHECK_EQ(cpu.gpr(GPRInfo::argumentGPR0), testWord(0));
489 CHECK_EQ(cpu.gpr(GPRInfo::argumentGPR1), testWord(1));
490 CHECK_EQ(cpu.gpr(GPRInfo::argumentGPR2), testWord(2));
491 CHECK_EQ(cpu.gpr(GPRInfo::argumentGPR3), testWord(3));
492
493 CHECK_EQ(cpu.fpr<uint64_t>(FPRInfo::fpRegT0), testWord64(0));
494 CHECK_EQ(cpu.fpr<uint64_t>(FPRInfo::fpRegT1), testWord64(1));
495 });
496
497 jit.popPair(GPRInfo::argumentGPR2, GPRInfo::argumentGPR3);
498 jit.popPair(GPRInfo::argumentGPR0, GPRInfo::argumentGPR1);
499
500 jit.emitFunctionEpilogue();
501 jit.ret();
502 });
503 CHECK_EQ(probeCallCount, 2);
504}
505
506static NEVER_INLINE NOT_TAIL_CALLED int testFunctionToTrashGPRs(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j)
507{
508 if (j > 0)
509 return testFunctionToTrashGPRs(a + 1, b + a, c + b, d + 5, e - a, f * 1.5, g ^ a, h - b, i, j - 1);
510 return a + 1;
511}
512static NEVER_INLINE NOT_TAIL_CALLED double testFunctionToTrashFPRs(double a, double b, double c, double d, double e, double f, double g, double h, double i, double j)
513{
514 if (j > 0)
515 return testFunctionToTrashFPRs(a + 1, b + a, c + b, d + 5, e - a, f * 1.5, pow(g, a), h - b, i, j - 1);
516 return a + 1;
517}
518
519void testProbePreservesGPRS()
520{
521 // This test relies on testProbeReadsArgumentRegisters() and testProbeWritesArgumentRegisters()
522 // having already validated that we can read and write from registers. We'll use these abilities
523 // to validate that the probe preserves register values.
524 unsigned probeCallCount = 0;
525 CPUState originalState;
526
527 compileAndRun<void>([&] (CCallHelpers& jit) {
528 jit.emitFunctionPrologue();
529
530 // Write expected values into the registers (except for sp, fp, and pc).
531 jit.probe([&] (Probe::Context& context) {
532 auto& cpu = context.cpu;
533 probeCallCount++;
534 for (auto id = CCallHelpers::firstRegister(); id <= CCallHelpers::lastRegister(); id = nextID(id)) {
535 originalState.gpr(id) = cpu.gpr(id);
536 if (isSpecialGPR(id))
537 continue;
538 cpu.gpr(id) = testWord(static_cast<int>(id));
539 }
540 for (auto id = CCallHelpers::firstFPRegister(); id <= CCallHelpers::lastFPRegister(); id = nextID(id)) {
541 originalState.fpr(id) = cpu.fpr(id);
542 cpu.fpr(id) = bitwise_cast<double>(testWord64(id));
543 }
544 });
545
546 // Invoke the probe to call a lot of functions and trash register values.
547 jit.probe([&] (Probe::Context&) {
548 probeCallCount++;
549 CHECK_EQ(testFunctionToTrashGPRs(0, 1, 2, 3, 4, 5, 6, 7, 8, 9), 10);
550 CHECK_EQ(testFunctionToTrashFPRs(0, 1, 2, 3, 4, 5, 6, 7, 8, 9), 10);
551 });
552
553 // Validate that the registers have the expected values.
554 jit.probe([&] (Probe::Context& context) {
555 auto& cpu = context.cpu;
556 probeCallCount++;
557 for (auto id = CCallHelpers::firstRegister(); id <= CCallHelpers::lastRegister(); id = nextID(id)) {
558 if (isSP(id) || isFP(id)) {
559 CHECK_EQ(cpu.gpr(id), originalState.gpr(id));
560 continue;
561 }
562 if (isSpecialGPR(id))
563 continue;
564 CHECK_EQ(cpu.gpr(id), testWord(id));
565 }
566 for (auto id = CCallHelpers::firstFPRegister(); id <= CCallHelpers::lastFPRegister(); id = nextID(id))
567#if CPU(MIPS)
568 if (!(id & 1))
569#endif
570 CHECK_EQ(cpu.fpr<uint64_t>(id), testWord64(id));
571 });
572
573 // Restore the original state.
574 jit.probe([&] (Probe::Context& context) {
575 auto& cpu = context.cpu;
576 probeCallCount++;
577 for (auto id = CCallHelpers::firstRegister(); id <= CCallHelpers::lastRegister(); id = nextID(id)) {
578 if (isSpecialGPR(id))
579 continue;
580 cpu.gpr(id) = originalState.gpr(id);
581 }
582 for (auto id = CCallHelpers::firstFPRegister(); id <= CCallHelpers::lastFPRegister(); id = nextID(id))
583 cpu.fpr(id) = originalState.fpr(id);
584 });
585
586 // Validate that the original state was restored.
587 jit.probe([&] (Probe::Context& context) {
588 auto& cpu = context.cpu;
589 probeCallCount++;
590 for (auto id = CCallHelpers::firstRegister(); id <= CCallHelpers::lastRegister(); id = nextID(id)) {
591 if (isSpecialGPR(id))
592 continue;
593 CHECK_EQ(cpu.gpr(id), originalState.gpr(id));
594 }
595 for (auto id = CCallHelpers::firstFPRegister(); id <= CCallHelpers::lastFPRegister(); id = nextID(id))
596#if CPU(MIPS)
597 if (!(id & 1))
598#endif
599 CHECK_EQ(cpu.fpr<uint64_t>(id), originalState.fpr<uint64_t>(id));
600 });
601
602 jit.emitFunctionEpilogue();
603 jit.ret();
604 });
605 CHECK_EQ(probeCallCount, 5);
606}
607
608void testProbeModifiesStackPointer(WTF::Function<void*(Probe::Context&)> computeModifiedStackPointer)
609{
610 unsigned probeCallCount = 0;
611 CPUState originalState;
612 void* originalSP { nullptr };
613 void* modifiedSP { nullptr };
614#if !(CPU(MIPS))
615 uintptr_t modifiedFlags { 0 };
616#endif
617
618#if CPU(X86) || CPU(X86_64)
619 auto flagsSPR = X86Registers::eflags;
620 uintptr_t flagsMask = 0xc5;
621#elif CPU(ARM_THUMB2)
622 auto flagsSPR = ARMRegisters::apsr;
623 uintptr_t flagsMask = 0xf8000000;
624#elif CPU(ARM64)
625 auto flagsSPR = ARM64Registers::nzcv;
626 uintptr_t flagsMask = 0xf0000000;
627#endif
628
629 compileAndRun<void>([&] (CCallHelpers& jit) {
630 jit.emitFunctionPrologue();
631
632 // Preserve original stack pointer and modify the sp, and
633 // write expected values into other registers (except for fp, and pc).
634 jit.probe([&] (Probe::Context& context) {
635 auto& cpu = context.cpu;
636 probeCallCount++;
637 for (auto id = CCallHelpers::firstRegister(); id <= CCallHelpers::lastRegister(); id = nextID(id)) {
638 originalState.gpr(id) = cpu.gpr(id);
639 if (isSpecialGPR(id))
640 continue;
641 cpu.gpr(id) = testWord(static_cast<int>(id));
642 }
643 for (auto id = CCallHelpers::firstFPRegister(); id <= CCallHelpers::lastFPRegister(); id = nextID(id)) {
644 originalState.fpr(id) = cpu.fpr(id);
645 cpu.fpr(id) = bitwise_cast<double>(testWord64(id));
646 }
647
648#if !(CPU(MIPS))
649 originalState.spr(flagsSPR) = cpu.spr(flagsSPR);
650 modifiedFlags = originalState.spr(flagsSPR) ^ flagsMask;
651 cpu.spr(flagsSPR) = modifiedFlags;
652#endif
653
654 originalSP = cpu.sp();
655 modifiedSP = computeModifiedStackPointer(context);
656 cpu.sp() = modifiedSP;
657 });
658
659 // Validate that the registers have the expected values.
660 jit.probe([&] (Probe::Context& context) {
661 auto& cpu = context.cpu;
662 probeCallCount++;
663 for (auto id = CCallHelpers::firstRegister(); id <= CCallHelpers::lastRegister(); id = nextID(id)) {
664 if (isFP(id)) {
665 CHECK_EQ(cpu.gpr(id), originalState.gpr(id));
666 continue;
667 }
668 if (isSpecialGPR(id))
669 continue;
670 CHECK_EQ(cpu.gpr(id), testWord(id));
671 }
672 for (auto id = CCallHelpers::firstFPRegister(); id <= CCallHelpers::lastFPRegister(); id = nextID(id))
673#if CPU(MIPS)
674 if (!(id & 1))
675#endif
676 CHECK_EQ(cpu.fpr<uint64_t>(id), testWord64(id));
677#if !(CPU(MIPS))
678 CHECK_EQ(cpu.spr(flagsSPR) & flagsMask, modifiedFlags & flagsMask);
679#endif
680 CHECK_EQ(cpu.sp(), modifiedSP);
681 });
682
683 // Restore the original state.
684 jit.probe([&] (Probe::Context& context) {
685 auto& cpu = context.cpu;
686 probeCallCount++;
687 for (auto id = CCallHelpers::firstRegister(); id <= CCallHelpers::lastRegister(); id = nextID(id)) {
688 if (isSpecialGPR(id))
689 continue;
690 cpu.gpr(id) = originalState.gpr(id);
691 }
692 for (auto id = CCallHelpers::firstFPRegister(); id <= CCallHelpers::lastFPRegister(); id = nextID(id))
693 cpu.fpr(id) = originalState.fpr(id);
694#if !(CPU(MIPS))
695 cpu.spr(flagsSPR) = originalState.spr(flagsSPR);
696#endif
697 cpu.sp() = originalSP;
698 });
699
700 // Validate that the original state was restored.
701 jit.probe([&] (Probe::Context& context) {
702 auto& cpu = context.cpu;
703 probeCallCount++;
704 for (auto id = CCallHelpers::firstRegister(); id <= CCallHelpers::lastRegister(); id = nextID(id)) {
705 if (isSpecialGPR(id))
706 continue;
707 CHECK_EQ(cpu.gpr(id), originalState.gpr(id));
708 }
709 for (auto id = CCallHelpers::firstFPRegister(); id <= CCallHelpers::lastFPRegister(); id = nextID(id))
710#if CPU(MIPS)
711 if (!(id & 1))
712#endif
713 CHECK_EQ(cpu.fpr<uint64_t>(id), originalState.fpr<uint64_t>(id));
714#if !(CPU(MIPS))
715 CHECK_EQ(cpu.spr(flagsSPR) & flagsMask, originalState.spr(flagsSPR) & flagsMask);
716#endif
717 CHECK_EQ(cpu.sp(), originalSP);
718 });
719
720 jit.emitFunctionEpilogue();
721 jit.ret();
722 });
723 CHECK_EQ(probeCallCount, 4);
724}
725
726void testProbeModifiesStackPointerToInsideProbeStateOnStack()
727{
728 size_t increment = sizeof(uintptr_t);
729#if CPU(ARM64)
730 // The ARM64 probe uses ldp and stp which require 16 byte alignment.
731 increment = 2 * sizeof(uintptr_t);
732#endif
733 for (size_t offset = 0; offset < sizeof(Probe::State); offset += increment) {
734 testProbeModifiesStackPointer([=] (Probe::Context& context) -> void* {
735 return reinterpret_cast<uint8_t*>(probeStateForContext(context)) + offset;
736
737 });
738 }
739}
740
741void testProbeModifiesStackPointerToNBytesBelowSP()
742{
743 size_t increment = sizeof(uintptr_t);
744#if CPU(ARM64)
745 // The ARM64 probe uses ldp and stp which require 16 byte alignment.
746 increment = 2 * sizeof(uintptr_t);
747#endif
748 for (size_t offset = 0; offset < 1 * KB; offset += increment) {
749 testProbeModifiesStackPointer([=] (Probe::Context& context) -> void* {
750 return context.cpu.sp<uint8_t*>() - offset;
751 });
752 }
753}
754
755void testProbeModifiesProgramCounter()
756{
757 // This test relies on testProbeReadsArgumentRegisters() and testProbeWritesArgumentRegisters()
758 // having already validated that we can read and write from registers. We'll use these abilities
759 // to validate that the probe preserves register values.
760 unsigned probeCallCount = 0;
761 bool continuationWasReached = false;
762
763 MacroAssemblerCodeRef<JSEntryPtrTag> continuation = compile([&] (CCallHelpers& jit) {
764 // Validate that we reached the continuation.
765 jit.probe([&] (Probe::Context&) {
766 probeCallCount++;
767 continuationWasReached = true;
768 });
769
770 jit.emitFunctionEpilogue();
771 jit.ret();
772 });
773
774 compileAndRun<void>([&] (CCallHelpers& jit) {
775 jit.emitFunctionPrologue();
776
777 // Write expected values into the registers.
778 jit.probe([&] (Probe::Context& context) {
779 probeCallCount++;
780 context.cpu.pc() = untagCodePtr(continuation.code().executableAddress(), JSEntryPtrTag);
781 });
782
783 jit.breakpoint(); // We should never get here.
784 });
785 CHECK_EQ(probeCallCount, 2);
786 CHECK_EQ(continuationWasReached, true);
787}
788
789void testProbeModifiesStackValues()
790{
791 unsigned probeCallCount = 0;
792 CPUState originalState;
793 void* originalSP { nullptr };
794 void* newSP { nullptr };
795#if !CPU(MIPS)
796 uintptr_t modifiedFlags { 0 };
797#endif
798 size_t numberOfExtraEntriesToWrite { 10 }; // ARM64 requires that this be 2 word aligned.
799
800#if CPU(X86) || CPU(X86_64)
801 MacroAssembler::SPRegisterID flagsSPR = X86Registers::eflags;
802 uintptr_t flagsMask = 0xc5;
803#elif CPU(ARM_THUMB2)
804 MacroAssembler::SPRegisterID flagsSPR = ARMRegisters::apsr;
805 uintptr_t flagsMask = 0xf8000000;
806#elif CPU(ARM64)
807 MacroAssembler::SPRegisterID flagsSPR = ARM64Registers::nzcv;
808 uintptr_t flagsMask = 0xf0000000;
809#endif
810
811 compileAndRun<void>([&] (CCallHelpers& jit) {
812 jit.emitFunctionPrologue();
813
814 // Write expected values into the registers.
815 jit.probe([&] (Probe::Context& context) {
816 auto& cpu = context.cpu;
817 auto& stack = context.stack();
818 probeCallCount++;
819
820 // Preserve the original CPU state.
821 for (auto id = CCallHelpers::firstRegister(); id <= CCallHelpers::lastRegister(); id = nextID(id)) {
822 originalState.gpr(id) = cpu.gpr(id);
823 if (isSpecialGPR(id))
824 continue;
825 cpu.gpr(id) = testWord(static_cast<int>(id));
826 }
827 for (auto id = CCallHelpers::firstFPRegister(); id <= CCallHelpers::lastFPRegister(); id = nextID(id)) {
828 originalState.fpr(id) = cpu.fpr(id);
829 cpu.fpr(id) = bitwise_cast<double>(testWord64(id));
830 }
831#if !(CPU(MIPS))
832 originalState.spr(flagsSPR) = cpu.spr(flagsSPR);
833 modifiedFlags = originalState.spr(flagsSPR) ^ flagsMask;
834 cpu.spr(flagsSPR) = modifiedFlags;
835#endif
836
837 // Ensure that we'll be writing over the regions of the stack where the Probe::State is.
838 originalSP = cpu.sp();
839 newSP = reinterpret_cast<uintptr_t*>(probeStateForContext(context)) - numberOfExtraEntriesToWrite;
840 cpu.sp() = newSP;
841
842 // Fill the stack with values.
843 uintptr_t* p = reinterpret_cast<uintptr_t*>(newSP);
844 int count = 0;
845 stack.set<double>(p++, 1.234567);
846 if (is32Bit())
847 p++; // On 32-bit targets, a double takes up 2 uintptr_t.
848 while (p < reinterpret_cast<uintptr_t*>(originalSP))
849 stack.set<uintptr_t>(p++, testWord(count++));
850 });
851
852 // Validate that the registers and stack have the expected values.
853 jit.probe([&] (Probe::Context& context) {
854 auto& cpu = context.cpu;
855 auto& stack = context.stack();
856 probeCallCount++;
857
858 // Validate the register values.
859 for (auto id = CCallHelpers::firstRegister(); id <= CCallHelpers::lastRegister(); id = nextID(id)) {
860 if (isFP(id)) {
861 CHECK_EQ(cpu.gpr(id), originalState.gpr(id));
862 continue;
863 }
864 if (isSpecialGPR(id))
865 continue;
866 CHECK_EQ(cpu.gpr(id), testWord(id));
867 }
868 for (auto id = CCallHelpers::firstFPRegister(); id <= CCallHelpers::lastFPRegister(); id = nextID(id))
869#if CPU(MIPS)
870 if (!(id & 1))
871#endif
872 CHECK_EQ(cpu.fpr<uint64_t>(id), testWord64(id));
873#if !(CPU(MIPS))
874 CHECK_EQ(cpu.spr(flagsSPR) & flagsMask, modifiedFlags & flagsMask);
875#endif
876 CHECK_EQ(cpu.sp(), newSP);
877
878 // Validate the stack values.
879 uintptr_t* p = reinterpret_cast<uintptr_t*>(newSP);
880 int count = 0;
881 CHECK_EQ(stack.get<double>(p++), 1.234567);
882 if (is32Bit())
883 p++; // On 32-bit targets, a double takes up 2 uintptr_t.
884 while (p < reinterpret_cast<uintptr_t*>(originalSP))
885 CHECK_EQ(stack.get<uintptr_t>(p++), testWord(count++));
886 });
887
888 // Restore the original state.
889 jit.probe([&] (Probe::Context& context) {
890 auto& cpu = context.cpu;
891 probeCallCount++;
892 for (auto id = CCallHelpers::firstRegister(); id <= CCallHelpers::lastRegister(); id = nextID(id)) {
893 if (isSpecialGPR(id))
894 continue;
895 cpu.gpr(id) = originalState.gpr(id);
896 }
897 for (auto id = CCallHelpers::firstFPRegister(); id <= CCallHelpers::lastFPRegister(); id = nextID(id))
898 cpu.fpr(id) = originalState.fpr(id);
899#if !(CPU(MIPS))
900 cpu.spr(flagsSPR) = originalState.spr(flagsSPR);
901#endif
902 cpu.sp() = originalSP;
903 });
904
905 jit.emitFunctionEpilogue();
906 jit.ret();
907 });
908
909 CHECK_EQ(probeCallCount, 3);
910}
911#endif // ENABLE(MASM_PROBE)
912
913void testByteSwap()
914{
915#if CPU(X86_64) || CPU(ARM64)
916 auto byteSwap16 = compile([] (CCallHelpers& jit) {
917 jit.emitFunctionPrologue();
918 jit.move(GPRInfo::argumentGPR0, GPRInfo::returnValueGPR);
919 jit.byteSwap16(GPRInfo::returnValueGPR);
920 jit.emitFunctionEpilogue();
921 jit.ret();
922 });
923 CHECK_EQ(invoke<uint64_t>(byteSwap16, 0xaabbccddee001122), static_cast<uint64_t>(0x2211));
924 CHECK_EQ(invoke<uint64_t>(byteSwap16, 0xaabbccddee00ffaa), static_cast<uint64_t>(0xaaff));
925
926 auto byteSwap32 = compile([] (CCallHelpers& jit) {
927 jit.emitFunctionPrologue();
928 jit.move(GPRInfo::argumentGPR0, GPRInfo::returnValueGPR);
929 jit.byteSwap32(GPRInfo::returnValueGPR);
930 jit.emitFunctionEpilogue();
931 jit.ret();
932 });
933 CHECK_EQ(invoke<uint64_t>(byteSwap32, 0xaabbccddee001122), static_cast<uint64_t>(0x221100ee));
934 CHECK_EQ(invoke<uint64_t>(byteSwap32, 0xaabbccddee00ffaa), static_cast<uint64_t>(0xaaff00ee));
935
936 auto byteSwap64 = compile([] (CCallHelpers& jit) {
937 jit.emitFunctionPrologue();
938 jit.move(GPRInfo::argumentGPR0, GPRInfo::returnValueGPR);
939 jit.byteSwap64(GPRInfo::returnValueGPR);
940 jit.emitFunctionEpilogue();
941 jit.ret();
942 });
943 CHECK_EQ(invoke<uint64_t>(byteSwap64, 0xaabbccddee001122), static_cast<uint64_t>(0x221100eeddccbbaa));
944 CHECK_EQ(invoke<uint64_t>(byteSwap64, 0xaabbccddee00ffaa), static_cast<uint64_t>(0xaaff00eeddccbbaa));
945#endif
946}
947
948void testMoveDoubleConditionally32()
949{
950#if CPU(X86_64) | CPU(ARM64)
951 double arg1 = 0;
952 double arg2 = 0;
953 const double zero = -0;
954
955 const double chosenDouble = 6.00000059604644775390625;
956 CHECK_EQ(static_cast<double>(static_cast<float>(chosenDouble)) == chosenDouble, false);
957
958 auto sel = compile([&] (CCallHelpers& jit) {
959 jit.emitFunctionPrologue();
960 jit.loadDouble(CCallHelpers::TrustedImmPtr(&zero), FPRInfo::returnValueFPR);
961 jit.loadDouble(CCallHelpers::TrustedImmPtr(&arg1), FPRInfo::fpRegT1);
962 jit.loadDouble(CCallHelpers::TrustedImmPtr(&arg2), FPRInfo::fpRegT2);
963
964 jit.move(MacroAssembler::TrustedImm32(-1), GPRInfo::regT0);
965 jit.moveDoubleConditionally32(MacroAssembler::Equal, GPRInfo::regT0, GPRInfo::regT0, FPRInfo::fpRegT1, FPRInfo::fpRegT2, FPRInfo::returnValueFPR);
966
967 jit.emitFunctionEpilogue();
968 jit.ret();
969 });
970
971 arg1 = chosenDouble;
972 arg2 = 43;
973 CHECK_EQ(invoke<double>(sel), chosenDouble);
974
975 arg1 = 43;
976 arg2 = chosenDouble;
977 CHECK_EQ(invoke<double>(sel), 43.0);
978
979#endif
980}
981
982void testMoveDoubleConditionally64()
983{
984#if CPU(X86_64) | CPU(ARM64)
985 double arg1 = 0;
986 double arg2 = 0;
987 const double zero = -0;
988
989 const double chosenDouble = 6.00000059604644775390625;
990 CHECK_EQ(static_cast<double>(static_cast<float>(chosenDouble)) == chosenDouble, false);
991
992 auto sel = compile([&] (CCallHelpers& jit) {
993 jit.emitFunctionPrologue();
994 jit.loadDouble(CCallHelpers::TrustedImmPtr(&zero), FPRInfo::returnValueFPR);
995 jit.loadDouble(CCallHelpers::TrustedImmPtr(&arg1), FPRInfo::fpRegT1);
996 jit.loadDouble(CCallHelpers::TrustedImmPtr(&arg2), FPRInfo::fpRegT2);
997
998 jit.move(MacroAssembler::TrustedImm64(-1), GPRInfo::regT0);
999 jit.moveDoubleConditionally64(MacroAssembler::Equal, GPRInfo::regT0, GPRInfo::regT0, FPRInfo::fpRegT1, FPRInfo::fpRegT2, FPRInfo::returnValueFPR);
1000
1001 jit.emitFunctionEpilogue();
1002 jit.ret();
1003 });
1004
1005 arg1 = chosenDouble;
1006 arg2 = 43;
1007 CHECK_EQ(invoke<double>(sel), chosenDouble);
1008
1009 arg1 = 43;
1010 arg2 = chosenDouble;
1011 CHECK_EQ(invoke<double>(sel), 43.0);
1012
1013#endif
1014}
1015
1016static void testCagePreservesPACFailureBit()
1017{
1018#if GIGACAGE_ENABLED
1019 ASSERT(!Gigacage::isDisablingPrimitiveGigacageDisabled());
1020 auto cage = compile([] (CCallHelpers& jit) {
1021 jit.emitFunctionPrologue();
1022 jit.cageConditionally(Gigacage::Primitive, GPRInfo::argumentGPR0, GPRInfo::argumentGPR1, GPRInfo::argumentGPR2);
1023 jit.move(GPRInfo::argumentGPR0, GPRInfo::returnValueGPR);
1024 jit.emitFunctionEpilogue();
1025 jit.ret();
1026 });
1027
1028 void* ptr = Gigacage::tryMalloc(Gigacage::Primitive, 1);
1029 void* taggedPtr = tagArrayPtr(ptr, 1);
1030 ASSERT(hasOneBitSet(Gigacage::size(Gigacage::Primitive) << 2));
1031 void* notCagedPtr = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(ptr) + (Gigacage::size(Gigacage::Primitive) << 2));
1032 CHECK_NOT_EQ(Gigacage::caged(Gigacage::Primitive, notCagedPtr), notCagedPtr);
1033 void* taggedNotCagedPtr = tagArrayPtr(notCagedPtr, 1);
1034
1035 if (isARM64E()) {
1036 // FIXME: This won't work if authentication failures trap but I don't know how to test for that right now.
1037 CHECK_NOT_EQ(invoke<void*>(cage, taggedPtr, 2), ptr);
1038 CHECK_EQ(invoke<void*>(cage, taggedNotCagedPtr, 1), untagArrayPtr(taggedPtr, 2));
1039 } else
1040 CHECK_EQ(invoke<void*>(cage, taggedPtr, 2), ptr);
1041
1042 CHECK_EQ(invoke<void*>(cage, taggedPtr, 1), ptr);
1043
1044 auto cageWithoutAuthentication = compile([] (CCallHelpers& jit) {
1045 jit.emitFunctionPrologue();
1046 jit.cageWithoutUntagging(Gigacage::Primitive, GPRInfo::argumentGPR0);
1047 jit.move(GPRInfo::argumentGPR0, GPRInfo::returnValueGPR);
1048 jit.emitFunctionEpilogue();
1049 jit.ret();
1050 });
1051
1052 CHECK_EQ(invoke<void*>(cageWithoutAuthentication, taggedPtr), taggedPtr);
1053 if (isARM64E()) {
1054 // FIXME: This won't work if authentication failures trap but I don't know how to test for that right now.
1055 CHECK_NOT_EQ(invoke<void*>(cageWithoutAuthentication, taggedNotCagedPtr), taggedNotCagedPtr);
1056 CHECK_NOT_EQ(untagArrayPtr(invoke<void*>(cageWithoutAuthentication, taggedNotCagedPtr), 1), notCagedPtr);
1057 CHECK_NOT_EQ(invoke<void*>(cageWithoutAuthentication, taggedNotCagedPtr), taggedPtr);
1058 CHECK_NOT_EQ(untagArrayPtr(invoke<void*>(cageWithoutAuthentication, taggedNotCagedPtr), 1), ptr);
1059 }
1060
1061 Gigacage::free(Gigacage::Primitive, ptr);
1062#endif
1063}
1064
1065#define RUN(test) do { \
1066 if (!shouldRun(#test)) \
1067 break; \
1068 numberOfTests++; \
1069 tasks.append( \
1070 createSharedTask<void()>( \
1071 [&] () { \
1072 dataLog(#test "...\n"); \
1073 test; \
1074 dataLog(#test ": OK!\n"); \
1075 })); \
1076 } while (false);
1077
1078void run(const char* filter)
1079{
1080 JSC::initializeThreading();
1081 unsigned numberOfTests = 0;
1082
1083 Deque<RefPtr<SharedTask<void()>>> tasks;
1084
1085 auto shouldRun = [&] (const char* testName) -> bool {
1086 return !filter || WTF::findIgnoringASCIICaseWithoutLength(testName, filter) != WTF::notFound;
1087 };
1088
1089 RUN(testSimple());
1090 RUN(testGetEffectiveAddress(0xff00, 42, 8, CCallHelpers::TimesEight));
1091 RUN(testGetEffectiveAddress(0xff00, -200, -300, CCallHelpers::TimesEight));
1092 RUN(testBranchTruncateDoubleToInt32(0, 0));
1093 RUN(testBranchTruncateDoubleToInt32(42, 42));
1094 RUN(testBranchTruncateDoubleToInt32(42.7, 42));
1095 RUN(testBranchTruncateDoubleToInt32(-1234, -1234));
1096 RUN(testBranchTruncateDoubleToInt32(-1234.56, -1234));
1097 RUN(testBranchTruncateDoubleToInt32(std::numeric_limits<double>::infinity(), 0));
1098 RUN(testBranchTruncateDoubleToInt32(-std::numeric_limits<double>::infinity(), 0));
1099 RUN(testBranchTruncateDoubleToInt32(std::numeric_limits<double>::quiet_NaN(), 0));
1100 RUN(testBranchTruncateDoubleToInt32(std::numeric_limits<double>::signaling_NaN(), 0));
1101 RUN(testBranchTruncateDoubleToInt32(std::numeric_limits<double>::max(), 0));
1102 RUN(testBranchTruncateDoubleToInt32(-std::numeric_limits<double>::max(), 0));
1103 // We run this last one to make sure that we don't use flags that were not
1104 // reset to check a conversion result
1105 RUN(testBranchTruncateDoubleToInt32(123, 123));
1106
1107 RUN(testCompareDouble(MacroAssembler::DoubleEqual));
1108 RUN(testCompareDouble(MacroAssembler::DoubleNotEqual));
1109 RUN(testCompareDouble(MacroAssembler::DoubleGreaterThan));
1110 RUN(testCompareDouble(MacroAssembler::DoubleGreaterThanOrEqual));
1111 RUN(testCompareDouble(MacroAssembler::DoubleLessThan));
1112 RUN(testCompareDouble(MacroAssembler::DoubleLessThanOrEqual));
1113 RUN(testCompareDouble(MacroAssembler::DoubleEqualOrUnordered));
1114 RUN(testCompareDouble(MacroAssembler::DoubleNotEqualOrUnordered));
1115 RUN(testCompareDouble(MacroAssembler::DoubleGreaterThanOrUnordered));
1116 RUN(testCompareDouble(MacroAssembler::DoubleGreaterThanOrEqualOrUnordered));
1117 RUN(testCompareDouble(MacroAssembler::DoubleLessThanOrUnordered));
1118 RUN(testCompareDouble(MacroAssembler::DoubleLessThanOrEqualOrUnordered));
1119 RUN(testMul32WithImmediates());
1120
1121#if CPU(X86) || CPU(X86_64) || CPU(ARM64)
1122 RUN(testCompareFloat(MacroAssembler::DoubleEqual));
1123 RUN(testCompareFloat(MacroAssembler::DoubleNotEqual));
1124 RUN(testCompareFloat(MacroAssembler::DoubleGreaterThan));
1125 RUN(testCompareFloat(MacroAssembler::DoubleGreaterThanOrEqual));
1126 RUN(testCompareFloat(MacroAssembler::DoubleLessThan));
1127 RUN(testCompareFloat(MacroAssembler::DoubleLessThanOrEqual));
1128 RUN(testCompareFloat(MacroAssembler::DoubleEqualOrUnordered));
1129 RUN(testCompareFloat(MacroAssembler::DoubleNotEqualOrUnordered));
1130 RUN(testCompareFloat(MacroAssembler::DoubleGreaterThanOrUnordered));
1131 RUN(testCompareFloat(MacroAssembler::DoubleGreaterThanOrEqualOrUnordered));
1132 RUN(testCompareFloat(MacroAssembler::DoubleLessThanOrUnordered));
1133 RUN(testCompareFloat(MacroAssembler::DoubleLessThanOrEqualOrUnordered));
1134#endif
1135
1136#if ENABLE(MASM_PROBE)
1137 RUN(testProbeReadsArgumentRegisters());
1138 RUN(testProbeWritesArgumentRegisters());
1139 RUN(testProbePreservesGPRS());
1140 RUN(testProbeModifiesStackPointerToInsideProbeStateOnStack());
1141 RUN(testProbeModifiesStackPointerToNBytesBelowSP());
1142 RUN(testProbeModifiesProgramCounter());
1143 RUN(testProbeModifiesStackValues());
1144#endif // ENABLE(MASM_PROBE)
1145
1146 RUN(testByteSwap());
1147 RUN(testMoveDoubleConditionally32());
1148 RUN(testMoveDoubleConditionally64());
1149
1150 RUN(testCagePreservesPACFailureBit());
1151
1152 if (tasks.isEmpty())
1153 usage();
1154
1155 Lock lock;
1156
1157 Vector<Ref<Thread>> threads;
1158 for (unsigned i = filter ? 1 : WTF::numberOfProcessorCores(); i--;) {
1159 threads.append(
1160 Thread::create(
1161 "testmasm thread",
1162 [&] () {
1163 for (;;) {
1164 RefPtr<SharedTask<void()>> task;
1165 {
1166 LockHolder locker(lock);
1167 if (tasks.isEmpty())
1168 return;
1169 task = tasks.takeFirst();
1170 }
1171
1172 task->run();
1173 }
1174 }));
1175 }
1176
1177 for (auto& thread : threads)
1178 thread->waitForCompletion();
1179 crashLock.lock();
1180 dataLog("Completed ", numberOfTests, " tests\n");
1181}
1182
1183} // anonymous namespace
1184
1185#else // not ENABLE(JIT)
1186
1187static void run(const char*)
1188{
1189 dataLog("JIT is not enabled.\n");
1190}
1191
1192#endif // ENABLE(JIT)
1193
1194int main(int argc, char** argv)
1195{
1196 const char* filter = nullptr;
1197 switch (argc) {
1198 case 1:
1199 break;
1200 case 2:
1201 filter = argv[1];
1202 break;
1203 default:
1204 usage();
1205 break;
1206 }
1207
1208 run(filter);
1209 return 0;
1210}
1211
1212#if OS(WINDOWS)
1213extern "C" __declspec(dllexport) int WINAPI dllLauncherEntryPoint(int argc, const char* argv[])
1214{
1215 return main(argc, const_cast<char**>(argv));
1216}
1217#endif
1218