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 "pass.h" 19 20 #include <functional> 21 #include <tuple> 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 barrier instructions. 61 // Memory and image instructions convert coherent and volatile decorations 62 // into flags on the instruction. Barriers in tessellation shaders get the 63 // output storage semantic if appropriate. 64 void UpgradeInstructions(); 65 66 // Returns whether |id| is coherent and/or volatile. 67 std::tuple<bool, bool, SpvScope> GetInstructionAttributes(uint32_t id); 68 69 // Traces |inst| to determine if it is coherent and/or volatile. 70 // |indices| tracks the access chain indices seen so far. 71 std::pair<bool, bool> TraceInstruction(Instruction* inst, 72 std::vector<uint32_t> indices, 73 std::unordered_set<uint32_t>* visited); 74 75 // Return true if |inst| is decorated with |decoration|. 76 // If |inst| is decorated by member decorations then either |value| must 77 // match the index or |value| must be a maximum allowable value. The max 78 // value allows any element to match. 79 bool HasDecoration(const Instruction* inst, uint32_t value, 80 SpvDecoration decoration); 81 82 // Returns whether |type_id| indexed via |indices| is coherent and/or 83 // volatile. 84 std::pair<bool, bool> CheckType(uint32_t type_id, 85 const std::vector<uint32_t>& indices); 86 87 // Returns whether any type/element under |inst| is coherent and/or volatile. 88 std::pair<bool, bool> CheckAllTypes(const Instruction* inst); 89 90 // Modifies the flags of |inst| to include the new flags for the Vulkan 91 // memory model. |operation_type| indicates whether flags should use 92 // MakeVisible or MakeAvailable variants. |inst_type| indicates whether the 93 // Pointer or Texel variants of flags should be used. 94 void UpgradeFlags(Instruction* inst, uint32_t in_operand, bool is_coherent, 95 bool is_volatile, OperationType operation_type, 96 InstructionType inst_type); 97 98 // Returns the result id for a constant for |scope|. 99 uint32_t GetScopeConstant(SpvScope scope); 100 101 // Returns the value of |index_inst|. |index_inst| must be an OpConstant of 102 // integer type.g 103 uint64_t GetIndexValue(Instruction* index_inst); 104 105 // Removes coherent and volatile decorations. 106 void CleanupDecorations(); 107 108 // For all tessellation control entry points, if there is an operation on 109 // Output storage class, then all barriers are modified to include the 110 // OutputMemoryKHR semantic. 111 void UpgradeBarriers(); 112 113 // If the Vulkan memory model is specified, device scope actually means 114 // device scope. The memory scope must be modified to be QueueFamilyKHR 115 // scope. 116 void UpgradeMemoryScope(); 117 118 // Returns true if |scope_id| is SpvScopeDevice. 119 bool IsDeviceScope(uint32_t scope_id); 120 121 // Upgrades GLSL.std.450 modf and frexp. Both instructions are replaced with 122 // their struct versions. New extracts and a store are added in order to 123 // facilitate adding memory model flags. 124 void UpgradeExtInst(Instruction* modf); 125 126 // Caches the result of TraceInstruction. For a given result id and set of 127 // indices, stores whether that combination is coherent and/or volatile. 128 std::unordered_map<std::pair<uint32_t, std::vector<uint32_t>>, 129 std::pair<bool, bool>, CacheHash> 130 cache_; 131 }; 132 } // namespace opt 133 } // namespace spvtools 134 #endif // LIBSPIRV_OPT_UPGRADE_MEMORY_MODEL_H_ 135