xref: /aosp_15_r20/external/XNNPACK/tools/xngen.py (revision 4bdc94577ba0e567308109d787f7fec7b531ce36)
1*4bdc9457SAndroid Build Coastguard Worker#!/usr/bin/env python
2*4bdc9457SAndroid Build Coastguard Worker# Copyright 2019 Google LLC
3*4bdc9457SAndroid Build Coastguard Worker#
4*4bdc9457SAndroid Build Coastguard Worker# This source code is licensed under the BSD-style license found in the
5*4bdc9457SAndroid Build Coastguard Worker# LICENSE file in the root directory of this source tree.
6*4bdc9457SAndroid Build Coastguard Worker
7*4bdc9457SAndroid Build Coastguard Workerimport argparse
8*4bdc9457SAndroid Build Coastguard Workerimport codecs
9*4bdc9457SAndroid Build Coastguard Workerimport io
10*4bdc9457SAndroid Build Coastguard Workerimport os
11*4bdc9457SAndroid Build Coastguard Workerimport re
12*4bdc9457SAndroid Build Coastguard Workerimport sys
13*4bdc9457SAndroid Build Coastguard Workerfrom itertools import chain
14*4bdc9457SAndroid Build Coastguard Worker
15*4bdc9457SAndroid Build Coastguard Worker
16*4bdc9457SAndroid Build Coastguard Workerdef key_value_pair(line):
17*4bdc9457SAndroid Build Coastguard Worker  key, value = line.split("=", 1)
18*4bdc9457SAndroid Build Coastguard Worker  # represent value as integer, if possible, otherwise as str
19*4bdc9457SAndroid Build Coastguard Worker  try:
20*4bdc9457SAndroid Build Coastguard Worker    value = int(value)
21*4bdc9457SAndroid Build Coastguard Worker  except ValueError:
22*4bdc9457SAndroid Build Coastguard Worker    pass
23*4bdc9457SAndroid Build Coastguard Worker  return key, value
24*4bdc9457SAndroid Build Coastguard Worker
25*4bdc9457SAndroid Build Coastguard Worker
26*4bdc9457SAndroid Build Coastguard Workerparser = argparse.ArgumentParser(description='XNNPACK generator')
27*4bdc9457SAndroid Build Coastguard Workerparser.add_argument("input", metavar="FILE", nargs=1,
28*4bdc9457SAndroid Build Coastguard Worker          help="Input file")
29*4bdc9457SAndroid Build Coastguard Workerparser.add_argument("-D", dest="defines", metavar="KEY=VALUE", nargs="*",
30*4bdc9457SAndroid Build Coastguard Worker          type=key_value_pair, action="append",
31*4bdc9457SAndroid Build Coastguard Worker          help="Predefined variables")
32*4bdc9457SAndroid Build Coastguard Workerparser.add_argument("-o", "--output",
33*4bdc9457SAndroid Build Coastguard Worker          help='Output file')
34*4bdc9457SAndroid Build Coastguard Workerparser.set_defaults(defines=list())
35*4bdc9457SAndroid Build Coastguard Worker
36*4bdc9457SAndroid Build Coastguard Worker
37*4bdc9457SAndroid Build Coastguard WorkerLEADING_WHITESPACE_REGEX = re.compile(r"^\s*", flags=0)
38*4bdc9457SAndroid Build Coastguard Worker
39*4bdc9457SAndroid Build Coastguard Worker
40*4bdc9457SAndroid Build Coastguard Workerdef extract_leading_whitespace(line):
41*4bdc9457SAndroid Build Coastguard Worker  match = re.match(r"\s*", line)
42*4bdc9457SAndroid Build Coastguard Worker  return match.group(0) if match else ""
43*4bdc9457SAndroid Build Coastguard Worker
44*4bdc9457SAndroid Build Coastguard Worker
45*4bdc9457SAndroid Build Coastguard Workerdef escape(line):
46*4bdc9457SAndroid Build Coastguard Worker  output_parts = []
47*4bdc9457SAndroid Build Coastguard Worker  while "${" in line:
48*4bdc9457SAndroid Build Coastguard Worker    start_pos = line.index("${")
49*4bdc9457SAndroid Build Coastguard Worker    end_pos = line.index("}", start_pos + 2)
50*4bdc9457SAndroid Build Coastguard Worker    if start_pos != 0:
51*4bdc9457SAndroid Build Coastguard Worker      output_parts.append("\"" + line[:start_pos].replace("\"", "\\\"") + "\"")
52*4bdc9457SAndroid Build Coastguard Worker    output_parts.append("str(" + line[start_pos+2:end_pos] + ")")
53*4bdc9457SAndroid Build Coastguard Worker    line = line[end_pos+1:]
54*4bdc9457SAndroid Build Coastguard Worker  if line:
55*4bdc9457SAndroid Build Coastguard Worker    output_parts.append("\"" + line.replace("\"", "\\\"") + "\"")
56*4bdc9457SAndroid Build Coastguard Worker  return " + ".join(output_parts)
57*4bdc9457SAndroid Build Coastguard Worker
58*4bdc9457SAndroid Build Coastguard Worker
59*4bdc9457SAndroid Build Coastguard Workerdef preprocess(input_text, input_globals, input_path="codegen"):
60*4bdc9457SAndroid Build Coastguard Worker  input_lines = input_text.splitlines()
61*4bdc9457SAndroid Build Coastguard Worker  python_lines = ["from __future__ import print_function"]
62*4bdc9457SAndroid Build Coastguard Worker
63*4bdc9457SAndroid Build Coastguard Worker  blank_lines = 0
64*4bdc9457SAndroid Build Coastguard Worker
65*4bdc9457SAndroid Build Coastguard Worker  last_line = ""
66*4bdc9457SAndroid Build Coastguard Worker  last_indent = ""
67*4bdc9457SAndroid Build Coastguard Worker
68*4bdc9457SAndroid Build Coastguard Worker  # List of tuples (total_index, python_indent)
69*4bdc9457SAndroid Build Coastguard Worker  indent_stack = [("", "")]
70*4bdc9457SAndroid Build Coastguard Worker
71*4bdc9457SAndroid Build Coastguard Worker  # Indicates whether this is the first line inside Python
72*4bdc9457SAndroid Build Coastguard Worker  # code block (i.e. for, while, if, elif, else)
73*4bdc9457SAndroid Build Coastguard Worker  python_block_start = True
74*4bdc9457SAndroid Build Coastguard Worker  for i, input_line in enumerate(input_lines):
75*4bdc9457SAndroid Build Coastguard Worker    if input_line == "":
76*4bdc9457SAndroid Build Coastguard Worker      blank_lines += 1
77*4bdc9457SAndroid Build Coastguard Worker      continue
78*4bdc9457SAndroid Build Coastguard Worker    # Skip lint markers.
79*4bdc9457SAndroid Build Coastguard Worker    if 'LINT' in input_line:
80*4bdc9457SAndroid Build Coastguard Worker      continue
81*4bdc9457SAndroid Build Coastguard Worker
82*4bdc9457SAndroid Build Coastguard Worker    input_indent = extract_leading_whitespace(input_line)
83*4bdc9457SAndroid Build Coastguard Worker    if python_block_start:
84*4bdc9457SAndroid Build Coastguard Worker      assert input_indent.startswith(last_indent)
85*4bdc9457SAndroid Build Coastguard Worker      extra_python_indent = input_indent[len(last_indent):]
86*4bdc9457SAndroid Build Coastguard Worker      python_indent = indent_stack[-1][1] + extra_python_indent
87*4bdc9457SAndroid Build Coastguard Worker      indent_stack.append((input_indent, python_indent))
88*4bdc9457SAndroid Build Coastguard Worker      assert input_indent.startswith(indent_stack[-1][0])
89*4bdc9457SAndroid Build Coastguard Worker    else:
90*4bdc9457SAndroid Build Coastguard Worker      while not input_indent.startswith(indent_stack[-1][0]):
91*4bdc9457SAndroid Build Coastguard Worker        del indent_stack[-1]
92*4bdc9457SAndroid Build Coastguard Worker    python_block_start = False
93*4bdc9457SAndroid Build Coastguard Worker
94*4bdc9457SAndroid Build Coastguard Worker    python_indent = indent_stack[-1][1]
95*4bdc9457SAndroid Build Coastguard Worker    stripped_input_line = input_line.strip()
96*4bdc9457SAndroid Build Coastguard Worker    if stripped_input_line.startswith("$") and not stripped_input_line.startswith("${"):
97*4bdc9457SAndroid Build Coastguard Worker      if stripped_input_line.endswith(":"):
98*4bdc9457SAndroid Build Coastguard Worker        python_block_start = True
99*4bdc9457SAndroid Build Coastguard Worker      while blank_lines != 0:
100*4bdc9457SAndroid Build Coastguard Worker        python_lines.append(python_indent + "print(file=OUT_STREAM)")
101*4bdc9457SAndroid Build Coastguard Worker        blank_lines -= 1
102*4bdc9457SAndroid Build Coastguard Worker      python_lines.append(python_indent + stripped_input_line.replace("$", ""))
103*4bdc9457SAndroid Build Coastguard Worker    else:
104*4bdc9457SAndroid Build Coastguard Worker      assert input_line.startswith(python_indent)
105*4bdc9457SAndroid Build Coastguard Worker      while blank_lines != 0:
106*4bdc9457SAndroid Build Coastguard Worker        python_lines.append(python_indent + "print(file=OUT_STREAM)")
107*4bdc9457SAndroid Build Coastguard Worker        blank_lines -= 1
108*4bdc9457SAndroid Build Coastguard Worker      python_lines.append(python_indent + "print(%s, file=OUT_STREAM)" % escape(input_line[len(python_indent):]))
109*4bdc9457SAndroid Build Coastguard Worker    last_line = input_line
110*4bdc9457SAndroid Build Coastguard Worker    last_indent = input_indent
111*4bdc9457SAndroid Build Coastguard Worker
112*4bdc9457SAndroid Build Coastguard Worker  while blank_lines != 0:
113*4bdc9457SAndroid Build Coastguard Worker    python_lines.append(python_indent + "print(file=OUT_STREAM)")
114*4bdc9457SAndroid Build Coastguard Worker    blank_lines -= 1
115*4bdc9457SAndroid Build Coastguard Worker
116*4bdc9457SAndroid Build Coastguard Worker  exec_globals = dict(input_globals)
117*4bdc9457SAndroid Build Coastguard Worker  if sys.version_info > (3, 0):
118*4bdc9457SAndroid Build Coastguard Worker    output_stream = io.StringIO()
119*4bdc9457SAndroid Build Coastguard Worker  else:
120*4bdc9457SAndroid Build Coastguard Worker    output_stream = io.BytesIO()
121*4bdc9457SAndroid Build Coastguard Worker  exec_globals["OUT_STREAM"] = output_stream
122*4bdc9457SAndroid Build Coastguard Worker  python_bytecode = compile("\n".join(python_lines), input_path, 'exec')
123*4bdc9457SAndroid Build Coastguard Worker  exec(python_bytecode, exec_globals)
124*4bdc9457SAndroid Build Coastguard Worker
125*4bdc9457SAndroid Build Coastguard Worker  return output_stream.getvalue()
126*4bdc9457SAndroid Build Coastguard Worker
127*4bdc9457SAndroid Build Coastguard Worker
128*4bdc9457SAndroid Build Coastguard WorkerPREAMBLE = """\
129*4bdc9457SAndroid Build Coastguard Worker// Auto-generated file. Do not edit!
130*4bdc9457SAndroid Build Coastguard Worker//   Template: {template}
131*4bdc9457SAndroid Build Coastguard Worker//   Generator: {generator}
132*4bdc9457SAndroid Build Coastguard Worker//
133*4bdc9457SAndroid Build Coastguard Worker"""
134*4bdc9457SAndroid Build Coastguard Worker
135*4bdc9457SAndroid Build Coastguard Worker
136*4bdc9457SAndroid Build Coastguard Workerdef main(args):
137*4bdc9457SAndroid Build Coastguard Worker  options = parser.parse_args(args)
138*4bdc9457SAndroid Build Coastguard Worker
139*4bdc9457SAndroid Build Coastguard Worker  input_text = codecs.open(options.input[0], "r", encoding="utf-8").read()
140*4bdc9457SAndroid Build Coastguard Worker  python_globals = dict(chain(*options.defines))
141*4bdc9457SAndroid Build Coastguard Worker  output_text = PREAMBLE.format(template=options.input[0], generator=sys.argv[0]) + preprocess(input_text, python_globals, options.input[0])
142*4bdc9457SAndroid Build Coastguard Worker
143*4bdc9457SAndroid Build Coastguard Worker  txt_changed = True
144*4bdc9457SAndroid Build Coastguard Worker  if os.path.exists(options.output):
145*4bdc9457SAndroid Build Coastguard Worker    with codecs.open(options.output, "r", encoding="utf-8") as output_file:
146*4bdc9457SAndroid Build Coastguard Worker      txt_changed = output_file.read() != output_text
147*4bdc9457SAndroid Build Coastguard Worker
148*4bdc9457SAndroid Build Coastguard Worker  if txt_changed:
149*4bdc9457SAndroid Build Coastguard Worker    with codecs.open(options.output, "w", encoding="utf-8") as output_file:
150*4bdc9457SAndroid Build Coastguard Worker      output_file.write(output_text)
151*4bdc9457SAndroid Build Coastguard Worker
152*4bdc9457SAndroid Build Coastguard Workerif __name__ == "__main__":
153*4bdc9457SAndroid Build Coastguard Worker  main(sys.argv[1:])
154