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