• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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