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