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