| /* |
| * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| package org.graalvm.compiler.core.match; |
| |
| import static org.graalvm.compiler.debug.GraalDebugConfig.Options.LogVerbose; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.graalvm.compiler.core.gen.NodeLIRBuilder; |
| import org.graalvm.compiler.core.match.MatchPattern.Result; |
| import org.graalvm.compiler.debug.Debug; |
| import org.graalvm.compiler.debug.GraalError; |
| import org.graalvm.compiler.graph.Node; |
| import org.graalvm.compiler.nodes.calc.FloatingNode; |
| import org.graalvm.compiler.nodes.virtual.VirtualObjectNode; |
| |
| /** |
| * Container for state captured during a match. |
| */ |
| public class MatchContext { |
| |
| private final Node root; |
| |
| private final List<Node> nodes; |
| |
| private final MatchStatement rule; |
| |
| private Map<String, NamedNode> namedNodes; |
| |
| private ArrayList<Node> consumed; |
| |
| private int startIndex; |
| |
| private int endIndex; |
| |
| private final NodeLIRBuilder builder; |
| |
| private static class NamedNode { |
| final Class<? extends Node> type; |
| final Node value; |
| |
| NamedNode(Class<? extends Node> type, Node value) { |
| this.type = type; |
| this.value = value; |
| } |
| } |
| |
| public MatchContext(NodeLIRBuilder builder, MatchStatement rule, int index, Node node, List<Node> nodes) { |
| this.builder = builder; |
| this.rule = rule; |
| this.root = node; |
| this.nodes = nodes; |
| assert index == nodes.indexOf(node); |
| // The root should be the last index since all the inputs must be scheduled before it. |
| startIndex = endIndex = index; |
| } |
| |
| public Node getRoot() { |
| return root; |
| } |
| |
| public Result captureNamedValue(String name, Class<? extends Node> type, Node value) { |
| if (namedNodes == null) { |
| namedNodes = new HashMap<>(2); |
| } |
| NamedNode current = namedNodes.get(name); |
| if (current == null) { |
| current = new NamedNode(type, value); |
| namedNodes.put(name, current); |
| return Result.OK; |
| } else { |
| if (current.value != value || current.type != type) { |
| return Result.namedValueMismatch(value, rule.getPattern()); |
| } |
| return Result.OK; |
| } |
| } |
| |
| public Result validate() { |
| // Ensure that there's no unsafe work in between these operations. |
| for (int i = startIndex; i <= endIndex; i++) { |
| Node node = nodes.get(i); |
| if (node instanceof VirtualObjectNode || node instanceof FloatingNode) { |
| // The order of evaluation of these nodes controlled by data dependence so they |
| // don't interfere with this match. |
| continue; |
| } else if ((consumed == null || !consumed.contains(node)) && node != root) { |
| if (LogVerbose.getValue()) { |
| Debug.log("unexpected node %s", node); |
| for (int j = startIndex; j <= endIndex; j++) { |
| Node theNode = nodes.get(j); |
| Debug.log("%s(%s) %1s", (consumed != null && consumed.contains(theNode) || theNode == root) ? "*" : " ", theNode.getUsageCount(), theNode); |
| } |
| } |
| return Result.notSafe(node, rule.getPattern()); |
| } |
| } |
| return Result.OK; |
| } |
| |
| /** |
| * Mark the interior nodes with INTERIOR_MATCH and set the Value of the root to be the result. |
| * During final LIR generation it will be evaluated to produce the actual LIR value. |
| * |
| * @param result |
| */ |
| public void setResult(ComplexMatchResult result) { |
| ComplexMatchValue value = new ComplexMatchValue(result); |
| if (Debug.isLogEnabled()) { |
| Debug.log("matched %s %s", rule.getName(), rule.getPattern()); |
| Debug.log("with nodes %s", rule.formatMatch(root)); |
| } |
| if (consumed != null) { |
| for (Node node : consumed) { |
| // All the interior nodes should be skipped during the normal doRoot calls in |
| // NodeLIRBuilder so mark them as interior matches. The root of the match will get a |
| // closure which will be evaluated to produce the final LIR. |
| builder.setMatchResult(node, ComplexMatchValue.INTERIOR_MATCH); |
| } |
| } |
| builder.setMatchResult(root, value); |
| } |
| |
| /** |
| * Mark a node as consumed by the match. Consumed nodes will never be evaluated. |
| * |
| * @return Result.OK if the node can be safely consumed. |
| */ |
| public Result consume(Node node) { |
| assert node.getUsageCount() <= 1 : "should have already been checked"; |
| |
| // Check NOT_IN_BLOCK first since that usually implies ALREADY_USED |
| int index = nodes.indexOf(node); |
| if (index == -1) { |
| return Result.notInBlock(node, rule.getPattern()); |
| } |
| |
| if (builder.hasOperand(node)) { |
| return Result.alreadyUsed(node, rule.getPattern()); |
| } |
| |
| startIndex = Math.min(startIndex, index); |
| if (consumed == null) { |
| consumed = new ArrayList<>(2); |
| } |
| consumed.add(node); |
| return Result.OK; |
| } |
| |
| /** |
| * Return the named node. It's an error if the |
| * |
| * @param name the name of a node in the match rule |
| * @return the matched node |
| * @throws GraalError is the named node doesn't exist. |
| */ |
| public Node namedNode(String name) { |
| if (namedNodes != null) { |
| NamedNode value = namedNodes.get(name); |
| if (value != null) { |
| return value.value; |
| } |
| } |
| throw new GraalError("missing node %s", name); |
| } |
| |
| @Override |
| public String toString() { |
| return String.format("%s %s (%d, %d) consumed %s", rule, root, startIndex, endIndex, consumed != null ? Arrays.toString(consumed.toArray()) : ""); |
| } |
| } |