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 }