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