1 /**
2 * Perform constant folding.
3 *
4 * Copyright: Copyright (C) 1999-2022 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
10 */
11
12 module dmd.optimize;
13
14 import core.stdc.stdio;
15
16 import dmd.astenums;
17 import dmd.constfold;
18 import dmd.ctfeexpr;
19 import dmd.dclass;
20 import dmd.declaration;
21 import dmd.dsymbol;
22 import dmd.dsymbolsem;
23 import dmd.errors;
24 import dmd.expression;
25 import dmd.expressionsem;
26 import dmd.globals;
27 import dmd.init;
28 import dmd.mtype;
29 import dmd.printast;
30 import dmd.root.ctfloat;
31 import dmd.sideeffect;
32 import dmd.tokens;
33 import dmd.visitor;
34
35 /*************************************
36 * If variable has a const initializer,
37 * return that initializer.
38 * Returns:
39 * initializer if there is one,
40 * null if not,
41 * ErrorExp if error
42 */
expandVar(int result,VarDeclaration v)43 Expression expandVar(int result, VarDeclaration v)
44 {
45 //printf("expandVar(result = %d, v = %p, %s)\n", result, v, v ? v.toChars() : "null");
46
47 /********
48 * Params:
49 * e = initializer expression
50 */
51 Expression initializerReturn(Expression e)
52 {
53 if (e.type != v.type)
54 {
55 e = e.castTo(null, v.type);
56 }
57 v.inuse++;
58 e = e.optimize(result);
59 v.inuse--;
60 //if (e) printf("\te = %p, %s, e.type = %d, %s\n", e, e.toChars(), e.type.ty, e.type.toChars());
61 return e;
62 }
63
64 static Expression nullReturn()
65 {
66 return null;
67 }
68
69 static Expression errorReturn()
70 {
71 return ErrorExp.get();
72 }
73
74 if (!v)
75 return nullReturn();
76 if (!v.originalType && v.semanticRun < PASS.semanticdone) // semantic() not yet run
77 v.dsymbolSemantic(null);
78 if (v.type &&
79 (v.isConst() || v.isImmutable() || v.storage_class & STC.manifest))
80 {
81 Type tb = v.type.toBasetype();
82 if (v.storage_class & STC.manifest ||
83 tb.isscalar() ||
84 ((result & WANTexpand) && (tb.ty != Tsarray && tb.ty != Tstruct)))
85 {
86 if (v._init)
87 {
88 if (v.inuse)
89 {
90 if (v.storage_class & STC.manifest)
91 {
92 v.error("recursive initialization of constant");
93 return errorReturn();
94 }
95 return nullReturn();
96 }
97 Expression ei = v.getConstInitializer();
98 if (!ei)
99 {
100 if (v.storage_class & STC.manifest)
101 {
102 v.error("enum cannot be initialized with `%s`", v._init.toChars());
103 return errorReturn();
104 }
105 return nullReturn();
106 }
107 if (ei.op == EXP.construct || ei.op == EXP.blit)
108 {
109 AssignExp ae = cast(AssignExp)ei;
110 ei = ae.e2;
111 if (ei.isConst() == 1)
112 {
113 }
114 else if (ei.op == EXP.string_)
115 {
116 // https://issues.dlang.org/show_bug.cgi?id=14459
117 // Do not constfold the string literal
118 // if it's typed as a C string, because the value expansion
119 // will drop the pointer identity.
120 if (!(result & WANTexpand) && ei.type.toBasetype().ty == Tpointer)
121 return nullReturn();
122 }
123 else
124 return nullReturn();
125 if (ei.type == v.type)
126 {
127 // const variable initialized with const expression
128 }
129 else if (ei.implicitConvTo(v.type) >= MATCH.constant)
130 {
131 // const var initialized with non-const expression
132 ei = ei.implicitCastTo(null, v.type);
133 ei = ei.expressionSemantic(null);
134 }
135 else
136 return nullReturn();
137 }
138 else if (!(v.storage_class & STC.manifest) &&
139 ei.isConst() != 1 &&
140 ei.op != EXP.string_ &&
141 ei.op != EXP.address)
142 {
143 return nullReturn();
144 }
145
146 if (!ei.type)
147 {
148 return nullReturn();
149 }
150 else
151 {
152 // Should remove the copy() operation by
153 // making all mods to expressions copy-on-write
154 return initializerReturn(ei.copy());
155 }
156 }
157 else
158 {
159 // v does not have an initializer
160 version (all)
161 {
162 return nullReturn();
163 }
164 else
165 {
166 // BUG: what if const is initialized in constructor?
167 auto e = v.type.defaultInit();
168 e.loc = e1.loc;
169 return initializerReturn(e);
170 }
171 }
172 assert(0);
173 }
174 }
175 return nullReturn();
176 }
177
fromConstInitializer(int result,Expression e1)178 private Expression fromConstInitializer(int result, Expression e1)
179 {
180 //printf("fromConstInitializer(result = %x, %s)\n", result, e1.toChars());
181 //static int xx; if (xx++ == 10) assert(0);
182 Expression e = e1;
183 if (auto ve = e1.isVarExp())
184 {
185 VarDeclaration v = ve.var.isVarDeclaration();
186 e = expandVar(result, v);
187 if (e)
188 {
189 // If it is a comma expression involving a declaration, we mustn't
190 // perform a copy -- we'd get two declarations of the same variable.
191 // See bugzilla 4465.
192 if (e.op == EXP.comma && e.isCommaExp().e1.isDeclarationExp())
193 e = e1;
194 else if (e.type != e1.type && e1.type && e1.type.ty != Tident)
195 {
196 // Type 'paint' operation
197 e = e.copy();
198 e.type = e1.type;
199 }
200 e.loc = e1.loc;
201 }
202 else
203 {
204 e = e1;
205 }
206 }
207 return e;
208 }
209
210 /***
211 * It is possible for constant folding to change an array expression of
212 * unknown length, into one where the length is known.
213 * If the expression 'arr' is a literal, set lengthVar to be its length.
214 * Params:
215 * lengthVar = variable declaration for the `.length` property
216 * arr = String, ArrayLiteral, or of TypeSArray
217 */
setLengthVarIfKnown(VarDeclaration lengthVar,Expression arr)218 package void setLengthVarIfKnown(VarDeclaration lengthVar, Expression arr)
219 {
220 if (!lengthVar)
221 return;
222 if (lengthVar._init && !lengthVar._init.isVoidInitializer())
223 return; // we have previously calculated the length
224 dinteger_t len;
225 if (auto se = arr.isStringExp())
226 len = se.len;
227 else if (auto ale = arr.isArrayLiteralExp())
228 len = ale.elements.dim;
229 else
230 {
231 auto tsa = arr.type.toBasetype().isTypeSArray();
232 if (!tsa)
233 return; // we don't know the length yet
234 len = tsa.dim.toInteger();
235 }
236 Expression dollar = new IntegerExp(Loc.initial, len, Type.tsize_t);
237 lengthVar._init = new ExpInitializer(Loc.initial, dollar);
238 lengthVar.storage_class |= STC.static_ | STC.const_;
239 }
240
241 /***
242 * Same as above, but determines the length from 'type'.
243 * Params:
244 * lengthVar = variable declaration for the `.length` property
245 * type = TypeSArray
246 */
setLengthVarIfKnown(VarDeclaration lengthVar,Type type)247 package void setLengthVarIfKnown(VarDeclaration lengthVar, Type type)
248 {
249 if (!lengthVar)
250 return;
251 if (lengthVar._init && !lengthVar._init.isVoidInitializer())
252 return; // we have previously calculated the length
253 auto tsa = type.toBasetype().isTypeSArray();
254 if (!tsa)
255 return; // we don't know the length yet
256 const len = tsa.dim.toInteger();
257 Expression dollar = new IntegerExp(Loc.initial, len, Type.tsize_t);
258 lengthVar._init = new ExpInitializer(Loc.initial, dollar);
259 lengthVar.storage_class |= STC.static_ | STC.const_;
260 }
261
262 /*********************************
263 * Constant fold an Expression.
264 * Params:
265 * e = expression to const fold; this may get modified in-place
266 * result = WANTvalue, WANTexpand, or both
267 * keepLvalue = `e` is an lvalue, and keep it as an lvalue since it is
268 * an argument to a `ref` or `out` parameter, or the operand of `&` operator
269 * Returns:
270 * Constant folded version of `e`
271 */
Expression_optimize(Expression e,int result,bool keepLvalue)272 Expression Expression_optimize(Expression e, int result, bool keepLvalue)
273 {
274 //printf("Expression_optimize() e: %s result: %d keepLvalue %d\n", e.toChars(), result, keepLvalue);
275 Expression ret = e;
276
277 void error()
278 {
279 ret = ErrorExp.get();
280 }
281
282 /* Returns: true if error
283 */
284 bool expOptimize(ref Expression e, int flags, bool keepLvalue = false)
285 {
286 if (!e)
287 return false;
288 Expression ex = Expression_optimize(e, flags, keepLvalue);
289 if (ex.op == EXP.error)
290 {
291 ret = ex; // store error result
292 return true;
293 }
294 else
295 {
296 e = ex; // modify original
297 return false;
298 }
299 }
300
301 bool unaOptimize(UnaExp e, int flags)
302 {
303 return expOptimize(e.e1, flags);
304 }
305
306 bool binOptimize(BinExp e, int flags, bool keepLhsLvalue = false)
307 {
308 return expOptimize(e.e1, flags, keepLhsLvalue) |
309 expOptimize(e.e2, flags);
310 }
311
312 void visitExp(Expression e)
313 {
314 //printf("Expression::optimize(result = x%x) %s\n", result, e.toChars());
315 }
316
317 void visitVar(VarExp e)
318 {
319 VarDeclaration v = e.var.isVarDeclaration();
320
321 if (!(keepLvalue && v && !(v.storage_class & STC.manifest)))
322 ret = fromConstInitializer(result, e);
323
324 // if unoptimized, try to optimize the dtor expression
325 // (e.g., might be a LogicalExp with constant lhs)
326 if (ret == e && v && v.edtor)
327 {
328 // prevent infinite recursion (`<var>.~this()`)
329 if (!v.inuse)
330 {
331 v.inuse++;
332 expOptimize(v.edtor, WANTvalue);
333 v.inuse--;
334 }
335 }
336 }
337
338 void visitTuple(TupleExp e)
339 {
340 expOptimize(e.e0, WANTvalue);
341 foreach (ref ex; (*e.exps)[])
342 {
343 expOptimize(ex, WANTvalue);
344 }
345 }
346
347 void visitArrayLiteral(ArrayLiteralExp e)
348 {
349 if (e.elements)
350 {
351 expOptimize(e.basis, result & WANTexpand);
352 foreach (ref ex; (*e.elements)[])
353 {
354 expOptimize(ex, result & WANTexpand);
355 }
356 }
357 }
358
359 void visitAssocArrayLiteral(AssocArrayLiteralExp e)
360 {
361 assert(e.keys.dim == e.values.dim);
362 foreach (i, ref ekey; (*e.keys)[])
363 {
364 expOptimize(ekey, result & WANTexpand);
365 expOptimize((*e.values)[i], result & WANTexpand);
366 }
367 }
368
369 void visitStructLiteral(StructLiteralExp e)
370 {
371 if (e.stageflags & stageOptimize)
372 return;
373 int old = e.stageflags;
374 e.stageflags |= stageOptimize;
375 if (e.elements)
376 {
377 foreach (ref ex; (*e.elements)[])
378 {
379 expOptimize(ex, result & WANTexpand);
380 }
381 }
382 e.stageflags = old;
383 }
384
385 void visitUna(UnaExp e)
386 {
387 //printf("UnaExp::optimize() %s\n", e.toChars());
388 if (unaOptimize(e, result))
389 return;
390 }
391
392 void visitNeg(NegExp e)
393 {
394 if (unaOptimize(e, result))
395 return;
396 if (e.e1.isConst() == 1)
397 {
398 ret = Neg(e.type, e.e1).copy();
399 }
400 }
401
402 void visitCom(ComExp e)
403 {
404 if (unaOptimize(e, result))
405 return;
406 if (e.e1.isConst() == 1)
407 {
408 ret = Com(e.type, e.e1).copy();
409 }
410 }
411
412 void visitNop(NotExp e)
413 {
414 if (unaOptimize(e, result))
415 return;
416 if (e.e1.isConst() == 1)
417 {
418 ret = Not(e.type, e.e1).copy();
419 }
420 }
421
422 void visitSymOff(SymOffExp e)
423 {
424 assert(e.var);
425 }
426
427 void visitAddr(AddrExp e)
428 {
429 //printf("AddrExp::optimize(result = %d, keepLvalue = %d) %s\n", result, keepLvalue, e.toChars());
430 /* Rewrite &(a,b) as (a,&b)
431 */
432 if (auto ce = e.e1.isCommaExp())
433 {
434 auto ae = new AddrExp(e.loc, ce.e2, e.type);
435 ret = new CommaExp(ce.loc, ce.e1, ae);
436 ret.type = e.type;
437 return;
438 }
439 // Keep lvalue-ness
440 if (expOptimize(e.e1, result, true))
441 return; // error return
442
443 // Convert &*ex to ex
444 if (auto pe = e.e1.isPtrExp())
445 {
446 Expression ex = pe.e1;
447 if (e.type.equals(ex.type))
448 ret = ex;
449 else if (e.type.toBasetype().equivalent(ex.type.toBasetype()))
450 {
451 ret = ex.copy();
452 ret.type = e.type;
453 }
454 return;
455 }
456 if (auto ve = e.e1.isVarExp())
457 {
458 if (!ve.var.isReference() && !ve.var.isImportedSymbol())
459 {
460 ret = new SymOffExp(e.loc, ve.var, 0, ve.hasOverloads);
461 ret.type = e.type;
462 return;
463 }
464 }
465 if (e.e1.isDotVarExp())
466 {
467 /******************************
468 * Run down the left side of the a.b.c expression to determine the
469 * leftmost variable being addressed (`a`), and accumulate the offsets of the `.b` and `.c`.
470 * Params:
471 * e = the DotVarExp or VarExp
472 * var = set to the VarExp at the end, or null if doesn't end in VarExp
473 * eint = set to the IntegerExp at the end, or null if doesn't end in IntegerExp
474 * offset = accumulation of all the .var offsets encountered
475 * Returns: true on error
476 */
477 static bool getVarAndOffset(Expression e, out VarDeclaration var, out IntegerExp eint, ref uint offset)
478 {
479 if (e.type.size() == SIZE_INVALID) // trigger computation of v.offset
480 return true;
481
482 if (auto dve = e.isDotVarExp())
483 {
484 auto v = dve.var.isVarDeclaration();
485 if (!v || !v.isField() || v.isBitFieldDeclaration())
486 return false;
487
488 if (getVarAndOffset(dve.e1, var, eint, offset))
489 return true;
490 offset += v.offset;
491 }
492 else if (auto ve = e.isVarExp())
493 {
494 if (!ve.var.isReference() &&
495 !ve.var.isImportedSymbol() &&
496 ve.var.isDataseg() &&
497 ve.var.isCsymbol())
498 {
499 var = ve.var.isVarDeclaration();
500 }
501 }
502 else if (auto ep = e.isPtrExp())
503 {
504 if (auto ei = ep.e1.isIntegerExp())
505 {
506 eint = ei;
507 }
508 else if (auto se = ep.e1.isSymOffExp())
509 {
510 if (!se.var.isReference() &&
511 !se.var.isImportedSymbol() &&
512 se.var.isDataseg())
513 {
514 var = se.var.isVarDeclaration();
515 offset += se.offset;
516 }
517 }
518 }
519 else if (auto ei = e.isIndexExp())
520 {
521 if (auto ve = ei.e1.isVarExp())
522 {
523 if (!ve.var.isReference() &&
524 !ve.var.isImportedSymbol() &&
525 ve.var.isDataseg() &&
526 ve.var.isCsymbol())
527 {
528 if (auto ie = ei.e2.isIntegerExp())
529 {
530 var = ve.var.isVarDeclaration();
531 offset += ie.toInteger() * ve.type.toBasetype().nextOf().size();
532 }
533 }
534 }
535 }
536 return false;
537 }
538
539 uint offset;
540 VarDeclaration var;
541 IntegerExp eint;
542 if (getVarAndOffset(e.e1, var, eint, offset))
543 {
544 ret = ErrorExp.get();
545 return;
546 }
547 if (var)
548 {
549 ret = new SymOffExp(e.loc, var, offset, false);
550 ret.type = e.type;
551 return;
552 }
553 if (eint)
554 {
555 ret = new IntegerExp(e.loc, eint.toInteger() + offset, e.type);
556 return;
557 }
558 }
559 else if (auto ae = e.e1.isIndexExp())
560 {
561 // Convert &array[n] to &array+n
562 if (ae.e2.isIntegerExp() && ae.e1.isVarExp())
563 {
564 sinteger_t index = ae.e2.toInteger();
565 VarExp ve = ae.e1.isVarExp();
566 if (ve.type.isTypeSArray() && !ve.var.isImportedSymbol())
567 {
568 TypeSArray ts = ve.type.isTypeSArray();
569 sinteger_t dim = ts.dim.toInteger();
570 if (index < 0 || index >= dim)
571 {
572 /* 0 for C static arrays means size is unknown, no need to check,
573 * and address one past the end is OK, too
574 */
575 if (!((dim == 0 || dim == index) && ve.var.isCsymbol()))
576 {
577 e.error("array index %lld is out of bounds `[0..%lld]`", index, dim);
578 return error();
579 }
580 }
581
582 import core.checkedint : mulu;
583 bool overflow;
584 const offset = mulu(index, ts.nextOf().size(e.loc), overflow);
585 if (overflow)
586 {
587 e.error("array offset overflow");
588 return error();
589 }
590
591 ret = new SymOffExp(e.loc, ve.var, offset);
592 ret.type = e.type;
593 return;
594 }
595 }
596 // Convert &((a.b)[index]) to (&a.b)+index*elementsize
597 else if (ae.e2.isIntegerExp() && ae.e1.isDotVarExp())
598 {
599 sinteger_t index = ae.e2.toInteger();
600 DotVarExp ve = ae.e1.isDotVarExp();
601 if (ve.type.isTypeSArray() && ve.var.isField() && ve.e1.isPtrExp())
602 {
603 TypeSArray ts = ve.type.isTypeSArray();
604 sinteger_t dim = ts.dim.toInteger();
605 if (index < 0 || index >= dim)
606 {
607 /* 0 for C static arrays means size is unknown, no need to check,
608 * and address one past the end is OK, too
609 */
610 if (!((dim == 0 || dim == index) && ve.var.isCsymbol()))
611 {
612 e.error("array index %lld is out of bounds `[0..%lld]`", index, dim);
613 return error();
614 }
615 }
616
617 import core.checkedint : mulu;
618 bool overflow;
619 const offset = mulu(index, ts.nextOf().size(e.loc), overflow); // index*elementsize
620 if (overflow)
621 {
622 e.error("array offset overflow");
623 return error();
624 }
625
626 auto pe = new AddrExp(e.loc, ve);
627 pe.type = e.type;
628 ret = new AddExp(e.loc, pe, new IntegerExp(e.loc, offset, Type.tsize_t));
629 ret.type = e.type;
630 return;
631 }
632 }
633 }
634 }
635
636 void visitPtr(PtrExp e)
637 {
638 //printf("PtrExp::optimize(result = x%x) %s\n", result, e.toChars());
639 if (expOptimize(e.e1, result))
640 return;
641 // Convert *&ex to ex
642 // But only if there is no type punning involved
643 if (auto ey = e.e1.isAddrExp())
644 {
645 Expression ex = ey.e1;
646 if (e.type.equals(ex.type))
647 ret = ex;
648 else if (e.type.toBasetype().equivalent(ex.type.toBasetype()))
649 {
650 ret = ex.copy();
651 ret.type = e.type;
652 }
653 }
654 if (keepLvalue)
655 return;
656 // Constant fold *(&structliteral + offset)
657 if (e.e1.op == EXP.add)
658 {
659 Expression ex = Ptr(e.type, e.e1).copy();
660 if (!CTFEExp.isCantExp(ex))
661 {
662 ret = ex;
663 return;
664 }
665 }
666 if (auto se = e.e1.isSymOffExp())
667 {
668 VarDeclaration v = se.var.isVarDeclaration();
669 Expression ex = expandVar(result, v);
670 if (ex && ex.isStructLiteralExp())
671 {
672 StructLiteralExp sle = ex.isStructLiteralExp();
673 ex = sle.getField(e.type, cast(uint)se.offset);
674 if (ex && !CTFEExp.isCantExp(ex))
675 {
676 ret = ex;
677 return;
678 }
679 }
680 }
681 }
682
683 void visitDotVar(DotVarExp e)
684 {
685 //printf("DotVarExp::optimize(result = x%x) %s\n", result, e.toChars());
686 if (expOptimize(e.e1, result))
687 return;
688 if (keepLvalue)
689 return;
690 Expression ex = e.e1;
691 if (auto ve = ex.isVarExp())
692 {
693 VarDeclaration v = ve.var.isVarDeclaration();
694 ex = expandVar(result, v);
695 }
696 if (ex && ex.isStructLiteralExp())
697 {
698 StructLiteralExp sle = ex.isStructLiteralExp();
699 VarDeclaration vf = e.var.isVarDeclaration();
700 if (vf && !vf.overlapped)
701 {
702 /* https://issues.dlang.org/show_bug.cgi?id=13021
703 * Prevent optimization if vf has overlapped fields.
704 */
705 ex = sle.getField(e.type, vf.offset);
706 if (ex && !CTFEExp.isCantExp(ex))
707 {
708 ret = ex;
709 return;
710 }
711 }
712 }
713 }
714
715 void visitNew(NewExp e)
716 {
717 expOptimize(e.thisexp, WANTvalue);
718 // Optimize parameters
719 if (e.arguments)
720 {
721 foreach (ref arg; (*e.arguments)[])
722 {
723 expOptimize(arg, WANTvalue);
724 }
725 }
726 }
727
728 void visitCall(CallExp e)
729 {
730 //printf("CallExp::optimize(result = %d) %s\n", result, e.toChars());
731 // Optimize parameters with keeping lvalue-ness
732 if (expOptimize(e.e1, result))
733 return;
734 if (e.arguments)
735 {
736 Type t1 = e.e1.type.toBasetype();
737 if (auto td = t1.isTypeDelegate())
738 t1 = td.next;
739 // t1 can apparently be void for __ArrayDtor(T) calls
740 if (auto tf = t1.isTypeFunction())
741 {
742 foreach (i, ref arg; (*e.arguments)[])
743 {
744 Parameter p = tf.parameterList[i];
745 bool keep = p && p.isReference();
746 expOptimize(arg, WANTvalue, keep);
747 }
748 }
749 }
750 }
751
752 void visitCast(CastExp e)
753 {
754 //printf("CastExp::optimize(result = %d) %s\n", result, e.toChars());
755 //printf("from %s to %s\n", e.type.toChars(), e.to.toChars());
756 //printf("from %s\n", e.type.toChars());
757 //printf("e1.type %s\n", e.e1.type.toChars());
758 //printf("type = %p\n", e.type);
759 assert(e.type);
760 const op1 = e.e1.op;
761 Expression e1old = e.e1;
762 if (expOptimize(e.e1, result, keepLvalue))
763 return;
764 if (!keepLvalue)
765 e.e1 = fromConstInitializer(result, e.e1);
766 if (e.e1 == e1old && e.e1.op == EXP.arrayLiteral && e.type.toBasetype().ty == Tpointer && e.e1.type.toBasetype().ty != Tsarray)
767 {
768 // Casting this will result in the same expression, and
769 // infinite loop because of Expression::implicitCastTo()
770 return; // no change
771 }
772 if ((e.e1.op == EXP.string_ || e.e1.op == EXP.arrayLiteral) &&
773 (e.type.ty == Tpointer || e.type.ty == Tarray))
774 {
775 const esz = e.type.nextOf().size(e.loc);
776 const e1sz = e.e1.type.toBasetype().nextOf().size(e.e1.loc);
777 if (esz == SIZE_INVALID || e1sz == SIZE_INVALID)
778 return error();
779
780 if (e1sz == esz)
781 {
782 // https://issues.dlang.org/show_bug.cgi?id=12937
783 // If target type is void array, trying to paint
784 // e.e1 with that type will cause infinite recursive optimization.
785 if (e.type.nextOf().ty == Tvoid)
786 return;
787 ret = e.e1.castTo(null, e.type);
788 //printf(" returning1 %s\n", ret.toChars());
789 return;
790 }
791 }
792
793 // Returning e.e1 with changing its type
794 void returnE_e1()
795 {
796 ret = (e1old == e.e1 ? e.e1.copy() : e.e1);
797 ret.type = e.type;
798 }
799
800 if (e.e1.op == EXP.structLiteral && e.e1.type.implicitConvTo(e.type) >= MATCH.constant)
801 {
802 //printf(" returning2 %s\n", e.e1.toChars());
803 return returnE_e1();
804 }
805 /* The first test here is to prevent infinite loops
806 */
807 if (op1 != EXP.arrayLiteral && e.e1.op == EXP.arrayLiteral)
808 {
809 ret = e.e1.castTo(null, e.to);
810 return;
811 }
812 if (e.e1.op == EXP.null_ && (e.type.ty == Tpointer || e.type.ty == Tclass || e.type.ty == Tarray))
813 {
814 //printf(" returning3 %s\n", e.e1.toChars());
815 return returnE_e1();
816 }
817 if (e.type.ty == Tclass && e.e1.type.ty == Tclass)
818 {
819 import dmd.astenums : Sizeok;
820
821 // See if we can remove an unnecessary cast
822 ClassDeclaration cdfrom = e.e1.type.isClassHandle();
823 ClassDeclaration cdto = e.type.isClassHandle();
824 if (cdfrom.errors || cdto.errors)
825 return error();
826 if (cdto == ClassDeclaration.object && !cdfrom.isInterfaceDeclaration())
827 return returnE_e1(); // can always convert a class to Object
828 // Need to determine correct offset before optimizing away the cast.
829 // https://issues.dlang.org/show_bug.cgi?id=16980
830 cdfrom.size(e.loc);
831 assert(cdfrom.sizeok == Sizeok.done);
832 assert(cdto.sizeok == Sizeok.done || !cdto.isBaseOf(cdfrom, null));
833 int offset;
834 if (cdto.isBaseOf(cdfrom, &offset) && offset == 0)
835 {
836 //printf(" returning4 %s\n", e.e1.toChars());
837 return returnE_e1();
838 }
839 }
840 if (e.e1.type.mutableOf().unSharedOf().equals(e.to.mutableOf().unSharedOf()))
841 {
842 //printf(" returning5 %s\n", e.e1.toChars());
843 return returnE_e1();
844 }
845 if (e.e1.isConst())
846 {
847 if (e.e1.op == EXP.symbolOffset)
848 {
849 if (e.type.toBasetype().ty != Tsarray)
850 {
851 const esz = e.type.size(e.loc);
852 const e1sz = e.e1.type.size(e.e1.loc);
853 if (esz == SIZE_INVALID ||
854 e1sz == SIZE_INVALID)
855 return error();
856
857 if (esz == e1sz)
858 return returnE_e1();
859 }
860 return;
861 }
862 if (e.to.toBasetype().ty != Tvoid)
863 {
864 if (e.e1.type.equals(e.type) && e.type.equals(e.to))
865 ret = e.e1;
866 else
867 ret = Cast(e.loc, e.type, e.to, e.e1).copy();
868 }
869 }
870 //printf(" returning6 %s\n", ret.toChars());
871 }
872
873 void visitBinAssign(BinAssignExp e)
874 {
875 //printf("BinAssignExp::optimize(result = %d) %s\n", result, e.toChars());
876 if (binOptimize(e, result, /*keepLhsLvalue*/ true))
877 return;
878 if (e.op == EXP.leftShiftAssign || e.op == EXP.rightShiftAssign || e.op == EXP.unsignedRightShiftAssign)
879 {
880 if (e.e2.isConst() == 1)
881 {
882 sinteger_t i2 = e.e2.toInteger();
883 uinteger_t sz = e.e1.type.size(e.e1.loc);
884 assert(sz != SIZE_INVALID);
885 sz *= 8;
886 if (i2 < 0 || i2 >= sz)
887 {
888 e.error("shift assign by %lld is outside the range `0..%llu`", i2, cast(ulong)sz - 1);
889 return error();
890 }
891 }
892 }
893 }
894
895 void visitBin(BinExp e)
896 {
897 //printf("BinExp::optimize(result = %d) %s\n", result, e.toChars());
898 const keepLhsLvalue = e.op == EXP.construct || e.op == EXP.blit || e.op == EXP.assign
899 || e.op == EXP.plusPlus || e.op == EXP.minusMinus
900 || e.op == EXP.prePlusPlus || e.op == EXP.preMinusMinus;
901 binOptimize(e, result, keepLhsLvalue);
902 }
903
904 void visitAdd(AddExp e)
905 {
906 //printf("AddExp::optimize(%s)\n", e.toChars());
907 if (binOptimize(e, result))
908 return;
909 if (e.e1.isConst() && e.e2.isConst())
910 {
911 if (e.e1.op == EXP.symbolOffset && e.e2.op == EXP.symbolOffset)
912 return;
913 ret = Add(e.loc, e.type, e.e1, e.e2).copy();
914 }
915 }
916
917 void visitMin(MinExp e)
918 {
919 //printf("MinExp::optimize(%s)\n", e.toChars());
920 if (binOptimize(e, result))
921 return;
922 if (e.e1.isConst() && e.e2.isConst())
923 {
924 if (e.e2.op == EXP.symbolOffset)
925 return;
926 ret = Min(e.loc, e.type, e.e1, e.e2).copy();
927 }
928 }
929
930 void visitMul(MulExp e)
931 {
932 //printf("MulExp::optimize(result = %d) %s\n", result, e.toChars());
933 if (binOptimize(e, result))
934 return;
935 if (e.e1.isConst() == 1 && e.e2.isConst() == 1)
936 {
937 ret = Mul(e.loc, e.type, e.e1, e.e2).copy();
938 }
939 }
940
941 void visitDiv(DivExp e)
942 {
943 //printf("DivExp::optimize(%s)\n", e.toChars());
944 if (binOptimize(e, result))
945 return;
946 if (e.e1.isConst() == 1 && e.e2.isConst() == 1)
947 {
948 ret = Div(e.loc, e.type, e.e1, e.e2).copy();
949 }
950 }
951
952 void visitMod(ModExp e)
953 {
954 if (binOptimize(e, result))
955 return;
956 if (e.e1.isConst() == 1 && e.e2.isConst() == 1)
957 {
958 ret = Mod(e.loc, e.type, e.e1, e.e2).copy();
959 }
960 }
961
962 extern (D) void shift_optimize(BinExp e, UnionExp function(const ref Loc, Type, Expression, Expression) shift)
963 {
964 if (binOptimize(e, result))
965 return;
966 if (e.e2.isConst() == 1)
967 {
968 sinteger_t i2 = e.e2.toInteger();
969 uinteger_t sz = e.e1.type.size(e.e1.loc);
970 assert(sz != SIZE_INVALID);
971 sz *= 8;
972 if (i2 < 0 || i2 >= sz)
973 {
974 e.error("shift by %lld is outside the range `0..%llu`", i2, cast(ulong)sz - 1);
975 return error();
976 }
977 if (e.e1.isConst() == 1)
978 ret = (*shift)(e.loc, e.type, e.e1, e.e2).copy();
979 }
980 }
981
982 void visitShl(ShlExp e)
983 {
984 //printf("ShlExp::optimize(result = %d) %s\n", result, e.toChars());
985 shift_optimize(e, &Shl);
986 }
987
988 void visitShr(ShrExp e)
989 {
990 //printf("ShrExp::optimize(result = %d) %s\n", result, e.toChars());
991 shift_optimize(e, &Shr);
992 }
993
994 void visitUshr(UshrExp e)
995 {
996 //printf("UshrExp::optimize(result = %d) %s\n", result, toChars());
997 shift_optimize(e, &Ushr);
998 }
999
1000 void visitAnd(AndExp e)
1001 {
1002 if (binOptimize(e, result))
1003 return;
1004 if (e.e1.isConst() == 1 && e.e2.isConst() == 1)
1005 ret = And(e.loc, e.type, e.e1, e.e2).copy();
1006 }
1007
1008 void visitOr(OrExp e)
1009 {
1010 if (binOptimize(e, result))
1011 return;
1012 if (e.e1.isConst() == 1 && e.e2.isConst() == 1)
1013 ret = Or(e.loc, e.type, e.e1, e.e2).copy();
1014 }
1015
1016 void visitXor(XorExp e)
1017 {
1018 if (binOptimize(e, result))
1019 return;
1020 if (e.e1.isConst() == 1 && e.e2.isConst() == 1)
1021 ret = Xor(e.loc, e.type, e.e1, e.e2).copy();
1022 }
1023
1024 void visitPow(PowExp e)
1025 {
1026 if (binOptimize(e, result))
1027 return;
1028 // All negative integral powers are illegal.
1029 if (e.e1.type.isintegral() && (e.e2.op == EXP.int64) && cast(sinteger_t)e.e2.toInteger() < 0)
1030 {
1031 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());
1032 return error();
1033 }
1034 // If e2 *could* have been an integer, make it one.
1035 if (e.e2.op == EXP.float64 && e.e2.toReal() == real_t(cast(sinteger_t)e.e2.toReal()))
1036 {
1037 // This only applies to floating point, or positive integral powers.
1038 if (e.e1.type.isfloating() || cast(sinteger_t)e.e2.toInteger() >= 0)
1039 e.e2 = new IntegerExp(e.loc, e.e2.toInteger(), Type.tint64);
1040 }
1041 if (e.e1.isConst() == 1 && e.e2.isConst() == 1)
1042 {
1043 Expression ex = Pow(e.loc, e.type, e.e1, e.e2).copy();
1044 if (!CTFEExp.isCantExp(ex))
1045 {
1046 ret = ex;
1047 return;
1048 }
1049 }
1050 }
1051
1052 void visitComma(CommaExp e)
1053 {
1054 //printf("CommaExp::optimize(result = %d) %s\n", result, e.toChars());
1055 // Comma needs special treatment, because it may
1056 // contain compiler-generated declarations. We can interpret them, but
1057 // otherwise we must NOT attempt to constant-fold them.
1058 // In particular, if the comma returns a temporary variable, it needs
1059 // to be an lvalue (this is particularly important for struct constructors)
1060 expOptimize(e.e1, WANTvalue);
1061 expOptimize(e.e2, result, keepLvalue);
1062 if (ret.op == EXP.error)
1063 return;
1064 if (!e.e1 || e.e1.op == EXP.int64 || e.e1.op == EXP.float64 || !hasSideEffect(e.e1))
1065 {
1066 ret = e.e2;
1067 if (ret)
1068 ret.type = e.type;
1069 }
1070 //printf("-CommaExp::optimize(result = %d) %s\n", result, e.e.toChars());
1071 }
1072
1073 void visitArrayLength(ArrayLengthExp e)
1074 {
1075 //printf("ArrayLengthExp::optimize(result = %d) %s\n", result, e.toChars());
1076 if (unaOptimize(e, WANTexpand))
1077 return;
1078 // CTFE interpret static immutable arrays (to get better diagnostics)
1079 if (auto ve = e.e1.isVarExp())
1080 {
1081 VarDeclaration v = ve.var.isVarDeclaration();
1082 if (v && (v.storage_class & STC.static_) && (v.storage_class & STC.immutable_) && v._init)
1083 {
1084 if (Expression ci = v.getConstInitializer())
1085 e.e1 = ci;
1086 }
1087 }
1088 if (e.e1.op == EXP.string_ || e.e1.op == EXP.arrayLiteral || e.e1.op == EXP.assocArrayLiteral || e.e1.type.toBasetype().ty == Tsarray)
1089 {
1090 ret = ArrayLength(e.type, e.e1).copy();
1091 }
1092 }
1093
1094 void visitEqual(EqualExp e)
1095 {
1096 //printf("EqualExp::optimize(result = %x) %s\n", result, e.toChars());
1097 if (binOptimize(e, WANTvalue))
1098 return;
1099 Expression e1 = fromConstInitializer(result, e.e1);
1100 Expression e2 = fromConstInitializer(result, e.e2);
1101 if (e1.op == EXP.error)
1102 {
1103 ret = e1;
1104 return;
1105 }
1106 if (e2.op == EXP.error)
1107 {
1108 ret = e2;
1109 return;
1110 }
1111 ret = Equal(e.op, e.loc, e.type, e1, e2).copy();
1112 if (CTFEExp.isCantExp(ret))
1113 ret = e;
1114 }
1115
1116 void visitIdentity(IdentityExp e)
1117 {
1118 //printf("IdentityExp::optimize(result = %d) %s\n", result, e.toChars());
1119 if (binOptimize(e, WANTvalue))
1120 return;
1121 if ((e.e1.isConst() && e.e2.isConst()) || (e.e1.op == EXP.null_ && e.e2.op == EXP.null_))
1122 {
1123 ret = Identity(e.op, e.loc, e.type, e.e1, e.e2).copy();
1124 if (CTFEExp.isCantExp(ret))
1125 ret = e;
1126 }
1127 }
1128
1129 void visitIndex(IndexExp e)
1130 {
1131 //printf("IndexExp::optimize(result = %d) %s\n", result, e.toChars());
1132 if (expOptimize(e.e1, result & WANTexpand))
1133 return;
1134 Expression ex = fromConstInitializer(result, e.e1);
1135 // We might know $ now
1136 setLengthVarIfKnown(e.lengthVar, ex);
1137 if (expOptimize(e.e2, WANTvalue))
1138 return;
1139 // Don't optimize to an array literal element directly in case an lvalue is requested
1140 if (keepLvalue && ex.op == EXP.arrayLiteral)
1141 return;
1142 ret = Index(e.type, ex, e.e2, e.indexIsInBounds).copy();
1143 if (CTFEExp.isCantExp(ret) || (!ret.isErrorExp() && keepLvalue && !ret.isLvalue()))
1144 ret = e;
1145 }
1146
1147 void visitSlice(SliceExp e)
1148 {
1149 //printf("SliceExp::optimize(result = %d) %s\n", result, e.toChars());
1150 if (expOptimize(e.e1, result & WANTexpand))
1151 return;
1152 if (!e.lwr)
1153 {
1154 if (e.e1.op == EXP.string_)
1155 {
1156 // Convert slice of string literal into dynamic array
1157 Type t = e.e1.type.toBasetype();
1158 if (Type tn = t.nextOf())
1159 ret = e.e1.castTo(null, tn.arrayOf());
1160 }
1161 }
1162 else
1163 {
1164 e.e1 = fromConstInitializer(result, e.e1);
1165 // We might know $ now
1166 setLengthVarIfKnown(e.lengthVar, e.e1);
1167 expOptimize(e.lwr, WANTvalue);
1168 expOptimize(e.upr, WANTvalue);
1169 if (ret.op == EXP.error)
1170 return;
1171 ret = Slice(e.type, e.e1, e.lwr, e.upr).copy();
1172 if (CTFEExp.isCantExp(ret))
1173 ret = e;
1174 }
1175 // https://issues.dlang.org/show_bug.cgi?id=14649
1176 // Leave the slice form so it might be
1177 // a part of array operation.
1178 // Assume that the backend codegen will handle the form `e[]`
1179 // as an equal to `e` itself.
1180 if (ret.op == EXP.string_)
1181 {
1182 e.e1 = ret;
1183 e.lwr = null;
1184 e.upr = null;
1185 ret = e;
1186 }
1187 //printf("-SliceExp::optimize() %s\n", ret.toChars());
1188 }
1189
1190 void visitLogical(LogicalExp e)
1191 {
1192 //printf("LogicalExp::optimize(%d) %s\n", result, e.toChars());
1193 if (expOptimize(e.e1, WANTvalue))
1194 return;
1195 const oror = e.op == EXP.orOr;
1196 if (e.e1.toBool().hasValue(oror))
1197 {
1198 // Replace with (e1, oror)
1199 ret = IntegerExp.createBool(oror);
1200 ret = Expression.combine(e.e1, ret);
1201 if (e.type.toBasetype().ty == Tvoid)
1202 {
1203 ret = new CastExp(e.loc, ret, Type.tvoid);
1204 ret.type = e.type;
1205 }
1206 ret = Expression_optimize(ret, result, false);
1207 return;
1208 }
1209 expOptimize(e.e2, WANTvalue);
1210 if (e.e1.isConst())
1211 {
1212 const e1Opt = e.e1.toBool();
1213 if (e.e2.isConst())
1214 {
1215 bool n1 = e1Opt.get();
1216 bool n2 = e.e2.toBool().get();
1217 ret = new IntegerExp(e.loc, oror ? (n1 || n2) : (n1 && n2), e.type);
1218 }
1219 else if (e1Opt.hasValue(!oror))
1220 {
1221 if (e.type.toBasetype().ty == Tvoid)
1222 ret = e.e2;
1223 else
1224 {
1225 ret = new CastExp(e.loc, e.e2, e.type);
1226 ret.type = e.type;
1227 }
1228 }
1229 }
1230 }
1231
1232 void visitCmp(CmpExp e)
1233 {
1234 //printf("CmpExp::optimize() %s\n", e.toChars());
1235 if (binOptimize(e, WANTvalue))
1236 return;
1237 Expression e1 = fromConstInitializer(result, e.e1);
1238 Expression e2 = fromConstInitializer(result, e.e2);
1239 ret = Cmp(e.op, e.loc, e.type, e1, e2).copy();
1240 if (CTFEExp.isCantExp(ret))
1241 ret = e;
1242 }
1243
1244 void visitCat(CatExp e)
1245 {
1246 //printf("CatExp::optimize(%d) %s\n", result, e.toChars());
1247 if (binOptimize(e, result))
1248 return;
1249 if (auto ce1 = e.e1.isCatExp())
1250 {
1251 // https://issues.dlang.org/show_bug.cgi?id=12798
1252 // optimize ((expr ~ str1) ~ str2)
1253 scope CatExp cex = new CatExp(e.loc, ce1.e2, e.e2);
1254 cex.type = e.type;
1255 Expression ex = Expression_optimize(cex, result, false);
1256 if (ex != cex)
1257 {
1258 e.e1 = ce1.e1;
1259 e.e2 = ex;
1260 }
1261 }
1262 // optimize "str"[] -> "str"
1263 if (auto se1 = e.e1.isSliceExp())
1264 {
1265 if (se1.e1.op == EXP.string_ && !se1.lwr)
1266 e.e1 = se1.e1;
1267 }
1268 if (auto se2 = e.e2.isSliceExp())
1269 {
1270 if (se2.e1.op == EXP.string_ && !se2.lwr)
1271 e.e2 = se2.e1;
1272 }
1273 ret = Cat(e.loc, e.type, e.e1, e.e2).copy();
1274 if (CTFEExp.isCantExp(ret))
1275 ret = e;
1276 }
1277
1278 void visitCond(CondExp e)
1279 {
1280 if (expOptimize(e.econd, WANTvalue))
1281 return;
1282 const opt = e.econd.toBool();
1283 if (opt.hasValue(true))
1284 ret = Expression_optimize(e.e1, result, keepLvalue);
1285 else if (opt.hasValue(false))
1286 ret = Expression_optimize(e.e2, result, keepLvalue);
1287 else
1288 {
1289 expOptimize(e.e1, result, keepLvalue);
1290 expOptimize(e.e2, result, keepLvalue);
1291 }
1292 }
1293
1294 // Optimize the expression until it can no longer be simplified.
1295 size_t b;
1296 while (1)
1297 {
1298 if (b++ == global.recursionLimit)
1299 {
1300 e.error("infinite loop while optimizing expression");
1301 fatal();
1302 }
1303
1304 auto ex = ret;
1305 switch (ex.op)
1306 {
1307 case EXP.variable: visitVar(ex.isVarExp()); break;
1308 case EXP.tuple: visitTuple(ex.isTupleExp()); break;
1309 case EXP.arrayLiteral: visitArrayLiteral(ex.isArrayLiteralExp()); break;
1310 case EXP.assocArrayLiteral: visitAssocArrayLiteral(ex.isAssocArrayLiteralExp()); break;
1311 case EXP.structLiteral: visitStructLiteral(ex.isStructLiteralExp()); break;
1312
1313 case EXP.import_:
1314 case EXP.assert_:
1315 case EXP.dotIdentifier:
1316 case EXP.dotTemplateDeclaration:
1317 case EXP.dotTemplateInstance:
1318 case EXP.delegate_:
1319 case EXP.dotType:
1320 case EXP.uadd:
1321 case EXP.delete_:
1322 case EXP.vector:
1323 case EXP.vectorArray:
1324 case EXP.array:
1325 case EXP.delegatePointer:
1326 case EXP.delegateFunctionPointer:
1327 case EXP.preMinusMinus:
1328 case EXP.prePlusPlus: visitUna(cast(UnaExp)ex); break;
1329
1330 case EXP.negate: visitNeg(ex.isNegExp()); break;
1331 case EXP.tilde: visitCom(ex.isComExp()); break;
1332 case EXP.not: visitNop(ex.isNotExp()); break;
1333 case EXP.symbolOffset: visitSymOff(ex.isSymOffExp()); break;
1334 case EXP.address: visitAddr(ex.isAddrExp()); break;
1335 case EXP.star: visitPtr(ex.isPtrExp()); break;
1336 case EXP.dotVariable: visitDotVar(ex.isDotVarExp()); break;
1337 case EXP.new_: visitNew(ex.isNewExp()); break;
1338 case EXP.call: visitCall(ex.isCallExp()); break;
1339 case EXP.cast_: visitCast(ex.isCastExp()); break;
1340
1341 case EXP.addAssign:
1342 case EXP.minAssign:
1343 case EXP.mulAssign:
1344 case EXP.divAssign:
1345 case EXP.modAssign:
1346 case EXP.andAssign:
1347 case EXP.orAssign:
1348 case EXP.xorAssign:
1349 case EXP.powAssign:
1350 case EXP.leftShiftAssign:
1351 case EXP.rightShiftAssign:
1352 case EXP.unsignedRightShiftAssign:
1353 case EXP.concatenateElemAssign:
1354 case EXP.concatenateDcharAssign:
1355 case EXP.concatenateAssign: visitBinAssign(ex.isBinAssignExp()); break;
1356
1357 case EXP.minusMinus:
1358 case EXP.plusPlus:
1359 case EXP.assign:
1360 case EXP.construct:
1361 case EXP.blit:
1362 case EXP.in_:
1363 case EXP.remove:
1364 case EXP.dot: visitBin(cast(BinExp)ex); break;
1365
1366 case EXP.add: visitAdd(ex.isAddExp()); break;
1367 case EXP.min: visitMin(ex.isMinExp()); break;
1368 case EXP.mul: visitMul(ex.isMulExp()); break;
1369 case EXP.div: visitDiv(ex.isDivExp()); break;
1370 case EXP.mod: visitMod(ex.isModExp()); break;
1371 case EXP.leftShift: visitShl(ex.isShlExp()); break;
1372 case EXP.rightShift: visitShr(ex.isShrExp()); break;
1373 case EXP.unsignedRightShift: visitUshr(ex.isUshrExp()); break;
1374 case EXP.and: visitAnd(ex.isAndExp()); break;
1375 case EXP.or: visitOr(ex.isOrExp()); break;
1376 case EXP.xor: visitXor(ex.isXorExp()); break;
1377 case EXP.pow: visitPow(ex.isPowExp()); break;
1378 case EXP.comma: visitComma(ex.isCommaExp()); break;
1379 case EXP.arrayLength: visitArrayLength(ex.isArrayLengthExp()); break;
1380 case EXP.notEqual:
1381 case EXP.equal: visitEqual(ex.isEqualExp()); break;
1382 case EXP.notIdentity:
1383 case EXP.identity: visitIdentity(ex.isIdentityExp()); break;
1384 case EXP.index: visitIndex(ex.isIndexExp()); break;
1385 case EXP.slice: visitSlice(ex.isSliceExp()); break;
1386 case EXP.andAnd:
1387 case EXP.orOr: visitLogical(ex.isLogicalExp()); break;
1388 case EXP.lessThan:
1389 case EXP.lessOrEqual:
1390 case EXP.greaterThan:
1391 case EXP.greaterOrEqual: visitCmp(cast(CmpExp)ex); break;
1392 case EXP.concatenate: visitCat(ex.isCatExp()); break;
1393 case EXP.question: visitCond(ex.isCondExp()); break;
1394
1395 default: visitExp(ex); break;
1396 }
1397
1398 if (ex == ret)
1399 break;
1400 }
1401 return ret;
1402 }
1403