1 /* 2 * Copyright (c) 2012, 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 */ 23 package org.graalvm.compiler.hotspot.stubs; 24 25 import static jdk.vm.ci.hotspot.HotSpotCallingConventionType.JavaCall; 26 import static jdk.vm.ci.hotspot.HotSpotCallingConventionType.JavaCallee; 27 import static jdk.vm.ci.hotspot.HotSpotCallingConventionType.NativeCall; 28 import static org.graalvm.compiler.hotspot.HotSpotForeignCallLinkage.RegisterEffect.DESTROYS_REGISTERS; 29 import static org.graalvm.compiler.hotspot.HotSpotForeignCallLinkage.RegisterEffect.PRESERVES_REGISTERS; 30 31 import org.graalvm.compiler.core.common.CompilationIdentifier; 32 import org.graalvm.compiler.core.common.LIRKind; 33 import org.graalvm.compiler.core.common.spi.ForeignCallDescriptor; 34 import org.graalvm.compiler.core.common.type.Stamp; 35 import org.graalvm.compiler.core.common.type.StampFactory; 36 import org.graalvm.compiler.core.common.type.StampPair; 37 import org.graalvm.compiler.debug.DebugContext; 38 import org.graalvm.compiler.debug.JavaMethodContext; 39 import org.graalvm.compiler.hotspot.HotSpotForeignCallLinkage; 40 import org.graalvm.compiler.hotspot.HotSpotForeignCallLinkage.Transition; 41 import org.graalvm.compiler.hotspot.HotSpotForeignCallLinkageImpl; 42 import org.graalvm.compiler.hotspot.meta.HotSpotProviders; 43 import org.graalvm.compiler.hotspot.nodes.StubForeignCallNode; 44 import org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil; 45 import org.graalvm.compiler.nodes.ConstantNode; 46 import org.graalvm.compiler.nodes.InvokeNode; 47 import org.graalvm.compiler.nodes.ParameterNode; 48 import org.graalvm.compiler.nodes.ReturnNode; 49 import org.graalvm.compiler.nodes.StructuredGraph; 50 import org.graalvm.compiler.nodes.ValueNode; 51 import org.graalvm.compiler.options.OptionValues; 52 import org.graalvm.compiler.phases.common.RemoveValueProxyPhase; 53 import org.graalvm.compiler.replacements.GraphKit; 54 import org.graalvm.compiler.replacements.nodes.ReadRegisterNode; 55 import org.graalvm.compiler.word.Word; 56 import org.graalvm.compiler.word.WordTypes; 57 import org.graalvm.word.LocationIdentity; 58 59 import jdk.vm.ci.hotspot.HotSpotJVMCIRuntimeProvider; 60 import jdk.vm.ci.hotspot.HotSpotSignature; 61 import jdk.vm.ci.meta.JavaKind; 62 import jdk.vm.ci.meta.JavaMethod; 63 import jdk.vm.ci.meta.JavaType; 64 import jdk.vm.ci.meta.MetaAccessProvider; 65 import jdk.vm.ci.meta.ResolvedJavaMethod; 66 import jdk.vm.ci.meta.ResolvedJavaType; 67 import jdk.vm.ci.meta.Signature; 68 69 /** 70 * A {@linkplain #getGraph generated} stub for a {@link Transition non-leaf} foreign call from 71 * compiled code. A stub is required for such calls as the caller may be scheduled for 72 * deoptimization while the call is in progress. And since these are foreign/runtime calls on slow 73 * paths, we don't want to force the register allocator to spill around the call. As such, this stub 74 * saves and restores all allocatable registers. It also 75 * {@linkplain StubUtil#handlePendingException(Word, boolean) handles} any exceptions raised during 76 * the foreign call. 77 */ 78 public class ForeignCallStub extends Stub { 79 80 private final HotSpotJVMCIRuntimeProvider jvmciRuntime; 81 82 /** 83 * The target of the call. 84 */ 85 private final HotSpotForeignCallLinkage target; 86 87 /** 88 * Specifies if the JavaThread value for the current thread is to be prepended to the arguments 89 * for the call to {@link #target}. 90 */ 91 protected final boolean prependThread; 92 93 /** 94 * Creates a stub for a call to code at a given address. 95 * 96 * @param address the address of the code to call 97 * @param descriptor the signature of the call to this stub 98 * @param prependThread true if the JavaThread value for the current thread is to be prepended 99 * to the arguments for the call to {@code address} 100 * @param reexecutable specifies if the stub call can be re-executed without (meaningful) side 101 * effects. Deoptimization will not return to a point before a stub call that cannot 102 * be re-executed. 103 * @param killedLocations the memory locations killed by the stub call 104 */ 105 public ForeignCallStub(OptionValues options, HotSpotJVMCIRuntimeProvider runtime, HotSpotProviders providers, long address, ForeignCallDescriptor descriptor, boolean prependThread, 106 Transition transition, 107 boolean reexecutable, 108 LocationIdentity... killedLocations) { 109 super(options, providers, HotSpotForeignCallLinkageImpl.create(providers.getMetaAccess(), providers.getCodeCache(), providers.getWordTypes(), providers.getForeignCalls(), descriptor, 0L, 110 PRESERVES_REGISTERS, JavaCall, JavaCallee, transition, reexecutable, killedLocations)); 111 this.jvmciRuntime = runtime; 112 this.prependThread = prependThread; 113 Class<?>[] targetParameterTypes = createTargetParameters(descriptor); 114 ForeignCallDescriptor targetSig = new ForeignCallDescriptor(descriptor.getName() + ":C", descriptor.getResultType(), targetParameterTypes); 115 target = HotSpotForeignCallLinkageImpl.create(providers.getMetaAccess(), providers.getCodeCache(), providers.getWordTypes(), providers.getForeignCalls(), targetSig, address, 116 DESTROYS_REGISTERS, NativeCall, NativeCall, transition, reexecutable, killedLocations); 117 } 118 119 /** 120 * Gets the linkage information for the call from this stub. 121 */ 122 public HotSpotForeignCallLinkage getTargetLinkage() { 123 return target; 124 } 125 126 private Class<?>[] createTargetParameters(ForeignCallDescriptor descriptor) { 127 Class<?>[] parameters = descriptor.getArgumentTypes(); 128 if (prependThread) { 129 Class<?>[] newParameters = new Class<?>[parameters.length + 1]; 130 System.arraycopy(parameters, 0, newParameters, 1, parameters.length); 131 newParameters[0] = Word.class; 132 return newParameters; 133 } 134 return parameters; 135 } 136 137 @Override 138 protected ResolvedJavaMethod getInstalledCodeOwner() { 139 return null; 140 } 141 142 private class DebugScopeContext implements JavaMethod, JavaMethodContext { 143 @Override 144 public JavaMethod asJavaMethod() { 145 return this; 146 } 147 148 @Override 149 public Signature getSignature() { 150 ForeignCallDescriptor d = linkage.getDescriptor(); 151 MetaAccessProvider metaAccess = providers.getMetaAccess(); 152 Class<?>[] arguments = d.getArgumentTypes(); 153 ResolvedJavaType[] parameters = new ResolvedJavaType[arguments.length]; 154 for (int i = 0; i < arguments.length; i++) { 155 parameters[i] = metaAccess.lookupJavaType(arguments[i]); 156 } 157 return new HotSpotSignature(jvmciRuntime, metaAccess.lookupJavaType(d.getResultType()), parameters); 158 } 159 160 @Override 161 public String getName() { 162 return linkage.getDescriptor().getName(); 163 } 164 165 @Override 166 public JavaType getDeclaringClass() { 167 return providers.getMetaAccess().lookupJavaType(ForeignCallStub.class); 168 } 169 170 @Override 171 public String toString() { 172 return format("ForeignCallStub<%n(%p)>"); 173 } 174 } 175 176 @Override 177 protected Object debugScopeContext() { 178 return new DebugScopeContext() { 179 180 }; 181 } 182 183 /** 184 * Creates a graph for this stub. 185 * <p> 186 * If the stub returns an object, the graph created corresponds to this pseudo code: 187 * 188 * <pre> 189 * Object foreignFunctionStub(args...) { 190 * foreignFunction(currentThread, args); 191 * if (clearPendingException(thread())) { 192 * getAndClearObjectResult(thread()); 193 * DeoptimizeCallerNode.deopt(InvalidateReprofile, RuntimeConstraint); 194 * } 195 * return verifyObject(getAndClearObjectResult(thread())); 196 * } 197 * </pre> 198 * 199 * If the stub returns a primitive or word, the graph created corresponds to this pseudo code 200 * (using {@code int} as the primitive return type): 201 * 202 * <pre> 203 * int foreignFunctionStub(args...) { 204 * int result = foreignFunction(currentThread, args); 205 * if (clearPendingException(thread())) { 206 * DeoptimizeCallerNode.deopt(InvalidateReprofile, RuntimeConstraint); 207 * } 208 * return result; 209 * } 210 * </pre> 211 * 212 * If the stub is void, the graph created corresponds to this pseudo code: 213 * 214 * <pre> 215 * void foreignFunctionStub(args...) { 216 * foreignFunction(currentThread, args); 217 * if (clearPendingException(thread())) { 218 * DeoptimizeCallerNode.deopt(InvalidateReprofile, RuntimeConstraint); 219 * } 220 * } 221 * </pre> 222 * 223 * In each example above, the {@code currentThread} argument is the C++ JavaThread value (i.e., 224 * %r15 on AMD64) and is only prepended if {@link #prependThread} is true. 225 */ 226 @Override 227 protected StructuredGraph getGraph(DebugContext debug, CompilationIdentifier compilationId) { 228 WordTypes wordTypes = providers.getWordTypes(); 229 Class<?>[] args = linkage.getDescriptor().getArgumentTypes(); 230 boolean isObjectResult = !LIRKind.isValue(linkage.getOutgoingCallingConvention().getReturn()); 231 StructuredGraph graph = new StructuredGraph.Builder(options, debug).name(toString()).compilationId(compilationId).build(); 232 graph.disableUnsafeAccessTracking(); 233 234 GraphKit kit = new GraphKit(graph, providers, wordTypes, providers.getGraphBuilderPlugins()); 235 ParameterNode[] params = createParameters(kit, args); 236 237 ReadRegisterNode thread = kit.append(new ReadRegisterNode(providers.getRegisters().getThreadRegister(), wordTypes.getWordKind(), true, false)); 238 ValueNode result = createTargetCall(kit, params, thread); 239 kit.createInvoke(StubUtil.class, "handlePendingException", thread, ConstantNode.forBoolean(isObjectResult, graph)); 240 if (isObjectResult) { 241 InvokeNode object = kit.createInvoke(HotSpotReplacementsUtil.class, "getAndClearObjectResult", thread); 242 result = kit.createInvoke(StubUtil.class, "verifyObject", object); 243 } 244 kit.append(new ReturnNode(linkage.getDescriptor().getResultType() == void.class ? null : result)); 245 246 debug.dump(DebugContext.VERBOSE_LEVEL, graph, "Initial stub graph"); 247 248 kit.inlineInvokes(); 249 250 new RemoveValueProxyPhase().apply(graph); 251 252 debug.dump(DebugContext.VERBOSE_LEVEL, graph, "Stub graph before compilation"); 253 254 return graph; 255 } 256 257 private ParameterNode[] createParameters(GraphKit kit, Class<?>[] args) { 258 ParameterNode[] params = new ParameterNode[args.length]; 259 ResolvedJavaType accessingClass = providers.getMetaAccess().lookupJavaType(getClass()); 260 for (int i = 0; i < args.length; i++) { 261 ResolvedJavaType type = providers.getMetaAccess().lookupJavaType(args[i]).resolve(accessingClass); 262 StampPair stamp = StampFactory.forDeclaredType(kit.getGraph().getAssumptions(), type, false); 263 ParameterNode param = kit.unique(new ParameterNode(i, stamp)); 264 params[i] = param; 265 } 266 return params; 267 } 268 269 private StubForeignCallNode createTargetCall(GraphKit kit, ParameterNode[] params, ReadRegisterNode thread) { 270 Stamp stamp = StampFactory.forKind(JavaKind.fromJavaClass(target.getDescriptor().getResultType())); 271 if (prependThread) { 272 ValueNode[] targetArguments = new ValueNode[1 + params.length]; 273 targetArguments[0] = thread; 274 System.arraycopy(params, 0, targetArguments, 1, params.length); 275 return kit.append(new StubForeignCallNode(providers.getForeignCalls(), stamp, target.getDescriptor(), targetArguments)); 276 } else { 277 return kit.append(new StubForeignCallNode(providers.getForeignCalls(), stamp, target.getDescriptor(), params)); 278 } 279 } 280 }