001 /* 002 * This file is part of the Jikes RVM project (http://jikesrvm.org). 003 * 004 * This file is licensed to You under the Eclipse Public License (EPL); 005 * You may not use this file except in compliance with the License. You 006 * may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/eclipse-1.0.php 009 * 010 * See the COPYRIGHT.txt file distributed with this work for information 011 * regarding copyright ownership. 012 */ 013 package org.jikesrvm.compilers.opt.inlining; 014 015 import java.util.Stack; 016 017 import org.jikesrvm.VM; 018 import org.jikesrvm.classloader.RVMClass; 019 import org.jikesrvm.classloader.RVMMethod; 020 import org.jikesrvm.classloader.NormalMethod; 021 import org.jikesrvm.classloader.TypeReference; 022 import org.jikesrvm.compilers.opt.driver.OptConstants; 023 import org.jikesrvm.compilers.opt.ir.Call; 024 import org.jikesrvm.compilers.opt.ir.Instruction; 025 import org.jikesrvm.compilers.opt.ir.operand.Operand; 026 import org.jikesrvm.compilers.opt.ir.operand.RegisterOperand; 027 import org.jikesrvm.compilers.opt.OptOptions; 028 import org.jikesrvm.runtime.Entrypoints; 029 import org.vmmagic.pragma.Inline; 030 031 /** 032 * This class provides some utilities that are useful for inlining. 033 */ 034 public abstract class InlineTools implements OptConstants { 035 036 /** 037 * Does class <code>A</code> directly implement the interface <code>B</code>? 038 */ 039 public static boolean implementsInterface(Class<?> A, Class<?> B) { 040 for (Class<?> i : A.getInterfaces()) { 041 if (i == B) { 042 return true; 043 } 044 } 045 return false; 046 } 047 048 /** 049 * Does the callee method have a body? 050 * @param callee The callee method 051 * @return <code>true</code> if it has bytecodes, false otherwise. 052 */ 053 public static boolean hasBody(RVMMethod callee) { 054 return !(callee.isNative() || callee.isAbstract()); 055 } 056 057 /** 058 * Does an inlined call to callee need a guard, to protect against 059 * a mispredicted dynamic dispatch? 060 * 061 * @param callee the callee method 062 */ 063 public static boolean needsGuard(RVMMethod callee) { 064 return !(callee.isFinal() || 065 callee.getDeclaringClass().isFinal() || 066 callee.isPrivate() || 067 callee.isObjectInitializer() || 068 callee.isStatic()); 069 } 070 071 /** 072 * Is the method CURRENTLY final (not overridden by any subclass)? 073 * Note that this says nothing about whether or not the method will 074 * be overriden by future dynamically loaded classes. 075 */ 076 public static boolean isCurrentlyFinal(RVMMethod callee, boolean searchSubclasses) { 077 RVMClass klass = callee.getDeclaringClass(); 078 if (klass.isInterface()) { 079 // interface methods are not final. 080 return false; 081 } 082 RVMClass[] subClasses = klass.getSubClasses(); 083 if (subClasses.length == 0) { 084 // Currently no subclasses, so trivially not overridden 085 return true; 086 } else if (searchSubclasses) { 087 // see if any subclasses have overridden the method 088 Stack<RVMClass> s = new Stack<RVMClass>(); 089 for (RVMClass subClass1 : subClasses) { 090 s.push(subClass1); 091 } 092 while (!s.isEmpty()) { 093 RVMClass subClass = s.pop(); 094 if (subClass.findDeclaredMethod(callee.getName(), callee.getDescriptor()) != null) { 095 return false; // found an overridding method 096 } 097 subClasses = subClass.getSubClasses(); 098 for (RVMClass subClass1 : subClasses) { 099 s.push(subClass1); 100 } 101 } 102 return true; // didn't find an overridding method in all currently resolved subclasses 103 } else { 104 return false; // could be one, so be conservative. 105 } 106 } 107 108 /** 109 * Given the currently available information at the call site, 110 * what's our best guess on the inlined size of the callee? 111 * @param callee the method to be inlined 112 * @param state the compilation state decribing the call site where it 113 * is to be inlined 114 * @return an inlined size estimate (number of machine code instructions) 115 */ 116 public static int inlinedSizeEstimate(NormalMethod callee, CompilationState state) { 117 int sizeEstimate = callee.inlinedSizeEstimate(); 118 // Adjust size estimate downward to account for optimizations 119 // that are typically enabled by constant parameters. 120 Instruction callInstr = state.getCallInstruction(); 121 int numArgs = Call.getNumberOfParams(callInstr); 122 double reductionFactor = 1.0; // no reduction. 123 OptOptions opts = state.getOptions(); 124 for (int i = 0; i < numArgs; i++) { 125 Operand op = Call.getParam(callInstr, i); 126 if (op instanceof RegisterOperand) { 127 RegisterOperand rop = (RegisterOperand)op; 128 TypeReference type = rop.getType(); 129 if (type.isReferenceType()) { 130 if (type.isArrayType()) { 131 // Reductions only come from optimization of dynamic type checks; all virtual methods on arrays are defined on Object. 132 if (rop.isPreciseType()) { 133 reductionFactor -= opts.INLINE_PRECISE_REG_ARRAY_ARG_BONUS; 134 } else if (rop.isDeclaredType() && callee.hasArrayWrite() && type.getArrayElementType().isReferenceType()) { 135 // potential to optimize checkstore portion of aastore bytecode on parameter 136 reductionFactor -= opts.INLINE_DECLARED_AASTORED_ARRAY_ARG_BONUS; 137 } 138 } else { 139 // Reductions come from optimization of dynamic type checks and improved inlining of virtual/interface calls 140 if (rop.isPreciseType()) { 141 reductionFactor -= opts.INLINE_PRECISE_REG_CLASS_ARG_BONUS; 142 } else if (rop.isExtant()) { 143 reductionFactor -= opts.INLINE_EXTANT_REG_CLASS_ARG_BONUS; 144 } 145 } 146 } 147 } else if (op.isIntConstant()) { 148 reductionFactor -= opts.INLINE_INT_CONST_ARG_BONUS; 149 } else if (op.isNullConstant()) { 150 reductionFactor -= opts.INLINE_NULL_CONST_ARG_BONUS; 151 } else if (op.isObjectConstant()) { 152 reductionFactor -= opts.INLINE_OBJECT_CONST_ARG_BONUS; 153 } 154 } 155 reductionFactor = Math.max(reductionFactor, 1.0-opts.INLINE_MAX_ARG_BONUS); 156 if (opts.INLINE_CALL_DEPTH_COST != 0.00) { 157 double depthCost = Math.pow(1.0+opts.INLINE_CALL_DEPTH_COST, state.getInlineDepth()+1); 158 return (int) (sizeEstimate * reductionFactor * depthCost); 159 } else { 160 return (int) (sizeEstimate * reductionFactor); 161 } 162 } 163 164 /** 165 * Should the callee method always be inlined? 166 * Usually this is because of a programmer directive (InlinePragma), 167 * but we also use this mechanism to hardwire a couple special cases. 168 * 169 * @param callee the method being considered for inlining 170 * @param state the compilation state of the caller. 171 * @return whether or not the callee should be unconditionally inlined. 172 */ 173 public static boolean hasInlinePragma(RVMMethod callee, CompilationState state) { 174 if (callee.hasInlineAnnotation()) { 175 Inline ann = callee.getAnnotation(Inline.class); 176 if (ann == null) { 177 // annotation was lost, assume it was Always 178 return true; 179 } 180 181 switch (ann.value()) { 182 case Always: 183 return true; 184 case AllArgumentsAreConstant: { 185 boolean result = true; 186 Instruction s = state.getCallInstruction(); 187 for (int i=0, n=Call.getNumberOfParams(s); i < n; i++) { 188 if (!Call.getParam(s, i).isConstant()) { 189 result = false; 190 break; 191 } 192 } 193 if (result) { 194 return true; 195 } 196 break; 197 } 198 case ArgumentsAreConstant: { 199 boolean result = true; 200 Instruction s = state.getCallInstruction(); 201 int[] args = ann.arguments(); 202 for (int arg : args) { 203 if (VM.VerifyAssertions) { 204 VM._assert(arg >= 0, "argument is invalid: " + arg); 205 VM._assert(arg < Call.getNumberOfParams(s), "argument is invalid: " + arg); 206 } 207 if (!Call.getParam(s, arg).isConstant()) { 208 result = false; 209 break; 210 } 211 } 212 if (result) { 213 return true; 214 } 215 break; 216 } 217 case AssertionsDisabled: { 218 return !VM.VerifyAssertions; 219 } 220 } 221 } 222 // TODO: clean this hack up 223 // Hack to inline java.lang.VMSystem.arraycopy in the case that 224 // arg 0 isn't an Object 225 if (callee == Entrypoints.sysArrayCopy) { 226 Operand src = Call.getParam(state.getCallInstruction(), 0); 227 return src.getType() != TypeReference.JavaLangObject; 228 } 229 return false; 230 } 231 232 /** 233 * Should the callee method be barred from ever being considered for inlining? 234 * 235 * @param callee the method being considered for inlining 236 * @param state the compilation state of the caller. 237 * @return whether or not the callee should be unconditionally barred 238 * from being inlined. 239 */ 240 public static boolean hasNoInlinePragma(RVMMethod callee, CompilationState state) { 241 return callee.hasNoInlinePragma(); 242 } 243 244 /** 245 * Is it safe to speculatively inline the callee into the caller? 246 * <p> 247 * Some forms of speculative inlining are unsafe to apply to 248 * methods of the core virtual machine because if we are forced to 249 * invalidate the methods, we will be unable to compile their 250 * replacement method. 251 * <p> 252 * TODO The current test is overly conservative, but past attempts at 253 * defining a more precise set of "third rail" classes have 254 * always resulted in missing some (only to discover them later 255 * when Jikes RVM hangs or crashes.) 256 * 257 * @param caller the caller method 258 * @param callee the callee method 259 * @return Whether or not we are allowed to speculatively inline 260 * the callee into the caller. 261 */ 262 public static boolean isForbiddenSpeculation(RVMMethod caller, RVMMethod callee) { 263 return caller.getDeclaringClass().isInBootImage() && !callee.getDeclaringClass().getDescriptor().isRVMDescriptor(); 264 } 265 }