| Related articles |
|---|
| Undefined behaviour in C23 mwardgkc@gmail.com (Martin Ward) (2025-08-20) |
| Re: Undefined behaviour in C23 643-408-1753@kylheku.com (Kaz Kylheku) (2025-08-20) |
| Re: Undefined behaviour in C23 anton@mips.complang.tuwien.ac.at (2025-08-21) |
| Re: Undefined behaviour in C23 david.brown@hesbynett.no (David Brown) (2025-08-21) |
| Re: Undefined behaviour in C23 mwardgkc@gmail.com (Martin Ward) (2025-08-21) |
| Re: Undefined behaviour in C23 Keith.S.Thompson+u@gmail.com (Keith Thompson) (2025-08-21) |
| Re: Undefined behaviour in C23 david.brown@hesbynett.no (David Brown) (2025-08-22) |
| Re: Undefined behaviour in C23 david.brown@hesbynett.no (David Brown) (2025-08-22) |
| Re: Undefined behaviour in C23 anton@mips.complang.tuwien.ac.at (2025-08-22) |
| Re: Undefined behaviour in C23 Keith.S.Thompson+u@gmail.com (Keith Thompson) (2025-08-22) |
| Re: Undefined behaviour in C23 david.brown@hesbynett.no (David Brown) (2025-08-23) |
| Re: Undefined behaviour in C23 antispam@fricas.org (2025-08-23) |
| Re: Undefined behaviour in C23 Keith.S.Thompson+u@gmail.com (Keith Thompson) (2025-08-23) |
| Re: Undefined behaviour in C23 jameskuyper@alumni.caltech.edu (James Kuyper) (2025-08-25) |
| [4 later articles] |
| From: | David Brown <david.brown@hesbynett.no> |
| Newsgroups: | comp.compilers |
| Date: | Fri, 22 Aug 2025 18:42:01 +0200 |
| Organization: | Compilers Central |
| References: | 25-08-002 25-08-004 |
| Injection-Info: | gal.iecc.com; posting-host="news.iecc.com:2001:470:1f07:1126:0:676f:7373:6970"; logging-data="72163"; mail-complaints-to="abuse@iecc.com" |
| Keywords: | C, standards |
| Posted-Date: | 22 Aug 2025 13:12:58 EDT |
| In-Reply-To: | 25-08-004 |
On 21/08/2025 07:44, anton@mips.complang.tuwien.ac.at wrote:
> Martin Ward <mwardgkc@gmail.com> writes:
> [actually, John Levine writes:]
>> [When a language is 50 years old and there is a mountain of legacy code that
>> they really don't want to break, it accumulates a lot of cruft.
>
> But there is a very vocal group of people who argue that programs that
> exercise undefined behaviour are already broken (and they often use
> stronger words that that) and that compilers are allowed to (and
> should) compile them to code that behaves differently than earlier
> compilers that the new compiler supposedly is just a new version of.
Yes.
It is good that compilers often support ways to get the "old" behaviour
if the user wants. But new compiler versions should not be held back by
the limitations of old compilers - that would stifle progress. Imagine
if car manufacturers had to limit the speeds of new cars to 10 miles per
hour, because some drivers a century ago assumed that they could safely
put their foot flat on the accelerator without hitting the horse and
cart in front of them.
And also remember that broken code is not necessarily useless code. For
programs of reasonable size, very few are completely bug-free. And yet
we still manage to use them if they are good enough, despite being
imperfect.
>
> So according to this argument, when something that the legacy code
> does is declared as undefined behaviour, this breaks this program.
>
> And the practice is that the people in C compiler maintenance reject
> bug reports as RESOLVED INVALID when the code exercises undefined
> behaviour, even when the code works as intended in earlier versions of
> the compiler and when the breakage could be easily fixed (e.g., for
> <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66804> and
> <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65709> by using movdqu
> instead of movdqa).
>
I don't see any problem with these being marked as "resolved invalid".
There is definitely a challenge in writing C code that is maximally
efficient on a wide variety of compilers - old and new, powerfully
optimising and weakly optimising, and for different target
architectures. C code can't always be written in an ideal and fully
portable way. This can be handled by using abstractions, compiler
detection and conditional compilation for things like block copies - use
unaligned non-conforming large moves if you know it is safe on a
particular implementation, and fall back to safe but possibly slow
memcpy() (or memmove()) in general. That kind of solution, of course,
has its own disadvantages in development time, code complexity, testing,
etc. The C programming world is not perfect.
But the solution is certainly /not/ to say that people everyone correct
C code and compiling with high optimisations should get slower results
because someone else previously wrote code that made unwarranted and
unchecked assumptions about particular compilers and particular target
processors.
> But they not always do so: The SATD function from the SPEC benchmark
> 464.h264ref exercises undefined behaviour, and a pre-release version
> of gcc-4.8 generated code that did not behave as intended. The
> release version of gcc-4.8 compiled 464.h264ref as intended (but later
> a similar case that was not in a SPEC program
> <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66875> was rejected as
> RESOLVED INVALID).
So the gcc developers made an exception for a particularly important,
useful and common case? Again that doesn't sound unreasonable to me.
Sometimes there are trade-offs - some code is more important than other
code. The C compiler development world is not perfect either.
> When I brought this up, the reactions reached from
> flat-out denial that it ever happened (despite it being widely
> publicized <https://lwn.net/Articles/544123/>) through a claim that
> the "optimization" turned out to have no benefit (and yet the similar
> case mentioned above still was "optimized" in a later gcc version) to
> a statement along the lines that 464.h264ref is a relevant benchmark.
>
Maybe this particular case was handled badly, or at least the
communications involved were bad. It was over a decade ago, in a
pre-release candidate compiler. (Pre-release candidates are used
precisely to check if changes cause trouble with real-world code.) How
long are you going to hold a grudge about this?
> The last reaction seems to be the most plausible to me. The people
> working on the optimizers tend to evaluate their performance on a
> number of benchmarks, i.e., "relevant benchmarks", and of course these
> benchmarks must be compiled as intended, so that's what happens. My
> guess is that "relevant benchmarks" are industry standard benchmarks
> like SPEC, but also programs coming from paying customers.
>
For gcc, a typical benchmark is the Linux kernel and a wide selection of
open source programs - people do full rebuilds of whole Linux
distributions as part of testing before full compiler releases. SPEC is
a special case, because these programs are used on a wide variety of C
compilers for comparison between toolchains, not just for one compiler.
> They also have their test suites of programs for regression testing,
> and any behavioural change in these programs that is visible in this
> regression testing probably leads to applying the optimization in a
> less aggressive way.
>
I would assume that they try to avoid UB in their test suite code
(though of course gcc developers can have bugs and mistakes like anyone
else). Sometimes test suite code is fixed when new bugs are found in it.
> How do tests get added into the regression test suite? Ideally, if
> somebody reports a case where a program behaves in one way in an
> earlier version of the same compiler and differently in a later
> version, that program and its original behaviour should usually be
> added to the test suite
> <https://www.complang.tuwien.ac.at/papers/ertl17kps.pdf>, but in gcc
> this does not happen (see the bug reports linked to above).
In what bizarre world would that be "ideal" ?
If you want gcc 4.8 without tree optimisations, you can get it. If you
want to use gcc 15 and not enable tree optimisations, that's fine too.
If you want to write code that can be highly optimised with automatic
generation of vector instructions that only work on aligned data, don't
faff around going out of your way to write bad C code that messes with
pointer types to create unaligned accesses. It's your choice. Let the
rest of us that get our data alignment correct (and you get that
naturally in C - you only get the UB if you've played silly buggers with
pointer casts to write "smart" code) get faster results.
Examples can be added to the test suite if they are useful. They can be
added to test new features - there is no point in a test to see if the
compiler generates code that matches an old compiler version unless
there is a specific new feature flag to give defined semantics matching
the old behaviour. (An example of that would be the
"-fno-delete-null-pointer-check" flag.)
> Apparently gcc has some other criteria for adding programs to the test
> suite.
>
> So, is C still usable when you do not maintain one of those programs
> that are considered to be relevant by C compiler maintainers? My
> experience is that the amount of breakage for the code I maintain has
> been almost non-existent in the last 15 years. A big part of that is
> that we use lots of flags to tell the compiler that certain behaviour
> is defined even if the C standard does not define it.
That sounds like you have a solution to your problem.
> Currently we
> try the following flags with the versions of gcc or clang that support
> them:
>
> -fno-gcse -fcaller-saves -fno-defer-pop -fno-inline -fwrapv
> -fchar-unsigned -fno-strict-aliasing -fno-cse-follow-jumps
> -fno-reorder-blocks -fno-reorder-blocks-and-partition
> -fno-toplevel-reorder -fno-trigraphs -falign-labels=1 -falign-loops=1
> -falign-jumps=1 -fno-delete-null-pointer-checks -fcf-protection=none
> -fno-tree-vectorize -mllvm=--tail-dup-indirect-size=0
>
That sounds like you have code that uses a great deal of UB and relies
on a wide range of very specific code generation and semantics that are
not defined anywhere, in the C standards or compiler documentation.
Maybe that's what you need for your projects, and if it works for you,
fine. But it is a very unusual situation, and cannot be extrapolated to
a non-negligible amount of C code. (Some flags, such as "-fwrapv" and
"-fno-strict-aliasing", are needed to counter unwarranted assumptions in
a more significant body of existing C code.)
> Some of these flags just disable certain transformations; in those
> cases there is no flag for defining the language in the way that our
> program relies on, but only the optimization transforms it in a way
> that is contrary to our intentions. In other cases, in particular
> -fno-tree-vectorize, using the flag just avoids slowdowns from the
> "optimization".
You know better than the solid majority of programmers that
"optimisation" is as much an art as a science, and that getting the best
from a combination of code, compiler and target processor is no simple
task. Compilers enable optimisations in groups (-O1, -O2, -O3, etc.)
based on what usually gives better results for a range of code bases and
a range of target devices - there are no guarantees for any particular
combination.
>
> Another big part of the lack of breakage experience is probably the
> code in the regression tests of the compiler, whatever the criteria
> are used for including this code. I.e., our code rides in the
> slipstream of this code.
>
>> On the other hand, there's the python approach in which they deprecate and
>> remove little used and crufty features, but old python code doesn't work any
>> more unless you go back and update it every year or two. -John]
>
> Is it so bad with Python? From what I read, after the huge problems
> that Python had with migrating the existing code base from Python2 to
> Python3 (where Python3 was intentionally not backwards compatible with
> Python2), they had decided not to make backwards-incompatible changes
> to the language in the future.
>
IME there are only sometimes issues with new Python versions, but the
Python 2 to 3 incompatibilities are still a widespread problem.
Return to the
comp.compilers page.
Search the
comp.compilers archives again.