1 /*
   2  * Copyright (c) 2012, 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;
  26 
  27 import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_64;
  28 import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_1;
  29 
  30 import jdk.vm.ci.meta.Value;
  31 import org.graalvm.compiler.core.common.spi.ForeignCallDescriptor;
  32 import org.graalvm.compiler.core.common.type.FloatStamp;
  33 import org.graalvm.compiler.core.common.type.PrimitiveStamp;
  34 import org.graalvm.compiler.core.common.type.Stamp;
  35 import org.graalvm.compiler.core.common.type.StampFactory;
  36 import org.graalvm.compiler.debug.GraalError;
  37 import org.graalvm.compiler.graph.NodeClass;
  38 import org.graalvm.compiler.graph.spi.CanonicalizerTool;
  39 import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool;
  40 import org.graalvm.compiler.nodeinfo.NodeInfo;
  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.UnaryNode;
  45 import org.graalvm.compiler.nodes.spi.ArithmeticLIRLowerable;
  46 import org.graalvm.compiler.nodes.spi.Lowerable;
  47 import org.graalvm.compiler.nodes.spi.LoweringTool;
  48 
  49 import jdk.vm.ci.meta.JavaKind;
  50 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
  51 
  52 @NodeInfo(nameTemplate = "MathIntrinsic#{p#operation/s}", cycles = CYCLES_64, size = SIZE_1)
  53 public final class UnaryMathIntrinsicNode extends UnaryNode implements ArithmeticLIRLowerable, Lowerable {
  54 
  55     public static final NodeClass<UnaryMathIntrinsicNode> TYPE = NodeClass.create(UnaryMathIntrinsicNode.class);
  56     protected final UnaryOperation operation;
  57 
  58     public enum UnaryOperation {
  59         LOG(new ForeignCallDescriptor("arithmeticLog", double.class, double.class)),
  60         LOG10(new ForeignCallDescriptor("arithmeticLog10", double.class, double.class)),
  61         SIN(new ForeignCallDescriptor("arithmeticSin", double.class, double.class)),
  62         COS(new ForeignCallDescriptor("arithmeticCos", double.class, double.class)),
  63         TAN(new ForeignCallDescriptor("arithmeticTan", double.class, double.class)),
  64         EXP(new ForeignCallDescriptor("arithmeticExp", double.class, double.class));
  65 
  66         public final ForeignCallDescriptor foreignCallDescriptor;
  67 
  68         UnaryOperation(ForeignCallDescriptor foreignCallDescriptor) {
  69             this.foreignCallDescriptor = foreignCallDescriptor;
  70         }
  71 
  72         public double compute(double value) {
  73             switch (this) {
  74                 case LOG:
  75                     return Math.log(value);
  76                 case LOG10:
  77                     return Math.log10(value);
  78                 case EXP:
  79                     return Math.exp(value);
  80                 case SIN:
  81                     return Math.sin(value);
  82                 case COS:
  83                     return Math.cos(value);
  84                 case TAN:
  85                     return Math.tan(value);
  86                 default:
  87                     throw new GraalError("unknown op %s", this);
  88             }
  89         }
  90 
  91         public Stamp computeStamp(Stamp valueStamp) {
  92             if (valueStamp instanceof FloatStamp) {
  93                 FloatStamp floatStamp = (FloatStamp) valueStamp;
  94                 switch (this) {
  95                     case COS:
  96                     case SIN: {
  97                         boolean nonNaN = floatStamp.lowerBound() != Double.NEGATIVE_INFINITY && floatStamp.upperBound() != Double.POSITIVE_INFINITY && floatStamp.isNonNaN();
  98                         return StampFactory.forFloat(JavaKind.Double, -1.0, 1.0, nonNaN);
  99                     }
 100                     case TAN: {
 101                         boolean nonNaN = floatStamp.lowerBound() != Double.NEGATIVE_INFINITY && floatStamp.upperBound() != Double.POSITIVE_INFINITY && floatStamp.isNonNaN();
 102                         return StampFactory.forFloat(JavaKind.Double, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, nonNaN);
 103                     }
 104                     case LOG:
 105                     case LOG10: {
 106                         double lowerBound = compute(floatStamp.lowerBound());
 107                         double upperBound = compute(floatStamp.upperBound());
 108                         if (floatStamp.contains(0.0)) {
 109                             // 0.0 and -0.0 infinity produces -Inf
 110                             lowerBound = Double.NEGATIVE_INFINITY;
 111                         }
 112                         boolean nonNaN = floatStamp.lowerBound() >= 0.0 && floatStamp.isNonNaN();
 113                         return StampFactory.forFloat(JavaKind.Double, lowerBound, upperBound, nonNaN);
 114                     }
 115                     case EXP: {
 116                         double lowerBound = Math.exp(floatStamp.lowerBound());
 117                         double upperBound = Math.exp(floatStamp.upperBound());
 118                         boolean nonNaN = floatStamp.isNonNaN();
 119                         return StampFactory.forFloat(JavaKind.Double, lowerBound, upperBound, nonNaN);
 120                     }
 121 
 122                 }
 123             }
 124             return StampFactory.forKind(JavaKind.Double);
 125         }
 126 
 127     }
 128 
 129     public UnaryOperation getOperation() {
 130         return operation;
 131     }
 132 
 133     public static ValueNode create(ValueNode value, UnaryOperation op) {
 134         ValueNode c = tryConstantFold(value, op);
 135         if (c != null) {
 136             return c;
 137         }
 138         return new UnaryMathIntrinsicNode(value, op);
 139     }
 140 
 141     protected static ValueNode tryConstantFold(ValueNode value, UnaryOperation op) {
 142         if (value.isConstant()) {
 143             return ConstantNode.forDouble(op.compute(value.asJavaConstant().asDouble()));
 144         }
 145         return null;
 146     }
 147 
 148     protected UnaryMathIntrinsicNode(ValueNode value, UnaryOperation op) {
 149         super(TYPE, op.computeStamp(value.stamp(NodeView.DEFAULT)), value);
 150         assert value.stamp(NodeView.DEFAULT) instanceof FloatStamp && PrimitiveStamp.getBits(value.stamp(NodeView.DEFAULT)) == 64;
 151         this.operation = op;
 152     }
 153 
 154     @Override
 155     public Stamp foldStamp(Stamp valueStamp) {
 156         return getOperation().computeStamp(valueStamp);
 157     }
 158 
 159     @Override
 160     public void lower(LoweringTool tool) {
 161         tool.getLowerer().lower(this, tool);
 162     }
 163 
 164     @Override
 165     public void generate(NodeLIRBuilderTool nodeValueMap, ArithmeticLIRGeneratorTool gen) {
 166         // We can only reach here in the math stubs
 167         Value input = nodeValueMap.operand(getValue());
 168         Value result;
 169         switch (getOperation()) {
 170             case LOG:
 171                 result = gen.emitMathLog(input, false);
 172                 break;
 173             case LOG10:
 174                 result = gen.emitMathLog(input, true);
 175                 break;
 176             case EXP:
 177                 result = gen.emitMathExp(input);
 178                 break;
 179             case SIN:
 180                 result = gen.emitMathSin(input);
 181                 break;
 182             case COS:
 183                 result = gen.emitMathCos(input);
 184                 break;
 185             case TAN:
 186                 result = gen.emitMathTan(input);
 187                 break;
 188             default:
 189                 throw GraalError.shouldNotReachHere();
 190         }
 191         nodeValueMap.setResult(this, result);
 192     }
 193 
 194     @Override
 195     public ValueNode canonical(CanonicalizerTool tool, ValueNode forValue) {
 196         ValueNode c = tryConstantFold(forValue, getOperation());
 197         if (c != null) {
 198             return c;
 199         }
 200         return this;
 201     }
 202 
 203     @NodeIntrinsic
 204     public static native double compute(double value, @ConstantNodeParameter UnaryOperation op);
 205 
 206 }