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::byte;
     39 using v8::internal::OS;
     40 using v8::internal::Assembler;
     41 using v8::internal::Operand;
     42 using v8::internal::Immediate;
     43 using v8::internal::Label;
     44 using v8::internal::rax;
     45 using v8::internal::rsi;
     46 using v8::internal::rdi;
     47 using v8::internal::rcx;
     48 using v8::internal::rdx;
     49 using v8::internal::rbp;
     50 using v8::internal::rsp;
     51 using v8::internal::FUNCTION_CAST;
     52 using v8::internal::CodeDesc;
     53 using v8::internal::less_equal;
     54 using v8::internal::not_equal;
     55 using v8::internal::greater;
     56 
     57 // Test the x64 assembler by compiling some simple functions into
     58 // a buffer and executing them.  These tests do not initialize the
     59 // V8 library, create a context, or use any V8 objects.
     60 // The AMD64 calling convention is used, with the first six arguments
     61 // in RDI, RSI, RDX, RCX, R8, and R9, and floating point arguments in
     62 // the XMM registers.  The return value is in RAX.
     63 // This calling convention is used on Linux, with GCC, and on Mac OS,
     64 // with GCC.  A different convention is used on 64-bit windows,
     65 // where the first four integer arguments are passed in RCX, RDX, R8 and R9.
     66 
     67 typedef int (*F0)();
     68 typedef int (*F1)(int64_t x);
     69 typedef int (*F2)(int64_t x, int64_t y);
     70 
     71 #ifdef _WIN64
     72 static const v8::internal::Register arg1 = rcx;
     73 static const v8::internal::Register arg2 = rdx;
     74 #else
     75 static const v8::internal::Register arg1 = rdi;
     76 static const v8::internal::Register arg2 = rsi;
     77 #endif
     78 
     79 #define __ assm.
     80 
     81 
     82 TEST(AssemblerX64ReturnOperation) {
     83   // Allocate an executable page of memory.
     84   size_t actual_size;
     85   byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
     86                                                  &actual_size,
     87                                                  true));
     88   CHECK(buffer);
     89   Assembler assm(buffer, static_cast<int>(actual_size));
     90 
     91   // Assemble a simple function that copies argument 2 and returns it.
     92   __ movq(rax, arg2);
     93   __ nop();
     94   __ ret(0);
     95 
     96   CodeDesc desc;
     97   assm.GetCode(&desc);
     98   // Call the function from C++.
     99   int result =  FUNCTION_CAST<F2>(buffer)(3, 2);
    100   CHECK_EQ(2, result);
    101 }
    102 
    103 TEST(AssemblerX64StackOperations) {
    104   // Allocate an executable page of memory.
    105   size_t actual_size;
    106   byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
    107                                                  &actual_size,
    108                                                  true));
    109   CHECK(buffer);
    110   Assembler assm(buffer, static_cast<int>(actual_size));
    111 
    112   // Assemble a simple function that copies argument 2 and returns it.
    113   // We compile without stack frame pointers, so the gdb debugger shows
    114   // incorrect stack frames when debugging this function (which has them).
    115   __ push(rbp);
    116   __ movq(rbp, rsp);
    117   __ push(arg2);  // Value at (rbp - 8)
    118   __ push(arg2);  // Value at (rbp - 16)
    119   __ push(arg1);  // Value at (rbp - 24)
    120   __ pop(rax);
    121   __ pop(rax);
    122   __ pop(rax);
    123   __ pop(rbp);
    124   __ nop();
    125   __ ret(0);
    126 
    127   CodeDesc desc;
    128   assm.GetCode(&desc);
    129   // Call the function from C++.
    130   int result =  FUNCTION_CAST<F2>(buffer)(3, 2);
    131   CHECK_EQ(2, result);
    132 }
    133 
    134 TEST(AssemblerX64ArithmeticOperations) {
    135   // Allocate an executable page of memory.
    136   size_t actual_size;
    137   byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
    138                                                  &actual_size,
    139                                                  true));
    140   CHECK(buffer);
    141   Assembler assm(buffer, static_cast<int>(actual_size));
    142 
    143   // Assemble a simple function that adds arguments returning the sum.
    144   __ movq(rax, arg2);
    145   __ addq(rax, arg1);
    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(5, result);
    153 }
    154 
    155 TEST(AssemblerX64ImulOperation) {
    156   // Allocate an executable page of memory.
    157   size_t actual_size;
    158   byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
    159                                                  &actual_size,
    160                                                  true));
    161   CHECK(buffer);
    162   Assembler assm(buffer, static_cast<int>(actual_size));
    163 
    164   // Assemble a simple function that multiplies arguments returning the high
    165   // word.
    166   __ movq(rax, arg2);
    167   __ imul(arg1);
    168   __ movq(rax, rdx);
    169   __ ret(0);
    170 
    171   CodeDesc desc;
    172   assm.GetCode(&desc);
    173   // Call the function from C++.
    174   int result =  FUNCTION_CAST<F2>(buffer)(3, 2);
    175   CHECK_EQ(0, result);
    176   result =  FUNCTION_CAST<F2>(buffer)(0x100000000l, 0x100000000l);
    177   CHECK_EQ(1, result);
    178   result =  FUNCTION_CAST<F2>(buffer)(-0x100000000l, 0x100000000l);
    179   CHECK_EQ(-1, result);
    180 }
    181 
    182 TEST(AssemblerX64MemoryOperands) {
    183   // Allocate an executable page of memory.
    184   size_t actual_size;
    185   byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
    186                                                  &actual_size,
    187                                                  true));
    188   CHECK(buffer);
    189   Assembler assm(buffer, static_cast<int>(actual_size));
    190 
    191   // Assemble a simple function that copies argument 2 and returns it.
    192   __ push(rbp);
    193   __ movq(rbp, rsp);
    194 
    195   __ push(arg2);  // Value at (rbp - 8)
    196   __ push(arg2);  // Value at (rbp - 16)
    197   __ push(arg1);  // Value at (rbp - 24)
    198 
    199   const int kStackElementSize = 8;
    200   __ movq(rax, Operand(rbp, -3 * kStackElementSize));
    201   __ pop(arg2);
    202   __ pop(arg2);
    203   __ pop(arg2);
    204   __ pop(rbp);
    205   __ nop();
    206   __ ret(0);
    207 
    208   CodeDesc desc;
    209   assm.GetCode(&desc);
    210   // Call the function from C++.
    211   int result =  FUNCTION_CAST<F2>(buffer)(3, 2);
    212   CHECK_EQ(3, result);
    213 }
    214 
    215 TEST(AssemblerX64ControlFlow) {
    216   // Allocate an executable page of memory.
    217   size_t actual_size;
    218   byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
    219                                                  &actual_size,
    220                                                  true));
    221   CHECK(buffer);
    222   Assembler assm(buffer, static_cast<int>(actual_size));
    223 
    224   // Assemble a simple function that copies argument 1 and returns it.
    225   __ push(rbp);
    226 
    227   __ movq(rbp, rsp);
    228   __ movq(rax, arg1);
    229   Label target;
    230   __ jmp(&target);
    231   __ movq(rax, arg2);
    232   __ bind(&target);
    233   __ pop(rbp);
    234   __ ret(0);
    235 
    236   CodeDesc desc;
    237   assm.GetCode(&desc);
    238   // Call the function from C++.
    239   int result =  FUNCTION_CAST<F2>(buffer)(3, 2);
    240   CHECK_EQ(3, result);
    241 }
    242 
    243 TEST(AssemblerX64LoopImmediates) {
    244   // Allocate an executable page of memory.
    245   size_t actual_size;
    246   byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
    247                                                  &actual_size,
    248                                                  true));
    249   CHECK(buffer);
    250   Assembler assm(buffer, static_cast<int>(actual_size));
    251   // Assemble two loops using rax as counter, and verify the ending counts.
    252   Label Fail;
    253   __ movq(rax, Immediate(-3));
    254   Label Loop1_test;
    255   Label Loop1_body;
    256   __ jmp(&Loop1_test);
    257   __ bind(&Loop1_body);
    258   __ addq(rax, Immediate(7));
    259   __ bind(&Loop1_test);
    260   __ cmpq(rax, Immediate(20));
    261   __ j(less_equal, &Loop1_body);
    262   // Did the loop terminate with the expected value?
    263   __ cmpq(rax, Immediate(25));
    264   __ j(not_equal, &Fail);
    265 
    266   Label Loop2_test;
    267   Label Loop2_body;
    268   __ movq(rax, Immediate(0x11FEED00));
    269   __ jmp(&Loop2_test);
    270   __ bind(&Loop2_body);
    271   __ addq(rax, Immediate(-0x1100));
    272   __ bind(&Loop2_test);
    273   __ cmpq(rax, Immediate(0x11FE8000));
    274   __ j(greater, &Loop2_body);
    275   // Did the loop terminate with the expected value?
    276   __ cmpq(rax, Immediate(0x11FE7600));
    277   __ j(not_equal, &Fail);
    278 
    279   __ movq(rax, Immediate(1));
    280   __ ret(0);
    281   __ bind(&Fail);
    282   __ movq(rax, Immediate(0));
    283   __ ret(0);
    284 
    285   CodeDesc desc;
    286   assm.GetCode(&desc);
    287   // Call the function from C++.
    288   int result =  FUNCTION_CAST<F0>(buffer)();
    289   CHECK_EQ(1, result);
    290 }
    291 
    292 #undef __
    293