001/*
002 * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.
008 *
009 * This code is distributed in the hope that it will be useful, but WITHOUT
010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
011 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
012 * version 2 for more details (a copy is included in the LICENSE file that
013 * accompanied this code).
014 *
015 * You should have received a copy of the GNU General Public License version
016 * 2 along with this work; if not, write to the Free Software Foundation,
017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
018 *
019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
020 * or visit www.oracle.com if you need additional information or have any
021 * questions.
022 */
023package com.oracle.graal.compiler.match;
024
025import com.oracle.graal.debug.*;
026
027import com.oracle.graal.graph.*;
028import com.oracle.graal.nodeinfo.*;
029
030/**
031 * A simple recursive pattern matcher for a DAG of nodes.
032 */
033
034public class MatchPattern {
035
036    enum MatchResultCode {
037        OK,
038        WRONG_CLASS,
039        NAMED_VALUE_MISMATCH,
040        TOO_MANY_USERS,
041        NOT_IN_BLOCK,
042        NOT_SAFE,
043        ALREADY_USED,
044    }
045
046    /**
047     * A descriptive result for match failures. This can be helpful for debugging why a match
048     * doesn't work as expected.
049     */
050    static class Result {
051        final MatchResultCode code;
052
053        final Node node;
054
055        final MatchPattern matcher;
056
057        Result(MatchResultCode result, Node node, MatchPattern matcher) {
058            this.code = result;
059            this.node = node;
060            this.matcher = matcher;
061        }
062
063        private static final DebugMetric MatchResult_WRONG_CLASS = Debug.metric("MatchResult_WRONG_CLASS");
064        private static final DebugMetric MatchResult_NAMED_VALUE_MISMATCH = Debug.metric("MatchResult_NAMED_VALUE_MISMATCH");
065        private static final DebugMetric MatchResult_TOO_MANY_USERS = Debug.metric("MatchResult_TOO_MANY_USERS");
066        private static final DebugMetric MatchResult_NOT_IN_BLOCK = Debug.metric("MatchResult_NOT_IN_BLOCK");
067        private static final DebugMetric MatchResult_NOT_SAFE = Debug.metric("MatchResult_NOT_SAFE");
068        private static final DebugMetric MatchResult_ALREADY_USED = Debug.metric("MatchResult_ALREADY_USED");
069
070        static final Result OK = new Result(MatchResultCode.OK, null, null);
071        private static final Result CACHED_WRONG_CLASS = new Result(MatchResultCode.WRONG_CLASS, null, null);
072        private static final Result CACHED_NAMED_VALUE_MISMATCH = new Result(MatchResultCode.NAMED_VALUE_MISMATCH, null, null);
073        private static final Result CACHED_TOO_MANY_USERS = new Result(MatchResultCode.TOO_MANY_USERS, null, null);
074        private static final Result CACHED_NOT_IN_BLOCK = new Result(MatchResultCode.NOT_IN_BLOCK, null, null);
075        private static final Result CACHED_NOT_SAFE = new Result(MatchResultCode.NOT_SAFE, null, null);
076        private static final Result CACHED_ALREADY_USED = new Result(MatchResultCode.ALREADY_USED, null, null);
077
078        static Result wrongClass(Node node, MatchPattern matcher) {
079            MatchResult_WRONG_CLASS.increment();
080            return Debug.isLogEnabled() ? new Result(MatchResultCode.WRONG_CLASS, node, matcher) : CACHED_WRONG_CLASS;
081        }
082
083        static Result namedValueMismatch(Node node, MatchPattern matcher) {
084            MatchResult_NAMED_VALUE_MISMATCH.increment();
085            return Debug.isLogEnabled() ? new Result(MatchResultCode.NAMED_VALUE_MISMATCH, node, matcher) : CACHED_NAMED_VALUE_MISMATCH;
086        }
087
088        static Result tooManyUsers(Node node, MatchPattern matcher) {
089            MatchResult_TOO_MANY_USERS.increment();
090            return Debug.isLogEnabled() ? new Result(MatchResultCode.TOO_MANY_USERS, node, matcher) : CACHED_TOO_MANY_USERS;
091        }
092
093        static Result notInBlock(Node node, MatchPattern matcher) {
094            MatchResult_NOT_IN_BLOCK.increment();
095            return Debug.isLogEnabled() ? new Result(MatchResultCode.NOT_IN_BLOCK, node, matcher) : CACHED_NOT_IN_BLOCK;
096        }
097
098        static Result notSafe(Node node, MatchPattern matcher) {
099            MatchResult_NOT_SAFE.increment();
100            return Debug.isLogEnabled() ? new Result(MatchResultCode.NOT_SAFE, node, matcher) : CACHED_NOT_SAFE;
101        }
102
103        static Result alreadyUsed(Node node, MatchPattern matcher) {
104            MatchResult_ALREADY_USED.increment();
105            return Debug.isLogEnabled() ? new Result(MatchResultCode.ALREADY_USED, node, matcher) : CACHED_ALREADY_USED;
106        }
107
108        @Override
109        public String toString() {
110            if (code == MatchResultCode.OK) {
111                return "OK";
112            }
113            if (node == null) {
114                return code.toString();
115            } else {
116                return code + " " + node.toString(Verbosity.Id) + "|" + node.getClass().getSimpleName() + " " + matcher;
117            }
118        }
119    }
120
121    /**
122     * The expected type of the node. It must match exactly.
123     */
124    private final Class<? extends Node> nodeClass;
125
126    /**
127     * An optional name for this node. A name can occur multiple times in a match and that name must
128     * always refer to the same node of the match will fail.
129     */
130    private final String name;
131
132    /**
133     * Patterns to match the inputs.
134     */
135    private final MatchPattern[] patterns;
136
137    /**
138     * The inputs to match the patterns against.
139     */
140    private final Position[] inputs;
141
142    /**
143     * Can there only be one user of the node. Constant nodes can be matched even if there are other
144     * users.
145     */
146    private final boolean singleUser;
147
148    private static final MatchPattern[] EMPTY_PATTERNS = new MatchPattern[0];
149
150    public MatchPattern(String name, boolean singleUser) {
151        this(null, name, singleUser);
152    }
153
154    public MatchPattern(Class<? extends Node> nodeClass, String name, boolean singleUser) {
155        this.nodeClass = nodeClass;
156        this.name = name;
157        this.singleUser = singleUser;
158        this.patterns = EMPTY_PATTERNS;
159        this.inputs = null;
160    }
161
162    private MatchPattern(Class<? extends Node> nodeClass, String name, boolean singleUser, MatchPattern[] patterns, Position[] inputs) {
163        assert inputs == null || inputs.length == patterns.length;
164        this.nodeClass = nodeClass;
165        this.name = name;
166        this.singleUser = singleUser;
167        this.patterns = patterns;
168        this.inputs = inputs;
169    }
170
171    public MatchPattern(Class<? extends Node> nodeClass, String name, MatchPattern first, Position[] inputs, boolean singleUser) {
172        this(nodeClass, name, singleUser, new MatchPattern[]{first}, inputs);
173    }
174
175    public MatchPattern(Class<? extends Node> nodeClass, String name, MatchPattern first, MatchPattern second, Position[] inputs, boolean singleUser) {
176        this(nodeClass, name, singleUser, new MatchPattern[]{first, second}, inputs);
177    }
178
179    public MatchPattern(Class<? extends Node> nodeClass, String name, MatchPattern first, MatchPattern second, MatchPattern third, Position[] inputs, boolean singleUser) {
180        this(nodeClass, name, singleUser, new MatchPattern[]{first, second, third}, inputs);
181    }
182
183    Class<? extends Node> nodeClass() {
184        return nodeClass;
185    }
186
187    private Result matchType(Node node) {
188        if (nodeClass != null && node.getClass() != nodeClass) {
189            return Result.wrongClass(node, this);
190        }
191        return Result.OK;
192    }
193
194    /**
195     * Match any named nodes and ensure that the consumed nodes can be safely merged.
196     *
197     * @param node
198     * @param context
199     * @return Result.OK is the pattern can be safely matched.
200     */
201    Result matchUsage(Node node, MatchContext context) {
202        Result result = matchUsage(node, context, true);
203        if (result == Result.OK) {
204            result = context.validate();
205        }
206        return result;
207    }
208
209    private Result matchUsage(Node node, MatchContext context, boolean atRoot) {
210        Result result = matchType(node);
211        if (result != Result.OK) {
212            return result;
213        }
214        if (singleUser && !atRoot) {
215            result = context.consume(node);
216            if (result != Result.OK) {
217                return result;
218            }
219        }
220
221        if (name != null) {
222            result = context.captureNamedValue(name, nodeClass, node);
223        }
224
225        for (int input = 0; input < patterns.length; input++) {
226            result = patterns[input].matchUsage(getInput(input, node), context, false);
227            if (result != Result.OK) {
228                return result;
229            }
230        }
231
232        return result;
233    }
234
235    /**
236     * Recursively match the shape of the tree without worry about named values. Most matches fail
237     * at this point so it's performed first.
238     *
239     * @param node
240     * @param statement
241     * @return Result.OK if the shape of the pattern matches.
242     */
243    public Result matchShape(Node node, MatchStatement statement) {
244        return matchShape(node, statement, true);
245    }
246
247    private Result matchShape(Node node, MatchStatement statement, boolean atRoot) {
248        Result result = matchType(node);
249        if (result != Result.OK) {
250            return result;
251        }
252
253        if (singleUser && !atRoot) {
254            if (node.getUsageCount() > 1) {
255                return Result.tooManyUsers(node, statement.getPattern());
256            }
257        }
258
259        for (int input = 0; input < patterns.length; input++) {
260            result = patterns[input].matchShape(getInput(input, node), statement, false);
261            if (result != Result.OK) {
262                return result;
263            }
264        }
265
266        return result;
267    }
268
269    /**
270     * For a node starting at root, produce a String showing the inputs that matched against this
271     * rule. It's assumed that a match has already succeeded against this rule, otherwise the
272     * printing may produce exceptions.
273     */
274    public String formatMatch(Node root) {
275        String result = String.format("%s", root);
276        if (patterns.length == 0) {
277            return result;
278        } else {
279            StringBuilder sb = new StringBuilder();
280            sb.append("(");
281            sb.append(result);
282            for (int input = 0; input < patterns.length; input++) {
283                sb.append(" ");
284                sb.append(patterns[input].formatMatch(getInput(input, root)));
285            }
286            sb.append(")");
287            return sb.toString();
288        }
289    }
290
291    private Node getInput(int index, Node node) {
292        return inputs[index].get(node);
293    }
294
295    @Override
296    public String toString() {
297        if (nodeClass == null) {
298            return name;
299        } else {
300            String nodeName = nodeClass.getSimpleName();
301            if (nodeName.endsWith("Node")) {
302                nodeName = nodeName.substring(0, nodeName.length() - 4);
303            }
304            if (patterns.length == 0) {
305                return nodeName + (name != null ? "=" + name : "");
306            } else {
307                StringBuilder sb = new StringBuilder();
308                sb.append("(");
309                sb.append(nodeName);
310                for (int index = 0; index < patterns.length; index++) {
311                    sb.append(" ");
312                    sb.append(patterns[index].toString());
313                }
314                sb.append(")");
315                return sb.toString();
316            }
317        }
318    }
319}