38 ms·
Emacs internals: Tagged pointers vs. C++ std:variant and LLVM (Part 3)
- thecloudlet 7mo agoEmacs internal part 2 HN link: https://news.ycombinator.com/item?id=47259961 https://news.ycombinator.com/item?id=47259961
- tialaramex 7mo agoIt's not clear to me (and as an unsafe language it's not called out by your compiler if you do something illegal) what the correct way to spell this kind of trick is in C++ I had thought you need the pointer-sized integer types and mustn't do this directly to an actual pointer, but maybe I was wrong (in theory, obviously practice doesn't follow but that's a dangerous game)
- db48x 7mo agoDo the way LLVM does it.
- thecloudlet 7mo agoDoing 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).
- shadowgovt 7mo agoIs there a similar solution to doing this in Rust? I suppose inside `unsafe` you can do basically anything.
- thecloudlet 7mo agoWaiting for Rust experts.
- simonask 7mo agoRust 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.
- tialaramex 7mo agoNope. 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-provenance-apis https://blog.rust-lang.org/2025/01/09/Rust-1.84.0/#strict-pr...
- uecker 7mo agoBut LLVM's optimizations aren't sound and this affects Rust too.
- simonask 7mo agoHuh? Which optimizations?
- tialaramex 7mo agoLLVM is quite sure that, for example, two pointers to different objects are different. That's true even if in fact the objects both lived in the exact same spot on the stack (but at different times). That's... well it's not what Rust wants but it's not necessarily an unacceptable outcome and Rust could just ask for their addresses and compare those... Except it turns out if we ask for their addresses, which are the same integer, LLVM remembers it believed the pointers were different and insists those are different too. Until you call its bluff and do arithmetic on them. Then, in some cases, it snaps out of it and remembers that they're identical... This is a compiler bug, but, apparently it's such a tricky bug to fix that I stopped even looking to see whether they'd fixed it after a few years... It affects C, C++, Rust, all of them as a result can be miscompiled by a compiler using LLVM [it's easiest to demonstrate this bug with Rust but it's the same in every language]. But as you've probably noticed, this doesn't have such an enormous impact that anybody stopped using LLVM.
- trws 7mo agoThere’s a paper in flight to add a stdlib type to handle pointer tagging as well while preserving pointer provenance and so-forth. It’s currently best to use the intptr types, but the goal is to make it so that an implementation can provide specializations based on what bits of a pointer are insignificant, or even ignored, on a given target without user code having to be specialized. Not sure where it has landed since discussion in SG1 but seemed like a good idea.
- tialaramex 7mo agoGiven you aren't sure since SG1 this might be useless but... do you have a paper number? Or, more likely, know an author's name ?
- legobmw99 7mo agoSeems like its p3125r0
- tialaramex 7mo agoThanks! https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p3125r4.html https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p31... (is the current version of that paper, the tracking ticket insisted there's a P3125R5 and that LEWG had seen it in 2025, but it isn't listed in a mailing so it might be a mirage) You know it's a Hana paper because it wants this to be allowed at compile time (C++ constrexpr) but joking aside this seems like a nice approach for C++ which stays agnostic about future implementation details.
- trws 7mo agoIt’s Hana Dusikova’s paper IIRC.
- VorpalWay 7mo agoDo (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 rules might be loosened in the future to be more lenient.) https://doc.rust-lang.org/std/ptr/index.html#provenance https://doc.rust-lang.org/std/ptr/index.html#provenance
- tialaramex 7mo agoWell, 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.
- jcranmer 7mo ago> 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.)
- jandrewrogers 7mo agoThe idiomatic way to safely do pointer tagging in C++ works through uintptr_t. If you don't care about portability or using every theoretically available bit then it is trivial. A maximalist implementation must be architecture aware and isn't entirely knowable at compile-time. This makes standardization more complicated since the lowest common denominator is unnecessarily limited. In C++ this really should be implemented through a tagged pointer wrapper class that abstracts the architectural assumptions and limitations.
- deleted 7mo ago[deleted]
- ndesaulniers 7mo agoHappy 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_pattern https://en.wikipedia.org/wiki/Curiously_recurring_template_p... 2. https://david.alvarezrosa.com/posts/devirtualization-and-static-polymorphism/ https://david.alvarezrosa.com/posts/devirtualization-and-sta... 3. https://llvm.org/docs/ProgrammersManual.html#the-isa-cast-and-dyn-cast-templates https://llvm.org/docs/ProgrammersManual.html#the-isa-cast-an...
- thecloudlet 7mo agoThanks for the links, Nick! It's fascinating how LLVM relies so heavily on CRTP.
- ndesaulniers 7mo agoConsider amending those references to your post!
- thecloudlet 7mo agoSure! I have updated my post! Thanks for your reference.
- dalvrosa 6mo agoThanks a lot for the reference!
- mshockwave 7mo agoLLVM now has another way to implement RTTI using the `CastInfo` trait instead of `classof`: https://llvm.org/doxygen/structllvm_1_1CastInfo.html https://llvm.org/doxygen/structllvm_1_1CastInfo.html But it's really just an implementation difference, the idea is still to have a lightweight RTTI.
- internet_points 7mo agoDrawn in by the Emacs, learnt something new about C and C++, thank you for this! Very readable article for someone who doesn't feel too confident with low-level bits. Btw, is this representation the reason why OCaml's ints are not as big as C ints? Also interesting that the Haskell pointer tagging you link to[0] was done the way it was to avoid CPU branch misprediction, and that the old way which it replaced was "the source of half of the branch misprediction events". I wonder how "branch prediction friendly" current Haskell is. [0] https://simonmar.github.io/bib/papers/ptr-tagging.pdf https://simonmar.github.io/bib/papers/ptr-tagging.pdf