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