|
|
Log in / Subscribe / Register

Rust and GCC, two different ways

Rust and GCC, two different ways

Posted Oct 4, 2021 17:03 UTC (Mon) by eru (subscriber, #2753)
Parent article: Rust and GCC, two different ways

A benefit not mentioned is that a truly independent reimplementation helps expose ambiguities in the definition of the language, and ensures the future of the language is not tied to just one implementation project, a consideration if you plan on using it in a long-lived software project.


to post comments

Rust and GCC, two different ways

Posted Oct 6, 2021 7:37 UTC (Wed) by marcH (subscriber, #57642) [Link] (10 responses)

Useful but I guess nowhere near as useful as using different compilers for C which is full of undefined and implementation defined behaviors.

Rust and GCC, two different ways

Posted Oct 9, 2021 4:52 UTC (Sat) by ncm (guest, #165) [Link] (9 responses)

You don't learn how many unspecified and ill-specified details your language spec really implies until after somebody attempts an independent compiler for it. So, we just don't know that yet.

Rust and GCC, two different ways

Posted Oct 9, 2021 9:21 UTC (Sat) by marcH (subscriber, #57642) [Link] (8 responses)

We already know that the people who designed Rust carefully avoided all the C ones they already knew about.

Also, you generally don't need multiple compilers to notice _undefined_ behaviors, A single, widely used compiler is often enough to notice that the same causes don't always produce the same effects.

Rust and GCC, two different ways

Posted Oct 11, 2021 6:46 UTC (Mon) by eru (subscriber, #2753) [Link] (7 responses)

Undefined behaviour does not always mean "random" behaviour. The same compiler is likely to do some things in the same way, regardless of target, even though your language definition might say the result is undefined, or fails to define it. For example, suppose the spec of the language with just one implementation does not say in which order function parameters are evaluated. So it is is undefined, although not explicitly. But as it happens, the compiler always evaluates them left-to-right, due to the decisions of its implementer. Lots of code is then written that works fine with this compiler.

Then along comes another, independent implementation, whose author has seen it useful to sometimes use another evaluation order. It is 100% certain that some of the code that ran with the original compiler fails with the new because of this, even though both follow the spec.

Rust and GCC, two different ways

Posted Oct 11, 2021 8:15 UTC (Mon) by marcH (subscriber, #57642) [Link]

> But as it happens, the compiler always evaluates them left-to-right, due to the decisions of its implementer

Except re-ordering stuff when allowed is an optimization favorite.

I totally get your point but I suspect your example is not great and that "good" examples are fairly rare.

Rust and GCC, two different ways

Posted Oct 11, 2021 10:08 UTC (Mon) by Wol (subscriber, #4433) [Link] (5 responses)

Which is why "undefined behaviour" should NOT be a thing. It's fine for the spec to delegate the definition elsewhere, it's not fine for the spec to allow the compiler writer to do any random thing.

For example, is the size of a byte defined? It wasn't always 8 bits. The spec should be "the most convenient size for the processor to store a Western character. A minimum of 6 so the Roman Alphabet and numbers fit, a maximum of 11 so no two compilers can argue how many bits in a byte". Obviously, modern x86 architecture that's 8 bits. ICL I believe was 6. Some other architectures were 9 iirc. But the point is, you can then pretty much guarantee any compiler on a specific hardware is going to agree.

Or you offload the default decision onto the compiler - "the compiler must implement any sane understanding consistently". No guarantees that different compilers do the same thing, but it's a breach of the spec for compilers to change their implementation without warning.

The quicker C (and C++?) ditch the concept of "undefined behaviour" - even if they just replace it with "defined elsewhere" - the better.

Cheers,
Wol

Rust and GCC, two different ways

Posted Oct 11, 2021 12:15 UTC (Mon) by farnz (subscriber, #17727) [Link] (4 responses)

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.

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.

Rust and GCC, two different ways

Posted Oct 7, 2021 16:12 UTC (Thu) by wtarreau (subscriber, #51152) [Link]

It will in fact make it a language. Right now until there's only one implemntation, it's only a compiler for its own, self-defined, dialect. It's possible that work on some areas will be boosted by this and that others will slow down because instead of focusing on how things can be done in the existing compiler they will have to think about whether or not it makes sense in general. It's always harder.

Rust and GCC, two different ways

Posted Oct 9, 2021 4:58 UTC (Sat) by ncm (guest, #165) [Link] (2 responses)

Another potential benefit is that the Gccrs implementation has a chance to be much, much faster than rustc. That is not to say it will be, but there is enormous room for such improvement. The monumental slowness of the rustc compiler probably keeps it out of more production workflows than does its limited target-platform set.

Rust and GCC, two different ways

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

Bear in mind that they're already working on a Cranelift backend for it to speed up debug builds.

Rust and GCC, two different ways

Posted Oct 11, 2021 16:51 UTC (Mon) by moltonel (subscriber, #45207) [Link]

I think you underestimate how optimized rustc (the frontend) already is. Its performance is monitored very closely, and the gains indicate that the remaining room for improvement is small. It has very advanced incremental (re)compilation support, and is thoroughly multithreaded. The gccrs frontend will have a hard enough time catching up to and keeping up with rustc features, I wouldn't count on it keeping up with the optimization work.

Most of the time is spent in the backend, which is why rustc's cranelift backend (which predates rustc_codegen_gcc and focuses on quick but unoptimized compilation) is very interesting for debug builds. There are surely going to be differences between the GCC and LLVM backends, but judging by the Clang vs Gcc benchmark for C and C++, there's not going to be one clear winner. Same goes for the speed of the compiled code.

Rust is a complex language, it's never going to compile as fast as Go. It compiles about as fast as C++ (comparisons are hard, beware of apples-to-oranges), which is what you can hope for.


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