summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZoran Zaric <Zoran.Zaric@amd.com>2020-12-07 19:00:31 +0000
committerSimon Marchi <simon.marchi@polymtl.ca>2020-12-08 11:16:21 -0500
commit1127a49b3242844d45d90658c7bfa886cd742ef1 (patch)
treea304584cbadb93df024c5df62075c9b1f19a3d7c
parentdab5aee6dc3a8b19d5058f7c1b24d632a78a7adf (diff)
downloadbinutils-gdb-users/zoran/allow-location-description-on-dwarf-stack.tar.gz
Add support for nested composite locationsusers/zoran/allow-location-description-on-dwarf-stack
After allowing a location description to be placed on a DWARF stack, in an effort to achieve a full composability of the DWARF expression, it is necessary to enable forming of a nested composite location descriptions. To be able do this, a new operation DW_OP_LLVM_piece_end needs to be introduced, along with some additional rules on the way how the composite location description is formed using the existing DW_OP_piece and DW_OP_bit_piece operations. These new rules are fully compatible with the composite forming rules from the DWARF 5 standard. More details on the new operation and added rules can be found here: https://llvm.org/docs/AMDGPUDwarfExtensionsForHeterogeneousDebugging.html gdb/ChangeLog: * compile/compile-loc2c.c (compute_stack_depth_worker): Add new DW_OP_LLVM_piece_end operation support. * dwarf2/expr.c (class dwarf_value): Add copy constructor. (class dwarf_location): Add copy constructor. (class dwarf_undefined): Add copy constructor. (class dwarf_memory): Add copy constructor. (class dwarf_register): Add copy constructor. (class dwarf_implicit): Add copy constructor. (class dwarf_implicit_pointer): Add copy constructor. (class dwarf_composite): Add copy constructor. (read_from_location): Add composite completed check. (write_to_location): Add composite completed check. (read_value_contents_from_location): New function. (dwarf_entry_factory::copy_entry): New method. (rw_closure_value): Now calls read_value_contents_from_location function. (dwarf_expr_context::add_piece): Use new composite forming rules. (dwarf_expr_context::execute_stack_op): Add new DW_OP_LLVM_piece_end operation support. * dwarf2/loc.c (dwarf2_get_symbol_read_needs): Add new DW_OP_LLVM_piece_end operation support. include/ChangeLog: * dwarf2.def (DW_OP_DUP): Add new DW_OP_LLVM_piece_end enumeration. gdb/testsuite/ChangeLog: * gdb.dwarf2/dw2-llvm-piece-end.exp: New test. Change-Id: Ib0b25e5de3f23df89d7d9e86aad56029c7d173df
-rw-r--r--gdb/compile/compile-loc2c.c1
-rw-r--r--gdb/dwarf2/expr.c305
-rw-r--r--gdb/dwarf2/loc.c1
-rw-r--r--gdb/testsuite/gdb.dwarf2/dw2-llvm-piece-end.exp191
-rw-r--r--include/dwarf2.def1
5 files changed, 469 insertions, 30 deletions
diff --git a/gdb/compile/compile-loc2c.c b/gdb/compile/compile-loc2c.c
index e9698a758e6..fd28f2cd4ed 100644
--- a/gdb/compile/compile-loc2c.c
+++ b/gdb/compile/compile-loc2c.c
@@ -365,6 +365,7 @@ compute_stack_depth_worker (int start, int *need_tempvar,
++stack_depth;
break;
+ case DW_OP_LLVM_piece_end:
case DW_OP_LLVM_offset_constu:
case DW_OP_nop:
break;
diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index a65d18d492a..647bf8810a5 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -308,6 +308,17 @@ public:
m_type = type;
}
+ dwarf_value (const dwarf_value &value)
+ {
+ struct type *type = value.m_type;
+ size_t type_len = TYPE_LENGTH (type);
+
+ m_contents.reset ((gdb_byte *) xzalloc (type_len));
+
+ memcpy (m_contents.get (), value.m_contents.get (), type_len);
+ m_type = type;
+ }
+
virtual ~dwarf_value () = default;
const gdb_byte* get_contents () const
@@ -352,6 +363,12 @@ public:
m_bit_suboffset = bit_suboffset % HOST_CHAR_BIT;
}
+ dwarf_location (const dwarf_location &location)
+ : m_offset (location.m_offset),
+ m_bit_suboffset (location.m_bit_suboffset),
+ m_initialised (location.m_initialised)
+ {}
+
virtual ~dwarf_location () = default;
LONGEST get_offset () const
@@ -404,6 +421,11 @@ public:
dwarf_undefined (LONGEST offset = 0, LONGEST bit_suboffset = 0)
: dwarf_location (offset, bit_suboffset)
{}
+
+ dwarf_undefined (const dwarf_undefined &undefined_entry)
+ : dwarf_location (undefined_entry)
+ {}
+
};
class dwarf_memory : public dwarf_location
@@ -415,6 +437,11 @@ public:
m_stack (stack)
{}
+ dwarf_memory (const dwarf_memory &memory_entry)
+ : dwarf_location (memory_entry),
+ m_stack (memory_entry.m_stack)
+ {}
+
bool in_stack () const
{
return m_stack;
@@ -440,6 +467,11 @@ public:
m_regnum (regnum)
{}
+ dwarf_register (const dwarf_register &register_entry)
+ : dwarf_location (register_entry),
+ m_regnum (register_entry.m_regnum)
+ {}
+
unsigned int get_regnum () const
{
return m_regnum;
@@ -468,6 +500,17 @@ public:
m_byte_order = byte_order;
}
+ dwarf_implicit (const dwarf_implicit &implicit_entry)
+ : dwarf_location (implicit_entry)
+ {
+ size_t size = implicit_entry.m_size;
+ m_contents.reset ((gdb_byte *) xzalloc (size));
+
+ memcpy (m_contents.get (), implicit_entry.m_contents.get (), size);
+ m_size = size;
+ m_byte_order = implicit_entry.m_byte_order;
+ }
+
const gdb_byte* get_contents () const
{
return m_contents.get ();
@@ -508,6 +551,14 @@ public:
m_addr_size (addr_size), m_die_offset (die_offset)
{}
+ dwarf_implicit_pointer (const dwarf_implicit_pointer &implicit_ptr_entry)
+ : dwarf_location (implicit_ptr_entry),
+ m_per_objfile (implicit_ptr_entry.m_per_objfile),
+ m_per_cu (implicit_ptr_entry.m_per_cu),
+ m_addr_size (implicit_ptr_entry.m_addr_size),
+ m_die_offset (implicit_ptr_entry.m_die_offset)
+ {}
+
dwarf2_per_objfile *get_per_objfile () const
{
return m_per_objfile;
@@ -551,6 +602,22 @@ public:
: dwarf_location (offset, bit_suboffset)
{}
+ dwarf_composite (const dwarf_composite &composite_entry)
+ : dwarf_location (composite_entry)
+ {
+ /* We do a shallow copy of the pieces because they are not
+ expected to be modified after they are already formed. */
+ for (unsigned int i = 0; i < composite_entry.m_pieces.size (); i++)
+ {
+ dwarf_location* location = composite_entry.m_pieces[i].m_location;
+
+ location->incref ();
+ m_pieces.emplace_back (location, composite_entry.m_pieces[i].m_size);
+ }
+
+ m_completed = composite_entry.m_completed;
+ }
+
/* A composite location gets detached from its factory object for
the purpose of lval_computed resolution, which means that it
needs to take care of garbage collecting its pieces. */
@@ -591,6 +658,16 @@ public:
return m_pieces.size ();
}
+ void set_completed (bool completed)
+ {
+ m_completed = completed;
+ };
+
+ bool is_completed () const
+ {
+ return m_completed;
+ };
+
private:
/* Composite piece that contains a piece location
description and it's size. */
@@ -608,6 +685,9 @@ private:
/* Vector of composite pieces. */
std::vector<struct piece> m_pieces;
+
+ /* True if location description is completed. */
+ bool m_completed = false;
};
/* Read contents from the location specified by the DWARF location
@@ -789,6 +869,9 @@ read_from_location (const dwarf_location *location, struct frame_info *frame,
unsigned int pieces_num = composite_entry->get_pieces_num ();
unsigned int i;
+ if (!composite_entry->is_completed ())
+ ill_formed_expression ();
+
total_bits_to_skip += offset * HOST_CHAR_BIT + bit_suboffset;
/* Skip pieces covered by the read offset. */
@@ -971,6 +1054,9 @@ write_to_location (const dwarf_location *location, struct frame_info *frame,
unsigned int pieces_num = composite_entry->get_pieces_num ();
unsigned int i;
+ if (!composite_entry->is_completed ())
+ ill_formed_expression ();
+
total_bits_to_skip += offset * HOST_CHAR_BIT + bit_suboffset;
/* Skip pieces covered by the write offset. */
@@ -1008,6 +1094,92 @@ write_to_location (const dwarf_location *location, struct frame_info *frame,
internal_error (__FILE__, __LINE__, _("invalid location type"));
}
+/* Read value contents from the location specified by the DWARF
+ location description entry LOCATION.
+
+ The read operation is performed in the context of FRAME. BIT_SIZE
+ is the number of bits to read. The data read is copied to the
+ caller-managed buffer BUF. BITS_TO_SKIP is a bit offset into the
+ location and BUF_BIT_OFFSET is buffer BUF's bit offset.
+ LOCATION_BIT_LIMIT is a maximum number of bits that location can
+ hold, where value zero signifies that there is no such restriction.
+
+ Note that some location types can be read without a FRAME context. */
+
+static void
+read_value_contents_from_location (struct value * value,
+ const dwarf_location *location,
+ struct frame_info *frame,
+ LONGEST bits_to_skip,
+ int value_bit_offset, size_t bit_size,
+ size_t location_bit_limit)
+{
+ /* Implicit pointers are handled later. */
+ if (dynamic_cast<const dwarf_implicit_pointer *> (location) != nullptr)
+ return;
+
+ auto composite_entry = dynamic_cast<const dwarf_composite *> (location);
+
+ if (composite_entry == nullptr)
+ {
+ int optimized, unavailable;
+ bool big_endian = type_byte_order (value_type (value)) == BFD_ENDIAN_BIG;
+
+ read_from_location (location, frame, bits_to_skip,
+ value_contents_raw (value),
+ value_bit_offset, bit_size, location_bit_limit,
+ big_endian, &optimized, &unavailable);
+
+ if (optimized)
+ mark_value_bits_optimized_out (value, value_bit_offset, bit_size);
+ if (unavailable)
+ mark_value_bits_unavailable (value, value_bit_offset, bit_size);
+
+ return;
+ }
+
+ if (!composite_entry->is_completed ())
+ ill_formed_expression ();
+
+ unsigned int pieces_num = composite_entry->get_pieces_num ();
+ unsigned int i;
+
+ LONGEST total_bits_to_skip = bits_to_skip
+ + composite_entry->get_offset () * HOST_CHAR_BIT
+ + composite_entry->get_bit_suboffset ();
+
+ /* Skip pieces covered by the read offset. */
+ for (i = 0; i < pieces_num; i++)
+ {
+ LONGEST piece_bit_size = composite_entry->get_bit_size_at (i);
+
+ if (total_bits_to_skip < piece_bit_size)
+ break;
+
+ total_bits_to_skip -= piece_bit_size;
+ }
+
+ for (; i < pieces_num; i++)
+ {
+ LONGEST piece_bit_size = composite_entry->get_bit_size_at (i);
+ const dwarf_location *piece = composite_entry->get_piece_at (i);
+
+ if (piece_bit_size > bit_size)
+ piece_bit_size = bit_size;
+
+ read_value_contents_from_location (value, piece, frame,
+ total_bits_to_skip,
+ value_bit_offset, piece_bit_size,
+ piece_bit_size);
+
+ if (bit_size == piece_bit_size)
+ break;
+
+ value_bit_offset += piece_bit_size;
+ bit_size -= piece_bit_size;
+ }
+}
+
/* Convert a value entry to the matching struct value representation
of a given TYPE. OFFSET defines the offset into the value
contents.
@@ -1107,6 +1279,9 @@ public:
dwarf_composite *create_composite (LONGEST offset = 0,
LONGEST bit_suboffset = 0);
+ /* Create a deep copy of the DWARF ENTRY. */
+ dwarf_entry *copy_entry (dwarf_entry *entry);
+
/* Convert an entry to a location description entry. If the entry
is a location description entry a dynamic cast is applied.
@@ -1252,6 +1427,33 @@ dwarf_entry_factory::create_composite (LONGEST offset, LONGEST bit_suboffset)
return composite_entry;
}
+dwarf_entry *
+dwarf_entry_factory::copy_entry (dwarf_entry *entry)
+{
+ dwarf_entry *entry_copy;
+
+ if (auto value = dynamic_cast<dwarf_value *> (entry))
+ entry_copy = new dwarf_value (*value);
+ else if (auto undefined = dynamic_cast<dwarf_undefined *> (entry))
+ entry_copy = new dwarf_undefined (*undefined);
+ else if (auto memory = dynamic_cast<dwarf_memory *> (entry))
+ entry_copy = new dwarf_memory (*memory);
+ else if (auto reg = dynamic_cast<dwarf_register *> (entry))
+ entry_copy = new dwarf_register (*reg);
+ else if (auto implicit = dynamic_cast<dwarf_implicit *> (entry))
+ entry_copy = new dwarf_implicit (*implicit);
+ else if (auto implicit_pointer
+ = dynamic_cast<dwarf_implicit_pointer *> (entry))
+ entry_copy = new dwarf_implicit_pointer (*implicit_pointer);
+ else if (auto composite = dynamic_cast<dwarf_composite *> (entry))
+ entry_copy = new dwarf_composite (*composite);
+ else
+ internal_error (__FILE__, __LINE__, _("invalid DWARF entry to copy."));
+
+ record_entry (entry_copy);
+ return entry_copy;
+}
+
dwarf_location *
dwarf_entry_factory::entry_to_location (dwarf_entry *entry)
{
@@ -1460,30 +1662,19 @@ rw_closure_value (struct value *v, struct value *from)
const dwarf_location *location = composite_entry->get_piece_at (i);
ULONGEST bit_size = composite_entry->get_bit_size_at (i);
size_t this_bit_size = bit_size - bits_to_skip;
- int optimized, unavailable;
if (this_bit_size > max_bit_offset - bit_offset)
this_bit_size = max_bit_offset - bit_offset;
if (from == NULL)
{
- /* Implicit pointers are handled later. */
- if (dynamic_cast<const dwarf_implicit_pointer *>
- (location) == nullptr)
- {
- read_from_location (location, frame, bits_to_skip,
- value_contents_raw (v), bit_offset,
- this_bit_size, bit_size, big_endian,
- &optimized, &unavailable);
-
- if (optimized)
- mark_value_bits_optimized_out (v, bit_offset, this_bit_size);
- if (unavailable)
- mark_value_bits_unavailable (v, bit_offset, this_bit_size);
- }
+ read_value_contents_from_location (v, location, frame, bits_to_skip,
+ bit_offset, this_bit_size, bit_size);
}
else
{
+ int optimized, unavailable;
+
write_to_location (location, frame, bits_to_skip,
value_contents (from), bit_offset,
this_bit_size, bit_size, big_endian,
@@ -1892,10 +2083,34 @@ private:
/* Pop a top element of the stack and add as a composite piece.
- If the fallowing top element of the stack is a composite
- location description, the piece will be added to it. Otherwise
- a new composite location description will be created and
- the piece will be added to that composite. */
+ The action is based on the context:
+
+ - If the stack is empty, then an incomplete composite location
+ description (comprised of one undefined location description),
+ is pushed on the stack.
+
+ - Otherwise, if the top stack entry is an incomplete composite
+ location description, then it is updated to append a new piece
+ comprised of one undefined location description. The
+ incomplete composite location description is then left on the
+ stack.
+
+ - Otherwise, if the top stack entry is a location description or
+ can be converted to one, it is popped. Then:
+
+ - If the top stack entry (after popping) is a location
+ description comprised of one incomplete composite location
+ description, then it is updated to append a new piece
+ specified by the previously popped location description.
+ The incomplete composite location description is then left
+ on the stack.
+
+ - Otherwise, a new location description comprised of one
+ incomplete composite location description, with a new piece
+ specified by the previously popped location description, is
+ pushed on the stack.
+
+ - Otherwise, the DWARF expression is ill-formed */
dwarf_entry *add_piece (ULONGEST bit_size, ULONGEST bit_offset);
/* The engine for the expression evaluator. Using the context in this
@@ -2534,26 +2749,39 @@ dwarf_expr_context::add_piece (ULONGEST bit_size, ULONGEST bit_offset)
dwarf_location *piece_entry;
dwarf_composite *composite_entry;
- if (!stack_empty_p ()
- && dynamic_cast<dwarf_composite *> (fetch (0)) == nullptr)
+ if (stack_empty_p ())
+ piece_entry = entry_factory->create_undefined ();
+ else
{
piece_entry = entry_factory->entry_to_location (fetch (0));
- pop ();
+
+ if (auto old_composite_entry
+ = dynamic_cast<dwarf_composite *> (piece_entry))
+ {
+ if (!old_composite_entry->is_completed ())
+ piece_entry = entry_factory->create_undefined ();
+ }
+ else if (dynamic_cast<dwarf_undefined *> (piece_entry) != nullptr)
+ pop ();
}
- else
- piece_entry = entry_factory->create_undefined ();
- piece_entry->add_bit_offset (bit_offset);
+ if (dynamic_cast<dwarf_undefined *> (piece_entry) == nullptr)
+ {
+ piece_entry->add_bit_offset (bit_offset);
+ pop ();
+ }
- /* If stack is empty then it is a start of a new composite. In the
- future this will check if the composite is finished or not. */
if (stack_empty_p ()
|| dynamic_cast<dwarf_composite *> (fetch (0)) == nullptr)
composite_entry = entry_factory->create_composite ();
else
{
composite_entry = dynamic_cast<dwarf_composite *> (fetch (0));
- pop ();
+
+ if (composite_entry->is_completed ())
+ composite_entry = entry_factory->create_composite ();
+ else
+ pop ();
}
composite_entry->add_piece (piece_entry, bit_size);
@@ -3156,7 +3384,7 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
break;
case DW_OP_dup:
- result_entry = fetch (0);
+ result_entry = entry_factory->copy_entry (fetch (0));
break;
case DW_OP_drop:
@@ -3182,7 +3410,7 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
}
case DW_OP_over:
- result_entry = fetch (1);
+ result_entry = entry_factory->copy_entry (fetch (1));
break;
case DW_OP_rot:
@@ -3742,6 +3970,23 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
result_entry = entry_factory->create_undefined ();
break;
+ case DW_OP_LLVM_piece_end:
+ {
+ dwarf_entry *entry = fetch (0);
+
+ dwarf_composite *composite_entry
+ = dynamic_cast<dwarf_composite *> (entry);
+
+ if (composite_entry == nullptr)
+ ill_formed_expression ();
+
+ if (composite_entry->is_completed ())
+ ill_formed_expression ();
+
+ composite_entry->set_completed (true);
+ goto no_push;
+ }
+
default:
error (_("Unhandled dwarf expression opcode 0x%x"), op);
}
diff --git a/gdb/dwarf2/loc.c b/gdb/dwarf2/loc.c
index 1a903ce1233..e98d6fb39d6 100644
--- a/gdb/dwarf2/loc.c
+++ b/gdb/dwarf2/loc.c
@@ -1832,6 +1832,7 @@ dwarf2_get_symbol_read_needs (gdb::array_view<const gdb_byte> expr,
case DW_OP_LLVM_offset:
case DW_OP_LLVM_bit_offset:
case DW_OP_LLVM_undefined:
+ case DW_OP_LLVM_piece_end:
break;
case DW_OP_form_tls_address:
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-llvm-piece-end.exp b/gdb/testsuite/gdb.dwarf2/dw2-llvm-piece-end.exp
new file mode 100644
index 00000000000..3da739ea720
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-llvm-piece-end.exp
@@ -0,0 +1,191 @@
+# Copyright 2017-2020 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# Test the nested composition location description by using the new
+# DW_OP_LLVM_piece_end operation.
+#
+# The test uses three nested levels of composite location descriptions
+# to define a location of an array.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if {![dwarf2_support]} {
+ return 0
+}
+
+# Choose suitable integer registers for the test.
+
+set dwarf_regnum 0
+
+if { [is_aarch64_target] } {
+ set regname x0
+} elseif { [is_aarch32_target]
+ || [istarget "s390*-*-*" ]
+ || [istarget "powerpc*-*-*"]
+ || [istarget "rs6000*-*-aix*"] } {
+ set regname r0
+} elseif { [is_x86_like_target] } {
+ set regname eax
+} elseif { [is_amd64_regs_target] } {
+ set regname rax
+} else {
+ verbose "Skipping ${gdb_test_file_name}."
+ return
+}
+
+standard_testfile var-access.c ${gdb_test_file_name}-dw.S
+
+# Make some DWARF for the test.
+
+set asm_file [standard_output_file $srcfile2]
+Dwarf::assemble $asm_file {
+ global dwarf_regnum regname srcdir subdir srcfile
+ set buf_src [gdb_target_symbol buf]
+
+ set main_result [function_range main ${srcdir}/${subdir}/${srcfile}]
+ set main_start [lindex $main_result 0]
+ set main_length [lindex $main_result 1]
+
+ cu {} {
+ DW_TAG_compile_unit {
+ {DW_AT_name var-access.c}
+ {DW_AT_comp_dir /tmp}
+ } {
+ declare_labels array_type_label int_type_label char_type_label
+
+ # define char type
+ char_type_label: DW_TAG_base_type {
+ {DW_AT_name "char"}
+ {DW_AT_encoding @DW_ATE_signed}
+ {DW_AT_byte_size 1 DW_FORM_sdata}
+ }
+
+ int_type_label: DW_TAG_base_type {
+ {DW_AT_name "int"}
+ {DW_AT_encoding @DW_ATE_signed}
+ {DW_AT_byte_size 4 DW_FORM_sdata}
+ }
+
+ array_type_label: DW_TAG_array_type {
+ {DW_AT_type :$char_type_label}
+ } {
+ DW_TAG_subrange_type {
+ {DW_AT_type :$int_type_label}
+ {DW_AT_upper_bound 7 DW_FORM_udata}
+ }
+ }
+
+ DW_TAG_subprogram {
+ {DW_AT_name main}
+ {DW_AT_low_pc $main_start addr}
+ {DW_AT_high_pc $main_length data8}
+ } {
+ # Array spread in different pieces, of which some are
+ # undefined (1st and sixth bytes) and some are either
+ # in buf variable or REGNAME register.
+ #
+ # Location consists of three nested composite levels:
+ # - Third level consists of a composite location
+ # descriptions which hold a single simple location
+ # description each.
+ # - Second level consist of two more composite location
+ # descriptions that hold two of the third level
+ # composite location descriptions.
+ # - First level holds two of the second level composite
+ # location descriptions.
+
+ DW_TAG_variable {
+ {DW_AT_name var_array}
+ {DW_AT_type :$array_type_label}
+ {DW_AT_location {
+ # First level composite start
+ # Second level first composite start
+ # Third level first composite start
+ DW_OP_addr $buf_src
+ DW_OP_piece 0x2
+ DW_OP_LLVM_piece_end
+ # Third level first composite end
+
+ # Third level second composite start
+ DW_OP_LLVM_undefined
+ DW_OP_piece 0x1
+ DW_OP_LLVM_piece_end
+ # Third level second composite end
+
+ DW_OP_piece 0x1
+ DW_OP_swap
+ DW_OP_piece 0x2
+ DW_OP_LLVM_piece_end
+ # Second level first composite end
+
+ # Second level second composite start
+ # Third level third composite start
+ DW_OP_regx $dwarf_regnum
+ DW_OP_piece 0x4
+ DW_OP_LLVM_piece_end
+ # Third level third composite end
+
+ # Third level fourth composite start
+ DW_OP_LLVM_undefined
+ DW_OP_piece 0x1
+ DW_OP_LLVM_piece_end
+ # Third level fourth composite end
+
+ DW_OP_piece 0x1
+ DW_OP_swap
+ DW_OP_piece 0x4
+ DW_OP_LLVM_piece_end
+ # Second level second composite end
+
+ DW_OP_piece 0x5
+ DW_OP_swap
+ DW_OP_piece 0x3
+ DW_OP_LLVM_piece_end
+ # First level composite end
+
+ } SPECIAL_expr}
+ }
+ }
+ }
+ }
+}
+
+if { [prepare_for_testing ${testfile}.exp ${testfile} \
+ [list $srcfile $asm_file] {nodebug}] } {
+ return -1
+}
+
+if ![runto_main] {
+ return -1
+}
+
+gdb_test_no_output "set var \$$regname = 0x4030201" "init reg"
+
+# Determine byte order.
+set endian [get_endianness]
+set optimized "<optimized out>"
+
+switch $endian {
+ little {
+ set val "$optimized, 0x1, 0x2, 0x3, 0x4, $optimized, 0x0, 0x1"
+ }
+ big {
+ set val "$optimized, 0x4, 0x3, 0x2, 0x1, $optimized, 0x0, 0x1"
+ }
+}
+
+gdb_test "print/x var_array" " = \\{${val}\\}" "var_array print"
+
diff --git a/include/dwarf2.def b/include/dwarf2.def
index b58560296d6..fa6c20a9ef0 100644
--- a/include/dwarf2.def
+++ b/include/dwarf2.def
@@ -709,6 +709,7 @@ DW_OP_DUP (DW_OP_LLVM_offset, 0xe3)
DW_OP_DUP (DW_OP_LLVM_offset_constu, 0xe4)
DW_OP_DUP (DW_OP_LLVM_bit_offset, 0xe5)
DW_OP (DW_OP_LLVM_undefined, 0xe7)
+DW_OP_DUP (DW_OP_LLVM_piece_end, 0xea)
DW_END_OP
DW_FIRST_ATE (DW_ATE_void, 0x0)