/*
* Copyright (c) 2014, 2019, 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.core.common.cfg.AbstractControlFlowGraph.dominates;
import static org.graalvm.compiler.core.common.cfg.AbstractControlFlowGraph.strictlyDominates;
import static org.graalvm.compiler.debug.DebugOptions.LogVerbose;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import jdk.internal.vm.compiler.collections.EconomicMap;
import jdk.internal.vm.compiler.collections.Equivalence;
import org.graalvm.compiler.core.common.cfg.BlockMap;
import org.graalvm.compiler.core.gen.NodeLIRBuilder;
import org.graalvm.compiler.core.match.MatchPattern.Result;
import org.graalvm.compiler.debug.CounterKey;
import org.graalvm.compiler.debug.DebugContext;
import org.graalvm.compiler.debug.GraalError;
import org.graalvm.compiler.graph.Node;
import org.graalvm.compiler.graph.NodeMap;
import org.graalvm.compiler.nodes.PhiNode;
import org.graalvm.compiler.nodes.StructuredGraph;
import org.graalvm.compiler.nodes.calc.FloatingNode;
import org.graalvm.compiler.nodes.cfg.Block;
import org.graalvm.compiler.nodes.virtual.VirtualObjectNode;
/**
* Container for state captured during a match.
*/
public class MatchContext {
private static final CounterKey MatchContextSuccessDifferentBlocks = DebugContext.counter("MatchContextSuccessDifferentBlocks");
private final Node root;
private final MatchStatement rule;
private final StructuredGraph.ScheduleResult schedule;
private EconomicMap<String, NamedNode> namedNodes;
/**
* A node consumed by a match. Keeps track of whether side effects can be ignored.
*/
static final class ConsumedNode {
final Node node;
final boolean ignoresSideEffects;
ConsumedNode(Node node, boolean ignoresSideEffects) {
this.node = node;
this.ignoresSideEffects = ignoresSideEffects;
}
}
/**
* The collection of nodes consumed by this match.
*/
static final class ConsumedNodes implements Iterable<ConsumedNode> {
private ArrayList<ConsumedNode> nodes;
ConsumedNodes() {
this.nodes = null;
}
public void add(Node node, boolean ignoresSideEffects) {
if (nodes == null) {
nodes = new ArrayList<>(2);
}
nodes.add(new ConsumedNode(node, ignoresSideEffects));
}
public boolean contains(Node node) {
for (ConsumedNode c : nodes) {
if (c.node == node) {
return true;
}
}
return false;
}
public ConsumedNode find(Node node) {
for (ConsumedNode c : nodes) {
if (c.node == node) {
return c;
}
}
return null;
}
@Override
public String toString() {
Node[] arr = new Node[nodes.size()];
int i = 0;
for (ConsumedNode c : nodes) {
arr[i++] = c.node;
}
return Arrays.toString(arr);
}
@Override
public Iterator<ConsumedNode> iterator() {
return nodes.iterator();
}
}
private ConsumedNodes consumed = new ConsumedNodes();
private Block rootBlock;
private int rootIndex;
/**
* Block and index in the block at which the match should be emitted. Differs from
* rootBlock/rootIndex for (ZeroExtend Read=access) for instance: match should be emitted where
* the Read is.
*/
private int emitIndex;
private Block emitBlock;
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, Block rootBlock, StructuredGraph.ScheduleResult schedule) {
this.builder = builder;
this.rule = rule;
this.root = node;
assert index == schedule.getBlockToNodesMap().get(rootBlock).indexOf(node);
this.schedule = schedule;
// The root should be the last index since all the inputs must be scheduled before it.
this.rootBlock = rootBlock;
rootIndex = index;
}
public Node getRoot() {
return root;
}
public Result captureNamedValue(String name, Class<? extends Node> type, Node value) {
if (namedNodes == null) {
namedNodes = EconomicMap.create(Equivalence.DEFAULT);
}
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() {
Result result = findEarlyPosition();
if (result != Result.OK) {
return result;
}
findLatePosition();
assert emitIndex == rootIndex || consumed.find(root).ignoresSideEffects;
return verifyInputs();
}
private Result findEarlyPosition() {
int startIndexSideEffect = -1;
int endIndexSideEffect = -1;
final NodeMap<Block> nodeToBlockMap = schedule.getNodeToBlockMap();
final BlockMap<List<Node>> blockToNodesMap = schedule.getBlockToNodesMap();
// Nodes affected by side effects must be in the same block
for (ConsumedNode cn : consumed) {
if (!cn.ignoresSideEffects) {
Block b = nodeToBlockMap.get(cn.node);
if (emitBlock == null) {
emitBlock = b;
startIndexSideEffect = endIndexSideEffect = blockToNodesMap.get(b).indexOf(cn.node);
} else if (emitBlock == b) {
int index = blockToNodesMap.get(b).indexOf(cn.node);
startIndexSideEffect = Math.min(startIndexSideEffect, index);
endIndexSideEffect = Math.max(endIndexSideEffect, index);
} else {
logFailedMatch("nodes affected by side effects in different blocks %s", cn.node);
return Result.notInBlock(cn.node, rule.getPattern());
}
}
}
if (emitBlock != null) {
// There must be no side effects between nodes that are affected by side effects
assert startIndexSideEffect != -1 && endIndexSideEffect != -1;
final List<Node> nodes = blockToNodesMap.get(emitBlock);
for (int i = startIndexSideEffect; i <= endIndexSideEffect; i++) {
Node node = nodes.get(i);
if (!sideEffectFree(node) && !consumed.contains(node)) {
logFailedMatch("unexpected side effect %s", node);
return Result.notSafe(node, rule.getPattern());
}
}
// early position is at the node affected by side effects the closest to the root
emitIndex = endIndexSideEffect;
} else {
// Nodes not affected by side effect: early position is at the root
emitBlock = nodeToBlockMap.get(root);
emitIndex = rootIndex;
}
return Result.OK;
}
private static boolean sideEffectFree(Node node) {
// The order of evaluation of these nodes controlled by data dependence so they
// don't interfere with this match.
return node instanceof VirtualObjectNode || node instanceof FloatingNode;
}
private void findLatePosition() {
// If emit position is at a node affected by side effects that's followed by side effect
// free nodes, the node can be emitted later. This helps when the match has inputs that are
// late in the block.
int index = rootIndex;
if (emitBlock != rootBlock) {
index = schedule.getBlockToNodesMap().get(emitBlock).size() - 1;
}
final List<Node> emitBlockNodes = schedule.getBlockToNodesMap().get(emitBlock);
for (int i = emitIndex + 1; i <= index; i++) {
Node node = emitBlockNodes.get(i);
ConsumedNode cn = consumed.find(node);
if (cn == null) {
if (!sideEffectFree(node)) {
return;
}
} else {
assert cn.ignoresSideEffects;
emitIndex = i;
}
}
}
private Result verifyInputs() {
DebugContext debug = root.getDebug();
if (emitBlock != rootBlock) {
assert consumed.find(root).ignoresSideEffects;
Result result = verifyInputsDifferentBlock(root);
if (result == Result.OK) {
MatchContextSuccessDifferentBlocks.increment(debug);
}
return result;
}
// We are going to emit the match at emitIndex. We need to make sure nodes of the match
// between emitIndex and rootIndex don't have inputs after position emitIndex that would
// make emitIndex an illegal position.
final List<Node> nodes = schedule.getBlockToNodesMap().get(rootBlock);
for (int i = emitIndex + 1; i <= rootIndex; i++) {
Node node = nodes.get(i);
ConsumedNode cn = consumed.find(node);
if (cn != null) {
assert cn.ignoresSideEffects;
for (Node in : node.inputs()) {
if (!consumed.contains(in)) {
for (int j = emitIndex + 1; j < i; j++) {
if (nodes.get(j) == in) {
logFailedMatch("Earliest position in block is too late %s", in);
assert consumed.find(root).ignoresSideEffects;
assert verifyInputsDifferentBlock(root) != Result.OK;
return Result.tooLate(node, rule.getPattern());
}
}
}
}
}
}
assert verifyInputsDifferentBlock(root) == Result.OK;
return Result.OK;
}
private Result verifyInputsDifferentBlock(Node node) {
// Is there an input that's not part of the match that's after the emit position?
for (Node in : node.inputs()) {
if (in instanceof PhiNode) {
Block b = schedule.getNodeToBlockMap().get(((PhiNode) in).merge());
if (dominates(b, emitBlock)) {
continue;
}
} else {
Block b = schedule.getNodeToBlockMap().get(in);
if (strictlyDominates(b, emitBlock) || (b == emitBlock && schedule.getBlockToNodesMap().get(emitBlock).indexOf(in) <= emitIndex)) {
continue;
}
}
ConsumedNode cn = consumed.find(in);
if (cn == null) {
logFailedMatch("Earliest position in block is too late %s", in);
return Result.tooLate(node, rule.getPattern());
}
assert cn.ignoresSideEffects;
Result res = verifyInputsDifferentBlock(in);
if (res != Result.OK) {
return res;
}
}
return Result.OK;
}
private void logFailedMatch(String s, Node node) {
if (LogVerbose.getValue(root.getOptions())) {
DebugContext debug = root.getDebug();
debug.log(s, node);
int startIndex = emitIndex;
if (emitBlock != rootBlock) {
int endIndex = schedule.getBlockToNodesMap().get(emitBlock).size() - 1;
final List<Node> emitBlockNodes = schedule.getBlockToNodesMap().get(emitBlock);
debug.log("%s:", emitBlock);
for (int j = startIndex; j <= endIndex; j++) {
Node theNode = emitBlockNodes.get(j);
debug.log("%s(%s) %1s", consumed.contains(theNode) ? "*" : " ", theNode.getUsageCount(), theNode);
}
startIndex = 0;
}
debug.log("%s:", rootBlock);
final List<Node> nodes = schedule.getBlockToNodesMap().get(rootBlock);
for (int j = startIndex; j <= rootIndex; j++) {
Node theNode = nodes.get(j);
debug.log("%s(%s) %1s", consumed.contains(theNode) ? "*" : " ", theNode.getUsageCount(), theNode);
}
}
}
/**
* 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);
Node emitNode = schedule.getBlockToNodesMap().get(emitBlock).get(emitIndex);
DebugContext debug = root.getDebug();
if (debug.isLogEnabled()) {
debug.log("matched %s %s%s", rule.getName(), rule.getPattern(), emitIndex != rootIndex ? " skipping side effects" : "");
debug.log("with nodes %s", rule.formatMatch(root));
}
for (ConsumedNode cn : consumed) {
if (cn.node == root || cn.node == emitNode) {
continue;
}
// 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(cn.node, ComplexMatchValue.INTERIOR_MATCH);
}
builder.setMatchResult(emitNode, value);
if (root != emitNode) {
/**代码未完, 请加载全部代码(NowJava.com).**/