Coverage Report

Created: 2026-06-30 06:51

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/spirv-tools/source/opt/ir_loader.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/ir_loader.h"
16
17
#include <utility>
18
19
#include "DebugInfo.h"
20
#include "OpenCLDebugInfo100.h"
21
#include "source/ext_inst.h"
22
#include "source/opt/ir_context.h"
23
#include "source/opt/log.h"
24
#include "source/opt/reflect.h"
25
#include "source/util/make_unique.h"
26
27
namespace spvtools {
28
namespace opt {
29
namespace {
30
constexpr uint32_t kExtInstSetIndex = 4;
31
constexpr uint32_t kLexicalScopeIndex = 5;
32
constexpr uint32_t kInlinedAtIndex = 6;
33
}  // namespace
34
35
IrLoader::IrLoader(const MessageConsumer& consumer, Module* m)
36
62.6k
    : consumer_(consumer),
37
62.6k
      module_(m),
38
62.6k
      source_("<instruction>"),
39
62.6k
      inst_index_(0),
40
62.6k
      last_dbg_scope_(kNoDebugScope, kNoInlinedAt) {}
41
42
26.0M
bool IsLineInst(const spv_parsed_instruction_t* inst) {
43
26.0M
  const auto opcode = static_cast<spv::Op>(inst->opcode);
44
26.0M
  if (IsOpLineInst(opcode)) return true;
45
25.9M
  if (!spvIsExtendedInstruction(opcode)) return false;
46
107k
  if (inst->ext_inst_type != SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100)
47
106k
    return false;
48
120
  const uint32_t ext_inst_index = inst->words[kExtInstSetIndex];
49
120
  const NonSemanticShaderDebugInfoInstructions ext_inst_key =
50
120
      NonSemanticShaderDebugInfoInstructions(ext_inst_index);
51
120
  return ext_inst_key == NonSemanticShaderDebugInfoDebugLine ||
52
120
         ext_inst_key == NonSemanticShaderDebugInfoDebugNoLine;
53
107k
}
54
55
26.0M
bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) {
56
26.0M
  ++inst_index_;
57
26.0M
  if (IsLineInst(inst)) {
58
21.9k
    module()->SetContainsDebugInfo();
59
21.9k
    last_line_inst_.reset();
60
21.9k
    dbg_line_info_.emplace_back(module()->context(), *inst, last_dbg_scope_);
61
21.9k
    return true;
62
21.9k
  }
63
64
  // If it is a DebugScope or DebugNoScope of debug extension, we do not
65
  // create a new instruction, but simply keep the information in
66
  // struct DebugScope.
67
25.9M
  const auto opcode = static_cast<spv::Op>(inst->opcode);
68
25.9M
  if (spvIsExtendedInstruction(opcode) &&
69
107k
      spvExtInstIsDebugInfo(inst->ext_inst_type)) {
70
529
    const uint32_t ext_inst_index = inst->words[kExtInstSetIndex];
71
529
    if (inst->ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 ||
72
434
        inst->ext_inst_type ==
73
434
            SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) {
74
215
      const CommonDebugInfoInstructions ext_inst_key =
75
215
          CommonDebugInfoInstructions(ext_inst_index);
76
215
      if (ext_inst_key == CommonDebugInfoDebugScope) {
77
8
        uint32_t inlined_at = 0;
78
8
        if (inst->num_words > kInlinedAtIndex)
79
6
          inlined_at = inst->words[kInlinedAtIndex];
80
8
        last_dbg_scope_ =
81
8
            DebugScope(inst->words[kLexicalScopeIndex], inlined_at);
82
8
        module()->SetContainsDebugInfo();
83
8
        return true;
84
8
      }
85
207
      if (ext_inst_key == CommonDebugInfoDebugNoScope) {
86
1
        last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt);
87
1
        module()->SetContainsDebugInfo();
88
1
        return true;
89
1
      }
90
314
    } else {
91
314
      const DebugInfoInstructions ext_inst_key =
92
314
          DebugInfoInstructions(ext_inst_index);
93
314
      if (ext_inst_key == DebugInfoDebugScope) {
94
85
        uint32_t inlined_at = 0;
95
85
        if (inst->num_words > kInlinedAtIndex)
96
14
          inlined_at = inst->words[kInlinedAtIndex];
97
85
        last_dbg_scope_ =
98
85
            DebugScope(inst->words[kLexicalScopeIndex], inlined_at);
99
85
        module()->SetContainsDebugInfo();
100
85
        return true;
101
85
      }
102
229
      if (ext_inst_key == DebugInfoDebugNoScope) {
103
19
        last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt);
104
19
        module()->SetContainsDebugInfo();
105
19
        return true;
106
19
      }
107
229
    }
108
529
  }
109
110
25.9M
  std::unique_ptr<Instruction> spv_inst(
111
25.9M
      new Instruction(module()->context(), *inst, std::move(dbg_line_info_)));
112
25.9M
  if (!spv_inst->dbg_line_insts().empty()) {
113
4.93k
    if (extra_line_tracking_ &&
114
4.93k
        (!spv_inst->dbg_line_insts().back().IsNoLine())) {
115
785
      last_line_inst_ = std::unique_ptr<Instruction>(
116
785
          spv_inst->dbg_line_insts().back().Clone(module()->context()));
117
785
      if (last_line_inst_->IsDebugLineInst())
118
0
        last_line_inst_->SetResultId(module()->context()->TakeNextId());
119
785
    }
120
4.93k
    dbg_line_info_.clear();
121
25.9M
  } else if (last_line_inst_ != nullptr) {
122
1.32k
    last_line_inst_->SetDebugScope(last_dbg_scope_);
123
1.32k
    spv_inst->dbg_line_insts().push_back(*last_line_inst_);
124
1.32k
    last_line_inst_ = std::unique_ptr<Instruction>(
125
1.32k
        spv_inst->dbg_line_insts().back().Clone(module()->context()));
126
1.32k
    if (last_line_inst_->IsDebugLineInst())
127
0
      last_line_inst_->SetResultId(module()->context()->TakeNextId());
128
1.32k
  }
129
130
25.9M
  const char* src = source_.c_str();
131
25.9M
  spv_position_t loc = {inst_index_, 0, 0};
132
133
  // Handle function and basic block boundaries first, then normal
134
  // instructions.
135
25.9M
  if (opcode == spv::Op::OpFunction) {
136
75.7k
    if (function_ != nullptr) {
137
2
      Error(consumer_, src, loc, "function inside function");
138
2
      return false;
139
2
    }
140
75.7k
    function_ = MakeUnique<Function>(std::move(spv_inst));
141
25.9M
  } else if (opcode == spv::Op::OpFunctionEnd) {
142
74.1k
    if (function_ == nullptr) {
143
5
      Error(consumer_, src, loc,
144
5
            "OpFunctionEnd without corresponding OpFunction");
145
5
      return false;
146
5
    }
147
74.1k
    if (block_ != nullptr) {
148
3
      Error(consumer_, src, loc, "OpFunctionEnd inside basic block");
149
3
      return false;
150
3
    }
151
74.1k
    function_->SetFunctionEnd(std::move(spv_inst));
152
74.1k
    module_->AddFunction(std::move(function_));
153
74.1k
    function_ = nullptr;
154
25.8M
  } else if (opcode == spv::Op::OpLabel) {
155
765k
    if (function_ == nullptr) {
156
2
      Error(consumer_, src, loc, "OpLabel outside function");
157
2
      return false;
158
2
    }
159
765k
    if (block_ != nullptr) {
160
3
      Error(consumer_, src, loc, "OpLabel inside basic block");
161
3
      return false;
162
3
    }
163
765k
    block_ = MakeUnique<BasicBlock>(std::move(spv_inst));
164
25.0M
  } else if (spvOpcodeIsBlockTerminator(opcode)) {
165
764k
    if (function_ == nullptr) {
166
27
      Error(consumer_, src, loc, "terminator instruction outside function");
167
27
      return false;
168
27
    }
169
764k
    if (block_ == nullptr) {
170
10
      Error(consumer_, src, loc, "terminator instruction outside basic block");
171
10
      return false;
172
10
    }
173
764k
    if (last_dbg_scope_.GetLexicalScope() != kNoDebugScope)
174
11
      spv_inst->SetDebugScope(last_dbg_scope_);
175
764k
    block_->AddInstruction(std::move(spv_inst));
176
764k
    function_->AddBasicBlock(std::move(block_));
177
764k
    block_ = nullptr;
178
764k
    last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt);
179
764k
    last_line_inst_.reset();
180
764k
    dbg_line_info_.clear();
181
24.3M
  } else if (opcode == spv::Op::OpGraphARM) {
182
28
    if (graph_ != nullptr) {
183
2
      Error(consumer_, src, loc, "graph inside graph");
184
2
      return false;
185
2
    }
186
26
    graph_ = MakeUnique<Graph>(std::move(spv_inst));
187
24.3M
  } else if (opcode == spv::Op::OpGraphEndARM) {
188
3
    if (graph_ == nullptr) {
189
3
      Error(consumer_, src, loc,
190
3
            "OpGraphEndARM without corresponding OpGraphARM");
191
3
      return false;
192
3
    }
193
0
    graph_->SetGraphEnd(std::move(spv_inst));
194
0
    module_->AddGraph(std::move(graph_));
195
0
    graph_ = nullptr;
196
24.3M
  } else if (opcode == spv::Op::OpGraphConstantARM) {
197
28
    module_->AddGlobalValue(std::move(spv_inst));
198
24.3M
  } else if (graph_ != nullptr) {  // Inside graph definition
199
57
    if (opcode == spv::Op::OpGraphInputARM) {
200
0
      graph_->AddInput(std::move(spv_inst));
201
57
    } else if (opcode == spv::Op::OpGraphSetOutputARM) {
202
47
      graph_->AddOutput(std::move(spv_inst));
203
47
    } else {
204
10
      switch (opcode) {
205
0
        case spv::Op::OpExtInst:
206
1
        case spv::Op::OpCompositeExtract:
207
1
          graph_->AddInstruction(std::move(spv_inst));
208
1
          break;
209
9
        default:
210
9
          Errorf(consumer_, src, loc,
211
9
                 "unhandled instruction (opcode %d) inside graph", opcode);
212
9
          return false;
213
10
      }
214
10
    }
215
24.3M
  } else if (function_ != nullptr) {  // Inside function definition
216
3.32M
    if (opcode == spv::Op::OpLoopMerge || opcode == spv::Op::OpSelectionMerge)
217
233k
      last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt);
218
3.32M
    if (last_dbg_scope_.GetLexicalScope() != kNoDebugScope)
219
2.70k
      spv_inst->SetDebugScope(last_dbg_scope_);
220
3.32M
    if (spvIsExtendedInstruction(opcode) &&
221
106k
        spvExtInstIsDebugInfo(inst->ext_inst_type)) {
222
57
      const uint32_t ext_inst_index = inst->words[kExtInstSetIndex];
223
57
      if (inst->ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) {
224
0
        const OpenCLDebugInfo100Instructions ext_inst_key =
225
0
            OpenCLDebugInfo100Instructions(ext_inst_index);
226
0
        switch (ext_inst_key) {
227
0
          case OpenCLDebugInfo100DebugDeclare: {
228
0
            if (block_ == nullptr)  // Inside function but outside blocks
229
0
              function_->AddDebugInstructionInHeader(std::move(spv_inst));
230
0
            else
231
0
              block_->AddInstruction(std::move(spv_inst));
232
0
            break;
233
0
          }
234
0
          case OpenCLDebugInfo100DebugValue: {
235
0
            if (block_ == nullptr)  // Inside function but outside blocks
236
0
              function_->AddDebugInstructionInHeader(std::move(spv_inst));
237
0
            else
238
0
              block_->AddInstruction(std::move(spv_inst));
239
0
            break;
240
0
          }
241
0
          default: {
242
0
            Errorf(consumer_, src, loc,
243
0
                   "Debug info extension instruction other than DebugScope, "
244
0
                   "DebugNoScope, DebugFunctionDefinition, DebugDeclare, and "
245
0
                   "DebugValue found inside function",
246
0
                   opcode);
247
0
            return false;
248
0
          }
249
0
        }
250
57
      } else if (inst->ext_inst_type ==
251
57
                 SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) {
252
37
        const NonSemanticShaderDebugInfoInstructions ext_inst_key =
253
37
            NonSemanticShaderDebugInfoInstructions(ext_inst_index);
254
37
        switch (ext_inst_key) {
255
4
          case NonSemanticShaderDebugInfoDebugDeclare:
256
30
          case NonSemanticShaderDebugInfoDebugValue:
257
30
          case NonSemanticShaderDebugInfoDebugScope:
258
30
          case NonSemanticShaderDebugInfoDebugNoScope:
259
33
          case NonSemanticShaderDebugInfoDebugFunctionDefinition: {
260
33
            if (block_ == nullptr) {  // Inside function but outside blocks
261
27
              Errorf(consumer_, src, loc,
262
27
                     "Debug info extension instruction found inside function "
263
27
                     "but outside block",
264
27
                     opcode);
265
27
            } else {
266
6
              block_->AddInstruction(std::move(spv_inst));
267
6
            }
268
33
            break;
269
30
          }
270
4
          default: {
271
4
            Errorf(consumer_, src, loc,
272
4
                   "Debug info extension instruction other than DebugScope, "
273
4
                   "DebugNoScope, DebugDeclare, and DebugValue found inside "
274
4
                   "function",
275
4
                   opcode);
276
4
            return false;
277
30
          }
278
37
        }
279
37
      } else {
280
20
        const DebugInfoInstructions ext_inst_key =
281
20
            DebugInfoInstructions(ext_inst_index);
282
20
        switch (ext_inst_key) {
283
0
          case DebugInfoDebugDeclare: {
284
0
            if (block_ == nullptr)  // Inside function but outside blocks
285
0
              function_->AddDebugInstructionInHeader(std::move(spv_inst));
286
0
            else
287
0
              block_->AddInstruction(std::move(spv_inst));
288
0
            break;
289
0
          }
290
18
          case DebugInfoDebugValue: {
291
18
            if (block_ == nullptr)  // Inside function but outside blocks
292
6
              function_->AddDebugInstructionInHeader(std::move(spv_inst));
293
12
            else
294
12
              block_->AddInstruction(std::move(spv_inst));
295
18
            break;
296
0
          }
297
2
          default: {
298
2
            Errorf(consumer_, src, loc,
299
2
                   "Debug info extension instruction other than DebugScope, "
300
2
                   "DebugNoScope, DebugDeclare, and DebugValue found inside "
301
2
                   "function",
302
2
                   opcode);
303
2
            return false;
304
0
          }
305
20
        }
306
20
      }
307
3.32M
    } else {
308
3.32M
      if (block_ == nullptr) {  // Inside function but outside blocks
309
40.2k
        if (opcode != spv::Op::OpFunctionParameter) {
310
65
          Errorf(consumer_, src, loc,
311
65
                 "Non-OpFunctionParameter (opcode: %d) found inside "
312
65
                 "function but outside basic block",
313
65
                 opcode);
314
65
          return false;
315
65
        }
316
40.1k
        function_->AddParameter(std::move(spv_inst));
317
3.28M
      } else {
318
3.28M
        block_->AddInstruction(std::move(spv_inst));
319
3.28M
      }
320
3.32M
    }
321
20.9M
  } else {  // Outside function or graph definition
322
20.9M
    SPIRV_ASSERT(consumer_, function_ == nullptr);
323
20.9M
    SPIRV_ASSERT(consumer_, block_ == nullptr);
324
20.9M
    SPIRV_ASSERT(consumer_, graph_ == nullptr);
325
20.9M
    if (opcode == spv::Op::OpCapability ||
326
20.9M
        opcode == spv::Op::OpConditionalCapabilityINTEL) {
327
71.9k
      module_->AddCapability(std::move(spv_inst));
328
20.9M
    } else if (opcode == spv::Op::OpExtension ||
329
20.8M
               opcode == spv::Op::OpConditionalExtensionINTEL) {
330
27.5k
      module_->AddExtension(std::move(spv_inst));
331
20.8M
    } else if (opcode == spv::Op::OpExtInstImport) {
332
25.4k
      module_->AddExtInstImport(std::move(spv_inst));
333
20.8M
    } else if (opcode == spv::Op::OpMemoryModel) {
334
54.5k
      module_->SetMemoryModel(std::move(spv_inst));
335
20.8M
    } else if (opcode == spv::Op::OpSamplerImageAddressingModeNV) {
336
97
      module_->SetSampledImageAddressMode(std::move(spv_inst));
337
20.8M
    } else if (opcode == spv::Op::OpEntryPoint) {
338
42.9k
      module_->AddEntryPoint(std::move(spv_inst));
339
20.7M
    } else if (opcode == spv::Op::OpGraphEntryPointARM) {
340
61
      module_->AddGraphEntryPoint(std::move(spv_inst));
341
20.7M
    } else if (opcode == spv::Op::OpExecutionMode ||
342
20.7M
               opcode == spv::Op::OpExecutionModeId) {
343
47.8k
      module_->AddExecutionMode(std::move(spv_inst));
344
20.7M
    } else if (IsDebug1Inst(opcode)) {
345
24.0k
      module_->AddDebug1Inst(std::move(spv_inst));
346
20.6M
    } else if (IsDebug2Inst(opcode)) {
347
238k
      module_->AddDebug2Inst(std::move(spv_inst));
348
20.4M
    } else if (IsDebug3Inst(opcode)) {
349
81
      module_->AddDebug3Inst(std::move(spv_inst));
350
20.4M
    } else if (IsAnnotationInst(opcode)) {
351
19.3M
      module_->AddAnnotationInst(std::move(spv_inst));
352
19.3M
    } else if (IsTypeInst(opcode)) {
353
561k
      module_->AddType(std::move(spv_inst));
354
582k
    } else if (IsConstantInst(opcode) || opcode == spv::Op::OpVariable ||
355
9.73k
               opcode == spv::Op::OpUntypedVariableKHR ||
356
581k
               opcode == spv::Op::OpUndef) {
357
581k
      module_->AddGlobalValue(std::move(spv_inst));
358
581k
    } else if (spvIsExtendedInstruction(opcode) &&
359
665
               spvExtInstIsDebugInfo(inst->ext_inst_type)) {
360
359
      module_->AddExtInstDebugInfo(std::move(spv_inst));
361
421
    } else if (spvIsExtendedInstruction(opcode) &&
362
306
               spvExtInstIsNonSemantic(inst->ext_inst_type)) {
363
      // If there are no functions or graphs, add the non-semantic instructions
364
      // to the global values. Otherwise append it to the list of the last
365
      // function or graph.
366
285
      auto func_begin = module_->begin();
367
285
      auto func_end = module_->end();
368
      // graphs come last so we need to check first
369
285
      if (module_->graphs().size() > 0) {
370
0
        module_->graphs().back()->AddNonSemanticInstruction(
371
0
            std::move(spv_inst));
372
        // then check functions
373
285
      } else if (func_begin != func_end) {
374
14
        (--func_end)->AddNonSemanticInstruction(std::move(spv_inst));
375
271
      } else {
376
271
        module_->AddGlobalValue(std::move(spv_inst));
377
271
      }
378
285
    } else {
379
136
      Errorf(consumer_, src, loc,
380
136
             "Unhandled inst type (opcode: %d) found outside function "
381
136
             "definition.",
382
136
             opcode);
383
136
      return false;
384
136
    }
385
20.9M
  }
386
25.9M
  return true;
387
25.9M
}
388
389
// Resolves internal references among the module, functions, basic blocks, etc.
390
// This function should be called after adding all instructions.
391
62.6k
void IrLoader::EndModule() {
392
62.6k
  if (block_ && function_) {
393
    // We're in the middle of a basic block, but the terminator is missing.
394
    // Register the block anyway.  This lets us write tests with less
395
    // boilerplate.
396
950
    function_->AddBasicBlock(std::move(block_));
397
950
    block_ = nullptr;
398
950
  }
399
62.6k
  if (function_) {
400
    // We're in the middle of a function, but the OpFunctionEnd is missing.
401
    // Register the function anyway.  This lets us write tests with less
402
    // boilerplate.
403
1.55k
    module_->AddFunction(std::move(function_));
404
1.55k
    function_ = nullptr;
405
1.55k
  }
406
75.7k
  for (auto& function : *module_) {
407
765k
    for (auto& bb : function) bb.SetParent(&function);
408
75.7k
  }
409
410
  // Copy any trailing Op*Line instruction into the module
411
62.6k
  module_->SetTrailingDbgLineInfo(std::move(dbg_line_info_));
412
62.6k
}
413
414
}  // namespace opt
415
}  // namespace spvtools