1 /* 2 * Copyright (c) 2015, 2019, 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 25 package org.graalvm.compiler.api.directives.test; 26 27 import java.lang.annotation.ElementType; 28 import java.lang.annotation.Repeatable; 29 import java.lang.annotation.Retention; 30 import java.lang.annotation.RetentionPolicy; 31 import java.lang.annotation.Target; 32 import java.util.Arrays; 33 import java.util.Collections; 34 import java.util.List; 35 36 import org.junit.Assert; 37 import org.junit.Test; 38 39 import org.graalvm.compiler.api.directives.GraalDirectives; 40 import org.graalvm.compiler.core.test.GraalCompilerTest; 41 import org.graalvm.compiler.graph.Node; 42 import org.graalvm.compiler.graph.iterators.NodeIterable; 43 import org.graalvm.compiler.nodes.IfNode; 44 import org.graalvm.compiler.nodes.LoopBeginNode; 45 import org.graalvm.compiler.nodes.ReturnNode; 46 import org.graalvm.compiler.nodes.StructuredGraph; 47 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions; 48 import org.graalvm.compiler.nodes.debug.ControlFlowAnchorNode; 49 50 import jdk.vm.ci.meta.ResolvedJavaMethod; 51 import org.graalvm.compiler.phases.OptimisticOptimizations; 52 import org.graalvm.compiler.phases.OptimisticOptimizations.Optimization; 53 import org.graalvm.compiler.phases.tiers.HighTierContext; 54 55 public class ControlFlowAnchorDirectiveTest extends GraalCompilerTest { 56 57 @Retention(RetentionPolicy.RUNTIME) 58 @Target(ElementType.METHOD) 59 @Repeatable(AnchorSnippet.class) 60 private @interface NodeCount { 61 62 Class<? extends Node> nodeClass(); 63 64 int expectedCount(); 65 } 66 67 @Retention(RetentionPolicy.RUNTIME) 68 @Target(ElementType.METHOD) 69 private @interface AnchorSnippet { 70 NodeCount[] value(); 71 } 72 73 @NodeCount(nodeClass = ReturnNode.class, expectedCount = 1) 74 public static int verifyMergeSnippet(int arg) { 75 if (arg > 5) { 76 return 1; 77 } else { 78 return 2; 79 } 80 } 81 82 @NodeCount(nodeClass = ControlFlowAnchorNode.class, expectedCount = 2) 83 @NodeCount(nodeClass = ReturnNode.class, expectedCount = 2) 84 public static int preventMergeSnippet(int arg) { 85 if (arg > 5) { 86 GraalDirectives.controlFlowAnchor(); 87 return 1; 88 } else { 89 GraalDirectives.controlFlowAnchor(); 90 return 2; 91 } 92 } 93 94 @Test 95 public void testMerge() { 96 test("verifyMergeSnippet", 42); 97 test("preventMergeSnippet", 42); 98 } 99 100 @NodeCount(nodeClass = ReturnNode.class, expectedCount = 2) 101 public static int verifyDuplicateSnippet(int arg) { 102 int ret; 103 if (arg > 5) { 104 ret = 17; 105 } else { 106 ret = arg; 107 } 108 return 42 / ret; 109 } 110 111 @NodeCount(nodeClass = ControlFlowAnchorNode.class, expectedCount = 1) 112 @NodeCount(nodeClass = ReturnNode.class, expectedCount = 1) 113 public static int preventDuplicateSnippet(int arg) { 114 int ret; 115 if (arg > 5) { 116 ret = 17; 117 } else { 118 ret = arg; 119 } 120 GraalDirectives.controlFlowAnchor(); 121 return 42 / ret; 122 } 123 124 @Test 125 public void testDuplicate() { 126 // test("verifyDuplicateSnippet", 42); 127 test("preventDuplicateSnippet", 42); 128 } 129 130 @NodeCount(nodeClass = LoopBeginNode.class, expectedCount = 0) 131 public static int verifyFullUnrollSnippet(int arg) { 132 int ret = arg; 133 for (int i = 0; i < 5; i++) { 134 ret = ret * 3 + 1; 135 } 136 return ret; 137 } 138 139 @NodeCount(nodeClass = LoopBeginNode.class, expectedCount = 1) 140 @NodeCount(nodeClass = ControlFlowAnchorNode.class, expectedCount = 1) 141 public static int preventFullUnrollSnippet(int arg) { 142 int ret = arg; 143 for (int i = 0; i < 5; i++) { 144 GraalDirectives.controlFlowAnchor(); 145 ret = ret * 3 + 1; 146 } 147 return ret; 148 } 149 150 @Test 151 public void testFullUnroll() { 152 test("verifyFullUnrollSnippet", 42); 153 test("preventFullUnrollSnippet", 42); 154 } 155 156 @NodeCount(nodeClass = LoopBeginNode.class, expectedCount = 1) 157 @NodeCount(nodeClass = IfNode.class, expectedCount = 4) 158 public static void verifyPeelSnippet(int arg) { 159 int ret = arg; 160 while (ret > 1) { 161 if (ret % 2 == 0) { 162 ret /= 2; 163 } else { 164 ret = 3 * ret + 1; 165 } 166 } 167 } 168 169 @NodeCount(nodeClass = LoopBeginNode.class, expectedCount = 1) 170 @NodeCount(nodeClass = IfNode.class, expectedCount = 2) 171 public static void preventPeelSnippet(int arg) { 172 int ret = arg; 173 while (ret > 1) { 174 GraalDirectives.controlFlowAnchor(); 175 if (ret % 2 == 0) { 176 GraalDirectives.controlFlowAnchor(); 177 ret /= 2; 178 } else { 179 ret = 3 * ret + 1; 180 } 181 } 182 } 183 184 @Test 185 public void testPeel() { 186 test("preventPeelSnippet", 42); 187 } 188 189 @NodeCount(nodeClass = LoopBeginNode.class, expectedCount = 2) 190 public static void verifyUnswitchSnippet(int arg, boolean flag) { 191 int ret = arg; 192 while (GraalDirectives.injectBranchProbability(0.9999, ret < 1000)) { 193 if (flag) { 194 ret = ret * 2 + 1; 195 } else { 196 ret = ret * 3 + 1; 197 } 198 } 199 } 200 201 @NodeCount(nodeClass = LoopBeginNode.class, expectedCount = 1) 202 @NodeCount(nodeClass = IfNode.class, expectedCount = 2) 203 public static void preventUnswitchSnippet(int arg, boolean flag) { 204 int ret = arg; 205 while (GraalDirectives.injectBranchProbability(0.9999, ret < 1000)) { 206 if (flag) { 207 GraalDirectives.controlFlowAnchor(); 208 ret++; 209 } else { 210 ret += 2; 211 } 212 } 213 } 214 215 @Test 216 public void testUnswitch() { 217 test("verifyUnswitchSnippet", 0, false); 218 test("preventUnswitchSnippet", 0, false); 219 } 220 221 /** 222 * Cloning a ControlFlowAnchorNode is not allowed but cloning a whole graph containing one is 223 * ok. 224 */ 225 @Test 226 public void testClone() { 227 StructuredGraph g = parseEager("preventPeelSnippet", AllowAssumptions.NO); 228 g.copy(g.getDebug()); 229 } 230 231 private static List<NodeCount> getNodeCountAnnotations(StructuredGraph graph) { 232 ResolvedJavaMethod method = graph.method(); 233 AnchorSnippet snippet = method.getAnnotation(AnchorSnippet.class); 234 if (snippet != null) { 235 return Arrays.asList(snippet.value()); 236 } 237 238 NodeCount single = method.getAnnotation(NodeCount.class); 239 if (single != null) { 240 return Collections.singletonList(single); 241 } 242 243 return Collections.emptyList(); 244 } 245 246 @Override 247 protected HighTierContext getDefaultHighTierContext() { 248 return new HighTierContext(getProviders(), getDefaultGraphBuilderSuite(), OptimisticOptimizations.ALL.remove(Optimization.RemoveNeverExecutedCode)); 249 } 250 251 @Override 252 protected void checkLowTierGraph(StructuredGraph graph) { 253 List<ControlFlowAnchorNode> anchors = graph.getNodes().filter(ControlFlowAnchorNode.class).snapshot(); 254 for (int i = 0; i < anchors.size(); i++) { 255 ControlFlowAnchorNode a = anchors.get(i); 256 for (int j = i + 1; j < anchors.size(); j++) { 257 ControlFlowAnchorNode b = anchors.get(j); 258 if (a.valueEquals(b)) { 259 Assert.fail("found duplicated control flow anchors (" + a + " and " + b + ")"); 260 } 261 } 262 } 263 264 for (NodeCount nodeCount : getNodeCountAnnotations(graph)) { 265 NodeIterable<? extends Node> nodes = graph.getNodes().filter(nodeCount.nodeClass()); 266 Assert.assertEquals(nodeCount.nodeClass().getSimpleName(), nodeCount.expectedCount(), nodes.count()); 267 } 268 } 269 }