diff --git a/ChangeLog b/ChangeLog index f912f8c58c..f478eeed45 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2018-11-30 Joseph Myers + + * 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. + 2018-11-29 H.J. Lu * elf/dl-exception.c: Include <_itoa.h>. diff --git a/Makerules b/Makerules index 07bfe8abcb..8e49a73342 100644 --- a/Makerules +++ b/Makerules @@ -282,15 +282,12 @@ ifdef gen-as-const-headers # may include . Target header files can check if # GEN_AS_CONST_HEADERS is defined to avoid circular dependency which # may lead to build hang on a many-core machine. -$(common-objpfx)%.h $(common-objpfx)%.h.d: $(..)scripts/gen-as-const.awk \ +$(common-objpfx)%.h $(common-objpfx)%.h.d: $(..)scripts/gen-as-const.py \ %.sym $(common-before-compile) - $(AWK) -f $< $(filter %.sym,$^) \ - | $(CC) -S -o $(@:.h.d=.h)T3 $(CFLAGS) $(CPPFLAGS) \ - -DGEN_AS_CONST_HEADERS -x c - \ - -MD -MP -MF $(@:.h=.h.d)T -MT '$(@:.h=.h.d) $(@:.h.d=.h)' - sed -n 's/^.*@@@name@@@\([^@]*\)@@@value@@@[^0-9Xxa-fA-F-]*\([0-9Xxa-fA-F-][0-9Xxa-fA-F-]*\).*@@@end@@@.*$$/#define \1 \2/p' \ - $(@:.h.d=.h)T3 > $(@:.h.d=.h)T - rm -f $(@:.h.d=.h)T3 + $(PYTHON) $< --cc="$(CC) $(CFLAGS) $(CPPFLAGS) -DGEN_AS_CONST_HEADERS \ + -MD -MP -MF $(@:.h=.h.d)T \ + -MT '$(@:.h=.h.d) $(@:.h.d=.h)'" \ + $(filter %.sym,$^) > $(@:.h.d=.h)T sed $(sed-remove-objpfx) $(sed-remove-dotdot) \ $(@:.h=.h.d)T > $(@:.h=.h.d)T2 rm -f $(@:.h=.h.d)T @@ -301,11 +298,10 @@ before-compile += $(gen-as-const-headers:%.sym=$(common-objpfx)%.h) tests-internal += $(gen-as-const-headers:%.sym=test-as-const-%) generated += $(gen-as-const-headers:%.sym=test-as-const-%.c) -$(objpfx)test-as-const-%.c: $(..)scripts/gen-as-const.awk $(..)Makerules \ +$(objpfx)test-as-const-%.c: $(..)scripts/gen-as-const.py $(..)Makerules \ %.sym $(common-objpfx)%.h ($(AWK) '{ sub(/^/, "asconst_", $$2); print; }' $(filter %.h,$^); \ - $(AWK) -v test=1 -f $< $(filter %.sym,$^); \ - echo '#include "$(..)test-skeleton.c"') > $@T + $(PYTHON) $< --test $(filter %.sym,$^)) > $@T mv -f $@T $@ endif diff --git a/scripts/gen-as-const.awk b/scripts/gen-as-const.awk deleted file mode 100644 index 1ffd5f2c1c..0000000000 --- a/scripts/gen-as-const.awk +++ /dev/null @@ -1,63 +0,0 @@ -# Script used in producing headers of assembly constants from C expressions. -# The input to this script looks like: -# #cpp-directive ... -# NAME1 -# NAME2 expression ... -# The output of this script is C code to be run through gcc -S and then -# massaged to extract the integer constant values of the given C expressions. -# A line giving just a name implies an expression consisting of just that name. - -BEGIN { started = 0 } - -# cpp directives go straight through. -/^#/ { print; next } - -NF >= 1 && !started { - if (test) { - print "\n#include "; - print "\n#include "; - print "\n#include "; - print "\n#if __WORDSIZE == 64"; - print "\ntypedef uint64_t c_t;"; - print "\n#define U(n) UINT64_C (n)"; - print "\n#define PRI PRId64"; - print "\n#else"; - print "\ntypedef uint32_t c_t;"; - print "\n#define U(n) UINT32_C (n)"; - print "\n#define PRI PRId32"; - print "\n#endif"; - print "\nstatic int do_test (void)\n{\n int bad = 0, good = 0;\n"; - print "#define TEST(name, source, expr) \\\n" \ - " if (U (asconst_##name) != (c_t) (expr)) { ++bad;" \ - " fprintf (stderr, \"%s: %s is %\" PRI \" but %s is %\"PRI \"\\n\"," \ - " source, #name, U (asconst_##name), #expr, (c_t) (expr));" \ - " } else ++good;\n"; - } - else - print "void dummy(void) {"; - started = 1; -} - -# Separator. -$1 == "--" { next } - -NF == 1 { sub(/^.*$/, "& &"); } - -NF > 1 { - name = $1; - sub(/^[^ ]+[ ]+/, ""); - if (test) - print " TEST (" name ", \"" FILENAME ":" FNR "\", " $0 ")"; - else - printf "asm (\"@@@name@@@%s@@@value@@@%%0@@@end@@@\" : : \"i\" ((long) %s));\n", - name, $0; -} - -END { - if (test) { - print " printf (\"%d errors in %d tests\\n\", bad, good + bad);" - print " return bad != 0 || good == 0;\n}\n"; - print "#define TEST_FUNCTION do_test ()"; - } - else if (started) print "}"; -} diff --git a/scripts/gen-as-const.py b/scripts/gen-as-const.py new file mode 100644 index 0000000000..b7a5744bb1 --- /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 +# . + +# 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 \n' + '#include \n' + '#include \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 ') + 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()