# Clarify Address Classes vs. Address Spaces This is a clarification proposal to clarify the meaning of address spaces, which are used by the `DW_OP_xderef` operations, and address classes, which were once used to distinguish "near" and "far" pointers in the old 16-bit x86 world. This is a prequel to [260211.1][260211.1] General Support for Address Spaces, and should supercede the section of that proposal that adds a new section 2.12 Address Spaces. Instead of adding a new section and possibly removing the old 2.11 Address Classes, I suggest expanding 2.11 to discuss both concepts together in order to better draw the distinction between the two. I've also added a short paragraph to define the term "address" in terms of an offset into a memory address space, so that it can be used unambiguously, and to deal with the possibility that addresses might be treated as signed on some architectures, while we've specified that offsets are always unsigned. ## Background PA-RISC had a “long pointer” type that consisted of a 32-bit space id and a 32-bit address, analogous to a “far” pointer on x86. On PA, as on other architectures that used a global address space, the process address space was segmented, but presented to the user space the illusion of a flat 2^32 byte virtual address space. A long pointer was used to access memory outside of that flat address space, and dereferencing one involved moving the space id to a free space register, and then using a load/store instruction specifying that space register explicitly (compared to the standard load that picked a space register based on the top two bits of the address). In both x86 and PA cases, a pointer with one of these alternate address classes had all the information needed to identify a byte in memory, and the compiler knew, based on the declaration of the pointer, what kind of load or store sequence was needed. The `DW_AT_address_class` attribute simply relayed that information to the debugger. Despite the fact that we agreed that the “near” and “far” pointers from the old pre-MMU 16-bit x86 world are obsolete, I think there’s at least a possibility that the concept remains useful on modern architectures, certainly PA, Itanium, and Power, which all use global virtual addressing. Perhaps we should have left in the obsolete class codes, just as an example for other architectures to follow. (They were non-normative anyway.) In the address space proposal, [260211.1][260211.1], we have a different situation: we have a pointer that is known to provide an address in a totally different address space, but the pointer itself does not provide that information. That proposal adds the `DW_AT_address_space` attribute to identify that alternate address space. There are examples outside the GPU world: - The old DEC PDP-11/70 offered separate I and D spaces (kind of a Harvard architecture), as a way of doubling the 64K limit on the process address space. Load/store instructions would reference D space for ordinary register-offset memory references, but would reference I space for pc-relative references. - Gcc and gdb support a couple of DSP architectures that use a side address space for flash memory. Unfortunately, these two architectures are currently (mis-)using DW_AT_address_class to identify pointers to flash memory. In the GPU world, the possibility has been raised that pointers for some address spaces may be larger or smaller than pointers to the default address space. That seems to me to be a separate consideration from the notion of address space, and the width of the pointer should be specified with a `DW_AT_byte_size` or `DW_AT_bit_size` attribute, while the address space that the pointer refers to is specified as an address space. I suggest combining these concepts into one section. I think we can drop the `DW_AT_address_class` attribute from subroutines and subroutine types. On legacy x86 systems, a “far” subroutine needed to be called with an intersegment CALL, and would return with an intersegment RET. This really should have been handled with `DW_AT_calling_convention`, and could be if ever needed again. ## Proposal In Section 2.5, add the following paragraph to the "Memory" bullet under "six kinds of storage": > An *address* is the offset of an addressable unit (byte or > word) in a memory address space. On some systems, an address > may be signed, and a negative address is converted to a > positive byte or word offset formed by by biasing it by the > size of the address space (e.g., in a 2^32 byte address space, > using two's complement arithmetic, the address -0x8000 is > equivalent to a byte offset of 0xffff8000). Replace 2.11 Address Classes with the following: > 2.11 Address Classes and Address Spaces > > Some systems support different classes of addresses. The address > class may affect the way a pointer is dereferenced or the way a > subroutine is called. > > Any debugging information entry representing a pointer or > reference type or a subroutine or subroutine type may have a > DW_AT_address_class attribute, whose value is an integer > constant. The set of permissible values is specific to each > target architecture. The value DW_ADDR_none, however, is common > to all encodings, and means that no address class has been > specified. > > *Some systems support different classes of addresses. The address > class may affect the representation of a pointer and how it is > dereferenced. Normally, a pointer is represented as an address in > the default memory address space, where the memory can be > accessed by standard load and store instruction. On some systems > with segmented memory models or global virtual address spaces, an > alternative pointer representation may consist of a separate > segment identifier and an offset within that segment.* > > *Examples of alternate address classes include:* > > - *x86 “far” pointers, which consist of a 16-bit segment > identifier and either a 16- or 32-bit offset within the segment. > Dereferencing a far pointer requires moving the segment > identifier to a free segment register and using a segment > override for the memory access.* > > - *PA-RISC “long” pointers, which consist of a 32-bit segment > (termed “space” in PA-RISC documentation) identifier and a 32-bit > offset. Dereferencing a long pointer requires moving the space > identifier to a free space register and using the explicit space > register number on the load or store instruction.* > > *Some systems may also support more than one memory address > space. There is always a default address space, and the default > size of a pointer corresponds to the size of the default address > space. The size of any other address space is not necessarily the > same as the size of the default address space.* > > *Examples of alternate address spaces include:* > > - *”Harvard” architectures with separate code and data spaces.* > > - *Embedded systems with a separate address space for flash > memory.* > > - *Heterogeneous systems with local memory spaces for each GPU, > or where large register files are mapped to their own address > space.* > > Any debugging information entry representing a pointer or > reference type may have a `DW_AT_address_class` attribute, whose > value is an integer constant. The set of permissible values is > specific to each target architecture. The value `DW_ADDR_none`, > however, is common to all encodings, and means that no address > class has been specified (that is, the pointer or reference type > uses the standard encoding of an address on the target > architecture). > > Address spaces are not guaranteed to be independent of one > another; for example, one address space might provide an > alternate addressing scheme for the same memory as another > address space. > > Address space identifiers are used by the `DW_OP_xderef*` > operations (see Section 3.13). [The following two paragraphs to replace the above paragraph with acceptance of [260211.1][260211.1]:] > Any debugging information entry representing a pointer or > reference type may also have a `DW_AT_address_space` attribute, > whose value is a non-negative integer constant which identifies > the address space to which the pointer refers. The value > `DW_ASPACE_default` identifies the default address space; other > values and their uses are assigned by the ABI committee for the > target. > > Address space identifiers are also used by the DWARF > operations `DW_OP_mem` (see Section 3.7), `DW_OP_aspace_bregx` (see > Section 3.7), and `DW_OP_aspace_deref*` (see Section 3.13). In Section 4.3.3 Subroutine and Entry Point Locations, remove the following paragraph: > Subroutines and entry points may also have a DW_AT_address_class > attribute, if appropriate, to specify the addressing mode to be > used in calling that subroutine. In Section 6.3 Type Modifier Entries, after the paragraph "A modified type entry describing a pointer or reference type ... may have a `DW_AT_address_class` attribute...", add the following paragraph: > A modified type entry describing a pointer or reference type > (using `DW_TAG_pointer_type`, `DW_TAG_reference_type` or > `DW_TAG_rvalue_reference_type`) may have a `DW_AT_byte_size` > or `DW_AT_bit_size` attribute whose integer constant value > (see Section 2.20) is the amount of storage needed to hold > the pointer value. (These attributes are already explicitly listed for these types in Appendix A, Attributes by Tag.) In Appendix A, Attributes by Tag, remove `DW_AT_address_class` from the following TAG entries: - `DW_TAG_entry_point` - `DW_TAG_subprogram` - `DW_TAG_subroutine_type` - `DW_TAG_with_stmt` [260211.1]: https://dwarfstd.org/issues/260211.1.html --- 2026-06-18: Revised to address review comments. 2026-06-22: Revised to add `DW_AT_byte_size` and `DW_AT_bit_size` for pointer and reference types. 2026-06-22: Accepted. In example for address-to-offset conversion, added note about two's complement arithmetic.