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