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 import core.stdc.stdio; 17 import core.stdc.stdlib; 18 import core.stdc.string; 19 20 import dmd.backend.cdef; 21 import dmd.backend.cc; 22 import dmd.backend.el; 23 import dmd.backend.global; 24 import dmd.backend.code; 25 import dmd.backend.code_x86; 26 import dmd.backend.codebuilder; 27 import dmd.backend.oper; 28 import dmd.backend.symtab; 29 import dmd.backend.ty; 30 import dmd.backend.type; 31 32 import dmd.backend.barray; 33 import dmd.backend.dlist; 34 import dmd.backend.dvec; 35 36 37 nothrow: 38 @safe: 39 40 private __gshared 41 { 42 int nretblocks; 43 44 vec_t[REGMAX] regrange; 45 46 Barray!int weights; 47 } 48 49 @trusted 50 ref int WEIGHTS(int bi, int si) { return weights[bi * globsym.length + si]; } 51 52 /****************************************** 53 */ 54 55 @trusted 56 void cgreg_init() 57 { 58 if (!(config.flags4 & CFG4optimized)) 59 return; 60 61 // Use calloc() instead because sometimes the alloc is too large 62 //printf("1weights: dfo.length = %d, globsym.length = %d\n", dfo.length, globsym.length); 63 weights.setLength(dfo.length * globsym.length); 64 weights[] = 0; 65 66 nretblocks = 0; 67 foreach (bi, b; dfo[]) 68 { 69 if (b.BC == BCret || b.BC == BCretexp) 70 nretblocks++; 71 if (b.Belem) 72 { 73 //printf("b.Bweight = x%x\n",b.Bweight); 74 el_weights(cast(int)bi,b.Belem,b.Bweight); 75 } 76 } 77 memset(regrange.ptr, 0, regrange.sizeof); 78 79 // Make adjustments to symbols we might stick in registers 80 for (size_t i = 0; i < globsym.length; i++) 81 { uint sz; 82 Symbol *s = globsym[i]; 83 84 //printf("considering candidate '%s' for register\n", s.Sident.ptr); 85 86 if (s.Srange) 87 s.Srange = vec_realloc(s.Srange,dfo.length); 88 89 // Determine symbols that are not candidates 90 if (!(s.Sflags & GTregcand) || 91 !s.Srange || 92 (sz = cast(uint)type_size(s.Stype)) == 0 || 93 (tysize(s.ty()) == -1) || 94 (I16 && sz > REGSIZE) || 95 (tyfloating(s.ty()) && !(config.fpxmmregs && tyxmmreg(s.ty()))) 96 ) 97 { 98 debug if (debugr) 99 { 100 printf("not considering variable '%s' for register\n",s.Sident.ptr); 101 if (!(s.Sflags & GTregcand)) 102 printf("\tnot GTregcand\n"); 103 if (!s.Srange) 104 printf("\tno Srange\n"); 105 if (sz == 0) 106 printf("\tsz == 0\n"); 107 if (tysize(s.ty()) == -1) 108 printf("\ttysize\n"); 109 } 110 111 s.Sflags &= ~GTregcand; 112 continue; 113 } 114 115 switch (s.Sclass) 116 { 117 case SC.parameter: 118 // Do not put parameters in registers if they are not used 119 // more than twice (otherwise we have a net loss). 120 if (s.Sweight <= 2 && !tyxmmreg(s.ty())) 121 { 122 debug if (debugr) 123 printf("parameter '%s' weight %d is not enough\n",s.Sident.ptr,s.Sweight); 124 s.Sflags &= ~GTregcand; 125 continue; 126 } 127 break; 128 129 default: 130 break; 131 } 132 133 if (sz == 1) 134 s.Sflags |= GTbyte; 135 136 if (!s.Slvreg) 137 s.Slvreg = vec_calloc(dfo.length); 138 139 //printf("dfo.length = %d, numbits = %d\n",dfo.length,vec_numbits(s.Srange)); 140 assert(vec_numbits(s.Srange) == dfo.length); 141 } 142 } 143 144 /****************************************** 145 */ 146 147 @trusted 148 void cgreg_term() 149 { 150 if (config.flags4 & CFG4optimized) 151 { 152 for (size_t i = 0; i < globsym.length; i++) 153 { 154 Symbol *s = globsym[i]; 155 vec_free(s.Srange); 156 vec_free(s.Slvreg); 157 s.Srange = null; 158 s.Slvreg = null; 159 } 160 161 for (size_t i = 0; i < regrange.length; i++) 162 { 163 if (regrange[i]) 164 { vec_free(regrange[i]); 165 regrange[i] = null; 166 } 167 } 168 169 // weights.dtor(); // save allocation for next time 170 } 171 } 172 173 /********************************* 174 */ 175 176 @trusted 177 void cgreg_reset() 178 { 179 for (size_t j = 0; j < regrange.length; j++) 180 if (!regrange[j]) 181 regrange[j] = vec_calloc(dfo.length); 182 else 183 vec_clear(regrange[j]); 184 } 185 186 /******************************* 187 * Registers used in block bi. 188 */ 189 190 @trusted 191 void cgreg_used(uint bi,regm_t used) 192 { 193 for (size_t j = 0; used; j++) 194 { if (used & 1) // if register j is used 195 vec_setbit(bi,regrange[j]); 196 used >>= 1; 197 } 198 } 199 200 /************************* 201 * Run through a tree calculating symbol weights. 202 */ 203 204 @trusted 205 private void el_weights(int bi,elem *e,uint weight) 206 { 207 while (1) 208 { elem_debug(e); 209 210 int op = e.Eoper; 211 if (!OTleaf(op)) 212 { 213 // This prevents variable references within common subexpressions 214 // from adding to the variable's usage count. 215 if (e.Ecount) 216 { 217 if (e.Ecomsub) 218 weight = 0; 219 else 220 e.Ecomsub = 1; 221 } 222 223 if (OTbinary(op)) 224 { el_weights(bi,e.EV.E2,weight); 225 if ((OTopeq(op) || OTpost(op)) && e.EV.E1.Eoper == OPvar) 226 { 227 if (weight >= 10) 228 weight += 10; 229 else 230 weight++; 231 } 232 } 233 e = e.EV.E1; 234 } 235 else 236 { 237 switch (op) 238 { 239 case OPvar: 240 Symbol *s = e.EV.Vsym; 241 if (s.Ssymnum != SYMIDX.max && s.Sflags & GTregcand) 242 { 243 s.Sweight += weight; 244 //printf("adding %d weight to '%s' (block %d, Ssymnum %d), giving Sweight %d\n",weight,s.Sident.ptr,bi,s.Ssymnum,s.Sweight); 245 if (weights) 246 WEIGHTS(bi,cast(int)s.Ssymnum) += weight; 247 } 248 break; 249 250 default: 251 break; 252 } 253 return; 254 } 255 } 256 } 257 258 /***************************************** 259 * Determine 'benefit' of assigning symbol s to register reg. 260 * Benefit is roughly the number of clocks saved. 261 * A negative value means that s cannot or should not be assigned to reg. 262 */ 263 264 @trusted 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 = cast(int)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 == SC.fastpar || s.Sclass == SC.shadowreg) && 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.length = %d\n", dfo.length, globsym.length); 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 @trusted 534 void cgreg_spillreg_prolog(block *b,Symbol *s,ref CodeBuilder cdbstore,ref CodeBuilder cdbload) 535 { 536 const int bi = b.Bdfoidx; 537 538 //printf("cgreg_spillreg_prolog(block %d, s = '%s')\n",bi,s.Sident.ptr); 539 540 // Load register from s 541 void load() 542 { 543 debug if (debugr) 544 { 545 printf("B%d: prolog moving '%s' into %s:%s\n", 546 bi, s.Sident.ptr, regstring[s.Sregmsw], 547 type_size(s.Stype) > REGSIZE ? regstring[s.Sreglsw] : ""); 548 } 549 gen_spill_reg(cdbload, s, true); 550 } 551 552 // Store register to s 553 void store() 554 { 555 debug if (debugr) 556 { 557 printf("B%d: prolog moving %s into '%s'\n",bi,regstring[s.Sreglsw],s.Sident.ptr); 558 } 559 gen_spill_reg(cdbstore, s, false); 560 } 561 562 const live = vec_testbit(bi,s.Slvreg) != 0; // if s is in a register in block b 563 564 // If it's startblock, and it's a spilled parameter, we 565 // need to load it 566 if (live && s.Sflags & SFLspill && bi == 0 && 567 (s.Sclass == SC.parameter || s.Sclass == SC.fastpar || s.Sclass == SC.shadowreg)) 568 { 569 return load(); 570 } 571 572 if (cgreg_gotoepilog(b,s)) 573 return; 574 575 // Look at predecessors to see if we need to load in/out of register 576 foreach (bl; ListRange(b.Bpred)) 577 { 578 const bpi = list_block(bl).Bdfoidx; 579 580 if (!vec_testbit(bpi,s.Srange)) 581 continue; 582 if (vec_testbit(bpi,s.Slvreg)) 583 { 584 if (!live) 585 { 586 return store(); 587 } 588 } 589 else 590 { 591 if (live) 592 { 593 return load(); 594 } 595 } 596 } 597 } 598 599 /********************************** 600 * Determine block epilog code - it's either 601 * assignments to register, or storing register back in memory. 602 * Params: 603 * b = block to generate prolog code for 604 * s = symbol in the block that may need epilog code 605 * cdbstore = append store code to this 606 * cdbload = append load code to this 607 */ 608 609 @trusted 610 void cgreg_spillreg_epilog(block *b,Symbol *s,ref CodeBuilder cdbstore, ref CodeBuilder cdbload) 611 { 612 const bi = b.Bdfoidx; 613 //printf("cgreg_spillreg_epilog(block %d, s = '%s')\n",bi,s.Sident.ptr); 614 //assert(b.BC == BCgoto); 615 if (!cgreg_gotoepilog(b.nthSucc(0), s)) 616 return; 617 618 const live = vec_testbit(bi,s.Slvreg) != 0; 619 620 // Look at successors to see if we need to load in/out of register 621 foreach (bl; ListRange(b.Bsucc)) 622 { 623 const bpi = list_block(bl).Bdfoidx; 624 if (!vec_testbit(bpi,s.Srange)) 625 continue; 626 if (vec_testbit(bpi,s.Slvreg)) 627 { 628 if (!live) 629 { 630 debug if (debugr) 631 printf("B%d: epilog moving '%s' into %s\n",bi,s.Sident.ptr,regstring[s.Sreglsw]); 632 gen_spill_reg(cdbload, s, true); 633 return; 634 } 635 } 636 else 637 { 638 if (live) 639 { 640 debug if (debugr) 641 printf("B%d: epilog moving %s into '%s'\n",bi,regstring[s.Sreglsw],s.Sident.ptr); 642 gen_spill_reg(cdbstore, s, false); 643 return; 644 } 645 } 646 } 647 } 648 649 /*************************** 650 * Map symbol s into registers [NOREG,reglsw] or [regmsw, reglsw]. 651 */ 652 653 @trusted 654 private void cgreg_map(Symbol *s, reg_t regmsw, reg_t reglsw) 655 { 656 //assert(I64 || reglsw < 8); 657 658 if (vec_disjoint(s.Srange,regrange[reglsw]) && 659 (regmsw == NOREG || vec_disjoint(s.Srange,regrange[regmsw])) 660 ) 661 { 662 s.Sfl = FLreg; 663 vec_copy(s.Slvreg,s.Srange); 664 } 665 else 666 { 667 s.Sflags |= SFLspill; 668 669 // Already computed by cgreg_benefit() 670 //vec_sub(s.Slvreg,s.Srange,regrange[reglsw]); 671 672 if (s.Sfl == FLreg) // if reassigned 673 { 674 switch (s.Sclass) 675 { 676 case SC.auto_: 677 case SC.register: 678 s.Sfl = FLauto; 679 break; 680 case SC.fastpar: 681 s.Sfl = FLfast; 682 break; 683 case SC.bprel: 684 s.Sfl = FLbprel; 685 break; 686 case SC.shadowreg: 687 case SC.parameter: 688 s.Sfl = FLpara; 689 break; 690 case SC.pseudo: 691 s.Sfl = FLpseudo; 692 break; 693 case SC.stack: 694 s.Sfl = FLstack; 695 break; 696 default: 697 symbol_print(s); 698 assert(0); 699 } 700 } 701 } 702 s.Sreglsw = cast(ubyte)reglsw; 703 s.Sregm = (1 << reglsw); 704 mfuncreg &= ~(1 << reglsw); 705 if (regmsw != NOREG) 706 vec_subass(s.Slvreg,regrange[regmsw]); 707 vec_orass(regrange[reglsw],s.Slvreg); 708 709 if (regmsw == NOREG) 710 { 711 debug 712 { 713 if (debugr) 714 { 715 printf("symbol '%s' %s in register %s\n ", 716 s.Sident.ptr, 717 (s.Sflags & SFLspill) ? "spilled".ptr : "put".ptr, 718 regstring[reglsw]); 719 vec_println(s.Slvreg); 720 } 721 } 722 } 723 else 724 { 725 assert(regmsw < 8); 726 s.Sregmsw = cast(ubyte)regmsw; 727 s.Sregm |= 1 << regmsw; 728 mfuncreg &= ~(1 << regmsw); 729 vec_orass(regrange[regmsw],s.Slvreg); 730 731 debug 732 { 733 if (debugr) 734 printf("symbol '%s' %s in register pair %s\n", 735 s.Sident.ptr, 736 (s.Sflags & SFLspill) ? "spilled".ptr : "put".ptr, 737 regm_str(s.Sregm)); 738 } 739 } 740 } 741 742 /******************************************** 743 * The register variables in this mask can not be in registers. 744 * "Unregister" them. 745 */ 746 747 @trusted 748 void cgreg_unregister(regm_t conflict) 749 { 750 if (pass == BackendPass.final_) 751 pass = BackendPass.reg; // have to codegen at least one more time 752 for (int i = 0; i < globsym.length; i++) 753 { Symbol *s = globsym[i]; 754 if (s.Sfl == FLreg && s.Sregm & conflict) 755 { 756 s.Sflags |= GTunregister; 757 } 758 } 759 } 760 761 /****************************************** 762 * Do register assignments. 763 * Returns: 764 * !=0 redo code generation 765 * 0 no more register assignments 766 */ 767 768 struct Reg // data for trial register assignment 769 { 770 Symbol *sym; 771 int benefit; 772 reg_t reglsw; 773 reg_t regmsw; 774 } 775 776 @trusted 777 int cgreg_assign(Symbol *retsym) 778 { 779 int flag = false; // assume no changes 780 781 /* First do any 'unregistering' which might have happened in the last 782 * code gen pass. 783 */ 784 for (size_t si = 0; si < globsym.length; si++) 785 { Symbol *s = globsym[si]; 786 787 if (s.Sflags & GTunregister) 788 { 789 debug if (debugr) 790 { 791 printf("symbol '%s' %s register %s\n ", 792 s.Sident.ptr, 793 (s.Sflags & SFLspill) ? "unspilled".ptr : "unregistered".ptr, 794 regstring[s.Sreglsw]); 795 vec_println(s.Slvreg); 796 } 797 798 flag = true; 799 s.Sflags &= ~(GTregcand | GTunregister | SFLspill); 800 if (s.Sfl == FLreg) 801 { 802 switch (s.Sclass) 803 { 804 case SC.auto_: 805 case SC.register: 806 s.Sfl = FLauto; 807 break; 808 case SC.fastpar: 809 s.Sfl = FLfast; 810 break; 811 case SC.bprel: 812 s.Sfl = FLbprel; 813 break; 814 case SC.shadowreg: 815 case SC.parameter: 816 s.Sfl = FLpara; 817 break; 818 case SC.pseudo: 819 s.Sfl = FLpseudo; 820 break; 821 case SC.stack: 822 s.Sfl = FLstack; 823 break; 824 default: 825 debug symbol_print(s); 826 assert(0); 827 } 828 } 829 } 830 } 831 832 vec_t v = vec_calloc(dfo.length); 833 834 reg_t dst_integer_reg; 835 reg_t dst_float_reg; 836 cgreg_dst_regs(&dst_integer_reg, &dst_float_reg); 837 regm_t dst_integer_mask = 1 << dst_integer_reg; 838 regm_t dst_float_mask = 1 << dst_float_reg; 839 840 /* Find all the parameters passed as named registers 841 */ 842 regm_t regparams = 0; 843 for (size_t si = 0; si < globsym.length; si++) 844 { Symbol *s = globsym[si]; 845 if (s.Sclass == SC.fastpar || s.Sclass == SC.shadowreg) 846 regparams |= s.Spregm(); 847 } 848 849 /* Disallow parameters being put in registers that are used by the 64 bit 850 * prolog generated by prolog_getvarargs() 851 */ 852 const regm_t variadicPrologRegs = (I64 && variadic(funcsym_p.Stype)) 853 ? (mAX | mR11) | // these are used by the prolog code 854 ((mDI | mSI | mDX | mCX | mR8 | mR9 | XMMREGS) & ~regparams) // unnamed register arguments 855 : 0; 856 857 // Find symbol t, which is the most 'deserving' symbol that should be 858 // placed into a register. 859 Reg t; 860 t.sym = null; 861 t.benefit = 0; 862 for (size_t si = 0; si < globsym.length; si++) 863 { Symbol *s = globsym[si]; 864 865 Reg u; 866 u.sym = s; 867 if (!(s.Sflags & GTregcand) || 868 s.Sflags & SFLspill || 869 // Keep trying to reassign retsym into destination register 870 (s.Sfl == FLreg && !(s == retsym && s.Sregm != dst_integer_mask && s.Sregm != dst_float_mask)) 871 ) 872 { 873 debug if (debugr) 874 { 875 if (s.Sfl == FLreg) 876 { 877 printf("symbol '%s' is in reg %s\n",s.Sident.ptr,regm_str(s.Sregm)); 878 } 879 else if (s.Sflags & SFLspill) 880 { 881 printf("symbol '%s' spilled in reg %s\n",s.Sident.ptr,regm_str(s.Sregm)); 882 } 883 else if (!(s.Sflags & GTregcand)) 884 { 885 printf("symbol '%s' is not a reg candidate\n",s.Sident.ptr); 886 } 887 else 888 printf("symbol '%s' is not a candidate\n",s.Sident.ptr); 889 } 890 891 continue; 892 } 893 894 tym_t ty = s.ty(); 895 896 debug 897 { 898 if (debugr) 899 { printf("symbol '%3s', ty x%x weight x%x %s\n ", 900 s.Sident.ptr,ty,s.Sweight, 901 regm_str(s.Spregm())); 902 vec_println(s.Srange); 903 } 904 } 905 906 // Select sequence of registers to try to map s onto 907 const(reg_t)* pseq; // sequence to try for LSW 908 const(reg_t)* pseqmsw = null; // sequence to try for MSW, null if none 909 cgreg_set_priorities(ty, &pseq, &pseqmsw); 910 911 u.benefit = 0; 912 for (int i = 0; pseq[i] != NOREG; i++) 913 { 914 reg_t reg = pseq[i]; 915 916 // Symbols used as return values should only be mapped into return value registers 917 if (s == retsym && !(reg == dst_integer_reg || reg == dst_float_reg)) 918 continue; 919 920 // If BP isn't available, can't assign to it 921 if (reg == BP && !(allregs & mBP)) 922 continue; 923 924 static if (0 && TARGET_LINUX) 925 { 926 // Need EBX for static pointer 927 if (reg == BX && !(allregs & mBX)) 928 continue; 929 } 930 /* Don't enregister any parameters to variadicPrologRegs 931 */ 932 if (variadicPrologRegs & (1 << reg)) 933 { 934 if (s.Sclass == SC.parameter || s.Sclass == SC.fastpar) 935 continue; 936 /* Win64 doesn't use the Posix variadic scheme, so we can skip SCshadowreg 937 */ 938 } 939 940 /* Don't assign register parameter to another register parameter 941 */ 942 if ((s.Sclass == SC.fastpar || s.Sclass == SC.shadowreg) && 943 (1 << reg) & regparams && 944 reg != s.Spreg) 945 continue; 946 947 if (s.Sflags & GTbyte && 948 !((1 << reg) & BYTEREGS)) 949 continue; 950 951 int benefit = cgreg_benefit(s,reg,retsym); 952 953 debug if (debugr) 954 { printf(" %s",regstring[reg]); 955 vec_print(regrange[reg]); 956 printf(" %d\n",benefit); 957 } 958 959 if (benefit > u.benefit) 960 { // successful assigning of lsw 961 reg_t regmsw = NOREG; 962 963 // Now assign MSW 964 if (pseqmsw) 965 { 966 for (uint regj = 0; 1; regj++) 967 { 968 regmsw = pseqmsw[regj]; 969 if (regmsw == NOREG) 970 goto Ltried; // tried and failed to assign MSW 971 if (regmsw == reg) // can't assign msw and lsw to same reg 972 continue; 973 if ((s.Sclass == SC.fastpar || s.Sclass == SC.shadowreg) && 974 (1 << regmsw) & regparams && 975 regmsw != s.Spreg2) 976 continue; 977 978 debug if (debugr) 979 { printf(".%s",regstring[regmsw]); 980 vec_println(regrange[regmsw]); 981 } 982 983 if (vec_disjoint(s.Slvreg,regrange[regmsw])) 984 break; 985 } 986 } 987 vec_copy(v,s.Slvreg); 988 u.benefit = benefit; 989 u.reglsw = reg; 990 u.regmsw = regmsw; 991 } 992 Ltried: 993 } 994 995 if (u.benefit > t.benefit) 996 { t = u; 997 vec_copy(t.sym.Slvreg,v); 998 } 999 } 1000 1001 if (t.sym && t.benefit > 0) 1002 { 1003 cgreg_map(t.sym,t.regmsw,t.reglsw); 1004 flag = true; 1005 } 1006 1007 /* See if any scratch registers have become available that we can use. 1008 * Scratch registers are cheaper, as they don't need save/restore. 1009 * All floating point registers are scratch registers, so no need 1010 * to do this for them. 1011 */ 1012 if ((I32 || I64) && // not worth the bother for 16 bit code 1013 !flag && // if haven't already assigned registers in this pass 1014 (mfuncreg & ~fregsaved) & ALLREGS && // if unused non-floating scratch registers 1015 !(funcsym_p.Sflags & SFLexit)) // don't need save/restore if function never returns 1016 { 1017 for (size_t si = 0; si < globsym.length; si++) 1018 { Symbol *s = globsym[si]; 1019 1020 if (s.Sfl == FLreg && // if assigned to register 1021 (1 << s.Sreglsw) & fregsaved && // and that register is not scratch 1022 type_size(s.Stype) <= REGSIZE && // don't bother with register pairs 1023 !tyfloating(s.ty())) // don't assign floating regs to non-floating regs 1024 { 1025 s.Sreglsw = findreg((mfuncreg & ~fregsaved) & ALLREGS); 1026 s.Sregm = 1 << s.Sreglsw; 1027 flag = true; 1028 1029 debug if (debugr) 1030 printf("re-assigned '%s' to %s\n",s.Sident.ptr,regstring[s.Sreglsw]); 1031 1032 break; 1033 } 1034 } 1035 } 1036 vec_free(v); 1037 1038 return flag; 1039 }