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