Coverage Report

Created: 2025-07-11 07:07

/src/shaderc/third_party/spirv-tools/source/opt/module.cpp
Line
Count
Source (jump to first uncovered line)
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/module.h"
16
17
#include <algorithm>
18
#include <cstring>
19
#include <ostream>
20
21
#include "source/operand.h"
22
#include "source/opt/ir_context.h"
23
#include "source/opt/reflect.h"
24
25
namespace spvtools {
26
namespace opt {
27
28
70.9k
uint32_t Module::TakeNextIdBound() {
29
70.9k
  if (context()) {
30
70.9k
    if (id_bound() >= context()->max_id_bound()) {
31
0
      return 0;
32
0
    }
33
70.9k
  } else if (id_bound() >= kDefaultMaxIdBound) {
34
0
    return 0;
35
0
  }
36
37
70.9k
  return header_.bound++;
38
70.9k
}
39
40
0
std::vector<Instruction*> Module::GetTypes() {
41
0
  std::vector<Instruction*> type_insts;
42
0
  for (auto& inst : types_values_) {
43
0
    if (IsTypeInst(inst.opcode())) type_insts.push_back(&inst);
44
0
  }
45
0
  return type_insts;
46
0
}
47
48
3.05k
std::vector<const Instruction*> Module::GetTypes() const {
49
3.05k
  std::vector<const Instruction*> type_insts;
50
63.2k
  for (auto& inst : types_values_) {
51
63.2k
    if (IsTypeInst(inst.opcode())) type_insts.push_back(&inst);
52
63.2k
  }
53
3.05k
  return type_insts;
54
3.05k
}
55
56
2.78k
std::vector<Instruction*> Module::GetConstants() {
57
2.78k
  std::vector<Instruction*> const_insts;
58
53.2k
  for (auto& inst : types_values_) {
59
53.2k
    if (IsConstantInst(inst.opcode())) const_insts.push_back(&inst);
60
53.2k
  }
61
2.78k
  return const_insts;
62
2.78k
}
63
64
3.05k
std::vector<const Instruction*> Module::GetConstants() const {
65
3.05k
  std::vector<const Instruction*> const_insts;
66
63.2k
  for (auto& inst : types_values_) {
67
63.2k
    if (IsConstantInst(inst.opcode())) const_insts.push_back(&inst);
68
63.2k
  }
69
3.05k
  return const_insts;
70
3.05k
}
71
72
0
uint32_t Module::GetGlobalValue(spv::Op opcode) const {
73
0
  for (auto& inst : types_values_) {
74
0
    if (inst.opcode() == opcode) return inst.result_id();
75
0
  }
76
0
  return 0;
77
0
}
78
79
void Module::AddGlobalValue(spv::Op opcode, uint32_t result_id,
80
0
                            uint32_t type_id) {
81
0
  std::unique_ptr<Instruction> newGlobal(
82
0
      new Instruction(context(), opcode, type_id, result_id, {}));
83
0
  AddGlobalValue(std::move(newGlobal));
84
0
}
85
86
void Module::ForEachInst(const std::function<void(Instruction*)>& f,
87
11.0k
                         bool run_on_debug_line_insts) {
88
122k
#define DELEGATE(list) list.ForEachInst(f, run_on_debug_line_insts)
89
11.0k
  DELEGATE(capabilities_);
90
11.0k
  DELEGATE(extensions_);
91
11.0k
  DELEGATE(ext_inst_imports_);
92
11.0k
  if (memory_model_) memory_model_->ForEachInst(f, run_on_debug_line_insts);
93
11.0k
  if (sampled_image_address_mode_)
94
0
    sampled_image_address_mode_->ForEachInst(f, run_on_debug_line_insts);
95
11.0k
  DELEGATE(entry_points_);
96
11.0k
  DELEGATE(execution_modes_);
97
11.0k
  DELEGATE(debugs1_);
98
11.0k
  DELEGATE(debugs2_);
99
11.0k
  DELEGATE(debugs3_);
100
11.0k
  DELEGATE(ext_inst_debuginfo_);
101
11.0k
  DELEGATE(annotations_);
102
11.0k
  DELEGATE(types_values_);
103
14.9k
  for (auto& i : functions_) {
104
14.9k
    i->ForEachInst(f, run_on_debug_line_insts,
105
14.9k
                   /* run_on_non_semantic_insts = */ true);
106
14.9k
  }
107
11.0k
#undef DELEGATE
108
11.0k
}
109
110
void Module::ForEachInst(const std::function<void(const Instruction*)>& f,
111
3.51k
                         bool run_on_debug_line_insts) const {
112
123k
#define DELEGATE(i) i.ForEachInst(f, run_on_debug_line_insts)
113
5.23k
  for (auto& i : capabilities_) DELEGATE(i);
114
3.51k
  for (auto& i : extensions_) DELEGATE(i);
115
3.58k
  for (auto& i : ext_inst_imports_) DELEGATE(i);
116
3.51k
  if (memory_model_)
117
3.51k
    static_cast<const Instruction*>(memory_model_.get())
118
3.51k
        ->ForEachInst(f, run_on_debug_line_insts);
119
3.51k
  if (sampled_image_address_mode_)
120
0
    static_cast<const Instruction*>(sampled_image_address_mode_.get())
121
0
        ->ForEachInst(f, run_on_debug_line_insts);
122
3.51k
  for (auto& i : entry_points_) DELEGATE(i);
123
3.51k
  for (auto& i : execution_modes_) DELEGATE(i);
124
3.51k
  for (auto& i : debugs1_) DELEGATE(i);
125
3.51k
  for (auto& i : debugs2_) DELEGATE(i);
126
3.51k
  for (auto& i : debugs3_) DELEGATE(i);
127
19.4k
  for (auto& i : annotations_) DELEGATE(i);
128
45.3k
  for (auto& i : types_values_) DELEGATE(i);
129
3.51k
  for (auto& i : ext_inst_debuginfo_) DELEGATE(i);
130
3.51k
  for (auto& i : functions_) {
131
3.51k
    static_cast<const Function*>(i.get())->ForEachInst(
132
3.51k
        f, run_on_debug_line_insts,
133
3.51k
        /* run_on_non_semantic_insts = */ true);
134
3.51k
  }
135
3.51k
  if (run_on_debug_line_insts) {
136
3.51k
    for (auto& i : trailing_dbg_line_info_) DELEGATE(i);
137
3.51k
  }
138
3.51k
#undef DELEGATE
139
3.51k
}
140
141
1.80k
void Module::ToBinary(std::vector<uint32_t>* binary, bool skip_nop) const {
142
1.80k
  binary->push_back(header_.magic_number);
143
1.80k
  binary->push_back(header_.version);
144
  // TODO(antiagainst): should we change the generator number?
145
1.80k
  binary->push_back(header_.generator);
146
1.80k
  binary->push_back(header_.bound);
147
1.80k
  binary->push_back(header_.schema);
148
149
1.80k
  size_t bound_idx = binary->size() - 2;
150
1.80k
  DebugScope last_scope(kNoDebugScope, kNoInlinedAt);
151
1.80k
  const Instruction* last_line_inst = nullptr;
152
1.80k
  bool between_merge_and_branch = false;
153
1.80k
  bool between_label_and_phi_var = false;
154
1.80k
  auto write_inst = [binary, skip_nop, &last_scope, &last_line_inst,
155
1.80k
                     &between_merge_and_branch, &between_label_and_phi_var,
156
121k
                     this](const Instruction* i) {
157
    // Skip emitting line instructions between merge and branch instructions.
158
121k
    auto opcode = i->opcode();
159
121k
    if (between_merge_and_branch && i->IsLineInst()) {
160
0
      return;
161
0
    }
162
121k
    if (last_line_inst != nullptr) {
163
      // If the current instruction is OpLine or DebugLine and it is the same
164
      // as the last line instruction that is still effective (can be applied
165
      // to the next instruction), we skip writing the current instruction.
166
0
      if (i->IsLine()) {
167
0
        uint32_t operand_index = 0;
168
0
        if (last_line_inst->WhileEachInOperand(
169
0
                [&operand_index, i](const uint32_t* word) {
170
0
                  assert(i->NumInOperandWords() > operand_index);
171
0
                  return *word == i->GetSingleWordInOperand(operand_index++);
172
0
                })) {
173
0
          return;
174
0
        }
175
0
      } else if (!i->IsNoLine() && i->dbg_line_insts().empty()) {
176
        // If the current instruction does not have the line information,
177
        // the last line information is not effective any more. Emit OpNoLine
178
        // or DebugNoLine to specify it.
179
0
        uint32_t shader_set_id = context()
180
0
                                     ->get_feature_mgr()
181
0
                                     ->GetExtInstImportId_Shader100DebugInfo();
182
0
        if (shader_set_id != 0) {
183
0
          binary->push_back((5 << 16) |
184
0
                            static_cast<uint16_t>(spv::Op::OpExtInst));
185
0
          binary->push_back(context()->get_type_mgr()->GetVoidTypeId());
186
0
          binary->push_back(context()->TakeNextId());
187
0
          binary->push_back(shader_set_id);
188
0
          binary->push_back(NonSemanticShaderDebugInfo100DebugNoLine);
189
0
        } else {
190
0
          binary->push_back((1 << 16) |
191
0
                            static_cast<uint16_t>(spv::Op::OpNoLine));
192
0
        }
193
0
        last_line_inst = nullptr;
194
0
      }
195
0
    }
196
197
121k
    if (opcode == spv::Op::OpLabel) {
198
10.3k
      between_label_and_phi_var = true;
199
111k
    } else if (opcode != spv::Op::OpVariable && opcode != spv::Op::OpPhi &&
200
111k
               !spvtools::opt::IsOpLineInst(opcode)) {
201
103k
      between_label_and_phi_var = false;
202
103k
    }
203
204
121k
    if (!(skip_nop && i->IsNop())) {
205
121k
      const auto& scope = i->GetDebugScope();
206
121k
      if (scope != last_scope && !between_merge_and_branch) {
207
        // Can only emit nonsemantic instructions after all phi instructions
208
        // in a block so don't emit scope instructions before phi instructions
209
        // for NonSemantic.Shader.DebugInfo.100.
210
0
        if (!between_label_and_phi_var ||
211
0
            context()
212
0
                ->get_feature_mgr()
213
0
                ->GetExtInstImportId_OpenCL100DebugInfo()) {
214
          // Emit DebugScope |scope| to |binary|.
215
0
          auto dbg_inst = ext_inst_debuginfo_.begin();
216
0
          scope.ToBinary(dbg_inst->type_id(), context()->TakeNextId(),
217
0
                         dbg_inst->GetSingleWordOperand(2), binary);
218
0
        }
219
0
        last_scope = scope;
220
0
      }
221
222
121k
      i->ToBinaryWithoutAttachedDebugInsts(binary);
223
121k
    }
224
    // Update the last line instruction.
225
121k
    between_merge_and_branch = false;
226
121k
    if (spvOpcodeIsBlockTerminator(opcode) || i->IsNoLine()) {
227
10.3k
      last_line_inst = nullptr;
228
111k
    } else if (opcode == spv::Op::OpLoopMerge ||
229
111k
               opcode == spv::Op::OpSelectionMerge) {
230
3.06k
      between_merge_and_branch = true;
231
3.06k
      last_line_inst = nullptr;
232
108k
    } else if (i->IsLine()) {
233
0
      last_line_inst = i;
234
0
    }
235
121k
  };
236
1.80k
  ForEachInst(write_inst, true);
237
238
  // We create new instructions for DebugScope and DebugNoLine. The bound must
239
  // be updated.
240
1.80k
  binary->data()[bound_idx] = header_.bound;
241
1.80k
}
242
243
1.70k
uint32_t Module::ComputeIdBound() const {
244
1.70k
  uint32_t highest = 0;
245
246
1.70k
  ForEachInst(
247
118k
      [&highest](const Instruction* inst) {
248
362k
        for (const auto& operand : *inst) {
249
362k
          if (spvIsIdType(operand.type)) {
250
288k
            highest = std::max(highest, operand.words[0]);
251
288k
          }
252
362k
        }
253
118k
      },
254
1.70k
      true /* scan debug line insts as well */);
255
256
1.70k
  return highest + 1;
257
1.70k
}
258
259
0
bool Module::HasExplicitCapability(uint32_t cap) {
260
0
  for (auto& ci : capabilities_) {
261
0
    uint32_t tcap = ci.GetSingleWordOperand(0);
262
0
    if (tcap == cap) {
263
0
      return true;
264
0
    }
265
0
  }
266
0
  return false;
267
0
}
268
269
5.42k
uint32_t Module::GetExtInstImportId(const char* extstr) {
270
5.42k
  for (auto& ei : ext_inst_imports_)
271
5.49k
    if (!ei.GetInOperand(0).AsString().compare(extstr)) return ei.result_id();
272
3.61k
  return 0;
273
5.42k
}
274
275
0
std::ostream& operator<<(std::ostream& str, const Module& module) {
276
0
  module.ForEachInst([&str](const Instruction* inst) {
277
0
    str << *inst;
278
0
    if (inst->opcode() != spv::Op::OpFunctionEnd) {
279
0
      str << std::endl;
280
0
    }
281
0
  });
282
0
  return str;
283
0
}
284
285
}  // namespace opt
286
}  // namespace spvtools