1 /** 2 * Compiler implementation of the 3 * $(LINK2 http://www.dlang.org, D programming language). 4 * 5 * Copyright: Copyright (c) 1999-2016 by Digital Mars, All Rights Reserved 6 * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright) 7 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 8 * Source: $(DMDSRC _inline.d) 9 */ 10 11 module ddmd.inline; 12 13 import core.stdc.stdio; 14 import core.stdc..string; 15 16 import ddmd.aggregate; 17 import ddmd.apply; 18 import ddmd.arraytypes; 19 import ddmd.attrib; 20 import ddmd.declaration; 21 import ddmd.dmodule; 22 import ddmd.dscope; 23 import ddmd.dstruct; 24 import ddmd.dsymbol; 25 import ddmd.dtemplate; 26 import ddmd.expression; 27 import ddmd.func; 28 import ddmd.globals; 29 import ddmd.id; 30 import ddmd.identifier; 31 import ddmd.init; 32 import ddmd.mtype; 33 import ddmd.opover; 34 import ddmd.statement; 35 import ddmd.tokens; 36 import ddmd.visitor; 37 38 private: 39 40 enum LOG = false; 41 enum CANINLINE_LOG = false; 42 enum EXPANDINLINE_LOG = false; 43 44 enum COST_MAX = 250; 45 enum STATEMENT_COST = 0x1000; 46 enum STATEMENT_COST_MAX = 250 * STATEMENT_COST; 47 48 // STATEMENT_COST be power of 2 and greater than COST_MAX 49 static assert((STATEMENT_COST & (STATEMENT_COST - 1)) == 0); 50 static assert(STATEMENT_COST > COST_MAX); 51 52 bool tooCostly(int cost) 53 { 54 return ((cost & (STATEMENT_COST - 1)) >= COST_MAX); 55 } 56 57 /*********************************************************** 58 * Compute cost of inlining. 59 * 60 * Walk trees to determine if inlining can be done, and if so, 61 * if it is too complex to be worth inlining or not. 62 */ 63 extern (C++) final class InlineCostVisitor : Visitor 64 { 65 alias visit = super.visit; 66 public: 67 int nested; 68 bool hasthis; 69 bool hdrscan; // if inline scan for 'header' content 70 bool allowAlloca; 71 FuncDeclaration fd; 72 int cost; // zero start for subsequent AST 73 74 extern (D) this() 75 { 76 } 77 78 extern (D) this(InlineCostVisitor icv) 79 { 80 nested = icv.nested; 81 hasthis = icv.hasthis; 82 hdrscan = icv.hdrscan; 83 allowAlloca = icv.allowAlloca; 84 fd = icv.fd; 85 } 86 87 override void visit(Statement s) 88 { 89 //printf("Statement.inlineCost = %d\n", COST_MAX); 90 //printf("%p\n", s.isScopeStatement()); 91 //printf("%s\n", s.toChars()); 92 cost += COST_MAX; // default is we can't inline it 93 } 94 95 override void visit(ExpStatement s) 96 { 97 expressionInlineCost(s.exp); 98 } 99 100 override void visit(CompoundStatement s) 101 { 102 scope InlineCostVisitor icv = new InlineCostVisitor(this); 103 foreach (i; 0 .. s.statements.dim) 104 { 105 Statement s2 = (*s.statements)[i]; 106 if (s2) 107 { 108 /* Specifically allow: 109 * if (condition) 110 * return exp1; 111 * return exp2; 112 */ 113 IfStatement ifs; 114 Statement s3; 115 if ((ifs = s2.isIfStatement()) !is null && 116 ifs.ifbody && 117 ifs.ifbody.isReturnStatement() && 118 !ifs.elsebody && 119 i + 1 < s.statements.dim && 120 (s3 = (*s.statements)[i + 1]) !is null && 121 s3.isReturnStatement() 122 ) 123 { 124 if (ifs.prm) // if variables are declared 125 { 126 cost = COST_MAX; 127 return; 128 } 129 expressionInlineCost(ifs.condition); 130 ifs.ifbody.accept(this); 131 s3.accept(this); 132 } 133 else 134 s2.accept(icv); 135 if (tooCostly(icv.cost)) 136 break; 137 } 138 } 139 cost += icv.cost; 140 } 141 142 override void visit(UnrolledLoopStatement s) 143 { 144 scope InlineCostVisitor icv = new InlineCostVisitor(this); 145 foreach (s2; *s.statements) 146 { 147 if (s2) 148 { 149 s2.accept(icv); 150 if (tooCostly(icv.cost)) 151 break; 152 } 153 } 154 cost += icv.cost; 155 } 156 157 override void visit(ScopeStatement s) 158 { 159 cost++; 160 if (s.statement) 161 s.statement.accept(this); 162 } 163 164 override void visit(IfStatement s) 165 { 166 /* Can't declare variables inside ?: expressions, so 167 * we cannot inline if a variable is declared. 168 */ 169 if (s.prm) 170 { 171 cost = COST_MAX; 172 return; 173 } 174 expressionInlineCost(s.condition); 175 /* Specifically allow: 176 * if (condition) 177 * return exp1; 178 * else 179 * return exp2; 180 * Otherwise, we can't handle return statements nested in if's. 181 */ 182 if (s.elsebody && s.ifbody && s.ifbody.isReturnStatement() && s.elsebody.isReturnStatement()) 183 { 184 s.ifbody.accept(this); 185 s.elsebody.accept(this); 186 //printf("cost = %d\n", cost); 187 } 188 else 189 { 190 nested += 1; 191 if (s.ifbody) 192 s.ifbody.accept(this); 193 if (s.elsebody) 194 s.elsebody.accept(this); 195 nested -= 1; 196 } 197 //printf("IfStatement.inlineCost = %d\n", cost); 198 } 199 200 override void visit(ReturnStatement s) 201 { 202 // Can't handle return statements nested in if's 203 if (nested) 204 { 205 cost = COST_MAX; 206 } 207 else 208 { 209 expressionInlineCost(s.exp); 210 } 211 } 212 213 override void visit(ImportStatement s) 214 { 215 } 216 217 override void visit(ForStatement s) 218 { 219 cost += STATEMENT_COST; 220 if (s._init) 221 s._init.accept(this); 222 if (s.condition) 223 s.condition.accept(this); 224 if (s.increment) 225 s.increment.accept(this); 226 if (s._body) 227 s._body.accept(this); 228 //printf("ForStatement: inlineCost = %d\n", cost); 229 } 230 231 override void visit(ThrowStatement s) 232 { 233 cost += STATEMENT_COST; 234 s.exp.accept(this); 235 } 236 237 /* -------------------------- */ 238 void expressionInlineCost(Expression e) 239 { 240 //printf("expressionInlineCost()\n"); 241 //e.print(); 242 if (e) 243 { 244 extern (C++) final class LambdaInlineCost : StoppableVisitor 245 { 246 alias visit = super.visit; 247 InlineCostVisitor icv; 248 249 public: 250 extern (D) this(InlineCostVisitor icv) 251 { 252 this.icv = icv; 253 } 254 255 override void visit(Expression e) 256 { 257 e.accept(icv); 258 stop = icv.cost >= COST_MAX; 259 } 260 } 261 262 scope InlineCostVisitor icv = new InlineCostVisitor(this); 263 scope LambdaInlineCost lic = new LambdaInlineCost(icv); 264 walkPostorder(e, lic); 265 cost += icv.cost; 266 } 267 } 268 269 override void visit(Expression e) 270 { 271 cost++; 272 } 273 274 override void visit(VarExp e) 275 { 276 //printf("VarExp.inlineCost3() %s\n", toChars()); 277 Type tb = e.type.toBasetype(); 278 if (tb.ty == Tstruct) 279 { 280 StructDeclaration sd = (cast(TypeStruct)tb).sym; 281 if (sd.isNested()) 282 { 283 /* An inner struct will be nested inside another function hierarchy than where 284 * we're inlining into, so don't inline it. 285 * At least not until we figure out how to 'move' the struct to be nested 286 * locally. Example: 287 * struct S(alias pred) { void unused_func(); } 288 * void abc() { int w; S!(w) m; } 289 * void bar() { abc(); } 290 */ 291 cost = COST_MAX; 292 return; 293 } 294 } 295 FuncDeclaration fd = e.var.isFuncDeclaration(); 296 if (fd && fd.isNested()) // see Bugzilla 7199 for test case 297 cost = COST_MAX; 298 else 299 cost++; 300 } 301 302 override void visit(ThisExp e) 303 { 304 //printf("ThisExp.inlineCost3() %s\n", toChars()); 305 if (!fd) 306 { 307 cost = COST_MAX; 308 return; 309 } 310 if (!hdrscan) 311 { 312 if (fd.isNested() || !hasthis) 313 { 314 cost = COST_MAX; 315 return; 316 } 317 } 318 cost++; 319 } 320 321 override void visit(StructLiteralExp e) 322 { 323 //printf("StructLiteralExp.inlineCost3() %s\n", toChars()); 324 if (e.sd.isNested()) 325 cost = COST_MAX; 326 else 327 cost++; 328 } 329 330 override void visit(NewExp e) 331 { 332 //printf("NewExp.inlineCost3() %s\n", e.toChars()); 333 AggregateDeclaration ad = isAggregate(e.newtype); 334 if (ad && ad.isNested()) 335 cost = COST_MAX; 336 else 337 cost++; 338 } 339 340 override void visit(FuncExp e) 341 { 342 //printf("FuncExp.inlineCost3()\n"); 343 // Right now, this makes the function be output to the .obj file twice. 344 cost = COST_MAX; 345 } 346 347 override void visit(DelegateExp e) 348 { 349 //printf("DelegateExp.inlineCost3()\n"); 350 cost = COST_MAX; 351 } 352 353 override void visit(DeclarationExp e) 354 { 355 //printf("DeclarationExp.inlineCost3()\n"); 356 VarDeclaration vd = e.declaration.isVarDeclaration(); 357 if (vd) 358 { 359 TupleDeclaration td = vd.toAlias().isTupleDeclaration(); 360 if (td) 361 { 362 cost = COST_MAX; // finish DeclarationExp.doInlineAs 363 return; 364 } 365 if (!hdrscan && vd.isDataseg()) 366 { 367 cost = COST_MAX; 368 return; 369 } 370 if (vd.edtor) 371 { 372 // if destructor required 373 // needs work to make this work 374 cost = COST_MAX; 375 return; 376 } 377 // Scan initializer (vd.init) 378 if (vd._init) 379 { 380 ExpInitializer ie = vd._init.isExpInitializer(); 381 if (ie) 382 { 383 expressionInlineCost(ie.exp); 384 } 385 } 386 cost += 1; 387 } 388 // These can contain functions, which when copied, get output twice. 389 if (e.declaration.isStructDeclaration() || e.declaration.isClassDeclaration() || e.declaration.isFuncDeclaration() || e.declaration.isAttribDeclaration() || e.declaration.isTemplateMixin()) 390 { 391 cost = COST_MAX; 392 return; 393 } 394 //printf("DeclarationExp.inlineCost3('%s')\n", toChars()); 395 } 396 397 override void visit(CallExp e) 398 { 399 //printf("CallExp.inlineCost3() %s\n", toChars()); 400 // Bugzilla 3500: super.func() calls must be devirtualized, and the inliner 401 // can't handle that at present. 402 if (e.e1.op == TOKdotvar && (cast(DotVarExp)e.e1).e1.op == TOKsuper) 403 cost = COST_MAX; 404 else if (e.f && e.f.ident == Id.__alloca && e.f.linkage == LINKc && !allowAlloca) 405 cost = COST_MAX; // inlining alloca may cause stack overflows 406 else 407 cost++; 408 } 409 } 410 411 /*********************************************************** 412 * Represent a context to inline statements and expressions. 413 * 414 * Todo: 415 * It would be better to make foundReturn an instance field of DoInlineAs visitor class, 416 * like as DoInlineAs!Result.result field, because it's one another result of inlining. 417 * The best would be to return a pair of result Expression and a bool value as foundReturn 418 * from doInlineAs function. 419 */ 420 final class InlineDoState 421 { 422 // inline context 423 VarDeclaration vthis; 424 Dsymbols from; // old Dsymbols 425 Dsymbols to; // parallel array of new Dsymbols 426 Dsymbol parent; // new parent 427 FuncDeclaration fd; // function being inlined (old parent) 428 // inline result 429 bool foundReturn; 430 431 this(Dsymbol parent, FuncDeclaration fd) 432 { 433 this.parent = parent; 434 this.fd = fd; 435 } 436 } 437 438 /*********************************************************** 439 * Perform the inlining from (Statement or Expression) to (Statement or Expression). 440 * 441 * Inlining is done by: 442 * - Converting to an Expression 443 * - Copying the trees of the function to be inlined 444 * - Renaming the variables 445 */ 446 extern (C++) final class DoInlineAs(Result) : Visitor 447 if (is(Result == Statement) || is(Result == Expression)) 448 { 449 alias visit = super.visit; 450 public: 451 InlineDoState ids; 452 Result result; 453 454 enum asStatements = is(Result == Statement); 455 456 extern (D) this(InlineDoState ids) 457 { 458 this.ids = ids; 459 } 460 461 // Statement -> (Statement | Expression) 462 463 override void visit(Statement s) 464 { 465 printf("Statement.doInlineAs!%s()\n%s\n", Result.stringof.ptr, s.toChars()); 466 fflush(stdout); 467 assert(0); // default is we can't inline it 468 } 469 470 override void visit(ExpStatement s) 471 { 472 static if (LOG) 473 { 474 if (s.exp) 475 printf("ExpStatement.doInlineAs!%s() '%s'\n", Result.stringof.ptr, s.exp.toChars()); 476 } 477 478 auto exp = doInlineAs!Expression(s.exp, ids); 479 static if (asStatements) 480 result = new ExpStatement(s.loc, exp); 481 else 482 result = exp; 483 } 484 485 override void visit(CompoundStatement s) 486 { 487 //printf("CompoundStatement.doInlineAs!%s() %d\n", Result.stringof.ptr, s.statements.dim); 488 static if (asStatements) 489 { 490 auto as = new Statements(); 491 as.reserve(s.statements.dim); 492 } 493 494 foreach (i, sx; *s.statements) 495 { 496 if (!sx) 497 continue; 498 static if (asStatements) 499 { 500 as.push(doInlineAs!Statement(sx, ids)); 501 } 502 else 503 { 504 /* Specifically allow: 505 * if (condition) 506 * return exp1; 507 * return exp2; 508 */ 509 IfStatement ifs; 510 Statement s3; 511 if ((ifs = sx.isIfStatement()) !is null && 512 ifs.ifbody && 513 ifs.ifbody.isReturnStatement() && 514 !ifs.elsebody && 515 i + 1 < s.statements.dim && 516 (s3 = (*s.statements)[i + 1]) !is null && 517 s3.isReturnStatement() 518 ) 519 { 520 /* Rewrite as ?: 521 */ 522 auto econd = doInlineAs!Expression(ifs.condition, ids); 523 assert(econd); 524 auto e1 = doInlineAs!Expression(ifs.ifbody, ids); 525 assert(ids.foundReturn); 526 auto e2 = doInlineAs!Expression(s3, ids); 527 528 Expression e = new CondExp(econd.loc, econd, e1, e2); 529 e.type = e1.type; 530 if (e.type.ty == Ttuple) 531 { 532 e1.type = Type.tvoid; 533 e2.type = Type.tvoid; 534 e.type = Type.tvoid; 535 } 536 result = Expression.combine(result, e); 537 } 538 else 539 { 540 auto e = doInlineAs!Expression(sx, ids); 541 result = Expression.combine(result, e); 542 } 543 } 544 545 if (ids.foundReturn) 546 break; 547 } 548 549 static if (asStatements) 550 result = new CompoundStatement(s.loc, as); 551 } 552 553 override void visit(UnrolledLoopStatement s) 554 { 555 //printf("UnrolledLoopStatement.doInlineAs!%s() %d\n", Result.stringof.ptr, s.statements.dim); 556 static if (asStatements) 557 { 558 auto as = new Statements(); 559 as.reserve(s.statements.dim); 560 } 561 562 foreach (sx; *s.statements) 563 { 564 if (!sx) 565 continue; 566 auto r = doInlineAs!Result(sx, ids); 567 static if (asStatements) 568 as.push(r); 569 else 570 result = Expression.combine(result, r); 571 572 if (ids.foundReturn) 573 break; 574 } 575 576 static if (asStatements) 577 result = new UnrolledLoopStatement(s.loc, as); 578 } 579 580 override void visit(ScopeStatement s) 581 { 582 //printf("ScopeStatement.doInlineAs!%s() %d\n", Result.stringof.ptr, s.statement.dim); 583 auto r = doInlineAs!Result(s.statement, ids); 584 static if (asStatements) 585 result = new ScopeStatement(s.loc, r, s.endloc); 586 else 587 result = r; 588 } 589 590 override void visit(IfStatement s) 591 { 592 assert(!s.prm); 593 auto econd = doInlineAs!Expression(s.condition, ids); 594 assert(econd); 595 596 auto ifbody = doInlineAs!Result(s.ifbody, ids); 597 bool bodyReturn = ids.foundReturn; 598 599 ids.foundReturn = false; 600 auto elsebody = doInlineAs!Result(s.elsebody, ids); 601 602 static if (asStatements) 603 { 604 result = new IfStatement(s.loc, s.prm, econd, ifbody, elsebody, s.endloc); 605 } 606 else 607 { 608 alias e1 = ifbody; 609 alias e2 = elsebody; 610 if (e1 && e2) 611 { 612 result = new CondExp(econd.loc, econd, e1, e2); 613 result.type = e1.type; 614 if (result.type.ty == Ttuple) 615 { 616 e1.type = Type.tvoid; 617 e2.type = Type.tvoid; 618 result.type = Type.tvoid; 619 } 620 } 621 else if (e1) 622 { 623 result = new AndAndExp(econd.loc, econd, e1); 624 result.type = Type.tvoid; 625 } 626 else if (e2) 627 { 628 result = new OrOrExp(econd.loc, econd, e2); 629 result.type = Type.tvoid; 630 } 631 else 632 { 633 result = econd; 634 } 635 } 636 ids.foundReturn = ids.foundReturn && bodyReturn; 637 } 638 639 override void visit(ReturnStatement s) 640 { 641 //printf("ReturnStatement.doInlineAs!%s() '%s'\n", Result.stringof.ptr, s.exp ? s.exp.toChars() : ""); 642 ids.foundReturn = true; 643 644 auto exp = doInlineAs!Expression(s.exp, ids); 645 if (!exp) // Bugzilla 14560: 'return' must not leave in the expand result 646 return; 647 static if (asStatements) 648 result = new ReturnStatement(s.loc, exp); 649 else 650 result = exp; 651 } 652 653 override void visit(ImportStatement s) 654 { 655 } 656 657 override void visit(ForStatement s) 658 { 659 //printf("ForStatement.doInlineAs!%s()\n", Result.stringof.ptr); 660 static if (asStatements) 661 { 662 auto sinit = doInlineAs!Statement(s._init, ids); 663 auto scond = doInlineAs!Expression(s.condition, ids); 664 auto sincr = doInlineAs!Expression(s.increment, ids); 665 auto sbody = doInlineAs!Statement(s._body, ids); 666 result = new ForStatement(s.loc, sinit, scond, sincr, sbody, s.endloc); 667 } 668 else 669 result = null; // cannot be inlined as an Expression 670 } 671 672 override void visit(ThrowStatement s) 673 { 674 //printf("ThrowStatement.doInlineAs!%s() '%s'\n", Result.stringof.ptr, s.exp.toChars()); 675 static if (asStatements) 676 result = new ThrowStatement(s.loc, doInlineAs!Expression(s.exp, ids)); 677 else 678 result = null; // cannot be inlined as an Expression 679 } 680 681 // Expression -> (Statement | Expression) 682 683 static if (asStatements) 684 { 685 override void visit(Expression e) 686 { 687 result = new ExpStatement(e.loc, doInlineAs!Expression(e, ids)); 688 } 689 } 690 else 691 { 692 /****************************** 693 * Perform doInlineAs() on an array of Expressions. 694 */ 695 Expressions* arrayExpressionDoInline(Expressions* a) 696 { 697 if (!a) 698 return null; 699 700 auto newa = new Expressions(); 701 newa.setDim(a.dim); 702 703 foreach (i; 0 .. a.dim) 704 { 705 (*newa)[i] = doInlineAs!Expression((*a)[i], ids); 706 } 707 return newa; 708 } 709 710 override void visit(Expression e) 711 { 712 //printf("Expression.doInlineAs!%s(%s): %s\n", Result.stringof.ptr, Token.toChars(e.op), e.toChars()); 713 result = e.copy(); 714 } 715 716 override void visit(SymOffExp e) 717 { 718 //printf("SymOffExp.doInlineAs!%s(%s)\n", Result.stringof.ptr, e.toChars()); 719 foreach (i; 0 .. ids.from.dim) 720 { 721 if (e.var != ids.from[i]) 722 continue; 723 auto se = cast(SymOffExp)e.copy(); 724 se.var = cast(Declaration)ids.to[i]; 725 result = se; 726 return; 727 } 728 result = e; 729 } 730 731 override void visit(VarExp e) 732 { 733 //printf("VarExp.doInlineAs!%s(%s)\n", Result.stringof.ptr, e.toChars()); 734 foreach (i; 0 .. ids.from.dim) 735 { 736 if (e.var != ids.from[i]) 737 continue; 738 auto ve = cast(VarExp)e.copy(); 739 ve.var = cast(Declaration)ids.to[i]; 740 result = ve; 741 return; 742 } 743 if (ids.fd && e.var == ids.fd.vthis) 744 { 745 result = new VarExp(e.loc, ids.vthis); 746 result.type = e.type; 747 return; 748 } 749 750 /* Inlining context pointer access for nested referenced variables. 751 * For example: 752 * auto fun() { 753 * int i = 40; 754 * auto foo() { 755 * int g = 2; 756 * struct Result { 757 * auto bar() { return i + g; } 758 * } 759 * return Result(); 760 * } 761 * return foo(); 762 * } 763 * auto t = fun(); 764 * 'i' and 'g' are nested referenced variables in Result.bar(), so: 765 * auto x = t.bar(); 766 * should be inlined to: 767 * auto x = *(t.vthis.vthis + i.voffset) + *(t.vthis + g.voffset) 768 */ 769 auto v = e.var.isVarDeclaration(); 770 if (v && v.nestedrefs.dim && ids.vthis) 771 { 772 Dsymbol s = ids.fd; 773 auto fdv = v.toParent().isFuncDeclaration(); 774 assert(fdv); 775 result = new VarExp(e.loc, ids.vthis); 776 result.type = ids.vthis.type; 777 while (s != fdv) 778 { 779 auto f = s.isFuncDeclaration(); 780 if (auto ad = s.isThis()) 781 { 782 assert(ad.vthis); 783 result = new DotVarExp(e.loc, result, ad.vthis); 784 result.type = ad.vthis.type; 785 s = ad.toParent2(); 786 } 787 else if (f && f.isNested()) 788 { 789 assert(f.vthis); 790 if (f.hasNestedFrameRefs()) 791 { 792 result = new DotVarExp(e.loc, result, f.vthis); 793 result.type = f.vthis.type; 794 } 795 s = f.toParent2(); 796 } 797 else 798 assert(0); 799 assert(s); 800 } 801 result = new DotVarExp(e.loc, result, v); 802 result.type = v.type; 803 //printf("\t==> result = %s, type = %s\n", result.toChars(), result.type.toChars()); 804 return; 805 } 806 807 result = e; 808 } 809 810 override void visit(ThisExp e) 811 { 812 //if (!ids.vthis) 813 // e.error("no 'this' when inlining %s", ids.parent.toChars()); 814 if (!ids.vthis) 815 { 816 result = e; 817 return; 818 } 819 result = new VarExp(e.loc, ids.vthis); 820 result.type = e.type; 821 } 822 823 override void visit(SuperExp e) 824 { 825 assert(ids.vthis); 826 result = new VarExp(e.loc, ids.vthis); 827 result.type = e.type; 828 } 829 830 override void visit(DeclarationExp e) 831 { 832 //printf("DeclarationExp.doInlineAs!%s(%s)\n", Result.stringof.ptr, e.toChars()); 833 if (auto vd = e.declaration.isVarDeclaration()) 834 { 835 version (none) 836 { 837 // Need to figure this out before inlining can work for tuples 838 if (auto tup = vd.toAlias().isTupleDeclaration()) 839 { 840 foreach (i; 0 .. tup.objects.dim) 841 { 842 DsymbolExp se = (*tup.objects)[i]; 843 assert(se.op == TOKdsymbol); 844 se.s; 845 } 846 result = st.objects.dim; 847 return; 848 } 849 } 850 if (vd.isStatic()) 851 return; 852 853 if (ids.fd && vd == ids.fd.nrvo_var) 854 { 855 foreach (i; 0 .. ids.from.dim) 856 { 857 if (vd != ids.from[i]) 858 continue; 859 if (vd._init && !vd._init.isVoidInitializer()) 860 { 861 result = vd._init.toExpression(); 862 assert(result); 863 result = doInlineAs!Expression(result, ids); 864 } 865 else 866 result = new IntegerExp(vd._init.loc, 0, Type.tint32); 867 return; 868 } 869 } 870 871 auto vto = new VarDeclaration(vd.loc, vd.type, vd.ident, vd._init); 872 memcpy(cast(void*)vto, cast(void*)vd, __traits(classInstanceSize, VarDeclaration)); 873 vto.parent = ids.parent; 874 vto.csym = null; 875 vto.isym = null; 876 877 ids.from.push(vd); 878 ids.to.push(vto); 879 880 if (vd._init) 881 { 882 if (vd._init.isVoidInitializer()) 883 { 884 vto._init = new VoidInitializer(vd._init.loc); 885 } 886 else 887 { 888 auto ei = vd._init.toExpression(); 889 assert(ei); 890 vto._init = new ExpInitializer(ei.loc, doInlineAs!Expression(ei, ids)); 891 } 892 } 893 auto de = cast(DeclarationExp)e.copy(); 894 de.declaration = vto; 895 result = de; 896 return; 897 } 898 899 /* This needs work, like DeclarationExp.toElem(), if we are 900 * to handle TemplateMixin's. For now, we just don't inline them. 901 */ 902 visit(cast(Expression)e); 903 } 904 905 override void visit(TypeidExp e) 906 { 907 //printf("TypeidExp.doInlineAs!%s(): %s\n", Result.stringof.ptr, e.toChars()); 908 auto te = cast(TypeidExp)e.copy(); 909 if (auto ex = isExpression(te.obj)) 910 { 911 te.obj = doInlineAs!Expression(ex, ids); 912 } 913 else 914 assert(isType(te.obj)); 915 result = te; 916 } 917 918 override void visit(NewExp e) 919 { 920 //printf("NewExp.doInlineAs!%s(): %s\n", Result.stringof.ptr, e.toChars()); 921 auto ne = cast(NewExp)e.copy(); 922 ne.thisexp = doInlineAs!Expression(e.thisexp, ids); 923 ne.newargs = arrayExpressionDoInline(e.newargs); 924 ne.arguments = arrayExpressionDoInline(e.arguments); 925 result = ne; 926 927 semanticTypeInfo(null, e.type); 928 } 929 930 override void visit(DeleteExp e) 931 { 932 visit(cast(UnaExp)e); 933 934 Type tb = e.e1.type.toBasetype(); 935 if (tb.ty == Tarray) 936 { 937 Type tv = tb.nextOf().baseElemOf(); 938 if (tv.ty == Tstruct) 939 { 940 auto ts = cast(TypeStruct)tv; 941 auto sd = ts.sym; 942 if (sd.dtor) 943 semanticTypeInfo(null, ts); 944 } 945 } 946 } 947 948 override void visit(UnaExp e) 949 { 950 auto ue = cast(UnaExp)e.copy(); 951 ue.e1 = doInlineAs!Expression(e.e1, ids); 952 result = ue; 953 } 954 955 override void visit(AssertExp e) 956 { 957 auto ae = cast(AssertExp)e.copy(); 958 ae.e1 = doInlineAs!Expression(e.e1, ids); 959 ae.msg = doInlineAs!Expression(e.msg, ids); 960 result = ae; 961 } 962 963 override void visit(BinExp e) 964 { 965 auto be = cast(BinExp)e.copy(); 966 be.e1 = doInlineAs!Expression(e.e1, ids); 967 be.e2 = doInlineAs!Expression(e.e2, ids); 968 result = be; 969 } 970 971 override void visit(CallExp e) 972 { 973 auto ce = cast(CallExp)e.copy(); 974 ce.e1 = doInlineAs!Expression(e.e1, ids); 975 ce.arguments = arrayExpressionDoInline(e.arguments); 976 result = ce; 977 } 978 979 override void visit(AssignExp e) 980 { 981 visit(cast(BinExp)e); 982 983 if (e.e1.op == TOKarraylength) 984 { 985 auto ale = cast(ArrayLengthExp)e.e1; 986 Type tn = ale.e1.type.toBasetype().nextOf(); 987 semanticTypeInfo(null, tn); 988 } 989 } 990 991 override void visit(EqualExp e) 992 { 993 visit(cast(BinExp)e); 994 995 Type t1 = e.e1.type.toBasetype(); 996 if (t1.ty == Tarray || t1.ty == Tsarray) 997 { 998 Type t = t1.nextOf().toBasetype(); 999 while (t.toBasetype().nextOf()) 1000 t = t.nextOf().toBasetype(); 1001 if (t.ty == Tstruct) 1002 semanticTypeInfo(null, t); 1003 } 1004 else if (t1.ty == Taarray) 1005 { 1006 semanticTypeInfo(null, t1); 1007 } 1008 } 1009 1010 override void visit(IndexExp e) 1011 { 1012 auto are = cast(IndexExp)e.copy(); 1013 are.e1 = doInlineAs!Expression(e.e1, ids); 1014 if (e.lengthVar) 1015 { 1016 //printf("lengthVar\n"); 1017 auto vd = e.lengthVar; 1018 auto vto = new VarDeclaration(vd.loc, vd.type, vd.ident, vd._init); 1019 memcpy(cast(void*)vto, cast(void*)vd, __traits(classInstanceSize, VarDeclaration)); 1020 vto.parent = ids.parent; 1021 vto.csym = null; 1022 vto.isym = null; 1023 1024 ids.from.push(vd); 1025 ids.to.push(vto); 1026 1027 if (vd._init && !vd._init.isVoidInitializer()) 1028 { 1029 auto ie = vd._init.isExpInitializer(); 1030 assert(ie); 1031 vto._init = new ExpInitializer(ie.loc, doInlineAs!Expression(ie.exp, ids)); 1032 } 1033 are.lengthVar = vto; 1034 } 1035 are.e2 = doInlineAs!Expression(e.e2, ids); 1036 result = are; 1037 } 1038 1039 override void visit(SliceExp e) 1040 { 1041 auto are = cast(SliceExp)e.copy(); 1042 are.e1 = doInlineAs!Expression(e.e1, ids); 1043 if (e.lengthVar) 1044 { 1045 //printf("lengthVar\n"); 1046 auto vd = e.lengthVar; 1047 auto vto = new VarDeclaration(vd.loc, vd.type, vd.ident, vd._init); 1048 memcpy(cast(void*)vto, cast(void*)vd, __traits(classInstanceSize, VarDeclaration)); 1049 vto.parent = ids.parent; 1050 vto.csym = null; 1051 vto.isym = null; 1052 1053 ids.from.push(vd); 1054 ids.to.push(vto); 1055 1056 if (vd._init && !vd._init.isVoidInitializer()) 1057 { 1058 auto ie = vd._init.isExpInitializer(); 1059 assert(ie); 1060 vto._init = new ExpInitializer(ie.loc, doInlineAs!Expression(ie.exp, ids)); 1061 } 1062 1063 are.lengthVar = vto; 1064 } 1065 are.lwr = doInlineAs!Expression(e.lwr, ids); 1066 are.upr = doInlineAs!Expression(e.upr, ids); 1067 result = are; 1068 } 1069 1070 override void visit(TupleExp e) 1071 { 1072 auto ce = cast(TupleExp)e.copy(); 1073 ce.e0 = doInlineAs!Expression(e.e0, ids); 1074 ce.exps = arrayExpressionDoInline(e.exps); 1075 result = ce; 1076 } 1077 1078 override void visit(ArrayLiteralExp e) 1079 { 1080 auto ce = cast(ArrayLiteralExp)e.copy(); 1081 ce.basis = doInlineAs!Expression(e.basis, ids); 1082 ce.elements = arrayExpressionDoInline(e.elements); 1083 result = ce; 1084 1085 semanticTypeInfo(null, e.type); 1086 } 1087 1088 override void visit(AssocArrayLiteralExp e) 1089 { 1090 auto ce = cast(AssocArrayLiteralExp)e.copy(); 1091 ce.keys = arrayExpressionDoInline(e.keys); 1092 ce.values = arrayExpressionDoInline(e.values); 1093 result = ce; 1094 1095 semanticTypeInfo(null, e.type); 1096 } 1097 1098 override void visit(StructLiteralExp e) 1099 { 1100 if (e.inlinecopy) 1101 { 1102 result = e.inlinecopy; 1103 return; 1104 } 1105 auto ce = cast(StructLiteralExp)e.copy(); 1106 e.inlinecopy = ce; 1107 ce.elements = arrayExpressionDoInline(e.elements); 1108 e.inlinecopy = null; 1109 result = ce; 1110 } 1111 1112 override void visit(ArrayExp e) 1113 { 1114 auto ce = cast(ArrayExp)e.copy(); 1115 ce.e1 = doInlineAs!Expression(e.e1, ids); 1116 ce.arguments = arrayExpressionDoInline(e.arguments); 1117 result = ce; 1118 } 1119 1120 override void visit(CondExp e) 1121 { 1122 auto ce = cast(CondExp)e.copy(); 1123 ce.econd = doInlineAs!Expression(e.econd, ids); 1124 ce.e1 = doInlineAs!Expression(e.e1, ids); 1125 ce.e2 = doInlineAs!Expression(e.e2, ids); 1126 result = ce; 1127 } 1128 } 1129 } 1130 1131 /// ditto 1132 Result doInlineAs(Result)(Statement s, InlineDoState ids) 1133 { 1134 if (!s) 1135 return null; 1136 1137 scope DoInlineAs!Result v = new DoInlineAs!Result(ids); 1138 s.accept(v); 1139 return v.result; 1140 } 1141 1142 /// ditto 1143 Result doInlineAs(Result)(Expression e, InlineDoState ids) 1144 { 1145 if (!e) 1146 return null; 1147 1148 scope DoInlineAs!Result v = new DoInlineAs!Result(ids); 1149 e.accept(v); 1150 return v.result; 1151 } 1152 1153 /*********************************************************** 1154 * Walk the trees, looking for functions to inline. 1155 * Inline any that can be. 1156 */ 1157 extern (C++) final class InlineScanVisitor : Visitor 1158 { 1159 alias visit = super.visit; 1160 public: 1161 FuncDeclaration parent; // function being scanned 1162 // As the visit method cannot return a value, these variables 1163 // are used to pass the result from 'visit' back to 'inlineScan' 1164 Statement sresult; 1165 Expression eresult; 1166 bool again; 1167 1168 extern (D) this() 1169 { 1170 } 1171 1172 override void visit(Statement s) 1173 { 1174 } 1175 1176 override void visit(ExpStatement s) 1177 { 1178 static if (LOG) 1179 { 1180 printf("ExpStatement.inlineScan(%s)\n", s.toChars()); 1181 } 1182 if (!s.exp) 1183 return; 1184 1185 Statement inlineScanExpAsStatement(ref Expression exp) 1186 { 1187 /* If there's a TOKcall at the top, then it may fail to inline 1188 * as an Expression. Try to inline as a Statement instead. 1189 */ 1190 if (exp.op == TOKcall) 1191 { 1192 visitCallExp(cast(CallExp)exp, null, true); 1193 if (eresult) 1194 exp = eresult; 1195 auto s = sresult; 1196 sresult = null; 1197 eresult = null; 1198 return s; 1199 } 1200 1201 /* If there's a CondExp or CommaExp at the top, then its 1202 * sub-expressions may be inlined as statements. 1203 */ 1204 if (exp.op == TOKquestion) 1205 { 1206 auto e = cast(CondExp)exp; 1207 inlineScan(e.econd); 1208 auto s1 = inlineScanExpAsStatement(e.e1); 1209 auto s2 = inlineScanExpAsStatement(e.e2); 1210 if (!s1 && !s2) 1211 return null; 1212 auto ifbody = !s1 ? new ExpStatement(e.e1.loc, e.e1) : s1; 1213 auto elsebody = !s2 ? new ExpStatement(e.e2.loc, e.e2) : s2; 1214 return new IfStatement(exp.loc, null, e.econd, ifbody, elsebody, exp.loc); 1215 } 1216 if (exp.op == TOKcomma) 1217 { 1218 auto e = cast(CommaExp)exp; 1219 auto s1 = inlineScanExpAsStatement(e.e1); 1220 auto s2 = inlineScanExpAsStatement(e.e2); 1221 if (!s1 && !s2) 1222 return null; 1223 auto a = new Statements(); 1224 a.push(!s1 ? new ExpStatement(e.e1.loc, e.e1) : s1); 1225 a.push(!s2 ? new ExpStatement(e.e2.loc, e.e2) : s2); 1226 return new CompoundStatement(exp.loc, a); 1227 } 1228 1229 // inline as an expression 1230 inlineScan(exp); 1231 return null; 1232 } 1233 1234 sresult = inlineScanExpAsStatement(s.exp); 1235 } 1236 1237 override void visit(CompoundStatement s) 1238 { 1239 foreach (i; 0 .. s.statements.dim) 1240 { 1241 inlineScan((*s.statements)[i]); 1242 } 1243 } 1244 1245 override void visit(UnrolledLoopStatement s) 1246 { 1247 foreach (i; 0 .. s.statements.dim) 1248 { 1249 inlineScan((*s.statements)[i]); 1250 } 1251 } 1252 1253 override void visit(ScopeStatement s) 1254 { 1255 inlineScan(s.statement); 1256 } 1257 1258 override void visit(WhileStatement s) 1259 { 1260 inlineScan(s.condition); 1261 inlineScan(s._body); 1262 } 1263 1264 override void visit(DoStatement s) 1265 { 1266 inlineScan(s._body); 1267 inlineScan(s.condition); 1268 } 1269 1270 override void visit(ForStatement s) 1271 { 1272 inlineScan(s._init); 1273 inlineScan(s.condition); 1274 inlineScan(s.increment); 1275 inlineScan(s._body); 1276 } 1277 1278 override void visit(ForeachStatement s) 1279 { 1280 inlineScan(s.aggr); 1281 inlineScan(s._body); 1282 } 1283 1284 override void visit(ForeachRangeStatement s) 1285 { 1286 inlineScan(s.lwr); 1287 inlineScan(s.upr); 1288 inlineScan(s._body); 1289 } 1290 1291 override void visit(IfStatement s) 1292 { 1293 inlineScan(s.condition); 1294 inlineScan(s.ifbody); 1295 inlineScan(s.elsebody); 1296 } 1297 1298 override void visit(SwitchStatement s) 1299 { 1300 //printf("SwitchStatement.inlineScan()\n"); 1301 inlineScan(s.condition); 1302 inlineScan(s._body); 1303 Statement sdefault = s.sdefault; 1304 inlineScan(sdefault); 1305 s.sdefault = cast(DefaultStatement)sdefault; 1306 if (s.cases) 1307 { 1308 foreach (i; 0 .. s.cases.dim) 1309 { 1310 Statement scase = (*s.cases)[i]; 1311 inlineScan(scase); 1312 (*s.cases)[i] = cast(CaseStatement)scase; 1313 } 1314 } 1315 } 1316 1317 override void visit(CaseStatement s) 1318 { 1319 //printf("CaseStatement.inlineScan()\n"); 1320 inlineScan(s.exp); 1321 inlineScan(s.statement); 1322 } 1323 1324 override void visit(DefaultStatement s) 1325 { 1326 inlineScan(s.statement); 1327 } 1328 1329 override void visit(ReturnStatement s) 1330 { 1331 //printf("ReturnStatement.inlineScan()\n"); 1332 inlineScan(s.exp); 1333 } 1334 1335 override void visit(SynchronizedStatement s) 1336 { 1337 inlineScan(s.exp); 1338 inlineScan(s._body); 1339 } 1340 1341 override void visit(WithStatement s) 1342 { 1343 inlineScan(s.exp); 1344 inlineScan(s._body); 1345 } 1346 1347 override void visit(TryCatchStatement s) 1348 { 1349 inlineScan(s._body); 1350 if (s.catches) 1351 { 1352 foreach (c; *s.catches) 1353 { 1354 inlineScan(c.handler); 1355 } 1356 } 1357 } 1358 1359 override void visit(TryFinallyStatement s) 1360 { 1361 inlineScan(s._body); 1362 inlineScan(s.finalbody); 1363 } 1364 1365 override void visit(ThrowStatement s) 1366 { 1367 inlineScan(s.exp); 1368 } 1369 1370 override void visit(LabelStatement s) 1371 { 1372 inlineScan(s.statement); 1373 } 1374 1375 /******************************** 1376 * Scan Statement s for inlining opportunities, 1377 * and if found replace s with an inlined one. 1378 * Params: 1379 * s = Statement to be scanned and updated 1380 */ 1381 void inlineScan(ref Statement s) 1382 { 1383 if (!s) 1384 return; 1385 assert(sresult is null); 1386 s.accept(this); 1387 if (sresult) 1388 { 1389 s = sresult; 1390 sresult = null; 1391 } 1392 } 1393 1394 /* -------------------------- */ 1395 void arrayInlineScan(Expressions* arguments) 1396 { 1397 if (arguments) 1398 { 1399 foreach (i; 0 .. arguments.dim) 1400 { 1401 inlineScan((*arguments)[i]); 1402 } 1403 } 1404 } 1405 1406 override void visit(Expression e) 1407 { 1408 } 1409 1410 void scanVar(Dsymbol s) 1411 { 1412 //printf("scanVar(%s %s)\n", s.kind(), s.toPrettyChars()); 1413 VarDeclaration vd = s.isVarDeclaration(); 1414 if (vd) 1415 { 1416 TupleDeclaration td = vd.toAlias().isTupleDeclaration(); 1417 if (td) 1418 { 1419 foreach (i; 0 .. td.objects.dim) 1420 { 1421 DsymbolExp se = cast(DsymbolExp)(*td.objects)[i]; 1422 assert(se.op == TOKdsymbol); 1423 scanVar(se.s); // TODO 1424 } 1425 } 1426 else if (vd._init) 1427 { 1428 if (ExpInitializer ie = vd._init.isExpInitializer()) 1429 { 1430 inlineScan(ie.exp); 1431 } 1432 } 1433 } 1434 else 1435 { 1436 s.accept(this); 1437 } 1438 } 1439 1440 override void visit(DeclarationExp e) 1441 { 1442 //printf("DeclarationExp.inlineScan()\n"); 1443 scanVar(e.declaration); 1444 } 1445 1446 override void visit(UnaExp e) 1447 { 1448 inlineScan(e.e1); 1449 } 1450 1451 override void visit(AssertExp e) 1452 { 1453 inlineScan(e.e1); 1454 inlineScan(e.msg); 1455 } 1456 1457 override void visit(BinExp e) 1458 { 1459 inlineScan(e.e1); 1460 inlineScan(e.e2); 1461 } 1462 1463 override void visit(AssignExp e) 1464 { 1465 // Look for NRVO, as inlining NRVO function returns require special handling 1466 if (e.op == TOKconstruct && e.e2.op == TOKcall) 1467 { 1468 CallExp ce = cast(CallExp)e.e2; 1469 if (ce.f && ce.f.nrvo_can && ce.f.nrvo_var) // NRVO 1470 { 1471 if (e.e1.op == TOKvar) 1472 { 1473 /* Inlining: 1474 * S s = foo(); // initializing by rvalue 1475 * S s = S(1); // constructor call 1476 */ 1477 Declaration d = (cast(VarExp)e.e1).var; 1478 if (d.storage_class & (STCout | STCref)) // refinit 1479 goto L1; 1480 } 1481 else 1482 { 1483 /* Inlining: 1484 * this.field = foo(); // inside constructor 1485 */ 1486 inlineScan(e.e1); 1487 } 1488 1489 visitCallExp(ce, e.e1, false); 1490 if (eresult) 1491 { 1492 //printf("call with nrvo: %s ==> %s\n", e.toChars(), eresult.toChars()); 1493 return; 1494 } 1495 } 1496 } 1497 L1: 1498 visit(cast(BinExp)e); 1499 } 1500 1501 override void visit(CallExp e) 1502 { 1503 //printf("CallExp.inlineScan() %s\n", e.toChars()); 1504 visitCallExp(e, null, false); 1505 } 1506 1507 /************************************** 1508 * Check function call to see if can be inlined, 1509 * and then inline it if it can. 1510 * Params: 1511 * e = the function call 1512 * eret = if !null, then this is the lvalue of the nrvo function result 1513 * asStatements = if inline as statements rather than as an Expression 1514 * Returns: 1515 * this.eresult if asStatements == false 1516 * this.sresult if asStatements == true 1517 */ 1518 void visitCallExp(CallExp e, Expression eret, bool asStatements) 1519 { 1520 inlineScan(e.e1); 1521 arrayInlineScan(e.arguments); 1522 1523 //printf("visitCallExp() %s\n", e.toChars()); 1524 FuncDeclaration fd; 1525 1526 void inlineFd() 1527 { 1528 if (fd && fd != parent && canInline(fd, false, false, asStatements)) 1529 { 1530 expandInline(e.loc, fd, parent, eret, null, e.arguments, asStatements, eresult, sresult, again); 1531 } 1532 } 1533 1534 /* Pattern match various ASTs looking for indirect function calls, delegate calls, 1535 * function literal calls, delegate literal calls, and dot member calls. 1536 * If so, and that is only assigned its _init. 1537 * If so, do 'copy propagation' of the _init value and try to inline it. 1538 */ 1539 if (e.e1.op == TOKvar) 1540 { 1541 VarExp ve = cast(VarExp)e.e1; 1542 fd = ve.var.isFuncDeclaration(); 1543 if (fd) 1544 // delegate call 1545 inlineFd(); 1546 else 1547 { 1548 // delegate literal call 1549 auto v = ve.var.isVarDeclaration(); 1550 if (v && v._init && v.type.ty == Tdelegate && onlyOneAssign(v, parent)) 1551 { 1552 //printf("init: %s\n", v._init.toChars()); 1553 auto ei = v._init.isExpInitializer(); 1554 if (ei && ei.exp.op == TOKblit) 1555 { 1556 Expression e2 = (cast(AssignExp)ei.exp).e2; 1557 if (e2.op == TOKfunction) 1558 { 1559 auto fld = (cast(FuncExp)e2).fd; 1560 assert(fld.tok == TOKdelegate); 1561 fd = fld; 1562 inlineFd(); 1563 } 1564 else if (e2.op == TOKdelegate) 1565 { 1566 auto de = cast(DelegateExp)e2; 1567 if (de.e1.op == TOKvar) 1568 { 1569 auto ve2 = cast(VarExp)de.e1; 1570 fd = ve2.var.isFuncDeclaration(); 1571 inlineFd(); 1572 } 1573 } 1574 } 1575 } 1576 } 1577 } 1578 else if (e.e1.op == TOKdotvar) 1579 { 1580 DotVarExp dve = cast(DotVarExp)e.e1; 1581 fd = dve.var.isFuncDeclaration(); 1582 if (fd && fd != parent && canInline(fd, true, false, asStatements)) 1583 { 1584 if (dve.e1.op == TOKcall && dve.e1.type.toBasetype().ty == Tstruct) 1585 { 1586 /* To create ethis, we'll need to take the address 1587 * of dve.e1, but this won't work if dve.e1 is 1588 * a function call. 1589 */ 1590 } 1591 else 1592 { 1593 expandInline(e.loc, fd, parent, eret, dve.e1, e.arguments, asStatements, eresult, sresult, again); 1594 } 1595 } 1596 } 1597 else if (e.e1.op == TOKstar && 1598 (cast(PtrExp)e.e1).e1.op == TOKvar) 1599 { 1600 VarExp ve = cast(VarExp)(cast(PtrExp)e.e1).e1; 1601 VarDeclaration v = ve.var.isVarDeclaration(); 1602 if (v && v._init && onlyOneAssign(v, parent)) 1603 { 1604 //printf("init: %s\n", v._init.toChars()); 1605 auto ei = v._init.isExpInitializer(); 1606 if (ei && ei.exp.op == TOKblit) 1607 { 1608 Expression e2 = (cast(AssignExp)ei.exp).e2; 1609 // function pointer call 1610 if (e2.op == TOKsymoff) 1611 { 1612 auto se = cast(SymOffExp)e2; 1613 fd = se.var.isFuncDeclaration(); 1614 inlineFd(); 1615 } 1616 // function literal call 1617 else if (e2.op == TOKfunction) 1618 { 1619 auto fld = (cast(FuncExp)e2).fd; 1620 assert(fld.tok == TOKfunction); 1621 fd = fld; 1622 inlineFd(); 1623 } 1624 } 1625 } 1626 } 1627 else 1628 return; 1629 1630 if (global.params.verbose && (eresult || sresult)) 1631 fprintf(global.stdmsg, "inlined %s =>\n %s\n", fd.toPrettyChars(), parent.toPrettyChars()); 1632 1633 if (eresult && e.type.ty != Tvoid) 1634 { 1635 Expression ex = eresult; 1636 while (ex.op == TOKcomma) 1637 { 1638 ex.type = e.type; 1639 ex = (cast(CommaExp)ex).e2; 1640 } 1641 ex.type = e.type; 1642 } 1643 } 1644 1645 override void visit(SliceExp e) 1646 { 1647 inlineScan(e.e1); 1648 inlineScan(e.lwr); 1649 inlineScan(e.upr); 1650 } 1651 1652 override void visit(TupleExp e) 1653 { 1654 //printf("TupleExp.inlineScan()\n"); 1655 inlineScan(e.e0); 1656 arrayInlineScan(e.exps); 1657 } 1658 1659 override void visit(ArrayLiteralExp e) 1660 { 1661 //printf("ArrayLiteralExp.inlineScan()\n"); 1662 inlineScan(e.basis); 1663 arrayInlineScan(e.elements); 1664 } 1665 1666 override void visit(AssocArrayLiteralExp e) 1667 { 1668 //printf("AssocArrayLiteralExp.inlineScan()\n"); 1669 arrayInlineScan(e.keys); 1670 arrayInlineScan(e.values); 1671 } 1672 1673 override void visit(StructLiteralExp e) 1674 { 1675 //printf("StructLiteralExp.inlineScan()\n"); 1676 if (e.stageflags & stageInlineScan) 1677 return; 1678 int old = e.stageflags; 1679 e.stageflags |= stageInlineScan; 1680 arrayInlineScan(e.elements); 1681 e.stageflags = old; 1682 } 1683 1684 override void visit(ArrayExp e) 1685 { 1686 //printf("ArrayExp.inlineScan()\n"); 1687 inlineScan(e.e1); 1688 arrayInlineScan(e.arguments); 1689 } 1690 1691 override void visit(CondExp e) 1692 { 1693 inlineScan(e.econd); 1694 inlineScan(e.e1); 1695 inlineScan(e.e2); 1696 } 1697 1698 /******************************** 1699 * Scan Expression e for inlining opportunities, 1700 * and if found replace e with an inlined one. 1701 * Params: 1702 * e = Expression to be scanned and updated 1703 */ 1704 void inlineScan(ref Expression e) 1705 { 1706 if (!e) 1707 return; 1708 assert(eresult is null); 1709 e.accept(this); 1710 if (eresult) 1711 { 1712 e = eresult; 1713 eresult = null; 1714 } 1715 } 1716 1717 /************************************* 1718 * Look for function inlining possibilities. 1719 */ 1720 override void visit(Dsymbol d) 1721 { 1722 // Most Dsymbols aren't functions 1723 } 1724 1725 override void visit(FuncDeclaration fd) 1726 { 1727 static if (LOG) 1728 { 1729 printf("FuncDeclaration.inlineScan('%s')\n", fd.toPrettyChars()); 1730 } 1731 if (fd.isUnitTestDeclaration() && !global.params.useUnitTests || 1732 fd.flags & FUNCFLAGinlineScanned) 1733 return; 1734 if (fd.fbody && !fd.naked) 1735 { 1736 auto againsave = again; 1737 auto parentsave = parent; 1738 parent = fd; 1739 do 1740 { 1741 again = false; 1742 fd.inlineNest++; 1743 fd.flags |= FUNCFLAGinlineScanned; 1744 inlineScan(fd.fbody); 1745 fd.inlineNest--; 1746 } 1747 while (again); 1748 again = againsave; 1749 parent = parentsave; 1750 } 1751 } 1752 1753 override void visit(AttribDeclaration d) 1754 { 1755 Dsymbols* decls = d.include(null, null); 1756 if (decls) 1757 { 1758 foreach (i; 0 .. decls.dim) 1759 { 1760 Dsymbol s = (*decls)[i]; 1761 //printf("AttribDeclaration.inlineScan %s\n", s.toChars()); 1762 s.accept(this); 1763 } 1764 } 1765 } 1766 1767 override void visit(AggregateDeclaration ad) 1768 { 1769 //printf("AggregateDeclaration.inlineScan(%s)\n", toChars()); 1770 if (ad.members) 1771 { 1772 foreach (i; 0 .. ad.members.dim) 1773 { 1774 Dsymbol s = (*ad.members)[i]; 1775 //printf("inline scan aggregate symbol '%s'\n", s.toChars()); 1776 s.accept(this); 1777 } 1778 } 1779 } 1780 1781 override void visit(TemplateInstance ti) 1782 { 1783 static if (LOG) 1784 { 1785 printf("TemplateInstance.inlineScan('%s')\n", ti.toChars()); 1786 } 1787 if (!ti.errors && ti.members) 1788 { 1789 foreach (i; 0 .. ti.members.dim) 1790 { 1791 Dsymbol s = (*ti.members)[i]; 1792 s.accept(this); 1793 } 1794 } 1795 } 1796 } 1797 1798 /*********************************************************** 1799 * Test that `fd` can be inlined. 1800 * 1801 * Params: 1802 * hasthis = `true` if the function call has explicit 'this' expression. 1803 * hdrscan = `true` if the inline scan is for 'D header' content. 1804 * statementsToo = `true` if the function call is placed on ExpStatement. 1805 * It means more code-block dependent statements in fd body - ForStatement, 1806 * ThrowStatement, etc. can be inlined. 1807 * 1808 * Returns: 1809 * true if the function body can be expanded. 1810 * 1811 * Todo: 1812 * - Would be able to eliminate `hasthis` parameter, because semantic analysis 1813 * no longer accepts calls of contextful function without valid 'this'. 1814 * - Would be able to eliminate `hdrscan` parameter, because it's always false. 1815 */ 1816 bool canInline(FuncDeclaration fd, bool hasthis, bool hdrscan, bool statementsToo) 1817 { 1818 int cost; 1819 1820 static if (CANINLINE_LOG) 1821 { 1822 printf("FuncDeclaration.canInline(hasthis = %d, statementsToo = %d, '%s')\n", 1823 hasthis, statementsToo, fd.toPrettyChars()); 1824 } 1825 1826 if (fd.needThis() && !hasthis) 1827 return false; 1828 1829 if (fd.inlineNest) 1830 { 1831 static if (CANINLINE_LOG) 1832 { 1833 printf("\t1: no, inlineNest = %d, semanticRun = %d\n", fd.inlineNest, fd.semanticRun); 1834 } 1835 return false; 1836 } 1837 1838 if (fd.semanticRun < PASSsemantic3 && !hdrscan) 1839 { 1840 if (!fd.fbody) 1841 return false; 1842 if (!fd.functionSemantic3()) 1843 return false; 1844 Module.runDeferredSemantic3(); 1845 if (global.errors) 1846 return false; 1847 assert(fd.semanticRun >= PASSsemantic3done); 1848 } 1849 1850 switch (statementsToo ? fd.inlineStatusStmt : fd.inlineStatusExp) 1851 { 1852 case ILSyes: 1853 static if (CANINLINE_LOG) 1854 { 1855 printf("\t1: yes %s\n", fd.toChars()); 1856 } 1857 return true; 1858 case ILSno: 1859 static if (CANINLINE_LOG) 1860 { 1861 printf("\t1: no %s\n", fd.toChars()); 1862 } 1863 return false; 1864 case ILSuninitialized: 1865 break; 1866 default: 1867 assert(0); 1868 } 1869 1870 switch (fd.inlining) 1871 { 1872 case PINLINEdefault: 1873 break; 1874 case PINLINEalways: 1875 break; 1876 case PINLINEnever: 1877 return false; 1878 default: 1879 assert(0); 1880 } 1881 1882 if (fd.type) 1883 { 1884 assert(fd.type.ty == Tfunction); 1885 TypeFunction tf = cast(TypeFunction)fd.type; 1886 1887 // no variadic parameter lists 1888 if (tf.varargs == 1) 1889 goto Lno; 1890 1891 /* Don't inline a function that returns non-void, but has 1892 * no return expression. 1893 * No statement inlining for non-voids. 1894 */ 1895 if (tf.next && tf.next.ty != Tvoid && 1896 (!(fd.hasReturnExp & 1) || statementsToo) && 1897 !hdrscan) 1898 { 1899 goto Lno; 1900 } 1901 1902 /* Bugzilla 14560: If fd returns void, all explicit `return;`s 1903 * must not appear in the expanded result. 1904 * See also ReturnStatement.doInlineAs!Statement(). 1905 */ 1906 } 1907 1908 // cannot inline constructor calls because we need to convert: 1909 // return; 1910 // to: 1911 // return this; 1912 // ensure() has magic properties the inliner loses 1913 // require() has magic properties too 1914 // see bug 7699 1915 // no nested references to this frame 1916 if (!fd.fbody || 1917 fd.ident == Id.ensure || 1918 (fd.ident == Id.require && 1919 fd.toParent().isFuncDeclaration() && 1920 fd.toParent().isFuncDeclaration().needThis()) || 1921 !hdrscan && (fd.isSynchronized() || 1922 fd.isImportedSymbol() || 1923 fd.hasNestedFrameRefs() || 1924 (fd.isVirtual() && !fd.isFinalFunc()))) 1925 { 1926 goto Lno; 1927 } 1928 1929 { 1930 scope InlineCostVisitor icv = new InlineCostVisitor(); 1931 icv.hasthis = hasthis; 1932 icv.fd = fd; 1933 icv.hdrscan = hdrscan; 1934 fd.fbody.accept(icv); 1935 cost = icv.cost; 1936 } 1937 static if (CANINLINE_LOG) 1938 { 1939 printf("\tcost = %d for %s\n", cost, fd.toChars()); 1940 } 1941 1942 if (tooCostly(cost)) 1943 goto Lno; 1944 if (!statementsToo && cost > COST_MAX) 1945 goto Lno; 1946 1947 if (!hdrscan) 1948 { 1949 // Don't modify inlineStatus for header content scan 1950 if (statementsToo) 1951 fd.inlineStatusStmt = ILSyes; 1952 else 1953 fd.inlineStatusExp = ILSyes; 1954 1955 scope InlineScanVisitor v = new InlineScanVisitor(); 1956 fd.accept(v); // Don't scan recursively for header content scan 1957 1958 if (fd.inlineStatusExp == ILSuninitialized) 1959 { 1960 // Need to redo cost computation, as some statements or expressions have been inlined 1961 scope InlineCostVisitor icv = new InlineCostVisitor(); 1962 icv.hasthis = hasthis; 1963 icv.fd = fd; 1964 icv.hdrscan = hdrscan; 1965 fd.fbody.accept(icv); 1966 cost = icv.cost; 1967 static if (CANINLINE_LOG) 1968 { 1969 printf("recomputed cost = %d for %s\n", cost, fd.toChars()); 1970 } 1971 1972 if (tooCostly(cost)) 1973 goto Lno; 1974 if (!statementsToo && cost > COST_MAX) 1975 goto Lno; 1976 1977 if (statementsToo) 1978 fd.inlineStatusStmt = ILSyes; 1979 else 1980 fd.inlineStatusExp = ILSyes; 1981 } 1982 } 1983 static if (CANINLINE_LOG) 1984 { 1985 printf("\t2: yes %s\n", fd.toChars()); 1986 } 1987 return true; 1988 1989 Lno: 1990 if (fd.inlining == PINLINEalways) 1991 fd.error("cannot inline function"); 1992 1993 if (!hdrscan) // Don't modify inlineStatus for header content scan 1994 { 1995 if (statementsToo) 1996 fd.inlineStatusStmt = ILSno; 1997 else 1998 fd.inlineStatusExp = ILSno; 1999 } 2000 static if (CANINLINE_LOG) 2001 { 2002 printf("\t2: no %s\n", fd.toChars()); 2003 } 2004 return false; 2005 } 2006 2007 /*********************************************************** 2008 * Scan function implementations in Module m looking for functions that can be inlined, 2009 * and inline them in situ. 2010 * 2011 * Params: 2012 * m = module to scan 2013 */ 2014 public void inlineScanModule(Module m) 2015 { 2016 if (m.semanticRun != PASSsemantic3done) 2017 return; 2018 m.semanticRun = PASSinline; 2019 2020 // Note that modules get their own scope, from scratch. 2021 // This is so regardless of where in the syntax a module 2022 // gets imported, it is unaffected by context. 2023 2024 //printf("Module = %p\n", m.sc.scopesym); 2025 2026 foreach (i; 0 .. m.members.dim) 2027 { 2028 Dsymbol s = (*m.members)[i]; 2029 //if (global.params.verbose) 2030 // fprintf(global.stdmsg, "inline scan symbol %s\n", s.toChars()); 2031 scope InlineScanVisitor v = new InlineScanVisitor(); 2032 s.accept(v); 2033 } 2034 m.semanticRun = PASSinlinedone; 2035 } 2036 2037 /*********************************************************** 2038 * Expand a function call inline, 2039 * ethis.fd(arguments) 2040 * 2041 * Params: 2042 * callLoc = location of CallExp 2043 * fd = function to expand 2044 * parent = function that the call to fd is being expanded into 2045 * eret = expression describing the lvalue of where the return value goes 2046 * ethis = 'this' reference 2047 * arguments = arguments passed to fd 2048 * asStatements = expand to Statements rather than Expressions 2049 * eresult = if expanding to an expression, this is where the expression is written to 2050 * sresult = if expanding to a statement, this is where the statement is written to 2051 * again = if true, then fd can be inline scanned again because there may be 2052 * more opportunities for inlining 2053 */ 2054 void expandInline(Loc callLoc, FuncDeclaration fd, FuncDeclaration parent, Expression eret, 2055 Expression ethis, Expressions* arguments, bool asStatements, 2056 out Expression eresult, out Statement sresult, out bool again) 2057 { 2058 TypeFunction tf = cast(TypeFunction)fd.type; 2059 static if (LOG || CANINLINE_LOG || EXPANDINLINE_LOG) 2060 printf("FuncDeclaration.expandInline('%s')\n", fd.toChars()); 2061 static if (EXPANDINLINE_LOG) 2062 { 2063 if (eret) printf("\teret = %s\n", eret.toChars()); 2064 if (ethis) printf("\tethis = %s\n", ethis.toChars()); 2065 } 2066 scope ids = new InlineDoState(parent, fd); 2067 2068 if (fd.isNested()) 2069 { 2070 if (!parent.inlinedNestedCallees) 2071 parent.inlinedNestedCallees = new FuncDeclarations(); 2072 parent.inlinedNestedCallees.push(fd); 2073 } 2074 2075 VarDeclaration vret; // will be set the function call result 2076 if (eret) 2077 { 2078 if (eret.op == TOKvar) 2079 { 2080 vret = (cast(VarExp)eret).var.isVarDeclaration(); 2081 assert(!(vret.storage_class & (STCout | STCref))); 2082 eret = null; 2083 } 2084 else 2085 { 2086 /* Inlining: 2087 * this.field = foo(); // inside constructor 2088 */ 2089 auto ei = new ExpInitializer(callLoc, null); 2090 auto tmp = Identifier.generateId("__retvar"); 2091 vret = new VarDeclaration(fd.loc, eret.type, tmp, ei); 2092 vret.storage_class |= STCtemp | STCref; 2093 vret.linkage = LINKd; 2094 vret.parent = parent; 2095 2096 ei.exp = new ConstructExp(fd.loc, vret, eret); 2097 ei.exp.type = vret.type; 2098 2099 auto de = new DeclarationExp(fd.loc, vret); 2100 de.type = Type.tvoid; 2101 eret = de; 2102 } 2103 2104 if (!asStatements && fd.nrvo_var) 2105 { 2106 ids.from.push(fd.nrvo_var); 2107 ids.to.push(vret); 2108 } 2109 } 2110 else 2111 { 2112 if (!asStatements && fd.nrvo_var) 2113 { 2114 auto tmp = Identifier.generateId("__retvar"); 2115 vret = new VarDeclaration(fd.loc, fd.nrvo_var.type, tmp, new VoidInitializer(fd.loc)); 2116 assert(!tf.isref); 2117 vret.storage_class = STCtemp | STCrvalue; 2118 vret.linkage = tf.linkage; 2119 vret.parent = parent; 2120 2121 auto de = new DeclarationExp(fd.loc, vret); 2122 de.type = Type.tvoid; 2123 eret = de; 2124 2125 ids.from.push(fd.nrvo_var); 2126 ids.to.push(vret); 2127 } 2128 } 2129 2130 // Set up vthis 2131 VarDeclaration vthis; 2132 if (ethis) 2133 { 2134 Expression e0; 2135 ethis = Expression.extractLast(ethis, &e0); 2136 if (ethis.op == TOKvar) 2137 { 2138 vthis = (cast(VarExp)ethis).var.isVarDeclaration(); 2139 } 2140 else 2141 { 2142 //assert(ethis.type.ty != Tpointer); 2143 if (ethis.type.ty == Tpointer) 2144 { 2145 Type t = ethis.type.nextOf(); 2146 ethis = new PtrExp(ethis.loc, ethis); 2147 ethis.type = t; 2148 } 2149 2150 auto ei = new ExpInitializer(fd.loc, ethis); 2151 vthis = new VarDeclaration(fd.loc, ethis.type, Id.This, ei); 2152 if (ethis.type.ty != Tclass) 2153 vthis.storage_class = STCref; 2154 else 2155 vthis.storage_class = STCin; 2156 vthis.linkage = LINKd; 2157 vthis.parent = parent; 2158 2159 ei.exp = new ConstructExp(fd.loc, vthis, ethis); 2160 ei.exp.type = vthis.type; 2161 2162 auto de = new DeclarationExp(fd.loc, vthis); 2163 de.type = Type.tvoid; 2164 e0 = Expression.combine(e0, de); 2165 } 2166 ethis = e0; 2167 2168 ids.vthis = vthis; 2169 } 2170 2171 // Set up parameters 2172 Expression eparams; 2173 if (arguments && arguments.dim) 2174 { 2175 assert(fd.parameters.dim == arguments.dim); 2176 foreach (i; 0 .. arguments.dim) 2177 { 2178 auto vfrom = (*fd.parameters)[i]; 2179 auto arg = (*arguments)[i]; 2180 2181 auto ei = new ExpInitializer(vfrom.loc, arg); 2182 auto vto = new VarDeclaration(vfrom.loc, vfrom.type, vfrom.ident, ei); 2183 vto.storage_class |= vfrom.storage_class & (STCtemp | STCin | STCout | STClazy | STCref); 2184 vto.linkage = vfrom.linkage; 2185 vto.parent = parent; 2186 //printf("vto = '%s', vto.storage_class = x%x\n", vto.toChars(), vto.storage_class); 2187 //printf("vto.parent = '%s'\n", parent.toChars()); 2188 2189 // Even if vto is STClazy, `vto = arg` is handled correctly in glue layer. 2190 ei.exp = new BlitExp(vto.loc, vto, arg); 2191 ei.exp.type = vto.type; 2192 //arg.type.print(); 2193 //ei.exp.print(); 2194 2195 ids.from.push(vfrom); 2196 ids.to.push(vto); 2197 2198 auto de = new DeclarationExp(vto.loc, vto); 2199 de.type = Type.tvoid; 2200 eparams = Expression.combine(eparams, de); 2201 2202 /* If function pointer or delegate parameters are present, 2203 * inline scan again because if they are initialized to a symbol, 2204 * any calls to the fp or dg can be inlined. 2205 */ 2206 if (vfrom.type.ty == Tdelegate || 2207 vfrom.type.ty == Tpointer && vfrom.type.nextOf().ty == Tfunction) 2208 { 2209 if (arg.op == TOKvar) 2210 { 2211 VarExp ve = cast(VarExp)arg; 2212 if (ve.var.isFuncDeclaration()) 2213 again = true; 2214 } 2215 else if (arg.op == TOKsymoff) 2216 { 2217 SymOffExp se = cast(SymOffExp)arg; 2218 if (se.var.isFuncDeclaration()) 2219 again = true; 2220 } 2221 else if (arg.op == TOKfunction || arg.op == TOKdelegate) 2222 again = true; 2223 } 2224 } 2225 } 2226 2227 if (asStatements) 2228 { 2229 /* Construct: 2230 * { eret; ethis; eparams; fd.fbody; } 2231 * or: 2232 * { eret; ethis; try { eparams; fd.fbody; } finally { vthis.edtor; } } 2233 */ 2234 2235 auto as = new Statements(); 2236 if (eret) 2237 as.push(new ExpStatement(callLoc, eret)); 2238 if (ethis) 2239 as.push(new ExpStatement(callLoc, ethis)); 2240 2241 auto as2 = as; 2242 if (vthis && !vthis.isDataseg()) 2243 { 2244 if (vthis.needsScopeDtor()) 2245 { 2246 // same with ExpStatement.scopeCode() 2247 as2 = new Statements(); 2248 vthis.storage_class |= STCnodtor; 2249 } 2250 } 2251 2252 if (eparams) 2253 as2.push(new ExpStatement(callLoc, eparams)); 2254 2255 fd.inlineNest++; 2256 Statement s = doInlineAs!Statement(fd.fbody, ids); 2257 fd.inlineNest--; 2258 as2.push(s); 2259 2260 if (as2 != as) 2261 { 2262 as.push(new TryFinallyStatement(callLoc, 2263 new CompoundStatement(callLoc, as2), 2264 new DtorExpStatement(callLoc, vthis.edtor, vthis))); 2265 } 2266 2267 sresult = new ScopeStatement(callLoc, new CompoundStatement(callLoc, as), callLoc); 2268 2269 static if (EXPANDINLINE_LOG) 2270 printf("\n[%s] %s expandInline sresult =\n%s\n", 2271 callLoc.toChars(), fd.toPrettyChars(), sresult.toChars()); 2272 } 2273 else 2274 { 2275 /* Construct: 2276 * (eret, ethis, eparams, fd.fbody) 2277 */ 2278 2279 fd.inlineNest++; 2280 auto e = doInlineAs!Expression(fd.fbody, ids); 2281 fd.inlineNest--; 2282 //e.type.print(); 2283 //e.print(); 2284 //e.print(); 2285 2286 // Bugzilla 11322: 2287 if (tf.isref) 2288 e = e.toLvalue(null, null); 2289 2290 /* There's a problem if what the function returns is used subsequently as an 2291 * lvalue, as in a struct return that is then used as a 'this'. 2292 * If we take the address of the return value, we will be taking the address 2293 * of the original, not the copy. Fix this by assigning the return value to 2294 * a temporary, then returning the temporary. If the temporary is used as an 2295 * lvalue, it will work. 2296 * This only happens with struct returns. 2297 * See Bugzilla 2127 for an example. 2298 * 2299 * On constructor call making __inlineretval is merely redundant, because 2300 * the returned reference is exactly same as vthis, and the 'this' variable 2301 * already exists at the caller side. 2302 */ 2303 if (tf.next.ty == Tstruct && !fd.nrvo_var && !fd.isCtorDeclaration()) 2304 { 2305 /* Generate a new variable to hold the result and initialize it with the 2306 * inlined body of the function: 2307 * tret __inlineretval = e; 2308 */ 2309 auto ei = new ExpInitializer(callLoc, e); 2310 auto tmp = Identifier.generateId("__inlineretval"); 2311 auto vd = new VarDeclaration(callLoc, tf.next, tmp, ei); 2312 vd.storage_class = STCtemp | (tf.isref ? STCref : STCrvalue); 2313 vd.linkage = tf.linkage; 2314 vd.parent = parent; 2315 2316 ei.exp = new ConstructExp(callLoc, vd, e); 2317 ei.exp.type = vd.type; 2318 2319 auto de = new DeclarationExp(callLoc, vd); 2320 de.type = Type.tvoid; 2321 2322 // Chain the two together: 2323 // ( typeof(return) __inlineretval = ( inlined body )) , __inlineretval 2324 e = Expression.combine(de, new VarExp(callLoc, vd)); 2325 2326 //fprintf(stderr, "CallExp.inlineScan: e = "); e.print(); 2327 } 2328 2329 // Bugzilla 15210 2330 if (tf.next.ty == Tvoid && e && e.type.ty != Tvoid) 2331 { 2332 e = new CastExp(callLoc, e, Type.tvoid); 2333 e.type = Type.tvoid; 2334 } 2335 2336 eresult = Expression.combine(eresult, eret); 2337 eresult = Expression.combine(eresult, ethis); 2338 eresult = Expression.combine(eresult, eparams); 2339 eresult = Expression.combine(eresult, e); 2340 2341 static if (EXPANDINLINE_LOG) 2342 printf("\n[%s] %s expandInline eresult = %s\n", 2343 callLoc.toChars(), fd.toPrettyChars(), eresult.toChars()); 2344 } 2345 2346 // Need to reevaluate whether parent can now be inlined 2347 // in expressions, as we might have inlined statements 2348 parent.inlineStatusExp = ILSuninitialized; 2349 } 2350 2351 /*********************************************************** 2352 * Perform the "inline copying" of a default argument for a function parameter. 2353 * 2354 * Todo: 2355 * The hack for bugzilla 4820 case is still questionable. Perhaps would have to 2356 * handle a delegate expression with 'null' context properly in front-end. 2357 */ 2358 public Expression inlineCopy(Expression e, Scope* sc) 2359 { 2360 /* See Bugzilla 2935 for explanation of why just a copy() is broken 2361 */ 2362 //return e.copy(); 2363 if (e.op == TOKdelegate) 2364 { 2365 DelegateExp de = cast(DelegateExp)e; 2366 if (de.func.isNested()) 2367 { 2368 /* See Bugzilla 4820 2369 * Defer checking until later if we actually need the 'this' pointer 2370 */ 2371 return de.copy(); 2372 } 2373 } 2374 scope InlineCostVisitor icv = new InlineCostVisitor(); 2375 icv.hdrscan = 1; 2376 icv.allowAlloca = true; 2377 icv.expressionInlineCost(e); 2378 int cost = icv.cost; 2379 if (cost >= COST_MAX) 2380 { 2381 e.error("cannot inline default argument %s", e.toChars()); 2382 return new ErrorExp(); 2383 } 2384 scope ids = new InlineDoState(sc.parent, null); 2385 return doInlineAs!Expression(e, ids); 2386 } 2387 2388 /*********************************************************** 2389 * Determine if v is 'head const', meaning 2390 * that once it is initialized it is not changed 2391 * again. 2392 * 2393 * This is done using a primitive flow analysis. 2394 * 2395 * v is head const if v is const or immutable. 2396 * Otherwise, v is assumed to be head const unless one of the 2397 * following is true: 2398 * 1. v is a `ref` or `out` variable 2399 * 2. v is a parameter and fd is a variadic function 2400 * 3. v is assigned to again 2401 * 4. the address of v is taken 2402 * 5. v is referred to by a function nested within fd 2403 * 6. v is ever assigned to a `ref` or `out` variable 2404 * 7. v is ever passed to another function as `ref` or `out` 2405 * 2406 * Params: 2407 * v variable to check 2408 * fd function that v is local to 2409 * Returns: 2410 * true if v's initializer is the only value assigned to v 2411 */ 2412 bool onlyOneAssign(VarDeclaration v, FuncDeclaration fd) 2413 { 2414 if (!v.type.isMutable()) 2415 return true; // currently the only case handled atm 2416 return false; 2417 }