209 lines
8.3 KiB
Diff
209 lines
8.3 KiB
Diff
|
Partial backport of:
|
||
|
|
||
|
commit 7e1d42400c1b8f03316fe14176133c8853cd3bbe
|
||
|
Author: Joseph Myers <joseph@codesourcery.com>
|
||
|
Date: Fri Nov 30 15:20:41 2018 +0000
|
||
|
|
||
|
Replace gen-as-const.awk by gen-as-const.py.
|
||
|
|
||
|
This patch replaces gen-as-const.awk, and some fragments of the
|
||
|
Makefile code that used it, by a Python script. The point is not such
|
||
|
much that awk is problematic for this particular script, as that I'd
|
||
|
like to build up a general Python infrastructure for extracting
|
||
|
information from C headers, for use in writing tests of such headers.
|
||
|
Thus, although this patch does not set up such infrastructure, the
|
||
|
compute_c_consts function in gen-as-const.py might be moved to a
|
||
|
separate Python module in a subsequent patch as a starting point for
|
||
|
such infrastructure.
|
||
|
|
||
|
The general idea of the code is the same as in the awk version, but no
|
||
|
attempt is made to make the output files textually identical. When
|
||
|
generating a header, a dict of constant names and values is generated
|
||
|
internally then defines are printed in sorted order (rather than the
|
||
|
order in the .sym file, which would have been used before). When
|
||
|
generating a test that the values computed match those from a normal
|
||
|
header inclusion, the test code is made into a compilation test using
|
||
|
_Static_assert, where previously the comparisons were done only when
|
||
|
the test was executed. One fragment of test generation (converting
|
||
|
the previously generated header to use asconst_* prefixes on its macro
|
||
|
names) is still in awk code in the makefiles; only the .sym processing
|
||
|
and subsequent execution of the compiler to extract constants have
|
||
|
moved to the Python script.
|
||
|
|
||
|
Tested for x86_64, and with build-many-glibcs.py.
|
||
|
|
||
|
* scripts/gen-as-const.py: New file.
|
||
|
* scripts/gen-as-const.awk: Remove.
|
||
|
* Makerules ($(common-objpfx)%.h $(common-objpfx)%.h.d): Use
|
||
|
gen-as-const.py.
|
||
|
($(objpfx)test-as-const-%.c): Likewise.
|
||
|
|
||
|
In the downstream version, scripts/gen-as-const.awk is not removed and
|
||
|
still used in Makerules.
|
||
|
|
||
|
diff --git a/scripts/gen-as-const.py b/scripts/gen-as-const.py
|
||
|
new file mode 100644
|
||
|
index 0000000000000000..b7a5744bb192dd67
|
||
|
--- /dev/null
|
||
|
+++ b/scripts/gen-as-const.py
|
||
|
@@ -0,0 +1,159 @@
|
||
|
+#!/usr/bin/python3
|
||
|
+# Produce headers of assembly constants from C expressions.
|
||
|
+# Copyright (C) 2018 Free Software Foundation, Inc.
|
||
|
+# This file is part of the GNU C Library.
|
||
|
+#
|
||
|
+# The GNU C Library is free software; you can redistribute it and/or
|
||
|
+# modify it under the terms of the GNU Lesser General Public
|
||
|
+# License as published by the Free Software Foundation; either
|
||
|
+# version 2.1 of the License, or (at your option) any later version.
|
||
|
+#
|
||
|
+# The GNU C Library 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
|
||
|
+# Lesser General Public License for more details.
|
||
|
+#
|
||
|
+# You should have received a copy of the GNU Lesser General Public
|
||
|
+# License along with the GNU C Library; if not, see
|
||
|
+# <http://www.gnu.org/licenses/>.
|
||
|
+
|
||
|
+# The input to this script looks like:
|
||
|
+# #cpp-directive ...
|
||
|
+# NAME1
|
||
|
+# NAME2 expression ...
|
||
|
+# A line giving just a name implies an expression consisting of just that name.
|
||
|
+
|
||
|
+import argparse
|
||
|
+import os.path
|
||
|
+import re
|
||
|
+import subprocess
|
||
|
+import tempfile
|
||
|
+
|
||
|
+
|
||
|
+def compute_c_consts(sym_data, cc):
|
||
|
+ """Compute the values of some C constants.
|
||
|
+
|
||
|
+ The first argument is a list whose elements are either strings
|
||
|
+ (preprocessor directives) or pairs of strings (a name and a C
|
||
|
+ expression for the corresponding value). Preprocessor directives
|
||
|
+ in the middle of the list may be used to select which constants
|
||
|
+ end up being evaluated using which expressions.
|
||
|
+
|
||
|
+ """
|
||
|
+ out_lines = []
|
||
|
+ started = False
|
||
|
+ for arg in sym_data:
|
||
|
+ if isinstance(arg, str):
|
||
|
+ out_lines.append(arg)
|
||
|
+ continue
|
||
|
+ name = arg[0]
|
||
|
+ value = arg[1]
|
||
|
+ if not started:
|
||
|
+ out_lines.append('void\ndummy (void)\n{')
|
||
|
+ started = True
|
||
|
+ out_lines.append('asm ("@@@name@@@%s@@@value@@@%%0@@@end@@@" '
|
||
|
+ ': : \"i\" ((long int) (%s)));'
|
||
|
+ % (name, value))
|
||
|
+ if started:
|
||
|
+ out_lines.append('}')
|
||
|
+ out_lines.append('')
|
||
|
+ out_text = '\n'.join(out_lines)
|
||
|
+ with tempfile.TemporaryDirectory() as temp_dir:
|
||
|
+ c_file_name = os.path.join(temp_dir, 'test.c')
|
||
|
+ s_file_name = os.path.join(temp_dir, 'test.s')
|
||
|
+ with open(c_file_name, 'w') as c_file:
|
||
|
+ c_file.write(out_text)
|
||
|
+ # Compilation has to be from stdin to avoid the temporary file
|
||
|
+ # name being written into the generated dependencies.
|
||
|
+ cmd = ('%s -S -o %s -x c - < %s' % (cc, s_file_name, c_file_name))
|
||
|
+ subprocess.check_call(cmd, shell=True)
|
||
|
+ consts = {}
|
||
|
+ with open(s_file_name, 'r') as s_file:
|
||
|
+ for line in s_file:
|
||
|
+ match = re.search('@@@name@@@([^@]*)'
|
||
|
+ '@@@value@@@[^0-9Xxa-fA-F-]*'
|
||
|
+ '([0-9Xxa-fA-F-]+).*@@@end@@@', line)
|
||
|
+ if match:
|
||
|
+ if (match.group(1) in consts
|
||
|
+ and match.group(2) != consts[match.group(1)]):
|
||
|
+ raise ValueError('duplicate constant %s'
|
||
|
+ % match.group(1))
|
||
|
+ consts[match.group(1)] = match.group(2)
|
||
|
+ return consts
|
||
|
+
|
||
|
+
|
||
|
+def gen_test(sym_data):
|
||
|
+ """Generate a test for the values of some C constants.
|
||
|
+
|
||
|
+ The first argument is as for compute_c_consts.
|
||
|
+
|
||
|
+ """
|
||
|
+ out_lines = []
|
||
|
+ started = False
|
||
|
+ for arg in sym_data:
|
||
|
+ if isinstance(arg, str):
|
||
|
+ out_lines.append(arg)
|
||
|
+ continue
|
||
|
+ name = arg[0]
|
||
|
+ value = arg[1]
|
||
|
+ if not started:
|
||
|
+ out_lines.append('#include <stdint.h>\n'
|
||
|
+ '#include <stdio.h>\n'
|
||
|
+ '#include <bits/wordsize.h>\n'
|
||
|
+ '#if __WORDSIZE == 64\n'
|
||
|
+ 'typedef uint64_t c_t;\n'
|
||
|
+ '# define U(n) UINT64_C (n)\n'
|
||
|
+ '#else\n'
|
||
|
+ 'typedef uint32_t c_t;\n'
|
||
|
+ '# define U(n) UINT32_C (n)\n'
|
||
|
+ '#endif\n'
|
||
|
+ 'static int\n'
|
||
|
+ 'do_test (void)\n'
|
||
|
+ '{\n'
|
||
|
+ # Compilation test only, using static assertions.
|
||
|
+ ' return 0;\n'
|
||
|
+ '}\n'
|
||
|
+ '#include <support/test-driver.c>')
|
||
|
+ started = True
|
||
|
+ out_lines.append('_Static_assert (U (asconst_%s) == (c_t) (%s), '
|
||
|
+ '"value of %s");'
|
||
|
+ % (name, value, name))
|
||
|
+ return '\n'.join(out_lines)
|
||
|
+
|
||
|
+
|
||
|
+def main():
|
||
|
+ """The main entry point."""
|
||
|
+ parser = argparse.ArgumentParser(
|
||
|
+ description='Produce headers of assembly constants.')
|
||
|
+ parser.add_argument('--cc', metavar='CC',
|
||
|
+ help='C compiler (including options) to use')
|
||
|
+ parser.add_argument('--test', action='store_true',
|
||
|
+ help='Generate test case instead of header')
|
||
|
+ parser.add_argument('sym_file',
|
||
|
+ help='.sym file to process')
|
||
|
+ args = parser.parse_args()
|
||
|
+ sym_data = []
|
||
|
+ with open(args.sym_file, 'r') as sym_file:
|
||
|
+ for line in sym_file:
|
||
|
+ line = line.strip()
|
||
|
+ if line == '':
|
||
|
+ continue
|
||
|
+ # Pass preprocessor directives through.
|
||
|
+ if line.startswith('#'):
|
||
|
+ sym_data.append(line)
|
||
|
+ continue
|
||
|
+ words = line.split(maxsplit=1)
|
||
|
+ # Separator.
|
||
|
+ if words[0] == '--':
|
||
|
+ continue
|
||
|
+ name = words[0]
|
||
|
+ value = words[1] if len(words) > 1 else words[0]
|
||
|
+ sym_data.append((name, value))
|
||
|
+ if args.test:
|
||
|
+ print(gen_test(sym_data))
|
||
|
+ else:
|
||
|
+ consts = compute_c_consts(sym_data, args.cc)
|
||
|
+ print('\n'.join('#define %s %s' % c for c in sorted(consts.items())))
|
||
|
+
|
||
|
+if __name__ == '__main__':
|
||
|
+ main()
|