1 /*************************** 2 * D programming language http://www.digitalmars.com/d/ 3 * Runtime support for float array operations. 4 * Based on code originally written by Burton Radons. 5 * Placed in public domain. 6 */ 7 8 module rt.compiler.gdc.rt.arrayfloat; 9 10 import CPUid = rt.compiler.util.cpuid; 11 12 debug(UnitTest) 13 { 14 private extern(C) int printf(char*,...); 15 /* This is so unit tests will test every CPU variant 16 */ 17 int cpuid; 18 const int CPUID_MAX = 5; 19 bool mmx() { return cpuid == 1 && CPUid.mmx(); } 20 bool sse() { return cpuid == 2 && CPUid.sse(); } 21 bool sse2() { return cpuid == 3 && CPUid.sse2(); } 22 bool amd3dnow() { return cpuid == 4 && CPUid.amd3dnow(); } 23 } 24 else 25 { 26 alias CPUid.mmx mmx; 27 alias CPUid.sse sse; 28 alias CPUid.sse2 sse2; 29 alias CPUid.amd3dnow amd3dnow; 30 } 31 32 //version = log; 33 34 bool disjoint(T)(T[] a, T[] b) 35 { 36 return (a.ptr + a.length <= b.ptr || b.ptr + b.length <= a.ptr); 37 } 38 39 alias float T; 40 41 extern (C): 42 43 /* ======================================================================== */ 44 /* ======================================================================== */ 45 46 /* template for the case 47 * a[] = b[] ? c[] 48 * with some binary operator ? 49 */ 50 private template CodeGenSliceSliceOp(char[] opD, char[] opSSE, char[] op3DNow) 51 { 52 const char[] CodeGenSliceSliceOp = ` 53 auto aptr = a.ptr; 54 auto aend = aptr + a.length; 55 auto bptr = b.ptr; 56 auto cptr = c.ptr; 57 58 version (D_InlineAsm_X86) 59 { 60 // SSE version is 834% faster 61 if (sse() && b.length >= 16) 62 { 63 auto n = aptr + (b.length & ~15); 64 65 // Unaligned case 66 asm 67 { 68 mov EAX, bptr; // left operand 69 mov ECX, cptr; // right operand 70 mov ESI, aptr; // destination operand 71 mov EDI, n; // end comparison 72 73 align 8; 74 startsseloopb: 75 movups XMM0, [EAX]; 76 movups XMM1, [EAX+16]; 77 movups XMM2, [EAX+32]; 78 movups XMM3, [EAX+48]; 79 add EAX, 64; 80 movups XMM4, [ECX]; 81 movups XMM5, [ECX+16]; 82 movups XMM6, [ECX+32]; 83 movups XMM7, [ECX+48]; 84 add ESI, 64; 85 ` ~ opSSE ~ ` XMM0, XMM4; 86 ` ~ opSSE ~ ` XMM1, XMM5; 87 ` ~ opSSE ~ ` XMM2, XMM6; 88 ` ~ opSSE ~ ` XMM3, XMM7; 89 add ECX, 64; 90 movups [ESI+ 0-64], XMM0; 91 movups [ESI+16-64], XMM1; 92 movups [ESI+32-64], XMM2; 93 movups [ESI+48-64], XMM3; 94 cmp ESI, EDI; 95 jb startsseloopb; 96 97 mov aptr, ESI; 98 mov bptr, EAX; 99 mov cptr, ECX; 100 } 101 } 102 else 103 // 3DNow! version is only 13% faster 104 if (amd3dnow() && b.length >= 8) 105 { 106 auto n = aptr + (b.length & ~7); 107 108 asm 109 { 110 mov ESI, aptr; // destination operand 111 mov EDI, n; // end comparison 112 mov EAX, bptr; // left operand 113 mov ECX, cptr; // right operand 114 115 align 4; 116 start3dnow: 117 movq MM0, [EAX]; 118 movq MM1, [EAX+8]; 119 movq MM2, [EAX+16]; 120 movq MM3, [EAX+24]; 121 ` ~ op3DNow ~ ` MM0, [ECX]; 122 ` ~ op3DNow ~ ` MM1, [ECX+8]; 123 ` ~ op3DNow ~ ` MM2, [ECX+16]; 124 ` ~ op3DNow ~ ` MM3, [ECX+24]; 125 movq [ESI], MM0; 126 movq [ESI+8], MM1; 127 movq [ESI+16], MM2; 128 movq [ESI+24], MM3; 129 add ECX, 32; 130 add ESI, 32; 131 add EAX, 32; 132 cmp ESI, EDI; 133 jb start3dnow; 134 135 emms; 136 mov aptr, ESI; 137 mov bptr, EAX; 138 mov cptr, ECX; 139 } 140 } 141 } 142 143 // Handle remainder 144 while (aptr < aend) 145 *aptr++ = *bptr++ ` ~ opD ~ ` *cptr++; 146 147 return a;`; 148 } 149 150 /* ======================================================================== */ 151 152 /*********************** 153 * Computes: 154 * a[] = b[] + c[] 155 */ 156 157 T[] _arraySliceSliceAddSliceAssign_f(T[] a, T[] c, T[] b) 158 in 159 { 160 assert(a.length == b.length && b.length == c.length); 161 assert(disjoint(a, b)); 162 assert(disjoint(a, c)); 163 assert(disjoint(b, c)); 164 } 165 body 166 { 167 mixin(CodeGenSliceSliceOp!("+", "addps", "pfadd")); 168 } 169 170 171 unittest 172 { 173 printf("_arraySliceSliceAddSliceAssign_f unittest\n"); 174 for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) 175 { 176 version (log) printf(" cpuid %d\n", cpuid); 177 178 for (int j = 0; j < 2; j++) 179 { 180 const int dim = 67; 181 T[] a = new T[dim + j]; // aligned on 16 byte boundary 182 a = a[j .. dim + j]; // misalign for second iteration 183 T[] b = new T[dim + j]; 184 b = b[j .. dim + j]; 185 T[] c = new T[dim + j]; 186 c = c[j .. dim + j]; 187 188 for (int i = 0; i < dim; i++) 189 { a[i] = cast(T)i; 190 b[i] = cast(T)(i + 7); 191 c[i] = cast(T)(i * 2); 192 } 193 194 c[] = a[] + b[]; 195 196 for (int i = 0; i < dim; i++) 197 { 198 if (c[i] != cast(T)(a[i] + b[i])) 199 { 200 printf("[%d]: %g != %g + %g\n", i, c[i], a[i], b[i]); 201 assert(0); 202 } 203 } 204 } 205 } 206 } 207 208 /* ======================================================================== */ 209 210 /*********************** 211 * Computes: 212 * a[] = b[] - c[] 213 */ 214 215 T[] _arraySliceSliceMinSliceAssign_f(T[] a, T[] c, T[] b) 216 in 217 { 218 assert(a.length == b.length && b.length == c.length); 219 assert(disjoint(a, b)); 220 assert(disjoint(a, c)); 221 assert(disjoint(b, c)); 222 } 223 body 224 { 225 mixin(CodeGenSliceSliceOp!("-", "subps", "pfsub")); 226 } 227 228 229 unittest 230 { 231 printf("_arraySliceSliceMinSliceAssign_f unittest\n"); 232 for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) 233 { 234 version (log) printf(" cpuid %d\n", cpuid); 235 236 for (int j = 0; j < 2; j++) 237 { 238 const int dim = 67; 239 T[] a = new T[dim + j]; // aligned on 16 byte boundary 240 a = a[j .. dim + j]; // misalign for second iteration 241 T[] b = new T[dim + j]; 242 b = b[j .. dim + j]; 243 T[] c = new T[dim + j]; 244 c = c[j .. dim + j]; 245 246 for (int i = 0; i < dim; i++) 247 { a[i] = cast(T)i; 248 b[i] = cast(T)(i + 7); 249 c[i] = cast(T)(i * 2); 250 } 251 252 c[] = a[] - b[]; 253 254 for (int i = 0; i < dim; i++) 255 { 256 if (c[i] != cast(T)(a[i] - b[i])) 257 { 258 printf("[%d]: %g != %gd - %g\n", i, c[i], a[i], b[i]); 259 assert(0); 260 } 261 } 262 } 263 } 264 } 265 266 /* ======================================================================== */ 267 268 /*********************** 269 * Computes: 270 * a[] = b[] * c[] 271 */ 272 273 T[] _arraySliceSliceMulSliceAssign_f(T[] a, T[] c, T[] b) 274 in 275 { 276 assert(a.length == b.length && b.length == c.length); 277 assert(disjoint(a, b)); 278 assert(disjoint(a, c)); 279 assert(disjoint(b, c)); 280 } 281 body 282 { 283 mixin(CodeGenSliceSliceOp!("*", "mulps", "pfmul")); 284 } 285 286 unittest 287 { 288 printf("_arraySliceSliceMulSliceAssign_f unittest\n"); 289 for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) 290 { 291 version (log) printf(" cpuid %d\n", cpuid); 292 293 for (int j = 0; j < 2; j++) 294 { 295 const int dim = 67; 296 T[] a = new T[dim + j]; // aligned on 16 byte boundary 297 a = a[j .. dim + j]; // misalign for second iteration 298 T[] b = new T[dim + j]; 299 b = b[j .. dim + j]; 300 T[] c = new T[dim + j]; 301 c = c[j .. dim + j]; 302 303 for (int i = 0; i < dim; i++) 304 { a[i] = cast(T)i; 305 b[i] = cast(T)(i + 7); 306 c[i] = cast(T)(i * 2); 307 } 308 309 c[] = a[] * b[]; 310 311 for (int i = 0; i < dim; i++) 312 { 313 if (c[i] != cast(T)(a[i] * b[i])) 314 { 315 printf("[%d]: %g != %g * %g\n", i, c[i], a[i], b[i]); 316 assert(0); 317 } 318 } 319 } 320 } 321 } 322 323 /* ======================================================================== */ 324 /* ======================================================================== */ 325 326 /* template for the case 327 * a[] ?= value 328 * with some binary operator ? 329 */ 330 private template CodeGenExpSliceOpAssign(char[] opD, char[] opSSE, char[] op3DNow) 331 { 332 const char[] CodeGenExpSliceOpAssign = ` 333 auto aptr = a.ptr; 334 auto aend = aptr + a.length; 335 336 version (D_InlineAsm_X86) 337 { 338 if (sse() && a.length >= 16) 339 { 340 auto aabeg = cast(T*)((cast(uint)aptr + 15) & ~15); // beginning of paragraph-aligned slice of a 341 auto aaend = cast(T*)((cast(uint)aend) & ~15); // end of paragraph-aligned slice of a 342 343 int numAligned = cast(int)(aaend - aabeg); // how many floats are in the aligned slice? 344 345 // are there at least 16 floats in the paragraph-aligned slice? 346 // otherwise we can't do anything with SSE. 347 if (numAligned >= 16) 348 { 349 aaend = aabeg + (numAligned & ~15); // make sure the slice is actually a multiple of 16 floats long 350 351 // process values up to aligned slice one by one 352 while (aptr < aabeg) 353 *aptr++ ` ~ opD ~ ` value; 354 355 // process aligned slice with fast SSE operations 356 asm 357 { 358 mov ESI, aabeg; 359 mov EDI, aaend; 360 movss XMM4, value; 361 shufps XMM4, XMM4, 0; 362 363 align 8; 364 startsseloopa: 365 movaps XMM0, [ESI]; 366 movaps XMM1, [ESI+16]; 367 movaps XMM2, [ESI+32]; 368 movaps XMM3, [ESI+48]; 369 add ESI, 64; 370 ` ~ opSSE ~ ` XMM0, XMM4; 371 ` ~ opSSE ~ ` XMM1, XMM4; 372 ` ~ opSSE ~ ` XMM2, XMM4; 373 ` ~ opSSE ~ ` XMM3, XMM4; 374 movaps [ESI+ 0-64], XMM0; 375 movaps [ESI+16-64], XMM1; 376 movaps [ESI+32-64], XMM2; 377 movaps [ESI+48-64], XMM3; 378 cmp ESI, EDI; 379 jb startsseloopa; 380 } 381 aptr = aaend; 382 } 383 } 384 else 385 // 3DNow! version is 63% faster 386 if (amd3dnow() && a.length >= 8) 387 { 388 auto n = aptr + (a.length & ~7); 389 390 ulong w = *cast(uint *) &value; 391 ulong v = w | (w << 32L); 392 393 asm 394 { 395 mov ESI, dword ptr [aptr]; 396 mov EDI, dword ptr [n]; 397 movq MM4, qword ptr [v]; 398 399 align 8; 400 start: 401 movq MM0, [ESI]; 402 movq MM1, [ESI+8]; 403 movq MM2, [ESI+16]; 404 movq MM3, [ESI+24]; 405 ` ~ op3DNow ~ ` MM0, MM4; 406 ` ~ op3DNow ~ ` MM1, MM4; 407 ` ~ op3DNow ~ ` MM2, MM4; 408 ` ~ op3DNow ~ ` MM3, MM4; 409 movq [ESI], MM0; 410 movq [ESI+8], MM1; 411 movq [ESI+16], MM2; 412 movq [ESI+24], MM3; 413 add ESI, 32; 414 cmp ESI, EDI; 415 jb start; 416 417 emms; 418 mov dword ptr [aptr], ESI; 419 } 420 } 421 } 422 423 while (aptr < aend) 424 *aptr++ ` ~ opD ~ ` value; 425 426 return a;`; 427 } 428 429 /* ======================================================================== */ 430 431 /*********************** 432 * Computes: 433 * a[] += value 434 */ 435 436 T[] _arrayExpSliceAddass_f(T[] a, T value) 437 { 438 mixin(CodeGenExpSliceOpAssign!("+=", "addps", "pfadd")); 439 } 440 441 unittest 442 { 443 printf("_arrayExpSliceAddass_f unittest\n"); 444 for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) 445 { 446 version (log) printf(" cpuid %d\n", cpuid); 447 448 for (int j = 0; j < 2; j++) 449 { 450 const int dim = 67; 451 T[] a = new T[dim + j]; // aligned on 16 byte boundary 452 a = a[j .. dim + j]; // misalign for second iteration 453 T[] b = new T[dim + j]; 454 b = b[j .. dim + j]; 455 T[] c = new T[dim + j]; 456 c = c[j .. dim + j]; 457 458 for (int i = 0; i < dim; i++) 459 { a[i] = cast(T)i; 460 b[i] = cast(T)(i + 7); 461 c[i] = cast(T)(i * 2); 462 } 463 464 a[] = c[]; 465 c[] += 6; 466 467 for (int i = 0; i < dim; i++) 468 { 469 if (c[i] != cast(T)(a[i] + 6)) 470 { 471 printf("[%d]: %g != %g + 6\n", i, c[i], a[i]); 472 assert(0); 473 } 474 } 475 } 476 } 477 } 478 479 /* ======================================================================== */ 480 481 /*********************** 482 * Computes: 483 * a[] -= value 484 */ 485 486 T[] _arrayExpSliceMinass_f(T[] a, T value) 487 { 488 mixin(CodeGenExpSliceOpAssign!("-=", "subps", "pfsub")); 489 } 490 491 unittest 492 { 493 printf("_arrayExpSliceminass_f unittest\n"); 494 for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) 495 { 496 version (log) printf(" cpuid %d\n", cpuid); 497 498 for (int j = 0; j < 2; j++) 499 { 500 const int dim = 67; 501 T[] a = new T[dim + j]; // aligned on 16 byte boundary 502 a = a[j .. dim + j]; // misalign for second iteration 503 T[] b = new T[dim + j]; 504 b = b[j .. dim + j]; 505 T[] c = new T[dim + j]; 506 c = c[j .. dim + j]; 507 508 for (int i = 0; i < dim; i++) 509 { a[i] = cast(T)i; 510 b[i] = cast(T)(i + 7); 511 c[i] = cast(T)(i * 2); 512 } 513 514 a[] = c[]; 515 c[] -= 6; 516 517 for (int i = 0; i < dim; i++) 518 { 519 if (c[i] != cast(T)(a[i] - 6)) 520 { 521 printf("[%d]: %g != %g - 6\n", i, c[i], a[i]); 522 assert(0); 523 } 524 } 525 } 526 } 527 } 528 529 /* ======================================================================== */ 530 531 /*********************** 532 * Computes: 533 * a[] *= value 534 */ 535 536 T[] _arrayExpSliceMulass_f(T[] a, T value) 537 { 538 mixin(CodeGenExpSliceOpAssign!("*=", "mulps", "pfmul")); 539 } 540 541 unittest 542 { 543 printf("_arrayExpSliceMulass_f unittest\n"); 544 for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) 545 { 546 version (log) printf(" cpuid %d\n", cpuid); 547 548 for (int j = 0; j < 2; j++) 549 { 550 const int dim = 67; 551 T[] a = new T[dim + j]; // aligned on 16 byte boundary 552 a = a[j .. dim + j]; // misalign for second iteration 553 T[] b = new T[dim + j]; 554 b = b[j .. dim + j]; 555 T[] c = new T[dim + j]; 556 c = c[j .. dim + j]; 557 558 for (int i = 0; i < dim; i++) 559 { a[i] = cast(T)i; 560 b[i] = cast(T)(i + 7); 561 c[i] = cast(T)(i * 2); 562 } 563 564 a[] = c[]; 565 c[] *= 6; 566 567 for (int i = 0; i < dim; i++) 568 { 569 if (c[i] != cast(T)(a[i] * 6)) 570 { 571 printf("[%d]: %g != %g * 6\n", i, c[i], a[i]); 572 assert(0); 573 } 574 } 575 } 576 } 577 } 578 579 /* ======================================================================== */ 580 581 /*********************** 582 * Computes: 583 * a[] /= value 584 */ 585 586 T[] _arrayExpSliceDivass_f(T[] a, T value) 587 { 588 return _arrayExpSliceMulass_f(a, 1f / value); 589 } 590 591 unittest 592 { 593 printf("_arrayExpSliceDivass_f unittest\n"); 594 for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) 595 { 596 version (log) printf(" cpuid %d\n", cpuid); 597 598 for (int j = 0; j < 2; j++) 599 { 600 const int dim = 67; 601 T[] a = new T[dim + j]; // aligned on 16 byte boundary 602 a = a[j .. dim + j]; // misalign for second iteration 603 T[] b = new T[dim + j]; 604 b = b[j .. dim + j]; 605 T[] c = new T[dim + j]; 606 c = c[j .. dim + j]; 607 608 for (int i = 0; i < dim; i++) 609 { a[i] = cast(T)i; 610 b[i] = cast(T)(i + 7); 611 c[i] = cast(T)(i * 2); 612 } 613 614 a[] = c[]; 615 c[] /= 8; 616 617 for (int i = 0; i < dim; i++) 618 { 619 if (c[i] != cast(T)(a[i] / 8)) 620 { 621 printf("[%d]: %g != %g / 8\n", i, c[i], a[i]); 622 assert(0); 623 } 624 } 625 } 626 } 627 } 628 629 630 /* ======================================================================== */ 631 /* ======================================================================== */ 632 633 /* template for the case 634 * a[] = b[] ? value 635 * with some binary operator ? 636 */ 637 private template CodeGenSliceExpOp(char[] opD, char[] opSSE, char[] op3DNow) 638 { 639 const char[] CodeGenSliceExpOp = ` 640 auto aptr = a.ptr; 641 auto aend = aptr + a.length; 642 auto bptr = b.ptr; 643 644 version (D_InlineAsm_X86) 645 { 646 // SSE version is 665% faster 647 if (sse() && a.length >= 16) 648 { 649 auto n = aptr + (a.length & ~15); 650 651 // Unaligned case 652 asm 653 { 654 mov EAX, bptr; 655 mov ESI, aptr; 656 mov EDI, n; 657 movss XMM4, value; 658 shufps XMM4, XMM4, 0; 659 660 align 8; 661 startsseloop: 662 add ESI, 64; 663 movups XMM0, [EAX]; 664 movups XMM1, [EAX+16]; 665 movups XMM2, [EAX+32]; 666 movups XMM3, [EAX+48]; 667 add EAX, 64; 668 ` ~ opSSE ~ ` XMM0, XMM4; 669 ` ~ opSSE ~ ` XMM1, XMM4; 670 ` ~ opSSE ~ ` XMM2, XMM4; 671 ` ~ opSSE ~ ` XMM3, XMM4; 672 movups [ESI+ 0-64], XMM0; 673 movups [ESI+16-64], XMM1; 674 movups [ESI+32-64], XMM2; 675 movups [ESI+48-64], XMM3; 676 cmp ESI, EDI; 677 jb startsseloop; 678 679 mov aptr, ESI; 680 mov bptr, EAX; 681 } 682 } 683 else 684 // 3DNow! version is 69% faster 685 if (amd3dnow() && a.length >= 8) 686 { 687 auto n = aptr + (a.length & ~7); 688 689 ulong w = *cast(uint *) &value; 690 ulong v = w | (w << 32L); 691 692 asm 693 { 694 mov ESI, aptr; 695 mov EDI, n; 696 mov EAX, bptr; 697 movq MM4, qword ptr [v]; 698 699 align 8; 700 start3dnow: 701 movq MM0, [EAX]; 702 movq MM1, [EAX+8]; 703 movq MM2, [EAX+16]; 704 movq MM3, [EAX+24]; 705 ` ~ op3DNow ~ ` MM0, MM4; 706 ` ~ op3DNow ~ ` MM1, MM4; 707 ` ~ op3DNow ~ ` MM2, MM4; 708 ` ~ op3DNow ~ ` MM3, MM4; 709 movq [ESI], MM0; 710 movq [ESI+8], MM1; 711 movq [ESI+16], MM2; 712 movq [ESI+24], MM3; 713 add ESI, 32; 714 add EAX, 32; 715 cmp ESI, EDI; 716 jb start3dnow; 717 718 emms; 719 mov aptr, ESI; 720 mov bptr, EAX; 721 } 722 } 723 } 724 725 while (aptr < aend) 726 *aptr++ = *bptr++ ` ~ opD ~ ` value; 727 728 return a;`; 729 } 730 731 /* ======================================================================== */ 732 733 /*********************** 734 * Computes: 735 * a[] = b[] + value 736 */ 737 738 T[] _arraySliceExpAddSliceAssign_f(T[] a, T value, T[] b) 739 in 740 { 741 assert(a.length == b.length); 742 assert(disjoint(a, b)); 743 } 744 body 745 { 746 mixin(CodeGenSliceExpOp!("+", "addps", "pfadd")); 747 } 748 749 unittest 750 { 751 printf("_arraySliceExpAddSliceAssign_f unittest\n"); 752 for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) 753 { 754 version (log) printf(" cpuid %d\n", cpuid); 755 756 for (int j = 0; j < 2; j++) 757 { 758 const int dim = 67; 759 T[] a = new T[dim + j]; // aligned on 16 byte boundary 760 a = a[j .. dim + j]; // misalign for second iteration 761 T[] b = new T[dim + j]; 762 b = b[j .. dim + j]; 763 T[] c = new T[dim + j]; 764 c = c[j .. dim + j]; 765 766 for (int i = 0; i < dim; i++) 767 { a[i] = cast(T)i; 768 b[i] = cast(T)(i + 7); 769 c[i] = cast(T)(i * 2); 770 } 771 772 c[] = a[] + 6; 773 774 for (int i = 0; i < dim; i++) 775 { 776 if (c[i] != cast(T)(a[i] + 6)) 777 { 778 printf("[%d]: %g != %g + 6\n", i, c[i], a[i]); 779 assert(0); 780 } 781 } 782 } 783 } 784 } 785 786 /* ======================================================================== */ 787 788 /*********************** 789 * Computes: 790 * a[] = b[] - value 791 */ 792 793 T[] _arraySliceExpMinSliceAssign_f(T[] a, T value, T[] b) 794 in 795 { 796 assert (a.length == b.length); 797 assert (disjoint(a, b)); 798 } 799 body 800 { 801 mixin(CodeGenSliceExpOp!("-", "subps", "pfsub")); 802 } 803 804 unittest 805 { 806 printf("_arraySliceExpMinSliceAssign_f unittest\n"); 807 for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) 808 { 809 version (log) printf(" cpuid %d\n", cpuid); 810 811 for (int j = 0; j < 2; j++) 812 { 813 const int dim = 67; 814 T[] a = new T[dim + j]; // aligned on 16 byte boundary 815 a = a[j .. dim + j]; // misalign for second iteration 816 T[] b = new T[dim + j]; 817 b = b[j .. dim + j]; 818 T[] c = new T[dim + j]; 819 c = c[j .. dim + j]; 820 821 for (int i = 0; i < dim; i++) 822 { a[i] = cast(T)i; 823 b[i] = cast(T)(i + 7); 824 c[i] = cast(T)(i * 2); 825 } 826 827 c[] = a[] - 6; 828 829 for (int i = 0; i < dim; i++) 830 { 831 if (c[i] != cast(T)(a[i] - 6)) 832 { 833 printf("[%d]: %g != %g - 6\n", i, c[i], a[i]); 834 assert(0); 835 } 836 } 837 } 838 } 839 } 840 841 /* ======================================================================== */ 842 843 /*********************** 844 * Computes: 845 * a[] = b[] * value 846 */ 847 848 T[] _arraySliceExpMulSliceAssign_f(T[] a, T value, T[] b) 849 in 850 { 851 assert(a.length == b.length); 852 assert(disjoint(a, b)); 853 } 854 body 855 { 856 mixin(CodeGenSliceExpOp!("*", "mulps", "pfmul")); 857 } 858 859 unittest 860 { 861 printf("_arraySliceExpMulSliceAssign_f unittest\n"); 862 for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) 863 { 864 version (log) printf(" cpuid %d\n", cpuid); 865 866 for (int j = 0; j < 2; j++) 867 { 868 const int dim = 67; 869 T[] a = new T[dim + j]; // aligned on 16 byte boundary 870 a = a[j .. dim + j]; // misalign for second iteration 871 T[] b = new T[dim + j]; 872 b = b[j .. dim + j]; 873 T[] c = new T[dim + j]; 874 c = c[j .. dim + j]; 875 876 for (int i = 0; i < dim; i++) 877 { a[i] = cast(T)i; 878 b[i] = cast(T)(i + 7); 879 c[i] = cast(T)(i * 2); 880 } 881 882 c[] = a[] * 6; 883 884 for (int i = 0; i < dim; i++) 885 { 886 if (c[i] != cast(T)(a[i] * 6)) 887 { 888 printf("[%d]: %g != %g * 6\n", i, c[i], a[i]); 889 assert(0); 890 } 891 } 892 } 893 } 894 } 895 896 /* ======================================================================== */ 897 898 /*********************** 899 * Computes: 900 * a[] = b[] / value 901 */ 902 903 T[] _arraySliceExpDivSliceAssign_f(T[] a, T value, T[] b) 904 { 905 return _arraySliceExpMulSliceAssign_f(a, 1f/value, b); 906 } 907 908 unittest 909 { 910 printf("_arraySliceExpDivSliceAssign_f unittest\n"); 911 for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) 912 { 913 version (log) printf(" cpuid %d\n", cpuid); 914 915 for (int j = 0; j < 2; j++) 916 { 917 const int dim = 67; 918 T[] a = new T[dim + j]; // aligned on 16 byte boundary 919 a = a[j .. dim + j]; // misalign for second iteration 920 T[] b = new T[dim + j]; 921 b = b[j .. dim + j]; 922 T[] c = new T[dim + j]; 923 c = c[j .. dim + j]; 924 925 for (int i = 0; i < dim; i++) 926 { a[i] = cast(T)i; 927 b[i] = cast(T)(i + 7); 928 c[i] = cast(T)(i * 2); 929 } 930 931 c[] = a[] / 8; 932 933 for (int i = 0; i < dim; i++) 934 { 935 if (c[i] != cast(T)(a[i] / 8)) 936 { 937 printf("[%d]: %g != %g / 8\n", i, c[i], a[i]); 938 assert(0); 939 } 940 } 941 } 942 } 943 } 944 945 /* ======================================================================== */ 946 /* ======================================================================== */ 947 948 private template CodeGenSliceOpAssign(char[] opD, char[] opSSE, char[] op3DNow) 949 { 950 const char[] CodeGenSliceOpAssign = ` 951 auto aptr = a.ptr; 952 auto aend = aptr + a.length; 953 auto bptr = b.ptr; 954 955 version (D_InlineAsm_X86) 956 { 957 // SSE version is 468% faster 958 if (sse() && a.length >= 16) 959 { 960 auto n = aptr + (a.length & ~15); 961 962 // Unaligned case 963 asm 964 { 965 mov ECX, bptr; // right operand 966 mov ESI, aptr; // destination operand 967 mov EDI, n; // end comparison 968 969 align 8; 970 startsseloopb: 971 movups XMM0, [ESI]; 972 movups XMM1, [ESI+16]; 973 movups XMM2, [ESI+32]; 974 movups XMM3, [ESI+48]; 975 add ESI, 64; 976 movups XMM4, [ECX]; 977 movups XMM5, [ECX+16]; 978 movups XMM6, [ECX+32]; 979 movups XMM7, [ECX+48]; 980 add ECX, 64; 981 ` ~ opSSE ~ ` XMM0, XMM4; 982 ` ~ opSSE ~ ` XMM1, XMM5; 983 ` ~ opSSE ~ ` XMM2, XMM6; 984 ` ~ opSSE ~ ` XMM3, XMM7; 985 movups [ESI+ 0-64], XMM0; 986 movups [ESI+16-64], XMM1; 987 movups [ESI+32-64], XMM2; 988 movups [ESI+48-64], XMM3; 989 cmp ESI, EDI; 990 jb startsseloopb; 991 992 mov aptr, ESI; 993 mov bptr, ECX; 994 } 995 } 996 else 997 // 3DNow! version is 57% faster 998 if (amd3dnow() && a.length >= 8) 999 { 1000 auto n = aptr + (a.length & ~7); 1001 1002 asm 1003 { 1004 mov ESI, dword ptr [aptr]; // destination operand 1005 mov EDI, dword ptr [n]; // end comparison 1006 mov ECX, dword ptr [bptr]; // right operand 1007 1008 align 4; 1009 start3dnow: 1010 movq MM0, [ESI]; 1011 movq MM1, [ESI+8]; 1012 movq MM2, [ESI+16]; 1013 movq MM3, [ESI+24]; 1014 ` ~ op3DNow ~ ` MM0, [ECX]; 1015 ` ~ op3DNow ~ ` MM1, [ECX+8]; 1016 ` ~ op3DNow ~ ` MM2, [ECX+16]; 1017 ` ~ op3DNow ~ ` MM3, [ECX+24]; 1018 movq [ESI], MM0; 1019 movq [ESI+8], MM1; 1020 movq [ESI+16], MM2; 1021 movq [ESI+24], MM3; 1022 add ESI, 32; 1023 add ECX, 32; 1024 cmp ESI, EDI; 1025 jb start3dnow; 1026 1027 emms; 1028 mov dword ptr [aptr], ESI; 1029 mov dword ptr [bptr], ECX; 1030 } 1031 } 1032 } 1033 1034 while (aptr < aend) 1035 *aptr++ ` ~ opD ~ ` *bptr++; 1036 1037 return a;`; 1038 } 1039 1040 /* ======================================================================== */ 1041 1042 /*********************** 1043 * Computes: 1044 * a[] += b[] 1045 */ 1046 1047 T[] _arraySliceSliceAddass_f(T[] a, T[] b) 1048 in 1049 { 1050 assert (a.length == b.length); 1051 assert (disjoint(a, b)); 1052 } 1053 body 1054 { 1055 mixin(CodeGenSliceOpAssign!("+=", "addps", "pfadd")); 1056 } 1057 1058 unittest 1059 { 1060 printf("_arraySliceSliceAddass_f unittest\n"); 1061 for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) 1062 { 1063 version (log) printf(" cpuid %d\n", cpuid); 1064 1065 for (int j = 0; j < 2; j++) 1066 { 1067 const int dim = 67; 1068 T[] a = new T[dim + j]; // aligned on 16 byte boundary 1069 a = a[j .. dim + j]; // misalign for second iteration 1070 T[] b = new T[dim + j]; 1071 b = b[j .. dim + j]; 1072 T[] c = new T[dim + j]; 1073 c = c[j .. dim + j]; 1074 1075 for (int i = 0; i < dim; i++) 1076 { a[i] = cast(T)i; 1077 b[i] = cast(T)(i + 7); 1078 c[i] = cast(T)(i * 2); 1079 } 1080 1081 a[] = c[]; 1082 c[] += b[]; 1083 1084 for (int i = 0; i < dim; i++) 1085 { 1086 if (c[i] != cast(T)(a[i] + b[i])) 1087 { 1088 printf("[%d]: %g != %g + %g\n", i, c[i], a[i], b[i]); 1089 assert(0); 1090 } 1091 } 1092 } 1093 } 1094 } 1095 1096 /* ======================================================================== */ 1097 1098 /*********************** 1099 * Computes: 1100 * a[] -= b[] 1101 */ 1102 1103 T[] _arraySliceSliceMinass_f(T[] a, T[] b) 1104 in 1105 { 1106 assert (a.length == b.length); 1107 assert (disjoint(a, b)); 1108 } 1109 body 1110 { 1111 mixin(CodeGenSliceOpAssign!("-=", "subps", "pfsub")); 1112 } 1113 1114 unittest 1115 { 1116 printf("_arrayExpSliceMinass_f unittest\n"); 1117 for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) 1118 { 1119 version (log) printf(" cpuid %d\n", cpuid); 1120 1121 for (int j = 0; j < 2; j++) 1122 { 1123 const int dim = 67; 1124 T[] a = new T[dim + j]; // aligned on 16 byte boundary 1125 a = a[j .. dim + j]; // misalign for second iteration 1126 T[] b = new T[dim + j]; 1127 b = b[j .. dim + j]; 1128 T[] c = new T[dim + j]; 1129 c = c[j .. dim + j]; 1130 1131 for (int i = 0; i < dim; i++) 1132 { a[i] = cast(T)i; 1133 b[i] = cast(T)(i + 7); 1134 c[i] = cast(T)(i * 2); 1135 } 1136 1137 a[] = c[]; 1138 c[] -= 6; 1139 1140 for (int i = 0; i < dim; i++) 1141 { 1142 if (c[i] != cast(T)(a[i] - 6)) 1143 { 1144 printf("[%d]: %g != %g - 6\n", i, c[i], a[i]); 1145 assert(0); 1146 } 1147 } 1148 } 1149 } 1150 } 1151 1152 /* ======================================================================== */ 1153 1154 /*********************** 1155 * Computes: 1156 * a[] *= b[] 1157 */ 1158 1159 T[] _arraySliceSliceMulass_f(T[] a, T[] b) 1160 in 1161 { 1162 assert (a.length == b.length); 1163 assert (disjoint(a, b)); 1164 } 1165 body 1166 { 1167 mixin(CodeGenSliceOpAssign!("*=", "mulps", "pfmul")); 1168 } 1169 1170 unittest 1171 { 1172 printf("_arrayExpSliceMulass_f unittest\n"); 1173 for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) 1174 { 1175 version (log) printf(" cpuid %d\n", cpuid); 1176 1177 for (int j = 0; j < 2; j++) 1178 { 1179 const int dim = 67; 1180 T[] a = new T[dim + j]; // aligned on 16 byte boundary 1181 a = a[j .. dim + j]; // misalign for second iteration 1182 T[] b = new T[dim + j]; 1183 b = b[j .. dim + j]; 1184 T[] c = new T[dim + j]; 1185 c = c[j .. dim + j]; 1186 1187 for (int i = 0; i < dim; i++) 1188 { a[i] = cast(T)i; 1189 b[i] = cast(T)(i + 7); 1190 c[i] = cast(T)(i * 2); 1191 } 1192 1193 a[] = c[]; 1194 c[] *= 6; 1195 1196 for (int i = 0; i < dim; i++) 1197 { 1198 if (c[i] != cast(T)(a[i] * 6)) 1199 { 1200 printf("[%d]: %g != %g * 6\n", i, c[i], a[i]); 1201 assert(0); 1202 } 1203 } 1204 } 1205 } 1206 } 1207 1208 /* ======================================================================== */ 1209 /* ======================================================================== */ 1210 1211 /*********************** 1212 * Computes: 1213 * a[] = value - b[] 1214 */ 1215 1216 T[] _arrayExpSliceMinSliceAssign_f(T[] a, T[] b, T value) 1217 in 1218 { 1219 assert (a.length == b.length); 1220 assert (disjoint(a, b)); 1221 } 1222 body 1223 { 1224 //printf("_arrayExpSliceMinSliceAssign_f()\n"); 1225 auto aptr = a.ptr; 1226 auto aend = aptr + a.length; 1227 auto bptr = b.ptr; 1228 1229 version (D_InlineAsm_X86) 1230 { 1231 // SSE version is 690% faster 1232 if (sse() && a.length >= 16) 1233 { 1234 auto n = aptr + (a.length & ~15); 1235 1236 // Unaligned case 1237 asm 1238 { 1239 mov EAX, bptr; 1240 mov ESI, aptr; 1241 mov EDI, n; 1242 movss XMM4, value; 1243 shufps XMM4, XMM4, 0; 1244 1245 align 8; 1246 startsseloop: 1247 add ESI, 64; 1248 movaps XMM5, XMM4; 1249 movaps XMM6, XMM4; 1250 movups XMM0, [EAX]; 1251 movups XMM1, [EAX+16]; 1252 movups XMM2, [EAX+32]; 1253 movups XMM3, [EAX+48]; 1254 add EAX, 64; 1255 subps XMM5, XMM0; 1256 subps XMM6, XMM1; 1257 movups [ESI+ 0-64], XMM5; 1258 movups [ESI+16-64], XMM6; 1259 movaps XMM5, XMM4; 1260 movaps XMM6, XMM4; 1261 subps XMM5, XMM2; 1262 subps XMM6, XMM3; 1263 movups [ESI+32-64], XMM5; 1264 movups [ESI+48-64], XMM6; 1265 cmp ESI, EDI; 1266 jb startsseloop; 1267 1268 mov aptr, ESI; 1269 mov bptr, EAX; 1270 } 1271 } 1272 else 1273 // 3DNow! version is 67% faster 1274 if (amd3dnow() && a.length >= 8) 1275 { 1276 auto n = aptr + (a.length & ~7); 1277 1278 ulong w = *cast(uint *) &value; 1279 ulong v = w | (w << 32L); 1280 1281 asm 1282 { 1283 mov ESI, aptr; 1284 mov EDI, n; 1285 mov EAX, bptr; 1286 movq MM4, qword ptr [v]; 1287 1288 align 8; 1289 start3dnow: 1290 movq MM0, [EAX]; 1291 movq MM1, [EAX+8]; 1292 movq MM2, [EAX+16]; 1293 movq MM3, [EAX+24]; 1294 pfsubr MM0, MM4; 1295 pfsubr MM1, MM4; 1296 pfsubr MM2, MM4; 1297 pfsubr MM3, MM4; 1298 movq [ESI], MM0; 1299 movq [ESI+8], MM1; 1300 movq [ESI+16], MM2; 1301 movq [ESI+24], MM3; 1302 add ESI, 32; 1303 add EAX, 32; 1304 cmp ESI, EDI; 1305 jb start3dnow; 1306 1307 emms; 1308 mov aptr, ESI; 1309 mov bptr, EAX; 1310 } 1311 } 1312 } 1313 1314 while (aptr < aend) 1315 *aptr++ = value - *bptr++; 1316 1317 return a; 1318 } 1319 1320 unittest 1321 { 1322 printf("_arrayExpSliceMinSliceAssign_f unittest\n"); 1323 for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) 1324 { 1325 version (log) printf(" cpuid %d\n", cpuid); 1326 1327 for (int j = 0; j < 2; j++) 1328 { 1329 const int dim = 67; 1330 T[] a = new T[dim + j]; // aligned on 16 byte boundary 1331 a = a[j .. dim + j]; // misalign for second iteration 1332 T[] b = new T[dim + j]; 1333 b = b[j .. dim + j]; 1334 T[] c = new T[dim + j]; 1335 c = c[j .. dim + j]; 1336 1337 for (int i = 0; i < dim; i++) 1338 { a[i] = cast(T)i; 1339 b[i] = cast(T)(i + 7); 1340 c[i] = cast(T)(i * 2); 1341 } 1342 1343 c[] = 6 - a[]; 1344 1345 for (int i = 0; i < dim; i++) 1346 { 1347 if (c[i] != cast(T)(6 - a[i])) 1348 { 1349 printf("[%d]: %g != 6 - %g\n", i, c[i], a[i]); 1350 assert(0); 1351 } 1352 } 1353 } 1354 } 1355 } 1356 1357 /* ======================================================================== */ 1358 1359 /*********************** 1360 * Computes: 1361 * a[] -= b[] * value 1362 */ 1363 1364 T[] _arraySliceExpMulSliceMinass_f(T[] a, T value, T[] b) 1365 { 1366 return _arraySliceExpMulSliceAddass_f(a, -value, b); 1367 } 1368 1369 /*********************** 1370 * Computes: 1371 * a[] += b[] * value 1372 */ 1373 1374 T[] _arraySliceExpMulSliceAddass_f(T[] a, T value, T[] b) 1375 in 1376 { 1377 assert(a.length == b.length); 1378 assert(disjoint(a, b)); 1379 } 1380 body 1381 { 1382 auto aptr = a.ptr; 1383 auto aend = aptr + a.length; 1384 auto bptr = b.ptr; 1385 1386 // Handle remainder 1387 while (aptr < aend) 1388 *aptr++ += *bptr++ * value; 1389 1390 return a; 1391 } 1392 1393 unittest 1394 { 1395 printf("_arraySliceExpMulSliceAddass_f unittest\n"); 1396 1397 cpuid = 1; 1398 { 1399 version (log) printf(" cpuid %d\n", cpuid); 1400 1401 for (int j = 0; j < 1; j++) 1402 { 1403 const int dim = 67; 1404 T[] a = new T[dim + j]; // aligned on 16 byte boundary 1405 a = a[j .. dim + j]; // misalign for second iteration 1406 T[] b = new T[dim + j]; 1407 b = b[j .. dim + j]; 1408 T[] c = new T[dim + j]; 1409 c = c[j .. dim + j]; 1410 1411 for (int i = 0; i < dim; i++) 1412 { a[i] = cast(T)i; 1413 b[i] = cast(T)(i + 7); 1414 c[i] = cast(T)(i * 2); 1415 } 1416 1417 b[] = c[]; 1418 c[] += a[] * 6; 1419 1420 for (int i = 0; i < dim; i++) 1421 { 1422 //printf("[%d]: %g ?= %g + %g * 6\n", i, c[i], b[i], a[i]); 1423 if (c[i] != cast(T)(b[i] + a[i] * 6)) 1424 { 1425 printf("[%d]: %g ?= %g + %g * 6\n", i, c[i], b[i], a[i]); 1426 assert(0); 1427 } 1428 } 1429 } 1430 } 1431 }