public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
From: Aditya Kamath1 <Aditya.Kamath1@ibm.com>
To: Ulrich Weigand <Ulrich.Weigand@de.ibm.com>,
	"gdb-patches@sourceware.org" <gdb-patches@sourceware.org>,
	"simark@simark.ca" <simark@simark.ca>
Cc: Sangamesh Mallayya <sangamesh.swamy@in.ibm.com>
Subject: Re: [PATCH] Enable vector instruction debugging for AIX
Date: Mon, 6 Mar 2023 13:45:55 +0000	[thread overview]
Message-ID: <CH2PR15MB3544D484ACE103AAC5299A1ED6B69@CH2PR15MB3544.namprd15.prod.outlook.com> (raw)
In-Reply-To: <cd3122b9d075c0a7e27404c14ee35a39b80277f2.camel@de.ibm.com>


[-- Attachment #1.1: Type: text/plain, Size: 9541 bytes --]

Hi Ulrich and community,

Please find attached the patch. {See: 0001-Enable-vector-instruction-debugging-for-AIX.patch}.

Kindly push this patch is there are no further changes.

Have a nice day ahead.

Thanks and regards,
Aditya.

 >GDB no longer supports AIX 4.x and AIX 5.1.  The minimum supported
 >AIX version is now AIX 5.2.

>As a separate note, I see that even AIX 5.2 itself has been out of
>support for over 13 years now, so I'm wondering how much sense it
>makes to still claim support for this in GDB.  If we're already
>deprecating old versions, maybe it would be better to set the
>minimum supported version at 6.1 or even 7.1 at this point?

So I connected with my seniors on this. So AIX folks are using 7.1 now. So I have changed this to minimum 7.x.. Kindly see it in this patch. For those who are below 7.1, we are not supporting.

>-RS/6000 AIX 4.x                                       rs6000-*-aix4*
>+RS/6000 AIX 5.2 or higher                     rs6000-*-aix4*

>This describes what was added in GDB 4.18 - I don't think this
>section should be retroactively modified.

This is not modified anymore. Kindly see the patch.

>+static void
>+supply_altivec_regs (struct regcache *regcache, __vmx_context_t vmx)
>+{
>+  ppc_gdbarch_tdep *tdep
>+    = gdbarch_tdep<ppc_gdbarch_tdep> (regcache->arch ());
>+  int regno;
>+  int num_of_vrregs = tdep->ppc_vrsave_regnum - tdep->ppc_vr0_regnum + 1;
>+  for (regno = 0; regno < num_of_vrregs; regno++)
>+     regcache->raw_supply (tdep->ppc_vr0_regnum + regno,
>+                         &(vmx.__vr[regno]));

>Now that you've taken VRSAVE and VSCR separately, this loop now
>only needs to execute 32 times, but you still do it 34 times.
>I think you should just use ppc_num_vrs instead of this
>num_of_vrregs you compute here.
This is done.

>+      if (REG_VALID == regcache->get_register_status (tdep->ppc_vrsave_regnum))
>+      {
>+      ctx.vmx.__vrsave = vmx.__vrsave;
>+      ctx.vmx.__vscr = vmx.__vscr;

>Also should do a separate REG_VALID check.
This is done.

>@@ -1766,6 +1979,56 @@ store_regs_kernel_thread (const struct regcache *regcache, int regno,
>+    /* vmx registers.  */
>Should be VSX ?
Yes. This is done.

>+    if (tdep->ppc_vsr0_upper_regnum != -1  && tdep->ppc_vrsave_regnum != -1
>+       && (regno == -1 || (regno >= tdep->ppc_vsr0_upper_regnum
>+       && regno <= tdep->ppc_vrsave_regnum)))

>The regno comparison looks inconsistent (lower bound VSX; upper bound VMX).
This is done. It was an error.

>+  for (i = 0; i < num_of_vrregs; i++)
>+    regcache->raw_supply (tdep->ppc_vr0_regnum + i,
>+                                    &(vmx->__vr[i]));

>Same loop count problem here.

>+  regcache->raw_supply (tdep->ppc_vrsave_regnum, &(vmx->__vrsave));
>+  regcache->raw_supply (tdep->ppc_vrsave_regnum - 1, &(vmx->__vscr));

This is done.

>+  /*Alti-vec register.  */
Missing space
>+  /*VSX register.  */
>Here as well.
This is done.

>Fix coding style ({ should be indented).
This is done. I have eliminated all RHS space and aligned the flower brackets wherever I saw it was not aligned.

From: Ulrich Weigand <Ulrich.Weigand@de.ibm.com>
Date: Friday, 3 March 2023 at 8:29 PM
To: gdb-patches@sourceware.org <gdb-patches@sourceware.org>, Aditya Kamath1 <Aditya.Kamath1@ibm.com>, simark@simark.ca <simark@simark.ca>
Cc: Sangamesh Mallayya <sangamesh.swamy@in.ibm.com>
Subject: Re: [PATCH] Enable vector instruction debugging for AIX
Aditya Kamath1 <Aditya.Kamath1@ibm.com> wrote:

>Please find attached the patch. {See: 0001-Enable-vector-instruction-debugging-for-AIX.patch}.

This should be nearly ready to commit, I've just found a
few minor issues - see below.

Also, please provide commit message.

>--- a/gdb/NEWS
>+++ b/gdb/NEWS
>@@ -3,6 +3,8 @@
>
> *** Changes since GDB 13
>
>+* AIX now needs to be of version 5.2 or higher to install GDB.
>+
> * Multi-target feature configuration
>
>   GDB now supports the individual configuration of remote targets' feature

I think these are usually under a separate section, something like:

 * Removed targets and native configurations

 GDB no longer supports AIX 4.x and AIX 5.1.  The minimum supported
 AIX version is now AIX 5.2.

As a separate note, I see that even AIX 5.2 itself has been out of
support for over 13 years now, so I'm wondering how much sense it
makes to still claim support for this in GDB.  If we're already
deprecating old versions, maybe it would be better to set the
minimum supported version at 6.1 or even 7.1 at this point?

>@@ -7606,10 +7608,10 @@ the possible architectures.
>
> Windows 95, x86 Windows NT                    i[345]86-*-cygwin32
> M68K NetBSD                                   m68k-*-netbsd*
>-PowerPC AIX 4.x                                       powerpc-*-aix*
>+PowerPC AIX 5.2 or higher                     powerpc-*-aix*
> PowerPC MacOS                                 powerpc-*-macos*
> PowerPC Windows NT                            powerpcle-*-cygwin32
>-RS/6000 AIX 4.x                                       rs6000-*-aix4*
>+RS/6000 AIX 5.2 or higher                     rs6000-*-aix4*

This describes what was added in GDB 4.18 - I don't think this
section should be retroactively modified.

>+static void
>+supply_altivec_regs (struct regcache *regcache, __vmx_context_t vmx)
>+{
>+  ppc_gdbarch_tdep *tdep
>+    = gdbarch_tdep<ppc_gdbarch_tdep> (regcache->arch ());
>+  int regno;
>+  int num_of_vrregs = tdep->ppc_vrsave_regnum - tdep->ppc_vr0_regnum + 1;
>+  for (regno = 0; regno < num_of_vrregs; regno++)
>+     regcache->raw_supply (tdep->ppc_vr0_regnum + regno,
>+                         &(vmx.__vr[regno]));

Now that you've taken VRSAVE and VSCR separately, this loop now
only needs to execute 32 times, but you still do it 34 times.
I think you should just use ppc_num_vrs instead of this
num_of_vrregs you compute here.

>+  regcache->raw_supply (tdep->ppc_vrsave_regnum, &(vmx.__vrsave));
>+  regcache->raw_supply (tdep->ppc_vrsave_regnum - 1, &(vmx.__vscr));

>+  /* vector registers.  */
>+  if (tdep->ppc_vr0_regnum != -1)
>+    {
>+      int ret = 0;
>+      int num_of_vrregs = tdep->ppc_vrsave_regnum - tdep->ppc_vr0_regnum + 1;
>+      __vmx_context_t vmx;
>+      if (data->arch64)
>+      ret = ptrace64aix (PTT_READ_VEC, tid, (long long) &vmx, 0, 0);
>+      else
>+      ret = ptrace32 (PTT_READ_VEC, tid, (uintptr_t) &vmx, 0, 0);
>+      if (ret < 0)
>+      memset(&vmx, 0, sizeof(__vmx_context_t));
>+      for (i = 0; i < num_of_vrregs; i++)
>+      regcache->raw_supply (tdep->ppc_vr0_regnum + i, &(vmx.__vr[i]));

Same problem here.

>+      regcache->raw_supply (tdep->ppc_vrsave_regnum, &(vmx.__vrsave));
>+      regcache->raw_supply (tdep->ppc_vrsave_regnum - 1, &(vmx.__vscr));

>+static void
>+fill_altivec (const struct regcache *regcache, __vmx_context_t *vmx)
>+{
>+  struct gdbarch *gdbarch = regcache->arch ();
>+  ppc_gdbarch_tdep *tdep = gdbarch_tdep<ppc_gdbarch_tdep> (gdbarch);
>+  int num_of_vrregs = tdep->ppc_vrsave_regnum - tdep->ppc_vr0_regnum + 1;
>+  int regno;
>+
>+  for (regno = 0; regno < num_of_vrregs; regno++)
>+    if (REG_VALID == regcache->get_register_status (tdep->ppc_vr0_regnum + regno))
>+      regcache->raw_collect (tdep->ppc_vr0_regnum + regno,
>+                                 &(vmx->__vr[regno]));

And here.

>+  if (REG_VALID == regcache->get_register_status (tdep->ppc_vrsave_regnum))
>+  {
>+    regcache->raw_collect (tdep->ppc_vrsave_regnum, &(vmx->__vrsave));
>+    regcache->raw_collect (tdep->ppc_vrsave_regnum - 1, &(vmx->__vscr));

You should separately check VSCR for REG_VALID.

>+  int num_of_vrregs = tdep->ppc_vrsave_regnum - tdep->ppc_vr0_regnum + 1;

Same problem here.

>+      if (REG_VALID == regcache->get_register_status (tdep->ppc_vrsave_regnum))
>+      {
>+      ctx.vmx.__vrsave = vmx.__vrsave;
>+      ctx.vmx.__vscr = vmx.__vscr;

Also should do a separate REG_VALID check.

>@@ -1766,6 +1979,56 @@ store_regs_kernel_thread (const struct regcache *regcache, int regno,
>+    /* vmx registers.  */
Should be VSX ?
>+    if (tdep->ppc_vsr0_upper_regnum != -1  && tdep->ppc_vrsave_regnum != -1
>+       && (regno == -1 || (regno >= tdep->ppc_vsr0_upper_regnum
>+       && regno <= tdep->ppc_vrsave_regnum)))

The regno comparison looks inconsistent (lower bound VSX; upper bound VMX).

>+supply_vrregset_aix (struct regcache *regcache, __vmx_context_t *vmx)
>+{
>+  int i;
>+  struct gdbarch *gdbarch = regcache->arch ();
>+  ppc_gdbarch_tdep *tdep = gdbarch_tdep<ppc_gdbarch_tdep> (gdbarch);
>+  int num_of_vrregs = tdep->ppc_vrsave_regnum - tdep->ppc_vr0_regnum + 1;
>+
>+  for (i = 0; i < num_of_vrregs; i++)
>+    regcache->raw_supply (tdep->ppc_vr0_regnum + i,
>+                                    &(vmx->__vr[i]));

Same loop count problem here.

>+  regcache->raw_supply (tdep->ppc_vrsave_regnum, &(vmx->__vrsave));
>+  regcache->raw_supply (tdep->ppc_vrsave_regnum - 1, &(vmx->__vscr));

>+  /*Alti-vec register.  */
Missing space
>+  /*VSX register.  */
Here as well.

>+  if (altivec_register_p (gdbarch, regno))
>+  {
>+    store_altivec_register_aix (regcache, regno);
>+    return;
>+  }
>+
>+  if (vsx_register_p (gdbarch, regno))
>+  {
>+    store_vsx_register_aix (regcache, regno);
>+    return;
>+  }
Fix coding style ({ should be indented).


Bye,
Ulrich

[-- Attachment #2: 0001-Enable-vector-instruction-debugging-for-AIX.patch --]
[-- Type: application/octet-stream, Size: 26306 bytes --]

From f68dc88d480fd94186348062a841aa15403b3c55 Mon Sep 17 00:00:00 2001
From: Aditya Vidyadhar Kamath <Aditya.Kamath1@ibm.com>
Date: Mon, 6 Mar 2023 07:34:14 -0600
Subject: [PATCH] Enable vector instruction debugging for AIX

AIX now supports vector register contents debugging for both VMX
VSX registers.
---
 gdb/NEWS              |   3 +
 gdb/aix-thread.c      | 258 +++++++++++++++++++++++++++++++++++++++++-
 gdb/rs6000-aix-nat.c  | 222 ++++++++++++++++++++++++++++++++++++
 gdb/rs6000-aix-tdep.c | 182 +++++++++++++++++++++++++++++
 4 files changed, 664 insertions(+), 1 deletion(-)

diff --git a/gdb/NEWS b/gdb/NEWS
index 75cd11b204e..e38c5dc64c5 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -938,6 +938,9 @@ info sources
 
 ARM Symbian			arm*-*-symbianelf*
 
+GDB no longer supports AIX 4.x, AIX 5.x and AIX 6.x.  The minimum supported
+AIX version is now AIX 7.1.
+
 * New remote packets
 
 qMemTags
diff --git a/gdb/aix-thread.c b/gdb/aix-thread.c
index 4184c87a92b..e465b49738b 100644
--- a/gdb/aix-thread.c
+++ b/gdb/aix-thread.c
@@ -301,7 +301,16 @@ ptrace_check (int req, int id, int ret)
 			req, id, ret, errno);
 	  return ret == -1 ? 0 : 1;
 	}
-      break;
+	break;
+     case PTT_READ_VEC:
+     case PTT_READ_VSX:
+        if (debug_aix_thread)
+            gdb_printf (gdb_stdlog,
+                        "ptrace (%d, %d) = %d (errno = %d)\n",
+                        req, id, ret, errno);
+	if (ret == -1)
+	  return -1;
+	break;
     }
   error (_("aix-thread: ptrace (%d, %d) returned %d (errno = %d %s)"),
 	 req, id, ret, errno, safe_strerror (errno));
@@ -475,6 +484,42 @@ pdc_read_regs (pthdb_user_t user_current_pid,
 	  memcpy (&context->msr, &sprs32, sizeof(sprs32));
 	}
     }  
+
+  /* vector registers.  */
+  __vmx_context_t vmx;
+  if (__power_vmx() && (flags & PTHDB_FLAG_REGS))
+  {
+    if (data->arch64)
+      {
+	if (!ptrace64aix (PTT_READ_VEC, tid, (long long) &vmx, 0, 0))
+	  memset (&vmx, 0, sizeof (vmx));
+	memcpy (&context->vmx, &vmx, sizeof(__vmx_context_t));
+      }
+    else
+      {
+	if (!ptrace32 (PTT_READ_VEC, tid, (long long) &vmx, 0, 0))
+	  memset (&vmx, 0, sizeof (vmx));
+	memcpy (&context->vmx, &vmx, sizeof(__vmx_context_t));
+      }
+  }
+
+  /* vsx registers.  */
+  __vsx_context_t vsx;
+  if (__power_vsx() && (flags & PTHDB_FLAG_REGS))
+  {
+    if (data->arch64)
+    {
+	if (!ptrace64aix (PTT_READ_VSX, tid, (long long) &vsx, 0, 0))
+	  memset (&vsx, 0, sizeof (vsx));
+	memcpy (&context->vsx, &vsx, sizeof(__vsx_context_t));
+    }
+    else
+    {
+      if (!ptrace32 (PTT_READ_VSX, tid, (long long) &vsx, 0, 0))
+	memset (&vsx, 0, sizeof (vsx));
+      memcpy (&context->vsx, &vsx, sizeof(__vsx_context_t));
+    }
+  }
   return 0;
 }
 
@@ -531,6 +576,24 @@ pdc_write_regs (pthdb_user_t user_current_pid,
 	  ptrace32 (PTT_WRITE_SPRS, tid, (uintptr_t) &context->msr, 0, NULL);
 	}
     }
+
+  /* vector registers.  */
+  if (__power_vmx() && (flags & PTHDB_FLAG_REGS))
+  {
+    if (data->arch64)
+      ptrace64aix (PTT_WRITE_VEC, tid, (unsigned long) &context->vmx, 0, 0);
+    else
+      ptrace32 (PTT_WRITE_VEC, tid, (uintptr_t) &context->vmx, 0, 0);
+  }
+
+  /* vsx registers.  */
+  if (__power_vsx() && (flags & PTHDB_FLAG_REGS))
+  {
+    if (data->arch64)
+      ptrace64aix (PTT_WRITE_VSX, tid, (unsigned long) &context->vsx, 0, 0);
+    else
+      ptrace32 (PTT_WRITE_VSX, tid, (uintptr_t) &context->vsx, 0, 0);
+  }
   return 0;
 }
 
@@ -1172,6 +1235,35 @@ aix_thread_target::wait (ptid_t ptid, struct target_waitstatus *status,
   return pd_update (ptid.pid ());
 }
 
+/* Supply AIX altivec registers, both 64 and 32 bit.  */
+
+static void 
+supply_altivec_regs (struct regcache *regcache, __vmx_context_t vmx)
+{
+  ppc_gdbarch_tdep *tdep
+    = gdbarch_tdep<ppc_gdbarch_tdep> (regcache->arch ());
+  int regno;
+  for (regno = 0; regno < ppc_num_vrs; regno++)
+     regcache->raw_supply (tdep->ppc_vr0_regnum + regno,
+			   &(vmx.__vr[regno]));
+  regcache->raw_supply (tdep->ppc_vrsave_regnum, &(vmx.__vrsave));
+  regcache->raw_supply (tdep->ppc_vrsave_regnum - 1, &(vmx.__vscr));
+}
+
+/* Supply AIX VSX registers, both 64 and 32 bit.  */
+
+static void
+supply_vsx_regs (struct regcache *regcache, __vsx_context_t vsx)
+{
+  ppc_gdbarch_tdep *tdep
+    = gdbarch_tdep<ppc_gdbarch_tdep> (regcache->arch ());
+  int regno;
+
+  for (regno = 0; regno < ppc_num_vshrs; regno++)
+     regcache->raw_supply (tdep->ppc_vsr0_upper_regnum + regno,
+                           &(vsx.__vsr_dw1[regno]));
+}
+
 /* Record that the 64-bit general-purpose registers contain VALS.  */
 
 static void
@@ -1321,6 +1413,12 @@ fetch_regs_user_thread (struct regcache *regcache, pthdb_pthread_t pdtid)
   else
     supply_sprs32 (regcache, ctx.iar, ctx.msr, ctx.cr, ctx.lr, ctx.ctr,
 			     ctx.xer, ctx.fpscr);
+
+  /* Altivec registers.  */
+  supply_altivec_regs (regcache, ctx.vmx);
+
+  /* VSX registers.  */
+  supply_vsx_regs (regcache, ctx.vsx);
 }
 
 /* Fetch register REGNO if != -1 or all registers otherwise from
@@ -1380,6 +1478,38 @@ fetch_regs_kernel_thread (struct regcache *regcache, int regno,
 	}
     }
 
+  /* vector registers.  */
+  if (tdep->ppc_vr0_regnum != -1)
+    {
+      int ret = 0;
+      __vmx_context_t vmx;
+      if (data->arch64)
+	ret = ptrace64aix (PTT_READ_VEC, tid, (long long) &vmx, 0, 0);
+      else
+	ret = ptrace32 (PTT_READ_VEC, tid, (uintptr_t) &vmx, 0, 0);
+      if (ret < 0)
+	memset(&vmx, 0, sizeof(__vmx_context_t));
+      for (i = 0; i < ppc_num_vrs; i++)
+	regcache->raw_supply (tdep->ppc_vr0_regnum + i, &(vmx.__vr[i]));
+      regcache->raw_supply (tdep->ppc_vrsave_regnum, &(vmx.__vrsave));
+      regcache->raw_supply (tdep->ppc_vrsave_regnum - 1, &(vmx.__vscr));
+    }
+
+  /* vsx registers.  */
+  if (tdep->ppc_vsr0_upper_regnum != -1)
+    {
+      __vsx_context_t vsx;
+      int ret = 0;
+      if (data->arch64)
+	ret = ptrace64aix (PTT_READ_VSX, tid, (long long) &vsx, 0, 0);
+      else
+	ret = ptrace32 (PTT_READ_VSX, tid, (long long) &vsx, 0, 0);
+      if (ret < 0)
+        memset(&vsx, 0, sizeof(__vsx_context_t));
+      for (i = 0; i < ppc_num_vshrs; i++)
+        regcache->raw_supply (tdep->ppc_vsr0_upper_regnum + i, &(vsx.__vsr_dw1[i]));
+    }
+
   /* Floating-point registers.  */
 
   if (ppc_floating_point_unit_p (gdbarch)
@@ -1447,6 +1577,41 @@ aix_thread_target::fetch_registers (struct regcache *regcache, int regno)
     }
 }
 
+/* Fill altivec registers.  */
+
+static void
+fill_altivec (const struct regcache *regcache, __vmx_context_t *vmx)
+{
+  struct gdbarch *gdbarch = regcache->arch ();
+  ppc_gdbarch_tdep *tdep = gdbarch_tdep<ppc_gdbarch_tdep> (gdbarch);
+  int regno;
+
+  for (regno = 0; regno < ppc_num_vrs; regno++)
+    if (REG_VALID == regcache->get_register_status (tdep->ppc_vr0_regnum + regno))
+      regcache->raw_collect (tdep->ppc_vr0_regnum + regno,
+				     &(vmx->__vr[regno]));
+
+  if (REG_VALID == regcache->get_register_status (tdep->ppc_vrsave_regnum))
+    regcache->raw_collect (tdep->ppc_vrsave_regnum, &(vmx->__vrsave));
+  if (REG_VALID == regcache->get_register_status (tdep->ppc_vrsave_regnum - 1))
+    regcache->raw_collect (tdep->ppc_vrsave_regnum - 1, &(vmx->__vscr));
+}
+
+/* Fill vsx registers. */
+
+static void
+fill_vsx (const struct regcache *regcache, __vsx_context_t  *vsx)
+{
+  struct gdbarch *gdbarch = regcache->arch ();
+  ppc_gdbarch_tdep *tdep = gdbarch_tdep<ppc_gdbarch_tdep> (gdbarch);
+  int regno;
+
+  for (regno = 0; regno < ppc_num_vshrs; regno++)
+    if (REG_VALID == regcache->get_register_status ( tdep->ppc_vsr0_upper_regnum + regno))
+      regcache->raw_collect (tdep->ppc_vsr0_upper_regnum + regno,
+                                   &(vsx->__vsr_dw1[0]) + regno);
+}
+
 /* Store the gp registers into an array of uint32_t or uint64_t.  */
 
 static void
@@ -1582,6 +1747,9 @@ store_regs_user_thread (const struct regcache *regcache, pthdb_pthread_t pdtid)
   uint64_t int64;
   struct aix_thread_variables *data;
   data = get_thread_data_helper_for_ptid (inferior_ptid);
+  int ret;
+  __vmx_context_t vmx;
+  __vsx_context_t  vsx;
 
   if (debug_aix_thread)
     gdb_printf (gdb_stdlog, 
@@ -1594,6 +1762,44 @@ store_regs_user_thread (const struct regcache *regcache, pthdb_pthread_t pdtid)
     error (_("aix-thread: store_registers: pthdb_pthread_context returned %s"),
 	   pd_status2str (status));
 
+  /* Fill altivec-registers.  */
+
+  if (__power_vmx())
+  {
+    memset(&vmx, 0, sizeof(__vmx_context_t));
+    if (data->arch64)
+    {
+      for (i = 0; i < ppc_num_vrs; i++)
+	if (REG_VALID == regcache->get_register_status (tdep->ppc_vr0_regnum + i))
+	  {
+	    regcache->raw_collect (tdep->ppc_vr0_regnum + i,
+				   &(vmx.__vr[i]));
+	    ctx.vmx.__vr[i] = vmx.__vr[i];
+	  }
+      if (REG_VALID == regcache->get_register_status (tdep->ppc_vrsave_regnum))
+	ctx.vmx.__vrsave = vmx.__vrsave;
+      if (REG_VALID == regcache->get_register_status (tdep->ppc_vrsave_regnum - 1))
+	ctx.vmx.__vscr = vmx.__vscr;
+    }
+  }
+
+  /* Fill vsx registers. */
+
+  if (__power_vsx())
+  {
+    memset(&vsx, 0, sizeof(__vsx_context_t));
+    if (data->arch64)
+    {
+      for (i = 0; i < ppc_num_vshrs; i++)
+	 if (REG_VALID == regcache->get_register_status (tdep->ppc_vsr0_regnum + i))
+	 {
+	   regcache->raw_collect (tdep->ppc_vr0_regnum + i,
+				      &(vsx.__vsr_dw1[i]));
+	   ctx.vsx.__vsr_dw1[i] = vsx.__vsr_dw1[i];
+	 }
+    }
+  }
+
   /* Collect general-purpose register values from the regcache.  */
 
   for (i = 0; i < ppc_num_gprs; i++)
@@ -1674,6 +1880,7 @@ store_regs_kernel_thread (const struct regcache *regcache, int regno,
   struct ptxsprs sprs64;
   struct ptsprs  sprs32;
   struct aix_thread_variables *data;
+  int ret = 0;
 
   data = get_thread_data_helper_for_ptid (regcache->ptid ());
 
@@ -1766,6 +1973,55 @@ store_regs_kernel_thread (const struct regcache *regcache, int regno,
 	  ptrace32 (PTT_WRITE_SPRS, tid, (uintptr_t) &sprs32, 0, NULL);
 	}
     }
+    
+    /* Vector registers.  */
+    if (tdep->ppc_vr0_regnum != -1 && tdep->ppc_vrsave_regnum != -1
+	&& (regno == -1 || (regno >= tdep->ppc_vr0_regnum
+	&& regno <= tdep->ppc_vrsave_regnum)))
+    {
+      __vmx_context_t vmx;
+      if (__power_vmx())
+      {
+	if (data->arch64)
+	  ret = ptrace64aix (PTT_READ_VEC, tid, (long long) &vmx, 0, 0);
+	else
+	  ret = ptrace32 (PTT_READ_VEC, tid, (long long) &vmx, 0, 0);
+        if (ret > 0)
+        {
+	  fill_altivec(regcache, &vmx);
+	  if (data->arch64)
+	    ret = ptrace64aix (PTT_WRITE_VEC, tid, (long long) &vmx, 0, 0);
+	  else
+	    ret = ptrace32 (PTT_WRITE_VEC, tid, (long long) &vmx, 0, 0);
+	  if (ret < 0)
+	    perror_with_name (_("Unable to store AltiVec register after read"));
+	}
+      }
+    }
+
+    /* VSX registers.  */
+    if (tdep->ppc_vsr0_upper_regnum != -1 && (regno == -1
+	|| (regno >=tdep->ppc_vsr0_upper_regnum)))
+    {
+      __vsx_context_t vsx;
+      if (__power_vsx())
+      {
+	if (data->arch64)
+	  ret =  ptrace64aix (PTT_READ_VSX, tid, (long long) &vsx, 0, 0);
+	else
+	  ret =  ptrace32 (PTT_READ_VSX, tid, (long long) &vsx, 0, 0);
+	if (ret > 0)
+	{
+	  fill_vsx (regcache, &vsx);
+	  if (data->arch64)
+	    ret = ptrace64aix (PTT_WRITE_VSX, tid, (long long) &vsx, 0, 0);
+	  else
+	    ret = ptrace32 (PTT_WRITE_VSX, tid, (long long) &vsx, 0, 0);
+	  if (ret < 0)
+	    perror_with_name (_("Unable to store VSX register after read"));
+	}
+      }
+    }
 }
 
 /* Store gdb's current view of the register set into the
diff --git a/gdb/rs6000-aix-nat.c b/gdb/rs6000-aix-nat.c
index 8c9dc1a6e68..8cd96778992 100644
--- a/gdb/rs6000-aix-nat.c
+++ b/gdb/rs6000-aix-nat.c
@@ -58,6 +58,14 @@
 #include <procinfo.h>
 #include <sys/types.h>
 
+/* Header files for alti-vec reg.  */
+#include <sys/context.h>
+
+#include "features/rs6000/powerpc-vsx64.c"
+#include "features/rs6000/powerpc-vsx32.c"
+#include "features/rs6000/powerpc-altivec32.c"
+#include "features/rs6000/powerpc-altivec64.c"
+
 /* On AIX4.3+, sys/ldr.h provides different versions of struct ld_info for
    debugging 32-bit and 64-bit processes.  Define a typedef and macros for
    accessing fields in the appropriate structures.  */
@@ -99,6 +107,8 @@ class rs6000_nat_target final : public inf_ptrace_target
      support.  */
   void follow_fork (inferior *, ptid_t, target_waitkind, bool, bool) override;
 
+  const struct target_desc *read_description ()  override;
+
 protected:
 
   void post_startup_inferior (ptid_t ptid) override;
@@ -272,6 +282,166 @@ rs6000_ptrace64 (int req, int id, long long addr, int data, void *buf)
   return ret;
 }
 
+/* Store the vsx registers.  */
+
+static void
+store_vsx_register_aix (struct regcache *regcache, int regno)
+{
+  int ret;
+  struct gdbarch *gdbarch = regcache->arch ();
+  ppc_gdbarch_tdep *tdep = gdbarch_tdep<ppc_gdbarch_tdep> (gdbarch);
+  struct thrdentry64 thrdentry;
+  __vsx_context_t vsx;
+  pid_t pid = inferior_ptid.pid ();
+  tid64_t thrd_i = 0;
+
+  if (getthrds64(pid, &thrdentry, sizeof(struct thrdentry64),
+                                           &thrd_i, 1) == 1)
+    thrd_i = thrdentry.ti_tid;
+
+  memset(&vsx, 0, sizeof(__vsx_context_t));
+  if (__power_vsx() && thrd_i > 0)
+  {
+    if (ARCH64 ())
+      ret = rs6000_ptrace64 (PTT_READ_VSX, thrd_i, (long long) &vsx, 0, 0);
+    else
+      ret = rs6000_ptrace32 (PTT_READ_VSX, thrd_i, (int *)&vsx, 0, 0);
+    if (ret < 0)
+      return;
+
+    regcache->raw_collect (regno, &(vsx.__vsr_dw1[0])+
+                           regno - tdep->ppc_vsr0_upper_regnum);
+
+    if (ARCH64 ())
+      ret = rs6000_ptrace64 (PTT_WRITE_VSX, thrd_i, (long long) &vsx, 0, 0);
+    else
+      ret = rs6000_ptrace32 (PTT_WRITE_VSX, thrd_i, (int *) &vsx, 0, 0);
+
+    if (ret < 0)
+      perror_with_name (_("Unable to write VSX registers after reading it"));
+  }
+}
+
+/* Store Altivec registers.  */
+
+static void
+store_altivec_register_aix (struct regcache *regcache, int regno)
+{
+  int ret;
+  struct gdbarch *gdbarch = regcache->arch ();
+  ppc_gdbarch_tdep *tdep = gdbarch_tdep<ppc_gdbarch_tdep> (gdbarch);
+  struct thrdentry64 thrdentry;
+  __vmx_context_t vmx;
+  pid_t pid = inferior_ptid.pid ();
+  tid64_t  thrd_i = 0;
+
+  if (getthrds64(pid, &thrdentry, sizeof(struct thrdentry64),
+                                            &thrd_i, 1) == 1)
+    thrd_i = thrdentry.ti_tid;
+
+  memset(&vmx, 0, sizeof(__vmx_context_t));
+  if (__power_vmx() && thrd_i > 0)
+  {
+    if (ARCH64 ())
+      ret = rs6000_ptrace64 (PTT_READ_VEC, thrd_i, (long long) &vmx, 0, 0);
+    else
+      ret = rs6000_ptrace32 (PTT_READ_VEC, thrd_i, (int *) &vmx, 0, 0);
+    if (ret < 0)
+      return;
+
+    regcache->raw_collect (regno, &(vmx.__vr[0]) + regno
+                                  - tdep->ppc_vr0_regnum);
+
+    if (ARCH64 ())
+      ret = rs6000_ptrace64 (PTT_WRITE_VEC, thrd_i, (long long) &vmx, 0, 0);
+    else
+      ret = rs6000_ptrace32 (PTT_WRITE_VEC, thrd_i, (int *) &vmx, 0, 0);
+    if (ret < 0)
+      perror_with_name (_("Unable to store AltiVec register after reading it"));
+  }
+
+}
+
+/* Supply altivec registers.  */
+
+static void
+supply_vrregset_aix (struct regcache *regcache, __vmx_context_t *vmx)
+{
+  int i;
+  struct gdbarch *gdbarch = regcache->arch ();
+  ppc_gdbarch_tdep *tdep = gdbarch_tdep<ppc_gdbarch_tdep> (gdbarch);
+  int num_of_vrregs = tdep->ppc_vrsave_regnum - tdep->ppc_vr0_regnum + 1;
+
+  for (i = 0; i < num_of_vrregs; i++)
+    regcache->raw_supply (tdep->ppc_vr0_regnum + i,
+                                    &(vmx->__vr[i]));
+  regcache->raw_supply (tdep->ppc_vrsave_regnum, &(vmx->__vrsave));
+  regcache->raw_supply (tdep->ppc_vrsave_regnum - 1, &(vmx->__vscr));
+}
+
+/* Fetch altivec register.  */
+
+static void
+fetch_altivec_registers_aix (struct regcache *regcache)
+{
+  struct thrdentry64 thrdentry;
+  __vmx_context_t vmx;
+  pid_t pid = current_inferior ()->pid;
+  tid64_t  thrd_i = 0;
+
+  if (getthrds64(pid, &thrdentry, sizeof(struct thrdentry64),
+                               &thrd_i, 1) == 1)
+    thrd_i = thrdentry.ti_tid;
+
+  memset(&vmx, 0, sizeof(__vmx_context_t));
+  if (__power_vmx() && thrd_i > 0)
+  {
+    if (ARCH64 ())
+      rs6000_ptrace64 (PTT_READ_VEC, thrd_i, (long long) &vmx, 0, 0);
+    else
+      rs6000_ptrace32 (PTT_READ_VEC, thrd_i, (int *) &vmx, 0, 0);
+    supply_vrregset_aix (regcache, &vmx);
+  }
+}
+
+/* supply vsx register.  */
+
+static void
+supply_vsxregset_aix (struct regcache *regcache, __vsx_context_t *vsx)
+{
+  int i;
+  struct gdbarch *gdbarch = regcache->arch ();
+  ppc_gdbarch_tdep *tdep = gdbarch_tdep<ppc_gdbarch_tdep> (gdbarch);
+
+  for (i = 0; i < ppc_num_vshrs; i++)
+   regcache->raw_supply (tdep->ppc_vsr0_upper_regnum + i,
+                                   &(vsx->__vsr_dw1[i]));
+}
+
+/* Fetch vsx registers.  */
+static void
+fetch_vsx_registers_aix (struct regcache *regcache)
+{
+  struct thrdentry64 thrdentry;
+  __vsx_context_t vsx;
+  pid_t pid = current_inferior ()->pid;
+  tid64_t  thrd_i = 0;
+
+  if (getthrds64(pid, &thrdentry, sizeof(struct thrdentry64),
+                                            &thrd_i, 1) == 1)
+    thrd_i = thrdentry.ti_tid;
+
+  memset(&vsx, 0, sizeof(__vsx_context_t));
+  if (__power_vsx() && thrd_i > 0)
+  {
+    if (ARCH64 ())
+      rs6000_ptrace64 (PTT_READ_VSX, thrd_i, (long long) &vsx, 0, 0);
+    else
+      rs6000_ptrace32 (PTT_READ_VSX, thrd_i, (int *) &vsx, 0, 0);
+    supply_vsxregset_aix (regcache, &vsx);
+  }
+}
+
 void rs6000_nat_target::post_startup_inferior (ptid_t ptid)
 {
 
@@ -326,6 +496,20 @@ fetch_register (struct regcache *regcache, int regno)
   /* Retrieved values may be -1, so infer errors from errno.  */
   errno = 0;
 
+  /* Alti-vec register.  */
+  if (altivec_register_p (gdbarch, regno))
+  {
+    fetch_altivec_registers_aix (regcache);
+    return;
+  }
+
+  /* VSX register.  */
+  if (vsx_register_p (gdbarch, regno))
+  {
+    fetch_vsx_registers_aix (regcache);
+    return;
+  }
+
   nr = regmap (gdbarch, regno, &isfloat);
 
   /* Floating-point registers.  */
@@ -388,6 +572,18 @@ store_register (struct regcache *regcache, int regno)
   /* -1 can be a successful return value, so infer errors from errno.  */
   errno = 0;
 
+  if (altivec_register_p (gdbarch, regno))
+  {
+    store_altivec_register_aix (regcache, regno);
+    return;
+  }
+
+  if (vsx_register_p (gdbarch, regno))
+  {
+    store_vsx_register_aix (regcache, regno);
+    return;
+  }
+
   nr = regmap (gdbarch, regno, &isfloat);
 
   /* Floating-point registers.  */
@@ -458,6 +654,12 @@ rs6000_nat_target::fetch_registers (struct regcache *regcache, int regno)
 	for (regno = 0; regno < ppc_num_fprs; regno++)
 	  fetch_register (regcache, tdep->ppc_fp0_regnum + regno);
 
+      if (tdep->ppc_vr0_regnum != -1 && tdep->ppc_vrsave_regnum != -1)
+        fetch_altivec_registers_aix (regcache);
+
+      if (tdep->ppc_vsr0_upper_regnum != -1)
+        fetch_vsx_registers_aix (regcache);
+
       /* Read special registers.  */
       fetch_register (regcache, gdbarch_pc_regnum (gdbarch));
       fetch_register (regcache, tdep->ppc_ps_regnum);
@@ -472,6 +674,26 @@ rs6000_nat_target::fetch_registers (struct regcache *regcache, int regno)
     }
 }
 
+const struct target_desc *
+rs6000_nat_target::read_description ()
+{
+   if (ARCH64())
+   {
+     if (__power_vsx ())
+       return tdesc_powerpc_vsx64;
+     else if (__power_vmx ())
+       return tdesc_powerpc_altivec64;
+   }
+   else
+   {
+     if (__power_vsx ())
+       return tdesc_powerpc_vsx32;
+     else if (__power_vmx ())
+       return tdesc_powerpc_altivec32;
+   }
+   return NULL;
+}
+
 /* Store our register values back into the inferior.
    If REGNO is -1, do this for all registers.
    Otherwise, REGNO specifies which register (so we can save time).  */
diff --git a/gdb/rs6000-aix-tdep.c b/gdb/rs6000-aix-tdep.c
index 15602c80b00..b71bbe750db 100644
--- a/gdb/rs6000-aix-tdep.c
+++ b/gdb/rs6000-aix-tdep.c
@@ -68,6 +68,178 @@
 /* Minimum possible text address in AIX.  */
 #define AIX_TEXT_SEGMENT_BASE 0x10000000
 
+struct rs6000_aix_reg_vrreg_offset
+{
+  int vr0_offset;
+  int vscr_offset;
+  int vrsave_offset;
+};
+
+static struct rs6000_aix_reg_vrreg_offset rs6000_aix_vrreg_offset =
+{
+   /* AltiVec registers.  */
+  32, /* vr0_offset */
+  544, /* vscr_offset. */
+  560 /* vrsave_offset */
+};
+
+static int
+rs6000_aix_get_vrreg_offset (ppc_gdbarch_tdep *tdep,
+  const struct rs6000_aix_reg_vrreg_offset *offsets,
+                                         int regnum)
+{
+  if (regnum >= tdep->ppc_vr0_regnum &&
+  regnum < tdep->ppc_vr0_regnum + ppc_num_vrs)
+    return offsets->vr0_offset + (regnum - tdep->ppc_vr0_regnum) * 16;
+
+  if (regnum == tdep->ppc_vrsave_regnum - 1)
+    return offsets->vscr_offset;
+
+  if (regnum == tdep->ppc_vrsave_regnum)
+    return offsets->vrsave_offset;
+
+  return -1;
+}
+
+static void
+rs6000_aix_supply_vrregset (const struct regset *regset, struct regcache *regcache,
+                                        int regnum, const void *vrregs, size_t len)
+{
+  struct gdbarch *gdbarch = regcache->arch ();
+  const struct rs6000_aix_reg_vrreg_offset  *offsets;
+  size_t offset;
+  ppc_gdbarch_tdep *tdep = gdbarch_tdep<ppc_gdbarch_tdep> (gdbarch);
+  if (!(tdep->ppc_vr0_regnum >= 0  && tdep->ppc_vrsave_regnum >= 0))
+    return;
+
+  offsets = (const struct rs6000_aix_reg_vrreg_offset *) regset->regmap;
+  if (regnum == -1)
+  {
+    int i;
+
+    for (i = tdep->ppc_vr0_regnum, offset = offsets->vr0_offset;
+                         i < tdep->ppc_vr0_regnum + ppc_num_vrs;
+                                              i++, offset += 16)
+        ppc_supply_reg (regcache, i, (const gdb_byte *) vrregs, offset, 16);
+
+    ppc_supply_reg (regcache, (tdep->ppc_vrsave_regnum - 1),
+        (const gdb_byte *) vrregs, offsets->vscr_offset, 4);
+
+    ppc_supply_reg (regcache, tdep->ppc_vrsave_regnum,
+    (const gdb_byte *) vrregs, offsets->vrsave_offset, 4);
+
+    return;
+  }
+  offset = rs6000_aix_get_vrreg_offset (tdep, offsets, regnum);
+  if (regnum != tdep->ppc_vrsave_regnum &&
+      regnum != tdep->ppc_vrsave_regnum - 1)
+    ppc_supply_reg (regcache, regnum, (const gdb_byte *) vrregs, offset, 16);
+  else
+    ppc_supply_reg (regcache, regnum,
+     (const gdb_byte *) vrregs, offset, 4);
+
+}
+
+static void
+rs6000_aix_supply_vsxregset (const struct regset *regset, struct regcache *regcache,
+                                        int regnum, const void *vsxregs, size_t len)
+{
+  struct gdbarch *gdbarch = regcache->arch ();
+  ppc_gdbarch_tdep *tdep = gdbarch_tdep<ppc_gdbarch_tdep> (gdbarch);
+  if (!(tdep->ppc_vsr0_regnum >= 0))
+    return;
+
+  if (regnum == -1)
+  {
+    int i, offset = 0;
+
+    for (i = tdep->ppc_vsr0_upper_regnum; i < tdep->ppc_vsr0_upper_regnum 
+                                                   + 32; i++, offset += 8)
+      ppc_supply_reg (regcache, i, (const gdb_byte *) vsxregs, offset, 8);
+
+    return;
+  }
+  else
+    ppc_supply_reg (regcache, regnum, (const gdb_byte *) vsxregs, 0, 8);
+}
+
+static void
+rs6000_aix_collect_vsxregset (const struct regset *regset,
+                          const struct regcache *regcache,
+                    int regnum, void *vsxregs, size_t len)
+{
+  struct gdbarch *gdbarch = regcache->arch ();
+  ppc_gdbarch_tdep *tdep = gdbarch_tdep<ppc_gdbarch_tdep> (gdbarch);
+  if (!(tdep->ppc_vsr0_regnum >= 0))
+    return;
+
+  if (regnum == -1)
+  {
+    int i;
+    int offset = 0;
+    for (i = tdep->ppc_vsr0_upper_regnum; i < tdep->ppc_vsr0_upper_regnum
+                                                  + 32; i++, offset += 8)
+      ppc_collect_reg (regcache, i, (gdb_byte *) vsxregs, offset, 8);
+
+    return;
+  }
+
+ else
+    ppc_collect_reg (regcache, regnum, (gdb_byte *) vsxregs, 0, 8);
+}
+
+static void
+rs6000_aix_collect_vrregset (const struct regset *regset,
+                         const struct regcache *regcache,
+                    int regnum, void *vrregs, size_t len)
+{
+  struct gdbarch *gdbarch = regcache->arch ();
+  const struct rs6000_aix_reg_vrreg_offset *offsets;
+  size_t offset;
+
+  ppc_gdbarch_tdep *tdep = gdbarch_tdep<ppc_gdbarch_tdep> (gdbarch);
+  if (!(tdep->ppc_vr0_regnum >= 0 && tdep->ppc_vrsave_regnum >= 0))
+    return;
+
+  offsets = (const struct rs6000_aix_reg_vrreg_offset *) regset->regmap;
+  if (regnum == -1)
+  {
+    int i;
+
+    for (i = tdep->ppc_vr0_regnum, offset = offsets->vr0_offset; i <
+              tdep->ppc_vr0_regnum + ppc_num_vrs; i++, offset += 16)
+      ppc_collect_reg (regcache, i, (gdb_byte *) vrregs, offset, 16);
+
+    ppc_collect_reg (regcache, (tdep->ppc_vrsave_regnum - 1),
+               (gdb_byte *) vrregs, offsets->vscr_offset, 4);
+
+    ppc_collect_reg (regcache, tdep->ppc_vrsave_regnum,
+             (gdb_byte *) vrregs, offsets->vrsave_offset, 4);
+
+    return;
+  }
+
+  offset = rs6000_aix_get_vrreg_offset (tdep, offsets, regnum);
+  if (regnum != tdep->ppc_vrsave_regnum
+      && regnum != tdep->ppc_vrsave_regnum - 1)
+    ppc_collect_reg (regcache, regnum, (gdb_byte *) vrregs, offset, 16);
+  else
+    ppc_collect_reg (regcache, regnum,
+                   (gdb_byte *) vrregs, offset, 4);
+}
+
+static const struct regset rs6000_aix_vrregset = {
+  &rs6000_aix_vrreg_offset,
+  rs6000_aix_supply_vrregset,
+  rs6000_aix_collect_vrregset
+};
+
+static const struct regset rs6000_aix_vsxregset = {
+  &rs6000_aix_vrreg_offset,
+  rs6000_aix_supply_vsxregset,
+  rs6000_aix_collect_vsxregset
+};
+
 static struct trad_frame_cache *
 aix_sighandle_frame_cache (frame_info_ptr this_frame,
 			   void **this_cache)
@@ -262,10 +434,20 @@ rs6000_aix_iterate_over_regset_sections (struct gdbarch *gdbarch,
 					 const struct regcache *regcache)
 {
   ppc_gdbarch_tdep *tdep = gdbarch_tdep<ppc_gdbarch_tdep> (gdbarch);
+  int have_altivec = tdep->ppc_vr0_regnum != -1;
+  int have_vsx = tdep->ppc_vsr0_upper_regnum != -1;
+
   if (tdep->wordsize == 4)
     cb (".reg", 592, 592, &rs6000_aix32_regset, NULL, cb_data);
   else
     cb (".reg", 576, 576, &rs6000_aix64_regset, NULL, cb_data);
+
+  if (have_altivec)
+   cb (".aix-vmx", 560, 560, &rs6000_aix_vrregset, "AIX altivec", cb_data);
+
+  if (have_vsx)
+   cb (".aix-vsx", 256, 256, &rs6000_aix_vsxregset, "AIX vsx", cb_data);
+
 }
 
 
-- 
2.38.3


  reply	other threads:[~2023-03-06 13:46 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-10-13 10:52 Aditya Kamath1
2022-10-13 10:52 ` Aditya Kamath1
2022-10-14 12:41 ` Ulrich Weigand
2022-10-28 10:35   ` Aditya Kamath1
2022-10-28 12:39     ` Ulrich Weigand
2022-11-14 16:26       ` Aditya Kamath1
2022-11-15 19:03         ` Ulrich Weigand
2023-02-23 12:49           ` Aditya Kamath1
2023-02-24 15:26             ` Ulrich Weigand
2023-03-01 12:34               ` Aditya Kamath1
2023-03-03 14:59                 ` Ulrich Weigand
2023-03-06 13:45                   ` Aditya Kamath1 [this message]
2023-03-07 10:25                     ` Ulrich Weigand
2023-03-07 12:13                       ` Aditya Kamath1
2023-03-07 12:55                         ` Ulrich Weigand
2023-03-07 13:24                           ` Aditya Kamath1
2023-03-07 15:53                             ` Ulrich Weigand
2023-03-09  2:21                               ` Aditya Kamath1

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=CH2PR15MB3544D484ACE103AAC5299A1ED6B69@CH2PR15MB3544.namprd15.prod.outlook.com \
    --to=aditya.kamath1@ibm.com \
    --cc=Ulrich.Weigand@de.ibm.com \
    --cc=gdb-patches@sourceware.org \
    --cc=sangamesh.swamy@in.ibm.com \
    --cc=simark@simark.ca \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).