Live data from Hacker News

Emacs internals: Tagged pointers vs. C++ std:variant and LLVM (Part 3)

thecloudlet.github.io

21–30 of 39 posts

Re: Emacs internals: Tagged pointers vs. C++ std:variant and LLVM (Part 3)

#21
Happy to see discussion of LLVM's interesting implementation of Static Polymorphism using CRTP. Some recommended reads:

1. https://en.wikipedia.org/wiki/Curiously_recurring_template_p...

2. https://david.alvarezrosa.com/posts/devirtualization-and-sta...

3. https://llvm.org/docs/ProgrammersManual.html#the-isa-cast-an...

Re: Emacs internals: Tagged pointers vs. C++ std:variant and LLVM (Part 3)

#22
post #16

Earlier quoted context omitted.

Is there a similar solution to doing this in Rust? I suppose inside `unsafe` you can do basically anything.

Everything else in the siblings is true, but remember that the language and std types in rust all do this already. Most of the time it’s better to use a native enum or optional/result because they do this in the compiler/lib. It’s only really worth it if you need more than a few types or need precise control of the representation for C interop or something.

To expand on the sibling answer: sort of! Rust will do niche optimisation, but for references and NonNull pointers this is limited to "the value 0 is invalid and can thus be used as a niche". But Rust does not (currently) take advantage of alignment niches in pointers. Nor does it use high bit on architectures where you know your whole theoretical address space isn't actually in use.

Is doing that manually worth it? Usually not, but for some core types (classical example is strings) or in language runtimes it can be.

Would it be awesome if this could be done automatically? Absolutely, but I understand it is a large change, and the plan is to later build upon the pattern types that are currently work in progress (and would allow you to specify custom ranged integer typed).

Re: Emacs internals: Tagged pointers vs. C++ std:variant and LLVM (Part 3)

#23

Earlier quoted context omitted.

Doing bitwise operations directly on raw pointers is a fast track to Undefined Behavior in standard C/C++. Emacs gets away with it largely due to its age, its heavy reliance on specific GCC behaviors/extensions, and how its build system configures compiler optimizations. In modern C++, the technically "correct" and safe way to spell this trick is exactly as you suggested: using uintptr_t (or intptr_t).

Do (u)intptr_t preserve provenance? Or does this count as exposed provenance when you convert back and forth? Maybe that is not the correct C++ terminology, I'm more familiar with how provenance works in Rust, where large parts of it got stabilised a little over a year ago. (What was stabilised was "strict provenance", which is a set of rules that if you abide them will definitely be correct, but it is possible the r…

Well, C++ does not have any promises about how Pointer Provenance works, so AFAIK the answer is "mu" meaning that's a bad question, don't ask that.

But the likely destiny of C++ is to inherit the provenance rules that are an adjunct to C23, PNVI-ae-udi, Provenance Not Via Integers, Addresses Exposed, User Disambiguates

As that name suggests, in this model provenance is not transmitted via integers. Every 123456 is always just the integer 123456 and there aren't magic 123456 values which are different and transmit some form of provenance from a pointer to some value which happened perhaps to be stored at address 123456 in memory.

However, PNVI-ae-udi has Exposure, which means if we exposed the pointer in an approved way then the associated provenance is somehow magically "out there" in the ether, as a result if we have exposed this pointer then just having that integer 123456 works fine because we combined that integer 123456 with that provenance from the ether and make a working pointer. User disambiguation means that the compiler has to give you "benefit of the doubt" e.g. if you could mean to make a pointer to that Doodad which no longer exists as of a minute ago or to this other Doodad which does exist, well, benefit of the doubt means it was the latter and so your pointer is valid even though the addresses of both Doodads were the same.

Re: Emacs internals: Tagged pointers vs. C++ std:variant and LLVM (Part 3)

#24

Earlier quoted context omitted.

Doing bitwise operations directly on raw pointers is a fast track to Undefined Behavior in standard C/C++. Emacs gets away with it largely due to its age, its heavy reliance on specific GCC behaviors/extensions, and how its build system configures compiler optimizations. In modern C++, the technically "correct" and safe way to spell this trick is exactly as you suggested: using uintptr_t (or intptr_t).

Do (u)intptr_t preserve provenance? Or does this count as exposed provenance when you convert back and forth? Maybe that is not the correct C++ terminology, I'm more familiar with how provenance works in Rust, where large parts of it got stabilised a little over a year ago. (What was stabilised was "strict provenance", which is a set of rules that if you abide them will definitely be correct, but it is possible the r…

Pointer provenance is not properly defined in C or C++. (There is a C TS that introduces pointer provenance, but it's not part of the main standard).

The problem of pointer provenance is more finding a workable theoretical model rather than one causing miscompiles on realistic code. While there are definitely miscompiles on carefully constructed examples, I'm not aware of any bugs on actual code. This is in comparison to topics like restrict(/noalias) semantics or lifetime semantics, where there is a steady drip of bug reports that turn out to be actual optimization failures.

Re: Emacs internals: Tagged pointers vs. C++ std:variant and LLVM (Part 3)

#25

Earlier quoted context omitted.

Do (u)intptr_t preserve provenance? Or does this count as exposed provenance when you convert back and forth? Maybe that is not the correct C++ terminology, I'm more familiar with how provenance works in Rust, where large parts of it got stabilised a little over a year ago. (What was stabilised was "strict provenance", which is a set of rules that if you abide them will definitely be correct, but it is possible the r…

Well, C++ does not have any promises about how Pointer Provenance works, so AFAIK the answer is "mu" meaning that's a bad question, don't ask that. But the likely destiny of C++ is to inherit the provenance rules that are an adjunct to C23, PNVI-ae-udi, Provenance Not Via Integers, Addresses Exposed, User Disambiguates As that name suggests, in this model provenance is not transmitted via integers. Every 123456 is al…

> But the likely destiny of C++ is to inherit the provenance rules that are an adjunct to C23, PNVI-ae-udi, Provenance Not Via Integers, Addresses Exposed, User Disambiguates

There's a competing proposal in C++ land to add provenance via angelic nondeterminism: if there's some provenance that makes the code non-UB, then use that provenance. (As you might imagine, I'm not a big fan of that proposal, but WG21 seems to love it a lot more than I do.)

Re: Emacs internals: Tagged pointers vs. C++ std:variant and LLVM (Part 3)

#27

Earlier quoted context omitted.

Rust is basically in the same place as C++, i.e. provenance rules are currently ad-hoc/conventional, meaning that pointer tagging is a grey area.

Nope. Rust stabilized strict provenance over a year ago. Some details about aliasing aren't tied down, but so long as you can obey the strict provenance rules you're golden today in Rust to hide flags in pointers etc. https://blog.rust-lang.org/2025/01/09/Rust-1.84.0/#strict-pr...

But LLVM's optimizations aren't sound and this affects Rust too.

Re: Emacs internals: Tagged pointers vs. C++ std:variant and LLVM (Part 3)

#28

Earlier quoted context omitted.

Well, C++ does not have any promises about how Pointer Provenance works, so AFAIK the answer is "mu" meaning that's a bad question, don't ask that. But the likely destiny of C++ is to inherit the provenance rules that are an adjunct to C23, PNVI-ae-udi, Provenance Not Via Integers, Addresses Exposed, User Disambiguates As that name suggests, in this model provenance is not transmitted via integers. Every 123456 is al…

> But the likely destiny of C++ is to inherit the provenance rules that are an adjunct to C23, PNVI-ae-udi, Provenance Not Via Integers, Addresses Exposed, User Disambiguates There's a competing proposal in C++ land to add provenance via angelic nondeterminism: if there's some provenance that makes the code non-UB, then use that provenance. (As you might imagine, I'm not a big fan of that proposal, but WG21 seems to…

Very interesting discussion. I hadn't realised that the final provenance model hadn't yet been decided for C and C++.

Angelic non-determinism seems difficult to use to determine if an optimisation is valid. If I understand this correctly, it is basically the as-if rule, but in this case applied to something that potentially needs global program analysis. Would that be an accurate understanding?

It sounds like both of these proposals will be strictly less able to optimize than strict provenance in rust to me. In particular, Rust allows applying a closure/lambda to map a pointer while keeping the provenance. That avoids exposing the provenance as you add and remove tag bits, which should at least in theory allow LLVM to optimise better. (But this keeps the value as a pointer, and having a dangling pointer that you don't access is fine in Rust, probably not in C?)

I'm not sure why I'm surprised actually, Rust can be a more sensible language in places thanks to hindsight. We see this in being able to use LLVM noalias (restrict basically) in more places thanks to the different aliasing model, while still not having the error prone TBAA of C and C++. And it doesn't need a model of memory consisting of typed objects (rather it is all just bytes and gets materialised into a value of a type on access).

Re: Emacs internals: Tagged pointers vs. C++ std:variant and LLVM (Part 3)

#29

Earlier quoted context omitted.

Rust is basically in the same place as C++, i.e. provenance rules are currently ad-hoc/conventional, meaning that pointer tagging is a grey area.

Nope. Rust stabilized strict provenance over a year ago. Some details about aliasing aren't tied down, but so long as you can obey the strict provenance rules you're golden today in Rust to hide flags in pointers etc. https://blog.rust-lang.org/2025/01/09/Rust-1.84.0/#strict-pr...

Oh damn, I missed this, thanks for the correction!

Re: Emacs internals: Tagged pointers vs. C++ std:variant and LLVM (Part 3)

#30
post #27

Earlier quoted context omitted.

Nope. Rust stabilized strict provenance over a year ago. Some details about aliasing aren't tied down, but so long as you can obey the strict provenance rules you're golden today in Rust to hide flags in pointers etc. https://blog.rust-lang.org/2025/01/09/Rust-1.84.0/#strict-pr...

But LLVM's optimizations aren't sound and this affects Rust too.

Huh? Which optimizations?
Post reply on HN