Coverage Report

Created: 2026-06-08 06:54

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/spirv-tools/source/opt/inline_opaque_pass.cpp
Line
Count
Source
1
// Copyright (c) 2017 The Khronos Group Inc.
2
// Copyright (c) 2017 Valve Corporation
3
// Copyright (c) 2017 LunarG Inc.
4
//
5
// Licensed under the Apache License, Version 2.0 (the "License");
6
// you may not use this file except in compliance with the License.
7
// You may obtain a copy of the License at
8
//
9
//     http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing, software
12
// distributed under the License is distributed on an "AS IS" BASIS,
13
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
// See the License for the specific language governing permissions and
15
// limitations under the License.
16
17
#include "source/opt/inline_opaque_pass.h"
18
19
#include <iterator>
20
#include <utility>
21
22
namespace spvtools {
23
namespace opt {
24
namespace {
25
constexpr uint32_t kTypePointerTypeIdInIdx = 1;
26
}  // namespace
27
28
0
bool InlineOpaquePass::IsOpaqueType(uint32_t typeId) {
29
0
  const Instruction* typeInst = get_def_use_mgr()->GetDef(typeId);
30
0
  switch (typeInst->opcode()) {
31
0
    case spv::Op::OpTypeSampler:
32
0
    case spv::Op::OpTypeImage:
33
0
    case spv::Op::OpTypeSampledImage:
34
0
      return true;
35
0
    case spv::Op::OpTypePointer:
36
0
      return IsOpaqueType(
37
0
          typeInst->GetSingleWordInOperand(kTypePointerTypeIdInIdx));
38
0
    default:
39
0
      break;
40
0
  }
41
  // TODO(greg-lunarg): Handle arrays containing opaque type
42
0
  if (typeInst->opcode() != spv::Op::OpTypeStruct) return false;
43
  // Return true if any member is opaque
44
0
  return !typeInst->WhileEachInId([this](const uint32_t* tid) {
45
0
    if (IsOpaqueType(*tid)) return false;
46
0
    return true;
47
0
  });
48
0
}
49
50
0
bool InlineOpaquePass::HasOpaqueArgsOrReturn(const Instruction* callInst) {
51
  // Check return type
52
0
  if (IsOpaqueType(callInst->type_id())) return true;
53
  // Check args
54
0
  int icnt = 0;
55
0
  return !callInst->WhileEachInId([&icnt, this](const uint32_t* iid) {
56
0
    if (icnt > 0) {
57
0
      const Instruction* argInst = get_def_use_mgr()->GetDef(*iid);
58
0
      if (IsOpaqueType(argInst->type_id())) return false;
59
0
    }
60
0
    ++icnt;
61
0
    return true;
62
0
  });
63
0
}
64
65
0
Pass::Status InlineOpaquePass::InlineOpaque(Function* func) {
66
0
  bool modified = false;
67
  // Using block iterators here because of block erasures and insertions.
68
0
  for (auto bi = func->begin(); bi != func->end(); ++bi) {
69
0
    for (auto ii = bi->begin(); ii != bi->end();) {
70
0
      if (IsInlinableFunctionCall(&*ii) && HasOpaqueArgsOrReturn(&*ii)) {
71
        // Save instruction before the call to avoid redundant re-scanning.
72
0
        Instruction* prev_inst =
73
0
            (ii == bi->begin()) ? nullptr : &*std::prev(ii);
74
75
        // Inline call.
76
0
        std::vector<std::unique_ptr<BasicBlock>> newBlocks;
77
0
        std::vector<std::unique_ptr<Instruction>> newVars;
78
0
        if (!GenInlineCode(&newBlocks, &newVars, ii, bi)) {
79
0
          return Status::Failure;
80
0
        }
81
82
        // If call block is replaced with more than one block, point
83
        // succeeding phis at new last block.
84
0
        if (newBlocks.size() > 1) UpdateSucceedingPhis(newBlocks);
85
        // Replace old calling block with new block(s).
86
0
        bi = bi.Erase();
87
88
0
        for (auto& bb : newBlocks) {
89
0
          bb->SetParent(func);
90
0
        }
91
0
        bi = bi.InsertBefore(&newBlocks);
92
        // Insert new function variables.
93
0
        if (newVars.size() > 0)
94
0
          func->begin()->begin().InsertBefore(std::move(newVars));
95
        // Restart inlining at the first instruction of the inlined code.
96
0
        ii = prev_inst ? ++InstructionList::iterator(prev_inst) : bi->begin();
97
0
        modified = true;
98
0
      } else {
99
0
        ++ii;
100
0
      }
101
0
    }
102
0
  }
103
104
0
  if (modified) {
105
0
    FixDebugDeclares(func);
106
0
  }
107
108
0
  return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange);
109
0
}
110
111
0
void InlineOpaquePass::Initialize() { InitializeInline(); }
112
113
0
Pass::Status InlineOpaquePass::ProcessImpl() {
114
0
  Status status = Status::SuccessWithoutChange;
115
  // Do opaque inlining on each function in entry point call tree
116
0
  ProcessFunction pfn = [&status, this](Function* fp) {
117
0
    status = CombineStatus(status, InlineOpaque(fp));
118
0
    return false;
119
0
  };
120
0
  context()->ProcessReachableCallTree(pfn);
121
0
  return status;
122
0
}
123
124
0
InlineOpaquePass::InlineOpaquePass() = default;
125
126
0
Pass::Status InlineOpaquePass::Process() {
127
0
  Initialize();
128
0
  return ProcessImpl();
129
0
}
130
131
}  // namespace opt
132
}  // namespace spvtools