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
23namespace spvtools {
24namespace opt {
25
26// Hashing functor for the memoized result store.
27struct CacheHash {
28 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.
43class UpgradeMemoryModel : public Pass {
44 public:
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