├── .github └── workflows │ └── CI.yml ├── .gitignore ├── CMakeLists.txt ├── DOCUMENTATION.md ├── LICENSE ├── LINKER.md ├── README.md ├── common ├── include │ └── common │ │ ├── Algorithms.h │ │ ├── BinaryFileReader.h │ │ ├── BinaryFileWriter.h │ │ ├── BinaryVectorWriter.h │ │ ├── Callable.h │ │ ├── ConsoleLogger.h │ │ ├── ControlFlowGraph.h │ │ ├── ExprGraph.h │ │ ├── Expression.h │ │ ├── ExternalMemoryAllocator.h │ │ ├── FunctionCallGraph.h │ │ ├── GrammarHelper.h │ │ ├── Graph.h │ │ ├── HeapAllocator.h │ │ ├── HeapCallable.h │ │ ├── HeapHashMap.h │ │ ├── HeapList.h │ │ ├── HeapString.h │ │ ├── HeapVector.h │ │ ├── LinkerHelper.h │ │ ├── ModulePrinter.h │ │ ├── Node.h │ │ ├── ReflectionHelper.h │ │ ├── ReflectionHelperTemplate.inl │ │ ├── TypeHelper.h │ │ └── VulkanInterop.h └── source │ ├── BinaryFileReader.cpp │ ├── BinaryFileWriter.cpp │ ├── ConsoleLogger.cpp │ ├── ExternalMemoryAllocator.cpp │ ├── FunctionCallGraph.cpp │ ├── GrammarHelper.cpp │ ├── HeapAllocator.cpp │ ├── LinkerHelper.cpp │ ├── ModulePrinter.cpp │ ├── ReflectionHelper.cpp │ ├── TypeHelper.cpp │ └── VulkanInterop.cpp ├── dis ├── include │ └── dis │ │ └── .gitkeep ├── source │ └── dis.cpp └── test │ ├── constants.spv │ ├── controlFlow.spv │ ├── expressionGraph.spv │ ├── extensions.spv │ ├── functionCall.spv │ ├── oldInstrTest.spv │ └── test.bat ├── lib ├── include │ └── spvgentwo │ │ ├── Allocator.h │ │ ├── BasicBlock.h │ │ ├── BasicBlockTemplate.inl │ │ ├── Constant.h │ │ ├── Entry.h │ │ ├── EntryIterator.h │ │ ├── EntryPoint.h │ │ ├── EntryPointTemplate.inl │ │ ├── FNV1aHasher.h │ │ ├── Flag.h │ │ ├── Function.h │ │ ├── FunctionTemplate.inl │ │ ├── GLSL450Instruction.h │ │ ├── Glsl.h │ │ ├── Grammar.h │ │ ├── HashMap.h │ │ ├── HashMapIterator.h │ │ ├── Instruction.h │ │ ├── InstructionTemplate.inl │ │ ├── List.h │ │ ├── Logger.h │ │ ├── Module.h │ │ ├── ModuleTemplate.inl │ │ ├── OpenCl.h │ │ ├── Operand.h │ │ ├── Operators.h │ │ ├── Reader.h │ │ ├── Spv.h │ │ ├── SpvDefines.h │ │ ├── SpvGenTwo.h │ │ ├── String.h │ │ ├── Templates.h │ │ ├── Type.h │ │ ├── TypeAlias.h │ │ ├── TypeInferenceAndValiation.h │ │ ├── Vector.h │ │ ├── Writer.h │ │ └── stdreplacement.h └── source │ ├── Allocator.cpp │ ├── BasicBlock.cpp │ ├── Constant.cpp │ ├── EntryPoint.cpp │ ├── Function.cpp │ ├── GLSL450Instruction.cpp │ ├── Grammar.cpp │ ├── Instruction.cpp │ ├── Module.cpp │ ├── Operand.cpp │ ├── Operators.cpp │ ├── String.cpp │ ├── Type.cpp │ └── TypeInferenceAndValiation.cpp ├── link ├── include │ └── link │ │ └── .gitkeep └── source │ └── link.cpp ├── misc ├── containers.natvis ├── dis.PNG ├── linker.PNG ├── refl.PNG └── spvgentwo.cppcheck ├── refl ├── include │ └── refl │ │ └── .gitkeep └── source │ └── refl.cpp └── test ├── catch2.runsettings ├── include └── test │ ├── .gitkeep │ ├── Modules.h │ ├── SpvValidator.h │ └── TestLogger.h └── source ├── AllocatorTests.cpp ├── BitInstructions.cpp ├── ComputeShader.cpp ├── ConstantFundamentals.cpp ├── Constants.cpp ├── ControlFlow.cpp ├── ExpressionGraph.cpp ├── Extensions.cpp ├── FragmentShader.cpp ├── FunctionCall.cpp ├── GeometryShader.cpp ├── ImageRead.cpp ├── Linkage.cpp ├── ModuleTests.cpp ├── OldInstrTest.cpp ├── PhysicalStorageTest.cpp ├── SpvValidator.cpp ├── TestLogger.cpp └── Types.cpp /.github/workflows/CI.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow that is manually triggered 2 | 3 | name: CI 4 | 5 | on: 6 | pull_request: 7 | branches: 8 | - master 9 | 10 | push: 11 | branches: 12 | - master 13 | 14 | jobs: 15 | 16 | build: 17 | name: Build on ${{ matrix.os }} 18 | runs-on: ${{ matrix.os }} 19 | strategy: 20 | fail-fast: false 21 | matrix: 22 | os: [ubuntu-latest, windows-latest, macOS-latest] 23 | 24 | steps: 25 | - uses: actions/checkout@v3 26 | - uses: actions/setup-python@v3 27 | with: 28 | python-version: '3.x' 29 | 30 | - name: Create Build Environment 31 | run: cmake -E make_directory ${{runner.workspace}}/build 32 | 33 | - name: CMake 34 | run: cmake -S . -B ${{runner.workspace}}/build -DCMAKE_BUILD_TYPE=Release -DSPVGENTWO_BUILD_TESTS=TRUE -DSPVGENTWO_BUILD_EXAMPLES=TRUE -DSPVGENTWO_BUILD_DISASSEMBLER=TRUE -DSPVGENTWO_BUILD_REFLECT=TRUE -DSPVGENTWO_BUILD_LINKER=TRUE 35 | 36 | - name: Build 37 | working-directory: ${{runner.workspace}}/build 38 | run: cmake --build . --config Release -j4 39 | 40 | - name: Test 41 | working-directory: ${{runner.workspace}}/build 42 | run: ctest -C Release --rerun-failed --output-on-failure 43 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | misc/spvgentwo-cppcheck-build-dir 3 | .vscode 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Fabian Wahlster 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /common/include/common/Algorithms.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "spvgentwo/stdreplacement.h" 4 | 5 | namespace spvgentwo::algo 6 | { 7 | // transform a Container to a Container 8 | template class Container, class Func> 9 | auto transform(const Container& _input, const Func& _func) 10 | { 11 | static_assert(traits::is_invocable_v, "Func not invocable with const Elem&"); 12 | using Transformed = decltype(stdrep::declval()(stdrep::declval())); 13 | Container output(_input.getAllocator()); 14 | 15 | for(const Elem& elem : _input) 16 | { 17 | output.emplace_back(_func(elem)); 18 | } 19 | 20 | return output; 21 | } 22 | 23 | template class Container, class Func> 24 | auto transform(Container& _input, const Func& _func) 25 | { 26 | static_assert(traits::is_invocable_v, "Func not invocable with Elem&"); 27 | using Transformed = decltype(stdrep::declval()(stdrep::declval())); 28 | Container output(_input.getAllocator()); 29 | 30 | for (Elem& elem : _input) 31 | { 32 | output.emplace_back(_func(elem)); 33 | } 34 | 35 | return output; 36 | } 37 | 38 | template class Container, class Func> 39 | bool any(const Container& _input, const Func& _func) 40 | { 41 | static_assert(traits::is_invocable_v, "Func not invocable with const Elem&"); 42 | 43 | for (const Elem& elem : _input) 44 | { 45 | if (_func(elem)) 46 | return true; 47 | } 48 | 49 | return false; 50 | } 51 | 52 | template class Container, class Func> 53 | bool all(const Container& _input, const Func& _func) 54 | { 55 | static_assert(traits::is_invocable_v, "Func not invocable with const Elem&"); 56 | 57 | for (const Elem& elem : _input) 58 | { 59 | if (_func(elem) == false) 60 | return false; 61 | } 62 | 63 | return true; 64 | } 65 | } //!spvgentwo -------------------------------------------------------------------------------- /common/include/common/BinaryFileReader.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "spvgentwo/Reader.h" 4 | #include "spvgentwo/Vector.h" 5 | 6 | namespace spvgentwo 7 | { 8 | class BinaryFileReader : public IReader 9 | { 10 | public: 11 | BinaryFileReader(IAllocator& _allocator, const char* _path = nullptr, sgt_size_t _offset = 0u, sgt_size_t _length = 0u); 12 | ~BinaryFileReader() override = default; 13 | 14 | // get can only be called after read was successful 15 | bool get(unsigned int& _word) final; 16 | 17 | bool read(const char* _path, sgt_size_t _offset = 0u, sgt_size_t _length = 0u); 18 | operator bool() const { return m_buffer.empty() == false; } 19 | 20 | private: 21 | Vector m_buffer; 22 | sgt_size_t m_pos = 0u; 23 | }; 24 | } //!spvgentwo 25 | -------------------------------------------------------------------------------- /common/include/common/BinaryFileWriter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "spvgentwo/Writer.h" 4 | #include "spvgentwo/Vector.h" 5 | 6 | namespace spvgentwo 7 | { 8 | class BinaryFileWriter : public IWriter 9 | { 10 | public: 11 | BinaryFileWriter(IAllocator& _allocator, const char* _path = nullptr, sgt_size_t _initialBufferSize = 2 * 1024u); 12 | ~BinaryFileWriter(); 13 | 14 | bool put(unsigned int _word) final; 15 | 16 | bool open(const char* _path); 17 | bool isOpen() const { return m_pFile != nullptr; } 18 | operator bool() const { return m_pFile != nullptr; } 19 | 20 | void close(); 21 | 22 | private: 23 | Vector m_buffer; 24 | void* m_pFile = nullptr; 25 | }; 26 | } //!spvgentwo 27 | -------------------------------------------------------------------------------- /common/include/common/BinaryVectorWriter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "spvgentwo/Writer.h" 4 | 5 | namespace spvgentwo 6 | { 7 | template 8 | class BinaryVectorWriter : public IWriter 9 | { 10 | public: 11 | BinaryVectorWriter(U32Vector& _vector) : m_vector(_vector) {}; 12 | virtual ~BinaryVectorWriter() = default; 13 | 14 | bool put(unsigned int _word) final; 15 | 16 | private: 17 | U32Vector& m_vector; 18 | }; 19 | 20 | template 21 | inline bool BinaryVectorWriter::put(unsigned int _word) 22 | { 23 | m_vector.emplace_back(_word); 24 | return true; 25 | } 26 | } //!spvgentwo 27 | -------------------------------------------------------------------------------- /common/include/common/ConsoleLogger.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "spvgentwo/Logger.h" 4 | 5 | namespace spvgentwo 6 | { 7 | class ConsoleLogger : public ILogger 8 | { 9 | public: 10 | ConsoleLogger( Flag _filter = LogLevel::All ); 11 | ConsoleLogger(const ConsoleLogger&) = delete; 12 | ConsoleLogger(ConsoleLogger&&) = delete; 13 | }; 14 | } // !spvgentwo -------------------------------------------------------------------------------- /common/include/common/ControlFlowGraph.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Graph.h" 4 | #include "spvgentwo/Function.h" 5 | 6 | namespace spvgentwo 7 | { 8 | template 9 | class ControlFlowGraph : public Graph 10 | { 11 | public: 12 | using Graph::Graph; 13 | using NodeType = typename Graph::NodeType; 14 | 15 | ControlFlowGraph(const Function& _func, IAllocator*_pAllocator = nullptr); 16 | }; 17 | 18 | template 19 | inline ControlFlowGraph::ControlFlowGraph(const Function& _func, IAllocator* _pAllocator) : 20 | Graph(_pAllocator != nullptr ? _pAllocator : _func.getAllocator()) 21 | { 22 | auto addEdge = [&](NodeType* src, Instruction::Iterator target) 23 | { 24 | BasicBlock* bb = target->getBranchTarget(); 25 | if (Instruction* label = target->getInstruction(); bb == nullptr && label != nullptr && *label == spv::Op::OpLabel) 26 | { 27 | bb = label->getBasicBlock(); 28 | } 29 | 30 | if (bb != nullptr) 31 | { 32 | auto it = this->m_nodes.find_if([bb](NodeType& n) {return n.data() == bb; }); 33 | NodeType* targetNode = it == this->m_nodes.end() ? this->emplace(bb) : it.operator->(); 34 | src->connect(targetNode); 35 | } 36 | }; 37 | 38 | for (BasicBlock& bb : _func) 39 | { 40 | auto it = this->m_nodes.find_if([&bb](NodeType& n) {return n.data() == &bb; }); 41 | NodeType* node = it == this->m_nodes.end() ? this->emplace(&bb) : it.operator->(); 42 | 43 | auto term = bb.getTerminator(); 44 | 45 | if (term != nullptr) 46 | { 47 | for (auto term_it = term->getFirstActualOperand(); term_it != term->end(); ++term_it) 48 | { 49 | addEdge(node, term_it); 50 | } 51 | } 52 | } 53 | } 54 | } // !spvgentwo -------------------------------------------------------------------------------- /common/include/common/ExprGraph.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Graph.h" 4 | #include "Expression.h" 5 | #include "Algorithms.h" 6 | 7 | namespace spvgentwo 8 | { 9 | enum class ExprArgs 10 | { 11 | EdgeLists, 12 | FunctionPtrLists 13 | }; 14 | 15 | template 16 | class ExprGraph : public Graph> 17 | { 18 | public: 19 | using Graph>::Graph; 20 | 21 | using NodeType = typename Graph>::NodeType; 22 | 23 | template // unfold => call expr with List ins, List outs, or if unfold == false => List& ins, List& outs 24 | static void evaluateRecursive(NodeType* _pExitNode); 25 | 26 | template // every node in _nodeOrder must be unique 27 | static bool evaluateExplicit(const List& _nodeOrder); 28 | 29 | // resets nodes 'evaluated' state to false 30 | void resetEvaluationState(); 31 | }; 32 | 33 | template 34 | template 35 | inline void ExprGraph::evaluateRecursive(NodeType* _pExitNode) 36 | { 37 | for (auto& edge : _pExitNode->inputs()) 38 | { 39 | NodeType* in = edge.pTarget; 40 | auto& expr = in->data(); 41 | if (!expr) 42 | { 43 | evaluateRecursive(in); 44 | } 45 | } 46 | 47 | auto& expr = _pExitNode->data(); 48 | 49 | if (!expr) 50 | { 51 | if constexpr (args == ExprArgs::FunctionPtrLists) 52 | { 53 | auto inputs = algo::transform(_pExitNode->inputs(), [](auto& edge) -> Func* {return &edge.pTarget->data().get(); }); 54 | auto outputs = algo::transform(_pExitNode->outputs(), [](auto& edge) -> Func* {return &edge.pTarget->data().get(); }); 55 | expr(inputs, outputs); 56 | } 57 | else 58 | { 59 | expr(_pExitNode->inputs(), _pExitNode->output()); 60 | } 61 | } 62 | } 63 | 64 | template 65 | template 66 | inline bool ExprGraph::evaluateExplicit(const List& _nodeOrder) 67 | { 68 | for (NodeType* node : _nodeOrder) 69 | { 70 | auto& expr = node->data(); 71 | 72 | if (expr.evaluated()) // node was already evaluated (non unique) 73 | { 74 | return false; 75 | } 76 | 77 | if constexpr (args == ExprArgs::FunctionPtrLists) 78 | { 79 | auto inputs = transform(node->inputs(), [](auto& edge) -> Func* {return &edge.pTarget->data().get(); }); 80 | auto outputs = transform(node->outputs(), [](auto& edge) -> Func* {return &edge.pTarget->data().get(); }); 81 | expr(inputs, outputs); 82 | } 83 | else 84 | { 85 | expr(node->inputs(), node->output()); 86 | } 87 | } 88 | 89 | return true; 90 | } 91 | 92 | template 93 | inline void ExprGraph::resetEvaluationState() 94 | { 95 | for (NodeType& node : Graph>::m_nodes) 96 | { 97 | node.data().reset(); 98 | } 99 | } 100 | } // !spvgentwo -------------------------------------------------------------------------------- /common/include/common/Expression.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "spvgentwo/stdreplacement.h" 4 | 5 | namespace spvgentwo 6 | { 7 | template 8 | class Expression 9 | { 10 | Func m_func; 11 | bool ran = false; 12 | public: 13 | using FuncType = Func; 14 | 15 | Expression(Func&& _func) : m_func(stdrep::move(_func)) {} 16 | Expression(const Func& _func) : m_func(_func) {} 17 | 18 | //template 19 | //Expression(Args&& ... _args) : m_func{ stdrep::forward(_args)... } {} 20 | 21 | Func& get() { return m_func; } 22 | const Func& get() const { return m_func; } 23 | 24 | Func* operator->() { return &m_func; } 25 | const Func* operator->() const { return &m_func; } 26 | 27 | bool evaluated() const { return ran; } 28 | operator bool() const { return ran; } 29 | void reset() { ran = false; } 30 | 31 | template 32 | auto operator()(Args&& ... _args) 33 | { 34 | ran = true; 35 | return m_func(stdrep::forward(_args)...); 36 | } 37 | }; 38 | 39 | template 40 | auto make_expr(Args&& ... _args) { return Expression(stdrep::forward(_args)...); } 41 | 42 | template 43 | using expr_type = decltype(make_expr(stdrep::declval()...)); 44 | } // !spvgentwo -------------------------------------------------------------------------------- /common/include/common/ExternalMemoryAllocator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "spvgentwo/Allocator.h" 4 | 5 | namespace spvgentwo 6 | { 7 | class ExternalMemoryAllocator : public IAllocator 8 | { 9 | public: 10 | constexpr ExternalMemoryAllocator(void* _pBuffer = nullptr, sgt_size_t _capacity = 0u) : 11 | IAllocator(), 12 | m_pCurrent(_pBuffer), 13 | m_pEnd(static_cast(_pBuffer) + _capacity) 14 | {}; 15 | 16 | template 17 | constexpr ExternalMemoryAllocator(T(&_array)[N]) : IAllocator(), 18 | m_pCurrent(_array), 19 | m_pEnd(reinterpret_cast(_array) + sizeof(T[N])) 20 | { 21 | } 22 | 23 | ~ExternalMemoryAllocator() override = default; 24 | 25 | void* allocate(sgt_size_t _bytes, unsigned int _aligment) final; 26 | 27 | // can only deallocate last allocation, does not keep track of allocations 28 | void deallocate(void* _ptr, sgt_size_t _bytes = 0u) final; 29 | 30 | private: 31 | void* m_pCurrent = nullptr; 32 | const void* const m_pEnd = nullptr; 33 | }; 34 | 35 | template 36 | class StackStorage 37 | { 38 | public: 39 | char m_storage[Capacity]{}; 40 | }; 41 | 42 | template 43 | class StackAllocator : protected StackStorage, public ExternalMemoryAllocator 44 | { 45 | public: 46 | constexpr StackAllocator() : ExternalMemoryAllocator(StackStorage::m_storage) {} 47 | }; 48 | 49 | template 50 | class StackContainer : StackAllocator, public Container 51 | { 52 | public: 53 | template 54 | StackContainer(ContainerArgs&& ... _args) : Container(this, stdrep::forward(_args)...) {} 55 | }; 56 | 57 | template 58 | auto make_stack_container(ContainerArgs&& ... _args) 59 | { 60 | return StackContainer(stdrep::forward(_args)...); 61 | } 62 | } // !spvgentwo -------------------------------------------------------------------------------- /common/include/common/FunctionCallGraph.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Graph.h" 4 | 5 | namespace spvgentwo 6 | { 7 | // forward decls 8 | class Function; 9 | class Instruction; 10 | 11 | class FunctionCallGraph : public Graph // Function -> OpFunctionCall instruction 12 | { 13 | public: 14 | using Graph::Graph; 15 | using NodeType = typename Graph::NodeType; 16 | 17 | FunctionCallGraph(const Function& _func, IAllocator* _pAllocator = nullptr); 18 | 19 | private: 20 | void add(const Function& _func); 21 | }; 22 | } // !spvgentwo -------------------------------------------------------------------------------- /common/include/common/GrammarHelper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "spvgentwo/Instruction.h" 4 | #include "spvgentwo/Grammar.h" 5 | 6 | namespace spvgentwo 7 | { 8 | namespace GrammarHelper 9 | { 10 | Instruction::Iterator getFirstOperandOfKind(const Instruction* _pInstruction, const Grammar& _gram, Grammar::OperandKind _kind, Grammar::Extension _ext = Grammar::Extension::Core); 11 | 12 | Instruction::Iterator getOperandByName(const Instruction* _pInstruction, const Grammar& _gram, const char* _pName, Grammar::Extension _ext = Grammar::Extension::Core); 13 | } 14 | } // spvgentwo -------------------------------------------------------------------------------- /common/include/common/Graph.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Node.h" 4 | 5 | namespace spvgentwo 6 | { 7 | template 8 | class Graph 9 | { 10 | public: 11 | using NodeType = Node; 12 | using Iterator = typename List::Iterator; 13 | 14 | constexpr Graph(IAllocator* _pAllocator = nullptr); 15 | 16 | Graph(Graph&& _other) noexcept; 17 | 18 | // since nodes can not be copied, neither can graphs 19 | Graph(const Graph& _other) = delete; 20 | 21 | Graph& operator=(Graph&& _other) noexcept; 22 | Graph& operator=(const Graph& _other) = delete; 23 | 24 | constexpr IAllocator* getAllocator() const { return m_nodes.getAllocator(); } 25 | 26 | constexpr Iterator begin() const { return m_nodes.begin(); } 27 | constexpr Iterator end() const { return m_nodes.end(); } 28 | 29 | // add new node 30 | template 31 | NodeType* emplace(Args&& ..._args); 32 | 33 | Iterator erase(Iterator _pos); 34 | 35 | void clear(); 36 | 37 | constexpr bool empty() const { return m_nodes.empty(); } 38 | 39 | constexpr sgt_size_t size() const { return m_nodes.size(); } 40 | 41 | NodeType& front() { return m_nodes.front(); } 42 | const NodeType& front() const { return m_nodes.front(); } 43 | 44 | NodeType& back() { return m_nodes.front(); } 45 | const NodeType& back() const { return m_nodes.front(); } 46 | 47 | protected: 48 | 49 | List m_nodes; 50 | }; 51 | 52 | template 53 | inline constexpr Graph::Graph(IAllocator* _pAllocator) : 54 | m_nodes(_pAllocator) 55 | { 56 | } 57 | 58 | template 59 | inline Graph::Graph(Graph&& _other) noexcept : 60 | m_nodes(stdrep::move(_other.m_nodes)) 61 | { 62 | } 63 | 64 | template 65 | inline Graph& Graph::operator=(Graph&& _other) noexcept 66 | { 67 | m_nodes = stdrep::move(_other.m_nodes); 68 | } 69 | 70 | template 71 | inline typename Graph::Iterator Graph::erase(Iterator _pos) 72 | { 73 | if (_pos != nullptr) 74 | { 75 | NodeType& n = *_pos; 76 | for (auto& in : n.inputs()) 77 | { 78 | in.pTarget->remove_output(&n); 79 | } 80 | for (auto& out : n.outputs()) 81 | { 82 | out.pTarget->remove_input(&n); 83 | } 84 | return m_nodes.erase(_pos); 85 | } 86 | 87 | return _pos; 88 | } 89 | 90 | template 91 | template 92 | inline typename Graph::NodeType* Graph::emplace(Args&& ..._args) 93 | { 94 | return &m_nodes.emplace_back(getAllocator(), stdrep::forward(_args)...); 95 | } 96 | 97 | template 98 | inline void Graph::clear() 99 | { 100 | m_nodes.clear(); 101 | } 102 | } // spvgentwo -------------------------------------------------------------------------------- /common/include/common/HeapAllocator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "spvgentwo/Allocator.h" 4 | 5 | namespace spvgentwo 6 | { 7 | class HeapAllocator : public IAllocator 8 | { 9 | public: 10 | HeapAllocator(); 11 | 12 | void* allocate(sgt_size_t _bytes, unsigned int _alignmentHint) final; 13 | void deallocate(void* _ptr, sgt_size_t _bytes) final; 14 | 15 | static HeapAllocator* instance(); 16 | 17 | static void setBreakAlloc( long alloc ); 18 | 19 | private: 20 | sgt_size_t m_Allocated = 0u; 21 | sgt_size_t m_Deallocated = 0u; 22 | }; 23 | 24 | template 25 | class HeapContainer : public Container 26 | { 27 | public: 28 | template 29 | HeapContainer(ContainerArgs&& ... _args) : Container(HeapAllocator::instance(), stdrep::forward(_args)...) {} 30 | }; 31 | 32 | template 33 | auto make_heap_container(ContainerArgs&& ... _args) 34 | { 35 | return HeapContainer(stdrep::forward(_args)...); 36 | } 37 | } //! spvgentwo 38 | -------------------------------------------------------------------------------- /common/include/common/HeapCallable.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Callable.h" 4 | #include "HeapAllocator.h" 5 | 6 | namespace spvgentwo 7 | { 8 | template 9 | class HeapCallable; 10 | 11 | template 12 | class HeapCallable : public Callable 13 | { 14 | public: 15 | HeapCallable(const HeapCallable& _other) noexcept : Callable(_other){} 16 | HeapCallable(HeapCallable&& _other) noexcept : Callable(stdrep::move(_other)) {} 17 | 18 | template 19 | HeapCallable(Args&& ... _args) : Callable(HeapAllocator::instance(), stdrep::forward(_args)...) {} 20 | 21 | virtual ~HeapCallable() override = default; 22 | 23 | HeapCallable& operator=(const HeapCallable& _other) { Callable::operator=(_other); return *this; } 24 | HeapCallable& operator=(HeapCallable&& _other) noexcept { Callable::operator=(stdrep::move(_other)); return *this; } 25 | 26 | template 27 | HeapCallable& operator=(Args&& ..._args) noexcept { Callable::operator=(stdrep::forward(_args)...); return *this; } 28 | }; 29 | 30 | template 31 | auto make_callable(ReturnType(*_func)(Args...)) 32 | { 33 | return Callable(HeapAllocator::instance(), _func); 34 | } 35 | 36 | template 37 | auto make_callable(ReturnType(*_func)(Args..., ...)) 38 | { 39 | return Callable(HeapAllocator::instance(), _func); 40 | } 41 | 42 | template 43 | auto make_callable(Obj* _obj, ReturnType(Obj::*_func)(Args...)) 44 | { 45 | return Callable(HeapAllocator::instance(), _obj, _func); 46 | } 47 | 48 | template 49 | auto make_callable(Obj* _obj, ReturnType(Obj::*_func)(Args..., ...)) 50 | { 51 | return Callable(HeapAllocator::instance(), _obj, _func); 52 | } 53 | 54 | // fallback 55 | template 56 | auto make_callable(const Func& _func) 57 | { 58 | return HeapCallable(_func); 59 | } 60 | 61 | template 62 | auto make_callable(Obj* _pObj, const Func& _func) 63 | { 64 | return HeapCallable(_pObj, _func); 65 | } 66 | 67 | } // !spvgentwo -------------------------------------------------------------------------------- /common/include/common/HeapHashMap.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "spvgentwo/HashMap.h" 4 | #include "HeapAllocator.h" 5 | 6 | namespace spvgentwo 7 | { 8 | template 9 | class HeapHashMap : public HashMap 10 | { 11 | public: 12 | using HashMap::HashMap; 13 | 14 | HeapHashMap(unsigned int _buckets = HashMap::DefaultBucktCount) : HashMap(HeapAllocator::instance(), _buckets) {} 15 | HeapHashMap(HeapHashMap&& _other) noexcept : HashMap(stdrep::move(_other)) {} 16 | 17 | template 18 | HeapHashMap(Key&& _key, Value&& _value, Args&& ... _keyvals) : 19 | HashMap(HeapAllocator::instance(), stdrep::forward(_key), stdrep::forward(_value), stdrep::forward(_keyvals)...) 20 | {} 21 | 22 | HeapHashMap& operator=(HeapHashMap&& _other) noexcept { HashMap::operator=(stdrep::move(_other)); return *this; } 23 | 24 | virtual ~HeapHashMap() override = default; 25 | }; 26 | }// !spvgentwo -------------------------------------------------------------------------------- /common/include/common/HeapList.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "spvgentwo/List.h" 4 | #include "HeapAllocator.h" 5 | 6 | namespace spvgentwo 7 | { 8 | template 9 | class HeapList : public List 10 | { 11 | public: 12 | using List::List; 13 | 14 | HeapList() : List(HeapAllocator::instance()) {} 15 | 16 | HeapList(const HeapList& _other) : List(_other) {} 17 | 18 | HeapList(HeapList&& _other) noexcept : List(stdrep::move(_other)) {} 19 | 20 | template // args must be of type T 21 | HeapList(T&& _first, Args&& ... _args) : List(HeapAllocator::instance(), stdrep::forward(_first), stdrep::forward(_args)...) {} 22 | 23 | HeapList& operator=(const HeapList& _other) { List::operator=(_other); return *this; } 24 | HeapList& operator=(HeapList&& _other) noexcept { List::operator=(stdrep::move(_other)); return *this; } 25 | 26 | virtual ~HeapList() override = default; 27 | }; 28 | } -------------------------------------------------------------------------------- /common/include/common/HeapString.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "spvgentwo/String.h" 4 | #include "HeapAllocator.h" 5 | 6 | namespace spvgentwo 7 | { 8 | class HeapString : public String 9 | { 10 | public: 11 | using String::String; 12 | 13 | HeapString(const HeapString& _other) : String(_other) {} 14 | 15 | HeapString(HeapString&& _other) noexcept: String(stdrep::move(_other)) {} 16 | 17 | HeapString(const char* _pStr = nullptr, sgt_size_t _length = 0u) : String(HeapAllocator::instance(), _pStr, _length) {}; 18 | 19 | template 20 | HeapString(const char(&_pStr)[N]) : String(HeapAllocator::instance(), _pStr) {} 21 | 22 | virtual ~HeapString() override = default; 23 | 24 | HeapString& operator=(const HeapString& _other) { String::operator=(_other); return *this; } 25 | HeapString& operator=(HeapString&& _other) noexcept { String::operator=(stdrep::move(_other)); return *this; } 26 | }; 27 | } // !spvgentwo -------------------------------------------------------------------------------- /common/include/common/HeapVector.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "spvgentwo/Vector.h" 4 | #include "HeapAllocator.h" 5 | 6 | namespace spvgentwo 7 | { 8 | template 9 | class HeapVector : public Vector 10 | { 11 | public: 12 | using T = typename stdrep::remove_cv_t; 13 | 14 | using Vector::Vector; 15 | 16 | HeapVector(const HeapVector& _other) : Vector(_other) {} 17 | 18 | HeapVector(HeapVector&& _other) noexcept : Vector(stdrep::move(_other)) {} 19 | 20 | HeapVector(sgt_size_t _size = 0u) : Vector(HeapAllocator::instance(), _size) {} 21 | 22 | // copy from array 23 | HeapVector(const T* _pData, sgt_size_t _size = 0u) : Vector(HeapAllocator::instance(), _pData, _size) {} 24 | 25 | template 26 | HeapVector(const T(&_array)[Size]) : Vector(HeapAllocator::instance(), _array) {} 27 | 28 | template 29 | HeapVector(const T& _first, Args&& ... _args) : Vector(HeapAllocator::instance(), _first, stdrep::forward(_args)...) {} 30 | 31 | virtual ~HeapVector() override = default; 32 | 33 | HeapVector& operator=(const HeapVector& _other) { Vector::operator=(_other); return *this; } 34 | HeapVector& operator=(HeapVector&& _other) noexcept { Vector::operator=(stdrep::move(_other)); return *this; } 35 | }; 36 | } // !spvgentwo -------------------------------------------------------------------------------- /common/include/common/LinkerHelper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "spvgentwo/Spv.h" 3 | #include "spvgentwo/Flag.h" 4 | 5 | namespace spvgentwo 6 | { 7 | // forward decls 8 | class Module; 9 | class Function; 10 | class IAllocator; 11 | class Instruction; 12 | class Grammar; 13 | class String; 14 | 15 | namespace ModulePrinter 16 | { 17 | class IModulePrinter; 18 | } 19 | 20 | namespace LinkerHelper 21 | { 22 | // turn function definition into a declaration by removing its basic blocks and instruction decorates/names from the module 23 | bool removeFunctionBody(Function& _func); 24 | 25 | // adds & returns linkage OpDecorate for _varOrFunc ans symbol name 26 | Instruction* addLinkageDecoration(Instruction* _varOrFunc, spv::LinkageType _linkage, const char* name); 27 | 28 | // adds OpDecorateLinkage to global variables referenced in _func, use variable names (from OpName) as export name, if no name is present, it can't be exportet 29 | bool addLinkageDecorateForUsedGlobalVariables(const Function& _func, spv::LinkageType _linkage, IAllocator* _pAllocator = nullptr); 30 | 31 | // extract linkage type, symbol (optional) and name (optional) from OpDecorate, returns LinkageType::Max on error 32 | spv::LinkageType getLinkageTypeFromDecoration(const Instruction* _pDecoration, Instruction** _ppOutSymbol = nullptr, String* _pOutName = nullptr); 33 | 34 | enum class LinkerOptionBits 35 | { 36 | ImportMissingTypes = 1 << 0, // during instruction lookup, import missing types 37 | ImportMissingConstants = 1 << 1, // during instruction lookup, import missing constants 38 | ImportMissingExtensionSets = 1 << 2, // during instruction lookup, import missing OpExtInstImport 39 | ImportReferencedDecorations = 1 << 3, // referencing imported symbol 40 | ImportReferencedNames = 1 << 4, // referencing imported symbol 41 | ImportReferencedFunctions = 1 << 5, // functions called from an imported function, but were not exported themselves 42 | ImportReferencedVariables = 1 << 6, // global variables referenced in (auto) imported functions 43 | ImportReferencedStrings = 1 << 7, // global strings referenced in OpLine etc 44 | AssignResultIDs = 1 << 8, // on-the-fligh assing ResultIds while transfering instructions from lib to consumber module (leaves original IDs intact, can improve performance if module.assignIDs() is not used) 45 | RemoveLinkageCapability = 1 << 8, // remove linkage capability from the consumer library if all imports and exports have been resolved 46 | AutoAddRequiredCapabilitiesAndExtensions = 1 << 10, // instead of copying ALL Capabilities & Extensions from the all libs to the consumer module, use LinkerOptions.grammar to select only required Caps & Exts for the consumer module. 47 | UpdateEntryPointGlobalVarInterface = 1 << 11, // Add global variables referenced by the consumer EntryPoints to their OpEntryPoint, this should always be switched on unless Module.finalizeEntryPoints() is called after linking. 48 | CopyOpSourceStringInstructions = 1 << 12, // Copy OpString, OpSource, OpSourceContinued and OpSourceExtension instructions from lib to consumer module 49 | CopyOpLineInstructions = 1 << 13, // Copy OpLine, OpNoLine 50 | CopyOpModuleProcessedInstructions = 1 << 14, // Copy OpModuleProcessed 51 | All = CopyOpModuleProcessedInstructions | (CopyOpModuleProcessedInstructions - 1) 52 | }; 53 | 54 | struct LinkerOptions 55 | { 56 | Flag flags{}; 57 | ModulePrinter::IModulePrinter* printer = nullptr; // used to print instructions when transfered 58 | const Grammar* grammar = nullptr; // used for printing instructions & add required capabilities for the resulting module 59 | IAllocator* allocator = nullptr; // used for intermediate allocations, if nullptr, _consumer allocator is used 60 | }; 61 | 62 | constexpr Flag operator&(const LinkerOptions& l, const LinkerOptionBits& r) { return l.flags & r; } 63 | 64 | // import exported symbols of _lib to _consumber 65 | bool import(const Module& _lib, Module& _consumer, const LinkerOptions& _options); 66 | } // !LinkerHelper 67 | } // spvgentwo -------------------------------------------------------------------------------- /common/include/common/ModulePrinter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "spvgentwo/Flag.h" 4 | 5 | namespace spvgentwo 6 | { 7 | // forward decl 8 | class Module; 9 | class Instruction; 10 | class Grammar; 11 | class String; 12 | class Constant; 13 | 14 | namespace ModulePrinter 15 | { 16 | namespace detail 17 | { 18 | struct EmptyConstantPrintFunc 19 | { 20 | constexpr bool append([[maybe_unused]] const Constant& _constant, [[maybe_unused]] const char* _pushColor, [[maybe_unused]] const char* _popColor) const { return false; } 21 | }; 22 | 23 | constexpr auto PushRed = u8"\x1B[31m"; 24 | constexpr auto PushGreen = u8"\x1B[32m"; 25 | constexpr auto PushYellow = u8"\x1B[33m"; 26 | constexpr auto PushBlue = u8"\x1B[34m"; 27 | constexpr auto PushPurple = u8"\x1B[35m"; 28 | constexpr auto PushLightBlue = u8"\x1B[36m"; 29 | constexpr auto PushWhite = u8"\x1B[97m"; 30 | constexpr auto PushRedBG = u8"\x1B[41m"; 31 | constexpr auto PopGrey = u8"\033[0m"; 32 | } 33 | 34 | struct ColorScheme 35 | { 36 | const char* literal = detail::PushRed; 37 | const char* string = detail::PushGreen; 38 | const char* operandId = detail::PushYellow; 39 | const char* resultId = detail::PushBlue; 40 | const char* extinst = detail::PushPurple; // ExtInst Instructions names 41 | const char* constant = detail::PushLightBlue; 42 | const char* enumValue = detail::PushWhite; 43 | const char* error = detail::PushRedBG; 44 | const char* defaultText = detail::PopGrey; 45 | }; 46 | 47 | class IModulePrinter 48 | { 49 | public: 50 | 51 | // expects ANSI color codes, nullptr does nothing 52 | virtual void append(const char* _pText, const char* _pushColor = nullptr, const char* _popColor = nullptr) = 0; 53 | // print contents of _constants, returns false if implementation unable to print this type 54 | virtual bool append(const Constant& _constant, const char* _pushColor = nullptr, const char* _popColor = nullptr); 55 | 56 | // append a literal value as base10 ascii string 57 | void append(unsigned int _literal, const char* _pushColor = nullptr, const char* _popColor = nullptr); 58 | 59 | void setColorScheme(const ColorScheme& _colors) { m_colors = _colors; } 60 | const ColorScheme& getColorScheme() const { return m_colors; } 61 | 62 | IModulePrinter& operator<<(const char* _pStr) { append(_pStr); return *this; } 63 | IModulePrinter& operator<<(unsigned int _literal) { append(_literal); return *this; } 64 | IModulePrinter& operator<<(const Constant& _constant) { append(_constant); return *this; } 65 | 66 | private: 67 | ColorScheme m_colors{}; 68 | }; 69 | 70 | // void FuncStr(const char* _pStr, const char* _pushColor, const char* _popColor) 71 | // void FuncConstant(const Constant& _constant, const char* _pushColor, const char* _popColor) 72 | template 73 | class ModuleFuncPrinter : public IModulePrinter 74 | { 75 | public: 76 | ModuleFuncPrinter(FuncStr _strFunc) : 77 | m_strFunc{ _strFunc } {} 78 | 79 | ModuleFuncPrinter(FuncStr _strFunc, FuncConstant _constantFunc) : 80 | m_strFunc{ _strFunc }, m_constantFunc{ _constantFunc } {} 81 | 82 | void append(const char* _pStr, const char* _pushColor = nullptr, const char* _popColor = nullptr) final { m_strFunc(_pStr, _pushColor, _popColor); } 83 | 84 | bool append(const Constant& _constant, const char* _pushColor = nullptr, const char* _popColor = nullptr) final { return m_constantFunc(_constant, _pushColor, _popColor); }; 85 | 86 | private: 87 | FuncStr m_strFunc{}; 88 | FuncConstant m_constantFunc{}; 89 | }; 90 | 91 | template 92 | ModuleFuncPrinter(FuncStr _strFunc)->ModuleFuncPrinter; 93 | 94 | template 95 | ModuleFuncPrinter(FuncStr _strFunc, FuncConstant _constantFunc)->ModuleFuncPrinter; 96 | 97 | template // void SimpleFuncStr(const char*) 98 | class ModuleSimpleFuncPrinter : public IModulePrinter 99 | { 100 | public: 101 | ModuleSimpleFuncPrinter(SimpleFuncStr _strFunc, bool _useColorCodes = false) : 102 | m_strFunc{ _strFunc }, m_useColor(_useColorCodes) {} 103 | 104 | void append(const char* _pStr, const char* _pushColor = nullptr, const char* _popColor = nullptr) final 105 | { 106 | if (_pushColor != nullptr && m_useColor) 107 | { 108 | m_strFunc(_pushColor); 109 | } 110 | if (_pStr != nullptr) 111 | { 112 | m_strFunc(_pStr); 113 | } 114 | if (_popColor != nullptr && m_useColor) 115 | { 116 | m_strFunc(_popColor); 117 | } 118 | } 119 | 120 | private: 121 | SimpleFuncStr m_strFunc{}; 122 | bool m_useColor = false; 123 | }; 124 | 125 | template 126 | ModuleSimpleFuncPrinter(FuncStr _strFunc, bool _useColorCodes = false)->ModuleSimpleFuncPrinter; 127 | 128 | class ModuleStringPrinter : public IModulePrinter 129 | { 130 | public: 131 | ModuleStringPrinter(String& _outBuffer, bool _useColorCodes = false) : m_buffer(_outBuffer), m_useColor(_useColorCodes) {} 132 | 133 | void append(const char* _pStr, const char* _pushColor = nullptr, const char* _popColor = nullptr) final; 134 | 135 | protected: 136 | String& m_buffer; 137 | bool m_useColor = false; 138 | }; 139 | 140 | enum class PrintOptionsBits 141 | { 142 | Preamble = 1 << 0, 143 | InstructionName = 1 << 1, 144 | OperandName = 1 << 2, 145 | ResultId = 1 << 3, 146 | OperationName = 1 << 4, 147 | TypeName = 1 << 5, 148 | ConstantData = 1 << 6, 149 | All = ConstantData | (ConstantData - 1) 150 | }; 151 | 152 | using PrintOptions = Flag; 153 | 154 | // returns false on failure / invalid instruction 155 | bool printInstruction(const Instruction& _instr, const Grammar& _grammar, IModulePrinter& _printer, PrintOptions _options = PrintOptionsBits::All, const char* _pIndentation = "\t\t"); 156 | 157 | bool printModule(const Module& _module, const Grammar& _grammar, IModulePrinter& _printer, PrintOptions _options = PrintOptionsBits::All, const char* _pIndentation = "\t\t"); 158 | } // !ModulePrinter 159 | } -------------------------------------------------------------------------------- /common/include/common/Node.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "spvgentwo/List.h" 4 | 5 | namespace spvgentwo 6 | { 7 | class IAllocator; 8 | 9 | struct EmptyEdge {}; 10 | 11 | template 12 | struct Edge 13 | { 14 | Node* pTarget{}; 15 | Data data{}; 16 | }; 17 | 18 | // N = node data, E = edge data 19 | template 20 | class Node 21 | { 22 | public: 23 | using EdgeType = Edge; 24 | 25 | Node(Node&& _other) noexcept; 26 | 27 | // node edges store pointers to other graph nodes and cant be copied 28 | Node(const Node& _other) = delete; 29 | 30 | constexpr Node(IAllocator* _pAllocator = nullptr); 31 | 32 | template 33 | constexpr Node(IAllocator* _pAllocator, Args&& ... _args); 34 | 35 | Node& operator=(Node&& _other) noexcept; 36 | Node& operator=(const Node& _other) = delete; 37 | 38 | // connect this nodes (output edge) to target (input edge) 39 | template 40 | void connect(Node* _pTarget, EdgeArgs&& ..._args); 41 | 42 | // disconnect pTarget form this outputs, and remove this from pTarget inputs 43 | void disconnect(Node* _pTarget); 44 | 45 | const List& inputs() const { return m_inputs; } 46 | List& inputs() { return m_inputs; } 47 | 48 | const List& outputs() const { return m_outputs; } 49 | List& outputs() { return m_outputs; } 50 | 51 | const N& data() const { return m_data; } 52 | N& data() { return m_data; } 53 | 54 | typename List::Iterator remove_input(const Node* _pNode); 55 | typename List::Iterator remove_output(const Node* _pNode); 56 | 57 | private: 58 | N m_data{}; 59 | 60 | List m_inputs; 61 | List m_outputs; 62 | }; 63 | 64 | template 65 | inline Node::Node(Node&& _other) noexcept : 66 | m_data(stdrep::move(_other.m_data)), 67 | m_inputs(stdrep::move(_other.m_inputs)), 68 | m_outputs(stdrep::move(_other.m_outputs)) 69 | { 70 | } 71 | 72 | template 73 | inline constexpr Node::Node(IAllocator* _pAllocator) : 74 | m_inputs(_pAllocator), 75 | m_outputs(_pAllocator) 76 | { 77 | } 78 | 79 | template 80 | inline Node& Node::operator=(Node&& _other) noexcept 81 | { 82 | if (this == &_other) return this; 83 | 84 | m_data = stdrep::move(_other.m_data); 85 | m_inputs = stdrep::move(_other.m_inputs); 86 | m_outputs = stdrep::move(_other.m_outputs); 87 | 88 | return *this; 89 | } 90 | 91 | template 92 | inline typename List::EdgeType>::Iterator Node::remove_input(const Node* _pNode) 93 | { 94 | auto it = m_inputs.find_if([_pNode](const auto& e) {return e.pTarget == _pNode; }); 95 | if (it != nullptr) 96 | { 97 | return m_inputs.erase(it); 98 | } 99 | return it; 100 | } 101 | 102 | template 103 | inline typename List::EdgeType>::Iterator Node::remove_output(const Node* _pNode) 104 | { 105 | auto it = m_outputs.find_if([_pNode](const auto& e) {return e.pTarget == _pNode; }); 106 | if (it != nullptr) 107 | { 108 | return m_outputs.erase(it); 109 | } 110 | return it; 111 | } 112 | 113 | template 114 | template 115 | inline constexpr Node::Node(IAllocator* _pAllocator, Args&& ..._args) : 116 | m_data{stdrep::forward(_args)...}, 117 | m_inputs(_pAllocator), 118 | m_outputs(_pAllocator) 119 | { 120 | } 121 | 122 | template 123 | template 124 | inline void Node::connect(Node* _pTarget, EdgeArgs&& ..._args) 125 | { 126 | m_outputs.emplace_back(_pTarget, stdrep::forward(_args)...); 127 | _pTarget->m_inputs.emplace_back(this, stdrep::forward(_args)...); 128 | } 129 | 130 | template 131 | inline void Node::disconnect(Node* _pTarget) 132 | { 133 | remove_output(_pTarget); 134 | _pTarget->remove_input(this); 135 | } 136 | } // spvgentwo -------------------------------------------------------------------------------- /common/include/common/ReflectionHelper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "spvgentwo/List.h" 4 | #include "spvgentwo/Spv.h" 5 | 6 | namespace spvgentwo 7 | { 8 | // forward decls 9 | 10 | class Module; 11 | class Instruction; 12 | 13 | namespace ReflectionHelper 14 | { 15 | // parse OpExecutionMode/Id operands, returns false if neither LocalSize or LocalSizeHint are present 16 | bool getLocalSize(const Module& _module, unsigned int& _x, unsigned int& _y, unsigned int& _z); 17 | 18 | // get all GLOBAL variables by StorageClass, Function variables are not gathered 19 | void getGlobalVariablesByStorageClass(const Module& _module, spv::StorageClass _class, List& _outVariables); 20 | 21 | // get all instructions which have been decorated with _decoration & _value 22 | void getVariablesWithDecoration(const Module& _module, spv::Decoration _decoration, List& _outTargets, const unsigned int* _pValue = nullptr); 23 | 24 | // call func with instructions which have been decorated with _decoration & _value (implemented in ReflectionHelperTemplate.inl) 25 | template 26 | void getVariablesWithDecorationFunc(const Module& _module, spv::Decoration _decoration, Func _func, const unsigned int* _pValue = nullptr); 27 | 28 | // get list of OpDecorate, OpMemberDecorate etc that target _pTarget in the instruction's module 29 | void getDecorations(const Instruction* _pTarget, List& _outDecorations); 30 | 31 | // call _func(Instruction*) for every OpDecorate# with _pTarget 32 | template 33 | void getDecorationsFunc(const Instruction* _pTarget, Func _func); 34 | 35 | // call _func(Instruction*) for every OpName# with _pTarget 36 | template 37 | void getNamesFunc(const Instruction* _pTarget, Func _func); 38 | 39 | // given OpDecorate or OpMemberDecorate, returns spv::Decorations, returns Decoration::Max if invalid input parameter 40 | spv::Decoration getSpvDecorationKindFromDecoration(const Instruction* _pDecoration); 41 | 42 | // given OpDecorate or OpMemberDecorate, returns spv::Decorations & literal value via output parameters, returns false if invalid input parameter 43 | bool getSpvDecorationAndLiteralFromDecoration(const Instruction* _pInDecoration, spv::Decoration& _outDecoration, unsigned int& _outValue); 44 | 45 | // _pDecoration must be OpDecorate (not OpMemberDecorate etc), returns uint_max if invalid/not applicable 46 | unsigned int getLiteralFromDecoration(spv::Decoration _decoration, const Instruction* _pDecoration); 47 | 48 | // get the literal value of _decoration used to decorate _pTarget instruction (type or variable) 49 | // _pOutDecoration can be used to receive the OpDecoration instruction associated to the target & decoration type 50 | unsigned int getDecorationLiteralFromTarget(spv::Decoration _decoration, const Instruction* _pTarget, const Instruction** _pOutDecoration = nullptr); 51 | } 52 | } // !spvgentwo -------------------------------------------------------------------------------- /common/include/common/ReflectionHelperTemplate.inl: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "spvgentwo/Module.h" 4 | #include "ReflectionHelper.h" 5 | 6 | // get call func with instructions which have been decorated with _decoration & _value 7 | template 8 | void spvgentwo::ReflectionHelper::getVariablesWithDecorationFunc(const Module& _module, spv::Decoration _decoration, Func _func, const unsigned int* _pValue) 9 | { 10 | for (const Instruction& deco : _module.getDecorations()) 11 | { 12 | auto target = deco.getFirstActualOperand(); 13 | 14 | auto it = target.next(); // skip target ID 15 | 16 | bool match = false; 17 | if (deco == spv::Op::OpDecorate && it != nullptr && static_cast(it->getLiteral().value) == _decoration) 18 | { 19 | match = true; 20 | } 21 | else if (deco == spv::Op::OpMemberDecorate && it.next() != nullptr && static_cast(it.next()->getLiteral().value) == _decoration) 22 | { 23 | match = true; 24 | ++it; 25 | } 26 | 27 | if (match && it.next() != nullptr) 28 | { 29 | if (_pValue != nullptr) 30 | { 31 | if (*_pValue == it.next()->getLiteral()) 32 | { 33 | _func(target->getInstruction()); 34 | } 35 | } 36 | else 37 | { 38 | _func(target->getInstruction()); 39 | } 40 | } 41 | } 42 | } 43 | 44 | template 45 | void spvgentwo::ReflectionHelper::getDecorationsFunc(const Instruction* _pTarget, Func _func) 46 | { 47 | if (_pTarget == nullptr || _pTarget->getModule() == nullptr) 48 | return; 49 | 50 | for (Instruction& decoration : _pTarget->getModule()->getDecorations()) 51 | { 52 | if (*decoration.getFirstActualOperand() == _pTarget) // check target 53 | { 54 | _func(&decoration); 55 | } 56 | } 57 | } 58 | 59 | template 60 | void spvgentwo::ReflectionHelper::getNamesFunc(const Instruction* _pTarget, Func _func) 61 | { 62 | if (_pTarget == nullptr || _pTarget->getModule() == nullptr) 63 | return; 64 | 65 | for (Instruction& name : _pTarget->getModule()->getNames()) 66 | { 67 | if (*name.getFirstActualOperand() == _pTarget) // check target 68 | { 69 | _func(&name); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /common/include/common/TypeHelper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace spvgentwo 4 | { 5 | // forward decls: 6 | class Type; 7 | class String; 8 | class Instruction; 9 | 10 | namespace TypeHelper 11 | { 12 | // _pOpTypeInstr is used to obtain names assigned via OpName. (struct names, optional parameter) 13 | void getTypeName(const Type& _type, String& _outName, const Instruction* _pOpTypeInstr = nullptr); 14 | } // TypeHelper 15 | } // spvgentwo -------------------------------------------------------------------------------- /common/include/common/VulkanInterop.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "spvgentwo/Spv.h" 4 | 5 | namespace spvgentwo 6 | { 7 | // forward decls 8 | class Instruction; 9 | 10 | namespace vk 11 | { 12 | // identical to VkShaderStageFlagBits 13 | enum class ShaderStageFlagBits : unsigned int 14 | { 15 | VK_SHADER_STAGE_VERTEX_BIT = 0x00000001, 16 | VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT = 0x00000002, 17 | VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT = 0x00000004, 18 | VK_SHADER_STAGE_GEOMETRY_BIT = 0x00000008, 19 | VK_SHADER_STAGE_FRAGMENT_BIT = 0x00000010, 20 | VK_SHADER_STAGE_COMPUTE_BIT = 0x00000020, 21 | VK_SHADER_STAGE_ALL_GRAPHICS = 0x0000001F, 22 | VK_SHADER_STAGE_ALL = 0x7FFFFFFF, 23 | VK_SHADER_STAGE_RAYGEN_BIT_KHR = 0x00000100, 24 | VK_SHADER_STAGE_ANY_HIT_BIT_KHR = 0x00000200, 25 | VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR = 0x00000400, 26 | VK_SHADER_STAGE_MISS_BIT_KHR = 0x00000800, 27 | VK_SHADER_STAGE_INTERSECTION_BIT_KHR = 0x00001000, 28 | VK_SHADER_STAGE_CALLABLE_BIT_KHR = 0x00002000, 29 | VK_SHADER_STAGE_TASK_BIT_NV = 0x00000040, 30 | VK_SHADER_STAGE_MESH_BIT_NV = 0x00000080, 31 | VK_SHADER_STAGE_RAYGEN_BIT_NV = VK_SHADER_STAGE_RAYGEN_BIT_KHR, 32 | VK_SHADER_STAGE_ANY_HIT_BIT_NV = VK_SHADER_STAGE_ANY_HIT_BIT_KHR, 33 | VK_SHADER_STAGE_CLOSEST_HIT_BIT_NV = VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR, 34 | VK_SHADER_STAGE_MISS_BIT_NV = VK_SHADER_STAGE_MISS_BIT_KHR, 35 | VK_SHADER_STAGE_INTERSECTION_BIT_NV = VK_SHADER_STAGE_INTERSECTION_BIT_KHR, 36 | VK_SHADER_STAGE_CALLABLE_BIT_NV = VK_SHADER_STAGE_CALLABLE_BIT_KHR, 37 | VK_SHADER_STAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF 38 | }; 39 | 40 | // returns VK_SHADER_STAGE_FLAG_BITS_MAX_ENUM if incompatible (OpenCL, unknown extension) 41 | ShaderStageFlagBits getShaderStageFromExecutionModel(spv::ExecutionModel _model); 42 | 43 | // identical to VkDescriptorType 44 | enum class DescriptorType 45 | { 46 | VK_DESCRIPTOR_TYPE_SAMPLER = 0, 47 | VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER = 1, 48 | VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE = 2, 49 | VK_DESCRIPTOR_TYPE_STORAGE_IMAGE = 3, 50 | VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER = 4, 51 | VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER = 5, 52 | VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER = 6, 53 | VK_DESCRIPTOR_TYPE_STORAGE_BUFFER = 7, 54 | VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC = 8, 55 | VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC = 9, 56 | VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT = 10, 57 | VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT = 1000138000, 58 | VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR = 1000150000, 59 | VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_NV = 1000165000, 60 | VK_DESCRIPTOR_TYPE_MAX_ENUM = 0x7FFFFFFF 61 | }; 62 | 63 | // _pVariable needs to have a valid type (Module::reconstructTypeAndConstantInfo() has to be called before) 64 | // returns VK_DESCRIPTOR_TYPE_MAX_ENUM if input variable is incompatible. This is not an accureate translation for all VK usecases (can't infer if buffer is 'dynamic') 65 | DescriptorType getDescriptorTypeFromVariable(const Instruction& _variable); 66 | } 67 | } //spvgentwo -------------------------------------------------------------------------------- /common/source/BinaryFileReader.cpp: -------------------------------------------------------------------------------- 1 | #include "common/BinaryFileReader.h" 2 | #include 3 | 4 | spvgentwo::BinaryFileReader::BinaryFileReader(IAllocator& _allocator, const char* _path, sgt_size_t _offset, sgt_size_t _length) : 5 | m_buffer(&_allocator) 6 | { 7 | read(_path, _offset, _length); 8 | } 9 | 10 | bool spvgentwo::BinaryFileReader::get(unsigned int& _word) 11 | { 12 | if (m_pos < m_buffer.size()) 13 | { 14 | _word = m_buffer[m_pos++]; 15 | return true; 16 | } 17 | 18 | return false; 19 | } 20 | 21 | bool spvgentwo::BinaryFileReader::read(const char* _path, sgt_size_t _offset, sgt_size_t _length) 22 | { 23 | if (_path == nullptr) 24 | { 25 | return false; 26 | } 27 | 28 | struct rai 29 | { 30 | FILE* file = nullptr; 31 | ~rai() 32 | { 33 | if (file != nullptr) 34 | { 35 | fclose(file); 36 | file = nullptr; 37 | } 38 | } 39 | }; 40 | 41 | FILE* file = nullptr; 42 | #ifdef _CRT_INSECURE_DEPRECATE 43 | if (fopen_s(&file, _path, "rb") != 0) 44 | { 45 | return false; 46 | } 47 | #else 48 | file = fopen(_path, "rb"); 49 | #endif 50 | 51 | const rai closer{ file }; 52 | 53 | if (file != nullptr) 54 | { 55 | if (fseek(file, static_cast(_offset), SEEK_END) != 0) 56 | { 57 | return false; 58 | } 59 | 60 | auto remaining = static_cast(ftell(file)); 61 | if (fseek(file, static_cast(_offset), SEEK_SET) != 0u) 62 | { 63 | return false; 64 | } 65 | 66 | if (_length <= remaining) 67 | { 68 | _length = _length == 0 ? remaining : _length; 69 | 70 | if (_length % sizeof(sgt_uint32_t) != 0u) 71 | { 72 | return false; 73 | } 74 | 75 | auto count = _length / sizeof(sgt_uint32_t); 76 | if (m_buffer.resize(count) == false) 77 | { 78 | return false; 79 | } 80 | 81 | return fread(m_buffer.data(), sizeof(sgt_uint32_t), count, file) == count; 82 | } 83 | } 84 | 85 | return false; 86 | } -------------------------------------------------------------------------------- /common/source/BinaryFileWriter.cpp: -------------------------------------------------------------------------------- 1 | #include "common/BinaryFileWriter.h" 2 | #include 3 | 4 | spvgentwo::BinaryFileWriter::BinaryFileWriter(IAllocator& _allocator, const char* _path, sgt_size_t _initialBufferSize) : 5 | m_buffer(&_allocator, _initialBufferSize) 6 | { 7 | open(_path); 8 | } 9 | 10 | spvgentwo::BinaryFileWriter::~BinaryFileWriter() 11 | { 12 | close(); 13 | } 14 | 15 | bool spvgentwo::BinaryFileWriter::put(unsigned int _word) 16 | { 17 | m_buffer.emplace_back(_word); 18 | return true; 19 | } 20 | 21 | bool spvgentwo::BinaryFileWriter::open(const char* _path) 22 | { 23 | if (m_pFile != nullptr || _path == nullptr) 24 | { 25 | return false; 26 | } 27 | 28 | #ifdef _CRT_INSECURE_DEPRECATE 29 | FILE* file = nullptr; 30 | if (fopen_s(&file, _path, "wb") == 0) 31 | { 32 | m_pFile = file; 33 | } 34 | #else 35 | m_pFile = fopen(_path, "wb"); 36 | #endif 37 | 38 | return m_pFile != nullptr; 39 | } 40 | 41 | void spvgentwo::BinaryFileWriter::close() 42 | { 43 | if (m_pFile != nullptr) 44 | { 45 | if (m_buffer.empty() == false) 46 | { 47 | fwrite(m_buffer.data(), sizeof(sgt_uint32_t), m_buffer.size(), static_cast(m_pFile)); 48 | } 49 | 50 | fclose(static_cast(m_pFile)); 51 | m_pFile = nullptr; 52 | } 53 | m_buffer.clear(); 54 | } -------------------------------------------------------------------------------- /common/source/ConsoleLogger.cpp: -------------------------------------------------------------------------------- 1 | #include "common/ConsoleLogger.h" 2 | #include 3 | #include 4 | 5 | namespace 6 | { 7 | void LogImpl( [[maybe_unused]] spvgentwo::ILogger* _pInstance, spvgentwo::LogLevel _level, const char* _pFormat, ... ) 8 | { 9 | switch( _level ) 10 | { 11 | case spvgentwo::LogLevel::Debug: 12 | printf( "Debug: " ); break; 13 | case spvgentwo::LogLevel::Info: 14 | printf( "Info: " ); break; 15 | case spvgentwo::LogLevel::Warning: 16 | printf( "Warning: " ); break; 17 | case spvgentwo::LogLevel::Error: 18 | printf( "Error: " ); break; 19 | case spvgentwo::LogLevel::Fatal: 20 | printf( "Fatal: " ); break; 21 | default: 22 | break; 23 | } 24 | 25 | va_list args; 26 | va_start( args, _pFormat ); 27 | vprintf( _pFormat, args ); 28 | va_end( args ); 29 | printf( "\n" ); 30 | } 31 | } 32 | 33 | spvgentwo::ConsoleLogger::ConsoleLogger( Flag _filter ) : ILogger( LogImpl, _filter ) 34 | { 35 | } 36 | -------------------------------------------------------------------------------- /common/source/ExternalMemoryAllocator.cpp: -------------------------------------------------------------------------------- 1 | #include "common/ExternalMemoryAllocator.h" 2 | 3 | void* spvgentwo::ExternalMemoryAllocator::allocate(sgt_size_t _bytes, unsigned int _aligment) 4 | { 5 | const auto capacity = static_cast(static_cast(m_pEnd) - static_cast(m_pCurrent)); 6 | void* ptr = alignPowerOf2(_aligment, _bytes, m_pCurrent, capacity); 7 | m_pCurrent = static_cast(ptr) + _bytes; 8 | return ptr; 9 | } 10 | 11 | void spvgentwo::ExternalMemoryAllocator::deallocate(void* _ptr, const sgt_size_t _bytes) 12 | { 13 | // _ptr was the previous allocation 14 | if (static_cast(_ptr) + _bytes == m_pCurrent) 15 | { 16 | m_pCurrent = _ptr; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /common/source/FunctionCallGraph.cpp: -------------------------------------------------------------------------------- 1 | #include "common/FunctionCallGraph.h" 2 | 3 | #include "spvgentwo/Function.h" 4 | 5 | spvgentwo::FunctionCallGraph::FunctionCallGraph(const Function& _func, IAllocator* _pAllocator) : 6 | Graph(_pAllocator != nullptr ? _pAllocator : _func.getAllocator()) 7 | { 8 | add(_func); 9 | } 10 | 11 | void spvgentwo::FunctionCallGraph::add(const Function& _func) 12 | { 13 | Function* srcFunc = _func.getFunction()->getFunction(); // use OpFunction to get non-const Function 14 | auto srcIt = this->m_nodes.find_if([&srcFunc](NodeType& n) { return n.data() == srcFunc; }); 15 | NodeType* src = srcIt == this->m_nodes.end() ? this->emplace(srcFunc) : srcIt.operator->(); 16 | 17 | for (BasicBlock& bb : _func) 18 | { 19 | for (Instruction& instr : bb) 20 | { 21 | if (instr == spv::Op::OpFunctionCall) 22 | { 23 | if (auto it = instr.getFirstActualOperand(); it != nullptr && it->isInstruction()) 24 | { 25 | Function* dstFunc = it->getInstruction()->getFunction(); 26 | auto dstIt = this->m_nodes.find_if([&dstFunc](NodeType& n) { return n.data() == dstFunc; }); 27 | 28 | NodeType* dst = nullptr; 29 | if (dstIt == this->m_nodes.end()) 30 | { 31 | dst = this->emplace(dstFunc); 32 | add(*dstFunc); 33 | } 34 | else 35 | { 36 | dst = dstIt.operator->(); 37 | } 38 | 39 | src->connect(dst, &instr); 40 | } 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /common/source/GrammarHelper.cpp: -------------------------------------------------------------------------------- 1 | #include "common/GrammarHelper.h" 2 | 3 | namespace 4 | { 5 | bool stringCmp(const char* _pLeft, const char* _pRight) 6 | { 7 | if (_pLeft == nullptr || _pRight == nullptr) 8 | { 9 | return false; 10 | } 11 | 12 | while (*_pLeft != '\0' && *_pRight != '\0') 13 | { 14 | if (*_pLeft != *_pRight) 15 | { 16 | return false; 17 | } 18 | 19 | ++_pLeft; 20 | ++_pRight; 21 | } 22 | 23 | return true; 24 | } 25 | } 26 | 27 | spvgentwo::Instruction::Iterator spvgentwo::GrammarHelper::getFirstOperandOfKind(const Instruction* _pInstruction, const Grammar& _gram, Grammar::OperandKind _kind, Grammar::Extension _ext) 28 | { 29 | if (auto* info = _gram.getInfo(static_cast(_pInstruction->getOperation()), _ext); info != nullptr) 30 | { 31 | auto opIt = info->operands.begin(); 32 | 33 | for (auto it = _pInstruction->begin(); it != nullptr && opIt != nullptr; ) 34 | { 35 | if (opIt->kind == _kind) 36 | { 37 | return it; 38 | } 39 | 40 | ++it; 41 | ++opIt; 42 | } 43 | } 44 | 45 | return nullptr; 46 | } 47 | 48 | spvgentwo::Instruction::Iterator spvgentwo::GrammarHelper::getOperandByName(const Instruction* _pInstruction, const Grammar& _gram, const char* _pName, Grammar::Extension _ext) 49 | { 50 | if (auto* info = _gram.getInfo(static_cast(_pInstruction->getOperation()), _ext); info != nullptr) 51 | { 52 | auto opIt = info->operands.begin(); 53 | 54 | for (auto it = _pInstruction->begin(); it != nullptr && opIt != nullptr; ) 55 | { 56 | if (stringCmp(_pName, opIt->name)) 57 | { 58 | return it; 59 | } 60 | 61 | ++it; 62 | ++opIt; 63 | } 64 | } 65 | 66 | return nullptr; 67 | } 68 | -------------------------------------------------------------------------------- /common/source/HeapAllocator.cpp: -------------------------------------------------------------------------------- 1 | #include "common/HeapAllocator.h" 2 | 3 | #ifdef SPVGENTWO_DEBUG_HEAP_ALLOC 4 | #define _CRTDBG_MAP_ALLOC 5 | #include 6 | #endif 7 | 8 | void* spvgentwo::HeapAllocator::allocate(sgt_size_t _bytes, [[maybe_unused]] unsigned int _alignment) 9 | { 10 | m_Allocated += _bytes; 11 | return new char[_bytes]; 12 | } 13 | 14 | void spvgentwo::HeapAllocator::deallocate(void* _ptr, sgt_size_t _bytes) 15 | { 16 | m_Deallocated += _bytes; 17 | 18 | delete[] (char*) _ptr; 19 | } 20 | 21 | spvgentwo::HeapAllocator::HeapAllocator() 22 | { 23 | #ifdef SPVGENTWO_DEBUG_HEAP_ALLOC 24 | static bool heapDbgInitialized = false; 25 | if( heapDbgInitialized == false ) 26 | { 27 | _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); 28 | heapDbgInitialized = true; 29 | } 30 | #endif 31 | } 32 | 33 | spvgentwo::HeapAllocator* spvgentwo::HeapAllocator::instance() 34 | { 35 | static HeapAllocator alloc; 36 | return &alloc; 37 | } 38 | 39 | void spvgentwo::HeapAllocator::setBreakAlloc( [[maybe_unused]] long alloc ) 40 | { 41 | #ifdef SPVGENTWO_DEBUG_HEAP_ALLOC 42 | _CrtSetBreakAlloc(alloc); 43 | #endif 44 | } 45 | -------------------------------------------------------------------------------- /common/source/ReflectionHelper.cpp: -------------------------------------------------------------------------------- 1 | #include "common/ReflectionHelper.h" 2 | #include "common/ReflectionHelperTemplate.inl" 3 | 4 | #include "spvgentwo/Module.h" 5 | 6 | bool spvgentwo::ReflectionHelper::getLocalSize(const Module& _module, unsigned int& _x, unsigned int& _y, unsigned int& _z) 7 | { 8 | for (const auto& mode : _module.getExecutionModes()) 9 | { 10 | if (auto it = mode.begin().next(); it != nullptr && mode.size() > 4u) 11 | { 12 | if (mode == spv::Op::OpExecutionMode && (*it == spv::ExecutionMode::LocalSize || *it == spv::ExecutionMode::LocalSizeHint)) 13 | { 14 | _x = (++it)->getLiteral(); 15 | _y = (++it)->getLiteral(); 16 | _z = (++it)->getLiteral(); 17 | 18 | return true; 19 | } 20 | else if (mode == spv::Op::OpExecutionModeId && (*it == spv::ExecutionMode::LocalSizeId || *it == spv::ExecutionMode::LocalSizeHintId)) 21 | { 22 | auto getValue = [](const Instruction* instr) -> unsigned int 23 | { 24 | if (instr == nullptr || instr->isSpecOrConstant() == false) 25 | return 0u; 26 | 27 | if(auto it = instr->getFirstActualOperand(); it->isLiteral() ) 28 | { 29 | return it->literal; 30 | } 31 | 32 | return 0u; 33 | }; 34 | 35 | _x = getValue((++it)->getInstruction()); 36 | _y = getValue((++it)->getInstruction()); 37 | _z = getValue((++it)->getInstruction()); 38 | 39 | return true; 40 | } 41 | } 42 | } 43 | 44 | return false; 45 | } 46 | 47 | void spvgentwo::ReflectionHelper::getGlobalVariablesByStorageClass(const Module& _module, spv::StorageClass _class, List& _outVariables) 48 | { 49 | for (const Instruction& var : _module.getGlobalVariables()) 50 | { 51 | if (var.getStorageClass() == _class) 52 | { 53 | _outVariables.emplace_back(&var); 54 | } 55 | } 56 | } 57 | 58 | void spvgentwo::ReflectionHelper::getVariablesWithDecoration(const Module& _module, spv::Decoration _decoration, List& _outTargets, const unsigned int* _pValue) 59 | { 60 | getVariablesWithDecorationFunc(_module, _decoration, [&_outTargets](const Instruction* _pTarget) {_outTargets.emplace_back(_pTarget); }, _pValue); 61 | } 62 | 63 | void spvgentwo::ReflectionHelper::getDecorations(const Instruction* _pTarget, List& _outDecorations) 64 | { 65 | if (_pTarget == nullptr || _pTarget->getModule() == nullptr) 66 | return; 67 | 68 | for(Instruction& decoration : _pTarget->getModule()->getDecorations()) 69 | { 70 | if (*decoration.getFirstActualOperand() == _pTarget) // check target 71 | { 72 | _outDecorations.emplace_back(&decoration); 73 | } 74 | } 75 | } 76 | 77 | spvgentwo::spv::Decoration spvgentwo::ReflectionHelper::getSpvDecorationKindFromDecoration(const Instruction* _pDecoration) 78 | { 79 | if (_pDecoration == nullptr) 80 | { 81 | return spv::Decoration::Max; 82 | } 83 | 84 | auto it = _pDecoration->getFirstActualOperand().next(); // skip target ID 85 | 86 | if (*_pDecoration == spv::Op::OpDecorate && it != nullptr) 87 | { 88 | return static_cast(it->getLiteral().value); 89 | } 90 | else if (*_pDecoration == spv::Op::OpMemberDecorate && it.next() != nullptr) 91 | { 92 | return static_cast(it.next()->getLiteral().value); // skip member index 93 | } 94 | 95 | return spv::Decoration::Max; 96 | } 97 | 98 | bool spvgentwo::ReflectionHelper::getSpvDecorationAndLiteralFromDecoration(const Instruction* _pInDecoration, spv::Decoration& _outDecoration, unsigned int& _outValue) 99 | { 100 | if (_pInDecoration == nullptr || (*_pInDecoration != spv::Op::OpDecorate && *_pInDecoration == spv::Op::OpMemberDecorate)) 101 | { 102 | return false; 103 | } 104 | 105 | auto it = _pInDecoration->getFirstActualOperand().next(); // skip target ID 106 | 107 | if (*_pInDecoration == spv::Op::OpMemberDecorate ) 108 | { 109 | ++it; // skip member index 110 | } 111 | 112 | if (it != nullptr) 113 | { 114 | _outDecoration = static_cast(it->getLiteral().value); 115 | 116 | if (it.next() != nullptr) 117 | { 118 | _outValue = it.next()->getLiteral(); 119 | } 120 | 121 | return true; 122 | } 123 | 124 | return false; 125 | } 126 | 127 | unsigned int spvgentwo::ReflectionHelper::getLiteralFromDecoration(spv::Decoration _decoration, const Instruction* _pDecoration) 128 | { 129 | spv::Decoration deco{}; 130 | unsigned int value = sgt_uint32_max; 131 | 132 | if (getSpvDecorationAndLiteralFromDecoration(_pDecoration, deco, value) == false || deco != _decoration) 133 | { 134 | return sgt_uint32_max; 135 | } 136 | 137 | return value; 138 | } 139 | 140 | unsigned int spvgentwo::ReflectionHelper::getDecorationLiteralFromTarget(spv::Decoration _decoration, const Instruction* _pTarget, const Instruction** _pOutDecoration) 141 | { 142 | if (_pTarget == nullptr || _pTarget->getModule() == nullptr) 143 | return sgt_uint32_max; 144 | 145 | for (const Instruction& decoration : _pTarget->getModule()->getDecorations()) 146 | { 147 | auto target = decoration.getFirstActualOperand(); 148 | 149 | if (target != nullptr && *target == _pTarget) 150 | { 151 | if (decoration == spv::Op::OpMemberDecorate) 152 | { 153 | target = target.next(); // skip member index 154 | } 155 | 156 | if (auto deco = target.next(); deco != nullptr && deco.next() != nullptr && deco->getLiteral() == static_cast(_decoration)) 157 | { 158 | if (_pOutDecoration != nullptr) 159 | { 160 | *_pOutDecoration = &decoration; 161 | } 162 | 163 | return deco.next()->getLiteral(); 164 | } 165 | } 166 | } 167 | 168 | return sgt_uint32_max; 169 | } 170 | -------------------------------------------------------------------------------- /common/source/TypeHelper.cpp: -------------------------------------------------------------------------------- 1 | #include "common/TypeHelper.h" 2 | 3 | #include "spvgentwo/Type.h" 4 | #include "spvgentwo/String.h" 5 | #include "spvgentwo/Instruction.h" 6 | 7 | namespace 8 | { 9 | void getTypeName(spvgentwo::sgt_size_t _offset, unsigned int _indent, const spvgentwo::Type& _type, spvgentwo::String& _outName, const spvgentwo::Instruction* _pOpTypeInstr, unsigned int _memberIndex = spvgentwo::sgt_uint32_max) 10 | { 11 | using namespace spvgentwo; 12 | 13 | Instruction::Iterator op = nullptr; 14 | 15 | if (_pOpTypeInstr != nullptr) // get subtype if possible 16 | { 17 | if (_pOpTypeInstr->isType()) 18 | { 19 | op = _pOpTypeInstr->getFirstActualOperand(); 20 | } 21 | else if (_pOpTypeInstr->hasResultType()) 22 | { 23 | op = _pOpTypeInstr->getResultTypeInstr()->getFirstActualOperand(); 24 | } 25 | } 26 | 27 | auto instrName = [](const spvgentwo::Instruction* _instr, unsigned int _memberIdx = sgt_uint32_max) -> const char* 28 | { 29 | const char* name = nullptr; 30 | 31 | if (_instr != nullptr) 32 | { 33 | if (name = _instr->getName(_memberIdx); name == nullptr && _instr->hasResultType()) 34 | { 35 | if (const Instruction* t = _instr->getResultTypeInstr(); t != nullptr) 36 | { 37 | name = t->getName(_memberIdx); 38 | } 39 | } 40 | } 41 | 42 | return name; 43 | }; 44 | 45 | // string 46 | auto insert = [&](const char* str) 47 | { 48 | auto prev = _outName.size(); 49 | _outName.insert(_offset, str); 50 | auto len = _outName.size() - prev; 51 | _offset += len; 52 | }; 53 | 54 | // type 55 | auto insertSubType = [&_outName, &_offset](const Type& _t, unsigned int _indent, const spvgentwo::Instruction* _pInstr, unsigned int _memberIdx = sgt_uint32_max) 56 | { 57 | auto prev = _outName.size(); 58 | getTypeName(_offset, _indent, _t, _outName, _pInstr, _memberIdx); 59 | auto len = _outName.size() - prev; 60 | _offset += len; 61 | }; 62 | 63 | auto indent = [&insert](unsigned int indent) 64 | { 65 | for(auto i = 0u; i < indent; ++i) 66 | { 67 | insert("\t"); 68 | } 69 | }; 70 | 71 | if (_type.isStruct()) 72 | { 73 | insert("struct "); 74 | 75 | if (const char* name = instrName(_pOpTypeInstr); name != nullptr) 76 | { 77 | insert(name); 78 | } 79 | 80 | insert(" {\n"); 81 | 82 | for (const Type& t : _type) 83 | { 84 | indent(_indent+1u); 85 | insertSubType(t, _indent + 1u, op != nullptr ? op->getInstruction() : nullptr, _memberIndex++); 86 | if (t.isStruct() == false) 87 | { 88 | insert(";\n"); 89 | } 90 | 91 | ++op; 92 | } 93 | 94 | indent(_indent); 95 | insert("};\n"); 96 | } 97 | else if (_type.isFunction()) 98 | { 99 | auto it = _type.begin(); 100 | 101 | // return type 102 | insertSubType(*it, 0u, op != nullptr ? op->getInstruction() : nullptr); 103 | 104 | if (const char* name = instrName(_pOpTypeInstr); name != nullptr) 105 | { 106 | insert(" "); // func name 107 | insert(name); 108 | } 109 | 110 | // parameters 111 | insert("("); 112 | 113 | while (++it != _type.end()) 114 | { 115 | ++op; 116 | 117 | // param name 118 | if (const char* name = instrName(op != nullptr ? op->getInstruction() : nullptr); name != nullptr) 119 | { 120 | insert(name); 121 | } 122 | else // parameter type 123 | { 124 | insertSubType(*it, 0u, op != nullptr ? op->getInstruction() : nullptr); 125 | } 126 | 127 | if (it.next() != nullptr) 128 | { 129 | insert(", "); 130 | } 131 | } 132 | 133 | insert(")"); 134 | } 135 | else if (_type.isPointer()) 136 | { 137 | insertSubType(_type.front(), 0u, op != nullptr ? op->getInstruction() : nullptr); 138 | insert("*"); 139 | 140 | if (const char* name = instrName(_pOpTypeInstr); name != nullptr) 141 | { 142 | insert(" "); 143 | insert(name); 144 | } 145 | } 146 | else if (_type.isArray()) 147 | { 148 | insertSubType(_type.front(), 0u, _pOpTypeInstr); // or pass subtype instr? 149 | 150 | if (const char* name = instrName(_pOpTypeInstr); name != nullptr) 151 | { 152 | insert(name); 153 | } 154 | 155 | insert("["); 156 | 157 | char num[11]{ '\0' }; 158 | uintToString(_type.getArrayLength(), num, 10u, 10u); 159 | insert(num); 160 | 161 | insert("]"); 162 | } 163 | else if (const char* name = instrName(_pOpTypeInstr, _memberIndex); name != nullptr) 164 | { 165 | insert(name); 166 | } 167 | else if(const char* fundamentalName = _type.getString(); fundamentalName != nullptr) 168 | { 169 | insert(fundamentalName); 170 | } 171 | } 172 | } // anon 173 | 174 | void spvgentwo::TypeHelper::getTypeName(const Type& _type, String& _outName, const Instruction* _pOpTypeInstr) 175 | { 176 | ::getTypeName(0u, 0u, _type, _outName, _pOpTypeInstr); 177 | } -------------------------------------------------------------------------------- /dis/include/dis/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rAzoR8/SpvGenTwo/4bdddca967fb01311076c67ad8dce9a6d6ef7730/dis/include/dis/.gitkeep -------------------------------------------------------------------------------- /dis/source/dis.cpp: -------------------------------------------------------------------------------- 1 | #include "spvgentwo/Logger.h" 2 | #include "spvgentwo/Module.h" 3 | #include "spvgentwo/Grammar.h" 4 | #include "common/HeapAllocator.h" 5 | #include "common/BinaryFileWriter.h" 6 | #include "common/BinaryFileReader.h" 7 | #include "common/ConsoleLogger.h" 8 | #include "common/ModulePrinter.h" 9 | 10 | #include 11 | #include 12 | 13 | using namespace spvgentwo; 14 | using namespace ModulePrinter; 15 | 16 | int main(int argc, char* argv[]) 17 | { 18 | ConsoleLogger logger; 19 | 20 | logger.logInfo("SpvGenTwoDisassembler by Fabian Wahlster - https://github.com/rAzoR8/SpvGenTwo"); 21 | 22 | const char* spv = nullptr; 23 | const char* tabs = "\t\t"; 24 | bool serialize = false; // for debugging 25 | bool reassignIDs = false; 26 | bool colors = false; 27 | 28 | PrintOptions options{ PrintOptionsBits::All }; 29 | 30 | for (int i = 1u; i < argc; ++i) 31 | { 32 | const char* arg = argv[i]; 33 | if (spv == nullptr) 34 | { 35 | spv = arg; 36 | } 37 | else if (strcmp(arg, "-serialize") == 0) 38 | { 39 | serialize = true; 40 | } 41 | else if (strcmp(arg, "-assignIDs") == 0 || strcmp(arg, "-assignids") == 0) 42 | { 43 | reassignIDs = true; 44 | } 45 | else if (strcmp(arg, "-noinstrnames") == 0) 46 | { 47 | options ^= PrintOptionsBits::InstructionName; 48 | } 49 | else if (strcmp(arg, "-noopnames") == 0) 50 | { 51 | options ^= PrintOptionsBits::OperandName; 52 | } 53 | else if (strcmp(arg, "-nopreamble") == 0) 54 | { 55 | options ^= PrintOptionsBits::Preamble; 56 | } 57 | else if (strcmp(arg, "-colors") == 0) 58 | { 59 | colors = true; 60 | } 61 | else if (i+1 < argc && strcmp(arg, "-tabs") == 0) 62 | { 63 | tabs = argv[++i]; 64 | } 65 | } 66 | 67 | if (spv == nullptr) 68 | { 69 | return -1; 70 | } 71 | 72 | HeapAllocator alloc; 73 | 74 | if (BinaryFileReader reader(alloc, spv); reader) 75 | { 76 | Module module(&alloc, &logger); 77 | Grammar gram(&alloc); 78 | 79 | // parse the binary instructions & operands 80 | if (module.readAndInit(reader, gram) == false) 81 | { 82 | return -1; 83 | } 84 | 85 | if (reassignIDs) 86 | { 87 | module.assignIDs(); // compact ids 88 | } 89 | 90 | auto printer = ModulePrinter::ModuleSimpleFuncPrinter([](const char* _pStr) { printf("%s", _pStr); }, colors); 91 | const bool success = ModulePrinter::printModule(module, gram, printer, options, tabs); 92 | 93 | if (success == false) 94 | { 95 | return -1; 96 | } 97 | 98 | if (serialize) 99 | { 100 | if (BinaryFileWriter writer(alloc, "serialized.spv"); writer.isOpen()) 101 | { 102 | module.finalizeAndWrite(writer); 103 | } 104 | } 105 | } 106 | else 107 | { 108 | logger.logError("Failed to open %s", spv); 109 | } 110 | 111 | return 0; 112 | } -------------------------------------------------------------------------------- /dis/test/constants.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rAzoR8/SpvGenTwo/4bdddca967fb01311076c67ad8dce9a6d6ef7730/dis/test/constants.spv -------------------------------------------------------------------------------- /dis/test/controlFlow.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rAzoR8/SpvGenTwo/4bdddca967fb01311076c67ad8dce9a6d6ef7730/dis/test/controlFlow.spv -------------------------------------------------------------------------------- /dis/test/expressionGraph.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rAzoR8/SpvGenTwo/4bdddca967fb01311076c67ad8dce9a6d6ef7730/dis/test/expressionGraph.spv -------------------------------------------------------------------------------- /dis/test/extensions.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rAzoR8/SpvGenTwo/4bdddca967fb01311076c67ad8dce9a6d6ef7730/dis/test/extensions.spv -------------------------------------------------------------------------------- /dis/test/functionCall.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rAzoR8/SpvGenTwo/4bdddca967fb01311076c67ad8dce9a6d6ef7730/dis/test/functionCall.spv -------------------------------------------------------------------------------- /dis/test/oldInstrTest.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rAzoR8/SpvGenTwo/4bdddca967fb01311076c67ad8dce9a6d6ef7730/dis/test/oldInstrTest.spv -------------------------------------------------------------------------------- /dis/test/test.bat: -------------------------------------------------------------------------------- 1 | for %%f in (*.spv*) do ( 2 | SpvGenTwoDisassembler.exe %%f --assignids 3 | IF %ERRORLEVEL% NEQ 0 ( 4 | pause 5 | ) 6 | ) 7 | 8 | pause -------------------------------------------------------------------------------- /lib/include/spvgentwo/Allocator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "stdreplacement.h" 3 | 4 | namespace spvgentwo 5 | { 6 | // forward decl 7 | class IAllocator; 8 | 9 | class ScopedAllocation 10 | { 11 | friend class IAllocator; 12 | explicit constexpr ScopedAllocation(void* _ptr, sgt_size_t _size, IAllocator* _pAllocator) noexcept : 13 | allocator{_pAllocator}, 14 | ptr{_ptr}, 15 | size{_size} 16 | {} 17 | 18 | IAllocator* allocator; 19 | 20 | public: 21 | ~ScopedAllocation(); 22 | 23 | ScopedAllocation(const ScopedAllocation&) = delete; 24 | ScopedAllocation& operator=(const ScopedAllocation&) = delete; 25 | 26 | constexpr ScopedAllocation(ScopedAllocation&& _other) noexcept : 27 | allocator{_other.allocator}, 28 | ptr{_other.ptr}, 29 | size{_other.size} 30 | { 31 | _other.allocator = nullptr; 32 | } 33 | 34 | constexpr operator bool() const { return ptr != nullptr && allocator != nullptr; } 35 | 36 | void* const ptr; 37 | const sgt_size_t size; 38 | }; 39 | 40 | class IAllocator 41 | { 42 | public: 43 | virtual ~IAllocator() = default; 44 | 45 | // alignment may only be a power of 2 46 | // the library has no alignment requirement so implementations are free to use the hint or return unaligned pointers 47 | [[nodiscard]] virtual void* allocate(sgt_size_t _bytes, unsigned int _alignmentHint) = 0; 48 | virtual void deallocate(void* _ptr, sgt_size_t _bytes = 0u) = 0; 49 | 50 | [[nodiscard]] ScopedAllocation allocateScoped(sgt_size_t _bytes, unsigned int _alignment); 51 | 52 | template 53 | T* construct(Args&& ..._args) noexcept 54 | { 55 | T* pData = reinterpret_cast(allocate(sizeof(T), alignof(T))); 56 | if (pData != nullptr) 57 | { 58 | new(pData) T{ stdrep::forward(_args)... }; 59 | } 60 | return pData; 61 | } 62 | 63 | template 64 | void destruct(T* _ptr) 65 | { 66 | _ptr->~T(); 67 | deallocate(_ptr, sizeof(T)); 68 | } 69 | 70 | static inline void* alignPowerOf2(sgt_size_t _alignment, sgt_size_t _size, void*& _ptr, sgt_size_t _space) noexcept 71 | { 72 | static_assert(sizeof(sgt_size_t) == sizeof(void*), "System architecture not supported"); 73 | 74 | sgt_size_t offset = static_cast(reinterpret_cast(_ptr) & (_alignment - 1)); 75 | if (offset != 0) 76 | { 77 | offset = _alignment - offset; 78 | } 79 | 80 | if (_space < offset || _space - offset < _size) 81 | { 82 | return nullptr; 83 | } 84 | 85 | _ptr = static_cast(_ptr) + offset; 86 | return _ptr; 87 | } 88 | }; 89 | } // spvgentwo 90 | -------------------------------------------------------------------------------- /lib/include/spvgentwo/BasicBlock.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Instruction.h" 3 | 4 | namespace spvgentwo 5 | { 6 | // forward delcs 7 | class Module; 8 | class Function; 9 | class IAllocator; 10 | class IWriter; 11 | class GLSL450Intruction; 12 | 13 | class BasicBlock : public List 14 | { 15 | friend class Module; 16 | friend class Function; 17 | private: 18 | 19 | Function* m_pFunction = nullptr; // parent 20 | Instruction m_Label; 21 | 22 | public: 23 | BasicBlock() = default; 24 | 25 | BasicBlock(Function* _pFunction, const char* _pName = nullptr); 26 | BasicBlock(Function* _pFunction, BasicBlock&& _other) noexcept; 27 | BasicBlock(const BasicBlock&) = delete; 28 | 29 | virtual ~BasicBlock(); 30 | 31 | BasicBlock& operator=(BasicBlock&& _other) noexcept; 32 | BasicBlock& operator=(const BasicBlock& _other) = delete; 33 | 34 | Function* getFunction() const { return m_pFunction; } 35 | Module* getModule() const; 36 | 37 | const char* getName() const; 38 | 39 | IAllocator* getAllocator() const; 40 | 41 | Instruction* getLabel() { return &m_Label; } 42 | const Instruction* getLabel() const { return &m_Label; } 43 | 44 | // get last instruction 45 | Instruction* getTerminator() const; 46 | 47 | // returns a list of BasicBlock branch targets of this blocks terminator 48 | bool getBranchTargets(List& _outTargetBlocks) const; 49 | 50 | // manual instruction add 51 | Instruction* addInstruction(); 52 | 53 | Instruction* operator->() { return &emplace_back(this, spv::Op::OpNop); } 54 | 55 | template 56 | ExtInstr* ext() { return reinterpret_cast(addInstruction()); } 57 | 58 | // call ext() 59 | GLSL450Intruction* glsl(); 60 | 61 | // set return value of this block (used in function), returns opReturn/opReturnValue instruction 62 | Instruction* returnValue(Instruction* _pValue = nullptr); 63 | 64 | // return last operation 65 | operator Instruction* () const { return m_pLast != nullptr ? m_pLast->operator->() : nullptr; }; 66 | 67 | void write(IWriter& _writer) const; 68 | 69 | bool read(IReader& _reader, const Grammar& _grammar); 70 | 71 | // remove instruction from this block (if it is in this block). OpLabel can't be removed. Returns true if the instruction was removed 72 | bool remove(const Instruction* _pInstr); 73 | 74 | // structured if, returns last instruction of MergeBlock which creats a result 75 | BasicBlock& If(Instruction* _pCondition, BasicBlock& _trueBlock, BasicBlock& _falseBlock, BasicBlock* _pMergeBlock = nullptr, const Flag _mask = spv::SelectionControlMask::MaskNone); 76 | 77 | // If without else block 78 | BasicBlock& If(Instruction* _pCondition, BasicBlock& _trueBlock, BasicBlock* _pMergeBlock = nullptr, const Flag _mask = spv::SelectionControlMask::MaskNone); 79 | 80 | // MergeBlock for selection merge is returned. If _pMergeBlock is nullptr, a new block will be added to the function 81 | template 82 | BasicBlock& If(Instruction* _pCondition, TrueFunc _true, FalseFunc _false, BasicBlock* _pMergeBlock = nullptr, const Flag _mask = spv::SelectionControlMask::MaskNone); 83 | 84 | template // If without else block 85 | BasicBlock& If(Instruction* _pCondition, TrueFunc _true, BasicBlock* _pMergeBlock = nullptr, const Flag _mask = spv::SelectionControlMask::MaskNone); 86 | 87 | BasicBlock& Loop(Instruction* _pCondition, BasicBlock& _continue, BasicBlock& _body, BasicBlock* _pMergeBlock = nullptr, const Flag _mask = spv::LoopControlMask::MaskNone); 88 | 89 | template 90 | BasicBlock& Loop(ConditionFunc _condition, ContinueFunc _continue, LoopBodyFunc _body, BasicBlock* _pMergeBlock = nullptr, const Flag _mask = spv::LoopControlMask::MaskNone); 91 | 92 | // infer op code from operands types, emplace instruction in this basic block 93 | BasicBlock& Add(Instruction* _pLeft, Instruction* _pRight) { addInstruction()->Add(_pLeft, _pRight); return *this; } 94 | BasicBlock& Sub(Instruction* _pLeft, Instruction* _pRight) { addInstruction()->Sub(_pLeft, _pRight); return *this; } 95 | BasicBlock& Mul(Instruction* _pLeft, Instruction* _pRight) { addInstruction()->Mul(_pLeft, _pRight); return *this; } 96 | BasicBlock& Div(Instruction* _pLeft, Instruction* _pRight) { addInstruction()->Div(_pLeft, _pRight); return *this; } 97 | 98 | BasicBlock& Not(Instruction* _pIntOrBool) { addInstruction()->Not(_pIntOrBool); return *this; } 99 | BasicBlock& Equal(Instruction* _pLeft, Instruction* _pRight) { addInstruction()->Equal(_pLeft, _pRight); return *this; } 100 | BasicBlock& NotEqual(Instruction* _pLeft, Instruction* _pRight) { addInstruction()->NotEqual(_pLeft, _pRight); return *this; } 101 | BasicBlock& Less(Instruction* _pLeft, Instruction* _pRight) { addInstruction()->Less(_pLeft, _pRight); return *this; } 102 | BasicBlock& LessEqual(Instruction* _pLeft, Instruction* _pRight) { addInstruction()->LessEqual(_pLeft, _pRight); return *this; } 103 | BasicBlock& Greater(Instruction* _pLeft, Instruction* _pRight) { addInstruction()->Greater(_pLeft, _pRight); return *this; } 104 | BasicBlock& GreaterEqual(Instruction* _pLeft, Instruction* _pRight) { addInstruction()->GreaterEqual(_pLeft, _pRight); return *this; } 105 | 106 | // add _pRight to last instruction in this basic block and push the result (stack like) to this basic block 107 | BasicBlock& Add(Instruction* _pRight) { return Add(&back(), _pRight); } 108 | BasicBlock& Sub(Instruction* _pRight) { return Sub(&back(), _pRight); } 109 | BasicBlock& Mul(Instruction* _pRight) { return Mul(&back(), _pRight); } 110 | BasicBlock& Div(Instruction* _pRight) { return Div(&back(), _pRight); } 111 | BasicBlock& Not() { return Not(&back()); } 112 | BasicBlock& Equal(Instruction* _pRight) { return Equal(&back(), _pRight); } 113 | BasicBlock& NotEqual(Instruction* _pRight) { return NotEqual(&back(), _pRight); } 114 | BasicBlock& Less(Instruction* _pRight) { return Less(&back(), _pRight); } 115 | BasicBlock& LessEqual(Instruction* _pRight) { return LessEqual(&back(), _pRight); } 116 | BasicBlock& Greater(Instruction* _pRight) { return Greater(&back(), _pRight); } 117 | BasicBlock& GreaterEqual(Instruction* _pRight) { return GreaterEqual(&back(), _pRight); } 118 | }; 119 | } // !spvgentwo -------------------------------------------------------------------------------- /lib/include/spvgentwo/BasicBlockTemplate.inl: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace spvgentwo 4 | { 5 | template 6 | inline BasicBlock& BasicBlock::If(Instruction* _pCondition, TrueFunc _true, FalseFunc _false, BasicBlock* _pMergeBlock, const Flag < spv::SelectionControlMask> _mask) 7 | { 8 | static_assert(traits::is_invocable_v, "TrueFunc _true is not invocable: _true(BasicBlock& trueBranchBB)"); 9 | static_assert(traits::is_invocable_v, "FalseFunc _false is not invocable: _true(BasicBlock& falseBranchBB)"); 10 | 11 | BasicBlock& trueBB = m_pFunction->addBasicBlock("IfTrue"); 12 | BasicBlock& falseBB = m_pFunction->addBasicBlock("IfFalse"); 13 | BasicBlock& mergeBB = _pMergeBlock != nullptr ? *_pMergeBlock : m_pFunction->addBasicBlock("IfMerge"); 14 | 15 | addInstruction()->opSelectionMerge(&mergeBB, _mask); 16 | addInstruction()->opBranchConditional(_pCondition, &trueBB, &falseBB); 17 | 18 | _true(trueBB); 19 | _false(falseBB); 20 | 21 | // check if user didnt exit controlflow via kill or similar 22 | if (trueBB.empty() == false && trueBB.back().isTerminator() == false) 23 | { 24 | trueBB->opBranch(&mergeBB); 25 | } 26 | if (falseBB.empty() == false && falseBB.back().isTerminator() == false) 27 | { 28 | falseBB->opBranch(&mergeBB); 29 | } 30 | 31 | return mergeBB; 32 | } 33 | 34 | template 35 | inline BasicBlock& BasicBlock::If(Instruction* _pCondition, TrueFunc _true, BasicBlock* _pMergeBlock, const Flag _mask) 36 | { 37 | static_assert(traits::is_invocable_v, "TrueFunc _true is not invocable: _true(BasicBlock& trueBranchBB)"); 38 | 39 | BasicBlock& trueBB = m_pFunction->addBasicBlock("IfTrue"); 40 | BasicBlock& mergeBB = _pMergeBlock != nullptr ? *_pMergeBlock : m_pFunction->addBasicBlock("IfMerge"); 41 | 42 | addInstruction()->opSelectionMerge(&mergeBB, _mask); 43 | addInstruction()->opBranchConditional(_pCondition, &trueBB, &mergeBB); 44 | 45 | _true(trueBB); 46 | 47 | // check if user didnt exit controlflow via kill or similar 48 | if (trueBB.empty() == false && trueBB.back().isTerminator() == false) 49 | { 50 | trueBB->opBranch(&mergeBB); 51 | } 52 | 53 | return mergeBB; 54 | } 55 | 56 | // ContinueFunc is increment func 57 | template 58 | inline BasicBlock& BasicBlock::Loop(ConditionFunc _condition, ContinueFunc _continue, LoopBodyFunc _body, BasicBlock* _pMergeBlock, const Flag _mask) 59 | { 60 | static_assert(traits::is_invocable_v, "ConditionFunc _condition is not invocable: _condition(BasicBlock& condBB)"); 61 | static_assert(traits::is_invocable_v, "ContinueFunc _continue is not invocable: _continue(BasicBlock& continueBB)"); 62 | static_assert(traits::is_invocable_v, "LoopBodyFunc _body is not invocable: _body(BasicBlock& bodyBB)"); 63 | 64 | BasicBlock& loop = m_pFunction->addBasicBlock("LoopEntry"); 65 | addInstruction()->opBranch(&loop); 66 | 67 | BasicBlock& condBB = m_pFunction->addBasicBlock("LoopCondition"); 68 | BasicBlock& bodyBB = m_pFunction->addBasicBlock("LoopBody"); 69 | BasicBlock& continueBB = m_pFunction->addBasicBlock("LoopContinue"); 70 | BasicBlock& mergeBB = _pMergeBlock != nullptr ? *_pMergeBlock : m_pFunction->addBasicBlock("LoopMerge"); 71 | 72 | loop->opLoopMerge(&mergeBB, &continueBB, _mask); 73 | loop->opBranch(&condBB); 74 | 75 | static_assert(stdrep::is_same_v, "ConditionFunc _condition needs to return a Instruction*"); 76 | 77 | Instruction* pCond = _condition(condBB); 78 | // take last instruction of condBB as conditional 79 | condBB->opBranchConditional(pCond, &bodyBB, &mergeBB); 80 | 81 | _body(bodyBB); 82 | bodyBB->opBranch(&continueBB); // branch to "increment" / continue block 83 | 84 | _continue(continueBB); 85 | continueBB->opBranch(&loop); // back edge to loop merge 86 | 87 | return mergeBB; 88 | } 89 | } // !spvgentwo -------------------------------------------------------------------------------- /lib/include/spvgentwo/Constant.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Type.h" 4 | #include "Operand.h" // for appendLiteralsToContainer 5 | #include "Vector.h" 6 | 7 | namespace spvgentwo 8 | { 9 | class Constant 10 | { 11 | public: 12 | Constant(IAllocator* _pAllocator = nullptr); 13 | Constant(const Constant& _other); 14 | Constant(Constant&& _other) noexcept; 15 | ~Constant(); 16 | 17 | Constant& operator=(const Constant& _other); 18 | Constant& operator=(Constant&& _other) noexcept; 19 | 20 | constexpr spv::Op getOperation() const { return m_Operation; } 21 | void setOperation(const spv::Op _op) { m_Operation = _op; } 22 | constexpr const Type& getType() const { return m_Type; } 23 | constexpr Type& getType() { return m_Type; } 24 | 25 | template 26 | Type& setType(); 27 | 28 | constexpr const Vector& getData() const { return m_literalData; } 29 | constexpr Vector& getData() { return m_literalData; } 30 | 31 | template 32 | void addData(const T& _data); 33 | 34 | constexpr const List& getComponents() const { return m_Components; } 35 | constexpr List& getComponents() { return m_Components; } 36 | 37 | template 38 | Constant& make(const T& _value, const bool _spec = false); 39 | 40 | // adds a new constituent constant 41 | Constant& Component(); 42 | 43 | void reset(); 44 | 45 | // get top-level data (would be empty for structs) 46 | template 47 | const T* getDataAs(bool _checkType = true) const; 48 | 49 | private: 50 | spv::Op m_Operation = spv::Op::OpConstantNull; 51 | Type m_Type; 52 | 53 | List m_Components; 54 | Vector m_literalData; 55 | }; 56 | 57 | template 58 | inline Type& Constant::setType() 59 | { 60 | return m_Type.make(); 61 | } 62 | 63 | template 64 | inline void Constant::addData(const T& _data) 65 | { 66 | m_literalData.reserve(m_literalData.size() + wordCount()); 67 | appendLiteralsToContainer(m_literalData, _data); 68 | } 69 | 70 | template 71 | Constant& Constant::make(const T& _value, const bool _spec) 72 | { 73 | if constexpr (stdrep::is_same_v) 74 | { 75 | m_Operation = _value ? (_spec ? spv::Op::OpSpecConstantTrue : spv::Op::OpConstantTrue) : (_spec ? spv::Op::OpSpecConstantFalse : spv::Op::OpConstantFalse); 76 | m_Type.make(); 77 | addData(_value); 78 | } 79 | else if constexpr (traits::is_primitive_type_v>) 80 | { 81 | m_Operation = _spec ? spv::Op::OpSpecConstant : spv::Op::OpConstant; 82 | m_Type.make(); 83 | addData(_value); 84 | } 85 | else if constexpr (is_const_null_v) 86 | { 87 | m_Type.make(); 88 | m_Operation = spv::Op::OpConstantNull; // no literal data 89 | } 90 | else if constexpr (is_const_array_v) 91 | { 92 | m_Operation = _spec ? spv::Op::OpSpecConstantComposite : spv::Op::OpConstantComposite; 93 | m_Type.make(); 94 | for (unsigned int i = 0u; i < T::Elements; ++i) 95 | { 96 | Component().make(_value.data[i]); 97 | } 98 | } 99 | else if constexpr (is_const_vector_v) 100 | { 101 | m_Operation = _spec ? spv::Op::OpSpecConstantComposite : spv::Op::OpConstantComposite; 102 | m_Type.make(); 103 | for (unsigned int i = 0u; i < T::Elements; ++i) 104 | { 105 | Component().make(_value.data[i]); 106 | } 107 | } 108 | else if constexpr (is_const_matrix_v) 109 | { 110 | m_Operation = _spec ? spv::Op::OpSpecConstantComposite : spv::Op::OpConstantComposite; 111 | m_Type.make(); 112 | for (unsigned int i = 0u; i < T::Columns; ++i) 113 | { 114 | Component().make(_value.data[i]); 115 | } 116 | } 117 | else if constexpr(is_const_sampler_v) 118 | { 119 | m_Operation = spv::Op::OpConstantSampler; 120 | m_Type.make(); 121 | addData(_value.addressMode); 122 | addData(_value.coordMode); 123 | addData(_value.filterMode); 124 | } 125 | else 126 | { 127 | // required for clang which can't deduce that this code is unreachable for supported type instantiation 128 | constexpr bool match = 129 | stdrep::is_same_v || 130 | traits::is_primitive_type_v> || 131 | is_const_null_v || 132 | is_const_array_v || 133 | is_const_vector_v || 134 | is_const_matrix_v || 135 | is_const_sampler_v; 136 | 137 | static_assert(match, "Could not match type"); 138 | } 139 | 140 | return *this; 141 | } 142 | 143 | template 144 | inline const T* Constant::getDataAs(bool _checkType) const 145 | { 146 | if (sizeof(T) <= m_literalData.size() * sizeof(unsigned int)) 147 | { 148 | if (_checkType) 149 | { 150 | Type t(m_literalData.getAllocator()); 151 | if (t.make() != m_Type) 152 | { 153 | return nullptr; 154 | } 155 | } 156 | 157 | return reinterpret_cast(m_literalData.data()); 158 | } 159 | return nullptr; 160 | } 161 | 162 | template <> 163 | struct Hasher 164 | { 165 | constexpr Hash64 operator()(const Constant& _const, Hash64 _seed) const 166 | { 167 | FNV1aHasher hasher(_seed); 168 | return operator()(_const, hasher); 169 | } 170 | 171 | constexpr Hash64 operator()(const Constant& _const, FNV1aHasher& _hasher) const 172 | { 173 | _hasher << _const.getOperation(); 174 | Hasher()(_const.getType(), _hasher); 175 | for (const auto& l : _const.getData()) 176 | { 177 | _hasher << l; 178 | } 179 | 180 | for (const Constant& component : _const.getComponents()) 181 | { 182 | operator()(component, _hasher); 183 | } 184 | 185 | return _hasher; 186 | } 187 | }; 188 | } // !spvgentwo -------------------------------------------------------------------------------- /lib/include/spvgentwo/EntryIterator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Entry.h" 4 | 5 | namespace spvgentwo 6 | { 7 | template 8 | class EntryIterator 9 | { 10 | public: 11 | constexpr EntryIterator(Entry* _pEntry = nullptr) : m_pEntry(_pEntry) {} 12 | constexpr EntryIterator(const EntryIterator& _other) : m_pEntry(_other.m_pEntry) {} 13 | 14 | constexpr EntryIterator& operator=(const EntryIterator& _other); 15 | 16 | constexpr bool operator==(const EntryIterator& _other) const; 17 | constexpr bool operator!=(const EntryIterator& _other) const; 18 | 19 | constexpr bool operator==(sgt_nullptr_t) const; 20 | constexpr bool operator!=(sgt_nullptr_t) const; 21 | 22 | constexpr EntryIterator operator+(unsigned int n) const; 23 | constexpr EntryIterator operator-(unsigned int n) const; 24 | 25 | constexpr EntryIterator prev() const; 26 | constexpr EntryIterator next() const; 27 | 28 | // pre 29 | constexpr EntryIterator& operator++(); 30 | constexpr EntryIterator& operator--(); 31 | 32 | // post 33 | constexpr EntryIterator operator++(int); 34 | constexpr EntryIterator operator--(int); 35 | 36 | constexpr T& operator*() { return m_pEntry->inner(); } 37 | constexpr const T& operator*() const { return m_pEntry->inner(); } 38 | 39 | constexpr T* operator->() { return m_pEntry->operator->(); } 40 | constexpr const T* operator->() const { return m_pEntry->operator->(); } 41 | 42 | constexpr Entry* entry() const { return m_pEntry; } 43 | 44 | constexpr operator Entry* () const { return m_pEntry; } 45 | 46 | private: 47 | Entry* m_pEntry = nullptr; 48 | }; 49 | 50 | template 51 | struct Range 52 | { 53 | Iterator m_Begin; 54 | Iterator m_End; 55 | 56 | constexpr Iterator begin() const { return m_Begin; } 57 | constexpr Iterator end() const { return m_End; } 58 | 59 | constexpr bool empty() const { return m_Begin == m_End; } 60 | }; 61 | 62 | template 63 | inline constexpr EntryIterator& EntryIterator::operator=(const EntryIterator& _other) 64 | { 65 | m_pEntry = _other.m_pEntry; 66 | return *this; 67 | } 68 | 69 | template 70 | inline constexpr bool EntryIterator::operator==(const EntryIterator& _other) const 71 | { 72 | return m_pEntry == _other.m_pEntry; 73 | } 74 | 75 | template 76 | inline constexpr bool EntryIterator::operator!=(const EntryIterator& _other) const 77 | { 78 | return m_pEntry != _other.m_pEntry; 79 | } 80 | 81 | template 82 | inline constexpr bool EntryIterator::operator==(sgt_nullptr_t) const 83 | { 84 | return m_pEntry == nullptr; 85 | } 86 | 87 | template 88 | inline constexpr bool EntryIterator::operator!=(sgt_nullptr_t) const 89 | { 90 | return m_pEntry != nullptr; 91 | } 92 | 93 | template 94 | inline constexpr EntryIterator& EntryIterator::operator++() 95 | { 96 | if(m_pEntry != nullptr) m_pEntry = m_pEntry->next(); 97 | return *this; 98 | } 99 | template 100 | inline constexpr EntryIterator& EntryIterator::operator--() 101 | { 102 | if (m_pEntry != nullptr) m_pEntry = m_pEntry->prev(); 103 | return *this; 104 | } 105 | template 106 | inline constexpr EntryIterator EntryIterator::operator++(int) 107 | { 108 | EntryIterator ret(m_pEntry); 109 | if (m_pEntry != nullptr) m_pEntry = m_pEntry->next(); 110 | return ret; 111 | } 112 | 113 | template 114 | inline constexpr EntryIterator EntryIterator::operator--(int) 115 | { 116 | EntryIterator ret(m_pEntry); 117 | if (m_pEntry != nullptr) m_pEntry = m_pEntry->prev(); 118 | return ret; 119 | } 120 | 121 | template 122 | inline constexpr EntryIterator EntryIterator::operator+(unsigned int n) const 123 | { 124 | EntryIterator ret(m_pEntry); 125 | for (unsigned int i = 0; i < n && ret != nullptr; ++i, ++ret) {} 126 | return ret; 127 | } 128 | 129 | template 130 | inline constexpr EntryIterator EntryIterator::operator-(unsigned int n) const 131 | { 132 | EntryIterator ret(m_pEntry); 133 | for (unsigned int i = 0; i < n && ret != nullptr; ++i, --ret) {} 134 | return ret; 135 | } 136 | 137 | template 138 | inline constexpr EntryIterator EntryIterator::prev() const 139 | { 140 | EntryIterator ret(m_pEntry); --ret; 141 | return ret; 142 | } 143 | 144 | template 145 | inline constexpr EntryIterator EntryIterator::next() const 146 | { 147 | EntryIterator ret(m_pEntry); ++ret; 148 | return ret; 149 | } 150 | } // !spvgentwo -------------------------------------------------------------------------------- /lib/include/spvgentwo/EntryPoint.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Function.h" 4 | #include "String.h" 5 | 6 | namespace spvgentwo 7 | { 8 | class EntryPoint : public Function 9 | { 10 | friend class Module; 11 | public: 12 | EntryPoint(); 13 | 14 | // empty function, call setReturnType() first, then use addParameters() and then finalize() to create the function 15 | EntryPoint(Module* _pModule); 16 | 17 | // creates the whole function signature, finalize() does NOT need to be called 18 | template 19 | EntryPoint(Module* _pModule, const spv::ExecutionModel _model, const char* _pEntryPointName, const Flag _control, Instruction* _pReturnType, TypeInstr* ... _paramTypeInstructions); 20 | 21 | ~EntryPoint() override; 22 | 23 | const char* getName() const; 24 | 25 | // TODO: move constructor & asignment 26 | 27 | spv::ExecutionModel getExecutionModel() const { return m_ExecutionModel; } 28 | void setExecutionModel(const spv::ExecutionModel _model) { m_ExecutionModel = _model; } 29 | 30 | // OpEntryPoint 31 | Instruction* getEntryPoint() { return &m_EntryPoint; } 32 | const Instruction* getEntryPoint() const { return &m_EntryPoint; } 33 | 34 | template 35 | Instruction* addExecutionMode(const spv::ExecutionMode _mode, Args ... _args); 36 | 37 | // overrides Functions finalize (used internally), _pEntryPointName is mandatory parameter, returns opFunction 38 | Instruction* finalize(const spv::ExecutionModel _model, const Flag _control, const char* _pEntryPointName); 39 | 40 | // get Variable interface (instructions) operands of OpEntryPoint 41 | Range getInterfaceVariables() const; 42 | 43 | private: 44 | // only to be called by the Module before serialization 45 | void finalizeGlobalInterface(const GlobalInterfaceVersion _version); 46 | 47 | String& getNameStorage(); 48 | 49 | private: 50 | Instruction m_EntryPoint; // OpEntryPoint 51 | spv::ExecutionModel m_ExecutionModel = spv::ExecutionModel::Max; 52 | String m_nameStorage; // literal string of EP name that was encoded in OpEntryPoint 53 | }; 54 | } // !spvgentwo -------------------------------------------------------------------------------- /lib/include/spvgentwo/EntryPointTemplate.inl: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace spvgentwo 4 | { 5 | template 6 | inline EntryPoint::EntryPoint(Module* _pModule, const spv::ExecutionModel _model, const char* _pEntryPointName, const Flag _control, Instruction* _pReturnType, TypeInstr* ..._paramTypeInstructions) : 7 | Function(_pModule, _pEntryPointName, _control, _pReturnType, _paramTypeInstructions...), 8 | m_EntryPoint(this, spv::Op::OpNop), 9 | m_ExecutionModel(_model) 10 | { 11 | m_isEntryPoint = true; 12 | m_EntryPoint.opEntryPoint(_model, &m_Function, _pEntryPointName); 13 | } 14 | 15 | template 16 | inline Instruction* EntryPoint::addExecutionMode(const spv::ExecutionMode _mode, Args ..._args) 17 | { 18 | Instruction* pInstr = getModule()->addExtensionModeInstr(); 19 | 20 | if (getExecutionModeOp(_mode) == spv::Op::OpExecutionModeId) 21 | { 22 | if constexpr (sizeof...(_args) > 0 && stdrep::conjunction_v...>) 23 | { 24 | pInstr->opExecutionModeId(&m_Function, _mode, _args...); 25 | } 26 | else if constexpr (sizeof...(_args) == 0) 27 | { 28 | pInstr->opExecutionModeId(&m_Function, _mode); 29 | } 30 | } 31 | else 32 | { 33 | if constexpr (sizeof...(_args) > 0 && false == stdrep::conjunction_v...>) 34 | { 35 | pInstr->opExecutionMode(&m_Function, _mode, _args...); 36 | } 37 | else if constexpr (sizeof...(_args) == 0) 38 | { 39 | pInstr->opExecutionMode(&m_Function, _mode); 40 | } 41 | } 42 | 43 | return pInstr; 44 | } 45 | } // !spvgentwo -------------------------------------------------------------------------------- /lib/include/spvgentwo/FNV1aHasher.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "stdreplacement.h" 3 | 4 | namespace spvgentwo 5 | { 6 | struct Hash64 7 | { 8 | sgt_uint64_t value; 9 | constexpr Hash64(sgt_uint64_t _val = 0ull) : value(_val) {} 10 | constexpr Hash64(sgt_uint32_t _low, sgt_uint32_t _high) : value(_low | static_cast(_high) << 32u) {} 11 | constexpr operator sgt_uint64_t() const { return value; } 12 | inline constexpr Hash64 operator^=(unsigned char _byte) { value ^= _byte; return *this; } 13 | inline constexpr Hash64 operator*=(const Hash64 _prime) { value *= _prime.value; return *this; } 14 | }; 15 | 16 | namespace detail 17 | { 18 | constexpr Hash64 Offset{ 0xcbf29ce484222325ull }; 19 | constexpr Hash64 Prime{ 0x100000001b3ull }; 20 | } 21 | 22 | template 23 | struct Hasher; 24 | 25 | // 64 bit FNV-1a hash 26 | class FNV1aHasher 27 | { 28 | template 29 | static constexpr Hash64 hasherOrDefault(const T& _data, Hash64 _seed) 30 | { 31 | if constexpr (traits::is_complete_v>) // check if specialization exists 32 | { 33 | Hasher h{}; 34 | return h(_data, _seed); 35 | } 36 | else // resort for default impl 37 | { 38 | const void* pErased = &_data; // avoid reinterpret_cast in constexpr 39 | const unsigned char* pBytes = static_cast(pErased); 40 | for (sgt_size_t i = 0u; i < sizeof(_data); ++i) 41 | { 42 | _seed ^= pBytes[i]; 43 | _seed *= detail::Prime; 44 | } 45 | return _seed; 46 | } 47 | } 48 | 49 | public: 50 | constexpr explicit FNV1aHasher(Hash64 _seed = detail::Offset); 51 | 52 | constexpr Hash64 add(const void* _pData, const sgt_size_t _length); 53 | 54 | constexpr Hash64 get() const { return m_Hash; } 55 | constexpr operator Hash64() const { return m_Hash; } 56 | 57 | constexpr Hash64 operator()(const char* _str); 58 | 59 | template 60 | constexpr Hash64 operator()(const T& _data); 61 | 62 | template 63 | constexpr Hash64 operator()(const T& _data, const Tail&... _tail); 64 | 65 | constexpr FNV1aHasher& operator<<(const char* _pStr) { operator()(_pStr); return *this; } 66 | 67 | template 68 | constexpr FNV1aHasher& operator<<(const T& _data) { operator()(_data); return *this; } 69 | 70 | template 71 | constexpr FNV1aHasher& operator<<(const T* _ptr) { operator()(_ptr); return *this; } 72 | 73 | private: 74 | Hash64 m_Hash = detail::Offset; 75 | }; 76 | 77 | inline constexpr FNV1aHasher::FNV1aHasher(Hash64 _seed) : 78 | m_Hash(_seed) 79 | { 80 | } 81 | 82 | inline constexpr Hash64 spvgentwo::FNV1aHasher::add(const void* _pData, const sgt_size_t _length) 83 | { 84 | const unsigned char* pBytes = static_cast(_pData); 85 | for (sgt_size_t i = 0u; i < _length; ++i) 86 | { 87 | m_Hash ^= pBytes[i]; 88 | m_Hash *= detail::Prime; 89 | } 90 | 91 | return m_Hash; 92 | } 93 | 94 | inline constexpr Hash64 FNV1aHasher::operator()(const char* _pStr) 95 | { 96 | for (; *_pStr != 0; ++_pStr) 97 | { 98 | m_Hash ^= static_cast(*_pStr); 99 | m_Hash *= detail::Prime; 100 | } 101 | 102 | return m_Hash; 103 | } 104 | 105 | template 106 | inline constexpr Hash64 FNV1aHasher::operator()(const T& _data) 107 | { 108 | m_Hash = hasherOrDefault(_data, m_Hash); 109 | return m_Hash; 110 | } 111 | 112 | template 113 | inline constexpr Hash64 FNV1aHasher::operator()(const T& _data, const Tail& ..._tail) 114 | { 115 | operator()(_data); 116 | 117 | if constexpr (sizeof...(_tail) > 0) 118 | { 119 | operator()(_tail...); 120 | } 121 | 122 | return m_Hash; 123 | } 124 | 125 | template 126 | inline constexpr Hash64 hash(const T& _data, const Tail&... _tail) 127 | { 128 | FNV1aHasher h{}; 129 | return h(_data, _tail...); 130 | } 131 | } // !spvgentwo -------------------------------------------------------------------------------- /lib/include/spvgentwo/Flag.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace spvgentwo 4 | { 5 | namespace detail 6 | { 7 | template 8 | constexpr unsigned int bit_or( TEnum ... _enum) 9 | { 10 | return ( ... | static_cast(_enum)); 11 | } 12 | 13 | template 14 | constexpr unsigned int bit_xor (TEnum ... _enum) 15 | { 16 | return (... ^ static_cast(_enum)); 17 | } 18 | 19 | template 20 | constexpr unsigned int bit_and(TEnum ... _enum) 21 | { 22 | return (... & static_cast(_enum)); 23 | } 24 | } // !detail 25 | 26 | template 27 | struct Flag 28 | { 29 | static_assert(sizeof(Enum) <= sizeof(unsigned int), "Enum type does not fit into mask"); 30 | 31 | // can not be explicit because otherwise aggregate init wont work 32 | constexpr Flag(unsigned int _mask = 0u) : mask{ _mask } {} 33 | 34 | // prohibit bad conversions 35 | template 36 | Flag(const PtrT*) = delete; 37 | 38 | Flag(bool) = delete; 39 | 40 | template 41 | constexpr Flag(Enum _first, TEnum ... _enums) : mask{ detail::bit_or(_first, _enums...) } { } 42 | 43 | template 44 | static constexpr Flag OR(Enum _first, TEnum ... _enums) { return static_cast(detail::bit_or(_first, _enums...)); } 45 | 46 | template 47 | static constexpr Flag XOR(Enum _first, TEnum ... _enums) { return static_cast(detail::bit_xor(_first, _enums...)); } 48 | 49 | template 50 | static constexpr Flag AND(Enum _first, TEnum ... _enums) { return static_cast(detail::bit_and(_first, _enums...)); } 51 | 52 | template 53 | void set(Enum _first, TEnum ... _enums) 54 | { 55 | mask |= detail::bit_or(_first, _enums...); 56 | } 57 | 58 | constexpr operator bool() const { return mask != 0u; } 59 | constexpr operator unsigned int() const { return mask; } 60 | constexpr operator Enum() const { return static_cast(mask); } 61 | constexpr bool empty() const { return mask == 0u; } 62 | 63 | template // check if any of the enum flags is set 64 | constexpr bool any(Enum _first, TEnum ... _enums) const { return (mask & detail::bit_or(_first, _enums...)) != 0u; } 65 | template // check if none of the enum flags is set 66 | constexpr bool none(Enum _first, TEnum ... _enums) const { return (mask & detail::bit_or(_first, _enums...)) == 0u; } 67 | template // check if all of the enum flags is set 68 | constexpr bool all(Enum _first, TEnum ... _enums) const { return (mask & detail::bit_or(_first, _enums...)) == detail::bit_or(_first, _enums...); } 69 | 70 | Flag& operator|=(const Flag& r) { mask |= r.mask; return *this; } 71 | Flag& operator&=(const Flag& r) { mask &= r.mask; return *this; } 72 | Flag& operator^=(const Flag& r) { mask ^= r.mask; return *this; } 73 | 74 | Flag& operator|=(const Enum& r) { mask |= static_cast(r); return *this; } 75 | Flag& operator&=(const Enum& r) { mask &= static_cast(r); return *this; } 76 | Flag& operator^=(const Enum& r) { mask ^= static_cast(r); return *this; } 77 | 78 | protected: 79 | unsigned int mask; 80 | }; 81 | 82 | template 83 | constexpr Flag operator|(const Flag& l, const Flag& r) { return Flag::OR(l, r); } 84 | template 85 | constexpr Flag operator&(const Flag& l, const Flag& r) { return Flag::AND(l, r); } 86 | template 87 | constexpr Flag operator^(const Flag& l, const Flag& r) { return Flag::XOR(l, r); } 88 | 89 | template 90 | constexpr Flag operator|(const Flag& l, const Enum& r) { return Flag::OR(l, r); } 91 | template 92 | constexpr Flag operator&(const Flag& l, const Enum& r) { return Flag::AND(l, r); } 93 | template 94 | constexpr Flag operator^(const Flag& l, const Enum& r) { return Flag::XOR(l, r); } 95 | 96 | template 97 | constexpr Flag operator|( const Enum& l, const Flag& r ) { return Flag::OR( l, r ); } 98 | template 99 | constexpr Flag operator&( const Enum& l, const Flag& r ) { return Flag::AND( l, r ); } 100 | template 101 | constexpr Flag operator^( const Enum& l, const Flag& r ) { return Flag::XOR( l, r ); } 102 | } // !spvgentwo -------------------------------------------------------------------------------- /lib/include/spvgentwo/Function.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "BasicBlock.h" 4 | #include "Type.h" 5 | 6 | namespace spvgentwo 7 | { 8 | // forward delcs 9 | class Module; 10 | class IAllocator; 11 | class EntryPoint; 12 | 13 | enum class GlobalInterfaceVersion 14 | { 15 | SpirV1_3, // 1.0 - 1.3 Input and Output StorageClass 16 | SpirV14_x // 1.4 - 1.x any StorageClass != Function 17 | }; 18 | 19 | class Function : public List 20 | { 21 | friend class Module; 22 | 23 | public: 24 | Function() = default; 25 | 26 | // empty function, call setReturnType() first, then use addParameters() and then finalize() to create the function 27 | Function(Module* _pModule); 28 | 29 | // creates the whole function signature, finalize() does NOT need to be called 30 | template 31 | Function(Module* _pModule, const char* _pName, const Flag _control, Instruction* _pReturnType, TypeInstr* ... _paramTypeInstructions); 32 | 33 | Function(Module* _pModule, Function&& _other) noexcept; 34 | Function(const Function& _other) = delete; 35 | 36 | virtual ~Function(); 37 | 38 | Function& operator=(Function&& _other) noexcept; 39 | Function& operator=(const Function& _other) = delete; 40 | 41 | // OpFunction 42 | Instruction* getFunction() { return &m_Function; } 43 | const Instruction* getFunction() const { return &m_Function; } 44 | 45 | // returns void by default 46 | const Type& getReturnType() const { return m_FunctionType.front(); } 47 | Type& getReturnType() { return m_FunctionType.front(); } 48 | 49 | // calling this function will materialize the result type operand of OpTypeFunction (add it to the module type lookup) 50 | Instruction* getReturnTypeInstr() const; 51 | 52 | const Type& getFunctionType() const { return m_FunctionType; } 53 | Type& getFunctionType() { return m_FunctionType; } 54 | 55 | // calling this function will materialize the OpTypeFunction. It should only be called after finalizing 56 | Instruction* getFunctionTypeInstr() const; 57 | 58 | Instruction* getFunctionEnd() { return &m_FunctionEnd; } 59 | const Instruction* getFunctionEnd() const { return &m_FunctionEnd; } 60 | 61 | Module* getModule() const { return m_pModule; } 62 | 63 | // get name assigned by OpName (if any). Calls Module::getName(&m_Function) 64 | const char* getName() const; 65 | 66 | bool isEntryPoint() const { return m_isEntryPoint; } 67 | EntryPoint* asEntryPoint() { return m_isEntryPoint ? reinterpret_cast(this) : nullptr; } 68 | const EntryPoint* asEntryPoint() const { return m_isEntryPoint ? reinterpret_cast(this) : nullptr; } 69 | 70 | BasicBlock& addBasicBlock(const char* _pName = nullptr) { return emplace_back(this, _pName); } 71 | 72 | // remove _pBB from this function (destroying it), optionally replacing it with _pReplacement, returning uses of this basic block or its label 73 | // if bool _gatherReferencedInstructions is true, also return uses of instructions from the removed basic block (OpName etc) 74 | List remove(const BasicBlock* _pBB, BasicBlock* _pReplacement = nullptr, IAllocator* _pAllocator = nullptr); 75 | 76 | // return entry bb (avoid confusion when adding a BB to this function and instructions are "magically" added to the last BB if using m_pLast 77 | BasicBlock& operator->() { return m_pBegin->inner(); } 78 | operator BasicBlock& () { return m_pBegin->inner();} 79 | BasicBlock& operator*() { return m_pBegin->inner(); } 80 | 81 | // write OpFunction OpFunctionParameters OpFunctionEnd to IWriter 82 | void write(IWriter& _writer) const; 83 | 84 | // read function from IReader user _grammer, assuming OpFunction was already parsed/consumed by module::read(Reader* _pReader) 85 | bool read(IReader& _rader, const Grammar& _grammar, Instruction&& _opFunc); 86 | 87 | // storage class is Function 88 | Instruction* variable(Instruction* _pPtrType, const char* _pName = nullptr, Instruction* _pInitialzer = nullptr); 89 | 90 | // if ptrType is not a pointer, it will be wrapped by a pointer with _storageClass 91 | Instruction* variable(const Type& _ptrType, const char* _pName = nullptr, Instruction* _pInitialzer = nullptr); 92 | 93 | template // adds Pointer to type T 94 | Instruction* variable(const char* _pName = nullptr, Instruction* _pInitialzer = nullptr); 95 | 96 | template 97 | Instruction* variable(const T& _initialValue, const char* _pName = nullptr); 98 | 99 | // set the first subtype of OpTypeFunction (opperands are added by addParameters), returns true on success 100 | bool setReturnType(Instruction* _pReturnType); 101 | bool setReturnType(const Type& _returnType); 102 | 103 | // sets m_FunctionType (OpTypeFunction), return true on success 104 | bool setFunctionType(Instruction* _pFunctionType); 105 | bool setFunctionType(const Type& _functionType); 106 | 107 | // adds opFunctionParameter(_pParamType) to m_parameters and _pParamType to m_pFunctionType, returns last opFunctionParameter generated 108 | template 109 | Instruction* addParameters(Instruction* _pParamType, TypeInstr* ... _paramTypeInstructions); 110 | 111 | // get opFunctionParameter in order they were added by addParameters 112 | Instruction* getParameter(unsigned int _index) const; 113 | 114 | // get list of all OpFunctionParameter instructions added by addParameters() 115 | const List& getParameters() const { return m_Parameters; } 116 | List& getParameters() { return m_Parameters; } 117 | 118 | // creates opFunction, m_pFunctionType must have been completed (all parameters added via addParameters), returns opFunction 119 | Instruction* finalize(const Flag _control, const char* _pName = nullptr); 120 | 121 | // returns true if this Functions has a valid OpFunctionType & m_Function is setup to OpFunction 122 | bool isFinalized() const; 123 | 124 | Flag getFunctionControl() const; 125 | 126 | protected: 127 | Module* m_pModule = nullptr; // parent 128 | 129 | Instruction m_Function; // OpFunction 130 | Instruction m_FunctionEnd; 131 | 132 | Type m_FunctionType; 133 | 134 | List m_Parameters; // OpFunctionParameters 135 | 136 | bool m_isEntryPoint = false; 137 | }; 138 | 139 | // get all the global OpVariables with StorageClass != Function used in this function 140 | void collectReferencedVariables(const Function& _func, List& _outVarInstr, const GlobalInterfaceVersion _version, IAllocator* _pAllocator); 141 | } // !spvgentwo -------------------------------------------------------------------------------- /lib/include/spvgentwo/FunctionTemplate.inl: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace spvgentwo 4 | { 5 | template 6 | inline Function::Function(Module* _pModule, const char* _pName, const Flag _control, Instruction* _pReturnType, TypeInstr* ..._paramTypeInstructions) 7 | : List(_pModule->getAllocator()), 8 | m_pModule(_pModule), 9 | m_Function(this, spv::Op::OpNop), // not finalized 10 | m_FunctionEnd(this, spv::Op::OpFunctionEnd), 11 | m_FunctionType(_pModule->getAllocator(), spv::Op::OpTypeFunction), 12 | m_Parameters(_pModule->getAllocator()) 13 | { 14 | m_FunctionType.VoidM(); // return type defaults to void 15 | // function signature type 16 | setReturnType(_pReturnType); 17 | 18 | if constexpr (sizeof...(_paramTypeInstructions) > 0) 19 | { 20 | addParameters(_paramTypeInstructions...); 21 | } 22 | 23 | finalize(_control, _pName); 24 | } 25 | 26 | template 27 | inline Instruction* Function::addParameters(Instruction* _pParamType, TypeInstr* ..._paramTypeInstructions) 28 | { 29 | if (const Type* type = _pParamType->getType(); type != nullptr) 30 | { 31 | if (type->isVoid()) 32 | { 33 | m_pModule->logError("_pParamType cannot be OpTypeVoid"); 34 | return nullptr; 35 | } 36 | m_FunctionType.getSubTypes().emplace_back(*type); 37 | } 38 | 39 | [[maybe_unused]] Instruction* param = m_Parameters.emplace_back(this, spv::Op::OpNop).opFunctionParameter(_pParamType); 40 | 41 | if constexpr (sizeof...(_paramTypeInstructions) > 0) 42 | { 43 | return addParameters(_paramTypeInstructions...); 44 | } 45 | else 46 | { 47 | return param; 48 | } 49 | } 50 | 51 | template 52 | inline Instruction* Function::variable(const char* _pName, Instruction* _pInitialzer) 53 | { 54 | return variable(getModule()->template type(spv::StorageClass::Function), _pName, _pInitialzer); 55 | } 56 | 57 | template 58 | inline Instruction* Function::variable(const T& _initialValue, const char* _pName) 59 | { 60 | return variable(getModule()->template type(spv::StorageClass::Function, _initialValue), _pName, getModule()->constant(_initialValue)); 61 | } 62 | } // !spvgentwo -------------------------------------------------------------------------------- /lib/include/spvgentwo/Glsl.h: -------------------------------------------------------------------------------- 1 | // Auto generated - do not modify 2 | // Copyright (c) 2014-2016 The Khronos Group Inc. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and/or associated documentation files (the "Materials"), 6 | // to deal in the Materials without restriction, including without limitation 7 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | // and/or sell copies of the Materials, and to permit persons to whom the 9 | // Materials are furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Materials. 13 | // 14 | // MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS 15 | // STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND 16 | // HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ 17 | // 18 | // THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 19 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | // FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS 24 | // IN THE MATERIALS. 25 | #pragma once 26 | 27 | namespace spvgentwo::glslstd450 28 | { 29 | static constexpr unsigned int Version = 100; 30 | static constexpr unsigned int Revision = 2; 31 | enum class Op : unsigned 32 | { 33 | Round = 1, 34 | RoundEven = 2, 35 | Trunc = 3, 36 | FAbs = 4, 37 | SAbs = 5, 38 | FSign = 6, 39 | SSign = 7, 40 | Floor = 8, 41 | Ceil = 9, 42 | Fract = 10, 43 | Radians = 11, 44 | Degrees = 12, 45 | Sin = 13, 46 | Cos = 14, 47 | Tan = 15, 48 | Asin = 16, 49 | Acos = 17, 50 | Atan = 18, 51 | Sinh = 19, 52 | Cosh = 20, 53 | Tanh = 21, 54 | Asinh = 22, 55 | Acosh = 23, 56 | Atanh = 24, 57 | Atan2 = 25, 58 | Pow = 26, 59 | Exp = 27, 60 | Log = 28, 61 | Exp2 = 29, 62 | Log2 = 30, 63 | Sqrt = 31, 64 | InverseSqrt = 32, 65 | Determinant = 33, 66 | MatrixInverse = 34, 67 | Modf = 35, 68 | ModfStruct = 36, 69 | FMin = 37, 70 | UMin = 38, 71 | SMin = 39, 72 | FMax = 40, 73 | UMax = 41, 74 | SMax = 42, 75 | FClamp = 43, 76 | UClamp = 44, 77 | SClamp = 45, 78 | FMix = 46, 79 | IMix = 47, 80 | Step = 48, 81 | SmoothStep = 49, 82 | Fma = 50, 83 | Frexp = 51, 84 | FrexpStruct = 52, 85 | Ldexp = 53, 86 | PackSnorm4x8 = 54, 87 | PackUnorm4x8 = 55, 88 | PackSnorm2x16 = 56, 89 | PackUnorm2x16 = 57, 90 | PackHalf2x16 = 58, 91 | PackDouble2x32 = 59, 92 | UnpackSnorm2x16 = 60, 93 | UnpackUnorm2x16 = 61, 94 | UnpackHalf2x16 = 62, 95 | UnpackSnorm4x8 = 63, 96 | UnpackUnorm4x8 = 64, 97 | UnpackDouble2x32 = 65, 98 | Length = 66, 99 | Distance = 67, 100 | Cross = 68, 101 | Normalize = 69, 102 | FaceForward = 70, 103 | Reflect = 71, 104 | Refract = 72, 105 | FindILsb = 73, 106 | FindSMsb = 74, 107 | FindUMsb = 75, 108 | InterpolateAtCentroid = 76, 109 | InterpolateAtSample = 77, 110 | InterpolateAtOffset = 78, 111 | NMin = 79, 112 | NMax = 80, 113 | NClamp = 81, 114 | Max = 0x7fffffff 115 | }; 116 | } // spvgentwo::glslstd450 117 | -------------------------------------------------------------------------------- /lib/include/spvgentwo/Grammar.h: -------------------------------------------------------------------------------- 1 | // Auto generated - do not modify 2 | #pragma once 3 | 4 | #include "Vector.h" 5 | #include "HashMap.h" 6 | #include "Spv.h" 7 | 8 | namespace spvgentwo 9 | { 10 | class Grammar 11 | { 12 | public: 13 | enum class Extension : unsigned short 14 | { 15 | Core = 0, 16 | Glsl = 1, 17 | OpenCl = 2, 18 | }; 19 | enum class OperandCategory : unsigned short 20 | { 21 | BitEnum, 22 | ValueEnum, 23 | Id, 24 | Literal, 25 | Composite, 26 | }; 27 | enum class OperandKind : unsigned short 28 | { 29 | ImageOperands=0u, 30 | FPFastMathMode=1u, 31 | SelectionControl=2u, 32 | LoopControl=3u, 33 | FunctionControl=4u, 34 | MemorySemantics=5u, 35 | MemoryAccess=6u, 36 | KernelProfilingInfo=7u, 37 | RayFlags=8u, 38 | FragmentShadingRate=9u, 39 | SourceLanguage=10u, 40 | ExecutionModel=11u, 41 | AddressingModel=12u, 42 | MemoryModel=13u, 43 | ExecutionMode=14u, 44 | StorageClass=15u, 45 | Dim=16u, 46 | SamplerAddressingMode=17u, 47 | SamplerFilterMode=18u, 48 | ImageFormat=19u, 49 | ImageChannelOrder=20u, 50 | ImageChannelDataType=21u, 51 | FPRoundingMode=22u, 52 | FPDenormMode=23u, 53 | QuantizationModes=24u, 54 | FPOperationMode=25u, 55 | OverflowModes=26u, 56 | LinkageType=27u, 57 | AccessQualifier=28u, 58 | HostAccessQualifier=29u, 59 | FunctionParameterAttribute=30u, 60 | Decoration=31u, 61 | BuiltIn=32u, 62 | Scope=33u, 63 | GroupOperation=34u, 64 | KernelEnqueueFlags=35u, 65 | Capability=36u, 66 | RayQueryIntersection=37u, 67 | RayQueryCommittedIntersectionType=38u, 68 | RayQueryCandidateIntersectionType=39u, 69 | PackedVectorFormat=40u, 70 | CooperativeMatrixOperands=41u, 71 | CooperativeMatrixLayout=42u, 72 | CooperativeMatrixUse=43u, 73 | InitializationModeQualifier=44u, 74 | LoadCacheControl=45u, 75 | StoreCacheControl=46u, 76 | IdResultType=47u, 77 | IdResult=48u, 78 | IdMemorySemantics=49u, 79 | IdScope=50u, 80 | IdRef=51u, 81 | LiteralInteger=52u, 82 | LiteralString=53u, 83 | LiteralFloat=54u, 84 | LiteralContextDependentNumber=55u, 85 | LiteralExtInstInteger=56u, 86 | LiteralSpecConstantOpInteger=57u, 87 | PairLiteralIntegerIdRef=58u, 88 | PairIdRefLiteralInteger=59u, 89 | PairIdRefIdRef=60u, 90 | }; 91 | enum class Quantifier 92 | { 93 | ZeroOrOne, // zero or one 94 | ZeroOrAny, // zero or any 95 | One, // exactly once 96 | }; 97 | struct Operand 98 | { 99 | OperandKind kind; 100 | OperandCategory category; 101 | const char* name; 102 | Quantifier quantifier; 103 | }; 104 | struct Instruction 105 | { 106 | const char* name; 107 | Vector operands; 108 | Vector capabilities; 109 | Vector extensions; 110 | unsigned int version; 111 | }; 112 | Grammar(IAllocator* _pAllocator); 113 | const Instruction* getInfo(unsigned int _opcode, Extension _extension = Extension::Core) const; 114 | const char* getOperandName(OperandKind _kind, unsigned int _literalValue) const; 115 | const Vector* getOperandParameters(OperandKind _kind, unsigned int _literalValue) const; 116 | const Vector* getOperandBases(OperandKind _kind) const; 117 | static bool hasOperandParameters(OperandKind _kind); 118 | private: 119 | HashMap m_instructions; 120 | HashMap m_operandNames; 121 | HashMap> m_operandParameters; 122 | HashMap> m_operandBases; 123 | }; 124 | } // spvgentwo 125 | -------------------------------------------------------------------------------- /lib/include/spvgentwo/HashMapIterator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "List.h" 4 | #include "FNV1aHasher.h" 5 | 6 | namespace spvgentwo 7 | { 8 | template 9 | struct NodeT 10 | { 11 | template 12 | friend class HashMap; 13 | 14 | template 15 | constexpr NodeT( Args&& ... _args) : kv( stdrep::forward(_args)... ) {} 16 | 17 | struct KV 18 | { 19 | template 20 | constexpr KV(KeyT&& _key, Args&& ... _args) : 21 | key{ stdrep::forward(_key) }, 22 | value{ stdrep::forward(_args)... } {} 23 | 24 | Key key{}; 25 | Value value{}; 26 | } kv; 27 | 28 | private: 29 | Hash64 hash{0u}; 30 | }; 31 | 32 | template 33 | class HashMapIterator 34 | { 35 | template 36 | friend class HashMap; 37 | 38 | public: 39 | using Node = NodeT; 40 | using KeyValue = typename Node::KV; 41 | using Bucket = List; 42 | 43 | constexpr HashMapIterator(Bucket* _pBucket = nullptr, Bucket* _pEnd = nullptr, typename Bucket::Iterator _element = nullptr) : m_element(_element), m_pBucket(_pBucket), m_pEnd(_pEnd){} 44 | constexpr HashMapIterator(const HashMapIterator& _other) : m_element(_other.m_element), m_pBucket(_other.m_pBucket), m_pEnd(_other.m_pEnd) {} 45 | 46 | constexpr bool operator==(const HashMapIterator& _other) const; 47 | constexpr bool operator!=(const HashMapIterator& _other) const; 48 | 49 | // pre 50 | constexpr HashMapIterator& operator++(); 51 | 52 | // post 53 | constexpr HashMapIterator operator++(int); 54 | 55 | constexpr HashMapIterator next() const; 56 | 57 | constexpr KeyValue& operator*() { return m_element->kv; } 58 | constexpr const KeyValue& operator*() const { return m_element->kv; } 59 | 60 | constexpr KeyValue* operator->() { return &m_element->kv; } 61 | constexpr const KeyValue* operator->() const { return &m_element->kv; } 62 | 63 | // check if iterator is valid, can be derefed. Might still be at end! 64 | constexpr operator bool() const { return m_pBucket != nullptr || m_element == nullptr; } 65 | 66 | private: 67 | typename Bucket::Iterator m_element = nullptr; 68 | Bucket* m_pBucket = nullptr; 69 | Bucket* m_pEnd = nullptr; 70 | }; 71 | 72 | template 73 | inline constexpr bool HashMapIterator::operator==(const HashMapIterator& _other) const 74 | { 75 | return m_pBucket == _other.m_pBucket && m_pEnd == _other.m_pEnd && m_element == _other.m_element; 76 | } 77 | 78 | template 79 | inline constexpr bool HashMapIterator::operator!=(const HashMapIterator& _other) const 80 | { 81 | return m_pBucket != _other.m_pBucket || m_pEnd != _other.m_pEnd || m_element != _other.m_element; 82 | } 83 | 84 | template 85 | inline constexpr HashMapIterator& HashMapIterator::operator++() 86 | { 87 | if (++m_element == nullptr) 88 | { 89 | while (++m_pBucket < m_pEnd) 90 | { 91 | if (m_pBucket->empty() == false) 92 | { 93 | m_element = m_pBucket->begin(); 94 | break; 95 | } 96 | } 97 | } 98 | 99 | return *this; 100 | } 101 | 102 | template 103 | inline constexpr HashMapIterator HashMapIterator::operator++(int) 104 | { 105 | HashMapIterator ret(*this); 106 | this->operator++(); 107 | return ret; 108 | } 109 | 110 | template 111 | inline constexpr HashMapIterator HashMapIterator::next() const 112 | { 113 | HashMapIterator ret(*this); 114 | return ++ret; 115 | } 116 | } // !spvgentwo -------------------------------------------------------------------------------- /lib/include/spvgentwo/Logger.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Flag.h" 4 | 5 | namespace spvgentwo 6 | { 7 | enum class LogLevel : unsigned int 8 | { 9 | None = 0, 10 | Debug = 1 << 0, // for dev 11 | Info = 1 << 1, // detail msg 12 | Warning = 1 << 2, // missing information 13 | Error = 1 << 3, // invalid program / spirv state 14 | Fatal = 1 << 4, // program should terminate 15 | All = Debug | Info | Warning | Error | Fatal 16 | }; 17 | 18 | class ILogger 19 | { 20 | public: 21 | using Callback = void (*) (ILogger* _pInstance, LogLevel _level, const char* _format, ...); 22 | 23 | ILogger( Callback _callback = nullptr, Flag _filter = LogLevel::All ) : m_callback( _callback ), m_filter(_filter) {} 24 | 25 | void setCallback( Callback _callback ) { m_callback = _callback; } 26 | void setFilter( Flag _filter ) { m_filter = _filter; } 27 | 28 | virtual ~ILogger() {} 29 | 30 | template 31 | void log(LogLevel _level, const char* _pFormat, Args... _args); 32 | 33 | template 34 | void logDebug(const char* _pFormat, Args... _args) { log(LogLevel::Debug, _pFormat, _args...); } 35 | template 36 | void logInfo(const char* _pFormat, Args... _args) { log(LogLevel::Info, _pFormat, _args...); } 37 | template 38 | void logWarning(const char* _pFormat, Args... _args) { log(LogLevel::Warning, _pFormat, _args...); } 39 | template 40 | void logError(const char* _pFormat, Args... _args) { log(LogLevel::Error, _pFormat, _args...); } 41 | template 42 | void logFatal(const char* _pFormat, Args... _args) { log(LogLevel::Fatal, _pFormat, _args...); } 43 | 44 | private: 45 | Callback m_callback = nullptr; 46 | Flag m_filter = LogLevel::All; 47 | }; 48 | 49 | template 50 | inline void ILogger::log(LogLevel _level, const char* _pFormat, Args... _args) 51 | { 52 | LogLevel level = _level & m_filter; 53 | if (m_callback != nullptr && level != LogLevel::None ) 54 | { 55 | m_callback(this, level, _pFormat, _args...); 56 | } 57 | } 58 | } //! spvgentwo -------------------------------------------------------------------------------- /lib/include/spvgentwo/OpenCl.h: -------------------------------------------------------------------------------- 1 | // Auto generated - do not modify 2 | // Copyright (c) 2014-2016 The Khronos Group Inc. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and/or associated documentation files (the "Materials"), 6 | // to deal in the Materials without restriction, including without limitation 7 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | // and/or sell copies of the Materials, and to permit persons to whom the 9 | // Materials are furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Materials. 13 | // 14 | // MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS 15 | // STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND 16 | // HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ 17 | // 18 | // THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 19 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | // FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS 24 | // IN THE MATERIALS. 25 | #pragma once 26 | 27 | namespace spvgentwo::opencl100 28 | { 29 | static constexpr unsigned int Version = 100; 30 | static constexpr unsigned int Revision = 2; 31 | enum class Op : unsigned 32 | { 33 | acos = 0, 34 | acosh = 1, 35 | acospi = 2, 36 | asin = 3, 37 | asinh = 4, 38 | asinpi = 5, 39 | atan = 6, 40 | atan2 = 7, 41 | atanh = 8, 42 | atanpi = 9, 43 | atan2pi = 10, 44 | cbrt = 11, 45 | ceil = 12, 46 | copysign = 13, 47 | cos = 14, 48 | cosh = 15, 49 | cospi = 16, 50 | erfc = 17, 51 | erf = 18, 52 | exp = 19, 53 | exp2 = 20, 54 | exp10 = 21, 55 | expm1 = 22, 56 | fabs = 23, 57 | fdim = 24, 58 | floor = 25, 59 | fma = 26, 60 | fmax = 27, 61 | fmin = 28, 62 | fmod = 29, 63 | fract = 30, 64 | frexp = 31, 65 | hypot = 32, 66 | ilogb = 33, 67 | ldexp = 34, 68 | lgamma = 35, 69 | lgamma_r = 36, 70 | log = 37, 71 | log2 = 38, 72 | log10 = 39, 73 | log1p = 40, 74 | logb = 41, 75 | mad = 42, 76 | maxmag = 43, 77 | minmag = 44, 78 | modf = 45, 79 | nan = 46, 80 | nextafter = 47, 81 | pow = 48, 82 | pown = 49, 83 | powr = 50, 84 | remainder = 51, 85 | remquo = 52, 86 | rint = 53, 87 | rootn = 54, 88 | round = 55, 89 | rsqrt = 56, 90 | sin = 57, 91 | sincos = 58, 92 | sinh = 59, 93 | sinpi = 60, 94 | sqrt = 61, 95 | tan = 62, 96 | tanh = 63, 97 | tanpi = 64, 98 | tgamma = 65, 99 | trunc = 66, 100 | half_cos = 67, 101 | half_divide = 68, 102 | half_exp = 69, 103 | half_exp2 = 70, 104 | half_exp10 = 71, 105 | half_log = 72, 106 | half_log2 = 73, 107 | half_log10 = 74, 108 | half_powr = 75, 109 | half_recip = 76, 110 | half_rsqrt = 77, 111 | half_sin = 78, 112 | half_sqrt = 79, 113 | half_tan = 80, 114 | native_cos = 81, 115 | native_divide = 82, 116 | native_exp = 83, 117 | native_exp2 = 84, 118 | native_exp10 = 85, 119 | native_log = 86, 120 | native_log2 = 87, 121 | native_log10 = 88, 122 | native_powr = 89, 123 | native_recip = 90, 124 | native_rsqrt = 91, 125 | native_sin = 92, 126 | native_sqrt = 93, 127 | native_tan = 94, 128 | s_abs = 141, 129 | s_abs_diff = 142, 130 | s_add_sat = 143, 131 | u_add_sat = 144, 132 | s_hadd = 145, 133 | u_hadd = 146, 134 | s_rhadd = 147, 135 | u_rhadd = 148, 136 | s_clamp = 149, 137 | u_clamp = 150, 138 | clz = 151, 139 | ctz = 152, 140 | s_mad_hi = 153, 141 | u_mad_sat = 154, 142 | s_mad_sat = 155, 143 | s_max = 156, 144 | u_max = 157, 145 | s_min = 158, 146 | u_min = 159, 147 | s_mul_hi = 160, 148 | rotate = 161, 149 | s_sub_sat = 162, 150 | u_sub_sat = 163, 151 | u_upsample = 164, 152 | s_upsample = 165, 153 | popcount = 166, 154 | s_mad24 = 167, 155 | u_mad24 = 168, 156 | s_mul24 = 169, 157 | u_mul24 = 170, 158 | u_abs = 201, 159 | u_abs_diff = 202, 160 | u_mul_hi = 203, 161 | u_mad_hi = 204, 162 | fclamp = 95, 163 | degrees = 96, 164 | fmax_common = 97, 165 | fmin_common = 98, 166 | mix = 99, 167 | radians = 100, 168 | step = 101, 169 | smoothstep = 102, 170 | sign = 103, 171 | cross = 104, 172 | distance = 105, 173 | length = 106, 174 | normalize = 107, 175 | fast_distance = 108, 176 | fast_length = 109, 177 | fast_normalize = 110, 178 | bitselect = 186, 179 | select = 187, 180 | vloadn = 171, 181 | vstoren = 172, 182 | vload_half = 173, 183 | vload_halfn = 174, 184 | vstore_half = 175, 185 | vstore_half_r = 176, 186 | vstore_halfn = 177, 187 | vstore_halfn_r = 178, 188 | vloada_halfn = 179, 189 | vstorea_halfn = 180, 190 | vstorea_halfn_r = 181, 191 | shuffle = 182, 192 | shuffle2 = 183, 193 | printf = 184, 194 | prefetch = 185, 195 | Max = 0x7fffffff 196 | }; 197 | } // spvgentwo::opencl100 198 | -------------------------------------------------------------------------------- /lib/include/spvgentwo/Operand.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "SpvDefines.h" 4 | #include "stdreplacement.h" 5 | 6 | namespace spvgentwo 7 | { 8 | // forward decls 9 | class Instruction; 10 | class BasicBlock; 11 | class IWriter; 12 | 13 | static constexpr spv::Id InvalidId{ 0u }; 14 | static constexpr Instruction* InvalidInstr = nullptr; 15 | 16 | struct literal_t 17 | { 18 | constexpr literal_t() = default; 19 | template 20 | constexpr literal_t(Arg&& _arg) : value{ static_cast(_arg) } {} 21 | 22 | unsigned int value = 0u; 23 | operator unsigned int() const { return value; } 24 | }; 25 | 26 | static_assert(sizeof(literal_t) == sizeof(unsigned int), "literal_t padded"); 27 | 28 | struct Operand 29 | { 30 | enum class Type : unsigned int 31 | { 32 | Instruction = 0, 33 | BranchTarget, 34 | Literal, 35 | Id 36 | } type{}; 37 | 38 | union 39 | { 40 | Instruction* instruction = nullptr; // intermediate or type 41 | literal_t literal; 42 | spv::Id id; 43 | BasicBlock* branchTarget; 44 | }; 45 | 46 | constexpr bool isBranchTarget() const { return type == Type::BranchTarget; } 47 | constexpr bool isInstruction() const { return type == Type::Instruction; } 48 | constexpr bool isLiteral() const { return type == Type::Literal; } 49 | constexpr bool isId() const { return type == Type::Id; } 50 | 51 | constexpr BasicBlock* getBranchTarget() const { return isBranchTarget() ? branchTarget : nullptr; } 52 | constexpr Instruction* getInstruction() const { return isInstruction() ? instruction : nullptr; } 53 | constexpr literal_t getLiteral() const { return isLiteral() ? literal : literal_t{}; } 54 | constexpr spv::Id getId() const { return isId() ? id : InvalidId; } 55 | 56 | constexpr explicit operator Instruction* () const { return getInstruction(); } 57 | constexpr explicit operator spv::Id() const { return getId(); } 58 | constexpr explicit operator literal_t() const { return getLiteral(); } 59 | constexpr explicit operator BasicBlock* () const { return getBranchTarget(); } 60 | 61 | Operand(const Operand& _other); 62 | Operand(Operand&& _other) noexcept; 63 | 64 | Operand(BasicBlock* _target) : type(Type::BranchTarget), branchTarget(_target) {} 65 | Operand(Instruction* _instr) : type(Type::Instruction), instruction(_instr) {} 66 | Operand(literal_t _value) : type(Type::Literal), instruction(nullptr) { literal = _value; } 67 | Operand(spv::Id _resutlId) : type(Type::Id), instruction(nullptr) { id = _resutlId; } 68 | 69 | bool write(IWriter& _writer) const; 70 | 71 | Operand& operator=(const Operand& _other); 72 | Operand& operator=(Operand&& _other) noexcept; 73 | 74 | bool operator==(const Operand& _other) const; 75 | 76 | bool operator==(const BasicBlock* _target) const; 77 | bool operator==(const Instruction* _instr) const; 78 | bool operator==(const literal_t& _value) const; 79 | bool operator==(const spv::Id& _resultId) const; 80 | 81 | bool operator!=(const BasicBlock* _target) const; 82 | bool operator!=(const Instruction* _instr) const; 83 | bool operator!=(const literal_t& _value) const; 84 | bool operator!=(const spv::Id& _resultId) const; 85 | }; 86 | 87 | template 88 | void appendLiteralsToContainer(Container& _out, T first, Args ..._args) 89 | { 90 | static_assert(stdrep::is_constructible_v, "Container does not accept literal_t"); 91 | static_assert(stdrep::is_constructible_v, "Value type T for 'first' can not be used to construct a literal"); 92 | 93 | [[maybe_unused]] 94 | auto copy = [](const void* _src, void* _dst, sgt_size_t size) 95 | { 96 | auto* src = static_cast(_src); 97 | auto* dst = static_cast(_dst); 98 | 99 | for ( sgt_size_t i = 0; i < size; i++ ) 100 | { 101 | dst[i] = src[i]; 102 | } 103 | }; 104 | 105 | using S = stdrep::decay_t; 106 | 107 | // The high-order bits of a literal number must be 0 for a floating-point type, or 0 for an integer type with Signedness of 0, or sign extended when Signedness is 1 108 | if constexpr ( stdrep::is_same_v ) 109 | { 110 | _out.emplace_back(static_cast(first < 0 ? 0xFFFFFF00 : 0u) | static_cast(first)); 111 | } 112 | else if constexpr ( stdrep::is_same_v ) 113 | { 114 | _out.emplace_back(static_cast(first < 0 ? 0xFFFF0000 : 0u) | static_cast(first)); 115 | } 116 | else if constexpr ( stdrep::is_same_v || stdrep::is_same_v || stdrep::is_same_v ) 117 | { 118 | static_assert(sizeof(double) == 2*sizeof(sgt_uint32_t)); 119 | sgt_uint32_t vals[2]{}; 120 | copy(&first, vals, sizeof(double)); 121 | 122 | _out.emplace_back(vals[0]); 123 | _out.emplace_back(vals[1]); 124 | } 125 | else if constexpr ( stdrep::is_same_v ) 126 | { 127 | static_assert(sizeof(float) == sizeof(sgt_uint32_t)); 128 | sgt_uint32_t val{}; 129 | copy(&first, &val, sizeof(sgt_uint32_t)); 130 | 131 | _out.emplace_back(val); 132 | } 133 | else 134 | { 135 | static_assert(sizeof(T) <= sizeof(sgt_uint32_t)); 136 | _out.emplace_back(literal_t{ first }); 137 | } 138 | 139 | if constexpr ( sizeof...(_args) > 0u ) 140 | { 141 | appendLiteralsToContainer(_out, _args...); 142 | } 143 | } 144 | 145 | template 146 | void appendLiteralsToContainer(Container& _out, const char* _pStr) 147 | { 148 | static_assert(stdrep::is_constructible_v, "Container does not accept literal_t"); 149 | 150 | literal_t word{ 0u }; 151 | unsigned int l = 0u; 152 | char c = 0u; 153 | 154 | do 155 | { 156 | c = _pStr[l]; 157 | const auto mod = l++ % sizeof(unsigned int); 158 | reinterpret_cast(&word)[mod] = c; 159 | 160 | if ( c == 0 || mod == 3 ) 161 | { 162 | _out.emplace_back(word); 163 | word.value = 0u; 164 | } 165 | 166 | } while ( c != 0 ); 167 | } 168 | } // !spvgentwo -------------------------------------------------------------------------------- /lib/include/spvgentwo/Operators.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace spvgentwo 4 | { 5 | // forward decls 6 | class Instruction; 7 | class BasicBlock; 8 | class Type; 9 | 10 | namespace ops 11 | { 12 | BasicBlock& operator!(BasicBlock& _left); 13 | 14 | BasicBlock& operator+(BasicBlock& _left, Instruction* _pRight); 15 | BasicBlock& operator+(BasicBlock& _left, Instruction& _right); 16 | 17 | BasicBlock& operator-(BasicBlock& _left, Instruction* _pRight); 18 | BasicBlock& operator-(BasicBlock& _left, Instruction& _right); 19 | 20 | BasicBlock& operator*(BasicBlock& _left, Instruction* _pRight); 21 | BasicBlock& operator*(BasicBlock& _left, Instruction& _right); 22 | 23 | BasicBlock& operator/(BasicBlock& _left, Instruction* _pRight); 24 | BasicBlock& operator/(BasicBlock& _left, Instruction& _right); 25 | 26 | BasicBlock& operator==(BasicBlock& _left, Instruction* _pRight); 27 | BasicBlock& operator==(BasicBlock& _left, Instruction& _right); 28 | 29 | BasicBlock& operator!=(BasicBlock& _left, Instruction* _pRight); 30 | BasicBlock& operator!=(BasicBlock& _left, Instruction& _right); 31 | 32 | BasicBlock& operator<(BasicBlock& _left, Instruction* _pRight); 33 | BasicBlock& operator<(BasicBlock& _left, Instruction& _right); 34 | 35 | BasicBlock& operator<=(BasicBlock& _left, Instruction* _pRight); 36 | BasicBlock& operator<=(BasicBlock& _left, Instruction& _right); 37 | 38 | BasicBlock& operator>(BasicBlock& _left, Instruction* _pRight); 39 | BasicBlock& operator>(BasicBlock& _left, Instruction& _right); 40 | 41 | BasicBlock& operator>=(BasicBlock& _left, Instruction* _pRight); 42 | BasicBlock& operator>=(BasicBlock& _left, Instruction& _right); 43 | } // !ops 44 | } // !spvgentwo -------------------------------------------------------------------------------- /lib/include/spvgentwo/Reader.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace spvgentwo 4 | { 5 | class IReader 6 | { 7 | public: 8 | // return value TRUE indicates success, FALSE fail or EOF 9 | virtual bool get(unsigned int& _word) = 0; 10 | virtual ~IReader() = default; 11 | }; 12 | } // !spvgentwo -------------------------------------------------------------------------------- /lib/include/spvgentwo/SpvGenTwo.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // include all relevant headers 4 | 5 | // base 6 | #include "Type.h" 7 | #include "Constant.h" 8 | 9 | #include "Operand.h" 10 | #include "Instruction.h" 11 | #include "BasicBlock.h" 12 | #include "Function.h" 13 | #include "EntryPoint.h" 14 | #include "Module.h" 15 | 16 | // additional functions 17 | #include "Operators.h" 18 | #include "Allocator.h" 19 | #include "Logger.h" 20 | #include "Writer.h" 21 | #include "Reader.h" 22 | #include "TypeInferenceAndValiation.h" 23 | #include "TypeAlias.h" 24 | 25 | // extensions 26 | #include "GLSL450Instruction.h" 27 | 28 | // templates 29 | #include "Templates.h" -------------------------------------------------------------------------------- /lib/include/spvgentwo/String.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Vector.h" 4 | #include "FNV1aHasher.h" 5 | 6 | namespace spvgentwo 7 | { 8 | // includes string terminator 9 | inline constexpr sgt_size_t stringLength(const char* _pStr) 10 | { 11 | if (_pStr == nullptr) 12 | return 0u; 13 | 14 | sgt_size_t i = 0u; 15 | for (; _pStr[i] != '\0'; ++i) {} 16 | return i+1u; 17 | } 18 | 19 | // _outBuffer must have space for at least 10 digits ( max length is log10(UINT_MAX) ~ 9.6 ) 20 | inline constexpr void uintToString(unsigned int _value, char* _outBuffer, unsigned int _bufferLength, unsigned int _base = 10u) 21 | { 22 | constexpr char alpha[] = { '0', '1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' }; 23 | _base = sizeof(alpha) < _base ? sizeof(alpha) : _base; 24 | 25 | unsigned int len = 0u; // count length of our string 26 | for (unsigned int val = _value; val != 0u; val /= _base, ++len) {} 27 | len = _value == 0u ? 1u : len; 28 | len = _bufferLength < len ? _bufferLength : len; 29 | 30 | do 31 | { 32 | _outBuffer[--len] = alpha[_value % _base]; 33 | _value /= _base; 34 | } while (_value != 0 && len > 0u); 35 | } 36 | 37 | class String : public Vector 38 | { 39 | public: 40 | using Vector::Vector; 41 | 42 | virtual ~String() override = default; 43 | 44 | String(const String& _other) : Vector(_other) {} 45 | 46 | constexpr String(String&& _other) noexcept : Vector(stdrep::move(_other)) {} 47 | 48 | String(IAllocator* _pAllocator, const char* _pStr = nullptr, sgt_size_t _length = 0u) : Vector(_pAllocator, _pStr, _length == 0u ? stringLength(_pStr) : _length) {}; 49 | 50 | String& operator=(const String& _other) { Vector::operator=(_other); return *this; } 51 | String& operator=(String&& _other) noexcept { Vector::operator=(stdrep::move(_other)); return *this; } 52 | 53 | String& operator=(const char* _pStr); 54 | 55 | constexpr char* c_str() const { return data(); } 56 | constexpr operator char* () const { return data(); } 57 | 58 | // exclude string terminator 59 | constexpr sgt_size_t size() const { return m_elements == 0u ? 0u : m_elements - 1u; } 60 | constexpr auto end() const { return begin() + size(); } 61 | 62 | constexpr T& back() { return m_pData[m_elements - 2u]; } 63 | constexpr const T& back() const { return m_pData[m_elements - 2u]; } 64 | 65 | String substr(sgt_size_t _offset, sgt_size_t _length); 66 | 67 | String operator+(const String& _other) const; 68 | String operator+(const char* _pStr) const; 69 | 70 | String& operator+=(const String& _other); 71 | String& operator+=(const char* _pStr); 72 | 73 | String& append(const char* _pStr, sgt_size_t _length = 0u); 74 | 75 | template 76 | Iterator insert(sgt_size_t _pos, const char(&_array)[N]) { return Vector::insert(_pos, _array, N); } 77 | 78 | // don't insert the additional string terminator from _pCStr 79 | Iterator insert(sgt_size_t _pos, const char* _pCStr, sgt_size_t _length = 0u); 80 | 81 | Iterator insert(sgt_size_t _pos, const String& _str) { return Vector::insert(_pos, _str.data(), _str.size()); } 82 | 83 | bool operator==(const String& _other) const; 84 | bool operator==(const char* _pStr) const; 85 | 86 | template 87 | T* emplace_back(Arg&& _arg); 88 | 89 | // emplace one element per argument 90 | template 91 | void emplace_back_args(const T& _first, Args&& ..._tail); 92 | }; 93 | 94 | template<> 95 | struct Hasher 96 | { 97 | Hash64 operator()(const String& _str, Hash64 _seed = detail::Offset) const noexcept 98 | { 99 | FNV1aHasher h(_seed); 100 | h.add(_str.data(), _str.size()); 101 | return h; 102 | } 103 | }; 104 | 105 | template 106 | inline String::T* String::emplace_back(Arg&& _arg) 107 | { 108 | if(m_elements == 0 || Vector::back() != '\0') 109 | { 110 | Vector::emplace_back(stdrep::forward(_arg)); 111 | Vector::emplace_back('\0'); 112 | return &back(); 113 | } 114 | else if (reserve(m_elements+1u)) 115 | { 116 | m_pData[m_elements] = m_pData[m_elements - 1]; // move string terminator to last element 117 | m_pData[m_elements - 1] = stdrep::forward(_arg); // new element is 2nd last 118 | ++m_elements; 119 | return &back(); 120 | } 121 | return nullptr; 122 | } 123 | 124 | template 125 | inline void String::emplace_back_args(const T& _first, Args && ..._tail) 126 | { 127 | String::emplace_back(_first); 128 | 129 | if constexpr (sizeof...(_tail) > 0) 130 | { 131 | String::emplace_back_args(stdrep::forward(_tail)...); 132 | } 133 | } 134 | } -------------------------------------------------------------------------------- /lib/include/spvgentwo/Templates.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ModuleTemplate.inl" 4 | #include "EntryPointTemplate.inl" 5 | #include "FunctionTemplate.inl" 6 | #include "BasicBlockTemplate.inl" 7 | #include "InstructionTemplate.inl" -------------------------------------------------------------------------------- /lib/include/spvgentwo/TypeAlias.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Type.h" 4 | 5 | namespace spvgentwo 6 | { 7 | namespace glsl 8 | { 9 | using vec2 = vector_t; 10 | using vec3 = vector_t; 11 | using vec4 = vector_t; 12 | 13 | using dvec2 = vector_t; 14 | using dvec3 = vector_t; 15 | using dvec4 = vector_t; 16 | 17 | using ivec2 = vector_t; 18 | using ivec3 = vector_t; 19 | using ivec4 = vector_t; 20 | 21 | using uvec2 = vector_t; 22 | using uvec3 = vector_t; 23 | using uvec4 = vector_t; 24 | 25 | using bvec2 = vector_t; 26 | using bvec3 = vector_t; 27 | using bvec4 = vector_t; 28 | 29 | using mat2 = matrix_t; 30 | using mat3 = matrix_t; 31 | using mat4 = matrix_t; 32 | } // !glsl 33 | 34 | namespace hlsl 35 | { 36 | using float2 = vector_t; 37 | using float3 = vector_t; 38 | using float4 = vector_t; 39 | 40 | using double2 = vector_t; 41 | using double3 = vector_t; 42 | using double4 = vector_t; 43 | 44 | using int2 = vector_t; 45 | using int3 = vector_t; 46 | using int4 = vector_t; 47 | 48 | using uint2 = vector_t; 49 | using uint3 = vector_t; 50 | using uint4 = vector_t; 51 | 52 | using float2x2 = matrix_t; 53 | using float3x3 = matrix_t; 54 | using float4x4 = matrix_t; 55 | } // !hlsl 56 | } // !spvgentwo -------------------------------------------------------------------------------- /lib/include/spvgentwo/TypeInferenceAndValiation.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace spvgentwo 4 | { 5 | // forward delcs 6 | class Instruction; 7 | 8 | class ITypeInferenceAndVailation 9 | { 10 | public: 11 | // some opXXXX functions already infer a result type before passing it to makeOp(), this function allows to override this behaviour 12 | // so that inferResultType() of this instance is call anyway 13 | virtual bool overridePredefinedResultType() const { return false; } 14 | 15 | // returns a new Instruction* to a opType based on _instr opcode and operands 16 | virtual Instruction* inferResultType(const Instruction& _instr) const; 17 | 18 | // return true if operands match opcode 19 | virtual bool validateOperands(const Instruction& _instr) const; 20 | }; 21 | } //!spvgentwo -------------------------------------------------------------------------------- /lib/include/spvgentwo/Writer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace spvgentwo 4 | { 5 | class IWriter 6 | { 7 | public: 8 | // append spv word to the output stream 9 | virtual bool put(unsigned int word) = 0; 10 | }; 11 | } // !spvgentwo -------------------------------------------------------------------------------- /lib/source/Allocator.cpp: -------------------------------------------------------------------------------- 1 | #include "spvgentwo/Allocator.h" 2 | 3 | spvgentwo::ScopedAllocation::~ScopedAllocation() 4 | { 5 | if (allocator != nullptr && ptr != nullptr) 6 | { 7 | allocator->deallocate(ptr, size); 8 | } 9 | allocator = nullptr; 10 | } 11 | 12 | spvgentwo::ScopedAllocation spvgentwo::IAllocator::allocateScoped(sgt_size_t _bytes, unsigned int _aligment) 13 | { 14 | return ScopedAllocation(allocate(_bytes, _aligment), _bytes, this); 15 | } 16 | -------------------------------------------------------------------------------- /lib/source/Constant.cpp: -------------------------------------------------------------------------------- 1 | #include "spvgentwo/Constant.h" 2 | #include "spvgentwo/Operand.h" 3 | 4 | spvgentwo::Constant::Constant(IAllocator* _pAllocator) : 5 | m_Type(_pAllocator), 6 | m_Components(_pAllocator), 7 | m_literalData(_pAllocator) 8 | { 9 | } 10 | 11 | spvgentwo::Constant::Constant(const Constant& _other) : 12 | m_Operation(_other.m_Operation), 13 | m_Type(_other.m_Type), 14 | m_Components(_other.m_Components), 15 | m_literalData(_other.m_literalData) 16 | { 17 | } 18 | 19 | spvgentwo::Constant::Constant(Constant&& _other) noexcept: 20 | m_Operation(stdrep::move(_other.m_Operation)), 21 | m_Type(stdrep::move(_other.m_Type)), 22 | m_Components(stdrep::move(_other.m_Components)), 23 | m_literalData(stdrep::move(_other.m_literalData)) 24 | { 25 | } 26 | 27 | spvgentwo::Constant::~Constant() 28 | { 29 | } 30 | 31 | spvgentwo::Constant& spvgentwo::Constant::operator=(const Constant& _other) 32 | { 33 | if (this == &_other) return *this; 34 | 35 | m_Components = _other.m_Components; 36 | m_literalData = _other.m_literalData; 37 | m_Operation = _other.m_Operation; 38 | m_Type = _other.m_Type; 39 | 40 | return *this; 41 | } 42 | 43 | spvgentwo::Constant& spvgentwo::Constant::operator=(Constant&& _other) noexcept 44 | { 45 | if (this == &_other) return *this; 46 | 47 | m_Components = stdrep::move(_other.m_Components); 48 | m_literalData = stdrep::move(_other.m_literalData); 49 | m_Operation = stdrep::move(_other.m_Operation); 50 | m_Type = stdrep::move(_other.m_Type); 51 | 52 | return *this; 53 | } 54 | 55 | spvgentwo::Constant& spvgentwo::Constant::Component() 56 | { 57 | return m_Components.emplace_back(m_Components.getAllocator()); 58 | } 59 | 60 | void spvgentwo::Constant::reset() 61 | { 62 | m_Operation = spv::Op::OpConstantNull; 63 | m_Components.clear(); 64 | m_literalData.clear(); 65 | m_Type.reset(); 66 | } 67 | -------------------------------------------------------------------------------- /lib/source/EntryPoint.cpp: -------------------------------------------------------------------------------- 1 | #include "spvgentwo/EntryPoint.h" 2 | 3 | #include "spvgentwo/InstructionTemplate.inl" 4 | 5 | spvgentwo::EntryPoint::EntryPoint() 6 | { 7 | m_isEntryPoint = true; 8 | } 9 | 10 | spvgentwo::EntryPoint::EntryPoint(Module* _pModule) : 11 | Function(_pModule), 12 | m_EntryPoint(this, spv::Op::OpNop), 13 | m_nameStorage(_pModule->getAllocator()) 14 | { 15 | m_isEntryPoint = true; 16 | } 17 | 18 | spvgentwo::EntryPoint::~EntryPoint() 19 | { 20 | } 21 | 22 | const char* spvgentwo::EntryPoint::getName() const 23 | { 24 | return m_nameStorage.c_str(); 25 | } 26 | 27 | void spvgentwo::EntryPoint::finalizeGlobalInterface(const GlobalInterfaceVersion _version) 28 | { 29 | // remove old interface IDs 30 | auto interface = getInterfaceVariables(); 31 | for (auto it = interface.begin(); it != interface.end();) 32 | { 33 | it = m_EntryPoint.erase(it); 34 | } 35 | 36 | // collect new interface 37 | collectReferencedVariables(*this, m_EntryPoint, _version, m_pAllocator); 38 | } 39 | 40 | spvgentwo::String& spvgentwo::EntryPoint::getNameStorage() 41 | { 42 | return m_nameStorage; 43 | } 44 | 45 | spvgentwo::Instruction* spvgentwo::EntryPoint::finalize(const spv::ExecutionModel _model, const Flag _control, const char* _pEntryPointName) 46 | { 47 | Instruction* pFunc = Function::finalize(_control, _pEntryPointName); 48 | 49 | if (pFunc != nullptr && pFunc->isErrorInstr() == false) 50 | { 51 | m_EntryPoint.opEntryPoint(_model, &m_Function, _pEntryPointName); 52 | m_nameStorage = _pEntryPointName; 53 | } 54 | 55 | return pFunc; 56 | } 57 | 58 | spvgentwo::Range spvgentwo::EntryPoint::getInterfaceVariables() const 59 | { 60 | auto it = skipLiteralString(m_EntryPoint.getFirstActualOperand() + 2u); // skip execution model & entry function id first, then skip over name string 61 | 62 | return { it, m_EntryPoint.end() }; 63 | } 64 | -------------------------------------------------------------------------------- /lib/source/Operand.cpp: -------------------------------------------------------------------------------- 1 | #include "spvgentwo/Operand.h" 2 | #include "spvgentwo/Writer.h" 3 | #include "spvgentwo/Instruction.h" 4 | #include "spvgentwo/BasicBlock.h" 5 | 6 | bool spvgentwo::Operand::write(IWriter& _writer) const 7 | { 8 | switch (type) 9 | { 10 | case Type::Instruction: 11 | return _writer.put(static_cast(instruction->getResultId())); 12 | case Type::BranchTarget: 13 | return _writer.put(static_cast(branchTarget->getLabel()->getResultId())); 14 | case Type::Literal: 15 | return _writer.put(literal.value); 16 | case Type::Id: 17 | return id != InvalidId && _writer.put(static_cast(id)); 18 | default: 19 | return false; 20 | } 21 | } 22 | 23 | spvgentwo::Operand::Operand(const Operand& _other) : 24 | type(_other.type) 25 | { 26 | switch (type) 27 | { 28 | case Type::Instruction: 29 | instruction = _other.instruction; 30 | break; 31 | case Type::BranchTarget: 32 | branchTarget = _other.branchTarget; 33 | break; 34 | case Type::Literal: 35 | literal = _other.literal; 36 | break; 37 | case Type::Id: 38 | id = _other.id; 39 | break; 40 | default: 41 | break; 42 | } 43 | } 44 | 45 | spvgentwo::Operand::Operand(Operand&& _other) noexcept : type(_other.type) 46 | { 47 | switch (type) 48 | { 49 | case Type::Instruction: 50 | instruction = _other.instruction; 51 | break; 52 | case Type::BranchTarget: 53 | branchTarget = _other.branchTarget; 54 | break; 55 | case Type::Literal: 56 | literal = _other.literal; 57 | break; 58 | case Type::Id: 59 | id = _other.id; 60 | break; 61 | default: 62 | break; 63 | } 64 | } 65 | 66 | spvgentwo::Operand& spvgentwo::Operand::operator=(const Operand& _other) 67 | { 68 | if (this == &_other) return *this; 69 | 70 | type = _other.type; 71 | switch (type) 72 | { 73 | case Type::Instruction: 74 | instruction = _other.instruction; 75 | break; 76 | case Type::BranchTarget: 77 | branchTarget = _other.branchTarget; 78 | break; 79 | case Type::Literal: 80 | literal = _other.literal; 81 | break; 82 | case Type::Id: 83 | id = _other.id; 84 | break; 85 | default: 86 | break; 87 | } 88 | 89 | return *this; 90 | } 91 | 92 | spvgentwo::Operand& spvgentwo::Operand::operator=(Operand&& _other) noexcept 93 | { 94 | if (this == &_other) return *this; 95 | 96 | type = _other.type; 97 | switch (type) 98 | { 99 | case Type::Instruction: 100 | instruction = _other.instruction; 101 | break; 102 | case Type::BranchTarget: 103 | branchTarget = _other.branchTarget; 104 | break; 105 | case Type::Literal: 106 | literal = _other.literal; 107 | break; 108 | case Type::Id: 109 | id = _other.id; 110 | break; 111 | default: 112 | break; 113 | } 114 | 115 | return *this; 116 | } 117 | 118 | bool spvgentwo::Operand::operator==(const Operand& _other) const 119 | { 120 | return type == _other.type && 121 | ((type == Type::BranchTarget && branchTarget == _other.branchTarget) || 122 | (type == Type::Instruction && instruction == _other.instruction) || 123 | (type == Type::Literal && literal == _other.literal) || 124 | (type == Type::Id && id == _other.id)); 125 | } 126 | 127 | bool spvgentwo::Operand::operator==(const BasicBlock* _block) const 128 | { 129 | return type == Type::BranchTarget && branchTarget == _block; 130 | } 131 | 132 | bool spvgentwo::Operand::operator==(const Instruction* _instr) const 133 | { 134 | return type == Type::Instruction && instruction == _instr; 135 | } 136 | 137 | bool spvgentwo::Operand::operator==(const literal_t& _value) const 138 | { 139 | return type == Type::Literal && literal == _value; 140 | } 141 | 142 | bool spvgentwo::Operand::operator==(const spv::Id& _resultID) const 143 | { 144 | return type == Type::Id && id == _resultID; 145 | } 146 | 147 | bool spvgentwo::Operand::operator!=(const BasicBlock* _block) const 148 | { 149 | return type != Type::BranchTarget || branchTarget != _block; 150 | } 151 | 152 | bool spvgentwo::Operand::operator!=(const Instruction* _instr) const 153 | { 154 | return type != Type::Instruction || instruction != _instr; 155 | } 156 | 157 | bool spvgentwo::Operand::operator!=(const literal_t& _value) const 158 | { 159 | return type != Type::Literal || literal != _value; 160 | } 161 | 162 | bool spvgentwo::Operand::operator!=(const spv::Id& _resultID) const 163 | { 164 | return type != Type::Id || id != _resultID; 165 | } -------------------------------------------------------------------------------- /lib/source/Operators.cpp: -------------------------------------------------------------------------------- 1 | #include "spvgentwo/Operators.h" 2 | #include "spvgentwo/BasicBlock.h" 3 | 4 | spvgentwo::BasicBlock& spvgentwo::ops::operator!(BasicBlock& _left) 5 | { 6 | return _left.Not(); 7 | } 8 | 9 | spvgentwo::BasicBlock& spvgentwo::ops::operator+(BasicBlock& _left, Instruction* _pRight) 10 | { 11 | return _left.Add(_pRight); 12 | } 13 | 14 | spvgentwo::BasicBlock& spvgentwo::ops::operator+(BasicBlock& _left, Instruction& _right) 15 | { 16 | return _left.Add(&_right); 17 | } 18 | 19 | spvgentwo::BasicBlock& spvgentwo::ops::operator-(BasicBlock& _left, Instruction* _pRight) 20 | { 21 | return _left.Sub(_pRight); 22 | } 23 | 24 | spvgentwo::BasicBlock& spvgentwo::ops::operator-(BasicBlock& _left, Instruction& _right) 25 | { 26 | return _left.Sub(&_right); 27 | } 28 | 29 | spvgentwo::BasicBlock& spvgentwo::ops::operator*(BasicBlock& _left, Instruction* _pRight) 30 | { 31 | return _left.Mul(_pRight); 32 | } 33 | 34 | spvgentwo::BasicBlock& spvgentwo::ops::operator*(BasicBlock& _left, Instruction& _right) 35 | { 36 | return _left.Mul(&_right); 37 | } 38 | 39 | spvgentwo::BasicBlock& spvgentwo::ops::operator/(BasicBlock& _left, Instruction* _pRight) 40 | { 41 | return _left.Div(_pRight); 42 | } 43 | 44 | spvgentwo::BasicBlock& spvgentwo::ops::operator/(BasicBlock& _left, Instruction& _right) 45 | { 46 | return _left.Div(&_right); 47 | } 48 | 49 | spvgentwo::BasicBlock& spvgentwo::ops::operator==(BasicBlock& _left, Instruction* _pRight) 50 | { 51 | return _left.Equal(_pRight); 52 | } 53 | 54 | spvgentwo::BasicBlock& spvgentwo::ops::operator==(BasicBlock& _left, Instruction& _right) 55 | { 56 | return _left.Equal(&_right); 57 | } 58 | 59 | spvgentwo::BasicBlock& spvgentwo::ops::operator!=(BasicBlock& _left, Instruction* _pRight) 60 | { 61 | return _left.NotEqual(_pRight); 62 | } 63 | 64 | spvgentwo::BasicBlock& spvgentwo::ops::operator!=(BasicBlock& _left, Instruction& _right) 65 | { 66 | return _left.NotEqual(&_right); 67 | } 68 | 69 | spvgentwo::BasicBlock& spvgentwo::ops::operator<(BasicBlock& _left, Instruction* _pRight) 70 | { 71 | return _left.Less(_pRight); 72 | } 73 | 74 | spvgentwo::BasicBlock& spvgentwo::ops::operator<(BasicBlock& _left, Instruction& _right) 75 | { 76 | return _left.Less(&_right); 77 | } 78 | 79 | spvgentwo::BasicBlock& spvgentwo::ops::operator<=(BasicBlock& _left, Instruction* _pRight) 80 | { 81 | return _left.LessEqual(_pRight); 82 | } 83 | 84 | spvgentwo::BasicBlock& spvgentwo::ops::operator<=(BasicBlock& _left, Instruction& _right) 85 | { 86 | return _left.LessEqual(&_right); 87 | } 88 | 89 | spvgentwo::BasicBlock& spvgentwo::ops::operator>(BasicBlock& _left, Instruction* _pRight) 90 | { 91 | return _left.Greater(_pRight); 92 | } 93 | 94 | spvgentwo::BasicBlock& spvgentwo::ops::operator>(BasicBlock& _left, Instruction& _right) 95 | { 96 | return _left.Greater(&_right); 97 | } 98 | 99 | spvgentwo::BasicBlock& spvgentwo::ops::operator>=(BasicBlock& _left, Instruction* _pRight) 100 | { 101 | return _left.GreaterEqual(_pRight); 102 | } 103 | 104 | spvgentwo::BasicBlock& spvgentwo::ops::operator>=(BasicBlock& _left, Instruction& _right) 105 | { 106 | return _left.GreaterEqual(&_right); 107 | } -------------------------------------------------------------------------------- /lib/source/String.cpp: -------------------------------------------------------------------------------- 1 | #include "spvgentwo/String.h" 2 | 3 | spvgentwo::String& spvgentwo::String::operator=(const char* _pStr) 4 | { 5 | const sgt_size_t length = stringLength(_pStr); 6 | 7 | if (reserve(length)) 8 | { 9 | for (sgt_size_t i = 0; i < length; ++i) 10 | { 11 | m_pData[i] = _pStr[i]; 12 | } 13 | 14 | m_elements = length; 15 | } 16 | 17 | return *this; 18 | } 19 | 20 | spvgentwo::String spvgentwo::String::substr(sgt_size_t _offset, sgt_size_t _length) 21 | { 22 | const sgt_size_t available = m_elements - _offset; 23 | return String(m_pAllocator, m_pData+_offset, _length > available ? available : _length); 24 | } 25 | 26 | spvgentwo::String spvgentwo::String::operator+(const String& _other) const 27 | { 28 | String both(m_pAllocator, m_elements + _other.m_elements); 29 | both = *this; // assign first half 30 | both += _other; // assign new part 31 | return both; 32 | } 33 | 34 | spvgentwo::String spvgentwo::String::operator+(const char* _pStr) const 35 | { 36 | String both(m_pAllocator); 37 | both = *this; // assign first half 38 | both.append(_pStr); // assign new part 39 | return both; 40 | } 41 | 42 | spvgentwo::String& spvgentwo::String::operator+=(const String& _other) 43 | { 44 | if (reserve(m_elements + _other.m_elements)) 45 | { 46 | const sgt_size_t offset = m_elements > 0 && m_pData[m_elements - 1u] == '\0' ? m_elements - 1u : m_elements; 47 | for (sgt_size_t i = 0; i < _other.m_elements; ++i) 48 | { 49 | m_pData[offset + i] = _other[i]; 50 | } 51 | m_elements = offset + _other.m_elements; 52 | } 53 | return *this; 54 | } 55 | 56 | spvgentwo::String& spvgentwo::String::operator+=(const char* _pStr) 57 | { 58 | return append(_pStr); 59 | } 60 | 61 | spvgentwo::String& spvgentwo::String::append(const char* _pStr, sgt_size_t _length) 62 | { 63 | const sgt_size_t length = _length == 0u ? stringLength(_pStr) : _length; 64 | if (reserve(m_elements + length)) 65 | { 66 | // we only want one string terminator, overwrite the old one an append 67 | const sgt_size_t offset = m_elements > 0 && m_pData[m_elements - 1u] == '\0' ? m_elements - 1u : m_elements; 68 | for (sgt_size_t i = 0u; i < length; ++i) 69 | { 70 | m_pData[offset + i] = _pStr[i]; 71 | } 72 | m_elements = offset + length; 73 | } 74 | return *this; 75 | } 76 | 77 | bool spvgentwo::String::operator==(const String& _other) const 78 | { 79 | if (m_elements == _other.m_elements) 80 | { 81 | for (sgt_size_t i = 0u; i < m_elements; ++i) 82 | { 83 | if (m_pData[i] != _other.m_pData[i]) 84 | return false; 85 | } 86 | return true; 87 | } 88 | return false; 89 | } 90 | 91 | bool spvgentwo::String::operator==(const char* _pStr) const 92 | { 93 | const sgt_size_t length = stringLength(_pStr); 94 | if (m_elements == length) 95 | { 96 | for (sgt_size_t i = 0; i < m_elements; ++i) 97 | { 98 | if (m_pData[i] != _pStr[i]) 99 | return false; 100 | } 101 | return true; 102 | } 103 | return false; 104 | } 105 | 106 | spvgentwo::String::Iterator spvgentwo::String::insert(sgt_size_t _pos, const char* _pCStr, sgt_size_t _length) 107 | { 108 | auto ret = Vector::insert(_pos, _pCStr, _length == 0u ? (stringLength(_pCStr) - 1u) : _length); 109 | 110 | if (m_elements != 0u && m_pData[m_elements - 1u] != '\0') 111 | { 112 | // append string terminator if necessar 113 | Vector::insert(m_elements, '\0'); 114 | } 115 | return ret; 116 | } -------------------------------------------------------------------------------- /link/include/link/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rAzoR8/SpvGenTwo/4bdddca967fb01311076c67ad8dce9a6d6ef7730/link/include/link/.gitkeep -------------------------------------------------------------------------------- /misc/dis.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rAzoR8/SpvGenTwo/4bdddca967fb01311076c67ad8dce9a6d6ef7730/misc/dis.PNG -------------------------------------------------------------------------------- /misc/linker.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rAzoR8/SpvGenTwo/4bdddca967fb01311076c67ad8dce9a6d6ef7730/misc/linker.PNG -------------------------------------------------------------------------------- /misc/refl.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rAzoR8/SpvGenTwo/4bdddca967fb01311076c67ad8dce9a6d6ef7730/misc/refl.PNG -------------------------------------------------------------------------------- /misc/spvgentwo.cppcheck: -------------------------------------------------------------------------------- 1 | 2 | 3 | spvgentwo-cppcheck-build-dir 4 | Unspecified 5 | false 6 | true 7 | true 8 | 2 9 | 100 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /refl/include/refl/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rAzoR8/SpvGenTwo/4bdddca967fb01311076c67ad8dce9a6d6ef7730/refl/include/refl/.gitkeep -------------------------------------------------------------------------------- /test/catch2.runsettings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 4 6 | .\TestResults 7 | 60000 8 | 9 | 10 | 11 | 12 | 13 | 14 | on 15 | 500 16 | ^SpvGenTwoTests 17 | true 18 | Verbose 19 | StatsOnly 20 | ShortInfo 21 | , 22 | 20000 23 | 24 | Executable 25 | 26 | 27 | -------------------------------------------------------------------------------- /test/include/test/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rAzoR8/SpvGenTwo/4bdddca967fb01311076c67ad8dce9a6d6ef7730/test/include/test/.gitkeep -------------------------------------------------------------------------------- /test/include/test/Modules.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "spvgentwo/Module.h" 4 | 5 | namespace test 6 | { 7 | spvgentwo::Module constants(spvgentwo::IAllocator* _pAllocator, spvgentwo::ILogger* _pLogger); 8 | spvgentwo::Module types(spvgentwo::IAllocator* _pAllocator, spvgentwo::ILogger* _pLogger); 9 | 10 | spvgentwo::Module oldInstrTest(spvgentwo::IAllocator* _pAllocator, spvgentwo::ILogger* _pLogger); 11 | spvgentwo::Module imageRead(spvgentwo::IAllocator* _pAllocator, spvgentwo::ILogger* _pLogger); 12 | 13 | spvgentwo::Module computeShader(spvgentwo::IAllocator* _pAllocator, spvgentwo::ILogger* _pLogger); 14 | spvgentwo::Module controlFlow(spvgentwo::IAllocator* _pAllocator, spvgentwo::ILogger* _pLogger); 15 | spvgentwo::Module expressionGraph(spvgentwo::IAllocator* _pAllocator, spvgentwo::ILogger* _pLogger); 16 | spvgentwo::Module extensions(spvgentwo::IAllocator* _pAllocator, spvgentwo::ILogger* _pLogger); 17 | spvgentwo::Module fragmentShader(spvgentwo::IAllocator* _pAllocator, spvgentwo::ILogger* _pLogger); 18 | spvgentwo::Module functionCall(spvgentwo::IAllocator* _pAllocator, spvgentwo::ILogger* _pLogger); 19 | spvgentwo::Module geometryShader(spvgentwo::IAllocator* _pAllocator, spvgentwo::ILogger* _pLogger); 20 | spvgentwo::Module physicalStorageTest(spvgentwo::IAllocator* _pAllocator, spvgentwo::ILogger* _pLogger); 21 | spvgentwo::Module bitInstructionTest(spvgentwo::IAllocator* _pAllocator, spvgentwo::ILogger* _pLogger); 22 | 23 | spvgentwo::Module linkageLibA(spvgentwo::IAllocator* _pAllocator, spvgentwo::ILogger* _pLogger); 24 | spvgentwo::Module linkageLibB(spvgentwo::IAllocator* _pAllocator, spvgentwo::ILogger* _pLogger); 25 | spvgentwo::Module linkageConsumer(spvgentwo::IAllocator* _pAllocator, spvgentwo::ILogger* _pLogger); 26 | bool linkageLinked(const spvgentwo::Module& _libA, const spvgentwo::Module& _libB, spvgentwo::Module& _consumer, spvgentwo::IAllocator* _pAllocator, const spvgentwo::Grammar* _pGrammar); 27 | } // !test 28 | -------------------------------------------------------------------------------- /test/include/test/SpvValidator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "spvgentwo/Module.h" 4 | 5 | namespace test 6 | { 7 | class SpvValidator 8 | { 9 | public: 10 | SpvValidator(const spvgentwo::Grammar& _gram); 11 | 12 | bool validate( const spvgentwo::Module& _module ); 13 | private: 14 | const spvgentwo::Grammar& m_gram; 15 | }; 16 | } -------------------------------------------------------------------------------- /test/include/test/TestLogger.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "spvgentwo/Logger.h" 4 | 5 | namespace test 6 | { 7 | class TestLogger : public spvgentwo::ILogger 8 | { 9 | public: 10 | TestLogger(); 11 | }; 12 | } -------------------------------------------------------------------------------- /test/source/AllocatorTests.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "common/HeapAllocator.h" 5 | using namespace spvgentwo; 6 | 7 | TEST_CASE( "alignment", "[HeapAllocator]" ) { 8 | HeapAllocator alloc; 9 | 10 | auto test = [&alloc](unsigned bytes) 11 | { 12 | void* p = alloc.allocate(10u, 4); 13 | REQUIRE(p != nullptr); 14 | alloc.deallocate(p, bytes); 15 | }; 16 | 17 | test(1); 18 | test(10); 19 | test(10); 20 | test(10); 21 | test(16); 22 | test(1337); 23 | } -------------------------------------------------------------------------------- /test/source/BitInstructions.cpp: -------------------------------------------------------------------------------- 1 | #include "test/Modules.h" 2 | #include "spvgentwo/Templates.h" 3 | 4 | using namespace spvgentwo; 5 | 6 | spvgentwo::Module test::bitInstructionTest(spvgentwo::IAllocator* _pAllocator, spvgentwo::ILogger* _pLogger) 7 | { 8 | Module module(_pAllocator, _pLogger); 9 | module.addCapability(spv::Capability::Shader); 10 | module.addCapability(spv::Capability::BitInstructions); 11 | module.addExtension(spv::Extension::SPV_KHR_bit_instructions); 12 | 13 | Function& main = module.addEntryPoint(spv::ExecutionModel::Vertex, u8"main"); 14 | BasicBlock& bb = *main; 15 | { 16 | Instruction* const base = module.constant(0b1111000011110000, false, "base"); 17 | Instruction* const insert = module.constant(0b0000111100000000, false, "insert"); 18 | Instruction* const offset = module.constant(4, false, "offset"); 19 | Instruction* const count = module.constant(4u, false, "count"); 20 | 21 | Instruction* const base3 = bb->opCompositeConstruct(module.type>(), base, insert, offset); 22 | Instruction* const insert3 = bb->opCompositeConstruct(module.type>(), insert, base, offset); 23 | 24 | bb->opBitFieldInsert(base, insert, offset, count); 25 | bb->opBitFieldSExtract(base, offset, count); 26 | bb->opBitFieldUExtract(base, offset, count); 27 | bb->opBitReverse(base); 28 | 29 | bb->opBitFieldInsert(base3, insert3, offset, count); 30 | bb->opBitFieldSExtract(base3, offset, count); 31 | bb->opBitFieldUExtract(base3, offset, count); 32 | bb->opBitReverse(base3); 33 | } 34 | 35 | bb.returnValue(); 36 | 37 | return module; 38 | } 39 | -------------------------------------------------------------------------------- /test/source/ComputeShader.cpp: -------------------------------------------------------------------------------- 1 | #include "test/Modules.h" 2 | #include "spvgentwo/Templates.h" 3 | 4 | using namespace spvgentwo; 5 | 6 | spvgentwo::Module test::computeShader(spvgentwo::IAllocator* _pAllocator, spvgentwo::ILogger* _pLogger) 7 | { 8 | // create a new spir-v module 9 | Module module(_pAllocator, _pLogger); 10 | 11 | // configure capabilities and extensions 12 | module.addCapability(spv::Capability::Shader); 13 | module.addCapability(spv::Capability::Kernel); 14 | module.addCapability(spv::Capability::Addresses); 15 | module.addCapability(spv::Capability::Int64); 16 | 17 | // global variables 18 | 19 | //struct 20 | //{ 21 | // float x[]; 22 | //}; 23 | 24 | Type stArray = module.newType().Struct(); 25 | // add runt-time float array 26 | stArray.Member().RuntimeArrayElement().Float(); 27 | 28 | Instruction* uniRtArray = module.uniform(stArray, "u_RtArray"); 29 | 30 | using complex = array_t, 8>; 31 | 32 | Instruction* uniArray = module.uniform("u_Array"); 33 | 34 | // void main(); entry point 35 | { 36 | EntryPoint& entry = module.addEntryPoint(spv::ExecutionModel::Kernel, "main"); 37 | 38 | BasicBlock& bb = *entry; // get entry block to this function 39 | 40 | Instruction* arLength = bb->opArrayLength(uniRtArray, 0u); 41 | 42 | Instruction* f3 = entry.variable(make_vector(1.f, -3.f, 1.f / -0.01f)); 43 | f3 = bb->opLoad(f3); 44 | 45 | Instruction* g3 = bb->Div(f3, module.constant(-0.0f)); 46 | 47 | bb->opIsNan(g3); 48 | bb->opIsInf(f3); 49 | bb->opIsFinite(g3); 50 | bb->opIsNormal(f3); 51 | bb->opSignBitSet(g3); 52 | bb->opOrdered(f3, g3); 53 | bb->opUnordered(f3, g3); 54 | 55 | Instruction* target = entry.variable("func_Array"); 56 | auto* zero = module.constant(1ull); 57 | bb->opCopyMemorySized(target, uniArray, zero); 58 | 59 | bb->opSatConvertSToU(module.constant(13u)); 60 | bb->opSatConvertUToS(module.constant(1337u)); 61 | 62 | Instruction* intVec1 = module.constant(make_vector(0u, 3u, 4u)); 63 | Instruction* intVec2 = module.constant(make_vector(3u, 0u, 4u)); 64 | 65 | bb->opIAddCarry(intVec1, intVec2); 66 | bb->opISubBorrow(intVec2, intVec1); 67 | bb->opUMulExtended(intVec1, intVec1); 68 | 69 | bb.returnValue(); 70 | } 71 | 72 | return module; 73 | } 74 | -------------------------------------------------------------------------------- /test/source/ConstantFundamentals.cpp: -------------------------------------------------------------------------------- 1 | #include "spvgentwo/Constant.h" 2 | #include "spvgentwo/Module.h" 3 | #include "spvgentwo/Grammar.h" 4 | #include "spvgentwo/Templates.h" 5 | 6 | #include "common/HeapAllocator.h" 7 | 8 | #include 9 | #include "test/SpvValidator.h" 10 | #include "test/TestLogger.h" 11 | 12 | using namespace spvgentwo; 13 | 14 | namespace { 15 | HeapAllocator g_alloc; 16 | test::TestLogger g_logger; 17 | Grammar g_gram(&g_alloc); 18 | test::SpvValidator g_validator(g_gram); 19 | 20 | inline Constant constant() { return Constant(&g_alloc); } 21 | 22 | bool valid(spvgentwo::Module&& _module) 23 | { 24 | _module.finalize(&g_gram); 25 | return g_validator.validate(_module); 26 | } 27 | 28 | bool valid(spvgentwo::Module& _module) 29 | { 30 | _module.finalize(&g_gram); 31 | return g_validator.validate(_module); 32 | } 33 | } 34 | 35 | TEST_CASE("Data consistency", "[Constants]") 36 | { 37 | auto testData = [](auto val) 38 | { 39 | using T = decltype(val); 40 | Constant c(constant().make(val)); 41 | auto& data = c.getData(); 42 | REQUIRE(data.size() > 0); 43 | REQUIRE(data.size() * sizeof(unsigned int) >= sizeof(T)); 44 | const T* ptr = c.template getDataAs(); 45 | CHECK(ptr != nullptr); 46 | if ( ptr != nullptr ) 47 | { 48 | T v = *ptr; 49 | CHECK(v == val); 50 | } 51 | }; 52 | 53 | testData((char)55); 54 | testData((char)-13); 55 | testData((unsigned char)255); 56 | testData((short)4900); 57 | testData((short)-1337); 58 | testData((unsigned short)0xffff); 59 | testData((unsigned short)12345); 60 | testData(85301); 61 | testData(-959999); 62 | testData(0xffffffff); 63 | testData(0xffffffffu); 64 | testData(0x1u); 65 | testData(-0xffffffffll); 66 | testData(0xffffffffffffffffllu); 67 | testData(-42.0f); 68 | testData(-0.0f); 69 | testData(-0.1 / 0.0000000000455667); 70 | } 71 | 72 | TEST_CASE("Create constant", "[Constants]") 73 | { 74 | Module module(&g_alloc, &g_logger); 75 | module.setMemoryModel(spv::AddressingModel::Logical, spv::MemoryModel::Simple); 76 | module.addCapability(spv::Capability::Shader); 77 | 78 | Instruction* instr = module.constant(1337); 79 | REQUIRE(1337 == *instr->getConstant()->getDataAs()); 80 | 81 | EntryPoint& ep = module.addEntryPoint(spv::ExecutionModel::Fragment, "main"); 82 | ep.addExecutionMode(spv::ExecutionMode::OriginUpperLeft); 83 | 84 | BasicBlock& bb = *ep; 85 | bb.Add(instr, instr); 86 | bb.returnValue(); 87 | 88 | REQUIRE(valid(module)); 89 | } -------------------------------------------------------------------------------- /test/source/Constants.cpp: -------------------------------------------------------------------------------- 1 | #include "test/Modules.h" 2 | #include "spvgentwo/Templates.h" 3 | 4 | using namespace spvgentwo; 5 | 6 | spvgentwo::Module test::constants(spvgentwo::IAllocator* _pAllocator, spvgentwo::ILogger* _pLogger) 7 | { 8 | Module module(_pAllocator, _pLogger); 9 | module.addCapability(spv::Capability::Shader); 10 | module.addCapability(spv::Capability::GenericPointer); 11 | module.addCapability(spv::Capability::LiteralSampler); 12 | module.addCapability(spv::Capability::Float64); 13 | module.addCapability(spv::Capability::Int16); 14 | module.addCapability(spv::Capability::Int8); 15 | 16 | Function& main = module.addEntryPoint(spv::ExecutionModel::Vertex, u8"main"); 17 | BasicBlock& bb = *main; 18 | 19 | // using addConstant() manually: 20 | { 21 | Constant myConst = module.newConstant(); 22 | 23 | // manual constant setup 24 | myConst.addData(123u); 25 | myConst.setType(); 26 | myConst.setOperation(spv::Op::OpConstant); 27 | 28 | // add constant to cache/map and retrieve generated OpConstantXXX instruction 29 | Instruction* inst = module.addConstant(myConst); 30 | 31 | myConst.reset(); // clear data and type for reuse 32 | 33 | // make infers type, data and operation based on value passed 34 | myConst.make(1337.f); 35 | inst = module.addConstant(myConst); 36 | 37 | // extract constant data 1337.f 38 | const float* val = inst->getConstant()->getDataAs(); 39 | } 40 | 41 | // use module constant() 42 | { 43 | // create a vec3 44 | Instruction* vecconst = module.constant(make_vector(1.f, 2.f, 3.f)); 45 | vecconst = module.constant(make_vector(-1.0, 32.0)); 46 | 47 | // create a literal sampler constant 48 | Instruction* samplerconst = module.constant(const_sampler_t{ spv::SamplerAddressingMode::ClampToEdge, ConstantSamplerCoordMode::UnNormalized, spv::SamplerFilterMode::Linear}); 49 | 50 | // create a nullptr constant for unsigned int pointer 51 | Instruction* nullptrconst = module.constant(const_null_t{}); 52 | } 53 | 54 | // specialization constants 55 | { 56 | // integer specialization constant with value 42 57 | Instruction* intconst = module.constant(42u, true); 58 | 59 | //same as above, just a bit shorter 60 | Instruction* s2 = module.specConstant(3u); 61 | 62 | // add new constant without added a Constant descriptor, just the instruction 63 | // initialize the new instructions as UDiv operation 64 | // finally convert this UDiv operation to OpSpecConstantOp instruction 65 | Instruction* specOp = module.addConstantInstr()->opUDiv(intconst, s2)->toSpecOp(); 66 | 67 | // same as above but with explicit opSpecConstantOp 68 | specOp = module.addConstantInstr()->opSpecConstantOp(module.type(), spv::Op::OpIMul, s2, intconst); 69 | 70 | // two short 'short' vectors 71 | Instruction* vecconst1 = module.constant(make_vector((short)8, (short)-16), true); 72 | Instruction* vecconst2 = module.constant(make_vector((short)-32, (short)64), true); 73 | specOp = module.addConstantInstr()->opIMul(vecconst1, vecconst2)->toSpecOp(); 74 | 75 | // test 8-bit integers 76 | Instruction* const1 = module.constant((char)4, true); 77 | Instruction* const2 = module.constant((char)-3, true); 78 | specOp = module.addConstantInstr()->opIMul(const1, const2)->toSpecOp(); 79 | } 80 | 81 | bb.returnValue(); 82 | 83 | return module; 84 | } 85 | -------------------------------------------------------------------------------- /test/source/ControlFlow.cpp: -------------------------------------------------------------------------------- 1 | #include "test/Modules.h" 2 | #include "spvgentwo/Operators.h" 3 | #include "common/ControlFlowGraph.h" 4 | #include "spvgentwo/Templates.h" 5 | 6 | using namespace spvgentwo; 7 | using namespace ops; 8 | 9 | spvgentwo::Module test::controlFlow(spvgentwo::IAllocator* _pAllocator, spvgentwo::ILogger* _pLogger) 10 | { 11 | // create a new spir-v module 12 | Module module(_pAllocator, _pLogger); 13 | 14 | // configure capabilities and extensions 15 | module.addCapability(spv::Capability::Shader); 16 | module.setMemoryModel(spv::AddressingModel::Logical, spv::MemoryModel::Simple); 17 | 18 | // uniforms 19 | Instruction* uniOffset = module.uniform(u8"u_Offset"); 20 | 21 | Function& loopFunc = module.addFunction(u8"loop", spv::FunctionControlMask::Const); 22 | { 23 | Instruction* one = module.constant(1); 24 | Instruction* loopCount = module.constant(10); 25 | Instruction* varI = loopFunc.variable(0); 26 | Instruction* varSum = loopFunc.variable(1.1f); 27 | 28 | BasicBlock& merge = (*loopFunc).Loop([&](BasicBlock& cond) -> Instruction* 29 | { 30 | auto i = cond->opLoad(varI); 31 | return cond < loopCount; // cond is translated to the last used instruction -> i < loopCount 32 | }, [&](BasicBlock& inc) 33 | { 34 | auto i = inc->opLoad(varI); 35 | i = inc.Add(i, one); 36 | inc->opStore(varI, i); // i++ 37 | }, [&](BasicBlock& body) 38 | { 39 | auto s = body->opLoad(varSum); 40 | s = body.Mul(s, s); 41 | body->opStore(varSum, s); // sum += sum 42 | }); 43 | 44 | auto s = merge->opLoad(varSum); 45 | 46 | merge.returnValue(s); 47 | 48 | ControlFlowGraph<> cfg(loopFunc); 49 | } 50 | 51 | // void entryPoint(); 52 | { 53 | EntryPoint& entry = module.addEntryPoint(spv::ExecutionModel::Fragment, "main"); 54 | entry.addExecutionMode(spv::ExecutionMode::OriginUpperLeft); 55 | 56 | BasicBlock& bb = *entry; 57 | 58 | Instruction* offset = bb->opLoad(uniOffset); 59 | Instruction* value = bb->opLoad(entry.variable(42, "value")); // function scope variable 60 | 61 | Instruction* cond = bb.Less(offset, value); // offset < 42 62 | 63 | Instruction* res1 = nullptr; 64 | Instruction* res2 = nullptr; 65 | 66 | BasicBlock& merge = bb.If(cond, [&](BasicBlock& trueBB) 67 | { 68 | res1 = trueBB.Add(value, offset) * value; 69 | }, [&](BasicBlock& falseBB) 70 | { 71 | res2 = falseBB.Sub(value, offset); 72 | }); 73 | 74 | Instruction* res = merge->opPhi(res1, res2); 75 | 76 | merge->call(&loopFunc); 77 | merge->opReturn(); 78 | } 79 | 80 | return module; 81 | } 82 | -------------------------------------------------------------------------------- /test/source/ExpressionGraph.cpp: -------------------------------------------------------------------------------- 1 | #include "test/Modules.h" 2 | #include "common/ExprGraph.h" 3 | 4 | #include "spvgentwo/Templates.h" 5 | 6 | using namespace spvgentwo; 7 | 8 | namespace { 9 | 10 | enum class Op 11 | { 12 | Nop, 13 | Var, 14 | Const, 15 | Add, 16 | Sub, 17 | Mul, 18 | Div 19 | }; 20 | 21 | struct MyExpr 22 | { 23 | const char* name = nullptr; 24 | BasicBlock& bb; 25 | Op op = Op::Nop; 26 | Instruction* result = nullptr; 27 | 28 | void operator()( const List& _inputs, [[maybe_unused]] const List& _outputs ) 29 | { 30 | Instruction* lhs = _inputs.empty() ? nullptr : _inputs.front()->result; 31 | Instruction* rhs = _inputs.size() == 2u ? _inputs.back()->result : nullptr; 32 | 33 | switch( op ) 34 | { 35 | case Op::Nop: 36 | bb->opNop(); 37 | break; 38 | case Op::Var: 39 | break; 40 | case Op::Const: 41 | // nothing to do 42 | break; 43 | case Op::Add: 44 | result = bb->Add( lhs, rhs ); 45 | break; 46 | case Op::Sub: 47 | result = bb->Sub( lhs, rhs ); 48 | break; 49 | case Op::Mul: 50 | result = bb->Mul( lhs, rhs ); 51 | break; 52 | case Op::Div: 53 | result = bb->Div( lhs, rhs ); 54 | break; 55 | default: 56 | break; 57 | } 58 | }; 59 | }; 60 | } 61 | 62 | spvgentwo::Module test::expressionGraph(spvgentwo::IAllocator* _pAllocator, spvgentwo::ILogger* _pLogger) 63 | { 64 | Module module(_pAllocator, _pLogger); 65 | module.addCapability(spv::Capability::Shader); 66 | Function& main = module.addEntryPoint(spv::ExecutionModel::Vertex, u8"mainÜmlautß"); //test utf-8 67 | BasicBlock& bb = *main; 68 | 69 | ExprGraph exprgraph(_pAllocator); 70 | 71 | auto* c1 = exprgraph.emplace(MyExpr{ "c1",bb, Op::Const, module.constant(1337) }); 72 | auto* c2 = exprgraph.emplace(MyExpr{ "c1",bb, Op::Const, module.constant(42) }); 73 | 74 | auto* add = exprgraph.emplace(MyExpr{ "add",bb, Op::Add }); 75 | auto* sub = exprgraph.emplace(MyExpr{ "sub",bb, Op::Sub }); 76 | auto* mul = exprgraph.emplace(MyExpr{ "mul",bb, Op::Mul }); 77 | auto* div = exprgraph.emplace(MyExpr{ "div",bb, Op::Div }); 78 | 79 | // c1 + c1 = add 80 | c1->connect(add); 81 | c2->connect(add); 82 | 83 | // c2 - add = sub 84 | c2->connect(sub); 85 | add->connect(sub); 86 | 87 | // sub * add = mul 88 | sub->connect(mul); 89 | add->connect(mul); 90 | 91 | // c1 / mul = div 92 | c1->connect(div); 93 | mul->connect(div); 94 | 95 | ExprGraph::evaluateRecursive(div); 96 | 97 | bb.returnValue(); 98 | 99 | return module; 100 | } 101 | -------------------------------------------------------------------------------- /test/source/Extensions.cpp: -------------------------------------------------------------------------------- 1 | #include "test/Modules.h" 2 | 3 | #include "spvgentwo/GLSL450Instruction.h" 4 | #include "spvgentwo/Templates.h" 5 | 6 | using namespace spvgentwo; 7 | using namespace ext; 8 | 9 | spvgentwo::Module test::extensions(spvgentwo::IAllocator* _pAllocator, spvgentwo::ILogger* _pLogger) 10 | { 11 | // create a new spir-v module 12 | Module module(_pAllocator, spv::AddressingModel::Logical, spv::MemoryModel::VulkanKHR, _pLogger); 13 | 14 | module.addCapability(spv::Capability::Shader); 15 | module.addCapability(spv::Capability::Float64); 16 | module.addCapability(spv::Capability::InterpolationFunction); 17 | 18 | // use Vulkan Memory Model 19 | module.addCapability(spv::Capability::VulkanMemoryModelKHR); 20 | module.addExtension(u8"SPV_KHR_vulkan_memory_model"); // add extension by string 21 | 22 | // Instruction* extId = module.getExtensionInstructionImport("GLSL.std.450"); 23 | 24 | // void entryPoint(); 25 | { 26 | EntryPoint& entry = module.addEntryPoint(spv::ExecutionModel::Fragment, u8"main"); 27 | entry.addExecutionMode(spv::ExecutionMode::OriginUpperLeft); 28 | BasicBlock& bb = *entry; // get entry block to this function 29 | 30 | // SPV_AMD_gcn_shader example: https://htmlpreview.github.io/?https://github.com/KhronosGroup/SPIRV-Registry/blob/master/extensions/AMD/SPV_AMD_gcn_shader.html 31 | { 32 | module.addExtension(u8"SPV_AMD_gcn_shader"); // adds an OpExtension instruction to the module 33 | 34 | // opcodes taken from extension spec: 35 | const unsigned int CubeFaceCoordAMD = 2; 36 | const unsigned int CubeFaceIndexAMD = 1; 37 | const unsigned int TimeAMD = 3; 38 | 39 | // getExtensionInstructionImport adds extension to the module 40 | // return value ext can be used with Instruction.opExtInst(resultType, extId, opCode, ...); 41 | Instruction* extId = module.addExtensionInstructionImport(u8"SPV_AMD_gcn_shader"); 42 | 43 | /// CubeFaceCoordAMD example: 44 | 45 | // Result Type must be a 2-component 32-bit floating-point vector. 46 | Instruction* vec2 = module.type>(); 47 | 48 | // The operand

must be a pointer to a 3-component 32-bit floating-point vector. 49 | Instruction* P = entry.variable>(); 50 | 51 | // use extId generated by getExtensionInstructionImport (which adds OpExtInstImport) 52 | Instruction* faceCoord = bb->opExtInst(vec2, extId, CubeFaceCoordAMD, P); 53 | 54 | /// TimeAMD example: 55 | 56 | //Use of this instruction requires declaration of the Int64 capability. 57 | //Result Type must be a 64 - bit unsigned integer scalar. 58 | Instruction* uint64 = module.type(); 59 | 60 | // The second variant of opExtInst can direclty add the extension to the module by supplying the extension name "GLSL.std.450" 61 | Instruction* time = bb->opExtInst(uint64, u8"SPV_AMD_gcn_shader", TimeAMD); 62 | } 63 | 64 | // SpvGenTwo comes with GLSL extension instructions (GLSL450Intruction derives from Instruction> 65 | Instruction* const vec3 = module.constant(make_vector(1.f, 2.f, 3.f)); 66 | 67 | // BasicBlock template function ext adds a new Instruction and casts it to type T 68 | // this works as long as T does not add any data members and just functionally extends the Instruction class 69 | Instruction* const cross = bb.ext()->opCross(vec3, vec3); // use GLSL.std.450 extension 70 | 71 | // ext is usefull for extension instruction derivates that are foreign to spvgentwo 72 | // For GLSL 4.5 we have the glsl() and ext() shortcuts: 73 | Instruction* const norm = bb.glsl()->opNormalize(cross); 74 | 75 | Instruction* const ff = bb.ext()->opFaceForward(vec3, norm, cross); 76 | 77 | Instruction* const refl = bb.glsl()->opReflect(vec3, norm); 78 | 79 | Instruction* const eta = bb->opDot(refl, ff); 80 | 81 | Instruction* const refr = bb.glsl()->opRefract(refl, ff, eta); 82 | 83 | Instruction* const len = bb.glsl()->opLength(refr); 84 | 85 | Instruction* const dist = bb.glsl()->opDistance(refl, refr); 86 | 87 | Instruction* const modf = bb.glsl()->opModfStruct(refl); 88 | 89 | Instruction* const frexp = bb.glsl()->opFrexpStruct(refr); 90 | 91 | Instruction* const expInt = bb->opCompositeExtract(frexp, 1u); // extract the exponent 92 | 93 | Instruction* const ldexp = bb.glsl()->opLdexp(ff, expInt); 94 | 95 | Instruction* const vec4 = module.constant(make_vector(1.f, 2.f, 3.f, 4.f)); 96 | 97 | Instruction* const vec2 = module.constant(make_vector(1.f, 2.f)); 98 | 99 | Instruction* const ivec2 = module.constant(make_vector(1, 2)); 100 | 101 | Instruction* pack = bb.glsl()->opPackSnorm4x8(vec4); 102 | bb.glsl()->opUnpackSnorm4x8(pack); 103 | 104 | pack = bb.glsl()->opPackUnorm4x8(vec4); 105 | bb.glsl()->opUnpackUnorm4x8(pack); 106 | 107 | pack = bb.glsl()->opPackSnorm2x16(vec2); 108 | bb.glsl()->opUnpackSnorm2x16(pack); 109 | 110 | pack = bb.glsl()->opPackUnorm2x16(vec2); 111 | bb.glsl()->opUnpackUnorm2x16(pack); 112 | 113 | pack = bb.glsl()->opPackHalf2x16(vec2); 114 | bb.glsl()->opUnpackHalf2x16(pack); 115 | 116 | pack = bb.glsl()->opPackDouble2x32(ivec2); 117 | bb.glsl()->opUnpackDouble2x32(pack); 118 | 119 | Instruction* const ilsb = bb.glsl()->opFindILsb(ivec2); 120 | Instruction* const smsb = bb.glsl()->opFindSMsb(ivec2); 121 | Instruction* const umsm = bb.glsl()->opFindUMsb(ivec2); 122 | 123 | Instruction* const inputVar = module.input>("interpolant"); 124 | Instruction* const interpolant = bb->opAccessChain(inputVar, 0u); 125 | 126 | Instruction* const ipolC = bb.glsl()->opInterpolateAtCentroid(interpolant); 127 | Instruction* const ipolS = bb.glsl()->opInterpolateAtSample(interpolant, module.constant(2)); 128 | Instruction* const ipolO = bb.glsl()->opInterpolateAtOffset(interpolant, vec2); 129 | 130 | bb.glsl()->opNMin(ipolC, ipolO); 131 | bb.glsl()->opNMax(ipolS, ipolO); 132 | bb.glsl()->opNClamp(ipolC, ipolO, ipolS); 133 | 134 | entry->opReturn(); 135 | } 136 | 137 | return module; 138 | } 139 | -------------------------------------------------------------------------------- /test/source/FragmentShader.cpp: -------------------------------------------------------------------------------- 1 | #include "test/Modules.h" 2 | #include "spvgentwo/TypeAlias.h" 3 | #include "spvgentwo/Templates.h" 4 | 5 | using namespace spvgentwo; 6 | 7 | spvgentwo::Module test::fragmentShader(spvgentwo::IAllocator* _pAllocator, spvgentwo::ILogger* _pLogger) 8 | { 9 | // create a new spir-v module 10 | Module module(_pAllocator, _pLogger); 11 | 12 | // configure capabilities and extensions 13 | module.addCapability(spv::Capability::Shader); 14 | module.addCapability(spv::Capability::StorageImageExtendedFormats); 15 | module.addCapability(spv::Capability::Float16); 16 | module.addCapability(spv::Capability::Int8); 17 | module.addCapability(spv::Capability::Kernel); 18 | module.addCapability(spv::Capability::ImageQuery); 19 | 20 | // global variables 21 | Instruction* const uniColor = module.uniform(u8"u_color", module.constant(make_vector(0.f, 0.f, 0.f, 1.f))); // use initializer 22 | Instruction* const uniTex = module.uniform(u8"u_baseColor", dyn_sampled_image_t{}); //default 2d float texture 23 | Instruction* const inUV = module.input(u8"v_inUV"); // varying input UVs 24 | 25 | dyn_image_t imgDesc{ dyn_scalar_t{spv::Op::OpTypeFloat, 16u}, spv::Dim::Dim2D }; 26 | imgDesc.format = spv::ImageFormat::Rg16; 27 | Instruction* const uniImgRawRG16 = module.uniform(u8"u_imgRG16", imgDesc); 28 | 29 | imgDesc.multiSampled = true; 30 | Instruction* const uniImgRawRG16MS = module.uniform(u8"u_imgRG16MS", imgDesc); 31 | imgDesc.multiSampled = false; 32 | 33 | imgDesc.format = spv::ImageFormat::Unknown; 34 | Instruction* const uniImageUnknownFmt = module.uniform(u8"u_imgUnknownFmt", dyn_sampled_image_t{ imgDesc }); 35 | 36 | imgDesc.format = spv::ImageFormat::Rg32f; 37 | imgDesc.sampledType.bits = 32u; 38 | imgDesc.accessQualifier = spv::AccessQualifier::WriteOnly; 39 | imgDesc.samplerAccess = SamplerImageAccess::Unknown; 40 | Instruction* const uniImgRawStorageRG32 = module.uniform(u8"u_imgStorageRG32", imgDesc); 41 | 42 | // 3d volume tex 43 | imgDesc.dimension = spv::Dim::Dim3D; 44 | imgDesc.sampledType = { spv::Op::OpTypeInt, 8u }; 45 | imgDesc.format = spv::ImageFormat::Rgba8ui; 46 | Instruction* const uni3DImgRawRGBA8 = module.uniform(u8"u_img3DRGBA8", imgDesc); 47 | 48 | // void sample type tex 49 | imgDesc.dimension = spv::Dim::Dim2D; 50 | imgDesc.format = spv::ImageFormat::Rgba16; 51 | imgDesc.sampledType = DynScalarVoid; 52 | imgDesc.accessQualifier = spv::AccessQualifier::ReadOnly; 53 | imgDesc.samplerAccess = SamplerImageAccess::Sampled; 54 | Instruction* const uni2DImgVoidRGBA16 = module.uniform(u8"u_img2DRGBA16Void", dyn_sampled_image_t{ imgDesc }); 55 | 56 | Instruction* const constCoord2D = module.constant(make_vector(0u, 1u)); 57 | Instruction* const constCoord3D = module.constant(make_vector(0u, 1u, 2u)); 58 | 59 | // void main(); entry point 60 | { 61 | EntryPoint& entry = module.addEntryPoint(spv::ExecutionModel::Fragment, "main"); 62 | entry.addExecutionMode(spv::ExecutionMode::OriginUpperLeft); 63 | BasicBlock& bb = *entry; // get entry block to this function 64 | 65 | Instruction* mask = bb->opLoad(uniColor); 66 | Instruction* alphaMask = bb->opVectorExtractDynamic(mask, module.constant(3u)); // extract alpha channel 67 | 68 | Instruction* const unknownSampledImg = bb->opLoad(uniImageUnknownFmt); 69 | Instruction* const unknownImg = bb->opImage(unknownSampledImg); 70 | Instruction* format = bb->opImageQueryFormat(unknownImg); 71 | Instruction* channelOrder = bb->opImageQueryOrder(unknownImg); 72 | Instruction* sizeDim = bb->opImageQuerySizeLod(unknownImg, module.constant(0u)); 73 | sizeDim = bb->opImageQuerySize(unknownImg); 74 | Instruction* lod = bb->opImageQueryLod(unknownSampledImg, constCoord2D); 75 | Instruction* levels = bb->opImageQueryLevels(unknownImg); 76 | 77 | Instruction* msImg = bb->opLoad(uniImgRawRG16MS); 78 | Instruction* samples = bb->opImageQuerySamples(msImg); 79 | 80 | Instruction* color = bb->opLoad(uniTex); 81 | Instruction* uv = bb->opLoad(inUV); 82 | Instruction* baseColor = bb->opImageSampleImplictLod(color, uv); 83 | Instruction* alpha = bb->opVectorExtractDynamic(baseColor, module.constant(3u)); // extract alpha channel 84 | 85 | Instruction* cond = bb->Less(alpha, alphaMask); 86 | BasicBlock& merge = bb.If(cond, [](BasicBlock& trueBB) 87 | { 88 | trueBB->opKill(); // discard pixel if baseColor.a < u_color.a 89 | }); 90 | 91 | Instruction* rawImgRg16 = merge->opLoad(uniImgRawRG16); 92 | Instruction* rawTexelRG16 = merge->opImageRead(rawImgRg16, constCoord2D); 93 | 94 | Instruction* rawImgRgba8 = merge->opLoad(uni3DImgRawRGBA8); 95 | Instruction* rawTexelRGBA7 = merge->opImageRead(rawImgRgba8, constCoord3D); 96 | 97 | Instruction* offsetCoord = merge->Add(constCoord2D, module.constant(make_vector(8u, 16u))); 98 | Instruction* rg32 = merge->opFConvert(rawTexelRG16, 32u); 99 | rg32 = merge->Mul(rg32, module.constant(0.5f)); 100 | 101 | Instruction* rawStorageImgRG32 = merge->opLoad(uniImgRawStorageRG32); 102 | merge->opImageWrite(rawStorageImgRG32, offsetCoord, rg32); 103 | 104 | Instruction* voidImgRGBA16 = merge->opLoad(uni2DImgVoidRGBA16); 105 | merge->opImageSampleImplictLod(voidImgRGBA16, uv); 106 | 107 | merge.returnValue(); 108 | } 109 | 110 | return module; 111 | } -------------------------------------------------------------------------------- /test/source/FunctionCall.cpp: -------------------------------------------------------------------------------- 1 | #include "test/Modules.h" 2 | #include "spvgentwo/Templates.h" 3 | 4 | using namespace spvgentwo; 5 | 6 | Module test::functionCall(IAllocator* _pAllocator, ILogger* _pLogger) 7 | { 8 | // create a new spir-v module 9 | Module module(_pAllocator, _pLogger); 10 | 11 | // configure capabilities and extensions 12 | module.addCapability(spv::Capability::Shader); 13 | 14 | // global variables 15 | Instruction* uniformVar = module.uniform>(u8"u_Position"); 16 | 17 | // float add(float x, float y) 18 | Function& funcAdd = module.addFunction(u8"add", spv::FunctionControlMask::Const); 19 | { 20 | BasicBlock& bb = *funcAdd; // get entry block to this function 21 | 22 | Instruction* x = funcAdd.getParameter(0); 23 | Instruction* y = funcAdd.getParameter(1); 24 | 25 | Instruction* z = bb.Add(x, y); 26 | bb.returnValue(z); 27 | } 28 | 29 | // void entryPoint(); 30 | { 31 | EntryPoint& entry = module.addEntryPoint(spv::ExecutionModel::Fragment, u8"main"); 32 | entry.addExecutionMode(spv::ExecutionMode::OriginUpperLeft); 33 | BasicBlock& bb = *entry; // get entry block to this function 34 | 35 | Instruction* uniVec = bb->opLoad(uniformVar); 36 | Instruction* s = bb->opDot(uniVec, uniVec); 37 | entry->call(&funcAdd, s, s); // call add(s, s) 38 | entry->opReturn(); 39 | } 40 | 41 | return module; 42 | } 43 | -------------------------------------------------------------------------------- /test/source/GeometryShader.cpp: -------------------------------------------------------------------------------- 1 | #include "test/Modules.h" 2 | #include "spvgentwo/TypeAlias.h" 3 | #include "spvgentwo/Templates.h" 4 | 5 | using namespace spvgentwo; 6 | 7 | Module test::geometryShader(IAllocator* _pAllocator, ILogger* _pLogger) 8 | { 9 | using namespace glsl; 10 | 11 | // create a new spir-v module 12 | Module module(_pAllocator, spv::AddressingModel::Logical, spv::MemoryModel::GLSL450, _pLogger); 13 | 14 | // configure capabilities and extensions 15 | module.addCapability(spv::Capability::Geometry); 16 | //module.addExtension("GLSL.std.450"); // needed? 17 | module.addSourceStringInstr()->opSource(spv::SourceLanguage::GLSL, 330u); 18 | 19 | EntryPoint& entry = module.addEntryPoint(spv::ExecutionModel::Geometry, u8"main"); 20 | entry.addExecutionMode(spv::ExecutionMode::InputPoints); 21 | entry.addExecutionMode(spv::ExecutionMode::Invocations, 1u); 22 | entry.addExecutionMode(spv::ExecutionMode::OutputLineStrip); 23 | entry.addExecutionMode(spv::ExecutionMode::OutputVertices, 2u); 24 | 25 | Type type = module.newType(); 26 | 27 | // wouldnt it be nice to have proper relfection in c++? 28 | struct gl_PerVertex 29 | { 30 | vec4 gl_Position; 31 | float gl_PointSize; 32 | float gl_ClipDistance[1]; 33 | }; 34 | 35 | type.Struct(); 36 | type.Member().VectorElement(4).Float(); // vec4 gl_Position 37 | type.FloatM(); // float gl_PointSize 38 | type.Member().ArrayElement(1u).Float(); // float gl_ClipDistance[]; 39 | 40 | Instruction* glPerVertexType = module.addType(type, u8"gl_PerVertex"); // just to assign the name 41 | module.addDecorationInstr()->opDecorate(glPerVertexType, spv::Decoration::Block); 42 | module.addDecorationInstr()->opMemberDecorate(glPerVertexType, 0, spv::Decoration::BuiltIn, spv::BuiltIn::Position); 43 | module.addDecorationInstr()->opMemberDecorate(glPerVertexType, 1, spv::Decoration::BuiltIn, spv::BuiltIn::PointSize); 44 | module.addDecorationInstr()->opMemberDecorate(glPerVertexType, 2, spv::Decoration::BuiltIn, spv::BuiltIn::ClipDistance); 45 | module.addMemberName(glPerVertexType, u8"gl_Position", 0u); 46 | module.addMemberName(glPerVertexType, u8"gl_PointSize", 1u); 47 | module.addMemberName(glPerVertexType, u8"gl_ClipDistance", 2u); 48 | 49 | const char* memberName = glPerVertexType->getName(1u); 50 | 51 | Instruction* outPerVertex = module.output(type, u8"gl_out"); 52 | Instruction* inPerVertex = module.input(type.wrapArray(1u), u8"gl_in"); 53 | 54 | // void main(); 55 | { 56 | BasicBlock& bb = *entry; // get entry block to this function 57 | 58 | // test comp 59 | { 60 | Instruction* glin_PerVertexPtr = bb->opAccessChain(inPerVertex); // gl_in[0].gl_Position 61 | Instruction* glin_PerVertex = bb->opLoad(glin_PerVertexPtr); // array 62 | 63 | Instruction* struct_ = bb->opCompositeExtract(glin_PerVertex, 0u); // get the struct 64 | Instruction* vec_y = bb->opCompositeExtract(struct_, 0u, 1u); // get vec -> get y element 65 | 66 | vec_y = bb->Mul(vec_y, vec_y); // square 67 | 68 | bb->opCompositeInsert(glin_PerVertex, vec_y, 0u, 0u, 1u); 69 | } 70 | 71 | Instruction* glin_PositionPtr = bb->opAccessChain(inPerVertex, 0u, 0u); // gl_in[0].gl_Position 72 | Instruction* glin_Position = bb->opLoad(glin_PositionPtr); 73 | 74 | Instruction* constNeg = module.constant(make_vector(-0.1f, 0.f, 0.f, 0.f)); 75 | Instruction* newPos = bb->Add(glin_Position, constNeg); 76 | 77 | Instruction* glout_PositionPtr = bb->opAccessChain(outPerVertex, 0u); // gl_out.glPosition 78 | bb->opStore(glout_PositionPtr, newPos); 79 | bb->opEmitVertex(); 80 | 81 | Instruction* constPos = module.constant(make_vector(0.1f, 0.f, 0.f, 0.f)); 82 | newPos = bb->Add(glin_Position, constPos); 83 | bb->opStore(glout_PositionPtr, newPos); 84 | bb->opEmitVertex(); 85 | 86 | bb->opEndPrimitive(); 87 | entry->opReturn(); 88 | } 89 | 90 | return module; 91 | } 92 | 93 | // SHADER: 94 | //#version 330 core 95 | //layout(points) in; 96 | //layout(line_strip, max_vertices = 2) out; 97 | // 98 | //void main() { 99 | // gl_Position = gl_in[0].gl_Position + vec4(-0.1, 0.0, 0.0, 0.0); 100 | // EmitVertex(); 101 | // 102 | // gl_Position = gl_in[0].gl_Position + vec4(0.1, 0.0, 0.0, 0.0); 103 | // EmitVertex(); 104 | // 105 | // EndPrimitive(); 106 | //} -------------------------------------------------------------------------------- /test/source/ImageRead.cpp: -------------------------------------------------------------------------------- 1 | #include "test/Modules.h" 2 | #include "spvgentwo/Templates.h" 3 | 4 | using namespace spvgentwo; 5 | 6 | spvgentwo::Module test::imageRead( spvgentwo::IAllocator* _pAllocator, spvgentwo::ILogger* _pLogger ) 7 | { 8 | Module module(_pAllocator, _pLogger); 9 | 10 | module.addCapability(spvgentwo::spv::Capability::Shader); 11 | module.setSpvVersion(1, 5); 12 | 13 | //glsl: 14 | // layout(set = 0, binding = 0, rgba8) uniform image2D myImg; 15 | dyn_image_t imgDesc; 16 | imgDesc.sampledType = dyn_scalar_t{ spvgentwo::spv::Op::OpTypeFloat, 32u }; 17 | imgDesc.dimension = spvgentwo::spv::Dim::Dim2D; 18 | imgDesc.format = spvgentwo::spv::ImageFormat::Rgba8; 19 | imgDesc.samplerAccess = SamplerImageAccess::Storage; 20 | 21 | Instruction* const pImageRGBA8 = module.uniformConstant("myImg", imgDesc); 22 | 23 | module.addDecorationInstr()->opDecorate( 24 | pImageRGBA8, spvgentwo::spv::Decoration::DescriptorSet, 0); 25 | module.addDecorationInstr()->opDecorate(pImageRGBA8, spvgentwo::spv::Decoration::Binding, 0); 26 | 27 | // void main(); entry point 28 | { 29 | EntryPoint& entry = 30 | module.addEntryPoint(spvgentwo::spv::ExecutionModel::GLCompute, "main"); 31 | entry.addExecutionMode(spvgentwo::spv::ExecutionMode::LocalSize, 256, 1, 1); 32 | BasicBlock& bb = *entry; // get entry block to this function 33 | auto imageRGBA8 = bb->opLoad(pImageRGBA8); 34 | auto pUV = entry.variable>(); 35 | auto uv = bb->opLoad(pUV); 36 | bb->opImageRead(imageRGBA8, uv); 37 | bb.returnValue(); 38 | } 39 | 40 | return module; 41 | } 42 | -------------------------------------------------------------------------------- /test/source/ModuleTests.cpp: -------------------------------------------------------------------------------- 1 | #include "spvgentwo/Grammar.h" 2 | #include "common/HeapAllocator.h" 3 | 4 | #include 5 | #include 6 | 7 | // test 8 | #include "test/Modules.h" 9 | #include "test/SpvValidator.h" 10 | #include "test/TestLogger.h" 11 | 12 | #include "spvgentwo/Templates.h" 13 | 14 | using namespace spvgentwo; 15 | using namespace test; 16 | 17 | namespace 18 | { 19 | HeapAllocator g_alloc; 20 | TestLogger g_logger; 21 | Grammar g_gram(&g_alloc); 22 | SpvValidator g_validator(g_gram); 23 | 24 | bool valid(spvgentwo::Module&& _module) 25 | { 26 | _module.finalize(&g_gram); 27 | return g_validator.validate(_module); 28 | } 29 | 30 | bool valid(spvgentwo::Module& _module) 31 | { 32 | _module.finalize(&g_gram); 33 | return g_validator.validate(_module); 34 | } 35 | } 36 | 37 | TEST_CASE("funcName", "[Modules]") 38 | { 39 | spvgentwo::Module module(&g_alloc); 40 | spvgentwo::Function& funcAdd = module.addFunction("add", spvgentwo::spv::FunctionControlMask::Const); 41 | REQUIRE(funcAdd.getName() != nullptr); 42 | 43 | String name(&g_alloc, funcAdd.getName() ); 44 | REQUIRE(name == "add"); 45 | } 46 | 47 | TEST_CASE( "types", "[Modules]" ) 48 | { 49 | REQUIRE( valid( test::types( &g_alloc, &g_logger ) ) ); 50 | } 51 | 52 | TEST_CASE( "constants", "[Modules]" ) 53 | { 54 | REQUIRE( valid( test::constants( &g_alloc, &g_logger ) ) ); 55 | } 56 | 57 | TEST_CASE( "extensions", "[Modules]" ) 58 | { 59 | REQUIRE( valid( test::extensions( &g_alloc, &g_logger ) ) ); 60 | } 61 | 62 | TEST_CASE( "oldInstrTest", "[Modules]" ) 63 | { 64 | REQUIRE( valid( test::oldInstrTest( &g_alloc, &g_logger ) ) ); 65 | } 66 | 67 | TEST_CASE("imageSampling", "[Modules]") 68 | { 69 | REQUIRE( valid( test::imageRead( &g_alloc, &g_logger ) ) ); 70 | } 71 | 72 | TEST_CASE( "functionCall", "[Modules]" ) 73 | { 74 | REQUIRE( valid( test::functionCall( &g_alloc, &g_logger ) ) ); 75 | } 76 | 77 | TEST_CASE( "controlFlow", "[Modules]" ) 78 | { 79 | REQUIRE( valid( test::controlFlow( &g_alloc, &g_logger ) ) ); 80 | } 81 | 82 | TEST_CASE( "geometryShader", "[Modules]" ) 83 | { 84 | REQUIRE( valid( test::geometryShader( &g_alloc, &g_logger ) ) ); 85 | } 86 | 87 | TEST_CASE( "fragmentShader", "[Modules]" ) 88 | { 89 | REQUIRE( valid( test::fragmentShader( &g_alloc, &g_logger ) ) ); 90 | } 91 | 92 | TEST_CASE( "compute", "[Modules]" ) 93 | { 94 | REQUIRE( valid( test::computeShader( &g_alloc, &g_logger ) ) ); 95 | } 96 | 97 | TEST_CASE( "expressionGraph", "[Modules]" ) 98 | { 99 | REQUIRE( valid( test::expressionGraph( &g_alloc, &g_logger ) ) ); 100 | } 101 | 102 | TEST_CASE("physicalStorageTest", "[Modules]") 103 | { 104 | REQUIRE(valid(test::physicalStorageTest(&g_alloc, &g_logger))); 105 | } 106 | 107 | TEST_CASE("bitInstructionTest", "[Modules]") 108 | { 109 | REQUIRE(valid(test::bitInstructionTest(&g_alloc, &g_logger))); 110 | } 111 | 112 | TEST_CASE( "linking", "[Modules]" ) 113 | { 114 | Module libA, libB, consumer; 115 | 116 | libA = test::linkageLibA( &g_alloc, &g_logger ); 117 | REQUIRE( valid( libA ) ); 118 | 119 | libB = test::linkageLibB( &g_alloc, &g_logger ); 120 | REQUIRE( valid( libB ) ); 121 | 122 | consumer = test::linkageConsumer( &g_alloc, &g_logger ); 123 | REQUIRE( valid( consumer ) ); 124 | 125 | REQUIRE( test::linkageLinked( libA, libB, consumer, &g_alloc, &g_gram ) ); 126 | REQUIRE( valid( consumer ) ); 127 | } -------------------------------------------------------------------------------- /test/source/PhysicalStorageTest.cpp: -------------------------------------------------------------------------------- 1 | #include "test/Modules.h" 2 | 3 | #include "common/HeapAllocator.h" 4 | #include "common/ConsoleLogger.h" 5 | #include "spvgentwo/Operators.h" 6 | #include "spvgentwo/Templates.h" 7 | 8 | using namespace spvgentwo; 9 | using namespace ops; 10 | 11 | Module test::physicalStorageTest(IAllocator* _pAllocator, ILogger* _pLogger) 12 | { 13 | Module module(_pAllocator, _pLogger); 14 | 15 | module.addCapability(spv::Capability::Shader); 16 | module.addCapability(spv::Capability::VulkanMemoryModelKHR); 17 | module.addCapability(spv::Capability::Int64); 18 | module.addCapability(spv::Capability::PhysicalStorageBufferAddresses); 19 | module.setSpvVersion(1, 4); 20 | 21 | module.addExtension(spv::Extension::SPV_KHR_physical_storage_buffer); 22 | module.addExtension(spv::Extension::SPV_KHR_storage_buffer_storage_class); 23 | module.addExtension(spv::Extension::SPV_KHR_vulkan_memory_model); 24 | 25 | module.setMemoryModel(spv::AddressingModel::Physical64, spv::MemoryModel::VulkanKHR); 26 | 27 | Type storageBufferType = module.newType(); 28 | // struct 29 | // { 30 | // uint64_t ptr; // to convert to a physical pointer 31 | // }; 32 | storageBufferType.Struct().Member().UInt(64); 33 | Instruction* storageBufferTypeInt = module.addType(storageBufferType); 34 | Instruction* storageBufferPtr = module.storageBuffer(storageBufferType, "testStorageBuffer"); 35 | module.addDecorationInstr()->opDecorate(storageBufferTypeInt, spv::Decoration::Block); 36 | module.addDecorationInstr()->opDecorate(storageBufferPtr, spv::Decoration::DescriptorSet, 0); 37 | module.addDecorationInstr()->opDecorate(storageBufferPtr, spv::Decoration::Binding, 0); 38 | module.addDecorationInstr()->opMemberDecorate(storageBufferTypeInt, 0, spv::Decoration::Offset, literal_t{ 0 }); 39 | 40 | Type physicalStorageBufferType = module.newType(); 41 | // struct 42 | // { 43 | // float data; 44 | // }; 45 | Type innerType = module.newType(); 46 | innerType.Struct().Member().Float(32); 47 | Instruction* storageBufferTypeFloat = module.addType(innerType); 48 | module.addDecorationInstr()->opMemberDecorate(storageBufferTypeFloat, 0, spv::Decoration::Offset, literal_t{ 0 }); 49 | physicalStorageBufferType.Pointer(spv::StorageClass::PhysicalStorageBuffer, &innerType); 50 | 51 | // void entryPoint(); 52 | { 53 | EntryPoint& entry = module.addEntryPoint(spv::ExecutionModel::GLCompute, u8"main"); 54 | 55 | BasicBlock& bb = *entry; 56 | 57 | Instruction* uintPtr = bb->opAccessChain(storageBufferPtr, 0u); 58 | Instruction* pUint = bb->opLoad(uintPtr); 59 | 60 | Instruction* ptrType = module.addType(physicalStorageBufferType); 61 | Instruction* bufferPtr = bb->opConvertUToPtr(ptrType, pUint); 62 | Instruction* floatPtr = bb->opAccessChain(bufferPtr, 0u); 63 | Instruction* pFloat = bb->opLoad(floatPtr, spvgentwo::spv::MemoryAccessMask::Aligned, literal_t{4}); 64 | 65 | Instruction* ptrEqualRes = bb->opPtrEqual(storageBufferPtr, storageBufferPtr); 66 | Instruction* ptrNotEqualRes = bb->opPtrNotEqual(uintPtr, uintPtr); 67 | 68 | 69 | Instruction* cond = module.constant(false); 70 | BasicBlock& merge = bb.If(cond, [&](BasicBlock& trueBB) 71 | { 72 | // this branch is unreachable for the cond is always false 73 | trueBB->opUnreachable(); 74 | }); 75 | 76 | merge.returnValue(); 77 | } 78 | 79 | return module; 80 | } -------------------------------------------------------------------------------- /test/source/SpvValidator.cpp: -------------------------------------------------------------------------------- 1 | #include "test/SpvValidator.h" 2 | #include "common/BinaryVectorWriter.h" 3 | #include "common/ModulePrinter.h" 4 | 5 | #include "spirv-tools/libspirv.hpp" 6 | #include 7 | 8 | using namespace spvgentwo; 9 | using namespace ModulePrinter; 10 | 11 | namespace 12 | { 13 | static spvtools::SpirvTools& instance( ) 14 | { 15 | static spvtools::SpirvTools tools( SPV_ENV_UNIVERSAL_1_6 ); 16 | static bool toolsInitialized = false; 17 | 18 | if( tools.IsValid() && toolsInitialized == false ) 19 | { 20 | tools.SetMessageConsumer( 21 | [](spv_message_level_t level, const char*, const spv_position_t& position, const char* message) 22 | { 23 | switch (level) { 24 | case SPV_MSG_FATAL: 25 | FAIL("line " << position.line << " id " << position.index << ": " << message); 26 | break; 27 | case SPV_MSG_INTERNAL_ERROR: 28 | case SPV_MSG_ERROR: 29 | case SPV_MSG_WARNING: 30 | WARN("line " << position.line << " id " << position.index << ": " << message); 31 | break; 32 | case SPV_MSG_INFO: 33 | UNSCOPED_INFO("line " << position.line << " id " << position.index << ": " << message); 34 | break; 35 | default: 36 | break; 37 | } 38 | } 39 | ); 40 | toolsInitialized = true; 41 | } 42 | 43 | return tools; 44 | } 45 | } 46 | 47 | 48 | test::SpvValidator::SpvValidator(const Grammar& _gram) : m_gram(_gram) 49 | { 50 | } 51 | 52 | bool test::SpvValidator::validate( const spvgentwo::Module& _module ) 53 | { 54 | std::vector vec; 55 | auto writer = BinaryVectorWriter>( vec ); 56 | 57 | if (_module.write(writer)) 58 | { 59 | if (instance().Validate(vec) == false) 60 | { 61 | PrintOptions options{ PrintOptionsBits::All }; 62 | String buffer(_module.getAllocator(), 1024); 63 | auto printer = ModuleStringPrinter(buffer, false); 64 | printModule(_module, m_gram, printer, options); 65 | WARN(buffer.c_str()); 66 | return false; 67 | } 68 | else return true; 69 | } 70 | else return false; 71 | } -------------------------------------------------------------------------------- /test/source/TestLogger.cpp: -------------------------------------------------------------------------------- 1 | #include "test/TestLogger.h" 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | namespace 9 | { 10 | void TestLog(spvgentwo::ILogger* _pInstance, spvgentwo::LogLevel _level, const char* _format, ...) 11 | { 12 | char buffer[1024]{}; 13 | 14 | va_list args; 15 | va_start(args, _format); 16 | vsnprintf(buffer, sizeof(buffer), _format, args); 17 | va_end(args); 18 | 19 | switch (_level) 20 | { 21 | case spvgentwo::LogLevel::Info: 22 | UNSCOPED_INFO(buffer); 23 | break; 24 | case spvgentwo::LogLevel::Warning: 25 | WARN(buffer); 26 | break; 27 | case spvgentwo::LogLevel::Error: 28 | case spvgentwo::LogLevel::Fatal: 29 | FAIL(buffer); 30 | break; 31 | case spvgentwo::LogLevel::Debug: 32 | default: 33 | break; 34 | } 35 | } 36 | } 37 | 38 | 39 | test::TestLogger::TestLogger() : spvgentwo::ILogger(TestLog) 40 | { 41 | } 42 | -------------------------------------------------------------------------------- /test/source/Types.cpp: -------------------------------------------------------------------------------- 1 | #include "test/Modules.h" 2 | 3 | #include "common/TypeHelper.h" 4 | #include "spvgentwo/Templates.h" 5 | 6 | using namespace spvgentwo; 7 | 8 | Module test::types(IAllocator* _pAllocator, ILogger* _pLogger) 9 | { 10 | Module module(_pAllocator, _pLogger); 11 | module.addCapability(spv::Capability::Shader); 12 | { 13 | Function& main = module.addEntryPoint(spv::ExecutionModel::Vertex, u8"main"); 14 | BasicBlock& bb = *main; 15 | 16 | Type innerStruct = module.newType().RayQueryKHR().wrapStruct(); 17 | 18 | //struct innerStruct 19 | //{ 20 | // ray_query_khr_t query; 21 | //}; 22 | 23 | // add a name to the type 24 | module.addType(innerStruct, u8"innerStruct"); 25 | 26 | Type myStruct = module.newType(); 27 | 28 | //struct myStruct 29 | //{ 30 | // float x; 31 | // int y; 32 | // vec3 v; 33 | // innerStruct i; 34 | //}; 35 | 36 | myStruct.Struct(); 37 | myStruct.FloatM(); // add 32 bit float as member 38 | myStruct.IntM(); // add signed int as member 39 | myStruct.Member().VectorElement(3).Float(); // add empty member to struct, make it a vector of 3 elements of type float 40 | myStruct.Member(&innerStruct); 41 | 42 | Instruction* type = module.addType(myStruct, u8"myStruct"); 43 | 44 | String name(_pAllocator); 45 | TypeHelper::getTypeName(myStruct, name, type); 46 | _pLogger->logDebug("%s", name.c_str()); 47 | 48 | type = module.addType(myStruct.wrapPointer(spv::StorageClass::Function)); 49 | // add via addType, make a pointer for storage class 'function 50 | Instruction* var = bb->opVariable(type, spv::StorageClass::Function); 51 | 52 | // add C++ type via type 53 | type = module.type(spv::StorageClass::Function, "uintptr"); 54 | 55 | dyn_sampled_image_t img{ spv::Op::OpTypeFloat }; 56 | 57 | // add complex dynamic C++ type 58 | type = module.type(img); 59 | 60 | auto ty = [&module](){ return module.newType(); }; 61 | 62 | Type t(ty()); 63 | module.addType(t.Struct(ty().Float(), ty().UInt(), ty().Bool())); 64 | 65 | bb.returnValue(); 66 | } 67 | 68 | { 69 | Function& f = module.addFunction>("testFunc"); 70 | 71 | String name(_pAllocator); 72 | TypeHelper::getTypeName(f.getFunctionType(), name, f.getFunction()); 73 | _pLogger->logDebug("%s", name.c_str()); 74 | 75 | BasicBlock& bb = *f; 76 | Instruction* var = f.variable>("myArray"); 77 | var = bb->opLoad(var); 78 | 79 | name.clear(); 80 | TypeHelper::getTypeName(*var->getType(), name, var); 81 | _pLogger->logDebug("%s", name.c_str()); 82 | 83 | var = bb->opCompositeExtract(var, 3u); 84 | 85 | (*f).returnValue(var); 86 | } 87 | 88 | return module; 89 | } 90 | --------------------------------------------------------------------------------