1 /** 2 * Perform constant folding. 3 * 4 * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved 5 * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright) 6 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 7 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/optimize.d, _optimize.d) 8 * Documentation: https://dlang.org/phobos/dmd_optimize.html 9 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/optimize.d 10 */ 11 12 module dmd.optimize; 13 14 import core.stdc.stdio; 15 16 import dmd.constfold; 17 import dmd.ctfeexpr; 18 import dmd.dclass; 19 import dmd.declaration; 20 import dmd.dsymbol; 21 import dmd.dsymbolsem; 22 import dmd.errors; 23 import dmd.expression; 24 import dmd.expressionsem; 25 import dmd.globals; 26 import dmd.init; 27 import dmd.mtype; 28 import dmd.root.ctfloat; 29 import dmd.sideeffect; 30 import dmd.tokens; 31 import dmd.visitor; 32 33 /************************************* 34 * If variable has a const initializer, 35 * return that initializer. 36 * Returns: 37 * initializer if there is one, 38 * null if not, 39 * ErrorExp if error 40 */ 41 Expression expandVar(int result, VarDeclaration v) 42 { 43 //printf("expandVar(result = %d, v = %p, %s)\n", result, v, v ? v.toChars() : "null"); 44 45 /******** 46 * Params: 47 * e = initializer expression 48 */ 49 Expression initializerReturn(Expression e) 50 { 51 if (e.type != v.type) 52 { 53 e = e.castTo(null, v.type); 54 } 55 v.inuse++; 56 e = e.optimize(result); 57 v.inuse--; 58 //if (e) printf("\te = %p, %s, e.type = %d, %s\n", e, e.toChars(), e.type.ty, e.type.toChars()); 59 return e; 60 } 61 62 static Expression nullReturn() 63 { 64 return null; 65 } 66 67 static Expression errorReturn() 68 { 69 return ErrorExp.get(); 70 } 71 72 if (!v) 73 return nullReturn(); 74 if (!v.originalType && v.semanticRun < PASS.semanticdone) // semantic() not yet run 75 v.dsymbolSemantic(null); 76 if (v.type && 77 (v.isConst() || v.isImmutable() || v.storage_class & STC.manifest)) 78 { 79 Type tb = v.type.toBasetype(); 80 if (v.storage_class & STC.manifest || 81 tb.isscalar() || 82 ((result & WANTexpand) && (tb.ty != Tsarray && tb.ty != Tstruct))) 83 { 84 if (v._init) 85 { 86 if (v.inuse) 87 { 88 if (v.storage_class & STC.manifest) 89 { 90 v.error("recursive initialization of constant"); 91 return errorReturn(); 92 } 93 return nullReturn(); 94 } 95 Expression ei = v.getConstInitializer(); 96 if (!ei) 97 { 98 if (v.storage_class & STC.manifest) 99 { 100 v.error("enum cannot be initialized with `%s`", v._init.toChars()); 101 return errorReturn(); 102 } 103 return nullReturn(); 104 } 105 if (ei.op == TOK.construct || ei.op == TOK.blit) 106 { 107 AssignExp ae = cast(AssignExp)ei; 108 ei = ae.e2; 109 if (ei.isConst() == 1) 110 { 111 } 112 else if (ei.op == TOK.string_) 113 { 114 // https://issues.dlang.org/show_bug.cgi?id=14459 115 // Do not constfold the string literal 116 // if it's typed as a C string, because the value expansion 117 // will drop the pointer identity. 118 if (!(result & WANTexpand) && ei.type.toBasetype().ty == Tpointer) 119 return nullReturn(); 120 } 121 else 122 return nullReturn(); 123 if (ei.type == v.type) 124 { 125 // const variable initialized with const expression 126 } 127 else if (ei.implicitConvTo(v.type) >= MATCH.constant) 128 { 129 // const var initialized with non-const expression 130 ei = ei.implicitCastTo(null, v.type); 131 ei = ei.expressionSemantic(null); 132 } 133 else 134 return nullReturn(); 135 } 136 else if (!(v.storage_class & STC.manifest) && 137 ei.isConst() != 1 && 138 ei.op != TOK.string_ && 139 ei.op != TOK.address) 140 { 141 return nullReturn(); 142 } 143 144 if (!ei.type) 145 { 146 return nullReturn(); 147 } 148 else 149 { 150 // Should remove the copy() operation by 151 // making all mods to expressions copy-on-write 152 return initializerReturn(ei.copy()); 153 } 154 } 155 else 156 { 157 // v does not have an initializer 158 version (all) 159 { 160 return nullReturn(); 161 } 162 else 163 { 164 // BUG: what if const is initialized in constructor? 165 auto e = v.type.defaultInit(); 166 e.loc = e1.loc; 167 return initializerReturn(e); 168 } 169 } 170 assert(0); 171 } 172 } 173 return nullReturn(); 174 } 175 176 private Expression fromConstInitializer(int result, Expression e1) 177 { 178 //printf("fromConstInitializer(result = %x, %s)\n", result, e1.toChars()); 179 //static int xx; if (xx++ == 10) assert(0); 180 Expression e = e1; 181 if (e1.op == TOK.variable) 182 { 183 VarExp ve = cast(VarExp)e1; 184 VarDeclaration v = ve.var.isVarDeclaration(); 185 e = expandVar(result, v); 186 if (e) 187 { 188 // If it is a comma expression involving a declaration, we mustn't 189 // perform a copy -- we'd get two declarations of the same variable. 190 // See bugzilla 4465. 191 if (e.op == TOK.comma && (cast(CommaExp)e).e1.op == TOK.declaration) 192 e = e1; 193 else if (e.type != e1.type && e1.type && e1.type.ty != Tident) 194 { 195 // Type 'paint' operation 196 e = e.copy(); 197 e.type = e1.type; 198 } 199 e.loc = e1.loc; 200 } 201 else 202 { 203 e = e1; 204 } 205 } 206 return e; 207 } 208 209 /* It is possible for constant folding to change an array expression of 210 * unknown length, into one where the length is known. 211 * If the expression 'arr' is a literal, set lengthVar to be its length. 212 */ 213 package void setLengthVarIfKnown(VarDeclaration lengthVar, Expression arr) 214 { 215 if (!lengthVar) 216 return; 217 if (lengthVar._init && !lengthVar._init.isVoidInitializer()) 218 return; // we have previously calculated the length 219 size_t len; 220 if (arr.op == TOK.string_) 221 len = (cast(StringExp)arr).len; 222 else if (arr.op == TOK.arrayLiteral) 223 len = (cast(ArrayLiteralExp)arr).elements.dim; 224 else 225 { 226 Type t = arr.type.toBasetype(); 227 if (t.ty == Tsarray) 228 len = cast(size_t)(cast(TypeSArray)t).dim.toInteger(); 229 else 230 return; // we don't know the length yet 231 } 232 Expression dollar = new IntegerExp(Loc.initial, len, Type.tsize_t); 233 lengthVar._init = new ExpInitializer(Loc.initial, dollar); 234 lengthVar.storage_class |= STC.static_ | STC.const_; 235 } 236 237 /* Same as above, but determines the length from 'type'. */ 238 package void setLengthVarIfKnown(VarDeclaration lengthVar, Type type) 239 { 240 if (!lengthVar) 241 return; 242 if (lengthVar._init && !lengthVar._init.isVoidInitializer()) 243 return; // we have previously calculated the length 244 size_t len; 245 Type t = type.toBasetype(); 246 if (t.ty == Tsarray) 247 len = cast(size_t)(cast(TypeSArray)t).dim.toInteger(); 248 else 249 return; // we don't know the length yet 250 Expression dollar = new IntegerExp(Loc.initial, len, Type.tsize_t); 251 lengthVar._init = new ExpInitializer(Loc.initial, dollar); 252 lengthVar.storage_class |= STC.static_ | STC.const_; 253 } 254 255 /********************************* 256 * Constant fold an Expression. 257 * Params: 258 * e = expression to const fold; this may get modified in-place 259 * result = WANTvalue, WANTexpand, or both 260 * keepLvalue = `e` is an lvalue, and keep it as an lvalue since it is 261 * an argument to a `ref` or `out` parameter, or the operand of `&` operator 262 * Returns: 263 * Constant folded version of `e` 264 */ 265 Expression Expression_optimize(Expression e, int result, bool keepLvalue) 266 { 267 extern (C++) final class OptimizeVisitor : Visitor 268 { 269 alias visit = Visitor.visit; 270 271 Expression ret; 272 private const int result; 273 private const bool keepLvalue; 274 275 extern (D) this(Expression e, int result, bool keepLvalue) 276 { 277 this.ret = e; // default result is original expression 278 this.result = result; 279 this.keepLvalue = keepLvalue; 280 } 281 282 void error() 283 { 284 ret = ErrorExp.get(); 285 } 286 287 bool expOptimize(ref Expression e, int flags, bool keepLvalue = false) 288 { 289 if (!e) 290 return false; 291 Expression ex = Expression_optimize(e, flags, keepLvalue); 292 if (ex.op == TOK.error) 293 { 294 ret = ex; // store error result 295 return true; 296 } 297 else 298 { 299 e = ex; // modify original 300 return false; 301 } 302 } 303 304 bool unaOptimize(UnaExp e, int flags) 305 { 306 return expOptimize(e.e1, flags); 307 } 308 309 bool binOptimize(BinExp e, int flags, bool keepLhsLvalue = false) 310 { 311 expOptimize(e.e1, flags, keepLhsLvalue); 312 expOptimize(e.e2, flags); 313 return ret.op == TOK.error; 314 } 315 316 override void visit(Expression e) 317 { 318 //printf("Expression::optimize(result = x%x) %s\n", result, e.toChars()); 319 } 320 321 override void visit(VarExp e) 322 { 323 if (keepLvalue) 324 { 325 VarDeclaration v = e.var.isVarDeclaration(); 326 if (v && !(v.storage_class & STC.manifest)) 327 return; 328 } 329 ret = fromConstInitializer(result, e); 330 } 331 332 override void visit(TupleExp e) 333 { 334 expOptimize(e.e0, WANTvalue); 335 for (size_t i = 0; i < e.exps.dim; i++) 336 { 337 expOptimize((*e.exps)[i], WANTvalue); 338 } 339 } 340 341 override void visit(ArrayLiteralExp e) 342 { 343 if (e.elements) 344 { 345 expOptimize(e.basis, result & WANTexpand); 346 for (size_t i = 0; i < e.elements.dim; i++) 347 { 348 expOptimize((*e.elements)[i], result & WANTexpand); 349 } 350 } 351 } 352 353 override void visit(AssocArrayLiteralExp e) 354 { 355 assert(e.keys.dim == e.values.dim); 356 for (size_t i = 0; i < e.keys.dim; i++) 357 { 358 expOptimize((*e.keys)[i], result & WANTexpand); 359 expOptimize((*e.values)[i], result & WANTexpand); 360 } 361 } 362 363 override void visit(StructLiteralExp e) 364 { 365 if (e.stageflags & stageOptimize) 366 return; 367 int old = e.stageflags; 368 e.stageflags |= stageOptimize; 369 if (e.elements) 370 { 371 for (size_t i = 0; i < e.elements.dim; i++) 372 { 373 expOptimize((*e.elements)[i], result & WANTexpand); 374 } 375 } 376 e.stageflags = old; 377 } 378 379 override void visit(UnaExp e) 380 { 381 //printf("UnaExp::optimize() %s\n", e.toChars()); 382 if (unaOptimize(e, result)) 383 return; 384 } 385 386 override void visit(NegExp e) 387 { 388 if (unaOptimize(e, result)) 389 return; 390 if (e.e1.isConst() == 1) 391 { 392 ret = Neg(e.type, e.e1).copy(); 393 } 394 } 395 396 override void visit(ComExp e) 397 { 398 if (unaOptimize(e, result)) 399 return; 400 if (e.e1.isConst() == 1) 401 { 402 ret = Com(e.type, e.e1).copy(); 403 } 404 } 405 406 override void visit(NotExp e) 407 { 408 if (unaOptimize(e, result)) 409 return; 410 if (e.e1.isConst() == 1) 411 { 412 ret = Not(e.type, e.e1).copy(); 413 } 414 } 415 416 override void visit(SymOffExp e) 417 { 418 assert(e.var); 419 } 420 421 override void visit(AddrExp e) 422 { 423 //printf("AddrExp::optimize(result = %d) %s\n", result, e.toChars()); 424 /* Rewrite &(a,b) as (a,&b) 425 */ 426 if (e.e1.op == TOK.comma) 427 { 428 CommaExp ce = cast(CommaExp)e.e1; 429 auto ae = new AddrExp(e.loc, ce.e2, e.type); 430 ret = new CommaExp(ce.loc, ce.e1, ae); 431 ret.type = e.type; 432 return; 433 } 434 // Keep lvalue-ness 435 if (expOptimize(e.e1, result, true)) 436 return; 437 // Convert &*ex to ex 438 if (e.e1.op == TOK.star) 439 { 440 Expression ex = (cast(PtrExp)e.e1).e1; 441 if (e.type.equals(ex.type)) 442 ret = ex; 443 else if (e.type.toBasetype().equivalent(ex.type.toBasetype())) 444 { 445 ret = ex.copy(); 446 ret.type = e.type; 447 } 448 return; 449 } 450 if (e.e1.op == TOK.variable) 451 { 452 VarExp ve = cast(VarExp)e.e1; 453 if (!ve.var.isOut() && !ve.var.isRef() && !ve.var.isImportedSymbol()) 454 { 455 ret = new SymOffExp(e.loc, ve.var, 0, ve.hasOverloads); 456 ret.type = e.type; 457 return; 458 } 459 } 460 if (e.e1.op == TOK.index) 461 { 462 // Convert &array[n] to &array+n 463 IndexExp ae = cast(IndexExp)e.e1; 464 if (ae.e2.op == TOK.int64 && ae.e1.op == TOK.variable) 465 { 466 sinteger_t index = ae.e2.toInteger(); 467 VarExp ve = cast(VarExp)ae.e1; 468 if (ve.type.ty == Tsarray && !ve.var.isImportedSymbol()) 469 { 470 TypeSArray ts = cast(TypeSArray)ve.type; 471 sinteger_t dim = ts.dim.toInteger(); 472 if (index < 0 || index >= dim) 473 { 474 e.error("array index %lld is out of bounds `[0..%lld]`", index, dim); 475 return error(); 476 } 477 478 import core.checkedint : mulu; 479 bool overflow; 480 const offset = mulu(index, ts.nextOf().size(e.loc), overflow); 481 if (overflow) 482 { 483 e.error("array offset overflow"); 484 return error(); 485 } 486 487 ret = new SymOffExp(e.loc, ve.var, offset); 488 ret.type = e.type; 489 return; 490 } 491 } 492 } 493 } 494 495 override void visit(PtrExp e) 496 { 497 //printf("PtrExp::optimize(result = x%x) %s\n", result, e.toChars()); 498 if (expOptimize(e.e1, result)) 499 return; 500 // Convert *&ex to ex 501 // But only if there is no type punning involved 502 if (e.e1.op == TOK.address) 503 { 504 Expression ex = (cast(AddrExp)e.e1).e1; 505 if (e.type.equals(ex.type)) 506 ret = ex; 507 else if (e.type.toBasetype().equivalent(ex.type.toBasetype())) 508 { 509 ret = ex.copy(); 510 ret.type = e.type; 511 } 512 } 513 if (keepLvalue) 514 return; 515 // Constant fold *(&structliteral + offset) 516 if (e.e1.op == TOK.add) 517 { 518 Expression ex = Ptr(e.type, e.e1).copy(); 519 if (!CTFEExp.isCantExp(ex)) 520 { 521 ret = ex; 522 return; 523 } 524 } 525 if (e.e1.op == TOK.symbolOffset) 526 { 527 SymOffExp se = cast(SymOffExp)e.e1; 528 VarDeclaration v = se.var.isVarDeclaration(); 529 Expression ex = expandVar(result, v); 530 if (ex && ex.op == TOK.structLiteral) 531 { 532 StructLiteralExp sle = cast(StructLiteralExp)ex; 533 ex = sle.getField(e.type, cast(uint)se.offset); 534 if (ex && !CTFEExp.isCantExp(ex)) 535 { 536 ret = ex; 537 return; 538 } 539 } 540 } 541 } 542 543 override void visit(DotVarExp e) 544 { 545 //printf("DotVarExp::optimize(result = x%x) %s\n", result, e.toChars()); 546 if (expOptimize(e.e1, result)) 547 return; 548 if (keepLvalue) 549 return; 550 Expression ex = e.e1; 551 if (ex.op == TOK.variable) 552 { 553 VarExp ve = cast(VarExp)ex; 554 VarDeclaration v = ve.var.isVarDeclaration(); 555 ex = expandVar(result, v); 556 } 557 if (ex && ex.op == TOK.structLiteral) 558 { 559 StructLiteralExp sle = cast(StructLiteralExp)ex; 560 VarDeclaration vf = e.var.isVarDeclaration(); 561 if (vf && !vf.overlapped) 562 { 563 /* https://issues.dlang.org/show_bug.cgi?id=13021 564 * Prevent optimization if vf has overlapped fields. 565 */ 566 ex = sle.getField(e.type, vf.offset); 567 if (ex && !CTFEExp.isCantExp(ex)) 568 { 569 ret = ex; 570 return; 571 } 572 } 573 } 574 } 575 576 override void visit(NewExp e) 577 { 578 expOptimize(e.thisexp, WANTvalue); 579 // Optimize parameters 580 if (e.newargs) 581 { 582 for (size_t i = 0; i < e.newargs.dim; i++) 583 { 584 expOptimize((*e.newargs)[i], WANTvalue); 585 } 586 } 587 if (e.arguments) 588 { 589 for (size_t i = 0; i < e.arguments.dim; i++) 590 { 591 expOptimize((*e.arguments)[i], WANTvalue); 592 } 593 } 594 } 595 596 override void visit(CallExp e) 597 { 598 //printf("CallExp::optimize(result = %d) %s\n", result, e.toChars()); 599 // Optimize parameters with keeping lvalue-ness 600 if (expOptimize(e.e1, result)) 601 return; 602 if (e.arguments) 603 { 604 Type t1 = e.e1.type.toBasetype(); 605 if (t1.ty == Tdelegate) 606 t1 = t1.nextOf(); 607 assert(t1.ty == Tfunction); 608 TypeFunction tf = cast(TypeFunction)t1; 609 for (size_t i = 0; i < e.arguments.dim; i++) 610 { 611 Parameter p = tf.parameterList[i]; 612 bool keep = p && p.isReference(); 613 expOptimize((*e.arguments)[i], WANTvalue, keep); 614 } 615 } 616 } 617 618 override void visit(CastExp e) 619 { 620 //printf("CastExp::optimize(result = %d) %s\n", result, e.toChars()); 621 //printf("from %s to %s\n", e.type.toChars(), e.to.toChars()); 622 //printf("from %s\n", e.type.toChars()); 623 //printf("e1.type %s\n", e.e1.type.toChars()); 624 //printf("type = %p\n", e.type); 625 assert(e.type); 626 TOK op1 = e.e1.op; 627 Expression e1old = e.e1; 628 if (expOptimize(e.e1, result, keepLvalue)) 629 return; 630 if (!keepLvalue) 631 e.e1 = fromConstInitializer(result, e.e1); 632 if (e.e1 == e1old && e.e1.op == TOK.arrayLiteral && e.type.toBasetype().ty == Tpointer && e.e1.type.toBasetype().ty != Tsarray) 633 { 634 // Casting this will result in the same expression, and 635 // infinite loop because of Expression::implicitCastTo() 636 return; // no change 637 } 638 if ((e.e1.op == TOK.string_ || e.e1.op == TOK.arrayLiteral) && 639 (e.type.ty == Tpointer || e.type.ty == Tarray)) 640 { 641 const esz = e.type.nextOf().size(e.loc); 642 const e1sz = e.e1.type.toBasetype().nextOf().size(e.e1.loc); 643 if (esz == SIZE_INVALID || e1sz == SIZE_INVALID) 644 return error(); 645 646 if (e1sz == esz) 647 { 648 // https://issues.dlang.org/show_bug.cgi?id=12937 649 // If target type is void array, trying to paint 650 // e.e1 with that type will cause infinite recursive optimization. 651 if (e.type.nextOf().ty == Tvoid) 652 return; 653 ret = e.e1.castTo(null, e.type); 654 //printf(" returning1 %s\n", ret.toChars()); 655 return; 656 } 657 } 658 659 if (e.e1.op == TOK.structLiteral && e.e1.type.implicitConvTo(e.type) >= MATCH.constant) 660 { 661 //printf(" returning2 %s\n", e.e1.toChars()); 662 L1: 663 // Returning e1 with changing its type 664 ret = (e1old == e.e1 ? e.e1.copy() : e.e1); 665 ret.type = e.type; 666 return; 667 } 668 /* The first test here is to prevent infinite loops 669 */ 670 if (op1 != TOK.arrayLiteral && e.e1.op == TOK.arrayLiteral) 671 { 672 ret = e.e1.castTo(null, e.to); 673 return; 674 } 675 if (e.e1.op == TOK.null_ && (e.type.ty == Tpointer || e.type.ty == Tclass || e.type.ty == Tarray)) 676 { 677 //printf(" returning3 %s\n", e.e1.toChars()); 678 goto L1; 679 } 680 if (e.type.ty == Tclass && e.e1.type.ty == Tclass) 681 { 682 import dmd.aggregate : Sizeok; 683 684 // See if we can remove an unnecessary cast 685 ClassDeclaration cdfrom = e.e1.type.isClassHandle(); 686 ClassDeclaration cdto = e.type.isClassHandle(); 687 if (cdto == ClassDeclaration.object && !cdfrom.isInterfaceDeclaration()) 688 goto L1; // can always convert a class to Object 689 // Need to determine correct offset before optimizing away the cast. 690 // https://issues.dlang.org/show_bug.cgi?id=16980 691 cdfrom.size(e.loc); 692 assert(cdfrom.sizeok == Sizeok.done); 693 assert(cdto.sizeok == Sizeok.done || !cdto.isBaseOf(cdfrom, null)); 694 int offset; 695 if (cdto.isBaseOf(cdfrom, &offset) && offset == 0) 696 { 697 //printf(" returning4 %s\n", e.e1.toChars()); 698 goto L1; 699 } 700 } 701 if (e.e1.type.mutableOf().unSharedOf().equals(e.to.mutableOf().unSharedOf())) 702 { 703 //printf(" returning5 %s\n", e.e1.toChars()); 704 goto L1; 705 } 706 if (e.e1.isConst()) 707 { 708 if (e.e1.op == TOK.symbolOffset) 709 { 710 if (e.type.toBasetype().ty != Tsarray) 711 { 712 const esz = e.type.size(e.loc); 713 const e1sz = e.e1.type.size(e.e1.loc); 714 if (esz == SIZE_INVALID || 715 e1sz == SIZE_INVALID) 716 return error(); 717 718 if (esz == e1sz) 719 goto L1; 720 } 721 return; 722 } 723 if (e.to.toBasetype().ty != Tvoid) 724 { 725 if (e.e1.type.equals(e.type) && e.type.equals(e.to)) 726 ret = e.e1; 727 else 728 ret = Cast(e.loc, e.type, e.to, e.e1).copy(); 729 } 730 } 731 //printf(" returning6 %s\n", ret.toChars()); 732 } 733 734 override void visit(BinAssignExp e) 735 { 736 //printf("BinAssignExp::optimize(result = %d) %s\n", result, e.toChars()); 737 if (binOptimize(e, result, /*keepLhsLvalue*/ true)) 738 return; 739 if (e.op == TOK.leftShiftAssign || e.op == TOK.rightShiftAssign || e.op == TOK.unsignedRightShiftAssign) 740 { 741 if (e.e2.isConst() == 1) 742 { 743 sinteger_t i2 = e.e2.toInteger(); 744 d_uns64 sz = e.e1.type.size(e.e1.loc); 745 assert(sz != SIZE_INVALID); 746 sz *= 8; 747 if (i2 < 0 || i2 >= sz) 748 { 749 e.error("shift assign by %lld is outside the range `0..%llu`", i2, cast(ulong)sz - 1); 750 return error(); 751 } 752 } 753 } 754 } 755 756 override void visit(BinExp e) 757 { 758 //printf("BinExp::optimize(result = %d) %s\n", result, e.toChars()); 759 const keepLhsLvalue = e.op == TOK.construct || e.op == TOK.blit || e.op == TOK.assign 760 || e.op == TOK.plusPlus || e.op == TOK.minusMinus 761 || e.op == TOK.prePlusPlus || e.op == TOK.preMinusMinus; 762 binOptimize(e, result, keepLhsLvalue); 763 } 764 765 override void visit(AddExp e) 766 { 767 //printf("AddExp::optimize(%s)\n", e.toChars()); 768 if (binOptimize(e, result)) 769 return; 770 if (e.e1.isConst() && e.e2.isConst()) 771 { 772 if (e.e1.op == TOK.symbolOffset && e.e2.op == TOK.symbolOffset) 773 return; 774 ret = Add(e.loc, e.type, e.e1, e.e2).copy(); 775 } 776 } 777 778 override void visit(MinExp e) 779 { 780 if (binOptimize(e, result)) 781 return; 782 if (e.e1.isConst() && e.e2.isConst()) 783 { 784 if (e.e2.op == TOK.symbolOffset) 785 return; 786 ret = Min(e.loc, e.type, e.e1, e.e2).copy(); 787 } 788 } 789 790 override void visit(MulExp e) 791 { 792 //printf("MulExp::optimize(result = %d) %s\n", result, e.toChars()); 793 if (binOptimize(e, result)) 794 return; 795 if (e.e1.isConst() == 1 && e.e2.isConst() == 1) 796 { 797 ret = Mul(e.loc, e.type, e.e1, e.e2).copy(); 798 } 799 } 800 801 override void visit(DivExp e) 802 { 803 //printf("DivExp::optimize(%s)\n", e.toChars()); 804 if (binOptimize(e, result)) 805 return; 806 if (e.e1.isConst() == 1 && e.e2.isConst() == 1) 807 { 808 ret = Div(e.loc, e.type, e.e1, e.e2).copy(); 809 } 810 } 811 812 override void visit(ModExp e) 813 { 814 if (binOptimize(e, result)) 815 return; 816 if (e.e1.isConst() == 1 && e.e2.isConst() == 1) 817 { 818 ret = Mod(e.loc, e.type, e.e1, e.e2).copy(); 819 } 820 } 821 822 extern (D) void shift_optimize(BinExp e, UnionExp function(const ref Loc, Type, Expression, Expression) shift) 823 { 824 if (binOptimize(e, result)) 825 return; 826 if (e.e2.isConst() == 1) 827 { 828 sinteger_t i2 = e.e2.toInteger(); 829 d_uns64 sz = e.e1.type.size(e.e1.loc); 830 assert(sz != SIZE_INVALID); 831 sz *= 8; 832 if (i2 < 0 || i2 >= sz) 833 { 834 e.error("shift by %lld is outside the range `0..%llu`", i2, cast(ulong)sz - 1); 835 return error(); 836 } 837 if (e.e1.isConst() == 1) 838 ret = (*shift)(e.loc, e.type, e.e1, e.e2).copy(); 839 } 840 } 841 842 override void visit(ShlExp e) 843 { 844 //printf("ShlExp::optimize(result = %d) %s\n", result, e.toChars()); 845 shift_optimize(e, &Shl); 846 } 847 848 override void visit(ShrExp e) 849 { 850 //printf("ShrExp::optimize(result = %d) %s\n", result, e.toChars()); 851 shift_optimize(e, &Shr); 852 } 853 854 override void visit(UshrExp e) 855 { 856 //printf("UshrExp::optimize(result = %d) %s\n", result, toChars()); 857 shift_optimize(e, &Ushr); 858 } 859 860 override void visit(AndExp e) 861 { 862 if (binOptimize(e, result)) 863 return; 864 if (e.e1.isConst() == 1 && e.e2.isConst() == 1) 865 ret = And(e.loc, e.type, e.e1, e.e2).copy(); 866 } 867 868 override void visit(OrExp e) 869 { 870 if (binOptimize(e, result)) 871 return; 872 if (e.e1.isConst() == 1 && e.e2.isConst() == 1) 873 ret = Or(e.loc, e.type, e.e1, e.e2).copy(); 874 } 875 876 override void visit(XorExp e) 877 { 878 if (binOptimize(e, result)) 879 return; 880 if (e.e1.isConst() == 1 && e.e2.isConst() == 1) 881 ret = Xor(e.loc, e.type, e.e1, e.e2).copy(); 882 } 883 884 override void visit(PowExp e) 885 { 886 if (binOptimize(e, result)) 887 return; 888 // All negative integral powers are illegal. 889 if (e.e1.type.isintegral() && (e.e2.op == TOK.int64) && cast(sinteger_t)e.e2.toInteger() < 0) 890 { 891 e.error("cannot raise `%s` to a negative integer power. Did you mean `(cast(real)%s)^^%s` ?", e.e1.type.toBasetype().toChars(), e.e1.toChars(), e.e2.toChars()); 892 return error(); 893 } 894 // If e2 *could* have been an integer, make it one. 895 if (e.e2.op == TOK.float64 && e.e2.toReal() == real_t(cast(sinteger_t)e.e2.toReal())) 896 { 897 // This only applies to floating point, or positive integral powers. 898 if (e.e1.type.isfloating() || cast(sinteger_t)e.e2.toInteger() >= 0) 899 e.e2 = new IntegerExp(e.loc, e.e2.toInteger(), Type.tint64); 900 } 901 if (e.e1.isConst() == 1 && e.e2.isConst() == 1) 902 { 903 Expression ex = Pow(e.loc, e.type, e.e1, e.e2).copy(); 904 if (!CTFEExp.isCantExp(ex)) 905 { 906 ret = ex; 907 return; 908 } 909 } 910 } 911 912 override void visit(CommaExp e) 913 { 914 //printf("CommaExp::optimize(result = %d) %s\n", result, e.toChars()); 915 // Comma needs special treatment, because it may 916 // contain compiler-generated declarations. We can interpret them, but 917 // otherwise we must NOT attempt to constant-fold them. 918 // In particular, if the comma returns a temporary variable, it needs 919 // to be an lvalue (this is particularly important for struct constructors) 920 expOptimize(e.e1, WANTvalue); 921 expOptimize(e.e2, result, keepLvalue); 922 if (ret.op == TOK.error) 923 return; 924 if (!e.e1 || e.e1.op == TOK.int64 || e.e1.op == TOK.float64 || !hasSideEffect(e.e1)) 925 { 926 ret = e.e2; 927 if (ret) 928 ret.type = e.type; 929 } 930 //printf("-CommaExp::optimize(result = %d) %s\n", result, e.e.toChars()); 931 } 932 933 override void visit(ArrayLengthExp e) 934 { 935 //printf("ArrayLengthExp::optimize(result = %d) %s\n", result, e.toChars()); 936 if (unaOptimize(e, WANTexpand)) 937 return; 938 // CTFE interpret static immutable arrays (to get better diagnostics) 939 if (e.e1.op == TOK.variable) 940 { 941 VarDeclaration v = (cast(VarExp)e.e1).var.isVarDeclaration(); 942 if (v && (v.storage_class & STC.static_) && (v.storage_class & STC.immutable_) && v._init) 943 { 944 if (Expression ci = v.getConstInitializer()) 945 e.e1 = ci; 946 } 947 } 948 if (e.e1.op == TOK.string_ || e.e1.op == TOK.arrayLiteral || e.e1.op == TOK.assocArrayLiteral || e.e1.type.toBasetype().ty == Tsarray) 949 { 950 ret = ArrayLength(e.type, e.e1).copy(); 951 } 952 } 953 954 override void visit(EqualExp e) 955 { 956 //printf("EqualExp::optimize(result = %x) %s\n", result, e.toChars()); 957 if (binOptimize(e, WANTvalue)) 958 return; 959 Expression e1 = fromConstInitializer(result, e.e1); 960 Expression e2 = fromConstInitializer(result, e.e2); 961 if (e1.op == TOK.error) 962 { 963 ret = e1; 964 return; 965 } 966 if (e2.op == TOK.error) 967 { 968 ret = e2; 969 return; 970 } 971 ret = Equal(e.op, e.loc, e.type, e1, e2).copy(); 972 if (CTFEExp.isCantExp(ret)) 973 ret = e; 974 } 975 976 override void visit(IdentityExp e) 977 { 978 //printf("IdentityExp::optimize(result = %d) %s\n", result, e.toChars()); 979 if (binOptimize(e, WANTvalue)) 980 return; 981 if ((e.e1.isConst() && e.e2.isConst()) || (e.e1.op == TOK.null_ && e.e2.op == TOK.null_)) 982 { 983 ret = Identity(e.op, e.loc, e.type, e.e1, e.e2).copy(); 984 if (CTFEExp.isCantExp(ret)) 985 ret = e; 986 } 987 } 988 989 override void visit(IndexExp e) 990 { 991 //printf("IndexExp::optimize(result = %d) %s\n", result, e.toChars()); 992 if (expOptimize(e.e1, result & WANTexpand)) 993 return; 994 Expression ex = fromConstInitializer(result, e.e1); 995 // We might know $ now 996 setLengthVarIfKnown(e.lengthVar, ex); 997 if (expOptimize(e.e2, WANTvalue)) 998 return; 999 // Don't optimize to an array literal element directly in case an lvalue is requested 1000 if (keepLvalue && ex.op == TOK.arrayLiteral) 1001 return; 1002 ret = Index(e.type, ex, e.e2).copy(); 1003 if (CTFEExp.isCantExp(ret) || (keepLvalue && !ret.isLvalue())) 1004 ret = e; 1005 } 1006 1007 override void visit(SliceExp e) 1008 { 1009 //printf("SliceExp::optimize(result = %d) %s\n", result, e.toChars()); 1010 if (expOptimize(e.e1, result & WANTexpand)) 1011 return; 1012 if (!e.lwr) 1013 { 1014 if (e.e1.op == TOK.string_) 1015 { 1016 // Convert slice of string literal into dynamic array 1017 Type t = e.e1.type.toBasetype(); 1018 if (Type tn = t.nextOf()) 1019 ret = e.e1.castTo(null, tn.arrayOf()); 1020 } 1021 } 1022 else 1023 { 1024 e.e1 = fromConstInitializer(result, e.e1); 1025 // We might know $ now 1026 setLengthVarIfKnown(e.lengthVar, e.e1); 1027 expOptimize(e.lwr, WANTvalue); 1028 expOptimize(e.upr, WANTvalue); 1029 if (ret.op == TOK.error) 1030 return; 1031 ret = Slice(e.type, e.e1, e.lwr, e.upr).copy(); 1032 if (CTFEExp.isCantExp(ret)) 1033 ret = e; 1034 } 1035 // https://issues.dlang.org/show_bug.cgi?id=14649 1036 // Leave the slice form so it might be 1037 // a part of array operation. 1038 // Assume that the backend codegen will handle the form `e[]` 1039 // as an equal to `e` itself. 1040 if (ret.op == TOK.string_) 1041 { 1042 e.e1 = ret; 1043 e.lwr = null; 1044 e.upr = null; 1045 ret = e; 1046 } 1047 //printf("-SliceExp::optimize() %s\n", ret.toChars()); 1048 } 1049 1050 override void visit(LogicalExp e) 1051 { 1052 //printf("LogicalExp::optimize(%d) %s\n", result, e.toChars()); 1053 if (expOptimize(e.e1, WANTvalue)) 1054 return; 1055 const oror = e.op == TOK.orOr; 1056 if (e.e1.isBool(oror)) 1057 { 1058 // Replace with (e1, oror) 1059 ret = IntegerExp.createBool(oror); 1060 ret = Expression.combine(e.e1, ret); 1061 if (e.type.toBasetype().ty == Tvoid) 1062 { 1063 ret = new CastExp(e.loc, ret, Type.tvoid); 1064 ret.type = e.type; 1065 } 1066 ret = Expression_optimize(ret, result, false); 1067 return; 1068 } 1069 if (expOptimize(e.e2, WANTvalue)) 1070 return; 1071 if (e.e1.isConst()) 1072 { 1073 if (e.e2.isConst()) 1074 { 1075 bool n1 = e.e1.isBool(true); 1076 bool n2 = e.e2.isBool(true); 1077 ret = new IntegerExp(e.loc, oror ? (n1 || n2) : (n1 && n2), e.type); 1078 } 1079 else if (e.e1.isBool(!oror)) 1080 { 1081 if (e.type.toBasetype().ty == Tvoid) 1082 ret = e.e2; 1083 else 1084 { 1085 ret = new CastExp(e.loc, e.e2, e.type); 1086 ret.type = e.type; 1087 } 1088 } 1089 } 1090 } 1091 1092 override void visit(CmpExp e) 1093 { 1094 //printf("CmpExp::optimize() %s\n", e.toChars()); 1095 if (binOptimize(e, WANTvalue)) 1096 return; 1097 Expression e1 = fromConstInitializer(result, e.e1); 1098 Expression e2 = fromConstInitializer(result, e.e2); 1099 ret = Cmp(e.op, e.loc, e.type, e1, e2).copy(); 1100 if (CTFEExp.isCantExp(ret)) 1101 ret = e; 1102 } 1103 1104 override void visit(CatExp e) 1105 { 1106 //printf("CatExp::optimize(%d) %s\n", result, e.toChars()); 1107 if (binOptimize(e, result)) 1108 return; 1109 if (e.e1.op == TOK.concatenate) 1110 { 1111 // https://issues.dlang.org/show_bug.cgi?id=12798 1112 // optimize ((expr ~ str1) ~ str2) 1113 CatExp ce1 = cast(CatExp)e.e1; 1114 scope CatExp cex = new CatExp(e.loc, ce1.e2, e.e2); 1115 cex.type = e.type; 1116 Expression ex = Expression_optimize(cex, result, false); 1117 if (ex != cex) 1118 { 1119 e.e1 = ce1.e1; 1120 e.e2 = ex; 1121 } 1122 } 1123 // optimize "str"[] -> "str" 1124 if (e.e1.op == TOK.slice) 1125 { 1126 SliceExp se1 = cast(SliceExp)e.e1; 1127 if (se1.e1.op == TOK.string_ && !se1.lwr) 1128 e.e1 = se1.e1; 1129 } 1130 if (e.e2.op == TOK.slice) 1131 { 1132 SliceExp se2 = cast(SliceExp)e.e2; 1133 if (se2.e1.op == TOK.string_ && !se2.lwr) 1134 e.e2 = se2.e1; 1135 } 1136 ret = Cat(e.type, e.e1, e.e2).copy(); 1137 if (CTFEExp.isCantExp(ret)) 1138 ret = e; 1139 } 1140 1141 override void visit(CondExp e) 1142 { 1143 if (expOptimize(e.econd, WANTvalue)) 1144 return; 1145 if (e.econd.isBool(true)) 1146 ret = Expression_optimize(e.e1, result, keepLvalue); 1147 else if (e.econd.isBool(false)) 1148 ret = Expression_optimize(e.e2, result, keepLvalue); 1149 else 1150 { 1151 expOptimize(e.e1, result, keepLvalue); 1152 expOptimize(e.e2, result, keepLvalue); 1153 } 1154 } 1155 } 1156 1157 scope OptimizeVisitor v = new OptimizeVisitor(e, result, keepLvalue); 1158 1159 // Optimize the expression until it can no longer be simplified. 1160 size_t b; 1161 while (1) 1162 { 1163 if (b++ == global.recursionLimit) 1164 { 1165 e.error("infinite loop while optimizing expression"); 1166 fatal(); 1167 } 1168 auto ex = v.ret; 1169 ex.accept(v); 1170 if (ex == v.ret) 1171 break; 1172 } 1173 return v.ret; 1174 }