Generally speaking, you only use it when you have a platform need for it (like various address spaces in offloading languages, sometimes spelled with keywords like __global).
FWIW, this feature is based loosely on what is specified in TR18037 which is freely available. To the best of my knowledge, we do not follow that TR exactly, so expect some differences between the document and reality. Knowing what those differences are would be quite helpful because WG14 is considering standardizing address spaces as they are specified in the TR.
It predates us documenting any attributes and nobody has taken the time to document it since then, unfortunately. This is one I’d like to see more comprehensive documentation written for given its complexity.
In general, yes. In principle, no. The basic idea of segregating the full memory space into separate buckets of memory isn’t really platform specific, but the specific details of what those memory spaces are and how they’re used is generally platform-dependent.
It really should be documented, I’d appreciate a PR on that.
Its semantics are mostly described in LLVM, in addition to what Aaron said, but I don’t think there’s dedicated documentation for it. Closest I’ve found is their relation to pointer types LLVM Language Reference Manual — LLVM 23.0.0git documentation. A lot of the original usage was probably from OpenCL, though our implementation doesn’t necessarily follow it addressSpaceQualifiers(3).
You’ll know exactly when you need to use these, fundamentally they modify which instructions are used to access memory and how that memory is placed.
The numbered address spaces are, though some targets share a lot of the name values like AMDGPU / NVPTX.
The backend’s documentation will probably tell you everything you need to know, but most backends do not need to worry about address spaces much.
There’s also the fun topic of flat addressing and generic address spaces. Right now the C TR allows address spaces to be subsets of others, so some targets allow specific address spaces to decay to a generic one. This is how pretty much all of the offloading languages behave to make the GPU targets behave like more normal C++ compared to OpenCL.
For a more concrete example, something like Compiler Explorer shows how these get lowered to different instructions. If you want to see these in-practice then the only thing off the top of my head is the OpenMP GPU runtime using this gpuintrin.h header and OpenCL, using the dedicated OpenCL address spaces like __private.
We use address_spaces in our out-of-tree clang on OpenVMS to support our dual-sized pointer model. We have both 32-bit pointers and 64-bit pointers (and pragmas to change the defaults, etc.). We’ve added a few additional frontend warnings about upgrade/downgrade pointers and a few backend changes (our 32-bit pointer are sign-extended them converted to 64-bit pointers).
If people are interested, I can get one of the developers to give a more detailed description.
I looked at the document to see if it would help. It sort of does, but it’s still pretty fuzzy to me.
Let instead explain what prompted me posting this question.
So, in clang-21, the Static Analyzer we landed a new checker core.FixedAddressDereference. As its name would suggest we warn for dereferencing addresses that are hardcoded constants (or computed constants.
The checker already had a suppression heuristic for address_space attributes, because in some address spaces, dereferencing NULL might be fine, and we similarly wanted to be on the conservative side for the core.FixedAddressDereference checker.
Since the checker didn’t have any official way to suppress its issues, users started interpreting this attribute as the desired suppression mechanism. This was not intentional and when I looked into it I noticed that it might have an effect on codegen and made me seriously concerned.
I wanted to verify if this attribute would still make sense, but then I struggled getting the documentation and I suspected that it might also depend on the concrete build target.
If that would be the case then suggesting the use of this attribute without knowing their platform would be irresponsible from the checker’s perspective.
IMO in an ideal world, there should be a pattern that the checker would recognise and suppress. That way the checker could still find relevant incidental bugs in their code while not getting spammed whenever using/dereferencing a hardware-specific address. (Think of some firmware or bootloader code for example)
So what I heard from users, they started adding address_space(0) to their pointers. Like I said, I have no idea what it means. Maybe it means that it’s just a pointer in the generic address-space, aka. this attribute is equivalent with nothing. But I also know that the llvm optimiser is also conservative when it comes to unknown attributes and things, and the presence of this “loop” attribute might pessimize their code. [this is speculation]
I share that concern; that is not an appropriate way to suppress the diagnostic. However, I would have expected a volatile qualifier would suppress the diagnostic, I think that’s fairly idiomatic isn’t it?
This is absolutely not what the address space attributes are for. Address space zero is supposed to be the default / generic address space, but some targets interpret it differently. The SPIR-V backend uses AS(0) as the thread-private address space, much to my consternation. There are some places in practice where 0 is a valid address, so it is correct to suppress however.
On platforms that don’t really use address spaces it’ll probably just be silently ignored, stuff like Compiler Explorer will “work” but it’s really not a good idea to mess around with these because they’re so target specific.
So, as it turns out the code I looked at didn’t use volatile. I also wondered how could they get away without using volatile, but here is a simplified version of what I saw: uint32_t value = *(uint32_t *)(0xDEADBEEF);
I figured it really should be uint32_t value = *(volatile uint32_t *)(0xDEADBEEF); but I’d need to carefully understand when volatile is valid and when it’s not. I still believe that they should use volatile, and the checker should also recognise and suppress if the pointee is volatile. I planned to implement that.
This confirms my suspicion and I’m happy I reached out. Thank you!
Now that we are here, my plan for the checker is to move it out of the core package so it wouldn’t be enabled by default, into the optin. Update the docs that it recognises the address_space attribute and suppress reports there; but you should probably use volatile that the checker considers as a suppression. Otherwise just prefer not “opting-in” to the checker.
This means that I wouldn’t need to point them (intentionally/unintentionally) to using the address_space attribute. This would solve the issue that prompted me writing this post.
I would like to emphasize that this is an important part – the checker documentation should highlight that address_space has other implementation-defined effects and it shouldn’t be used purely as a suppression marker. (adding volatile may be more reasonable, and opting out is also a good choice).