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::Code; 40using v8::internal::CodeDesc; 41using v8::internal::FUNCTION_CAST; 42using v8::internal::Immediate; 43using v8::internal::Isolate; 44using v8::internal::Label; 45using v8::internal::OS; 46using v8::internal::Operand; 47using v8::internal::byte; 48using v8::internal::greater; 49using v8::internal::less_equal; 50using v8::internal::equal; 51using v8::internal::not_equal; 52using v8::internal::r13; 53using v8::internal::r15; 54using v8::internal::r8; 55using v8::internal::r9; 56using v8::internal::rax; 57using v8::internal::rbx; 58using v8::internal::rbp; 59using v8::internal::rcx; 60using v8::internal::rdi; 61using v8::internal::rdx; 62using v8::internal::rsi; 63using v8::internal::rsp; 64using 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 76typedef int (*F0)(); 77typedef int (*F1)(int64_t x); 78typedef int (*F2)(int64_t x, int64_t y); 79 80#ifdef _WIN64 81static const v8::internal::Register arg1 = rcx; 82static const v8::internal::Register arg2 = rdx; 83#else 84static const v8::internal::Register arg1 = rdi; 85static const v8::internal::Register arg2 = rsi; 86#endif 87 88#define __ assm. 89 90 91static v8::Persistent<v8::Context> env; 92 93 94static void InitializeVM() { 95 if (env.IsEmpty()) { 96 env = v8::Context::New(); 97 } 98} 99 100 101TEST(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 123TEST(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 155TEST(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 177TEST(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 205TEST(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 239TEST(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 268TEST(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 319TEST(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 362TEST(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 375TEST(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