Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  control_flow_simplifier.cc

  Sprache: C
 

/*
 * Copyright (C) 2016 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 "control_flow_simplifier.h"

#include "com_android_art_rw_flags.h"
#include "common_dominator.h"
#include "optimizing/nodes.h"
#include "reference_type_propagation.h"

namespace art HIDDEN {

static constexpr size_t kMaxInstructionsInBranch = 1u;

HControlFlowSimplifier::HControlFlowSimplifier(HGraph* graph,
                                               OptimizingCompilerStats* stats,
                                               const char* name)
    : HOptimization(graph, name, stats) {
}

bool HControlFlowSimplifier::ReturnSinking() {
  HBasicBlock* exit = graph_->GetExitBlock();
  if (exit == nullptr) {
    return false;  // No exit block, only infinite loop(s).
  }

  size_t number_of_returns = 0u;
  bool saw_return = false;
  for (HBasicBlock* pred : exit->GetPredecessors()) {
    // TODO(solanes): We might have Return/ReturnVoid->TryBoundary->Exit. We can theoretically
    // handle them and move them out of the TryBoundary. However, it is a border case and it adds
    // codebase complexity.
    if (pred->GetLastInstruction()->IsReturn() || pred->GetLastInstruction()->IsReturnVoid()) {
      DCHECK(!pred->IsInLoop());  // Return can be in a loop only if it's in a try-block.
      saw_return |= pred->GetLastInstruction()->IsReturn();
      ++number_of_returns;
    }
  }

  if (number_of_returns < 2) {
    // Nothing to do.
    return false;
  }

  // `new_block` will coalesce the Return instructions into Phi+Return,
  // or the ReturnVoid instructions into a ReturnVoid.
  HBasicBlock* new_block = nullptr;
  HInstruction::InstructionKind return_kind =
      saw_return ? HInstruction::kReturn : HInstruction::kReturnVoid;
  HReturn* first_return = nullptr;  // The first `HReturn` shall be reused by the new block.
  HPhi* new_phi = nullptr;
  size_t new_phi_input_index = 0u;
  ArrayRef<HBasicBlock* const> rpo(graph_->GetReversePostOrder());
  size_t rpo_insert_index = 0u;
  for (size_t rpo_index : Range(rpo.size())) {
    HBasicBlock* pred = rpo[rpo_index];
    HInstruction* last_inst = pred->GetLastInstruction();
    DCHECK_IMPLIES(last_inst == nullptr, pred == exit);
    if (last_inst == nullptr ||                 // Exit block?
        last_inst->GetKind() != return_kind ||  // Not `HReturn`/`HReturnVoid`?
        pred->GetSingleSuccessor() != exit) {   // Leading to try boundary instead of `exit`?
      continue;
    }
    if (new_block == nullptr) {
      new_block = pred->SplitBefore(last_inst);
      if (saw_return) {
        first_return = last_inst->AsReturn();
        // Create the `new_phi`. We do it here since we need to know the type to assign to it.
        new_phi = new (graph_->GetAllocator()) HPhi(
            graph_->GetAllocator(),
            kNoRegNumber,
            /*number_of_inputs=*/ number_of_returns,
            DataType::Kind(first_return->InputAt(0)->GetType()));
        new_phi->SetRawInputAt(new_phi_input_index, first_return->InputAt(0));
        ++new_phi_input_index;
      }
    } else {
      pred->ReplaceSuccessor(exit, new_block);
      if (saw_return) {
        DCHECK(new_phi != nullptr);
        new_phi->SetRawInputAt(new_phi_input_index, last_inst->AsReturn()->InputAt(0));
        ++new_phi_input_index;
      }
      pred->ReplaceAndRemoveInstructionWith(
          last_inst, new (graph_->GetAllocator()) HGoto(last_inst->GetDexPc()));
    }
    rpo_insert_index = rpo_index + 1u;
  }
  if (saw_return) {
    DCHECK_EQ(number_of_returns, new_phi_input_index);
    DCHECK(new_phi != nullptr);
    new_block->AddPhi(new_phi);
    DCHECK(first_return != nullptr);
    first_return->ReplaceInput(new_phi, 0);
  }

  graph_->reverse_post_order_.insert(graph_->reverse_post_order_.begin() + rpo_insert_index,
                                     new_block);
  if (exit->GetPredecessors().size() == 1u) {
    HBasicBlock* dominator = exit->GetDominator();
    dominator->RemoveDominatedBlock(exit);
    new_block->SetDominator(dominator);
    dominator->AddDominatedBlock(new_block);
    exit->SetDominator(new_block);
    new_block->AddDominatedBlock(exit);
  } else {
    ArrayRef<HBasicBlock* const> predecessors(new_block->GetPredecessors());
    CommonDominator dominator(predecessors[0]);
    for (HBasicBlock* pred : predecessors.SubArray(/*pos=*/ 1u)) {
      dominator.Update(pred);
    }
    new_block->SetDominator(dominator.Get());
    dominator.Get()->AddDominatedBlock(new_block);
  }
  return true;
}

bool HControlFlowSimplifier::TrySimplifyPackedSwitch(HBasicBlock* block,
                                                     ScopedArenaAllocator* allocator) {
  DCHECK(!block->GetInstructions().IsEmpty());
  DCHECK(block->GetLastInstruction()->IsPackedSwitch());
  HPackedSwitch* packed_switch = block->GetLastInstruction()->AsPackedSwitch();

  // Defensively check if we have at least 4 entries. This should be true as long as we build
  // a decision tree for smaller switches, see `DexSwitchTable::ShouldBuildDecisionTree()`.
  static constexpr size_t kMinEntries = 4u;
  size_t num_entries = packed_switch->GetNumEntries();
  if (num_entries < kMinEntries) {
    return false;
  }

  // Check if all non-default successors are single-goto blocks merging at the same block.
  ArrayRef<HBasicBlock* const> successors(block->GetSuccessors());
  DCHECK_EQ(num_entries + 1u, successors.size());
  if (!successors[0]->IsSingleGoto()) {
    return false;
  }
  HBasicBlock* merge = successors[0]->GetSingleSuccessor();
  for (size_t i : Range(1, num_entries)) {
    if (!successors[i]->IsSingleGoto() || successors[i]->GetSingleSuccessor() != merge) {
      return false;
    }
  }

  // Check if the `merge` has at most one Phi.
  HPhi* phi = nullptr;
  if (merge->GetPhis().IsEmpty()) {
    // We won't even need a constant table load to simplify this.
  } else if (merge->HasSinglePhi()) {
    phi = merge->GetFirstPhi()->AsPhi();
    DCHECK(phi != nullptr);
  } else {
    return false;  // Do not simplify when there are two or more phis.
  }

  auto get_value = [](HInstruction* input, int64_t* value) {
    if (input->IsIntConstant()) {
      *value = input->AsIntConstant()->GetValue();
    } else if (input->IsLongConstant()) {
      *value = input->AsLongConstant()->GetValue();
    } else if (input->IsFloatConstant()) {
      *value = input->AsFloatConstant()->GetValueAsUint64();
    } else if (input->IsDoubleConstant()) {
      *value = input->AsDoubleConstant()->GetValueAsUint64();
    } else {
      return false;
    }
    return true;
  };

  // Check if the default case can be included in the simplified pattern.
  HBasicBlock* default_block = successors[num_entries];
  int64_t default_value = 0;
  bool with_default =
      default_block->IsSingleGoto() &&
      default_block->GetSingleSuccessor() == merge &&
      (phi == nullptr || get_value(phi->InputAt(merge->GetPredecessorIndexOf(default_block)),
                                   &default_value));
  bool with_default_index_0 = with_default && packed_switch->GetStartValue() == 1;

  ArrayRef<int64_t> entries;
  if (phi != nullptr) {
    // Check if all the phi's non-default inputs are constants and collect the values.
    size_t size = num_entries + (with_default ? 1u : 0u);
    entries = ArrayRef<int64_t>(
        allocator->AllocArray<int64_t>(size, kArenaAllocControlFlowSimplifier), size);
    ArrayRef<int64_t> non_default_entries =
        entries.SubArray(with_default_index_0 ? 1u : 0u, num_entries);
    for (size_t i : Range(num_entries)) {
      size_t predecessor_index = merge->GetPredecessorIndexOf(successors[i]);
      if (!get_value(phi->InputAt(predecessor_index), &non_default_entries[i])) {
        return false;
      }
    }
    if (with_default) {
      entries[with_default_index_0 ? 0u : num_entries] = default_value;
    }
  }

  // Determine table type.
  DataType::Type table_type = (phi != nullptr) ? phi->GetType() : DataType::Type::kVoid;
  if (DataType::Kind(table_type) == DataType::Type::kInt32) {
    // Try to narrow down the type to save space.
    auto [min_it, max_it] = std::minmax_element(entries.begin(), entries.end());
    DCHECK_GE(*min_it, std::numeric_limits<int32_t>::min());
    DCHECK_LE(*max_it, std::numeric_limits<int32_t>::max());
    bool has_negative = (*min_it < 0);
    int max_value_to_encode = has_negative
        ? dchecked_integral_cast<int32_t>(std::max(*max_it, -1 - *min_it))
        : dchecked_integral_cast<int32_t>(*max_it);
    if (!has_negative && max_value_to_encode <= 1) {
      table_type = DataType::Type::kBool;
    } else if (max_value_to_encode <= std::numeric_limits<int8_t>::max()) {
      table_type = DataType::Type::kInt8;
    } else if (!has_negative && max_value_to_encode <= std::numeric_limits<uint8_t>::max()) {
      table_type = DataType::Type::kUint8;
    } else if (max_value_to_encode <= std::numeric_limits<int16_t>::max()) {
      table_type = DataType::Type::kInt16;
    } else if (!has_negative && max_value_to_encode <= std::numeric_limits<uint16_t>::max()) {
      table_type = DataType::Type::kUint16;
    } else {
      DCHECK_EQ(table_type, DataType::Type::kInt32);  // Keep the `kInt32`.
    }
  }

  // Prepare the index calculation.
  uint32_t dex_pc = packed_switch->GetDexPc();
  HInstruction* index = packed_switch->InputAt(0);
  if (packed_switch->GetStartValue() != 0 && !with_default_index_0) {
    HInstruction* addend = graph_->GetIntConstant(-packed_switch->GetStartValue());
    index = new (graph_->GetAllocator()) HAdd(DataType::Type::kInt32, index, addend, dex_pc);
    block->InsertInstructionBefore(index, packed_switch);
  }
  // Prepare the comparison with the upper bound.
  HInstruction* bound = graph_->GetIntConstant(num_entries + (with_default_index_0 ? 1 : 0));
  HBelow* below = new (graph_->GetAllocator()) HBelow(index, bound, dex_pc);
  block->InsertInstructionBefore(below, packed_switch);
  if (with_default) {
    // Insert `HSelect` to clamp the index.
    HInstruction* false_value = graph_->GetIntConstant(with_default_index_0 ? 0 : num_entries);
    index = new (graph_->GetAllocator()) HSelect(below, index, false_value, dex_pc);
    block->InsertInstructionBefore(index, packed_switch);
  }
  if (phi != nullptr) {
    // Insert constant table load and update the phi input we intend to keep.
    HLoadConstantTableEntry* load = new (graph_->GetAllocator()) HLoadConstantTableEntry(
        table_type, index, ArrayRef<const int64_t>(entries), graph_->GetAllocator(), dex_pc);
    if (with_default) {
      block->InsertInstructionBefore(load, packed_switch);
    } else {
      successors[0]->InsertInstructionBefore(load, successors[0]->GetFirstInstruction());
    }
    phi->ReplaceInput(load, merge->GetPredecessorIndexOf(successors[0]));
  }
  // Remove the default successor if `with_default` and all non-default successors,
  // except the first. This also removes `phi` inputs and replaces the Phi with the
  // `load` if it's the only remaining input.
  // Note: Stop using `successors` as the underlying vector is being modified.
  for (size_t i : Range(with_default ? 0u : 1u, num_entries)) {
    block->GetSuccessors()[num_entries - i]->DisconnectAndDelete();
  }
  // If the merge block has only one predecessor now, update domination info and merge.
  if (merge->GetPredecessors().size() == 1u) {
    HBasicBlock* remaining_block = block->GetSuccessors()[0];
    DCHECK(remaining_block == merge->GetSinglePredecessor());
    DCHECK(merge->GetDominator() == block);
    block->RemoveDominatedBlock(merge);
    merge->SetDominator(remaining_block);
    remaining_block->AddDominatedBlock(merge);
    remaining_block->MergeWith(merge);
  }
  if (with_default) {
    // Merge `block` with its remaining successor.
    DCHECK(block->GetLastInstruction()->IsGoto());  // Updated by last `DisconnectAndDelete()`.
    block->MergeWith(block->GetSingleSuccessor());
  } else {
    // Replace the switch with `HBelow` and `HIf`.
    DCHECK_EQ(2u, block->GetSuccessors().size());
    HIf* if_ = new (graph_->GetAllocator()) HIf(below, dex_pc);
    block->ReplaceAndRemoveInstructionWith(packed_switch, if_);
  }

  MaybeRecordStat(stats_, MethodCompilationStat::kControlFlowSwitchSimplified);
  return true;
}

// Returns true if `block` has only one predecessor, ends with a Goto
// or a Return and contains at most `kMaxInstructionsInBranch` other
// movable instruction with no side-effects.
static bool IsSimpleBlock(HBasicBlock* block) {
  if (block->GetPredecessors().size() != 1u) {
    return false;
  }
  DCHECK(block->GetPhis().IsEmpty());

  size_t num_instructions = 0u;
  for (HInstructionIteratorPrefetchNext it(block->GetInstructions()); !it.Done(); it.Advance()) {
    HInstruction* instruction = it.Current();
    if (instruction->IsControlFlow()) {
      return instruction->IsGoto() || instruction->IsReturn();
    } else if (instruction->CanBeMoved() &&
               !instruction->HasSideEffects() &&
               !instruction->CanThrow()) {
      if (instruction->IsSelect() && instruction->AsSelect()->GetCondition()->GetBlock() == block) {
        // Count one HCondition and HSelect in the same block as a single instruction.
        // This enables finding nested selects.
        continue;
      } else if (++num_instructions > kMaxInstructionsInBranch) {
        return false;  // bail as soon as we exceed number of allowed instructions
      }
    } else {
      return false;
    }
  }

  LOG(FATAL) << "Unreachable";
  UNREACHABLE();
}

// Returns true if 'block1' and 'block2' are empty and merge into the
// same single successor.
static bool BlocksMergeTogether(HBasicBlock* block1, HBasicBlock* block2) {
  return block1->GetSingleSuccessor() == block2->GetSingleSuccessor();
}

// Search `block` for phis that have different inputs at `index1` and `index2`.
// If none is found, returns `{true, nullptr}`.
// If exactly one such `phi` is found, returns `{true, phi}`.
// Otherwise (if more than one such phi is found), returns `{false, nullptr}`.
static std::pair<bool, HPhi*> HasAtMostOnePhiWithDifferentInputs(HBasicBlock* block,
                                                                 size_t index1,
                                                                 size_t index2) {
  DCHECK_NE(index1, index2);

  HPhi* select_phi = nullptr;
  for (HInstructionIteratorPrefetchNext it(block->GetPhis()); !it.Done(); it.Advance()) {
    HPhi* phi = it.Current()->AsPhi();
    auto&& inputs = phi->GetInputs();
    if (inputs[index1] == inputs[index2]) {
      continue;
    }
    if (select_phi == nullptr) {
      // First phi found.
      select_phi = phi;
    } else {
      // More than one phi found, return null.
      return {false, nullptr};
    }
  }
  return {true, select_phi};
}

bool HControlFlowSimplifier::TryGenerateSelectSimpleDiamondPattern(
    HBasicBlock* block, ScopedArenaSafeMap<HInstruction*, HSelect*>* cache) {
  DCHECK(block->GetLastInstruction()->IsIf());
  HIf* if_instruction = block->GetLastInstruction()->AsIf();
  HBasicBlock* true_block = if_instruction->IfTrueSuccessor();
  HBasicBlock* false_block = if_instruction->IfFalseSuccessor();
  DCHECK_NE(true_block, false_block);

  if (!IsSimpleBlock(true_block) ||
      !IsSimpleBlock(false_block) ||
      !BlocksMergeTogether(true_block, false_block)) {
    return false;
  }
  HBasicBlock* merge_block = true_block->GetSingleSuccessor();

  // If the branches are not empty, move instructions in front of the If.
  // TODO(dbrazdil): This puts an instruction between If and its condition.
  //                 Implement moving of conditions to first users if possible.
  while (!true_block->IsSingleGoto() && !true_block->IsSingleReturn()) {
    HInstruction* instr = true_block->GetFirstInstruction();
    DCHECK(!instr->CanThrow());
    instr->MoveBefore(if_instruction);
  }
  while (!false_block->IsSingleGoto() && !false_block->IsSingleReturn()) {
    HInstruction* instr = false_block->GetFirstInstruction();
    DCHECK(!instr->CanThrow());
    instr->MoveBefore(if_instruction);
  }
  DCHECK(true_block->IsSingleGoto() || true_block->IsSingleReturn());
  DCHECK(false_block->IsSingleGoto() || false_block->IsSingleReturn());

  // Find the resulting true/false values.
  size_t predecessor_index_true = merge_block->GetPredecessorIndexOf(true_block);
  size_t predecessor_index_false = merge_block->GetPredecessorIndexOf(false_block);
  DCHECK_NE(predecessor_index_true, predecessor_index_false);

  bool both_successors_return = true_block->IsSingleReturn() && false_block->IsSingleReturn();
  // TODO(solanes): Extend to support multiple phis? e.g.
  //   int a, b;
  //   if (bool) {
  //     a = 0; b = 1;
  //   } else {
  //     a = 1; b = 2;
  //   }
  //   // use a and b
  bool at_most_one_phi_with_different_inputs = false;
  HPhi* phi = nullptr;
  HInstruction* true_value = nullptr;
  HInstruction* false_value = nullptr;
  if (both_successors_return) {
    // Note: This can create a select with the same then-value and else-value.
    true_value = true_block->GetFirstInstruction()->InputAt(0);
    false_value = false_block->GetFirstInstruction()->InputAt(0);
  } else {
    std::tie(at_most_one_phi_with_different_inputs, phi) = HasAtMostOnePhiWithDifferentInputs(
        merge_block, predecessor_index_true, predecessor_index_false);
    if (!at_most_one_phi_with_different_inputs) {
      return false;
    }
    if (phi != nullptr) {
      true_value = phi->InputAt(predecessor_index_true);
      false_value = phi->InputAt(predecessor_index_false);
    }  // else we don't need to create a `HSelect` at all.
  }
  DCHECK(both_successors_return || at_most_one_phi_with_different_inputs);

  // Create the Select instruction and insert it in front of the If.
  HInstruction* condition = if_instruction->InputAt(0);
  HSelect* select = nullptr;
  if (both_successors_return || phi != nullptr) {
    select = new (graph_->GetAllocator()) HSelect(condition,
                                                  true_value,
                                                  false_value,
                                                  if_instruction->GetDexPc());
    block->InsertInstructionBefore(select, if_instruction);
    if (both_successors_return) {
      if (true_value->GetType() == DataType::Type::kReference) {
        DCHECK(false_value->GetType() == DataType::Type::kReference);
        ReferenceTypePropagation::FixUpSelectType(select, graph_->GetHandleCache());
      }
      false_block->GetFirstInstruction()->ReplaceInput(select, 0);
    } else {
      if (phi->GetType() == DataType::Type::kReference) {
        select->SetReferenceTypeInfoIfValid(phi->GetReferenceTypeInfo());
      }
      phi->ReplaceInput(select, predecessor_index_false);  // We'll remove the true branch below.
    }
  }

  // Remove the true branch which removes the corresponding Phi input if needed.
  // If left only with the false branch, the Phi is automatically removed.
  true_block->DisconnectAndDelete();

  // Merge remaining blocks which are now connected with Goto.
  DCHECK_EQ(block->GetSingleSuccessor(), false_block);
  block->MergeWith(false_block);
  if (!both_successors_return && merge_block->GetPredecessors().size() == 1u) {
    DCHECK_IMPLIES(phi != nullptr, phi->GetBlock() == nullptr);
    DCHECK(merge_block->GetPhis().IsEmpty());
    DCHECK_EQ(block->GetSingleSuccessor(), merge_block);
    block->MergeWith(merge_block);
  }

  MaybeRecordStat(stats_, select != nullptr ? MethodCompilationStat::kControlFlowSelectGenerated
                                            : MethodCompilationStat::kControlFlowDiamondRemoved);

  // Very simple way of finding common subexpressions in the generated HSelect statements
  // (since this runs after GVN). Lookup by condition, and reuse latest one if possible
  // (due to post order, latest select is most likely replacement). If needed, we could
  // improve this by e.g. using the operands in the map as well.
  if (select != nullptr) {
    auto it = cache->find(condition);
    if (it == cache->end()) {
      cache->Put(condition, select);
    } else {
      // Found cached value. See if latest can replace cached in the HIR.
      HSelect* cached_select = it->second;
      DCHECK_EQ(cached_select->GetCondition(), select->GetCondition());
      if (cached_select->GetTrueValue() == select->GetTrueValue() &&
          cached_select->GetFalseValue() == select->GetFalseValue() &&
          select->StrictlyDominates(cached_select)) {
        cached_select->ReplaceWith(select);
        cached_select->GetBlock()->RemoveInstruction(cached_select);
      }
      it->second = select;  // always cache latest
    }
  }

  // No need to update dominance information, as we are simplifying
  // a simple diamond shape, where the join block is merged with the
  // entry block. Any following blocks would have had the join block
  // as a dominator, and `MergeWith` handles changing that to the
  // entry block
  return true;
}

HBasicBlock* HControlFlowSimplifier::TryFixupDoubleDiamondPattern(HBasicBlock* block) {
  DCHECK(block->GetLastInstruction()->IsIf());
  HIf* if_instruction = block->GetLastInstruction()->AsIf();
  HBasicBlock* true_block = if_instruction->IfTrueSuccessor();
  HBasicBlock* false_block = if_instruction->IfFalseSuccessor();
  DCHECK_NE(true_block, false_block);

  // One branch must be a single goto, and the other one the inner if.
  if (true_block->IsSingleGoto() == false_block->IsSingleGoto()) {
    return nullptr;
  }

  HBasicBlock* single_goto = true_block->IsSingleGoto() ? true_block : false_block;
  HBasicBlock* inner_if_block = true_block->IsSingleGoto() ? false_block : true_block;

  // The innner if branch has to be a block with just a comparison and an if.
  if (!inner_if_block->EndsWithIf() ||
      inner_if_block->GetLastInstruction()->AsIf()->InputAt(0) !=
          inner_if_block->GetFirstInstruction() ||
      inner_if_block->GetLastInstruction()->GetPrevious() !=
          inner_if_block->GetFirstInstruction() ||
      !inner_if_block->GetFirstInstruction()->IsCondition()) {
    return nullptr;
  }

  HIf* inner_if_instruction = inner_if_block->GetLastInstruction()->AsIf();
  HBasicBlock* inner_if_true_block = inner_if_instruction->IfTrueSuccessor();
  HBasicBlock* inner_if_false_block = inner_if_instruction->IfFalseSuccessor();
  if (!inner_if_true_block->IsSingleGoto() || !inner_if_false_block->IsSingleGoto()) {
    return nullptr;
  }

  // One must merge into the outer condition and the other must not.
  if (BlocksMergeTogether(single_goto, inner_if_true_block) ==
      BlocksMergeTogether(single_goto, inner_if_false_block)) {
    return nullptr;
  }

  // First merge merges the outer if with one of the inner if branches. The block must be a Phi and
  // a Goto.
  HBasicBlock* first_merge = single_goto->GetSingleSuccessor();
  if (first_merge->GetNumberOfPredecessors() != 2 ||
      first_merge->GetPhis().CountSize() != 1 ||
      !first_merge->GetLastInstruction()->IsGoto() ||
      first_merge->GetFirstInstruction() != first_merge->GetLastInstruction()) {
    return nullptr;
  }

  HPhi* first_phi = first_merge->GetFirstPhi()->AsPhi();

  // Second merge is first_merge and the remainder branch merging. It must be phi + goto, or phi +
  // return. Depending on the first merge, we define the second merge.
  HBasicBlock* merges_into_second_merge =
    BlocksMergeTogether(single_goto, inner_if_true_block)
      ? inner_if_false_block
      : inner_if_true_block;
  if (!BlocksMergeTogether(first_merge, merges_into_second_merge)) {
    return nullptr;
  }

  HBasicBlock* second_merge = merges_into_second_merge->GetSingleSuccessor();
  if (second_merge->GetNumberOfPredecessors() != 2 ||
      second_merge->GetPhis().CountSize() != 1 ||
      !(second_merge->GetLastInstruction()->IsGoto() ||
        second_merge->GetLastInstruction()->IsReturn()) ||
      second_merge->GetFirstInstruction() != second_merge->GetLastInstruction()) {
    return nullptr;
  }

  HPhi* second_phi = second_merge->GetFirstPhi()->AsPhi();
  size_t first_merge_index = second_merge->GetPredecessorIndexOf(first_merge);
  if (second_phi->InputAt(first_merge_index) != first_phi) {
    // The first phi does not merge into the second one. This is an odd case where `first_phi` is
    // dead but hasn't been eliminated from the graph yet.
    // TODO(solanes): We can refactor this method to do:
    //                1) Keep the `second_merge` alive instead of `first_merge`
    //                2) For all `first_phi` instances, fetch the appropriate `first_phi` input
    //                3) For the rest of the inputs, duplicate the value that was already present in
    //                `second_phi`.
    //                When we do this, we can eliminate this `if` case, but it would complicate the
    //                logic of this method.
    return nullptr;
  }

  size_t merges_into_second_merge_index =
      second_merge->GetPredecessorIndexOf(merges_into_second_merge);

  // Merge the phis.
  first_phi->AddInput(second_phi->InputAt(merges_into_second_merge_index));
  merges_into_second_merge->ReplaceSuccessor(second_merge, first_merge);
  second_phi->ReplaceWith(first_phi);
  second_merge->RemovePhi(second_phi);

  // Sort out the new domination before merging the blocks
  DCHECK_EQ(second_merge->GetSinglePredecessor(), first_merge);
  second_merge->GetDominator()->RemoveDominatedBlock(second_merge);
  second_merge->SetDominator(first_merge);
  first_merge->AddDominatedBlock(second_merge);
  first_merge->MergeWith(second_merge);

  // No need to update dominance information. There's a chance that `merges_into_second_merge`
  // doesn't come before `first_merge` but we don't need to fix it since `merges_into_second_merge`
  // will disappear from the graph altogether when doing the follow-up
  // TryGenerateSelectSimpleDiamondPattern.

  return inner_if_block;
}

bool HControlFlowSimplifier::TryFlattenMerge(HBasicBlock* block,
                                             size_t reverse_post_order_index,
                                             BitVectorView<size_t> visited_blocks) {
  DCHECK(block->GetFirstInstruction()->IsGoto());
  DCHECK_EQ(block->GetFirstInstruction(), block->GetLastInstruction());
  HBasicBlock* successor = block->GetSingleSuccessor();
  DCHECK(!graph_->IsExitBlock(successor));  // `HGoto` does not flow to exit block.
  if (block->GetPredecessors().size() < 2u || successor->GetPredecessors().size() < 2u) {
    return false;
  }
  if (block->IsCatchBlock() || successor->IsCatchBlock()) {
    // Phi inputs do not correspond to catch block predecessors. Do not flatten.
    return false;
  }
  if (block->GetLoopInformation() != successor->GetLoopInformation()) {
    // The `block` is a pre-header, including the case when `successor` is an irreducible
    // loop entry that's not actually marked as loop header in the `HLoopInformation`.
    return false;
  }
  if (block->IsInLoop()) {
    // Do not merge if the `block` or the `successor` is a loop header, including irreducible
    // loop entries that are not actually marked as loop header in the `HLoopInformation`.
    // Even for irreducible loops, check for the recorded loop header first.
    HLoopInformation* loop_info = block->GetLoopInformation();
    if (block == loop_info->GetHeader() || successor == loop_info->GetHeader()) {
      return false;
    }
    if (UNLIKELY(loop_info->IsIrreducible())) {
      auto is_loop_header = [loop_info](HBasicBlock* b) {
        DCHECK_EQ(loop_info, b->GetLoopInformation());
        auto&& predecessors = b->GetPredecessors();
        return std::any_of(
            predecessors.begin(),
            predecessors.end(),
            [loop_info](HBasicBlock* p) { return p->GetLoopInformation() != loop_info; });
      };
      if (is_loop_header(block) || is_loop_header(successor)) {
        return false;
      }
    }
  }

  block->TakeGotoBlockSuccessorsOtherPredecessorsAndMergePhis();

  // Fix up domination information for unmerged blocks before calling `MergeWith()`.
  HBasicBlock* dominator = successor->GetDominator();
  if (block->GetDominator() == dominator) {
    dominator->RemoveDominatedBlock(successor);
  } else {
    block->GetDominator()->RemoveDominatedBlock(block);
    block->SetDominator(dominator);
    dominator->ReplaceDominatedBlock(successor, block);
  }
  successor->SetDominator(block);
  block->AddDominatedBlock(successor);

  // Move predecessors before `block` in reverse post order if needed.
  ScopedArenaAllocator allocator(graph_->GetArenaStack());
  BitVectorView<size_t> predecessors_to_move = ArenaBitVector::CreateFixedSize(
      &allocator, graph_->GetBlocks().size(), kArenaAllocControlFlowSimplifier);
  ScopedArenaVector<HBasicBlock*> work_queue(allocator.Adapter(kArenaAllocControlFlowSimplifier));
  auto mark_predecessors = [&](HBasicBlock* current) {
    for (HBasicBlock* predecessor : current->GetPredecessors()) {
      if (visited_blocks.IsBitSet(predecessor->GetBlockId()) &&
          !predecessors_to_move.IsBitSet(predecessor->GetBlockId())) {
        predecessors_to_move.SetBit(predecessor->GetBlockId());
        work_queue.push_back(predecessor);
      }
    }
  };
  mark_predecessors(block);
  if (!work_queue.empty()) {
    do {
      HBasicBlock* current = work_queue.back();
      work_queue.pop_back();
      mark_predecessors(current);
    } while (!work_queue.empty());
    // Move blocks marked in `predecessors_to_move` to the correct position in the reverse
    // post order while extracting `block` and other unmarked blocks to a temporary vector.
    ScopedArenaVector<HBasicBlock*> extracted(allocator.Adapter(kArenaAllocControlFlowSimplifier));
    auto moved_end = graph_->reverse_post_order_.begin() + reverse_post_order_index;
    DCHECK_EQ(block, *moved_end);
    DCHECK(!predecessors_to_move.IsBitSet(block->GetBlockId()));
    extracted.push_back(block);
    auto move_it = std::next(moved_end);
    DCHECK(move_it != graph_->reverse_post_order_.end());
    while (*move_it != successor) {
      if (predecessors_to_move.IsBitSet((*move_it)->GetBlockId())) {
        *moved_end = *move_it;
        ++moved_end;
      } else {
        extracted.push_back(*move_it);
      }
      ++move_it;
      DCHECK(move_it != graph_->reverse_post_order_.end());
    }
    // Place extracted blocks in the freed range in reverse post order.
    DCHECK_EQ(static_cast<size_t>(std::distance(moved_end, move_it)), extracted.size());
    std::copy(extracted.begin(), extracted.end(), moved_end);
  }

  // Finish the merge using `MergeWith()`.
  block->MergeWith(successor);

  MaybeRecordStat(stats_, MethodCompilationStat::kControlFlowFlattenedMerge);
  return true;
}

bool HControlFlowSimplifier::Run() {
  bool simplified = com::android::art::rw::flags::packed_switch_simplification() && ReturnSinking();

  ScopedArenaAllocator allocator(graph_->GetArenaStack());
  // Select cache with local allocator for `TryGenerateSelectSimpleDiamondPattern()`.
  ScopedArenaSafeMap<HInstruction*, HSelect*> select_cache(
      std::less<HInstruction*>(), allocator.Adapter(kArenaAllocControlFlowSimplifier));
  // Mark visited blocks by block id for reverse post order fixup in `TryFlattenMerge()`.
  BitVectorView<size_t> visited_blocks = ArenaBitVector::CreateFixedSize(
      &allocator, graph_->GetBlocks().size(), kArenaAllocControlFlowSimplifier);

  // Iterate in post order in the case that simplifying a block exposes simplification
  // opportunities for earier blocks. Do not process the entry block.
  // We may remove blocks from the reverse post order array, so make the iteration very explicit.
  HBasicBlock* const * reverse_post_order_data = graph_->GetReversePostOrder().data();
  size_t reverse_post_order_index = graph_->GetReversePostOrder().size();
  while (reverse_post_order_index != /* Do not process entry block with index 0. */ 1u) {
    --reverse_post_order_index;
    HBasicBlock* block = reverse_post_order_data[reverse_post_order_index];
    DCHECK(block != nullptr);
    DCHECK(!block->GetInstructions().IsEmpty());
    HInstruction* last_inst = block->GetLastInstruction();
    bool block_may_have_moved_in_rpo = false;
    if (com::android::art::rw::flags::packed_switch_simplification() &&
        last_inst->IsPackedSwitch() &&
        TrySimplifyPackedSwitch(block, &allocator)) {
      simplified = true;
    } else if (com::android::art::rw::flags::packed_switch_simplification() &&
               last_inst->IsGoto() &&
               last_inst == block->GetFirstInstruction() &&
               TryFlattenMerge(block, reverse_post_order_index, visited_blocks)) {
      simplified = true;
      block_may_have_moved_in_rpo = true;
    } else if (last_inst->IsIf()) {
      if (TryGenerateSelectSimpleDiamondPattern(block, &select_cache)) {
        simplified = true;
      } else if (!com::android::art::rw::flags::packed_switch_simplification()) {
        // Note: The `TryFlattenMerge()` simplification above replaces the
        // `TryFixupDoubleDiamondPattern()` here if the flag is enabled.
        // Try to fix up the odd version of the double diamond pattern. If we could do it, it means
        // that we can generate two selects.
        HBasicBlock* inner_if_block = TryFixupDoubleDiamondPattern(block);
        if (inner_if_block != nullptr) {
          // Generate the selects now since `inner_if_block` should be after `block` in PostOrder.
          bool result = TryGenerateSelectSimpleDiamondPattern(inner_if_block, &select_cache);
          DCHECK(result);
          result = TryGenerateSelectSimpleDiamondPattern(block, &select_cache);
          DCHECK(result);
          simplified = true;
        }
      }
    }
    DCHECK_LT(block->GetBlockId(), graph_->GetBlocks().size());
    visited_blocks.SetBit(block->GetBlockId());
    // Blocks with higher indexes may have been removed and the `block` may have been moved
    // further back. Removing from a `std::vector<>` does not change the data pointer.
    DCHECK_EQ(reverse_post_order_data, graph_->GetReversePostOrder().data());
    DCHECK_LT(reverse_post_order_index, graph_->GetReversePostOrder().size());
    if (block_may_have_moved_in_rpo) {
      DCHECK_GE(IndexOfElement(graph_->GetReversePostOrder(), block), reverse_post_order_index);
    } else {
      DCHECK_EQ(block, reverse_post_order_data[reverse_post_order_index]);
    }
  }
  DCHECK_EQ(reverse_post_order_index, 1u);
  DCHECK(graph_->IsEntryBlock(reverse_post_order_data[0u]));

  return simplified;
}

}  // namespace art

Messung V0.5 in Prozent
C=86 H=88 G=86

¤ Dauer der Verarbeitung: 0.3 Sekunden  (vorverarbeitet am  2026-06-29) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik