1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  * vim: set ts=8 sts=4 et sw=4 tw=99:
3  */
4 /* This Source Code Form is subject to the terms of the Mozilla Public
5  * License, v. 2.0. If a copy of the MPL was not distributed with this
6  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 
8 #include "jit/IonAnalysis.h"
9 #include "jit/Linker.h"
10 #include "jit/MacroAssembler.h"
11 #include "jit/MIRGenerator.h"
12 #include "jit/MIRGraph.h"
13 #include "jit/ValueNumbering.h"
14 #include "js/Value.h"
15 
16 #include "jsapi-tests/tests.h"
17 
18 #include "jit/MacroAssembler-inl.h"
19 
20 using namespace js;
21 using namespace js::jit;
22 
23 using mozilla::PositiveInfinity;
24 using mozilla::NegativeInfinity;
25 
26 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
27 
28 typedef void (*EnterTest)();
29 
Prepare(MacroAssembler & masm)30 static bool Prepare(MacroAssembler& masm)
31 {
32     AllocatableRegisterSet regs(RegisterSet::Volatile());
33     LiveRegisterSet save(regs.asLiveSet());
34     masm.PushRegsInMask(save);
35     return true;
36 }
37 
Execute(JSContext * cx,MacroAssembler & masm)38 static bool Execute(JSContext* cx, MacroAssembler& masm)
39 {
40     AllocatableRegisterSet regs(RegisterSet::Volatile());
41     LiveRegisterSet save(regs.asLiveSet());
42     masm.PopRegsInMask(save);
43     masm.ret(); // Add return statement to be sure.
44 
45     if (masm.oom())
46         return false;
47 
48     Linker linker(masm);
49     JitCode* code = linker.newCode<CanGC>(cx, OTHER_CODE);
50     if (!code)
51         return false;
52     if (!ExecutableAllocator::makeExecutable(code->raw(), code->bufferSize()))
53         return false;
54 
55     EnterTest test = code->as<EnterTest>();
56     test();
57     return true;
58 }
59 
BEGIN_TEST(testJitMacroAssembler_truncateDoubleToInt64)60 BEGIN_TEST(testJitMacroAssembler_truncateDoubleToInt64)
61 {
62     MacroAssembler masm(cx);
63 
64     if (!Prepare(masm))
65         return false;
66 
67     AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
68     AllocatableFloatRegisterSet allFloatRegs(FloatRegisterSet::All());
69     FloatRegister input = allFloatRegs.takeAny();
70 #ifdef JS_NUNBOX32
71     Register64 output(allRegs.takeAny(), allRegs.takeAny());
72 #else
73     Register64 output(allRegs.takeAny());
74 #endif
75     Register temp = allRegs.takeAny();
76 
77     masm.reserveStack(sizeof(int32_t));
78 
79 #define TEST(INPUT, OUTPUT)                                                     \
80     {                                                                           \
81         Label next;                                                             \
82         masm.loadConstantDouble(double(INPUT), input);                          \
83         masm.storeDouble(input, Operand(esp, 0));                               \
84         masm.truncateDoubleToInt64(Address(esp, 0), Address(esp, 0), temp);     \
85         masm.branch64(Assembler::Equal, Address(esp, 0), Imm64(OUTPUT), &next); \
86         masm.printf("truncateDoubleToInt64("#INPUT") failed\n");                \
87         masm.breakpoint();                                                      \
88         masm.bind(&next);                                                       \
89     }
90 
91     TEST(0, 0);
92     TEST(-0, 0);
93     TEST(1, 1);
94     TEST(9223372036854774784.0, 9223372036854774784);
95     TEST(-9223372036854775808.0, 0x8000000000000000);
96     TEST(9223372036854775808.0, 0x8000000000000000);
97     TEST(JS::GenericNaN(), 0x8000000000000000);
98     TEST(PositiveInfinity<double>(), 0x8000000000000000);
99     TEST(NegativeInfinity<double>(), 0x8000000000000000);
100 #undef TEST
101 
102     masm.freeStack(sizeof(int32_t));
103 
104     return Execute(cx, masm);
105 }
106 END_TEST(testJitMacroAssembler_truncateDoubleToInt64)
107 
BEGIN_TEST(testJitMacroAssembler_truncateDoubleToUInt64)108 BEGIN_TEST(testJitMacroAssembler_truncateDoubleToUInt64)
109 {
110     MacroAssembler masm(cx);
111 
112     if (!Prepare(masm))
113         return false;
114 
115     AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
116     AllocatableFloatRegisterSet allFloatRegs(FloatRegisterSet::All());
117     FloatRegister input = allFloatRegs.takeAny();
118     FloatRegister floatTemp = allFloatRegs.takeAny();
119 #ifdef JS_NUNBOX32
120     Register64 output(allRegs.takeAny(), allRegs.takeAny());
121 #else
122     Register64 output(allRegs.takeAny());
123 #endif
124     Register temp = allRegs.takeAny();
125 
126     masm.reserveStack(sizeof(int32_t));
127 
128 #define TEST(INPUT, OUTPUT)                                                                \
129     {                                                                                      \
130         Label next;                                                                        \
131         masm.loadConstantDouble(double(INPUT), input);                                     \
132         masm.storeDouble(input, Operand(esp, 0));                                          \
133         masm.truncateDoubleToUInt64(Address(esp, 0), Address(esp, 0), temp, floatTemp);    \
134         masm.branch64(Assembler::Equal, Address(esp, 0), Imm64(OUTPUT), &next);            \
135         masm.printf("truncateDoubleToUInt64("#INPUT") failed\n");                          \
136         masm.breakpoint();                                                                 \
137         masm.bind(&next);                                                                  \
138     }
139 
140     TEST(0, 0);
141     TEST(1, 1);
142     TEST(9223372036854774784.0, 9223372036854774784);
143     TEST((uint64_t)0x8000000000000000, 0x8000000000000000);
144     TEST((uint64_t)0x8000000000000001, 0x8000000000000000);
145     TEST((uint64_t)0x8006004000000001, 0x8006004000000000);
146     TEST(-0.0, 0);
147     TEST(-0.5, 0);
148     TEST(-0.99, 0);
149     TEST(JS::GenericNaN(), 0x8000000000000000);
150     TEST(PositiveInfinity<double>(), 0x8000000000000000);
151     TEST(NegativeInfinity<double>(), 0x8000000000000000);
152 #undef TEST
153 
154     masm.freeStack(sizeof(int32_t));
155 
156     return Execute(cx, masm);
157 }
158 END_TEST(testJitMacroAssembler_truncateDoubleToUInt64)
159 
BEGIN_TEST(testJitMacroAssembler_branchDoubleNotInInt64Range)160 BEGIN_TEST(testJitMacroAssembler_branchDoubleNotInInt64Range)
161 {
162     MacroAssembler masm(cx);
163 
164     if (!Prepare(masm))
165         return false;
166 
167     AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
168     AllocatableFloatRegisterSet allFloatRegs(FloatRegisterSet::All());
169     FloatRegister input = allFloatRegs.takeAny();
170 #ifdef JS_NUNBOX32
171     Register64 output(allRegs.takeAny(), allRegs.takeAny());
172 #else
173     Register64 output(allRegs.takeAny());
174 #endif
175     Register temp = allRegs.takeAny();
176 
177     masm.reserveStack(sizeof(int32_t));
178 
179 #define TEST(INPUT, OUTPUT)                                                                 \
180     {                                                                                       \
181         Label next;                                                                         \
182         masm.loadConstantDouble(double(INPUT), input);                                      \
183         masm.storeDouble(input, Operand(esp, 0));                                           \
184         if (OUTPUT) {                                                                       \
185             masm.branchDoubleNotInInt64Range(Address(esp, 0), temp, &next);                 \
186         } else {                                                                            \
187             Label fail;                                                                     \
188             masm.branchDoubleNotInInt64Range(Address(esp, 0), temp, &fail);                 \
189             masm.jump(&next);                                                               \
190             masm.bind(&fail);                                                               \
191         }                                                                                   \
192         masm.printf("branchDoubleNotInInt64Range("#INPUT") failed\n");                      \
193         masm.breakpoint();                                                                  \
194         masm.bind(&next);                                                                   \
195     }
196 
197     TEST(0, false);
198     TEST(-0, false);
199     TEST(1, false);
200     TEST(9223372036854774784.0, false);
201     TEST(-9223372036854775808.0, true);
202     TEST(9223372036854775808.0, true);
203     TEST(JS::GenericNaN(), true);
204     TEST(PositiveInfinity<double>(), true);
205     TEST(NegativeInfinity<double>(), true);
206 #undef TEST
207 
208     masm.freeStack(sizeof(int32_t));
209 
210     return Execute(cx, masm);
211 }
212 END_TEST(testJitMacroAssembler_branchDoubleNotInInt64Range)
213 
BEGIN_TEST(testJitMacroAssembler_branchDoubleNotInUInt64Range)214 BEGIN_TEST(testJitMacroAssembler_branchDoubleNotInUInt64Range)
215 {
216     MacroAssembler masm(cx);
217 
218     if (!Prepare(masm))
219         return false;
220 
221     AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
222     AllocatableFloatRegisterSet allFloatRegs(FloatRegisterSet::All());
223     FloatRegister input = allFloatRegs.takeAny();
224 #ifdef JS_NUNBOX32
225     Register64 output(allRegs.takeAny(), allRegs.takeAny());
226 #else
227     Register64 output(allRegs.takeAny());
228 #endif
229     Register temp = allRegs.takeAny();
230 
231     masm.reserveStack(sizeof(int32_t));
232 
233 #define TEST(INPUT, OUTPUT)                                                                 \
234     {                                                                                       \
235         Label next;                                                                         \
236         masm.loadConstantDouble(double(INPUT), input);                                      \
237         masm.storeDouble(input, Operand(esp, 0));                                           \
238         if (OUTPUT) {                                                                       \
239             masm.branchDoubleNotInUInt64Range(Address(esp, 0), temp, &next);                \
240         } else {                                                                            \
241             Label fail;                                                                     \
242             masm.branchDoubleNotInUInt64Range(Address(esp, 0), temp, &fail);                \
243             masm.jump(&next);                                                               \
244             masm.bind(&fail);                                                               \
245         }                                                                                   \
246         masm.printf("branchDoubleNotInUInt64Range("#INPUT") failed\n");                     \
247         masm.breakpoint();                                                                  \
248         masm.bind(&next);                                                                   \
249     }
250 
251     TEST(0, false);
252     TEST(1, false);
253     TEST(9223372036854774784.0, false);
254     TEST((uint64_t)0x8000000000000000, false);
255     TEST((uint64_t)0x8000000000000001, false);
256     TEST((uint64_t)0x8006004000000001, false);
257     TEST(-0.0, true);
258     TEST(-0.5, true);
259     TEST(-0.99, true);
260     TEST(JS::GenericNaN(), true);
261     TEST(PositiveInfinity<double>(), true);
262     TEST(NegativeInfinity<double>(), true);
263 #undef TEST
264 
265     masm.freeStack(sizeof(int32_t));
266 
267     return Execute(cx, masm);
268 }
269 END_TEST(testJitMacroAssembler_branchDoubleNotInUInt64Range)
270 
BEGIN_TEST(testJitMacroAssembler_lshift64)271 BEGIN_TEST(testJitMacroAssembler_lshift64)
272 {
273     MacroAssembler masm(cx);
274 
275     if (!Prepare(masm))
276         return false;
277 
278     AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
279     AllocatableFloatRegisterSet allFloatRegs(FloatRegisterSet::All());
280 #if defined(JS_CODEGEN_X86)
281     Register shift = ecx;
282     allRegs.take(shift);
283 #elif defined(JS_CODEGEN_X64)
284     Register shift = rcx;
285     allRegs.take(shift);
286 #else
287     Register shift = allRegs.takeAny();
288 #endif
289 
290 #ifdef JS_NUNBOX32
291     Register64 input(allRegs.takeAny(), allRegs.takeAny());
292 #else
293     Register64 input(allRegs.takeAny());
294 #endif
295 
296     masm.reserveStack(sizeof(int32_t));
297 
298 #define TEST(SHIFT, INPUT, OUTPUT)                                                          \
299     {                                                                                       \
300         Label next;                                                                         \
301         masm.move64(Imm64(INPUT), input);                                                   \
302         masm.move32(Imm32(SHIFT), shift);                                                   \
303         masm.lshift64(shift, input);                                                        \
304         masm.branch64(Assembler::Equal, input, Imm64(OUTPUT), &next);                       \
305         masm.printf("lshift64("#SHIFT", "#INPUT") failed\n");                               \
306         masm.breakpoint();                                                                  \
307         masm.bind(&next);                                                                   \
308     }                                                                                       \
309     {                                                                                       \
310         Label next;                                                                         \
311         masm.move64(Imm64(INPUT), input);                                                   \
312         masm.lshift64(Imm32(SHIFT & 0x3f), input);                                          \
313         masm.branch64(Assembler::Equal, input, Imm64(OUTPUT), &next);                       \
314         masm.printf("lshift64(Imm32("#SHIFT"&0x3f), "#INPUT") failed\n");                   \
315         masm.breakpoint();                                                                  \
316         masm.bind(&next);                                                                   \
317     }
318 
319     TEST(0, 1, 1);
320     TEST(1, 1, 2);
321     TEST(2, 1, 4);
322     TEST(32, 1, 0x0000000100000000);
323     TEST(33, 1, 0x0000000200000000);
324     TEST(0, -1, 0xffffffffffffffff);
325     TEST(1, -1, 0xfffffffffffffffe);
326     TEST(2, -1, 0xfffffffffffffffc);
327     TEST(32, -1, 0xffffffff00000000);
328     TEST(0xffffffff, 1, 0x8000000000000000);
329     TEST(0xfffffffe, 1, 0x4000000000000000);
330     TEST(0xfffffffd, 1, 0x2000000000000000);
331     TEST(0x80000001, 1, 2);
332 #undef TEST
333 
334     masm.freeStack(sizeof(int32_t));
335 
336     return Execute(cx, masm);
337 }
338 END_TEST(testJitMacroAssembler_lshift64)
339 
BEGIN_TEST(testJitMacroAssembler_rshift64Arithmetic)340 BEGIN_TEST(testJitMacroAssembler_rshift64Arithmetic)
341 {
342     MacroAssembler masm(cx);
343 
344     if (!Prepare(masm))
345         return false;
346 
347     AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
348     AllocatableFloatRegisterSet allFloatRegs(FloatRegisterSet::All());
349 #if defined(JS_CODEGEN_X86)
350     Register shift = ecx;
351     allRegs.take(shift);
352 #elif defined(JS_CODEGEN_X64)
353     Register shift = rcx;
354     allRegs.take(shift);
355 #else
356     Register shift = allRegs.takeAny();
357 #endif
358 
359 #ifdef JS_NUNBOX32
360     Register64 input(allRegs.takeAny(), allRegs.takeAny());
361 #else
362     Register64 input(allRegs.takeAny());
363 #endif
364 
365     masm.reserveStack(sizeof(int32_t));
366 
367 #define TEST(SHIFT, INPUT, OUTPUT)                                                          \
368     {                                                                                       \
369         Label next;                                                                         \
370         masm.move64(Imm64(INPUT), input);                                                   \
371         masm.move32(Imm32(SHIFT), shift);                                                   \
372         masm.rshift64Arithmetic(shift, input);                                              \
373         masm.branch64(Assembler::Equal, input, Imm64(OUTPUT), &next);                       \
374         masm.printf("rshift64Arithmetic("#SHIFT", "#INPUT") failed\n");                     \
375         masm.breakpoint();                                                                  \
376         masm.bind(&next);                                                                   \
377     }                                                                                       \
378     {                                                                                       \
379         Label next;                                                                         \
380         masm.move64(Imm64(INPUT), input);                                                   \
381         masm.rshift64Arithmetic(Imm32(SHIFT & 0x3f), input);                                \
382         masm.branch64(Assembler::Equal, input, Imm64(OUTPUT), &next);                       \
383         masm.printf("rshift64Arithmetic(Imm32("#SHIFT"&0x3f), "#INPUT") failed\n");         \
384         masm.breakpoint();                                                                  \
385         masm.bind(&next);                                                                   \
386     }
387 
388     TEST(0, 0x4000000000000000, 0x4000000000000000);
389     TEST(1, 0x4000000000000000, 0x2000000000000000);
390     TEST(2, 0x4000000000000000, 0x1000000000000000);
391     TEST(32, 0x4000000000000000, 0x0000000040000000);
392     TEST(0, 0x8000000000000000, 0x8000000000000000);
393     TEST(1, 0x8000000000000000, 0xc000000000000000);
394     TEST(2, 0x8000000000000000, 0xe000000000000000);
395     TEST(32, 0x8000000000000000, 0xffffffff80000000);
396     TEST(0xffffffff, 0x8000000000000000, 0xffffffffffffffff);
397     TEST(0xfffffffe, 0x8000000000000000, 0xfffffffffffffffe);
398     TEST(0xfffffffd, 0x8000000000000000, 0xfffffffffffffffc);
399     TEST(0x80000001, 0x8000000000000000, 0xc000000000000000);
400 #undef TEST
401 
402     masm.freeStack(sizeof(int32_t));
403 
404     return Execute(cx, masm);
405 }
406 END_TEST(testJitMacroAssembler_rshift64Arithmetic)
407 
BEGIN_TEST(testJitMacroAssembler_rshift64)408 BEGIN_TEST(testJitMacroAssembler_rshift64)
409 {
410     MacroAssembler masm(cx);
411 
412     if (!Prepare(masm))
413         return false;
414 
415     AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
416     AllocatableFloatRegisterSet allFloatRegs(FloatRegisterSet::All());
417 #if defined(JS_CODEGEN_X86)
418     Register shift = ecx;
419     allRegs.take(shift);
420 #elif defined(JS_CODEGEN_X64)
421     Register shift = rcx;
422     allRegs.take(shift);
423 #else
424     Register shift = allRegs.takeAny();
425 #endif
426 
427 #ifdef JS_NUNBOX32
428     Register64 input(allRegs.takeAny(), allRegs.takeAny());
429 #else
430     Register64 input(allRegs.takeAny());
431 #endif
432 
433     masm.reserveStack(sizeof(int32_t));
434 
435 #define TEST(SHIFT, INPUT, OUTPUT)                                                          \
436     {                                                                                       \
437         Label next;                                                                         \
438         masm.move64(Imm64(INPUT), input);                                                   \
439         masm.move32(Imm32(SHIFT), shift);                                                   \
440         masm.rshift64(shift, input);                                                        \
441         masm.branch64(Assembler::Equal, input, Imm64(OUTPUT), &next);                       \
442         masm.printf("rshift64("#SHIFT", "#INPUT") failed\n");                               \
443         masm.breakpoint();                                                                  \
444         masm.bind(&next);                                                                   \
445     }                                                                                       \
446     {                                                                                       \
447         Label next;                                                                         \
448         masm.move64(Imm64(INPUT), input);                                                   \
449         masm.rshift64(Imm32(SHIFT & 0x3f), input);                                          \
450         masm.branch64(Assembler::Equal, input, Imm64(OUTPUT), &next);                       \
451         masm.printf("rshift64(Imm32("#SHIFT"&0x3f), "#INPUT") failed\n");                   \
452         masm.breakpoint();                                                                  \
453         masm.bind(&next);                                                                   \
454     }
455 
456     TEST(0, 0x4000000000000000, 0x4000000000000000);
457     TEST(1, 0x4000000000000000, 0x2000000000000000);
458     TEST(2, 0x4000000000000000, 0x1000000000000000);
459     TEST(32, 0x4000000000000000, 0x0000000040000000);
460     TEST(0, 0x8000000000000000, 0x8000000000000000);
461     TEST(1, 0x8000000000000000, 0x4000000000000000);
462     TEST(2, 0x8000000000000000, 0x2000000000000000);
463     TEST(32, 0x8000000000000000, 0x0000000080000000);
464     TEST(0xffffffff, 0x8000000000000000, 0x0000000000000001);
465     TEST(0xfffffffe, 0x8000000000000000, 0x0000000000000002);
466     TEST(0xfffffffd, 0x8000000000000000, 0x0000000000000004);
467     TEST(0x80000001, 0x8000000000000000, 0x4000000000000000);
468 #undef TEST
469 
470     masm.freeStack(sizeof(int32_t));
471 
472     return Execute(cx, masm);
473 }
474 END_TEST(testJitMacroAssembler_rshift64)
475 
476 #endif
477