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