/** * Compiler implementation of the * $(LINK2 http://www.dlang.org, D programming language). * * Copyright: Copyright (c) 1999-2016 by Digital Mars, All Rights Reserved * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright) * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/_tocsym.d, _toir.d) */ module ddmd.toir; import core.stdc.stdio; import core.stdc.string; import core.stdc.stdlib; import ddmd.root.array; import ddmd.root.outbuffer; import ddmd.root.rmem; import ddmd.backend.cdef; import ddmd.backend.cc; import ddmd.backend.dt; import ddmd.backend.el; import ddmd.backend.global; import ddmd.backend.oper; import ddmd.backend.rtlsym; import ddmd.backend.ty; import ddmd.backend.type; import ddmd.aggregate; import ddmd.arraytypes; import ddmd.dclass; import ddmd.declaration; import ddmd.dmangle; import ddmd.dmodule; import ddmd.dstruct; import ddmd.dsymbol; import ddmd.dtemplate; import ddmd.toctype; import ddmd.e2ir; import ddmd.func; import ddmd.globals; import ddmd.glue; import ddmd.identifier; import ddmd.id; import ddmd.irstate; import ddmd.mtype; import ddmd.target; import ddmd.tocsym; alias toSymbol = ddmd.tocsym.toSymbol; alias toSymbol = ddmd.glue.toSymbol; extern (C++): /********************************************* * Produce elem which increments the usage count for a particular line. * Used to implement -cov switch (coverage analysis). */ elem *incUsageElem(IRState *irs, Loc loc) { uint linnum = loc.linnum; Module m = cast(Module)irs.blx._module; if (!m.cov || !linnum || loc.filename != m.srcfile.toChars()) return null; //printf("cov = %p, covb = %p, linnum = %u\n", m.cov, m.covb, p, linnum); linnum--; // from 1-based to 0-based /* Set bit in covb[] indicating this is a valid code line number */ uint *p = m.covb; if (p) // covb can be null if it has already been written out to its .obj file { assert(linnum < m.numlines); p += linnum / ((*p).sizeof * 8); *p |= 1 << (linnum & ((*p).sizeof * 8 - 1)); } elem *e; e = el_ptr(m.cov); e = el_bin(OPadd, TYnptr, e, el_long(TYuint, linnum * 4)); e = el_una(OPind, TYuint, e); e = el_bin(OPaddass, TYuint, e, el_long(TYuint, 1)); return e; } /****************************************** * Return elem that evaluates to the static frame pointer for function fd. * If fd is a member function, the returned expression will compute the value * of fd's 'this' variable. * This routine is critical for implementing nested functions. */ elem *getEthis(Loc loc, IRState *irs, Dsymbol fd) { elem *ethis; FuncDeclaration thisfd = irs.getFunc(); Dsymbol fdparent = fd.toParent2(); Dsymbol fdp = fdparent; /* These two are compiler generated functions for the in and out contracts, * and are called from an overriding function, not just the one they're * nested inside, so this hack is so they'll pass */ if (fdparent != thisfd && (fd.ident == Id.require || fd.ident == Id.ensure)) { FuncDeclaration fdthis = thisfd; for (size_t i = 0; ; ) { if (i == fdthis.foverrides.dim) { if (i == 0) break; fdthis = fdthis.foverrides[0]; i = 0; continue; } if (fdthis.foverrides[i] == fdp) { fdparent = thisfd; break; } i++; } } //printf("[%s] getEthis(thisfd = '%s', fd = '%s', fdparent = '%s')\n", loc.toChars(), thisfd.toPrettyChars(), fd.toPrettyChars(), fdparent.toPrettyChars()); if (fdparent == thisfd) { /* Going down one nesting level, i.e. we're calling * a nested function from its enclosing function. */ if (irs.sclosure && !(fd.ident == Id.require || fd.ident == Id.ensure)) { ethis = el_var(irs.sclosure); } else if (irs.sthis) { // We have a 'this' pointer for the current function if (fdp != thisfd) { /* fdparent (== thisfd) is a derived member function, * fdp is the overridden member function in base class, and * fd is the nested function '__require' or '__ensure'. * Even if there's a closure environment, we should give * original stack data as the nested function frame. * See also: SymbolExp.toElem() in e2ir.c (Bugzilla 9383 fix) */ /* Address of 'sthis' gives the 'this' for the nested * function. */ //printf("L%d fd = %s, fdparent = %s, fd.toParent2() = %s\n", // __LINE__, fd.toPrettyChars(), fdparent.toPrettyChars(), fdp.toPrettyChars()); assert(fd.ident == Id.require || fd.ident == Id.ensure); assert(thisfd.hasNestedFrameRefs()); ClassDeclaration cdp = fdp.isThis().isClassDeclaration(); ClassDeclaration cd = thisfd.isThis().isClassDeclaration(); assert(cdp && cd); int offset; cdp.isBaseOf(cd, &offset); assert(offset != ClassDeclaration.OFFSET_RUNTIME); //printf("%s to %s, offset = %d\n", cd.toChars(), cdp.toChars(), offset); if (offset) { /* Bugzilla 7517: If fdp is declared in interface, offset the * 'this' pointer to get correct interface type reference. */ Symbol *stmp = symbol_genauto(TYnptr); ethis = el_bin(OPadd, TYnptr, el_var(irs.sthis), el_long(TYsize_t, offset)); ethis = el_bin(OPeq, TYnptr, el_var(stmp), ethis); ethis = el_combine(ethis, el_ptr(stmp)); //elem_print(ethis); } else ethis = el_ptr(irs.sthis); } else if (thisfd.hasNestedFrameRefs()) { /* Local variables are referenced, can't skip. * Address of 'sthis' gives the 'this' for the nested * function. */ ethis = el_ptr(irs.sthis); } else { /* If no variables in the current function's frame are * referenced by nested functions, then we can 'skip' * adding this frame into the linked list of stack * frames. */ ethis = el_var(irs.sthis); } } else { /* No 'this' pointer for current function, */ if (thisfd.hasNestedFrameRefs()) { /* OPframeptr is an operator that gets the frame pointer * for the current function, i.e. for the x86 it gets * the value of EBP */ ethis = el_long(TYnptr, 0); ethis.Eoper = OPframeptr; } else { /* Use null if no references to the current function's frame */ ethis = el_long(TYnptr, 0); } } } else { if (!irs.sthis) // if no frame pointer for this function { fd.error(loc, "is a nested function and cannot be accessed from %s", irs.getFunc().toPrettyChars()); return el_long(TYnptr, 0); // error recovery } /* Go up a nesting level, i.e. we need to find the 'this' * of an enclosing function. * Our 'enclosing function' may also be an inner class. */ ethis = el_var(irs.sthis); Dsymbol s = thisfd; while (fd != s) { FuncDeclaration fdp2 = s.toParent2().isFuncDeclaration(); //printf("\ts = '%s'\n", s.toChars()); thisfd = s.isFuncDeclaration(); if (thisfd) { /* Enclosing function is a function. */ // Error should have been caught by front end assert(thisfd.isNested() || thisfd.vthis); } else { /* Enclosed by an aggregate. That means the current * function must be a member function of that aggregate. */ AggregateDeclaration ad = s.isAggregateDeclaration(); if (!ad) { Lnoframe: irs.getFunc().error(loc, "cannot get frame pointer to %s", fd.toPrettyChars()); return el_long(TYnptr, 0); // error recovery } ClassDeclaration cd = ad.isClassDeclaration(); ClassDeclaration cdx = fd.isClassDeclaration(); if (cd && cdx && cdx.isBaseOf(cd, null)) break; StructDeclaration sd = ad.isStructDeclaration(); if (fd == sd) break; if (!ad.isNested() || !ad.vthis) goto Lnoframe; ethis = el_bin(OPadd, TYnptr, ethis, el_long(TYsize_t, ad.vthis.offset)); ethis = el_una(OPind, TYnptr, ethis); } if (fdparent == s.toParent2()) break; /* Remember that frames for functions that have no * nested references are skipped in the linked list * of frames. */ if (fdp2 && fdp2.hasNestedFrameRefs()) ethis = el_una(OPind, TYnptr, ethis); s = s.toParent2(); assert(s); } } version (none) { printf("ethis:\n"); elem_print(ethis); printf("\n"); } return ethis; } /************************* * Initialize the hidden aggregate member, vthis, with * the context pointer. * Returns: * *(ey + ad.vthis.offset) = this; */ elem *setEthis(Loc loc, IRState *irs, elem *ey, AggregateDeclaration ad) { elem *ethis; FuncDeclaration thisfd = irs.getFunc(); int offset = 0; Dsymbol adp = ad.toParent2(); // class/func we're nested in //printf("[%s] setEthis(ad = %s, adp = %s, thisfd = %s)\n", loc.toChars(), ad.toChars(), adp.toChars(), thisfd.toChars()); if (adp == thisfd) { ethis = getEthis(loc, irs, ad); } else if (thisfd.vthis && (adp == thisfd.toParent2() || (adp.isClassDeclaration() && adp.isClassDeclaration().isBaseOf(thisfd.toParent2().isClassDeclaration(), &offset) ) ) ) { /* Class we're new'ing is at the same level as thisfd */ assert(offset == 0); // BUG: should handle this case ethis = el_var(irs.sthis); } else { ethis = getEthis(loc, irs, adp); FuncDeclaration fdp = adp.isFuncDeclaration(); if (fdp && fdp.hasNestedFrameRefs()) ethis = el_una(OPaddr, TYnptr, ethis); } ey = el_bin(OPadd, TYnptr, ey, el_long(TYsize_t, ad.vthis.offset)); ey = el_una(OPind, TYnptr, ey); ey = el_bin(OPeq, TYnptr, ey, ethis); return ey; } /******************************************* * Convert intrinsic function to operator. * Returns that operator, -1 if not an intrinsic function. */ int intrinsic_op(FuncDeclaration fd) { fd = fd.toAliasFunc(); const char *name = mangleExact(fd); //printf("intrinsic_op(%s)\n", name); __gshared immutable char*[11] std_namearray = [ /* The names are mangled differently because of the pure and * nothrow attributes. */ "4math3cosFNaNbNiNfeZe", "4math3sinFNaNbNiNfeZe", "4math4fabsFNaNbNiNfeZe", "4math4rintFNaNbNiNfeZe", "4math4sqrtFNaNbNiNfdZd", "4math4sqrtFNaNbNiNfeZe", "4math4sqrtFNaNbNiNffZf", "4math4yl2xFNaNbNiNfeeZe", "4math5ldexpFNaNbNiNfeiZe", "4math6rndtolFNaNbNiNfeZl", "4math6yl2xp1FNaNbNiNfeeZe", ]; __gshared immutable char*[11] std_namearray64 = [ /* The names are mangled differently because of the pure and * nothrow attributes. */ "4math3cosFNaNbNiNfeZe", "4math3sinFNaNbNiNfeZe", "4math4fabsFNaNbNiNfeZe", "4math4rintFNaNbNiNfeZe", "4math4sqrtFNaNbNiNfdZd", "4math4sqrtFNaNbNiNfeZe", "4math4sqrtFNaNbNiNffZf", "4math4yl2xFNaNbNiNfeeZe", "4math5ldexpFNaNbNiNfeiZe", "4math6rndtolFNaNbNiNfeZl", "4math6yl2xp1FNaNbNiNfeeZe", ]; __gshared immutable ubyte[11] std_ioptab = [ OPcos, OPsin, OPabs, OPrint, OPsqrt, OPsqrt, OPsqrt, OPyl2x, OPscale, OPrndtol, OPyl2xp1, ]; __gshared immutable char*[46] core_namearray = [ "4math3cosFNaNbNiNfeZe", "4math3sinFNaNbNiNfeZe", "4math4fabsFNaNbNiNfeZe", "4math4rintFNaNbNiNfeZe", "4math4sqrtFNaNbNiNfdZd", "4math4sqrtFNaNbNiNfeZe", "4math4sqrtFNaNbNiNffZf", "4math4yl2xFNaNbNiNfeeZe", "4math5ldexpFNaNbNiNfeiZe", "4math6rndtolFNaNbNiNfeZl", "4math6yl2xp1FNaNbNiNfeeZe", "4simd10__prefetchFNaNbNiNfxPvhZv", "4simd10__simd_stoFNaNbNiNfE4core4simd3XMMNhG16vNhG16vZNhG16v", "4simd10__simd_stoFNaNbNiNfE4core4simd3XMMdNhG16vZNhG16v", "4simd10__simd_stoFNaNbNiNfE4core4simd3XMMfNhG16vZNhG16v", "4simd6__simdFNaNbNiNfE4core4simd3XMMNhG16vNhG16vZNhG16v", "4simd6__simdFNaNbNiNfE4core4simd3XMMNhG16vNhG16vhZNhG16v", "4simd6__simdFNaNbNiNfE4core4simd3XMMNhG16vZNhG16v", "4simd6__simdFNaNbNiNfE4core4simd3XMMdZNhG16v", "4simd6__simdFNaNbNiNfE4core4simd3XMMfZNhG16v", "4simd9__simd_ibFNaNbNiNfE4core4simd3XMMNhG16vhZNhG16v", "5bitop12volatileLoadFNbNiNfPhZh", "5bitop12volatileLoadFNbNiNfPkZk", "5bitop12volatileLoadFNbNiNfPmZm", "5bitop12volatileLoadFNbNiNfPtZt", "5bitop13volatileStoreFNbNiNfPhhZv", "5bitop13volatileStoreFNbNiNfPkkZv", "5bitop13volatileStoreFNbNiNfPmmZv", "5bitop13volatileStoreFNbNiNfPttZv", "5bitop3bsfFNaNbNiNfkZi", "5bitop3bsfFNaNbNiNfmZi", "5bitop3bsrFNaNbNiNfkZi", "5bitop3bsrFNaNbNiNfmZi", "5bitop3btcFNaNbNiPkkZi", "5bitop3btrFNaNbNiPkkZi", "5bitop3btsFNaNbNiPkkZi", "5bitop3inpFNbNikZh", "5bitop4inplFNbNikZk", "5bitop4inpwFNbNikZt", "5bitop4outpFNbNikhZh", "5bitop5bswapFNaNbNiNfkZk", "5bitop5outplFNbNikkZk", "5bitop5outpwFNbNiktZt", "5bitop7_popcntFNaNbNiNfkZi", "5bitop7_popcntFNaNbNiNfmxx", // don't find 64 bit version in 32 bit code "5bitop7_popcntFNaNbNiNftZt", ]; __gshared immutable char*[46] core_namearray64 = [ "4math3cosFNaNbNiNfeZe", "4math3sinFNaNbNiNfeZe", "4math4fabsFNaNbNiNfeZe", "4math4rintFNaNbNiNfeZe", "4math4sqrtFNaNbNiNfdZd", "4math4sqrtFNaNbNiNfeZe", "4math4sqrtFNaNbNiNffZf", "4math4yl2xFNaNbNiNfeeZe", "4math5ldexpFNaNbNiNfeiZe", "4math6rndtolFNaNbNiNfeZl", "4math6yl2xp1FNaNbNiNfeeZe", "4simd10__prefetchFNaNbNiNfxPvhZv", "4simd10__simd_stoFNaNbNiNfE4core4simd3XMMNhG16vNhG16vZNhG16v", "4simd10__simd_stoFNaNbNiNfE4core4simd3XMMdNhG16vZNhG16v", "4simd10__simd_stoFNaNbNiNfE4core4simd3XMMfNhG16vZNhG16v", "4simd6__simdFNaNbNiNfE4core4simd3XMMNhG16vNhG16vZNhG16v", "4simd6__simdFNaNbNiNfE4core4simd3XMMNhG16vNhG16vhZNhG16v", "4simd6__simdFNaNbNiNfE4core4simd3XMMNhG16vZNhG16v", "4simd6__simdFNaNbNiNfE4core4simd3XMMdZNhG16v", "4simd6__simdFNaNbNiNfE4core4simd3XMMfZNhG16v", "4simd9__simd_ibFNaNbNiNfE4core4simd3XMMNhG16vhZNhG16v", "5bitop12volatileLoadFNbNiNfPhZh", "5bitop12volatileLoadFNbNiNfPkZk", "5bitop12volatileLoadFNbNiNfPmZm", "5bitop12volatileLoadFNbNiNfPtZt", "5bitop13volatileStoreFNbNiNfPhhZv", "5bitop13volatileStoreFNbNiNfPkkZv", "5bitop13volatileStoreFNbNiNfPmmZv", "5bitop13volatileStoreFNbNiNfPttZv", "5bitop3bsfFNaNbNiNfkZi", "5bitop3bsfFNaNbNiNfmZi", "5bitop3bsrFNaNbNiNfkZi", "5bitop3bsrFNaNbNiNfmZi", "5bitop3btcFNaNbNiPmmZi", "5bitop3btrFNaNbNiPmmZi", "5bitop3btsFNaNbNiPmmZi", "5bitop3inpFNbNikZh", "5bitop4inplFNbNikZk", "5bitop4inpwFNbNikZt", "5bitop4outpFNbNikhZh", "5bitop5bswapFNaNbNiNfkZk", "5bitop5outplFNbNikkZk", "5bitop5outpwFNbNiktZt", "5bitop7_popcntFNaNbNiNfkZi", "5bitop7_popcntFNaNbNiNfmZi", "5bitop7_popcntFNaNbNiNftZt", ]; __gshared immutable ubyte[46] core_ioptab = [ OPcos, OPsin, OPabs, OPrint, OPsqrt, OPsqrt, OPsqrt, OPyl2x, OPscale, OPrndtol, OPyl2xp1, OPprefetch, OPvector, OPvector, OPvector, OPvector, OPvector, OPvector, OPvector, OPvector, OPvector, OPind, OPind, OPind, OPind, OPeq, OPeq, OPeq, OPeq, OPbsf, OPbsf, OPbsr, OPbsr, OPbtc, OPbtr, OPbts, OPinp, OPinp, OPinp, OPoutp, OPbswap, OPoutp, OPoutp, OPpopcnt, OPpopcnt, OPpopcnt, ]; static assert(std_namearray.length == std_namearray64.length); static assert(std_namearray.length == std_ioptab.length); static assert(core_namearray.length == core_namearray64.length); static assert(core_namearray.length == core_ioptab.length); debug { for (size_t i = 0; i < std_namearray.length - 1; i++) { if (strcmp(std_namearray[i], std_namearray[i + 1]) >= 0) { printf("std_namearray[%ld] = '%s'\n", cast(long)i, std_namearray[i]); assert(0); } } for (size_t i = 0; i < std_namearray64.length - 1; i++) { if (strcmp(std_namearray64[i], std_namearray64[i + 1]) >= 0) { printf("std_namearray64[%ld] = '%s'\n", cast(long)i, std_namearray64[i]); assert(0); } } for (size_t i = 0; i < core_namearray.length - 1; i++) { //printf("test1 %s %s %d\n", core_namearray[i], core_namearray[i + 1], strcmp(core_namearray[i], core_namearray[i + 1])); if (strcmp(core_namearray[i], core_namearray[i + 1]) >= 0) { printf("core_namearray[%ld] = '%s'\n", cast(long)i, core_namearray[i]); assert(0); } } for (size_t i = 0; i < core_namearray64.length - 1; i++) { if (strcmp(core_namearray64[i], core_namearray64[i + 1]) >= 0) { printf("core_namearray64[%ld] = '%s'\n", cast(long)i, core_namearray64[i]); assert(0); } } } size_t length = strlen(name); if (length > 10 && (name[7] == 'm' || name[7] == 'i') && !memcmp(name, "_D3std".ptr, 6)) { int i = binary(name + 6, cast(const(char)**)(global.params.is64bit ? std_namearray64.ptr : std_namearray.ptr), cast(int)std_namearray.length); return (i == -1) ? i : std_ioptab[i]; } if (length > 12 && (name[8] == 'm' || name[8] == 'b' || name[8] == 's') && !memcmp(name, "_D4core".ptr, 7)) { int i = binary(name + 7, cast(const(char)**)(global.params.is64bit ? core_namearray64.ptr : core_namearray.ptr), cast(int)core_namearray.length); if (i != -1) { int op = core_ioptab[i]; if (!global.params.is64bit && (op == OPbsf || op == OPbsr) && op == core_ioptab[i - 1]) { // Don't recognize 64 bit bsf() / bsr() in 32 bit mode op = -1; } return op; } if (global.params.is64bit && fd.toParent().isTemplateInstance() && fd.ident == Id.va_start) { OutBuffer buf; mangleToBuffer(fd.getModule(), &buf); const s = buf.peekString(); if (!strcmp(s, "4core4stdc6stdarg")) { return OPva_start; } } return -1; } return -1; } /************************************** * Given an expression e that is an array, * determine and set the 'length' variable. * Input: * lengthVar Symbol of 'length' variable * &e expression that is the array * t1 Type of the array * Output: * e is rewritten to avoid side effects * Returns: * expression that initializes 'length' */ elem *resolveLengthVar(VarDeclaration lengthVar, elem **pe, Type t1) { //printf("resolveLengthVar()\n"); elem *einit = null; if (lengthVar && !(lengthVar.storage_class & STCconst)) { elem *elength; Symbol *slength; if (t1.ty == Tsarray) { TypeSArray tsa = cast(TypeSArray)t1; dinteger_t length = tsa.dim.toInteger(); elength = el_long(TYsize_t, length); goto L3; } else if (t1.ty == Tarray) { elength = *pe; *pe = el_same(&elength); elength = el_una(global.params.is64bit ? OP128_64 : OP64_32, TYsize_t, elength); L3: slength = toSymbol(lengthVar); //symbol_add(slength); einit = el_bin(OPeq, TYsize_t, el_var(slength), elength); } } return einit; } void setClosureVarOffset(FuncDeclaration fd) { if (fd.needsClosure()) { uint offset = Target.ptrsize; // leave room for previous sthis for (size_t i = 0; i < fd.closureVars.dim; i++) { VarDeclaration v = fd.closureVars[i]; /* Align and allocate space for v in the closure * just like AggregateDeclaration.addField() does. */ uint memsize; uint memalignsize; structalign_t xalign; if (v.storage_class & STClazy) { /* Lazy variables are really delegates, * so give same answers that TypeDelegate would */ memsize = Target.ptrsize * 2; memalignsize = memsize; xalign = STRUCTALIGN_DEFAULT; } else if (v.storage_class & (STCout | STCref)) { // reference parameters are just pointers memsize = Target.ptrsize; memalignsize = memsize; xalign = STRUCTALIGN_DEFAULT; } else { memsize = cast(uint)v.type.size(); memalignsize = v.type.alignsize(); xalign = v.alignment; } AggregateDeclaration.alignmember(xalign, memalignsize, &offset); v.offset = offset; //printf("closure var %s, offset = %d\n", v.toChars(), v.offset); offset += memsize; /* Can't do nrvo if the variable is put in a closure, since * what the shidden points to may no longer exist. */ if (fd.nrvo_can && fd.nrvo_var == v) { fd.nrvo_can = 0; } } } } /************************************* * Closures are implemented by taking the local variables that * need to survive the scope of the function, and copying them * into a gc allocated chuck of memory. That chunk, called the * closure here, is inserted into the linked list of stack * frames instead of the usual stack frame. * * buildClosure() inserts code just after the function prolog * is complete. It allocates memory for the closure, allocates * a local variable (sclosure) to point to it, inserts into it * the link to the enclosing frame, and copies into it the parameters * that are referred to in nested functions. * In VarExp::toElem and SymOffExp::toElem, when referring to a * variable that is in a closure, takes the offset from sclosure rather * than from the frame pointer. * * getEthis() and NewExp::toElem need to use sclosure, if set, rather * than the current frame pointer. */ void buildClosure(FuncDeclaration fd, IRState *irs) { //printf("buildClosure(fd = %s)\n", fd.toChars()); if (fd.needsClosure()) { setClosureVarOffset(fd); // Generate closure on the heap // BUG: doesn't capture variadic arguments passed to this function /* BUG: doesn't handle destructors for the local variables. * The way to do it is to make the closure variables the fields * of a class object: * class Closure { * vtbl[] * monitor * ptr to destructor * sthis * ... closure variables ... * ~this() { call destructor } * } */ //printf("FuncDeclaration.buildClosure() %s\n", fd.toChars()); /* Generate type name for closure struct */ const char *name1 = "CLOSURE."; const char *name2 = fd.toPrettyChars(); size_t namesize = strlen(name1)+strlen(name2)+1; char *closname = cast(char *) calloc(namesize, char.sizeof); strcat(strcat(closname, name1), name2); /* Build type for closure */ type *Closstru = type_struct_class(closname, Target.ptrsize, 0, null, null, false, false, true); free(closname); symbol_struct_addField(Closstru.Ttag, "__chain", Type_toCtype(Type.tvoidptr), 0); Symbol *sclosure; sclosure = symbol_name("__closptr", SCauto, type_pointer(Closstru)); sclosure.Sflags |= SFLtrue | SFLfree; symbol_add(sclosure); irs.sclosure = sclosure; assert(fd.closureVars.dim); assert(fd.closureVars[0].offset >= Target.ptrsize); for (size_t i = 0; i < fd.closureVars.dim; i++) { VarDeclaration v = fd.closureVars[i]; //printf("closure var %s\n", v.toChars()); // Hack for the case fail_compilation/fail10666.d, // until proper issue 5730 fix will come. bool isScopeDtorParam = v.edtor && (v.storage_class & STCparameter); if (v.needsScopeDtor() || isScopeDtorParam) { /* Because the value needs to survive the end of the scope! */ v.error("has scoped destruction, cannot build closure"); } if (v.isargptr) { /* See Bugzilla 2479 * This is actually a bug, but better to produce a nice * message at compile time rather than memory corruption at runtime */ v.error("cannot reference variadic arguments from closure"); } /* Set Sscope to closure */ Symbol *vsym = toSymbol(v); assert(vsym.Sscope == null); vsym.Sscope = sclosure; /* Add variable as closure type member */ symbol_struct_addField(Closstru.Ttag, &vsym.Sident[0], vsym.Stype, v.offset); //printf("closure field %s: memalignsize: %i, offset: %i\n", &vsym.Sident[0], memalignsize, v.offset); } // Calculate the size of the closure VarDeclaration vlast = fd.closureVars[fd.closureVars.dim - 1]; uint structsize; if (vlast.storage_class & STClazy) structsize = vlast.offset + Target.ptrsize * 2; else if (vlast.isRef() || vlast.isOut()) structsize = vlast.offset + Target.ptrsize; else structsize = cast(uint)(vlast.offset + vlast.type.size()); //printf("structsize = %d\n", structsize); Closstru.Ttag.Sstruct.Sstructsize = structsize; // Allocate memory for the closure elem *e = el_long(TYsize_t, structsize); e = el_bin(OPcall, TYnptr, el_var(getRtlsym(RTLSYM_ALLOCMEMORY)), e); toTraceGC(irs, e, &fd.loc); // Assign block of memory to sclosure // sclosure = allocmemory(sz); e = el_bin(OPeq, TYvoid, el_var(sclosure), e); // Set the first element to sthis // *(sclosure + 0) = sthis; elem *ethis; if (irs.sthis) ethis = el_var(irs.sthis); else ethis = el_long(TYnptr, 0); elem *ex = el_una(OPind, TYnptr, el_var(sclosure)); ex = el_bin(OPeq, TYnptr, ex, ethis); e = el_combine(e, ex); // Copy function parameters into closure for (size_t i = 0; i < fd.closureVars.dim; i++) { VarDeclaration v = fd.closureVars[i]; if (!v.isParameter()) continue; tym_t tym = totym(v.type); bool win64ref = ISWIN64REF(v); if (win64ref) { if (v.storage_class & STClazy) tym = TYdelegate; } else if (ISREF(v, null)) tym = TYnptr; // reference parameters are just pointers else if (v.storage_class & STClazy) tym = TYdelegate; ex = el_bin(OPadd, TYnptr, el_var(sclosure), el_long(TYsize_t, v.offset)); ex = el_una(OPind, tym, ex); elem *ev = el_var(toSymbol(v)); if (win64ref) { ev.Ety = TYnptr; ev = el_una(OPind, tym, ev); if (tybasic(ev.Ety) == TYstruct || tybasic(ev.Ety) == TYarray) ev.ET = Type_toCtype(v.type); } if (tybasic(ex.Ety) == TYstruct || tybasic(ex.Ety) == TYarray) { .type *t = Type_toCtype(v.type); ex.ET = t; ex = el_bin(OPstreq, tym, ex, ev); ex.ET = t; } else ex = el_bin(OPeq, tym, ex, ev); e = el_combine(e, ex); } block_appendexp(irs.blx.curblock, e); } } /*************************** * Determine return style of function - whether in registers or * through a hidden pointer to the caller's stack. */ RET retStyle(TypeFunction tf) { //printf("TypeFunction.retStyle() %s\n", toChars()); if (tf.isref) { //printf(" ref RETregs\n"); return RETregs; // returns a pointer } Type tn = tf.next.toBasetype(); //printf("tn = %s\n", tn.toChars()); d_uns64 sz = tn.size(); Type tns = tn; if (global.params.isWindows && global.params.is64bit) { // http://msdn.microsoft.com/en-us/library/7572ztz4.aspx if (tns.ty == Tcomplex32) return RETstack; if (tns.isscalar()) return RETregs; tns = tns.baseElemOf(); if (tns.ty == Tstruct) { StructDeclaration sd = (cast(TypeStruct)tns).sym; if (sd.ident == Id.__c_long_double) return RETregs; if (!sd.isPOD() || sz > 8) return RETstack; if (sd.fields.dim == 0) return RETstack; } if (sz <= 16 && !(sz & (sz - 1))) return RETregs; return RETstack; } else if (global.params.isWindows && global.params.mscoff) { Type tb = tns.baseElemOf(); if (tb.ty == Tstruct) { StructDeclaration sd = (cast(TypeStruct)tb).sym; if (sd.ident == Id.__c_long_double) return RETregs; } } Lagain: if (tns.ty == Tsarray) { tns = tns.baseElemOf(); if (tns.ty != Tstruct) { L2: if (global.params.isLinux && tf.linkage != LINKd && !global.params.is64bit) { // 32 bit C/C++ structs always on stack } else { switch (sz) { case 1: case 2: case 4: case 8: //printf(" sarray RETregs\n"); return RETregs; // return small structs in regs // (not 3 byte structs!) default: break; } } //printf(" sarray RETstack\n"); return RETstack; } } if (tns.ty == Tstruct) { StructDeclaration sd = (cast(TypeStruct)tns).sym; if (global.params.isLinux && tf.linkage != LINKd && !global.params.is64bit) { if (sd.ident == Id.__c_long || sd.ident == Id.__c_ulong) return RETregs; //printf(" 2 RETstack\n"); return RETstack; // 32 bit C/C++ structs always on stack } if (global.params.isWindows && tf.linkage == LINKcpp && !global.params.is64bit && sd.isPOD() && sd.ctor) { // win32 returns otherwise POD structs with ctors via memory // unless it's not really a struct if (sd.ident == Id.__c_long || sd.ident == Id.__c_ulong) return RETregs; return RETstack; } if (sd.arg1type && !sd.arg2type) { tns = sd.arg1type; if (tns.ty != Tstruct) goto L2; goto Lagain; } else if (global.params.is64bit && !sd.arg1type && !sd.arg2type) return RETstack; else if (sd.isPOD()) { switch (sz) { case 1: case 2: case 4: case 8: //printf(" 3 RETregs\n"); return RETregs; // return small structs in regs // (not 3 byte structs!) case 16: if (!global.params.isWindows && global.params.is64bit) return RETregs; break; default: break; } } //printf(" 3 RETstack\n"); return RETstack; } else if ((global.params.isLinux || global.params.isOSX || global.params.isFreeBSD || global.params.isSolaris) && tf.linkage == LINKc && tns.iscomplex()) { if (tns.ty == Tcomplex32) return RETregs; // in EDX:EAX, not ST1:ST0 else return RETstack; } else { //assert(sz <= 16); //printf(" 4 RETregs\n"); return RETregs; } }