#endif /* GDB_SELF_TEST */
 
+/* Return the number of bytes overlapping a contiguous chunk of N_BITS
+   bits whose first bit is located at bit offset START.  */
+
+static size_t
+bits_to_bytes (ULONGEST start, ULONGEST n_bits)
+{
+  return (start % 8 + n_bits + 7) / 8;
+}
+
 static void
 read_pieced_value (struct value *v)
 {
       if (this_size_bits > max_offset - offset)
        this_size_bits = max_offset - offset;
 
-      this_size = (this_size_bits + source_offset_bits % 8 + 7) / 8;
+      this_size = bits_to_bytes (source_offset_bits, this_size_bits);
       buffer.reserve (this_size);
       source_offset = source_offset_bits / 8;
       intermediate_buffer = buffer.data ();
            struct frame_info *frame = frame_find_by_id (c->frame_id);
            struct gdbarch *arch = get_frame_arch (frame);
            int gdb_regnum = dwarf_reg_to_regnum_or_error (arch, p->v.regno);
+           ULONGEST reg_bits = 8 * register_size (arch, gdb_regnum);
            int optim, unavail;
-           LONGEST reg_offset = source_offset;
 
            if (gdbarch_byte_order (arch) == BFD_ENDIAN_BIG
-               && this_size < register_size (arch, gdb_regnum))
+               && p->size < reg_bits)
              {
                /* Big-endian, and we want less than full size.  */
-               reg_offset = register_size (arch, gdb_regnum) - this_size;
-               /* We want the lower-order THIS_SIZE_BITS of the bytes
-                  we extract from the register.  */
-               source_offset_bits += 8 * this_size - this_size_bits;
+               source_offset_bits += reg_bits - p->size;
              }
+           this_size = bits_to_bytes (source_offset_bits, this_size_bits);
+           buffer.reserve (this_size);
 
-           if (!get_frame_register_bytes (frame, gdb_regnum, reg_offset,
+           if (!get_frame_register_bytes (frame, gdb_regnum,
+                                          source_offset_bits / 8,
                                           this_size, buffer.data (),
                                           &optim, &unavail))
              {
              }
 
            copy_bitwise (contents, dest_offset_bits,
-                         intermediate_buffer, source_offset_bits % 8,
+                         buffer.data (), source_offset_bits % 8,
                          this_size_bits, bits_big_endian);
          }
          break;
       if (this_size_bits > max_offset - offset)
        this_size_bits = max_offset - offset;
 
-      this_size = (this_size_bits + dest_offset_bits % 8 + 7) / 8;
+      this_size = bits_to_bytes (dest_offset_bits, this_size_bits);
       source_offset = source_offset_bits / 8;
       dest_offset = dest_offset_bits / 8;
 
            struct frame_info *frame = frame_find_by_id (c->frame_id);
            struct gdbarch *arch = get_frame_arch (frame);
            int gdb_regnum = dwarf_reg_to_regnum_or_error (arch, p->v.regno);
-           int reg_offset = dest_offset;
+           ULONGEST reg_bits = 8 * register_size (arch, gdb_regnum);
 
            if (gdbarch_byte_order (arch) == BFD_ENDIAN_BIG
-               && this_size <= register_size (arch, gdb_regnum))
+               && p->size <= reg_bits)
              {
                /* Big-endian, and we want less than full size.  */
-               reg_offset = register_size (arch, gdb_regnum) - this_size;
+               dest_offset_bits += reg_bits - p->size;
              }
+           this_size = bits_to_bytes (dest_offset_bits, this_size_bits);
+           buffer.reserve (this_size);
 
-           if (need_bitwise)
+           if (dest_offset_bits % 8 != 0 || this_size_bits % 8 != 0)
              {
+               /* Data is copied non-byte-aligned into the register.
+                  Need some bits from original register value.  */
                int optim, unavail;
 
-               if (!get_frame_register_bytes (frame, gdb_regnum, reg_offset,
+               if (!get_frame_register_bytes (frame, gdb_regnum,
+                                              dest_offset_bits / 8,
                                               this_size, buffer.data (),
                                               &optim, &unavail))
                  {
                                     "bitfield; containing word "
                                     "is unavailable"));
                  }
-               copy_bitwise (buffer.data (), dest_offset_bits,
-                             contents, source_offset_bits,
-                             this_size_bits,
-                             bits_big_endian);
              }
 
-           put_frame_register_bytes (frame, gdb_regnum, reg_offset, 
-                                     this_size, source_buffer);
+           copy_bitwise (buffer.data (), dest_offset_bits % 8,
+                         contents, source_offset_bits,
+                         this_size_bits, bits_big_endian);
+           put_frame_register_bytes (frame, gdb_regnum,
+                                     dest_offset_bits / 8,
+                                     this_size, buffer.data ());
          }
          break;
        case DWARF_VALUE_MEMORY:
 
                        piece 1
                    } SPECIAL_expr}
                }
+               # Register pieces for bitfield access: 4 bytes optimized
+               # out, 3 bytes from r0, and 1 byte from r1.
+               DW_TAG_variable {
+                   {name "t2"}
+                   {type :$struct_t_label}
+                   {location {
+                       piece 4
+                       regx [lindex $dwarf_regnum 0]
+                       piece 3
+                       regx [lindex $dwarf_regnum 1]
+                       piece 1
+                   } SPECIAL_expr}
+               }
            }
        }
     }
 #                             val
 gdb_test "print/x a" " = \\{0x0, ${val}, 0x0, 0x0\\}" \
     "verify st1 through a"
+
+switch $endian { big {set val 0x7ffc} little {set val 0x3ffe00} }
+gdb_test_no_output "set var \$[lindex $regname 0] = $val" \
+    "init t2, first piece"
+gdb_test_no_output "set var \$[lindex $regname 1] = 0" \
+    "init t2, second piece"
+gdb_test "print/d t2" " = \\{u = <optimized out>, x = 0, y = -1, z = 0\\}" \
+    "initialized t2 from regs"
+gdb_test_no_output "set var t2.y = 2641"
+gdb_test_no_output "set var t2.z = -400"
+gdb_test_no_output "set var t2.x = 200"
+gdb_test "print t2.x + t2.y + t2.z" " = 2441"