1 /*
   2  * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package jdk.nashorn.internal.codegen;
  27 
  28 import static jdk.nashorn.internal.codegen.Condition.EQ;
  29 import static jdk.nashorn.internal.codegen.Condition.GE;
  30 import static jdk.nashorn.internal.codegen.Condition.GT;
  31 import static jdk.nashorn.internal.codegen.Condition.LE;
  32 import static jdk.nashorn.internal.codegen.Condition.LT;
  33 import static jdk.nashorn.internal.codegen.Condition.NE;
  34 import static jdk.nashorn.internal.parser.TokenType.NOT;
  35 
  36 import jdk.nashorn.internal.ir.BinaryNode;
  37 import jdk.nashorn.internal.ir.Expression;
  38 import jdk.nashorn.internal.ir.JoinPredecessorExpression;
  39 import jdk.nashorn.internal.ir.LocalVariableConversion;
  40 import jdk.nashorn.internal.ir.UnaryNode;
  41 
  42 /**
  43  * Branch optimizer for CodeGenerator. Given a jump condition this helper
  44  * class attempts to simplify the control flow
  45  */
  46 final class BranchOptimizer {
  47 
  48     private final CodeGenerator codegen;
  49     private final MethodEmitter method;
  50 
  51     BranchOptimizer(final CodeGenerator codegen, final MethodEmitter method) {
  52         this.codegen = codegen;
  53         this.method  = method;
  54     }
  55 
  56     void execute(final Expression node, final Label label, final boolean state) {
  57         branchOptimizer(node, label, state);
  58     }
  59 
  60     private void branchOptimizer(final UnaryNode unaryNode, final Label label, final boolean state) {
  61         if (unaryNode.isTokenType(NOT)) {
  62             branchOptimizer(unaryNode.getExpression(), label, !state);
  63         } else {
  64             loadTestAndJump(unaryNode, label, state);
  65         }
  66     }
  67 
  68     private void branchOptimizer(final BinaryNode binaryNode, final Label label, final boolean state) {
  69         final Expression lhs = binaryNode.lhs();
  70         final Expression rhs = binaryNode.rhs();
  71 
  72         switch (binaryNode.tokenType()) {
  73         case AND:
  74             if (state) {
  75                 final Label skip = new Label("skip");
  76                 optimizeLogicalOperand(lhs, skip,  false, false);
  77                 optimizeLogicalOperand(rhs, label, true,  true);
  78                 method.label(skip);
  79             } else {
  80                 optimizeLogicalOperand(lhs, label, false, false);
  81                 optimizeLogicalOperand(rhs, label, false, true);
  82             }
  83             return;
  84 
  85         case OR:
  86             if (state) {
  87                 optimizeLogicalOperand(lhs, label, true, false);
  88                 optimizeLogicalOperand(rhs, label, true, true);
  89             } else {
  90                 final Label skip = new Label("skip");
  91                 optimizeLogicalOperand(lhs, skip,  true,  false);
  92                 optimizeLogicalOperand(rhs, label, false, true);
  93                 method.label(skip);
  94             }
  95             return;
  96 
  97         case EQ:
  98         case EQ_STRICT:
  99             codegen.loadComparisonOperands(binaryNode);
 100             method.conditionalJump(state ? EQ : NE, true, label);
 101             return;
 102 
 103         case NE:
 104         case NE_STRICT:
 105             codegen.loadComparisonOperands(binaryNode);
 106             method.conditionalJump(state ? NE : EQ, true, label);
 107             return;
 108 
 109         case GE:
 110             codegen.loadComparisonOperands(binaryNode);
 111             method.conditionalJump(state ? GE : LT, false, label);
 112             return;
 113 
 114         case GT:
 115             codegen.loadComparisonOperands(binaryNode);
 116             method.conditionalJump(state ? GT : LE, false, label);
 117             return;
 118 
 119         case LE:
 120             codegen.loadComparisonOperands(binaryNode);
 121             method.conditionalJump(state ? LE : GT, true, label);
 122             return;
 123 
 124         case LT:
 125             codegen.loadComparisonOperands(binaryNode);
 126             method.conditionalJump(state ? LT : GE, true, label);
 127             return;
 128 
 129         default:
 130             break;
 131         }
 132 
 133         loadTestAndJump(binaryNode, label, state);
 134     }
 135 
 136     private void optimizeLogicalOperand(final Expression expr, final Label label, final boolean state, final boolean isRhs) {
 137         final JoinPredecessorExpression jpexpr = (JoinPredecessorExpression)expr;
 138         if(LocalVariableConversion.hasLiveConversion(jpexpr)) {
 139             final Label after = new Label("after");
 140             branchOptimizer(jpexpr.getExpression(), after, !state);
 141             method.beforeJoinPoint(jpexpr);
 142             method._goto(label);
 143             method.label(after);
 144             if(isRhs) {
 145                 method.beforeJoinPoint(jpexpr);
 146             }
 147         } else {
 148             branchOptimizer(jpexpr.getExpression(), label, state);
 149         }
 150     }
 151     private void branchOptimizer(final Expression node, final Label label, final boolean state) {
 152         if (node instanceof BinaryNode) {
 153             branchOptimizer((BinaryNode)node, label, state);
 154             return;
 155         }
 156 
 157         if (node instanceof UnaryNode) {
 158             branchOptimizer((UnaryNode)node, label, state);
 159             return;
 160         }
 161 
 162         loadTestAndJump(node, label, state);
 163     }
 164 
 165     private void loadTestAndJump(final Expression node, final Label label, final boolean state) {
 166         codegen.loadExpressionAsBoolean(node);
 167         if (state) {
 168             method.ifne(label);
 169         } else {
 170             method.ifeq(label);
 171         }
 172     }
 173 }