# HG changeset patch # User Doug Simon # Date 1362749613 -3600 # Node ID 80a825206cbc2b56eee2846a01eff70955289cec # Parent 67ee3325c285829aea57f1cc272d2fbdddfd88ae replaced AMD64Arithmetic.ConvertSlowPath with a snippet (GRAAL-140) diff -r 67ee3325c285 -r 80a825206cbc graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotRuntime.java --- 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; diff -r 67ee3325c285 -r 80a825206cbc graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/snippets/AMD64ConvertSnippets.java --- /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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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 { + + 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); + } + } +} diff -r 67ee3325c285 -r 80a825206cbc graal/com.oracle.graal.jtt/src/com/oracle/graal/jtt/bytecode/BC_d2l03.java --- /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); + } + +} diff -r 67ee3325c285 -r 80a825206cbc graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64Arithmetic.java --- 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) diff -r 67ee3325c285 -r 80a825206cbc graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/ConvertNode.java --- 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()))); }