blob: 44dedd84038a9829b1c69db9e1d7d93a65d6b4c3 [file] [log] [blame]
/* Compiler implementation of the D programming language
* Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
* written by Walter Bright
* http://www.digitalmars.com
* Distributed under the Boost Software License, Version 1.0
* http://www.boost.org/LICENSE_1_0.txt
* https://github.com/D-Programming-Language/dmd/blob/master/src/optimize.c
*/
#include "root/dsystem.h"
#include "root/checkedint.h"
#include "lexer.h"
#include "mtype.h"
#include "expression.h"
#include "declaration.h"
#include "aggregate.h"
#include "init.h"
#include "enum.h"
#include "ctfe.h"
#include "errors.h"
/*************************************
* If variable has a const initializer,
* return that initializer.
*/
Expression *expandVar(int result, VarDeclaration *v)
{
//printf("expandVar(result = %d, v = %p, %s)\n", result, v, v ? v->toChars() : "null");
Expression *e = NULL;
if (!v)
return e;
if (!v->originalType && v->semanticRun < PASSsemanticdone) // semantic() not yet run
dsymbolSemantic(v, NULL);
if (v->isConst() || v->isImmutable() || v->storage_class & STCmanifest)
{
if (!v->type)
{
return e;
}
Type *tb = v->type->toBasetype();
if (v->storage_class & STCmanifest ||
v->type->toBasetype()->isscalar() ||
((result & WANTexpand) && (tb->ty != Tsarray && tb->ty != Tstruct))
)
{
if (v->_init)
{
if (v->inuse)
{
if (v->storage_class & STCmanifest)
{
v->error("recursive initialization of constant");
goto Lerror;
}
goto L1;
}
Expression *ei = v->getConstInitializer();
if (!ei)
{
if (v->storage_class & STCmanifest)
{
v->error("enum cannot be initialized with %s", v->_init->toChars());
goto Lerror;
}
goto L1;
}
if (ei->op == TOKconstruct || ei->op == TOKblit)
{
AssignExp *ae = (AssignExp *)ei;
ei = ae->e2;
if (ei->isConst() == 1)
{
}
else if (ei->op == TOKstring)
{
// Bugzilla 14459: We should not constfold the string literal
// if it's typed as a C string, because the value expansion
// will drop the pointer identity.
if (!(result & WANTexpand) && ei->type->toBasetype()->ty == Tpointer)
goto L1;
}
else
goto L1;
if (ei->type == v->type)
{
// const variable initialized with const expression
}
else if (ei->implicitConvTo(v->type) >= MATCHconst)
{
// const var initialized with non-const expression
ei = ei->implicitCastTo(NULL, v->type);
ei = expressionSemantic(ei, NULL);
}
else
goto L1;
}
else if (!(v->storage_class & STCmanifest) &&
ei->isConst() != 1 && ei->op != TOKstring &&
ei->op != TOKaddress)
{
goto L1;
}
if (!ei->type)
{
goto L1;
}
else
{
// Should remove the copy() operation by
// making all mods to expressions copy-on-write
e = ei->copy();
}
}
else
{
goto L1;
}
if (e->type != v->type)
{
e = e->castTo(NULL, v->type);
}
v->inuse++;
e = e->optimize(result);
v->inuse--;
}
}
L1:
//if (e) printf("\te = %p, %s, e->type = %d, %s\n", e, e->toChars(), e->type->ty, e->type->toChars());
return e;
Lerror:
return new ErrorExp();
}
Expression *fromConstInitializer(int result, Expression *e1)
{
//printf("fromConstInitializer(result = %x, %s)\n", result, e1->toChars());
//static int xx; if (xx++ == 10) assert(0);
Expression *e = e1;
if (e1->op == TOKvar)
{
VarExp *ve = (VarExp *)e1;
VarDeclaration *v = ve->var->isVarDeclaration();
e = expandVar(result, v);
if (e)
{
// If it is a comma expression involving a declaration, we mustn't
// perform a copy -- we'd get two declarations of the same variable.
// See bugzilla 4465.
if (e->op == TOKcomma && ((CommaExp *)e)->e1->op == TOKdeclaration)
e = e1;
else
if (e->type != e1->type && e1->type && e1->type->ty != Tident)
{
// Type 'paint' operation
e = e->copy();
e->type = e1->type;
}
e->loc = e1->loc;
}
else
{
e = e1;
}
}
return e;
}
Expression *Expression_optimize(Expression *e, int result, bool keepLvalue)
{
class OptimizeVisitor : public Visitor
{
public:
int result;
bool keepLvalue;
Expression *ret;
OptimizeVisitor(int result, bool keepLvalue)
: result(result), keepLvalue(keepLvalue)
{
}
void error()
{
ret = new ErrorExp();
}
bool expOptimize(Expression *&e, int flags, bool keepLvalue = false)
{
if (!e)
return false;
Expression *ex = e->optimize(flags, keepLvalue);
if (ex->op == TOKerror)
{
ret = ex; // store error result
return true;
}
else
{
e = ex; // modify original
return false;
}
}
bool unaOptimize(UnaExp *e, int flags)
{
return expOptimize(e->e1, flags);
}
bool binOptimize(BinExp *e, int flags)
{
expOptimize(e->e1, flags);
expOptimize(e->e2, flags);
return ret->op == TOKerror;
}
void visit(Expression *)
{
//printf("Expression::optimize(result = x%x) %s\n", result, e->toChars());
}
void visit(VarExp *e)
{
if (keepLvalue)
{
VarDeclaration *v = e->var->isVarDeclaration();
if (v && !(v->storage_class & STCmanifest))
return;
}
ret = fromConstInitializer(result, e);
}
void visit(TupleExp *e)
{
expOptimize(e->e0, WANTvalue);
for (size_t i = 0; i < e->exps->length; i++)
{
expOptimize((*e->exps)[i], WANTvalue);
}
}
void visit(ArrayLiteralExp *e)
{
if (e->elements)
{
expOptimize(e->basis, result & WANTexpand);
for (size_t i = 0; i < e->elements->length; i++)
{
expOptimize((*e->elements)[i], result & WANTexpand);
}
}
}
void visit(AssocArrayLiteralExp *e)
{
assert(e->keys->length == e->values->length);
for (size_t i = 0; i < e->keys->length; i++)
{
expOptimize((*e->keys)[i], result & WANTexpand);
expOptimize((*e->values)[i], result & WANTexpand);
}
}
void visit(StructLiteralExp *e)
{
if (e->stageflags & stageOptimize) return;
int old = e->stageflags;
e->stageflags |= stageOptimize;
if (e->elements)
{
for (size_t i = 0; i < e->elements->length; i++)
{
expOptimize((*e->elements)[i], result & WANTexpand);
}
}
e->stageflags = old;
}
void visit(UnaExp *e)
{
//printf("UnaExp::optimize() %s\n", e->toChars());
if (unaOptimize(e, result))
return;
}
void visit(NegExp *e)
{
if (unaOptimize(e, result))
return;
if (e->e1->isConst() == 1)
{
ret = Neg(e->type, e->e1).copy();
}
}
void visit(ComExp *e)
{
if (unaOptimize(e, result))
return;
if (e->e1->isConst() == 1)
{
ret = Com(e->type, e->e1).copy();
}
}
void visit(NotExp *e)
{
if (unaOptimize(e, result))
return;
if (e->e1->isConst() == 1)
{
ret = Not(e->type, e->e1).copy();
}
}
void visit(SymOffExp *e)
{
assert(e->var);
}
void visit(AddrExp *e)
{
//printf("AddrExp::optimize(result = %d) %s\n", result, e->toChars());
/* Rewrite &(a,b) as (a,&b)
*/
if (e->e1->op == TOKcomma)
{
CommaExp *ce = (CommaExp *)e->e1;
AddrExp *ae = new AddrExp(e->loc, ce->e2, e->type);
ret = new CommaExp(ce->loc, ce->e1, ae);
ret->type = e->type;
return;
}
// Keep lvalue-ness
if (expOptimize(e->e1, result, true))
return;
// Convert &*ex to ex
if (e->e1->op == TOKstar)
{
Expression *ex = ((PtrExp *)e->e1)->e1;
if (e->type->equals(ex->type))
ret = ex;
else if (e->type->toBasetype()->equivalent(ex->type->toBasetype()))
{
ret = ex->copy();
ret->type = e->type;
}
return;
}
if (e->e1->op == TOKvar)
{
VarExp *ve = (VarExp *)e->e1;
if (!ve->var->isOut() && !ve->var->isRef() &&
!ve->var->isImportedSymbol())
{
ret = new SymOffExp(e->loc, ve->var, 0, ve->hasOverloads);
ret->type = e->type;
return;
}
}
if (e->e1->op == TOKindex)
{
// Convert &array[n] to &array+n
IndexExp *ae = (IndexExp *)e->e1;
if (ae->e2->op == TOKint64 && ae->e1->op == TOKvar)
{
sinteger_t index = ae->e2->toInteger();
VarExp *ve = (VarExp *)ae->e1;
if (ve->type->ty == Tsarray
&& !ve->var->isImportedSymbol())
{
TypeSArray *ts = (TypeSArray *)ve->type;
sinteger_t dim = ts->dim->toInteger();
if (index < 0 || index >= dim)
{
e->error("array index %lld is out of bounds [0..%lld]", index, dim);
return error();
}
bool overflow = false;
const d_uns64 offset = mulu(index, ts->nextOf()->size(e->loc), overflow);
if (overflow)
{
e->error("array offset overflow");
return error();
}
ret = new SymOffExp(e->loc, ve->var, offset);
ret->type = e->type;
return;
}
}
}
}
void visit(PtrExp *e)
{
//printf("PtrExp::optimize(result = x%x) %s\n", result, e->toChars());
if (expOptimize(e->e1, result))
return;
// Convert *&ex to ex
// But only if there is no type punning involved
if (e->e1->op == TOKaddress)
{
Expression *ex = ((AddrExp *)e->e1)->e1;
if (e->type->equals(ex->type))
ret = ex;
else if (e->type->toBasetype()->equivalent(ex->type->toBasetype()))
{
ret = ex->copy();
ret->type = e->type;
}
}
if (keepLvalue)
return;
// Constant fold *(&structliteral + offset)
if (e->e1->op == TOKadd)
{
Expression *ex = Ptr(e->type, e->e1).copy();
if (!CTFEExp::isCantExp(ex))
{
ret = ex;
return;
}
}
if (e->e1->op == TOKsymoff)
{
SymOffExp *se = (SymOffExp *)e->e1;
VarDeclaration *v = se->var->isVarDeclaration();
Expression *ex = expandVar(result, v);
if (ex && ex->op == TOKstructliteral)
{
StructLiteralExp *sle = (StructLiteralExp *)ex;
ex = sle->getField(e->type, (unsigned)se->offset);
if (ex && !CTFEExp::isCantExp(ex))
{
ret = ex;
return;
}
}
}
}
void visit(DotVarExp *e)
{
//printf("DotVarExp::optimize(result = x%x) %s\n", result, e->toChars());
if (expOptimize(e->e1, result))
return;
if (keepLvalue)
return;
Expression *ex = e->e1;
if (ex->op == TOKvar)
{
VarExp *ve = (VarExp *)ex;
VarDeclaration *v = ve->var->isVarDeclaration();
ex = expandVar(result, v);
}
if (ex && ex->op == TOKstructliteral)
{
StructLiteralExp *sle = (StructLiteralExp *)ex;
VarDeclaration *vf = e->var->isVarDeclaration();
if (vf && !vf->overlapped)
{
/* Bugzilla 13021: Prevent optimization if vf has overlapped fields.
*/
ex = sle->getField(e->type, vf->offset);
if (ex && !CTFEExp::isCantExp(ex))
{
ret = ex;
return;
}
}
}
}
void visit(NewExp *e)
{
expOptimize(e->thisexp, WANTvalue);
// Optimize parameters
if (e->newargs)
{
for (size_t i = 0; i < e->newargs->length; i++)
{
expOptimize((*e->newargs)[i], WANTvalue);
}
}
if (e->arguments)
{
for (size_t i = 0; i < e->arguments->length; i++)
{
expOptimize((*e->arguments)[i], WANTvalue);
}
}
}
void visit(CallExp *e)
{
//printf("CallExp::optimize(result = %d) %s\n", result, e->toChars());
// Optimize parameters with keeping lvalue-ness
if (expOptimize(e->e1, result))
return;
if (e->arguments)
{
Type *t1 = e->e1->type->toBasetype();
if (t1->ty == Tdelegate) t1 = t1->nextOf();
assert(t1->ty == Tfunction);
TypeFunction *tf = (TypeFunction *)t1;
for (size_t i = 0; i < e->arguments->length; i++)
{
Parameter *p = tf->parameterList[i];
bool keep = p && (p->storageClass & (STCref | STCout)) != 0;
expOptimize((*e->arguments)[i], WANTvalue, keep);
}
}
}
void visit(CastExp *e)
{
//printf("CastExp::optimize(result = %d) %s\n", result, e->toChars());
//printf("from %s to %s\n", e->type->toChars(), e->to->toChars());
//printf("from %s\n", e->type->toChars());
//printf("e1->type %s\n", e->e1->type->toChars());
//printf("type = %p\n", e->type);
assert(e->type);
TOK op1 = e->e1->op;
Expression *e1old = e->e1;
if (expOptimize(e->e1, result))
return;
e->e1 = fromConstInitializer(result, e->e1);
if (e->e1 == e1old &&
e->e1->op == TOKarrayliteral &&
e->type->toBasetype()->ty == Tpointer &&
e->e1->type->toBasetype()->ty != Tsarray)
{
// Casting this will result in the same expression, and
// infinite loop because of Expression::implicitCastTo()
return; // no change
}
if ((e->e1->op == TOKstring || e->e1->op == TOKarrayliteral) &&
(e->type->ty == Tpointer || e->type->ty == Tarray))
{
const d_uns64 esz = e->type->nextOf()->size(e->loc);
const d_uns64 e1sz = e->e1->type->toBasetype()->nextOf()->size(e->e1->loc);
if (esz == SIZE_INVALID || e1sz == SIZE_INVALID)
return error();
if (e1sz == esz)
{
// Bugzilla 12937: If target type is void array, trying to paint
// e->e1 with that type will cause infinite recursive optimization.
if (e->type->nextOf()->ty == Tvoid)
return;
ret = e->e1->castTo(NULL, e->type);
//printf(" returning1 %s\n", ret->toChars());
return;
}
}
if (e->e1->op == TOKstructliteral &&
e->e1->type->implicitConvTo(e->type) >= MATCHconst)
{
//printf(" returning2 %s\n", e->e1->toChars());
L1: // Returning e1 with changing its type
ret = (e1old == e->e1 ? e->e1->copy() : e->e1);
ret->type = e->type;
return;
}
/* The first test here is to prevent infinite loops
*/
if (op1 != TOKarrayliteral && e->e1->op == TOKarrayliteral)
{
ret = e->e1->castTo(NULL, e->to);
return;
}
if (e->e1->op == TOKnull &&
(e->type->ty == Tpointer || e->type->ty == Tclass || e->type->ty == Tarray))
{
//printf(" returning3 %s\n", e->e1->toChars());
goto L1;
}
if (e->type->ty == Tclass && e->e1->type->ty == Tclass)
{
// See if we can remove an unnecessary cast
ClassDeclaration *cdfrom = e->e1->type->isClassHandle();
ClassDeclaration *cdto = e->type->isClassHandle();
if (cdto == ClassDeclaration::object && !cdfrom->isInterfaceDeclaration())
goto L1; // can always convert a class to Object
// Need to determine correct offset before optimizing away the cast.
// https://issues.dlang.org/show_bug.cgi?id=16980
cdfrom->size(e->loc);
assert(cdfrom->sizeok == SIZEOKdone);
assert(cdto->sizeok == SIZEOKdone || !cdto->isBaseOf(cdfrom, NULL));
int offset;
if (cdto->isBaseOf(cdfrom, &offset) && offset == 0)
{
//printf(" returning4 %s\n", e->e1->toChars());
goto L1;
}
}
// We can convert 'head const' to mutable
if (e->to->mutableOf()->constOf()->equals(e->e1->type->mutableOf()->constOf()))
{
//printf(" returning5 %s\n", e->e1->toChars());
goto L1;
}
if (e->e1->isConst())
{
if (e->e1->op == TOKsymoff)
{
if (e->type->toBasetype()->ty != Tsarray)
{
const d_uns64 esz = e->type->size(e->loc);
const d_uns64 e1sz = e->e1->type->size(e->e1->loc);
if (esz == SIZE_INVALID ||
e1sz == SIZE_INVALID)
return error();
if (esz == e1sz)
goto L1;
}
return;
}
if (e->to->toBasetype()->ty != Tvoid)
{
if (e->e1->type->equals(e->type) && e->type->equals(e->to))
ret = e->e1;
else
ret = Cast(e->loc, e->type, e->to, e->e1).copy();
}
}
//printf(" returning6 %s\n", ret->toChars());
}
void visit(BinExp *e)
{
//printf("BinExp::optimize(result = %d) %s\n", result, e->toChars());
// don't replace const variable with its initializer in e1
bool e2only = (e->op == TOKconstruct || e->op == TOKblit);
if (e2only ? expOptimize(e->e2, result) : binOptimize(e, result))
return;
if (e->op == TOKshlass || e->op == TOKshrass || e->op == TOKushrass)
{
if (e->e2->isConst() == 1)
{
sinteger_t i2 = e->e2->toInteger();
d_uns64 sz = e->e1->type->size(e->e1->loc);
assert(sz != SIZE_INVALID);
sz *= 8;
if (i2 < 0 || (d_uns64)i2 >= sz)
{
e->error("shift assign by %lld is outside the range 0..%llu", i2, (ulonglong)sz - 1);
return error();
}
}
}
}
void visit(AddExp *e)
{
//printf("AddExp::optimize(%s)\n", e->toChars());
if (binOptimize(e, result))
return;
if (e->e1->isConst() && e->e2->isConst())
{
if (e->e1->op == TOKsymoff && e->e2->op == TOKsymoff)
return;
ret = Add(e->loc, e->type, e->e1, e->e2).copy();
}
}
void visit(MinExp *e)
{
if (binOptimize(e, result))
return;
if (e->e1->isConst() && e->e2->isConst())
{
if (e->e2->op == TOKsymoff)
return;
ret = Min(e->loc, e->type, e->e1, e->e2).copy();
}
}
void visit(MulExp *e)
{
//printf("MulExp::optimize(result = %d) %s\n", result, e->toChars());
if (binOptimize(e, result))
return;
if (e->e1->isConst() == 1 && e->e2->isConst() == 1)
{
ret = Mul(e->loc, e->type, e->e1, e->e2).copy();
}
}
void visit(DivExp *e)
{
//printf("DivExp::optimize(%s)\n", e->toChars());
if (binOptimize(e, result))
return;
if (e->e1->isConst() == 1 && e->e2->isConst() == 1)
{
ret = Div(e->loc, e->type, e->e1, e->e2).copy();
}
}
void visit(ModExp *e)
{
if (binOptimize(e, result))
return;
if (e->e1->isConst() == 1 && e->e2->isConst() == 1)
{
ret = Mod(e->loc, e->type, e->e1, e->e2).copy();
}
}
void shift_optimize(BinExp *e, UnionExp (*shift)(Loc, Type *, Expression *, Expression *))
{
if (binOptimize(e, result))
return;
if (e->e2->isConst() == 1)
{
sinteger_t i2 = e->e2->toInteger();
d_uns64 sz = e->e1->type->size();
assert(sz != SIZE_INVALID);
sz *= 8;
if (i2 < 0 || (d_uns64)i2 >= sz)
{
e->error("shift by %lld is outside the range 0..%llu", i2, (ulonglong)sz - 1);
return error();
}
if (e->e1->isConst() == 1)
ret = (*shift)(e->loc, e->type, e->e1, e->e2).copy();
}
}
void visit(ShlExp *e)
{
//printf("ShlExp::optimize(result = %d) %s\n", result, e->toChars());
shift_optimize(e, &Shl);
}
void visit(ShrExp *e)
{
//printf("ShrExp::optimize(result = %d) %s\n", result, e->toChars());
shift_optimize(e, &Shr);
}
void visit(UshrExp *e)
{
//printf("UshrExp::optimize(result = %d) %s\n", result, toChars());
shift_optimize(e, &Ushr);
}
void visit(AndExp *e)
{
if (binOptimize(e, result))
return;
if (e->e1->isConst() == 1 && e->e2->isConst() == 1)
ret = And(e->loc, e->type, e->e1, e->e2).copy();
}
void visit(OrExp *e)
{
if (binOptimize(e, result))
return;
if (e->e1->isConst() == 1 && e->e2->isConst() == 1)
ret = Or(e->loc, e->type, e->e1, e->e2).copy();
}
void visit(XorExp *e)
{
if (binOptimize(e, result))
return;
if (e->e1->isConst() == 1 && e->e2->isConst() == 1)
ret = Xor(e->loc, e->type, e->e1, e->e2).copy();
}
void visit(PowExp *e)
{
if (binOptimize(e, result))
return;
// Replace 1 ^^ x or 1.0^^x by (x, 1)
if ((e->e1->op == TOKint64 && e->e1->toInteger() == 1) ||
(e->e1->op == TOKfloat64 && e->e1->toReal() == CTFloat::one))
{
ret = new CommaExp(e->loc, e->e2, e->e1);
ret->type = e->type;
return;
}
// Replace -1 ^^ x by (x&1) ? -1 : 1, where x is integral
if (e->e2->type->isintegral() && e->e1->op == TOKint64 && (sinteger_t)e->e1->toInteger() == -1L)
{
ret = new AndExp(e->loc, e->e2, new IntegerExp(e->loc, 1, e->e2->type));
ret->type = e->e2->type;
ret = new CondExp(e->loc, ret, new IntegerExp(e->loc, -1L, e->type), new IntegerExp(e->loc, 1L, e->type));
ret->type = e->type;
return;
}
// Replace x ^^ 0 or x^^0.0 by (x, 1)
if ((e->e2->op == TOKint64 && e->e2->toInteger() == 0) ||
(e->e2->op == TOKfloat64 && e->e2->toReal() == CTFloat::zero))
{
if (e->e1->type->isintegral())
ret = new IntegerExp(e->loc, 1, e->e1->type);
else
ret = new RealExp(e->loc, CTFloat::one, e->e1->type);
ret = new CommaExp(e->loc, e->e1, ret);
ret->type = e->type;
return;
}
// Replace x ^^ 1 or x^^1.0 by (x)
if ((e->e2->op == TOKint64 && e->e2->toInteger() == 1) ||
(e->e2->op == TOKfloat64 && e->e2->toReal() == CTFloat::one))
{
ret = e->e1;
return;
}
// Replace x ^^ -1.0 by (1.0 / x)
if ((e->e2->op == TOKfloat64 && e->e2->toReal() == CTFloat::minusone))
{
ret = new DivExp(e->loc, new RealExp(e->loc, CTFloat::one, e->e2->type), e->e1);
ret->type = e->type;
return;
}
// All other negative integral powers are illegal
if ((e->e1->type->isintegral()) && (e->e2->op == TOKint64) && (sinteger_t)e->e2->toInteger() < 0)
{
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());
return error();
}
// If e2 *could* have been an integer, make it one.
if (e->e2->op == TOKfloat64 && (e->e2->toReal() == ldouble((sinteger_t)e->e2->toReal())))
e->e2 = new IntegerExp(e->loc, e->e2->toInteger(), Type::tint64);
if (e->e1->isConst() == 1 && e->e2->isConst() == 1)
{
Expression *ex = Pow(e->loc, e->type, e->e1, e->e2).copy();
if (!CTFEExp::isCantExp(ex))
{
ret = ex;
return;
}
}
// (2 ^^ n) ^^ p -> 1 << n * p
if (e->e1->op == TOKint64 && e->e1->toInteger() > 0 &&
!((e->e1->toInteger() - 1) & e->e1->toInteger()) &&
e->e2->type->isintegral() && e->e2->type->isunsigned())
{
dinteger_t i = e->e1->toInteger();
dinteger_t mul = 1;
while ((i >>= 1) > 1)
mul++;
Expression *shift = new MulExp(e->loc, e->e2, new IntegerExp(e->loc, mul, e->e2->type));
shift->type = e->e2->type;
shift = shift->castTo(NULL, Type::tshiftcnt);
ret = new ShlExp(e->loc, new IntegerExp(e->loc, 1, e->e1->type), shift);
ret->type = e->type;
return;
}
}
void visit(CommaExp *e)
{
//printf("CommaExp::optimize(result = %d) %s\n", result, e->toChars());
// Comma needs special treatment, because it may
// contain compiler-generated declarations. We can interpret them, but
// otherwise we must NOT attempt to constant-fold them.
// In particular, if the comma returns a temporary variable, it needs
// to be an lvalue (this is particularly important for struct constructors)
expOptimize(e->e1, WANTvalue);
expOptimize(e->e2, result, keepLvalue);
if (ret->op == TOKerror)
return;
if (!e->e1 || e->e1->op == TOKint64 || e->e1->op == TOKfloat64 || !hasSideEffect(e->e1))
{
ret = e->e2;
if (ret)
ret->type = e->type;
}
//printf("-CommaExp::optimize(result = %d) %s\n", result, e->e->toChars());
}
void visit(ArrayLengthExp *e)
{
//printf("ArrayLengthExp::optimize(result = %d) %s\n", result, e->toChars());
if (unaOptimize(e, WANTexpand))
return;
// CTFE interpret static immutable arrays (to get better diagnostics)
if (e->e1->op == TOKvar)
{
VarDeclaration *v = ((VarExp *)e->e1)->var->isVarDeclaration();
if (v && (v->storage_class & STCstatic) && (v->storage_class & STCimmutable) && v->_init)
{
if (Expression *ci = v->getConstInitializer())
e->e1 = ci;
}
}
if (e->e1->op == TOKstring || e->e1->op == TOKarrayliteral || e->e1->op == TOKassocarrayliteral ||
e->e1->type->toBasetype()->ty == Tsarray)
{
ret = ArrayLength(e->type, e->e1).copy();
}
}
void visit(EqualExp *e)
{
//printf("EqualExp::optimize(result = %x) %s\n", result, e->toChars());
if (binOptimize(e, WANTvalue))
return;
Expression *e1 = fromConstInitializer(result, e->e1);
Expression *e2 = fromConstInitializer(result, e->e2);
if (e1->op == TOKerror)
{
ret = e1;
return;
}
if (e2->op == TOKerror)
{
ret = e2;
return;
}
ret = Equal(e->op, e->loc, e->type, e1, e2).copy();
if (CTFEExp::isCantExp(ret))
ret = e;
}
void visit(IdentityExp *e)
{
//printf("IdentityExp::optimize(result = %d) %s\n", result, e->toChars());
if (binOptimize(e, WANTvalue))
return;
if ((e->e1->isConst() && e->e2->isConst()) ||
(e->e1->op == TOKnull && e->e2->op == TOKnull)
)
{
ret = Identity(e->op, e->loc, e->type, e->e1, e->e2).copy();
if (CTFEExp::isCantExp(ret))
ret = e;
}
}
/* It is possible for constant folding to change an array expression of
* unknown length, into one where the length is known.
* If the expression 'arr' is a literal, set lengthVar to be its length.
*/
static void setLengthVarIfKnown(VarDeclaration *lengthVar, Expression *arr)
{
if (!lengthVar)
return;
if (lengthVar->_init && !lengthVar->_init->isVoidInitializer())
return; // we have previously calculated the length
size_t len;
if (arr->op == TOKstring)
len = ((StringExp *)arr)->len;
else if (arr->op == TOKarrayliteral)
len = ((ArrayLiteralExp *)arr)->elements->length;
else
{
Type *t = arr->type->toBasetype();
if (t->ty == Tsarray)
len = (size_t)((TypeSArray *)t)->dim->toInteger();
else
return; // we don't know the length yet
}
Expression *dollar = new IntegerExp(Loc(), len, Type::tsize_t);
lengthVar->_init = new ExpInitializer(Loc(), dollar);
lengthVar->storage_class |= STCstatic | STCconst;
}
void visit(IndexExp *e)
{
//printf("IndexExp::optimize(result = %d) %s\n", result, e->toChars());
if (expOptimize(e->e1, result & WANTexpand))
return;
Expression *ex = fromConstInitializer(result, e->e1);
// We might know $ now
setLengthVarIfKnown(e->lengthVar, ex);
if (expOptimize(e->e2, WANTvalue))
return;
if (keepLvalue)
return;
ret = Index(e->type, ex, e->e2).copy();
if (CTFEExp::isCantExp(ret))
ret = e;
}
void visit(SliceExp *e)
{
//printf("SliceExp::optimize(result = %d) %s\n", result, e->toChars());
if (expOptimize(e->e1, result & WANTexpand))
return;
if (!e->lwr)
{
if (e->e1->op == TOKstring)
{
// Convert slice of string literal into dynamic array
Type *t = e->e1->type->toBasetype();
if (Type *tn = t->nextOf())
ret = e->e1->castTo(NULL, tn->arrayOf());
}
}
else
{
e->e1 = fromConstInitializer(result, e->e1);
// We might know $ now
setLengthVarIfKnown(e->lengthVar, e->e1);
expOptimize(e->lwr, WANTvalue);
expOptimize(e->upr, WANTvalue);
if (ret->op == TOKerror)
return;
ret = Slice(e->type, e->e1, e->lwr, e->upr).copy();
if (CTFEExp::isCantExp(ret))
ret = e;
}
// Bugzilla 14649: We need to leave the slice form so it might be
// a part of array operation.
// Assume that the backend codegen will handle the form `e[]`
// as an equal to `e` itself.
if (ret->op == TOKstring)
{
e->e1 = ret;
e->lwr = NULL;
e->upr = NULL;
ret = e;
}
//printf("-SliceExp::optimize() %s\n", ret->toChars());
}
void visit(LogicalExp *e)
{
//printf("LogicalExp::optimize(%d) %s\n", result, e->toChars());
if (expOptimize(e->e1, WANTvalue))
return;
const bool oror = e->op == TOKoror;
if (e->e1->isBool(oror))
{
// Replace with (e1, oror)
ret = new IntegerExp(e->loc, oror, Type::tbool);
ret = Expression::combine(e->e1, ret);
if (e->type->toBasetype()->ty == Tvoid)
{
ret = new CastExp(e->loc, ret, Type::tvoid);
ret->type = e->type;
}
ret = ret->optimize(result);
return;
}
if (expOptimize(e->e2, WANTvalue))
return;
if (e->e1->isConst())
{
if (e->e2->isConst())
{
bool n1 = e->e1->isBool(true);
bool n2 = e->e2->isBool(true);
ret = new IntegerExp(e->loc, oror ? (n1 || n2) : (n1 && n2), e->type);
}
else if (e->e1->isBool(!oror))
{
if (e->type->toBasetype()->ty == Tvoid)
ret = e->e2;
else
{
ret = new CastExp(e->loc, e->e2, e->type);
ret->type = e->type;
}
}
}
}
void visit(CmpExp *e)
{
//printf("CmpExp::optimize() %s\n", e->toChars());
if (binOptimize(e, WANTvalue))
return;
Expression *e1 = fromConstInitializer(result, e->e1);
Expression *e2 = fromConstInitializer(result, e->e2);
ret = Cmp(e->op, e->loc, e->type, e1, e2).copy();
if (CTFEExp::isCantExp(ret))
ret = e;
}
void visit(CatExp *e)
{
//printf("CatExp::optimize(%d) %s\n", result, e->toChars());
if (binOptimize(e, result))
return;
if (e->e1->op == TOKcat)
{
// Bugzilla 12798: optimize ((expr ~ str1) ~ str2)
CatExp *ce1 = (CatExp *)e->e1;
CatExp cex(e->loc, ce1->e2, e->e2);
cex.type = e->type;
Expression *ex = cex.optimize(result);
if (ex != &cex)
{
e->e1 = ce1->e1;
e->e2 = ex;
}
}
// optimize "str"[] -> "str"
if (e->e1->op == TOKslice)
{
SliceExp *se1 = (SliceExp *)e->e1;
if (se1->e1->op == TOKstring && !se1->lwr)
e->e1 = se1->e1;
}
if (e->e2->op == TOKslice)
{
SliceExp *se2 = (SliceExp *)e->e2;
if (se2->e1->op == TOKstring && !se2->lwr)
e->e2 = se2->e1;
}
ret = Cat(e->type, e->e1, e->e2).copy();
if (CTFEExp::isCantExp(ret))
ret = e;
}
void visit(CondExp *e)
{
if (expOptimize(e->econd, WANTvalue))
return;
if (e->econd->isBool(true))
ret = e->e1->optimize(result, keepLvalue);
else if (e->econd->isBool(false))
ret = e->e2->optimize(result, keepLvalue);
else
{
expOptimize(e->e1, result, keepLvalue);
expOptimize(e->e2, result, keepLvalue);
}
}
};
OptimizeVisitor v(result, keepLvalue);
Expression *ex = NULL;
v.ret = e;
// Optimize the expression until it can no longer be simplified.
size_t b = 0;
while (1)
{
if (b++ == global.recursionLimit)
{
e->error("infinite loop while optimizing expression");
fatal();
}
ex = v.ret;
ex->accept(&v);
if (ex == v.ret)
break;
}
return ex;
}