Skip to content

recognize DW_AT_address_space and use in pointers - #255

Open
aktemur wants to merge 13 commits into
amd-stagingfrom
users/aktemur/DW_AT_address_space
Open

recognize DW_AT_address_space and use in pointers#255
aktemur wants to merge 13 commits into
amd-stagingfrom
users/aktemur/DW_AT_address_space

Conversation

@aktemur

@aktemur aktemur commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Motivation

Recognize the DW_AT_address_space attribute for pointer types. Use this information to dereference, assign, cast, etc. pointers.

Technical Details

Add an attribute to pointer types and use it in various pointer-related operations.

Test Plan

Via existing and new tests.

Test Result

No ROCgdb regression. There are a few failures related to a dbgapi issue, which I'll handle separately.

aktemur added 13 commits August 6, 2026 15:27
…r-input.exp

In gdb.rocm/aspace-user-input.exp there are some tests that convert a
pointer-to-address-space to a scalar value.  Although today such
pointer values are implemented by carrying the address space bits in
the core address, the pointer value is in fact a pair: address space
id and offset in the target memory.  Therefore, it doesn't make sense
to cast such pointers to scalar and expect that they will still retain
the same meaning.  In DWARF, converting a memory location in a
non-default address space to a scalar value is not possible.  Such a
conversion is defined only for memory locations to the default address
space.  Hence, remove those tests.
Define the DW_AT_address_space attribute (currently still an LLVM
extension).
Revise the `dwarf_address_space_to_address_space_id` gdbarch function
as follows:

  - update the default implementation to iterate over the existing
    `address_spaces` list obtained from the arch, to detect
    unrecognized address spaces earlier,

  - make it a method to have a handle on the gdbarch object,

  - rename the method to `address_space_dwarf_to_id` to match the
    existing `address_class_dwarf_to_id` gdbarch function.

  - update the address space argument to be of an unsigned type.
…_name_to_id

Rename gdbarch_name_to_address_space_id to
gdbarch_address_space_name_to_id to match the existing
address_class_name_to_id, address_class_id_to_name,
address_class_dwarf_to_id, address_space_dwarf_to_id,
address_space_id_to_name pattern.
Read DW_AT_address_space attribute of a pointer, reference, or an
rvalue reference type.  The attribute is then saved in the type
instance flags of the type.  For this purposes, we allocate a bitfield
in type instance flags.
When creating a pointer value with a given address and type, if the
address denotes a non-default address space, pass that information to
the type, so that newly-formed pointer value's type knows which
address space it points to.

The practical impact of this change is that we produce a pointer type
with address space information when (1) the address-space operator '#'
is used, and (2) when we take the address of a variable.  E.g.:

  (gdb) print &tid
  $1 = (int *) private_lane#0x0
  (gdb) maintenance print type $1
  type node 0x588d3e19be90
  name '<unnamed type>' (0x0)
  code 0x1 (TYPE_CODE_PTR)
  ...
  instance_flags [ TYPE_ADDRESS_SPACE(5)]      <<< AS SEEN HERE
  ...
  (gdb) print local#0x12
  $2 = (void *) local#0x12
  (gdb) maintenance print type $2
  ...
  instance_flags [ TYPE_ADDRESS_SPACE(3)]      <<< AND HERE.
  ...
  (gdb)
In language_defn::watch_location_expression, GDB builds a watchpoint
expression that would later be parsed and evaluated to an address.
The address received in this method maybe have an address space
encoded in it.  This is only because GDB currently does not track
addresses as a pair of address space and offset.  So, do not use that
address verbatim in the watchpoint expression; instead, split it apart
to the address space and segment address pieces and use the '#'
operator.

With this change, for example, a watchpoint expression that would
previously be built as "(int *) 0x0008800000000018" becomes
"(int *) private_lane#0x18".

Tested by gdb.rocm/aspace-watchpoint.exp.
Define a new gdbarch method, pointer_to_pointer, so that architectures
can convert one pointer value to another.  This is in particular
needed for when pointers may point to different address spaces.  For
instance, on AMD GPUs, a pointer to the local data share (LDS) address
space may be converted to a generic pointer value.  This would happen,
for example, when the user does the following in a program where 'ptr'
is a generic program and 'lds_var' is a variable that is located in
LDS memory.

  (gdb) print ptr = &lds_var

Similarly, it's possible to convert a private lane/wave address to a
generic pointer value or to an address in the default (i.e. global)
address space.

In value_cast_pointers, if the source and destination pointers have
different address spaces, call the gdbarch method to let the
architecture do the conversion of the address value.

A particularly interesting case is when the source of casting is a
pointer to an address space whereas the destination is a pointer with
no address space.  This can happen when the user wants to specify the
data type that is being pointed at.  A typical usecase would be the
address space operator '#', which gives a pointer to void ('void *'),
so that it can be dereferenced.  E.g.

  (gdb) p *(int *)local#0x123
  # Want to read an int from 'local' memory, offset 0x123.

Because "int *" is a pointer type with no address space annotation,
casting would appear to be from the 'local' address space to the
default 'global' address space, which may not be possible.  For user's
convenience, propagate the address space from the operator to the
destination type of casting.  With this, we would get, for instance:

  (gdb) p local#0x123
  $1 = (void *) local#0x123      <<< Pointer to void in 'local' aspace.
  (gdb) p (int *)local#0x123
  $2 = (int *) local#0x123       <<< Pointer to int in 'local' aspace.
  (gdb) p *(int *)local#0x123
  $3 = ....                      <<< Read an int from 'local' memory, offset 0x123.

Similarly, the user can do things like

  (gdb) p &an_int_var_in_local_address_space
  $3 = (int *) local#0x123       <<< Pointer to int in 'local' aspace.
  (gdb) p (short *) &an_int_var_in_local_address_space
  $4 = (short *) local#0x123     <<< Pointer to short in 'local' aspace.

If there is more to the expression that requires further conversion,
the existing casting mechanism with gdbarch_pointer_to_pointer would
handle it.  For example, if 'ptr' is a generic pointer, then:

  (gdb) p ptr = local#0x123
  $5 = (int *) generic#0x1000000000123     <<< Pointer to int in 'generic' aspace.

If 'gptr' were a pointer to the global address space to which it is
not possible to convert a pointer to local, GDB would give an error:

  (gdb) p gptr = local#0x123
  cannot convert pointer-to-'local' to a pointer-to-'global'

Tested by gdb.rocm tests when a subsequent patch implements the
pointer_to_pointer gdbarch method for the amdgpu architecture.
…_aliases

Extract a function from amdgpu_get_watchable_aliases into
amdgpu_convert_address to convert an address in a particular address
space to an address in another address space.  This is a refactoring
with the goal of making address conversion a reusable function.
Use address space information attached to a pointer type to convert
the pointer value to an address.  This is done in the
'pointer_to_address' gdbarch method.

Before this change, we have, for example:

  (gdb) print ptr
  $1 = (short *) 0x2000000000010
  (gdb) print *ptr
  Cannot access memory at address 0x2000000000010
  (gdb)

After this change, we get:

  (gdb) print ptr
  $1 = (short *) generic#0x2000000000010
  (gdb) print *ptr
  $2 = 12
  (gdb)

We also define the 'address_to_pointer' gdbarch method to convert an
address to the corresponding pointer value.  This is important in
particular when the user assigns new values to pointers.  E.g.:

Before this change:

  (gdb) p ptr = generic#0x1234
  $2 = (int *) generic#0x1234
  (gdb) x/1gx &ptr
  private_lane#0x20:      0x0000800000001234
  (gdb)

Note the value 0x0000800000001234 contains address space id bits still
encoded.

After this change:

  (gdb) p ptr = generic#0x1234
  $2 = (int *) generic#0x1234
  (gdb) x/1gx &ptr
  private_lane#0x20:      0x0000000000001234
  (gdb)

We also implement the 'pointer_to_pointer' gdbarch method.  This is
essential to convert addresses according to the address spaces.  E.g.:

  (gdb) p pointer
  $1 = (int *) generic#0x2000000000000
  (gdb) p &loc_int
  $2 = (int *) local#0x8
  (gdb) p pointer = &loc_int
  $3 = (int *) generic#0x1000000000008
  (gdb) p loc_int
  $4 = 42
  (gdb) p *pointer
  $5 = 42
  (gdb)

Several tests can now be turned from KFAIL into PASS.

There are two testcases in gdb.rocm/finish.exp that demand
explanation.  Suppose we have a generic pointer that points to a
global variable named 'global_var'.  Before this change, printing the
pointer gives:

  (gdb) p global_ptr
  $1 = (int *) 0x7ffff4e6bbf0 <global_var>
  (gdb)

Note the '<global_var>' annotation showing the symbol that is being
pointed to.  After this change, we get

  (gdb) p global_ptr
  $1 = (int *) generic#0x7ffff4e6bbf0

There is no symbol annotation anymore.  This is not a regression,
though.  It is rather a side-effect of taking address space
information of a pointer type into account and printing that address
space in the pointer value.  The same behavior existed before this
patch:

  (gdb) p (int *) 0x7ffff4e6bbf0
  $5 = (int *) 0x7ffff4e6bbf0 <global_var>
  (gdb) p (int *) generic#0x7ffff4e6bbf0
  $6 = (int *) generic#0x7ffff4e6bbf0

Bug: AIROCGDB-605
Add new tests to gdb.rocm/generic-address.exp where we check that
writing via generic pointers works as expected.
Add a test to check that a generic null pointer is printed without
problems.

Also check that global_ptr and global_ptr2 have exactly the same
contents.  This is essentially the same as resuming the program to
end, but we prefer to do the check more explicitly in the test by
reading pointer values, because in case of a failure debugging becomes
easier.
Pointers to address spaces are not necessarily the same size as
default pointers.  Define a gdbarch method to ask the architecture for
the size of a pointer to a particular address space and use that
information in make_qualified_type, which is the utility function used
by make_type_with_address_space.  This is the place where
address-spaced pointer type variants are added to the type chain.

Ideally DWARF should tell the size of a pointer to an address space
with a DW_AT_byte_size attribute and this value should match what the
arch says.  Complain if there is a mismatch and continue with the size
set by the arch.

Also update value_cast to do casting from a pointer to a pointer
without the strict requirement that they have the same length.

Implement the gdbarch method in the amdgpu arch.
@aktemur
aktemur requested a review from a team as a code owner August 6, 2026 13:30
@aktemur aktemur assigned palves and unassigned lancesix Aug 6, 2026
@palves

palves commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator

Is it really DW_AT_address_space? Or DW_LLVM_AT_address_space?

@aktemur

aktemur commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

Is it really DW_AT_address_space? Or DW_LLVM_AT_address_space?

llvm-dwarfdump displays it as DW_AT_LLVM_address_space. https://dwarfstd.org/issues/260211.1.html is not officially accepted, and there is no binary code for the attribute, yet. So, I defined it in GDB as DW_AT_LLVM_address_space, but in the code I usually refer to it as DW_AT_address_space.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants