1 /** 2 * Convert to Intermediate Representation (IR) for the back-end. 3 * 4 * Copyright: Copyright (C) 1999-2021 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.length == 0) 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.length == 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 if (id3 == Id.fabs && 527 (argtype1 is Type.tfloat32 || argtype1 is Type.tfloat64)) 528 { 529 op = OPabs; 530 } 531 } 532 else if (id1 == Id.core) 533 { 534 if (id2 == Id.math) 535 { 536 Lmath: 537 if (argtype1 is Type.tfloat80 || argtype1 is Type.tfloat32 || argtype1 is Type.tfloat64) 538 { 539 if (id3 == Id.cos) op = OPcos; 540 else if (id3 == Id.sin) op = OPsin; 541 else if (id3 == Id.fabs) op = OPabs; 542 else if (id3 == Id.rint) op = OPrint; 543 else if (id3 == Id._sqrt) op = OPsqrt; 544 else if (id3 == Id.yl2x) op = OPyl2x; 545 else if (id3 == Id.ldexp) op = OPscale; 546 else if (id3 == Id.rndtol) op = OPrndtol; 547 else if (id3 == Id.yl2xp1) op = OPyl2xp1; 548 else if (id3 == Id.toPrec) op = OPtoPrec; 549 } 550 } 551 else if (id2 == Id.simd) 552 { 553 if (id3 == Id.__prefetch) op = OPprefetch; 554 else if (id3 == Id.__simd_sto) op = OPvector; 555 else if (id3 == Id.__simd) op = OPvector; 556 else if (id3 == Id.__simd_ib) op = OPvector; 557 } 558 else if (id2 == Id.bitop) 559 { 560 if (id3 == Id.volatileLoad) op = OPind; 561 else if (id3 == Id.volatileStore) op = OPeq; 562 563 else if (id3 == Id.bsf) op = OPbsf; 564 else if (id3 == Id.bsr) op = OPbsr; 565 else if (id3 == Id.btc) op = OPbtc; 566 else if (id3 == Id.btr) op = OPbtr; 567 else if (id3 == Id.bts) op = OPbts; 568 569 else if (id3 == Id.inp) op = OPinp; 570 else if (id3 == Id.inpl) op = OPinp; 571 else if (id3 == Id.inpw) op = OPinp; 572 573 else if (id3 == Id.outp) op = OPoutp; 574 else if (id3 == Id.outpl) op = OPoutp; 575 else if (id3 == Id.outpw) op = OPoutp; 576 577 else if (id3 == Id.bswap) op = OPbswap; 578 else if (id3 == Id._popcnt) op = OPpopcnt; 579 } 580 else if (id2 == Id..volatile) 581 { 582 if (id3 == Id.volatileLoad) op = OPind; 583 else if (id3 == Id.volatileStore) op = OPeq; 584 } 585 } 586 587 if (!global.params.is64bit) 588 // No 64-bit bsf bsr in 32bit mode 589 { 590 if ((op == OPbsf || op == OPbsr) && argtype1 is Type.tuns64) 591 return NotIntrinsic; 592 } 593 return op; 594 595 Lva_start: 596 if (global.params.is64bit && 597 fd.toParent().isTemplateInstance() && 598 id3 == Id.va_start && 599 id2 == Id.stdarg && 600 md.packages[1] == Id.stdc && 601 id1 == Id.core) 602 { 603 return OPva_start; 604 } 605 return op; 606 } 607 608 /************************************** 609 * Given an expression e that is an array, 610 * determine and set the 'length' variable. 611 * Input: 612 * lengthVar Symbol of 'length' variable 613 * &e expression that is the array 614 * t1 Type of the array 615 * Output: 616 * e is rewritten to avoid side effects 617 * Returns: 618 * expression that initializes 'length' 619 */ 620 elem *resolveLengthVar(VarDeclaration lengthVar, elem **pe, Type t1) 621 { 622 //printf("resolveLengthVar()\n"); 623 elem *einit = null; 624 625 if (lengthVar && !(lengthVar.storage_class & STC.const_)) 626 { 627 elem *elength; 628 Symbol *slength; 629 630 if (t1.ty == Tsarray) 631 { 632 TypeSArray tsa = cast(TypeSArray)t1; 633 dinteger_t length = tsa.dim.toInteger(); 634 635 elength = el_long(TYsize_t, length); 636 goto L3; 637 } 638 else if (t1.ty == Tarray) 639 { 640 elength = *pe; 641 *pe = el_same(&elength); 642 elength = el_una(global.params.is64bit ? OP128_64 : OP64_32, TYsize_t, elength); 643 644 L3: 645 slength = toSymbol(lengthVar); 646 //symbol_add(slength); 647 648 einit = el_bin(OPeq, TYsize_t, el_var(slength), elength); 649 } 650 } 651 return einit; 652 } 653 654 /************************************* 655 * for a nested function 'fd' return the type of the closure 656 * of an outer function or aggregate. If the function is a member function 657 * the 'this' type is expected to be stored in 'sthis.Sthis'. 658 * It is always returned if it is not a void pointer. 659 * buildClosure() must have been called on the outer function before. 660 * 661 * Params: 662 * sthis = the symbol of the current 'this' derived from fd.vthis 663 * fd = the nested function 664 */ 665 TYPE* getParentClosureType(Symbol* sthis, FuncDeclaration fd) 666 { 667 if (sthis) 668 { 669 // only replace void* 670 if (sthis.Stype.Tty != TYnptr || sthis.Stype.Tnext.Tty != TYvoid) 671 return sthis.Stype; 672 } 673 for (Dsymbol sym = fd.toParent2(); sym; sym = sym.toParent2()) 674 { 675 if (auto fn = sym.isFuncDeclaration()) 676 if (fn.csym && fn.csym.Sscope) 677 return fn.csym.Sscope.Stype; 678 if (sym.isAggregateDeclaration()) 679 break; 680 } 681 return sthis ? sthis.Stype : Type_toCtype(Type.tvoidptr); 682 } 683 684 /************************************** 685 * Go through the variables in function fd that are 686 * to be allocated in a closure, and set the .offset fields 687 * for those variables to their positions relative to the start 688 * of the closure instance. 689 * Also turns off nrvo for closure variables. 690 * Params: 691 * fd = function 692 */ 693 void setClosureVarOffset(FuncDeclaration fd) 694 { 695 // Nothing to do 696 if (!fd.needsClosure()) 697 return; 698 699 uint offset = target.ptrsize; // leave room for previous sthis 700 701 foreach (v; fd.closureVars) 702 { 703 /* Align and allocate space for v in the closure 704 * just like AggregateDeclaration.addField() does. 705 */ 706 uint memsize; 707 uint memalignsize; 708 structalign_t xalign; 709 if (v.storage_class & STC.lazy_) 710 { 711 /* Lazy variables are really delegates, 712 * so give same answers that TypeDelegate would 713 */ 714 memsize = target.ptrsize * 2; 715 memalignsize = memsize; 716 xalign = STRUCTALIGN_DEFAULT; 717 } 718 else if (v.storage_class & (STC.out_ | STC.ref_)) 719 { 720 // reference parameters are just pointers 721 memsize = target.ptrsize; 722 memalignsize = memsize; 723 xalign = STRUCTALIGN_DEFAULT; 724 } 725 else 726 { 727 memsize = cast(uint)v.type.size(); 728 memalignsize = v.type.alignsize(); 729 xalign = v.alignment; 730 } 731 AggregateDeclaration.alignmember(xalign, memalignsize, &offset); 732 v.offset = offset; 733 //printf("closure var %s, offset = %d\n", v.toChars(), v.offset); 734 735 offset += memsize; 736 737 /* Can't do nrvo if the variable is put in a closure, since 738 * what the shidden points to may no longer exist. 739 */ 740 if (fd.nrvo_can && fd.nrvo_var == v) 741 { 742 fd.nrvo_can = false; 743 } 744 } 745 } 746 747 /************************************* 748 * Closures are implemented by taking the local variables that 749 * need to survive the scope of the function, and copying them 750 * into a gc allocated chuck of memory. That chunk, called the 751 * closure here, is inserted into the linked list of stack 752 * frames instead of the usual stack frame. 753 * 754 * buildClosure() inserts code just after the function prolog 755 * is complete. It allocates memory for the closure, allocates 756 * a local variable (sclosure) to point to it, inserts into it 757 * the link to the enclosing frame, and copies into it the parameters 758 * that are referred to in nested functions. 759 * In VarExp::toElem and SymOffExp::toElem, when referring to a 760 * variable that is in a closure, takes the offset from sclosure rather 761 * than from the frame pointer. 762 * 763 * getEthis() and NewExp::toElem need to use sclosure, if set, rather 764 * than the current frame pointer. 765 */ 766 void buildClosure(FuncDeclaration fd, IRState *irs) 767 { 768 //printf("buildClosure(fd = %s)\n", fd.toChars()); 769 if (fd.needsClosure()) 770 { 771 setClosureVarOffset(fd); 772 773 // Generate closure on the heap 774 // BUG: doesn't capture variadic arguments passed to this function 775 776 /* BUG: doesn't handle destructors for the local variables. 777 * The way to do it is to make the closure variables the fields 778 * of a class object: 779 * class Closure { 780 * vtbl[] 781 * monitor 782 * ptr to destructor 783 * sthis 784 * ... closure variables ... 785 * ~this() { call destructor } 786 * } 787 */ 788 //printf("FuncDeclaration.buildClosure() %s\n", fd.toChars()); 789 790 /* Generate type name for closure struct */ 791 const char *name1 = "CLOSURE."; 792 const char *name2 = fd.toPrettyChars(); 793 size_t namesize = strlen(name1)+strlen(name2)+1; 794 char *closname = cast(char *)Mem.check(calloc(namesize, char.sizeof)); 795 strcat(strcat(closname, name1), name2); 796 797 /* Build type for closure */ 798 type *Closstru = type_struct_class(closname, target.ptrsize, 0, null, null, false, false, true, false); 799 free(closname); 800 auto chaintype = getParentClosureType(irs.sthis, fd); 801 symbol_struct_addField(Closstru.Ttag, "__chain", chaintype, 0); 802 803 Symbol *sclosure; 804 sclosure = symbol_name("__closptr", SCauto, type_pointer(Closstru)); 805 sclosure.Sflags |= SFLtrue | SFLfree; 806 symbol_add(sclosure); 807 irs.sclosure = sclosure; 808 809 assert(fd.closureVars.dim); 810 assert(fd.closureVars[0].offset >= target.ptrsize); 811 foreach (v; fd.closureVars) 812 { 813 //printf("closure var %s\n", v.toChars()); 814 815 // Hack for the case fail_compilation/fail10666.d, 816 // until proper issue 5730 fix will come. 817 bool isScopeDtorParam = v.edtor && (v.storage_class & STC.parameter); 818 if (v.needsScopeDtor() || isScopeDtorParam) 819 { 820 /* Because the value needs to survive the end of the scope! 821 */ 822 v.error("has scoped destruction, cannot build closure"); 823 } 824 if (v.isargptr) 825 { 826 /* See https://issues.dlang.org/show_bug.cgi?id=2479 827 * This is actually a bug, but better to produce a nice 828 * message at compile time rather than memory corruption at runtime 829 */ 830 v.error("cannot reference variadic arguments from closure"); 831 } 832 833 /* Set Sscope to closure */ 834 Symbol *vsym = toSymbol(v); 835 assert(vsym.Sscope == null); 836 vsym.Sscope = sclosure; 837 838 /* Add variable as closure type member */ 839 symbol_struct_addField(Closstru.Ttag, &vsym.Sident[0], vsym.Stype, v.offset); 840 //printf("closure field %s: memalignsize: %i, offset: %i\n", &vsym.Sident[0], memalignsize, v.offset); 841 } 842 843 // Calculate the size of the closure 844 VarDeclaration vlast = fd.closureVars[fd.closureVars.dim - 1]; 845 typeof(Type.size()) lastsize; 846 if (vlast.storage_class & STC.lazy_) 847 lastsize = target.ptrsize * 2; 848 else if (vlast.isRef() || vlast.isOut()) 849 lastsize = target.ptrsize; 850 else 851 lastsize = vlast.type.size(); 852 bool overflow; 853 const structsize = addu(vlast.offset, lastsize, overflow); 854 assert(!overflow && structsize <= uint.max); 855 //printf("structsize = %d\n", cast(uint)structsize); 856 857 Closstru.Ttag.Sstruct.Sstructsize = cast(uint)structsize; 858 fd.csym.Sscope = sclosure; 859 860 if (global.params.symdebug) 861 toDebugClosure(Closstru.Ttag); 862 863 // Allocate memory for the closure 864 elem *e = el_long(TYsize_t, structsize); 865 e = el_bin(OPcall, TYnptr, el_var(getRtlsym(RTLSYM_ALLOCMEMORY)), e); 866 toTraceGC(irs, e, fd.loc); 867 868 // Assign block of memory to sclosure 869 // sclosure = allocmemory(sz); 870 e = el_bin(OPeq, TYvoid, el_var(sclosure), e); 871 872 // Set the first element to sthis 873 // *(sclosure + 0) = sthis; 874 elem *ethis; 875 if (irs.sthis) 876 ethis = el_var(irs.sthis); 877 else 878 ethis = el_long(TYnptr, 0); 879 elem *ex = el_una(OPind, TYnptr, el_var(sclosure)); 880 ex = el_bin(OPeq, TYnptr, ex, ethis); 881 e = el_combine(e, ex); 882 883 // Copy function parameters into closure 884 foreach (v; fd.closureVars) 885 { 886 if (!v.isParameter()) 887 continue; 888 tym_t tym = totym(v.type); 889 const x64ref = ISX64REF(v); 890 if (x64ref && config.exe == EX_WIN64) 891 { 892 if (v.storage_class & STC.lazy_) 893 tym = TYdelegate; 894 } 895 else if (ISREF(v) && !x64ref) 896 tym = TYnptr; // reference parameters are just pointers 897 else if (v.storage_class & STC.lazy_) 898 tym = TYdelegate; 899 ex = el_bin(OPadd, TYnptr, el_var(sclosure), el_long(TYsize_t, v.offset)); 900 ex = el_una(OPind, tym, ex); 901 elem *ev = el_var(toSymbol(v)); 902 if (x64ref) 903 { 904 ev.Ety = TYnref; 905 ev = el_una(OPind, tym, ev); 906 if (tybasic(ev.Ety) == TYstruct || tybasic(ev.Ety) == TYarray) 907 ev.ET = Type_toCtype(v.type); 908 } 909 if (tybasic(ex.Ety) == TYstruct || tybasic(ex.Ety) == TYarray) 910 { 911 .type *t = Type_toCtype(v.type); 912 ex.ET = t; 913 ex = el_bin(OPstreq, tym, ex, ev); 914 ex.ET = t; 915 } 916 else 917 ex = el_bin(OPeq, tym, ex, ev); 918 919 e = el_combine(e, ex); 920 } 921 922 block_appendexp(irs.blx.curblock, e); 923 } 924 } 925 926 /************************************* 927 * build a debug info struct for variables captured by nested functions, 928 * but not in a closure. 929 * must be called after generating the function to fill stack offsets 930 * Params: 931 * fd = function 932 */ 933 void buildCapture(FuncDeclaration fd) 934 { 935 if (!global.params.symdebug) 936 return; 937 if (!global.params.mscoff) // toDebugClosure only implemented for CodeView, 938 return; // but optlink crashes for negative field offsets 939 940 if (fd.closureVars.dim && !fd.needsClosure) 941 { 942 /* Generate type name for struct with captured variables */ 943 const char *name1 = "CAPTURE."; 944 const char *name2 = fd.toPrettyChars(); 945 size_t namesize = strlen(name1)+strlen(name2)+1; 946 char *capturename = cast(char *)Mem.check(calloc(namesize, char.sizeof)); 947 strcat(strcat(capturename, name1), name2); 948 949 /* Build type for struct */ 950 type *capturestru = type_struct_class(capturename, target.ptrsize, 0, null, null, false, false, true, false); 951 free(capturename); 952 953 foreach (v; fd.closureVars) 954 { 955 Symbol *vsym = toSymbol(v); 956 957 /* Add variable as capture type member */ 958 auto soffset = vsym.Soffset; 959 if (fd.vthis) 960 soffset -= toSymbol(fd.vthis).Soffset; // see toElem.ToElemVisitor.visit(SymbolExp) 961 symbol_struct_addField(capturestru.Ttag, &vsym.Sident[0], vsym.Stype, cast(uint)soffset); 962 //printf("capture field %s: offset: %i\n", &vsym.Sident[0], v.offset); 963 } 964 965 // generate pseudo symbol to put into functions' Sscope 966 Symbol *scapture = symbol_name("__captureptr", SCalias, type_pointer(capturestru)); 967 scapture.Sflags |= SFLtrue | SFLfree; 968 //symbol_add(scapture); 969 fd.csym.Sscope = scapture; 970 971 toDebugClosure(capturestru.Ttag); 972 } 973 } 974 975 976 /*************************** 977 * Determine return style of function - whether in registers or 978 * through a hidden pointer to the caller's stack. 979 * Params: 980 * tf = function type to check 981 * needsThis = true if the function type is for a non-static member function 982 * Returns: 983 * RET.stack if return value from function is on the stack, RET.regs otherwise 984 */ 985 RET retStyle(TypeFunction tf, bool needsThis) 986 { 987 //printf("TypeFunction.retStyle() %s\n", toChars()); 988 return target.isReturnOnStack(tf, needsThis) ? RET.stack : RET.regs; 989 }