001/*
002 * Copyright (c) 2013, 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 jdk.internal.jvmci.code.*;
026import com.oracle.graal.debug.*;
027import static com.oracle.graal.nodes.extended.BranchProbabilityNode.*;
028import static com.oracle.graal.replacements.SnippetTemplate.*;
029
030import com.oracle.graal.api.replacements.*;
031import com.oracle.graal.nodes.*;
032import com.oracle.graal.nodes.calc.*;
033import com.oracle.graal.nodes.spi.*;
034import com.oracle.graal.phases.util.*;
035import com.oracle.graal.replacements.*;
036import com.oracle.graal.replacements.SnippetTemplate.AbstractTemplates;
037import com.oracle.graal.replacements.SnippetTemplate.Arguments;
038import com.oracle.graal.replacements.SnippetTemplate.SnippetInfo;
039
040/**
041 * Snippets used for conversion operations on AMD64 where the AMD64 instruction used does not match
042 * the semantics of the JVM specification.
043 */
044public class AMD64ConvertSnippets implements Snippets {
045
046    /**
047     * Converts a float to an int.
048     * <p>
049     * This snippet accounts for the semantics of the x64 CVTTSS2SI instruction used to do the
050     * conversion. If the float value is a NaN, infinity or if the result of the conversion is
051     * larger than {@link Integer#MAX_VALUE} then CVTTSS2SI returns {@link Integer#MIN_VALUE} and
052     * extra tests are required on the float value to return the correct int value.
053     *
054     * @param input the float being converted
055     * @param result the result produced by the CVTTSS2SI instruction
056     */
057    @Snippet
058    public static int f2i(float input, int result) {
059        if (probability(SLOW_PATH_PROBABILITY, result == Integer.MIN_VALUE)) {
060            if (Float.isNaN(input)) {
061                // input is NaN -> return 0
062                return 0;
063            } else if (input > 0.0f) {
064                // input is > 0 -> return max int
065                return Integer.MAX_VALUE;
066            }
067        }
068        return result;
069    }
070
071    /**
072     * Converts a float to a long.
073     * <p>
074     * This snippet accounts for the semantics of the x64 CVTTSS2SI instruction used to do the
075     * conversion. If the float value is a NaN or infinity then CVTTSS2SI returns
076     * {@link Long#MIN_VALUE} and extra tests are required on the float value to return the correct
077     * long value.
078     *
079     * @param input the float being converted
080     * @param result the result produced by the CVTTSS2SI instruction
081     */
082    @Snippet
083    public static long f2l(float input, long result) {
084        if (probability(SLOW_PATH_PROBABILITY, result == Long.MIN_VALUE)) {
085            if (Float.isNaN(input)) {
086                // input is NaN -> return 0
087                return 0;
088            } else if (input > 0.0f) {
089                // input is > 0 -> return max int
090                return Long.MAX_VALUE;
091            }
092        }
093        return result;
094    }
095
096    /**
097     * Converts a double to an int.
098     * <p>
099     * This snippet accounts for the semantics of the x64 CVTTSD2SI instruction used to do the
100     * conversion. If the double value is a NaN, infinity or if the result of the conversion is
101     * larger than {@link Integer#MAX_VALUE} then CVTTSD2SI returns {@link Integer#MIN_VALUE} and
102     * extra tests are required on the double value to return the correct int value.
103     *
104     * @param input the double being converted
105     * @param result the result produced by the CVTTSS2SI instruction
106     */
107    @Snippet
108    public static int d2i(double input, int result) {
109        if (probability(SLOW_PATH_PROBABILITY, result == Integer.MIN_VALUE)) {
110            if (Double.isNaN(input)) {
111                // input is NaN -> return 0
112                return 0;
113            } else if (input > 0.0d) {
114                // input is positive -> return maxInt
115                return Integer.MAX_VALUE;
116            }
117        }
118        return result;
119    }
120
121    /**
122     * Converts a double to a long.
123     * <p>
124     * This snippet accounts for the semantics of the x64 CVTTSD2SI instruction used to do the
125     * conversion. If the double value is a NaN, infinity or if the result of the conversion is
126     * larger than {@link Long#MAX_VALUE} then CVTTSD2SI returns {@link Long#MIN_VALUE} and extra
127     * tests are required on the double value to return the correct long value.
128     *
129     * @param input the double being converted
130     * @param result the result produced by the CVTTSS2SI instruction
131     */
132    @Snippet
133    public static long d2l(double input, long result) {
134        if (probability(SLOW_PATH_PROBABILITY, result == Long.MIN_VALUE)) {
135            if (Double.isNaN(input)) {
136                // input is NaN -> return 0
137                return 0;
138            } else if (input > 0.0d) {
139                // input is positive -> return maxInt
140                return Long.MAX_VALUE;
141            }
142        }
143        return result;
144    }
145
146    public static class Templates extends AbstractTemplates {
147
148        private final SnippetInfo f2i;
149        private final SnippetInfo f2l;
150        private final SnippetInfo d2i;
151        private final SnippetInfo d2l;
152
153        public Templates(Providers providers, SnippetReflectionProvider snippetReflection, TargetDescription target) {
154            super(providers, snippetReflection, target);
155
156            f2i = snippet(AMD64ConvertSnippets.class, "f2i");
157            f2l = snippet(AMD64ConvertSnippets.class, "f2l");
158            d2i = snippet(AMD64ConvertSnippets.class, "d2i");
159            d2l = snippet(AMD64ConvertSnippets.class, "d2l");
160        }
161
162        public void lower(FloatConvertNode convert, LoweringTool tool) {
163            SnippetInfo key;
164            switch (convert.getFloatConvert()) {
165                case F2I:
166                    key = f2i;
167                    break;
168                case F2L:
169                    key = f2l;
170                    break;
171                case D2I:
172                    key = d2i;
173                    break;
174                case D2L:
175                    key = d2l;
176                    break;
177                default:
178                    return;
179            }
180
181            StructuredGraph graph = convert.graph();
182
183            Arguments args = new Arguments(key, graph.getGuardsStage(), tool.getLoweringStage());
184            args.add("input", convert.getValue());
185            args.add("result", graph.unique(new AMD64FloatConvertNode(convert.getFloatConvert(), convert.getValue())));
186
187            SnippetTemplate template = template(args);
188            Debug.log("Lowering %s in %s: node=%s, template=%s, arguments=%s", convert.getFloatConvert(), graph, convert, template, args);
189            template.instantiate(providers.getMetaAccess(), convert, DEFAULT_REPLACER, tool, args);
190            graph.removeFloating(convert);
191        }
192    }
193}