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