| /* |
| * Copyright (C) 2014 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 "global_value_numbering.h" |
| |
| #include "base/bit_vector-inl.h" |
| #include "base/stl_util.h" |
| #include "local_value_numbering.h" |
| |
| namespace art { |
| |
| GlobalValueNumbering::GlobalValueNumbering(CompilationUnit* cu, ScopedArenaAllocator* allocator, |
| Mode mode) |
| : cu_(cu), |
| mir_graph_(cu->mir_graph.get()), |
| allocator_(allocator), |
| bbs_processed_(0u), |
| max_bbs_to_process_(kMaxBbsToProcessMultiplyFactor * mir_graph_->GetNumReachableBlocks()), |
| last_value_(kNullValue), |
| modifications_allowed_(true), |
| mode_(mode), |
| global_value_map_(std::less<uint64_t>(), allocator->Adapter()), |
| array_location_map_(ArrayLocationComparator(), allocator->Adapter()), |
| array_location_reverse_map_(allocator->Adapter()), |
| ref_set_map_(std::less<ValueNameSet>(), allocator->Adapter()), |
| lvns_(mir_graph_->GetNumBlocks(), nullptr, allocator->Adapter()), |
| work_lvn_(nullptr), |
| merge_lvns_(allocator->Adapter()) { |
| } |
| |
| GlobalValueNumbering::~GlobalValueNumbering() { |
| STLDeleteElements(&lvns_); |
| } |
| |
| LocalValueNumbering* GlobalValueNumbering::PrepareBasicBlock(BasicBlock* bb, |
| ScopedArenaAllocator* allocator) { |
| if (UNLIKELY(!Good())) { |
| return nullptr; |
| } |
| if (bb->block_type != kDalvikByteCode && bb->block_type != kEntryBlock) { |
| DCHECK(bb->first_mir_insn == nullptr); |
| return nullptr; |
| } |
| if (mode_ == kModeGvn && UNLIKELY(bbs_processed_ == max_bbs_to_process_)) { |
| // If we're still trying to converge, stop now. Otherwise, proceed to apply optimizations. |
| last_value_ = kNoValue; // Make bad. |
| return nullptr; |
| } |
| if (mode_ == kModeGvnPostProcessing && |
| mir_graph_->GetTopologicalSortOrderLoopHeadStack()->empty()) { |
| // Modifications outside loops are performed during the main phase. |
| return nullptr; |
| } |
| if (allocator == nullptr) { |
| allocator = allocator_; |
| } |
| DCHECK(work_lvn_.get() == nullptr); |
| work_lvn_.reset(new (allocator) LocalValueNumbering(this, bb->id, allocator)); |
| if (bb->block_type == kEntryBlock) { |
| work_lvn_->PrepareEntryBlock(); |
| DCHECK(bb->first_mir_insn == nullptr); // modifications_allowed_ is irrelevant. |
| } else { |
| // To avoid repeated allocation on the ArenaStack, reuse a single vector kept as a member. |
| DCHECK(merge_lvns_.empty()); |
| // If we're running the full GVN, the RepeatingTopologicalSortIterator keeps the loop |
| // head stack in the MIRGraph up to date and for a loop head we need to check whether |
| // we're making the initial computation and need to merge only preceding blocks in the |
| // topological order, or we're recalculating a loop head and need to merge all incoming |
| // LVNs. When we're not at a loop head (including having an empty loop head stack) all |
| // predecessors should be preceding blocks and we shall merge all of them anyway. |
| bool use_all_predecessors = true; |
| uint16_t loop_head_idx = 0u; // Used only if !use_all_predecessors. |
| if (mode_ == kModeGvn && mir_graph_->GetTopologicalSortOrderLoopHeadStack()->size() != 0) { |
| // Full GVN inside a loop, see if we're at the loop head for the first time. |
| modifications_allowed_ = false; |
| auto top = mir_graph_->GetTopologicalSortOrderLoopHeadStack()->back(); |
| loop_head_idx = top.first; |
| bool recalculating = top.second; |
| use_all_predecessors = recalculating || |
| loop_head_idx != mir_graph_->GetTopologicalSortOrderIndexes()[bb->id]; |
| } else { |
| modifications_allowed_ = true; |
| } |
| for (BasicBlockId pred_id : bb->predecessors) { |
| DCHECK_NE(pred_id, NullBasicBlockId); |
| if (lvns_[pred_id] != nullptr && |
| (use_all_predecessors || |
| mir_graph_->GetTopologicalSortOrderIndexes()[pred_id] < loop_head_idx)) { |
| merge_lvns_.push_back(lvns_[pred_id]); |
| } |
| } |
| // Determine merge type. |
| LocalValueNumbering::MergeType merge_type = LocalValueNumbering::kNormalMerge; |
| if (bb->catch_entry) { |
| merge_type = LocalValueNumbering::kCatchMerge; |
| } else if (bb->last_mir_insn != nullptr && |
| IsInstructionReturn(bb->last_mir_insn->dalvikInsn.opcode) && |
| bb->GetFirstNonPhiInsn() == bb->last_mir_insn) { |
| merge_type = LocalValueNumbering::kReturnMerge; |
| } |
| // At least one predecessor must have been processed before this bb. |
| CHECK(!merge_lvns_.empty()); |
| if (merge_lvns_.size() == 1u) { |
| work_lvn_->MergeOne(*merge_lvns_[0], merge_type); |
| } else { |
| work_lvn_->Merge(merge_type); |
| } |
| } |
| return work_lvn_.get(); |
| } |
| |
| bool GlobalValueNumbering::FinishBasicBlock(BasicBlock* bb) { |
| DCHECK(work_lvn_ != nullptr); |
| DCHECK_EQ(bb->id, work_lvn_->Id()); |
| ++bbs_processed_; |
| merge_lvns_.clear(); |
| |
| bool change = (lvns_[bb->id] == nullptr) || !lvns_[bb->id]->Equals(*work_lvn_); |
| if (mode_ == kModeGvn) { |
| // In GVN mode, keep the latest LVN even if Equals() indicates no change. This is |
| // to keep the correct values of fields that do not contribute to Equals() as long |
| // as they depend only on predecessor LVNs' fields that do contribute to Equals(). |
| // Currently, that's LVN::merge_map_ used by LVN::GetStartingVregValueNumberImpl(). |
| std::unique_ptr<const LocalValueNumbering> old_lvn(lvns_[bb->id]); |
| lvns_[bb->id] = work_lvn_.release(); |
| } else { |
| work_lvn_.reset(); |
| } |
| return change; |
| } |
| |
| uint16_t GlobalValueNumbering::GetArrayLocation(uint16_t base, uint16_t index) { |
| auto cmp = array_location_map_.key_comp(); |
| ArrayLocation key = { base, index }; |
| auto lb = array_location_map_.lower_bound(key); |
| if (lb != array_location_map_.end() && !cmp(key, lb->first)) { |
| return lb->second; |
| } |
| uint16_t location = static_cast<uint16_t>(array_location_reverse_map_.size()); |
| DCHECK_EQ(location, array_location_reverse_map_.size()); // No overflow. |
| auto it = array_location_map_.PutBefore(lb, key, location); |
| array_location_reverse_map_.push_back(&*it); |
| return location; |
| } |
| |
| bool GlobalValueNumbering::HasNullCheckLastInsn(const BasicBlock* pred_bb, |
| BasicBlockId succ_id) { |
| if (pred_bb->block_type != kDalvikByteCode || pred_bb->last_mir_insn == nullptr) { |
| return false; |
| } |
| Instruction::Code last_opcode = pred_bb->last_mir_insn->dalvikInsn.opcode; |
| return ((last_opcode == Instruction::IF_EQZ && pred_bb->fall_through == succ_id) || |
| (last_opcode == Instruction::IF_NEZ && pred_bb->taken == succ_id)); |
| } |
| |
| bool GlobalValueNumbering::NullCheckedInAllPredecessors( |
| const ScopedArenaVector<uint16_t>& merge_names) const { |
| // Implicit parameters: |
| // - *work_lvn: the LVN for which we're checking predecessors. |
| // - merge_lvns_: the predecessor LVNs. |
| DCHECK_EQ(merge_lvns_.size(), merge_names.size()); |
| for (size_t i = 0, size = merge_lvns_.size(); i != size; ++i) { |
| const LocalValueNumbering* pred_lvn = merge_lvns_[i]; |
| uint16_t value_name = merge_names[i]; |
| if (!pred_lvn->IsValueNullChecked(value_name)) { |
| // Check if the predecessor has an IF_EQZ/IF_NEZ as the last insn. |
| const BasicBlock* pred_bb = mir_graph_->GetBasicBlock(pred_lvn->Id()); |
| if (!HasNullCheckLastInsn(pred_bb, work_lvn_->Id())) { |
| return false; |
| } |
| // IF_EQZ/IF_NEZ checks some sreg, see if that sreg contains the value_name. |
| int s_reg = pred_bb->last_mir_insn->ssa_rep->uses[0]; |
| if (pred_lvn->GetSregValue(s_reg) != value_name) { |
| return false; |
| } |
| } |
| } |
| return true; |
| } |
| |
| bool GlobalValueNumbering::DivZeroCheckedInAllPredecessors( |
| const ScopedArenaVector<uint16_t>& merge_names) const { |
| // Implicit parameters: |
| // - *work_lvn: the LVN for which we're checking predecessors. |
| // - merge_lvns_: the predecessor LVNs. |
| DCHECK_EQ(merge_lvns_.size(), merge_names.size()); |
| for (size_t i = 0, size = merge_lvns_.size(); i != size; ++i) { |
| const LocalValueNumbering* pred_lvn = merge_lvns_[i]; |
| uint16_t value_name = merge_names[i]; |
| if (!pred_lvn->IsValueDivZeroChecked(value_name)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| bool GlobalValueNumbering::IsBlockEnteredOnTrue(uint16_t cond, BasicBlockId bb_id) { |
| DCHECK_NE(cond, kNoValue); |
| BasicBlock* bb = mir_graph_->GetBasicBlock(bb_id); |
| if (bb->predecessors.size() == 1u) { |
| BasicBlockId pred_id = bb->predecessors[0]; |
| BasicBlock* pred_bb = mir_graph_->GetBasicBlock(pred_id); |
| if (pred_bb->last_mir_insn != nullptr) { |
| Instruction::Code opcode = pred_bb->last_mir_insn->dalvikInsn.opcode; |
| if ((opcode == Instruction::IF_NEZ && pred_bb->taken == bb_id) || |
| (opcode == Instruction::IF_EQZ && pred_bb->fall_through == bb_id)) { |
| DCHECK(lvns_[pred_id] != nullptr); |
| uint16_t operand = lvns_[pred_id]->GetSregValue(pred_bb->last_mir_insn->ssa_rep->uses[0]); |
| if (operand == cond) { |
| return true; |
| } |
| } |
| } |
| } |
| return false; |
| } |
| |
| bool GlobalValueNumbering::IsTrueInBlock(uint16_t cond, BasicBlockId bb_id) { |
| // We're not doing proper value propagation, so just see if the condition is used |
| // with if-nez/if-eqz to branch/fall-through to this bb or one of its dominators. |
| DCHECK_NE(cond, kNoValue); |
| if (IsBlockEnteredOnTrue(cond, bb_id)) { |
| return true; |
| } |
| BasicBlock* bb = mir_graph_->GetBasicBlock(bb_id); |
| for (uint32_t dom_id : bb->dominators->Indexes()) { |
| if (IsBlockEnteredOnTrue(cond, dom_id)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| } // namespace art |