/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 |