blob: 5d292df05f5578bc452f73a04e734fabbe4e0f51 [file] [log] [blame]
Steve Blocka7e24c12009-10-30 11:49:00 +00001// 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;
Steve Block3ce2e202009-11-05 08:53:23 +000047using v8::internal::rcx;
Steve Blocka7e24c12009-10-30 11:49:00 +000048using v8::internal::rdx;
49using v8::internal::rbp;
50using v8::internal::rsp;
Steve Block1e0659c2011-05-24 12:43:12 +010051using v8::internal::r8;
52using v8::internal::r9;
53using v8::internal::r12;
54using v8::internal::r13;
55using v8::internal::times_1;
56
Steve Blocka7e24c12009-10-30 11:49:00 +000057using v8::internal::FUNCTION_CAST;
58using v8::internal::CodeDesc;
59using v8::internal::less_equal;
60using v8::internal::not_equal;
61using v8::internal::greater;
62
Steve Blocka7e24c12009-10-30 11:49:00 +000063// 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.
Steve Block3ce2e202009-11-05 08:53:23 +000066// 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
Steve Blocka7e24c12009-10-30 11:49:00 +000068// the XMM registers. The return value is in RAX.
69// This calling convention is used on Linux, with GCC, and on Mac OS,
Steve Block3ce2e202009-11-05 08:53:23 +000070// 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.
Steve Blocka7e24c12009-10-30 11:49:00 +000072
73typedef int (*F0)();
74typedef int (*F1)(int64_t x);
75typedef int (*F2)(int64_t x, int64_t y);
76
Steve Block3ce2e202009-11-05 08:53:23 +000077#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
Steve Blocka7e24c12009-10-30 11:49:00 +000085#define __ assm.
86
87
88TEST(AssemblerX64ReturnOperation) {
89 // Allocate an executable page of memory.
90 size_t actual_size;
91 byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
92 &actual_size,
93 true));
94 CHECK(buffer);
Steve Blockd0582a62009-12-15 09:54:21 +000095 Assembler assm(buffer, static_cast<int>(actual_size));
Steve Blocka7e24c12009-10-30 11:49:00 +000096
97 // Assemble a simple function that copies argument 2 and returns it.
Steve Block3ce2e202009-11-05 08:53:23 +000098 __ movq(rax, arg2);
Steve Blocka7e24c12009-10-30 11:49:00 +000099 __ nop();
100 __ ret(0);
101
102 CodeDesc desc;
103 assm.GetCode(&desc);
104 // Call the function from C++.
105 int result = FUNCTION_CAST<F2>(buffer)(3, 2);
106 CHECK_EQ(2, result);
107}
108
109TEST(AssemblerX64StackOperations) {
110 // Allocate an executable page of memory.
111 size_t actual_size;
112 byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
113 &actual_size,
114 true));
115 CHECK(buffer);
Steve Blockd0582a62009-12-15 09:54:21 +0000116 Assembler assm(buffer, static_cast<int>(actual_size));
Steve Blocka7e24c12009-10-30 11:49:00 +0000117
118 // Assemble a simple function that copies argument 2 and returns it.
119 // We compile without stack frame pointers, so the gdb debugger shows
120 // incorrect stack frames when debugging this function (which has them).
121 __ push(rbp);
122 __ movq(rbp, rsp);
Steve Block3ce2e202009-11-05 08:53:23 +0000123 __ push(arg2); // Value at (rbp - 8)
124 __ push(arg2); // Value at (rbp - 16)
125 __ push(arg1); // Value at (rbp - 24)
Steve Blocka7e24c12009-10-30 11:49:00 +0000126 __ pop(rax);
127 __ pop(rax);
128 __ pop(rax);
129 __ pop(rbp);
130 __ nop();
131 __ ret(0);
132
133 CodeDesc desc;
134 assm.GetCode(&desc);
135 // Call the function from C++.
136 int result = FUNCTION_CAST<F2>(buffer)(3, 2);
137 CHECK_EQ(2, result);
138}
139
140TEST(AssemblerX64ArithmeticOperations) {
141 // Allocate an executable page of memory.
142 size_t actual_size;
143 byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
144 &actual_size,
145 true));
146 CHECK(buffer);
Steve Blockd0582a62009-12-15 09:54:21 +0000147 Assembler assm(buffer, static_cast<int>(actual_size));
Steve Blocka7e24c12009-10-30 11:49:00 +0000148
149 // Assemble a simple function that adds arguments returning the sum.
Steve Block3ce2e202009-11-05 08:53:23 +0000150 __ movq(rax, arg2);
151 __ addq(rax, arg1);
Steve Blocka7e24c12009-10-30 11:49:00 +0000152 __ ret(0);
153
154 CodeDesc desc;
155 assm.GetCode(&desc);
156 // Call the function from C++.
157 int result = FUNCTION_CAST<F2>(buffer)(3, 2);
158 CHECK_EQ(5, result);
159}
160
161TEST(AssemblerX64ImulOperation) {
162 // Allocate an executable page of memory.
163 size_t actual_size;
164 byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
165 &actual_size,
166 true));
167 CHECK(buffer);
Steve Blockd0582a62009-12-15 09:54:21 +0000168 Assembler assm(buffer, static_cast<int>(actual_size));
Steve Blocka7e24c12009-10-30 11:49:00 +0000169
170 // Assemble a simple function that multiplies arguments returning the high
171 // word.
Steve Block3ce2e202009-11-05 08:53:23 +0000172 __ movq(rax, arg2);
173 __ imul(arg1);
Steve Blocka7e24c12009-10-30 11:49:00 +0000174 __ movq(rax, rdx);
175 __ ret(0);
176
177 CodeDesc desc;
178 assm.GetCode(&desc);
179 // Call the function from C++.
180 int result = FUNCTION_CAST<F2>(buffer)(3, 2);
181 CHECK_EQ(0, result);
182 result = FUNCTION_CAST<F2>(buffer)(0x100000000l, 0x100000000l);
183 CHECK_EQ(1, result);
184 result = FUNCTION_CAST<F2>(buffer)(-0x100000000l, 0x100000000l);
185 CHECK_EQ(-1, result);
186}
187
188TEST(AssemblerX64MemoryOperands) {
189 // Allocate an executable page of memory.
190 size_t actual_size;
191 byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
192 &actual_size,
193 true));
194 CHECK(buffer);
Steve Blockd0582a62009-12-15 09:54:21 +0000195 Assembler assm(buffer, static_cast<int>(actual_size));
Steve Blocka7e24c12009-10-30 11:49:00 +0000196
197 // Assemble a simple function that copies argument 2 and returns it.
198 __ push(rbp);
199 __ movq(rbp, rsp);
Steve Block3ce2e202009-11-05 08:53:23 +0000200
201 __ push(arg2); // Value at (rbp - 8)
202 __ push(arg2); // Value at (rbp - 16)
203 __ push(arg1); // Value at (rbp - 24)
204
Steve Blocka7e24c12009-10-30 11:49:00 +0000205 const int kStackElementSize = 8;
206 __ movq(rax, Operand(rbp, -3 * kStackElementSize));
Steve Block3ce2e202009-11-05 08:53:23 +0000207 __ pop(arg2);
208 __ pop(arg2);
209 __ pop(arg2);
Steve Blocka7e24c12009-10-30 11:49:00 +0000210 __ pop(rbp);
211 __ nop();
212 __ ret(0);
213
214 CodeDesc desc;
215 assm.GetCode(&desc);
216 // Call the function from C++.
217 int result = FUNCTION_CAST<F2>(buffer)(3, 2);
218 CHECK_EQ(3, result);
219}
220
221TEST(AssemblerX64ControlFlow) {
222 // Allocate an executable page of memory.
223 size_t actual_size;
224 byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
225 &actual_size,
226 true));
227 CHECK(buffer);
Steve Blockd0582a62009-12-15 09:54:21 +0000228 Assembler assm(buffer, static_cast<int>(actual_size));
Steve Blocka7e24c12009-10-30 11:49:00 +0000229
Steve Block3ce2e202009-11-05 08:53:23 +0000230 // Assemble a simple function that copies argument 1 and returns it.
Steve Blocka7e24c12009-10-30 11:49:00 +0000231 __ push(rbp);
Steve Block3ce2e202009-11-05 08:53:23 +0000232
Steve Blocka7e24c12009-10-30 11:49:00 +0000233 __ movq(rbp, rsp);
Steve Block3ce2e202009-11-05 08:53:23 +0000234 __ movq(rax, arg1);
Steve Blocka7e24c12009-10-30 11:49:00 +0000235 Label target;
236 __ jmp(&target);
Steve Block3ce2e202009-11-05 08:53:23 +0000237 __ movq(rax, arg2);
Steve Blocka7e24c12009-10-30 11:49:00 +0000238 __ bind(&target);
239 __ pop(rbp);
240 __ ret(0);
241
242 CodeDesc desc;
243 assm.GetCode(&desc);
244 // Call the function from C++.
245 int result = FUNCTION_CAST<F2>(buffer)(3, 2);
246 CHECK_EQ(3, result);
247}
248
249TEST(AssemblerX64LoopImmediates) {
250 // Allocate an executable page of memory.
251 size_t actual_size;
252 byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
253 &actual_size,
254 true));
255 CHECK(buffer);
Steve Blockd0582a62009-12-15 09:54:21 +0000256 Assembler assm(buffer, static_cast<int>(actual_size));
Steve Blocka7e24c12009-10-30 11:49:00 +0000257 // Assemble two loops using rax as counter, and verify the ending counts.
258 Label Fail;
259 __ movq(rax, Immediate(-3));
260 Label Loop1_test;
261 Label Loop1_body;
262 __ jmp(&Loop1_test);
263 __ bind(&Loop1_body);
264 __ addq(rax, Immediate(7));
265 __ bind(&Loop1_test);
266 __ cmpq(rax, Immediate(20));
267 __ j(less_equal, &Loop1_body);
268 // Did the loop terminate with the expected value?
269 __ cmpq(rax, Immediate(25));
270 __ j(not_equal, &Fail);
271
272 Label Loop2_test;
273 Label Loop2_body;
274 __ movq(rax, Immediate(0x11FEED00));
275 __ jmp(&Loop2_test);
276 __ bind(&Loop2_body);
277 __ addq(rax, Immediate(-0x1100));
278 __ bind(&Loop2_test);
279 __ cmpq(rax, Immediate(0x11FE8000));
280 __ j(greater, &Loop2_body);
281 // Did the loop terminate with the expected value?
282 __ cmpq(rax, Immediate(0x11FE7600));
283 __ j(not_equal, &Fail);
284
285 __ movq(rax, Immediate(1));
286 __ ret(0);
287 __ bind(&Fail);
288 __ movq(rax, Immediate(0));
289 __ ret(0);
290
291 CodeDesc desc;
292 assm.GetCode(&desc);
293 // Call the function from C++.
294 int result = FUNCTION_CAST<F0>(buffer)();
295 CHECK_EQ(1, result);
296}
297
Steve Block1e0659c2011-05-24 12:43:12 +0100298
299TEST(OperandRegisterDependency) {
300 int offsets[4] = {0, 1, 0xfed, 0xbeefcad};
301 for (int i = 0; i < 4; i++) {
302 int offset = offsets[i];
303 CHECK(Operand(rax, offset).AddressUsesRegister(rax));
304 CHECK(!Operand(rax, offset).AddressUsesRegister(r8));
305 CHECK(!Operand(rax, offset).AddressUsesRegister(rcx));
306
307 CHECK(Operand(rax, rax, times_1, offset).AddressUsesRegister(rax));
308 CHECK(!Operand(rax, rax, times_1, offset).AddressUsesRegister(r8));
309 CHECK(!Operand(rax, rax, times_1, offset).AddressUsesRegister(rcx));
310
311 CHECK(Operand(rax, rcx, times_1, offset).AddressUsesRegister(rax));
312 CHECK(Operand(rax, rcx, times_1, offset).AddressUsesRegister(rcx));
313 CHECK(!Operand(rax, rcx, times_1, offset).AddressUsesRegister(r8));
314 CHECK(!Operand(rax, rcx, times_1, offset).AddressUsesRegister(r9));
315 CHECK(!Operand(rax, rcx, times_1, offset).AddressUsesRegister(rdx));
316 CHECK(!Operand(rax, rcx, times_1, offset).AddressUsesRegister(rsp));
317
318 CHECK(Operand(rsp, offset).AddressUsesRegister(rsp));
319 CHECK(!Operand(rsp, offset).AddressUsesRegister(rax));
320 CHECK(!Operand(rsp, offset).AddressUsesRegister(r12));
321
322 CHECK(Operand(rbp, offset).AddressUsesRegister(rbp));
323 CHECK(!Operand(rbp, offset).AddressUsesRegister(rax));
324 CHECK(!Operand(rbp, offset).AddressUsesRegister(r13));
325
326 CHECK(Operand(rbp, rax, times_1, offset).AddressUsesRegister(rbp));
327 CHECK(Operand(rbp, rax, times_1, offset).AddressUsesRegister(rax));
328 CHECK(!Operand(rbp, rax, times_1, offset).AddressUsesRegister(rcx));
329 CHECK(!Operand(rbp, rax, times_1, offset).AddressUsesRegister(r13));
330 CHECK(!Operand(rbp, rax, times_1, offset).AddressUsesRegister(r8));
331 CHECK(!Operand(rbp, rax, times_1, offset).AddressUsesRegister(rsp));
332
333 CHECK(Operand(rsp, rbp, times_1, offset).AddressUsesRegister(rsp));
334 CHECK(Operand(rsp, rbp, times_1, offset).AddressUsesRegister(rbp));
335 CHECK(!Operand(rsp, rbp, times_1, offset).AddressUsesRegister(rax));
336 CHECK(!Operand(rsp, rbp, times_1, offset).AddressUsesRegister(r12));
337 CHECK(!Operand(rsp, rbp, times_1, offset).AddressUsesRegister(r13));
338 }
339}
340
Steve Blocka7e24c12009-10-30 11:49:00 +0000341#undef __