ForeignCallStub.java revision 12968:4d8a004e5c6d
1160814Ssimon/*
2160814Ssimon * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
3160814Ssimon * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4160814Ssimon *
5160814Ssimon * This code is free software; you can redistribute it and/or modify it
6160814Ssimon * under the terms of the GNU General Public License version 2 only, as
7160814Ssimon * published by the Free Software Foundation.
8160814Ssimon *
9160814Ssimon * This code is distributed in the hope that it will be useful, but WITHOUT
10160814Ssimon * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11160814Ssimon * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12160814Ssimon * version 2 for more details (a copy is included in the LICENSE file that
13160814Ssimon * accompanied this code).
14160814Ssimon *
15160814Ssimon * You should have received a copy of the GNU General Public License version
16160814Ssimon * 2 along with this work; if not, write to the Free Software Foundation,
17160814Ssimon * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18160814Ssimon *
19160814Ssimon * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20160814Ssimon * or visit www.oracle.com if you need additional information or have any
21160814Ssimon * questions.
22160814Ssimon */
23160814Ssimonpackage org.graalvm.compiler.hotspot.stubs;
24280297Sjkim
25160814Ssimonimport static org.graalvm.compiler.hotspot.HotSpotForeignCallLinkage.RegisterEffect.DESTROYS_REGISTERS;
26160814Ssimonimport static org.graalvm.compiler.hotspot.HotSpotForeignCallLinkage.RegisterEffect.PRESERVES_REGISTERS;
27160814Ssimonimport static jdk.vm.ci.hotspot.HotSpotCallingConventionType.JavaCall;
28160814Ssimonimport static jdk.vm.ci.hotspot.HotSpotCallingConventionType.JavaCallee;
29160814Ssimonimport static jdk.vm.ci.hotspot.HotSpotCallingConventionType.NativeCall;
30160814Ssimon
31160814Ssimonimport org.graalvm.compiler.core.common.CompilationIdentifier;
32160814Ssimonimport org.graalvm.compiler.core.common.LIRKind;
33160814Ssimonimport org.graalvm.compiler.core.common.LocationIdentity;
34160814Ssimonimport org.graalvm.compiler.core.common.spi.ForeignCallDescriptor;
35160814Ssimonimport org.graalvm.compiler.core.common.type.StampFactory;
36160814Ssimonimport org.graalvm.compiler.core.common.type.StampPair;
37160814Ssimonimport org.graalvm.compiler.debug.Debug;
38160814Ssimonimport org.graalvm.compiler.debug.JavaMethodContext;
39160814Ssimonimport org.graalvm.compiler.hotspot.HotSpotForeignCallLinkage;
40160814Ssimonimport org.graalvm.compiler.hotspot.HotSpotForeignCallLinkage.Transition;
41160814Ssimonimport org.graalvm.compiler.hotspot.HotSpotForeignCallLinkageImpl;
42160814Ssimonimport org.graalvm.compiler.hotspot.meta.HotSpotProviders;
43160814Ssimonimport org.graalvm.compiler.hotspot.nodes.StubForeignCallNode;
44160814Ssimonimport org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil;
45160814Ssimonimport org.graalvm.compiler.nodes.ConstantNode;
46160814Ssimonimport org.graalvm.compiler.nodes.InvokeNode;
47160814Ssimonimport org.graalvm.compiler.nodes.ParameterNode;
48160814Ssimonimport org.graalvm.compiler.nodes.ReturnNode;
49160814Ssimonimport org.graalvm.compiler.nodes.StructuredGraph;
50160814Ssimonimport org.graalvm.compiler.nodes.ValueNode;
51160814Ssimonimport org.graalvm.compiler.options.OptionValues;
52160814Ssimonimport org.graalvm.compiler.phases.common.RemoveValueProxyPhase;
53160814Ssimonimport org.graalvm.compiler.replacements.GraphKit;
54160814Ssimonimport org.graalvm.compiler.replacements.nodes.ReadRegisterNode;
55160814Ssimonimport org.graalvm.compiler.word.Word;
56160814Ssimonimport org.graalvm.compiler.word.WordTypes;
57160814Ssimon
58160814Ssimonimport jdk.vm.ci.hotspot.HotSpotJVMCIRuntimeProvider;
59160814Ssimonimport jdk.vm.ci.hotspot.HotSpotSignature;
60160814Ssimonimport jdk.vm.ci.meta.JavaMethod;
61160814Ssimonimport jdk.vm.ci.meta.JavaType;
62160814Ssimonimport jdk.vm.ci.meta.MetaAccessProvider;
63160814Ssimonimport jdk.vm.ci.meta.ResolvedJavaMethod;
64160814Ssimonimport jdk.vm.ci.meta.ResolvedJavaType;
65160814Ssimonimport jdk.vm.ci.meta.Signature;
66160814Ssimon
67160814Ssimon/**
68160814Ssimon * A {@linkplain #getGraph generated} stub for a {@link Transition non-leaf} foreign call from
69160814Ssimon * compiled code. A stub is required for such calls as the caller may be scheduled for
70160814Ssimon * deoptimization while the call is in progress. And since these are foreign/runtime calls on slow
71160814Ssimon * paths, we don't want to force the register allocator to spill around the call. As such, this stub
72160814Ssimon * saves and restores all allocatable registers. It also
73280297Sjkim * {@linkplain StubUtil#handlePendingException(Word, boolean) handles} any exceptions raised during
74160814Ssimon * the foreign call.
75160814Ssimon */
76238405Sjkimpublic class ForeignCallStub extends Stub {
77280297Sjkim
78238405Sjkim    private final HotSpotJVMCIRuntimeProvider jvmciRuntime;
79160814Ssimon
80280297Sjkim    /**
81160814Ssimon     * The target of the call.
82160814Ssimon     */
83160814Ssimon    private final HotSpotForeignCallLinkage target;
84160814Ssimon
85160814Ssimon    /**
86280297Sjkim     * Specifies if the JavaThread value for the current thread is to be prepended to the arguments
87160814Ssimon     * for the call to {@link #target}.
88160814Ssimon     */
89280297Sjkim    protected final boolean prependThread;
90280297Sjkim
91280297Sjkim    /**
92160814Ssimon     * Creates a stub for a call to code at a given address.
93160814Ssimon     *
94280297Sjkim     * @param address the address of the code to call
95280297Sjkim     * @param descriptor the signature of the call to this stub
96238405Sjkim     * @param prependThread true if the JavaThread value for the current thread is to be prepended
97280297Sjkim     *            to the arguments for the call to {@code address}
98280297Sjkim     * @param reexecutable specifies if the stub call can be re-executed without (meaningful) side
99280297Sjkim     *            effects. Deoptimization will not return to a point before a stub call that cannot
100280297Sjkim     *            be re-executed.
101238405Sjkim     * @param killedLocations the memory locations killed by the stub call
102280297Sjkim     */
103238405Sjkim    public ForeignCallStub(OptionValues options, HotSpotJVMCIRuntimeProvider runtime, HotSpotProviders providers, long address, ForeignCallDescriptor descriptor, boolean prependThread,
104280297Sjkim                    Transition transition,
105280297Sjkim                    boolean reexecutable, LocationIdentity... killedLocations) {
106280297Sjkim        super(options, providers, HotSpotForeignCallLinkageImpl.create(providers.getMetaAccess(), providers.getCodeCache(), providers.getWordTypes(), providers.getForeignCalls(), descriptor, 0L,
107160814Ssimon                        PRESERVES_REGISTERS, JavaCall, JavaCallee, transition, reexecutable, killedLocations));
108160814Ssimon        this.jvmciRuntime = runtime;
109280297Sjkim        this.prependThread = prependThread;
110280297Sjkim        Class<?>[] targetParameterTypes = createTargetParameters(descriptor);
111160814Ssimon        ForeignCallDescriptor targetSig = new ForeignCallDescriptor(descriptor.getName() + ":C", descriptor.getResultType(), targetParameterTypes);
112280297Sjkim        target = HotSpotForeignCallLinkageImpl.create(providers.getMetaAccess(), providers.getCodeCache(), providers.getWordTypes(), providers.getForeignCalls(), targetSig, address,
113160814Ssimon                        DESTROYS_REGISTERS, NativeCall, NativeCall, transition, reexecutable, killedLocations);
114280297Sjkim    }
115280297Sjkim
116160814Ssimon    /**
117238405Sjkim     * Gets the linkage information for the call from this stub.
118280297Sjkim     */
119280297Sjkim    public HotSpotForeignCallLinkage getTargetLinkage() {
120280297Sjkim        return target;
121238405Sjkim    }
122160814Ssimon
123280297Sjkim    private Class<?>[] createTargetParameters(ForeignCallDescriptor descriptor) {
124280297Sjkim        Class<?>[] parameters = descriptor.getArgumentTypes();
125280297Sjkim        if (prependThread) {
126280297Sjkim            Class<?>[] newParameters = new Class<?>[parameters.length + 1];
127160814Ssimon            System.arraycopy(parameters, 0, newParameters, 1, parameters.length);
128280297Sjkim            newParameters[0] = Word.class;
129160814Ssimon            return newParameters;
130280297Sjkim        }
131280297Sjkim        return parameters;
132160814Ssimon    }
133280297Sjkim
134280297Sjkim    @Override
135160814Ssimon    protected ResolvedJavaMethod getInstalledCodeOwner() {
136160814Ssimon        return null;
137280297Sjkim    }
138280297Sjkim
139160814Ssimon    private class DebugScopeContext implements JavaMethod, JavaMethodContext {
140280297Sjkim        @Override
141280297Sjkim        public JavaMethod asJavaMethod() {
142280297Sjkim            return this;
143280297Sjkim        }
144280297Sjkim
145160814Ssimon        @Override
146280297Sjkim        public Signature getSignature() {
147160814Ssimon            ForeignCallDescriptor d = linkage.getDescriptor();
148280297Sjkim            MetaAccessProvider metaAccess = providers.getMetaAccess();
149280297Sjkim            Class<?>[] arguments = d.getArgumentTypes();
150160814Ssimon            ResolvedJavaType[] parameters = new ResolvedJavaType[arguments.length];
151280297Sjkim            for (int i = 0; i < arguments.length; i++) {
152280297Sjkim                parameters[i] = metaAccess.lookupJavaType(arguments[i]);
153280297Sjkim            }
154280297Sjkim            return new HotSpotSignature(jvmciRuntime, metaAccess.lookupJavaType(d.getResultType()), parameters);
155280297Sjkim        }
156280297Sjkim
157280297Sjkim        @Override
158280297Sjkim        public String getName() {
159280297Sjkim            return linkage.getDescriptor().getName();
160280297Sjkim        }
161280297Sjkim
162160814Ssimon        @Override
163160814Ssimon        public JavaType getDeclaringClass() {
164280297Sjkim            return providers.getMetaAccess().lookupJavaType(ForeignCallStub.class);
165280297Sjkim        }
166160814Ssimon
167280297Sjkim        @Override
168280297Sjkim        public String toString() {
169280297Sjkim            return format("ForeignCallStub<%n(%p)>");
170280297Sjkim        }
171280297Sjkim    }
172280297Sjkim
173280297Sjkim    @Override
174280297Sjkim    protected Object debugScopeContext() {
175160814Ssimon        return new DebugScopeContext() {
176160814Ssimon
177280297Sjkim        };
178280297Sjkim    }
179280297Sjkim
180160814Ssimon    /**
181160814Ssimon     * Creates a graph for this stub.
182160814Ssimon     * <p>
183280297Sjkim     * If the stub returns an object, the graph created corresponds to this pseudo code:
184160814Ssimon     *
185280297Sjkim     * <pre>
186280297Sjkim     *     Object foreignFunctionStub(args...) {
187280297Sjkim     *         foreignFunction(currentThread,  args);
188160814Ssimon     *         if (clearPendingException(thread())) {
189280297Sjkim     *             getAndClearObjectResult(thread());
190160814Ssimon     *             DeoptimizeCallerNode.deopt(InvalidateReprofile, RuntimeConstraint);
191160814Ssimon     *         }
192160814Ssimon     *         return verifyObject(getAndClearObjectResult(thread()));
193280297Sjkim     *     }
194280297Sjkim     * </pre>
195160814Ssimon     *
196160814Ssimon     * If the stub returns a primitive or word, the graph created corresponds to this pseudo code
197280297Sjkim     * (using {@code int} as the primitive return type):
198280297Sjkim     *
199160814Ssimon     * <pre>
200160814Ssimon     *     int foreignFunctionStub(args...) {
201280297Sjkim     *         int result = foreignFunction(currentThread,  args);
202160814Ssimon     *         if (clearPendingException(thread())) {
203280297Sjkim     *             DeoptimizeCallerNode.deopt(InvalidateReprofile, RuntimeConstraint);
204160814Ssimon     *         }
205280297Sjkim     *         return result;
206280297Sjkim     *     }
207160814Ssimon     * </pre>
208160814Ssimon     *
209280297Sjkim     * If the stub is void, the graph created corresponds to this pseudo code:
210280297Sjkim     *
211280297Sjkim     * <pre>
212280297Sjkim     *     void foreignFunctionStub(args...) {
213280297Sjkim     *         foreignFunction(currentThread,  args);
214280297Sjkim     *         if (clearPendingException(thread())) {
215280297Sjkim     *             DeoptimizeCallerNode.deopt(InvalidateReprofile, RuntimeConstraint);
216280297Sjkim     *         }
217280297Sjkim     *     }
218280297Sjkim     * </pre>
219280297Sjkim     *
220280297Sjkim     * In each example above, the {@code currentThread} argument is the C++ JavaThread value (i.e.,
221280297Sjkim     * %r15 on AMD64) and is only prepended if {@link #prependThread} is true.
222280297Sjkim     */
223280297Sjkim    @Override
224280297Sjkim    protected StructuredGraph getGraph(CompilationIdentifier compilationId) {
225280297Sjkim        WordTypes wordTypes = providers.getWordTypes();
226280297Sjkim        Class<?>[] args = linkage.getDescriptor().getArgumentTypes();
227280297Sjkim        boolean isObjectResult = !LIRKind.isValue(linkage.getOutgoingCallingConvention().getReturn());
228325337Sjkim
229325337Sjkim        StructuredGraph graph = new StructuredGraph.Builder(options).name(toString()).compilationId(compilationId).build();
230325337Sjkim        graph.disableUnsafeAccessTracking();
231325337Sjkim
232325337Sjkim        GraphKit kit = new GraphKit(graph, providers, wordTypes, providers.getGraphBuilderPlugins());
233325337Sjkim        ParameterNode[] params = createParameters(kit, args);
234280297Sjkim
235325337Sjkim        ReadRegisterNode thread = kit.append(new ReadRegisterNode(providers.getRegisters().getThreadRegister(), wordTypes.getWordKind(), true, false));
236280297Sjkim        ValueNode result = createTargetCall(kit, params, thread);
237325337Sjkim        kit.createInvoke(StubUtil.class, "handlePendingException", thread, ConstantNode.forBoolean(isObjectResult, graph));
238238405Sjkim        if (isObjectResult) {
239280297Sjkim            InvokeNode object = kit.createInvoke(HotSpotReplacementsUtil.class, "getAndClearObjectResult", thread);
240280297Sjkim            result = kit.createInvoke(StubUtil.class, "verifyObject", object);
241280297Sjkim        }
242280297Sjkim        kit.append(new ReturnNode(linkage.getDescriptor().getResultType() == void.class ? null : result));
243280297Sjkim
244238405Sjkim        if (Debug.isDumpEnabled(Debug.INFO_LOG_LEVEL)) {
245160814Ssimon            Debug.dump(Debug.INFO_LOG_LEVEL, graph, "Initial stub graph");
246280297Sjkim        }
247280297Sjkim
248160814Ssimon        kit.inlineInvokes();
249160814Ssimon
250280297Sjkim        new RemoveValueProxyPhase().apply(graph);
251280297Sjkim
252280297Sjkim        if (Debug.isDumpEnabled(Debug.INFO_LOG_LEVEL)) {
253280297Sjkim            Debug.dump(Debug.INFO_LOG_LEVEL, graph, "Stub graph before compilation");
254280297Sjkim        }
255160814Ssimon
256160814Ssimon        return graph;
257280297Sjkim    }
258280297Sjkim
259280297Sjkim    private ParameterNode[] createParameters(GraphKit kit, Class<?>[] args) {
260280297Sjkim        ParameterNode[] params = new ParameterNode[args.length];
261280297Sjkim        ResolvedJavaType accessingClass = providers.getMetaAccess().lookupJavaType(getClass());
262280297Sjkim        for (int i = 0; i < args.length; i++) {
263280297Sjkim            ResolvedJavaType type = providers.getMetaAccess().lookupJavaType(args[i]).resolve(accessingClass);
264160814Ssimon            StampPair stamp = StampFactory.forDeclaredType(kit.getGraph().getAssumptions(), type, false);
265160814Ssimon            ParameterNode param = kit.unique(new ParameterNode(i, stamp));
266280297Sjkim            params[i] = param;
267280297Sjkim        }
268280297Sjkim        return params;
269280297Sjkim    }
270280297Sjkim
271280297Sjkim    private StubForeignCallNode createTargetCall(GraphKit kit, ParameterNode[] params, ReadRegisterNode thread) {
272280297Sjkim        if (prependThread) {
273            ValueNode[] targetArguments = new ValueNode[1 + params.length];
274            targetArguments[0] = thread;
275            System.arraycopy(params, 0, targetArguments, 1, params.length);
276            return kit.append(new StubForeignCallNode(providers.getForeignCalls(), target.getDescriptor(), targetArguments));
277        } else {
278            return kit.append(new StubForeignCallNode(providers.getForeignCalls(), target.getDescriptor(), params));
279        }
280    }
281}
282