3 ms·
I was utterly unconvinced that the original infinite-loop UB gave the compiler any important performance optimization, and I'm unconvinced that this is providin
by JoshTriplett 8d ago
I was utterly unconvinced that the original infinite-loop UB gave the compiler any important performance optimization, and I'm unconvinced that this is providing useful value to compensate for its surprise. If I wanted a yield in my infinite loop, I'd add one.
- mitxela 7d agoThe original UB was to allow the compiler to merge two loops without proving termination.
- CamperBob2 7d agoWhat difference does it make? If the loop doesn't terminate, it doesn't terminate, which is almost always a bug, except when it's not. If it does terminate, then great, it terminates. Merging a buggy loop with another loop creates... a buggy loop.
- teo_zero 7d agoTake this example: for (i=0;i<n;i++) A[i]=0; for (i=0;i<n;i++) B[i]=0; It can be conveniently transformed into this: for (i=0;i<n;i++) A[i]=B[i]=0; They are exactly equivalent except if the first loop never terminates. Now, the compiler could try to understand if the first loop does or doesn't terminate, and apply or not the optimization accordingly, but Turing tought us that is indeed a hard task! Or it could decide to never apply it, for fear of those rare and usually pathological cases where the first loop doesn't terminate. Or it could decide to apply it by default and accept that in those cases the program does something different than what the source code says. The latter is better known as UB. The third option won, and that's why infinite loops are UB in the standard.
- mitxela 7d agoYou might ask why it's important that the first loop terminates since in this case the extra side effect would just be a dead store - but if the first loop doesn't terminate then it's possible B is an invalid pointer and then accessing it during the first loop is UB when it shouldn't be. Making a nonterminating loop UB is the patch for this. The standard example is a linked list instead of an array because the compiler can't prove it never has a cycle.
- CamperBob2 7d agoThe complaint I have is that for the sake of benchmark wars, UB has been retconned from "The code might not behave the way you want under certain conditions on certain platforms, hopefully you know what you're doing" to "The compiler can do anything it wants, including rickrolling the user." That is no longer undefined behavior in my book. That is defined behavior that just has an unusually-shitty definition. It's all moot anyway given other trends in progress, but... UB, bah humbug. Stop trying to fix problems that no one had. This is why people are clamoring to replace C/C++ with Rust and AI and whatever. The language needed to become more understandable and more predictable in everyday use, and instead it got worse.
- mitxela 7d agoYes.
- imtringued 6d agoSo you're saying the person you are responding completely failed to make their point clear? Why use a for loop with a bound as an example instead of while loops with linked lists? He or she can prompt an LLM for a better example so laziness doesn't count as an excuse.
- teo_zero 6d ago> Why use a for loop with a bound as an example instead of while loops with linked lists? I'm the author of the example. I wanted to keep it as simple as possible, and this is the most common form of for loop. I was sure that HN readers would be clever enough to "map" it to whatever they have in their mind that satisfy the undecidability of the condition. But since you're nitpicking, I haven't specified the types of i and n: i is uint8_t and n is uint32_t. Does it terminate? It depends on the value of n!
- imtringued 6d agoYour explanation is completely insane. > but Turing tought us that is indeed a hard task! Analyzing whether a bounded loop terminates is impossible, got it. >Or it could decide to apply it by default and accept that in those cases the program does something different than what the source code says. The latter is better known as UB. But the reason why it lets the compiler fuse the loops has nothing to do with whether the loop terminates or not. The infinite loop UB is just a way of adding more UB and then invoking non infinite loop optimization. We don't know if A[i] aliases with the pointer that stores the address of the B array and note I mean B itself not A and B overlapping. It could also alias with the loop bound. So the first loop must run until completion simply because it could accidentally overwrite a pointer or variable that is used in the second loop. But now that we have infinite loop UB we can ignore all of that and it's not because infinite loops themselves produce optimization potential, it's because more stuff is UB now so the compiler is allowed to break aliasing rules, which is the actual thing that was preventing the optimization. The infinite loop UB is just the permission slip.
- teo_zero 6d agoWhenever you write a succinct example or a metaphor to try to explain in a few words a complicated concept, someone will nitpick small details of your construction, thus focusing on the form and missing the spirit! Forget if "i<n" is decidable or not: the sense is that there will always be some loops that the compiler can't determine if it's finite or not. Forget if A and B can alias or not: the sense is having two independent actions that can be executed in the same loop or in two consecutive loops. Let's see... what about the following example, that replaces all a's with @ and all e's with & in a zero-terminated string s? for (char *p=s; *p; p++) if (*p=='a') *p='@'; for (char *p=s; *p; p++) if (*p=='e') *p='&'; If a compiler is allowed to assume that the first loop terminates, then it may optimize it to: for (char *p=s; *p; p++) { if (*p=='a') *p='@'; if (*p=='e') *p='&'; } Is this explanation less insane?