]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
Run the bootstrap object comparison in parallel
authorKyrylo Tkachov <ktkachov@nvidia.com>
Mon, 6 Jul 2026 09:42:14 +0000 (02:42 -0700)
committerKyrylo Tkachov <ktkachov@nvidia.com>
Tue, 4 Aug 2026 17:07:07 +0000 (19:07 +0200)
The stage 2 and stage 3 object comparison runs one command per object in a
serial shell loop.  Everything after it waits while an otherwise idle machine
compares thousands of objects.

Generate one make target per object and run the comparisons through $(MAKE),
so they share the original jobserver.  Keep the generated makefile and result
shards invocation-specific, clean them on normal and signal exits, and sort
the shards before creating the traditional .bad_compare diagnostic.  Files in
the exclusion list still only warn.  Comparator statuses other than zero or
one now report an operational error instead of being treated as equal.

The tail-based fallback for cmp implementations without byte skipping used
fixed temporary names.  Give those files process-specific names, clean them
with a trap, and preserve the same status contract for tail failures and
signals.

On highly parallel AArch64 and x86_64 systems this reduces comparison wall
time by about 95%.

ChangeLog:

* Makefile.tpl ([+compare-target+]): Run object comparisons through an
invocation-specific parallel sub-make.  Collect sorted failure shards
and clean temporary state on exit.
* Makefile.in: Regenerate.
* configure: Regenerate.

config/ChangeLog:

* acx.m4 (ACX_PROG_CMP_IGNORE_INITIAL): Use process-specific temporary
files and clean them on exit.  Diagnose tail failures as errors.

Signed-off-by: Kyrylo Tkachov <ktkachov@nvidia.com>
Makefile.in
Makefile.tpl
config/acx.m4
configure

index f34142cc077dd9f647398b6185f2761361d16b75..5cca69c3f5234042a000bf96d0a9d072771efacd 100644 (file)
@@ -66022,6 +66022,9 @@ do-clean: clean-stage3
 # only possibility, but now it conflicts with no-bootstrap rules
 @if gcc-bootstrap
 
+# Run the comparisons in parallel through a generated sub-makefile under
+# the jobserver.  Each failing recipe writes a separate result shard that
+# is collected after the sub-make finishes.
 compare:
        @r=`${PWD_COMMAND}`; export r; \
        s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
@@ -66030,29 +66033,52 @@ compare:
          exit 0; \
        fi; \
        : $(MAKE); $(stage); \
-       rm -f .bad_compare; \
+       compare_id=$$$$; \
+       bad_compare=.bad_compare.$$compare_id; export bad_compare; \
+       compare_makefile=compare.$$compare_id.mk; \
+       trap 'st=$$?; rm -f "$$compare_makefile" "$$bad_compare" \
+         "$$bad_compare".*; trap - 0; exit $$st' 0; \
+       trap 'exit 1' 1 2 3 15; \
        echo Comparing stages 2 and 3; \
         sed=`echo stage3 | sed 's,^stage,,;s,.,.,g'`; \
        files=`find stage3-* -name "*$(objext)" -print | \
                 sed -n s,^stage$$sed-,,p`; \
-       for file in $${files} ${extra-compare}; do \
-         f1=$$r/stage2-$$file; f2=$$r/stage3-$$file; \
-         if test ! -f $$f1; then continue; fi; \
-         $(do-compare) > /dev/null 2>&1; \
-         if test $$? -eq 1; then \
-           case $$file in \
-             @compare_exclusions@) \
-               echo warning: $$file differs ;; \
-             *) \
-               echo $$file differs >> .bad_compare ;; \
-           esac; \
-         fi; \
-       done; \
-       if [ -f .bad_compare ]; then \
+       cmp_raw='$(do-compare)'; \
+       { \
+         echo 'all:'; \
+         echo '.PHONY: all FORCE'; \
+         echo 'FORCE:'; \
+         printf 'compare/%%: FORCE ; @'; \
+         printf 'f1=$$$$r/stage2-$$*; '; \
+         printf 'f2=$$$$r/stage3-$$*; '; \
+         printf '%s' "$$cmp_raw" | sed 's,\$$,$$$$,g'; \
+         printf ' > /dev/null 2>&1; st=$$$$?; '; \
+         printf 'if test $$$$st -eq 1; then '; \
+         printf 'case $$* in '; \
+         printf '@compare_exclusions@) echo warning: $$* differs ;; '; \
+         printf '*) echo $$* differs >> "$$$$bad_compare.$$$$$$$$" ;; '; \
+         printf 'esac; '; \
+         printf 'elif test $$$$st -ne 0; then '; \
+         printf 'echo "$$* compare: error status $$$$st" '; \
+         printf '>> "$$$$bad_compare.$$$$$$$$"; fi\n'; \
+         for file in $${files} ${extra-compare}; do \
+           if test ! -f $$r/stage2-$$file; then continue; fi; \
+           echo "all: compare/$$file"; \
+         done; \
+       } > "$$compare_makefile"; \
+       $(MAKE) -s -f "$$compare_makefile" all; compare_status=$$?; \
+       if test $$compare_status -ne 0; then \
+         exit $$compare_status; \
+       fi; \
+       set -- "$$bad_compare".*; \
+       if test -f "$$1"; then \
          echo "Bootstrap comparison failure!"; \
-         cat .bad_compare; \
+         LC_ALL=C sort "$$bad_compare".* > "$$bad_compare" || exit 1; \
+         cat "$$bad_compare"; \
+         mv -f "$$bad_compare" .bad_compare; \
          exit 1; \
        else \
+         rm -f .bad_compare; \
          echo Comparison successful.; \
        fi; \
        $(STAMP) compare
@@ -66456,6 +66482,9 @@ do-clean: clean-stage4
 # only possibility, but now it conflicts with no-bootstrap rules
 @if gcc-bootstrap
 
+# Run the comparisons in parallel through a generated sub-makefile under
+# the jobserver.  Each failing recipe writes a separate result shard that
+# is collected after the sub-make finishes.
 compare3:
        @r=`${PWD_COMMAND}`; export r; \
        s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
@@ -66464,29 +66493,52 @@ compare3:
          exit 0; \
        fi; \
        : $(MAKE); $(stage); \
-       rm -f .bad_compare; \
+       compare_id=$$$$; \
+       bad_compare=.bad_compare.$$compare_id; export bad_compare; \
+       compare_makefile=compare3.$$compare_id.mk; \
+       trap 'st=$$?; rm -f "$$compare_makefile" "$$bad_compare" \
+         "$$bad_compare".*; trap - 0; exit $$st' 0; \
+       trap 'exit 1' 1 2 3 15; \
        echo Comparing stages 3 and 4; \
         sed=`echo stage4 | sed 's,^stage,,;s,.,.,g'`; \
        files=`find stage4-* -name "*$(objext)" -print | \
                 sed -n s,^stage$$sed-,,p`; \
-       for file in $${files} ${extra-compare}; do \
-         f1=$$r/stage3-$$file; f2=$$r/stage4-$$file; \
-         if test ! -f $$f1; then continue; fi; \
-         $(do-compare3) > /dev/null 2>&1; \
-         if test $$? -eq 1; then \
-           case $$file in \
-             @compare_exclusions@) \
-               echo warning: $$file differs ;; \
-             *) \
-               echo $$file differs >> .bad_compare ;; \
-           esac; \
-         fi; \
-       done; \
-       if [ -f .bad_compare ]; then \
+       cmp_raw='$(do-compare3)'; \
+       { \
+         echo 'all:'; \
+         echo '.PHONY: all FORCE'; \
+         echo 'FORCE:'; \
+         printf 'compare3/%%: FORCE ; @'; \
+         printf 'f1=$$$$r/stage3-$$*; '; \
+         printf 'f2=$$$$r/stage4-$$*; '; \
+         printf '%s' "$$cmp_raw" | sed 's,\$$,$$$$,g'; \
+         printf ' > /dev/null 2>&1; st=$$$$?; '; \
+         printf 'if test $$$$st -eq 1; then '; \
+         printf 'case $$* in '; \
+         printf '@compare_exclusions@) echo warning: $$* differs ;; '; \
+         printf '*) echo $$* differs >> "$$$$bad_compare.$$$$$$$$" ;; '; \
+         printf 'esac; '; \
+         printf 'elif test $$$$st -ne 0; then '; \
+         printf 'echo "$$* compare: error status $$$$st" '; \
+         printf '>> "$$$$bad_compare.$$$$$$$$"; fi\n'; \
+         for file in $${files} ${extra-compare}; do \
+           if test ! -f $$r/stage3-$$file; then continue; fi; \
+           echo "all: compare3/$$file"; \
+         done; \
+       } > "$$compare_makefile"; \
+       $(MAKE) -s -f "$$compare_makefile" all; compare_status=$$?; \
+       if test $$compare_status -ne 0; then \
+         exit $$compare_status; \
+       fi; \
+       set -- "$$bad_compare".*; \
+       if test -f "$$1"; then \
          echo "Bootstrap comparison failure!"; \
-         cat .bad_compare; \
+         LC_ALL=C sort "$$bad_compare".* > "$$bad_compare" || exit 1; \
+         cat "$$bad_compare"; \
+         mv -f "$$bad_compare" .bad_compare; \
          exit 1; \
        else \
+         rm -f .bad_compare; \
          echo Comparison successful.; \
        fi; \
        $(STAMP) compare3
index 5891b67b69749f6c122fcc0e851cc244fb655adc..98bd03e9a33177215cf3bad0fdddcd79051fa93b 100644 (file)
@@ -1821,6 +1821,9 @@ do-clean: clean-stage[+id+]
 # only possibility, but now it conflicts with no-bootstrap rules
 @if gcc-bootstrap
 [+ IF compare-target +]
+# Run the comparisons in parallel through a generated sub-makefile under
+# the jobserver.  Each failing recipe writes a separate result shard that
+# is collected after the sub-make finishes.
 [+compare-target+]:
        @r=`${PWD_COMMAND}`; export r; \
        s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
@@ -1829,29 +1832,52 @@ do-clean: clean-stage[+id+]
          exit 0; \
        fi; \
        : $(MAKE); $(stage); \
-       rm -f .bad_compare; \
+       compare_id=$$$$; \
+       bad_compare=.bad_compare.$$compare_id; export bad_compare; \
+       compare_makefile=[+compare-target+].$$compare_id.mk; \
+       trap 'st=$$?; rm -f "$$compare_makefile" "$$bad_compare" \
+         "$$bad_compare".*; trap - 0; exit $$st' 0; \
+       trap 'exit 1' 1 2 3 15; \
        echo Comparing stages [+prev+] and [+id+]; \
         sed=`echo stage[+id+] | sed 's,^stage,,;s,.,.,g'`; \
        files=`find stage[+id+]-* -name "*$(objext)" -print | \
                 sed -n s,^stage$$sed-,,p`; \
-       for file in $${files} ${extra-compare}; do \
-         f1=$$r/stage[+prev+]-$$file; f2=$$r/stage[+id+]-$$file; \
-         if test ! -f $$f1; then continue; fi; \
-         $(do-[+compare-target+]) > /dev/null 2>&1; \
-         if test $$? -eq 1; then \
-           case $$file in \
-             @compare_exclusions@) \
-               echo warning: $$file differs ;; \
-             *) \
-               echo $$file differs >> .bad_compare ;; \
-           esac; \
-         fi; \
-       done; \
-       if [ -f .bad_compare ]; then \
+       cmp_raw='$(do-[+compare-target+])'; \
+       { \
+         echo 'all:'; \
+         echo '.PHONY: all FORCE'; \
+         echo 'FORCE:'; \
+         printf '[+compare-target+]/%%: FORCE ; @'; \
+         printf 'f1=$$$$r/stage[+prev+]-$$*; '; \
+         printf 'f2=$$$$r/stage[+id+]-$$*; '; \
+         printf '%s' "$$cmp_raw" | sed 's,\$$,$$$$,g'; \
+         printf ' > /dev/null 2>&1; st=$$$$?; '; \
+         printf 'if test $$$$st -eq 1; then '; \
+         printf 'case $$* in '; \
+         printf '@compare_exclusions@) echo warning: $$* differs ;; '; \
+         printf '*) echo $$* differs >> "$$$$bad_compare.$$$$$$$$" ;; '; \
+         printf 'esac; '; \
+         printf 'elif test $$$$st -ne 0; then '; \
+         printf 'echo "$$* compare: error status $$$$st" '; \
+         printf '>> "$$$$bad_compare.$$$$$$$$"; fi\n'; \
+         for file in $${files} ${extra-compare}; do \
+           if test ! -f $$r/stage[+prev+]-$$file; then continue; fi; \
+           echo "all: [+compare-target+]/$$file"; \
+         done; \
+       } > "$$compare_makefile"; \
+       $(MAKE) -s -f "$$compare_makefile" all; compare_status=$$?; \
+       if test $$compare_status -ne 0; then \
+         exit $$compare_status; \
+       fi; \
+       set -- "$$bad_compare".*; \
+       if test -f "$$1"; then \
          echo "Bootstrap comparison failure!"; \
-         cat .bad_compare; \
+         LC_ALL=C sort "$$bad_compare".* > "$$bad_compare" || exit 1; \
+         cat "$$bad_compare"; \
+         mv -f "$$bad_compare" .bad_compare; \
          exit 1; \
        else \
+         rm -f .bad_compare; \
          echo Comparison successful.; \
        fi; \
        $(STAMP) [+compare-target+][+ IF prev +]
index ef5468bdd3560aed133cc31c943e36ea57c7bd9a..5547cbeb9b8fdabe28568dac71d63d58ceb63744 100644 (file)
@@ -477,7 +477,16 @@ AC_DEFUN([ACX_PROG_CMP_IGNORE_INITIAL],
 [AC_CACHE_CHECK([how to compare bootstrapped objects], gcc_cv_prog_cmp_skip,
 [ echo abfoo >t1
   echo cdfoo >t2
-  gcc_cv_prog_cmp_skip='tail -c +17 $$f1 > tmp-foo1; tail -c +17 $$f2 > tmp-foo2; cmp tmp-foo1 tmp-foo2'
+  gcc_cv_prog_cmp_skip='(trap "st=\$$?; rm -f tmp-foo1.$$$$ '
+  gcc_cv_prog_cmp_skip="$gcc_cv_prog_cmp_skip"'tmp-foo2.$$$$; '
+  gcc_cv_prog_cmp_skip="$gcc_cv_prog_cmp_skip"'trap - 0; exit \$$st" 0; '
+  gcc_cv_prog_cmp_skip="$gcc_cv_prog_cmp_skip"'trap "exit 2" 1 2 3 15; '
+  gcc_cv_prog_cmp_skip="$gcc_cv_prog_cmp_skip"'tail -c +17 $$f1 '
+  gcc_cv_prog_cmp_skip="$gcc_cv_prog_cmp_skip"'> tmp-foo1.$$$$ || exit 2; '
+  gcc_cv_prog_cmp_skip="$gcc_cv_prog_cmp_skip"'tail -c +17 $$f2 '
+  gcc_cv_prog_cmp_skip="$gcc_cv_prog_cmp_skip"'> tmp-foo2.$$$$ || exit 2; '
+  gcc_cv_prog_cmp_skip="$gcc_cv_prog_cmp_skip"'cmp tmp-foo1.$$$$ '
+  gcc_cv_prog_cmp_skip="$gcc_cv_prog_cmp_skip"'tmp-foo2.$$$$)'
   if cmp t1 t2 2 2 > /dev/null 2>&1; then
     if cmp t1 t2 1 1 > /dev/null 2>&1; then
       :
index ad490083741ea0d59473e6604fa7547adde69c76..4db0edbdccdb5f3192b843e035d2dc3d5badaf2b 100755 (executable)
--- a/configure
+++ b/configure
@@ -6123,7 +6123,16 @@ if ${gcc_cv_prog_cmp_skip+:} false; then :
 else
    echo abfoo >t1
   echo cdfoo >t2
-  gcc_cv_prog_cmp_skip='tail -c +17 $$f1 > tmp-foo1; tail -c +17 $$f2 > tmp-foo2; cmp tmp-foo1 tmp-foo2'
+  gcc_cv_prog_cmp_skip='(trap "st=\$$?; rm -f tmp-foo1.$$$$ '
+  gcc_cv_prog_cmp_skip="$gcc_cv_prog_cmp_skip"'tmp-foo2.$$$$; '
+  gcc_cv_prog_cmp_skip="$gcc_cv_prog_cmp_skip"'trap - 0; exit \$$st" 0; '
+  gcc_cv_prog_cmp_skip="$gcc_cv_prog_cmp_skip"'trap "exit 2" 1 2 3 15; '
+  gcc_cv_prog_cmp_skip="$gcc_cv_prog_cmp_skip"'tail -c +17 $$f1 '
+  gcc_cv_prog_cmp_skip="$gcc_cv_prog_cmp_skip"'> tmp-foo1.$$$$ || exit 2; '
+  gcc_cv_prog_cmp_skip="$gcc_cv_prog_cmp_skip"'tail -c +17 $$f2 '
+  gcc_cv_prog_cmp_skip="$gcc_cv_prog_cmp_skip"'> tmp-foo2.$$$$ || exit 2; '
+  gcc_cv_prog_cmp_skip="$gcc_cv_prog_cmp_skip"'cmp tmp-foo1.$$$$ '
+  gcc_cv_prog_cmp_skip="$gcc_cv_prog_cmp_skip"'tmp-foo2.$$$$)'
   if cmp t1 t2 2 2 > /dev/null 2>&1; then
     if cmp t1 t2 1 1 > /dev/null 2>&1; then
       :