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
38 using v8::internal::Assembler;
39 using v8::internal::Code;
40 using v8::internal::CodeDesc;
41 using v8::internal::FUNCTION_CAST;
42 using v8::internal::Immediate;
43 using v8::internal::Isolate;
44 using v8::internal::Label;
45 using v8::internal::OS;
46 using v8::internal::Operand;
47 using v8::internal::byte;
48 using v8::internal::greater;
49 using v8::internal::less_equal;
50 using v8::internal::equal;
51 using v8::internal::not_equal;
52 using v8::internal::r13;
53 using v8::internal::r15;
54 using v8::internal::r8;
55 using v8::internal::r9;
56 using v8::internal::rax;
57 using v8::internal::rbx;
58 using v8::internal::rbp;
59 using v8::internal::rcx;
60 using v8::internal::rdi;
61 using v8::internal::rdx;
62 using v8::internal::rsi;
63 using v8::internal::rsp;
64 using 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
76 typedef int (*F0)();
77 typedef int (*F1)(int64_t x);
78 typedef int (*F2)(int64_t x, int64_t y);
79
80 #ifdef _WIN64
81 static const v8::internal::Register arg1 = rcx;
82 static const v8::internal::Register arg2 = rdx;
83 #else
84 static const v8::internal::Register arg1 = rdi;
85 static const v8::internal::Register arg2 = rsi;
86 #endif
87
88 #define __ assm.
89
90
91 static v8::Persistent<v8::Context> env;
92
93
InitializeVM()94 static void InitializeVM() {
95 if (env.IsEmpty()) {
96 env = v8::Context::New();
97 }
98 }
99
100
TEST(AssemblerX64ReturnOperation)101 TEST(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
TEST(AssemblerX64StackOperations)123 TEST(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
TEST(AssemblerX64ArithmeticOperations)155 TEST(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
TEST(AssemblerX64ImulOperation)177 TEST(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
TEST(AssemblerX64MemoryOperands)205 TEST(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
TEST(AssemblerX64ControlFlow)239 TEST(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
TEST(AssemblerX64LoopImmediates)268 TEST(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
TEST(OperandRegisterDependency)319 TEST(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
TEST(AssemblerX64LabelChaining)362 TEST(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
TEST(AssemblerMultiByteNop)375 TEST(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