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 38using v8::internal::Assembler; 39using v8::internal::CodeDesc; 40using v8::internal::FUNCTION_CAST; 41using v8::internal::Immediate; 42using v8::internal::Isolate; 43using v8::internal::Label; 44using v8::internal::OS; 45using v8::internal::Operand; 46using v8::internal::byte; 47using v8::internal::greater; 48using v8::internal::less_equal; 49using v8::internal::not_equal; 50using v8::internal::r13; 51using v8::internal::r15; 52using v8::internal::r8; 53using v8::internal::r9; 54using v8::internal::rax; 55using v8::internal::rbp; 56using v8::internal::rcx; 57using v8::internal::rdi; 58using v8::internal::rdx; 59using v8::internal::rsi; 60using v8::internal::rsp; 61using 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 73typedef int (*F0)(); 74typedef int (*F1)(int64_t x); 75typedef int (*F2)(int64_t x, int64_t y); 76 77#ifdef _WIN64 78static const v8::internal::Register arg1 = rcx; 79static const v8::internal::Register arg2 = rdx; 80#else 81static const v8::internal::Register arg1 = rdi; 82static const v8::internal::Register arg2 = rsi; 83#endif 84 85#define __ assm. 86 87 88TEST(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 110TEST(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 142TEST(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 164TEST(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 192TEST(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 226TEST(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 255TEST(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 306TEST(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