diff gcc/d/dmd/optimize.c @ 145:1830386684a0

gcc-9.2.0
author anatofuz
date Thu, 13 Feb 2020 11:34:05 +0900
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gcc/d/dmd/optimize.c	Thu Feb 13 11:34:05 2020 +0900
@@ -0,0 +1,1265 @@
+
+/* Compiler implementation of the D programming language
+ * Copyright (C) 1999-2019 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"
+
+Expression *semantic(Expression *e, Scope *sc);
+
+/*************************************
+ * 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
+        v->semantic(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 = semantic(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->dim; 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->dim; i++)
+                {
+                    expOptimize((*e->elements)[i], result & WANTexpand);
+                }
+            }
+        }
+
+        void visit(AssocArrayLiteralExp *e)
+        {
+            assert(e->keys->dim == e->values->dim);
+            for (size_t i = 0; i < e->keys->dim; 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->dim; 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->dim; i++)
+                {
+                    expOptimize((*e->newargs)[i], WANTvalue);
+                }
+            }
+
+            if (e->arguments)
+            {
+                for (size_t i = 0; i < e->arguments->dim; 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->dim; i++)
+                {
+                    Parameter *p = Parameter::getNth(tf->parameters, 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->dim;
+            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(AndAndExp *e)
+        {
+            //printf("AndAndExp::optimize(%d) %s\n", result, e->toChars());
+            if (expOptimize(e->e1, WANTvalue))
+                return;
+
+            if (e->e1->isBool(false))
+            {
+                // Replace with (e1, false)
+                ret = new IntegerExp(e->loc, 0, 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;
+                }
+                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, n1 && n2, e->type);
+                }
+                else if (e->e1->isBool(true))
+                {
+                    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(OrOrExp *e)
+        {
+            //printf("OrOrExp::optimize(%d) %s\n", result, e->toChars());
+            if (expOptimize(e->e1, WANTvalue))
+                return;
+
+            if (e->e1->isBool(true))
+            {
+                // Replace with (e1, true)
+                ret = new IntegerExp(e->loc, 1, 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;
+                }
+                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, n1 || n2, e->type);
+                }
+                else if (e->e1->isBool(false))
+                {
+                    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.
+    while (ex != v.ret)
+    {
+        ex = v.ret;
+        ex->accept(&v);
+    }
+    return ex;
+}