1 /* 2 * Copyright (c) 2014, 2015, 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 package org.graalvm.compiler.core.match; 24 25 import static org.graalvm.compiler.debug.GraalDebugConfig.Options.LogVerbose; 26 import java.util.ArrayList; 27 import java.util.List; 28 29 import org.graalvm.compiler.core.gen.NodeMatchRules; 30 import org.graalvm.compiler.debug.Debug; 31 import org.graalvm.compiler.debug.Debug.Scope; 32 import org.graalvm.compiler.debug.GraalError; 33 import org.graalvm.compiler.graph.Edges; 34 import org.graalvm.compiler.graph.Node; 35 import org.graalvm.compiler.graph.NodeClass; 36 import org.graalvm.compiler.graph.Position; 37 import org.graalvm.compiler.options.OptionValues; 38 import org.graalvm.compiler.serviceprovider.GraalServices; 39 import org.graalvm.util.Equivalence; 40 import org.graalvm.util.EconomicMap; 41 import org.graalvm.util.MapCursor; 42 43 public class MatchRuleRegistry { 44 45 /** 46 * Convert a list of field names into {@link org.graalvm.compiler.graph.Position} objects that 47 * can be used to read them during a match. The names should already have been confirmed to 48 * exist in the type. 49 * 50 * @param nodeClass 51 * @param names 52 * @return an array of Position objects corresponding to the named fields. 53 */ 54 public static Position[] findPositions(NodeClass<? extends Node> nodeClass, String[] names) { 55 Position[] result = new Position[names.length]; 56 for (int i = 0; i < names.length; i++) { 57 Edges edges = nodeClass.getInputEdges(); 58 for (int e = 0; e < edges.getDirectCount(); e++) { 59 if (names[i].equals(edges.getName(e))) { 60 result[i] = new Position(edges, e, Node.NOT_ITERABLE); 61 } 62 } 63 if (result[i] == null) { 64 throw new GraalError("unknown field \"%s\" in class %s", names[i], nodeClass); 65 } 66 } 67 return result; 68 } 69 70 private static final EconomicMap<Class<? extends NodeMatchRules>, EconomicMap<Class<? extends Node>, List<MatchStatement>>> registry = EconomicMap.create(Equivalence.IDENTITY); 71 72 /** 73 * Collect all the {@link MatchStatement}s defined by the superclass chain of theClass. 74 * 75 * @param theClass 76 * @param options 77 * @return the set of {@link MatchStatement}s applicable to theClass. 78 */ 79 @SuppressWarnings("try") 80 public static synchronized EconomicMap<Class<? extends Node>, List<MatchStatement>> lookup(Class<? extends NodeMatchRules> theClass, OptionValues options) { 81 EconomicMap<Class<? extends Node>, List<MatchStatement>> result = registry.get(theClass); 82 83 if (result == null) { 84 EconomicMap<Class<? extends Node>, List<MatchStatement>> rules = createRules(theClass); 85 registry.put(theClass, rules); 86 assert registry.get(theClass) == rules; 87 result = rules; 88 89 if (LogVerbose.getValue(options)) { 90 try (Scope s = Debug.scope("MatchComplexExpressions")) { 91 Debug.log("Match rules for %s", theClass.getSimpleName()); 92 MapCursor<Class<? extends Node>, List<MatchStatement>> cursor = result.getEntries(); 93 while (cursor.advance()) { 94 Debug.log(" For node class: %s", cursor.getKey()); 95 for (MatchStatement statement : cursor.getValue()) { 96 Debug.log(" %s", statement.getPattern()); 97 } 98 } 99 } 100 } 101 } 102 103 if (result.size() == 0) { 104 return null; 105 } 106 return result; 107 } 108 109 /* 110 * This is a separate, public method so that external clients can create rules with a custom 111 * lookup and without the default caching behavior. 112 */ 113 @SuppressWarnings("unchecked") 114 public static EconomicMap<Class<? extends Node>, List<MatchStatement>> createRules(Class<? extends NodeMatchRules> theClass) { 115 EconomicMap<Class<? extends NodeMatchRules>, MatchStatementSet> matchSets = EconomicMap.create(Equivalence.IDENTITY); 116 Iterable<MatchStatementSet> sl = GraalServices.load(MatchStatementSet.class); 117 for (MatchStatementSet rules : sl) { 118 matchSets.put(rules.forClass(), rules); 119 } 120 121 // Walk the class hierarchy collecting lists and merge them together. The subclass 122 // rules are first which gives them preference over earlier rules. 123 EconomicMap<Class<? extends Node>, List<MatchStatement>> rules = EconomicMap.create(Equivalence.IDENTITY); 124 Class<? extends NodeMatchRules> currentClass = theClass; 125 do { 126 MatchStatementSet matchSet = matchSets.get(currentClass); 127 if (matchSet != null) { 128 List<MatchStatement> statements = matchSet.statements(); 129 for (MatchStatement statement : statements) { 130 Class<? extends Node> nodeClass = statement.getPattern().nodeClass(); 131 List<MatchStatement> current = rules.get(nodeClass); 132 if (current == null) { 133 current = new ArrayList<>(); 134 rules.put(nodeClass, current); 135 } 136 current.add(statement); 137 } 138 } 139 currentClass = (Class<? extends NodeMatchRules>) currentClass.getSuperclass(); 140 } while (currentClass != NodeMatchRules.class); 141 return rules; 142 } 143 }