1 // Copyright (c) 2018 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #ifndef LIBSPIRV_OPT_UPGRADE_MEMORY_MODEL_H_ 16 #define LIBSPIRV_OPT_UPGRADE_MEMORY_MODEL_H_ 17 18 #include <functional> 19 #include <tuple> 20 21 #include "pass.h" 22 23 namespace spvtools { 24 namespace opt { 25 26 // Hashing functor for the memoized result store. 27 struct CacheHash { operatorCacheHash28 size_t operator()( 29 const std::pair<uint32_t, std::vector<uint32_t>>& item) const { 30 std::u32string to_hash; 31 to_hash.push_back(item.first); 32 for (auto i : item.second) to_hash.push_back(i); 33 return std::hash<std::u32string>()(to_hash); 34 } 35 }; 36 37 // Upgrades the memory model from Logical GLSL450 to Logical VulkanKHR. 38 // 39 // This pass remove deprecated decorations (Volatile and Coherent) and replaces 40 // them with new flags on individual instructions. It adds the Output storage 41 // class semantic to control barriers in tessellation control shaders that have 42 // an access to Output memory. 43 class UpgradeMemoryModel : public Pass { 44 public: name()45 const char* name() const override { return "upgrade-memory-model"; } 46 Status Process() override; 47 48 private: 49 // Used to indicate whether the operation performs an availability or 50 // visibility operation. 51 enum OperationType { kVisibility, kAvailability }; 52 53 // Used to indicate whether the instruction is a memory or image instruction. 54 enum InstructionType { kMemory, kImage }; 55 56 // Modifies the OpMemoryModel to use VulkanKHR. Adds the Vulkan memory model 57 // capability and extension. 58 void UpgradeMemoryModelInstruction(); 59 60 // Upgrades memory, image and atomic instructions. 61 // Memory and image instructions convert coherent and volatile decorations 62 // into flags on the instruction. 63 // Atomic memory semantics convert volatile decoration into flags on the 64 // instruction. 65 void UpgradeInstructions(); 66 67 // Upgrades memory and image operands for instructions that have them. 68 void UpgradeMemoryAndImages(); 69 70 // Adds the volatile memory semantic if necessary. 71 void UpgradeAtomics(); 72 73 // Returns whether |id| is coherent and/or volatile. 74 std::tuple<bool, bool, SpvScope> GetInstructionAttributes(uint32_t id); 75 76 // Traces |inst| to determine if it is coherent and/or volatile. 77 // |indices| tracks the access chain indices seen so far. 78 std::pair<bool, bool> TraceInstruction(Instruction* inst, 79 std::vector<uint32_t> indices, 80 std::unordered_set<uint32_t>* visited); 81 82 // Return true if |inst| is decorated with |decoration|. 83 // If |inst| is decorated by member decorations then either |value| must 84 // match the index or |value| must be a maximum allowable value. The max 85 // value allows any element to match. 86 bool HasDecoration(const Instruction* inst, uint32_t value, 87 SpvDecoration decoration); 88 89 // Returns whether |type_id| indexed via |indices| is coherent and/or 90 // volatile. 91 std::pair<bool, bool> CheckType(uint32_t type_id, 92 const std::vector<uint32_t>& indices); 93 94 // Returns whether any type/element under |inst| is coherent and/or volatile. 95 std::pair<bool, bool> CheckAllTypes(const Instruction* inst); 96 97 // Modifies the flags of |inst| to include the new flags for the Vulkan 98 // memory model. |operation_type| indicates whether flags should use 99 // MakeVisible or MakeAvailable variants. |inst_type| indicates whether the 100 // Pointer or Texel variants of flags should be used. 101 void UpgradeFlags(Instruction* inst, uint32_t in_operand, bool is_coherent, 102 bool is_volatile, OperationType operation_type, 103 InstructionType inst_type); 104 105 // Modifies the semantics at |in_operand| of |inst| to include the volatile 106 // bit if |is_volatile| is true. 107 void UpgradeSemantics(Instruction* inst, uint32_t in_operand, 108 bool is_volatile); 109 110 // Returns the result id for a constant for |scope|. 111 uint32_t GetScopeConstant(SpvScope scope); 112 113 // Returns the value of |index_inst|. |index_inst| must be an OpConstant of 114 // integer type.g 115 uint64_t GetIndexValue(Instruction* index_inst); 116 117 // Removes coherent and volatile decorations. 118 void CleanupDecorations(); 119 120 // For all tessellation control entry points, if there is an operation on 121 // Output storage class, then all barriers are modified to include the 122 // OutputMemoryKHR semantic. 123 void UpgradeBarriers(); 124 125 // If the Vulkan memory model is specified, device scope actually means 126 // device scope. The memory scope must be modified to be QueueFamilyKHR 127 // scope. 128 void UpgradeMemoryScope(); 129 130 // Returns true if |scope_id| is SpvScopeDevice. 131 bool IsDeviceScope(uint32_t scope_id); 132 133 // Upgrades GLSL.std.450 modf and frexp. Both instructions are replaced with 134 // their struct versions. New extracts and a store are added in order to 135 // facilitate adding memory model flags. 136 void UpgradeExtInst(Instruction* modf); 137 138 // Returns the number of words taken up by a memory access argument and its 139 // implied operands. 140 uint32_t MemoryAccessNumWords(uint32_t mask); 141 142 // Caches the result of TraceInstruction. For a given result id and set of 143 // indices, stores whether that combination is coherent and/or volatile. 144 std::unordered_map<std::pair<uint32_t, std::vector<uint32_t>>, 145 std::pair<bool, bool>, CacheHash> 146 cache_; 147 }; 148 } // namespace opt 149 } // namespace spvtools 150 #endif // LIBSPIRV_OPT_UPGRADE_MEMORY_MODEL_H_ 151