/* * Copyright (c) 2014, 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.type; import static jdk.vm.ci.meta.MetaUtil.getSimpleName; import java.util.Arrays; import java.util.Objects; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; import jdk.vm.ci.meta.Constant; import jdk.vm.ci.meta.JavaKind; import org.graalvm.compiler.core.common.calc.FloatConvert; import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.Add; import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.And; import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.Div; import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.Mul; import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.Or; import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.Rem; import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.Sub; import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.Xor; import org.graalvm.compiler.core.common.type.ArithmeticOpTable.IntegerConvertOp.Narrow; import org.graalvm.compiler.core.common.type.ArithmeticOpTable.IntegerConvertOp.SignExtend; import org.graalvm.compiler.core.common.type.ArithmeticOpTable.IntegerConvertOp.ZeroExtend; import org.graalvm.compiler.core.common.type.ArithmeticOpTable.ShiftOp.Shl; import org.graalvm.compiler.core.common.type.ArithmeticOpTable.ShiftOp.Shr; import org.graalvm.compiler.core.common.type.ArithmeticOpTable.ShiftOp.UShr; import org.graalvm.compiler.core.common.type.ArithmeticOpTable.UnaryOp.Abs; import org.graalvm.compiler.core.common.type.ArithmeticOpTable.UnaryOp.Neg; import org.graalvm.compiler.core.common.type.ArithmeticOpTable.UnaryOp.Not; import org.graalvm.compiler.core.common.type.ArithmeticOpTable.UnaryOp.Sqrt; /** * Information about arithmetic operations. */ public final class ArithmeticOpTable { private final UnaryOp neg; private final BinaryOp add; private final BinaryOp sub; private final BinaryOp mul; private final BinaryOp
div; private final BinaryOp rem; private final UnaryOp not; private final BinaryOp and; private final BinaryOp or; private final BinaryOp xor; private final ShiftOp shl; private final ShiftOp shr; private final ShiftOp ushr; private final UnaryOp abs; private final UnaryOp sqrt; private final IntegerConvertOp zeroExtend; private final IntegerConvertOp signExtend; private final IntegerConvertOp narrow; private final FloatConvertOp[] floatConvert; private final int hash; public static ArithmeticOpTable forStamp(Stamp s) { if (s instanceof ArithmeticStamp) { return ((ArithmeticStamp) s).getOps(); } else { return EMPTY; } } public static final ArithmeticOpTable EMPTY = new ArithmeticOpTable(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null); public ArithmeticOpTable(UnaryOp neg, BinaryOp add, BinaryOp sub, BinaryOp mul, BinaryOp
div, BinaryOp rem, UnaryOp not, BinaryOp and, BinaryOp or, BinaryOp xor, ShiftOp shl, ShiftOp shr, ShiftOp ushr, UnaryOp abs, UnaryOp sqrt, IntegerConvertOp zeroExtend, IntegerConvertOp signExtend, IntegerConvertOp narrow, FloatConvertOp... floatConvert) { this(neg, add, sub, mul, div, rem, not, and, or, xor, shl, shr, ushr, abs, sqrt, zeroExtend, signExtend, narrow, Stream.of(floatConvert)); } public interface ArithmeticOpWrapper { UnaryOp wrapUnaryOp(UnaryOp op); BinaryOp wrapBinaryOp(BinaryOp op); ShiftOp wrapShiftOp(ShiftOp op); IntegerConvertOp wrapIntegerConvertOp(IntegerConvertOp op); FloatConvertOp wrapFloatConvertOp(FloatConvertOp op); } private static T wrapIfNonNull(Function wrapper, T obj) { if (obj == null) { return null; } else { return wrapper.apply(obj); } } public static ArithmeticOpTable wrap(ArithmeticOpWrapper wrapper, ArithmeticOpTable inner) { UnaryOp neg = wrapIfNonNull(wrapper::wrapUnaryOp, inner.getNeg()); BinaryOp add = wrapIfNonNull(wrapper::wrapBinaryOp, inner.getAdd()); BinaryOp sub = wrapIfNonNull(wrapper::wrapBinaryOp, inner.getSub()); BinaryOp mul = wrapIfNonNull(wrapper::wrapBinaryOp, inner.getMul()); BinaryOp
div = wrapIfNonNull(wrapper::wrapBinaryOp, inner.getDiv()); BinaryOp rem = wrapIfNonNull(wrapper::wrapBinaryOp, inner.getRem()); UnaryOp not = wrapIfNonNull(wrapper::wrapUnaryOp, inner.getNot()); BinaryOp and = wrapIfNonNull(wrapper::wrapBinaryOp, inner.getAnd()); BinaryOp or = wrapIfNonNull(wrapper::wrapBinaryOp, inner.getOr()); BinaryOp xor = wrapIfNonNull(wrapper::wrapBinaryOp, inner.getXor()); ShiftOp shl = wrapIfNonNull(wrapper::wrapShiftOp, inner.getShl()); ShiftOp shr = wrapIfNonNull(wrapper::wrapShiftOp, inner.getShr()); ShiftOp ushr = wrapIfNonNull(wrapper::wrapShiftOp, inner.getUShr()); UnaryOp abs = wrapIfNonNull(wrapper::wrapUnaryOp, inner.getAbs()); UnaryOp sqrt = wrapIfNonNull(wrapper::wrapUnaryOp, inner.getSqrt()); IntegerConvertOp zeroExtend = wrapIfNonNull(wrapper::wrapIntegerConvertOp, inner.getZeroExtend()); IntegerConvertOp signExtend = wrapIfNonNull(wrapper::wrapIntegerConvertOp, inner.getSignExtend()); IntegerConvertOp narrow = wrapIfNonNull(wrapper::wrapIntegerConvertOp, inner.getNarrow()); Stream floatConvert = Stream.of(inner.floatConvert).filter(Objects::nonNull).map(wrapper::wrapFloatConvertOp); return new ArithmeticOpTable(neg, add, sub, mul, div, rem, not, and, or, xor, shl, shr, ushr, abs, sqrt, zeroExtend, signExtend, narrow, floatConvert); } private ArithmeticOpTable(UnaryOp neg, BinaryOp add, BinaryOp sub, BinaryOp mul, BinaryOp
div, BinaryOp rem, UnaryOp not, BinaryOp and, BinaryOp or, BinaryOp xor, ShiftOp shl, ShiftOp shr, ShiftOp ushr, UnaryOp abs, UnaryOp sqrt, IntegerConvertOp zeroExtend, IntegerConvertOp signExtend, IntegerConvertOp narrow, Stream floatConvert) { this.neg = neg; this.add = add; this.sub = sub; this.mul = mul; this.div = div; this.rem = rem; this.not = not; this.and = and; this.or = or; this.xor = xor; this.shl = shl; this.shr = shr; this.ushr = ushr; this.abs = abs; this.sqrt = sqrt; this.zeroExtend = zeroExtend; this.signExtend = signExtend; this.narrow = narrow; this.floatConvert = new FloatConvertOp[FloatConvert.values().length]; floatConvert.forEach(op -> this.floatConvert[op.getFloatConvert().ordinal()] = op); this.hash = Objects.hash(neg, add, sub, mul, div, rem, not, and, or, xor, shl, shr, ushr, abs, sqrt, zeroExtend, signExtend, narrow); } @Override public int hashCode() { return hash; } /** * Describes the unary negation operation. */ public UnaryOp getNeg() { return neg; } /** * Describes the addition operation. */ public BinaryOp getAdd() { return add; } /** * Describes the subtraction operation. */ public BinaryOp getSub() { return sub; } /** * Describes the multiplication operation. */ public BinaryOp getMul() { return mul; } /** * Describes the division operation. */ public BinaryOp
getDiv() { return div; } /** * Describes the remainder operation. */ public BinaryOp getRem() { return rem; } /** * Describes the bitwise not operation. */ public UnaryOp getNot() { return not; } /** * Describes the bitwise and operation. */ public BinaryOp getAnd() { return and; } /** * Describes the bitwise or operation. */ public BinaryOp getOr() { return or; } /** * Describes the bitwise xor operation. */ public BinaryOp getXor() { return xor; } /** * Describes the shift left operation. */ public ShiftOp getShl() { return shl; } /** * Describes the signed shift right operation. */ public ShiftOp getShr() { return shr; } /** * Describes the unsigned shift right operation. */ public ShiftOp getUShr() { return ushr; } /** * Describes the absolute value operation. */ public UnaryOp getAbs() { return abs; } /** * Describes the square root operation. */ public UnaryOp getSqrt() { return sqrt; } /** * Describes the zero extend conversion. */ public IntegerConvertOp getZeroExtend() { return zeroExtend; } /** * Describes the sign extend conversion. */ public IntegerConvertOp getSignExtend() { return signExtend; } /** * Describes the narrowing conversion. */ public IntegerConvertOp getNarrow() { return narrow; } /** * Describes integer/float/double conversions. */ public FloatConvertOp getFloatConvert(FloatConvert op) { return floatConvert[op.ordinal()]; } public static String toString(Op... ops) { return Arrays.asList(ops).stream().map(o -> o == null ? "null" : o.operator + "{" + getSimpleName(o.getClass(), false) + "}").collect(Collectors.joining(",")); } private boolean opsEquals(ArithmeticOpTable that) { // @formatter:off return Objects.equals(neg, that.neg) && Objects.equals(add, that.add) && Objects.equals(sub, that.sub) && Objects.equals(mul, that.mul) && Objects.equals(div, that.div) && Objects.equals(rem, that.rem) && Objects.equals(not, that.not) && Objects.equals(and, that.and) && Objects.equals(or, that.or) && Objects.equals(xor, that.xor) && Objects.equals(shl, that.shl) && Objects.equals(shr, that.shr) && Objects.equals(ushr, that.ushr) && Objects.equals(abs, that.abs) && Objects.equals(sqrt, that.sqrt) && Objects.equals(zeroExtend, that.zeroExtend) && Objects.equals(signExtend, that.signExtend) && Objects.equals(narrow, that.narrow); // @formatter:on } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } ArithmeticOpTable that = (ArithmeticOpTable) obj; if (opsEquals(that)) { if (Arrays.equals(this.floatConvert, that.floatConvert)) { return true; } } return false; } @Override public String toString() { return getClass().getSimpleName() + "[" + toString(neg, add, sub, mul, div, rem, not, and, or, xor, shl, shr, ushr, abs, sqrt, zeroExtend, signExtend, narrow) + ",floatConvert[" + toString(floatConvert) + "]]"; } public abstract static class Op { private final String operator; protected Op(String operator) { this.operator = operator; } @Override public String toString() { return operator; } @Override public int hashCode() { return operator.hashCode(); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } Op that = (Op) obj; if (operator.equals(that.operator)) { return true; } return true; } } /** * Describes a unary arithmetic operation. */ public abstract static class UnaryOp extends Op { public abstract static class Neg extends UnaryOp { protected Neg() { super("-"); } } public abstract static class Not extends UnaryOp { protected Not() { super("~"); } } public abstract static class Abs extends UnaryOp { protected Abs() { super("ABS"); } } public abstract static class Sqrt extends UnaryOp { protected Sqrt() { super("SQRT"); } } protected UnaryOp(String operation) { super(operation); } /** * Apply the operation to a {@link Constant}. */ public abstract Constant foldConstant(Constant value); /** * Apply the operation to a {@link Stamp}. */ public abstract Stamp foldStamp(Stamp stamp); public UnaryOp unwrap() { return this; } } /** * Describes a binary arithmetic operation. */ public abstract static class BinaryOp extends Op { public abstract static class Add extends BinaryOp { protected Add(boolean associative, boolean commutative) { super("+", associative, commutative); } } public abstract static class Sub extends BinaryOp { protected Sub(boolean associative, boolean commutative) { super("-", associative, commutative); } } public abstract static class Mul extends BinaryOp { protected Mul(boolean associative, boolean commutative) { super("*", associative, commutative); } } public abstract static class Div extends BinaryOp
{ protected Div(boolean associative, boolean commutative) { super("/", associative, commutative); } } public abstract static class Rem extends BinaryOp { protected Rem(boolean associative, boolean commutative) { super("%", associative, commutative); } } public abstract static class And extends BinaryOp { protected And(boolean associative, boolean commutative) { super("&", associative, commutative); } } public abstract static class Or extends BinaryOp { protected Or(boolean associative, boolean commutative) { super("|", associative, commutative); } } public abstract static class Xor extends BinaryOp { protected Xor(boolean associative, boolean commutative) { super("^", associative, commutative); } } private final boolean associative; private final boolean commutative; protected BinaryOp(String operation, boolean associative, boolean commutative) { super(operation); this.associative = associative; this.commutative = commutative; } /** * Apply the operation to two {@linkplain Constant Constants}. */ public abstract Constant foldConstant(Constant a, Constant b); /** * Apply the operation to two {@linkplain Stamp Stamps}. */ public abstract Stamp foldStamp(Stamp a, Stamp b); /** * Checks whether this operation is associative. An operation is associative when * {@code (a . b) . c == a . (b . c)} for all a, b, c. Note that you still have to be * careful with inverses. For example the integer subtraction operation will report * {@code true} here, since you can still reassociate as long as the correct negations are * inserted. */ public final boolean isAssociative() { return associative; } /** * Checks whether this operation is commutative. An operation is commutative when * {@code a . b == b . a} for all a, b. */ public final boolean isCommutative() { return commutative; } /** * Check whether a {@link Constant} is a neutral element for this operation. A neutral * element is any element {@code n} where {@code a . n == a} for all a. * * @param n the {@link Constant} that should be tested * @return true iff for all {@code a}: {@code a . n == a} */ public boolean isNeutral(Constant n) { return false; } /** * Check whether this operation has a zero {@code z == a . a} for each a. Examples of * operations having such an element are subtraction and exclusive-or. Note that this may be * different from the numbers tested by {@link #isNeutral}. * * @param stamp a {@link Stamp} * @return a unique {@code z} such that {@code z == a . a} for each {@code a} in * {@code stamp} if it exists, otherwise {@code null} */ public Constant getZero(Stamp stamp) { return null; } public BinaryOp unwrap() { return this; } @Override public int hashCode() { final int prime = 31; int result = super.hashCode(); result = prime * result + (associative ? 1231 : 1237); result = prime * result + (commutative ? 1231 : 1237); return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!super.equals(obj)) { return false; } if (getClass() != obj.getClass()) { return false; } BinaryOp that = (BinaryOp) obj; if (associative != that.associative) { return false; } if (commutative != that.commutative) { return false; } return true; } @Override public String toString() { if (associative) { if (commutative) { return super.toString() + "[AC]"; } else { return super.toString() + "[A]"; } } else if (commutative) { return super.toString() + "[C]"; } return super.toString(); } } /** * Describes a shift operation. The right argument of a shift operation always has kind * {@link JavaKind#Int}. */ public abstract static class ShiftOp extends Op { public abstract static class Shl extends ShiftOp { public Shl() { super("<<"); } } public abstract static class Shr extends ShiftOp { public Shr() { super(">>"); } } public abstract static class UShr extends ShiftOp { public UShr() { super(">>>"); } } protected ShiftOp(String operation) { super(operation); } /** * Apply the shift to a constant. */ public abstract Constant foldConstant(Constant c, int amount); /** * Apply the shift to a stamp. */ public abstract Stamp foldStamp(Stamp s, IntegerStamp amount); /** * Get the shift amount mask for a given result stamp. */ public abstract int getShiftAmountMask(Stamp s); } public abstract static class FloatConvertOp extends UnaryOp { private final FloatConvert op; protected FloatConvertOp(FloatConvert op) { super(op.name()); this.op = op; } public FloatConvert getFloatConvert() { return op; } @Override public FloatConvertOp unwrap() { return this; } @Override public int hashCode() { final int prime = 31; return prime * super.hashCode() + op.hashCode(); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!super.equals(obj)) { return false; } if (getClass() != obj.getClass()) { return false; } FloatConvertOp that = (FloatConvertOp) obj; if (op != that.op) { return false; } return true; } } public abstract static class IntegerConvertOp extends Op { public abstract static class ZeroExtend extends IntegerConvertOp { protected ZeroExtend() { super("ZeroExtend"); } } public abstract static class SignExtend extends IntegerConvertOp { protected SignExtend() { super("SignExtend"); } } public abstract static class Narrow extends IntegerConvertOp { protected Narrow() { super("Narrow"); } } protected IntegerConvertOp(String op) { super(op); } public abstract Constant foldConstant(int inputBits, int resultBits, Constant value); public abstract Stamp foldStamp(int inputBits, int resultBits, Stamp stamp); public IntegerConvertOp unwrap() { return this; } } }