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
40using namespace v8::internal;
41
42
43typedef int (*F0)();
44typedef int (*F1)(int x);
45typedef int (*F2)(int x, int y);
46
47
48#define __ assm.
49
50TEST(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
77TEST(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
114TEST(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
155typedef int (*F3)(float x);
156
157TEST(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
185typedef int (*F4)(double x);
186
187TEST(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
215static int baz = 42;
216TEST(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
237typedef double (*F5)(double x, double y);
238
239TEST(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
279typedef double (*F6)(int x);
280
281TEST(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
312typedef int (*F7)(double x, double y);
313
314TEST(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
367TEST(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
382TEST(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
449void 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
493TEST(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
531TEST(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
562typedef int (*F8)(float x, float y);
563TEST(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