Gardening: fixed C Loop build after r219790.
[WebKit-https.git] / Source / JavaScriptCore / assembler / testmasm.cpp
1 /*
2  * Copyright (C) 2017 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 <wtf/Compiler.h>
35 #include <wtf/DataLog.h>
36 #include <wtf/Lock.h>
37 #include <wtf/NumberOfCores.h>
38 #include <wtf/Threading.h>
39
40 // We don't have a NO_RETURN_DUE_TO_EXIT, nor should we. That's ridiculous.
41 static bool hiddenTruthBecauseNoReturnIsStupid() { return true; }
42
43 static void usage()
44 {
45     dataLog("Usage: testmasm [<filter>]\n");
46     if (hiddenTruthBecauseNoReturnIsStupid())
47         exit(1);
48 }
49
50 #if ENABLE(JIT)
51
52 using namespace JSC;
53
54 namespace {
55
56 StaticLock crashLock;
57
58 typedef std::function<void(CCallHelpers&)> Generator;
59
60 template<typename T> T nextID(T id) { return static_cast<T>(id + 1); }
61
62 #define TESTWORD64 0x0c0defefebeef000
63 #define TESTWORD32 0x0beef000
64
65 #define testWord32(x) (TESTWORD32 + static_cast<uint32_t>(x))
66 #define testWord64(x) (TESTWORD64 + static_cast<uint64_t>(x))
67
68 #if ENABLE(JSVALUE64)
69 #define testWord(x) testWord64(x)
70 #else
71 #define testWord(x) testWord32(x)
72 #endif
73
74 // Nothing fancy for now; we just use the existing WTF assertion machinery.
75 #define CHECK(x) do {                                                   \
76         if (!!(x))                                                      \
77             break;                                                      \
78         crashLock.lock();                                               \
79         WTFReportAssertionFailure(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, #x); \
80         CRASH();                                                        \
81     } while (false)
82
83 #if ENABLE(MASM_PROBE)
84 bool isPC(MacroAssembler::RegisterID id)
85 {
86 #if CPU(ARM_THUMB2) || CPU(ARM_TRADITIONAL)
87     return id == ARMRegisters::pc;
88 #else
89     UNUSED_PARAM(id);
90     return false;
91 #endif
92 }
93
94 bool isSP(MacroAssembler::RegisterID id)
95 {
96     return id == MacroAssembler::stackPointerRegister;
97 }
98
99 bool isFP(MacroAssembler::RegisterID id)
100 {
101     return id == MacroAssembler::framePointerRegister;
102 }
103 #endif // ENABLE(MASM_PROBE)
104
105 MacroAssemblerCodeRef compile(Generator generate)
106 {
107     CCallHelpers jit;
108     generate(jit);
109     LinkBuffer linkBuffer(jit, nullptr);
110     return FINALIZE_CODE(linkBuffer, ("testmasm compilation"));
111 }
112
113 template<typename T, typename... Arguments>
114 T invoke(MacroAssemblerCodeRef code, Arguments... arguments)
115 {
116     T (*function)(Arguments...) = bitwise_cast<T(*)(Arguments...)>(code.code().executableAddress());
117     return function(arguments...);
118 }
119
120 template<typename T, typename... Arguments>
121 T compileAndRun(Generator generator, Arguments... arguments)
122 {
123     return invoke<T>(compile(generator), arguments...);
124 }
125
126 void testSimple()
127 {
128     CHECK(compileAndRun<int>([] (CCallHelpers& jit) {
129         jit.emitFunctionPrologue();
130         jit.move(CCallHelpers::TrustedImm32(42), GPRInfo::returnValueGPR);
131         jit.emitFunctionEpilogue();
132         jit.ret();
133     }) == 42);
134 }
135
136 #if ENABLE(MASM_PROBE)
137 void testProbeReadsArgumentRegisters()
138 {
139     bool success = true;
140     compileAndRun<void>([&] (CCallHelpers& jit) {
141         jit.emitFunctionPrologue();
142
143         jit.move(CCallHelpers::TrustedImm32(testWord(0)), GPRInfo::argumentGPR0);
144         jit.convertInt32ToDouble(GPRInfo::argumentGPR0, FPRInfo::fpRegT0);
145         jit.move(CCallHelpers::TrustedImm32(testWord(1)), GPRInfo::argumentGPR0);
146         jit.convertInt32ToDouble(GPRInfo::argumentGPR0, FPRInfo::fpRegT1);
147 #if ENABLE(JSVALUE64)
148         jit.move(CCallHelpers::TrustedImm64(testWord(0)), GPRInfo::argumentGPR0);
149         jit.move(CCallHelpers::TrustedImm64(testWord(1)), GPRInfo::argumentGPR1);
150         jit.move(CCallHelpers::TrustedImm64(testWord(2)), GPRInfo::argumentGPR2);
151         jit.move(CCallHelpers::TrustedImm64(testWord(3)), GPRInfo::argumentGPR3);
152 #else
153         jit.move(CCallHelpers::TrustedImm32(testWord(0)), GPRInfo::argumentGPR0);
154         jit.move(CCallHelpers::TrustedImm32(testWord(1)), GPRInfo::argumentGPR1);
155         jit.move(CCallHelpers::TrustedImm32(testWord(2)), GPRInfo::argumentGPR2);
156         jit.move(CCallHelpers::TrustedImm32(testWord(3)), GPRInfo::argumentGPR3);
157 #endif
158
159         jit.probe([&] (ProbeContext* context) {
160             success = success && context->gpr(GPRInfo::argumentGPR0) == testWord(0);
161             success = success && context->gpr(GPRInfo::argumentGPR1) == testWord(1);
162             success = success && context->gpr(GPRInfo::argumentGPR2) == testWord(2);
163             success = success && context->gpr(GPRInfo::argumentGPR3) == testWord(3);
164
165             success = success && context->fpr(FPRInfo::fpRegT0) == testWord32(0);
166             success = success && context->fpr(FPRInfo::fpRegT1) == testWord32(1);
167         });
168         jit.emitFunctionEpilogue();
169         jit.ret();
170     });
171     CHECK(success);
172 }
173
174 void testProbeWritesArgumentRegisters()
175 {
176     // This test relies on testProbeReadsArgumentRegisters() having already validated
177     // that we can read from argument registers. We'll use that ability to validate
178     // that our writes did take effect.
179     bool success = true;
180     compileAndRun<void>([&] (CCallHelpers& jit) {
181         jit.emitFunctionPrologue();
182
183         // Pre-initialize with non-expected values.
184 #if ENABLE(JSVALUE64)
185         jit.move(CCallHelpers::TrustedImm64(0), GPRInfo::argumentGPR0);
186         jit.move(CCallHelpers::TrustedImm64(0), GPRInfo::argumentGPR1);
187         jit.move(CCallHelpers::TrustedImm64(0), GPRInfo::argumentGPR2);
188         jit.move(CCallHelpers::TrustedImm64(0), GPRInfo::argumentGPR3);
189 #else
190         jit.move(CCallHelpers::TrustedImm32(0), GPRInfo::argumentGPR0);
191         jit.move(CCallHelpers::TrustedImm32(0), GPRInfo::argumentGPR1);
192         jit.move(CCallHelpers::TrustedImm32(0), GPRInfo::argumentGPR2);
193         jit.move(CCallHelpers::TrustedImm32(0), GPRInfo::argumentGPR3);
194 #endif
195         jit.convertInt32ToDouble(GPRInfo::argumentGPR0, FPRInfo::fpRegT0);
196         jit.convertInt32ToDouble(GPRInfo::argumentGPR0, FPRInfo::fpRegT1);
197
198         // Write expected values.
199         jit.probe([] (ProbeContext* context) {
200             context->gpr(GPRInfo::argumentGPR0) = testWord(0);
201             context->gpr(GPRInfo::argumentGPR1) = testWord(1);
202             context->gpr(GPRInfo::argumentGPR2) = testWord(2);
203             context->gpr(GPRInfo::argumentGPR3) = testWord(3);
204             
205             context->fpr(FPRInfo::fpRegT0) = testWord32(0);
206             context->fpr(FPRInfo::fpRegT1) = testWord32(1);
207         });
208
209         // Validate that expected values were written.
210         jit.probe([&] (ProbeContext* context) {
211             success = success && context->gpr(GPRInfo::argumentGPR0) == testWord(0);
212             success = success && context->gpr(GPRInfo::argumentGPR1) == testWord(1);
213             success = success && context->gpr(GPRInfo::argumentGPR2) == testWord(2);
214             success = success && context->gpr(GPRInfo::argumentGPR3) == testWord(3);
215
216             success = success && context->fpr(FPRInfo::fpRegT0) == testWord32(0);
217             success = success && context->fpr(FPRInfo::fpRegT1) == testWord32(1);
218         });
219
220         jit.emitFunctionEpilogue();
221         jit.ret();
222     });
223     CHECK(success);
224 }
225
226 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)
227 {
228     if (j > 0)
229         return testFunctionToTrashGPRs(a + 1, b + a, c + b, d + 5, e - a, f * 1.5, g ^ a, h - b, i, j - 1);
230     return a + 1;
231 }
232 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)
233 {
234     if (j > 0)
235         return testFunctionToTrashFPRs(a + 1, b + a, c + b, d + 5, e - a, f * 1.5, pow(g, a), h - b, i, j - 1);
236     return a + 1;
237 }
238
239 void testProbePreservesGPRS()
240 {
241     // This test relies on testProbeReadsArgumentRegisters() and testProbeWritesArgumentRegisters()
242     // having already validated that we can read and write from registers. We'll use these abilities
243     // to validate that the probe preserves register values.
244     bool success = true;
245     MacroAssembler::CPUState originalState;
246
247     compileAndRun<void>([&] (CCallHelpers& jit) {
248         jit.emitFunctionPrologue();
249
250         // Write expected values into the registers (except for sp, fp, and pc).
251         jit.probe([&] (ProbeContext* context) {
252             for (auto id = CCallHelpers::firstRegister(); id <= CCallHelpers::lastRegister(); id = nextID(id)) {
253                 originalState.gpr(id) = context->gpr(id);
254                 if (isPC(id) || isSP(id) || isFP(id))
255                     continue;
256                 context->gpr(id) = testWord(static_cast<int>(id));
257             }
258             for (auto id = CCallHelpers::firstFPRegister(); id <= CCallHelpers::lastFPRegister(); id = nextID(id)) {
259                 originalState.fpr(id) = context->fpr(id);
260                 context->fpr(id) = testWord(id);
261             }
262         });
263
264         // Invoke the probe to call a lot of functions and trash register values.
265         jit.probe([&] (ProbeContext*) {
266             success = success && (testFunctionToTrashGPRs(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) == 10);
267             success = success && (testFunctionToTrashFPRs(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) == 10);
268         });
269
270         // Validate that the registers have the expected values.
271         jit.probe([&] (ProbeContext* context) {
272             for (auto id = CCallHelpers::firstRegister(); id <= CCallHelpers::lastRegister(); id = nextID(id)) {
273                 if (isPC(id))
274                     continue;
275                 if (isSP(id) || isFP(id)) {
276                     success = success && context->gpr(id) == originalState.gpr(id);
277                     continue;
278                 }
279                 success = success && context->gpr(id) == testWord(id);
280             }
281             for (auto id = CCallHelpers::firstFPRegister(); id <= CCallHelpers::lastFPRegister(); id = nextID(id))
282                 success = success && context->fpr(id) == testWord(id);
283         });
284
285         // Restore the original state.
286         jit.probe([&] (ProbeContext* context) {
287             for (auto id = CCallHelpers::firstRegister(); id <= CCallHelpers::lastRegister(); id = nextID(id)) {
288                 if (isPC(id) || isSP(id) || isFP(id))
289                     continue;
290                 context->gpr(id) = originalState.gpr(id);
291             }
292             for (auto id = CCallHelpers::firstFPRegister(); id <= CCallHelpers::lastFPRegister(); id = nextID(id))
293                 context->fpr(id) = originalState.fpr(id);
294         });
295
296         jit.emitFunctionEpilogue();
297         jit.ret();
298     });
299     CHECK(success);
300 }
301
302 void testProbeModifiesStackPointer()
303 {
304     bool success = true;
305     uint8_t* originalSP;
306
307     compileAndRun<void>([&] (CCallHelpers& jit) {
308         jit.emitFunctionPrologue();
309
310         // Preserve original stack pointer and modify the sp.
311         jit.probe([&] (ProbeContext* context) {
312             originalSP = reinterpret_cast<uint8_t*>(context->sp());
313             context->sp() = originalSP - 1 * KB;
314         });
315
316         // Validate that the stack pointer has the expected value, and restore the original.
317         jit.probe([&] (ProbeContext* context) {
318             success = (reinterpret_cast<uint8_t*>(context->sp()) == (originalSP - 1 * KB));
319             context->sp() = originalSP;
320         });
321
322         // Validate that the original stack pointer was restored.
323         jit.probe([&] (ProbeContext* context) {
324             success = (context->sp() == originalSP);
325         });
326
327         jit.emitFunctionEpilogue();
328         jit.ret();
329     });
330     CHECK(success);
331 }
332
333 void testProbeModifiesProgramCounter()
334 {
335     // This test relies on testProbeReadsArgumentRegisters() and testProbeWritesArgumentRegisters()
336     // having already validated that we can read and write from registers. We'll use these abilities
337     // to validate that the probe preserves register values.
338     bool success = false;
339
340     MacroAssemblerCodeRef continuation = compile([&] (CCallHelpers& jit) {
341         // Validate that we reached the continuation.
342         jit.probe([&] (ProbeContext*) {
343             success = true;
344         });
345
346         jit.emitFunctionEpilogue();
347         jit.ret();
348     });
349
350     compileAndRun<void>([&] (CCallHelpers& jit) {
351         jit.emitFunctionPrologue();
352
353         // Write expected values into the registers.
354         jit.probe([&] (ProbeContext* context) {
355             context->pc() = continuation.code().executableAddress();
356         });
357
358         jit.breakpoint(); // We should never get here.
359     });
360     CHECK(success);
361 }
362 #endif // ENABLE(MASM_PROBE)
363
364 #define RUN(test) do {                          \
365         if (!shouldRun(#test))                  \
366             break;                              \
367         tasks.append(                           \
368             createSharedTask<void()>(           \
369                 [&] () {                        \
370                     dataLog(#test "...\n");     \
371                     test;                       \
372                     dataLog(#test ": OK!\n");   \
373                 }));                            \
374     } while (false);
375
376 void run(const char* filter)
377 {
378     JSC::initializeThreading();
379
380     Deque<RefPtr<SharedTask<void()>>> tasks;
381
382     auto shouldRun = [&] (const char* testName) -> bool {
383         return !filter || !!strcasestr(testName, filter);
384     };
385
386     RUN(testSimple());
387
388 #if ENABLE(MASM_PROBE)
389     RUN(testProbeReadsArgumentRegisters());
390     RUN(testProbeWritesArgumentRegisters());
391     RUN(testProbePreservesGPRS());
392     RUN(testProbeModifiesStackPointer());
393     RUN(testProbeModifiesProgramCounter());
394 #endif
395
396     if (tasks.isEmpty())
397         usage();
398
399     Lock lock;
400
401     Vector<RefPtr<Thread>> threads;
402     for (unsigned i = filter ? 1 : WTF::numberOfProcessorCores(); i--;) {
403         threads.append(
404             Thread::create(
405                 "testmasm thread",
406                 [&] () {
407                     for (;;) {
408                         RefPtr<SharedTask<void()>> task;
409                         {
410                             LockHolder locker(lock);
411                             if (tasks.isEmpty())
412                                 return;
413                             task = tasks.takeFirst();
414                         }
415
416                         task->run();
417                     }
418                 }));
419     }
420
421     for (RefPtr<Thread> thread : threads)
422         thread->waitForCompletion();
423     crashLock.lock();
424 }
425
426 } // anonymous namespace
427
428 #else // not ENABLE(JIT)
429
430 static void run(const char*)
431 {
432     dataLog("JIT is not enabled.\n");
433 }
434
435 #endif // ENABLE(JIT)
436
437 int main(int argc, char** argv)
438 {
439     const char* filter = nullptr;
440     switch (argc) {
441     case 1:
442         break;
443     case 2:
444         filter = argv[1];
445         break;
446     default:
447         usage();
448         break;
449     }
450
451     run(filter);
452     return 0;
453 }