]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
Add support for nested composite locations users/zoran/allow-location-description-on-dwarf-stack
authorZoran Zaric <Zoran.Zaric@amd.com>
Mon, 7 Dec 2020 19:00:31 +0000 (19:00 +0000)
committerSimon Marchi <simon.marchi@polymtl.ca>
Tue, 8 Dec 2020 16:16:21 +0000 (11:16 -0500)
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

gdb/compile/compile-loc2c.c
gdb/dwarf2/expr.c
gdb/dwarf2/loc.c
gdb/testsuite/gdb.dwarf2/dw2-llvm-piece-end.exp [new file with mode: 0644]
include/dwarf2.def

index e9698a758e659adbd3e809d1baa419948554a957..fd28f2cd4ed618862f1ff97aaa65fd9c51807f37 100644 (file)
@@ -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;
index a65d18d492a9bc6ca3615e48a0b7fb313e1c9533..647bf8810a5cc801be832e7d1132bd74fa20d6a4 100644 (file)
@@ -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);
        }
index 1a903ce123332753f93d935640560e404b10ff25..e98d6fb39d6843ac95be775ec8661a84536680b7 100644 (file)
@@ -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 (file)
index 0000000..3da739e
--- /dev/null
@@ -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"
+
index b58560296d6e4ba100a32bd939d7ec14d2541c85..fa6c20a9ef0c07329a75ba8a5c87207397a8962e 100644 (file)
@@ -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)