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