Issue 250408.1: Replace composite locations with mapping lists

Author: Ron Brender
Champion: Ron Brender
Date submitted: 2025-04-08
Date revised: 2026-01-04
Date closed: 2026-05-07
Type: Enhancement
Status: Withdrawn
DWARF version: 6

Introduction

This proposal provides a complete replacement for the concept of composite locations as described in DWARF V5 and of composite storage as proposed in DWARF V6 (DW_OP_piece and friends are removed). A primary goal is improved space and time efficiency through elimination of the intermediate composite storage representation defined in the current V6 specification.

The proposal is organized in three parts:

  1. Section 3.20.2.1 [2.6.3.2.1] is the core proposal and a necessary base for the other two parts are moot.
  2. Section 3.20.2.2 [2.6.3.2.2] adds operations that seek to optimize the size of the operands when constant.
  3. Section 3.20.2.3 [2.6.3.2.3] adds operations that seek to integrate additional useful liveliness information at minimum additional cost.

Given part 1, either of the other two can be considered independently.

Note: Section numbers refer to the current working draft of Version 6. If section number in square brackets immediately follows, it gives the corresponding corresponding section in the V5 specification.

Discussion

In contrast to the composite location formulations in V5 and proposed in V6, this approach does not notionally construct a complete description of an object in some imaginary address space. Rather it describes mappings only for locations of an object that are not in their “normal” locations when, due to optimization, part(s) or the entire object are promoted to alternate locations (typically registers). The intent is that any and all address arithmetic involved in accessing parts of an object are performed in their normal manner in the containing memory address space of an object but an access to the resulting location involves mapping that location as appropriate to its “current” promoted location prior to accessing any contents.

Briefly, the new group of mapping operations all take four operands and push one result on the stack (with one exception). In half of the group, all four operands are on the stack, while in the other half only the first is on the stack and the other three are inline operands that follow the opcode. The operands are:

  1. A “source location” to be mapped to a new, or possibly the same, “target location”.
  2. The beginning location of a sequence of contiguous “mapped locations” that are to be mapped.
  3. The beginning location of a sequence of contiguous “target locations”.
  4. The number of locations in each of the two sequences.

The result is defined as follows:

A. If the source location is not in the range of locations defined by the second and fourth operands, then the result is the same as the source location. B. Otherwise, let I be the zero-based ordinal position of the source location in the mapped location sequence; the result is the corresponding Ith target location in the target location sequence.

The location information for an object is split into two parts:

It is helpful to observe that the DW_AT_mapping attribute is only needed if and when an object is not a single consecutive sequence of locations in memory.

To illustrate consider this simple example: Let OBJ be an object of type Cmpl which is a struct or record consisting of two single precision floating point components, RE and IM, as in

type Cmpl is struct {
    float RE;
    float IM;
}

Further suppose that in a certain part of the program extending from program counter values PC1 through PC2, the second component IM is promoted to register RX.

The value of the DW_AT_location attribute will be just the simple base location for the object as a whole, &OBJ.

The value of the DW_AT_mapping attribute will be a location list which could look like:

    DW_LLE_start_end, PC1, PC2+1
    .uleb 2$-1$                 ! Size of mapping expression
1$: DW_OP_addrx (OBJ+4)         ! Push beginning of O.IM
    DW_OP_regRX                 ! Push beginning of target RX
    DW_OP_const4                ! Size of each range
    DW_OP_map
2$: DW_LLE_end_of_list

Note that a source location that is not in the same address space as the mapped locations is simply not mapped; it becomes the result.

The mapping is completely open-ended on both sides of the mapped sequence. This is deliberate and key to simple handling of hidden storage prior to the notional base address of an object, such as vtable pointers, RTTI or the like, that is seldom promoted (but if they are, they can be mapped like any other component). It also works well for C implicit arrays which have no defined length (other than that imposed by the size of the containing address space).

To find the location OBJ.RE, look up OBJ, get its base address from the DW_AT_location attribute, look up component RE in its type, get its offset from the DW_AT_data_member_location attribute and add them together—all exactly as in classic DWARF. Then, push the result &OBJ.RE on the stack and scan the mapping list for a matching entry.

To find the location of OBJ.IM, the same process causes &OBJ+4 to be pushed on the stack. Then

Now, extend this example by adding a third component to the struct/record: an intensity value ITN, which is double precision floating point. Further suppose that the program occupies a sequence of addresses PC1 through PC4 where PC1 < PC2 < PC3 < PC4. Finally suppose component RE is always in memory, component IM is promoted to register RX during PC1..PC3 and component INT is promoted to register RY during PC2..PC4.

The mapping list now looks like:

    DW_LLE_start_end, PC1, PC3+1
    .uleb 2$-1$                 ! Size of first mapping expression
1$: DW_OP_addrx (O+4)           ! Push beginning of O.IM
    DW_OP_regRX                 ! Push beginning of target RX
    DW_OP_const4                ! Size of each range
    DW_OP_map
2$: DW_LLE_start_end, PC2, PC4+1
    .uleb 4$-3$                 ! Size of second mapping expression
3$: DW_OP_addrx (O+8)           ! Push beginning of O.ITN
    DW_OP_regRY
    DW_OP_const8
    DW_OP_map
4$: DW_LLE_end_of_list

Note that the result of the first DW_OP_map operation becomes the first parameter of the second DW_OP_map operation.

It is worth highlighting here that there is nothing special about the fact that in the PC range PC2..PC3 there are two components that are mapped. Because the memory ranges for the two components do not overlap, there is no interaction between the two. Because the memory ranges do not overlap, an implementation can end evaluation of the mapping list as soon as a match is found.

An Important Special case

It is expected that in a large percentage of cases, all three of these special cases apply:

  1. Component location within the containing object can be easily determined as an offset relative to the base of the object (for example, when all components are of fixed size), where the needed base address is given by the DW_AT_location (or similar) attribute.
  2. The target location is a register that can be identified using its ABI-defined register number.
  3. The size of the mapped and target ranges is a constant value.

Given these constraints, the last three operands can be expressed as unsigned LEB128 integers that are inline (following the mapping opcode). These constraints happen to apply in the above example, which can thereby be simplified to the following:

    DW_LLE_start_end, PC1, PC3+1
    .uleb 2$-1$                 ! Size of first mapping expression
1$: DW_OP_mapc
    .uleb 4                     ! Offset of O.IM relative to O
    .uleb <ABI code for register RX>
    .uleb 4                     ! Size of mapped and target ranges
2$: DW_LLE_start_end, PC2, PC4+1
    .uleb 4$-3$                 ! Size of second mapping expression
3$: .uleb 8
    .uleb <ABI code for register RY>
    .uleb 8
    DW_OP_mapc
4$: DW_LLE_end_of_list

The suffix c at the end of the compact form operator name is intended to suggest “compact”.

Note that the mapped range of addresses can be calculated dynamically, for example, as a function of a loop control variable. This makes it straightforward to describe loop unrolling. A fully developed example of this is included in one of the examples provided later in this proposal.

Nothing further is needed to handle a dynamic or unbounded size vector.

Futures

There are several opportunities for additional operations that support the same paradigm but cater to common special cases and/or idiomatic patterns of use. These may be considered as follow-on proposals.

One obvious simple set of special cases is to define variants DW_OP_map1, DW_OP_map2, DW_OP_map4, DW_OP_map8 and possibly DW_OP_map16, corresponding to an implicit size parameter of 1, 2, 4, 8 and 16, respectively.

I also think it would be simple to define special case unroll variants in support of typical unroll factors (say 2, 4, 8, 16). This would be especially true if the ABI defined some set of registers as a sequence. One might think of these as “macros” that are functionally equivalent to a sequence of mappings in the obvious way. I have not formalized the details but the approach seems promising.

PROPOSAL

In Table 2.2 of Section 2.2 Attributes Names, insert the following row:

DW_AT_mappings      Supplementary location information describing
                    parts of an object that change location at run-time.

In Table 2.3 Classes of attribute value of Section 2.2 Attribute Types, insert the following row:

maplist           Specifies the location of a mapping list.

In Section 2.5, change the count of the number of DWARF storage kinds from six to five. Further, delete the last bullet at the end of the section which defines composite storage.

In Section 3.17 [2.5.1.7] Special Operations, add the following at the end of the section:

Other special operations include the mapping operations which are defined only for use in mapping lists (see Section 3.20).

Delete Section 3.12 [2.6.1.2] Composite Locations in total.

Insert new Section 3.20 Mapping Lists

3.20 Mapping Lists

Mapping lists are used to supplement the location information given in location lists. They describe the actual location of a piece of an object that has moved from its original or nominal location within an object during execution.

Location lists describe the location of a complete object while mapping lists describe the location of a piece of a complete object that has moved to another location (normally outside of the object such as to a register).

3.20.1 Mapping List Representation

Mapping lists are contained in a separate object file section, along with location lists and value lists (see Section {locationlists}).

A mapping list is indicated by an attribute whose value is of class maplist (see Section {classesandforms}).

A mapping list consists of a series of mapping list entries. The representation of a mapping list is the same as for a location list (see Section {locationlists}), except that

  1. The expression of a counted location description must be one of the mapping list operations (see Section 3.20.1 [2.6.3.1]).

    Any suitable operation may occur within expressions that push operands on the stack for use by a mapping operation.

  2. There is no default mapping description that is analogous to a default location description. That is, use of the DW_LLE_default_location entry kind is not defined in a mapping list.

A mapping list entry produces a location. Certain mapping operations also convey other information as a consequence of their evaluation.

3.20.2 Mapping List Operations

There are three groups of mapping list operations, which are all very similar. They differ primarily in how their operands are expressed and how many results may be produced.

3.20.2.1 General Form Mapping List Operations

The general form mapping operations all take the following four operands on the stack, consisting of

  • S, the source location (TOS-3)
  • M, the starting location of the mapped range (TOS-2)
  • T, the starting location of the target range (TOS-1)
  • N, the size of both ranges (TOS)

The result R is pushed on the stack, where

  • If S is not in the mapped range (including not in the same address space as that range) then S is the result.
  • Otherwise, if S is the same as M, then T is the result.
  • Otherwise, S corresponds to an element of M, call it the I'th, then the result corresponds to the I'th element of T.

The operators are:

  1. DW_OP_map

    DW_OP_map pops four operands from the stack, consisting of

    • S, the source location (TOS-3)
    • M, the starting location of the mapped range (TOS-2)
    • T, the starting location of the target range (TOS-1)
    • N, the size of both ranges (TOS)

    The size is given in bytes and the locations must all be byte-aligned.

    The result R (see above) is pushed on the stack.

    The result is necessarily byte-aligned.

  2. DW_OP_bit_map

    DW_OP_bit_map takes the same operands as DW_OP_map, except that the size is given in bits and the locations need not be byte-aligned. The result is calculated in the same way as for DW_OP_map.

3.20.2.2 Compact Form Mapping Operations

The compact form operations are equivalent to the general form operations given in the previous section except that the last three operations are given as inline constants following the operator. To make this possible, no address within the address range of a mapping entry may overlap the address range of more than one location list entry or value list entry of the containing object. This ensures that the mapped range is based on a unique location.

The compact form mapping operations are:

  1. DW_OP_mapc

    DW_OP_mapc pops one operand from the stack, consisting of S, the source location. There are also three inline operands following, consisting of

    • M, the starting location of the mapped range, an unsigned LEB value relative to the nominal address of the containing object as specified by the DW_AT_location attribute of the containing object.
    • T, the starting location of the target range, an unsigned LEB128 value for a register number as defined by the architecture ABI.
    • N, the size of both ranges, an unsigned LEB128 value.

    The offset to the beginning of the mapped range and size are given in bytes. The size of the specified register must be equal to or larger than the size of the mapped range.

    The result R (see Section 3.20.2.1) is pushed on the stack.

  2. DW_OP_bit_mapc

    DW_OP_bit_mapc takes the same operands as DW_OP_mapc, except that the offset to the beginning of the mapped range and size are given in bits. The result is calculated in the same way as for DW_OP_map.

3.20.2.3 Multi-Target Mapping Operations

All of the mapping operations given above provide a single result. However, immediately after the instruction that copies an in-memory component into a register, it may be that the original component remains alive and may be accessed later. In rare cases, there may be more than one target location, all of which contain the same value as the component in the parent variable.

In practice, a location that is dead, that is, whose contents is never used again should not be included in the location list(s) or result(s) of a mapping list of an object even though its most recent value may persist in that location for some time.

The multi-target operators are:

  1. DW_OP_maps

    DW_OP_maps is identical to DW_OP_map except that in addition to the result, the source location continues to be alive (when not the same as the result).

  2. DW_OP_bit_maps

    DW_OP_bit_maps is identical to DW_OP_bit_map except that in addition to the result, the source location continues to be alive (when not the same as the result).

  3. DW_OP_mapm

    DW_OP_mapm pops four + C operands from the stack, where C is the value of the second operand on the stack. The operands consist of

    • S, the source location (TOS-C-3)
    • M, the starting location of the mapped range (TOS-C-2)
    • T..T<1>1, the starting locations of C target ranges (TOS-1..TOS-C-1)
    • C, the unsigned ULEB count of the number of target ranges (TOS-1)
    • N, the size of all ranges (TOS)

    The size N is given in bytes and the locations must all be byte-aligned.

    [ASIDE: C = 0 is a null operation and C=1 is redundant with DW_OP_map, thus is not useful. Thus, C could be biased by 2 to allow a barely more compact representation in a few cases. This does not seem worthwhile.]

    The result R (see Section 3.20.2.1) is pushed on the stack.

    In addition, for each T, T + M-S becomes or remains alive.

    The result is necessarily byte-aligned. Unlike DW_OP_maps, the source location is treated explicitly by this operator. If the source location should be kept alive, it must be included again among the set of target locations.

  4. DW_OP_bit_mapm

    DW_OP_bit_mapm takes the same operands as DW_OP_mapm, except that the size is given in bits and the locations need not be byte-aligned. The result is calculated in the same way as for DW_OP_mapm and all targets become or remain alive as for DW_OP_mapm.

A compact form of DW_OP_bit_mapm is not provided because multiple targets are rare and the inline operands would have to be in a different order (with the count of targets before the sequence of targets rather than after).

In Section 4.1[5.1] Data Object Entries, insert the following after item 4:

  1. A DW_AT_mappings attribute, whose value describes the pieces of an object that have moved, relative to the object as a whole, to another location at run-time.

    If no DW_AT_mappings attribute is present then the object is represented as a single consecutive sequence of bits or bytes throughout its lifetime (if present at all).

In Table 7.5[8.5] Attribute Encodings of Section 7.3[8.3], add new row for

DW_AT_mappings ‡    0x??        maplist

In Section 7.5.5[8.5.5] Classes and Forms, insert before the entry for rnglist:

  • maplist

    A mapping list (see Section 2.6.3). This class has the same representation as class loclist.

    This class is new in DWARF Version 6.

In Table 8.6[7.6] of Section 8.5.6 Form Encodings, add maplist to the Classes columns for DW_FORM_loclistx.

In Table 8.9[7.9] DWARF operation encodings of Section 8.7.1[7.7.1], insert the following rows for new operations:

DW_OP_map ‡       ox??        4       location,
                                      location,
                                      location,
                                      ULEB128 constant

DW_OP_bit_map ‡   0x??        4       location,
                                      location,
                                      location,
                                      ULEB128 constant

DW_OP_mapc ‡      0x??        1       location

DW_OP_bit_mapc ‡  0x??        1       location

DW_OP_maps ‡      ox??        4       location,
                                      location,
                                      location,
                                      ULEB128 constant

DW_OP_bit_maps ‡  0x??        4       location,
                                      location,
                                      location,
                                      ULEB128 constant

DW_OP_mapm ‡      0x??        3+C     location,
                                      location,
                                      location,...,location,
                                      ULEB128 constant,
                                      ULEB128 constant

DW_OP_bit_mapm ‡  0x??        3+C     location,
                                      location,
                                      location,...,location,
                                      ULEB128 constant,
                                      ULEB128 constant

In Table A.1 of Appendix A, add the attribute DW_AT_mappings to the right column for the TAGs DW_TAG_constant, DW_TAG_formal_parameter and DW_TAG_variable.

In Appendix D.1.3, change the title to Location Mapping Examples, The entire section is replaced. For the benefit of this proposal, the following side-by-side comparison of the current and proposed replacement text:

From V6 post 230524.1 version of D.1.3 DWARF Location Expression Examples. Shows equivalent DW_OP_map* version at right. Assume the source is contiguous and in memory at address 0 for direct comparison with composites. Uses DW_OP_mapc where possible for simplicity.

---------
DW_OP_composite  
DW_OP_reg3  
DW_OP_piece (4)                        DW_OP_mapc (0, ABI reg3, 4)

DW_OP_reg10
DW_OP_piece (2)                        DW_OP_mapc (4, ABI reg10, 2)

A variable whose first four bytes reside in register 3 and whose next two
bytes reside in register 10.

-----------
DW_OP_composite
DW_OP_reg0
DW_OP_piece (4)                       DW_OP_mapc (0, ABI reg0, 4)

                                      DW_OP_lit4
                                      DW_OP_undefined
                                      DW_OP_lit4
DW_OP_piece (4)                       DW_OP_map

                                      DW_OP_lit8
DW_OP_fbreg (-12)                     DW_OP_fbreg (-12)
                                      DW_OP_lit4
DW_OP_piece (4)                       DW_OP_map

A twelve byte value whose first four bytes reside in register zero, whose
middle four bytes are unavailable (perhaps due to optimization), and
whose last four bytes are in memory, 12 bytes before the frame base.

-----------
DW_OP_composite                       DW_OP_lit0
DW_OP_lit1                            DW_OP_lit1
DW_OP_stack_value                     DW_OP_stack_value
DW_OP_piece (4)                       DW_OP_lit4
                                      DW_OP_map
                                      DW_OP_lit4
DW_OP_breg3 (0)                       DW_OP_breg3 (0)
DW_OP_breg4 (0)                       DW_OP_breg4 (0)
DW_OP_plus                            DW_OP_plus
DW_OP_stack_value                     DW_OP_stack_value
DW_OP_piece (4)                       DW_OP_lit4
                                      DW_OP_map

The object value is found in an anonymous (virtual) location whose value
consists of two parts, given in memory address order: the 4 byte value 1
followed by the four byte value computed from the sum of the contents of
r3 and r4.

----------
DW_OP_composite
                                      DW_OP_lit0
DW_OP_reg0                            DW_OP_reg0
                                      DW_OP_lit31
                                      DW_OP_bit_offset
                                      DW_OP_lit1
DW_OP_bit_piece (1, 31)               DW_OP_bit_map

                                      DW_OP_lit1
                                      DW_OP_undefined
                                      DW_OP_lit7
DW_OP_bit_piece (7, 0)                DW_OP_bit_map
DW_OP_reg1
DW_OP_piece (1)                       DW_OP_mapc (1, ABI reg1, 1)

A variable whose first bit resides in the 31st bit of register 0, whose next
seven bits are undefined and whose second byte resides in register 1.

Using only the right column, the actual replacement text for Appendix D.1.3 is:

These examples are based on D.1.3 DWARF Location Expression Examples from DWARF Version 5. They assume the base object in memory is at location 0 for direct comparison. They use DW_OP_mapc where possible for simplicity and compactness. Not shown are the location list and mapping list entries (giving the applicable PC range).

.loclist
   DW_OP_lit0
.maplist
   DW_OP_mapc (0, ABI reg3, 4)

   DW_OP_mapc (4, ABI reg10, 2)

A variable whose first four bytes reside in register 3 and whose next two bytes reside in register 10.

.loclist
   DW_OP_lit0
.maplist
   DW_OP_mapc (0, ABI reg0, 4)

   DW_OP_lit4
   DW_OP_undefined
   DW_OP_lit4
   DW_OP_map

   DW_OP_lit8
   WW_OP_fbreg (-12)
   DW_OP_lit4
   DW_OP_map

A twelve byte value whose first four bytes reside in register zero, whose middle four bytes are unavailable (perhaps due to optimization), and whose last four bytes are in memory, 12 bytes before the frame base.

.loclist
   DW_OP_lit0
.maplist
    DW_OP_lit0
    DW_OP_lit1
    DW_OP_stack_value
    DW_OP_lit4
    DW_OP_map

    DW_OP_lit4
    DW_OP_breg3 (0)
    DW_OP_breg4 (0)
    DW_OP_plus
    DW_OP_stack_value
    DW_OP_lit4
    DW_OP_map

The object value is found in an anonymous (virtual) location whose value consists of two parts, given in memory address order: the 4 byte value 1 followed by the four byte value computed from the sum of the contents of r3 and r4.

.loclist
   DW_OP_lit0
.maplist
    DW_OP_lit0
    DW_OP_reg0
    DW_OP_lit31
    DW_OP_bit_offset
    DW_OP_lit1
    DW_OP_bit_map

    DW_OP_lit1
    DW_OP_undefined
    DW_OP_lit7
    DW_OP_bit_map

    DW_OP_mapc (1, ABI reg1, 1)

A variable whose first bit resides in the 31st bit of register 0, whose next seven bits are undefined and whose second byte resides in register 1.

In Appendix D. Figure D.86: SIMD Lane Example: Pseudo-Assembly Code, change all labels to begin with ".L" (rather than ".l") for better readability. Also, add the label ".L4" following the final return instruction.

In Appendix D Examples, replace Figure D.87 SIMD Lane Example with the following:

    DW_TAG_subprogram
        DW_AT_name ("vec_add")
        DW_AT_num_lanes .vallist.0
        DW_TAG_formal_parameter
            DW_AT_name ("dst")
            DW_AT_type (reference to type: pointer to int)
            DW_AT_location (DW_OP_reg0)
            DW_AT_mappings .maplist.1
        DW_TAG_formal_parameter
            DW_AT_name ("src")
            DW_AT_type (reference to type: pointer to int)
            DW_AT_location (DW_OP_reg1)
            DW_AT_mappings .maplist.2
        DW_TAG_formal_parameter
            DW_AT_name ("len")
            DW_AT_type (reference to type int)
            DW_AT_location DW_OP_reg2
        ...
        DW_TAG_variable
            DW_AT_name ("i")
            DW_AT_type (reference to type int)
            DW_AT_location .loclist.3
        ...

.vallist.0:
    range [.L1, .L2)
        DW_OP_lit8
    end-of-list

.maplist.1:                    ! dst
    range [.L1, .L2)
        DW_OP_breg0 (0)        ! Base of source range for dst
        DW_OP_breg3 (0)        ! i
        DW_OP_lit4
        DW_OP_mul
        DW_OP_add
        DW_OP_regx v0          ! Start of target
        DW_OP_constu (32)      ! Size of mapped range
        DW_OP_map
    end-of-list

.maplist.2:                    ! src
    range [.L1, .L2)
        DW_OP_breg1 (0)        ! Base source range for src
        DW_OP_breg3 (0)        ! i
        DW_OP_lit4
        DW_OP_mul
        DW_OP_add
        DW_OP_regx v0          ! Start of target
        DW_OP_constu (32)      ! Size of mapped range
        DW_OP_map
    end-of-list

.loclist.3:                    ! i
    range [.L0, .L1)
        DW_OP_reg3
    range [.L1, .L2)
        DW_OP_breg3 (0)
        DW_OP_push_lane
        DW_OP_plus
        DW_OP_stack_value
    range [.L2, .L4)
        DW_OP_reg3
    end-of-list

2025-10-06: Replaced original concept proposal with fully-developed proposal.

2025-10-13: Fixed typos and other minor editorial issues.

2026-01-04: Revised. Complete proposed examples in full detail, and update other editorial matters.

2026-05-07: Withdrawn.