1 /**
2  * Compiler implementation of the
3  * $(LINK2 http://www.dlang.org, D programming language).
4  *
5  * Copyright:   Copyright (C) 1984-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/out.d, backend/out.d)
10  */
11 
12 
13 module dmd.backend.dout;
14 
15 version (SPP) { } else
16 {
17 
18 import core.stdc.stdio;
19 import core.stdc.string;
20 
21 import dmd.backend.cc;
22 import dmd.backend.cdef;
23 import dmd.backend.cgcv;
24 import dmd.backend.code;
25 import dmd.backend.code_x86;
26 import dmd.backend.cv4;
27 import dmd.backend.dt;
28 import dmd.backend.dlist;
29 import dmd.backend.mem;
30 import dmd.backend.el;
31 import dmd.backend.exh;
32 import dmd.backend.global;
33 import dmd.backend.goh;
34 import dmd.backend.obj;
35 import dmd.backend.oper;
36 import dmd.backend.outbuf;
37 import dmd.backend.rtlsym;
38 import dmd.backend.symtab;
39 import dmd.backend.ty;
40 import dmd.backend.type;
41 
42 version (SCPP)
43 {
44     import cpp;
45     import msgs2;
46     import parser;
47 }
48 version (HTOD)
49 {
50     import cpp;
51     import msgs2;
52     import parser;
53 }
54 
55 version (Windows)
56 {
57     extern (C)
58     {
59         int stricmp(const(char)*, const(char)*) pure nothrow @nogc;
60         int memicmp(const(void)*, const(void)*, size_t) pure nothrow @nogc;
61     }
62 }
63 
64 extern (C++):
65 
66 nothrow:
67 
68 void dt_writeToObj(Obj objmod, dt_t *dt, int seg, ref targ_size_t offset);
69 
70 // Determine if this Symbol is stored in a COMDAT
71 bool symbol_iscomdat2(Symbol* s)
72 {
73     version (MARS)
74     {
75         return s.Sclass == SCcomdat ||
76             config.flags2 & CFG2comdat && s.Sclass == SCinline ||
77             config.flags4 & CFG4allcomdat && s.Sclass == SCglobal;
78     }
79     else
80     {
81         return s.Sclass == SCcomdat ||
82             config.flags2 & CFG2comdat && s.Sclass == SCinline ||
83             config.flags4 & CFG4allcomdat && (s.Sclass == SCglobal || s.Sclass == SCstatic);
84     }
85 }
86 
87 version (SCPP)
88 {
89 
90 /**********************************
91  * We put out an external definition.
92  */
93 void out_extdef(Symbol *s)
94 {
95     pstate.STflags |= PFLextdef;
96     if (//config.flags2 & CFG2phgen ||
97         (config.flags2 & (CFG2phauto | CFG2phautoy) &&
98             !(pstate.STflags & (PFLhxwrote | PFLhxdone)))
99        )
100 
101         synerr(EM_data_in_pch,prettyident(s));          // data or code in precompiled header
102 }
103 
104 /********************************
105  * Put out code segment name record.
106  */
107 void outcsegname(char *csegname)
108 {
109     Obj.codeseg(csegname,0);
110 }
111 
112 }
113 
114 version (HTOD)
115 {
116     void outcsegname(char *csegname) { }
117 }
118 
119 /***********************************
120  * Output function thunk.
121  */
122 extern (C) void outthunk(Symbol *sthunk,Symbol *sfunc,uint p,tym_t thisty,
123         targ_size_t d,int i,targ_size_t d2)
124 {
125 version (HTOD) { } else
126 {
127     sthunk.Sseg = cseg;
128     cod3_thunk(sthunk,sfunc,p,thisty,cast(uint)d,i,cast(uint)d2);
129     sthunk.Sfunc.Fflags &= ~Fpending;
130     sthunk.Sfunc.Fflags |= Foutput;   /* mark it as having been output */
131 }
132 }
133 
134 
135 /***************************
136  * Write out statically allocated data.
137  * Input:
138  *      s               symbol to be initialized
139  */
140 
141 void outdata(Symbol *s)
142 {
143 version (HTOD)
144 {
145     return;
146 }
147 
148     int seg;
149     targ_size_t offset;
150     int flags;
151     const int codeseg = cseg;
152 
153     symbol_debug(s);
154 
155     debug
156     debugy && printf("outdata('%s')\n",s.Sident.ptr);
157 
158     //printf("outdata('%s', ty=x%x)\n",s.Sident.ptr,s.Stype.Tty);
159     //symbol_print(s);
160 
161     // Data segment variables are always live on exit from a function
162     s.Sflags |= SFLlivexit;
163 
164     dt_t *dtstart = s.Sdt;
165     s.Sdt = null;                      // it will be free'd
166     targ_size_t datasize = 0;
167     tym_t ty = s.ty();
168 version (SCPP)
169 {
170     if (eecontext.EEcompile)
171     {   s.Sfl = (s.ty() & mTYfar) ? FLfardata : FLextern;
172         s.Sseg = UNKNOWN;
173         goto Lret;                      // don't output any data
174     }
175 }
176     if (ty & mTYexport && config.wflags & WFexpdef && s.Sclass != SCstatic)
177         objmod.export_symbol(s,0);        // export data definition
178     for (dt_t *dt = dtstart; dt; dt = dt.DTnext)
179     {
180         //printf("\tdt = %p, dt = %d\n",dt,dt.dt);
181         switch (dt.dt)
182         {   case DT_abytes:
183             {   // Put out the data for the string, and
184                 // reserve a spot for a pointer to that string
185                 datasize += size(dt.Dty);      // reserve spot for pointer to string
186                 if (tybasic(dt.Dty) == TYcptr)
187                 {   dt.DTseg = codeseg;
188                     dt.DTabytes += Offset(codeseg);
189                     goto L1;
190                 }
191                 else if (tybasic(dt.Dty) == TYfptr &&
192                          dt.DTnbytes > config.threshold)
193                 {
194 version (SCPP)
195 {
196                     {
197                     targ_size_t foffset;
198                     dt.DTseg = objmod.fardata(s.Sident.ptr,dt.DTnbytes,&foffset);
199                     dt.DTabytes += foffset;
200                     }
201 }
202                 L1:
203                     objmod.write_bytes(SegData[dt.DTseg],dt.DTnbytes,dt.DTpbytes);
204                     break;
205                 }
206                 else
207                 {
208                     version (SCPP)
209                         alignOffset(DATA, 2 << dt.DTalign);
210                     version (MARS)
211                         alignOffset(CDATA, 2 << dt.DTalign);
212                     dt.DTabytes += objmod.data_readonly(cast(char*)dt.DTpbytes,dt.DTnbytes,&dt.DTseg);
213                 }
214                 break;
215             }
216 
217             case DT_ibytes:
218                 datasize += dt.DTn;
219                 break;
220 
221             case DT_nbytes:
222                 //printf("DT_nbytes %d\n", dt.DTnbytes);
223                 datasize += dt.DTnbytes;
224                 break;
225 
226             case DT_azeros:
227                 /* A block of zeros
228                  */
229                 //printf("DT_azeros %d\n", dt.DTazeros);
230                 datasize += dt.DTazeros;
231                 if (dt == dtstart && !dt.DTnext && s.Sclass != SCcomdat &&
232                     (s.Sseg == UNKNOWN || s.Sseg <= UDATA))
233                 {   /* first and only, so put in BSS segment
234                      */
235                     switch (ty & mTYLINK)
236                     {
237 version (SCPP)
238 {
239                         case mTYfar:                    // if far data
240                             s.Sseg = objmod.fardata(s.Sident.ptr,datasize,&s.Soffset);
241                             s.Sfl = FLfardata;
242                             break;
243 }
244 
245                         case mTYcs:
246                             s.Sseg = codeseg;
247                             Offset(codeseg) = _align(datasize,Offset(codeseg));
248                             s.Soffset = Offset(codeseg);
249                             Offset(codeseg) += datasize;
250                             s.Sfl = FLcsdata;
251                             break;
252 
253                         case mTYthreadData:
254                             assert(config.objfmt == OBJ_MACH && I64);
255                             goto case;
256                         case mTYthread:
257                         {   seg_data *pseg = objmod.tlsseg_bss();
258                             s.Sseg = pseg.SDseg;
259                             objmod.data_start(s, datasize, pseg.SDseg);
260                             if (config.objfmt == OBJ_OMF)
261                                 pseg.SDoffset += datasize;
262                             else
263                                 objmod.lidata(pseg.SDseg, pseg.SDoffset, datasize);
264                             s.Sfl = FLtlsdata;
265                             break;
266                         }
267 
268                         default:
269                             s.Sseg = UDATA;
270                             objmod.data_start(s,datasize,UDATA);
271                             objmod.lidata(s.Sseg,s.Soffset,datasize);
272                             s.Sfl = FLudata;           // uninitialized data
273                             break;
274                     }
275                     assert(s.Sseg && s.Sseg != UNKNOWN);
276                     if (s.Sclass == SCglobal || (s.Sclass == SCstatic && config.objfmt != OBJ_OMF)) // if a pubdef to be done
277                         objmod.pubdefsize(s.Sseg,s,s.Soffset,datasize);   // do the definition
278                     searchfixlist(s);
279                     if (config.fulltypes &&
280                         !(s.Sclass == SCstatic && funcsym_p)) // not local static
281                         cv_outsym(s);
282 version (SCPP)
283 {
284                     out_extdef(s);
285 }
286                     goto Lret;
287                 }
288                 break;
289 
290             case DT_common:
291                 assert(!dt.DTnext);
292                 outcommon(s,dt.DTazeros);
293                 goto Lret;
294 
295             case DT_xoff:
296             {   Symbol *sb = dt.DTsym;
297 
298                 if (tyfunc(sb.ty()))
299                 {
300 version (SCPP)
301 {
302                     nwc_mustwrite(sb);
303 }
304                 }
305                 else if (sb.Sdt)               // if initializer for symbol
306 { if (!s.Sseg) s.Sseg = DATA;
307                     outdata(sb);                // write out data for symbol
308 }
309             }
310                 goto case;
311             case DT_coff:
312                 datasize += size(dt.Dty);
313                 break;
314             default:
315                 debug
316                 printf("dt = %p, dt = %d\n",dt,dt.dt);
317                 assert(0);
318         }
319     }
320 
321     if (s.Sclass == SCcomdat)          // if initialized common block
322     {
323         seg = objmod.comdatsize(s, datasize);
324         switch (ty & mTYLINK)
325         {
326             case mTYfar:                // if far data
327                 s.Sfl = FLfardata;
328                 break;
329 
330             case mTYcs:
331                 s.Sfl = FLcsdata;
332                 break;
333 
334             case mTYnear:
335             case 0:
336                 s.Sfl = FLdata;        // initialized data
337                 break;
338 
339             case mTYthread:
340                 s.Sfl = FLtlsdata;
341                 break;
342 
343             default:
344                 assert(0);
345         }
346     }
347     else
348     {
349       switch (ty & mTYLINK)
350       {
351 version (SCPP)
352 {
353         case mTYfar:                    // if far data
354             seg = objmod.fardata(s.Sident.ptr,datasize,&s.Soffset);
355             s.Sfl = FLfardata;
356             break;
357 }
358 
359         case mTYcs:
360             seg = codeseg;
361             Offset(codeseg) = _align(datasize,Offset(codeseg));
362             s.Soffset = Offset(codeseg);
363             s.Sfl = FLcsdata;
364             break;
365 
366         case mTYthreadData:
367         {
368             assert(config.objfmt == OBJ_MACH && I64);
369 
370             seg_data *pseg = objmod.tlsseg_data();
371             s.Sseg = pseg.SDseg;
372             objmod.data_start(s, datasize, s.Sseg);
373             seg = pseg.SDseg;
374             s.Sfl = FLtlsdata;
375             break;
376         }
377         case mTYthread:
378         {
379             seg_data *pseg = objmod.tlsseg();
380             s.Sseg = pseg.SDseg;
381             objmod.data_start(s, datasize, s.Sseg);
382             seg = pseg.SDseg;
383             s.Sfl = FLtlsdata;
384             break;
385         }
386         case mTYnear:
387         case 0:
388             if (
389                 s.Sseg == 0 ||
390                 s.Sseg == UNKNOWN)
391                 s.Sseg = DATA;
392             seg = objmod.data_start(s,datasize,DATA);
393             s.Sfl = FLdata;            // initialized data
394             break;
395 
396         default:
397             assert(0);
398       }
399     }
400     if (s.Sseg == UNKNOWN && (config.objfmt == OBJ_ELF || config.objfmt == OBJ_MACH))
401         s.Sseg = seg;
402     else if (config.objfmt == OBJ_OMF)
403         s.Sseg = seg;
404     else
405         seg = s.Sseg;
406 
407     if (s.Sclass == SCglobal || (s.Sclass == SCstatic && config.objfmt != OBJ_OMF))
408         objmod.pubdefsize(seg,s,s.Soffset,datasize);    /* do the definition            */
409 
410     assert(s.Sseg != UNKNOWN);
411     if (config.fulltypes &&
412         !(s.Sclass == SCstatic && funcsym_p)) // not local static
413         cv_outsym(s);
414     searchfixlist(s);
415 
416     /* Go back through list, now that we know its size, and send out    */
417     /* the data.                                                        */
418 
419     offset = s.Soffset;
420 
421     dt_writeToObj(objmod, dtstart, seg, offset);
422     Offset(seg) = offset;
423 version (SCPP)
424 {
425     out_extdef(s);
426 }
427 Lret:
428     dt_free(dtstart);
429 }
430 
431 
432 /********************************************
433  * Write dt to Object file.
434  * Params:
435  *      objmod = reference to object file
436  *      dt = data to write
437  *      seg = segment to write it to
438  *      offset = starting offset in segment - will get updated to reflect ending offset
439  */
440 
441 void dt_writeToObj(Obj objmod, dt_t *dt, int seg, ref targ_size_t offset)
442 {
443     for (; dt; dt = dt.DTnext)
444     {
445         switch (dt.dt)
446         {
447             case DT_abytes:
448             {
449                 int flags;
450                 if (tyreg(dt.Dty))
451                     flags = CFoff;
452                 else
453                     flags = CFoff | CFseg;
454                 if (I64)
455                     flags |= CFoffset64;
456                 if (tybasic(dt.Dty) == TYcptr)
457                     objmod.reftocodeseg(seg,offset,dt.DTabytes);
458                 else
459                 {
460 static if (TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_DRAGONFLYBSD || TARGET_SOLARIS)
461 {
462                     objmod.reftodatseg(seg,offset,dt.DTabytes,dt.DTseg,flags);
463 }
464 else
465 {
466                     if (dt.DTseg == DATA)
467                         objmod.reftodatseg(seg,offset,dt.DTabytes,DATA,flags);
468                     else
469                     {
470 version (MARS)
471 {
472                         if (dt.DTseg == CDATA)
473                             objmod.reftodatseg(seg,offset,dt.DTabytes,CDATA,flags);
474                         else
475                             objmod.reftofarseg(seg,offset,dt.DTabytes,dt.DTseg,flags);
476 }
477 else
478 {
479                         objmod.reftofarseg(seg,offset,dt.DTabytes,dt.DTseg,flags);
480 }
481                     }
482 }
483                 }
484                 offset += size(dt.Dty);
485                 break;
486             }
487 
488             case DT_ibytes:
489                 objmod.bytes(seg,offset,dt.DTn,dt.DTdata.ptr);
490                 offset += dt.DTn;
491                 break;
492 
493             case DT_nbytes:
494                 objmod.bytes(seg,offset,dt.DTnbytes,dt.DTpbytes);
495                 offset += dt.DTnbytes;
496                 break;
497 
498             case DT_azeros:
499                 //printf("objmod.lidata(seg = %d, offset = %d, azeros = %d)\n", seg, offset, dt.DTazeros);
500                 SegData[seg].SDoffset = offset;
501                 objmod.lidata(seg,offset,dt.DTazeros);
502                 offset = SegData[seg].SDoffset;
503                 break;
504 
505             case DT_xoff:
506             {
507                 Symbol *sb = dt.DTsym;          // get external symbol pointer
508                 targ_size_t a = dt.DToffset;    // offset from it
509                 int flags;
510                 if (tyreg(dt.Dty))
511                     flags = CFoff;
512                 else
513                     flags = CFoff | CFseg;
514                 if (I64 && tysize(dt.Dty) == 8)
515                     flags |= CFoffset64;
516                 offset += objmod.reftoident(seg,offset,sb,a,flags);
517                 break;
518             }
519 
520             case DT_coff:
521                 objmod.reftocodeseg(seg,offset,dt.DToffset);
522                 offset += _tysize[TYint];
523                 break;
524 
525             default:
526                 //printf("dt = %p, dt = %d\n",dt,dt.dt);
527                 assert(0);
528         }
529     }
530 }
531 
532 
533 /******************************
534  * Output n bytes of a common block, n > 0.
535  */
536 
537 void outcommon(Symbol *s,targ_size_t n)
538 {
539     //printf("outcommon('%s',%d)\n",s.Sident.ptr,n);
540     if (n != 0)
541     {
542         assert(s.Sclass == SCglobal);
543         if (s.ty() & mTYcs) // if store in code segment
544         {
545             /* COMDEFs not supported in code segment
546              * so put them out as initialized 0s
547              */
548             auto dtb = DtBuilder(0);
549             dtb.nzeros(cast(uint)n);
550             s.Sdt = dtb.finish();
551             outdata(s);
552 version (SCPP)
553 {
554             out_extdef(s);
555 }
556         }
557         else if (s.ty() & mTYthread) // if store in thread local segment
558         {
559             if (config.objfmt == OBJ_ELF)
560             {
561                 s.Sclass = SCcomdef;
562                 objmod.common_block(s, 0, n, 1);
563             }
564             else
565             {
566                 /* COMDEFs not supported in tls segment
567                  * so put them out as COMDATs with initialized 0s
568                  */
569                 s.Sclass = SCcomdat;
570                 auto dtb = DtBuilder(0);
571                 dtb.nzeros(cast(uint)n);
572                 s.Sdt = dtb.finish();
573                 outdata(s);
574 version (SCPP)
575 {
576                 if (config.objfmt == OBJ_OMF)
577                     out_extdef(s);
578 }
579             }
580         }
581         else
582         {
583             s.Sclass = SCcomdef;
584             if (config.objfmt == OBJ_OMF)
585             {
586                 s.Sxtrnnum = objmod.common_block(s,(s.ty() & mTYfar) == 0,n,1);
587                 if (s.ty() & mTYfar)
588                     s.Sfl = FLfardata;
589                 else
590                     s.Sfl = FLextern;
591                 s.Sseg = UNKNOWN;
592                 pstate.STflags |= PFLcomdef;
593 version (SCPP)
594 {
595                 ph_comdef(s);               // notify PH that a COMDEF went out
596 }
597             }
598             else
599                 objmod.common_block(s, 0, n, 1);
600         }
601         if (config.fulltypes)
602             cv_outsym(s);
603     }
604 }
605 
606 /*************************************
607  * Mark a Symbol as going into a read-only segment.
608  */
609 
610 void out_readonly(Symbol *s)
611 {
612     if (config.objfmt == OBJ_ELF || config.objfmt == OBJ_MACH)
613     {
614         /* Cannot have pointers in CDATA when compiling PIC code, because
615          * they require dynamic relocations of the read-only segment.
616          * Instead use the .data.rel.ro section.
617          * https://issues.dlang.org/show_bug.cgi?id=11171
618          */
619         if (config.flags3 & CFG3pic && dtpointers(s.Sdt))
620             s.Sseg = CDATAREL;
621         else
622             s.Sseg = CDATA;
623     }
624     else
625     {
626         s.Sseg = CDATA;
627     }
628 }
629 
630 /*************************************
631  * Write out a readonly string literal in an implementation-defined
632  * manner.
633  * Params:
634  *      str = pointer to string data (need not have terminating 0)
635  *      len = number of characters in string
636  *      sz = size of each character (1, 2 or 4)
637  * Returns: a Symbol pointing to it.
638  */
639 Symbol *out_string_literal(const(char)* str, uint len, uint sz)
640 {
641     tym_t ty = TYchar;
642     if (sz == 2)
643         ty = TYchar16;
644     else if (sz == 4)
645         ty = TYdchar;
646     Symbol *s = symbol_generate(SCstatic,type_static_array(len, tstypes[ty]));
647     switch (config.objfmt)
648     {
649         case OBJ_ELF:
650         case OBJ_MACH:
651             s.Sseg = objmod.string_literal_segment(sz);
652             break;
653 
654         case OBJ_MSCOFF:
655         case OBJ_OMF:   // goes into COMDATs, handled elsewhere
656         default:
657             assert(0);
658     }
659 
660     /* If there are any embedded zeros, this can't go in the special string segments
661      * which assume that 0 is the end of the string.
662      */
663     switch (sz)
664     {
665         case 1:
666             if (memchr(str, 0, len))
667                 s.Sseg = CDATA;
668             break;
669 
670         case 2:
671             foreach (i; 0 .. len)
672             {
673                 auto p = cast(const(ushort)*)str;
674                 if (p[i] == 0)
675                 {
676                     s.Sseg = CDATA;
677                     break;
678                 }
679             }
680             break;
681 
682         case 4:
683             foreach (i; 0 .. len)
684             {
685                 auto p = cast(const(uint)*)str;
686                 if (p[i] == 0)
687                 {
688                     s.Sseg = CDATA;
689                     break;
690                 }
691             }
692             break;
693 
694         default:
695             assert(0);
696     }
697 
698     auto dtb = DtBuilder(0);
699     dtb.nbytes(cast(uint)(len * sz), str);
700     dtb.nzeros(cast(uint)sz);       // include terminating 0
701     s.Sdt = dtb.finish();
702     s.Sfl = FLdata;
703     s.Salignment = sz;
704     outdata(s);
705     return s;
706 }
707 
708 
709 /******************************
710  * Walk expression tree, converting it from a PARSER tree to
711  * a code generator tree.
712  */
713 
714 /*private*/ void outelem(elem *e, ref bool addressOfParam)
715 {
716     Symbol *s;
717     tym_t tym;
718     elem *e1;
719 version (SCPP)
720 {
721     type *t;
722 }
723 
724 again:
725     assert(e);
726     elem_debug(e);
727 
728 debug
729 {
730     if (OTbinary(e.Eoper))
731         assert(e.EV.E1 && e.EV.E2);
732 //    else if (OTunary(e.Eoper))
733 //      assert(e.EV.E1 && !e.EV.E2);
734 }
735 
736 version (SCPP)
737 {
738     t = e.ET;
739     assert(t);
740     type_debug(t);
741     tym = t.Tty;
742     switch (tybasic(tym))
743     {
744         case TYstruct:
745             t.Tcount++;
746             break;
747 
748         case TYarray:
749             t.Tcount++;
750             break;
751 
752         case TYbool:
753         case TYwchar_t:
754         case TYchar16:
755         case TYmemptr:
756         case TYvtshape:
757         case TYnullptr:
758             tym = tym_conv(t);
759             e.ET = null;
760             break;
761 
762         case TYenum:
763             tym = tym_conv(t.Tnext);
764             e.ET = null;
765             break;
766 
767         default:
768             e.ET = null;
769             break;
770     }
771     e.Nflags = 0;
772     e.Ety = tym;
773 }
774 
775     switch (e.Eoper)
776     {
777     default:
778     Lop:
779 debug
780 {
781         //if (!EOP(e)) printf("e.Eoper = x%x\n",e.Eoper);
782 }
783         if (OTbinary(e.Eoper))
784         {   outelem(e.EV.E1, addressOfParam);
785             e = e.EV.E2;
786         }
787         else if (OTunary(e.Eoper))
788         {
789             e = e.EV.E1;
790         }
791         else
792             break;
793 version (SCPP)
794 {
795         type_free(t);
796 }
797         goto again;                     /* iterate instead of recurse   */
798     case OPaddr:
799         e1 = e.EV.E1;
800         if (e1.Eoper == OPvar)
801         {   // Fold into an OPrelconst
802 version (SCPP)
803 {
804             el_copy(e,e1);
805             e.ET = t;
806 }
807 else
808 {
809             tym = e.Ety;
810             el_copy(e,e1);
811             e.Ety = tym;
812 }
813             e.Eoper = OPrelconst;
814             el_free(e1);
815             goto again;
816         }
817         goto Lop;
818 
819     case OPrelconst:
820     case OPvar:
821     L6:
822         s = e.EV.Vsym;
823         assert(s);
824         symbol_debug(s);
825         switch (s.Sclass)
826         {
827             case SCregpar:
828             case SCparameter:
829             case SCshadowreg:
830                 if (e.Eoper == OPrelconst)
831                 {
832                     if (I16)
833                         addressOfParam = true;   // taking addr of param list
834                     else
835                         s.Sflags &= ~(SFLunambig | GTregcand);
836                 }
837                 break;
838 
839             case SCstatic:
840             case SClocstat:
841             case SCextern:
842             case SCglobal:
843             case SCcomdat:
844             case SCcomdef:
845             case SCpseudo:
846             case SCinline:
847             case SCsinline:
848             case SCeinline:
849                 s.Sflags |= SFLlivexit;
850                 goto case;
851             case SCauto:
852             case SCregister:
853             case SCfastpar:
854             case SCbprel:
855                 if (e.Eoper == OPrelconst)
856                 {
857                     s.Sflags &= ~(SFLunambig | GTregcand);
858                 }
859                 else if (s.ty() & mTYfar)
860                     e.Ety |= mTYfar;
861                 break;
862 version (SCPP)
863 {
864             case SCmember:
865                 err_noinstance(s.Sscope,s);
866                 goto L5;
867 
868             case SCstruct:
869                 cpperr(EM_no_instance,s.Sident.ptr);       // no instance of class
870             L5:
871                 e.Eoper = OPconst;
872                 e.Ety = TYint;
873                 return;
874 
875             case SCfuncalias:
876                 e.EV.Vsym = s.Sfunc.Falias;
877                 goto L6;
878 
879             case SCstack:
880                 break;
881 
882             case SCfunctempl:
883                 cpperr(EM_no_template_instance, s.Sident.ptr);
884                 break;
885 
886             default:
887                 symbol_print(s);
888                 WRclass(cast(SC) s.Sclass);
889                 assert(0);
890 }
891 else
892 {
893             default:
894                 break;
895 }
896         }
897 version (SCPP)
898 {
899         if (tyfunc(s.ty()))
900         {
901             nwc_mustwrite(s);           /* must write out function      */
902         }
903         else if (s.Sdt)                /* if initializer for symbol    */
904             outdata(s);                 // write out data for symbol
905         if (config.flags3 & CFG3pic)
906         {
907             objmod.gotref(s);
908         }
909 }
910         break;
911 
912     case OPstring:
913     case OPconst:
914     case OPstrthis:
915         break;
916 
917     case OPsizeof:
918 version (SCPP)
919 {
920         e.Eoper = OPconst;
921         e.EV.Vlong = type_size(e.EV.Vsym.Stype);
922         break;
923 }
924 else
925 {
926         assert(0);
927 }
928 
929 version (SCPP)
930 {
931     case OPstreq:
932     case OPstrpar:
933     case OPstrctor:
934         type_size(e.EV.E1.ET);
935         goto Lop;
936 
937     case OPasm:
938         break;
939 
940     case OPctor:
941         nwc_mustwrite(e.EV.Edtor);
942         goto case;
943     case OPdtor:
944         // Don't put 'this' pointers in registers if we need
945         // them for EH stack cleanup.
946         e1 = e.EV.E1;
947         elem_debug(e1);
948         if (e1.Eoper == OPadd)
949             e1 = e1.EV.E1;
950         if (e1.Eoper == OPvar)
951             e1.EV.Vsym.Sflags &= ~GTregcand;
952         goto Lop;
953 
954     case OPmark:
955         break;
956 }
957     }
958 version (SCPP)
959 {
960     type_free(t);
961 }
962 }
963 
964 /*************************************
965  * Determine register candidates.
966  */
967 
968 void out_regcand(symtab_t *psymtab)
969 {
970     //printf("out_regcand()\n");
971     const bool ifunc = (tybasic(funcsym_p.ty()) == TYifunc);
972     for (SYMIDX si = 0; si < psymtab.length; si++)
973     {   Symbol *s = psymtab.tab[si];
974 
975         symbol_debug(s);
976         //assert(sytab[s.Sclass] & SCSS);      // only stack variables
977         s.Ssymnum = si;                        // Ssymnum trashed by cpp_inlineexpand
978         if (!(s.ty() & (mTYvolatile | mTYshared)) &&
979             !(ifunc && (s.Sclass == SCparameter || s.Sclass == SCregpar)) &&
980             s.Sclass != SCstatic)
981             s.Sflags |= (GTregcand | SFLunambig);      // assume register candidate
982         else
983             s.Sflags &= ~(GTregcand | SFLunambig);
984     }
985 
986     bool addressOfParam = false;                  // haven't taken addr of param yet
987     for (block *b = startblock; b; b = b.Bnext)
988     {
989         if (b.Belem)
990             out_regcand_walk(b.Belem, addressOfParam);
991 
992         // Any assembler blocks make everything ambiguous
993         if (b.BC == BCasm)
994             for (SYMIDX si = 0; si < psymtab.length; si++)
995                 psymtab.tab[si].Sflags &= ~(SFLunambig | GTregcand);
996     }
997 
998     // If we took the address of one parameter, assume we took the
999     // address of all non-register parameters.
1000     if (addressOfParam)                      // if took address of a parameter
1001     {
1002         for (SYMIDX si = 0; si < psymtab.length; si++)
1003             if (psymtab.tab[si].Sclass == SCparameter || psymtab.tab[si].Sclass == SCshadowreg)
1004                 psymtab.tab[si].Sflags &= ~(SFLunambig | GTregcand);
1005     }
1006 
1007 }
1008 
1009 private void out_regcand_walk(elem *e, ref bool addressOfParam)
1010 {
1011     while (1)
1012     {   elem_debug(e);
1013 
1014         if (OTbinary(e.Eoper))
1015         {   if (e.Eoper == OPstreq)
1016             {   if (e.EV.E1.Eoper == OPvar)
1017                 {
1018                     Symbol *s = e.EV.E1.EV.Vsym;
1019                     s.Sflags &= ~(SFLunambig | GTregcand);
1020                 }
1021                 if (e.EV.E2.Eoper == OPvar)
1022                 {
1023                     Symbol *s = e.EV.E2.EV.Vsym;
1024                     s.Sflags &= ~(SFLunambig | GTregcand);
1025                 }
1026             }
1027             out_regcand_walk(e.EV.E1, addressOfParam);
1028             e = e.EV.E2;
1029         }
1030         else if (OTunary(e.Eoper))
1031         {
1032             // Don't put 'this' pointers in registers if we need
1033             // them for EH stack cleanup.
1034             if (e.Eoper == OPctor)
1035             {   elem *e1 = e.EV.E1;
1036 
1037                 if (e1.Eoper == OPadd)
1038                     e1 = e1.EV.E1;
1039                 if (e1.Eoper == OPvar)
1040                     e1.EV.Vsym.Sflags &= ~GTregcand;
1041             }
1042             e = e.EV.E1;
1043         }
1044         else
1045         {   if (e.Eoper == OPrelconst)
1046             {
1047                 Symbol *s = e.EV.Vsym;
1048                 assert(s);
1049                 symbol_debug(s);
1050                 switch (s.Sclass)
1051                 {
1052                     case SCregpar:
1053                     case SCparameter:
1054                     case SCshadowreg:
1055                         if (I16)
1056                             addressOfParam = true;       // taking addr of param list
1057                         else
1058                             s.Sflags &= ~(SFLunambig | GTregcand);
1059                         break;
1060 
1061                     case SCauto:
1062                     case SCregister:
1063                     case SCfastpar:
1064                     case SCbprel:
1065                         s.Sflags &= ~(SFLunambig | GTregcand);
1066                         break;
1067 
1068                     default:
1069                         break;
1070                 }
1071             }
1072             else if (e.Eoper == OPvar)
1073             {
1074                 if (e.EV.Voffset)
1075                 {   if (!(e.EV.Voffset == 1 && tybyte(e.Ety)) &&
1076                         !(e.EV.Voffset == REGSIZE && tysize(e.Ety) == REGSIZE))
1077                     {
1078                         e.EV.Vsym.Sflags &= ~GTregcand;
1079                     }
1080                 }
1081             }
1082             break;
1083         }
1084     }
1085 }
1086 
1087 
1088 /**************************
1089  * Optimize function,
1090  * generate code for it,
1091  * and write it out.
1092  */
1093 
1094 void writefunc(Symbol *sfunc)
1095 {
1096 version (HTOD)
1097 {
1098     return;
1099 }
1100 else version (SCPP)
1101 {
1102     writefunc2(sfunc);
1103 }
1104 else
1105 {
1106     cstate.CSpsymtab = &globsym;
1107     writefunc2(sfunc);
1108     cstate.CSpsymtab = null;
1109 }
1110 }
1111 
1112 private void writefunc2(Symbol *sfunc)
1113 {
1114     func_t *f = sfunc.Sfunc;
1115 
1116     //printf("writefunc(%s)\n",sfunc.Sident.ptr);
1117     debug debugy && printf("writefunc(%s)\n",sfunc.Sident.ptr);
1118 version (SCPP)
1119 {
1120     if (CPP)
1121     {
1122 
1123     // If constructor or destructor, make sure it has been fixed.
1124     if (f.Fflags & (Fctor | Fdtor))
1125         assert(errcnt || f.Fflags & Ffixed);
1126 
1127     // If this function is the 'trigger' to output the vtbl[], do so
1128     if (f.Fflags3 & Fvtblgen && !eecontext.EEcompile)
1129     {
1130         Classsym *stag = cast(Classsym *) sfunc.Sscope;
1131         {
1132             SC scvtbl;
1133 
1134             scvtbl = cast(SC) ((config.flags2 & CFG2comdat) ? SCcomdat : SCglobal);
1135             n2_genvtbl(stag,scvtbl,1);
1136             n2_genvbtbl(stag,scvtbl,1);
1137 static if (SYMDEB_CODEVIEW)
1138 {
1139             if (config.fulltypes == CV4)
1140                 cv4_struct(stag,2);
1141 }
1142         }
1143     }
1144     }
1145 }
1146 
1147     /* Signify that function has been output                    */
1148     /* (before inline_do() to prevent infinite recursion!)      */
1149     f.Fflags &= ~Fpending;
1150     f.Fflags |= Foutput;
1151 
1152 version (SCPP)
1153 {
1154     if (errcnt)
1155         return;
1156 }
1157 
1158     if (eecontext.EEcompile && eecontext.EEfunc != sfunc)
1159         return;
1160 
1161     /* Copy local symbol table onto main one, making sure       */
1162     /* that the symbol numbers are adjusted accordingly */
1163     //printf("f.Flocsym.length = %d\n",f.Flocsym.length);
1164     debug debugy && printf("appending symbols to symtab...\n");
1165     const nsymbols = f.Flocsym.length;
1166     globsym.setLength(nsymbols);
1167     memcpy(&globsym.tab[0],&f.Flocsym.tab[0],nsymbols * (Symbol *).sizeof);
1168 
1169     assert(startblock == null);
1170     if (f.Fflags & Finline)            // if keep function around
1171     {   // Generate copy of function
1172 
1173         block **pb = &startblock;
1174         for (block *bf = f.Fstartblock; bf; bf = bf.Bnext)
1175         {
1176             block *b = block_calloc();
1177             *pb = b;
1178             pb = &b.Bnext;
1179 
1180             *b = *bf;
1181             assert(b.numSucc() == 0);
1182             assert(!b.Bpred);
1183             b.Belem = el_copytree(b.Belem);
1184         }
1185     }
1186     else
1187     {   startblock = sfunc.Sfunc.Fstartblock;
1188         sfunc.Sfunc.Fstartblock = null;
1189     }
1190     assert(startblock);
1191 
1192     /* Do any in-line expansion of function calls inside sfunc  */
1193 version (SCPP)
1194 {
1195     inline_do(sfunc);
1196 }
1197 
1198 version (SCPP)
1199 {
1200     /* If function is _STIxxxx, add in the auto destructors             */
1201     if (cpp_stidtors && memcmp("__SI".ptr,sfunc.Sident.ptr,4) == 0)
1202     {
1203         assert(startblock.Bnext == null);
1204         list_t el = cpp_stidtors;
1205         do
1206         {
1207             startblock.Belem = el_combine(startblock.Belem,list_elem(el));
1208             el = list_next(el);
1209         } while (el);
1210         list_free(&cpp_stidtors,FPNULL);
1211     }
1212 }
1213     assert(funcsym_p == null);
1214     funcsym_p = sfunc;
1215     tym_t tyf = tybasic(sfunc.ty());
1216 
1217 version (SCPP)
1218 {
1219     out_extdef(sfunc);
1220 }
1221 
1222     // TX86 computes parameter offsets in stackoffsets()
1223     //printf("globsym.length = %d\n", globsym.length);
1224 
1225 version (SCPP)
1226 {
1227     FuncParamRegs fpr = FuncParamRegs_create(tyf);
1228 }
1229 
1230     for (SYMIDX si = 0; si < globsym.length; si++)
1231     {   Symbol *s = globsym[si];
1232 
1233         symbol_debug(s);
1234         //printf("symbol %d '%s'\n",si,s.Sident.ptr);
1235 
1236         type_size(s.Stype);    // do any forward template instantiations
1237 
1238         s.Ssymnum = si;        // Ssymnum trashed by cpp_inlineexpand
1239         s.Sflags &= ~(SFLunambig | GTregcand);
1240         switch (s.Sclass)
1241         {
1242             case SCbprel:
1243                 s.Sfl = FLbprel;
1244                 goto L3;
1245 
1246             case SCauto:
1247             case SCregister:
1248                 s.Sfl = FLauto;
1249                 goto L3;
1250 
1251 version (SCPP)
1252 {
1253             case SCfastpar:
1254             case SCregpar:
1255             case SCparameter:
1256                 if (si == 0 && FuncParamRegs_alloc(fpr, s.Stype, s.Stype.Tty, &s.Spreg, &s.Spreg2))
1257                 {
1258                     assert(s.Spreg == ((tyf == TYmfunc) ? CX : AX));
1259                     assert(s.Spreg2 == NOREG);
1260                     assert(si == 0);
1261                     s.Sclass = SCfastpar;
1262                     s.Sfl = FLfast;
1263                     goto L3;
1264                 }
1265                 assert(s.Sclass != SCfastpar);
1266 }
1267 else
1268 {
1269             case SCfastpar:
1270                 s.Sfl = FLfast;
1271                 goto L3;
1272 
1273             case SCregpar:
1274             case SCparameter:
1275             case SCshadowreg:
1276 }
1277                 s.Sfl = FLpara;
1278                 if (tyf == TYifunc)
1279                 {   s.Sflags |= SFLlivexit;
1280                     break;
1281                 }
1282             L3:
1283                 if (!(s.ty() & (mTYvolatile | mTYshared)))
1284                     s.Sflags |= GTregcand | SFLunambig; // assume register candidate   */
1285                 break;
1286 
1287             case SCpseudo:
1288                 s.Sfl = FLpseudo;
1289                 break;
1290 
1291             case SCstatic:
1292                 break;                  // already taken care of by datadef()
1293 
1294             case SCstack:
1295                 s.Sfl = FLstack;
1296                 break;
1297 
1298             default:
1299                 symbol_print(s);
1300                 assert(0);
1301         }
1302     }
1303 
1304     bool addressOfParam = false;  // see if any parameters get their address taken
1305     bool anyasm = false;
1306     for (block *b = startblock; b; b = b.Bnext)
1307     {
1308         memset(&b._BLU,0,block.sizeof - block._BLU.offsetof);
1309         if (b.Belem)
1310         {   outelem(b.Belem, addressOfParam);
1311 version (SCPP)
1312 {
1313             if (!el_returns(b.Belem) && !(config.flags3 & CFG3eh))
1314             {   b.BC = BCexit;
1315                 list_free(&b.Bsucc,FPNULL);
1316             }
1317 }
1318 version (MARS)
1319 {
1320             if (b.Belem.Eoper == OPhalt)
1321             {   b.BC = BCexit;
1322                 list_free(&b.Bsucc,FPNULL);
1323             }
1324 }
1325         }
1326         if (b.BC == BCasm)
1327             anyasm = true;
1328         if (sfunc.Sflags & SFLexit && (b.BC == BCret || b.BC == BCretexp))
1329         {   b.BC = BCexit;
1330             list_free(&b.Bsucc,FPNULL);
1331         }
1332         assert(b != b.Bnext);
1333     }
1334     PARSER = 0;
1335     if (eecontext.EEelem)
1336     {
1337         const marksi = globsym.length;
1338         eecontext.EEin++;
1339         outelem(eecontext.EEelem, addressOfParam);
1340         eecontext.EEelem = doptelem(eecontext.EEelem,true);
1341         eecontext.EEin--;
1342         eecontext_convs(marksi);
1343     }
1344 
1345     // If we took the address of one parameter, assume we took the
1346     // address of all non-register parameters.
1347     if (addressOfParam | anyasm)        // if took address of a parameter
1348     {
1349         for (SYMIDX si = 0; si < globsym.length; si++)
1350             if (anyasm || globsym[si].Sclass == SCparameter)
1351                 globsym[si].Sflags &= ~(SFLunambig | GTregcand);
1352     }
1353 
1354     block_pred();                       // compute predecessors to blocks
1355     block_compbcount();                 // eliminate unreachable blocks
1356     if (go.mfoptim)
1357     {   OPTIMIZER = 1;
1358         optfunc();                      /* optimize function            */
1359         OPTIMIZER = 0;
1360     }
1361     else
1362     {
1363         //printf("blockopt()\n");
1364         blockopt(0);                    /* optimize                     */
1365     }
1366 
1367 version (SCPP)
1368 {
1369     if (CPP)
1370     {
1371         version (DEBUG_XSYMGEN)
1372         {
1373             /* the internal dataview function is allowed to lie about its return value */
1374             enum noret = compile_state != kDataView;
1375         }
1376         else
1377             enum noret = true;
1378 
1379         // Look for any blocks that return nothing.
1380         // Do it after optimization to eliminate any spurious
1381         // messages like the implicit return on { while(1) { ... } }
1382         if (tybasic(funcsym_p.Stype.Tnext.Tty) != TYvoid &&
1383             !(funcsym_p.Sfunc.Fflags & (Fctor | Fdtor | Finvariant))
1384             && noret
1385            )
1386         {
1387             char err = 0;
1388             for (block *b = startblock; b; b = b.Bnext)
1389             {   if (b.BC == BCasm)     // no errors if any asm blocks
1390                     err |= 2;
1391                 else if (b.BC == BCret)
1392                     err |= 1;
1393             }
1394             if (err == 1)
1395                 func_noreturnvalue();
1396         }
1397     }
1398 }
1399     assert(funcsym_p == sfunc);
1400     const int CSEGSAVE_DEFAULT = -10_000;        // some unlikely number
1401     int csegsave = CSEGSAVE_DEFAULT;
1402     if (eecontext.EEcompile != 1)
1403     {
1404         if (symbol_iscomdat2(sfunc))
1405         {
1406             csegsave = cseg;
1407             objmod.comdat(sfunc);
1408             cseg = sfunc.Sseg;
1409         }
1410         else
1411             if (config.flags & CFGsegs) // if user set switch for this
1412             {
1413 version (SCPP)
1414 {
1415                 objmod.codeseg(cpp_mangle(funcsym_p),1);
1416 }
1417 else static if (TARGET_WINDOS)
1418 {
1419                 objmod.codeseg(cast(char*)cpp_mangle(funcsym_p),1);
1420 }
1421 else
1422 {
1423                 objmod.codeseg(funcsym_p.Sident.ptr, 1);
1424 }
1425                                         // generate new code segment
1426             }
1427         cod3_align(cseg);               // align start of function
1428 version (HTOD) { } else
1429 {
1430         objmod.func_start(sfunc);
1431 }
1432         searchfixlist(sfunc);           // backpatch any refs to this function
1433     }
1434 
1435     //printf("codgen()\n");
1436 version (SCPP)
1437 {
1438     if (!errcnt)
1439         codgen(sfunc);                  // generate code
1440 }
1441 else
1442 {
1443     codgen(sfunc);                  // generate code
1444 }
1445     //printf("after codgen for %s Coffset %x\n",sfunc.Sident.ptr,Offset(cseg));
1446     blocklist_free(&startblock);
1447 version (SCPP)
1448 {
1449     PARSER = 1;
1450 }
1451 version (HTOD) { } else
1452 {
1453     objmod.func_term(sfunc);
1454 }
1455     if (eecontext.EEcompile == 1)
1456         goto Ldone;
1457     if (sfunc.Sclass == SCglobal)
1458     {
1459         if ((config.objfmt == OBJ_OMF || config.objfmt == OBJ_MSCOFF) && !(config.flags4 & CFG4allcomdat))
1460         {
1461             assert(sfunc.Sseg == cseg);
1462             objmod.pubdef(sfunc.Sseg,sfunc,sfunc.Soffset);       // make a public definition
1463         }
1464 
1465 version (SCPP)
1466 {
1467 version (Win32)
1468 {
1469         // Determine which startup code to reference
1470         if (!CPP || !isclassmember(sfunc))              // if not member function
1471         {   __gshared const(char)*[6] startup =
1472             [   "__acrtused","__acrtused_winc","__acrtused_dll",
1473                 "__acrtused_con","__wacrtused","__wacrtused_con",
1474             ];
1475             int i;
1476 
1477             const(char)* id = sfunc.Sident.ptr;
1478             switch (id[0])
1479             {
1480                 case 'D': if (strcmp(id,"DllMain"))
1481                                 break;
1482                           if (config.exe == EX_WIN32)
1483                           {     i = 2;
1484                                 goto L2;
1485                           }
1486                           break;
1487 
1488                 case 'm': if (strcmp(id,"main"))
1489                                 break;
1490                           if (config.exe == EX_WIN32)
1491                                 i = 3;
1492                           else if (config.wflags & WFwindows)
1493                                 i = 1;
1494                           else
1495                                 i = 0;
1496                           goto L2;
1497 
1498                 case 'w': if (strcmp(id,"wmain") == 0)
1499                           {
1500                                 if (config.exe == EX_WIN32)
1501                                 {   i = 5;
1502                                     goto L2;
1503                                 }
1504                                 break;
1505                           }
1506                           goto case;
1507                 case 'W': if (stricmp(id,"WinMain") == 0)
1508                           {
1509                                 i = 0;
1510                                 goto L2;
1511                           }
1512                           if (stricmp(id,"wWinMain") == 0)
1513                           {
1514                                 if (config.exe == EX_WIN32)
1515                                 {   i = 4;
1516                                     goto L2;
1517                                 }
1518                           }
1519                           break;
1520 
1521                 case 'L':
1522                 case 'l': if (stricmp(id,"LibMain"))
1523                                 break;
1524                           if (config.exe != EX_WIN32 && config.wflags & WFwindows)
1525                           {     i = 2;
1526                                 goto L2;
1527                           }
1528                           break;
1529 
1530                 L2:     objmod.external_def(startup[i]);          // pull in startup code
1531                         break;
1532 
1533                 default:
1534                     break;
1535             }
1536         }
1537 }
1538 }
1539     }
1540     if (config.wflags & WFexpdef &&
1541         sfunc.Sclass != SCstatic &&
1542         sfunc.Sclass != SCsinline &&
1543         !(sfunc.Sclass == SCinline && !(config.flags2 & CFG2comdat)) &&
1544         sfunc.ty() & mTYexport)
1545         objmod.export_symbol(sfunc,cast(uint)Para.offset);      // export function definition
1546 
1547     if (config.fulltypes && config.fulltypes != CV8)
1548         cv_func(sfunc);                 // debug info for function
1549 
1550 version (MARS)
1551 {
1552     /* This is to make uplevel references to SCfastpar variables
1553      * from nested functions work.
1554      */
1555     for (SYMIDX si = 0; si < globsym.length; si++)
1556     {
1557         Symbol *s = globsym[si];
1558 
1559         switch (s.Sclass)
1560         {   case SCfastpar:
1561                 s.Sclass = SCauto;
1562                 break;
1563 
1564             default:
1565                 break;
1566         }
1567     }
1568     /* After codgen() and writing debug info for the locals,
1569      * readjust the offsets of all stack variables so they
1570      * are relative to the frame pointer.
1571      * Necessary for nested function access to lexically enclosing frames.
1572      */
1573      cod3_adjSymOffsets();
1574 }
1575 
1576     if (symbol_iscomdat2(sfunc))         // if generated a COMDAT
1577     {
1578         assert(csegsave != CSEGSAVE_DEFAULT);
1579         objmod.setcodeseg(csegsave);       // reset to real code seg
1580         if (config.objfmt == OBJ_MACH)
1581             assert(cseg == CODE);
1582     }
1583 
1584     /* Check if function is a constructor or destructor, by     */
1585     /* seeing if the function name starts with _STI or _STD     */
1586     {
1587 version (LittleEndian)
1588 {
1589         short *p = cast(short *) sfunc.Sident.ptr;
1590         if (p[0] == (('S' << 8) | '_') && (p[1] == (('I' << 8) | 'T') || p[1] == (('D' << 8) | 'T')))
1591             objmod.setModuleCtorDtor(sfunc, sfunc.Sident.ptr[3] == 'I');
1592 }
1593 else
1594 {
1595         char *p = sfunc.Sident.ptr;
1596         if (p[0] == '_' && p[1] == 'S' && p[2] == 'T' &&
1597             (p[3] == 'I' || p[3] == 'D'))
1598             objmod.setModuleCtorDtor(sfunc, sfunc.Sident.ptr[3] == 'I');
1599 }
1600     }
1601 
1602 Ldone:
1603     funcsym_p = null;
1604 
1605 version (SCPP)
1606 {
1607     // Free any added symbols
1608     freesymtab(globsym.tab,nsymbols,globsym.length);
1609 }
1610     globsym.length = 0;
1611 
1612     //printf("done with writefunc()\n");
1613     //dfo.dtor();       // save allocation for next time
1614 }
1615 
1616 /*************************
1617  * Align segment offset.
1618  * Input:
1619  *      seg             segment to be aligned
1620  *      datasize        size in bytes of object to be aligned
1621  */
1622 
1623 void alignOffset(int seg,targ_size_t datasize)
1624 {
1625     targ_size_t alignbytes = _align(datasize,Offset(seg)) - Offset(seg);
1626     //printf("seg %d datasize = x%x, Offset(seg) = x%x, alignbytes = x%x\n",
1627       //seg,datasize,Offset(seg),alignbytes);
1628     if (alignbytes)
1629         objmod.lidata(seg,Offset(seg),alignbytes);
1630 }
1631 
1632 /***************************************
1633  * Write data into read-only data segment.
1634  * Return symbol for it.
1635  */
1636 
1637 enum ROMAX = 32;
1638 struct Readonly
1639 {
1640     Symbol *sym;
1641     size_t length;
1642     ubyte[ROMAX] p;
1643 }
1644 
1645 enum RMAX = 16;
1646 private __gshared
1647 {
1648     Readonly[RMAX] readonly;
1649     size_t readonly_length;
1650     size_t readonly_i;
1651 }
1652 
1653 void out_reset()
1654 {
1655     readonly_length = 0;
1656     readonly_i = 0;
1657 }
1658 
1659 Symbol *out_readonly_sym(tym_t ty, void *p, int len)
1660 {
1661 version (HTOD)
1662 {
1663     return null;
1664 }
1665 else
1666 {
1667 static if (0)
1668 {
1669     printf("out_readonly_sym(ty = x%x)\n", ty);
1670     for (int i = 0; i < len; i++)
1671         printf(" [%d] = %02x\n", i, (cast(ubyte*)p)[i]);
1672 }
1673     // Look for previous symbol we can reuse
1674     for (int i = 0; i < readonly_length; i++)
1675     {
1676         Readonly *r = &readonly[i];
1677         if (r.length == len && memcmp(p, r.p.ptr, len) == 0)
1678             return r.sym;
1679     }
1680 
1681     Symbol *s;
1682 
1683 version (MARS)
1684 {
1685     bool cdata = config.objfmt == OBJ_ELF ||
1686                  config.objfmt == OBJ_OMF ||
1687                  config.objfmt == OBJ_MSCOFF;
1688 }
1689 else
1690 {
1691     bool cdata = config.objfmt == OBJ_ELF;
1692 }
1693     if (cdata)
1694     {
1695         /* MACHOBJ can't go here, because the const data segment goes into
1696          * the _TEXT segment, and one cannot have a fixup from _TEXT to _TEXT.
1697          */
1698         s = objmod.sym_cdata(ty, cast(char *)p, len);
1699     }
1700     else
1701     {
1702         uint sz = tysize(ty);
1703 
1704         alignOffset(DATA, sz);
1705         s = symboldata(Offset(DATA),ty | mTYconst);
1706         s.Sseg = DATA;
1707         objmod.write_bytes(SegData[DATA], len, p);
1708         //printf("s.Sseg = %d:x%x\n", s.Sseg, s.Soffset);
1709     }
1710 
1711     if (len <= ROMAX)
1712     {   Readonly *r;
1713 
1714         if (readonly_length < RMAX)
1715         {
1716             r = &readonly[readonly_length];
1717             readonly_length++;
1718         }
1719         else
1720         {   r = &readonly[readonly_i];
1721             readonly_i++;
1722             if (readonly_i >= RMAX)
1723                 readonly_i = 0;
1724         }
1725         r.length = len;
1726         r.sym = s;
1727         memcpy(r.p.ptr, p, len);
1728     }
1729     return s;
1730 }
1731 }
1732 
1733 /*************************************
1734  * Output Symbol as a readonly comdat.
1735  * Params:
1736  *      s = comdat symbol
1737  *      p = pointer to the data to write
1738  *      len = length of that data
1739  *      nzeros = number of trailing zeros to append
1740  */
1741 void out_readonly_comdat(Symbol *s, const(void)* p, uint len, uint nzeros)
1742 {
1743     objmod.readonly_comdat(s);         // create comdat segment
1744     objmod.write_bytes(SegData[s.Sseg], len, cast(void *)p);
1745     objmod.lidata(s.Sseg, len, nzeros);
1746 }
1747 
1748 void Srcpos_print(ref const Srcpos srcpos, const(char)* func)
1749 {
1750     printf("%s(", func);
1751 version (MARS)
1752 {
1753     printf("Sfilename = %s", srcpos.Sfilename ? srcpos.Sfilename : "null".ptr);
1754 }
1755 else
1756 {
1757     const sf = srcpos.Sfilptr ? *srcpos.Sfilptr : null;
1758     printf("Sfilptr = %p (filename = %s)", sf, sf ? sf.SFname : "null".ptr);
1759 }
1760     printf(", Slinnum = %u", srcpos.Slinnum);
1761     printf(")\n");
1762 }
1763 
1764 
1765 }