1 /**
2  * Transition from intermediate representation to code generator
3  *
4  * Compiler implementation of the
5  * $(LINK2 https://www.dlang.org, D programming language).
6  *
7  * Copyright:   Copyright (C) 1984-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/out.d, backend/out.d)
12  */
13 
14 module dmd.backend.dout;
15 
16 import core.stdc.stdio;
17 import core.stdc.string;
18 
19 import dmd.backend.cc;
20 import dmd.backend.cdef;
21 import dmd.backend.cgcv;
22 import dmd.backend.code;
23 import dmd.backend.code_x86;
24 import dmd.backend.cv4;
25 import dmd.backend.dt;
26 import dmd.backend.dlist;
27 import dmd.backend.mem;
28 import dmd.backend.el;
29 import dmd.backend.global;
30 import dmd.backend.goh;
31 import dmd.backend.inliner;
32 import dmd.backend.obj;
33 import dmd.backend.oper;
34 import dmd.backend.rtlsym;
35 import dmd.backend.symtab;
36 import dmd.backend.ty;
37 import dmd.backend.type;
38 
39 import dmd.backend.barray;
40 
41 version (Windows)
42 {
43     extern (C)
44     {
45         int stricmp(const(char)*, const(char)*) pure nothrow @nogc;
46         int memicmp(const(void)*, const(void)*, size_t) pure nothrow @nogc;
47     }
48 }
49 
50 
51 nothrow:
52 @safe:
53 
54 // Determine if this Symbol is stored in a COMDAT
55 @trusted
56 bool symbol_iscomdat2(Symbol* s)
57 {
58     return s.Sclass == SC.comdat ||
59         config.flags2 & CFG2comdat && s.Sclass == SC.inline ||
60         config.flags4 & CFG4allcomdat && s.Sclass == SC.global;
61 }
62 
63 /***********************************
64  * Output function thunk.
65  */
66 @trusted
67 extern (C) void outthunk(Symbol *sthunk,Symbol *sfunc,uint p,tym_t thisty,
68         targ_size_t d,int i,targ_size_t d2)
69 {
70     sthunk.Sseg = cseg;
71     cod3_thunk(sthunk,sfunc,p,thisty,cast(uint)d,i,cast(uint)d2);
72     sthunk.Sfunc.Fflags &= ~Fpending;
73     sthunk.Sfunc.Fflags |= Foutput;   /* mark it as having been output */
74 }
75 
76 
77 /***************************
78  * Write out statically allocated data.
79  * Input:
80  *      s               symbol to be initialized
81  */
82 @trusted
83 void outdata(Symbol *s)
84 {
85     int seg;
86     targ_size_t offset;
87     int flags;
88     const int codeseg = cseg;
89 
90     symbol_debug(s);
91 
92     debug
93     debugy && printf("outdata('%s')\n",s.Sident.ptr);
94 
95     //printf("outdata('%s', ty=x%x)\n",s.Sident.ptr,s.Stype.Tty);
96     //symbol_print(s);
97 
98     // Data segment variables are always live on exit from a function
99     s.Sflags |= SFLlivexit;
100 
101     dt_t *dtstart = s.Sdt;
102     s.Sdt = null;                      // it will be free'd
103     targ_size_t datasize = 0;
104     tym_t ty = s.ty();
105     if (ty & mTYexport && config.wflags & WFexpdef && s.Sclass != SC.static_)
106         objmod.export_symbol(s,0);        // export data definition
107     for (dt_t *dt = dtstart; dt; dt = dt.DTnext)
108     {
109         //printf("\tdt = %p, dt = %d\n",dt,dt.dt);
110         switch (dt.dt)
111         {   case DT_abytes:
112             {   // Put out the data for the string, and
113                 // reserve a spot for a pointer to that string
114                 datasize += size(dt.Dty);      // reserve spot for pointer to string
115                 if (tybasic(dt.Dty) == TYcptr)
116                 {   dt.DTseg = codeseg;
117                     dt.DTabytes += Offset(codeseg);
118                     goto L1;
119                 }
120                 else if (tybasic(dt.Dty) == TYfptr &&
121                          dt.DTnbytes > config.threshold)
122                 {
123                 L1:
124                     objmod.write_bytes(SegData[dt.DTseg],dt.DTpbytes[0 .. dt.DTnbytes]);
125                     break;
126                 }
127                 else
128                 {
129                     alignOffset(CDATA, 2 << dt.DTalign);
130                     dt.DTabytes += objmod.data_readonly(cast(char*)dt.DTpbytes,dt.DTnbytes,&dt.DTseg);
131                 }
132                 break;
133             }
134 
135             case DT_ibytes:
136                 datasize += dt.DTn;
137                 break;
138 
139             case DT_nbytes:
140                 //printf("DT_nbytes %d\n", dt.DTnbytes);
141                 datasize += dt.DTnbytes;
142                 break;
143 
144             case DT_azeros:
145                 /* A block of zeros
146                  */
147                 //printf("DT_azeros %d\n", dt.DTazeros);
148                 datasize += dt.DTazeros;
149                 if (dt == dtstart && !dt.DTnext && s.Sclass != SC.comdat &&
150                     (s.Sseg == UNKNOWN || s.Sseg <= UDATA))
151                 {   /* first and only, so put in BSS segment
152                      */
153                     switch (ty & mTYLINK)
154                     {
155                         case mTYcs:
156                             s.Sseg = codeseg;
157                             Offset(codeseg) = _align(datasize,Offset(codeseg));
158                             s.Soffset = Offset(codeseg);
159                             Offset(codeseg) += datasize;
160                             s.Sfl = FLcsdata;
161                             break;
162 
163                         case mTYthreadData:
164                             assert(config.objfmt == OBJ_MACH && I64);
165                             goto case;
166                         case mTYthread:
167                         {   seg_data *pseg = objmod.tlsseg_bss();
168                             s.Sseg = pseg.SDseg;
169                             objmod.data_start(s, datasize, pseg.SDseg);
170                             if (config.objfmt == OBJ_OMF)
171                                 pseg.SDoffset += datasize;
172                             else
173                                 objmod.lidata(pseg.SDseg, pseg.SDoffset, datasize);
174                             s.Sfl = FLtlsdata;
175                             break;
176                         }
177 
178                         default:
179                             s.Sseg = UDATA;
180                             objmod.data_start(s,datasize,UDATA);
181                             objmod.lidata(s.Sseg,s.Soffset,datasize);
182                             s.Sfl = FLudata;           // uninitialized data
183                             break;
184                     }
185                     assert(s.Sseg && s.Sseg != UNKNOWN);
186                     if (s.Sclass == SC.global || (s.Sclass == SC.static_ && config.objfmt != OBJ_OMF)) // if a pubdef to be done
187                         objmod.pubdefsize(s.Sseg,s,s.Soffset,datasize);   // do the definition
188                     if (config.fulltypes &&
189                         !(s.Sclass == SC.static_ && funcsym_p)) // not local static
190                     {
191                         if (config.objfmt == OBJ_ELF || config.objfmt == OBJ_MACH)
192                             dwarf_outsym(s);
193                         else
194                             cv_outsym(s);
195                     }
196                     goto Lret;
197                 }
198                 break;
199 
200             case DT_common:
201                 assert(!dt.DTnext);
202                 outcommon(s,dt.DTazeros);
203                 goto Lret;
204 
205             case DT_xoff:
206             {   Symbol *sb = dt.DTsym;
207 
208                 if (tyfunc(sb.ty()))
209                 {
210                 }
211                 else if (sb.Sdt)               // if initializer for symbol
212 { if (!s.Sseg) s.Sseg = DATA;
213                     outdata(sb);                // write out data for symbol
214 }
215             }
216                 goto case;
217             case DT_coff:
218                 datasize += size(dt.Dty);
219                 break;
220             default:
221                 debug
222                 printf("dt = %p, dt = %d\n",dt,dt.dt);
223                 assert(0);
224         }
225     }
226 
227     if (s.Sclass == SC.comdat)          // if initialized common block
228     {
229         seg = objmod.comdatsize(s, datasize);
230         switch (ty & mTYLINK)
231         {
232             case mTYfar:                // if far data
233                 s.Sfl = FLfardata;
234                 break;
235 
236             case mTYcs:
237                 s.Sfl = FLcsdata;
238                 break;
239 
240             case mTYnear:
241             case 0:
242                 s.Sfl = FLdata;        // initialized data
243                 break;
244 
245             case mTYthread:
246                 s.Sfl = FLtlsdata;
247                 break;
248 
249             case mTYweakLinkage:
250                 s.Sfl = FLdata;        // initialized data
251                 break;
252 
253             default:
254                 assert(0);
255         }
256     }
257     else
258     {
259       switch (ty & mTYLINK)
260       {
261         case mTYcs:
262             seg = codeseg;
263             Offset(codeseg) = _align(datasize,Offset(codeseg));
264             s.Soffset = Offset(codeseg);
265             s.Sfl = FLcsdata;
266             break;
267 
268         case mTYthreadData:
269         {
270             assert(config.objfmt == OBJ_MACH && I64);
271 
272             seg_data *pseg = objmod.tlsseg_data();
273             s.Sseg = pseg.SDseg;
274             objmod.data_start(s, datasize, s.Sseg);
275             seg = pseg.SDseg;
276             s.Sfl = FLtlsdata;
277             break;
278         }
279         case mTYthread:
280         {
281             seg_data *pseg = objmod.tlsseg();
282             s.Sseg = pseg.SDseg;
283             objmod.data_start(s, datasize, s.Sseg);
284             seg = pseg.SDseg;
285             s.Sfl = FLtlsdata;
286             break;
287         }
288         case mTYnear:
289         case 0:
290             if (
291                 s.Sseg == 0 ||
292                 s.Sseg == UNKNOWN)
293                 s.Sseg = DATA;
294             seg = objmod.data_start(s,datasize,DATA);
295             s.Sfl = FLdata;            // initialized data
296             break;
297 
298         default:
299             assert(0);
300       }
301     }
302     if (s.Sseg == UNKNOWN && (config.objfmt == OBJ_ELF || config.objfmt == OBJ_MACH))
303         s.Sseg = seg;
304     else if (config.objfmt == OBJ_OMF)
305         s.Sseg = seg;
306     else
307         seg = s.Sseg;
308 
309     if (s.Sclass == SC.global || (s.Sclass == SC.static_ && config.objfmt != OBJ_OMF))
310         objmod.pubdefsize(seg,s,s.Soffset,datasize);    /* do the definition            */
311 
312     assert(s.Sseg != UNKNOWN);
313     if (config.fulltypes &&
314         !(s.Sclass == SC.static_ && funcsym_p)) // not local static
315     {
316         if (config.objfmt == OBJ_ELF || config.objfmt == OBJ_MACH)
317             dwarf_outsym(s);
318         else
319             cv_outsym(s);
320     }
321 
322     /* Go back through list, now that we know its size, and send out    */
323     /* the data.                                                        */
324 
325     offset = s.Soffset;
326 
327     dt_writeToObj(objmod, dtstart, seg, offset);
328     Offset(seg) = offset;
329 Lret:
330     dt_free(dtstart);
331 }
332 
333 
334 /********************************************
335  * Write dt to Object file.
336  * Params:
337  *      objmod = reference to object file
338  *      dt = data to write
339  *      seg = segment to write it to
340  *      offset = starting offset in segment - will get updated to reflect ending offset
341  */
342 
343 @trusted
344 void dt_writeToObj(Obj objmod, dt_t *dt, int seg, ref targ_size_t offset)
345 {
346     for (; dt; dt = dt.DTnext)
347     {
348         switch (dt.dt)
349         {
350             case DT_abytes:
351             {
352                 int flags;
353                 if (tyreg(dt.Dty))
354                     flags = CFoff;
355                 else
356                     flags = CFoff | CFseg;
357                 if (I64)
358                     flags |= CFoffset64;
359                 if (tybasic(dt.Dty) == TYcptr)
360                     objmod.reftocodeseg(seg,offset,dt.DTabytes);
361                 else
362                 {
363 if (config.exe & EX_posix)
364 {
365                     objmod.reftodatseg(seg,offset,dt.DTabytes,dt.DTseg,flags);
366 }
367 else
368 {
369                     if (dt.DTseg == DATA)
370                         objmod.reftodatseg(seg,offset,dt.DTabytes,DATA,flags);
371                     else
372                     {
373                         if (dt.DTseg == CDATA)
374                             objmod.reftodatseg(seg,offset,dt.DTabytes,CDATA,flags);
375                         else
376                             objmod.reftofarseg(seg,offset,dt.DTabytes,dt.DTseg,flags);
377                     }
378 }
379                 }
380                 offset += size(dt.Dty);
381                 break;
382             }
383 
384             case DT_ibytes:
385                 objmod.bytes(seg,offset,dt.DTn,dt.DTdata.ptr);
386                 offset += dt.DTn;
387                 break;
388 
389             case DT_nbytes:
390                 objmod.bytes(seg,offset,dt.DTnbytes,dt.DTpbytes);
391                 offset += dt.DTnbytes;
392                 break;
393 
394             case DT_azeros:
395                 //printf("objmod.lidata(seg = %d, offset = %d, azeros = %d)\n", seg, offset, dt.DTazeros);
396                 SegData[seg].SDoffset = offset;
397                 objmod.lidata(seg,offset,dt.DTazeros);
398                 offset = SegData[seg].SDoffset;
399                 break;
400 
401             case DT_xoff:
402             {
403                 Symbol *sb = dt.DTsym;          // get external symbol pointer
404                 targ_size_t a = dt.DToffset;    // offset from it
405                 int flags;
406                 if (tyreg(dt.Dty))
407                     flags = CFoff;
408                 else
409                     flags = CFoff | CFseg;
410                 if (I64 && tysize(dt.Dty) == 8)
411                     flags |= CFoffset64;
412                 offset += objmod.reftoident(seg,offset,sb,a,flags);
413                 break;
414             }
415 
416             case DT_coff:
417                 objmod.reftocodeseg(seg,offset,dt.DToffset);
418                 offset += _tysize[TYint];
419                 break;
420 
421             default:
422                 //printf("dt = %p, dt = %d\n",dt,dt.dt);
423                 assert(0);
424         }
425     }
426 }
427 
428 
429 /******************************
430  * Output n bytes of a common block, n > 0.
431  */
432 
433 @trusted
434 void outcommon(Symbol *s,targ_size_t n)
435 {
436     //printf("outcommon('%s',%d)\n",s.Sident.ptr,n);
437     if (n != 0)
438     {
439         assert(s.Sclass == SC.global);
440         if (s.ty() & mTYcs) // if store in code segment
441         {
442             /* COMDEFs not supported in code segment
443              * so put them out as initialized 0s
444              */
445             auto dtb = DtBuilder(0);
446             dtb.nzeros(cast(uint)n);
447             s.Sdt = dtb.finish();
448             outdata(s);
449         }
450         else if (s.ty() & mTYthread) // if store in thread local segment
451         {
452             if (config.objfmt == OBJ_ELF)
453             {
454                 s.Sclass = SC.comdef;
455                 objmod.common_block(s, 0, n, 1);
456             }
457             else
458             {
459                 /* COMDEFs not supported in tls segment
460                  * so put them out as COMDATs with initialized 0s
461                  */
462                 s.Sclass = SC.comdat;
463                 auto dtb = DtBuilder(0);
464                 dtb.nzeros(cast(uint)n);
465                 s.Sdt = dtb.finish();
466                 outdata(s);
467             }
468         }
469         else
470         {
471             s.Sclass = SC.comdef;
472             if (config.objfmt == OBJ_OMF)
473             {
474                 s.Sxtrnnum = objmod.common_block(s,(s.ty() & mTYfar) == 0,n,1);
475                 if (s.ty() & mTYfar)
476                     s.Sfl = FLfardata;
477                 else
478                     s.Sfl = FLextern;
479                 s.Sseg = UNKNOWN;
480                 pstate.STflags |= PFLcomdef;
481             }
482             else
483                 objmod.common_block(s, 0, n, 1);
484         }
485         if (config.fulltypes)
486         {
487             if (config.objfmt == OBJ_ELF || config.objfmt == OBJ_MACH)
488                 dwarf_outsym(s);
489             else
490                 cv_outsym(s);
491         }
492     }
493 }
494 
495 /*************************************
496  * Mark a Symbol as going into a read-only segment.
497  */
498 
499 @trusted
500 void out_readonly(Symbol *s)
501 {
502     if (config.objfmt == OBJ_ELF || config.objfmt == OBJ_MACH)
503     {
504         /* Cannot have pointers in CDATA when compiling PIC code, because
505          * they require dynamic relocations of the read-only segment.
506          * Instead use the .data.rel.ro section.
507          * https://issues.dlang.org/show_bug.cgi?id=11171
508          */
509         if (config.flags3 & CFG3pic && dtpointers(s.Sdt))
510             s.Sseg = CDATAREL;
511         else
512             s.Sseg = CDATA;
513     }
514     else
515     {
516         s.Sseg = CDATA;
517     }
518 }
519 
520 /*************************************
521  * Write out a readonly string literal in an implementation-defined
522  * manner.
523  * Params:
524  *      str = pointer to string data (need not have terminating 0)
525  *      len = number of characters in string
526  *      sz = size of each character (1, 2 or 4)
527  * Returns: a Symbol pointing to it.
528  */
529 @trusted
530 Symbol *out_string_literal(const(char)* str, uint len, uint sz)
531 {
532     tym_t ty = TYchar;
533     if (sz == 2)
534         ty = TYchar16;
535     else if (sz == 4)
536         ty = TYdchar;
537     Symbol *s = symbol_generate(SC.static_,type_static_array(len, tstypes[ty]));
538     switch (config.objfmt)
539     {
540         case OBJ_ELF:
541         case OBJ_MACH:
542             s.Sseg = objmod.string_literal_segment(sz);
543             break;
544 
545         case OBJ_MSCOFF:
546         case OBJ_OMF:   // goes into COMDATs, handled elsewhere
547         default:
548             assert(0);
549     }
550 
551     /* If there are any embedded zeros, this can't go in the special string segments
552      * which assume that 0 is the end of the string.
553      */
554     switch (sz)
555     {
556         case 1:
557             if (memchr(str, 0, len))
558                 s.Sseg = CDATA;
559             break;
560 
561         case 2:
562             foreach (i; 0 .. len)
563             {
564                 auto p = cast(const(ushort)*)str;
565                 if (p[i] == 0)
566                 {
567                     s.Sseg = CDATA;
568                     break;
569                 }
570             }
571             break;
572 
573         case 4:
574             foreach (i; 0 .. len)
575             {
576                 auto p = cast(const(uint)*)str;
577                 if (p[i] == 0)
578                 {
579                     s.Sseg = CDATA;
580                     break;
581                 }
582             }
583             break;
584 
585         default:
586             assert(0);
587     }
588 
589     auto dtb = DtBuilder(0);
590     dtb.nbytes(cast(uint)(len * sz), str);
591     dtb.nzeros(cast(uint)sz);       // include terminating 0
592     s.Sdt = dtb.finish();
593     s.Sfl = FLdata;
594     s.Salignment = sz;
595     outdata(s);
596     return s;
597 }
598 
599 
600 /******************************
601  * Walk expression tree, converting it from a PARSER tree to
602  * a code generator tree.
603  */
604 
605 @trusted
606 /*private*/ void outelem(elem *e, ref bool addressOfParam)
607 {
608     Symbol *s;
609     tym_t tym;
610     elem *e1;
611 
612 again:
613     assert(e);
614     elem_debug(e);
615 
616 debug
617 {
618     if (OTbinary(e.Eoper))
619         assert(e.EV.E1 && e.EV.E2);
620 //    else if (OTunary(e.Eoper))
621 //      assert(e.EV.E1 && !e.EV.E2);
622 }
623 
624     switch (e.Eoper)
625     {
626     default:
627     Lop:
628 debug
629 {
630         //if (!EOP(e)) printf("e.Eoper = x%x\n",e.Eoper);
631 }
632         if (OTbinary(e.Eoper))
633         {   outelem(e.EV.E1, addressOfParam);
634             e = e.EV.E2;
635         }
636         else if (OTunary(e.Eoper))
637         {
638             e = e.EV.E1;
639         }
640         else
641             break;
642         goto again;                     /* iterate instead of recurse   */
643     case OPaddr:
644         e1 = e.EV.E1;
645         if (e1.Eoper == OPvar)
646         {   // Fold into an OPrelconst
647             tym = e.Ety;
648             el_copy(e,e1);
649             e.Ety = tym;
650             e.Eoper = OPrelconst;
651             el_free(e1);
652             goto again;
653         }
654         goto Lop;
655 
656     case OPrelconst:
657     case OPvar:
658         s = e.EV.Vsym;
659         assert(s);
660         symbol_debug(s);
661         switch (s.Sclass)
662         {
663             case SC.regpar:
664             case SC.parameter:
665             case SC.shadowreg:
666                 if (e.Eoper == OPrelconst)
667                 {
668                     if (I16)
669                         addressOfParam = true;   // taking addr of param list
670                     else
671                         s.Sflags &= ~(SFLunambig | GTregcand);
672                 }
673                 break;
674 
675             case SC.static_:
676             case SC.locstat:
677             case SC.extern_:
678             case SC.global:
679             case SC.comdat:
680             case SC.comdef:
681             case SC.pseudo:
682             case SC.inline:
683             case SC.sinline:
684             case SC.einline:
685                 s.Sflags |= SFLlivexit;
686                 goto case;
687             case SC.auto_:
688             case SC.register:
689             case SC.fastpar:
690             case SC.bprel:
691                 if (e.Eoper == OPrelconst)
692                 {
693                     s.Sflags &= ~(SFLunambig | GTregcand);
694                 }
695                 else if (s.ty() & mTYfar)
696                     e.Ety |= mTYfar;
697                 break;
698             default:
699                 break;
700         }
701         break;
702 
703     case OPstring:
704     case OPconst:
705     case OPstrthis:
706         break;
707 
708     case OPsizeof:
709         assert(0);
710 
711     }
712 }
713 
714 /*************************************
715  * Determine register candidates.
716  */
717 
718 @trusted
719 void out_regcand(symtab_t *psymtab)
720 {
721     //printf("out_regcand()\n");
722     const bool ifunc = (tybasic(funcsym_p.ty()) == TYifunc);
723     for (SYMIDX si = 0; si < psymtab.length; si++)
724     {   Symbol *s = (*psymtab)[si];
725 
726         symbol_debug(s);
727         //assert(sytab[s.Sclass] & SCSS);      // only stack variables
728         s.Ssymnum = si;                        // Ssymnum trashed by cpp_inlineexpand
729         if (!(s.ty() & (mTYvolatile | mTYshared)) &&
730             !(ifunc && (s.Sclass == SC.parameter || s.Sclass == SC.regpar)) &&
731             s.Sclass != SC.static_)
732             s.Sflags |= (GTregcand | SFLunambig);      // assume register candidate
733         else
734             s.Sflags &= ~(GTregcand | SFLunambig);
735     }
736 
737     bool addressOfParam = false;                  // haven't taken addr of param yet
738     for (block *b = startblock; b; b = b.Bnext)
739     {
740         if (b.Belem)
741             out_regcand_walk(b.Belem, addressOfParam);
742 
743         // Any assembler blocks make everything ambiguous
744         if (b.BC == BCasm)
745             for (SYMIDX si = 0; si < psymtab.length; si++)
746                 (*psymtab)[si].Sflags &= ~(SFLunambig | GTregcand);
747     }
748 
749     // If we took the address of one parameter, assume we took the
750     // address of all non-register parameters.
751     if (addressOfParam)                      // if took address of a parameter
752     {
753         for (SYMIDX si = 0; si < psymtab.length; si++)
754             if ((*psymtab)[si].Sclass == SC.parameter || (*psymtab)[si].Sclass == SC.shadowreg)
755                 (*psymtab)[si].Sflags &= ~(SFLunambig | GTregcand);
756     }
757 
758 }
759 
760 @trusted
761 private void out_regcand_walk(elem *e, ref bool addressOfParam)
762 {
763     while (1)
764     {   elem_debug(e);
765 
766         if (OTbinary(e.Eoper))
767         {   if (e.Eoper == OPstreq)
768             {   if (e.EV.E1.Eoper == OPvar)
769                 {
770                     Symbol *s = e.EV.E1.EV.Vsym;
771                     s.Sflags &= ~(SFLunambig | GTregcand);
772                 }
773                 if (e.EV.E2.Eoper == OPvar)
774                 {
775                     Symbol *s = e.EV.E2.EV.Vsym;
776                     s.Sflags &= ~(SFLunambig | GTregcand);
777                 }
778             }
779             out_regcand_walk(e.EV.E1, addressOfParam);
780             e = e.EV.E2;
781         }
782         else if (OTunary(e.Eoper))
783         {
784             // Don't put 'this' pointers in registers if we need
785             // them for EH stack cleanup.
786             if (e.Eoper == OPctor)
787             {   elem *e1 = e.EV.E1;
788 
789                 if (e1.Eoper == OPadd)
790                     e1 = e1.EV.E1;
791                 if (e1.Eoper == OPvar)
792                     e1.EV.Vsym.Sflags &= ~GTregcand;
793             }
794             e = e.EV.E1;
795         }
796         else
797         {   if (e.Eoper == OPrelconst)
798             {
799                 Symbol *s = e.EV.Vsym;
800                 assert(s);
801                 symbol_debug(s);
802                 switch (s.Sclass)
803                 {
804                     case SC.regpar:
805                     case SC.parameter:
806                     case SC.shadowreg:
807                         if (I16)
808                             addressOfParam = true;       // taking addr of param list
809                         else
810                             s.Sflags &= ~(SFLunambig | GTregcand);
811                         break;
812 
813                     case SC.auto_:
814                     case SC.register:
815                     case SC.fastpar:
816                     case SC.bprel:
817                         s.Sflags &= ~(SFLunambig | GTregcand);
818                         break;
819 
820                     default:
821                         break;
822                 }
823             }
824             else if (e.Eoper == OPvar)
825             {
826                 if (e.EV.Voffset)
827                 {   if (!(e.EV.Voffset == 1 && tybyte(e.Ety)) &&
828                         !(e.EV.Voffset == REGSIZE && tysize(e.Ety) == REGSIZE))
829                     {
830                         e.EV.Vsym.Sflags &= ~GTregcand;
831                     }
832                 }
833             }
834             break;
835         }
836     }
837 }
838 
839 
840 /**************************
841  * Optimize function,
842  * generate code for it,
843  * and write it out.
844  */
845 
846 @trusted
847 void writefunc(Symbol *sfunc)
848 {
849     cstate.CSpsymtab = &globsym;
850     writefunc2(sfunc);
851     cstate.CSpsymtab = null;
852 }
853 
854 @trusted
855 private void writefunc2(Symbol *sfunc)
856 {
857     func_t *f = sfunc.Sfunc;
858 
859     //printf("writefunc(%s)\n",sfunc.Sident.ptr);
860     //symbol_print(sfunc);
861     debug debugy && printf("writefunc(%s)\n",sfunc.Sident.ptr);
862 
863     /* Signify that function has been output                    */
864     /* (before inline_do() to prevent infinite recursion!)      */
865     f.Fflags &= ~Fpending;
866     f.Fflags |= Foutput;
867 
868     if (eecontext.EEcompile && eecontext.EEfunc != sfunc)
869         return;
870 
871     /* Copy local symbol table onto main one, making sure       */
872     /* that the symbol numbers are adjusted accordingly */
873     //printf("f.Flocsym.length = %d\n",f.Flocsym.length);
874     debug debugy && printf("appending symbols to symtab...\n");
875     const nsymbols = f.Flocsym.length;
876     globsym.setLength(nsymbols);
877     foreach (si; 0 .. nsymbols)
878         globsym[si] = f.Flocsym[si];
879 
880     assert(startblock == null);
881     startblock = sfunc.Sfunc.Fstartblock;
882     sfunc.Sfunc.Fstartblock = null;
883     assert(startblock);
884 
885     assert(funcsym_p == null);
886     funcsym_p = sfunc;
887     tym_t tyf = tybasic(sfunc.ty());
888 
889     // TX86 computes parameter offsets in stackoffsets()
890     //printf("globsym.length = %d\n", globsym.length);
891 
892     for (SYMIDX si = 0; si < globsym.length; si++)
893     {   Symbol *s = globsym[si];
894 
895         symbol_debug(s);
896         //printf("symbol %d '%s'\n",si,s.Sident.ptr);
897 
898         type_size(s.Stype);    // do any forward template instantiations
899 
900         s.Ssymnum = si;        // Ssymnum trashed by cpp_inlineexpand
901         s.Sflags &= ~(SFLunambig | GTregcand);
902         switch (s.Sclass)
903         {
904             case SC.bprel:
905                 s.Sfl = FLbprel;
906                 goto L3;
907 
908             case SC.auto_:
909             case SC.register:
910                 s.Sfl = FLauto;
911                 goto L3;
912 
913             case SC.fastpar:
914                 s.Sfl = FLfast;
915                 goto L3;
916 
917             case SC.regpar:
918             case SC.parameter:
919             case SC.shadowreg:
920                 s.Sfl = FLpara;
921                 if (tyf == TYifunc)
922                 {   s.Sflags |= SFLlivexit;
923                     break;
924                 }
925             L3:
926                 if (!(s.ty() & (mTYvolatile | mTYshared)))
927                     s.Sflags |= GTregcand | SFLunambig; // assume register candidate   */
928                 break;
929 
930             case SC.pseudo:
931                 s.Sfl = FLpseudo;
932                 break;
933 
934             case SC.static_:
935                 break;                  // already taken care of by datadef()
936 
937             case SC.stack:
938                 s.Sfl = FLstack;
939                 break;
940 
941             default:
942                 symbol_print(s);
943                 assert(0);
944         }
945     }
946 
947     bool addressOfParam = false;  // see if any parameters get their address taken
948     bool anyasm = false;
949     for (block *b = startblock; b; b = b.Bnext)
950     {
951         memset(&b._BLU,0,block.sizeof - block._BLU.offsetof);
952         if (b.Belem)
953         {   outelem(b.Belem, addressOfParam);
954             if (b.Belem.Eoper == OPhalt)
955             {   b.BC = BCexit;
956                 list_free(&b.Bsucc,FPNULL);
957             }
958         }
959         if (b.BC == BCasm)
960             anyasm = true;
961         if (sfunc.Sflags & SFLexit && (b.BC == BCret || b.BC == BCretexp))
962         {   b.BC = BCexit;
963             list_free(&b.Bsucc,FPNULL);
964         }
965         assert(b != b.Bnext);
966     }
967     PARSER = 0;
968     if (eecontext.EEelem)
969     {
970         const marksi = globsym.length;
971         eecontext.EEin++;
972         outelem(eecontext.EEelem, addressOfParam);
973         eecontext.EEelem = doptelem(eecontext.EEelem,true);
974         eecontext.EEin--;
975         eecontext_convs(marksi);
976     }
977 
978     // If we took the address of one parameter, assume we took the
979     // address of all non-register parameters.
980     if (addressOfParam | anyasm)        // if took address of a parameter
981     {
982         for (SYMIDX si = 0; si < globsym.length; si++)
983             if (anyasm || globsym[si].Sclass == SC.parameter)
984                 globsym[si].Sflags &= ~(SFLunambig | GTregcand);
985     }
986 
987     block_pred();                       // compute predecessors to blocks
988     block_compbcount();                 // eliminate unreachable blocks
989 
990     debug { } else
991     {
992         if (debugb)
993         {
994             WRfunc("codegen", funcsym_p, startblock);
995         }
996     }
997 
998     if (go.mfoptim)
999     {   OPTIMIZER = 1;
1000         optfunc();                      /* optimize function            */
1001         OPTIMIZER = 0;
1002     }
1003     else
1004     {
1005         //printf("blockopt()\n");
1006         blockopt(0);                    /* optimize                     */
1007     }
1008 
1009     assert(funcsym_p == sfunc);
1010     const int CSEGSAVE_DEFAULT = -10_000;        // some unlikely number
1011     int csegsave = CSEGSAVE_DEFAULT;
1012     if (eecontext.EEcompile != 1)
1013     {
1014         if (symbol_iscomdat2(sfunc))
1015         {
1016             csegsave = cseg;
1017             objmod.comdat(sfunc);
1018             cseg = sfunc.Sseg;
1019         }
1020         else if (config.flags & CFGsegs) // if user set switch for this
1021         {
1022             objmod.codeseg(&funcsym_p.Sident[0], 1);
1023                                         // generate new code segment
1024         }
1025         cod3_align(cseg);               // align start of function
1026         objmod.func_start(sfunc);
1027     }
1028 
1029     //printf("codgen()\n");
1030     codgen(sfunc);                  // generate code
1031     //printf("after codgen for %s Coffset %x\n",sfunc.Sident.ptr,Offset(cseg));
1032     sfunc.Sfunc.Fstartblock = startblock;
1033     bool saveForInlining = canInlineFunction(sfunc);
1034     if (saveForInlining)
1035     {
1036         startblock = null;
1037     }
1038     else
1039     {
1040         sfunc.Sfunc.Fstartblock = null;
1041         blocklist_free(&startblock);
1042     }
1043 
1044     objmod.func_term(sfunc);
1045     if (eecontext.EEcompile == 1)
1046         goto Ldone;
1047     if (sfunc.Sclass == SC.global)
1048     {
1049         if ((config.objfmt == OBJ_OMF || config.objfmt == OBJ_MSCOFF) && !(config.flags4 & CFG4allcomdat))
1050         {
1051             assert(sfunc.Sseg == cseg);
1052             objmod.pubdef(sfunc.Sseg,sfunc,sfunc.Soffset);       // make a public definition
1053         }
1054 
1055         addStartupReference(sfunc);
1056     }
1057 
1058     if (config.wflags & WFexpdef &&
1059         sfunc.Sclass != SC.static_ &&
1060         sfunc.Sclass != SC.sinline &&
1061         !(sfunc.Sclass == SC.inline && !(config.flags2 & CFG2comdat)) &&
1062         sfunc.ty() & mTYexport)
1063         objmod.export_symbol(sfunc,cast(uint)Para.offset);      // export function definition
1064 
1065     if (config.fulltypes && config.fulltypes != CV8)
1066     {
1067         if (config.objfmt == OBJ_OMF || config.objfmt == OBJ_MSCOFF)
1068             cv_func(sfunc);                 // debug info for function
1069     }
1070 
1071     /* This is to make uplevel references to SCfastpar variables
1072      * from nested functions work.
1073      */
1074     for (SYMIDX si = 0; si < globsym.length; si++)
1075     {
1076         Symbol *s = globsym[si];
1077 
1078         switch (s.Sclass)
1079         {   case SC.fastpar:
1080                 s.Sclass = SC.auto_;
1081                 break;
1082 
1083             default:
1084                 break;
1085         }
1086     }
1087     /* After codgen() and writing debug info for the locals,
1088      * readjust the offsets of all stack variables so they
1089      * are relative to the frame pointer.
1090      * Necessary for nested function access to lexically enclosing frames.
1091      */
1092      cod3_adjSymOffsets();
1093 
1094     if (symbol_iscomdat2(sfunc))         // if generated a COMDAT
1095     {
1096         assert(csegsave != CSEGSAVE_DEFAULT);
1097         objmod.setcodeseg(csegsave);       // reset to real code seg
1098         if (config.objfmt == OBJ_MACH)
1099             assert(cseg == CODE);
1100     }
1101 
1102     /* Check if function is a constructor or destructor, by     */
1103     /* seeing if the function name starts with _STI or _STD     */
1104     {
1105 version (LittleEndian)
1106 {
1107         short *p = cast(short *) sfunc.Sident.ptr;
1108         if (p[0] == (('S' << 8) | '_') && (p[1] == (('I' << 8) | 'T') || p[1] == (('D' << 8) | 'T')))
1109             objmod.setModuleCtorDtor(sfunc, sfunc.Sident.ptr[3] == 'I');
1110 }
1111 else
1112 {
1113         char *p = sfunc.Sident.ptr;
1114         if (p[0] == '_' && p[1] == 'S' && p[2] == 'T' &&
1115             (p[3] == 'I' || p[3] == 'D'))
1116             objmod.setModuleCtorDtor(sfunc, sfunc.Sident.ptr[3] == 'I');
1117 }
1118     }
1119 
1120 Ldone:
1121     funcsym_p = null;
1122 
1123     if (saveForInlining)
1124     {
1125         f.Flocsym.setLength(globsym.length);
1126         foreach (si; 0 .. globsym.length)
1127             f.Flocsym[si] = globsym[si];
1128     }
1129     else
1130     {
1131     }
1132     globsym.setLength(0);
1133 
1134     //printf("done with writefunc()\n");
1135     //dfo.dtor();       // save allocation for next time
1136 }
1137 
1138 /*************************
1139  * Align segment offset.
1140  * Input:
1141  *      seg             segment to be aligned
1142  *      datasize        size in bytes of object to be aligned
1143  */
1144 
1145 @trusted
1146 void alignOffset(int seg,targ_size_t datasize)
1147 {
1148     targ_size_t alignbytes = _align(datasize,Offset(seg)) - Offset(seg);
1149     //printf("seg %d datasize = x%x, Offset(seg) = x%x, alignbytes = x%x\n",
1150       //seg,datasize,Offset(seg),alignbytes);
1151     if (alignbytes)
1152         objmod.lidata(seg,Offset(seg),alignbytes);
1153 }
1154 
1155 /***************************************
1156  * Write data into read-only data segment.
1157  * Return symbol for it.
1158  */
1159 
1160 enum ROMAX = 32;
1161 struct Readonly
1162 {
1163     Symbol *sym;
1164     size_t length;
1165     ubyte[ROMAX] p;
1166 }
1167 
1168 enum RMAX = 16;
1169 private __gshared
1170 {
1171     Readonly[RMAX] readonly;
1172     size_t readonly_length;
1173     size_t readonly_i;
1174 }
1175 
1176 @trusted
1177 void out_reset()
1178 {
1179     readonly_length = 0;
1180     readonly_i = 0;
1181 }
1182 
1183 @trusted
1184 Symbol *out_readonly_sym(tym_t ty, void *p, int len)
1185 {
1186 static if (0)
1187 {
1188     printf("out_readonly_sym(ty = x%x)\n", ty);
1189     for (int i = 0; i < len; i++)
1190         printf(" [%d] = %02x\n", i, (cast(ubyte*)p)[i]);
1191 }
1192     // Look for previous symbol we can reuse
1193     for (int i = 0; i < readonly_length; i++)
1194     {
1195         Readonly *r = &readonly[i];
1196         if (r.length == len && memcmp(p, r.p.ptr, len) == 0)
1197             return r.sym;
1198     }
1199 
1200     Symbol *s;
1201 
1202     bool cdata = config.objfmt == OBJ_ELF ||
1203                  config.objfmt == OBJ_OMF ||
1204                  config.objfmt == OBJ_MSCOFF;
1205     if (cdata)
1206     {
1207         /* MACHOBJ can't go here, because the const data segment goes into
1208          * the _TEXT segment, and one cannot have a fixup from _TEXT to _TEXT.
1209          */
1210         s = objmod.sym_cdata(ty, cast(char *)p, len);
1211     }
1212     else
1213     {
1214         uint sz = tysize(ty);
1215 
1216         alignOffset(DATA, sz);
1217         s = symboldata(Offset(DATA),ty | mTYconst);
1218         s.Sseg = DATA;
1219         objmod.write_bytes(SegData[DATA], p[0 .. len]);
1220         //printf("s.Sseg = %d:x%x\n", s.Sseg, s.Soffset);
1221     }
1222 
1223     if (len <= ROMAX)
1224     {   Readonly *r;
1225 
1226         if (readonly_length < RMAX)
1227         {
1228             r = &readonly[readonly_length];
1229             readonly_length++;
1230         }
1231         else
1232         {   r = &readonly[readonly_i];
1233             readonly_i++;
1234             if (readonly_i >= RMAX)
1235                 readonly_i = 0;
1236         }
1237         r.length = len;
1238         r.sym = s;
1239         memcpy(r.p.ptr, p, len);
1240     }
1241     return s;
1242 }
1243 
1244 /*************************************
1245  * Output Symbol as a readonly comdat.
1246  * Params:
1247  *      s = comdat symbol
1248  *      p = pointer to the data to write
1249  *      len = length of that data
1250  *      nzeros = number of trailing zeros to append
1251  */
1252 @trusted
1253 void out_readonly_comdat(Symbol *s, const(void)* p, uint len, uint nzeros)
1254 {
1255     objmod.readonly_comdat(s);         // create comdat segment
1256     objmod.write_bytes(SegData[s.Sseg], p[0 .. len]);
1257     objmod.lidata(s.Sseg, len, nzeros);
1258 }
1259 
1260 @trusted
1261 void Srcpos_print(ref const Srcpos srcpos, const(char)* func)
1262 {
1263     printf("%s(", func);
1264     printf("Sfilename = %s", srcpos.Sfilename ? srcpos.Sfilename : "null".ptr);
1265     printf(", Slinnum = %u", srcpos.Slinnum);
1266     printf(")\n");
1267 }
1268 
1269 /*********************************************
1270  * If sfunc is the entry point, add a reference to pull
1271  * in the startup code.
1272  * Params:
1273  *      sfunc = function
1274  */
1275 private
1276 @trusted
1277 void addStartupReference(Symbol* sfunc)
1278 {
1279 }