changeset 8179:80a825206cbc

replaced AMD64Arithmetic.ConvertSlowPath with a snippet (GRAAL-140)
author Doug Simon <doug.simon@oracle.com>
date Fri, 08 Mar 2013 14:33:33 +0100
parents 67ee3325c285
children 0934903d28f3
files graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotRuntime.java graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/snippets/AMD64ConvertSnippets.java graal/com.oracle.graal.jtt/src/com/oracle/graal/jtt/bytecode/BC_d2l03.java graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64Arithmetic.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/ConvertNode.java
diffstat 5 files changed, 271 insertions(+), 67 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotRuntime.java	Fri Mar 08 13:54:41 2013 +0100
+++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotRuntime.java	Fri Mar 08 14:33:33 2013 +0100
@@ -43,8 +43,14 @@
 
 import com.oracle.graal.api.code.*;
 import com.oracle.graal.api.meta.*;
+import com.oracle.graal.compiler.target.*;
+import com.oracle.graal.graph.*;
 import com.oracle.graal.hotspot.*;
+import com.oracle.graal.hotspot.amd64.snippets.*;
 import com.oracle.graal.hotspot.meta.*;
+import com.oracle.graal.nodes.calc.*;
+import com.oracle.graal.nodes.spi.*;
+import com.oracle.graal.snippets.*;
 
 public class AMD64HotSpotRuntime extends HotSpotRuntime {
 
@@ -186,6 +192,24 @@
 
     }
 
+    private AMD64ConvertSnippets.Templates convertSnippets;
+
+    @Override
+    public void installSnippets(Backend backend, SnippetInstaller installer, Assumptions assumptions) {
+        installer.installSnippets(AMD64ConvertSnippets.class);
+        convertSnippets = new AMD64ConvertSnippets.Templates(this, assumptions, graalRuntime.getTarget());
+        super.installSnippets(backend, installer, assumptions);
+    }
+
+    @Override
+    public void lower(Node n, LoweringTool tool) {
+        if (n instanceof ConvertNode) {
+            convertSnippets.lower((ConvertNode) n, tool);
+        } else {
+            super.lower(n, tool);
+        }
+    }
+
     @Override
     public Register threadRegister() {
         return r15;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/snippets/AMD64ConvertSnippets.java	Fri Mar 08 14:33:33 2013 +0100
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.hotspot.amd64.snippets;
+
+import static com.oracle.graal.snippets.SnippetTemplate.*;
+import static com.oracle.graal.snippets.SnippetTemplate.Arguments.*;
+
+import com.oracle.graal.api.code.*;
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.debug.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.calc.*;
+import com.oracle.graal.nodes.calc.ConvertNode.Op;
+import com.oracle.graal.nodes.spi.*;
+import com.oracle.graal.snippets.*;
+import com.oracle.graal.snippets.Snippet.Parameter;
+import com.oracle.graal.snippets.SnippetTemplate.AbstractTemplates;
+import com.oracle.graal.snippets.SnippetTemplate.Arguments;
+import com.oracle.graal.snippets.SnippetTemplate.Key;
+
+/**
+ * Snippets used for conversion operations on AMD64 where the AMD64 instruction used does not match
+ * the semantics of the JVM specification.
+ */
+public class AMD64ConvertSnippets implements SnippetsInterface {
+
+    /**
+     * Converts a float to an int.
+     * <p>
+     * This snippet accounts for the semantics of the x64 CVTTSS2SI instruction used to do the
+     * conversion. If the float value is a NaN, infinity or if the result of the conversion is
+     * larger than {@link Integer#MAX_VALUE} then CVTTSS2SI returns {@link Integer#MIN_VALUE} and
+     * extra tests are required on the float value to return the correct int value.
+     * 
+     * @param input the float being converted
+     * @param result the result produced by the CVTTSS2SI instruction
+     */
+    @Snippet
+    public static int f2i(@Parameter("input") float input, @Parameter("result") int result) {
+        if (result == Integer.MIN_VALUE) {
+            if (Float.isNaN(input)) {
+                // input is NaN -> return 0
+                return 0;
+            } else if (input > 0.0f) {
+                // input is > 0 -> return max int
+                return Integer.MAX_VALUE;
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Converts a float to a long.
+     * <p>
+     * This snippet accounts for the semantics of the x64 CVTTSS2SI instruction used to do the
+     * conversion. If the float value is a NaN or infinity then CVTTSS2SI returns
+     * {@link Long#MIN_VALUE} and extra tests are required on the float value to return the correct
+     * long value.
+     * 
+     * @param input the float being converted
+     * @param result the result produced by the CVTTSS2SI instruction
+     */
+    @Snippet
+    public static long f2l(@Parameter("input") float input, @Parameter("result") long result) {
+        if (result == Long.MIN_VALUE) {
+            if (Float.isNaN(input)) {
+                // input is NaN -> return 0
+                return 0;
+            } else if (input > 0.0f) {
+                // input is > 0 -> return max int
+                return Long.MAX_VALUE;
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Converts a double to an int.
+     * <p>
+     * This snippet accounts for the semantics of the x64 CVTTSD2SI instruction used to do the
+     * conversion. If the double value is a NaN, infinity or if the result of the conversion is
+     * larger than {@link Integer#MAX_VALUE} then CVTTSD2SI returns {@link Integer#MIN_VALUE} and
+     * extra tests are required on the double value to return the correct int value.
+     * 
+     * @param input the double being converted
+     * @param result the result produced by the CVTTSS2SI instruction
+     */
+    @Snippet
+    public static int d2i(@Parameter("input") double input, @Parameter("result") int result) {
+        if (result == Integer.MIN_VALUE) {
+            if (Double.isNaN(input)) {
+                // input is NaN -> return 0
+                return 0;
+            } else if (input > 0.0d) {
+                // input is positive -> return maxInt
+                return Integer.MAX_VALUE;
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Converts a double to a long.
+     * <p>
+     * This snippet accounts for the semantics of the x64 CVTTSD2SI instruction used to do the
+     * conversion. If the double value is a NaN, infinity or if the result of the conversion is
+     * larger than {@link Long#MAX_VALUE} then CVTTSD2SI returns {@link Long#MIN_VALUE} and extra
+     * tests are required on the double value to return the correct long value.
+     * 
+     * @param input the double being converted
+     * @param result the result produced by the CVTTSS2SI instruction
+     */
+    @Snippet
+    public static long d2l(@Parameter("input") double input, @Parameter("result") long result) {
+        if (result == Long.MIN_VALUE) {
+            if (Double.isNaN(input)) {
+                // input is NaN -> return 0
+                return 0;
+            } else if (input > 0.0d) {
+                // input is positive -> return maxInt
+                return Long.MAX_VALUE;
+            }
+        }
+        return result;
+    }
+
+    public static class Templates extends AbstractTemplates<AMD64ConvertSnippets> {
+
+        private final ResolvedJavaMethod f2i;
+        private final ResolvedJavaMethod f2l;
+        private final ResolvedJavaMethod d2i;
+        private final ResolvedJavaMethod d2l;
+
+        public Templates(CodeCacheProvider runtime, Assumptions assumptions, TargetDescription target) {
+            super(runtime, assumptions, target, AMD64ConvertSnippets.class);
+            f2i = snippet("f2i", float.class, int.class);
+            f2l = snippet("f2l", float.class, long.class);
+            d2i = snippet("d2i", double.class, int.class);
+            d2l = snippet("d2l", double.class, long.class);
+        }
+
+        public void lower(ConvertNode convert, LoweringTool tool) {
+            if (convert.opcode == Op.F2I) {
+                lower0(convert, tool, f2i);
+            } else if (convert.opcode == Op.F2L) {
+                lower0(convert, tool, f2l);
+            } else if (convert.opcode == Op.D2I) {
+                lower0(convert, tool, d2i);
+            } else if (convert.opcode == Op.D2L) {
+                lower0(convert, tool, d2l);
+            }
+        }
+
+        private void lower0(ConvertNode convert, LoweringTool tool, ResolvedJavaMethod snippet) {
+            StructuredGraph graph = (StructuredGraph) convert.graph();
+
+            // Insert a unique placeholder node in place of the Convert node so that the
+            // Convert node can be used as an input to the snippet. All usage of the
+            // Convert node are replaced by the placeholder which in turn is replaced by the
+            // snippet.
+
+            LocalNode replacee = graph.add(new LocalNode(Integer.MAX_VALUE, convert.stamp()));
+            convert.replaceAtUsages(replacee);
+            Key key = new Key(snippet);
+            Arguments arguments = arguments("input", convert.value()).add("result", convert);
+            SnippetTemplate template = cache.get(key, assumptions);
+            Debug.log("Lowering %s in %s: node=%s, template=%s, arguments=%s", convert.opcode, graph, convert, template, arguments);
+            template.instantiate(runtime, replacee, DEFAULT_REPLACER, tool, arguments);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.jtt/src/com/oracle/graal/jtt/bytecode/BC_d2l03.java	Fri Mar 08 14:33:33 2013 +0100
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.oracle.graal.jtt.bytecode;
+
+import com.oracle.graal.jtt.*;
+import org.junit.*;
+
+/*
+ */
+public class BC_d2l03 extends JTTTest {
+
+    public static long test(double divider) {
+        return (long) (((long) divider) * divider);
+    }
+
+    @Test
+    public void run0() throws Throwable {
+        runTest("test", 34.5D);
+    }
+
+}
--- a/graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64Arithmetic.java	Fri Mar 08 13:54:41 2013 +0100
+++ b/graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64Arithmetic.java	Fri Mar 08 14:33:33 2013 +0100
@@ -28,8 +28,6 @@
 import com.oracle.graal.amd64.*;
 import com.oracle.graal.api.code.*;
 import com.oracle.graal.api.meta.*;
-import com.oracle.graal.asm.*;
-import com.oracle.graal.asm.amd64.AMD64Assembler.ConditionFlag;
 import com.oracle.graal.asm.amd64.*;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.lir.*;
@@ -44,10 +42,15 @@
     INEG, LNEG,
     I2L, L2I, I2B, I2C, I2S,
     F2D, D2F,
-    I2F, I2D, F2I, D2I,
-    L2F, L2D, F2L, D2L,
-    MOV_I2F, MOV_L2D, MOV_F2I, MOV_D2L;
+    I2F, I2D,
+    L2F, L2D,
+    MOV_I2F, MOV_L2D, MOV_F2I, MOV_D2L,
 
+    /*
+     * Converts a float/double to an int/long. The result of the conversion does not comply with Java semantics
+     * when the input is a NaN, infinity or the conversion result is greater than Integer.MAX_VALUE/Long.MAX_VALUE.
+     */
+    F2I, D2I, F2L, D2L;
 
     /**
      * Unary operation with separate source and destination operand. 
@@ -339,19 +342,15 @@
                 case L2D: masm.cvtsi2sdq(asDoubleReg(dst), asLongReg(src)); break;
                 case F2I:
                     masm.cvttss2sil(asIntReg(dst), asFloatReg(src));
-                    emitConvertFixup(tasm, masm, dst, src);
                     break;
                 case D2I:
                     masm.cvttsd2sil(asIntReg(dst), asDoubleReg(src));
-                    emitConvertFixup(tasm, masm, dst, src);
                     break;
                 case F2L:
                     masm.cvttss2siq(asLongReg(dst), asFloatReg(src));
-                    emitConvertFixup(tasm, masm, dst, src);
                     break;
                 case D2L:
                     masm.cvttsd2siq(asLongReg(dst), asDoubleReg(src));
-                    emitConvertFixup(tasm, masm, dst, src);
                     break;
                 case MOV_I2F: masm.movdl(asFloatReg(dst), asIntReg(src)); break;
                 case MOV_L2D: masm.movdq(asDoubleReg(dst), asLongReg(src)); break;
@@ -466,63 +465,6 @@
         }
     }
 
-    private static void emitConvertFixup(TargetMethodAssembler tasm, AMD64MacroAssembler masm, Value result, Value x) {
-        ConvertSlowPath slowPath = new ConvertSlowPath(result, x);
-        tasm.stubs.add(slowPath);
-        switch (result.getKind()) {
-            case Int:  masm.cmpl(asIntReg(result),  Integer.MIN_VALUE); break;
-            case Long: masm.cmpq(asLongReg(result), (AMD64Address) tasm.asLongConstRef(Constant.forLong(java.lang.Long.MIN_VALUE))); break;
-            default:   throw GraalInternalError.shouldNotReachHere();
-        }
-        masm.jcc(ConditionFlag.Equal, slowPath.start);
-        masm.bind(slowPath.continuation);
-    }
-
-    private static class ConvertSlowPath extends AMD64Code {
-        public final Label start = new Label();
-        public final Label continuation = new Label();
-        private final Value result;
-        private final Value x;
-
-        public ConvertSlowPath(Value result, Value x) {
-            this.result = result;
-            this.x = x;
-        }
-
-        @Override
-        public void emitCode(TargetMethodAssembler tasm, AMD64MacroAssembler masm) {
-            masm.bind(start);
-            switch (x.getKind()) {
-                case Float:  masm.ucomiss(asFloatReg(x),  (AMD64Address) tasm.asFloatConstRef(Constant.FLOAT_0)); break;
-                case Double: masm.ucomisd(asDoubleReg(x), (AMD64Address) tasm.asDoubleConstRef(Constant.DOUBLE_0)); break;
-                default:     throw GraalInternalError.shouldNotReachHere();
-            }
-            Label nan = new Label();
-            masm.jcc(ConditionFlag.Parity, nan);
-            masm.jcc(ConditionFlag.Below, continuation);
-
-            // input is > 0 -> return maxInt
-            // result register already contains 0x80000000, so subtracting 1 gives 0x7fffffff
-            switch (result.getKind()) {
-                case Int:  masm.decrementl(asIntReg(result),  1); break;
-                case Long: masm.decrementq(asLongReg(result), 1); break;
-                default:   throw GraalInternalError.shouldNotReachHere();
-            }
-            masm.jmp(continuation);
-
-            // input is NaN -> return 0
-            masm.bind(nan);
-            masm.xorptr(asRegister(result), asRegister(result));
-            masm.jmp(continuation);
-        }
-
-        @Override
-        public String description() {
-            return "convert " + x + " to " + result;
-        }
-    }
-
-
     private static void verifyKind(AMD64Arithmetic opcode, Value result, Value x, Value y) {
         assert (opcode.name().startsWith("I") && result.getKind() == Kind.Int && x.getKind().getStackKind() == Kind.Int && y.getKind().getStackKind() == Kind.Int)
             || (opcode.name().startsWith("L") && result.getKind() == Kind.Long && x.getKind() == Kind.Long && y.getKind() == Kind.Long)
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/ConvertNode.java	Fri Mar 08 13:54:41 2013 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/ConvertNode.java	Fri Mar 08 14:33:33 2013 +0100
@@ -32,7 +32,7 @@
 /**
  * The {@code ConvertNode} class represents a conversion between primitive types.
  */
-public final class ConvertNode extends FloatingNode implements Canonicalizable, LIRLowerable {
+public final class ConvertNode extends FloatingNode implements Canonicalizable, LIRLowerable, Lowerable {
 
     public enum Op {
         I2L(Int, Long),
@@ -162,6 +162,11 @@
     }
 
     @Override
+    public void lower(LoweringTool tool) {
+        tool.getRuntime().lower(this, tool);
+    }
+
+    @Override
     public void generate(LIRGeneratorTool gen) {
         gen.setResult(this, gen.emitConvert(opcode, gen.operand(value())));
     }