Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: Andrew Cagney <ac131313@redhat.com>
To: Kevin Buettner <kevinb@redhat.com>
Cc: gdb-patches@sources.redhat.com,
	Jason R Thorpe <thorpej@wasabisystems.com>
Subject: Re: [rfa?] Implement ppc32 SYSV {extract,store} return value
Date: Fri, 10 Oct 2003 20:22:00 -0000	[thread overview]
Message-ID: <3F8714F5.2030605@redhat.com> (raw)
In-Reply-To: <1031006191231.ZM12230@localhost.localdomain>

[-- Attachment #1: Type: text/plain, Size: 1440 bytes --]

> On Oct 4,  1:43pm, Andrew Cagney wrote:

> The comment should say r3/r4, not r3/r3.  Likewise here:

Changed.

> +      if (inval)
> +	{
> +	  /* This matches SVr4 PPC, it does not match gcc.  */
> +	  /* The value is padded out to 8 bytes and then loaded, as
> +	     two "words" into r3/r3.  */
> 
> In a comment prior to do_ppc_sysv_return_value(), please specify
> precisely which ABIs this function covers.  The function name implies
> that it's only SysV, but it looks to me like it's for SysV, Altivec,
> and e500.  I don't think it's worth making the function name more
> verbose, but I do think it's important to list the other ABIs that
> someone looking at this function needs to consider.  Otherwise, a lot
> of it doesn't make sense.

Done, they call themselves "suplements".


> I realize that this was lifted from code that existed elsewhere before,
> but after comparing this to the e500 ABI doc that I have, this doesn't
> look right.  The ABI says:
> 
>     Functions shall return values of 64-bit DSP types (__ev64_opaque__)
>     in r3.

I suspect a cut/paste tipo.

> 	  /* The e500 ABI places return values for the 64-bit DSP types
> 	     (__ev64_opaque__) in r3.  However, in GDB-speak, ev3 corresponds
> 	     to the entire r3 value for e500, whereas r3 only corresponds
> 	     to the lower 32-bits.  So place the 64-bit DSP type's value in
> 	     ev3.  */

I've added a comment.

How is the attached?

Andrew


[-- Attachment #2: diffs --]
[-- Type: text/plain, Size: 17911 bytes --]

2003-10-10  Andrew Cagney  <cagney@redhat.com>

	* rs6000-tdep.c (e500_store_return_value): Delete function.
	(e500_extract_return_value): Delete function.
	(rs6000_gdbarch_init): When SYSV, set "extract_return_value" and
	"restore_return_value" to "ppc_sysv_abi_extract_return_value" and
	"ppc_sysv_abi_restore_return_value" where applicable.
	* ppc-tdep.h: (ppc_sysv_abi_store_return_value): Declare.
	(ppc_sysv_abi_extract_return_value): Declare.
	(ppc_sysv_abi_broken_store_return_value): Declare.
	(ppc_sysv_abi_broken_extract_return_value): Declare.
	(ppc_sysv_abi_broken_use_struct_convention:) Delete declaration.
	* ppc-sysv-tdep.c (return_value_convention): Move definition to
	start of file.
	(do_ppc_sysv_return_value): New function.
	(ppc_sysv_abi_extract_return_value): New function.
	(ppc_sysv_abi_store_return_value): New function.
	(ppc_sysv_abi_broken_extract_return_value): New function.
	(ppc_sysv_abi_broken_store_return_value): New function.
	(ppc_sysv_abi_use_struct_convention): Call
	do_ppc_sysv_return_value.

Index: ppc-sysv-tdep.c
===================================================================
RCS file: /cvs/src/src/gdb/ppc-sysv-tdep.c,v
retrieving revision 1.15
diff -u -r1.15 ppc-sysv-tdep.c
--- ppc-sysv-tdep.c	10 Oct 2003 18:29:13 -0000	1.15
+++ ppc-sysv-tdep.c	10 Oct 2003 20:19:53 -0000
@@ -305,16 +305,264 @@
   return sp;
 }
 
+/* Potential ways that a function can return a value of a given type.  */
+enum return_value_convention
+{
+  /* Where the return value has been squeezed into one or more
+     registers.  */
+  RETURN_VALUE_REGISTER_CONVENTION,
+  /* Commonly known as the "struct return convention".  The caller
+     passes an additional hidden first parameter to the caller.  That
+     parameter contains the address at which the value being returned
+     should be stored.  While typically, and historically, used for
+     large structs, this is convention is applied to values of many
+     different types.  */
+  RETURN_VALUE_STRUCT_CONVENTION
+};
+
+/* Handle the return-value conventions specified by the SysV 32-bit
+   PowerPC ABI (including all the supplements):
+
+   no floating-point: floating-point values returned using 32-bit
+   general-purpose registers.
+
+   Altivec: 128-bit vectors returned using vector registers.
+
+   e500: 64-bit vectors returned using the full full 64 bit EV
+   register, floating-point values returned using 32-bit
+   general-purpose registers.
+
+   GCC (broken): Small struct values right (instead of left) aligned
+   when returned in general-purpose registers.  */
+
+static enum return_value_convention
+do_ppc_sysv_return_value (struct type *type, struct regcache *regcache,
+			  const void *inval, void *outval, int broken_gcc)
+{
+  struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
+  if (TYPE_CODE (type) == TYPE_CODE_FLT
+      && TYPE_LENGTH (type) <= 8
+      && ppc_floating_point_unit_p (current_gdbarch))
+    {
+      if (outval)
+	{
+	  /* Floats and doubles stored in "f1".  Convert the value to
+	     the required type.  */
+	  char regval[MAX_REGISTER_SIZE];
+	  struct type *regtype = register_type (current_gdbarch,
+						FP0_REGNUM + 1);
+	  regcache_cooked_read (regcache, FP0_REGNUM + 1, regval);
+	  convert_typed_floating (regval, regtype, outval, type);
+	}
+      if (inval)
+	{
+	  /* Floats and doubles stored in "f1".  Convert the value to
+	     the register's "double" type.  */
+	  char regval[MAX_REGISTER_SIZE];
+	  struct type *regtype = register_type (current_gdbarch, FP0_REGNUM);
+	  convert_typed_floating (inval, type, regval, regtype);
+	  regcache_cooked_write (regcache, FP0_REGNUM + 1, regval);
+	}
+      return RETURN_VALUE_REGISTER_CONVENTION;
+    }
+  if ((TYPE_CODE (type) == TYPE_CODE_INT
+       && TYPE_LENGTH (type) == 8
+       && tdep->wordsize == 4)
+      || (TYPE_CODE (type) == TYPE_CODE_FLT
+	  && TYPE_LENGTH (type) == 8 && tdep->wordsize == 4))
+    {
+      if (outval)
+	{
+	  /* A long long, or a double stored in the 32 bit r3/r4.  */
+	  regcache_cooked_read (regcache, tdep->ppc_gp0_regnum + 3,
+				(bfd_byte *) outval + 0);
+	  regcache_cooked_read (regcache, tdep->ppc_gp0_regnum + 4,
+				(bfd_byte *) outval + 4);
+	}
+      if (inval)
+	{
+	  /* A long long, or a double stored in the 32 bit r3/r4.  */
+	  regcache_cooked_write (regcache, tdep->ppc_gp0_regnum + 3,
+				 (bfd_byte *) inval + 0);
+	  regcache_cooked_write (regcache, tdep->ppc_gp0_regnum + 4,
+				 (bfd_byte *) inval + 4);
+	}
+      return RETURN_VALUE_REGISTER_CONVENTION;
+    }
+  if (TYPE_CODE (type) == TYPE_CODE_INT
+      && TYPE_LENGTH (type) <= tdep->wordsize)
+    {
+      if (outval)
+	{
+	  /* Some sort of integer stored in r3.  Since TYPE isn't
+	     bigger than the register, sign extension isn't a problem
+	     - just do everything unsigned.  */
+	  ULONGEST regval;
+	  regcache_cooked_read_unsigned (regcache, tdep->ppc_gp0_regnum + 3,
+					 &regval);
+	  store_unsigned_integer (outval, TYPE_LENGTH (type), regval);
+	}
+      if (inval)
+	{
+	  /* Some sort of integer stored in r3.  Use unpack_long since
+	     that should handle any required sign extension.  */
+	  regcache_cooked_write_unsigned (regcache, tdep->ppc_gp0_regnum + 3,
+					  unpack_long (type, inval));
+	}
+      return RETURN_VALUE_REGISTER_CONVENTION;
+    }
+  if (TYPE_LENGTH (type) == 16
+      && TYPE_CODE (type) == TYPE_CODE_ARRAY
+      && TYPE_VECTOR (type) && tdep->ppc_vr0_regnum >= 0)
+    {
+      if (outval)
+	{
+	  /* Altivec places the return value in "v2".  */
+	  regcache_cooked_read (regcache, tdep->ppc_vr0_regnum + 2, outval);
+	}
+      if (inval)
+	{
+	  /* Altivec places the return value in "v2".  */
+	  regcache_cooked_write (regcache, tdep->ppc_vr0_regnum + 2, inval);
+	}
+      return RETURN_VALUE_REGISTER_CONVENTION;
+    }
+  if (TYPE_LENGTH (type) == 8
+      && TYPE_CODE (type) == TYPE_CODE_ARRAY
+      && TYPE_VECTOR (type) && tdep->ppc_ev0_regnum >= 0)
+    {
+      /* The e500 ABI places return values for the 64-bit DSP types
+	 (__ev64_opaque__) in r3.  However, in GDB-speak, ev3
+	 corresponds to the entire r3 value for e500, whereas GDB's r3
+	 only corresponds to the least significant 32-bits.  So place
+	 the 64-bit DSP type's value in ev3.  */
+      if (outval)
+	regcache_cooked_read (regcache, tdep->ppc_ev0_regnum + 3, outval);
+      if (inval)
+	regcache_cooked_write (regcache, tdep->ppc_ev0_regnum + 3, inval);
+      return RETURN_VALUE_REGISTER_CONVENTION;
+    }
+  if (broken_gcc && TYPE_LENGTH (type) <= 8)
+    {
+      if (outval)
+	{
+	  /* GCC screwed up.  The last register isn't "left" aligned.
+	     Need to extract the least significant part of each
+	     register and then store that.  */
+	  /* Transfer any full words.  */
+	  int word = 0;
+	  while (1)
+	    {
+	      ULONGEST reg;
+	      int len = TYPE_LENGTH (type) - word * tdep->wordsize;
+	      if (len <= 0)
+		break;
+	      if (len > tdep->wordsize)
+		len = tdep->wordsize;
+	      regcache_cooked_read_unsigned (regcache,
+					     tdep->ppc_gp0_regnum + 3 + word,
+					     &reg);
+	      store_unsigned_integer (((bfd_byte *) outval
+				       + word * tdep->wordsize), len, reg);
+	      word++;
+	    }
+	}
+      if (inval)
+	{
+	  /* GCC screwed up.  The last register isn't "left" aligned.
+	     Need to extract the least significant part of each
+	     register and then store that.  */
+	  /* Transfer any full words.  */
+	  int word = 0;
+	  while (1)
+	    {
+	      ULONGEST reg;
+	      int len = TYPE_LENGTH (type) - word * tdep->wordsize;
+	      if (len <= 0)
+		break;
+	      if (len > tdep->wordsize)
+		len = tdep->wordsize;
+	      reg = extract_unsigned_integer (((bfd_byte *) inval
+					       + word * tdep->wordsize), len);
+	      regcache_cooked_write_unsigned (regcache,
+					      tdep->ppc_gp0_regnum + 3 + word,
+					      reg);
+	      word++;
+	    }
+	}
+      return RETURN_VALUE_REGISTER_CONVENTION;
+    }
+  if (TYPE_LENGTH (type) <= 8)
+    {
+      if (outval)
+	{
+	  /* This matches SVr4 PPC, it does not match GCC.  */
+	  /* The value is right-padded to 8 bytes and then loaded, as
+	     two "words", into r3/r4.  */
+	  char regvals[MAX_REGISTER_SIZE * 2];
+	  regcache_cooked_read (regcache, tdep->ppc_gp0_regnum + 3,
+				regvals + 0 * tdep->wordsize);
+	  if (TYPE_LENGTH (type) > tdep->wordsize)
+	    regcache_cooked_read (regcache, tdep->ppc_gp0_regnum + 4,
+				  regvals + 1 * tdep->wordsize);
+	  memcpy (outval, regvals, TYPE_LENGTH (type));
+	}
+      if (inval)
+	{
+	  /* This matches SVr4 PPC, it does not match GCC.  */
+	  /* The value is padded out to 8 bytes and then loaded, as
+	     two "words" into r3/r4.  */
+	  char regvals[MAX_REGISTER_SIZE * 2];
+	  memset (regvals, 0, sizeof regvals);
+	  memcpy (regvals, inval, TYPE_LENGTH (type));
+	  regcache_cooked_write (regcache, tdep->ppc_gp0_regnum + 3,
+				 regvals + 0 * tdep->wordsize);
+	  if (TYPE_LENGTH (type) > tdep->wordsize)
+	    regcache_cooked_write (regcache, tdep->ppc_gp0_regnum + 4,
+				   regvals + 1 * tdep->wordsize);
+	}
+      return RETURN_VALUE_REGISTER_CONVENTION;
+    }
+  return RETURN_VALUE_STRUCT_CONVENTION;
+}
+
+void
+ppc_sysv_abi_extract_return_value (struct type *type,
+				   struct regcache *regcache, void *valbuf)
+{
+  do_ppc_sysv_return_value (type, regcache, NULL, valbuf, 0);
+}
+
+void
+ppc_sysv_abi_broken_extract_return_value (struct type *type,
+					  struct regcache *regcache,
+					  void *valbuf)
+{
+  do_ppc_sysv_return_value (type, regcache, NULL, valbuf, 1);
+}
+
+void
+ppc_sysv_abi_store_return_value (struct type *type, struct regcache *regcache,
+				 const void *valbuf)
+{
+  do_ppc_sysv_return_value (type, regcache, valbuf, NULL, 0);
+}
+
+void
+ppc_sysv_abi_broken_store_return_value (struct type *type,
+					struct regcache *regcache,
+					const void *valbuf)
+{
+  do_ppc_sysv_return_value (type, regcache, valbuf, NULL, 1);
+}
+
 /* Structures 8 bytes or less long are returned in the r3 & r4
    registers, according to the SYSV ABI. */
 int
 ppc_sysv_abi_use_struct_convention (int gcc_p, struct type *value_type)
 {
-  if ((TYPE_LENGTH (value_type) == 16 || TYPE_LENGTH (value_type) == 8)
-      && TYPE_VECTOR (value_type))
-    return 0;
-
-  return (TYPE_LENGTH (value_type) > 8);
+  return (do_ppc_sysv_return_value (value_type, NULL, NULL, NULL, 0)
+	  == RETURN_VALUE_STRUCT_CONVENTION);
 }
 
 /* Pass the arguments in either registers, or in the stack. Using the
@@ -617,22 +865,6 @@
    copy the buffer to the corresponding register return-value location
    location; when OUTVAL is non-NULL, fill the buffer from the
    corresponding register return-value location.  */
-
-/* Potential ways that a function can return a value of a given type.  */
-enum return_value_convention
-{
-  /* Where the return value has been squeezed into one or more
-     registers.  */
-  RETURN_VALUE_REGISTER_CONVENTION,
-  /* Commonly known as the "struct return convention".  The caller
-     passes an additional hidden first parameter to the caller.  That
-     parameter contains the address at which the value being returned
-     should be stored.  While typically, and historically, used for
-     large structs, this is convention is applied to values of many
-     different types.  */
-  RETURN_VALUE_STRUCT_CONVENTION
-};
-
 static enum return_value_convention
 ppc64_sysv_abi_return_value (struct type *valtype, struct regcache *regcache,
 			     const void *inval, void *outval)
Index: ppc-tdep.h
===================================================================
RCS file: /cvs/src/src/gdb/ppc-tdep.h,v
retrieving revision 1.21
diff -u -r1.21 ppc-tdep.h
--- ppc-tdep.h	10 Oct 2003 18:29:13 -0000	1.21
+++ ppc-tdep.h	10 Oct 2003 20:19:53 -0000
@@ -35,7 +35,18 @@
 void ppc_linux_frame_init_saved_regs (struct frame_info *);
 CORE_ADDR ppc_linux_frame_chain (struct frame_info *);
 int ppc_sysv_abi_use_struct_convention (int, struct type *);
-int ppc_sysv_abi_broken_use_struct_convention (int, struct type *);
+void ppc_sysv_abi_store_return_value (struct type *type,
+				      struct regcache *regcache,
+				      const void *valbuf);
+void ppc_sysv_abi_extract_return_value (struct type *type,
+					struct regcache *regcache,
+					void *valbuf);
+void ppc_sysv_abi_broken_store_return_value (struct type *type,
+					     struct regcache *regcache,
+					     const void *valbuf);
+void ppc_sysv_abi_broken_extract_return_value (struct type *type,
+					       struct regcache *regcache,
+					       void *valbuf);
 CORE_ADDR ppc_sysv_abi_push_dummy_call (struct gdbarch *gdbarch,
 					CORE_ADDR func_addr,
 					struct regcache *regcache,
Index: ppcnbsd-tdep.c
===================================================================
RCS file: /cvs/src/src/gdb/ppcnbsd-tdep.c,v
retrieving revision 1.8
diff -u -r1.8 ppcnbsd-tdep.c
--- ppcnbsd-tdep.c	17 Sep 2003 20:10:48 -0000	1.8
+++ ppcnbsd-tdep.c	10 Oct 2003 20:19:53 -0000
@@ -226,8 +226,11 @@
                   struct gdbarch *gdbarch)
 {
   set_gdbarch_pc_in_sigtramp (gdbarch, ppcnbsd_pc_in_sigtramp);
-
+  /* For NetBSD, this is an on again, off again thing.  Some systems
+     do use the broken struct convention, and some don't.  */
   set_gdbarch_use_struct_convention (gdbarch, ppcnbsd_use_struct_convention);
+  set_gdbarch_extract_return_value (gdbarch, ppc_sysv_abi_broken_extract_return_value);
+  set_gdbarch_store_return_value (gdbarch, ppc_sysv_abi_broken_store_return_value);
   set_solib_svr4_fetch_link_map_offsets (gdbarch,
                                 nbsd_ilp32_solib_svr4_fetch_link_map_offsets);
 }
Index: rs6000-tdep.c
===================================================================
RCS file: /cvs/src/src/gdb/rs6000-tdep.c,v
retrieving revision 1.168
diff -u -r1.168 rs6000-tdep.c
--- rs6000-tdep.c	10 Oct 2003 18:29:13 -0000	1.168
+++ rs6000-tdep.c	10 Oct 2003 20:19:54 -0000
@@ -1313,65 +1313,6 @@
   return sp;
 }
 
-/* Extract a function return value of type TYPE from raw register array
-   REGBUF, and copy that return value into VALBUF in virtual format.  */
-static void
-e500_extract_return_value (struct type *valtype, struct regcache *regbuf, void *valbuf)
-{
-  int offset = 0;
-  int vallen = TYPE_LENGTH (valtype);
-  struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
-
-  if (TYPE_CODE (valtype) == TYPE_CODE_ARRAY
-      && vallen == 8
-      && TYPE_VECTOR (valtype))
-    {
-      regcache_raw_read (regbuf, tdep->ppc_ev0_regnum + 3, valbuf);
-    }
-  else
-    {
-      /* Return value is copied starting from r3.  Note that r3 for us
-         is a pseudo register.  */
-      int offset = 0;
-      int return_regnum = tdep->ppc_gp0_regnum + 3;
-      int reg_size = DEPRECATED_REGISTER_RAW_SIZE (return_regnum);
-      int reg_part_size;
-      char *val_buffer;
-      int copied = 0;
-      int i = 0;
-
-      /* Compute where we will start storing the value from.  */ 
-      if (TARGET_BYTE_ORDER == BFD_ENDIAN_BIG)
-        {
-	  if (vallen <= reg_size)
-	    offset = reg_size - vallen;
-	  else
-	    offset = reg_size + (reg_size - vallen);
-        }
-
-      /* How big does the local buffer need to be?  */
-      if (vallen <= reg_size)
-	val_buffer = alloca (reg_size);
-      else
-	val_buffer = alloca (vallen);
-
-      /* Read all we need into our private buffer.  We copy it in
-         chunks that are as long as one register, never shorter, even
-         if the value is smaller than the register.  */
-      while (copied < vallen)
-        {
-          reg_part_size = DEPRECATED_REGISTER_RAW_SIZE (return_regnum + i);
-	  /* It is a pseudo/cooked register.  */
-          regcache_cooked_read (regbuf, return_regnum + i,
-				val_buffer + copied);
-          copied += reg_part_size;
-          i++;
-        }
-      /* Put the stuff in the return buffer.  */
-      memcpy (valbuf, val_buffer + offset, vallen);
-    }
-}
-
 /* PowerOpen always puts structures in memory.  Vectors, which were
    added later, do get returned in a register though.  */
 
@@ -2046,30 +1987,6 @@
   return regnum;
 }
 
-/* Write into appropriate registers a function return value
-   of type TYPE, given in virtual format.  */
-static void
-e500_store_return_value (struct type *type, char *valbuf)
-{
-  struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
-
-  /* Everything is returned in GPR3 and up.  */
-  int copied = 0;
-  int i = 0;
-  int len = TYPE_LENGTH (type);
-  while (copied < len)
-    {
-      int regnum = gdbarch_tdep (current_gdbarch)->ppc_gp0_regnum + 3 + i;
-      int reg_size = DEPRECATED_REGISTER_RAW_SIZE (regnum);
-      char *reg_val_buf = alloca (reg_size);
-
-      memcpy (reg_val_buf, valbuf + copied, reg_size);
-      copied += reg_size;
-      deprecated_write_register_gen (regnum, reg_val_buf);
-      i++;
-    }
-}
-
 static void
 rs6000_store_return_value (struct type *type, char *valbuf)
 {
@@ -2835,6 +2752,11 @@
       set_gdbarch_extract_return_value (gdbarch, ppc64_sysv_abi_extract_return_value);
       set_gdbarch_store_return_value (gdbarch, ppc64_sysv_abi_store_return_value);
     }
+  else if (sysv_abi && wordsize == 4)
+    {
+      set_gdbarch_extract_return_value (gdbarch, ppc_sysv_abi_extract_return_value);
+      set_gdbarch_store_return_value (gdbarch, ppc_sysv_abi_store_return_value);
+    }
   else
     {
       set_gdbarch_deprecated_extract_return_value (gdbarch, rs6000_extract_return_value);
@@ -2873,8 +2795,6 @@
         set_gdbarch_dwarf2_reg_to_regnum (gdbarch, e500_dwarf2_reg_to_regnum);
         set_gdbarch_pseudo_register_read (gdbarch, e500_pseudo_register_read);
         set_gdbarch_pseudo_register_write (gdbarch, e500_pseudo_register_write);
-        set_gdbarch_extract_return_value (gdbarch, e500_extract_return_value);
-        set_gdbarch_deprecated_store_return_value (gdbarch, e500_store_return_value);
 	break;
       default:
 	tdep->ppc_vr0_regnum = -1;

  reply	other threads:[~2003-10-10 20:22 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2003-09-17 21:54 Andrew Cagney
2003-09-22 21:58 ` Kevin Buettner
2003-10-04 17:43   ` Andrew Cagney
2003-10-06 19:12     ` Kevin Buettner
2003-10-10 20:22       ` Andrew Cagney [this message]
2003-10-10 20:25         ` Jason Thorpe
2003-10-10 20:32           ` Andrew Cagney
2003-10-10 20:34             ` Jason Thorpe
2003-10-10 21:01               ` Andrew Cagney
2003-10-10 21:00         ` Kevin Buettner
2003-10-10 21:27           ` Andrew Cagney

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=3F8714F5.2030605@redhat.com \
    --to=ac131313@redhat.com \
    --cc=gdb-patches@sources.redhat.com \
    --cc=kevinb@redhat.com \
    --cc=thorpej@wasabisystems.com \
    /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