void UsedVarsFixpointIterator::analyze_instruction( const IRInstruction* insn, UsedVarsSet* used_vars) const { TRACE(DEAD_CODE, 5, "Before %s : %s : %s\n", SHOW(insn), SHOW(*used_vars), show_subset(m_insn_env_map.at(insn), insn).c_str()); bool required = is_required(insn, *used_vars); auto op = insn->opcode(); if (ptrs::is_alloc_opcode(op)) { used_vars->remove(insn); } if (insn->dests_size()) { used_vars->remove(insn->dest()); } else if (insn->has_move_result()) { used_vars->remove(RESULT_REGISTER); } if (required) { const auto& env = m_insn_env_map.at(insn); if (env.is_bottom()) { return; } for (auto reg : object_read_registers(insn)) { auto pointers = env.get_pointers(reg); // XXX: We should never encounter this case since we explicitly bind all // potential pointer-containing registers to non-Top values in our // environment. If we did encounter Top here, however, we should treat // all local allocations as potentially used -- a read from // PointerSet::top() must be treated like a read from every possible // heap location. always_assert(!pointers.is_top()); for (auto pointer : pointers.elements()) { used_vars->add(pointer); } } for (size_t i = 0; i < insn->srcs_size(); ++i) { used_vars->add(insn->src(i)); } if (is_move_result(op) || opcode::is_move_result_pseudo(op)) { used_vars->add(RESULT_REGISTER); } } TRACE(DEAD_CODE, 5, "After: %s\n", SHOW(*used_vars)); }
bool MethodTransform::inline_16regs(InlineContext& context, DexMethod *callee, DexOpcodeMethod *invoke) { TRACE(INL, 2, "caller: %s\ncallee: %s\n", SHOW(context.caller), SHOW(callee)); auto caller = context.caller; MethodTransformer mtcaller(caller); MethodTransformer mtcallee(callee); auto fcaller = mtcaller->m_fmethod; auto fcallee = mtcallee->m_fmethod; auto callee_code = callee->get_code(); auto temps_needed = callee_code->get_registers_size() - callee_code->get_ins_size(); uint16_t newregs = caller->get_code()->get_registers_size(); if (context.inline_regs_used < temps_needed) { newregs = newregs + temps_needed - context.inline_regs_used; if (newregs > 16) return false; remap_caller_regs(caller, fcaller, newregs); context.inline_regs_used = temps_needed; } RegMap callee_reg_map; build_remap_regs(callee_reg_map, invoke, callee, context.new_tmp_off); auto pos = std::find_if( fcaller->begin(), fcaller->end(), [invoke](const MethodItemEntry& mei) { return mei.type == MFLOW_OPCODE && mei.insn == invoke; }); // find the move-result after the invoke, if any. Must be the first // instruction after the invoke auto move_res = pos; while (move_res++ != fcaller->end() && move_res->type != MFLOW_OPCODE); if (!is_move_result(move_res->insn->opcode())) { move_res = fcaller->end(); } // Skip dbg prologue in callee, we don't need two. auto it = fcallee->begin(); if (it->type == MFLOW_DEBUG && it->dbgop->opcode() == DBG_SET_PROLOGUE_END) { ++it; } // find the last position entry before the invoke. // we need to decrement the reverse iterator because it gets constructed // as pointing to the element preceding pos auto position_it = --FatMethod::reverse_iterator(pos); while (++position_it != fcaller->rend() && position_it->type != MFLOW_POSITION); auto invoke_position = position_it == fcaller->rend() ? nullptr : position_it->pos; if (invoke_position != nullptr) { TRACE(MTRANS, 5, "Inlining call at %s:%d\n", invoke_position->file->c_str(), invoke_position->line); } // Copy the callee up to the return. Everything else we push at the end // of the caller auto splice = MethodSplicer(callee_reg_map, invoke_position); auto ret_it = std::find_if(it, fcallee->end(), [](const MethodItemEntry& mei) { return mei.type == MFLOW_OPCODE && is_return(mei.insn->opcode()); }); splice(fcaller, pos, it, ret_it); if (move_res != fcaller->end()) { std::unique_ptr<DexInstruction> ret_insn(ret_it->insn->clone()); remap_registers(ret_insn.get(), callee_reg_map); DexInstruction* move = move_result(ret_insn.get(), move_res->insn); auto move_mei = new MethodItemEntry(move); fcaller->insert(pos, *move_mei); } // ensure that the caller's code after the inlined method retain their // original position if (invoke_position != nullptr) { fcaller->insert(pos, *(new MethodItemEntry(invoke_position))); } // remove invoke fcaller->erase_and_dispose(pos, FatMethodDisposer()); // remove move_result if (move_res != fcaller->end()) { fcaller->erase_and_dispose(move_res, FatMethodDisposer()); } // Copy the opcodes in the callee after the return and put them at the end of // the caller. splice(fcaller, fcaller->end(), std::next(ret_it), fcallee->end()); // adjust method header caller->get_code()->set_registers_size(newregs); caller->get_code()->set_outs_size( std::max(callee->get_code()->get_outs_size(), caller->get_code()->get_outs_size())); return true; }