GraphKit.java revision 13083:b9a173f12fe6
1/*
2 * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23package org.graalvm.compiler.replacements;
24
25import static org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext.CompilationContext.INLINE_AFTER_PARSING;
26
27import java.lang.reflect.Method;
28import java.lang.reflect.Modifier;
29import java.util.ArrayList;
30import java.util.List;
31
32import org.graalvm.compiler.core.common.spi.ConstantFieldProvider;
33import org.graalvm.compiler.core.common.type.StampFactory;
34import org.graalvm.compiler.core.common.type.StampPair;
35import org.graalvm.compiler.graph.Graph;
36import org.graalvm.compiler.graph.Node.ValueNumberable;
37import org.graalvm.compiler.java.FrameStateBuilder;
38import org.graalvm.compiler.java.GraphBuilderPhase;
39import org.graalvm.compiler.nodes.AbstractBeginNode;
40import org.graalvm.compiler.nodes.AbstractMergeNode;
41import org.graalvm.compiler.nodes.BeginNode;
42import org.graalvm.compiler.nodes.CallTargetNode.InvokeKind;
43import org.graalvm.compiler.nodes.EndNode;
44import org.graalvm.compiler.nodes.FixedNode;
45import org.graalvm.compiler.nodes.FixedWithNextNode;
46import org.graalvm.compiler.nodes.IfNode;
47import org.graalvm.compiler.nodes.InvokeNode;
48import org.graalvm.compiler.nodes.LogicNode;
49import org.graalvm.compiler.nodes.MergeNode;
50import org.graalvm.compiler.nodes.StructuredGraph;
51import org.graalvm.compiler.nodes.ValueNode;
52import org.graalvm.compiler.nodes.calc.FloatingNode;
53import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration;
54import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins;
55import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderTool;
56import org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext;
57import org.graalvm.compiler.nodes.java.MethodCallTargetNode;
58import org.graalvm.compiler.nodes.spi.StampProvider;
59import org.graalvm.compiler.nodes.type.StampTool;
60import org.graalvm.compiler.phases.OptimisticOptimizations;
61import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase;
62import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase.Optionality;
63import org.graalvm.compiler.phases.common.inlining.InliningUtil;
64import org.graalvm.compiler.phases.util.Providers;
65import org.graalvm.compiler.word.WordTypes;
66
67import jdk.vm.ci.code.BytecodeFrame;
68import jdk.vm.ci.meta.ConstantReflectionProvider;
69import jdk.vm.ci.meta.JavaKind;
70import jdk.vm.ci.meta.JavaType;
71import jdk.vm.ci.meta.MetaAccessProvider;
72import jdk.vm.ci.meta.ResolvedJavaMethod;
73import jdk.vm.ci.meta.ResolvedJavaType;
74import jdk.vm.ci.meta.Signature;
75
76/**
77 * A utility for manually creating a graph. This will be expanded as necessary to support all
78 * subsystems that employ manual graph creation (as opposed to {@linkplain GraphBuilderPhase
79 * bytecode parsing} based graph creation).
80 */
81public class GraphKit implements GraphBuilderTool {
82
83    protected final Providers providers;
84    protected final StructuredGraph graph;
85    protected final WordTypes wordTypes;
86    protected final GraphBuilderConfiguration.Plugins graphBuilderPlugins;
87    protected FixedWithNextNode lastFixedNode;
88
89    private final List<Structure> structures;
90
91    protected abstract static class Structure {
92    }
93
94    public GraphKit(StructuredGraph graph, Providers providers, WordTypes wordTypes, GraphBuilderConfiguration.Plugins graphBuilderPlugins) {
95        this.providers = providers;
96        this.graph = graph;
97        this.wordTypes = wordTypes;
98        this.graphBuilderPlugins = graphBuilderPlugins;
99        this.lastFixedNode = graph.start();
100
101        structures = new ArrayList<>();
102        /*
103         * Add a dummy element, so that the access of the last element never leads to an exception.
104         */
105        structures.add(new Structure() {
106        });
107    }
108
109    @Override
110    public StructuredGraph getGraph() {
111        return graph;
112    }
113
114    @Override
115    public ConstantReflectionProvider getConstantReflection() {
116        return providers.getConstantReflection();
117    }
118
119    @Override
120    public ConstantFieldProvider getConstantFieldProvider() {
121        return providers.getConstantFieldProvider();
122    }
123
124    @Override
125    public MetaAccessProvider getMetaAccess() {
126        return providers.getMetaAccess();
127    }
128
129    @Override
130    public StampProvider getStampProvider() {
131        return providers.getStampProvider();
132    }
133
134    @Override
135    public boolean parsingIntrinsic() {
136        return true;
137    }
138
139    /**
140     * Ensures a floating node is added to or already present in the graph via {@link Graph#unique}.
141     *
142     * @return a node similar to {@code node} if one exists, otherwise {@code node}
143     */
144    public <T extends FloatingNode & ValueNumberable> T unique(T node) {
145        return graph.unique(changeToWord(node));
146    }
147
148    public <T extends ValueNode> T add(T node) {
149        return graph.add(changeToWord(node));
150    }
151
152    public <T extends ValueNode> T changeToWord(T node) {
153        if (wordTypes != null && wordTypes.isWord(node)) {
154            node.setStamp(wordTypes.getWordStamp(StampTool.typeOrNull(node)));
155        }
156        return node;
157    }
158
159    @Override
160    public <T extends ValueNode> T append(T node) {
161        T result = graph.addOrUniqueWithInputs(changeToWord(node));
162        if (result instanceof FixedNode) {
163            updateLastFixed((FixedNode) result);
164        }
165        return result;
166    }
167
168    private void updateLastFixed(FixedNode result) {
169        assert lastFixedNode != null;
170        assert result.predecessor() == null;
171        graph.addAfterFixed(lastFixedNode, result);
172        if (result instanceof FixedWithNextNode) {
173            lastFixedNode = (FixedWithNextNode) result;
174        } else {
175            lastFixedNode = null;
176        }
177    }
178
179    public InvokeNode createInvoke(Class<?> declaringClass, String name, ValueNode... args) {
180        return createInvoke(declaringClass, name, InvokeKind.Static, null, BytecodeFrame.UNKNOWN_BCI, args);
181    }
182
183    /**
184     * Creates and appends an {@link InvokeNode} for a call to a given method with a given set of
185     * arguments. The method is looked up via reflection based on the declaring class and name.
186     *
187     * @param declaringClass the class declaring the invoked method
188     * @param name the name of the invoked method
189     * @param args the arguments to the invocation
190     */
191    public InvokeNode createInvoke(Class<?> declaringClass, String name, InvokeKind invokeKind, FrameStateBuilder frameStateBuilder, int bci, ValueNode... args) {
192        boolean isStatic = invokeKind == InvokeKind.Static;
193        ResolvedJavaMethod method = findMethod(declaringClass, name, isStatic);
194        return createInvoke(method, invokeKind, frameStateBuilder, bci, args);
195    }
196
197    public ResolvedJavaMethod findMethod(Class<?> declaringClass, String name, boolean isStatic) {
198        ResolvedJavaMethod method = null;
199        for (Method m : declaringClass.getDeclaredMethods()) {
200            if (Modifier.isStatic(m.getModifiers()) == isStatic && m.getName().equals(name)) {
201                assert method == null : "found more than one method in " + declaringClass + " named " + name;
202                method = providers.getMetaAccess().lookupJavaMethod(m);
203            }
204        }
205        assert method != null : "did not find method in " + declaringClass + " named " + name;
206        return method;
207    }
208
209    public ResolvedJavaMethod findMethod(Class<?> declaringClass, String name, Class<?>... parameterTypes) {
210        try {
211            Method m = declaringClass.getDeclaredMethod(name, parameterTypes);
212            return providers.getMetaAccess().lookupJavaMethod(m);
213        } catch (NoSuchMethodException | SecurityException e) {
214            throw new AssertionError(e);
215        }
216    }
217
218    /**
219     * Creates and appends an {@link InvokeNode} for a call to a given method with a given set of
220     * arguments.
221     */
222    public InvokeNode createInvoke(ResolvedJavaMethod method, InvokeKind invokeKind, FrameStateBuilder frameStateBuilder, int bci, ValueNode... args) {
223        assert method.isStatic() == (invokeKind == InvokeKind.Static);
224        Signature signature = method.getSignature();
225        JavaType returnType = signature.getReturnType(null);
226        assert checkArgs(method, args);
227        StampPair returnStamp = graphBuilderPlugins.getOverridingStamp(this, returnType, false);
228        if (returnStamp == null) {
229            returnStamp = StampFactory.forDeclaredType(graph.getAssumptions(), returnType, false);
230        }
231        MethodCallTargetNode callTarget = graph.add(createMethodCallTarget(invokeKind, method, args, returnStamp, bci));
232        InvokeNode invoke = append(new InvokeNode(callTarget, bci));
233
234        if (frameStateBuilder != null) {
235            if (invoke.getStackKind() != JavaKind.Void) {
236                frameStateBuilder.push(returnType.getJavaKind(), invoke);
237            }
238            invoke.setStateAfter(frameStateBuilder.create(bci, invoke));
239            if (invoke.getStackKind() != JavaKind.Void) {
240                frameStateBuilder.pop(returnType.getJavaKind());
241            }
242        }
243        return invoke;
244    }
245
246    protected MethodCallTargetNode createMethodCallTarget(InvokeKind invokeKind, ResolvedJavaMethod targetMethod, ValueNode[] args, StampPair returnStamp, @SuppressWarnings("unused") int bci) {
247        return new MethodCallTargetNode(invokeKind, targetMethod, args, returnStamp, null);
248    }
249
250    /**
251     * Determines if a given set of arguments is compatible with the signature of a given method.
252     *
253     * @return true if {@code args} are compatible with the signature if {@code method}
254     * @throws AssertionError if {@code args} are not compatible with the signature if
255     *             {@code method}
256     */
257    public boolean checkArgs(ResolvedJavaMethod method, ValueNode... args) {
258        Signature signature = method.getSignature();
259        boolean isStatic = method.isStatic();
260        if (signature.getParameterCount(!isStatic) != args.length) {
261            throw new AssertionError(graph + ": wrong number of arguments to " + method);
262        }
263        int argIndex = 0;
264        if (!isStatic) {
265            ResolvedJavaType expectedType = method.getDeclaringClass();
266            JavaKind expected = wordTypes == null ? expectedType.getJavaKind() : wordTypes.asKind(expectedType);
267            JavaKind actual = args[argIndex++].stamp().getStackKind();
268            assert expected == actual : graph + ": wrong kind of value for receiver argument of call to " + method + " [" + actual + " != " + expected + "]";
269        }
270        for (int i = 0; i != signature.getParameterCount(false); i++) {
271            JavaType expectedType = signature.getParameterType(i, method.getDeclaringClass());
272            JavaKind expected = wordTypes == null ? expectedType.getJavaKind().getStackKind() : wordTypes.asKind(expectedType).getStackKind();
273            JavaKind actual = args[argIndex++].stamp().getStackKind();
274            if (expected != actual) {
275                throw new AssertionError(graph + ": wrong kind of value for argument " + i + " of call to " + method + " [" + actual + " != " + expected + "]");
276            }
277        }
278        return true;
279    }
280
281    /**
282     * Recursively {@linkplain #inline inlines} all invocations currently in the graph.
283     */
284    public void inlineInvokes() {
285        while (!graph.getNodes().filter(InvokeNode.class).isEmpty()) {
286            for (InvokeNode invoke : graph.getNodes().filter(InvokeNode.class).snapshot()) {
287                inline(invoke);
288            }
289        }
290
291        // Clean up all code that is now dead after inlining.
292        new DeadCodeEliminationPhase().apply(graph);
293    }
294
295    /**
296     * Inlines a given invocation to a method. The graph of the inlined method is processed in the
297     * same manner as for snippets and method substitutions.
298     */
299    public void inline(InvokeNode invoke) {
300        ResolvedJavaMethod method = ((MethodCallTargetNode) invoke.callTarget()).targetMethod();
301
302        MetaAccessProvider metaAccess = providers.getMetaAccess();
303        Plugins plugins = new Plugins(graphBuilderPlugins);
304        GraphBuilderConfiguration config = GraphBuilderConfiguration.getSnippetDefault(plugins);
305
306        StructuredGraph calleeGraph = new StructuredGraph.Builder(invoke.getOptions()).method(method).build();
307        IntrinsicContext initialReplacementContext = new IntrinsicContext(method, method, providers.getReplacements().getDefaultReplacementBytecodeProvider(), INLINE_AFTER_PARSING);
308        GraphBuilderPhase.Instance instance = new GraphBuilderPhase.Instance(metaAccess, providers.getStampProvider(), providers.getConstantReflection(), providers.getConstantFieldProvider(), config,
309                        OptimisticOptimizations.NONE,
310                        initialReplacementContext);
311        instance.apply(calleeGraph);
312
313        // Remove all frame states from inlinee
314        calleeGraph.clearAllStateAfter();
315        new DeadCodeEliminationPhase(Optionality.Required).apply(calleeGraph);
316
317        InliningUtil.inline(invoke, calleeGraph, false, method);
318    }
319
320    protected void pushStructure(Structure structure) {
321        structures.add(structure);
322    }
323
324    protected <T extends Structure> T getTopStructure(Class<T> expectedClass) {
325        return expectedClass.cast(structures.get(structures.size() - 1));
326    }
327
328    protected void popStructure() {
329        structures.remove(structures.size() - 1);
330    }
331
332    protected enum IfState {
333        CONDITION,
334        THEN_PART,
335        ELSE_PART,
336        FINISHED
337    }
338
339    static class IfStructure extends Structure {
340        protected IfState state;
341        protected FixedNode thenPart;
342        protected FixedNode elsePart;
343    }
344
345    /**
346     * Starts an if-block. This call can be followed by a call to {@link #thenPart} to start
347     * emitting the code executed when the condition hold; and a call to {@link #elsePart} to start
348     * emititng the code when the condition does not hold. It must be followed by a call to
349     * {@link #endIf} to close the if-block.
350     *
351     * @param condition The condition for the if-block
352     * @param trueProbability The estimated probability the condition is true
353     */
354    public void startIf(LogicNode condition, double trueProbability) {
355        AbstractBeginNode thenSuccessor = graph.add(new BeginNode());
356        AbstractBeginNode elseSuccessor = graph.add(new BeginNode());
357        append(new IfNode(condition, thenSuccessor, elseSuccessor, trueProbability));
358        lastFixedNode = null;
359
360        IfStructure s = new IfStructure();
361        s.state = IfState.CONDITION;
362        s.thenPart = thenSuccessor;
363        s.elsePart = elseSuccessor;
364        pushStructure(s);
365    }
366
367    private IfStructure saveLastNode() {
368        IfStructure s = getTopStructure(IfStructure.class);
369        switch (s.state) {
370            case CONDITION:
371                assert lastFixedNode == null;
372                break;
373            case THEN_PART:
374                s.thenPart = lastFixedNode;
375                break;
376            case ELSE_PART:
377                s.elsePart = lastFixedNode;
378                break;
379            case FINISHED:
380                assert false;
381                break;
382        }
383        lastFixedNode = null;
384        return s;
385    }
386
387    public void thenPart() {
388        IfStructure s = saveLastNode();
389        lastFixedNode = (FixedWithNextNode) s.thenPart;
390        s.state = IfState.THEN_PART;
391    }
392
393    public void elsePart() {
394        IfStructure s = saveLastNode();
395        lastFixedNode = (FixedWithNextNode) s.elsePart;
396        s.state = IfState.ELSE_PART;
397    }
398
399    public void endIf() {
400        IfStructure s = saveLastNode();
401
402        FixedWithNextNode thenPart = s.thenPart instanceof FixedWithNextNode ? (FixedWithNextNode) s.thenPart : null;
403        FixedWithNextNode elsePart = s.elsePart instanceof FixedWithNextNode ? (FixedWithNextNode) s.elsePart : null;
404
405        if (thenPart != null && elsePart != null) {
406            /* Both parts are alive, we need a real merge. */
407            EndNode thenEnd = graph.add(new EndNode());
408            graph.addAfterFixed(thenPart, thenEnd);
409            EndNode elseEnd = graph.add(new EndNode());
410            graph.addAfterFixed(elsePart, elseEnd);
411
412            AbstractMergeNode merge = graph.add(new MergeNode());
413            merge.addForwardEnd(thenEnd);
414            merge.addForwardEnd(elseEnd);
415
416            lastFixedNode = merge;
417
418        } else if (thenPart != null) {
419            /* elsePart ended with a control sink, so we can continue with thenPart. */
420            lastFixedNode = thenPart;
421
422        } else if (elsePart != null) {
423            /* thenPart ended with a control sink, so we can continue with elsePart. */
424            lastFixedNode = elsePart;
425
426        } else {
427            /* Both parts ended with a control sink, so no nodes can be added after the if. */
428            assert lastFixedNode == null;
429        }
430        s.state = IfState.FINISHED;
431        popStructure();
432    }
433}
434