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