1 // llmath.d 2 // Copyright (C) 1993-2008 by Digital Mars, http://www.digitalmars.com 3 // All Rights Reserved 4 // Written by Walter Bright 5 6 // Compiler runtime support for 64 bit longs 7 8 module rt.compiler.dmd.posix.llmath; 9 10 extern (C): 11 12 13 /*************************************** 14 * Unsigned long divide. 15 * Input: 16 * [EDX,EAX],[ECX,EBX] 17 * Output: 18 * [EDX,EAX] = [EDX,EAX] / [ECX,EBX] 19 * [ECX,EBX] = [EDX,EAX] % [ECX,EBX] 20 */ 21 22 23 void __ULDIV__() 24 { 25 version (D_InlineAsm_X86) 26 asm 27 { 28 naked ; 29 test ECX,ECX ; 30 jz uldiv ; 31 32 // if ECX > EDX, then quotient is 0 and remainder is [EDX,EAX] 33 cmp ECX,EDX ; 34 ja quo0 ; 35 36 test ECX,ECX ; 37 js Lleft ; 38 39 /* We have n>d, and know that n/d will fit in 32 bits. 40 * d will be left justified if we shift it left s bits. 41 * [d1,d0] <<= s 42 * [n2,n1,n0] = [n1,n0] << s 43 * 44 * Use one divide, by this reasoning: 45 * ([n2,n1]<<32 + n0)/(d1<<32 + d0) 46 * becomes: 47 * ([n2,n1]<<32)/(d1<<32 + d0) + n0/(d1<<32 + d0) 48 * The second divide is always 0. 49 * Ignore the d0 in the first divide, which will yield a quotient 50 * that might be too high by 1 (because d1 is left justified). 51 * We can tell if it's too big if: 52 * q*[d1,d0] > [n2,n1,n0] 53 * which is: 54 * q*[d1,d0] > [[q*[d1,0]+q%[d1,0],n1,n0] 55 * If we subtract q*[d1,0] from both sides, we get: 56 * q*d0 > [[n2,n1]%d1,n0] 57 * So if it is too big by one, reduce q by one to q'=q-one. 58 * Compute remainder as: 59 * r = ([n1,n0] - q'*[d1,d0]) >> s 60 * Again, we can subtract q*[d1,0]: 61 * r = ([n1,n0] - q*[d1,0] - (q'*[d1,d0] - q*[d1,0])) >> s 62 * r = ([[n2,n1]%d1,n0] + (q*[d1,0] - (q - one)*[d1,d0])) >> s 63 * r = ([[n2,n1]%d1,n0] + (q*[d1,0] - [d1 *(q-one),d0*(1-q)])) >> s 64 * r = ([[n2,n1]%d1,n0] + [d1 *one,d0*(one-q)])) >> s 65 */ 66 67 push EBP ; 68 push ESI ; 69 push EDI ; 70 71 mov ESI,EDX ; 72 mov EDI,EAX ; 73 mov EBP,ECX ; 74 75 bsr EAX,ECX ; // EAX is now 30..0 76 xor EAX,0x1F ; // EAX is now 1..31 77 mov CH,AL ; 78 neg EAX ; 79 add EAX,32 ; 80 mov CL,AL ; 81 82 mov EAX,EBX ; 83 shr EAX,CL ; 84 xchg CH,CL ; 85 shl EBP,CL ; 86 or EBP,EAX ; 87 shl EBX,CL ; 88 89 mov EDX,ESI ; 90 xchg CH,CL ; 91 shr EDX,CL ; 92 93 mov EAX,EDI ; 94 shr EAX,CL ; 95 xchg CH,CL ; 96 shl EDI,CL ; 97 shl ESI,CL ; 98 or EAX,ESI ; 99 100 div EBP ; 101 push EBP ; 102 mov EBP,EAX ; 103 mov ESI,EDX ; 104 105 mul EBX ; 106 cmp EDX,ESI ; 107 ja L1 ; 108 jb L2 ; 109 cmp EAX,EDI ; 110 jbe L2 ; 111 L1: dec EBP ; 112 sub EAX,EBX ; 113 sbb EDX,0[ESP] ; 114 L2: 115 add ESP,4 ; 116 sub EDI,EAX ; 117 sbb ESI,EDX ; 118 mov EAX,ESI ; 119 xchg CH,CL ; 120 shl EAX,CL ; 121 xchg CH,CL ; 122 shr EDI,CL ; 123 or EDI,EAX ; 124 shr ESI,CL ; 125 mov EBX,EDI ; 126 mov ECX,ESI ; 127 mov EAX,EBP ; 128 xor EDX,EDX ; 129 130 pop EDI ; 131 pop ESI ; 132 pop EBP ; 133 ret ; 134 135 uldiv: test EDX,EDX ; 136 jnz D3 ; 137 // Both high words are 0, we can use the DIV instruction 138 div EBX ; 139 mov EBX,EDX ; 140 mov EDX,ECX ; // EDX = ECX = 0 141 ret ; 142 143 even ; 144 D3: // Divide [EDX,EAX] by EBX 145 mov ECX,EAX ; 146 mov EAX,EDX ; 147 xor EDX,EDX ; 148 div EBX ; 149 xchg ECX,EAX ; 150 div EBX ; 151 // ECX,EAX = result 152 // EDX = remainder 153 mov EBX,EDX ; 154 mov EDX,ECX ; 155 xor ECX,ECX ; 156 ret ; 157 158 quo0: // Quotient is 0 159 // Remainder is [EDX,EAX] 160 mov EBX,EAX ; 161 mov ECX,EDX ; 162 xor EAX,EAX ; 163 xor EDX,EDX ; 164 ret ; 165 166 Lleft: // The quotient is 0 or 1 and EDX >= ECX 167 cmp EDX,ECX ; 168 ja quo1 ; // [EDX,EAX] > [ECX,EBX] 169 // EDX == ECX 170 cmp EAX,EBX ; 171 jb quo0 ; 172 173 quo1: // Quotient is 1 174 // Remainder is [EDX,EAX] - [ECX,EBX] 175 sub EAX,EBX ; 176 sbb EDX,ECX ; 177 mov EBX,EAX ; 178 mov ECX,EDX ; 179 mov EAX,1 ; 180 xor EDX,EDX ; 181 ret ; 182 } 183 else version (D_InlineAsm_X86_64) 184 assert(0); 185 else 186 static assert(0); 187 } 188 189 190 /*************************************** 191 * Signed long divide. 192 * Input: 193 * [EDX,EAX],[ECX,EBX] 194 * Output: 195 * [EDX,EAX] = [EDX,EAX] / [ECX,EBX] 196 * [ECX,EBX] = [EDX,EAX] % [ECX,EBX] 197 * ESI,EDI destroyed 198 */ 199 200 void __LDIV__() 201 { 202 version (D_InlineAsm_X86) 203 asm 204 { 205 naked ; 206 test EDX,EDX ; // [EDX,EAX] negative? 207 jns L10 ; // no 208 //neg64 EDX,EAX ; // [EDX,EAX] = -[EDX,EAX] 209 neg EDX ; 210 neg EAX ; 211 sbb EDX,0 ; 212 test ECX,ECX ; // [ECX,EBX] negative? 213 jns L11 ; // no 214 //neg64 ECX,EBX ; 215 neg ECX ; 216 neg EBX ; 217 sbb ECX,0 ; 218 call __ULDIV__ ; 219 //neg64 ECX,EBX ; // remainder same sign as dividend 220 neg ECX ; 221 neg EBX ; 222 sbb ECX,0 ; 223 ret ; 224 225 L11: call __ULDIV__ ; 226 //neg64 ECX,EBX ; // remainder same sign as dividend 227 neg ECX ; 228 neg EBX ; 229 sbb ECX,0 ; 230 //neg64 EDX,EAX ; // quotient is negative 231 neg EDX ; 232 neg EAX ; 233 sbb EDX,0 ; 234 ret ; 235 236 L10: test ECX,ECX ; // [ECX,EBX] negative? 237 jns L12 ; // no (all is positive) 238 //neg64 ECX,EBX ; 239 neg ECX ; 240 neg EBX ; 241 sbb ECX,0 ; 242 call __ULDIV__ ; 243 //neg64 EDX,EAX ; // quotient is negative 244 neg EDX ; 245 neg EAX ; 246 sbb EDX,0 ; 247 ret ; 248 249 L12: jmp __ULDIV__ ; 250 } 251 else version (D_InlineAsm_X86_64) 252 assert(0); 253 else 254 static assert(0); 255 } 256 257 /*************************************** 258 * Compare [EDX,EAX] with [ECX,EBX] 259 * Signed 260 * Returns result in flags 261 */ 262 263 void __LCMP__() 264 { 265 version (D_InlineAsm_X86) 266 asm 267 { 268 naked ; 269 cmp EDX,ECX ; 270 jne C1 ; 271 push EDX ; 272 xor EDX,EDX ; 273 cmp EAX,EBX ; 274 jz C2 ; 275 ja C3 ; 276 dec EDX ; 277 pop EDX ; 278 ret ; 279 280 C3: inc EDX ; 281 C2: pop EDX ; 282 C1: ret ; 283 } 284 else version (D_InlineAsm_X86_64) 285 assert(0); 286 else 287 static assert(0); 288 } 289 290 // Convert ulong to real 291 292 private real adjust = cast(real)0x800_0000_0000_0000 * 0x10; 293 294 real __U64_LDBL() 295 { 296 version (OSX) 297 { 298 version (D_InlineAsm_X86) 299 asm 300 { naked ; 301 push EDX ; 302 push EAX ; 303 and dword ptr 4[ESP], 0x7FFFFFFF ; 304 fild qword ptr [ESP] ; 305 test EDX,EDX ; 306 jns L1 ; 307 push 0x0000403e ; 308 push 0x80000000 ; 309 push 0 ; 310 fld real ptr [ESP] ; // adjust 311 add ESP,12 ; 312 faddp ST(1), ST ; 313 L1: ; 314 add ESP, 8 ; 315 ret ; 316 } 317 else version (D_InlineAsm_X86_64) 318 static assert(0); 319 else 320 static assert(0); 321 } 322 else 323 { 324 version (D_InlineAsm_X86) 325 asm 326 { naked ; 327 push EDX ; 328 push EAX ; 329 and dword ptr 4[ESP], 0x7FFFFFFF ; 330 fild qword ptr [ESP] ; 331 test EDX,EDX ; 332 jns L1 ; 333 fld real ptr adjust ; 334 faddp ST(1), ST ; 335 L1: ; 336 add ESP, 8 ; 337 ret ; 338 } 339 else version (D_InlineAsm_X86_64) 340 asm 341 { naked ; 342 push RAX ; 343 and dword ptr 4[RSP], 0x7FFFFFFF ; 344 fild qword ptr [RSP] ; 345 test RAX,RAX ; 346 jns L1 ; 347 fld real ptr adjust ; 348 faddp ST(1), ST ; 349 L1: ; 350 add RSP, 8 ; 351 ret ; 352 } 353 else 354 static assert(0); 355 } 356 } 357 358 // Same as __U64_LDBL, but return result as double in [EDX,EAX] 359 ulong __ULLNGDBL() 360 { 361 version (D_InlineAsm_X86) 362 asm 363 { naked ; 364 call __U64_LDBL ; 365 sub ESP,8 ; 366 fstp double ptr [ESP] ; 367 pop EAX ; 368 pop EDX ; 369 ret ; 370 } 371 else version (D_InlineAsm_X86_64) 372 asm 373 { naked ; 374 call __U64_LDBL ; 375 sub RSP,8 ; 376 fstp double ptr [RSP] ; 377 pop RAX ; 378 ret ; 379 } 380 else 381 static assert(0); 382 } 383 384 385 // Convert double to ulong 386 387 private short roundTo0 = 0xFBF; 388 389 ulong __DBLULLNG() 390 { 391 // BUG: should handle NAN's and overflows 392 version (OSX) 393 { 394 version (D_InlineAsm_X86) 395 asm 396 { naked ; 397 push 0xFBF ; // roundTo0 398 push 0x0000403e ; 399 push 0x80000000 ; 400 push 0 ; // adjust 401 push EDX ; 402 push EAX ; 403 fld double ptr [ESP] ; 404 sub ESP,8 ; 405 fld real ptr 16[ESP] ; // adjust 406 fcomp ; 407 fstsw AX ; 408 fstcw 8[ESP] ; 409 fldcw 28[ESP] ; // roundTo0 410 sahf ; 411 jae L1 ; 412 fld real ptr 16[ESP] ; // adjust 413 fsubp ST(1), ST ; 414 fistp qword ptr [ESP] ; 415 pop EAX ; 416 pop EDX ; 417 fldcw [ESP] ; 418 add ESP,24 ; 419 add EDX,0x8000_0000 ; 420 ret ; 421 L1: ; 422 fistp qword ptr [ESP] ; 423 pop EAX ; 424 pop EDX ; 425 fldcw [ESP] ; 426 add ESP,24 ; 427 ret ; 428 } 429 else version (D_InlineAsm_X86_64) 430 static assert(0); 431 else 432 static assert(0); 433 } 434 else 435 { 436 version (D_InlineAsm_X86) 437 asm 438 { naked ; 439 push EDX ; 440 push EAX ; 441 fld double ptr [ESP] ; 442 sub ESP,8 ; 443 fld real ptr adjust ; 444 fcomp ; 445 fstsw AX ; 446 fstcw 8[ESP] ; 447 fldcw roundTo0 ; 448 sahf ; 449 jae L1 ; 450 fld real ptr adjust ; 451 fsubp ST(1), ST ; 452 fistp qword ptr [ESP] ; 453 pop EAX ; 454 pop EDX ; 455 fldcw [ESP] ; 456 add ESP,8 ; 457 add EDX,0x8000_0000 ; 458 ret ; 459 L1: ; 460 fistp qword ptr [ESP] ; 461 pop EAX ; 462 pop EDX ; 463 fldcw [ESP] ; 464 add ESP,8 ; 465 ret ; 466 } 467 else version (D_InlineAsm_X86_64) 468 asm 469 { naked ; 470 push RAX ; 471 fld double ptr [RSP] ; 472 sub RSP,8 ; 473 fld real ptr adjust ; 474 fcomp ; 475 fstsw AX ; 476 fstcw 8[RSP] ; 477 fldcw roundTo0 ; 478 sahf ; 479 jae L1 ; 480 fld real ptr adjust ; 481 fsubp ST(1), ST ; 482 fistp qword ptr [RSP] ; 483 pop RAX ; 484 fldcw [RSP] ; 485 add RSP,8 ; 486 mov EDX,0x8000_0000 ; 487 shl RDX,32 ; 488 add RAX,RDX ; 489 ret ; 490 L1: ; 491 fistp qword ptr [RSP] ; 492 pop RAX ; 493 fldcw [RSP] ; 494 add RSP,8 ; 495 ret ; 496 } 497 else 498 static assert(0); 499 } 500 } 501 502 // Convert double in ST0 to uint 503 504 uint __DBLULNG() 505 { 506 // BUG: should handle NAN's and overflows 507 version (OSX) 508 { 509 version (D_InlineAsm_X86) 510 asm 511 { naked ; 512 push 0xFBF ; // roundTo0 513 sub ESP,12 ; 514 fstcw 8[ESP] ; 515 fldcw 12[ESP] ; // roundTo0 516 fistp qword ptr [ESP] ; 517 fldcw 8[ESP] ; 518 pop EAX ; 519 add ESP,12 ; 520 ret ; 521 } 522 else version (D_InlineAsm_X86_64) 523 assert(0); 524 else 525 static assert(0); 526 } 527 else 528 { 529 version (D_InlineAsm_X86) 530 asm 531 { naked ; 532 sub ESP,16 ; 533 fstcw 8[ESP] ; 534 fldcw roundTo0 ; 535 fistp qword ptr [ESP] ; 536 fldcw 8[ESP] ; 537 pop EAX ; 538 add ESP,12 ; 539 ret ; 540 } 541 else version (D_InlineAsm_X86_64) 542 asm 543 { naked ; 544 sub RSP,16 ; 545 fstcw 8[RSP] ; 546 fldcw roundTo0 ; 547 fistp qword ptr [RSP] ; 548 fldcw 8[RSP] ; 549 pop RAX ; 550 add RSP,8 ; 551 ret ; 552 } 553 else 554 static assert(0); 555 } 556 } 557 558 // Convert real in ST0 to ulong 559 560 ulong __LDBLULLNG() 561 { 562 version (OSX) 563 { 564 version (D_InlineAsm_X86) 565 asm 566 { naked ; 567 push 0xFBF ; // roundTo0 568 push 0x0000403e ; 569 push 0x80000000 ; 570 push 0 ; // adjust 571 sub ESP,16 ; 572 fld real ptr 16[ESP] ; // adjust 573 fcomp ; 574 fstsw AX ; 575 fstcw 8[ESP] ; 576 fldcw 28[ESP] ; // roundTo0 577 sahf ; 578 jae L1 ; 579 fld real ptr 16[ESP] ; // adjust 580 fsubp ST(1), ST ; 581 fistp qword ptr [ESP] ; 582 pop EAX ; 583 pop EDX ; 584 fldcw [ESP] ; 585 add ESP,24 ; 586 add EDX,0x8000_0000 ; 587 ret ; 588 L1: ; 589 fistp qword ptr [ESP] ; 590 pop EAX ; 591 pop EDX ; 592 fldcw [ESP] ; 593 add ESP,24 ; 594 ret ; 595 } 596 else version (D_InlineAsm_X86_64) 597 static assert(0); 598 else 599 static assert(0); 600 } 601 else 602 { 603 version (D_InlineAsm_X86) 604 asm 605 { naked ; 606 sub ESP,16 ; 607 fld real ptr adjust ; 608 fcomp ; 609 fstsw AX ; 610 fstcw 8[ESP] ; 611 fldcw roundTo0 ; 612 sahf ; 613 jae L1 ; 614 fld real ptr adjust ; 615 fsubp ST(1), ST ; 616 fistp qword ptr [ESP] ; 617 pop EAX ; 618 pop EDX ; 619 fldcw [ESP] ; 620 add ESP,8 ; 621 add EDX,0x8000_0000 ; 622 ret ; 623 L1: ; 624 fistp qword ptr [ESP] ; 625 pop EAX ; 626 pop EDX ; 627 fldcw [ESP] ; 628 add ESP,8 ; 629 ret ; 630 } 631 else version (D_InlineAsm_X86_64) 632 asm 633 { naked ; 634 sub RSP,16 ; 635 fld real ptr adjust ; 636 fcomp ; 637 fstsw AX ; 638 fstcw 8[RSP] ; 639 fldcw roundTo0 ; 640 sahf ; 641 jae L1 ; 642 fld real ptr adjust ; 643 fsubp ST(1), ST ; 644 fistp qword ptr [RSP] ; 645 pop RAX ; 646 fldcw [RSP] ; 647 add RSP,8 ; 648 mov RCX,0x8000_0000 ; 649 shl RCX,32 ; 650 add RAX,RCX ; 651 ret ; 652 L1: ; 653 fistp qword ptr [RSP] ; 654 pop RAX ; 655 fldcw [RSP] ; 656 add RSP,8 ; 657 ret ; 658 } 659 else 660 static assert(0); 661 } 662 }