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 }