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