Live data from Hacker News

Pointers Are Complicated II, or: We need better language specs

ralfj.de

31–40 of 135 posts

Re: Pointers Are Complicated II, or: We need better language specs

#31
post #19

Earlier quoted context omitted.

They are not always integers. See this comment from the previous discussions on the other posts, for example: https://news.ycombinator.com/item?id=17607595

If 'b' is actually used then the compiler has to put 'a' in memory. Theoretically. He says that b is not changed, and does not "escape" (the scope of the code in question). In that case b is useless, and thus it doesn't really even exist. A compiler can "collapse" a lot of code into a fraction of it. That's a part of the "optimizing" part of "optimizing compiler". A compiler can put a value in a register. A compiler…

> If 'b' is actually used then the compiler has to put 'a' in memory.

Theoretically, no—the “as if” rule applies. In practice, also no, because real compilers perform escape analysis and there are aggressive optimization passes for eliminating dead loads and dead stores.

Just like the compiler can optimize out b and replace it with a reference to a, the compiler can optimize out the value stored in a but keep the reference to it stored in b.

> In that case b is useless, and thus it doesn't really even exist.

I don’t think this notion of “exist” has legs.

Re: Pointers Are Complicated II, or: We need better language specs

#32

Earlier quoted context omitted.

And I agree, with an UB the original code is undefined and so any optimization is not right nor wrong, simply keep that UB. But then, why such a long article for a code whose second line is a UB ('p+1' is undefined)?

uintptr_t ip = (uintptr_t)(p+1); Is perfectly defined. What is undefined is converting ip back to a pointer and writing to it, which the original program never does.

Hmm, I think I understand now.

So now I think it's the first optimization the one that is wrong. If you replace a variable with another, don't you need to keep information of the original variable?

  I mean, if a==b and you do *a=1 you can replace it with *b=1, but then you need to keep the information that 'a' was written to, so other optimizations (the third one) don't think it wasn't. Or am I missing something else here?
Edit: sorry for the code block, otherwise the asterisks are removed.

Re: Pointers Are Complicated II, or: We need better language specs

#33

Great article It just seems to me there's too much risk in "overoptimizing" especially in a weakly defined language like C with trigger happy optimizers and even more trigger happy "UB means let's go crazy" I take it as principle that no compilers should "throw their hands up" when detecting UB. Crash the program, don't just take that part out.

Compilers don't detect UB (at least as optimizations are concerned), they assume no UB.

If you want to detect UB, at least dynamically, compile with sanitizers.

Re: Pointers Are Complicated II, or: We need better language specs

#34
post #8

First of all: fantastic article . In-depth, insightful, and the examples are absolutely top-notch. On the razor's edge between accessible and profound. Hats off to the author. I will say that the problems seem to lie in a few interesting interlanguage quirks, and not so much on language specs . For example, LLVM and C have different definitions of "undefined behavior"[1] -- this is pointed out when looking at the `po…

> As a side note, I'd be curious what happens when LLVM sees something like: `void p = &p` which is a self-referential pointer. What does it deduce about it? Does it optimize it away? Why would it do anything special here? This is not really different from something like this: struct a { struct a *p; }; struct a x = { &a }; The only real difference is that the void version involves a cast, because the type is otherwi…

> Why would it do anything special here? This is not really different from something like this:

Yeah, I suppose you're right. I was Googling potential interesting cases and ran across this one[1] which made me think of weird edge cases w.r.t. self-reference.

[1] https://stackoverflow.com/questions/20596856/self-referentia...

Re: Pointers Are Complicated II, or: We need better language specs

#35
post #8

First of all: fantastic article . In-depth, insightful, and the examples are absolutely top-notch. On the razor's edge between accessible and profound. Hats off to the author. I will say that the problems seem to lie in a few interesting interlanguage quirks, and not so much on language specs . For example, LLVM and C have different definitions of "undefined behavior"[1] -- this is pointed out when looking at the `po…

I'm pretty sure there is no UB in the second part. It's just LLVM bug. The pointer comparison is valid (pointer to and object and to an another object that's one past the end MAY compare equal). The write is valid.

Reads and writes to char* always alias everything. Unless the compiler can prove that no writes happened it has to emit a read. So if it "forgets" due to the uintptr_t cast where the char* came from then it must assume that it can be pretty much arbitrary and must emit a read.

Re: Pointers Are Complicated II, or: We need better language specs

#36
post #19

Earlier quoted context omitted.

If 'b' is actually used then the compiler has to put 'a' in memory. Theoretically. He says that b is not changed, and does not "escape" (the scope of the code in question). In that case b is useless, and thus it doesn't really even exist. A compiler can "collapse" a lot of code into a fraction of it. That's a part of the "optimizing" part of "optimizing compiler". A compiler can put a value in a register. A compiler…

> If 'b' is actually used then the compiler has to put 'a' in memory. Theoretically, no—the “as if” rule applies. In practice, also no, because real compilers perform escape analysis and there are aggressive optimization passes for eliminating dead loads and dead stores. Just like the compiler can optimize out b and replace it with a reference to a, the compiler can optimize out the value stored in a but keep the ref…

That is what i said, that it can be optimized away.

"exist" is the wrong word, "is useless" would be better. It is like.. you have a spanner in a box labeled "a" and you have a box "b" that has a piece of paper that says "look in a". You ask for a spanner and you get told to look in box "a". The box labeled "b" is then completely useless. One day some guy, weirdly named "compiler", decides to trow away box "b", and nobody cares.

But we have to assume that a compiler will compile the program just as we wrote it. Otherwise we are not talking about the language, but about the compiler. This is gone off topic, if there even was one.

Re: Pointers Are Complicated II, or: We need better language specs

#37

Excellent article. It seems to me (and this is somewhat off-the-cuff) that compilers have another option: integers which are cast from pointers have provenance. That is, provenance is a taint: you can't clean it off through casting. So casting from pointer means the user can do integer-things like addition, but it means the compiler can't do integer-things like constant folding.

I don't think that works. Consider the following (contrived) program: char* q[1] = {0}; int iq = (uintptr_t)q; int ip = 0; while (iq>0) { iq--; ip++; } char* p = (char*) ip; You can make this more efficient (albeit no less contrived) by iterating through iq bit-wise. Less contrived would be sending a pointer through some IPC mechanism (allowing it to be used as an opaque handle externally, while the program will blin…

So what are the optimizations here? iq is tainted, you can't hoist the loop out. You've said "I'm doing a weird thing here with an integer which used to be a pointer, you have to assume that it involves memory and treat it more like a pointer than an integer"

Re: Pointers Are Complicated II, or: We need better language specs

#38
post #36

Earlier quoted context omitted.

> If 'b' is actually used then the compiler has to put 'a' in memory. Theoretically, no—the “as if” rule applies. In practice, also no, because real compilers perform escape analysis and there are aggressive optimization passes for eliminating dead loads and dead stores. Just like the compiler can optimize out b and replace it with a reference to a, the compiler can optimize out the value stored in a but keep the ref…

That is what i said, that it can be optimized away. "exist" is the wrong word, "is useless" would be better. It is like.. you have a spanner in a box labeled "a" and you have a box "b" that has a piece of paper that says "look in a". You ask for a spanner and you get told to look in box "a". The box labeled "b" is then completely useless. One day some guy, weirdly named "compiler", decides to trow away box "b", and n…

A variable is useless because it is optimized away? I don’t agree with that terminology.

The variable is useful / exists in the source code, and that is enough. Variables / objects only exist conceptually in the original program anyways, we just infer their existence in the compiled program by correspondence with the original code, or educated guesses about the original code.

Re: Pointers Are Complicated II, or: We need better language specs

#39
post #11

Pointers are "just" integers (unsigned integers of a size, that is). Most languages treat them differently (C assigns them a type, and a stride with it), but that is usually on top of them being integers. Pointers "point" to (are addresses to) bytes, as he said. If you want you can pack data to a resolution of a bit, and some languages help you do that as well. You can also do whatever you want with pointers (in some…

They are not always integers. See this comment from the previous discussions on the other posts, for example: https://news.ycombinator.com/item?id=17607595

> What happens when 'a' is allocated to a CPU register?

When the & operator is used on 'a' it should mark it as unsafe to place the 'a' on a CPU register - CPU allocation is an optimization and optimizations should never affect how the program behaves (except making it run faster, of course) and as such they should only be applied when the compiler can be sure that they're safe to do so.

(and yes, the same applies on using & on something like an array or struct element - the use of & should taint any variable)

Re: Pointers Are Complicated II, or: We need better language specs

#40
post #21
post #8

First of all: fantastic article . In-depth, insightful, and the examples are absolutely top-notch. On the razor's edge between accessible and profound. Hats off to the author. I will say that the problems seem to lie in a few interesting interlanguage quirks, and not so much on language specs . For example, LLVM and C have different definitions of "undefined behavior"[1] -- this is pointed out when looking at the `po…

As usual; if a compiler knows about undefined behavior I would much rather it throw an error rather than optimize something the programmer didn't intend based on the compiler out-smarting a human's ability to be specific.

It would sometimes work but I think this (very common comment) misses the general point, there's absolutely fine and non buggy on warning-worthy code that can be optimized away if the compiler relies on UB. A very simple example:

    void do_stuff(int *some_ptr) {
        do_substuff(some_ptr);

        *some_ptr += 2;
    }

    static void do_substuff(int *some_ptr) {
        if (some_ptr != NULL) {
            *some_ptr = 10;
        }
    }
do_stuff calls a subroutine that does a NULL pointer check, then unconditionally dereferences the same pointer.

From this the compiler, if it decides to inline that code, can decide to optimize the NULL check away since if the pointer is NULL it's guaranteed to trigger UB. The logic being "clearly the programmer assumes that the pointer can't be NULL here, so thanks to UB rules I can too".

There's nothing wrong with this code, there's no reason to emit a warning.

Distinguishing between "of course that's a reasonable optimization, that's probably what the developer intended" and "wow this compiler is so dumb, obviously I never meant for that to happen" is a very tough nut to crack.

At this point you can push the blame to the C language not being expressive enough, in this case not giving us a way to express nullable vs. non-nullable pointers within the language, which forces the compiler to lean onto these UB heuristics to optimize.

Post reply on HN