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