/* * This file is part of the Jikes RVM project (http://jikesrvm.org). * * This file is licensed to You under the Common Public License (CPL); * You may not use this file except in compliance with the License. You * may obtain a copy of the License at * * http://www.opensource.org/licenses/cpl1.0.php * * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. */ package org.jikesrvm.compilers.opt; import java.util.Stack; import org.jikesrvm.classloader.VM_Atom; import org.jikesrvm.classloader.VM_Class; import org.jikesrvm.classloader.VM_Method; import org.jikesrvm.classloader.VM_NormalMethod; import org.jikesrvm.classloader.VM_TypeReference; import org.jikesrvm.compilers.opt.ir.Call; import org.jikesrvm.compilers.opt.ir.OPT_CompilationState; import org.jikesrvm.compilers.opt.ir.OPT_Instruction; import org.jikesrvm.compilers.opt.ir.OPT_Operand; import org.jikesrvm.compilers.opt.ir.OPT_RegisterOperand; import org.jikesrvm.runtime.VM_Entrypoints; /** * This class provides some utilities that are useful for inlining. */ public abstract class OPT_InlineTools implements OPT_Constants { private static final VM_Atom arraycopyName = VM_Atom.findOrCreateAsciiAtom("arraycopy"); private static final VM_Atom objectArrayCopyDescriptor = VM_Atom.findOrCreateAsciiAtom("([Ljava/lang/Object;I[Ljava/lang/Object;II)V"); /** * Does class <code>A</code> directly implement the interface <code>B</code>? */ public static boolean implementsInterface(Class<?> A, Class<?> B) { for (Class<?> i : A.getInterfaces()) { if (i == B) { return true; } } return false; } /** * Does the callee method have a body? * @param callee The callee method * @return <code>true</code> if it has bytecodes, false otherwise. */ public static boolean hasBody(VM_Method callee) { return !(callee.isNative() || callee.isAbstract()); } /** * Does an inlined call to callee need a guard, to protect against * a mispredicted dynamic dispatch? * * @param callee the callee method */ public static boolean needsGuard(VM_Method callee) { return !(callee.isFinal() || callee.getDeclaringClass().isFinal() || callee.isPrivate() || callee.isObjectInitializer() || callee.isStatic()); } /** * Is the method CURRENTLY final (not overridden by any subclass)? * Note that this says nothing about whether or not the method will * be overriden by future dynamically loaded classes. */ public static boolean isCurrentlyFinal(VM_Method callee, boolean searchSubclasses) { VM_Class klass = callee.getDeclaringClass(); if (klass.isInterface()) { // interface methods are not final. return false; } VM_Class[] subClasses = klass.getSubClasses(); if (subClasses.length == 0) { // Currently no subclasses, so trivially not overridden return true; } else if (searchSubclasses) { // see if any subclasses have overridden the method Stack<VM_Class> s = new Stack<VM_Class>(); for (VM_Class subClass1 : subClasses) { s.push(subClass1); } while (!s.isEmpty()) { VM_Class subClass = s.pop(); if (subClass.findDeclaredMethod(callee.getName(), callee.getDescriptor()) != null) { return false; // found an overridding method } subClasses = subClass.getSubClasses(); for (VM_Class subClass1 : subClasses) { s.push(subClass1); } } return true; // didn't find an overridding method in all currently resolved subclasses } else { return false; // could be one, so be conservative. } } /** * Given the currently available information at the call site, * what's our best guess on the inlined size of the callee? * @param callee the method to be inlined * @param state the compilation state decribing the call site where it * is to be inlined * @return an inlined size estimate (number of machine code instructions) */ public static int inlinedSizeEstimate(VM_NormalMethod callee, OPT_CompilationState state) { int sizeEstimate = callee.inlinedSizeEstimate(); // Adjust size estimate downward to account for optimizations enabled // by constant parameters. OPT_Instruction callInstr = state.getCallInstruction(); int numArgs = Call.getNumberOfParams(callInstr); double reductionFactor = 1.0; // no reduction. for (int i = 0; i < numArgs; i++) { OPT_Operand op = Call.getParam(callInstr, i); if (op instanceof OPT_RegisterOperand) { OPT_RegisterOperand rop = (OPT_RegisterOperand) op; if (rop.isExtant()) { reductionFactor -= 0.05; // 5% credit for being extant. } if (rop.isPreciseType()) { reductionFactor -= 0.15; // 15% credit for being a precise type. } if (rop.isDeclaredType()) { reductionFactor -= 0.01; // 1% credit for being a declared type. } } else if (op.isIntConstant()) { reductionFactor -= 0.10; // 10% credit for being an int constant } else if (op.isNullConstant()) { reductionFactor -= 0.15; // 15% credit for being 'null' } else if (op.isStringConstant()) { reductionFactor -= 0.10; // 10% credit for being a string constant } } reductionFactor = Math.max(reductionFactor, 0.40); // bound credits at 60% // off. return (int) (sizeEstimate * reductionFactor); } /** * Should the callee method always be inlined? * Usually this is becuase of a programmer directive (InlinePragma), * but we also use this mechanism to hardwire a couple special cases. * * @param callee the method being considered for inlining * @param state the compilation state of the caller. * @return whether or not the callee should be unconditionally inlined. */ public static boolean hasInlinePragma(VM_Method callee, OPT_CompilationState state) { if (callee.hasInlineAnnotation()) return true; // If we know what kind of array "src" (argument 0) is // then we always want to inline java.lang.System.arraycopy. // TODO: Would be nice to discover this automatically!!! // There have to be other methods with similar properties. if (callee == VM_Entrypoints.sysArrayCopy) { OPT_Operand src = Call.getParam(state.getCallInstruction(), 0); return src.getType() != VM_TypeReference.JavaLangObject; } // More arraycopy hacks. If we the two starting indices are constant and // it's not the object array version // (too big...kills other inlining), then inline it. if (callee.getDeclaringClass().getTypeRef() == VM_TypeReference.VM_Array && callee.getName() == arraycopyName && callee.getDescriptor() != objectArrayCopyDescriptor) { return Call.getParam(state.getCallInstruction(), 1).isConstant() && Call.getParam(state.getCallInstruction(), 3).isConstant(); } return false; } /** * Should the callee method be barred from ever being considered for inlining? * * @param callee the method being considered for inlining * @param state the compilation state of the caller. * @return whether or not the callee should be unconditionally barred * from being inlined. */ public static boolean hasNoInlinePragma(VM_Method callee, OPT_CompilationState state) { return callee.hasNoInlinePragma(); } /** * Is it safe to speculatively inline the callee into the caller? * * Some forms of speculative inlining are unsafe to apply to * methods of the core virtual machine because if we are forced to * invalidate the methods, we will be unable to compile their * replacement method. * The current test is overly conservative, but past attempts at * defining a more precise set of "third rail" classes have * always resulted in missing some (only to discover them later * when Jikes RVM hangs or crashes.) * * @param caller the caller method * @param callee the callee method * @return Whether or not we are allowed to speculatively inline * the callee into the caller. */ public static boolean isForbiddenSpeculation(VM_Method caller, VM_Method callee) { return caller.getDeclaringClass().isInBootImage() && !callee.getDeclaringClass().getDescriptor().isRVMDescriptor(); } }