1 /* 2 * Copyright (c) 2014, 2015, 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 package org.graalvm.compiler.replacements.aarch64; 25 26 import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_IGNORED; 27 import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_IGNORED; 28 29 import org.graalvm.compiler.api.replacements.Snippet; 30 import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; 31 import org.graalvm.compiler.graph.Node.NodeIntrinsic; 32 import org.graalvm.compiler.graph.NodeClass; 33 import org.graalvm.compiler.nodeinfo.NodeInfo; 34 import org.graalvm.compiler.nodes.StructuredGraph; 35 import org.graalvm.compiler.nodes.ValueNode; 36 import org.graalvm.compiler.nodes.calc.RemNode; 37 import org.graalvm.compiler.nodes.spi.LoweringTool; 38 import org.graalvm.compiler.phases.util.Providers; 39 import org.graalvm.compiler.replacements.SnippetTemplate; 40 import org.graalvm.compiler.replacements.SnippetTemplate.Arguments; 41 import org.graalvm.compiler.replacements.Snippets; 42 43 import jdk.vm.ci.code.TargetDescription; 44 import jdk.vm.ci.meta.JavaKind; 45 46 /** 47 * AArch64 does not have a remainder operation. We use <code>n % d == n - Truncate(n / d) * d</code> 48 * for it instead. This is not correct for some edge cases, so we have to fix it up using these 49 * snippets. 50 */ 51 public class AArch64FloatArithmeticSnippets extends SnippetTemplate.AbstractTemplates implements Snippets { 52 53 private final SnippetTemplate.SnippetInfo drem; 54 private final SnippetTemplate.SnippetInfo frem; 55 56 public AArch64FloatArithmeticSnippets(Providers providers, SnippetReflectionProvider snippetReflection, TargetDescription target) { 57 super(providers, snippetReflection, target); 58 drem = snippet(AArch64FloatArithmeticSnippets.class, "dremSnippet"); 59 frem = snippet(AArch64FloatArithmeticSnippets.class, "fremSnippet"); 60 } 61 62 public void lower(RemNode node, LoweringTool tool) { 63 JavaKind kind = node.stamp().getStackKind(); 64 assert kind == JavaKind.Float || kind == JavaKind.Double; 65 if (node instanceof SafeNode) { 66 // We already introduced the necessary checks, nothing to do. 67 return; 68 } 69 SnippetTemplate.SnippetInfo snippet = kind == JavaKind.Float ? frem : drem; 70 StructuredGraph graph = node.graph(); 71 Arguments args = new Arguments(snippet, graph.getGuardsStage(), tool.getLoweringStage()); 72 args.add("x", node.getX()); 73 args.add("y", node.getY()); 74 template(args).instantiate(providers.getMetaAccess(), node, SnippetTemplate.DEFAULT_REPLACER, tool, args); 75 } 76 77 @Snippet 78 public static float fremSnippet(float x, float y) { 79 // JVMS: If either value1' or value2' is NaN, the result is NaN. 80 // JVMS: If the dividend is an infinity or the divisor is a zero or both, the result is NaN. 81 if (Float.isInfinite(x) || y == 0.0f || Float.isNaN(y)) { 82 return Float.NaN; 83 } 84 // JVMS: If the dividend is finite and the divisor is an infinity, the result equals the 85 // dividend. 86 // JVMS: If the dividend is a zero and the divisor is finite, the result equals the 87 // dividend. 88 if (x == 0.0f || Float.isInfinite(y)) { 89 return x; 90 } 91 92 float result = safeRem(x, y); 93 94 // JVMS: If neither value1' nor value2' is NaN, the sign of the result equals the sign of 95 // the dividend. 96 if (result == 0.0f && x < 0.0f) { 97 return -result; 98 } 99 return result; 100 } 101 102 @Snippet 103 public static double dremSnippet(double x, double y) { 104 // JVMS: If either value1' or value2' is NaN, the result is NaN. 105 // JVMS: If the dividend is an infinity or the divisor is a zero or both, the result is NaN. 106 if (Double.isInfinite(x) || y == 0.0 || Double.isNaN(y)) { 107 return Double.NaN; 108 } 109 // JVMS: If the dividend is finite and the divisor is an infinity, the result equals the 110 // dividend. 111 // JVMS: If the dividend is a zero and the divisor is finite, the result equals the 112 // dividend. 113 if (x == 0.0 || Double.isInfinite(y)) { 114 return x; 115 } 116 117 double result = safeRem(x, y); 118 119 // JVMS: If neither value1' nor value2' is NaN, the sign of the result equals the sign of 120 // the dividend. 121 if (result == 0.0 && x < 0.0) { 122 return -result; 123 } 124 return result; 125 } 126 127 @NodeIntrinsic(SafeFloatRemNode.class) 128 private static native float safeRem(float x, float y); 129 130 @NodeIntrinsic(SafeFloatRemNode.class) 131 private static native double safeRem(double x, double y); 132 133 /** 134 * Marker interface to distinguish untreated nodes from ones where we have installed the 135 * additional checks. 136 */ 137 private interface SafeNode { 138 } 139 140 @NodeInfo(cycles = CYCLES_IGNORED, size = SIZE_IGNORED) 141 // static class SafeFloatRemNode extends FloatRemNode implements SafeNode { 142 static class SafeFloatRemNode extends RemNode implements SafeNode { 143 public static final NodeClass<SafeFloatRemNode> TYPE = NodeClass.create(SafeFloatRemNode.class); 144 145 protected SafeFloatRemNode(ValueNode x, ValueNode y) { 146 super(TYPE, x, y); 147 } 148 } 149 150 }