1 /*
2  * Copyright (c) 2012, 2018, 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  */
23 
24 
25 package org.graalvm.compiler.hotspot;
26 
27 import static jdk.vm.ci.hotspot.HotSpotJVMCIRuntime.runtime;
28 import static org.graalvm.compiler.hotspot.HotSpotForeignCallLinkage.RegisterEffect.DESTROYS_REGISTERS;
29 
30 import jdk.internal.vm.compiler.collections.EconomicSet;
31 import org.graalvm.compiler.core.common.spi.ForeignCallDescriptor;
32 import org.graalvm.compiler.core.target.Backend;
33 import org.graalvm.compiler.hotspot.meta.HotSpotForeignCallsProvider;
34 import org.graalvm.compiler.hotspot.stubs.Stub;
35 import org.graalvm.compiler.word.WordTypes;
36 import jdk.internal.vm.compiler.word.LocationIdentity;
37 
38 import jdk.vm.ci.code.CallingConvention;
39 import jdk.vm.ci.code.CallingConvention.Type;
40 import jdk.vm.ci.code.CodeCacheProvider;
41 import jdk.vm.ci.code.InstalledCode;
42 import jdk.vm.ci.code.Register;
43 import jdk.vm.ci.code.RegisterConfig;
44 import jdk.vm.ci.code.ValueKindFactory;
45 import jdk.vm.ci.hotspot.HotSpotCallingConventionType;
46 import jdk.vm.ci.hotspot.HotSpotForeignCallTarget;
47 import jdk.vm.ci.meta.AllocatableValue;
48 import jdk.vm.ci.meta.JavaType;
49 import jdk.vm.ci.meta.MetaAccessProvider;
50 import jdk.vm.ci.meta.ResolvedJavaType;
51 import jdk.vm.ci.meta.Value;
52 
53 /**
54  * The details required to link a HotSpot runtime or stub call.
55  */
56 public class HotSpotForeignCallLinkageImpl extends HotSpotForeignCallTarget implements HotSpotForeignCallLinkage {
57 
58     /**
59      * The descriptor of the call.
60      */
61     protected final ForeignCallDescriptor descriptor;
62 
63     /**
64      * Non-null (eventually) iff this is a call to a compiled {@linkplain Stub stub}.
65      */
66     private Stub stub;
67 
68     /**
69      * The calling convention for this call.
70      */
71     private final CallingConvention outgoingCallingConvention;
72 
73     /**
74      * The calling convention for incoming arguments to the stub, iff this call uses a compiled
75      * {@linkplain Stub stub}.
76      */
77     private final CallingConvention incomingCallingConvention;
78 
79     private final RegisterEffect effect;
80 
81     private final Transition transition;
82 
83     /**
84      * The registers and stack slots defined/killed by the call.
85      */
86     private Value[] temporaries = AllocatableValue.NONE;
87 
88     /**
89      * The memory locations killed by the call.
90      */
91     private final LocationIdentity[] killedLocations;
92 
93     private final Reexecutability reexecutability;
94 
95     /**
96      * Creates a {@link HotSpotForeignCallLinkage}.
97      *
98      * @param descriptor the descriptor of the call
99      * @param address the address of the code to call
100      * @param effect specifies if the call destroys or preserves all registers (apart from
101      *            temporaries which are always destroyed)
102      * @param outgoingCcType outgoing (caller) calling convention type
103      * @param incomingCcType incoming (callee) calling convention type (can be null)
104      * @param transition specifies if this is a {@linkplain #needsDebugInfo() leaf} call
105      * @param reexecutability specifies if the call can be re-executed without (meaningful) side
106      *            effects. Deoptimization will not return to a point before a call that cannot be
107      *            re-executed.
108      * @param killedLocations the memory locations killed by the call
109      */
create(MetaAccessProvider metaAccess, CodeCacheProvider codeCache, WordTypes wordTypes, HotSpotForeignCallsProvider foreignCalls, ForeignCallDescriptor descriptor, long address, RegisterEffect effect, Type outgoingCcType, Type incomingCcType, Transition transition, Reexecutability reexecutability, LocationIdentity... killedLocations)110     public static HotSpotForeignCallLinkage create(MetaAccessProvider metaAccess, CodeCacheProvider codeCache, WordTypes wordTypes, HotSpotForeignCallsProvider foreignCalls,
111                     ForeignCallDescriptor descriptor, long address, RegisterEffect effect, Type outgoingCcType, Type incomingCcType, Transition transition, Reexecutability reexecutability,
112                     LocationIdentity... killedLocations) {
113         CallingConvention outgoingCc = createCallingConvention(metaAccess, codeCache, wordTypes, foreignCalls, descriptor, outgoingCcType);
114         CallingConvention incomingCc = incomingCcType == null ? null : createCallingConvention(metaAccess, codeCache, wordTypes, foreignCalls, descriptor, incomingCcType);
115         HotSpotForeignCallLinkageImpl linkage = new HotSpotForeignCallLinkageImpl(descriptor, address, effect, transition, reexecutability, outgoingCc, incomingCc,
116                         killedLocations);
117         if (outgoingCcType == HotSpotCallingConventionType.NativeCall) {
118             linkage.temporaries = foreignCalls.getNativeABICallerSaveRegisters();
119         }
120         return linkage;
121     }
122 
123     /**
124      * Gets a calling convention for a given descriptor and call type.
125      */
createCallingConvention(MetaAccessProvider metaAccess, CodeCacheProvider codeCache, WordTypes wordTypes, ValueKindFactory<?> valueKindFactory, ForeignCallDescriptor descriptor, Type ccType)126     public static CallingConvention createCallingConvention(MetaAccessProvider metaAccess, CodeCacheProvider codeCache, WordTypes wordTypes, ValueKindFactory<?> valueKindFactory,
127                     ForeignCallDescriptor descriptor, Type ccType) {
128         assert ccType != null;
129         Class<?>[] argumentTypes = descriptor.getArgumentTypes();
130         JavaType[] parameterTypes = new JavaType[argumentTypes.length];
131         for (int i = 0; i < parameterTypes.length; ++i) {
132             parameterTypes[i] = asJavaType(argumentTypes[i], metaAccess, wordTypes);
133         }
134         JavaType returnType = asJavaType(descriptor.getResultType(), metaAccess, wordTypes);
135         RegisterConfig regConfig = codeCache.getRegisterConfig();
136         return regConfig.getCallingConvention(ccType, returnType, parameterTypes, valueKindFactory);
137     }
138 
asJavaType(Class<?> type, MetaAccessProvider metaAccess, WordTypes wordTypes)139     private static JavaType asJavaType(Class<?> type, MetaAccessProvider metaAccess, WordTypes wordTypes) {
140         ResolvedJavaType javaType = metaAccess.lookupJavaType(type);
141         if (wordTypes.isWord(javaType)) {
142             javaType = metaAccess.lookupJavaType(wordTypes.getWordKind().toJavaClass());
143         }
144         return javaType;
145     }
146 
HotSpotForeignCallLinkageImpl(ForeignCallDescriptor descriptor, long address, RegisterEffect effect, Transition transition, Reexecutability reexecutability, CallingConvention outgoingCallingConvention, CallingConvention incomingCallingConvention, LocationIdentity... killedLocations)147     public HotSpotForeignCallLinkageImpl(ForeignCallDescriptor descriptor, long address, RegisterEffect effect, Transition transition, Reexecutability reexecutability,
148                     CallingConvention outgoingCallingConvention, CallingConvention incomingCallingConvention, LocationIdentity... killedLocations) {
149         super(address);
150         this.descriptor = descriptor;
151         this.address = address;
152         this.effect = effect;
153         this.transition = transition;
154         this.reexecutability = reexecutability;
155         assert outgoingCallingConvention != null : "only incomingCallingConvention can be null";
156         this.outgoingCallingConvention = outgoingCallingConvention;
157         this.incomingCallingConvention = incomingCallingConvention != null ? incomingCallingConvention : outgoingCallingConvention;
158         this.killedLocations = killedLocations;
159     }
160 
161     @Override
toString()162     public String toString() {
163         StringBuilder sb = new StringBuilder(stub == null ? descriptor.toString() : stub.toString());
164         sb.append("@0x").append(Long.toHexString(address)).append(':').append(outgoingCallingConvention).append(":").append(incomingCallingConvention);
165         if (temporaries != null && temporaries.length != 0) {
166             sb.append("; temps=");
167             String sep = "";
168             for (Value op : temporaries) {
169                 sb.append(sep).append(op);
170                 sep = ",";
171             }
172         }
173         return sb.toString();
174     }
175 
176     @Override
isReexecutable()177     public boolean isReexecutable() {
178         return reexecutability == Reexecutability.REEXECUTABLE;
179     }
180 
181     @Override
isReexecutableOnlyAfterException()182     public boolean isReexecutableOnlyAfterException() {
183         return reexecutability == Reexecutability.REEXECUTABLE_ONLY_AFTER_EXCEPTION;
184     }
185 
186     @Override
isGuaranteedSafepoint()187     public boolean isGuaranteedSafepoint() {
188         return transition == Transition.SAFEPOINT;
189     }
190 
191     @Override
getKilledLocations()192     public LocationIdentity[] getKilledLocations() {
193         return killedLocations;
194     }
195 
196     @Override
getOutgoingCallingConvention()197     public CallingConvention getOutgoingCallingConvention() {
198         return outgoingCallingConvention;
199     }
200 
201     @Override
getIncomingCallingConvention()202     public CallingConvention getIncomingCallingConvention() {
203         return incomingCallingConvention;
204     }
205 
206     @Override
getTemporaries()207     public Value[] getTemporaries() {
208         if (temporaries.length == 0) {
209             return temporaries;
210         }
211         return temporaries.clone();
212     }
213 
214     @Override
getMaxCallTargetOffset()215     public long getMaxCallTargetOffset() {
216         return runtime().getHostJVMCIBackend().getCodeCache().getMaxCallTargetOffset(address);
217     }
218 
219     @Override
getDescriptor()220     public ForeignCallDescriptor getDescriptor() {
221         return descriptor;
222     }
223 
224     @Override
setCompiledStub(Stub stub)225     public void setCompiledStub(Stub stub) {
226         assert address == 0L : "cannot set stub for linkage that already has an address: " + this;
227         this.stub = stub;
228     }
229 
230     /**
231      * Determines if this is a call to a compiled {@linkplain Stub stub}.
232      */
233     @Override
isCompiledStub()234     public boolean isCompiledStub() {
235         return address == 0L || stub != null;
236     }
237 
238     @Override
getStub()239     public Stub getStub() {
240         assert checkStubCondition();
241         return stub;
242     }
243 
checkStubCondition()244     private boolean checkStubCondition() {
245         assert stub != null : "linkage without an address must be a stub - forgot to register a Stub associated with " + descriptor + "?";
246         return true;
247     }
248 
249     @Override
finalizeAddress(Backend backend)250     public void finalizeAddress(Backend backend) {
251         if (address == 0) {
252             assert checkStubCondition();
253             InstalledCode code = stub.getCode(backend);
254 
255             EconomicSet<Register> destroyedRegisters = stub.getDestroyedCallerRegisters();
256             if (!destroyedRegisters.isEmpty()) {
257                 AllocatableValue[] temporaryLocations = new AllocatableValue[destroyedRegisters.size()];
258                 int i = 0;
259                 for (Register reg : destroyedRegisters) {
260                     temporaryLocations[i++] = reg.asValue();
261                 }
262                 temporaries = temporaryLocations;
263             }
264             address = code.getStart();
265         }
266     }
267 
268     @Override
getAddress()269     public long getAddress() {
270         assert address != 0L : "address not yet finalized: " + this;
271         return address;
272     }
273 
274     @Override
destroysRegisters()275     public boolean destroysRegisters() {
276         return effect == DESTROYS_REGISTERS;
277     }
278 
279     @Override
needsDebugInfo()280     public boolean needsDebugInfo() {
281         return transition == Transition.SAFEPOINT;
282     }
283 
284     @Override
mayContainFP()285     public boolean mayContainFP() {
286         return transition != Transition.LEAF_NOFP;
287     }
288 
289     @Override
needsJavaFrameAnchor()290     public boolean needsJavaFrameAnchor() {
291         if (transition == Transition.SAFEPOINT || transition == Transition.STACK_INSPECTABLE_LEAF) {
292             if (stub != null) {
293                 // The stub will do the JavaFrameAnchor management
294                 // around the runtime call(s) it makes
295                 return false;
296             } else {
297                 return true;
298             }
299         }
300         return false;
301     }
302 
303     @Override
getSymbol()304     public String getSymbol() {
305         return stub == null ? null : stub.toString();
306     }
307 }
308