/* * Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.max.vm.compiler; import static com.sun.max.platform.Platform.*; import static com.sun.max.vm.AbstractVMScheme.*; import static com.sun.max.vm.MaxineVM.*; import static com.sun.max.vm.VMOptions.*; import static com.sun.max.vm.compiler.CallEntryPoint.*; import static com.sun.max.vm.compiler.RuntimeCompiler.*; import static com.sun.max.vm.compiler.target.Safepoints.*; import static com.sun.max.vm.intrinsics.Infopoints.*; import java.util.*; import com.sun.cri.ci.*; import com.sun.max.annotate.*; import com.sun.max.lang.*; import com.sun.max.unsafe.*; import com.sun.max.vm.*; import com.sun.max.vm.MaxineVM.Phase; import com.sun.max.vm.actor.*; import com.sun.max.vm.actor.holder.*; import com.sun.max.vm.actor.member.*; import com.sun.max.vm.code.*; import com.sun.max.vm.compiler.RuntimeCompiler.Nature; import com.sun.max.vm.compiler.target.*; import com.sun.max.vm.compiler.target.amd64.*; import com.sun.max.vm.heap.*; import com.sun.max.vm.object.*; import com.sun.max.vm.profile.*; import com.sun.max.vm.runtime.*; import com.sun.max.vm.stack.*; import com.sun.max.vm.thread.*; import com.sun.max.vm.ti.*; /** * This class implements an adaptive compilation system with multiple compilers with different compilation time / code * quality tradeoffs. It encapsulates the necessary infrastructure for recording profiling data, selecting what and when * to recompile, etc. */ public class CompilationBroker { /** * The threshold at which a recompilation is triggered from the baseline compiler to the next level * of optimization. This is typically the number of invocations of the method. */ private static int RCT = 5000; /** * A queue of pending compilations. */ protected final LinkedList<Compilation> pending = new LinkedList<Compilation>(); /** * The baseline compiler. */ public final RuntimeCompiler baselineCompiler; /** * The optimizing compiler. */ public final RuntimeCompiler optimizingCompiler; private static boolean opt; private static boolean GCOnRecompilation; private static boolean FailOverCompilation = true; static int PrintCodeCacheMetrics; static { addFieldOption("-X", "opt", "Select optimizing compiler whenever possible."); addFieldOption("-XX:", "RCT", "Set the recompilation threshold for methods. Use 0 to disable recompilation. (default: " + RCT + ")."); addFieldOption("-XX:", "GCOnRecompilation", "Force GC before every re-compilation."); addFieldOption("-XX:", "FailOverCompilation", "Retry failed compilations with another compiler (if available)."); addFieldOption("-XX:", "PrintCodeCacheMetrics", "Print code cache metrics (0 = disabled, 1 = summary, 2 = verbose)."); } @RESET static String CompileCommand; static { VMOptions.addFieldOption("-XX:", "CompileCommand", "Specify which compiler to use for methods matching given patterns. For example, " + "'-XX:CompileCommand=test.output:T1X,com.acme.util.Strings:Graal' specifies that " + "any method whose fully qualified name contains the substring 'test.output' " + "should be compiled with the compiler named 'T1X' and any method whose fully " + "qualified name contains 'com.acme.util.String' should be compiled with the 'Graal' " + "compiler. No checking is done to ensure that a named compiler exists."); } private LinkedHashMap<String, String> compileCommandMap; /** * Gets the name of the compiler to be used to * compile {@code cma} as specified by the {@code -XX:CompileCommand} VM option. * * @return {@code null} if no specific compiler was specified for {@code cma} by a {@code -XX:CompileCommand} VM option */ public String compilerFor(ClassMethodActor cma) { if (CompileCommand == null) { return null; } // A race to parse the option and create the map is fine. The result will be identical // for both threads and so one result just becomes instant garbage. if (compileCommandMap == null) { LinkedHashMap<String, String> map = new LinkedHashMap<String, String>(); String[] parts = CompileCommand.split(","); for (String part : parts) { int colon = part.indexOf(':'); if (colon == -1 || colon == 0 || colon == part.length() - 1) { Log.println("CompileCommand part does not match a <key>:<value> pattern: " + part); } else { String key = part.substring(0, colon); String value = part.substring(colon + 1); map.put(key, value); } } compileCommandMap = map; } String methodString = cma.toString(); for (Map.Entry<String, String> e : compileCommandMap.entrySet()) { if (methodString.contains(e.getKey()) || "*".equals(e.getKey())) { return e.getValue(); } } return null; } /** * The default compiler to use. */ private RuntimeCompiler defaultCompiler; private static final boolean BACKGROUND_COMPILATION = false; public boolean needsAdapters() { return baselineCompiler != null; } public boolean isDeoptSupported() { return baselineCompiler != null; } private static final String OPTIMIZING_COMPILER_PROPERTY = CompilationBroker.class.getSimpleName() + "." + optimizingCompilerOption.getName(); private static final String BASELINE_COMPILER_PROPERTY = CompilationBroker.class.getSimpleName() + "." + baselineCompilerOption.getName(); /** * Gets the class name of the optimizing compiler that will be configured when an instance of this scheme is instantiated. */ @HOSTED_ONLY public static String optName() { return configValue(OPTIMIZING_COMPILER_PROPERTY, optimizingCompilerOption, aliases); } /** * Gets the class name of the baseline compiler that will be configured when an instance of this scheme is instantiated. */ @HOSTED_ONLY public static String baselineName() { return configValue(BASELINE_COMPILER_PROPERTY, baselineCompilerOption, aliases); } /** * The name of the system property specifying a subclass of {@link CompilationBroker} that is * to be instantiated instead of {@link CompilationBroker} itself. */ public static final String COMPILATION_BROKER_CLASS_PROPERTY_NAME = "max.CompilationBroker.class"; /** * Creates the single {@link CompilationBroker} instance to be used by the VM. * This factory-style instantiation allows a subclass of {@link CompilationBroker} to * be created instead. * * @see #COMPILATION_BROKER_CLASS_PROPERTY_NAME */ @HOSTED_ONLY public static CompilationBroker create() { final String className = System.getProperty(COMPILATION_BROKER_CLASS_PROPERTY_NAME); if (className == null) { return new CompilationBroker(); } else { try { return (CompilationBroker) Class.forName(className).newInstance(); } catch (Exception exception) { throw FatalError.unexpected("Error instantiating " + className, exception); } } } /** * This constructor should only be called from {@link #create()} or a subclass of {@link CompilationBroker}. */ @HOSTED_ONLY protected CompilationBroker() { assert optimizingCompilerOption.getValue() != null; String optName = optName(); String baselineName = baselineName(); optimizingCompiler = instantiateCompiler(optName); assert optimizingCompiler.nature() == Nature.OPT : optimizingCompiler + " is not an optimizing compiler"; if (baselineName != null) { baselineCompiler = instantiateCompiler(baselineName); assert baselineCompiler.nature() == Nature.BASELINE : baselineCompiler + " is not a baseline compiler"; assert baselineCompiler != optimizingCompiler; defaultCompiler = baselineCompiler; } else { baselineCompiler = null; defaultCompiler = optimizingCompiler; } } @HOSTED_ONLY public static RuntimeCompiler instantiateCompiler(String name) { try { return (RuntimeCompiler) Class.forName(name).newInstance(); } catch (Exception e) { throw FatalError.unexpected("Error instantiating compiler " + name, e); } } /** * Gets a string describing the compilation mode. * * @return a string suitable for inclusion in the output produced by the {@link sun.misc.Version -version} VM option */ public String mode() { if (RCT != 0) { if (defaultCompiler == baselineCompiler) { return "mixed mode, baseline-compile first"; } return "mixed mode, optimize first"; } return "optimizing-only"; } /** * Gets the set of system properties which are used to configure the compilers. */ public Properties properties() { Properties props = new Properties(); props.put(OPTIMIZING_COMPILER_PROPERTY, optimizingCompiler.getClass().getName()); if (baselineCompiler != null) { props.put(BASELINE_COMPILER_PROPERTY, baselineCompiler.getClass().getName()); } return props; } /** * This method initializes the adaptive compilation system, either while bootstrapping or * at VM startup time. This implementation may create daemon threads for background compilation. * * @param phase the phase of VM starting up. */ public void initialize(MaxineVM.Phase phase) { optimizingCompiler.initialize(phase); if (baselineCompiler != null) { baselineCompiler.initialize(phase); } if (isHosted()) { if (BACKGROUND_COMPILATION) { // launch a compiler thread if background compilation is supported (currently no) final CompilationThread compilationThread = new CompilationThread(); compilationThread.setDaemon(true); compilationThread.start(); } } else if (phase == MaxineVM.Phase.STARTING) { if (opt) { defaultCompiler = optimizingCompiler; } if (RCT != 0 && baselineCompiler != null) { MethodInstrumentation.enable(RCT); } if (BACKGROUND_COMPILATION) { // launch a compiler thread if background compilation is supported (currently no) final CompilationThread compilationThread = new CompilationThread(); compilationThread.setDaemon(true); compilationThread.start(); } } else if (phase == Phase.RUNNING) { if (PrintCodeCacheMetrics != 0) { Runtime.getRuntime().addShutdownHook(new Thread("CodeCacheMetricsPrinter") { @Override public void run() { new CodeCacheMetricsPrinter(CompilationBroker.PrintCodeCacheMetrics > 1).printTo(Log.out); } }); } } } /** * Default compilation, not for deopt. * @param cma * @param nature */ public TargetMethod compile(ClassMethodActor cma, Nature nature) { return compile(cma, nature, false); } /** * Deopt compilation, if necessary. * The method is only recompiled if the current target method has been invalidated, which is the normal deopt case. * However, a VMTI handler may already have compiled the method before the deoptimzation step happened. * @param cma * @param force always compile iff {@code true}. */ public TargetMethod compileForDeopt(ClassMethodActor cma) { TargetMethod tm = cma.currentTargetMethod(); if (tm != null && tm.isBaseline() && tm.invalidated() == null) { return tm; } return compile(cma, Nature.BASELINE, true); } public TargetMethod compile(ClassMethodActor cma, Nature nature, boolean isDeopt) { try { return compile(cma, nature, isDeopt, false); } catch (Throwable t) { // cannot happen return null; } } /** * Produces a target method for the specified method actor. If another thread is currently * compiling {@code cma}, then the result of that compilation is returned. Otherwise, * a new compilation is scheduled and its result is returned. Either way, this method * waits for the result of a compilation to return it. * * @param cma the method for which to make the target method * @param nature the specific type of target method required or {@code null} if any target method is acceptable * @param isDeopt if the compilation is for a deoptimzation * @param failFast don't try recompilation on failure, instead rethrow the exception * @return a newly compiled version of a {@code cma} * @throws iff failFast the exception that was thrown by first selected compiler */ public TargetMethod compile(ClassMethodActor cma, Nature nature, boolean isDeopt, boolean failFast) throws Throwable { boolean retryRun = false; while (true) { Compilation compilation; boolean doCompile = true; synchronized (cma) { assert !(cma.isNative() && cma.isVmEntryPoint()) : "cannot compile JNI functions that are native"; Object compiledState = cma.compiledState; compilation = compiledState instanceof Compilation ? (Compilation) compiledState : null; if (compilation != null && (nature == null || nature == compilation.nature)) { // Only wait for a pending compilation if it is compatible with the current request. // That is, the current request does not specify a special nature (nature == null) // or it specifies the same nature as the pending compilation (nature == compilation.nature) if (retryRun) { assert compilation.compilingThread == Thread.currentThread(); assert nature == null : "cannot retry if specific compilation nature is specified"; compilation.compiler = selectRetryCompiler(cma, nature, compilation.compiler); } else { // the method is currently being compiled, just wait for the result doCompile = false; } } else { Compilations prevCompilations = compilation != null ? compilation.prevCompilations : (Compilations) compiledState; RuntimeCompiler compiler = selectCompiler(cma, nature, isDeopt); if (retryRun) { compiler = selectRetryCompiler(cma, nature, compiler); } compilation = new Compilation(compiler, cma, prevCompilations, Thread.currentThread(), nature, isDeopt); cma.compiledState = compilation; } } try { if (doCompile) { TargetMethod tm = compilation.compile(); VMTI.handler().methodCompiled(cma); return tm; } else { // return result from other thread (which will have send the VMTI event) return compilation.get(); } } catch (Throwable t) { if (VMOptions.verboseOption.verboseCompilation) { boolean lockDisabledSafepoints = Log.lock(); Log.printCurrentThread(false); Log.print(": Compilation of " + cma + " by " + compilation.compiler + " failed"); t.printStackTrace(Log.out); Log.unlock(lockDisabledSafepoints); } if (failFast) { throw t; } if (!FailOverCompilation || retryRun || (baselineCompiler == null) || (isHosted() && compilation.compiler == optimizingCompiler)) { // This is the final failure: no other compilers available or failover is disabled throw FatalError.unexpected("Compilation of " + cma + " by " + compilation.compiler + " failed (final attempt)", t); } retryRun = true; if (VMOptions.verboseOption.verboseCompilation) { boolean lockDisabledSafepoints = Log.lock(); Log.printCurrentThread(false); Log.println(": Retrying with " + selectRetryCompiler(cma, nature, compilation.compiler) + "..."); Log.unlock(lockDisabledSafepoints); } } } } /** * Select the appropriate compiler based on the current state of the method. * * @param cma the class method actor to compile * @param nature the specific type of target method required or {@code null} if any target method is acceptable * @param isDeopt TODO * @return the compiler that should be used to perform the next compilation of the method */ protected RuntimeCompiler selectCompiler(ClassMethodActor cma, RuntimeCompiler.Nature nature, boolean isDeopt) { String reason; RuntimeCompiler compiler; if (Actor.isUnsafe(cma.compilee().flags())) { assert nature != Nature.BASELINE : "cannot produce baseline version of " + cma; reason = "unsafe"; compiler = optimizingCompiler; } else { if (nature == Nature.BASELINE) { compiler = baselineCompiler; reason = "nature:baseline"; assert compiler != null; } else if (nature == Nature.OPT) { reason = "nature:opt"; compiler = optimizingCompiler; } else { // The -XX:CompileCommand is only considered if a specific nature was not specified String compilerName = compilerFor(cma); reason = null; compiler = null; if (compilerName != null) { if (optimizingCompiler != null && optimizingCompiler.matches(compilerName)) { compiler = optimizingCompiler; reason = "CompileCommand"; } else if (baselineCompiler != null && baselineCompiler.matches(compilerName)) { compiler = baselineCompiler; reason = "CompileCommand"; } } if (reason == null) { if (isHosted()) { // at prototyping time, default to the opt compiler compiler = optimizingCompiler; } else { if (VMTI.handler().hasBreakpoints(cma)) { reason = "vmti"; compiler = baselineCompiler; } else if (!isDeopt && cma.isVM()) { // compile VM extensions with the opt compiler (cf isHosted) reason = "vm"; compiler = optimizingCompiler; } else { compiler = defaultCompiler; } } } } } // Print the reason for the compiler selection if it's not the default if (VMOptions.verboseOption.verboseCompilation && reason != null) { String methodString = cma.format("%H.%n(%p)"); boolean lockDisabledSafepoints = Log.lock(); Log.printCurrentThread(false); Log.print(": "); Log.print(compiler.getClass().getSimpleName()); Log.print(" selected to compile "); Log.print(methodString); Log.print(", reason: "); Log.println(reason); Log.unlock(lockDisabledSafepoints); } return compiler; } /** * Select the appropriate compiler to retry compilation based on the current state of the method * and the previous compiler. * * @param cma the class method actor to compile * @param nature the specific type of target method required or {@code null} if any target method is acceptable * @param previousCompiler compiler compiler that already tried to compile * @return the compiler that should be used to perform the next compilation of the method */ protected RuntimeCompiler selectRetryCompiler(ClassMethodActor cma, RuntimeCompiler.Nature nature, RuntimeCompiler previousCompiler) { if (previousCompiler == optimizingCompiler) { return baselineCompiler; } else { return optimizingCompiler; } } /** * Reset the compiled state for a given method. This method * should only be used in very specific circumstances to force recompilation of a method and is NOT FOR GENERAL * USE. * * @param cma the method for which to reset the method state */ @HOSTED_ONLY public static void resetCompiledState(ClassMethodActor cma) { cma.compiledState = Compilations.EMPTY; } /** * Handles an instrumentation counter overflow upon entry to a profiled method. * This method must be called on the thread that overflowed the counter. * * @param mpo profiling object (including the method itself) * @param receiver the receiver object of the profiled method. This will be {@code null} if the profiled method is static. */ public static void instrumentationCounterOverflow(MethodProfile mpo, Object receiver) { if (mpo.compilationDisabled) { mpo.entryCount = Integer.MAX_VALUE; return; } if (Heap.isAllocationDisabledForCurrentThread()) { logCounterOverflow(mpo, "Stopped recompilation because allocation is currently disabled"); // We don't want to see another counter overflow in the near future mpo.entryCount = 1000; return; } if (Compilation.isCompilationRunningInCurrentThread()) { logCounterOverflow(mpo, "Stopped recompilation because compilation is running in current thread"); // We don't want to see another counter overflow in the near future mpo.entryCount = 1000; return; } ClassMethodActor cma = mpo.method.classMethodActor; TargetMethod oldMethod = mpo.method; TargetMethod newMethod = Compilations.currentTargetMethod(cma.compiledState, null); if (oldMethod == newMethod || newMethod == null) { if (!(cma.compiledState instanceof Compilation)) { // There is no newer compiled version available yet that we could just patch to, so recompile logCounterOverflow(mpo, ""); try { newMethod = vm().compilationBroker.compile(cma, Nature.OPT); } catch (InternalError e) { if (VMOptions.verboseOption.verboseCompilation) { e.printStackTrace(Log.out); } // Optimization failed - stay with the baseline method. By not resetting the counter, // the next counter overflow (due to integer wrapping) will be a while away. return; } } } if (oldMethod == newMethod || newMethod == null) { // No compiled method available yet, maybe compilation is pending. // We don't want to see another counter overflow in the near future. mpo.entryCount = 10000; } else { assert newMethod != null : oldMethod; logPatching(cma, oldMethod, newMethod); mpo.entryCount = 0; if (receiver != null) { Address from = oldMethod.getEntryPoint(VTABLE_ENTRY_POINT).toAddress(); Address to = newMethod.getEntryPoint(VTABLE_ENTRY_POINT).toAddress(); // Simply overwrite all vtable slots containing 'oldMethod' with 'newMethod'. // These updates can be made atomically without need for a lock. Hub hub = ObjectAccess.readHub(receiver); for (int i = 0; i < hub.vTableLength(); i++) { int index = Hub.vTableStartIndex() + i; if (hub.getWord(index).equals(from)) { logDispatchTablePatch(cma, from, to, hub, index, "vtable"); hub.setWord(index, to); } } for (int i = 0; i < hub.iTableLength; i++) { int index = hub.iTableStartIndex + i; if (hub.getWord(index).equals(from)) { logDispatchTablePatch(cma, from, to, hub, index, "itable"); hub.setWord(index, to); } } } // Look for a static call to 'oldMethod' and patch it. // This occurs even if 'cma' is non-static // as it may have been called directly. DirectCallPatcher patcher = new DirectCallPatcher(oldMethod, newMethod); new VmStackFrameWalker(VmThread.current().tla()).inspect(Pointer.fromLong(here()), VMRegister.getCpuStackPointer(), VMRegister.getCpuFramePointer(), patcher); } } public static void logCounterOverflow(MethodProfile mpo, String msg) { if (VMOptions.verboseOption.verboseCompilation) { boolean lockDisabledSafepoints = Log.lock(); Log.printCurrentThread(false); Log.print(": Invocation counter overflow of "); Log.printMethod(mpo.method, false); Log.print(" counter "); Log.print(mpo.entryCount); Log.print(" "); Log.print(msg); Log.println(); Log.unlock(lockDisabledSafepoints); } } private static void logPatching(ClassMethodActor cma, TargetMethod oldMethod, TargetMethod newMethod) { if (verboseOption.verboseCompilation) { boolean lockDisabledSafepoints = Log.lock(); Log.printCurrentThread(false); Log.print(": Patching for method "); Log.printMethod(cma, false); Log.print(" oldMethod "); Log.print(oldMethod.getEntryPoint(BASELINE_ENTRY_POINT)); Log.print(" newMethod "); Log.print(newMethod.getEntryPoint(BASELINE_ENTRY_POINT)); Log.println(); Log.unlock(lockDisabledSafepoints); } } private static void logDispatchTablePatch(ClassMethodActor cma, final Address from, final Address to, Hub hub, int index, String table) { if (verboseOption.verboseCompilation) { boolean lockDisabledSafepoints = Log.lock(); Log.printCurrentThread(false); Log.print(": Patching "); Log.print(hub.classActor.name()); Log.print('.'); Log.print(table); Log.print('['); Log.print(index); Log.print("] {"); Log.printMethod(cma, false); Log.print("} "); Log.print(from); Log.print(" -> "); Log.println(to); Log.unlock(lockDisabledSafepoints); } } private static void logStaticCallPatch(StackFrameCursor current, CodePointer callSite, int dcIndex, CodePointer to) { if (verboseOption.verboseCompilation) { boolean lockDisabledSafepoints = Log.lock(); Log.printCurrentThread(false); Log.print(": Patching static call at "); Log.printLocation(current.targetMethod(), callSite, false); Log.print(" (direct callee index "); Log.print(dcIndex); Log.print(") to "); Log.println(to); Log.unlock(lockDisabledSafepoints); } } private static void logNoFurtherStaticCallPatching() { if (verboseOption.verboseCompilation) { boolean lockDisabledSafepoints = Log.lock(); Log.printCurrentThread(false); Log.println(": No further patching of static calls"); Log.unlock(lockDisabledSafepoints); } } /** * This class implements a daemon thread that performs compilations in the background. Depending on the compiler * configuration, multiple compilation threads may be working in parallel. */ protected class CompilationThread extends Thread { protected CompilationThread() { super("compile"); setDaemon(true); } /** * The current compilation being performed by this thread. */ Compilation compilation; /** * Continuously polls the compilation queue for work, performing compilations as they are removed from the * queue. */ @Override public void run() { while (true) { try { compileOne(); } catch (InterruptedException e) { // do nothing. } catch (Throwable t) { Log.print("Exception during compilation of " + compilation.classMethodActor); t.printStackTrace(); } } } /** * Polls the compilation queue and performs a single compilation. * * @throws InterruptedException if the thread was interrupted waiting on the queue */ void compileOne() throws InterruptedException { compilation = null; synchronized (pending) { while (compilation == null) { compilation = pending.poll(); if (compilation == null) { pending.wait(); } } } compilation.compilingThread = Thread.currentThread(); if (GCOnRecompilation) { System.gc(); } compilation.compile(); compilation = null; } } /** * Helper class for patching any direct call sites on the stack corresponding to a target method * being replaced by a recompiled version. */ static class DirectCallPatcher extends RawStackFrameVisitor { /** * The maximum number of frames to search for a patchable direct call site. */ static final int FRAME_SEARCH_LIMIT = 10; private final TargetMethod oldMethod; private final TargetMethod newMethod; int frameCount; public DirectCallPatcher(TargetMethod oldMethod, TargetMethod newMethod) { this.oldMethod = oldMethod; this.newMethod = newMethod; } private int directCalleePosition(TargetMethod tm, CodePointer callSite) { final Safepoints safepoints = tm.safepoints(); final int callPos = callSite.minus(tm.codeStart()).toInt(); int dcIndex = 0; for (int i = 0; i < safepoints.size(); i++) { if (safepoints.isSetAt(DIRECT_CALL, i)) { if (safepoints.causePosAt(i) == callPos) { return dcIndex; } dcIndex++; } } return -1; } @Override public boolean visitFrame(StackFrameCursor current, StackFrameCursor callee) { if (platform().isa == ISA.AMD64) { if (current.isTopFrame()) { return true; } Pointer ip = current.ipAsPointer(); CodePointer callSite = CodePointer.from(ip.minus(AMD64TargetMethodUtil.RIP_CALL_INSTRUCTION_SIZE)); Pointer callSitePointer = callSite.toPointer(); if ((callSitePointer.readByte(0) & 0xFF) == AMD64TargetMethodUtil.RIP_CALL) { CodePointer target = CodePointer.from(ip.plus(callSitePointer.readInt(1))); if (target.equals(oldMethod.getEntryPoint(BASELINE_ENTRY_POINT))) { final CodePointer to = newMethod.getEntryPoint(BASELINE_ENTRY_POINT); final TargetMethod tm = current.targetMethod(); final int dcIndex = directCalleePosition(tm, callSite); assert dcIndex != -1 : "no valid direct callee for call site " + callSite.to0xHexString(); logStaticCallPatch(current, callSite, dcIndex, to); AMD64TargetMethodUtil.mtSafePatchCallDisplacement(tm, callSite, to); // Stop traversing the stack after a direct call site has been patched return false; } if (target.equals(oldMethod.getEntryPoint(OPTIMIZED_ENTRY_POINT))) { final CodePointer to = newMethod.getEntryPoint(OPTIMIZED_ENTRY_POINT); final TargetMethod tm = current.targetMethod(); final int dcIndex = directCalleePosition(tm, callSite); assert dcIndex != -1 : "no valid direct callee for call site " + callSite.to0xHexString(); logStaticCallPatch(current, callSite, dcIndex, to); AMD64TargetMethodUtil.mtSafePatchCallDisplacement(tm, callSite, to); // Stop traversing the stack after a direct call site has been patched return false; } } if (++frameCount > FRAME_SEARCH_LIMIT) { logNoFurtherStaticCallPatching(); return false; } return true; } throw FatalError.unimplemented(); } } /** * Compiler that can be used to create a functional Maxine runtime (albeit without compilation) * when a specified compiler can't be found on the class path. */ @HOSTED_ONLY public static class NullCompiler implements RuntimeCompiler { public final Nature nature; public NullCompiler(Nature nature) { this.nature = nature; } public void initialize(Phase phase) { } public TargetMethod compile(ClassMethodActor classMethodActor, boolean isDeopt, boolean install, CiStatistics stats) { return null; } public Nature nature() { return nature; } public boolean matches(String compilerName) { return true; } @Override public String toString() { return getClass().getSimpleName() + "@" + Integer.toHexString(hashCode()) + "[" + nature + "]"; } } public static class NullOptCompiler extends NullCompiler { public NullOptCompiler() { super(Nature.OPT); } } public static class NullBaselineCompiler extends NullCompiler { public NullBaselineCompiler() { super(Nature.BASELINE); } } }