001/* 002 * Copyright (c) 2013, 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.truffle.hotspot; 024 025import static com.oracle.graal.compiler.GraalCompiler.*; 026import static com.oracle.graal.graph.util.CollectionsAccess.*; 027import static com.oracle.graal.hotspot.meta.HotSpotSuitesProvider.*; 028import static com.oracle.graal.truffle.TruffleCompilerOptions.*; 029import static jdk.internal.jvmci.code.CodeUtil.*; 030 031import java.util.*; 032import java.util.concurrent.*; 033import java.util.stream.*; 034 035import jdk.internal.jvmci.code.*; 036import jdk.internal.jvmci.code.CallingConvention.Type; 037import jdk.internal.jvmci.common.*; 038import jdk.internal.jvmci.hotspot.*; 039import jdk.internal.jvmci.meta.*; 040import jdk.internal.jvmci.service.*; 041 042import com.oracle.graal.api.runtime.*; 043import com.oracle.graal.compiler.*; 044import com.oracle.graal.compiler.target.*; 045import com.oracle.graal.debug.*; 046import com.oracle.graal.debug.Debug.Scope; 047import com.oracle.graal.graphbuilderconf.*; 048import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins; 049import com.oracle.graal.hotspot.*; 050import com.oracle.graal.hotspot.meta.*; 051import com.oracle.graal.java.*; 052import com.oracle.graal.lir.asm.*; 053import com.oracle.graal.lir.phases.*; 054import com.oracle.graal.nodes.*; 055import com.oracle.graal.nodes.StructuredGraph.AllowAssumptions; 056import com.oracle.graal.phases.*; 057import com.oracle.graal.phases.common.inlining.*; 058import com.oracle.graal.phases.tiers.*; 059import com.oracle.graal.phases.util.*; 060import com.oracle.graal.runtime.*; 061import com.oracle.graal.truffle.*; 062import com.oracle.graal.truffle.hotspot.nfi.*; 063import com.oracle.nfi.api.*; 064import com.oracle.truffle.api.*; 065import com.oracle.truffle.api.nodes.*; 066 067/** 068 * Implementation of the Truffle runtime when running on top of Graal. 069 */ 070public final class HotSpotTruffleRuntime extends GraalTruffleRuntime { 071 072 public static TruffleRuntime makeInstance() { 073 if (GraalTruffleRuntime.alternateRuntime != null) { 074 return GraalTruffleRuntime.alternateRuntime; 075 } 076 return new HotSpotTruffleRuntime(); 077 } 078 079 private Map<OptimizedCallTarget, Future<?>> compilations = newIdentityMap(); 080 private final ThreadPoolExecutor compileQueue; 081 082 private final Map<RootCallTarget, Void> callTargets = Collections.synchronizedMap(new WeakHashMap<RootCallTarget, Void>()); 083 private static final long THREAD_EETOP_OFFSET = eetopOffset(); 084 085 private HotSpotTruffleRuntime() { 086 installOptimizedCallTargetCallMethod(); 087 lookupCallMethods(getGraalProviders().getMetaAccess()); 088 089 installDefaultListeners(); 090 091 // Create compilation queue. 092 CompilerThreadFactory factory = new CompilerThreadFactory("TruffleCompilerThread", new CompilerThreadFactory.DebugConfigAccess() { 093 public GraalDebugConfig getDebugConfig() { 094 if (Debug.isEnabled()) { 095 GraalDebugConfig debugConfig = DebugEnvironment.initialize(TTY.out().out()); 096 debugConfig.dumpHandlers().add(new TruffleTreeDumpHandler()); 097 return debugConfig; 098 } else { 099 return null; 100 } 101 } 102 }); 103 int selectedProcessors = TruffleCompilerOptions.TruffleCompilerThreads.getValue(); 104 if (selectedProcessors == 0) { 105 // No manual selection made, check how many processors are available. 106 int availableProcessors = Runtime.getRuntime().availableProcessors(); 107 if (availableProcessors >= 4) { 108 selectedProcessors = 2; 109 } else if (availableProcessors >= 12) { 110 selectedProcessors = 4; 111 } 112 } 113 selectedProcessors = Math.max(1, selectedProcessors); 114 compileQueue = new ThreadPoolExecutor(selectedProcessors, selectedProcessors, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), factory); 115 116 } 117 118 @Override 119 public String getName() { 120 return "Graal Truffle Runtime"; 121 } 122 123 @Override 124 public TruffleCompiler getTruffleCompiler() { 125 if (truffleCompiler == null) { 126 truffleCompiler = DefaultTruffleCompiler.create(); 127 } 128 return truffleCompiler; 129 } 130 131 @Override 132 public RootCallTarget createCallTarget(RootNode rootNode) { 133 return createCallTargetImpl(null, rootNode); 134 } 135 136 private RootCallTarget createCallTargetImpl(OptimizedCallTarget source, RootNode rootNode) { 137 CompilationPolicy compilationPolicy; 138 if (acceptForCompilation(rootNode)) { 139 compilationPolicy = new CounterAndTimeBasedCompilationPolicy(); 140 } else { 141 compilationPolicy = new InterpreterOnlyCompilationPolicy(); 142 } 143 OptimizedCallTarget target = new OptimizedCallTarget(source, rootNode, this, compilationPolicy, new HotSpotSpeculationLog()); 144 rootNode.setCallTarget(target); 145 callTargets.put(target, null); 146 147 return target; 148 } 149 150 @Override 151 public RootCallTarget createClonedCallTarget(OptimizedCallTarget source, RootNode root) { 152 return createCallTargetImpl(source, root); 153 } 154 155 public static void installOptimizedCallTargetCallMethod() { 156 Providers providers = getGraalProviders(); 157 MetaAccessProvider metaAccess = providers.getMetaAccess(); 158 ResolvedJavaType type = metaAccess.lookupJavaType(OptimizedCallTarget.class); 159 for (ResolvedJavaMethod method : type.getDeclaredMethods()) { 160 if (method.getAnnotation(TruffleCallBoundary.class) != null) { 161 CompilationResult compResult = compileMethod(method); 162 CodeCacheProvider codeCache = providers.getCodeCache(); 163 try (Scope s = Debug.scope("CodeInstall", codeCache, method)) { 164 codeCache.setDefaultMethod(method, compResult); 165 } catch (Throwable e) { 166 throw Debug.handle(e); 167 } 168 } 169 } 170 } 171 172 private static CompilationResultBuilderFactory getOptimizedCallTargetInstrumentationFactory(String arch, ResolvedJavaMethod method) { 173 for (OptimizedCallTargetInstrumentationFactory factory : Services.load(OptimizedCallTargetInstrumentationFactory.class)) { 174 if (factory.getArchitecture().equals(arch)) { 175 factory.setInstrumentedMethod(method); 176 return factory; 177 } 178 } 179 // No specialization of OptimizedCallTarget on this platform. 180 return CompilationResultBuilderFactory.Default; 181 } 182 183 private static CompilationResult compileMethod(ResolvedJavaMethod javaMethod) { 184 HotSpotProviders providers = getGraalProviders(); 185 SuitesProvider suitesProvider = providers.getSuites(); 186 Suites suites = suitesProvider.createSuites(); 187 LIRSuites lirSuites = suitesProvider.createLIRSuites(); 188 removeInliningPhase(suites); 189 StructuredGraph graph = new StructuredGraph(javaMethod, AllowAssumptions.NO); 190 191 MetaAccessProvider metaAccess = providers.getMetaAccess(); 192 Plugins plugins = new Plugins(new InvocationPlugins(metaAccess)); 193 boolean infoPoints = HotSpotGraalRuntime.runtime().getCompilerToVM().shouldDebugNonSafepoints(); 194 GraphBuilderConfiguration config = infoPoints ? GraphBuilderConfiguration.getInfopointEagerDefault(plugins) : GraphBuilderConfiguration.getEagerDefault(plugins); 195 new GraphBuilderPhase.Instance(metaAccess, providers.getStampProvider(), providers.getConstantReflection(), config, OptimisticOptimizations.ALL, null).apply(graph); 196 197 PhaseSuite<HighTierContext> graphBuilderSuite = getGraphBuilderSuite(suitesProvider); 198 CallingConvention cc = getCallingConvention(providers.getCodeCache(), Type.JavaCallee, graph.method(), false); 199 Backend backend = Graal.getRequiredCapability(RuntimeProvider.class).getHostBackend(); 200 CompilationResultBuilderFactory factory = getOptimizedCallTargetInstrumentationFactory(backend.getTarget().arch.getName(), javaMethod); 201 return compileGraph(graph, cc, javaMethod, providers, backend, providers.getCodeCache().getTarget(), graphBuilderSuite, OptimisticOptimizations.ALL, getProfilingInfo(graph), suites, 202 lirSuites, new CompilationResult(), factory); 203 } 204 205 private static HotSpotProviders getGraalProviders() { 206 RuntimeProvider runtimeProvider = Graal.getRequiredCapability(RuntimeProvider.class); 207 return (HotSpotProviders) runtimeProvider.getHostBackend().getProviders(); 208 } 209 210 private static PhaseSuite<HighTierContext> getGraphBuilderSuite(SuitesProvider suitesProvider) { 211 PhaseSuite<HighTierContext> graphBuilderSuite = suitesProvider.getDefaultGraphBuilderSuite(); 212 return withSimpleDebugInfoIfRequested(graphBuilderSuite); 213 } 214 215 private static void removeInliningPhase(Suites suites) { 216 ListIterator<BasePhase<? super HighTierContext>> inliningPhase = suites.getHighTier().findPhase(InliningPhase.class); 217 if (inliningPhase != null) { 218 inliningPhase.remove(); 219 } 220 } 221 222 @Override 223 public void compile(OptimizedCallTarget optimizedCallTarget, boolean mayBeAsynchronous) { 224 /* Ensure compiler is created. */ 225 getTruffleCompiler(); 226 227 Runnable r = new Runnable() { 228 @Override 229 public void run() { 230 doCompile(optimizedCallTarget); 231 } 232 }; 233 Future<?> future = compileQueue.submit(r); 234 this.compilations.put(optimizedCallTarget, future); 235 getCompilationNotify().notifyCompilationQueued(optimizedCallTarget); 236 237 if (!mayBeAsynchronous) { 238 try { 239 future.get(); 240 } catch (ExecutionException e) { 241 if (TruffleCompilationExceptionsAreThrown.getValue() && !(e.getCause() instanceof BailoutException && !((BailoutException) e.getCause()).isPermanent())) { 242 throw new RuntimeException(e.getCause()); 243 } else { 244 // silently ignored 245 } 246 } catch (InterruptedException | CancellationException e) { 247 // silently ignored 248 } 249 } 250 } 251 252 @Override 253 public boolean cancelInstalledTask(OptimizedCallTarget optimizedCallTarget, Object source, CharSequence reason) { 254 Future<?> codeTask = this.compilations.get(optimizedCallTarget); 255 if (codeTask != null && isCompiling(optimizedCallTarget)) { 256 this.compilations.remove(optimizedCallTarget); 257 boolean result = codeTask.cancel(true); 258 if (result) { 259 optimizedCallTarget.notifyCompilationFinished(false); 260 getCompilationNotify().notifyCompilationDequeued(optimizedCallTarget, source, reason); 261 } 262 return result; 263 } 264 return false; 265 } 266 267 @Override 268 public void waitForCompilation(OptimizedCallTarget optimizedCallTarget, long timeout) throws ExecutionException, TimeoutException { 269 Future<?> codeTask = this.compilations.get(optimizedCallTarget); 270 if (codeTask != null && isCompiling(optimizedCallTarget)) { 271 try { 272 codeTask.get(timeout, TimeUnit.MILLISECONDS); 273 } catch (InterruptedException e) { 274 // ignore interrupted 275 } 276 } 277 } 278 279 @Override 280 public Collection<OptimizedCallTarget> getQueuedCallTargets() { 281 return compilations.keySet().stream().filter(e -> !compilations.get(e).isDone()).collect(Collectors.toList()); 282 } 283 284 @Override 285 public boolean isCompiling(OptimizedCallTarget optimizedCallTarget) { 286 Future<?> codeTask = this.compilations.get(optimizedCallTarget); 287 if (codeTask != null) { 288 if (codeTask.isCancelled() || codeTask.isDone()) { 289 this.compilations.remove(optimizedCallTarget); 290 return false; 291 } 292 return true; 293 } 294 return false; 295 } 296 297 @Override 298 public void invalidateInstalledCode(OptimizedCallTarget optimizedCallTarget, Object source, CharSequence reason) { 299 HotSpotGraalRuntime.runtime().getCompilerToVM().invalidateInstalledCode(optimizedCallTarget); 300 getCompilationNotify().notifyCompilationInvalidated(optimizedCallTarget, source, reason); 301 } 302 303 @Override 304 public void reinstallStubs() { 305 installOptimizedCallTargetCallMethod(); 306 } 307 308 @Override 309 public boolean platformEnableInfopoints() { 310 return HotSpotGraalRuntime.runtime().getCompilerToVM().shouldDebugNonSafepoints(); 311 } 312 313 @Override 314 public Collection<RootCallTarget> getCallTargets() { 315 return Collections.unmodifiableSet(callTargets.keySet()); 316 } 317 318 @Override 319 public void notifyTransferToInterpreter() { 320 CompilerAsserts.neverPartOfCompilation(); 321 if (TraceTruffleTransferToInterpreter.getValue()) { 322 long thread = UnsafeAccess.unsafe.getLong(Thread.currentThread(), THREAD_EETOP_OFFSET); 323 long pendingTransferToInterpreterAddress = thread + HotSpotGraalRuntime.runtime().getConfig().pendingTransferToInterpreterOffset; 324 boolean deoptimized = UnsafeAccess.unsafe.getByte(pendingTransferToInterpreterAddress) != 0; 325 if (deoptimized) { 326 UnsafeAccess.unsafe.putByte(pendingTransferToInterpreterAddress, (byte) 0); 327 328 logTransferToInterpreter(); 329 } 330 } 331 } 332 333 private static void logTransferToInterpreter() { 334 final int skip = 2; 335 final int limit = TraceTruffleStackTraceLimit.getValue(); 336 StackTraceElement[] stackTrace = new Throwable().getStackTrace(); 337 String suffix = stackTrace.length > skip + limit ? "\n ..." : ""; 338 TTY.out().out().println(Arrays.stream(stackTrace).skip(skip).limit(limit).map(StackTraceElement::toString).collect(Collectors.joining("\n ", "", suffix))); 339 } 340 341 private static long eetopOffset() { 342 try { 343 return UnsafeAccess.unsafe.objectFieldOffset(Thread.class.getDeclaredField("eetop")); 344 } catch (Exception e) { 345 throw new JVMCIError(e); 346 } 347 } 348 349 private static RawNativeCallNodeFactory getRawNativeCallNodeFactory(String arch) { 350 for (RawNativeCallNodeFactory factory : Services.load(RawNativeCallNodeFactory.class)) { 351 if (factory.getArchitecture().equals(arch)) { 352 return factory; 353 } 354 } 355 // No RawNativeCallNodeFactory on this platform. 356 return null; 357 } 358 359 public static NativeFunctionInterface createNativeFunctionInterface() { 360 HotSpotVMConfig config = HotSpotGraalRuntime.runtime().getConfig(); 361 Backend backend = HotSpotGraalRuntime.runtime().getHostBackend(); 362 RawNativeCallNodeFactory factory = getRawNativeCallNodeFactory(backend.getTarget().arch.getName()); 363 if (factory == null) { 364 return null; 365 } 366 return new HotSpotNativeFunctionInterface(HotSpotGraalRuntime.runtime().getHostProviders(), factory, backend, config.dllLoad, config.dllLookup, config.rtldDefault); 367 } 368}