1fd4e5da5Sopenharmony_ci// Copyright (c) 2019 Google LLC
2fd4e5da5Sopenharmony_ci//
3fd4e5da5Sopenharmony_ci// Licensed under the Apache License, Version 2.0 (the "License");
4fd4e5da5Sopenharmony_ci// you may not use this file except in compliance with the License.
5fd4e5da5Sopenharmony_ci// You may obtain a copy of the License at
6fd4e5da5Sopenharmony_ci//
7fd4e5da5Sopenharmony_ci//     http://www.apache.org/licenses/LICENSE-2.0
8fd4e5da5Sopenharmony_ci//
9fd4e5da5Sopenharmony_ci// Unless required by applicable law or agreed to in writing, software
10fd4e5da5Sopenharmony_ci// distributed under the License is distributed on an "AS IS" BASIS,
11fd4e5da5Sopenharmony_ci// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12fd4e5da5Sopenharmony_ci// See the License for the specific language governing permissions and
13fd4e5da5Sopenharmony_ci// limitations under the License.
14fd4e5da5Sopenharmony_ci
15fd4e5da5Sopenharmony_ci#include "source/reduce/merge_blocks_reduction_opportunity.h"
16fd4e5da5Sopenharmony_ci
17fd4e5da5Sopenharmony_ci#include "source/opt/block_merge_util.h"
18fd4e5da5Sopenharmony_ci#include "source/opt/ir_context.h"
19fd4e5da5Sopenharmony_ci
20fd4e5da5Sopenharmony_cinamespace spvtools {
21fd4e5da5Sopenharmony_cinamespace reduce {
22fd4e5da5Sopenharmony_ci
23fd4e5da5Sopenharmony_ciMergeBlocksReductionOpportunity::MergeBlocksReductionOpportunity(
24fd4e5da5Sopenharmony_ci    opt::IRContext* context, opt::Function* function, opt::BasicBlock* block) {
25fd4e5da5Sopenharmony_ci  // Precondition: the terminator has to be OpBranch.
26fd4e5da5Sopenharmony_ci  assert(block->terminator()->opcode() == spv::Op::OpBranch);
27fd4e5da5Sopenharmony_ci  context_ = context;
28fd4e5da5Sopenharmony_ci  function_ = function;
29fd4e5da5Sopenharmony_ci  // Get the successor block associated with the OpBranch.
30fd4e5da5Sopenharmony_ci  successor_block_ =
31fd4e5da5Sopenharmony_ci      context->cfg()->block(block->terminator()->GetSingleWordInOperand(0));
32fd4e5da5Sopenharmony_ci}
33fd4e5da5Sopenharmony_ci
34fd4e5da5Sopenharmony_cibool MergeBlocksReductionOpportunity::PreconditionHolds() {
35fd4e5da5Sopenharmony_ci  // Merge block opportunities can disable each other.
36fd4e5da5Sopenharmony_ci  // Example: Given blocks: A->B->C.
37fd4e5da5Sopenharmony_ci  // A is a loop header; B and C are blocks in the loop; C ends with OpReturn.
38fd4e5da5Sopenharmony_ci  // There are two opportunities: B and C can be merged with their predecessors.
39fd4e5da5Sopenharmony_ci  // Merge C. B now ends with OpReturn. We now just have: A->B.
40fd4e5da5Sopenharmony_ci  // Merge B is now disabled, as this would lead to A, a loop header, ending
41fd4e5da5Sopenharmony_ci  // with an OpReturn, which is invalid.
42fd4e5da5Sopenharmony_ci
43fd4e5da5Sopenharmony_ci  const auto predecessors = context_->cfg()->preds(successor_block_->id());
44fd4e5da5Sopenharmony_ci  assert(1 == predecessors.size() &&
45fd4e5da5Sopenharmony_ci         "For a successor to be merged into its predecessor, exactly one "
46fd4e5da5Sopenharmony_ci         "predecessor must be present.");
47fd4e5da5Sopenharmony_ci  const uint32_t predecessor_id = predecessors[0];
48fd4e5da5Sopenharmony_ci  opt::BasicBlock* predecessor_block =
49fd4e5da5Sopenharmony_ci      context_->get_instr_block(predecessor_id);
50fd4e5da5Sopenharmony_ci  return opt::blockmergeutil::CanMergeWithSuccessor(context_,
51fd4e5da5Sopenharmony_ci                                                    predecessor_block);
52fd4e5da5Sopenharmony_ci}
53fd4e5da5Sopenharmony_ci
54fd4e5da5Sopenharmony_civoid MergeBlocksReductionOpportunity::Apply() {
55fd4e5da5Sopenharmony_ci  // While the original block that targeted the successor may not exist anymore
56fd4e5da5Sopenharmony_ci  // (it might have been merged with another block), some block must exist that
57fd4e5da5Sopenharmony_ci  // targets the successor.  Find it.
58fd4e5da5Sopenharmony_ci
59fd4e5da5Sopenharmony_ci  const auto predecessors = context_->cfg()->preds(successor_block_->id());
60fd4e5da5Sopenharmony_ci  assert(1 == predecessors.size() &&
61fd4e5da5Sopenharmony_ci         "For a successor to be merged into its predecessor, exactly one "
62fd4e5da5Sopenharmony_ci         "predecessor must be present.");
63fd4e5da5Sopenharmony_ci  const uint32_t predecessor_id = predecessors[0];
64fd4e5da5Sopenharmony_ci
65fd4e5da5Sopenharmony_ci  // We need an iterator pointing to the predecessor, hence the loop.
66fd4e5da5Sopenharmony_ci  for (auto bi = function_->begin(); bi != function_->end(); ++bi) {
67fd4e5da5Sopenharmony_ci    if (bi->id() == predecessor_id) {
68fd4e5da5Sopenharmony_ci      opt::blockmergeutil::MergeWithSuccessor(context_, function_, bi);
69fd4e5da5Sopenharmony_ci      // Block merging changes the control flow graph, so invalidate it.
70fd4e5da5Sopenharmony_ci      context_->InvalidateAnalysesExceptFor(
71fd4e5da5Sopenharmony_ci          opt::IRContext::Analysis::kAnalysisNone);
72fd4e5da5Sopenharmony_ci      return;
73fd4e5da5Sopenharmony_ci    }
74fd4e5da5Sopenharmony_ci  }
75fd4e5da5Sopenharmony_ci
76fd4e5da5Sopenharmony_ci  assert(false &&
77fd4e5da5Sopenharmony_ci         "Unreachable: we should have found a block with the desired id.");
78fd4e5da5Sopenharmony_ci}
79fd4e5da5Sopenharmony_ci
80fd4e5da5Sopenharmony_ci}  // namespace reduce
81fd4e5da5Sopenharmony_ci}  // namespace spvtools
82