spirv_cross.hpp 52 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220
  1. /*
  2. * Copyright 2015-2021 Arm Limited
  3. * SPDX-License-Identifier: Apache-2.0 OR MIT
  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. /*
  18. * At your option, you may choose to accept this material under either:
  19. * 1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
  20. * 2. The MIT License, found at <http://opensource.org/licenses/MIT>.
  21. */
  22. #ifndef SPIRV_CROSS_HPP
  23. #define SPIRV_CROSS_HPP
  24. #ifndef SPV_ENABLE_UTILITY_CODE
  25. #define SPV_ENABLE_UTILITY_CODE
  26. #endif
  27. // Pragmatic hack to avoid symbol conflicts when including both hpp11 and hpp headers in same translation unit.
  28. // This is an unfortunate SPIRV-Headers issue that we cannot easily deal with ourselves.
  29. #ifdef SPIRV_CROSS_SPV_HEADER_NAMESPACE_OVERRIDE
  30. #define spv SPIRV_CROSS_SPV_HEADER_NAMESPACE_OVERRIDE
  31. #endif
  32. #include "spirv.hpp"
  33. #include "spirv_cfg.hpp"
  34. #include "spirv_cross_parsed_ir.hpp"
  35. namespace SPIRV_CROSS_NAMESPACE
  36. {
  37. using namespace SPIRV_CROSS_SPV_HEADER_NAMESPACE;
  38. struct Resource
  39. {
  40. // Resources are identified with their SPIR-V ID.
  41. // This is the ID of the OpVariable.
  42. ID id;
  43. // The type ID of the variable which includes arrays and all type modifications.
  44. // This type ID is not suitable for parsing OpMemberDecoration of a struct and other decorations in general
  45. // since these modifications typically happen on the base_type_id.
  46. TypeID type_id;
  47. // The base type of the declared resource.
  48. // This type is the base type which ignores pointers and arrays of the type_id.
  49. // This is mostly useful to parse decorations of the underlying type.
  50. // base_type_id can also be obtained with get_type(get_type(type_id).self).
  51. TypeID base_type_id;
  52. // The declared name (OpName) of the resource.
  53. // For Buffer blocks, the name actually reflects the externally
  54. // visible Block name.
  55. //
  56. // This name can be retrieved again by using either
  57. // get_name(id) or get_name(base_type_id) depending if it's a buffer block or not.
  58. //
  59. // This name can be an empty string in which case get_fallback_name(id) can be
  60. // used which obtains a suitable fallback identifier for an ID.
  61. std::string name;
  62. };
  63. struct BuiltInResource
  64. {
  65. // This is mostly here to support reflection of builtins such as Position/PointSize/CullDistance/ClipDistance.
  66. // This needs to be different from Resource since we can collect builtins from blocks.
  67. // A builtin present here does not necessarily mean it's considered an active builtin,
  68. // since variable ID "activeness" is only tracked on OpVariable level, not Block members.
  69. // For that, update_active_builtins() -> has_active_builtin() can be used to further refine the reflection.
  70. BuiltIn builtin;
  71. // This is the actual value type of the builtin.
  72. // Typically float4, float, array<float, N> for the gl_PerVertex builtins.
  73. // If the builtin is a control point, the control point array type will be stripped away here as appropriate.
  74. TypeID value_type_id;
  75. // This refers to the base resource which contains the builtin.
  76. // If resource is a Block, it can hold multiple builtins, or it might not be a block.
  77. // For advanced reflection scenarios, all information in builtin/value_type_id can be deduced,
  78. // it's just more convenient this way.
  79. Resource resource;
  80. };
  81. struct ShaderResources
  82. {
  83. SmallVector<Resource> uniform_buffers;
  84. SmallVector<Resource> storage_buffers;
  85. SmallVector<Resource> stage_inputs;
  86. SmallVector<Resource> stage_outputs;
  87. SmallVector<Resource> subpass_inputs;
  88. SmallVector<Resource> storage_images;
  89. SmallVector<Resource> sampled_images;
  90. SmallVector<Resource> atomic_counters;
  91. SmallVector<Resource> acceleration_structures;
  92. SmallVector<Resource> gl_plain_uniforms;
  93. SmallVector<Resource> tensors;
  94. // There can only be one push constant block,
  95. // but keep the vector in case this restriction is lifted in the future.
  96. SmallVector<Resource> push_constant_buffers;
  97. SmallVector<Resource> shader_record_buffers;
  98. // For Vulkan GLSL and HLSL source,
  99. // these correspond to separate texture2D and samplers respectively.
  100. SmallVector<Resource> separate_images;
  101. SmallVector<Resource> separate_samplers;
  102. SmallVector<BuiltInResource> builtin_inputs;
  103. SmallVector<BuiltInResource> builtin_outputs;
  104. };
  105. struct CombinedImageSampler
  106. {
  107. // The ID of the sampler2D variable.
  108. VariableID combined_id;
  109. // The ID of the texture2D variable.
  110. VariableID image_id;
  111. // The ID of the sampler variable.
  112. VariableID sampler_id;
  113. };
  114. struct SpecializationConstant
  115. {
  116. // The ID of the specialization constant.
  117. ConstantID id;
  118. // The constant ID of the constant, used in Vulkan during pipeline creation.
  119. uint32_t constant_id;
  120. };
  121. struct BufferRange
  122. {
  123. unsigned index;
  124. size_t offset;
  125. size_t range;
  126. };
  127. enum BufferPackingStandard
  128. {
  129. BufferPackingStd140,
  130. BufferPackingStd430,
  131. BufferPackingStd140EnhancedLayout,
  132. BufferPackingStd430EnhancedLayout,
  133. BufferPackingHLSLCbuffer,
  134. BufferPackingHLSLCbufferPackOffset,
  135. BufferPackingScalar,
  136. BufferPackingScalarEnhancedLayout
  137. };
  138. struct EntryPoint
  139. {
  140. std::string name;
  141. ExecutionModel execution_model;
  142. };
  143. class Compiler
  144. {
  145. public:
  146. friend class CFG;
  147. friend class DominatorBuilder;
  148. // The constructor takes a buffer of SPIR-V words and parses it.
  149. // It will create its own parser, parse the SPIR-V and move the parsed IR
  150. // as if you had called the constructors taking ParsedIR directly.
  151. explicit Compiler(std::vector<uint32_t> ir);
  152. Compiler(const uint32_t *ir, size_t word_count);
  153. // This is more modular. We can also consume a ParsedIR structure directly, either as a move, or copy.
  154. // With copy, we can reuse the same parsed IR for multiple Compiler instances.
  155. explicit Compiler(const ParsedIR &ir);
  156. explicit Compiler(ParsedIR &&ir);
  157. virtual ~Compiler() = default;
  158. // After parsing, API users can modify the SPIR-V via reflection and call this
  159. // to disassemble the SPIR-V into the desired langauage.
  160. // Sub-classes actually implement this.
  161. virtual std::string compile();
  162. // Gets the identifier (OpName) of an ID. If not defined, an empty string will be returned.
  163. const std::string &get_name(ID id) const;
  164. // Applies a decoration to an ID. Effectively injects OpDecorate.
  165. void set_decoration(ID id, Decoration decoration, uint32_t argument = 0);
  166. void set_decoration_string(ID id, Decoration decoration, const std::string &argument);
  167. // Overrides the identifier OpName of an ID.
  168. // Identifiers beginning with underscores or identifiers which contain double underscores
  169. // are reserved by the implementation.
  170. void set_name(ID id, const std::string &name);
  171. // Gets a bitmask for the decorations which are applied to ID.
  172. // I.e. (1ull << DecorationFoo) | (1ull << DecorationBar)
  173. const Bitset &get_decoration_bitset(ID id) const;
  174. // Returns whether the decoration has been applied to the ID.
  175. bool has_decoration(ID id, Decoration decoration) const;
  176. // Gets the value for decorations which take arguments.
  177. // If the decoration is a boolean (i.e. DecorationNonWritable),
  178. // 1 will be returned.
  179. // If decoration doesn't exist or decoration is not recognized,
  180. // 0 will be returned.
  181. uint32_t get_decoration(ID id, Decoration decoration) const;
  182. const std::string &get_decoration_string(ID id, Decoration decoration) const;
  183. // Removes the decoration for an ID.
  184. void unset_decoration(ID id, Decoration decoration);
  185. // Gets the SPIR-V type associated with ID.
  186. // Mostly used with Resource::type_id and Resource::base_type_id to parse the underlying type of a resource.
  187. const SPIRType &get_type(TypeID id) const;
  188. // Gets the SPIR-V type of a variable.
  189. const SPIRType &get_type_from_variable(VariableID id) const;
  190. // Gets the underlying storage class for an OpVariable.
  191. StorageClass get_storage_class(VariableID id) const;
  192. // If get_name() is an empty string, get the fallback name which will be used
  193. // instead in the disassembled source.
  194. virtual const std::string get_fallback_name(ID id) const;
  195. // If get_name() of a Block struct is an empty string, get the fallback name.
  196. // This needs to be per-variable as multiple variables can use the same block type.
  197. virtual const std::string get_block_fallback_name(VariableID id) const;
  198. // Given an OpTypeStruct in ID, obtain the identifier for member number "index".
  199. // This may be an empty string.
  200. const std::string &get_member_name(TypeID id, uint32_t index) const;
  201. // Given an OpTypeStruct in ID, obtain the OpMemberDecoration for member number "index".
  202. uint32_t get_member_decoration(TypeID id, uint32_t index, Decoration decoration) const;
  203. const std::string &get_member_decoration_string(TypeID id, uint32_t index, Decoration decoration) const;
  204. // Sets the member identifier for OpTypeStruct ID, member number "index".
  205. void set_member_name(TypeID id, uint32_t index, const std::string &name);
  206. // Returns the qualified member identifier for OpTypeStruct ID, member number "index",
  207. // or an empty string if no qualified alias exists
  208. const std::string &get_member_qualified_name(TypeID type_id, uint32_t index) const;
  209. // Gets the decoration mask for a member of a struct, similar to get_decoration_mask.
  210. const Bitset &get_member_decoration_bitset(TypeID id, uint32_t index) const;
  211. // Returns whether the decoration has been applied to a member of a struct.
  212. bool has_member_decoration(TypeID id, uint32_t index, Decoration decoration) const;
  213. // Similar to set_decoration, but for struct members.
  214. void set_member_decoration(TypeID id, uint32_t index, Decoration decoration, uint32_t argument = 0);
  215. void set_member_decoration_string(TypeID id, uint32_t index, Decoration decoration,
  216. const std::string &argument);
  217. // Unsets a member decoration, similar to unset_decoration.
  218. void unset_member_decoration(TypeID id, uint32_t index, Decoration decoration);
  219. // Gets the fallback name for a member, similar to get_fallback_name.
  220. virtual const std::string get_fallback_member_name(uint32_t index) const
  221. {
  222. return join("_", index);
  223. }
  224. // Returns a vector of which members of a struct are potentially in use by a
  225. // SPIR-V shader. The granularity of this analysis is per-member of a struct.
  226. // This can be used for Buffer (UBO), BufferBlock/StorageBuffer (SSBO) and PushConstant blocks.
  227. // ID is the Resource::id obtained from get_shader_resources().
  228. SmallVector<BufferRange> get_active_buffer_ranges(VariableID id) const;
  229. // Returns the effective size of a buffer block.
  230. size_t get_declared_struct_size(const SPIRType &struct_type) const;
  231. // Returns the effective size of a buffer block, with a given array size
  232. // for a runtime array.
  233. // SSBOs are typically declared as runtime arrays. get_declared_struct_size() will return 0 for the size.
  234. // This is not very helpful for applications which might need to know the array stride of its last member.
  235. // This can be done through the API, but it is not very intuitive how to accomplish this, so here we provide a helper function
  236. // to query the size of the buffer, assuming that the last member has a certain size.
  237. // If the buffer does not contain a runtime array, array_size is ignored, and the function will behave as
  238. // get_declared_struct_size().
  239. // To get the array stride of the last member, something like:
  240. // get_declared_struct_size_runtime_array(type, 1) - get_declared_struct_size_runtime_array(type, 0) will work.
  241. size_t get_declared_struct_size_runtime_array(const SPIRType &struct_type, size_t array_size) const;
  242. // Returns the effective size of a buffer block struct member.
  243. size_t get_declared_struct_member_size(const SPIRType &struct_type, uint32_t index) const;
  244. // Returns a set of all global variables which are statically accessed
  245. // by the control flow graph from the current entry point.
  246. // Only variables which change the interface for a shader are returned, that is,
  247. // variables with storage class of Input, Output, Uniform, UniformConstant, PushConstant and AtomicCounter
  248. // storage classes are returned.
  249. //
  250. // To use the returned set as the filter for which variables are used during compilation,
  251. // this set can be moved to set_enabled_interface_variables().
  252. std::unordered_set<VariableID> get_active_interface_variables() const;
  253. // Sets the interface variables which are used during compilation.
  254. // By default, all variables are used.
  255. // Once set, compile() will only consider the set in active_variables.
  256. void set_enabled_interface_variables(std::unordered_set<VariableID> active_variables);
  257. // Query shader resources, use ids with reflection interface to modify or query binding points, etc.
  258. ShaderResources get_shader_resources() const;
  259. // Query shader resources, but only return the variables which are part of active_variables.
  260. // E.g.: get_shader_resources(get_active_variables()) to only return the variables which are statically
  261. // accessed.
  262. ShaderResources get_shader_resources(const std::unordered_set<VariableID> &active_variables) const;
  263. // Remapped variables are considered built-in variables and a backend will
  264. // not emit a declaration for this variable.
  265. // This is mostly useful for making use of builtins which are dependent on extensions.
  266. void set_remapped_variable_state(VariableID id, bool remap_enable);
  267. bool get_remapped_variable_state(VariableID id) const;
  268. // For subpassInput variables which are remapped to plain variables,
  269. // the number of components in the remapped
  270. // variable must be specified as the backing type of subpass inputs are opaque.
  271. void set_subpass_input_remapped_components(VariableID id, uint32_t components);
  272. uint32_t get_subpass_input_remapped_components(VariableID id) const;
  273. // All operations work on the current entry point.
  274. // Entry points can be swapped out with set_entry_point().
  275. // Entry points should be set right after the constructor completes as some reflection functions traverse the graph from the entry point.
  276. // Resource reflection also depends on the entry point.
  277. // By default, the current entry point is set to the first OpEntryPoint which appears in the SPIR-V module.
  278. // Some shader languages restrict the names that can be given to entry points, and the
  279. // corresponding backend will automatically rename an entry point name, during the call
  280. // to compile() if it is illegal. For example, the common entry point name main() is
  281. // illegal in MSL, and is renamed to an alternate name by the MSL backend.
  282. // Given the original entry point name contained in the SPIR-V, this function returns
  283. // the name, as updated by the backend during the call to compile(). If the name is not
  284. // illegal, and has not been renamed, or if this function is called before compile(),
  285. // this function will simply return the same name.
  286. // New variants of entry point query and reflection.
  287. // Names for entry points in the SPIR-V module may alias if they belong to different execution models.
  288. // To disambiguate, we must pass along with the entry point names the execution model.
  289. SmallVector<EntryPoint> get_entry_points_and_stages() const;
  290. void set_entry_point(const std::string &entry, ExecutionModel execution_model);
  291. // Renames an entry point from old_name to new_name.
  292. // If old_name is currently selected as the current entry point, it will continue to be the current entry point,
  293. // albeit with a new name.
  294. // get_entry_points() is essentially invalidated at this point.
  295. void rename_entry_point(const std::string &old_name, const std::string &new_name,
  296. ExecutionModel execution_model);
  297. const SPIREntryPoint &get_entry_point(const std::string &name, ExecutionModel execution_model) const;
  298. SPIREntryPoint &get_entry_point(const std::string &name, ExecutionModel execution_model);
  299. const std::string &get_cleansed_entry_point_name(const std::string &name,
  300. ExecutionModel execution_model) const;
  301. // Traverses all reachable opcodes and sets active_builtins to a bitmask of all builtin variables which are accessed in the shader.
  302. void update_active_builtins();
  303. bool has_active_builtin(BuiltIn builtin, StorageClass storage) const;
  304. // Query and modify OpExecutionMode.
  305. const Bitset &get_execution_mode_bitset() const;
  306. void unset_execution_mode(ExecutionMode mode);
  307. void set_execution_mode(ExecutionMode mode, uint32_t arg0 = 0, uint32_t arg1 = 0, uint32_t arg2 = 0);
  308. // Gets argument for an execution mode (LocalSize, Invocations, OutputVertices).
  309. // For LocalSize or LocalSizeId, the index argument is used to select the dimension (X = 0, Y = 1, Z = 2).
  310. // For execution modes which do not have arguments, 0 is returned.
  311. // LocalSizeId query returns an ID. If LocalSizeId execution mode is not used, it returns 0.
  312. // LocalSize always returns a literal. If execution mode is LocalSizeId,
  313. // the literal (spec constant or not) is still returned.
  314. uint32_t get_execution_mode_argument(ExecutionMode mode, uint32_t index = 0) const;
  315. ExecutionModel get_execution_model() const;
  316. bool is_tessellation_shader() const;
  317. bool is_tessellating_triangles() const;
  318. // In SPIR-V, the compute work group size can be represented by a constant vector, in which case
  319. // the LocalSize execution mode is ignored.
  320. //
  321. // This constant vector can be a constant vector, specialization constant vector, or partly specialized constant vector.
  322. // To modify and query work group dimensions which are specialization constants, SPIRConstant values must be modified
  323. // directly via get_constant() rather than using LocalSize directly. This function will return which constants should be modified.
  324. //
  325. // To modify dimensions which are *not* specialization constants, set_execution_mode should be used directly.
  326. // Arguments to set_execution_mode which are specialization constants are effectively ignored during compilation.
  327. // NOTE: This is somewhat different from how SPIR-V works. In SPIR-V, the constant vector will completely replace LocalSize,
  328. // while in this interface, LocalSize is only ignored for specialization constants.
  329. //
  330. // The specialization constant will be written to x, y and z arguments.
  331. // If the component is not a specialization constant, a zeroed out struct will be written.
  332. // The return value is the constant ID of the builtin WorkGroupSize, but this is not expected to be useful
  333. // for most use cases.
  334. // If LocalSizeId is used, there is no uvec3 value representing the workgroup size, so the return value is 0,
  335. // but x, y and z are written as normal if the components are specialization constants.
  336. uint32_t get_work_group_size_specialization_constants(SpecializationConstant &x, SpecializationConstant &y,
  337. SpecializationConstant &z) const;
  338. // Analyzes all OpImageFetch (texelFetch) opcodes and checks if there are instances where
  339. // said instruction is used without a combined image sampler.
  340. // GLSL targets do not support the use of texelFetch without a sampler.
  341. // To workaround this, we must inject a dummy sampler which can be used to form a sampler2D at the call-site of
  342. // texelFetch as necessary.
  343. //
  344. // This must be called before build_combined_image_samplers().
  345. // build_combined_image_samplers() may refer to the ID returned by this method if the returned ID is non-zero.
  346. // The return value will be the ID of a sampler object if a dummy sampler is necessary, or 0 if no sampler object
  347. // is required.
  348. //
  349. // If the returned ID is non-zero, it can be decorated with set/bindings as desired before calling compile().
  350. // Calling this function also invalidates get_active_interface_variables(), so this should be called
  351. // before that function.
  352. VariableID build_dummy_sampler_for_combined_images();
  353. // Analyzes all separate image and samplers used from the currently selected entry point,
  354. // and re-routes them all to a combined image sampler instead.
  355. // This is required to "support" separate image samplers in targets which do not natively support
  356. // this feature, like GLSL/ESSL.
  357. //
  358. // This must be called before compile() if such remapping is desired.
  359. // This call will add new sampled images to the SPIR-V,
  360. // so it will appear in reflection if get_shader_resources() is called after build_combined_image_samplers.
  361. //
  362. // If any image/sampler remapping was found, no separate image/samplers will appear in the decompiled output,
  363. // but will still appear in reflection.
  364. //
  365. // The resulting samplers will be void of any decorations like name, descriptor sets and binding points,
  366. // so this can be added before compile() if desired.
  367. //
  368. // Combined image samplers originating from this set are always considered active variables.
  369. // Arrays of separate samplers are not supported, but arrays of separate images are supported.
  370. // Array of images + sampler -> Array of combined image samplers.
  371. void build_combined_image_samplers();
  372. // Gets a remapping for the combined image samplers.
  373. const SmallVector<CombinedImageSampler> &get_combined_image_samplers() const
  374. {
  375. return combined_image_samplers;
  376. }
  377. // Set a new variable type remap callback.
  378. // The type remapping is designed to allow global interface variable to assume more special types.
  379. // A typical example here is to remap sampler2D into samplerExternalOES, which currently isn't supported
  380. // directly by SPIR-V.
  381. //
  382. // In compile() while emitting code,
  383. // for every variable that is declared, including function parameters, the callback will be called
  384. // and the API user has a chance to change the textual representation of the type used to declare the variable.
  385. // The API user can detect special patterns in names to guide the remapping.
  386. void set_variable_type_remap_callback(VariableTypeRemapCallback cb)
  387. {
  388. variable_remap_callback = std::move(cb);
  389. }
  390. // API for querying which specialization constants exist.
  391. // To modify a specialization constant before compile(), use get_constant(constant.id),
  392. // then update constants directly in the SPIRConstant data structure.
  393. // For composite types, the subconstants can be iterated over and modified.
  394. // constant_type is the SPIRType for the specialization constant,
  395. // which can be queried to determine which fields in the unions should be poked at.
  396. SmallVector<SpecializationConstant> get_specialization_constants() const;
  397. SPIRConstant &get_constant(ConstantID id);
  398. const SPIRConstant &get_constant(ConstantID id) const;
  399. uint32_t get_current_id_bound() const
  400. {
  401. return uint32_t(ir.ids.size());
  402. }
  403. // API for querying buffer objects.
  404. // The type passed in here should be the base type of a resource, i.e.
  405. // get_type(resource.base_type_id)
  406. // as decorations are set in the basic Block type.
  407. // The type passed in here must have these decorations set, or an exception is raised.
  408. // Only UBOs and SSBOs or sub-structs which are part of these buffer types will have these decorations set.
  409. uint32_t type_struct_member_offset(const SPIRType &type, uint32_t index) const;
  410. uint32_t type_struct_member_array_stride(const SPIRType &type, uint32_t index) const;
  411. uint32_t type_struct_member_matrix_stride(const SPIRType &type, uint32_t index) const;
  412. // Gets the offset in SPIR-V words (uint32_t) for a decoration which was originally declared in the SPIR-V binary.
  413. // The offset will point to one or more uint32_t literals which can be modified in-place before using the SPIR-V binary.
  414. // Note that adding or removing decorations using the reflection API will not change the behavior of this function.
  415. // If the decoration was declared, sets the word_offset to an offset into the provided SPIR-V binary buffer and returns true,
  416. // otherwise, returns false.
  417. // If the decoration does not have any value attached to it (e.g. DecorationRelaxedPrecision), this function will also return false.
  418. bool get_binary_offset_for_decoration(VariableID id, Decoration decoration, uint32_t &word_offset) const;
  419. // HLSL counter buffer reflection interface.
  420. // Append/Consume/Increment/Decrement in HLSL is implemented as two "neighbor" buffer objects where
  421. // one buffer implements the storage, and a single buffer containing just a lone "int" implements the counter.
  422. // To SPIR-V these will be exposed as two separate buffers, but glslang HLSL frontend emits a special indentifier
  423. // which lets us link the two buffers together.
  424. // Queries if a variable ID is a counter buffer which "belongs" to a regular buffer object.
  425. // If SPV_GOOGLE_hlsl_functionality1 is used, this can be used even with a stripped SPIR-V module.
  426. // Otherwise, this query is purely based on OpName identifiers as found in the SPIR-V module, and will
  427. // only return true if OpSource was reported HLSL.
  428. // To rely on this functionality, ensure that the SPIR-V module is not stripped.
  429. bool buffer_is_hlsl_counter_buffer(VariableID id) const;
  430. // Queries if a buffer object has a neighbor "counter" buffer.
  431. // If so, the ID of that counter buffer will be returned in counter_id.
  432. // If SPV_GOOGLE_hlsl_functionality1 is used, this can be used even with a stripped SPIR-V module.
  433. // Otherwise, this query is purely based on OpName identifiers as found in the SPIR-V module, and will
  434. // only return true if OpSource was reported HLSL.
  435. // To rely on this functionality, ensure that the SPIR-V module is not stripped.
  436. bool buffer_get_hlsl_counter_buffer(VariableID id, uint32_t &counter_id) const;
  437. // Gets the list of all SPIR-V Capabilities which were declared in the SPIR-V module.
  438. const SmallVector<Capability> &get_declared_capabilities() const;
  439. // Gets the list of all SPIR-V extensions which were declared in the SPIR-V module.
  440. const SmallVector<std::string> &get_declared_extensions() const;
  441. // When declaring buffer blocks in GLSL, the name declared in the GLSL source
  442. // might not be the same as the name declared in the SPIR-V module due to naming conflicts.
  443. // In this case, SPIRV-Cross needs to find a fallback-name, and it might only
  444. // be possible to know this name after compiling to GLSL.
  445. // This is particularly important for HLSL input and UAVs which tends to reuse the same block type
  446. // for multiple distinct blocks. For these cases it is not possible to modify the name of the type itself
  447. // because it might be unique. Instead, you can use this interface to check after compilation which
  448. // name was actually used if your input SPIR-V tends to have this problem.
  449. // For other names like remapped names for variables, etc, it's generally enough to query the name of the variables
  450. // after compiling, block names are an exception to this rule.
  451. // ID is the name of a variable as returned by Resource::id, and must be a variable with a Block-like type.
  452. //
  453. // This also applies to HLSL cbuffers.
  454. std::string get_remapped_declared_block_name(VariableID id) const;
  455. // For buffer block variables, get the decorations for that variable.
  456. // Sometimes, decorations for buffer blocks are found in member decorations instead
  457. // of direct decorations on the variable itself.
  458. // The most common use here is to check if a buffer is readonly or writeonly.
  459. Bitset get_buffer_block_flags(VariableID id) const;
  460. // Returns whether the position output is invariant
  461. bool is_position_invariant() const
  462. {
  463. return position_invariant;
  464. }
  465. protected:
  466. const uint32_t *stream(const Instruction &instr) const
  467. {
  468. // If we're not going to use any arguments, just return nullptr.
  469. // We want to avoid case where we return an out of range pointer
  470. // that trips debug assertions on some platforms.
  471. if (!instr.length)
  472. return nullptr;
  473. if (instr.is_embedded())
  474. {
  475. auto &embedded = static_cast<const EmbeddedInstruction &>(instr);
  476. assert(embedded.ops.size() == instr.length);
  477. return embedded.ops.data();
  478. }
  479. else
  480. {
  481. if (instr.offset + instr.length > ir.spirv.size())
  482. SPIRV_CROSS_THROW("Compiler::stream() out of range.");
  483. return &ir.spirv[instr.offset];
  484. }
  485. }
  486. uint32_t *stream_mutable(const Instruction &instr) const
  487. {
  488. return const_cast<uint32_t *>(stream(instr));
  489. }
  490. ParsedIR ir;
  491. // Marks variables which have global scope and variables which can alias with other variables
  492. // (SSBO, image load store, etc)
  493. SmallVector<uint32_t> global_variables;
  494. SmallVector<uint32_t> aliased_variables;
  495. SPIRFunction *current_function = nullptr;
  496. SPIRBlock *current_block = nullptr;
  497. uint32_t current_loop_level = 0;
  498. std::unordered_set<VariableID> active_interface_variables;
  499. bool check_active_interface_variables = false;
  500. void add_loop_level();
  501. void set_initializers(SPIRExpression &e)
  502. {
  503. e.emitted_loop_level = current_loop_level;
  504. }
  505. template <typename T>
  506. void set_initializers(const T &)
  507. {
  508. }
  509. // If our IDs are out of range here as part of opcodes, throw instead of
  510. // undefined behavior.
  511. template <typename T, typename... P>
  512. T &set(uint32_t id, P &&... args)
  513. {
  514. ir.add_typed_id(static_cast<Types>(T::type), id);
  515. auto &var = variant_set<T>(ir.ids[id], std::forward<P>(args)...);
  516. var.self = id;
  517. set_initializers(var);
  518. return var;
  519. }
  520. template <typename T>
  521. T &get(uint32_t id)
  522. {
  523. return variant_get<T>(ir.ids[id]);
  524. }
  525. template <typename T>
  526. T *maybe_get(uint32_t id)
  527. {
  528. if (id >= ir.ids.size())
  529. return nullptr;
  530. else if (ir.ids[id].get_type() == static_cast<Types>(T::type))
  531. return &get<T>(id);
  532. else
  533. return nullptr;
  534. }
  535. template <typename T>
  536. const T &get(uint32_t id) const
  537. {
  538. return variant_get<T>(ir.ids[id]);
  539. }
  540. template <typename T>
  541. const T *maybe_get(uint32_t id) const
  542. {
  543. if (id >= ir.ids.size())
  544. return nullptr;
  545. else if (ir.ids[id].get_type() == static_cast<Types>(T::type))
  546. return &get<T>(id);
  547. else
  548. return nullptr;
  549. }
  550. // Gets the id of SPIR-V type underlying the given type_id, which might be a pointer.
  551. uint32_t get_pointee_type_id(uint32_t type_id) const;
  552. // Gets the SPIR-V type underlying the given type, which might be a pointer.
  553. const SPIRType &get_pointee_type(const SPIRType &type) const;
  554. // Gets the SPIR-V type underlying the given type_id, which might be a pointer.
  555. const SPIRType &get_pointee_type(uint32_t type_id) const;
  556. // Gets the ID of the SPIR-V type underlying a variable.
  557. uint32_t get_variable_data_type_id(const SPIRVariable &var) const;
  558. // Gets the SPIR-V type underlying a variable.
  559. SPIRType &get_variable_data_type(const SPIRVariable &var);
  560. // Gets the SPIR-V type underlying a variable.
  561. const SPIRType &get_variable_data_type(const SPIRVariable &var) const;
  562. // Gets the SPIR-V element type underlying an array variable.
  563. SPIRType &get_variable_element_type(const SPIRVariable &var);
  564. // Gets the SPIR-V element type underlying an array variable.
  565. const SPIRType &get_variable_element_type(const SPIRVariable &var) const;
  566. // Sets the qualified member identifier for OpTypeStruct ID, member number "index".
  567. void set_member_qualified_name(uint32_t type_id, uint32_t index, const std::string &name);
  568. void set_qualified_name(uint32_t id, const std::string &name);
  569. // Returns if the given type refers to a sampled image.
  570. bool is_sampled_image_type(const SPIRType &type);
  571. const SPIREntryPoint &get_entry_point() const;
  572. SPIREntryPoint &get_entry_point();
  573. static bool is_tessellation_shader(ExecutionModel model);
  574. virtual std::string to_name(uint32_t id, bool allow_alias = true) const;
  575. bool is_builtin_variable(const SPIRVariable &var) const;
  576. bool is_builtin_type(const SPIRType &type) const;
  577. bool is_hidden_variable(const SPIRVariable &var, bool include_builtins = false) const;
  578. bool is_immutable(uint32_t id) const;
  579. bool is_member_builtin(const SPIRType &type, uint32_t index, BuiltIn *builtin) const;
  580. bool is_scalar(const SPIRType &type) const;
  581. bool is_vector(const SPIRType &type) const;
  582. bool is_matrix(const SPIRType &type) const;
  583. bool is_array(const SPIRType &type) const;
  584. bool is_pointer(const SPIRType &type) const;
  585. bool is_physical_pointer(const SPIRType &type) const;
  586. bool is_physical_or_buffer_pointer(const SPIRType &type) const;
  587. bool is_physical_pointer_to_buffer_block(const SPIRType &type) const;
  588. static bool is_runtime_size_array(const SPIRType &type);
  589. uint32_t expression_type_id(uint32_t id) const;
  590. const SPIRType &expression_type(uint32_t id) const;
  591. bool expression_is_lvalue(uint32_t id) const;
  592. bool variable_storage_is_aliased(const SPIRVariable &var);
  593. SPIRVariable *maybe_get_backing_variable(uint32_t chain);
  594. void register_read(uint32_t expr, uint32_t chain, bool forwarded);
  595. void register_write(uint32_t chain);
  596. inline bool is_continue(uint32_t next) const
  597. {
  598. return (ir.block_meta[next] & ParsedIR::BLOCK_META_CONTINUE_BIT) != 0;
  599. }
  600. inline bool is_single_block_loop(uint32_t next) const
  601. {
  602. auto &block = get<SPIRBlock>(next);
  603. return block.merge == SPIRBlock::MergeLoop && block.continue_block == ID(next);
  604. }
  605. inline bool is_break(uint32_t next) const
  606. {
  607. return (ir.block_meta[next] &
  608. (ParsedIR::BLOCK_META_LOOP_MERGE_BIT | ParsedIR::BLOCK_META_MULTISELECT_MERGE_BIT)) != 0;
  609. }
  610. inline bool is_loop_break(uint32_t next) const
  611. {
  612. return (ir.block_meta[next] & ParsedIR::BLOCK_META_LOOP_MERGE_BIT) != 0;
  613. }
  614. inline bool is_conditional(uint32_t next) const
  615. {
  616. return (ir.block_meta[next] &
  617. (ParsedIR::BLOCK_META_SELECTION_MERGE_BIT | ParsedIR::BLOCK_META_MULTISELECT_MERGE_BIT)) != 0;
  618. }
  619. // Dependency tracking for temporaries read from variables.
  620. void flush_dependees(SPIRVariable &var);
  621. void flush_all_active_variables();
  622. void flush_control_dependent_expressions(uint32_t block);
  623. void flush_all_atomic_capable_variables();
  624. void flush_all_aliased_variables();
  625. void register_global_read_dependencies(const SPIRBlock &func, uint32_t id);
  626. void register_global_read_dependencies(const SPIRFunction &func, uint32_t id);
  627. std::unordered_set<uint32_t> invalid_expressions;
  628. void update_name_cache(std::unordered_set<std::string> &cache, std::string &name);
  629. // A variant which takes two sets of names. The secondary is only used to verify there are no collisions,
  630. // but the set is not updated when we have found a new name.
  631. // Used primarily when adding block interface names.
  632. void update_name_cache(std::unordered_set<std::string> &cache_primary,
  633. const std::unordered_set<std::string> &cache_secondary, std::string &name);
  634. bool function_is_pure(const SPIRFunction &func);
  635. bool block_is_pure(const SPIRBlock &block);
  636. bool function_is_control_dependent(const SPIRFunction &func);
  637. bool block_is_control_dependent(const SPIRBlock &block);
  638. bool execution_is_branchless(const SPIRBlock &from, const SPIRBlock &to) const;
  639. bool execution_is_direct_branch(const SPIRBlock &from, const SPIRBlock &to) const;
  640. bool execution_is_noop(const SPIRBlock &from, const SPIRBlock &to) const;
  641. SPIRBlock::ContinueBlockType continue_block_type(const SPIRBlock &continue_block) const;
  642. void force_recompile();
  643. void force_recompile_guarantee_forward_progress();
  644. void clear_force_recompile();
  645. bool is_forcing_recompilation() const;
  646. bool is_force_recompile = false;
  647. bool is_force_recompile_forward_progress = false;
  648. bool block_is_noop(const SPIRBlock &block) const;
  649. bool block_is_loop_candidate(const SPIRBlock &block, SPIRBlock::Method method) const;
  650. bool types_are_logically_equivalent(const SPIRType &a, const SPIRType &b) const;
  651. void inherit_expression_dependencies(uint32_t dst, uint32_t source);
  652. void add_implied_read_expression(SPIRExpression &e, uint32_t source);
  653. void add_implied_read_expression(SPIRAccessChain &e, uint32_t source);
  654. void add_active_interface_variable(uint32_t var_id);
  655. // For proper multiple entry point support, allow querying if an Input or Output
  656. // variable is part of that entry points interface.
  657. bool interface_variable_exists_in_entry_point(uint32_t id) const;
  658. SmallVector<CombinedImageSampler> combined_image_samplers;
  659. void remap_variable_type_name(const SPIRType &type, const std::string &var_name, std::string &type_name) const
  660. {
  661. if (variable_remap_callback)
  662. variable_remap_callback(type, var_name, type_name);
  663. }
  664. void set_ir(const ParsedIR &parsed);
  665. void set_ir(ParsedIR &&parsed);
  666. void parse_fixup();
  667. // Used internally to implement various traversals for queries.
  668. struct OpcodeHandler
  669. {
  670. explicit OpcodeHandler(Compiler &compiler_) : compiler(compiler_) {}
  671. virtual ~OpcodeHandler() = default;
  672. // Return true if traversal should continue.
  673. // If false, traversal will end immediately.
  674. virtual bool handle(Op opcode, const uint32_t *args, uint32_t length) = 0;
  675. virtual bool handle_terminator(const SPIRBlock &)
  676. {
  677. return true;
  678. }
  679. virtual bool follow_function_call(const SPIRFunction &)
  680. {
  681. return true;
  682. }
  683. virtual void set_current_block(const SPIRBlock &)
  684. {
  685. }
  686. // Called after returning from a function or when entering a block,
  687. // can be called multiple times per block,
  688. // while set_current_block is only called on block entry.
  689. virtual void rearm_current_block(const SPIRBlock &)
  690. {
  691. }
  692. virtual bool begin_function_scope(const uint32_t *, uint32_t)
  693. {
  694. return true;
  695. }
  696. virtual bool end_function_scope(const uint32_t *, uint32_t)
  697. {
  698. return true;
  699. }
  700. Compiler &compiler;
  701. std::unordered_map<uint32_t, uint32_t> result_types;
  702. const SPIRType *get_expression_result_type(uint32_t id) const;
  703. bool enable_result_types = false;
  704. template <typename T> T &get(uint32_t id)
  705. {
  706. return compiler.get<T>(id);
  707. }
  708. template <typename T> const T &get(uint32_t id) const
  709. {
  710. return compiler.get<T>(id);
  711. }
  712. template <typename T, typename... P>
  713. T &set(uint32_t id, P &&... args)
  714. {
  715. return compiler.set<T>(id, std::forward<P>(args)...);
  716. }
  717. };
  718. struct BufferAccessHandler : OpcodeHandler
  719. {
  720. BufferAccessHandler(const Compiler &compiler_, SmallVector<BufferRange> &ranges_, uint32_t id_)
  721. : OpcodeHandler(const_cast<Compiler &>(compiler_))
  722. , ranges(ranges_)
  723. , id(id_)
  724. {
  725. }
  726. bool handle(Op opcode, const uint32_t *args, uint32_t length) override;
  727. SmallVector<BufferRange> &ranges;
  728. uint32_t id;
  729. std::unordered_set<uint32_t> seen;
  730. };
  731. struct InterfaceVariableAccessHandler : OpcodeHandler
  732. {
  733. InterfaceVariableAccessHandler(const Compiler &compiler_, std::unordered_set<VariableID> &variables_)
  734. : OpcodeHandler(const_cast<Compiler &>(compiler_))
  735. , variables(variables_)
  736. {
  737. }
  738. bool handle(Op opcode, const uint32_t *args, uint32_t length) override;
  739. std::unordered_set<VariableID> &variables;
  740. };
  741. struct CombinedImageSamplerHandler : OpcodeHandler
  742. {
  743. explicit CombinedImageSamplerHandler(Compiler &compiler_)
  744. : OpcodeHandler(compiler_)
  745. {
  746. }
  747. bool handle(Op opcode, const uint32_t *args, uint32_t length) override;
  748. bool begin_function_scope(const uint32_t *args, uint32_t length) override;
  749. bool end_function_scope(const uint32_t *args, uint32_t length) override;
  750. // Each function in the call stack needs its own remapping for parameters so we can deduce which global variable each texture/sampler the parameter is statically bound to.
  751. std::stack<std::unordered_map<uint32_t, uint32_t>> parameter_remapping;
  752. std::stack<SPIRFunction *> functions;
  753. uint32_t remap_parameter(uint32_t id);
  754. void push_remap_parameters(const SPIRFunction &func, const uint32_t *args, uint32_t length);
  755. void pop_remap_parameters();
  756. void register_combined_image_sampler(SPIRFunction &caller, VariableID combined_id, VariableID texture_id,
  757. VariableID sampler_id, bool depth);
  758. };
  759. struct DummySamplerForCombinedImageHandler : OpcodeHandler
  760. {
  761. explicit DummySamplerForCombinedImageHandler(Compiler &compiler_)
  762. : OpcodeHandler(compiler_)
  763. {
  764. }
  765. bool handle(Op opcode, const uint32_t *args, uint32_t length) override;
  766. bool need_dummy_sampler = false;
  767. };
  768. struct ActiveBuiltinHandler : OpcodeHandler
  769. {
  770. explicit ActiveBuiltinHandler(Compiler &compiler_)
  771. : OpcodeHandler(compiler_)
  772. {
  773. }
  774. bool handle(Op opcode, const uint32_t *args, uint32_t length) override;
  775. void handle_builtin(const SPIRType &type, BuiltIn builtin, const Bitset &decoration_flags);
  776. void add_if_builtin(uint32_t id);
  777. void add_if_builtin_or_block(uint32_t id);
  778. void add_if_builtin(uint32_t id, bool allow_blocks);
  779. };
  780. bool traverse_all_reachable_opcodes(const SPIRBlock &block, OpcodeHandler &handler) const;
  781. bool traverse_all_reachable_opcodes(const SPIRFunction &block, OpcodeHandler &handler) const;
  782. // This must be an ordered data structure so we always pick the same type aliases.
  783. SmallVector<uint32_t> global_struct_cache;
  784. ShaderResources get_shader_resources(const std::unordered_set<VariableID> *active_variables) const;
  785. VariableTypeRemapCallback variable_remap_callback;
  786. bool get_common_basic_type(const SPIRType &type, SPIRType::BaseType &base_type);
  787. std::unordered_set<uint32_t> forced_temporaries;
  788. std::unordered_set<uint32_t> forwarded_temporaries;
  789. std::unordered_set<uint32_t> suppressed_usage_tracking;
  790. std::unordered_set<uint32_t> hoisted_temporaries;
  791. std::unordered_set<uint32_t> forced_invariant_temporaries;
  792. Bitset active_input_builtins;
  793. Bitset active_output_builtins;
  794. uint32_t clip_distance_count = 0;
  795. uint32_t cull_distance_count = 0;
  796. bool position_invariant = false;
  797. void analyze_parameter_preservation(
  798. SPIRFunction &entry, const CFG &cfg,
  799. const std::unordered_map<uint32_t, std::unordered_set<uint32_t>> &variable_to_blocks,
  800. const std::unordered_map<uint32_t, std::unordered_set<uint32_t>> &complete_write_blocks);
  801. // If a variable ID or parameter ID is found in this set, a sampler is actually a shadow/comparison sampler.
  802. // SPIR-V does not support this distinction, so we must keep track of this information outside the type system.
  803. // There might be unrelated IDs found in this set which do not correspond to actual variables.
  804. // This set should only be queried for the existence of samplers which are already known to be variables or parameter IDs.
  805. // Similar is implemented for images, as well as if subpass inputs are needed.
  806. std::unordered_set<uint32_t> comparison_ids;
  807. bool need_subpass_input = false;
  808. bool need_subpass_input_ms = false;
  809. // In certain backends, we will need to use a dummy sampler to be able to emit code.
  810. // GLSL does not support texelFetch on texture2D objects, but SPIR-V does,
  811. // so we need to workaround by having the application inject a dummy sampler.
  812. uint32_t dummy_sampler_id = 0;
  813. void analyze_image_and_sampler_usage();
  814. struct CombinedImageSamplerDrefHandler : OpcodeHandler
  815. {
  816. explicit CombinedImageSamplerDrefHandler(Compiler &compiler_)
  817. : OpcodeHandler(compiler_)
  818. {
  819. }
  820. bool handle(Op opcode, const uint32_t *args, uint32_t length) override;
  821. std::unordered_set<uint32_t> dref_combined_samplers;
  822. };
  823. struct CombinedImageSamplerUsageHandler : OpcodeHandler
  824. {
  825. CombinedImageSamplerUsageHandler(Compiler &compiler_,
  826. const std::unordered_set<uint32_t> &dref_combined_samplers_)
  827. : OpcodeHandler(compiler_)
  828. , dref_combined_samplers(dref_combined_samplers_)
  829. {
  830. }
  831. bool begin_function_scope(const uint32_t *args, uint32_t length) override;
  832. bool handle(Op opcode, const uint32_t *args, uint32_t length) override;
  833. const std::unordered_set<uint32_t> &dref_combined_samplers;
  834. std::unordered_map<uint32_t, std::unordered_set<uint32_t>> dependency_hierarchy;
  835. std::unordered_set<uint32_t> comparison_ids;
  836. void add_hierarchy_to_comparison_ids(uint32_t ids);
  837. bool need_subpass_input = false;
  838. bool need_subpass_input_ms = false;
  839. void add_dependency(uint32_t dst, uint32_t src);
  840. };
  841. void build_function_control_flow_graphs_and_analyze();
  842. std::unordered_map<uint32_t, std::unique_ptr<CFG>> function_cfgs;
  843. const CFG &get_cfg_for_current_function() const;
  844. const CFG &get_cfg_for_function(uint32_t id) const;
  845. struct CFGBuilder : OpcodeHandler
  846. {
  847. explicit CFGBuilder(Compiler &compiler_);
  848. bool follow_function_call(const SPIRFunction &func) override;
  849. bool handle(Op op, const uint32_t *args, uint32_t length) override;
  850. std::unordered_map<uint32_t, std::unique_ptr<CFG>> function_cfgs;
  851. };
  852. struct AnalyzeVariableScopeAccessHandler : OpcodeHandler
  853. {
  854. AnalyzeVariableScopeAccessHandler(Compiler &compiler_, SPIRFunction &entry_);
  855. bool follow_function_call(const SPIRFunction &) override;
  856. void set_current_block(const SPIRBlock &block) override;
  857. void notify_variable_access(uint32_t id, uint32_t block);
  858. bool id_is_phi_variable(uint32_t id) const;
  859. bool id_is_potential_temporary(uint32_t id) const;
  860. bool handle(Op op, const uint32_t *args, uint32_t length) override;
  861. bool handle_terminator(const SPIRBlock &block) override;
  862. SPIRFunction &entry;
  863. std::unordered_map<uint32_t, std::unordered_set<uint32_t>> accessed_variables_to_block;
  864. std::unordered_map<uint32_t, std::unordered_set<uint32_t>> accessed_temporaries_to_block;
  865. std::unordered_map<uint32_t, uint32_t> result_id_to_type;
  866. std::unordered_map<uint32_t, std::unordered_set<uint32_t>> complete_write_variables_to_block;
  867. std::unordered_map<uint32_t, std::unordered_set<uint32_t>> partial_write_variables_to_block;
  868. std::unordered_set<uint32_t> access_chain_expressions;
  869. // Access chains used in multiple blocks mean hoisting all the variables used to construct the access chain as not all backends can use pointers.
  870. // This is also relevant when forwarding opaque objects since we cannot lower these to temporaries.
  871. std::unordered_map<uint32_t, std::unordered_set<uint32_t>> rvalue_forward_children;
  872. const SPIRBlock *current_block = nullptr;
  873. };
  874. struct StaticExpressionAccessHandler : OpcodeHandler
  875. {
  876. StaticExpressionAccessHandler(Compiler &compiler_, uint32_t variable_id_);
  877. bool follow_function_call(const SPIRFunction &) override;
  878. bool handle(Op op, const uint32_t *args, uint32_t length) override;
  879. uint32_t variable_id;
  880. uint32_t static_expression = 0;
  881. uint32_t write_count = 0;
  882. };
  883. struct PhysicalBlockMeta
  884. {
  885. uint32_t alignment = 0;
  886. };
  887. struct PhysicalStorageBufferPointerHandler : OpcodeHandler
  888. {
  889. explicit PhysicalStorageBufferPointerHandler(Compiler &compiler_);
  890. bool handle(Op op, const uint32_t *args, uint32_t length) override;
  891. std::unordered_set<uint32_t> non_block_types;
  892. std::unordered_map<uint32_t, PhysicalBlockMeta> physical_block_type_meta;
  893. std::unordered_map<uint32_t, PhysicalBlockMeta *> access_chain_to_physical_block;
  894. std::unordered_set<uint32_t> analyzed_type_ids;
  895. void mark_aligned_access(uint32_t id, const uint32_t *args, uint32_t length);
  896. PhysicalBlockMeta *find_block_meta(uint32_t id) const;
  897. bool type_is_bda_block_entry(uint32_t type_id) const;
  898. void setup_meta_chain(uint32_t type_id, uint32_t var_id);
  899. uint32_t get_minimum_scalar_alignment(const SPIRType &type) const;
  900. void analyze_non_block_types_from_block(const SPIRType &type);
  901. uint32_t get_base_non_block_type_id(uint32_t type_id) const;
  902. };
  903. void analyze_non_block_pointer_types();
  904. SmallVector<uint32_t> physical_storage_non_block_pointer_types;
  905. std::unordered_map<uint32_t, PhysicalBlockMeta> physical_storage_type_to_alignment;
  906. void analyze_variable_scope(SPIRFunction &function, AnalyzeVariableScopeAccessHandler &handler);
  907. void find_function_local_luts(SPIRFunction &function, const AnalyzeVariableScopeAccessHandler &handler,
  908. bool single_function);
  909. bool may_read_undefined_variable_in_block(const SPIRBlock &block, uint32_t var);
  910. struct GeometryEmitDisocveryHandler : OpcodeHandler
  911. {
  912. explicit GeometryEmitDisocveryHandler(Compiler &compiler_)
  913. : OpcodeHandler(compiler_)
  914. {
  915. }
  916. bool handle(Op opcode, const uint32_t *args, uint32_t length) override;
  917. bool begin_function_scope(const uint32_t *, uint32_t) override;
  918. bool end_function_scope(const uint32_t *, uint32_t) override;
  919. SmallVector<SPIRFunction *> function_stack;
  920. };
  921. void discover_geometry_emitters();
  922. // Finds all resources that are written to from inside the critical section, if present.
  923. // The critical section is delimited by OpBeginInvocationInterlockEXT and
  924. // OpEndInvocationInterlockEXT instructions. In MSL and HLSL, any resources written
  925. // while inside the critical section must be placed in a raster order group.
  926. struct InterlockedResourceAccessHandler : OpcodeHandler
  927. {
  928. InterlockedResourceAccessHandler(Compiler &compiler_, uint32_t entry_point_id)
  929. : OpcodeHandler(compiler_)
  930. {
  931. call_stack.push_back(entry_point_id);
  932. }
  933. bool handle(Op op, const uint32_t *args, uint32_t length) override;
  934. bool begin_function_scope(const uint32_t *args, uint32_t length) override;
  935. bool end_function_scope(const uint32_t *args, uint32_t length) override;
  936. bool in_crit_sec = false;
  937. uint32_t interlock_function_id = 0;
  938. bool split_function_case = false;
  939. bool control_flow_interlock = false;
  940. bool use_critical_section = false;
  941. bool call_stack_is_interlocked = false;
  942. SmallVector<uint32_t> call_stack;
  943. void access_potential_resource(uint32_t id);
  944. };
  945. struct InterlockedResourceAccessPrepassHandler : OpcodeHandler
  946. {
  947. InterlockedResourceAccessPrepassHandler(Compiler &compiler_, uint32_t entry_point_id)
  948. : OpcodeHandler(compiler_)
  949. {
  950. call_stack.push_back(entry_point_id);
  951. }
  952. void rearm_current_block(const SPIRBlock &block) override;
  953. bool handle(Op op, const uint32_t *args, uint32_t length) override;
  954. bool begin_function_scope(const uint32_t *args, uint32_t length) override;
  955. bool end_function_scope(const uint32_t *args, uint32_t length) override;
  956. uint32_t interlock_function_id = 0;
  957. uint32_t current_block_id = 0;
  958. bool split_function_case = false;
  959. bool control_flow_interlock = false;
  960. SmallVector<uint32_t> call_stack;
  961. };
  962. void analyze_interlocked_resource_usage();
  963. // The set of all resources written while inside the critical section, if present.
  964. std::unordered_set<uint32_t> interlocked_resources;
  965. bool interlocked_is_complex = false;
  966. void make_constant_null(uint32_t id, uint32_t type);
  967. std::unordered_map<uint32_t, std::string> declared_block_names;
  968. static bool instruction_to_result_type(
  969. uint32_t &result_type, uint32_t &result_id, Op op, const uint32_t *args, uint32_t length);
  970. Bitset combined_decoration_for_member(const SPIRType &type, uint32_t index) const;
  971. static bool is_desktop_only_format(ImageFormat format);
  972. bool is_depth_image(const SPIRType &type, uint32_t id) const;
  973. void set_extended_decoration(uint32_t id, ExtendedDecorations decoration, uint32_t value = 0);
  974. uint32_t get_extended_decoration(uint32_t id, ExtendedDecorations decoration) const;
  975. bool has_extended_decoration(uint32_t id, ExtendedDecorations decoration) const;
  976. void unset_extended_decoration(uint32_t id, ExtendedDecorations decoration);
  977. void set_extended_member_decoration(uint32_t type, uint32_t index, ExtendedDecorations decoration,
  978. uint32_t value = 0);
  979. uint32_t get_extended_member_decoration(uint32_t type, uint32_t index, ExtendedDecorations decoration) const;
  980. bool has_extended_member_decoration(uint32_t type, uint32_t index, ExtendedDecorations decoration) const;
  981. void unset_extended_member_decoration(uint32_t type, uint32_t index, ExtendedDecorations decoration);
  982. bool check_internal_recursion(const SPIRType &type, std::unordered_set<uint32_t> &checked_ids);
  983. bool type_contains_recursion(const SPIRType &type);
  984. bool type_is_array_of_pointers(const SPIRType &type) const;
  985. bool type_is_block_like(const SPIRType &type) const;
  986. bool type_is_explicit_layout(const SPIRType &type) const;
  987. bool type_is_top_level_block(const SPIRType &type) const;
  988. bool type_is_opaque_value(const SPIRType &type) const;
  989. bool reflection_ssbo_instance_name_is_significant() const;
  990. std::string get_remapped_declared_block_name(uint32_t id, bool fallback_prefer_instance_name) const;
  991. bool flush_phi_required(BlockID from, BlockID to) const;
  992. uint32_t evaluate_spec_constant_u32(const SPIRConstantOp &spec) const;
  993. uint32_t evaluate_constant_u32(uint32_t id) const;
  994. bool is_vertex_like_shader() const;
  995. // Get the correct case list for the OpSwitch, since it can be either a
  996. // 32 bit wide condition or a 64 bit, but the type is not embedded in the
  997. // instruction itself.
  998. const SmallVector<SPIRBlock::Case> &get_case_list(const SPIRBlock &block) const;
  999. private:
  1000. // Used only to implement the old deprecated get_entry_point() interface.
  1001. const SPIREntryPoint &get_first_entry_point(const std::string &name) const;
  1002. SPIREntryPoint &get_first_entry_point(const std::string &name);
  1003. };
  1004. } // namespace SPIRV_CROSS_NAMESPACE
  1005. #ifdef SPIRV_CROSS_SPV_HEADER_NAMESPACE_OVERRIDE
  1006. #undef spv
  1007. #endif
  1008. #endif