Live data from Hacker News

Clang vs. Clang

blog.cr.yp.to

321–330 of 405 posts

Re: Clang vs. Clang

#321

Earlier quoted context omitted.

> your program has UB it's broken and it doesn't matter if it currently happen to work correct under a specific compiler version, it's also fully your fault Except that compiler writers essentially decide what's UB. Which is a conflict of interest. And they add UB, making previously non-UB code fall under UB. Would you call such code buggy?

If you want a programming language without undefined behaviour, you want something that's not C .

Correct. Which is why I made my own. But C is still better than other languages because it is small.

Re: Clang vs. Clang

#322
post #273

Earlier quoted context omitted.

What languages are suitable for writing algorithms with constant-time guarantees?

According to some comments under this submission, even x86 assembly isn't suitable, or only under specific circumstances that are generally not available in userspace.

At this time, the idea of a constant-time operation embedded into a language’s semantics is not a thing. Similar for CPU architectures. Our computing base is about being fast and faster.

Re: Clang vs. Clang

#323
post #284

Earlier quoted context omitted.

So your issue is not at all any specific thing or action anyone took, but just in general having UB in places not strictly necessary. And "Especially anything [different from The Golden Days]", besides being extremely cliche, is a completely arbitrary cutoff point. 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 ac…

Sigh...yes, I don't want any UB where it's not necessary. But if you must have a concrete example, how about realloc? In C89 [1] (page 155), realloc with a 0 size and a non-NULL pointer was defined as free: > If size is zero and ptr is not a null pointer, the object it points to is freed. In C99 [2] (page 314), that sentence was removed, making it undefined behavior when it wasn't before. This is a pure example of be…

If SQLite wants exactly C89, it can just require -std=c89, and then people compiling it with a different standard target are to blame. This is just standard backwards incompatibility, nothing about UB (in other languages requiring specific compiler/language versions is routine). Problems would arise even if it was changed from being a defined 'free(x)' to being a defined 'printf("here's the thing you realloc(x,0)'d: %p",x)'. (whether the C standard should always be backwards compatible is a more interesting question, but is orthogonal to UB)

I do remember reading somewhere that a real platform in fact not handling size 0 properly (or having explicitly-defined behavior going against what the standard allowed?) being an argument for changing the standard requirement. It's certainly not because compiler developers had big plans for optimizing around it, given that both gcc and clang don't: https://godbolt.org/z/jjcGYsE7W. and I'm pretty sure there's no way this could amount to any optimization on non-extremely-contrived examples anyway.

I had edited one of my parent comments to mention realloc, so if we both landed on the same example, there's probably not that many significant other cases.

Re: Clang vs. Clang

#324
post #304

Earlier quoted context omitted.

In the context of C++ and STL it is UB. They are in the process of rewording such cases as erroneous instead of UB, but it will take time.

…what’s the difference?

If it is UB, the compiler is allowed to optimize based on the assumption that it can't happen. For example, if you have an if in which one branch leads to UB and the other doesn't, the compiler can assume that the branch that led to UB will never happen and remove it from the program, and even remove other branches based on "knowing" that the branch condition didn't happen.

If it's simply erroneous, then it behaves like in every other language outside C and C++: it leaves memory in a bad state if it happens at runtime, but it doesn't have any effect at compile time.

Re: Clang vs. Clang

#325
post #323

Earlier quoted context omitted.

Sigh...yes, I don't want any UB where it's not necessary. But if you must have a concrete example, how about realloc? In C89 [1] (page 155), realloc with a 0 size and a non-NULL pointer was defined as free: > If size is zero and ptr is not a null pointer, the object it points to is freed. In C99 [2] (page 314), that sentence was removed, making it undefined behavior when it wasn't before. This is a pure example of be…

If SQLite wants exactly C89, it can just require -std=c89, and then people compiling it with a different standard target are to blame. This is just standard backwards incompatibility, nothing about UB (in other languages requiring specific compiler/language versions is routine). Problems would arise even if it was changed from being a defined 'free(x)' to being a defined 'printf("here's the thing you realloc(x,0)'d:…

> If SQLite wants exactly C89, it can just require -std=c89, and then people compiling it with a different standard target are to blame.

Backwards compatibility? I thought that was a target for WG14.

> This is just standard backwards incompatibility, nothing about UB

But UB is insidious and can bite you with implicit compiler settings, like the default to C99 or C11.

> whether the C standard should always be backwards compatible is more interesting, but is a question orthogonal to UB

If it's a target, then it should be.

And on the contrary, UB is not orthogonal to backwards compatibility.

Any UB could have been made implementation-defined and still be backwards compatible. But it's backwards-incompatible to make anything UB that wasn't UB. These count as examples of WG14 screwing over its users.

> I do remember some mention somewhere of a real platform in fact not handling size 0 properly being an argument for reducing the standard requirement.

So WG14 just decides to screw over users from other platforms? Just keep it implementation-defined! It already was! And that's still a concession from the pure defined behavior of C89!

> I had edited one of my parent comments to mention realloc, so if we both landed on the same example, there's probably not that many significant other cases.

I beg to differ. Any case where UB was implicit just because it wasn't defined in the standard could have easily been made implementation-defined instead.

Anytime WG14 adds UB that doesn't need to be UB, it is screwing over users.

Re: Clang vs. Clang

#326

Earlier quoted context omitted.

The blog post does, at the very end, mention the thing you should actually do . You need a language where you can express what you actually meant, which in this case is "Perform this constant time operation". Having expressed what you meant, now everybody between you and the hardware can co-operate to potentially deliver that. So long as you write C (or C++) you're ruined, you cannot express what you meant, you are s…

> You need a language where you can express what you actually meant, which in this case is "Perform this constant time operation". Having expressed what you meant, now everybody between you and the hardware can co-operate to potentially deliver that. Yeah, even with assembly, your timing guarantees are limited on modern architectures. But if you REALLY need something specific from the machine, that's where you go.

Most architectures have a subset of their instructions that can be called in constant time (constant time meaning data-independent time). Things like non-constant pointer loads and branches are obviously out, and so are div/mod in almost all chips, but other arithmetic and conditional moves are in that set.

CPUs are actually much better at making those guarantees than compilers are.

Re: Clang vs. Clang

#327
post #323

Earlier quoted context omitted.

If SQLite wants exactly C89, it can just require -std=c89, and then people compiling it with a different standard target are to blame. This is just standard backwards incompatibility, nothing about UB (in other languages requiring specific compiler/language versions is routine). Problems would arise even if it was changed from being a defined 'free(x)' to being a defined 'printf("here's the thing you realloc(x,0)'d:…

> If SQLite wants exactly C89, it can just require -std=c89, and then people compiling it with a different standard target are to blame. Backwards compatibility? I thought that was a target for WG14. > This is just standard backwards incompatibility, nothing about UB But UB is insidious and can bite you with implicit compiler settings, like the default to C99 or C11. > whether the C standard should always be backward…

> Backwards compatibility? I thought that was a target for WG14.

C23 removed K&R function declarations. Indeed backwards-compatibility is important for them, but it's not the be-all end-all.

Having a standard state exact possible behavior is meaningless if in practice it isn't followed. And it wasn't just implementation-defined, it had a specific set of options for what it could do.

> Any case where UB was implicit just because it wasn't defined in the standard could have easily been made implementation-defined instead. Any UB could have been made implementation-defined and still be backwards compatible. But anything that wasn't UB that now is counts as an example of WG14 screwing over its users.

If this is such a big issue for you, you could just name another example. It'd take, like, 5 words to say another feature in question unnecessarily changed. I'll happily do the research on how it changed over time.

It's clear that you don't like UB, but I don't think you've said anything more than that. I quite like that my compiler will optimize out dead null comparisons or some check that collapses to a 'a + C1 < a' after inlining/constant propagation. I think it's quite neat that not being able to assume signed wrapping means that one can run sanitizers that warn on such, without heaps of false-positives from people doing wrapping arith with it. If anything, I'd want some unsigned types with no unsigned wrapping (though I'd of course still want some way to do wrapping arith where needed)

Re: Clang vs. Clang

#328
post #299

Earlier quoted context omitted.

> You need a language where you can express what you actually meant, which in this case is "Perform this constant time operation". Having expressed what you meant, now everybody between you and the hardware can co-operate to potentially deliver that. Yeah, even with assembly, your timing guarantees are limited on modern architectures. But if you REALLY need something specific from the machine, that's where you go.

And besides, Assembly should not be a scary thing. Even most managed languages provide a way to get down into Assembly dumps from their AOT and JIT compiler toolchains. Maybe we need some TikTok videos showing how to do such workflows.

There's nothing terribly difficult to writing specific routines in asm. It's kinda fun, actually. Assembly _in the large_ is hard, just because you need to be next-level organized to keep things from being a mess.

Re: Clang vs. Clang

#329
post #287

A 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…

Rust is possible and proves that you don’t need "optimizations" to optimize, but that optimizations are actually possible. Now that's kind of irrelevent for most of the article focusing about constant versus variable time which is not really an "optimization" problem but already an optimization one, but at least putting appart this rust proves that a langage doesn't need to allow nasal daemons to get good perfs. You…

I specifically addressed the claim that compiler optimization are worthless. I did not addressed the other claims in the article.

In particular, however, Rust relies a lot on Undefined Behavior to optimize well. It manages to hide it (mostly) in the surface language, but in the IR they are necessary to perform well.

Re: Clang vs. Clang

#330
post #327

Earlier quoted context omitted.

> If SQLite wants exactly C89, it can just require -std=c89, and then people compiling it with a different standard target are to blame. Backwards compatibility? I thought that was a target for WG14. > This is just standard backwards incompatibility, nothing about UB But UB is insidious and can bite you with implicit compiler settings, like the default to C99 or C11. > whether the C standard should always be backward…

> Backwards compatibility? I thought that was a target for WG14. C23 removed K&R function declarations. Indeed backwards-compatibility is important for them, but it's not the be-all end-all. Having a standard state exact possible behavior is meaningless if in practice it isn't followed. And it wasn't just implementation-defined, it had a specific set of options for what it could do. > Any case where UB was implicit j…

> Having a standard state exact possible behavior is meaningless if in practice it isn't followed.

No, it means that the bug is documented to be in the platform, not the program.

> If this is such a big issue for you, you could just name another example. It'd take, like, 5 words to say another feature in question unnecessarily changed.

Okay, how about `signal()` being called in a multi-threaded program? Why couldn't they define it in C11 such that it could be called? Obviously, such a thing didn't really exist in C99, but it did in POSIX, and in POSIX, it wasn't, and still isn't, undefined. Why couldn't WG14 have simply made it implementation-defined?

> I quite like that my compiler will optimize out dead null comparisons or some check that collapses to a 'a + C1 I'd rather not be forced to be a superhuman programmer.

Post reply on HN