1 /*
   2  * Copyright (c) 2013, 2018, 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.replacements.nodes.arithmetic;
  26 
  27 import static org.graalvm.compiler.core.common.type.IntegerStamp.addOverflowsNegatively;
  28 import static org.graalvm.compiler.core.common.type.IntegerStamp.addOverflowsPositively;
  29 import static org.graalvm.compiler.core.common.type.IntegerStamp.carryBits;
  30 import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_2;
  31 import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_2;
  32 
  33 import org.graalvm.compiler.core.common.type.IntegerStamp;
  34 import org.graalvm.compiler.core.common.type.Stamp;
  35 import org.graalvm.compiler.core.common.type.StampFactory;
  36 import org.graalvm.compiler.graph.NodeClass;
  37 import org.graalvm.compiler.graph.spi.CanonicalizerTool;
  38 import org.graalvm.compiler.nodeinfo.InputType;
  39 import org.graalvm.compiler.nodeinfo.NodeInfo;
  40 import org.graalvm.compiler.nodes.AbstractBeginNode;
  41 import org.graalvm.compiler.nodes.ConstantNode;
  42 import org.graalvm.compiler.nodes.NodeView;
  43 import org.graalvm.compiler.nodes.ValueNode;
  44 import org.graalvm.compiler.nodes.calc.AddNode;
  45 import org.graalvm.compiler.nodes.extended.AnchoringNode;
  46 import org.graalvm.compiler.nodes.spi.LoweringTool;
  47 
  48 import jdk.vm.ci.code.CodeUtil;
  49 import jdk.vm.ci.meta.JavaConstant;
  50 import jdk.vm.ci.meta.JavaKind;
  51 import jdk.vm.ci.meta.SpeculationLog.SpeculationReason;
  52 
  53 /**
  54  * Node representing an exact integer addition that will throw an {@link ArithmeticException} in
  55  * case the addition would overflow the 32 bit range.
  56  */
  57 @NodeInfo(cycles = CYCLES_2, size = SIZE_2)
  58 public final class IntegerAddExactNode extends AddNode implements IntegerExactArithmeticNode {
  59     public static final NodeClass<IntegerAddExactNode> TYPE = NodeClass.create(IntegerAddExactNode.class);
  60 
  61     @OptionalInput(InputType.Anchor) protected AnchoringNode anchor;
  62     protected final SpeculationReason speculation;
  63 
  64     public IntegerAddExactNode(ValueNode x, ValueNode y, SpeculationReason speculation) {
  65         super(TYPE, x, y);
  66         setStamp(x.stamp(NodeView.DEFAULT).unrestricted());
  67         assert x.stamp(NodeView.DEFAULT).isCompatible(y.stamp(NodeView.DEFAULT)) && x.stamp(NodeView.DEFAULT) instanceof IntegerStamp;
  68         this.speculation = speculation;
  69     }
  70 
  71     @Override
  72     public boolean inferStamp() {
  73         /*
  74          * Note: it is not allowed to use the foldStamp method of the regular add node as we do not
  75          * know the result stamp of this node if we do not know whether we may deopt. If we know we
  76          * can never overflow we will replace this node with its non overflow checking counterpart
  77          * anyway.
  78          */
  79         return false;
  80     }
  81 
  82     @Override
  83     public Stamp foldStamp(Stamp stampX, Stamp stampY) {
  84         IntegerStamp a = (IntegerStamp) stampX;
  85         IntegerStamp b = (IntegerStamp) stampY;
  86 
  87         int bits = a.getBits();
  88         assert bits == b.getBits();
  89 
  90         long defaultMask = CodeUtil.mask(bits);
  91         long variableBits = (a.downMask() ^ a.upMask()) | (b.downMask() ^ b.upMask());
  92         long variableBitsWithCarry = variableBits | (carryBits(a.downMask(), b.downMask()) ^ carryBits(a.upMask(), b.upMask()));
  93         long newDownMask = (a.downMask() + b.downMask()) & ~variableBitsWithCarry;
  94         long newUpMask = (a.downMask() + b.downMask()) | variableBitsWithCarry;
  95 
  96         newDownMask &= defaultMask;
  97         newUpMask &= defaultMask;
  98 
  99         long newLowerBound;
 100         long newUpperBound;
 101         boolean lowerOverflowsPositively = addOverflowsPositively(a.lowerBound(), b.lowerBound(), bits);
 102         boolean upperOverflowsPositively = addOverflowsPositively(a.upperBound(), b.upperBound(), bits);
 103         boolean lowerOverflowsNegatively = addOverflowsNegatively(a.lowerBound(), b.lowerBound(), bits);
 104         boolean upperOverflowsNegatively = addOverflowsNegatively(a.upperBound(), b.upperBound(), bits);
 105         if (lowerOverflowsPositively) {
 106             newLowerBound = CodeUtil.maxValue(bits);
 107         } else if (lowerOverflowsNegatively) {
 108             newLowerBound = CodeUtil.minValue(bits);
 109         } else {
 110             newLowerBound = CodeUtil.signExtend((a.lowerBound() + b.lowerBound()) & defaultMask, bits);
 111         }
 112 
 113         if (upperOverflowsPositively) {
 114             newUpperBound = CodeUtil.maxValue(bits);
 115         } else if (upperOverflowsNegatively) {
 116             newUpperBound = CodeUtil.minValue(bits);
 117         } else {
 118             newUpperBound = CodeUtil.signExtend((a.upperBound() + b.upperBound()) & defaultMask, bits);
 119         }
 120 
 121         IntegerStamp limit = StampFactory.forInteger(bits, newLowerBound, newUpperBound);
 122         newUpMask &= limit.upMask();
 123         newUpperBound = CodeUtil.signExtend(newUpperBound & newUpMask, bits);
 124         newDownMask |= limit.downMask();
 125         newLowerBound |= newDownMask;
 126         return IntegerStamp.create(bits, newLowerBound, newUpperBound, newDownMask, newUpMask);
 127     }
 128 
 129     @Override
 130     public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) {
 131         if (forX.isConstant() && !forY.isConstant()) {
 132             return new IntegerAddExactNode(forY, forX, speculation).canonical(tool);
 133         }
 134         if (forX.isConstant()) {
 135             ConstantNode constantNode = canonicalXconstant(forX, forY);
 136             if (constantNode != null) {
 137                 return constantNode;
 138             }
 139         } else if (forY.isConstant()) {
 140             long c = forY.asJavaConstant().asLong();
 141             if (c == 0) {
 142                 return forX;
 143             }
 144         }
 145         if (!IntegerStamp.addCanOverflow((IntegerStamp) forX.stamp(NodeView.DEFAULT), (IntegerStamp) forY.stamp(NodeView.DEFAULT))) {
 146             return new AddNode(forX, forY).canonical(tool);
 147         }
 148         return this;
 149     }
 150 
 151     private static ConstantNode canonicalXconstant(ValueNode forX, ValueNode forY) {
 152         JavaConstant xConst = forX.asJavaConstant();
 153         JavaConstant yConst = forY.asJavaConstant();
 154         if (xConst != null && yConst != null) {
 155             assert xConst.getJavaKind() == yConst.getJavaKind();
 156             try {
 157                 if (xConst.getJavaKind() == JavaKind.Int) {
 158                     return ConstantNode.forInt(Math.addExact(xConst.asInt(), yConst.asInt()));
 159                 } else {
 160                     assert xConst.getJavaKind() == JavaKind.Long;
 161                     return ConstantNode.forLong(Math.addExact(xConst.asLong(), yConst.asLong()));
 162                 }
 163             } catch (ArithmeticException ex) {
 164                 // The operation will result in an overflow exception, so do not canonicalize.
 165             }
 166         }
 167         return null;
 168     }
 169 
 170     @Override
 171     public IntegerExactArithmeticSplitNode createSplit(AbstractBeginNode next, AbstractBeginNode deopt) {
 172         return graph().add(new IntegerAddExactSplitNode(stamp(NodeView.DEFAULT), getX(), getY(), next, deopt));
 173     }
 174 
 175     @Override
 176     public SpeculationReason getSpeculation() {
 177         return speculation;
 178     }
 179 
 180     @Override
 181     public AnchoringNode getAnchor() {
 182         return anchor;
 183     }
 184 
 185     @Override
 186     public void setAnchor(AnchoringNode x) {
 187         updateUsagesInterface(this.anchor, x);
 188         this.anchor = x;
 189     }
 190 
 191     @Override
 192     public void lower(LoweringTool tool) {
 193         IntegerExactArithmeticSplitNode.lower(tool, this);
 194     }
 195 }