1 // Copyright 2011 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are
4 // met:
5 //
6 //     * Redistributions of source code must retain the above copyright
7 //       notice, this list of conditions and the following disclaimer.
8 //     * Redistributions in binary form must reproduce the above
9 //       copyright notice, this list of conditions and the following
10 //       disclaimer in the documentation and/or other materials provided
11 //       with the distribution.
12 //     * Neither the name of Google Inc. nor the names of its
13 //       contributors may be used to endorse or promote products derived
14 //       from this software without specific prior written permission.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 
28 #include <stdlib.h>
29 
30 #include "src/v8.h"
31 
32 #include "src/base/platform/platform.h"
33 #include "src/disassembler.h"
34 #include "src/factory.h"
35 #include "src/macro-assembler.h"
36 #include "src/ostreams.h"
37 #include "src/serialize.h"
38 #include "test/cctest/cctest.h"
39 
40 using namespace v8::internal;
41 
42 
43 typedef int (*F0)();
44 typedef int (*F1)(int x);
45 typedef int (*F2)(int x, int y);
46 
47 
48 #define __ assm.
49 
TEST(AssemblerIa320)50 TEST(AssemblerIa320) {
51   CcTest::InitializeVM();
52   Isolate* isolate = reinterpret_cast<Isolate*>(CcTest::isolate());
53   HandleScope scope(isolate);
54 
55   v8::internal::byte buffer[256];
56   Assembler assm(isolate, buffer, sizeof buffer);
57 
58   __ mov(eax, Operand(esp, 4));
59   __ add(eax, Operand(esp, 8));
60   __ ret(0);
61 
62   CodeDesc desc;
63   assm.GetCode(&desc);
64   Handle<Code> code = isolate->factory()->NewCode(
65       desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
66 #ifdef OBJECT_PRINT
67   OFStream os(stdout);
68   code->Print(os);
69 #endif
70   F2 f = FUNCTION_CAST<F2>(code->entry());
71   int res = f(3, 4);
72   ::printf("f() = %d\n", res);
73   CHECK_EQ(7, res);
74 }
75 
76 
TEST(AssemblerIa321)77 TEST(AssemblerIa321) {
78   CcTest::InitializeVM();
79   Isolate* isolate = reinterpret_cast<Isolate*>(CcTest::isolate());
80   HandleScope scope(isolate);
81 
82   v8::internal::byte buffer[256];
83   Assembler assm(isolate, buffer, sizeof buffer);
84   Label L, C;
85 
86   __ mov(edx, Operand(esp, 4));
87   __ xor_(eax, eax);  // clear eax
88   __ jmp(&C);
89 
90   __ bind(&L);
91   __ add(eax, edx);
92   __ sub(edx, Immediate(1));
93 
94   __ bind(&C);
95   __ test(edx, edx);
96   __ j(not_zero, &L);
97   __ ret(0);
98 
99   CodeDesc desc;
100   assm.GetCode(&desc);
101   Handle<Code> code = isolate->factory()->NewCode(
102       desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
103 #ifdef OBJECT_PRINT
104   OFStream os(stdout);
105   code->Print(os);
106 #endif
107   F1 f = FUNCTION_CAST<F1>(code->entry());
108   int res = f(100);
109   ::printf("f() = %d\n", res);
110   CHECK_EQ(5050, res);
111 }
112 
113 
TEST(AssemblerIa322)114 TEST(AssemblerIa322) {
115   CcTest::InitializeVM();
116   Isolate* isolate = reinterpret_cast<Isolate*>(CcTest::isolate());
117   HandleScope scope(isolate);
118 
119   v8::internal::byte buffer[256];
120   Assembler assm(isolate, buffer, sizeof buffer);
121   Label L, C;
122 
123   __ mov(edx, Operand(esp, 4));
124   __ mov(eax, 1);
125   __ jmp(&C);
126 
127   __ bind(&L);
128   __ imul(eax, edx);
129   __ sub(edx, Immediate(1));
130 
131   __ bind(&C);
132   __ test(edx, edx);
133   __ j(not_zero, &L);
134   __ ret(0);
135 
136   // some relocated stuff here, not executed
137   __ mov(eax, isolate->factory()->true_value());
138   __ jmp(NULL, RelocInfo::RUNTIME_ENTRY);
139 
140   CodeDesc desc;
141   assm.GetCode(&desc);
142   Handle<Code> code = isolate->factory()->NewCode(
143       desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
144 #ifdef OBJECT_PRINT
145   OFStream os(stdout);
146   code->Print(os);
147 #endif
148   F1 f = FUNCTION_CAST<F1>(code->entry());
149   int res = f(10);
150   ::printf("f() = %d\n", res);
151   CHECK_EQ(3628800, res);
152 }
153 
154 
155 typedef int (*F3)(float x);
156 
TEST(AssemblerIa323)157 TEST(AssemblerIa323) {
158   CcTest::InitializeVM();
159 
160   Isolate* isolate = reinterpret_cast<Isolate*>(CcTest::isolate());
161   HandleScope scope(isolate);
162 
163   v8::internal::byte buffer[256];
164   Assembler assm(isolate, buffer, sizeof buffer);
165 
166   __ cvttss2si(eax, Operand(esp, 4));
167   __ ret(0);
168 
169   CodeDesc desc;
170   assm.GetCode(&desc);
171   Handle<Code> code = isolate->factory()->NewCode(
172       desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
173   // don't print the code - our disassembler can't handle cvttss2si
174   // instead print bytes
175   Disassembler::Dump(stdout,
176                      code->instruction_start(),
177                      code->instruction_start() + code->instruction_size());
178   F3 f = FUNCTION_CAST<F3>(code->entry());
179   int res = f(static_cast<float>(-3.1415));
180   ::printf("f() = %d\n", res);
181   CHECK_EQ(-3, res);
182 }
183 
184 
185 typedef int (*F4)(double x);
186 
TEST(AssemblerIa324)187 TEST(AssemblerIa324) {
188   CcTest::InitializeVM();
189 
190   Isolate* isolate = reinterpret_cast<Isolate*>(CcTest::isolate());
191   HandleScope scope(isolate);
192 
193   v8::internal::byte buffer[256];
194   Assembler assm(isolate, buffer, sizeof buffer);
195 
196   __ cvttsd2si(eax, Operand(esp, 4));
197   __ ret(0);
198 
199   CodeDesc desc;
200   assm.GetCode(&desc);
201   Handle<Code> code = isolate->factory()->NewCode(
202       desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
203   // don't print the code - our disassembler can't handle cvttsd2si
204   // instead print bytes
205   Disassembler::Dump(stdout,
206                      code->instruction_start(),
207                      code->instruction_start() + code->instruction_size());
208   F4 f = FUNCTION_CAST<F4>(code->entry());
209   int res = f(2.718281828);
210   ::printf("f() = %d\n", res);
211   CHECK_EQ(2, res);
212 }
213 
214 
215 static int baz = 42;
TEST(AssemblerIa325)216 TEST(AssemblerIa325) {
217   CcTest::InitializeVM();
218   Isolate* isolate = reinterpret_cast<Isolate*>(CcTest::isolate());
219   HandleScope scope(isolate);
220 
221   v8::internal::byte buffer[256];
222   Assembler assm(isolate, buffer, sizeof buffer);
223 
224   __ mov(eax, Operand(reinterpret_cast<intptr_t>(&baz), RelocInfo::NONE32));
225   __ ret(0);
226 
227   CodeDesc desc;
228   assm.GetCode(&desc);
229   Handle<Code> code = isolate->factory()->NewCode(
230       desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
231   F0 f = FUNCTION_CAST<F0>(code->entry());
232   int res = f();
233   CHECK_EQ(42, res);
234 }
235 
236 
237 typedef double (*F5)(double x, double y);
238 
TEST(AssemblerIa326)239 TEST(AssemblerIa326) {
240   CcTest::InitializeVM();
241 
242   Isolate* isolate = reinterpret_cast<Isolate*>(CcTest::isolate());
243   HandleScope scope(isolate);
244   v8::internal::byte buffer[256];
245   Assembler assm(isolate, buffer, sizeof buffer);
246 
247   __ movsd(xmm0, Operand(esp, 1 * kPointerSize));
248   __ movsd(xmm1, Operand(esp, 3 * kPointerSize));
249   __ addsd(xmm0, xmm1);
250   __ mulsd(xmm0, xmm1);
251   __ subsd(xmm0, xmm1);
252   __ divsd(xmm0, xmm1);
253   // Copy xmm0 to st(0) using eight bytes of stack.
254   __ sub(esp, Immediate(8));
255   __ movsd(Operand(esp, 0), xmm0);
256   __ fld_d(Operand(esp, 0));
257   __ add(esp, Immediate(8));
258   __ ret(0);
259 
260   CodeDesc desc;
261   assm.GetCode(&desc);
262   Handle<Code> code = isolate->factory()->NewCode(
263       desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
264 #ifdef DEBUG
265   ::printf("\n---\n");
266   // don't print the code - our disassembler can't handle SSE instructions
267   // instead print bytes
268   Disassembler::Dump(stdout,
269                      code->instruction_start(),
270                      code->instruction_start() + code->instruction_size());
271 #endif
272   F5 f = FUNCTION_CAST<F5>(code->entry());
273   double res = f(2.2, 1.1);
274   ::printf("f() = %f\n", res);
275   CHECK(2.29 < res && res < 2.31);
276 }
277 
278 
279 typedef double (*F6)(int x);
280 
TEST(AssemblerIa328)281 TEST(AssemblerIa328) {
282   CcTest::InitializeVM();
283 
284   Isolate* isolate = reinterpret_cast<Isolate*>(CcTest::isolate());
285   HandleScope scope(isolate);
286   v8::internal::byte buffer[256];
287   Assembler assm(isolate, buffer, sizeof buffer);
288   __ mov(eax, Operand(esp, 4));
289   __ cvtsi2sd(xmm0, eax);
290   // Copy xmm0 to st(0) using eight bytes of stack.
291   __ sub(esp, Immediate(8));
292   __ movsd(Operand(esp, 0), xmm0);
293   __ fld_d(Operand(esp, 0));
294   __ add(esp, Immediate(8));
295   __ ret(0);
296   CodeDesc desc;
297   assm.GetCode(&desc);
298   Handle<Code> code = isolate->factory()->NewCode(
299       desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
300 #ifdef OBJECT_PRINT
301   OFStream os(stdout);
302   code->Print(os);
303 #endif
304   F6 f = FUNCTION_CAST<F6>(code->entry());
305   double res = f(12);
306 
307   ::printf("f() = %f\n", res);
308   CHECK(11.99 < res && res < 12.001);
309 }
310 
311 
312 typedef int (*F7)(double x, double y);
313 
TEST(AssemblerIa329)314 TEST(AssemblerIa329) {
315   CcTest::InitializeVM();
316   Isolate* isolate = reinterpret_cast<Isolate*>(CcTest::isolate());
317   HandleScope scope(isolate);
318   v8::internal::byte buffer[256];
319   MacroAssembler assm(isolate, buffer, sizeof buffer);
320   enum { kEqual = 0, kGreater = 1, kLess = 2, kNaN = 3, kUndefined = 4 };
321   Label equal_l, less_l, greater_l, nan_l;
322   __ fld_d(Operand(esp, 3 * kPointerSize));
323   __ fld_d(Operand(esp, 1 * kPointerSize));
324   __ FCmp();
325   __ j(parity_even, &nan_l);
326   __ j(equal, &equal_l);
327   __ j(below, &less_l);
328   __ j(above, &greater_l);
329 
330   __ mov(eax, kUndefined);
331   __ ret(0);
332 
333   __ bind(&equal_l);
334   __ mov(eax, kEqual);
335   __ ret(0);
336 
337   __ bind(&greater_l);
338   __ mov(eax, kGreater);
339   __ ret(0);
340 
341   __ bind(&less_l);
342   __ mov(eax, kLess);
343   __ ret(0);
344 
345   __ bind(&nan_l);
346   __ mov(eax, kNaN);
347   __ ret(0);
348 
349 
350   CodeDesc desc;
351   assm.GetCode(&desc);
352   Handle<Code> code = isolate->factory()->NewCode(
353       desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
354 #ifdef OBJECT_PRINT
355   OFStream os(stdout);
356   code->Print(os);
357 #endif
358 
359   F7 f = FUNCTION_CAST<F7>(code->entry());
360   CHECK_EQ(kLess, f(1.1, 2.2));
361   CHECK_EQ(kEqual, f(2.2, 2.2));
362   CHECK_EQ(kGreater, f(3.3, 2.2));
363   CHECK_EQ(kNaN, f(v8::base::OS::nan_value(), 1.1));
364 }
365 
366 
TEST(AssemblerIa3210)367 TEST(AssemblerIa3210) {
368   // Test chaining of label usages within instructions (issue 1644).
369   CcTest::InitializeVM();
370   Isolate* isolate = reinterpret_cast<Isolate*>(CcTest::isolate());
371   HandleScope scope(isolate);
372   Assembler assm(isolate, NULL, 0);
373 
374   Label target;
375   __ j(equal, &target);
376   __ j(not_equal, &target);
377   __ bind(&target);
378   __ nop();
379 }
380 
381 
TEST(AssemblerMultiByteNop)382 TEST(AssemblerMultiByteNop) {
383   CcTest::InitializeVM();
384   Isolate* isolate = reinterpret_cast<Isolate*>(CcTest::isolate());
385   HandleScope scope(isolate);
386   v8::internal::byte buffer[1024];
387   Assembler assm(isolate, buffer, sizeof(buffer));
388   __ push(ebx);
389   __ push(ecx);
390   __ push(edx);
391   __ push(edi);
392   __ push(esi);
393   __ mov(eax, 1);
394   __ mov(ebx, 2);
395   __ mov(ecx, 3);
396   __ mov(edx, 4);
397   __ mov(edi, 5);
398   __ mov(esi, 6);
399   for (int i = 0; i < 16; i++) {
400     int before = assm.pc_offset();
401     __ Nop(i);
402     CHECK_EQ(assm.pc_offset() - before, i);
403   }
404 
405   Label fail;
406   __ cmp(eax, 1);
407   __ j(not_equal, &fail);
408   __ cmp(ebx, 2);
409   __ j(not_equal, &fail);
410   __ cmp(ecx, 3);
411   __ j(not_equal, &fail);
412   __ cmp(edx, 4);
413   __ j(not_equal, &fail);
414   __ cmp(edi, 5);
415   __ j(not_equal, &fail);
416   __ cmp(esi, 6);
417   __ j(not_equal, &fail);
418   __ mov(eax, 42);
419   __ pop(esi);
420   __ pop(edi);
421   __ pop(edx);
422   __ pop(ecx);
423   __ pop(ebx);
424   __ ret(0);
425   __ bind(&fail);
426   __ mov(eax, 13);
427   __ pop(esi);
428   __ pop(edi);
429   __ pop(edx);
430   __ pop(ecx);
431   __ pop(ebx);
432   __ ret(0);
433 
434   CodeDesc desc;
435   assm.GetCode(&desc);
436   Handle<Code> code = isolate->factory()->NewCode(
437       desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
438   CHECK(code->IsCode());
439 
440   F0 f = FUNCTION_CAST<F0>(code->entry());
441   int res = f();
442   CHECK_EQ(42, res);
443 }
444 
445 
446 #ifdef __GNUC__
447 #define ELEMENT_COUNT 4
448 
DoSSE2(const v8::FunctionCallbackInfo<v8::Value> & args)449 void DoSSE2(const v8::FunctionCallbackInfo<v8::Value>& args) {
450   Isolate* isolate = reinterpret_cast<Isolate*>(CcTest::isolate());
451   HandleScope scope(isolate);
452 
453   CHECK(args[0]->IsArray());
454   v8::Local<v8::Array> vec = v8::Local<v8::Array>::Cast(args[0]);
455   CHECK_EQ(ELEMENT_COUNT, vec->Length());
456 
457   v8::internal::byte buffer[256];
458   Assembler assm(isolate, buffer, sizeof buffer);
459 
460   // Remove return address from the stack for fix stack frame alignment.
461   __ pop(ecx);
462 
463   // Store input vector on the stack.
464   for (int i = 0; i < ELEMENT_COUNT; ++i) {
465     __ push(Immediate(vec->Get(i)->Int32Value()));
466   }
467 
468   // Read vector into a xmm register.
469   __ pxor(xmm0, xmm0);
470   __ movdqa(xmm0, Operand(esp, 0));
471   // Create mask and store it in the return register.
472   __ movmskps(eax, xmm0);
473 
474   // Remove unused data from the stack.
475   __ add(esp, Immediate(ELEMENT_COUNT * sizeof(int32_t)));
476   // Restore return address.
477   __ push(ecx);
478 
479   __ ret(0);
480 
481   CodeDesc desc;
482   assm.GetCode(&desc);
483 
484   Handle<Code> code = isolate->factory()->NewCode(
485       desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
486 
487   F0 f = FUNCTION_CAST<F0>(code->entry());
488   int res = f();
489   args.GetReturnValue().Set(v8::Integer::New(CcTest::isolate(), res));
490 }
491 
492 
TEST(StackAlignmentForSSE2)493 TEST(StackAlignmentForSSE2) {
494   CcTest::InitializeVM();
495   CHECK_EQ(0, v8::base::OS::ActivationFrameAlignment() % 16);
496 
497   v8::Isolate* isolate = CcTest::isolate();
498   v8::HandleScope handle_scope(isolate);
499   v8::Handle<v8::ObjectTemplate> global_template =
500       v8::ObjectTemplate::New(isolate);
501   global_template->Set(v8_str("do_sse2"),
502                        v8::FunctionTemplate::New(isolate, DoSSE2));
503 
504   LocalContext env(NULL, global_template);
505   CompileRun(
506       "function foo(vec) {"
507       "  return do_sse2(vec);"
508       "}");
509 
510   v8::Local<v8::Object> global_object = env->Global();
511   v8::Local<v8::Function> foo =
512       v8::Local<v8::Function>::Cast(global_object->Get(v8_str("foo")));
513 
514   int32_t vec[ELEMENT_COUNT] = { -1, 1, 1, 1 };
515   v8::Local<v8::Array> v8_vec = v8::Array::New(isolate, ELEMENT_COUNT);
516   for (int i = 0; i < ELEMENT_COUNT; i++) {
517       v8_vec->Set(i, v8_num(vec[i]));
518   }
519 
520   v8::Local<v8::Value> args[] = { v8_vec };
521   v8::Local<v8::Value> result = foo->Call(global_object, 1, args);
522 
523   // The mask should be 0b1000.
524   CHECK_EQ(8, result->Int32Value());
525 }
526 
527 #undef ELEMENT_COUNT
528 #endif  // __GNUC__
529 
530 
TEST(AssemblerIa32Extractps)531 TEST(AssemblerIa32Extractps) {
532   CcTest::InitializeVM();
533   if (!CpuFeatures::IsSupported(SSE4_1)) return;
534 
535   Isolate* isolate = reinterpret_cast<Isolate*>(CcTest::isolate());
536   HandleScope scope(isolate);
537   v8::internal::byte buffer[256];
538   MacroAssembler assm(isolate, buffer, sizeof buffer);
539   { CpuFeatureScope fscope41(&assm, SSE4_1);
540     __ movsd(xmm1, Operand(esp, 4));
541     __ extractps(eax, xmm1, 0x1);
542     __ ret(0);
543   }
544 
545   CodeDesc desc;
546   assm.GetCode(&desc);
547   Handle<Code> code = isolate->factory()->NewCode(
548       desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
549 #ifdef OBJECT_PRINT
550   OFStream os(stdout);
551   code->Print(os);
552 #endif
553 
554   F4 f = FUNCTION_CAST<F4>(code->entry());
555   uint64_t value1 = V8_2PART_UINT64_C(0x12345678, 87654321);
556   CHECK_EQ(0x12345678, f(uint64_to_double(value1)));
557   uint64_t value2 = V8_2PART_UINT64_C(0x87654321, 12345678);
558   CHECK_EQ(0x87654321, f(uint64_to_double(value2)));
559 }
560 
561 
562 typedef int (*F8)(float x, float y);
TEST(AssemblerIa32SSE)563 TEST(AssemblerIa32SSE) {
564   CcTest::InitializeVM();
565 
566   Isolate* isolate = reinterpret_cast<Isolate*>(CcTest::isolate());
567   HandleScope scope(isolate);
568   v8::internal::byte buffer[256];
569   MacroAssembler assm(isolate, buffer, sizeof buffer);
570   {
571     __ movss(xmm0, Operand(esp, kPointerSize));
572     __ movss(xmm1, Operand(esp, 2 * kPointerSize));
573     __ shufps(xmm0, xmm0, 0x0);
574     __ shufps(xmm1, xmm1, 0x0);
575     __ movaps(xmm2, xmm1);
576     __ addps(xmm2, xmm0);
577     __ mulps(xmm2, xmm1);
578     __ subps(xmm2, xmm0);
579     __ divps(xmm2, xmm1);
580     __ cvttss2si(eax, xmm2);
581     __ ret(0);
582   }
583 
584   CodeDesc desc;
585   assm.GetCode(&desc);
586   Handle<Code> code = isolate->factory()->NewCode(
587       desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
588 #ifdef OBJECT_PRINT
589   OFStream os(stdout);
590   code->Print(os);
591 #endif
592 
593   F8 f = FUNCTION_CAST<F8>(code->entry());
594   CHECK_EQ(2, f(1.0, 2.0));
595 }
596 
597 
598 #undef __
599