From 0b12c0b0dae13d5f8410d5b71ec43b5316dc2816 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Mon, 12 Jun 2017 00:58:44 -0700 Subject: [PATCH] working on generate_spirv_parser --- src/generate_spirv_parser/ast.h | 104 +++++++++++ src/generate_spirv_parser/generate.cpp | 248 +++++++++++++++++++++++++ src/generate_spirv_parser/generate.h | 162 ++++++++++++++++ src/spirv/CMakeLists.txt | 2 +- 4 files changed, 515 insertions(+), 1 deletion(-) diff --git a/src/generate_spirv_parser/ast.h b/src/generate_spirv_parser/ast.h index 3c9c3ed..3228746 100644 --- a/src/generate_spirv_parser/ast.h +++ b/src/generate_spirv_parser/ast.h @@ -45,6 +45,11 @@ struct Copyright { } json::ast::Value to_json() const; + template + void visit(Fn fn) const + { + fn(*this); + } }; struct Capabilities @@ -62,6 +67,11 @@ struct Capabilities return capabilities.empty(); } json::ast::Value to_json() const; + template + void visit(Fn fn) const + { + fn(*this); + } }; struct Extensions @@ -79,6 +89,11 @@ struct Extensions return extensions.empty(); } json::ast::Value to_json() const; + template + void visit(Fn fn) const + { + fn(*this); + } }; struct Instructions @@ -118,6 +133,11 @@ struct Instructions { } json::ast::Value to_json() const; + template + void visit(Fn fn) const + { + fn(*this); + } }; std::vector operands; Operands() : operands() @@ -132,6 +152,13 @@ struct Instructions return operands.empty(); } json::ast::Value to_json() const; + template + void visit(Fn fn) const + { + fn(*this); + for(auto &operand : operands) + operand.visit(fn); + } }; std::string opname; std::uint32_t opcode; @@ -147,6 +174,13 @@ struct Instructions { } json::ast::Value to_json() const; + template + void visit(Fn fn) const + { + fn(*this); + operands.visit(fn); + capabilities.visit(fn); + } }; std::vector instructions; explicit Instructions(std::vector instructions) noexcept @@ -154,6 +188,13 @@ struct Instructions { } json::ast::Value to_json() const; + template + void visit(Fn fn) const + { + fn(*this); + for(auto &instruction : instructions) + instruction.visit(fn); + } }; struct Operand_kinds @@ -210,6 +251,11 @@ struct Operand_kinds { } json::ast::Value to_json() const; + template + void visit(Fn fn) const + { + fn(*this); + } }; std::vector parameters; Parameters() : parameters() @@ -224,6 +270,13 @@ struct Operand_kinds { return parameters.empty(); } + template + void visit(Fn fn) const + { + fn(*this); + for(auto ¶meter : parameters) + parameter.visit(fn); + } }; Parameters parameters; Extensions extensions; @@ -239,6 +292,14 @@ struct Operand_kinds { } json::ast::Value to_json(bool is_bit_enumerant) const; + template + void visit(Fn fn) const + { + fn(*this); + capabilities.visit(fn); + parameters.visit(fn); + extensions.visit(fn); + } }; std::vector enumerants; explicit Enumerants(std::vector enumerants) noexcept : enumerants(enumerants) @@ -249,6 +310,13 @@ struct Operand_kinds { return to_json(category == Category::bit_enum); } + template + void visit(Fn fn) const + { + fn(*this); + for(auto &enumerant : enumerants) + enumerant.visit(fn); + } }; struct Doc { @@ -266,6 +334,11 @@ struct Operand_kinds { return to_json(); } + template + void visit(Fn fn) const + { + fn(*this); + } }; struct Bases { @@ -283,6 +356,11 @@ struct Operand_kinds { return to_json(); } + template + void visit(Fn fn) const + { + fn(*this); + } }; typedef util::variant Value; Value value; @@ -324,6 +402,17 @@ struct Operand_kinds { } json::ast::Value to_json() const; + template + void visit(Fn fn) const + { + fn(*this); + util::visit( + [&](auto &&value) + { + value.visit(fn); + }, + value); + } }; std::vector operand_kinds; explicit Operand_kinds(std::vector operand_kinds) noexcept @@ -331,6 +420,13 @@ struct Operand_kinds { } json::ast::Value to_json() const; + template + void visit(Fn fn) const + { + fn(*this); + for(auto &operand_kind : operand_kinds) + operand_kind.visit(fn); + } }; struct Top_level @@ -359,6 +455,14 @@ struct Top_level { } json::ast::Value to_json() const; + template + void visit(Fn fn) const + { + fn(*this); + copyright.visit(fn); + instructions.visit(fn); + operand_kinds.visit(fn); + } }; } } diff --git a/src/generate_spirv_parser/generate.cpp b/src/generate_spirv_parser/generate.cpp index 4b3f53d..b39259c 100644 --- a/src/generate_spirv_parser/generate.cpp +++ b/src/generate_spirv_parser/generate.cpp @@ -21,6 +21,9 @@ * */ #include "generate.h" +#include "../json/json.h" +#include +#include namespace vulkan_cpu { @@ -34,6 +37,7 @@ Generator::Generator_state::Generator_state(const Generator *generator, indent_level(0), full_output_file_name(generator_args.output_directory + "/" + generator->output_base_file_name), + guard_macro_name(get_guard_macro_name_from_file_name(full_output_file_name)), os() { os.exceptions(std::ios::badbit | std::ios::failbit); @@ -45,6 +49,44 @@ void Generator::Generator_state::open_output_file() } constexpr Generator::Indent_t Generator::indent; +constexpr const char *Generator::vulkan_cpu_namespace_name; +constexpr const char *Generator::spirv_namespace_name; +constexpr const char *Generator::spirv_namespace_names[]; + +std::string Generator::get_guard_macro_name_from_file_name(std::string file_name) +{ + auto retval = std::move(file_name); + for(char &ch : retval) + { + if(ch >= 'a' && ch <= 'z') + { + ch = ch - 'a' + 'A'; // convert to uppercase + continue; + } + if(ch >= 'A' && ch <= 'Z') + continue; + if(ch >= '0' && ch <= '9') + continue; + ch = '_'; + } + retval += '_'; + if(retval[0] >= '0' && retval[0] <= '9') + retval.insert(0, 1, '_'); + for(std::size_t double_underline_index = retval.find("__"); + double_underline_index != std::string::npos; + double_underline_index = retval.find("__", double_underline_index + 1)) + { + // insert a u in all pairs of underlines to prevent generating a reserved identifier + retval.insert(++double_underline_index, "u"); + } + if(retval.size() >= 2 && retval[0] == '_' && retval[1] >= 'A' && retval[1] <= 'Z') + { + // insert a u to prevent generating a reserved identifier: starting with an underline and a + // capital letter + retval.insert(1, "u"); + } + return retval; +} void Generator::write_indent(Generator_state &state) { @@ -83,17 +125,223 @@ void Generator::write_copyright_comment(Generator_state &state, const ast::Copyr state << " */\n"; } +void Generator::write_file_guard_start(Generator_state &state) +{ + state << "#ifdef " << state.guard_macro_name << "\n#define " << state.guard_macro_name + << "\n\n"; +} + +void Generator::write_file_guard_end(Generator_state &state) +{ + state << "#endif /* " << state.guard_macro_name << " */\n"; +} + +void Generator::write_namespace_start(Generator_state &state, const char *namespace_name) +{ + state << "namespace " << namespace_name << "\n{\n"; +} + +void Generator::write_namespace_start(Generator_state &state, const std::string &namespace_name) +{ + state << "namespace " << namespace_name << "\n{\n"; +} + +void Generator::write_namespace_end(Generator_state &state) +{ + state << "}\n"; +} + +void Generator::write_unsigned_integer_literal(Generator_state &state, + std::uint64_t value, + Integer_literal_base base, + std::size_t minimum_digit_count) +{ + constexpr std::uint64_t max_unsigned_value = std::numeric_limits::max(); + constexpr std::uint64_t max_unsigned_long_value = std::numeric_limits::max(); + auto literal_type = + value <= max_unsigned_value ? "U" : value <= max_unsigned_long_value ? "UL" : "ULL"; + auto number_prefix = ""; + unsigned base_as_number = 10; + switch(base) + { + case Integer_literal_base::dec: + minimum_digit_count = 1; + break; + case Integer_literal_base::hex: + base_as_number = 0x10; + number_prefix = "0x"; + break; + case Integer_literal_base::oct: + base_as_number = 010; + number_prefix = "0"; + break; + } + auto number_string = json::ast::Number_value::append_unsigned_integer_to_string( + value, number_prefix, base_as_number, minimum_digit_count) + + literal_type; + state << number_string; +} + +void Generator::write_signed_integer_literal(Generator_state &state, std::int64_t value) +{ + constexpr std::int64_t max_int_value = std::numeric_limits::max(); + constexpr std::int64_t min_int_value = std::numeric_limits::min(); + constexpr std::int64_t max_long_value = std::numeric_limits::max(); + constexpr std::int64_t min_long_value = std::numeric_limits::min(); + auto literal_type = ""; + if(value < min_int_value || value > max_int_value) + literal_type = "L"; + if(value < min_long_value || value > max_long_value) + literal_type = "LL"; + state << value << literal_type; +} + +struct Generator::Get_extensions_visitor +{ + std::unordered_set &retval; + constexpr Get_extensions_visitor(std::unordered_set &retval) noexcept + : retval(retval) + { + } + template + void operator()(const T &) + { + } + void operator()(const ast::Extensions &extensions) + { + for(auto &extension : extensions.extensions) + retval.insert(extension); + } +}; + +std::unordered_set Generator::get_extensions(const ast::Top_level &top_level) +{ + std::unordered_set retval; + top_level.visit(Get_extensions_visitor(retval)); + return retval; +} + struct Spirv_header_generator final : public Generator { Spirv_header_generator() : Generator("spirv.h") { } + enum class Enum_priority + { + default_priority = 0, + capability = 1, + }; + static Enum_priority get_enum_priority(const std::string &enum_name) noexcept + { + if(enum_name == "Capability") + return Enum_priority::capability; + return Enum_priority::default_priority; + } + static bool compare_enum_names(const std::string &l, const std::string &r) noexcept + { + auto l_priority = get_enum_priority(l); + auto r_priority = get_enum_priority(r); + if(l_priority > r_priority) + return true; // higher priority sorts first + if(l_priority < r_priority) + return false; + return l < r; + } virtual void run(Generator_args &generator_args, const ast::Top_level &top_level) const override { Generator_state state(this, generator_args); state.open_output_file(); write_file_comments(state, top_level.copyright); + write_file_guard_start(state); + state << "#include \n"; + state << "\n"; + write_namespaces_start(state, spirv_namespace_names); + state << "typedef std::uint32_t Word;\n"; + state << "constexpr Word magic_number = " + << unsigned_hex_integer_literal(top_level.magic_number, 8) << ";\n"; + state << "constexpr std::uint32_t major_version = " + << unsigned_dec_integer_literal(top_level.major_version) << ";\n"; + state << "constexpr std::uint32_t minor_version = " + << unsigned_dec_integer_literal(top_level.minor_version) << ";\n"; + state << "constexpr std::uint32_t revision = " + << unsigned_dec_integer_literal(top_level.revision) << ";\n"; + auto extensions_set = get_extensions(top_level); + std::vector extensions_list; + extensions_list.reserve(extensions_set.size()); + for(auto &extension : extensions_set) + extensions_list.push_back(extension); + std::sort(extensions_list.begin(), extensions_list.end()); + state << "\n" + "enum class Extension\n" + "{\n"; + { + auto push_indent = state.pushed_indent(); + for(auto &extension : extensions_list) + state << indent << extension << ",\n"; + } + state << "};\n" + "\n" + "constexpr const char *get_extension_name(Extension extension) noexcept\n" + "{\n"; + { + auto push_indent = state.pushed_indent(); + state << indent << "switch(extension)\n" << indent << "{\n"; + for(auto &extension : extensions_list) + { + state << indent << "case Extension::" << extension << ":\n"; + auto push_indent2 = state.pushed_indent(); + state << indent << "return \"" << extension << "\";\n"; + } + state << indent << "}\n" << indent << "return \"\";\n"; + } + state << "}\n"; + std::vector operand_kinds; + operand_kinds.reserve(top_level.operand_kinds.operand_kinds.size()); + for(auto &operand_kind : top_level.operand_kinds.operand_kinds) + operand_kinds.push_back(&operand_kind); + std::sort( + operand_kinds.begin(), + operand_kinds.end(), + [](const ast::Operand_kinds::Operand_kind *a, const ast::Operand_kinds::Operand_kind *b) + { + return compare_enum_names(a->kind, b->kind); + }); + for(auto *operand_kind : operand_kinds) + { + switch(operand_kind->category) + { + case ast::Operand_kinds::Operand_kind::Category::value_enum: + case ast::Operand_kinds::Operand_kind::Category::bit_enum: + { + auto &enumerants = + util::get(operand_kind->value); + state << "\n" + "enum class " + << operand_kind->kind << " : Word\n" + "{\n"; + auto push_indent = state.pushed_indent(); + for(auto &enumerant : enumerants.enumerants) + { + state << indent << enumerant.enumerant << " = "; + if(operand_kind->category + == ast::Operand_kinds::Operand_kind::Category::bit_enum) + state << unsigned_hex_integer_literal(enumerant.value); + else + state << unsigned_dec_integer_literal(enumerant.value); + state << ",\n"; + } + push_indent.finish(); + state << "};\n"; + break; + } +#warning finish + } + } + + #warning finish + write_namespaces_end(state, spirv_namespace_names); + write_file_guard_end(state); } }; diff --git a/src/generate_spirv_parser/generate.h b/src/generate_spirv_parser/generate.h index 1663bd2..50c816a 100644 --- a/src/generate_spirv_parser/generate.h +++ b/src/generate_spirv_parser/generate.h @@ -30,6 +30,8 @@ #include #include #include +#include +#include namespace vulkan_cpu { @@ -54,11 +56,13 @@ public: }; protected: + class Push_indent; struct Generator_state { Generator_args &generator_args; std::size_t indent_level; std::string full_output_file_name; + std::string guard_macro_name; std::ofstream os; explicit Generator_state(const Generator *generator, Generator_args &generator_args); void open_output_file(); @@ -68,6 +72,7 @@ protected: os << std::forward(v); return *this; } + Push_indent pushed_indent() noexcept; }; class Push_indent final { @@ -108,11 +113,68 @@ protected: } }; static constexpr Indent_t indent{}; + enum class Integer_literal_base + { + dec = 0, + hex, + oct + }; + struct Unsigned_integer_literal + { + std::uint64_t value; + Integer_literal_base base; + std::size_t minimum_digit_count; + constexpr Unsigned_integer_literal(std::uint64_t value, + Integer_literal_base base, + std::size_t minimum_digit_count = 1) noexcept + : value(value), + base(base), + minimum_digit_count(minimum_digit_count) + { + } + friend Generator_state &operator<<(Generator_state &state, Unsigned_integer_literal v) + { + write_unsigned_integer_literal(state, v.value, v.base, v.minimum_digit_count); + return state; + } + }; + static constexpr Unsigned_integer_literal unsigned_dec_integer_literal( + std::uint64_t value) noexcept + { + return Unsigned_integer_literal(value, Integer_literal_base::dec); + } + static constexpr Unsigned_integer_literal unsigned_hex_integer_literal( + std::uint64_t value, std::size_t minimum_digit_count = 1) noexcept + { + return Unsigned_integer_literal(value, Integer_literal_base::hex, minimum_digit_count); + } + static constexpr Unsigned_integer_literal unsigned_oct_integer_literal( + std::uint64_t value, std::size_t minimum_digit_count = 1) noexcept + { + return Unsigned_integer_literal(value, Integer_literal_base::oct, minimum_digit_count); + } + struct Signed_integer_literal + { + std::int64_t value; + constexpr explicit Signed_integer_literal(std::int64_t value) noexcept : value(value) + { + } + friend Generator_state &operator<<(Generator_state &state, Signed_integer_literal v) + { + write_signed_integer_literal(state, v.value); + return state; + } + }; + static constexpr Signed_integer_literal signed_integer_literal(std::int64_t value) noexcept + { + return Signed_integer_literal(value); + } protected: const char *const output_base_file_name; protected: + static std::string get_guard_macro_name_from_file_name(std::string file_name); static void write_indent(Generator_state &state); static void write_automatically_generated_file_warning(Generator_state &state); static void write_copyright_comment(Generator_state &state, const ast::Copyright ©right); @@ -121,6 +183,101 @@ protected: write_automatically_generated_file_warning(state); write_copyright_comment(state, copyright); } + static void write_file_guard_start(Generator_state &state); + static void write_file_guard_end(Generator_state &state); + static void write_namespace_start(Generator_state &state, const char *namespace_name); + static void write_namespace_start(Generator_state &state, const std::string &namespace_name); + +private: + static void write_namespace_end(Generator_state &state); + +protected: + static void write_namespace_end(Generator_state &state, const char *namespace_name) + { + write_namespace_end(state); + } + static void write_namespace_end(Generator_state &state, const std::string &namespace_name) + { + write_namespace_end(state); + } + static void write_namespaces_start(Generator_state &state, + const char *const *namespace_names, + std::size_t namespace_name_count) + { + for(std::size_t i = 0; i < namespace_name_count; i++) + write_namespace_start(state, namespace_names[i]); + } + static void write_namespaces_start(Generator_state &state, + const std::string *namespace_names, + std::size_t namespace_name_count) + { + for(std::size_t i = 0; i < namespace_name_count; i++) + write_namespace_start(state, namespace_names[i]); + } + static void write_namespaces_end(Generator_state &state, + const char *const *namespace_names, + std::size_t namespace_name_count) + { + for(std::size_t i = 0; i < namespace_name_count; i++) + write_namespace_end(state, namespace_names[namespace_name_count - i - 1]); + state << '\n'; + } + static void write_namespaces_end(Generator_state &state, + const std::string *namespace_names, + std::size_t namespace_name_count) + { + for(std::size_t i = 0; i < namespace_name_count; i++) + write_namespace_end(state, namespace_names[namespace_name_count - i - 1]); + state << '\n'; + } + template + static void write_namespaces_start(Generator_state &state, const T(&namespace_names)[N]) + { + write_namespaces_start(state, namespace_names, N); + } + template + static void write_namespaces_end(Generator_state &state, const T(&namespace_names)[N]) + { + write_namespaces_end(state, namespace_names, N); + } + static void write_namespaces_start(Generator_state &state, + std::initializer_list namespace_names) + { + write_namespaces_start(state, namespace_names.begin(), namespace_names.size()); + } + static void write_namespaces_start(Generator_state &state, + std::initializer_list namespace_names) + { + write_namespaces_start(state, namespace_names.begin(), namespace_names.size()); + } + static void write_namespaces_end(Generator_state &state, + std::initializer_list namespace_names) + { + write_namespaces_end(state, namespace_names.begin(), namespace_names.size()); + } + static void write_namespaces_end(Generator_state &state, + std::initializer_list namespace_names) + { + write_namespaces_end(state, namespace_names.begin(), namespace_names.size()); + } + static void write_unsigned_integer_literal(Generator_state &state, + std::uint64_t value, + Integer_literal_base base, + std::size_t minimum_digit_count); + static void write_signed_integer_literal(Generator_state &state, std::int64_t value); + +private: + struct Get_extensions_visitor; + +protected: + static std::unordered_set get_extensions(const ast::Top_level &top_level); + +protected: + static constexpr const char *vulkan_cpu_namespace_name = "vulkan_cpu"; + static constexpr const char *spirv_namespace_name = "spirv"; + static constexpr const char *spirv_namespace_names[] = { + vulkan_cpu_namespace_name, spirv_namespace_name, + }; public: explicit Generator(const char *output_base_file_name) noexcept @@ -137,6 +294,11 @@ public: virtual ~Generator() = default; }; +inline Generator::Push_indent Generator::Generator_state::pushed_indent() noexcept +{ + return Push_indent(*this); +} + struct Spirv_header_generator; struct Generators diff --git a/src/spirv/CMakeLists.txt b/src/spirv/CMakeLists.txt index 4ea7ac2..552aee9 100644 --- a/src/spirv/CMakeLists.txt +++ b/src/spirv/CMakeLists.txt @@ -34,7 +34,7 @@ if(${do_generate_spirv_parser}) add_custom_command(OUTPUT ${spirv_parser_sources} ${spirv_parser_headers} COMMAND ${CMAKE_COMMAND} -E make_directory ${spirv_parser_generated_dir} - COMMAND ${CMAKE_COMMAND} -E chdir ${spirv_parser_generated_dir} $ ${spirv_core_grammar_json} ${spirv_parser_generated_dir} + COMMAND ${CMAKE_COMMAND} -E chdir ${spirv_parser_generated_include_dir} $ ${spirv_core_grammar_json} spirv MAIN_DEPENDENCY ${spirv_core_grammar_json} DEPENDS $ VERBATIM -- 2.30.2