| /* |
| * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| package org.graalvm.compiler.core.common.calc; |
| |
| import org.graalvm.compiler.debug.GraalError; |
| |
| import jdk.vm.ci.meta.Constant; |
| import jdk.vm.ci.meta.ConstantReflectionProvider; |
| import jdk.vm.ci.meta.JavaConstant; |
| import jdk.vm.ci.meta.PrimitiveConstant; |
| |
| /** |
| * Condition codes used in conditionals. |
| */ |
| public enum Condition { |
| /** |
| * Equal. |
| */ |
| EQ("=="), |
| |
| /** |
| * Not equal. |
| */ |
| NE("!="), |
| |
| /** |
| * Signed less than. |
| */ |
| LT("<"), |
| |
| /** |
| * Signed less than or equal. |
| */ |
| LE("<="), |
| |
| /** |
| * Signed greater than. |
| */ |
| GT(">"), |
| |
| /** |
| * Signed greater than or equal. |
| */ |
| GE(">="), |
| |
| /** |
| * Unsigned greater than or equal ("above than or equal"). |
| */ |
| AE("|>=|"), |
| |
| /** |
| * Unsigned less than or equal ("below than or equal"). |
| */ |
| BE("|<=|"), |
| |
| /** |
| * Unsigned greater than ("above than"). |
| */ |
| AT("|>|"), |
| |
| /** |
| * Unsigned less than ("below than"). |
| */ |
| BT("|<|"); |
| |
| public final String operator; |
| |
| Condition(String operator) { |
| this.operator = operator; |
| } |
| |
| public boolean check(int left, int right) { |
| switch (this) { |
| case EQ: |
| return left == right; |
| case NE: |
| return left != right; |
| case LT: |
| return left < right; |
| case LE: |
| return left <= right; |
| case GT: |
| return left > right; |
| case GE: |
| return left >= right; |
| case AE: |
| return UnsignedMath.aboveOrEqual(left, right); |
| case BE: |
| return UnsignedMath.belowOrEqual(left, right); |
| case AT: |
| return UnsignedMath.aboveThan(left, right); |
| case BT: |
| return UnsignedMath.belowThan(left, right); |
| } |
| throw new IllegalArgumentException(this.toString()); |
| } |
| |
| /** |
| * Given a condition and its negation, this method returns true for one of the two and false for |
| * the other one. This can be used to keep comparisons in a canonical form. |
| * |
| * @return true if this condition is considered to be the canonical form, false otherwise. |
| */ |
| public boolean isCanonical() { |
| switch (this) { |
| case EQ: |
| return true; |
| case NE: |
| return false; |
| case LT: |
| return true; |
| case LE: |
| return false; |
| case GT: |
| return false; |
| case GE: |
| return false; |
| case BT: |
| return true; |
| case BE: |
| return false; |
| case AT: |
| return false; |
| case AE: |
| return false; |
| } |
| throw new IllegalArgumentException(this.toString()); |
| } |
| |
| /** |
| * Returns true if the condition needs to be mirrored to get to a canonical condition. The |
| * result of the mirroring operation might still need to be negated to achieve a canonical form. |
| */ |
| public boolean canonicalMirror() { |
| switch (this) { |
| case EQ: |
| return false; |
| case NE: |
| return false; |
| case LT: |
| return false; |
| case LE: |
| return true; |
| case GT: |
| return true; |
| case GE: |
| return false; |
| case BT: |
| return false; |
| case BE: |
| return true; |
| case AT: |
| return true; |
| case AE: |
| return false; |
| } |
| throw new IllegalArgumentException(this.toString()); |
| } |
| |
| /** |
| * Returns true if the condition needs to be negated to get to a canonical condition. The result |
| * of the negation might still need to be mirrored to achieve a canonical form. |
| */ |
| public boolean canonicalNegate() { |
| switch (this) { |
| case EQ: |
| return false; |
| case NE: |
| return true; |
| case LT: |
| return false; |
| case LE: |
| return true; |
| case GT: |
| return false; |
| case GE: |
| return true; |
| case BT: |
| return false; |
| case BE: |
| return true; |
| case AT: |
| return false; |
| case AE: |
| return true; |
| } |
| throw new IllegalArgumentException(this.toString()); |
| } |
| |
| /** |
| * Negate this conditional. |
| * |
| * @return the condition that represents the negation |
| */ |
| public final Condition negate() { |
| switch (this) { |
| case EQ: |
| return NE; |
| case NE: |
| return EQ; |
| case LT: |
| return GE; |
| case LE: |
| return GT; |
| case GT: |
| return LE; |
| case GE: |
| return LT; |
| case BT: |
| return AE; |
| case BE: |
| return AT; |
| case AT: |
| return BE; |
| case AE: |
| return BT; |
| } |
| throw new IllegalArgumentException(this.toString()); |
| } |
| |
| public boolean implies(Condition other) { |
| if (other == this) { |
| return true; |
| } |
| switch (this) { |
| case EQ: |
| return other == LE || other == GE || other == BE || other == AE; |
| case NE: |
| return false; |
| case LT: |
| return other == LE || other == NE; |
| case LE: |
| return false; |
| case GT: |
| return other == GE || other == NE; |
| case GE: |
| return false; |
| case BT: |
| return other == BE || other == NE; |
| case BE: |
| return false; |
| case AT: |
| return other == AE || other == NE; |
| case AE: |
| return false; |
| } |
| throw new IllegalArgumentException(this.toString()); |
| } |
| |
| /** |
| * Mirror this conditional (i.e. commute "a op b" to "b op' a") |
| * |
| * @return the condition representing the equivalent commuted operation |
| */ |
| public final Condition mirror() { |
| switch (this) { |
| case EQ: |
| return EQ; |
| case NE: |
| return NE; |
| case LT: |
| return GT; |
| case LE: |
| return GE; |
| case GT: |
| return LT; |
| case GE: |
| return LE; |
| case BT: |
| return AT; |
| case BE: |
| return AE; |
| case AT: |
| return BT; |
| case AE: |
| return BE; |
| } |
| throw new IllegalArgumentException(); |
| } |
| |
| /** |
| * Returns true if this condition represents an unsigned comparison. EQ and NE are not |
| * considered to be unsigned. |
| */ |
| public final boolean isUnsigned() { |
| return this == Condition.BT || this == Condition.BE || this == Condition.AT || this == Condition.AE; |
| } |
| |
| /** |
| * Checks if this conditional operation is commutative. |
| * |
| * @return {@code true} if this operation is commutative |
| */ |
| public final boolean isCommutative() { |
| return this == EQ || this == NE; |
| } |
| |
| /** |
| * Attempts to fold a comparison between two constants and return the result. |
| * |
| * @param lt the constant on the left side of the comparison |
| * @param rt the constant on the right side of the comparison |
| * @param constantReflection needed to compare constants |
| * @return {@link Boolean#TRUE} if the comparison is known to be true, {@link Boolean#FALSE} if |
| * the comparison is known to be false |
| */ |
| public boolean foldCondition(JavaConstant lt, JavaConstant rt, ConstantReflectionProvider constantReflection) { |
| assert !lt.getJavaKind().isNumericFloat() && !rt.getJavaKind().isNumericFloat(); |
| return foldCondition(lt, rt, constantReflection, false); |
| } |
| |
| /** |
| * Attempts to fold a comparison between two constants and return the result. |
| * |
| * @param lt the constant on the left side of the comparison |
| * @param rt the constant on the right side of the comparison |
| * @param constantReflection needed to compare constants |
| * @param unorderedIsTrue true if an undecided float comparison should result in "true" |
| * @return true if the comparison is known to be true, false if the comparison is known to be |
| * false |
| */ |
| public boolean foldCondition(Constant lt, Constant rt, ConstantReflectionProvider constantReflection, boolean unorderedIsTrue) { |
| if (lt instanceof PrimitiveConstant) { |
| PrimitiveConstant lp = (PrimitiveConstant) lt; |
| PrimitiveConstant rp = (PrimitiveConstant) rt; |
| switch (lp.getJavaKind()) { |
| case Boolean: |
| case Byte: |
| case Char: |
| case Short: |
| case Int: { |
| int x = lp.asInt(); |
| int y = rp.asInt(); |
| switch (this) { |
| case EQ: |
| return x == y; |
| case NE: |
| return x != y; |
| case LT: |
| return x < y; |
| case LE: |
| return x <= y; |
| case GT: |
| return x > y; |
| case GE: |
| return x >= y; |
| case AE: |
| return UnsignedMath.aboveOrEqual(x, y); |
| case BE: |
| return UnsignedMath.belowOrEqual(x, y); |
| case AT: |
| return UnsignedMath.aboveThan(x, y); |
| case BT: |
| return UnsignedMath.belowThan(x, y); |
| default: |
| throw new GraalError("expected condition: %s", this); |
| } |
| } |
| case Long: { |
| long x = lp.asLong(); |
| long y = rp.asLong(); |
| switch (this) { |
| case EQ: |
| return x == y; |
| case NE: |
| return x != y; |
| case LT: |
| return x < y; |
| case LE: |
| return x <= y; |
| case GT: |
| return x > y; |
| case GE: |
| return x >= y; |
| case AE: |
| return UnsignedMath.aboveOrEqual(x, y); |
| case BE: |
| return UnsignedMath.belowOrEqual(x, y); |
| case AT: |
| return UnsignedMath.aboveThan(x, y); |
| case BT: |
| return UnsignedMath.belowThan(x, y); |
| default: |
| throw new GraalError("expected condition: %s", this); |
| } |
| } |
| case Float: { |
| float x = lp.asFloat(); |
| float y = rp.asFloat(); |
| if (Float.isNaN(x) || Float.isNaN(y)) { |
| return unorderedIsTrue; |
| } |
| switch (this) { |
| case EQ: |
| return x == y; |
| case NE: |
| return x != y; |
| case LT: |
| return x < y; |
| case LE: |
| return x <= y; |
| case GT: |
| return x > y; |
| case GE: |
| return x >= y; |
| default: |
| throw new GraalError("expected condition: %s", this); |
| } |
| } |
| case Double: { |
| double x = lp.asDouble(); |
| double y = rp.asDouble(); |
| if (Double.isNaN(x) || Double.isNaN(y)) { |
| return unorderedIsTrue; |
| } |
| switch (this) { |
| case EQ: |
| return x == y; |
| case NE: |
| return x != y; |
| case LT: |
| return x < y; |
| case LE: |
| return x <= y; |
| case GT: |
| return x > y; |
| case GE: |
| return x >= y; |
| default: |
| throw new GraalError("expected condition: %s", this); |
| } |
| } |
| default: |
| throw new GraalError("expected value kind %s while folding condition: %s", lp.getJavaKind(), this); |
| } |
| } else { |
| Boolean equal = constantReflection.constantEquals(lt, rt); |
| if (equal == null) { |
| throw new GraalError("could not fold %s %s %s", lt, this, rt); |
| } |
| switch (this) { |
| case EQ: |
| return equal.booleanValue(); |
| case NE: |
| return !equal.booleanValue(); |
| default: |
| throw new GraalError("expected condition: %s", this); |
| } |
| } |
| } |
| |
| public Condition join(Condition other) { |
| if (other == this) { |
| return this; |
| } |
| switch (this) { |
| case EQ: |
| if (other == LE || other == GE || other == BE || other == AE) { |
| return EQ; |
| } else { |
| return null; |
| } |
| case NE: |
| if (other == LT || other == GT || other == BT || other == AT) { |
| return other; |
| } else if (other == LE) { |
| return LT; |
| } else if (other == GE) { |
| return GT; |
| } else if (other == BE) { |
| return BT; |
| } else if (other == AE) { |
| return AT; |
| } else { |
| return null; |
| } |
| case LE: |
| if (other == GE || other == EQ) { |
| return EQ; |
| } else if (other == NE || other == LT) { |
| return LT; |
| } else { |
| return null; |
| } |
| case LT: |
| if (other == NE || other == LE) { |
| return LT; |
| } else { |
| return null; |
| } |
| case GE: |
| if (other == LE || other == EQ) { |
| return EQ; |
| } else if (other == NE || other == GT) { |
| return GT; |
| } else { |
| return null; |
| } |
| case GT: |
| if (other == NE || other == GE) { |
| return GT; |
| } else { |
| return null; |
| } |
| case BE: |
| if (other == AE || other == EQ) { |
| return EQ; |
| } else if (other == NE || other == BT) { |
| return BT; |
| } else { |
| return null; |
| } |
| case BT: |
| if (other == NE || other == BE) { |
| return BT; |
| } else { |
| return null; |
| } |
| case AE: |
| if (other == BE || other == EQ) { |
| return EQ; |
| } else if (other == NE || other == AT) { |
| return AT; |
| } else { |
| return null; |
| } |
| case AT: |
| if (other == NE || other == AE) { |
| return AT; |
| } else { |
| return null; |
| } |
| } |
| throw new IllegalArgumentException(this.toString()); |
| } |
| |
| public Condition meet(Condition other) { |
| if (other == this) { |
| return this; |
| } |
| switch (this) { |
| case EQ: |
| if (other == LE || other == GE || other == BE || other == AE) { |
| return other; |
| } else if (other == LT) { |
| return LE; |
| } else if (other == GT) { |
| return GE; |
| } else if (other == BT) { |
| return BE; |
| } else if (other == AT) { |
| return AE; |
| } else { |
| return null; |
| } |
| case NE: |
| if (other == LT || other == GT || other == BT || other == AT) { |
| return NE; |
| } else { |
| return null; |
| } |
| case LE: |
| if (other == EQ || other == LT) { |
| return LE; |
| } else { |
| return null; |
| } |
| case LT: |
| if (other == EQ || other == LE) { |
| return LE; |
| } else if (other == NE || other == GT) { |
| return NE; |
| } else { |
| return null; |
| } |
| case GE: |
| if (other == EQ || other == GT) { |
| return GE; |
| } else { |
| return null; |
| } |
| case GT: |
| if (other == EQ || other == GE) { |
| return GE; |
| } else if (other == NE || other == LT) { |
| return NE; |
| } else { |
| return null; |
| } |
| case BE: |
| if (other == EQ || other == BT) { |
| return BE; |
| } else { |
| return null; |
| } |
| case BT: |
| if (other == EQ || other == BE) { |
| return BE; |
| } else if (other == NE || other == AT) { |
| return NE; |
| } else { |
| return null; |
| } |
| case AE: |
| if (other == EQ || other == AT) { |
| return AE; |
| } else { |
| return null; |
| } |
| case AT: |
| if (other == EQ || other == AE) { |
| return AE; |
| } else if (other == NE || other == BT) { |
| return NE; |
| } else { |
| return null; |
| } |
| } |
| throw new IllegalArgumentException(this.toString()); |
| } |
| } |