001/*
002 * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.
008 *
009 * This code is distributed in the hope that it will be useful, but WITHOUT
010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
011 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
012 * version 2 for more details (a copy is included in the LICENSE file that
013 * accompanied this code).
014 *
015 * You should have received a copy of the GNU General Public License version
016 * 2 along with this work; if not, write to the Free Software Foundation,
017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
018 *
019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
020 * or visit www.oracle.com if you need additional information or have any
021 * questions.
022 */
023package com.oracle.graal.replacements.amd64;
024
025import static com.oracle.graal.compiler.target.Backend.*;
026
027import com.oracle.graal.compiler.common.spi.*;
028import com.oracle.graal.graph.Node.ConstantNodeParameter;
029import com.oracle.graal.graph.Node.NodeIntrinsic;
030import com.oracle.graal.nodes.extended.*;
031import com.oracle.graal.replacements.amd64.AMD64MathIntrinsicNode.Operation;
032
033// JaCoCo Exclude
034
035/**
036 * Substitutions for some {@link java.lang.Math} methods that leverage AMD64 instructions for
037 * selected input values.
038 */
039public class AMD64MathSubstitutions {
040
041    private static final double PI_4 = Math.PI / 4;
042
043    /**
044     * Special cases from {@link Math#pow} and __ieee754_pow (in sharedRuntimeTrans.cpp).
045     */
046    public static double pow(double x, double y) {
047        // If the second argument is positive or negative zero, then the result is 1.0.
048        if (y == 0.0D) {
049            return 1;
050        }
051
052        // If the second argument is 1.0, then the result is the same as the first argument.
053        if (y == 1.0D) {
054            return x;
055        }
056
057        // If the second argument is NaN, then the result is NaN.
058        if (Double.isNaN(y)) {
059            return Double.NaN;
060        }
061
062        // If the first argument is NaN and the second argument is nonzero, then the result is NaN.
063        if (Double.isNaN(x) && y != 0.0D) {
064            return Double.NaN;
065        }
066
067        // x**-1 = 1/x
068        if (y == -1.0D) {
069            return 1 / x;
070        }
071
072        // x**2 = x*x
073        if (y == 2.0D) {
074            return x * x;
075        }
076
077        // x**0.5 = sqrt(x)
078        if (y == 0.5D && x >= 0.0D) {
079            return Math.sqrt(x);
080        }
081        return callDouble2(ARITHMETIC_POW, x, y);
082    }
083
084    // NOTE on snippets below:
085    // Math.sin(), .cos() and .tan() guarantee a value within 1 ULP of the
086    // exact result, but x87 trigonometric FPU instructions are only that
087    // accurate within [-pi/4, pi/4]. Examine the passed value and provide
088    // a slow path for inputs outside of that interval.
089
090    public static double sin(double x) {
091        if (Math.abs(x) < PI_4) {
092            return AMD64MathIntrinsicNode.compute(x, Operation.SIN);
093        } else {
094            return callDouble1(ARITHMETIC_SIN, x);
095        }
096    }
097
098    public static double cos(double x) {
099        if (Math.abs(x) < PI_4) {
100            return AMD64MathIntrinsicNode.compute(x, Operation.COS);
101        } else {
102            return callDouble1(ARITHMETIC_COS, x);
103        }
104    }
105
106    public static double tan(double x) {
107        if (Math.abs(x) < PI_4) {
108            return AMD64MathIntrinsicNode.compute(x, Operation.TAN);
109        } else {
110            return callDouble1(ARITHMETIC_TAN, x);
111        }
112    }
113
114    @NodeIntrinsic(value = ForeignCallNode.class, setStampFromReturnType = true)
115    private static native double callDouble1(@ConstantNodeParameter ForeignCallDescriptor descriptor, double value);
116
117    @NodeIntrinsic(value = ForeignCallNode.class, setStampFromReturnType = true)
118    private static native double callDouble2(@ConstantNodeParameter ForeignCallDescriptor descriptor, double a, double b);
119}