diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..1c6b71b --- /dev/null +++ b/.clang-format @@ -0,0 +1,88 @@ +--- +Language: Cpp +# BasedOnStyle: LLVM +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlinesLeft: false +AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Empty +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: true +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Attach +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +ColumnLimit: 100 +CommentPragmas: '^ IWYU pragma:' +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] +IncludeCategories: + - Regex: '^\<[^Q][^/.>]*\>' + Priority: -2 + - Regex: '^\<' + Priority: -1 + - Regex: '^\"' + Priority: 0 +IndentCaseLabels: false +IndentWidth: 4 +IndentWrappedFunctionNames: false +KeepEmptyLinesAtTheStartOfBlocks: true +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 150 +PointerAlignment: Left +ReflowComments: true +SortIncludes: true +SpaceAfterCStyleCast: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp11 +TabWidth: 4 +UseTab: Never +... diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7353e7e --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +build +*.o diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..e925e6a --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "externals/SPIRV-Headers"] + path = externals/SPIRV-Headers + url = https://github.com/KhronosGroup/SPIRV-Headers diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..ca7e3b6 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,99 @@ +# This file has been adapted from dynarmic + +cmake_minimum_required(VERSION 3.8) +project(sirit CXX) + +# Determine if we're built as a subproject (using add_subdirectory) +# or if this is the master project. +set(MASTER_PROJECT OFF) +if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) + set(MASTER_PROJECT ON) +endif() + +# Sirit project options +option(SIRIT_TESTS "Build tests" OFF) +option(SIRIT_USE_SYSTEM_SPIRV_HEADERS "Use system SPIR-V headers" OFF) + +# Default to a Release build +if (NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE) + message(STATUS "Defaulting to a Release build") +endif() + +# Set hard requirements for C++ +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +# Warn on CMake API deprecations +set(CMAKE_WARN_DEPRECATED ON) + +# Disable in-source builds +set(CMAKE_DISABLE_SOURCE_CHANGES ON) +set(CMAKE_DISABLE_IN_SOURCE_BUILD ON) +if ("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}") + message(SEND_ERROR "In-source builds are not allowed.") +endif() + +# Add the module directory to the list of paths +list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/CMakeModules") + +# Compiler flags +if (MSVC) + set(SIRIT_CXX_FLAGS + /std:c++latest # CMAKE_CXX_STANDARD as no effect on MSVC until CMake 3.10. + /W4 + /w34263 # Non-virtual member function hides base class virtual function + /w44265 # Class has virtual functions, but destructor is not virtual + /w34456 # Declaration of 'var' hides previous local declaration + /w34457 # Declaration of 'var' hides function parameter + /w34458 # Declaration of 'var' hides class member + /w34459 # Declaration of 'var' hides global definition + /w34946 # Reinterpret-cast between related types + /wd4592 # Symbol will be dynamically initialized (implementation limitation) + /permissive- # Stricter C++ standards conformance + /MP + /Zi + /Zo + /EHsc + /Zc:throwingNew # Assumes new never returns null + /Zc:inline # Omits inline functions from object-file output + /DNOMINMAX + /WX) + + if (CMAKE_VS_PLATFORM_TOOLSET MATCHES "LLVM-vs[0-9]+") + list(APPEND SIRIT_CXX_FLAGS + -Qunused-arguments + -Wno-missing-braces) + endif() +else() + set(SIRIT_CXX_FLAGS + -Wall + -Wextra + -Wcast-qual + -pedantic + -pedantic-errors + -Wfatal-errors + -Wno-missing-braces + -Wconversion + -Wsign-conversion + -Wshadow + -Werror) +endif() + +# Enable unit-testing. +enable_testing(true) + +# SPIR-V headers +if (SIRIT_USE_SYSTEM_SPIRV_HEADERS) + find_package(SPIRV-Headers REQUIRED) +else() + add_subdirectory(externals/SPIRV-Headers EXCLUDE_FROM_ALL) + add_library(SPIRV-Headers::SPIRV-Headers ALIAS SPIRV-Headers) +endif() + +# Sirit project files +add_subdirectory(src) +if (SIRIT_TESTS) + add_subdirectory(tests) +endif() diff --git a/externals/SPIRV-Headers b/externals/SPIRV-Headers new file mode 160000 index 0000000..8b246ff --- /dev/null +++ b/externals/SPIRV-Headers @@ -0,0 +1 @@ +Subproject commit 8b246ff75c6615ba4532fe4fde20f1be090c3764 diff --git a/include/sirit/sirit.h b/include/sirit/sirit.h new file mode 100644 index 0000000..aea4468 --- /dev/null +++ b/include/sirit/sirit.h @@ -0,0 +1,1330 @@ +/* This file is part of the sirit project. + * Copyright (c) 2019 sirit + * This software may be used and distributed according to the terms of the + * 3-Clause BSD License + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace Sirit { + +constexpr std::uint32_t GENERATOR_MAGIC_NUMBER = 0; + +class Declarations; +class Operand; +class Stream; + +using Literal = + std::variant; + +struct Id { + std::uint32_t value; +}; + +[[nodiscard]] inline bool ValidId(Id id) noexcept { + return id.value != 0; +} + +class Module { +public: + explicit Module(std::uint32_t version = spv::Version); + ~Module(); + + /** + * Assembles current module into a SPIR-V stream. + * It can be called multiple times but it's recommended to copy code + * externally. + * @return A stream of bytes representing a SPIR-V module. + */ + std::vector Assemble() const; + + /// Patches deferred phi nodes calling the passed function on each phi argument + void PatchDeferredPhi(const std::function& func); + + /// Adds a SPIR-V extension. + void AddExtension(std::string extension_name); + + /// Adds a module capability. + void AddCapability(spv::Capability capability); + + /// Sets module memory model. + void SetMemoryModel(spv::AddressingModel addressing_model_, spv::MemoryModel memory_model_); + + /// Adds an entry point. + void AddEntryPoint(spv::ExecutionModel execution_model, Id entry_point, std::string_view name, + std::span interfaces = {}); + + /// Adds an entry point. + // TODO: Change std::is_convertible_v to std::convertible_to when compilers + // support it; same elsewhere. + template + requires(...&& std::is_convertible_v) void AddEntryPoint( + spv::ExecutionModel execution_model, Id entry_point, std::string_view name, + Ts&&... interfaces) { + AddEntryPoint(execution_model, std::move(entry_point), name, + std::span({interfaces...})); + } + + /// Declare an execution mode for an entry point. + void AddExecutionMode(Id entry_point, spv::ExecutionMode mode, + std::span literals = {}); + + /// Declare an execution mode for an entry point. + template + requires(...&& std::is_convertible_v) void AddExecutionMode( + Id entry_point, spv::ExecutionMode mode, Ts&&... literals) { + AddExecutionMode(entry_point, mode, std::span({literals...})); + } + + /** + * Adds an existing label to the code + * @param label Label to insert into code. + * @return Returns label. + */ + Id AddLabel(Id label); + + /** + * Adds a label to the code + * @return Returns the created label. + */ + Id AddLabel() { + return AddLabel(OpLabel()); + } + + /// Adds a local variable to the code + Id AddLocalVariable(Id result_type, spv::StorageClass storage_class, + std::optional initializer = std::nullopt); + + /// Adds a global variable + Id AddGlobalVariable(Id result_type, spv::StorageClass storage_class, + std::optional initializer = std::nullopt); + + // Types + + /// Returns type void. + Id TypeVoid(); + + /// Returns type bool. + Id TypeBool(); + + /// Returns type integer. + Id TypeInt(int width, bool is_signed); + + /// Returns type signed integer. + Id TypeSInt(int width); + + /// Returns type unsigned integer. + Id TypeUInt(int width); + + /// Returns type float. + Id TypeFloat(int width); + + /// Returns type vector. + Id TypeVector(Id component_type, int component_count); + + /// Returns type matrix. + Id TypeMatrix(Id column_type, int column_count); + + /// Returns type image. + Id TypeImage(Id sampled_type, spv::Dim dim, int depth, bool arrayed, bool ms, int sampled, + spv::ImageFormat image_format, + std::optional access_qualifier = std::nullopt); + + /// Returns type sampler. + Id TypeSampler(); + + /// Returns type sampled image. + Id TypeSampledImage(Id image_type); + + /// Returns type array. + Id TypeArray(Id element_type, Id length); + + /// Returns type runtime array. + Id TypeRuntimeArray(Id element_type); + + /// Returns type struct. + Id TypeStruct(std::span members = {}); + + /// Returns type struct. + template + requires(...&& std::is_convertible_v) Id TypeStruct(Ts&&... members) { + return TypeStruct(std::span({members...})); + } + + /// Returns type opaque. + Id TypeOpaque(std::string_view name); + + /// Returns type pointer. + Id TypePointer(spv::StorageClass storage_class, Id type); + + /// Returns type function. + Id TypeFunction(Id return_type, std::span arguments = {}); + + /// Returns type function. + template + requires(...&& std::is_convertible_v) Id + TypeFunction(Id return_type, Ts&&... arguments) { + return TypeFunction(return_type, std::span({arguments...})); + } + + /// Returns type event. + Id TypeEvent(); + + /// Returns type device event. + Id TypeDeviceEvent(); + + /// Returns type reserve id. + Id TypeReserveId(); + + /// Returns type queue. + Id TypeQueue(); + + /// Returns type pipe. + Id TypePipe(spv::AccessQualifier access_qualifier); + + // Constant + + /// Returns a true scalar constant. + Id ConstantTrue(Id result_type); + + /// Returns a false scalar constant. + Id ConstantFalse(Id result_type); + + /// Returns a numeric scalar constant. + Id Constant(Id result_type, const Literal& literal); + + /// Returns a numeric scalar constant. + Id ConstantComposite(Id result_type, std::span constituents); + + /// Returns a numeric scalar constant. + template + requires(...&& std::is_convertible_v) Id + ConstantComposite(Id result_type, Ts&&... constituents) { + return ConstantComposite(result_type, std::span({constituents...})); + } + + /// Returns a sampler constant. + Id ConstantSampler(Id result_type, spv::SamplerAddressingMode addressing_mode, bool normalized, + spv::SamplerFilterMode filter_mode); + + /// Returns a null constant value. + Id ConstantNull(Id result_type); + + // Function + + /// Declares a function. + Id OpFunction(Id result_type, spv::FunctionControlMask function_control, Id function_type); + + /// Ends a function. + void OpFunctionEnd(); + + /// Call a function. + Id OpFunctionCall(Id result_type, Id function, std::span arguments = {}); + + /// Call a function. + template + requires(...&& std::is_convertible_v) Id + OpFunctionCall(Id result_type, Id function, Ts&&... arguments) { + return OpFunctionCall(result_type, function, std::span({arguments...})); + } + + /// Declare a formal parameter of the current function. + Id OpFunctionParameter(Id result_type); + + // Flow + + /** + * The SSA phi function. + * + * @param result_type The result type. + * @param operands An immutable span of variable, parent block pairs + */ + Id OpPhi(Id result_type, std::span operands); + + /** + * The SSA phi function. This instruction will be revisited when patching phi nodes. + * + * @param result_type The result type. + * @param blocks An immutable span of block pairs. + */ + Id DeferredOpPhi(Id result_type, std::span blocks); + + /// Declare a structured loop. + Id OpLoopMerge(Id merge_block, Id continue_target, spv::LoopControlMask loop_control, + std::span literals = {}); + + /// Declare a structured loop. + template + requires(...&& std::is_convertible_v) Id + OpLoopMerge(Id merge_block, Id continue_target, spv::LoopControlMask loop_control, + Ts&&... literals) { + return OpLoopMerge(merge_block, continue_target, loop_control, + std::span({literals...})); + } + + /// Declare a structured selection. + Id OpSelectionMerge(Id merge_block, spv::SelectionControlMask selection_control); + + /// The block label instruction: Any reference to a block is through this ref. + Id OpLabel(); + + /// The block label instruction: Any reference to a block is through this ref. + Id OpLabel(std::string_view label_name) { + return Name(OpLabel(), label_name); + } + + /// Unconditional jump to label. + Id OpBranch(Id target_label); + + /// If condition is true branch to true_label, otherwise branch to + /// false_label. + Id OpBranchConditional(Id condition, Id true_label, Id false_label, + std::uint32_t true_weight = 0, std::uint32_t false_weight = 0); + + /// Multi-way branch to one of the operand label. + Id OpSwitch(Id selector, Id default_label, std::span literals, + std::span labels); + + /// Returns with no value from a function with void return type. + void OpReturn(); + + /// Behavior is undefined if this instruction is executed. + void OpUnreachable(); + + /// Return a value from a function. + Id OpReturnValue(Id value); + + /// Deprecated fragment-shader discard. + void OpKill(); + + /// Demote fragment shader invocation to a helper invocation + void OpDemoteToHelperInvocation(); + void OpDemoteToHelperInvocationEXT(); + + /// Fragment-shader discard. + void OpTerminateInvocation(); + + // Debug + + /// Assign a name string to a reference. + /// @return target + Id Name(Id target, std::string_view name); + + /// Assign a name string to a member of a structure type. + /// @return type + Id MemberName(Id type, std::uint32_t member, std::string_view name); + + /// Assign a Result to a string for use by other debug instructions. + Id String(std::string_view string); + + /// Add source-level location information + Id OpLine(Id file, Literal line, Literal column); + + // Memory + + /// Form a pointer to a texel of an image. Use of such a pointer is limited to atomic + /// operations. + Id OpImageTexelPointer(Id result_type, Id image, Id coordinate, Id sample); + + /// Load through a pointer. + Id OpLoad(Id result_type, Id pointer, + std::optional memory_access = std::nullopt); + + /// Store through a pointer. + Id OpStore(Id pointer, Id object, + std::optional memory_access = std::nullopt); + + /// Create a pointer into a composite object that can be used with OpLoad and OpStore. + Id OpAccessChain(Id result_type, Id base, std::span indexes = {}); + + /// Create a pointer into a composite object that can be used with OpLoad and OpStore. + template + requires(...&& std::is_convertible_v) Id + OpAccessChain(Id result_type, Id base, Ts&&... indexes) { + return OpAccessChain(result_type, base, std::span({indexes...})); + } + + /// Extract a single, dynamically selected, component of a vector. + Id OpVectorExtractDynamic(Id result_type, Id vector, Id index); + + /// Make a copy of a vector, with a single, variably selected, component modified. + Id OpVectorInsertDynamic(Id result_type, Id vector, Id component, Id index); + + /// Make a copy of a composite object, while modifying one part of it. + Id OpCompositeInsert(Id result_type, Id object, Id composite, + std::span indexes = {}); + + /// Make a copy of a composite object, while modifying one part of it. + template + requires(...&& std::is_convertible_v) Id + OpCompositeInsert(Id result_type, Id object, Id composite, Ts&&... indexes) { + const Literal stack_indexes[] = {std::forward(indexes)...}; + return OpCompositeInsert(result_type, object, composite, + std::span{stack_indexes}); + } + + /// Extract a part of a composite object. + Id OpCompositeExtract(Id result_type, Id composite, std::span indexes = {}); + + /// Extract a part of a composite object. + template + requires(...&& std::is_convertible_v) Id + OpCompositeExtract(Id result_type, Id composite, Ts&&... indexes) { + const Literal stack_indexes[] = {std::forward(indexes)...}; + return OpCompositeExtract(result_type, composite, std::span{stack_indexes}); + } + + /// Construct a new composite object from a set of constituent objects that will fully form it. + Id OpCompositeConstruct(Id result_type, std::span ids); + + /// Construct a new composite object from a set of constituent objects that will fully form it. + template + requires(...&& std::is_convertible_v) Id + OpCompositeConstruct(Id result_type, Ts&&... ids) { + return OpCompositeConstruct(result_type, std::span({ids...})); + } + + // Annotation + + /// Add a decoration to target. + Id Decorate(Id target, spv::Decoration decoration, std::span literals = {}); + + /// Add a decoration to target. + template + requires(...&& std::is_convertible_v) Id + Decorate(Id target, spv::Decoration decoration, Ts&&... literals) { + const Literal stack_literals[] = {std::forward(literals)...}; + return Decorate(target, decoration, std::span{stack_literals}); + } + + /// Add a decoration to target. + template + requires std::is_enum_v Id Decorate(Id target, spv::Decoration decoration, T literal) { + return Decorate(target, decoration, static_cast(literal)); + } + + Id MemberDecorate(Id structure_type, Literal member, spv::Decoration decoration, + std::span literals = {}); + + template + requires(...&& std::is_convertible_v) Id + MemberDecorate(Id structure_type, Literal member, spv::Decoration decoration, + Ts&&... literals) { + const Literal stack_literals[] = {std::forward(literals)...}; + return MemberDecorate(structure_type, member, decoration, + std::span{stack_literals}); + } + + // Misc + + /// Make an intermediate object whose value is undefined. + Id OpUndef(Id result_type); + + /// Emits the current values of all output variables to the current output primitive. + void OpEmitVertex(); + + /// Finish the current primitive and start a new one. No vertex is emitted. + void OpEndPrimitive(); + + /// Emits the current values of all output variables to the current output primitive. After + /// execution, the values of all output variables are undefined. + void OpEmitStreamVertex(Id stream); + + /// Finish the current primitive and start a new one. No vertex is emitted. + void OpEndStreamPrimitive(Id stream); + + // Barrier + + /// Wait for other invocations of this module to reach the current point of execution. + Id OpControlBarrier(Id execution, Id memory, Id semantics); + + /// Control the order that memory accesses are observed. + Id OpMemoryBarrier(Id scope, Id semantics); + + // Logical + + /// Result is true if any component of Vector is true, otherwise result is false. + Id OpAny(Id result_type, Id vector); + + /// Result is true if all components of Vector are true, otherwise result is false. + Id OpAll(Id result_type, Id vector); + + /// Result is true if x is an IEEE NaN, otherwise result is false. + Id OpIsNan(Id result_type, Id operand); + + /// Result is true if x is an IEEE Inf, otherwise result is false. + Id OpIsInf(Id result_type, Id operand); + + /// Result is true if Operand 1 and Operand 2 have the same value. Result is false if Operand 1 + /// and Operand 2 have different values. + Id OpLogicalEqual(Id result_type, Id operand_1, Id operand_2); + + /// Result is true if Operand 1 and Operand 2 have different values. Result is false if Operand + /// 1 and Operand 2 have the same value. + Id OpLogicalNotEqual(Id result_type, Id operand_1, Id operand_2); + + /// Result is true if either Operand 1 or Operand 2 is true. Result is false if both Operand 1 + /// and Operand 2 are false. + Id OpLogicalOr(Id result_type, Id operand_1, Id operand_2); + + /// Result is true if both Operand 1 and Operand 2 are true. Result is false if either Operand 1 + /// or Operand 2 are false. + Id OpLogicalAnd(Id result_type, Id operand_1, Id operand_2); + + /// Result is true if Operand is false. Result is false if Operand is true. + Id OpLogicalNot(Id result_type, Id operand); + + /// Select components from two objects. + Id OpSelect(Id result_type, Id condition, Id operand_1, Id operand_2); + + /// Integer comparison for equality. + Id OpIEqual(Id result_type, Id operand_1, Id operand_2); + + /// Integer comparison for inequality. + Id OpINotEqual(Id result_type, Id operand_1, Id operand_2); + + /// Unsigned-integer comparison if Operand 1 is greater than Operand 2. + Id OpUGreaterThan(Id result_type, Id operand_1, Id operand_2); + + /// Signed-integer comparison if Operand 1 is greater than Operand 2. + Id OpSGreaterThan(Id result_type, Id operand_1, Id operand_2); + + /// Unsigned-integer comparison if Operand 1 is greater than or equal to Operand 2. + Id OpUGreaterThanEqual(Id result_type, Id operand_1, Id operand_2); + + /// Signed-integer comparison if Operand 1 is greater than or equal to Operand 2. + Id OpSGreaterThanEqual(Id result_type, Id operand_1, Id operand_2); + + /// Unsigned-integer comparison if Operand 1 is less than Operand 2. + Id OpULessThan(Id result_type, Id operand_1, Id operand_2); + + /// Signed-integer comparison if Operand 1 is less than Operand 2. + Id OpSLessThan(Id result_type, Id operand_1, Id operand_2); + + /// Unsigned-integer comparison if Operand 1 is less than or equal to Operand 2. + Id OpULessThanEqual(Id result_type, Id operand_1, Id operand_2); + + /// Signed-integer comparison if Operand 1 is less than or equal to Operand 2. + Id OpSLessThanEqual(Id result_type, Id operand_1, Id operand_2); + + /// Floating-point comparison for being ordered and equal. + Id OpFOrdEqual(Id result_type, Id operand_1, Id operand_2); + + /// Floating-point comparison for being unordered or equal. + Id OpFUnordEqual(Id result_type, Id operand_1, Id operand_2); + + /// Floating-point comparison for being ordered and not equal. + Id OpFOrdNotEqual(Id result_type, Id operand_1, Id operand_2); + + /// Floating-point comparison for being unordered or not equal. + Id OpFUnordNotEqual(Id result_type, Id operand_1, Id operand_2); + + /// Floating-point comparison if operands are ordered and Operand 1 is less than Operand 2. + Id OpFOrdLessThan(Id result_type, Id operand_1, Id operand_2); + + /// Floating-point comparison if operands are unordered or Operand 1 is less than Operand 2. + Id OpFUnordLessThan(Id result_type, Id operand_1, Id operand_2); + + /// Floating-point comparison if operands are ordered and Operand 1 is greater than Operand 2. + Id OpFOrdGreaterThan(Id result_type, Id operand_1, Id operand_2); + + /// Floating-point comparison if operands are unordered or Operand 1 is greater than Operand 2. + Id OpFUnordGreaterThan(Id result_type, Id operand_1, Id operand_2); + + /// Floating-point comparison if operands are ordered and Operand 1 is less than or equal to + /// Operand 2. + Id OpFOrdLessThanEqual(Id result_type, Id operand_1, Id operand_2); + + /// Floating-point comparison if operands are unordered or Operand 1 is less than or equal to + /// Operand 2. + Id OpFUnordLessThanEqual(Id result_type, Id operand_1, Id operand_2); + + /// Floating-point comparison if operands are ordered and Operand 1 is greater than or equal to + /// Operand 2. + Id OpFOrdGreaterThanEqual(Id result_type, Id operand_1, Id operand_2); + + /// Floating-point comparison if operands are unordered or Operand 1 is greater than or equal to + /// Operand 2. + Id OpFUnordGreaterThanEqual(Id result_type, Id operand_1, Id operand_2); + + // Conversion + + /// Convert (value preserving) from floating point to unsigned integer, with round toward 0.0. + Id OpConvertFToU(Id result_type, Id operand); + + /// Convert (value preserving) from floating point to signed integer, with round toward 0.0. + Id OpConvertFToS(Id result_type, Id operand); + + /// Convert (value preserving) from signed integer to floating point. + Id OpConvertSToF(Id result_type, Id operand); + + /// Convert (value preserving) from unsigned integer to floating point. + Id OpConvertUToF(Id result_type, Id operand); + + /// Convert (value preserving) unsigned width. This is either a truncate or a zero extend. + Id OpUConvert(Id result_type, Id operand); + + /// Convert (value preserving) signed width. This is either a truncate or a sign extend. + Id OpSConvert(Id result_type, Id operand); + + /// Convert (value preserving) floating-point width. + Id OpFConvert(Id result_type, Id operand); + + /// Quantize a floating-point value to what is expressible by a 16-bit floating-point value. + Id OpQuantizeToF16(Id result_type, Id operand); + + /// Bit pattern-preserving type conversion. + Id OpBitcast(Id result_type, Id operand); + + // Bit + + /// Shift the bits in Base right by the number of bits specified in Shift. + /// The most-significant bits will be zero filled. + Id OpShiftRightLogical(Id result_type, Id base, Id shift); + + /// Shift the bits in Base right by the number of bits specified in Shift. + /// The most-significant bits will be filled with the sign bit from Base. + Id OpShiftRightArithmetic(Id result_type, Id base, Id shift); + + /// Shift the bits in Base left by the number of bits specified in Shift. + /// The least-significant bits will be zero filled. + Id OpShiftLeftLogical(Id result_type, Id base, Id shift); + + /// Does a bitwise Or between operands 1 and 2. + Id OpBitwiseOr(Id result_type, Id operand_1, Id operand_2); + + /// Does a bitwise Xor between operands 1 and 2. + Id OpBitwiseXor(Id result_type, Id operand_1, Id operand_2); + + /// Result is 1 if both Operand 1 and Operand 2 are 1. Result is 0 if either + /// Operand 1 or Operand 2 are 0. + Id OpBitwiseAnd(Id result_type, Id operand_1, Id operand_2); + + /// Does a bitwise Not on the operand. + Id OpNot(Id result_type, Id operand); + + /// Make a copy of an object, with a modified bit field that comes from another object. + Id OpBitFieldInsert(Id result_type, Id base, Id insert, Id offset, Id count); + + /// Extract a bit field from an object, with sign extension. + Id OpBitFieldSExtract(Id result_type, Id base, Id offset, Id count); + + /// Extract a bit field from an object, without sign extension. + Id OpBitFieldUExtract(Id result_type, Id base, Id offset, Id count); + + /// Reverse the bits in an object. + Id OpBitReverse(Id result_type, Id base); + + /// Count the number of set bits in an object. + Id OpBitCount(Id result_type, Id base); + + // Arithmetic + + /// Floating-point subtract of Operand from zero. + Id OpSNegate(Id result_type, Id operand); + + /// Floating-point subtract of Operand from zero. + Id OpFNegate(Id result_type, Id operand); + + /// Integer addition of Operand 1 and Operand 2. + Id OpIAdd(Id result_type, Id operand_1, Id operand_2); + + /// Floating-point addition of Operand 1 and Operand 2. + Id OpFAdd(Id result_type, Id operand_1, Id operand_2); + + /// Integer substraction of Operand 1 and Operand 2. + Id OpISub(Id result_type, Id operand_1, Id operand_2); + + /// Floating-point subtraction of Operand 1 and Operand 2. + Id OpFSub(Id result_type, Id operand_1, Id operand_2); + + /// Integer multiplication of Operand 1 and Operand 2. + Id OpIMul(Id result_type, Id operand_1, Id operand_2); + + /// Floating-point multiplication of Operand 1 and Operand 2. + Id OpFMul(Id result_type, Id operand_1, Id operand_2); + + /// Unsigned-integer division of Operand 1 divided by Operand 2. + Id OpUDiv(Id result_type, Id operand_1, Id operand_2); + + /// signed-integer division of Operand 1 divided by Operand 2. + Id OpSDiv(Id result_type, Id operand_1, Id operand_2); + + /// Floating-point division of Operand 1 divided by Operand 2. + Id OpFDiv(Id result_type, Id operand_1, Id operand_2); + + /// Unsigned modulo operation of Operand 1 modulo Operand 2. + Id OpUMod(Id result_type, Id operand_1, Id operand_2); + + /// Signed modulo operation of Operand 1 modulo Operand 2. + Id OpSMod(Id result_type, Id operand_1, Id operand_2); + + /// Floating-point modulo operation of Operand 1 modulo Operand 2. + Id OpFMod(Id result_type, Id operand_1, Id operand_2); + + /// Signed reminder operation of Operand 1 modulo Operand 2. + Id OpSRem(Id result_type, Id operand_1, Id operand_2); + + /// Floating-point reminder operation of Operand 1 modulo Operand 2. + Id OpFRem(Id result_type, Id operand_1, Id operand_2); + + /// Result is the unsigned integer addition of Operand 1 and Operand 2, including its carry. + Id OpIAddCarry(Id result_type, Id operand_1, Id operand_2); + + // Extensions + + /// Execute an instruction in an imported set of extended instructions. + Id OpExtInst(Id result_type, Id set, std::uint32_t instruction, std::span operands); + + /// Execute an instruction in an imported set of extended instructions. + template + requires(...&& std::is_convertible_v) Id + OpExtInst(Id result_type, Id set, std::uint32_t instruction, Ts&&... operands) { + return OpExtInst(result_type, set, instruction, std::span({operands...})); + } + + /// Result is x if x >= 0; otherwise result is -x. + Id OpFAbs(Id result_type, Id x); + + /// Result is x if x >= 0; otherwise result is -x. + Id OpSAbs(Id result_type, Id x); + + /// Result is the value equal to the nearest whole number to x. The fraction 0.5 will round in a + /// direction chosen by the implementation, presumably the direction that is fastest. + Id OpRound(Id result_type, Id x); + + /// Result is the value equal to the nearest whole number to x. A fractional part of 0.5 will + /// round toward the nearest even whole number. + Id OpRoundEven(Id result_type, Id x); + + /// Result is the value equal to the nearest whole number to x whose absolute value is not + /// larger than the absolute value of x. + Id OpTrunc(Id result_type, Id x); + + /// Result is 1.0 if x > 0, 0.0 if x = 0, or -1.0 if x < 0. + Id OpFSign(Id result_type, Id x); + + /// Result is 1 if x > 0, 0 if x = 0, or -1 if x < 0, where x is interpreted as a signed + /// integer. + Id OpSSign(Id result_type, Id x); + + /// Result is the value equal to the nearest whole number that is less than or equal to x. + Id OpFloor(Id result_type, Id x); + + /// Result is the value equal to the nearest whole number that is greater than or equal to x. + Id OpCeil(Id result_type, Id x); + + /// Result is x - floor x. + Id OpFract(Id result_type, Id x); + + /// The standard trigonometric sine of x radians. + Id OpSin(Id result_type, Id x); + + /// The standard trigonometric cosine of x radians. + Id OpCos(Id result_type, Id x); + + /// Arc sine. Result is an angle, in radians, whose sine is x. The range of result values is + /// [-pi / 2, pi / 2]. Result is undefined if abs x > 1. + Id OpAsin(Id result_type, Id x); + + /// Arc cosine. Result is an angle, in radians, whose cosine is x. The range of result values is + /// [0, pi]. Result is undefined if abs x > 1. + Id OpAcos(Id result_type, Id x); + + /// Result is x raised to the y power. Result is undefined if x < 0. Result is undefined if x = + /// 0 and y <= 0. + Id OpPow(Id result_type, Id x, Id y); + + /// Result is the natural exponentiation of x. + Id OpExp(Id result_type, Id x); + + /// Result is the natural logarithm of x. Result is undefined if x <= 0. + Id OpLog(Id result_type, Id x); + + /// Result is 2 raised to the x power. + Id OpExp2(Id result_type, Id x); + + /// Result is the base-2 logarithm of x. Result is undefined if x <= 0. + Id OpLog2(Id result_type, Id x); + + /// Result is the square root of x. Result is undefined if x < 0. + Id OpSqrt(Id result_type, Id x); + + /// Result is the reciprocal of sqrt x. Result is undefined if x <= 0. + Id OpInverseSqrt(Id result_type, Id x); + + /// Result is y if y < x; otherwise result is x. Which operand is the result is undefined if one + /// of the operands is a NaN. + Id OpFMin(Id result_type, Id x, Id y); + + /// Result is y if y < x; otherwise result is x, where x and y are interpreted as unsigned + /// integers. + Id OpUMin(Id result_type, Id x, Id y); + + /// Result is y if y < x; otherwise result is x, where x and y are interpreted as signed + /// integers. + Id OpSMin(Id result_type, Id x, Id y); + + /// Result is y if x < y; otherwise result is x. Which operand is the result is undefined if one + /// of the operands is a NaN. + Id OpFMax(Id result_type, Id x, Id y); + + /// Result is y if x < y; otherwise result is x, where x and y are interpreted as unsigned + /// integers. + Id OpUMax(Id result_type, Id x, Id y); + + /// Result is y if x < y; otherwise result is x, where x and y are interpreted as signed + /// integers. + Id OpSMax(Id result_type, Id x, Id y); + + /// Result is min(max(x, minVal), maxVal). Result is undefined if minVal > maxVal.The semantics + /// used by min() and max() are those of FMin and FMax. + Id OpFClamp(Id result_type, Id x, Id min_val, Id max_val); + + /// Result is min(max(x, minVal), maxVal), where x, minVal and maxVal are interpreted as + /// unsigned integers. Result is undefined if minVal > maxVal. + Id OpUClamp(Id result_type, Id x, Id min_val, Id max_val); + + /// Result is min(max(x, minVal), maxVal), where x, minVal and maxVal are interpreted as signed + /// integers. Result is undefined if minVal > maxVal. + Id OpSClamp(Id result_type, Id x, Id min_val, Id max_val); + + /// Computes a * b + c. + Id OpFma(Id result_type, Id a, Id b, Id c); + + /// Result is the unsigned integer obtained by converting the components of a two-component + /// floating-point vector to the 16-bit OpTypeFloat, and then packing these two 16-bit integers + /// into a 32-bit unsigned integer. + Id OpPackHalf2x16(Id result_type, Id v); + + /// Result is the two-component floating-point vector with components obtained by unpacking a + /// 32-bit unsigned integer into a pair of 16-bit values. + Id OpUnpackHalf2x16(Id result_type, Id v); + + /// Integer least-significant bit. + Id OpFindILsb(Id result_type, Id value); + + /// Signed-integer most-significant bit, with value interpreted as a signed integer. + Id OpFindSMsb(Id result_type, Id value); + + /// Unsigned-integer most-significant bit. + Id OpFindUMsb(Id result_type, Id value); + + /// Result is the value of the input interpolant sampled at a location inside both the pixel and + /// the primitive being processed. + Id OpInterpolateAtCentroid(Id result_type, Id interpolant); + + /// Result is the value of the input interpolant variable at the location of sample number + /// sample. + Id OpInterpolateAtSample(Id result_type, Id interpolant, Id sample); + + /// Result is the value of the input interpolant variable sampled at an offset from the center + /// of the pixel specified by offset. + Id OpInterpolateAtOffset(Id result_type, Id interpolant, Id offset); + + // Derivatives + + /// Same result as either OpDPdxFine or OpDPdxCoarse on the input. + /// Selection of which one is based on external factors. + Id OpDPdx(Id result_type, Id operand); + + /// Same result as either OpDPdyFine or OpDPdyCoarse on the input. + /// Selection of which one is based on external factors. + Id OpDPdy(Id result_type, Id operand); + + /// Result is the same as computing the sum of the absolute values of OpDPdx and OpDPdy + /// on the input. + Id OpFwidth(Id result_type, Id operand); + + /// Result is the partial derivative of the input with respect to the window x coordinate. + /// Uses local differencing based on the value of the input for the current fragment and + /// its immediate neighbor(s). + Id OpDPdxFine(Id result_type, Id operand); + + /// Result is the partial derivative of the input with respect to the window y coordinate. + /// Uses local differencing based on the value of the input for the current fragment and + /// its immediate neighbor(s). + Id OpDPdyFine(Id result_type, Id operand); + + /// Result is the same as computing the sum of the absolute values of OpDPdxFine and OpDPdyFine + /// on the input. + Id OpFwidthFine(Id result_type, Id operand); + + /// Result is the partial derivative of the input with respect to the window x coordinate. + /// Uses local differencing based on the value of the input for the current fragment's + /// neighbors, and possibly, but not necessarily, includes the value of the input for the + /// current fragment. That is, over a given area, the implementation can compute x derivatives + /// in fewer unique locations than would be allowed for OpDPdxFine. + Id OpDPdxCoarse(Id result_type, Id operand); + + /// Result is the partial derivative of the input with respect to the window y coordinate. + /// Uses local differencing based on the value of the input for the current fragment's + /// neighbors, and possibly, but not necessarily, includes the value of the input for the + /// current fragment. That is, over a given area, the implementation can compute y derivatives + /// in fewer unique locations than would be allowed for OpDPdyFine. + Id OpDPdyCoarse(Id result_type, Id operand); + + /// Result is the same as computing the sum of the absolute values of OpDPdxCoarse and + /// OpDPdyCoarse on the input. + Id OpFwidthCoarse(Id result_type, Id operand); + + // Image + + /// Create a sampled image, containing both a sampler and an image. + Id OpSampledImage(Id result_type, Id image, Id sampler); + + /// Sample an image with an implicit level of detail. + Id OpImageSampleImplicitLod(Id result_type, Id sampled_image, Id coordinate, + std::optional image_operands = std::nullopt, + std::span operands = {}); + + /// Sample an image with an implicit level of detail. + template + requires(...&& std::is_convertible_v) Id + OpImageSampleImplicitLod(Id result_type, Id sampled_image, Id coordinate, + spv::ImageOperandsMask image_operands, Ts&&... operands) { + return OpImageSampleImplicitLod(result_type, sampled_image, coordinate, image_operands, + std::span({operands...})); + } + + /// Sample an image using an explicit level of detail. + Id OpImageSampleExplicitLod(Id result_type, Id sampled_image, Id coordinate, + spv::ImageOperandsMask image_operands, + std::span operands = {}); + + /// Sample an image using an explicit level of detail. + template + requires(...&& std::is_convertible_v) Id + OpImageSampleExplicitLod(Id result_type, Id sampled_image, Id coordinate, + spv::ImageOperandsMask image_operands, Ts&&... operands) { + return OpImageSampleExplicitLod(result_type, sampled_image, coordinate, image_operands, + std::span({operands...})); + } + + /// Sample an image doing depth-comparison with an implicit level of detail. + Id OpImageSampleDrefImplicitLod( + Id result_type, Id sampled_image, Id coordinate, Id dref, + std::optional image_operands = std::nullopt, + std::span operands = {}); + + /// Sample an image doing depth-comparison with an implicit level of detail. + template + requires(...&& std::is_convertible_v) Id + OpImageSampleDrefImplicitLod(Id result_type, Id sampled_image, Id coordinate, Id dref, + spv::ImageOperandsMask image_operands, Ts&&... operands) { + return OpImageSampleDrefImplicitLod(result_type, sampled_image, coordinate, dref, + image_operands, std::span({operands...})); + } + + /// Sample an image doing depth-comparison using an explicit level of detail. + Id OpImageSampleDrefExplicitLod(Id result_type, Id sampled_image, Id coordinate, Id dref, + spv::ImageOperandsMask image_operands, + std::span operands = {}); + + /// Sample an image doing depth-comparison using an explicit level of detail. + template + requires(...&& std::is_convertible_v) Id + OpImageSampleDrefExplicitLod(Id result_type, Id sampled_image, Id coordinate, Id dref, + spv::ImageOperandsMask image_operands, Ts&&... operands) { + return OpImageSampleDrefExplicitLod(result_type, sampled_image, coordinate, dref, + image_operands, std::span({operands...})); + } + + /// Sample an image with with a project coordinate and an implicit level of detail. + Id OpImageSampleProjImplicitLod( + Id result_type, Id sampled_image, Id coordinate, + std::optional image_operands = std::nullopt, + std::span operands = {}); + + /// Sample an image with with a project coordinate and an implicit level of detail. + template + requires(...&& std::is_convertible_v) Id + OpImageSampleProjImplicitLod(Id result_type, Id sampled_image, Id coordinate, + spv::ImageOperandsMask image_operands, Ts&&... operands) { + return OpImageSampleProjImplicitLod(result_type, sampled_image, coordinate, image_operands, + std::span({operands...})); + } + + /// Sample an image with a project coordinate using an explicit level of detail. + Id OpImageSampleProjExplicitLod(Id result_type, Id sampled_image, Id coordinate, + spv::ImageOperandsMask image_operands, + std::span operands = {}); + + /// Sample an image with a project coordinate using an explicit level of detail. + template + requires(...&& std::is_convertible_v) Id + OpImageSampleProjExplicitLod(Id result_type, Id sampled_image, Id coordinate, + spv::ImageOperandsMask image_operands, Ts&&... operands) { + return OpImageSampleProjExplicitLod(result_type, sampled_image, coordinate, image_operands, + std::span({operands...})); + } + + /// Sample an image with a project coordinate, doing depth-comparison, with an implicit level of + /// detail. + Id OpImageSampleProjDrefImplicitLod( + Id result_type, Id sampled_image, Id coordinate, Id dref, + std::optional image_operands = std::nullopt, + std::span operands = {}); + + /// Sample an image with a project coordinate, doing depth-comparison, with an implicit level of + /// detail. + template + requires(...&& std::is_convertible_v) Id + OpImageSampleProjDrefImplicitLod(Id result_type, Id sampled_image, Id coordinate, Id dref, + spv::ImageOperandsMask image_operands, Ts&&... operands) { + return OpImageSampleProjDrefImplicitLod(result_type, sampled_image, coordinate, dref, + image_operands, std::span({operands...})); + } + + /// Sample an image with a project coordinate, doing depth-comparison, using an explicit level + /// of detail. + Id OpImageSampleProjDrefExplicitLod(Id result_type, Id sampled_image, Id coordinate, Id dref, + spv::ImageOperandsMask image_operands, + std::span operands = {}); + + /// Sample an image with a project coordinate, doing depth-comparison, using an explicit level + /// of detail. + template + requires(...&& std::is_convertible_v) Id + OpImageSampleProjDrefExplicitLod(Id result_type, Id sampled_image, Id coordinate, Id dref, + spv::ImageOperandsMask image_operands, Ts&&... operands) { + return OpImageSampleProjDrefExplicitLod(result_type, sampled_image, coordinate, dref, + image_operands, std::span({operands...})); + } + + /// Fetch a single texel from an image whose Sampled operand is 1. + Id OpImageFetch(Id result_type, Id sampled_image, Id coordinate, + std::optional image_operands = std::nullopt, + std::span operands = {}); + + /// Fetch a single texel from an image whose Sampled operand is 1. + template + requires(...&& std::is_convertible_v) Id + OpImageFetch(Id result_type, Id sampled_image, Id coordinate, + spv::ImageOperandsMask image_operands, Ts&&... operands) { + return OpImageFetch(result_type, sampled_image, coordinate, image_operands, + std::span({operands...})); + } + + /// Gathers the requested component from four texels. + Id OpImageGather(Id result_type, Id sampled_image, Id coordinate, Id component, + std::optional image_operands = std::nullopt, + std::span operands = {}); + + /// Gathers the requested component from four texels. + template + requires(...&& std::is_convertible_v) Id + OpImageGather(Id result_type, Id sampled_image, Id coordinate, Id component, + spv::ImageOperandsMask image_operands, Ts&&... operands) { + return OpImageGather(result_type, sampled_image, coordinate, component, image_operands, + std::span({operands...})); + } + + /// Gathers the requested depth-comparison from four texels. + Id OpImageDrefGather(Id result_type, Id sampled_image, Id coordinate, Id dref, + std::optional image_operands = std::nullopt, + std::span operands = {}); + + /// Gathers the requested depth-comparison from four texels. + template + requires(...&& std::is_convertible_v) Id + OpImageDrefGather(Id result_type, Id sampled_image, Id coordinate, Id dref, + spv::ImageOperandsMask image_operands, Ts&&... operands) { + return OpImageDrefGather(result_type, sampled_image, coordinate, dref, image_operands, + std::span({operands...})); + } + + /// Read a texel from an image without a sampler. + Id OpImageRead(Id result_type, Id sampled_image, Id coordinate, + std::optional image_operands = std::nullopt, + std::span operands = {}); + + /// Read a texel from an image without a sampler. + template + requires(...&& std::is_convertible_v) Id + OpImageRead(Id result_type, Id sampled_image, Id coordinate, + spv::ImageOperandsMask image_operands, Ts&&... operands) { + return OpImageRead(result_type, sampled_image, coordinate, image_operands, + std::span({operands...})); + } + + /// Write a texel to an image without a sampler. + Id OpImageWrite(Id image, Id coordinate, Id texel, + std::optional image_operands = std::nullopt, + std::span operands = {}); + + /// Write a texel to an image without a sampler. + template + requires(...&& std::is_convertible_v) Id + OpImageWrite(Id image, Id coordinate, Id texel, spv::ImageOperandsMask image_operands, + Ts&&... operands) { + return OpImageWrite(image, coordinate, texel, image_operands, + std::span({operands...})); + } + + /// Extract the image from a sampled image. + Id OpImage(Id result_type, Id sampled_image); + + /// Query the dimensions of Image for mipmap level for Level of Detail. + Id OpImageQuerySizeLod(Id result_type, Id image, Id level_of_detail); + + /// Query the dimensions of Image, with no level of detail. + Id OpImageQuerySize(Id result_type, Id image); + + /// Query the mipmap level and the level of detail for a hypothetical sampling of Image at + /// Coordinate using an implicit level of detail. + Id OpImageQueryLod(Id result_type, Id image, Id coordinate); + + /// Query the number of mipmap levels accessible through Image. + Id OpImageQueryLevels(Id result_type, Id image); + + /// Query the number of samples available per texel fetch in a multisample image. + Id OpImageQuerySamples(Id result_type, Id image); + + /// Sample a sparse image with an implicit level of detail. + Id OpImageSparseSampleImplicitLod(Id result_type, Id sampled_image, Id coordinate, + std::optional image_operands, + std::span operands); + + /// Sample a sparse image using an explicit level of detail. + Id OpImageSparseSampleExplicitLod(Id result_type, Id sampled_image, Id coordinate, + spv::ImageOperandsMask image_operands, + std::span operands); + + /// Sample a sparse image doing depth-comparison with an implicit level of detail. + Id OpImageSparseSampleDrefImplicitLod(Id result_type, Id sampled_image, Id coordinate, Id dref, + std::optional image_operands, + std::span operands); + + /// Sample a sparse image doing depth-comparison using an explicit level of detail. + Id OpImageSparseSampleDrefExplicitLod(Id result_type, Id sampled_image, Id coordinate, Id dref, + spv::ImageOperandsMask image_operands, + std::span operands); + + /// Fetch a single texel from a sampled sparse image. + Id OpImageSparseFetch(Id result_type, Id image, Id coordinate, + std::optional image_operands, + std::span operands); + + /// Gathers the requested component from four texels of a sparse image. + Id OpImageSparseGather(Id result_type, Id sampled_image, Id coordinate, Id component, + std::optional image_operands, + std::span operands); + + /// Gathers the requested depth-comparison from four texels of a sparse image. + Id OpImageSparseDrefGather(Id result_type, Id sampled_image, Id coordinate, Id dref, + std::optional image_operands, + std::span operands); + + /// Translates a Resident Code into a Boolean. Result is false if any of the texels were in + /// uncommitted texture memory, and true otherwise. + Id OpImageSparseTexelsResident(Id result_type, Id resident_code); + + /// Read a texel from a sparse image without a sampler. + Id OpImageSparseRead(Id result_type, Id image, Id coordinate, + std::optional image_operands, + std::span operands); + + // Group + + /// Computes a bitfield value combining the Predicate value from all invocations in the current + /// Subgroup that execute the same dynamic instance of this instruction. + Id OpSubgroupBallotKHR(Id result_type, Id predicate); + + /// Return the value from the invocation in the subgroup with an invocation ID equal to index. + /// The index must be the same for all active invocations in the subgroup, otherwise the results + /// are undefined. + Id OpSubgroupReadInvocationKHR(Id result_type, Id value, Id index); + + /// TBD + Id OpSubgroupAllKHR(Id result_type, Id predicate); + + /// TBD + Id OpSubgroupAnyKHR(Id result_type, Id predicate); + + /// TBD + Id OpSubgroupAllEqualKHR(Id result_type, Id predicate); + + // Result is true only in the active invocation with the lowest id in the group, otherwise + // result is false. + Id OpGroupNonUniformElect(Id result_type, Id scope); + + // Result is the Value of the invocation from the active invocation with the lowest id in the + // group to all active invocations in the group. + Id OpGroupNonUniformBroadcastFirst(Id result_type, Id scope, Id value); + + // Result is the Value of the invocation identified by the id Id to all active invocations in + // the group. + Id OpGroupNonUniformBroadcast(Id result_type, Id scope, Id value, Id id); + + // Result is the Value of the invocation identified by the id Id. + Id OpGroupNonUniformShuffle(Id result_type, Id scope, Id value, Id id); + + /// Return the value of the invocation identified by the current invocation's id within the + /// group xor'ed with mask. + Id OpGroupNonUniformShuffleXor(Id result_type, Id scope, Id value, Id mask); + + /// Evaluates a predicate for all active invocations in the group, resulting in + /// true if predicate evaluates to true for all active invocations in the + /// group, otherwise the result is false. + Id OpGroupNonUniformAll(Id result_type, Id scope, Id predicate); + + /// Evaluates a predicate for all active invocations in the group, + /// resulting in true if predicate evaluates to true for any active + /// invocation in the group, otherwise the result is false. + Id OpGroupNonUniformAny(Id result_type, Id scope, Id predicate); + + /// Evaluates a value for all active invocations in the group. The result + /// is true if Value is equal for all active invocations in the group. + /// Otherwise, the result is false. + Id OpGroupNonUniformAllEqual(Id result_type, Id scope, Id value); + + /// Result is a bitfield value combining the Predicate value from all + /// invocations in the group that execute the same dynamic instance of this + /// instruction. The bit is set to one if the corresponding invocation is + /// active and the Predicate for that invocation evaluated to true; + /// otherwise, it is set to zero. + Id OpGroupNonUniformBallot(Id result_type, Id scope, Id predicate); + + // Atomic + + /// Atomically load through Pointer using the given Semantics. All subparts of the value that is + /// loaded will be read atomically with respect to all other atomic accesses to it within Scope. + Id OpAtomicLoad(Id result_type, Id pointer, Id memory, Id semantics); + + /// Atomically store through Pointer using the given Semantics. All subparts of Value will be + /// written atomically with respect to all other atomic accesses to it within Scope. + Id OpAtomicStore(Id pointer, Id memory, Id semantics, Id value); + + /// Perform the following steps atomically with respect to any other atomic accesses within + /// Scope to the same location: + /// 1) load through Pointer to get an Original Value, + /// 2) get a New Value from copying Value, and + /// 3) store the New Value back through Pointer. + Id OpAtomicExchange(Id result_type, Id pointer, Id memory, Id semantics, Id value); + + /// Perform the following steps atomically with respect to any other atomic accesses within + /// Scope to the same location: + /// 1) load through Pointer to get an Original Value, + /// 2) get a New Value from Value only if Original Value equals Comparator, and + /// 3) store the New Value back through Pointer only if 'Original Value equaled Comparator. + Id OpAtomicCompareExchange(Id result_type, Id pointer, Id memory, Id equal, Id unequal, + Id value, Id comparator); + + /// Perform the following steps atomically with respect to any other atomic accesses within + /// Scope to the same location: + /// 1) load through Pointer to get an Original Value, + /// 2) get a New Value through integer addition of 1 to Original Value, and + /// 3) store the New Value back through Pointer. + Id OpAtomicIIncrement(Id result_type, Id pointer, Id memory, Id semantics); + + /// Perform the following steps atomically with respect to any other atomic accesses within + /// Scope to the same location: + /// 1) load through Pointer to get an Original Value, + /// 2) get a New Value through integer subtraction of 1 from Original Value, and + /// 3) store the New Value back through Pointer. + Id OpAtomicIDecrement(Id result_type, Id pointer, Id memory, Id semantics); + + /// Perform the following steps atomically with respect to any other atomic accesses within + /// Scope to the same location: + /// 1) load through Pointer to get an Original Value, + /// 2) get a New Value by integer addition of Original Value and Value, and + /// 3) store the New Value back through Pointer. + Id OpAtomicIAdd(Id result_type, Id pointer, Id memory, Id semantics, Id value); + + /// Perform the following steps atomically with respect to any other atomic accesses within + /// Scope to the same location: + /// 1) load through Pointer to get an Original Value, + /// 2) get a New Value by integer subtraction of Value from Original Value, and + /// 3) store the New Value back through Pointer. + Id OpAtomicISub(Id result_type, Id pointer, Id memory, Id semantics, Id value); + + /// Perform the following steps atomically with respect to any other atomic accesses within + /// Scope to the same location: + /// 1) load through Pointer to get an Original Value, + /// 2) get a New Value by finding the smallest signed integer of Original Value and Value, and + /// 3) store the New Value back through Pointer. + Id OpAtomicSMin(Id result_type, Id pointer, Id memory, Id semantics, Id value); + + /// Perform the following steps atomically with respect to any other atomic accesses within + /// Scope to the same location: + /// 1) load through Pointer to get an Original Value, + /// 2) get a New Value by finding the smallest unsigned integer of Original Value and Value, and + /// 3) store the New Value back through Pointer. + Id OpAtomicUMin(Id result_type, Id pointer, Id memory, Id semantics, Id value); + + /// Perform the following steps atomically with respect to any other atomic accesses within + /// Scope to the same location: + /// 1) load through Pointer to get an Original Value, + /// 2) get a New Value by finding the largest signed integer of Original Value and Value, and + /// 3) store the New Value back through Pointer. + Id OpAtomicSMax(Id result_type, Id pointer, Id memory, Id semantics, Id value); + + /// Perform the following steps atomically with respect to any other atomic accesses within + /// Scope to the same location: + /// 1) load through Pointer to get an Original Value, + /// 2) get a New Value by finding the largest unsigned integer of Original Value and Value, and + /// 3) store the New Value back through Pointer. + Id OpAtomicUMax(Id result_type, Id pointer, Id memory, Id semantics, Id value); + + /// Perform the following steps atomically with respect to any other atomic accesses within + /// Scope to the same location: + /// 1) load through Pointer to get an Original Value, + /// 2) get a New Value by the bitwise AND of Original Value and Value, and + /// 3) store the New Value back through Pointer. + Id OpAtomicAnd(Id result_type, Id pointer, Id memory, Id semantics, Id value); + + /// Perform the following steps atomically with respect to any other atomic accesses within + /// Scope to the same location: + /// 1) load through Pointer to get an Original Value, + /// 2) get a New Value by the bitwise OR of Original Value and Value, and + /// 3) store the New Value back through Pointer. + Id OpAtomicOr(Id result_type, Id pointer, Id memory, Id semantics, Id value); + + /// Perform the following steps atomically with respect to any other atomic accesses within + /// Scope to the same location: + /// 1) load through Pointer to get an Original Value, + /// 2) get a New Value by the bitwise exclusive OR of Original Value and Value, and + /// 3) store the New Value back through Pointer. + Id OpAtomicXor(Id result_type, Id pointer, Id memory, Id semantics, Id value); + +private: + Id GetGLSLstd450(); + + std::uint32_t version{}; + std::uint32_t bound{}; + + std::unordered_set extensions; + std::unordered_set capabilities; + std::optional glsl_std_450; + + spv::AddressingModel addressing_model{spv::AddressingModel::Logical}; + spv::MemoryModel memory_model{spv::MemoryModel::GLSL450}; + + std::unique_ptr ext_inst_imports; + std::unique_ptr entry_points; + std::unique_ptr execution_modes; + std::unique_ptr debug; + std::unique_ptr annotations; + std::unique_ptr declarations; + std::unique_ptr global_variables; + std::unique_ptr code; + std::vector deferred_phi_nodes; +}; + +} // namespace Sirit diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 0000000..06d55cb --- /dev/null +++ b/src/.gitignore @@ -0,0 +1 @@ +sirit.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..18f3e1b --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,32 @@ +add_library(sirit + ../include/sirit/sirit.h + sirit.cpp + stream.h + common_types.h + instructions/type.cpp + instructions/constant.cpp + instructions/function.cpp + instructions/flow.cpp + instructions/debug.cpp + instructions/derivatives.cpp + instructions/memory.cpp + instructions/annotation.cpp + instructions/misc.cpp + instructions/logical.cpp + instructions/conversion.cpp + instructions/bit.cpp + instructions/arithmetic.cpp + instructions/extension.cpp + instructions/image.cpp + instructions/group.cpp + instructions/barrier.cpp + instructions/atomic.cpp +) + +target_compile_options(sirit PRIVATE ${SIRIT_CXX_FLAGS}) + +target_include_directories(sirit + PUBLIC ../include + PRIVATE .) + +target_link_libraries(sirit PUBLIC SPIRV-Headers::SPIRV-Headers) diff --git a/src/common_types.h b/src/common_types.h new file mode 100644 index 0000000..42df6f6 --- /dev/null +++ b/src/common_types.h @@ -0,0 +1,31 @@ +/* This file is part of the sirit project. + * Copyright (c) 2019 sirit + * This software may be used and distributed according to the terms of the + * 3-Clause BSD License + */ + +#pragma once + +#include +#include + +namespace Sirit { + +using u8 = std::uint8_t; +using u16 = std::uint16_t; +using u32 = std::uint32_t; +using u64 = std::uint64_t; +using uptr = std::uintptr_t; + +using s8 = std::int8_t; +using s16 = std::int16_t; +using s32 = std::int32_t; +using s64 = std::int64_t; +using sptr = std::intptr_t; + +using f32 = float; +using f64 = double; +static_assert(sizeof(f32) == sizeof(u32), "f32 must be 32 bits wide"); +static_assert(sizeof(f64) == sizeof(u64), "f64 must be 64 bits wide"); + +} // namespace Sirit diff --git a/src/instructions/annotation.cpp b/src/instructions/annotation.cpp new file mode 100644 index 0000000..e3524bb --- /dev/null +++ b/src/instructions/annotation.cpp @@ -0,0 +1,27 @@ +/* This file is part of the sirit project. + * Copyright (c) 2019 sirit + * This software may be used and distributed according to the terms of the + * 3-Clause BSD License + */ + +#include + +#include "sirit/sirit.h" + +#include "stream.h" + +namespace Sirit { + +Id Module::Decorate(Id target, spv::Decoration decoration, std::span literals) { + annotations->Reserve(3 + literals.size()); + return *annotations << spv::Op::OpDecorate << target << decoration << literals << EndOp{}; +} + +Id Module::MemberDecorate(Id structure_type, Literal member, spv::Decoration decoration, + std::span literals) { + annotations->Reserve(4 + literals.size()); + return *annotations << spv::Op::OpMemberDecorate << structure_type << member << decoration + << literals << EndOp{}; +} + +} // namespace Sirit diff --git a/src/instructions/arithmetic.cpp b/src/instructions/arithmetic.cpp new file mode 100644 index 0000000..4fa8057 --- /dev/null +++ b/src/instructions/arithmetic.cpp @@ -0,0 +1,44 @@ +/* This file is part of the sirit project. + * Copyright (c) 2019 sirit + * This software may be used and distributed according to the terms of the + * 3-Clause BSD License + */ + +#include "sirit/sirit.h" + +#include "stream.h" + +namespace Sirit { + +#define DEFINE_UNARY(funcname, opcode) \ + Id Module::funcname(Id result_type, Id operand) { \ + code->Reserve(4); \ + return *code << OpId{opcode, result_type} << operand << EndOp{}; \ + } + +#define DEFINE_BINARY(funcname, opcode) \ + Id Module::funcname(Id result_type, Id operand_1, Id operand_2) { \ + code->Reserve(5); \ + return *code << OpId{opcode, result_type} << operand_1 << operand_2 << EndOp{}; \ + } + +DEFINE_UNARY(OpSNegate, spv::Op::OpSNegate) +DEFINE_UNARY(OpFNegate, spv::Op::OpFNegate) + +DEFINE_BINARY(OpIAdd, spv::Op::OpIAdd) +DEFINE_BINARY(OpFAdd, spv::Op::OpFAdd) +DEFINE_BINARY(OpISub, spv::Op::OpISub) +DEFINE_BINARY(OpFSub, spv::Op::OpFSub) +DEFINE_BINARY(OpIMul, spv::Op::OpIMul) +DEFINE_BINARY(OpFMul, spv::Op::OpFMul) +DEFINE_BINARY(OpUDiv, spv::Op::OpUDiv) +DEFINE_BINARY(OpSDiv, spv::Op::OpSDiv) +DEFINE_BINARY(OpFDiv, spv::Op::OpFDiv) +DEFINE_BINARY(OpUMod, spv::Op::OpUMod) +DEFINE_BINARY(OpSMod, spv::Op::OpSMod) +DEFINE_BINARY(OpFMod, spv::Op::OpFMod) +DEFINE_BINARY(OpSRem, spv::Op::OpSRem) +DEFINE_BINARY(OpFRem, spv::Op::OpFRem) +DEFINE_BINARY(OpIAddCarry, spv::Op::OpIAddCarry) + +} // namespace Sirit diff --git a/src/instructions/atomic.cpp b/src/instructions/atomic.cpp new file mode 100644 index 0000000..cee8849 --- /dev/null +++ b/src/instructions/atomic.cpp @@ -0,0 +1,104 @@ +/* This file is part of the sirit project. + * Copyright (c) 2019 sirit + * This software may be used and distributed according to the terms of the + * 3-Clause BSD License + */ + +#include "sirit/sirit.h" + +#include "stream.h" + +namespace Sirit { + +Id Module::OpAtomicLoad(Id result_type, Id pointer, Id memory, Id semantics) { + code->Reserve(6); + return *code << OpId{spv::Op::OpAtomicLoad, result_type} << pointer << memory << semantics + << EndOp{}; +} + +Id Module::OpAtomicStore(Id pointer, Id memory, Id semantics, Id value) { + code->Reserve(5); + return *code << OpId{spv::Op::OpAtomicStore} << pointer << memory << semantics << value + << EndOp{}; +} + +Id Module::OpAtomicExchange(Id result_type, Id pointer, Id memory, Id semantics, Id value) { + code->Reserve(7); + return *code << OpId{spv::Op::OpAtomicExchange, result_type} << pointer << memory << semantics + << value << EndOp{}; +} + +Id Module::OpAtomicCompareExchange(Id result_type, Id pointer, Id memory, Id equal, Id unequal, + Id value, Id comparator) { + code->Reserve(9); + return *code << OpId{spv::Op::OpAtomicCompareExchange, result_type} << pointer << memory + << equal << unequal << value << comparator << EndOp{}; +} + +Id Module::OpAtomicIIncrement(Id result_type, Id pointer, Id memory, Id semantics) { + code->Reserve(6); + return *code << OpId{spv::Op::OpAtomicIIncrement, result_type} << pointer << memory << semantics + << EndOp{}; +} + +Id Module::OpAtomicIDecrement(Id result_type, Id pointer, Id memory, Id semantics) { + code->Reserve(6); + return *code << OpId{spv::Op::OpAtomicIDecrement, result_type} << pointer << memory << semantics + << EndOp{}; +} + +Id Module::OpAtomicIAdd(Id result_type, Id pointer, Id memory, Id semantics, Id value) { + code->Reserve(7); + return *code << OpId{spv::Op::OpAtomicIAdd, result_type} << pointer << memory << semantics + << value << EndOp{}; +} + +Id Module::OpAtomicISub(Id result_type, Id pointer, Id memory, Id semantics, Id value) { + code->Reserve(7); + return *code << OpId{spv::Op::OpAtomicISub, result_type} << pointer << memory << semantics + << value << EndOp{}; +} + +Id Module::OpAtomicSMin(Id result_type, Id pointer, Id memory, Id semantics, Id value) { + code->Reserve(7); + return *code << OpId{spv::Op::OpAtomicSMin, result_type} << pointer << memory << semantics + << value << EndOp{}; +} + +Id Module::OpAtomicUMin(Id result_type, Id pointer, Id memory, Id semantics, Id value) { + code->Reserve(7); + return *code << OpId{spv::Op::OpAtomicUMin, result_type} << pointer << memory << semantics + << value << EndOp{}; +} + +Id Module::OpAtomicSMax(Id result_type, Id pointer, Id memory, Id semantics, Id value) { + code->Reserve(7); + return *code << OpId{spv::Op::OpAtomicSMax, result_type} << pointer << memory << semantics + << value << EndOp{}; +} + +Id Module::OpAtomicUMax(Id result_type, Id pointer, Id memory, Id semantics, Id value) { + code->Reserve(7); + return *code << OpId{spv::Op::OpAtomicUMax, result_type} << pointer << memory << semantics + << value << EndOp{}; +} + +Id Module::OpAtomicAnd(Id result_type, Id pointer, Id memory, Id semantics, Id value) { + code->Reserve(7); + return *code << OpId{spv::Op::OpAtomicAnd, result_type} << pointer << memory << semantics + << value << EndOp{}; +} + +Id Module::OpAtomicOr(Id result_type, Id pointer, Id memory, Id semantics, Id value) { + code->Reserve(7); + return *code << OpId{spv::Op::OpAtomicOr, result_type} << pointer << memory << semantics + << value << EndOp{}; +} + +Id Module::OpAtomicXor(Id result_type, Id pointer, Id memory, Id semantics, Id value) { + code->Reserve(7); + return *code << OpId{spv::Op::OpAtomicXor, result_type} << pointer << memory << semantics + << value << EndOp{}; +} + +} // namespace Sirit diff --git a/src/instructions/barrier.cpp b/src/instructions/barrier.cpp new file mode 100644 index 0000000..646b5cf --- /dev/null +++ b/src/instructions/barrier.cpp @@ -0,0 +1,23 @@ +/* This file is part of the sirit project. + * Copyright (c) 2019 sirit + * This software may be used and distributed according to the terms of the + * 3-Clause BSD License + */ + +#include "sirit/sirit.h" + +#include "stream.h" + +namespace Sirit { + +Id Module::OpControlBarrier(Id execution, Id memory, Id semantics) { + code->Reserve(4); + return *code << spv::Op::OpControlBarrier << execution << memory << semantics << EndOp{}; +} + +Id Module::OpMemoryBarrier(Id scope, Id semantics) { + code->Reserve(3); + return *code << spv::Op::OpMemoryBarrier << scope << semantics << EndOp{}; +} + +} // namespace Sirit diff --git a/src/instructions/bit.cpp b/src/instructions/bit.cpp new file mode 100644 index 0000000..c860613 --- /dev/null +++ b/src/instructions/bit.cpp @@ -0,0 +1,76 @@ +/* This file is part of the sirit project. + * Copyright (c) 2019 sirit + * This software may be used and distributed according to the terms of the + * 3-Clause BSD License + */ + +#include "sirit/sirit.h" + +#include "stream.h" + +namespace Sirit { + +Id Module::OpShiftRightLogical(Id result_type, Id base, Id shift) { + code->Reserve(5); + return *code << OpId{spv::Op::OpShiftRightLogical, result_type} << base << shift << EndOp{}; +} + +Id Module::OpShiftRightArithmetic(Id result_type, Id base, Id shift) { + code->Reserve(5); + return *code << OpId{spv::Op::OpShiftRightArithmetic, result_type} << base << shift << EndOp{}; +} + +Id Module::OpShiftLeftLogical(Id result_type, Id base, Id shift) { + code->Reserve(5); + return *code << OpId{spv::Op::OpShiftLeftLogical, result_type} << base << shift << EndOp{}; +} + +Id Module::OpBitwiseOr(Id result_type, Id operand_1, Id operand_2) { + code->Reserve(5); + return *code << OpId{spv::Op::OpBitwiseOr, result_type} << operand_1 << operand_2 << EndOp{}; +} + +Id Module::OpBitwiseXor(Id result_type, Id operand_1, Id operand_2) { + code->Reserve(5); + return *code << OpId{spv::Op::OpBitwiseXor, result_type} << operand_1 << operand_2 << EndOp{}; +} + +Id Module::OpBitwiseAnd(Id result_type, Id operand_1, Id operand_2) { + code->Reserve(5); + return *code << OpId{spv::Op::OpBitwiseAnd, result_type} << operand_1 << operand_2 << EndOp{}; +} + +Id Module::OpNot(Id result_type, Id operand) { + code->Reserve(4); + return *code << OpId{spv::Op::OpNot, result_type} << operand << EndOp{}; +} + +Id Module::OpBitFieldInsert(Id result_type, Id base, Id insert, Id offset, Id count) { + code->Reserve(7); + return *code << OpId{spv::Op::OpBitFieldInsert, result_type} << base << insert << offset + << count << EndOp{}; +} + +Id Module::OpBitFieldSExtract(Id result_type, Id base, Id offset, Id count) { + code->Reserve(6); + return *code << OpId{spv::Op::OpBitFieldSExtract, result_type} << base << offset << count + << EndOp{}; +} + +Id Module::OpBitFieldUExtract(Id result_type, Id base, Id offset, Id count) { + code->Reserve(6); + return *code << OpId{spv::Op::OpBitFieldUExtract, result_type} << base << offset << count + << EndOp{}; +} + +Id Module::OpBitReverse(Id result_type, Id base) { + code->Reserve(4); + return *code << OpId{spv::Op::OpBitReverse, result_type} << base << EndOp{}; +} + +Id Module::OpBitCount(Id result_type, Id base) { + code->Reserve(4); + return *code << OpId{spv::Op::OpBitCount, result_type} << base << EndOp{}; +} + +} // namespace Sirit diff --git a/src/instructions/constant.cpp b/src/instructions/constant.cpp new file mode 100644 index 0000000..612049c --- /dev/null +++ b/src/instructions/constant.cpp @@ -0,0 +1,48 @@ +/* This file is part of the sirit project. + * Copyright (c) 2019 sirit + * This software may be used and distributed according to the terms of the + * 3-Clause BSD License + */ + +#include + +#include "sirit/sirit.h" + +#include "stream.h" + +namespace Sirit { + +Id Module::ConstantTrue(Id result_type) { + declarations->Reserve(3); + return *declarations << OpId{spv::Op::OpConstantTrue, result_type} << EndOp{}; +} + +Id Module::ConstantFalse(Id result_type) { + declarations->Reserve(3); + return *declarations << OpId{spv::Op::OpConstantFalse, result_type} << EndOp{}; +} + +Id Module::Constant(Id result_type, const Literal& literal) { + declarations->Reserve(3 + 2); + return *declarations << OpId{spv::Op::OpConstant, result_type} << literal << EndOp{}; +} + +Id Module::ConstantComposite(Id result_type, std::span constituents) { + declarations->Reserve(3 + constituents.size()); + return *declarations << OpId{spv::Op::OpConstantComposite, result_type} << constituents + << EndOp{}; +} + +Id Module::ConstantSampler(Id result_type, spv::SamplerAddressingMode addressing_mode, + bool normalized, spv::SamplerFilterMode filter_mode) { + declarations->Reserve(6); + return *declarations << OpId{spv::Op::OpConstantSampler, result_type} << addressing_mode + << normalized << filter_mode << EndOp{}; +} + +Id Module::ConstantNull(Id result_type) { + declarations->Reserve(3); + return *declarations << OpId{spv::Op::OpConstantNull, result_type} << EndOp{}; +} + +} // namespace Sirit diff --git a/src/instructions/conversion.cpp b/src/instructions/conversion.cpp new file mode 100644 index 0000000..0b4e2c8 --- /dev/null +++ b/src/instructions/conversion.cpp @@ -0,0 +1,29 @@ +/* This file is part of the sirit project. + * Copyright (c) 2019 sirit + * This software may be used and distributed according to the terms of the + * 3-Clause BSD License + */ + +#include "sirit/sirit.h" + +#include "stream.h" + +namespace Sirit { + +#define DEFINE_UNARY(opcode) \ + Id Module::opcode(Id result_type, Id operand) { \ + code->Reserve(4); \ + return *code << OpId{spv::Op::opcode, result_type} << operand << EndOp{}; \ + } + +DEFINE_UNARY(OpConvertFToU) +DEFINE_UNARY(OpConvertFToS) +DEFINE_UNARY(OpConvertSToF) +DEFINE_UNARY(OpConvertUToF) +DEFINE_UNARY(OpUConvert) +DEFINE_UNARY(OpSConvert) +DEFINE_UNARY(OpFConvert) +DEFINE_UNARY(OpQuantizeToF16) +DEFINE_UNARY(OpBitcast) + +} // namespace Sirit diff --git a/src/instructions/debug.cpp b/src/instructions/debug.cpp new file mode 100644 index 0000000..1ca3462 --- /dev/null +++ b/src/instructions/debug.cpp @@ -0,0 +1,36 @@ +/* This file is part of the sirit project. + * Copyright (c) 2019 sirit + * This software may be used and distributed according to the terms of the + * 3-Clause BSD License + */ + +#include "sirit/sirit.h" + +#include "common_types.h" +#include "stream.h" + +namespace Sirit { + +Id Module::Name(Id target, std::string_view name) { + debug->Reserve(3 + WordsInString(name)); + *debug << spv::Op::OpName << target << name << EndOp{}; + return target; +} + +Id Module::MemberName(Id type, u32 member, std::string_view name) { + debug->Reserve(4 + WordsInString(name)); + *debug << spv::Op::OpMemberName << type << member << name << EndOp{}; + return type; +} + +Id Module::String(std::string_view string) { + debug->Reserve(3 + WordsInString(string)); + return *debug << OpId{spv::Op::OpString} << string << EndOp{}; +} + +Id Module::OpLine(Id file, Literal line, Literal column) { + debug->Reserve(4); + return *debug << spv::Op::OpLine << file << line << column << EndOp{}; +} + +} // namespace Sirit diff --git a/src/instructions/derivatives.cpp b/src/instructions/derivatives.cpp new file mode 100644 index 0000000..c944e2a --- /dev/null +++ b/src/instructions/derivatives.cpp @@ -0,0 +1,29 @@ +/* This file is part of the sirit project. + * Copyright (c) 2021 sirit + * This software may be used and distributed according to the terms of the + * 3-Clause BSD License + */ + +#include "sirit/sirit.h" + +#include "stream.h" + +namespace Sirit { + +#define DEFINE_UNARY(funcname, opcode) \ + Id Module::funcname(Id result_type, Id operand) { \ + code->Reserve(4); \ + return *code << OpId{opcode, result_type} << operand << EndOp{}; \ + } + +DEFINE_UNARY(OpDPdx, spv::Op::OpDPdx) +DEFINE_UNARY(OpDPdy, spv::Op::OpDPdy) +DEFINE_UNARY(OpFwidth, spv::Op::OpFwidth) +DEFINE_UNARY(OpDPdxFine, spv::Op::OpDPdxFine) +DEFINE_UNARY(OpDPdyFine, spv::Op::OpDPdyFine) +DEFINE_UNARY(OpFwidthFine, spv::Op::OpFwidthFine) +DEFINE_UNARY(OpDPdxCoarse, spv::Op::OpDPdxCoarse) +DEFINE_UNARY(OpDPdyCoarse, spv::Op::OpDPdyCoarse) +DEFINE_UNARY(OpFwidthCoarse, spv::Op::OpFwidthCoarse) + +} // namespace Sirit diff --git a/src/instructions/extension.cpp b/src/instructions/extension.cpp new file mode 100644 index 0000000..9f7aa43 --- /dev/null +++ b/src/instructions/extension.cpp @@ -0,0 +1,76 @@ +/* This file is part of the sirit project. + * Copyright (c) 2019 sirit + * This software may be used and distributed according to the terms of the + * 3-Clause BSD License + */ + +#include + +#include "sirit/sirit.h" + +#include "stream.h" + +namespace Sirit { + +Id Module::OpExtInst(Id result_type, Id set, u32 instruction, std::span operands) { + code->Reserve(5 + operands.size()); + return *code << OpId{spv::Op::OpExtInst, result_type} << set << instruction << operands + << EndOp{}; +} + +#define DEFINE_UNARY(funcname, opcode) \ + Id Module::funcname(Id result_type, Id operand) { \ + return OpExtInst(result_type, GetGLSLstd450(), opcode, operand); \ + } + +#define DEFINE_BINARY(funcname, opcode) \ + Id Module::funcname(Id result_type, Id operand_1, Id operand_2) { \ + return OpExtInst(result_type, GetGLSLstd450(), opcode, operand_1, operand_2); \ + } + +#define DEFINE_TRINARY(funcname, opcode) \ + Id Module::funcname(Id result_type, Id operand_1, Id operand_2, Id operand_3) { \ + return OpExtInst(result_type, GetGLSLstd450(), opcode, operand_1, operand_2, operand_3); \ + } + +DEFINE_UNARY(OpFAbs, GLSLstd450FAbs) +DEFINE_UNARY(OpSAbs, GLSLstd450SAbs) +DEFINE_UNARY(OpRound, GLSLstd450Round) +DEFINE_UNARY(OpRoundEven, GLSLstd450RoundEven) +DEFINE_UNARY(OpTrunc, GLSLstd450Trunc) +DEFINE_UNARY(OpFSign, GLSLstd450FSign) +DEFINE_UNARY(OpSSign, GLSLstd450SSign) +DEFINE_UNARY(OpFloor, GLSLstd450Floor) +DEFINE_UNARY(OpCeil, GLSLstd450Ceil) +DEFINE_UNARY(OpFract, GLSLstd450Fract) +DEFINE_UNARY(OpSin, GLSLstd450Sin) +DEFINE_UNARY(OpCos, GLSLstd450Cos) +DEFINE_UNARY(OpAsin, GLSLstd450Asin) +DEFINE_UNARY(OpAcos, GLSLstd450Acos) +DEFINE_BINARY(OpPow, GLSLstd450Pow) +DEFINE_UNARY(OpExp, GLSLstd450Exp) +DEFINE_UNARY(OpLog, GLSLstd450Log) +DEFINE_UNARY(OpExp2, GLSLstd450Exp2) +DEFINE_UNARY(OpLog2, GLSLstd450Log2) +DEFINE_UNARY(OpSqrt, GLSLstd450Sqrt) +DEFINE_UNARY(OpInverseSqrt, GLSLstd450InverseSqrt) +DEFINE_BINARY(OpFMin, GLSLstd450FMin) +DEFINE_BINARY(OpUMin, GLSLstd450UMin) +DEFINE_BINARY(OpSMin, GLSLstd450SMin) +DEFINE_BINARY(OpFMax, GLSLstd450FMax) +DEFINE_BINARY(OpUMax, GLSLstd450UMax) +DEFINE_BINARY(OpSMax, GLSLstd450SMax) +DEFINE_TRINARY(OpFClamp, GLSLstd450FClamp) +DEFINE_TRINARY(OpUClamp, GLSLstd450UClamp) +DEFINE_TRINARY(OpSClamp, GLSLstd450SClamp) +DEFINE_TRINARY(OpFma, GLSLstd450Fma) +DEFINE_UNARY(OpPackHalf2x16, GLSLstd450PackHalf2x16) +DEFINE_UNARY(OpUnpackHalf2x16, GLSLstd450UnpackHalf2x16) +DEFINE_UNARY(OpFindILsb, GLSLstd450FindILsb) +DEFINE_UNARY(OpFindSMsb, GLSLstd450FindSMsb) +DEFINE_UNARY(OpFindUMsb, GLSLstd450FindUMsb) +DEFINE_UNARY(OpInterpolateAtCentroid, GLSLstd450InterpolateAtCentroid) +DEFINE_BINARY(OpInterpolateAtSample, GLSLstd450InterpolateAtSample) +DEFINE_BINARY(OpInterpolateAtOffset, GLSLstd450InterpolateAtOffset) + +} // namespace Sirit diff --git a/src/instructions/flow.cpp b/src/instructions/flow.cpp new file mode 100644 index 0000000..5f6b693 --- /dev/null +++ b/src/instructions/flow.cpp @@ -0,0 +1,109 @@ +/* This file is part of the sirit project. + * Copyright (c) 2019 sirit + * This software may be used and distributed according to the terms of the + * 3-Clause BSD License + */ + +#include + +#include "sirit/sirit.h" + +#include "stream.h" + +namespace Sirit { + +Id Module::OpPhi(Id result_type, std::span operands) { + assert(operands.size() % 2 == 0); + code->Reserve(3 + operands.size()); + return *code << OpId{spv::Op::OpPhi, result_type} << operands << EndOp{}; +} + +Id Module::DeferredOpPhi(Id result_type, std::span blocks) { + deferred_phi_nodes.push_back(code->LocalAddress()); + code->Reserve(3 + blocks.size() * 2); + *code << OpId{spv::Op::OpPhi, result_type}; + for (const Id block : blocks) { + *code << u32{0} << block; + } + return *code << EndOp{}; +} + +Id Module::OpLoopMerge(Id merge_block, Id continue_target, spv::LoopControlMask loop_control, + std::span literals) { + code->Reserve(4 + literals.size()); + return *code << spv::Op::OpLoopMerge << merge_block << continue_target << loop_control + << literals << EndOp{}; +} + +Id Module::OpSelectionMerge(Id merge_block, spv::SelectionControlMask selection_control) { + code->Reserve(3); + return *code << spv::Op::OpSelectionMerge << merge_block << selection_control << EndOp{}; +} + +Id Module::OpLabel() { + return Id{++bound}; +} + +Id Module::OpBranch(Id target_label) { + code->Reserve(2); + return *code << spv::Op::OpBranch << target_label << EndOp{}; +} + +Id Module::OpBranchConditional(Id condition, Id true_label, Id false_label, u32 true_weight, + u32 false_weight) { + code->Reserve(6); + *code << spv::Op::OpBranchConditional << condition << true_label << false_label; + if (true_weight != 0 || false_weight != 0) { + *code << true_weight << false_weight; + } + return *code << EndOp{}; +} + +Id Module::OpSwitch(Id selector, Id default_label, std::span literals, + std::span labels) { + assert(literals.size() == labels.size()); + const size_t size = literals.size(); + code->Reserve(3 + size * 2); + + *code << spv::Op::OpSwitch << selector << default_label; + for (std::size_t i = 0; i < size; ++i) { + *code << literals[i] << labels[i]; + } + return *code << EndOp{}; +} + +void Module::OpReturn() { + code->Reserve(1); + *code << spv::Op::OpReturn << EndOp{}; +} + +void Module::OpUnreachable() { + code->Reserve(1); + *code << spv::Op::OpUnreachable << EndOp{}; +} + +Id Module::OpReturnValue(Id value) { + code->Reserve(2); + return *code << spv::Op::OpReturnValue << value << EndOp{}; +} + +void Module::OpKill() { + code->Reserve(1); + *code << spv::Op::OpKill << EndOp{}; +} + +void Module::OpDemoteToHelperInvocation() { + code->Reserve(1); + *code << spv::Op::OpDemoteToHelperInvocation << EndOp{}; +} + +void Module::OpDemoteToHelperInvocationEXT() { + OpDemoteToHelperInvocation(); +} + +void Module::OpTerminateInvocation() { + code->Reserve(1); + *code << spv::Op::OpTerminateInvocation << EndOp{}; +} + +} // namespace Sirit diff --git a/src/instructions/function.cpp b/src/instructions/function.cpp new file mode 100644 index 0000000..f84cd7f --- /dev/null +++ b/src/instructions/function.cpp @@ -0,0 +1,34 @@ +/* This file is part of the sirit project. + * Copyright (c) 2019 sirit + * This software may be used and distributed according to the terms of the + * 3-Clause BSD License + */ + +#include "sirit/sirit.h" + +#include "stream.h" + +namespace Sirit { + +Id Module::OpFunction(Id result_type, spv::FunctionControlMask function_control, Id function_type) { + code->Reserve(5); + return *code << OpId{spv::Op::OpFunction, result_type} << function_control << function_type + << EndOp{}; +} + +void Module::OpFunctionEnd() { + code->Reserve(1); + *code << spv::Op::OpFunctionEnd << EndOp{}; +} + +Id Module::OpFunctionCall(Id result_type, Id function, std::span arguments) { + code->Reserve(4 + arguments.size()); + return *code << OpId{spv::Op::OpFunctionCall, result_type} << function << arguments << EndOp{}; +} + +Id Module::OpFunctionParameter(Id result_type) { + code->Reserve(3); + return *code << OpId{spv::Op::OpFunctionParameter, result_type} << EndOp{}; +} + +} // namespace Sirit diff --git a/src/instructions/group.cpp b/src/instructions/group.cpp new file mode 100644 index 0000000..b853284 --- /dev/null +++ b/src/instructions/group.cpp @@ -0,0 +1,88 @@ +/* This file is part of the sirit project. + * Copyright (c) 2019 sirit + * This software may be used and distributed according to the terms of the + * 3-Clause BSD License + */ + +#include "sirit/sirit.h" + +#include "stream.h" + +namespace Sirit { + +Id Module::OpSubgroupBallotKHR(Id result_type, Id predicate) { + code->Reserve(4); + return *code << OpId{spv::Op::OpSubgroupBallotKHR, result_type} << predicate << EndOp{}; +} + +Id Module::OpSubgroupReadInvocationKHR(Id result_type, Id value, Id index) { + code->Reserve(5); + return *code << OpId{spv::Op::OpSubgroupReadInvocationKHR, result_type} << value << index + << EndOp{}; +} + +Id Module::OpSubgroupAllKHR(Id result_type, Id predicate) { + code->Reserve(4); + return *code << OpId{spv::Op::OpSubgroupAllKHR, result_type} << predicate << EndOp{}; +} + +Id Module::OpSubgroupAnyKHR(Id result_type, Id predicate) { + code->Reserve(4); + return *code << OpId{spv::Op::OpSubgroupAnyKHR, result_type} << predicate << EndOp{}; +} + +Id Module::OpSubgroupAllEqualKHR(Id result_type, Id predicate) { + code->Reserve(4); + return *code << OpId{spv::Op::OpSubgroupAllEqualKHR, result_type} << predicate << EndOp{}; +} + +Id Module::OpGroupNonUniformElect(Id result_type, Id scope) { + code->Reserve(4); + return *code << OpId{spv::Op::OpGroupNonUniformElect, result_type} << scope << EndOp{}; +} + +Id Module::OpGroupNonUniformBroadcastFirst(Id result_type, Id scope, Id value) { + code->Reserve(5); + return *code << OpId{spv::Op::OpGroupNonUniformBroadcastFirst, result_type} << scope << value + << EndOp{}; +} + +Id Module::OpGroupNonUniformBroadcast(Id result_type, Id scope, Id value, Id id) { + code->Reserve(6); + return *code << OpId{spv::Op::OpGroupNonUniformBroadcast, result_type} << scope << value + << id << EndOp{}; +} + +Id Module::OpGroupNonUniformShuffle(Id result_type, Id scope, Id value, Id id) { + code->Reserve(6); + return *code << OpId{spv::Op::OpGroupNonUniformShuffle, result_type} << scope << value << id + << EndOp{}; +} + +Id Module::OpGroupNonUniformShuffleXor(Id result_type, Id scope, Id value, Id mask) { + code->Reserve(6); + return *code << OpId{spv::Op::OpGroupNonUniformShuffleXor, result_type} << scope << value + << mask << EndOp{}; +} + +Id Module::OpGroupNonUniformAll(Id result_type, Id scope, Id predicate) { + code->Reserve(5); + return *code << OpId{spv::Op::OpGroupNonUniformAll, result_type} << scope << predicate << EndOp{}; +} + +Id Module::OpGroupNonUniformAny(Id result_type, Id scope, Id predicate) { + code->Reserve(5); + return *code << OpId{spv::Op::OpGroupNonUniformAny, result_type} << scope << predicate << EndOp{}; +} + +Id Module::OpGroupNonUniformAllEqual(Id result_type, Id scope, Id value) { + code->Reserve(5); + return *code << OpId{spv::Op::OpGroupNonUniformAllEqual, result_type} << scope << value << EndOp{}; +} + +Id Module::OpGroupNonUniformBallot(Id result_type, Id scope, Id predicate) { + code->Reserve(5); + return *code << OpId{spv::Op::OpGroupNonUniformBallot, result_type} << scope << predicate << EndOp{}; +} + +} // namespace Sirit diff --git a/src/instructions/image.cpp b/src/instructions/image.cpp new file mode 100644 index 0000000..e68447f --- /dev/null +++ b/src/instructions/image.cpp @@ -0,0 +1,169 @@ +/* This file is part of the sirit project. + * Copyright (c) 2019 sirit + * This software may be used and distributed according to the terms of the + * 3-Clause BSD License + */ + +#include + +#include "sirit/sirit.h" + +#include "stream.h" + +namespace Sirit { + +#define DEFINE_IMAGE_OP(opcode) \ + Id Module::opcode(Id result_type, Id sampled_image, Id coordinate, \ + std::optional image_operands, \ + std::span operands) { \ + code->Reserve(6 + operands.size()); \ + return *code << OpId{spv::Op::opcode, result_type} << sampled_image << coordinate \ + << image_operands << operands << EndOp{}; \ + } + +#define DEFINE_IMAGE_EXP_OP(opcode) \ + Id Module::opcode(Id result_type, Id sampled_image, Id coordinate, \ + spv::ImageOperandsMask image_operands, std::span operands) { \ + code->Reserve(6 + operands.size()); \ + return *code << OpId{spv::Op::opcode, result_type} << sampled_image << coordinate \ + << image_operands << operands << EndOp{}; \ + } + +#define DEFINE_IMAGE_EXTRA_OP(opcode) \ + Id Module::opcode(Id result_type, Id sampled_image, Id coordinate, Id extra, \ + std::optional image_operands, \ + std::span operands) { \ + code->Reserve(7 + operands.size()); \ + return *code << OpId{spv::Op::opcode, result_type} << sampled_image << coordinate << extra \ + << image_operands << operands << EndOp{}; \ + } + +#define DEFINE_IMAGE_EXTRA_EXP_OP(opcode) \ + Id Module::opcode(Id result_type, Id sampled_image, Id coordinate, Id extra, \ + spv::ImageOperandsMask image_operands, std::span operands) { \ + code->Reserve(8 + operands.size()); \ + return *code << OpId{spv::Op::opcode, result_type} << sampled_image << coordinate << extra \ + << image_operands << operands << EndOp{}; \ + } + +#define DEFINE_IMAGE_QUERY_OP(opcode) \ + Id Module::opcode(Id result_type, Id image) { \ + code->Reserve(5); \ + return *code << OpId{spv::Op::opcode, result_type} << image << EndOp{}; \ + } + +#define DEFINE_IMAGE_QUERY_BIN_OP(opcode) \ + Id Module::opcode(Id result_type, Id image, Id extra) { \ + code->Reserve(5); \ + return *code << OpId{spv::Op::opcode, result_type} << image << extra << EndOp{}; \ + } + +DEFINE_IMAGE_OP(OpImageSampleImplicitLod) +DEFINE_IMAGE_EXP_OP(OpImageSampleExplicitLod) +DEFINE_IMAGE_EXTRA_OP(OpImageSampleDrefImplicitLod) +DEFINE_IMAGE_EXTRA_EXP_OP(OpImageSampleDrefExplicitLod) +DEFINE_IMAGE_OP(OpImageSampleProjImplicitLod) +DEFINE_IMAGE_EXP_OP(OpImageSampleProjExplicitLod) +DEFINE_IMAGE_EXTRA_OP(OpImageSampleProjDrefImplicitLod) +DEFINE_IMAGE_EXTRA_EXP_OP(OpImageSampleProjDrefExplicitLod) +DEFINE_IMAGE_OP(OpImageFetch) +DEFINE_IMAGE_EXTRA_OP(OpImageGather) +DEFINE_IMAGE_EXTRA_OP(OpImageDrefGather) +DEFINE_IMAGE_OP(OpImageRead) +DEFINE_IMAGE_QUERY_BIN_OP(OpImageQuerySizeLod) +DEFINE_IMAGE_QUERY_OP(OpImageQuerySize) +DEFINE_IMAGE_QUERY_BIN_OP(OpImageQueryLod) +DEFINE_IMAGE_QUERY_OP(OpImageQueryLevels) +DEFINE_IMAGE_QUERY_OP(OpImageQuerySamples) + +Id Module::OpSampledImage(Id result_type, Id image, Id sampler) { + code->Reserve(5); + return *code << OpId{spv::Op::OpSampledImage, result_type} << image << sampler << EndOp{}; +} + +Id Module::OpImageWrite(Id image, Id coordinate, Id texel, + std::optional image_operands, + std::span operands) { + assert(image_operands.has_value() != operands.empty()); + code->Reserve(5 + operands.size()); + return *code << spv::Op::OpImageWrite << image << coordinate << texel << image_operands + << operands << EndOp{}; +} + +Id Module::OpImage(Id result_type, Id sampled_image) { + code->Reserve(4); + return *code << OpId{spv::Op::OpImage, result_type} << sampled_image << EndOp{}; +} + +Id Module::OpImageSparseSampleImplicitLod(Id result_type, Id sampled_image, Id coordinate, + std::optional image_operands, + std::span operands) { + code->Reserve(5 + (image_operands.has_value() ? 1 : 0) + operands.size()); + return *code << OpId{spv::Op::OpImageSparseSampleImplicitLod, result_type} << sampled_image + << coordinate << image_operands << operands << EndOp{}; +} + +Id Module::OpImageSparseSampleExplicitLod(Id result_type, Id sampled_image, Id coordinate, + spv::ImageOperandsMask image_operands, + std::span operands) { + code->Reserve(6 + operands.size()); + return *code << OpId{spv::Op::OpImageSparseSampleExplicitLod, result_type} << sampled_image + << coordinate << image_operands << operands << EndOp{}; +} + +Id Module::OpImageSparseSampleDrefImplicitLod(Id result_type, Id sampled_image, Id coordinate, + Id dref, + std::optional image_operands, + std::span operands) { + code->Reserve(6 + (image_operands.has_value() ? 1 : 0) + operands.size()); + return *code << OpId{spv::Op::OpImageSparseSampleDrefImplicitLod, result_type} << sampled_image + << coordinate << dref << image_operands << operands << EndOp{}; +} + +Id Module::OpImageSparseSampleDrefExplicitLod(Id result_type, Id sampled_image, Id coordinate, + Id dref, spv::ImageOperandsMask image_operands, + std::span operands) { + code->Reserve(7 + operands.size()); + return *code << OpId{spv::Op::OpImageSparseSampleDrefExplicitLod, result_type} << sampled_image + << coordinate << dref << image_operands << operands << EndOp{}; +} + +Id Module::OpImageSparseFetch(Id result_type, Id image, Id coordinate, + std::optional image_operands, + std::span operands) { + code->Reserve(5 + (image_operands.has_value() ? 1 : 0) + operands.size()); + return *code << OpId{spv::Op::OpImageSparseFetch, result_type} << image << coordinate + << image_operands << operands << EndOp{}; +} + +Id Module::OpImageSparseGather(Id result_type, Id sampled_image, Id coordinate, Id component, + std::optional image_operands, + std::span operands) { + code->Reserve(6 + operands.size()); + return *code << OpId{spv::Op::OpImageSparseGather, result_type} << sampled_image << coordinate + << component << image_operands << operands << EndOp{}; +} + +Id Module::OpImageSparseDrefGather(Id result_type, Id sampled_image, Id coordinate, Id dref, + std::optional image_operands, + std::span operands) { + code->Reserve(6 + operands.size()); + return *code << OpId{spv::Op::OpImageSparseDrefGather, result_type} << sampled_image + << coordinate << dref << image_operands << operands << EndOp{}; +} + +Id Module::OpImageSparseTexelsResident(Id result_type, Id resident_code) { + code->Reserve(4); + return *code << OpId{spv::Op::OpImageSparseTexelsResident, result_type} << resident_code + << EndOp{}; +} + +Id Module::OpImageSparseRead(Id result_type, Id image, Id coordinate, + std::optional image_operands, + std::span operands) { + code->Reserve(5 + (image_operands.has_value() ? 1 : 0) + operands.size()); + return *code << OpId{spv::Op::OpImageSparseTexelsResident, result_type} << image << coordinate + << image_operands << operands << EndOp{}; +} + +} // namespace Sirit diff --git a/src/instructions/logical.cpp b/src/instructions/logical.cpp new file mode 100644 index 0000000..4d7292b --- /dev/null +++ b/src/instructions/logical.cpp @@ -0,0 +1,65 @@ +/* This file is part of the sirit project. + * Copyright (c) 2019 sirit + * This software may be used and distributed according to the terms of the + * 3-Clause BSD License + */ + +#include "sirit/sirit.h" + +#include "stream.h" + +namespace Sirit { + +#define DEFINE_UNARY(opcode) \ + Id Module::opcode(Id result_type, Id operand) { \ + code->Reserve(4); \ + return *code << OpId{spv::Op::opcode, result_type} << operand << EndOp{}; \ + } + +#define DEFINE_BINARY(opcode) \ + Id Module::opcode(Id result_type, Id operand_1, Id operand_2) { \ + code->Reserve(5); \ + return *code << OpId{spv::Op::opcode, result_type} << operand_1 << operand_2 << EndOp{}; \ + } + +#define DEFINE_TRINARY(opcode) \ + Id Module::opcode(Id result_type, Id operand_1, Id operand_2, Id operand_3) { \ + code->Reserve(6); \ + return *code << OpId{spv::Op::opcode, result_type} << operand_1 << operand_2 << operand_3 \ + << EndOp{}; \ + } + +DEFINE_UNARY(OpAny) +DEFINE_UNARY(OpAll) +DEFINE_UNARY(OpIsNan) +DEFINE_UNARY(OpIsInf) +DEFINE_BINARY(OpLogicalEqual) +DEFINE_BINARY(OpLogicalNotEqual) +DEFINE_BINARY(OpLogicalOr) +DEFINE_BINARY(OpLogicalAnd) +DEFINE_UNARY(OpLogicalNot) +DEFINE_TRINARY(OpSelect) +DEFINE_BINARY(OpIEqual) +DEFINE_BINARY(OpINotEqual) +DEFINE_BINARY(OpUGreaterThan) +DEFINE_BINARY(OpSGreaterThan) +DEFINE_BINARY(OpUGreaterThanEqual) +DEFINE_BINARY(OpSGreaterThanEqual) +DEFINE_BINARY(OpULessThan) +DEFINE_BINARY(OpSLessThan) +DEFINE_BINARY(OpULessThanEqual) +DEFINE_BINARY(OpSLessThanEqual) +DEFINE_BINARY(OpFOrdEqual) +DEFINE_BINARY(OpFUnordEqual) +DEFINE_BINARY(OpFOrdNotEqual) +DEFINE_BINARY(OpFUnordNotEqual) +DEFINE_BINARY(OpFOrdLessThan) +DEFINE_BINARY(OpFUnordLessThan) +DEFINE_BINARY(OpFOrdGreaterThan) +DEFINE_BINARY(OpFUnordGreaterThan) +DEFINE_BINARY(OpFOrdLessThanEqual) +DEFINE_BINARY(OpFUnordLessThanEqual) +DEFINE_BINARY(OpFOrdGreaterThanEqual) +DEFINE_BINARY(OpFUnordGreaterThanEqual) + +} // namespace Sirit diff --git a/src/instructions/memory.cpp b/src/instructions/memory.cpp new file mode 100644 index 0000000..a542e9f --- /dev/null +++ b/src/instructions/memory.cpp @@ -0,0 +1,68 @@ +/* This file is part of the sirit project. + * Copyright (c) 2019 sirit + * This software may be used and distributed according to the terms of the + * 3-Clause BSD License + */ + +#include + +#include "sirit/sirit.h" + +#include "stream.h" + +namespace Sirit { + +Id Module::OpImageTexelPointer(Id result_type, Id image, Id coordinate, Id sample) { + code->Reserve(6); + return *code << OpId{spv::Op::OpImageTexelPointer, result_type} << image << coordinate << sample + << EndOp{}; +} + +Id Module::OpLoad(Id result_type, Id pointer, std::optional memory_access) { + code->Reserve(5); + return *code << OpId{spv::Op::OpLoad, result_type} << pointer << memory_access << EndOp{}; +} + +Id Module::OpStore(Id pointer, Id object, std::optional memory_access) { + code->Reserve(4); + return *code << spv::Op::OpStore << pointer << object << memory_access << EndOp{}; +} + +Id Module::OpAccessChain(Id result_type, Id base, std::span indexes) { + assert(!indexes.empty()); + code->Reserve(4 + indexes.size()); + return *code << OpId{spv::Op::OpAccessChain, result_type} << base << indexes << EndOp{}; +} + +Id Module::OpVectorExtractDynamic(Id result_type, Id vector, Id index) { + code->Reserve(5); + return *code << OpId{spv::Op::OpVectorExtractDynamic, result_type} << vector << index + << EndOp{}; +} + +Id Module::OpVectorInsertDynamic(Id result_type, Id vector, Id component, Id index) { + code->Reserve(6); + return *code << OpId{spv::Op::OpVectorInsertDynamic, result_type} << vector << component + << index << EndOp{}; +} + +Id Module::OpCompositeInsert(Id result_type, Id object, Id composite, + std::span indexes) { + code->Reserve(5 + indexes.size()); + return *code << OpId{spv::Op::OpCompositeInsert, result_type} << object << composite << indexes + << EndOp{}; +} + +Id Module::OpCompositeExtract(Id result_type, Id composite, std::span indexes) { + code->Reserve(4 + indexes.size()); + return *code << OpId{spv::Op::OpCompositeExtract, result_type} << composite << indexes + << EndOp{}; +} + +Id Module::OpCompositeConstruct(Id result_type, std::span ids) { + assert(ids.size() >= 1); + code->Reserve(3 + ids.size()); + return *code << OpId{spv::Op::OpCompositeConstruct, result_type} << ids << EndOp{}; +} + +} // namespace Sirit diff --git a/src/instructions/misc.cpp b/src/instructions/misc.cpp new file mode 100644 index 0000000..6785346 --- /dev/null +++ b/src/instructions/misc.cpp @@ -0,0 +1,38 @@ +/* This file is part of the sirit project. + * Copyright (c) 2019 sirit + * This software may be used and distributed according to the terms of the + * 3-Clause BSD License + */ + +#include "sirit/sirit.h" + +#include "stream.h" + +namespace Sirit { + +Id Module::OpUndef(Id result_type) { + code->Reserve(3); + return *code << OpId{spv::Op::OpUndef, result_type} << EndOp{}; +} + +void Module::OpEmitVertex() { + code->Reserve(1); + *code << spv::Op::OpEmitVertex << EndOp{}; +} + +void Module::OpEndPrimitive() { + code->Reserve(1); + *code << spv::Op::OpEndPrimitive << EndOp{}; +} + +void Module::OpEmitStreamVertex(Id stream) { + code->Reserve(2); + *code << spv::Op::OpEmitStreamVertex << stream << EndOp{}; +} + +void Module::OpEndStreamPrimitive(Id stream) { + code->Reserve(2); + *code << spv::Op::OpEndStreamPrimitive << stream << EndOp{}; +} + +} // namespace Sirit diff --git a/src/instructions/type.cpp b/src/instructions/type.cpp new file mode 100644 index 0000000..3509d1f --- /dev/null +++ b/src/instructions/type.cpp @@ -0,0 +1,130 @@ +/* This file is part of the sirit project. + * Copyright (c) 2019 sirit + * This software may be used and distributed according to the terms of the + * 3-Clause BSD License + */ + +#include +#include + +#include "sirit/sirit.h" + +#include "stream.h" + +namespace Sirit { + +Id Module::TypeVoid() { + declarations->Reserve(2); + return *declarations << OpId{spv::Op::OpTypeVoid} << EndOp{}; +} + +Id Module::TypeBool() { + declarations->Reserve(2); + return *declarations << OpId{spv::Op::OpTypeBool} << EndOp{}; +} + +Id Module::TypeInt(int width, bool is_signed) { + declarations->Reserve(4); + return *declarations << OpId{spv::Op::OpTypeInt} << width << is_signed << EndOp{}; +} + +Id Module::TypeSInt(int width) { + return TypeInt(width, true); +} + +Id Module::TypeUInt(int width) { + return TypeInt(width, false); +} + +Id Module::TypeFloat(int width) { + declarations->Reserve(3); + return *declarations << OpId{spv::Op::OpTypeFloat} << width << EndOp{}; +} + +Id Module::TypeVector(Id component_type, int component_count) { + assert(component_count >= 2); + declarations->Reserve(4); + return *declarations << OpId{spv::Op::OpTypeVector} << component_type << component_count + << EndOp{}; +} + +Id Module::TypeMatrix(Id column_type, int column_count) { + assert(column_count >= 2); + declarations->Reserve(4); + return *declarations << OpId{spv::Op::OpTypeMatrix} << column_type << column_count << EndOp{}; +} + +Id Module::TypeImage(Id sampled_type, spv::Dim dim, int depth, bool arrayed, bool ms, int sampled, + spv::ImageFormat image_format, + std::optional access_qualifier) { + declarations->Reserve(10); + return *declarations << OpId{spv::Op::OpTypeImage} << sampled_type << dim << depth << arrayed + << ms << sampled << image_format << access_qualifier << EndOp{}; +} + +Id Module::TypeSampler() { + declarations->Reserve(2); + return *declarations << OpId{spv::Op::OpTypeSampler} << EndOp{}; +} + +Id Module::TypeSampledImage(Id image_type) { + declarations->Reserve(3); + return *declarations << OpId{spv::Op::OpTypeSampledImage} << image_type << EndOp{}; +} + +Id Module::TypeArray(Id element_type, Id length) { + declarations->Reserve(4); + return *declarations << OpId{spv::Op::OpTypeArray} << element_type << length << EndOp{}; +} + +Id Module::TypeRuntimeArray(Id element_type) { + declarations->Reserve(3); + return *declarations << OpId{spv::Op::OpTypeRuntimeArray} << element_type << EndOp{}; +} + +Id Module::TypeStruct(std::span members) { + declarations->Reserve(2 + members.size()); + return *declarations << OpId{spv::Op::OpTypeStruct} << members << EndOp{}; +} + +Id Module::TypeOpaque(std::string_view name) { + declarations->Reserve(3 + WordsInString(name)); + return *declarations << OpId{spv::Op::OpTypeOpaque} << name << EndOp{}; +} + +Id Module::TypePointer(spv::StorageClass storage_class, Id type) { + declarations->Reserve(4); + return *declarations << OpId{spv::Op::OpTypePointer} << storage_class << type << EndOp{}; +} + +Id Module::TypeFunction(Id return_type, std::span arguments) { + declarations->Reserve(3 + arguments.size()); + return *declarations << OpId{spv::Op::OpTypeFunction} << return_type << arguments << EndOp{}; +} + +Id Module::TypeEvent() { + declarations->Reserve(2); + return *declarations << OpId{spv::Op::OpTypeEvent} << EndOp{}; +} + +Id Module::TypeDeviceEvent() { + declarations->Reserve(2); + return *declarations << OpId{spv::Op::OpTypeDeviceEvent} << EndOp{}; +} + +Id Module::TypeReserveId() { + declarations->Reserve(2); + return *declarations << OpId{spv::Op::OpTypeReserveId} << EndOp{}; +} + +Id Module::TypeQueue() { + declarations->Reserve(2); + return *declarations << OpId{spv::Op::OpTypeQueue} << EndOp{}; +} + +Id Module::TypePipe(spv::AccessQualifier access_qualifier) { + declarations->Reserve(2); + return *declarations << OpId{spv::Op::OpTypePipe} << access_qualifier << EndOp{}; +} + +} // namespace Sirit diff --git a/src/sirit.cpp b/src/sirit.cpp new file mode 100644 index 0000000..7075f23 --- /dev/null +++ b/src/sirit.cpp @@ -0,0 +1,142 @@ +/* This file is part of the sirit project. + * Copyright (c) 2019 sirit + * This software may be used and distributed according to the terms of the + * 3-Clause BSD License + */ + +#include + +#include "sirit/sirit.h" + +#include "common_types.h" +#include "stream.h" + +namespace Sirit { + +constexpr u32 MakeWord0(spv::Op op, size_t word_count) { + return static_cast(op) | static_cast(word_count) << 16; +} + +Module::Module(u32 version_) + : version{version_}, ext_inst_imports{std::make_unique(&bound)}, + entry_points{std::make_unique(&bound)}, + execution_modes{std::make_unique(&bound)}, debug{std::make_unique(&bound)}, + annotations{std::make_unique(&bound)}, declarations{std::make_unique( + &bound)}, + global_variables{std::make_unique(&bound)}, code{std::make_unique(&bound)} {} + +Module::~Module() = default; + +std::vector Module::Assemble() const { + std::vector words = {spv::MagicNumber, version, GENERATOR_MAGIC_NUMBER, bound + 1, 0}; + const auto insert = [&words](std::span input) { + words.insert(words.end(), input.begin(), input.end()); + }; + + words.reserve(words.size() + capabilities.size() * 2); + for (const spv::Capability capability : capabilities) { + insert(std::array{ + MakeWord0(spv::Op::OpCapability, 2), + static_cast(capability), + }); + } + + for (const std::string_view extension_name : extensions) { + size_t string_words = WordsInString(extension_name); + words.push_back(MakeWord0(spv::Op::OpExtension, string_words + 1)); + size_t insert_index = words.size(); + words.resize(words.size() + string_words); + InsertStringView(words, insert_index, extension_name); + } + + insert(ext_inst_imports->Words()); + + insert(std::array{ + MakeWord0(spv::Op::OpMemoryModel, 3), + static_cast(addressing_model), + static_cast(memory_model), + }); + + insert(entry_points->Words()); + insert(execution_modes->Words()); + insert(debug->Words()); + insert(annotations->Words()); + insert(declarations->Words()); + insert(global_variables->Words()); + insert(code->Words()); + + return words; +} + +void Module::PatchDeferredPhi(const std::function& func) { + for (const u32 phi_index : deferred_phi_nodes) { + const u32 first_word = code->Value(phi_index); + [[maybe_unused]] const spv::Op op = static_cast(first_word & 0xffff); + assert(op == spv::Op::OpPhi); + const u32 num_words = first_word >> 16; + const u32 num_args = (num_words - 3) / 2; + u32 cursor = phi_index + 3; + for (u32 arg = 0; arg < num_args; ++arg, cursor += 2) { + code->SetValue(cursor, func(arg).value); + } + } +} + +void Module::AddExtension(std::string extension_name) { + extensions.insert(std::move(extension_name)); +} + +void Module::AddCapability(spv::Capability capability) { + capabilities.insert(capability); +} + +void Module::SetMemoryModel(spv::AddressingModel addressing_model_, + spv::MemoryModel memory_model_) { + addressing_model = addressing_model_; + memory_model = memory_model_; +} + +void Module::AddEntryPoint(spv::ExecutionModel execution_model, Id entry_point, + std::string_view name, std::span interfaces) { + entry_points->Reserve(4 + WordsInString(name) + interfaces.size()); + *entry_points << spv::Op::OpEntryPoint << execution_model << entry_point << name << interfaces + << EndOp{}; +} + +void Module::AddExecutionMode(Id entry_point, spv::ExecutionMode mode, + std::span literals) { + execution_modes->Reserve(3 + literals.size()); + *execution_modes << spv::Op::OpExecutionMode << entry_point << mode << literals << EndOp{}; +} + +Id Module::AddLabel(Id label) { + assert(label.value != 0); + code->Reserve(2); + *code << MakeWord0(spv::Op::OpLabel, 2) << label.value; + return label; +} + +Id Module::AddLocalVariable(Id result_type, spv::StorageClass storage_class, + std::optional initializer) { + code->Reserve(5); + return *code << OpId{spv::Op::OpVariable, result_type} << storage_class << initializer + << EndOp{}; +} + +Id Module::AddGlobalVariable(Id result_type, spv::StorageClass storage_class, + std::optional initializer) { + global_variables->Reserve(5); + return *global_variables << OpId{spv::Op::OpVariable, result_type} << storage_class + << initializer << EndOp{}; +} + +Id Module::GetGLSLstd450() { + if (!glsl_std_450) { + ext_inst_imports->Reserve(3 + 4); + glsl_std_450 = *ext_inst_imports << OpId{spv::Op::OpExtInstImport} << "GLSL.std.450" + << EndOp{}; + } + return *glsl_std_450; +} + +} // namespace Sirit diff --git a/src/stream.cpp b/src/stream.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/stream.h b/src/stream.h new file mode 100644 index 0000000..7029b6c --- /dev/null +++ b/src/stream.h @@ -0,0 +1,266 @@ +/* This file is part of the sirit project. + * Copyright (c) 2019 sirit + * This software may be used and distributed according to the terms of the + * 3-Clause BSD License + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef __cpp_lib_bit_cast +#include +#endif + +#include + +#include "common_types.h" + +namespace Sirit { + +class Declarations; + +struct OpId { + OpId(spv::Op opcode_) : opcode{opcode_} {} + OpId(spv::Op opcode_, Id result_type_) : opcode{opcode_}, result_type{result_type_} { + assert(result_type.value != 0); + } + + spv::Op opcode{}; + Id result_type{}; +}; + +struct EndOp {}; + +inline size_t WordsInString(std::string_view string) { + return string.size() / sizeof(u32) + 1; +} + +inline void InsertStringView(std::vector& words, size_t& insert_index, + std::string_view string) { + const size_t size = string.size(); + const auto read = [string, size](size_t offset) { + return offset < size ? static_cast(string[offset]) : 0u; + }; + + for (size_t i = 0; i < size; i += sizeof(u32)) { + words[insert_index++] = read(i) | read(i + 1) << 8 | read(i + 2) << 16 | read(i + 3) << 24; + } + if (size % sizeof(u32) == 0) { + words[insert_index++] = 0; + } +} + +class Stream { + friend Declarations; + +public: + explicit Stream(u32* bound_) : bound{bound_} {} + + void Reserve(size_t num_words) { + if (insert_index + num_words <= words.size()) { + return; + } + words.resize(insert_index + num_words); + } + + std::span Words() const noexcept { + return std::span(words.data(), insert_index); + } + + u32 LocalAddress() const noexcept { + return static_cast(words.size()); + } + + u32 Value(u32 index) const noexcept { + return words[index]; + } + + void SetValue(u32 index, u32 value) noexcept { + words[index] = value; + } + + Stream& operator<<(spv::Op op) { + op_index = insert_index; + words[insert_index++] = static_cast(op); + return *this; + } + + Stream& operator<<(OpId op) { + op_index = insert_index; + words[insert_index++] = static_cast(op.opcode); + if (op.result_type.value != 0) { + words[insert_index++] = op.result_type.value; + } + words[insert_index++] = ++*bound; + return *this; + } + + Id operator<<(EndOp) { + const size_t num_words = insert_index - op_index; + words[op_index] |= static_cast(num_words) << 16; + return Id{*bound}; + } + + Stream& operator<<(u32 value) { + words[insert_index++] = value; + return *this; + } + + Stream& operator<<(s32 value) { + return *this << static_cast(value); + } + + Stream& operator<<(u64 value) { + return *this << static_cast(value) << static_cast(value >> 32); + } + + Stream& operator<<(s64 value) { + return *this << static_cast(value); + } + + Stream& operator<<(float value) { +#ifdef __cpp_lib_bit_cast + return *this << std::bit_cast(value); +#else + static_assert(sizeof(float) == sizeof(u32)); + u32 int_value; + std::memcpy(&int_value, &value, sizeof(int_value)); + return *this << int_value; +#endif + } + + Stream& operator<<(double value) { +#ifdef __cpp_lib_bit_cast + return *this << std::bit_cast(value); +#else + static_assert(sizeof(double) == sizeof(u64)); + u64 int_value; + std::memcpy(&int_value, &value, sizeof(int_value)); + return *this << int_value; +#endif + } + + Stream& operator<<(bool value) { + return *this << static_cast(value ? 1 : 0); + } + + Stream& operator<<(Id value) { + assert(value.value != 0); + return *this << value.value; + } + + Stream& operator<<(const Literal& literal) { + std::visit([this](auto value) { *this << value; }, literal); + return *this; + } + + Stream& operator<<(std::string_view string) { + InsertStringView(words, insert_index, string); + return *this; + } + + Stream& operator<<(const char* string) { + return *this << std::string_view{string}; + } + + template + requires std::is_enum_v Stream& operator<<(T value) { + static_assert(sizeof(T) == sizeof(u32)); + return *this << static_cast(value); + } + + template + Stream& operator<<(std::optional value) { + if (value) { + *this << *value; + } + return *this; + } + + template + Stream& operator<<(std::span values) { + for (const auto& value : values) { + *this << value; + } + return *this; + } + +private: + u32* bound = nullptr; + std::vector words; + size_t insert_index = 0; + size_t op_index = 0; +}; + +class Declarations { +public: + explicit Declarations(u32* bound) : stream{bound} {} + + void Reserve(size_t num_words) { + return stream.Reserve(num_words); + } + + std::span Words() const noexcept { + return stream.Words(); + } + + template + Declarations& operator<<(const T& value) { + stream << value; + return *this; + } + + // Declarations without an id don't exist + Declarations& operator<<(spv::Op) = delete; + + Declarations& operator<<(OpId op) { + id_index = op.result_type.value != 0 ? 2 : 1; + stream << op; + return *this; + } + + Id operator<<(EndOp) { + const auto begin = stream.words.data(); + std::vector declarations(begin + stream.op_index, begin + stream.insert_index); + + // Normalize result id for lookups + const u32 id = std::exchange(declarations[id_index], 0); + + const auto [entry, inserted] = existing_declarations.emplace(declarations, id); + if (inserted) { + return stream << EndOp{}; + } + // If the declaration already exists, undo the operation + stream.insert_index = stream.op_index; + --*stream.bound; + + return Id{entry->second}; + } + +private: + struct HashVector { + size_t operator()(const std::vector& vector) const noexcept { + size_t hash = std::hash{}(vector.size()); + for (const u32 value : vector) { + hash ^= std::hash{}(value); + } + return hash; + } + }; + + Stream stream; + std::unordered_map, u32, HashVector> existing_declarations; + size_t id_index = 0; +}; + +} // namespace Sirit diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..a3f9534 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,6 @@ +add_executable(sirit_tests + main.cpp) +target_link_libraries(sirit_tests PRIVATE sirit) +target_include_directories(sirit_tests PRIVATE . ../include) + +add_test(sirit_tests sirit_tests) diff --git a/tests/main.cpp b/tests/main.cpp new file mode 100644 index 0000000..d119c25 --- /dev/null +++ b/tests/main.cpp @@ -0,0 +1,139 @@ +/* This file is part of the sirit project. + * Copyright (c) 2019 sirit + * This software may be used and distributed according to the terms of the + * 3-Clause BSD License + */ + +#include +#include +#include + +#include + +class MyModule : public Sirit::Module { +public: + MyModule() : Sirit::Module{0x00010300} {} + ~MyModule() = default; + + void Generate() { + AddCapability(spv::Capability::Shader); + SetMemoryModel(spv::AddressingModel::Logical, spv::MemoryModel::GLSL450); + + const auto t_void = Name(TypeVoid(), "void"); + const auto t_uint = Name(TypeInt(32, false), "uint"); + const auto t_float = Name(TypeFloat(32), "float"); + + const auto float4 = Name(TypeVector(t_float, 4), "float4"); + const auto in_float = Name(TypePointer(spv::StorageClass::Input, t_float), "in_float"); + const auto in_float4 = Name(TypePointer(spv::StorageClass::Input, float4), "in_float4"); + const auto out_float4 = Name(TypePointer(spv::StorageClass::Output, float4), "out_float4"); + + const auto gl_per_vertex = Name(TypeStruct(float4), "gl_PerVertex"); + const auto gl_per_vertex_ptr = + Name(TypePointer(spv::StorageClass::Output, gl_per_vertex), "out_gl_PerVertex"); + + const auto in_pos = Name(AddGlobalVariable(in_float4, spv::StorageClass::Input), "in_pos"); + const auto per_vertex = + Name(AddGlobalVariable(gl_per_vertex_ptr, spv::StorageClass::Output), "per_vertex"); + + Decorate(in_pos, spv::Decoration::Location, 0); + Decorate(gl_per_vertex, spv::Decoration::Block); + Decorate(gl_per_vertex, spv::Decoration::Block); + MemberDecorate(gl_per_vertex, 0, spv::Decoration::BuiltIn, + static_cast(spv::BuiltIn::Position)); + + const auto main_func = Name( + OpFunction(t_void, spv::FunctionControlMask::MaskNone, TypeFunction(t_void)), "main"); + AddLabel(); + + const auto ptr_pos_x = OpAccessChain(in_float, in_pos, Constant(t_uint, 0u)); + const auto ptr_pos_y = OpAccessChain(in_float, in_pos, Constant(t_uint, 1u)); + + const auto pos_x = OpLoad(t_float, ptr_pos_x); + const auto pos_y = OpLoad(t_float, ptr_pos_y); + + auto tmp_position = OpUndef(float4); + tmp_position = OpCompositeInsert(float4, pos_x, tmp_position, 0); + tmp_position = OpCompositeInsert(float4, pos_y, tmp_position, 1); + tmp_position = OpCompositeInsert(float4, Constant(t_float, 0.0f), tmp_position, 2); + tmp_position = OpCompositeInsert(float4, Constant(t_float, 1.0f), tmp_position, 3); + + const auto gl_position = OpAccessChain(out_float4, per_vertex, Constant(t_uint, 0u)); + OpStore(gl_position, tmp_position); + + OpReturn(); + OpFunctionEnd(); + + AddEntryPoint(spv::ExecutionModel::Vertex, main_func, "main", in_pos, per_vertex); + } +}; + +static constexpr std::uint8_t expected_binary[] = { + 0x03, 0x02, 0x23, 0x07, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x76, 0x6f, 0x69, 0x64, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x75, 0x69, 0x6e, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x03, 0x00, 0x00, 0x00, 0x66, 0x6c, 0x6f, 0x61, + 0x74, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x66, 0x6c, 0x6f, 0x61, + 0x74, 0x34, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x00, 0x00, 0x69, 0x6e, 0x5f, 0x66, + 0x6c, 0x6f, 0x61, 0x74, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x69, 0x6e, 0x5f, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x6f, 0x75, 0x74, 0x5f, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x00, 0x00, + 0x05, 0x00, 0x06, 0x00, 0x08, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x50, 0x65, 0x72, 0x56, 0x65, + 0x72, 0x74, 0x65, 0x78, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x6f, 0x75, 0x74, 0x5f, 0x67, 0x6c, 0x5f, 0x50, 0x65, 0x72, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x69, 0x6e, 0x5f, 0x70, + 0x6f, 0x73, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x70, 0x65, 0x72, 0x5f, + 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, + 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x05, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x2b, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2b, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x2b, 0x00, 0x04, 0x00, 0x03, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2b, 0x00, 0x04, 0x00, 0x03, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, + 0x3b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x36, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0x05, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x03, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x03, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x52, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x52, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x52, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x1b, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x0f, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, + 0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, +}; + +int main(int argc, char** argv) { + MyModule module; + module.Generate(); + + std::vector code = module.Assemble(); + if (std::size(code) * sizeof(std::uint32_t) != std::size(expected_binary)) { + return EXIT_FAILURE; + } + if (std::memcmp(std::data(code), std::data(expected_binary), std::size(expected_binary)) != 0) { + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +}