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 <utility> |
20 | |
21 | namespace spvtools { |
22 | namespace opt { |
23 | namespace { |
24 | |
25 | const uint32_t kTypePointerTypeIdInIdx = 1; |
26 | |
27 | } // anonymous namespace |
28 | |
29 | bool InlineOpaquePass::IsOpaqueType(uint32_t typeId) { |
30 | const Instruction* typeInst = get_def_use_mgr()->GetDef(typeId); |
31 | switch (typeInst->opcode()) { |
32 | case SpvOpTypeSampler: |
33 | case SpvOpTypeImage: |
34 | case SpvOpTypeSampledImage: |
35 | return true; |
36 | case SpvOpTypePointer: |
37 | return IsOpaqueType( |
38 | typeInst->GetSingleWordInOperand(kTypePointerTypeIdInIdx)); |
39 | default: |
40 | break; |
41 | } |
42 | // TODO(greg-lunarg): Handle arrays containing opaque type |
43 | if (typeInst->opcode() != SpvOpTypeStruct) return false; |
44 | // Return true if any member is opaque |
45 | return !typeInst->WhileEachInId([this](const uint32_t* tid) { |
46 | if (IsOpaqueType(*tid)) return false; |
47 | return true; |
48 | }); |
49 | } |
50 | |
51 | bool InlineOpaquePass::HasOpaqueArgsOrReturn(const Instruction* callInst) { |
52 | // Check return type |
53 | if (IsOpaqueType(callInst->type_id())) return true; |
54 | // Check args |
55 | int icnt = 0; |
56 | return !callInst->WhileEachInId([&icnt, this](const uint32_t* iid) { |
57 | if (icnt > 0) { |
58 | const Instruction* argInst = get_def_use_mgr()->GetDef(*iid); |
59 | if (IsOpaqueType(argInst->type_id())) return false; |
60 | } |
61 | ++icnt; |
62 | return true; |
63 | }); |
64 | } |
65 | |
66 | Pass::Status InlineOpaquePass::InlineOpaque(Function* func) { |
67 | bool modified = false; |
68 | // Using block iterators here because of block erasures and insertions. |
69 | for (auto bi = func->begin(); bi != func->end(); ++bi) { |
70 | for (auto ii = bi->begin(); ii != bi->end();) { |
71 | if (IsInlinableFunctionCall(&*ii) && HasOpaqueArgsOrReturn(&*ii)) { |
72 | // Inline call. |
73 | std::vector<std::unique_ptr<BasicBlock>> newBlocks; |
74 | std::vector<std::unique_ptr<Instruction>> newVars; |
75 | if (!GenInlineCode(&newBlocks, &newVars, ii, bi)) { |
76 | return Status::Failure; |
77 | } |
78 | |
79 | // If call block is replaced with more than one block, point |
80 | // succeeding phis at new last block. |
81 | if (newBlocks.size() > 1) UpdateSucceedingPhis(newBlocks); |
82 | // Replace old calling block with new block(s). |
83 | bi = bi.Erase(); |
84 | bi = bi.InsertBefore(&newBlocks); |
85 | // Insert new function variables. |
86 | if (newVars.size() > 0) |
87 | func->begin()->begin().InsertBefore(std::move(newVars)); |
88 | // Restart inlining at beginning of calling block. |
89 | ii = bi->begin(); |
90 | modified = true; |
91 | } else { |
92 | ++ii; |
93 | } |
94 | } |
95 | } |
96 | return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange); |
97 | } |
98 | |
99 | void InlineOpaquePass::Initialize() { InitializeInline(); } |
100 | |
101 | Pass::Status InlineOpaquePass::ProcessImpl() { |
102 | Status status = Status::SuccessWithoutChange; |
103 | // Do opaque inlining on each function in entry point call tree |
104 | ProcessFunction pfn = [&status, this](Function* fp) { |
105 | status = CombineStatus(status, InlineOpaque(fp)); |
106 | return false; |
107 | }; |
108 | context()->ProcessEntryPointCallTree(pfn); |
109 | return status; |
110 | } |
111 | |
112 | InlineOpaquePass::InlineOpaquePass() = default; |
113 | |
114 | Pass::Status InlineOpaquePass::Process() { |
115 | Initialize(); |
116 | return ProcessImpl(); |
117 | } |
118 | |
119 | } // namespace opt |
120 | } // namespace spvtools |
121 | |