* [RFC PATCH v4 00/15] gdbserver improvements for AArch64 SVE support
@ 2024-11-02 2:56 Thiago Jung Bauermann
2024-11-02 2:56 ` [RFC PATCH v4 01/15] GDB, gdbserver: Convert regcache_register_size function to method Thiago Jung Bauermann
` (14 more replies)
0 siblings, 15 replies; 16+ messages in thread
From: Thiago Jung Bauermann @ 2024-11-02 2:56 UTC (permalink / raw)
To: gdb-patches
Hello,
This is the fourth try at implementing variable-length register support in
gdbserver and the remote protocol. It corresponds to the talk I gave at
GNU Tools Cauldron 2024:
https://www.youtube.com/watch?v=sghWiqTKBN0&list=PL_GiHdX17Wtye5q4zPVnJc6ec7PdnUNil&index=7
The v3 submission is here:
https://inbox.sourceware.org/gdb-patches/20230130044518.3322695-1-thiago.bauermann@linaro.org/
Thank you all for attending the talk and for the great discussion and
suggestions that followed it. I really appreciate it. This code doesn't
reflect that discussion nor the suggestions though, because I wanted to
post a version of the patches that corresponded to what I presented.
I will address the presentation comments in the next version. Namely:
- Pedro's suggestion of changing the VG register (which gives the size of
the vector registers) to appear before the variable-length vector
registers in the internal GDB register numbering, without affecting the
remote protocol register numbering.
- Simon's suggestion of keeping the fixed-size registers and the
variable-size registers in the same regcache buffer, making sure that
the latter are always placed at the end.
- Baris' suggestion of implementing an 'e' packet to request the expedited
registers from the remote target.
- Luis' suggestion of extending the 'p' packet to fetch a specific list or
registers.
There are some FIXMEs in this code, which is why one of the reasons this
version is marked as RFC. The other reason is the lack of implementing the
suggestions above.
v4 is a different approach from the previous versions, because the main
point of this series is to allow expressing a vector register's size using
a math expression. Previous versions relied on a different target
description for each register size.
The first 5 patches are cleanups and can be pushed independently, if
approved. Some of them are about using gdb::array_view<gdb_byte> in more
functions related to unwinding and manipulating registers, so that GDB can
make sure that the buffers being passed around have the correct size.
This helped me gain confidence that variable-sized registers were being
correctly read and stored, but I also think it's a useful cleanup in
general.
Thiago Jung Bauermann (15):
GDB, gdbserver: Convert regcache_register_size function to method
GDB: Use gdb::array_view for buffers used in register reading and
unwinding
GDB: remote: Print total bytes received in debug message
GDB: trad-frame: Store length of value_bytes in trad_frame_saved_reg
gdbserver: nat/aarch64: Only check Z registers for extra contents if
they can have it
gdbserver: Implement p and P packets
GDB, gdbserver: Create concept of load-early registers
GDB: Allow DWARF expression evaluation to use regcache
GDB tdesc: Add vector type with number of elements given by math
expression.
GDB: Add concept of variable-size registers to the regcache
gdbserver: Add concept of variable-size registers to the regcache
GDB: aarch64-linux: Load and store VG separately from other SVE
registers
GDB, gdbserver: aarch64: Convert SVE feature to use variable-size
registers
GDB, gdbserver: aarch64: Use VG instead of VQ
gdb/testsuite: Add test to exercise multi-threaded AArch64 SVE
inferiors
gdb/aarch64-linux-nat.c | 53 ++-
gdb/aarch64-linux-tdep.c | 55 ++-
gdb/aarch64-tdep.c | 124 +++---
gdb/aarch64-tdep.h | 21 +-
gdb/arch/aarch64.c | 36 +-
gdb/arch/aarch64.h | 25 +-
gdb/dwarf2/expr.c | 63 ++-
gdb/dwarf2/expr.h | 17 +-
gdb/dwarf2/frame.c | 2 +-
gdb/dwarf2/loc.c | 41 +-
gdb/dwarf2/loc.h | 2 +
gdb/eval.c | 13 +-
gdb/features/Makefile | 1 +
gdb/features/aarch64-sve.c | 199 +++++----
gdb/features/aarch64-sve.xml | 217 +++++++++
gdb/features/gdb-target.dtd | 17 +-
gdb/findvar.c | 22 +-
gdb/frame-unwind.c | 18 +-
gdb/frame-unwind.h | 2 +-
gdb/frame.c | 72 +--
gdb/frame.h | 8 +-
gdb/gdbarch-gen.c | 32 ++
gdb/gdbarch-gen.h | 9 +
gdb/gdbarch_components.py | 11 +
gdb/gdbtypes.c | 138 ++++--
gdb/gdbtypes.h | 7 +-
gdb/gnu-v3-abi.c | 2 +-
gdb/i387-tdep.c | 4 +-
gdb/jit.c | 2 +-
gdb/mips-tdep.c | 19 +-
gdb/nat/aarch64-hw-point.c | 2 +-
gdb/nat/aarch64-scalable-linux-ptrace.c | 52 +--
gdb/nat/aarch64-scalable-linux-ptrace.h | 12 +-
gdb/process-stratum-target.h | 3 +
gdb/python/py-unwind.c | 7 +-
gdb/record-full.c | 21 +-
gdb/regcache-dump.c | 2 +-
gdb/regcache.c | 376 +++++++++++++---
gdb/regcache.h | 61 ++-
gdb/regformats/regdef.h | 35 +-
gdb/remote.c | 163 +++++--
gdb/stack.c | 2 +-
gdb/target-descriptions.c | 114 ++++-
gdb/target-descriptions.h | 11 +
gdb/testsuite/gdb.arch/aarch64-sve-threads.c | 125 ++++++
.../gdb.arch/aarch64-sve-threads.exp | 80 ++++
gdb/trad-frame.h | 10 +-
gdb/value.c | 16 +-
gdb/value.h | 3 +
gdb/xml-tdesc.c | 416 +++++++++++++++++-
gdbserver/Makefile.in | 2 +
gdbserver/ax.cc | 2 +-
gdbserver/config.in | 3 +
gdbserver/configure | 6 +
gdbserver/configure.ac | 5 +
gdbserver/configure.srv | 1 +
gdbserver/linux-aarch64-low.cc | 6 +-
gdbserver/linux-aarch64-tdesc.cc | 17 +-
gdbserver/locexpr.cc | 107 +++++
gdbserver/locexpr.h | 31 ++
gdbserver/regcache.cc | 217 +++++++--
gdbserver/regcache.h | 32 +-
gdbserver/remote-utils.cc | 2 +-
gdbserver/server.cc | 75 ++++
gdbserver/tdesc.cc | 110 ++++-
gdbserver/tdesc.h | 4 +-
gdbserver/tracepoint.cc | 4 +-
gdbsupport/common-regcache.h | 19 +-
gdbsupport/tdesc.cc | 143 +++++-
gdbsupport/tdesc.h | 60 ++-
70 files changed, 3022 insertions(+), 567 deletions(-)
create mode 100644 gdb/features/aarch64-sve.xml
create mode 100644 gdb/testsuite/gdb.arch/aarch64-sve-threads.c
create mode 100644 gdb/testsuite/gdb.arch/aarch64-sve-threads.exp
create mode 100644 gdbserver/locexpr.cc
create mode 100644 gdbserver/locexpr.h
^ permalink raw reply [flat|nested] 16+ messages in thread
* [RFC PATCH v4 01/15] GDB, gdbserver: Convert regcache_register_size function to method
2024-11-02 2:56 [RFC PATCH v4 00/15] gdbserver improvements for AArch64 SVE support Thiago Jung Bauermann
@ 2024-11-02 2:56 ` Thiago Jung Bauermann
2024-11-02 2:56 ` [RFC PATCH v4 02/15] GDB: Use gdb::array_view for buffers used in register reading and unwinding Thiago Jung Bauermann
` (13 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Thiago Jung Bauermann @ 2024-11-02 2:56 UTC (permalink / raw)
To: gdb-patches
The regcache_register_size function has one implementation in GDB, and
one in gdbserver. Both of them have a gdb::checked_static_cast to their
corresponding regcache class. This can be avoided by defining a
pure virtual register_size method in the
reg_buffer_common class, which is then implemented by the reg_buffer
class in GDB, and by the regcache class in gdbserver.
Calls to the register_size () function from methods of classes in the
reg_buffer_common hierarchy need to be changed to calls to the newly
defined method, otherwise the compiler complains that a matching method
cannot be found.
---
gdb/i387-tdep.c | 4 ++--
gdb/nat/aarch64-hw-point.c | 2 +-
gdb/regcache-dump.c | 2 +-
gdb/regcache.c | 18 ++++++++----------
gdb/regcache.h | 3 +++
gdbserver/regcache.cc | 5 ++---
gdbserver/regcache.h | 3 +++
gdbsupport/common-regcache.h | 16 +++++-----------
8 files changed, 25 insertions(+), 28 deletions(-)
diff --git a/gdb/i387-tdep.c b/gdb/i387-tdep.c
index 3bda88807943..92f898caf770 100644
--- a/gdb/i387-tdep.c
+++ b/gdb/i387-tdep.c
@@ -1565,7 +1565,7 @@ i387_collect_xsave (const struct regcache *regcache, int regnum,
byte_order, I387_FCTRL_INIT_VAL);
else
memset (FXSAVE_ADDR (tdep, regs, i), 0,
- regcache_register_size (regcache, i));
+ regcache->register_size (i));
}
}
}
@@ -1888,7 +1888,7 @@ i387_collect_xsave (const struct regcache *regcache, int regnum,
int regsize;
regcache->raw_collect (i, raw);
- regsize = regcache_register_size (regcache, i);
+ regsize = regcache->register_size (i);
p = FXSAVE_ADDR (tdep, regs, i);
if (memcmp (raw, p, regsize))
{
diff --git a/gdb/nat/aarch64-hw-point.c b/gdb/nat/aarch64-hw-point.c
index 6acee0fb814c..8ab91fe85142 100644
--- a/gdb/nat/aarch64-hw-point.c
+++ b/gdb/nat/aarch64-hw-point.c
@@ -166,7 +166,7 @@ aarch64_point_is_aligned (ptid_t ptid, int is_watchpoint, CORE_ADDR addr,
/* Set alignment to 2 only if the current process is 32-bit,
since thumb instruction can be 2-byte aligned. Otherwise, set
alignment to AARCH64_HBP_ALIGNMENT. */
- if (regcache_register_size (regcache, 0) == 8)
+ if (regcache->register_size (0) == 8)
alignment = AARCH64_HBP_ALIGNMENT;
else
alignment = 2;
diff --git a/gdb/regcache-dump.c b/gdb/regcache-dump.c
index 3e6880535235..0a851e3ed771 100644
--- a/gdb/regcache-dump.c
+++ b/gdb/regcache-dump.c
@@ -110,7 +110,7 @@ class register_dump_reg_buffer : public register_dump, reg_buffer
{
if (regnum < gdbarch_num_regs (m_gdbarch) || m_has_pseudo)
{
- auto size = register_size (m_gdbarch, regnum);
+ auto size = register_size (regnum);
if (size == 0)
return;
diff --git a/gdb/regcache.c b/gdb/regcache.c
index f04354d822f9..6e0c730d0d59 100644
--- a/gdb/regcache.c
+++ b/gdb/regcache.c
@@ -179,10 +179,9 @@ register_size (struct gdbarch *gdbarch, int regnum)
/* See gdbsupport/common-regcache.h. */
int
-regcache_register_size (const reg_buffer_common *regcache, int n)
+reg_buffer::register_size (int regnum) const
{
- return register_size
- (gdb::checked_static_cast<const struct regcache *> (regcache)->arch (), n);
+ return ::register_size (this->arch (), regnum);
}
reg_buffer::reg_buffer (gdbarch *gdbarch, bool has_pseudo)
@@ -939,7 +938,7 @@ register_status
readable_regcache::read_part (int regnum, int offset,
gdb::array_view<gdb_byte> dst, bool is_raw)
{
- int reg_size = register_size (arch (), regnum);
+ int reg_size = register_size (regnum);
gdb_assert (offset >= 0);
gdb_assert (offset + dst.size () <= reg_size);
@@ -983,7 +982,7 @@ void
reg_buffer::raw_collect_part (int regnum, int offset,
gdb::array_view<gdb_byte> dst) const
{
- int reg_size = register_size (arch (), regnum);
+ int reg_size = register_size (regnum);
gdb_assert (offset >= 0);
gdb_assert (offset + dst.size () <= reg_size);
@@ -1013,7 +1012,7 @@ register_status
regcache::write_part (int regnum, int offset,
gdb::array_view<const gdb_byte> src, bool is_raw)
{
- int reg_size = register_size (arch (), regnum);
+ int reg_size = register_size (regnum);
gdb_assert (offset >= 0);
gdb_assert (offset + src.size () <= reg_size);
@@ -1065,7 +1064,7 @@ void
reg_buffer::raw_supply_part (int regnum, int offset,
gdb::array_view<const gdb_byte> src)
{
- int reg_size = register_size (arch (), regnum);
+ int reg_size = register_size (regnum);
gdb_assert (offset >= 0);
gdb_assert (offset + src.size () <= reg_size);
@@ -1226,8 +1225,7 @@ regcache::transfer_regset_register (struct regcache *out_regcache, int regnum,
const gdb_byte *in_buf, gdb_byte *out_buf,
int slot_size, int offs) const
{
- struct gdbarch *gdbarch = arch ();
- int reg_size = std::min (register_size (gdbarch, regnum), slot_size);
+ int reg_size = std::min (register_size (regnum), slot_size);
/* Use part versions and reg_size to prevent possible buffer overflows when
accessing the regcache. */
@@ -1244,7 +1242,7 @@ regcache::transfer_regset_register (struct regcache *out_regcache, int regnum,
else if (in_buf != nullptr)
{
/* Zero-extend the register value if the slot is smaller than the register. */
- if (slot_size < register_size (gdbarch, regnum))
+ if (slot_size < register_size (regnum))
out_regcache->raw_supply_zeroed (regnum);
out_regcache->raw_supply_part (regnum, 0,
gdb::make_array_view (in_buf + offs,
diff --git a/gdb/regcache.h b/gdb/regcache.h
index 2f4b7d94c693..65e9f7bb79da 100644
--- a/gdb/regcache.h
+++ b/gdb/regcache.h
@@ -256,6 +256,9 @@ class reg_buffer : public reg_buffer_common
/* See gdbsupport/common-regcache.h. */
bool raw_compare (int regnum, const void *buf, int offset) const override;
+ /* See gdbsupport/common-regcache.h. */
+ int register_size (int regnum) const override;
+
protected:
/* Assert on the range of REGNUM. */
void assert_regnum (int regnum) const;
diff --git a/gdbserver/regcache.cc b/gdbserver/regcache.cc
index 1bb71d103288..6a1526246867 100644
--- a/gdbserver/regcache.cc
+++ b/gdbserver/regcache.cc
@@ -308,10 +308,9 @@ register_size (const struct target_desc *tdesc, int n)
/* See gdbsupport/common-regcache.h. */
int
-regcache_register_size (const reg_buffer_common *regcache, int n)
+regcache::register_size (int regnum) const
{
- return register_size
- (gdb::checked_static_cast<const struct regcache *> (regcache)->tdesc, n);
+ return ::register_size (tdesc, regnum);
}
static gdb::array_view<gdb_byte>
diff --git a/gdbserver/regcache.h b/gdbserver/regcache.h
index 1752c3979d39..df0feca102e0 100644
--- a/gdbserver/regcache.h
+++ b/gdbserver/regcache.h
@@ -49,6 +49,9 @@ struct regcache : public reg_buffer_common
/* See gdbsupport/common-regcache.h. */
enum register_status get_register_status (int regnum) const override;
+ /* See gdbsupport/common-regcache.h. */
+ int register_size (int regnum) const override;
+
/* See gdbsupport/common-regcache.h. */
void raw_supply (int regnum, gdb::array_view<const gdb_byte> src) override;
diff --git a/gdbsupport/common-regcache.h b/gdbsupport/common-regcache.h
index f8704c16939a..4594999346fd 100644
--- a/gdbsupport/common-regcache.h
+++ b/gdbsupport/common-regcache.h
@@ -48,11 +48,6 @@ enum register_status : signed char
extern reg_buffer_common *get_thread_regcache_for_ptid (ptid_t ptid);
-/* Return the size of register numbered N in REGCACHE. This function
- must be provided by the client. */
-
-extern int regcache_register_size (const reg_buffer_common *regcache, int n);
-
/* Read the PC register. This function must be provided by the
client. */
@@ -78,6 +73,9 @@ struct reg_buffer_common
buffer. */
virtual register_status get_register_status (int regnum) const = 0;
+ /* Return the size of register numbered REGNUM in this buffer. */
+ virtual int register_size (int regnum) const = 0;
+
/* Supply register REGNUM, whose contents are stored in SRC, to this register
buffer. */
virtual void raw_supply (int regnum, gdb::array_view<const gdb_byte> src)
@@ -91,9 +89,7 @@ struct reg_buffer_common
void raw_supply (int regnum, const gdb_byte *src)
{
- raw_supply (regnum,
- gdb::make_array_view (src,
- regcache_register_size (this, regnum)));
+ raw_supply (regnum, gdb::make_array_view (src, register_size (regnum)));
}
/* Collect register REGNUM from this register buffer and store its contents in
@@ -109,9 +105,7 @@ struct reg_buffer_common
void raw_collect (int regnum, gdb_byte *dst)
{
- raw_collect (regnum,
- gdb::make_array_view (dst,
- regcache_register_size (this, regnum)));
+ raw_collect (regnum, gdb::make_array_view (dst, register_size (regnum)));
}
/* Compare the contents of the register stored in the regcache (ignoring the
^ permalink raw reply [flat|nested] 16+ messages in thread
* [RFC PATCH v4 02/15] GDB: Use gdb::array_view for buffers used in register reading and unwinding
2024-11-02 2:56 [RFC PATCH v4 00/15] gdbserver improvements for AArch64 SVE support Thiago Jung Bauermann
2024-11-02 2:56 ` [RFC PATCH v4 01/15] GDB, gdbserver: Convert regcache_register_size function to method Thiago Jung Bauermann
@ 2024-11-02 2:56 ` Thiago Jung Bauermann
2024-11-02 2:56 ` [RFC PATCH v4 03/15] GDB: remote: Print total bytes received in debug message Thiago Jung Bauermann
` (12 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Thiago Jung Bauermann @ 2024-11-02 2:56 UTC (permalink / raw)
To: gdb-patches
This allows checking the size of the given buffer. Changes
frame_register_unwind (), frame_unwind_register (), get_frame_register ()
and deprecated_frame_register_read ().
---
gdb/frame.c | 24 ++++++++++++++----------
gdb/frame.h | 8 ++++----
gdb/mips-tdep.c | 19 +++++++++++++------
gdb/stack.c | 2 +-
4 files changed, 32 insertions(+), 21 deletions(-)
diff --git a/gdb/frame.c b/gdb/frame.c
index a6900b280724..2c1ea012191b 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -1112,7 +1112,7 @@ frame_save_as_regcache (const frame_info_ptr &this_frame)
{
auto cooked_read = [this_frame] (int regnum, gdb::array_view<gdb_byte> buf)
{
- if (!deprecated_frame_register_read (this_frame, regnum, buf.data ()))
+ if (!deprecated_frame_register_read (this_frame, regnum, buf))
return REG_UNAVAILABLE;
else
return REG_VALID;
@@ -1177,7 +1177,8 @@ void
frame_register_unwind (const frame_info_ptr &next_frame, int regnum,
int *optimizedp, int *unavailablep,
enum lval_type *lvalp, CORE_ADDR *addrp,
- int *realnump, gdb_byte *bufferp)
+ int *realnump,
+ std::optional<gdb::array_view<gdb_byte>> bufferp)
{
struct value *value;
@@ -1204,11 +1205,13 @@ frame_register_unwind (const frame_info_ptr &next_frame, int regnum,
if (bufferp)
{
+ gdb_assert (bufferp->size () >= value->type ()->length ());
+
if (!*optimizedp && !*unavailablep)
- memcpy (bufferp, value->contents_all ().data (),
+ memcpy (bufferp->data (), value->contents_all ().data (),
value->type ()->length ());
else
- memset (bufferp, 0, value->type ()->length ());
+ memset (bufferp->data (), 0, value->type ()->length ());
}
/* Dispose of the new value. This prevents watchpoints from
@@ -1217,7 +1220,8 @@ frame_register_unwind (const frame_info_ptr &next_frame, int regnum,
}
void
-frame_unwind_register (const frame_info_ptr &next_frame, int regnum, gdb_byte *buf)
+frame_unwind_register (const frame_info_ptr &next_frame, int regnum,
+ gdb::array_view<gdb_byte> buf)
{
int optimized;
int unavailable;
@@ -1238,7 +1242,7 @@ frame_unwind_register (const frame_info_ptr &next_frame, int regnum, gdb_byte *b
void
get_frame_register (const frame_info_ptr &frame,
- int regnum, gdb_byte *buf)
+ int regnum, gdb::array_view<gdb_byte> buf)
{
frame_unwind_register (frame_info_ptr (frame->next), regnum, buf);
}
@@ -1447,7 +1451,7 @@ put_frame_register (const frame_info_ptr &next_frame, int regnum,
gdb_assert (buf.size () == size);
frame_register_unwind (next_frame, regnum, &optim, &unavail, &lval, &addr,
- &realnum, nullptr);
+ &realnum);
if (optim)
error (_("Attempt to assign to a register that was not saved."));
switch (lval)
@@ -1482,7 +1486,7 @@ put_frame_register (const frame_info_ptr &next_frame, int regnum,
bool
deprecated_frame_register_read (const frame_info_ptr &frame, int regnum,
- gdb_byte *myaddr)
+ gdb::array_view<gdb_byte> myaddr)
{
int optimized;
int unavailable;
@@ -1541,7 +1545,7 @@ get_frame_register_bytes (const frame_info_ptr &next_frame, int regnum,
int realnum;
frame_register_unwind (next_frame, regnum, optimizedp, unavailablep,
- &lval, &addr, &realnum, buffer.data ());
+ &lval, &addr, &realnum, buffer);
if (*optimizedp || *unavailablep)
return false;
}
@@ -2159,7 +2163,7 @@ frame_register_unwind_location (const frame_info_ptr &initial_this_frame,
int unavailable;
frame_register_unwind (this_frame, regnum, optimizedp, &unavailable,
- lvalp, addrp, realnump, NULL);
+ lvalp, addrp, realnump);
if (*optimizedp)
break;
diff --git a/gdb/frame.h b/gdb/frame.h
index e784c17b4807..23a3ade69c92 100644
--- a/gdb/frame.h
+++ b/gdb/frame.h
@@ -692,7 +692,7 @@ extern void frame_register_unwind (const frame_info_ptr &frame, int regnum,
int *optimizedp, int *unavailablep,
enum lval_type *lvalp,
CORE_ADDR *addrp, int *realnump,
- gdb_byte *valuep);
+ std::optional<gdb::array_view<gdb_byte>> valuep = {});
/* Fetch a register from this, or unwind a register from the next
frame. Note that the get_frame methods are wrappers to
@@ -701,9 +701,9 @@ extern void frame_register_unwind (const frame_info_ptr &frame, int regnum,
do return a lazy value. */
extern void frame_unwind_register (const frame_info_ptr &next_frame,
- int regnum, gdb_byte *buf);
+ int regnum, gdb::array_view<gdb_byte> buf);
extern void get_frame_register (const frame_info_ptr &frame,
- int regnum, gdb_byte *buf);
+ int regnum, gdb::array_view<gdb_byte> buf);
struct value *frame_unwind_register_value (const frame_info_ptr &next_frame,
int regnum);
@@ -889,7 +889,7 @@ extern void print_frame_info (const frame_print_options &fp_opts,
extern frame_info_ptr block_innermost_frame (const struct block *);
extern bool deprecated_frame_register_read (const frame_info_ptr &frame, int regnum,
- gdb_byte *buf);
+ gdb::array_view<gdb_byte> buf);
/* From stack.c. */
diff --git a/gdb/mips-tdep.c b/gdb/mips-tdep.c
index c00efbd02ad0..24970c7637ad 100644
--- a/gdb/mips-tdep.c
+++ b/gdb/mips-tdep.c
@@ -951,14 +951,17 @@ mips_register_to_value (const frame_info_ptr &frame, int regnum,
if (mips_convert_register_float_case_p (gdbarch, regnum, type))
{
- get_frame_register (frame, regnum + 0, to + 4);
- get_frame_register (frame, regnum + 1, to + 0);
+ gdb::array_view<gdb_byte> first_half = gdb::make_array_view (to, 4);
+ gdb::array_view<gdb_byte> second_half = gdb::make_array_view (to + 4, 4);
- if (!get_frame_register_bytes (next_frame, regnum + 0, 0, { to + 4, 4 },
+ get_frame_register (frame, regnum + 0, second_half);
+ get_frame_register (frame, regnum + 1, first_half);
+
+ if (!get_frame_register_bytes (next_frame, regnum + 0, 0, second_half,
optimizedp, unavailablep))
return 0;
- if (!get_frame_register_bytes (next_frame, regnum + 1, 0, { to + 0, 4 },
+ if (!get_frame_register_bytes (next_frame, regnum + 1, 0, first_half,
optimizedp, unavailablep))
return 0;
*optimizedp = *unavailablep = 0;
@@ -6257,8 +6260,10 @@ mips_read_fp_register_single (const frame_info_ptr &frame, int regno,
struct gdbarch *gdbarch = get_frame_arch (frame);
int raw_size = register_size (gdbarch, regno);
gdb_byte *raw_buffer = (gdb_byte *) alloca (raw_size);
+ gdb::array_view<gdb_byte> raw_view = gdb::make_array_view (raw_buffer,
+ raw_size);
- if (!deprecated_frame_register_read (frame, regno, raw_buffer))
+ if (!deprecated_frame_register_read (frame, regno, raw_view))
error (_("can't read register %d (%s)"),
regno, gdbarch_register_name (gdbarch, regno));
if (raw_size == 8)
@@ -6295,7 +6300,9 @@ mips_read_fp_register_double (const frame_info_ptr &frame, int regno,
{
/* We have a 64-bit value for this register, and we should use
all 64 bits. */
- if (!deprecated_frame_register_read (frame, regno, rare_buffer))
+ gdb::array_view<gdb_byte> rare_view = gdb::make_array_view (rare_buffer,
+ raw_size);
+ if (!deprecated_frame_register_read (frame, regno, rare_view))
error (_("can't read register %d (%s)"),
regno, gdbarch_register_name (gdbarch, regno));
}
diff --git a/gdb/stack.c b/gdb/stack.c
index 4a3e7e4ff006..b585e6e636d5 100644
--- a/gdb/stack.c
+++ b/gdb/stack.c
@@ -1750,7 +1750,7 @@ info_frame_command_core (const frame_info_ptr &fi, bool selected_frame_p)
/* Find out the location of the saved register without
fetching the corresponding value. */
frame_register_unwind (fi, i, &optimized, &unavailable,
- &lval, &addr, &realnum, NULL);
+ &lval, &addr, &realnum);
/* For moment, only display registers that were saved on the
stack. */
if (!optimized && !unavailable && lval == lval_memory)
^ permalink raw reply [flat|nested] 16+ messages in thread
* [RFC PATCH v4 03/15] GDB: remote: Print total bytes received in debug message
2024-11-02 2:56 [RFC PATCH v4 00/15] gdbserver improvements for AArch64 SVE support Thiago Jung Bauermann
2024-11-02 2:56 ` [RFC PATCH v4 01/15] GDB, gdbserver: Convert regcache_register_size function to method Thiago Jung Bauermann
2024-11-02 2:56 ` [RFC PATCH v4 02/15] GDB: Use gdb::array_view for buffers used in register reading and unwinding Thiago Jung Bauermann
@ 2024-11-02 2:56 ` Thiago Jung Bauermann
2024-11-02 2:56 ` [RFC PATCH v4 04/15] GDB: trad-frame: Store length of value_bytes in trad_frame_saved_reg Thiago Jung Bauermann
` (11 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Thiago Jung Bauermann @ 2024-11-02 2:56 UTC (permalink / raw)
To: gdb-patches
This is useful information I missed while debugging issues with
the g packet reply.
---
gdb/remote.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/gdb/remote.c b/gdb/remote.c
index b65a1c3094ae..6ffc51e4e2f5 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -10484,8 +10484,8 @@ remote_target::getpkt (gdb::char_vector *buf, bool forever, bool *is_notif)
if (val > max_chars)
remote_debug_printf_nofunc
- ("Packet received: %s [%d bytes omitted]", str.c_str (),
- val - max_chars);
+ ("Packet received: %s [%d bytes omitted, %d bytes total]", str.c_str (),
+ val - max_chars, val);
else
remote_debug_printf_nofunc ("Packet received: %s",
str.c_str ());
^ permalink raw reply [flat|nested] 16+ messages in thread
* [RFC PATCH v4 04/15] GDB: trad-frame: Store length of value_bytes in trad_frame_saved_reg
2024-11-02 2:56 [RFC PATCH v4 00/15] gdbserver improvements for AArch64 SVE support Thiago Jung Bauermann
` (2 preceding siblings ...)
2024-11-02 2:56 ` [RFC PATCH v4 03/15] GDB: remote: Print total bytes received in debug message Thiago Jung Bauermann
@ 2024-11-02 2:56 ` Thiago Jung Bauermann
2024-11-02 2:56 ` [RFC PATCH v4 05/15] gdbserver: nat/aarch64: Only check Z registers for extra contents if they can have it Thiago Jung Bauermann
` (10 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Thiago Jung Bauermann @ 2024-11-02 2:56 UTC (permalink / raw)
To: gdb-patches
The goal is to ensure that it is available in frame_unwind_got_bytes () to
make sure that the provided buf isn't larger than the size of the register
being provisioned.
In the process, regcache's cached_reg_t::data also needed to be
converted to a gdb::byte_vector, so that the register contents' size can
be tracked.
---
gdb/frame-unwind.c | 18 +++++++++++++++---
gdb/frame-unwind.h | 2 +-
gdb/jit.c | 2 +-
gdb/python/py-unwind.c | 7 ++++---
gdb/regcache.h | 2 +-
gdb/remote.c | 11 +++++------
gdb/trad-frame.h | 10 +++++++---
7 files changed, 34 insertions(+), 18 deletions(-)
diff --git a/gdb/frame-unwind.c b/gdb/frame-unwind.c
index fecd1070e912..ad8ebb29d736 100644
--- a/gdb/frame-unwind.c
+++ b/gdb/frame-unwind.c
@@ -313,14 +313,26 @@ frame_unwind_got_constant (const frame_info_ptr &frame, int regnum,
}
struct value *
-frame_unwind_got_bytes (const frame_info_ptr &frame, int regnum, const gdb_byte *buf)
+frame_unwind_got_bytes (const frame_info_ptr &frame, int regnum,
+ gdb::array_view<const gdb_byte> buf)
{
struct gdbarch *gdbarch = frame_unwind_arch (frame);
struct value *reg_val;
reg_val = value::zero (register_type (gdbarch, regnum), not_lval);
- memcpy (reg_val->contents_raw ().data (), buf,
- register_size (gdbarch, regnum));
+ gdb::array_view<gdb_byte> val_contents = reg_val->contents_raw ();
+
+ /* The value's contents buffer is zeroed on allocation so if buf is
+ smaller, the remaining space will be filled with zero.
+
+ This can happen when unwinding through signal frames. For example, if
+ an AArch64 program doesn't use SVE, then the Linux kernel will only
+ save in the signal frame the first 128 bits of the vector registers,
+ which is their minimum size, even if the vector length says they're
+ bigger. */
+ gdb_assert (buf.size () <= val_contents.size ());
+
+ memcpy (val_contents.data (), buf.data (), buf.size ());
return reg_val;
}
diff --git a/gdb/frame-unwind.h b/gdb/frame-unwind.h
index 53fcd6869e95..36e7fbdb4f9c 100644
--- a/gdb/frame-unwind.h
+++ b/gdb/frame-unwind.h
@@ -226,7 +226,7 @@ value *frame_unwind_got_constant (const frame_info_ptr &frame, int regnum,
inside BUF. */
value *frame_unwind_got_bytes (const frame_info_ptr &frame, int regnum,
- const gdb_byte *buf);
+ gdb::array_view<const gdb_byte> buf);
/* Return a value which indicates that FRAME's saved version of REGNUM
has a known constant (computed) value of ADDR. Convert the
diff --git a/gdb/jit.c b/gdb/jit.c
index ed3b26cd4bd1..e068b0a4b572 100644
--- a/gdb/jit.c
+++ b/gdb/jit.c
@@ -1094,7 +1094,7 @@ jit_frame_prev_register (const frame_info_ptr &this_frame, void **cache, int reg
return frame_unwind_got_optimized (this_frame, reg);
gdbarch = priv->regcache->arch ();
- gdb_byte *buf = (gdb_byte *) alloca (register_size (gdbarch, reg));
+ gdb::byte_vector buf (register_size (gdbarch, reg));
enum register_status status = priv->regcache->cooked_read (reg, buf);
if (status == REG_VALID)
diff --git a/gdb/python/py-unwind.c b/gdb/python/py-unwind.c
index 68deaf98d81f..c1a01bf7cfe4 100644
--- a/gdb/python/py-unwind.c
+++ b/gdb/python/py-unwind.c
@@ -812,7 +812,7 @@ pyuw_prev_register (const frame_info_ptr &this_frame, void **cache_ptr,
for (; reg_info < reg_info_end; ++reg_info)
{
if (regnum == reg_info->num)
- return frame_unwind_got_bytes (this_frame, regnum, reg_info->data.get ());
+ return frame_unwind_got_bytes (this_frame, regnum, reg_info->data);
}
return frame_unwind_got_optimized (this_frame, regnum);
@@ -936,8 +936,9 @@ pyuw_sniffer (const struct frame_unwind *self, const frame_info_ptr &this_frame,
cached_reg_t *cached = new (&cached_frame->reg[i]) cached_reg_t ();
cached->num = reg->number;
- cached->data.reset ((gdb_byte *) xmalloc (data_size));
- memcpy (cached->data.get (), value->contents ().data (), data_size);
+ cached->data.resize (data_size);
+ gdb::array_view<const gdb_byte> contents = value->contents ();
+ cached->data.assign (contents.begin (), contents.end ());
}
}
diff --git a/gdb/regcache.h b/gdb/regcache.h
index 65e9f7bb79da..739172a249b8 100644
--- a/gdb/regcache.h
+++ b/gdb/regcache.h
@@ -177,7 +177,7 @@ using register_read_ftype
struct cached_reg_t
{
int num;
- gdb::unique_xmalloc_ptr<gdb_byte> data;
+ gdb::byte_vector data;
cached_reg_t () = default;
cached_reg_t (cached_reg_t &&rhs) = default;
diff --git a/gdb/remote.c b/gdb/remote.c
index 6ffc51e4e2f5..2da2c5a4789a 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -8225,13 +8225,12 @@ Packet: '%s'\n"),
hex_string (pnum), p, buf);
cached_reg.num = reg->regnum;
- cached_reg.data.reset ((gdb_byte *)
- xmalloc (register_size (event->arch,
- reg->regnum)));
+ cached_reg.data.resize (register_size (event->arch,
+ reg->regnum));
p = p1 + 1;
- fieldsize = hex2bin (p, cached_reg.data.get (),
- register_size (event->arch, reg->regnum));
+ fieldsize = hex2bin (p, cached_reg.data.data (),
+ cached_reg.data.size ());
p += 2 * fieldsize;
if (fieldsize < register_size (event->arch, reg->regnum))
warning (_("Remote reply is too short: %s"), buf);
@@ -8572,7 +8571,7 @@ remote_target::process_stop_reply (stop_reply_up stop_reply,
for (cached_reg_t ® : stop_reply->regcache)
{
- regcache->raw_supply (reg.num, reg.data.get ());
+ regcache->raw_supply (reg.num, reg.data);
rs->last_seen_expedited_registers.insert (reg.num);
}
}
diff --git a/gdb/trad-frame.h b/gdb/trad-frame.h
index ca8792baa17d..ef84a24c0bc7 100644
--- a/gdb/trad-frame.h
+++ b/gdb/trad-frame.h
@@ -122,6 +122,7 @@ struct trad_frame_saved_reg
m_kind = trad_frame_saved_reg_kind::VALUE_BYTES;
m_reg.value_bytes = data;
+ m_reg.bytes_len = bytes.size ();
}
/* Getters */
@@ -144,10 +145,10 @@ struct trad_frame_saved_reg
return m_reg.addr;
}
- const gdb_byte *value_bytes () const
+ gdb::array_view<const gdb_byte> value_bytes () const
{
gdb_assert (m_kind == trad_frame_saved_reg_kind::VALUE_BYTES);
- return m_reg.value_bytes;
+ return { m_reg.value_bytes, m_reg.bytes_len };
}
/* Convenience functions, return true if the register has been
@@ -185,7 +186,10 @@ struct trad_frame_saved_reg
LONGEST value;
int realreg;
LONGEST addr;
- const gdb_byte *value_bytes;
+ struct {
+ const gdb_byte *value_bytes;
+ size_t bytes_len;
+ };
} m_reg;
};
^ permalink raw reply [flat|nested] 16+ messages in thread
* [RFC PATCH v4 05/15] gdbserver: nat/aarch64: Only check Z registers for extra contents if they can have it
2024-11-02 2:56 [RFC PATCH v4 00/15] gdbserver improvements for AArch64 SVE support Thiago Jung Bauermann
` (3 preceding siblings ...)
2024-11-02 2:56 ` [RFC PATCH v4 04/15] GDB: trad-frame: Store length of value_bytes in trad_frame_saved_reg Thiago Jung Bauermann
@ 2024-11-02 2:56 ` Thiago Jung Bauermann
2024-11-02 2:56 ` [RFC PATCH v4 06/15] gdbserver: Implement p and P packets Thiago Jung Bauermann
` (9 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Thiago Jung Bauermann @ 2024-11-02 2:56 UTC (permalink / raw)
To: gdb-patches
The bottom 128 bits of Z registers overlap with the V registers. If the
vector length is 128 bits, then they have no extra contents and in that
case this code will hit the gdb_assert (offset < regbuf.size ()); in
gdbserver's regcache::raw_compare ().
Therefore in that case we need to skip the meaningless comparison.
---
gdb/nat/aarch64-scalable-linux-ptrace.c | 19 ++++++++++---------
1 file changed, 10 insertions(+), 9 deletions(-)
diff --git a/gdb/nat/aarch64-scalable-linux-ptrace.c b/gdb/nat/aarch64-scalable-linux-ptrace.c
index 0f1bedf78002..ac375b78541a 100644
--- a/gdb/nat/aarch64-scalable-linux-ptrace.c
+++ b/gdb/nat/aarch64-scalable-linux-ptrace.c
@@ -745,15 +745,16 @@ aarch64_sve_regs_copy_from_reg_buf (int tid,
/* Check in the reg_buf if any of the Z registers are set after the
first 128 bits, or if any of the other SVE registers are set. */
bool has_sve_state = false;
- for (int i = 0; i < AARCH64_SVE_Z_REGS_NUM; i++)
- {
- if (!reg_buf->raw_compare (AARCH64_SVE_Z0_REGNUM + i, reg,
- V_REGISTER_SIZE))
- {
- has_sve_state = true;
- break;
- }
- }
+ if (reg_buf->register_size (AARCH64_SVE_Z0_REGNUM) > V_REGISTER_SIZE)
+ for (int i = 0; i < AARCH64_SVE_Z_REGS_NUM; i++)
+ {
+ if (!reg_buf->raw_compare (AARCH64_SVE_Z0_REGNUM + i, reg,
+ V_REGISTER_SIZE))
+ {
+ has_sve_state = true;
+ break;
+ }
+ }
if (!has_sve_state)
for (int i = 0; i < AARCH64_SVE_P_REGS_NUM; i++)
^ permalink raw reply [flat|nested] 16+ messages in thread
* [RFC PATCH v4 06/15] gdbserver: Implement p and P packets
2024-11-02 2:56 [RFC PATCH v4 00/15] gdbserver improvements for AArch64 SVE support Thiago Jung Bauermann
` (4 preceding siblings ...)
2024-11-02 2:56 ` [RFC PATCH v4 05/15] gdbserver: nat/aarch64: Only check Z registers for extra contents if they can have it Thiago Jung Bauermann
@ 2024-11-02 2:56 ` Thiago Jung Bauermann
2024-11-02 2:56 ` [RFC PATCH v4 07/15] GDB, gdbserver: Create concept of load-early registers Thiago Jung Bauermann
` (8 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Thiago Jung Bauermann @ 2024-11-02 2:56 UTC (permalink / raw)
To: gdb-patches
GDB will need the p packet to individually request "load early" registers
before using the g packet.
Not sure if P is necessary, but if p is supported, why not implement P?
Alternatively, to be more efficient there could be a packet where GDB can
specify a list of registers it wants do load or set. Or there could be a
register to request/set the expedited registers.
---
gdbserver/regcache.cc | 44 +++++++++++++++++++++++++
gdbserver/regcache.h | 9 ++++++
gdbserver/server.cc | 75 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 128 insertions(+)
diff --git a/gdbserver/regcache.cc b/gdbserver/regcache.cc
index 6a1526246867..ec19864bd690 100644
--- a/gdbserver/regcache.cc
+++ b/gdbserver/regcache.cc
@@ -208,6 +208,50 @@ find_register_by_number (const struct target_desc *tdesc, int n)
#ifndef IN_PROCESS_AGENT
+static gdb::array_view<gdb_byte> register_data (const struct regcache *regcache,
+ int n);
+
+/* See regcache.h. */
+
+void
+register_to_string (struct regcache *regcache, int regnum, char *buf)
+{
+ if (regcache->register_status[regnum] == REG_VALID)
+ {
+ gdb::array_view<gdb_byte> value = register_data (regcache, regnum);
+
+ bin2hex (value.data (), buf, value.size ());
+ buf[value.size () * 2] = '\0';
+ }
+ else
+ {
+ int len = regcache->register_size (regnum) * 2;
+
+ memset (buf, 'x', len);
+ buf[len] = '\0';
+ }
+}
+
+/* See regcache.h. */
+
+void
+register_from_string (struct regcache *regcache, int regnum, char *buf)
+{
+ int len = strlen (buf);
+ gdb::array_view<gdb_byte> value = register_data (regcache, regnum);
+ int expected_len = value.size () * 2;
+
+ if (len != expected_len)
+ {
+ warning ("Wrong sized packet for register %d (expected %d bytes, got %d)",
+ regnum, expected_len, len);
+ if (len > expected_len)
+ len = expected_len;
+ }
+
+ hex2bin (buf, value.data (), len / 2);
+}
+
void
registers_to_string (struct regcache *regcache, char *buf)
{
diff --git a/gdbserver/regcache.h b/gdbserver/regcache.h
index df0feca102e0..5de8fd3d127e 100644
--- a/gdbserver/regcache.h
+++ b/gdbserver/regcache.h
@@ -96,6 +96,15 @@ void regcache_invalidate (void);
void regcache_release (void);
+/* Save contents of register REGNUM to BUF as an hexadecimal string. */
+
+void register_to_string (struct regcache *regcache, int regnum, char *buf);
+
+/* Set contents of register REGNUM from BUF, interpreted as an hexadecimal
+ string. */
+
+void register_from_string (struct regcache *regcache, int regnum, char *buf);
+
/* Convert all registers to a string in the currently specified remote
format. */
diff --git a/gdbserver/server.cc b/gdbserver/server.cc
index 5190df4aed5f..4d075fce2359 100644
--- a/gdbserver/server.cc
+++ b/gdbserver/server.cc
@@ -4712,6 +4712,81 @@ process_serial_event (void)
}
}
break;
+ case 'p':
+ {
+ require_running_or_break (cs.own_buf);
+ if (cs.current_traceframe >= 0)
+ {
+ write_enn (cs.own_buf);
+ break;
+ }
+ if (!set_desired_thread ())
+ {
+ write_enn (cs.own_buf);
+ break;
+ }
+
+ int i = 1, regnum = 0;
+ char c;
+ while ((c = cs.own_buf[i++]) != '\0')
+ {
+ regnum = regnum << 4;
+ regnum |= fromhex (c) & 0x0f;
+ }
+
+ struct regcache *regcache = get_thread_regcache (current_thread, true);
+
+ if (regnum < 0 || regnum >= regcache->tdesc->reg_defs.size ())
+ {
+ write_enn (cs.own_buf);
+ break;
+ }
+
+ fetch_inferior_registers (regcache, regnum);
+ register_to_string (regcache, regnum, cs.own_buf);
+ }
+ break;
+ case 'P':
+ {
+ require_running_or_break (cs.own_buf);
+ if (cs.current_traceframe >= 0)
+ {
+ write_enn (cs.own_buf);
+ break;
+ }
+ if (!set_desired_thread ())
+ {
+ write_enn (cs.own_buf);
+ break;
+ }
+ if (strchr (cs.own_buf, '=') == nullptr)
+ {
+ write_enn (cs.own_buf);
+ break;
+ }
+
+ int i = 1, regnum = 0;
+ char c;
+ while ((c = cs.own_buf[i++]) != '=')
+ {
+ regnum = regnum << 4;
+ regnum |= fromhex (c) & 0x0f;
+ }
+
+ struct regcache *regcache = get_thread_regcache (current_thread, true);
+
+ if (regnum < 0 || regnum >= regcache->tdesc->reg_defs.size ())
+ {
+ write_enn (cs.own_buf);
+ break;
+ }
+
+ register_from_string (regcache, regnum, &cs.own_buf[i]);
+ /* FIXME: Why doesn't the G packet need this as well? */
+ store_inferior_registers (regcache, regnum);
+ write_ok (cs.own_buf);
+ }
+ break;
case 'm':
{
require_running_or_break (cs.own_buf);
^ permalink raw reply [flat|nested] 16+ messages in thread
* [RFC PATCH v4 07/15] GDB, gdbserver: Create concept of load-early registers
2024-11-02 2:56 [RFC PATCH v4 00/15] gdbserver improvements for AArch64 SVE support Thiago Jung Bauermann
` (5 preceding siblings ...)
2024-11-02 2:56 ` [RFC PATCH v4 06/15] gdbserver: Implement p and P packets Thiago Jung Bauermann
@ 2024-11-02 2:56 ` Thiago Jung Bauermann
2024-11-02 2:56 ` [RFC PATCH v4 08/15] GDB: Allow DWARF expression evaluation to use regcache Thiago Jung Bauermann
` (7 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Thiago Jung Bauermann @ 2024-11-02 2:56 UTC (permalink / raw)
To: gdb-patches
These are registers that need to be saved first into the regcache.
Consequently, also gdbserver needs to send these registers as expedited
registers so that GDB can use them as early as possible.
When debugging a remote target, load early registers using expedited
registers and the p packet.
For now the load-early registers concept is unused, as nothing in this
patch adds registers to the load-early set. It is being sent separately to
facilitate review.
In a subsequent patch, registers which are used in location expressions
that determine the size of other registers will need to be loaded early
into the regcache so that they can be used to resolve the dynamic types of
the variable-sized registers.
This will be done by calling tdesc_create_reg () with the new load_early
argument set to true.
If I can move the VG register before the Z registers as Pedro suggested,
this patch may be unnecessary.
---
gdb/process-stratum-target.h | 3 ++
gdb/record-full.c | 18 +++++--
gdb/regcache.c | 99 +++++++++++++++++++++++++++++++++++-
gdb/regcache.h | 17 +++++++
gdb/regformats/regdef.h | 16 ++++--
gdb/remote.c | 88 ++++++++++++++++++++++++++++----
gdb/target-descriptions.c | 24 ++++++++-
gdb/target-descriptions.h | 5 ++
gdbserver/regcache.cc | 8 +++
gdbserver/regcache.h | 2 +
gdbserver/tdesc.cc | 12 ++++-
gdbsupport/common-regcache.h | 3 ++
gdbsupport/tdesc.cc | 9 ++--
gdbsupport/tdesc.h | 11 +++-
14 files changed, 285 insertions(+), 30 deletions(-)
diff --git a/gdb/process-stratum-target.h b/gdb/process-stratum-target.h
index 9aa9d874ecbb..4d4ad3431d16 100644
--- a/gdb/process-stratum-target.h
+++ b/gdb/process-stratum-target.h
@@ -55,6 +55,9 @@ class process_stratum_target : public target_ops
gdbarch. */
struct gdbarch *thread_architecture (ptid_t ptid) override;
+ /* Supply the load-early registers to REGCACHE when it's first created. */
+ virtual void supply_initial_registers (regcache *regcache) {};
+
/* Default implementations for process_stratum targets. Return true
if there's a selected inferior, false otherwise. */
bool has_all_memory () override;
diff --git a/gdb/record-full.c b/gdb/record-full.c
index 22a513ac79ab..8e4915abd083 100644
--- a/gdb/record-full.c
+++ b/gdb/record-full.c
@@ -926,15 +926,20 @@ record_full_core_open_1 ()
{
regcache *regcache = get_thread_regcache (inferior_thread ());
int regnum = gdbarch_num_regs (regcache->arch ());
- int i;
/* Get record_full_core_regbuf. */
target_fetch_registers (regcache, -1);
record_full_core_regbuf = new detached_regcache (regcache->arch (), false);
- for (i = 0; i < regnum; i ++)
+ /* Supply load-early registers first. */
+ for (int i : regcache->load_early_registers ())
record_full_core_regbuf->raw_supply (i, *regcache);
+ for (int i = 0; i < regnum; i ++)
+ /* Load-early registers were already supplied. */
+ if (!regcache->is_load_early_register (i))
+ record_full_core_regbuf->raw_supply (i, *regcache);
+
record_full_core_sections
= build_section_table (current_program_space->core_bfd ());
@@ -2107,10 +2112,15 @@ record_full_core_target::fetch_registers (struct regcache *regcache,
if (regno < 0)
{
int num = gdbarch_num_regs (regcache->arch ());
- int i;
- for (i = 0; i < num; i ++)
+ /* Supply load-early registers first. */
+ for (int i : regcache->load_early_registers ())
regcache->raw_supply (i, *record_full_core_regbuf);
+
+ for (int i = 0; i < num; i ++)
+ /* Load-early registers were already supplied. */
+ if (!regcache->is_load_early_register (i))
+ regcache->raw_supply (i, *record_full_core_regbuf);
}
else
regcache->raw_supply (regno, *record_full_core_regbuf);
diff --git a/gdb/regcache.c b/gdb/regcache.c
index 6e0c730d0d59..61289d46f4b1 100644
--- a/gdb/regcache.c
+++ b/gdb/regcache.c
@@ -30,6 +30,7 @@
#include "regset.h"
#include <unordered_map>
#include "cli/cli-cmds.h"
+#include "target-descriptions.h"
/*
* DATA STRUCTURE
@@ -68,6 +69,9 @@ struct regcache_descr
long *register_offset = nullptr;
long *sizeof_register = nullptr;
+ /* Registers that need to be loaded early in the register cache. */
+ std::set<int> load_early_regs;
+
/* Cached table containing the type of each register. */
struct type **register_type = nullptr;
};
@@ -120,6 +124,9 @@ init_regcache_descr (struct gdbarch *gdbarch)
descr->sizeof_register[i] = descr->register_type[i]->length ();
descr->register_offset[i] = offset;
offset += descr->sizeof_register[i];
+
+ if (tdesc_register_is_early_load (gdbarch, i))
+ descr->load_early_regs.insert (i);
}
/* Set the real size of the raw register cache buffer. */
descr->sizeof_raw_registers = offset;
@@ -230,6 +237,50 @@ reg_buffer::arch () const
return m_descr->gdbarch;
}
+/* Utility functions returning useful register attributes stored in
+ the regcache descr. */
+
+/* See regcache.h. */
+
+bool
+reg_buffer::fetch_load_early_registers ()
+{
+ for (int regnum : this->load_early_registers ())
+ if (this->get_register_status (regnum) != REG_VALID)
+ /* A reg_buffer can't fetch registers, so we can only report failure. */
+ return false;
+
+ return true;
+}
+
+/* See regcache.h. */
+
+bool
+regcache::fetch_load_early_registers ()
+{
+ for (int regnum : this->load_early_registers ())
+ if (this->get_register_status (regnum) != REG_VALID)
+ target_fetch_registers (this, regnum);
+
+ return true;
+}
+
+/* See regcache.h. */
+
+bool
+reg_buffer::has_load_early_registers ()
+{
+ return !m_descr->load_early_regs.empty ();
+}
+
+/* See regcache.h. */
+
+const std::set<int> &
+reg_buffer::load_early_registers ()
+{
+ return m_descr->load_early_regs;
+}
+
/* Helper for reg_buffer::register_buffer. */
template<typename ElemType>
@@ -268,12 +319,31 @@ reg_buffer::save (register_read_ftype cooked_read)
/* Clear the dest. */
memset (m_registers.get (), 0, m_descr->sizeof_cooked_registers);
memset (m_register_status.get (), REG_UNKNOWN, m_descr->nr_cooked_registers);
+
+ /* Save load early registers first. */
+ for (int regnum : m_descr->load_early_regs)
+ {
+ gdb::array_view<gdb_byte> dst_buf = register_buffer (regnum);
+ register_status status = cooked_read (regnum, dst_buf);
+
+ gdb_assert (status != REG_UNKNOWN);
+
+ if (status != REG_VALID)
+ memset (dst_buf.data (), 0, dst_buf.size ());
+
+ m_register_status[regnum] = status;
+ }
+
/* Copy over any registers (identified by their membership in the
save_reggroup) and mark them as valid. The full [0 .. gdbarch_num_regs +
gdbarch_num_pseudo_regs) range is checked since some architectures need
to save/restore `cooked' registers that live in memory. */
for (int regnum = 0; regnum < m_descr->nr_cooked_registers; regnum++)
{
+ /* Load-early registers were already saved. */
+ if (this->is_load_early_register (regnum))
+ continue;
+
if (gdbarch_register_reggroup_p (gdbarch, regnum, save_reggroup))
{
gdb::array_view<gdb_byte> dst_buf = register_buffer (regnum);
@@ -293,19 +363,34 @@ void
regcache::restore (readonly_detached_regcache *src)
{
struct gdbarch *gdbarch = m_descr->gdbarch;
- int regnum;
gdb_assert (src != NULL);
gdb_assert (src->m_has_pseudo);
gdb_assert (gdbarch == src->arch ());
+ /* Restore load early registers first. */
+ for (int regnum : m_descr->load_early_regs)
+ {
+ if (!gdbarch_register_reggroup_p (gdbarch, regnum, restore_reggroup))
+ continue;
+
+ if (src->m_register_status[regnum] != REG_VALID)
+ continue;
+
+ cooked_write (regnum, src->register_buffer (regnum));
+ }
+
/* Copy over any registers, being careful to only restore those that
were both saved and need to be restored. The full [0 .. gdbarch_num_regs
+ gdbarch_num_pseudo_regs) range is checked since some architectures need
to save/restore `cooked' registers that live in memory. */
- for (regnum = 0; regnum < m_descr->nr_cooked_registers; regnum++)
+ for (int regnum = 0; regnum < m_descr->nr_cooked_registers; regnum++)
{
+ /* Load-early registers were already restored. */
+ if (this->is_load_early_register (regnum))
+ continue;
+
if (gdbarch_register_reggroup_p (gdbarch, regnum, restore_reggroup))
{
if (src->m_register_status[regnum] == REG_VALID)
@@ -324,6 +409,14 @@ reg_buffer::get_register_status (int regnum) const
return m_register_status[regnum];
}
+/* See gdbsupport/common-regcache.h. */
+
+bool
+reg_buffer::is_load_early_register (int regnum) const
+{
+ return m_descr->load_early_regs.count (regnum) > 0;
+}
+
void
reg_buffer::invalidate (int regnum)
{
@@ -394,6 +487,8 @@ get_thread_arch_regcache (inferior *inf_for_target_calls, ptid_t ptid,
constructor explicitly instead of implicitly. */
ptid_regc_map.insert (std::make_pair (ptid, regcache_up (new_regcache)));
+ proc_target->supply_initial_registers (new_regcache);
+
return new_regcache;
}
diff --git a/gdb/regcache.h b/gdb/regcache.h
index 739172a249b8..5c28fec4af11 100644
--- a/gdb/regcache.h
+++ b/gdb/regcache.h
@@ -20,6 +20,7 @@
#ifndef REGCACHE_H
#define REGCACHE_H
+#include <set>
#include "gdbsupport/array-view.h"
#include "gdbsupport/common-regcache.h"
#include "gdbsupport/function-view.h"
@@ -198,6 +199,9 @@ class reg_buffer : public reg_buffer_common
/* See gdbsupport/common-regcache.h. */
enum register_status get_register_status (int regnum) const override;
+/* See gdbsupport/common-regcache.h. */
+ bool is_load_early_register (int regnum) const override;
+
/* See gdbsupport/common-regcache.h. */
void raw_collect (int regnum, gdb::array_view<gdb_byte> dst) const override;
@@ -256,6 +260,12 @@ class reg_buffer : public reg_buffer_common
/* See gdbsupport/common-regcache.h. */
bool raw_compare (int regnum, const void *buf, int offset) const override;
+ /* Whether any register needs to be loaded before other registers. */
+ bool has_load_early_registers ();
+
+ /* Return set of regnums which need to be loaded before other registers. */
+ const std::set<int> &load_early_registers ();
+
/* See gdbsupport/common-regcache.h. */
int register_size (int regnum) const override;
@@ -265,6 +275,10 @@ class reg_buffer : public reg_buffer_common
int num_raw_registers () const;
+ /* Ensure all load early registers are fetched in this cache.
+ Return false if they aren't. */
+ virtual bool fetch_load_early_registers ();
+
/* Return a view on register REGNUM's buffer cache. */
template <typename ElemType>
gdb::array_view<ElemType> register_buffer (int regnum) const;
@@ -485,6 +499,9 @@ class regcache : public detached_regcache
gdb::array_view<const gdb_byte> src,
bool is_raw);
+ /* See class reg_buffer. */
+ bool fetch_load_early_registers () override;
+
/* The inferior to switch to, to make target calls.
This may not be the inferior of thread M_PTID. For instance, this
diff --git a/gdb/regformats/regdef.h b/gdb/regformats/regdef.h
index 0ba7a08bb0c5..09339d495209 100644
--- a/gdb/regformats/regdef.h
+++ b/gdb/regformats/regdef.h
@@ -26,13 +26,15 @@ struct reg
reg (int _offset)
: name (""),
offset (_offset),
- size (0)
+ size (0),
+ load_early (false)
{}
- reg (const char *_name, int _offset, int _size)
+ reg (const char *_name, int _offset, int _size, bool _load_early)
: name (_name),
offset (_offset),
- size (_size)
+ size (_size),
+ load_early (_load_early)
{}
/* The name of this register - NULL for pad entries. */
@@ -49,11 +51,17 @@ struct reg
/* The size (in bits) of the value of this register, as transmitted. */
int size;
+ /* Whether this register needs to be loaded early in the register cache,
+ because variable-size registers depend on it to calculate their
+ size. */
+ bool load_early;
+
bool operator== (const reg &other) const
{
return (strcmp (name, other.name) == 0
&& offset == other.offset
- && size == other.size);
+ && size == other.size
+ && load_early == other.load_early);
}
bool operator!= (const reg &other) const
diff --git a/gdb/remote.c b/gdb/remote.c
index 2da2c5a4789a..0217c05bce52 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -683,6 +683,9 @@ class remote_state
immediately, so queue is not needed for them. */
std::vector<stop_reply_up> stop_reply_queue;
+ /* Contains the stop reply packet when first starting the inferior. */
+ gdb::char_vector first_stop_reply;
+
/* FIXME: cagney/1999-09-23: Even though getpkt was called with
``forever'' still use the normal timeout mechanism. This is
currently used by the ASYNC code to guarantee that target reads
@@ -1218,6 +1221,8 @@ class remote_target : public process_stratum_target
ptid_t select_thread_for_ambiguous_stop_reply
(const struct target_waitstatus &status);
+ void supply_initial_registers (regcache *regcache) override;
+
void remote_notice_new_inferior (ptid_t currthread, bool executing);
void print_one_stopped_thread (thread_info *thread);
@@ -1317,6 +1322,8 @@ class remote_target : public process_stratum_target
int fetch_register_using_p (struct regcache *regcache,
packet_reg *reg);
+
+ void fetch_early_registers (struct regcache *regcache);
int send_g_packet ();
void process_g_packet (struct regcache *regcache);
void fetch_registers_using_g (struct regcache *regcache);
@@ -1417,6 +1424,9 @@ class remote_target : public process_stratum_target
bool start_remote_1 (int from_tty, int extended_p);
+ void supply_expedited_regs (struct regcache *regcache,
+ std::vector<cached_reg_t> &expedited_regs);
+
/* The remote state. Don't reference this directly. Use the
get_remote_state method instead. */
remote_state m_remote_state;
@@ -8530,6 +8540,21 @@ remote_target::select_thread_for_ambiguous_stop_reply
return first_resumed_thread->ptid;
}
+/* Supply the contents of EXPEDITED_REGS to REGCACHE. */
+
+void
+remote_target::supply_expedited_regs (struct regcache *regcache,
+ std::vector<cached_reg_t> &expedited_regs)
+{
+ remote_state *rs = get_remote_state ();
+
+ for (cached_reg_t ® : expedited_regs)
+ {
+ regcache->raw_supply (reg.num, reg.data);
+ rs->last_seen_expedited_registers.insert (reg.num);
+ }
+}
+
/* Called when it is decided that STOP_REPLY holds the info of the
event that is to be returned to the core. This function always
destroys STOP_REPLY. */
@@ -8568,12 +8593,7 @@ remote_target::process_stop_reply (stop_reply_up stop_reply,
regcache *regcache
= get_thread_arch_regcache (find_inferior_ptid (this, ptid), ptid,
stop_reply->arch);
-
- for (cached_reg_t ® : stop_reply->regcache)
- {
- regcache->raw_supply (reg.num, reg.data);
- rs->last_seen_expedited_registers.insert (reg.num);
- }
+ supply_expedited_regs (regcache, stop_reply->regcache);
}
remote_thread_info *remote_thr = get_remote_thread_info (this, ptid);
@@ -8599,6 +8619,28 @@ remote_target::process_stop_reply (stop_reply_up stop_reply,
return ptid;
}
+/* See gdb/process-stratum-target.h. */
+
+void
+remote_target::supply_initial_registers (regcache *regcache)
+{
+ remote_state *rs = get_remote_state ();
+
+ if (rs->first_stop_reply.empty ())
+ return;
+
+ notif_event_up reply
+ = remote_notif_parse (this, ¬if_client_stop,
+ rs->first_stop_reply.data ());
+ std::vector<cached_reg_t> &expedited_regs
+ = ((struct stop_reply *) reply.get ())->regcache;
+
+ if (!expedited_regs.empty ())
+ supply_expedited_regs (regcache, expedited_regs);
+
+ rs->first_stop_reply.clear ();
+}
+
/* The non-stop mode version of target_wait. */
ptid_t
@@ -8918,6 +8960,27 @@ remote_target::fetch_register_using_p (struct regcache *regcache,
return 1;
}
+/* Fetch load-early registers individually with the 'p' packet. */
+
+void
+remote_target::fetch_early_registers (struct regcache *regcache)
+{
+ struct remote_state *rs = get_remote_state ();
+ remote_arch_state *rsa = rs->get_remote_arch_state (regcache->arch ());
+
+ for (int regnum : regcache->load_early_registers ())
+ {
+ /* We may already have it from a stop reply packet. */
+ if (regcache->get_register_status (regnum) == REG_VALID)
+ continue;
+
+ packet_reg *reg = packet_reg_from_regnum (regcache->arch (), rsa, regnum);
+ int res = fetch_register_using_p (regcache, reg);
+ if (res == 0)
+ error (_("Could not load early register %d using p packet."), regnum);
+ }
+}
+
/* Fetch the registers included in the target's 'g' packet. */
int
@@ -9062,6 +9125,9 @@ remote_target::process_g_packet (struct regcache *regcache)
void
remote_target::fetch_registers_using_g (struct regcache *regcache)
{
+ if (regcache->has_load_early_registers ())
+ fetch_early_registers (regcache);
+
send_g_packet ();
process_g_packet (regcache);
}
@@ -10920,7 +10986,6 @@ extended_remote_target::create_inferior (const char *exec_file,
char **env, int from_tty)
{
int run_worked;
- char *stop_reply;
struct remote_state *rs = get_remote_state ();
const char *remote_exec_file = get_remote_exec_file ();
@@ -10953,7 +11018,10 @@ Remote replied unexpectedly while setting startup-with-shell: %s"),
/* Now restart the remote server. */
run_worked = extended_remote_run (args) != -1;
- if (!run_worked)
+ if (run_worked)
+ /* vRun's success return is a stop reply. */
+ rs->first_stop_reply = rs->buf;
+ else
{
/* vRun was not supported. Fail if we need it to do what the
user requested. */
@@ -10966,9 +11034,7 @@ Remote replied unexpectedly while setting startup-with-shell: %s"),
extended_remote_restart ();
}
- /* vRun's success return is a stop reply. */
- stop_reply = run_worked ? rs->buf.data () : NULL;
- add_current_inferior_and_thread (stop_reply);
+ add_current_inferior_and_thread (run_worked ? rs->buf.data () : nullptr);
/* Get updated offsets, if the stub uses qOffsets. */
get_offsets ();
diff --git a/gdb/target-descriptions.c b/gdb/target-descriptions.c
index 1bd22c273a29..82e4d96276e3 100644
--- a/gdb/target-descriptions.c
+++ b/gdb/target-descriptions.c
@@ -850,6 +850,16 @@ tdesc_register_name (struct gdbarch *gdbarch, int regno)
return "";
}
+/* See target-descriptions.h. */
+
+bool
+tdesc_register_is_early_load (struct gdbarch *gdbarch, int regno)
+{
+ struct tdesc_reg *reg = tdesc_find_register (gdbarch, regno);
+
+ return reg == nullptr ? false : reg->load_early;
+}
+
struct type *
tdesc_register_type (struct gdbarch *gdbarch, int regno)
{
@@ -1484,7 +1494,12 @@ class print_c_tdesc : public tdesc_element_visitor
gdb_printf ("\"%s\", ", reg->group.c_str ());
else
gdb_printf ("NULL, ");
- gdb_printf ("%d, \"%s\");\n", reg->bitsize, reg->type.c_str ());
+ gdb_printf ("%d, \"%s\"", reg->bitsize, reg->type.c_str ());
+
+ if (reg->load_early)
+ gdb_printf (", true");
+
+ gdb_printf (");\n");
}
protected:
@@ -1627,7 +1642,12 @@ class print_c_feature : public print_c_tdesc
gdb_printf ("\"%s\", ", reg->group.c_str ());
else
gdb_printf ("NULL, ");
- gdb_printf ("%d, \"%s\");\n", reg->bitsize, reg->type.c_str ());
+ gdb_printf ("%d, \"%s\"", reg->bitsize, reg->type.c_str ());
+
+ if (reg->load_early)
+ gdb_printf (", true");
+
+ gdb_printf (");\n");
m_next_regnum++;
}
diff --git a/gdb/target-descriptions.h b/gdb/target-descriptions.h
index dc83db0acf28..e40c7db5f79f 100644
--- a/gdb/target-descriptions.h
+++ b/gdb/target-descriptions.h
@@ -199,6 +199,11 @@ const char *tdesc_feature_name (const struct tdesc_feature *feature);
const char *tdesc_register_name (struct gdbarch *gdbarch, int regno);
+/* Return whether register REGNO needs to be loaded early in the
+ register cache. */
+
+bool tdesc_register_is_early_load (struct gdbarch *gdbarch, int regno);
+
/* Return the type of register REGNO, from the target description or
from an architecture-provided pseudo_register_type method. */
diff --git a/gdbserver/regcache.cc b/gdbserver/regcache.cc
index ec19864bd690..cd1ee2e5f145 100644
--- a/gdbserver/regcache.cc
+++ b/gdbserver/regcache.cc
@@ -568,6 +568,14 @@ regcache::get_register_status (int regnum) const
/* See gdbsupport/common-regcache.h. */
+bool
+regcache::is_load_early_register (int regnum) const
+{
+ return find_register_by_number (this->tdesc, regnum).load_early;
+}
+
+/* See gdbsupport/common-regcache.h. */
+
bool
regcache::raw_compare (int regnum, const void *buf, int offset) const
{
diff --git a/gdbserver/regcache.h b/gdbserver/regcache.h
index 5de8fd3d127e..07e48a7432b7 100644
--- a/gdbserver/regcache.h
+++ b/gdbserver/regcache.h
@@ -49,6 +49,8 @@ struct regcache : public reg_buffer_common
/* See gdbsupport/common-regcache.h. */
enum register_status get_register_status (int regnum) const override;
+ bool is_load_early_register (int regnum) const override;
+
/* See gdbsupport/common-regcache.h. */
int register_size (int regnum) const override;
diff --git a/gdbserver/tdesc.cc b/gdbserver/tdesc.cc
index d052f43c76e6..6f7ebb7c5c76 100644
--- a/gdbserver/tdesc.cc
+++ b/gdbserver/tdesc.cc
@@ -56,6 +56,8 @@ init_target_desc (struct target_desc *tdesc,
const char **expedite_regs)
{
int offset = 0;
+ /* Additional registers to expedite from the features. */
+ std::vector<const char *> expedite_from_features;
/* Go through all the features and populate reg_defs. */
for (const tdesc_feature_up &feature : tdesc->features)
@@ -70,8 +72,11 @@ init_target_desc (struct target_desc *tdesc,
tdesc->reg_defs.resize (regnum, gdb::reg (offset));
tdesc->reg_defs.emplace_back (treg->name.c_str (), offset,
- treg->bitsize);
+ treg->bitsize, treg->load_early);
offset += treg->bitsize;
+
+ if (treg->load_early)
+ expedite_from_features.push_back (treg->name.c_str ());
}
tdesc->registers_size = offset / 8;
@@ -88,6 +93,11 @@ init_target_desc (struct target_desc *tdesc,
int expedite_count = 0;
while (expedite_regs[expedite_count] != nullptr)
tdesc->expedite_regs.push_back (expedite_regs[expedite_count++]);
+
+ if (!expedite_from_features.empty ())
+ tdesc->expedite_regs.insert (tdesc->expedite_regs.end (),
+ expedite_from_features.cbegin (),
+ expedite_from_features.cend ());
#endif
}
diff --git a/gdbsupport/common-regcache.h b/gdbsupport/common-regcache.h
index 4594999346fd..93b1b5d522f2 100644
--- a/gdbsupport/common-regcache.h
+++ b/gdbsupport/common-regcache.h
@@ -73,6 +73,9 @@ struct reg_buffer_common
buffer. */
virtual register_status get_register_status (int regnum) const = 0;
+ /* Does this register need to be loaded before others? */
+ virtual bool is_load_early_register (int regnum) const = 0;
+
/* Return the size of register numbered REGNUM in this buffer. */
virtual int register_size (int regnum) const = 0;
diff --git a/gdbsupport/tdesc.cc b/gdbsupport/tdesc.cc
index 080d39c485dc..a99119274f44 100644
--- a/gdbsupport/tdesc.cc
+++ b/gdbsupport/tdesc.cc
@@ -21,12 +21,13 @@
tdesc_reg::tdesc_reg (struct tdesc_feature *feature, const std::string &name_,
int regnum, int save_restore_, const char *group_,
- int bitsize_, const char *type_)
+ int bitsize_, const char *type_, bool load_early_)
: name (name_), target_regnum (regnum),
save_restore (save_restore_),
group (group_ != NULL ? group_ : ""),
bitsize (bitsize_),
- type (type_ != NULL ? type_ : "<unknown>")
+ type (type_ != NULL ? type_ : "<unknown>"),
+ load_early (load_early_)
{
/* If the register's type is target-defined, look it up now. We may not
have easy access to the containing feature when we want it later. */
@@ -137,10 +138,10 @@ tdesc_named_type (const struct tdesc_feature *feature, const char *id)
void
tdesc_create_reg (struct tdesc_feature *feature, const char *name,
int regnum, int save_restore, const char *group,
- int bitsize, const char *type)
+ int bitsize, const char *type, bool load_early)
{
tdesc_reg *reg = new tdesc_reg (feature, name, regnum, save_restore,
- group, bitsize, type);
+ group, bitsize, type, load_early);
feature->registers.emplace_back (reg);
}
diff --git a/gdbsupport/tdesc.h b/gdbsupport/tdesc.h
index c9e7603369cb..7e483486139b 100644
--- a/gdbsupport/tdesc.h
+++ b/gdbsupport/tdesc.h
@@ -70,7 +70,7 @@ struct tdesc_reg : tdesc_element
{
tdesc_reg (struct tdesc_feature *feature, const std::string &name_,
int regnum, int save_restore_, const char *group_,
- int bitsize_, const char *type_);
+ int bitsize_, const char *type_, bool load_early_ = false);
virtual ~tdesc_reg () = default;
@@ -110,6 +110,12 @@ struct tdesc_reg : tdesc_element
/* The target-described type corresponding to TYPE, if found. */
struct tdesc_type *tdesc_type;
+ /* Whether this register needs to be loaded early in GDB's regcache.
+
+ In addition, if true gdbserver will send it as an expedited register
+ in stop replies. */
+ bool load_early;
+
void accept (tdesc_element_visitor &v) const override
{
v.visit (this);
@@ -415,7 +421,8 @@ void tdesc_add_enum_value (tdesc_type_with_fields *type, int value,
/* Create a register in feature FEATURE. */
void tdesc_create_reg (struct tdesc_feature *feature, const char *name,
int regnum, int save_restore, const char *group,
- int bitsize, const char *type);
+ int bitsize, const char *type,
+ bool load_early = false);
/* Return the tdesc in string XML format. */
^ permalink raw reply [flat|nested] 16+ messages in thread
* [RFC PATCH v4 08/15] GDB: Allow DWARF expression evaluation to use regcache
2024-11-02 2:56 [RFC PATCH v4 00/15] gdbserver improvements for AArch64 SVE support Thiago Jung Bauermann
` (6 preceding siblings ...)
2024-11-02 2:56 ` [RFC PATCH v4 07/15] GDB, gdbserver: Create concept of load-early registers Thiago Jung Bauermann
@ 2024-11-02 2:56 ` Thiago Jung Bauermann
2024-11-02 2:56 ` [RFC PATCH v4 09/15] GDB tdesc: Add vector type with number of elements given by math expression Thiago Jung Bauermann
` (6 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Thiago Jung Bauermann @ 2024-11-02 2:56 UTC (permalink / raw)
To: gdb-patches
Currently, expression evaluation is done in the context of a frame.
However, for variable-size registers we will need to evaluate
expressions when no frame is available, but we do have a regcache.
Therefore, allow passing a regcache to dwarf2_evaluate_property.
This requires adding a regcache parameter to resolve_dynamic_type and
its internal functions.
Also, this means that a DWARF expression may not be associated with an
objfile, so when it needs a gdbarch it may not be possible to get the
one from the objfile. The solution is to add an m_gdbarch member to
dwarf_expr_context, and make it point to the gdbarch of either the
objfile, frame or regcache associated with the expression.
Simon suggested using GDB Agent Expressions rather than DWARF
expressions to encode the register length. I will try it out, and if it
works then this patch can be dropped.
---
gdb/dwarf2/expr.c | 63 +++++++++++++++++++++++++++++++------
gdb/dwarf2/expr.h | 17 +++++++++-
gdb/dwarf2/frame.c | 2 +-
gdb/dwarf2/loc.c | 41 +++++++++++++++++-------
gdb/dwarf2/loc.h | 2 ++
gdb/findvar.c | 17 ++++++++++
gdb/frame.c | 3 +-
gdb/gdbtypes.c | 77 ++++++++++++++++++++++++++--------------------
gdb/gdbtypes.h | 5 ++-
gdb/gnu-v3-abi.c | 2 +-
gdb/value.h | 3 ++
11 files changed, 173 insertions(+), 59 deletions(-)
diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index 5ad17eaaaff8..4a667dbd8107 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -56,6 +56,20 @@ ensure_have_frame (const frame_info_ptr &frame, const char *op_name)
_("%s evaluation requires a frame."), op_name);
}
+/* Ensure that either a FRAME or a REGCACHE is defined, throw an exception
+ otherwise. */
+
+static void
+ensure_have_frame_or_regcache (const frame_info_ptr &frame,
+ const reg_buffer *regcache,
+ const char *op_name)
+{
+ if (frame == nullptr && regcache == nullptr)
+ throw_error (GENERIC_ERROR,
+ _("%s evaluation requires a frame or register cache."),
+ op_name);
+}
+
/* Ensure that a PER_CU is defined and throw an exception otherwise. */
static void
@@ -86,6 +100,16 @@ read_addr_from_reg (const frame_info_ptr &frame, int reg)
return address_from_register (regnum, frame);
}
+/* See expr.h. */
+
+CORE_ADDR
+read_addr_from_reg (reg_buffer *regcache, int reg)
+{
+ int regnum = dwarf_reg_to_regnum_or_error (regcache->arch (), reg);
+
+ return address_from_register (regnum, regcache);
+}
+
struct piece_closure
{
/* Reference count. */
@@ -690,7 +714,8 @@ sect_variable_value (sect_offset sect_off,
struct type *
dwarf_expr_context::address_type () const
{
- gdbarch *arch = this->m_per_objfile->objfile->arch ();
+ gdbarch *arch = (m_gdbarch == nullptr ? this->m_per_objfile->objfile->arch ()
+ : m_gdbarch);
dwarf_gdbarch_types *types = dwarf_arch_cookie.get (arch);
if (types == nullptr)
types = dwarf_arch_cookie.emplace (arch);
@@ -720,9 +745,11 @@ dwarf_expr_context::address_type () const
/* Create a new context for the expression evaluator. */
dwarf_expr_context::dwarf_expr_context (dwarf2_per_objfile *per_objfile,
- int addr_size)
+ int addr_size,
+ gdbarch *gdbarch)
: m_addr_size (addr_size),
- m_per_objfile (per_objfile)
+ m_per_objfile (per_objfile),
+ m_gdbarch (gdbarch)
{
}
@@ -915,7 +942,6 @@ dwarf_expr_context::fetch_result (struct type *type, struct type *subobj_type,
LONGEST subobj_offset, bool as_lval)
{
value *retval = nullptr;
- gdbarch *arch = this->m_per_objfile->objfile->arch ();
if (type == nullptr)
type = address_type ();
@@ -948,6 +974,9 @@ dwarf_expr_context::fetch_result (struct type *type, struct type *subobj_type,
}
else
{
+ gdbarch *arch = (m_gdbarch == nullptr ? this->m_per_objfile->objfile->arch ()
+ : m_gdbarch);
+
/* If AS_LVAL is false, means that the implicit conversion
from a location description to value is expected. */
if (!as_lval)
@@ -1077,12 +1106,14 @@ dwarf_expr_context::fetch_result (struct type *type, struct type *subobj_type,
value *
dwarf_expr_context::evaluate (const gdb_byte *addr, size_t len, bool as_lval,
dwarf2_per_cu_data *per_cu, const frame_info_ptr &frame,
+ reg_buffer *regcache,
const struct property_addr_info *addr_info,
struct type *type, struct type *subobj_type,
LONGEST subobj_offset)
{
this->m_per_cu = per_cu;
this->m_frame = frame;
+ this->m_regcache = regcache;
this->m_addr_info = addr_info;
eval (addr, len);
@@ -1149,7 +1180,8 @@ get_signed_type (struct gdbarch *gdbarch, struct type *type)
CORE_ADDR
dwarf_expr_context::fetch_address (int n)
{
- gdbarch *arch = this->m_per_objfile->objfile->arch ();
+ gdbarch *arch = (m_gdbarch == nullptr ? this->m_per_objfile->objfile->arch ()
+ : m_gdbarch);
value *result_val = fetch (n);
bfd_endian byte_order = gdbarch_byte_order (arch);
ULONGEST result;
@@ -1493,7 +1525,8 @@ void
dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
const gdb_byte *op_end)
{
- gdbarch *arch = this->m_per_objfile->objfile->arch ();
+ gdbarch *arch = (this->m_gdbarch ? this->m_gdbarch
+ : this->m_per_objfile->objfile->arch ());
bfd_endian byte_order = gdbarch_byte_order (arch);
/* Old-style "untyped" DWARF values need special treatment in a
couple of places, specifically DW_OP_mod and DW_OP_shr. We need
@@ -1784,9 +1817,14 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
case DW_OP_breg31:
{
op_ptr = safe_read_sleb128 (op_ptr, op_end, &offset);
- ensure_have_frame (this->m_frame, "DW_OP_breg");
+ ensure_have_frame_or_regcache (this->m_frame, this->m_regcache,
+ "DW_OP_breg");
+
+ if (this->m_frame != nullptr)
+ result = read_addr_from_reg (this->m_frame, op - DW_OP_breg0);
+ else
+ result = read_addr_from_reg (this->m_regcache, op - DW_OP_breg0);
- result = read_addr_from_reg (this->m_frame, op - DW_OP_breg0);
result += offset;
result_val = value_from_ulongest (address_type, result);
}
@@ -1795,9 +1833,14 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
{
op_ptr = safe_read_uleb128 (op_ptr, op_end, ®);
op_ptr = safe_read_sleb128 (op_ptr, op_end, &offset);
- ensure_have_frame (this->m_frame, "DW_OP_bregx");
+ ensure_have_frame_or_regcache (this->m_frame, this->m_regcache,
+ "DW_OP_bregx");
+
+ if (this->m_frame != nullptr)
+ result = read_addr_from_reg (this->m_frame, reg);
+ else
+ result = read_addr_from_reg (this->m_regcache, reg);
- result = read_addr_from_reg (this->m_frame, reg);
result += offset;
result_val = value_from_ulongest (address_type, result);
}
diff --git a/gdb/dwarf2/expr.h b/gdb/dwarf2/expr.h
index b02cc5316406..9a3c92650664 100644
--- a/gdb/dwarf2/expr.h
+++ b/gdb/dwarf2/expr.h
@@ -115,12 +115,14 @@ struct dwarf_stack_value
bool in_stack_memory;
};
+struct reg_buffer;
+
/* The expression evaluator works with a dwarf_expr_context, describing
its current state and its callbacks. */
struct dwarf_expr_context
{
dwarf_expr_context (dwarf2_per_objfile *per_objfile,
- int addr_size);
+ int addr_size, gdbarch *gdbarch = nullptr);
virtual ~dwarf_expr_context () = default;
void push_address (CORE_ADDR value, bool in_stack_memory);
@@ -128,6 +130,8 @@ struct dwarf_expr_context
/* Evaluate the expression at ADDR (LEN bytes long) in a given PER_CU
and FRAME context.
+ If FRAME is nullptr, then registers are read from REGCACHE.
+
AS_LVAL defines if the returned struct value is expected to be a
value (false) or a location description (true).
@@ -138,6 +142,7 @@ struct dwarf_expr_context
memory addresses with the passed in buffer. */
value *evaluate (const gdb_byte *addr, size_t len, bool as_lval,
dwarf2_per_cu_data *per_cu, const frame_info_ptr &frame,
+ reg_buffer *regcache,
const struct property_addr_info *addr_info = nullptr,
struct type *type = nullptr,
struct type *subobj_type = nullptr,
@@ -198,6 +203,12 @@ struct dwarf_expr_context
/* Frame information used for the evaluation. */
frame_info_ptr m_frame = nullptr;
+ /* Register cache used for the evaluation, in case there's no frame. */
+ reg_buffer *m_regcache = nullptr;
+
+ /* gdbarch used for the evaluation. */
+ gdbarch *m_gdbarch = nullptr;
+
/* Compilation unit used for the evaluation. */
dwarf2_per_cu_data *m_per_cu = nullptr;
@@ -258,6 +269,10 @@ struct dwarf_expr_context
read as an address in a given FRAME. */
CORE_ADDR read_addr_from_reg (const frame_info_ptr &frame, int reg);
+/* Return the value of register number REG (a DWARF register number),
+ read as an address in a given REGCACHE. */
+CORE_ADDR read_addr_from_reg (reg_buffer *regcache, int reg);
+
void dwarf_expr_require_composition (const gdb_byte *, const gdb_byte *,
const char *);
diff --git a/gdb/dwarf2/frame.c b/gdb/dwarf2/frame.c
index 841d2d4a2fec..3da9551a7ace 100644
--- a/gdb/dwarf2/frame.c
+++ b/gdb/dwarf2/frame.c
@@ -237,7 +237,7 @@ execute_stack_op (const gdb_byte *exp, ULONGEST len, int addr_size,
scoped_value_mark free_values;
ctx.push_address (initial, initial_in_stack_memory);
- value *result_val = ctx.evaluate (exp, len, true, nullptr, this_frame);
+ value *result_val = ctx.evaluate (exp, len, true, nullptr, this_frame, nullptr);
if (result_val->lval () == lval_memory)
return result_val->address ();
diff --git a/gdb/dwarf2/loc.c b/gdb/dwarf2/loc.c
index d5153862a01d..d7fa58047cdd 100644
--- a/gdb/dwarf2/loc.c
+++ b/gdb/dwarf2/loc.c
@@ -1518,7 +1518,7 @@ dwarf2_evaluate_loc_desc_full (struct type *type, const frame_info_ptr &frame,
try
{
retval = ctx.evaluate (data, size, as_lval, per_cu, frame, nullptr,
- type, subobj_type, subobj_byte_offset);
+ nullptr, type, subobj_type, subobj_byte_offset);
}
catch (const gdb_exception_error &ex)
{
@@ -1565,11 +1565,12 @@ dwarf2_evaluate_loc_desc (struct type *type, const frame_info_ptr &frame,
per_objfile, NULL, 0, as_lval);
}
-/* Evaluates a dwarf expression and stores the result in VAL,
- expecting that the dwarf expression only produces a single
- CORE_ADDR. FRAME is the frame in which the expression is
- evaluated. ADDR_STACK is a context (location of a variable) and
- might be needed to evaluate the location expression.
+/* Evaluates a dwarf expression and stores the result in VAL, expecting
+ that the dwarf expression only produces a single CORE_ADDR. FRAME is
+ the frame in which the expression is evaluated. If FRAME is nullptr,
+ then registers are read from REGCACHE instead. ADDR_STACK is a context
+ (location of a variable) and might be needed to evaluate the location
+ expression.
PUSH_VALUES is an array of values to be pushed to the expression stack
before evaluation starts. PUSH_VALUES[0] is pushed first, then
@@ -1580,6 +1581,7 @@ dwarf2_evaluate_loc_desc (struct type *type, const frame_info_ptr &frame,
static int
dwarf2_locexpr_baton_eval (const struct dwarf2_locexpr_baton *dlbaton,
const frame_info_ptr &frame,
+ reg_buffer *regcache,
const struct property_addr_info *addr_stack,
CORE_ADDR *valp,
gdb::array_view<CORE_ADDR> push_values,
@@ -1590,7 +1592,21 @@ dwarf2_locexpr_baton_eval (const struct dwarf2_locexpr_baton *dlbaton,
dwarf2_per_objfile *per_objfile = dlbaton->per_objfile;
dwarf2_per_cu_data *per_cu = dlbaton->per_cu;
- dwarf_expr_context ctx (per_objfile, per_cu->addr_size ());
+ gdbarch *gdbarch;
+
+ if (per_objfile != nullptr)
+ gdbarch = per_objfile->objfile->arch ();
+ else if (frame != nullptr)
+ gdbarch = get_frame_arch (frame);
+ else
+ {
+ gdb_assert (regcache != nullptr);
+ gdbarch = regcache->arch ();
+ }
+
+ int addr_size = (per_cu == nullptr ? gdbarch_addr_bit (gdbarch) / 8
+ : per_cu->addr_size ());
+ dwarf_expr_context ctx (per_objfile, addr_size, gdbarch);
value *result;
scoped_value_mark free_values;
@@ -1602,7 +1618,7 @@ dwarf2_locexpr_baton_eval (const struct dwarf2_locexpr_baton *dlbaton,
try
{
result = ctx.evaluate (dlbaton->data, dlbaton->size,
- true, per_cu, frame, addr_stack);
+ true, per_cu, frame, regcache, addr_stack);
}
catch (const gdb_exception_error &ex)
{
@@ -1641,6 +1657,7 @@ dwarf2_locexpr_baton_eval (const struct dwarf2_locexpr_baton *dlbaton,
bool
dwarf2_evaluate_property (const dynamic_prop *prop,
const frame_info_ptr &initial_frame,
+ reg_buffer *regcache,
const property_addr_info *addr_stack,
CORE_ADDR *value,
gdb::array_view<CORE_ADDR> push_values)
@@ -1654,7 +1671,8 @@ dwarf2_evaluate_property (const dynamic_prop *prop,
scoped_restore_current_language save_language;
frame_info_ptr frame = initial_frame;
- if (frame == NULL && has_stack_frames ())
+ /* Only try to get a frame if no regcache was provided. */
+ if (frame == nullptr && regcache == nullptr && has_stack_frames ())
frame = get_selected_frame (NULL);
switch (prop->kind ())
@@ -1665,8 +1683,9 @@ dwarf2_evaluate_property (const dynamic_prop *prop,
gdb_assert (baton->property_type != NULL);
bool is_reference = baton->locexpr.is_reference;
- if (dwarf2_locexpr_baton_eval (&baton->locexpr, frame, addr_stack,
- value, push_values, &is_reference))
+ if (dwarf2_locexpr_baton_eval (&baton->locexpr, frame, regcache,
+ addr_stack, value, push_values,
+ &is_reference))
{
if (is_reference)
{
diff --git a/gdb/dwarf2/loc.h b/gdb/dwarf2/loc.h
index 4fb743618e7c..679c4962d137 100644
--- a/gdb/dwarf2/loc.h
+++ b/gdb/dwarf2/loc.h
@@ -118,8 +118,10 @@ struct property_addr_info
etc. This means the during evaluation PUSH_VALUES[0] will be at the
bottom of the stack. */
+struct reg_buffer;
bool dwarf2_evaluate_property (const struct dynamic_prop *prop,
const frame_info_ptr &frame,
+ reg_buffer *regcache,
const property_addr_info *addr_stack,
CORE_ADDR *value,
gdb::array_view<CORE_ADDR> push_values = {});
diff --git a/gdb/findvar.c b/gdb/findvar.c
index f7760aa61ca9..d65bf2fc278c 100644
--- a/gdb/findvar.c
+++ b/gdb/findvar.c
@@ -656,3 +656,20 @@ address_from_register (int regnum, const frame_info_ptr &frame)
return value_as_address (v.get ());
}
+
+/* Return contents of register REGNUM in REGCACHE as address.
+ Will abort if register value is not available. */
+
+CORE_ADDR
+address_from_register (int regnum, reg_buffer *regcache)
+{
+ if (regcache->get_register_status (regnum) != REG_VALID)
+ throw_error (NOT_AVAILABLE_ERROR, _("register %d is not available"),
+ regnum);
+
+ gdb_byte buffer[sizeof (CORE_ADDR)];
+ regcache->raw_collect (regnum, buffer);
+
+ type *type = builtin_type (regcache->arch ())->builtin_data_ptr;
+ return extract_typed_address (buffer, type);
+}
diff --git a/gdb/frame.c b/gdb/frame.c
index 2c1ea012191b..663ff7fa773b 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -3175,7 +3175,8 @@ frame_follow_static_link (const frame_info_ptr &initial_frame)
CORE_ADDR upper_frame_base;
- if (!dwarf2_evaluate_property (static_link, initial_frame, NULL, &upper_frame_base))
+ if (!dwarf2_evaluate_property (static_link, initial_frame, nullptr, nullptr,
+ &upper_frame_base))
return {};
/* Now climb up the stack frame until we reach the frame we are interested
diff --git a/gdb/gdbtypes.c b/gdb/gdbtypes.c
index 2a3aea229cb0..ea8bad4e826c 100644
--- a/gdb/gdbtypes.c
+++ b/gdb/gdbtypes.c
@@ -2146,7 +2146,7 @@ is_dynamic_type (struct type *type)
static struct type *resolve_dynamic_type_internal
(struct type *type, struct property_addr_info *addr_stack,
- const frame_info_ptr &frame, bool top_level);
+ const frame_info_ptr &frame, reg_buffer *regcache, bool top_level);
/* Given a dynamic range type (dyn_range_type) and a stack of
struct property_addr_info elements, return a static version
@@ -2168,6 +2168,7 @@ static struct type *
resolve_dynamic_range (struct type *dyn_range_type,
struct property_addr_info *addr_stack,
const frame_info_ptr &frame,
+ reg_buffer *regcache,
int rank, bool resolve_p = true)
{
CORE_ADDR value;
@@ -2180,7 +2181,7 @@ resolve_dynamic_range (struct type *dyn_range_type,
const struct dynamic_prop *prop = &dyn_range_type->bounds ()->low;
if (resolve_p)
{
- if (dwarf2_evaluate_property (prop, frame, addr_stack, &value,
+ if (dwarf2_evaluate_property (prop, frame, regcache, addr_stack, &value,
{ (CORE_ADDR) rank }))
low_bound.set_const_val (value);
else if (prop->kind () == PROP_UNDEFINED)
@@ -2194,7 +2195,7 @@ resolve_dynamic_range (struct type *dyn_range_type,
prop = &dyn_range_type->bounds ()->high;
if (resolve_p)
{
- if (dwarf2_evaluate_property (prop, frame, addr_stack, &value,
+ if (dwarf2_evaluate_property (prop, frame, regcache, addr_stack, &value,
{ (CORE_ADDR) rank }))
{
high_bound.set_const_val (value);
@@ -2213,8 +2214,8 @@ resolve_dynamic_range (struct type *dyn_range_type,
bool byte_stride_p = dyn_range_type->bounds ()->flag_is_byte_stride;
prop = &dyn_range_type->bounds ()->stride;
- if (resolve_p && dwarf2_evaluate_property (prop, frame, addr_stack, &value,
- { (CORE_ADDR) rank }))
+ if (resolve_p && dwarf2_evaluate_property (prop, frame, regcache, addr_stack,
+ &value, { (CORE_ADDR) rank }))
{
stride.set_const_val (value);
@@ -2236,7 +2237,7 @@ resolve_dynamic_range (struct type *dyn_range_type,
static_target_type
= resolve_dynamic_type_internal (dyn_range_type->target_type (),
- addr_stack, frame, false);
+ addr_stack, frame, regcache, false);
LONGEST bias = dyn_range_type->bounds ()->bias;
type_allocator alloc (dyn_range_type);
static_range_type = create_range_type_with_stride
@@ -2270,6 +2271,7 @@ static struct type *
resolve_dynamic_array_or_string_1 (struct type *type,
struct property_addr_info *addr_stack,
const frame_info_ptr &frame,
+ reg_buffer *regcache,
int rank, bool resolve_p)
{
CORE_ADDR value;
@@ -2299,7 +2301,7 @@ resolve_dynamic_array_or_string_1 (struct type *type,
dimension of the array. */
prop = TYPE_ALLOCATED_PROP (type);
if (prop != NULL && resolve_p
- && dwarf2_evaluate_property (prop, frame, addr_stack, &value))
+ && dwarf2_evaluate_property (prop, frame, regcache, addr_stack, &value))
{
prop->set_const_val (value);
if (value == 0)
@@ -2308,7 +2310,7 @@ resolve_dynamic_array_or_string_1 (struct type *type,
prop = TYPE_ASSOCIATED_PROP (type);
if (prop != NULL && resolve_p
- && dwarf2_evaluate_property (prop, frame, addr_stack, &value))
+ && dwarf2_evaluate_property (prop, frame, regcache, addr_stack, &value))
{
prop->set_const_val (value);
if (value == 0)
@@ -2317,14 +2319,15 @@ resolve_dynamic_array_or_string_1 (struct type *type,
range_type = check_typedef (type->index_type ());
range_type
- = resolve_dynamic_range (range_type, addr_stack, frame, rank, resolve_p);
+ = resolve_dynamic_range (range_type, addr_stack, frame, regcache, rank,
+ resolve_p);
ary_dim = check_typedef (type->target_type ());
if (ary_dim != NULL && ary_dim->code () == TYPE_CODE_ARRAY)
{
ary_dim = copy_type (ary_dim);
elt_type = resolve_dynamic_array_or_string_1 (ary_dim, addr_stack,
- frame, rank - 1,
+ frame, regcache, rank - 1,
resolve_p);
}
else if (ary_dim != nullptr && ary_dim->code () == TYPE_CODE_STRING)
@@ -2355,7 +2358,7 @@ resolve_dynamic_array_or_string_1 (struct type *type,
Fortran, and hope that this doesn't cause problems for anyone
else. */
elt_type = resolve_dynamic_type_internal (type->target_type (),
- addr_stack, frame, 0);
+ addr_stack, frame, regcache, 0);
}
else
elt_type = type->target_type ();
@@ -2365,7 +2368,7 @@ resolve_dynamic_array_or_string_1 (struct type *type,
prop = nullptr;
if (prop != NULL && resolve_p)
{
- if (dwarf2_evaluate_property (prop, frame, addr_stack, &value))
+ if (dwarf2_evaluate_property (prop, frame, regcache, addr_stack, &value))
{
type->remove_dyn_prop (DYN_PROP_BYTE_STRIDE);
bit_stride = (unsigned int) (value * 8);
@@ -2397,7 +2400,8 @@ resolve_dynamic_array_or_string_1 (struct type *type,
static struct type *
resolve_dynamic_array_or_string (struct type *type,
struct property_addr_info *addr_stack,
- const frame_info_ptr &frame)
+ const frame_info_ptr &frame,
+ reg_buffer *regcache)
{
CORE_ADDR value;
int rank = 0;
@@ -2411,7 +2415,7 @@ resolve_dynamic_array_or_string (struct type *type,
/* Resolve the rank property to get rank value. */
struct dynamic_prop *prop = TYPE_RANK_PROP (type);
- if (dwarf2_evaluate_property (prop, frame, addr_stack, &value))
+ if (dwarf2_evaluate_property (prop, frame, regcache, addr_stack, &value))
{
prop->set_const_val (value);
rank = value;
@@ -2479,8 +2483,8 @@ resolve_dynamic_array_or_string (struct type *type,
reduce the rank by 1 here. */
--rank;
- return resolve_dynamic_array_or_string_1 (type, addr_stack, frame, rank,
- true);
+ return resolve_dynamic_array_or_string_1 (type, addr_stack, frame, regcache,
+ rank, true);
}
/* Resolve dynamic bounds of members of the union TYPE to static
@@ -2490,7 +2494,8 @@ resolve_dynamic_array_or_string (struct type *type,
static struct type *
resolve_dynamic_union (struct type *type,
struct property_addr_info *addr_stack,
- const frame_info_ptr &frame)
+ const frame_info_ptr &frame,
+ reg_buffer *regcache)
{
struct type *resolved_type;
int i;
@@ -2508,7 +2513,7 @@ resolve_dynamic_union (struct type *type,
continue;
t = resolve_dynamic_type_internal (resolved_type->field (i).type (),
- addr_stack, frame, false);
+ addr_stack, frame, regcache, false);
resolved_type->field (i).set_type (t);
struct type *real_type = check_typedef (t);
@@ -2682,7 +2687,8 @@ compute_variant_fields (struct type *type,
static struct type *
resolve_dynamic_struct (struct type *type,
struct property_addr_info *addr_stack,
- const frame_info_ptr &frame)
+ const frame_info_ptr &frame,
+ reg_buffer *regcache)
{
struct type *resolved_type;
int i;
@@ -2725,8 +2731,8 @@ resolve_dynamic_struct (struct type *type,
prop.set_locexpr (&baton);
CORE_ADDR addr;
- if (dwarf2_evaluate_property (&prop, frame, addr_stack, &addr,
- {addr_stack->addr}))
+ if (dwarf2_evaluate_property (&prop, frame, regcache, addr_stack,
+ &addr, {addr_stack->addr}))
resolved_type->field (i).set_loc_bitpos
(TARGET_CHAR_BIT * (addr - addr_stack->addr));
}
@@ -2752,7 +2758,7 @@ resolve_dynamic_struct (struct type *type,
resolved_type->field (i).set_type
(resolve_dynamic_type_internal (resolved_type->field (i).type (),
- &pinfo, frame, false));
+ &pinfo, frame, regcache, false));
gdb_assert (resolved_type->field (i).loc_kind ()
== FIELD_LOC_KIND_BITPOS);
@@ -2798,6 +2804,7 @@ static struct type *
resolve_dynamic_type_internal (struct type *type,
struct property_addr_info *addr_stack,
const frame_info_ptr &frame,
+ reg_buffer *regcache,
bool top_level)
{
struct type *real_type = check_typedef (type);
@@ -2811,7 +2818,7 @@ resolve_dynamic_type_internal (struct type *type,
std::optional<CORE_ADDR> type_length;
prop = TYPE_DYNAMIC_LENGTH (type);
if (prop != NULL
- && dwarf2_evaluate_property (prop, frame, addr_stack, &value))
+ && dwarf2_evaluate_property (prop, frame, regcache, addr_stack, &value))
type_length = value;
if (type->code () == TYPE_CODE_TYPEDEF)
@@ -2819,7 +2826,7 @@ resolve_dynamic_type_internal (struct type *type,
resolved_type = copy_type (type);
resolved_type->set_target_type
(resolve_dynamic_type_internal (type->target_type (), addr_stack,
- frame, top_level));
+ frame, regcache, top_level));
}
else
{
@@ -2849,8 +2856,8 @@ resolve_dynamic_type_internal (struct type *type,
{
resolved_type = copy_type (type);
resolved_type->set_target_type
- (resolve_dynamic_type_internal (type->target_type (),
- &pinfo, frame, true));
+ (resolve_dynamic_type_internal (type->target_type (), &pinfo,
+ frame, regcache, true));
}
break;
}
@@ -2860,7 +2867,7 @@ resolve_dynamic_type_internal (struct type *type,
treated as one here. */
case TYPE_CODE_ARRAY:
resolved_type = resolve_dynamic_array_or_string (type, addr_stack,
- frame);
+ frame, regcache);
break;
case TYPE_CODE_RANGE:
@@ -2869,15 +2876,18 @@ resolve_dynamic_type_internal (struct type *type,
this rank value is not actually required for the resolution of
the dynamic range, otherwise, we'd be resolving this range
within the context of a dynamic array. */
- resolved_type = resolve_dynamic_range (type, addr_stack, frame, 0);
+ resolved_type = resolve_dynamic_range (type, addr_stack, frame,
+ regcache, 0);
break;
case TYPE_CODE_UNION:
- resolved_type = resolve_dynamic_union (type, addr_stack, frame);
+ resolved_type = resolve_dynamic_union (type, addr_stack, frame,
+ regcache);
break;
case TYPE_CODE_STRUCT:
- resolved_type = resolve_dynamic_struct (type, addr_stack, frame);
+ resolved_type = resolve_dynamic_struct (type, addr_stack, frame,
+ regcache);
break;
}
}
@@ -2894,7 +2904,7 @@ resolve_dynamic_type_internal (struct type *type,
/* Resolve data_location attribute. */
prop = TYPE_DATA_LOCATION (resolved_type);
if (prop != NULL
- && dwarf2_evaluate_property (prop, frame, addr_stack, &value))
+ && dwarf2_evaluate_property (prop, frame, regcache, addr_stack, &value))
{
/* Start of Fortran hack. See comment in f-lang.h for what is going
on here.*/
@@ -2915,7 +2925,8 @@ struct type *
resolve_dynamic_type (struct type *type,
gdb::array_view<const gdb_byte> valaddr,
CORE_ADDR addr,
- const frame_info_ptr *in_frame)
+ const frame_info_ptr *in_frame,
+ reg_buffer *regcache)
{
struct property_addr_info pinfo
= {check_typedef (type), valaddr, addr, NULL};
@@ -2924,7 +2935,7 @@ resolve_dynamic_type (struct type *type,
if (in_frame != nullptr)
frame = *in_frame;
- return resolve_dynamic_type_internal (type, &pinfo, frame, true);
+ return resolve_dynamic_type_internal (type, &pinfo, frame, regcache, true);
}
/* See gdbtypes.h */
diff --git a/gdb/gdbtypes.h b/gdb/gdbtypes.h
index f80bd7e071a3..514af300de3b 100644
--- a/gdb/gdbtypes.h
+++ b/gdb/gdbtypes.h
@@ -2625,9 +2625,12 @@ extern CORE_ADDR get_pointer_type_max (struct type *);
have a different type when resolved (depending on the contents of
memory). In this situation, 'is_dynamic_type' will still return
true for the return value of this function. */
+
+struct reg_buffer;
extern struct type *resolve_dynamic_type
(struct type *type, gdb::array_view<const gdb_byte> valaddr,
- CORE_ADDR addr, const frame_info_ptr *frame = nullptr);
+ CORE_ADDR addr, const frame_info_ptr *frame = nullptr,
+ reg_buffer *regcache = nullptr);
/* * Predicate if the type has dynamic values, which are not resolved yet.
See the caveat in 'resolve_dynamic_type' to understand a scenario
diff --git a/gdb/gnu-v3-abi.c b/gdb/gnu-v3-abi.c
index aefbee542703..5cc0d1d2d4f4 100644
--- a/gdb/gnu-v3-abi.c
+++ b/gdb/gnu-v3-abi.c
@@ -493,7 +493,7 @@ gnuv3_baseclass_offset (struct type *type, int index,
addr_stack.next = nullptr;
CORE_ADDR result;
- if (dwarf2_evaluate_property (&prop, nullptr, &addr_stack, &result,
+ if (dwarf2_evaluate_property (&prop, nullptr, nullptr, &addr_stack, &result,
{addr_stack.addr}))
return (int) (result - addr_stack.addr);
}
diff --git a/gdb/value.h b/gdb/value.h
index 13cfb007aa2c..802a49066288 100644
--- a/gdb/value.h
+++ b/gdb/value.h
@@ -1133,6 +1133,9 @@ extern struct value *value_from_register (struct type *type, int regnum,
extern CORE_ADDR address_from_register (int regnum,
const frame_info_ptr &frame);
+struct reg_buffer;
+extern CORE_ADDR address_from_register (int regnum,
+ reg_buffer *regcache);
extern struct value *value_of_variable (struct symbol *var,
const struct block *b);
^ permalink raw reply [flat|nested] 16+ messages in thread
* [RFC PATCH v4 09/15] GDB tdesc: Add vector type with number of elements given by math expression.
2024-11-02 2:56 [RFC PATCH v4 00/15] gdbserver improvements for AArch64 SVE support Thiago Jung Bauermann
` (7 preceding siblings ...)
2024-11-02 2:56 ` [RFC PATCH v4 08/15] GDB: Allow DWARF expression evaluation to use regcache Thiago Jung Bauermann
@ 2024-11-02 2:56 ` Thiago Jung Bauermann
2024-11-02 2:56 ` [RFC PATCH v4 10/15] GDB: Add concept of variable-size registers to the regcache Thiago Jung Bauermann
` (5 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Thiago Jung Bauermann @ 2024-11-02 2:56 UTC (permalink / raw)
To: gdb-patches
This allows using MathML¹ to express the vector count, including
referencing the contents of other registers. E.g.:
<vector id="svevqu" type="uint128">
<count id="svevq_count">
<!-- How many 128-bit elements there are in a z register: $vg / 2 -->
<math>
<apply>
<divide/>
<ci>85</ci>
<cn>2</cn>
</apply>
</math>
</count>
</vector>
The <ci> element (meaning "content identifier") is used to represent
variables. In this case, it represents the VG register which has regnum
85 in the SVE AArch64 feature. The <cn> element is used to represent
numbers.
MathML has two sets of markup. One for representing a math expression
visually, and another to represent its meaning. This patch uses the
latter markup, which is suited for computation. It currently implements
just a subset of MathML to enable simple expressions. If this approach
is accepted, we don't have to implement everything from the standard.
We could add things as we need them.
Currently, registers are referenced by their numbers but this is a
temporary hack. I'll fix it to reference registers by their names
instead.
When the XML above is parsed, it's converted to a DWARF expression which
is stored in the GDB type.
When an XML feature contains a math expression referencing a register, it
needs to have an <architecture> tag. This is because when generating the
C code corresponding to the XML, the feature XML is parsed in isolation
and not as part of a whole target description (which would contain the
architecture information).
One minor disadvantage of this approach is that gdbserver needs to have a
DWARF expression interpreter. Since this feature only needs a subset of
the DWARF expression opcodes, it's a small amount of simple code and thus
it's not really a problem IMHO.
¹ https://www.w3.org/TR/MathML/
---
gdb/features/gdb-target.dtd | 17 +-
gdb/gdbarch-gen.c | 32 +++
gdb/gdbarch-gen.h | 9 +
gdb/gdbarch_components.py | 11 +
gdb/gdbtypes.c | 61 ++++++
gdb/gdbtypes.h | 2 +
gdb/target-descriptions.c | 94 +++++++-
gdb/target-descriptions.h | 6 +
gdb/xml-tdesc.c | 416 +++++++++++++++++++++++++++++++++---
gdbserver/Makefile.in | 2 +
gdbserver/config.in | 3 +
gdbserver/configure | 6 +
gdbserver/configure.ac | 5 +
gdbserver/locexpr.cc | 107 ++++++++++
gdbserver/locexpr.h | 31 +++
gdbserver/tdesc.cc | 18 ++
gdbsupport/tdesc.cc | 134 +++++++++++-
gdbsupport/tdesc.h | 47 ++++
18 files changed, 963 insertions(+), 38 deletions(-)
create mode 100644 gdbserver/locexpr.cc
create mode 100644 gdbserver/locexpr.h
diff --git a/gdb/features/gdb-target.dtd b/gdb/features/gdb-target.dtd
index d07703fca8b6..d38ca6f90bce 100644
--- a/gdb/features/gdb-target.dtd
+++ b/gdb/features/gdb-target.dtd
@@ -20,7 +20,7 @@
<!ELEMENT compatible (#PCDATA)>
<!ELEMENT feature
- ((vector | flags | struct | union )*, reg*)>
+ (architecture?, (vector | flags | struct | union )*, reg*)>
<!ATTLIST feature
name ID #REQUIRED>
@@ -34,11 +34,22 @@
group CDATA #IMPLIED
>
-<!ELEMENT vector EMPTY>
+<!ELEMENT vector (count?)>
<!ATTLIST vector
id CDATA #REQUIRED
type CDATA #REQUIRED
- count CDATA #REQUIRED>
+ count CDATA #IMPLIED>
+
+<!ELEMENT count (math?)>
+<!ATTLIST count
+ id ID #IMPLIED
+ idref IDREF #IMPLIED>
+<!ELEMENT math (apply | ci | cn)>
+<!ELEMENT apply ((divide | times), (ci | cn)+)>
+<!ELEMENT ci (#PCDATA)>
+<!ELEMENT cn (#PCDATA)>
+<!ELEMENT divide EMPTY>
+<!ELEMENT times EMPTY>
<!ELEMENT flags (field+)>
<!ATTLIST flags
diff --git a/gdb/gdbarch-gen.c b/gdb/gdbarch-gen.c
index 0d00cd7c9933..00afb37d7ef8 100644
--- a/gdb/gdbarch-gen.c
+++ b/gdb/gdbarch-gen.c
@@ -89,6 +89,7 @@ struct gdbarch
gdbarch_ecoff_reg_to_regnum_ftype *ecoff_reg_to_regnum = no_op_reg_to_regnum;
gdbarch_sdb_reg_to_regnum_ftype *sdb_reg_to_regnum = no_op_reg_to_regnum;
gdbarch_dwarf2_reg_to_regnum_ftype *dwarf2_reg_to_regnum = no_op_reg_to_regnum;
+ gdbarch_regnum_to_dwarf2_reg_ftype *regnum_to_dwarf2_reg = nullptr;
gdbarch_register_name_ftype *register_name = nullptr;
gdbarch_register_type_ftype *register_type = nullptr;
gdbarch_dummy_id_ftype *dummy_id = default_dummy_id;
@@ -347,6 +348,7 @@ verify_gdbarch (struct gdbarch *gdbarch)
/* Skip verify of ecoff_reg_to_regnum, invalid_p == 0. */
/* Skip verify of sdb_reg_to_regnum, invalid_p == 0. */
/* Skip verify of dwarf2_reg_to_regnum, invalid_p == 0. */
+ /* Skip verify of regnum_to_dwarf2_reg, has predicate. */
if (gdbarch->register_name == 0)
log.puts ("\n\tregister_name");
if (gdbarch->register_type == 0)
@@ -711,6 +713,12 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
gdb_printf (file,
"gdbarch_dump: dwarf2_reg_to_regnum = <%s>\n",
host_address_to_string (gdbarch->dwarf2_reg_to_regnum));
+ gdb_printf (file,
+ "gdbarch_dump: gdbarch_regnum_to_dwarf2_reg_p() = %d\n",
+ gdbarch_regnum_to_dwarf2_reg_p (gdbarch));
+ gdb_printf (file,
+ "gdbarch_dump: regnum_to_dwarf2_reg = <%s>\n",
+ host_address_to_string (gdbarch->regnum_to_dwarf2_reg));
gdb_printf (file,
"gdbarch_dump: register_name = <%s>\n",
host_address_to_string (gdbarch->register_name));
@@ -2201,6 +2209,30 @@ set_gdbarch_dwarf2_reg_to_regnum (struct gdbarch *gdbarch,
gdbarch->dwarf2_reg_to_regnum = dwarf2_reg_to_regnum;
}
+bool
+gdbarch_regnum_to_dwarf2_reg_p (struct gdbarch *gdbarch)
+{
+ gdb_assert (gdbarch != NULL);
+ return gdbarch->regnum_to_dwarf2_reg != NULL;
+}
+
+int
+gdbarch_regnum_to_dwarf2_reg (struct gdbarch *gdbarch, int regnum)
+{
+ gdb_assert (gdbarch != NULL);
+ gdb_assert (gdbarch->regnum_to_dwarf2_reg != NULL);
+ if (gdbarch_debug >= 2)
+ gdb_printf (gdb_stdlog, "gdbarch_regnum_to_dwarf2_reg called\n");
+ return gdbarch->regnum_to_dwarf2_reg (gdbarch, regnum);
+}
+
+void
+set_gdbarch_regnum_to_dwarf2_reg (struct gdbarch *gdbarch,
+ gdbarch_regnum_to_dwarf2_reg_ftype regnum_to_dwarf2_reg)
+{
+ gdbarch->regnum_to_dwarf2_reg = regnum_to_dwarf2_reg;
+}
+
const char *
gdbarch_register_name (struct gdbarch *gdbarch, int regnr)
{
diff --git a/gdb/gdbarch-gen.h b/gdb/gdbarch-gen.h
index b982fd7cd092..1d4d19cf6774 100644
--- a/gdb/gdbarch-gen.h
+++ b/gdb/gdbarch-gen.h
@@ -308,6 +308,15 @@ typedef int (gdbarch_dwarf2_reg_to_regnum_ftype) (struct gdbarch *gdbarch, int d
extern int gdbarch_dwarf2_reg_to_regnum (struct gdbarch *gdbarch, int dwarf2_regnr);
extern void set_gdbarch_dwarf2_reg_to_regnum (struct gdbarch *gdbarch, gdbarch_dwarf2_reg_to_regnum_ftype *dwarf2_reg_to_regnum);
+/* Provide a default mapping from a gdb REGNUM to a DWARF2 register number.
+ Return -1 for bad REGNUM. */
+
+extern bool gdbarch_regnum_to_dwarf2_reg_p (struct gdbarch *gdbarch);
+
+typedef int (gdbarch_regnum_to_dwarf2_reg_ftype) (struct gdbarch *gdbarch, int regnum);
+extern int gdbarch_regnum_to_dwarf2_reg (struct gdbarch *gdbarch, int regnum);
+extern void set_gdbarch_regnum_to_dwarf2_reg (struct gdbarch *gdbarch, gdbarch_regnum_to_dwarf2_reg_ftype *regnum_to_dwarf2_reg);
+
/* Return the name of register REGNR for the specified architecture.
REGNR can be any value greater than, or equal to zero, and less than
'gdbarch_num_cooked_regs (GDBARCH)'. If REGNR is not supported for
diff --git a/gdb/gdbarch_components.py b/gdb/gdbarch_components.py
index 4006380076dc..0764a6c31d92 100644
--- a/gdb/gdbarch_components.py
+++ b/gdb/gdbarch_components.py
@@ -589,6 +589,17 @@ Return -1 for bad REGNUM. Note: Several targets get this wrong.
invalid=False,
)
+Method(
+ comment="""
+Provide a default mapping from a gdb REGNUM to a DWARF2 register number.
+Return -1 for bad REGNUM.
+""",
+ type="int",
+ name="regnum_to_dwarf2_reg",
+ params=[("int", "regnum")],
+ predicate=True,
+)
+
Method(
comment="""
Return the name of register REGNR for the specified architecture.
diff --git a/gdb/gdbtypes.c b/gdb/gdbtypes.c
index ea8bad4e826c..8037283e3706 100644
--- a/gdb/gdbtypes.c
+++ b/gdb/gdbtypes.c
@@ -1030,6 +1030,40 @@ create_static_range_type (type_allocator &alloc, struct type *index_type,
return result_type;
}
+/* Create a range type using ALLOC.
+
+ Indices will be of type INDEX_TYPE, and will range from 0 to the value
+ LOC_EXPR_ evaluates to, inclusive. */
+
+static struct type *
+create_dynamic_range_type (type_allocator &alloc, struct type *index_type,
+ const gdb::array_view<const gdb_byte> locexpr_)
+{
+ struct dynamic_prop low, high;
+ struct dwarf2_property_baton *baton
+ = GDBARCH_OBSTACK_ZALLOC (alloc.arch (), dwarf2_property_baton);
+ size_t len = locexpr_.size () + 2;
+ gdb_byte *locexpr = GDBARCH_OBSTACK_CALLOC (alloc.arch (), len, gdb_byte);
+
+ memcpy (locexpr, locexpr_.data (), locexpr_.size());
+
+ /* The high bound is inclusive, so decrement the element count by one. */
+ locexpr[len - 2] = DW_OP_lit1;
+ locexpr[len - 1] = DW_OP_minus;
+
+ baton->property_type = builtin_type (alloc.arch ())->builtin_unsigned_int;
+ baton->locexpr.data = locexpr;
+ baton->locexpr.size = len;
+ baton->locexpr.is_reference = false;
+ baton->locexpr.per_objfile = nullptr;
+ baton->locexpr.per_cu = nullptr;
+
+ low.set_const_val (0);
+ high.set_locexpr (baton);
+
+ return create_range_type (alloc, index_type, &low, &high, 0);
+}
+
/* Predicate tests whether BOUNDS are static. Returns 1 if all bounds values
are static, otherwise returns 0. */
@@ -1409,6 +1443,21 @@ lookup_array_range_type (struct type *element_type,
return create_array_type (alloc, element_type, range_type);
}
+/* Create type for array ranges where the low bound is 0 and the high
+ bound is given by the provided DWARF LOCEXPR_. */
+
+static struct type *
+lookup_array_range_type (struct type *element_type,
+ const gdb::array_view<const gdb_byte> locexpr_)
+{
+ type_allocator alloc (element_type);
+ struct type *index_type, *range_type;
+
+ index_type = builtin_type (element_type->arch ())->builtin_unsigned_int;
+ range_type = create_dynamic_range_type (alloc, index_type, locexpr_);
+ return create_array_type (alloc, element_type, range_type);
+}
+
/* See gdbtypes.h. */
struct type *
@@ -1497,6 +1546,18 @@ init_vector_type (struct type *elt_type, int n)
return array_type;
}
+/* Create vector type of elements of type ELT_TYPE, where the number of
+ elements is given by the provided DWARF LOCEXPR. */
+
+struct type *
+init_vector_type (struct type *elt_type,
+ const gdb::array_view<const gdb_byte> locexpr)
+{
+ struct type *array_type = lookup_array_range_type (elt_type, locexpr);
+ make_vector_type (array_type);
+ return array_type;
+}
+
/* Internal routine called by TYPE_SELF_TYPE to return the type that TYPE
belongs to. In c++ this is the class of "this", but TYPE_THIS_TYPE is too
confusing. "self" is a common enough replacement for "this".
diff --git a/gdb/gdbtypes.h b/gdb/gdbtypes.h
index 514af300de3b..46103ea44656 100644
--- a/gdb/gdbtypes.h
+++ b/gdb/gdbtypes.h
@@ -2427,6 +2427,8 @@ extern void append_flags_type_flag (struct type *type, int bitpos,
extern void make_vector_type (struct type *array_type);
extern struct type *init_vector_type (struct type *elt_type, int n);
+extern struct type *init_vector_type (struct type *elt_type,
+ const gdb::array_view<const gdb_byte> locexpr);
extern struct type *lookup_reference_type (struct type *, enum type_code);
extern struct type *lookup_lvalue_reference_type (struct type *);
diff --git a/gdb/target-descriptions.c b/gdb/target-descriptions.c
index 82e4d96276e3..9c9cc2ceadba 100644
--- a/gdb/target-descriptions.c
+++ b/gdb/target-descriptions.c
@@ -159,7 +159,10 @@ make_gdb_type (struct gdbarch *gdbarch, struct tdesc_type *ttype)
return;
type *element_gdb_type = make_gdb_type (m_gdbarch, e->element_type);
- m_type = init_vector_type (element_gdb_type, e->count);
+ if (e->locexpr.size() > 0)
+ m_type = init_vector_type (element_gdb_type, e->locexpr);
+ else
+ m_type = init_vector_type (element_gdb_type, e->count);
m_type->set_name (xstrdup (e->name.c_str ()));
return;
}
@@ -364,6 +367,10 @@ struct target_desc : tdesc_element
/* The features associated with this target. */
std::vector<tdesc_feature_up> features;
+ /* Location expressions for variable-size vector registers may use constants
+ defined in the DWARF header. */
+ bool include_dwarf_header = false;
+
/* Used to cache the generated xml version of the target description. */
mutable char *xmltarget = nullptr;
@@ -452,6 +459,23 @@ get_arch_data (struct gdbarch *gdbarch)
return result;
}
+/* See gdbsupport/tdesc.h. */
+
+int
+tdesc_dwarf_reg_to_regnum (const char *arch, int dwarf_reg)
+{
+ struct gdbarch_info info;
+ info.bfd_arch_info = bfd_scan_arch (arch);
+ if (info.bfd_arch_info == nullptr)
+ return -1;
+
+ gdbarch *gdbarch = gdbarch_find_by_info (info);
+ if (gdbarch == nullptr)
+ return -1;
+
+ return gdbarch_dwarf2_reg_to_regnum (gdbarch, dwarf_reg);
+}
+
/* The string manipulated by the "set tdesc filename ..." command. */
static std::string tdesc_filename_cmd_string;
@@ -1214,6 +1238,14 @@ set_tdesc_osabi (struct target_desc *target_desc, enum gdb_osabi osabi)
{
target_desc->osabi = osabi;
}
+
+/* See gdb/target-descriptions.h. */
+
+void
+set_tdesc_include_dwarf_header (struct target_desc *target_desc, bool include)
+{
+ target_desc->include_dwarf_header = include;
+}
\f
static struct cmd_list_element *tdesc_set_cmdlist, *tdesc_show_cmdlist;
@@ -1372,9 +1404,45 @@ class print_c_tdesc : public tdesc_element_visitor
gdb_printf
(" element_type = tdesc_named_type (feature, \"%s\");\n",
type->element_type->name.c_str ());
- gdb_printf
- (" tdesc_create_vector (feature, \"%s\", element_type, %d);\n",
- type->name.c_str (), type->count);
+
+ if (type->count == TDESC_REG_VARIABLE_SIZE)
+ {
+ const std::string *prefix;
+
+ if (type->locexpr_id.has_value ())
+ prefix = &*type->locexpr_id;
+ else if (type->locexpr_idref.has_value ())
+ prefix = &*type->locexpr_idref;
+ else
+ prefix = &type->name;
+
+ if (!type->locexpr_idref.has_value ())
+ {
+ gdb_printf (" gdb_byte %s_locexpr[%zu];\n", prefix->c_str (),
+ type->locexpr.size ());
+ for (unsigned int i = 0; i < type->locexpr.size (); i++)
+ {
+ const char *op_name;
+
+ if (type->locexpr_str.has_value ())
+ op_name = type->locexpr_str->at (i);
+ else
+ op_name = nullptr;
+
+ if (op_name == nullptr)
+ gdb_printf (" %s_locexpr[%u] = %u;\n", prefix->c_str (), i,
+ type->locexpr[i]);
+ else
+ gdb_printf (" %s_locexpr[%u] = %s;\n", prefix->c_str (), i,
+ op_name);
+ }
+ }
+ gdb_printf (" tdesc_create_vector (feature, \"%s\", element_type, %s_locexpr);\n",
+ type->name.c_str (), prefix->c_str ());
+ }
+ else
+ gdb_printf (" tdesc_create_vector (feature, \"%s\", element_type, %d);\n",
+ type->name.c_str (), type->count);
gdb_printf ("\n");
}
@@ -1494,7 +1562,13 @@ class print_c_tdesc : public tdesc_element_visitor
gdb_printf ("\"%s\", ", reg->group.c_str ());
else
gdb_printf ("NULL, ");
- gdb_printf ("%d, \"%s\"", reg->bitsize, reg->type.c_str ());
+
+ if (reg->bitsize == TDESC_REG_VARIABLE_SIZE)
+ gdb_printf ("TDESC_REG_VARIABLE_SIZE, ");
+ else
+ gdb_printf ("%d, ", reg->bitsize);
+
+ gdb_printf ("\"%s\"", reg->type.c_str ());
if (reg->load_early)
gdb_printf (", true");
@@ -1558,6 +1632,8 @@ class print_c_feature : public print_c_tdesc
lbasename (m_filename_after_features.c_str ()));
gdb_printf ("#include \"gdbsupport/tdesc.h\"\n");
+ if (e->include_dwarf_header)
+ gdb_printf ("#include \"dwarf2.h\"\n");
gdb_printf ("\n");
}
@@ -1642,7 +1718,13 @@ class print_c_feature : public print_c_tdesc
gdb_printf ("\"%s\", ", reg->group.c_str ());
else
gdb_printf ("NULL, ");
- gdb_printf ("%d, \"%s\"", reg->bitsize, reg->type.c_str ());
+
+ if (reg->bitsize == TDESC_REG_VARIABLE_SIZE)
+ gdb_printf ("TDESC_REG_VARIABLE_SIZE, ");
+ else
+ gdb_printf ("%d, ", reg->bitsize);
+
+ gdb_printf ("\"%s\"", reg->type.c_str ());
if (reg->load_early)
gdb_printf (", true");
diff --git a/gdb/target-descriptions.h b/gdb/target-descriptions.h
index e40c7db5f79f..c65e5d203933 100644
--- a/gdb/target-descriptions.h
+++ b/gdb/target-descriptions.h
@@ -229,6 +229,12 @@ void set_tdesc_property (struct target_desc *,
void tdesc_add_compatible (struct target_desc *,
const struct bfd_arch_info *);
+/* Set whether the C version of the target description should include
+ dwarf2.h. */
+
+void set_tdesc_include_dwarf_header (struct target_desc *target_desc,
+ bool include);
+
#if GDB_SELF_TEST
namespace selftests {
diff --git a/gdb/xml-tdesc.c b/gdb/xml-tdesc.c
index 436c493d4f91..2f28968838d6 100644
--- a/gdb/xml-tdesc.c
+++ b/gdb/xml-tdesc.c
@@ -71,21 +71,62 @@ static std::unordered_map<std::string, target_desc_up> xml_cache;
struct tdesc_parsing_data
{
/* The target description we are building. */
- struct target_desc *tdesc;
+ struct target_desc *tdesc = nullptr;
/* The target feature we are currently parsing, or last parsed. */
- struct tdesc_feature *current_feature;
+ struct tdesc_feature *current_feature = nullptr;
/* The register number to use for the next register we see, if
it does not have its own. This starts at zero. */
- int next_regnum;
+ int next_regnum = 0;
/* The struct or union we are currently parsing, or last parsed. */
- tdesc_type_with_fields *current_type;
+ tdesc_type_with_fields *current_type = nullptr;
/* The byte size of the current struct/flags type, if specified. Zero
if not specified. Flags values must specify a size. */
- int current_type_size;
+ int current_type_size = 0;
+
+ /* Map used to implement id/idref attribute support for the <count>
+ element in a vector type. The fist member is the count id, and the
+ second is the DWARF expression it corresponds to. */
+ std::unordered_map<std::string, gdb::byte_vector> vector_count_locexprs;
+
+ /* Information needed to parse <vector> element. */
+ struct {
+ /* id attribute for the <vector> element being processed. */
+ std::string id;
+
+ /* Type of field in <vector> element being processed. */
+ struct tdesc_type *field_type = nullptr;
+
+ /* Holds number of elements, if <vector> has "count" attribute. */
+ std::optional<int> count;
+
+ /* Holds DWARF expression, if <vector> has a <math> child. */
+ gdb::byte_vector locexpr;
+
+ /* Holds string representation of opcodes in locexpr. Used to provide
+ more readable code when converting to C. Needs to have the same
+ number of elements as locexpr. */
+ std::optional<std::vector<const char *>> locexpr_str;
+
+ /* id attribute for the <count> element being processed. */
+ std::string count_id;
+
+ std::string count_idref;
+
+ std::optional<gdb_byte> apply_operator;
+
+ std::optional<const char *> apply_operator_str;
+ } current_vector;
+
+ /* gdbarch that roughly corresponds to the XML target description being
+ parsed. Used to gather basic facts about the architecture such as the
+ DWARF register numbering. */
+ gdbarch *preliminary_gdbarch = nullptr;
+
+ std::set<int> load_early_regnums;
};
/* Handle the end of an <architecture> element and its value. */
@@ -166,6 +207,36 @@ tdesc_start_feature (struct gdb_xml_parser *parser,
data->current_feature = tdesc_create_feature (data->tdesc, name);
}
+/* Handle the end of a <feature> element. */
+
+static void
+tdesc_end_feature (struct gdb_xml_parser *parser,
+ const struct gdb_xml_element *element, void *user_data,
+ const char *body_text)
+{
+ struct tdesc_parsing_data *data = (struct tdesc_parsing_data *) user_data;
+
+ for (int regnum : data->load_early_regnums)
+ {
+ std::vector<tdesc_reg_up> ®isters = data->current_feature->registers;
+ auto reg = std::find_if (registers.begin (), registers.end (),
+ [=] (tdesc_reg_up &i) {
+ if (i->target_regnum == regnum)
+ return true;
+ else
+ return false;
+ });
+ if (reg == registers.end ())
+ gdb_xml_error (parser,
+ _("Register number %d used in expression but not found in feature."),
+ regnum);
+
+ (*reg)->load_early = true;
+ }
+
+ data->load_early_regnums.clear ();
+}
+
/* Handle the start of a <reg> element. Fill in the optional
attributes and attach it to the containing feature. */
@@ -474,27 +545,287 @@ tdesc_start_vector (struct gdb_xml_parser *parser,
void *user_data, std::vector<gdb_xml_value> &attributes)
{
struct tdesc_parsing_data *data = (struct tdesc_parsing_data *) user_data;
- struct tdesc_type *field_type;
- char *id, *field_type_id;
- ULONGEST count;
+ char *field_type_id;
- id = (char *) attributes[0].value.get ();
+ data->current_vector.id = (char *) attributes[0].value.get ();
field_type_id = (char *) attributes[1].value.get ();
- count = * (ULONGEST *) attributes[2].value.get ();
- if (count > MAX_VECTOR_SIZE)
+ if (attributes.size () >= 3)
{
- gdb_xml_error (parser,
- _("Vector size %s is larger than maximum (%d)"),
- pulongest (count), MAX_VECTOR_SIZE);
+ ULONGEST count = *(ULONGEST *)attributes[2].value.get ();
+
+ if (count > MAX_VECTOR_SIZE)
+ gdb_xml_error (parser,
+ _ ("Vector \"%s\": size %s is larger than maximum (%d)"),
+ data->current_vector.id.c_str (), pulongest (count),
+ MAX_VECTOR_SIZE);
+
+ data->current_vector.count = (int) count;
}
- field_type = tdesc_named_type (data->current_feature, field_type_id);
- if (field_type == NULL)
+ data->current_vector.field_type = tdesc_named_type (data->current_feature,
+ field_type_id);
+ if (data->current_vector.field_type == nullptr)
gdb_xml_error (parser, _("Vector \"%s\" references undefined type \"%s\""),
- id, field_type_id);
+ data->current_vector.id.c_str (), field_type_id);
+}
+
+/* Handle the end of a <vector> element. */
+
+static void
+tdesc_end_vector (struct gdb_xml_parser *parser,
+ const struct gdb_xml_element *element, void *user_data,
+ const char *body_text)
+{
+ struct tdesc_parsing_data *data = (struct tdesc_parsing_data *) user_data;
+
+ if (data->current_vector.count.has_value ()
+ && !data->current_vector.locexpr.empty ())
+ gdb_xml_error (parser,
+ _ ("Vector \"%s\" has both a count attribute and a count expression"),
+ data->current_vector.id.c_str ());
+ else if (!data->current_vector.count.has_value ()
+ && data->current_vector.locexpr.empty ())
+ gdb_xml_error (parser,
+ _ ("Vector \"%s\" has neither a count attribute nor a count expression"),
+ data->current_vector.id.c_str ());
+
+ if (data->current_vector.count.has_value ())
+ tdesc_create_vector (data->current_feature,
+ data->current_vector.id.c_str (),
+ data->current_vector.field_type,
+ *data->current_vector.count);
+ else
+ {
+ tdesc_type *type = tdesc_create_vector (data->current_feature,
+ data->current_vector.id.c_str (),
+ data->current_vector.field_type,
+ data->current_vector.locexpr,
+ data->current_vector.locexpr_str);
+ tdesc_type_vector *type_vector = static_cast<tdesc_type_vector *> (type);
+
+ if (!data->current_vector.count_id.empty ())
+ type_vector->locexpr_id = data->current_vector.count_id;
+ else if (!data->current_vector.count_idref.empty ())
+ type_vector->locexpr_idref = data->current_vector.count_idref;
+
+ set_tdesc_include_dwarf_header (data->tdesc, true);
+ }
+
+ data->current_vector = {};
+}
+
+/* Handle the start of a <count> element. Store any given count ID and process
+ IDREFs. */
+
+static void
+tdesc_start_count (struct gdb_xml_parser *parser,
+ const struct gdb_xml_element *element,
+ void *user_data, std::vector<gdb_xml_value> &attributes)
+{
+ struct tdesc_parsing_data *data = (struct tdesc_parsing_data *) user_data;
+ const char *id = nullptr, *idref = nullptr;
+
+ for (gdb_xml_value &attr : attributes)
+ {
+ if (!strcmp (attr.name, "id"))
+ id = (const char *) attr.value.get ();
+ else if (!strcmp (attr.name, "idref"))
+ idref = (const char *) attr.value.get ();
+ }
+
+ if (id != nullptr && idref != nullptr)
+ gdb_xml_error (parser,
+ _ ("Vector \"%s\": count expression has both an id and an idref"),
+ data->current_vector.id.c_str ());
+
+ if (id != nullptr)
+ {
+ std::string current_count_id = id;
+
+ if (data->vector_count_locexprs.find (current_count_id)
+ != data->vector_count_locexprs.end ())
+ gdb_xml_error (parser,
+ _ ("Vector \"%s\": count expression has duplicate id \"%s\""),
+ data->current_vector.id.c_str (), id);
+
+ data->current_vector.count_id = std::move (current_count_id);
+ }
+ else if (idref != nullptr)
+ {
+ std::string current_count_idref = idref;
+
+ if (data->vector_count_locexprs.find (current_count_idref)
+ == data->vector_count_locexprs.end ())
+ gdb_xml_error (parser,
+ _ ("Vector \"%s\": count expression references unknown id \"%s\""),
+ data->current_vector.id.c_str (), idref);
- tdesc_create_vector (data->current_feature, id, field_type, count);
+ data->current_vector.count_idref = idref;
+ }
+}
+
+/* Handle the end of a <count> element. If the element has an id attribute, store the
+ location expression in the map. If it has an idref attribute, fetch the location
+ expression from the map. */
+
+static void
+tdesc_end_count (struct gdb_xml_parser *parser,
+ const struct gdb_xml_element *element, void *user_data,
+ const char *body_text)
+{
+ struct tdesc_parsing_data *data = (struct tdesc_parsing_data *) user_data;
+
+ if (!data->current_vector.count_id.empty ())
+ {
+ bool inserted
+ = data->vector_count_locexprs
+ .insert ({ data->current_vector.count_id,
+ data->current_vector.locexpr })
+ .second;
+
+ /* Insertion isn't supposed to fail: we already checked in the start
+ handler that this id isn't present in the map. */
+ gdb_assert (inserted);
+ }
+ else if (!data->current_vector.count_idref.empty ())
+ data->current_vector.locexpr
+ = data->vector_count_locexprs.at (data->current_vector.count_idref);
+}
+
+/* Handle the start of a <math> element. It just has to check whether it's inside
+ a <count> element with an idref and initialize data->current_vector.locexpr_str. */
+
+static void
+tdesc_start_math (struct gdb_xml_parser *parser,
+ const struct gdb_xml_element *element, void *user_data,
+ std::vector<gdb_xml_value> &attributes)
+{
+ struct tdesc_parsing_data *data = (struct tdesc_parsing_data *)user_data;
+
+ if (!data->current_vector.count_idref.empty ())
+ gdb_xml_error (parser,
+ _ ("Vector \"%s\": count element with an idref isn't empty."),
+ data->current_vector.id.c_str ());
+
+ data->current_vector.locexpr_str.emplace ();
+}
+
+/* Handle the end of an <apply> element. We just need to store the expression
+ operator at the end of the location expression. */
+
+static void
+tdesc_end_apply (struct gdb_xml_parser *parser,
+ const struct gdb_xml_element *element, void *user_data,
+ const char *body_text)
+{
+ struct tdesc_parsing_data *data = (struct tdesc_parsing_data *) user_data;
+
+ if (!data->current_vector.apply_operator.has_value ())
+ gdb_xml_error (parser,
+ _ ("Vector \"%s\": apply element doesn't have an operator."),
+ data->current_vector.id.c_str ());
+
+ data->current_vector.locexpr.push_back (*data->current_vector.apply_operator);
+ data->current_vector.locexpr_str->push_back (*data->current_vector.apply_operator_str);
+}
+
+/* Handle the end of a <ci> element and its value. */
+
+static void
+tdesc_end_ci (struct gdb_xml_parser *parser,
+ const struct gdb_xml_element *element, void *user_data,
+ const char *body_text)
+{
+ struct tdesc_parsing_data *data = (struct tdesc_parsing_data *) user_data;
+
+ if (data->preliminary_gdbarch == nullptr) {
+ gdbarch_info arch_info;
+ arch_info.bfd_arch_info = tdesc_architecture (data->tdesc);
+ arch_info.osabi = tdesc_osabi (data->tdesc);
+ data->preliminary_gdbarch = gdbarch_find_by_info (arch_info);
+
+ if (data->preliminary_gdbarch == nullptr)
+ gdb_xml_error (parser,
+ _("Vector \"%s\": couldn't find basic architecture information."),
+ data->current_vector.id.c_str ());
+
+ if (!gdbarch_regnum_to_dwarf2_reg_p (data->preliminary_gdbarch))
+ gdb_xml_error (parser,
+ _("Vector \"%s\": architecture %s doesn't support vector count based on another register."),
+ data->current_vector.id.c_str (),
+ gdbarch_bfd_arch_info (data->preliminary_gdbarch)->printable_name);
+ }
+
+ ULONGEST regnum = gdb_xml_parse_ulongest (parser, body_text);
+ if (regnum > INT_MAX)
+ gdb_xml_error (parser,
+ _("Vector \"%s\": count expression uses register number %s out of range."),
+ data->current_vector.id.c_str (), pulongest (regnum));
+
+ int dwarf_reg = gdbarch_regnum_to_dwarf2_reg (data->preliminary_gdbarch,
+ regnum);
+ if (dwarf_reg < 0 || dwarf_reg > 255)
+ gdb_xml_error (parser,
+ _("Vector \"%s\": count expression uses register number %s out of range."),
+ data->current_vector.id.c_str (), pulongest (dwarf_reg));
+
+ data->current_vector.locexpr.push_back (DW_OP_bregx);
+ data->current_vector.locexpr.push_back ((gdb_byte) dwarf_reg);
+ data->current_vector.locexpr.push_back (0);
+
+ data->current_vector.locexpr_str->push_back ("DW_OP_bregx");
+ data->current_vector.locexpr_str->push_back (nullptr);
+ data->current_vector.locexpr_str->push_back (nullptr);
+
+ data->load_early_regnums.insert (regnum);
+}
+
+/* Handle the end of a <cn> element and its value. */
+
+static void
+tdesc_end_cn (struct gdb_xml_parser *parser,
+ const struct gdb_xml_element *element, void *user_data,
+ const char *body_text)
+{
+ struct tdesc_parsing_data *data = (struct tdesc_parsing_data *) user_data;
+ ULONGEST number = gdb_xml_parse_ulongest (parser, body_text);
+
+ /* We only support DW_OP_lit0 to DW_OP_lit31. */
+ if (number > 31)
+ gdb_xml_error (parser,
+ _ ("Vector \"%s\": count expression uses value %s larger than maximum of 31."),
+ data->current_vector.id.c_str (), pulongest (number));
+
+ gdb_byte value = DW_OP_lit0 + number;
+ data->current_vector.locexpr.push_back (value);
+ data->current_vector.locexpr_str->push_back (get_DW_OP_name (value));
+}
+
+/* Handle the end of a <divide> element. */
+
+static void
+tdesc_end_divide (struct gdb_xml_parser *parser,
+ const struct gdb_xml_element *element, void *user_data,
+ const char *body_text)
+{
+ struct tdesc_parsing_data *data = (struct tdesc_parsing_data *) user_data;
+
+ data->current_vector.apply_operator = DW_OP_div;
+ data->current_vector.apply_operator_str = "DW_OP_div";
+}
+
+/* Handle the end of a <times> element. */
+
+static void
+tdesc_end_times (struct gdb_xml_parser *parser,
+ const struct gdb_xml_element *element, void *user_data,
+ const char *body_text)
+{
+ struct tdesc_parsing_data *data = (struct tdesc_parsing_data *) user_data;
+
+ data->current_vector.apply_operator = DW_OP_mul;
+ data->current_vector.apply_operator_str = "DW_OP_mul";
}
/* The elements and attributes of an XML target description. */
@@ -525,6 +856,43 @@ static const struct gdb_xml_element enum_children[] = {
{ NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
};
+/* MathML allows recursing <apply> elements, but we don't have a need for it
+ yet, so for simplicity we don't. */
+static const struct gdb_xml_element apply_children[] = {
+ { "ci", nullptr, nullptr, GDB_XML_EF_NONE, nullptr, tdesc_end_ci },
+ { "cn", nullptr, nullptr, GDB_XML_EF_OPTIONAL, nullptr, tdesc_end_cn },
+ { "divide", nullptr, nullptr, GDB_XML_EF_OPTIONAL, nullptr,
+ tdesc_end_divide },
+ { "times", nullptr, nullptr, GDB_XML_EF_OPTIONAL, nullptr, tdesc_end_times },
+ { nullptr, nullptr, nullptr, GDB_XML_EF_NONE, nullptr, nullptr }
+};
+
+static const struct gdb_xml_element math_children[] = {
+ { "apply", nullptr, apply_children, GDB_XML_EF_OPTIONAL, nullptr,
+ tdesc_end_apply },
+ { "ci", nullptr, nullptr, GDB_XML_EF_OPTIONAL, nullptr, tdesc_end_ci },
+ { "cn", nullptr, nullptr, GDB_XML_EF_OPTIONAL, nullptr, tdesc_end_cn },
+ { nullptr, nullptr, nullptr, GDB_XML_EF_NONE, nullptr, nullptr }
+};
+
+static const struct gdb_xml_element count_children[] = {
+ { "math", nullptr, math_children, GDB_XML_EF_OPTIONAL, tdesc_start_math,
+ nullptr },
+ { nullptr, nullptr, nullptr, GDB_XML_EF_NONE, nullptr, nullptr }
+};
+
+static const struct gdb_xml_attribute count_attributes[] = {
+ { "id", GDB_XML_AF_OPTIONAL, nullptr, nullptr },
+ { "idref", GDB_XML_AF_OPTIONAL, nullptr, nullptr },
+ { nullptr, GDB_XML_AF_NONE, nullptr, nullptr }
+};
+
+static const struct gdb_xml_element vector_children[] = {
+ { "count", count_attributes, count_children, GDB_XML_EF_OPTIONAL,
+ tdesc_start_count, tdesc_end_count },
+ { nullptr, nullptr, nullptr, GDB_XML_EF_NONE, nullptr, nullptr }
+};
+
static const struct gdb_xml_attribute reg_attributes[] = {
{ "name", GDB_XML_AF_NONE, NULL, NULL },
{ "bitsize", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
@@ -557,7 +925,7 @@ static const struct gdb_xml_attribute enum_attributes[] = {
static const struct gdb_xml_attribute vector_attributes[] = {
{ "id", GDB_XML_AF_NONE, NULL, NULL },
{ "type", GDB_XML_AF_NONE, NULL, NULL },
- { "count", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
+ { "count", GDB_XML_AF_OPTIONAL, gdb_xml_parse_attr_ulongest, NULL },
{ NULL, GDB_XML_AF_NONE, NULL, NULL }
};
@@ -582,9 +950,10 @@ static const struct gdb_xml_element feature_children[] = {
{ "enum", enum_attributes, enum_children,
GDB_XML_EF_OPTIONAL | GDB_XML_EF_REPEATABLE,
tdesc_start_enum, NULL },
- { "vector", vector_attributes, NULL,
- GDB_XML_EF_OPTIONAL | GDB_XML_EF_REPEATABLE,
- tdesc_start_vector, NULL },
+ { "vector", vector_attributes, vector_children,
+ GDB_XML_EF_OPTIONAL | GDB_XML_EF_REPEATABLE, tdesc_start_vector,
+ tdesc_end_vector },
+ { "architecture", NULL, NULL, GDB_XML_EF_OPTIONAL, NULL, tdesc_end_arch },
{ NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
};
@@ -602,7 +971,7 @@ static const struct gdb_xml_element target_children[] = {
NULL, tdesc_end_compatible },
{ "feature", feature_attributes, feature_children,
GDB_XML_EF_OPTIONAL | GDB_XML_EF_REPEATABLE,
- tdesc_start_feature, NULL },
+ tdesc_start_feature, tdesc_end_feature },
{ NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
};
@@ -636,7 +1005,6 @@ tdesc_parse_xml (const char *document, xml_fetch_another fetcher)
if (it != xml_cache.end ())
return it->second.get ();
- memset (&data, 0, sizeof (struct tdesc_parsing_data));
target_desc_up description = allocate_target_description ();
data.tdesc = description.get ();
diff --git a/gdbserver/Makefile.in b/gdbserver/Makefile.in
index 6148ccf9121b..3315a0c95f19 100644
--- a/gdbserver/Makefile.in
+++ b/gdbserver/Makefile.in
@@ -208,6 +208,7 @@ SFILES = \
$(srcdir)/linux-sparc-low.cc \
$(srcdir)/linux-x86-low.cc \
$(srcdir)/linux-xtensa-low.cc \
+ $(srcdir)/locexpr.cc \
$(srcdir)/mem-break.cc \
$(srcdir)/netbsd-aarch64-low.cc \
$(srcdir)/netbsd-amd64-low.cc \
@@ -262,6 +263,7 @@ OBS = \
dll.o \
hostio.o \
inferiors.o \
+ locexpr.o \
mem-break.o \
notif.o \
regcache.o \
diff --git a/gdbserver/config.in b/gdbserver/config.in
index 65f9ff6e6470..25aefbecae4f 100644
--- a/gdbserver/config.in
+++ b/gdbserver/config.in
@@ -104,6 +104,9 @@
/* Define to 1 if you have the <dlfcn.h> header file. */
#undef HAVE_DLFCN_H
+/* Define if the target provides DWARF register to regnum mapping. */
+#undef HAVE_DWARF_REG_TO_REGNUM
+
/* Define to 1 if the system has the type `Elf32_auxv_t'. */
#undef HAVE_ELF32_AUXV_T
diff --git a/gdbserver/configure b/gdbserver/configure
index 09cb3c5bf434..c310d522ef10 100755
--- a/gdbserver/configure
+++ b/gdbserver/configure
@@ -14743,6 +14743,12 @@ if $want_ipa ; then
fi
fi
+if test "${srv_dwarf_reg_to_regnum}" = "yes"; then
+
+$as_echo "#define HAVE_DWARF_REG_TO_REGNUM 1" >>confdefs.h
+
+fi
+
diff --git a/gdbserver/configure.ac b/gdbserver/configure.ac
index ee0de9decbde..35ec08da06e4 100644
--- a/gdbserver/configure.ac
+++ b/gdbserver/configure.ac
@@ -441,6 +441,11 @@ if $want_ipa ; then
fi
fi
+if test "${srv_dwarf_reg_to_regnum}" = "yes"; then
+ AC_DEFINE(HAVE_DWARF_REG_TO_REGNUM, 1,
+ [Define if the target provides DWARF register to regnum mapping.])
+fi
+
AC_SUBST(GDBSERVER_DEPFILES)
AC_SUBST(GDBSERVER_LIBS)
AC_SUBST(srv_xmlbuiltin)
diff --git a/gdbserver/locexpr.cc b/gdbserver/locexpr.cc
new file mode 100644
index 000000000000..25d614c3a2bb
--- /dev/null
+++ b/gdbserver/locexpr.cc
@@ -0,0 +1,107 @@
+/* Simplified DWARF location expression evaluator for gdbserver.
+ Copyright (C) 2024 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ 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/>. */
+
+#include "server.h"
+#include "leb128.h"
+#include "locexpr.h"
+#include "regcache.h"
+#include "arch/aarch64.h"
+
+#define DW_OP_lit0 0x30
+#define DW_OP_lit2 0x32
+#define DW_OP_lit4 0x34
+#define DW_OP_lit8 0x38
+#define DW_OP_bregx 0x92
+#define DW_OP_div 0x1b
+#define DW_OP_mul 0x1e
+
+/* See gdbserver/locexpr.h. */
+
+long
+evaluate_locexpr (const gdb::array_view<const gdb_byte> locexpr,
+ const struct regcache *regcache)
+{
+ std::vector<long> stack;
+
+ for (auto it = locexpr.begin (); it != locexpr.end ();)
+ {
+ gdb_byte op = *it;
+ long result;
+
+ it++;
+ switch (op)
+ {
+ case DW_OP_div:
+ {
+ long divisor = stack.back ();
+ stack.pop_back ();
+ long dividend = stack.back ();
+ stack.pop_back ();
+ result = dividend / divisor;
+ break;
+ }
+ case DW_OP_mul:
+ {
+ long multiplicand = stack.back ();
+ stack.pop_back ();
+ long multiplier = stack.back ();
+ stack.pop_back ();
+ result = multiplier * multiplicand;
+ break;
+ }
+ case DW_OP_lit0:
+ case DW_OP_lit2:
+ case DW_OP_lit4:
+ case DW_OP_lit8:
+ result = op - DW_OP_lit0;
+ break;
+ case DW_OP_bregx:
+ {
+ uint64_t reg;
+ int read = read_uleb128_to_uint64 (it, locexpr.end (), ®);
+ gdb_assert (read != 0);
+
+ it += read;
+
+ int64_t offset;
+ read = read_sleb128_to_int64 (it, locexpr.end (), &offset);
+ gdb_assert (read != 0);
+
+ it += read;
+
+ const char *arch = tdesc_architecture_name (regcache->tdesc);
+ reg = tdesc_dwarf_reg_to_regnum (arch, reg);
+ gdb_assert (reg != -1);
+
+ register_status reg_status = regcache->get_register_status (reg);
+ gdb_assert (reg_status == REG_VALID);
+ regcache->raw_collect (reg,
+ gdb::make_array_view ((gdb_byte *) &result,
+ sizeof (result)));
+ result += offset;
+ break;
+ }
+ default:
+ gdb_assert_not_reached ("Unsupported locexpr operation: %c\n", op);
+ }
+
+ stack.push_back (result);
+ }
+
+ return stack.back ();
+}
diff --git a/gdbserver/locexpr.h b/gdbserver/locexpr.h
new file mode 100644
index 000000000000..7b768058697d
--- /dev/null
+++ b/gdbserver/locexpr.h
@@ -0,0 +1,31 @@
+/* Simplified DWARF location expression evaluator for gdbserver.
+ Copyright (C) 2024 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ 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/>. */
+
+#ifndef GDBSERVER_LOCEXPR_H
+#define GDBSERVER_LOCEXPR_H
+
+#include "gdbsupport/array-view.h"
+#include "gdbsupport/common-types.h"
+#include "regcache.h"
+
+/* Evaluate the given DWARF LOCEXPR, using REGCACHE to get register values. */
+
+long evaluate_locexpr (const gdb::array_view<const gdb_byte> locexpr,
+ const struct regcache *regcache);
+
+#endif /* GDBSERVER_LOCEXPR_H */
diff --git a/gdbserver/tdesc.cc b/gdbserver/tdesc.cc
index 6f7ebb7c5c76..fbaf756f5050 100644
--- a/gdbserver/tdesc.cc
+++ b/gdbserver/tdesc.cc
@@ -129,6 +129,9 @@ copy_target_description (struct target_desc *dest,
dest->expedite_regs = src->expedite_regs;
dest->registers_size = src->registers_size;
dest->xmltarget = src->xmltarget;
+
+ if (src->arch)
+ set_tdesc_architecture (dest, src->arch.get ());
}
const struct target_desc *
@@ -242,3 +245,18 @@ tdesc_contains_feature (const target_desc *tdesc, const std::string &feature)
return false;
}
+
+#ifndef HAVE_DWARF_REG_TO_REGNUM
+
+/* See gdbsupport/tdesc.h.
+
+ This is a dummy implementation. It's not used if the target description
+ doesn't have variable-length registers. */
+
+int
+tdesc_dwarf_reg_to_regnum (const char *arch, int dwarf_reg)
+{
+ return -1;
+}
+
+#endif /* HAVE_DWARF_REG_TO_REGNUM */
diff --git a/gdbsupport/tdesc.cc b/gdbsupport/tdesc.cc
index a99119274f44..05b348fd8077 100644
--- a/gdbsupport/tdesc.cc
+++ b/gdbsupport/tdesc.cc
@@ -18,6 +18,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "gdbsupport/tdesc.h"
+#include "dwarf2.h"
tdesc_reg::tdesc_reg (struct tdesc_feature *feature, const std::string &name_,
int regnum, int save_restore_, const char *group_,
@@ -160,6 +161,21 @@ tdesc_create_vector (struct tdesc_feature *feature, const char *name,
/* See gdbsupport/tdesc.h. */
+struct tdesc_type *
+tdesc_create_vector (struct tdesc_feature *feature, const char *name,
+ struct tdesc_type *field_type,
+ const gdb::array_view<const gdb_byte> locexpr,
+ std::optional<std::vector<const char *>> locexpr_str)
+{
+ tdesc_type_vector *type = new tdesc_type_vector (name, field_type, locexpr,
+ locexpr_str);
+ feature->types.emplace_back (type);
+
+ return type;
+}
+
+/* See gdbsupport/tdesc.h. */
+
tdesc_type_with_fields *
tdesc_create_struct (struct tdesc_feature *feature, const char *name)
{
@@ -312,8 +328,113 @@ void print_xml_feature::visit (const tdesc_type_builtin *t)
void print_xml_feature::visit (const tdesc_type_vector *t)
{
- add_line ("<vector id=\"%s\" type=\"%s\" count=\"%d\"/>",
- t->name.c_str (), t->element_type->name.c_str (), t->count);
+ if (t->locexpr.empty ())
+ add_line ("<vector id=\"%s\" type=\"%s\" count=\"%d\"/>",
+ t->name.c_str (), t->element_type->name.c_str (), t->count);
+ else
+ {
+ add_line ("<vector id=\"%s\" type=\"%s\">",
+ t->name.c_str (), t->element_type->name.c_str ());
+
+ if (t->locexpr_idref.has_value ())
+ add_line (" <count idref=\"%s\"/>", t->locexpr_idref->c_str ());
+ else
+ {
+ if (t->locexpr_id.has_value ())
+ add_line (" <count id=\"%s\">", t->locexpr_id->c_str ());
+ else
+ add_line (" <count>");
+
+ add_line (" <math>");
+
+ std::vector<std::string> stack;
+
+ /* Convert DWARF location expression to MathML. */
+ for (int i = 0; i < t->locexpr.size (); i++)
+ {
+ switch (t->locexpr[i])
+ {
+ case DW_OP_bregx:
+ {
+ gdb_byte arg1 = t->locexpr[i + 1];
+ gdb_byte arg2 = t->locexpr[i + 2];
+ gdb_byte dwarf_reg;
+
+ if (arg1 == 0)
+ dwarf_reg = arg2;
+ else if (arg2 == 0)
+ dwarf_reg = arg1;
+ else
+ error (_("In a vector count locexpr one of the "
+ "operands of DW_OP_bregx must be 0."));
+
+ if (m_arch == nullptr)
+ error (_("Feature references a register in locexpr but "
+ "the architecture is unknown."));
+
+ int regnum = tdesc_dwarf_reg_to_regnum (m_arch, dwarf_reg);
+ if (regnum == -1)
+ error (_("Unknown DWARF register %d."), dwarf_reg);
+
+ stack.push_back (string_printf ("<ci>%d</ci>", regnum));
+
+ i += 2;
+ }
+ break;
+ case DW_OP_div:
+ {
+ std::string divisor = stack.back ();
+ stack.pop_back ();
+ std::string dividend = stack.back ();
+ stack.pop_back ();
+
+ stack.push_back ("</apply>");
+ stack.push_back (" " + divisor);
+ stack.push_back (" " + dividend);
+ stack.push_back (" <divide/>");
+ stack.push_back ("<apply>");
+ }
+ break;
+ case DW_OP_mul:
+ {
+ std::string multiplicand = stack.back ();
+ stack.pop_back ();
+ std::string multiplier = stack.back ();
+ stack.pop_back ();
+
+ stack.push_back ("</apply>");
+ stack.push_back (" " + multiplicand);
+ stack.push_back (" " + multiplier);
+ stack.push_back (" <times/>");
+ stack.push_back ("<apply>");
+ }
+ break;
+ default:
+ {
+ gdb_byte val = t->locexpr[i];
+
+ if (val > DW_OP_lit0 && val <= DW_OP_lit31)
+ stack.push_back (string_printf ("<cn>%d</cn>",
+ val - DW_OP_lit0));
+ else
+ error (_ ("Unsupported DWARF operator %d."), val);
+ }
+ break;
+ }
+ }
+
+ while (!stack.empty ())
+ {
+ add_line (" " + stack.back ());
+ stack.pop_back ();
+ }
+
+ add_line (" </math>");
+ add_line (" </count>");
+ }
+
+ add_line ("</vector>");
+ }
}
void print_xml_feature::visit (const tdesc_type_with_fields *t)
@@ -405,9 +526,12 @@ void print_xml_feature::visit_pre (const target_desc *e)
add_line ("<!DOCTYPE target SYSTEM \"gdb-target.dtd\">");
add_line ("<target>");
indent (1);
- if (tdesc_architecture_name (e))
- add_line ("<architecture>%s</architecture>",
- tdesc_architecture_name (e));
+ const char *arch = tdesc_architecture_name (e);
+ if (arch != nullptr)
+ {
+ add_line ("<architecture>%s</architecture>", arch);
+ m_arch = arch;
+ }
const char *osabi = tdesc_osabi_name (e);
if (osabi != nullptr)
diff --git a/gdbsupport/tdesc.h b/gdbsupport/tdesc.h
index 7e483486139b..5f20e6ab8627 100644
--- a/gdbsupport/tdesc.h
+++ b/gdbsupport/tdesc.h
@@ -64,6 +64,11 @@ class tdesc_element
virtual void accept (tdesc_element_visitor &v) const = 0;
};
+/* Used in vector type element count or register bitsize to indicate that
+ the corresponding value is given by a DWARF expression. */
+
+#define TDESC_REG_VARIABLE_SIZE -1
+
/* An individual register from a target description. */
struct tdesc_reg : tdesc_element
@@ -242,13 +247,40 @@ struct tdesc_type_vector : tdesc_type
element_type (element_type_), count (count_)
{}
+ tdesc_type_vector (const std::string &name, tdesc_type *element_type_,
+ const gdb::array_view<const gdb_byte> locexpr_,
+ std::optional<std::vector<const char *>> locexpr_str_)
+ : tdesc_type (name, TDESC_TYPE_VECTOR), element_type (element_type_),
+ count (TDESC_REG_VARIABLE_SIZE), locexpr (locexpr_.begin (),
+ locexpr_.end()),
+ locexpr_str (locexpr_str_)
+ {
+ if (locexpr_str.has_value () && locexpr.size () != locexpr_str->size ())
+ error (_ ("Vector locexpr and its string representation must have the same size."));
+ }
+
void accept (tdesc_element_visitor &v) const override
{
v.visit (this);
}
struct tdesc_type *element_type;
+
+ /* Ignored if LOCEXPR isn't empty. */
int count;
+
+ /* DWARF location expression providing number of elements. */
+ gdb::byte_vector locexpr;
+
+ /* Vector with strings to use instead of the corresponding raw bytecode
+ in C code that creates the vector type. */
+ std::optional<std::vector<const char *>> locexpr_str;
+
+ /* XML id used to reference this location expression. */
+ std::optional<std::string> locexpr_id;
+
+ /* XML id of location expression to be used for number of elements. */
+ std::optional<std::string> locexpr_idref;
};
/* A named type from a target description. */
@@ -368,6 +400,14 @@ struct tdesc_type *tdesc_create_vector (struct tdesc_feature *feature,
struct tdesc_type *field_type,
int count);
+/* Return the created vector tdesc_type named NAME in FEATURE,
+ with number of elements given by DWARF LOCEXPR. */
+struct tdesc_type * tdesc_create_vector (struct tdesc_feature *feature,
+ const char *name,
+ struct tdesc_type *field_type,
+ const gdb::array_view<const gdb_byte> locexpr,
+ std::optional<std::vector<const char *>> locexpr_str = {});
+
/* Return the created struct tdesc_type named NAME in FEATURE. */
tdesc_type_with_fields *tdesc_create_struct (struct tdesc_feature *feature,
const char *name);
@@ -469,6 +509,13 @@ class print_xml_feature : public tdesc_element_visitor
/* The current indentation depth. */
int m_depth;
+
+ /* The current feature's architecture, if any. */
+ const char *m_arch = nullptr;
};
+/* Convert ARCH's DWARF register number DWARF_REG to GDB's register number.
+ Returns -1 if ARCH or DWARF_REG are unknown. */
+int tdesc_dwarf_reg_to_regnum (const char *arch, int dwarf_reg);
+
#endif /* COMMON_TDESC_H */
^ permalink raw reply [flat|nested] 16+ messages in thread
* [RFC PATCH v4 10/15] GDB: Add concept of variable-size registers to the regcache
2024-11-02 2:56 [RFC PATCH v4 00/15] gdbserver improvements for AArch64 SVE support Thiago Jung Bauermann
` (8 preceding siblings ...)
2024-11-02 2:56 ` [RFC PATCH v4 09/15] GDB tdesc: Add vector type with number of elements given by math expression Thiago Jung Bauermann
@ 2024-11-02 2:56 ` Thiago Jung Bauermann
2024-11-02 2:56 ` [RFC PATCH v4 11/15] gdbserver: " Thiago Jung Bauermann
` (4 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Thiago Jung Bauermann @ 2024-11-02 2:56 UTC (permalink / raw)
To: gdb-patches
The contents of variable-size registers are placed in a separate buffer,
because their size will only be known a while after the buffer is
created but some fixed-size registers (particularly the program counter)
need to be stored or fetched before then. This is also why the state
related to variable-size registers is lazily initialised at the moment
it's first needed (by calling the initialize_variable_size_registers ()
method).
Simon suggested placing the variable-size registers at the end of the
existing contents buffer to avoid having to use a separate one. I will
experiment with that idea and see if it simplifies the code.
The regcache now also stores the resolved type and size of the
variable-size registers. Some places needed to be changed from calling
the register_size () function to calling the register_size () method
instead, because they may call them for variable-size registers. The
frame-unwinding code doesn't have a regcache readily available, so the
register_size () function is changed to optionally accept a
frame_info_ptr which it uses to create a readonly_detached_regcache.
When debugging a remote target, if the regcache has variable-size
registers the maximum packet size may change with new values of
expedited registers. Therefore, update the maximum packet size when
expedited registers are supplied, or load-early registers are fetched.
Finally, there are FIXMEs related to the const_casts needed to remove
the const from the "this" pointer when calling
reg_buffer::initialize_variable_size_registers (). I'm still thinking
about what to do with them. I tried the simple solution of changing the
calling methods to be non-const, but the change escalates quickly.
---
gdb/aarch64-tdep.c | 9 +-
gdb/eval.c | 13 ++-
gdb/findvar.c | 5 +-
gdb/frame.c | 45 +++++---
gdb/record-full.c | 3 +-
gdb/regcache.c | 269 +++++++++++++++++++++++++++++++++++++--------
gdb/regcache.h | 39 ++++++-
gdb/remote.c | 62 +++++++++--
gdb/value.c | 16 ++-
gdbsupport/tdesc.h | 4 +-
10 files changed, 377 insertions(+), 88 deletions(-)
diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c
index 8a2a9b1e23c1..04442f22ea3f 100644
--- a/gdb/aarch64-tdep.c
+++ b/gdb/aarch64-tdep.c
@@ -1728,7 +1728,7 @@ pass_in_v (struct gdbarch *gdbarch,
{
int regnum = AARCH64_V0_REGNUM + info->nsrn;
/* Enough space for a full vector register. */
- gdb::byte_vector reg (register_size (gdbarch, regnum), 0);
+ gdb::byte_vector reg (regcache->register_size (regnum), 0);
gdb_assert (len <= reg.size ());
info->argnum++;
@@ -2543,7 +2543,7 @@ aarch64_extract_return_value (struct type *type, struct regcache *regs,
{
int regno = AARCH64_V0_REGNUM + i;
/* Enough space for a full vector register. */
- gdb::byte_vector buf (register_size (gdbarch, regno));
+ gdb::byte_vector buf (regs->register_size (regno));
gdb_assert (len <= buf.size ());
aarch64_debug_printf
@@ -2657,7 +2657,7 @@ aarch64_store_return_value (struct type *type, struct regcache *regs,
{
int regno = AARCH64_V0_REGNUM + i;
/* Enough space for a full vector register. */
- gdb::byte_vector tmpbuf (register_size (gdbarch, regno));
+ gdb::byte_vector tmpbuf (regs->register_size (regno));
gdb_assert (len <= tmpbuf.size ());
aarch64_debug_printf
@@ -3308,7 +3308,8 @@ aarch64_pseudo_write_1 (gdbarch *gdbarch, const frame_info_ptr &next_frame,
various 'scalar' pseudo registers to behavior like architectural
writes, register width bytes are written the remainder are set to
zero. */
- gdb::byte_vector raw_buf (register_size (gdbarch, raw_regnum), 0);
+ int raw_reg_size = register_size (gdbarch, raw_regnum, &next_frame);
+ gdb::byte_vector raw_buf (raw_reg_size, 0);
static_assert (AARCH64_V0_REGNUM == AARCH64_SVE_Z0_REGNUM);
gdb::array_view<gdb_byte> raw_view (raw_buf);
diff --git a/gdb/eval.c b/gdb/eval.c
index 457a4362289d..9f20ec557db3 100644
--- a/gdb/eval.c
+++ b/gdb/eval.c
@@ -1151,13 +1151,14 @@ eval_op_register (struct type *expect_type, struct expression *exp,
if (regno == -1)
error (_("Register $%s not available."), name);
- /* In EVAL_AVOID_SIDE_EFFECTS mode, we only need to return
- a value with the appropriate register type. Unfortunately,
- we don't have easy access to the type of user registers.
- So for these registers, we fetch the register value regardless
- of the evaluation mode. */
+ /* In EVAL_AVOID_SIDE_EFFECTS mode, we only need to return a value with
+ the appropriate register type. Unfortunately, we don't have easy
+ access to the type of user registers and variable-size registers. So
+ for these registers, we fetch the register value regardless of the
+ evaluation mode. */
if (noside == EVAL_AVOID_SIDE_EFFECTS
- && regno < gdbarch_num_cooked_regs (exp->gdbarch))
+ && regno < gdbarch_num_cooked_regs (exp->gdbarch)
+ && !register_is_variable_size (exp->gdbarch, regno))
val = value::zero (register_type (exp->gdbarch, regno), not_lval);
else
val = value_of_register
diff --git a/gdb/findvar.c b/gdb/findvar.c
index d65bf2fc278c..1ecfd3558383 100644
--- a/gdb/findvar.c
+++ b/gdb/findvar.c
@@ -562,11 +562,12 @@ read_frame_register_value (value *value)
LONGEST reg_offset = value->offset ();
int regnum = value->regnum ();
int len = type_length_units (check_typedef (value->type ()));
+ int reg_size = register_size (gdbarch, regnum, &next_frame);
/* Skip registers wholly inside of REG_OFFSET. */
- while (reg_offset >= register_size (gdbarch, regnum))
+ while (reg_offset >= reg_size)
{
- reg_offset -= register_size (gdbarch, regnum);
+ reg_offset -= reg_size;
regnum++;
}
diff --git a/gdb/frame.c b/gdb/frame.c
index 663ff7fa773b..0f480ce75b09 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -1322,12 +1322,24 @@ frame_unwind_register_value (const frame_info_ptr &next_frame, int regnum)
gdb_printf (&debug_file, " lazy");
else if (value->entirely_available ())
{
- int i;
+ int i, reg_size;
gdb::array_view<const gdb_byte> buf = value->contents ();
gdb_printf (&debug_file, " bytes=");
gdb_printf (&debug_file, "[");
- for (i = 0; i < register_size (gdbarch, regnum); i++)
+
+ if (register_is_variable_size (gdbarch, regnum))
+ /* To get the size of a variable-size register, we need to
+ call frame_save_as_regcache () so that we can call
+ regcache::register_size (). Unfortunatlly the former
+ ends up calling this function so we enter into an
+ infinite recursion. So just assume that the value has
+ the correct size. */
+ reg_size = buf.size ();
+ else
+ reg_size = register_size (gdbarch, regnum);
+
+ for (i = 0; i < reg_size; i++)
gdb_printf (&debug_file, "%02x", buf[i]);
gdb_printf (&debug_file, "]");
}
@@ -1446,7 +1458,7 @@ put_frame_register (const frame_info_ptr &next_frame, int regnum,
int unavail;
enum lval_type lval;
CORE_ADDR addr;
- int size = register_size (gdbarch, regnum);
+ int size = register_size (gdbarch, regnum, &next_frame);
gdb_assert (buf.size () == size);
@@ -1462,8 +1474,9 @@ put_frame_register (const frame_info_ptr &next_frame, int regnum,
break;
}
case lval_register:
- /* Not sure if that's always true... but we have a problem if not. */
- gdb_assert (size == register_size (gdbarch, realnum));
+ if (regnum != realnum)
+ /* Not sure if that's always true... but we have a problem if not. */
+ gdb_assert (size == register_size (gdbarch, realnum));
if (realnum < gdbarch_num_regs (gdbarch)
|| !gdbarch_pseudo_register_write_p (gdbarch))
@@ -1509,9 +1522,9 @@ get_frame_register_bytes (const frame_info_ptr &next_frame, int regnum,
gdbarch *gdbarch = frame_unwind_arch (next_frame);
/* Skip registers wholly inside of OFFSET. */
- while (offset >= register_size (gdbarch, regnum))
+ while (offset >= register_size (gdbarch, regnum, &next_frame))
{
- offset -= register_size (gdbarch, regnum);
+ offset -= register_size (gdbarch, regnum, &next_frame);
regnum++;
}
@@ -1521,7 +1534,7 @@ get_frame_register_bytes (const frame_info_ptr &next_frame, int regnum,
int numregs = gdbarch_num_cooked_regs (gdbarch);
for (int i = regnum; i < numregs; i++)
{
- int thissize = register_size (gdbarch, i);
+ int thissize = register_size (gdbarch, i, &next_frame);
if (thissize == 0)
break; /* This register is not available on this architecture. */
@@ -1535,10 +1548,10 @@ get_frame_register_bytes (const frame_info_ptr &next_frame, int regnum,
/* Copy the data. */
while (!buffer.empty ())
{
- int curr_len = std::min<int> (register_size (gdbarch, regnum) - offset,
- buffer.size ());
+ int reg_size = register_size (gdbarch, regnum, &next_frame);
+ int curr_len = std::min<int> (reg_size - offset, buffer.size ());
- if (curr_len == register_size (gdbarch, regnum))
+ if (curr_len == reg_size)
{
enum lval_type lval;
CORE_ADDR addr;
@@ -1586,19 +1599,19 @@ put_frame_register_bytes (const frame_info_ptr &next_frame, int regnum,
gdbarch *gdbarch = frame_unwind_arch (next_frame);
/* Skip registers wholly inside of OFFSET. */
- while (offset >= register_size (gdbarch, regnum))
+ while (offset >= register_size (gdbarch, regnum, &next_frame))
{
- offset -= register_size (gdbarch, regnum);
+ offset -= register_size (gdbarch, regnum, &next_frame);
regnum++;
}
/* Copy the data. */
while (!buffer.empty ())
{
- int curr_len = std::min<int> (register_size (gdbarch, regnum) - offset,
- buffer.size ());
+ int reg_size = register_size (gdbarch, regnum, &next_frame);
+ int curr_len = std::min<int> (reg_size - offset, buffer.size ());
- if (curr_len == register_size (gdbarch, regnum))
+ if (curr_len == reg_size)
put_frame_register (next_frame, regnum, buffer.slice (0, curr_len));
else
{
diff --git a/gdb/record-full.c b/gdb/record-full.c
index 8e4915abd083..0ce370ae32bf 100644
--- a/gdb/record-full.c
+++ b/gdb/record-full.c
@@ -399,12 +399,11 @@ static inline struct record_full_entry *
record_full_reg_alloc (struct regcache *regcache, int regnum)
{
struct record_full_entry *rec;
- struct gdbarch *gdbarch = regcache->arch ();
rec = XCNEW (struct record_full_entry);
rec->type = record_full_reg;
rec->u.reg.num = regnum;
- rec->u.reg.len = register_size (gdbarch, regnum);
+ rec->u.reg.len = regcache->register_size (regnum);
if (rec->u.reg.len > sizeof (rec->u.reg.u.buf))
rec->u.reg.u.ptr = (gdb_byte *) xmalloc (rec->u.reg.len);
diff --git a/gdb/regcache.c b/gdb/regcache.c
index 61289d46f4b1..dfcfffb02aa0 100644
--- a/gdb/regcache.c
+++ b/gdb/regcache.c
@@ -51,7 +51,7 @@ struct regcache_descr
redundant information - if the PC is constructed from two
registers then those registers and not the PC lives in the raw
cache. */
- long sizeof_raw_registers = 0;
+ long sizeof_fixed_size_raw_registers = 0;
/* The cooked register space. Each cooked register in the range
[0..NR_RAW_REGISTERS) is direct-mapped onto the corresponding raw
@@ -60,7 +60,7 @@ struct regcache_descr
both raw registers and memory by the architecture methods
gdbarch_pseudo_register_read and gdbarch_pseudo_register_write. */
int nr_cooked_registers = 0;
- long sizeof_cooked_registers = 0;
+ long sizeof_fixed_size_cooked_registers = 0;
/* Offset and size (in 8 bit bytes), of each register in the
register cache. All registers (including those in the range
@@ -69,9 +69,16 @@ struct regcache_descr
long *register_offset = nullptr;
long *sizeof_register = nullptr;
- /* Registers that need to be loaded early in the register cache. */
+ /* Registers that need to be loaded early in the register cache, because
+ variable-size registers depend on them to calculate their size. */
std::set<int> load_early_regs;
+ /* Vector indicating whether a given register is variable-size. */
+ bool *register_is_variable_size = nullptr;
+
+ /* Does the regcache contains any variable-size register? */
+ bool has_variable_size_registers = false;
+
/* Cached table containing the type of each register. */
struct type **register_type = nullptr;
};
@@ -119,26 +126,52 @@ init_regcache_descr (struct gdbarch *gdbarch)
= GDBARCH_OBSTACK_CALLOC (gdbarch, descr->nr_cooked_registers, long);
descr->register_offset
= GDBARCH_OBSTACK_CALLOC (gdbarch, descr->nr_cooked_registers, long);
+ descr->register_is_variable_size
+ = GDBARCH_OBSTACK_CALLOC (gdbarch, descr->nr_cooked_registers, bool);
+
for (i = 0; i < gdbarch_num_regs (gdbarch); i++)
{
- descr->sizeof_register[i] = descr->register_type[i]->length ();
- descr->register_offset[i] = offset;
- offset += descr->sizeof_register[i];
+ descr->register_is_variable_size[i]
+ = is_dynamic_type (descr->register_type[i]);
+ if (descr->register_is_variable_size[i])
+ {
+ descr->sizeof_register[i] = -1;
+ descr->register_offset[i] = -1;
+ descr->has_variable_size_registers = true;
+ }
+ else
+ {
+ descr->sizeof_register[i] = descr->register_type[i]->length ();
+ descr->register_offset[i] = offset;
+ offset += descr->sizeof_register[i];
+ }
if (tdesc_register_is_early_load (gdbarch, i))
descr->load_early_regs.insert (i);
}
+
/* Set the real size of the raw register cache buffer. */
- descr->sizeof_raw_registers = offset;
+ descr->sizeof_fixed_size_raw_registers = offset;
for (; i < descr->nr_cooked_registers; i++)
{
- descr->sizeof_register[i] = descr->register_type[i]->length ();
- descr->register_offset[i] = offset;
- offset += descr->sizeof_register[i];
+ descr->register_is_variable_size[i]
+ = is_dynamic_type (descr->register_type[i]);
+ if (descr->register_is_variable_size[i])
+ {
+ descr->sizeof_register[i] = -1;
+ descr->register_offset[i] = -1;
+ descr->has_variable_size_registers = true;
+ }
+ else
+ {
+ descr->sizeof_register[i] = descr->register_type[i]->length ();
+ descr->register_offset[i] = offset;
+ offset += descr->sizeof_register[i];
+ }
}
/* Set the real size of the readonly register cache buffer. */
- descr->sizeof_cooked_registers = offset;
+ descr->sizeof_fixed_size_cooked_registers = offset;
}
return descr;
@@ -160,27 +193,60 @@ regcache_descr (struct gdbarch *gdbarch)
/* Utility functions returning useful register attributes stored in
the regcache descr. */
+/* See gdb/regcache.h. */
+
struct type *
register_type (struct gdbarch *gdbarch, int regnum)
{
struct regcache_descr *descr = regcache_descr (gdbarch);
gdb_assert (regnum >= 0 && regnum < descr->nr_cooked_registers);
- return descr->register_type[regnum];
+
+ struct type *type = descr->register_type[regnum];
+
+ if (descr->register_is_variable_size[regnum])
+ {
+ frame_info_ptr current_frame = get_current_frame ();
+ type = resolve_dynamic_type (type, {}, 0, ¤t_frame);
+ }
+
+ return type;
}
-/* Utility functions returning useful register attributes stored in
- the regcache descr. */
+/* See gdb/regcache.h. */
int
-register_size (struct gdbarch *gdbarch, int regnum)
+register_size (struct gdbarch *gdbarch, int regnum,
+ const frame_info_ptr *next_frame)
{
struct regcache_descr *descr = regcache_descr (gdbarch);
- int size;
- gdb_assert (regnum >= 0 && regnum < gdbarch_num_cooked_regs (gdbarch));
- size = descr->sizeof_register[regnum];
- return size;
+ gdb_assert (regnum >= 0);
+ gdb_assert (regnum < descr->nr_cooked_registers);
+
+ if (descr->register_is_variable_size[regnum])
+ {
+ gdb_assert (next_frame != nullptr);
+
+ std::unique_ptr<readonly_detached_regcache> regcache =
+ frame_save_as_regcache (get_prev_frame (*next_frame));
+ return regcache->register_size (regnum);
+ }
+
+ return descr->sizeof_register[regnum];
+}
+
+/* See gdb/regcache.h. */
+
+bool
+register_is_variable_size (struct gdbarch *gdbarch, int regnum)
+{
+ struct regcache_descr *descr = regcache_descr (gdbarch);
+
+ gdb_assert (regnum >= 0);
+ gdb_assert (regnum < descr->nr_cooked_registers);
+
+ return descr->register_is_variable_size[regnum];
}
/* See gdbsupport/common-regcache.h. */
@@ -188,7 +254,47 @@ register_size (struct gdbarch *gdbarch, int regnum)
int
reg_buffer::register_size (int regnum) const
{
- return ::register_size (this->arch (), regnum);
+ gdb_assert (regnum >= 0);
+ gdb_assert (regnum < m_descr->nr_cooked_registers);
+
+ int size;
+
+ if (m_descr->register_is_variable_size[regnum])
+ {
+ if (!m_variable_size_registers)
+ const_cast<reg_buffer *> (this)
+ ->initialize_variable_size_registers (); // FIXME: Remove cast.
+
+ size = m_variable_size_register_sizeof[regnum];
+ }
+ else
+ size = m_descr->sizeof_register[regnum];
+
+ return size;
+}
+
+/* See gdb/regcache.h. */
+
+struct type *
+reg_buffer::register_type (int regnum) const
+{
+ gdb_assert (regnum >= 0);
+ gdb_assert (regnum < m_descr->nr_cooked_registers);
+
+ struct type *type;
+
+ if (m_descr->register_is_variable_size[regnum])
+ {
+ if (!m_variable_size_registers)
+ const_cast<reg_buffer *> (this)
+ ->initialize_variable_size_registers (); // FIXME: Remove cast.
+
+ type = m_variable_size_register_type[regnum];
+ }
+ else
+ type = m_descr->register_type[regnum];
+
+ return type;
}
reg_buffer::reg_buffer (gdbarch *gdbarch, bool has_pseudo)
@@ -202,13 +308,13 @@ reg_buffer::reg_buffer (gdbarch *gdbarch, bool has_pseudo)
REG_VALID. */
if (has_pseudo)
{
- m_registers.reset (new gdb_byte[m_descr->sizeof_cooked_registers]);
+ m_fixed_size_registers.reset (new gdb_byte[m_descr->sizeof_fixed_size_cooked_registers]);
m_register_status.reset
(new register_status[m_descr->nr_cooked_registers] ());
}
else
{
- m_registers.reset (new gdb_byte[m_descr->sizeof_raw_registers]);
+ m_fixed_size_registers.reset (new gdb_byte[m_descr->sizeof_fixed_size_raw_registers]);
m_register_status.reset
(new register_status[gdbarch_num_regs (gdbarch)] ());
}
@@ -237,9 +343,6 @@ reg_buffer::arch () const
return m_descr->gdbarch;
}
-/* Utility functions returning useful register attributes stored in
- the regcache descr. */
-
/* See regcache.h. */
bool
@@ -281,6 +384,55 @@ reg_buffer::load_early_registers ()
return m_descr->load_early_regs;
}
+/* See regcache.h. */
+
+void
+reg_buffer::initialize_variable_size_registers ()
+{
+ unsigned long total_size = 0;
+
+ /* We need load early registers to resolve types of variable-size
+ registers. */
+ if (!this->fetch_load_early_registers ())
+ error (_("Missing register contents for variable-size registers."));
+
+ m_variable_size_register_type.resize (m_descr->nr_cooked_registers);
+ m_variable_size_register_sizeof.resize (m_descr->nr_cooked_registers);
+ m_variable_size_register_offset.resize (m_descr->nr_cooked_registers);
+
+ for (unsigned int i = 0; i < m_descr->nr_cooked_registers; i++)
+ {
+ if (!m_descr->register_is_variable_size[i])
+ {
+ m_variable_size_register_type[i] = nullptr;
+ m_variable_size_register_sizeof[i] = -1;
+ m_variable_size_register_offset[i] = -1;
+ continue;
+ }
+
+ m_variable_size_register_type[i]
+ = resolve_dynamic_type (m_descr->register_type[i], {},
+ /* Unused address. */ 0, nullptr, this);
+
+ ULONGEST size = m_variable_size_register_type[i]->length ();
+ gdb_assert (size != 0);
+ m_variable_size_register_sizeof[i] = size;
+ m_variable_size_register_offset[i] = total_size;
+ m_register_status[i] = REG_UNKNOWN;
+ total_size += size;
+ }
+
+ m_variable_size_registers.reset (new gdb_byte[total_size]);
+}
+
+/* See regcache.h. */
+
+bool
+reg_buffer::has_variable_size_registers ()
+{
+ return m_descr->has_variable_size_registers;
+}
+
/* Helper for reg_buffer::register_buffer. */
template<typename ElemType>
@@ -288,8 +440,19 @@ gdb::array_view<ElemType>
reg_buffer::register_buffer (int regnum) const
{
assert_regnum (regnum);
- ElemType *start = &m_registers[m_descr->register_offset[regnum]];
- int size = m_descr->sizeof_register[regnum];
+ ElemType *start;
+
+ if (m_descr->register_is_variable_size[regnum])
+ {
+ if (!m_variable_size_registers)
+ const_cast<reg_buffer *>(this)->initialize_variable_size_registers (); // FIXME: Remove cast.
+
+ start = &m_variable_size_registers[m_variable_size_register_offset[regnum]];
+ }
+ else
+ start = &m_fixed_size_registers[m_descr->register_offset[regnum]];
+
+ int size = register_size (regnum);
return gdb::array_view<ElemType> (start, size);
}
@@ -317,9 +480,14 @@ reg_buffer::save (register_read_ftype cooked_read)
/* It should have pseudo registers. */
gdb_assert (m_has_pseudo);
/* Clear the dest. */
- memset (m_registers.get (), 0, m_descr->sizeof_cooked_registers);
+ memset (m_fixed_size_registers.get (), 0, m_descr->sizeof_fixed_size_cooked_registers);
memset (m_register_status.get (), REG_UNKNOWN, m_descr->nr_cooked_registers);
+ /* For data related to variable-size registers, we only need to reset
+ their buffer at this point. Other data will be resized/re-set by
+ initialize_variable_size_registers (). */
+ m_variable_size_registers.reset ();
+
/* Save load early registers first. */
for (int regnum : m_descr->load_early_regs)
{
@@ -379,6 +547,12 @@ regcache::restore (readonly_detached_regcache *src)
continue;
cooked_write (regnum, src->register_buffer (regnum));
+
+ if (m_variable_size_registers)
+ /* For data related to variable-size registers, we only need to reset
+ their buffer at this point. Other data will be resized/re-set by
+ initialize_variable_size_registers (). */
+ m_variable_size_registers.reset ();
}
/* Copy over any registers, being careful to only restore those that
@@ -709,7 +883,7 @@ register_status
readable_regcache::raw_read (int regnum, gdb::array_view<gdb_byte> dst)
{
assert_regnum (regnum);
- gdb_assert (dst.size () == m_descr->sizeof_register[regnum]);
+ gdb_assert (dst.size () == register_size (regnum));
raw_update (regnum);
@@ -725,7 +899,7 @@ register_status
readable_regcache::raw_read (int regnum, gdb_byte *dst)
{
assert_regnum (regnum);
- int size = m_descr->sizeof_register[regnum];
+ int size = register_size (regnum);
return raw_read (regnum, gdb::make_array_view (dst, size));
}
@@ -741,7 +915,7 @@ enum register_status
readable_regcache::raw_read (int regnum, T *val)
{
assert_regnum (regnum);
- size_t size = m_descr->sizeof_register[regnum];
+ size_t size = register_size (regnum);
gdb_byte *buf = (gdb_byte *) alloca (size);
auto view = gdb::make_array_view (buf, size);
register_status status = raw_read (regnum, view);
@@ -776,7 +950,7 @@ regcache::raw_write (int regnum, T val)
{
assert_regnum (regnum);
- int size = m_descr->sizeof_register[regnum];
+ int size = register_size (regnum);
gdb_byte *buf = (gdb_byte *) alloca (size);
auto view = gdb::make_array_view (buf, size);
store_integer (view, gdbarch_byte_order (m_descr->gdbarch), val);
@@ -815,7 +989,7 @@ readable_regcache::cooked_read (int regnum, gdb::array_view<gdb_byte> dst)
if (regnum < num_raw_registers ())
return raw_read (regnum, dst);
- gdb_assert (dst.size () == m_descr->sizeof_register[regnum]);
+ gdb_assert (dst.size () == register_size (regnum));
if (m_has_pseudo && m_register_status[regnum] != REG_UNKNOWN)
{
@@ -857,7 +1031,7 @@ readable_regcache::cooked_read (int regnum, gdb_byte *dst)
gdb_assert (regnum >= 0);
gdb_assert (regnum < m_descr->nr_cooked_registers);
- int size = m_descr->sizeof_register[regnum];
+ int size = register_size (regnum);
return cooked_read (regnum, gdb::make_array_view (dst, size));
}
@@ -871,8 +1045,12 @@ readable_regcache::cooked_read_value (int regnum)
|| (m_has_pseudo && m_register_status[regnum] != REG_UNKNOWN)
|| !gdbarch_pseudo_register_read_value_p (m_descr->gdbarch))
{
+ /* Provide the register type if its size is fixed to avoid infinite
+ recursion in the case of variable-size registers. */
+ struct type *type = (m_descr->register_is_variable_size[regnum] ?
+ nullptr : m_descr->register_type[regnum]);
value *result = value::allocate_register
- (get_next_frame_sentinel_okay (get_current_frame ()), regnum);
+ (get_next_frame_sentinel_okay (get_current_frame ()), regnum, type);
/* It is more efficient in general to do this delegation in this
direction than in the other one, even though the value-based
@@ -902,7 +1080,7 @@ enum register_status
readable_regcache::cooked_read (int regnum, T *val)
{
gdb_assert (regnum >= 0 && regnum < m_descr->nr_cooked_registers);
- size_t size = m_descr->sizeof_register[regnum];
+ size_t size = register_size (regnum);
gdb_byte *buf = (gdb_byte *) alloca (size);
auto view = gdb::make_array_view (buf, size);
register_status status = cooked_read (regnum, view);
@@ -936,7 +1114,7 @@ regcache::cooked_write (int regnum, T val)
gdb_assert (regnum >= 0);
gdb_assert (regnum < m_descr->nr_cooked_registers);
- int size = m_descr->sizeof_register[regnum];
+ int size = register_size (regnum);
gdb_byte *buf = (gdb_byte *) alloca (size);
auto view = gdb::make_array_view (buf, size);
store_integer (view, gdbarch_byte_order (m_descr->gdbarch), val);
@@ -955,7 +1133,7 @@ void
regcache::raw_write (int regnum, gdb::array_view<const gdb_byte> src)
{
assert_regnum (regnum);
- gdb_assert (src.size () == m_descr->sizeof_register[regnum]);
+ gdb_assert (src.size () == register_size (regnum));
/* On the sparc, writing %g0 is a no-op, so we don't even want to
change the registers array if something writes to this register. */
@@ -992,7 +1170,7 @@ regcache::raw_write (int regnum, const gdb_byte *src)
{
assert_regnum (regnum);
- int size = m_descr->sizeof_register[regnum];
+ int size = register_size (regnum);
raw_write (regnum, gdb::make_array_view (src, size));
}
@@ -1023,7 +1201,7 @@ regcache::cooked_write (int regnum, const gdb_byte *src)
gdb_assert (regnum >= 0);
gdb_assert (regnum < m_descr->nr_cooked_registers);
- int size = m_descr->sizeof_register[regnum];
+ int size = register_size (regnum);
return cooked_write (regnum, gdb::make_array_view (src, size));
}
@@ -1235,6 +1413,11 @@ reg_buffer::raw_supply (int regnum, gdb::array_view<const gdb_byte> src)
{
copy (src, dst);
m_register_status[regnum] = REG_VALID;
+
+ if (this->is_load_early_register (regnum)
+ && memcmp (src.data (), dst.data (), dst.size ()))
+ /* Invalidate variable-size registers. */
+ this->initialize_variable_size_registers ();
}
else
{
@@ -1253,7 +1436,7 @@ reg_buffer::raw_supply (int regnum, const void *src)
{
assert_regnum (regnum);
- int size = m_descr->sizeof_register[regnum];
+ int size = register_size (regnum);
raw_supply (regnum, gdb::make_array_view ((const gdb_byte *) src, size));
}
@@ -1297,7 +1480,7 @@ reg_buffer::raw_collect (int regnum, void *dst) const
{
assert_regnum (regnum);
- int size = m_descr->sizeof_register[regnum];
+ int size = register_size (regnum);
return raw_collect (regnum, gdb::make_array_view ((gdb_byte *) dst, size));
}
@@ -1372,7 +1555,7 @@ regcache::transfer_regset (const struct regset *regset, int regbase,
regno += regbase;
if (slot_size == 0 && regno != REGCACHE_MAP_SKIP)
- slot_size = m_descr->sizeof_register[regno];
+ slot_size = register_size (regnum);
if (regno == REGCACHE_MAP_SKIP
|| (regnum != -1
diff --git a/gdb/regcache.h b/gdb/regcache.h
index 5c28fec4af11..6b6687b9f349 100644
--- a/gdb/regcache.h
+++ b/gdb/regcache.h
@@ -165,10 +165,15 @@ extern bool regcache_map_supplies (const struct regcache_map_entry *map,
extern struct type *register_type (struct gdbarch *gdbarch, int regnum);
-/* Return the size of register REGNUM. All registers should have only
- one size. */
+/* Return the size of register REGNUM. FRAME is needed in case regnum is a
+ variable-size register. */
-extern int register_size (struct gdbarch *gdbarch, int regnum);
+extern int register_size (struct gdbarch *gdbarch, int regnum,
+ const frame_info_ptr *next_frame = nullptr);
+
+/* Return whether REGNUM is a variable-size register. */
+
+extern bool register_is_variable_size (struct gdbarch *gdbarch, int regnum);
using register_read_ftype
= gdb::function_view<register_status (int, gdb::array_view<gdb_byte>)>;
@@ -266,6 +271,12 @@ class reg_buffer : public reg_buffer_common
/* Return set of regnums which need to be loaded before other registers. */
const std::set<int> &load_early_registers ();
+ /* Whether any register in this regcache has a dynamic type. */
+ bool has_variable_size_registers ();
+
+ /* Return type of register REGNUM. */
+ struct type *register_type (int regnum) const;
+
/* See gdbsupport/common-regcache.h. */
int register_size (int regnum) const override;
@@ -279,6 +290,10 @@ class reg_buffer : public reg_buffer_common
Return false if they aren't. */
virtual bool fetch_load_early_registers ();
+ /* Initialize (or reset) information about variable-size registers in this
+ reg_buffer. */
+ void initialize_variable_size_registers ();
+
/* Return a view on register REGNUM's buffer cache. */
template <typename ElemType>
gdb::array_view<ElemType> register_buffer (int regnum) const;
@@ -293,11 +308,25 @@ class reg_buffer : public reg_buffer_common
struct regcache_descr *m_descr;
bool m_has_pseudo;
- /* The register buffers. */
- std::unique_ptr<gdb_byte[]> m_registers;
+
+ /* The fixed-size register buffers. */
+ std::unique_ptr<gdb_byte[]> m_fixed_size_registers;
+
+ /* The variable-size register buffers (if any). */
+ std::unique_ptr<gdb_byte[]> m_variable_size_registers;
+
/* Register cache status. */
std::unique_ptr<register_status[]> m_register_status;
+ /* The resolved types for variable-size registers (if any). */
+ std::vector<struct type *> m_variable_size_register_type;
+
+ /* The size of resolved types for variable-size registers (if any). */
+ std::vector<long> m_variable_size_register_sizeof;
+
+ /* The offset of resolved types for variable-size registers (if any). */
+ std::vector<long> m_variable_size_register_offset;
+
friend class regcache;
friend class detached_regcache;
};
diff --git a/gdb/remote.c b/gdb/remote.c
index 0217c05bce52..0930977d4304 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -471,6 +471,8 @@ struct packet_reg
at present. */
};
+struct remote_state;
+
struct remote_arch_state
{
explicit remote_arch_state (struct gdbarch *gdbarch);
@@ -493,6 +495,10 @@ struct remote_arch_state
/* This is the maximum size (in chars) of a non read/write packet.
It is also used as a cap on the size of read/write packets. */
long remote_packet_size;
+
+ /* Make sure the maximum packet size reflects current size of variable-size
+ registers. */
+ void update_packet_size (const struct regcache *regcache, remote_state *rs);
};
/* Description of the remote protocol state for the currently
@@ -1897,7 +1903,8 @@ show_remote_exec_file (struct ui_file *file, int from_tty,
}
static int
-map_regcache_remote_table (struct gdbarch *gdbarch, struct packet_reg *regs)
+map_regcache_remote_table (struct gdbarch *gdbarch, struct packet_reg *regs,
+ const struct regcache *regcache = nullptr)
{
int regnum, num_remote_regs, offset;
struct packet_reg **remote_regs;
@@ -1905,8 +1912,15 @@ map_regcache_remote_table (struct gdbarch *gdbarch, struct packet_reg *regs)
for (regnum = 0; regnum < gdbarch_num_regs (gdbarch); regnum++)
{
struct packet_reg *r = ®s[regnum];
+ bool skip_register;
- if (register_size (gdbarch, regnum) == 0)
+ if (regcache == nullptr)
+ skip_register = (register_is_variable_size (gdbarch, regnum)
+ || register_size (gdbarch, regnum) == 0);
+ else
+ skip_register = regcache->register_size (regnum) == 0;
+
+ if (skip_register)
/* Do not try to fetch zero-sized (placeholder) registers. */
r->pnum = -1;
else
@@ -1934,7 +1948,10 @@ map_regcache_remote_table (struct gdbarch *gdbarch, struct packet_reg *regs)
{
remote_regs[regnum]->in_g_packet = 1;
remote_regs[regnum]->offset = offset;
- offset += register_size (gdbarch, remote_regs[regnum]->regnum);
+ if (regcache == nullptr)
+ offset += register_size (gdbarch, remote_regs[regnum]->regnum);
+ else
+ offset += regcache->register_size (remote_regs[regnum]->regnum);
}
return offset;
@@ -1995,6 +2012,29 @@ remote_arch_state::remote_arch_state (struct gdbarch *gdbarch)
this->remote_packet_size = (this->sizeof_g_packet * 2 + 32);
}
+/* See remote_arch_state class declaration. */
+
+void
+remote_arch_state::update_packet_size (const struct regcache *regcache,
+ remote_state *rs)
+{
+ /* Record the maximum possible size of the g packet - it may turn out
+ to be smaller. */
+ this->sizeof_g_packet
+ = map_regcache_remote_table (regcache->arch (), this->regs.get (),
+ regcache);
+
+ this->remote_packet_size = 400 - 1;
+
+ if (this->sizeof_g_packet > (this->remote_packet_size - 32) / 2)
+ this->remote_packet_size = this->sizeof_g_packet * 2 + 32;
+
+ /* Make sure that the packet buffer is plenty big enough for
+ this architecture. */
+ if (rs->buf.size () < this->remote_packet_size)
+ rs->buf.resize (2 * this->remote_packet_size);
+}
+
/* Get a pointer to the current remote target. If not connected to a
remote target, return NULL. */
@@ -8553,6 +8593,10 @@ remote_target::supply_expedited_regs (struct regcache *regcache,
regcache->raw_supply (reg.num, reg.data);
rs->last_seen_expedited_registers.insert (reg.num);
}
+
+ if (regcache->has_variable_size_registers ())
+ get_remote_state ()->get_remote_arch_state (regcache->arch ())
+ ->update_packet_size (regcache, &m_remote_state);
}
/* Called when it is decided that STOP_REPLY holds the info of the
@@ -8979,6 +9023,9 @@ remote_target::fetch_early_registers (struct regcache *regcache)
if (res == 0)
error (_("Could not load early register %d using p packet."), regnum);
}
+
+ get_remote_state ()->get_remote_arch_state (regcache->arch ())
+ ->update_packet_size (regcache, &m_remote_state);
}
/* Fetch the registers included in the target's 'g' packet. */
@@ -9054,7 +9101,7 @@ remote_target::process_g_packet (struct regcache *regcache)
for (i = 0; i < gdbarch_num_regs (gdbarch); i++)
{
long offset = rsa->regs[i].offset;
- long reg_size = register_size (gdbarch, i);
+ long reg_size = regcache->register_size (i);
if (rsa->regs[i].pnum == -1)
continue;
@@ -9102,7 +9149,7 @@ remote_target::process_g_packet (struct regcache *regcache)
for (i = 0; i < gdbarch_num_regs (gdbarch); i++)
{
struct packet_reg *r = &rsa->regs[i];
- long reg_size = register_size (gdbarch, i);
+ long reg_size = regcache->register_size (i);
if (r->in_g_packet)
{
@@ -9240,7 +9287,8 @@ remote_target::store_register_using_P (const struct regcache *regcache,
struct remote_state *rs = get_remote_state ();
/* Try storing a single register. */
char *buf = rs->buf.data ();
- gdb_byte *regp = (gdb_byte *) alloca (register_size (gdbarch, reg->regnum));
+ int reg_size = regcache->register_size (reg->regnum);
+ gdb_byte *regp = (gdb_byte *) alloca (reg_size);
char *p;
if (m_features.packet_support (PACKET_P) == PACKET_DISABLE)
@@ -9252,7 +9300,7 @@ remote_target::store_register_using_P (const struct regcache *regcache,
xsnprintf (buf, get_remote_packet_size (), "P%s=", phex_nz (reg->pnum, 0));
p = buf + strlen (buf);
regcache->raw_collect (reg->regnum, regp);
- bin2hex (regp, p, register_size (gdbarch, reg->regnum));
+ bin2hex (regp, p, reg_size);
putpkt (rs->buf);
getpkt (&rs->buf);
diff --git a/gdb/value.c b/gdb/value.c
index d9b3c6ece04c..2b88279bd845 100644
--- a/gdb/value.c
+++ b/gdb/value.c
@@ -4071,12 +4071,24 @@ value::fetch_lazy_register ()
if (new_val->entirely_available ())
{
- int i;
+ int i, reg_size;
gdb::array_view<const gdb_byte> buf = new_val->contents ();
gdb_printf (&debug_file, " bytes=");
gdb_printf (&debug_file, "[");
- for (i = 0; i < register_size (gdbarch, regnum); i++)
+
+ if (register_is_variable_size (gdbarch, regnum))
+ /* To get the size of a variable-size register, we need to
+ call frame_save_as_regcache () so that we can call
+ regcache::register_size (). Unfortunatlly the former
+ ends up calling this function so we enter into an
+ infinite recursion. So just assume that the value has
+ the correct size. */
+ reg_size = buf.size ();
+ else
+ reg_size = register_size (gdbarch, regnum);
+
+ for (i = 0; i < reg_size; i++)
gdb_printf (&debug_file, "%02x", buf[i]);
gdb_printf (&debug_file, "]");
}
diff --git a/gdbsupport/tdesc.h b/gdbsupport/tdesc.h
index 5f20e6ab8627..74c5ff4deaf1 100644
--- a/gdbsupport/tdesc.h
+++ b/gdbsupport/tdesc.h
@@ -115,7 +115,9 @@ struct tdesc_reg : tdesc_element
/* The target-described type corresponding to TYPE, if found. */
struct tdesc_type *tdesc_type;
- /* Whether this register needs to be loaded early in GDB's regcache.
+ /* Whether this register needs to be loaded early in GDB's regcache,
+ because it is used to evaluate the DWARF expression giving the size of
+ another register.
In addition, if true gdbserver will send it as an expedited register
in stop replies. */
^ permalink raw reply [flat|nested] 16+ messages in thread
* [RFC PATCH v4 11/15] gdbserver: Add concept of variable-size registers to the regcache
2024-11-02 2:56 [RFC PATCH v4 00/15] gdbserver improvements for AArch64 SVE support Thiago Jung Bauermann
` (9 preceding siblings ...)
2024-11-02 2:56 ` [RFC PATCH v4 10/15] GDB: Add concept of variable-size registers to the regcache Thiago Jung Bauermann
@ 2024-11-02 2:56 ` Thiago Jung Bauermann
2024-11-02 2:56 ` [RFC PATCH v4 12/15] GDB: aarch64-linux: Load and store VG separately from other SVE registers Thiago Jung Bauermann
` (3 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Thiago Jung Bauermann @ 2024-11-02 2:56 UTC (permalink / raw)
To: gdb-patches
This is the gdbserver equivalent of the previous patch, and many of the
same remarks also apply:
1. The contents of variable-size registers are placed in a separate buffer.
2. Some places needed to be changed from calling he register_size ()
function to calling the register_size () method instead.
3. There are FIXMEs related to the const_casts needed to remove the const
from the "this" pointer when calling initialize_variable_size_registers ().
Probably the main thing to note in this patch is that it adds a puny DWARF
location expression parser which supports only the subset of opcodes
needed for variable-size registers.
Finally, this patch doesn't add support for handling variable-size
registers in tracepoints. This causes some FAILs in
gdb.base/internal-string-values.exp when it uses tracepoints.
---
gdb/regformats/regdef.h | 19 ++++-
gdbserver/ax.cc | 2 +-
gdbserver/regcache.cc | 162 ++++++++++++++++++++++++++++++++------
gdbserver/regcache.h | 18 ++++-
gdbserver/remote-utils.cc | 2 +-
gdbserver/tdesc.cc | 80 ++++++++++++++++++-
gdbserver/tdesc.h | 4 +-
gdbserver/tracepoint.cc | 4 +-
8 files changed, 253 insertions(+), 38 deletions(-)
diff --git a/gdb/regformats/regdef.h b/gdb/regformats/regdef.h
index 09339d495209..fcf7b177f04e 100644
--- a/gdb/regformats/regdef.h
+++ b/gdb/regformats/regdef.h
@@ -27,6 +27,8 @@ struct reg
: name (""),
offset (_offset),
size (0),
+ element_bitsize (0),
+ count_locexpr (nullptr),
load_early (false)
{}
@@ -34,6 +36,8 @@ struct reg
: name (_name),
offset (_offset),
size (_size),
+ element_bitsize (0),
+ count_locexpr (nullptr),
load_early (_load_early)
{}
@@ -48,9 +52,20 @@ struct reg
/* The offset (in bits) of the value of this register in the buffer. */
int offset;
- /* The size (in bits) of the value of this register, as transmitted. */
+ /* The size (in bits) of the value of this register, as transmitted. If
+ it's TDESC_REG_VARIABLE_SIZE then this register contains a vector with
+ variable number of elements given by COUNT_LOCEXPR, and each element
+ has a size of ELEMENT_BITSIZE. */
int size;
+ /* Size of each element in the vector. Only valid if
+ size == TDESC_REG_VARIABLE_SIZE. */
+ int element_bitsize;
+
+ /* DWARF location expression for number of elements in the vector. Only
+ valid if size == TDESC_REG_VARIABLE_SIZE. */
+ gdb::byte_vector *count_locexpr;
+
/* Whether this register needs to be loaded early in the register cache,
because variable-size registers depend on it to calculate their
size. */
@@ -61,6 +76,8 @@ struct reg
return (strcmp (name, other.name) == 0
&& offset == other.offset
&& size == other.size
+ && element_bitsize == other.element_bitsize
+ && count_locexpr == other.count_locexpr
&& load_early == other.load_early);
}
diff --git a/gdbserver/ax.cc b/gdbserver/ax.cc
index ff42795582ff..121ea6b420a3 100644
--- a/gdbserver/ax.cc
+++ b/gdbserver/ax.cc
@@ -1191,7 +1191,7 @@ gdb_eval_agent_expr (struct eval_agent_expr_context *ctx,
int regnum = arg;
struct regcache *regcache = ctx->regcache;
- switch (register_size (regcache->tdesc, regnum))
+ switch (regcache->register_size (regnum))
{
case 8:
collect_register (regcache, regnum, cnv.u64.bytes);
diff --git a/gdbserver/regcache.cc b/gdbserver/regcache.cc
index cd1ee2e5f145..8d491590d995 100644
--- a/gdbserver/regcache.cc
+++ b/gdbserver/regcache.cc
@@ -19,6 +19,7 @@
#include "regdef.h"
#include "gdbthread.h"
#include "tdesc.h"
+#include "locexpr.h"
#include "gdbsupport/rsp-low.h"
#include "gdbsupport/gdb-checked-static-cast.h"
@@ -56,6 +57,7 @@ get_thread_regcache (struct thread_info *thread, int fetch)
/* Invalidate all registers, to prevent stale left-overs. */
memset (regcache->register_status, REG_UNAVAILABLE,
regcache->tdesc->reg_defs.size ());
+ regcache->variable_size_registers.reset ();
fetch_inferior_registers (regcache, -1);
regcache->registers_valid = 1;
}
@@ -127,13 +129,14 @@ init_register_cache (struct regcache *regcache,
fetches. This way they'll read as zero instead of
garbage. */
regcache->tdesc = tdesc;
- regcache->registers
- = (unsigned char *) xcalloc (1, tdesc->registers_size);
+ regcache->fixed_size_registers
+ = (unsigned char *) xcalloc (1, tdesc->fixed_registers_size);
regcache->registers_owned = 1;
regcache->register_status
= (unsigned char *) xmalloc (tdesc->reg_defs.size ());
memset ((void *) regcache->register_status, REG_UNAVAILABLE,
tdesc->reg_defs.size ());
+ regcache->variable_size_registers.reset ();
#else
gdb_assert_not_reached ("can't allocate memory from the heap");
#endif
@@ -141,10 +144,11 @@ init_register_cache (struct regcache *regcache,
else
{
regcache->tdesc = tdesc;
- regcache->registers = regbuf;
+ regcache->fixed_size_registers = regbuf;
regcache->registers_owned = 0;
#ifndef IN_PROCESS_AGENT
regcache->register_status = NULL;
+ regcache->variable_size_registers.reset ();
#endif
}
@@ -160,7 +164,7 @@ new_register_cache (const struct target_desc *tdesc)
{
struct regcache *regcache = new struct regcache;
- gdb_assert (tdesc->registers_size != 0);
+ gdb_assert (tdesc->fixed_registers_size != 0);
return init_register_cache (regcache, tdesc, NULL);
}
@@ -171,7 +175,7 @@ free_register_cache (struct regcache *regcache)
if (regcache)
{
if (regcache->registers_owned)
- free (regcache->registers);
+ free (regcache->fixed_size_registers);
free (regcache->register_status);
delete regcache;
}
@@ -186,7 +190,7 @@ regcache_cpy (struct regcache *dst, struct regcache *src)
gdb_assert (src->tdesc == dst->tdesc);
gdb_assert (src != dst);
- memcpy (dst->registers, src->registers, src->tdesc->registers_size);
+ memcpy (dst->fixed_size_registers, src->fixed_size_registers, src->tdesc->fixed_registers_size);
#ifndef IN_PROCESS_AGENT
if (dst->register_status != NULL && src->register_status != NULL)
memcpy (dst->register_status, src->register_status,
@@ -255,22 +259,34 @@ register_from_string (struct regcache *regcache, int regnum, char *buf)
void
registers_to_string (struct regcache *regcache, char *buf)
{
- unsigned char *registers = regcache->registers;
+ unsigned char *registers = regcache->fixed_size_registers;
+ unsigned char *var_size_registers = regcache->variable_size_registers.get ();
const struct target_desc *tdesc = regcache->tdesc;
for (int i = 0; i < tdesc->reg_defs.size (); ++i)
{
+ int reg_size = regcache->register_size (i);
if (regcache->register_status[i] == REG_VALID)
{
- bin2hex (registers, buf, register_size (tdesc, i));
- buf += register_size (tdesc, i) * 2;
+ unsigned char *regs;
+ if (tdesc->reg_defs[i].size == TDESC_REG_VARIABLE_SIZE)
+ regs = var_size_registers;
+ else
+ regs = registers;
+
+ bin2hex (regs, buf, reg_size);
+ buf += reg_size * 2;
}
else
{
- memset (buf, 'x', register_size (tdesc, i) * 2);
- buf += register_size (tdesc, i) * 2;
+ memset (buf, 'x', reg_size * 2);
+ buf += reg_size * 2;
}
- registers += register_size (tdesc, i);
+
+ if (tdesc->reg_defs[i].size == TDESC_REG_VARIABLE_SIZE)
+ var_size_registers += reg_size;
+ else
+ registers += reg_size;
}
*buf = '\0';
}
@@ -279,17 +295,32 @@ void
registers_from_string (struct regcache *regcache, char *buf)
{
int len = strlen (buf);
- unsigned char *registers = regcache->registers;
+ unsigned char *registers = regcache->fixed_size_registers;
+ unsigned char *var_size_registers = regcache->variable_size_registers.get ();
const struct target_desc *tdesc = regcache->tdesc;
+ int expected_len = (tdesc->fixed_registers_size + regcache->variable_registers_size) * 2;
- if (len != tdesc->registers_size * 2)
+ if (len != expected_len)
{
warning ("Wrong sized register packet (expected %d bytes, got %d)",
- 2 * tdesc->registers_size, len);
- if (len > tdesc->registers_size * 2)
- len = tdesc->registers_size * 2;
+ expected_len, len);
+ if (len > expected_len)
+ len = expected_len;
+ }
+
+ for (int i = 0; i < tdesc->reg_defs.size (); ++i)
+ {
+ int reg_size = regcache->register_size (i);
+ unsigned char *regs;
+
+ if (tdesc->reg_defs[i].size == TDESC_REG_VARIABLE_SIZE)
+ regs = var_size_registers;
+ else
+ regs = registers;
+
+ hex2bin (buf, regs, reg_size);
+ buf += reg_size * 2;
}
- hex2bin (buf, registers, len / 2);
}
/* See regcache.h */
@@ -337,16 +368,56 @@ regcache_release (void)
}
#endif
+#ifndef IN_PROCESS_AGENT
+/* See regcache.h */
+
+void
+regcache::initialize_variable_size_registers ()
+{
+ const unsigned int num_regs = tdesc->reg_defs.size ();
+ int total_size = 0;
+
+ variable_size_sizes.resize (num_regs);
+ variable_size_offsets.resize (num_regs);
+
+ for (int i = 0; i < num_regs; i++)
+ {
+ const gdb::reg ® = tdesc->reg_defs[i];
+ if (reg.size != TDESC_REG_VARIABLE_SIZE)
+ {
+ variable_size_sizes[i] = -1;
+ variable_size_offsets[i] = -1;
+ continue;
+ }
+
+ long size = (evaluate_locexpr (*reg.count_locexpr, this)
+ * reg.element_bitsize / 8);
+ variable_size_sizes[i] = size;
+ variable_size_offsets[i] = total_size;
+ total_size += size;
+ }
+
+ variable_size_registers.reset (new unsigned char[total_size]);
+ variable_registers_size = total_size;
+}
+#endif
+
int
register_cache_size (const struct target_desc *tdesc)
{
- return tdesc->registers_size;
+ /* Variable-size registers aren't considered here because this function is
+ only used for tracepoints. */
+ return tdesc->fixed_registers_size;
}
int
register_size (const struct target_desc *tdesc, int n)
{
- return find_register_by_number (tdesc, n).size / 8;
+ const gdb::reg ® = find_register_by_number (tdesc, n);
+
+ gdb_assert (reg.size != TDESC_REG_VARIABLE_SIZE);
+
+ return reg.size / 8;
}
/* See gdbsupport/common-regcache.h. */
@@ -354,15 +425,46 @@ register_size (const struct target_desc *tdesc, int n)
int
regcache::register_size (int regnum) const
{
- return ::register_size (tdesc, regnum);
+ const gdb::reg ® = find_register_by_number (tdesc, regnum);
+
+ if (reg.size == TDESC_REG_VARIABLE_SIZE)
+ {
+#ifndef IN_PROCESS_AGENT
+ if (!variable_size_registers)
+ // FIXME: Remove cast.
+ const_cast<struct regcache *> (this)->initialize_variable_size_registers ();
+
+ return variable_size_sizes[regnum];
+#else
+ gdb_assert_not_reached ("Variable-size registers not supported.");
+#endif
+ }
+ else
+ return reg.size / 8;
}
static gdb::array_view<gdb_byte>
register_data (const struct regcache *regcache, int n)
{
const gdb::reg ® = find_register_by_number (regcache->tdesc, n);
- return gdb::make_array_view (regcache->registers + reg.offset / 8,
- reg.size / 8);
+
+ if (reg.size == TDESC_REG_VARIABLE_SIZE)
+ {
+#ifndef IN_PROCESS_AGENT
+ if (!regcache->variable_size_registers)
+ // FIXME: Remove cast.
+ const_cast<struct regcache *> (regcache)->initialize_variable_size_registers ();
+
+ return gdb::make_array_view (regcache->variable_size_registers.get ()
+ + regcache->variable_size_offsets[n],
+ regcache->variable_size_sizes[n]);
+#else
+ gdb_assert_not_reached ("Variable-size registers not supported.");
+#endif
+ }
+ else
+ return gdb::make_array_view (regcache->fixed_size_registers + reg.offset / 8,
+ reg.size / 8);
}
void
@@ -396,6 +498,12 @@ regcache::raw_supply (int n, gdb::array_view<const gdb_byte> src)
register_status[n] = REG_UNAVAILABLE;
#endif
}
+
+#ifndef IN_PROCESS_AGENT
+ if (this->is_load_early_register (n))
+ /* Invalidate variable-size registers. */
+ this->initialize_variable_size_registers ();
+#endif
}
/* Supply register N with value zero to REGCACHE. */
@@ -435,7 +543,7 @@ supply_regblock (struct regcache *regcache, const void *buf)
{
const struct target_desc *tdesc = regcache->tdesc;
- memcpy (regcache->registers, buf, tdesc->registers_size);
+ memcpy (regcache->fixed_size_registers, buf, tdesc->fixed_registers_size);
#ifndef IN_PROCESS_AGENT
{
int i;
@@ -449,7 +557,7 @@ supply_regblock (struct regcache *regcache, const void *buf)
{
const struct target_desc *tdesc = regcache->tdesc;
- memset (regcache->registers, 0, tdesc->registers_size);
+ memset (regcache->fixed_size_registers, 0, tdesc->fixed_registers_size);
#ifndef IN_PROCESS_AGENT
{
int i;
@@ -457,6 +565,8 @@ supply_regblock (struct regcache *regcache, const void *buf)
for (i = 0; i < tdesc->reg_defs.size (); i++)
regcache->register_status[i] = REG_UNAVAILABLE;
}
+
+ regcache->variable_size_registers.reset ();
#endif
}
}
@@ -498,7 +608,7 @@ regcache_raw_read_unsigned (reg_buffer_common *reg_buf, int regnum,
gdb_assert (regcache != NULL);
- size = register_size (regcache->tdesc, regnum);
+ size = regcache->register_size (regnum);
if (size > (int) sizeof (ULONGEST))
error (_("That operation is not available on integers of more than"
diff --git a/gdbserver/regcache.h b/gdbserver/regcache.h
index 07e48a7432b7..e1202d708a6a 100644
--- a/gdbserver/regcache.h
+++ b/gdbserver/regcache.h
@@ -40,10 +40,26 @@ struct regcache : public reg_buffer_common
in a traceframe. For that, check REGISTER_STATUS below. */
int registers_valid = 0;
int registers_owned = 0;
- unsigned char *registers = nullptr;
+ unsigned char *fixed_size_registers = nullptr;
#ifndef IN_PROCESS_AGENT
+ /* The variable-size register buffers (if any). */
+ std::unique_ptr<unsigned char[]> variable_size_registers;
+
+ /* Size of the variable_size_registers buffer, if there is one. */
+ int variable_registers_size;
+
/* One of REG_UNAVAILABLE or REG_VALID. */
unsigned char *register_status = nullptr;
+
+ /* Offsets into variable_size_registers. */
+ std::vector<long> variable_size_offsets;
+
+ /* Size of each variable-size register. */
+ std::vector<long> variable_size_sizes;
+
+ /* Initialize (or reset) information about variable-size registers in this
+ regcache. */
+ void initialize_variable_size_registers ();
#endif
/* See gdbsupport/common-regcache.h. */
diff --git a/gdbserver/remote-utils.cc b/gdbserver/remote-utils.cc
index 98c34e91220c..b4e4b6145eb4 100644
--- a/gdbserver/remote-utils.cc
+++ b/gdbserver/remote-utils.cc
@@ -1043,7 +1043,7 @@ outreg (struct regcache *regcache, int regno, char *buf)
*buf++ = tohex (regno & 0xf);
*buf++ = ':';
collect_register_as_string (regcache, regno, buf);
- buf += 2 * register_size (regcache->tdesc, regno);
+ buf += 2 * regcache->register_size (regno);
*buf++ = ';';
return buf;
diff --git a/gdbserver/tdesc.cc b/gdbserver/tdesc.cc
index fbaf756f5050..bf5a5c6e79a4 100644
--- a/gdbserver/tdesc.cc
+++ b/gdbserver/tdesc.cc
@@ -51,6 +51,69 @@ void target_desc::accept (tdesc_element_visitor &v) const
#endif
}
+/* Return bit size of given TYPE. */
+
+static int
+tdesc_type_bitsize (const tdesc_type *type)
+{
+ switch (type->kind) {
+ case TDESC_TYPE_INT8:
+ case TDESC_TYPE_UINT8:
+ return 8;
+ case TDESC_TYPE_IEEE_HALF:
+ case TDESC_TYPE_INT16:
+ case TDESC_TYPE_UINT16:
+ case TDESC_TYPE_BFLOAT16:
+ return 16;
+ case TDESC_TYPE_IEEE_SINGLE:
+ case TDESC_TYPE_INT32:
+ case TDESC_TYPE_UINT32:
+ return 32;
+ case TDESC_TYPE_IEEE_DOUBLE:
+ case TDESC_TYPE_INT64:
+ case TDESC_TYPE_UINT64:
+ return 64;
+ case TDESC_TYPE_I387_EXT:
+ return 80;
+ case TDESC_TYPE_ARM_FPA_EXT:
+ return 96;
+ case TDESC_TYPE_INT128:
+ case TDESC_TYPE_UINT128:
+ return 128;
+ default:
+ /* The other types require a gdbarch to determine their size. */
+ error ("Target description uses unsupported type in variable-size register.");
+ }
+}
+
+/* Sets up REG size information using TYPE. */
+
+static void
+setup_variable_size_reg (tdesc_type *type, gdb::reg ®)
+{
+ /* We only support vector types or unions containing vector types as
+ variable-size. */
+ gdb_assert (type->kind == TDESC_TYPE_UNION
+ || type->kind == TDESC_TYPE_VECTOR);
+
+ if (type->kind == TDESC_TYPE_VECTOR)
+ {
+ tdesc_type_vector *vec_type = static_cast<tdesc_type_vector *> (type);
+
+ reg.element_bitsize = tdesc_type_bitsize (vec_type->element_type);
+ reg.count_locexpr = &vec_type->locexpr;
+ }
+ else
+ {
+ tdesc_type_with_fields *union_type
+ = static_cast<tdesc_type_with_fields *> (type);
+
+ /* We assume that all fields in the union have the same size, so
+ just get the first one. */
+ setup_variable_size_reg (union_type->fields.front ().type, reg);
+ }
+}
+
void
init_target_desc (struct target_desc *tdesc,
const char **expedite_regs)
@@ -73,17 +136,26 @@ init_target_desc (struct target_desc *tdesc,
tdesc->reg_defs.emplace_back (treg->name.c_str (), offset,
treg->bitsize, treg->load_early);
- offset += treg->bitsize;
+
+ if (treg->bitsize == TDESC_REG_VARIABLE_SIZE)
+ {
+ tdesc_type *type
+ = tdesc_named_type (feature.get (), treg->type.c_str ());
+
+ setup_variable_size_reg (type, tdesc->reg_defs.back ());
+ }
+ else
+ offset += treg->bitsize;
if (treg->load_early)
expedite_from_features.push_back (treg->name.c_str ());
}
- tdesc->registers_size = offset / 8;
+ tdesc->fixed_registers_size = offset / 8;
/* Make sure PBUFSIZ is large enough to hold a full register
packet. */
- gdb_assert (2 * tdesc->registers_size + 32 <= PBUFSIZ);
+ gdb_assert (2 * tdesc->fixed_registers_size + 32 <= PBUFSIZ);
#ifndef IN_PROCESS_AGENT
/* Drop the contents of the previous vector, if any. */
@@ -127,7 +199,7 @@ copy_target_description (struct target_desc *dest,
{
dest->reg_defs = src->reg_defs;
dest->expedite_regs = src->expedite_regs;
- dest->registers_size = src->registers_size;
+ dest->fixed_registers_size = src->fixed_registers_size;
dest->xmltarget = src->xmltarget;
if (src->arch)
diff --git a/gdbserver/tdesc.h b/gdbserver/tdesc.h
index 4796b50b4d13..b7a0166c6e65 100644
--- a/gdbserver/tdesc.h
+++ b/gdbserver/tdesc.h
@@ -34,7 +34,7 @@ struct target_desc final : tdesc_element
std::vector<struct gdb::reg> reg_defs;
/* The register cache size, in bytes. */
- int registers_size;
+ int fixed_registers_size;
/* XML features in this target description. */
std::vector<tdesc_feature_up> features;
@@ -61,7 +61,7 @@ struct target_desc final : tdesc_element
public:
target_desc ()
- : registers_size (0)
+ : fixed_registers_size (0)
{}
bool operator== (const target_desc &other) const;
diff --git a/gdbserver/tracepoint.cc b/gdbserver/tracepoint.cc
index b7d0ef2c2932..1cbf0fddc1e6 100644
--- a/gdbserver/tracepoint.cc
+++ b/gdbserver/tracepoint.cc
@@ -5097,7 +5097,7 @@ traceframe_walk_blocks (unsigned char *database, unsigned int datasize,
{
case 'R':
/* Skip over the registers block. */
- dataptr += current_target_desc ()->registers_size;
+ dataptr += current_target_desc ()->fixed_registers_size;
break;
case 'M':
/* Skip over the memory block. */
@@ -5776,7 +5776,7 @@ gdb_collect (struct tracepoint *tpoint, unsigned char *regs)
ctx.regcache_initted = 0;
/* Wrap the regblock in a register cache (in the stack, we don't
want to malloc here). */
- ctx.regspace = (unsigned char *) alloca (ipa_tdesc->registers_size);
+ ctx.regspace = (unsigned char *) alloca (ipa_tdesc->fixed_registers_size);
if (ctx.regspace == NULL)
{
trace_debug ("Trace buffer block allocation failed, skipping");
^ permalink raw reply [flat|nested] 16+ messages in thread
* [RFC PATCH v4 12/15] GDB: aarch64-linux: Load and store VG separately from other SVE registers
2024-11-02 2:56 [RFC PATCH v4 00/15] gdbserver improvements for AArch64 SVE support Thiago Jung Bauermann
` (10 preceding siblings ...)
2024-11-02 2:56 ` [RFC PATCH v4 11/15] gdbserver: " Thiago Jung Bauermann
@ 2024-11-02 2:56 ` Thiago Jung Bauermann
2024-11-02 2:56 ` [RFC PATCH v4 13/15] GDB, gdbserver: aarch64: Convert SVE feature to use variable-size registers Thiago Jung Bauermann
` (2 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Thiago Jung Bauermann @ 2024-11-02 2:56 UTC (permalink / raw)
To: gdb-patches
GDB currently loads and stores all SVE registers, including the VG
register, at the same time.
To suport variable-size SVE registers, GDB needs to be able to load and
store VG separately from them so that it knows the size of the SVE
vector registers in advance. So change it to fetch or store VG
separately when only that register is requested.
---
gdb/aarch64-linux-nat.c | 36 ++++++++++++++++++++++++++++++++++--
1 file changed, 34 insertions(+), 2 deletions(-)
diff --git a/gdb/aarch64-linux-nat.c b/gdb/aarch64-linux-nat.c
index 0fa5bee500b1..f1b014b5eb26 100644
--- a/gdb/aarch64-linux-nat.c
+++ b/gdb/aarch64-linux-nat.c
@@ -310,6 +310,21 @@ store_fpregs_to_thread (const struct regcache *regcache)
}
}
+/* Fill GDB's REGCACHE with the SVE VG register value from the thread
+ associated with REGCACHE.
+
+ This function handles reading data from SVE or SSVE states, depending
+ on which state is active at the moment. */
+
+static void
+fetch_sve_vg_from_thread (struct regcache *regcache)
+{
+ uint64_t vq = aarch64_sve_get_vq (regcache->ptid ().lwp ());
+ uint64_t vg = sve_vg_from_vq (vq);
+
+ regcache->raw_supply (AARCH64_SVE_VG_REGNUM, &vg);
+}
+
/* Fill GDB's REGCACHE with the valid SVE register values from the thread
associated with REGCACHE.
@@ -323,6 +338,19 @@ fetch_sveregs_from_thread (struct regcache *regcache)
aarch64_sve_regs_copy_to_reg_buf (regcache->ptid ().lwp (), regcache);
}
+/* Store the SVE VG register value from GDB's REGCACHE to the thread
+ associated with REGCACHE.
+
+ This function handles writing data to SVE or SSVE state, depending
+ on which state is active at the moment. */
+
+static void
+store_sve_vg_to_thread (struct regcache *regcache)
+{
+ if (!aarch64_sve_set_vq (regcache->ptid ().lwp (), regcache))
+ perror_with_name (_ ("Unable to set VG register"));
+}
+
/* Store the valid SVE register values from GDB's REGCACHE to the thread
associated with REGCACHE.
@@ -583,8 +611,10 @@ aarch64_fetch_registers (struct regcache *regcache, int regno)
fetch_gregs_from_thread (regcache);
/* SVE register? */
else if ((tdep->has_sve () || tdep->has_sme ())
- && regno <= AARCH64_SVE_VG_REGNUM)
+ && regno < AARCH64_SVE_VG_REGNUM)
fetch_sveregs_from_thread (regcache);
+ else if (tdep->has_sve () && regno == AARCH64_SVE_VG_REGNUM)
+ fetch_sve_vg_from_thread (regcache);
/* FPSIMD register? */
else if (regno <= AARCH64_FPCR_REGNUM)
fetch_fpregs_from_thread (regcache);
@@ -686,8 +716,10 @@ aarch64_store_registers (struct regcache *regcache, int regno)
store_gregs_to_thread (regcache);
/* SVE register? */
else if ((tdep->has_sve () || tdep->has_sme ())
- && regno <= AARCH64_SVE_VG_REGNUM)
+ && regno < AARCH64_SVE_VG_REGNUM)
store_sveregs_to_thread (regcache);
+ else if (tdep->has_sve () && regno == AARCH64_SVE_VG_REGNUM)
+ store_sve_vg_to_thread (regcache);
/* FPSIMD register? */
else if (regno <= AARCH64_FPCR_REGNUM)
store_fpregs_to_thread (regcache);
^ permalink raw reply [flat|nested] 16+ messages in thread
* [RFC PATCH v4 13/15] GDB, gdbserver: aarch64: Convert SVE feature to use variable-size registers
2024-11-02 2:56 [RFC PATCH v4 00/15] gdbserver improvements for AArch64 SVE support Thiago Jung Bauermann
` (11 preceding siblings ...)
2024-11-02 2:56 ` [RFC PATCH v4 12/15] GDB: aarch64-linux: Load and store VG separately from other SVE registers Thiago Jung Bauermann
@ 2024-11-02 2:56 ` Thiago Jung Bauermann
2024-11-02 2:56 ` [RFC PATCH v4 14/15] GDB, gdbserver: aarch64: Use VG instead of VQ Thiago Jung Bauermann
2024-11-02 2:56 ` [RFC PATCH v4 15/15] gdb/testsuite: Add test to exercise multi-threaded AArch64 SVE inferiors Thiago Jung Bauermann
14 siblings, 0 replies; 16+ messages in thread
From: Thiago Jung Bauermann @ 2024-11-02 2:56 UTC (permalink / raw)
To: gdb-patches
Everything needed to support describing variable-size vector registers
in XML is now in place, so convert aarch64 SVE feature to XML. This
commit introduces gdb/features/aarch64-sve.xml, and the corresponding
aarch64-sve.c is created from it.
aarch64_dwarf_reg_to_regnum () is moved to gdb/arch/aarch64.c so that it
can be used by gdbserver, and an aarch64 implementation of the recently
added regnum_to_dwarf2_reg () gdbarch hook is added.
There's a FIXME about using max VQ in
aarch64_linux_iterate_over_regset_sections () which I'm still
considering how to solve.
---
gdb/aarch64-linux-nat.c | 26 ++--
gdb/aarch64-linux-tdep.c | 55 +++++---
gdb/aarch64-tdep.c | 115 ++++++++--------
gdb/aarch64-tdep.h | 21 +--
gdb/arch/aarch64.c | 36 ++++-
gdb/arch/aarch64.h | 25 ++--
gdb/features/Makefile | 1 +
gdb/features/aarch64-sve.c | 199 +++++++++++++++-------------
gdb/features/aarch64-sve.xml | 217 +++++++++++++++++++++++++++++++
gdbserver/configure.srv | 1 +
gdbserver/linux-aarch64-low.cc | 6 +-
gdbserver/linux-aarch64-tdesc.cc | 17 ++-
12 files changed, 494 insertions(+), 225 deletions(-)
create mode 100644 gdb/features/aarch64-sve.xml
diff --git a/gdb/aarch64-linux-nat.c b/gdb/aarch64-linux-nat.c
index f1b014b5eb26..5e12a2e403f6 100644
--- a/gdb/aarch64-linux-nat.c
+++ b/gdb/aarch64-linux-nat.c
@@ -586,7 +586,7 @@ aarch64_fetch_registers (struct regcache *regcache, int regno)
/* We attempt to fetch SVE registers if there is support for either
SVE or SME (due to the SSVE state of SME). */
- if (tdep->has_sve () || tdep->has_sme ())
+ if (tdep->has_sve || tdep->has_sme ())
fetch_sveregs_from_thread (regcache);
else
fetch_fpregs_from_thread (regcache);
@@ -610,10 +610,9 @@ aarch64_fetch_registers (struct regcache *regcache, int regno)
else if (regno < AARCH64_V0_REGNUM)
fetch_gregs_from_thread (regcache);
/* SVE register? */
- else if ((tdep->has_sve () || tdep->has_sme ())
- && regno < AARCH64_SVE_VG_REGNUM)
+ else if ((tdep->has_sve || tdep->has_sme ()) && regno < AARCH64_SVE_VG_REGNUM)
fetch_sveregs_from_thread (regcache);
- else if (tdep->has_sve () && regno == AARCH64_SVE_VG_REGNUM)
+ else if (tdep->has_sve && regno == AARCH64_SVE_VG_REGNUM)
fetch_sve_vg_from_thread (regcache);
/* FPSIMD register? */
else if (regno <= AARCH64_FPCR_REGNUM)
@@ -694,7 +693,7 @@ aarch64_store_registers (struct regcache *regcache, int regno)
/* We attempt to store SVE registers if there is support for either
SVE or SME (due to the SSVE state of SME). */
- if (tdep->has_sve () || tdep->has_sme ())
+ if (tdep->has_sve || tdep->has_sme ())
store_sveregs_to_thread (regcache);
else
store_fpregs_to_thread (regcache);
@@ -715,10 +714,9 @@ aarch64_store_registers (struct regcache *regcache, int regno)
else if (regno < AARCH64_V0_REGNUM)
store_gregs_to_thread (regcache);
/* SVE register? */
- else if ((tdep->has_sve () || tdep->has_sme ())
- && regno < AARCH64_SVE_VG_REGNUM)
+ else if ((tdep->has_sve || tdep->has_sme ()) && regno < AARCH64_SVE_VG_REGNUM)
store_sveregs_to_thread (regcache);
- else if (tdep->has_sve () && regno == AARCH64_SVE_VG_REGNUM)
+ else if (tdep->has_sve && regno == AARCH64_SVE_VG_REGNUM)
store_sve_vg_to_thread (regcache);
/* FPSIMD register? */
else if (regno <= AARCH64_FPCR_REGNUM)
@@ -911,7 +909,7 @@ aarch64_linux_nat_target::read_description ()
/* SVE/SSVE check. Reading VQ may return either the regular vector length
or the streaming vector length, depending on whether streaming mode is
active or not. */
- features.vq = aarch64_sve_get_vq (tid);
+ features.sve = aarch64_sve_get_vq (tid) != 0;
features.pauth = hwcap & AARCH64_HWCAP_PACA;
features.mte = hwcap2 & HWCAP2_MTE;
features.tls = aarch64_tls_register_count (tid);
@@ -1025,19 +1023,19 @@ aarch64_linux_nat_target::thread_architecture (ptid_t ptid)
the tdep. */
aarch64_gdbarch_tdep *tdep
= gdbarch_tdep<aarch64_gdbarch_tdep> (inf->arch ());
- uint64_t vq = aarch64_sve_get_vq (ptid.lwp ());
+ bool sve = aarch64_sve_get_vq (ptid.lwp ()) != 0;
uint64_t svq = aarch64_za_get_svq (ptid.lwp ());
- if (vq == tdep->vq && svq == tdep->sme_svq)
+ if (sve == tdep->has_sve && svq == tdep->sme_svq)
return inf->arch ();
/* We reach here if any vector length for the thread is different from its
value at process start. Lookup gdbarch via info (potentially creating a
- new one) by using a target description that corresponds to the new vq/svq
- value and the current architecture features. */
+ new one) by using a target description that corresponds to the new
+ vector/matrix state and the current architecture features. */
const struct target_desc *tdesc = gdbarch_target_desc (inf->arch ());
aarch64_features features = aarch64_features_from_target_desc (tdesc);
- features.vq = vq;
+ features.sve = sve;
features.svq = svq;
/* Check for the SME2 feature. */
diff --git a/gdb/aarch64-linux-tdep.c b/gdb/aarch64-linux-tdep.c
index c608a84bc711..c04b693f70e7 100644
--- a/gdb/aarch64-linux-tdep.c
+++ b/gdb/aarch64-linux-tdep.c
@@ -275,7 +275,8 @@ read_aarch64_ctx (CORE_ADDR ctx_addr, enum bfd_endian byte_order,
static void
aarch64_linux_restore_vregs (struct gdbarch *gdbarch,
struct trad_frame_cache *cache,
- CORE_ADDR fpsimd_context)
+ CORE_ADDR fpsimd_context,
+ ULONGEST vl)
{
/* WARNING: SIMD state is laid out in memory in target-endian format.
@@ -335,7 +336,7 @@ aarch64_linux_restore_vregs (struct gdbarch *gdbarch,
num_regs + AARCH64_B0_REGNUM
+ i, {buf, B_REGISTER_SIZE});
- if (tdep->has_sve ())
+ if (tdep->has_sve)
trad_frame_set_reg_value_bytes (cache,
num_regs + AARCH64_SVE_V0_REGNUM
+ i, {buf, V_REGISTER_SIZE});
@@ -356,22 +357,22 @@ aarch64_linux_restore_vregs (struct gdbarch *gdbarch,
trad_frame_set_reg_addr (cache, num_regs + AARCH64_B0_REGNUM + i,
offset);
- if (tdep->has_sve ())
+ if (tdep->has_sve)
trad_frame_set_reg_addr (cache, num_regs + AARCH64_SVE_V0_REGNUM
+ i, offset);
}
- if (tdep->has_sve ())
+ if (tdep->has_sve)
{
/* If SVE is supported for this target, zero out the Z
registers then copy the first 16 bytes of each of the V
registers to the associated Z register. Otherwise the Z
registers will contain uninitialized data. */
- std::vector<gdb_byte> z_buffer (tdep->vq * 16);
+ std::vector<gdb_byte> z_buffer (vl);
/* We have already handled the endianness swap above, so we don't need
to worry about it here. */
- memcpy (z_buffer.data (), buf, V_REGISTER_SIZE);
+ memcpy (z_buffer.data (), buf, vl);
trad_frame_set_reg_value_bytes (cache,
AARCH64_SVE_Z0_REGNUM + i,
z_buffer);
@@ -403,8 +404,8 @@ aarch64_linux_read_signal_frame_info (const frame_info_ptr &this_frame,
CORE_ADDR section_end = signal_frame.section_end;
uint32_t size, magic;
bool extra_found = false;
- enum bfd_endian byte_order
- = gdbarch_byte_order (get_frame_arch (this_frame));
+ struct gdbarch *gdbarch = get_frame_arch (this_frame);
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
while ((magic = read_aarch64_ctx (section, byte_order, &size)) != 0
&& size != 0)
@@ -423,6 +424,11 @@ aarch64_linux_read_signal_frame_info (const frame_info_ptr &this_frame,
/* Check if the section is followed by a full SVE dump, and set
sve_regs if it is. */
gdb_byte buf[4];
+ aarch64_gdbarch_tdep *tdep
+ = gdbarch_tdep<aarch64_gdbarch_tdep> (gdbarch);
+
+ if (!tdep->has_sve)
+ break;
/* Extract the vector length. */
if (target_read_memory (section + AARCH64_SVE_CONTEXT_VL_OFFSET,
@@ -436,6 +442,10 @@ aarch64_linux_read_signal_frame_info (const frame_info_ptr &this_frame,
signal_frame.vl = extract_unsigned_integer (buf, 2, byte_order);
+ if (signal_frame.vl == 0)
+ error (_ ("Invalid vector length in signal frame %" PRIu64 "."),
+ signal_frame.vl);
+
/* Extract the flags to check if we are in streaming mode. */
if (target_read_memory (section
+ AARCH64_SVE_CONTEXT_FLAGS_OFFSET,
@@ -598,7 +608,7 @@ aarch64_linux_sigframe_init (const struct tramp_frame *self,
aarch64_gdbarch_tdep *tdep = gdbarch_tdep<aarch64_gdbarch_tdep> (gdbarch);
/* Restore the SVE / FPSIMD registers. */
- if (tdep->has_sve () && signal_frame.sve_section != 0)
+ if (tdep->has_sve && signal_frame.sve_section != 0)
{
ULONGEST vq = sve_vq_from_vl (signal_frame.vl);
CORE_ADDR sve_regs = signal_frame.sve_section;
@@ -648,8 +658,9 @@ aarch64_linux_sigframe_init (const struct tramp_frame *self,
fpsimd + AARCH64_FPSIMD_FPCR_OFFSET);
/* If there was no SVE section then set up the V registers. */
- if (!tdep->has_sve () || signal_frame.sve_section == 0)
- aarch64_linux_restore_vregs (gdbarch, this_cache, fpsimd);
+ if (!tdep->has_sve || signal_frame.sve_section == 0)
+ aarch64_linux_restore_vregs (gdbarch, this_cache, fpsimd,
+ tdep->has_sve ? signal_frame.vl : 0);
}
/* Restore the SME registers. */
@@ -726,7 +737,7 @@ aarch64_linux_sigframe_prev_arch (const frame_info_ptr &this_frame,
const struct target_desc *tdesc
= gdbarch_target_desc (get_frame_arch (this_frame));
aarch64_features features = aarch64_features_from_target_desc (tdesc);
- features.vq = sve_vq_from_vl (signal_frame.vl);
+ features.sve = signal_frame.vl != 0;
features.svq = (uint8_t) sve_vq_from_vl (signal_frame.svl);
struct gdbarch_info info;
@@ -1053,9 +1064,12 @@ collect_sve_regset (const struct regset *regset,
gdb_byte *header = (gdb_byte *) buf;
struct gdbarch *gdbarch = regcache->arch ();
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
- aarch64_gdbarch_tdep *tdep = gdbarch_tdep<aarch64_gdbarch_tdep> (gdbarch);
- uint64_t vq = tdep->vq;
+ ULONGEST vg;
+ // FIXME: Remove this cast.
+ enum register_status vg_status
+ = const_cast<struct regcache *> (regcache)->raw_read (AARCH64_SVE_VG_REGNUM, &vg);
+ gdb_assert (vg_status == REG_VALID);
gdb_assert (buf != NULL);
gdb_assert (size > SVE_HEADER_SIZE);
@@ -1067,7 +1081,7 @@ collect_sve_regset (const struct regset *regset,
store_unsigned_integer (header + SVE_HEADER_MAX_SIZE_OFFSET,
SVE_HEADER_MAX_SIZE_LENGTH, byte_order, max_size);
store_unsigned_integer (header + SVE_HEADER_VL_OFFSET, SVE_HEADER_VL_LENGTH,
- byte_order, sve_vl_from_vq (vq));
+ byte_order, sve_vl_from_vg (vg));
uint16_t max_vl = SVE_CORE_DUMMY_MAX_VL;
store_unsigned_integer (header + SVE_HEADER_MAX_VL_OFFSET,
SVE_HEADER_MAX_VL_LENGTH, byte_order,
@@ -1437,14 +1451,15 @@ aarch64_linux_iterate_over_regset_sections (struct gdbarch *gdbarch,
cb (".reg", AARCH64_LINUX_SIZEOF_GREGSET, AARCH64_LINUX_SIZEOF_GREGSET,
&aarch64_linux_gregset, NULL, cb_data);
- if (tdep->has_sve ())
+ if (tdep->has_sve)
{
/* Create this on the fly in order to handle vector register sizes. */
+ // FIXME: Don't use maximum VQ.
const struct regcache_map_entry sve_regmap[] =
{
- { 32, AARCH64_SVE_Z0_REGNUM, (int) (tdep->vq * 16) },
- { 16, AARCH64_SVE_P0_REGNUM, (int) (tdep->vq * 16 / 8) },
- { 1, AARCH64_SVE_FFR_REGNUM, (int) (tdep->vq * 16 / 8) },
+ { 32, AARCH64_SVE_Z0_REGNUM, (int)(AARCH64_MAX_SVE_VQ * 16) },
+ { 16, AARCH64_SVE_P0_REGNUM, (int)(AARCH64_MAX_SVE_VQ * 16 / 8) },
+ { 1, AARCH64_SVE_FFR_REGNUM, (int)(AARCH64_MAX_SVE_VQ * 16 / 8) },
{ 1, AARCH64_FPSR_REGNUM, 4 },
{ 1, AARCH64_FPCR_REGNUM, 4 },
{ 0 }
@@ -1627,7 +1642,7 @@ aarch64_linux_core_read_description (struct gdbarch *gdbarch,
Otherwise the SVE section is considered active. This guarantees we will
have the correct target description with the correct SVE vector
length. */
- features.vq = aarch64_linux_core_read_vq_from_sections (gdbarch, abfd);
+ features.sve = aarch64_linux_core_read_vq_from_sections (gdbarch, abfd) != 0;
features.pauth = hwcap & AARCH64_HWCAP_PACA;
features.mte = hwcap2 & HWCAP2_MTE;
diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c
index 04442f22ea3f..ff356b6a0ac7 100644
--- a/gdb/aarch64-tdep.c
+++ b/gdb/aarch64-tdep.c
@@ -2468,38 +2468,54 @@ aarch64_vnv_type (struct gdbarch *gdbarch)
/* Implement the "dwarf2_reg_to_regnum" gdbarch method. */
static int
-aarch64_dwarf_reg_to_regnum (struct gdbarch *gdbarch, int reg)
+aarch64_tdep_dwarf_reg_to_regnum (struct gdbarch *gdbarch, int reg)
{
+ int ret = aarch64_dwarf_reg_to_regnum (reg);
+
+ if (ret != -1)
+ return ret;
+
aarch64_gdbarch_tdep *tdep = gdbarch_tdep<aarch64_gdbarch_tdep> (gdbarch);
+ if (tdep->has_pauth () && reg == AARCH64_DWARF_RA_SIGN_STATE)
+ return tdep->ra_sign_state_regnum;
- if (reg >= AARCH64_DWARF_X0 && reg <= AARCH64_DWARF_X0 + 30)
- return AARCH64_X0_REGNUM + reg - AARCH64_DWARF_X0;
+ return -1;
+}
+
+/* Implement the "regnum_to_dwarf2_reg" gdbarch method. */
+
+static int
+aarch64_regnum_to_dwarf_reg (struct gdbarch *gdbarch, int regnum)
+{
+ if (regnum >= AARCH64_X0_REGNUM && regnum <= AARCH64_X0_REGNUM + 30)
+ return AARCH64_DWARF_X0 + regnum - AARCH64_X0_REGNUM;
- if (reg == AARCH64_DWARF_SP)
- return AARCH64_SP_REGNUM;
+ if (regnum == AARCH64_SP_REGNUM)
+ return AARCH64_DWARF_SP;
- if (reg == AARCH64_DWARF_PC)
- return AARCH64_PC_REGNUM;
+ if (regnum == AARCH64_PC_REGNUM)
+ return AARCH64_DWARF_PC;
- if (reg >= AARCH64_DWARF_V0 && reg <= AARCH64_DWARF_V0 + 31)
- return AARCH64_V0_REGNUM + reg - AARCH64_DWARF_V0;
+ if (regnum >= AARCH64_V0_REGNUM && regnum <= AARCH64_V0_REGNUM + 31)
+ return AARCH64_DWARF_V0 + regnum - AARCH64_V0_REGNUM;
- if (reg == AARCH64_DWARF_SVE_VG)
- return AARCH64_SVE_VG_REGNUM;
+ if (regnum == AARCH64_SVE_VG_REGNUM)
+ return AARCH64_DWARF_SVE_VG;
- if (reg == AARCH64_DWARF_SVE_FFR)
- return AARCH64_SVE_FFR_REGNUM;
+ if (regnum == AARCH64_SVE_FFR_REGNUM)
+ return AARCH64_DWARF_SVE_FFR;
- if (reg >= AARCH64_DWARF_SVE_P0 && reg <= AARCH64_DWARF_SVE_P0 + 15)
- return AARCH64_SVE_P0_REGNUM + reg - AARCH64_DWARF_SVE_P0;
+ if (regnum >= AARCH64_SVE_P0_REGNUM && regnum <= AARCH64_SVE_P0_REGNUM + 15)
+ return AARCH64_DWARF_SVE_P0 + regnum - AARCH64_SVE_P0_REGNUM;
- if (reg >= AARCH64_DWARF_SVE_Z0 && reg <= AARCH64_DWARF_SVE_Z0 + 15)
- return AARCH64_SVE_Z0_REGNUM + reg - AARCH64_DWARF_SVE_Z0;
+ if (regnum >= AARCH64_SVE_Z0_REGNUM && regnum <= AARCH64_SVE_Z0_REGNUM + 15)
+ return AARCH64_DWARF_SVE_Z0 + regnum - AARCH64_SVE_Z0_REGNUM;
+ aarch64_gdbarch_tdep *tdep = gdbarch_tdep<aarch64_gdbarch_tdep> (gdbarch);
if (tdep->has_pauth ())
{
- if (reg == AARCH64_DWARF_RA_SIGN_STATE)
- return tdep->ra_sign_state_regnum;
+ if (regnum == tdep->ra_sign_state_regnum)
+ return AARCH64_DWARF_RA_SIGN_STATE;
}
return -1;
@@ -2994,7 +3010,7 @@ aarch64_pseudo_register_name (struct gdbarch *gdbarch, int regnum)
if (is_w_pseudo_register (gdbarch, regnum))
return w_name[regnum - tdep->w_pseudo_base];
- if (tdep->has_sve ())
+ if (tdep->has_sve)
{
static const char *const sve_v_name[] =
{
@@ -3050,7 +3066,7 @@ aarch64_pseudo_register_type (struct gdbarch *gdbarch, int regnum)
if (p_regnum >= AARCH64_B0_REGNUM && p_regnum < AARCH64_B0_REGNUM + 32)
return aarch64_vnb_type (gdbarch);
- if (tdep->has_sve () && p_regnum >= AARCH64_SVE_V0_REGNUM
+ if (tdep->has_sve && p_regnum >= AARCH64_SVE_V0_REGNUM
&& p_regnum < AARCH64_SVE_V0_REGNUM + AARCH64_V_REGS_NUM)
return aarch64_vnv_type (gdbarch);
@@ -3090,7 +3106,7 @@ aarch64_pseudo_register_reggroup_p (struct gdbarch *gdbarch, int regnum,
return group == all_reggroup || group == vector_reggroup;
else if (p_regnum >= AARCH64_B0_REGNUM && p_regnum < AARCH64_B0_REGNUM + 32)
return group == all_reggroup || group == vector_reggroup;
- else if (tdep->has_sve () && p_regnum >= AARCH64_SVE_V0_REGNUM
+ else if (tdep->has_sve && p_regnum >= AARCH64_SVE_V0_REGNUM
&& p_regnum < AARCH64_SVE_V0_REGNUM + AARCH64_V_REGS_NUM)
return group == all_reggroup || group == vector_reggroup;
else if (is_sme_pseudo_register (gdbarch, regnum))
@@ -3285,7 +3301,7 @@ aarch64_pseudo_read_value (gdbarch *gdbarch, const frame_info_ptr &next_frame,
return aarch64_pseudo_read_value_1 (next_frame, pseudo_reg_num,
pseudo_offset - AARCH64_B0_REGNUM);
- if (tdep->has_sve () && pseudo_offset >= AARCH64_SVE_V0_REGNUM
+ if (tdep->has_sve && pseudo_offset >= AARCH64_SVE_V0_REGNUM
&& pseudo_offset < AARCH64_SVE_V0_REGNUM + 32)
return aarch64_pseudo_read_value_1 (next_frame, pseudo_reg_num,
pseudo_offset - AARCH64_SVE_V0_REGNUM);
@@ -3425,7 +3441,7 @@ aarch64_pseudo_write (gdbarch *gdbarch, const frame_info_ptr &next_frame,
return aarch64_pseudo_write_1 (gdbarch, next_frame,
pseudo_offset - AARCH64_B0_REGNUM, buf);
- if (tdep->has_sve () && pseudo_offset >= AARCH64_SVE_V0_REGNUM
+ if (tdep->has_sve && pseudo_offset >= AARCH64_SVE_V0_REGNUM
&& pseudo_offset < AARCH64_SVE_V0_REGNUM + 32)
return aarch64_pseudo_write_1 (gdbarch, next_frame,
pseudo_offset - AARCH64_SVE_V0_REGNUM, buf);
@@ -3941,10 +3957,6 @@ aarch64_displaced_step_hw_singlestep (struct gdbarch *gdbarch)
const target_desc *
aarch64_read_description (const aarch64_features &features)
{
- if (features.vq > AARCH64_MAX_SVE_VQ)
- error (_("VQ is %" PRIu64 ", maximum supported value is %d"), features.vq,
- AARCH64_MAX_SVE_VQ);
-
struct target_desc *tdesc = tdesc_aarch64_map[features];
if (tdesc == NULL)
@@ -3956,27 +3968,6 @@ aarch64_read_description (const aarch64_features &features)
return tdesc;
}
-/* Return the VQ used when creating the target description TDESC. */
-
-static uint64_t
-aarch64_get_tdesc_vq (const struct target_desc *tdesc)
-{
- const struct tdesc_feature *feature_sve;
-
- if (!tdesc_has_registers (tdesc))
- return 0;
-
- feature_sve = tdesc_find_feature (tdesc, "org.gnu.gdb.aarch64.sve");
-
- if (feature_sve == nullptr)
- return 0;
-
- uint64_t vl = tdesc_register_bitsize (feature_sve,
- aarch64_sve_register_names[0]) / 8;
- return sve_vq_from_vl (vl);
-}
-
-
/* Return the svq (streaming vector quotient) used when creating the target
description TDESC. */
@@ -4013,7 +4004,8 @@ aarch64_features_from_target_desc (const struct target_desc *tdesc)
if (tdesc == nullptr)
return features;
- features.vq = aarch64_get_tdesc_vq (tdesc);
+ features.sve
+ = tdesc_find_feature (tdesc, "org.gnu.gdb.aarch64.sve") != nullptr;
/* We need to look for a couple pauth feature name variations. */
features.pauth
@@ -4358,13 +4350,11 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
int i, num_regs = 0, num_pseudo_regs = 0;
int first_pauth_regnum = -1, ra_sign_state_offset = -1;
int first_mte_regnum = -1, first_tls_regnum = -1;
- uint64_t vq = aarch64_get_tdesc_vq (info.target_desc);
+ bool has_sve = (info.target_desc == nullptr ? false
+ : tdesc_find_feature (info.target_desc,
+ "org.gnu.gdb.aarch64.sve") != nullptr);
uint64_t svq = aarch64_get_tdesc_svq (info.target_desc);
- if (vq > AARCH64_MAX_SVE_VQ)
- internal_error (_("VQ out of bounds: %s (max %d)"),
- pulongest (vq), AARCH64_MAX_SVE_VQ);
-
if (svq > AARCH64_MAX_SVE_VQ)
internal_error (_("Streaming vector quotient (svq) out of bounds: %s"
" (max %d)"),
@@ -4377,18 +4367,17 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
{
aarch64_gdbarch_tdep *tdep
= gdbarch_tdep<aarch64_gdbarch_tdep> (best_arch->gdbarch);
- if (tdep && tdep->vq == vq && tdep->sme_svq == svq)
+ if (tdep && tdep->has_sve == has_sve && tdep->sme_svq == svq)
return best_arch->gdbarch;
}
/* Ensure we always have a target descriptor, and that it is for the given VQ
value. */
const struct target_desc *tdesc = info.target_desc;
- if (!tdesc_has_registers (tdesc) || vq != aarch64_get_tdesc_vq (tdesc)
- || svq != aarch64_get_tdesc_svq (tdesc))
+ if (!tdesc_has_registers (tdesc))
{
aarch64_features features;
- features.vq = vq;
+ features.sve = has_sve;
features.svq = svq;
tdesc = aarch64_read_description (features);
}
@@ -4603,7 +4592,7 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
tdep->lowest_pc = 0x20;
tdep->jb_pc = -1; /* Longjump support not enabled by default. */
tdep->jb_elt_size = 8;
- tdep->vq = vq;
+ tdep->has_sve = feature_sve != nullptr;
tdep->pauth_reg_base = first_pauth_regnum;
tdep->pauth_reg_count = pauth_masks;
tdep->ra_sign_state_regnum = -1;
@@ -4690,7 +4679,8 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
set_gdbarch_stack_frame_destroyed_p (gdbarch, aarch64_stack_frame_destroyed_p);
/* Internal <-> external register number maps. */
- set_gdbarch_dwarf2_reg_to_regnum (gdbarch, aarch64_dwarf_reg_to_regnum);
+ set_gdbarch_dwarf2_reg_to_regnum (gdbarch, aarch64_tdep_dwarf_reg_to_regnum);
+ set_gdbarch_regnum_to_dwarf2_reg (gdbarch, aarch64_regnum_to_dwarf_reg);
/* Returning results. */
set_gdbarch_return_value_as_value (gdbarch, aarch64_return_value);
@@ -4857,6 +4847,9 @@ aarch64_dump_tdep (struct gdbarch *gdbarch, struct ui_file *file)
gdb_printf (file, _("aarch64_dump_tdep: Lowest pc = 0x%s\n"),
paddress (gdbarch, tdep->lowest_pc));
+ gdb_printf (file, _ ("aarch64_dump_tdep: has_sve = %s\n"),
+ tdep->has_sve ? "true" : "false");
+
/* SME fields. */
gdb_printf (file, _("aarch64_dump_tdep: sme_tile_type_q = %s\n"),
host_address_to_string (tdep->sme_tile_type_q));
diff --git a/gdb/aarch64-tdep.h b/gdb/aarch64-tdep.h
index 50166fb4f24f..c34027c7f387 100644
--- a/gdb/aarch64-tdep.h
+++ b/gdb/aarch64-tdep.h
@@ -31,17 +31,6 @@
struct gdbarch;
struct regset;
-/* AArch64 Dwarf register numbering. */
-#define AARCH64_DWARF_X0 0
-#define AARCH64_DWARF_SP 31
-#define AARCH64_DWARF_PC 32
-#define AARCH64_DWARF_RA_SIGN_STATE 34
-#define AARCH64_DWARF_V0 64
-#define AARCH64_DWARF_SVE_VG 46
-#define AARCH64_DWARF_SVE_FFR 47
-#define AARCH64_DWARF_SVE_P0 48
-#define AARCH64_DWARF_SVE_Z0 96
-
/* Size of integer registers. */
#define X_REGISTER_SIZE 8
#define B_REGISTER_SIZE 1
@@ -100,14 +89,8 @@ struct aarch64_gdbarch_tdep : gdbarch_tdep_base
int (*aarch64_syscall_record) (struct regcache *regcache,
unsigned long svc_number) = nullptr;
- /* The VQ value for SVE targets, or zero if SVE is not supported. */
- uint64_t vq = 0;
-
- /* Returns true if the target supports SVE. */
- bool has_sve () const
- {
- return vq != 0;
- }
+ /* Whether SVE is supported. */
+ bool has_sve = false;
int pauth_reg_base = 0;
/* Number of pauth masks. */
diff --git a/gdb/arch/aarch64.c b/gdb/arch/aarch64.c
index 5526aa1b580b..aa246857a79f 100644
--- a/gdb/arch/aarch64.c
+++ b/gdb/arch/aarch64.c
@@ -42,10 +42,10 @@ aarch64_create_target_description (const aarch64_features &features)
regnum = create_feature_aarch64_core (tdesc.get (), regnum);
- if (features.vq == 0)
+ if (!features.sve)
regnum = create_feature_aarch64_fpu (tdesc.get (), regnum);
else
- regnum = create_feature_aarch64_sve (tdesc.get (), regnum, features.vq);
+ regnum = create_feature_aarch64_sve (tdesc.get (), regnum);
if (features.pauth)
regnum = create_feature_aarch64_pauth (tdesc.get (), regnum);
@@ -98,3 +98,35 @@ aarch64_mask_from_pac_registers (const CORE_ADDR cmask, const CORE_ADDR dmask)
return cmask;
}
+
+/* See arch/aarch64.h. */
+
+int
+aarch64_dwarf_reg_to_regnum (int reg)
+{
+ if (reg >= AARCH64_DWARF_X0 && reg <= AARCH64_DWARF_X0 + 30)
+ return AARCH64_X0_REGNUM + reg - AARCH64_DWARF_X0;
+
+ if (reg == AARCH64_DWARF_SP)
+ return AARCH64_SP_REGNUM;
+
+ if (reg == AARCH64_DWARF_PC)
+ return AARCH64_PC_REGNUM;
+
+ if (reg >= AARCH64_DWARF_V0 && reg <= AARCH64_DWARF_V0 + 31)
+ return AARCH64_V0_REGNUM + reg - AARCH64_DWARF_V0;
+
+ if (reg == AARCH64_DWARF_SVE_VG)
+ return AARCH64_SVE_VG_REGNUM;
+
+ if (reg == AARCH64_DWARF_SVE_FFR)
+ return AARCH64_SVE_FFR_REGNUM;
+
+ if (reg >= AARCH64_DWARF_SVE_P0 && reg <= AARCH64_DWARF_SVE_P0 + 15)
+ return AARCH64_SVE_P0_REGNUM + reg - AARCH64_DWARF_SVE_P0;
+
+ if (reg >= AARCH64_DWARF_SVE_Z0 && reg <= AARCH64_DWARF_SVE_Z0 + 15)
+ return AARCH64_SVE_Z0_REGNUM + reg - AARCH64_DWARF_SVE_Z0;
+
+ return -1;
+}
diff --git a/gdb/arch/aarch64.h b/gdb/arch/aarch64.h
index b4c0111aeb49..1857fd4802fb 100644
--- a/gdb/arch/aarch64.h
+++ b/gdb/arch/aarch64.h
@@ -26,12 +26,7 @@
used to select register sets. */
struct aarch64_features
{
- /* A non zero VQ value indicates both the presence of SVE and the
- Vector Quotient - the number of 128-bit chunks in an SVE Z
- register.
-
- The maximum value for VQ is 16 (5 bits). */
- uint64_t vq = 0;
+ bool sve = false;
bool pauth = false;
bool mte = false;
@@ -55,7 +50,7 @@ struct aarch64_features
inline bool operator==(const aarch64_features &lhs, const aarch64_features &rhs)
{
- return lhs.vq == rhs.vq
+ return lhs.sve == rhs.sve
&& lhs.pauth == rhs.pauth
&& lhs.mte == rhs.mte
&& lhs.tls == rhs.tls
@@ -72,7 +67,7 @@ namespace std
{
std::size_t h;
- h = features.vq;
+ h = features.sve;
h = h << 1 | features.pauth;
h = h << 1 | features.mte;
/* Shift by two bits for now. We may need to increase this in the future
@@ -107,6 +102,9 @@ CORE_ADDR aarch64_remove_top_bits (CORE_ADDR pointer, CORE_ADDR mask);
CORE_ADDR
aarch64_mask_from_pac_registers (const CORE_ADDR cmask, const CORE_ADDR dmask);
+/* Map DWARF reg number to GDB regnum. */
+int aarch64_dwarf_reg_to_regnum (int reg);
+
/* Register numbers of various important registers.
Note that on SVE, the Z registers reuse the V register numbers and the V
registers become pseudo registers. */
@@ -136,6 +134,17 @@ enum aarch64_regnum
AARCH64_LAST_V_ARG_REGNUM = AARCH64_V0_REGNUM + 7
};
+/* AArch64 Dwarf register numbering. */
+#define AARCH64_DWARF_X0 0
+#define AARCH64_DWARF_SP 31
+#define AARCH64_DWARF_PC 32
+#define AARCH64_DWARF_RA_SIGN_STATE 34
+#define AARCH64_DWARF_V0 64
+#define AARCH64_DWARF_SVE_VG 46
+#define AARCH64_DWARF_SVE_FFR 47
+#define AARCH64_DWARF_SVE_P0 48
+#define AARCH64_DWARF_SVE_Z0 96
+
/* Sizes of various AArch64 registers. */
#define AARCH64_TLS_REGISTER_SIZE 8
#define V_REGISTER_SIZE 16
diff --git a/gdb/features/Makefile b/gdb/features/Makefile
index 01b327cbce1f..f84debb4f15c 100644
--- a/gdb/features/Makefile
+++ b/gdb/features/Makefile
@@ -202,6 +202,7 @@ FEATURE_XMLFILES = aarch64-core.xml \
aarch64-fpu.xml \
aarch64-pauth.xml \
aarch64-mte.xml \
+ aarch64-sve.xml \
arc/v1-core.xml \
arc/v1-aux.xml \
arc/v2-core.xml \
diff --git a/gdb/features/aarch64-sve.c b/gdb/features/aarch64-sve.c
index 67fc801ec70e..1bed5f402376 100644
--- a/gdb/features/aarch64-sve.c
+++ b/gdb/features/aarch64-sve.c
@@ -1,76 +1,86 @@
-/* Copyright (C) 2018-2024 Free Software Foundation, Inc.
-
- This file is part of GDB.
-
- 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/>. */
+/* THIS FILE IS GENERATED. -*- buffer-read-only: t -*- vi:set ro:
+ Original: aarch64-sve.xml */
#include "gdbsupport/tdesc.h"
-
-/* This function is NOT auto generated from xml. Create the aarch64 with SVE
- feature into RESULT, where SCALE is the number of 128 bit chunks in a Z
- register. */
+#include "dwarf2.h"
static int
-create_feature_aarch64_sve (struct target_desc *result, long regnum,
- uint64_t scale)
+create_feature_aarch64_sve (struct target_desc *result, long regnum)
{
struct tdesc_feature *feature;
- tdesc_type *element_type, *field_type;
- tdesc_type_with_fields *type_with_fields;
feature = tdesc_create_feature (result, "org.gnu.gdb.aarch64.sve");
-
+ tdesc_type *element_type;
element_type = tdesc_named_type (feature, "uint128");
- tdesc_create_vector (feature, "svevqu", element_type, scale);
+ gdb_byte svevq_count_locexpr[5];
+ svevq_count_locexpr[0] = DW_OP_bregx;
+ svevq_count_locexpr[1] = 46;
+ svevq_count_locexpr[2] = 0;
+ svevq_count_locexpr[3] = DW_OP_lit2;
+ svevq_count_locexpr[4] = DW_OP_div;
+ tdesc_create_vector (feature, "svevqu", element_type, svevq_count_locexpr);
element_type = tdesc_named_type (feature, "int128");
- tdesc_create_vector (feature, "svevqs", element_type, scale);
+ tdesc_create_vector (feature, "svevqs", element_type, svevq_count_locexpr);
element_type = tdesc_named_type (feature, "ieee_double");
- tdesc_create_vector (feature, "svevdf", element_type, 2 * scale);
+ gdb_byte svevd_count_locexpr[3];
+ svevd_count_locexpr[0] = DW_OP_bregx;
+ svevd_count_locexpr[1] = 46;
+ svevd_count_locexpr[2] = 0;
+ tdesc_create_vector (feature, "svevdf", element_type, svevd_count_locexpr);
element_type = tdesc_named_type (feature, "uint64");
- tdesc_create_vector (feature, "svevdu", element_type, 2 * scale);
+ tdesc_create_vector (feature, "svevdu", element_type, svevd_count_locexpr);
element_type = tdesc_named_type (feature, "int64");
- tdesc_create_vector (feature, "svevds", element_type, 2 * scale);
+ tdesc_create_vector (feature, "svevds", element_type, svevd_count_locexpr);
element_type = tdesc_named_type (feature, "ieee_single");
- tdesc_create_vector (feature, "svevsf", element_type, 4 * scale);
+ gdb_byte svevs_count_locexpr[5];
+ svevs_count_locexpr[0] = DW_OP_bregx;
+ svevs_count_locexpr[1] = 46;
+ svevs_count_locexpr[2] = 0;
+ svevs_count_locexpr[3] = DW_OP_lit2;
+ svevs_count_locexpr[4] = DW_OP_mul;
+ tdesc_create_vector (feature, "svevsf", element_type, svevs_count_locexpr);
element_type = tdesc_named_type (feature, "uint32");
- tdesc_create_vector (feature, "svevsu", element_type, 4 * scale);
+ tdesc_create_vector (feature, "svevsu", element_type, svevs_count_locexpr);
element_type = tdesc_named_type (feature, "int32");
- tdesc_create_vector (feature, "svevss", element_type, 4 * scale);
+ tdesc_create_vector (feature, "svevss", element_type, svevs_count_locexpr);
element_type = tdesc_named_type (feature, "ieee_half");
- tdesc_create_vector (feature, "svevhf", element_type, 8 * scale);
+ gdb_byte svevh_count_locexpr[5];
+ svevh_count_locexpr[0] = DW_OP_bregx;
+ svevh_count_locexpr[1] = 46;
+ svevh_count_locexpr[2] = 0;
+ svevh_count_locexpr[3] = DW_OP_lit4;
+ svevh_count_locexpr[4] = DW_OP_mul;
+ tdesc_create_vector (feature, "svevhf", element_type, svevh_count_locexpr);
element_type = tdesc_named_type (feature, "uint16");
- tdesc_create_vector (feature, "svevhu", element_type, 8 * scale);
+ tdesc_create_vector (feature, "svevhu", element_type, svevh_count_locexpr);
element_type = tdesc_named_type (feature, "int16");
- tdesc_create_vector (feature, "svevhs", element_type, 8 * scale);
+ tdesc_create_vector (feature, "svevhs", element_type, svevh_count_locexpr);
element_type = tdesc_named_type (feature, "uint8");
- tdesc_create_vector (feature, "svevbu", element_type, 16 * scale);
+ gdb_byte svevb_count_locexpr[5];
+ svevb_count_locexpr[0] = DW_OP_bregx;
+ svevb_count_locexpr[1] = 46;
+ svevb_count_locexpr[2] = 0;
+ svevb_count_locexpr[3] = DW_OP_lit8;
+ svevb_count_locexpr[4] = DW_OP_mul;
+ tdesc_create_vector (feature, "svevbu", element_type, svevb_count_locexpr);
element_type = tdesc_named_type (feature, "int8");
- tdesc_create_vector (feature, "svevbs", element_type, 16 * scale);
+ tdesc_create_vector (feature, "svevbs", element_type, svevb_count_locexpr);
+ tdesc_type_with_fields *type_with_fields;
type_with_fields = tdesc_create_union (feature, "svevnq");
+ tdesc_type *field_type;
field_type = tdesc_named_type (feature, "svevqu");
tdesc_add_field (type_with_fields, "u", field_type);
field_type = tdesc_named_type (feature, "svevqs");
@@ -118,10 +128,15 @@ create_feature_aarch64_sve (struct target_desc *result, long regnum,
field_type = tdesc_named_type (feature, "svevnb");
tdesc_add_field (type_with_fields, "b", field_type);
- field_type = tdesc_named_type (feature, "uint8");
- tdesc_create_vector (feature, "svep", field_type, 2 * scale);
+ element_type = tdesc_named_type (feature, "uint8");
+ gdb_byte svep_locexpr[5];
+ svep_locexpr[0] = DW_OP_bregx;
+ svep_locexpr[1] = 46;
+ svep_locexpr[2] = 0;
+ svep_locexpr[3] = DW_OP_lit4;
+ svep_locexpr[4] = DW_OP_mul;
+ tdesc_create_vector (feature, "svep", element_type, svep_locexpr);
- /* FPSR register type */
type_with_fields = tdesc_create_flags (feature, "fpsr_flags", 4);
tdesc_add_flag (type_with_fields, 0, "IOC");
tdesc_add_flag (type_with_fields, 1, "DZC");
@@ -135,7 +150,6 @@ create_feature_aarch64_sve (struct target_desc *result, long regnum,
tdesc_add_flag (type_with_fields, 30, "Z");
tdesc_add_flag (type_with_fields, 31, "N");
- /* FPCR register type */
type_with_fields = tdesc_create_flags (feature, "fpcr_flags", 4);
tdesc_add_flag (type_with_fields, 0, "FIZ");
tdesc_add_flag (type_with_fields, 1, "AH");
@@ -155,57 +169,58 @@ create_feature_aarch64_sve (struct target_desc *result, long regnum,
tdesc_add_flag (type_with_fields, 25, "DN");
tdesc_add_flag (type_with_fields, 26, "AHP");
- tdesc_create_reg (feature, "z0", regnum++, 1, NULL, 128 * scale, "svev");
- tdesc_create_reg (feature, "z1", regnum++, 1, NULL, 128 * scale, "svev");
- tdesc_create_reg (feature, "z2", regnum++, 1, NULL, 128 * scale, "svev");
- tdesc_create_reg (feature, "z3", regnum++, 1, NULL, 128 * scale, "svev");
- tdesc_create_reg (feature, "z4", regnum++, 1, NULL, 128 * scale, "svev");
- tdesc_create_reg (feature, "z5", regnum++, 1, NULL, 128 * scale, "svev");
- tdesc_create_reg (feature, "z6", regnum++, 1, NULL, 128 * scale, "svev");
- tdesc_create_reg (feature, "z7", regnum++, 1, NULL, 128 * scale, "svev");
- tdesc_create_reg (feature, "z8", regnum++, 1, NULL, 128 * scale, "svev");
- tdesc_create_reg (feature, "z9", regnum++, 1, NULL, 128 * scale, "svev");
- tdesc_create_reg (feature, "z10", regnum++, 1, NULL, 128 * scale, "svev");
- tdesc_create_reg (feature, "z11", regnum++, 1, NULL, 128 * scale, "svev");
- tdesc_create_reg (feature, "z12", regnum++, 1, NULL, 128 * scale, "svev");
- tdesc_create_reg (feature, "z13", regnum++, 1, NULL, 128 * scale, "svev");
- tdesc_create_reg (feature, "z14", regnum++, 1, NULL, 128 * scale, "svev");
- tdesc_create_reg (feature, "z15", regnum++, 1, NULL, 128 * scale, "svev");
- tdesc_create_reg (feature, "z16", regnum++, 1, NULL, 128 * scale, "svev");
- tdesc_create_reg (feature, "z17", regnum++, 1, NULL, 128 * scale, "svev");
- tdesc_create_reg (feature, "z18", regnum++, 1, NULL, 128 * scale, "svev");
- tdesc_create_reg (feature, "z19", regnum++, 1, NULL, 128 * scale, "svev");
- tdesc_create_reg (feature, "z20", regnum++, 1, NULL, 128 * scale, "svev");
- tdesc_create_reg (feature, "z21", regnum++, 1, NULL, 128 * scale, "svev");
- tdesc_create_reg (feature, "z22", regnum++, 1, NULL, 128 * scale, "svev");
- tdesc_create_reg (feature, "z23", regnum++, 1, NULL, 128 * scale, "svev");
- tdesc_create_reg (feature, "z24", regnum++, 1, NULL, 128 * scale, "svev");
- tdesc_create_reg (feature, "z25", regnum++, 1, NULL, 128 * scale, "svev");
- tdesc_create_reg (feature, "z26", regnum++, 1, NULL, 128 * scale, "svev");
- tdesc_create_reg (feature, "z27", regnum++, 1, NULL, 128 * scale, "svev");
- tdesc_create_reg (feature, "z28", regnum++, 1, NULL, 128 * scale, "svev");
- tdesc_create_reg (feature, "z29", regnum++, 1, NULL, 128 * scale, "svev");
- tdesc_create_reg (feature, "z30", regnum++, 1, NULL, 128 * scale, "svev");
- tdesc_create_reg (feature, "z31", regnum++, 1, NULL, 128 * scale, "svev");
+ regnum = 34;
+ tdesc_create_reg (feature, "z0", regnum++, 1, NULL, TDESC_REG_VARIABLE_SIZE, "svev");
+ tdesc_create_reg (feature, "z1", regnum++, 1, NULL, TDESC_REG_VARIABLE_SIZE, "svev");
+ tdesc_create_reg (feature, "z2", regnum++, 1, NULL, TDESC_REG_VARIABLE_SIZE, "svev");
+ tdesc_create_reg (feature, "z3", regnum++, 1, NULL, TDESC_REG_VARIABLE_SIZE, "svev");
+ tdesc_create_reg (feature, "z4", regnum++, 1, NULL, TDESC_REG_VARIABLE_SIZE, "svev");
+ tdesc_create_reg (feature, "z5", regnum++, 1, NULL, TDESC_REG_VARIABLE_SIZE, "svev");
+ tdesc_create_reg (feature, "z6", regnum++, 1, NULL, TDESC_REG_VARIABLE_SIZE, "svev");
+ tdesc_create_reg (feature, "z7", regnum++, 1, NULL, TDESC_REG_VARIABLE_SIZE, "svev");
+ tdesc_create_reg (feature, "z8", regnum++, 1, NULL, TDESC_REG_VARIABLE_SIZE, "svev");
+ tdesc_create_reg (feature, "z9", regnum++, 1, NULL, TDESC_REG_VARIABLE_SIZE, "svev");
+ tdesc_create_reg (feature, "z10", regnum++, 1, NULL, TDESC_REG_VARIABLE_SIZE, "svev");
+ tdesc_create_reg (feature, "z11", regnum++, 1, NULL, TDESC_REG_VARIABLE_SIZE, "svev");
+ tdesc_create_reg (feature, "z12", regnum++, 1, NULL, TDESC_REG_VARIABLE_SIZE, "svev");
+ tdesc_create_reg (feature, "z13", regnum++, 1, NULL, TDESC_REG_VARIABLE_SIZE, "svev");
+ tdesc_create_reg (feature, "z14", regnum++, 1, NULL, TDESC_REG_VARIABLE_SIZE, "svev");
+ tdesc_create_reg (feature, "z15", regnum++, 1, NULL, TDESC_REG_VARIABLE_SIZE, "svev");
+ tdesc_create_reg (feature, "z16", regnum++, 1, NULL, TDESC_REG_VARIABLE_SIZE, "svev");
+ tdesc_create_reg (feature, "z17", regnum++, 1, NULL, TDESC_REG_VARIABLE_SIZE, "svev");
+ tdesc_create_reg (feature, "z18", regnum++, 1, NULL, TDESC_REG_VARIABLE_SIZE, "svev");
+ tdesc_create_reg (feature, "z19", regnum++, 1, NULL, TDESC_REG_VARIABLE_SIZE, "svev");
+ tdesc_create_reg (feature, "z20", regnum++, 1, NULL, TDESC_REG_VARIABLE_SIZE, "svev");
+ tdesc_create_reg (feature, "z21", regnum++, 1, NULL, TDESC_REG_VARIABLE_SIZE, "svev");
+ tdesc_create_reg (feature, "z22", regnum++, 1, NULL, TDESC_REG_VARIABLE_SIZE, "svev");
+ tdesc_create_reg (feature, "z23", regnum++, 1, NULL, TDESC_REG_VARIABLE_SIZE, "svev");
+ tdesc_create_reg (feature, "z24", regnum++, 1, NULL, TDESC_REG_VARIABLE_SIZE, "svev");
+ tdesc_create_reg (feature, "z25", regnum++, 1, NULL, TDESC_REG_VARIABLE_SIZE, "svev");
+ tdesc_create_reg (feature, "z26", regnum++, 1, NULL, TDESC_REG_VARIABLE_SIZE, "svev");
+ tdesc_create_reg (feature, "z27", regnum++, 1, NULL, TDESC_REG_VARIABLE_SIZE, "svev");
+ tdesc_create_reg (feature, "z28", regnum++, 1, NULL, TDESC_REG_VARIABLE_SIZE, "svev");
+ tdesc_create_reg (feature, "z29", regnum++, 1, NULL, TDESC_REG_VARIABLE_SIZE, "svev");
+ tdesc_create_reg (feature, "z30", regnum++, 1, NULL, TDESC_REG_VARIABLE_SIZE, "svev");
+ tdesc_create_reg (feature, "z31", regnum++, 1, NULL, TDESC_REG_VARIABLE_SIZE, "svev");
tdesc_create_reg (feature, "fpsr", regnum++, 1, NULL, 32, "fpsr_flags");
tdesc_create_reg (feature, "fpcr", regnum++, 1, NULL, 32, "fpcr_flags");
- tdesc_create_reg (feature, "p0", regnum++, 1, NULL, 16 * scale, "svep");
- tdesc_create_reg (feature, "p1", regnum++, 1, NULL, 16 * scale, "svep");
- tdesc_create_reg (feature, "p2", regnum++, 1, NULL, 16 * scale, "svep");
- tdesc_create_reg (feature, "p3", regnum++, 1, NULL, 16 * scale, "svep");
- tdesc_create_reg (feature, "p4", regnum++, 1, NULL, 16 * scale, "svep");
- tdesc_create_reg (feature, "p5", regnum++, 1, NULL, 16 * scale, "svep");
- tdesc_create_reg (feature, "p6", regnum++, 1, NULL, 16 * scale, "svep");
- tdesc_create_reg (feature, "p7", regnum++, 1, NULL, 16 * scale, "svep");
- tdesc_create_reg (feature, "p8", regnum++, 1, NULL, 16 * scale, "svep");
- tdesc_create_reg (feature, "p9", regnum++, 1, NULL, 16 * scale, "svep");
- tdesc_create_reg (feature, "p10", regnum++, 1, NULL, 16 * scale, "svep");
- tdesc_create_reg (feature, "p11", regnum++, 1, NULL, 16 * scale, "svep");
- tdesc_create_reg (feature, "p12", regnum++, 1, NULL, 16 * scale, "svep");
- tdesc_create_reg (feature, "p13", regnum++, 1, NULL, 16 * scale, "svep");
- tdesc_create_reg (feature, "p14", regnum++, 1, NULL, 16 * scale, "svep");
- tdesc_create_reg (feature, "p15", regnum++, 1, NULL, 16 * scale, "svep");
- tdesc_create_reg (feature, "ffr", regnum++, 1, NULL, 16 * scale, "svep");
- tdesc_create_reg (feature, "vg", regnum++, 1, NULL, 64, "int");
+ tdesc_create_reg (feature, "p0", regnum++, 1, NULL, TDESC_REG_VARIABLE_SIZE, "svep");
+ tdesc_create_reg (feature, "p1", regnum++, 1, NULL, TDESC_REG_VARIABLE_SIZE, "svep");
+ tdesc_create_reg (feature, "p2", regnum++, 1, NULL, TDESC_REG_VARIABLE_SIZE, "svep");
+ tdesc_create_reg (feature, "p3", regnum++, 1, NULL, TDESC_REG_VARIABLE_SIZE, "svep");
+ tdesc_create_reg (feature, "p4", regnum++, 1, NULL, TDESC_REG_VARIABLE_SIZE, "svep");
+ tdesc_create_reg (feature, "p5", regnum++, 1, NULL, TDESC_REG_VARIABLE_SIZE, "svep");
+ tdesc_create_reg (feature, "p6", regnum++, 1, NULL, TDESC_REG_VARIABLE_SIZE, "svep");
+ tdesc_create_reg (feature, "p7", regnum++, 1, NULL, TDESC_REG_VARIABLE_SIZE, "svep");
+ tdesc_create_reg (feature, "p8", regnum++, 1, NULL, TDESC_REG_VARIABLE_SIZE, "svep");
+ tdesc_create_reg (feature, "p9", regnum++, 1, NULL, TDESC_REG_VARIABLE_SIZE, "svep");
+ tdesc_create_reg (feature, "p10", regnum++, 1, NULL, TDESC_REG_VARIABLE_SIZE, "svep");
+ tdesc_create_reg (feature, "p11", regnum++, 1, NULL, TDESC_REG_VARIABLE_SIZE, "svep");
+ tdesc_create_reg (feature, "p12", regnum++, 1, NULL, TDESC_REG_VARIABLE_SIZE, "svep");
+ tdesc_create_reg (feature, "p13", regnum++, 1, NULL, TDESC_REG_VARIABLE_SIZE, "svep");
+ tdesc_create_reg (feature, "p14", regnum++, 1, NULL, TDESC_REG_VARIABLE_SIZE, "svep");
+ tdesc_create_reg (feature, "p15", regnum++, 1, NULL, TDESC_REG_VARIABLE_SIZE, "svep");
+ tdesc_create_reg (feature, "ffr", regnum++, 1, NULL, TDESC_REG_VARIABLE_SIZE, "svep");
+ tdesc_create_reg (feature, "vg", regnum++, 1, NULL, 64, "int", true);
return regnum;
}
diff --git a/gdb/features/aarch64-sve.xml b/gdb/features/aarch64-sve.xml
new file mode 100644
index 000000000000..b843012b7927
--- /dev/null
+++ b/gdb/features/aarch64-sve.xml
@@ -0,0 +1,217 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2024 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.aarch64.sve">
+ <architecture>aarch64</architecture>
+ <vector id="svevqu" type="uint128">
+ <count id="svevq_count">
+ <!-- How many 128-bit elements there are in a z register: $vg / 2 -->
+ <math>
+ <apply>
+ <divide/>
+ <ci>85</ci>
+ <cn>2</cn>
+ </apply>
+ </math>
+ </count>
+ </vector>
+ <vector id="svevqs" type="int128">
+ <count idref="svevq_count"/>
+ </vector>
+ <vector id="svevdf" type="ieee_double">
+ <count id="svevd_count">
+ <!-- How many 64-bit elements there are in a z register: $vg -->
+ <math>
+ <ci>85</ci>
+ </math>
+ </count>
+ </vector>
+ <vector id="svevdu" type="uint64">
+ <count idref="svevd_count"/>
+ </vector>
+ <vector id="svevds" type="int64">
+ <count idref="svevd_count"/>
+ </vector>
+ <vector id="svevsf" type="ieee_single">
+ <count id="svevs_count">
+ <!-- How many 32-bit elements there are in a z register: $vg * 2 -->
+ <math>
+ <apply>
+ <times/>
+ <ci>85</ci>
+ <cn>2</cn>
+ </apply>
+ </math>
+ </count>
+ </vector>
+ <vector id="svevsu" type="uint32">
+ <count idref="svevs_count"/>
+ </vector>
+ <vector id="svevss" type="int32">
+ <count idref="svevs_count"/>
+ </vector>
+ <vector id="svevhf" type="ieee_half">
+ <count id="svevh_count">
+ <!-- How many 16-bit elements there are in a z register: $vg * 4 -->
+ <math>
+ <apply>
+ <times/>
+ <ci>85</ci>
+ <cn>4</cn>
+ </apply>
+ </math>
+ </count>
+ </vector>
+ <vector id="svevhu" type="uint16">
+ <count idref="svevh_count"/>
+ </vector>
+ <vector id="svevhs" type="int16">
+ <count idref="svevh_count"/>
+ </vector>
+ <vector id="svevbu" type="uint8">
+ <count id="svevb_count">
+ <!-- How many 8-bit elements there are in a z register: $vg * 8 -->
+ <math>
+ <apply>
+ <times/>
+ <ci>85</ci>
+ <cn>8</cn>
+ </apply>
+ </math>
+ </count>
+ </vector>
+ <vector id="svevbs" type="int8">
+ <count idref="svevb_count"/>
+ </vector>
+ <union id="svevnq">
+ <field name="u" type="svevqu"/>
+ <field name="s" type="svevqs"/>
+ </union>
+ <union id="svevnd">
+ <field name="f" type="svevdf"/>
+ <field name="u" type="svevdu"/>
+ <field name="s" type="svevds"/>
+ </union>
+ <union id="svevns">
+ <field name="f" type="svevsf"/>
+ <field name="u" type="svevsu"/>
+ <field name="s" type="svevss"/>
+ </union>
+ <union id="svevnh">
+ <field name="f" type="svevhf"/>
+ <field name="u" type="svevhu"/>
+ <field name="s" type="svevhs"/>
+ </union>
+ <union id="svevnb">
+ <field name="u" type="svevbu"/>
+ <field name="s" type="svevbs"/>
+ </union>
+ <union id="svev">
+ <field name="q" type="svevnq"/>
+ <field name="d" type="svevnd"/>
+ <field name="s" type="svevns"/>
+ <field name="h" type="svevnh"/>
+ <field name="b" type="svevnb"/>
+ </union>
+ <vector id="svep" type="uint8">
+ <count>
+ <!-- How many predicate elements there are in a p register: $vg * 4 -->
+ <math>
+ <apply>
+ <times/>
+ <ci>85</ci>
+ <cn>4</cn>
+ </apply>
+ </math>
+ </count>
+ </vector>
+ <flags id="fpsr_flags" size="4">
+ <field name="IOC" start="0" end="0" type="bool"/>
+ <field name="DZC" start="1" end="1" type="bool"/>
+ <field name="OFC" start="2" end="2" type="bool"/>
+ <field name="UFC" start="3" end="3" type="bool"/>
+ <field name="IXC" start="4" end="4" type="bool"/>
+ <field name="IDC" start="7" end="7" type="bool"/>
+ <field name="QC" start="27" end="27" type="bool"/>
+ <field name="V" start="28" end="28" type="bool"/>
+ <field name="C" start="29" end="29" type="bool"/>
+ <field name="Z" start="30" end="30" type="bool"/>
+ <field name="N" start="31" end="31" type="bool"/>
+ </flags>
+ <flags id="fpcr_flags" size="4">
+ <field name="FIZ" start="0" end="0" type="bool"/>
+ <field name="AH" start="1" end="1" type="bool"/>
+ <field name="NEP" start="2" end="2" type="bool"/>
+ <field name="IOE" start="8" end="8" type="bool"/>
+ <field name="DZE" start="9" end="9" type="bool"/>
+ <field name="OFE" start="10" end="10" type="bool"/>
+ <field name="UFE" start="11" end="11" type="bool"/>
+ <field name="IXE" start="12" end="12" type="bool"/>
+ <field name="EBF" start="13" end="13" type="bool"/>
+ <field name="IDE" start="15" end="15" type="bool"/>
+ <field name="Len" start="16" end="18" type="uint32"/>
+ <field name="FZ16" start="19" end="19" type="bool"/>
+ <field name="Stride" start="20" end="21" type="uint32"/>
+ <field name="RMode" start="22" end="23" type="uint32"/>
+ <field name="FZ" start="24" end="24" type="bool"/>
+ <field name="DN" start="25" end="25" type="bool"/>
+ <field name="AHP" start="26" end="26" type="bool"/>
+ </flags>
+ <reg name="z0" bitsize="-1" type="svev" regnum="34"/>
+ <reg name="z1" bitsize="-1" type="svev" regnum="35"/>
+ <reg name="z2" bitsize="-1" type="svev" regnum="36"/>
+ <reg name="z3" bitsize="-1" type="svev" regnum="37"/>
+ <reg name="z4" bitsize="-1" type="svev" regnum="38"/>
+ <reg name="z5" bitsize="-1" type="svev" regnum="39"/>
+ <reg name="z6" bitsize="-1" type="svev" regnum="40"/>
+ <reg name="z7" bitsize="-1" type="svev" regnum="41"/>
+ <reg name="z8" bitsize="-1" type="svev" regnum="42"/>
+ <reg name="z9" bitsize="-1" type="svev" regnum="43"/>
+ <reg name="z10" bitsize="-1" type="svev" regnum="44"/>
+ <reg name="z11" bitsize="-1" type="svev" regnum="45"/>
+ <reg name="z12" bitsize="-1" type="svev" regnum="46"/>
+ <reg name="z13" bitsize="-1" type="svev" regnum="47"/>
+ <reg name="z14" bitsize="-1" type="svev" regnum="48"/>
+ <reg name="z15" bitsize="-1" type="svev" regnum="49"/>
+ <reg name="z16" bitsize="-1" type="svev" regnum="50"/>
+ <reg name="z17" bitsize="-1" type="svev" regnum="51"/>
+ <reg name="z18" bitsize="-1" type="svev" regnum="52"/>
+ <reg name="z19" bitsize="-1" type="svev" regnum="53"/>
+ <reg name="z20" bitsize="-1" type="svev" regnum="54"/>
+ <reg name="z21" bitsize="-1" type="svev" regnum="55"/>
+ <reg name="z22" bitsize="-1" type="svev" regnum="56"/>
+ <reg name="z23" bitsize="-1" type="svev" regnum="57"/>
+ <reg name="z24" bitsize="-1" type="svev" regnum="58"/>
+ <reg name="z25" bitsize="-1" type="svev" regnum="59"/>
+ <reg name="z26" bitsize="-1" type="svev" regnum="60"/>
+ <reg name="z27" bitsize="-1" type="svev" regnum="61"/>
+ <reg name="z28" bitsize="-1" type="svev" regnum="62"/>
+ <reg name="z29" bitsize="-1" type="svev" regnum="63"/>
+ <reg name="z30" bitsize="-1" type="svev" regnum="64"/>
+ <reg name="z31" bitsize="-1" type="svev" regnum="65"/>
+ <reg name="fpsr" bitsize="32" type="fpsr_flags" regnum="66"/>
+ <reg name="fpcr" bitsize="32" type="fpcr_flags" regnum="67"/>
+ <reg name="p0" bitsize="-1" type="svep" regnum="68"/>
+ <reg name="p1" bitsize="-1" type="svep" regnum="69"/>
+ <reg name="p2" bitsize="-1" type="svep" regnum="70"/>
+ <reg name="p3" bitsize="-1" type="svep" regnum="71"/>
+ <reg name="p4" bitsize="-1" type="svep" regnum="72"/>
+ <reg name="p5" bitsize="-1" type="svep" regnum="73"/>
+ <reg name="p6" bitsize="-1" type="svep" regnum="74"/>
+ <reg name="p7" bitsize="-1" type="svep" regnum="75"/>
+ <reg name="p8" bitsize="-1" type="svep" regnum="76"/>
+ <reg name="p9" bitsize="-1" type="svep" regnum="77"/>
+ <reg name="p10" bitsize="-1" type="svep" regnum="78"/>
+ <reg name="p11" bitsize="-1" type="svep" regnum="79"/>
+ <reg name="p12" bitsize="-1" type="svep" regnum="80"/>
+ <reg name="p13" bitsize="-1" type="svep" regnum="81"/>
+ <reg name="p14" bitsize="-1" type="svep" regnum="82"/>
+ <reg name="p15" bitsize="-1" type="svep" regnum="83"/>
+ <reg name="ffr" bitsize="-1" type="svep" regnum="84"/>
+ <reg name="vg" bitsize="64" type="int" regnum="85"/>
+</feature>
diff --git a/gdbserver/configure.srv b/gdbserver/configure.srv
index 95359137f4f4..62de58d0c013 100644
--- a/gdbserver/configure.srv
+++ b/gdbserver/configure.srv
@@ -57,6 +57,7 @@ case "${gdbserver_host}" in
srv_tgtobj="${srv_tgtobj} $srv_linux_obj"
srv_linux_regsets=yes
srv_linux_thread_db=yes
+ srv_dwarf_reg_to_regnum=yes
ipa_obj="linux-aarch64-ipa.o"
ipa_obj="${ipa_obj} linux-aarch64-tdesc-ipa.o"
ipa_obj="${ipa_obj} arch/aarch64-ipa.o"
diff --git a/gdbserver/linux-aarch64-low.cc b/gdbserver/linux-aarch64-low.cc
index 998ad0a9d658..13c891d282ba 100644
--- a/gdbserver/linux-aarch64-low.cc
+++ b/gdbserver/linux-aarch64-low.cc
@@ -884,11 +884,11 @@ aarch64_adjust_register_sets (const struct aarch64_features &features)
break;
case NT_FPREGSET:
/* This is unavailable when SVE is present. */
- if (features.vq == 0)
+ if (!features.sve)
regset->size = sizeof (struct user_fpsimd_state);
break;
case NT_ARM_SVE:
- if (features.vq > 0)
+ if (features.sve)
regset->size = SVE_PT_SIZE (AARCH64_MAX_SVE_VQ, SVE_PT_REGS_SVE);
break;
case NT_ARM_PAC_MASK:
@@ -938,7 +938,7 @@ aarch64_target::low_arch_setup ()
struct aarch64_features features;
int pid = current_thread->id.pid ();
- features.vq = aarch64_sve_get_vq (tid);
+ features.sve = aarch64_sve_get_vq (tid) != 0;
/* A-profile PAC is 64-bit only. */
features.pauth = linux_get_hwcap (pid, 8) & AARCH64_HWCAP_PACA;
/* A-profile MTE is 64-bit only. */
diff --git a/gdbserver/linux-aarch64-tdesc.cc b/gdbserver/linux-aarch64-tdesc.cc
index 31ec7854cc0b..87f2a484ac32 100644
--- a/gdbserver/linux-aarch64-tdesc.cc
+++ b/gdbserver/linux-aarch64-tdesc.cc
@@ -37,10 +37,6 @@ aarch64_linux_read_description (const aarch64_features &features)
initialised. */
static std::unordered_map<aarch64_features, target_desc *> tdesc_aarch64_map;
- if (features.vq > AARCH64_MAX_SVE_VQ)
- error (_("VQ is %" PRIu64 ", maximum supported value is %d"), features.vq,
- AARCH64_MAX_SVE_VQ);
-
if (features.svq > AARCH64_MAX_SVE_VQ)
error (_("Streaming svq is %" PRIu8 ", maximum supported value is %d"),
features.svq,
@@ -60,8 +56,6 @@ aarch64_linux_read_description (const aarch64_features &features)
expedited_registers.push_back ("sp");
expedited_registers.push_back ("pc");
- if (features.vq > 0)
- expedited_registers.push_back ("vg");
if (features.svq > 0)
expedited_registers.push_back ("svg");
@@ -74,3 +68,14 @@ aarch64_linux_read_description (const aarch64_features &features)
return tdesc;
}
+
+/* See gdbsupport/tdesc.h. */
+
+int
+tdesc_dwarf_reg_to_regnum (const char *arch, int dwarf_reg)
+{
+ if (!strcmp (arch, "aarch64"))
+ return aarch64_dwarf_reg_to_regnum (dwarf_reg);
+
+ return -1;
+}
^ permalink raw reply [flat|nested] 16+ messages in thread
* [RFC PATCH v4 14/15] GDB, gdbserver: aarch64: Use VG instead of VQ
2024-11-02 2:56 [RFC PATCH v4 00/15] gdbserver improvements for AArch64 SVE support Thiago Jung Bauermann
` (12 preceding siblings ...)
2024-11-02 2:56 ` [RFC PATCH v4 13/15] GDB, gdbserver: aarch64: Convert SVE feature to use variable-size registers Thiago Jung Bauermann
@ 2024-11-02 2:56 ` Thiago Jung Bauermann
2024-11-02 2:56 ` [RFC PATCH v4 15/15] gdb/testsuite: Add test to exercise multi-threaded AArch64 SVE inferiors Thiago Jung Bauermann
14 siblings, 0 replies; 16+ messages in thread
From: Thiago Jung Bauermann @ 2024-11-02 2:56 UTC (permalink / raw)
To: gdb-patches
GDB uses VQ ("vector quotient", i.e. chunks of 128-bit values)
profusely, but it's actually easier for a debugger to work in terms of
VG ("vector granule", i.e. chunks of 64-bit values) because DWARF
defines the VG register.
The only place where we need to use VQ is with the SVE_PT_SIZE macro.
---
gdb/aarch64-linux-nat.c | 9 +++----
gdb/nat/aarch64-scalable-linux-ptrace.c | 33 ++++++++++++-------------
gdb/nat/aarch64-scalable-linux-ptrace.h | 12 ++++-----
gdbserver/linux-aarch64-low.cc | 2 +-
4 files changed, 27 insertions(+), 29 deletions(-)
diff --git a/gdb/aarch64-linux-nat.c b/gdb/aarch64-linux-nat.c
index 5e12a2e403f6..81caffe666ea 100644
--- a/gdb/aarch64-linux-nat.c
+++ b/gdb/aarch64-linux-nat.c
@@ -319,8 +319,7 @@ store_fpregs_to_thread (const struct regcache *regcache)
static void
fetch_sve_vg_from_thread (struct regcache *regcache)
{
- uint64_t vq = aarch64_sve_get_vq (regcache->ptid ().lwp ());
- uint64_t vg = sve_vg_from_vq (vq);
+ uint64_t vg = aarch64_sve_get_vg (regcache->ptid ().lwp ());
regcache->raw_supply (AARCH64_SVE_VG_REGNUM, &vg);
}
@@ -347,7 +346,7 @@ fetch_sveregs_from_thread (struct regcache *regcache)
static void
store_sve_vg_to_thread (struct regcache *regcache)
{
- if (!aarch64_sve_set_vq (regcache->ptid ().lwp (), regcache))
+ if (!aarch64_sve_set_vg (regcache->ptid ().lwp (), regcache))
perror_with_name (_ ("Unable to set VG register"));
}
@@ -909,7 +908,7 @@ aarch64_linux_nat_target::read_description ()
/* SVE/SSVE check. Reading VQ may return either the regular vector length
or the streaming vector length, depending on whether streaming mode is
active or not. */
- features.sve = aarch64_sve_get_vq (tid) != 0;
+ features.sve = aarch64_sve_get_vg (tid) != 0;
features.pauth = hwcap & AARCH64_HWCAP_PACA;
features.mte = hwcap2 & HWCAP2_MTE;
features.tls = aarch64_tls_register_count (tid);
@@ -1023,7 +1022,7 @@ aarch64_linux_nat_target::thread_architecture (ptid_t ptid)
the tdep. */
aarch64_gdbarch_tdep *tdep
= gdbarch_tdep<aarch64_gdbarch_tdep> (inf->arch ());
- bool sve = aarch64_sve_get_vq (ptid.lwp ()) != 0;
+ bool sve = aarch64_sve_get_vg (ptid.lwp ()) != 0;
uint64_t svq = aarch64_za_get_svq (ptid.lwp ());
if (sve == tdep->has_sve && svq == tdep->sme_svq)
return inf->arch ();
diff --git a/gdb/nat/aarch64-scalable-linux-ptrace.c b/gdb/nat/aarch64-scalable-linux-ptrace.c
index ac375b78541a..126cc5a29e28 100644
--- a/gdb/nat/aarch64-scalable-linux-ptrace.c
+++ b/gdb/nat/aarch64-scalable-linux-ptrace.c
@@ -217,7 +217,7 @@ aarch64_sve_vl_valid (const bool sve_state, size_t vl)
/* See nat/aarch64-scalable-linux-ptrace.h. */
uint64_t
-aarch64_sve_get_vq (int tid)
+aarch64_sve_get_vg (int tid)
{
struct iovec iovec;
struct user_sve_header header;
@@ -234,10 +234,10 @@ aarch64_sve_get_vq (int tid)
return 0;
}
- /* Ptrace gives the vector length in bytes. Convert it to VQ, the number of
- 128bit chunks in a Z register. We use VQ because 128 bits is the minimum
- a Z register can increase in size. */
- uint64_t vq = sve_vq_from_vl (header.vl);
+ /* Ptrace gives the vector length in bytes. Convert it to VG, the number
+ of 64-bit chunks in a Z register. We use VG because DWARF defines a VG
+ pseudo-register. */
+ uint64_t vg = sve_vg_from_vl (header.vl);
if (!aarch64_sve_vl_valid (has_sve_state, header.vl))
{
@@ -245,13 +245,13 @@ aarch64_sve_get_vq (int tid)
return 0;
}
- return vq;
+ return vg;
}
/* See nat/aarch64-scalable-linux-ptrace.h. */
bool
-aarch64_sve_set_vq (int tid, uint64_t vq)
+aarch64_sve_set_vg (int tid, uint64_t vg)
{
struct iovec iovec;
struct user_sve_header header;
@@ -269,7 +269,7 @@ aarch64_sve_set_vq (int tid, uint64_t vq)
return false;
}
- header.vl = sve_vl_from_vq (vq);
+ header.vl = sve_vl_from_vg (vg);
if (ptrace (PTRACE_SETREGSET, tid, has_sve_state? NT_ARM_SVE : NT_ARM_SSVE,
&iovec) < 0)
@@ -284,7 +284,7 @@ aarch64_sve_set_vq (int tid, uint64_t vq)
/* See nat/aarch64-scalable-linux-ptrace.h. */
bool
-aarch64_sve_set_vq (int tid, struct reg_buffer_common *reg_buf)
+aarch64_sve_set_vg (int tid, struct reg_buffer_common *reg_buf)
{
uint64_t reg_vg = 0;
@@ -296,18 +296,16 @@ aarch64_sve_set_vq (int tid, struct reg_buffer_common *reg_buf)
{
/* If vg is not available yet, fetch it from ptrace. The VG value from
ptrace is likely the correct one. */
- uint64_t vq = aarch64_sve_get_vq (tid);
+ reg_vg = aarch64_sve_get_vg (tid);
/* If something went wrong, just bail out. */
- if (vq == 0)
+ if (reg_vg == 0)
return false;
-
- reg_vg = sve_vg_from_vq (vq);
}
else
reg_buf->raw_collect (AARCH64_SVE_VG_REGNUM, ®_vg);
- return aarch64_sve_set_vq (tid, sve_vq_from_vg (reg_vg));
+ return aarch64_sve_set_vg (tid, reg_vg);
}
/* See nat/aarch64-scalable-linux-ptrace.h. */
@@ -410,9 +408,9 @@ aarch64_za_set_svq (int tid, const struct reg_buffer_common *reg_buf,
gdb::byte_vector
aarch64_fetch_sve_regset (int tid)
{
- uint64_t vq = aarch64_sve_get_vq (tid);
+ uint64_t vg = aarch64_sve_get_vg (tid);
- if (vq == 0)
+ if (vg == 0)
perror_with_name (_("Unable to fetch SVE/SSVE vector length"));
/* A ptrace call with NT_ARM_SVE will return a header followed by either a
@@ -420,6 +418,7 @@ aarch64_fetch_sve_regset (int tid)
the one returned by NT_FPREGSET) if the kernel has not yet executed any
SVE code. Make sure we allocate enough space for a full SVE dump. */
+ uint64_t vq = sve_vq_from_vg (vg);
gdb::byte_vector sve_state (SVE_PT_SIZE (vq, SVE_PT_REGS_SVE), 0);
struct iovec iovec;
@@ -709,7 +708,7 @@ aarch64_sve_regs_copy_from_reg_buf (int tid,
{
/* First store the vector length to the thread. This is done first to
ensure the ptrace buffers read from the kernel are the correct size. */
- if (!aarch64_sve_set_vq (tid, reg_buf))
+ if (!aarch64_sve_set_vg (tid, reg_buf))
perror_with_name (_("Unable to set VG register"));
/* Obtain a dump of SVE registers from ptrace. */
diff --git a/gdb/nat/aarch64-scalable-linux-ptrace.h b/gdb/nat/aarch64-scalable-linux-ptrace.h
index 2be45588785e..61edbdf1f864 100644
--- a/gdb/nat/aarch64-scalable-linux-ptrace.h
+++ b/gdb/nat/aarch64-scalable-linux-ptrace.h
@@ -80,15 +80,15 @@ bool read_za_header (int tid, struct user_za_header &header);
Return true if successful, false otherwise. */
bool write_za_header (int tid, const struct user_za_header &header);
-/* Read VQ for the given tid using ptrace. If SVE is not supported then zero
- is returned (on a system that supports SVE, then VQ cannot be zero). */
-uint64_t aarch64_sve_get_vq (int tid);
+/* Read VG for the given tid using ptrace. If SVE is not supported then zero
+ is returned (on a system that supports SVE, then VG cannot be zero). */
+uint64_t aarch64_sve_get_vg (int tid);
-/* Set VQ in the kernel for the given tid, using either the value VQ or
+/* Set VG in the kernel for the given tid, using either the value VG or
reading from the register VG in the register buffer. */
-bool aarch64_sve_set_vq (int tid, uint64_t vq);
-bool aarch64_sve_set_vq (int tid, struct reg_buffer_common *reg_buf);
+bool aarch64_sve_set_vg (int tid, uint64_t vg);
+bool aarch64_sve_set_vg (int tid, struct reg_buffer_common *reg_buf);
/* Read the streaming mode vq (svq) for the given TID. If the ZA state is not
supported or active, return 0. */
diff --git a/gdbserver/linux-aarch64-low.cc b/gdbserver/linux-aarch64-low.cc
index 13c891d282ba..c781e0246e49 100644
--- a/gdbserver/linux-aarch64-low.cc
+++ b/gdbserver/linux-aarch64-low.cc
@@ -938,7 +938,7 @@ aarch64_target::low_arch_setup ()
struct aarch64_features features;
int pid = current_thread->id.pid ();
- features.sve = aarch64_sve_get_vq (tid) != 0;
+ features.sve = aarch64_sve_get_vg (tid) != 0;
/* A-profile PAC is 64-bit only. */
features.pauth = linux_get_hwcap (pid, 8) & AARCH64_HWCAP_PACA;
/* A-profile MTE is 64-bit only. */
^ permalink raw reply [flat|nested] 16+ messages in thread
* [RFC PATCH v4 15/15] gdb/testsuite: Add test to exercise multi-threaded AArch64 SVE inferiors
2024-11-02 2:56 [RFC PATCH v4 00/15] gdbserver improvements for AArch64 SVE support Thiago Jung Bauermann
` (13 preceding siblings ...)
2024-11-02 2:56 ` [RFC PATCH v4 14/15] GDB, gdbserver: aarch64: Use VG instead of VQ Thiago Jung Bauermann
@ 2024-11-02 2:56 ` Thiago Jung Bauermann
14 siblings, 0 replies; 16+ messages in thread
From: Thiago Jung Bauermann @ 2024-11-02 2:56 UTC (permalink / raw)
To: gdb-patches
This testcase exercises two scenarios involving a multi-threaded
inferior.
In the first scenario, the secondary inferior thread changes its vector
length and then hits a breakpoint. GDB then examines the SVE state.
In the second one, the secondary inferior thread changes its vector
length and then the main thread hits a breakpoint. GDB then switches
to the secondary thread and examines the SVE state.
---
gdb/testsuite/gdb.arch/aarch64-sve-threads.c | 125 ++++++++++++++++++
.../gdb.arch/aarch64-sve-threads.exp | 80 +++++++++++
2 files changed, 205 insertions(+)
create mode 100644 gdb/testsuite/gdb.arch/aarch64-sve-threads.c
create mode 100644 gdb/testsuite/gdb.arch/aarch64-sve-threads.exp
diff --git a/gdb/testsuite/gdb.arch/aarch64-sve-threads.c b/gdb/testsuite/gdb.arch/aarch64-sve-threads.c
new file mode 100644
index 000000000000..6640bd01a282
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/aarch64-sve-threads.c
@@ -0,0 +1,125 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2024 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/>. */
+
+/* Exercise AArch64's Scalable Vector Extension in a multi-threaded program. */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <pthread.h>
+#include <semaphore.h>
+#include <sys/prctl.h>
+#include <unistd.h>
+
+/* For one of the tests, the thread needs to sleep after setting the vector
+ length. This variable is set by GDB. */
+volatile bool should_sleep = false;
+
+/* Used to signal to the main thread that the additional thread's vector length
+ was changed. */
+sem_t vl_changed;
+
+/* Start routine for the additional thread. Sets a new vector length, sleeps if
+ requested then restores the original vector length. */
+
+static void *
+thread_function (void *unused)
+{
+ unsigned int vl;
+ int rc;
+
+ rc = prctl (PR_SVE_GET_VL, 0, 0, 0, 0);
+ if (rc < 0)
+ {
+ perror ("FAILED to PR_SVE_GET_VL");
+ sem_post (&vl_changed);
+ return NULL;
+ }
+
+ vl = rc & PR_SVE_VL_LEN_MASK;
+
+ /* Decrease vector length by 16 bytes. */
+ vl -= 16;
+
+ rc = prctl (PR_SVE_SET_VL, vl, 0, 0, 0, 0);
+ if (rc < 0)
+ {
+ perror ("FAILED to PR_SVE_SET_VL");
+ sem_post (&vl_changed);
+ return NULL;
+ }
+
+ /* Let the main thread continue. */
+ rc = sem_post (&vl_changed);
+ if (rc != 0)
+ {
+ perror ("sem_post");
+ return NULL;
+ }
+
+ if (should_sleep)
+ sleep (10);
+
+ /* Restore original vector length. */
+ vl += 16; /* break here 1 */
+
+ rc = prctl (PR_SVE_SET_VL, vl, 0, 0, 0, 0);
+ if (rc < 0)
+ {
+ perror ("FAILED to PR_SVE_SET_VL");
+ return NULL;
+ }
+
+ return NULL; /* break here 2 */
+}
+
+int
+main (int argc, char **argv)
+{
+ pthread_t thread;
+ int rc;
+
+ rc = sem_init (&vl_changed, 0, 0);
+ if (rc != 0)
+ {
+ perror ("sem_init");
+ return 1;
+ }
+
+ rc = pthread_create (&thread, NULL, thread_function, NULL);
+ if (rc != 0)
+ {
+ perror ("pthread_create");
+ return 1;
+ }
+
+ /* Wait until the additional thread changes it's vector length. */
+ rc = sem_wait (&vl_changed);
+ if (rc != 0)
+ {
+ perror ("sem_wait");
+ return 1;
+ }
+
+ rc = pthread_join (thread, NULL); /* break here 3 */
+ if (rc != 0)
+ {
+ perror ("pthread_join");
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/gdb/testsuite/gdb.arch/aarch64-sve-threads.exp b/gdb/testsuite/gdb.arch/aarch64-sve-threads.exp
new file mode 100644
index 000000000000..049eb24b1e6d
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/aarch64-sve-threads.exp
@@ -0,0 +1,80 @@
+# Copyright 2024 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 a multi-threaded binary that uses SVE and changes the SVE vector
+# length in the additional thread.
+
+require allow_aarch64_sve_tests
+
+standard_testfile
+if {[prepare_for_testing "failed to prepare" ${testfile} ${srcfile} \
+ {debug pthreads}] == -1} {
+ return -1
+}
+
+if ![runto_main] {
+ return -1
+}
+
+# Get the original VG value.
+set orig_vg [get_valueof "" {$vg} "0" "get value of VG register"]
+set expected_vg [expr {$orig_vg - 2}]
+
+# Stop after the additional thread has changed its vector length.
+gdb_breakpoint [gdb_get_line_number "break here 1"]
+gdb_continue_to_breakpoint "break here 1"
+
+# If GDB and gdbserver don't agree on the thread's vector length, this
+# command will fail.
+gdb_test "print \$z0" " = {q = {u = {.*}}}" "print z0 register"
+
+gdb_test "print \$vg" ". = ${expected_vg}" "vg was changed to ${expected_vg}"
+
+# Stop after the additional thread has restored its original vector length.
+gdb_breakpoint [gdb_get_line_number "break here 2"]
+gdb_continue_to_breakpoint "break here 2"
+
+# Test that going back to the original vector length doesn't confuse GDB or
+# gdbserver.
+gdb_test "print \$z0" " = {q = {u = {.*}}}" "print z0 register again"
+
+gdb_test "print \$vg" ". = ${orig_vg}" "vg was changed back to ${orig_vg}"
+
+# Restart GDB to test a scenario where GDB switches to a thread that
+# changed its vector length but hasn't hit any breakpoints yet.
+clean_restart ${binfile}
+
+if ![runto_main] {
+ return -1
+}
+
+# Make the thread sleep after changing its vector length.
+gdb_test_no_output -nopass "set var should_sleep = 1" "make thread sleep"
+
+# Stop in the main thread after the additional thread has changed its
+# vector length.
+gdb_breakpoint [gdb_get_line_number "break here 3"]
+gdb_continue_to_breakpoint "break here 3"
+
+# The regexp accounts for two lines of output after the "Switching to thread"
+# message.
+gdb_test_lines "thread 2" "switch to another thread" \
+ {\[Switching to thread 2 \(.*\)\]\r\n#0 [[:print:]]+}
+
+# Make sure everything is still fine.
+gdb_test "print \$z0" " = {q = {u = {.*}}}" "print z0 register in thread 2"
+
+gdb_test "print \$vg" ". = ${expected_vg}" \
+ "vg was changed to ${expected_vg} in thread 2"
^ permalink raw reply [flat|nested] 16+ messages in thread
end of thread, other threads:[~2024-11-02 2:57 UTC | newest]
Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-11-02 2:56 [RFC PATCH v4 00/15] gdbserver improvements for AArch64 SVE support Thiago Jung Bauermann
2024-11-02 2:56 ` [RFC PATCH v4 01/15] GDB, gdbserver: Convert regcache_register_size function to method Thiago Jung Bauermann
2024-11-02 2:56 ` [RFC PATCH v4 02/15] GDB: Use gdb::array_view for buffers used in register reading and unwinding Thiago Jung Bauermann
2024-11-02 2:56 ` [RFC PATCH v4 03/15] GDB: remote: Print total bytes received in debug message Thiago Jung Bauermann
2024-11-02 2:56 ` [RFC PATCH v4 04/15] GDB: trad-frame: Store length of value_bytes in trad_frame_saved_reg Thiago Jung Bauermann
2024-11-02 2:56 ` [RFC PATCH v4 05/15] gdbserver: nat/aarch64: Only check Z registers for extra contents if they can have it Thiago Jung Bauermann
2024-11-02 2:56 ` [RFC PATCH v4 06/15] gdbserver: Implement p and P packets Thiago Jung Bauermann
2024-11-02 2:56 ` [RFC PATCH v4 07/15] GDB, gdbserver: Create concept of load-early registers Thiago Jung Bauermann
2024-11-02 2:56 ` [RFC PATCH v4 08/15] GDB: Allow DWARF expression evaluation to use regcache Thiago Jung Bauermann
2024-11-02 2:56 ` [RFC PATCH v4 09/15] GDB tdesc: Add vector type with number of elements given by math expression Thiago Jung Bauermann
2024-11-02 2:56 ` [RFC PATCH v4 10/15] GDB: Add concept of variable-size registers to the regcache Thiago Jung Bauermann
2024-11-02 2:56 ` [RFC PATCH v4 11/15] gdbserver: " Thiago Jung Bauermann
2024-11-02 2:56 ` [RFC PATCH v4 12/15] GDB: aarch64-linux: Load and store VG separately from other SVE registers Thiago Jung Bauermann
2024-11-02 2:56 ` [RFC PATCH v4 13/15] GDB, gdbserver: aarch64: Convert SVE feature to use variable-size registers Thiago Jung Bauermann
2024-11-02 2:56 ` [RFC PATCH v4 14/15] GDB, gdbserver: aarch64: Use VG instead of VQ Thiago Jung Bauermann
2024-11-02 2:56 ` [RFC PATCH v4 15/15] gdb/testsuite: Add test to exercise multi-threaded AArch64 SVE inferiors Thiago Jung Bauermann
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).