001/* 002 * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. 008 * 009 * This code is distributed in the hope that it will be useful, but WITHOUT 010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 011 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 012 * version 2 for more details (a copy is included in the LICENSE file that 013 * accompanied this code). 014 * 015 * You should have received a copy of the GNU General Public License version 016 * 2 along with this work; if not, write to the Free Software Foundation, 017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 018 * 019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 020 * or visit www.oracle.com if you need additional information or have any 021 * questions. 022 */ 023package com.oracle.graal.hotspot.stubs; 024 025import com.oracle.graal.debug.*; 026 027import jdk.internal.jvmci.hotspot.*; 028import jdk.internal.jvmci.meta.*; 029import static com.oracle.graal.hotspot.HotSpotForeignCallLinkage.RegisterEffect.*; 030import static jdk.internal.jvmci.code.CallingConvention.Type.*; 031 032import com.oracle.graal.compiler.common.spi.*; 033import com.oracle.graal.compiler.common.type.*; 034import com.oracle.graal.hotspot.*; 035import com.oracle.graal.hotspot.HotSpotForeignCallLinkage.Transition; 036import com.oracle.graal.hotspot.meta.*; 037import com.oracle.graal.hotspot.nodes.*; 038import com.oracle.graal.hotspot.replacements.*; 039import com.oracle.graal.nodes.*; 040import com.oracle.graal.nodes.StructuredGraph.AllowAssumptions; 041import com.oracle.graal.replacements.*; 042import com.oracle.graal.replacements.nodes.*; 043import com.oracle.graal.word.*; 044 045/** 046 * A {@linkplain #getGraph() generated} stub for a {@link Transition non-leaf} foreign call from 047 * compiled code. A stub is required for such calls as the caller may be scheduled for 048 * deoptimization while the call is in progress. And since these are foreign/runtime calls on slow 049 * paths, we don't want to force the register allocator to spill around the call. As such, this stub 050 * saves and restores all allocatable registers. It also 051 * {@linkplain StubUtil#handlePendingException(Word, boolean) handles} any exceptions raised during 052 * the foreign call. 053 */ 054public class ForeignCallStub extends Stub { 055 056 private final HotSpotGraalRuntimeProvider runtime; 057 058 /** 059 * The target of the call. 060 */ 061 private final HotSpotForeignCallLinkage target; 062 063 /** 064 * Specifies if the JavaThread value for the current thread is to be prepended to the arguments 065 * for the call to {@link #target}. 066 */ 067 protected final boolean prependThread; 068 069 /** 070 * Creates a stub for a call to code at a given address. 071 * 072 * @param address the address of the code to call 073 * @param descriptor the signature of the call to this stub 074 * @param prependThread true if the JavaThread value for the current thread is to be prepended 075 * to the arguments for the call to {@code address} 076 * @param reexecutable specifies if the stub call can be re-executed without (meaningful) side 077 * effects. Deoptimization will not return to a point before a stub call that cannot 078 * be re-executed. 079 * @param killedLocations the memory locations killed by the stub call 080 */ 081 public ForeignCallStub(HotSpotGraalRuntimeProvider runtime, HotSpotProviders providers, long address, ForeignCallDescriptor descriptor, boolean prependThread, Transition transition, 082 boolean reexecutable, LocationIdentity... killedLocations) { 083 super(providers, HotSpotForeignCallLinkageImpl.create(providers.getMetaAccess(), providers.getCodeCache(), providers.getForeignCalls(), descriptor, 0L, PRESERVES_REGISTERS, JavaCall, 084 JavaCallee, transition, reexecutable, killedLocations)); 085 this.runtime = runtime; 086 this.prependThread = prependThread; 087 Class<?>[] targetParameterTypes = createTargetParameters(descriptor); 088 ForeignCallDescriptor targetSig = new ForeignCallDescriptor(descriptor.getName() + ":C", descriptor.getResultType(), targetParameterTypes); 089 target = HotSpotForeignCallLinkageImpl.create(providers.getMetaAccess(), providers.getCodeCache(), providers.getForeignCalls(), targetSig, address, DESTROYS_REGISTERS, NativeCall, NativeCall, 090 transition, reexecutable, killedLocations); 091 } 092 093 /** 094 * Gets the linkage information for the call from this stub. 095 */ 096 public HotSpotForeignCallLinkage getTargetLinkage() { 097 return target; 098 } 099 100 private Class<?>[] createTargetParameters(ForeignCallDescriptor descriptor) { 101 Class<?>[] parameters = descriptor.getArgumentTypes(); 102 if (prependThread) { 103 Class<?>[] newParameters = new Class[parameters.length + 1]; 104 System.arraycopy(parameters, 0, newParameters, 1, parameters.length); 105 newParameters[0] = Word.class; 106 return newParameters; 107 } 108 return parameters; 109 } 110 111 @Override 112 protected ResolvedJavaMethod getInstalledCodeOwner() { 113 return null; 114 } 115 116 private class DebugScopeContext implements JavaMethod, JavaMethodContext { 117 public JavaMethod asJavaMethod() { 118 return this; 119 } 120 121 public Signature getSignature() { 122 ForeignCallDescriptor d = linkage.getDescriptor(); 123 MetaAccessProvider metaAccess = providers.getMetaAccess(); 124 Class<?>[] arguments = d.getArgumentTypes(); 125 ResolvedJavaType[] parameters = new ResolvedJavaType[arguments.length]; 126 for (int i = 0; i < arguments.length; i++) { 127 parameters[i] = metaAccess.lookupJavaType(arguments[i]); 128 } 129 return new HotSpotSignature(runtime.getJVMCIRuntime(), metaAccess.lookupJavaType(d.getResultType()), parameters); 130 } 131 132 public String getName() { 133 return linkage.getDescriptor().getName(); 134 } 135 136 public JavaType getDeclaringClass() { 137 return providers.getMetaAccess().lookupJavaType(ForeignCallStub.class); 138 } 139 140 @Override 141 public String toString() { 142 return format("ForeignCallStub<%n(%p)>"); 143 } 144 } 145 146 @Override 147 protected Object debugScopeContext() { 148 return new DebugScopeContext() { 149 150 }; 151 } 152 153 /** 154 * Creates a graph for this stub. 155 * <p> 156 * If the stub returns an object, the graph created corresponds to this pseudo code: 157 * 158 * <pre> 159 * Object foreignFunctionStub(args...) { 160 * foreignFunction(currentThread, args); 161 * if (clearPendingException(thread())) { 162 * getAndClearObjectResult(thread()); 163 * DeoptimizeCallerNode.deopt(InvalidateReprofile, RuntimeConstraint); 164 * } 165 * return verifyObject(getAndClearObjectResult(thread())); 166 * } 167 * </pre> 168 * 169 * If the stub returns a primitive or word, the graph created corresponds to this pseudo code 170 * (using {@code int} as the primitive return type): 171 * 172 * <pre> 173 * int foreignFunctionStub(args...) { 174 * int result = foreignFunction(currentThread, args); 175 * if (clearPendingException(thread())) { 176 * DeoptimizeCallerNode.deopt(InvalidateReprofile, RuntimeConstraint); 177 * } 178 * return result; 179 * } 180 * </pre> 181 * 182 * If the stub is void, the graph created corresponds to this pseudo code: 183 * 184 * <pre> 185 * void foreignFunctionStub(args...) { 186 * foreignFunction(currentThread, args); 187 * if (clearPendingException(thread())) { 188 * DeoptimizeCallerNode.deopt(InvalidateReprofile, RuntimeConstraint); 189 * } 190 * } 191 * </pre> 192 * 193 * In each example above, the {@code currentThread} argument is the C++ JavaThread value (i.e., 194 * %r15 on AMD64) and is only prepended if {@link #prependThread} is true. 195 */ 196 @Override 197 protected StructuredGraph getGraph() { 198 WordTypes wordTypes = providers.getWordTypes(); 199 Class<?>[] args = linkage.getDescriptor().getArgumentTypes(); 200 boolean isObjectResult = linkage.getOutgoingCallingConvention().getReturn().getKind() == Kind.Object; 201 202 StructuredGraph graph = new StructuredGraph(toString(), null, AllowAssumptions.NO); 203 graph.disableInlinedMethodRecording(); 204 graph.disableUnsafeAccessTracking(); 205 206 GraphKit kit = new GraphKit(graph, providers, wordTypes, providers.getGraphBuilderPlugins()); 207 ParameterNode[] params = createParameters(kit, args); 208 209 ReadRegisterNode thread = kit.append(new ReadRegisterNode(providers.getRegisters().getThreadRegister(), wordTypes.getWordKind(), true, false)); 210 ValueNode result = createTargetCall(kit, params, thread); 211 kit.createInvoke(StubUtil.class, "handlePendingException", thread, ConstantNode.forBoolean(isObjectResult, graph)); 212 if (isObjectResult) { 213 InvokeNode object = kit.createInvoke(HotSpotReplacementsUtil.class, "getAndClearObjectResult", thread); 214 result = kit.createInvoke(StubUtil.class, "verifyObject", object); 215 } 216 kit.append(new ReturnNode(linkage.getDescriptor().getResultType() == void.class ? null : result)); 217 218 if (Debug.isDumpEnabled()) { 219 Debug.dump(graph, "Initial stub graph"); 220 } 221 222 kit.inlineInvokes(); 223 224 if (Debug.isDumpEnabled()) { 225 Debug.dump(graph, "Stub graph before compilation"); 226 } 227 228 return graph; 229 } 230 231 private ParameterNode[] createParameters(GraphKit kit, Class<?>[] args) { 232 ParameterNode[] params = new ParameterNode[args.length]; 233 ResolvedJavaType accessingClass = providers.getMetaAccess().lookupJavaType(getClass()); 234 for (int i = 0; i < args.length; i++) { 235 ResolvedJavaType type = providers.getMetaAccess().lookupJavaType(args[i]).resolve(accessingClass); 236 Stamp stamp; 237 if (type.getKind().getStackKind() == Kind.Object) { 238 stamp = StampFactory.declared(type); 239 } else { 240 stamp = StampFactory.forKind(type.getKind()); 241 } 242 ParameterNode param = kit.unique(new ParameterNode(i, stamp)); 243 params[i] = param; 244 } 245 return params; 246 } 247 248 private StubForeignCallNode createTargetCall(GraphKit kit, ParameterNode[] params, ReadRegisterNode thread) { 249 if (prependThread) { 250 ValueNode[] targetArguments = new ValueNode[1 + params.length]; 251 targetArguments[0] = thread; 252 System.arraycopy(params, 0, targetArguments, 1, params.length); 253 return kit.append(new StubForeignCallNode(providers.getForeignCalls(), target.getDescriptor(), targetArguments)); 254 } else { 255 return kit.append(new StubForeignCallNode(providers.getForeignCalls(), target.getDescriptor(), params)); 256 } 257 } 258}