TEST(IRInstruction, SelectCheckCast) { using namespace dex_asm; g_redex = new RedexContext(); DexMethod* method = static_cast<DexMethod*>(DexMethod::make_method("Lfoo;", "bar", "V", {})); method->make_concrete(ACC_STATIC, 0); method->set_code(std::make_unique<IRCode>(method, 0)); auto code = method->get_code(); code->push_back(dasm(OPCODE_CHECK_CAST, get_object_type(), {1_v})); code->push_back(dasm(IOPCODE_MOVE_RESULT_PSEUDO_OBJECT, {0_v})); instruction_lowering::lower(method); // check that we inserted a move opcode before the check-cast auto it = code->begin(); EXPECT_EQ( *it->dex_insn, *(new DexInstruction(DOPCODE_MOVE_OBJECT))->set_dest(0)->set_src(0, 1)); ++it; EXPECT_EQ(*it->dex_insn, *(new DexOpcodeType(DOPCODE_CHECK_CAST, get_object_type())) ->set_src(0, 0)); delete g_redex; }
DexMethod* get_fresh_method(const std::string& name) { DexMethod* method = static_cast<DexMethod*>(DexMethod::make_method( m_type, DexString::make_string(name), m_proto)); method->make_concrete(ACC_PUBLIC | ACC_STATIC, false); method->set_code(std::make_unique<IRCode>(method, 1)); m_creator->add_method(method); return method; }
// Performs one peephole test. Applies peephole optimizations to the given // source instruction stream, and checks that it equals the expected result void test_1(const std::string& name, const IRInstructionList& src, const IRInstructionList& expected) { DexMethod* method = make_void_method(name.c_str(), src); dex_class->add_method(method); manager.run_passes(stores, config); IRInstructionList result(method->get_code()); EXPECT_EQ(result, expected) << " for test " << name; dex_class->remove_method(method); }
/* * Helper function to run select and then extract the resulting instruction * from the instruction list. The only reason it's a list is that const-cast * IRInstructions can expand into two instructions due to select. Everything * else is a simple one-to-one instruction mapping, and that's the case that * this makes easy to test. */ IRInstruction* select_instruction(IRInstruction* insn) { DexMethod* method = static_cast<DexMethod*>(DexMethod::make_method("Lfoo;", "bar", "V", {})); method->make_concrete(ACC_STATIC, 0); method->set_code(std::make_unique<IRCode>(method, 0)); auto code = method->get_code(); code->push_back(insn); instruction_lowering::lower(method); return code->begin()->insn; }
/* Given a list of class/method pairs, finds all non-abstract candidates to be made abstract, then mutates them. */ void make_methods_abstract(std::vector<DexClass*>& classes, CMethodStrs cMethodStrs) { CMethods methods; std::cout << "Marking methods to make abstract..." << std::endl; int64_t markedForAbstraction = 0; mark_methods(classes, cMethodStrs, methods, markedForAbstraction); int64_t madeAbstract = 0; for (auto const& cm : methods) { DexMethod* m = cm.second; std::string descriptor = gen_method_desc(cm); if (is_abstract(m)) { std::cout << descriptor << " is already abstract" << std::endl; } else { DexAccessFlags original_access = m->get_access(); DexMethodSpec ref(m->get_class(), m->get_name(), m->get_proto()); m->change(ref, false); if (m->is_def()) { std::cout << "Making " << descriptor << " abstract" << std::endl; m->set_access(original_access | ACC_ABSTRACT); } else { std::cout << descriptor << " has false is_def()" << std::endl; } madeAbstract++; } } std::cout << madeAbstract << " methods made abstract (from " << markedForAbstraction << " marked)." << std::endl; }
void EquivalenceTest::generate(DexClass* cls) { setup(cls); auto ret = DexType::make_type("I"); auto args = DexTypeList::make_type_list({}); auto proto = DexProto::make_proto(ret, args); // I() DexMethod* before = static_cast<DexMethod*>(DexMethod::make_method( cls->get_type(), DexString::make_string("before_" + test_name()), proto)); before->make_concrete(ACC_PUBLIC | ACC_STATIC, false); before->set_code(std::make_unique<IRCode>(before, 0)); build_method(before); cls->add_method(before); auto after = DexMethod::make_method_from( before, cls->get_type(), DexString::make_string("after_" + test_name())); cls->add_method(after); transform_method(after); }
// add a void->void static method to our dex_class DexMethod* make_void_method(const char* method_name, const IRInstructionList& insns) const { auto ret = get_void_type(); auto args = DexTypeList::make_type_list({}); auto proto = DexProto::make_proto(ret, args); // I() DexMethod* method = static_cast<DexMethod*>(DexMethod::make_method( dex_class->get_type(), DexString::make_string(method_name), proto)); method->make_concrete(ACC_PUBLIC | ACC_STATIC, false); // FIXME we should determine the actual number of temp regs used from // the IRInstructionList method->set_code(std::make_unique<IRCode>(method, 0)); // import our instructions auto mt = method->get_code(); for (const auto& insn_ptr : insns.instructions) { mt->push_back(new IRInstruction(*insn_ptr)); } return method; }
virtual void setup(DexClass* cls) { auto ret = DexType::make_type("I"); auto arg = DexType::make_type("I"); auto args = DexTypeList::make_type_list({arg, arg}); auto proto = DexProto::make_proto(ret, args); // I(I, I) m_callee = DexMethod::make_method(cls->get_type(), DexString::make_string("callee_" + test_name()), proto); m_callee->make_concrete( ACC_PUBLIC | ACC_STATIC, std::make_unique<DexCode>(), false); { using namespace dex_asm; MethodTransformer mt(m_callee); // note that this method will not behave the same way if v0 and v1 get // mapped to the same register mt->push_back(dasm(OPCODE_ADD_INT_2ADDR, {0_v, 1_v})); mt->push_back(dasm(OPCODE_ADD_INT_2ADDR, {1_v, 0_v})); mt->push_back(dasm(OPCODE_RETURN, {1_v})); m_callee->get_code()->set_registers_size(2); m_callee->get_code()->set_ins_size(2); } cls->add_method(m_callee); }
// in Code: A B E C D (where C == D) // in CFG: A -> B -> C -> E // \ / // > -- D > // // out Code: A B E C // out CFG: A -> B -> C -> E // \ / // > --- > TEST_F(DedupBlocksTest, simplestCase) { using namespace dex_asm; DexMethod* method = get_fresh_method("simplestCase"); auto A_if_D = create_branch(OPCODE_IF_EQZ); auto B_goto_C = create_branch(OPCODE_GOTO); auto C_goto_E = create_branch(OPCODE_GOTO); auto D_goto_E = create_branch(OPCODE_GOTO); auto code = method->get_code(); ASSERT_NE(code, nullptr); // A code->push_back(dasm(OPCODE_CONST, {0_v, 0_L})); code->push_back(dasm(OPCODE_MUL_INT, {0_v, 0_v, 0_v})); code->push_back(*A_if_D.source); // B code->push_back(dasm(OPCODE_MUL_INT, {0_v, 0_v, 0_v})); code->push_back(*B_goto_C.source); // E code->push_back(*C_goto_E.target); code->push_back(*D_goto_E.target); code->push_back(dasm(OPCODE_RETURN_VOID)); // C code->push_back(*B_goto_C.target); code->push_back(dasm(OPCODE_ADD_INT, {0_v, 0_v, 0_v})); code->push_back(*C_goto_E.source); // D code->push_back(*A_if_D.target); code->push_back(dasm(OPCODE_ADD_INT, {0_v, 0_v, 0_v})); code->push_back(*D_goto_E.source); code->build_cfg(true); EXPECT_EQ(5, code->cfg().blocks().size()); printf("Input cfg:\n%s\n", SHOW(code->cfg())); code->clear_cfg(); run_dedup_blocks(); code->build_cfg(true); printf("Result cfg:\n%s\n", SHOW(code->cfg())); EXPECT_EQ(4, code->cfg().blocks().size()); code->clear_cfg(); auto mie = code->begin(); // A EXPECT_EQ(OPCODE_CONST, mie->insn->opcode()); ++mie; EXPECT_EQ(OPCODE_MUL_INT, mie->insn->opcode()); ++mie; EXPECT_EQ(OPCODE_IF_EQZ, mie->insn->opcode()); auto a_c = mie; ++mie; // B EXPECT_EQ(OPCODE_MUL_INT, mie->insn->opcode()); ++mie; EXPECT_EQ(OPCODE_GOTO, mie->insn->opcode()); auto b_c = mie; ++mie; // E EXPECT_EQ(MFLOW_TARGET, mie->type); auto c_e = mie->target->src; ++mie; EXPECT_EQ(OPCODE_RETURN_VOID, mie->insn->opcode()); ++mie; // C EXPECT_EQ(MFLOW_TARGET, mie->type); EXPECT_TRUE(mie->target->src == &*a_c || mie->target->src == &*b_c); ++mie; EXPECT_EQ(MFLOW_TARGET, mie->type); EXPECT_TRUE(mie->target->src == &*a_c || mie->target->src == &*b_c); ++mie; EXPECT_EQ(OPCODE_ADD_INT, mie->insn->opcode()); ++mie; EXPECT_EQ(OPCODE_GOTO, mie->insn->opcode()); EXPECT_EQ(c_e, &*mie); ++mie; // no D! EXPECT_EQ(code->end(), mie); }