Issue 260617.1: Clarify Address Classes vs. Address Spaces
| Author: | Cary Coutant |
|---|---|
| Champion: | |
| Date submitted: | 2026-06-17 |
| Date revised: | 2026-06-22 |
| Date closed: | 2026-06-22 |
| Type: | Clarification |
| Status: | Accepted |
| DWARF version: | 6 |
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 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, 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_classattribute, whose value is an integer constant. The set of permissible values is specific to each target architecture. The valueDW_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:]
Any debugging information entry representing a pointer or reference type may also have a
DW_AT_address_spaceattribute, whose value is a non-negative integer constant which identifies the address space to which the pointer refers. The valueDW_ASPACE_defaultidentifies 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), andDW_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_typeorDW_TAG_rvalue_reference_type) may have aDW_AT_byte_sizeorDW_AT_bit_sizeattribute 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_pointDW_TAG_subprogramDW_TAG_subroutine_typeDW_TAG_with_stmt
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.