1 /* 2 * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 25 package org.graalvm.compiler.core.match; 26 27 import static org.graalvm.compiler.debug.DebugOptions.LogVerbose; 28 29 import java.util.ArrayList; 30 import java.util.Arrays; 31 import java.util.List; 32 33 import jdk.internal.vm.compiler.collections.EconomicMap; 34 import jdk.internal.vm.compiler.collections.Equivalence; 35 import org.graalvm.compiler.core.gen.NodeLIRBuilder; 36 import org.graalvm.compiler.core.match.MatchPattern.Result; 37 import org.graalvm.compiler.debug.DebugContext; 38 import org.graalvm.compiler.debug.GraalError; 39 import org.graalvm.compiler.graph.Node; 40 import org.graalvm.compiler.nodes.calc.FloatingNode; 41 import org.graalvm.compiler.nodes.virtual.VirtualObjectNode; 42 43 /** 44 * Container for state captured during a match. 45 */ 46 public class MatchContext { 47 48 private final Node root; 49 50 private final List<Node> nodes; 51 52 private final MatchStatement rule; 53 54 private EconomicMap<String, NamedNode> namedNodes; 55 56 private ArrayList<Node> consumed; 57 58 private int startIndex; 59 60 private int endIndex; 61 62 private final NodeLIRBuilder builder; 63 64 private static class NamedNode { 65 final Class<? extends Node> type; 66 final Node value; 67 68 NamedNode(Class<? extends Node> type, Node value) { 69 this.type = type; 70 this.value = value; 71 } 72 } 73 74 public MatchContext(NodeLIRBuilder builder, MatchStatement rule, int index, Node node, List<Node> nodes) { 75 this.builder = builder; 76 this.rule = rule; 77 this.root = node; 78 this.nodes = nodes; 79 assert index == nodes.indexOf(node); 80 // The root should be the last index since all the inputs must be scheduled before it. 81 startIndex = endIndex = index; 82 } 83 84 public Node getRoot() { 85 return root; 86 } 87 88 public Result captureNamedValue(String name, Class<? extends Node> type, Node value) { 89 if (namedNodes == null) { 90 namedNodes = EconomicMap.create(Equivalence.DEFAULT); 91 } 92 NamedNode current = namedNodes.get(name); 93 if (current == null) { 94 current = new NamedNode(type, value); 95 namedNodes.put(name, current); 96 return Result.OK; 97 } else { 98 if (current.value != value || current.type != type) { 99 return Result.namedValueMismatch(value, rule.getPattern()); 100 } 101 return Result.OK; 102 } 103 } 104 105 public Result validate() { 106 // Ensure that there's no unsafe work in between these operations. 107 for (int i = startIndex; i <= endIndex; i++) { 108 Node node = nodes.get(i); 109 if (node instanceof VirtualObjectNode || node instanceof FloatingNode) { 110 // The order of evaluation of these nodes controlled by data dependence so they 111 // don't interfere with this match. 112 continue; 113 } else if ((consumed == null || !consumed.contains(node)) && node != root) { 114 if (LogVerbose.getValue(root.getOptions())) { 115 DebugContext debug = root.getDebug(); 116 debug.log("unexpected node %s", node); 117 for (int j = startIndex; j <= endIndex; j++) { 118 Node theNode = nodes.get(j); 119 debug.log("%s(%s) %1s", (consumed != null && consumed.contains(theNode) || theNode == root) ? "*" : " ", theNode.getUsageCount(), theNode); 120 } 121 } 122 return Result.notSafe(node, rule.getPattern()); 123 } 124 } 125 return Result.OK; 126 } 127 128 /** 129 * Mark the interior nodes with INTERIOR_MATCH and set the Value of the root to be the result. 130 * During final LIR generation it will be evaluated to produce the actual LIR value. 131 * 132 * @param result 133 */ 134 public void setResult(ComplexMatchResult result) { 135 ComplexMatchValue value = new ComplexMatchValue(result); 136 DebugContext debug = root.getDebug(); 137 if (debug.isLogEnabled()) { 138 debug.log("matched %s %s", rule.getName(), rule.getPattern()); 139 debug.log("with nodes %s", rule.formatMatch(root)); 140 } 141 if (consumed != null) { 142 for (Node node : consumed) { 143 // All the interior nodes should be skipped during the normal doRoot calls in 144 // NodeLIRBuilder so mark them as interior matches. The root of the match will get a 145 // closure which will be evaluated to produce the final LIR. 146 builder.setMatchResult(node, ComplexMatchValue.INTERIOR_MATCH); 147 } 148 } 149 builder.setMatchResult(root, value); 150 } 151 152 /** 153 * Mark a node as consumed by the match. Consumed nodes will never be evaluated. 154 * 155 * @return Result.OK if the node can be safely consumed. 156 */ 157 public Result consume(Node node) { 158 assert MatchPattern.isSingleValueUser(node) : "should have already been checked"; 159 160 // Check NOT_IN_BLOCK first since that usually implies ALREADY_USED 161 int index = nodes.indexOf(node); 162 if (index == -1) { 163 return Result.notInBlock(node, rule.getPattern()); 164 } 165 166 if (builder.hasOperand(node)) { 167 return Result.alreadyUsed(node, rule.getPattern()); 168 } 169 170 startIndex = Math.min(startIndex, index); 171 if (consumed == null) { 172 consumed = new ArrayList<>(2); 173 } 174 consumed.add(node); 175 return Result.OK; 176 } 177 178 /** 179 * Return the named node. It's an error if the 180 * 181 * @param name the name of a node in the match rule 182 * @return the matched node 183 * @throws GraalError is the named node doesn't exist. 184 */ 185 public Node namedNode(String name) { 186 if (namedNodes != null) { 187 NamedNode value = namedNodes.get(name); 188 if (value != null) { 189 return value.value; 190 } 191 } 192 throw new GraalError("missing node %s", name); 193 } 194 195 @Override 196 public String toString() { 197 return String.format("%s %s (%d, %d) consumed %s", rule, root, startIndex, endIndex, consumed != null ? Arrays.toString(consumed.toArray()) : ""); 198 } 199 }