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