From 71263f0d2d76ab43cb25210aabd7e603330e17f3 Mon Sep 17 00:00:00 2001 From: Ismael Luceno Date: Fri, 21 Aug 2026 14:42:52 +0200 Subject: [PATCH] preprocessor: Cap -E column padding at column 200 [PR126913] The first token of a non-directive output line is padded out to the column it had in the source, "so the output is easy to read". do_line_change is also reached mid-line, from scan_translation_unit, whenever a linemarker has to be emitted -- which happens on every expansion context switch, notably for an object-like macro from a system header. A line carrying M such macros pays the column cost M times, so the output grows as the square of the line length: #include static const _Bool a[] = {true,true,...,0}; N=1000 src=5055 -E=5093292 N=8000 src=40055 -E=320744292 40 KB of source, 320 MB of output, ~99.9% spaces. Nobody reads column 287,000, and -E output is overwhelmingly machine input now. Cap the padding at column 200 and emit none past it, leaving the separating space scan_translation_unit already supplies; truncating the run would be as misleading and far more expensive. The reproducer becomes linear, 320 MB -> 280 KB at N=8000. Preprocessing 60 libiberty translation units and the 407 tests in gcc.dg/cpp is byte-identical to before. PR preprocessor/126913 gcc/c-family/ChangeLog: * c-ppoutput.cc (MAX_PADDING_COLUMN): New macro. (do_line_change): Emit no column padding past MAX_PADDING_COLUMN. gcc/ChangeLog: * doc/cpp.texi (Preprocessor Output): Document the cap. (Implementation-defined behavior): Likewise. gcc/testsuite/ChangeLog: * gcc.dg/cpp/pr126913.c: New test. * gcc.dg/cpp/pr126913.h: New test header. Upstream-Status: Submitted [https://gcc.gnu.org/bugzilla/show_bug.cgi?id=126913] Signed-off-by: Ismael Luceno --- gcc/c-family/c-ppoutput.cc | 8 ++++++++ gcc/doc/cpp.texi | 5 +++-- gcc/testsuite/gcc.dg/cpp/pr126913.c | 21 +++++++++++++++++++++ gcc/testsuite/gcc.dg/cpp/pr126913.h | 5 +++++ 4 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/cpp/pr126913.c create mode 100644 gcc/testsuite/gcc.dg/cpp/pr126913.h diff --git a/gcc/c-family/c-ppoutput.cc b/gcc/c-family/c-ppoutput.cc index a8c8a021503e..74558c22c59d 100644 --- a/gcc/c-family/c-ppoutput.cc +++ b/gcc/c-family/c-ppoutput.cc @@ -44,6 +44,8 @@ static struct object. */ } print; +#define MAX_PADDING_COLUMN 200 + /* Defined and undefined macros being queued for output with -dU at the next newline. */ struct macro_queue @@ -667,6 +669,9 @@ do_line_change (cpp_reader *pfile, const cpp_token *token, reconstruct tabs; we can't get it right in general, and nothing ought to care. Some things do care; the fault lies with them. + Past MAX_PADDING_COLUMN drop the padding rather than truncate it: a + partial run would be as misleading and far more expensive. + Also do not output the spaces if this is a CPP_PRAGMA token. In this case, libcpp has provided the location of the first token after #pragma, so we would start at the wrong column. */ @@ -675,6 +680,9 @@ do_line_change (cpp_reader *pfile, const cpp_token *token, int spaces = LOCATION_COLUMN (src_loc) - 2; print.printed = true; + if (spaces > MAX_PADDING_COLUMN - 2) + spaces = 0; + while (-- spaces >= 0) putc (' ', print.outf); } diff --git a/gcc/doc/cpp.texi b/gcc/doc/cpp.texi index 859b5395047a..2fce5889e84a 100644 --- a/gcc/doc/cpp.texi +++ b/gcc/doc/cpp.texi @@ -4050,7 +4050,8 @@ e.g.@: a single space. In GNU CPP, whitespace between tokens is collapsed to become a single space, with the exception that the first token on a non-directive line is preceded with sufficient spaces that it appears in the same column in the preprocessed output that it appeared in the -original source file. This is so the output is easy to read. +original source file. This is so the output is easy to read. A token +past column 200 is not padded at all. CPP does not insert any whitespace where there was none in the original source, except where necessary to prevent an accidental token paste. @@ -4443,7 +4444,7 @@ You can override the default with @option{-fdollars-in-identifiers} or In textual output, each whitespace sequence is collapsed to a single space. For aesthetic reasons, the first token on each non-directive line of output is preceded with sufficient spaces that it appears in the -same column as it did in the original source file. +same column as it did in the original source file, up to column 200. @item The numeric value of character constants in preprocessor expressions. diff --git a/gcc/testsuite/gcc.dg/cpp/pr126913.c b/gcc/testsuite/gcc.dg/cpp/pr126913.c new file mode 100644 index 000000000000..9876f158ba25 --- /dev/null +++ b/gcc/testsuite/gcc.dg/cpp/pr126913.c @@ -0,0 +1,21 @@ +/* PR preprocessor/126913: column padding is capped, so a long line does + not cost O(columns) bytes for every macro expanded on it. */ + +/* { dg-do preprocess } */ +/* { dg-options "" } */ + +#include "pr126913.h" + +/* Column 21, under the cap: padded as before. */ + int under_cap; + +/* Column 261, past the cap: only the separating space. */ + int over_cap; + +/* Each expansion restarts the padding from its own column; this line + alone used to emit tens of kilobytes of spaces. */ +static const int a[] = {PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,PR126913_TRUE,0}; + +/* { dg-final { scan-file pr126913.i "\n {20}int under_cap;" } } */ +/* { dg-final { scan-file pr126913.i "\n int over_cap;" } } */ +/* { dg-final { scan-file-not pr126913.i " {200}" } } */ diff --git a/gcc/testsuite/gcc.dg/cpp/pr126913.h b/gcc/testsuite/gcc.dg/cpp/pr126913.h new file mode 100644 index 000000000000..94dca812dea1 --- /dev/null +++ b/gcc/testsuite/gcc.dg/cpp/pr126913.h @@ -0,0 +1,5 @@ +#pragma GCC system_header + +/* Expanding this mid-line switches expansion context, which emits a + linemarker pair and restarts the column padding. */ +#define PR126913_TRUE 1 .