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