Coverage Report

Created: 2026-02-14 06:30

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
798k
BasicBlock* BasicBlock::Clone(IRContext* context) const {
32
798k
  Instruction* label_clone = GetLabelInst()->Clone(context);
33
798k
  if (!label_clone) {
34
0
    return nullptr;
35
0
  }
36
798k
  BasicBlock* clone = new BasicBlock(std::unique_ptr<Instruction>(label_clone));
37
3.98M
  for (const auto& inst : insts_) {
38
    // Use the incoming context
39
3.98M
    Instruction* inst_clone = inst.Clone(context);
40
3.98M
    if (!inst_clone) {
41
0
      delete clone;
42
0
      return nullptr;
43
0
    }
44
3.98M
    clone->AddInstruction(std::unique_ptr<Instruction>(inst_clone));
45
3.98M
  }
46
47
798k
  if (context->AreAnalysesValid(
48
798k
          IRContext::Analysis::kAnalysisInstrToBlockMapping)) {
49
1.82M
    for (auto& inst : *clone) {
50
1.82M
      context->set_instr_block(&inst, clone);
51
1.82M
    }
52
509k
  }
53
54
798k
  return clone;
55
798k
}
56
57
24.9M
const Instruction* BasicBlock::GetMergeInst() const {
58
24.9M
  const Instruction* result = nullptr;
59
  // If it exists, the merge instruction immediately precedes the
60
  // terminator.
61
24.9M
  auto iter = ctail();
62
24.9M
  if (iter != cbegin()) {
63
20.4M
    --iter;
64
20.4M
    const auto opcode = iter->opcode();
65
20.4M
    if (opcode == spv::Op::OpLoopMerge || opcode == spv::Op::OpSelectionMerge) {
66
8.36M
      result = &*iter;
67
8.36M
    }
68
20.4M
  }
69
24.9M
  return result;
70
24.9M
}
71
72
25.9M
Instruction* BasicBlock::GetMergeInst() {
73
25.9M
  Instruction* result = nullptr;
74
  // If it exists, the merge instruction immediately precedes the
75
  // terminator.
76
25.9M
  auto iter = tail();
77
25.9M
  if (iter != begin()) {
78
22.2M
    --iter;
79
22.2M
    const auto opcode = iter->opcode();
80
22.2M
    if (opcode == spv::Op::OpLoopMerge || opcode == spv::Op::OpSelectionMerge) {
81
13.1M
      result = &*iter;
82
13.1M
    }
83
22.2M
  }
84
25.9M
  return result;
85
25.9M
}
86
87
24.9M
const Instruction* BasicBlock::GetLoopMergeInst() const {
88
24.9M
  if (auto* merge = GetMergeInst()) {
89
8.36M
    if (merge->opcode() == spv::Op::OpLoopMerge) {
90
728k
      return merge;
91
728k
    }
92
8.36M
  }
93
24.1M
  return nullptr;
94
24.9M
}
95
96
10.5M
Instruction* BasicBlock::GetLoopMergeInst() {
97
10.5M
  if (auto* merge = GetMergeInst()) {
98
4.08M
    if (merge->opcode() == spv::Op::OpLoopMerge) {
99
868k
      return merge;
100
868k
    }
101
4.08M
  }
102
9.66M
  return nullptr;
103
10.5M
}
104
105
101k
void BasicBlock::KillAllInsts(bool killLabel) {
106
405k
  ForEachInst([killLabel](Instruction* ip) {
107
405k
    if (killLabel || ip->opcode() != spv::Op::OpLabel) {
108
388k
      ip->context()->KillInst(ip);
109
388k
    }
110
405k
  });
111
101k
}
112
113
void BasicBlock::ForEachSuccessorLabel(
114
21.6M
    const std::function<void(const uint32_t)>& f) const {
115
25.4M
  WhileEachSuccessorLabel([f](const uint32_t l) {
116
25.4M
    f(l);
117
25.4M
    return true;
118
25.4M
  });
119
21.6M
}
120
121
bool BasicBlock::WhileEachSuccessorLabel(
122
27.9M
    const std::function<bool(const uint32_t)>& f) const {
123
27.9M
  const auto br = &insts_.back();
124
27.9M
  switch (br->opcode()) {
125
20.0M
    case spv::Op::OpBranch:
126
20.0M
      return f(br->GetOperand(0).words[0]);
127
5.80M
    case spv::Op::OpBranchConditional:
128
6.71M
    case spv::Op::OpSwitch: {
129
6.71M
      bool is_first = true;
130
19.2M
      return br->WhileEachInId([&is_first, &f](const uint32_t* idp) {
131
19.2M
        if (!is_first) return f(*idp);
132
6.71M
        is_first = false;
133
6.71M
        return true;
134
19.2M
      });
135
5.80M
    }
136
1.13M
    default:
137
1.13M
      return true;
138
27.9M
  }
139
27.9M
}
140
141
void BasicBlock::ForEachSuccessorLabel(
142
97.2k
    const std::function<void(uint32_t*)>& f) {
143
97.2k
  auto br = &insts_.back();
144
97.2k
  switch (br->opcode()) {
145
85.3k
    case spv::Op::OpBranch: {
146
85.3k
      uint32_t tmp_id = br->GetOperand(0).words[0];
147
85.3k
      f(&tmp_id);
148
85.3k
      if (tmp_id != br->GetOperand(0).words[0]) br->SetOperand(0, {tmp_id});
149
85.3k
    } break;
150
11.7k
    case spv::Op::OpBranchConditional:
151
11.8k
    case spv::Op::OpSwitch: {
152
11.8k
      bool is_first = true;
153
35.7k
      br->ForEachInId([&is_first, &f](uint32_t* idp) {
154
35.7k
        if (!is_first) f(idp);
155
35.7k
        is_first = false;
156
35.7k
      });
157
11.8k
    } break;
158
16
    default:
159
16
      break;
160
97.2k
  }
161
97.2k
}
162
163
703k
bool BasicBlock::IsSuccessor(const BasicBlock* block) const {
164
703k
  uint32_t succId = block->id();
165
703k
  bool isSuccessor = false;
166
915k
  ForEachSuccessorLabel([&isSuccessor, succId](const uint32_t label) {
167
915k
    if (label == succId) isSuccessor = true;
168
915k
  });
169
703k
  return isSuccessor;
170
703k
}
171
172
void BasicBlock::ForMergeAndContinueLabel(
173
2.74M
    const std::function<void(const uint32_t)>& f) {
174
2.74M
  auto ii = insts_.end();
175
2.74M
  --ii;
176
2.74M
  if (ii == insts_.begin()) return;
177
1.48M
  --ii;
178
1.48M
  if (ii->opcode() == spv::Op::OpSelectionMerge ||
179
966k
      ii->opcode() == spv::Op::OpLoopMerge) {
180
779k
    ii->ForEachInId([&f](const uint32_t* idp) { f(*idp); });
181
649k
  }
182
1.48M
}
183
184
22.5M
uint32_t BasicBlock::MergeBlockIdIfAny() const {
185
22.5M
  auto merge_ii = cend();
186
22.5M
  --merge_ii;
187
22.5M
  uint32_t mbid = 0;
188
22.5M
  if (merge_ii != cbegin()) {
189
16.6M
    --merge_ii;
190
16.6M
    if (merge_ii->opcode() == spv::Op::OpLoopMerge) {
191
1.23M
      mbid = merge_ii->GetSingleWordInOperand(kLoopMergeMergeBlockIdInIdx);
192
15.3M
    } else if (merge_ii->opcode() == spv::Op::OpSelectionMerge) {
193
5.53M
      mbid = merge_ii->GetSingleWordInOperand(kSelectionMergeMergeBlockIdInIdx);
194
5.53M
    }
195
16.6M
  }
196
197
22.5M
  return mbid;
198
22.5M
}
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
4.40M
uint32_t BasicBlock::ContinueBlockIdIfAny() const {
207
4.40M
  auto merge_ii = cend();
208
4.40M
  --merge_ii;
209
4.40M
  uint32_t cbid = 0;
210
4.40M
  if (merge_ii != cbegin()) {
211
3.76M
    --merge_ii;
212
3.76M
    if (merge_ii->opcode() == spv::Op::OpLoopMerge) {
213
580k
      cbid = merge_ii->GetSingleWordInOperand(kLoopMergeContinueBlockIdInIdx);
214
580k
    }
215
3.76M
  }
216
4.40M
  return cbid;
217
4.40M
}
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.2k
                                        iterator iter) {
247
10.2k
  assert(!insts_.empty());
248
249
10.2k
  std::unique_ptr<BasicBlock> new_block_temp = MakeUnique<BasicBlock>(
250
10.2k
      MakeUnique<Instruction>(context, spv::Op::OpLabel, 0, label_id,
251
10.2k
                              std::initializer_list<Operand>{}));
252
10.2k
  BasicBlock* new_block = new_block_temp.get();
253
10.2k
  function_->InsertBasicBlockAfter(std::move(new_block_temp), this);
254
255
10.2k
  new_block->insts_.Splice(new_block->end(), &insts_, iter, end());
256
10.2k
  assert(new_block->GetParent() == GetParent() &&
257
10.2k
         "The parent should already be set appropriately.");
258
259
10.2k
  context->AnalyzeDefUse(new_block->GetLabelInst());
260
261
  // Update the phi nodes in the successor blocks to reference the new block id.
262
10.2k
  const_cast<const BasicBlock*>(new_block)->ForEachSuccessorLabel(
263
12.9k
      [new_block, this, context](const uint32_t label) {
264
12.9k
        BasicBlock* target_bb = context->get_instr_block(label);
265
12.9k
        target_bb->ForEachPhiInst(
266
12.9k
            [this, new_block, context](Instruction* phi_inst) {
267
796
              bool changed = false;
268
2.38k
              for (uint32_t i = 1; i < phi_inst->NumInOperands(); i += 2) {
269
1.59k
                if (phi_inst->GetSingleWordInOperand(i) == this->id()) {
270
796
                  changed = true;
271
796
                  phi_inst->SetInOperand(i, {new_block->id()});
272
796
                }
273
1.59k
              }
274
275
796
              if (changed) {
276
796
                context->UpdateDefUse(phi_inst);
277
796
              }
278
796
            });
279
12.9k
      });
280
281
10.2k
  if (context->AreAnalysesValid(IRContext::kAnalysisInstrToBlockMapping)) {
282
10.2k
    context->set_instr_block(new_block->GetLabelInst(), new_block);
283
87.4k
    new_block->ForEachInst([new_block, context](Instruction* inst) {
284
87.4k
      context->set_instr_block(inst, new_block);
285
87.4k
    });
286
10.2k
  }
287
288
10.2k
  return new_block;
289
10.2k
}
290
291
}  // namespace opt
292
}  // namespace spvtools