1 /* 2 * Copyright (c) 2013, 2014, 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. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 25 package org.graalvm.compiler.nodes; 26 27 import static org.graalvm.compiler.nodeinfo.InputType.Condition; 28 import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_0; 29 import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_0; 30 31 import org.graalvm.compiler.core.common.type.IntegerStamp; 32 import org.graalvm.compiler.graph.IterableNodeType; 33 import org.graalvm.compiler.graph.NodeClass; 34 import org.graalvm.compiler.graph.spi.Canonicalizable; 35 import org.graalvm.compiler.graph.spi.CanonicalizerTool; 36 import org.graalvm.compiler.nodeinfo.NodeInfo; 37 import org.graalvm.compiler.nodes.calc.IntegerBelowNode; 38 import org.graalvm.compiler.nodes.calc.IntegerLessThanNode; 39 40 import jdk.vm.ci.meta.TriState; 41 42 @NodeInfo(cycles = CYCLES_0, size = SIZE_0) 43 public final class ShortCircuitOrNode extends LogicNode implements IterableNodeType, Canonicalizable.Binary<LogicNode> { 44 public static final NodeClass<ShortCircuitOrNode> TYPE = NodeClass.create(ShortCircuitOrNode.class); 45 @Input(Condition) LogicNode x; 46 @Input(Condition) LogicNode y; 47 protected boolean xNegated; 48 protected boolean yNegated; 49 protected double shortCircuitProbability; 50 51 public ShortCircuitOrNode(LogicNode x, boolean xNegated, LogicNode y, boolean yNegated, double shortCircuitProbability) { 52 super(TYPE); 53 this.x = x; 54 this.xNegated = xNegated; 55 this.y = y; 56 this.yNegated = yNegated; 57 this.shortCircuitProbability = shortCircuitProbability; 58 } 59 60 @Override 61 public LogicNode getX() { 62 return x; 63 } 64 65 @Override 66 public LogicNode getY() { 67 return y; 68 } 69 70 public boolean isXNegated() { 71 return xNegated; 72 } 73 74 public boolean isYNegated() { 75 return yNegated; 76 } 77 78 /** 79 * Gets the probability that the {@link #getY() y} part of this binary node is <b>not</b> 80 * evaluated. This is the probability that this operator will short-circuit its execution. 81 */ 82 public double getShortCircuitProbability() { 83 return shortCircuitProbability; 84 } 85 86 protected ShortCircuitOrNode canonicalizeNegation(LogicNode forX, LogicNode forY) { 87 LogicNode xCond = forX; 88 boolean xNeg = xNegated; 89 while (xCond instanceof LogicNegationNode) { 90 xCond = ((LogicNegationNode) xCond).getValue(); 91 xNeg = !xNeg; 92 } 93 94 LogicNode yCond = forY; 95 boolean yNeg = yNegated; 96 while (yCond instanceof LogicNegationNode) { 97 yCond = ((LogicNegationNode) yCond).getValue(); 98 yNeg = !yNeg; 99 } 100 101 if (xCond != forX || yCond != forY) { 102 return new ShortCircuitOrNode(xCond, xNeg, yCond, yNeg, shortCircuitProbability); 103 } else { 104 return this; 105 } 106 } 107 108 @Override 109 public LogicNode canonical(CanonicalizerTool tool, LogicNode forX, LogicNode forY) { 110 ShortCircuitOrNode ret = canonicalizeNegation(forX, forY); 111 if (ret != this) { 112 return ret; 113 } 114 115 if (forX == forY) { 116 // @formatter:off 117 // a || a = a 118 // a || !a = true 119 // !a || a = true 120 // !a || !a = !a 121 // @formatter:on 122 if (isXNegated()) { 123 if (isYNegated()) { 124 // !a || !a = !a 125 return LogicNegationNode.create(forX); 126 } else { 127 // !a || a = true 128 return LogicConstantNode.tautology(); 129 } 130 } else { 131 if (isYNegated()) { 132 // a || !a = true 133 return LogicConstantNode.tautology(); 134 } else { 135 // a || a = a 136 return forX; 137 } 138 } 139 } 140 if (forX instanceof LogicConstantNode) { 141 if (((LogicConstantNode) forX).getValue() ^ isXNegated()) { 142 return LogicConstantNode.tautology(); 143 } else { 144 if (isYNegated()) { 145 return new LogicNegationNode(forY); 146 } else { 147 return forY; 148 } 149 } 150 } 151 if (forY instanceof LogicConstantNode) { 152 if (((LogicConstantNode) forY).getValue() ^ isYNegated()) { 153 return LogicConstantNode.tautology(); 154 } else { 155 if (isXNegated()) { 156 return new LogicNegationNode(forX); 157 } else { 158 return forX; 159 } 160 } 161 } 162 163 if (forX instanceof ShortCircuitOrNode) { 164 ShortCircuitOrNode inner = (ShortCircuitOrNode) forX; 165 if (forY == inner.getX()) { 166 return optimizeShortCircuit(inner, this.xNegated, this.yNegated, true); 167 } else if (forY == inner.getY()) { 168 return optimizeShortCircuit(inner, this.xNegated, this.yNegated, false); 169 } 170 } else if (forY instanceof ShortCircuitOrNode) { 171 ShortCircuitOrNode inner = (ShortCircuitOrNode) forY; 172 if (inner.getX() == forX) { 173 return optimizeShortCircuit(inner, this.yNegated, this.xNegated, true); 174 } else if (inner.getY() == forX) { 175 return optimizeShortCircuit(inner, this.yNegated, this.xNegated, false); 176 } 177 } 178 179 // !X => Y constant 180 TriState impliedForY = forX.implies(!isXNegated(), forY); 181 if (impliedForY.isKnown()) { 182 boolean yResult = impliedForY.toBoolean() ^ isYNegated(); 183 return yResult 184 ? LogicConstantNode.tautology() 185 : (isXNegated() 186 ? LogicNegationNode.create(forX) 187 : forX); 188 } 189 190 // if X >= 0: 191 // u < 0 || X < u ==>> X |<| u 192 if (!isXNegated() && !isYNegated()) { 193 LogicNode sym = simplifyComparison(forX, forY); 194 if (sym != null) { 195 return sym; 196 } 197 } 198 199 // if X >= 0: 200 // X |<| u || X < u ==>> X |<| u 201 if (forX instanceof IntegerBelowNode && forY instanceof IntegerLessThanNode && !isXNegated() && !isYNegated()) { 202 IntegerBelowNode xNode = (IntegerBelowNode) forX; 203 IntegerLessThanNode yNode = (IntegerLessThanNode) forY; 204 ValueNode xxNode = xNode.getX(); // X >= 0 205 ValueNode yxNode = yNode.getX(); // X >= 0 206 if (xxNode == yxNode && ((IntegerStamp) xxNode.stamp(NodeView.DEFAULT)).isPositive()) { 207 ValueNode xyNode = xNode.getY(); // u 208 ValueNode yyNode = yNode.getY(); // u 209 if (xyNode == yyNode) { 210 return forX; 211 } 212 } 213 } 214 215 // if X >= 0: 216 // u < 0 || (X < u || tail) ==>> X |<| u || tail 217 if (forY instanceof ShortCircuitOrNode && !isXNegated() && !isYNegated()) { 218 ShortCircuitOrNode yNode = (ShortCircuitOrNode) forY; 219 if (!yNode.isXNegated()) { 220 LogicNode sym = simplifyComparison(forX, yNode.getX()); 221 if (sym != null) { 222 double p1 = getShortCircuitProbability(); 223 double p2 = yNode.getShortCircuitProbability(); 224 return new ShortCircuitOrNode(sym, isXNegated(), yNode.getY(), yNode.isYNegated(), p1 + (1 - p1) * p2); 225 } 226 } 227 } 228 229 return this; 230 } 231 232 private static LogicNode simplifyComparison(LogicNode forX, LogicNode forY) { 233 LogicNode sym = simplifyComparisonOrdered(forX, forY); 234 if (sym == null) { 235 return simplifyComparisonOrdered(forY, forX); 236 } 237 return sym; 238 } 239 240 private static LogicNode simplifyComparisonOrdered(LogicNode forX, LogicNode forY) { 241 // if X is >= 0: 242 // u < 0 || X < u ==>> X |<| u 243 if (forX instanceof IntegerLessThanNode && forY instanceof IntegerLessThanNode) { 244 IntegerLessThanNode xNode = (IntegerLessThanNode) forX; 245 IntegerLessThanNode yNode = (IntegerLessThanNode) forY; 246 ValueNode xyNode = xNode.getY(); // 0 247 if (xyNode.isConstant() && IntegerStamp.OPS.getAdd().isNeutral(xyNode.asConstant())) { 248 ValueNode yxNode = yNode.getX(); // X >= 0 249 IntegerStamp stamp = (IntegerStamp) yxNode.stamp(NodeView.DEFAULT); 250 if (stamp.isPositive()) { 251 if (xNode.getX() == yNode.getY()) { 252 ValueNode u = xNode.getX(); 253 return IntegerBelowNode.create(yxNode, u, NodeView.DEFAULT); 254 } 255 } 256 } 257 } 258 259 return null; 260 } 261 262 private static LogicNode optimizeShortCircuit(ShortCircuitOrNode inner, boolean innerNegated, boolean matchNegated, boolean matchIsInnerX) { 263 boolean innerMatchNegated; 264 if (matchIsInnerX) { 265 innerMatchNegated = inner.isXNegated(); 266 } else { 267 innerMatchNegated = inner.isYNegated(); 268 } 269 if (!innerNegated) { 270 // The four digit results of the expression used in the 16 subsequent formula comments 271 // correspond to results when using the following truth table for inputs a and b 272 // and testing all 4 possible input combinations: 273 // _ 1234 274 // a 1100 275 // b 1010 276 if (innerMatchNegated == matchNegated) { 277 // ( (!a ||!b) ||!a) => 0111 (!a ||!b) 278 // ( (!a || b) ||!a) => 1011 (!a || b) 279 // ( ( a ||!b) || a) => 1101 ( a ||!b) 280 // ( ( a || b) || a) => 1110 ( a || b) 281 // Only the inner or is relevant, the outer or never adds information. 282 return inner; 283 } else { 284 // ( ( a || b) ||!a) => 1111 (true) 285 // ( (!a ||!b) || a) => 1111 (true) 286 // ( (!a || b) || a) => 1111 (true) 287 // ( ( a ||!b) ||!a) => 1111 (true) 288 // The result of the expression is always true. 289 return LogicConstantNode.tautology(); 290 } 291 } else { 292 if (innerMatchNegated == matchNegated) { 293 // (!(!a ||!b) ||!a) => 1011 (!a || b) 294 // (!(!a || b) ||!a) => 0111 (!a ||!b) 295 // (!( a ||!b) || a) => 1110 ( a || b) 296 // (!( a || b) || a) => 1101 ( a ||!b) 297 boolean newInnerXNegated = inner.isXNegated(); 298 boolean newInnerYNegated = inner.isYNegated(); 299 double newProbability = inner.getShortCircuitProbability(); 300 if (matchIsInnerX) { 301 newInnerYNegated = !newInnerYNegated; 302 } else { 303 newInnerXNegated = !newInnerXNegated; 304 newProbability = 1.0 - newProbability; 305 } 306 // The expression can be transformed into a single or. 307 return new ShortCircuitOrNode(inner.getX(), newInnerXNegated, inner.getY(), newInnerYNegated, newProbability); 308 } else { 309 // (!(!a ||!b) || a) => 1100 (a) 310 // (!(!a || b) || a) => 1100 (a) 311 // (!( a ||!b) ||!a) => 0011 (!a) 312 // (!( a || b) ||!a) => 0011 (!a) 313 LogicNode result = inner.getY(); 314 if (matchIsInnerX) { 315 result = inner.getX(); 316 } 317 // Only the second part of the outer or is relevant. 318 if (matchNegated) { 319 return LogicNegationNode.create(result); 320 } else { 321 return result; 322 } 323 } 324 } 325 } 326 }