Earlier quoted context omitted.
The problem is that c and c++ have a ridiculous amount of undefined behavior, and it is extremely difficult to avoid all of it. One of the advantages of rust is it confines any potential UB to unsafe blocks. But even in rust, which has defined behavior in a lot of places that are UB in c, if you venture into unsafe code, it is remarkable easy to accidentally run into subtle UB issues.
It’s true that UB is not intuitive at first, but “ridiculous amount” and “difficult to avoid” is overstating it. You have to have a proof-writing mindset when coding, but you do get sensitized to the pitfalls once you read up on what the language constructs actually guarantee (and don’t guarantee), and it’s not that much more difficult than, say, avoiding panics in Rust.
Clang vs. Clang
281–290 of 405 posts
Re: Clang vs. Clang
#282Compile your code with `-O0` and shut up already.
If you want your code to contain specific assembly instructions, code in assembly. Programming language by design is an abstraction of a higher level and when you use it you shouldn't care that much about actual assembly it produces.
Re: Clang vs. Clang
#283Earlier quoted context omitted.
It’s true that UB is not intuitive at first, but “ridiculous amount” and “difficult to avoid” is overstating it. You have to have a proof-writing mindset when coding, but you do get sensitized to the pitfalls once you read up on what the language constructs actually guarantee (and don’t guarantee), and it’s not that much more difficult than, say, avoiding panics in Rust.
I've spent hours debugging a memory alignment issues. Its not fun. The problem is that you don't know (at first) the full space of UB. So you spend the first 10 years of programming suffering through all kinds of weird UBs and then at the end of the pipeline claims "pftt, just git gud at it. C is perfect!".
Making sure you have no UB certainly slows you down considerably, and I strongly prefer languages that can catch all non-defined behavior statically for sure, but I don’t find C to be unmanageable.
Memory alignment issues only happen when you cast pointers from the middle of raw memory to/from other types, which, yes, is dangerous territory, and you have to know what you are doing there.
Re: Clang vs. Clang
#284Earlier quoted context omitted.
Which UB upsets you? Can you be specific so we can revert it?
All of it. But especially anything added after C89 that was not already there implicitly. Edit: okay, not all of it. I was hyperbolic. Race conditions and data races should be UB. But anything that can be implementation-defined should be.
A given compiler is free to define specific behavior for UB (and indeed you can add compiler flags to do that for many things); the standard explicitly acknowledges that with "Possible undefined behavior ranges from […], to behaving during translation or program execution in a documented manner characteristic of the environment".
Re: Clang vs. Clang
#285Earlier quoted context omitted.
What data?
TFA? Quoting: Compiler writers measure an "optimization" as successful if they can find any example where the "optimization" saves time. Does this matter for the overall user experience? The typical debate runs as follows: In 2000, Todd A. Proebsting introduced "Proebsting's Law: Compiler Advances Double Computing Power Every 18 Years" (emphasis in original) and concluded that "compiler optimization work makes only m…
-O0 gives you what you are after.
Re: Clang vs. Clang
#286Earlier quoted context omitted.
The compiler should emit the code to add one to a, and then code to check if the result is greater than a. This is completely evident, and is what all C and C++ compilers did for the first few decades of their existence. Maybe a particularly smart compiler could issue a `jo` instead of a `cmp ax, bx; jz `. The for loop example is silly. There is no reason whatsoever to add an overflow check in a for loop. The code of…
The interesting case is what should the code do if inlined on a code path where a is deduced to be INT_MAX. A compiler will just avoid inlining any code here, since it's not valid, and thus by definition that branch cannot be taken, removing cruft that would impact the instruction cache.
Note that also `return 1 The problem related to UB appears if the function is inlined in a situation where a is INT_MAX. That causes the whole branch of code to be UB, and the compiler is allowed to compile the whole context with the assumption that this didn't happen.
For example, the following function can well be compiled to print "not zero":
int foo(int x) {
if (x == 0) {
return stupid(INT_MAX);
} else {
printf("not zero");
return -1;
}
}
foo(0); //prints "not zero"
This is a valid compilation, because stupid(INT_MAX) would be UB, so it can't happen in a valid program. The only way for the program to be valid is for x to never be 0, so the `if` is superfluous and `foo` can be compiled to only have the code where UB can't happen.Eidt: Now, neither clang nor gcc seem to do this optimization. But if we replace stupid(INT_MAX) with a "worse" kind of UB, say `*(int*)NULL = 1`, then they do indeed compile the function to simply call printf [0].
Re: Clang vs. Clang
#287A point of the post that I didn't see discussed here is this: > LLVM 11 tends to take 2x longer to compile code with optimizations, and as a result produces code that runs 10-20% faster (with occasional outliers in either direction), compared to LLVM 2.7 which is more than 10 years old. Yes, C code is expected to benefit less from optimizations, since it is already close to assembly. But compiler optimizations in the…
Re: Clang vs. Clang
#288Earlier quoted context omitted.
> The standards left things undefined in the hopes that the language would be more widely applicable and implementers would give those areas thought themselves and decide the right thing. That sounds like implementation-defined behavior, not undefined behavior.
Same difference. You still have to think about what's right.
Re: Clang vs. Clang
#289Earlier quoted context omitted.
An attribute for functions that says "no optimisations may be applied to the body that would change timings" seems like a reasonable level of granularity here, and if you were conservative about which optimisations it allowed in version zero it'd probably not be a vast amount of work. I'm sort of reminded of the software people vs. hardware people stuff in embedded work, where ideally you'd have people around who'd c…
Why not just specify "all branches of this code must execute in the same amount of time", and let the compiler figure it out for the architecture being compiled for?
I do wonder though how often cpu instructions have data-dependent execution times....
Re: Clang vs. Clang
#290Earlier quoted context omitted.
> Out-of-bounds and use-after-free can be dealt with by having good coding strategies You're basically saying that every project in the wild has bad “coding strategy”… > I expect that we will have full bounds safety options in compilers soon Which will be disabled in most places because of the overhead it incurs. > But having a good ownership model and good abstractions also avoids most problems here in my experience…
No, I am not saying that every project in the wild has a bad "coding strategy". Some of the most reliable software I use everyday is written in C. Some of this I use for decades without every encountering a crash or similar bug. So the meme that "all C code crashes all the time because of UB" is clearly wrong. It is not intractable, in my experience you just have to document some rules and occasionally make sure they…
It's not about crash at all, but “all software has security vulnerabilities because of UB” is unfortunately true.
> It is not intractable, in my experience you just have to document some rules and occasionally make sure they are followed.
If even DJB couldn't get that part perfectly I'm pretty certain you cannot either.