public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc(refs/vendors/ARM/heads/morello)] Handle dwarf capability registers in unwind information
@ 2021-12-10 16:49 Matthew Malcomson
  0 siblings, 0 replies; only message in thread
From: Matthew Malcomson @ 2021-12-10 16:49 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:1f9e0742c2f9a489fc948311cc445f78badfb23b

commit 1f9e0742c2f9a489fc948311cc445f78badfb23b
Author: Matthew Malcomson <matthew.malcomson@arm.com>
Date:   Fri Dec 10 16:31:19 2021 +0000

    Handle dwarf capability registers in unwind information
    
    Requirements as far as the ABI is concerned are:
    1) We need to use the DWARF register numbers for capabilities in our CFI
       data structures.
    2) The unwinder must consume these registers and ensure that the
       capabilities are fully preserved.
    
    There is a slight implementation difficulty (which is a general
    complication around emitting DWARF information for capabilities).
    Inside GCC we represent capability registers with the same register
    number as non-capability registers but with a different mode, while the
    DWARF information represents capability registers with a different
    register number.
    
    One implementation of recording these capability registers would be to
    add columns to the unwinder register table to maintain these extra
    registers.  What we choose to do here is to record both the capability
    registers and the non-capability registers in the same unwinder
    column.
    
    This decision relies on the address registers being the low parts of the
    capability registers.  This is something we already rely on in GCC, and
    in this case we only rely on it for Morello (so it's less of a strong
    claim).
    
    We implement this mapping using the macro DWARF_REG_TO_UNWIND_COLUMN.
    This macro is also used inside GCC to define a builtin which libgcc uses
    to initialise sizes for the unwinder columns.  This use of the macro in
    two different contexts is a little tricky.  We would like to ensure that
    we only make the relevant transformation from the capability dwarf
    register numbers to the non-capability dwarf register numbers if we're
    targetting pure capability.  This check must be implemented in a
    different way depending on the context this macro is used in.  If the
    macro is used inside GCC then the check required is a runtime check that
    the user provided a given argument.  If the macro is used in libgcc then
    the check required is a compile-time check on what arguments this file
    was compiled with.
    
    It so happens that the fact we use the same register number inside GCC
    for capability and non-capability registers means we automatically
    create the unwinder register size table with the set of general
    registers having the size of a capability.
    
    The code in this area does require that the `reg_raw_mode` table --
    which defines the mode to be used for saving a given register -- should
    be updated to indicate that the general registers must be saved in a
    capability mode if one exists for them.  This makes the store of
    register modes throughout GCC described better on top of ensuring the
    behaviour we want in this case.
    
    In this patch we have left some of this work unfinished.  It just so
    happens to work for the milestone we're aiming at, and the extra work
    is required to make things sensible in the end.
    That extra work ties in with what's required for a different milestone
    in the future, which is why we're leaving it for now.
    
    This extra work is:
     - GCC needs to emit `.cfi_startproc purecap` instead of plain
       `.cfi_startproc` on purecap functions.
     - GCC needs to emit unwind rules on the *capability* registers rather
       than the address registers.
    
    Both of these require being able to tell the difference between a
    fake-capability target and a pure-capability target.  This is not yet
    available to the mid-end.
    
    Emitting `.cfi_startproc` without the `purecap` still works for two
    reasons:  1.  The assembler currently adds the "C" character based on
    the target architecture rather than the CFI annotation (n.b. I believe
    we should look into changing this behaviour).  2.  The unwinder doesn't
    base any decision on the existence of the "C" character in the
    augmentation string.
    
    Using the `x` DWARF register numbers in CFI directives happens to work
    because the unwinder uses the same unwind column for both, and does
    have any special logic to use address sized values for the `x`
    registers.  This is something that would be nice to have in the
    unwinder, but would only matter either in 1.  "broken" cases where
    someone emits a CFI directive using the `x` register in a purecap
    function, or 2.  unwinding past hybrid frames in a purecap binary.
    The second case is a valid use case, but it is not the priority right
    now and would require more work than just this point.

Diff:
---
 gcc/config/aarch64/aarch64.h | 38 ++++++++++++++++++++++++++++++++++++++
 gcc/reginfo.c                | 14 +++++++++++---
 gcc/regs.h                   |  6 +++---
 libgcc/unwind-dw2.c          | 10 ++++++++++
 4 files changed, 62 insertions(+), 6 deletions(-)

diff --git a/gcc/config/aarch64/aarch64.h b/gcc/config/aarch64/aarch64.h
index 81715803e48..0a60d78b289 100644
--- a/gcc/config/aarch64/aarch64.h
+++ b/gcc/config/aarch64/aarch64.h
@@ -625,6 +625,44 @@ extern unsigned aarch64_architecture_version;
 
 #define DWARF2_UNWIND_INFO 1
 
+/* When defining CFI unwinder register information we either choose to
+   represent `c` registers or `x` registers based on the ABI.  For purecap we
+   only represent `c` registers since saving and restoring those saves and
+   restores the entire register value.  For hybrid we only represent `x`
+   registers since those are the only part of the register that the PCS defines
+   as callee-saved.
+
+   DWARF has different register numbers for capability registers to standard
+   registers.  This means that the unwinder sees CFI descriptions of dwarf
+   registers 198-231 when we save and restore the `c` registers.  Rather than
+   add columns in the unwinder table, we re-use the entries that would
+   otherwise be used for the `x` registers.  Since these are not described in
+   purecap code they are not used
+
+   We only want to perform this transformation if it is necessary, so we do not
+   start treating invalid DWARF register numbers as valid ones.  The condition
+   with which to tell this is different whether the macro is being used in the
+   GCC context or in the libgcc context (this macro is used in both).
+   Hence we define one macro for the GCC context which makes a check at runtime
+   whether the user gave us a specific argument, and we define another macro
+   for the libgcc context that does not check anything at runtime but is
+   defined behind a compile-time check.
+
+   N.b. Mapping these `c` registers to the same columns as the `x` registers
+   could still work (as long as the unwinder agrees with this) if both `c` and
+   `x` registers were given unwind info.  This could work because of the same
+   overlapping between `x` and `c` registers that GCC relies on elsewhere.  */
+#ifdef IN_LIBGCC2
+#ifdef __CHERI_PURE_CAPABILITY__
+#define DWARF_REG_TO_UNWIND_COLUMN(REGNO) \
+  ((REGNO) >= 198 && (REGNO) <= 230) ? (REGNO) - 198 : (REGNO)
+#endif
+#else
+#define DWARF_REG_TO_UNWIND_COLUMN(REGNO) \
+  (TARGET_CAPABILITY_PURE && (REGNO) >= 198 && (REGNO) <= 230) \
+      ? (REGNO) - 198 : (REGNO)
+#endif
+
 /* Use R0 through R3 to pass exception handling information.  */
 #define EH_RETURN_DATA_REGNO(N) \
   ((N) < 4 ? ((unsigned int) R0_REGNUM + (N)) : INVALID_REGNUM)
diff --git a/gcc/reginfo.c b/gcc/reginfo.c
index e34b74af9f1..8fce62a684b 100644
--- a/gcc/reginfo.c
+++ b/gcc/reginfo.c
@@ -561,12 +561,20 @@ choose_hard_reg_mode (unsigned int regno ATTRIBUTE_UNUSED,
   unsigned int /* machine_mode */ m;
   machine_mode found_mode = VOIDmode, mode;
 
-  /* We first look for the largest integer mode that can be validly
-     held in REGNO.  If none, we look for the largest floating-point mode.
-     If we still didn't find a valid mode, try CCmode.
+  /* We first look for the largest capability mode that can be validly
+     held in REGNO.  If none, we look for the largest integer mode, then
+     largest floating-point mode.  If we still didn't find a valid mode, try
+     CCmode.
 
      The tests use maybe_gt rather than known_gt because we want (for example)
      N V4SFs to win over plain V4SF even though N might be 1.  */
+  FOR_EACH_MODE_IN_CLASS (mode, MODE_CAPABILITY)
+    if (hard_regno_nregs (regno, mode) == nregs
+	&& targetm.hard_regno_mode_ok (regno, mode)
+	&& (!abi || !abi->clobbers_reg_p (mode, regno))
+	&& maybe_gt (GET_MODE_SIZE (mode), GET_MODE_SIZE (found_mode)))
+      found_mode = mode;
+
   FOR_EACH_MODE_IN_CLASS (mode, MODE_INT)
     if (hard_regno_nregs (regno, mode) == nregs
 	&& targetm.hard_regno_mode_ok (regno, mode)
diff --git a/gcc/regs.h b/gcc/regs.h
index 1decd2c2d2a..004920081b8 100644
--- a/gcc/regs.h
+++ b/gcc/regs.h
@@ -202,9 +202,9 @@ struct target_regs {
   unsigned char x_hard_regno_nregs[FIRST_PSEUDO_REGISTER][MAX_MACHINE_MODE];
 
   /* For each hard register, the widest mode object that it can contain.
-     This will be a MODE_INT mode if the register can hold integers.  Otherwise
-     it will be a MODE_FLOAT or a MODE_CC mode, whichever is valid for the
-     register.  */
+     This will be a MODE_INT or MODE_CAPABILITY mode if the register can hold
+     integers or capabilities.  Otherwise it will be a MODE_FLOAT or a MODE_CC
+     mode, whichever is valid for the register.  */
   machine_mode x_reg_raw_mode[FIRST_PSEUDO_REGISTER];
 
   /* Vector indexed by machine mode saying whether there are regs of
diff --git a/libgcc/unwind-dw2.c b/libgcc/unwind-dw2.c
index d88b99e8f96..47c61424529 100644
--- a/libgcc/unwind-dw2.c
+++ b/libgcc/unwind-dw2.c
@@ -531,6 +531,16 @@ static _Unwind_CapWord
 execute_stack_op (const unsigned char *op_ptr, const unsigned char *op_end,
 		  struct _Unwind_Context *context, _Unwind_CapWord initial)
 {
+  /* N.b. We make no account for capability architectures which unwind past
+     frames in both non-capabiilty mode and capability mode.  I.e. frames which
+     only work with (and hence only record) changes to non-capability registers
+     and frames which directly work with capability registers.
+
+     For such targets we would need to adjust the size of object that we read
+     into the DWARF stack depending on the frame -- for some frames we would
+     want to read an address-sized object, for others we woud want to read a
+     capability-sized object.  */
+
   _Unwind_CapWord stack[64];	/* ??? Assume this is enough.  */
   int stack_elt;


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2021-12-10 16:49 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-12-10 16:49 [gcc(refs/vendors/ARM/heads/morello)] Handle dwarf capability registers in unwind information Matthew Malcomson

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).