|
|
|
|
|
|
|
|
|
|
|
|
|
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:]) |
|
|