/* * 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 nodes; private final MatchStatement rule; private Map namedNodes; private ArrayList consumed; private int startIndex; private int endIndex; private final NodeLIRBuilder builder; private static class NamedNode { final Class type; final Node value; NamedNode(Class type, Node value) { this.type = type; this.value = value; } } public MatchContext(NodeLIRBuilder builder, MatchStatement rule, int index, Node node, List 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 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()) : ""); } }