public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
From: Richard Guenther <richard.guenther@gmail.com>
To: Andrew Stubbs <andrew.stubbs@gmail.com>
Cc: Michael Matz <matz@suse.de>, gcc-patches@gcc.gnu.org, patches@linaro.org
Subject: Re: [PATCH (3/7)] Widening multiply-and-accumulate pattern matching
Date: Thu, 07 Jul 2011 12:49:00 -0000	[thread overview]
Message-ID: <CAFiYyc374622edHaPkwCB14y=pck8Am8noma6oaMWENQY4FyVQ@mail.gmail.com> (raw)
In-Reply-To: <CAFiYyc1jES6zmkTMZDaeriSH_7Jm5vSGo-DDvao0HXk3aZ3dmg@mail.gmail.com>

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

On Thu, Jul 7, 2011 at 2:28 PM, Richard Guenther
<richard.guenther@gmail.com> wrote:
> On Thu, Jul 7, 2011 at 1:43 PM, Andrew Stubbs <andrew.stubbs@gmail.com> wrote:
>> On 07/07/11 11:26, Andrew Stubbs wrote:
>>>
>>> On 07/07/11 10:58, Richard Guenther wrote:
>>>>
>>>> I think you should assume that series of widenings,
>>>> (int)(short)char_variable
>>>> are already combined.  Thus I believe you only need to consider a single
>>>> conversion in valid_types_for_madd_p.
>>>
>>> Hmm, I'm not so sure. I'll look into it a bit further.
>>
>> OK, here's a test case that gives multiple conversions:
>>
>>  long long
>>  foo (long long a, signed char b, signed char c)
>>  {
>>    int bc = b * c;
>>    return a + (short)bc;
>>  }
>>
>> The dump right before the widen_mult pass gives:
>>
>>  foo (long long int a, signed char b, signed char c)
>>  {
>>    int bc;
>>    long long int D.2018;
>>    short int D.2017;
>>    long long int D.2016;
>>    int D.2015;
>>    int D.2014;
>>
>>  <bb 2>:
>>    D.2014_2 = (int) b_1(D);
>>    D.2015_4 = (int) c_3(D);
>>    bc_5 = D.2014_2 * D.2015_4;
>>    D.2017_6 = (short int) bc_5;
>
> Ok, so you have a truncation that is a no-op value-wise.  I would
> argue that this truncation should be removed independent on
> whether we have a widening multiply instruction or not.
>
> The technically most capable place to remove non-value-changing
> truncations (and combine them with a successive conversion)
> would be value-range propagation.  Which already knows:
>
> Value ranges after VRP:
>
> b_1(D): VARYING
> D.2698_2: [-128, 127]
> c_3(D): VARYING
> D.2699_4: [-128, 127]
> bc_5: [-16256, 16384]
> D.2701_6: [-16256, 16384]
> D.2702_7: [-16256, 16384]
> a_8(D): VARYING
> D.2700_9: VARYING
>
> thus truncating bc_5 to short does not change the value.
>
> The simplification could be made when looking at the
> statement
>
>>    D.2018_7 = (long long int) D.2017_6;
>
> in vrp_fold_stmt, based on the fact that this conversion
> converts from a value-preserving intermediate conversion.
> Thus the transform would replace the D.2017_6 operand
> with bc_5.
>
> So yes, the case appears - but it shouldn't ;)
>
> I'll cook up a quick patch for VRP.

Like the attached.  I'll finish and properly test it.

Richard.

[-- Attachment #2: p --]
[-- Type: application/octet-stream, Size: 10194 bytes --]

Index: gcc/tree-vrp.c
===================================================================
--- gcc/tree-vrp.c	(revision 175962)
+++ gcc/tree-vrp.c	(working copy)
@@ -161,10 +161,10 @@ static VEC (switch_update, heap) *to_upd
 static inline tree
 vrp_val_max (const_tree type)
 {
-  if (!INTEGRAL_TYPE_P (type))
-    return NULL_TREE;
+  if (INTEGRAL_TYPE_P (type))
+    return upper_bound_in_type (CONST_CAST_TREE (type), CONST_CAST_TREE (type));
 
-  return TYPE_MAX_VALUE (type);
+  return NULL_TREE;
 }
 
 /* Return the minimum value for TYPE.  */
@@ -172,10 +172,10 @@ vrp_val_max (const_tree type)
 static inline tree
 vrp_val_min (const_tree type)
 {
-  if (!INTEGRAL_TYPE_P (type))
-    return NULL_TREE;
+  if (INTEGRAL_TYPE_P (type))
+    return lower_bound_in_type (CONST_CAST_TREE (type), CONST_CAST_TREE (type));
 
-  return TYPE_MIN_VALUE (type);
+  return NULL_TREE;
 }
 
 /* Return whether VAL is equal to the maximum value of its type.  This
@@ -565,7 +565,7 @@ set_value_range_to_nonnegative (value_ra
   set_value_range (vr, VR_RANGE, zero,
 		   (overflow_infinity
 		    ? positive_overflow_infinity (type)
-		    : TYPE_MAX_VALUE (type)),
+		    : vrp_val_max (type)),
 		   vr->equiv);
 }
 
@@ -1627,7 +1627,7 @@ extract_range_from_assert (value_range_t
     }
   else if (cond_code == LE_EXPR || cond_code == LT_EXPR)
     {
-      min = TYPE_MIN_VALUE (type);
+      min = vrp_val_min (type);
 
       if (limit_vr == NULL || limit_vr->type == VR_ANTI_RANGE)
 	max = limit;
@@ -1662,7 +1662,7 @@ extract_range_from_assert (value_range_t
     }
   else if (cond_code == GE_EXPR || cond_code == GT_EXPR)
     {
-      max = TYPE_MAX_VALUE (type);
+      max = vrp_val_max (type);
 
       if (limit_vr == NULL || limit_vr->type == VR_ANTI_RANGE)
 	min = limit;
@@ -2079,11 +2079,11 @@ vrp_int_const_binop (enum tree_code code
 	  || code == ROUND_DIV_EXPR)
 	return (needs_overflow_infinity (TREE_TYPE (res))
 		? positive_overflow_infinity (TREE_TYPE (res))
-		: TYPE_MAX_VALUE (TREE_TYPE (res)));
+		: vrp_val_max (TREE_TYPE (res)));
       else
 	return (needs_overflow_infinity (TREE_TYPE (res))
 		? negative_overflow_infinity (TREE_TYPE (res))
-		: TYPE_MIN_VALUE (TREE_TYPE (res)));
+		: vrp_val_min (TREE_TYPE (res)));
     }
 
   return res;
@@ -2888,8 +2888,8 @@ extract_range_from_unary_expr (value_ran
 	  && TYPE_PRECISION (inner_type) < TYPE_PRECISION (outer_type))
 	{
 	  vr0.type = VR_RANGE;
-	  vr0.min = TYPE_MIN_VALUE (inner_type);
-	  vr0.max = TYPE_MAX_VALUE (inner_type);
+	  vr0.min = vrp_val_min (inner_type);
+	  vr0.max = vrp_val_max (inner_type);
 	}
 
       /* If VR0 is a constant range or anti-range and the conversion is
@@ -2974,7 +2974,7 @@ extract_range_from_unary_expr (value_ran
 	    }
 	}
       else
-	min = TYPE_MIN_VALUE (type);
+	min = vrp_val_min (type);
 
       if (is_positive_overflow_infinity (vr0.min))
 	max = negative_overflow_infinity (type);
@@ -2993,7 +2993,7 @@ extract_range_from_unary_expr (value_ran
 	    }
 	}
       else
-	max = TYPE_MIN_VALUE (type);
+	max = vrp_val_min (type);
     }
   else if (code == NEGATE_EXPR
 	   && TYPE_UNSIGNED (type))
@@ -3035,7 +3035,7 @@ extract_range_from_unary_expr (value_ran
       else if (!vrp_val_is_min (vr0.min))
 	min = fold_unary_to_constant (code, type, vr0.min);
       else if (!needs_overflow_infinity (type))
-	min = TYPE_MAX_VALUE (type);
+	min = vrp_val_max (type);
       else if (supports_overflow_infinity (type))
 	min = positive_overflow_infinity (type);
       else
@@ -3049,7 +3049,7 @@ extract_range_from_unary_expr (value_ran
       else if (!vrp_val_is_min (vr0.max))
 	max = fold_unary_to_constant (code, type, vr0.max);
       else if (!needs_overflow_infinity (type))
-	max = TYPE_MAX_VALUE (type);
+	max = vrp_val_max (type);
       else if (supports_overflow_infinity (type)
 	       /* We shouldn't generate [+INF, +INF] as set_value_range
 		  doesn't like this and ICEs.  */
@@ -3079,7 +3079,7 @@ extract_range_from_unary_expr (value_ran
 	         TYPE_MIN_VALUE, remember -TYPE_MIN_VALUE = TYPE_MIN_VALUE.  */
 	      if (TYPE_OVERFLOW_WRAPS (type))
 		{
-		  tree type_min_value = TYPE_MIN_VALUE (type);
+		  tree type_min_value = vrp_val_min (type);
 
 		  min = (vr0.min != type_min_value
 			 ? int_const_binop (PLUS_EXPR, type_min_value,
@@ -3091,7 +3091,7 @@ extract_range_from_unary_expr (value_ran
 		  if (overflow_infinity_range_p (&vr0))
 		    min = negative_overflow_infinity (type);
 		  else
-		    min = TYPE_MIN_VALUE (type);
+		    min = vrp_val_min (type);
 		}
 	    }
 	  else
@@ -3112,7 +3112,7 @@ extract_range_from_unary_expr (value_ran
 		    }
 		}
 	      else
-		max = TYPE_MAX_VALUE (type);
+		max = vrp_val_max (type);
 	    }
 	}
 
@@ -3396,11 +3396,11 @@ adjust_range_with_scev (value_range_t *v
   if (POINTER_TYPE_P (type) || !TYPE_MIN_VALUE (type))
     tmin = lower_bound_in_type (type, type);
   else
-    tmin = TYPE_MIN_VALUE (type);
+    tmin = vrp_val_min (type);
   if (POINTER_TYPE_P (type) || !TYPE_MAX_VALUE (type))
     tmax = upper_bound_in_type (type, type);
   else
-    tmax = TYPE_MAX_VALUE (type);
+    tmax = vrp_val_max (type);
 
   /* Try to use estimated number of iterations for the loop to constrain the
      final value in the evolution.  */
@@ -4318,8 +4318,8 @@ extract_code_and_val_from_cond_with_ops
   if ((comp_code == GT_EXPR || comp_code == LT_EXPR)
       && INTEGRAL_TYPE_P (TREE_TYPE (val)))
     {
-      tree min = TYPE_MIN_VALUE (TREE_TYPE (val));
-      tree max = TYPE_MAX_VALUE (TREE_TYPE (val));
+      tree min = vrp_val_min (TREE_TYPE (val));
+      tree max = vrp_val_max (TREE_TYPE (val));
 
       if (comp_code == GT_EXPR
 	  && (!max
@@ -6685,7 +6685,7 @@ vrp_visit_phi_node (gimple phi)
 	{
 	  if (!needs_overflow_infinity (TREE_TYPE (vr_result.min))
 	      || !vrp_var_may_overflow (lhs, phi))
-	    vr_result.min = TYPE_MIN_VALUE (TREE_TYPE (vr_result.min));
+	    vr_result.min = vrp_val_min (TREE_TYPE (vr_result.min));
 	  else if (supports_overflow_infinity (TREE_TYPE (vr_result.min)))
 	    vr_result.min =
 		negative_overflow_infinity (TREE_TYPE (vr_result.min));
@@ -6697,7 +6697,7 @@ vrp_visit_phi_node (gimple phi)
 	{
 	  if (!needs_overflow_infinity (TREE_TYPE (vr_result.max))
 	      || !vrp_var_may_overflow (lhs, phi))
-	    vr_result.max = TYPE_MAX_VALUE (TREE_TYPE (vr_result.max));
+	    vr_result.max = vrp_val_max (TREE_TYPE (vr_result.max));
 	  else if (supports_overflow_infinity (TREE_TYPE (vr_result.max)))
 	    vr_result.max =
 		positive_overflow_infinity (TREE_TYPE (vr_result.max));
@@ -7119,7 +7119,7 @@ test_for_singularity (enum tree_code con
     {
       /* This should not be negative infinity; there is no overflow
 	 here.  */
-      min = TYPE_MIN_VALUE (TREE_TYPE (op0));
+      min = vrp_val_min (TREE_TYPE (op0));
 
       max = op1;
       if (cond_code == LT_EXPR && !is_overflow_infinity (max))
@@ -7134,7 +7134,7 @@ test_for_singularity (enum tree_code con
     {
       /* This should not be positive infinity; there is no overflow
 	 here.  */
-      max = TYPE_MAX_VALUE (TREE_TYPE (op0));
+      max = vrp_val_max (TREE_TYPE (op0));
 
       min = op1;
       if (cond_code == GT_EXPR && !is_overflow_infinity (min))
@@ -7342,6 +7342,33 @@ simplify_switch_using_ranges (gimple stm
   return false;
 }
 
+/* Simplify an integral conversion from an SSA name in STMT.  */
+
+static bool
+simplify_conversion_using_ranges (gimple stmt)
+{
+  tree rhs1 = gimple_assign_rhs1 (stmt);
+  tree type = TREE_TYPE (gimple_assign_lhs (stmt));
+  gimple def_stmt = SSA_NAME_DEF_STMT (rhs1);
+  value_range_t *vr;
+
+  if (!is_gimple_assign (def_stmt)
+      || !CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (def_stmt)))
+    return false;
+  rhs1 = gimple_assign_rhs1 (def_stmt);
+  if (TREE_CODE (rhs1) != SSA_NAME)
+    return false;
+  vr = get_value_range (rhs1);
+  if (vr->type != VR_RANGE)
+    return false;
+  if (!int_fits_type_p (vr->min, type)
+      || !int_fits_type_p (vr->max, type))
+    return false;
+  gimple_assign_set_rhs1 (stmt, rhs1);
+  update_stmt (stmt);
+  return true;
+}
+
 /* Simplify STMT using ranges if possible.  */
 
 static bool
@@ -7351,6 +7378,7 @@ simplify_stmt_using_ranges (gimple_stmt_
   if (is_gimple_assign (stmt))
     {
       enum tree_code rhs_code = gimple_assign_rhs_code (stmt);
+      tree rhs1 = gimple_assign_rhs1 (stmt);
 
       switch (rhs_code)
 	{
@@ -7364,7 +7392,7 @@ simplify_stmt_using_ranges (gimple_stmt_
 	     or identity if the RHS is zero or one, and the LHS are known
 	     to be boolean values.  Transform all TRUTH_*_EXPR into
              BIT_*_EXPR if both arguments are known to be boolean values.  */
-	  if (INTEGRAL_TYPE_P (TREE_TYPE (gimple_assign_rhs1 (stmt))))
+	  if (INTEGRAL_TYPE_P (TREE_TYPE (rhs1)))
 	    return simplify_truth_ops_using_ranges (gsi, stmt);
 	  break;
 
@@ -7373,15 +7401,15 @@ simplify_stmt_using_ranges (gimple_stmt_
 	 than zero and the second operand is an exact power of two.  */
 	case TRUNC_DIV_EXPR:
 	case TRUNC_MOD_EXPR:
-	  if (INTEGRAL_TYPE_P (TREE_TYPE (gimple_assign_rhs1 (stmt)))
+	  if (INTEGRAL_TYPE_P (TREE_TYPE (rhs1))
 	      && integer_pow2p (gimple_assign_rhs2 (stmt)))
 	    return simplify_div_or_mod_using_ranges (stmt);
 	  break;
 
       /* Transform ABS (X) into X or -X as appropriate.  */
 	case ABS_EXPR:
-	  if (TREE_CODE (gimple_assign_rhs1 (stmt)) == SSA_NAME
-	      && INTEGRAL_TYPE_P (TREE_TYPE (gimple_assign_rhs1 (stmt))))
+	  if (TREE_CODE (rhs1) == SSA_NAME
+	      && INTEGRAL_TYPE_P (TREE_TYPE (rhs1)))
 	    return simplify_abs_using_ranges (stmt);
 	  break;
 
@@ -7390,10 +7418,16 @@ simplify_stmt_using_ranges (gimple_stmt_
 	  /* Optimize away BIT_AND_EXPR and BIT_IOR_EXPR
 	     if all the bits being cleared are already cleared or
 	     all the bits being set are already set.  */
-	  if (INTEGRAL_TYPE_P (TREE_TYPE (gimple_assign_rhs1 (stmt))))
+	  if (INTEGRAL_TYPE_P (TREE_TYPE (rhs1)))
 	    return simplify_bit_ops_using_ranges (gsi, stmt);
 	  break;
 
+	CASE_CONVERT:
+	  if (TREE_CODE (rhs1) == SSA_NAME
+	      && INTEGRAL_TYPE_P (TREE_TYPE (rhs1)))
+	    return simplify_conversion_using_ranges (stmt);
+	  break;
+
 	default:
 	  break;
 	}

  reply	other threads:[~2011-07-07 12:37 UTC|newest]

Thread overview: 107+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-06-23 14:38 [PATCH (0/7)] Improve use of Widening Multiplies Andrew Stubbs
2011-06-23 14:39 ` [PATCH (1/7)] New optab framework for widening multiplies Andrew Stubbs
2011-07-09 15:38   ` Andrew Stubbs
2011-07-14 15:29     ` Andrew Stubbs
2011-07-22 13:01     ` Bernd Schmidt
2011-07-22 13:50       ` Andrew Stubbs
2011-07-22 14:01         ` Bernd Schmidt
2011-07-22 15:52           ` Andrew Stubbs
2011-08-19 14:41             ` Andrew Stubbs
2011-08-19 14:55               ` Richard Guenther
2011-08-19 15:07                 ` Andrew Stubbs
2011-08-19 16:40                   ` Andrew Stubbs
2011-06-23 14:41 ` [PATCH (2/7)] Widening multiplies by more than one mode Andrew Stubbs
2011-07-12 10:15   ` Andrew Stubbs
2011-07-12 11:05     ` Richard Guenther
2011-07-12 11:14       ` Richard Guenther
2011-07-12 11:38         ` Andrew Stubbs
2011-07-12 11:51           ` Richard Guenther
2011-07-21 19:51         ` Joseph S. Myers
2011-07-22  8:58           ` Andrew Stubbs
2011-07-14 14:17       ` Andrew Stubbs
2011-07-14 14:24         ` Richard Guenther
2011-08-19 14:45           ` Andrew Stubbs
2011-06-23 14:42 ` [PATCH (3/7)] Widening multiply-and-accumulate pattern matching Andrew Stubbs
2011-06-23 16:28   ` Richard Guenther
2011-06-24  8:14     ` Andrew Stubbs
2011-06-24  9:31       ` Richard Guenther
2011-06-24 14:08         ` Stubbs, Andrew
2011-06-24 16:13           ` Richard Guenther
2011-06-24 18:22             ` Stubbs, Andrew
2011-06-25  9:58               ` Richard Guenther
2011-06-28 11:32             ` Andrew Stubbs
2011-06-28 12:48               ` Richard Guenther
2011-06-28 16:37                 ` Michael Matz
2011-06-28 16:48                   ` Andrew Stubbs
2011-06-28 17:09                     ` Michael Matz
2011-07-01 11:58                       ` Stubbs, Andrew
2011-07-01 12:25                         ` Richard Guenther
2011-07-04 14:23                           ` Andrew Stubbs
2011-07-07 10:00                             ` Richard Guenther
2011-07-07 10:27                               ` Andrew Stubbs
2011-07-07 12:18                                 ` Andrew Stubbs
2011-07-07 12:34                                   ` Richard Guenther
2011-07-07 12:49                                     ` Richard Guenther [this message]
2011-07-08 12:55                                       ` Andrew Stubbs
2011-07-08 13:22                                         ` Richard Guenther
2011-07-11 17:01                               ` Andrew Stubbs
2011-07-12 11:05                                 ` Richard Guenther
2011-08-19 14:50                                   ` Andrew Stubbs
2011-07-14 14:26                                 ` Andrew Stubbs
2011-07-19  0:36                                   ` Janis Johnson
2011-07-19  9:01                                     ` Andrew Stubbs
2011-07-01 12:33                         ` Paolo Bonzini
2011-07-01 13:31                           ` Stubbs, Andrew
2011-07-01 14:41                             ` Paolo Bonzini
2011-07-01 14:55                               ` Stubbs, Andrew
2011-07-01 15:54                                 ` Paolo Bonzini
2011-07-01 18:18                                   ` Stubbs, Andrew
2011-07-01 15:10                             ` Stubbs, Andrew
2011-07-01 16:40                     ` Bernd Schmidt
2011-06-23 21:55   ` Janis Johnson
2011-06-23 14:43 ` [PATCH (4/7)] Unsigned multiplies using wider signed multiplies Andrew Stubbs
2011-06-28 13:28   ` Andrew Stubbs
2011-06-28 14:49     ` Andrew Stubbs
2011-07-04 14:27       ` Andrew Stubbs
2011-07-07 10:10         ` Richard Guenther
2011-07-07 10:42           ` Andrew Stubbs
2011-07-07 11:08             ` Richard Guenther
2011-07-12 14:10         ` Andrew Stubbs
2011-07-14 14:28           ` Andrew Stubbs
2011-07-14 14:31             ` Richard Guenther
2011-08-19 14:51               ` Andrew Stubbs
2011-06-28 13:30   ` Paolo Bonzini
2011-06-23 14:44 ` [PATCH (5/7)] Widening multiplies for mis-matched mode inputs Andrew Stubbs
2011-06-28 15:44   ` Andrew Stubbs
2011-07-04 14:29     ` Andrew Stubbs
2011-07-07 10:11       ` Richard Guenther
2011-07-14 14:34         ` Andrew Stubbs
2011-07-14 14:35           ` Richard Guenther
2011-08-19 14:54             ` Andrew Stubbs
2011-06-23 14:51 ` [PATCH (6/7)] More widening multiply-and-accumulate pattern matching Andrew Stubbs
2011-06-28 15:49   ` Andrew Stubbs
2011-07-04 14:32     ` Andrew Stubbs
2011-07-07 10:20       ` Richard Guenther
2011-07-14 14:35         ` Andrew Stubbs
2011-07-14 14:41           ` Richard Guenther
2011-08-19 15:03             ` Andrew Stubbs
2011-10-13 16:25               ` Matthew Gretton-Dann
2011-06-23 14:54 ` [PATCH (7/7)] Mixed-sign multiplies using narrowest mode Andrew Stubbs
2011-06-28 17:02   ` Andrew Stubbs
2011-07-14 14:44     ` Andrew Stubbs
2011-07-14 14:48       ` Richard Guenther
2011-08-19 15:56         ` Andrew Stubbs
2011-06-25 16:14 ` [PATCH (0/7)] Improve use of Widening Multiplies Bernd Schmidt
2011-06-27  9:16   ` Andrew Stubbs
2011-07-18 14:34 ` [PATCH (8/7)] Fix a bug in multiply-and-accumulate Andrew Stubbs
2011-07-18 16:09   ` Richard Guenther
2011-07-21 13:48     ` Andrew Stubbs
2011-08-19 16:22       ` Andrew Stubbs
2011-07-21 13:14 ` [PATCH (9/7)] Widening multiplies with constant inputs Andrew Stubbs
2011-07-21 14:34   ` Richard Guenther
2011-07-22 12:28     ` Andrew Stubbs
2011-07-22 12:32       ` Andrew Stubbs
2011-07-22 12:34         ` Richard Guenther
2011-07-22 16:06           ` Andrew Stubbs
2011-08-19 16:24             ` Andrew Stubbs
2011-08-19 16:52               ` H.J. Lu

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='CAFiYyc374622edHaPkwCB14y=pck8Am8noma6oaMWENQY4FyVQ@mail.gmail.com' \
    --to=richard.guenther@gmail.com \
    --cc=andrew.stubbs@gmail.com \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=matz@suse.de \
    --cc=patches@linaro.org \
    /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).