|
|
Log in / Subscribe / Register

Rust and GCC, two different ways

Rust and GCC, two different ways

Posted Oct 11, 2021 12:15 UTC (Mon) by farnz (subscriber, #17727)
In reply to: Rust and GCC, two different ways by Wol
Parent article: Rust and GCC, two different ways

I disagree with you on ditching UB as a concept; UB exists in hardware, too, and ignoring that is a road to extreme pain. For example, JEDEC DDR says that reading a DRAM bit cell returns a 1 or a 0, and that the value you read will be the value most recently written to that cell. But it says nothing about what happens if you read the cell more than once before it's written, or what the cell contains on power on.

Many CPUs in the past did not define what happens if you execute an undefined instruction - e.g. instruction DD on a Motorola 6800. If you do manage to execute such an instruction, you literally don't know what the system will do. While modern CPUs "shouldn't" have similar instructions, I suspect that there are errata for some CPUs that mean that they do.

Similarly, data races between two harts are genuinely nondeterministic from the program's point of view. The exact winner of a data race if two harts try to write to the same bit cell on the same clock cycle depends on environmental conditions that influence the winner of the arbitration protocol.

Given that you have to handle UB somehow, you might as well make it exist in the language, and use it to pass on the optimizer's assumptions about meaning. Rust, for example has a reasonably tight list of things that are UB (6 items in the list, and you have to use the unsafe keyword to access features of Rust that can result in UB). There's more to go to fully define that list (notably a complete aliasing model), but it's a better place than C-style UB, which is scattered and complex.


to post comments

Rust and GCC, two different ways

Posted Oct 11, 2021 15:34 UTC (Mon) by Wol (subscriber, #4433) [Link] (3 responses)

Yep. But at least with that, C can specify "what the hardware does", which is most definitely NOT undefined as far as C is concerned.

If the hardware behaviour is undefined, that's not C's problem ... it can point out (or not, as the case may be) that what the hardware does may be random.

In your case, there's also the option of saying "The hardware behaviour is undefined, it's down to the compiler how to handle it".

So basically, I'm not saying the concept of UB should be ditched entirely - at the end of the day life is random - but the language CAN and SHOULD specify what happens at the language level (after all, it's maths, it can and should specify deterministic behaviour AT THE LANGUAGE LEVEL). If the language punts it and says "we can't guarantee what the hardware will do", then that's fine. After all, "whatever the hardware does" is deterministic to a point ...

Cheers,
Wol

Rust and GCC, two different ways

Posted Oct 11, 2021 16:34 UTC (Mon) by ssokolow (guest, #94568) [Link] (1 responses)

Based on my understanding of the terms, you two are talking about "implementation-defined behaviour".

"Undefined behaviour" is used in the mathematical sense of "undefined". It's like taking the result of dividing by zero.

As such, undefined behaviour is stuff where the compiler optimizers are allowed to transform their input based on the assumption that it can never happen.

See, for example, Why undefined behavior may call a never-called function and How undefined signed overflow enables optimizations in GCC by Krister Walfridsson.

I have a bunch of links to other resources in this reddit comment if you want more.

Rust and GCC, two different ways

Posted Oct 11, 2021 16:44 UTC (Mon) by farnz (subscriber, #17727) [Link]

Specifically, Wol is talking about making all UB implementation defined; however, there is UB that is not implementation definable other than as UB in the implementation.

Rust and GCC, two different ways

Posted Oct 11, 2021 18:16 UTC (Mon) by farnz (subscriber, #17727) [Link]

When you say "what the hardware does", what hardware (and OS - some things will trap into error handlers and be turned into something by the OS) do you mean? If you mean "any hardware and platform you can reasonably run C code on", then you're advocating for the existing position, where the things that are UB in the C standard are also UB on some real hardware - e.g. division by integer zero is UB on some CPUs that run C code).

At the other extreme, you can say that it's only UB if it's also UB on the specific platform the code is running on - but note that in the past, I've used processors with errata that have UB only if the power limit on the CPU is set below a threshold. If you go to this extreme, then everything is awful; you end up with a language definition that gives you nothing portable.

So, in practice, you want to limit what's UB and what's not UB. This is something that downstream standards from C can do if there's a need; for example, POSIX C is standard C with extra restrictions (notably that CHAR_BIT is 8, and WORD_BIT is at least 32).

The fact that no-one's created a standard that's like POSIX but with more UB defined (as defining behaviour is something that still meets UB standards for the C standard) and then persuaded both compiler authors and users to accept the use of that standard is telling.


Copyright © 2026, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds