# HG changeset patch # User Michael Van De Vanter # Date 1430106630 25200 # Node ID fc6fd02ecf957010db99bb02cb50460b314860db # Parent d6d9631eb05707f06e6cf5068edf527463554d78# Parent f6f3f44a183051ae5ee581be7c8c6c8cbbc0d5fe Merge with f6f3f44a183051ae5ee581be7c8c6c8cbbc0d5fe diff -r d6d9631eb057 -r fc6fd02ecf95 graal/com.oracle.graal.asm.amd64/src/com/oracle/graal/asm/amd64/AMD64Assembler.java --- a/graal/com.oracle.graal.asm.amd64/src/com/oracle/graal/asm/amd64/AMD64Assembler.java Tue Apr 21 17:02:06 2015 -0700 +++ b/graal/com.oracle.graal.asm.amd64/src/com/oracle/graal/asm/amd64/AMD64Assembler.java Sun Apr 26 20:50:30 2015 -0700 @@ -2249,8 +2249,8 @@ } } - public void nullCheck(Register r) { - testl(AMD64.rax, new AMD64Address(r, 0)); + public void nullCheck(AMD64Address address) { + testl(AMD64.rax, address); } @Override diff -r d6d9631eb057 -r fc6fd02ecf95 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 Tue Apr 21 17:02:06 2015 -0700 +++ b/graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64NodeLIRBuilder.java Sun Apr 26 20:50:30 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 d6d9631eb057 -r fc6fd02ecf95 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 Tue Apr 21 17:02:06 2015 -0700 +++ b/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/util/FrequencyEncoder.java Sun Apr 26 20:50:30 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 d6d9631eb057 -r fc6fd02ecf95 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 Sun Apr 26 20:50:30 2015 -0700 @@ -0,0 +1,1 @@ +com.oracle.graal.compiler.match.processor.MatchProcessor diff -r d6d9631eb057 -r fc6fd02ecf95 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 Sun Apr 26 20:50:30 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 d6d9631eb057 -r fc6fd02ecf95 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 Tue Apr 21 17:02:06 2015 -0700 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/SchedulingTest2.java Sun Apr 26 20:50:30 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 d6d9631eb057 -r fc6fd02ecf95 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 Tue Apr 21 17:02:06 2015 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -com.oracle.graal.compiler.match.MatchProcessor diff -r d6d9631eb057 -r fc6fd02ecf95 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 Tue Apr 21 17:02:06 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 d6d9631eb057 -r fc6fd02ecf95 graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/InvocationPlugin.java --- a/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/InvocationPlugin.java Tue Apr 21 17:02:06 2015 -0700 +++ b/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/InvocationPlugin.java Sun Apr 26 20:50:30 2015 -0700 @@ -45,6 +45,7 @@ /** * Determines if this plugin can only be used when inlining the method is it associated with. + * That is, this plugin cannot be used when the associated method is the compilation root. */ default boolean inlineOnly() { return isSignaturePolymorphic(); diff -r d6d9631eb057 -r fc6fd02ecf95 graal/com.oracle.graal.hotspot.amd64.test/src/com/oracle/graal/hotspot/amd64/test/CompressedNullCheckTest.java --- a/graal/com.oracle.graal.hotspot.amd64.test/src/com/oracle/graal/hotspot/amd64/test/CompressedNullCheckTest.java Tue Apr 21 17:02:06 2015 -0700 +++ b/graal/com.oracle.graal.hotspot.amd64.test/src/com/oracle/graal/hotspot/amd64/test/CompressedNullCheckTest.java Sun Apr 26 20:50:30 2015 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 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 @@ -24,11 +24,15 @@ import org.junit.*; +import com.oracle.graal.api.meta.*; +import com.oracle.graal.compiler.common.*; import com.oracle.graal.compiler.test.*; import com.oracle.graal.hotspot.*; import com.oracle.graal.hotspot.nodes.*; import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.calc.*; +import com.oracle.graal.options.*; +import com.oracle.graal.options.OptionValue.OverrideScope; /** * Ensures that frame omission works in cases where it is expected to. @@ -36,17 +40,60 @@ public class CompressedNullCheckTest extends GraalCompilerTest { private static final class Container { - Integer i = new Integer(1); + Integer i; } public static void testSnippet(Container c) { c.i.intValue(); } + private void testImplicit(Integer i) { + Assume.assumeTrue(HotSpotGraalRuntime.runtime().getConfig().useCompressedOops); + + Container c = new Container(); + c.i = i; + + try (OverrideScope s = OptionValue.override(GraalOptions.OptImplicitNullChecks, true)) { + ResolvedJavaMethod method = getResolvedJavaMethod("testSnippet"); + Result expect = executeExpected(method, null, c); + + // make sure we don't get a profile that removes the implicit null check + method.reprofile(); + + Result actual = executeActual(method, null, c); + assertEquals(expect, actual); + } + } + + private void testExplicit(Integer i) { + Assume.assumeTrue(HotSpotGraalRuntime.runtime().getConfig().useCompressedOops); + + Container c = new Container(); + c.i = i; + + try (OverrideScope s = OptionValue.override(GraalOptions.OptImplicitNullChecks, false)) { + test("testSnippet", c); + } + } + @Test - public void test() { - Assume.assumeTrue(HotSpotGraalRuntime.runtime().getConfig().useCompressedOops); - test("testSnippet", new Container()); + public void implicit() { + testImplicit(new Integer(1)); + } + + @Test + public void implicitNull() { + testImplicit(null); + } + + @Test + public void explicit() { + testExplicit(new Integer(1)); + } + + @Test + public void explicitNull() { + testExplicit(null); } @Override diff -r d6d9631eb057 -r fc6fd02ecf95 graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotLIRGenerator.java --- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotLIRGenerator.java Tue Apr 21 17:02:06 2015 -0700 +++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotLIRGenerator.java Sun Apr 26 20:50:30 2015 -0700 @@ -736,16 +736,16 @@ public void emitNullCheck(Value address, LIRFrameState state) { if (address.getLIRKind().getPlatformKind() == Kind.Int) { CompressEncoding encoding = config.getOopEncoding(); + Value uncompressed; if (encoding.shift <= 3) { - AMD64AddressValue uncompressionAddress = emitAddress(getProviders().getRegisters().getHeapBaseRegister().asValue(), 0, load(address), 1 << encoding.shift); - append(new AMD64HotSpotMove.CompressedNullCheckOp(uncompressionAddress, state)); + uncompressed = emitAddress(getProviders().getRegisters().getHeapBaseRegister().asValue(), 0, load(address), 1 << encoding.shift); } else { - Value uncompress = emitUncompress(address, encoding, false); - append(new AMD64Move.NullCheckOp(load(uncompress), state)); + uncompressed = emitUncompress(address, encoding, false); } + append(new AMD64Move.NullCheckOp(asAddressValue(uncompressed), state)); } else { assert address.getKind() == Kind.Object || address.getKind() == Kind.Long : address + " - " + address.getKind() + " not a pointer!"; - append(new AMD64Move.NullCheckOp(load(address), state)); + append(new AMD64Move.NullCheckOp(asAddressValue(address), state)); } } diff -r d6d9631eb057 -r fc6fd02ecf95 graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotMove.java --- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotMove.java Tue Apr 21 17:02:06 2015 -0700 +++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotMove.java Sun Apr 26 20:50:30 2015 -0700 @@ -25,7 +25,6 @@ import static com.oracle.graal.api.code.ValueUtil.*; import static com.oracle.graal.lir.LIRInstruction.OperandFlag.*; -import com.oracle.graal.amd64.*; import com.oracle.graal.api.code.*; import com.oracle.graal.api.meta.*; import com.oracle.graal.asm.*; @@ -241,27 +240,4 @@ masm.addq(register, scratch); } } - - public static final class CompressedNullCheckOp extends AMD64LIRInstruction { - public static final LIRInstructionClass TYPE = LIRInstructionClass.create(CompressedNullCheckOp.class); - - @Use({COMPOSITE}) protected AMD64AddressValue address; - @State protected LIRFrameState state; - - public CompressedNullCheckOp(AMD64AddressValue address, LIRFrameState state) { - super(TYPE); - this.address = address; - this.state = state; - } - - @Override - public void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler masm) { - crb.recordImplicitException(masm.position(), state); - masm.testl(AMD64.rax, address.toAddress()); - } - - public LIRFrameState getState() { - return state; - } - } } diff -r d6d9631eb057 -r fc6fd02ecf95 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 Tue Apr 21 17:02:06 2015 -0700 +++ b/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/ArrayCopyIntrinsificationTest.java Sun Apr 26 20:50:30 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 d6d9631eb057 -r fc6fd02ecf95 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 Tue Apr 21 17:02:06 2015 -0700 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotDebugInfoBuilder.java Sun Apr 26 20:50:30 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.*; @@ -55,14 +56,17 @@ StackSlotValue slot = lockStack.makeLockSlot(lockDepth); ValueNode lock = state.lockAt(lockIndex); Value object = toValue(lock); - boolean eliminated = object instanceof VirtualObject && state.monitorIdAt(lockIndex) != null; + boolean eliminated = object instanceof VirtualObject || state.monitorIdAt(lockIndex) == null; assert state.monitorIdAt(lockIndex) == null || state.monitorIdAt(lockIndex).getLockDepth() == lockDepth; return new StackLockValue(object, slot, eliminated); } @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 d6d9631eb057 -r fc6fd02ecf95 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 Tue Apr 21 17:02:06 2015 -0700 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/DefaultHotSpotLoweringProvider.java Sun Apr 26 20:50:30 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 d6d9631eb057 -r fc6fd02ecf95 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotSnippetReflectionProvider.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotSnippetReflectionProvider.java Tue Apr 21 17:02:06 2015 -0700 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotSnippetReflectionProvider.java Sun Apr 26 20:50:30 2015 -0700 @@ -78,7 +78,9 @@ private ResolvedJavaType configType; public Object getInjectedNodeIntrinsicParameter(ResolvedJavaType type) { - if (wordTypesType == null) { + // Need to test all fields since there no guarantee under the JMM + // about the order in which these fields are written. + if (configType == null || wordTypesType == null || configType == null) { MetaAccessProvider metaAccess = runtime.getHostProviders().getMetaAccess(); wordTypesType = metaAccess.lookupJavaType(runtime.getHostProviders().getWordTypes().getClass()); runtimeType = metaAccess.lookupJavaType(runtime.getClass()); diff -r d6d9631eb057 -r fc6fd02ecf95 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 Tue Apr 21 17:02:06 2015 -0700 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotSuitesProvider.java Sun Apr 26 20:50:30 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 d6d9631eb057 -r fc6fd02ecf95 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 Tue Apr 21 17:02:06 2015 -0700 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/SerialWriteBarrier.java Sun Apr 26 20:50:30 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 d6d9631eb057 -r fc6fd02ecf95 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 Tue Apr 21 17:02:06 2015 -0700 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/phases/WriteBarrierAdditionPhase.java Sun Apr 26 20:50:30 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 d6d9631eb057 -r fc6fd02ecf95 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 Tue Apr 21 17:02:06 2015 -0700 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/phases/WriteBarrierVerificationPhase.java Sun Apr 26 20:50:30 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 d6d9631eb057 -r fc6fd02ecf95 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 Tue Apr 21 17:02:06 2015 -0700 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/WriteBarrierSnippets.java Sun Apr 26 20:50:30 2015 -0700 @@ -112,8 +112,8 @@ @Snippet public static void g1PreWriteBarrier(Object object, Object expectedObject, Object location, @ConstantParameter boolean doLoad, @ConstantParameter boolean nullCheck, @ConstantParameter Register threadRegister, @ConstantParameter boolean trace) { - if (nullCheck && object == null) { - DeoptimizeNode.deopt(DeoptimizationAction.InvalidateReprofile, DeoptimizationReason.NullCheckException); + if (nullCheck) { + NullCheckNode.nullCheck(object); } Word thread = registerAsWord(threadRegister); Object fixedObject = FixedValueAnchorNode.getObject(object); @@ -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 d6d9631eb057 -r fc6fd02ecf95 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 Tue Apr 21 17:02:06 2015 -0700 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/arraycopy/ArrayCopySnippets.java Sun Apr 26 20:50:30 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 d6d9631eb057 -r fc6fd02ecf95 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 Sun Apr 26 20:50:30 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 d6d9631eb057 -r fc6fd02ecf95 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 Sun Apr 26 20:50:30 2015 -0700 @@ -0,0 +1,1 @@ +com.oracle.graal.hotspotvmconfig.processor.HotSpotVMConfigProcessor diff -r d6d9631eb057 -r fc6fd02ecf95 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 Sun Apr 26 20:50:30 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 d6d9631eb057 -r fc6fd02ecf95 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 Tue Apr 21 17:02:06 2015 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -com.oracle.graal.hotspotvmconfig.HotSpotVMConfigProcessor diff -r d6d9631eb057 -r fc6fd02ecf95 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 Tue Apr 21 17:02:06 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 d6d9631eb057 -r fc6fd02ecf95 graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java --- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java Tue Apr 21 17:02:06 2015 -0700 +++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java Sun Apr 26 20:50:30 2015 -0700 @@ -124,7 +124,7 @@ frameState.initializeForMethodStart(graphBuilderConfig.eagerResolving() || replacementContext != null, graphBuilderConfig.getPlugins().getParameterPlugin()); parser.build(graph.start(), frameState); - connectLoopEndToBegin(graph); + GraphUtil.normalizeLoops(graph); // Remove dead parameters. for (ParameterNode param : graph.getNodes(ParameterNode.TYPE)) { @@ -2428,27 +2428,4 @@ assert assertionsEnabled = true; return assertionsEnabled; } - - /** - * Remove loop header without loop ends. This can happen with degenerated loops like this one: - * - *
-     * for (;;) {
-     *     try {
-     *         break;
-     *     } catch (UnresolvedException iioe) {
-     *     }
-     * }
-     * 
- */ - public static void connectLoopEndToBegin(StructuredGraph graph) { - for (LoopBeginNode begin : graph.getNodes(LoopBeginNode.TYPE)) { - if (begin.loopEnds().isEmpty()) { - assert begin.forwardEndCount() == 1; - graph.reduceDegenerateLoopBegin(begin); - } else { - GraphUtil.normalizeLoopBegin(begin); - } - } - } } diff -r d6d9631eb057 -r fc6fd02ecf95 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 Sun Apr 26 20:50:30 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 d6d9631eb057 -r fc6fd02ecf95 graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64Move.java --- a/graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64Move.java Tue Apr 21 17:02:06 2015 -0700 +++ b/graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64Move.java Sun Apr 26 20:50:30 2015 -0700 @@ -182,23 +182,23 @@ public static final class NullCheckOp extends AMD64LIRInstruction implements NullCheck { public static final LIRInstructionClass TYPE = LIRInstructionClass.create(NullCheckOp.class); - @Use({REG}) protected AllocatableValue input; + @Use({COMPOSITE}) protected AMD64AddressValue address; @State protected LIRFrameState state; - public NullCheckOp(Variable input, LIRFrameState state) { + public NullCheckOp(AMD64AddressValue address, LIRFrameState state) { super(TYPE); - this.input = input; + this.address = address; this.state = state; } @Override public void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler masm) { crb.recordImplicitException(masm.position(), state); - masm.nullCheck(asRegister(input)); + masm.nullCheck(address.toAddress()); } public Value getCheckedValue() { - return input; + return address.base; } public LIRFrameState getState() { diff -r d6d9631eb057 -r fc6fd02ecf95 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 Tue Apr 21 17:02:06 2015 -0700 +++ b/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/LoopEx.java Sun Apr 26 20:50:30 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 d6d9631eb057 -r fc6fd02ecf95 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 Tue Apr 21 17:02:06 2015 -0700 +++ b/graal/com.oracle.graal.nodeinfo/src/com/oracle/graal/nodeinfo/StructuralInput.java Sun Apr 26 20:50:30 2015 -0700 @@ -35,6 +35,7 @@ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) + @Inherited public @interface MarkerType { InputType value(); } diff -r d6d9631eb057 -r fc6fd02ecf95 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 Tue Apr 21 17:02:06 2015 -0700 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GraphDecoder.java Sun Apr 26 20:50:30 2015 -0700 @@ -257,10 +257,14 @@ } public final void decode(StructuredGraph graph, EncodedGraph encodedGraph) { - MethodScope methodScope = new MethodScope(graph, encodedGraph, LoopExplosionKind.NONE); - decode(methodScope, null); - cleanupGraph(methodScope, null); - methodScope.graph.verify(); + try (Debug.Scope scope = Debug.scope("GraphDecoder", graph)) { + MethodScope methodScope = new MethodScope(graph, encodedGraph, LoopExplosionKind.NONE); + decode(methodScope, null); + cleanupGraph(methodScope, null); + methodScope.graph.verify(); + } catch (Throwable ex) { + Debug.handle(ex); + } } protected final void decode(MethodScope methodScope, FixedWithNextNode startNode) { @@ -287,6 +291,7 @@ assert loopScope.nextIterations.peekFirst().loopIteration == loopScope.loopIteration + 1; loopScope = loopScope.nextIterations.removeFirst(); } else { + propagateCreatedNodes(loopScope); loopScope = loopScope.outer; } } @@ -298,11 +303,23 @@ */ FixedNode detectLoopsStart = startNode.predecessor() != null ? (FixedNode) startNode.predecessor() : startNode; cleanupGraph(methodScope, start); - Debug.dump(methodScope.graph, "Before loop detection"); detectLoops(methodScope.graph, detectLoopsStart); } } + private static void propagateCreatedNodes(LoopScope loopScope) { + if (loopScope.outer == null) { + return; + } + + /* Register nodes that were created while decoding the loop to the outside scope. */ + for (int i = 0; i < loopScope.createdNodes.length; i++) { + if (loopScope.outer.createdNodes[i] == null) { + loopScope.outer.createdNodes[i] = loopScope.createdNodes[i]; + } + } + } + protected LoopScope processNextNode(MethodScope methodScope, LoopScope loopScope) { int nodeOrderId = loopScope.nodesToProcess.nextSetBit(0); loopScope.nodesToProcess.clear(nodeOrderId); @@ -370,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); } } @@ -662,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); @@ -697,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()]; @@ -778,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); } @@ -786,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. */ @@ -1002,7 +1033,6 @@ } } - Debug.dump(currentGraph, "After loops detected"); insertLoopEnds(currentGraph, startInstruction); } @@ -1127,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; @@ -1137,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()) { @@ -1147,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 d6d9631eb057 -r fc6fd02ecf95 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 Tue Apr 21 17:02:06 2015 -0700 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GraphEncoder.java Sun Apr 26 20:50:30 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; } } @@ -442,9 +451,6 @@ } } - for (Node expectedNode : expectedGraph.getNodes()) { - assert nodeMapping.get(expectedNode) != null || (expectedNode.hasNoUsages() && !(expectedNode instanceof FixedNode)) : "expectedNode"; - } return true; } diff -r d6d9631eb057 -r fc6fd02ecf95 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/SimplifyingGraphDecoder.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/SimplifyingGraphDecoder.java Sun Apr 26 20:50:30 2015 -0700 @@ -0,0 +1,192 @@ +/* + * 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.nodes; + +import java.util.*; + +import com.oracle.graal.api.code.*; +import com.oracle.graal.api.meta.*; +import com.oracle.graal.graph.*; +import com.oracle.graal.graph.spi.*; +import com.oracle.graal.nodes.extended.*; +import com.oracle.graal.nodes.spi.*; +import com.oracle.graal.nodes.util.*; + +/** + * Graph decoder that simplifies nodes during decoding. The standard + * {@link Canonicalizable#canonical node canonicalization} interface is used to canonicalize nodes + * during decoding. Additionally, {@link IfNode branches} and {@link IntegerSwitchNode switches} + * with constant conditions are simplified. + */ +public class SimplifyingGraphDecoder extends GraphDecoder { + + protected final MetaAccessProvider metaAccess; + protected final ConstantReflectionProvider constantReflection; + protected final StampProvider stampProvider; + protected final boolean canonicalizeReads; + + protected class PECanonicalizerTool implements CanonicalizerTool { + @Override + public MetaAccessProvider getMetaAccess() { + return metaAccess; + } + + @Override + public ConstantReflectionProvider getConstantReflection() { + return constantReflection; + } + + @Override + public boolean canonicalizeReads() { + return canonicalizeReads; + } + + @Override + public boolean allUsagesAvailable() { + return false; + } + } + + public SimplifyingGraphDecoder(MetaAccessProvider metaAccess, ConstantReflectionProvider constantReflection, StampProvider stampProvider, boolean canonicalizeReads, Architecture architecture) { + super(architecture); + this.metaAccess = metaAccess; + this.constantReflection = constantReflection; + this.stampProvider = stampProvider; + this.canonicalizeReads = canonicalizeReads; + } + + @Override + protected void cleanupGraph(MethodScope methodScope, Graph.Mark start) { + GraphUtil.normalizeLoops(methodScope.graph); + super.cleanupGraph(methodScope, start); + } + + @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; + if (ifNode.condition() instanceof LogicNegationNode) { + ifNode.eliminateNegation(); + } + if (ifNode.condition() instanceof LogicConstantNode) { + boolean condition = ((LogicConstantNode) ifNode.condition()).getValue(); + AbstractBeginNode survivingSuccessor = ifNode.getSuccessor(condition); + AbstractBeginNode deadSuccessor = ifNode.getSuccessor(!condition); + + methodScope.graph.removeSplit(ifNode, survivingSuccessor); + assert deadSuccessor.next() == null : "must not be parsed yet"; + deadSuccessor.safeDelete(); + } + + } else if (node instanceof IntegerSwitchNode && ((IntegerSwitchNode) node).value().isConstant()) { + IntegerSwitchNode switchNode = (IntegerSwitchNode) node; + int value = switchNode.value().asJavaConstant().asInt(); + AbstractBeginNode survivingSuccessor = switchNode.successorAtKey(value); + List allSuccessors = switchNode.successors().snapshot(); + + methodScope.graph.removeSplit(switchNode, survivingSuccessor); + for (Node successor : allSuccessors) { + if (successor != survivingSuccessor) { + assert ((AbstractBeginNode) successor).next() == null : "must not be parsed yet"; + successor.safeDelete(); + } + } + + } else if (node instanceof Canonicalizable) { + Node canonical = ((Canonicalizable) node).canonical(new PECanonicalizerTool()); + if (canonical == null) { + /* + * This is a possible return value of canonicalization. However, we might need to + * add additional usages later on for which we need a node. Therefore, we just do + * nothing and leave the node in place. + */ + } else if (canonical != node) { + if (!canonical.isAlive()) { + assert !canonical.isDeleted(); + canonical = methodScope.graph.addOrUniqueWithInputs(canonical); + if (canonical instanceof FixedWithNextNode) { + methodScope.graph.addBeforeFixed(node, (FixedWithNextNode) canonical); + } else if (canonical instanceof ControlSinkNode) { + FixedWithNextNode predecessor = (FixedWithNextNode) node.predecessor(); + predecessor.setNext((ControlSinkNode) canonical); + node.safeDelete(); + for (Node successor : node.successors()) { + successor.safeDelete(); + } + + } else { + assert !(canonical instanceof FixedNode); + } + } + if (!node.isDeleted()) { + GraphUtil.unlinkFixedNode((FixedWithNextNode) node); + node.replaceAtUsages(canonical); + node.safeDelete(); + } + assert lookupNode(loopScope, nodeOrderId) == node; + registerNode(loopScope, nodeOrderId, canonical, true, false); + } + } + } + + @Override + protected Node handleFloatingNodeBeforeAdd(MethodScope methodScope, LoopScope loopScope, Node node) { + if (node instanceof Canonicalizable) { + Node canonical = ((Canonicalizable) node).canonical(new PECanonicalizerTool()); + if (canonical == null) { + /* + * This is a possible return value of canonicalization. However, we might need to + * add additional usages later on for which we need a node. Therefore, we just do + * nothing and leave the node in place. + */ + } else if (canonical != node) { + if (!canonical.isAlive()) { + assert !canonical.isDeleted(); + canonical = methodScope.graph.addOrUniqueWithInputs(canonical); + } + assert node.hasNoUsages(); + // methodScope.graph.replaceFloating((FloatingNode) node, canonical); + return canonical; + } + } + 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 d6d9631eb057 -r fc6fd02ecf95 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/NullCheckNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/NullCheckNode.java Tue Apr 21 17:02:06 2015 -0700 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/NullCheckNode.java Sun Apr 26 20:50:30 2015 -0700 @@ -52,4 +52,7 @@ public boolean canDeoptimize() { return true; } + + @NodeIntrinsic + public static native void nullCheck(Object object); } diff -r d6d9631eb057 -r fc6fd02ecf95 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 Tue Apr 21 17:02:06 2015 -0700 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/LoadFieldNode.java Sun Apr 26 20:50:30 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 d6d9631eb057 -r fc6fd02ecf95 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/TypeCheckNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/TypeCheckNode.java Tue Apr 21 17:02:06 2015 -0700 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/TypeCheckNode.java Sun Apr 26 20:50:30 2015 -0700 @@ -79,7 +79,7 @@ if (result != null) { return result; } - Assumptions assumptions = graph().getAssumptions(); + Assumptions assumptions = graph() == null ? null : graph().getAssumptions(); if (assumptions != null) { AssumptionResult leafConcreteSubtype = stampType.findLeafConcreteSubtype(); if (leafConcreteSubtype != null) { diff -r d6d9631eb057 -r fc6fd02ecf95 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 Tue Apr 21 17:02:06 2015 -0700 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/util/GraphUtil.java Sun Apr 26 20:50:30 2015 -0700 @@ -225,7 +225,44 @@ } } - public static void normalizeLoopBegin(LoopBeginNode begin) { + /** + * Remove loop header without loop ends. This can happen with degenerated loops like this one: + * + *
+     * for (;;) {
+     *     try {
+     *         break;
+     *     } catch (UnresolvedException iioe) {
+     *     }
+     * }
+     * 
+ */ + 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) { // Delete unnecessary loop phi functions, i.e., phi functions where all inputs are either // the same or the phi itself. for (PhiNode phi : begin.phis().snapshot()) { diff -r d6d9631eb057 -r fc6fd02ecf95 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 Sun Apr 26 20:50:30 2015 -0700 @@ -0,0 +1,1 @@ +com.oracle.graal.options.processor.OptionProcessor \ No newline at end of file diff -r d6d9631eb057 -r fc6fd02ecf95 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 Sun Apr 26 20:50:30 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 d6d9631eb057 -r fc6fd02ecf95 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 Tue Apr 21 17:02:06 2015 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -com.oracle.graal.options.OptionProcessor diff -r d6d9631eb057 -r fc6fd02ecf95 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 Tue Apr 21 17:02:06 2015 -0700 +++ b/graal/com.oracle.graal.options/src/com/oracle/graal/options/Option.java Sun Apr 26 20:50:30 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 d6d9631eb057 -r fc6fd02ecf95 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 Tue Apr 21 17:02:06 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 d6d9631eb057 -r fc6fd02ecf95 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 Tue Apr 21 17:02:06 2015 -0700 +++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/schedule/SchedulePhase.java Sun Apr 26 20:50:30 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 d6d9631eb057 -r fc6fd02ecf95 graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/CachingPEGraphDecoder.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/CachingPEGraphDecoder.java Sun Apr 26 20:50:30 2015 -0700 @@ -0,0 +1,87 @@ +/* + * 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.replacements; + +import java.util.*; + +import com.oracle.graal.api.code.*; +import com.oracle.graal.api.meta.*; +import com.oracle.graal.debug.*; +import com.oracle.graal.graphbuilderconf.*; +import com.oracle.graal.java.*; +import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.StructuredGraph.AllowAssumptions; +import com.oracle.graal.phases.*; +import com.oracle.graal.phases.common.*; +import com.oracle.graal.phases.tiers.*; +import com.oracle.graal.phases.util.*; + +/** + * A graph decoder that provides all necessary encoded graphs on-the-fly (by parsing the methods and + * encoding the graphs). + */ +public class CachingPEGraphDecoder extends PEGraphDecoder { + + private final Providers providers; + private final GraphBuilderConfiguration graphBuilderConfig; + private final OptimisticOptimizations optimisticOpts; + private final AllowAssumptions allowAssumptions; + private final Map graphCache; + + public CachingPEGraphDecoder(Providers providers, GraphBuilderConfiguration graphBuilderConfig, OptimisticOptimizations optimisticOpts, AllowAssumptions allowAssumptions, Architecture architecture) { + super(providers.getMetaAccess(), providers.getConstantReflection(), providers.getStampProvider(), architecture); + + this.providers = providers; + this.graphBuilderConfig = graphBuilderConfig; + this.optimisticOpts = optimisticOpts; + this.allowAssumptions = allowAssumptions; + this.graphCache = new HashMap<>(); + } + + private EncodedGraph createGraph(ResolvedJavaMethod method) { + StructuredGraph graph = new StructuredGraph(method, allowAssumptions); + try (Debug.Scope scope = Debug.scope("createGraph", graph)) { + + new GraphBuilderPhase.Instance(providers.getMetaAccess(), providers.getStampProvider(), providers.getConstantReflection(), graphBuilderConfig, optimisticOpts, null).apply(graph); + + PhaseContext context = new PhaseContext(providers); + new CanonicalizerPhase().apply(graph, context); + + EncodedGraph encodedGraph = GraphEncoder.encodeSingleGraph(graph, architecture); + graphCache.put(method, encodedGraph); + return encodedGraph; + + } catch (Throwable ex) { + throw Debug.handle(ex); + } + } + + @Override + protected EncodedGraph lookupEncodedGraph(ResolvedJavaMethod method) { + EncodedGraph result = graphCache.get(method); + if (result == null && method.hasBytecodes()) { + result = createGraph(method); + } + return result; + } +} diff -r d6d9631eb057 -r fc6fd02ecf95 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 Tue Apr 21 17:02:06 2015 -0700 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/DefaultGenericInvocationPlugin.java Sun Apr 26 20:50:30 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 d6d9631eb057 -r fc6fd02ecf95 graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/DefaultJavaLoweringProvider.java --- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/DefaultJavaLoweringProvider.java Tue Apr 21 17:02:06 2015 -0700 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/DefaultJavaLoweringProvider.java Sun Apr 26 20:50:30 2015 -0700 @@ -67,6 +67,7 @@ public void initialize(Providers providers, SnippetReflectionProvider snippetReflection) { boxingSnippets = new BoxingSnippets.Templates(providers, snippetReflection, target); + providers.getReplacements().registerSnippetTemplateCache(new SnippetCounterNode.SnippetCounterSnippets.Templates(providers, snippetReflection, target)); } @Override diff -r d6d9631eb057 -r fc6fd02ecf95 graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/PEGraphDecoder.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/PEGraphDecoder.java Sun Apr 26 20:50:30 2015 -0700 @@ -0,0 +1,615 @@ +/* + * 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.replacements; + +import static com.oracle.graal.compiler.common.GraalInternalError.*; +import static com.oracle.graal.java.AbstractBytecodeParser.Options.*; + +import java.util.*; + +import com.oracle.graal.api.code.*; +import com.oracle.graal.api.meta.*; +import com.oracle.graal.compiler.common.type.*; +import com.oracle.graal.graph.*; +import com.oracle.graal.graph.spi.*; +import com.oracle.graal.graphbuilderconf.*; +import com.oracle.graal.graphbuilderconf.InlineInvokePlugin.InlineInfo; +import com.oracle.graal.graphbuilderconf.InvocationPlugins.InvocationPluginReceiver; +import com.oracle.graal.java.*; +import com.oracle.graal.nodeinfo.*; +import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.CallTargetNode.InvokeKind; +import com.oracle.graal.nodes.extended.*; +import com.oracle.graal.nodes.java.*; +import com.oracle.graal.nodes.spi.*; +import com.oracle.graal.phases.common.inlining.*; + +/** + * A graph decoder that performs partial evaluation, i.e., that performs method inlining and + * canonicalization/simplification of nodes during decoding. + * + * Inlining and loop explosion are configured via the plugin mechanism also used by the + * {@link GraphBuilderPhase}. However, not all callback methods defined in + * {@link GraphBuilderContext} are available since decoding is more limited than graph building. + * + * The standard {@link Canonicalizable#canonical node canonicalization} interface is used to + * canonicalize nodes during decoding. Additionally, {@link IfNode branches} and + * {@link IntegerSwitchNode switches} with constant conditions are simplified. + */ +public abstract class PEGraphDecoder extends SimplifyingGraphDecoder { + + protected class PEMethodScope extends MethodScope { + /** The state of the caller method. Only non-null during method inlining. */ + protected final PEMethodScope caller; + protected final LoopScope callerLoopScope; + protected final ResolvedJavaMethod method; + protected final InvokeData invokeData; + protected final int inliningDepth; + + protected final LoopExplosionPlugin loopExplosionPlugin; + protected final InvocationPlugins invocationPlugins; + protected final InlineInvokePlugin inlineInvokePlugin; + protected final ParameterPlugin parameterPlugin; + protected final ValueNode[] arguments; + + protected FrameState outerState; + protected FrameState exceptionState; + protected ExceptionPlaceholderNode exceptionPlaceholderNode; + protected BytecodePosition bytecodePosition; + + protected PEMethodScope(StructuredGraph targetGraph, PEMethodScope caller, LoopScope callerLoopScope, EncodedGraph encodedGraph, ResolvedJavaMethod method, InvokeData invokeData, + int inliningDepth, LoopExplosionPlugin loopExplosionPlugin, InvocationPlugins invocationPlugins, InlineInvokePlugin inlineInvokePlugin, ParameterPlugin parameterPlugin, + ValueNode[] arguments) { + super(targetGraph, encodedGraph, loopExplosionKind(method, loopExplosionPlugin)); + + this.caller = caller; + this.callerLoopScope = callerLoopScope; + this.method = method; + this.invokeData = invokeData; + this.inliningDepth = inliningDepth; + this.loopExplosionPlugin = loopExplosionPlugin; + this.invocationPlugins = invocationPlugins; + this.inlineInvokePlugin = inlineInvokePlugin; + this.parameterPlugin = parameterPlugin; + this.arguments = arguments; + } + + public boolean isInlinedMethod() { + return caller != null; + } + } + + protected class PENonAppendGraphBuilderContext implements GraphBuilderContext { + protected final PEMethodScope methodScope; + protected final Invoke invoke; + + public PENonAppendGraphBuilderContext(PEMethodScope methodScope, Invoke invoke) { + this.methodScope = methodScope; + this.invoke = invoke; + } + + @Override + public BailoutException bailout(String string) { + throw new BailoutException(string); + } + + @Override + public StampProvider getStampProvider() { + return stampProvider; + } + + @Override + public MetaAccessProvider getMetaAccess() { + return metaAccess; + } + + @Override + public ConstantReflectionProvider getConstantReflection() { + return constantReflection; + } + + @Override + public StructuredGraph getGraph() { + return methodScope.graph; + } + + @Override + public int getDepth() { + return methodScope.inliningDepth; + } + + @Override + public Replacement getReplacement() { + return null; + } + + @Override + public T append(T value) { + throw unimplemented(); + } + + @Override + public T recursiveAppend(T value) { + throw unimplemented(); + } + + @Override + public void push(Kind kind, ValueNode value) { + throw unimplemented(); + } + + @Override + public void handleReplacedInvoke(InvokeKind invokeKind, ResolvedJavaMethod targetMethod, ValueNode[] args) { + throw unimplemented(); + } + + @Override + public void intrinsify(ResolvedJavaMethod targetMethod, ResolvedJavaMethod substitute, ValueNode[] args) { + throw unimplemented(); + } + + @Override + public FrameState createStateAfter() { + throw unimplemented(); + } + + @Override + public GraphBuilderContext getParent() { + throw unimplemented(); + } + + @Override + public ResolvedJavaMethod getMethod() { + throw unimplemented(); + } + + @Override + public int bci() { + return invoke.bci(); + } + + @Override + public InvokeKind getInvokeKind() { + throw unimplemented(); + } + + @Override + public JavaType getInvokeReturnType() { + throw unimplemented(); + } + } + + protected class PEAppendGraphBuilderContext extends PENonAppendGraphBuilderContext { + protected FixedWithNextNode lastInstr; + protected ValueNode pushedNode; + + public PEAppendGraphBuilderContext(PEMethodScope inlineScope, FixedWithNextNode lastInstr) { + super(inlineScope, inlineScope.invokeData.invoke); + this.lastInstr = lastInstr; + } + + @Override + public void push(Kind kind, ValueNode value) { + if (pushedNode != null) { + throw unimplemented("Only one push is supported"); + } + pushedNode = value; + } + + @Override + public FrameState createStateAfter() { + Node stateAfter = decodeFloatingNode(methodScope.caller, methodScope.callerLoopScope, methodScope.invokeData.stateAfterOrderId); + getGraph().add(stateAfter); + return (FrameState) handleFloatingNodeAfterAdd(methodScope.caller, methodScope.callerLoopScope, stateAfter); + } + + @Override + public T append(T v) { + if (v.graph() != null) { + return v; + } + T added = getGraph().addOrUnique(v); + if (added == v) { + updateLastInstruction(v); + } + return added; + } + + @Override + public T recursiveAppend(T v) { + if (v.graph() != null) { + return v; + } + T added = getGraph().addOrUniqueWithInputs(v); + if (added == v) { + updateLastInstruction(v); + } + return added; + } + + private void updateLastInstruction(T v) { + if (v instanceof FixedNode) { + FixedNode fixedNode = (FixedNode) v; + lastInstr.setNext(fixedNode); + if (fixedNode instanceof FixedWithNextNode) { + FixedWithNextNode fixedWithNextNode = (FixedWithNextNode) fixedNode; + assert fixedWithNextNode.next() == null : "cannot append instruction to instruction which isn't end"; + lastInstr = fixedWithNextNode; + } else { + lastInstr = null; + } + } + } + } + + @NodeInfo + static class ExceptionPlaceholderNode extends ValueNode { + public static final NodeClass TYPE = NodeClass.create(ExceptionPlaceholderNode.class); + + public ExceptionPlaceholderNode() { + super(TYPE, StampFactory.object()); + } + } + + public PEGraphDecoder(MetaAccessProvider metaAccess, ConstantReflectionProvider constantReflection, StampProvider stampProvider, Architecture architecture) { + super(metaAccess, constantReflection, stampProvider, true, architecture); + } + + protected static LoopExplosionKind loopExplosionKind(ResolvedJavaMethod method, LoopExplosionPlugin loopExplosionPlugin) { + if (loopExplosionPlugin == null) { + return LoopExplosionKind.NONE; + } else if (loopExplosionPlugin.shouldMergeExplosions(method)) { + return LoopExplosionKind.MERGE_EXPLODE; + } else if (loopExplosionPlugin.shouldExplodeLoops(method)) { + return LoopExplosionKind.FULL_EXPLODE; + } else { + return LoopExplosionKind.NONE; + } + } + + public void decode(StructuredGraph targetGraph, ResolvedJavaMethod method, LoopExplosionPlugin loopExplosionPlugin, InvocationPlugins invocationPlugins, InlineInvokePlugin inlineInvokePlugin, + ParameterPlugin parameterPlugin) { + PEMethodScope methodScope = new PEMethodScope(targetGraph, null, null, lookupEncodedGraph(method), method, null, 0, loopExplosionPlugin, invocationPlugins, inlineInvokePlugin, + parameterPlugin, null); + decode(methodScope, null); + cleanupGraph(methodScope, null); + methodScope.graph.verify(); + } + + @Override + protected void checkLoopExplosionIteration(MethodScope s, LoopScope loopScope) { + PEMethodScope methodScope = (PEMethodScope) s; + + if (loopScope.loopIteration > MaximumLoopExplosionCount.getValue()) { + String message = "too many loop explosion iterations - does the explosion not terminate for method " + methodScope.method + "?"; + if (FailedLoopExplosionIsFatal.getValue()) { + throw new RuntimeException(message); + } else { + throw new BailoutException(message); + } + } + } + + @Override + protected void handleInvoke(MethodScope s, LoopScope loopScope, InvokeData invokeData) { + PEMethodScope methodScope = (PEMethodScope) s; + + /* + * Decode the call target, but do not add it to the graph yet. This avoids adding usages for + * all the arguments, which are expensive to remove again when we can inline the method. + */ + assert invokeData.invoke.callTarget() == null : "callTarget edge is ignored during decoding of Invoke"; + CallTargetNode callTarget = (CallTargetNode) decodeFloatingNode(methodScope, loopScope, invokeData.callTargetOrderId); + if (!(callTarget instanceof MethodCallTargetNode) || !trySimplifyInvoke(methodScope, loopScope, invokeData, (MethodCallTargetNode) callTarget)) { + + /* We know that we need an invoke, so now we can add the call target to the graph. */ + methodScope.graph.add(callTarget); + registerNode(loopScope, invokeData.callTargetOrderId, callTarget, false, false); + super.handleInvoke(methodScope, loopScope, invokeData); + } + } + + protected boolean trySimplifyInvoke(PEMethodScope methodScope, LoopScope loopScope, InvokeData invokeData, MethodCallTargetNode callTarget) { + // attempt to devirtualize the call + ResolvedJavaMethod specialCallTarget = MethodCallTargetNode.findSpecialCallTarget(callTarget.invokeKind(), callTarget.receiver(), callTarget.targetMethod(), invokeData.contextType); + if (specialCallTarget != null) { + callTarget.setTargetMethod(specialCallTarget); + callTarget.setInvokeKind(InvokeKind.Special); + } + + if (tryInvocationPlugin(methodScope, loopScope, invokeData, callTarget)) { + return true; + } + if (tryInline(methodScope, loopScope, invokeData, callTarget)) { + return true; + } + + if (methodScope.inlineInvokePlugin != null) { + methodScope.inlineInvokePlugin.notifyOfNoninlinedInvoke(new PENonAppendGraphBuilderContext(methodScope, invokeData.invoke), callTarget.targetMethod(), invokeData.invoke); + } + return false; + } + + protected boolean tryInvocationPlugin(PEMethodScope methodScope, LoopScope loopScope, InvokeData invokeData, MethodCallTargetNode callTarget) { + if (methodScope.invocationPlugins == null) { + return false; + } + + Invoke invoke = invokeData.invoke; + + ResolvedJavaMethod targetMethod = callTarget.targetMethod(); + InvocationPlugin invocationPlugin = methodScope.invocationPlugins.lookupInvocation(targetMethod); + if (invocationPlugin == null) { + return false; + } + + ValueNode[] arguments = callTarget.arguments().toArray(new ValueNode[0]); + FixedWithNextNode invokePredecessor = (FixedWithNextNode) invoke.asNode().predecessor(); + + /* Remove invoke from graph so that invocation plugin can append nodes to the predecessor. */ + invoke.asNode().replaceAtPredecessor(null); + + PEMethodScope inlineScope = new PEMethodScope(methodScope.graph, methodScope, loopScope, null, targetMethod, invokeData, methodScope.inliningDepth + 1, methodScope.loopExplosionPlugin, + methodScope.invocationPlugins, methodScope.inlineInvokePlugin, null, arguments); + PEAppendGraphBuilderContext graphBuilderContext = new PEAppendGraphBuilderContext(inlineScope, invokePredecessor); + InvocationPluginReceiver invocationPluginReceiver = new InvocationPluginReceiver(graphBuilderContext); + + if (invocationPlugin.execute(graphBuilderContext, targetMethod, invocationPluginReceiver.init(targetMethod, arguments), arguments)) { + + if (graphBuilderContext.lastInstr != null) { + registerNode(loopScope, invokeData.invokeOrderId, graphBuilderContext.pushedNode, true, true); + invoke.asNode().replaceAtUsages(graphBuilderContext.pushedNode); + graphBuilderContext.lastInstr.setNext(nodeAfterInvoke(methodScope, loopScope, invokeData)); + } else { + assert graphBuilderContext.pushedNode == null : "Why push a node when the invoke does not return anyway?"; + invoke.asNode().replaceAtUsages(null); + } + + deleteInvoke(invoke); + return true; + + } else { + /* Intrinsification failed, restore original state: invoke is in Graph. */ + invokePredecessor.setNext(invoke.asNode()); + return false; + } + } + + protected boolean tryInline(PEMethodScope methodScope, LoopScope loopScope, InvokeData invokeData, MethodCallTargetNode callTarget) { + if (methodScope.inlineInvokePlugin == null || !callTarget.invokeKind().isDirect()) { + return false; + } + + ResolvedJavaMethod targetMethod = callTarget.targetMethod(); + if (!targetMethod.canBeInlined()) { + return false; + } + + ValueNode[] arguments = callTarget.arguments().toArray(new ValueNode[0]); + GraphBuilderContext graphBuilderContext = new PENonAppendGraphBuilderContext(methodScope, invokeData.invoke); + InlineInfo inlineInfo = methodScope.inlineInvokePlugin.getInlineInfo(graphBuilderContext, targetMethod, arguments, callTarget.returnType()); + if (inlineInfo == null) { + return false; + } + assert !inlineInfo.isIntrinsic && !inlineInfo.isReplacement : "not supported"; + + ResolvedJavaMethod inlineMethod = inlineInfo.methodToInline; + EncodedGraph graphToInline = lookupEncodedGraph(inlineMethod); + if (graphToInline == null) { + return false; + } + + Invoke invoke = invokeData.invoke; + FixedNode invokeNode = invoke.asNode(); + FixedWithNextNode predecessor = (FixedWithNextNode) invokeNode.predecessor(); + invokeNode.replaceAtPredecessor(null); + + PEMethodScope inlineScope = new PEMethodScope(methodScope.graph, methodScope, loopScope, graphToInline, inlineMethod, invokeData, methodScope.inliningDepth + 1, + methodScope.loopExplosionPlugin, methodScope.invocationPlugins, methodScope.inlineInvokePlugin, null, arguments); + /* Do the actual inlining by decoding the inlineMethod */ + decode(inlineScope, predecessor); + + ValueNode exceptionValue = null; + if (inlineScope.unwindNode != null) { + exceptionValue = inlineScope.unwindNode.exception(); + } + UnwindNode unwindNode = inlineScope.unwindNode; + + if (invoke instanceof InvokeWithExceptionNode) { + InvokeWithExceptionNode invokeWithException = ((InvokeWithExceptionNode) invoke); + assert invokeWithException.next() == null; + assert invokeWithException.exceptionEdge() == null; + + if (unwindNode != null) { + assert unwindNode.predecessor() != null; + Node n = makeStubNode(methodScope, loopScope, invokeData.exceptionNextOrderId); + unwindNode.replaceAndDelete(n); + } + + } else { + if (unwindNode != null && !unwindNode.isDeleted()) { + DeoptimizeNode deoptimizeNode = methodScope.graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateRecompile, DeoptimizationReason.NotCompiledExceptionHandler)); + unwindNode.replaceAndDelete(deoptimizeNode); + } + } + + assert invoke.next() == null; + + ValueNode returnValue; + List returnNodes = inlineScope.returnNodes; + if (!returnNodes.isEmpty()) { + FixedNode n; + n = nodeAfterInvoke(methodScope, loopScope, invokeData); + if (returnNodes.size() == 1) { + ReturnNode returnNode = returnNodes.get(0); + returnValue = returnNode.result(); + returnNode.replaceAndDelete(n); + } else { + AbstractMergeNode merge = methodScope.graph.add(new MergeNode()); + merge.setStateAfter((FrameState) ensureNodeCreated(methodScope, loopScope, invokeData.stateAfterOrderId)); + returnValue = InliningUtil.mergeReturns(merge, returnNodes, null); + merge.setNext(n); + } + } else { + returnValue = null; + } + invokeNode.replaceAtUsages(returnValue); + + /* + * Usage the handles that we have on the return value and the exception to update the + * orderId->Node table. + */ + registerNode(loopScope, invokeData.invokeOrderId, returnValue, true, true); + if (invoke instanceof InvokeWithExceptionNode) { + registerNode(loopScope, invokeData.exceptionOrderId, exceptionValue, true, true); + } + if (inlineScope.exceptionPlaceholderNode != null) { + inlineScope.exceptionPlaceholderNode.replaceAndDelete(exceptionValue); + } + deleteInvoke(invoke); + + methodScope.inlineInvokePlugin.postInline(inlineMethod); + return true; + } + + public FixedNode nodeAfterInvoke(PEMethodScope methodScope, LoopScope loopScope, InvokeData invokeData) { + FixedNode n; + if (invokeData.invoke instanceof InvokeWithExceptionNode) { + n = makeStubNode(methodScope, loopScope, invokeData.nextNextOrderId); + } else { + n = makeStubNode(methodScope, loopScope, invokeData.nextOrderId); + } + return n; + } + + private static void deleteInvoke(Invoke invoke) { + /* + * Clean up unused nodes. We cannot just call killCFG on the invoke node because that can + * kill too much: nodes that are decoded later can use values that appear unused by now. + */ + FrameState frameState = invoke.stateAfter(); + invoke.asNode().safeDelete(); + assert invoke.callTarget() == null : "must not have been added to the graph yet"; + if (frameState != null && frameState.hasNoUsages()) { + frameState.safeDelete(); + } + } + + protected abstract EncodedGraph lookupEncodedGraph(ResolvedJavaMethod method); + + @Override + protected Node handleFloatingNodeBeforeAdd(MethodScope s, LoopScope loopScope, Node node) { + PEMethodScope methodScope = (PEMethodScope) s; + + if (node instanceof ParameterNode) { + if (methodScope.arguments != null) { + Node result = methodScope.arguments[((ParameterNode) node).index()]; + assert result != null; + return result; + + } else if (methodScope.parameterPlugin != null) { + GraphBuilderContext graphBuilderContext = new PENonAppendGraphBuilderContext(methodScope, null); + Node result = methodScope.parameterPlugin.interceptParameter(graphBuilderContext, ((ParameterNode) node).index(), ((ParameterNode) node).stamp()); + if (result != null) { + return result; + } + } + + } + + return super.handleFloatingNodeBeforeAdd(methodScope, loopScope, node); + } + + protected void ensureOuterStateDecoded(PEMethodScope methodScope) { + if (methodScope.outerState == null && methodScope.caller != null) { + FrameState stateAtReturn = methodScope.invokeData.invoke.stateAfter(); + if (stateAtReturn == null) { + stateAtReturn = (FrameState) decodeFloatingNode(methodScope.caller, methodScope.callerLoopScope, methodScope.invokeData.stateAfterOrderId); + } + + Kind invokeReturnKind = methodScope.invokeData.invoke.asNode().getKind(); + FrameState outerState = stateAtReturn.duplicateModified(methodScope.graph, methodScope.invokeData.invoke.bci(), stateAtReturn.rethrowException(), true, invokeReturnKind); + + /* + * When the encoded graph has methods inlining, we can already have a proper caller + * state. If not, we set the caller state here. + */ + if (outerState.outerFrameState() == null && methodScope.caller != null) { + ensureOuterStateDecoded(methodScope.caller); + outerState.setOuterFrameState(methodScope.caller.outerState); + } + methodScope.outerState = outerState; + } + } + + protected void ensureStateAfterDecoded(PEMethodScope methodScope) { + if (methodScope.invokeData.invoke.stateAfter() == null) { + methodScope.invokeData.invoke.setStateAfter((FrameState) ensureNodeCreated(methodScope.caller, methodScope.callerLoopScope, methodScope.invokeData.stateAfterOrderId)); + } + } + + protected void ensureExceptionStateDecoded(PEMethodScope methodScope) { + if (methodScope.exceptionState == null && methodScope.caller != null && methodScope.invokeData.invoke instanceof InvokeWithExceptionNode) { + ensureStateAfterDecoded(methodScope); + + assert methodScope.exceptionPlaceholderNode == null; + methodScope.exceptionPlaceholderNode = methodScope.graph.add(new ExceptionPlaceholderNode()); + registerNode(methodScope.callerLoopScope, methodScope.invokeData.exceptionOrderId, methodScope.exceptionPlaceholderNode, false, false); + FrameState exceptionState = (FrameState) ensureNodeCreated(methodScope.caller, methodScope.callerLoopScope, methodScope.invokeData.exceptionStateOrderId); + + if (exceptionState.outerFrameState() == null && methodScope.caller != null) { + ensureOuterStateDecoded(methodScope.caller); + exceptionState.setOuterFrameState(methodScope.caller.outerState); + } + methodScope.exceptionState = exceptionState; + } + } + + @Override + protected Node handleFloatingNodeAfterAdd(MethodScope s, LoopScope loopScope, Node node) { + PEMethodScope methodScope = (PEMethodScope) s; + + if (methodScope.isInlinedMethod()) { + if (node instanceof SimpleInfopointNode) { + methodScope.bytecodePosition = InliningUtil.processSimpleInfopoint(methodScope.invokeData.invoke, (SimpleInfopointNode) node, methodScope.bytecodePosition); + return node; + + } else if (node instanceof FrameState) { + FrameState frameState = (FrameState) node; + + ensureOuterStateDecoded(methodScope); + if (frameState.bci < 0) { + ensureExceptionStateDecoded(methodScope); + } + return InliningUtil.processFrameState(frameState, methodScope.invokeData.invoke, methodScope.method, methodScope.exceptionState, methodScope.outerState, true); + + } else if (node instanceof MonitorIdNode) { + ensureOuterStateDecoded(methodScope); + InliningUtil.processMonitorId(methodScope.outerState, (MonitorIdNode) node); + return node; + } + } + + return node; + } +} diff -r d6d9631eb057 -r fc6fd02ecf95 graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/SnippetCounter.java --- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/SnippetCounter.java Tue Apr 21 17:02:06 2015 -0700 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/SnippetCounter.java Sun Apr 26 20:50:30 2015 -0700 @@ -24,16 +24,9 @@ //JaCoCo Exclude -import static com.oracle.graal.compiler.common.UnsafeAccess.*; - import java.io.*; import java.util.*; -import com.oracle.graal.api.meta.*; -import com.oracle.graal.api.replacements.*; -import com.oracle.graal.compiler.common.*; -import com.oracle.graal.word.*; - /** * A counter that can be safely {@linkplain #inc() incremented} from within a snippet for gathering * snippet specific metrics. @@ -94,15 +87,6 @@ private final String description; private long value; - @Fold - private static int countOffset() { - try { - return (int) unsafe.objectFieldOffset(SnippetCounter.class.getDeclaredField("value")); - } catch (Exception e) { - throw new GraalInternalError(e); - } - } - /** * Creates a counter. * @@ -128,20 +112,12 @@ } /** - * We do not want to use the {@link LocationIdentity} of the {@link #value} field, so that the - * usage in snippets is always possible. If a method accesses the counter via the field and the - * snippet, the result might not be correct though. - */ - protected static final LocationIdentity SNIPPET_COUNTER_LOCATION = NamedLocationIdentity.mutable("SnippetCounter"); - - /** * Increments the value of this counter. This method can be safely used in a snippet if it is * invoked on a compile-time constant {@link SnippetCounter} object. */ public void inc() { if (group != null) { - long loadedValue = ObjectAccess.readLong(this, countOffset(), SNIPPET_COUNTER_LOCATION); - ObjectAccess.writeLong(this, countOffset(), loadedValue + 1, SNIPPET_COUNTER_LOCATION); + SnippetCounterNode.increment(this); } } @@ -152,6 +128,14 @@ return value; } + @Override + public String toString() { + if (group != null) { + return "SnippetCounter-" + group.name + ":" + name; + } + return super.toString(); + } + /** * Prints all the counter groups to a given stream. */ diff -r d6d9631eb057 -r fc6fd02ecf95 graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/SnippetCounterNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/SnippetCounterNode.java Sun Apr 26 20:50:30 2015 -0700 @@ -0,0 +1,131 @@ +/* + * 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.replacements; + +import static com.oracle.graal.compiler.common.UnsafeAccess.*; +import static com.oracle.graal.replacements.SnippetTemplate.*; + +import com.oracle.graal.api.code.*; +import com.oracle.graal.api.meta.*; +import com.oracle.graal.api.replacements.*; +import com.oracle.graal.compiler.common.*; +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.*; +import com.oracle.graal.phases.util.*; +import com.oracle.graal.replacements.SnippetTemplate.AbstractTemplates; +import com.oracle.graal.replacements.SnippetTemplate.Arguments; +import com.oracle.graal.replacements.SnippetTemplate.SnippetInfo; +import com.oracle.graal.word.*; + +/** + * This node can be used to add a counter to the code that will estimate the dynamic number of calls + * by adding an increment to the compiled code. This should of course only be used for + * debugging/testing purposes. + * + * A unique counter will be created for each unique name passed to the constructor. Depending on the + * value of withContext, the name of the root method is added to the counter's name. + */ +@NodeInfo +public class SnippetCounterNode extends FixedWithNextNode implements Lowerable { + + public static final NodeClass TYPE = NodeClass.create(SnippetCounterNode.class); + + @Input protected ValueNode increment; + + protected final SnippetCounter counter; + + public SnippetCounterNode(SnippetCounter counter, ValueNode increment) { + super(TYPE, StampFactory.forVoid()); + this.counter = counter; + this.increment = increment; + } + + public SnippetCounter getCounter() { + return counter; + } + + public ValueNode getIncrement() { + return increment; + } + + @NodeIntrinsic + public static native void add(@ConstantNodeParameter SnippetCounter counter, int increment); + + public static void increment(@ConstantNodeParameter SnippetCounter counter) { + add(counter, 1); + } + + public void lower(LoweringTool tool) { + if (graph().getGuardsStage().areFrameStatesAtDeopts()) { + SnippetCounterSnippets.Templates templates = tool.getReplacements().getSnippetTemplateCache(SnippetCounterSnippets.Templates.class); + templates.lower(this, tool); + } + } + + static class SnippetCounterSnippets implements Snippets { + + @Fold + private static int countOffset() { + try { + return (int) unsafe.objectFieldOffset(SnippetCounter.class.getDeclaredField("value")); + } catch (Exception e) { + throw new GraalInternalError(e); + } + } + + /** + * We do not want to use the {@link LocationIdentity} of the {@link SnippetCounter#value} + * field, so that the usage in snippets is always possible. If a method accesses the counter + * via the field and the snippet, the result might not be correct though. + */ + protected static final LocationIdentity SNIPPET_COUNTER_LOCATION = NamedLocationIdentity.mutable("SnippetCounter"); + + @Snippet + public static void add(SnippetCounter counter, int increment) { + long loadedValue = ObjectAccess.readLong(counter, countOffset(), SNIPPET_COUNTER_LOCATION); + ObjectAccess.writeLong(counter, countOffset(), loadedValue + increment, SNIPPET_COUNTER_LOCATION); + } + + public static class Templates extends AbstractTemplates { + + private final SnippetInfo add = snippet(SnippetCounterSnippets.class, "add", SNIPPET_COUNTER_LOCATION); + + public Templates(Providers providers, SnippetReflectionProvider snippetReflection, TargetDescription target) { + super(providers, snippetReflection, target); + } + + public void lower(SnippetCounterNode counter, LoweringTool tool) { + StructuredGraph graph = counter.graph(); + Arguments args = new Arguments(add, graph.getGuardsStage(), tool.getLoweringStage()); + args.addConst("counter", counter.getCounter()); + args.add("increment", counter.getIncrement()); + + template(args).instantiate(providers.getMetaAccess(), counter, DEFAULT_REPLACER, args); + } + } + } + +} diff -r d6d9631eb057 -r fc6fd02ecf95 graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/SnippetTemplate.java --- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/SnippetTemplate.java Tue Apr 21 17:02:06 2015 -0700 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/SnippetTemplate.java Sun Apr 26 20:50:30 2015 -0700 @@ -23,7 +23,6 @@ package com.oracle.graal.replacements; import static com.oracle.graal.api.meta.LocationIdentity.*; -import static com.oracle.graal.compiler.common.GraalOptions.*; import static com.oracle.graal.debug.Debug.*; import static com.oracle.graal.phases.common.DeadCodeEliminationPhase.Optionality.*; import static com.oracle.graal.replacements.SnippetTemplate.AbstractTemplates.*; @@ -1016,14 +1015,6 @@ // node can only lower to a ANY_LOCATION kill if the replacee also kills ANY_LOCATION assert !kills.contains(any()) : "snippet graph contains a kill to ANY_LOCATION, but replacee (" + replacee + ") doesn't kill ANY_LOCATION. kills: " + kills; - if (SnippetCounters.getValue()) { - /* - * accesses to snippet counters are artificially introduced and violate the memory - * semantics. - */ - kills.remove(SnippetCounter.SNIPPET_COUNTER_LOCATION); - } - /* * Kills to private locations are safe, since there can be no floating read to these * locations except reads that are introduced by the snippet itself or related snippets in diff -r d6d9631eb057 -r fc6fd02ecf95 graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/CachingPEGraphDecoder.java --- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/CachingPEGraphDecoder.java Tue Apr 21 17:02:06 2015 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,84 +0,0 @@ -/* - * 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.truffle; - -import java.util.*; - -import com.oracle.graal.api.code.*; -import com.oracle.graal.api.meta.*; -import com.oracle.graal.debug.*; -import com.oracle.graal.graphbuilderconf.*; -import com.oracle.graal.java.*; -import com.oracle.graal.nodes.*; -import com.oracle.graal.nodes.StructuredGraph.AllowAssumptions; -import com.oracle.graal.phases.common.*; -import com.oracle.graal.phases.tiers.*; -import com.oracle.graal.phases.util.*; - -/** - * A graph decoder that provides all necessary encoded graphs on-the-fly (by parsing the methods and - * encoding the graphs). - */ -public class CachingPEGraphDecoder extends PEGraphDecoder { - - private final Providers providers; - private final GraphBuilderConfiguration graphBuilderConfig; - private final AllowAssumptions allowAssumptions; - private final Map graphCache; - - public CachingPEGraphDecoder(Providers providers, GraphBuilderConfiguration graphBuilderConfig, AllowAssumptions allowAssumptions, Architecture architecture) { - super(providers.getMetaAccess(), providers.getConstantReflection(), providers.getStampProvider(), architecture); - - this.providers = providers; - this.graphBuilderConfig = graphBuilderConfig; - this.allowAssumptions = allowAssumptions; - this.graphCache = new HashMap<>(); - } - - private EncodedGraph createGraph(ResolvedJavaMethod method) { - StructuredGraph graph = new StructuredGraph(method, allowAssumptions); - try (Debug.Scope scope = Debug.scope("createGraph", graph)) { - - new GraphBuilderPhase.Instance(providers.getMetaAccess(), providers.getStampProvider(), providers.getConstantReflection(), graphBuilderConfig, TruffleCompilerImpl.Optimizations, null).apply(graph); - - PhaseContext context = new PhaseContext(providers); - new CanonicalizerPhase().apply(graph, context); - - EncodedGraph encodedGraph = GraphEncoder.encodeSingleGraph(graph, architecture); - graphCache.put(method, encodedGraph); - return encodedGraph; - - } catch (Throwable ex) { - throw Debug.handle(ex); - } - } - - @Override - protected EncodedGraph lookupEncodedGraph(ResolvedJavaMethod method) { - EncodedGraph result = graphCache.get(method); - if (result == null && method.hasBytecodes()) { - result = createGraph(method); - } - return result; - } -} diff -r d6d9631eb057 -r fc6fd02ecf95 graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PEGraphDecoder.java --- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PEGraphDecoder.java Tue Apr 21 17:02:06 2015 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,725 +0,0 @@ -/* - * 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.truffle; - -import static com.oracle.graal.compiler.common.GraalInternalError.*; -import static com.oracle.graal.java.AbstractBytecodeParser.Options.*; - -import java.util.*; - -import com.oracle.graal.api.code.*; -import com.oracle.graal.api.meta.*; -import com.oracle.graal.compiler.common.type.*; -import com.oracle.graal.graph.*; -import com.oracle.graal.graph.spi.*; -import com.oracle.graal.graphbuilderconf.*; -import com.oracle.graal.graphbuilderconf.InlineInvokePlugin.InlineInfo; -import com.oracle.graal.graphbuilderconf.InvocationPlugins.InvocationPluginReceiver; -import com.oracle.graal.java.*; -import com.oracle.graal.nodeinfo.*; -import com.oracle.graal.nodes.*; -import com.oracle.graal.nodes.CallTargetNode.InvokeKind; -import com.oracle.graal.nodes.extended.*; -import com.oracle.graal.nodes.java.*; -import com.oracle.graal.nodes.spi.*; -import com.oracle.graal.nodes.util.*; -import com.oracle.graal.phases.common.inlining.*; - -/** - * A graph decoder that performs partial evaluation, i.e., that performs method inlining and - * canonicalization/simplification of nodes during decoding. - * - * Inlining and loop explosion are configured via the plugin mechanism also used by the - * {@link GraphBuilderPhase}. However, not all callback methods defined in - * {@link GraphBuilderContext} are available since decoding is more limited than graph building. - * - * The standard {@link Canonicalizable#canonical node canonicalization} interface is used to - * canonicalize nodes during decoding. Additionally, {@link IfNode branches} and - * {@link IntegerSwitchNode switches} with constant conditions are simplified. - */ -public abstract class PEGraphDecoder extends GraphDecoder { - - protected final MetaAccessProvider metaAccess; - protected final ConstantReflectionProvider constantReflection; - protected final StampProvider stampProvider; - - protected class PEMethodScope extends MethodScope { - /** The state of the caller method. Only non-null during method inlining. */ - protected final PEMethodScope caller; - protected final LoopScope callerLoopScope; - protected final ResolvedJavaMethod method; - protected final InvokeData invokeData; - protected final int inliningDepth; - - protected final LoopExplosionPlugin loopExplosionPlugin; - protected final InvocationPlugins invocationPlugins; - protected final InlineInvokePlugin inlineInvokePlugin; - protected final ParameterPlugin parameterPlugin; - protected final ValueNode[] arguments; - - protected FrameState outerState; - protected FrameState exceptionState; - protected ExceptionPlaceholderNode exceptionPlaceholderNode; - protected BytecodePosition bytecodePosition; - - protected PEMethodScope(StructuredGraph targetGraph, PEMethodScope caller, LoopScope callerLoopScope, EncodedGraph encodedGraph, ResolvedJavaMethod method, InvokeData invokeData, - int inliningDepth, LoopExplosionPlugin loopExplosionPlugin, InvocationPlugins invocationPlugins, InlineInvokePlugin inlineInvokePlugin, ParameterPlugin parameterPlugin, - ValueNode[] arguments) { - super(targetGraph, encodedGraph, loopExplosionKind(method, loopExplosionPlugin)); - - this.caller = caller; - this.callerLoopScope = callerLoopScope; - this.method = method; - this.invokeData = invokeData; - this.inliningDepth = inliningDepth; - this.loopExplosionPlugin = loopExplosionPlugin; - this.invocationPlugins = invocationPlugins; - this.inlineInvokePlugin = inlineInvokePlugin; - this.parameterPlugin = parameterPlugin; - this.arguments = arguments; - } - - public boolean isInlinedMethod() { - return caller != null; - } - } - - protected class PECanonicalizerTool implements CanonicalizerTool { - @Override - public MetaAccessProvider getMetaAccess() { - return metaAccess; - } - - @Override - public ConstantReflectionProvider getConstantReflection() { - return constantReflection; - } - - @Override - public boolean canonicalizeReads() { - return true; - } - - @Override - public boolean allUsagesAvailable() { - return false; - } - } - - protected class PENonAppendGraphBuilderContext implements GraphBuilderContext { - protected final PEMethodScope methodScope; - protected final Invoke invoke; - - public PENonAppendGraphBuilderContext(PEMethodScope methodScope, Invoke invoke) { - this.methodScope = methodScope; - this.invoke = invoke; - } - - @Override - public BailoutException bailout(String string) { - throw new BailoutException(string); - } - - @Override - public StampProvider getStampProvider() { - return stampProvider; - } - - @Override - public MetaAccessProvider getMetaAccess() { - return metaAccess; - } - - @Override - public ConstantReflectionProvider getConstantReflection() { - return constantReflection; - } - - @Override - public StructuredGraph getGraph() { - return methodScope.graph; - } - - @Override - public int getDepth() { - return methodScope.inliningDepth; - } - - @Override - public Replacement getReplacement() { - return null; - } - - @Override - public T append(T value) { - throw unimplemented(); - } - - @Override - public T recursiveAppend(T value) { - throw unimplemented(); - } - - @Override - public void push(Kind kind, ValueNode value) { - throw unimplemented(); - } - - @Override - public void handleReplacedInvoke(InvokeKind invokeKind, ResolvedJavaMethod targetMethod, ValueNode[] args) { - throw unimplemented(); - } - - @Override - public void intrinsify(ResolvedJavaMethod targetMethod, ResolvedJavaMethod substitute, ValueNode[] args) { - throw unimplemented(); - } - - @Override - public FrameState createStateAfter() { - throw unimplemented(); - } - - @Override - public GraphBuilderContext getParent() { - throw unimplemented(); - } - - @Override - public ResolvedJavaMethod getMethod() { - throw unimplemented(); - } - - @Override - public int bci() { - return invoke.bci(); - } - - @Override - public InvokeKind getInvokeKind() { - throw unimplemented(); - } - - @Override - public JavaType getInvokeReturnType() { - throw unimplemented(); - } - } - - protected class PEAppendGraphBuilderContext extends PENonAppendGraphBuilderContext { - protected FixedWithNextNode lastInstr; - protected ValueNode pushedNode; - - public PEAppendGraphBuilderContext(PEMethodScope inlineScope, FixedWithNextNode lastInstr) { - super(inlineScope, inlineScope.invokeData.invoke); - this.lastInstr = lastInstr; - } - - @Override - public void push(Kind kind, ValueNode value) { - if (pushedNode != null) { - throw unimplemented("Only one push is supported"); - } - pushedNode = value; - } - - @Override - public FrameState createStateAfter() { - Node stateAfter = decodeFloatingNode(methodScope.caller, methodScope.callerLoopScope, methodScope.invokeData.stateAfterOrderId); - getGraph().add(stateAfter); - return (FrameState) handleFloatingNodeAfterAdd(methodScope.caller, methodScope.callerLoopScope, stateAfter); - } - - @Override - public T append(T v) { - if (v.graph() != null) { - return v; - } - T added = getGraph().addOrUnique(v); - if (added == v) { - updateLastInstruction(v); - } - return added; - } - - @Override - public T recursiveAppend(T v) { - if (v.graph() != null) { - return v; - } - T added = getGraph().addOrUniqueWithInputs(v); - if (added == v) { - updateLastInstruction(v); - } - return added; - } - - private void updateLastInstruction(T v) { - if (v instanceof FixedNode) { - FixedNode fixedNode = (FixedNode) v; - lastInstr.setNext(fixedNode); - if (fixedNode instanceof FixedWithNextNode) { - FixedWithNextNode fixedWithNextNode = (FixedWithNextNode) fixedNode; - assert fixedWithNextNode.next() == null : "cannot append instruction to instruction which isn't end"; - lastInstr = fixedWithNextNode; - } else { - lastInstr = null; - } - } - } - } - - @NodeInfo - static class ExceptionPlaceholderNode extends ValueNode { - public static final NodeClass TYPE = NodeClass.create(ExceptionPlaceholderNode.class); - - public ExceptionPlaceholderNode() { - super(TYPE, StampFactory.object()); - } - } - - public PEGraphDecoder(MetaAccessProvider metaAccess, ConstantReflectionProvider constantReflection, StampProvider stampProvider, Architecture architecture) { - super(architecture); - this.metaAccess = metaAccess; - this.constantReflection = constantReflection; - this.stampProvider = stampProvider; - } - - protected static LoopExplosionKind loopExplosionKind(ResolvedJavaMethod method, LoopExplosionPlugin loopExplosionPlugin) { - if (loopExplosionPlugin == null) { - return LoopExplosionKind.NONE; - } else if (loopExplosionPlugin.shouldMergeExplosions(method)) { - return LoopExplosionKind.MERGE_EXPLODE; - } else if (loopExplosionPlugin.shouldExplodeLoops(method)) { - return LoopExplosionKind.FULL_EXPLODE; - } else { - return LoopExplosionKind.NONE; - } - } - - public void decode(StructuredGraph targetGraph, ResolvedJavaMethod method, LoopExplosionPlugin loopExplosionPlugin, InvocationPlugins invocationPlugins, InlineInvokePlugin inlineInvokePlugin, - ParameterPlugin parameterPlugin) { - PEMethodScope methodScope = new PEMethodScope(targetGraph, null, null, lookupEncodedGraph(method), method, null, 0, loopExplosionPlugin, invocationPlugins, inlineInvokePlugin, - parameterPlugin, null); - decode(methodScope, null); - cleanupGraph(methodScope, null); - methodScope.graph.verify(); - } - - @Override - protected void cleanupGraph(MethodScope methodScope, Graph.Mark start) { - GraphBuilderPhase.connectLoopEndToBegin(methodScope.graph); - super.cleanupGraph(methodScope, start); - } - - @Override - protected void checkLoopExplosionIteration(MethodScope s, LoopScope loopScope) { - PEMethodScope methodScope = (PEMethodScope) s; - - if (loopScope.loopIteration > MaximumLoopExplosionCount.getValue()) { - String message = "too many loop explosion iterations - does the explosion not terminate for method " + methodScope.method + "?"; - if (FailedLoopExplosionIsFatal.getValue()) { - throw new RuntimeException(message); - } else { - throw new BailoutException(message); - } - } - } - - @Override - protected void handleInvoke(MethodScope s, LoopScope loopScope, InvokeData invokeData) { - PEMethodScope methodScope = (PEMethodScope) s; - - /* - * Decode the call target, but do not add it to the graph yet. This avoids adding usages for - * all the arguments, which are expensive to remove again when we can inline the method. - */ - assert invokeData.invoke.callTarget() == null : "callTarget edge is ignored during decoding of Invoke"; - CallTargetNode callTarget = (CallTargetNode) decodeFloatingNode(methodScope, loopScope, invokeData.callTargetOrderId); - if (!(callTarget instanceof MethodCallTargetNode) || !trySimplifyInvoke(methodScope, loopScope, invokeData, (MethodCallTargetNode) callTarget)) { - - /* We know that we need an invoke, so now we can add the call target to the graph. */ - methodScope.graph.add(callTarget); - registerNode(loopScope, invokeData.callTargetOrderId, callTarget, false, false); - super.handleInvoke(methodScope, loopScope, invokeData); - } - } - - protected boolean trySimplifyInvoke(PEMethodScope methodScope, LoopScope loopScope, InvokeData invokeData, MethodCallTargetNode callTarget) { - // attempt to devirtualize the call - ResolvedJavaMethod specialCallTarget = MethodCallTargetNode.findSpecialCallTarget(callTarget.invokeKind(), callTarget.receiver(), callTarget.targetMethod(), invokeData.contextType); - if (specialCallTarget != null) { - callTarget.setTargetMethod(specialCallTarget); - callTarget.setInvokeKind(InvokeKind.Special); - } - - if (tryInvocationPlugin(methodScope, loopScope, invokeData, callTarget)) { - return true; - } - if (tryInline(methodScope, loopScope, invokeData, callTarget)) { - return true; - } - - if (methodScope.inlineInvokePlugin != null) { - methodScope.inlineInvokePlugin.notifyOfNoninlinedInvoke(new PENonAppendGraphBuilderContext(methodScope, invokeData.invoke), callTarget.targetMethod(), invokeData.invoke); - } - return false; - } - - protected boolean tryInvocationPlugin(PEMethodScope methodScope, LoopScope loopScope, InvokeData invokeData, MethodCallTargetNode callTarget) { - if (methodScope.invocationPlugins == null) { - return false; - } - - Invoke invoke = invokeData.invoke; - - ResolvedJavaMethod targetMethod = callTarget.targetMethod(); - InvocationPlugin invocationPlugin = methodScope.invocationPlugins.lookupInvocation(targetMethod); - if (invocationPlugin == null) { - return false; - } - - ValueNode[] arguments = callTarget.arguments().toArray(new ValueNode[0]); - FixedWithNextNode invokePredecessor = (FixedWithNextNode) invoke.asNode().predecessor(); - - /* Remove invoke from graph so that invocation plugin can append nodes to the predecessor. */ - invoke.asNode().replaceAtPredecessor(null); - - PEMethodScope inlineScope = new PEMethodScope(methodScope.graph, methodScope, loopScope, null, targetMethod, invokeData, methodScope.inliningDepth + 1, methodScope.loopExplosionPlugin, - methodScope.invocationPlugins, methodScope.inlineInvokePlugin, null, arguments); - PEAppendGraphBuilderContext graphBuilderContext = new PEAppendGraphBuilderContext(inlineScope, invokePredecessor); - InvocationPluginReceiver invocationPluginReceiver = new InvocationPluginReceiver(graphBuilderContext); - - if (invocationPlugin.execute(graphBuilderContext, targetMethod, invocationPluginReceiver.init(targetMethod, arguments), arguments)) { - - if (graphBuilderContext.lastInstr != null) { - registerNode(loopScope, invokeData.invokeOrderId, graphBuilderContext.pushedNode, true, true); - invoke.asNode().replaceAtUsages(graphBuilderContext.pushedNode); - graphBuilderContext.lastInstr.setNext(nodeAfterInvoke(methodScope, loopScope, invokeData)); - } else { - assert graphBuilderContext.pushedNode == null : "Why push a node when the invoke does not return anyway?"; - invoke.asNode().replaceAtUsages(null); - } - - deleteInvoke(invoke); - return true; - - } else { - /* Intrinsification failed, restore original state: invoke is in Graph. */ - invokePredecessor.setNext(invoke.asNode()); - return false; - } - } - - protected boolean tryInline(PEMethodScope methodScope, LoopScope loopScope, InvokeData invokeData, MethodCallTargetNode callTarget) { - if (methodScope.inlineInvokePlugin == null || !callTarget.invokeKind().isDirect()) { - return false; - } - - ResolvedJavaMethod targetMethod = callTarget.targetMethod(); - if (!targetMethod.canBeInlined()) { - return false; - } - - ValueNode[] arguments = callTarget.arguments().toArray(new ValueNode[0]); - GraphBuilderContext graphBuilderContext = new PENonAppendGraphBuilderContext(methodScope, invokeData.invoke); - InlineInfo inlineInfo = methodScope.inlineInvokePlugin.getInlineInfo(graphBuilderContext, targetMethod, arguments, callTarget.returnType()); - if (inlineInfo == null) { - return false; - } - assert !inlineInfo.isIntrinsic && !inlineInfo.isReplacement : "not supported"; - - ResolvedJavaMethod inlineMethod = inlineInfo.methodToInline; - EncodedGraph graphToInline = lookupEncodedGraph(inlineMethod); - if (graphToInline == null) { - return false; - } - - Invoke invoke = invokeData.invoke; - FixedNode invokeNode = invoke.asNode(); - FixedWithNextNode predecessor = (FixedWithNextNode) invokeNode.predecessor(); - invokeNode.replaceAtPredecessor(null); - - PEMethodScope inlineScope = new PEMethodScope(methodScope.graph, methodScope, loopScope, graphToInline, inlineMethod, invokeData, methodScope.inliningDepth + 1, - methodScope.loopExplosionPlugin, methodScope.invocationPlugins, methodScope.inlineInvokePlugin, null, arguments); - /* Do the actual inlining by decoding the inlineMethod */ - decode(inlineScope, predecessor); - - ValueNode exceptionValue = null; - if (inlineScope.unwindNode != null) { - exceptionValue = inlineScope.unwindNode.exception(); - } - UnwindNode unwindNode = inlineScope.unwindNode; - - if (invoke instanceof InvokeWithExceptionNode) { - InvokeWithExceptionNode invokeWithException = ((InvokeWithExceptionNode) invoke); - assert invokeWithException.next() == null; - assert invokeWithException.exceptionEdge() == null; - - if (unwindNode != null) { - assert unwindNode.predecessor() != null; - Node n = makeStubNode(methodScope, loopScope, invokeData.exceptionNextOrderId); - unwindNode.replaceAndDelete(n); - } - - } else { - if (unwindNode != null && !unwindNode.isDeleted()) { - DeoptimizeNode deoptimizeNode = methodScope.graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateRecompile, DeoptimizationReason.NotCompiledExceptionHandler)); - unwindNode.replaceAndDelete(deoptimizeNode); - } - } - - assert invoke.next() == null; - - ValueNode returnValue; - List returnNodes = inlineScope.returnNodes; - if (!returnNodes.isEmpty()) { - FixedNode n; - n = nodeAfterInvoke(methodScope, loopScope, invokeData); - if (returnNodes.size() == 1) { - ReturnNode returnNode = returnNodes.get(0); - returnValue = returnNode.result(); - returnNode.replaceAndDelete(n); - } else { - AbstractMergeNode merge = methodScope.graph.add(new MergeNode()); - merge.setStateAfter((FrameState) ensureNodeCreated(methodScope, loopScope, invokeData.stateAfterOrderId)); - returnValue = InliningUtil.mergeReturns(merge, returnNodes, null); - merge.setNext(n); - } - } else { - returnValue = null; - } - invokeNode.replaceAtUsages(returnValue); - - /* - * Usage the handles that we have on the return value and the exception to update the - * orderId->Node table. - */ - registerNode(loopScope, invokeData.invokeOrderId, returnValue, true, true); - if (invoke instanceof InvokeWithExceptionNode) { - registerNode(loopScope, invokeData.exceptionOrderId, exceptionValue, true, true); - } - if (inlineScope.exceptionPlaceholderNode != null) { - inlineScope.exceptionPlaceholderNode.replaceAndDelete(exceptionValue); - } - deleteInvoke(invoke); - - methodScope.inlineInvokePlugin.postInline(inlineMethod); - return true; - } - - public FixedNode nodeAfterInvoke(PEMethodScope methodScope, LoopScope loopScope, InvokeData invokeData) { - FixedNode n; - if (invokeData.invoke instanceof InvokeWithExceptionNode) { - n = makeStubNode(methodScope, loopScope, invokeData.nextNextOrderId); - } else { - n = makeStubNode(methodScope, loopScope, invokeData.nextOrderId); - } - return n; - } - - private static void deleteInvoke(Invoke invoke) { - /* - * Clean up unused nodes. We cannot just call killCFG on the invoke node because that can - * kill too much: nodes that are decoded later can use values that appear unused by now. - */ - FrameState frameState = invoke.stateAfter(); - invoke.asNode().safeDelete(); - assert invoke.callTarget() == null : "must not have been added to the graph yet"; - if (frameState != null && frameState.hasNoUsages()) { - frameState.safeDelete(); - } - } - - protected abstract EncodedGraph lookupEncodedGraph(ResolvedJavaMethod method); - - @Override - protected void simplifyFixedNode(MethodScope s, LoopScope loopScope, int nodeOrderId, FixedNode node) { - PEMethodScope methodScope = (PEMethodScope) s; - - if (node instanceof IfNode) { - IfNode ifNode = (IfNode) node; - if (ifNode.condition() instanceof LogicNegationNode) { - ifNode.eliminateNegation(); - } - if (ifNode.condition() instanceof LogicConstantNode) { - boolean condition = ((LogicConstantNode) ifNode.condition()).getValue(); - AbstractBeginNode survivingSuccessor = ifNode.getSuccessor(condition); - AbstractBeginNode deadSuccessor = ifNode.getSuccessor(!condition); - - methodScope.graph.removeSplit(ifNode, survivingSuccessor); - assert deadSuccessor.next() == null : "must not be parsed yet"; - deadSuccessor.safeDelete(); - } - - } else if (node instanceof IntegerSwitchNode && ((IntegerSwitchNode) node).value().isConstant()) { - IntegerSwitchNode switchNode = (IntegerSwitchNode) node; - int value = switchNode.value().asJavaConstant().asInt(); - AbstractBeginNode survivingSuccessor = switchNode.successorAtKey(value); - List allSuccessors = switchNode.successors().snapshot(); - - methodScope.graph.removeSplit(switchNode, survivingSuccessor); - for (Node successor : allSuccessors) { - if (successor != survivingSuccessor) { - assert ((AbstractBeginNode) successor).next() == null : "must not be parsed yet"; - successor.safeDelete(); - } - } - - } else if (node instanceof Canonicalizable) { - Node canonical = ((Canonicalizable) node).canonical(new PECanonicalizerTool()); - if (canonical == null) { - /* - * This is a possible return value of canonicalization. However, we might need to - * add additional usages later on for which we need a node. Therefore, we just do - * nothing and leave the node in place. - */ - } else if (canonical != node) { - if (!canonical.isAlive()) { - assert !canonical.isDeleted(); - canonical = methodScope.graph.addOrUniqueWithInputs(canonical); - if (canonical instanceof FixedWithNextNode) { - methodScope.graph.addBeforeFixed(node, (FixedWithNextNode) canonical); - } - } - GraphUtil.unlinkFixedNode((FixedWithNextNode) node); - node.replaceAtUsages(canonical); - node.safeDelete(); - assert lookupNode(loopScope, nodeOrderId) == node; - registerNode(loopScope, nodeOrderId, canonical, true, false); - } - } - } - - @Override - protected Node handleFloatingNodeBeforeAdd(MethodScope s, LoopScope loopScope, Node node) { - PEMethodScope methodScope = (PEMethodScope) s; - - if (node instanceof ParameterNode) { - if (methodScope.arguments != null) { - Node result = methodScope.arguments[((ParameterNode) node).index()]; - assert result != null; - return result; - - } else if (methodScope.parameterPlugin != null) { - GraphBuilderContext graphBuilderContext = new PENonAppendGraphBuilderContext(methodScope, null); - Node result = methodScope.parameterPlugin.interceptParameter(graphBuilderContext, ((ParameterNode) node).index(), ((ParameterNode) node).stamp()); - if (result != null) { - return result; - } - } - - } else if (node instanceof Canonicalizable) { - Node canonical = ((Canonicalizable) node).canonical(new PECanonicalizerTool()); - if (canonical == null) { - /* - * This is a possible return value of canonicalization. However, we might need to - * add additional usages later on for which we need a node. Therefore, we just do - * nothing and leave the node in place. - */ - } else if (canonical != node) { - if (!canonical.isAlive()) { - assert !canonical.isDeleted(); - canonical = methodScope.graph.addOrUniqueWithInputs(canonical); - } - assert node.hasNoUsages(); - // methodScope.graph.replaceFloating((FloatingNode) node, canonical); - return canonical; - } - } - return node; - } - - protected void ensureOuterStateDecoded(PEMethodScope methodScope) { - if (methodScope.outerState == null && methodScope.caller != null) { - FrameState stateAtReturn = methodScope.invokeData.invoke.stateAfter(); - if (stateAtReturn == null) { - stateAtReturn = (FrameState) decodeFloatingNode(methodScope.caller, methodScope.callerLoopScope, methodScope.invokeData.stateAfterOrderId); - } - - Kind invokeReturnKind = methodScope.invokeData.invoke.asNode().getKind(); - FrameState outerState = stateAtReturn.duplicateModified(methodScope.graph, methodScope.invokeData.invoke.bci(), stateAtReturn.rethrowException(), true, invokeReturnKind); - - /* - * When the encoded graph has methods inlining, we can already have a proper caller - * state. If not, we set the caller state here. - */ - if (outerState.outerFrameState() == null && methodScope.caller != null) { - ensureOuterStateDecoded(methodScope.caller); - outerState.setOuterFrameState(methodScope.caller.outerState); - } - methodScope.outerState = outerState; - } - } - - protected void ensureStateAfterDecoded(PEMethodScope methodScope) { - if (methodScope.invokeData.invoke.stateAfter() == null) { - methodScope.invokeData.invoke.setStateAfter((FrameState) ensureNodeCreated(methodScope.caller, methodScope.callerLoopScope, methodScope.invokeData.stateAfterOrderId)); - } - } - - protected void ensureExceptionStateDecoded(PEMethodScope methodScope) { - if (methodScope.exceptionState == null && methodScope.caller != null && methodScope.invokeData.invoke instanceof InvokeWithExceptionNode) { - ensureStateAfterDecoded(methodScope); - - assert methodScope.exceptionPlaceholderNode == null; - methodScope.exceptionPlaceholderNode = methodScope.graph.add(new ExceptionPlaceholderNode()); - registerNode(methodScope.callerLoopScope, methodScope.invokeData.exceptionOrderId, methodScope.exceptionPlaceholderNode, false, false); - FrameState exceptionState = (FrameState) ensureNodeCreated(methodScope.caller, methodScope.callerLoopScope, methodScope.invokeData.exceptionStateOrderId); - - if (exceptionState.outerFrameState() == null && methodScope.caller != null) { - ensureOuterStateDecoded(methodScope.caller); - exceptionState.setOuterFrameState(methodScope.caller.outerState); - } - methodScope.exceptionState = exceptionState; - } - } - - @Override - protected Node handleFloatingNodeAfterAdd(MethodScope s, LoopScope loopScope, Node node) { - PEMethodScope methodScope = (PEMethodScope) s; - - if (methodScope.isInlinedMethod()) { - if (node instanceof SimpleInfopointNode) { - methodScope.bytecodePosition = InliningUtil.processSimpleInfopoint(methodScope.invokeData.invoke, (SimpleInfopointNode) node, methodScope.bytecodePosition); - return node; - - } else if (node instanceof FrameState) { - FrameState frameState = (FrameState) node; - - ensureOuterStateDecoded(methodScope); - if (frameState.bci < 0) { - ensureExceptionStateDecoded(methodScope); - } - return InliningUtil.processFrameState(frameState, methodScope.invokeData.invoke, methodScope.method, methodScope.exceptionState, methodScope.outerState, true); - - } else if (node instanceof MonitorIdNode) { - ensureOuterStateDecoded(methodScope); - InliningUtil.processMonitorId(methodScope.outerState, (MonitorIdNode) node); - return node; - } - } - - return node; - } -} diff -r d6d9631eb057 -r fc6fd02ecf95 graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java --- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java Tue Apr 21 17:02:06 2015 -0700 +++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java Sun Apr 26 20:50:30 2015 -0700 @@ -340,7 +340,7 @@ plugins.setInlineInvokePlugin(new ParsingInlineInvokePlugin((ReplacementsImpl) providers.getReplacements(), parsingInvocationPlugins, loopExplosionPlugin, !PrintTruffleExpansionHistogram.getValue())); - CachingPEGraphDecoder decoder = new CachingPEGraphDecoder(providers, newConfig, AllowAssumptions.from(graph.getAssumptions() != null), architecture); + CachingPEGraphDecoder decoder = new CachingPEGraphDecoder(providers, newConfig, TruffleCompilerImpl.Optimizations, AllowAssumptions.from(graph.getAssumptions() != null), architecture); ParameterPlugin parameterPlugin = new InterceptReceiverPlugin(callTarget); diff -r d6d9631eb057 -r fc6fd02ecf95 graal/com.oracle.graal.word/src/com/oracle/graal/word/nodes/WordCastNode.java --- a/graal/com.oracle.graal.word/src/com/oracle/graal/word/nodes/WordCastNode.java Tue Apr 21 17:02:06 2015 -0700 +++ b/graal/com.oracle.graal.word/src/com/oracle/graal/word/nodes/WordCastNode.java Sun Apr 26 20:50:30 2015 -0700 @@ -63,7 +63,7 @@ @Override public Node canonical(CanonicalizerTool tool) { - if (getUsageCount() == 0) { + if (tool.allUsagesAvailable() && hasNoUsages()) { /* If the cast is unused, it can be eliminated. */ return input; } diff -r d6d9631eb057 -r fc6fd02ecf95 graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/CachedTest.java --- a/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/CachedTest.java Tue Apr 21 17:02:06 2015 -0700 +++ b/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/CachedTest.java Sun Apr 26 20:50:30 2015 -0700 @@ -40,6 +40,7 @@ import com.oracle.truffle.api.dsl.test.CachedTestFactory.TestMultipleCachesFactory; import com.oracle.truffle.api.dsl.test.CachedTestFactory.UnboundCacheFactory; import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode; +import com.oracle.truffle.api.nodes.*; @SuppressWarnings("unused") public class CachedTest { @@ -263,6 +264,26 @@ } + /* + * Node should not produce any warnings in isIdentical of the generated code. Unnecessary casts + * were generated for isIdentical on the fast path. + */ + @NodeChildren({@NodeChild, @NodeChild}) + static class RegressionTestWarningInIsIdentical extends ValueNode { + + @Specialization(guards = {"cachedName == name"}) + protected Object directAccess(String receiver, String name, // + @Cached("name") String cachedName, // + @Cached("create(receiver, name)") Object callHandle) { + return receiver; + } + + protected static Object create(String receiver, String name) { + return receiver; + } + + } + @NodeChild static class TestMultipleCaches extends ValueNode { diff -r d6d9631eb057 -r fc6fd02ecf95 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java Tue Apr 21 17:02:06 2015 -0700 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java Sun Apr 26 20:50:30 2015 -0700 @@ -118,18 +118,20 @@ } /** - * Retrieves the guest language source code section that is currently assigned to this node. + * Retrieves the segment of guest language source code that is represented by this Node. * - * @return the assigned source code section + * @return the source code represented by this Node */ public final SourceSection getSourceSection() { return sourceSection; } /** - * Retrieves the guest language source code section that is currently assigned to this node. + * Retrieves the segment of guest language source code that is represented by this Node, if + * present; otherwise retrieves the segment represented by the nearest AST ancestor that has + * this information. * - * @return the assigned source code section + * @return an approximation of the source code represented by this Node */ @ExplodeLoop public final SourceSection getEncapsulatingSourceSection() { diff -r d6d9631eb057 -r fc6fd02ecf95 graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/NodeGenFactory.java --- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/NodeGenFactory.java Tue Apr 21 17:02:06 2015 -0700 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/NodeGenFactory.java Sun Apr 26 20:50:30 2015 -0700 @@ -1981,7 +1981,7 @@ } else { castGuards = new HashSet<>(); for (TypeGuard castGuard : group.getTypeGuards()) { - if (isTypeGuardUsedInAnyGuardOrCacheBelow(group, currentValues, castGuard)) { + if (isTypeGuardUsedInAnyGuardOrCacheBelow(group, currentValues, castGuard, execution.isFastPath())) { castGuards.add(castGuard); } } @@ -2059,7 +2059,7 @@ return true; } - private boolean isTypeGuardUsedInAnyGuardOrCacheBelow(SpecializationGroup group, LocalContext currentValues, TypeGuard typeGuard) { + private boolean isTypeGuardUsedInAnyGuardOrCacheBelow(SpecializationGroup group, LocalContext currentValues, TypeGuard typeGuard, boolean fastPath) { String localName = currentValues.getValue(typeGuard.getSignatureIndex()).getName(); SpecializationData specialization = group.getSpecialization(); @@ -2068,7 +2068,7 @@ return true; } } - if (specialization != null) { + if (!fastPath && specialization != null) { for (CacheExpression cache : specialization.getCaches()) { if (isVariableBoundIn(specialization, cache.getExpression(), localName, currentValues)) { return true; @@ -2077,7 +2077,7 @@ } for (SpecializationGroup child : group.getChildren()) { - if (isTypeGuardUsedInAnyGuardOrCacheBelow(child, currentValues, typeGuard)) { + if (isTypeGuardUsedInAnyGuardOrCacheBelow(child, currentValues, typeGuard, fastPath)) { return true; } } diff -r d6d9631eb057 -r fc6fd02ecf95 graal/com.oracle.truffle.interop/src/com/oracle/truffle/interop/messages/MessageUtil.java --- a/graal/com.oracle.truffle.interop/src/com/oracle/truffle/interop/messages/MessageUtil.java Tue Apr 21 17:02:06 2015 -0700 +++ b/graal/com.oracle.truffle.interop/src/com/oracle/truffle/interop/messages/MessageUtil.java Sun Apr 26 20:50:30 2015 -0700 @@ -34,6 +34,6 @@ } else if (o1 instanceof Receiver && o2 instanceof Receiver) { return true; } - throw new IllegalStateException(); + return false; } } diff -r d6d9631eb057 -r fc6fd02ecf95 make/Makefile --- a/make/Makefile Tue Apr 21 17:02:06 2015 -0700 +++ b/make/Makefile Sun Apr 26 20:50:30 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 d6d9631eb057 -r fc6fd02ecf95 make/defs.make --- a/make/defs.make Tue Apr 21 17:02:06 2015 -0700 +++ b/make/defs.make Sun Apr 26 20:50:30 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 d6d9631eb057 -r fc6fd02ecf95 mx/mx_graal.py --- a/mx/mx_graal.py Tue Apr 21 17:02:06 2015 -0700 +++ b/mx/mx_graal.py Sun Apr 26 20:50:30 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 d6d9631eb057 -r fc6fd02ecf95 mx/suite.py --- a/mx/suite.py Tue Apr 21 17:02:06 2015 -0700 +++ b/mx/suite.py Sun Apr 26 20:50:30 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", diff -r d6d9631eb057 -r fc6fd02ecf95 src/share/vm/code/dependencies.cpp --- a/src/share/vm/code/dependencies.cpp Tue Apr 21 17:02:06 2015 -0700 +++ b/src/share/vm/code/dependencies.cpp Sun Apr 26 20:50:30 2015 -0700 @@ -1029,7 +1029,13 @@ assert((uint)n <= (uint)_num_participants, "oob"); Method* fm = _found_methods[n]; assert(n == _num_participants || fm != NULL, "proper usage"); - assert(fm == NULL || fm->method_holder() == _participants[n], "sanity"); + if (fm != NULL && fm->method_holder() != _participants[n]) { + // Default methods from interfaces can be added to classes. In + // that case the holder of the method is not the class but the + // interface where it's defined. + assert(fm->is_default_method(), "sanity"); + return NULL; + } return fm; } diff -r d6d9631eb057 -r fc6fd02ecf95 src/share/vm/graal/graalJavaAccess.hpp --- a/src/share/vm/graal/graalJavaAccess.hpp Tue Apr 21 17:02:06 2015 -0700 +++ b/src/share/vm/graal/graalJavaAccess.hpp Sun Apr 26 20:50:30 2015 -0700 @@ -273,8 +273,8 @@ class name : AllStatic { \ private: \ friend class GraalCompiler; \ - static void check(oop obj) { \ - assert(obj != NULL, "NULL field access of class " #name); \ + static void check(oop obj, const char* field_name) { \ + assert(obj != NULL, err_msg("NULL field access of %s.%s", #name, field_name)); \ assert(obj->is_a(SystemDictionary::name##_klass()), "wrong class, " #name " expected"); \ } \ static void compute_offsets(); \ @@ -283,14 +283,14 @@ #define END_CLASS }; -#define FIELD(name, type, accessor, cast) \ - static int _##name##_offset; \ - static type name(oop obj) { check(obj); return cast obj->accessor(_##name##_offset); } \ - static type name(Handle& obj) { check(obj()); return cast obj->accessor(_##name##_offset); } \ - static type name(jobject obj) { check(JNIHandles::resolve(obj)); return cast JNIHandles::resolve(obj)->accessor(_##name##_offset); } \ - static void set_##name(oop obj, type x) { check(obj); obj->accessor##_put(_##name##_offset, x); } \ - static void set_##name(Handle& obj, type x) { check(obj()); obj->accessor##_put(_##name##_offset, x); } \ - static void set_##name(jobject obj, type x) { check(JNIHandles::resolve(obj)); JNIHandles::resolve(obj)->accessor##_put(_##name##_offset, x); } +#define FIELD(name, type, accessor, cast) \ + static int _##name##_offset; \ + static type name(oop obj) { check(obj, #name); return cast obj->accessor(_##name##_offset); } \ + static type name(Handle& obj) { check(obj(), #name); return cast obj->accessor(_##name##_offset); } \ + static type name(jobject obj) { check(JNIHandles::resolve(obj), #name); return cast JNIHandles::resolve(obj)->accessor(_##name##_offset); } \ + static void set_##name(oop obj, type x) { check(obj, #name); obj->accessor##_put(_##name##_offset, x); } \ + static void set_##name(Handle& obj, type x) { check(obj(), #name); obj->accessor##_put(_##name##_offset, x); } \ + static void set_##name(jobject obj, type x) { check(JNIHandles::resolve(obj), #name); JNIHandles::resolve(obj)->accessor##_put(_##name##_offset, x); } #define EMPTY_CAST #define CHAR_FIELD(klass, name) FIELD(name, jchar, char_field, EMPTY_CAST) diff -r d6d9631eb057 -r fc6fd02ecf95 src/share/vm/memory/cardTableRS.cpp --- a/src/share/vm/memory/cardTableRS.cpp Tue Apr 21 17:02:06 2015 -0700 +++ b/src/share/vm/memory/cardTableRS.cpp Sun Apr 26 20:50:30 2015 -0700 @@ -352,15 +352,22 @@ "[_begin, _end) = [" PTR_FORMAT "," PTR_FORMAT ")", p2i(jp), p2i(_begin), p2i(_end))); oop obj = oopDesc::load_decode_heap_oop(p); - guarantee(obj == NULL || (HeapWord*)obj >= _boundary, - err_msg("pointer " PTR_FORMAT " at " PTR_FORMAT " on " - "clean card crosses boundary" PTR_FORMAT, - p2i((HeapWord*)obj), p2i(jp), p2i(_boundary))); + if (!(obj == NULL || (HeapWord*)obj >= _boundary)) { + tty->print_cr("pointer " PTR_FORMAT " at " PTR_FORMAT " on " + "clean card crosses boundary" PTR_FORMAT, + p2i((HeapWord*)obj), p2i(jp), p2i(_boundary)); +#ifndef PRODUCT + obj->print(); +#endif + had_error = true; + } } public: + bool had_error; + VerifyCleanCardClosure(HeapWord* b, HeapWord* begin, HeapWord* end) : - _boundary(b), _begin(begin), _end(end) { + _boundary(b), _begin(begin), _end(end), had_error(false) { assert(b <= begin, err_msg("Error: boundary " PTR_FORMAT " should be at or below begin " PTR_FORMAT, p2i(b), p2i(begin))); @@ -400,6 +407,7 @@ // We don't need to do young-gen spaces. if (s->end() <= gen_boundary) return; MemRegion used = s->used_region(); + bool had_error = false; jbyte* cur_entry = byte_for(used.start()); jbyte* limit = byte_after(used.last()); @@ -438,6 +446,13 @@ for (HeapWord* cur = start_block; cur < end; cur += s->block_size(cur)) { if (s->block_is_obj(cur) && s->obj_is_alive(cur)) { oop(cur)->oop_iterate_no_header(&verify_blk, mr); + had_error |= verify_blk.had_error; + if (verify_blk.had_error) { + verify_blk.had_error = false; +#ifndef PRODUCT + oop(cur)->print(); +#endif + } } } } @@ -598,6 +613,7 @@ cur_entry++; } } + guarantee(!had_error, "Card table errors found"); } void CardTableRS::verify() { diff -r d6d9631eb057 -r fc6fd02ecf95 src/share/vm/memory/referenceProcessor.cpp --- a/src/share/vm/memory/referenceProcessor.cpp Tue Apr 21 17:02:06 2015 -0700 +++ b/src/share/vm/memory/referenceProcessor.cpp Sun Apr 26 20:50:30 2015 -0700 @@ -57,8 +57,11 @@ java_lang_ref_SoftReference::set_clock(_soft_ref_timestamp_clock); _always_clear_soft_ref_policy = new AlwaysClearPolicy(); - _default_soft_ref_policy = new COMPILER2_PRESENT(LRUMaxHeapPolicy()) - NOT_COMPILER2(LRUCurrentHeapPolicy()); +#if defined(COMPILER2) || defined(GRAAL) + _default_soft_ref_policy = new LRUMaxHeapPolicy(); +#else + _default_soft_ref_policy = new LRUCurrentHeapPolicy(); +#endif if (_always_clear_soft_ref_policy == NULL || _default_soft_ref_policy == NULL) { vm_exit_during_initialization("Could not allocate reference policy object"); } diff -r d6d9631eb057 -r fc6fd02ecf95 test/compiler/inlining/DefaultMethodsDependencies.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/inlining/DefaultMethodsDependencies.java Sun Apr 26 20:50:30 2015 -0700 @@ -0,0 +1,63 @@ +/* + * 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. + */ + +/* + * @test + * @bug 8069263 + * @summary Deoptimization between array allocation and arraycopy may result in non initialized array + * @run main/othervm -XX:-BackgroundCompilation -XX:CompileOnly=DefaultMethodsDependencies::test -XX:CompileOnly=DefaultMethodsDependencies$I2::m1 DefaultMethodsDependencies + * + */ + +public class DefaultMethodsDependencies { + + interface I1 { + void m1(); + // triggers processing of default methods in C1 + default void m2() { + } + } + + interface I2 extends I1 { + // added to C2 as default method + default void m1() { + } + } + + static abstract class C1 implements I1 { + } + + static class C2 extends C1 implements I2 { + } + + static void test(C1 obj) { + obj.m1(); + } + + static public void main(String[] args) { + C2 obj = new C2(); + for (int i = 0; i < 20000; i++) { + test(obj); + } + } +}