2 * Perform constant folding.
4 * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved
5 * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright)
6 * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
7 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/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
14 import core
.stdc
.stdio
;
21 import dmd
.declaration
;
23 import dmd
.dsymbolsem
;
25 import dmd
.expression
;
26 import dmd
.expressionsem
;
33 import dmd
.root
.ctfloat
;
34 import dmd
.sideeffect
;
38 /*************************************
39 * If variable has a const initializer,
40 * return that initializer.
42 * initializer if there is one,
46 Expression
expandVar(int result
, VarDeclaration v
)
48 //printf("expandVar(result = %d, v = %p, %s)\n", result, v, v ? v.toChars() : "null");
52 * e = initializer expression
54 Expression
initializerReturn(Expression e
)
58 e
= e
.castTo(null, v
.type
);
61 e
= e
.optimize(result
);
63 //if (e) printf("\te = %p, %s, e.type = %d, %s\n", e, e.toChars(), e.type.ty, e.type.toChars());
67 static Expression
nullReturn()
72 static Expression
errorReturn()
74 return ErrorExp
.get();
79 if (!v
.originalType
&& v
.semanticRun
< PASS
.semanticdone
) // semantic() not yet run
80 v
.dsymbolSemantic(null);
82 (v
.isConst() || v
.isImmutable() || v
.storage_class
& STC
.manifest
))
84 Type tb
= v
.type
.toBasetype();
85 if (v
.storage_class
& STC
.manifest ||
87 ((result
& WANTexpand
) && (tb
.ty
!= Tsarray
&& tb
.ty
!= Tstruct
)))
93 if (v
.storage_class
& STC
.manifest
)
95 .error(v
.loc
, "%s `%s` recursive initialization of constant", v
.kind
, v
.toPrettyChars
);
100 Expression ei
= v
.getConstInitializer();
103 if (v
.storage_class
& STC
.manifest
)
105 .error(v
.loc
, "%s `%s` enum cannot be initialized with `%s`", v
.kind
, v
.toPrettyChars
, dmd
.hdrgen
.toChars(v
._init
));
106 return errorReturn();
110 if (ei
.op
== EXP
.construct || ei
.op
== EXP
.blit
)
112 AssignExp ae
= cast(AssignExp
)ei
;
114 if (ei
.isConst() == 1)
117 else if (ei
.op
== EXP
.string_
)
119 // https://issues.dlang.org/show_bug.cgi?id=14459
120 // Do not constfold the string literal
121 // if it's typed as a C string, because the value expansion
122 // will drop the pointer identity.
123 if (!(result
& WANTexpand
) && ei
.type
.toBasetype().ty
== Tpointer
)
128 if (ei
.type
== v
.type
)
130 // const variable initialized with const expression
132 else if (ei
.implicitConvTo(v
.type
) >= MATCH
.constant
)
134 // const var initialized with non-const expression
135 ei
= ei
.implicitCastTo(null, v
.type
);
136 ei
= ei
.expressionSemantic(null);
141 else if (!(v
.storage_class
& STC
.manifest
) &&
143 ei
.op
!= EXP
.string_
&&
144 ei
.op
!= EXP
.address
)
155 // Should remove the copy() operation by
156 // making all mods to expressions copy-on-write
157 return initializerReturn(ei
.copy());
162 // v does not have an initializer
169 // BUG: what if const is initialized in constructor?
170 auto e
= v
.type
.defaultInit();
172 return initializerReturn(e
);
181 private Expression
fromConstInitializer(int result
, Expression e1
)
183 //printf("fromConstInitializer(result = %x, %s)\n", result, e1.toChars());
184 //static int xx; if (xx++ == 10) assert(0);
186 if (auto ve
= e1
.isVarExp())
188 VarDeclaration v
= ve
.var
.isVarDeclaration();
189 e
= expandVar(result
, v
);
192 // If it is a comma expression involving a declaration, we mustn't
193 // perform a copy -- we'd get two declarations of the same variable.
194 // See https://issues.dlang.org/show_bug.cgi?id=4465.
195 if (e
.op
== EXP
.comma
&& e
.isCommaExp().e1
.isDeclarationExp())
197 else if (e
.type
!= e1
.type
&& e1
.type
&& e1
.type
.ty
!= Tident
)
199 // Type 'paint' operation
214 * It is possible for constant folding to change an array expression of
215 * unknown length, into one where the length is known.
216 * If the expression 'arr' is a literal, set lengthVar to be its length.
218 * lengthVar = variable declaration for the `.length` property
219 * arr = String, ArrayLiteral, or of TypeSArray
221 package void setLengthVarIfKnown(VarDeclaration lengthVar
, Expression arr
)
225 if (lengthVar
._init
&& !lengthVar
._init
.isVoidInitializer())
226 return; // we have previously calculated the length
228 if (auto se
= arr
.isStringExp())
230 else if (auto ale
= arr
.isArrayLiteralExp())
231 len
= ale
.elements
.length
;
234 auto tsa
= arr
.type
.toBasetype().isTypeSArray();
236 return; // we don't know the length yet
237 len
= tsa
.dim
.toInteger();
239 Expression dollar
= new IntegerExp(Loc
.initial
, len
, Type
.tsize_t
);
240 lengthVar
._init
= new ExpInitializer(Loc
.initial
, dollar
);
241 lengthVar
.storage_class |
= STC
.static_ | STC
.const_
;
245 * Same as above, but determines the length from 'type'.
247 * lengthVar = variable declaration for the `.length` property
250 package void setLengthVarIfKnown(VarDeclaration lengthVar
, Type type
)
254 if (lengthVar
._init
&& !lengthVar
._init
.isVoidInitializer())
255 return; // we have previously calculated the length
256 auto tsa
= type
.toBasetype().isTypeSArray();
258 return; // we don't know the length yet
259 const len
= tsa
.dim
.toInteger();
260 Expression dollar
= new IntegerExp(Loc
.initial
, len
, Type
.tsize_t
);
261 lengthVar
._init
= new ExpInitializer(Loc
.initial
, dollar
);
262 lengthVar
.storage_class |
= STC
.static_ | STC
.const_
;
265 /*********************************
266 * Constant fold an Expression.
268 * e = expression to const fold; this may get modified in-place
269 * result = WANTvalue, WANTexpand, or both
270 * keepLvalue = `e` is an lvalue, and keep it as an lvalue since it is
271 * an argument to a `ref` or `out` parameter, or the operand of `&` operator
273 * Constant folded version of `e`
275 Expression
optimize(Expression e
, int result
, bool keepLvalue
= false)
277 //printf("optimize() e: %s result: %d keepLvalue %d\n", e.toChars(), result, keepLvalue);
282 ret = ErrorExp
.get();
285 /* Returns: true if error
287 bool expOptimize(ref Expression e
, int flags
, bool keepLvalue
= false)
291 Expression ex
= optimize(e
, flags
, keepLvalue
);
292 if (ex
.op
== EXP
.error
)
294 ret = ex
; // store error result
299 e
= ex
; // modify original
304 bool unaOptimize(UnaExp e
, int flags
)
306 return expOptimize(e
.e1
, flags
);
309 bool binOptimize(BinExp e
, int flags
, bool keepLhsLvalue
= false)
311 return expOptimize(e
.e1
, flags
, keepLhsLvalue
) |
312 expOptimize(e
.e2
, flags
);
315 void visitExp(Expression e
)
317 //printf("Expression::optimize(result = x%x) %s\n", result, e.toChars());
320 void visitVar(VarExp e
)
322 VarDeclaration v
= e
.var
.isVarDeclaration();
324 if (!(keepLvalue
&& v
&& !(v
.storage_class
& STC
.manifest
)))
325 ret = fromConstInitializer(result
, e
);
327 // if unoptimized, try to optimize the dtor expression
328 // (e.g., might be a LogicalExp with constant lhs)
329 if (ret == e
&& v
&& v
.edtor
)
331 // prevent infinite recursion (`<var>.~this()`)
335 expOptimize(v
.edtor
, WANTvalue
);
341 void visitTuple(TupleExp e
)
343 expOptimize(e
.e0
, WANTvalue
);
344 foreach (ref ex
; (*e
.exps
)[])
346 expOptimize(ex
, WANTvalue
);
350 void visitArrayLiteral(ArrayLiteralExp e
)
354 expOptimize(e
.basis
, result
& WANTexpand
);
355 foreach (ref ex
; (*e
.elements
)[])
357 expOptimize(ex
, result
& WANTexpand
);
362 void visitAssocArrayLiteral(AssocArrayLiteralExp e
)
364 assert(e
.keys
.length
== e
.values
.length
);
365 foreach (i
, ref ekey
; (*e
.keys
)[])
367 expOptimize(ekey
, result
& WANTexpand
);
368 expOptimize((*e
.values
)[i
], result
& WANTexpand
);
372 void visitStructLiteral(StructLiteralExp e
)
374 if (e
.stageflags
& stageOptimize
)
376 const old
= e
.stageflags
;
377 e
.stageflags |
= stageOptimize
;
380 foreach (ref ex
; (*e
.elements
)[])
382 expOptimize(ex
, result
& WANTexpand
);
388 void visitUna(UnaExp e
)
390 //printf("UnaExp::optimize() %s\n", e.toChars());
391 if (unaOptimize(e
, result
))
395 void visitNeg(NegExp e
)
397 if (unaOptimize(e
, result
))
399 if (e
.e1
.isConst() == 1)
401 ret = Neg(e
.type
, e
.e1
).copy();
405 void visitCom(ComExp e
)
407 if (unaOptimize(e
, result
))
409 if (e
.e1
.isConst() == 1)
411 ret = Com(e
.type
, e
.e1
).copy();
415 void visitNop(NotExp e
)
417 if (unaOptimize(e
, result
))
419 if (e
.e1
.isConst() == 1)
421 ret = Not(e
.type
, e
.e1
).copy();
425 void visitSymOff(SymOffExp e
)
430 void visitAddr(AddrExp e
)
432 //printf("AddrExp::optimize(result = %d, keepLvalue = %d) %s\n", result, keepLvalue, e.toChars());
433 /* Rewrite &(a,b) as (a,&b)
435 if (auto ce
= e
.e1
.isCommaExp())
437 auto ae
= new AddrExp(e
.loc
, ce
.e2
, e
.type
);
438 ret = new CommaExp(ce
.loc
, ce
.e1
, ae
);
443 if (expOptimize(e
.e1
, result
, true))
444 return; // error return
446 // Convert &*ex to ex
447 if (auto pe
= e
.e1
.isPtrExp())
449 Expression ex
= pe
.e1
;
450 if (e
.type
.equals(ex
.type
))
452 else if (e
.type
.toBasetype().equivalent(ex
.type
.toBasetype()))
459 if (auto ve
= e
.e1
.isVarExp())
461 if (!ve
.var
.isReference() && !ve
.var
.isImportedSymbol())
463 ret = new SymOffExp(e
.loc
, ve
.var
, 0, ve
.hasOverloads
);
468 if (e
.e1
.isDotVarExp())
470 /******************************
471 * Run down the left side of the a.b.c expression to determine the
472 * leftmost variable being addressed (`a`), and accumulate the offsets of the `.b` and `.c`.
474 * e = the DotVarExp or VarExp
475 * var = set to the VarExp at the end, or null if doesn't end in VarExp
476 * eint = set to the IntegerExp at the end, or null if doesn't end in IntegerExp
477 * offset = accumulation of all the .var offsets encountered
478 * Returns: true on error
480 static bool getVarAndOffset(Expression e
, out VarDeclaration var
, out IntegerExp eint
, ref uint offset
)
482 if (e
.type
.size() == SIZE_INVALID
) // trigger computation of v.offset
485 if (auto dve
= e
.isDotVarExp())
487 auto v
= dve
.var
.isVarDeclaration();
488 if (!v ||
!v
.isField() || v
.isBitFieldDeclaration())
491 if (getVarAndOffset(dve
.e1
, var
, eint
, offset
))
495 else if (auto ve
= e
.isVarExp())
497 if (!ve
.var
.isReference() &&
498 !ve
.var
.isImportedSymbol() &&
499 ve
.var
.isDataseg() &&
502 var
= ve
.var
.isVarDeclaration();
505 else if (auto ep
= e
.isPtrExp())
507 if (auto ei
= ep
.e1
.isIntegerExp())
511 else if (auto se
= ep
.e1
.isSymOffExp())
513 if (!se
.var
.isReference() &&
514 !se
.var
.isImportedSymbol() &&
517 var
= se
.var
.isVarDeclaration();
522 else if (auto ei
= e
.isIndexExp())
524 if (auto ve
= ei
.e1
.isVarExp())
526 if (!ve
.var
.isReference() &&
527 !ve
.var
.isImportedSymbol() &&
528 ve
.var
.isDataseg() &&
531 if (auto ie
= ei
.e2
.isIntegerExp())
533 var
= ve
.var
.isVarDeclaration();
534 offset
+= ie
.toInteger() * ve
.type
.toBasetype().nextOf().size();
545 if (getVarAndOffset(e
.e1
, var
, eint
, offset
))
547 ret = ErrorExp
.get();
552 ret = new SymOffExp(e
.loc
, var
, offset
, false);
558 ret = new IntegerExp(e
.loc
, eint
.toInteger() + offset
, e
.type
);
562 else if (auto ae
= e
.e1
.isIndexExp())
564 if (ae
.e2
.isIntegerExp() && ae
.e1
.isIndexExp())
566 /* Rewrite `(a[i])[index]` to `(&a[i]) + index*size`
568 sinteger_t index
= ae
.e2
.toInteger();
569 auto ae1
= ae
.e1
.isIndexExp(); // ae1 is a[i]
570 if (auto ts
= ae1
.type
.isTypeSArray())
572 sinteger_t dim
= ts
.dim
.toInteger();
574 if (index
< 0 || index
> dim
)
576 error(e
.loc
, "array index %lld is out of bounds `[0..%lld]`", index
, dim
);
577 return errorReturn();
580 import core
.checkedint
: mulu
;
582 const offset
= mulu(index
, ts
.nextOf().size(e
.loc
), overflow
); // offset = index*size
585 error(e
.loc
, "array offset overflow");
586 return errorReturn();
589 Expression ex
= new AddrExp(ae1
.loc
, ae1
); // &a[i]
590 ex
.type
= ae1
.type
.pointerTo();
592 Expression
add = new AddExp(ae
.loc
, ex
, new IntegerExp(ae
.e2
.loc
, offset
, ae
.e2
.type
));
594 ret = optimize(add, result
, keepLvalue
);
599 // Convert &array[n] to &array+n
600 if (ae
.e2
.isIntegerExp() && ae
.e1
.isVarExp())
602 sinteger_t index
= ae
.e2
.toInteger();
603 VarExp ve
= ae
.e1
.isVarExp();
604 if (ve
.type
.isTypeSArray() && !ve
.var
.isImportedSymbol())
606 TypeSArray ts
= ve
.type
.isTypeSArray();
607 sinteger_t dim
= ts
.dim
.toInteger();
608 if (index
< 0 || index
>= dim
)
610 /* 0 for C static arrays means size is unknown, no need to check,
611 * and address one past the end is OK, too
613 if (!((dim
== 0 || dim
== index
) && ve
.var
.isCsymbol()))
615 error(e
.loc
, "array index %lld is out of bounds `[0..%lld]`", index
, dim
);
616 return errorReturn();
620 import core
.checkedint
: mulu
;
622 const offset
= mulu(index
, ts
.nextOf().size(e
.loc
), overflow
);
625 error(e
.loc
, "array offset overflow");
626 return errorReturn();
629 ret = new SymOffExp(e
.loc
, ve
.var
, offset
);
634 // Convert &((a.b)[index]) to (&a.b)+index*elementsize
635 else if (ae
.e2
.isIntegerExp() && ae
.e1
.isDotVarExp())
637 sinteger_t index
= ae
.e2
.toInteger();
638 DotVarExp ve
= ae
.e1
.isDotVarExp();
639 if (ve
.type
.isTypeSArray() && ve
.var
.isField() && ve
.e1
.isPtrExp())
641 TypeSArray ts
= ve
.type
.isTypeSArray();
642 sinteger_t dim
= ts
.dim
.toInteger();
643 if (index
< 0 || index
>= dim
)
645 /* 0 for C static arrays means size is unknown, no need to check,
646 * and address one past the end is OK, too
648 if (!((dim
== 0 || dim
== index
) && ve
.var
.isCsymbol()))
650 error(e
.loc
, "array index %lld is out of bounds `[0..%lld]`", index
, dim
);
651 return errorReturn();
655 import core
.checkedint
: mulu
;
657 const offset
= mulu(index
, ts
.nextOf().size(e
.loc
), overflow
); // index*elementsize
660 error(e
.loc
, "array offset overflow");
661 return errorReturn();
664 auto pe
= new AddrExp(e
.loc
, ve
);
666 ret = new AddExp(e
.loc
, pe
, new IntegerExp(e
.loc
, offset
, Type
.tsize_t
));
674 void visitPtr(PtrExp e
)
676 //printf("PtrExp::optimize(result = x%x) %s\n", result, e.toChars());
677 if (expOptimize(e
.e1
, result
))
679 // Convert *&ex to ex
680 // But only if there is no type punning involved
681 if (auto ey
= e
.e1
.isAddrExp())
683 Expression ex
= ey
.e1
;
684 if (e
.type
.equals(ex
.type
))
686 else if (e
.type
.toBasetype().equivalent(ex
.type
.toBasetype()))
694 // Constant fold *(&structliteral + offset)
695 if (e
.e1
.op
== EXP
.add)
697 Expression ex
= Ptr(e
.type
, e
.e1
).copy();
698 if (!CTFEExp
.isCantExp(ex
))
704 if (auto se
= e
.e1
.isSymOffExp())
706 VarDeclaration v
= se
.var
.isVarDeclaration();
707 Expression ex
= expandVar(result
, v
);
708 if (ex
&& ex
.isStructLiteralExp())
710 StructLiteralExp sle
= ex
.isStructLiteralExp();
711 ex
= sle
.getField(e
.type
, cast(uint)se
.offset
);
712 if (ex
&& !CTFEExp
.isCantExp(ex
))
721 void visitDotVar(DotVarExp e
)
723 //printf("DotVarExp::optimize(result = x%x) %s\n", result, e.toChars());
724 if (expOptimize(e
.e1
, result
))
728 Expression ex
= e
.e1
;
729 if (auto ve
= ex
.isVarExp())
731 VarDeclaration v
= ve
.var
.isVarDeclaration();
732 ex
= expandVar(result
, v
);
734 if (ex
&& ex
.isStructLiteralExp())
736 StructLiteralExp sle
= ex
.isStructLiteralExp();
737 VarDeclaration vf
= e
.var
.isVarDeclaration();
738 if (vf
&& !vf
.overlapped
)
740 /* https://issues.dlang.org/show_bug.cgi?id=13021
741 * Prevent optimization if vf has overlapped fields.
743 ex
= sle
.getField(e
.type
, vf
.offset
);
744 if (ex
&& !CTFEExp
.isCantExp(ex
))
753 void visitNew(NewExp e
)
755 expOptimize(e
.thisexp
, WANTvalue
);
756 // Optimize parameters
759 foreach (ref arg
; (*e
.arguments
)[])
761 expOptimize(arg
, WANTvalue
);
766 void visitCall(CallExp e
)
768 //printf("CallExp::optimize(result = %d) %s\n", result, e.toChars());
769 // Optimize parameters with keeping lvalue-ness
770 if (expOptimize(e
.e1
, result
))
774 // t1 can apparently be void for __ArrayDtor(T) calls
775 if (auto tf
= e
.calledFunctionType())
777 foreach (i
, ref arg
; (*e
.arguments
)[])
779 Parameter p
= tf
.parameterList
[i
];
780 bool keep
= p
&& p
.isReference();
781 expOptimize(arg
, WANTvalue
, keep
);
787 void visitCast(CastExp e
)
789 //printf("CastExp::optimize(result = %d) %s\n", result, e.toChars());
790 //printf("from %s to %s\n", e.type.toChars(), e.to.toChars());
791 //printf("from %s\n", e.type.toChars());
792 //printf("e1.type %s\n", e.e1.type.toChars());
793 //printf("type = %p\n", e.type);
796 Expression e1old
= e
.e1
;
797 if (expOptimize(e
.e1
, result
, keepLvalue
))
800 e
.e1
= fromConstInitializer(result
, e
.e1
);
801 if (e
.e1
== e1old
&& e
.e1
.op
== EXP
.arrayLiteral
&& e
.type
.toBasetype().ty
== Tpointer
&& e
.e1
.type
.toBasetype().ty
!= Tsarray
)
803 // Casting this will result in the same expression, and
804 // infinite loop because of Expression::implicitCastTo()
807 if ((e
.e1
.op
== EXP
.string_ || e
.e1
.op
== EXP
.arrayLiteral
) &&
808 (e
.type
.ty
== Tpointer || e
.type
.ty
== Tarray
))
810 const esz
= e
.type
.nextOf().size(e
.loc
);
811 const e1sz
= e
.e1
.type
.toBasetype().nextOf().size(e
.e1
.loc
);
812 if (esz
== SIZE_INVALID || e1sz
== SIZE_INVALID
)
813 return errorReturn();
817 // https://issues.dlang.org/show_bug.cgi?id=12937
818 // If target type is void array, trying to paint
819 // e.e1 with that type will cause infinite recursive optimization.
820 if (e
.type
.nextOf().ty
== Tvoid
)
822 ret = e
.e1
.castTo(null, e
.type
);
823 //printf(" returning1 %s\n", ret.toChars());
828 // Returning e.e1 with changing its type
831 ret = (e1old
== e
.e1 ? e
.e1
.copy() : e
.e1
);
835 if (e
.e1
.op
== EXP
.structLiteral
&& e
.e1
.type
.implicitConvTo(e
.type
) >= MATCH
.constant
)
837 //printf(" returning2 %s\n", e.e1.toChars());
840 /* The first test here is to prevent infinite loops
842 if (op1
!= EXP
.arrayLiteral
&& e
.e1
.op
== EXP
.arrayLiteral
)
844 ret = e
.e1
.castTo(null, e
.to
);
847 if (e
.e1
.op
== EXP
.null_
&& (e
.type
.ty
== Tpointer || e
.type
.ty
== Tclass || e
.type
.ty
== Tarray
))
849 //printf(" returning3 %s\n", e.e1.toChars());
852 if (e
.type
.ty
== Tclass
&& e
.e1
.type
.ty
== Tclass
)
854 import dmd
.astenums
: Sizeok
;
856 // See if we can remove an unnecessary cast
857 ClassDeclaration cdfrom
= e
.e1
.type
.isClassHandle();
858 ClassDeclaration cdto
= e
.type
.isClassHandle();
859 if (cdfrom
.errors || cdto
.errors
)
860 return errorReturn();
861 if (cdto
== ClassDeclaration
.object
&& !cdfrom
.isInterfaceDeclaration())
862 return returnE_e1(); // can always convert a class to Object
863 // Need to determine correct offset before optimizing away the cast.
864 // https://issues.dlang.org/show_bug.cgi?id=16980
865 if (cdfrom
.size(e
.loc
) == SIZE_INVALID
)
866 return errorReturn();
867 assert(cdfrom
.sizeok
== Sizeok
.done
);
868 assert(cdto
.sizeok
== Sizeok
.done ||
!cdto
.isBaseOf(cdfrom
, null));
870 if (cdto
.isBaseOf(cdfrom
, &offset
) && offset
== 0)
872 //printf(" returning4 %s\n", e.e1.toChars());
876 if (e
.e1
.type
.mutableOf().unSharedOf().equals(e
.to
.mutableOf().unSharedOf()))
878 //printf(" returning5 %s\n", e.e1.toChars());
883 if (e
.e1
.op
== EXP
.symbolOffset
)
885 if (e
.type
.toBasetype().ty
!= Tsarray
)
887 const esz
= e
.type
.size(e
.loc
);
888 const e1sz
= e
.e1
.type
.size(e
.e1
.loc
);
889 if (esz
== SIZE_INVALID ||
890 e1sz
== SIZE_INVALID
)
891 return errorReturn();
898 if (e
.to
.toBasetype().ty
!= Tvoid
)
900 if (e
.e1
.type
.equals(e
.type
) && e
.type
.equals(e
.to
))
903 ret = Cast(e
.loc
, e
.type
, e
.to
, e
.e1
).copy();
906 //printf(" returning6 %s\n", ret.toChars());
909 void visitBinAssign(BinAssignExp e
)
911 //printf("BinAssignExp::optimize(result = %d) %s\n", result, e.toChars());
912 if (binOptimize(e
, result
, /*keepLhsLvalue*/ true))
914 if (e
.op
== EXP
.leftShiftAssign || e
.op
== EXP
.rightShiftAssign || e
.op
== EXP
.unsignedRightShiftAssign
)
916 if (e
.e2
.isConst() == 1)
918 sinteger_t i2
= e
.e2
.toInteger();
919 uinteger_t sz
= e
.e1
.type
.size(e
.e1
.loc
);
920 assert(sz
!= SIZE_INVALID
);
922 if (i2
< 0 || i2
>= sz
)
924 error(e
.loc
, "shift assign by %lld is outside the range `0..%llu`", i2
, cast(ulong)sz
- 1);
925 return errorReturn();
931 void visitCatAssign(CatAssignExp e
)
933 if (auto lowering
= e
.lowering
)
934 optimize(lowering
, result
, keepLvalue
);
939 void visitBin(BinExp e
)
941 //printf("BinExp::optimize(result = %d) %s\n", result, e.toChars());
942 const keepLhsLvalue
= e
.op
== EXP
.construct || e
.op
== EXP
.blit || e
.op
== EXP
.assign
943 || e
.op
== EXP
.plusPlus || e
.op
== EXP
.minusMinus
944 || e
.op
== EXP
.prePlusPlus || e
.op
== EXP
.preMinusMinus
;
945 binOptimize(e
, result
, keepLhsLvalue
);
948 void visitAdd(AddExp e
)
950 //printf("AddExp::optimize(%s)\n", e.toChars());
951 if (binOptimize(e
, result
))
953 if (e
.e1
.isConst() && e
.e2
.isConst())
955 if (e
.e1
.op
== EXP
.symbolOffset
&& e
.e2
.op
== EXP
.symbolOffset
)
957 ret = Add(e
.loc
, e
.type
, e
.e1
, e
.e2
).copy();
961 void visitMin(MinExp e
)
963 //printf("MinExp::optimize(%s)\n", e.toChars());
964 if (binOptimize(e
, result
))
966 if (e
.e1
.isConst() && e
.e2
.isConst())
968 if (e
.e2
.op
== EXP
.symbolOffset
)
970 ret = Min(e
.loc
, e
.type
, e
.e1
, e
.e2
).copy();
974 void visitMul(MulExp e
)
976 //printf("MulExp::optimize(result = %d) %s\n", result, e.toChars());
977 if (binOptimize(e
, result
))
979 if (e
.e1
.isConst() == 1 && e
.e2
.isConst() == 1)
981 ret = Mul(e
.loc
, e
.type
, e
.e1
, e
.e2
).copy();
985 void visitDiv(DivExp e
)
987 //printf("DivExp::optimize(%s)\n", e.toChars());
988 if (binOptimize(e
, result
))
990 if (e
.e1
.isConst() == 1 && e
.e2
.isConst() == 1)
992 ret = Div(e
.loc
, e
.type
, e
.e1
, e
.e2
).copy();
996 void visitMod(ModExp e
)
998 if (binOptimize(e
, result
))
1000 if (e
.e1
.isConst() == 1 && e
.e2
.isConst() == 1)
1002 ret = Mod(e
.loc
, e
.type
, e
.e1
, e
.e2
).copy();
1006 extern (D
) void shift_optimize(BinExp e
, UnionExp
function(const ref Loc
, Type
, Expression
, Expression
) shift
)
1008 if (binOptimize(e
, result
))
1010 if (e
.e2
.isConst() == 1)
1012 sinteger_t i2
= e
.e2
.toInteger();
1013 uinteger_t sz
= e
.e1
.type
.size(e
.e1
.loc
);
1014 assert(sz
!= SIZE_INVALID
);
1016 if (i2
< 0 || i2
>= sz
)
1018 error(e
.loc
, "shift by %lld is outside the range `0..%llu`", i2
, cast(ulong)sz
- 1);
1019 return errorReturn();
1021 if (e
.e1
.isConst() == 1)
1022 ret = (*shift
)(e
.loc
, e
.type
, e
.e1
, e
.e2
).copy();
1026 void visitShl(ShlExp e
)
1028 //printf("ShlExp::optimize(result = %d) %s\n", result, e.toChars());
1029 shift_optimize(e
, &Shl
);
1032 void visitShr(ShrExp e
)
1034 //printf("ShrExp::optimize(result = %d) %s\n", result, e.toChars());
1035 shift_optimize(e
, &Shr
);
1038 void visitUshr(UshrExp e
)
1040 //printf("UshrExp::optimize(result = %d) %s\n", result, toChars());
1041 shift_optimize(e
, &Ushr
);
1044 void visitAnd(AndExp e
)
1046 if (binOptimize(e
, result
))
1048 if (e
.e1
.isConst() == 1 && e
.e2
.isConst() == 1)
1049 ret = And(e
.loc
, e
.type
, e
.e1
, e
.e2
).copy();
1052 void visitOr(OrExp e
)
1054 if (binOptimize(e
, result
))
1056 if (e
.e1
.isConst() == 1 && e
.e2
.isConst() == 1)
1057 ret = Or(e
.loc
, e
.type
, e
.e1
, e
.e2
).copy();
1060 void visitXor(XorExp e
)
1062 if (binOptimize(e
, result
))
1064 if (e
.e1
.isConst() == 1 && e
.e2
.isConst() == 1)
1065 ret = Xor(e
.loc
, e
.type
, e
.e1
, e
.e2
).copy();
1068 void visitPow(PowExp e
)
1070 if (binOptimize(e
, result
))
1072 // All negative integral powers are illegal.
1073 if (e
.e1
.type
.isintegral() && (e
.e2
.op
== EXP
.int64
) && cast(sinteger_t
)e
.e2
.toInteger() < 0)
1075 error(e
.loc
, "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());
1076 return errorReturn();
1078 // If e2 *could* have been an integer, make it one.
1079 if (e
.e2
.op
== EXP
.float64
&& e
.e2
.toReal() == real_t(cast(sinteger_t
)e
.e2
.toReal()))
1081 // This only applies to floating point, or positive integral powers.
1082 if (e
.e1
.type
.isfloating() ||
cast(sinteger_t
)e
.e2
.toInteger() >= 0)
1083 e
.e2
= new IntegerExp(e
.loc
, e
.e2
.toInteger(), Type
.tint64
);
1085 if (e
.e1
.isConst() == 1 && e
.e2
.isConst() == 1)
1087 Expression ex
= Pow(e
.loc
, e
.type
, e
.e1
, e
.e2
).copy();
1088 if (!CTFEExp
.isCantExp(ex
))
1096 void visitComma(CommaExp e
)
1098 //printf("CommaExp::optimize(result = %d) %s\n", result, e.toChars());
1099 // Comma needs special treatment, because it may
1100 // contain compiler-generated declarations. We can interpret them, but
1101 // otherwise we must NOT attempt to constant-fold them.
1102 // In particular, if the comma returns a temporary variable, it needs
1103 // to be an lvalue (this is particularly important for struct constructors)
1104 expOptimize(e
.e1
, WANTvalue
);
1105 expOptimize(e
.e2
, result
, keepLvalue
);
1106 if (ret.op
== EXP
.error
)
1108 if (!e
.e1 || e
.e1
.op
== EXP
.int64 || e
.e1
.op
== EXP
.float64 ||
!hasSideEffect(e
.e1
))
1114 //printf("-CommaExp::optimize(result = %d) %s\n", result, e.e.toChars());
1117 void visitArrayLength(ArrayLengthExp e
)
1119 //printf("ArrayLengthExp::optimize(result = %d) %s\n", result, e.toChars());
1120 if (unaOptimize(e
, WANTexpand
))
1122 // CTFE interpret static immutable arrays (to get better diagnostics)
1123 if (auto ve
= e
.e1
.isVarExp())
1125 VarDeclaration v
= ve
.var
.isVarDeclaration();
1126 if (v
&& (v
.storage_class
& STC
.static_
) && (v
.storage_class
& STC
.immutable_
) && v
._init
)
1128 if (Expression ci
= v
.getConstInitializer())
1132 if (e
.e1
.op
== EXP
.string_ || e
.e1
.op
== EXP
.arrayLiteral || e
.e1
.op
== EXP
.assocArrayLiteral || e
.e1
.type
.toBasetype().ty
== Tsarray || e
.e1
.op
== EXP
.null_
)
1134 ret = ArrayLength(e
.type
, e
.e1
).copy();
1138 void visitEqual(EqualExp e
)
1140 //printf("EqualExp::optimize(result = %x) %s\n", result, e.toChars());
1141 if (binOptimize(e
, WANTvalue
))
1143 Expression e1
= fromConstInitializer(result
, e
.e1
);
1144 Expression e2
= fromConstInitializer(result
, e
.e2
);
1145 if (e1
.op
== EXP
.error
)
1150 if (e2
.op
== EXP
.error
)
1155 ret = Equal(e
.op
, e
.loc
, e
.type
, e1
, e2
).copy();
1156 if (CTFEExp
.isCantExp(ret))
1160 void visitIdentity(IdentityExp e
)
1162 //printf("IdentityExp::optimize(result = %d) %s\n", result, e.toChars());
1163 if (binOptimize(e
, WANTvalue
))
1165 if ((e
.e1
.isConst() && e
.e2
.isConst()) ||
(e
.e1
.op
== EXP
.null_
&& e
.e2
.op
== EXP
.null_
))
1167 ret = Identity(e
.op
, e
.loc
, e
.type
, e
.e1
, e
.e2
).copy();
1168 if (CTFEExp
.isCantExp(ret))
1173 void visitIndex(IndexExp e
)
1175 //printf("IndexExp::optimize(result = %d) %s\n", result, e.toChars());
1176 if (expOptimize(e
.e1
, result
& WANTexpand
))
1178 Expression ex
= fromConstInitializer(result
, e
.e1
);
1179 // We might know $ now
1180 setLengthVarIfKnown(e
.lengthVar
, ex
);
1181 if (expOptimize(e
.e2
, WANTvalue
))
1183 // Don't optimize to an array literal element directly in case an lvalue is requested
1184 if (keepLvalue
&& ex
.op
== EXP
.arrayLiteral
)
1186 ret = Index(e
.type
, ex
, e
.e2
, e
.indexIsInBounds
).copy();
1187 if (CTFEExp
.isCantExp(ret) ||
(!ret.isErrorExp() && keepLvalue
&& !ret.isLvalue()))
1191 void visitSlice(SliceExp e
)
1193 //printf("SliceExp::optimize(result = %d) %s\n", result, e.toChars());
1194 if (expOptimize(e
.e1
, result
& WANTexpand
))
1198 if (e
.e1
.op
== EXP
.string_
)
1200 // Convert slice of string literal into dynamic array
1201 Type t
= e
.e1
.type
.toBasetype();
1202 if (Type tn
= t
.nextOf())
1203 ret = e
.e1
.castTo(null, tn
.arrayOf());
1208 e
.e1
= fromConstInitializer(result
, e
.e1
);
1209 // We might know $ now
1210 setLengthVarIfKnown(e
.lengthVar
, e
.e1
);
1211 expOptimize(e
.lwr
, WANTvalue
);
1212 expOptimize(e
.upr
, WANTvalue
);
1213 if (ret.op
== EXP
.error
)
1215 ret = Slice(e
.type
, e
.e1
, e
.lwr
, e
.upr
).copy();
1216 if (CTFEExp
.isCantExp(ret))
1219 // https://issues.dlang.org/show_bug.cgi?id=14649
1220 // Leave the slice form so it might be
1221 // a part of array operation.
1222 // Assume that the backend codegen will handle the form `e[]`
1223 // as an equal to `e` itself.
1224 if (ret.op
== EXP
.string_
)
1231 //printf("-SliceExp::optimize() %s\n", ret.toChars());
1234 void visitLogical(LogicalExp e
)
1236 //printf("LogicalExp::optimize(%d) %s\n", result, e.toChars());
1237 if (expOptimize(e
.e1
, WANTvalue
))
1239 const oror
= e
.op
== EXP
.orOr
;
1240 if (e
.e1
.toBool().hasValue(oror
))
1242 // Replace with (e1, oror)
1243 ret = IntegerExp
.createBool(oror
);
1244 ret = Expression
.combine(e
.e1
, ret);
1245 if (e
.type
.toBasetype().ty
== Tvoid
)
1247 ret = new CastExp(e
.loc
, ret, Type
.tvoid
);
1250 ret = optimize(ret, result
, false);
1253 expOptimize(e
.e2
, WANTvalue
);
1256 const e1Opt
= e
.e1
.toBool();
1259 bool n1
= e1Opt
.get();
1260 bool n2
= e
.e2
.toBool().get();
1261 ret = new IntegerExp(e
.loc
, oror ?
(n1 || n2
) : (n1
&& n2
), e
.type
);
1263 else if (e1Opt
.hasValue(!oror
))
1265 if (e
.type
.toBasetype().ty
== Tvoid
)
1269 ret = new CastExp(e
.loc
, e
.e2
, e
.type
);
1276 void visitCmp(CmpExp e
)
1278 //printf("CmpExp::optimize() %s\n", e.toChars());
1279 if (binOptimize(e
, WANTvalue
))
1281 Expression e1
= fromConstInitializer(result
, e
.e1
);
1282 Expression e2
= fromConstInitializer(result
, e
.e2
);
1283 ret = Cmp(e
.op
, e
.loc
, e
.type
, e1
, e2
).copy();
1284 if (CTFEExp
.isCantExp(ret))
1288 void visitCat(CatExp e
)
1290 //printf("CatExp::optimize(%d) %s\n", result, e.toChars());
1291 if (binOptimize(e
, result
))
1294 if (e
.type
== Type
.tstring
)
1295 if (auto ce1
= e
.e1
.isCatExp())
1297 // https://issues.dlang.org/show_bug.cgi?id=12798
1298 // optimize ((expr ~ str1) ~ str2)
1299 // https://issues.dlang.org/show_bug.cgi?id=24078
1300 // This optimization is only valid if `expr` is a string.
1301 // Otherwise it leads to:
1302 // `["c"] ~ "a" ~ "b"` becoming `["c"] ~ "ab"`
1303 scope CatExp cex
= new CatExp(e
.loc
, ce1
.e2
, e
.e2
);
1305 Expression ex
= optimize(cex
, result
, false);
1312 // optimize "str"[] -> "str"
1313 if (auto se1
= e
.e1
.isSliceExp())
1315 if (se1
.e1
.op
== EXP
.string_
&& !se1
.lwr
)
1318 if (auto se2
= e
.e2
.isSliceExp())
1320 if (se2
.e1
.op
== EXP
.string_
&& !se2
.lwr
)
1323 ret = Cat(e
.loc
, e
.type
, e
.e1
, e
.e2
).copy();
1324 if (CTFEExp
.isCantExp(ret))
1328 void visitCond(CondExp e
)
1330 if (expOptimize(e
.econd
, WANTvalue
))
1332 const opt
= e
.econd
.toBool();
1333 if (opt
.hasValue(true))
1334 ret = optimize(e
.e1
, result
, keepLvalue
);
1335 else if (opt
.hasValue(false))
1336 ret = optimize(e
.e2
, result
, keepLvalue
);
1339 expOptimize(e
.e1
, result
, keepLvalue
);
1340 expOptimize(e
.e2
, result
, keepLvalue
);
1344 // Optimize the expression until it can no longer be simplified.
1348 if (b
++ == global
.recursionLimit
)
1350 error(e
.loc
, "infinite loop while optimizing expression");
1357 case EXP
.variable
: visitVar(ex
.isVarExp()); break;
1358 case EXP
.tuple
: visitTuple(ex
.isTupleExp()); break;
1359 case EXP
.arrayLiteral
: visitArrayLiteral(ex
.isArrayLiteralExp()); break;
1360 case EXP
.assocArrayLiteral
: visitAssocArrayLiteral(ex
.isAssocArrayLiteralExp()); break;
1361 case EXP
.structLiteral
: visitStructLiteral(ex
.isStructLiteralExp()); break;
1365 case EXP
.dotIdentifier
:
1366 case EXP
.dotTemplateDeclaration
:
1367 case EXP
.dotTemplateInstance
:
1373 case EXP
.vectorArray
:
1375 case EXP
.delegatePointer
:
1376 case EXP
.delegateFunctionPointer
:
1377 case EXP
.preMinusMinus
:
1378 case EXP
.prePlusPlus
: visitUna(cast(UnaExp
)ex
); break;
1380 case EXP
.negate
: visitNeg(ex
.isNegExp()); break;
1381 case EXP
.tilde
: visitCom(ex
.isComExp()); break;
1382 case EXP
.not: visitNop(ex
.isNotExp()); break;
1383 case EXP
.symbolOffset
: visitSymOff(ex
.isSymOffExp()); break;
1384 case EXP
.address
: visitAddr(ex
.isAddrExp()); break;
1385 case EXP
.star
: visitPtr(ex
.isPtrExp()); break;
1386 case EXP
.dotVariable
: visitDotVar(ex
.isDotVarExp()); break;
1387 case EXP
.new_
: visitNew(ex
.isNewExp()); break;
1388 case EXP
.call: visitCall(ex
.isCallExp()); break;
1389 case EXP
.cast_
: visitCast(ex
.isCastExp()); break;
1400 case EXP
.leftShiftAssign
:
1401 case EXP
.rightShiftAssign
:
1402 case EXP
.unsignedRightShiftAssign
:
1403 case EXP
.concatenateDcharAssign
: visitBinAssign(ex
.isBinAssignExp()); break;
1404 case EXP
.concatenateElemAssign
:
1405 case EXP
.concatenateAssign
: visitCatAssign(cast(CatAssignExp
) ex
); break;
1407 case EXP
.minusMinus
:
1414 case EXP
.dot
: visitBin(cast(BinExp
)ex
); break;
1416 case EXP
.add: visitAdd(ex
.isAddExp()); break;
1417 case EXP
.min
: visitMin(ex
.isMinExp()); break;
1418 case EXP
.mul: visitMul(ex
.isMulExp()); break;
1419 case EXP
.div: visitDiv(ex
.isDivExp()); break;
1420 case EXP
.mod
: visitMod(ex
.isModExp()); break;
1421 case EXP
.leftShift
: visitShl(ex
.isShlExp()); break;
1422 case EXP
.rightShift
: visitShr(ex
.isShrExp()); break;
1423 case EXP
.unsignedRightShift
: visitUshr(ex
.isUshrExp()); break;
1424 case EXP
.and: visitAnd(ex
.isAndExp()); break;
1425 case EXP
.or: visitOr(ex
.isOrExp()); break;
1426 case EXP
.xor: visitXor(ex
.isXorExp()); break;
1427 case EXP
.pow
: visitPow(ex
.isPowExp()); break;
1428 case EXP
.comma
: visitComma(ex
.isCommaExp()); break;
1429 case EXP
.arrayLength
: visitArrayLength(ex
.isArrayLengthExp()); break;
1431 case EXP
.equal
: visitEqual(ex
.isEqualExp()); break;
1432 case EXP
.notIdentity
:
1433 case EXP
.identity
: visitIdentity(ex
.isIdentityExp()); break;
1434 case EXP
.index
: visitIndex(ex
.isIndexExp()); break;
1435 case EXP
.slice
: visitSlice(ex
.isSliceExp()); break;
1437 case EXP
.orOr
: visitLogical(ex
.isLogicalExp()); break;
1439 case EXP
.lessOrEqual
:
1440 case EXP
.greaterThan
:
1441 case EXP
.greaterOrEqual
: visitCmp(cast(CmpExp
)ex
); break;
1442 case EXP
.concatenate
: visitCat(ex
.isCatExp()); break;
1443 case EXP
.question
: visitCond(ex
.isCondExp()); break;
1445 default: visitExp(ex
); break;