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