| |
| |
| |
| |
| |
|
|
| import argparse |
| import codecs |
| import math |
| import os |
| import re |
| import sys |
| import yaml |
|
|
| sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) |
| import xngen |
| import xnncommon |
|
|
| parser = argparse.ArgumentParser( |
| description='Generates enum header and convertion-to-string code.') |
| parser.add_argument( |
| '-s', |
| '--spec', |
| metavar='FILE', |
| required=True, |
| help='Specification (YAML) file') |
| parser.add_argument( |
| '--output_src', |
| metavar='FILE', |
| required=True, |
| help='Output C source file') |
| parser.add_argument( |
| '--output_hdr', |
| metavar='FILE', |
| required=True, |
| help='Output C/C++ header file') |
| parser.add_argument( |
| '-e', |
| '--enum', |
| metavar='NAME', |
| required=True, |
| help='Name of the enum variable') |
| parser.set_defaults(defines=list()) |
|
|
|
|
| def generate_source(enum_name, spec_path, output_path, header_path): |
| with codecs.open(spec_path, 'r', encoding='utf-8') as spec_file: |
| spec_yaml = yaml.safe_load(spec_file) |
| if not isinstance(spec_yaml, list): |
| raise ValueError('expected a list of enumeration values in the spec') |
|
|
| output = f"""\ |
| // Copyright 2022 Google LLC |
| // |
| // This source code is licensed under the BSD-style license found in the |
| // LICENSE file in the root directory of this source tree. |
| // |
| // Auto-generated file. Do not edit! |
| // Specification: {spec_path} |
| // Generator: {sys.argv[0]} |
| |
| |
| #include <assert.h> |
| #include <stdint.h> |
| |
| #include <{header_path}>\n\n\n""" |
|
|
| max_offset = sum(len(entry['string']) + 1 for entry in spec_yaml[:-1]) |
| if max_offset < 256: |
| offset_type = 'uint8_t' |
| elif max_offset < 65536: |
| offset_type = 'uint16_t' |
| else: |
| offset_type = 'uint32_t' |
|
|
| offset_declaration = f'static const {offset_type} offset[{len(spec_yaml)}] = {{\n '; |
| string_declaration = 'static const char data[] =\n' |
| pos = 0 |
| for i, spec_entry in enumerate(spec_yaml): |
| enum_item_name = spec_entry['name'] |
| assert enum_item_name.startswith(enum_name + "_") |
| enum_item_string = spec_entry['string'] |
|
|
| if i + 1 != len(spec_yaml): |
| string_declaration += ' "' + enum_item_string + '\\0"\n' |
| offset_declaration += ' ' + str(pos) + ',' |
| else: |
| string_declaration += ' "' + enum_item_string + '";\n' |
| offset_declaration += ' ' + str(pos) + '\n};' |
|
|
| |
| last_offset_line = offset_declaration[offset_declaration.rfind('\n')+1:] |
| if len(last_offset_line) > 120: |
| last_offset_start = offset_declaration.rfind(',', 0, -1) + 1 |
| offset_declaration = offset_declaration[:last_offset_start] + '\n ' + offset_declaration[last_offset_start:] |
|
|
| pos += len(enum_item_string) + 1 |
|
|
| output += offset_declaration |
| output += '\n\n' |
| output += string_declaration |
|
|
| arg_name = enum_name[len("xnn_"):] |
| output += f""" |
| const char* {enum_name}_to_string(enum {enum_name} {arg_name}) {{ |
| assert({arg_name} >= {spec_yaml[0]['name']}); |
| assert({arg_name} <= {spec_yaml[-1]['name']}); |
| return &data[offset[{arg_name}]]; |
| }}\n""" |
|
|
| txt_changed = True |
| if os.path.exists(output_path): |
| with codecs.open(output_path, 'r', encoding='utf-8') as output_file: |
| txt_changed = output_file.read() != output |
|
|
| if txt_changed: |
| with codecs.open(output_path, 'w', encoding='utf-8') as output_file: |
| output_file.write(output) |
|
|
| def generate_header(enum_name, spec_path, output_path): |
| with codecs.open(spec_path, 'r', encoding='utf-8') as spec_file: |
| spec_yaml = yaml.safe_load(spec_file) |
| if not isinstance(spec_yaml, list): |
| raise ValueError('expected a list of enumeration values in the spec') |
|
|
| output = f"""\ |
| // Copyright 2022 Google LLC |
| // |
| // This source code is licensed under the BSD-style license found in the |
| // LICENSE file in the root directory of this source tree. |
| // |
| // Auto-generated file. Do not edit! |
| // Specification: {spec_path} |
| // Generator: {sys.argv[0]} |
| |
| #pragma once |
| |
| #include <xnnpack/common.h> |
| |
| |
| #ifdef __cplusplus |
| extern "C" {{ |
| #endif |
| |
| enum {enum_name} {{\n""" |
|
|
| enum_item_name = spec_yaml[0]['name'] |
| assert enum_item_name.startswith(enum_name + "_") |
| output += ' ' + enum_item_name + ' = 0,\n' |
| for spec_entry in spec_yaml[1:]: |
| enum_item_name = spec_entry['name'] |
| assert enum_item_name.startswith(enum_name + "_") |
| output += ' ' + enum_item_name + ',\n' |
|
|
| arg_name = enum_name[len("xnn_"):] |
| output += f"""}}; |
| |
| XNN_INTERNAL const char* {enum_name}_to_string(enum {enum_name} {arg_name}); |
| |
| #ifdef __cplusplus |
| }} // extern "C" |
| #endif |
| """ |
|
|
|
|
| txt_changed = True |
| if os.path.exists(output_path): |
| with codecs.open(output_path, 'r', encoding='utf-8') as output_file: |
| txt_changed = output_file.read() != output |
|
|
| if txt_changed: |
| with codecs.open(output_path, 'w', encoding='utf-8') as output_file: |
| output_file.write(output) |
|
|
| def main(args): |
| options = parser.parse_args(args) |
| generate_header(options.enum, options.spec, options.output_hdr) |
|
|
| assert options.enum.startswith('xnn_') |
| header_path = 'xnnpack/' + options.enum[len('xnn_'):].replace('_', '-') + '.h' |
| generate_source(options.enum, options.spec, options.output_src, header_path) |
|
|
| if __name__ == '__main__': |
| main(sys.argv[1:]) |
|
|