001/*
002 * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.
008 *
009 * This code is distributed in the hope that it will be useful, but WITHOUT
010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
011 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
012 * version 2 for more details (a copy is included in the LICENSE file that
013 * accompanied this code).
014 *
015 * You should have received a copy of the GNU General Public License version
016 * 2 along with this work; if not, write to the Free Software Foundation,
017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
018 *
019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
020 * or visit www.oracle.com if you need additional information or have any
021 * questions.
022 */
023package com.oracle.graal.phases.common.inlining.info.elem;
024
025import static com.oracle.graal.compiler.common.GraalOptions.*;
026import static com.oracle.graal.phases.common.DeadCodeEliminationPhase.Optionality.*;
027
028import java.util.*;
029
030import com.oracle.graal.debug.*;
031import jdk.internal.jvmci.meta.*;
032
033import com.oracle.graal.compiler.common.type.*;
034import com.oracle.graal.graph.*;
035import com.oracle.graal.nodes.*;
036import com.oracle.graal.nodes.StructuredGraph.AllowAssumptions;
037import com.oracle.graal.phases.common.*;
038import com.oracle.graal.phases.common.inlining.*;
039import com.oracle.graal.phases.graph.*;
040import com.oracle.graal.phases.tiers.*;
041
042/**
043 * <p>
044 * Represents a feasible concrete target for inlining, whose graph has been copied already and thus
045 * can be modified without affecting the original (usually cached) version.
046 * </p>
047 *
048 * <p>
049 * Instances of this class don't make sense in isolation but as part of an
050 * {@link com.oracle.graal.phases.common.inlining.info.InlineInfo InlineInfo}.
051 * </p>
052 *
053 * @see com.oracle.graal.phases.common.inlining.walker.InliningData#moveForward()
054 * @see com.oracle.graal.phases.common.inlining.walker.CallsiteHolderExplorable
055 */
056public class InlineableGraph implements Inlineable {
057
058    private final StructuredGraph graph;
059
060    private FixedNodeProbabilityCache probabilites = new FixedNodeProbabilityCache();
061
062    public InlineableGraph(final ResolvedJavaMethod method, final Invoke invoke, final HighTierContext context, CanonicalizerPhase canonicalizer) {
063        StructuredGraph original = getOriginalGraph(method, context, canonicalizer, invoke.asNode().graph(), invoke.bci());
064        // TODO copying the graph is only necessary if it is modified or if it contains any invokes
065        this.graph = (StructuredGraph) original.copy();
066        specializeGraphToArguments(invoke, context, canonicalizer);
067    }
068
069    /**
070     * This method looks up in a cache the graph for the argument, if not found bytecode is parsed.
071     * The graph thus obtained is returned, ie the caller is responsible for cloning before
072     * modification.
073     */
074    private static StructuredGraph getOriginalGraph(final ResolvedJavaMethod method, final HighTierContext context, CanonicalizerPhase canonicalizer, StructuredGraph caller, int callerBci) {
075        StructuredGraph result = InliningUtil.getIntrinsicGraph(context.getReplacements(), method, callerBci);
076        if (result != null) {
077            return result;
078        }
079        return parseBytecodes(method, context, canonicalizer, caller);
080    }
081
082    /**
083     * @return true iff one or more parameters <code>newGraph</code> were specialized to account for
084     *         a constant argument, or an argument with a more specific stamp.
085     */
086    private boolean specializeGraphToArguments(final Invoke invoke, final HighTierContext context, CanonicalizerPhase canonicalizer) {
087        try (Debug.Scope s = Debug.scope("InlineGraph", graph)) {
088
089            ArrayList<Node> parameterUsages = replaceParamsWithMoreInformativeArguments(invoke, context);
090            if (parameterUsages != null && OptCanonicalizer.getValue()) {
091                assert !parameterUsages.isEmpty() : "The caller didn't have more information about arguments after all";
092                canonicalizer.applyIncremental(graph, context, parameterUsages);
093                return true;
094            } else {
095                // TODO (chaeubl): if args are not more concrete, inlining should be avoided
096                // in most cases or we could at least use the previous graph size + invoke
097                // probability to check the inlining
098                return false;
099            }
100
101        } catch (Throwable e) {
102            throw Debug.handle(e);
103        }
104    }
105
106    private static boolean isArgMoreInformativeThanParam(ValueNode arg, ParameterNode param) {
107        return arg.isConstant() || canStampBeImproved(arg, param);
108    }
109
110    private static boolean canStampBeImproved(ValueNode arg, ParameterNode param) {
111        return improvedStamp(arg, param) != null;
112    }
113
114    private static Stamp improvedStamp(ValueNode arg, ParameterNode param) {
115        Stamp joinedStamp = param.stamp().join(arg.stamp());
116        if (joinedStamp == null || joinedStamp.equals(param.stamp())) {
117            return null;
118        }
119        return joinedStamp;
120    }
121
122    /**
123     * This method detects:
124     * <ul>
125     * <li>constants among the arguments to the <code>invoke</code></li>
126     * <li>arguments with more precise type than that declared by the corresponding parameter</li>
127     * </ul>
128     *
129     * <p>
130     * The corresponding parameters are updated to reflect the above information. Before doing so,
131     * their usages are added to <code>parameterUsages</code> for later incremental
132     * canonicalization.
133     * </p>
134     *
135     * @return null if no incremental canonicalization is need, a list of nodes for such
136     *         canonicalization otherwise.
137     */
138    private ArrayList<Node> replaceParamsWithMoreInformativeArguments(final Invoke invoke, final HighTierContext context) {
139        NodeInputList<ValueNode> args = invoke.callTarget().arguments();
140        ArrayList<Node> parameterUsages = null;
141        List<ParameterNode> params = graph.getNodes(ParameterNode.TYPE).snapshot();
142        assert params.size() <= args.size();
143        /*
144         * param-nodes that aren't used (eg, as a result of canonicalization) don't occur in
145         * `params`. Thus, in general, the sizes of `params` and `args` don't always match. Still,
146         * it's always possible to pair a param-node with its corresponding arg-node using
147         * param.index() as index into `args`.
148         */
149        for (ParameterNode param : params) {
150            if (param.usages().isNotEmpty()) {
151                ValueNode arg = args.get(param.index());
152                if (arg.isConstant()) {
153                    Constant constant = arg.asConstant();
154                    parameterUsages = trackParameterUsages(param, parameterUsages);
155                    // collect param usages before replacing the param
156                    graph.replaceFloating(param, ConstantNode.forConstant(arg.stamp(), constant, context.getMetaAccess(), graph));
157                    // param-node gone, leaving a gap in the sequence given by param.index()
158                } else {
159                    Stamp impro = improvedStamp(arg, param);
160                    if (impro != null) {
161                        param.setStamp(impro);
162                        parameterUsages = trackParameterUsages(param, parameterUsages);
163                    } else {
164                        assert !isArgMoreInformativeThanParam(arg, param);
165                    }
166                }
167            }
168        }
169        assert (parameterUsages == null) || (!parameterUsages.isEmpty());
170        return parameterUsages;
171    }
172
173    private static ArrayList<Node> trackParameterUsages(ParameterNode param, ArrayList<Node> parameterUsages) {
174        ArrayList<Node> result = (parameterUsages == null) ? new ArrayList<>() : parameterUsages;
175        param.usages().snapshotTo(result);
176        return result;
177    }
178
179    /**
180     * This method builds the IR nodes for the given <code>method</code> and canonicalizes them.
181     * Provided profiling info is mature, the resulting graph is cached. The caller is responsible
182     * for cloning before modification. </p>
183     */
184    private static StructuredGraph parseBytecodes(ResolvedJavaMethod method, HighTierContext context, CanonicalizerPhase canonicalizer, StructuredGraph caller) {
185        StructuredGraph newGraph = new StructuredGraph(method, AllowAssumptions.from(caller.getAssumptions() != null));
186        try (Debug.Scope s = Debug.scope("InlineGraph", newGraph)) {
187            if (!caller.isInlinedMethodRecordingEnabled()) {
188                // Don't record inlined methods in the callee if
189                // the caller doesn't want them. This decision is
190                // preserved in the graph cache (if used) which is
191                // ok since the graph cache is compilation local.
192                newGraph.disableInlinedMethodRecording();
193            }
194            if (!caller.isUnsafeAccessTrackingEnabled()) {
195                newGraph.disableUnsafeAccessTracking();
196            }
197            if (context.getGraphBuilderSuite() != null) {
198                context.getGraphBuilderSuite().apply(newGraph, context);
199            }
200            assert newGraph.start().next() != null : "graph needs to be populated by the GraphBuilderSuite " + method + ", " + method.canBeInlined();
201
202            new DeadCodeEliminationPhase(Optional).apply(newGraph);
203
204            if (OptCanonicalizer.getValue()) {
205                canonicalizer.apply(newGraph, context);
206            }
207
208            return newGraph;
209        } catch (Throwable e) {
210            throw Debug.handle(e);
211        }
212    }
213
214    @Override
215    public int getNodeCount() {
216        return graph.getNodeCount();
217    }
218
219    @Override
220    public Iterable<Invoke> getInvokes() {
221        return graph.getInvokes();
222    }
223
224    @Override
225    public double getProbability(Invoke invoke) {
226        return probabilites.applyAsDouble(invoke.asNode());
227    }
228
229    public StructuredGraph getGraph() {
230        return graph;
231    }
232}