blob: d81923fa5c227d3e174b602a8f6464fdfb82311a [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
Steve Blocka7e24c12009-10-30 11:49:00 +000038using v8::internal::Assembler;
Ben Murdoch3ef787d2012-04-12 10:51:47 +010039using v8::internal::Code;
Steve Blocka7e24c12009-10-30 11:49:00 +000040using v8::internal::CodeDesc;
Ben Murdoch8b112d22011-06-08 16:22:53 +010041using 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;
Steve Blocka7e24c12009-10-30 11:49:00 +000049using v8::internal::less_equal;
Ben Murdoch69a99ed2011-11-30 16:03:39 +000050using v8::internal::equal;
Steve Blocka7e24c12009-10-30 11:49:00 +000051using v8::internal::not_equal;
Ben Murdoch8b112d22011-06-08 16:22:53 +010052using v8::internal::r13;
53using v8::internal::r15;
54using v8::internal::r8;
55using v8::internal::r9;
56using v8::internal::rax;
Ben Murdoch3ef787d2012-04-12 10:51:47 +010057using v8::internal::rbx;
Ben Murdoch8b112d22011-06-08 16:22:53 +010058using 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;
Steve Blocka7e24c12009-10-30 11:49:00 +000065
Steve Blocka7e24c12009-10-30 11:49:00 +000066// 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.
Steve Block3ce2e202009-11-05 08:53:23 +000069// 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
Steve Blocka7e24c12009-10-30 11:49:00 +000071// the XMM registers. The return value is in RAX.
72// This calling convention is used on Linux, with GCC, and on Mac OS,
Steve Block3ce2e202009-11-05 08:53:23 +000073// 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.
Steve Blocka7e24c12009-10-30 11:49:00 +000075
76typedef int (*F0)();
77typedef int (*F1)(int64_t x);
78typedef int (*F2)(int64_t x, int64_t y);
79
Steve Block3ce2e202009-11-05 08:53:23 +000080#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
Steve Blocka7e24c12009-10-30 11:49:00 +000088#define __ assm.
89
90
Ben Murdoch3ef787d2012-04-12 10:51:47 +010091static v8::Persistent<v8::Context> env;
92
93
94static void InitializeVM() {
95 if (env.IsEmpty()) {
96 env = v8::Context::New();
97 }
98}
99
100
Steve Blocka7e24c12009-10-30 11:49:00 +0000101TEST(AssemblerX64ReturnOperation) {
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100102 OS::SetUp();
Steve Blocka7e24c12009-10-30 11:49:00 +0000103 // 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);
Ben Murdoch8b112d22011-06-08 16:22:53 +0100109 Assembler assm(Isolate::Current(), buffer, static_cast<int>(actual_size));
Steve Blocka7e24c12009-10-30 11:49:00 +0000110
111 // Assemble a simple function that copies argument 2 and returns it.
Steve Block3ce2e202009-11-05 08:53:23 +0000112 __ movq(rax, arg2);
Steve Blocka7e24c12009-10-30 11:49:00 +0000113 __ 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) {
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100124 OS::SetUp();
Steve Blocka7e24c12009-10-30 11:49:00 +0000125 // 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);
Ben Murdoch8b112d22011-06-08 16:22:53 +0100131 Assembler assm(Isolate::Current(), buffer, static_cast<int>(actual_size));
Steve Blocka7e24c12009-10-30 11:49:00 +0000132
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);
Steve Block3ce2e202009-11-05 08:53:23 +0000138 __ push(arg2); // Value at (rbp - 8)
139 __ push(arg2); // Value at (rbp - 16)
140 __ push(arg1); // Value at (rbp - 24)
Steve Blocka7e24c12009-10-30 11:49:00 +0000141 __ 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) {
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100156 OS::SetUp();
Steve Blocka7e24c12009-10-30 11:49:00 +0000157 // 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);
Ben Murdoch8b112d22011-06-08 16:22:53 +0100163 Assembler assm(Isolate::Current(), buffer, static_cast<int>(actual_size));
Steve Blocka7e24c12009-10-30 11:49:00 +0000164
165 // Assemble a simple function that adds arguments returning the sum.
Steve Block3ce2e202009-11-05 08:53:23 +0000166 __ movq(rax, arg2);
167 __ addq(rax, arg1);
Steve Blocka7e24c12009-10-30 11:49:00 +0000168 __ 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) {
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100178 OS::SetUp();
Steve Blocka7e24c12009-10-30 11:49:00 +0000179 // 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);
Ben Murdoch8b112d22011-06-08 16:22:53 +0100185 Assembler assm(Isolate::Current(), buffer, static_cast<int>(actual_size));
Steve Blocka7e24c12009-10-30 11:49:00 +0000186
187 // Assemble a simple function that multiplies arguments returning the high
188 // word.
Steve Block3ce2e202009-11-05 08:53:23 +0000189 __ movq(rax, arg2);
190 __ imul(arg1);
Steve Blocka7e24c12009-10-30 11:49:00 +0000191 __ 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) {
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100206 OS::SetUp();
Steve Blocka7e24c12009-10-30 11:49:00 +0000207 // 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);
Ben Murdoch8b112d22011-06-08 16:22:53 +0100213 Assembler assm(Isolate::Current(), buffer, static_cast<int>(actual_size));
Steve Blocka7e24c12009-10-30 11:49:00 +0000214
215 // Assemble a simple function that copies argument 2 and returns it.
216 __ push(rbp);
217 __ movq(rbp, rsp);
Steve Block3ce2e202009-11-05 08:53:23 +0000218
219 __ push(arg2); // Value at (rbp - 8)
220 __ push(arg2); // Value at (rbp - 16)
221 __ push(arg1); // Value at (rbp - 24)
222
Steve Blocka7e24c12009-10-30 11:49:00 +0000223 const int kStackElementSize = 8;
224 __ movq(rax, Operand(rbp, -3 * kStackElementSize));
Steve Block3ce2e202009-11-05 08:53:23 +0000225 __ pop(arg2);
226 __ pop(arg2);
227 __ pop(arg2);
Steve Blocka7e24c12009-10-30 11:49:00 +0000228 __ 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) {
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100240 OS::SetUp();
Steve Blocka7e24c12009-10-30 11:49:00 +0000241 // 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);
Ben Murdoch8b112d22011-06-08 16:22:53 +0100247 Assembler assm(Isolate::Current(), buffer, static_cast<int>(actual_size));
Steve Blocka7e24c12009-10-30 11:49:00 +0000248
Steve Block3ce2e202009-11-05 08:53:23 +0000249 // Assemble a simple function that copies argument 1 and returns it.
Steve Blocka7e24c12009-10-30 11:49:00 +0000250 __ push(rbp);
Steve Block3ce2e202009-11-05 08:53:23 +0000251
Steve Blocka7e24c12009-10-30 11:49:00 +0000252 __ movq(rbp, rsp);
Steve Block3ce2e202009-11-05 08:53:23 +0000253 __ movq(rax, arg1);
Steve Blocka7e24c12009-10-30 11:49:00 +0000254 Label target;
255 __ jmp(&target);
Steve Block3ce2e202009-11-05 08:53:23 +0000256 __ movq(rax, arg2);
Steve Blocka7e24c12009-10-30 11:49:00 +0000257 __ 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) {
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100269 OS::SetUp();
Steve Blocka7e24c12009-10-30 11:49:00 +0000270 // 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);
Ben Murdoch8b112d22011-06-08 16:22:53 +0100276 Assembler assm(Isolate::Current(), buffer, static_cast<int>(actual_size));
Steve Blocka7e24c12009-10-30 11:49:00 +0000277 // 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
Steve Block1e0659c2011-05-24 12:43:12 +0100318
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));
Steve Block44f0eee2011-05-26 01:26:41 +0100340 CHECK(!Operand(rsp, offset).AddressUsesRegister(r15));
Steve Block1e0659c2011-05-24 12:43:12 +0100341
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));
Steve Block44f0eee2011-05-26 01:26:41 +0100356 CHECK(!Operand(rsp, rbp, times_1, offset).AddressUsesRegister(r15));
Steve Block1e0659c2011-05-24 12:43:12 +0100357 CHECK(!Operand(rsp, rbp, times_1, offset).AddressUsesRegister(r13));
358 }
359}
360
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000361
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
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100374
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
Steve Blocka7e24c12009-10-30 11:49:00 +0000443#undef __