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::byte; 39using v8::internal::OS; 40using v8::internal::Assembler; 41using v8::internal::Operand; 42using v8::internal::Immediate; 43using v8::internal::Label; 44using v8::internal::rax; 45using v8::internal::rsi; 46using v8::internal::rdi; 47using v8::internal::rcx; 48using v8::internal::rdx; 49using v8::internal::rbp; 50using v8::internal::rsp; 51using v8::internal::FUNCTION_CAST; 52using v8::internal::CodeDesc; 53using v8::internal::less_equal; 54using v8::internal::not_equal; 55using 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 67typedef int (*F0)(); 68typedef int (*F1)(int64_t x); 69typedef int (*F2)(int64_t x, int64_t y); 70 71#ifdef _WIN64 72static const v8::internal::Register arg1 = rcx; 73static const v8::internal::Register arg2 = rdx; 74#else 75static const v8::internal::Register arg1 = rdi; 76static const v8::internal::Register arg2 = rsi; 77#endif 78 79#define __ assm. 80 81 82TEST(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 103TEST(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 134TEST(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 155TEST(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 182TEST(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 215TEST(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 243TEST(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