| /* |
| * Copyright (C) 2015 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "dataflow_iterator-inl.h" |
| #include "dex/mir_field_info.h" |
| #include "global_value_numbering.h" |
| #include "gvn_dead_code_elimination.h" |
| #include "local_value_numbering.h" |
| #include "gtest/gtest.h" |
| |
| namespace art { |
| |
| class GvnDeadCodeEliminationTest : public testing::Test { |
| protected: |
| static constexpr uint16_t kNoValue = GlobalValueNumbering::kNoValue; |
| |
| struct IFieldDef { |
| uint16_t field_idx; |
| uintptr_t declaring_dex_file; |
| uint16_t declaring_field_idx; |
| bool is_volatile; |
| DexMemAccessType type; |
| }; |
| |
| struct SFieldDef { |
| uint16_t field_idx; |
| uintptr_t declaring_dex_file; |
| uint16_t declaring_field_idx; |
| bool is_volatile; |
| DexMemAccessType type; |
| }; |
| |
| struct BBDef { |
| static constexpr size_t kMaxSuccessors = 4; |
| static constexpr size_t kMaxPredecessors = 4; |
| |
| BBType type; |
| size_t num_successors; |
| BasicBlockId successors[kMaxPredecessors]; |
| size_t num_predecessors; |
| BasicBlockId predecessors[kMaxPredecessors]; |
| }; |
| |
| struct MIRDef { |
| static constexpr size_t kMaxSsaDefs = 2; |
| static constexpr size_t kMaxSsaUses = 4; |
| |
| BasicBlockId bbid; |
| Instruction::Code opcode; |
| int64_t value; |
| uint32_t field_info; |
| size_t num_uses; |
| int32_t uses[kMaxSsaUses]; |
| size_t num_defs; |
| int32_t defs[kMaxSsaDefs]; |
| }; |
| |
| #define DEF_SUCC0() \ |
| 0u, { } |
| #define DEF_SUCC1(s1) \ |
| 1u, { s1 } |
| #define DEF_SUCC2(s1, s2) \ |
| 2u, { s1, s2 } |
| #define DEF_SUCC3(s1, s2, s3) \ |
| 3u, { s1, s2, s3 } |
| #define DEF_SUCC4(s1, s2, s3, s4) \ |
| 4u, { s1, s2, s3, s4 } |
| #define DEF_PRED0() \ |
| 0u, { } |
| #define DEF_PRED1(p1) \ |
| 1u, { p1 } |
| #define DEF_PRED2(p1, p2) \ |
| 2u, { p1, p2 } |
| #define DEF_PRED3(p1, p2, p3) \ |
| 3u, { p1, p2, p3 } |
| #define DEF_PRED4(p1, p2, p3, p4) \ |
| 4u, { p1, p2, p3, p4 } |
| #define DEF_BB(type, succ, pred) \ |
| { type, succ, pred } |
| |
| #define DEF_CONST(bb, opcode, reg, value) \ |
| { bb, opcode, value, 0u, 0, { }, 1, { reg } } |
| #define DEF_CONST_WIDE(bb, opcode, reg, value) \ |
| { bb, opcode, value, 0u, 0, { }, 2, { reg, reg + 1 } } |
| #define DEF_CONST_STRING(bb, opcode, reg, index) \ |
| { bb, opcode, index, 0u, 0, { }, 1, { reg } } |
| #define DEF_IGET(bb, opcode, reg, obj, field_info) \ |
| { bb, opcode, 0u, field_info, 1, { obj }, 1, { reg } } |
| #define DEF_IGET_WIDE(bb, opcode, reg, obj, field_info) \ |
| { bb, opcode, 0u, field_info, 1, { obj }, 2, { reg, reg + 1 } } |
| #define DEF_IPUT(bb, opcode, reg, obj, field_info) \ |
| { bb, opcode, 0u, field_info, 2, { reg, obj }, 0, { } } |
| #define DEF_IPUT_WIDE(bb, opcode, reg, obj, field_info) \ |
| { bb, opcode, 0u, field_info, 3, { reg, reg + 1, obj }, 0, { } } |
| #define DEF_SGET(bb, opcode, reg, field_info) \ |
| { bb, opcode, 0u, field_info, 0, { }, 1, { reg } } |
| #define DEF_SGET_WIDE(bb, opcode, reg, field_info) \ |
| { bb, opcode, 0u, field_info, 0, { }, 2, { reg, reg + 1 } } |
| #define DEF_SPUT(bb, opcode, reg, field_info) \ |
| { bb, opcode, 0u, field_info, 1, { reg }, 0, { } } |
| #define DEF_SPUT_WIDE(bb, opcode, reg, field_info) \ |
| { bb, opcode, 0u, field_info, 2, { reg, reg + 1 }, 0, { } } |
| #define DEF_AGET(bb, opcode, reg, obj, idx) \ |
| { bb, opcode, 0u, 0u, 2, { obj, idx }, 1, { reg } } |
| #define DEF_AGET_WIDE(bb, opcode, reg, obj, idx) \ |
| { bb, opcode, 0u, 0u, 2, { obj, idx }, 2, { reg, reg + 1 } } |
| #define DEF_APUT(bb, opcode, reg, obj, idx) \ |
| { bb, opcode, 0u, 0u, 3, { reg, obj, idx }, 0, { } } |
| #define DEF_APUT_WIDE(bb, opcode, reg, obj, idx) \ |
| { bb, opcode, 0u, 0u, 4, { reg, reg + 1, obj, idx }, 0, { } } |
| #define DEF_INVOKE1(bb, opcode, reg) \ |
| { bb, opcode, 0u, 0u, 1, { reg }, 0, { } } |
| #define DEF_UNIQUE_REF(bb, opcode, reg) \ |
| { bb, opcode, 0u, 0u, 0, { }, 1, { reg } } // CONST_CLASS, CONST_STRING, NEW_ARRAY, ... |
| #define DEF_IFZ(bb, opcode, reg) \ |
| { bb, opcode, 0u, 0u, 1, { reg }, 0, { } } |
| #define DEF_MOVE(bb, opcode, reg, src) \ |
| { bb, opcode, 0u, 0u, 1, { src }, 1, { reg } } |
| #define DEF_MOVE_WIDE(bb, opcode, reg, src) \ |
| { bb, opcode, 0u, 0u, 2, { src, src + 1 }, 2, { reg, reg + 1 } } |
| #define DEF_PHI2(bb, reg, src1, src2) \ |
| { bb, static_cast<Instruction::Code>(kMirOpPhi), 0, 0u, 2u, { src1, src2 }, 1, { reg } } |
| #define DEF_UNOP(bb, opcode, result, src1) \ |
| { bb, opcode, 0u, 0u, 1, { src1 }, 1, { result } } |
| #define DEF_BINOP(bb, opcode, result, src1, src2) \ |
| { bb, opcode, 0u, 0u, 2, { src1, src2 }, 1, { result } } |
| |
| void DoPrepareIFields(const IFieldDef* defs, size_t count) { |
| cu_.mir_graph->ifield_lowering_infos_.clear(); |
| cu_.mir_graph->ifield_lowering_infos_.reserve(count); |
| for (size_t i = 0u; i != count; ++i) { |
| const IFieldDef* def = &defs[i]; |
| MirIFieldLoweringInfo field_info(def->field_idx, def->type, false); |
| if (def->declaring_dex_file != 0u) { |
| field_info.declaring_dex_file_ = reinterpret_cast<const DexFile*>(def->declaring_dex_file); |
| field_info.declaring_field_idx_ = def->declaring_field_idx; |
| field_info.flags_ = |
| MirIFieldLoweringInfo::kFlagFastGet | MirIFieldLoweringInfo::kFlagFastPut | |
| (field_info.flags_ & ~(def->is_volatile ? 0u : MirIFieldLoweringInfo::kFlagIsVolatile)); |
| } |
| cu_.mir_graph->ifield_lowering_infos_.push_back(field_info); |
| } |
| } |
| |
| template <size_t count> |
| void PrepareIFields(const IFieldDef (&defs)[count]) { |
| DoPrepareIFields(defs, count); |
| } |
| |
| void DoPrepareSFields(const SFieldDef* defs, size_t count) { |
| cu_.mir_graph->sfield_lowering_infos_.clear(); |
| cu_.mir_graph->sfield_lowering_infos_.reserve(count); |
| for (size_t i = 0u; i != count; ++i) { |
| const SFieldDef* def = &defs[i]; |
| MirSFieldLoweringInfo field_info(def->field_idx, def->type); |
| // Mark even unresolved fields as initialized. |
| field_info.flags_ |= MirSFieldLoweringInfo::kFlagClassIsInitialized; |
| // NOTE: MirSFieldLoweringInfo::kFlagClassIsInDexCache isn't used by GVN. |
| if (def->declaring_dex_file != 0u) { |
| field_info.declaring_dex_file_ = reinterpret_cast<const DexFile*>(def->declaring_dex_file); |
| field_info.declaring_field_idx_ = def->declaring_field_idx; |
| field_info.flags_ = |
| MirSFieldLoweringInfo::kFlagFastGet | MirSFieldLoweringInfo::kFlagFastPut | |
| (field_info.flags_ & ~(def->is_volatile ? 0u : MirSFieldLoweringInfo::kFlagIsVolatile)); |
| } |
| cu_.mir_graph->sfield_lowering_infos_.push_back(field_info); |
| } |
| } |
| |
| template <size_t count> |
| void PrepareSFields(const SFieldDef (&defs)[count]) { |
| DoPrepareSFields(defs, count); |
| } |
| |
| void DoPrepareBasicBlocks(const BBDef* defs, size_t count) { |
| cu_.mir_graph->block_id_map_.clear(); |
| cu_.mir_graph->block_list_.clear(); |
| ASSERT_LT(3u, count); // null, entry, exit and at least one bytecode block. |
| ASSERT_EQ(kNullBlock, defs[0].type); |
| ASSERT_EQ(kEntryBlock, defs[1].type); |
| ASSERT_EQ(kExitBlock, defs[2].type); |
| for (size_t i = 0u; i != count; ++i) { |
| const BBDef* def = &defs[i]; |
| BasicBlock* bb = cu_.mir_graph->CreateNewBB(def->type); |
| if (def->num_successors <= 2) { |
| bb->successor_block_list_type = kNotUsed; |
| bb->fall_through = (def->num_successors >= 1) ? def->successors[0] : 0u; |
| bb->taken = (def->num_successors >= 2) ? def->successors[1] : 0u; |
| } else { |
| bb->successor_block_list_type = kPackedSwitch; |
| bb->fall_through = 0u; |
| bb->taken = 0u; |
| bb->successor_blocks.reserve(def->num_successors); |
| for (size_t j = 0u; j != def->num_successors; ++j) { |
| SuccessorBlockInfo* successor_block_info = |
| static_cast<SuccessorBlockInfo*>(cu_.arena.Alloc(sizeof(SuccessorBlockInfo), |
| kArenaAllocSuccessor)); |
| successor_block_info->block = j; |
| successor_block_info->key = 0u; // Not used by class init check elimination. |
| bb->successor_blocks.push_back(successor_block_info); |
| } |
| } |
| bb->predecessors.assign(def->predecessors, def->predecessors + def->num_predecessors); |
| if (def->type == kDalvikByteCode || def->type == kEntryBlock || def->type == kExitBlock) { |
| bb->data_flow_info = static_cast<BasicBlockDataFlow*>( |
| cu_.arena.Alloc(sizeof(BasicBlockDataFlow), kArenaAllocDFInfo)); |
| bb->data_flow_info->live_in_v = live_in_v_; |
| bb->data_flow_info->vreg_to_ssa_map_exit = nullptr; |
| } |
| } |
| ASSERT_EQ(count, cu_.mir_graph->block_list_.size()); |
| cu_.mir_graph->entry_block_ = cu_.mir_graph->block_list_[1]; |
| ASSERT_EQ(kEntryBlock, cu_.mir_graph->entry_block_->block_type); |
| cu_.mir_graph->exit_block_ = cu_.mir_graph->block_list_[2]; |
| ASSERT_EQ(kExitBlock, cu_.mir_graph->exit_block_->block_type); |
| } |
| |
| template <size_t count> |
| void PrepareBasicBlocks(const BBDef (&defs)[count]) { |
| DoPrepareBasicBlocks(defs, count); |
| } |
| |
| int SRegToVReg(int32_t s_reg, bool wide) { |
| int v_reg = cu_.mir_graph->SRegToVReg(s_reg); |
| CHECK_LT(static_cast<size_t>(v_reg), num_vregs_); |
| if (wide) { |
| CHECK_LT(static_cast<size_t>(v_reg + 1), num_vregs_); |
| } |
| return v_reg; |
| } |
| |
| int SRegToVReg(int32_t* uses, size_t* use, bool wide) { |
| int v_reg = SRegToVReg(uses[*use], wide); |
| if (wide) { |
| CHECK_EQ(uses[*use] + 1, uses[*use + 1]); |
| *use += 2u; |
| } else { |
| *use += 1u; |
| } |
| return v_reg; |
| } |
| |
| void DoPrepareMIRs(const MIRDef* defs, size_t count) { |
| mir_count_ = count; |
| mirs_ = reinterpret_cast<MIR*>(cu_.arena.Alloc(sizeof(MIR) * count, kArenaAllocMIR)); |
| ssa_reps_.resize(count); |
| for (size_t i = 0u; i != count; ++i) { |
| const MIRDef* def = &defs[i]; |
| MIR* mir = &mirs_[i]; |
| ASSERT_LT(def->bbid, cu_.mir_graph->block_list_.size()); |
| BasicBlock* bb = cu_.mir_graph->block_list_[def->bbid]; |
| bb->AppendMIR(mir); |
| mir->dalvikInsn.opcode = def->opcode; |
| mir->dalvikInsn.vB = static_cast<int32_t>(def->value); |
| mir->dalvikInsn.vB_wide = def->value; |
| if (IsInstructionIGetOrIPut(def->opcode)) { |
| ASSERT_LT(def->field_info, cu_.mir_graph->ifield_lowering_infos_.size()); |
| mir->meta.ifield_lowering_info = def->field_info; |
| ASSERT_EQ(cu_.mir_graph->ifield_lowering_infos_[def->field_info].MemAccessType(), |
| IGetOrIPutMemAccessType(def->opcode)); |
| } else if (IsInstructionSGetOrSPut(def->opcode)) { |
| ASSERT_LT(def->field_info, cu_.mir_graph->sfield_lowering_infos_.size()); |
| mir->meta.sfield_lowering_info = def->field_info; |
| ASSERT_EQ(cu_.mir_graph->sfield_lowering_infos_[def->field_info].MemAccessType(), |
| SGetOrSPutMemAccessType(def->opcode)); |
| } else if (def->opcode == static_cast<Instruction::Code>(kMirOpPhi)) { |
| mir->meta.phi_incoming = |
| allocator_->AllocArray<BasicBlockId>(def->num_uses, kArenaAllocDFInfo); |
| ASSERT_EQ(def->num_uses, bb->predecessors.size()); |
| std::copy(bb->predecessors.begin(), bb->predecessors.end(), mir->meta.phi_incoming); |
| } |
| mir->ssa_rep = &ssa_reps_[i]; |
| cu_.mir_graph->AllocateSSAUseData(mir, def->num_uses); |
| std::copy_n(def->uses, def->num_uses, mir->ssa_rep->uses); |
| // Keep mir->ssa_rep->fp_use[.] zero-initialized (false). Not used by DCE, only copied. |
| cu_.mir_graph->AllocateSSADefData(mir, def->num_defs); |
| std::copy_n(def->defs, def->num_defs, mir->ssa_rep->defs); |
| // Keep mir->ssa_rep->fp_def[.] zero-initialized (false). Not used by DCE, only copied. |
| mir->dalvikInsn.opcode = def->opcode; |
| mir->offset = i; // LVN uses offset only for debug output |
| mir->optimization_flags = 0u; |
| uint64_t df_attrs = MIRGraph::GetDataFlowAttributes(mir); |
| if ((df_attrs & DF_DA) != 0) { |
| CHECK_NE(def->num_defs, 0u); |
| mir->dalvikInsn.vA = SRegToVReg(def->defs[0], (df_attrs & DF_A_WIDE) != 0); |
| bb->data_flow_info->vreg_to_ssa_map_exit[mir->dalvikInsn.vA] = def->defs[0]; |
| if ((df_attrs & DF_A_WIDE) != 0) { |
| CHECK_EQ(def->defs[0] + 1, def->defs[1]); |
| bb->data_flow_info->vreg_to_ssa_map_exit[mir->dalvikInsn.vA + 1u] = def->defs[0] + 1; |
| } |
| } |
| if ((df_attrs & (DF_UA | DF_UB | DF_UC)) != 0) { |
| size_t use = 0; |
| if ((df_attrs & DF_UA) != 0) { |
| mir->dalvikInsn.vA = SRegToVReg(mir->ssa_rep->uses, &use, (df_attrs & DF_A_WIDE) != 0); |
| } |
| if ((df_attrs & DF_UB) != 0) { |
| mir->dalvikInsn.vB = SRegToVReg(mir->ssa_rep->uses, &use, (df_attrs & DF_B_WIDE) != 0); |
| } |
| if ((df_attrs & DF_UC) != 0) { |
| mir->dalvikInsn.vC = SRegToVReg(mir->ssa_rep->uses, &use, (df_attrs & DF_C_WIDE) != 0); |
| } |
| DCHECK_EQ(def->num_uses, use); |
| } |
| } |
| DexFile::CodeItem* code_item = static_cast<DexFile::CodeItem*>( |
| cu_.arena.Alloc(sizeof(DexFile::CodeItem), kArenaAllocMisc)); |
| code_item->insns_size_in_code_units_ = 2u * count; |
| code_item->registers_size_ = kMaxVRegs; |
| cu_.mir_graph->current_code_item_ = code_item; |
| } |
| |
| template <size_t count> |
| void PrepareMIRs(const MIRDef (&defs)[count]) { |
| DoPrepareMIRs(defs, count); |
| } |
| |
| template <size_t count> |
| void PrepareSRegToVRegMap(const int (&map)[count]) { |
| cu_.mir_graph->ssa_base_vregs_.assign(map, map + count); |
| num_vregs_ = *std::max_element(map, map + count) + 1u; |
| AllNodesIterator iterator(cu_.mir_graph.get()); |
| for (BasicBlock* bb = iterator.Next(); bb != nullptr; bb = iterator.Next()) { |
| if (bb->data_flow_info != nullptr) { |
| bb->data_flow_info->vreg_to_ssa_map_exit = static_cast<int32_t*>( |
| cu_.arena.Alloc(sizeof(int32_t) * num_vregs_, kArenaAllocDFInfo)); |
| std::fill_n(bb->data_flow_info->vreg_to_ssa_map_exit, num_vregs_, INVALID_SREG); |
| } |
| } |
| } |
| |
| void PerformGVN() { |
| cu_.mir_graph->SSATransformationStart(); |
| cu_.mir_graph->ComputeDFSOrders(); |
| cu_.mir_graph->ComputeDominators(); |
| cu_.mir_graph->ComputeTopologicalSortOrder(); |
| cu_.mir_graph->SSATransformationEnd(); |
| cu_.mir_graph->temp_.gvn.ifield_ids = GlobalValueNumbering::PrepareGvnFieldIds( |
| allocator_.get(), cu_.mir_graph->ifield_lowering_infos_); |
| cu_.mir_graph->temp_.gvn.sfield_ids = GlobalValueNumbering::PrepareGvnFieldIds( |
| allocator_.get(), cu_.mir_graph->sfield_lowering_infos_); |
| ASSERT_TRUE(gvn_ == nullptr); |
| gvn_.reset(new (allocator_.get()) GlobalValueNumbering(&cu_, allocator_.get(), |
| GlobalValueNumbering::kModeGvn)); |
| value_names_.resize(mir_count_, 0xffffu); |
| LoopRepeatingTopologicalSortIterator iterator(cu_.mir_graph.get()); |
| bool change = false; |
| for (BasicBlock* bb = iterator.Next(change); bb != nullptr; bb = iterator.Next(change)) { |
| LocalValueNumbering* lvn = gvn_->PrepareBasicBlock(bb); |
| if (lvn != nullptr) { |
| for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { |
| value_names_[mir - mirs_] = lvn->GetValueNumber(mir); |
| } |
| } |
| change = (lvn != nullptr) && gvn_->FinishBasicBlock(bb); |
| ASSERT_TRUE(gvn_->Good()); |
| } |
| } |
| |
| void PerformGVNCodeModifications() { |
| ASSERT_TRUE(gvn_ != nullptr); |
| ASSERT_TRUE(gvn_->Good()); |
| gvn_->StartPostProcessing(); |
| TopologicalSortIterator iterator(cu_.mir_graph.get()); |
| for (BasicBlock* bb = iterator.Next(); bb != nullptr; bb = iterator.Next()) { |
| LocalValueNumbering* lvn = gvn_->PrepareBasicBlock(bb); |
| if (lvn != nullptr) { |
| for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { |
| uint16_t value_name = lvn->GetValueNumber(mir); |
| ASSERT_EQ(value_name, value_names_[mir - mirs_]); |
| } |
| } |
| bool change = (lvn != nullptr) && gvn_->FinishBasicBlock(bb); |
| ASSERT_FALSE(change); |
| ASSERT_TRUE(gvn_->Good()); |
| } |
| } |
| |
| void FillVregToSsaRegExitMaps() { |
| // Fill in vreg_to_ssa_map_exit for each BB. |
| PreOrderDfsIterator iterator(cu_.mir_graph.get()); |
| for (BasicBlock* bb = iterator.Next(); bb != nullptr; bb = iterator.Next()) { |
| if (bb->block_type == kDalvikByteCode) { |
| CHECK(!bb->predecessors.empty()); |
| BasicBlock* pred_bb = cu_.mir_graph->GetBasicBlock(bb->predecessors[0]); |
| for (size_t v_reg = 0; v_reg != num_vregs_; ++v_reg) { |
| if (bb->data_flow_info->vreg_to_ssa_map_exit[v_reg] == INVALID_SREG) { |
| bb->data_flow_info->vreg_to_ssa_map_exit[v_reg] = |
| pred_bb->data_flow_info->vreg_to_ssa_map_exit[v_reg]; |
| } |
| } |
| } |
| } |
| } |
| |
| void PerformDCE() { |
| FillVregToSsaRegExitMaps(); |
| cu_.mir_graph->GetNumOfCodeAndTempVRs(); |
| dce_.reset(new (allocator_.get()) GvnDeadCodeElimination(gvn_.get(), allocator_.get())); |
| PreOrderDfsIterator iterator(cu_.mir_graph.get()); |
| for (BasicBlock* bb = iterator.Next(); bb != nullptr; bb = iterator.Next()) { |
| if (bb->block_type == kDalvikByteCode) { |
| dce_->Apply(bb); |
| } |
| } |
| } |
| |
| void PerformGVN_DCE() { |
| PerformGVN(); |
| PerformGVNCodeModifications(); // Eliminate null/range checks. |
| PerformDCE(); |
| } |
| |
| template <size_t count> |
| void ExpectValueNamesNE(const size_t (&indexes)[count]) { |
| for (size_t i1 = 0; i1 != count; ++i1) { |
| size_t idx1 = indexes[i1]; |
| for (size_t i2 = i1 + 1; i2 != count; ++i2) { |
| size_t idx2 = indexes[i2]; |
| EXPECT_NE(value_names_[idx1], value_names_[idx2]) << idx1 << " " << idx2; |
| } |
| } |
| } |
| |
| template <size_t count> |
| void ExpectNoNullCheck(const size_t (&indexes)[count]) { |
| for (size_t i = 0; i != count; ++i) { |
| size_t idx = indexes[i]; |
| EXPECT_EQ(MIR_IGNORE_NULL_CHECK, mirs_[idx].optimization_flags & MIR_IGNORE_NULL_CHECK) |
| << idx; |
| } |
| size_t num_no_null_ck = 0u; |
| for (size_t i = 0; i != mir_count_; ++i) { |
| if ((mirs_[i].optimization_flags & MIR_IGNORE_NULL_CHECK) != 0) { |
| ++num_no_null_ck; |
| } |
| } |
| EXPECT_EQ(count, num_no_null_ck); |
| } |
| |
| GvnDeadCodeEliminationTest() |
| : pool_(), |
| cu_(&pool_, kRuntimeISA, nullptr, nullptr), |
| num_vregs_(0u), |
| mir_count_(0u), |
| mirs_(nullptr), |
| ssa_reps_(), |
| allocator_(), |
| gvn_(), |
| dce_(), |
| value_names_(), |
| live_in_v_(new (&cu_.arena) ArenaBitVector(&cu_.arena, kMaxSsaRegs, false, kBitMapMisc)) { |
| cu_.mir_graph.reset(new MIRGraph(&cu_, &cu_.arena)); |
| cu_.access_flags = kAccStatic; // Don't let "this" interfere with this test. |
| allocator_.reset(ScopedArenaAllocator::Create(&cu_.arena_stack)); |
| // By default, the zero-initialized reg_location_[.] with ref == false tells LVN that |
| // 0 constants are integral, not references. Nothing else is used by LVN/GVN. |
| cu_.mir_graph->reg_location_ = static_cast<RegLocation*>(cu_.arena.Alloc( |
| kMaxSsaRegs * sizeof(cu_.mir_graph->reg_location_[0]), kArenaAllocRegAlloc)); |
| // Bind all possible sregs to live vregs for test purposes. |
| live_in_v_->SetInitialBits(kMaxSsaRegs); |
| cu_.mir_graph->ssa_base_vregs_.reserve(kMaxSsaRegs); |
| cu_.mir_graph->ssa_subscripts_.reserve(kMaxSsaRegs); |
| for (unsigned int i = 0; i < kMaxSsaRegs; i++) { |
| cu_.mir_graph->ssa_base_vregs_.push_back(i); |
| cu_.mir_graph->ssa_subscripts_.push_back(0); |
| } |
| // Set shorty for a void-returning method without arguments. |
| cu_.shorty = "V"; |
| } |
| |
| static constexpr size_t kMaxSsaRegs = 16384u; |
| static constexpr size_t kMaxVRegs = 256u; |
| |
| ArenaPool pool_; |
| CompilationUnit cu_; |
| size_t num_vregs_; |
| size_t mir_count_; |
| MIR* mirs_; |
| std::vector<SSARepresentation> ssa_reps_; |
| std::unique_ptr<ScopedArenaAllocator> allocator_; |
| std::unique_ptr<GlobalValueNumbering> gvn_; |
| std::unique_ptr<GvnDeadCodeElimination> dce_; |
| std::vector<uint16_t> value_names_; |
| ArenaBitVector* live_in_v_; |
| }; |
| |
| constexpr uint16_t GvnDeadCodeEliminationTest::kNoValue; |
| |
| class GvnDeadCodeEliminationTestSimple : public GvnDeadCodeEliminationTest { |
| public: |
| GvnDeadCodeEliminationTestSimple(); |
| |
| private: |
| static const BBDef kSimpleBbs[]; |
| }; |
| |
| const GvnDeadCodeEliminationTest::BBDef GvnDeadCodeEliminationTestSimple::kSimpleBbs[] = { |
| DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), |
| DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), |
| DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(3)), |
| DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(1)), |
| }; |
| |
| GvnDeadCodeEliminationTestSimple::GvnDeadCodeEliminationTestSimple() |
| : GvnDeadCodeEliminationTest() { |
| PrepareBasicBlocks(kSimpleBbs); |
| } |
| |
| class GvnDeadCodeEliminationTestDiamond : public GvnDeadCodeEliminationTest { |
| public: |
| GvnDeadCodeEliminationTestDiamond(); |
| |
| private: |
| static const BBDef kDiamondBbs[]; |
| }; |
| |
| const GvnDeadCodeEliminationTest::BBDef GvnDeadCodeEliminationTestDiamond::kDiamondBbs[] = { |
| DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), |
| DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), |
| DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(6)), |
| DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 5), DEF_PRED1(1)), // Block #3, top of the diamond. |
| DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)), // Block #4, left side. |
| DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)), // Block #5, right side. |
| DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED2(4, 5)), // Block #6, bottom. |
| }; |
| |
| GvnDeadCodeEliminationTestDiamond::GvnDeadCodeEliminationTestDiamond() |
| : GvnDeadCodeEliminationTest() { |
| PrepareBasicBlocks(kDiamondBbs); |
| } |
| |
| class GvnDeadCodeEliminationTestLoop : public GvnDeadCodeEliminationTest { |
| public: |
| GvnDeadCodeEliminationTestLoop(); |
| |
| private: |
| static const BBDef kLoopBbs[]; |
| }; |
| |
| const GvnDeadCodeEliminationTest::BBDef GvnDeadCodeEliminationTestLoop::kLoopBbs[] = { |
| DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), |
| DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), |
| DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(5)), |
| DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)), |
| DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 4), DEF_PRED2(3, 4)), // "taken" loops to self. |
| DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(4)), |
| }; |
| |
| GvnDeadCodeEliminationTestLoop::GvnDeadCodeEliminationTestLoop() |
| : GvnDeadCodeEliminationTest() { |
| PrepareBasicBlocks(kLoopBbs); |
| } |
| |
| TEST_F(GvnDeadCodeEliminationTestSimple, Rename1) { |
| static const IFieldDef ifields[] = { |
| { 0u, 1u, 0u, false, kDexMemAccessWord }, |
| { 1u, 1u, 1u, false, kDexMemAccessWord }, |
| }; |
| static const MIRDef mirs[] = { |
| DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), |
| DEF_IGET(3, Instruction::IGET, 1u, 0u, 0u), |
| DEF_MOVE(3, Instruction::MOVE_OBJECT, 2u, 0u), |
| DEF_IGET(3, Instruction::IGET, 3u, 2u, 1u), |
| }; |
| |
| static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 2 }; |
| PrepareSRegToVRegMap(sreg_to_vreg_map); |
| |
| PrepareIFields(ifields); |
| PrepareMIRs(mirs); |
| PerformGVN_DCE(); |
| |
| ASSERT_EQ(arraysize(mirs), value_names_.size()); |
| static const size_t diff_indexes[] = { 0, 1, 3 }; |
| ExpectValueNamesNE(diff_indexes); |
| EXPECT_EQ(value_names_[0], value_names_[2]); |
| |
| const size_t no_null_ck_indexes[] = { 1, 3 }; |
| ExpectNoNullCheck(no_null_ck_indexes); |
| |
| static const bool eliminated[] = { |
| false, false, true, false |
| }; |
| static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); |
| for (size_t i = 0; i != arraysize(eliminated); ++i) { |
| bool actually_eliminated = (static_cast<int>(mirs_[i].dalvikInsn.opcode) == kMirOpNop); |
| EXPECT_EQ(eliminated[i], actually_eliminated) << i; |
| } |
| // Check that the IGET uses the s_reg 0, v_reg 0, defined by mirs_[0]. |
| ASSERT_EQ(1, mirs_[3].ssa_rep->num_uses); |
| EXPECT_EQ(0, mirs_[3].ssa_rep->uses[0]); |
| EXPECT_EQ(0u, mirs_[3].dalvikInsn.vB); |
| } |
| |
| TEST_F(GvnDeadCodeEliminationTestSimple, Rename2) { |
| static const IFieldDef ifields[] = { |
| { 0u, 1u, 0u, false, kDexMemAccessWord }, |
| { 1u, 1u, 1u, false, kDexMemAccessWord }, |
| }; |
| static const MIRDef mirs[] = { |
| DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), |
| DEF_IGET(3, Instruction::IGET, 1u, 0u, 0u), |
| DEF_MOVE(3, Instruction::MOVE_OBJECT, 2u, 0u), |
| DEF_IGET(3, Instruction::IGET, 3u, 2u, 1u), |
| DEF_CONST(3, Instruction::CONST, 4u, 1000), |
| }; |
| |
| static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 3, 2 }; |
| PrepareSRegToVRegMap(sreg_to_vreg_map); |
| |
| PrepareIFields(ifields); |
| PrepareMIRs(mirs); |
| PerformGVN_DCE(); |
| |
| ASSERT_EQ(arraysize(mirs), value_names_.size()); |
| static const size_t diff_indexes[] = { 0, 1, 3, 4 }; |
| ExpectValueNamesNE(diff_indexes); |
| EXPECT_EQ(value_names_[0], value_names_[2]); |
| |
| const size_t no_null_ck_indexes[] = { 1, 3 }; |
| ExpectNoNullCheck(no_null_ck_indexes); |
| |
| static const bool eliminated[] = { |
| false, false, true, false, false |
| }; |
| static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); |
| for (size_t i = 0; i != arraysize(eliminated); ++i) { |
| bool actually_eliminated = (static_cast<int>(mirs_[i].dalvikInsn.opcode) == kMirOpNop); |
| EXPECT_EQ(eliminated[i], actually_eliminated) << i; |
| } |
| // Check that the IGET uses the s_reg 0, v_reg 0, defined by mirs_[0]. |
| ASSERT_EQ(1, mirs_[3].ssa_rep->num_uses); |
| EXPECT_EQ(0, mirs_[3].ssa_rep->uses[0]); |
| EXPECT_EQ(0u, mirs_[3].dalvikInsn.vB); |
| } |
| |
| TEST_F(GvnDeadCodeEliminationTestSimple, Rename3) { |
| static const IFieldDef ifields[] = { |
| { 0u, 1u, 0u, false, kDexMemAccessWord }, |
| { 1u, 1u, 1u, false, kDexMemAccessWord }, |
| }; |
| static const MIRDef mirs[] = { |
| DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), |
| DEF_IGET(3, Instruction::IGET, 1u, 0u, 0u), |
| DEF_MOVE(3, Instruction::MOVE_OBJECT, 2u, 0u), |
| DEF_IGET(3, Instruction::IGET, 3u, 2u, 1u), |
| }; |
| |
| static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 0 }; |
| PrepareSRegToVRegMap(sreg_to_vreg_map); |
| |
| PrepareIFields(ifields); |
| PrepareMIRs(mirs); |
| PerformGVN_DCE(); |
| |
| ASSERT_EQ(arraysize(mirs), value_names_.size()); |
| static const size_t diff_indexes[] = { 0, 1, 3 }; |
| ExpectValueNamesNE(diff_indexes); |
| EXPECT_EQ(value_names_[0], value_names_[2]); |
| |
| const size_t no_null_ck_indexes[] = { 1, 3 }; |
| ExpectNoNullCheck(no_null_ck_indexes); |
| |
| static const bool eliminated[] = { |
| false, false, true, false |
| }; |
| static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); |
| for (size_t i = 0; i != arraysize(eliminated); ++i) { |
| bool actually_eliminated = (static_cast<int>(mirs_[i].dalvikInsn.opcode) == kMirOpNop); |
| EXPECT_EQ(eliminated[i], actually_eliminated) << i; |
| } |
| // Check that the NEW_INSTANCE defines the s_reg 2, v_reg 2, originally defined by the move. |
| ASSERT_EQ(1, mirs_[0].ssa_rep->num_defs); |
| EXPECT_EQ(2, mirs_[0].ssa_rep->defs[0]); |
| EXPECT_EQ(2u, mirs_[0].dalvikInsn.vA); |
| // Check that the first IGET is using the s_reg 2, v_reg 2. |
| ASSERT_EQ(1, mirs_[1].ssa_rep->num_uses); |
| EXPECT_EQ(2, mirs_[1].ssa_rep->uses[0]); |
| EXPECT_EQ(2u, mirs_[1].dalvikInsn.vB); |
| } |
| |
| TEST_F(GvnDeadCodeEliminationTestSimple, Rename4) { |
| static const MIRDef mirs[] = { |
| DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), |
| DEF_MOVE(3, Instruction::MOVE_OBJECT, 1u, 0u), |
| DEF_MOVE(3, Instruction::MOVE_OBJECT, 2u, 1u), |
| DEF_CONST_WIDE(3, Instruction::CONST_WIDE, 3u, 1000u), |
| }; |
| |
| static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 0, 1 /* high word */ }; |
| PrepareSRegToVRegMap(sreg_to_vreg_map); |
| |
| PrepareMIRs(mirs); |
| PerformGVN_DCE(); |
| |
| ASSERT_EQ(arraysize(mirs), value_names_.size()); |
| static const size_t diff_indexes[] = { 0, 3 }; |
| ExpectValueNamesNE(diff_indexes); |
| EXPECT_EQ(value_names_[0], value_names_[1]); |
| EXPECT_EQ(value_names_[0], value_names_[2]); |
| |
| static const bool eliminated[] = { |
| false, true, true, false |
| }; |
| static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); |
| for (size_t i = 0; i != arraysize(eliminated); ++i) { |
| bool actually_eliminated = (static_cast<int>(mirs_[i].dalvikInsn.opcode) == kMirOpNop); |
| EXPECT_EQ(eliminated[i], actually_eliminated) << i; |
| } |
| // Check that the NEW_INSTANCE defines the s_reg 2, v_reg 2, originally defined by the move 2u. |
| ASSERT_EQ(1, mirs_[0].ssa_rep->num_defs); |
| EXPECT_EQ(2, mirs_[0].ssa_rep->defs[0]); |
| EXPECT_EQ(2u, mirs_[0].dalvikInsn.vA); |
| } |
| |
| TEST_F(GvnDeadCodeEliminationTestSimple, Rename5) { |
| static const IFieldDef ifields[] = { |
| { 0u, 1u, 0u, false, kDexMemAccessWord }, |
| }; |
| static const MIRDef mirs[] = { |
| DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), |
| DEF_IGET(3, Instruction::IGET, 1u, 0u, 0u), |
| DEF_UNOP(3, Instruction::INT_TO_FLOAT, 2u, 1u), |
| DEF_MOVE(3, Instruction::MOVE_OBJECT, 3u, 0u), |
| DEF_MOVE(3, Instruction::MOVE_OBJECT, 4u, 3u), |
| DEF_CONST_WIDE(3, Instruction::CONST_WIDE, 5u, 1000u), |
| }; |
| |
| static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 1, 3, 0, 1 /* high word */ }; |
| PrepareSRegToVRegMap(sreg_to_vreg_map); |
| |
| PrepareIFields(ifields); |
| PrepareMIRs(mirs); |
| PerformGVN_DCE(); |
| |
| ASSERT_EQ(arraysize(mirs), value_names_.size()); |
| static const size_t diff_indexes[] = { 0, 1, 2, 5 }; |
| ExpectValueNamesNE(diff_indexes); |
| EXPECT_EQ(value_names_[0], value_names_[3]); |
| EXPECT_EQ(value_names_[0], value_names_[4]); |
| |
| static const bool eliminated[] = { |
| false, false, false, true, true, false |
| }; |
| static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); |
| for (size_t i = 0; i != arraysize(eliminated); ++i) { |
| bool actually_eliminated = (static_cast<int>(mirs_[i].dalvikInsn.opcode) == kMirOpNop); |
| EXPECT_EQ(eliminated[i], actually_eliminated) << i; |
| } |
| // Check that the NEW_INSTANCE defines the s_reg 4, v_reg 3, originally defined by the move 4u. |
| ASSERT_EQ(1, mirs_[0].ssa_rep->num_defs); |
| EXPECT_EQ(4, mirs_[0].ssa_rep->defs[0]); |
| EXPECT_EQ(3u, mirs_[0].dalvikInsn.vA); |
| } |
| |
| TEST_F(GvnDeadCodeEliminationTestSimple, Rename6) { |
| static const MIRDef mirs[] = { |
| DEF_CONST_WIDE(3, Instruction::CONST_WIDE, 0u, 1000u), |
| DEF_MOVE_WIDE(3, Instruction::MOVE_WIDE, 2u, 0u), |
| }; |
| |
| static const int32_t sreg_to_vreg_map[] = { 0, 1 /* high word */, 1, 2 /* high word */ }; |
| PrepareSRegToVRegMap(sreg_to_vreg_map); |
| |
| PrepareMIRs(mirs); |
| PerformGVN_DCE(); |
| |
| ASSERT_EQ(arraysize(mirs), value_names_.size()); |
| EXPECT_EQ(value_names_[0], value_names_[1]); |
| |
| static const bool eliminated[] = { |
| false, true |
| }; |
| static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); |
| for (size_t i = 0; i != arraysize(eliminated); ++i) { |
| bool actually_eliminated = (static_cast<int>(mirs_[i].dalvikInsn.opcode) == kMirOpNop); |
| EXPECT_EQ(eliminated[i], actually_eliminated) << i; |
| } |
| // Check that the CONST_WIDE defines the s_reg 2, v_reg 1, originally defined by the move 2u. |
| ASSERT_EQ(2, mirs_[0].ssa_rep->num_defs); |
| EXPECT_EQ(2, mirs_[0].ssa_rep->defs[0]); |
| EXPECT_EQ(3, mirs_[0].ssa_rep->defs[1]); |
| EXPECT_EQ(1u, mirs_[0].dalvikInsn.vA); |
| } |
| |
| TEST_F(GvnDeadCodeEliminationTestSimple, Rename7) { |
| static const MIRDef mirs[] = { |
| DEF_CONST(3, Instruction::CONST, 0u, 1000u), |
| DEF_MOVE(3, Instruction::MOVE, 1u, 0u), |
| DEF_BINOP(3, Instruction::ADD_INT, 2u, 0u, 1u), |
| }; |
| |
| static const int32_t sreg_to_vreg_map[] = { 0, 1, 0 }; |
| PrepareSRegToVRegMap(sreg_to_vreg_map); |
| |
| PrepareMIRs(mirs); |
| PerformGVN_DCE(); |
| |
| ASSERT_EQ(arraysize(mirs), value_names_.size()); |
| EXPECT_NE(value_names_[0], value_names_[2]); |
| EXPECT_EQ(value_names_[0], value_names_[1]); |
| |
| static const bool eliminated[] = { |
| false, true, false |
| }; |
| static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); |
| for (size_t i = 0; i != arraysize(eliminated); ++i) { |
| bool actually_eliminated = (static_cast<int>(mirs_[i].dalvikInsn.opcode) == kMirOpNop); |
| EXPECT_EQ(eliminated[i], actually_eliminated) << i; |
| } |
| // Check that the CONST defines the s_reg 1, v_reg 1, originally defined by the move 1u. |
| ASSERT_EQ(1, mirs_[0].ssa_rep->num_defs); |
| EXPECT_EQ(1, mirs_[0].ssa_rep->defs[0]); |
| EXPECT_EQ(1u, mirs_[0].dalvikInsn.vA); |
| // Check that the ADD_INT inputs are both s_reg1, vreg 1. |
| ASSERT_EQ(2, mirs_[2].ssa_rep->num_uses); |
| EXPECT_EQ(1, mirs_[2].ssa_rep->uses[0]); |
| EXPECT_EQ(1, mirs_[2].ssa_rep->uses[1]); |
| EXPECT_EQ(1u, mirs_[2].dalvikInsn.vB); |
| EXPECT_EQ(1u, mirs_[2].dalvikInsn.vC); |
| } |
| |
| TEST_F(GvnDeadCodeEliminationTestSimple, Rename8) { |
| static const MIRDef mirs[] = { |
| DEF_CONST(3, Instruction::CONST, 0u, 1000u), |
| DEF_MOVE(3, Instruction::MOVE, 1u, 0u), |
| DEF_BINOP(3, Instruction::ADD_INT_2ADDR, 2u, 0u, 1u), |
| }; |
| |
| static const int32_t sreg_to_vreg_map[] = { 0, 1, 0 }; |
| PrepareSRegToVRegMap(sreg_to_vreg_map); |
| |
| PrepareMIRs(mirs); |
| PerformGVN_DCE(); |
| |
| ASSERT_EQ(arraysize(mirs), value_names_.size()); |
| EXPECT_NE(value_names_[0], value_names_[2]); |
| EXPECT_EQ(value_names_[0], value_names_[1]); |
| |
| static const bool eliminated[] = { |
| false, true, false |
| }; |
| static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); |
| for (size_t i = 0; i != arraysize(eliminated); ++i) { |
| bool actually_eliminated = (static_cast<int>(mirs_[i].dalvikInsn.opcode) == kMirOpNop); |
| EXPECT_EQ(eliminated[i], actually_eliminated) << i; |
| } |
| // Check that the CONST defines the s_reg 1, v_reg 1, originally defined by the move 1u. |
| ASSERT_EQ(1, mirs_[0].ssa_rep->num_defs); |
| EXPECT_EQ(1, mirs_[0].ssa_rep->defs[0]); |
| EXPECT_EQ(1u, mirs_[0].dalvikInsn.vA); |
| // Check that the ADD_INT_2ADDR was replaced by ADD_INT and inputs are both s_reg 1, vreg 1. |
| EXPECT_EQ(Instruction::ADD_INT, mirs_[2].dalvikInsn.opcode); |
| ASSERT_EQ(2, mirs_[2].ssa_rep->num_uses); |
| EXPECT_EQ(1, mirs_[2].ssa_rep->uses[0]); |
| EXPECT_EQ(1, mirs_[2].ssa_rep->uses[1]); |
| EXPECT_EQ(1u, mirs_[2].dalvikInsn.vB); |
| EXPECT_EQ(1u, mirs_[2].dalvikInsn.vC); |
| } |
| |
| TEST_F(GvnDeadCodeEliminationTestSimple, Rename9) { |
| static const MIRDef mirs[] = { |
| DEF_CONST(3, Instruction::CONST, 0u, 1000u), |
| DEF_BINOP(3, Instruction::ADD_INT_2ADDR, 1u, 0u, 0u), |
| DEF_MOVE(3, Instruction::MOVE, 2u, 1u), |
| DEF_CONST(3, Instruction::CONST, 3u, 3000u), |
| }; |
| |
| static const int32_t sreg_to_vreg_map[] = { 0, 0, 1, 0 }; |
| PrepareSRegToVRegMap(sreg_to_vreg_map); |
| |
| PrepareMIRs(mirs); |
| PerformGVN_DCE(); |
| |
| ASSERT_EQ(arraysize(mirs), value_names_.size()); |
| static const size_t diff_indexes[] = { 0, 1, 3 }; |
| ExpectValueNamesNE(diff_indexes); |
| EXPECT_EQ(value_names_[1], value_names_[2]); |
| |
| static const bool eliminated[] = { |
| false, false, true, false |
| }; |
| static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); |
| for (size_t i = 0; i != arraysize(eliminated); ++i) { |
| bool actually_eliminated = (static_cast<int>(mirs_[i].dalvikInsn.opcode) == kMirOpNop); |
| EXPECT_EQ(eliminated[i], actually_eliminated) << i; |
| } |
| // Check that the ADD_INT_2ADDR was replaced by ADD_INT and output is in s_reg 2, vreg 1. |
| EXPECT_EQ(Instruction::ADD_INT, mirs_[1].dalvikInsn.opcode); |
| ASSERT_EQ(2, mirs_[1].ssa_rep->num_uses); |
| EXPECT_EQ(0, mirs_[1].ssa_rep->uses[0]); |
| EXPECT_EQ(0, mirs_[1].ssa_rep->uses[1]); |
| EXPECT_EQ(0u, mirs_[1].dalvikInsn.vB); |
| EXPECT_EQ(0u, mirs_[1].dalvikInsn.vC); |
| ASSERT_EQ(1, mirs_[1].ssa_rep->num_defs); |
| EXPECT_EQ(2, mirs_[1].ssa_rep->defs[0]); |
| EXPECT_EQ(1u, mirs_[1].dalvikInsn.vA); |
| } |
| |
| TEST_F(GvnDeadCodeEliminationTestSimple, NoRename1) { |
| static const IFieldDef ifields[] = { |
| { 0u, 1u, 0u, false, kDexMemAccessWord }, |
| { 1u, 1u, 1u, false, kDexMemAccessWord }, |
| }; |
| static const MIRDef mirs[] = { |
| DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), |
| DEF_IGET(3, Instruction::IGET, 1u, 0u, 0u), |
| DEF_UNOP(3, Instruction::INT_TO_FLOAT, 2u, 1u), |
| DEF_MOVE(3, Instruction::MOVE_OBJECT, 3u, 0u), |
| DEF_CONST(3, Instruction::CONST, 4u, 1000), |
| DEF_IGET(3, Instruction::IGET, 5u, 3u, 1u), |
| }; |
| |
| static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 1, 0, 1 }; |
| PrepareSRegToVRegMap(sreg_to_vreg_map); |
| |
| PrepareIFields(ifields); |
| PrepareMIRs(mirs); |
| PerformGVN_DCE(); |
| |
| ASSERT_EQ(arraysize(mirs), value_names_.size()); |
| static const size_t diff_indexes[] = { 0, 1, 2, 4, 5 }; |
| ExpectValueNamesNE(diff_indexes); |
| EXPECT_EQ(value_names_[0], value_names_[3]); |
| |
| const size_t no_null_ck_indexes[] = { 1, 5 }; |
| ExpectNoNullCheck(no_null_ck_indexes); |
| |
| static const bool eliminated[] = { |
| false, false, false, false, false, false |
| }; |
| static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); |
| for (size_t i = 0; i != arraysize(eliminated); ++i) { |
| bool actually_eliminated = (static_cast<int>(mirs_[i].dalvikInsn.opcode) == kMirOpNop); |
| EXPECT_EQ(eliminated[i], actually_eliminated) << i; |
| } |
| } |
| |
| TEST_F(GvnDeadCodeEliminationTestSimple, NoRename2) { |
| static const IFieldDef ifields[] = { |
| { 0u, 1u, 0u, false, kDexMemAccessWord }, |
| { 1u, 1u, 1u, false, kDexMemAccessWord }, |
| }; |
| static const MIRDef mirs[] = { |
| DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), |
| DEF_IGET(3, Instruction::IGET, 1u, 0u, 0u), |
| DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 2u), |
| DEF_MOVE(3, Instruction::MOVE_OBJECT, 3u, 0u), |
| DEF_CONST(3, Instruction::CONST, 4u, 1000), |
| DEF_IGET(3, Instruction::IGET, 5u, 3u, 1u), |
| DEF_CONST(3, Instruction::CONST, 6u, 2000), |
| }; |
| |
| static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 2, 0, 3, 2 }; |
| PrepareSRegToVRegMap(sreg_to_vreg_map); |
| |
| PrepareIFields(ifields); |
| PrepareMIRs(mirs); |
| PerformGVN_DCE(); |
| |
| ASSERT_EQ(arraysize(mirs), value_names_.size()); |
| static const size_t diff_indexes[] = { 0, 1, 2, 4, 5, 6 }; |
| ExpectValueNamesNE(diff_indexes); |
| EXPECT_EQ(value_names_[0], value_names_[3]); |
| |
| const size_t no_null_ck_indexes[] = { 1, 5 }; |
| ExpectNoNullCheck(no_null_ck_indexes); |
| |
| static const bool eliminated[] = { |
| false, false, false, false, false, false, false |
| }; |
| static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); |
| for (size_t i = 0; i != arraysize(eliminated); ++i) { |
| bool actually_eliminated = (static_cast<int>(mirs_[i].dalvikInsn.opcode) == kMirOpNop); |
| EXPECT_EQ(eliminated[i], actually_eliminated) << i; |
| } |
| } |
| |
| TEST_F(GvnDeadCodeEliminationTestSimple, NoRename3) { |
| static const IFieldDef ifields[] = { |
| { 0u, 1u, 0u, false, kDexMemAccessWord }, |
| { 1u, 1u, 1u, false, kDexMemAccessWord }, |
| { 2u, 1u, 2u, false, kDexMemAccessWord }, |
| }; |
| static const MIRDef mirs[] = { |
| DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), |
| DEF_IGET(3, Instruction::IGET, 1u, 0u, 0u), |
| DEF_IGET(3, Instruction::IGET, 2u, 0u, 2u), |
| DEF_BINOP(3, Instruction::ADD_INT, 3u, 1u, 2u), |
| DEF_MOVE(3, Instruction::MOVE_OBJECT, 4u, 0u), |
| DEF_IGET(3, Instruction::IGET, 5u, 4u, 1u), |
| }; |
| |
| static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 3, 2, 0 }; |
| PrepareSRegToVRegMap(sreg_to_vreg_map); |
| |
| PrepareIFields(ifields); |
| PrepareMIRs(mirs); |
| PerformGVN_DCE(); |
| |
| ASSERT_EQ(arraysize(mirs), value_names_.size()); |
| static const size_t diff_indexes[] = { 0, 1, 2, 3, 5 }; |
| ExpectValueNamesNE(diff_indexes); |
| EXPECT_EQ(value_names_[0], value_names_[4]); |
| |
| const size_t no_null_ck_indexes[] = { 1, 2, 5 }; |
| ExpectNoNullCheck(no_null_ck_indexes); |
| |
| static const bool eliminated[] = { |
| false, false, false, false, false, false |
| }; |
| static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); |
| for (size_t i = 0; i != arraysize(eliminated); ++i) { |
| bool actually_eliminated = (static_cast<int>(mirs_[i].dalvikInsn.opcode) == kMirOpNop); |
| EXPECT_EQ(eliminated[i], actually_eliminated) << i; |
| } |
| } |
| |
| TEST_F(GvnDeadCodeEliminationTestSimple, Simple1) { |
| static const IFieldDef ifields[] = { |
| { 0u, 1u, 0u, false, kDexMemAccessObject }, |
| { 1u, 1u, 1u, false, kDexMemAccessObject }, |
| { 2u, 1u, 2u, false, kDexMemAccessWord }, |
| }; |
| static const MIRDef mirs[] = { |
| DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), |
| DEF_IGET(3, Instruction::IGET_OBJECT, 1u, 0u, 0u), |
| DEF_IGET(3, Instruction::IGET_OBJECT, 2u, 1u, 1u), |
| DEF_IGET(3, Instruction::IGET, 3u, 2u, 2u), |
| DEF_IGET(3, Instruction::IGET_OBJECT, 4u, 0u, 0u), |
| DEF_IGET(3, Instruction::IGET_OBJECT, 5u, 4u, 1u), |
| }; |
| |
| static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 3, 1, 2 }; |
| PrepareSRegToVRegMap(sreg_to_vreg_map); |
| |
| PrepareIFields(ifields); |
| PrepareMIRs(mirs); |
| PerformGVN_DCE(); |
| |
| ASSERT_EQ(arraysize(mirs), value_names_.size()); |
| EXPECT_NE(value_names_[0], value_names_[1]); |
| EXPECT_NE(value_names_[0], value_names_[2]); |
| EXPECT_NE(value_names_[0], value_names_[3]); |
| EXPECT_NE(value_names_[1], value_names_[2]); |
| EXPECT_NE(value_names_[1], value_names_[3]); |
| EXPECT_NE(value_names_[2], value_names_[3]); |
| EXPECT_EQ(value_names_[1], value_names_[4]); |
| EXPECT_EQ(value_names_[2], value_names_[5]); |
| |
| EXPECT_EQ(MIR_IGNORE_NULL_CHECK, mirs_[4].optimization_flags & MIR_IGNORE_NULL_CHECK); |
| EXPECT_EQ(MIR_IGNORE_NULL_CHECK, mirs_[5].optimization_flags & MIR_IGNORE_NULL_CHECK); |
| |
| static const bool eliminated[] = { |
| false, false, false, false, true, true |
| }; |
| static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); |
| for (size_t i = 0; i != arraysize(eliminated); ++i) { |
| bool actually_eliminated = (static_cast<int>(mirs_[i].dalvikInsn.opcode) == kMirOpNop); |
| EXPECT_EQ(eliminated[i], actually_eliminated) << i; |
| } |
| // Check that the sregs have been renamed correctly. |
| ASSERT_EQ(1, mirs_[1].ssa_rep->num_defs); |
| EXPECT_EQ(4, mirs_[1].ssa_rep->defs[0]); |
| ASSERT_EQ(1, mirs_[1].ssa_rep->num_uses); |
| EXPECT_EQ(0, mirs_[1].ssa_rep->uses[0]); |
| ASSERT_EQ(1, mirs_[2].ssa_rep->num_defs); |
| EXPECT_EQ(5, mirs_[2].ssa_rep->defs[0]); |
| ASSERT_EQ(1, mirs_[2].ssa_rep->num_uses); |
| EXPECT_EQ(4, mirs_[2].ssa_rep->uses[0]); |
| ASSERT_EQ(1, mirs_[3].ssa_rep->num_defs); |
| EXPECT_EQ(3, mirs_[3].ssa_rep->defs[0]); |
| ASSERT_EQ(1, mirs_[3].ssa_rep->num_uses); |
| EXPECT_EQ(5, mirs_[3].ssa_rep->uses[0]); |
| } |
| |
| TEST_F(GvnDeadCodeEliminationTestSimple, Simple2) { |
| static const IFieldDef ifields[] = { |
| { 0u, 1u, 0u, false, kDexMemAccessWord }, |
| }; |
| static const MIRDef mirs[] = { |
| DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), |
| DEF_CONST(3, Instruction::CONST, 1u, 1000), |
| DEF_IGET(3, Instruction::IGET, 2u, 0u, 0u), |
| DEF_BINOP(3, Instruction::ADD_INT_2ADDR, 3u, 2u, 1u), |
| DEF_UNOP(3, Instruction::INT_TO_FLOAT, 4u, 3u), |
| DEF_IGET(3, Instruction::IGET, 5u, 0u, 0u), |
| DEF_BINOP(3, Instruction::ADD_INT_2ADDR, 6u, 5u, 1u), |
| }; |
| |
| static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 2, 3, 2, 2 }; |
| PrepareSRegToVRegMap(sreg_to_vreg_map); |
| |
| PrepareIFields(ifields); |
| PrepareMIRs(mirs); |
| PerformGVN_DCE(); |
| |
| ASSERT_EQ(arraysize(mirs), value_names_.size()); |
| static const size_t diff_indexes[] = { 0, 1, 2, 3 }; |
| ExpectValueNamesNE(diff_indexes); |
| EXPECT_EQ(value_names_[2], value_names_[5]); |
| EXPECT_EQ(value_names_[3], value_names_[6]); |
| |
| const size_t no_null_ck_indexes[] = { 2, 5 }; |
| ExpectNoNullCheck(no_null_ck_indexes); |
| |
| static const bool eliminated[] = { |
| false, false, false, false, false, true, true |
| }; |
| static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); |
| for (size_t i = 0; i != arraysize(eliminated); ++i) { |
| bool actually_eliminated = (static_cast<int>(mirs_[i].dalvikInsn.opcode) == kMirOpNop); |
| EXPECT_EQ(eliminated[i], actually_eliminated) << i; |
| } |
| // Check that the sregs have been renamed correctly. |
| ASSERT_EQ(1, mirs_[3].ssa_rep->num_defs); |
| EXPECT_EQ(6, mirs_[3].ssa_rep->defs[0]); |
| ASSERT_EQ(2, mirs_[3].ssa_rep->num_uses); |
| EXPECT_EQ(2, mirs_[3].ssa_rep->uses[0]); |
| EXPECT_EQ(1, mirs_[3].ssa_rep->uses[1]); |
| ASSERT_EQ(1, mirs_[4].ssa_rep->num_defs); |
| EXPECT_EQ(4, mirs_[4].ssa_rep->defs[0]); |
| ASSERT_EQ(1, mirs_[4].ssa_rep->num_uses); |
| EXPECT_EQ(6, mirs_[4].ssa_rep->uses[0]); |
| } |
| |
| TEST_F(GvnDeadCodeEliminationTestSimple, Simple3) { |
| static const IFieldDef ifields[] = { |
| { 0u, 1u, 0u, false, kDexMemAccessWord }, |
| }; |
| static const MIRDef mirs[] = { |
| DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), |
| DEF_CONST(3, Instruction::CONST, 1u, 1000), |
| DEF_CONST(3, Instruction::CONST, 2u, 2000), |
| DEF_CONST(3, Instruction::CONST, 3u, 3000), |
| DEF_IGET(3, Instruction::IGET, 4u, 0u, 0u), |
| DEF_BINOP(3, Instruction::ADD_INT, 5u, 4u, 1u), |
| DEF_BINOP(3, Instruction::MUL_INT, 6u, 5u, 2u), |
| DEF_BINOP(3, Instruction::SUB_INT, 7u, 6u, 3u), |
| DEF_UNOP(3, Instruction::INT_TO_FLOAT, 8u, 7u), |
| DEF_IGET(3, Instruction::IGET, 9u, 0u, 0u), |
| DEF_BINOP(3, Instruction::ADD_INT, 10u, 9u, 1u), |
| DEF_BINOP(3, Instruction::MUL_INT, 11u, 10u, 2u), // Simple elimination of ADD+MUL |
| DEF_BINOP(3, Instruction::SUB_INT, 12u, 11u, 3u), // allows simple elimination of IGET+SUB. |
| }; |
| |
| static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 3, 4, 5, 5, 4, 6, 4, 5, 5, 4 }; |
| PrepareSRegToVRegMap(sreg_to_vreg_map); |
| |
| PrepareIFields(ifields); |
| PrepareMIRs(mirs); |
| PerformGVN_DCE(); |
| |
| ASSERT_EQ(arraysize(mirs), value_names_.size()); |
| static const size_t diff_indexes[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 }; |
| ExpectValueNamesNE(diff_indexes); |
| EXPECT_EQ(value_names_[4], value_names_[9]); |
| EXPECT_EQ(value_names_[5], value_names_[10]); |
| EXPECT_EQ(value_names_[6], value_names_[11]); |
| EXPECT_EQ(value_names_[7], value_names_[12]); |
| |
| const size_t no_null_ck_indexes[] = { 4, 9 }; |
| ExpectNoNullCheck(no_null_ck_indexes); |
| |
| static const bool eliminated[] = { |
| false, false, false, false, false, false, false, false, false, true, true, true, true |
| }; |
| static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); |
| for (size_t i = 0; i != arraysize(eliminated); ++i) { |
| bool actually_eliminated = (static_cast<int>(mirs_[i].dalvikInsn.opcode) == kMirOpNop); |
| EXPECT_EQ(eliminated[i], actually_eliminated) << i; |
| } |
| // Check that the sregs have been renamed correctly. |
| ASSERT_EQ(1, mirs_[6].ssa_rep->num_defs); |
| EXPECT_EQ(11, mirs_[6].ssa_rep->defs[0]); // 6 -> 11 |
| ASSERT_EQ(2, mirs_[6].ssa_rep->num_uses); |
| EXPECT_EQ(5, mirs_[6].ssa_rep->uses[0]); |
| EXPECT_EQ(2, mirs_[6].ssa_rep->uses[1]); |
| ASSERT_EQ(1, mirs_[7].ssa_rep->num_defs); |
| EXPECT_EQ(12, mirs_[7].ssa_rep->defs[0]); // 7 -> 12 |
| ASSERT_EQ(2, mirs_[7].ssa_rep->num_uses); |
| EXPECT_EQ(11, mirs_[7].ssa_rep->uses[0]); // 6 -> 11 |
| EXPECT_EQ(3, mirs_[7].ssa_rep->uses[1]); |
| ASSERT_EQ(1, mirs_[8].ssa_rep->num_defs); |
| EXPECT_EQ(8, mirs_[8].ssa_rep->defs[0]); |
| ASSERT_EQ(1, mirs_[8].ssa_rep->num_uses); |
| EXPECT_EQ(12, mirs_[8].ssa_rep->uses[0]); // 7 -> 12 |
| } |
| |
| TEST_F(GvnDeadCodeEliminationTestSimple, Simple4) { |
| static const IFieldDef ifields[] = { |
| { 0u, 1u, 0u, false, kDexMemAccessWord }, |
| }; |
| static const MIRDef mirs[] = { |
| DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), |
| DEF_CONST_WIDE(3, Instruction::CONST_WIDE, 1u, INT64_C(1)), |
| DEF_BINOP(3, Instruction::LONG_TO_FLOAT, 3u, 1u, 2u), |
| DEF_IGET(3, Instruction::IGET, 4u, 0u, 0u), |
| DEF_UNOP(3, Instruction::INT_TO_FLOAT, 5u, 4u), |
| DEF_CONST_WIDE(3, Instruction::CONST_WIDE, 6u, INT64_C(1)), |
| DEF_BINOP(3, Instruction::LONG_TO_FLOAT, 8u, 6u, 7u), |
| DEF_IGET(3, Instruction::IGET, 9u, 0u, 0u), |
| }; |
| |
| static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 1, 2, 3, 1, 2, 1, 2 }; |
| PrepareSRegToVRegMap(sreg_to_vreg_map); |
| |
| PrepareIFields(ifields); |
| PrepareMIRs(mirs); |
| PerformGVN_DCE(); |
| |
| ASSERT_EQ(arraysize(mirs), value_names_.size()); |
| static const size_t diff_indexes[] = { 0, 1, 2, 3, 4 }; |
| ExpectValueNamesNE(diff_indexes); |
| EXPECT_EQ(value_names_[1], value_names_[5]); |
| EXPECT_EQ(value_names_[2], value_names_[6]); |
| EXPECT_EQ(value_names_[3], value_names_[7]); |
| |
| const size_t no_null_ck_indexes[] = { 3, 7 }; |
| ExpectNoNullCheck(no_null_ck_indexes); |
| |
| static const bool eliminated[] = { |
| // Simple elimination of CONST_WIDE+LONG_TO_FLOAT allows simple eliminatiion of IGET. |
| false, false, false, false, false, true, true, true |
| }; |
| static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); |
| for (size_t i = 0; i != arraysize(eliminated); ++i) { |
| bool actually_eliminated = (static_cast<int>(mirs_[i].dalvikInsn.opcode) == kMirOpNop); |
| EXPECT_EQ(eliminated[i], actually_eliminated) << i; |
| } |
| // Check that the sregs have been renamed correctly. |
| ASSERT_EQ(1, mirs_[2].ssa_rep->num_defs); |
| EXPECT_EQ(8, mirs_[2].ssa_rep->defs[0]); // 3 -> 8 |
| ASSERT_EQ(2, mirs_[2].ssa_rep->num_uses); |
| EXPECT_EQ(1, mirs_[2].ssa_rep->uses[0]); |
| EXPECT_EQ(2, mirs_[2].ssa_rep->uses[1]); |
| ASSERT_EQ(1, mirs_[3].ssa_rep->num_defs); |
| EXPECT_EQ(9, mirs_[3].ssa_rep->defs[0]); // 4 -> 9 |
| ASSERT_EQ(1, mirs_[3].ssa_rep->num_uses); |
| EXPECT_EQ(0, mirs_[3].ssa_rep->uses[0]); |
| ASSERT_EQ(1, mirs_[4].ssa_rep->num_defs); |
| EXPECT_EQ(5, mirs_[4].ssa_rep->defs[0]); |
| ASSERT_EQ(1, mirs_[4].ssa_rep->num_uses); |
| EXPECT_EQ(9, mirs_[4].ssa_rep->uses[0]); // 4 -> 9 |
| } |
| |
| TEST_F(GvnDeadCodeEliminationTestSimple, KillChain1) { |
| static const IFieldDef ifields[] = { |
| { 0u, 1u, 0u, false, kDexMemAccessWord }, |
| }; |
| static const MIRDef mirs[] = { |
| DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), |
| DEF_CONST(3, Instruction::CONST, 1u, 1000), |
| DEF_CONST(3, Instruction::CONST, 2u, 2000), |
| DEF_CONST(3, Instruction::CONST, 3u, 3000), |
| DEF_IGET(3, Instruction::IGET, 4u, 0u, 0u), |
| DEF_BINOP(3, Instruction::ADD_INT, 5u, 4u, 1u), |
| DEF_BINOP(3, Instruction::MUL_INT, 6u, 5u, 2u), |
| DEF_BINOP(3, Instruction::SUB_INT, 7u, 6u, 3u), |
| DEF_UNOP(3, Instruction::INT_TO_FLOAT, 8u, 7u), |
| DEF_IGET(3, Instruction::IGET, 9u, 0u, 0u), |
| DEF_BINOP(3, Instruction::ADD_INT, 10u, 9u, 1u), |
| DEF_BINOP(3, Instruction::MUL_INT, 11u, 10u, 2u), |
| DEF_BINOP(3, Instruction::SUB_INT, 12u, 11u, 3u), |
| }; |
| |
| static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 3, 4, 5, 4, 5, 6, 4, 5, 4, 5 }; |
| PrepareSRegToVRegMap(sreg_to_vreg_map); |
| |
| PrepareIFields(ifields); |
| PrepareMIRs(mirs); |
| PerformGVN_DCE(); |
| |
| ASSERT_EQ(arraysize(mirs), value_names_.size()); |
| static const size_t diff_indexes[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 }; |
| ExpectValueNamesNE(diff_indexes); |
| EXPECT_EQ(value_names_[4], value_names_[9]); |
| EXPECT_EQ(value_names_[5], value_names_[10]); |
| EXPECT_EQ(value_names_[6], value_names_[11]); |
| EXPECT_EQ(value_names_[7], value_names_[12]); |
| |
| const size_t no_null_ck_indexes[] = { 4, 9 }; |
| ExpectNoNullCheck(no_null_ck_indexes); |
| |
| static const bool eliminated[] = { |
| false, false, false, false, false, false, false, false, false, true, true, true, true |
| }; |
| static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); |
| for (size_t i = 0; i != arraysize(eliminated); ++i) { |
| bool actually_eliminated = (static_cast<int>(mirs_[i].dalvikInsn.opcode) == kMirOpNop); |
| EXPECT_EQ(eliminated[i], actually_eliminated) << i; |
| } |
| // Check that the sregs have been renamed correctly. |
| ASSERT_EQ(1, mirs_[6].ssa_rep->num_defs); |
| EXPECT_EQ(11, mirs_[6].ssa_rep->defs[0]); // 6 -> 11 |
| ASSERT_EQ(2, mirs_[6].ssa_rep->num_uses); |
| EXPECT_EQ(5, mirs_[6].ssa_rep->uses[0]); |
| EXPECT_EQ(2, mirs_[6].ssa_rep->uses[1]); |
| ASSERT_EQ(1, mirs_[7].ssa_rep->num_defs); |
| EXPECT_EQ(12, mirs_[7].ssa_rep->defs[0]); // 7 -> 12 |
| ASSERT_EQ(2, mirs_[7].ssa_rep->num_uses); |
| EXPECT_EQ(11, mirs_[7].ssa_rep->uses[0]); // 6 -> 11 |
| EXPECT_EQ(3, mirs_[7].ssa_rep->uses[1]); |
| ASSERT_EQ(1, mirs_[8].ssa_rep->num_defs); |
| EXPECT_EQ(8, mirs_[8].ssa_rep->defs[0]); |
| ASSERT_EQ(1, mirs_[8].ssa_rep->num_uses); |
| EXPECT_EQ(12, mirs_[8].ssa_rep->uses[0]); // 7 -> 12 |
| } |
| |
| TEST_F(GvnDeadCodeEliminationTestSimple, KillChain2) { |
| static const IFieldDef ifields[] = { |
| { 0u, 1u, 0u, false, kDexMemAccessWord }, |
| }; |
| static const MIRDef mirs[] = { |
| DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), |
| DEF_CONST(3, Instruction::CONST, 1u, 1000), |
| DEF_CONST(3, Instruction::CONST, 2u, 2000), |
| DEF_CONST(3, Instruction::CONST, 3u, 3000), |
| DEF_IGET(3, Instruction::IGET, 4u, 0u, 0u), |
| DEF_BINOP(3, Instruction::ADD_INT, 5u, 4u, 1u), |
| DEF_BINOP(3, Instruction::MUL_INT, 6u, 5u, 2u), |
| DEF_BINOP(3, Instruction::SUB_INT, 7u, 6u, 3u), |
| DEF_UNOP(3, Instruction::INT_TO_FLOAT, 8u, 7u), |
| DEF_IGET(3, Instruction::IGET, 9u, 0u, 0u), |
| DEF_BINOP(3, Instruction::ADD_INT, 10u, 9u, 1u), |
| DEF_BINOP(3, Instruction::MUL_INT, 11u, 10u, 2u), |
| DEF_BINOP(3, Instruction::SUB_INT, 12u, 11u, 3u), |
| DEF_CONST(3, Instruction::CONST, 13u, 4000), |
| }; |
| |
| static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 3, 4, 5, 5, 4, 6, 4, 7, 7, 4, 7 }; |
| PrepareSRegToVRegMap(sreg_to_vreg_map); |
| |
| PrepareIFields(ifields); |
| PrepareMIRs(mirs); |
| PerformGVN_DCE(); |
| |
| ASSERT_EQ(arraysize(mirs), value_names_.size()); |
| static const size_t diff_indexes[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 13 }; |
| ExpectValueNamesNE(diff_indexes); |
| EXPECT_EQ(value_names_[4], value_names_[9]); |
| EXPECT_EQ(value_names_[5], value_names_[10]); |
| EXPECT_EQ(value_names_[6], value_names_[11]); |
| EXPECT_EQ(value_names_[7], value_names_[12]); |
| |
| const size_t no_null_ck_indexes[] = { 4, 9 }; |
| ExpectNoNullCheck(no_null_ck_indexes); |
| |
| static const bool eliminated[] = { |
| false, false, false, false, false, false, false, false, false, true, true, true, true, false |
| }; |
| static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); |
| for (size_t i = 0; i != arraysize(eliminated); ++i) { |
| bool actually_eliminated = (static_cast<int>(mirs_[i].dalvikInsn.opcode) == kMirOpNop); |
| EXPECT_EQ(eliminated[i], actually_eliminated) << i; |
| } |
| // Check that the sregs have been renamed correctly. |
| ASSERT_EQ(1, mirs_[7].ssa_rep->num_defs); |
| EXPECT_EQ(12, mirs_[7].ssa_rep->defs[0]); // 7 -> 12 |
| ASSERT_EQ(2, mirs_[7].ssa_rep->num_uses); |
| EXPECT_EQ(6, mirs_[7].ssa_rep->uses[0]); |
| EXPECT_EQ(3, mirs_[7].ssa_rep->uses[1]); |
| ASSERT_EQ(1, mirs_[8].ssa_rep->num_defs); |
| EXPECT_EQ(8, mirs_[8].ssa_rep->defs[0]); |
| ASSERT_EQ(1, mirs_[8].ssa_rep->num_uses); |
| EXPECT_EQ(12, mirs_[8].ssa_rep->uses[0]); // 7 -> 12 |
| } |
| |
| TEST_F(GvnDeadCodeEliminationTestSimple, KillChain3) { |
| static const IFieldDef ifields[] = { |
| { 0u, 1u, 0u, false, kDexMemAccessWord }, |
| }; |
| static const MIRDef mirs[] = { |
| DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), |
| DEF_CONST(3, Instruction::CONST, 1u, 1000), |
| DEF_CONST(3, Instruction::CONST, 2u, 2000), |
| DEF_CONST(3, Instruction::CONST, 3u, 3000), |
| DEF_IGET(3, Instruction::IGET, 4u, 0u, 0u), |
| DEF_BINOP(3, Instruction::ADD_INT, 5u, 4u, 1u), |
| DEF_BINOP(3, Instruction::MUL_INT, 6u, 5u, 2u), |
| DEF_BINOP(3, Instruction::SUB_INT, 7u, 6u, 3u), |
| DEF_UNOP(3, Instruction::INT_TO_FLOAT, 8u, 7u), |
| DEF_IGET(3, Instruction::IGET, 9u, 0u, 0u), |
| DEF_BINOP(3, Instruction::ADD_INT, 10u, 9u, 1u), |
| DEF_BINOP(3, Instruction::MUL_INT, 11u, 10u, 2u), |
| DEF_CONST(3, Instruction::CONST, 12u, 4000), |
| DEF_BINOP(3, Instruction::SUB_INT, 13u, 11u, 3u), |
| }; |
| |
| static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 3, 4, 5, 5, 4, 6, 4, 7, 4, 7, 4 }; |
| PrepareSRegToVRegMap(sreg_to_vreg_map); |
| |
| PrepareIFields(ifields); |
| PrepareMIRs(mirs); |
| PerformGVN_DCE(); |
| |
| ASSERT_EQ(arraysize(mirs), value_names_.size()); |
| static const size_t diff_indexes[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 12 }; |
| ExpectValueNamesNE(diff_indexes); |
| EXPECT_EQ(value_names_[4], value_names_[9]); |
| EXPECT_EQ(value_names_[5], value_names_[10]); |
| EXPECT_EQ(value_names_[6], value_names_[11]); |
| EXPECT_EQ(value_names_[7], value_names_[13]); |
| |
| const size_t no_null_ck_indexes[] = { 4, 9 }; |
| ExpectNoNullCheck(no_null_ck_indexes); |
| |
| static const bool eliminated[] = { |
| false, false, false, false, false, false, false, false, false, true, true, true, false, true |
| }; |
| static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); |
| for (size_t i = 0; i != arraysize(eliminated); ++i) { |
| bool actually_eliminated = (static_cast<int>(mirs_[i].dalvikInsn.opcode) == kMirOpNop); |
| EXPECT_EQ(eliminated[i], actually_eliminated) << i; |
| } |
| // Check that the sregs have been renamed correctly. |
| ASSERT_EQ(1, mirs_[7].ssa_rep->num_defs); |
| EXPECT_EQ(13, mirs_[7].ssa_rep->defs[0]); // 7 -> 13 |
| ASSERT_EQ(2, mirs_[7].ssa_rep->num_uses); |
| EXPECT_EQ(6, mirs_[7].ssa_rep->uses[0]); |
| EXPECT_EQ(3, mirs_[7].ssa_rep->uses[1]); |
| ASSERT_EQ(1, mirs_[8].ssa_rep->num_defs); |
| EXPECT_EQ(8, mirs_[8].ssa_rep->defs[0]); |
| ASSERT_EQ(1, mirs_[8].ssa_rep->num_uses); |
| EXPECT_EQ(13, mirs_[8].ssa_rep->uses[0]); // 7 -> 13 |
| } |
| |
| TEST_F(GvnDeadCodeEliminationTestSimple, KeepChain1) { |
| // KillChain2 without the final CONST. |
| static const IFieldDef ifields[] = { |
| { 0u, 1u, 0u, false, kDexMemAccessWord }, |
| }; |
| static const MIRDef mirs[] = { |
| DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), |
| DEF_CONST(3, Instruction::CONST, 1u, 1000), |
| DEF_CONST(3, Instruction::CONST, 2u, 2000), |
| DEF_CONST(3, Instruction::CONST, 3u, 3000), |
| DEF_IGET(3, Instruction::IGET, 4u, 0u, 0u), |
| DEF_BINOP(3, Instruction::ADD_INT, 5u, 4u, 1u), |
| DEF_BINOP(3, Instruction::MUL_INT, 6u, 5u, 2u), |
| DEF_BINOP(3, Instruction::SUB_INT, 7u, 6u, 3u), |
| DEF_UNOP(3, Instruction::INT_TO_FLOAT, 8u, 7u), |
| DEF_IGET(3, Instruction::IGET, 9u, 0u, 0u), |
| DEF_BINOP(3, Instruction::ADD_INT, 10u, 9u, 1u), |
| DEF_BINOP(3, Instruction::MUL_INT, 11u, 10u, 2u), |
| DEF_BINOP(3, Instruction::SUB_INT, 12u, 11u, 3u), |
| }; |
| |
| static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 3, 4, 5, 5, 4, 6, 4, 7, 7, 4 }; |
| PrepareSRegToVRegMap(sreg_to_vreg_map); |
| |
| PrepareIFields(ifields); |
| PrepareMIRs(mirs); |
| PerformGVN_DCE(); |
| |
| ASSERT_EQ(arraysize(mirs), value_names_.size()); |
| static const size_t diff_indexes[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 }; |
| ExpectValueNamesNE(diff_indexes); |
| EXPECT_EQ(value_names_[4], value_names_[9]); |
| EXPECT_EQ(value_names_[5], value_names_[10]); |
| EXPECT_EQ(value_names_[6], value_names_[11]); |
| EXPECT_EQ(value_names_[7], value_names_[12]); |
| |
| const size_t no_null_ck_indexes[] = { 4, 9 }; |
| ExpectNoNullCheck(no_null_ck_indexes); |
| |
| static const bool eliminated[] = { |
| false, false, false, false, false, false, false, false, false, false, false, false, false |
| }; |
| static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); |
| for (size_t i = 0; i != arraysize(eliminated); ++i) { |
| bool actually_eliminated = (static_cast<int>(mirs_[i].dalvikInsn.opcode) == kMirOpNop); |
| EXPECT_EQ(eliminated[i], actually_eliminated) << i; |
| } |
| } |
| |
| TEST_F(GvnDeadCodeEliminationTestSimple, KeepChain2) { |
| // KillChain1 with MIRs in the middle of the chain. |
| static const IFieldDef ifields[] = { |
| { 0u, 1u, 0u, false, kDexMemAccessWord }, |
| }; |
| static const MIRDef mirs[] = { |
| DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), |
| DEF_CONST(3, Instruction::CONST, 1u, 1000), |
| DEF_CONST(3, Instruction::CONST, 2u, 2000), |
| DEF_CONST(3, Instruction::CONST, 3u, 3000), |
| DEF_IGET(3, Instruction::IGET, 4u, 0u, 0u), |
| DEF_BINOP(3, Instruction::ADD_INT, 5u, 4u, 1u), |
| DEF_BINOP(3, Instruction::MUL_INT, 6u, 5u, 2u), |
| DEF_BINOP(3, Instruction::SUB_INT, 7u, 6u, 3u), |
| DEF_UNOP(3, Instruction::INT_TO_FLOAT, 8u, 7u), |
| DEF_IGET(3, Instruction::IGET, 9u, 0u, 0u), |
| DEF_BINOP(3, Instruction::ADD_INT, 10u, 9u, 1u), |
| DEF_CONST(3, Instruction::CONST, 11u, 4000), |
| DEF_UNOP(3, Instruction::INT_TO_FLOAT, 12u, 11u), |
| DEF_BINOP(3, Instruction::MUL_INT, 13u, 10u, 2u), |
| DEF_BINOP(3, Instruction::SUB_INT, 14u, 13u, 3u), |
| }; |
| |
| static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 3, 4, 5, 4, 5, 6, 4, 5, 4, 7, 4, 5 }; |
| PrepareSRegToVRegMap(sreg_to_vreg_map); |
| |
| PrepareIFields(ifields); |
| PrepareMIRs(mirs); |
| PerformGVN_DCE(); |
| |
| ASSERT_EQ(arraysize(mirs), value_names_.size()); |
| static const size_t diff_indexes[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 }; |
| ExpectValueNamesNE(diff_indexes); |
| EXPECT_EQ(value_names_[4], value_names_[9]); |
| EXPECT_EQ(value_names_[5], value_names_[10]); |
| EXPECT_EQ(value_names_[6], value_names_[13]); |
| EXPECT_EQ(value_names_[7], value_names_[14]); |
| |
| const size_t no_null_ck_indexes[] = { 4, 9 }; |
| ExpectNoNullCheck(no_null_ck_indexes); |
| |
| static const bool eliminated[] = { |
| false, false, false, false, false, false, false, false, false, |
| false, false, false, false, false, false |
| }; |
| static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); |
| for (size_t i = 0; i != arraysize(eliminated); ++i) { |
| bool actually_eliminated = (static_cast<int>(mirs_[i].dalvikInsn.opcode) == kMirOpNop); |
| EXPECT_EQ(eliminated[i], actually_eliminated) << i; |
| } |
| } |
| |
| TEST_F(GvnDeadCodeEliminationTestDiamond, CreatePhi1) { |
| static const MIRDef mirs[] = { |
| DEF_CONST(3, Instruction::CONST, 0u, 1000), |
| DEF_CONST(4, Instruction::CONST, 1u, 1000), |
| }; |
| |
| static const int32_t sreg_to_vreg_map[] = { 0, 0 }; |
| PrepareSRegToVRegMap(sreg_to_vreg_map); |
| |
| PrepareMIRs(mirs); |
| PerformGVN_DCE(); |
| |
| ASSERT_EQ(arraysize(mirs), value_names_.size()); |
| EXPECT_EQ(value_names_[0], value_names_[1]); |
| |
| static const bool eliminated[] = { |
| false, true, |
| }; |
| static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); |
| for (size_t i = 0; i != arraysize(eliminated); ++i) { |
| bool actually_eliminated = (static_cast<int>(mirs_[i].dalvikInsn.opcode) == kMirOpNop); |
| EXPECT_EQ(eliminated[i], actually_eliminated) << i; |
| } |
| // Check that we've created a single-input Phi to replace the CONST 3u. |
| BasicBlock* bb4 = cu_.mir_graph->GetBasicBlock(4); |
| MIR* phi = bb4->first_mir_insn; |
| ASSERT_TRUE(phi != nullptr); |
| ASSERT_EQ(kMirOpPhi, static_cast<int>(phi->dalvikInsn.opcode)); |
| ASSERT_EQ(1, phi->ssa_rep->num_uses); |
| EXPECT_EQ(0, phi->ssa_rep->uses[0]); |
| ASSERT_EQ(1, phi->ssa_rep->num_defs); |
| EXPECT_EQ(1, phi->ssa_rep->defs[0]); |
| EXPECT_EQ(0u, phi->dalvikInsn.vA); |
| } |
| |
| TEST_F(GvnDeadCodeEliminationTestDiamond, CreatePhi2) { |
| static const IFieldDef ifields[] = { |
| { 0u, 1u, 0u, false, kDexMemAccessWord }, |
| }; |
| static const MIRDef mirs[] = { |
| DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), |
| DEF_CONST(4, Instruction::CONST, 1u, 1000), |
| DEF_IPUT(4, Instruction::IPUT, 1u, 0u, 0u), |
| DEF_CONST(5, Instruction::CONST, 3u, 2000), |
| DEF_IPUT(5, Instruction::IPUT, 3u, 0u, 0u), |
| DEF_IGET(6, Instruction::IGET, 5u, 0u, 0u), |
| }; |
| |
| static const int32_t sreg_to_vreg_map[] = { 0, 1, 2 /* dummy */, 1, 2 /* dummy */, 1 }; |
| PrepareSRegToVRegMap(sreg_to_vreg_map); |
| |
| PrepareIFields(ifields); |
| PrepareMIRs(mirs); |
| PerformGVN_DCE(); |
| |
| ASSERT_EQ(arraysize(mirs), value_names_.size()); |
| static const size_t diff_indexes[] = { 0, 1, 3, 5 }; |
| ExpectValueNamesNE(diff_indexes); |
| |
| const size_t no_null_ck_indexes[] = { 2, 4, 5 }; |
| ExpectNoNullCheck(no_null_ck_indexes); |
| |
| static const bool eliminated[] = { |
| false, false, false, false, false, true, |
| }; |
| static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); |
| for (size_t i = 0; i != arraysize(eliminated); ++i) { |
| bool actually_eliminated = (static_cast<int>(mirs_[i].dalvikInsn.opcode) == kMirOpNop); |
| EXPECT_EQ(eliminated[i], actually_eliminated) << i; |
| } |
| // Check that we've created a two-input Phi to replace the IGET 5u. |
| BasicBlock* bb6 = cu_.mir_graph->GetBasicBlock(6); |
| MIR* phi = bb6->first_mir_insn; |
| ASSERT_TRUE(phi != nullptr); |
| ASSERT_EQ(kMirOpPhi, static_cast<int>(phi->dalvikInsn.opcode)); |
| ASSERT_EQ(2, phi->ssa_rep->num_uses); |
| EXPECT_EQ(1, phi->ssa_rep->uses[0]); |
| EXPECT_EQ(3, phi->ssa_rep->uses[1]); |
| ASSERT_EQ(1, phi->ssa_rep->num_defs); |
| EXPECT_EQ(5, phi->ssa_rep->defs[0]); |
| EXPECT_EQ(1u, phi->dalvikInsn.vA); |
| } |
| |
| TEST_F(GvnDeadCodeEliminationTestDiamond, KillChainInAnotherBlock1) { |
| static const IFieldDef ifields[] = { |
| { 0u, 1u, 0u, false, kDexMemAccessObject }, // linked list |
| }; |
| static const MIRDef mirs[] = { |
| DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), |
| DEF_IGET(3, Instruction::IGET_OBJECT, 1u, 0u, 0u), |
| DEF_IGET(3, Instruction::IGET_OBJECT, 2u, 1u, 0u), |
| DEF_IGET(3, Instruction::IGET_OBJECT, 3u, 2u, 0u), |
| DEF_IGET(3, Instruction::IGET_OBJECT, 4u, 3u, 0u), |
| DEF_IFZ(3, Instruction::IF_NEZ, 4u), |
| DEF_IGET(4, Instruction::IGET_OBJECT, 6u, 0u, 0u), |
| DEF_IGET(4, Instruction::IGET_OBJECT, 7u, 6u, 0u), |
| DEF_IGET(4, Instruction::IGET_OBJECT, 8u, 7u, 0u), |
| DEF_IGET(4, Instruction::IGET_OBJECT, 9u, 8u, 0u), |
| }; |
| |
| static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 1, 2, 3 /* dummy */, 1, 2, 1, 2 }; |
| PrepareSRegToVRegMap(sreg_to_vreg_map); |
| |
| PrepareIFields(ifields); |
| PrepareMIRs(mirs); |
| PerformGVN_DCE(); |
| |
| ASSERT_EQ(arraysize(mirs), value_names_.size()); |
| static const size_t diff_indexes[] = { 0, 1, 2, 3, 4 }; |
| ExpectValueNamesNE(diff_indexes); |
| EXPECT_EQ(value_names_[1], value_names_[6]); |
| EXPECT_EQ(value_names_[2], value_names_[7]); |
| EXPECT_EQ(value_names_[3], value_names_[8]); |
| EXPECT_EQ(value_names_[4], value_names_[9]); |
| |
| const size_t no_null_ck_indexes[] = { 1, 6, 7, 8, 9 }; |
| ExpectNoNullCheck(no_null_ck_indexes); |
| |
| static const bool eliminated[] = { |
| false, false, false, false, false, false, true, true, true, true, |
| }; |
| static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); |
| for (size_t i = 0; i != arraysize(eliminated); ++i) { |
| bool actually_eliminated = (static_cast<int>(mirs_[i].dalvikInsn.opcode) == kMirOpNop); |
| EXPECT_EQ(eliminated[i], actually_eliminated) << i; |
| } |
| // Check that we've created two single-input Phis to replace the IGET 8u and IGET 9u; |
| // the IGET 6u and IGET 7u were killed without a replacement. |
| BasicBlock* bb4 = cu_.mir_graph->GetBasicBlock(4); |
| MIR* phi1 = bb4->first_mir_insn; |
| ASSERT_TRUE(phi1 != nullptr); |
| ASSERT_EQ(kMirOpPhi, static_cast<int>(phi1->dalvikInsn.opcode)); |
| MIR* phi2 = phi1->next; |
| ASSERT_TRUE(phi2 != nullptr); |
| ASSERT_EQ(kMirOpPhi, static_cast<int>(phi2->dalvikInsn.opcode)); |
| ASSERT_TRUE(phi2->next == &mirs_[6]); |
| if (phi1->dalvikInsn.vA == 2u) { |
| std::swap(phi1, phi2); |
| } |
| ASSERT_EQ(1, phi1->ssa_rep->num_uses); |
| EXPECT_EQ(3, phi1->ssa_rep->uses[0]); |
| ASSERT_EQ(1, phi1->ssa_rep->num_defs); |
| EXPECT_EQ(8, phi1->ssa_rep->defs[0]); |
| EXPECT_EQ(1u, phi1->dalvikInsn.vA); |
| ASSERT_EQ(1, phi2->ssa_rep->num_uses); |
| EXPECT_EQ(4, phi2->ssa_rep->uses[0]); |
| ASSERT_EQ(1, phi2->ssa_rep->num_defs); |
| EXPECT_EQ(9, phi2->ssa_rep->defs[0]); |
| EXPECT_EQ(2u, phi2->dalvikInsn.vA); |
| } |
| |
| TEST_F(GvnDeadCodeEliminationTestDiamond, KillChainInAnotherBlock2) { |
| static const IFieldDef ifields[] = { |
| { 0u, 1u, 0u, false, kDexMemAccessObject }, // linked list |
| }; |
| static const MIRDef mirs[] = { |
| DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), |
| DEF_IGET(3, Instruction::IGET_OBJECT, 1u, 0u, 0u), |
| DEF_IGET(3, Instruction::IGET_OBJECT, 2u, 1u, 0u), |
| DEF_IGET(3, Instruction::IGET_OBJECT, 3u, 2u, 0u), |
| DEF_IGET(3, Instruction::IGET_OBJECT, 4u, 3u, 0u), |
| DEF_IFZ(3, Instruction::IF_NEZ, 4u), |
| DEF_IGET(4, Instruction::IGET_OBJECT, 6u, 0u, 0u), |
| DEF_IGET(4, Instruction::IGET_OBJECT, 7u, 6u, 0u), |
| DEF_IGET(4, Instruction::IGET_OBJECT, 8u, 7u, 0u), |
| DEF_CONST(4, Instruction::CONST, 9u, 1000), |
| }; |
| |
| static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 1, 2, 3 /* dummy */, 1, 2, 1, 2 }; |
| PrepareSRegToVRegMap(sreg_to_vreg_map); |
| |
| PrepareIFields(ifields); |
| PrepareMIRs(mirs); |
| PerformGVN_DCE(); |
| |
| ASSERT_EQ(arraysize(mirs), value_names_.size()); |
| static const size_t diff_indexes[] = { 0, 1, 2, 3, 4, 9 }; |
| ExpectValueNamesNE(diff_indexes); |
| EXPECT_EQ(value_names_[1], value_names_[6]); |
| EXPECT_EQ(value_names_[2], value_names_[7]); |
| EXPECT_EQ(value_names_[3], value_names_[8]); |
| |
| const size_t no_null_ck_indexes[] = { 1, 6, 7, 8 }; |
| ExpectNoNullCheck(no_null_ck_indexes); |
| |
| static const bool eliminated[] = { |
| false, false, false, false, false, false, true, true, true, false, |
| }; |
| static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); |
| for (size_t i = 0; i != arraysize(eliminated); ++i) { |
| bool actually_eliminated = (static_cast<int>(mirs_[i].dalvikInsn.opcode) == kMirOpNop); |
| EXPECT_EQ(eliminated[i], actually_eliminated) << i; |
| } |
| // Check that we've created a single-input Phi to replace the IGET 8u; |
| // the IGET 6u and IGET 7u were killed without a replacement. |
| BasicBlock* bb4 = cu_.mir_graph->GetBasicBlock(4); |
| MIR* phi = bb4->first_mir_insn; |
| ASSERT_TRUE(phi != nullptr); |
| ASSERT_EQ(kMirOpPhi, static_cast<int>(phi->dalvikInsn.opcode)); |
| ASSERT_TRUE(phi->next == &mirs_[6]); |
| ASSERT_EQ(1, phi->ssa_rep->num_uses); |
| EXPECT_EQ(3, phi->ssa_rep->uses[0]); |
| ASSERT_EQ(1, phi->ssa_rep->num_defs); |
| EXPECT_EQ(8, phi->ssa_rep->defs[0]); |
| EXPECT_EQ(1u, phi->dalvikInsn.vA); |
| } |
| |
| TEST_F(GvnDeadCodeEliminationTestLoop, IFieldLoopVariable) { |
| static const IFieldDef ifields[] = { |
| { 0u, 1u, 0u, false, kDexMemAccessWord }, |
| }; |
| static const MIRDef mirs[] = { |
| DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), |
| DEF_CONST(3, Instruction::CONST, 1u, 1), |
| DEF_CONST(3, Instruction::CONST, 2u, 0), |
| DEF_IPUT(3, Instruction::IPUT, 2u, 0u, 0u), |
| DEF_IGET(4, Instruction::IGET, 4u, 0u, 0u), |
| DEF_BINOP(4, Instruction::ADD_INT, 5u, 4u, 1u), |
| DEF_IPUT(4, Instruction::IPUT, 5u, 0u, 0u), |
| }; |
| |
| static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 3 /* dummy */, 2, 2 }; |
| PrepareSRegToVRegMap(sreg_to_vreg_map); |
| |
| PrepareIFields(ifields); |
| PrepareMIRs(mirs); |
| PerformGVN_DCE(); |
| |
| ASSERT_EQ(arraysize(mirs), value_names_.size()); |
| static const size_t diff_indexes[] = { 0, 1, 2, 4, 5 }; |
| ExpectValueNamesNE(diff_indexes); |
| |
| const size_t no_null_ck_indexes[] = { 3, 4, 6 }; |
| ExpectNoNullCheck(no_null_ck_indexes); |
| |
| static const bool eliminated[] = { |
| false, false, false, false, true, false, false, |
| }; |
| static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); |
| for (size_t i = 0; i != arraysize(eliminated); ++i) { |
| bool actually_eliminated = (static_cast<int>(mirs_[i].dalvikInsn.opcode) == kMirOpNop); |
| EXPECT_EQ(eliminated[i], actually_eliminated) << i; |
| } |
| // Check that we've created a two-input Phi to replace the IGET 3u. |
| BasicBlock* bb4 = cu_.mir_graph->GetBasicBlock(4); |
| MIR* phi = bb4->first_mir_insn; |
| ASSERT_TRUE(phi != nullptr); |
| ASSERT_EQ(kMirOpPhi, static_cast<int>(phi->dalvikInsn.opcode)); |
| ASSERT_TRUE(phi->next == &mirs_[4]); |
| ASSERT_EQ(2, phi->ssa_rep->num_uses); |
| EXPECT_EQ(2, phi->ssa_rep->uses[0]); |
| EXPECT_EQ(5, phi->ssa_rep->uses[1]); |
| ASSERT_EQ(1, phi->ssa_rep->num_defs); |
| EXPECT_EQ(4, phi->ssa_rep->defs[0]); |
| EXPECT_EQ(2u, phi->dalvikInsn.vA); |
| } |
| |
| } // namespace art |