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:
- 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.
- Section 3.20.2.2 [2.6.3.2.2] adds operations that seek to optimize the size of the operands when constant.
- 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:
- A “source location” to be mapped to a new, or possibly the same, “target location”.
- The beginning location of a sequence of contiguous “mapped locations” that are to be mapped.
- The beginning location of a sequence of contiguous “target locations”.
- 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:
- The normal
DW_AT_locationinformation that gives the location of the object as a whole. If the complete object moves, but is always contained in a single contiguous sequence of locations, then it is completely describable using a traditional location list. This applies generally to all scalar objects but is sometimes true of composite objects as well. - A new
DW_AT_mappingattribute is introduced whose value is also a location list. The “expressions” in this location list, however, consist solely of a sequence ofDW_OP_map[*]operations together with their second, third and fourth operands (which are all pushed or all inline).
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.
- If the current PC is not in the range PC1..PC2, there is no match and the result on the stack is the same as the input, namely the location of OBJ.
- If the current PC is in the range PC1..PC2 then there is a match for the mapping entry but the source location for &OBJ.RE is not in the range to be mapped so the result is again &OBJ.RE.
To find the location of OBJ.IM, the same process causes &OBJ+4 to be pushed on the stack. Then
- If the current PC is not in the range PC1..PC2, there is no match and the result on the stack is the same as the input, namely the location of OBJ+4.
- If the current PC is in the range PC1..PC2 then there is a match for the mapping entry AND the source location for &OBJ.RE is in the range to be mapped so the result is the location for register RX.
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:
- 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. - The target location is a register that can be identified using its ABI-defined register number.
- 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
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.
There is no default mapping description that is analogous to a default location description. That is, use of the
DW_LLE_default_locationentry 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:
DW_OP_map
DW_OP_mappops 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.
DW_OP_bit_map
DW_OP_bit_maptakes the same operands asDW_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 forDW_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:
DW_OP_mapc
DW_OP_mapcpops 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_locationattribute 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.
DW_OP_bit_mapc
DW_OP_bit_mapctakes the same operands asDW_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 forDW_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:
DW_OP_maps
DW_OP_mapsis identical toDW_OP_mapexcept that in addition to the result, the source location continues to be alive (when not the same as the result).
DW_OP_bit_maps
DW_OP_bit_mapsis identical toDW_OP_bit_mapexcept that in addition to the result, the source location continues to be alive (when not the same as the result).
DW_OP_mapm
DW_OP_mapmpops 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.
DW_OP_bit_mapm
DW_OP_bit_mapmtakes the same operands asDW_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 forDW_OP_mapmand all targets become or remain alive as forDW_OP_mapm.A compact form of
DW_OP_bit_mapmis 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:
A
DW_AT_mappingsattribute, 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_mappingsattribute 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. UsesDW_OP_mapcwhere 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_mapcwhere 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_mapA 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_mapThe 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.