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
21namespace spvtools {
22namespace opt {
23namespace {
24
25const uint32_t kTypePointerTypeIdInIdx = 1;
26
27} // anonymous namespace
28
29bool 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
51bool 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
66Pass::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
99void InlineOpaquePass::Initialize() { InitializeInline(); }
100
101Pass::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
112InlineOpaquePass::InlineOpaquePass() = default;
113
114Pass::Status InlineOpaquePass::Process() {
115 Initialize();
116 return ProcessImpl();
117}
118
119} // namespace opt
120} // namespace spvtools
121