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