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