working on generate_spirv_parser
authorJacob Lifshay <programmerjake@gmail.com>
Mon, 12 Jun 2017 07:58:44 +0000 (00:58 -0700)
committerJacob Lifshay <programmerjake@gmail.com>
Mon, 12 Jun 2017 07:58:44 +0000 (00:58 -0700)
src/generate_spirv_parser/ast.h
src/generate_spirv_parser/generate.cpp
src/generate_spirv_parser/generate.h
src/spirv/CMakeLists.txt

index 3c9c3ed8dba00bf61604a3965bbdb85174900f9a..3228746941f1e21e0ecf0e37f6d795444e043c91 100644 (file)
@@ -45,6 +45,11 @@ struct Copyright
     {
     }
     json::ast::Value to_json() const;
+    template <typename Fn>
+    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 <typename Fn>
+    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 <typename Fn>
+    void visit(Fn fn) const
+    {
+        fn(*this);
+    }
 };
 
 struct Instructions
@@ -118,6 +133,11 @@ struct Instructions
                 {
                 }
                 json::ast::Value to_json() const;
+                template <typename Fn>
+                void visit(Fn fn) const
+                {
+                    fn(*this);
+                }
             };
             std::vector<Operand> operands;
             Operands() : operands()
@@ -132,6 +152,13 @@ struct Instructions
                 return operands.empty();
             }
             json::ast::Value to_json() const;
+            template <typename Fn>
+            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 <typename Fn>
+        void visit(Fn fn) const
+        {
+            fn(*this);
+            operands.visit(fn);
+            capabilities.visit(fn);
+        }
     };
     std::vector<Instruction> instructions;
     explicit Instructions(std::vector<Instruction> instructions) noexcept
@@ -154,6 +188,13 @@ struct Instructions
     {
     }
     json::ast::Value to_json() const;
+    template <typename Fn>
+    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 <typename Fn>
+                        void visit(Fn fn) const
+                        {
+                            fn(*this);
+                        }
                     };
                     std::vector<Parameter> parameters;
                     Parameters() : parameters()
@@ -224,6 +270,13 @@ struct Operand_kinds
                     {
                         return parameters.empty();
                     }
+                    template <typename Fn>
+                    void visit(Fn fn) const
+                    {
+                        fn(*this);
+                        for(auto &parameter : 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 <typename Fn>
+                void visit(Fn fn) const
+                {
+                    fn(*this);
+                    capabilities.visit(fn);
+                    parameters.visit(fn);
+                    extensions.visit(fn);
+                }
             };
             std::vector<Enumerant> enumerants;
             explicit Enumerants(std::vector<Enumerant> enumerants) noexcept : enumerants(enumerants)
@@ -249,6 +310,13 @@ struct Operand_kinds
             {
                 return to_json(category == Category::bit_enum);
             }
+            template <typename Fn>
+            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 <typename Fn>
+            void visit(Fn fn) const
+            {
+                fn(*this);
+            }
         };
         struct Bases
         {
@@ -283,6 +356,11 @@ struct Operand_kinds
             {
                 return to_json();
             }
+            template <typename Fn>
+            void visit(Fn fn) const
+            {
+                fn(*this);
+            }
         };
         typedef util::variant<Enumerants, Doc, Bases> Value;
         Value value;
@@ -324,6 +402,17 @@ struct Operand_kinds
         {
         }
         json::ast::Value to_json() const;
+        template <typename Fn>
+        void visit(Fn fn) const
+        {
+            fn(*this);
+            util::visit(
+                [&](auto &&value)
+                {
+                    value.visit(fn);
+                },
+                value);
+        }
     };
     std::vector<Operand_kind> operand_kinds;
     explicit Operand_kinds(std::vector<Operand_kind> operand_kinds) noexcept
@@ -331,6 +420,13 @@ struct Operand_kinds
     {
     }
     json::ast::Value to_json() const;
+    template <typename Fn>
+    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 <typename Fn>
+    void visit(Fn fn) const
+    {
+        fn(*this);
+        copyright.visit(fn);
+        instructions.visit(fn);
+        operand_kinds.visit(fn);
+    }
 };
 }
 }
index 4b3f53d1a6ce132e70c9718875bf681a4062753c..b39259c26981ce4aa05c7f6305748df612cbc8d4 100644 (file)
@@ -21,6 +21,9 @@
  *
  */
 #include "generate.h"
+#include "../json/json.h"
+#include <limits>
+#include <algorithm>
 
 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<std::uint16_t>::max();
+    constexpr std::uint64_t max_unsigned_long_value = std::numeric_limits<std::uint32_t>::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<std::int16_t>::max();
+    constexpr std::int64_t min_int_value = std::numeric_limits<std::int16_t>::min();
+    constexpr std::int64_t max_long_value = std::numeric_limits<std::int32_t>::max();
+    constexpr std::int64_t min_long_value = std::numeric_limits<std::int32_t>::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<std::string> &retval;
+    constexpr Get_extensions_visitor(std::unordered_set<std::string> &retval) noexcept
+        : retval(retval)
+    {
+    }
+    template <typename T>
+    void operator()(const T &)
+    {
+    }
+    void operator()(const ast::Extensions &extensions)
+    {
+        for(auto &extension : extensions.extensions)
+            retval.insert(extension);
+    }
+};
+
+std::unordered_set<std::string> Generator::get_extensions(const ast::Top_level &top_level)
+{
+    std::unordered_set<std::string> 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 <cstdint>\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<std::string> 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<const ast::Operand_kinds::Operand_kind *> 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<ast::Operand_kinds::Operand_kind::Enumerants>(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);
     }
 };
 
index 1663bd2d3689597353161cc2317b485280ccc903..50c816acc51002dde46179610fc76da45358a570 100644 (file)
@@ -30,6 +30,8 @@
 #include <string>
 #include <cassert>
 #include <type_traits>
+#include <cstdint>
+#include <unordered_set>
 
 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<T>(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 &copyright);
@@ -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 <typename T, std::size_t N>
+    static void write_namespaces_start(Generator_state &state, const T(&namespace_names)[N])
+    {
+        write_namespaces_start(state, namespace_names, N);
+    }
+    template <typename T, std::size_t N>
+    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<std::string> namespace_names)
+    {
+        write_namespaces_start(state, namespace_names.begin(), namespace_names.size());
+    }
+    static void write_namespaces_start(Generator_state &state,
+                                       std::initializer_list<const char *> namespace_names)
+    {
+        write_namespaces_start(state, namespace_names.begin(), namespace_names.size());
+    }
+    static void write_namespaces_end(Generator_state &state,
+                                     std::initializer_list<std::string> namespace_names)
+    {
+        write_namespaces_end(state, namespace_names.begin(), namespace_names.size());
+    }
+    static void write_namespaces_end(Generator_state &state,
+                                     std::initializer_list<const char *> 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<std::string> 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
index 4ea7ac20be18b469fd3d96938e374552fe20522f..552aee920703b9c7c26e5eceeb4c6e3e0724add8 100644 (file)
@@ -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} $<TARGET_FILE:generate_spirv_parser> ${spirv_core_grammar_json} ${spirv_parser_generated_dir}
+                       COMMAND ${CMAKE_COMMAND} -E chdir ${spirv_parser_generated_include_dir} $<TARGET_FILE:generate_spirv_parser> ${spirv_core_grammar_json} spirv
                        MAIN_DEPENDENCY ${spirv_core_grammar_json}
                        DEPENDS $<TARGET_FILE:generate_spirv_parser>
                        VERBATIM