1 /*
   2  * Copyright (c) 2014, 2015, 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 package org.graalvm.compiler.core.common.type;
  24 
  25 import static jdk.vm.ci.meta.MetaUtil.getSimpleName;
  26 
  27 import java.util.Arrays;
  28 import java.util.Objects;
  29 import java.util.function.Function;
  30 
  31 import org.graalvm.compiler.core.common.calc.FloatConvert;
  32 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.Add;
  33 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.And;
  34 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.Div;
  35 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.Mul;
  36 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.MulHigh;
  37 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.Or;
  38 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.Rem;
  39 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.Sub;
  40 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.UMulHigh;
  41 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.Xor;
  42 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.IntegerConvertOp.Narrow;
  43 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.IntegerConvertOp.SignExtend;
  44 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.IntegerConvertOp.ZeroExtend;
  45 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.ShiftOp.Shl;
  46 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.ShiftOp.Shr;
  47 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.ShiftOp.UShr;
  48 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.UnaryOp.Abs;
  49 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.UnaryOp.Neg;
  50 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.UnaryOp.Not;
  51 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.UnaryOp.Sqrt;
  52 import org.graalvm.util.CollectionsUtil;
  53 
  54 import jdk.vm.ci.meta.Constant;
  55 import jdk.vm.ci.meta.JavaKind;
  56 
  57 /**
  58  * Information about arithmetic operations.
  59  */
  60 public final class ArithmeticOpTable {
  61 
  62     private final UnaryOp<Neg> neg;
  63     private final BinaryOp<Add> add;
  64     private final BinaryOp<Sub> sub;
  65 
  66     private final BinaryOp<Mul> mul;
  67     private final BinaryOp<MulHigh> mulHigh;
  68     private final BinaryOp<UMulHigh> umulHigh;
  69     private final BinaryOp<Div> div;
  70     private final BinaryOp<Rem> rem;
  71 
  72     private final UnaryOp<Not> not;
  73     private final BinaryOp<And> and;
  74     private final BinaryOp<Or> or;
  75     private final BinaryOp<Xor> xor;
  76 
  77     private final ShiftOp<Shl> shl;
  78     private final ShiftOp<Shr> shr;
  79     private final ShiftOp<UShr> ushr;
  80 
  81     private final UnaryOp<Abs> abs;
  82     private final UnaryOp<Sqrt> sqrt;
  83 
  84     private final IntegerConvertOp<ZeroExtend> zeroExtend;
  85     private final IntegerConvertOp<SignExtend> signExtend;
  86     private final IntegerConvertOp<Narrow> narrow;
  87 
  88     private final FloatConvertOp[] floatConvert;
  89     private final int hash;
  90 
  91     public static ArithmeticOpTable forStamp(Stamp s) {
  92         if (s instanceof ArithmeticStamp) {
  93             return ((ArithmeticStamp) s).getOps();
  94         } else {
  95             return EMPTY;
  96         }
  97     }
  98 
  99     public BinaryOp<?>[] getBinaryOps() {
 100         return new BinaryOp<?>[]{add, sub, mul, mulHigh, umulHigh, div, rem, and, or, xor};
 101     }
 102 
 103     public UnaryOp<?>[] getUnaryOps() {
 104         return new UnaryOp<?>[]{neg, not, abs, sqrt};
 105     }
 106 
 107     public ShiftOp<?>[] getShiftOps() {
 108         return new ShiftOp<?>[]{shl, shr, ushr};
 109     }
 110 
 111     public IntegerConvertOp<?>[] getIntegerConvertOps() {
 112         return new IntegerConvertOp<?>[]{zeroExtend, signExtend, narrow};
 113     }
 114 
 115     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, null, null);
 116 
 117     public interface ArithmeticOpWrapper {
 118 
 119         <OP> UnaryOp<OP> wrapUnaryOp(UnaryOp<OP> op);
 120 
 121         <OP> BinaryOp<OP> wrapBinaryOp(BinaryOp<OP> op);
 122 
 123         <OP> ShiftOp<OP> wrapShiftOp(ShiftOp<OP> op);
 124 
 125         <OP> IntegerConvertOp<OP> wrapIntegerConvertOp(IntegerConvertOp<OP> op);
 126 
 127         FloatConvertOp wrapFloatConvertOp(FloatConvertOp op);
 128     }
 129 
 130     private static <T> T wrapIfNonNull(Function<T, T> wrapper, T obj) {
 131         if (obj == null) {
 132             return null;
 133         } else {
 134             return wrapper.apply(obj);
 135         }
 136     }
 137 
 138     public static ArithmeticOpTable wrap(ArithmeticOpWrapper wrapper, ArithmeticOpTable inner) {
 139         UnaryOp<Neg> neg = wrapIfNonNull(wrapper::wrapUnaryOp, inner.getNeg());
 140         BinaryOp<Add> add = wrapIfNonNull(wrapper::wrapBinaryOp, inner.getAdd());
 141         BinaryOp<Sub> sub = wrapIfNonNull(wrapper::wrapBinaryOp, inner.getSub());
 142 
 143         BinaryOp<Mul> mul = wrapIfNonNull(wrapper::wrapBinaryOp, inner.getMul());
 144         BinaryOp<MulHigh> mulHigh = wrapIfNonNull(wrapper::wrapBinaryOp, inner.getMulHigh());
 145         BinaryOp<UMulHigh> umulHigh = wrapIfNonNull(wrapper::wrapBinaryOp, inner.getUMulHigh());
 146         BinaryOp<Div> div = wrapIfNonNull(wrapper::wrapBinaryOp, inner.getDiv());
 147         BinaryOp<Rem> rem = wrapIfNonNull(wrapper::wrapBinaryOp, inner.getRem());
 148 
 149         UnaryOp<Not> not = wrapIfNonNull(wrapper::wrapUnaryOp, inner.getNot());
 150         BinaryOp<And> and = wrapIfNonNull(wrapper::wrapBinaryOp, inner.getAnd());
 151         BinaryOp<Or> or = wrapIfNonNull(wrapper::wrapBinaryOp, inner.getOr());
 152         BinaryOp<Xor> xor = wrapIfNonNull(wrapper::wrapBinaryOp, inner.getXor());
 153 
 154         ShiftOp<Shl> shl = wrapIfNonNull(wrapper::wrapShiftOp, inner.getShl());
 155         ShiftOp<Shr> shr = wrapIfNonNull(wrapper::wrapShiftOp, inner.getShr());
 156         ShiftOp<UShr> ushr = wrapIfNonNull(wrapper::wrapShiftOp, inner.getUShr());
 157 
 158         UnaryOp<Abs> abs = wrapIfNonNull(wrapper::wrapUnaryOp, inner.getAbs());
 159         UnaryOp<Sqrt> sqrt = wrapIfNonNull(wrapper::wrapUnaryOp, inner.getSqrt());
 160 
 161         IntegerConvertOp<ZeroExtend> zeroExtend = wrapIfNonNull(wrapper::wrapIntegerConvertOp, inner.getZeroExtend());
 162         IntegerConvertOp<SignExtend> signExtend = wrapIfNonNull(wrapper::wrapIntegerConvertOp, inner.getSignExtend());
 163         IntegerConvertOp<Narrow> narrow = wrapIfNonNull(wrapper::wrapIntegerConvertOp, inner.getNarrow());
 164 
 165         FloatConvertOp[] floatConvert = CollectionsUtil.filterAndMapToArray(inner.floatConvert, Objects::nonNull, wrapper::wrapFloatConvertOp, FloatConvertOp[]::new);
 166         return new ArithmeticOpTable(neg, add, sub, mul, mulHigh, umulHigh, div, rem, not, and, or, xor, shl, shr, ushr, abs, sqrt, zeroExtend, signExtend, narrow, floatConvert);
 167     }
 168 
 169     protected ArithmeticOpTable(UnaryOp<Neg> neg, BinaryOp<Add> add, BinaryOp<Sub> sub, BinaryOp<Mul> mul, BinaryOp<MulHigh> mulHigh, BinaryOp<UMulHigh> umulHigh, BinaryOp<Div> div, BinaryOp<Rem> rem,
 170                     UnaryOp<Not> not, BinaryOp<And> and, BinaryOp<Or> or, BinaryOp<Xor> xor, ShiftOp<Shl> shl, ShiftOp<Shr> shr, ShiftOp<UShr> ushr, UnaryOp<Abs> abs, UnaryOp<Sqrt> sqrt,
 171                     IntegerConvertOp<ZeroExtend> zeroExtend, IntegerConvertOp<SignExtend> signExtend, IntegerConvertOp<Narrow> narrow, FloatConvertOp... floatConvert) {
 172         this.neg = neg;
 173         this.add = add;
 174         this.sub = sub;
 175         this.mul = mul;
 176         this.mulHigh = mulHigh;
 177         this.umulHigh = umulHigh;
 178         this.div = div;
 179         this.rem = rem;
 180         this.not = not;
 181         this.and = and;
 182         this.or = or;
 183         this.xor = xor;
 184         this.shl = shl;
 185         this.shr = shr;
 186         this.ushr = ushr;
 187         this.abs = abs;
 188         this.sqrt = sqrt;
 189         this.zeroExtend = zeroExtend;
 190         this.signExtend = signExtend;
 191         this.narrow = narrow;
 192         this.floatConvert = new FloatConvertOp[FloatConvert.values().length];
 193         for (FloatConvertOp op : floatConvert) {
 194             this.floatConvert[op.getFloatConvert().ordinal()] = op;
 195         }
 196 
 197         this.hash = Objects.hash(neg, add, sub, mul, div, rem, not, and, or, xor, shl, shr, ushr, abs, sqrt, zeroExtend, signExtend, narrow);
 198     }
 199 
 200     @Override
 201     public int hashCode() {
 202         return hash;
 203     }
 204 
 205     /**
 206      * Describes the unary negation operation.
 207      */
 208     public UnaryOp<Neg> getNeg() {
 209         return neg;
 210     }
 211 
 212     /**
 213      * Describes the addition operation.
 214      */
 215     public BinaryOp<Add> getAdd() {
 216         return add;
 217     }
 218 
 219     /**
 220      * Describes the subtraction operation.
 221      */
 222     public BinaryOp<Sub> getSub() {
 223         return sub;
 224     }
 225 
 226     /**
 227      * Describes the multiplication operation.
 228      */
 229     public BinaryOp<Mul> getMul() {
 230         return mul;
 231     }
 232 
 233     /**
 234      * Describes a signed operation that multiples the upper 32-bits of two long values.
 235      */
 236     public BinaryOp<MulHigh> getMulHigh() {
 237         return mulHigh;
 238     }
 239 
 240     /**
 241      * Describes an unsigned operation that multiples the upper 32-bits of two long values.
 242      */
 243     public BinaryOp<UMulHigh> getUMulHigh() {
 244         return umulHigh;
 245     }
 246 
 247     /**
 248      * Describes the division operation.
 249      */
 250     public BinaryOp<Div> getDiv() {
 251         return div;
 252     }
 253 
 254     /**
 255      * Describes the remainder operation.
 256      */
 257     public BinaryOp<Rem> getRem() {
 258         return rem;
 259     }
 260 
 261     /**
 262      * Describes the bitwise not operation.
 263      */
 264     public UnaryOp<Not> getNot() {
 265         return not;
 266     }
 267 
 268     /**
 269      * Describes the bitwise and operation.
 270      */
 271     public BinaryOp<And> getAnd() {
 272         return and;
 273     }
 274 
 275     /**
 276      * Describes the bitwise or operation.
 277      */
 278     public BinaryOp<Or> getOr() {
 279         return or;
 280     }
 281 
 282     /**
 283      * Describes the bitwise xor operation.
 284      */
 285     public BinaryOp<Xor> getXor() {
 286         return xor;
 287     }
 288 
 289     /**
 290      * Describes the shift left operation.
 291      */
 292     public ShiftOp<Shl> getShl() {
 293         return shl;
 294     }
 295 
 296     /**
 297      * Describes the signed shift right operation.
 298      */
 299     public ShiftOp<Shr> getShr() {
 300         return shr;
 301     }
 302 
 303     /**
 304      * Describes the unsigned shift right operation.
 305      */
 306     public ShiftOp<UShr> getUShr() {
 307         return ushr;
 308     }
 309 
 310     /**
 311      * Describes the absolute value operation.
 312      */
 313     public UnaryOp<Abs> getAbs() {
 314         return abs;
 315     }
 316 
 317     /**
 318      * Describes the square root operation.
 319      */
 320     public UnaryOp<Sqrt> getSqrt() {
 321         return sqrt;
 322     }
 323 
 324     /**
 325      * Describes the zero extend conversion.
 326      */
 327     public IntegerConvertOp<ZeroExtend> getZeroExtend() {
 328         return zeroExtend;
 329     }
 330 
 331     /**
 332      * Describes the sign extend conversion.
 333      */
 334     public IntegerConvertOp<SignExtend> getSignExtend() {
 335         return signExtend;
 336     }
 337 
 338     /**
 339      * Describes the narrowing conversion.
 340      */
 341     public IntegerConvertOp<Narrow> getNarrow() {
 342         return narrow;
 343     }
 344 
 345     /**
 346      * Describes integer/float/double conversions.
 347      */
 348     public FloatConvertOp getFloatConvert(FloatConvert op) {
 349         return floatConvert[op.ordinal()];
 350     }
 351 
 352     public static String toString(Op... ops) {
 353         return CollectionsUtil.mapAndJoin(ops, o -> o == null ? "null" : o.operator + "{" + getSimpleName(o.getClass(), false) + "}", ",");
 354     }
 355 
 356     private boolean opsEquals(ArithmeticOpTable that) {
 357         // @formatter:off
 358         return Objects.equals(neg, that.neg) &&
 359                Objects.equals(add, that.add) &&
 360                Objects.equals(sub, that.sub) &&
 361                Objects.equals(mul, that.mul) &&
 362                Objects.equals(mulHigh, that.mulHigh) &&
 363                Objects.equals(umulHigh, that.umulHigh) &&
 364                Objects.equals(div, that.div) &&
 365                Objects.equals(rem, that.rem) &&
 366                Objects.equals(not, that.not) &&
 367                Objects.equals(and, that.and) &&
 368                Objects.equals(or, that.or) &&
 369                Objects.equals(xor, that.xor) &&
 370                Objects.equals(shl, that.shl) &&
 371                Objects.equals(shr, that.shr) &&
 372                Objects.equals(ushr, that.ushr) &&
 373                Objects.equals(abs, that.abs) &&
 374                Objects.equals(sqrt, that.sqrt) &&
 375                Objects.equals(zeroExtend, that.zeroExtend) &&
 376                Objects.equals(signExtend, that.signExtend) &&
 377                Objects.equals(narrow, that.narrow);
 378         // @formatter:on
 379     }
 380 
 381     @Override
 382     public boolean equals(Object obj) {
 383         if (this == obj) {
 384             return true;
 385         }
 386         if (obj == null) {
 387             return false;
 388         }
 389         if (getClass() != obj.getClass()) {
 390             return false;
 391         }
 392         ArithmeticOpTable that = (ArithmeticOpTable) obj;
 393         if (opsEquals(that)) {
 394             if (Arrays.equals(this.floatConvert, that.floatConvert)) {
 395                 return true;
 396             }
 397         }
 398         return false;
 399     }
 400 
 401     @Override
 402     public String toString() {
 403         return getClass().getSimpleName() + "[" + toString(neg, add, sub, mul, mulHigh, umulHigh, div, rem, not, and, or, xor, shl, shr, ushr, abs, sqrt, zeroExtend, signExtend, narrow) +
 404                         ",floatConvert[" + toString(floatConvert) + "]]";
 405     }
 406 
 407     public abstract static class Op {
 408 
 409         private final String operator;
 410 
 411         protected Op(String operator) {
 412             this.operator = operator;
 413         }
 414 
 415         @Override
 416         public String toString() {
 417             return operator;
 418         }
 419 
 420         @Override
 421         public int hashCode() {
 422             return operator.hashCode();
 423         }
 424 
 425         @Override
 426         public boolean equals(Object obj) {
 427             if (this == obj) {
 428                 return true;
 429             }
 430             if (obj == null) {
 431                 return false;
 432             }
 433             if (getClass() != obj.getClass()) {
 434                 return false;
 435             }
 436             Op that = (Op) obj;
 437             if (operator.equals(that.operator)) {
 438                 return true;
 439             }
 440             return true;
 441         }
 442     }
 443 
 444     /**
 445      * Describes a unary arithmetic operation.
 446      */
 447     public abstract static class UnaryOp<T> extends Op {
 448 
 449         public abstract static class Neg extends UnaryOp<Neg> {
 450 
 451             protected Neg() {
 452                 super("-");
 453             }
 454         }
 455 
 456         public abstract static class Not extends UnaryOp<Not> {
 457 
 458             protected Not() {
 459                 super("~");
 460             }
 461         }
 462 
 463         public abstract static class Abs extends UnaryOp<Abs> {
 464 
 465             protected Abs() {
 466                 super("ABS");
 467             }
 468         }
 469 
 470         public abstract static class Sqrt extends UnaryOp<Sqrt> {
 471 
 472             protected Sqrt() {
 473                 super("SQRT");
 474             }
 475         }
 476 
 477         protected UnaryOp(String operation) {
 478             super(operation);
 479         }
 480 
 481         /**
 482          * Apply the operation to a {@link Constant}.
 483          */
 484         public abstract Constant foldConstant(Constant value);
 485 
 486         /**
 487          * Apply the operation to a {@link Stamp}.
 488          */
 489         public abstract Stamp foldStamp(Stamp stamp);
 490 
 491         public UnaryOp<T> unwrap() {
 492             return this;
 493         }
 494     }
 495 
 496     /**
 497      * Describes a binary arithmetic operation.
 498      */
 499     public abstract static class BinaryOp<T> extends Op {
 500 
 501         public abstract static class Add extends BinaryOp<Add> {
 502 
 503             protected Add(boolean associative, boolean commutative) {
 504                 super("+", associative, commutative);
 505             }
 506         }
 507 
 508         public abstract static class Sub extends BinaryOp<Sub> {
 509 
 510             protected Sub(boolean associative, boolean commutative) {
 511                 super("-", associative, commutative);
 512             }
 513         }
 514 
 515         public abstract static class Mul extends BinaryOp<Mul> {
 516 
 517             protected Mul(boolean associative, boolean commutative) {
 518                 super("*", associative, commutative);
 519             }
 520         }
 521 
 522         public abstract static class MulHigh extends BinaryOp<MulHigh> {
 523 
 524             protected MulHigh(boolean associative, boolean commutative) {
 525                 super("*H", associative, commutative);
 526             }
 527         }
 528 
 529         public abstract static class UMulHigh extends BinaryOp<UMulHigh> {
 530 
 531             protected UMulHigh(boolean associative, boolean commutative) {
 532                 super("|*H|", associative, commutative);
 533             }
 534         }
 535 
 536         public abstract static class Div extends BinaryOp<Div> {
 537 
 538             protected Div(boolean associative, boolean commutative) {
 539                 super("/", associative, commutative);
 540             }
 541         }
 542 
 543         public abstract static class Rem extends BinaryOp<Rem> {
 544 
 545             protected Rem(boolean associative, boolean commutative) {
 546                 super("%", associative, commutative);
 547             }
 548         }
 549 
 550         public abstract static class And extends BinaryOp<And> {
 551 
 552             protected And(boolean associative, boolean commutative) {
 553                 super("&", associative, commutative);
 554             }
 555         }
 556 
 557         public abstract static class Or extends BinaryOp<Or> {
 558 
 559             protected Or(boolean associative, boolean commutative) {
 560                 super("|", associative, commutative);
 561             }
 562         }
 563 
 564         public abstract static class Xor extends BinaryOp<Xor> {
 565 
 566             protected Xor(boolean associative, boolean commutative) {
 567                 super("^", associative, commutative);
 568             }
 569         }
 570 
 571         private final boolean associative;
 572         private final boolean commutative;
 573 
 574         protected BinaryOp(String operation, boolean associative, boolean commutative) {
 575             super(operation);
 576             this.associative = associative;
 577             this.commutative = commutative;
 578         }
 579 
 580         /**
 581          * Applies this operation to {@code a} and {@code b}.
 582          *
 583          * @return the result of applying this operation or {@code null} if applying it would raise
 584          *         an exception (e.g., {@link ArithmeticException} for dividing by 0)
 585          */
 586         public abstract Constant foldConstant(Constant a, Constant b);
 587 
 588         /**
 589          * Apply the operation to two {@linkplain Stamp Stamps}.
 590          */
 591         public abstract Stamp foldStamp(Stamp a, Stamp b);
 592 
 593         /**
 594          * Checks whether this operation is associative. An operation is associative when
 595          * {@code (a . b) . c == a . (b . c)} for all a, b, c. Note that you still have to be
 596          * careful with inverses. For example the integer subtraction operation will report
 597          * {@code true} here, since you can still reassociate as long as the correct negations are
 598          * inserted.
 599          */
 600         public final boolean isAssociative() {
 601             return associative;
 602         }
 603 
 604         /**
 605          * Checks whether this operation is commutative. An operation is commutative when
 606          * {@code a . b == b . a} for all a, b.
 607          */
 608         public final boolean isCommutative() {
 609             return commutative;
 610         }
 611 
 612         /**
 613          * Check whether a {@link Constant} is a neutral element for this operation. A neutral
 614          * element is any element {@code n} where {@code a . n == a} for all a.
 615          *
 616          * @param n the {@link Constant} that should be tested
 617          * @return true iff for all {@code a}: {@code a . n == a}
 618          */
 619         public boolean isNeutral(Constant n) {
 620             return false;
 621         }
 622 
 623         /**
 624          * Check whether this operation has a zero {@code z == a . a} for each a. Examples of
 625          * operations having such an element are subtraction and exclusive-or. Note that this may be
 626          * different from the numbers tested by {@link #isNeutral}.
 627          *
 628          * @param stamp a {@link Stamp}
 629          * @return a unique {@code z} such that {@code z == a . a} for each {@code a} in
 630          *         {@code stamp} if it exists, otherwise {@code null}
 631          */
 632         public Constant getZero(Stamp stamp) {
 633             return null;
 634         }
 635 
 636         public BinaryOp<T> unwrap() {
 637             return this;
 638         }
 639 
 640         @Override
 641         public int hashCode() {
 642             final int prime = 31;
 643             int result = super.hashCode();
 644             result = prime * result + (associative ? 1231 : 1237);
 645             result = prime * result + (commutative ? 1231 : 1237);
 646             return result;
 647         }
 648 
 649         @Override
 650         public boolean equals(Object obj) {
 651             if (this == obj) {
 652                 return true;
 653             }
 654             if (!super.equals(obj)) {
 655                 return false;
 656             }
 657             if (getClass() != obj.getClass()) {
 658                 return false;
 659             }
 660             BinaryOp<?> that = (BinaryOp<?>) obj;
 661             if (associative != that.associative) {
 662                 return false;
 663             }
 664             if (commutative != that.commutative) {
 665                 return false;
 666             }
 667             return true;
 668         }
 669 
 670         @Override
 671         public String toString() {
 672             if (associative) {
 673                 if (commutative) {
 674                     return super.toString() + "[AC]";
 675                 } else {
 676                     return super.toString() + "[A]";
 677                 }
 678             } else if (commutative) {
 679                 return super.toString() + "[C]";
 680             }
 681             return super.toString();
 682         }
 683     }
 684 
 685     /**
 686      * Describes a shift operation. The right argument of a shift operation always has kind
 687      * {@link JavaKind#Int}.
 688      */
 689     public abstract static class ShiftOp<OP> extends Op {
 690 
 691         public abstract static class Shl extends ShiftOp<Shl> {
 692 
 693             public Shl() {
 694                 super("<<");
 695             }
 696         }
 697 
 698         public abstract static class Shr extends ShiftOp<Shr> {
 699 
 700             public Shr() {
 701                 super(">>");
 702             }
 703         }
 704 
 705         public abstract static class UShr extends ShiftOp<UShr> {
 706 
 707             public UShr() {
 708                 super(">>>");
 709             }
 710         }
 711 
 712         protected ShiftOp(String operation) {
 713             super(operation);
 714         }
 715 
 716         /**
 717          * Apply the shift to a constant.
 718          */
 719         public abstract Constant foldConstant(Constant c, int amount);
 720 
 721         /**
 722          * Apply the shift to a stamp.
 723          */
 724         public abstract Stamp foldStamp(Stamp s, IntegerStamp amount);
 725 
 726         /**
 727          * Get the shift amount mask for a given result stamp.
 728          */
 729         public abstract int getShiftAmountMask(Stamp s);
 730     }
 731 
 732     public abstract static class FloatConvertOp extends UnaryOp<FloatConvertOp> {
 733 
 734         private final FloatConvert op;
 735 
 736         protected FloatConvertOp(FloatConvert op) {
 737             super(op.name());
 738             this.op = op;
 739         }
 740 
 741         public FloatConvert getFloatConvert() {
 742             return op;
 743         }
 744 
 745         @Override
 746         public FloatConvertOp unwrap() {
 747             return this;
 748         }
 749 
 750         @Override
 751         public int hashCode() {
 752             final int prime = 31;
 753             return prime * super.hashCode() + op.hashCode();
 754         }
 755 
 756         @Override
 757         public boolean equals(Object obj) {
 758             if (this == obj) {
 759                 return true;
 760             }
 761             if (!super.equals(obj)) {
 762                 return false;
 763             }
 764             if (getClass() != obj.getClass()) {
 765                 return false;
 766             }
 767             FloatConvertOp that = (FloatConvertOp) obj;
 768             if (op != that.op) {
 769                 return false;
 770             }
 771             return true;
 772         }
 773     }
 774 
 775     public abstract static class IntegerConvertOp<T> extends Op {
 776 
 777         public abstract static class ZeroExtend extends IntegerConvertOp<ZeroExtend> {
 778 
 779             protected ZeroExtend() {
 780                 super("ZeroExtend");
 781             }
 782         }
 783 
 784         public abstract static class SignExtend extends IntegerConvertOp<SignExtend> {
 785 
 786             protected SignExtend() {
 787                 super("SignExtend");
 788             }
 789         }
 790 
 791         public abstract static class Narrow extends IntegerConvertOp<Narrow> {
 792 
 793             protected Narrow() {
 794                 super("Narrow");
 795             }
 796 
 797             @Override
 798             public Stamp invertStamp(int inputBits, int resultBits, Stamp outStamp) {
 799                 return null;
 800             }
 801         }
 802 
 803         protected IntegerConvertOp(String op) {
 804             super(op);
 805         }
 806 
 807         public abstract Constant foldConstant(int inputBits, int resultBits, Constant value);
 808 
 809         public abstract Stamp foldStamp(int inputBits, int resultBits, Stamp stamp);
 810 
 811         public IntegerConvertOp<T> unwrap() {
 812             return this;
 813         }
 814 
 815         /**
 816          * Computes the stamp of the input for the given output stamp.
 817          */
 818         public abstract Stamp invertStamp(int inputBits, int resultBits, Stamp outStamp);
 819     }
 820 }