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 IsOpaqueType(uint32_t typeId)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 HasOpaqueArgsOrReturn(const Instruction * callInst)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 InlineOpaque(Function * func)66bool 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 GenInlineCode(&newBlocks, &newVars, ii, bi); 76 // If call block is replaced with more than one block, point 77 // succeeding phis at new last block. 78 if (newBlocks.size() > 1) UpdateSucceedingPhis(newBlocks); 79 // Replace old calling block with new block(s). 80 bi = bi.Erase(); 81 bi = bi.InsertBefore(&newBlocks); 82 // Insert new function variables. 83 if (newVars.size() > 0) 84 func->begin()->begin().InsertBefore(std::move(newVars)); 85 // Restart inlining at beginning of calling block. 86 ii = bi->begin(); 87 modified = true; 88 } else { 89 ++ii; 90 } 91 } 92 } 93 return modified; 94 } 95 Initialize()96void InlineOpaquePass::Initialize() { InitializeInline(); } 97 ProcessImpl()98Pass::Status InlineOpaquePass::ProcessImpl() { 99 // Do opaque inlining on each function in entry point call tree 100 ProcessFunction pfn = [this](Function* fp) { return InlineOpaque(fp); }; 101 bool modified = context()->ProcessEntryPointCallTree(pfn); 102 return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; 103 } 104 105 InlineOpaquePass::InlineOpaquePass() = default; 106 Process()107Pass::Status InlineOpaquePass::Process() { 108 Initialize(); 109 return ProcessImpl(); 110 } 111 112 } // namespace opt 113 } // namespace spvtools 114