1 /* 2 * Copyright (c) 2012, 2019, 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_ALL_CALLER_SAVE_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 */ 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 */ 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 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 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 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 177 public boolean isReexecutable() { 178 return reexecutability == Reexecutability.REEXECUTABLE; 179 } 180 181 @Override 182 public boolean isGuaranteedSafepoint() { 183 return transition == Transition.SAFEPOINT; 184 } 185 186 @Override 187 public RegisterEffect getEffect() { 188 return effect; 189 } 190 191 @Override 192 public LocationIdentity[] getKilledLocations() { 193 return killedLocations; 194 } 195 196 @Override 197 public CallingConvention getOutgoingCallingConvention() { 198 return outgoingCallingConvention; 199 } 200 201 @Override 202 public CallingConvention getIncomingCallingConvention() { 203 return incomingCallingConvention; 204 } 205 206 @Override 207 public Value[] getTemporaries() { 208 if (temporaries.length == 0) { 209 return temporaries; 210 } 211 return temporaries.clone(); 212 } 213 214 @Override 215 public long getMaxCallTargetOffset() { 216 return runtime().getHostJVMCIBackend().getCodeCache().getMaxCallTargetOffset(address); 217 } 218 219 @Override 220 public ForeignCallDescriptor getDescriptor() { 221 return descriptor; 222 } 223 224 @Override 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 234 public boolean isCompiledStub() { 235 return address == 0L || stub != null; 236 } 237 238 @Override 239 public Stub getStub() { 240 assert checkStubCondition(); 241 return stub; 242 } 243 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 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 269 public long getAddress() { 270 assert address != 0L : "address not yet finalized: " + this; 271 return address; 272 } 273 274 @Override 275 public boolean destroysRegisters() { 276 return effect == DESTROYS_ALL_CALLER_SAVE_REGISTERS; 277 } 278 279 @Override 280 public boolean needsDebugInfo() { 281 return transition == Transition.SAFEPOINT; 282 } 283 284 @Override 285 public boolean mayContainFP() { 286 return transition != Transition.LEAF_NO_VZERO; 287 } 288 289 @Override 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 304 public String getSymbol() { 305 return stub == null ? null : stub.toString(); 306 } 307 }