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
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.
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.
32 #include "macro-assembler.h"
35 #include "serialize.h"
38 using namespace v8::internal;
40 // Test the x64 assembler by compiling some simple functions into
41 // a buffer and executing them. These tests do not initialize the
42 // V8 library, create a context, or use any V8 objects.
43 // The AMD64 calling convention is used, with the first six arguments
44 // in RDI, RSI, RDX, RCX, R8, and R9, and floating point arguments in
45 // the XMM registers. The return value is in RAX.
46 // This calling convention is used on Linux, with GCC, and on Mac OS,
47 // with GCC. A different convention is used on 64-bit windows,
48 // where the first four integer arguments are passed in RCX, RDX, R8 and R9.
51 typedef int (*F1)(int64_t x);
52 typedef int (*F2)(int64_t x, int64_t y);
53 typedef int (*F3)(double x);
54 typedef int64_t (*F4)(int64_t* x, int64_t* y);
55 typedef int64_t (*F5)(int64_t x);
58 static const Register arg1 = rcx;
59 static const Register arg2 = rdx;
61 static const Register arg1 = rdi;
62 static const Register arg2 = rsi;
68 TEST(AssemblerX64ReturnOperation) {
69 // Allocate an executable page of memory.
71 byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
75 Assembler assm(CcTest::i_isolate(), buffer, static_cast<int>(actual_size));
77 // Assemble a simple function that copies argument 2 and returns it.
84 // Call the function from C++.
85 int result = FUNCTION_CAST<F2>(buffer)(3, 2);
90 TEST(AssemblerX64StackOperations) {
91 // Allocate an executable page of memory.
93 byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
97 Assembler assm(CcTest::i_isolate(), buffer, static_cast<int>(actual_size));
99 // Assemble a simple function that copies argument 2 and returns it.
100 // We compile without stack frame pointers, so the gdb debugger shows
101 // incorrect stack frames when debugging this function (which has them).
104 __ push(arg2); // Value at (rbp - 8)
105 __ push(arg2); // Value at (rbp - 16)
106 __ push(arg1); // Value at (rbp - 24)
116 // Call the function from C++.
117 int result = FUNCTION_CAST<F2>(buffer)(3, 2);
122 TEST(AssemblerX64ArithmeticOperations) {
123 // Allocate an executable page of memory.
125 byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
129 Assembler assm(CcTest::i_isolate(), buffer, static_cast<int>(actual_size));
131 // Assemble a simple function that adds arguments returning the sum.
138 // Call the function from C++.
139 int result = FUNCTION_CAST<F2>(buffer)(3, 2);
144 TEST(AssemblerX64ImulOperation) {
145 // Allocate an executable page of memory.
147 byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
151 Assembler assm(CcTest::i_isolate(), buffer, static_cast<int>(actual_size));
153 // Assemble a simple function that multiplies arguments returning the high
162 // Call the function from C++.
163 int result = FUNCTION_CAST<F2>(buffer)(3, 2);
165 result = FUNCTION_CAST<F2>(buffer)(0x100000000l, 0x100000000l);
167 result = FUNCTION_CAST<F2>(buffer)(-0x100000000l, 0x100000000l);
168 CHECK_EQ(-1, result);
172 TEST(AssemblerX64XchglOperations) {
173 // Allocate an executable page of memory.
175 byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
179 Assembler assm(CcTest::i_isolate(), buffer, static_cast<int>(actual_size));
181 __ movq(rax, Operand(arg1, 0));
182 __ movq(r11, Operand(arg2, 0));
184 __ movq(Operand(arg1, 0), rax);
185 __ movq(Operand(arg2, 0), r11);
190 // Call the function from C++.
191 int64_t left = V8_2PART_UINT64_C(0x10000000, 20000000);
192 int64_t right = V8_2PART_UINT64_C(0x30000000, 40000000);
193 int64_t result = FUNCTION_CAST<F4>(buffer)(&left, &right);
194 CHECK_EQ(V8_2PART_UINT64_C(0x00000000, 40000000), left);
195 CHECK_EQ(V8_2PART_UINT64_C(0x00000000, 20000000), right);
200 TEST(AssemblerX64OrlOperations) {
201 // Allocate an executable page of memory.
203 byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
207 Assembler assm(CcTest::i_isolate(), buffer, static_cast<int>(actual_size));
209 __ movq(rax, Operand(arg2, 0));
210 __ orl(Operand(arg1, 0), rax);
215 // Call the function from C++.
216 int64_t left = V8_2PART_UINT64_C(0x10000000, 20000000);
217 int64_t right = V8_2PART_UINT64_C(0x30000000, 40000000);
218 int64_t result = FUNCTION_CAST<F4>(buffer)(&left, &right);
219 CHECK_EQ(V8_2PART_UINT64_C(0x10000000, 60000000), left);
224 TEST(AssemblerX64RollOperations) {
225 // Allocate an executable page of memory.
227 byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
231 Assembler assm(CcTest::i_isolate(), buffer, static_cast<int>(actual_size));
234 __ roll(rax, Immediate(1));
239 // Call the function from C++.
240 int64_t src = V8_2PART_UINT64_C(0x10000000, C0000000);
241 int64_t result = FUNCTION_CAST<F5>(buffer)(src);
242 CHECK_EQ(V8_2PART_UINT64_C(0x00000000, 80000001), result);
246 TEST(AssemblerX64SublOperations) {
247 // Allocate an executable page of memory.
249 byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
253 Assembler assm(CcTest::i_isolate(), buffer, static_cast<int>(actual_size));
255 __ movq(rax, Operand(arg2, 0));
256 __ subl(Operand(arg1, 0), rax);
261 // Call the function from C++.
262 int64_t left = V8_2PART_UINT64_C(0x10000000, 20000000);
263 int64_t right = V8_2PART_UINT64_C(0x30000000, 40000000);
264 int64_t result = FUNCTION_CAST<F4>(buffer)(&left, &right);
265 CHECK_EQ(V8_2PART_UINT64_C(0x10000000, e0000000), left);
270 TEST(AssemblerX64TestlOperations) {
271 // Allocate an executable page of memory.
273 byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
277 Assembler assm(CcTest::i_isolate(), buffer, static_cast<int>(actual_size));
279 // Set rax with the ZF flag of the testl instruction.
281 __ movq(rax, Immediate(1));
282 __ movq(r11, Operand(arg2, 0));
283 __ testl(Operand(arg1, 0), r11);
284 __ j(zero, &done, Label::kNear);
285 __ movq(rax, Immediate(0));
291 // Call the function from C++.
292 int64_t left = V8_2PART_UINT64_C(0x10000000, 20000000);
293 int64_t right = V8_2PART_UINT64_C(0x30000000, 00000000);
294 int64_t result = FUNCTION_CAST<F4>(buffer)(&left, &right);
295 CHECK_EQ(static_cast<int64_t>(1), result);
299 TEST(AssemblerX64XorlOperations) {
300 // Allocate an executable page of memory.
302 byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
306 Assembler assm(CcTest::i_isolate(), buffer, static_cast<int>(actual_size));
308 __ movq(rax, Operand(arg2, 0));
309 __ xorl(Operand(arg1, 0), rax);
314 // Call the function from C++.
315 int64_t left = V8_2PART_UINT64_C(0x10000000, 20000000);
316 int64_t right = V8_2PART_UINT64_C(0x30000000, 60000000);
317 int64_t result = FUNCTION_CAST<F4>(buffer)(&left, &right);
318 CHECK_EQ(V8_2PART_UINT64_C(0x10000000, 40000000), left);
323 TEST(AssemblerX64MemoryOperands) {
324 // Allocate an executable page of memory.
326 byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
330 Assembler assm(CcTest::i_isolate(), buffer, static_cast<int>(actual_size));
332 // Assemble a simple function that copies argument 2 and returns it.
336 __ push(arg2); // Value at (rbp - 8)
337 __ push(arg2); // Value at (rbp - 16)
338 __ push(arg1); // Value at (rbp - 24)
340 const int kStackElementSize = 8;
341 __ movq(rax, Operand(rbp, -3 * kStackElementSize));
351 // Call the function from C++.
352 int result = FUNCTION_CAST<F2>(buffer)(3, 2);
357 TEST(AssemblerX64ControlFlow) {
358 // Allocate an executable page of memory.
360 byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
364 Assembler assm(CcTest::i_isolate(), buffer, static_cast<int>(actual_size));
366 // Assemble a simple function that copies argument 1 and returns it.
380 // Call the function from C++.
381 int result = FUNCTION_CAST<F2>(buffer)(3, 2);
386 TEST(AssemblerX64LoopImmediates) {
387 // Allocate an executable page of memory.
389 byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
393 Assembler assm(CcTest::i_isolate(), buffer, static_cast<int>(actual_size));
394 // Assemble two loops using rax as counter, and verify the ending counts.
396 __ movq(rax, Immediate(-3));
400 __ bind(&Loop1_body);
401 __ addq(rax, Immediate(7));
402 __ bind(&Loop1_test);
403 __ cmpq(rax, Immediate(20));
404 __ j(less_equal, &Loop1_body);
405 // Did the loop terminate with the expected value?
406 __ cmpq(rax, Immediate(25));
407 __ j(not_equal, &Fail);
411 __ movq(rax, Immediate(0x11FEED00));
413 __ bind(&Loop2_body);
414 __ addq(rax, Immediate(-0x1100));
415 __ bind(&Loop2_test);
416 __ cmpq(rax, Immediate(0x11FE8000));
417 __ j(greater, &Loop2_body);
418 // Did the loop terminate with the expected value?
419 __ cmpq(rax, Immediate(0x11FE7600));
420 __ j(not_equal, &Fail);
422 __ movq(rax, Immediate(1));
425 __ movq(rax, Immediate(0));
430 // Call the function from C++.
431 int result = FUNCTION_CAST<F0>(buffer)();
436 TEST(OperandRegisterDependency) {
437 int offsets[4] = {0, 1, 0xfed, 0xbeefcad};
438 for (int i = 0; i < 4; i++) {
439 int offset = offsets[i];
440 CHECK(Operand(rax, offset).AddressUsesRegister(rax));
441 CHECK(!Operand(rax, offset).AddressUsesRegister(r8));
442 CHECK(!Operand(rax, offset).AddressUsesRegister(rcx));
444 CHECK(Operand(rax, rax, times_1, offset).AddressUsesRegister(rax));
445 CHECK(!Operand(rax, rax, times_1, offset).AddressUsesRegister(r8));
446 CHECK(!Operand(rax, rax, times_1, offset).AddressUsesRegister(rcx));
448 CHECK(Operand(rax, rcx, times_1, offset).AddressUsesRegister(rax));
449 CHECK(Operand(rax, rcx, times_1, offset).AddressUsesRegister(rcx));
450 CHECK(!Operand(rax, rcx, times_1, offset).AddressUsesRegister(r8));
451 CHECK(!Operand(rax, rcx, times_1, offset).AddressUsesRegister(r9));
452 CHECK(!Operand(rax, rcx, times_1, offset).AddressUsesRegister(rdx));
453 CHECK(!Operand(rax, rcx, times_1, offset).AddressUsesRegister(rsp));
455 CHECK(Operand(rsp, offset).AddressUsesRegister(rsp));
456 CHECK(!Operand(rsp, offset).AddressUsesRegister(rax));
457 CHECK(!Operand(rsp, offset).AddressUsesRegister(r15));
459 CHECK(Operand(rbp, offset).AddressUsesRegister(rbp));
460 CHECK(!Operand(rbp, offset).AddressUsesRegister(rax));
461 CHECK(!Operand(rbp, offset).AddressUsesRegister(r13));
463 CHECK(Operand(rbp, rax, times_1, offset).AddressUsesRegister(rbp));
464 CHECK(Operand(rbp, rax, times_1, offset).AddressUsesRegister(rax));
465 CHECK(!Operand(rbp, rax, times_1, offset).AddressUsesRegister(rcx));
466 CHECK(!Operand(rbp, rax, times_1, offset).AddressUsesRegister(r13));
467 CHECK(!Operand(rbp, rax, times_1, offset).AddressUsesRegister(r8));
468 CHECK(!Operand(rbp, rax, times_1, offset).AddressUsesRegister(rsp));
470 CHECK(Operand(rsp, rbp, times_1, offset).AddressUsesRegister(rsp));
471 CHECK(Operand(rsp, rbp, times_1, offset).AddressUsesRegister(rbp));
472 CHECK(!Operand(rsp, rbp, times_1, offset).AddressUsesRegister(rax));
473 CHECK(!Operand(rsp, rbp, times_1, offset).AddressUsesRegister(r15));
474 CHECK(!Operand(rsp, rbp, times_1, offset).AddressUsesRegister(r13));
479 TEST(AssemblerX64LabelChaining) {
480 // Test chaining of label usages within instructions (issue 1644).
481 CcTest::InitializeVM();
482 v8::HandleScope scope(CcTest::isolate());
483 Assembler assm(CcTest::i_isolate(), NULL, 0);
486 __ j(equal, &target);
487 __ j(not_equal, &target);
493 TEST(AssemblerMultiByteNop) {
494 CcTest::InitializeVM();
495 v8::HandleScope scope(CcTest::isolate());
497 Isolate* isolate = CcTest::i_isolate();
498 Assembler assm(isolate, buffer, sizeof(buffer));
504 __ movq(rax, Immediate(1));
505 __ movq(rbx, Immediate(2));
506 __ movq(rcx, Immediate(3));
507 __ movq(rdx, Immediate(4));
508 __ movq(rdi, Immediate(5));
509 __ movq(rsi, Immediate(6));
510 for (int i = 0; i < 16; i++) {
511 int before = assm.pc_offset();
513 CHECK_EQ(assm.pc_offset() - before, i);
517 __ cmpq(rax, Immediate(1));
518 __ j(not_equal, &fail);
519 __ cmpq(rbx, Immediate(2));
520 __ j(not_equal, &fail);
521 __ cmpq(rcx, Immediate(3));
522 __ j(not_equal, &fail);
523 __ cmpq(rdx, Immediate(4));
524 __ j(not_equal, &fail);
525 __ cmpq(rdi, Immediate(5));
526 __ j(not_equal, &fail);
527 __ cmpq(rsi, Immediate(6));
528 __ j(not_equal, &fail);
529 __ movq(rax, Immediate(42));
537 __ movq(rax, Immediate(13));
547 Code* code = Code::cast(isolate->heap()->CreateCode(
549 Code::ComputeFlags(Code::STUB),
550 Handle<Code>())->ToObjectChecked());
551 CHECK(code->IsCode());
553 F0 f = FUNCTION_CAST<F0>(code->entry());
560 #define ELEMENT_COUNT 4
562 void DoSSE2(const v8::FunctionCallbackInfo<v8::Value>& args) {
563 v8::HandleScope scope(CcTest::isolate());
566 CHECK(args[0]->IsArray());
567 v8::Local<v8::Array> vec = v8::Local<v8::Array>::Cast(args[0]);
568 CHECK_EQ(ELEMENT_COUNT, vec->Length());
570 Isolate* isolate = CcTest::i_isolate();
571 Assembler assm(isolate, buffer, sizeof(buffer));
573 // Remove return address from the stack for fix stack frame alignment.
576 // Store input vector on the stack.
577 for (int i = 0; i < ELEMENT_COUNT; i++) {
578 __ movl(rax, Immediate(vec->Get(i)->Int32Value()));
579 __ shl(rax, Immediate(0x20));
580 __ or_(rax, Immediate(vec->Get(++i)->Int32Value()));
584 // Read vector into a xmm register.
585 __ xorps(xmm0, xmm0);
586 __ movdqa(xmm0, Operand(rsp, 0));
587 // Create mask and store it in the return register.
588 __ movmskps(rax, xmm0);
590 // Remove unused data from the stack.
591 __ addq(rsp, Immediate(ELEMENT_COUNT * sizeof(int32_t)));
592 // Restore return address.
599 Code* code = Code::cast(isolate->heap()->CreateCode(
601 Code::ComputeFlags(Code::STUB),
602 Handle<Code>())->ToObjectChecked());
603 CHECK(code->IsCode());
605 F0 f = FUNCTION_CAST<F0>(code->entry());
607 args.GetReturnValue().Set(v8::Integer::New(CcTest::isolate(), res));
611 TEST(StackAlignmentForSSE2) {
612 CcTest::InitializeVM();
613 CHECK_EQ(0, OS::ActivationFrameAlignment() % 16);
615 v8::Isolate* isolate = CcTest::isolate();
616 v8::HandleScope handle_scope(isolate);
617 v8::Handle<v8::ObjectTemplate> global_template =
618 v8::ObjectTemplate::New(isolate);
619 global_template->Set(v8_str("do_sse2"),
620 v8::FunctionTemplate::New(isolate, DoSSE2));
622 LocalContext env(NULL, global_template);
624 "function foo(vec) {"
625 " return do_sse2(vec);"
628 v8::Local<v8::Object> global_object = env->Global();
629 v8::Local<v8::Function> foo =
630 v8::Local<v8::Function>::Cast(global_object->Get(v8_str("foo")));
632 int32_t vec[ELEMENT_COUNT] = { -1, 1, 1, 1 };
633 v8::Local<v8::Array> v8_vec = v8::Array::New(isolate, ELEMENT_COUNT);
634 for (int i = 0; i < ELEMENT_COUNT; i++) {
635 v8_vec->Set(i, v8_num(vec[i]));
638 v8::Local<v8::Value> args[] = { v8_vec };
639 v8::Local<v8::Value> result = foo->Call(global_object, 1, args);
641 // The mask should be 0b1000.
642 CHECK_EQ(8, result->Int32Value());
649 TEST(AssemblerX64Extractps) {
650 CcTest::InitializeVM();
651 if (!CpuFeatures::IsSupported(SSE4_1)) return;
653 v8::HandleScope scope(CcTest::isolate());
655 Isolate* isolate = CcTest::i_isolate();
656 Assembler assm(isolate, buffer, sizeof(buffer));
657 { CpuFeatureScope fscope2(&assm, SSE4_1);
658 __ extractps(rax, xmm0, 0x1);
664 Code* code = Code::cast(isolate->heap()->CreateCode(
666 Code::ComputeFlags(Code::STUB),
667 Handle<Code>())->ToObjectChecked());
668 CHECK(code->IsCode());
670 Code::cast(code)->Print();
673 F3 f = FUNCTION_CAST<F3>(Code::cast(code)->entry());
674 uint64_t value1 = V8_2PART_UINT64_C(0x12345678, 87654321);
675 CHECK_EQ(0x12345678, f(uint64_to_double(value1)));
676 uint64_t value2 = V8_2PART_UINT64_C(0x87654321, 12345678);
677 CHECK_EQ(0x87654321, f(uint64_to_double(value2)));
681 typedef int (*F6)(float x, float y);
682 TEST(AssemblerX64SSE) {
683 CcTest::InitializeVM();
685 Isolate* isolate = reinterpret_cast<Isolate*>(CcTest::isolate());
686 HandleScope scope(isolate);
687 v8::internal::byte buffer[256];
688 MacroAssembler assm(isolate, buffer, sizeof buffer);
690 __ shufps(xmm0, xmm0, 0x0); // brocast first argument
691 __ shufps(xmm1, xmm1, 0x0); // brocast second argument
692 __ movaps(xmm2, xmm1);
693 __ addps(xmm2, xmm0);
694 __ mulps(xmm2, xmm1);
695 __ subps(xmm2, xmm0);
696 __ divps(xmm2, xmm1);
697 __ cvttss2si(rax, xmm2);
703 Code* code = Code::cast(isolate->heap()->CreateCode(
705 Code::ComputeFlags(Code::STUB),
706 Handle<Code>())->ToObjectChecked());
707 CHECK(code->IsCode());
709 Code::cast(code)->Print();
712 F6 f = FUNCTION_CAST<F6>(Code::cast(code)->entry());
713 CHECK_EQ(2, f(1.0, 2.0));