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