Home | History | Annotate | Download | only in cctest
      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 "v8.h"
     31 
     32 #include "macro-assembler.h"
     33 #include "factory.h"
     34 #include "platform.h"
     35 #include "serialize.h"
     36 #include "cctest.h"
     37 
     38 using v8::internal::Assembler;
     39 using v8::internal::Code;
     40 using v8::internal::CodeDesc;
     41 using v8::internal::FUNCTION_CAST;
     42 using v8::internal::Immediate;
     43 using v8::internal::Isolate;
     44 using v8::internal::Label;
     45 using v8::internal::OS;
     46 using v8::internal::Operand;
     47 using v8::internal::byte;
     48 using v8::internal::greater;
     49 using v8::internal::less_equal;
     50 using v8::internal::equal;
     51 using v8::internal::not_equal;
     52 using v8::internal::r13;
     53 using v8::internal::r15;
     54 using v8::internal::r8;
     55 using v8::internal::r9;
     56 using v8::internal::rax;
     57 using v8::internal::rbx;
     58 using v8::internal::rbp;
     59 using v8::internal::rcx;
     60 using v8::internal::rdi;
     61 using v8::internal::rdx;
     62 using v8::internal::rsi;
     63 using v8::internal::rsp;
     64 using v8::internal::times_1;
     65 using v8::internal::xmm0;
     66 
     67 // Test the x64 assembler by compiling some simple functions into
     68 // a buffer and executing them.  These tests do not initialize the
     69 // V8 library, create a context, or use any V8 objects.
     70 // The AMD64 calling convention is used, with the first six arguments
     71 // in RDI, RSI, RDX, RCX, R8, and R9, and floating point arguments in
     72 // the XMM registers.  The return value is in RAX.
     73 // This calling convention is used on Linux, with GCC, and on Mac OS,
     74 // with GCC.  A different convention is used on 64-bit windows,
     75 // where the first four integer arguments are passed in RCX, RDX, R8 and R9.
     76 
     77 typedef int (*F0)();
     78 typedef int (*F1)(int64_t x);
     79 typedef int (*F2)(int64_t x, int64_t y);
     80 
     81 #ifdef _WIN64
     82 static const v8::internal::Register arg1 = rcx;
     83 static const v8::internal::Register arg2 = rdx;
     84 #else
     85 static const v8::internal::Register arg1 = rdi;
     86 static const v8::internal::Register arg2 = rsi;
     87 #endif
     88 
     89 #define __ assm.
     90 
     91 
     92 TEST(AssemblerX64ReturnOperation) {
     93   OS::SetUp();
     94   // Allocate an executable page of memory.
     95   size_t actual_size;
     96   byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
     97                                                  &actual_size,
     98                                                  true));
     99   CHECK(buffer);
    100   Assembler assm(Isolate::Current(), buffer, static_cast<int>(actual_size));
    101 
    102   // Assemble a simple function that copies argument 2 and returns it.
    103   __ movq(rax, arg2);
    104   __ nop();
    105   __ ret(0);
    106 
    107   CodeDesc desc;
    108   assm.GetCode(&desc);
    109   // Call the function from C++.
    110   int result =  FUNCTION_CAST<F2>(buffer)(3, 2);
    111   CHECK_EQ(2, result);
    112 }
    113 
    114 
    115 TEST(AssemblerX64StackOperations) {
    116   OS::SetUp();
    117   // Allocate an executable page of memory.
    118   size_t actual_size;
    119   byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
    120                                                  &actual_size,
    121                                                  true));
    122   CHECK(buffer);
    123   Assembler assm(Isolate::Current(), buffer, static_cast<int>(actual_size));
    124 
    125   // Assemble a simple function that copies argument 2 and returns it.
    126   // We compile without stack frame pointers, so the gdb debugger shows
    127   // incorrect stack frames when debugging this function (which has them).
    128   __ push(rbp);
    129   __ movq(rbp, rsp);
    130   __ push(arg2);  // Value at (rbp - 8)
    131   __ push(arg2);  // Value at (rbp - 16)
    132   __ push(arg1);  // Value at (rbp - 24)
    133   __ pop(rax);
    134   __ pop(rax);
    135   __ pop(rax);
    136   __ pop(rbp);
    137   __ nop();
    138   __ ret(0);
    139 
    140   CodeDesc desc;
    141   assm.GetCode(&desc);
    142   // Call the function from C++.
    143   int result =  FUNCTION_CAST<F2>(buffer)(3, 2);
    144   CHECK_EQ(2, result);
    145 }
    146 
    147 
    148 TEST(AssemblerX64ArithmeticOperations) {
    149   OS::SetUp();
    150   // Allocate an executable page of memory.
    151   size_t actual_size;
    152   byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
    153                                                  &actual_size,
    154                                                  true));
    155   CHECK(buffer);
    156   Assembler assm(Isolate::Current(), buffer, static_cast<int>(actual_size));
    157 
    158   // Assemble a simple function that adds arguments returning the sum.
    159   __ movq(rax, arg2);
    160   __ addq(rax, arg1);
    161   __ ret(0);
    162 
    163   CodeDesc desc;
    164   assm.GetCode(&desc);
    165   // Call the function from C++.
    166   int result =  FUNCTION_CAST<F2>(buffer)(3, 2);
    167   CHECK_EQ(5, result);
    168 }
    169 
    170 
    171 TEST(AssemblerX64ImulOperation) {
    172   OS::SetUp();
    173   // Allocate an executable page of memory.
    174   size_t actual_size;
    175   byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
    176                                                  &actual_size,
    177                                                  true));
    178   CHECK(buffer);
    179   Assembler assm(Isolate::Current(), buffer, static_cast<int>(actual_size));
    180 
    181   // Assemble a simple function that multiplies arguments returning the high
    182   // word.
    183   __ movq(rax, arg2);
    184   __ imul(arg1);
    185   __ movq(rax, rdx);
    186   __ ret(0);
    187 
    188   CodeDesc desc;
    189   assm.GetCode(&desc);
    190   // Call the function from C++.
    191   int result =  FUNCTION_CAST<F2>(buffer)(3, 2);
    192   CHECK_EQ(0, result);
    193   result =  FUNCTION_CAST<F2>(buffer)(0x100000000l, 0x100000000l);
    194   CHECK_EQ(1, result);
    195   result =  FUNCTION_CAST<F2>(buffer)(-0x100000000l, 0x100000000l);
    196   CHECK_EQ(-1, result);
    197 }
    198 
    199 
    200 TEST(AssemblerX64MemoryOperands) {
    201   OS::SetUp();
    202   // Allocate an executable page of memory.
    203   size_t actual_size;
    204   byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
    205                                                  &actual_size,
    206                                                  true));
    207   CHECK(buffer);
    208   Assembler assm(Isolate::Current(), buffer, static_cast<int>(actual_size));
    209 
    210   // Assemble a simple function that copies argument 2 and returns it.
    211   __ push(rbp);
    212   __ movq(rbp, rsp);
    213 
    214   __ push(arg2);  // Value at (rbp - 8)
    215   __ push(arg2);  // Value at (rbp - 16)
    216   __ push(arg1);  // Value at (rbp - 24)
    217 
    218   const int kStackElementSize = 8;
    219   __ movq(rax, Operand(rbp, -3 * kStackElementSize));
    220   __ pop(arg2);
    221   __ pop(arg2);
    222   __ pop(arg2);
    223   __ pop(rbp);
    224   __ nop();
    225   __ ret(0);
    226 
    227   CodeDesc desc;
    228   assm.GetCode(&desc);
    229   // Call the function from C++.
    230   int result =  FUNCTION_CAST<F2>(buffer)(3, 2);
    231   CHECK_EQ(3, result);
    232 }
    233 
    234 
    235 TEST(AssemblerX64ControlFlow) {
    236   OS::SetUp();
    237   // Allocate an executable page of memory.
    238   size_t actual_size;
    239   byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
    240                                                  &actual_size,
    241                                                  true));
    242   CHECK(buffer);
    243   Assembler assm(Isolate::Current(), buffer, static_cast<int>(actual_size));
    244 
    245   // Assemble a simple function that copies argument 1 and returns it.
    246   __ push(rbp);
    247 
    248   __ movq(rbp, rsp);
    249   __ movq(rax, arg1);
    250   Label target;
    251   __ jmp(&target);
    252   __ movq(rax, arg2);
    253   __ bind(&target);
    254   __ pop(rbp);
    255   __ ret(0);
    256 
    257   CodeDesc desc;
    258   assm.GetCode(&desc);
    259   // Call the function from C++.
    260   int result =  FUNCTION_CAST<F2>(buffer)(3, 2);
    261   CHECK_EQ(3, result);
    262 }
    263 
    264 
    265 TEST(AssemblerX64LoopImmediates) {
    266   OS::SetUp();
    267   // Allocate an executable page of memory.
    268   size_t actual_size;
    269   byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
    270                                                  &actual_size,
    271                                                  true));
    272   CHECK(buffer);
    273   Assembler assm(Isolate::Current(), buffer, static_cast<int>(actual_size));
    274   // Assemble two loops using rax as counter, and verify the ending counts.
    275   Label Fail;
    276   __ movq(rax, Immediate(-3));
    277   Label Loop1_test;
    278   Label Loop1_body;
    279   __ jmp(&Loop1_test);
    280   __ bind(&Loop1_body);
    281   __ addq(rax, Immediate(7));
    282   __ bind(&Loop1_test);
    283   __ cmpq(rax, Immediate(20));
    284   __ j(less_equal, &Loop1_body);
    285   // Did the loop terminate with the expected value?
    286   __ cmpq(rax, Immediate(25));
    287   __ j(not_equal, &Fail);
    288 
    289   Label Loop2_test;
    290   Label Loop2_body;
    291   __ movq(rax, Immediate(0x11FEED00));
    292   __ jmp(&Loop2_test);
    293   __ bind(&Loop2_body);
    294   __ addq(rax, Immediate(-0x1100));
    295   __ bind(&Loop2_test);
    296   __ cmpq(rax, Immediate(0x11FE8000));
    297   __ j(greater, &Loop2_body);
    298   // Did the loop terminate with the expected value?
    299   __ cmpq(rax, Immediate(0x11FE7600));
    300   __ j(not_equal, &Fail);
    301 
    302   __ movq(rax, Immediate(1));
    303   __ ret(0);
    304   __ bind(&Fail);
    305   __ movq(rax, Immediate(0));
    306   __ ret(0);
    307 
    308   CodeDesc desc;
    309   assm.GetCode(&desc);
    310   // Call the function from C++.
    311   int result =  FUNCTION_CAST<F0>(buffer)();
    312   CHECK_EQ(1, result);
    313 }
    314 
    315 
    316 TEST(OperandRegisterDependency) {
    317   int offsets[4] = {0, 1, 0xfed, 0xbeefcad};
    318   for (int i = 0; i < 4; i++) {
    319     int offset = offsets[i];
    320     CHECK(Operand(rax, offset).AddressUsesRegister(rax));
    321     CHECK(!Operand(rax, offset).AddressUsesRegister(r8));
    322     CHECK(!Operand(rax, offset).AddressUsesRegister(rcx));
    323 
    324     CHECK(Operand(rax, rax, times_1, offset).AddressUsesRegister(rax));
    325     CHECK(!Operand(rax, rax, times_1, offset).AddressUsesRegister(r8));
    326     CHECK(!Operand(rax, rax, times_1, offset).AddressUsesRegister(rcx));
    327 
    328     CHECK(Operand(rax, rcx, times_1, offset).AddressUsesRegister(rax));
    329     CHECK(Operand(rax, rcx, times_1, offset).AddressUsesRegister(rcx));
    330     CHECK(!Operand(rax, rcx, times_1, offset).AddressUsesRegister(r8));
    331     CHECK(!Operand(rax, rcx, times_1, offset).AddressUsesRegister(r9));
    332     CHECK(!Operand(rax, rcx, times_1, offset).AddressUsesRegister(rdx));
    333     CHECK(!Operand(rax, rcx, times_1, offset).AddressUsesRegister(rsp));
    334 
    335     CHECK(Operand(rsp, offset).AddressUsesRegister(rsp));
    336     CHECK(!Operand(rsp, offset).AddressUsesRegister(rax));
    337     CHECK(!Operand(rsp, offset).AddressUsesRegister(r15));
    338 
    339     CHECK(Operand(rbp, offset).AddressUsesRegister(rbp));
    340     CHECK(!Operand(rbp, offset).AddressUsesRegister(rax));
    341     CHECK(!Operand(rbp, offset).AddressUsesRegister(r13));
    342 
    343     CHECK(Operand(rbp, rax, times_1, offset).AddressUsesRegister(rbp));
    344     CHECK(Operand(rbp, rax, times_1, offset).AddressUsesRegister(rax));
    345     CHECK(!Operand(rbp, rax, times_1, offset).AddressUsesRegister(rcx));
    346     CHECK(!Operand(rbp, rax, times_1, offset).AddressUsesRegister(r13));
    347     CHECK(!Operand(rbp, rax, times_1, offset).AddressUsesRegister(r8));
    348     CHECK(!Operand(rbp, rax, times_1, offset).AddressUsesRegister(rsp));
    349 
    350     CHECK(Operand(rsp, rbp, times_1, offset).AddressUsesRegister(rsp));
    351     CHECK(Operand(rsp, rbp, times_1, offset).AddressUsesRegister(rbp));
    352     CHECK(!Operand(rsp, rbp, times_1, offset).AddressUsesRegister(rax));
    353     CHECK(!Operand(rsp, rbp, times_1, offset).AddressUsesRegister(r15));
    354     CHECK(!Operand(rsp, rbp, times_1, offset).AddressUsesRegister(r13));
    355   }
    356 }
    357 
    358 
    359 TEST(AssemblerX64LabelChaining) {
    360   // Test chaining of label usages within instructions (issue 1644).
    361   CcTest::InitializeVM();
    362   v8::HandleScope scope(CcTest::isolate());
    363   Assembler assm(Isolate::Current(), NULL, 0);
    364 
    365   Label target;
    366   __ j(equal, &target);
    367   __ j(not_equal, &target);
    368   __ bind(&target);
    369   __ nop();
    370 }
    371 
    372 
    373 TEST(AssemblerMultiByteNop) {
    374   CcTest::InitializeVM();
    375   v8::HandleScope scope(CcTest::isolate());
    376   v8::internal::byte buffer[1024];
    377   Isolate* isolate = Isolate::Current();
    378   Assembler assm(isolate, buffer, sizeof(buffer));
    379   __ push(rbx);
    380   __ push(rcx);
    381   __ push(rdx);
    382   __ push(rdi);
    383   __ push(rsi);
    384   __ movq(rax, Immediate(1));
    385   __ movq(rbx, Immediate(2));
    386   __ movq(rcx, Immediate(3));
    387   __ movq(rdx, Immediate(4));
    388   __ movq(rdi, Immediate(5));
    389   __ movq(rsi, Immediate(6));
    390   for (int i = 0; i < 16; i++) {
    391     int before = assm.pc_offset();
    392     __ Nop(i);
    393     CHECK_EQ(assm.pc_offset() - before, i);
    394   }
    395 
    396   Label fail;
    397   __ cmpq(rax, Immediate(1));
    398   __ j(not_equal, &fail);
    399   __ cmpq(rbx, Immediate(2));
    400   __ j(not_equal, &fail);
    401   __ cmpq(rcx, Immediate(3));
    402   __ j(not_equal, &fail);
    403   __ cmpq(rdx, Immediate(4));
    404   __ j(not_equal, &fail);
    405   __ cmpq(rdi, Immediate(5));
    406   __ j(not_equal, &fail);
    407   __ cmpq(rsi, Immediate(6));
    408   __ j(not_equal, &fail);
    409   __ movq(rax, Immediate(42));
    410   __ pop(rsi);
    411   __ pop(rdi);
    412   __ pop(rdx);
    413   __ pop(rcx);
    414   __ pop(rbx);
    415   __ ret(0);
    416   __ bind(&fail);
    417   __ movq(rax, Immediate(13));
    418   __ pop(rsi);
    419   __ pop(rdi);
    420   __ pop(rdx);
    421   __ pop(rcx);
    422   __ pop(rbx);
    423   __ ret(0);
    424 
    425   CodeDesc desc;
    426   assm.GetCode(&desc);
    427   Code* code = Code::cast(isolate->heap()->CreateCode(
    428       desc,
    429       Code::ComputeFlags(Code::STUB),
    430       v8::internal::Handle<Code>())->ToObjectChecked());
    431   CHECK(code->IsCode());
    432 
    433   F0 f = FUNCTION_CAST<F0>(code->entry());
    434   int res = f();
    435   CHECK_EQ(42, res);
    436 }
    437 
    438 
    439 #ifdef __GNUC__
    440 #define ELEMENT_COUNT 4
    441 
    442 void DoSSE2(const v8::FunctionCallbackInfo<v8::Value>& args) {
    443   CcTest::InitializeVM();
    444   v8::HandleScope scope(CcTest::isolate());
    445   v8::internal::byte buffer[1024];
    446 
    447   CHECK(args[0]->IsArray());
    448   v8::Local<v8::Array> vec = v8::Local<v8::Array>::Cast(args[0]);
    449   CHECK_EQ(ELEMENT_COUNT, vec->Length());
    450 
    451   Isolate* isolate = Isolate::Current();
    452   Assembler assm(isolate, buffer, sizeof(buffer));
    453 
    454   // Remove return address from the stack for fix stack frame alignment.
    455   __ pop(rcx);
    456 
    457   // Store input vector on the stack.
    458   for (int i = 0; i < ELEMENT_COUNT; i++) {
    459     __ movl(rax, Immediate(vec->Get(i)->Int32Value()));
    460     __ shl(rax, Immediate(0x20));
    461     __ or_(rax, Immediate(vec->Get(++i)->Int32Value()));
    462     __ push(rax);
    463   }
    464 
    465   // Read vector into a xmm register.
    466   __ xorps(xmm0, xmm0);
    467   __ movdqa(xmm0, Operand(rsp, 0));
    468   // Create mask and store it in the return register.
    469   __ movmskps(rax, xmm0);
    470 
    471   // Remove unused data from the stack.
    472   __ addq(rsp, Immediate(ELEMENT_COUNT * sizeof(int32_t)));
    473   // Restore return address.
    474   __ push(rcx);
    475 
    476   __ ret(0);
    477 
    478   CodeDesc desc;
    479   assm.GetCode(&desc);
    480   Code* code = Code::cast(isolate->heap()->CreateCode(
    481       desc,
    482       Code::ComputeFlags(Code::STUB),
    483       v8::internal::Handle<Code>())->ToObjectChecked());
    484   CHECK(code->IsCode());
    485 
    486   F0 f = FUNCTION_CAST<F0>(code->entry());
    487   int res = f();
    488   args.GetReturnValue().Set(v8::Integer::New(res));
    489 }
    490 
    491 
    492 TEST(StackAlignmentForSSE2) {
    493   CHECK_EQ(0, OS::ActivationFrameAlignment() % 16);
    494 
    495   v8::Isolate* isolate = v8::Isolate::GetCurrent();
    496   v8::HandleScope handle_scope(isolate);
    497   v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
    498   global_template->Set(v8_str("do_sse2"), v8::FunctionTemplate::New(DoSSE2));
    499 
    500   LocalContext env(NULL, global_template);
    501   CompileRun(
    502       "function foo(vec) {"
    503       "  return do_sse2(vec);"
    504       "}");
    505 
    506   v8::Local<v8::Object> global_object = env->Global();
    507   v8::Local<v8::Function> foo =
    508       v8::Local<v8::Function>::Cast(global_object->Get(v8_str("foo")));
    509 
    510   int32_t vec[ELEMENT_COUNT] = { -1, 1, 1, 1 };
    511   v8::Local<v8::Array> v8_vec = v8::Array::New(ELEMENT_COUNT);
    512   for (int i = 0; i < ELEMENT_COUNT; i++) {
    513     v8_vec->Set(i, v8_num(vec[i]));
    514   }
    515 
    516   v8::Local<v8::Value> args[] = { v8_vec };
    517   v8::Local<v8::Value> result = foo->Call(global_object, 1, args);
    518 
    519   // The mask should be 0b1000.
    520   CHECK_EQ(8, result->Int32Value());
    521 }
    522 
    523 #undef ELEMENT_COUNT
    524 #endif  // __GNUC__
    525 
    526 
    527 #undef __
    528