1 /** 2 * Perform constant folding. 3 * 4 * Copyright: Copyright (C) 1999-2020 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 new ErrorExp(); 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 = new ErrorExp(); 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) 310 { 311 expOptimize(e.e1, flags); 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.storageClass & (STC.ref_ | STC.out_)) != 0; 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)) 629 return; 630 e.e1 = fromConstInitializer(result, e.e1); 631 if (e.e1 == e1old && e.e1.op == TOK.arrayLiteral && e.type.toBasetype().ty == Tpointer && e.e1.type.toBasetype().ty != Tsarray) 632 { 633 // Casting this will result in the same expression, and 634 // infinite loop because of Expression::implicitCastTo() 635 return; // no change 636 } 637 if ((e.e1.op == TOK.string_ || e.e1.op == TOK.arrayLiteral) && 638 (e.type.ty == Tpointer || e.type.ty == Tarray)) 639 { 640 const esz = e.type.nextOf().size(e.loc); 641 const e1sz = e.e1.type.toBasetype().nextOf().size(e.e1.loc); 642 if (esz == SIZE_INVALID || e1sz == SIZE_INVALID) 643 return error(); 644 645 if (e1sz == esz) 646 { 647 // https://issues.dlang.org/show_bug.cgi?id=12937 648 // If target type is void array, trying to paint 649 // e.e1 with that type will cause infinite recursive optimization. 650 if (e.type.nextOf().ty == Tvoid) 651 return; 652 ret = e.e1.castTo(null, e.type); 653 //printf(" returning1 %s\n", ret.toChars()); 654 return; 655 } 656 } 657 658 if (e.e1.op == TOK.structLiteral && e.e1.type.implicitConvTo(e.type) >= MATCH.constant) 659 { 660 //printf(" returning2 %s\n", e.e1.toChars()); 661 L1: 662 // Returning e1 with changing its type 663 ret = (e1old == e.e1 ? e.e1.copy() : e.e1); 664 ret.type = e.type; 665 return; 666 } 667 /* The first test here is to prevent infinite loops 668 */ 669 if (op1 != TOK.arrayLiteral && e.e1.op == TOK.arrayLiteral) 670 { 671 ret = e.e1.castTo(null, e.to); 672 return; 673 } 674 if (e.e1.op == TOK.null_ && (e.type.ty == Tpointer || e.type.ty == Tclass || e.type.ty == Tarray)) 675 { 676 //printf(" returning3 %s\n", e.e1.toChars()); 677 goto L1; 678 } 679 if (e.type.ty == Tclass && e.e1.type.ty == Tclass) 680 { 681 import dmd.aggregate : Sizeok; 682 683 // See if we can remove an unnecessary cast 684 ClassDeclaration cdfrom = e.e1.type.isClassHandle(); 685 ClassDeclaration cdto = e.type.isClassHandle(); 686 if (cdto == ClassDeclaration.object && !cdfrom.isInterfaceDeclaration()) 687 goto L1; // can always convert a class to Object 688 // Need to determine correct offset before optimizing away the cast. 689 // https://issues.dlang.org/show_bug.cgi?id=16980 690 cdfrom.size(e.loc); 691 assert(cdfrom.sizeok == Sizeok.done); 692 assert(cdto.sizeok == Sizeok.done || !cdto.isBaseOf(cdfrom, null)); 693 int offset; 694 if (cdto.isBaseOf(cdfrom, &offset) && offset == 0) 695 { 696 //printf(" returning4 %s\n", e.e1.toChars()); 697 goto L1; 698 } 699 } 700 // We can convert 'head const' to mutable 701 if (e.to.mutableOf().constOf().equals(e.e1.type.mutableOf().constOf())) 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(BinExp e) 735 { 736 //printf("BinExp::optimize(result = %d) %s\n", result, e.toChars()); 737 // don't replace const variable with its initializer in e1 738 bool e2only = (e.op == TOK.construct || e.op == TOK.blit); 739 if (e2only ? expOptimize(e.e2, result) : binOptimize(e, result)) 740 return; 741 if (e.op == TOK.leftShiftAssign || e.op == TOK.rightShiftAssign || e.op == TOK.unsignedRightShiftAssign) 742 { 743 if (e.e2.isConst() == 1) 744 { 745 sinteger_t i2 = e.e2.toInteger(); 746 d_uns64 sz = e.e1.type.size(e.e1.loc); 747 assert(sz != SIZE_INVALID); 748 sz *= 8; 749 if (i2 < 0 || i2 >= sz) 750 { 751 e.error("shift assign by %lld is outside the range `0..%llu`", i2, cast(ulong)sz - 1); 752 return error(); 753 } 754 } 755 } 756 } 757 758 override void visit(AddExp e) 759 { 760 //printf("AddExp::optimize(%s)\n", e.toChars()); 761 if (binOptimize(e, result)) 762 return; 763 if (e.e1.isConst() && e.e2.isConst()) 764 { 765 if (e.e1.op == TOK.symbolOffset && e.e2.op == TOK.symbolOffset) 766 return; 767 ret = Add(e.loc, e.type, e.e1, e.e2).copy(); 768 } 769 } 770 771 override void visit(MinExp e) 772 { 773 if (binOptimize(e, result)) 774 return; 775 if (e.e1.isConst() && e.e2.isConst()) 776 { 777 if (e.e2.op == TOK.symbolOffset) 778 return; 779 ret = Min(e.loc, e.type, e.e1, e.e2).copy(); 780 } 781 } 782 783 override void visit(MulExp e) 784 { 785 //printf("MulExp::optimize(result = %d) %s\n", result, e.toChars()); 786 if (binOptimize(e, result)) 787 return; 788 if (e.e1.isConst() == 1 && e.e2.isConst() == 1) 789 { 790 ret = Mul(e.loc, e.type, e.e1, e.e2).copy(); 791 } 792 } 793 794 override void visit(DivExp e) 795 { 796 //printf("DivExp::optimize(%s)\n", e.toChars()); 797 if (binOptimize(e, result)) 798 return; 799 if (e.e1.isConst() == 1 && e.e2.isConst() == 1) 800 { 801 ret = Div(e.loc, e.type, e.e1, e.e2).copy(); 802 } 803 } 804 805 override void visit(ModExp e) 806 { 807 if (binOptimize(e, result)) 808 return; 809 if (e.e1.isConst() == 1 && e.e2.isConst() == 1) 810 { 811 ret = Mod(e.loc, e.type, e.e1, e.e2).copy(); 812 } 813 } 814 815 extern (D) void shift_optimize(BinExp e, UnionExp function(const ref Loc, Type, Expression, Expression) shift) 816 { 817 if (binOptimize(e, result)) 818 return; 819 if (e.e2.isConst() == 1) 820 { 821 sinteger_t i2 = e.e2.toInteger(); 822 d_uns64 sz = e.e1.type.size(e.e1.loc); 823 assert(sz != SIZE_INVALID); 824 sz *= 8; 825 if (i2 < 0 || i2 >= sz) 826 { 827 e.error("shift by %lld is outside the range `0..%llu`", i2, cast(ulong)sz - 1); 828 return error(); 829 } 830 if (e.e1.isConst() == 1) 831 ret = (*shift)(e.loc, e.type, e.e1, e.e2).copy(); 832 } 833 } 834 835 override void visit(ShlExp e) 836 { 837 //printf("ShlExp::optimize(result = %d) %s\n", result, e.toChars()); 838 shift_optimize(e, &Shl); 839 } 840 841 override void visit(ShrExp e) 842 { 843 //printf("ShrExp::optimize(result = %d) %s\n", result, e.toChars()); 844 shift_optimize(e, &Shr); 845 } 846 847 override void visit(UshrExp e) 848 { 849 //printf("UshrExp::optimize(result = %d) %s\n", result, toChars()); 850 shift_optimize(e, &Ushr); 851 } 852 853 override void visit(AndExp e) 854 { 855 if (binOptimize(e, result)) 856 return; 857 if (e.e1.isConst() == 1 && e.e2.isConst() == 1) 858 ret = And(e.loc, e.type, e.e1, e.e2).copy(); 859 } 860 861 override void visit(OrExp e) 862 { 863 if (binOptimize(e, result)) 864 return; 865 if (e.e1.isConst() == 1 && e.e2.isConst() == 1) 866 ret = Or(e.loc, e.type, e.e1, e.e2).copy(); 867 } 868 869 override void visit(XorExp e) 870 { 871 if (binOptimize(e, result)) 872 return; 873 if (e.e1.isConst() == 1 && e.e2.isConst() == 1) 874 ret = Xor(e.loc, e.type, e.e1, e.e2).copy(); 875 } 876 877 override void visit(PowExp e) 878 { 879 if (binOptimize(e, result)) 880 return; 881 // All negative integral powers are illegal. 882 if (e.e1.type.isintegral() && (e.e2.op == TOK.int64) && cast(sinteger_t)e.e2.toInteger() < 0) 883 { 884 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()); 885 return error(); 886 } 887 // If e2 *could* have been an integer, make it one. 888 if (e.e2.op == TOK.float64 && e.e2.toReal() == real_t(cast(sinteger_t)e.e2.toReal())) 889 { 890 // This only applies to floating point, or positive integral powers. 891 if (e.e1.type.isfloating() || cast(sinteger_t)e.e2.toInteger() >= 0) 892 e.e2 = new IntegerExp(e.loc, e.e2.toInteger(), Type.tint64); 893 } 894 if (e.e1.isConst() == 1 && e.e2.isConst() == 1) 895 { 896 Expression ex = Pow(e.loc, e.type, e.e1, e.e2).copy(); 897 if (!CTFEExp.isCantExp(ex)) 898 { 899 ret = ex; 900 return; 901 } 902 } 903 } 904 905 override void visit(CommaExp e) 906 { 907 //printf("CommaExp::optimize(result = %d) %s\n", result, e.toChars()); 908 // Comma needs special treatment, because it may 909 // contain compiler-generated declarations. We can interpret them, but 910 // otherwise we must NOT attempt to constant-fold them. 911 // In particular, if the comma returns a temporary variable, it needs 912 // to be an lvalue (this is particularly important for struct constructors) 913 expOptimize(e.e1, WANTvalue); 914 expOptimize(e.e2, result, keepLvalue); 915 if (ret.op == TOK.error) 916 return; 917 if (!e.e1 || e.e1.op == TOK.int64 || e.e1.op == TOK.float64 || !hasSideEffect(e.e1)) 918 { 919 ret = e.e2; 920 if (ret) 921 ret.type = e.type; 922 } 923 //printf("-CommaExp::optimize(result = %d) %s\n", result, e.e.toChars()); 924 } 925 926 override void visit(ArrayLengthExp e) 927 { 928 //printf("ArrayLengthExp::optimize(result = %d) %s\n", result, e.toChars()); 929 if (unaOptimize(e, WANTexpand)) 930 return; 931 // CTFE interpret static immutable arrays (to get better diagnostics) 932 if (e.e1.op == TOK.variable) 933 { 934 VarDeclaration v = (cast(VarExp)e.e1).var.isVarDeclaration(); 935 if (v && (v.storage_class & STC.static_) && (v.storage_class & STC.immutable_) && v._init) 936 { 937 if (Expression ci = v.getConstInitializer()) 938 e.e1 = ci; 939 } 940 } 941 if (e.e1.op == TOK.string_ || e.e1.op == TOK.arrayLiteral || e.e1.op == TOK.assocArrayLiteral || e.e1.type.toBasetype().ty == Tsarray) 942 { 943 ret = ArrayLength(e.type, e.e1).copy(); 944 } 945 } 946 947 override void visit(EqualExp e) 948 { 949 //printf("EqualExp::optimize(result = %x) %s\n", result, e.toChars()); 950 if (binOptimize(e, WANTvalue)) 951 return; 952 Expression e1 = fromConstInitializer(result, e.e1); 953 Expression e2 = fromConstInitializer(result, e.e2); 954 if (e1.op == TOK.error) 955 { 956 ret = e1; 957 return; 958 } 959 if (e2.op == TOK.error) 960 { 961 ret = e2; 962 return; 963 } 964 ret = Equal(e.op, e.loc, e.type, e1, e2).copy(); 965 if (CTFEExp.isCantExp(ret)) 966 ret = e; 967 } 968 969 override void visit(IdentityExp e) 970 { 971 //printf("IdentityExp::optimize(result = %d) %s\n", result, e.toChars()); 972 if (binOptimize(e, WANTvalue)) 973 return; 974 if ((e.e1.isConst() && e.e2.isConst()) || (e.e1.op == TOK.null_ && e.e2.op == TOK.null_)) 975 { 976 ret = Identity(e.op, e.loc, e.type, e.e1, e.e2).copy(); 977 if (CTFEExp.isCantExp(ret)) 978 ret = e; 979 } 980 } 981 982 override void visit(IndexExp e) 983 { 984 //printf("IndexExp::optimize(result = %d) %s\n", result, e.toChars()); 985 if (expOptimize(e.e1, result & WANTexpand)) 986 return; 987 Expression ex = fromConstInitializer(result, e.e1); 988 // We might know $ now 989 setLengthVarIfKnown(e.lengthVar, ex); 990 if (expOptimize(e.e2, WANTvalue)) 991 return; 992 if (keepLvalue) 993 return; 994 ret = Index(e.type, ex, e.e2).copy(); 995 if (CTFEExp.isCantExp(ret)) 996 ret = e; 997 } 998 999 override void visit(SliceExp e) 1000 { 1001 //printf("SliceExp::optimize(result = %d) %s\n", result, e.toChars()); 1002 if (expOptimize(e.e1, result & WANTexpand)) 1003 return; 1004 if (!e.lwr) 1005 { 1006 if (e.e1.op == TOK.string_) 1007 { 1008 // Convert slice of string literal into dynamic array 1009 Type t = e.e1.type.toBasetype(); 1010 if (Type tn = t.nextOf()) 1011 ret = e.e1.castTo(null, tn.arrayOf()); 1012 } 1013 } 1014 else 1015 { 1016 e.e1 = fromConstInitializer(result, e.e1); 1017 // We might know $ now 1018 setLengthVarIfKnown(e.lengthVar, e.e1); 1019 expOptimize(e.lwr, WANTvalue); 1020 expOptimize(e.upr, WANTvalue); 1021 if (ret.op == TOK.error) 1022 return; 1023 ret = Slice(e.type, e.e1, e.lwr, e.upr).copy(); 1024 if (CTFEExp.isCantExp(ret)) 1025 ret = e; 1026 } 1027 // https://issues.dlang.org/show_bug.cgi?id=14649 1028 // Leave the slice form so it might be 1029 // a part of array operation. 1030 // Assume that the backend codegen will handle the form `e[]` 1031 // as an equal to `e` itself. 1032 if (ret.op == TOK.string_) 1033 { 1034 e.e1 = ret; 1035 e.lwr = null; 1036 e.upr = null; 1037 ret = e; 1038 } 1039 //printf("-SliceExp::optimize() %s\n", ret.toChars()); 1040 } 1041 1042 override void visit(LogicalExp e) 1043 { 1044 //printf("LogicalExp::optimize(%d) %s\n", result, e.toChars()); 1045 if (expOptimize(e.e1, WANTvalue)) 1046 return; 1047 const oror = e.op == TOK.orOr; 1048 if (e.e1.isBool(oror)) 1049 { 1050 // Replace with (e1, oror) 1051 ret = IntegerExp.createBool(oror); 1052 ret = Expression.combine(e.e1, ret); 1053 if (e.type.toBasetype().ty == Tvoid) 1054 { 1055 ret = new CastExp(e.loc, ret, Type.tvoid); 1056 ret.type = e.type; 1057 } 1058 ret = Expression_optimize(ret, result, false); 1059 return; 1060 } 1061 if (expOptimize(e.e2, WANTvalue)) 1062 return; 1063 if (e.e1.isConst()) 1064 { 1065 if (e.e2.isConst()) 1066 { 1067 bool n1 = e.e1.isBool(true); 1068 bool n2 = e.e2.isBool(true); 1069 ret = new IntegerExp(e.loc, oror ? (n1 || n2) : (n1 && n2), e.type); 1070 } 1071 else if (e.e1.isBool(!oror)) 1072 { 1073 if (e.type.toBasetype().ty == Tvoid) 1074 ret = e.e2; 1075 else 1076 { 1077 ret = new CastExp(e.loc, e.e2, e.type); 1078 ret.type = e.type; 1079 } 1080 } 1081 } 1082 } 1083 1084 override void visit(CmpExp e) 1085 { 1086 //printf("CmpExp::optimize() %s\n", e.toChars()); 1087 if (binOptimize(e, WANTvalue)) 1088 return; 1089 Expression e1 = fromConstInitializer(result, e.e1); 1090 Expression e2 = fromConstInitializer(result, e.e2); 1091 ret = Cmp(e.op, e.loc, e.type, e1, e2).copy(); 1092 if (CTFEExp.isCantExp(ret)) 1093 ret = e; 1094 } 1095 1096 override void visit(CatExp e) 1097 { 1098 //printf("CatExp::optimize(%d) %s\n", result, e.toChars()); 1099 if (binOptimize(e, result)) 1100 return; 1101 if (e.e1.op == TOK.concatenate) 1102 { 1103 // https://issues.dlang.org/show_bug.cgi?id=12798 1104 // optimize ((expr ~ str1) ~ str2) 1105 CatExp ce1 = cast(CatExp)e.e1; 1106 scope CatExp cex = new CatExp(e.loc, ce1.e2, e.e2); 1107 cex.type = e.type; 1108 Expression ex = Expression_optimize(cex, result, false); 1109 if (ex != cex) 1110 { 1111 e.e1 = ce1.e1; 1112 e.e2 = ex; 1113 } 1114 } 1115 // optimize "str"[] -> "str" 1116 if (e.e1.op == TOK.slice) 1117 { 1118 SliceExp se1 = cast(SliceExp)e.e1; 1119 if (se1.e1.op == TOK.string_ && !se1.lwr) 1120 e.e1 = se1.e1; 1121 } 1122 if (e.e2.op == TOK.slice) 1123 { 1124 SliceExp se2 = cast(SliceExp)e.e2; 1125 if (se2.e1.op == TOK.string_ && !se2.lwr) 1126 e.e2 = se2.e1; 1127 } 1128 ret = Cat(e.type, e.e1, e.e2).copy(); 1129 if (CTFEExp.isCantExp(ret)) 1130 ret = e; 1131 } 1132 1133 override void visit(CondExp e) 1134 { 1135 if (expOptimize(e.econd, WANTvalue)) 1136 return; 1137 if (e.econd.isBool(true)) 1138 ret = Expression_optimize(e.e1, result, keepLvalue); 1139 else if (e.econd.isBool(false)) 1140 ret = Expression_optimize(e.e2, result, keepLvalue); 1141 else 1142 { 1143 expOptimize(e.e1, result, keepLvalue); 1144 expOptimize(e.e2, result, keepLvalue); 1145 } 1146 } 1147 } 1148 1149 scope OptimizeVisitor v = new OptimizeVisitor(e, result, keepLvalue); 1150 1151 // Optimize the expression until it can no longer be simplified. 1152 size_t b; 1153 while (1) 1154 { 1155 if (b++ == global.recursionLimit) 1156 { 1157 e.error("infinite loop while optimizing expression"); 1158 fatal(); 1159 } 1160 auto ex = v.ret; 1161 ex.accept(v); 1162 if (ex == v.ret) 1163 break; 1164 } 1165 return v.ret; 1166 }