/* spirv_grammar.c SPIR-V grammar json embedding Copyright (C) 2024 Bill Currie Author: Bill Currie This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to: Free Software Foundation, Inc. 59 Temple Place - Suite 330 Boston, MA 02111-1307, USA */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifdef HAVE_STRING_H # include #endif #ifdef HAVE_STRINGS_H # include #endif #include #include "QF/heapsort.h" #include "QF/plist.h" #include "QF/sys.h" #include "QF/va.h" #include "tools/qfcc/include/qfcc.h" #include "tools/qfcc/include/diagnostic.h" #include "tools/qfcc/include/expr.h" #include "tools/qfcc/include/options.h" #include "tools/qfcc/include/shared.h" #include "tools/qfcc/include/strpool.h" #include "tools/qfcc/include/struct.h" #include "tools/qfcc/include/symtab.h" #include "tools/qfcc/include/spirv.h" #include "tools/qfcc/include/spirv_grammar.h" #include "tools/qfcc/include/type.h" #include "tools/qfcc/include/value.h" typedef struct spirv_json_s { struct spirv_json_s *parent; const char *name; const char *json; spirv_grammar_t *grammar; } spirv_json_t; static spirv_json_t builtin_json[] = { { .name = "core", .json = #include "tools/qfcc/source/spirv.core.grammar.jinc" }, { .parent = &builtin_json[0], .name = "GLSL.std.450", .json = #include "tools/qfcc/source/extinst.glsl.std.450.grammar.jinc" }, {} }; static void * spvg_alloc (void *data, size_t size) { return malloc (size); } typedef struct parse_string_s { size_t value_offset; } parse_string_t; typedef struct parse_array_s { pltype_t type; size_t stride; plparser_t parser; void *data; size_t value_offset; size_t size_offset; __compar_fn_t cmp; } parse_array_t; static int parse_char (const plfield_t *field, const plitem_t *item, void *data, plitem_t *messages, void *context) { auto value = (char *) data; const char *str = PL_String (item); *value = *str; return 1; } static int parse_hex (const plfield_t *field, const plitem_t *item, void *data, plitem_t *messages, void *context) { auto value = (uint32_t *) data; const char *str = PL_String (item); char *end = 0; *value = strtol (str, &end, 0); if (*end) { PL_Message (messages, item, "error parsing %s: %s", field->name, str); } return !*end; } static int parse_uint32_t (const plfield_t *field, const plitem_t *item, void *data, plitem_t *messages, void *context) { auto value = (uint32_t *) data; *value = PL_Number (item); return 1; } static int parse_value (const plfield_t *field, const plitem_t *item, void *data, plitem_t *messages, void *context) { if (PL_Type (item) == QFString) { return parse_hex (field, item, data, messages, context); } else { return parse_uint32_t (field, item, data, messages, context); } } static parse_string_t parse_string_array = { 0 }; static int parse_string (const plfield_t *field, const plitem_t *item, void *data, plitem_t *messages, void *context) { auto string = (parse_string_t *) field->data; auto value = (const char **) ((byte *)data + string->value_offset); const char *str = PL_String (item); *value = save_string (str); return 1; } static int parse_array (const plfield_t *field, const plitem_t *item, void *data, plitem_t *messages, void *context) { auto array = (parse_array_t *) field->data; auto value = (void **) ((byte *)data + array->value_offset); auto size = (uint32_t *) ((byte *)data + array->size_offset); plelement_t element = { array->type, array->stride, spvg_alloc, array->parser, array->data, }; plfield_t f = { 0, 0, 0, 0, &element }; typedef struct arr_s DARRAY_TYPE(byte) arr_t; arr_t *arr; if (!PL_ParseArray (&f, item, &arr, messages, context)) { return 0; } *value = arr->a; if ((void *) size >= data) { *size = arr->size; } if (array->cmp) { heapsort (*value, *size, array->stride, array->cmp); } return 1; } static int parse_struct (const plfield_t *field, const plitem_t *dict, void *data, plitem_t *messages, void *context) { return PL_ParseStruct (field->data, dict, data, messages, context); } static int parse_ignore (const plfield_t *field, const plitem_t *item, void *data, plitem_t *messages, void *context) { return 1; } static int spirv_enumerant_cmp (const void *_a, const void *_b) { auto a = (const spirv_enumerant_t *) _a; auto b = (const spirv_enumerant_t *) _b; return strcmp (a->enumerant, b->enumerant); } static int spirv_instruction_cmp (const void *_a, const void *_b) { auto a = (const spirv_instruction_t *) _a; auto b = (const spirv_instruction_t *) _b; return strcmp (a->opname, b->opname); } static int spirv_kind_cmp (const void *_a, const void *_b) { auto a = (const spirv_kind_t *) _a; auto b = (const spirv_kind_t *) _b; return strcmp (a->kind, b->kind); } static plfield_t spirv_operand_fields[] = { {"kind", offsetof (spirv_operand_t, kind), QFString, parse_string, &parse_string_array}, {"quantifier", offsetof (spirv_operand_t, quantifier), QFString, parse_char, nullptr}, {"name", offsetof (spirv_operand_t, name), QFString, parse_string, &parse_string_array}, { } }; static parse_array_t parse_ecapability_data = { .type = QFString, .stride = sizeof (const char *), .parser = parse_string, .data = &parse_string_array, .value_offset = offsetof (spirv_enumerant_t, capabilities), .size_offset = offsetof (spirv_enumerant_t, num_capabilities), }; static parse_array_t parse_eextension_data = { .type = QFString, .stride = sizeof (const char *), .parser = parse_string, .data = &parse_string_array, .value_offset = offsetof (spirv_enumerant_t, extensions), .size_offset = offsetof (spirv_enumerant_t, num_extensions), }; static parse_array_t parse_parameter_data = { .type = QFDictionary, .stride = sizeof (spirv_operand_t), .parser = parse_struct, .data = &spirv_operand_fields, .value_offset = offsetof (spirv_enumerant_t, parameters), .size_offset = offsetof (spirv_enumerant_t, num_parameters), }; static plfield_t spirv_enumerant_fields[] = { {"enumerant", offsetof (spirv_enumerant_t, enumerant), QFString, parse_string, &parse_string_array}, {"value", offsetof (spirv_enumerant_t, value), QFMultiType | (1 << QFString) | (1 << QFNumber), parse_value, nullptr}, {"capabilities", 0, QFArray, parse_array, &parse_ecapability_data}, {"extensions", 0, QFArray, parse_array, &parse_eextension_data}, {"parameters", 0, QFArray, parse_array, &parse_parameter_data}, {"version", offsetof (spirv_enumerant_t, version), QFString, parse_string, &parse_string_array}, {"lastVersion", offsetof (spirv_enumerant_t, lastVersion), QFString, parse_string, &parse_string_array}, { } }; static parse_array_t parse_enumerant_data = { .type = QFDictionary, .stride = sizeof (spirv_enumerant_t), .parser = parse_struct, .data = &spirv_enumerant_fields, .value_offset = offsetof (spirv_kind_t, enumerants), .size_offset = offsetof (spirv_kind_t, num), .cmp = spirv_enumerant_cmp, }; static parse_array_t parse_base_data = { .type = QFString, .stride = sizeof (const char *), .parser = parse_string, .data = &parse_string_array, .value_offset = offsetof (spirv_kind_t, bases), .size_offset = offsetof (spirv_kind_t, num), }; static plfield_t spirv_kind_fields[] = { {"category", offsetof (spirv_kind_t, category), QFString, parse_string, &parse_string_array}, {"kind", offsetof (spirv_kind_t, kind), QFString, parse_string, &parse_string_array}, {"doc", offsetof (spirv_kind_t, doc), QFString, parse_string, &parse_string_array}, {"enumerants", 0, QFArray, parse_array, &parse_enumerant_data}, {"bases", 0, QFArray, parse_array, &parse_base_data}, }; static parse_array_t parse_operand_data = { .type = QFDictionary, .stride = sizeof (spirv_operand_t), .parser = parse_struct, .data = &spirv_operand_fields, .value_offset = offsetof (spirv_instruction_t, operands), .size_offset = offsetof (spirv_instruction_t, num_operands), }; static parse_array_t parse_capability_data = { .type = QFString, .stride = sizeof (const char *), .parser = parse_string, .data = &parse_string_array, .value_offset = offsetof (spirv_instruction_t, capabilities), .size_offset = offsetof (spirv_instruction_t, num_capabilities), }; static parse_array_t parse_extension_data = { .type = QFString, .stride = sizeof (const char *), .parser = parse_string, .data = &parse_string_array, .value_offset = offsetof (spirv_instruction_t, extensions), .size_offset = offsetof (spirv_instruction_t, num_extensions), }; static plfield_t spirv_instruction_fields[] = { {"opname", offsetof (spirv_instruction_t, opname), QFString, parse_string, &parse_string_array}, {"opcode", offsetof (spirv_instruction_t, opcode), QFNumber, parse_uint32_t, nullptr}, {"operands", 0, QFArray, parse_array, &parse_operand_data}, {"capabilities", 0, QFArray, parse_array, &parse_capability_data}, {"extensions", 0, QFArray, parse_array, &parse_extension_data}, {"version", offsetof (spirv_instruction_t, version), QFString, parse_string, &parse_string_array}, {"lastVersion", offsetof (spirv_instruction_t, lastVersion), QFString, parse_string, &parse_string_array}, {"class", 0, QFString, parse_ignore, nullptr}, { } }; static parse_array_t parse_copyright_data = { .type = QFString, .stride = sizeof (const char *), .parser = parse_string, .data = &parse_string_array, .value_offset = offsetof (spirv_grammar_t, copyright), .size_offset = offsetof (spirv_grammar_t, num_copyright), }; static parse_array_t parse_instruction_data = { .type = QFDictionary, .stride = sizeof (spirv_instruction_t), .parser = parse_struct, .data = spirv_instruction_fields, .value_offset = offsetof (spirv_grammar_t, instructions), .size_offset = offsetof (spirv_grammar_t, num_instructions), .cmp = spirv_instruction_cmp, }; static parse_array_t parse_operand_kind_data = { .type = QFDictionary, .stride = sizeof (spirv_kind_t), .parser = parse_struct, .data = spirv_kind_fields, .value_offset = offsetof (spirv_grammar_t, operand_kinds), .size_offset = offsetof (spirv_grammar_t, num_operand_kinds), .cmp = spirv_kind_cmp, }; static bool built; static plfield_t spirv_grammar_fields[] = { {"copyright", 0, QFArray, parse_array, &parse_copyright_data}, {"magic_number", offsetof (spirv_grammar_t, magic_number), QFString, parse_hex, nullptr}, {"major_version", offsetof (spirv_grammar_t, major_version), QFNumber, parse_uint32_t, nullptr}, {"minor_version", offsetof (spirv_grammar_t, minor_version), QFNumber, parse_uint32_t, nullptr}, {"version", offsetof (spirv_grammar_t, version), QFNumber, parse_uint32_t, nullptr}, {"revision", offsetof (spirv_grammar_t, revision), QFNumber, parse_uint32_t, nullptr}, {"instructions", 0, QFArray, parse_array, &parse_instruction_data}, {"operand_kinds", 0, QFArray, parse_array, &parse_operand_kind_data}, {"instruction_printing_class", 0, QFArray, parse_ignore, nullptr}, { } }; static bool parse_grammar (plitem_t *plitem, spirv_grammar_t **grammar) { spirv_grammar_t g = {}; auto messages = PL_NewArray (); bool ret = PL_ParseStruct (spirv_grammar_fields, plitem, &g, messages, nullptr); if (!ret) { for (int i = 0; i < PL_A_NumObjects (messages); i++) { fprintf (stderr, "%s\n", PL_String (PL_ObjectAtIndex (messages, i))); } } else { *grammar = malloc (sizeof (**grammar)); **grammar = g; } PL_Release (messages); return ret; } static void build_grammars (void) { for (int i = 0; builtin_json[i].name; i++) { auto plitem = PL_ParseJSON (builtin_json[i].json, nullptr); if (!plitem) { internal_error (0, "could not parse JSON for %s", builtin_json[i].name); } if (!parse_grammar (plitem, &builtin_json[i].grammar)) { internal_error (0, "could not parse grammar spec for %s", builtin_json[i].name); } } for (int i = 0; builtin_json[i].name; i++) { auto parent = builtin_json[i].parent; if (parent) { builtin_json[i].grammar->parent = parent->grammar; } } } const plitem_t * spirv_operand_kind (const char *set, const char *kind) { if (!built) { build_grammars (); built = true; } return nullptr; }