Coverage Report

Created: 2026-08-14 06:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/spirv-tools/source/opt/basic_block.cpp
Line
Count
Source
1
// Copyright (c) 2016 Google Inc.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//     http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
#include "source/opt/basic_block.h"
16
17
#include <ostream>
18
19
#include "source/opt/ir_context.h"
20
#include "source/opt/reflect.h"
21
#include "source/util/make_unique.h"
22
23
namespace spvtools {
24
namespace opt {
25
namespace {
26
constexpr uint32_t kLoopMergeContinueBlockIdInIdx = 1;
27
constexpr uint32_t kLoopMergeMergeBlockIdInIdx = 0;
28
constexpr uint32_t kSelectionMergeMergeBlockIdInIdx = 0;
29
}  // namespace
30
31
998k
BasicBlock* BasicBlock::Clone(IRContext* context) const {
32
998k
  Instruction* label_clone = GetLabelInst()->Clone(context);
33
998k
  if (!label_clone) {
34
0
    return nullptr;
35
0
  }
36
998k
  BasicBlock* clone = new BasicBlock(std::unique_ptr<Instruction>(label_clone));
37
4.84M
  for (const auto& inst : insts_) {
38
    // Use the incoming context
39
4.84M
    Instruction* inst_clone = inst.Clone(context);
40
4.84M
    if (!inst_clone) {
41
0
      delete clone;
42
0
      return nullptr;
43
0
    }
44
4.84M
    clone->AddInstruction(std::unique_ptr<Instruction>(inst_clone));
45
4.84M
  }
46
47
998k
  if (context->AreAnalysesValid(
48
998k
          IRContext::Analysis::kAnalysisInstrToBlockMapping)) {
49
2.31M
    for (auto& inst : *clone) {
50
2.31M
      context->set_instr_block(&inst, clone);
51
2.31M
    }
52
645k
  }
53
54
998k
  return clone;
55
998k
}
56
57
28.5M
const Instruction* BasicBlock::GetMergeInst() const {
58
28.5M
  const Instruction* result = nullptr;
59
  // If it exists, the merge instruction immediately precedes the
60
  // terminator.
61
28.5M
  auto iter = ctail();
62
28.5M
  if (iter != cbegin()) {
63
23.3M
    --iter;
64
23.3M
    const auto opcode = iter->opcode();
65
23.3M
    if (opcode == spv::Op::OpLoopMerge || opcode == spv::Op::OpSelectionMerge) {
66
9.28M
      result = &*iter;
67
9.28M
    }
68
23.3M
  }
69
28.5M
  return result;
70
28.5M
}
71
72
35.0M
Instruction* BasicBlock::GetMergeInst() {
73
35.0M
  Instruction* result = nullptr;
74
  // If it exists, the merge instruction immediately precedes the
75
  // terminator.
76
35.0M
  auto iter = tail();
77
35.0M
  if (iter != begin()) {
78
28.3M
    --iter;
79
28.3M
    const auto opcode = iter->opcode();
80
28.3M
    if (opcode == spv::Op::OpLoopMerge || opcode == spv::Op::OpSelectionMerge) {
81
15.9M
      result = &*iter;
82
15.9M
    }
83
28.3M
  }
84
35.0M
  return result;
85
35.0M
}
86
87
28.5M
const Instruction* BasicBlock::GetLoopMergeInst() const {
88
28.5M
  if (auto* merge = GetMergeInst()) {
89
9.28M
    if (merge->opcode() == spv::Op::OpLoopMerge) {
90
788k
      return merge;
91
788k
    }
92
9.28M
  }
93
27.7M
  return nullptr;
94
28.5M
}
95
96
12.0M
Instruction* BasicBlock::GetLoopMergeInst() {
97
12.0M
  if (auto* merge = GetMergeInst()) {
98
4.57M
    if (merge->opcode() == spv::Op::OpLoopMerge) {
99
985k
      return merge;
100
985k
    }
101
4.57M
  }
102
11.0M
  return nullptr;
103
12.0M
}
104
105
133k
void BasicBlock::KillAllInsts(bool killLabel) {
106
537k
  ForEachInst([killLabel](Instruction* ip) {
107
537k
    if (killLabel || ip->opcode() != spv::Op::OpLabel) {
108
518k
      ip->context()->KillInst(ip);
109
518k
    }
110
537k
  });
111
133k
}
112
113
void BasicBlock::ForEachSuccessorLabel(
114
51.8M
    const std::function<void(const uint32_t)>& f) const {
115
59.9M
  WhileEachSuccessorLabel([f](const uint32_t l) {
116
59.9M
    f(l);
117
59.9M
    return true;
118
59.9M
  });
119
51.8M
}
120
121
bool BasicBlock::WhileEachSuccessorLabel(
122
58.6M
    const std::function<bool(const uint32_t)>& f) const {
123
58.6M
  const auto br = &insts_.back();
124
58.6M
  switch (br->opcode()) {
125
42.1M
    case spv::Op::OpBranch:
126
42.1M
      return f(br->GetOperand(0).words[0]);
127
11.7M
    case spv::Op::OpBranchConditional:
128
13.4M
    case spv::Op::OpSwitch: {
129
13.4M
      bool is_first = true;
130
39.1M
      return br->WhileEachInId([&is_first, &f](const uint32_t* idp) {
131
39.1M
        if (!is_first) return f(*idp);
132
13.4M
        is_first = false;
133
13.4M
        return true;
134
39.1M
      });
135
11.7M
    }
136
3.08M
    default:
137
3.08M
      return true;
138
58.6M
  }
139
58.6M
}
140
141
void BasicBlock::ForEachSuccessorLabel(
142
117k
    const std::function<void(uint32_t*)>& f) {
143
117k
  auto br = &insts_.back();
144
117k
  switch (br->opcode()) {
145
103k
    case spv::Op::OpBranch: {
146
103k
      uint32_t tmp_id = br->GetOperand(0).words[0];
147
103k
      f(&tmp_id);
148
103k
      if (tmp_id != br->GetOperand(0).words[0]) br->SetOperand(0, {tmp_id});
149
103k
    } break;
150
13.4k
    case spv::Op::OpBranchConditional:
151
13.7k
    case spv::Op::OpSwitch: {
152
13.7k
      bool is_first = true;
153
41.3k
      br->ForEachInId([&is_first, &f](uint32_t* idp) {
154
41.3k
        if (!is_first) f(idp);
155
41.3k
        is_first = false;
156
41.3k
      });
157
13.7k
    } break;
158
23
    default:
159
23
      break;
160
117k
  }
161
117k
}
162
163
773k
bool BasicBlock::IsSuccessor(const BasicBlock* block) const {
164
773k
  uint32_t succId = block->id();
165
773k
  bool isSuccessor = false;
166
1.00M
  ForEachSuccessorLabel([&isSuccessor, succId](const uint32_t label) {
167
1.00M
    if (label == succId) isSuccessor = true;
168
1.00M
  });
169
773k
  return isSuccessor;
170
773k
}
171
172
void BasicBlock::ForMergeAndContinueLabel(
173
3.13M
    const std::function<void(const uint32_t)>& f) {
174
3.13M
  auto ii = insts_.end();
175
3.13M
  --ii;
176
3.13M
  if (ii == insts_.begin()) return;
177
1.67M
  --ii;
178
1.67M
  if (ii->opcode() == spv::Op::OpSelectionMerge ||
179
1.09M
      ii->opcode() == spv::Op::OpLoopMerge) {
180
855k
    ii->ForEachInId([&f](const uint32_t* idp) { f(*idp); });
181
716k
  }
182
1.67M
}
183
184
31.3M
uint32_t BasicBlock::MergeBlockIdIfAny() const {
185
31.3M
  auto merge_ii = cend();
186
31.3M
  --merge_ii;
187
31.3M
  uint32_t mbid = 0;
188
31.3M
  if (merge_ii != cbegin()) {
189
22.0M
    --merge_ii;
190
22.0M
    if (merge_ii->opcode() == spv::Op::OpLoopMerge) {
191
1.67M
      mbid = merge_ii->GetSingleWordInOperand(kLoopMergeMergeBlockIdInIdx);
192
20.3M
    } else if (merge_ii->opcode() == spv::Op::OpSelectionMerge) {
193
7.14M
      mbid = merge_ii->GetSingleWordInOperand(kSelectionMergeMergeBlockIdInIdx);
194
7.14M
    }
195
22.0M
  }
196
197
31.3M
  return mbid;
198
31.3M
}
199
200
0
uint32_t BasicBlock::MergeBlockId() const {
201
0
  uint32_t mbid = MergeBlockIdIfAny();
202
0
  assert(mbid && "Expected block to have a corresponding merge block");
203
0
  return mbid;
204
0
}
205
206
6.36M
uint32_t BasicBlock::ContinueBlockIdIfAny() const {
207
6.36M
  auto merge_ii = cend();
208
6.36M
  --merge_ii;
209
6.36M
  uint32_t cbid = 0;
210
6.36M
  if (merge_ii != cbegin()) {
211
5.57M
    --merge_ii;
212
5.57M
    if (merge_ii->opcode() == spv::Op::OpLoopMerge) {
213
963k
      cbid = merge_ii->GetSingleWordInOperand(kLoopMergeContinueBlockIdInIdx);
214
963k
    }
215
5.57M
  }
216
6.36M
  return cbid;
217
6.36M
}
218
219
0
uint32_t BasicBlock::ContinueBlockId() const {
220
0
  uint32_t cbid = ContinueBlockIdIfAny();
221
0
  assert(cbid && "Expected block to have a corresponding continue target");
222
0
  return cbid;
223
0
}
224
225
0
std::ostream& operator<<(std::ostream& str, const BasicBlock& block) {
226
0
  str << block.PrettyPrint();
227
0
  return str;
228
0
}
229
230
0
void BasicBlock::Dump() const {
231
0
  std::cerr << "Basic block #" << id() << "\n" << *this << "\n ";
232
0
}
233
234
0
std::string BasicBlock::PrettyPrint(uint32_t options) const {
235
0
  std::ostringstream str;
236
0
  ForEachInst([&str, options](const Instruction* inst) {
237
0
    str << inst->PrettyPrint(options);
238
0
    if (!spvOpcodeIsBlockTerminator(inst->opcode())) {
239
0
      str << std::endl;
240
0
    }
241
0
  });
242
0
  return str.str();
243
0
}
244
245
BasicBlock* BasicBlock::SplitBasicBlock(IRContext* context, uint32_t label_id,
246
10.6k
                                        iterator iter) {
247
10.6k
  assert(!insts_.empty());
248
249
10.6k
  std::unique_ptr<BasicBlock> new_block_temp = MakeUnique<BasicBlock>(
250
10.6k
      MakeUnique<Instruction>(context, spv::Op::OpLabel, 0, label_id,
251
10.6k
                              std::initializer_list<Operand>{}));
252
10.6k
  BasicBlock* new_block = new_block_temp.get();
253
10.6k
  function_->InsertBasicBlockAfter(std::move(new_block_temp), this);
254
255
10.6k
  new_block->insts_.Splice(new_block->end(), &insts_, iter, end());
256
10.6k
  assert(new_block->GetParent() == GetParent() &&
257
10.6k
         "The parent should already be set appropriately.");
258
259
10.6k
  context->AnalyzeDefUse(new_block->GetLabelInst());
260
261
  // Update the phi nodes in the successor blocks to reference the new block id.
262
10.6k
  const_cast<const BasicBlock*>(new_block)->ForEachSuccessorLabel(
263
13.5k
      [new_block, this, context](const uint32_t label) {
264
13.5k
        BasicBlock* target_bb = context->get_instr_block(label);
265
13.5k
        target_bb->ForEachPhiInst(
266
13.5k
            [this, new_block, context](Instruction* phi_inst) {
267
821
              bool changed = false;
268
2.46k
              for (uint32_t i = 1; i < phi_inst->NumInOperands(); i += 2) {
269
1.64k
                if (phi_inst->GetSingleWordInOperand(i) == this->id()) {
270
821
                  changed = true;
271
821
                  phi_inst->SetInOperand(i, {new_block->id()});
272
821
                }
273
1.64k
              }
274
275
821
              if (changed) {
276
821
                context->UpdateDefUse(phi_inst);
277
821
              }
278
821
            });
279
13.5k
      });
280
281
10.6k
  if (context->AreAnalysesValid(IRContext::kAnalysisInstrToBlockMapping)) {
282
10.6k
    context->set_instr_block(new_block->GetLabelInst(), new_block);
283
93.8k
    new_block->ForEachInst([new_block, context](Instruction* inst) {
284
93.8k
      context->set_instr_block(inst, new_block);
285
93.8k
    });
286
10.6k
  }
287
288
10.6k
  return new_block;
289
10.6k
}
290
291
}  // namespace opt
292
}  // namespace spvtools