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