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