# HG changeset patch # User Christian Wimmer # Date 1379468214 25200 # Node ID 4eec2ac671c22b67410da1fac3ae439c44599d57 # Parent 56e59e384dc1eb3424c1ffc514dfd31793106a44 Refactor the WordTypeVerificationPhase to use fewer graph iterations, and invoke it a fewer places diff -r 56e59e384dc1 -r 4eec2ac671c2 graal/com.oracle.graal.graph/src/com/oracle/graal/graph/Node.java --- a/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/Node.java Wed Sep 18 02:41:52 2013 +0200 +++ b/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/Node.java Tue Sep 17 18:36:54 2013 -0700 @@ -104,6 +104,9 @@ /** * Determines if the stamp of the instantiated intrinsic node has its stamp set from the * return type of the annotated method. + *

+ * When it is set to true, the stamp that is passed in to the constructor of ValueNode is + * ignored and can therefore safely be {@code null}. */ boolean setStampFromReturnType() default false; } diff -r 56e59e384dc1 -r 4eec2ac671c2 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/BeginLockScopeNode.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/BeginLockScopeNode.java Wed Sep 18 02:41:52 2013 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/BeginLockScopeNode.java Tue Sep 17 18:36:54 2013 -0700 @@ -29,7 +29,6 @@ import com.oracle.graal.hotspot.*; import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.extended.*; -import com.oracle.graal.nodes.type.*; import com.oracle.graal.word.*; /** @@ -42,8 +41,8 @@ private int lockDepth; - public BeginLockScopeNode(int lockDepth) { - super(StampFactory.forWord()); + private BeginLockScopeNode(int lockDepth) { + super(null); this.lockDepth = lockDepth; } @@ -66,6 +65,6 @@ gen.setResult(this, result); } - @NodeIntrinsic + @NodeIntrinsic(setStampFromReturnType = true) public static native Word beginLockScope(@ConstantNodeParameter int lockDepth); } diff -r 56e59e384dc1 -r 4eec2ac671c2 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/CStringNode.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/CStringNode.java Wed Sep 18 02:41:52 2013 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/CStringNode.java Tue Sep 17 18:36:54 2013 -0700 @@ -27,7 +27,6 @@ import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.calc.*; import com.oracle.graal.nodes.spi.*; -import com.oracle.graal.nodes.type.*; import com.oracle.graal.word.*; /** @@ -38,8 +37,8 @@ private final String string; - public CStringNode(String string) { - super(StampFactory.forWord()); + private CStringNode(String string) { + super(null); this.string = string; } @@ -55,6 +54,6 @@ graph().replaceFloating(this, replacement); } - @NodeIntrinsic + @NodeIntrinsic(setStampFromReturnType = true) public static native Word cstring(@ConstantNodeParameter String string); } diff -r 56e59e384dc1 -r 4eec2ac671c2 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/CurrentJavaThreadNode.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/CurrentJavaThreadNode.java Wed Sep 18 02:41:52 2013 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/CurrentJavaThreadNode.java Tue Sep 17 18:36:54 2013 -0700 @@ -28,16 +28,15 @@ import com.oracle.graal.graph.*; import com.oracle.graal.nodes.calc.*; import com.oracle.graal.nodes.spi.*; -import com.oracle.graal.nodes.type.*; import com.oracle.graal.word.*; /** * Gets the address of the C++ JavaThread object for the current thread. */ -public class CurrentJavaThreadNode extends FloatingNode implements LIRLowerable { +public final class CurrentJavaThreadNode extends FloatingNode implements LIRLowerable { - public CurrentJavaThreadNode() { - super(StampFactory.forWord()); + private CurrentJavaThreadNode() { + super(null); } @Override @@ -54,7 +53,7 @@ } } - @NodeIntrinsic + @NodeIntrinsic(setStampFromReturnType = true) public static Word get() { return Word.unsigned(unsafeReadWord(Thread.currentThread(), eetopOffset())); } diff -r 56e59e384dc1 -r 4eec2ac671c2 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/CurrentLockNode.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/CurrentLockNode.java Wed Sep 18 02:41:52 2013 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/CurrentLockNode.java Tue Sep 17 18:36:54 2013 -0700 @@ -28,7 +28,6 @@ import com.oracle.graal.compiler.target.*; import com.oracle.graal.hotspot.*; import com.oracle.graal.nodes.*; -import com.oracle.graal.nodes.type.*; import com.oracle.graal.word.*; /** @@ -38,8 +37,8 @@ private int lockDepth; - public CurrentLockNode(int lockDepth) { - super(StampFactory.forWord()); + private CurrentLockNode(int lockDepth) { + super(null); this.lockDepth = lockDepth; } @@ -53,6 +52,6 @@ gen.setResult(this, result); } - @NodeIntrinsic + @NodeIntrinsic(setStampFromReturnType = true) public static native Word currentLock(@ConstantNodeParameter int lockDepth); } diff -r 56e59e384dc1 -r 4eec2ac671c2 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/DimensionsNode.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/DimensionsNode.java Wed Sep 18 02:41:52 2013 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/DimensionsNode.java Tue Sep 17 18:36:54 2013 -0700 @@ -27,7 +27,6 @@ import com.oracle.graal.compiler.gen.*; import com.oracle.graal.compiler.target.*; import com.oracle.graal.nodes.*; -import com.oracle.graal.nodes.type.*; import com.oracle.graal.word.*; /** @@ -38,8 +37,8 @@ private final int rank; - public DimensionsNode(int rank) { - super(StampFactory.forWord()); + private DimensionsNode(int rank) { + super(null); this.rank = rank; } @@ -51,6 +50,6 @@ gen.setResult(this, result); } - @NodeIntrinsic + @NodeIntrinsic(setStampFromReturnType = true) public static native Word allocaDimsArray(@ConstantNodeParameter int rank); } diff -r 56e59e384dc1 -r 4eec2ac671c2 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/MonitorCounterNode.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/MonitorCounterNode.java Wed Sep 18 02:41:52 2013 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/MonitorCounterNode.java Tue Sep 17 18:36:54 2013 -0700 @@ -27,7 +27,6 @@ import com.oracle.graal.compiler.gen.*; import com.oracle.graal.compiler.target.*; import com.oracle.graal.nodes.calc.*; -import com.oracle.graal.nodes.type.*; import com.oracle.graal.word.*; /** @@ -35,8 +34,8 @@ */ public final class MonitorCounterNode extends FloatingNode implements LIRGenLowerable { - public MonitorCounterNode() { - super(StampFactory.forWord()); + private MonitorCounterNode() { + super(null); } @Override @@ -47,6 +46,6 @@ gen.setResult(this, result); } - @NodeIntrinsic + @NodeIntrinsic(setStampFromReturnType = true) public static native Word counter(); } diff -r 56e59e384dc1 -r 4eec2ac671c2 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/ForeignCallStub.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/ForeignCallStub.java Wed Sep 18 02:41:52 2013 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/ForeignCallStub.java Tue Sep 17 18:36:54 2013 -0700 @@ -237,9 +237,14 @@ Debug.dump(builder.graph, "Initial stub graph"); } + /* Rewrite all word types that can come in from the method argument types. */ + new WordTypeRewriterPhase(runtime, wordKind()).apply(builder.graph); + /* Inline all method calls that are create above. */ for (InvokeNode invoke : builder.graph.getNodes().filter(InvokeNode.class).snapshot()) { inline(invoke); } + /* Clean up all code that is now dead after inlining. */ + new DeadCodeEliminationPhase().apply(builder.graph); assert builder.graph.getNodes().filter(InvokeNode.class).isEmpty(); if (Debug.isDumpEnabled()) { @@ -294,13 +299,9 @@ } private void inline(InvokeNode invoke) { - StructuredGraph graph = invoke.graph(); ResolvedJavaMethod method = ((MethodCallTargetNode) invoke.callTarget()).targetMethod(); ReplacementsImpl repl = new ReplacementsImpl(runtime, new Assumptions(false), runtime.getTarget()); StructuredGraph calleeGraph = repl.makeGraph(method, null, null); InliningUtil.inline(invoke, calleeGraph, false); - new NodeIntrinsificationPhase(runtime).apply(graph); - new WordTypeRewriterPhase(runtime, wordKind()).apply(graph); - new DeadCodeEliminationPhase().apply(graph); } } diff -r 56e59e384dc1 -r 4eec2ac671c2 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/type/StampFactory.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/type/StampFactory.java Wed Sep 18 02:41:52 2013 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/type/StampFactory.java Tue Sep 17 18:36:54 2013 -0700 @@ -22,8 +22,6 @@ */ package com.oracle.graal.nodes.type; -import com.oracle.graal.api.code.*; - import com.oracle.graal.api.meta.*; import com.oracle.graal.graph.*; import com.oracle.graal.nodes.type.GenericStamp.GenericStampType; @@ -43,7 +41,6 @@ private static final Stamp conditionStamp = new GenericStamp(GenericStampType.Condition); private static final Stamp voidStamp = new GenericStamp(GenericStampType.Void); private static final Stamp nodeIntrinsicStamp = new ObjectStamp(null, false, false, false); - private static final Stamp wordStamp = new ObjectStamp(null, false, false, false); private static final Stamp positiveInt = forInteger(Kind.Int, 0, Integer.MAX_VALUE, 0, Integer.MAX_VALUE); private static void setCache(Kind kind, Stamp stamp) { @@ -85,14 +82,6 @@ return nodeIntrinsicStamp; } - /** - * A stamp used only in the graph of intrinsics, e.g., snippets. It is then replaced by the - * actual primitive type stamp for the target-specific {@link TargetDescription#wordKind}. - */ - public static Stamp forWord() { - return wordStamp; - } - public static Stamp intValue() { return forKind(Kind.Int); } diff -r 56e59e384dc1 -r 4eec2ac671c2 graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/NodeIntrinsificationPhase.java --- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/NodeIntrinsificationPhase.java Wed Sep 18 02:41:52 2013 +0200 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/NodeIntrinsificationPhase.java Tue Sep 17 18:36:54 2013 -0700 @@ -76,7 +76,6 @@ assert Modifier.isStatic(target.getModifiers()) : "node intrinsic must be static: " + target; ResolvedJavaType[] parameterTypes = MetaUtil.resolveJavaTypes(MetaUtil.signatureToTypes(target), declaringClass); - ResolvedJavaType returnType = target.getSignature().getReturnType(declaringClass).resolve(declaringClass); // Prepare the arguments for the reflective constructor call on the node class. Constant[] nodeConstructorArguments = prepareArguments(methodCallTargetNode, parameterTypes, target, false); @@ -86,7 +85,7 @@ // Create the new node instance. ResolvedJavaType c = getNodeClass(target, intrinsic); - Node newInstance = createNodeInstance(c, parameterTypes, returnType, intrinsic.setStampFromReturnType(), nodeConstructorArguments); + Node newInstance = createNodeInstance(c, parameterTypes, methodCallTargetNode.invoke().asNode().stamp(), intrinsic.setStampFromReturnType(), nodeConstructorArguments); // Replace the invoke with the new node. newInstance = methodCallTargetNode.graph().addOrUnique(newInstance); @@ -194,7 +193,7 @@ return result; } - private Node createNodeInstance(ResolvedJavaType nodeClass, ResolvedJavaType[] parameterTypes, ResolvedJavaType returnType, boolean setStampFromReturnType, Constant[] nodeConstructorArguments) { + private Node createNodeInstance(ResolvedJavaType nodeClass, ResolvedJavaType[] parameterTypes, Stamp invokeStamp, boolean setStampFromReturnType, Constant[] nodeConstructorArguments) { ResolvedJavaMethod constructor = null; Constant[] arguments = null; @@ -218,11 +217,7 @@ ValueNode intrinsicNode = (ValueNode) constructor.newInstance(arguments).asObject(); if (setStampFromReturnType) { - if (returnType.getKind() == Kind.Object) { - intrinsicNode.setStamp(StampFactory.declared(returnType)); - } else { - intrinsicNode.setStamp(StampFactory.forKind(returnType.getKind())); - } + intrinsicNode.setStamp(invokeStamp); } return intrinsicNode; } catch (Exception e) { diff -r 56e59e384dc1 -r 4eec2ac671c2 graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ReplacementsImpl.java --- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ReplacementsImpl.java Wed Sep 18 02:41:52 2013 +0200 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ReplacementsImpl.java Tue Sep 17 18:36:54 2013 -0700 @@ -161,7 +161,7 @@ /** * Registers a method substitution. - * + * * @param originalMember a method or constructor being substituted * @param substituteMethod the substitute method * @return the original method @@ -182,7 +182,7 @@ /** * Registers a macro substitution. - * + * * @param originalMethod a method or constructor being substituted * @param macro the substitute macro node class * @return the original method @@ -216,7 +216,7 @@ /** * Creates a preprocessed graph for a snippet or method substitution. - * + * * @param method the snippet or method substitution for which a graph will be created * @param original the original method if {@code method} is a {@linkplain MethodSubstitution * substitution} otherwise null @@ -325,13 +325,11 @@ @Override public void run() { - GraphBuilderConfiguration config = GraphBuilderConfiguration.getSnippetDefault(); - GraphBuilderPhase graphBuilder = new GraphBuilderPhase(runtime, config, OptimisticOptimizations.NONE); - graphBuilder.apply(graph); + new GraphBuilderPhase(runtime, GraphBuilderConfiguration.getSnippetDefault(), OptimisticOptimizations.NONE).apply(graph); + new WordTypeVerificationPhase(runtime, target.wordKind).apply(graph); + new WordTypeRewriterPhase(runtime, target.wordKind).apply(graph); - new WordTypeVerificationPhase(runtime, target.wordKind).apply(graph); if (OptCanonicalizer.getValue()) { - new WordTypeRewriterPhase(runtime, target.wordKind).apply(graph); new CanonicalizerPhase(true).apply(graph, new PhaseContext(runtime, assumptions, ReplacementsImpl.this)); } } @@ -345,14 +343,13 @@ /** * Called after a graph is inlined. - * + * * @param caller the graph into which {@code callee} was inlined * @param callee the graph that was inlined into {@code caller} * @param beforeInlineData value returned by {@link #beforeInline}. */ protected void afterInline(StructuredGraph caller, StructuredGraph callee, Object beforeInlineData) { if (OptCanonicalizer.getValue()) { - new WordTypeRewriterPhase(runtime, target.wordKind).apply(caller); new CanonicalizerPhase(true).apply(caller, new PhaseContext(runtime, assumptions, ReplacementsImpl.this)); } } @@ -362,9 +359,6 @@ */ protected void afterInlining(StructuredGraph graph) { new NodeIntrinsificationPhase(runtime).apply(graph); - - new WordTypeRewriterPhase(runtime, target.wordKind).apply(graph); - new DeadCodeEliminationPhase().apply(graph); if (OptCanonicalizer.getValue()) { new CanonicalizerPhase(true).apply(graph, new PhaseContext(runtime, assumptions, ReplacementsImpl.this)); @@ -383,6 +377,9 @@ if (callee == method) { final StructuredGraph originalGraph = new StructuredGraph(original); new GraphBuilderPhase(runtime, GraphBuilderConfiguration.getSnippetDefault(), OptimisticOptimizations.NONE).apply(originalGraph); + new WordTypeVerificationPhase(runtime, target.wordKind).apply(graph); + new WordTypeRewriterPhase(runtime, target.wordKind).apply(graph); + InliningUtil.inline(callTarget.invoke(), originalGraph, true); Debug.dump(graph, "after inlining %s", callee); @@ -434,7 +431,7 @@ /** * Resolves a name to a class. - * + * * @param className the name of the class to resolve * @param optional if true, resolution failure returns null * @return the resolved class or null if resolution fails and {@code optional} is true diff -r 56e59e384dc1 -r 4eec2ac671c2 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 Wed Sep 18 02:41:52 2013 +0200 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/SnippetTemplate.java Tue Sep 17 18:36:54 2013 -0700 @@ -46,7 +46,6 @@ import com.oracle.graal.replacements.Snippet.VarargsParameter; import com.oracle.graal.replacements.nodes.*; import com.oracle.graal.word.*; -import com.oracle.graal.word.phases.*; /** * A snippet template is a graph created by parsing a snippet method and then specialized by binding @@ -352,7 +351,7 @@ @Override public SnippetTemplate call() throws Exception { - return new SnippetTemplate(runtime, replacements, target, args); + return new SnippetTemplate(runtime, replacements, args); } }); templates.put(args.cacheKey, template); @@ -383,7 +382,7 @@ /** * Creates a snippet template. */ - protected SnippetTemplate(MetaAccessProvider runtime, Replacements replacements, TargetDescription target, Arguments args) { + protected SnippetTemplate(MetaAccessProvider runtime, Replacements replacements, Arguments args) { StructuredGraph snippetGraph = replacements.getSnippet(args.info.method); ResolvedJavaMethod method = snippetGraph.method(); @@ -426,8 +425,6 @@ if (!nodeReplacements.isEmpty()) { // Do deferred intrinsification of node intrinsics new NodeIntrinsificationPhase(runtime).apply(snippetCopy); - new WordTypeRewriterPhase(runtime, target.wordKind).apply(snippetCopy); - new CanonicalizerPhase(true).apply(snippetCopy, context); } NodeIntrinsificationVerificationPhase.verify(snippetCopy); diff -r 56e59e384dc1 -r 4eec2ac671c2 graal/com.oracle.graal.word/src/com/oracle/graal/word/phases/WordTypeRewriterPhase.java --- a/graal/com.oracle.graal.word/src/com/oracle/graal/word/phases/WordTypeRewriterPhase.java Wed Sep 18 02:41:52 2013 +0200 +++ b/graal/com.oracle.graal.word/src/com/oracle/graal/word/phases/WordTypeRewriterPhase.java Tue Sep 17 18:36:54 2013 -0700 @@ -47,10 +47,10 @@ */ public class WordTypeRewriterPhase extends Phase { - private final MetaAccessProvider metaAccess; - private final ResolvedJavaType wordBaseType; - private final ResolvedJavaType wordImplType; - private final Kind wordKind; + protected final MetaAccessProvider metaAccess; + protected final ResolvedJavaType wordBaseType; + protected final ResolvedJavaType wordImplType; + protected final Kind wordKind; public WordTypeRewriterPhase(MetaAccessProvider metaAccess, Kind wordKind) { this.metaAccess = metaAccess; @@ -59,198 +59,250 @@ this.wordImplType = metaAccess.lookupJavaType(Word.class); } - public ResolvedJavaType getWordBaseType() { - return wordBaseType; - } - - public ResolvedJavaType getWordImplType() { - return wordImplType; - } - @Override protected void run(StructuredGraph graph) { - for (Node n : GraphOrder.forwardGraph(graph)) { - if (n instanceof ValueNode && !(n instanceof PhiNode && ((PhiNode) n).isLoopPhi())) { - ValueNode valueNode = (ValueNode) n; - if (isWord(valueNode)) { - changeToWord(valueNode); - } - } - } - for (PhiNode phi : graph.getNodes(PhiNode.class)) { - if (phi.isLoopPhi() && isWord(phi)) { - changeToWord(phi); - } - } - - // Remove casts between different word types (which by now no longer have kind Object) - for (CheckCastNode checkCastNode : graph.getNodes().filter(CheckCastNode.class).snapshot()) { - if (!checkCastNode.isDeleted() && checkCastNode.kind() == wordKind) { - checkCastNode.replaceAtUsages(checkCastNode.object()); - graph.removeFixed(checkCastNode); - } - } - - // Remove unnecessary/redundant unsafe casts - for (UnsafeCastNode unsafeCastNode : graph.getNodes().filter(UnsafeCastNode.class).snapshot()) { - if (!unsafeCastNode.isDeleted() && unsafeCastNode.object().stamp() == unsafeCastNode.stamp()) { - graph.replaceFloating(unsafeCastNode, unsafeCastNode.object()); - } - } - - // Fold constant field reads (e.g. enum constants) - for (LoadFieldNode load : graph.getNodes(LoadFieldNode.class).snapshot()) { - ConstantNode constant = load.asConstant(metaAccess); - if (constant != null) { - graph.replaceFixedWithFloating(load, constant); - } - } + inferStamps(graph); - // Replace ObjectEqualsNodes with IntegerEqualsNodes where the values being compared are - // words - for (ObjectEqualsNode objectEqualsNode : graph.getNodes().filter(ObjectEqualsNode.class).snapshot()) { - ValueNode x = objectEqualsNode.x(); - ValueNode y = objectEqualsNode.y(); - if (x.kind() == wordKind || y.kind() == wordKind) { - assert x.kind() == wordKind; - assert y.kind() == wordKind; - - // TODO Remove the whole iteration of ObjectEqualsNodes when we are sure that there - // is no more code where this triggers. - throw GraalInternalError.shouldNotReachHere("Comparison of words with == and != is no longer supported"); - } - } - - for (AccessIndexedNode node : graph.getNodes().filter(AccessIndexedNode.class).snapshot()) { - ValueNode array = node.array(); - ResolvedJavaType arrayType = ObjectStamp.typeOrNull(array); - if (arrayType == null) { - // There are cases where the array does not have a known type yet. Assume it is not - // a word type. - continue; - } - assert arrayType.isArray(); - if (isWord(arrayType.getComponentType())) { - /* - * The elementKind of the node is a final field, and other information such as the - * stamp depends on elementKind. Therefore, just create a new node and replace the - * old one. - */ - if (node instanceof LoadIndexedNode) { - graph.replaceFixedWithFixed(node, graph.add(new LoadIndexedNode(node.array(), node.index(), wordKind))); - } else if (node instanceof StoreIndexedNode) { - graph.replaceFixedWithFixed(node, graph.add(new StoreIndexedNode(node.array(), node.index(), wordKind, ((StoreIndexedNode) node).value()))); - } else { - throw GraalInternalError.shouldNotReachHere(); - } + for (Node n : graph.getNodes()) { + if (n instanceof ValueNode) { + changeToWord(graph, (ValueNode) n); } } - for (MethodCallTargetNode callTargetNode : graph.getNodes(MethodCallTargetNode.class).snapshot()) { - ResolvedJavaMethod targetMethod = callTargetNode.targetMethod(); - if (!callTargetNode.isStatic() && (callTargetNode.receiver().kind() == wordKind || isWord(callTargetNode.receiver()))) { - targetMethod = getWordImplType().resolveMethod(targetMethod); - } - Operation operation = targetMethod.getAnnotation(Word.Operation.class); - if (operation != null) { - NodeInputList arguments = callTargetNode.arguments(); - Invoke invoke = (Invoke) callTargetNode.usages().first(); - assert invoke != null : callTargetNode.targetMethod(); - - switch (operation.opcode()) { - case NODE_CLASS: - assert arguments.size() == 2; - ValueNode left = arguments.get(0); - ValueNode right = operation.rightOperandIsInt() ? toUnsigned(graph, arguments.get(1), Kind.Int) : fromSigned(graph, arguments.get(1)); - replace(invoke, nodeClassOp(graph, operation.node(), left, right, invoke)); - break; - - case COMPARISON: - assert arguments.size() == 2; - replace(invoke, comparisonOp(graph, operation.condition(), arguments.get(0), fromSigned(graph, arguments.get(1)))); - break; + for (Node node : graph.getNodes()) { + rewriteNode(graph, node); + } + } - case NOT: - assert arguments.size() == 1; - replace(invoke, graph.unique(new XorNode(wordKind, arguments.get(0), ConstantNode.forIntegerKind(wordKind, -1, graph)))); - break; - - case READ: { - assert arguments.size() == 2 || arguments.size() == 3; - Kind readKind = asKind(callTargetNode.returnType()); - LocationNode location; - if (arguments.size() == 2) { - location = makeLocation(graph, arguments.get(1), readKind, ANY_LOCATION); - } else { - location = makeLocation(graph, arguments.get(1), readKind, arguments.get(2)); - } - replace(invoke, readOp(graph, arguments.get(0), invoke, location, BarrierType.NONE, false)); - break; - } - case READ_HEAP: { - assert arguments.size() == 4; - Kind readKind = asKind(callTargetNode.returnType()); - LocationNode location = makeLocation(graph, arguments.get(1), readKind, ANY_LOCATION); - BarrierType barrierType = (BarrierType) arguments.get(2).asConstant().asObject(); - replace(invoke, readOp(graph, arguments.get(0), invoke, location, barrierType, arguments.get(3).asConstant().asInt() == 0 ? false : true)); - break; + /** + * Infer the stamps for all Object nodes in the graph, to make the stamps as precise as + * possible. For example, this propagates the word-type through phi functions. To handle phi + * functions at loop headers, the stamp inference is called until a fix point is reached. + *

+ * Note that we cannot rely on the normal canonicalizer to propagate stamps: The word type + * rewriting must run before the first run of the canonicalizer because many nodes are not + * prepared to see the word type during canonicalization. + */ + protected void inferStamps(StructuredGraph graph) { + boolean stampChanged; + do { + stampChanged = false; + for (Node n : GraphOrder.forwardGraph(graph)) { + if (n instanceof ValueNode) { + ValueNode node = (ValueNode) n; + if (node.kind() == Kind.Object) { + stampChanged |= node.inferStamp(); } - case WRITE: - case INITIALIZE: { - assert arguments.size() == 3 || arguments.size() == 4; - Kind writeKind = asKind(targetMethod.getSignature().getParameterType(1, targetMethod.getDeclaringClass())); - LocationNode location; - if (arguments.size() == 3) { - location = makeLocation(graph, arguments.get(1), writeKind, LocationIdentity.ANY_LOCATION); - } else { - location = makeLocation(graph, arguments.get(1), writeKind, arguments.get(3)); - } - replace(invoke, writeOp(graph, arguments.get(0), arguments.get(2), invoke, location, operation.opcode())); - break; - } - case ZERO: - assert arguments.size() == 0; - replace(invoke, ConstantNode.forIntegerKind(wordKind, 0L, graph)); - break; - - case FROM_UNSIGNED: - assert arguments.size() == 1; - replace(invoke, fromUnsigned(graph, arguments.get(0))); - break; + } + } + } while (stampChanged); + } - case FROM_SIGNED: - assert arguments.size() == 1; - replace(invoke, fromSigned(graph, arguments.get(0))); - break; - - case TO_RAW_VALUE: - assert arguments.size() == 1; - replace(invoke, toUnsigned(graph, arguments.get(0), Kind.Long)); - break; + /** + * Change the stamp for word nodes from the object stamp ({@link WordBase} or anything extending + * or implementing that interface) to the primitive word stamp. + */ + protected void changeToWord(StructuredGraph graph, ValueNode node) { + if (isWord(node)) { + if (node.isConstant()) { + ConstantNode oldConstant = (ConstantNode) node; + assert oldConstant.value.getKind() == Kind.Object; + WordBase value = (WordBase) oldConstant.value.asObject(); + ConstantNode newConstant = ConstantNode.forIntegerKind(wordKind, value.rawValue(), node.graph()); + graph.replaceFloating(oldConstant, newConstant); - case FROM_OBJECT: - assert arguments.size() == 1; - replace(invoke, graph.unique(new UnsafeCastNode(arguments.get(0), StampFactory.forKind(wordKind)))); - break; - - case FROM_ARRAY: - assert arguments.size() == 2; - replace(invoke, graph.unique(new ComputeAddressNode(arguments.get(0), arguments.get(1), StampFactory.forKind(wordKind)))); - break; - - case TO_OBJECT: - assert arguments.size() == 1; - replace(invoke, graph.unique(new UnsafeCastNode(arguments.get(0), invoke.asNode().stamp()))); - break; - - default: - throw new GraalInternalError("Unknown opcode: %s", operation.opcode()); - } + } else { + node.setStamp(StampFactory.forKind(wordKind)); } } } - private ValueNode fromUnsigned(StructuredGraph graph, ValueNode value) { + /** + * Clean up nodes that are no longer necessary or valid after the stamp change, and perform + * intrinsification of all methods called on word types. + */ + protected void rewriteNode(StructuredGraph graph, Node node) { + if (node instanceof CheckCastNode) { + rewriteCheckCast(graph, (CheckCastNode) node); + } else if (node instanceof UnsafeCastNode) { + rewriteUnsafeCast(graph, (UnsafeCastNode) node); + } else if (node instanceof LoadFieldNode) { + rewriteLoadField(graph, (LoadFieldNode) node); + } else if (node instanceof AccessIndexedNode) { + rewriteAccessIndexed(graph, (AccessIndexedNode) node); + } else if (node instanceof MethodCallTargetNode) { + rewriteInvoke(graph, (MethodCallTargetNode) node); + } + } + + /** + * Remove casts between word types (which by now no longer have kind Object). + */ + protected void rewriteCheckCast(StructuredGraph graph, CheckCastNode node) { + if (node.kind() == wordKind) { + node.replaceAtUsages(node.object()); + graph.removeFixed(node); + } + } + + /** + * Remove unnecessary/redundant unsafe casts. + */ + protected void rewriteUnsafeCast(StructuredGraph graph, UnsafeCastNode node) { + if (node.object().stamp() == node.stamp()) { + node.replaceAtUsages(node.object()); + graph.removeFloating(node); + } + } + + /** + * Fold constant field reads, e.g. enum constants. + */ + protected void rewriteLoadField(StructuredGraph graph, LoadFieldNode node) { + ConstantNode constant = node.asConstant(metaAccess); + if (constant != null) { + node.replaceAtUsages(constant); + graph.removeFixed(node); + } + } + + /** + * Change loads and stores of word-arrays. Since the element kind is managed by the node on its + * own and not in the stamp, {@link #changeToWord} does not perform all necessary changes. + */ + protected void rewriteAccessIndexed(StructuredGraph graph, AccessIndexedNode node) { + ResolvedJavaType arrayType = ObjectStamp.typeOrNull(node.array()); + /* + * There are cases where the array does not have a known type yet, i.e., the type is null. + * In that case we assume it is not a word type. + */ + if (arrayType != null && isWord(arrayType.getComponentType()) && node.elementKind() != wordKind) { + /* + * The elementKind of the node is a final field, and other information such as the stamp + * depends on elementKind. Therefore, just create a new node and replace the old one. + */ + if (node instanceof LoadIndexedNode) { + graph.replaceFixedWithFixed(node, graph.add(new LoadIndexedNode(node.array(), node.index(), wordKind))); + } else if (node instanceof StoreIndexedNode) { + graph.replaceFixedWithFixed(node, graph.add(new StoreIndexedNode(node.array(), node.index(), wordKind, ((StoreIndexedNode) node).value()))); + } else { + throw GraalInternalError.shouldNotReachHere(); + } + } + } + + /** + * Intrinsification of methods defined on the {@link Word} class that are annotated with + * {@link Operation}. + */ + protected void rewriteInvoke(StructuredGraph graph, MethodCallTargetNode callTargetNode) { + ResolvedJavaMethod targetMethod = callTargetNode.targetMethod(); + if (!callTargetNode.isStatic() && (callTargetNode.receiver().kind() == wordKind || isWord(callTargetNode.receiver()))) { + targetMethod = wordImplType.resolveMethod(targetMethod); + } + Operation operation = targetMethod.getAnnotation(Word.Operation.class); + if (operation != null) { + NodeInputList arguments = callTargetNode.arguments(); + Invoke invoke = callTargetNode.invoke(); + + switch (operation.opcode()) { + case NODE_CLASS: + assert arguments.size() == 2; + ValueNode left = arguments.get(0); + ValueNode right = operation.rightOperandIsInt() ? toUnsigned(graph, arguments.get(1), Kind.Int) : fromSigned(graph, arguments.get(1)); + + ValueNode replacement = graph.addOrUnique(createBinaryNodeInstance(operation.node(), wordKind, left, right)); + if (replacement instanceof FixedWithNextNode) { + graph.addBeforeFixed(invoke.asNode(), (FixedWithNextNode) replacement); + } + replace(invoke, replacement); + break; + + case COMPARISON: + assert arguments.size() == 2; + replace(invoke, comparisonOp(graph, operation.condition(), arguments.get(0), fromSigned(graph, arguments.get(1)))); + break; + + case NOT: + assert arguments.size() == 1; + replace(invoke, graph.unique(new XorNode(wordKind, arguments.get(0), ConstantNode.forIntegerKind(wordKind, -1, graph)))); + break; + + case READ: { + assert arguments.size() == 2 || arguments.size() == 3; + Kind readKind = asKind(callTargetNode.returnType()); + LocationNode location; + if (arguments.size() == 2) { + location = makeLocation(graph, arguments.get(1), readKind, ANY_LOCATION); + } else { + location = makeLocation(graph, arguments.get(1), readKind, arguments.get(2)); + } + replace(invoke, readOp(graph, arguments.get(0), invoke, location, BarrierType.NONE, false)); + break; + } + case READ_HEAP: { + assert arguments.size() == 4; + Kind readKind = asKind(callTargetNode.returnType()); + LocationNode location = makeLocation(graph, arguments.get(1), readKind, ANY_LOCATION); + BarrierType barrierType = (BarrierType) arguments.get(2).asConstant().asObject(); + replace(invoke, readOp(graph, arguments.get(0), invoke, location, barrierType, arguments.get(3).asConstant().asInt() == 0 ? false : true)); + break; + } + case WRITE: + case INITIALIZE: { + assert arguments.size() == 3 || arguments.size() == 4; + Kind writeKind = asKind(targetMethod.getSignature().getParameterType(1, targetMethod.getDeclaringClass())); + LocationNode location; + if (arguments.size() == 3) { + location = makeLocation(graph, arguments.get(1), writeKind, LocationIdentity.ANY_LOCATION); + } else { + location = makeLocation(graph, arguments.get(1), writeKind, arguments.get(3)); + } + replace(invoke, writeOp(graph, arguments.get(0), arguments.get(2), invoke, location, operation.opcode())); + break; + } + case ZERO: + assert arguments.size() == 0; + replace(invoke, ConstantNode.forIntegerKind(wordKind, 0L, graph)); + break; + + case FROM_UNSIGNED: + assert arguments.size() == 1; + replace(invoke, fromUnsigned(graph, arguments.get(0))); + break; + + case FROM_SIGNED: + assert arguments.size() == 1; + replace(invoke, fromSigned(graph, arguments.get(0))); + break; + + case TO_RAW_VALUE: + assert arguments.size() == 1; + replace(invoke, toUnsigned(graph, arguments.get(0), Kind.Long)); + break; + + case FROM_OBJECT: + assert arguments.size() == 1; + replace(invoke, graph.unique(new UnsafeCastNode(arguments.get(0), StampFactory.forKind(wordKind)))); + break; + + case FROM_ARRAY: + assert arguments.size() == 2; + replace(invoke, graph.unique(new ComputeAddressNode(arguments.get(0), arguments.get(1), StampFactory.forKind(wordKind)))); + break; + + case TO_OBJECT: + assert arguments.size() == 1; + replace(invoke, graph.unique(new UnsafeCastNode(arguments.get(0), invoke.asNode().stamp()))); + break; + + default: + throw new GraalInternalError("Unknown opcode: %s", operation.opcode()); + } + } + } + + protected ValueNode fromUnsigned(StructuredGraph graph, ValueNode value) { return convert(graph, value, wordKind, ConvertNode.Op.L2I, ConvertNode.Op.UNSIGNED_I2L); } @@ -258,7 +310,7 @@ return convert(graph, value, wordKind, ConvertNode.Op.L2I, ConvertNode.Op.I2L); } - private static ValueNode toUnsigned(StructuredGraph graph, ValueNode value, Kind toKind) { + protected ValueNode toUnsigned(StructuredGraph graph, ValueNode value, Kind toKind) { return convert(graph, value, toKind, ConvertNode.Op.L2I, ConvertNode.Op.UNSIGNED_I2L); } @@ -279,14 +331,15 @@ } } - private ValueNode nodeClassOp(StructuredGraph graph, Class nodeClass, ValueNode left, ValueNode right, Invoke invoke) { + /** + * Create an instance of a binary node which is used to lower Word operations. This method is + * called for all Word operations which are annotated with @Operation(node = ...) and + * encapsulates the reflective allocation of the node. + */ + private static ValueNode createBinaryNodeInstance(Class nodeClass, Kind kind, ValueNode left, ValueNode right) { try { Constructor constructor = nodeClass.getConstructor(Kind.class, ValueNode.class, ValueNode.class); - ValueNode result = graph.addOrUnique(constructor.newInstance(wordKind, left, right)); - if (result instanceof FixedWithNextNode) { - graph.addBeforeFixed(invoke.asNode(), (FixedWithNextNode) result); - } - return result; + return constructor.newInstance(kind, left, right); } catch (Throwable ex) { throw new GraalInternalError(ex).addContext(nodeClass.getName()); } @@ -329,20 +382,22 @@ return SnippetLocationNode.create(locationIdentity, ConstantNode.forObject(readKind, metaAccess, graph), ConstantNode.forLong(0, graph), offset, ConstantNode.forInt(1, graph), graph); } - private static LocationNode makeLocation(StructuredGraph graph, ValueNode offset, Kind readKind, LocationIdentity locationIdentity) { + protected LocationNode makeLocation(StructuredGraph graph, ValueNode offset, Kind readKind, LocationIdentity locationIdentity) { return IndexedLocationNode.create(locationIdentity, readKind, 0, offset, graph, 1); } - private static ValueNode readOp(StructuredGraph graph, ValueNode base, Invoke invoke, LocationNode location, BarrierType barrierType, boolean compressible) { + protected ValueNode readOp(StructuredGraph graph, ValueNode base, Invoke invoke, LocationNode location, BarrierType barrierType, boolean compressible) { ReadNode read = graph.add(new ReadNode(base, location, invoke.asNode().stamp(), barrierType, compressible)); graph.addBeforeFixed(invoke.asNode(), read); - // The read must not float outside its block otherwise it may float above an explicit zero - // check on its base address + /* + * The read must not float outside its block otherwise it may float above an explicit zero + * check on its base address. + */ read.setGuard(AbstractBeginNode.prevBegin(invoke.asNode())); return read; } - private static ValueNode writeOp(StructuredGraph graph, ValueNode base, ValueNode value, Invoke invoke, LocationNode location, Opcode op) { + protected ValueNode writeOp(StructuredGraph graph, ValueNode base, ValueNode value, Invoke invoke, LocationNode location, Opcode op) { assert op == Opcode.WRITE || op == Opcode.INITIALIZE; WriteNode write = graph.add(new WriteNode(base, value, location, BarrierType.NONE, false, op == Opcode.INITIALIZE)); write.setStateAfter(invoke.stateAfter()); @@ -350,7 +405,7 @@ return write; } - private static void replace(Invoke invoke, ValueNode value) { + protected void replace(Invoke invoke, ValueNode value) { FixedNode next = invoke.next(); invoke.setNext(null); invoke.asNode().replaceAtPredecessor(next); @@ -358,52 +413,19 @@ GraphUtil.killCFG(invoke.asNode()); } - public boolean isWord(ValueNode node) { - /* - * If we already know that we have a word type, we do not need to infer the stamp. This - * avoids exceptions in inferStamp when the inputs have already been rewritten to word, - * i.e., when the expected input is no longer an object. - */ - if (isWord0(node)) { - return true; - } - node.inferStamp(); - return isWord0(node); - } - - private boolean isWord0(ValueNode node) { - if (node.stamp() == StampFactory.forWord()) { - return true; - } - if (node.stamp() instanceof ObjectStamp) { - return isWord(((ObjectStamp) node.stamp()).type()); - } - return false; + protected boolean isWord(ValueNode node) { + return isWord(ObjectStamp.typeOrNull(node)); } - public boolean isWord(ResolvedJavaType type) { - if (type != null && wordBaseType.isAssignableFrom(type)) { - return true; - } - return false; + protected boolean isWord(ResolvedJavaType type) { + return type != null && wordBaseType.isAssignableFrom(type); } - public Kind asKind(JavaType type) { - if (type instanceof ResolvedJavaType) { - return isWord((ResolvedJavaType) type) ? wordKind : type.getKind(); + protected Kind asKind(JavaType type) { + if (type instanceof ResolvedJavaType && isWord((ResolvedJavaType) type)) { + return wordKind; } else { - return Kind.Object; - } - } - - private void changeToWord(ValueNode valueNode) { - if (valueNode.isConstant() && valueNode.asConstant().getKind() == Kind.Object) { - WordBase value = (WordBase) valueNode.asConstant().asObject(); - ConstantNode newConstant = ConstantNode.forIntegerKind(wordKind, value.rawValue(), valueNode.graph()); - valueNode.graph().replaceFloating((ConstantNode) valueNode, newConstant); - } else { - assert !(valueNode instanceof ConstantNode) : "boxed Word constants should not appear in a snippet graph: " + valueNode + ", stamp: " + valueNode.stamp(); - valueNode.setStamp(StampFactory.forKind(wordKind)); + return type.getKind(); } } } diff -r 56e59e384dc1 -r 4eec2ac671c2 graal/com.oracle.graal.word/src/com/oracle/graal/word/phases/WordTypeVerificationPhase.java --- a/graal/com.oracle.graal.word/src/com/oracle/graal/word/phases/WordTypeVerificationPhase.java Wed Sep 18 02:41:52 2013 +0200 +++ b/graal/com.oracle.graal.word/src/com/oracle/graal/word/phases/WordTypeVerificationPhase.java Tue Sep 17 18:36:54 2013 -0700 @@ -36,8 +36,8 @@ import com.oracle.graal.word.Word.Operation; /** - * Verifies invariants that must hold for snippet code above and beyond normal bytecode - * verification. + * Verifies invariants that must hold for code that uses the {@link WordBase word type} above and + * beyond normal bytecode verification. */ public class WordTypeVerificationPhase extends Phase { @@ -49,6 +49,17 @@ @Override protected void run(StructuredGraph graph) { + assert verify(graph); + } + + protected boolean verify(StructuredGraph inputGraph) { + /* + * This is a verification phase, so we do not want to have side effects. Since inferStamps() + * modifies the stamp of nodes, we copy the graph before running the verification. + */ + StructuredGraph graph = inputGraph.copy(); + wordAccess.inferStamps(graph); + for (ValueNode node : graph.getNodes().filter(ValueNode.class)) { for (Node usage : node.usages()) { if (usage instanceof AccessMonitorNode) { @@ -70,32 +81,7 @@ verify(!isWord(node) || ((StoreIndexedNode) usage).value() != node, node, usage, "cannot store word value to array"); } else if (usage instanceof MethodCallTargetNode) { MethodCallTargetNode callTarget = (MethodCallTargetNode) usage; - ResolvedJavaMethod method = callTarget.targetMethod(); - if (method.getAnnotation(NodeIntrinsic.class) == null) { - Invoke invoke = (Invoke) callTarget.usages().first(); - NodeInputList arguments = callTarget.arguments(); - boolean isStatic = Modifier.isStatic(method.getModifiers()); - int argc = 0; - if (!isStatic) { - ValueNode receiver = arguments.get(argc); - if (receiver == node && isWord(node)) { - ResolvedJavaMethod resolvedMethod = wordAccess.getWordImplType().resolveMethod(method); - verify(resolvedMethod != null, node, invoke.asNode(), "cannot resolve method on Word class: " + MetaUtil.format("%H.%n(%P) %r", method)); - Operation operation = resolvedMethod.getAnnotation(Word.Operation.class); - verify(operation != null, node, invoke.asNode(), "cannot dispatch on word value to non @Operation annotated method " + resolvedMethod); - } - argc++; - } - Signature signature = method.getSignature(); - for (int i = 0; i < signature.getParameterCount(false); i++) { - ValueNode argument = arguments.get(argc); - if (argument == node) { - ResolvedJavaType type = (ResolvedJavaType) signature.getParameterType(i, method.getDeclaringClass()); - verify(isWord(type) == isWord(argument), node, invoke.asNode(), "cannot pass word value to non-word parameter " + i + " or vice-versa"); - } - argc++; - } - } + verifyInvoke(node, callTarget); } else if (usage instanceof ObjectEqualsNode) { verify(!isWord(node) || ((ObjectEqualsNode) usage).x() != node, node, usage, "cannot use word type in comparison"); verify(!isWord(node) || ((ObjectEqualsNode) usage).y() != node, node, usage, "cannot use word type in comparison"); @@ -111,19 +97,39 @@ } } } + return true; + } + + protected void verifyInvoke(ValueNode node, MethodCallTargetNode callTarget) { + ResolvedJavaMethod method = callTarget.targetMethod(); + if (method.getAnnotation(NodeIntrinsic.class) == null) { + Invoke invoke = (Invoke) callTarget.usages().first(); + NodeInputList arguments = callTarget.arguments(); + boolean isStatic = Modifier.isStatic(method.getModifiers()); + int argc = 0; + if (!isStatic) { + ValueNode receiver = arguments.get(argc); + if (receiver == node && isWord(node)) { + ResolvedJavaMethod resolvedMethod = wordAccess.wordImplType.resolveMethod(method); + verify(resolvedMethod != null, node, invoke.asNode(), "cannot resolve method on Word class: " + MetaUtil.format("%H.%n(%P) %r", method)); + Operation operation = resolvedMethod.getAnnotation(Word.Operation.class); + verify(operation != null, node, invoke.asNode(), "cannot dispatch on word value to non @Operation annotated method " + resolvedMethod); + } + argc++; + } + Signature signature = method.getSignature(); + for (int i = 0; i < signature.getParameterCount(false); i++) { + ValueNode argument = arguments.get(argc); + if (argument == node) { + ResolvedJavaType type = (ResolvedJavaType) signature.getParameterType(i, method.getDeclaringClass()); + verify(isWord(type) == isWord(argument), node, invoke.asNode(), "cannot pass word value to non-word parameter " + i + " or vice-versa"); + } + argc++; + } + } } private boolean isWord(ValueNode node) { - if (node instanceof ProxyNode) { - /* - * The proxy node will eventually get the same stamp as the value it is proxying. - * However, since we cannot guarantee the order in which isWord is called during the - * verification phase, the stamp assignment for the value might not have happened yet. - * Therefore, we check the proxied value directly instead of the proxy. - */ - return isWord(((ProxyNode) node).value()); - } - return wordAccess.isWord(node); }