1 /* 2 * Copyright (c) 2014, 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 package org.graalvm.compiler.core.match; 24 25 import org.graalvm.compiler.debug.Debug; 26 import org.graalvm.compiler.debug.DebugCounter; 27 import org.graalvm.compiler.graph.Node; 28 import org.graalvm.compiler.graph.Position; 29 import org.graalvm.compiler.nodeinfo.InputType; 30 import org.graalvm.compiler.nodeinfo.Verbosity; 31 32 /** 33 * A simple recursive pattern matcher for a DAG of nodes. 34 */ 35 36 public class MatchPattern { 37 38 enum MatchResultCode { 39 OK, 40 WRONG_CLASS, 41 NAMED_VALUE_MISMATCH, 42 TOO_MANY_USERS, 43 NOT_IN_BLOCK, 44 NOT_SAFE, 45 ALREADY_USED, 46 } 47 48 /** 49 * A descriptive result for match failures. This can be helpful for debugging why a match 50 * doesn't work as expected. 51 */ 52 static class Result { 53 final MatchResultCode code; 54 55 final Node node; 56 57 final MatchPattern matcher; 58 59 Result(MatchResultCode result, Node node, MatchPattern matcher) { 60 this.code = result; 61 this.node = node; 62 this.matcher = matcher; 63 } 64 65 private static final DebugCounter MatchResult_WRONG_CLASS = Debug.counter("MatchResult_WRONG_CLASS"); 66 private static final DebugCounter MatchResult_NAMED_VALUE_MISMATCH = Debug.counter("MatchResult_NAMED_VALUE_MISMATCH"); 67 private static final DebugCounter MatchResult_TOO_MANY_USERS = Debug.counter("MatchResult_TOO_MANY_USERS"); 68 private static final DebugCounter MatchResult_NOT_IN_BLOCK = Debug.counter("MatchResult_NOT_IN_BLOCK"); 69 private static final DebugCounter MatchResult_NOT_SAFE = Debug.counter("MatchResult_NOT_SAFE"); 70 private static final DebugCounter MatchResult_ALREADY_USED = Debug.counter("MatchResult_ALREADY_USED"); 71 72 static final Result OK = new Result(MatchResultCode.OK, null, null); 73 private static final Result CACHED_WRONG_CLASS = new Result(MatchResultCode.WRONG_CLASS, null, null); 74 private static final Result CACHED_NAMED_VALUE_MISMATCH = new Result(MatchResultCode.NAMED_VALUE_MISMATCH, null, null); 75 private static final Result CACHED_TOO_MANY_USERS = new Result(MatchResultCode.TOO_MANY_USERS, null, null); 76 private static final Result CACHED_NOT_IN_BLOCK = new Result(MatchResultCode.NOT_IN_BLOCK, null, null); 77 private static final Result CACHED_NOT_SAFE = new Result(MatchResultCode.NOT_SAFE, null, null); 78 private static final Result CACHED_ALREADY_USED = new Result(MatchResultCode.ALREADY_USED, null, null); 79 80 static Result wrongClass(Node node, MatchPattern matcher) { 81 MatchResult_WRONG_CLASS.increment(); 82 return Debug.isLogEnabled() ? new Result(MatchResultCode.WRONG_CLASS, node, matcher) : CACHED_WRONG_CLASS; 83 } 84 85 static Result namedValueMismatch(Node node, MatchPattern matcher) { 86 MatchResult_NAMED_VALUE_MISMATCH.increment(); 87 return Debug.isLogEnabled() ? new Result(MatchResultCode.NAMED_VALUE_MISMATCH, node, matcher) : CACHED_NAMED_VALUE_MISMATCH; 88 } 89 90 static Result tooManyUsers(Node node, MatchPattern matcher) { 91 MatchResult_TOO_MANY_USERS.increment(); 92 return Debug.isLogEnabled() ? new Result(MatchResultCode.TOO_MANY_USERS, node, matcher) : CACHED_TOO_MANY_USERS; 93 } 94 95 static Result notInBlock(Node node, MatchPattern matcher) { 96 MatchResult_NOT_IN_BLOCK.increment(); 97 return Debug.isLogEnabled() ? new Result(MatchResultCode.NOT_IN_BLOCK, node, matcher) : CACHED_NOT_IN_BLOCK; 98 } 99 100 static Result notSafe(Node node, MatchPattern matcher) { 101 MatchResult_NOT_SAFE.increment(); 102 return Debug.isLogEnabled() ? new Result(MatchResultCode.NOT_SAFE, node, matcher) : CACHED_NOT_SAFE; 103 } 104 105 static Result alreadyUsed(Node node, MatchPattern matcher) { 106 MatchResult_ALREADY_USED.increment(); 107 return Debug.isLogEnabled() ? new Result(MatchResultCode.ALREADY_USED, node, matcher) : CACHED_ALREADY_USED; 108 } 109 110 @Override 111 public String toString() { 112 if (code == MatchResultCode.OK) { 113 return "OK"; 114 } 115 if (node == null) { 116 return code.toString(); 117 } else { 118 return code + " " + node.toString(Verbosity.Id) + "|" + node.getClass().getSimpleName() + " " + matcher; 119 } 120 } 121 } 122 123 /** 124 * The expected type of the node. It must match exactly. 125 */ 126 private final Class<? extends Node> nodeClass; 127 128 /** 129 * An optional name for this node. A name can occur multiple times in a match and that name must 130 * always refer to the same node of the match will fail. 131 */ 132 private final String name; 133 134 /** 135 * Patterns to match the inputs. 136 */ 137 private final MatchPattern[] patterns; 138 139 /** 140 * The inputs to match the patterns against. 141 */ 142 private final Position[] inputs; 143 144 /** 145 * Can there only be one user of the node. Constant nodes can be matched even if there are other 146 * users. 147 */ 148 private final boolean singleUser; 149 150 private static final MatchPattern[] EMPTY_PATTERNS = new MatchPattern[0]; 151 152 public MatchPattern(String name, boolean singleUser) { 153 this(null, name, singleUser); 154 } 155 156 public MatchPattern(Class<? extends Node> nodeClass, String name, boolean singleUser) { 157 this.nodeClass = nodeClass; 158 this.name = name; 159 this.singleUser = singleUser; 160 this.patterns = EMPTY_PATTERNS; 161 this.inputs = null; 162 } 163 164 private MatchPattern(Class<? extends Node> nodeClass, String name, boolean singleUser, MatchPattern[] patterns, Position[] inputs) { 165 assert inputs == null || inputs.length == patterns.length; 166 this.nodeClass = nodeClass; 167 this.name = name; 168 this.singleUser = singleUser; 169 this.patterns = patterns; 170 this.inputs = inputs; 171 } 172 173 public MatchPattern(Class<? extends Node> nodeClass, String name, MatchPattern first, Position[] inputs, boolean singleUser) { 174 this(nodeClass, name, singleUser, new MatchPattern[]{first}, inputs); 175 } 176 177 public MatchPattern(Class<? extends Node> nodeClass, String name, MatchPattern first, MatchPattern second, Position[] inputs, boolean singleUser) { 178 this(nodeClass, name, singleUser, new MatchPattern[]{first, second}, inputs); 179 } 180 181 public MatchPattern(Class<? extends Node> nodeClass, String name, MatchPattern first, MatchPattern second, MatchPattern third, Position[] inputs, boolean singleUser) { 182 this(nodeClass, name, singleUser, new MatchPattern[]{first, second, third}, inputs); 183 } 184 185 Class<? extends Node> nodeClass() { 186 return nodeClass; 187 } 188 189 private Result matchType(Node node) { 190 if (nodeClass != null && node.getClass() != nodeClass) { 191 return Result.wrongClass(node, this); 192 } 193 return Result.OK; 194 } 195 196 /** 197 * Match any named nodes and ensure that the consumed nodes can be safely merged. 198 * 199 * @param node 200 * @param context 201 * @return Result.OK is the pattern can be safely matched. 202 */ 203 Result matchUsage(Node node, MatchContext context) { 204 Result result = matchUsage(node, context, true); 205 if (result == Result.OK) { 206 result = context.validate(); 207 } 208 return result; 209 } 210 211 private Result matchUsage(Node node, MatchContext context, boolean atRoot) { 212 Result result = matchType(node); 213 if (result != Result.OK) { 214 return result; 215 } 216 if (singleUser && !atRoot) { 217 result = context.consume(node); 218 if (result != Result.OK) { 219 return result; 220 } 221 } 222 223 if (name != null) { 224 result = context.captureNamedValue(name, nodeClass, node); 225 } 226 227 for (int input = 0; input < patterns.length; input++) { 228 result = patterns[input].matchUsage(getInput(input, node), context, false); 229 if (result != Result.OK) { 230 return result; 231 } 232 } 233 234 return result; 235 } 236 237 /** 238 * Recursively match the shape of the tree without worry about named values. Most matches fail 239 * at this point so it's performed first. 240 * 241 * @param node 242 * @param statement 243 * @return Result.OK if the shape of the pattern matches. 244 */ 245 public Result matchShape(Node node, MatchStatement statement) { 246 return matchShape(node, statement, true); 247 } 248 249 private Result matchShape(Node node, MatchStatement statement, boolean atRoot) { 250 Result result = matchType(node); 251 if (result != Result.OK) { 252 return result; 253 } 254 255 if (singleUser && !atRoot) { 256 if (!isSingleValueUser(node)) { 257 return Result.tooManyUsers(node, statement.getPattern()); 258 } 259 } 260 261 for (int input = 0; input < patterns.length; input++) { 262 result = patterns[input].matchShape(getInput(input, node), statement, false); 263 if (result != Result.OK) { 264 return result; 265 } 266 } 267 268 return result; 269 } 270 271 public static boolean isSingleValueUser(Node node) { 272 int valueUsage = node.getUsageCount(); 273 if (valueUsage == 1) { 274 return true; 275 } 276 if (node.isAllowedUsageType(InputType.Guard)) { 277 // See if the other usages are non-Value usages. 278 valueUsage = 0; 279 for (Node usage : node.usages()) { 280 for (Position input : usage.inputPositions()) { 281 if (input.getInputType() == InputType.Value && input.get(usage) == node) { 282 valueUsage++; 283 if (valueUsage > 1) { 284 // Too many value users 285 return false; 286 } 287 } 288 } 289 } 290 assert valueUsage == 1; 291 return true; 292 } 293 return false; 294 } 295 296 /** 297 * For a node starting at root, produce a String showing the inputs that matched against this 298 * rule. It's assumed that a match has already succeeded against this rule, otherwise the 299 * printing may produce exceptions. 300 */ 301 public String formatMatch(Node root) { 302 String result = String.format("%s", root); 303 if (patterns.length == 0) { 304 return result; 305 } else { 306 StringBuilder sb = new StringBuilder(); 307 sb.append("("); 308 sb.append(result); 309 for (int input = 0; input < patterns.length; input++) { 310 sb.append(" "); 311 sb.append(patterns[input].formatMatch(getInput(input, root))); 312 } 313 sb.append(")"); 314 return sb.toString(); 315 } 316 } 317 318 private Node getInput(int index, Node node) { 319 return inputs[index].get(node); 320 } 321 322 @Override 323 public String toString() { 324 if (nodeClass == null) { 325 return name; 326 } else { 327 String nodeName = nodeClass.getSimpleName(); 328 if (nodeName.endsWith("Node")) { 329 nodeName = nodeName.substring(0, nodeName.length() - 4); 330 } 331 if (patterns.length == 0) { 332 return nodeName + (name != null ? "=" + name : ""); 333 } else { 334 StringBuilder sb = new StringBuilder(); 335 sb.append("("); 336 sb.append(nodeName); 337 for (int index = 0; index < patterns.length; index++) { 338 sb.append(" "); 339 sb.append(patterns[index].toString()); 340 } 341 sb.append(")"); 342 return sb.toString(); 343 } 344 } 345 } 346 }