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 
     66 // Test the x64 assembler by compiling some simple functions into
     67 // a buffer and executing them.  These tests do not initialize the
     68 // V8 library, create a context, or use any V8 objects.
     69 // The AMD64 calling convention is used, with the first six arguments
     70 // in RDI, RSI, RDX, RCX, R8, and R9, and floating point arguments in
     71 // the XMM registers.  The return value is in RAX.
     72 // This calling convention is used on Linux, with GCC, and on Mac OS,
     73 // with GCC.  A different convention is used on 64-bit windows,
     74 // where the first four integer arguments are passed in RCX, RDX, R8 and R9.
     75 
     76 typedef int (*F0)();
     77 typedef int (*F1)(int64_t x);
     78 typedef int (*F2)(int64_t x, int64_t y);
     79 
     80 #ifdef _WIN64
     81 static const v8::internal::Register arg1 = rcx;
     82 static const v8::internal::Register arg2 = rdx;
     83 #else
     84 static const v8::internal::Register arg1 = rdi;
     85 static const v8::internal::Register arg2 = rsi;
     86 #endif
     87 
     88 #define __ assm.
     89 
     90 
     91 static v8::Persistent<v8::Context> env;
     92 
     93 
     94 static void InitializeVM() {
     95   if (env.IsEmpty()) {
     96     env = v8::Context::New();
     97   }
     98 }
     99 
    100 
    101 TEST(AssemblerX64ReturnOperation) {
    102   OS::SetUp();
    103   // Allocate an executable page of memory.
    104   size_t actual_size;
    105   byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
    106                                                  &actual_size,
    107                                                  true));
    108   CHECK(buffer);
    109   Assembler assm(Isolate::Current(), buffer, static_cast<int>(actual_size));
    110 
    111   // Assemble a simple function that copies argument 2 and returns it.
    112   __ movq(rax, arg2);
    113   __ nop();
    114   __ ret(0);
    115 
    116   CodeDesc desc;
    117   assm.GetCode(&desc);
    118   // Call the function from C++.
    119   int result =  FUNCTION_CAST<F2>(buffer)(3, 2);
    120   CHECK_EQ(2, result);
    121 }
    122 
    123 TEST(AssemblerX64StackOperations) {
    124   OS::SetUp();
    125   // Allocate an executable page of memory.
    126   size_t actual_size;
    127   byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
    128                                                  &actual_size,
    129                                                  true));
    130   CHECK(buffer);
    131   Assembler assm(Isolate::Current(), buffer, static_cast<int>(actual_size));
    132 
    133   // Assemble a simple function that copies argument 2 and returns it.
    134   // We compile without stack frame pointers, so the gdb debugger shows
    135   // incorrect stack frames when debugging this function (which has them).
    136   __ push(rbp);
    137   __ movq(rbp, rsp);
    138   __ push(arg2);  // Value at (rbp - 8)
    139   __ push(arg2);  // Value at (rbp - 16)
    140   __ push(arg1);  // Value at (rbp - 24)
    141   __ pop(rax);
    142   __ pop(rax);
    143   __ pop(rax);
    144   __ pop(rbp);
    145   __ nop();
    146   __ ret(0);
    147 
    148   CodeDesc desc;
    149   assm.GetCode(&desc);
    150   // Call the function from C++.
    151   int result =  FUNCTION_CAST<F2>(buffer)(3, 2);
    152   CHECK_EQ(2, result);
    153 }
    154 
    155 TEST(AssemblerX64ArithmeticOperations) {
    156   OS::SetUp();
    157   // Allocate an executable page of memory.
    158   size_t actual_size;
    159   byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
    160                                                  &actual_size,
    161                                                  true));
    162   CHECK(buffer);
    163   Assembler assm(Isolate::Current(), buffer, static_cast<int>(actual_size));
    164 
    165   // Assemble a simple function that adds arguments returning the sum.
    166   __ movq(rax, arg2);
    167   __ addq(rax, arg1);
    168   __ ret(0);
    169 
    170   CodeDesc desc;
    171   assm.GetCode(&desc);
    172   // Call the function from C++.
    173   int result =  FUNCTION_CAST<F2>(buffer)(3, 2);
    174   CHECK_EQ(5, result);
    175 }
    176 
    177 TEST(AssemblerX64ImulOperation) {
    178   OS::SetUp();
    179   // Allocate an executable page of memory.
    180   size_t actual_size;
    181   byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
    182                                                  &actual_size,
    183                                                  true));
    184   CHECK(buffer);
    185   Assembler assm(Isolate::Current(), buffer, static_cast<int>(actual_size));
    186 
    187   // Assemble a simple function that multiplies arguments returning the high
    188   // word.
    189   __ movq(rax, arg2);
    190   __ imul(arg1);
    191   __ movq(rax, rdx);
    192   __ ret(0);
    193 
    194   CodeDesc desc;
    195   assm.GetCode(&desc);
    196   // Call the function from C++.
    197   int result =  FUNCTION_CAST<F2>(buffer)(3, 2);
    198   CHECK_EQ(0, result);
    199   result =  FUNCTION_CAST<F2>(buffer)(0x100000000l, 0x100000000l);
    200   CHECK_EQ(1, result);
    201   result =  FUNCTION_CAST<F2>(buffer)(-0x100000000l, 0x100000000l);
    202   CHECK_EQ(-1, result);
    203 }
    204 
    205 TEST(AssemblerX64MemoryOperands) {
    206   OS::SetUp();
    207   // Allocate an executable page of memory.
    208   size_t actual_size;
    209   byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
    210                                                  &actual_size,
    211                                                  true));
    212   CHECK(buffer);
    213   Assembler assm(Isolate::Current(), buffer, static_cast<int>(actual_size));
    214 
    215   // Assemble a simple function that copies argument 2 and returns it.
    216   __ push(rbp);
    217   __ movq(rbp, rsp);
    218 
    219   __ push(arg2);  // Value at (rbp - 8)
    220   __ push(arg2);  // Value at (rbp - 16)
    221   __ push(arg1);  // Value at (rbp - 24)
    222 
    223   const int kStackElementSize = 8;
    224   __ movq(rax, Operand(rbp, -3 * kStackElementSize));
    225   __ pop(arg2);
    226   __ pop(arg2);
    227   __ pop(arg2);
    228   __ pop(rbp);
    229   __ nop();
    230   __ ret(0);
    231 
    232   CodeDesc desc;
    233   assm.GetCode(&desc);
    234   // Call the function from C++.
    235   int result =  FUNCTION_CAST<F2>(buffer)(3, 2);
    236   CHECK_EQ(3, result);
    237 }
    238 
    239 TEST(AssemblerX64ControlFlow) {
    240   OS::SetUp();
    241   // Allocate an executable page of memory.
    242   size_t actual_size;
    243   byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
    244                                                  &actual_size,
    245                                                  true));
    246   CHECK(buffer);
    247   Assembler assm(Isolate::Current(), buffer, static_cast<int>(actual_size));
    248 
    249   // Assemble a simple function that copies argument 1 and returns it.
    250   __ push(rbp);
    251 
    252   __ movq(rbp, rsp);
    253   __ movq(rax, arg1);
    254   Label target;
    255   __ jmp(&target);
    256   __ movq(rax, arg2);
    257   __ bind(&target);
    258   __ pop(rbp);
    259   __ ret(0);
    260 
    261   CodeDesc desc;
    262   assm.GetCode(&desc);
    263   // Call the function from C++.
    264   int result =  FUNCTION_CAST<F2>(buffer)(3, 2);
    265   CHECK_EQ(3, result);
    266 }
    267 
    268 TEST(AssemblerX64LoopImmediates) {
    269   OS::SetUp();
    270   // Allocate an executable page of memory.
    271   size_t actual_size;
    272   byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
    273                                                  &actual_size,
    274                                                  true));
    275   CHECK(buffer);
    276   Assembler assm(Isolate::Current(), buffer, static_cast<int>(actual_size));
    277   // Assemble two loops using rax as counter, and verify the ending counts.
    278   Label Fail;
    279   __ movq(rax, Immediate(-3));
    280   Label Loop1_test;
    281   Label Loop1_body;
    282   __ jmp(&Loop1_test);
    283   __ bind(&Loop1_body);
    284   __ addq(rax, Immediate(7));
    285   __ bind(&Loop1_test);
    286   __ cmpq(rax, Immediate(20));
    287   __ j(less_equal, &Loop1_body);
    288   // Did the loop terminate with the expected value?
    289   __ cmpq(rax, Immediate(25));
    290   __ j(not_equal, &Fail);
    291 
    292   Label Loop2_test;
    293   Label Loop2_body;
    294   __ movq(rax, Immediate(0x11FEED00));
    295   __ jmp(&Loop2_test);
    296   __ bind(&Loop2_body);
    297   __ addq(rax, Immediate(-0x1100));
    298   __ bind(&Loop2_test);
    299   __ cmpq(rax, Immediate(0x11FE8000));
    300   __ j(greater, &Loop2_body);
    301   // Did the loop terminate with the expected value?
    302   __ cmpq(rax, Immediate(0x11FE7600));
    303   __ j(not_equal, &Fail);
    304 
    305   __ movq(rax, Immediate(1));
    306   __ ret(0);
    307   __ bind(&Fail);
    308   __ movq(rax, Immediate(0));
    309   __ ret(0);
    310 
    311   CodeDesc desc;
    312   assm.GetCode(&desc);
    313   // Call the function from C++.
    314   int result =  FUNCTION_CAST<F0>(buffer)();
    315   CHECK_EQ(1, result);
    316 }
    317 
    318 
    319 TEST(OperandRegisterDependency) {
    320   int offsets[4] = {0, 1, 0xfed, 0xbeefcad};
    321   for (int i = 0; i < 4; i++) {
    322     int offset = offsets[i];
    323     CHECK(Operand(rax, offset).AddressUsesRegister(rax));
    324     CHECK(!Operand(rax, offset).AddressUsesRegister(r8));
    325     CHECK(!Operand(rax, offset).AddressUsesRegister(rcx));
    326 
    327     CHECK(Operand(rax, rax, times_1, offset).AddressUsesRegister(rax));
    328     CHECK(!Operand(rax, rax, times_1, offset).AddressUsesRegister(r8));
    329     CHECK(!Operand(rax, rax, times_1, offset).AddressUsesRegister(rcx));
    330 
    331     CHECK(Operand(rax, rcx, times_1, offset).AddressUsesRegister(rax));
    332     CHECK(Operand(rax, rcx, times_1, offset).AddressUsesRegister(rcx));
    333     CHECK(!Operand(rax, rcx, times_1, offset).AddressUsesRegister(r8));
    334     CHECK(!Operand(rax, rcx, times_1, offset).AddressUsesRegister(r9));
    335     CHECK(!Operand(rax, rcx, times_1, offset).AddressUsesRegister(rdx));
    336     CHECK(!Operand(rax, rcx, times_1, offset).AddressUsesRegister(rsp));
    337 
    338     CHECK(Operand(rsp, offset).AddressUsesRegister(rsp));
    339     CHECK(!Operand(rsp, offset).AddressUsesRegister(rax));
    340     CHECK(!Operand(rsp, offset).AddressUsesRegister(r15));
    341 
    342     CHECK(Operand(rbp, offset).AddressUsesRegister(rbp));
    343     CHECK(!Operand(rbp, offset).AddressUsesRegister(rax));
    344     CHECK(!Operand(rbp, offset).AddressUsesRegister(r13));
    345 
    346     CHECK(Operand(rbp, rax, times_1, offset).AddressUsesRegister(rbp));
    347     CHECK(Operand(rbp, rax, times_1, offset).AddressUsesRegister(rax));
    348     CHECK(!Operand(rbp, rax, times_1, offset).AddressUsesRegister(rcx));
    349     CHECK(!Operand(rbp, rax, times_1, offset).AddressUsesRegister(r13));
    350     CHECK(!Operand(rbp, rax, times_1, offset).AddressUsesRegister(r8));
    351     CHECK(!Operand(rbp, rax, times_1, offset).AddressUsesRegister(rsp));
    352 
    353     CHECK(Operand(rsp, rbp, times_1, offset).AddressUsesRegister(rsp));
    354     CHECK(Operand(rsp, rbp, times_1, offset).AddressUsesRegister(rbp));
    355     CHECK(!Operand(rsp, rbp, times_1, offset).AddressUsesRegister(rax));
    356     CHECK(!Operand(rsp, rbp, times_1, offset).AddressUsesRegister(r15));
    357     CHECK(!Operand(rsp, rbp, times_1, offset).AddressUsesRegister(r13));
    358   }
    359 }
    360 
    361 
    362 TEST(AssemblerX64LabelChaining) {
    363   // Test chaining of label usages within instructions (issue 1644).
    364   v8::HandleScope scope;
    365   Assembler assm(Isolate::Current(), NULL, 0);
    366 
    367   Label target;
    368   __ j(equal, &target);
    369   __ j(not_equal, &target);
    370   __ bind(&target);
    371   __ nop();
    372 }
    373 
    374 
    375 TEST(AssemblerMultiByteNop) {
    376   InitializeVM();
    377   v8::HandleScope scope;
    378   v8::internal::byte buffer[1024];
    379   Assembler assm(Isolate::Current(), buffer, sizeof(buffer));
    380   __ push(rbx);
    381   __ push(rcx);
    382   __ push(rdx);
    383   __ push(rdi);
    384   __ push(rsi);
    385   __ movq(rax, Immediate(1));
    386   __ movq(rbx, Immediate(2));
    387   __ movq(rcx, Immediate(3));
    388   __ movq(rdx, Immediate(4));
    389   __ movq(rdi, Immediate(5));
    390   __ movq(rsi, Immediate(6));
    391   for (int i = 0; i < 16; i++) {
    392     int before = assm.pc_offset();
    393     __ Nop(i);
    394     CHECK_EQ(assm.pc_offset() - before, i);
    395   }
    396 
    397   Label fail;
    398   __ cmpq(rax, Immediate(1));
    399   __ j(not_equal, &fail);
    400   __ cmpq(rbx, Immediate(2));
    401   __ j(not_equal, &fail);
    402   __ cmpq(rcx, Immediate(3));
    403   __ j(not_equal, &fail);
    404   __ cmpq(rdx, Immediate(4));
    405   __ j(not_equal, &fail);
    406   __ cmpq(rdi, Immediate(5));
    407   __ j(not_equal, &fail);
    408   __ cmpq(rsi, Immediate(6));
    409   __ j(not_equal, &fail);
    410   __ movq(rax, Immediate(42));
    411   __ pop(rsi);
    412   __ pop(rdi);
    413   __ pop(rdx);
    414   __ pop(rcx);
    415   __ pop(rbx);
    416   __ ret(0);
    417   __ bind(&fail);
    418   __ movq(rax, Immediate(13));
    419   __ pop(rsi);
    420   __ pop(rdi);
    421   __ pop(rdx);
    422   __ pop(rcx);
    423   __ pop(rbx);
    424   __ ret(0);
    425 
    426   CodeDesc desc;
    427   assm.GetCode(&desc);
    428   Code* code = Code::cast(HEAP->CreateCode(
    429       desc,
    430       Code::ComputeFlags(Code::STUB),
    431       v8::internal::Handle<v8::internal::Object>(
    432           HEAP->undefined_value()))->ToObjectChecked());
    433   CHECK(code->IsCode());
    434 
    435   F0 f = FUNCTION_CAST<F0>(code->entry());
    436   int res = f();
    437   CHECK_EQ(42, res);
    438 }
    439 
    440 
    441 
    442 
    443 #undef __
    444