1 /**
2  * Convert to Intermediate Representation (IR) for the back-end.
3  *
4  * Copyright:   Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved
5  * Authors:     $(LINK2 https://www.digitalmars.com, Walter Bright)
6  * License:     $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
7  * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/_tocsym.d, _toir.d)
8  * Documentation:  https://dlang.org/phobos/dmd_toir.html
9  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/toir.d
10  */
11 
12 module dmd.toir;
13 
14 import core.checkedint;
15 import core.stdc.stdio;
16 import core.stdc.string;
17 import core.stdc.stdlib;
18 
19 import dmd.root.array;
20 import dmd.common.outbuffer;
21 import dmd.root.rmem;
22 
23 import dmd.backend.cdef;
24 import dmd.backend.cc;
25 import dmd.backend.dt;
26 import dmd.backend.el;
27 import dmd.backend.global;
28 import dmd.backend.oper;
29 import dmd.backend.rtlsym;
30 import dmd.backend.symtab : SYMIDX;
31 import dmd.backend.ty;
32 import dmd.backend.type;
33 
34 import dmd.aggregate;
35 import dmd.arraytypes;
36 import dmd.astenums;
37 import dmd.attrib;
38 import dmd.dclass;
39 import dmd.declaration;
40 import dmd.dmangle;
41 import dmd.dmdparams;
42 import dmd.dmodule;
43 import dmd.dstruct;
44 import dmd.dsymbol;
45 import dmd.dtemplate;
46 import dmd.toctype;
47 import dmd.e2ir;
48 import dmd.func;
49 import dmd.globals;
50 import dmd.glue;
51 import dmd.identifier;
52 import dmd.id;
53 import dmd.location;
54 import dmd.mtype;
55 import dmd.target;
56 import dmd.tocvdebug;
57 import dmd.tocsym;
58 
59 alias toSymbol = dmd.tocsym.toSymbol;
60 alias toSymbol = dmd.glue.toSymbol;
61 
62 /****************************************
63  * Our label symbol
64  */
65 
66 struct Label
67 {
68     block *lblock;      // The block to which the label is defined.
69 }
70 
71 /***********************************************************
72  * Collect state variables needed by the intermediate representation (IR)
73  */
74 struct IRState
75 {
76     Module m;                       // module
77     private FuncDeclaration symbol; // function that code is being generate for
78     Symbol* shidden;                // hidden parameter to function
79     Symbol* sthis;                  // 'this' parameter to function (member and nested)
80     Symbol* sclosure;               // pointer to closure instance
81     Blockx* blx;
82     Dsymbols* deferToObj;           // array of Dsymbol's to run toObjFile(bool multiobj) on later
83     elem* ehidden;                  // transmit hidden pointer to CallExp::toElem()
84     Symbol* startaddress;
85     Array!(elem*)* varsInScope;     // variables that are in scope that will need destruction later
86     Label*[void*]* labels;          // table of labels used/declared in function
87     const Param* params;            // command line parameters
88     const Target* target;           // target
89     bool mayThrow;                  // the expression being evaluated may throw
90     bool Cfile;                     // use C semantics
91 
92     this(Module m, FuncDeclaration fd, Array!(elem*)* varsInScope, Dsymbols* deferToObj, Label*[void*]* labels,
93         const Param* params, const Target* target)
94     {
95         this.m = m;
96         this.symbol = fd;
97         this.varsInScope = varsInScope;
98         this.deferToObj = deferToObj;
99         this.labels = labels;
100         this.params = params;
101         this.target = target;
102         mayThrow = global.params.useExceptions
103             && ClassDeclaration.throwable
104             && !(fd && fd.hasNoEH);
105         this.Cfile = m.filetype == FileType.c;
106     }
107 
108     FuncDeclaration getFunc()
109     {
110         return symbol;
111     }
112 
113     /**********************
114      * Returns:
115      *    true if do array bounds checking for the current function
116      */
117     bool arrayBoundsCheck()
118     {
119         if (m.filetype == FileType.c)
120             return false;
121         bool result;
122         final switch (global.params.useArrayBounds)
123         {
124         case CHECKENABLE.off:
125             result = false;
126             break;
127         case CHECKENABLE.on:
128             result = true;
129             break;
130         case CHECKENABLE.safeonly:
131             {
132                 result = false;
133                 FuncDeclaration fd = getFunc();
134                 if (fd)
135                 {
136                     Type t = fd.type;
137                     if (t.ty == Tfunction && (cast(TypeFunction)t).trust == TRUST.safe)
138                         result = true;
139                 }
140                 break;
141             }
142         case CHECKENABLE._default:
143             assert(0);
144         }
145         return result;
146     }
147 
148     /****************************
149      * Returns:
150      *  true if in a nothrow section of code
151      */
152     bool isNothrow()
153     {
154         return !mayThrow;
155     }
156 }
157 
158 extern (C++):
159 
160 /*********************************************
161  * Produce elem which increments the usage count for a particular line.
162  * Sets corresponding bit in bitmap `m.covb[linnum]`.
163  * Used to implement -cov switch (coverage analysis).
164  * Params:
165  *      irs = context
166  *      loc = line and file of what line to show usage for
167  * Returns:
168  *      elem that increments the line count
169  * References:
170  * https://dlang.org/dmd-windows.html#switch-cov
171  */
172 extern (D) elem *incUsageElem(IRState *irs, const ref Loc loc)
173 {
174     uint linnum = loc.linnum;
175 
176     Module m = cast(Module)irs.blx._module;
177     if (!m.cov || !linnum ||
178         loc.filename != m.srcfile.toChars())
179         return null;
180 
181     //printf("cov = %p, covb = %p, linnum = %u\n", m.cov, m.covb, p, linnum);
182 
183     linnum--;           // from 1-based to 0-based
184 
185     /* Set bit in covb[] indicating this is a valid code line number
186      */
187     uint *p = m.covb;
188     if (p)      // covb can be null if it has already been written out to its .obj file
189     {
190         assert(linnum < m.numlines);
191         p += linnum / ((*p).sizeof * 8);
192         *p |= 1 << (linnum & ((*p).sizeof * 8 - 1));
193     }
194 
195     /* Generate: *(m.cov + linnum * 4) += 1
196      */
197     elem *e;
198     e = el_ptr(m.cov);
199     e = el_bin(OPadd, TYnptr, e, el_long(TYuint, linnum * 4));
200     e = el_una(OPind, TYuint, e);
201     e = el_bin(OPaddass, TYuint, e, el_long(TYuint, 1));
202     return e;
203 }
204 
205 /******************************************
206  * Return elem that evaluates to the static frame pointer for function fd.
207  * If fd is a member function, the returned expression will compute the value
208  * of fd's 'this' variable.
209  * 'fdp' is the parent of 'fd' if the frame pointer is being used to call 'fd'.
210  * 'origSc' is the original scope we inlined from.
211  * This routine is critical for implementing nested functions.
212  */
213 elem *getEthis(const ref Loc loc, IRState *irs, Dsymbol fd, Dsymbol fdp = null, Dsymbol origSc = null)
214 {
215     elem *ethis;
216     FuncDeclaration thisfd = irs.getFunc();
217     Dsymbol ctxt0 = fdp ? fdp : fd;                     // follow either of these two
218     Dsymbol ctxt1 = origSc ? origSc.toParent2() : null; // contexts from template arguments
219     if (!fdp) fdp = fd.toParent2();
220     Dsymbol fdparent = fdp;
221 
222     /* These two are compiler generated functions for the in and out contracts,
223      * and are called from an overriding function, not just the one they're
224      * nested inside, so this hack sets fdparent so it'll pass
225      */
226     if (fdparent != thisfd && (fd.ident == Id.require || fd.ident == Id.ensure))
227     {
228         FuncDeclaration fdthis = thisfd;
229         for (size_t i = 0; ; )
230         {
231             if (i == fdthis.foverrides.length)
232             {
233                 if (i == 0)
234                     break;
235                 fdthis = fdthis.foverrides[0];
236                 i = 0;
237                 continue;
238             }
239             if (fdthis.foverrides[i] == fdp)
240             {
241                 fdparent = thisfd;
242                 break;
243             }
244             i++;
245         }
246     }
247 
248     //printf("[%s] getEthis(thisfd = '%s', fd = '%s', fdparent = '%s')\n", loc.toChars(), thisfd.toPrettyChars(), fd.toPrettyChars(), fdparent.toPrettyChars());
249     if (fdparent == thisfd)
250     {
251         /* Going down one nesting level, i.e. we're calling
252          * a nested function from its enclosing function.
253          */
254         if (irs.sclosure && !(fd.ident == Id.require || fd.ident == Id.ensure))
255         {
256             ethis = el_var(irs.sclosure);
257         }
258         else if (irs.sthis)
259         {
260             // We have a 'this' pointer for the current function
261 
262             if (fdp != thisfd)
263             {
264                 /* fdparent (== thisfd) is a derived member function,
265                  * fdp is the overridden member function in base class, and
266                  * fd is the nested function '__require' or '__ensure'.
267                  * Even if there's a closure environment, we should give
268                  * original stack data as the nested function frame.
269                  * See also: SymbolExp.toElem() in e2ir.c (https://issues.dlang.org/show_bug.cgi?id=9383 fix)
270                  */
271                 /* Address of 'sthis' gives the 'this' for the nested
272                  * function.
273                  */
274                 //printf("L%d fd = %s, fdparent = %s, fd.toParent2() = %s\n",
275                 //    __LINE__, fd.toPrettyChars(), fdparent.toPrettyChars(), fdp.toPrettyChars());
276                 assert(fd.ident == Id.require || fd.ident == Id.ensure);
277                 assert(thisfd.hasNestedFrameRefs());
278 
279                 ClassDeclaration cdp = fdp.isThis().isClassDeclaration();
280                 ClassDeclaration cd = thisfd.isThis().isClassDeclaration();
281                 assert(cdp && cd);
282 
283                 int offset;
284                 cdp.isBaseOf(cd, &offset);
285                 assert(offset != ClassDeclaration.OFFSET_RUNTIME);
286                 //printf("%s to %s, offset = %d\n", cd.toChars(), cdp.toChars(), offset);
287                 if (offset)
288                 {
289                     /* https://issues.dlang.org/show_bug.cgi?id=7517: If fdp is declared in interface, offset the
290                      * 'this' pointer to get correct interface type reference.
291                      */
292                     Symbol *stmp = symbol_genauto(TYnptr);
293                     ethis = el_bin(OPadd, TYnptr, el_var(irs.sthis), el_long(TYsize_t, offset));
294                     ethis = el_bin(OPeq, TYnptr, el_var(stmp), ethis);
295                     ethis = el_combine(ethis, el_ptr(stmp));
296                     //elem_print(ethis);
297                 }
298                 else
299                     ethis = el_ptr(irs.sthis);
300             }
301             else if (thisfd.hasNestedFrameRefs())
302             {
303                 /* Local variables are referenced, can't skip.
304                  * Address of 'sthis' gives the 'this' for the nested
305                  * function.
306                  */
307                 ethis = el_ptr(irs.sthis);
308             }
309             else
310             {
311                 /* If no variables in the current function's frame are
312                  * referenced by nested functions, then we can 'skip'
313                  * adding this frame into the linked list of stack
314                  * frames.
315                  */
316                 ethis = el_var(irs.sthis);
317             }
318         }
319         else
320         {
321             /* No 'this' pointer for current function,
322              */
323             if (thisfd.hasNestedFrameRefs())
324             {
325                 /* OPframeptr is an operator that gets the frame pointer
326                  * for the current function, i.e. for the x86 it gets
327                  * the value of EBP
328                  */
329                 ethis = el_long(TYnptr, 0);
330                 ethis.Eoper = OPframeptr;
331 
332                 thisfd.csym.Sfunc.Fflags &= ~Finline; // inliner breaks with this because the offsets are off
333                                                       // see runnable/ice10086b.d
334             }
335             else
336             {
337                 /* Use null if no references to the current function's frame
338                  */
339                 ethis = el_long(TYnptr, 0);
340             }
341         }
342     }
343     else
344     {
345         if (!irs.sthis)                // if no frame pointer for this function
346         {
347             fd.error(loc, "is a nested function and cannot be accessed from `%s`", irs.getFunc().toPrettyChars());
348             return el_long(TYnptr, 0); // error recovery
349         }
350 
351         /* Go up a nesting level, i.e. we need to find the 'this'
352          * of an enclosing function.
353          * Our 'enclosing function' may also be an inner class.
354          */
355         ethis = el_var(irs.sthis);
356         Dsymbol s = thisfd;
357         while (fd != s)
358         {
359             //printf("\ts = '%s'\n", s.toChars());
360             thisfd = s.isFuncDeclaration();
361 
362             if (thisfd)
363             {
364                 /* Enclosing function is a function.
365                  */
366                 // Error should have been caught by front end
367                 assert(thisfd.isNested() || thisfd.vthis);
368 
369                 // pick one context
370                 ethis = fixEthis2(ethis, thisfd, thisfd.followInstantiationContext(ctxt0, ctxt1));
371             }
372             else
373             {
374                 /* Enclosed by an aggregate. That means the current
375                  * function must be a member function of that aggregate.
376                  */
377                 AggregateDeclaration ad = s.isAggregateDeclaration();
378                 if (!ad)
379                 {
380                   Lnoframe:
381                     irs.getFunc().error(loc, "cannot get frame pointer to `%s`", fd.toPrettyChars());
382                     return el_long(TYnptr, 0);      // error recovery
383                 }
384                 ClassDeclaration cd = ad.isClassDeclaration();
385                 ClassDeclaration cdx = fd.isClassDeclaration();
386                 if (cd && cdx && cdx.isBaseOf(cd, null))
387                     break;
388                 StructDeclaration sd = ad.isStructDeclaration();
389                 if (fd == sd)
390                     break;
391                 if (!ad.isNested() || !(ad.vthis || ad.vthis2))
392                     goto Lnoframe;
393 
394                 bool i = ad.followInstantiationContext(ctxt0, ctxt1);
395                 const voffset = i ? ad.vthis2.offset : ad.vthis.offset;
396                 ethis = el_bin(OPadd, TYnptr, ethis, el_long(TYsize_t, voffset));
397                 ethis = el_una(OPind, TYnptr, ethis);
398             }
399             if (fdparent == s.toParentP(ctxt0, ctxt1))
400                 break;
401 
402             /* Remember that frames for functions that have no
403              * nested references are skipped in the linked list
404              * of frames.
405              */
406             FuncDeclaration fdp2 = s.toParentP(ctxt0, ctxt1).isFuncDeclaration();
407             if (fdp2 && fdp2.hasNestedFrameRefs())
408                 ethis = el_una(OPind, TYnptr, ethis);
409 
410             s = s.toParentP(ctxt0, ctxt1);
411             assert(s);
412         }
413     }
414     version (none)
415     {
416         printf("ethis:\n");
417         elem_print(ethis);
418         printf("\n");
419     }
420     return ethis;
421 }
422 
423 /************************
424  * Select one context pointer from a dual-context array
425  * Returns:
426  *      *(ethis + offset);
427  */
428 elem *fixEthis2(elem *ethis, FuncDeclaration fd, bool ctxt2 = false)
429 {
430     if (fd && fd.hasDualContext())
431     {
432         if (ctxt2)
433             ethis = el_bin(OPadd, TYnptr, ethis, el_long(TYsize_t, tysize(TYnptr)));
434         ethis = el_una(OPind, TYnptr, ethis);
435     }
436     return ethis;
437 }
438 
439 /*************************
440  * Initialize the hidden aggregate member, vthis, with
441  * the context pointer.
442  * Returns:
443  *      *(ey + (ethis2 ? ad.vthis2 : ad.vthis).offset) = this;
444  */
445 elem *setEthis(const ref Loc loc, IRState *irs, elem *ey, AggregateDeclaration ad, bool setthis2 = false)
446 {
447     elem *ethis;
448     FuncDeclaration thisfd = irs.getFunc();
449     int offset = 0;
450     Dsymbol adp = setthis2 ? ad.toParent2(): ad.toParentLocal();     // class/func we're nested in
451 
452     //printf("[%s] setEthis(ad = %s, adp = %s, thisfd = %s)\n", loc.toChars(), ad.toChars(), adp.toChars(), thisfd.toChars());
453 
454     if (adp == thisfd)
455     {
456         ethis = getEthis(loc, irs, ad);
457     }
458     else if (thisfd.vthis && !thisfd.hasDualContext() &&
459           (adp == thisfd.toParent2() ||
460            (adp.isClassDeclaration() &&
461             adp.isClassDeclaration().isBaseOf(thisfd.toParent2().isClassDeclaration(), &offset)
462            )
463           )
464         )
465     {
466         /* Class we're new'ing is at the same level as thisfd
467          */
468         assert(offset == 0);    // BUG: should handle this case
469         ethis = el_var(irs.sthis);
470     }
471     else
472     {
473         ethis = getEthis(loc, irs, adp);
474         FuncDeclaration fdp = adp.isFuncDeclaration();
475         if (fdp && fdp.hasNestedFrameRefs())
476             ethis = el_una(OPaddr, TYnptr, ethis);
477     }
478 
479     assert(!setthis2 || ad.vthis2);
480     const voffset = setthis2 ? ad.vthis2.offset : ad.vthis.offset;
481     ey = el_bin(OPadd, TYnptr, ey, el_long(TYsize_t, voffset));
482     ey = el_una(OPind, TYnptr, ey);
483     ey = el_bin(OPeq, TYnptr, ey, ethis);
484     return ey;
485 }
486 
487 enum NotIntrinsic = -1;
488 enum OPtoPrec = OPMAX + 1; // front end only
489 
490 /*******************************************
491  * Convert intrinsic function to operator.
492  * Returns:
493  *      the operator as backend OPER,
494  *      NotIntrinsic if not an intrinsic function,
495  *      OPtoPrec if frontend-only intrinsic
496  */
497 int intrinsic_op(FuncDeclaration fd)
498 {
499     int op = NotIntrinsic;
500     fd = fd.toAliasFunc();
501     if (fd.isDeprecated())
502         return op;
503     //printf("intrinsic_op(%s)\n", name);
504 
505     const Identifier id3 = fd.ident;
506 
507     // Look for [core|std].module.function as id3.id2.id1 ...
508     auto m = fd.getModule();
509     if (!m || !m.md)
510         return op;
511 
512     const md = m.md;
513     const Identifier id2 = md.id;
514 
515     if (md.packages.length == 0)
516         return op;
517 
518     // get type of first argument
519     auto tf = fd.type ? fd.type.isTypeFunction() : null;
520     auto param1 = tf && tf.parameterList.length > 0 ? tf.parameterList[0] : null;
521     auto argtype1 = param1 ? param1.type : null;
522 
523     const Identifier id1 = md.packages[0];
524     // ... except std.math package and core.stdc.stdarg.va_start.
525     if (md.packages.length == 2)
526     {
527         // Matches any module in std.math.*
528         if (md.packages[1] == Id.math && id1 == Id.std)
529         {
530             goto Lstdmath;
531         }
532         goto Lva_start;
533     }
534 
535     if (id1 == Id.std && id2 == Id.math)
536     {
537     Lstdmath:
538         if (argtype1 is Type.tfloat80 || id3 == Id._sqrt)
539             goto Lmath;
540         if (id3 == Id.fabs &&
541             (argtype1 is Type.tfloat32 || argtype1 is Type.tfloat64))
542         {
543             op = OPabs;
544         }
545     }
546     else if (id1 == Id.core)
547     {
548         if (id2 == Id.math)
549         {
550         Lmath:
551             if (argtype1 is Type.tfloat80 || argtype1 is Type.tfloat32 || argtype1 is Type.tfloat64)
552             {
553                      if (id3 == Id.cos)    op = OPcos;
554                 else if (id3 == Id.sin)    op = OPsin;
555                 else if (id3 == Id.fabs)   op = OPabs;
556                 else if (id3 == Id.rint)   op = OPrint;
557                 else if (id3 == Id._sqrt)  op = OPsqrt;
558                 else if (id3 == Id.yl2x)   op = OPyl2x;
559                 else if (id3 == Id.ldexp)  op = OPscale;
560                 else if (id3 == Id.rndtol) op = OPrndtol;
561                 else if (id3 == Id.yl2xp1) op = OPyl2xp1;
562                 else if (id3 == Id.toPrec) op = OPtoPrec;
563             }
564         }
565         else if (id2 == Id.simd)
566         {
567                  if (id3 == Id.__prefetch) op = OPprefetch;
568             else if (id3 == Id.__simd_sto) op = OPvector;
569             else if (id3 == Id.__simd)     op = OPvector;
570             else if (id3 == Id.__simd_ib)  op = OPvector;
571         }
572         else if (id2 == Id.bitop)
573         {
574                  if (id3 == Id.volatileLoad)  op = OPind;
575             else if (id3 == Id.volatileStore) op = OPeq;
576 
577             else if (id3 == Id.bsf) op = OPbsf;
578             else if (id3 == Id.bsr) op = OPbsr;
579             else if (id3 == Id.btc) op = OPbtc;
580             else if (id3 == Id.btr) op = OPbtr;
581             else if (id3 == Id.bts) op = OPbts;
582 
583             else if (id3 == Id.inp)  op = OPinp;
584             else if (id3 == Id.inpl) op = OPinp;
585             else if (id3 == Id.inpw) op = OPinp;
586 
587             else if (id3 == Id.outp)  op = OPoutp;
588             else if (id3 == Id.outpl) op = OPoutp;
589             else if (id3 == Id.outpw) op = OPoutp;
590 
591             else if (id3 == Id.bswap)   op = OPbswap;
592             else if (id3 == Id._popcnt) op = OPpopcnt;
593         }
594         else if (id2 == Id..volatile)
595         {
596                  if (id3 == Id.volatileLoad)  op = OPind;
597             else if (id3 == Id.volatileStore) op = OPeq;
598         }
599     }
600 
601     if (!target.is64bit)
602     // No 64-bit bsf bsr in 32bit mode
603     {
604         if ((op == OPbsf || op == OPbsr) && argtype1 is Type.tuns64)
605             return NotIntrinsic;
606     }
607     return op;
608 
609 Lva_start:
610     if (target.is64bit &&
611         fd.toParent().isTemplateInstance() &&
612         id3 == Id.va_start &&
613         id2 == Id.stdarg &&
614         md.packages[1] == Id.stdc &&
615         id1 == Id.core)
616     {
617         return OPva_start;
618     }
619     return op;
620 }
621 
622 /**************************************
623  * Given an expression e that is an array,
624  * determine and set the 'length' variable.
625  * Input:
626  *      lengthVar       Symbol of 'length' variable
627  *      &e      expression that is the array
628  *      t1      Type of the array
629  * Output:
630  *      e       is rewritten to avoid side effects
631  * Returns:
632  *      expression that initializes 'length'
633  */
634 elem *resolveLengthVar(VarDeclaration lengthVar, elem **pe, Type t1)
635 {
636     //printf("resolveLengthVar()\n");
637     elem *einit = null;
638 
639     if (lengthVar && !(lengthVar.storage_class & STC.const_))
640     {
641         elem *elength;
642         Symbol *slength;
643 
644         if (t1.ty == Tsarray)
645         {
646             TypeSArray tsa = cast(TypeSArray)t1;
647             dinteger_t length = tsa.dim.toInteger();
648 
649             elength = el_long(TYsize_t, length);
650             goto L3;
651         }
652         else if (t1.ty == Tarray)
653         {
654             elength = *pe;
655             *pe = el_same(&elength);
656             elength = el_una(target.is64bit ? OP128_64 : OP64_32, TYsize_t, elength);
657 
658         L3:
659             slength = toSymbol(lengthVar);
660             if (slength.Sclass == SC.auto_ && slength.Ssymnum == SYMIDX.max)
661                 symbol_add(slength);
662 
663             einit = el_bin(OPeq, TYsize_t, el_var(slength), elength);
664         }
665     }
666     return einit;
667 }
668 
669 /*************************************
670  * for a nested function 'fd' return the type of the closure
671  * of an outer function or aggregate. If the function is a member function
672  * the 'this' type is expected to be stored in 'sthis.Sthis'.
673  * It is always returned if it is not a void pointer.
674  * buildClosure() must have been called on the outer function before.
675  *
676  * Params:
677  *      sthis = the symbol of the current 'this' derived from fd.vthis
678  *      fd = the nested function
679  */
680 TYPE* getParentClosureType(Symbol* sthis, FuncDeclaration fd)
681 {
682     if (sthis)
683     {
684         // only replace void*
685         if (sthis.Stype.Tty != TYnptr || sthis.Stype.Tnext.Tty != TYvoid)
686             return sthis.Stype;
687     }
688     for (Dsymbol sym = fd.toParent2(); sym; sym = sym.toParent2())
689     {
690         if (auto fn = sym.isFuncDeclaration())
691             if (fn.csym && fn.csym.Sscope)
692                 return fn.csym.Sscope.Stype;
693         if (sym.isAggregateDeclaration())
694             break;
695     }
696     return sthis ? sthis.Stype : Type_toCtype(Type.tvoidptr);
697 }
698 
699 /**************************************
700  * Go through the variables in function fd that are
701  * to be allocated in a closure, and set the .offset fields
702  * for those variables to their positions relative to the start
703  * of the closure instance.
704  * Also turns off nrvo for closure variables.
705  * Params:
706  *      fd = function
707  * Returns:
708  *      overall alignment of the closure
709  */
710 uint setClosureVarOffset(FuncDeclaration fd)
711 {
712     // Nothing to do
713     if (!fd.needsClosure())
714         return 0;
715 
716     uint offset = target.ptrsize;      // leave room for previous sthis
717     uint aggAlignment = offset;        // overall alignment for the closure
718 
719     foreach (v; fd.closureVars)
720     {
721         /* Align and allocate space for v in the closure
722          * just like AggregateDeclaration.addField() does.
723          */
724         uint memsize;
725         uint memalignsize;
726         structalign_t xalign;
727         if (v.storage_class & STC.lazy_)
728         {
729             /* Lazy variables are really delegates,
730              * so give same answers that TypeDelegate would
731              */
732             memsize = target.ptrsize * 2;
733             memalignsize = memsize;
734             xalign.setDefault();
735         }
736         else if (v.storage_class & (STC.out_ | STC.ref_))
737         {
738             // reference parameters are just pointers
739             memsize = target.ptrsize;
740             memalignsize = memsize;
741             xalign.setDefault();
742         }
743         else
744         {
745             memsize = cast(uint)v.type.size();
746             memalignsize = v.type.alignsize();
747             xalign = v.alignment;
748         }
749         AggregateDeclaration.alignmember(xalign, memalignsize, &offset);
750         v.offset = offset;
751         //printf("closure var %s, offset = %d\n", v.toChars(), v.offset);
752 
753         offset += memsize;
754 
755         uint actualAlignment = xalign.isDefault() ? memalignsize : xalign.get();
756         if (aggAlignment < actualAlignment)
757             aggAlignment = actualAlignment;     // take the largest
758 
759         /* Can't do nrvo if the variable is put in a closure, since
760          * what the shidden points to may no longer exist.
761          */
762         assert(!fd.isNRVO() || fd.nrvo_var != v);
763     }
764     return aggAlignment;
765 }
766 
767 /*************************************
768  * Closures are implemented by taking the local variables that
769  * need to survive the scope of the function, and copying them
770  * into a gc allocated chuck of memory. That chunk, called the
771  * closure here, is inserted into the linked list of stack
772  * frames instead of the usual stack frame.
773  *
774  * buildClosure() inserts code just after the function prolog
775  * is complete. It allocates memory for the closure, allocates
776  * a local variable (sclosure) to point to it, inserts into it
777  * the link to the enclosing frame, and copies into it the parameters
778  * that are referred to in nested functions.
779  * In VarExp::toElem and SymOffExp::toElem, when referring to a
780  * variable that is in a closure, takes the offset from sclosure rather
781  * than from the frame pointer.
782  *
783  * getEthis() and NewExp::toElem need to use sclosure, if set, rather
784  * than the current frame pointer.
785  */
786 void buildClosure(FuncDeclaration fd, IRState *irs)
787 {
788     //printf("buildClosure(fd = %s)\n", fd.toChars());
789     const oldValue = fd.requiresClosure;
790     if (fd.needsClosure())
791     {
792         /* nrvo is incompatible with closure
793          */
794         if (oldValue != fd.requiresClosure && (fd.nrvo_var || irs.params.betterC))
795         {
796             /* https://issues.dlang.org/show_bug.cgi?id=23112
797              * This can shift due to templates being expanded that access alias symbols.
798              */
799             fd.checkClosure();   // give decent diagnostic
800         }
801 
802         auto aggAlignment = setClosureVarOffset(fd);
803 
804         // Generate closure on the heap
805         // BUG: doesn't capture variadic arguments passed to this function
806 
807         /* BUG: doesn't handle destructors for the local variables.
808          * The way to do it is to make the closure variables the fields
809          * of a class object:
810          *    class Closure {
811          *        vtbl[]
812          *        monitor
813          *        ptr to destructor
814          *        sthis
815          *        ... closure variables ...
816          *        ~this() { call destructor }
817          *    }
818          */
819         //printf("FuncDeclaration.buildClosure() %s\n", fd.toChars());
820 
821         /* Generate type name for closure struct */
822         const char *name1 = "CLOSURE.";
823         const char *name2 = fd.toPrettyChars();
824         size_t namesize = strlen(name1)+strlen(name2)+1;
825         char *closname = cast(char *)Mem.check(calloc(namesize, char.sizeof));
826         strcat(strcat(closname, name1), name2);
827 
828         /* Build type for closure */
829         type *Closstru = type_struct_class(closname, target.ptrsize, 0, null, null, false, false, true, false);
830         free(closname);
831         auto chaintype = getParentClosureType(irs.sthis, fd);
832         symbol_struct_addField(Closstru.Ttag, "__chain", chaintype, 0);
833 
834         Symbol *sclosure;
835         sclosure = symbol_name("__closptr", SC.auto_, type_pointer(Closstru));
836         sclosure.Sflags |= SFLtrue | SFLfree;
837         symbol_add(sclosure);
838         irs.sclosure = sclosure;
839 
840         assert(fd.closureVars.length);
841         assert(fd.closureVars[0].offset >= target.ptrsize);
842         foreach (v; fd.closureVars)
843         {
844             //printf("closure var %s\n", v.toChars());
845             v.inClosure = true;
846 
847             // Hack for the case fail_compilation/fail10666.d,
848             // until proper issue 5730 fix will come.
849             bool isScopeDtorParam = v.edtor && (v.storage_class & STC.parameter);
850             if (v.needsScopeDtor() || isScopeDtorParam)
851             {
852                 /* Because the value needs to survive the end of the scope!
853                  */
854                 v.error("has scoped destruction, cannot build closure");
855             }
856             if (v.isargptr)
857             {
858                 /* See https://issues.dlang.org/show_bug.cgi?id=2479
859                  * This is actually a bug, but better to produce a nice
860                  * message at compile time rather than memory corruption at runtime
861                  */
862                 v.error("cannot reference variadic arguments from closure");
863             }
864 
865             /* Set Sscope to closure */
866             Symbol *vsym = toSymbol(v);
867             assert(vsym.Sscope == null);
868             vsym.Sscope = sclosure;
869 
870             /* Add variable as closure type member */
871             symbol_struct_addField(Closstru.Ttag, &vsym.Sident[0], vsym.Stype, v.offset);
872             //printf("closure field %s: memalignsize: %i, offset: %i\n", &vsym.Sident[0], memalignsize, v.offset);
873         }
874 
875         // Calculate the size of the closure
876         VarDeclaration  vlast = fd.closureVars[fd.closureVars.length - 1];
877         typeof(Type.size()) lastsize;
878         if (vlast.storage_class & STC.lazy_)
879             lastsize = target.ptrsize * 2;
880         else if (vlast.isReference)
881             lastsize = target.ptrsize;
882         else
883             lastsize = vlast.type.size();
884         bool overflow;
885         auto structsize = addu(vlast.offset, lastsize, overflow);
886         assert(!overflow && structsize <= uint.max);
887         //printf("structsize = %d\n", cast(uint)structsize);
888 
889         Closstru.Ttag.Sstruct.Sstructsize = cast(uint)structsize;
890         fd.csym.Sscope = sclosure;
891 
892         if (driverParams.symdebug)
893             toDebugClosure(Closstru.Ttag);
894 
895         // Add extra size so we can align it
896         enum GC_ALIGN = 16;     // gc aligns on 16 bytes
897         if (aggAlignment > GC_ALIGN)
898             structsize += aggAlignment - GC_ALIGN;
899 
900         // Allocate memory for the closure
901         elem *e = el_long(TYsize_t, structsize);
902         e = el_bin(OPcall, TYnptr, el_var(getRtlsym(RTLSYM.ALLOCMEMORY)), e);
903         toTraceGC(irs, e, fd.loc);
904 
905         // Align it
906         if (aggAlignment > GC_ALIGN)
907         {
908             // e + (aggAlignment - 1) & ~(aggAlignment - 1)
909             e = el_bin(OPadd, TYsize_t, e, el_long(TYsize_t, aggAlignment - 1));
910             e = el_bin(OPand, TYsize_t, e, el_long(TYsize_t, ~(aggAlignment - 1L)));
911         }
912 
913         // Assign block of memory to sclosure
914         //    sclosure = allocmemory(sz);
915         e = el_bin(OPeq, TYvoid, el_var(sclosure), e);
916 
917         // Set the first element to sthis
918         //    *(sclosure + 0) = sthis;
919         elem *ethis;
920         if (irs.sthis)
921             ethis = el_var(irs.sthis);
922         else
923             ethis = el_long(TYnptr, 0);
924         elem *ex = el_una(OPind, TYnptr, el_var(sclosure));
925         ex = el_bin(OPeq, TYnptr, ex, ethis);
926         e = el_combine(e, ex);
927 
928         // Copy function parameters into closure
929         foreach (v; fd.closureVars)
930         {
931             if (!v.isParameter())
932                 continue;
933             tym_t tym = totym(v.type);
934             const x64ref = ISX64REF(v);
935             if (x64ref && config.exe == EX_WIN64)
936             {
937                 if (v.storage_class & STC.lazy_)
938                     tym = TYdelegate;
939             }
940             else if (ISREF(v) && !x64ref)
941                 tym = TYnptr;   // reference parameters are just pointers
942             else if (v.storage_class & STC.lazy_)
943                 tym = TYdelegate;
944             ex = el_bin(OPadd, TYnptr, el_var(sclosure), el_long(TYsize_t, v.offset));
945             ex = el_una(OPind, tym, ex);
946             elem *ev = el_var(toSymbol(v));
947             if (x64ref)
948             {
949                 ev.Ety = TYnref;
950                 ev = el_una(OPind, tym, ev);
951                 if (tybasic(ev.Ety) == TYstruct || tybasic(ev.Ety) == TYarray)
952                     ev.ET = Type_toCtype(v.type);
953             }
954             if (tybasic(ex.Ety) == TYstruct || tybasic(ex.Ety) == TYarray)
955             {
956                 .type *t = Type_toCtype(v.type);
957                 ex.ET = t;
958                 ex = el_bin(OPstreq, tym, ex, ev);
959                 ex.ET = t;
960             }
961             else
962                 ex = el_bin(OPeq, tym, ex, ev);
963 
964             e = el_combine(e, ex);
965         }
966 
967         block_appendexp(irs.blx.curblock, e);
968     }
969 }
970 
971 /**************************************
972  * Go through the variables in function fd that are
973  * to be allocated in an aligned section, and set the .offset fields
974  * for those variables to their positions relative to the start
975  * of the aligned section instance.
976  * Params:
977  *      fd = function
978  * Returns:
979  *      overall alignment of the align section
980  * Reference:
981  *      setClosureVarOffset
982  */
983 uint setAlignSectionVarOffset(FuncDeclaration fd)
984 {
985     // Nothing to do
986     if (!fd.alignSectionVars)
987         return 0;
988 
989     uint offset = 0;
990     uint aggAlignment = offset;        // overall alignment for the closure
991 
992     // first go through and find overall alignment for the entire section
993     foreach (v; (*fd.alignSectionVars)[])
994     {
995         if (v.inClosure)
996             continue;
997 
998         /* Align and allocate space for v in the align closure
999          * just like AggregateDeclaration.addField() does.
1000          */
1001         const memsize = cast(uint)v.type.size();
1002         const memalignsize = v.type.alignsize();
1003         const xalign = v.alignment;
1004 
1005         AggregateDeclaration.alignmember(xalign, memalignsize, &offset);
1006         v.offset = offset;
1007         //printf("align closure var %s, offset = %d\n", v.toChars(), offset);
1008 
1009         offset += memsize;
1010 
1011         uint actualAlignment = xalign.isDefault() ? memalignsize : xalign.get();
1012         //printf("actualAlignment = x%x, x%x\n", actualAlignment, xalign.get());
1013         if (aggAlignment < actualAlignment)
1014             aggAlignment = actualAlignment;     // take the largest
1015     }
1016 
1017     return aggAlignment;
1018 }
1019 
1020 /*************************************
1021  * Aligned sections are implemented by taking the local variables that
1022  * need alignment that is larger than the stack alignment.
1023  * They are allocated into a separate chunk of memory on the stack
1024  * called an align section, which is aligned on function entry.
1025  *
1026  * buildAlignSection() inserts code just after the function prolog
1027  * is complete. It allocates memory for the align closure by making
1028  * a local stack variable to contain that memory, allocates
1029  * a local variable (salignSection) to point to it.
1030  * In VarExp::toElem and SymOffExp::toElem, when referring to a
1031  * variable that is in an align closure, take the offset from salignSection rather
1032  * than from the frame pointer.
1033  * A variable cannot be in both a closure and an align section. They go in the closure
1034  * and then that closure is aligned.
1035  *
1036  * getEthis() and NewExp::toElem need to use sclosure, if set, rather
1037  * than the current frame pointer??
1038  *
1039  * Run after buildClosure, as buildClosure gets first dibs on inAlignSection variables
1040  * Params:
1041  *      fd = function in which all this occurs
1042  *      irs = state of the intermediate code generation
1043  * Reference:
1044  *      buildClosure() is very similar.
1045  *
1046  *      https://github.com/dlang/dmd/pull/9143 was an incomplete attempt to solve this problem
1047  *      that was merged. It should probably be removed.
1048  */
1049 void buildAlignSection(FuncDeclaration fd, ref IRState irs)
1050 {
1051     enum log = false;
1052     if (log) printf("buildAlignSection(fd = %s)\n", fd.toChars());
1053     if (!fd.alignSectionVars)
1054         return;
1055     auto alignSectionVars = (*fd.alignSectionVars)[];
1056 
1057     /* If they're all in a closure, don't need to build an align section
1058      */
1059     foreach (v; alignSectionVars)
1060     {
1061         if (!v.inClosure)
1062             goto L1;
1063     }
1064     return;
1065   L1:
1066 
1067     auto stackAlign = target.stackAlign();
1068     auto aggAlignment = setAlignSectionVarOffset(fd);
1069 
1070     /* Generate type name for align closure struct */
1071     const char *name1 = "ALIGNSECTION.";
1072     const char *name2 = fd.toPrettyChars();
1073     size_t namesize = strlen(name1)+strlen(name2)+1;
1074     char *closname = cast(char *)Mem.check(calloc(namesize, char.sizeof));
1075     strcat(strcat(closname, name1), name2);
1076 
1077     /* Build type for aligned section */
1078     type *Closstru = type_struct_class(closname, stackAlign, 0, null, null, false, false, true, false);
1079     free(closname);
1080 
1081     Symbol *sclosure;
1082     type* t = type_pointer(Closstru);
1083     type_setcv(&t, t.Tty | mTYvolatile);        // so optimizer doesn't delete it
1084     sclosure = symbol_name("__alignsecptr", SC.auto_, t);
1085     sclosure.Sflags |= SFLtrue | SFLfree;
1086     symbol_add(sclosure);
1087     fd.salignSection = sclosure;
1088 
1089     foreach (v; alignSectionVars)
1090     {
1091         if (v.inClosure)
1092             continue;
1093 
1094         if (log) printf("align section var %s\n", v.toChars());
1095         v.inAlignSection = true;
1096 
1097         Symbol *vsym = toSymbol(v);
1098         assert(vsym.Sscope == null);
1099         vsym.Sscope = sclosure;
1100 
1101         /* Add variable as align section type member */
1102         symbol_struct_addField(Closstru.Ttag, &vsym.Sident[0], vsym.Stype, v.offset);
1103         if (log) printf("align section field %s: offset: %i\n", &vsym.Sident[0], v.offset);
1104     }
1105 
1106     // Calculate the size of the align section
1107     VarDeclaration  vlast = alignSectionVars[$ - 1];
1108     typeof(Type.size()) lastsize;
1109     lastsize = vlast.type.size();
1110     bool overflow;
1111     auto structsize = addu(vlast.offset, lastsize, overflow);
1112     assert(!overflow && structsize <= uint.max);
1113     if (log) printf("structsize = %d\n", cast(uint)structsize);
1114 
1115     // Add extra size so we can align it
1116     if (log) printf("aggAlignment: x%x, stackAlign: x%x\n", aggAlignment, stackAlign);
1117     assert(aggAlignment > stackAlign);
1118     structsize += aggAlignment - stackAlign;
1119 
1120     Closstru.Ttag.Sstruct.Sstructsize = cast(uint)structsize;
1121     fd.csym.Sscope = sclosure;
1122 
1123     if (driverParams.symdebug)
1124         toDebugClosure(Closstru.Ttag);
1125 
1126     // Create Symbol that is an instance of the align closure
1127     Symbol *salignSectionInstance = symbol_name("__alignsec", SC.auto_, Closstru);
1128     salignSectionInstance.Sflags |= SFLtrue | SFLfree;
1129     symbol_add(salignSectionInstance);
1130 
1131     elem *e = el_ptr(salignSectionInstance);
1132 
1133     /* Align it
1134      * e + (aggAlignment - 1) & ~(aggAlignment - 1)
1135      */
1136     e = el_bin(OPadd, TYsize_t, e, el_long(TYsize_t, aggAlignment - 1));
1137     e = el_bin(OPand, TYsize_t, e, el_long(TYsize_t, ~(aggAlignment - 1L)));
1138 
1139     // Assign pointer to align section instance to sclosure
1140     //    salignSection = allocmemory(sz);
1141     e = el_bin(OPeq, TYvoid, el_var(sclosure), e);
1142 
1143     block_appendexp(irs.blx.curblock, e);
1144 }
1145 
1146 /*************************************
1147  * build a debug info struct for variables captured by nested functions,
1148  * but not in a closure.
1149  * must be called after generating the function to fill stack offsets
1150  * Params:
1151  *      fd = function
1152  */
1153 void buildCapture(FuncDeclaration fd)
1154 {
1155     if (!driverParams.symdebug)
1156         return;
1157     if (target.objectFormat() != Target.ObjectFormat.coff)  // toDebugClosure only implemented for CodeView,
1158         return;                 //  but optlink crashes for negative field offsets
1159 
1160     if (fd.closureVars.length && !fd.needsClosure)
1161     {
1162         /* Generate type name for struct with captured variables */
1163         const char *name1 = "CAPTURE.";
1164         const char *name2 = fd.toPrettyChars();
1165         size_t namesize = strlen(name1)+strlen(name2)+1;
1166         char *capturename = cast(char *)Mem.check(calloc(namesize, char.sizeof));
1167         strcat(strcat(capturename, name1), name2);
1168 
1169         /* Build type for struct */
1170         type *capturestru = type_struct_class(capturename, target.ptrsize, 0, null, null, false, false, true, false);
1171         free(capturename);
1172 
1173         foreach (v; fd.closureVars)
1174         {
1175             Symbol *vsym = toSymbol(v);
1176 
1177             /* Add variable as capture type member */
1178             auto soffset = vsym.Soffset;
1179             if (fd.vthis)
1180                 soffset -= toSymbol(fd.vthis).Soffset; // see toElem.ToElemVisitor.visit(SymbolExp)
1181             symbol_struct_addField(capturestru.Ttag, &vsym.Sident[0], vsym.Stype, cast(uint)soffset);
1182             //printf("capture field %s: offset: %i\n", &vsym.Sident[0], v.offset);
1183         }
1184 
1185         // generate pseudo symbol to put into functions' Sscope
1186         Symbol *scapture = symbol_name("__captureptr", SC.alias_, type_pointer(capturestru));
1187         scapture.Sflags |= SFLtrue | SFLfree;
1188         //symbol_add(scapture);
1189         fd.csym.Sscope = scapture;
1190 
1191         toDebugClosure(capturestru.Ttag);
1192     }
1193 }
1194 
1195 
1196 /***************************
1197  * Determine return style of function - whether in registers or
1198  * through a hidden pointer to the caller's stack.
1199  * Params:
1200  *   tf = function type to check
1201  *   needsThis = true if the function type is for a non-static member function
1202  * Returns:
1203  *   RET.stack if return value from function is on the stack, RET.regs otherwise
1204  */
1205 RET retStyle(TypeFunction tf, bool needsThis)
1206 {
1207     //printf("TypeFunction.retStyle() %s\n", toChars());
1208     return target.isReturnOnStack(tf, needsThis) ? RET.stack : RET.regs;
1209 }