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