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