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