# HG changeset patch # User Tom Rodriguez # Date 1429852167 25200 # Node ID db96f9915540bf52437fd767359dbd80c9de551b # Parent 2daf39328194ecf2921494b029da68cb9ef706b1# Parent 905d93bb3d319230d69dc00dfde7b74ef30920c7 Merge diff -r 2daf39328194 -r db96f9915540 graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64NodeLIRBuilder.java --- a/graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64NodeLIRBuilder.java Thu Apr 23 21:18:27 2015 -0700 +++ b/graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64NodeLIRBuilder.java Thu Apr 23 22:09:27 2015 -0700 @@ -402,7 +402,7 @@ } } - @MatchRule("(Write Narrow=narrow location value)") + @MatchRule("(Write object location Narrow=narrow)") public ComplexMatchResult writeNarrow(WriteNode root, NarrowNode narrow) { return builder -> { LIRKind writeKind = getLIRGeneratorTool().getLIRKind(root.value().stamp()); diff -r 2daf39328194 -r db96f9915540 graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/util/FrequencyEncoder.java --- a/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/util/FrequencyEncoder.java Thu Apr 23 21:18:27 2015 -0700 +++ b/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/util/FrequencyEncoder.java Thu Apr 23 22:09:27 2015 -0700 @@ -46,6 +46,7 @@ } protected final Map> map; + protected boolean containsNull; /** * Creates an encoder that uses object identity. @@ -69,17 +70,17 @@ * Adds an object to the array. */ public void addObject(T object) { + if (object == null) { + containsNull = true; + return; + } + Entry entry = map.get(object); if (entry == null) { entry = new Entry<>(object); map.put(object, entry); } - if (object == null) { - /* null must get index 0, so sort it up. */ - entry.frequency = Integer.MAX_VALUE; - } else { - entry.frequency++; - } + entry.frequency++; } /** @@ -87,6 +88,10 @@ * {@link #addObject(Object) added} before. */ public int getIndex(Object object) { + if (object == null) { + assert containsNull; + return 0; + } Entry entry = map.get(object); assert entry != null && entry.index >= 0; return entry.index; @@ -96,7 +101,7 @@ * Returns the number of distinct objects that have been added, i.e., the length of the array. */ public int getLength() { - return map.size(); + return map.size() + (containsNull ? 1 : 0); } /** @@ -104,14 +109,21 @@ * correct length}. */ public T[] encodeAll(T[] allObjects) { - assert allObjects.length == map.size(); + assert allObjects.length == getLength(); List> sortedEntries = new ArrayList<>(map.values()); sortedEntries.sort((e1, e2) -> -Integer.compare(e1.frequency, e2.frequency)); + + int offset = 0; + if (containsNull) { + allObjects[0] = null; + offset = 1; + } for (int i = 0; i < sortedEntries.size(); i++) { Entry entry = sortedEntries.get(i); - entry.index = i; - allObjects[i] = entry.object; - assert entry.object != null || entry.index == 0; + int index = i + offset; + entry.index = index; + allObjects[index] = entry.object; + assert entry.object != null; } return allObjects; } diff -r 2daf39328194 -r db96f9915540 graal/com.oracle.graal.compiler.match.processor/src/META-INF/services/javax.annotation.processing.Processor --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.compiler.match.processor/src/META-INF/services/javax.annotation.processing.Processor Thu Apr 23 22:09:27 2015 -0700 @@ -0,0 +1,1 @@ +com.oracle.graal.compiler.match.processor.MatchProcessor diff -r 2daf39328194 -r db96f9915540 graal/com.oracle.graal.compiler.match.processor/src/com/oracle/graal/compiler/match/processor/MatchProcessor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.compiler.match.processor/src/com/oracle/graal/compiler/match/processor/MatchProcessor.java Thu Apr 23 22:09:27 2015 -0700 @@ -0,0 +1,1104 @@ +/* + * Copyright (c) 2014, 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 com.oracle.graal.compiler.match.processor; + +import java.io.*; +import java.util.*; +import java.util.regex.*; + +import javax.annotation.processing.*; +import javax.lang.model.*; +import javax.lang.model.element.*; +import javax.lang.model.type.*; +import javax.lang.model.util.*; +import javax.tools.Diagnostic.Kind; +import javax.tools.*; + +import com.oracle.graal.compiler.common.*; +import com.oracle.graal.compiler.gen.*; +import com.oracle.graal.compiler.match.*; +import com.oracle.graal.graph.*; +import com.oracle.graal.nodes.*; + +/** + * Processes classes annotated with {@link MatchRule}. A {@link MatchStatementSet} service is + * generated for each top level class containing at least one such field. These service objects can + * be retrieved as follows: + * + *
+ *     Iterable sl = Services.load(MatchStatementSet.class);
+ *     for (MatchStatementSet rules : sl) {
+ *         ...
+ *     }
+ * 
+ */ +@SupportedAnnotationTypes({"com.oracle.graal.compiler.match.MatchRule", "com.oracle.graal.compiler.match.MatchRules", "com.oracle.graal.compiler.match.MatchableNode", + "com.oracle.graal.compiler.match.MatchableNodes"}) +public class MatchProcessor extends AbstractProcessor { + + public MatchProcessor() { + } + + @Override + public SourceVersion getSupportedSourceVersion() { + return SourceVersion.latest(); + } + + private final Set processedMatchRule = new HashSet<>(); + private final Set processedMatchableNode = new HashSet<>(); + + private static class RuleParseError extends RuntimeException { + private static final long serialVersionUID = 6456128283609257490L; + + RuleParseError(String format, Object... args) { + super(String.format(format, args)); + } + } + + private static final Pattern tokenizer = Pattern.compile("\\s*([()=]|[A-Za-z][A-Za-z0-9]*)\\s*"); + + private class RuleParser { + private ArrayList capturedTypes = new ArrayList<>(); + + private ArrayList capturedNames = new ArrayList<>(); + + private final String[] tokens; + + private int current; + + private MatchDescriptor matchDescriptor; + + private final Set originatingElements = new HashSet<>(); + + private Set requiredPackages = new HashSet<>(); + + RuleParser(String rule) { + Matcher m = tokenizer.matcher(rule); + List list = new ArrayList<>(); + int end = 0; + while (m.lookingAt()) { + list.add(m.group(1)); + end = m.end(); + m.region(m.end(), m.regionEnd()); + } + if (end != m.regionEnd()) { + throw new RuleParseError("Unexpected tokens :" + rule.substring(m.end(), m.regionEnd())); + } + tokens = list.toArray(new String[0]); + + matchDescriptor = parseExpression(); + if (!done()) { + throw new RuleParseError("didn't consume all tokens"); + } + capturedNames.add(0, "root"); + capturedTypes.add(0, matchDescriptor.nodeType); + } + + String next() { + return tokens[current++]; + } + + String peek(String name) { + if (current >= tokens.length) { + if (name == null) { + throw new RuleParseError("Out of tokens"); + } + throw new RuleParseError("Out of tokens looking for %s", name); + } + return tokens[current]; + } + + boolean done() { + return current == tokens.length; + } + + private MatchDescriptor parseExpression() { + if (peek("(").equals("(")) { + next(); + MatchDescriptor descriptor = parseType(true); + for (int n = 0; n < descriptor.nodeType.inputs.length; n++) { + if (peek("(").equals("(")) { + descriptor.inputs[n] = parseExpression(); + } else { + descriptor.inputs[n] = parseType(false); + } + } + for (int n = 0; n < descriptor.nodeType.inputs.length; n++) { + if (descriptor.inputs[n] == null) { + throw new RuleParseError("not enough inputs for " + descriptor.name); + } + } + if (peek(")").equals(")")) { + next(); + return descriptor; + } + throw new RuleParseError("Too many arguments to " + descriptor.nodeType.nodeClass); + } + throw new RuleParseError("Extra tokens following match pattern: " + peek(null)); + } + + private MatchDescriptor parseType(boolean forExpression) { + TypeDescriptor type = null; + String name = null; + if (Character.isUpperCase(peek("node type or name").charAt(0))) { + String token = next(); + type = knownTypes.get(token); + if (type == null) { + throw new RuleParseError("Unknown node type: " + token); + } + if (peek("=").equals("=")) { + next(); + name = next(); + } + originatingElements.addAll(type.originatingElements); + requiredPackages.add(type.nodePackage); + } else if (Character.isLowerCase(peek("name").charAt(0))) { + name = next(); + type = valueType; + } else { + throw new RuleParseError("Unexpected token \"%s\" when looking for name or node type", peek(null)); + } + if (name != null) { + if (!capturedNames.contains(name)) { + capturedNames.add(name); + capturedTypes.add(type); + } else { + int index = capturedNames.indexOf(name); + if (capturedTypes.get(index) != type) { + throw new RuleParseError("Captured node \"%s\" has differing types", name); + } + } + } + return new MatchDescriptor(type, name, forExpression); + } + + List generateVariants() { + return matchDescriptor.generateVariants(); + } + + /** + * Recursively accumulate any required Position declarations. + */ + void generatePositionDeclarations(Set declarations) { + matchDescriptor.generatePositionDeclarations(declarations); + } + + /** + * + * @return the list of node types which are captured by name + */ + public ArrayList capturedTypes() { + return capturedTypes; + } + + public ArrayList capturedNames() { + return capturedNames; + } + } + + /** + * Set to true to enable logging to a local file during annotation processing. There's no normal + * channel for any debug messages and debugging annotation processors requires some special + * setup. + */ + private static final boolean DEBUG = false; + + private PrintWriter log; + + /** + * Logging facility for debugging the annotation processor. + */ + + private PrintWriter getLog() { + if (log == null) { + try { + // Create the log file within the generated source directory so it's easy to find. + // /tmp isn't platform independent and java.io.tmpdir can map anywhere, particularly + // on the mac. + FileObject file = processingEnv.getFiler().createResource(StandardLocation.SOURCE_OUTPUT, "", getClass().getSimpleName() + "log"); + log = new PrintWriter(new FileWriter(file.toUri().getPath(), true)); + } catch (IOException e) { + // Do nothing + } + } + return log; + } + + private void logMessage(String format, Object... args) { + if (!DEBUG) { + return; + } + PrintWriter bw = getLog(); + if (bw != null) { + bw.printf(format, args); + bw.flush(); + } + } + + private void logException(Throwable t) { + if (!DEBUG) { + return; + } + PrintWriter bw = getLog(); + if (bw != null) { + t.printStackTrace(bw); + bw.flush(); + } + } + + /** + * Bugs in an annotation processor can cause silent failure so try to report any exception + * throws as errors. + */ + private void reportExceptionThrow(Element element, Throwable t) { + if (element != null) { + logMessage("throw for %s:\n", element); + } + logException(t); + errorMessage(element, "Exception throw during processing: %s %s", t, Arrays.toString(Arrays.copyOf(t.getStackTrace(), 4))); + } + + static class TypeDescriptor { + final TypeMirror mirror; + + /** + * The name uses in match expressions to refer to this type. + */ + final String shortName; + + /** + * The simple name of the {@link ValueNode} class represented by this type. + */ + final String nodeClass; + + /** + * The package of {@link ValueNode} class represented by this type. + */ + final String nodePackage; + + /** + * The matchable inputs of the node. + */ + final String[] inputs; + + /** + * Should swapped variants of this match be generated. The user of the match is expected to + * compensate for any ordering differences in compare which are commutative but require + * reinterpreting the condition in that case. + */ + final boolean commutative; + + /** + * Can multiple users of this node subsume it. Constants can be swallowed into a match even + * if there are multiple users. + */ + final boolean shareable; + + final Set originatingElements = new HashSet<>(); + + TypeDescriptor(TypeMirror mirror, String shortName, String nodeClass, String nodePackage, String[] inputs, boolean commutative, boolean shareable) { + this.mirror = mirror; + this.shortName = shortName; + this.nodeClass = nodeClass; + this.nodePackage = nodePackage; + this.inputs = inputs; + this.commutative = commutative; + this.shareable = shareable; + assert !commutative || inputs.length == 2; + } + } + + /** + * The types which are know for purpose of parsing MatchRule expressions. + */ + Map knownTypes = new HashMap<>(); + + /** + * The mapping between elements with MatchRules and the wrapper class used invoke the code + * generation after the match. + */ + private Map invokers = new LinkedHashMap<>(); + + private TypeDescriptor valueType; + + private TypeMirror matchRulesTypeMirror; + + private TypeMirror matchRuleTypeMirror; + + private TypeMirror matchableNodeTypeMirror; + + private TypeMirror matchableNodesTypeMirror; + + private void declareType(TypeMirror mirror, String shortName, String nodeClass, String nodePackage, String[] inputs, boolean commutative, boolean shareable, Element element) { + TypeDescriptor descriptor = new TypeDescriptor(mirror, shortName, nodeClass, nodePackage, inputs, commutative, shareable); + descriptor.originatingElements.add(element); + knownTypes.put(shortName, descriptor); + } + + private String findPackage(Element type) { + PackageElement p = processingEnv.getElementUtils().getPackageOf(type); + if (p != null) { + return p.getQualifiedName().toString(); + } + throw new GraalInternalError("can't find package for %s", type); + } + + class MatchDescriptor { + TypeDescriptor nodeType; + String name; + MatchDescriptor[] inputs; + + MatchDescriptor(TypeDescriptor nodeType, String name, boolean forExpression) { + this.nodeType = nodeType; + this.name = name; + if (forExpression) { + this.inputs = new MatchDescriptor[nodeType.inputs.length]; + } else { + this.inputs = new MatchDescriptor[0]; + } + } + + public void generatePositionDeclarations(Set declarations) { + if (inputs.length == 0) { + return; + } + declarations.add(generatePositionDeclaration()); + for (MatchDescriptor desc : inputs) { + desc.generatePositionDeclarations(declarations); + } + } + + List recurseVariants(int index) { + if (inputs.length == 0) { + return new ArrayList<>(); + } + List currentVariants = inputs[index].generateVariants(); + if (index == inputs.length - 1) { + return currentVariants; + } + List subVariants = recurseVariants(index + 1); + List result = new ArrayList<>(); + for (String current : currentVariants) { + for (String sub : subVariants) { + result.add(current + ", " + sub); + if (nodeType.commutative) { + result.add(sub + ", " + current); + } + } + } + return result; + } + + /** + * Recursively generate all the variants of this rule pattern. Currently that just means to + * swap the inputs for commutative rules, producing all possible permutations. + * + * @return a list of Strings which will construct pattern matchers for this rule. + */ + List generateVariants() { + String prefix = formatPrefix(); + String suffix = formatSuffix(); + ArrayList variants = new ArrayList<>(); + if (inputs.length > 0) { + for (String var : recurseVariants(0)) { + variants.add(prefix + ", " + var + suffix); + } + } else { + assert inputs.length == 0; + variants.add(prefix + suffix); + } + + return variants; + } + + private String formatPrefix() { + if (nodeType == valueType) { + return String.format("new MatchPattern(%s, false", name != null ? ("\"" + name + "\"") : "null"); + } else { + return String.format("new MatchPattern(%s.class, %s", nodeType.nodeClass, name != null ? ("\"" + name + "\"") : "null"); + } + } + + private String formatSuffix() { + if (nodeType != null) { + if (inputs.length != nodeType.inputs.length) { + return ", true)"; + } else { + if (nodeType.inputs.length > 0) { + return ", " + nodeType.nodeClass + "_positions, " + !nodeType.shareable + ")"; + } + if (nodeType.shareable) { + return ", false)"; + } + } + } + return ")"; + } + + String generatePositionDeclaration() { + return String.format("Position[] %s_positions = MatchRuleRegistry.findPositions(%s.TYPE, new String[]{\"%s\"});", nodeType.nodeClass, nodeType.nodeClass, + String.join("\", \"", nodeType.inputs)); + } + } + + /** + * Strip the package off a class name leaving the full class name including any outer classes. + */ + private String fullClassName(Element element) { + assert element.getKind() == ElementKind.CLASS || element.getKind() == ElementKind.INTERFACE : element; + String pkg = findPackage(element); + return ((TypeElement) element).getQualifiedName().toString().substring(pkg.length() + 1); + } + + private void createFiles(MatchRuleDescriptor info) { + String pkg = ((PackageElement) info.topDeclaringType.getEnclosingElement()).getQualifiedName().toString(); + Name topDeclaringClass = info.topDeclaringType.getSimpleName(); + + String matchStatementClassName = topDeclaringClass + "_" + MatchStatementSet.class.getSimpleName(); + Element[] originatingElements = info.originatingElements.toArray(new Element[info.originatingElements.size()]); + + Types typeUtils = typeUtils(); + Filer filer = processingEnv.getFiler(); + try (PrintWriter out = createSourceFile(pkg, matchStatementClassName, filer, originatingElements)) { + + out.println("// CheckStyle: stop header check"); + out.println("// GENERATED CONTENT - DO NOT EDIT"); + out.println("// Source: " + topDeclaringClass + ".java"); + out.println("package " + pkg + ";"); + out.println(""); + out.println("import java.util.*;"); + out.println("import " + MatchStatementSet.class.getPackage().getName() + ".*;"); + out.println("import " + NodeLIRBuilder.class.getName() + ";"); + out.println("import " + Position.class.getName() + ";"); + for (String p : info.requiredPackages) { + out.println("import " + p + ".*;"); + } + out.println(""); + + out.println("public class " + matchStatementClassName + " implements " + MatchStatementSet.class.getSimpleName() + " {"); + + out.println(); + + // Generate declarations for the wrapper class to invoke the code generation methods. + for (MethodInvokerItem invoker : invokers.values()) { + StringBuilder args = new StringBuilder(); + StringBuilder types = new StringBuilder(); + int count = invoker.fields.size(); + int index = 0; + for (VariableElement arg : invoker.fields) { + args.append('"'); + args.append(arg.getSimpleName()); + args.append('"'); + types.append(String.format("(%s) args[%s]", fullClassName(typeUtils.asElement(arg.asType())), index++)); + if (count-- > 1) { + args.append(", "); + types.append(", "); + } + } + out.printf(" private static final String[] %s = new String[] {%s};\n", invoker.argumentsListName(), args); + out.printf(" private static final class %s implements MatchGenerator {\n", invoker.wrapperClass()); + out.printf(" static MatchGenerator instance = new %s();\n", invoker.wrapperClass()); + out.printf(" public ComplexMatchResult match(NodeLIRBuilder builder, Object...args) {\n"); + out.printf(" return ((%s) builder).%s(%s);\n", invoker.nodeLIRBuilderClass, invoker.methodName, types); + out.printf(" }\n"); + out.printf(" public String getName() {\n"); + out.printf(" return \"%s\";\n", invoker.methodName); + out.printf(" }\n"); + out.printf(" }\n"); + out.println(); + + } + + String desc = MatchStatement.class.getSimpleName(); + + out.println(" public Class forClass() {"); + out.println(" return " + topDeclaringClass + ".class;"); + out.println(" }"); + out.println(); + out.println(" @Override"); + out.println(" public List<" + desc + "> statements() {"); + out.println(" // Checkstyle: stop "); + + for (String positionDeclaration : info.positionDeclarations) { + out.println(" " + positionDeclaration); + } + out.println(); + + out.println(" List<" + desc + "> statements = Collections.unmodifiableList(Arrays.asList("); + + int i = 0; + for (MatchRuleItem matchRule : info.matchRules) { + String comma = i == info.matchRules.size() - 1 ? "" : ","; + out.printf(" %s%s\n", matchRule.ruleBuilder(), comma); + i++; + } + out.println(" ));"); + out.println(" // Checkstyle: resume"); + out.println(" return statements;"); + out.println(" }"); + + out.println(); + + out.println("}"); + } + + try { + createProviderFile(pkg, matchStatementClassName, originatingElements); + } catch (IOException e) { + reportExceptionThrow(info.topDeclaringType, e); + } + } + + private void createProviderFile(String pkg, String providerClassName, Element... originatingElements) throws IOException { + String filename = "META-INF/providers/" + pkg + "." + providerClassName; + FileObject file = processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", filename, originatingElements); + PrintWriter writer = new PrintWriter(new OutputStreamWriter(file.openOutputStream(), "UTF-8")); + writer.println(MatchStatementSet.class.getName()); + writer.close(); + } + + protected PrintWriter createSourceFile(String pkg, String relativeName, Filer filer, Element... originatingElements) { + try { + // Ensure Unix line endings to comply with Graal code style guide checked by Checkstyle + JavaFileObject sourceFile = filer.createSourceFile(pkg + "." + relativeName, originatingElements); + return new PrintWriter(sourceFile.openWriter()) { + + @Override + public void println() { + print("\n"); + } + }; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * Used to generate the MatchStatement constructor invocation. + */ + static class MatchRuleItem { + private final String matchPattern; + private final MethodInvokerItem invoker; + + public MatchRuleItem(String matchPattern, MethodInvokerItem invoker) { + this.matchPattern = matchPattern; + this.invoker = invoker; + } + + /** + * @return a string which will construct the MatchStatement instance to match this pattern. + */ + public String ruleBuilder() { + return String.format("new MatchStatement(\"%s\", %s, %s.instance, %s)", invoker.methodName, matchPattern, invoker.wrapperClass(), invoker.argumentsListName()); + } + } + + /** + * Used to generate the wrapper class to invoke the code generation method. + */ + static class MethodInvokerItem { + final String methodName; + final String nodeLIRBuilderClass; + final ExecutableElement method; + final List fields; + + MethodInvokerItem(String methodName, String nodeLIRBuilderClass, ExecutableElement method, List fields) { + this.methodName = methodName; + this.nodeLIRBuilderClass = nodeLIRBuilderClass; + this.method = method; + this.fields = fields; + } + + String wrapperClass() { + return "MatchGenerator_" + methodName; + } + + String argumentsListName() { + return methodName + "_arguments"; + } + } + + static class MatchRuleDescriptor { + + final TypeElement topDeclaringType; + final List matchRules = new ArrayList<>(); + private final Set originatingElements = new HashSet<>(); + public Set positionDeclarations = new LinkedHashSet<>(); + + /** + * The set of packages which must be imported to refer the classes mention in matchRules. + */ + Set requiredPackages = new HashSet<>(); + + public MatchRuleDescriptor(TypeElement topDeclaringType) { + this.topDeclaringType = topDeclaringType; + } + } + + private static TypeElement topDeclaringType(Element element) { + Element enclosing = element.getEnclosingElement(); + if (enclosing == null || enclosing.getKind() == ElementKind.PACKAGE) { + assert element.getKind() == ElementKind.CLASS || element.getKind() == ElementKind.INTERFACE; + return (TypeElement) element; + } + return topDeclaringType(enclosing); + } + + private AnnotationMirror findAnnotationMirror(Element element, TypeMirror typeMirror) { + for (AnnotationMirror mirror : element.getAnnotationMirrors()) { + if (typeUtils().isSameType(mirror.getAnnotationType(), typeMirror)) { + return mirror; + } + } + return null; + } + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + if (roundEnv.processingOver()) { + return true; + } + + logMessage("Starting round %s\n", roundEnv); + matchRulesTypeMirror = processingEnv.getElementUtils().getTypeElement(MatchRules.class.getCanonicalName()).asType(); + matchRuleTypeMirror = processingEnv.getElementUtils().getTypeElement(MatchRule.class.getCanonicalName()).asType(); + + matchableNodeTypeMirror = processingEnv.getElementUtils().getTypeElement(MatchableNode.class.getCanonicalName()).asType(); + matchableNodesTypeMirror = processingEnv.getElementUtils().getTypeElement(MatchableNodes.class.getCanonicalName()).asType(); + + Element currentElement = null; + try { + for (Element element : roundEnv.getElementsAnnotatedWith(MatchableNode.class)) { + logMessage("%s\n", element); + processMatchableNode(element); + } + for (Element element : roundEnv.getElementsAnnotatedWith(MatchableNodes.class)) { + logMessage("%s\n", element); + processMatchableNode(element); + } + // Define a TypeDescriptor for the generic node but don't enter it into the nodeTypes + // table since it shouldn't be mentioned in match rules. + TypeMirror valueTypeMirror = processingEnv.getElementUtils().getTypeElement(ValueNode.class.getName()).asType(); + valueType = new TypeDescriptor(valueTypeMirror, "Value", ValueNode.class.getSimpleName(), ValueNode.class.getPackage().getName(), new String[0], false, false); + + Map map = new LinkedHashMap<>(); + + for (Element element : roundEnv.getElementsAnnotatedWith(MatchRule.class)) { + currentElement = element; + processMatchRule(map, element, findAnnotationMirror(element, matchRuleTypeMirror)); + } + for (Element element : roundEnv.getElementsAnnotatedWith(MatchRules.class)) { + currentElement = element; + processMatchRule(map, element, findAnnotationMirror(element, matchRulesTypeMirror)); + } + + currentElement = null; + for (MatchRuleDescriptor info : map.values()) { + createFiles(info); + } + + } catch (Throwable t) { + reportExceptionThrow(currentElement, t); + } + + return true; + } + + /** + * Build up the type table to be used during parsing of the MatchRule. + */ + private void processMatchableNode(Element element) { + if (!processedMatchableNode.contains(element)) { + try { + processedMatchableNode.add(element); + + AnnotationMirror mirror = findAnnotationMirror(element, matchableNodesTypeMirror); + if (mirror == null) { + mirror = findAnnotationMirror(element, matchableNodeTypeMirror); + } + if (mirror == null) { + return; + } + TypeElement topDeclaringType = topDeclaringType(element); + List mirrors = null; + if (typeUtils().isSameType(mirror.getAnnotationType(), matchableNodesTypeMirror)) { + // Unpack the mirrors for a repeatable annotation + mirrors = getAnnotationValueList(AnnotationMirror.class, mirror, "value"); + } + int i = 0; + for (MatchableNode matchableNode : element.getAnnotationsByType(MatchableNode.class)) { + processMatchableNode(element, topDeclaringType, matchableNode, mirrors != null ? mirrors.get(i++) : mirror); + } + } catch (Throwable t) { + reportExceptionThrow(element, t); + } + } + } + + private void processMatchableNode(Element element, TypeElement topDeclaringType, MatchableNode matchable, AnnotationMirror mirror) throws GraalInternalError { + logMessage("processMatchableNode %s %s %s\n", topDeclaringType, element, matchable); + String nodeClass; + String nodePackage; + TypeMirror nodeClassMirror = null; + try { + matchable.nodeClass(); + } catch (MirroredTypeException e) { + nodeClassMirror = e.getTypeMirror(); + } + if (nodeClassMirror == null) { + throw new GraalInternalError("Can't get mirror for node class %s", element); + } + if (nodeClassMirror.toString().equals(MatchableNode.class.getName())) { + nodeClass = topDeclaringType.getQualifiedName().toString(); + } else { + nodeClass = nodeClassMirror.toString(); + } + TypeElement typeElement = processingEnv.getElementUtils().getTypeElement(nodeClass); + if (typeElement == null) { + errorMessage(element, mirror, "Class \"%s\" cannot be resolved to a type", nodeClass); + return; + } + nodePackage = findPackage(typeElement); + assert nodeClass.startsWith(nodePackage); + nodeClass = nodeClass.substring(nodePackage.length() + 1); + assert nodeClass.endsWith("Node"); + String shortName = nodeClass.substring(0, nodeClass.length() - 4); + + Types typeUtils = processingEnv.getTypeUtils(); + TypeElement nodeClassElement = (TypeElement) typeUtils.asElement(nodeClassMirror); + for (String input : matchable.inputs()) { + boolean ok = false; + TypeElement current = nodeClassElement; + while (!ok && current != null) { + for (Element fieldElement : ElementFilter.fieldsIn(current.getEnclosedElements())) { + if (fieldElement.getSimpleName().toString().equals(input)) { + ok = true; + break; + } + } + TypeMirror theSuper = current.getSuperclass(); + current = (TypeElement) typeUtils.asElement(theSuper); + } + if (!ok) { + errorMessage(element, mirror, "Input named \"%s\" doesn't exist in %s", input, nodeClassElement.getSimpleName()); + } + } + + declareType(nodeClassMirror, shortName, nodeClass, nodePackage, matchable.inputs(), matchable.commutative(), matchable.shareable(), element); + } + + private void processMatchRule(Map map, Element element, AnnotationMirror mirror) { + if (!processedMatchRule.contains(element)) { + try { + processedMatchRule.add(element); + + // The annotation element type should ensure this is true. + assert element instanceof ExecutableElement; + + findMatchableNodes(element); + + TypeElement topDeclaringType = topDeclaringType(element); + MatchRuleDescriptor info = map.get(topDeclaringType); + if (info == null) { + info = new MatchRuleDescriptor(topDeclaringType); + map.put(topDeclaringType, info); + } + List mirrors = null; + if (typeUtils().isSameType(mirror.getAnnotationType(), matchRulesTypeMirror)) { + // Unpack the mirrors for a repeatable annotation + mirrors = getAnnotationValueList(AnnotationMirror.class, mirror, "value"); + } + int i = 0; + for (MatchRule matchRule : element.getAnnotationsByType(MatchRule.class)) { + processMethodMatchRule((ExecutableElement) element, info, matchRule, mirrors != null ? mirrors.get(i++) : mirror); + } + } catch (Throwable t) { + reportExceptionThrow(element, t); + } + } + } + + /** + * Search the super types of element for MatchableNode definitions. Any superclass or super + * interface can contain definitions of matchable nodes. + * + * @param element + */ + private void findMatchableNodes(Element element) { + processMatchableNode(element); + Element enclosing = element.getEnclosingElement(); + while (enclosing != null) { + if (enclosing.getKind() == ElementKind.CLASS || enclosing.getKind() == ElementKind.INTERFACE) { + TypeElement current = (TypeElement) enclosing; + while (current != null) { + processMatchableNode(current); + for (TypeMirror intf : current.getInterfaces()) { + Element interfaceElement = typeUtils().asElement(intf); + processMatchableNode(interfaceElement); + // Recurse + findMatchableNodes(interfaceElement); + } + TypeMirror theSuper = current.getSuperclass(); + current = (TypeElement) typeUtils().asElement(theSuper); + } + } + enclosing = enclosing.getEnclosingElement(); + } + } + + private Types typeUtils() { + return processingEnv.getTypeUtils(); + } + + private void processMethodMatchRule(ExecutableElement method, MatchRuleDescriptor info, MatchRule matchRule, AnnotationMirror mirror) { + logMessage("processMethodMatchRule %s %s\n", method, mirror); + + Types typeUtils = typeUtils(); + + if (!method.getModifiers().contains(Modifier.PUBLIC)) { + errorMessage(method, "MatchRule method %s must be public", method.getSimpleName()); + return; + } + if (method.getModifiers().contains(Modifier.STATIC)) { + errorMessage(method, "MatchRule method %s must be non-static", method.getSimpleName()); + return; + } + + try { + TypeMirror returnType = method.getReturnType(); + if (!typeUtils.isSameType(returnType, processingEnv.getElementUtils().getTypeElement(ComplexMatchResult.class.getName()).asType())) { + errorMessage(method, "MatchRule method return type must be %s", ComplexMatchResult.class.getName()); + return; + } + + String rule = matchRule.value(); + RuleParser parser = new RuleParser(rule); + ArrayList expectedTypes = parser.capturedTypes(); + ArrayList expectedNames = parser.capturedNames(); + List actualParameters = method.getParameters(); + if (expectedTypes.size() + 1 < actualParameters.size()) { + errorMessage(method, "Too many arguments for match method %s != %s", expectedTypes.size() + 1, actualParameters.size()); + return; + } + + // Walk through the parameters to the method and see if they exist in the match rule. + // The order doesn't matter but only names mentioned in the rule can be used and they + // must be assignment compatible. + for (VariableElement parameter : actualParameters) { + String name = parameter.getSimpleName().toString(); + int nameIndex = expectedNames.indexOf(name); + if (nameIndex == -1) { + errorMessage(method, "Argument \"%s\" isn't captured in the match rule", name); + return; + } + TypeMirror type = parameter.asType(); + if (!typeUtils.isAssignable(expectedTypes.get(nameIndex).mirror, type)) { + errorMessage(method, "Captured value \"%s\" of type %s is not assignable to argument of type %s", name, expectedTypes.get(nameIndex).mirror, type); + return; + } + } + + String methodName = method.getSimpleName().toString(); + MethodInvokerItem invoker = invokers.get(methodName); + if (invoker == null) { + invoker = new MethodInvokerItem(methodName, topDeclaringType(method).getSimpleName().toString(), method, actualParameters); + invokers.put(methodName, invoker); + } else if (invoker.method != method) { + // This could be supported but it's easier if they are unique since the names + // are used in log output and snippet counters. + errorMessage(method, "Use unique method names for match methods."); + return; + } + + Element enclosing = method.getEnclosingElement(); + String declaringClass = ""; + String separator = ""; + Set originatingElementsList = info.originatingElements; + originatingElementsList.add(method); + while (enclosing != null) { + if (enclosing.getKind() == ElementKind.CLASS || enclosing.getKind() == ElementKind.INTERFACE) { + if (enclosing.getModifiers().contains(Modifier.PRIVATE)) { + errorMessage(method, "MatchRule cannot be declared in a private %s %s", enclosing.getKind().name().toLowerCase(), enclosing); + return; + } + originatingElementsList.add(enclosing); + declaringClass = enclosing.getSimpleName() + separator + declaringClass; + separator = "."; + } else { + assert enclosing.getKind() == ElementKind.PACKAGE; + } + enclosing = enclosing.getEnclosingElement(); + } + + originatingElementsList.addAll(parser.originatingElements); + info.requiredPackages.addAll(parser.requiredPackages); + + // Accumulate any position declarations. + parser.generatePositionDeclarations(info.positionDeclarations); + + List matches = parser.generateVariants(); + for (String match : matches) { + info.matchRules.add(new MatchRuleItem(match, invoker)); + } + } catch (RuleParseError e) { + errorMessage(method, mirror, e.getMessage()); + } + } + + private void errorMessage(Element element, String format, Object... args) { + processingEnv.getMessager().printMessage(Kind.ERROR, String.format(format, args), element); + } + + private void errorMessage(Element element, AnnotationMirror mirror, String format, Object... args) { + processingEnv.getMessager().printMessage(Kind.ERROR, String.format(format, args), element, mirror); + } + + // TODO borrowed from com.oracle.truffle.dsl.processor.Utils + @SuppressWarnings("unchecked") + private static List getAnnotationValueList(Class expectedListType, AnnotationMirror mirror, String name) { + List values = getAnnotationValue(List.class, mirror, name); + List result = new ArrayList<>(); + + if (values != null) { + for (AnnotationValue value : values) { + T annotationValue = resolveAnnotationValue(expectedListType, value); + if (annotationValue != null) { + result.add(annotationValue); + } + } + } + return result; + } + + private static T getAnnotationValue(Class expectedType, AnnotationMirror mirror, String name) { + return resolveAnnotationValue(expectedType, getAnnotationValue(mirror, name)); + } + + @SuppressWarnings({"unchecked"}) + private static T resolveAnnotationValue(Class expectedType, AnnotationValue value) { + if (value == null) { + return null; + } + + Object unboxedValue = value.accept(new AnnotationValueVisitorImpl(), null); + if (unboxedValue != null) { + if (expectedType == TypeMirror.class && unboxedValue instanceof String) { + return null; + } + if (!expectedType.isAssignableFrom(unboxedValue.getClass())) { + throw new ClassCastException(unboxedValue.getClass().getName() + " not assignable from " + expectedType.getName()); + } + } + return (T) unboxedValue; + } + + private static AnnotationValue getAnnotationValue(AnnotationMirror mirror, String name) { + ExecutableElement valueMethod = null; + for (ExecutableElement method : ElementFilter.methodsIn(mirror.getAnnotationType().asElement().getEnclosedElements())) { + if (method.getSimpleName().toString().equals(name)) { + valueMethod = method; + break; + } + } + + if (valueMethod == null) { + return null; + } + + AnnotationValue value = mirror.getElementValues().get(valueMethod); + if (value == null) { + value = valueMethod.getDefaultValue(); + } + + return value; + } + + private static class AnnotationValueVisitorImpl extends AbstractAnnotationValueVisitor7 { + + @Override + public Object visitBoolean(boolean b, Void p) { + return Boolean.valueOf(b); + } + + @Override + public Object visitByte(byte b, Void p) { + return Byte.valueOf(b); + } + + @Override + public Object visitChar(char c, Void p) { + return c; + } + + @Override + public Object visitDouble(double d, Void p) { + return d; + } + + @Override + public Object visitFloat(float f, Void p) { + return f; + } + + @Override + public Object visitInt(int i, Void p) { + return i; + } + + @Override + public Object visitLong(long i, Void p) { + return i; + } + + @Override + public Object visitShort(short s, Void p) { + return s; + } + + @Override + public Object visitString(String s, Void p) { + return s; + } + + @Override + public Object visitType(TypeMirror t, Void p) { + return t; + } + + @Override + public Object visitEnumConstant(VariableElement c, Void p) { + return c; + } + + @Override + public Object visitAnnotation(AnnotationMirror a, Void p) { + return a; + } + + @Override + public Object visitArray(List vals, Void p) { + return vals; + } + + } +} diff -r 2daf39328194 -r db96f9915540 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/SchedulingTest2.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/SchedulingTest2.java Thu Apr 23 21:18:27 2015 -0700 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/SchedulingTest2.java Thu Apr 23 22:09:27 2015 -0700 @@ -66,7 +66,7 @@ assertDeepEquals(2, schedule.getCFG().getBlocks().size()); for (BinaryArithmeticNode node : graph.getNodes().filter(BinaryArithmeticNode.class)) { if (node instanceof AddNode) { - assertTrue(node.toString() + " expected: " + nodeToBlock.get(beginNode) + " but was: " + nodeToBlock.get(node), nodeToBlock.get(node) == nodeToBlock.get(beginNode)); + assertTrue(node.toString() + " expected: " + nodeToBlock.get(beginNode) + " but was: " + nodeToBlock.get(node), nodeToBlock.get(node) != nodeToBlock.get(beginNode)); } } diff -r 2daf39328194 -r db96f9915540 graal/com.oracle.graal.compiler/src/META-INF/services/javax.annotation.processing.Processor --- a/graal/com.oracle.graal.compiler/src/META-INF/services/javax.annotation.processing.Processor Thu Apr 23 21:18:27 2015 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -com.oracle.graal.compiler.match.MatchProcessor diff -r 2daf39328194 -r db96f9915540 graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchProcessor.java --- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchProcessor.java Thu Apr 23 21:18:27 2015 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1103 +0,0 @@ -/* - * Copyright (c) 2014, 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 com.oracle.graal.compiler.match; - -import java.io.*; -import java.util.*; -import java.util.regex.*; - -import javax.annotation.processing.*; -import javax.lang.model.*; -import javax.lang.model.element.*; -import javax.lang.model.type.*; -import javax.lang.model.util.*; -import javax.tools.Diagnostic.Kind; -import javax.tools.*; - -import com.oracle.graal.compiler.common.*; -import com.oracle.graal.compiler.gen.*; -import com.oracle.graal.graph.*; -import com.oracle.graal.nodes.*; - -/** - * Processes classes annotated with {@link MatchRule}. A {@link MatchStatementSet} service is - * generated for each top level class containing at least one such field. These service objects can - * be retrieved as follows: - * - *
- *     Iterable sl = Services.load(MatchStatementSet.class);
- *     for (MatchStatementSet rules : sl) {
- *         ...
- *     }
- * 
- */ -@SupportedAnnotationTypes({"com.oracle.graal.compiler.match.MatchRule", "com.oracle.graal.compiler.match.MatchRules", "com.oracle.graal.compiler.match.MatchableNode", - "com.oracle.graal.compiler.match.MatchableNodes"}) -public class MatchProcessor extends AbstractProcessor { - - public MatchProcessor() { - } - - @Override - public SourceVersion getSupportedSourceVersion() { - return SourceVersion.latest(); - } - - private final Set processedMatchRule = new HashSet<>(); - private final Set processedMatchableNode = new HashSet<>(); - - private static class RuleParseError extends RuntimeException { - private static final long serialVersionUID = 6456128283609257490L; - - RuleParseError(String format, Object... args) { - super(String.format(format, args)); - } - } - - private static final Pattern tokenizer = Pattern.compile("\\s*([()=]|[A-Za-z][A-Za-z0-9]*)\\s*"); - - private class RuleParser { - private ArrayList capturedTypes = new ArrayList<>(); - - private ArrayList capturedNames = new ArrayList<>(); - - private final String[] tokens; - - private int current; - - private MatchDescriptor matchDescriptor; - - private final Set originatingElements = new HashSet<>(); - - private Set requiredPackages = new HashSet<>(); - - RuleParser(String rule) { - Matcher m = tokenizer.matcher(rule); - List list = new ArrayList<>(); - int end = 0; - while (m.lookingAt()) { - list.add(m.group(1)); - end = m.end(); - m.region(m.end(), m.regionEnd()); - } - if (end != m.regionEnd()) { - throw new RuleParseError("Unexpected tokens :" + rule.substring(m.end(), m.regionEnd())); - } - tokens = list.toArray(new String[0]); - - matchDescriptor = parseExpression(); - if (!done()) { - throw new RuleParseError("didn't consume all tokens"); - } - capturedNames.add(0, "root"); - capturedTypes.add(0, matchDescriptor.nodeType); - } - - String next() { - return tokens[current++]; - } - - String peek(String name) { - if (current >= tokens.length) { - if (name == null) { - throw new RuleParseError("Out of tokens"); - } - throw new RuleParseError("Out of tokens looking for %s", name); - } - return tokens[current]; - } - - boolean done() { - return current == tokens.length; - } - - private MatchDescriptor parseExpression() { - if (peek("(").equals("(")) { - next(); - MatchDescriptor descriptor = parseType(true); - for (int n = 0; n < descriptor.nodeType.inputs.length; n++) { - if (peek("(").equals("(")) { - descriptor.inputs[n] = parseExpression(); - } else { - descriptor.inputs[n] = parseType(false); - } - } - for (int n = 0; n < descriptor.nodeType.inputs.length; n++) { - if (descriptor.inputs[n] == null) { - throw new RuleParseError("not enough inputs for " + descriptor.name); - } - } - if (peek(")").equals(")")) { - next(); - return descriptor; - } - throw new RuleParseError("Too many arguments to " + descriptor.nodeType.nodeClass); - } - throw new RuleParseError("Extra tokens following match pattern: " + peek(null)); - } - - private MatchDescriptor parseType(boolean forExpression) { - TypeDescriptor type = null; - String name = null; - if (Character.isUpperCase(peek("node type or name").charAt(0))) { - String token = next(); - type = knownTypes.get(token); - if (type == null) { - throw new RuleParseError("Unknown node type: " + token); - } - if (peek("=").equals("=")) { - next(); - name = next(); - } - originatingElements.addAll(type.originatingElements); - requiredPackages.add(type.nodePackage); - } else if (Character.isLowerCase(peek("name").charAt(0))) { - name = next(); - type = valueType; - } else { - throw new RuleParseError("Unexpected token \"%s\" when looking for name or node type", peek(null)); - } - if (name != null) { - if (!capturedNames.contains(name)) { - capturedNames.add(name); - capturedTypes.add(type); - } else { - int index = capturedNames.indexOf(name); - if (capturedTypes.get(index) != type) { - throw new RuleParseError("Captured node \"%s\" has differing types", name); - } - } - } - return new MatchDescriptor(type, name, forExpression); - } - - List generateVariants() { - return matchDescriptor.generateVariants(); - } - - /** - * Recursively accumulate any required Position declarations. - */ - void generatePositionDeclarations(Set declarations) { - matchDescriptor.generatePositionDeclarations(declarations); - } - - /** - * - * @return the list of node types which are captured by name - */ - public ArrayList capturedTypes() { - return capturedTypes; - } - - public ArrayList capturedNames() { - return capturedNames; - } - } - - /** - * Set to true to enable logging to a local file during annotation processing. There's no normal - * channel for any debug messages and debugging annotation processors requires some special - * setup. - */ - private static final boolean DEBUG = false; - - private PrintWriter log; - - /** - * Logging facility for debugging the annotation processor. - */ - - private PrintWriter getLog() { - if (log == null) { - try { - // Create the log file within the generated source directory so it's easy to find. - // /tmp isn't platform independent and java.io.tmpdir can map anywhere, particularly - // on the mac. - FileObject file = processingEnv.getFiler().createResource(StandardLocation.SOURCE_OUTPUT, "", getClass().getSimpleName() + "log"); - log = new PrintWriter(new FileWriter(file.toUri().getPath(), true)); - } catch (IOException e) { - // Do nothing - } - } - return log; - } - - private void logMessage(String format, Object... args) { - if (!DEBUG) { - return; - } - PrintWriter bw = getLog(); - if (bw != null) { - bw.printf(format, args); - bw.flush(); - } - } - - private void logException(Throwable t) { - if (!DEBUG) { - return; - } - PrintWriter bw = getLog(); - if (bw != null) { - t.printStackTrace(bw); - bw.flush(); - } - } - - /** - * Bugs in an annotation processor can cause silent failure so try to report any exception - * throws as errors. - */ - private void reportExceptionThrow(Element element, Throwable t) { - if (element != null) { - logMessage("throw for %s:\n", element); - } - logException(t); - errorMessage(element, "Exception throw during processing: %s %s", t, Arrays.toString(Arrays.copyOf(t.getStackTrace(), 4))); - } - - static class TypeDescriptor { - final TypeMirror mirror; - - /** - * The name uses in match expressions to refer to this type. - */ - final String shortName; - - /** - * The simple name of the {@link ValueNode} class represented by this type. - */ - final String nodeClass; - - /** - * The package of {@link ValueNode} class represented by this type. - */ - final String nodePackage; - - /** - * The matchable inputs of the node. - */ - final String[] inputs; - - /** - * Should swapped variants of this match be generated. The user of the match is expected to - * compensate for any ordering differences in compare which are commutative but require - * reinterpreting the condition in that case. - */ - final boolean commutative; - - /** - * Can multiple users of this node subsume it. Constants can be swallowed into a match even - * if there are multiple users. - */ - final boolean shareable; - - final Set originatingElements = new HashSet<>(); - - TypeDescriptor(TypeMirror mirror, String shortName, String nodeClass, String nodePackage, String[] inputs, boolean commutative, boolean shareable) { - this.mirror = mirror; - this.shortName = shortName; - this.nodeClass = nodeClass; - this.nodePackage = nodePackage; - this.inputs = inputs; - this.commutative = commutative; - this.shareable = shareable; - assert !commutative || inputs.length == 2; - } - } - - /** - * The types which are know for purpose of parsing MatchRule expressions. - */ - Map knownTypes = new HashMap<>(); - - /** - * The mapping between elements with MatchRules and the wrapper class used invoke the code - * generation after the match. - */ - private Map invokers = new LinkedHashMap<>(); - - private TypeDescriptor valueType; - - private TypeMirror matchRulesTypeMirror; - - private TypeMirror matchRuleTypeMirror; - - private TypeMirror matchableNodeTypeMirror; - - private TypeMirror matchableNodesTypeMirror; - - private void declareType(TypeMirror mirror, String shortName, String nodeClass, String nodePackage, String[] inputs, boolean commutative, boolean shareable, Element element) { - TypeDescriptor descriptor = new TypeDescriptor(mirror, shortName, nodeClass, nodePackage, inputs, commutative, shareable); - descriptor.originatingElements.add(element); - knownTypes.put(shortName, descriptor); - } - - private String findPackage(Element type) { - PackageElement p = processingEnv.getElementUtils().getPackageOf(type); - if (p != null) { - return p.getQualifiedName().toString(); - } - throw new GraalInternalError("can't find package for %s", type); - } - - class MatchDescriptor { - TypeDescriptor nodeType; - String name; - MatchDescriptor[] inputs; - - MatchDescriptor(TypeDescriptor nodeType, String name, boolean forExpression) { - this.nodeType = nodeType; - this.name = name; - if (forExpression) { - this.inputs = new MatchDescriptor[nodeType.inputs.length]; - } else { - this.inputs = new MatchDescriptor[0]; - } - } - - public void generatePositionDeclarations(Set declarations) { - if (inputs.length == 0) { - return; - } - declarations.add(generatePositionDeclaration()); - for (MatchDescriptor desc : inputs) { - desc.generatePositionDeclarations(declarations); - } - } - - List recurseVariants(int index) { - if (inputs.length == 0) { - return new ArrayList<>(); - } - List currentVariants = inputs[index].generateVariants(); - if (index == inputs.length - 1) { - return currentVariants; - } - List subVariants = recurseVariants(index + 1); - List result = new ArrayList<>(); - for (String current : currentVariants) { - for (String sub : subVariants) { - result.add(current + ", " + sub); - if (nodeType.commutative) { - result.add(sub + ", " + current); - } - } - } - return result; - } - - /** - * Recursively generate all the variants of this rule pattern. Currently that just means to - * swap the inputs for commutative rules, producing all possible permutations. - * - * @return a list of Strings which will construct pattern matchers for this rule. - */ - List generateVariants() { - String prefix = formatPrefix(); - String suffix = formatSuffix(); - ArrayList variants = new ArrayList<>(); - if (inputs.length > 0) { - for (String var : recurseVariants(0)) { - variants.add(prefix + ", " + var + suffix); - } - } else { - assert inputs.length == 0; - variants.add(prefix + suffix); - } - - return variants; - } - - private String formatPrefix() { - if (nodeType == valueType) { - return String.format("new MatchPattern(%s, false", name != null ? ("\"" + name + "\"") : "null"); - } else { - return String.format("new MatchPattern(%s.class, %s", nodeType.nodeClass, name != null ? ("\"" + name + "\"") : "null"); - } - } - - private String formatSuffix() { - if (nodeType != null) { - if (inputs.length != nodeType.inputs.length) { - return ", true)"; - } else { - if (nodeType.inputs.length > 0) { - return ", " + nodeType.nodeClass + "_positions, " + !nodeType.shareable + ")"; - } - if (nodeType.shareable) { - return ", false)"; - } - } - } - return ")"; - } - - String generatePositionDeclaration() { - return String.format("Position[] %s_positions = MatchRuleRegistry.findPositions(%s.TYPE, new String[]{\"%s\"});", nodeType.nodeClass, nodeType.nodeClass, - String.join("\", \"", nodeType.inputs)); - } - } - - /** - * Strip the package off a class name leaving the full class name including any outer classes. - */ - private String fullClassName(Element element) { - assert element.getKind() == ElementKind.CLASS || element.getKind() == ElementKind.INTERFACE : element; - String pkg = findPackage(element); - return ((TypeElement) element).getQualifiedName().toString().substring(pkg.length() + 1); - } - - private void createFiles(MatchRuleDescriptor info) { - String pkg = ((PackageElement) info.topDeclaringType.getEnclosingElement()).getQualifiedName().toString(); - Name topDeclaringClass = info.topDeclaringType.getSimpleName(); - - String matchStatementClassName = topDeclaringClass + "_" + MatchStatementSet.class.getSimpleName(); - Element[] originatingElements = info.originatingElements.toArray(new Element[info.originatingElements.size()]); - - Types typeUtils = typeUtils(); - Filer filer = processingEnv.getFiler(); - try (PrintWriter out = createSourceFile(pkg, matchStatementClassName, filer, originatingElements)) { - - out.println("// CheckStyle: stop header check"); - out.println("// GENERATED CONTENT - DO NOT EDIT"); - out.println("// Source: " + topDeclaringClass + ".java"); - out.println("package " + pkg + ";"); - out.println(""); - out.println("import java.util.*;"); - out.println("import " + MatchStatementSet.class.getPackage().getName() + ".*;"); - out.println("import " + NodeLIRBuilder.class.getName() + ";"); - out.println("import " + Position.class.getName() + ";"); - for (String p : info.requiredPackages) { - out.println("import " + p + ".*;"); - } - out.println(""); - - out.println("public class " + matchStatementClassName + " implements " + MatchStatementSet.class.getSimpleName() + " {"); - - out.println(); - - // Generate declarations for the wrapper class to invoke the code generation methods. - for (MethodInvokerItem invoker : invokers.values()) { - StringBuilder args = new StringBuilder(); - StringBuilder types = new StringBuilder(); - int count = invoker.fields.size(); - int index = 0; - for (VariableElement arg : invoker.fields) { - args.append('"'); - args.append(arg.getSimpleName()); - args.append('"'); - types.append(String.format("(%s) args[%s]", fullClassName(typeUtils.asElement(arg.asType())), index++)); - if (count-- > 1) { - args.append(", "); - types.append(", "); - } - } - out.printf(" private static final String[] %s = new String[] {%s};\n", invoker.argumentsListName(), args); - out.printf(" private static final class %s implements MatchGenerator {\n", invoker.wrapperClass()); - out.printf(" static MatchGenerator instance = new %s();\n", invoker.wrapperClass()); - out.printf(" public ComplexMatchResult match(NodeLIRBuilder builder, Object...args) {\n"); - out.printf(" return ((%s) builder).%s(%s);\n", invoker.nodeLIRBuilderClass, invoker.methodName, types); - out.printf(" }\n"); - out.printf(" public String getName() {\n"); - out.printf(" return \"%s\";\n", invoker.methodName); - out.printf(" }\n"); - out.printf(" }\n"); - out.println(); - - } - - String desc = MatchStatement.class.getSimpleName(); - - out.println(" public Class forClass() {"); - out.println(" return " + topDeclaringClass + ".class;"); - out.println(" }"); - out.println(); - out.println(" @Override"); - out.println(" public List<" + desc + "> statements() {"); - out.println(" // Checkstyle: stop "); - - for (String positionDeclaration : info.positionDeclarations) { - out.println(" " + positionDeclaration); - } - out.println(); - - out.println(" List<" + desc + "> statements = Collections.unmodifiableList(Arrays.asList("); - - int i = 0; - for (MatchRuleItem matchRule : info.matchRules) { - String comma = i == info.matchRules.size() - 1 ? "" : ","; - out.printf(" %s%s\n", matchRule.ruleBuilder(), comma); - i++; - } - out.println(" ));"); - out.println(" // Checkstyle: resume"); - out.println(" return statements;"); - out.println(" }"); - - out.println(); - - out.println("}"); - } - - try { - createProviderFile(pkg, matchStatementClassName, originatingElements); - } catch (IOException e) { - reportExceptionThrow(info.topDeclaringType, e); - } - } - - private void createProviderFile(String pkg, String providerClassName, Element... originatingElements) throws IOException { - String filename = "META-INF/providers/" + pkg + "." + providerClassName; - FileObject file = processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", filename, originatingElements); - PrintWriter writer = new PrintWriter(new OutputStreamWriter(file.openOutputStream(), "UTF-8")); - writer.println(MatchStatementSet.class.getName()); - writer.close(); - } - - protected PrintWriter createSourceFile(String pkg, String relativeName, Filer filer, Element... originatingElements) { - try { - // Ensure Unix line endings to comply with Graal code style guide checked by Checkstyle - JavaFileObject sourceFile = filer.createSourceFile(pkg + "." + relativeName, originatingElements); - return new PrintWriter(sourceFile.openWriter()) { - - @Override - public void println() { - print("\n"); - } - }; - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - /** - * Used to generate the MatchStatement constructor invocation. - */ - static class MatchRuleItem { - private final String matchPattern; - private final MethodInvokerItem invoker; - - public MatchRuleItem(String matchPattern, MethodInvokerItem invoker) { - this.matchPattern = matchPattern; - this.invoker = invoker; - } - - /** - * @return a string which will construct the MatchStatement instance to match this pattern. - */ - public String ruleBuilder() { - return String.format("new MatchStatement(\"%s\", %s, %s.instance, %s)", invoker.methodName, matchPattern, invoker.wrapperClass(), invoker.argumentsListName()); - } - } - - /** - * Used to generate the wrapper class to invoke the code generation method. - */ - static class MethodInvokerItem { - final String methodName; - final String nodeLIRBuilderClass; - final ExecutableElement method; - final List fields; - - MethodInvokerItem(String methodName, String nodeLIRBuilderClass, ExecutableElement method, List fields) { - this.methodName = methodName; - this.nodeLIRBuilderClass = nodeLIRBuilderClass; - this.method = method; - this.fields = fields; - } - - String wrapperClass() { - return "MatchGenerator_" + methodName; - } - - String argumentsListName() { - return methodName + "_arguments"; - } - } - - static class MatchRuleDescriptor { - - final TypeElement topDeclaringType; - final List matchRules = new ArrayList<>(); - private final Set originatingElements = new HashSet<>(); - public Set positionDeclarations = new LinkedHashSet<>(); - - /** - * The set of packages which must be imported to refer the classes mention in matchRules. - */ - Set requiredPackages = new HashSet<>(); - - public MatchRuleDescriptor(TypeElement topDeclaringType) { - this.topDeclaringType = topDeclaringType; - } - } - - private static TypeElement topDeclaringType(Element element) { - Element enclosing = element.getEnclosingElement(); - if (enclosing == null || enclosing.getKind() == ElementKind.PACKAGE) { - assert element.getKind() == ElementKind.CLASS || element.getKind() == ElementKind.INTERFACE; - return (TypeElement) element; - } - return topDeclaringType(enclosing); - } - - private AnnotationMirror findAnnotationMirror(Element element, TypeMirror typeMirror) { - for (AnnotationMirror mirror : element.getAnnotationMirrors()) { - if (typeUtils().isSameType(mirror.getAnnotationType(), typeMirror)) { - return mirror; - } - } - return null; - } - - @Override - public boolean process(Set annotations, RoundEnvironment roundEnv) { - if (roundEnv.processingOver()) { - return true; - } - - logMessage("Starting round %s\n", roundEnv); - matchRulesTypeMirror = processingEnv.getElementUtils().getTypeElement(MatchRules.class.getCanonicalName()).asType(); - matchRuleTypeMirror = processingEnv.getElementUtils().getTypeElement(MatchRule.class.getCanonicalName()).asType(); - - matchableNodeTypeMirror = processingEnv.getElementUtils().getTypeElement(MatchableNode.class.getCanonicalName()).asType(); - matchableNodesTypeMirror = processingEnv.getElementUtils().getTypeElement(MatchableNodes.class.getCanonicalName()).asType(); - - Element currentElement = null; - try { - for (Element element : roundEnv.getElementsAnnotatedWith(MatchableNode.class)) { - logMessage("%s\n", element); - processMatchableNode(element); - } - for (Element element : roundEnv.getElementsAnnotatedWith(MatchableNodes.class)) { - logMessage("%s\n", element); - processMatchableNode(element); - } - // Define a TypeDescriptor for the generic node but don't enter it into the nodeTypes - // table since it shouldn't be mentioned in match rules. - TypeMirror valueTypeMirror = processingEnv.getElementUtils().getTypeElement(ValueNode.class.getName()).asType(); - valueType = new TypeDescriptor(valueTypeMirror, "Value", ValueNode.class.getSimpleName(), ValueNode.class.getPackage().getName(), new String[0], false, false); - - Map map = new LinkedHashMap<>(); - - for (Element element : roundEnv.getElementsAnnotatedWith(MatchRule.class)) { - currentElement = element; - processMatchRule(map, element, findAnnotationMirror(element, matchRuleTypeMirror)); - } - for (Element element : roundEnv.getElementsAnnotatedWith(MatchRules.class)) { - currentElement = element; - processMatchRule(map, element, findAnnotationMirror(element, matchRulesTypeMirror)); - } - - currentElement = null; - for (MatchRuleDescriptor info : map.values()) { - createFiles(info); - } - - } catch (Throwable t) { - reportExceptionThrow(currentElement, t); - } - - return true; - } - - /** - * Build up the type table to be used during parsing of the MatchRule. - */ - private void processMatchableNode(Element element) { - if (!processedMatchableNode.contains(element)) { - try { - processedMatchableNode.add(element); - - AnnotationMirror mirror = findAnnotationMirror(element, matchableNodesTypeMirror); - if (mirror == null) { - mirror = findAnnotationMirror(element, matchableNodeTypeMirror); - } - if (mirror == null) { - return; - } - TypeElement topDeclaringType = topDeclaringType(element); - List mirrors = null; - if (typeUtils().isSameType(mirror.getAnnotationType(), matchableNodesTypeMirror)) { - // Unpack the mirrors for a repeatable annotation - mirrors = getAnnotationValueList(AnnotationMirror.class, mirror, "value"); - } - int i = 0; - for (MatchableNode matchableNode : element.getAnnotationsByType(MatchableNode.class)) { - processMatchableNode(element, topDeclaringType, matchableNode, mirrors != null ? mirrors.get(i++) : mirror); - } - } catch (Throwable t) { - reportExceptionThrow(element, t); - } - } - } - - private void processMatchableNode(Element element, TypeElement topDeclaringType, MatchableNode matchable, AnnotationMirror mirror) throws GraalInternalError { - logMessage("processMatchableNode %s %s %s\n", topDeclaringType, element, matchable); - String nodeClass; - String nodePackage; - TypeMirror nodeClassMirror = null; - try { - matchable.nodeClass(); - } catch (MirroredTypeException e) { - nodeClassMirror = e.getTypeMirror(); - } - if (nodeClassMirror == null) { - throw new GraalInternalError("Can't get mirror for node class %s", element); - } - if (nodeClassMirror.toString().equals(MatchableNode.class.getName())) { - nodeClass = topDeclaringType.getQualifiedName().toString(); - } else { - nodeClass = nodeClassMirror.toString(); - } - TypeElement typeElement = processingEnv.getElementUtils().getTypeElement(nodeClass); - if (typeElement == null) { - errorMessage(element, mirror, "Class \"%s\" cannot be resolved to a type", nodeClass); - return; - } - nodePackage = findPackage(typeElement); - assert nodeClass.startsWith(nodePackage); - nodeClass = nodeClass.substring(nodePackage.length() + 1); - assert nodeClass.endsWith("Node"); - String shortName = nodeClass.substring(0, nodeClass.length() - 4); - - Types typeUtils = processingEnv.getTypeUtils(); - TypeElement nodeClassElement = (TypeElement) typeUtils.asElement(nodeClassMirror); - for (String input : matchable.inputs()) { - boolean ok = false; - TypeElement current = nodeClassElement; - while (!ok && current != null) { - for (Element fieldElement : ElementFilter.fieldsIn(current.getEnclosedElements())) { - if (fieldElement.getSimpleName().toString().equals(input)) { - ok = true; - break; - } - } - TypeMirror theSuper = current.getSuperclass(); - current = (TypeElement) typeUtils.asElement(theSuper); - } - if (!ok) { - errorMessage(element, mirror, "Input named \"%s\" doesn't exist in %s", input, nodeClassElement.getSimpleName()); - } - } - - declareType(nodeClassMirror, shortName, nodeClass, nodePackage, matchable.inputs(), matchable.commutative(), matchable.shareable(), element); - } - - private void processMatchRule(Map map, Element element, AnnotationMirror mirror) { - if (!processedMatchRule.contains(element)) { - try { - processedMatchRule.add(element); - - // The annotation element type should ensure this is true. - assert element instanceof ExecutableElement; - - findMatchableNodes(element); - - TypeElement topDeclaringType = topDeclaringType(element); - MatchRuleDescriptor info = map.get(topDeclaringType); - if (info == null) { - info = new MatchRuleDescriptor(topDeclaringType); - map.put(topDeclaringType, info); - } - List mirrors = null; - if (typeUtils().isSameType(mirror.getAnnotationType(), matchRulesTypeMirror)) { - // Unpack the mirrors for a repeatable annotation - mirrors = getAnnotationValueList(AnnotationMirror.class, mirror, "value"); - } - int i = 0; - for (MatchRule matchRule : element.getAnnotationsByType(MatchRule.class)) { - processMethodMatchRule((ExecutableElement) element, info, matchRule, mirrors != null ? mirrors.get(i++) : mirror); - } - } catch (Throwable t) { - reportExceptionThrow(element, t); - } - } - } - - /** - * Search the super types of element for MatchableNode definitions. Any superclass or super - * interface can contain definitions of matchable nodes. - * - * @param element - */ - private void findMatchableNodes(Element element) { - processMatchableNode(element); - Element enclosing = element.getEnclosingElement(); - while (enclosing != null) { - if (enclosing.getKind() == ElementKind.CLASS || enclosing.getKind() == ElementKind.INTERFACE) { - TypeElement current = (TypeElement) enclosing; - while (current != null) { - processMatchableNode(current); - for (TypeMirror intf : current.getInterfaces()) { - Element interfaceElement = typeUtils().asElement(intf); - processMatchableNode(interfaceElement); - // Recurse - findMatchableNodes(interfaceElement); - } - TypeMirror theSuper = current.getSuperclass(); - current = (TypeElement) typeUtils().asElement(theSuper); - } - } - enclosing = enclosing.getEnclosingElement(); - } - } - - private Types typeUtils() { - return processingEnv.getTypeUtils(); - } - - private void processMethodMatchRule(ExecutableElement method, MatchRuleDescriptor info, MatchRule matchRule, AnnotationMirror mirror) { - logMessage("processMethodMatchRule %s %s\n", method, mirror); - - Types typeUtils = typeUtils(); - - if (!method.getModifiers().contains(Modifier.PUBLIC)) { - errorMessage(method, "MatchRule method %s must be public", method.getSimpleName()); - return; - } - if (method.getModifiers().contains(Modifier.STATIC)) { - errorMessage(method, "MatchRule method %s must be non-static", method.getSimpleName()); - return; - } - - try { - TypeMirror returnType = method.getReturnType(); - if (!typeUtils.isSameType(returnType, processingEnv.getElementUtils().getTypeElement(ComplexMatchResult.class.getName()).asType())) { - errorMessage(method, "MatchRule method return type must be %s", ComplexMatchResult.class.getName()); - return; - } - - String rule = matchRule.value(); - RuleParser parser = new RuleParser(rule); - ArrayList expectedTypes = parser.capturedTypes(); - ArrayList expectedNames = parser.capturedNames(); - List actualParameters = method.getParameters(); - if (expectedTypes.size() + 1 < actualParameters.size()) { - errorMessage(method, "Too many arguments for match method %s != %s", expectedTypes.size() + 1, actualParameters.size()); - return; - } - - // Walk through the parameters to the method and see if they exist in the match rule. - // The order doesn't matter but only names mentioned in the rule can be used and they - // must be assignment compatible. - for (VariableElement parameter : actualParameters) { - String name = parameter.getSimpleName().toString(); - int nameIndex = expectedNames.indexOf(name); - if (nameIndex == -1) { - errorMessage(method, "Argument \"%s\" isn't captured in the match rule", name); - return; - } - TypeMirror type = parameter.asType(); - if (!typeUtils.isAssignable(expectedTypes.get(nameIndex).mirror, type)) { - errorMessage(method, "Captured value \"%s\" of type %s is not assignable to argument of type %s", name, expectedTypes.get(nameIndex).mirror, type); - return; - } - } - - String methodName = method.getSimpleName().toString(); - MethodInvokerItem invoker = invokers.get(methodName); - if (invoker == null) { - invoker = new MethodInvokerItem(methodName, topDeclaringType(method).getSimpleName().toString(), method, actualParameters); - invokers.put(methodName, invoker); - } else if (invoker.method != method) { - // This could be supported but it's easier if they are unique since the names - // are used in log output and snippet counters. - errorMessage(method, "Use unique method names for match methods."); - return; - } - - Element enclosing = method.getEnclosingElement(); - String declaringClass = ""; - String separator = ""; - Set originatingElementsList = info.originatingElements; - originatingElementsList.add(method); - while (enclosing != null) { - if (enclosing.getKind() == ElementKind.CLASS || enclosing.getKind() == ElementKind.INTERFACE) { - if (enclosing.getModifiers().contains(Modifier.PRIVATE)) { - errorMessage(method, "MatchRule cannot be declared in a private %s %s", enclosing.getKind().name().toLowerCase(), enclosing); - return; - } - originatingElementsList.add(enclosing); - declaringClass = enclosing.getSimpleName() + separator + declaringClass; - separator = "."; - } else { - assert enclosing.getKind() == ElementKind.PACKAGE; - } - enclosing = enclosing.getEnclosingElement(); - } - - originatingElementsList.addAll(parser.originatingElements); - info.requiredPackages.addAll(parser.requiredPackages); - - // Accumulate any position declarations. - parser.generatePositionDeclarations(info.positionDeclarations); - - List matches = parser.generateVariants(); - for (String match : matches) { - info.matchRules.add(new MatchRuleItem(match, invoker)); - } - } catch (RuleParseError e) { - errorMessage(method, mirror, e.getMessage()); - } - } - - private void errorMessage(Element element, String format, Object... args) { - processingEnv.getMessager().printMessage(Kind.ERROR, String.format(format, args), element); - } - - private void errorMessage(Element element, AnnotationMirror mirror, String format, Object... args) { - processingEnv.getMessager().printMessage(Kind.ERROR, String.format(format, args), element, mirror); - } - - // TODO borrowed from com.oracle.truffle.dsl.processor.Utils - @SuppressWarnings("unchecked") - private static List getAnnotationValueList(Class expectedListType, AnnotationMirror mirror, String name) { - List values = getAnnotationValue(List.class, mirror, name); - List result = new ArrayList<>(); - - if (values != null) { - for (AnnotationValue value : values) { - T annotationValue = resolveAnnotationValue(expectedListType, value); - if (annotationValue != null) { - result.add(annotationValue); - } - } - } - return result; - } - - private static T getAnnotationValue(Class expectedType, AnnotationMirror mirror, String name) { - return resolveAnnotationValue(expectedType, getAnnotationValue(mirror, name)); - } - - @SuppressWarnings({"unchecked"}) - private static T resolveAnnotationValue(Class expectedType, AnnotationValue value) { - if (value == null) { - return null; - } - - Object unboxedValue = value.accept(new AnnotationValueVisitorImpl(), null); - if (unboxedValue != null) { - if (expectedType == TypeMirror.class && unboxedValue instanceof String) { - return null; - } - if (!expectedType.isAssignableFrom(unboxedValue.getClass())) { - throw new ClassCastException(unboxedValue.getClass().getName() + " not assignable from " + expectedType.getName()); - } - } - return (T) unboxedValue; - } - - private static AnnotationValue getAnnotationValue(AnnotationMirror mirror, String name) { - ExecutableElement valueMethod = null; - for (ExecutableElement method : ElementFilter.methodsIn(mirror.getAnnotationType().asElement().getEnclosedElements())) { - if (method.getSimpleName().toString().equals(name)) { - valueMethod = method; - break; - } - } - - if (valueMethod == null) { - return null; - } - - AnnotationValue value = mirror.getElementValues().get(valueMethod); - if (value == null) { - value = valueMethod.getDefaultValue(); - } - - return value; - } - - private static class AnnotationValueVisitorImpl extends AbstractAnnotationValueVisitor7 { - - @Override - public Object visitBoolean(boolean b, Void p) { - return Boolean.valueOf(b); - } - - @Override - public Object visitByte(byte b, Void p) { - return Byte.valueOf(b); - } - - @Override - public Object visitChar(char c, Void p) { - return c; - } - - @Override - public Object visitDouble(double d, Void p) { - return d; - } - - @Override - public Object visitFloat(float f, Void p) { - return f; - } - - @Override - public Object visitInt(int i, Void p) { - return i; - } - - @Override - public Object visitLong(long i, Void p) { - return i; - } - - @Override - public Object visitShort(short s, Void p) { - return s; - } - - @Override - public Object visitString(String s, Void p) { - return s; - } - - @Override - public Object visitType(TypeMirror t, Void p) { - return t; - } - - @Override - public Object visitEnumConstant(VariableElement c, Void p) { - return c; - } - - @Override - public Object visitAnnotation(AnnotationMirror a, Void p) { - return a; - } - - @Override - public Object visitArray(List vals, Void p) { - return vals; - } - - } -} diff -r 2daf39328194 -r db96f9915540 graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/ArrayCopyIntrinsificationTest.java --- a/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/ArrayCopyIntrinsificationTest.java Thu Apr 23 21:18:27 2015 -0700 +++ b/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/ArrayCopyIntrinsificationTest.java Thu Apr 23 22:09:27 2015 -0700 @@ -256,7 +256,6 @@ * "https://code.google.com/r/baggiogamp-guava/source/browse/guava/src/com/google/common/collect/ArrayTable.java?r=d2e06112416223cb5437d43c12a989c0adc7345b#181" * > com.google.common.collect.ArrayTable(ArrayTable other). */ - @Ignore @Test public void testCopyRows() { mustIntrinsify = false; diff -r 2daf39328194 -r db96f9915540 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotDebugInfoBuilder.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotDebugInfoBuilder.java Thu Apr 23 21:18:27 2015 -0700 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotDebugInfoBuilder.java Thu Apr 23 22:09:27 2015 -0700 @@ -26,6 +26,7 @@ import com.oracle.graal.api.code.*; import com.oracle.graal.api.meta.*; +import com.oracle.graal.compiler.common.*; import com.oracle.graal.compiler.gen.*; import com.oracle.graal.graph.*; import com.oracle.graal.nodes.*; @@ -62,7 +63,10 @@ @Override protected BytecodeFrame computeFrameForState(FrameState state) { - assert !isPlaceholderBci(state.bci) || state.bci == BytecodeFrame.BEFORE_BCI : state.bci; + if (isPlaceholderBci(state.bci) && state.bci != BytecodeFrame.BEFORE_BCI) { + // This is really a hard error since an incorrect state could crash hotspot + throw GraalInternalError.shouldNotReachHere("Invalid state " + BytecodeFrame.getPlaceholderBciName(state.bci) + " " + state); + } return super.computeFrameForState(state); } } diff -r 2daf39328194 -r db96f9915540 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/DefaultHotSpotLoweringProvider.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/DefaultHotSpotLoweringProvider.java Thu Apr 23 21:18:27 2015 -0700 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/DefaultHotSpotLoweringProvider.java Thu Apr 23 22:09:27 2015 -0700 @@ -475,7 +475,7 @@ private WriteNode createWriteHub(StructuredGraph graph, ValueNode object, ValueNode value) { HotSpotVMConfig config = runtime.getConfig(); LocationNode location = graph.unique(new ConstantLocationNode(HUB_WRITE_LOCATION, config.hubOffset)); - assert !object.isConstant() || object.isNullConstant(); + assert !object.isConstant() || object.asConstant().isDefaultForKind(); ValueNode writeValue = value; if (config.useCompressedClassPointers) { diff -r 2daf39328194 -r db96f9915540 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotSuitesProvider.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotSuitesProvider.java Thu Apr 23 21:18:27 2015 -0700 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotSuitesProvider.java Thu Apr 23 22:09:27 2015 -0700 @@ -31,6 +31,8 @@ import com.oracle.graal.hotspot.phases.*; import com.oracle.graal.java.*; import com.oracle.graal.lir.phases.*; +import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.StructuredGraph.*; import com.oracle.graal.options.*; import com.oracle.graal.options.DerivedOptionValue.OptionSupplier; import com.oracle.graal.phases.*; @@ -104,10 +106,34 @@ PhaseSuite suite = new PhaseSuite<>(); GraphBuilderConfiguration config = GraphBuilderConfiguration.getDefault(plugins); suite.appendPhase(new GraphBuilderPhase(config)); + assert appendGraphEncoderTest(suite); return suite; } /** + * When assertions are enabled, we encode and decode every parsed graph, to ensure that the + * encoding and decoding process work correctly. The decoding performs canonicalization during + * decoding, so the decoded graph can be different than the encoded graph - we cannot check them + * for equality here. However, the encoder {@link GraphEncoder#verifyEncoding verifies the + * encoding itself}, i.e., performs a decoding without canoncialization and checks the graphs + * for equality. + */ + private boolean appendGraphEncoderTest(PhaseSuite suite) { + suite.appendPhase(new BasePhase() { + @Override + protected void run(StructuredGraph graph, HighTierContext context) { + EncodedGraph encodedGraph = GraphEncoder.encodeSingleGraph(graph, runtime.getTarget().arch); + + SimplifyingGraphDecoder graphDecoder = new SimplifyingGraphDecoder(context.getMetaAccess(), context.getConstantReflection(), context.getStampProvider(), !ImmutableCode.getValue(), + runtime.getTarget().arch); + StructuredGraph targetGraph = new StructuredGraph(graph.method(), AllowAssumptions.YES); + graphDecoder.decode(targetGraph, encodedGraph); + } + }); + return true; + } + + /** * Modifies the {@link GraphBuilderConfiguration} to build extra * {@linkplain DebugInfoMode#Simple debug info} if the VM * {@linkplain CompilerToVM#shouldDebugNonSafepoints() requests} it. diff -r 2daf39328194 -r db96f9915540 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/SerialWriteBarrier.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/SerialWriteBarrier.java Thu Apr 23 21:18:27 2015 -0700 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/SerialWriteBarrier.java Thu Apr 23 22:09:27 2015 -0700 @@ -31,18 +31,12 @@ public class SerialWriteBarrier extends WriteBarrier { public static final NodeClass TYPE = NodeClass.create(SerialWriteBarrier.class); - protected final boolean alwaysNull; - public SerialWriteBarrier(ValueNode object, LocationNode location, boolean precise, boolean alwaysNull) { - this(TYPE, object, location, precise, alwaysNull); + public SerialWriteBarrier(ValueNode object, LocationNode location, boolean precise) { + this(TYPE, object, location, precise); } - protected SerialWriteBarrier(NodeClass c, ValueNode object, LocationNode location, boolean precise, boolean alwaysNull) { + protected SerialWriteBarrier(NodeClass c, ValueNode object, LocationNode location, boolean precise) { super(c, object, null, location, precise); - this.alwaysNull = alwaysNull; - } - - public boolean alwaysNull() { - return alwaysNull; } } diff -r 2daf39328194 -r db96f9915540 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/phases/WriteBarrierAdditionPhase.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/phases/WriteBarrierAdditionPhase.java Thu Apr 23 21:18:27 2015 -0700 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/phases/WriteBarrierAdditionPhase.java Thu Apr 23 22:09:27 2015 -0700 @@ -87,8 +87,12 @@ protected void addSerialPostWriteBarrier(FixedAccessNode node, ValueNode object, ValueNode value, LocationNode location, boolean precise, StructuredGraph graph) { final boolean alwaysNull = StampTool.isPointerAlwaysNull(value); + if (alwaysNull) { + // Serial barrier isn't needed for null value + return; + } final LocationNode loc = (precise ? location : null); - graph.addAfterFixed(node, graph.add(new SerialWriteBarrier(object, loc, precise, alwaysNull))); + graph.addAfterFixed(node, graph.add(new SerialWriteBarrier(object, loc, precise))); } private void addWriteNodeBarriers(WriteNode node, StructuredGraph graph) { diff -r 2daf39328194 -r db96f9915540 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/phases/WriteBarrierVerificationPhase.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/phases/WriteBarrierVerificationPhase.java Thu Apr 23 21:18:27 2015 -0700 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/phases/WriteBarrierVerificationPhase.java Thu Apr 23 22:09:27 2015 -0700 @@ -27,6 +27,7 @@ import java.util.*; +import com.oracle.graal.compiler.common.*; import com.oracle.graal.graph.*; import com.oracle.graal.hotspot.nodes.*; import com.oracle.graal.hotspot.replacements.*; @@ -34,6 +35,7 @@ import com.oracle.graal.nodes.HeapAccess.BarrierType; import com.oracle.graal.nodes.extended.*; import com.oracle.graal.nodes.java.*; +import com.oracle.graal.nodes.type.*; import com.oracle.graal.phases.*; /** @@ -92,17 +94,22 @@ final Node previous = node.predecessor(); final boolean validatePreBarrier = HotSpotReplacementsUtil.useG1GC() && (isObjectWrite(node) || !((ArrayRangeWriteNode) node).isInitialization()); if (isObjectWrite(node)) { - return next instanceof WriteBarrier && validateBarrier((FixedAccessNode) node, (WriteBarrier) next) && - (!validatePreBarrier || (previous instanceof WriteBarrier && validateBarrier((FixedAccessNode) node, (WriteBarrier) previous))); - + return (isObjectBarrier(node, next) || StampTool.isPointerAlwaysNull(getValueWritten(node))) && (!validatePreBarrier || isObjectBarrier(node, previous)); } else if (isObjectArrayRangeWrite(node)) { - return ((next instanceof ArrayRangeWriteBarrier) && ((ArrayRangeWriteNode) node).getArray() == ((ArrayRangeWriteBarrier) next).getObject()) && - (!validatePreBarrier || ((previous instanceof ArrayRangeWriteBarrier) && ((ArrayRangeWriteNode) node).getArray() == ((ArrayRangeWriteBarrier) previous).getObject())); + return (isArrayBarrier(node, next) || StampTool.isPointerAlwaysNull(getValueWritten(node))) && (!validatePreBarrier || isArrayBarrier(node, previous)); } else { return true; } } + private static boolean isObjectBarrier(FixedWithNextNode node, final Node next) { + return next instanceof WriteBarrier && validateBarrier((FixedAccessNode) node, (WriteBarrier) next); + } + + private static boolean isArrayBarrier(FixedWithNextNode node, final Node next) { + return (next instanceof ArrayRangeWriteBarrier) && ((ArrayRangeWriteNode) node).getArray() == ((ArrayRangeWriteBarrier) next).getObject(); + } + private static boolean isObjectWrite(Node node) { // Read nodes with barrier attached (G1 Ref field) are not validated yet. return node instanceof FixedAccessNode && ((HeapAccess) node).getBarrierType() != BarrierType.NONE && !(node instanceof ReadNode); @@ -129,6 +136,18 @@ return ((node instanceof DeoptimizingNode) && ((DeoptimizingNode) node).canDeoptimize()) || (node instanceof LoopBeginNode); } + private static ValueNode getValueWritten(FixedWithNextNode write) { + if (write instanceof WriteNode) { + return ((WriteNode) write).value(); + } else if (write instanceof LoweredCompareAndSwapNode) { + return ((LoweredCompareAndSwapNode) write).getNewValue(); + } else if (write instanceof LoweredAtomicReadAndWriteNode) { + return ((LoweredAtomicReadAndWriteNode) write).getNewValue(); + } else { + throw GraalInternalError.shouldNotReachHere(String.format("unexpected write node %s", write)); + } + } + private static boolean validateBarrier(FixedAccessNode write, WriteBarrier barrier) { assert write instanceof WriteNode || write instanceof LoweredCompareAndSwapNode || write instanceof LoweredAtomicReadAndWriteNode : "Node must be of type requiring a write barrier " + write; if ((barrier.getObject() == write.object()) && (!barrier.usePrecise() || (barrier.usePrecise() && barrier.getLocation() == write.location()))) { diff -r 2daf39328194 -r db96f9915540 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/WriteBarrierSnippets.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/WriteBarrierSnippets.java Thu Apr 23 21:18:27 2015 -0700 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/WriteBarrierSnippets.java Thu Apr 23 22:09:27 2015 -0700 @@ -350,10 +350,6 @@ } public void lower(SerialWriteBarrier writeBarrier, LoweringTool tool) { - if (writeBarrier.alwaysNull()) { - writeBarrier.graph().removeFixed(writeBarrier); - return; - } Arguments args = new Arguments(serialWriteBarrier, writeBarrier.graph().getGuardsStage(), tool.getLoweringStage()); args.add("object", writeBarrier.getObject()); args.add("location", writeBarrier.getLocation()); diff -r 2daf39328194 -r db96f9915540 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/arraycopy/ArrayCopySnippets.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/arraycopy/ArrayCopySnippets.java Thu Apr 23 21:18:27 2015 -0700 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/arraycopy/ArrayCopySnippets.java Thu Apr 23 22:09:27 2015 -0700 @@ -207,6 +207,9 @@ // copied elements (xor'd with -1). copiedElements ^= -1; System.arraycopy(nonNullSrc, srcPos + copiedElements, nonNullDest, destPos + copiedElements, length - copiedElements); + } else { + // Capture an after state for this path. + ArrayCopyStateNode.captureState(); } } diff -r 2daf39328194 -r db96f9915540 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/arraycopy/ArrayCopyStateNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/arraycopy/ArrayCopyStateNode.java Thu Apr 23 22:09:27 2015 -0700 @@ -0,0 +1,57 @@ +/* + * Copyright (c) 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. + */ +//JaCoCo Exclude +package com.oracle.graal.hotspot.replacements.arraycopy; + +import com.oracle.graal.api.meta.*; +import com.oracle.graal.compiler.common.type.*; +import com.oracle.graal.graph.*; +import com.oracle.graal.nodeinfo.*; +import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.spi.*; + +/** + * A dummy node whose only purpose is to capture a final {@link FrameState} when lowering complex + * arraycopy snippets. + */ + +@NodeInfo +public final class ArrayCopyStateNode extends AbstractStateSplit implements Lowerable { + + public static final NodeClass TYPE = NodeClass.create(ArrayCopyStateNode.class); + + protected ArrayCopyStateNode() { + super(TYPE, StampFactory.forKind(Kind.Void)); + + } + + @Override + public void lower(LoweringTool tool) { + if (graph().getGuardsStage().areFrameStatesAtDeopts()) { + graph().removeFixed(this); + } + } + + @NodeIntrinsic + public static native void captureState(); +} diff -r 2daf39328194 -r db96f9915540 graal/com.oracle.graal.hotspotvmconfig.processor/src/META-INF/services/javax.annotation.processing.Processor --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.hotspotvmconfig.processor/src/META-INF/services/javax.annotation.processing.Processor Thu Apr 23 22:09:27 2015 -0700 @@ -0,0 +1,1 @@ +com.oracle.graal.hotspotvmconfig.processor.HotSpotVMConfigProcessor diff -r 2daf39328194 -r db96f9915540 graal/com.oracle.graal.hotspotvmconfig.processor/src/com/oracle/graal/hotspotvmconfig/processor/HotSpotVMConfigProcessor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.hotspotvmconfig.processor/src/com/oracle/graal/hotspotvmconfig/processor/HotSpotVMConfigProcessor.java Thu Apr 23 22:09:27 2015 -0700 @@ -0,0 +1,424 @@ +/* + * Copyright (c) 2014, 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 com.oracle.graal.hotspotvmconfig.processor; + +import java.io.*; +import java.lang.annotation.*; +import java.util.*; +import java.util.Map.Entry; +import java.util.function.*; + +import javax.annotation.processing.*; +import javax.lang.model.*; +import javax.lang.model.element.*; +import javax.tools.Diagnostic.Kind; +import javax.tools.*; + +import com.oracle.graal.compiler.common.*; +import com.oracle.graal.hotspotvmconfig.*; + +@SupportedAnnotationTypes({"com.oracle.graal.hotspotvmconfig.HotSpotVMConstant", "com.oracle.graal.hotspotvmconfig.HotSpotVMFlag", "com.oracle.graal.hotspotvmconfig.HotSpotVMField", + "com.oracle.graal.hotspotvmconfig.HotSpotVMType", "com.oracle.graal.hotspotvmconfig.HotSpotVMValue"}) +public class HotSpotVMConfigProcessor extends AbstractProcessor { + + public HotSpotVMConfigProcessor() { + } + + @Override + public SourceVersion getSupportedSourceVersion() { + return SourceVersion.latest(); + } + + /** + * Set to true to enable logging to a local file during annotation processing. There's no normal + * channel for any debug messages and debugging annotation processors requires some special + * setup. + */ + private static final boolean DEBUG = false; + + private PrintWriter log; + + /** + * Logging facility for debugging the annotation processor. + */ + + private PrintWriter getLog() { + if (log == null) { + try { + // Create the log file within the generated source directory so it's easy to find. + // /tmp isn't platform independent and java.io.tmpdir can map anywhere, particularly + // on the mac. + FileObject file = processingEnv.getFiler().createResource(StandardLocation.SOURCE_OUTPUT, "", getClass().getSimpleName() + "log"); + log = new PrintWriter(new FileWriter(file.toUri().getPath(), true)); + } catch (IOException e) { + // Do nothing + } + } + return log; + } + + private void logMessage(String format, Object... args) { + if (!DEBUG) { + return; + } + PrintWriter bw = getLog(); + if (bw != null) { + bw.printf(format, args); + bw.flush(); + } + } + + private void logException(Throwable t) { + if (!DEBUG) { + return; + } + PrintWriter bw = getLog(); + if (bw != null) { + t.printStackTrace(bw); + bw.flush(); + } + } + + /** + * Bugs in an annotation processor can cause silent failure so try to report any exception + * throws as errors. + */ + private void reportExceptionThrow(Element element, Throwable t) { + if (element != null) { + logMessage("throw for %s:\n", element); + } + logException(t); + errorMessage(element, "Exception throw during processing: %s %s", t, Arrays.toString(Arrays.copyOf(t.getStackTrace(), 4))); + } + + //@formatter:off + String[] prologue = new String[]{ + "// The normal wrappers CommandLineFlags::boolAt and CommandLineFlags::intxAt skip constant flags", + "static bool boolAt(char* name, bool* value) {", + " Flag* result = Flag::find_flag(name, strlen(name), true, true);", + " if (result == NULL) return false;", + " if (!result->is_bool()) return false;", + " *value = result->get_bool();", + " return true;", + "}", + "", + "static bool intxAt(char* name, intx* value) {", + " Flag* result = Flag::find_flag(name, strlen(name), true, true);", + " if (result == NULL) return false;", + " if (!result->is_intx()) return false;", + " *value = result->get_intx();", + " return true;", + "}", + "", + "#define set_boolean(name, value) vmconfig_oop->bool_field_put(fs.offset(), value)", + "#define set_byte(name, value) vmconfig_oop->byte_field_put(fs.offset(), (jbyte)(value))", + "#define set_short(name, value) vmconfig_oop->short_field_put(fs.offset(), (jshort)(value))", + "#define set_int(name, value) vmconfig_oop->int_field_put(fs.offset(), (int)(value))", + "#define set_long(name, value) vmconfig_oop->long_field_put(fs.offset(), value)", + "#define set_address(name, value) do { set_long(name, (jlong)(value)); } while (0)", + "", + "#define set_optional_boolean_flag(varName, flagName) do { bool flagValue; if (boolAt((char*) flagName, &flagValue)) { set_boolean(varName, flagValue); } } while (0)", + "#define set_optional_int_flag(varName, flagName) do { intx flagValue; if (intxAt((char*) flagName, &flagValue)) { set_int(varName, flagValue); } } while (0)", + "#define set_optional_long_flag(varName, flagName) do { intx flagValue; if (intxAt((char*) flagName, &flagValue)) { set_long(varName, flagValue); } } while (0)", + "", + "void VMStructs::initHotSpotVMConfig(oop vmconfig_oop) {", + " InstanceKlass* vmconfig_klass = InstanceKlass::cast(vmconfig_oop->klass());", + "", + }; + //@formatter:on + + String outputName = "HotSpotVMConfig.inline.hpp"; + String outputDirectory = "hotspot"; + + private void createFiles(Map annotations, Element element) { + + Filer filer = processingEnv.getFiler(); + try (PrintWriter out = createSourceFile(outputDirectory, outputName, filer, element)) { + + for (String line : prologue) { + out.println(line); + } + + Map expectedValues = new HashMap<>(); + for (VMConfigField value : annotations.values()) { + if (!value.optional) { + String key = value.define != null ? value.define : ""; + if (expectedValues.get(key) == null) { + expectedValues.put(key, 1); + } else { + expectedValues.put(key, expectedValues.get(key) + 1); + } + } + } + + out.printf(" int expected = %s;%n", expectedValues.get("")); + for (Entry entry : expectedValues.entrySet()) { + if (entry.getKey().equals("")) { + continue; + } + out.printf("#if %s%n", entry.getKey()); + out.printf(" expected += %s;%n", entry.getValue()); + out.printf("#endif%n"); + } + out.println(" int assigned = 0;"); + out.println(" for (JavaFieldStream fs(vmconfig_klass); !fs.done(); fs.next()) {"); + + Set fieldTypes = new HashSet<>(); + for (VMConfigField key : annotations.values()) { + fieldTypes.add(key.getType()); + } + // For each type of field, generate a switch on the length of the symbol and then do a + // direct compare. In general this reduces each operation to 2 tests plus a string + // compare. Being more perfect than that is probably not worth it. + for (String type : fieldTypes) { + String sigtype = type.equals("boolean") ? "bool" : type; + out.println(" if (fs.signature() == vmSymbols::" + sigtype + "_signature()) {"); + Set lengths = new HashSet<>(); + for (Entry entry : annotations.entrySet()) { + if (entry.getValue().getType().equals(type)) { + lengths.add(entry.getKey().length()); + } + } + out.println(" switch (fs.name()->utf8_length()) {"); + for (int len : lengths) { + out.println(" case " + len + ":"); + for (Entry entry : annotations.entrySet()) { + if (entry.getValue().getType().equals(type) && entry.getKey().length() == len) { + out.println(" if (fs.name()->equals(\"" + entry.getKey() + "\")) {"); + entry.getValue().emit(out); + out.println(" continue;"); + out.println(" }"); + } + } + out.println(" continue;"); + } + out.println(" } // switch"); + out.println(" continue;"); + out.println(" } // if"); + } + out.println(" } // for"); + out.println(" guarantee(assigned == expected, \"Didn't find all fields during init of HotSpotVMConfig. Maybe recompile?\");"); + out.println("}"); + } + } + + protected PrintWriter createSourceFile(String pkg, String relativeName, Filer filer, Element... originatingElements) { + try { + // Ensure Unix line endings to comply with Graal code style guide checked by Checkstyle + FileObject sourceFile = filer.createResource(StandardLocation.SOURCE_OUTPUT, pkg, relativeName, originatingElements); + logMessage("%s\n", sourceFile); + return new PrintWriter(sourceFile.openWriter()) { + + @Override + public void println() { + print("\n"); + } + }; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + static class VMConfigField { + final String setter; + final String define; + private boolean optional; + final VariableElement field; + + public VMConfigField(VariableElement field, HotSpotVMField value) { + this.field = field; + define = archDefines(value.archs()); + String type = field.asType().toString(); + String name = value.name(); + int i = name.lastIndexOf("::"); + switch (value.get()) { + case OFFSET: + setter = String.format("set_%s(\"%s\", offset_of(%s, %s));", type, field.getSimpleName(), name.substring(0, i), name.substring(i + 2)); + break; + case ADDRESS: + setter = String.format("set_address(\"%s\", &%s);", field.getSimpleName(), name); + break; + case VALUE: + setter = String.format("set_%s(\"%s\", (%s) (intptr_t) %s);", type, field.getSimpleName(), type, name); + break; + default: + throw new GraalInternalError("unexpected type: " + value.get()); + } + } + + public VMConfigField(VariableElement field, HotSpotVMType value) { + this.field = field; + define = null; // ((HotSpotVMType) annotation).archs(); + String type = field.asType().toString(); + setter = String.format("set_%s(\"%s\", sizeof(%s));", type, field.getSimpleName(), value.name()); + } + + public VMConfigField(VariableElement field, HotSpotVMValue value) { + this.field = field; + String[] defines = value.defines(); + int length = defines.length; + if (length != 0) { + for (int i = 0; i < length; i++) { + defines[i] = "defined(" + defines[i] + ")"; + } + define = String.join(" || ", defines); + } else { + define = null; // ((HotSpotVMValue) annotation).archs(); + } + String type = field.asType().toString(); + if (value.get() == HotSpotVMValue.Type.ADDRESS) { + setter = String.format("set_address(\"%s\", %s);", field.getSimpleName(), value.expression()); + } else { + setter = String.format("set_%s(\"%s\", %s);", type, field.getSimpleName(), value.expression()); + } + } + + public VMConfigField(VariableElement field, HotSpotVMConstant value) { + this.field = field; + define = archDefines(value.archs()); + String type = field.asType().toString(); + setter = String.format("set_%s(\"%s\", %s);", type, field.getSimpleName(), value.name()); + } + + public VMConfigField(VariableElement field, HotSpotVMFlag value) { + this.field = field; + define = archDefines(value.archs()); + optional = value.optional(); + String type = field.asType().toString(); + if (value.optional()) { + setter = String.format("set_optional_%s_flag(\"%s\", \"%s\");", type, field.getSimpleName(), value.name()); + } else { + setter = String.format("set_%s(\"%s\", %s);", type, field.getSimpleName(), value.name()); + } + } + + public String getType() { + return field.asType().toString(); + } + + private static String archDefine(String arch) { + switch (arch) { + case "amd64": + return "defined(AMD64)"; + case "sparcv9": + return "(defined(SPARC) && defined(_LP64))"; + case "sparc": + return "defined(SPARC)"; + default: + throw new GraalInternalError("unexpected arch: " + arch); + } + } + + private static String archDefines(String[] archs) { + if (archs == null || archs.length == 0) { + return null; + } + if (archs.length == 1) { + return archDefine(archs[0]); + } + String[] defs = new String[archs.length]; + int i = 0; + for (String arch : archs) { + defs[i++] = archDefine(arch); + } + return String.join(" || ", defs); + } + + public void emit(PrintWriter out) { + if (define != null) { + out.printf("#if %s\n", define); + } + out.printf(" %s%n", setter); + if (!optional) { + out.printf(" assigned++;%n"); + } + if (define != null) { + out.printf("#endif\n"); + } + } + + } + + @SuppressWarnings("unchecked") + private void collectAnnotations(RoundEnvironment roundEnv, Map annotationMap, Class annotationClass, + BiFunction builder) { + for (Element element : roundEnv.getElementsAnnotatedWith(annotationClass)) { + Annotation constant = element.getAnnotation(annotationClass); + if (element.getKind() != ElementKind.FIELD) { + errorMessage(element, "%s annotations may only be on fields", annotationClass.getSimpleName()); + } + if (annotationClass == HotSpotVMValue.class) { + HotSpotVMValue value = (HotSpotVMValue) constant; + if (value.get() == HotSpotVMValue.Type.ADDRESS && !element.asType().toString().equals("long")) { + errorMessage(element, "HotSpotVMValue with get == ADDRESS must be of type long, but found %s", element.asType()); + } + } + if (currentTypeElement == null) { + currentTypeElement = element.getEnclosingElement(); + } else { + if (!currentTypeElement.equals(element.getEnclosingElement())) { + errorMessage(element, "Multiple types encountered. Only HotSpotVMConfig is supported"); + } + } + annotationMap.put(element.getSimpleName().toString(), builder.apply((VariableElement) element, (T) constant)); + } + } + + private void errorMessage(Element element, String format, Object... args) { + processingEnv.getMessager().printMessage(Kind.ERROR, String.format(format, args), element); + } + + Element currentTypeElement = null; + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + if (roundEnv.processingOver()) { + return true; + } + logMessage("Starting round %s %s\n", roundEnv, annotations); + try { + + currentTypeElement = null; + + // First collect all the annotations. + Map annotationMap = new HashMap<>(); + collectAnnotations(roundEnv, annotationMap, HotSpotVMConstant.class, (e, v) -> new VMConfigField(e, v)); + collectAnnotations(roundEnv, annotationMap, HotSpotVMFlag.class, (e, v) -> new VMConfigField(e, v)); + collectAnnotations(roundEnv, annotationMap, HotSpotVMField.class, (e, v) -> new VMConfigField(e, v)); + collectAnnotations(roundEnv, annotationMap, HotSpotVMType.class, (e, v) -> new VMConfigField(e, v)); + collectAnnotations(roundEnv, annotationMap, HotSpotVMValue.class, (e, v) -> new VMConfigField(e, v)); + + if (annotationMap.isEmpty()) { + return true; + } + + logMessage("type element %s\n", currentTypeElement); + createFiles(annotationMap, currentTypeElement); + + } catch (Throwable t) { + reportExceptionThrow(null, t); + } + + return true; + } +} diff -r 2daf39328194 -r db96f9915540 graal/com.oracle.graal.hotspotvmconfig/src/META-INF/services/javax.annotation.processing.Processor --- a/graal/com.oracle.graal.hotspotvmconfig/src/META-INF/services/javax.annotation.processing.Processor Thu Apr 23 21:18:27 2015 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -com.oracle.graal.hotspotvmconfig.HotSpotVMConfigProcessor diff -r 2daf39328194 -r db96f9915540 graal/com.oracle.graal.hotspotvmconfig/src/com/oracle/graal/hotspotvmconfig/HotSpotVMConfigProcessor.java --- a/graal/com.oracle.graal.hotspotvmconfig/src/com/oracle/graal/hotspotvmconfig/HotSpotVMConfigProcessor.java Thu Apr 23 21:18:27 2015 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,423 +0,0 @@ -/* - * Copyright (c) 2014, 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 com.oracle.graal.hotspotvmconfig; - -import java.io.*; -import java.lang.annotation.*; -import java.util.*; -import java.util.Map.Entry; -import java.util.function.*; - -import javax.annotation.processing.*; -import javax.lang.model.*; -import javax.lang.model.element.*; -import javax.tools.Diagnostic.Kind; -import javax.tools.*; - -import com.oracle.graal.compiler.common.*; - -@SupportedAnnotationTypes({"com.oracle.graal.hotspotvmconfig.HotSpotVMConstant", "com.oracle.graal.hotspotvmconfig.HotSpotVMFlag", "com.oracle.graal.hotspotvmconfig.HotSpotVMField", - "com.oracle.graal.hotspotvmconfig.HotSpotVMType", "com.oracle.graal.hotspotvmconfig.HotSpotVMValue"}) -public class HotSpotVMConfigProcessor extends AbstractProcessor { - - public HotSpotVMConfigProcessor() { - } - - @Override - public SourceVersion getSupportedSourceVersion() { - return SourceVersion.latest(); - } - - /** - * Set to true to enable logging to a local file during annotation processing. There's no normal - * channel for any debug messages and debugging annotation processors requires some special - * setup. - */ - private static final boolean DEBUG = false; - - private PrintWriter log; - - /** - * Logging facility for debugging the annotation processor. - */ - - private PrintWriter getLog() { - if (log == null) { - try { - // Create the log file within the generated source directory so it's easy to find. - // /tmp isn't platform independent and java.io.tmpdir can map anywhere, particularly - // on the mac. - FileObject file = processingEnv.getFiler().createResource(StandardLocation.SOURCE_OUTPUT, "", getClass().getSimpleName() + "log"); - log = new PrintWriter(new FileWriter(file.toUri().getPath(), true)); - } catch (IOException e) { - // Do nothing - } - } - return log; - } - - private void logMessage(String format, Object... args) { - if (!DEBUG) { - return; - } - PrintWriter bw = getLog(); - if (bw != null) { - bw.printf(format, args); - bw.flush(); - } - } - - private void logException(Throwable t) { - if (!DEBUG) { - return; - } - PrintWriter bw = getLog(); - if (bw != null) { - t.printStackTrace(bw); - bw.flush(); - } - } - - /** - * Bugs in an annotation processor can cause silent failure so try to report any exception - * throws as errors. - */ - private void reportExceptionThrow(Element element, Throwable t) { - if (element != null) { - logMessage("throw for %s:\n", element); - } - logException(t); - errorMessage(element, "Exception throw during processing: %s %s", t, Arrays.toString(Arrays.copyOf(t.getStackTrace(), 4))); - } - - //@formatter:off - String[] prologue = new String[]{ - "// The normal wrappers CommandLineFlags::boolAt and CommandLineFlags::intxAt skip constant flags", - "static bool boolAt(char* name, bool* value) {", - " Flag* result = Flag::find_flag(name, strlen(name), true, true);", - " if (result == NULL) return false;", - " if (!result->is_bool()) return false;", - " *value = result->get_bool();", - " return true;", - "}", - "", - "static bool intxAt(char* name, intx* value) {", - " Flag* result = Flag::find_flag(name, strlen(name), true, true);", - " if (result == NULL) return false;", - " if (!result->is_intx()) return false;", - " *value = result->get_intx();", - " return true;", - "}", - "", - "#define set_boolean(name, value) vmconfig_oop->bool_field_put(fs.offset(), value)", - "#define set_byte(name, value) vmconfig_oop->byte_field_put(fs.offset(), (jbyte)(value))", - "#define set_short(name, value) vmconfig_oop->short_field_put(fs.offset(), (jshort)(value))", - "#define set_int(name, value) vmconfig_oop->int_field_put(fs.offset(), (int)(value))", - "#define set_long(name, value) vmconfig_oop->long_field_put(fs.offset(), value)", - "#define set_address(name, value) do { set_long(name, (jlong)(value)); } while (0)", - "", - "#define set_optional_boolean_flag(varName, flagName) do { bool flagValue; if (boolAt((char*) flagName, &flagValue)) { set_boolean(varName, flagValue); } } while (0)", - "#define set_optional_int_flag(varName, flagName) do { intx flagValue; if (intxAt((char*) flagName, &flagValue)) { set_int(varName, flagValue); } } while (0)", - "#define set_optional_long_flag(varName, flagName) do { intx flagValue; if (intxAt((char*) flagName, &flagValue)) { set_long(varName, flagValue); } } while (0)", - "", - "void VMStructs::initHotSpotVMConfig(oop vmconfig_oop) {", - " InstanceKlass* vmconfig_klass = InstanceKlass::cast(vmconfig_oop->klass());", - "", - }; - //@formatter:on - - String outputName = "HotSpotVMConfig.inline.hpp"; - String outputDirectory = "hotspot"; - - private void createFiles(Map annotations, Element element) { - - Filer filer = processingEnv.getFiler(); - try (PrintWriter out = createSourceFile(outputDirectory, outputName, filer, element)) { - - for (String line : prologue) { - out.println(line); - } - - Map expectedValues = new HashMap<>(); - for (VMConfigField value : annotations.values()) { - if (!value.optional) { - String key = value.define != null ? value.define : ""; - if (expectedValues.get(key) == null) { - expectedValues.put(key, 1); - } else { - expectedValues.put(key, expectedValues.get(key) + 1); - } - } - } - - out.printf(" int expected = %s;%n", expectedValues.get("")); - for (Entry entry : expectedValues.entrySet()) { - if (entry.getKey().equals("")) { - continue; - } - out.printf("#if %s%n", entry.getKey()); - out.printf(" expected += %s;%n", entry.getValue()); - out.printf("#endif%n"); - } - out.println(" int assigned = 0;"); - out.println(" for (JavaFieldStream fs(vmconfig_klass); !fs.done(); fs.next()) {"); - - Set fieldTypes = new HashSet<>(); - for (VMConfigField key : annotations.values()) { - fieldTypes.add(key.getType()); - } - // For each type of field, generate a switch on the length of the symbol and then do a - // direct compare. In general this reduces each operation to 2 tests plus a string - // compare. Being more perfect than that is probably not worth it. - for (String type : fieldTypes) { - String sigtype = type.equals("boolean") ? "bool" : type; - out.println(" if (fs.signature() == vmSymbols::" + sigtype + "_signature()) {"); - Set lengths = new HashSet<>(); - for (Entry entry : annotations.entrySet()) { - if (entry.getValue().getType().equals(type)) { - lengths.add(entry.getKey().length()); - } - } - out.println(" switch (fs.name()->utf8_length()) {"); - for (int len : lengths) { - out.println(" case " + len + ":"); - for (Entry entry : annotations.entrySet()) { - if (entry.getValue().getType().equals(type) && entry.getKey().length() == len) { - out.println(" if (fs.name()->equals(\"" + entry.getKey() + "\")) {"); - entry.getValue().emit(out); - out.println(" continue;"); - out.println(" }"); - } - } - out.println(" continue;"); - } - out.println(" } // switch"); - out.println(" continue;"); - out.println(" } // if"); - } - out.println(" } // for"); - out.println(" guarantee(assigned == expected, \"Didn't find all fields during init of HotSpotVMConfig. Maybe recompile?\");"); - out.println("}"); - } - } - - protected PrintWriter createSourceFile(String pkg, String relativeName, Filer filer, Element... originatingElements) { - try { - // Ensure Unix line endings to comply with Graal code style guide checked by Checkstyle - FileObject sourceFile = filer.createResource(StandardLocation.SOURCE_OUTPUT, pkg, relativeName, originatingElements); - logMessage("%s\n", sourceFile); - return new PrintWriter(sourceFile.openWriter()) { - - @Override - public void println() { - print("\n"); - } - }; - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - static class VMConfigField { - final String setter; - final String define; - private boolean optional; - final VariableElement field; - - public VMConfigField(VariableElement field, HotSpotVMField value) { - this.field = field; - define = archDefines(value.archs()); - String type = field.asType().toString(); - String name = value.name(); - int i = name.lastIndexOf("::"); - switch (value.get()) { - case OFFSET: - setter = String.format("set_%s(\"%s\", offset_of(%s, %s));", type, field.getSimpleName(), name.substring(0, i), name.substring(i + 2)); - break; - case ADDRESS: - setter = String.format("set_address(\"%s\", &%s);", field.getSimpleName(), name); - break; - case VALUE: - setter = String.format("set_%s(\"%s\", (%s) (intptr_t) %s);", type, field.getSimpleName(), type, name); - break; - default: - throw new GraalInternalError("unexpected type: " + value.get()); - } - } - - public VMConfigField(VariableElement field, HotSpotVMType value) { - this.field = field; - define = null; // ((HotSpotVMType) annotation).archs(); - String type = field.asType().toString(); - setter = String.format("set_%s(\"%s\", sizeof(%s));", type, field.getSimpleName(), value.name()); - } - - public VMConfigField(VariableElement field, HotSpotVMValue value) { - this.field = field; - String[] defines = value.defines(); - int length = defines.length; - if (length != 0) { - for (int i = 0; i < length; i++) { - defines[i] = "defined(" + defines[i] + ")"; - } - define = String.join(" || ", defines); - } else { - define = null; // ((HotSpotVMValue) annotation).archs(); - } - String type = field.asType().toString(); - if (value.get() == HotSpotVMValue.Type.ADDRESS) { - setter = String.format("set_address(\"%s\", %s);", field.getSimpleName(), value.expression()); - } else { - setter = String.format("set_%s(\"%s\", %s);", type, field.getSimpleName(), value.expression()); - } - } - - public VMConfigField(VariableElement field, HotSpotVMConstant value) { - this.field = field; - define = archDefines(value.archs()); - String type = field.asType().toString(); - setter = String.format("set_%s(\"%s\", %s);", type, field.getSimpleName(), value.name()); - } - - public VMConfigField(VariableElement field, HotSpotVMFlag value) { - this.field = field; - define = archDefines(value.archs()); - optional = value.optional(); - String type = field.asType().toString(); - if (value.optional()) { - setter = String.format("set_optional_%s_flag(\"%s\", \"%s\");", type, field.getSimpleName(), value.name()); - } else { - setter = String.format("set_%s(\"%s\", %s);", type, field.getSimpleName(), value.name()); - } - } - - public String getType() { - return field.asType().toString(); - } - - private static String archDefine(String arch) { - switch (arch) { - case "amd64": - return "defined(AMD64)"; - case "sparcv9": - return "(defined(SPARC) && defined(_LP64))"; - case "sparc": - return "defined(SPARC)"; - default: - throw new GraalInternalError("unexpected arch: " + arch); - } - } - - private static String archDefines(String[] archs) { - if (archs == null || archs.length == 0) { - return null; - } - if (archs.length == 1) { - return archDefine(archs[0]); - } - String[] defs = new String[archs.length]; - int i = 0; - for (String arch : archs) { - defs[i++] = archDefine(arch); - } - return String.join(" || ", defs); - } - - public void emit(PrintWriter out) { - if (define != null) { - out.printf("#if %s\n", define); - } - out.printf(" %s%n", setter); - if (!optional) { - out.printf(" assigned++;%n"); - } - if (define != null) { - out.printf("#endif\n"); - } - } - - } - - @SuppressWarnings("unchecked") - private void collectAnnotations(RoundEnvironment roundEnv, Map annotationMap, Class annotationClass, - BiFunction builder) { - for (Element element : roundEnv.getElementsAnnotatedWith(annotationClass)) { - Annotation constant = element.getAnnotation(annotationClass); - if (element.getKind() != ElementKind.FIELD) { - errorMessage(element, "%s annotations may only be on fields", annotationClass.getSimpleName()); - } - if (annotationClass == HotSpotVMValue.class) { - HotSpotVMValue value = (HotSpotVMValue) constant; - if (value.get() == HotSpotVMValue.Type.ADDRESS && !element.asType().toString().equals("long")) { - errorMessage(element, "HotSpotVMValue with get == ADDRESS must be of type long, but found %s", element.asType()); - } - } - if (currentTypeElement == null) { - currentTypeElement = element.getEnclosingElement(); - } else { - if (!currentTypeElement.equals(element.getEnclosingElement())) { - errorMessage(element, "Multiple types encountered. Only HotSpotVMConfig is supported"); - } - } - annotationMap.put(element.getSimpleName().toString(), builder.apply((VariableElement) element, (T) constant)); - } - } - - private void errorMessage(Element element, String format, Object... args) { - processingEnv.getMessager().printMessage(Kind.ERROR, String.format(format, args), element); - } - - Element currentTypeElement = null; - - @Override - public boolean process(Set annotations, RoundEnvironment roundEnv) { - if (roundEnv.processingOver()) { - return true; - } - logMessage("Starting round %s %s\n", roundEnv, annotations); - try { - - currentTypeElement = null; - - // First collect all the annotations. - Map annotationMap = new HashMap<>(); - collectAnnotations(roundEnv, annotationMap, HotSpotVMConstant.class, (e, v) -> new VMConfigField(e, v)); - collectAnnotations(roundEnv, annotationMap, HotSpotVMFlag.class, (e, v) -> new VMConfigField(e, v)); - collectAnnotations(roundEnv, annotationMap, HotSpotVMField.class, (e, v) -> new VMConfigField(e, v)); - collectAnnotations(roundEnv, annotationMap, HotSpotVMType.class, (e, v) -> new VMConfigField(e, v)); - collectAnnotations(roundEnv, annotationMap, HotSpotVMValue.class, (e, v) -> new VMConfigField(e, v)); - - if (annotationMap.isEmpty()) { - return true; - } - - logMessage("type element %s\n", currentTypeElement); - createFiles(annotationMap, currentTypeElement); - - } catch (Throwable t) { - reportExceptionThrow(null, t); - } - - return true; - } -} diff -r 2daf39328194 -r db96f9915540 graal/com.oracle.graal.jtt/src/com/oracle/graal/jtt/optimize/SchedulingBug_01.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.jtt/src/com/oracle/graal/jtt/optimize/SchedulingBug_01.java Thu Apr 23 22:09:27 2015 -0700 @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2015, 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 com.oracle.graal.jtt.optimize; + +import org.junit.*; + +import com.oracle.graal.jtt.*; + +/* + */ +public class SchedulingBug_01 extends JTTTest { + + private static class VolatileBoxHolder { + volatile Integer box; + } + + public static int test(VolatileBoxHolder a, VolatileBoxHolder b) { + int value = a.box; + int result = 0; + if (b.box != null) { + result += value; + } + return result + value; + } + + @Test + public void run0() throws Throwable { + runTest("test", new VolatileBoxHolder(), new VolatileBoxHolder()); + } + +} diff -r 2daf39328194 -r db96f9915540 graal/com.oracle.graal.loop/src/com/oracle/graal/loop/LoopEx.java --- a/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/LoopEx.java Thu Apr 23 21:18:27 2015 -0700 +++ b/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/LoopEx.java Thu Apr 23 22:09:27 2015 -0700 @@ -45,7 +45,7 @@ private final Loop loop; private LoopFragmentInside inside; private LoopFragmentWhole whole; - private CountedLoopInfo counted; // TODO (gd) detect + private CountedLoopInfo counted; private LoopsData data; private Map ivs; @@ -198,9 +198,6 @@ limit = lessThan.getY(); } } - if (iv != null && iv.isConstantStride() && iv.constantStride() != 1 && !(ifTest instanceof IntegerLessThanNode)) { - return false; - } if (condition == null) { return false; } @@ -212,6 +209,9 @@ case EQ: return false; case NE: { + if (!iv.isConstantStride() || Math.abs(iv.constantStride()) != 1) { + return false; + } IntegerStamp initStamp = (IntegerStamp) iv.initNode().stamp(); IntegerStamp limitStamp = (IntegerStamp) limit.stamp(); if (iv.direction() == Direction.Up) { diff -r 2daf39328194 -r db96f9915540 graal/com.oracle.graal.nodeinfo/src/com/oracle/graal/nodeinfo/StructuralInput.java --- a/graal/com.oracle.graal.nodeinfo/src/com/oracle/graal/nodeinfo/StructuralInput.java Thu Apr 23 21:18:27 2015 -0700 +++ b/graal/com.oracle.graal.nodeinfo/src/com/oracle/graal/nodeinfo/StructuralInput.java Thu Apr 23 22:09:27 2015 -0700 @@ -35,6 +35,7 @@ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) + @Inherited public @interface MarkerType { InputType value(); } diff -r 2daf39328194 -r db96f9915540 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GraphDecoder.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GraphDecoder.java Thu Apr 23 21:18:27 2015 -0700 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GraphDecoder.java Thu Apr 23 22:09:27 2015 -0700 @@ -303,7 +303,6 @@ */ FixedNode detectLoopsStart = startNode.predecessor() != null ? (FixedNode) startNode.predecessor() : startNode; cleanupGraph(methodScope, start); - Debug.dump(methodScope.graph, "Before loop detection"); detectLoops(methodScope.graph, detectLoopsStart); } } @@ -388,11 +387,12 @@ resultScope = new LoopScope(loopScope, loopScope.loopDepth + 1, 0, mergeOrderId, Arrays.copyOf(loopScope.createdNodes, loopScope.createdNodes.length), // methodScope.loopExplosion != LoopExplosionKind.NONE ? new ArrayDeque<>() : null, // methodScope.loopExplosion == LoopExplosionKind.MERGE_EXPLODE ? new HashMap<>() : null); + phiInputScope = resultScope; phiNodeScope = resultScope; - registerNode(phiInputScope, mergeOrderId, null, true, true); - phiInputScope.nodesToProcess.clear(mergeOrderId); - phiNodeScope.nodesToProcess.set(mergeOrderId); + registerNode(loopScope, mergeOrderId, null, true, true); + loopScope.nodesToProcess.clear(mergeOrderId); + resultScope.nodesToProcess.set(mergeOrderId); } } @@ -680,7 +680,7 @@ * not processed yet when processing the loop body, we need to create all phi functions * upfront. */ - boolean lazyPhi = !(merge instanceof LoopBeginNode) || methodScope.loopExplosion != LoopExplosionKind.NONE; + boolean lazyPhi = allowLazyPhis() && (!(merge instanceof LoopBeginNode) || methodScope.loopExplosion != LoopExplosionKind.NONE); int numPhis = methodScope.reader.getUVInt(); for (int i = 0; i < numPhis; i++) { int phiInputOrderId = readOrderId(methodScope); @@ -715,6 +715,11 @@ } } + protected boolean allowLazyPhis() { + /* We need to exactly reproduce the encoded graph, including unnecessary phi functions. */ + return false; + } + protected Node instantiateNode(MethodScope methodScope, int nodeOrderId) { methodScope.reader.setByteIndex(methodScope.encodedGraph.nodeStartOffsets[nodeOrderId]); NodeClass nodeClass = methodScope.encodedGraph.getNodeClasses()[methodScope.reader.getUVInt()]; @@ -796,7 +801,7 @@ /* Allow subclasses to canonicalize and intercept nodes. */ node = handleFloatingNodeBeforeAdd(methodScope, loopScope, node); if (!node.isAlive()) { - node = methodScope.graph.addOrUnique(node); + node = addFloatingNode(methodScope, node); } node = handleFloatingNodeAfterAdd(methodScope, loopScope, node); } @@ -804,6 +809,14 @@ return node; } + protected Node addFloatingNode(MethodScope methodScope, Node node) { + /* + * We want to exactly reproduce the encoded graph. Even though nodes should be unique in the + * encoded graph, this is not always guaranteed. + */ + return methodScope.graph.addWithoutUnique(node); + } + /** * Decodes a non-fixed node, but does not do any post-processing and does not register it. */ @@ -1020,7 +1033,6 @@ } } - Debug.dump(currentGraph, "After loops detected"); insertLoopEnds(currentGraph, startInstruction); } @@ -1145,7 +1157,6 @@ protected void cleanupGraph(MethodScope methodScope, Graph.Mark start) { assert verifyEdges(methodScope); - Debug.dump(methodScope.graph, "Before removing redundant merges"); for (Node node : methodScope.graph.getNewNodes(start)) { if (node instanceof MergeNode) { MergeNode mergeNode = (MergeNode) node; @@ -1155,7 +1166,6 @@ } } - Debug.dump(methodScope.graph, "Before removing redundant begins"); for (Node node : methodScope.graph.getNewNodes(start)) { if (node instanceof BeginNode || node instanceof KillingBeginNode) { if (!(node.predecessor() instanceof ControlSplitNode) && node.hasNoUsages()) { @@ -1165,7 +1175,6 @@ } } - Debug.dump(methodScope.graph, "Before removing unused non-fixed nodes"); for (Node node : methodScope.graph.getNewNodes(start)) { if (!(node instanceof FixedNode) && node.hasNoUsages()) { GraphUtil.killCFG(node); diff -r 2daf39328194 -r db96f9915540 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GraphEncoder.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GraphEncoder.java Thu Apr 23 21:18:27 2015 -0700 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GraphEncoder.java Thu Apr 23 22:09:27 2015 -0700 @@ -27,6 +27,7 @@ import com.oracle.graal.api.code.*; import com.oracle.graal.compiler.common.*; import com.oracle.graal.compiler.common.util.*; +import com.oracle.graal.debug.*; import com.oracle.graal.graph.*; import com.oracle.graal.graph.iterators.*; import com.oracle.graal.nodes.StructuredGraph.AllowAssumptions; @@ -388,7 +389,15 @@ decoder.decode(decodedGraph, encodedGraph); decodedGraph.verify(); - GraphComparison.verifyGraphsEqual(originalGraph, decodedGraph); + try { + GraphComparison.verifyGraphsEqual(originalGraph, decodedGraph); + } catch (Throwable ex) { + try (Debug.Scope scope = Debug.scope("GraphEncoder")) { + Debug.dump(originalGraph, "Original Graph"); + Debug.dump(decodedGraph, "Decoded Graph"); + } + throw ex; + } return true; } } @@ -483,19 +492,7 @@ assert !actualIter.hasNext(); } - protected static void verifyNodeEqual(Node e, Node actualNode, NodeMap nodeMapping, Deque> workList, boolean ignoreEndNode) { - Node expectedNode = e; - if (expectedNode instanceof PhiNode) { - /* - * The input graph can contain unnecessary (eliminatable) phi functions. Such phis are - * not re-created during decoding, so we need to simplify the expected node too. - */ - Node singleValue = ((PhiNode) expectedNode).singleValue(); - if (singleValue != null && singleValue != PhiNode.MULTIPLE_VALUES) { - expectedNode = singleValue; - } - } - + protected static void verifyNodeEqual(Node expectedNode, Node actualNode, NodeMap nodeMapping, Deque> workList, boolean ignoreEndNode) { assert expectedNode.getClass() == actualNode.getClass(); if (ignoreEndNode && expectedNode instanceof EndNode) { return; diff -r 2daf39328194 -r db96f9915540 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/SimplifyingGraphDecoder.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/SimplifyingGraphDecoder.java Thu Apr 23 21:18:27 2015 -0700 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/SimplifyingGraphDecoder.java Thu Apr 23 22:09:27 2015 -0700 @@ -82,6 +82,15 @@ } @Override + protected boolean allowLazyPhis() { + /* + * We do not need to exactly reproduce the encoded graph, so we want to avoid unnecessary + * phi functions. + */ + return true; + } + + @Override protected void simplifyFixedNode(MethodScope methodScope, LoopScope loopScope, int nodeOrderId, FixedNode node) { if (node instanceof IfNode) { IfNode ifNode = (IfNode) node; @@ -171,4 +180,13 @@ } return node; } + + @Override + protected Node addFloatingNode(MethodScope methodScope, Node node) { + /* + * In contrast to the base class implementation, we do not need to exactly reproduce the + * encoded graph. Since we do canonicalization, we also want nodes to be unique. + */ + return methodScope.graph.addOrUnique(node); + } } diff -r 2daf39328194 -r db96f9915540 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/LoadFieldNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/LoadFieldNode.java Thu Apr 23 21:18:27 2015 -0700 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/LoadFieldNode.java Thu Apr 23 22:09:27 2015 -0700 @@ -70,9 +70,11 @@ if (constant != null) { return constant; } - PhiNode phi = asPhi(metaAccess, constantReflection, forObject); - if (phi != null) { - return phi; + if (tool.allUsagesAvailable()) { + PhiNode phi = asPhi(metaAccess, constantReflection, forObject); + if (phi != null) { + return phi; + } } } if (!isStatic() && forObject.isNullConstant()) { diff -r 2daf39328194 -r db96f9915540 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/util/GraphUtil.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/util/GraphUtil.java Thu Apr 23 21:18:27 2015 -0700 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/util/GraphUtil.java Thu Apr 23 22:09:27 2015 -0700 @@ -238,14 +238,28 @@ * */ public static void normalizeLoops(StructuredGraph graph) { + boolean loopRemoved = false; for (LoopBeginNode begin : graph.getNodes(LoopBeginNode.TYPE)) { if (begin.loopEnds().isEmpty()) { assert begin.forwardEndCount() == 1; graph.reduceDegenerateLoopBegin(begin); + loopRemoved = true; } else { normalizeLoopBegin(begin); } } + + if (loopRemoved) { + /* + * Removing a degenerated loop can make non-loop phi functions unnecessary. Therefore, + * we re-check all phi functions and remove redundant ones. + */ + for (Node node : graph.getNodes()) { + if (node instanceof PhiNode) { + checkRedundantPhi((PhiNode) node); + } + } + } } private static void normalizeLoopBegin(LoopBeginNode begin) { diff -r 2daf39328194 -r db96f9915540 graal/com.oracle.graal.options.processor/src/META-INF/services/javax.annotation.processing.Processor --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.options.processor/src/META-INF/services/javax.annotation.processing.Processor Thu Apr 23 22:09:27 2015 -0700 @@ -0,0 +1,1 @@ +com.oracle.graal.options.processor.OptionProcessor \ No newline at end of file diff -r 2daf39328194 -r db96f9915540 graal/com.oracle.graal.options.processor/src/com/oracle/graal/options/processor/OptionProcessor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.options.processor/src/com/oracle/graal/options/processor/OptionProcessor.java Thu Apr 23 22:09:27 2015 -0700 @@ -0,0 +1,330 @@ +/* + * Copyright (c) 2013, 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 com.oracle.graal.options.processor; + +import java.io.*; +import java.util.*; + +import javax.annotation.processing.*; +import javax.lang.model.*; +import javax.lang.model.element.*; +import javax.lang.model.type.*; +import javax.lang.model.util.*; +import javax.tools.Diagnostic.Kind; +import javax.tools.*; + +import com.oracle.graal.options.*; + +/** + * Processes static fields annotated with {@link Option}. An {@link Options} service is generated + * for each top level class containing at least one such field. These service objects can be + * retrieved as follows: + * + *
+ * ServiceLoader<Options> sl = ServiceLoader.load(Options.class);
+ * for (Options opts : sl) {
+ *     for (OptionDescriptor desc : sl) {
+ *         // use desc
+ *     }
+ * }
+ * 
+ */ +@SupportedAnnotationTypes({"com.oracle.graal.options.Option"}) +public class OptionProcessor extends AbstractProcessor { + + @Override + public SourceVersion getSupportedSourceVersion() { + return SourceVersion.latest(); + } + + private final Set processed = new HashSet<>(); + + private void processElement(Element element, OptionsInfo info) { + + if (!element.getModifiers().contains(Modifier.STATIC)) { + processingEnv.getMessager().printMessage(Kind.ERROR, "Option field must be static", element); + return; + } + + Option annotation = element.getAnnotation(Option.class); + assert annotation != null; + assert element instanceof VariableElement; + assert element.getKind() == ElementKind.FIELD; + VariableElement field = (VariableElement) element; + String fieldName = field.getSimpleName().toString(); + + Elements elements = processingEnv.getElementUtils(); + Types types = processingEnv.getTypeUtils(); + + TypeMirror fieldType = field.asType(); + if (fieldType.getKind() != TypeKind.DECLARED) { + processingEnv.getMessager().printMessage(Kind.ERROR, "Option field must be of type " + OptionValue.class.getName(), element); + return; + } + DeclaredType declaredFieldType = (DeclaredType) fieldType; + + TypeMirror optionValueType = elements.getTypeElement(OptionValue.class.getName()).asType(); + if (!types.isSubtype(fieldType, types.erasure(optionValueType))) { + String msg = String.format("Option field type %s is not a subclass of %s", fieldType, optionValueType); + processingEnv.getMessager().printMessage(Kind.ERROR, msg, element); + return; + } + + if (!field.getModifiers().contains(Modifier.STATIC)) { + processingEnv.getMessager().printMessage(Kind.ERROR, "Option field must be static", element); + return; + } + + String help = annotation.help(); + if (help.length() != 0) { + char firstChar = help.charAt(0); + if (!Character.isUpperCase(firstChar)) { + processingEnv.getMessager().printMessage(Kind.ERROR, "Option help text must start with upper case letter", element); + return; + } + } + + String optionName = annotation.name(); + if (optionName.equals("")) { + optionName = fieldName; + } + + DeclaredType declaredOptionValueType = declaredFieldType; + while (!types.isSameType(types.erasure(declaredOptionValueType), types.erasure(optionValueType))) { + List directSupertypes = types.directSupertypes(declaredFieldType); + assert !directSupertypes.isEmpty(); + declaredOptionValueType = (DeclaredType) directSupertypes.get(0); + } + + assert !declaredOptionValueType.getTypeArguments().isEmpty(); + String optionType = declaredOptionValueType.getTypeArguments().get(0).toString(); + if (optionType.startsWith("java.lang.")) { + optionType = optionType.substring("java.lang.".length()); + } + + Element enclosing = element.getEnclosingElement(); + String declaringClass = ""; + String separator = ""; + Set originatingElementsList = info.originatingElements; + originatingElementsList.add(field); + while (enclosing != null) { + if (enclosing.getKind() == ElementKind.CLASS || enclosing.getKind() == ElementKind.INTERFACE) { + if (enclosing.getModifiers().contains(Modifier.PRIVATE)) { + String msg = String.format("Option field cannot be declared in a private %s %s", enclosing.getKind().name().toLowerCase(), enclosing); + processingEnv.getMessager().printMessage(Kind.ERROR, msg, element); + return; + } + originatingElementsList.add(enclosing); + declaringClass = enclosing.getSimpleName() + separator + declaringClass; + separator = "."; + } else { + assert enclosing.getKind() == ElementKind.PACKAGE; + } + enclosing = enclosing.getEnclosingElement(); + } + + info.options.add(new OptionInfo(optionName, help, optionType, declaringClass, field)); + } + + private void createFiles(OptionsInfo info) { + String pkg = ((PackageElement) info.topDeclaringType.getEnclosingElement()).getQualifiedName().toString(); + Name topDeclaringClass = info.topDeclaringType.getSimpleName(); + + String optionsClassName = topDeclaringClass + "_" + Options.class.getSimpleName(); + Element[] originatingElements = info.originatingElements.toArray(new Element[info.originatingElements.size()]); + + Filer filer = processingEnv.getFiler(); + try (PrintWriter out = createSourceFile(pkg, optionsClassName, filer, originatingElements)) { + + out.println("// CheckStyle: stop header check"); + out.println("// GENERATED CONTENT - DO NOT EDIT"); + out.println("// Source: " + topDeclaringClass + ".java"); + out.println("package " + pkg + ";"); + out.println(""); + out.println("import java.util.*;"); + out.println("import " + Options.class.getPackage().getName() + ".*;"); + out.println(""); + out.println("public class " + optionsClassName + " implements " + Options.class.getSimpleName() + " {"); + out.println(" @Override"); + String desc = OptionDescriptor.class.getSimpleName(); + out.println(" public Iterator<" + desc + "> iterator() {"); + out.println(" // CheckStyle: stop line length check"); + out.println(" List<" + desc + "> options = Arrays.asList("); + + boolean needPrivateFieldAccessor = false; + int i = 0; + Collections.sort(info.options); + for (OptionInfo option : info.options) { + String optionValue; + if (option.field.getModifiers().contains(Modifier.PRIVATE)) { + needPrivateFieldAccessor = true; + optionValue = "field(" + option.declaringClass + ".class, \"" + option.field.getSimpleName() + "\")"; + } else { + optionValue = option.declaringClass + "." + option.field.getSimpleName(); + } + String name = option.name; + String type = option.type; + String help = option.help; + String declaringClass = option.declaringClass; + Name fieldName = option.field.getSimpleName(); + String comma = i == info.options.size() - 1 ? "" : ","; + out.printf(" new %s(\"%s\", %s.class, \"%s\", %s.class, \"%s\", %s)%s\n", desc, name, type, help, declaringClass, fieldName, optionValue, comma); + i++; + } + out.println(" );"); + out.println(" // CheckStyle: resume line length check"); + out.println(" return options.iterator();"); + out.println(" }"); + if (needPrivateFieldAccessor) { + out.println(" private static " + OptionValue.class.getSimpleName() + " field(Class declaringClass, String fieldName) {"); + out.println(" try {"); + out.println(" java.lang.reflect.Field field = declaringClass.getDeclaredField(fieldName);"); + out.println(" field.setAccessible(true);"); + out.println(" return (" + OptionValue.class.getSimpleName() + ") field.get(null);"); + out.println(" } catch (Exception e) {"); + out.println(" throw (InternalError) new InternalError().initCause(e);"); + out.println(" }"); + out.println(" }"); + } + out.println("}"); + } + + try { + createProviderFile(pkg, optionsClassName, originatingElements); + } catch (IOException e) { + processingEnv.getMessager().printMessage(Kind.ERROR, e.getMessage(), info.topDeclaringType); + } + } + + private void createProviderFile(String pkg, String providerClassName, Element... originatingElements) throws IOException { + String filename = "META-INF/providers/" + pkg + "." + providerClassName; + FileObject file = processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", filename, originatingElements); + PrintWriter writer = new PrintWriter(new OutputStreamWriter(file.openOutputStream(), "UTF-8")); + writer.println(Options.class.getName()); + writer.close(); + } + + protected PrintWriter createSourceFile(String pkg, String relativeName, Filer filer, Element... originatingElements) { + try { + // Ensure Unix line endings to comply with Graal code style guide checked by Checkstyle + JavaFileObject sourceFile = filer.createSourceFile(pkg + "." + relativeName, originatingElements); + return new PrintWriter(sourceFile.openWriter()) { + + @Override + public void println() { + print("\n"); + } + }; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + static class OptionInfo implements Comparable { + + final String name; + final String help; + final String type; + final String declaringClass; + final VariableElement field; + + public OptionInfo(String name, String help, String type, String declaringClass, VariableElement field) { + this.name = name; + this.help = help; + this.type = type; + this.declaringClass = declaringClass; + this.field = field; + } + + @Override + public int compareTo(OptionInfo other) { + return name.compareTo(other.name); + } + + @Override + public String toString() { + return declaringClass + "." + field; + } + } + + static class OptionsInfo { + + final Element topDeclaringType; + final List options = new ArrayList<>(); + final Set originatingElements = new HashSet<>(); + + public OptionsInfo(Element topDeclaringType) { + this.topDeclaringType = topDeclaringType; + } + } + + private static Element topDeclaringType(Element element) { + Element enclosing = element.getEnclosingElement(); + if (enclosing == null || enclosing.getKind() == ElementKind.PACKAGE) { + assert element.getKind() == ElementKind.CLASS || element.getKind() == ElementKind.INTERFACE; + return element; + } + return topDeclaringType(enclosing); + } + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + if (roundEnv.processingOver()) { + return true; + } + + Map map = new HashMap<>(); + for (Element element : roundEnv.getElementsAnnotatedWith(Option.class)) { + if (!processed.contains(element)) { + processed.add(element); + Element topDeclaringType = topDeclaringType(element); + OptionsInfo options = map.get(topDeclaringType); + if (options == null) { + options = new OptionsInfo(topDeclaringType); + map.put(topDeclaringType, options); + } + processElement(element, options); + } + } + + boolean ok = true; + Map uniqueness = new HashMap<>(); + for (OptionsInfo info : map.values()) { + for (OptionInfo option : info.options) { + OptionInfo conflict = uniqueness.put(option.name, option); + if (conflict != null) { + processingEnv.getMessager().printMessage(Kind.ERROR, "Duplicate option names for " + option + " and " + conflict, option.field); + ok = false; + } + } + } + + if (ok) { + for (OptionsInfo info : map.values()) { + createFiles(info); + } + } + + return true; + } +} diff -r 2daf39328194 -r db96f9915540 graal/com.oracle.graal.options/src/META-INF/services/javax.annotation.processing.Processor --- a/graal/com.oracle.graal.options/src/META-INF/services/javax.annotation.processing.Processor Thu Apr 23 21:18:27 2015 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -com.oracle.graal.options.OptionProcessor diff -r 2daf39328194 -r db96f9915540 graal/com.oracle.graal.options/src/com/oracle/graal/options/Option.java --- a/graal/com.oracle.graal.options/src/com/oracle/graal/options/Option.java Thu Apr 23 21:18:27 2015 -0700 +++ b/graal/com.oracle.graal.options/src/com/oracle/graal/options/Option.java Thu Apr 23 22:09:27 2015 -0700 @@ -28,7 +28,6 @@ * Describes the attributes of an option whose {@link OptionValue value} is in a static field * annotated by this annotation type. * - * @see OptionProcessor * @see OptionDescriptor */ @Retention(RetentionPolicy.CLASS) diff -r 2daf39328194 -r db96f9915540 graal/com.oracle.graal.options/src/com/oracle/graal/options/OptionProcessor.java --- a/graal/com.oracle.graal.options/src/com/oracle/graal/options/OptionProcessor.java Thu Apr 23 21:18:27 2015 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,328 +0,0 @@ -/* - * Copyright (c) 2013, 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 com.oracle.graal.options; - -import java.io.*; -import java.util.*; - -import javax.annotation.processing.*; -import javax.lang.model.*; -import javax.lang.model.element.*; -import javax.lang.model.type.*; -import javax.lang.model.util.*; -import javax.tools.Diagnostic.Kind; -import javax.tools.*; - -/** - * Processes static fields annotated with {@link Option}. An {@link Options} service is generated - * for each top level class containing at least one such field. These service objects can be - * retrieved as follows: - * - *
- * ServiceLoader<Options> sl = ServiceLoader.load(Options.class);
- * for (Options opts : sl) {
- *     for (OptionDescriptor desc : sl) {
- *         // use desc
- *     }
- * }
- * 
- */ -@SupportedAnnotationTypes({"com.oracle.graal.options.Option"}) -public class OptionProcessor extends AbstractProcessor { - - @Override - public SourceVersion getSupportedSourceVersion() { - return SourceVersion.latest(); - } - - private final Set processed = new HashSet<>(); - - private void processElement(Element element, OptionsInfo info) { - - if (!element.getModifiers().contains(Modifier.STATIC)) { - processingEnv.getMessager().printMessage(Kind.ERROR, "Option field must be static", element); - return; - } - - Option annotation = element.getAnnotation(Option.class); - assert annotation != null; - assert element instanceof VariableElement; - assert element.getKind() == ElementKind.FIELD; - VariableElement field = (VariableElement) element; - String fieldName = field.getSimpleName().toString(); - - Elements elements = processingEnv.getElementUtils(); - Types types = processingEnv.getTypeUtils(); - - TypeMirror fieldType = field.asType(); - if (fieldType.getKind() != TypeKind.DECLARED) { - processingEnv.getMessager().printMessage(Kind.ERROR, "Option field must be of type " + OptionValue.class.getName(), element); - return; - } - DeclaredType declaredFieldType = (DeclaredType) fieldType; - - TypeMirror optionValueType = elements.getTypeElement(OptionValue.class.getName()).asType(); - if (!types.isSubtype(fieldType, types.erasure(optionValueType))) { - String msg = String.format("Option field type %s is not a subclass of %s", fieldType, optionValueType); - processingEnv.getMessager().printMessage(Kind.ERROR, msg, element); - return; - } - - if (!field.getModifiers().contains(Modifier.STATIC)) { - processingEnv.getMessager().printMessage(Kind.ERROR, "Option field must be static", element); - return; - } - - String help = annotation.help(); - if (help.length() != 0) { - char firstChar = help.charAt(0); - if (!Character.isUpperCase(firstChar)) { - processingEnv.getMessager().printMessage(Kind.ERROR, "Option help text must start with upper case letter", element); - return; - } - } - - String optionName = annotation.name(); - if (optionName.equals("")) { - optionName = fieldName; - } - - DeclaredType declaredOptionValueType = declaredFieldType; - while (!types.isSameType(types.erasure(declaredOptionValueType), types.erasure(optionValueType))) { - List directSupertypes = types.directSupertypes(declaredFieldType); - assert !directSupertypes.isEmpty(); - declaredOptionValueType = (DeclaredType) directSupertypes.get(0); - } - - assert !declaredOptionValueType.getTypeArguments().isEmpty(); - String optionType = declaredOptionValueType.getTypeArguments().get(0).toString(); - if (optionType.startsWith("java.lang.")) { - optionType = optionType.substring("java.lang.".length()); - } - - Element enclosing = element.getEnclosingElement(); - String declaringClass = ""; - String separator = ""; - Set originatingElementsList = info.originatingElements; - originatingElementsList.add(field); - while (enclosing != null) { - if (enclosing.getKind() == ElementKind.CLASS || enclosing.getKind() == ElementKind.INTERFACE) { - if (enclosing.getModifiers().contains(Modifier.PRIVATE)) { - String msg = String.format("Option field cannot be declared in a private %s %s", enclosing.getKind().name().toLowerCase(), enclosing); - processingEnv.getMessager().printMessage(Kind.ERROR, msg, element); - return; - } - originatingElementsList.add(enclosing); - declaringClass = enclosing.getSimpleName() + separator + declaringClass; - separator = "."; - } else { - assert enclosing.getKind() == ElementKind.PACKAGE; - } - enclosing = enclosing.getEnclosingElement(); - } - - info.options.add(new OptionInfo(optionName, help, optionType, declaringClass, field)); - } - - private void createFiles(OptionsInfo info) { - String pkg = ((PackageElement) info.topDeclaringType.getEnclosingElement()).getQualifiedName().toString(); - Name topDeclaringClass = info.topDeclaringType.getSimpleName(); - - String optionsClassName = topDeclaringClass + "_" + Options.class.getSimpleName(); - Element[] originatingElements = info.originatingElements.toArray(new Element[info.originatingElements.size()]); - - Filer filer = processingEnv.getFiler(); - try (PrintWriter out = createSourceFile(pkg, optionsClassName, filer, originatingElements)) { - - out.println("// CheckStyle: stop header check"); - out.println("// GENERATED CONTENT - DO NOT EDIT"); - out.println("// Source: " + topDeclaringClass + ".java"); - out.println("package " + pkg + ";"); - out.println(""); - out.println("import java.util.*;"); - out.println("import " + Options.class.getPackage().getName() + ".*;"); - out.println(""); - out.println("public class " + optionsClassName + " implements " + Options.class.getSimpleName() + " {"); - out.println(" @Override"); - String desc = OptionDescriptor.class.getSimpleName(); - out.println(" public Iterator<" + desc + "> iterator() {"); - out.println(" // CheckStyle: stop line length check"); - out.println(" List<" + desc + "> options = Arrays.asList("); - - boolean needPrivateFieldAccessor = false; - int i = 0; - Collections.sort(info.options); - for (OptionInfo option : info.options) { - String optionValue; - if (option.field.getModifiers().contains(Modifier.PRIVATE)) { - needPrivateFieldAccessor = true; - optionValue = "field(" + option.declaringClass + ".class, \"" + option.field.getSimpleName() + "\")"; - } else { - optionValue = option.declaringClass + "." + option.field.getSimpleName(); - } - String name = option.name; - String type = option.type; - String help = option.help; - String declaringClass = option.declaringClass; - Name fieldName = option.field.getSimpleName(); - String comma = i == info.options.size() - 1 ? "" : ","; - out.printf(" new %s(\"%s\", %s.class, \"%s\", %s.class, \"%s\", %s)%s\n", desc, name, type, help, declaringClass, fieldName, optionValue, comma); - i++; - } - out.println(" );"); - out.println(" // CheckStyle: resume line length check"); - out.println(" return options.iterator();"); - out.println(" }"); - if (needPrivateFieldAccessor) { - out.println(" private static " + OptionValue.class.getSimpleName() + " field(Class declaringClass, String fieldName) {"); - out.println(" try {"); - out.println(" java.lang.reflect.Field field = declaringClass.getDeclaredField(fieldName);"); - out.println(" field.setAccessible(true);"); - out.println(" return (" + OptionValue.class.getSimpleName() + ") field.get(null);"); - out.println(" } catch (Exception e) {"); - out.println(" throw (InternalError) new InternalError().initCause(e);"); - out.println(" }"); - out.println(" }"); - } - out.println("}"); - } - - try { - createProviderFile(pkg, optionsClassName, originatingElements); - } catch (IOException e) { - processingEnv.getMessager().printMessage(Kind.ERROR, e.getMessage(), info.topDeclaringType); - } - } - - private void createProviderFile(String pkg, String providerClassName, Element... originatingElements) throws IOException { - String filename = "META-INF/providers/" + pkg + "." + providerClassName; - FileObject file = processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", filename, originatingElements); - PrintWriter writer = new PrintWriter(new OutputStreamWriter(file.openOutputStream(), "UTF-8")); - writer.println(Options.class.getName()); - writer.close(); - } - - protected PrintWriter createSourceFile(String pkg, String relativeName, Filer filer, Element... originatingElements) { - try { - // Ensure Unix line endings to comply with Graal code style guide checked by Checkstyle - JavaFileObject sourceFile = filer.createSourceFile(pkg + "." + relativeName, originatingElements); - return new PrintWriter(sourceFile.openWriter()) { - - @Override - public void println() { - print("\n"); - } - }; - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - static class OptionInfo implements Comparable { - - final String name; - final String help; - final String type; - final String declaringClass; - final VariableElement field; - - public OptionInfo(String name, String help, String type, String declaringClass, VariableElement field) { - this.name = name; - this.help = help; - this.type = type; - this.declaringClass = declaringClass; - this.field = field; - } - - @Override - public int compareTo(OptionInfo other) { - return name.compareTo(other.name); - } - - @Override - public String toString() { - return declaringClass + "." + field; - } - } - - static class OptionsInfo { - - final Element topDeclaringType; - final List options = new ArrayList<>(); - final Set originatingElements = new HashSet<>(); - - public OptionsInfo(Element topDeclaringType) { - this.topDeclaringType = topDeclaringType; - } - } - - private static Element topDeclaringType(Element element) { - Element enclosing = element.getEnclosingElement(); - if (enclosing == null || enclosing.getKind() == ElementKind.PACKAGE) { - assert element.getKind() == ElementKind.CLASS || element.getKind() == ElementKind.INTERFACE; - return element; - } - return topDeclaringType(enclosing); - } - - @Override - public boolean process(Set annotations, RoundEnvironment roundEnv) { - if (roundEnv.processingOver()) { - return true; - } - - Map map = new HashMap<>(); - for (Element element : roundEnv.getElementsAnnotatedWith(Option.class)) { - if (!processed.contains(element)) { - processed.add(element); - Element topDeclaringType = topDeclaringType(element); - OptionsInfo options = map.get(topDeclaringType); - if (options == null) { - options = new OptionsInfo(topDeclaringType); - map.put(topDeclaringType, options); - } - processElement(element, options); - } - } - - boolean ok = true; - Map uniqueness = new HashMap<>(); - for (OptionsInfo info : map.values()) { - for (OptionInfo option : info.options) { - OptionInfo conflict = uniqueness.put(option.name, option); - if (conflict != null) { - processingEnv.getMessager().printMessage(Kind.ERROR, "Duplicate option names for " + option + " and " + conflict, option.field); - ok = false; - } - } - } - - if (ok) { - for (OptionsInfo info : map.values()) { - createFiles(info); - } - } - - return true; - } -} diff -r 2daf39328194 -r db96f9915540 graal/com.oracle.graal.phases/src/com/oracle/graal/phases/schedule/SchedulePhase.java --- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/schedule/SchedulePhase.java Thu Apr 23 21:18:27 2015 -0700 +++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/schedule/SchedulePhase.java Thu Apr 23 22:09:27 2015 -0700 @@ -313,8 +313,14 @@ } } FixedNode endNode = b.getEndNode(); + FixedNode fixedEndNode = null; + if (isFixedEnd(endNode)) { + // Only if the end node is either a control split or an end node, we need to force it to + // be the last node in the schedule. + fixedEndNode = endNode; + } for (Node n : earliestSorting) { - if (n != endNode) { + if (n != fixedEndNode) { if (n instanceof FixedNode) { assert nodeMap.get(n) == b; checkWatchList(b, nodeMap, unprocessed, result, watchList, n); @@ -337,11 +343,11 @@ assert nodeMap.get(n) == b; assert !(n instanceof FixedNode); if (unprocessed.isMarked(n)) { - sortIntoList(n, b, result, nodeMap, unprocessed, endNode); + sortIntoList(n, b, result, nodeMap, unprocessed, fixedEndNode); } } - if (unprocessed.isMarked(endNode)) { + if (endNode != null && unprocessed.isMarked(endNode)) { sortIntoList(endNode, b, result, nodeMap, unprocessed, null); } @@ -491,8 +497,10 @@ // Start analysis with control flow ends. for (Block b : cfg.postOrder()) { FixedNode endNode = b.getEndNode(); - stack.push(endNode); - nodeToBlock.set(endNode, b); + if (isFixedEnd(endNode)) { + stack.push(endNode); + nodeToBlock.set(endNode, b); + } } processStack(cfg, blockToNodes, nodeToBlock, visited, floatingReads, stack); @@ -539,8 +547,10 @@ // Add end nodes as the last nodes in each block. for (Block b : cfg.getBlocks()) { FixedNode endNode = b.getEndNode(); - if (endNode != b.getBeginNode()) { - addNode(blockToNodes, b, endNode); + if (isFixedEnd(endNode)) { + if (endNode != b.getBeginNode()) { + addNode(blockToNodes, b, endNode); + } } } @@ -555,6 +565,10 @@ assert MemoryScheduleVerification.check(cfg.getStartBlock(), blockToNodes); } + private static boolean isFixedEnd(FixedNode endNode) { + return endNode instanceof ControlSplitNode || endNode instanceof ControlSinkNode || endNode instanceof AbstractEndNode; + } + private static void resortEarliestWithinBlock(Block b, BlockMap> blockToNodes, NodeMap nodeToBlock, NodeBitMap unprocessed) { ArrayList watchList = new ArrayList<>(); List oldList = blockToNodes.get(b); @@ -583,18 +597,19 @@ for (int i = 1; i < oldList.size(); ++i) { Node n = oldList.get(i); if (unprocessed.isMarked(n)) { - if (n instanceof MemoryCheckpoint) { - assert n instanceof FixedNode; - if (watchList.size() > 0) { - // Check whether we need to commit reads from the watch list. - checkWatchList(b, nodeToBlock, unprocessed, newList, watchList, n); + if (n instanceof MemoryNode) { + if (n instanceof MemoryCheckpoint) { + assert n instanceof FixedNode; + if (watchList.size() > 0) { + // Check whether we need to commit reads from the watch list. + checkWatchList(b, nodeToBlock, unprocessed, newList, watchList, n); + } } - // Add potential dependent reads to the watch list. for (Node usage : n.usages()) { if (usage instanceof FloatingReadNode) { FloatingReadNode floatingReadNode = (FloatingReadNode) usage; - if (nodeToBlock.get(floatingReadNode) == b && floatingReadNode.getLastLocationAccess() == n) { + if (nodeToBlock.get(floatingReadNode) == b && floatingReadNode.getLastLocationAccess() == n && !(n instanceof MemoryPhiNode)) { watchList.add(floatingReadNode); } } @@ -679,7 +694,8 @@ inputEarliest = nodeToBlock.get(((ControlSplitNode) input).getPrimarySuccessor()); } else { assert inputEarliest.getSuccessorCount() == 1; - inputEarliest = inputEarliest.getSuccessors().get(0); + assert !(input instanceof AbstractEndNode); + // Keep regular inputEarliest } } if (earliest.getDominatorDepth() < inputEarliest.getDominatorDepth()) { diff -r 2daf39328194 -r db96f9915540 graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/DefaultGenericInvocationPlugin.java --- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/DefaultGenericInvocationPlugin.java Thu Apr 23 21:18:27 2015 -0700 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/DefaultGenericInvocationPlugin.java Thu Apr 23 22:09:27 2015 -0700 @@ -123,17 +123,12 @@ private InputType getInputType(ResolvedJavaType type) { if (type != null && structuralInputType.isAssignableFrom(type)) { - ResolvedJavaType current = type; - while (current != null) { - MarkerType markerType = type.getAnnotation(MarkerType.class); - if (markerType != null) { - return markerType.value(); - } - - current = current.getSuperclass(); + MarkerType markerType = type.getAnnotation(MarkerType.class); + if (markerType != null) { + return markerType.value(); + } else { + throw GraalInternalError.shouldNotReachHere(String.format("%s extends StructuralInput, but is not annotated with @MarkerType", type)); } - - throw GraalInternalError.shouldNotReachHere(String.format("%s extends StructuralInput, but is not annotated with @MarkerType", type)); } else { return InputType.Value; } diff -r 2daf39328194 -r db96f9915540 make/Makefile --- a/make/Makefile Thu Apr 23 21:18:27 2015 -0700 +++ b/make/Makefile Thu Apr 23 22:09:27 2015 -0700 @@ -635,6 +635,9 @@ $(EXPORT_JRE_LIB_GRAAL_DIR)/%.jar: $(SHARED_DIR)/%.jar $(install-file) +$(EXPORT_JRE_LIB_GRAAL_SERVICES_DIR)/%: $(SHARED_DIR)/services/% + $(install-file) + $(EXPORT_INCLUDE_DIR)/%: $(HS_SRC_DIR)/share/vm/code/% $(install-file) diff -r 2daf39328194 -r db96f9915540 make/defs.make --- a/make/defs.make Thu Apr 23 21:18:27 2015 -0700 +++ b/make/defs.make Thu Apr 23 22:09:27 2015 -0700 @@ -342,6 +342,7 @@ EXPORT_JRE_LIB_DIR = $(EXPORT_JRE_DIR)/lib EXPORT_JRE_LIB_EXT_DIR = $(EXPORT_JRE_LIB_DIR)/ext EXPORT_JRE_LIB_GRAAL_DIR = $(EXPORT_JRE_LIB_DIR)/graal +EXPORT_JRE_LIB_GRAAL_SERVICES_DIR = $(EXPORT_JRE_LIB_GRAAL_DIR)/services EXPORT_JRE_LIB_ARCH_DIR = $(EXPORT_JRE_LIB_DIR)/$(LIBARCH) # non-universal macosx builds need to appear universal @@ -357,10 +358,17 @@ EXPORT_LIST += $(EXPORT_INCLUDE_DIR)/jni.h EXPORT_LIST += $(EXPORT_INCLUDE_DIR)/$(JDK_INCLUDE_SUBDIR)/jni_md.h EXPORT_LIST += $(EXPORT_INCLUDE_DIR)/jmm.h +EXPORT_LIST += $(EXPORT_JRE_LIB_DIR)/graal-loader.jar +EXPORT_LIST += $(EXPORT_JRE_LIB_DIR)/truffle.jar EXPORT_LIST += $(EXPORT_JRE_LIB_GRAAL_DIR)/graal.jar EXPORT_LIST += $(EXPORT_JRE_LIB_GRAAL_DIR)/graal-truffle.jar -EXPORT_LIST += $(EXPORT_JRE_LIB_DIR)/graal-loader.jar -EXPORT_LIST += $(EXPORT_JRE_LIB_DIR)/truffle.jar + +EXPORT_LIST += $(EXPORT_JRE_LIB_GRAAL_SERVICES_DIR)/com.oracle.graal.compiler.match.MatchStatementSet +EXPORT_LIST += $(EXPORT_JRE_LIB_GRAAL_SERVICES_DIR)/com.oracle.graal.hotspot.HotSpotBackendFactory +EXPORT_LIST += $(EXPORT_JRE_LIB_GRAAL_SERVICES_DIR)/com.oracle.graal.nodes.spi.ReplacementsProvider +EXPORT_LIST += $(EXPORT_JRE_LIB_GRAAL_SERVICES_DIR)/com.oracle.graal.phases.tiers.CompilerConfiguration +EXPORT_LIST += $(EXPORT_JRE_LIB_GRAAL_SERVICES_DIR)/com.oracle.graal.truffle.hotspot.nfi.RawNativeCallNodeFactory +EXPORT_LIST += $(EXPORT_JRE_LIB_GRAAL_SERVICES_DIR)/com.oracle.graal.truffle.OptimizedCallTargetInstrumentationFactory .PHONY: $(HS_ALT_MAKE)/defs.make diff -r 2daf39328194 -r db96f9915540 mx/mx_graal.py --- a/mx/mx_graal.py Thu Apr 23 21:18:27 2015 -0700 +++ b/mx/mx_graal.py Thu Apr 23 22:09:27 2015 -0700 @@ -594,13 +594,81 @@ shutil.move(tmp, dstLib) os.chmod(dstLib, permissions) -def _updateGraalServiceFiles(jdkDir): - jreGraalDir = join(jdkDir, 'jre', 'lib', 'graal') - graalJars = [join(jreGraalDir, e) for e in os.listdir(jreGraalDir) if e.startswith('graal') and e.endswith('.jar')] - jreGraalServicesDir = join(jreGraalDir, 'services') - if exists(jreGraalServicesDir): - shutil.rmtree(jreGraalServicesDir) - os.makedirs(jreGraalServicesDir) +def _eraseGenerics(className): + if '<' in className: + return className[:className.index('<')] + return className + +def _classifyGraalServices(classNames, graalJars): + classification = {} + for className in classNames: + classification[className] = None + javap = mx.java().javap + output = subprocess.check_output([javap, '-cp', os.pathsep.join(graalJars)] + classNames, stderr=subprocess.STDOUT) + lines = output.split(os.linesep) + for line in lines: + if line.startswith('public interface '): + declLine = line[len('public interface '):].strip() + for className in classNames: + if declLine.startswith(className): + assert classification[className] is None + afterName = declLine[len(className):] + if not afterName.startswith(' extends '): + classification[className] = False + break + superInterfaces = afterName[len(' extends '):-len(' {')].split(',') + if 'com.oracle.graal.api.runtime.Service' in superInterfaces: + classification[className] = True + break + maybe = [_eraseGenerics(superInterface) for superInterface in superInterfaces] + classification[className] = maybe + break + for className, v in classification.items(): + if v is None: + mx.abort('Could not find interface for service ' + className + ':\n' + output) + return classification + +def _extractMaybes(classification): + maybes = [] + for v in classification.values(): + if isinstance(v, list): + maybes.extend(v) + return maybes + +def _mergeClassification(classification, newClassification): + for className, value in classification.items(): + if isinstance(value, list): + classification[className] = None + for superInterface in value: + if newClassification[superInterface] is True: + classification[className] = True + break + elif newClassification[superInterface] is False: + if classification[className] is None: + classification[className] = False + else: + if not classification[className]: + classification[className] = [] + classification[className].extend(newClassification[superInterface]) + +def _filterGraalService(classNames, graalJars): + classification = _classifyGraalServices(classNames, graalJars) + needClassification = _extractMaybes(classification) + while needClassification: + _mergeClassification(classification, _classifyGraalServices(needClassification, graalJars)) + needClassification = _extractMaybes(classification) + filtered = [] + for className in classNames: + if classification[className] is True: + filtered.append(className) + return filtered + +def _extractGraalServiceFiles(graalJars, destination, cleanDestination=True): + if cleanDestination: + if exists(destination): + shutil.rmtree(destination) + os.makedirs(destination) + servicesMap = {} for jar in graalJars: if os.path.isfile(jar): with zipfile.ZipFile(jar) as zf: @@ -609,17 +677,29 @@ continue serviceName = basename(member) # we don't handle directories - assert serviceName - target = join(jreGraalServicesDir, serviceName) - lines = [] + assert serviceName and member == 'META-INF/services/' + serviceName with zf.open(member) as serviceFile: - lines.extend(serviceFile.readlines()) - if exists(target): - with open(target) as targetFile: - lines.extend(targetFile.readlines()) - with open(target, "w+") as targetFile: - for line in lines: - targetFile.write(line.rstrip() + os.linesep) + serviceImpls = servicesMap.setdefault(serviceName, []) + serviceImpls.extend(serviceFile.readlines()) + graalServices = _filterGraalService(servicesMap.keys(), graalJars) + for serviceName in graalServices: + serviceImpls = servicesMap[serviceName] + fd, tmp = tempfile.mkstemp(prefix=serviceName) + f = os.fdopen(fd, 'w+') + for serviceImpl in serviceImpls: + f.write(serviceImpl.rstrip() + os.linesep) + target = join(destination, serviceName) + f.close() + shutil.move(tmp, target) + if mx.get_os() != 'windows': + os.chmod(target, JDK_UNIX_PERMISSIONS_FILE) + return graalServices + +def _updateGraalServiceFiles(jdkDir): + jreGraalDir = join(jdkDir, 'jre', 'lib', 'graal') + graalJars = [join(jreGraalDir, e) for e in os.listdir(jreGraalDir) if e.startswith('graal') and e.endswith('.jar')] + jreGraalServicesDir = join(jreGraalDir, 'services') + _extractGraalServiceFiles(graalJars, jreGraalServicesDir) @@ -819,6 +899,7 @@ defsPath = join(_graal_home, 'make', 'defs.make') with open(defsPath) as fp: defs = fp.read() + graalJars = [] for jdkDist in _jdkDeployedDists: dist = mx.distribution(jdkDist.name) defLine = 'EXPORT_LIST += $(EXPORT_JRE_LIB_DIR)/' + basename(dist.path) @@ -826,11 +907,17 @@ defLine = 'EXPORT_LIST += $(EXPORT_JRE_LIB_EXT_DIR)/' + basename(dist.path) elif jdkDist.isGraalClassLoader: defLine = 'EXPORT_LIST += $(EXPORT_JRE_LIB_GRAAL_DIR)/' + basename(dist.path) + graalJars.append(dist.path) else: defLine = 'EXPORT_LIST += $(EXPORT_JRE_LIB_DIR)/' + basename(dist.path) if defLine not in defs: mx.abort('Missing following line in ' + defsPath + '\n' + defLine) shutil.copy(dist.path, opts2.export_dir) + services = _extractGraalServiceFiles(graalJars, join(opts2.export_dir, 'services')) + for service in services: + defLine = 'EXPORT_LIST += $(EXPORT_JRE_LIB_GRAAL_SERVICES_DIR)/' + service + if defLine not in defs: + mx.abort('Missing following line in ' + defsPath + ' for service from ' + dist.name + '\n' + defLine) graalOptions = join(_graal_home, 'graal.options') if exists(graalOptions): shutil.copy(graalOptions, opts2.export_dir) diff -r 2daf39328194 -r db96f9915540 mx/suite.py --- a/mx/suite.py Thu Apr 23 21:18:27 2015 -0700 +++ b/mx/suite.py Thu Apr 23 22:09:27 2015 -0700 @@ -314,13 +314,20 @@ "com.oracle.graal.hotspotvmconfig" : { "subDir" : "graal", "sourceDirs" : ["src"], - "dependencies" : ["com.oracle.graal.compiler.common"], "checkstyle" : "com.oracle.graal.graph", - "annotationProcessors" : ["com.oracle.graal.service.processor"], "javaCompliance" : "1.8", "workingSets" : "Graal,HotSpot", }, + "com.oracle.graal.hotspotvmconfig.processor" : { + "subDir" : "graal", + "sourceDirs" : ["src"], + "dependencies" : ["com.oracle.graal.hotspotvmconfig", "com.oracle.graal.compiler.common"], + "checkstyle" : "com.oracle.graal.graph", + "javaCompliance" : "1.8", + "workingSets" : "Graal,HotSpot,Codegen", + }, + "com.oracle.graal.hotspot" : { "subDir" : "graal", "sourceDirs" : ["src"], @@ -334,6 +341,7 @@ "annotationProcessors" : [ "com.oracle.graal.replacements.verifier", "com.oracle.graal.service.processor", + "com.oracle.graal.hotspotvmconfig.processor", ], "javaCompliance" : "1.8", "workingSets" : "Graal,HotSpot", @@ -393,7 +401,7 @@ "com.oracle.graal.replacements.sparc", ], "checkstyle" : "com.oracle.graal.graph", - "annotationProcessors" : ["com.oracle.graal.service.processor"], + "annotationProcessors" : ["com.oracle.graal.service.processor", "com.oracle.graal.compiler.match.processor"], "javaCompliance" : "1.8", "workingSets" : "Graal,HotSpot,SPARC", }, @@ -436,6 +444,17 @@ "sourceDirs" : ["src"], "checkstyle" : "com.oracle.graal.graph", "javaCompliance" : "1.8", + "workingSets" : "Graal", + }, + + "com.oracle.graal.options.processor" : { + "subDir" : "graal", + "sourceDirs" : ["src"], + "dependencies" : [ + "com.oracle.graal.options", + ], + "checkstyle" : "com.oracle.graal.graph", + "javaCompliance" : "1.8", "workingSets" : "Graal,Codegen", }, @@ -732,6 +751,17 @@ "workingSets" : "Graal", }, + "com.oracle.graal.compiler.match.processor" : { + "subDir" : "graal", + "sourceDirs" : ["src"], + "dependencies" : [ + "com.oracle.graal.compiler", + ], + "checkstyle" : "com.oracle.graal.graph", + "javaCompliance" : "1.8", + "workingSets" : "Graal,Codegen", + }, + "com.oracle.graal.compiler.amd64" : { "subDir" : "graal", "sourceDirs" : ["src"], @@ -740,6 +770,7 @@ "com.oracle.graal.lir.amd64", ], "checkstyle" : "com.oracle.graal.graph", + "annotationProcessors" : ["com.oracle.graal.compiler.match.processor"], "javaCompliance" : "1.8", "workingSets" : "Graal,AMD64", }, @@ -826,6 +857,7 @@ "com.oracle.graal.options", "com.oracle.graal.debug", ], + "annotationProcessors" : ["com.oracle.graal.options.processor"], "checkstyle" : "com.oracle.graal.graph", "javaCompliance" : "1.8", "workingSets" : "Graal,Java",