// Copyright (c) 2016 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef SOURCE_OPT_MODULE_H_ #define SOURCE_OPT_MODULE_H_ #include #include #include #include #include #include #include "source/opt/function.h" #include "source/opt/instruction.h" #include "source/opt/iterator.h" namespace spvtools { namespace opt { class IRContext; // A struct for containing the module header information. struct ModuleHeader { uint32_t magic_number; uint32_t version; uint32_t generator; uint32_t bound; uint32_t schema; }; // A SPIR-V module. It contains all the information for a SPIR-V module and // serves as the backbone of optimization transformations. class Module { public: using iterator = UptrVectorIterator; using const_iterator = UptrVectorIterator; using inst_iterator = InstructionList::iterator; using const_inst_iterator = InstructionList::const_iterator; // Creates an empty module with zero'd header. Module() : header_({}), contains_debug_info_(false) {} // Sets the header to the given |header|. void SetHeader(const ModuleHeader& header) { header_ = header; } // Sets the Id bound. The Id bound cannot be set to 0. void SetIdBound(uint32_t bound) { assert(bound != 0); header_.bound = bound; } // Returns the Id bound. uint32_t IdBound() const { return header_.bound; } // Returns the current Id bound and increases it to the next available value. // If the id bound has already reached its maximum value, then 0 is returned. // The maximum value for the id bound is obtained from the context. If there // is none, then the minimum that limit can be according to the spir-v // specification. // TODO(1841): Update the uses to check for a 0 return value. uint32_t TakeNextIdBound(); // Appends a capability instruction to this module. inline void AddCapability(std::unique_ptr c); // Appends an extension instruction to this module. inline void AddExtension(std::unique_ptr e); // Appends an extended instruction set instruction to this module. inline void AddExtInstImport(std::unique_ptr e); // Set the memory model for this module. inline void SetMemoryModel(std::unique_ptr m); // Set the sampled image addressing mode for this module. inline void SetSampledImageAddressMode(std::unique_ptr m); // Appends an entry point instruction to this module. inline void AddEntryPoint(std::unique_ptr e); // Appends an execution mode instruction to this module. inline void AddExecutionMode(std::unique_ptr e); // Appends a debug 1 instruction (excluding OpLine & OpNoLine) to this module. // "debug 1" instructions are the ones in layout section 7.a), see section // 2.4 Logical Layout of a Module from the SPIR-V specification. inline void AddDebug1Inst(std::unique_ptr d); // Appends a debug 2 instruction (excluding OpLine & OpNoLine) to this module. // "debug 2" instructions are the ones in layout section 7.b), see section // 2.4 Logical Layout of a Module from the SPIR-V specification. inline void AddDebug2Inst(std::unique_ptr d); // Appends a debug 3 instruction (OpModuleProcessed) to this module. // This is due to decision by the SPIR Working Group, pending publication. inline void AddDebug3Inst(std::unique_ptr d); // Appends a debug info extension (OpenCL.DebugInfo.100, // NonSemantic.Shader.DebugInfo.100, or DebugInfo) instruction to this module. inline void AddExtInstDebugInfo(std::unique_ptr d); // Appends an annotation instruction to this module. inline void AddAnnotationInst(std::unique_ptr a); // Appends a type-declaration instruction to this module. inline void AddType(std::unique_ptr t); // Appends a constant, global variable, or OpUndef instruction to this module. inline void AddGlobalValue(std::unique_ptr v); // Prepends a function declaration to this module. inline void AddFunctionDeclaration(std::unique_ptr f); // Appends a function to this module. inline void AddFunction(std::unique_ptr f); // Sets |contains_debug_info_| as true. inline void SetContainsDebugInfo(); inline bool ContainsDebugInfo() { return contains_debug_info_; } // Returns a vector of pointers to type-declaration instructions in this // module. std::vector GetTypes(); std::vector GetTypes() const; // Returns a vector of pointers to constant-creation instructions in this // module. std::vector GetConstants(); std::vector GetConstants() const; // Return result id of global value with |opcode|, 0 if not present. uint32_t GetGlobalValue(spv::Op opcode) const; // Add global value with |opcode|, |result_id| and |type_id| void AddGlobalValue(spv::Op opcode, uint32_t result_id, uint32_t type_id); inline uint32_t id_bound() const { return header_.bound; } inline uint32_t version() const { return header_.version; } inline uint32_t generator() const { return header_.generator; } inline uint32_t schema() const { return header_.schema; } inline void set_version(uint32_t v) { header_.version = v; } // Iterators for capabilities instructions contained in this module. inline inst_iterator capability_begin(); inline inst_iterator capability_end(); inline IteratorRange capabilities(); inline IteratorRange capabilities() const; // Iterators for ext_inst_imports instructions contained in this module. inline inst_iterator ext_inst_import_begin(); inline inst_iterator ext_inst_import_end(); inline IteratorRange ext_inst_imports(); inline IteratorRange ext_inst_imports() const; // Return the memory model instruction contained in this module. inline Instruction* GetMemoryModel() { return memory_model_.get(); } inline const Instruction* GetMemoryModel() const { return memory_model_.get(); } // Return the sampled image address mode instruction contained in this module. inline Instruction* GetSampledImageAddressMode() { return sampled_image_address_mode_.get(); } inline const Instruction* GetSampledImageAddressMode() const { return sampled_image_address_mode_.get(); } // There are several kinds of debug instructions, according to where they can // appear in the logical layout of a module: // - Section 7a: OpString, OpSourceExtension, OpSource, OpSourceContinued // - Section 7b: OpName, OpMemberName // - Section 7c: OpModuleProcessed // - Mostly anywhere: OpLine and OpNoLine // // Iterators for debug 1 instructions (excluding OpLine & OpNoLine) contained // in this module. These are for layout section 7a. inline inst_iterator debug1_begin(); inline inst_iterator debug1_end(); inline IteratorRange debugs1(); inline IteratorRange debugs1() const; // Iterators for debug 2 instructions (excluding OpLine & OpNoLine) contained // in this module. These are for layout section 7b. inline inst_iterator debug2_begin(); inline inst_iterator debug2_end(); inline IteratorRange debugs2(); inline IteratorRange debugs2() const; // Iterators for debug 3 instructions (excluding OpLine & OpNoLine) contained // in this module. These are for layout section 7c. inline inst_iterator debug3_begin(); inline inst_iterator debug3_end(); inline IteratorRange debugs3(); inline IteratorRange debugs3() const; // Iterators for debug info instructions (excluding OpLine & OpNoLine) // contained in this module. These are OpExtInst for DebugInfo extension // placed between section 9 and 10. inline inst_iterator ext_inst_debuginfo_begin(); inline inst_iterator ext_inst_debuginfo_end(); inline IteratorRange ext_inst_debuginfo(); inline IteratorRange ext_inst_debuginfo() const; // Iterators for entry point instructions contained in this module inline IteratorRange entry_points(); inline IteratorRange entry_points() const; // Iterators for execution_modes instructions contained in this module. inline inst_iterator execution_mode_begin(); inline inst_iterator execution_mode_end(); inline IteratorRange execution_modes(); inline IteratorRange execution_modes() const; // Iterators for annotation instructions contained in this module. inline inst_iterator annotation_begin(); inline inst_iterator annotation_end(); IteratorRange annotations(); IteratorRange annotations() const; // Iterators for extension instructions contained in this module. inline inst_iterator extension_begin(); inline inst_iterator extension_end(); IteratorRange extensions(); IteratorRange extensions() const; // Iterators for types, constants and global variables instructions. inline inst_iterator types_values_begin(); inline inst_iterator types_values_end(); inline IteratorRange types_values(); inline IteratorRange types_values() const; // Iterators for functions contained in this module. iterator begin() { return iterator(&functions_, functions_.begin()); } iterator end() { return iterator(&functions_, functions_.end()); } const_iterator begin() const { return cbegin(); } const_iterator end() const { return cend(); } inline const_iterator cbegin() const; inline const_iterator cend() const; // Invokes function |f| on all instructions in this module, and optionally on // the debug line instructions that precede them. void ForEachInst(const std::function& f, bool run_on_debug_line_insts = false); void ForEachInst(const std::function& f, bool run_on_debug_line_insts = false) const; // Pushes the binary segments for this instruction into the back of *|binary|. // If |skip_nop| is true and this is a OpNop, do nothing. void ToBinary(std::vector* binary, bool skip_nop) const; // Returns 1 more than the maximum Id value mentioned in the module. uint32_t ComputeIdBound() const; // Returns true if module has capability |cap| bool HasExplicitCapability(uint32_t cap); // Returns id for OpExtInst instruction for extension |extstr|. // Returns 0 if not found. uint32_t GetExtInstImportId(const char* extstr); // Sets the associated context for this module void SetContext(IRContext* c) { context_ = c; } // Gets the associated context for this module IRContext* context() const { return context_; } // Sets the trailing debug line info to |dbg_line_info|. void SetTrailingDbgLineInfo(std::vector&& dbg_line_info) { trailing_dbg_line_info_ = std::move(dbg_line_info); } std::vector& trailing_dbg_line_info() { return trailing_dbg_line_info_; } const std::vector& trailing_dbg_line_info() const { return trailing_dbg_line_info_; } private: ModuleHeader header_; // Module header // The following fields respect the "Logical Layout of a Module" in // Section 2.4 of the SPIR-V specification. IRContext* context_; InstructionList capabilities_; InstructionList extensions_; InstructionList ext_inst_imports_; // A module only has one memory model instruction. std::unique_ptr memory_model_; // A module can only have one optional sampled image addressing mode std::unique_ptr sampled_image_address_mode_; InstructionList entry_points_; InstructionList execution_modes_; InstructionList debugs1_; InstructionList debugs2_; InstructionList debugs3_; InstructionList ext_inst_debuginfo_; InstructionList annotations_; // Type declarations, constants, and global variable declarations. InstructionList types_values_; std::vector> functions_; // If the module ends with Op*Line instruction, they will not be attached to // any instruction. We record them here, so they will not be lost. std::vector trailing_dbg_line_info_; // This module contains DebugScope/DebugNoScope or OpLine/OpNoLine. bool contains_debug_info_; }; // Pretty-prints |module| to |str|. Returns |str|. std::ostream& operator<<(std::ostream& str, const Module& module); inline void Module::AddCapability(std::unique_ptr c) { capabilities_.push_back(std::move(c)); } inline void Module::AddExtension(std::unique_ptr e) { extensions_.push_back(std::move(e)); } inline void Module::AddExtInstImport(std::unique_ptr e) { ext_inst_imports_.push_back(std::move(e)); } inline void Module::SetMemoryModel(std::unique_ptr m) { memory_model_ = std::move(m); } inline void Module::SetSampledImageAddressMode(std::unique_ptr m) { sampled_image_address_mode_ = std::move(m); } inline void Module::AddEntryPoint(std::unique_ptr e) { entry_points_.push_back(std::move(e)); } inline void Module::AddExecutionMode(std::unique_ptr e) { execution_modes_.push_back(std::move(e)); } inline void Module::AddDebug1Inst(std::unique_ptr d) { debugs1_.push_back(std::move(d)); } inline void Module::AddDebug2Inst(std::unique_ptr d) { debugs2_.push_back(std::move(d)); } inline void Module::AddDebug3Inst(std::unique_ptr d) { debugs3_.push_back(std::move(d)); } inline void Module::AddExtInstDebugInfo(std::unique_ptr d) { ext_inst_debuginfo_.push_back(std::move(d)); } inline void Module::AddAnnotationInst(std::unique_ptr a) { annotations_.push_back(std::move(a)); } inline void Module::AddType(std::unique_ptr t) { types_values_.push_back(std::move(t)); } inline void Module::AddGlobalValue(std::unique_ptr v) { types_values_.push_back(std::move(v)); } inline void Module::AddFunctionDeclaration(std::unique_ptr f) { // function declarations must come before function definitions. functions_.emplace(functions_.begin(), std::move(f)); } inline void Module::AddFunction(std::unique_ptr f) { functions_.emplace_back(std::move(f)); } inline void Module::SetContainsDebugInfo() { contains_debug_info_ = true; } inline Module::inst_iterator Module::capability_begin() { return capabilities_.begin(); } inline Module::inst_iterator Module::capability_end() { return capabilities_.end(); } inline IteratorRange Module::capabilities() { return make_range(capabilities_.begin(), capabilities_.end()); } inline IteratorRange Module::capabilities() const { return make_range(capabilities_.begin(), capabilities_.end()); } inline Module::inst_iterator Module::ext_inst_import_begin() { return ext_inst_imports_.begin(); } inline Module::inst_iterator Module::ext_inst_import_end() { return ext_inst_imports_.end(); } inline IteratorRange Module::ext_inst_imports() { return make_range(ext_inst_imports_.begin(), ext_inst_imports_.end()); } inline IteratorRange Module::ext_inst_imports() const { return make_range(ext_inst_imports_.begin(), ext_inst_imports_.end()); } inline Module::inst_iterator Module::debug1_begin() { return debugs1_.begin(); } inline Module::inst_iterator Module::debug1_end() { return debugs1_.end(); } inline IteratorRange Module::debugs1() { return make_range(debugs1_.begin(), debugs1_.end()); } inline IteratorRange Module::debugs1() const { return make_range(debugs1_.begin(), debugs1_.end()); } inline Module::inst_iterator Module::debug2_begin() { return debugs2_.begin(); } inline Module::inst_iterator Module::debug2_end() { return debugs2_.end(); } inline IteratorRange Module::debugs2() { return make_range(debugs2_.begin(), debugs2_.end()); } inline IteratorRange Module::debugs2() const { return make_range(debugs2_.begin(), debugs2_.end()); } inline Module::inst_iterator Module::debug3_begin() { return debugs3_.begin(); } inline Module::inst_iterator Module::debug3_end() { return debugs3_.end(); } inline IteratorRange Module::debugs3() { return make_range(debugs3_.begin(), debugs3_.end()); } inline IteratorRange Module::debugs3() const { return make_range(debugs3_.begin(), debugs3_.end()); } inline Module::inst_iterator Module::ext_inst_debuginfo_begin() { return ext_inst_debuginfo_.begin(); } inline Module::inst_iterator Module::ext_inst_debuginfo_end() { return ext_inst_debuginfo_.end(); } inline IteratorRange Module::ext_inst_debuginfo() { return make_range(ext_inst_debuginfo_.begin(), ext_inst_debuginfo_.end()); } inline IteratorRange Module::ext_inst_debuginfo() const { return make_range(ext_inst_debuginfo_.begin(), ext_inst_debuginfo_.end()); } inline IteratorRange Module::entry_points() { return make_range(entry_points_.begin(), entry_points_.end()); } inline IteratorRange Module::entry_points() const { return make_range(entry_points_.begin(), entry_points_.end()); } inline Module::inst_iterator Module::execution_mode_begin() { return execution_modes_.begin(); } inline Module::inst_iterator Module::execution_mode_end() { return execution_modes_.end(); } inline IteratorRange Module::execution_modes() { return make_range(execution_modes_.begin(), execution_modes_.end()); } inline IteratorRange Module::execution_modes() const { return make_range(execution_modes_.begin(), execution_modes_.end()); } inline Module::inst_iterator Module::annotation_begin() { return annotations_.begin(); } inline Module::inst_iterator Module::annotation_end() { return annotations_.end(); } inline IteratorRange Module::annotations() { return make_range(annotations_.begin(), annotations_.end()); } inline IteratorRange Module::annotations() const { return make_range(annotations_.begin(), annotations_.end()); } inline Module::inst_iterator Module::extension_begin() { return extensions_.begin(); } inline Module::inst_iterator Module::extension_end() { return extensions_.end(); } inline IteratorRange Module::extensions() { return make_range(extensions_.begin(), extensions_.end()); } inline IteratorRange Module::extensions() const { return make_range(extensions_.begin(), extensions_.end()); } inline Module::inst_iterator Module::types_values_begin() { return types_values_.begin(); } inline Module::inst_iterator Module::types_values_end() { return types_values_.end(); } inline IteratorRange Module::types_values() { return make_range(types_values_.begin(), types_values_.end()); } inline IteratorRange Module::types_values() const { return make_range(types_values_.begin(), types_values_.end()); } inline Module::const_iterator Module::cbegin() const { return const_iterator(&functions_, functions_.cbegin()); } inline Module::const_iterator Module::cend() const { return const_iterator(&functions_, functions_.cend()); } } // namespace opt } // namespace spvtools #endif // SOURCE_OPT_MODULE_H_