Package joust.optimisers.cse

Source Code of joust.optimisers.cse.CommonSubExpressionTranslator

package joust.optimisers.cse;

import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.util.Name;
import joust.optimisers.invar.ExpressionComplexityClassifier;
import joust.optimisers.translators.BaseTranslator;
import joust.tree.annotatedtree.AJCComparableExpressionTree;
import joust.tree.annotatedtree.AJCForest;
import joust.tree.annotatedtree.AJCTree;
import joust.tree.annotatedtree.treeinfo.EffectSet;
import joust.utils.logging.LogUtils;
import joust.utils.tree.NameFactory;
import joust.utils.data.SymbolSet;
import joust.utils.data.StackMap;
import lombok.experimental.ExtensionMethod;
import lombok.extern.java.Log;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;

import static joust.tree.annotatedtree.AJCTree.*;
import static joust.utils.compiler.StaticCompilerUtils.*;
import static joust.tree.annotatedtree.AJCComparableExpressionTree.*;

/**
* Visit all statements in a method in order, recording which expressions are available as you go along.
* When an expression is ceasing to be available, determine if it's worth introducing a temporary variable for it and
* do so if necessary.
*/
@Log
@ExtensionMethod({Logger.class, LogUtils.LogExtensions.class})
public class CommonSubExpressionTranslator extends BaseTranslator {
    // The minimum score for an expression to have to qualify for consideration.
    public static int MINIMUM_CSE_SCORE = 6;

    // A stack of maps for available expressions. Maps are pushed and popped as we enter/leave scopes.
    StackMap<AJCComparableExpressionTree, AvailableExpression> availableExpressions = new StackMap<AJCComparableExpressionTree, AvailableExpression>();

    // The set of AvailableExpressions ever live in this method - we look here when deciding what needs transforming
    // after completing our traversal of the method.
    List<AvailableExpression> everAvailInMethod = new ArrayList<AvailableExpression>();

    private void enterScope() {
        availableExpressions.pushMap();
    }

    private void leaveScope() {
        Map<AJCComparableExpressionTree, AvailableExpression> popped = availableExpressions.popMap();

        // Finalise the popped expressions...
        Set<AJCComparableExpressionTree> keysCopy = new HashSet<AJCComparableExpressionTree>(popped.keySet());
        for (AJCComparableExpressionTree t : keysCopy) {
            log.debug("Key: {}", t);
            finaliseAvailableExpression(popped.get(t));
        }
    }

    @Override
    protected void visitClassDef(AJCClassDecl that) {
        visit(that.methods);
        visit(that.classes);
    }

    @Override
    protected void visitBlock(AJCBlock that) {
        enterScope();
        super.visitBlock(that);
        leaveScope();
    }

    @Override
    protected void visitMethodDef(AJCMethodDecl that) {
        Symbol.MethodSymbol enclosingMethod = that.getTargetSymbol();

        // Clear out any junk from the previous run.
        availableExpressions.clear();
        everAvailInMethod.clear();

        super.visitMethodDef(that);

        // Visit forwards.
        // Visit all expressions of high enough value.
        // Keep an available expression set of things of enough value.

        everAvailInMethod.sort(new AvailableExpressionComparator());

        if (everAvailInMethod.isEmpty()) {
            return;
        }

        AvailableExpression targetExpr = everAvailInMethod.get(0);
        log.info("Most valuable expression: {} with score {}", targetExpr, targetExpr.getComplexityScore());

        AJCExpressionTree expr = targetExpr.firstInstance.wrappedNode;

        // Name and symbol for new temporary variable.
        Name tempName = NameFactory.getName();
        Symbol.VarSymbol newSym = new Symbol.VarSymbol(Flags.FINAL, tempName, expr.getNodeType(), enclosingMethod);

        // Declaration thereof.
        AJCVariableDecl newDecl = treeMaker.VarDef(newSym, treeCopier.copy(expr));

        // Insert the new declaration before the first use.
        expr.getEnclosingBlock().insertBefore(expr.getEnclosingStatement(), newDecl);

        // Replace uses of the expression with a reference to the new temporary variable.
        for (AJCComparableExpressionTree use : targetExpr.usages) {
            AJCForest.getInstance().increment("Common Subexpressions Eliminated:");
            use.wrappedNode.swapFor(treeMaker.Ident(newSym));
        }

        mHasMadeAChange = true;

        log.info("After CSE pass:\n{}", expr.getEnclosingBlock());

        // We changed the tree. Rerun side effect analysis and do it all over again. (Hopefully not forever...)
        AJCForest.getInstance().initialAnalysis();
        visitMethodDef(that);
    }

    /**
     * General visitor function for considering expressions.
     *
     * @param that The expression to consider.
     * @return true if the visitor should continue down the tree, false if this branch should be explored no further.
     *         (Used to avoid exploring down trees that are already too inexpensive to be interesting).
     */
    private void visitExpression(AJCComparableExpressionTree<? extends AJCExpressionTree> that) {
        // Determine if this tree is cheap enough that we don't care about it.
        ExpressionComplexityClassifier classifier = new ExpressionComplexityClassifier();
        classifier.visitTree(that.wrappedNode);

        log.debug("Encountered: {}", that);

        int score = classifier.getScore();
        if (score < MINIMUM_CSE_SCORE) {
            log.debug("Score too low - stop.");
            return;
        }

        // Determine if this tree holds side effects that prevent us from being able to consider it for CSE.
        EffectSet effects = that.wrappedNode.effects.getEffectSet();
        if (effects.contains(EffectSet.EffectType.WRITE_ESCAPING)
         || effects.contains(EffectSet.EffectType.READ_ESCAPING) // Concurrency...
         || effects.contains(EffectSet.EffectType.IO)) {
            log.debug("Effects problematic - stop.");
            return;
        }

        // Do we have an AvailableExpression for this already?
        // Need stack of Maps from expressions to AvailableExpressions.
        if (availableExpressions.containsKey(that)) {
            AvailableExpression existingExpr = availableExpressions.get(that);
            existingExpr.addUsage(that);

            log.debug("Existing key for {} found. Now: {}", that, existingExpr);
        } else {
            AvailableExpression expr = new AvailableExpression(that);
            log.debug("New entry: {}", expr);
            availableExpressions.put(that, expr);
        }
    }

    /**
     * General visitor method for handling updates - strips from the availableExpressions map anything invalidated by
     * this tree. (Using effect information).
     */
    private void visitUpdate(AJCEffectAnnotatedTree tree) {
        EffectSet effects = tree.effects.getEffectSet();
        SymbolSet internalWrites = effects.writeInternal;

        // If this tree writes anything that is read by an availableExpression, it has to go.
        // TODO: Make this more efficient.
        Set<AJCComparableExpressionTree> keysCopy = new HashSet<AJCComparableExpressionTree>(availableExpressions.keySet());
        for (AJCComparableExpressionTree t : keysCopy) {
            if (t.wrappedNode instanceof AJCEffectAnnotatedTree) {
                AJCEffectAnnotatedTree effectTree = (AJCEffectAnnotatedTree) t.wrappedNode;

                EffectSet availEffects = effectTree.effects.getEffectSet();
                for (Symbol.VarSymbol sym : availEffects.readInternal) {
                    if (internalWrites.contains(sym)) {
                        log.debug("Dropping {} because {} wrote {}", t, tree, sym);
                        finaliseAvailableExpression(availableExpressions.get(t));
                        availableExpressions.remove(t);
                        break;
                    }
                }
            }
        }
    }

    /**
     * Determine if a given available expression provides enough value to be worth swapping.
     * If it does, add it to the everAvailInMethod set. Otherwise, don't.
     */
    private void finaliseAvailableExpression(AvailableExpression expr) {
        log.debug("Finalising {}", expr);
        if (expr.usages.size() <= 1) {
            log.debug("One use. No point.");
            return;
        }

        int score = expr.getComplexityScore();
        int afterScore = expr.getApplicationCost();

        log.debug("beforeScore: {}", score);
        log.debug("afterScore:  {}", afterScore);

        if (afterScore >= score) {
            log.debug("No point... :(");
            // No point...
            return;
        }

        everAvailInMethod.add(expr);
    }

    @Override
    protected void visitFieldAccess(AJCFieldAccess that) {
        if (that.getTargetSymbol() instanceof Symbol.VarSymbol) {
            visitExpression(new ComparableAJCSymbolRefTree(that));
            super.visitFieldAccess(that);
        }
    }

    @Override
    protected void visitBinary(AJCBinary that) {
        visitExpression(new ComparableAJCBinary(that));
        super.visitBinary(that);
    }

    @Override
    protected void visitUnary(AJCUnary that) {
        visitExpression(new ComparableAJCUnary(that));
        super.visitUnary(that);
    }

    @Override
    protected void visitUnaryAsg(AJCUnaryAsg that) {
        visitUpdate(that);
        visitExpression(new ComparableAJCUnaryAsg(that));
        super.visitUnaryAsg(that);
    }

    @Override
    protected void visitTypeCast(AJCTypeCast that) {
        visitExpression(new ComparableAJCTypeCast(that));
        super.visitTypeCast(that);
    }

    @Override
    protected void visitInstanceOf(AJCInstanceOf that) {
        visitExpression(new ComparableAJCInstanceOf(that));
        super.visitInstanceOf(that);
    }

    @Override
    protected void visitCall(AJCCall that) {
        visitUpdate(that);
        visitExpression(new ComparableAJCCall(that));
        super.visitCall(that);
    }

    @Override
    protected void visitArrayAccess(AJCArrayAccess that) {
        visitUpdate(that);
        super.visitArrayAccess(that);
    }

    @Override
    protected void visitAssignop(AJCAssignOp that) {
        visitUpdate(that);
        super.visitAssignop(that);
    }

    @Override
    protected void visitAssign(AJCAssign that) {
        visitUpdate(that);
        super.visitAssign(that);
    }

    @Override
    protected void visitDoWhileLoop(AJCDoWhileLoop that) {
        visitUpdate(that);
        super.visitDoWhileLoop(that);
    }

    @Override
    protected void visitWhileLoop(AJCWhileLoop that) {
        visitUpdate(that);
        super.visitWhileLoop(that);
    }

    @Override
    protected void visitForLoop(AJCForLoop that) {
        visitUpdate(that);
        super.visitForLoop(that);
    }

    @Override
    protected void visitConditional(AJCConditional that) {
        visitUpdate(that);
        visitExpression(new ComparableAJCConditional(that));
        super.visitConditional(that);
    }

    @Override
    protected void visitSwitch(AJCSwitch that) {
        // Because of the stupid scoping rules, this needs special treatment.
        // You enter a scope when you hit the first case, and you leave it when you hit a break.
        visit(that.selector);

        // For the purposes of CSE, you want to throw away newly-available expressions between cases.
        for (AJCCase cas : that.cases) {
            enterScope();
            visitCase(cas);
            leaveScope();
        }
    }
}
TOP

Related Classes of joust.optimisers.cse.CommonSubExpressionTranslator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.