Stream SPIR-V instructions directly to a binary

Before this commit sirit generated a stream of tokens that would then be
inserted to the final SPIR-V binary. This design was carried from the
initial design of manually inserting opcodes into the code. Now that
all instructions but labels are inserted when their respective function
is called, the old design can be dropped in favor of generating a valid
stream of SPIR-V opcodes.

The API for variables is broken, but adopting the new one is trivial.
Instead of calling OpVariable and then adding a global or local
variable, OpVariable was removed and global or local variables are
generated when they are called.

Avoiding duplicates is now done with an std::unordered_set instead of
using a linear search jumping through vtables.
This commit is contained in:
ReinUsesLisp 2020-08-01 01:50:01 -03:00
parent c4ea8f4b76
commit 0b9ee36247
31 changed files with 651 additions and 1150 deletions

View file

@ -12,6 +12,7 @@
#include <optional>
#include <span>
#include <string>
#include <string_view>
#include <unordered_set>
#include <variant>
#include <vector>
@ -22,12 +23,16 @@ namespace Sirit {
constexpr std::uint32_t GENERATOR_MAGIC_NUMBER = 0;
class Op;
class Declarations;
class Operand;
class Stream;
using Literal =
std::variant<std::uint32_t, std::uint64_t, std::int32_t, std::int64_t, float, double>;
using Id = const Op*;
struct Id {
std::uint32_t value;
};
class Module {
public:
@ -52,12 +57,12 @@ public:
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 name,
void AddEntryPoint(spv::ExecutionModel execution_model, Id entry_point, std::string_view name,
std::span<const Id> interfaces = {});
/// Adds an entry point.
template <typename... Ts>
void AddEntryPoint(spv::ExecutionModel execution_model, Id entry_point, std::string name,
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<const Id>{std::array{interfaces...}});
@ -89,19 +94,13 @@ public:
return AddLabel(OpLabel());
}
/**
* Adds a local variable to the code
* @param variable Variable to insert into code.
* @return Returns variable.
*/
Id AddLocalVariable(Id label);
/// Adds a local variable to the code
Id AddLocalVariable(Id result_type, spv::StorageClass storage_class,
std::optional<Id> initializer = {});
/**
* Adds a global variable
* @param variable Global variable to add.
* @return Returns variable.
*/
Id AddGlobalVariable(Id variable);
/// Adds a global variable
Id AddGlobalVariable(Id result_type, spv::StorageClass storage_class,
std::optional<Id> initializer = {});
// Types
@ -150,7 +149,7 @@ public:
}
/// Returns type opaque.
Id TypeOpaque(std::string name);
Id TypeOpaque(std::string_view name);
/// Returns type pointer.
Id TypePointer(spv::StorageClass storage_class, Id type);
@ -212,7 +211,7 @@ public:
Id OpFunction(Id result_type, spv::FunctionControlMask function_control, Id function_type);
/// Ends a function.
Id OpFunctionEnd();
void OpFunctionEnd();
/// Call a function.
Id OpFunctionCall(Id result_type, Id function, std::span<const Id> arguments = {});
@ -244,7 +243,7 @@ public:
Id OpLabel();
/// The block label instruction: Any reference to a block is through this ref.
Id OpLabel(std::string label_name) {
Id OpLabel(std::string_view label_name) {
return Name(OpLabel(), std::move(label_name));
}
@ -273,23 +272,20 @@ public:
/// Assign a name string to a reference.
/// @return target
Id Name(Id target, std::string name);
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 name);
Id MemberName(Id type, std::uint32_t member, std::string_view name);
/// Assign a Result <id> to a string for use by other debug instructions.
Id String(std::string string);
Id String(std::string_view string);
/// Add source-level location information
Id OpLine(Id file, Literal line, Literal column);
// Memory
/// Allocate an object in memory, resulting in a copy to it.
Id OpVariable(Id result_type, spv::StorageClass storage_class, Id initializer = nullptr);
/// 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);
@ -1097,38 +1093,27 @@ public:
Id OpAtomicXor(Id result_type, Id pointer, Id memory, Id semantics, Id value);
private:
Id AddCode(std::unique_ptr<Op> op);
Id AddCode(spv::Op opcode, std::optional<std::uint32_t> id = {});
Id AddDeclaration(std::unique_ptr<Op> op);
void AddAnnotation(std::unique_ptr<Op> op);
Id GetGLSLstd450();
std::uint32_t version{};
std::uint32_t bound{1};
std::uint32_t bound{};
std::unordered_set<std::string> extensions;
std::unordered_set<spv::Capability> capabilities;
std::unordered_set<std::unique_ptr<Op>> ext_inst_import;
std::unique_ptr<Op> glsl_std_450;
std::optional<Id> glsl_std_450;
spv::AddressingModel addressing_model{spv::AddressingModel::Logical};
spv::MemoryModel memory_model{spv::MemoryModel::GLSL450};
std::vector<std::unique_ptr<Op>> entry_points;
std::vector<std::unique_ptr<Op>> execution_modes;
std::vector<std::unique_ptr<Op>> debug;
std::vector<std::unique_ptr<Op>> annotations;
std::vector<std::unique_ptr<Op>> declarations;
std::vector<Id> global_variables;
std::vector<Id> code;
std::vector<std::unique_ptr<Op>> code_store;
std::unique_ptr<Stream> ext_inst_imports;
std::unique_ptr<Stream> entry_points;
std::unique_ptr<Stream> execution_modes;
std::unique_ptr<Stream> debug;
std::unique_ptr<Stream> annotations;
std::unique_ptr<Declarations> declarations;
std::unique_ptr<Stream> global_variables;
std::unique_ptr<Stream> code;
};
} // namespace Sirit

View file

@ -1,16 +1,7 @@
add_library(sirit
../include/sirit/sirit.h
sirit.cpp
op.cpp
op.h
stream.cpp
stream.h
operand.cpp
operand.h
literal_number.cpp
literal_number.h
literal_string.cpp
literal_string.h
common_types.h
instructions/type.cpp
instructions/constant.cpp

View file

@ -4,32 +4,24 @@
* 3-Clause BSD License
*/
#include <memory>
#include <vector>
#include "common_types.h"
#include "op.h"
#include <span>
#include "sirit/sirit.h"
#include "stream.h"
namespace Sirit {
Id Module::Decorate(Id target, spv::Decoration decoration, std::span<const Literal> literals) {
auto op{std::make_unique<Op>(spv::Op::OpDecorate)};
op->Add(target);
op->Add(static_cast<u32>(decoration));
op->Add(literals);
AddAnnotation(std::move(op));
return target;
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<const Literal> literals) {
auto op{std::make_unique<Op>(spv::Op::OpMemberDecorate)};
op->Add(structure_type);
op->Add(member);
op->Add(static_cast<u32>(decoration));
op->Add(literals);
AddAnnotation(std::move(op));
return structure_type;
annotations->Reserve(4 + literals.size());
return *annotations << spv::Op::OpMemberDecorate << structure_type << member << decoration
<< literals << EndOp{};
}
} // namespace Sirit

View file

@ -4,35 +4,22 @@
* 3-Clause BSD License
*/
#include <memory>
#include "common_types.h"
#include "op.h"
#include "sirit/sirit.h"
#include "stream.h"
namespace Sirit {
#define DEFINE_UNARY(funcname, opcode) \
Id Module::funcname(Id result_type, Id operand) { \
auto op{std::make_unique<Op>(opcode, bound++, result_type)}; \
op->Add(operand); \
return AddCode(std::move(op)); \
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) { \
auto op{std::make_unique<Op>(opcode, bound++, result_type)}; \
op->Add(operand_1); \
op->Add(operand_2); \
return AddCode(std::move(op)); \
}
#define DEFINE_TRINARY(funcname, opcode) \
Id Module::funcname(Id result_type, Id operand_1, Id operand_2, Id operand_3) { \
auto op{std::make_unique<Op>(opcode, bound++, result_type)}; \
op->Add(operand_1); \
op->Add(operand_2); \
op->Add(operand_3); \
return AddCode(std::move(op)); \
code->Reserve(5); \
return *code << OpId{opcode, result_type} << operand_1 << operand_2 << EndOp{}; \
}
DEFINE_UNARY(OpSNegate, spv::Op::OpSNegate)

View file

@ -4,145 +4,101 @@
* 3-Clause BSD License
*/
#include "common_types.h"
#include "op.h"
#include "sirit/sirit.h"
#include "stream.h"
namespace Sirit {
Id Module::OpAtomicLoad(Id result_type, Id pointer, Id memory, Id semantics) {
auto op{std::make_unique<Op>(spv::Op::OpAtomicLoad, bound++, result_type)};
op->Add(pointer);
op->Add(memory);
op->Add(semantics);
return AddCode(std::move(op));
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) {
auto op{std::make_unique<Op>(spv::Op::OpAtomicStore)};
op->Add(pointer);
op->Add(memory);
op->Add(semantics);
op->Add(value);
return AddCode(std::move(op));
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) {
auto op{std::make_unique<Op>(spv::Op::OpAtomicExchange, bound++, result_type)};
op->Add(pointer);
op->Add(memory);
op->Add(semantics);
op->Add(value);
return AddCode(std::move(op));
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) {
auto op{std::make_unique<Op>(spv::Op::OpAtomicCompareExchange, bound++, result_type)};
op->Add(pointer);
op->Add(memory);
op->Add(equal);
op->Add(unequal);
op->Add(value);
op->Add(comparator);
return AddCode(std::move(op));
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) {
auto op{std::make_unique<Op>(spv::Op::OpAtomicIIncrement, bound++, result_type)};
op->Add(pointer);
op->Add(memory);
op->Add(semantics);
return AddCode(std::move(op));
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) {
auto op{std::make_unique<Op>(spv::Op::OpAtomicIDecrement, bound++, result_type)};
op->Add(pointer);
op->Add(memory);
op->Add(semantics);
return AddCode(std::move(op));
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) {
auto op{std::make_unique<Op>(spv::Op::OpAtomicIAdd, bound++, result_type)};
op->Add(pointer);
op->Add(memory);
op->Add(semantics);
op->Add(value);
return AddCode(std::move(op));
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) {
auto op{std::make_unique<Op>(spv::Op::OpAtomicISub, bound++, result_type)};
op->Add(pointer);
op->Add(memory);
op->Add(semantics);
op->Add(value);
return AddCode(std::move(op));
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) {
auto op{std::make_unique<Op>(spv::Op::OpAtomicSMin, bound++, result_type)};
op->Add(pointer);
op->Add(memory);
op->Add(semantics);
op->Add(value);
return AddCode(std::move(op));
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) {
auto op{std::make_unique<Op>(spv::Op::OpAtomicUMin, bound++, result_type)};
op->Add(pointer);
op->Add(memory);
op->Add(semantics);
op->Add(value);
return AddCode(std::move(op));
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) {
auto op{std::make_unique<Op>(spv::Op::OpAtomicSMax, bound++, result_type)};
op->Add(pointer);
op->Add(memory);
op->Add(semantics);
op->Add(value);
return AddCode(std::move(op));
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) {
auto op{std::make_unique<Op>(spv::Op::OpAtomicUMax, bound++, result_type)};
op->Add(pointer);
op->Add(memory);
op->Add(semantics);
op->Add(value);
return AddCode(std::move(op));
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) {
auto op{std::make_unique<Op>(spv::Op::OpAtomicAnd, bound++, result_type)};
op->Add(pointer);
op->Add(memory);
op->Add(semantics);
op->Add(value);
return AddCode(std::move(op));
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) {
auto op{std::make_unique<Op>(spv::Op::OpAtomicOr, bound++, result_type)};
op->Add(pointer);
op->Add(memory);
op->Add(semantics);
op->Add(value);
return AddCode(std::move(op));
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) {
auto op{std::make_unique<Op>(spv::Op::OpAtomicXor, bound++, result_type)};
op->Add(pointer);
op->Add(memory);
op->Add(semantics);
op->Add(value);
return AddCode(std::move(op));
code->Reserve(7);
return *code << OpId{spv::Op::OpAtomicXor, result_type} << pointer << memory << semantics
<< value << EndOp{};
}
} // namespace Sirit

View file

@ -4,25 +4,20 @@
* 3-Clause BSD License
*/
#include <memory>
#include "op.h"
#include "sirit/sirit.h"
#include "stream.h"
namespace Sirit {
Id Module::OpControlBarrier(Id execution, Id memory, Id semantics) {
auto op = std::make_unique<Op>(spv::Op::OpControlBarrier);
op->Add(execution);
op->Add(memory);
op->Add(semantics);
return AddCode(std::move(op));
code->Reserve(4);
return *code << spv::Op::OpControlBarrier << execution << memory << semantics << EndOp{};
}
Id Module::OpMemoryBarrier(Id scope, Id semantics) {
auto op = std::make_unique<Op>(spv::Op::OpMemoryBarrier);
op->Add(scope);
op->Add(semantics);
return AddCode(std::move(op));
code->Reserve(3);
return *code << spv::Op::OpMemoryBarrier << scope << semantics << EndOp{};
}
} // namespace Sirit

View file

@ -4,96 +4,73 @@
* 3-Clause BSD License
*/
#include <memory>
#include "common_types.h"
#include "op.h"
#include "sirit/sirit.h"
#include "stream.h"
namespace Sirit {
Id Module::OpShiftRightLogical(Id result_type, Id base, Id shift) {
auto op{std::make_unique<Op>(spv::Op::OpShiftRightLogical, bound++, result_type)};
op->Add(base);
op->Add(shift);
return AddCode(std::move(op));
code->Reserve(5);
return *code << OpId{spv::Op::OpShiftRightLogical, result_type} << base << shift << EndOp{};
}
Id Module::OpShiftRightArithmetic(Id result_type, Id base, Id shift) {
auto op{std::make_unique<Op>(spv::Op::OpShiftRightArithmetic, bound++, result_type)};
op->Add(base);
op->Add(shift);
return AddCode(std::move(op));
code->Reserve(5);
return *code << OpId{spv::Op::OpShiftRightArithmetic, result_type} << base << shift << EndOp{};
}
Id Module::OpShiftLeftLogical(Id result_type, Id base, Id shift) {
auto op{std::make_unique<Op>(spv::Op::OpShiftLeftLogical, bound++, result_type)};
op->Add(base);
op->Add(shift);
return AddCode(std::move(op));
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) {
auto op{std::make_unique<Op>(spv::Op::OpBitwiseOr, bound++, result_type)};
op->Add(operand_1);
op->Add(operand_2);
return AddCode(std::move(op));
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) {
auto op{std::make_unique<Op>(spv::Op::OpBitwiseXor, bound++, result_type)};
op->Add(operand_1);
op->Add(operand_2);
return AddCode(std::move(op));
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) {
auto op{std::make_unique<Op>(spv::Op::OpBitwiseAnd, bound++, result_type)};
op->Add(operand_1);
op->Add(operand_2);
return AddCode(std::move(op));
code->Reserve(5);
return *code << OpId{spv::Op::OpBitwiseAnd, result_type} << operand_1 << operand_2 << EndOp{};
}
Id Module::OpNot(Id result_type, Id operand) {
auto op{std::make_unique<Op>(spv::Op::OpNot, bound++, result_type)};
op->Add(operand);
return AddCode(std::move(op));
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) {
auto op{std::make_unique<Op>(spv::Op::OpBitFieldInsert, bound++, result_type)};
op->Add(base);
op->Add(insert);
op->Add(offset);
op->Add(count);
return AddCode(std::move(op));
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) {
auto op{std::make_unique<Op>(spv::Op::OpBitFieldSExtract, bound++, result_type)};
op->Add(base);
op->Add(offset);
op->Add(count);
return AddCode(std::move(op));
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) {
auto op{std::make_unique<Op>(spv::Op::OpBitFieldUExtract, bound++, result_type)};
op->Add(base);
op->Add(offset);
op->Add(count);
return AddCode(std::move(op));
code->Reserve(6);
return *code << OpId{spv::Op::OpBitFieldUExtract, result_type} << base << offset << count
<< EndOp{};
}
Id Module::OpBitReverse(Id result_type, Id base) {
auto op{std::make_unique<Op>(spv::Op::OpBitReverse, bound++, result_type)};
op->Add(base);
return AddCode(std::move(op));
code->Reserve(4);
return *code << OpId{spv::Op::OpBitReverse, result_type} << base << EndOp{};
}
Id Module::OpBitCount(Id result_type, Id base) {
auto op{std::make_unique<Op>(spv::Op::OpBitCount, bound++, result_type)};
op->Add(base);
return AddCode(std::move(op));
code->Reserve(4);
return *code << OpId{spv::Op::OpBitCount, result_type} << base << EndOp{};
}
} // namespace Sirit

View file

@ -5,42 +5,44 @@
*/
#include <cassert>
#include "op.h"
#include "sirit/sirit.h"
#include "stream.h"
namespace Sirit {
Id Module::ConstantTrue(Id result_type) {
return AddDeclaration(std::make_unique<Op>(spv::Op::OpConstantTrue, bound, result_type));
declarations->Reserve(3);
return *declarations << OpId{spv::Op::OpConstantTrue, result_type} << EndOp{};
}
Id Module::ConstantFalse(Id result_type) {
return AddDeclaration(std::make_unique<Op>(spv::Op::OpConstantFalse, bound, result_type));
declarations->Reserve(3);
return *declarations << OpId{spv::Op::OpConstantFalse, result_type} << EndOp{};
}
Id Module::Constant(Id result_type, const Literal& literal) {
auto op{std::make_unique<Op>(spv::Op::OpConstant, bound, result_type)};
op->Add(literal);
return AddDeclaration(std::move(op));
declarations->Reserve(3 + 2);
return *declarations << OpId{spv::Op::OpConstant, result_type} << literal << EndOp{};
}
Id Module::ConstantComposite(Id result_type, std::span<const Id> constituents) {
auto op{std::make_unique<Op>(spv::Op::OpConstantComposite, bound, result_type)};
op->Add(constituents);
return AddDeclaration(std::move(op));
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) {
auto op{std::make_unique<Op>(spv::Op::OpConstantSampler, bound, result_type)};
op->Add(static_cast<u32>(addressing_mode));
op->Add(normalized ? 1 : 0);
op->Add(static_cast<u32>(filter_mode));
return AddDeclaration(std::move(op));
declarations->Reserve(6);
return *declarations << OpId{spv::Op::OpConstantSampler, result_type} << addressing_mode
<< normalized << filter_mode << EndOp{};
}
Id Module::ConstantNull(Id result_type) {
return AddDeclaration(std::make_unique<Op>(spv::Op::OpConstantNull, bound, result_type));
declarations->Reserve(3);
return *declarations << OpId{spv::Op::OpConstantNull, result_type} << EndOp{};
}
} // namespace Sirit

View file

@ -4,18 +4,16 @@
* 3-Clause BSD License
*/
#include <memory>
#include "common_types.h"
#include "op.h"
#include "sirit/sirit.h"
#include "stream.h"
namespace Sirit {
#define DEFINE_UNARY(opcode) \
Id Module::opcode(Id result_type, Id operand) { \
auto op{std::make_unique<Op>(spv::Op::opcode, bound++, result_type)}; \
op->Add(operand); \
return AddCode(std::move(op)); \
code->Reserve(4); \
return *code << OpId{spv::Op::opcode, result_type} << operand << EndOp{}; \
}
DEFINE_UNARY(OpConvertFToU)

View file

@ -4,44 +4,33 @@
* 3-Clause BSD License
*/
#include <memory>
#include <string>
#include "op.h"
#include "sirit/sirit.h"
#include "common_types.h"
#include "stream.h"
namespace Sirit {
Id Module::Name(Id target, std::string name) {
auto op{std::make_unique<Op>(spv::Op::OpName)};
op->Add(target);
op->Add(std::move(name));
debug.push_back(std::move(op));
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 name) {
auto op{std::make_unique<Op>(spv::Op::OpMemberName)};
op->Add(type);
op->Add(member);
op->Add(std::move(name));
debug.push_back(std::move(op));
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 string) {
auto op{std::make_unique<Op>(spv::Op::OpString, bound++)};
op->Add(std::move(string));
const auto id = op.get();
debug.push_back(std::move(op));
return id;
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) {
auto op{std::make_unique<Op>(spv::Op::OpLine)};
op->Add(file);
op->Add(line);
op->Add(column);
return AddCode(std::move(op));
debug->Reserve(4);
return *debug << spv::Op::OpLine << file << line << column << EndOp{};
}
} // namespace Sirit

View file

@ -4,20 +4,18 @@
* 3-Clause BSD License
*/
#include <memory>
#include <spirv/unified1/GLSL.std.450.h>
#include "common_types.h"
#include "op.h"
#include "sirit/sirit.h"
#include "stream.h"
namespace Sirit {
Id Module::OpExtInst(Id result_type, Id set, u32 instruction, std::span<const Id> operands) {
auto op{std::make_unique<Op>(spv::Op::OpExtInst, bound++, result_type)};
op->Add(set);
op->Add(instruction);
op->Add(operands);
return AddCode(std::move(op));
code->Reserve(5 + operands.size());
return *code << OpId{spv::Op::OpExtInst, result_type} << set << instruction << operands
<< EndOp{};
}
#define DEFINE_UNARY(funcname, opcode) \
@ -74,4 +72,5 @@ DEFINE_UNARY(OpFindUMsb, GLSLstd450FindUMsb)
DEFINE_UNARY(OpInterpolateAtCentroid, GLSLstd450InterpolateAtCentroid)
DEFINE_BINARY(OpInterpolateAtSample, GLSLstd450InterpolateAtSample)
DEFINE_BINARY(OpInterpolateAtOffset, GLSLstd450InterpolateAtOffset)
} // namespace Sirit

View file

@ -5,79 +5,70 @@
*/
#include <cassert>
#include <vector>
#include "common_types.h"
#include "op.h"
#include "sirit/sirit.h"
#include "stream.h"
namespace Sirit {
Id Module::OpLoopMerge(Id merge_block, Id continue_target, spv::LoopControlMask loop_control,
std::span<const Id> literals) {
auto op{std::make_unique<Op>(spv::Op::OpLoopMerge)};
op->Add(merge_block);
op->Add(continue_target);
op->Add(static_cast<u32>(loop_control));
op->Add(literals);
return AddCode(std::move(op));
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) {
auto op{std::make_unique<Op>(spv::Op::OpSelectionMerge)};
op->Add(merge_block);
op->Add(static_cast<u32>(selection_control));
return AddCode(std::move(op));
code->Reserve(3);
return *code << spv::Op::OpSelectionMerge << merge_block << selection_control << EndOp{};
}
Id Module::OpLabel() {
return code_store.emplace_back(std::make_unique<Op>(spv::Op::OpLabel, bound++)).get();
return Id{++bound};
}
Id Module::OpBranch(Id target_label) {
auto op{std::make_unique<Op>(spv::Op::OpBranch)};
op->Add(target_label);
return AddCode(std::move(op));
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) {
auto op{std::make_unique<Op>(spv::Op::OpBranchConditional)};
op->Add(condition);
op->Add(true_label);
op->Add(false_label);
code->Reserve(6);
*code << spv::Op::OpBranchConditional << condition << true_label << false_label;
if (true_weight != 0 || false_weight != 0) {
op->Add(true_weight);
op->Add(false_weight);
*code << true_weight << false_weight;
}
return AddCode(std::move(op));
return *code << EndOp{};
}
Id Module::OpSwitch(Id selector, Id default_label, std::span<const Literal> literals,
std::span<const Id> labels) {
const std::size_t size = literals.size();
assert(literals.size() == labels.size());
auto op{std::make_unique<Op>(spv::Op::OpSwitch)};
op->Add(selector);
op->Add(default_label);
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) {
op->Add(literals[i]);
op->Add(labels[i]);
*code << literals[i] << labels[i];
}
return AddCode(std::move(op));
return *code << EndOp{};
}
Id Module::OpReturn() {
return AddCode(spv::Op::OpReturn);
code->Reserve(1);
return *code << spv::Op::OpReturn << EndOp{};
}
Id Module::OpReturnValue(Id value) {
auto op{std::make_unique<Op>(spv::Op::OpReturnValue)};
op->Add(value);
return AddCode(std::move(op));
code->Reserve(2);
return *code << spv::Op::OpReturnValue << value << EndOp{};
}
Id Module::OpKill() {
return AddCode(std::make_unique<Op>(spv::Op::OpKill));
code->Reserve(1);
return *code << spv::Op::OpKill << EndOp{};
}
} // namespace Sirit

View file

@ -4,28 +4,26 @@
* 3-Clause BSD License
*/
#include "common_types.h"
#include "op.h"
#include "sirit/sirit.h"
#include "stream.h"
namespace Sirit {
Id Module::OpFunction(Id result_type, spv::FunctionControlMask function_control, Id function_type) {
auto op{std::make_unique<Op>(spv::Op::OpFunction, bound++, result_type)};
op->Add(static_cast<u32>(function_control));
op->Add(function_type);
return AddCode(std::move(op));
code->Reserve(5);
return *code << OpId{spv::Op::OpFunction, result_type} << function_control << function_type
<< EndOp{};
}
Id Module::OpFunctionEnd() {
return AddCode(spv::Op::OpFunctionEnd);
void Module::OpFunctionEnd() {
code->Reserve(1);
*code << spv::Op::OpFunctionEnd << EndOp{};
}
Id Module::OpFunctionCall(Id result_type, Id function, std::span<const Id> arguments) {
auto op{std::make_unique<Op>(spv::Op::OpFunctionCall, bound++, result_type)};
op->Add(function);
op->Add(arguments);
return AddCode(std::move(op));
code->Reserve(4 + arguments.size());
return *code << OpId{spv::Op::OpFunctionCall, result_type} << function << arguments << EndOp{};
}
} // namespace Sirit

View file

@ -4,48 +4,42 @@
* 3-Clause BSD License
*/
#include "op.h"
#include "sirit/sirit.h"
#include "stream.h"
namespace Sirit {
Id Module::OpSubgroupBallotKHR(Id result_type, Id predicate) {
auto op = std::make_unique<Op>(spv::Op::OpSubgroupBallotKHR, bound++, result_type);
op->Add(predicate);
return AddCode(std::move(op));
code->Reserve(4);
return *code << OpId{spv::Op::OpSubgroupBallotKHR, result_type} << predicate << EndOp{};
}
Id Module::OpSubgroupReadInvocationKHR(Id result_type, Id value, Id index) {
auto op = std::make_unique<Op>(spv::Op::OpSubgroupReadInvocationKHR, bound++, result_type);
op->Add(value);
op->Add(index);
return AddCode(std::move(op));
code->Reserve(5);
return *code << OpId{spv::Op::OpSubgroupReadInvocationKHR, result_type} << value << index
<< EndOp{};
}
Id Module::OpSubgroupAllKHR(Id result_type, Id predicate) {
auto op = std::make_unique<Op>(spv::Op::OpSubgroupAllKHR, bound++, result_type);
op->Add(predicate);
return AddCode(std::move(op));
code->Reserve(4);
return *code << OpId{spv::Op::OpSubgroupAllKHR, result_type} << predicate << EndOp{};
}
Id Module::OpSubgroupAnyKHR(Id result_type, Id predicate) {
auto op = std::make_unique<Op>(spv::Op::OpSubgroupAnyKHR, bound++, result_type);
op->Add(predicate);
return AddCode(std::move(op));
code->Reserve(4);
return *code << OpId{spv::Op::OpSubgroupAnyKHR, result_type} << predicate << EndOp{};
}
Id Module::OpSubgroupAllEqualKHR(Id result_type, Id predicate) {
auto op = std::make_unique<Op>(spv::Op::OpSubgroupAllEqualKHR, bound++, result_type);
op->Add(predicate);
return AddCode(std::move(op));
code->Reserve(4);
return *code << OpId{spv::Op::OpSubgroupAllEqualKHR, result_type} << predicate << EndOp{};
}
Id Module::OpGroupNonUniformShuffleXor(Id result_type, spv::Scope scope, Id value, Id mask) {
auto op = std::make_unique<Op>(spv::Op::OpGroupNonUniformShuffleXor, bound++, result_type);
op->Add(static_cast<u32>(scope));
op->Add(value);
op->Add(mask);
return AddCode(std::move(op));
code->Reserve(6);
return *code << OpId{spv::Op::OpGroupNonUniformShuffleXor, result_type} << scope << value
<< mask << EndOp{};
}
} // namespace Sirit

View file

@ -4,79 +4,58 @@
* 3-Clause BSD License
*/
#include "common_types.h"
#include "op.h"
#include <cassert>
#include "sirit/sirit.h"
namespace Sirit {
#include "stream.h"
static void AddImageOperands(Op* op, std::optional<spv::ImageOperandsMask> image_operands,
std::span<const Id> operands) {
if (!image_operands)
return;
op->Add(static_cast<u32>(*image_operands));
op->Add(operands);
}
namespace Sirit {
#define DEFINE_IMAGE_OP(opcode) \
Id Module::opcode(Id result_type, Id sampled_image, Id coordinate, \
std::optional<spv::ImageOperandsMask> image_operands, \
std::span<const Id> operands) { \
auto op{std::make_unique<Op>(spv::Op::opcode, bound++, result_type)}; \
op->Add(sampled_image); \
op->Add(coordinate); \
AddImageOperands(op.get(), image_operands, operands); \
return AddCode(std::move(op)); \
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<const Id> operands) { \
auto op{std::make_unique<Op>(spv::Op::opcode, bound++, result_type)}; \
op->Add(sampled_image); \
op->Add(coordinate); \
op->Add(static_cast<u32>(image_operands)); \
op->Add(operands); \
return AddCode(std::move(op)); \
code->Reserve(7 + operands.size()); \
return *code << OpId{spv::Op::OpDecorate, 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<spv::ImageOperandsMask> image_operands, \
std::span<const Id> operands) { \
auto op{std::make_unique<Op>(spv::Op::opcode, bound++, result_type)}; \
op->Add(sampled_image); \
op->Add(coordinate); \
op->Add(extra); \
AddImageOperands(op.get(), image_operands, operands); \
return AddCode(std::move(op)); \
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<const Id> operands) { \
auto op{std::make_unique<Op>(spv::Op::opcode, bound++, result_type)}; \
op->Add(sampled_image); \
op->Add(coordinate); \
op->Add(extra); \
op->Add(static_cast<u32>(image_operands)); \
op->Add(operands); \
return AddCode(std::move(op)); \
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) { \
auto op{std::make_unique<Op>(spv::Op::opcode, bound++, result_type)}; \
op->Add(image); \
return AddCode(std::move(op)); \
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) { \
auto op{std::make_unique<Op>(spv::Op::opcode, bound++, result_type)}; \
op->Add(image); \
op->Add(extra); \
return AddCode(std::move(op)); \
code->Reserve(5); \
return *code << OpId{spv::Op::OpDecorate, result_type} << image << extra << EndOp{}; \
}
DEFINE_IMAGE_OP(OpImageSampleImplicitLod)
@ -98,27 +77,22 @@ DEFINE_IMAGE_QUERY_OP(OpImageQueryLevels)
DEFINE_IMAGE_QUERY_OP(OpImageQuerySamples)
Id Module::OpSampledImage(Id result_type, Id image, Id sampler) {
auto op{std::make_unique<Op>(spv::Op::OpSampledImage, bound++, result_type)};
op->Add(image);
op->Add(sampler);
return AddCode(std::move(op));
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<spv::ImageOperandsMask> image_operands,
std::span<const Id> operands) {
auto op{std::make_unique<Op>(spv::Op::OpImageWrite)};
op->Add(image);
op->Add(coordinate);
op->Add(texel);
AddImageOperands(op.get(), image_operands, operands);
return AddCode(std::move(op));
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) {
auto op{std::make_unique<Op>(spv::Op::OpImage, bound++, result_type)};
op->Add(sampled_image);
return AddCode(std::move(op));
code->Reserve(4);
return *code << OpId{spv::Op::OpImage, result_type} << sampled_image << EndOp{};
}
} // namespace Sirit

View file

@ -4,68 +4,62 @@
* 3-Clause BSD License
*/
#include <memory>
#include "common_types.h"
#include "op.h"
#include "sirit/sirit.h"
#include "stream.h"
namespace Sirit {
#define DEFINE_UNARY(funcname, opcode) \
Id Module::funcname(Id result_type, Id operand) { \
auto op{std::make_unique<Op>(opcode, bound++, result_type)}; \
op->Add(operand); \
return AddCode(std::move(op)); \
#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(funcname, opcode) \
Id Module::funcname(Id result_type, Id operand_1, Id operand_2) { \
auto op{std::make_unique<Op>(opcode, bound++, result_type)}; \
op->Add(operand_1); \
op->Add(operand_2); \
return AddCode(std::move(op)); \
#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(funcname, opcode) \
Id Module::funcname(Id result_type, Id operand_1, Id operand_2, Id operand_3) { \
auto op{std::make_unique<Op>(opcode, bound++, result_type)}; \
op->Add(operand_1); \
op->Add(operand_2); \
op->Add(operand_3); \
return AddCode(std::move(op)); \
#define DEFINE_TRINARY(opcode) \
Id Module::opcode(Id result_type, Id operand_1, Id operand_2, Id operand_3) { \
code->Reserve(5); \
return *code << OpId{spv::Op::opcode, result_type} << operand_1 << operand_2 << operand_3 \
<< EndOp{}; \
}
DEFINE_UNARY(OpAny, spv::Op::OpAny)
DEFINE_UNARY(OpAll, spv::Op::OpAll)
DEFINE_UNARY(OpIsNan, spv::Op::OpIsNan)
DEFINE_UNARY(OpIsInf, spv::Op::OpIsInf)
DEFINE_BINARY(OpLogicalEqual, spv::Op::OpLogicalEqual)
DEFINE_BINARY(OpLogicalNotEqual, spv::Op::OpLogicalNotEqual)
DEFINE_BINARY(OpLogicalOr, spv::Op::OpLogicalOr)
DEFINE_BINARY(OpLogicalAnd, spv::Op::OpLogicalAnd)
DEFINE_UNARY(OpLogicalNot, spv::Op::OpLogicalNot)
DEFINE_TRINARY(OpSelect, spv::Op::OpSelect)
DEFINE_BINARY(OpIEqual, spv::Op::OpIEqual)
DEFINE_BINARY(OpINotEqual, spv::Op::OpINotEqual)
DEFINE_BINARY(OpUGreaterThan, spv::Op::OpUGreaterThan)
DEFINE_BINARY(OpSGreaterThan, spv::Op::OpSGreaterThan)
DEFINE_BINARY(OpUGreaterThanEqual, spv::Op::OpUGreaterThanEqual)
DEFINE_BINARY(OpSGreaterThanEqual, spv::Op::OpSGreaterThanEqual)
DEFINE_BINARY(OpULessThan, spv::Op::OpULessThan)
DEFINE_BINARY(OpSLessThan, spv::Op::OpSLessThan)
DEFINE_BINARY(OpULessThanEqual, spv::Op::OpULessThanEqual)
DEFINE_BINARY(OpSLessThanEqual, spv::Op::OpSLessThanEqual)
DEFINE_BINARY(OpFOrdEqual, spv::Op::OpFOrdEqual)
DEFINE_BINARY(OpFUnordEqual, spv::Op::OpFUnordEqual)
DEFINE_BINARY(OpFOrdNotEqual, spv::Op::OpFOrdNotEqual)
DEFINE_BINARY(OpFUnordNotEqual, spv::Op::OpFUnordNotEqual)
DEFINE_BINARY(OpFOrdLessThan, spv::Op::OpFOrdLessThan)
DEFINE_BINARY(OpFUnordLessThan, spv::Op::OpFUnordLessThan)
DEFINE_BINARY(OpFOrdGreaterThan, spv::Op::OpFOrdGreaterThan)
DEFINE_BINARY(OpFUnordGreaterThan, spv::Op::OpFUnordGreaterThan)
DEFINE_BINARY(OpFOrdLessThanEqual, spv::Op::OpFOrdLessThanEqual)
DEFINE_BINARY(OpFUnordLessThanEqual, spv::Op::OpFUnordLessThanEqual)
DEFINE_BINARY(OpFOrdGreaterThanEqual, spv::Op::OpFOrdGreaterThanEqual)
DEFINE_BINARY(OpFUnordGreaterThanEqual, spv::Op::OpFUnordGreaterThanEqual)
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

View file

@ -5,91 +5,64 @@
*/
#include <cassert>
#include "op.h"
#include "sirit/sirit.h"
#include "stream.h"
namespace Sirit {
Id Module::OpVariable(Id result_type, spv::StorageClass storage_class, Id initializer) {
auto op{std::make_unique<Op>(spv::Op::OpVariable, bound++, result_type)};
op->Add(static_cast<u32>(storage_class));
if (initializer) {
op->Add(initializer);
}
return code_store.emplace_back(std::move(op)).get();
}
Id Module::OpImageTexelPointer(Id result_type, Id image, Id coordinate, Id sample) {
auto op{std::make_unique<Op>(spv::Op::OpImageTexelPointer, bound++, result_type)};
op->Add(image);
op->Add(coordinate);
op->Add(sample);
return AddCode(std::move(op));
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<spv::MemoryAccessMask> memory_access) {
auto op{std::make_unique<Op>(spv::Op::OpLoad, bound++, result_type)};
op->Add(pointer);
if (memory_access) {
op->Add(static_cast<u32>(*memory_access));
}
return AddCode(std::move(op));
code->Reserve(5);
return *code << OpId{spv::Op::OpLoad, result_type} << pointer << memory_access << EndOp{};
}
Id Module::OpStore(Id pointer, Id object, std::optional<spv::MemoryAccessMask> memory_access) {
auto op{std::make_unique<Op>(spv::Op::OpStore)};
op->Add(pointer);
op->Add(object);
if (memory_access) {
op->Add(static_cast<u32>(*memory_access));
}
return AddCode(std::move(op));
code->Reserve(4);
return *code << spv::Op::OpStore << pointer << object << memory_access << EndOp{};
}
Id Module::OpAccessChain(Id result_type, Id base, std::span<const Id> indexes) {
assert(indexes.size() > 0);
auto op{std::make_unique<Op>(spv::Op::OpAccessChain, bound++, result_type)};
op->Add(base);
op->Add(indexes);
return AddCode(std::move(op));
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) {
auto op{std::make_unique<Op>(spv::Op::OpVectorExtractDynamic, bound++, result_type)};
op->Add(vector);
op->Add(index);
return AddCode(std::move(op));
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) {
auto op{std::make_unique<Op>(spv::Op::OpVectorInsertDynamic, bound++, result_type)};
op->Add(vector);
op->Add(component);
op->Add(index);
return AddCode(std::move(op));
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<const Literal> indexes) {
auto op{std::make_unique<Op>(spv::Op::OpCompositeInsert, bound++, result_type)};
op->Add(object);
op->Add(composite);
op->Add(indexes);
return AddCode(std::move(op));
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<const Literal> indexes) {
auto op{std::make_unique<Op>(spv::Op::OpCompositeExtract, bound++, result_type)};
op->Add(composite);
op->Add(indexes);
return AddCode(std::move(op));
code->Reserve(4 + indexes.size());
return *code << OpId{spv::Op::OpCompositeExtract, result_type} << composite << indexes
<< EndOp{};
}
Id Module::OpCompositeConstruct(Id result_type, std::span<const Id> ids) {
assert(ids.size() >= 1);
auto op{std::make_unique<Op>(spv::Op::OpCompositeConstruct, bound++, result_type)};
op->Add(ids);
return AddCode(std::move(op));
code->Reserve(3 + ids.size());
return *code << OpId{spv::Op::OpCompositeConstruct, result_type} << ids << EndOp{};
}
} // namespace Sirit

View file

@ -4,21 +4,25 @@
* 3-Clause BSD License
*/
#include "op.h"
#include "sirit/sirit.h"
#include "stream.h"
namespace Sirit {
Id Module::OpUndef(Id result_type) {
return AddCode(std::make_unique<Op>(spv::Op::OpUndef, bound++, result_type));
code->Reserve(3);
return *code << OpId{spv::Op::OpUndef, result_type} << EndOp{};
}
Id Module::OpEmitVertex() {
return AddCode(std::make_unique<Op>(spv::Op::OpEmitVertex));
code->Reserve(1);
return *code << OpId{spv::Op::OpEmitVertex} << EndOp{};
}
Id Module::OpEndPrimitive() {
return AddCode(std::make_unique<Op>(spv::Op::OpEndPrimitive));
code->Reserve(1);
return *code << OpId{spv::Op::OpEndPrimitive} << EndOp{};
}
} // namespace Sirit

View file

@ -5,137 +5,118 @@
*/
#include <cassert>
#include <memory>
#include <optional>
#include "op.h"
#include "sirit/sirit.h"
#include "stream.h"
namespace Sirit {
Id Module::TypeVoid() {
return AddDeclaration(std::make_unique<Op>(spv::Op::OpTypeVoid, bound));
declarations->Reserve(2);
return *declarations << OpId{spv::Op::OpTypeVoid} << EndOp{};
}
Id Module::TypeBool() {
return AddDeclaration(std::make_unique<Op>(spv::Op::OpTypeBool, bound));
declarations->Reserve(2);
return *declarations << OpId{spv::Op::OpTypeBool} << EndOp{};
}
Id Module::TypeInt(int width, bool is_signed) {
auto op{std::make_unique<Op>(spv::Op::OpTypeInt, bound)};
op->Add(width);
op->Add(is_signed ? 1 : 0);
return AddDeclaration(std::move(op));
declarations->Reserve(4);
return *declarations << OpId{spv::Op::OpTypeInt} << width << is_signed << EndOp{};
}
Id Module::TypeFloat(int width) {
auto op{std::make_unique<Op>(spv::Op::OpTypeFloat, bound)};
op->Add(width);
return AddDeclaration(std::move(op));
declarations->Reserve(3);
return *declarations << OpId{spv::Op::OpTypeFloat} << width << EndOp{};
}
Id Module::TypeVector(Id component_type, int component_count) {
assert(component_count >= 2);
auto op{std::make_unique<Op>(spv::Op::OpTypeVector, bound)};
op->Add(component_type);
op->Add(component_count);
return AddDeclaration(std::move(op));
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);
auto op{std::make_unique<Op>(spv::Op::OpTypeMatrix, bound)};
op->Add(column_type);
op->Add(column_count);
return AddDeclaration(std::move(op));
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<spv::AccessQualifier> access_qualifier) {
auto op{std::make_unique<Op>(spv::Op::OpTypeImage, bound)};
op->Add(sampled_type);
op->Add(static_cast<u32>(dim));
op->Add(depth);
op->Add(arrayed ? 1 : 0);
op->Add(ms ? 1 : 0);
op->Add(sampled);
op->Add(static_cast<u32>(image_format));
if (access_qualifier.has_value()) {
op->Add(static_cast<u32>(access_qualifier.value()));
}
return AddDeclaration(std::move(op));
declarations->Reserve(10);
return *declarations << OpId{spv::Op::OpTypeImage} << sampled_type << dim << depth << arrayed
<< ms << sampled << image_format << access_qualifier << EndOp{};
}
Id Module::TypeSampler() {
return AddDeclaration(std::make_unique<Op>(spv::Op::OpTypeSampler, bound));
declarations->Reserve(2);
return *declarations << OpId{spv::Op::OpTypeSampler} << EndOp{};
}
Id Module::TypeSampledImage(Id image_type) {
auto op{std::make_unique<Op>(spv::Op::OpTypeSampledImage, bound)};
op->Add(image_type);
return AddDeclaration(std::move(op));
declarations->Reserve(3);
return *declarations << OpId{spv::Op::OpTypeSampledImage} << image_type << EndOp{};
}
Id Module::TypeArray(Id element_type, Id length) {
auto op{std::make_unique<Op>(spv::Op::OpTypeArray, bound)};
op->Add(element_type);
op->Add(length);
return AddDeclaration(std::move(op));
declarations->Reserve(4);
return *declarations << OpId{spv::Op::OpTypeArray} << element_type << length << EndOp{};
}
Id Module::TypeRuntimeArray(Id element_type) {
auto op{std::make_unique<Op>(spv::Op::OpTypeRuntimeArray, bound)};
op->Add(element_type);
return AddDeclaration(std::move(op));
declarations->Reserve(3);
return *declarations << OpId{spv::Op::OpTypeRuntimeArray} << element_type << EndOp{};
}
Id Module::TypeStruct(std::span<const Id> members) {
auto op{std::make_unique<Op>(spv::Op::OpTypeStruct, bound)};
op->Add(members);
return AddDeclaration(std::move(op));
declarations->Reserve(2 + members.size());
return *declarations << OpId{spv::Op::OpTypeStruct} << members << EndOp{};
}
Id Module::TypeOpaque(std::string name) {
auto op{std::make_unique<Op>(spv::Op::OpTypeOpaque, bound)};
op->Add(std::move(name));
return AddDeclaration(std::move(op));
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) {
auto op{std::make_unique<Op>(spv::Op::OpTypePointer, bound)};
op->Add(static_cast<u32>(storage_class));
op->Add(type);
return AddDeclaration(std::move(op));
declarations->Reserve(4);
return *declarations << OpId{spv::Op::OpTypePointer} << storage_class << type << EndOp{};
}
Id Module::TypeFunction(Id return_type, std::span<const Id> arguments) {
auto op{std::make_unique<Op>(spv::Op::OpTypeFunction, bound)};
op->Add(return_type);
op->Add(arguments);
return AddDeclaration(std::move(op));
declarations->Reserve(3 + arguments.size());
return *declarations << OpId{spv::Op::OpTypeFunction} << return_type << arguments << EndOp{};
}
Id Module::TypeEvent() {
return AddDeclaration(std::make_unique<Op>(spv::Op::OpTypeEvent, bound));
declarations->Reserve(2);
return *declarations << OpId{spv::Op::OpTypeEvent} << EndOp{};
}
Id Module::TypeDeviceEvent() {
return AddDeclaration(std::make_unique<Op>(spv::Op::OpTypeDeviceEvent, bound));
declarations->Reserve(2);
return *declarations << OpId{spv::Op::OpTypeDeviceEvent} << EndOp{};
}
Id Module::TypeReserveId() {
return AddDeclaration(std::make_unique<Op>(spv::Op::OpTypeReserveId, bound));
declarations->Reserve(2);
return *declarations << OpId{spv::Op::OpTypeReserveId} << EndOp{};
}
Id Module::TypeQueue() {
return AddDeclaration(std::make_unique<Op>(spv::Op::OpTypeQueue, bound));
declarations->Reserve(2);
return *declarations << OpId{spv::Op::OpTypeQueue} << EndOp{};
}
Id Module::TypePipe(spv::AccessQualifier access_qualifier) {
auto op{std::make_unique<Op>(spv::Op::OpTypePipe, bound)};
op->Add(static_cast<u32>(access_qualifier));
return AddDeclaration(std::move(op));
declarations->Reserve(2);
return *declarations << OpId{spv::Op::OpTypePipe} << access_qualifier << EndOp{};
}
} // namespace Sirit

View file

@ -1,37 +0,0 @@
/* 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 <cassert>
#include "literal_number.h"
namespace Sirit {
LiteralNumber::LiteralNumber(u64 raw_, bool is_32_)
: Operand{OperandType::Number}, raw{raw_}, is_32{is_32_} {}
LiteralNumber::~LiteralNumber() = default;
void LiteralNumber::Fetch(Stream& stream) const {
if (is_32) {
stream.Write(static_cast<u32>(raw));
} else {
stream.Write(raw);
}
}
std::size_t LiteralNumber::GetWordCount() const noexcept {
return is_32 ? 1 : 2;
}
bool LiteralNumber::Equal(const Operand& other) const noexcept {
if (!EqualType(other)) {
return false;
}
const auto& o{static_cast<const LiteralNumber&>(other)};
return o.raw == raw && o.is_32 == is_32;
}
} // namespace Sirit

View file

@ -1,43 +0,0 @@
/* 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 <cstddef>
#include <cstring>
#include <memory>
#include "operand.h"
#include "stream.h"
namespace Sirit {
class LiteralNumber final : public Operand {
public:
explicit LiteralNumber(u64 raw, bool is_32);
~LiteralNumber() override;
void Fetch(Stream& stream) const override;
std::size_t GetWordCount() const noexcept override;
bool Equal(const Operand& other) const noexcept override;
template <typename T>
static std::unique_ptr<LiteralNumber> Create(T value) {
static_assert(sizeof(T) == 4 || sizeof(T) == 8);
u64 raw{};
std::memcpy(&raw, &value, sizeof(T));
return std::make_unique<LiteralNumber>(raw, sizeof(T) == 4);
}
private:
u64 raw{};
bool is_32{};
};
} // namespace Sirit

View file

@ -1,33 +0,0 @@
/* 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 <string>
#include "common_types.h"
#include "literal_string.h"
namespace Sirit {
LiteralString::LiteralString(std::string string_)
: Operand{OperandType::String}, string{std::move(string_)} {}
LiteralString::~LiteralString() = default;
void LiteralString::Fetch(Stream& stream) const {
stream.Write(string);
}
std::size_t LiteralString::GetWordCount() const noexcept {
return string.size() / 4 + 1;
}
bool LiteralString::Equal(const Operand& other) const noexcept {
if (!EqualType(other)) {
return false;
}
return static_cast<const LiteralString&>(other).string == string;
}
} // namespace Sirit

View file

@ -1,30 +0,0 @@
/* 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 <string>
#include "operand.h"
#include "stream.h"
namespace Sirit {
class LiteralString final : public Operand {
public:
LiteralString(std::string string);
~LiteralString() override;
void Fetch(Stream& stream) const override;
std::size_t GetWordCount() const noexcept override;
bool Equal(const Operand& other) const noexcept override;
private:
std::string string;
};
} // namespace Sirit

View file

@ -1,135 +0,0 @@
/* 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 <algorithm>
#include <cassert>
#include <limits>
#include "common_types.h"
#include "literal_number.h"
#include "literal_string.h"
#include "op.h"
#include "operand.h"
namespace Sirit {
Op::Op(spv::Op opcode_, std::optional<u32> id_, Id result_type_)
: Operand{OperandType::Op}, opcode{opcode_}, result_type{result_type_}, id{id_} {}
Op::~Op() = default;
void Op::Fetch(Stream& stream) const {
assert(id.has_value());
stream.Write(id.value());
}
std::size_t Op::GetWordCount() const noexcept {
return 1;
}
bool Op::Equal(const Operand& other) const noexcept {
if (!EqualType(other)) {
return false;
}
const auto& op = static_cast<const Op&>(other);
if (op.opcode == opcode && result_type == op.result_type &&
operands.size() == op.operands.size()) {
for (std::size_t i = 0; i < operands.size(); i++) {
if (!operands[i]->Equal(*op.operands[i])) {
return false;
}
}
return true;
}
return false;
}
void Op::Write(Stream& stream) const {
stream.Write(static_cast<u16>(opcode), CalculateTotalWords());
if (result_type) {
result_type->Fetch(stream);
}
if (id.has_value()) {
stream.Write(id.value());
}
for (const auto* operand : operands) {
operand->Fetch(stream);
}
}
void Op::Sink(std::unique_ptr<Operand> operand) {
Add(static_cast<const Operand*>(operand.get()));
operand_store.push_back(std::move(operand));
}
void Op::Add(const Literal& literal) {
Sink([&] {
switch (literal.index()) {
case 0:
return LiteralNumber::Create(std::get<0>(literal));
case 1:
return LiteralNumber::Create(std::get<1>(literal));
case 2:
return LiteralNumber::Create(std::get<2>(literal));
case 3:
return LiteralNumber::Create(std::get<3>(literal));
case 4:
return LiteralNumber::Create(std::get<4>(literal));
case 5:
return LiteralNumber::Create(std::get<5>(literal));
default:
// Invalid literal type
assert(0);
abort();
}
}());
}
void Op::Add(std::span<const Literal> literals) {
for (const auto& literal : literals) {
Add(literal);
}
}
void Op::Add(const Operand* operand) {
assert(operand);
operands.push_back(operand);
}
void Op::Add(u32 integer) {
Sink(LiteralNumber::Create(integer));
}
void Op::Add(s32 integer) {
Sink(LiteralNumber::Create(integer));
}
void Op::Add(std::string string) {
Sink(std::make_unique<LiteralString>(std::move(string)));
}
void Op::Add(std::span<const Id> ids) {
assert(std::all_of(ids.begin(), ids.end(), [](auto id_) { return id_; }));
operands.insert(operands.end(), ids.begin(), ids.end());
}
u16 Op::CalculateTotalWords() const noexcept {
std::size_t count = 1;
if (result_type) {
++count;
}
if (id.has_value()) {
++count;
}
for (const Operand* operand : operands) {
count += operand->GetWordCount();
}
assert(count < std::numeric_limits<u16>::max());
return static_cast<u16>(count);
}
} // namespace Sirit

View file

@ -1,63 +0,0 @@
/* 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 <cstddef>
#include <optional>
#include <vector>
#include "common_types.h"
#include "operand.h"
#include "sirit/sirit.h"
#include "stream.h"
namespace Sirit {
class Op final : public Operand {
public:
explicit Op(spv::Op opcode, std::optional<u32> id = {}, Id result_type = nullptr);
~Op() override;
void Fetch(Stream& stream) const override;
std::size_t GetWordCount() const noexcept override;
bool Equal(const Operand& other) const noexcept override;
void Write(Stream& stream) const;
void Sink(std::unique_ptr<Operand> operand);
void Add(const Literal& literal);
void Add(std::span<const Literal> literals);
void Add(const Operand* operand);
void Add(u32 integer);
void Add(s32 integer);
void Add(std::string string);
void Add(std::span<const Id> ids);
private:
u16 CalculateTotalWords() const noexcept;
spv::Op opcode;
Id result_type;
std::optional<u32> id;
std::vector<const Operand*> operands;
std::vector<std::unique_ptr<Operand>> operand_store;
};
} // namespace Sirit

View file

@ -1,16 +0,0 @@
/* 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 <cassert>
#include "operand.h"
namespace Sirit {
Operand::Operand(OperandType operand_type_) : operand_type{operand_type_} {}
Operand::~Operand() = default;
} // namespace Sirit

View file

@ -1,36 +0,0 @@
/* 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 <cstddef>
#include "stream.h"
namespace Sirit {
enum class OperandType { Invalid, Op, Number, String };
class Operand {
public:
explicit Operand(OperandType operand_type);
virtual ~Operand();
virtual void Fetch(Stream& stream) const = 0;
virtual std::size_t GetWordCount() const noexcept = 0;
virtual bool Equal(const Operand& other) const noexcept = 0;
bool EqualType(const Operand& other) const noexcept {
return operand_type == other.operand_type;
}
private:
OperandType operand_type;
};
} // namespace Sirit

View file

@ -4,66 +4,66 @@
* 3-Clause BSD License
*/
#include <algorithm>
#include <cassert>
#include "common_types.h"
#include "op.h"
#include "sirit/sirit.h"
#include "common_types.h"
#include "stream.h"
namespace Sirit {
template <typename T>
static void WriteSet(Stream& stream, const T& set) {
for (const auto& item : set) {
item->Write(stream);
}
constexpr u32 MakeWord0(spv::Op op, size_t word_count) {
return static_cast<u32>(op) | static_cast<u32>(word_count) << 16;
}
Module::Module(u32 version_) : version{version_} {}
Module::Module(u32 version_)
: version{version_}, ext_inst_imports{std::make_unique<Stream>(&bound)},
entry_points{std::make_unique<Stream>(&bound)},
execution_modes{std::make_unique<Stream>(&bound)}, debug{std::make_unique<Stream>(&bound)},
annotations{std::make_unique<Stream>(&bound)}, declarations{std::make_unique<Declarations>(
&bound)},
global_variables{std::make_unique<Stream>(&bound)}, code{std::make_unique<Stream>(&bound)} {}
Module::~Module() = default;
std::vector<u32> Module::Assemble() const {
std::vector<u32> bytes;
Stream stream{bytes};
std::vector<u32> words = {spv::MagicNumber, version, GENERATOR_MAGIC_NUMBER, bound + 1, 0};
const auto insert = [&words](std::span<const u32> input) {
words.insert(words.end(), input.begin(), input.end());
};
stream.Write(spv::MagicNumber);
stream.Write(version);
stream.Write(GENERATOR_MAGIC_NUMBER);
stream.Write(bound);
stream.Write(static_cast<u32>(0));
for (const auto capability : capabilities) {
Op op(spv::Op::OpCapability);
op.Add(static_cast<u32>(capability));
op.Write(stream);
words.reserve(words.size() + capabilities.size() * 2);
for (const spv::Capability capability : capabilities) {
insert(std::array{
MakeWord0(spv::Op::OpCapability, 2),
static_cast<u32>(capability),
});
}
for (const auto& extension_name : extensions) {
Op op(spv::Op::OpExtension);
op.Add(extension_name);
op.Write(stream);
for (const std::string_view extension_name : extensions) {
size_t insert_index = words.size();
words.resize(words.size() + WordsInString(extension_name));
InsertStringView(words, insert_index, extension_name);
}
if (glsl_std_450) {
glsl_std_450->Write(stream);
}
insert(ext_inst_imports->Words());
Op memory_model_ref{spv::Op::OpMemoryModel};
memory_model_ref.Add(static_cast<u32>(addressing_model));
memory_model_ref.Add(static_cast<u32>(memory_model));
memory_model_ref.Write(stream);
insert(std::array{
MakeWord0(spv::Op::OpMemoryModel, 3),
static_cast<u32>(addressing_model),
static_cast<u32>(memory_model),
});
WriteSet(stream, entry_points);
WriteSet(stream, execution_modes);
WriteSet(stream, debug);
WriteSet(stream, annotations);
WriteSet(stream, declarations);
WriteSet(stream, global_variables);
WriteSet(stream, code);
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 bytes;
return words;
}
void Module::AddExtension(std::string extension_name) {
@ -76,75 +76,51 @@ void Module::AddCapability(spv::Capability capability) {
void Module::SetMemoryModel(spv::AddressingModel addressing_model_,
spv::MemoryModel memory_model_) {
this->addressing_model = addressing_model_;
this->memory_model = memory_model_;
addressing_model = addressing_model_;
memory_model = memory_model_;
}
void Module::AddEntryPoint(spv::ExecutionModel execution_model, Id entry_point, std::string name,
std::span<const Id> interfaces) {
auto op{std::make_unique<Op>(spv::Op::OpEntryPoint)};
op->Add(static_cast<u32>(execution_model));
op->Add(entry_point);
op->Add(std::move(name));
op->Add(interfaces);
entry_points.push_back(std::move(op));
void Module::AddEntryPoint(spv::ExecutionModel execution_model, Id entry_point,
std::string_view name, std::span<const Id> 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<const Literal> literals) {
auto op{std::make_unique<Op>(spv::Op::OpExecutionMode)};
op->Add(entry_point);
op->Add(static_cast<u32>(mode));
op->Add(literals);
execution_modes.push_back(std::move(op));
execution_modes->Reserve(3 + literals.size());
*execution_modes << spv::Op::OpExecutionMode << entry_point << mode << literals << EndOp{};
}
Id Module::AddLabel(Id label) {
assert(label != nullptr);
return code.emplace_back(label);
assert(label.value != 0);
code->Reserve(2);
*code << MakeWord0(spv::Op::OpLabel, 2) << label.value;
return label;
}
Id Module::AddLocalVariable(Id variable) {
assert(variable != nullptr);
return code.emplace_back(variable);
Id Module::AddLocalVariable(Id result_type, spv::StorageClass storage_class,
std::optional<Id> initializer) {
code->Reserve(5);
return *code << OpId{spv::Op::OpVariable, result_type} << storage_class << initializer
<< EndOp{};
}
Id Module::AddGlobalVariable(Id variable) {
assert(variable);
return global_variables.emplace_back(variable);
}
Id Module::AddCode(std::unique_ptr<Op> op) {
const Id id = code_store.emplace_back(std::move(op)).get();
return code.emplace_back(id);
}
Id Module::AddCode(spv::Op opcode, std::optional<u32> id) {
return AddCode(std::make_unique<Op>(opcode, id));
}
Id Module::AddDeclaration(std::unique_ptr<Op> op) {
const auto found = std::find_if(declarations.begin(), declarations.end(),
[&op](const auto& other) { return op->Equal(*other); });
if (found != declarations.end()) {
return found->get();
}
const auto id = op.get();
declarations.push_back(std::move(op));
++bound;
return id;
}
void Module::AddAnnotation(std::unique_ptr<Op> op) {
annotations.push_back(std::move(op));
Id Module::AddGlobalVariable(Id result_type, spv::StorageClass storage_class,
std::optional<Id> initializer) {
code->Reserve(5);
return *code << OpId{spv::Op::OpVariable, result_type} << storage_class << initializer
<< EndOp{};
}
Id Module::GetGLSLstd450() {
if (!glsl_std_450) {
glsl_std_450 = std::make_unique<Op>(spv::Op::OpExtInstImport, bound++);
glsl_std_450->Add("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.get();
return *glsl_std_450;
}
} // namespace Sirit

View file

@ -1,51 +0,0 @@
/* 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 "stream.h"
namespace Sirit {
Stream::Stream(std::vector<u32>& words_) : words{words_} {}
Stream::~Stream() = default;
void Stream::Write(std::string_view string) {
constexpr std::size_t word_size = 4;
const auto size = string.size();
const auto read = [string, size](std::size_t offset) {
return offset < size ? static_cast<u8>(string[offset]) : u8(0);
};
words.reserve(words.size() + size / word_size + 1);
for (std::size_t i = 0; i < size; i += word_size) {
Write(read(i), read(i + 1), read(i + 2), read(i + 3));
}
if (size % word_size == 0) {
Write(u32(0));
}
}
void Stream::Write(u64 value) {
const u32 dword[] = {static_cast<u32>(value), static_cast<u32>(value >> 32)};
words.insert(std::begin(words), std::cbegin(dword), std::cend(dword));
}
void Stream::Write(u32 value) {
words.push_back(value);
}
void Stream::Write(u16 first, u16 second) {
const u32 word = static_cast<u32>(first) | static_cast<u32>(second) << 16;
Write(word);
}
void Stream::Write(u8 first, u8 second, u8 third, u8 fourth) {
const u32 word = static_cast<u32>(first) | static_cast<u32>(second) << 8 |
static_cast<u32>(third) << 16 | static_cast<u32>(fourth) << 24;
Write(word);
}
} // namespace Sirit

View file

@ -6,29 +6,219 @@
#pragma once
#include <bit>
#include <concepts>
#include <cstddef>
#include <functional>
#include <string_view>
#include <unordered_map>
#include <variant>
#include <vector>
#include <spirv/unified1/spirv.hpp>
#include "common_types.h"
namespace Sirit {
class Declarations;
struct OpId {
spv::Op opcode;
Id result_type;
};
struct EndOp {};
constexpr size_t WordsInString(std::string_view string) {
return string.size() / sizeof(u32);
}
inline void InsertStringView(std::vector<u32>& 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<u8>(string[offset]) : u8(0);
};
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(std::vector<u32>& words);
~Stream();
explicit Stream(u32* bound_) : bound{bound_} {}
void Write(std::string_view string);
void Reserve(size_t num_words) {
if (insert_index + num_words <= words.size()) {
return;
}
words.resize(insert_index + num_words);
}
void Write(u64 value);
std::span<const u32> Words() const noexcept {
return std::span(words.data(), insert_index);
}
void Write(u32 value);
Stream& operator<<(spv::Op op) {
op_index = insert_index;
words[insert_index++] = static_cast<u32>(op);
return *this;
}
void Write(u16 first, u16 second);
Stream& operator<<(OpId op) {
op_index = insert_index;
words[insert_index++] = static_cast<u32>(op.opcode);
if (op.result_type.value != 0) {
words[insert_index++] = op.result_type.value;
}
words[insert_index++] = ++*bound;
return *this;
}
void Write(u8 first, u8 second, u8 third, u8 fourth);
Id operator<<(EndOp) {
const size_t num_words = insert_index - op_index;
words[op_index] |= static_cast<u32>(num_words) << 16;
return Id{*bound};
}
Stream& operator<<(u32 value) {
words[insert_index++] = value;
return *this;
}
Stream& operator<<(s32 value) {
return *this << static_cast<u32>(value);
}
Stream& operator<<(u64 value) {
return *this << static_cast<u32>(value) << static_cast<u32>(value >> 32);
}
Stream& operator<<(s64 value) {
return *this << static_cast<u64>(value);
}
Stream& operator<<(float value) {
return *this << std::bit_cast<u32>(value);
}
Stream& operator<<(double value) {
return *this << std::bit_cast<u64>(value);
}
Stream& operator<<(bool value) {
return *this << static_cast<u32>(value ? 1 : 0);
}
Stream& operator<<(Id value) {
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;
}
template <typename T>
requires std::is_enum_v<T> Stream& operator<<(T value) {
static_assert(sizeof(T) == sizeof(u32));
return *this << static_cast<u32>(value);
}
template <typename T>
Stream& operator<<(std::optional<T> value) {
if (value) {
*this << *value;
}
return *this;
}
template <typename T>
Stream& operator<<(std::span<const T> values) {
for (const auto& value : values) {
*this << value;
}
return *this;
}
private:
std::vector<u32>& words;
u32* bound = nullptr;
std::vector<u32> 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<const u32> Words() const noexcept {
return stream.Words();
}
template <typename T>
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.begin();
std::vector<u32> 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<u32>& vector) const noexcept {
size_t hash = std::hash<size_t>{}(vector.size());
for (const u32 value : vector) {
hash ^= std::hash<u32>{}(value);
}
return hash;
}
};
Stream stream;
std::unordered_map<std::vector<u32>, u32, HashVector> existing_declarations;
size_t id_index = 0;
};
} // namespace Sirit

View file

@ -7,6 +7,7 @@
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sirit/sirit.h>
class MyModule : public Sirit::Module {
@ -31,9 +32,9 @@ public:
const auto gl_per_vertex_ptr =
Name(TypePointer(spv::StorageClass::Output, gl_per_vertex), "out_gl_PerVertex");
const auto in_pos = Name(OpVariable(in_float4, spv::StorageClass::Input), "in_pos");
const auto in_pos = Name(AddGlobalVariable(in_float4, spv::StorageClass::Input), "in_pos");
const auto per_vertex =
Name(OpVariable(gl_per_vertex_ptr, spv::StorageClass::Output), "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);
@ -41,9 +42,6 @@ public:
MemberDecorate(gl_per_vertex, 0, spv::Decoration::BuiltIn,
static_cast<std::uint32_t>(spv::BuiltIn::Position));
AddGlobalVariable(in_pos);
AddGlobalVariable(per_vertex);
const auto main_func = Name(
OpFunction(t_void, spv::FunctionControlMask::MaskNone, TypeFunction(t_void)), "main");
AddLabel();
@ -57,8 +55,8 @@ public:
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.f), tmp_position, 2);
tmp_position = OpCompositeInsert(float4, Constant(t_float, 1.f), tmp_position, 3);
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);
@ -123,7 +121,8 @@ static constexpr std::uint8_t expected_binary[] = {
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};
0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00,
};
int main(int argc, char** argv) {
MyModule module;