1// Copyright 2009 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/factory.h"
34#include "src/macro-assembler.h"
35#include "src/ostreams.h"
36#include "src/serialize.h"
37#include "test/cctest/cctest.h"
38
39using namespace v8::internal;
40
41// Test the x64 assembler by compiling some simple functions into
42// a buffer and executing them.  These tests do not initialize the
43// V8 library, create a context, or use any V8 objects.
44// The AMD64 calling convention is used, with the first six arguments
45// in RDI, RSI, RDX, RCX, R8, and R9, and floating point arguments in
46// the XMM registers.  The return value is in RAX.
47// This calling convention is used on Linux, with GCC, and on Mac OS,
48// with GCC.  A different convention is used on 64-bit windows,
49// where the first four integer arguments are passed in RCX, RDX, R8 and R9.
50
51typedef int (*F0)();
52typedef int (*F1)(int64_t x);
53typedef int (*F2)(int64_t x, int64_t y);
54typedef int (*F3)(double x);
55typedef int64_t (*F4)(int64_t* x, int64_t* y);
56typedef int64_t (*F5)(int64_t x);
57
58#ifdef _WIN64
59static const Register arg1 = rcx;
60static const Register arg2 = rdx;
61#else
62static const Register arg1 = rdi;
63static const Register arg2 = rsi;
64#endif
65
66#define __ assm.
67
68
69TEST(AssemblerX64ReturnOperation) {
70  CcTest::InitializeVM();
71  // Allocate an executable page of memory.
72  size_t actual_size;
73  byte* buffer = static_cast<byte*>(v8::base::OS::Allocate(
74      Assembler::kMinimalBufferSize, &actual_size, true));
75  CHECK(buffer);
76  Assembler assm(CcTest::i_isolate(), buffer, static_cast<int>(actual_size));
77
78  // Assemble a simple function that copies argument 2 and returns it.
79  __ movq(rax, arg2);
80  __ nop();
81  __ ret(0);
82
83  CodeDesc desc;
84  assm.GetCode(&desc);
85  // Call the function from C++.
86  int result =  FUNCTION_CAST<F2>(buffer)(3, 2);
87  CHECK_EQ(2, result);
88}
89
90
91TEST(AssemblerX64StackOperations) {
92  CcTest::InitializeVM();
93  // Allocate an executable page of memory.
94  size_t actual_size;
95  byte* buffer = static_cast<byte*>(v8::base::OS::Allocate(
96      Assembler::kMinimalBufferSize, &actual_size, true));
97  CHECK(buffer);
98  Assembler assm(CcTest::i_isolate(), buffer, static_cast<int>(actual_size));
99
100  // Assemble a simple function that copies argument 2 and returns it.
101  // We compile without stack frame pointers, so the gdb debugger shows
102  // incorrect stack frames when debugging this function (which has them).
103  __ pushq(rbp);
104  __ movq(rbp, rsp);
105  __ pushq(arg2);  // Value at (rbp - 8)
106  __ pushq(arg2);  // Value at (rbp - 16)
107  __ pushq(arg1);  // Value at (rbp - 24)
108  __ popq(rax);
109  __ popq(rax);
110  __ popq(rax);
111  __ popq(rbp);
112  __ nop();
113  __ ret(0);
114
115  CodeDesc desc;
116  assm.GetCode(&desc);
117  // Call the function from C++.
118  int result =  FUNCTION_CAST<F2>(buffer)(3, 2);
119  CHECK_EQ(2, result);
120}
121
122
123TEST(AssemblerX64ArithmeticOperations) {
124  CcTest::InitializeVM();
125  // Allocate an executable page of memory.
126  size_t actual_size;
127  byte* buffer = static_cast<byte*>(v8::base::OS::Allocate(
128      Assembler::kMinimalBufferSize, &actual_size, true));
129  CHECK(buffer);
130  Assembler assm(CcTest::i_isolate(), buffer, static_cast<int>(actual_size));
131
132  // Assemble a simple function that adds arguments returning the sum.
133  __ movq(rax, arg2);
134  __ addq(rax, arg1);
135  __ ret(0);
136
137  CodeDesc desc;
138  assm.GetCode(&desc);
139  // Call the function from C++.
140  int result =  FUNCTION_CAST<F2>(buffer)(3, 2);
141  CHECK_EQ(5, result);
142}
143
144
145TEST(AssemblerX64CmpbOperation) {
146  CcTest::InitializeVM();
147  // Allocate an executable page of memory.
148  size_t actual_size;
149  byte* buffer = static_cast<byte*>(v8::base::OS::Allocate(
150      Assembler::kMinimalBufferSize, &actual_size, true));
151  CHECK(buffer);
152  Assembler assm(CcTest::i_isolate(), buffer, static_cast<int>(actual_size));
153
154  // Assemble a function that compare argument byte returing 1 if equal else 0.
155  // On Windows, it compares rcx with rdx which does not require REX prefix;
156  // on Linux, it compares rdi with rsi which requires REX prefix.
157
158  Label done;
159  __ movq(rax, Immediate(1));
160  __ cmpb(arg1, arg2);
161  __ j(equal, &done);
162  __ movq(rax, Immediate(0));
163  __ bind(&done);
164  __ ret(0);
165
166  CodeDesc desc;
167  assm.GetCode(&desc);
168  // Call the function from C++.
169  int result =  FUNCTION_CAST<F2>(buffer)(0x1002, 0x2002);
170  CHECK_EQ(1, result);
171  result =  FUNCTION_CAST<F2>(buffer)(0x1002, 0x2003);
172  CHECK_EQ(0, result);
173}
174
175
176TEST(AssemblerX64ImulOperation) {
177  CcTest::InitializeVM();
178  // Allocate an executable page of memory.
179  size_t actual_size;
180  byte* buffer = static_cast<byte*>(v8::base::OS::Allocate(
181      Assembler::kMinimalBufferSize, &actual_size, true));
182  CHECK(buffer);
183  Assembler assm(CcTest::i_isolate(), buffer, static_cast<int>(actual_size));
184
185  // Assemble a simple function that multiplies arguments returning the high
186  // word.
187  __ movq(rax, arg2);
188  __ imulq(arg1);
189  __ movq(rax, rdx);
190  __ ret(0);
191
192  CodeDesc desc;
193  assm.GetCode(&desc);
194  // Call the function from C++.
195  int result =  FUNCTION_CAST<F2>(buffer)(3, 2);
196  CHECK_EQ(0, result);
197  result =  FUNCTION_CAST<F2>(buffer)(0x100000000l, 0x100000000l);
198  CHECK_EQ(1, result);
199  result =  FUNCTION_CAST<F2>(buffer)(-0x100000000l, 0x100000000l);
200  CHECK_EQ(-1, result);
201}
202
203
204TEST(AssemblerX64XchglOperations) {
205  CcTest::InitializeVM();
206  // Allocate an executable page of memory.
207  size_t actual_size;
208  byte* buffer = static_cast<byte*>(v8::base::OS::Allocate(
209      Assembler::kMinimalBufferSize, &actual_size, true));
210  CHECK(buffer);
211  Assembler assm(CcTest::i_isolate(), buffer, static_cast<int>(actual_size));
212
213  __ movq(rax, Operand(arg1, 0));
214  __ movq(r11, Operand(arg2, 0));
215  __ xchgl(rax, r11);
216  __ movq(Operand(arg1, 0), rax);
217  __ movq(Operand(arg2, 0), r11);
218  __ ret(0);
219
220  CodeDesc desc;
221  assm.GetCode(&desc);
222  // Call the function from C++.
223  int64_t left   = V8_2PART_UINT64_C(0x10000000, 20000000);
224  int64_t right  = V8_2PART_UINT64_C(0x30000000, 40000000);
225  int64_t result = FUNCTION_CAST<F4>(buffer)(&left, &right);
226  CHECK_EQ(V8_2PART_UINT64_C(0x00000000, 40000000), left);
227  CHECK_EQ(V8_2PART_UINT64_C(0x00000000, 20000000), right);
228  USE(result);
229}
230
231
232TEST(AssemblerX64OrlOperations) {
233  CcTest::InitializeVM();
234  // Allocate an executable page of memory.
235  size_t actual_size;
236  byte* buffer = static_cast<byte*>(v8::base::OS::Allocate(
237      Assembler::kMinimalBufferSize, &actual_size, true));
238  CHECK(buffer);
239  Assembler assm(CcTest::i_isolate(), buffer, static_cast<int>(actual_size));
240
241  __ movq(rax, Operand(arg2, 0));
242  __ orl(Operand(arg1, 0), rax);
243  __ ret(0);
244
245  CodeDesc desc;
246  assm.GetCode(&desc);
247  // Call the function from C++.
248  int64_t left   = V8_2PART_UINT64_C(0x10000000, 20000000);
249  int64_t right  = V8_2PART_UINT64_C(0x30000000, 40000000);
250  int64_t result = FUNCTION_CAST<F4>(buffer)(&left, &right);
251  CHECK_EQ(V8_2PART_UINT64_C(0x10000000, 60000000), left);
252  USE(result);
253}
254
255
256TEST(AssemblerX64RollOperations) {
257  CcTest::InitializeVM();
258  // Allocate an executable page of memory.
259  size_t actual_size;
260  byte* buffer = static_cast<byte*>(v8::base::OS::Allocate(
261      Assembler::kMinimalBufferSize, &actual_size, true));
262  CHECK(buffer);
263  Assembler assm(CcTest::i_isolate(), buffer, static_cast<int>(actual_size));
264
265  __ movq(rax, arg1);
266  __ roll(rax, Immediate(1));
267  __ ret(0);
268
269  CodeDesc desc;
270  assm.GetCode(&desc);
271  // Call the function from C++.
272  int64_t src    = V8_2PART_UINT64_C(0x10000000, C0000000);
273  int64_t result = FUNCTION_CAST<F5>(buffer)(src);
274  CHECK_EQ(V8_2PART_UINT64_C(0x00000000, 80000001), result);
275}
276
277
278TEST(AssemblerX64SublOperations) {
279  CcTest::InitializeVM();
280  // Allocate an executable page of memory.
281  size_t actual_size;
282  byte* buffer = static_cast<byte*>(v8::base::OS::Allocate(
283      Assembler::kMinimalBufferSize, &actual_size, true));
284  CHECK(buffer);
285  Assembler assm(CcTest::i_isolate(), buffer, static_cast<int>(actual_size));
286
287  __ movq(rax, Operand(arg2, 0));
288  __ subl(Operand(arg1, 0), rax);
289  __ ret(0);
290
291  CodeDesc desc;
292  assm.GetCode(&desc);
293  // Call the function from C++.
294  int64_t left   = V8_2PART_UINT64_C(0x10000000, 20000000);
295  int64_t right  = V8_2PART_UINT64_C(0x30000000, 40000000);
296  int64_t result = FUNCTION_CAST<F4>(buffer)(&left, &right);
297  CHECK_EQ(V8_2PART_UINT64_C(0x10000000, e0000000), left);
298  USE(result);
299}
300
301
302TEST(AssemblerX64TestlOperations) {
303  CcTest::InitializeVM();
304  // Allocate an executable page of memory.
305  size_t actual_size;
306  byte* buffer = static_cast<byte*>(v8::base::OS::Allocate(
307      Assembler::kMinimalBufferSize, &actual_size, true));
308  CHECK(buffer);
309  Assembler assm(CcTest::i_isolate(), buffer, static_cast<int>(actual_size));
310
311  // Set rax with the ZF flag of the testl instruction.
312  Label done;
313  __ movq(rax, Immediate(1));
314  __ movq(r11, Operand(arg2, 0));
315  __ testl(Operand(arg1, 0), r11);
316  __ j(zero, &done, Label::kNear);
317  __ movq(rax, Immediate(0));
318  __ bind(&done);
319  __ ret(0);
320
321  CodeDesc desc;
322  assm.GetCode(&desc);
323  // Call the function from C++.
324  int64_t left   = V8_2PART_UINT64_C(0x10000000, 20000000);
325  int64_t right  = V8_2PART_UINT64_C(0x30000000, 00000000);
326  int64_t result = FUNCTION_CAST<F4>(buffer)(&left, &right);
327  CHECK_EQ(static_cast<int64_t>(1), result);
328}
329
330
331TEST(AssemblerX64XorlOperations) {
332  CcTest::InitializeVM();
333  // Allocate an executable page of memory.
334  size_t actual_size;
335  byte* buffer = static_cast<byte*>(v8::base::OS::Allocate(
336      Assembler::kMinimalBufferSize, &actual_size, true));
337  CHECK(buffer);
338  Assembler assm(CcTest::i_isolate(), buffer, static_cast<int>(actual_size));
339
340  __ movq(rax, Operand(arg2, 0));
341  __ xorl(Operand(arg1, 0), rax);
342  __ ret(0);
343
344  CodeDesc desc;
345  assm.GetCode(&desc);
346  // Call the function from C++.
347  int64_t left   = V8_2PART_UINT64_C(0x10000000, 20000000);
348  int64_t right  = V8_2PART_UINT64_C(0x30000000, 60000000);
349  int64_t result = FUNCTION_CAST<F4>(buffer)(&left, &right);
350  CHECK_EQ(V8_2PART_UINT64_C(0x10000000, 40000000), left);
351  USE(result);
352}
353
354
355TEST(AssemblerX64MemoryOperands) {
356  CcTest::InitializeVM();
357  // Allocate an executable page of memory.
358  size_t actual_size;
359  byte* buffer = static_cast<byte*>(v8::base::OS::Allocate(
360      Assembler::kMinimalBufferSize, &actual_size, true));
361  CHECK(buffer);
362  Assembler assm(CcTest::i_isolate(), buffer, static_cast<int>(actual_size));
363
364  // Assemble a simple function that copies argument 2 and returns it.
365  __ pushq(rbp);
366  __ movq(rbp, rsp);
367
368  __ pushq(arg2);  // Value at (rbp - 8)
369  __ pushq(arg2);  // Value at (rbp - 16)
370  __ pushq(arg1);  // Value at (rbp - 24)
371
372  const int kStackElementSize = 8;
373  __ movq(rax, Operand(rbp, -3 * kStackElementSize));
374  __ popq(arg2);
375  __ popq(arg2);
376  __ popq(arg2);
377  __ popq(rbp);
378  __ nop();
379  __ ret(0);
380
381  CodeDesc desc;
382  assm.GetCode(&desc);
383  // Call the function from C++.
384  int result =  FUNCTION_CAST<F2>(buffer)(3, 2);
385  CHECK_EQ(3, result);
386}
387
388
389TEST(AssemblerX64ControlFlow) {
390  CcTest::InitializeVM();
391  // Allocate an executable page of memory.
392  size_t actual_size;
393  byte* buffer = static_cast<byte*>(v8::base::OS::Allocate(
394      Assembler::kMinimalBufferSize, &actual_size, true));
395  CHECK(buffer);
396  Assembler assm(CcTest::i_isolate(), buffer, static_cast<int>(actual_size));
397
398  // Assemble a simple function that copies argument 1 and returns it.
399  __ pushq(rbp);
400
401  __ movq(rbp, rsp);
402  __ movq(rax, arg1);
403  Label target;
404  __ jmp(&target);
405  __ movq(rax, arg2);
406  __ bind(&target);
407  __ popq(rbp);
408  __ ret(0);
409
410  CodeDesc desc;
411  assm.GetCode(&desc);
412  // Call the function from C++.
413  int result =  FUNCTION_CAST<F2>(buffer)(3, 2);
414  CHECK_EQ(3, result);
415}
416
417
418TEST(AssemblerX64LoopImmediates) {
419  CcTest::InitializeVM();
420  // Allocate an executable page of memory.
421  size_t actual_size;
422  byte* buffer = static_cast<byte*>(v8::base::OS::Allocate(
423      Assembler::kMinimalBufferSize, &actual_size, true));
424  CHECK(buffer);
425  Assembler assm(CcTest::i_isolate(), buffer, static_cast<int>(actual_size));
426  // Assemble two loops using rax as counter, and verify the ending counts.
427  Label Fail;
428  __ movq(rax, Immediate(-3));
429  Label Loop1_test;
430  Label Loop1_body;
431  __ jmp(&Loop1_test);
432  __ bind(&Loop1_body);
433  __ addq(rax, Immediate(7));
434  __ bind(&Loop1_test);
435  __ cmpq(rax, Immediate(20));
436  __ j(less_equal, &Loop1_body);
437  // Did the loop terminate with the expected value?
438  __ cmpq(rax, Immediate(25));
439  __ j(not_equal, &Fail);
440
441  Label Loop2_test;
442  Label Loop2_body;
443  __ movq(rax, Immediate(0x11FEED00));
444  __ jmp(&Loop2_test);
445  __ bind(&Loop2_body);
446  __ addq(rax, Immediate(-0x1100));
447  __ bind(&Loop2_test);
448  __ cmpq(rax, Immediate(0x11FE8000));
449  __ j(greater, &Loop2_body);
450  // Did the loop terminate with the expected value?
451  __ cmpq(rax, Immediate(0x11FE7600));
452  __ j(not_equal, &Fail);
453
454  __ movq(rax, Immediate(1));
455  __ ret(0);
456  __ bind(&Fail);
457  __ movq(rax, Immediate(0));
458  __ ret(0);
459
460  CodeDesc desc;
461  assm.GetCode(&desc);
462  // Call the function from C++.
463  int result =  FUNCTION_CAST<F0>(buffer)();
464  CHECK_EQ(1, result);
465}
466
467
468TEST(OperandRegisterDependency) {
469  int offsets[4] = {0, 1, 0xfed, 0xbeefcad};
470  for (int i = 0; i < 4; i++) {
471    int offset = offsets[i];
472    CHECK(Operand(rax, offset).AddressUsesRegister(rax));
473    CHECK(!Operand(rax, offset).AddressUsesRegister(r8));
474    CHECK(!Operand(rax, offset).AddressUsesRegister(rcx));
475
476    CHECK(Operand(rax, rax, times_1, offset).AddressUsesRegister(rax));
477    CHECK(!Operand(rax, rax, times_1, offset).AddressUsesRegister(r8));
478    CHECK(!Operand(rax, rax, times_1, offset).AddressUsesRegister(rcx));
479
480    CHECK(Operand(rax, rcx, times_1, offset).AddressUsesRegister(rax));
481    CHECK(Operand(rax, rcx, times_1, offset).AddressUsesRegister(rcx));
482    CHECK(!Operand(rax, rcx, times_1, offset).AddressUsesRegister(r8));
483    CHECK(!Operand(rax, rcx, times_1, offset).AddressUsesRegister(r9));
484    CHECK(!Operand(rax, rcx, times_1, offset).AddressUsesRegister(rdx));
485    CHECK(!Operand(rax, rcx, times_1, offset).AddressUsesRegister(rsp));
486
487    CHECK(Operand(rsp, offset).AddressUsesRegister(rsp));
488    CHECK(!Operand(rsp, offset).AddressUsesRegister(rax));
489    CHECK(!Operand(rsp, offset).AddressUsesRegister(r15));
490
491    CHECK(Operand(rbp, offset).AddressUsesRegister(rbp));
492    CHECK(!Operand(rbp, offset).AddressUsesRegister(rax));
493    CHECK(!Operand(rbp, offset).AddressUsesRegister(r13));
494
495    CHECK(Operand(rbp, rax, times_1, offset).AddressUsesRegister(rbp));
496    CHECK(Operand(rbp, rax, times_1, offset).AddressUsesRegister(rax));
497    CHECK(!Operand(rbp, rax, times_1, offset).AddressUsesRegister(rcx));
498    CHECK(!Operand(rbp, rax, times_1, offset).AddressUsesRegister(r13));
499    CHECK(!Operand(rbp, rax, times_1, offset).AddressUsesRegister(r8));
500    CHECK(!Operand(rbp, rax, times_1, offset).AddressUsesRegister(rsp));
501
502    CHECK(Operand(rsp, rbp, times_1, offset).AddressUsesRegister(rsp));
503    CHECK(Operand(rsp, rbp, times_1, offset).AddressUsesRegister(rbp));
504    CHECK(!Operand(rsp, rbp, times_1, offset).AddressUsesRegister(rax));
505    CHECK(!Operand(rsp, rbp, times_1, offset).AddressUsesRegister(r15));
506    CHECK(!Operand(rsp, rbp, times_1, offset).AddressUsesRegister(r13));
507  }
508}
509
510
511TEST(AssemblerX64LabelChaining) {
512  // Test chaining of label usages within instructions (issue 1644).
513  CcTest::InitializeVM();
514  v8::HandleScope scope(CcTest::isolate());
515  Assembler assm(CcTest::i_isolate(), NULL, 0);
516
517  Label target;
518  __ j(equal, &target);
519  __ j(not_equal, &target);
520  __ bind(&target);
521  __ nop();
522}
523
524
525TEST(AssemblerMultiByteNop) {
526  CcTest::InitializeVM();
527  v8::HandleScope scope(CcTest::isolate());
528  byte buffer[1024];
529  Isolate* isolate = CcTest::i_isolate();
530  Assembler assm(isolate, buffer, sizeof(buffer));
531  __ pushq(rbx);
532  __ pushq(rcx);
533  __ pushq(rdx);
534  __ pushq(rdi);
535  __ pushq(rsi);
536  __ movq(rax, Immediate(1));
537  __ movq(rbx, Immediate(2));
538  __ movq(rcx, Immediate(3));
539  __ movq(rdx, Immediate(4));
540  __ movq(rdi, Immediate(5));
541  __ movq(rsi, Immediate(6));
542  for (int i = 0; i < 16; i++) {
543    int before = assm.pc_offset();
544    __ Nop(i);
545    CHECK_EQ(assm.pc_offset() - before, i);
546  }
547
548  Label fail;
549  __ cmpq(rax, Immediate(1));
550  __ j(not_equal, &fail);
551  __ cmpq(rbx, Immediate(2));
552  __ j(not_equal, &fail);
553  __ cmpq(rcx, Immediate(3));
554  __ j(not_equal, &fail);
555  __ cmpq(rdx, Immediate(4));
556  __ j(not_equal, &fail);
557  __ cmpq(rdi, Immediate(5));
558  __ j(not_equal, &fail);
559  __ cmpq(rsi, Immediate(6));
560  __ j(not_equal, &fail);
561  __ movq(rax, Immediate(42));
562  __ popq(rsi);
563  __ popq(rdi);
564  __ popq(rdx);
565  __ popq(rcx);
566  __ popq(rbx);
567  __ ret(0);
568  __ bind(&fail);
569  __ movq(rax, Immediate(13));
570  __ popq(rsi);
571  __ popq(rdi);
572  __ popq(rdx);
573  __ popq(rcx);
574  __ popq(rbx);
575  __ ret(0);
576
577  CodeDesc desc;
578  assm.GetCode(&desc);
579  Handle<Code> code = isolate->factory()->NewCode(
580      desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
581
582  F0 f = FUNCTION_CAST<F0>(code->entry());
583  int res = f();
584  CHECK_EQ(42, res);
585}
586
587
588#ifdef __GNUC__
589#define ELEMENT_COUNT 4
590
591void DoSSE2(const v8::FunctionCallbackInfo<v8::Value>& args) {
592  v8::HandleScope scope(CcTest::isolate());
593  byte buffer[1024];
594
595  CHECK(args[0]->IsArray());
596  v8::Local<v8::Array> vec = v8::Local<v8::Array>::Cast(args[0]);
597  CHECK_EQ(ELEMENT_COUNT, vec->Length());
598
599  Isolate* isolate = CcTest::i_isolate();
600  Assembler assm(isolate, buffer, sizeof(buffer));
601
602  // Remove return address from the stack for fix stack frame alignment.
603  __ popq(rcx);
604
605  // Store input vector on the stack.
606  for (int i = 0; i < ELEMENT_COUNT; i++) {
607    __ movl(rax, Immediate(vec->Get(i)->Int32Value()));
608    __ shlq(rax, Immediate(0x20));
609    __ orq(rax, Immediate(vec->Get(++i)->Int32Value()));
610    __ pushq(rax);
611  }
612
613  // Read vector into a xmm register.
614  __ xorps(xmm0, xmm0);
615  __ movdqa(xmm0, Operand(rsp, 0));
616  // Create mask and store it in the return register.
617  __ movmskps(rax, xmm0);
618
619  // Remove unused data from the stack.
620  __ addq(rsp, Immediate(ELEMENT_COUNT * sizeof(int32_t)));
621  // Restore return address.
622  __ pushq(rcx);
623
624  __ ret(0);
625
626  CodeDesc desc;
627  assm.GetCode(&desc);
628  Handle<Code> code = isolate->factory()->NewCode(
629      desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
630
631  F0 f = FUNCTION_CAST<F0>(code->entry());
632  int res = f();
633  args.GetReturnValue().Set(v8::Integer::New(CcTest::isolate(), res));
634}
635
636
637TEST(StackAlignmentForSSE2) {
638  CcTest::InitializeVM();
639  CHECK_EQ(0, v8::base::OS::ActivationFrameAlignment() % 16);
640
641  v8::Isolate* isolate = CcTest::isolate();
642  v8::HandleScope handle_scope(isolate);
643  v8::Handle<v8::ObjectTemplate> global_template =
644      v8::ObjectTemplate::New(isolate);
645  global_template->Set(v8_str("do_sse2"),
646                       v8::FunctionTemplate::New(isolate, DoSSE2));
647
648  LocalContext env(NULL, global_template);
649  CompileRun(
650      "function foo(vec) {"
651      "  return do_sse2(vec);"
652      "}");
653
654  v8::Local<v8::Object> global_object = env->Global();
655  v8::Local<v8::Function> foo =
656      v8::Local<v8::Function>::Cast(global_object->Get(v8_str("foo")));
657
658  int32_t vec[ELEMENT_COUNT] = { -1, 1, 1, 1 };
659  v8::Local<v8::Array> v8_vec = v8::Array::New(isolate, ELEMENT_COUNT);
660  for (int i = 0; i < ELEMENT_COUNT; i++) {
661    v8_vec->Set(i, v8_num(vec[i]));
662  }
663
664  v8::Local<v8::Value> args[] = { v8_vec };
665  v8::Local<v8::Value> result = foo->Call(global_object, 1, args);
666
667  // The mask should be 0b1000.
668  CHECK_EQ(8, result->Int32Value());
669}
670
671#undef ELEMENT_COUNT
672#endif  // __GNUC__
673
674
675TEST(AssemblerX64Extractps) {
676  CcTest::InitializeVM();
677  if (!CpuFeatures::IsSupported(SSE4_1)) return;
678
679  v8::HandleScope scope(CcTest::isolate());
680  byte buffer[256];
681  Isolate* isolate = CcTest::i_isolate();
682  Assembler assm(isolate, buffer, sizeof(buffer));
683  { CpuFeatureScope fscope2(&assm, SSE4_1);
684    __ extractps(rax, xmm0, 0x1);
685    __ ret(0);
686  }
687
688  CodeDesc desc;
689  assm.GetCode(&desc);
690  Handle<Code> code = isolate->factory()->NewCode(
691      desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
692#ifdef OBJECT_PRINT
693  OFStream os(stdout);
694  code->Print(os);
695#endif
696
697  F3 f = FUNCTION_CAST<F3>(code->entry());
698  uint64_t value1 = V8_2PART_UINT64_C(0x12345678, 87654321);
699  CHECK_EQ(0x12345678, f(uint64_to_double(value1)));
700  uint64_t value2 = V8_2PART_UINT64_C(0x87654321, 12345678);
701  CHECK_EQ(0x87654321, f(uint64_to_double(value2)));
702}
703
704
705typedef int (*F6)(float x, float y);
706TEST(AssemblerX64SSE) {
707  CcTest::InitializeVM();
708
709  Isolate* isolate = reinterpret_cast<Isolate*>(CcTest::isolate());
710  HandleScope scope(isolate);
711  v8::internal::byte buffer[256];
712  MacroAssembler assm(isolate, buffer, sizeof buffer);
713  {
714    __ shufps(xmm0, xmm0, 0x0);  // brocast first argument
715    __ shufps(xmm1, xmm1, 0x0);  // brocast second argument
716    __ movaps(xmm2, xmm1);
717    __ addps(xmm2, xmm0);
718    __ mulps(xmm2, xmm1);
719    __ subps(xmm2, xmm0);
720    __ divps(xmm2, xmm1);
721    __ cvttss2si(rax, xmm2);
722    __ ret(0);
723  }
724
725  CodeDesc desc;
726  assm.GetCode(&desc);
727  Handle<Code> code = isolate->factory()->NewCode(
728      desc,
729      Code::ComputeFlags(Code::STUB),
730      Handle<Code>());
731#ifdef OBJECT_PRINT
732  OFStream os(stdout);
733  code->Print(os);
734#endif
735
736  F6 f = FUNCTION_CAST<F6>(code->entry());
737  CHECK_EQ(2, f(1.0, 2.0));
738}
739#undef __
740