1// Copyright 2011 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 "disassembler.h"
33#include "factory.h"
34#include "macro-assembler.h"
35#include "platform.h"
36#include "serialize.h"
37#include "cctest.h"
38
39using namespace v8::internal;
40
41
42typedef int (*F0)();
43typedef int (*F1)(int x);
44typedef int (*F2)(int x, int y);
45
46
47#define __ assm.
48
49TEST(AssemblerIa320) {
50  CcTest::InitializeVM();
51  Isolate* isolate = reinterpret_cast<Isolate*>(CcTest::isolate());
52  HandleScope scope(isolate);
53
54  v8::internal::byte buffer[256];
55  Assembler assm(isolate, buffer, sizeof buffer);
56
57  __ mov(eax, Operand(esp, 4));
58  __ add(eax, Operand(esp, 8));
59  __ ret(0);
60
61  CodeDesc desc;
62  assm.GetCode(&desc);
63  Object* code = isolate->heap()->CreateCode(
64      desc,
65      Code::ComputeFlags(Code::STUB),
66      Handle<Code>())->ToObjectChecked();
67  CHECK(code->IsCode());
68#ifdef OBJECT_PRINT
69  Code::cast(code)->Print();
70#endif
71  F2 f = FUNCTION_CAST<F2>(Code::cast(code)->entry());
72  int res = f(3, 4);
73  ::printf("f() = %d\n", res);
74  CHECK_EQ(7, res);
75}
76
77
78TEST(AssemblerIa321) {
79  CcTest::InitializeVM();
80  Isolate* isolate = reinterpret_cast<Isolate*>(CcTest::isolate());
81  HandleScope scope(isolate);
82
83  v8::internal::byte buffer[256];
84  Assembler assm(isolate, buffer, sizeof buffer);
85  Label L, C;
86
87  __ mov(edx, Operand(esp, 4));
88  __ xor_(eax, eax);  // clear eax
89  __ jmp(&C);
90
91  __ bind(&L);
92  __ add(eax, edx);
93  __ sub(edx, Immediate(1));
94
95  __ bind(&C);
96  __ test(edx, edx);
97  __ j(not_zero, &L);
98  __ ret(0);
99
100  CodeDesc desc;
101  assm.GetCode(&desc);
102  Object* code = isolate->heap()->CreateCode(
103      desc,
104      Code::ComputeFlags(Code::STUB),
105      Handle<Code>())->ToObjectChecked();
106  CHECK(code->IsCode());
107#ifdef OBJECT_PRINT
108  Code::cast(code)->Print();
109#endif
110  F1 f = FUNCTION_CAST<F1>(Code::cast(code)->entry());
111  int res = f(100);
112  ::printf("f() = %d\n", res);
113  CHECK_EQ(5050, res);
114}
115
116
117TEST(AssemblerIa322) {
118  CcTest::InitializeVM();
119  Isolate* isolate = reinterpret_cast<Isolate*>(CcTest::isolate());
120  HandleScope scope(isolate);
121
122  v8::internal::byte buffer[256];
123  Assembler assm(isolate, buffer, sizeof buffer);
124  Label L, C;
125
126  __ mov(edx, Operand(esp, 4));
127  __ mov(eax, 1);
128  __ jmp(&C);
129
130  __ bind(&L);
131  __ imul(eax, edx);
132  __ sub(edx, Immediate(1));
133
134  __ bind(&C);
135  __ test(edx, edx);
136  __ j(not_zero, &L);
137  __ ret(0);
138
139  // some relocated stuff here, not executed
140  __ mov(eax, isolate->factory()->true_value());
141  __ jmp(NULL, RelocInfo::RUNTIME_ENTRY);
142
143  CodeDesc desc;
144  assm.GetCode(&desc);
145  Object* code = isolate->heap()->CreateCode(
146      desc,
147      Code::ComputeFlags(Code::STUB),
148      Handle<Code>())->ToObjectChecked();
149  CHECK(code->IsCode());
150#ifdef OBJECT_PRINT
151  Code::cast(code)->Print();
152#endif
153  F1 f = FUNCTION_CAST<F1>(Code::cast(code)->entry());
154  int res = f(10);
155  ::printf("f() = %d\n", res);
156  CHECK_EQ(3628800, res);
157}
158
159
160typedef int (*F3)(float x);
161
162TEST(AssemblerIa323) {
163  CcTest::InitializeVM();
164  if (!CpuFeatures::IsSupported(SSE2)) return;
165
166  Isolate* isolate = reinterpret_cast<Isolate*>(CcTest::isolate());
167  HandleScope scope(isolate);
168
169  v8::internal::byte buffer[256];
170  Assembler assm(isolate, buffer, sizeof buffer);
171
172  CHECK(CpuFeatures::IsSupported(SSE2));
173  { CpuFeatureScope fscope(&assm, SSE2);
174    __ cvttss2si(eax, Operand(esp, 4));
175    __ ret(0);
176  }
177
178  CodeDesc desc;
179  assm.GetCode(&desc);
180  Code* code = Code::cast(isolate->heap()->CreateCode(
181      desc,
182      Code::ComputeFlags(Code::STUB),
183      Handle<Code>())->ToObjectChecked());
184  // don't print the code - our disassembler can't handle cvttss2si
185  // instead print bytes
186  Disassembler::Dump(stdout,
187                     code->instruction_start(),
188                     code->instruction_start() + code->instruction_size());
189  F3 f = FUNCTION_CAST<F3>(code->entry());
190  int res = f(static_cast<float>(-3.1415));
191  ::printf("f() = %d\n", res);
192  CHECK_EQ(-3, res);
193}
194
195
196typedef int (*F4)(double x);
197
198TEST(AssemblerIa324) {
199  CcTest::InitializeVM();
200  if (!CpuFeatures::IsSupported(SSE2)) return;
201
202  Isolate* isolate = reinterpret_cast<Isolate*>(CcTest::isolate());
203  HandleScope scope(isolate);
204
205  v8::internal::byte buffer[256];
206  Assembler assm(isolate, buffer, sizeof buffer);
207
208  CHECK(CpuFeatures::IsSupported(SSE2));
209  CpuFeatureScope fscope(&assm, SSE2);
210  __ cvttsd2si(eax, Operand(esp, 4));
211  __ ret(0);
212
213  CodeDesc desc;
214  assm.GetCode(&desc);
215  Code* code = Code::cast(isolate->heap()->CreateCode(
216      desc,
217      Code::ComputeFlags(Code::STUB),
218      Handle<Code>())->ToObjectChecked());
219  // don't print the code - our disassembler can't handle cvttsd2si
220  // instead print bytes
221  Disassembler::Dump(stdout,
222                     code->instruction_start(),
223                     code->instruction_start() + code->instruction_size());
224  F4 f = FUNCTION_CAST<F4>(code->entry());
225  int res = f(2.718281828);
226  ::printf("f() = %d\n", res);
227  CHECK_EQ(2, res);
228}
229
230
231static int baz = 42;
232TEST(AssemblerIa325) {
233  CcTest::InitializeVM();
234  Isolate* isolate = reinterpret_cast<Isolate*>(CcTest::isolate());
235  HandleScope scope(isolate);
236
237  v8::internal::byte buffer[256];
238  Assembler assm(isolate, buffer, sizeof buffer);
239
240  __ mov(eax, Operand(reinterpret_cast<intptr_t>(&baz), RelocInfo::NONE32));
241  __ ret(0);
242
243  CodeDesc desc;
244  assm.GetCode(&desc);
245  Code* code = Code::cast(isolate->heap()->CreateCode(
246      desc,
247      Code::ComputeFlags(Code::STUB),
248      Handle<Code>())->ToObjectChecked());
249  F0 f = FUNCTION_CAST<F0>(code->entry());
250  int res = f();
251  CHECK_EQ(42, res);
252}
253
254
255typedef double (*F5)(double x, double y);
256
257TEST(AssemblerIa326) {
258  CcTest::InitializeVM();
259  if (!CpuFeatures::IsSupported(SSE2)) return;
260
261  Isolate* isolate = reinterpret_cast<Isolate*>(CcTest::isolate());
262  HandleScope scope(isolate);
263  v8::internal::byte buffer[256];
264  Assembler assm(isolate, buffer, sizeof buffer);
265
266  CpuFeatureScope fscope(&assm, SSE2);
267  __ movdbl(xmm0, Operand(esp, 1 * kPointerSize));
268  __ movdbl(xmm1, Operand(esp, 3 * kPointerSize));
269  __ addsd(xmm0, xmm1);
270  __ mulsd(xmm0, xmm1);
271  __ subsd(xmm0, xmm1);
272  __ divsd(xmm0, xmm1);
273  // Copy xmm0 to st(0) using eight bytes of stack.
274  __ sub(esp, Immediate(8));
275  __ movdbl(Operand(esp, 0), xmm0);
276  __ fld_d(Operand(esp, 0));
277  __ add(esp, Immediate(8));
278  __ ret(0);
279
280  CodeDesc desc;
281  assm.GetCode(&desc);
282  Code* code = Code::cast(isolate->heap()->CreateCode(
283      desc,
284      Code::ComputeFlags(Code::STUB),
285      Handle<Code>())->ToObjectChecked());
286#ifdef DEBUG
287  ::printf("\n---\n");
288  // don't print the code - our disassembler can't handle SSE instructions
289  // instead print bytes
290  Disassembler::Dump(stdout,
291                     code->instruction_start(),
292                     code->instruction_start() + code->instruction_size());
293#endif
294  F5 f = FUNCTION_CAST<F5>(code->entry());
295  double res = f(2.2, 1.1);
296  ::printf("f() = %f\n", res);
297  CHECK(2.29 < res && res < 2.31);
298}
299
300
301typedef double (*F6)(int x);
302
303TEST(AssemblerIa328) {
304  CcTest::InitializeVM();
305  if (!CpuFeatures::IsSupported(SSE2)) return;
306
307  Isolate* isolate = reinterpret_cast<Isolate*>(CcTest::isolate());
308  HandleScope scope(isolate);
309  v8::internal::byte buffer[256];
310  Assembler assm(isolate, buffer, sizeof buffer);
311  CpuFeatureScope fscope(&assm, SSE2);
312  __ mov(eax, Operand(esp, 4));
313  __ cvtsi2sd(xmm0, eax);
314  // Copy xmm0 to st(0) using eight bytes of stack.
315  __ sub(esp, Immediate(8));
316  __ movdbl(Operand(esp, 0), xmm0);
317  __ fld_d(Operand(esp, 0));
318  __ add(esp, Immediate(8));
319  __ ret(0);
320  CodeDesc desc;
321  assm.GetCode(&desc);
322  Code* code = Code::cast(isolate->heap()->CreateCode(
323      desc,
324      Code::ComputeFlags(Code::STUB),
325      Handle<Code>())->ToObjectChecked());
326  CHECK(code->IsCode());
327#ifdef OBJECT_PRINT
328  Code::cast(code)->Print();
329#endif
330  F6 f = FUNCTION_CAST<F6>(Code::cast(code)->entry());
331  double res = f(12);
332
333  ::printf("f() = %f\n", res);
334  CHECK(11.99 < res && res < 12.001);
335}
336
337
338typedef int (*F7)(double x, double y);
339
340TEST(AssemblerIa329) {
341  CcTest::InitializeVM();
342  Isolate* isolate = reinterpret_cast<Isolate*>(CcTest::isolate());
343  HandleScope scope(isolate);
344  v8::internal::byte buffer[256];
345  MacroAssembler assm(isolate, buffer, sizeof buffer);
346  enum { kEqual = 0, kGreater = 1, kLess = 2, kNaN = 3, kUndefined = 4 };
347  Label equal_l, less_l, greater_l, nan_l;
348  __ fld_d(Operand(esp, 3 * kPointerSize));
349  __ fld_d(Operand(esp, 1 * kPointerSize));
350  __ FCmp();
351  __ j(parity_even, &nan_l);
352  __ j(equal, &equal_l);
353  __ j(below, &less_l);
354  __ j(above, &greater_l);
355
356  __ mov(eax, kUndefined);
357  __ ret(0);
358
359  __ bind(&equal_l);
360  __ mov(eax, kEqual);
361  __ ret(0);
362
363  __ bind(&greater_l);
364  __ mov(eax, kGreater);
365  __ ret(0);
366
367  __ bind(&less_l);
368  __ mov(eax, kLess);
369  __ ret(0);
370
371  __ bind(&nan_l);
372  __ mov(eax, kNaN);
373  __ ret(0);
374
375
376  CodeDesc desc;
377  assm.GetCode(&desc);
378  Code* code = Code::cast(isolate->heap()->CreateCode(
379      desc,
380      Code::ComputeFlags(Code::STUB),
381      Handle<Code>())->ToObjectChecked());
382  CHECK(code->IsCode());
383#ifdef OBJECT_PRINT
384  Code::cast(code)->Print();
385#endif
386
387  F7 f = FUNCTION_CAST<F7>(Code::cast(code)->entry());
388  CHECK_EQ(kLess, f(1.1, 2.2));
389  CHECK_EQ(kEqual, f(2.2, 2.2));
390  CHECK_EQ(kGreater, f(3.3, 2.2));
391  CHECK_EQ(kNaN, f(OS::nan_value(), 1.1));
392}
393
394
395TEST(AssemblerIa3210) {
396  // Test chaining of label usages within instructions (issue 1644).
397  CcTest::InitializeVM();
398  Isolate* isolate = reinterpret_cast<Isolate*>(CcTest::isolate());
399  HandleScope scope(isolate);
400  Assembler assm(isolate, NULL, 0);
401
402  Label target;
403  __ j(equal, &target);
404  __ j(not_equal, &target);
405  __ bind(&target);
406  __ nop();
407}
408
409
410TEST(AssemblerMultiByteNop) {
411  CcTest::InitializeVM();
412  Isolate* isolate = reinterpret_cast<Isolate*>(CcTest::isolate());
413  HandleScope scope(isolate);
414  v8::internal::byte buffer[1024];
415  Assembler assm(isolate, buffer, sizeof(buffer));
416  __ push(ebx);
417  __ push(ecx);
418  __ push(edx);
419  __ push(edi);
420  __ push(esi);
421  __ mov(eax, 1);
422  __ mov(ebx, 2);
423  __ mov(ecx, 3);
424  __ mov(edx, 4);
425  __ mov(edi, 5);
426  __ mov(esi, 6);
427  for (int i = 0; i < 16; i++) {
428    int before = assm.pc_offset();
429    __ Nop(i);
430    CHECK_EQ(assm.pc_offset() - before, i);
431  }
432
433  Label fail;
434  __ cmp(eax, 1);
435  __ j(not_equal, &fail);
436  __ cmp(ebx, 2);
437  __ j(not_equal, &fail);
438  __ cmp(ecx, 3);
439  __ j(not_equal, &fail);
440  __ cmp(edx, 4);
441  __ j(not_equal, &fail);
442  __ cmp(edi, 5);
443  __ j(not_equal, &fail);
444  __ cmp(esi, 6);
445  __ j(not_equal, &fail);
446  __ mov(eax, 42);
447  __ pop(esi);
448  __ pop(edi);
449  __ pop(edx);
450  __ pop(ecx);
451  __ pop(ebx);
452  __ ret(0);
453  __ bind(&fail);
454  __ mov(eax, 13);
455  __ pop(esi);
456  __ pop(edi);
457  __ pop(edx);
458  __ pop(ecx);
459  __ pop(ebx);
460  __ ret(0);
461
462  CodeDesc desc;
463  assm.GetCode(&desc);
464  Code* code = Code::cast(isolate->heap()->CreateCode(
465      desc,
466      Code::ComputeFlags(Code::STUB),
467      Handle<Code>())->ToObjectChecked());
468  CHECK(code->IsCode());
469
470  F0 f = FUNCTION_CAST<F0>(code->entry());
471  int res = f();
472  CHECK_EQ(42, res);
473}
474
475
476#ifdef __GNUC__
477#define ELEMENT_COUNT 4
478
479void DoSSE2(const v8::FunctionCallbackInfo<v8::Value>& args) {
480  Isolate* isolate = reinterpret_cast<Isolate*>(CcTest::isolate());
481  HandleScope scope(isolate);
482
483  CHECK(args[0]->IsArray());
484  v8::Local<v8::Array> vec = v8::Local<v8::Array>::Cast(args[0]);
485  CHECK_EQ(ELEMENT_COUNT, vec->Length());
486
487  v8::internal::byte buffer[256];
488  Assembler assm(isolate, buffer, sizeof buffer);
489
490  ASSERT(CpuFeatures::IsSupported(SSE2));
491  CpuFeatureScope fscope(&assm, SSE2);
492
493  // Remove return address from the stack for fix stack frame alignment.
494  __ pop(ecx);
495
496  // Store input vector on the stack.
497  for (int i = 0; i < ELEMENT_COUNT; ++i) {
498    __ push(Immediate(vec->Get(i)->Int32Value()));
499  }
500
501  // Read vector into a xmm register.
502  __ pxor(xmm0, xmm0);
503  __ movdqa(xmm0, Operand(esp, 0));
504  // Create mask and store it in the return register.
505  __ movmskps(eax, xmm0);
506
507  // Remove unused data from the stack.
508  __ add(esp, Immediate(ELEMENT_COUNT * sizeof(int32_t)));
509  // Restore return address.
510  __ push(ecx);
511
512  __ ret(0);
513
514  CodeDesc desc;
515  assm.GetCode(&desc);
516
517  Object* code = isolate->heap()->CreateCode(
518      desc,
519      Code::ComputeFlags(Code::STUB),
520      Handle<Code>())->ToObjectChecked();
521  CHECK(code->IsCode());
522
523  F0 f = FUNCTION_CAST<F0>(Code::cast(code)->entry());
524  int res = f();
525  args.GetReturnValue().Set(v8::Integer::New(res));
526}
527
528
529TEST(StackAlignmentForSSE2) {
530  CcTest::InitializeVM();
531  if (!CpuFeatures::IsSupported(SSE2)) return;
532
533  CHECK_EQ(0, OS::ActivationFrameAlignment() % 16);
534
535  v8::Isolate* isolate = v8::Isolate::GetCurrent();
536  v8::HandleScope handle_scope(isolate);
537  v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
538  global_template->Set(v8_str("do_sse2"), v8::FunctionTemplate::New(DoSSE2));
539
540  LocalContext env(NULL, global_template);
541  CompileRun(
542      "function foo(vec) {"
543      "  return do_sse2(vec);"
544      "}");
545
546  v8::Local<v8::Object> global_object = env->Global();
547  v8::Local<v8::Function> foo =
548      v8::Local<v8::Function>::Cast(global_object->Get(v8_str("foo")));
549
550  int32_t vec[ELEMENT_COUNT] = { -1, 1, 1, 1 };
551  v8::Local<v8::Array> v8_vec = v8::Array::New(ELEMENT_COUNT);
552  for (int i = 0; i < ELEMENT_COUNT; i++) {
553      v8_vec->Set(i, v8_num(vec[i]));
554  }
555
556  v8::Local<v8::Value> args[] = { v8_vec };
557  v8::Local<v8::Value> result = foo->Call(global_object, 1, args);
558
559  // The mask should be 0b1000.
560  CHECK_EQ(8, result->Int32Value());
561}
562
563#undef ELEMENT_COUNT
564#endif  // __GNUC__
565
566
567#undef __
568