blob: 9abe43aefffd7412b14ca6e940e76a63bd3d3b2f [file] [log] [blame]
/*
* 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()) : "");
}
}