Live data from Hacker News

Undefined Behavior in C and C++ (2024)

russellw.github.io

131–140 of 234 posts

Re: Undefined Behavior in C and C++ (2024)

#131
post #80

Earlier quoted context omitted.

Pointer provenance already existed before, but the standards were contradictory and incomplete. This is an effort to more rigorously nail down the semantics. i.e., the UB already existed, but it was not explicit had to be inferred from the whole text and the boundaries were fuzzy. Remember that anything not explicitly defined by the standard, is implicitly undefined. Also remember, just because you can legally constr…

The current standard still says integer-to-pointer conversions are implementation defined ( not undefined) and furthermore "intended to be consistent with the addressing structure of the execution environment" (that's a direct quote). I have an execution environment, Wasm, where doing this is pretty well defined, in fact. So if I want to read the memory at address 12345, which is within bounds of the linear memory (a…

It is important to understand why undefined behaviour has proliferated over the past ~25 years. Compiler developers are (like the rest of us) under pressure to improve metrics like the performance of compiled code. Often enough that's because a CPU vendor is the one paying for the work and has a particular target they need to reach at time of product launch, or there's a new optimization being implemented that has to be justified as showing a benefit on existing code.

The performance of compilers is frequently measured using the SPEC series of CPU benchmarks, and one of the main constraints of the series SPEC series of tests is that the source code of the benchmark cannot be changed. It is static.

As a result, compiler authors have to find increasingly convoluted ways to make it possible for various new compiler optimizations to be applied to the legacy code used in SPEC. Take 403.gcc: it's based on gcc version 3.2 which was released on August 14th 2002 -- nearly 23 years ago.

By making certain code patterns undefined behaviour, compiler developers are able to relax the constraints and allow various optimizations to be applied to legacy code in places which would not otherwise be possible. I believe the gcc optimization to eliminate NULL pointer checks when the pointer is dereferenced was motivated by such a scenario.

In the real world code tends to get updated when compilers are updated, or when performance optimizations are made, so there is no need for excessive compiler "heroics" to weasel its way into making optimizations apply via undefined behaviour. So long as SPEC is used to measure compiler performance using static and unchanging legacy code, we will continue to see compiler developers committing undefined behaviour madness.

The only way around this is for non-compiler developer folks to force language standards to prevent compilers from using undefined behaviour to do that which normal software developers considers to be utterly insane code transformations.

Re: Undefined Behavior in C and C++ (2024)

#132
post #47

Earlier quoted context omitted.

Basically all of those problems originate with the tradition of conflating pointers and object identity, which is a problem in Rust as soon as you have ambiguous ownership or incongruent access patterns. It's also very often not the best way to identify objects, for many reasons, including performance (spatial locality is a big deal). These problems go away almost completely by simply using `EntityID` and going throu…

The video I linked to is long but goes through all of this. Pretty much nobody writing games in C++ uses raw pointers in entities to hold references to other related entities, because entities can be destroyed at any time and there's no simple way for a referring entity to know when a referenced entity is destroyed. Using some sort of entity ID or entity handle is very common in C++, the problem is that when implemen…

Can you give an example of what problems need workarounds here?

The benefits seem pretty massive, at least on the surface. For example, you can run any system that only takes `&World` (i.e., immutable access) in parallel without breaking a sweat.

Re: Undefined Behavior in C and C++ (2024)

#133

Earlier quoted context omitted.

Pacman is extremely good, too, for C. :)

Pacman solves a different problem. Cargo manages your project's dependencies, not system packages.

I know, but often that is all you need for C.

Re: Undefined Behavior in C and C++ (2024)

#134

Earlier quoted context omitted.

There is no C or C++ ways. It's widely known that every codebase is its own dialect.

There are lots of C and particularly C++ ways, but you're still restricted. Want to use methods in C: nope, you can't. Want language-level tagged unions and pattern matching in either language: nope. Same for guaranteed tail call optimisation and a bunch of other things. This is especially true for C which supports almost nothing (it doesn't even have a sensible array type!). But is also true for C++: while it suppor…

what changes, in your opinion, would need to be made to the C array type to make it "sensible"? C's array is simplistic, but I don't think it's not "sensible"...

Re: Undefined Behavior in C and C++ (2024)

#135
post #105

Earlier quoted context omitted.

No, it's absolutely because of optimization. For instance, C++20 defined signed integer representation as having two's complement, but signed integer overflow is still undefined behaviour. The reason is that if you compile with flags that make it defined, you lose a few percentage points of performance (primarily from preventing loop unrolling and auto-vectorization). Same thing with e.g. strict aliasing or the vario…

I wish there was a way to opt into undefined behavior for unsigned overflow. Its rare that wraparound is actually what you want and in many cases overflow is still a bug. Sucks to have to either miss out on potential optimizations or miss out on the guarantee that the value can't be negative.

I recently filed a bug for this: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=116193

Re: Undefined Behavior in C and C++ (2024)

#136
post #10

Earlier quoted context omitted.

> Rusts tooling is hands down better than C/++ which aids to a more streamlined and efficient development experience Would you expand on this? What was your C tooling/workflow that was inferior to your new Rust experience?

Not the GP, but the biggest one is dependency management. Cargo is just extremely good. As for the language tooling itself, static and runtime analyzers in C and C++ (and these are table stakes at this point) do not come close to the level of accuracy of the Rust compiler. If you care about writing unsafe code, Miri is orders of magnitude better at detecting UB than any runtime analyzer I've seen for C and C++.

I do not think package management should be done at the level of programming languages.

Re: Undefined Behavior in C and C++ (2024)

#137

Earlier quoted context omitted.

> nonescaping locals don’t get addresses inlining, interprocedural optimizations. For example, something as an trivial accessor member function would be hard to optimize.

Inlining doesn’t require UB

I didn't claim that. What I mean is that if a pointer escapes into an inlined function and no further, it will still prevent further optimizations if we apply your rule that only non-escaping locals don't get addresses. The main benefit of inlining is that it is effectively a simple way to do interprocedurally optimizations. I.e.

  inline void add(int* to, int what) { *to += what; }
  void foo();
  void bar() {
      int x = 0;
      add(&x, 1);
      foo();
      return x;
  }
By your rules, optimizing bar to return the constant 1 would not be allowed.

Re: Undefined Behavior in C and C++ (2024)

#138
post #131
post #80

Earlier quoted context omitted.

The current standard still says integer-to-pointer conversions are implementation defined ( not undefined) and furthermore "intended to be consistent with the addressing structure of the execution environment" (that's a direct quote). I have an execution environment, Wasm, where doing this is pretty well defined, in fact. So if I want to read the memory at address 12345, which is within bounds of the linear memory (a…

It is important to understand why undefined behaviour has proliferated over the past ~25 years. Compiler developers are (like the rest of us) under pressure to improve metrics like the performance of compiled code. Often enough that's because a CPU vendor is the one paying for the work and has a particular target they need to reach at time of product launch, or there's a new optimization being implemented that has to…

Dr. Dobbs used to have articles with those benchmarks, here are a couple of examples,

https://dl.acm.org/doi/10.5555/11616.11617

https://jacobfilipp.com/DrDobbs/articles/DDJ/1991/9108/9108h...

Re: Undefined Behavior in C and C++ (2024)

#139
post #65

Earlier quoted context omitted.

Compilers existing is essential and not trivial (and also usually then what other languages build on). The conformance model of C also allows you to write programs that are portable without change to different platforms. This is possible, my software runs on 20 different architectures without change. That one can then also adopt it to make use of specific features of different platforms is quite natural in my opinion…

It is essential and nontrivial, but it's also the extremely bare minimum. You cannot write portable code without platform-specific and even environment-specific adaptations, like handling the presence of certain headers (looking at you, stdint.h and stddef.h), and let's not even start about interacting with the OS in any way.

There may be platforms that are not conforming to the C standard. But I doubt those then have comprehensive implementations of other languages either.

Re: Undefined Behavior in C and C++ (2024)

#140
post #123

Earlier quoted context omitted.

Ah yes, the good old "compiler writers only care about benchmarks and are out to hurt everyone else" nonsense. I for one am glad that compilers can assume that things that can't happen according to the language do in fact not happen and don't bloat my programs with code to handle them.

Moral hazard here. The rest of us, and all of society, now rests on a huge pile of code written by incorrigible misers who imagined themselves able to write perfect, bug-free code that would go infinitely fast because bad things never happen. But see, there's bugs in your code and other people pay the cost.

There's bugs in your code without undefined behavior too. Go use a different language if you don't care about performance, there are many to choose from.
Post reply on HN