1 /** 2 * Compiler implementation of the 3 * $(LINK2 http://www.dlang.org, D programming language). 4 * 5 * Copyright: Copyright (C) 1985-1998 by Symantec 6 * Copyright (C) 2000-2020 by The D Language Foundation, All Rights Reserved 7 * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright) 8 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 9 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/backend/cgreg.c, backend/cgreg.d) 10 */ 11 12 module dmd.backend.cgreg; 13 14 version (SCPP) 15 version = COMPILE; 16 version (MARS) 17 version = COMPILE; 18 19 version (COMPILE) 20 { 21 22 import core.stdc.stdio; 23 import core.stdc.stdlib; 24 import core.stdc.string; 25 26 import dmd.backend.cdef; 27 import dmd.backend.cc; 28 import dmd.backend.el; 29 import dmd.backend.global; 30 import dmd.backend.code; 31 import dmd.backend.code_x86; 32 import dmd.backend.codebuilder; 33 import dmd.backend.oper; 34 import dmd.backend.ty; 35 import dmd.backend.type; 36 37 import dmd.backend.barray; 38 import dmd.backend.dlist; 39 import dmd.backend.dvec; 40 41 extern (C++): 42 43 nothrow: 44 45 int REGSIZE(); 46 47 private __gshared 48 { 49 int nretblocks; 50 51 vec_t[REGMAX] regrange; 52 53 Barray!int weights; 54 } 55 56 ref int WEIGHTS(int bi, int si) { return weights[bi * globsym.top + si]; } 57 58 /****************************************** 59 */ 60 61 void cgreg_init() 62 { 63 if (!(config.flags4 & CFG4optimized)) 64 return; 65 66 // Use calloc() instead because sometimes the alloc is too large 67 //printf("1weights: dfo.length = %d, globsym.top = %d\n", dfo.length, globsym.top); 68 weights.setLength(dfo.length * globsym.top); 69 weights[] = 0; 70 71 nretblocks = 0; 72 foreach (bi, b; dfo[]) 73 { 74 if (b.BC == BCret || b.BC == BCretexp) 75 nretblocks++; 76 if (b.Belem) 77 { 78 //printf("b.Bweight = x%x\n",b.Bweight); 79 el_weights(cast(int)bi,b.Belem,b.Bweight); 80 } 81 } 82 memset(regrange.ptr, 0, regrange.sizeof); 83 84 // Make adjustments to symbols we might stick in registers 85 for (size_t i = 0; i < globsym.top; i++) 86 { uint sz; 87 Symbol *s = globsym.tab[i]; 88 89 //printf("considering candidate '%s' for register\n",s.Sident); 90 91 if (s.Srange) 92 s.Srange = vec_realloc(s.Srange,dfo.length); 93 94 // Determine symbols that are not candidates 95 if (!(s.Sflags & GTregcand) || 96 !s.Srange || 97 (sz = cast(uint)type_size(s.Stype)) == 0 || 98 (tysize(s.ty()) == -1) || 99 (I16 && sz > REGSIZE) || 100 (tyfloating(s.ty()) && !(config.fpxmmregs && tyxmmreg(s.ty()))) 101 ) 102 { 103 debug if (debugr) 104 { 105 printf("not considering variable '%s' for register\n",s.Sident.ptr); 106 if (!(s.Sflags & GTregcand)) 107 printf("\tnot GTregcand\n"); 108 if (!s.Srange) 109 printf("\tno Srange\n"); 110 if (sz == 0) 111 printf("\tsz == 0\n"); 112 if (tysize(s.ty()) == -1) 113 printf("\ttysize\n"); 114 } 115 116 s.Sflags &= ~GTregcand; 117 continue; 118 } 119 120 switch (s.Sclass) 121 { 122 case SCparameter: 123 // Do not put parameters in registers if they are not used 124 // more than twice (otherwise we have a net loss). 125 if (s.Sweight <= 2 && !tyxmmreg(s.ty())) 126 { 127 debug if (debugr) 128 printf("parameter '%s' weight %d is not enough\n",s.Sident.ptr,s.Sweight); 129 s.Sflags &= ~GTregcand; 130 continue; 131 } 132 break; 133 134 default: 135 break; 136 } 137 138 if (sz == 1) 139 s.Sflags |= GTbyte; 140 141 if (!s.Slvreg) 142 s.Slvreg = vec_calloc(dfo.length); 143 144 //printf("dfo.length = %d, numbits = %d\n",dfo.length,vec_numbits(s.Srange)); 145 assert(vec_numbits(s.Srange) == dfo.length); 146 } 147 } 148 149 /****************************************** 150 */ 151 152 void cgreg_term() 153 { 154 if (config.flags4 & CFG4optimized) 155 { 156 for (size_t i = 0; i < globsym.top; i++) 157 { 158 Symbol *s = globsym.tab[i]; 159 vec_free(s.Srange); 160 vec_free(s.Slvreg); 161 s.Srange = null; 162 s.Slvreg = null; 163 } 164 165 for (size_t i = 0; i < regrange.length; i++) 166 { 167 if (regrange[i]) 168 { vec_free(regrange[i]); 169 regrange[i] = null; 170 } 171 } 172 173 // weights.dtor(); // save allocation for next time 174 } 175 } 176 177 /********************************* 178 */ 179 180 void cgreg_reset() 181 { 182 for (size_t j = 0; j < regrange.length; j++) 183 if (!regrange[j]) 184 regrange[j] = vec_calloc(dfo.length); 185 else 186 vec_clear(regrange[j]); 187 } 188 189 /******************************* 190 * Registers used in block bi. 191 */ 192 193 void cgreg_used(uint bi,regm_t used) 194 { 195 for (size_t j = 0; used; j++) 196 { if (used & 1) // if register j is used 197 vec_setbit(bi,regrange[j]); 198 used >>= 1; 199 } 200 } 201 202 /************************* 203 * Run through a tree calculating symbol weights. 204 */ 205 206 private void el_weights(int bi,elem *e,uint weight) 207 { 208 while (1) 209 { elem_debug(e); 210 211 int op = e.Eoper; 212 if (!OTleaf(op)) 213 { 214 // This prevents variable references within common subexpressions 215 // from adding to the variable's usage count. 216 if (e.Ecount) 217 { 218 if (e.Ecomsub) 219 weight = 0; 220 else 221 e.Ecomsub = 1; 222 } 223 224 if (OTbinary(op)) 225 { el_weights(bi,e.EV.E2,weight); 226 if ((OTopeq(op) || OTpost(op)) && e.EV.E1.Eoper == OPvar) 227 { 228 if (weight >= 10) 229 weight += 10; 230 else 231 weight++; 232 } 233 } 234 e = e.EV.E1; 235 } 236 else 237 { 238 switch (op) 239 { 240 case OPvar: 241 Symbol *s = e.EV.Vsym; 242 if (s.Ssymnum != -1 && s.Sflags & GTregcand) 243 { 244 s.Sweight += weight; 245 //printf("adding %d weight to '%s' (block %d, Ssymnum %d), giving Sweight %d\n",weight,s.Sident.ptr,bi,s.Ssymnum,s.Sweight); 246 if (weights) 247 WEIGHTS(bi,s.Ssymnum) += weight; 248 } 249 break; 250 251 default: 252 break; 253 } 254 return; 255 } 256 } 257 } 258 259 /***************************************** 260 * Determine 'benefit' of assigning symbol s to register reg. 261 * Benefit is roughly the number of clocks saved. 262 * A negative value means that s cannot or should not be assigned to reg. 263 */ 264 265 private int cgreg_benefit(Symbol *s, reg_t reg, Symbol *retsym) 266 { 267 int benefit; 268 int benefit2; 269 block *b; 270 int bi; 271 int gotoepilog; 272 int retsym_cnt; 273 274 //printf("cgreg_benefit(s = '%s', reg = %d)\n", s.Sident.ptr, reg); 275 276 vec_sub(s.Slvreg,s.Srange,regrange[reg]); 277 int si = s.Ssymnum; 278 279 reg_t dst_integer_reg; 280 reg_t dst_float_reg; 281 cgreg_dst_regs(&dst_integer_reg, &dst_float_reg); 282 283 Lagain: 284 //printf("again\n"); 285 benefit = 0; 286 retsym_cnt = 0; 287 288 static if (1) // causes assert failure in std.range(4488) from std.parallelism's unit tests 289 { 290 // (it works now - but keep an eye on it for the moment) 291 // If s is passed in a register to the function, favor that register 292 if ((s.Sclass == SCfastpar || s.Sclass == SCshadowreg) && s.Spreg == reg) 293 ++benefit; 294 } 295 296 // Make sure we have enough uses to justify 297 // using a register we must save 298 if (fregsaved & (1 << reg) & mfuncreg) 299 benefit -= 1 + nretblocks; 300 301 for (bi = 0; (bi = cast(uint) vec_index(bi, s.Srange)) < dfo.length; ++bi) 302 { int inoutp; 303 int inout_; 304 305 b = dfo[bi]; 306 switch (b.BC) 307 { 308 case BCjcatch: 309 case BCcatch: 310 case BC_except: 311 case BC_finally: 312 case BC_lpad: 313 case BC_ret: 314 s.Sflags &= ~GTregcand; 315 goto Lcant; // can't assign to register 316 317 default: 318 break; 319 } 320 if (vec_testbit(bi,s.Slvreg)) 321 { benefit += WEIGHTS(bi,si); 322 //printf("WEIGHTS(%d,%d) = %d, benefit = %d\n",bi,si,WEIGHTS(bi,si),benefit); 323 inout_ = 1; 324 325 if (s == retsym && (reg == dst_integer_reg || reg == dst_float_reg) && b.BC == BCretexp) 326 { benefit += 1; 327 retsym_cnt++; 328 //printf("retsym, benefit = %d\n",benefit); 329 if (s.Sfl == FLreg && !vec_disjoint(s.Srange,regrange[reg])) 330 goto Lcant; // don't spill if already in register 331 } 332 } 333 else 334 inout_ = -1; 335 336 // Look at predecessors to see if we need to load in/out of register 337 gotoepilog = 0; 338 L2: 339 inoutp = 0; 340 benefit2 = 0; 341 foreach (bl; ListRange(b.Bpred)) 342 { 343 block *bp = list_block(bl); 344 int bpi = bp.Bdfoidx; 345 if (!vec_testbit(bpi,s.Srange)) 346 continue; 347 if (gotoepilog && bp.BC == BCgoto) 348 { 349 if (vec_testbit(bpi,s.Slvreg)) 350 { 351 if (inout_ == -1) 352 benefit2 -= bp.Bweight; // need to mov into mem 353 } 354 else 355 { 356 if (inout_ == 1) 357 benefit2 -= bp.Bweight; // need to mov into reg 358 } 359 } 360 else if (vec_testbit(bpi,s.Slvreg)) 361 { 362 switch (inoutp) 363 { 364 case 0: 365 inoutp = 1; 366 if (inout_ != 1) 367 { if (gotoepilog) 368 { vec_clearbit(bpi,s.Slvreg); 369 goto Lagain; 370 } 371 benefit2 -= b.Bweight; // need to mov into mem 372 } 373 break; 374 case 1: 375 break; 376 case -1: 377 if (gotoepilog == 0) 378 { gotoepilog = 1; 379 goto L2; 380 } 381 vec_clearbit(bpi,s.Slvreg); 382 goto Lagain; 383 384 default: 385 assert(0); 386 } 387 } 388 else 389 { 390 switch (inoutp) 391 { 392 case 0: 393 inoutp = -1; 394 if (inout_ != -1) 395 { if (gotoepilog) 396 { vec_clearbit(bi,s.Slvreg); 397 goto Lagain; 398 } 399 benefit2 -= b.Bweight; // need to mov into reg 400 } 401 break; 402 case 1: 403 if (gotoepilog == 0) 404 { gotoepilog = 1; 405 goto L2; 406 } 407 if (inout_ == 1) 408 { vec_clearbit(bi,s.Slvreg); 409 goto Lagain; 410 } 411 goto Lcant; 412 case -1: 413 break; 414 415 default: 416 assert(0); 417 } 418 } 419 } 420 //printf("benefit2 = %d\n", benefit2); 421 benefit += benefit2; 422 } 423 424 //printf("2weights: dfo.length = %d, globsym.top = %d\n", dfo.length, globsym.top); 425 debug if (benefit > s.Sweight + retsym_cnt + 1) 426 printf("s = '%s', benefit = %d, Sweight = %d, retsym_cnt = x%x\n",s.Sident.ptr,benefit,s.Sweight, retsym_cnt); 427 428 /* This can happen upon overflow of s.Sweight, but only in extreme cases such as 429 * issues.dlang.org/show_bug.cgi?id=17098 430 * It essentially means "a whole lotta uses in nested loops", where 431 * it should go into a register anyway. So just saturate it at int.max 432 */ 433 //assert(benefit <= s.Sweight + retsym_cnt + 1); 434 if (benefit > s.Sweight + retsym_cnt + 1) 435 benefit = int.max; // saturate instead of overflow error 436 return benefit; 437 438 Lcant: 439 return -1; // can't assign to reg 440 } 441 442 /********************************************* 443 * Determine if block gets symbol loaded by predecessor epilog (1), 444 * or by prolog (0). 445 */ 446 447 int cgreg_gotoepilog(block *b,Symbol *s) 448 { 449 int bi = b.Bdfoidx; 450 451 int inout_; 452 if (vec_testbit(bi,s.Slvreg)) 453 inout_ = 1; 454 else 455 inout_ = -1; 456 457 // Look at predecessors to see if we need to load in/out of register 458 int gotoepilog = 0; 459 int inoutp = 0; 460 foreach (bl; ListRange(b.Bpred)) 461 { 462 block *bp = list_block(bl); 463 int bpi = bp.Bdfoidx; 464 if (!vec_testbit(bpi,s.Srange)) 465 continue; 466 if (vec_testbit(bpi,s.Slvreg)) 467 { 468 switch (inoutp) 469 { 470 case 0: 471 inoutp = 1; 472 if (inout_ != 1) 473 { if (gotoepilog) 474 goto Lcant; 475 } 476 break; 477 case 1: 478 break; 479 case -1: 480 if (gotoepilog == 0) 481 { gotoepilog = 1; 482 goto Lret; 483 } 484 goto Lcant; 485 486 default: 487 assert(0); 488 } 489 } 490 else 491 { 492 switch (inoutp) 493 { 494 case 0: 495 inoutp = -1; 496 if (inout_ != -1) 497 { if (gotoepilog) 498 goto Lcant; 499 } 500 break; 501 case 1: 502 if (gotoepilog == 0) 503 { gotoepilog = 1; 504 goto Lret; 505 } 506 goto Lcant; 507 case -1: 508 break; 509 510 default: 511 assert(0); 512 } 513 } 514 } 515 Lret: 516 return gotoepilog; 517 518 Lcant: 519 assert(0); 520 // return -1; // can't assign to reg 521 } 522 523 /********************************** 524 * Determine block prolog code for `s` - it's either 525 * assignments to register, or storing register back in memory. 526 * Params: 527 * b = block to generate prolog code for 528 * s = symbol in the block that may need prolog code 529 * cdbstore = append store code to this 530 * cdbload = append load code to this 531 */ 532 533 void cgreg_spillreg_prolog(block *b,Symbol *s,ref CodeBuilder cdbstore,ref CodeBuilder cdbload) 534 { 535 const int bi = b.Bdfoidx; 536 537 //printf("cgreg_spillreg_prolog(block %d, s = '%s')\n",bi,s.Sident.ptr); 538 539 // Load register from s 540 void load() 541 { 542 debug if (debugr) 543 { 544 printf("B%d: prolog moving '%s' into %s:%s\n", 545 bi, s.Sident.ptr, regstring[s.Sregmsw], 546 type_size(s.Stype) > REGSIZE ? regstring[s.Sreglsw] : ""); 547 } 548 gen_spill_reg(cdbload, s, true); 549 } 550 551 // Store register to s 552 void store() 553 { 554 debug if (debugr) 555 { 556 printf("B%d: prolog moving %s into '%s'\n",bi,regstring[s.Sreglsw],s.Sident.ptr); 557 } 558 gen_spill_reg(cdbstore, s, false); 559 } 560 561 const live = vec_testbit(bi,s.Slvreg) != 0; // if s is in a register in block b 562 563 // If it's startblock, and it's a spilled parameter, we 564 // need to load it 565 if (live && s.Sflags & SFLspill && bi == 0 && 566 (s.Sclass == SCparameter || s.Sclass == SCfastpar || s.Sclass == SCshadowreg)) 567 { 568 return load(); 569 } 570 571 if (cgreg_gotoepilog(b,s)) 572 return; 573 574 // Look at predecessors to see if we need to load in/out of register 575 foreach (bl; ListRange(b.Bpred)) 576 { 577 const bpi = list_block(bl).Bdfoidx; 578 579 if (!vec_testbit(bpi,s.Srange)) 580 continue; 581 if (vec_testbit(bpi,s.Slvreg)) 582 { 583 if (!live) 584 { 585 return store(); 586 } 587 } 588 else 589 { 590 if (live) 591 { 592 return load(); 593 } 594 } 595 } 596 } 597 598 /********************************** 599 * Determine block epilog code - it's either 600 * assignments to register, or storing register back in memory. 601 * Params: 602 * b = block to generate prolog code for 603 * s = symbol in the block that may need epilog code 604 * cdbstore = append store code to this 605 * cdbload = append load code to this 606 */ 607 608 void cgreg_spillreg_epilog(block *b,Symbol *s,ref CodeBuilder cdbstore, ref CodeBuilder cdbload) 609 { 610 const bi = b.Bdfoidx; 611 //printf("cgreg_spillreg_epilog(block %d, s = '%s')\n",bi,s.Sident.ptr); 612 //assert(b.BC == BCgoto); 613 if (!cgreg_gotoepilog(b.nthSucc(0), s)) 614 return; 615 616 const live = vec_testbit(bi,s.Slvreg) != 0; 617 618 // Look at successors to see if we need to load in/out of register 619 foreach (bl; ListRange(b.Bsucc)) 620 { 621 const bpi = list_block(bl).Bdfoidx; 622 if (!vec_testbit(bpi,s.Srange)) 623 continue; 624 if (vec_testbit(bpi,s.Slvreg)) 625 { 626 if (!live) 627 { 628 debug if (debugr) 629 printf("B%d: epilog moving '%s' into %s\n",bi,s.Sident.ptr,regstring[s.Sreglsw]); 630 gen_spill_reg(cdbload, s, true); 631 return; 632 } 633 } 634 else 635 { 636 if (live) 637 { 638 debug if (debugr) 639 printf("B%d: epilog moving %s into '%s'\n",bi,regstring[s.Sreglsw],s.Sident.ptr); 640 gen_spill_reg(cdbstore, s, false); 641 return; 642 } 643 } 644 } 645 } 646 647 /*************************** 648 * Map symbol s into registers [NOREG,reglsw] or [regmsw, reglsw]. 649 */ 650 651 private void cgreg_map(Symbol *s, reg_t regmsw, reg_t reglsw) 652 { 653 //assert(I64 || reglsw < 8); 654 655 if (vec_disjoint(s.Srange,regrange[reglsw]) && 656 (regmsw == NOREG || vec_disjoint(s.Srange,regrange[regmsw])) 657 ) 658 { 659 s.Sfl = FLreg; 660 vec_copy(s.Slvreg,s.Srange); 661 } 662 else 663 { 664 s.Sflags |= SFLspill; 665 666 // Already computed by cgreg_benefit() 667 //vec_sub(s.Slvreg,s.Srange,regrange[reglsw]); 668 669 if (s.Sfl == FLreg) // if reassigned 670 { 671 switch (s.Sclass) 672 { 673 case SCauto: 674 case SCregister: 675 s.Sfl = FLauto; 676 break; 677 case SCfastpar: 678 s.Sfl = FLfast; 679 break; 680 case SCbprel: 681 s.Sfl = FLbprel; 682 break; 683 case SCshadowreg: 684 case SCparameter: 685 s.Sfl = FLpara; 686 break; 687 case SCpseudo: 688 s.Sfl = FLpseudo; 689 break; 690 case SCstack: 691 s.Sfl = FLstack; 692 break; 693 default: 694 symbol_print(s); 695 assert(0); 696 } 697 } 698 } 699 s.Sreglsw = cast(ubyte)reglsw; 700 s.Sregm = (1 << reglsw); 701 mfuncreg &= ~(1 << reglsw); 702 if (regmsw != NOREG) 703 vec_subass(s.Slvreg,regrange[regmsw]); 704 vec_orass(regrange[reglsw],s.Slvreg); 705 706 if (regmsw == NOREG) 707 { 708 debug 709 { 710 if (debugr) 711 { 712 printf("symbol '%s' %s in register %s\n ", 713 s.Sident.ptr, 714 (s.Sflags & SFLspill) ? "spilled".ptr : "put".ptr, 715 regstring[reglsw]); 716 vec_println(s.Slvreg); 717 } 718 } 719 } 720 else 721 { 722 assert(regmsw < 8); 723 s.Sregmsw = cast(ubyte)regmsw; 724 s.Sregm |= 1 << regmsw; 725 mfuncreg &= ~(1 << regmsw); 726 vec_orass(regrange[regmsw],s.Slvreg); 727 728 debug 729 { 730 if (debugr) 731 printf("symbol '%s' %s in register pair %s\n", 732 s.Sident.ptr, 733 (s.Sflags & SFLspill) ? "spilled".ptr : "put".ptr, 734 regm_str(s.Sregm)); 735 } 736 } 737 } 738 739 /******************************************** 740 * The register variables in this mask can not be in registers. 741 * "Unregister" them. 742 */ 743 744 void cgreg_unregister(regm_t conflict) 745 { 746 if (pass == PASSfinal) 747 pass = PASSreg; // have to codegen at least one more time 748 for (int i = 0; i < globsym.top; i++) 749 { Symbol *s = globsym.tab[i]; 750 if (s.Sfl == FLreg && s.Sregm & conflict) 751 { 752 s.Sflags |= GTunregister; 753 } 754 } 755 } 756 757 /****************************************** 758 * Do register assignments. 759 * Returns: 760 * !=0 redo code generation 761 * 0 no more register assignments 762 */ 763 764 struct Reg // data for trial register assignment 765 { 766 Symbol *sym; 767 int benefit; 768 reg_t reglsw; 769 reg_t regmsw; 770 } 771 772 int cgreg_assign(Symbol *retsym) 773 { 774 int flag = false; // assume no changes 775 776 /* First do any 'unregistering' which might have happened in the last 777 * code gen pass. 778 */ 779 for (size_t si = 0; si < globsym.top; si++) 780 { Symbol *s = globsym.tab[si]; 781 782 if (s.Sflags & GTunregister) 783 { 784 debug if (debugr) 785 { 786 printf("symbol '%s' %s register %s\n ", 787 s.Sident.ptr, 788 (s.Sflags & SFLspill) ? "unspilled".ptr : "unregistered".ptr, 789 regstring[s.Sreglsw]); 790 vec_println(s.Slvreg); 791 } 792 793 flag = true; 794 s.Sflags &= ~(GTregcand | GTunregister | SFLspill); 795 if (s.Sfl == FLreg) 796 { 797 switch (s.Sclass) 798 { 799 case SCauto: 800 case SCregister: 801 s.Sfl = FLauto; 802 break; 803 case SCfastpar: 804 s.Sfl = FLfast; 805 break; 806 case SCbprel: 807 s.Sfl = FLbprel; 808 break; 809 case SCshadowreg: 810 case SCparameter: 811 s.Sfl = FLpara; 812 break; 813 case SCpseudo: 814 s.Sfl = FLpseudo; 815 break; 816 case SCstack: 817 s.Sfl = FLstack; 818 break; 819 default: 820 debug symbol_print(s); 821 assert(0); 822 } 823 } 824 } 825 } 826 827 vec_t v = vec_calloc(dfo.length); 828 829 reg_t dst_integer_reg; 830 reg_t dst_float_reg; 831 cgreg_dst_regs(&dst_integer_reg, &dst_float_reg); 832 regm_t dst_integer_mask = 1 << dst_integer_reg; 833 regm_t dst_float_mask = 1 << dst_float_reg; 834 835 /* Find all the parameters passed as named registers 836 */ 837 regm_t regparams = 0; 838 for (size_t si = 0; si < globsym.top; si++) 839 { Symbol *s = globsym.tab[si]; 840 if (s.Sclass == SCfastpar || s.Sclass == SCshadowreg) 841 regparams |= s.Spregm(); 842 } 843 844 /* Disallow parameters being put in registers that are used by the 64 bit 845 * prolog generated by prolog_getvarargs() 846 */ 847 const regm_t variadicPrologRegs = (I64 && variadic(funcsym_p.Stype)) 848 ? (mAX | mR11) | // these are used by the prolog code 849 ((mDI | mSI | mDX | mCX | mR8 | mR9 | XMMREGS) & ~regparams) // unnamed register arguments 850 : 0; 851 852 // Find symbol t, which is the most 'deserving' symbol that should be 853 // placed into a register. 854 Reg t; 855 t.sym = null; 856 t.benefit = 0; 857 for (size_t si = 0; si < globsym.top; si++) 858 { Symbol *s = globsym.tab[si]; 859 860 Reg u; 861 u.sym = s; 862 if (!(s.Sflags & GTregcand) || 863 s.Sflags & SFLspill || 864 // Keep trying to reassign retsym into destination register 865 (s.Sfl == FLreg && !(s == retsym && s.Sregm != dst_integer_mask && s.Sregm != dst_float_mask)) 866 ) 867 { 868 debug if (debugr) 869 { 870 if (s.Sfl == FLreg) 871 { 872 printf("symbol '%s' is in reg %s\n",s.Sident.ptr,regm_str(s.Sregm)); 873 } 874 else if (s.Sflags & SFLspill) 875 { 876 printf("symbol '%s' spilled in reg %s\n",s.Sident.ptr,regm_str(s.Sregm)); 877 } 878 else if (!(s.Sflags & GTregcand)) 879 { 880 printf("symbol '%s' is not a reg candidate\n",s.Sident.ptr); 881 } 882 else 883 printf("symbol '%s' is not a candidate\n",s.Sident.ptr); 884 } 885 886 continue; 887 } 888 889 tym_t ty = s.ty(); 890 891 debug 892 { 893 if (debugr) 894 { printf("symbol '%3s', ty x%x weight x%x %s\n ", 895 s.Sident.ptr,ty,s.Sweight, 896 regm_str(s.Spregm())); 897 vec_println(s.Srange); 898 } 899 } 900 901 // Select sequence of registers to try to map s onto 902 const(reg_t)* pseq; // sequence to try for LSW 903 const(reg_t)* pseqmsw = null; // sequence to try for MSW, null if none 904 cgreg_set_priorities(ty, &pseq, &pseqmsw); 905 906 u.benefit = 0; 907 for (int i = 0; pseq[i] != NOREG; i++) 908 { 909 reg_t reg = pseq[i]; 910 911 // Symbols used as return values should only be mapped into return value registers 912 if (s == retsym && !(reg == dst_integer_reg || reg == dst_float_reg)) 913 continue; 914 915 // If BP isn't available, can't assign to it 916 if (reg == BP && !(allregs & mBP)) 917 continue; 918 919 static if (0 && TARGET_LINUX) 920 { 921 // Need EBX for static pointer 922 if (reg == BX && !(allregs & mBX)) 923 continue; 924 } 925 /* Don't enregister any parameters to variadicPrologRegs 926 */ 927 if (variadicPrologRegs & (1 << reg)) 928 { 929 if (s.Sclass == SCparameter || s.Sclass == SCfastpar) 930 continue; 931 /* Win64 doesn't use the Posix variadic scheme, so we can skip SCshadowreg 932 */ 933 } 934 935 /* Don't assign register parameter to another register parameter 936 */ 937 if ((s.Sclass == SCfastpar || s.Sclass == SCshadowreg) && 938 (1 << reg) & regparams && 939 reg != s.Spreg) 940 continue; 941 942 if (s.Sflags & GTbyte && 943 !((1 << reg) & BYTEREGS)) 944 continue; 945 946 int benefit = cgreg_benefit(s,reg,retsym); 947 948 debug if (debugr) 949 { printf(" %s",regstring[reg]); 950 vec_print(regrange[reg]); 951 printf(" %d\n",benefit); 952 } 953 954 if (benefit > u.benefit) 955 { // successful assigning of lsw 956 reg_t regmsw = NOREG; 957 958 // Now assign MSW 959 if (pseqmsw) 960 { 961 for (uint regj = 0; 1; regj++) 962 { 963 regmsw = pseqmsw[regj]; 964 if (regmsw == NOREG) 965 goto Ltried; // tried and failed to assign MSW 966 if (regmsw == reg) // can't assign msw and lsw to same reg 967 continue; 968 if ((s.Sclass == SCfastpar || s.Sclass == SCshadowreg) && 969 (1 << regmsw) & regparams && 970 regmsw != s.Spreg2) 971 continue; 972 973 debug if (debugr) 974 { printf(".%s",regstring[regmsw]); 975 vec_println(regrange[regmsw]); 976 } 977 978 if (vec_disjoint(s.Slvreg,regrange[regmsw])) 979 break; 980 } 981 } 982 vec_copy(v,s.Slvreg); 983 u.benefit = benefit; 984 u.reglsw = reg; 985 u.regmsw = regmsw; 986 } 987 Ltried: 988 } 989 990 if (u.benefit > t.benefit) 991 { t = u; 992 vec_copy(t.sym.Slvreg,v); 993 } 994 } 995 996 if (t.sym && t.benefit > 0) 997 { 998 cgreg_map(t.sym,t.regmsw,t.reglsw); 999 flag = true; 1000 } 1001 1002 /* See if any scratch registers have become available that we can use. 1003 * Scratch registers are cheaper, as they don't need save/restore. 1004 * All floating point registers are scratch registers, so no need 1005 * to do this for them. 1006 */ 1007 if ((I32 || I64) && // not worth the bother for 16 bit code 1008 !flag && // if haven't already assigned registers in this pass 1009 (mfuncreg & ~fregsaved) & ALLREGS && // if unused non-floating scratch registers 1010 !(funcsym_p.Sflags & SFLexit)) // don't need save/restore if function never returns 1011 { 1012 for (size_t si = 0; si < globsym.top; si++) 1013 { Symbol *s = globsym.tab[si]; 1014 1015 if (s.Sfl == FLreg && // if assigned to register 1016 (1 << s.Sreglsw) & fregsaved && // and that register is not scratch 1017 type_size(s.Stype) <= REGSIZE && // don't bother with register pairs 1018 !tyfloating(s.ty())) // don't assign floating regs to non-floating regs 1019 { 1020 s.Sreglsw = findreg((mfuncreg & ~fregsaved) & ALLREGS); 1021 s.Sregm = 1 << s.Sreglsw; 1022 flag = true; 1023 1024 debug if (debugr) 1025 printf("re-assigned '%s' to %s\n",s.Sident.ptr,regstring[s.Sreglsw]); 1026 1027 break; 1028 } 1029 } 1030 } 1031 vec_free(v); 1032 1033 return flag; 1034 } 1035 1036 }