/* * This file is part of the Jikes RVM project (http://jikesrvm.org). * * This file is licensed to You under the Eclipse Public License (EPL); * 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/eclipse-1.0.php * * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. */ package org.jikesrvm.compilers.opt.escape; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import org.jikesrvm.VM; import org.jikesrvm.classloader.RVMMethod; import org.jikesrvm.classloader.NormalMethod; import org.jikesrvm.compilers.opt.DefUse; import org.jikesrvm.compilers.opt.MagicNotImplementedException; import org.jikesrvm.compilers.opt.OptimizingCompilerException; import org.jikesrvm.compilers.opt.OptOptions; import org.jikesrvm.compilers.opt.Simple; import org.jikesrvm.compilers.opt.bc2ir.ConvertBCtoHIR; import org.jikesrvm.compilers.opt.driver.CompilationPlan; import org.jikesrvm.compilers.opt.driver.CompilerPhase; import org.jikesrvm.compilers.opt.driver.OptimizationPlanCompositeElement; import org.jikesrvm.compilers.opt.driver.OptimizationPlanElement; import org.jikesrvm.compilers.opt.driver.OptimizingCompiler; import org.jikesrvm.compilers.opt.ir.AStore; import org.jikesrvm.compilers.opt.ir.Call; import org.jikesrvm.compilers.opt.ir.Move; import org.jikesrvm.compilers.opt.ir.IR; import org.jikesrvm.compilers.opt.ir.Instruction; import org.jikesrvm.compilers.opt.ir.InstructionEnumeration; import org.jikesrvm.compilers.opt.ir.OperandEnumeration; import org.jikesrvm.compilers.opt.ir.Operators; import static org.jikesrvm.compilers.opt.ir.Operators.ADDR_2INT_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.ADDR_2LONG_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.ARRAYLENGTH_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.ATHROW_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.ATTEMPT_ADDR_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.ATTEMPT_INT_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.ATTEMPT_LONG_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.BOOLEAN_CMP_ADDR_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.BOOLEAN_CMP_INT_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.BOUNDS_CHECK_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.BYTE_ALOAD_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.BYTE_ASTORE_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.BYTE_LOAD_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.BYTE_STORE_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.CALL_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.CHECKCAST_NOTNULL_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.CHECKCAST_UNRESOLVED_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.CHECKCAST_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.DOUBLE_ALOAD_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.DOUBLE_ASTORE_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.DOUBLE_LOAD_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.DOUBLE_STORE_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.FLOAT_ALOAD_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.FLOAT_ASTORE_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.FLOAT_LOAD_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.FLOAT_STORE_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.GETFIELD_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.GETSTATIC_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.GET_CAUGHT_EXCEPTION_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.GET_OBJ_TIB_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.GET_TYPE_FROM_TIB_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.IG_CLASS_TEST_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.IG_METHOD_TEST_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.IG_PATCH_POINT_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.INSTANCEOF_NOTNULL_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.INSTANCEOF_UNRESOLVED_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.INSTANCEOF_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.INT_2ADDRSigExt_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.INT_2ADDRZerExt_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.INT_2LONG_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.INT_ADD_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.INT_ALOAD_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.INT_AND_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.INT_ASTORE_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.INT_COND_MOVE_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.INT_DIV_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.INT_IFCMP_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.INT_LOAD_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.INT_MOVE_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.INT_MUL_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.INT_NEG_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.INT_OR_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.INT_REM_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.INT_SHL_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.INT_SHR_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.INT_STORE_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.INT_SUB_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.INT_USHR_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.INT_XOR_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.INT_ZERO_CHECK_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.IR_PROLOGUE_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.LONG_ALOAD_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.LONG_ASTORE_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.LONG_LOAD_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.LONG_STORE_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.MONITORENTER_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.MONITOREXIT_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.MUST_IMPLEMENT_INTERFACE_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.NEWARRAY_UNRESOLVED_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.NEWARRAY_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.NEWOBJMULTIARRAY_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.NEW_UNRESOLVED_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.NEW_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.NULL_CHECK_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.OBJARRAY_STORE_CHECK_NOTNULL_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.OBJARRAY_STORE_CHECK_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.PHI_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.PREPARE_ADDR_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.PREPARE_INT_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.PREPARE_LONG_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.PUTFIELD_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.PUTSTATIC_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.REF_ADD_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.REF_ALOAD_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.REF_AND_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.REF_ASTORE_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.REF_COND_MOVE_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.REF_IFCMP_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.REF_LOAD_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.REF_MOVE_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.REF_OR_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.REF_SHL_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.REF_SHR_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.REF_STORE_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.REF_SUB_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.REF_USHR_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.REF_XOR_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.RETURN_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.SET_CAUGHT_EXCEPTION_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.SHORT_ALOAD_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.SHORT_ASTORE_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.SHORT_LOAD_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.SHORT_STORE_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.SYSCALL_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.UBYTE_ALOAD_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.UBYTE_LOAD_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.USHORT_ALOAD_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.USHORT_LOAD_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.YIELDPOINT_OSR_opcode; import org.jikesrvm.compilers.opt.ir.Register; import org.jikesrvm.compilers.opt.ir.PutField; import org.jikesrvm.compilers.opt.ir.PutStatic; import org.jikesrvm.compilers.opt.ir.ResultCarrier; import org.jikesrvm.compilers.opt.ir.Return; import org.jikesrvm.compilers.opt.ir.Store; import org.jikesrvm.compilers.opt.ir.operand.MethodOperand; import org.jikesrvm.compilers.opt.ir.operand.Operand; import org.jikesrvm.compilers.opt.ir.operand.RegisterOperand; /** * Simple flow-insensitive escape analysis * * <p> TODO: This would be more effective if formulated as a data-flow * problem, and solved with iteration */ class SimpleEscape extends CompilerPhase { /** * Return this instance of this phase. This phase contains no * per-compilation instance fields. * @param ir not used * @return this */ public CompilerPhase newExecution(IR ir) { return this; } public final boolean shouldPerform(OptOptions options) { return options.ESCAPE_SIMPLE_IPA; } public final String getName() { return "Simple Escape Analysis"; } public final boolean printingEnabled(OptOptions options, boolean before) { return false; } public void perform(IR ir) { SimpleEscape analyzer = new SimpleEscape(); analyzer.simpleEscapeAnalysis(ir); } /** * Perform the escape analysis for a method. Returns an * object holding the result of the analysis * * <p> Side effect: updates method summary database to hold * escape analysis result for parameters * * @param ir IR for the target method */ public FI_EscapeSummary simpleEscapeAnalysis(IR ir) { final boolean DEBUG = false; if (DEBUG) { VM.sysWrite("ENTER Simple Escape Analysis " + ir.method + "\n"); } if (DEBUG) { ir.printInstructions(); } // create a method summary object for this method RVMMethod m = ir.method; MethodSummary summ = SummaryDatabase.findOrCreateMethodSummary(m); summ.setInProgress(true); FI_EscapeSummary result = new FI_EscapeSummary(); // set up register lists, SSA flags DefUse.computeDU(ir); DefUse.recomputeSSA(ir); // pass through registers, and mark escape information for (Register reg = ir.regpool.getFirstSymbolicRegister(); reg != null; reg = reg.getNext()) { // skip the following types of registers: if (reg.isFloatingPoint()) { continue; } if (reg.isInteger()) { continue; } if (reg.isLong()) { continue; } if (reg.isCondition()) { continue; } if (reg.isValidation()) { continue; } if (reg.isPhysical()) { continue; } if (!reg.isSSA()) { continue; } AnalysisResult escapes = checkAllAppearances(reg, ir); if (escapes.threadLocal) { result.setThreadLocal(reg, true); } if (escapes.methodLocal) { result.setMethodLocal(reg, true); } } // update the method summary database to note whether // parameters may escape int numParam = 0; for (OperandEnumeration e = ir.getParameters(); e.hasMoreElements(); numParam++) { Register p = ((RegisterOperand) e.next()).getRegister(); if (result.isThreadLocal(p)) { summ.setParameterMayEscapeThread(numParam, false); } else { summ.setParameterMayEscapeThread(numParam, true); } } // update the method summary to note whether the return value // may escape boolean foundEscapingReturn = false; for (Iterator<Operand> itr = iterateReturnValues(ir); itr.hasNext();) { Operand op = itr.next(); if (op == null) { continue; } if (op.isRegister()) { Register r = op.asRegister().getRegister(); if (!result.isThreadLocal(r)) { foundEscapingReturn = true; } } } if (!foundEscapingReturn) { summ.setResultMayEscapeThread(false); } // record that we're done with analysis summ.setInProgress(false); if (DEBUG) { VM.sysWrite("LEAVE Simple Escape Analysis " + ir.method + "\n"); } return result; } /** * This member represents the directions to the optimizing compiler to * perform escape analysis on a method, but do <em> not </em> generate * code. */ private static final OptimizationPlanElement escapePlan = initEscapePlan(); /** * Check all appearances of a register, to see if any object pointed * to by this register may escape this thread and/or method. * * @param reg the register to check * @param ir the governing IR * @return true if it may escape this thread, false otherwise */ private static AnalysisResult checkAllAppearances(Register reg, IR ir) { return new AnalysisResult(!checkIfUseEscapesThread(reg, ir, null), !checkIfUseEscapesMethod(reg, ir, null)); } private static boolean checkIfUseEscapesThread(Register reg, IR ir, Set<Register> visited) { for (RegisterOperand use = reg.useList; use != null; use = use.getNext()) { if (VM.VerifyAssertions && use.getType() == null) { ir.printInstructions(); VM._assert(false, "type of " + use + " is null"); } // if the type is primitive, just say it escapes // TODO: handle this more cleanly if (use.getType().isPrimitiveType()) { return true; } if (checkEscapesThread(use, ir, visited)) { return true; } } for (RegisterOperand def = reg.defList; def != null; def = def.getNext()) { if (VM.VerifyAssertions && def.getType() == null) { ir.printInstructions(); VM._assert(false, "type of " + def + " is null"); } // if the type is primitive, just say it escapes // TODO: handle this more cleanly if (def.getType() == null || def.getType().isPrimitiveType()) { return true; } if (checkEscapesThread(def, ir, visited)) { return true; } } return false; } private static boolean checkIfUseEscapesMethod(Register reg, IR ir, Set<Register> visited) { for (RegisterOperand use = reg.useList; use != null; use = use.getNext()) { if (VM.VerifyAssertions && use.getType() == null) { ir.printInstructions(); VM._assert(false, "type of " + use + " is null"); } // if the type is primitive, just say it escapes // TODO: handle this more cleanly if (use.getType().isPrimitiveType()) { return false; } if (checkEscapesMethod(use, ir, visited)) { return true; } } for (RegisterOperand def = reg.defList; def != null; def = def.getNext()) { if (VM.VerifyAssertions && def.getType() == null) { ir.printInstructions(); VM._assert(false, "type of " + def + " is null"); } // if the type is primitive, just say it escapes // TODO: handle this more cleanly if (def.getType() == null || def.getType().isPrimitiveType()) { return true; } if (checkEscapesMethod(def, ir, visited)) { return true; } } return false; } /** * Check a single use, to see if this use may cause the object * referenced to escape from this thread. * * @param use the use to check * @param ir the governing IR * @return true if it may escape, false otherwise */ private static boolean checkEscapesThread(RegisterOperand use, IR ir, Set<Register> visited) { Instruction inst = use.instruction; switch (inst.getOpcode()) { case INT_ASTORE_opcode: case LONG_ASTORE_opcode: case FLOAT_ASTORE_opcode: case DOUBLE_ASTORE_opcode: case BYTE_ASTORE_opcode: case SHORT_ASTORE_opcode: case REF_ASTORE_opcode: // as long as we don't store this operand elsewhere, all // is OK Operand value = AStore.getValue(inst); return value == use; case GETFIELD_opcode: case GETSTATIC_opcode: case INT_ALOAD_opcode: case LONG_ALOAD_opcode: case FLOAT_ALOAD_opcode: case DOUBLE_ALOAD_opcode: case BYTE_ALOAD_opcode: case UBYTE_ALOAD_opcode: case BYTE_LOAD_opcode: case UBYTE_LOAD_opcode: case SHORT_ALOAD_opcode: case USHORT_ALOAD_opcode: case SHORT_LOAD_opcode: case USHORT_LOAD_opcode: case REF_ALOAD_opcode: case INT_LOAD_opcode: case LONG_LOAD_opcode: case FLOAT_LOAD_opcode: case DOUBLE_LOAD_opcode: case REF_LOAD_opcode: // all is OK, unless we load this register from memory Operand result = ResultCarrier.getResult(inst); return result == use; case PUTFIELD_opcode: // as long as we don't store this operand elsewhere, all // is OK. TODO: add more smarts. value = PutField.getValue(inst); return value == use; case PUTSTATIC_opcode: // as long as we don't store this operand elsewhere, all // is OK. TODO: add more smarts. value = PutStatic.getValue(inst); return value == use; case BYTE_STORE_opcode: case SHORT_STORE_opcode: case REF_STORE_opcode: case INT_STORE_opcode: case LONG_STORE_opcode: case FLOAT_STORE_opcode: case DOUBLE_STORE_opcode: // as long as we don't store this operand elsewhere, all // is OK. TODO: add more smarts. value = Store.getValue(inst); return value == use; // the following instructions never cause an object to // escape case BOUNDS_CHECK_opcode: case MONITORENTER_opcode: case MONITOREXIT_opcode: case NULL_CHECK_opcode: case ARRAYLENGTH_opcode: case REF_IFCMP_opcode: case INT_IFCMP_opcode: case IG_PATCH_POINT_opcode: case IG_CLASS_TEST_opcode: case IG_METHOD_TEST_opcode: case BOOLEAN_CMP_INT_opcode: case BOOLEAN_CMP_ADDR_opcode: case OBJARRAY_STORE_CHECK_opcode: case OBJARRAY_STORE_CHECK_NOTNULL_opcode: case GET_OBJ_TIB_opcode: case GET_TYPE_FROM_TIB_opcode: case NEW_opcode: case NEWARRAY_opcode: case NEWOBJMULTIARRAY_opcode: case NEW_UNRESOLVED_opcode: case NEWARRAY_UNRESOLVED_opcode: case INSTANCEOF_opcode: case INSTANCEOF_NOTNULL_opcode: case INSTANCEOF_UNRESOLVED_opcode: case CHECKCAST_opcode: case MUST_IMPLEMENT_INTERFACE_opcode: case CHECKCAST_NOTNULL_opcode: case CHECKCAST_UNRESOLVED_opcode: case GET_CAUGHT_EXCEPTION_opcode: case IR_PROLOGUE_opcode: return false; case RETURN_opcode: // a return instruction might cause an object to escape, // but not a parameter (whose escape properties are determined // by caller) return !ir.isParameter(use); case CALL_opcode: MethodOperand mop = Call.getMethod(inst); if (mop == null) { return true; } if (!mop.hasPreciseTarget()) { // if we're not sure of the dynamic target, give up return true; } // pure methods don't let object escape if (mop.getTarget().isPure()) { return false; } // Assume non-annotated native methods let object escape if (mop.getTarget().isNative()) { return false; } // try to get a method summary for the called method MethodSummary summ = findOrCreateMethodSummary(mop.getTarget(), ir.options); if (summ == null) { // couldn't get one. assume the object escapes return true; } // if use is result of the call... if (use == Call.getResult(inst)) { return summ.resultMayEscapeThread(); } // use is a parameter to the call. Find out which one. int p = getParameterIndex(use, inst); return summ.parameterMayEscapeThread(p); case REF_MOVE_opcode: { Register copy = Move.getResult(inst).getRegister(); if (!copy.isSSA()) { return true; } else { if (visited == null) { visited = new HashSet<Register>(); } visited.add(use.getRegister()); if (visited.contains(copy)) { return false; } else { return checkIfUseEscapesThread(copy, ir, visited); } } } case ATHROW_opcode: case PREPARE_INT_opcode: case PREPARE_ADDR_opcode: case PREPARE_LONG_opcode: case ATTEMPT_LONG_opcode: case ATTEMPT_INT_opcode: case ATTEMPT_ADDR_opcode: case INT_MOVE_opcode: case INT_ADD_opcode: case REF_ADD_opcode: case INT_MUL_opcode: case INT_DIV_opcode: case INT_REM_opcode: case INT_NEG_opcode: case INT_ZERO_CHECK_opcode: case INT_OR_opcode: case INT_AND_opcode: case INT_XOR_opcode: case REF_OR_opcode: case REF_AND_opcode: case REF_XOR_opcode: case INT_SUB_opcode: case REF_SUB_opcode: case INT_SHL_opcode: case INT_SHR_opcode: case INT_USHR_opcode: case SYSCALL_opcode: case REF_SHL_opcode: case REF_SHR_opcode: case REF_USHR_opcode: case SET_CAUGHT_EXCEPTION_opcode: case PHI_opcode: case INT_2LONG_opcode: case REF_COND_MOVE_opcode: case INT_COND_MOVE_opcode: case INT_2ADDRSigExt_opcode: case INT_2ADDRZerExt_opcode: case ADDR_2INT_opcode: case ADDR_2LONG_opcode: // we don't currently analyze these instructions, // so conservatively assume everything escapes // TODO: add more smarts case YIELDPOINT_OSR_opcode: // on stack replacement really a part of the current method, but // we do not know exactly, so be conservative return true; default: return Operators.helper.mayEscapeThread(inst); } } /** * Check a single use, to see if this use may cause the object * referenced to escape from this method. * * @param use the use to check * @param ir the governing IR * @return true if it may escape, false otherwise */ private static boolean checkEscapesMethod(RegisterOperand use, IR ir, Set<Register> visited) { Instruction inst = use.instruction; try { switch (inst.getOpcode()) { case INT_ASTORE_opcode: case LONG_ASTORE_opcode: case FLOAT_ASTORE_opcode: case DOUBLE_ASTORE_opcode: case BYTE_ASTORE_opcode: case SHORT_ASTORE_opcode: case REF_ASTORE_opcode: // as long as we don't store this operand elsewhere, all // is OK Operand value = AStore.getValue(inst); return value == use; case GETFIELD_opcode: case GETSTATIC_opcode: case INT_ALOAD_opcode: case LONG_ALOAD_opcode: case FLOAT_ALOAD_opcode: case DOUBLE_ALOAD_opcode: case BYTE_ALOAD_opcode: case UBYTE_ALOAD_opcode: case BYTE_LOAD_opcode: case UBYTE_LOAD_opcode: case USHORT_ALOAD_opcode: case SHORT_ALOAD_opcode: case USHORT_LOAD_opcode: case SHORT_LOAD_opcode: case REF_ALOAD_opcode: case INT_LOAD_opcode: case LONG_LOAD_opcode: case FLOAT_LOAD_opcode: case DOUBLE_LOAD_opcode: case REF_LOAD_opcode: // all is OK, unless we load this register from memory Operand result = ResultCarrier.getResult(inst); return result == use; case PUTFIELD_opcode: // as long as we don't store this operand elsewhere, all // is OK. TODO: add more smarts. value = PutField.getValue(inst); return value == use; case PUTSTATIC_opcode: // as long as we don't store this operand elsewhere, all // is OK. TODO: add more smarts. value = PutStatic.getValue(inst); return value == use; case BYTE_STORE_opcode: case SHORT_STORE_opcode: case REF_STORE_opcode: case INT_STORE_opcode: case LONG_STORE_opcode: case FLOAT_STORE_opcode: case DOUBLE_STORE_opcode: // as long as we don't store this operand elsewhere, all // is OK. TODO: add more smarts. value = Store.getValue(inst); return value == use; // the following instructions never cause an object to // escape case BOUNDS_CHECK_opcode: case MONITORENTER_opcode: case MONITOREXIT_opcode: case NULL_CHECK_opcode: case ARRAYLENGTH_opcode: case REF_IFCMP_opcode: case INT_IFCMP_opcode: case IG_PATCH_POINT_opcode: case IG_CLASS_TEST_opcode: case IG_METHOD_TEST_opcode: case BOOLEAN_CMP_INT_opcode: case BOOLEAN_CMP_ADDR_opcode: case OBJARRAY_STORE_CHECK_opcode: case OBJARRAY_STORE_CHECK_NOTNULL_opcode: case GET_OBJ_TIB_opcode: case GET_TYPE_FROM_TIB_opcode: case NEW_opcode: case NEWARRAY_opcode: case NEWOBJMULTIARRAY_opcode: case NEW_UNRESOLVED_opcode: case NEWARRAY_UNRESOLVED_opcode: case INSTANCEOF_opcode: case INSTANCEOF_NOTNULL_opcode: case INSTANCEOF_UNRESOLVED_opcode: case CHECKCAST_opcode: case MUST_IMPLEMENT_INTERFACE_opcode: case CHECKCAST_NOTNULL_opcode: case CHECKCAST_UNRESOLVED_opcode: case GET_CAUGHT_EXCEPTION_opcode: case IR_PROLOGUE_opcode: return false; case RETURN_opcode: // a return instruction causes an object to escape this method. return true; case CALL_opcode: { // A call instruction causes an object to escape this method // except when the target is to Throwable.<init> (which we never inline) MethodOperand mop = Call.getMethod(inst); if (mop != null && mop.hasPreciseTarget()) { RVMMethod target = mop.getTarget(); if (target.hasNoEscapesAnnotation()) { return false; } } return true; } case REF_MOVE_opcode: { if (visited == null) { visited = new HashSet<Register>(); } Register copy = Move.getResult(inst).getRegister(); if(!copy.isSSA()) { return true; } else { visited.add(use.getRegister()); if (visited.contains(copy)) { return false; } else { boolean result2 = checkIfUseEscapesMethod(copy, ir, visited); return result2; } } } case ATHROW_opcode: case PREPARE_INT_opcode: case PREPARE_ADDR_opcode: case ATTEMPT_INT_opcode: case ATTEMPT_ADDR_opcode: case PREPARE_LONG_opcode: case ATTEMPT_LONG_opcode: case INT_MOVE_opcode: case INT_ADD_opcode: case REF_ADD_opcode: case INT_MUL_opcode: case INT_DIV_opcode: case INT_REM_opcode: case INT_NEG_opcode: case INT_ZERO_CHECK_opcode: case INT_OR_opcode: case INT_AND_opcode: case INT_XOR_opcode: case REF_OR_opcode: case REF_AND_opcode: case REF_XOR_opcode: case INT_SUB_opcode: case REF_SUB_opcode: case INT_SHL_opcode: case INT_SHR_opcode: case INT_USHR_opcode: case SYSCALL_opcode: case REF_SHL_opcode: case REF_SHR_opcode: case REF_USHR_opcode: case SET_CAUGHT_EXCEPTION_opcode: case PHI_opcode: case INT_2LONG_opcode: case REF_COND_MOVE_opcode: case INT_COND_MOVE_opcode: case INT_2ADDRSigExt_opcode: case INT_2ADDRZerExt_opcode: case ADDR_2INT_opcode: case ADDR_2LONG_opcode: case YIELDPOINT_OSR_opcode: // we don't currently analyze these instructions, // so conservatively assume everything escapes // TODO: add more smarts return true; default: return Operators.helper.mayEscapeMethod(inst); } } catch (Exception e) { OptimizingCompilerException oe = new OptimizingCompilerException("Error handling use ("+ use +") of: "+ inst); oe.initCause(e); throw oe; } } /** * Which parameter to a call instruction corresponds to op? * <p> PRECONDITION: Call.conforms(s) */ private static int getParameterIndex(Operand op, Instruction s) { for (int i = 0; i < Call.getNumberOfParams(s); i++) { Operand p = Call.getParam(s, i); if (p == op) { return i; } } throw new OptimizingCompilerException("Parameter not found" + op + s); } /** * If a method summary exists for a method, get it. * Else, iff SIMPLE_ESCAPE_IPA, * perform escape analysis, which will create the method * summary as a side effect, and return the summary */ private static MethodSummary findOrCreateMethodSummary(RVMMethod m, OptOptions options) { MethodSummary summ = SummaryDatabase.findMethodSummary(m); if (summ == null) { if (options.ESCAPE_SIMPLE_IPA) { performSimpleEscapeAnalysis(m, options); summ = SummaryDatabase.findMethodSummary(m); } return summ; } else { return summ; } } /** * Perform the simple escape analysis for a method. */ private static void performSimpleEscapeAnalysis(RVMMethod m, OptOptions options) { if (!options.ESCAPE_SIMPLE_IPA) { return; } // do not perform for unloaded methods MethodSummary summ = SummaryDatabase.findMethodSummary(m); if (summ != null) { // do not attempt to perform escape analysis recursively if (summ.inProgress()) { return; } } CompilationPlan plan = new CompilationPlan((NormalMethod) m, escapePlan, null, options); plan.analyzeOnly = true; try { OptimizingCompiler.compile(plan); } catch (MagicNotImplementedException e) { summ.setInProgress(false); // summary stays at bottom } } /** * Static initializer: set up the compilation plan for * simple escape analysis of a method. */ private static OptimizationPlanElement initEscapePlan() { return OptimizationPlanCompositeElement.compose("Escape Analysis", new Object[]{new ConvertBCtoHIR(), new Simple(1, true, true, false, false), new SimpleEscape()}); } /** * Return an iterator over the operands that serve as return values * in an IR * * <p> TODO: Move this utility elsewhere */ private static Iterator<Operand> iterateReturnValues(IR ir) { ArrayList<Operand> returnValues = new ArrayList<Operand>(); for (InstructionEnumeration e = ir.forwardInstrEnumerator(); e.hasMoreElements();) { Instruction s = e.next(); if (Return.conforms(s)) { returnValues.add(Return.getVal(s)); } } return returnValues.iterator(); } /** * Utility class used to hold the result of the escape analysis. */ private static final class AnalysisResult { /** * Was the result "the register must point to thread-local objects"? */ final boolean threadLocal; /** * Was the result "the register must point to method-local objects"? */ final boolean methodLocal; /** * Constructor */ AnalysisResult(boolean tl, boolean ml) { threadLocal = tl; methodLocal = ml; } } }