public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc r13-2535] Implement known/maybe fpclassify like API for frange.
@ 2022-09-08 10:56 Aldy Hernandez
  0 siblings, 0 replies; only message in thread
From: Aldy Hernandez @ 2022-09-08 10:56 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:672632317ac901f369b65b25f2147b3e7790ecca

commit r13-2535-g672632317ac901f369b65b25f2147b3e7790ecca
Author: Aldy Hernandez <aldyh@redhat.com>
Date:   Thu Sep 8 08:11:43 2022 +0200

    Implement known/maybe fpclassify like API for frange.
    
    gcc/ChangeLog:
    
            * gimple-range-fold.cc
            (fold_using_range::range_of_builtin_int_call): Use fpclassify like API.
            * range-op-float.cc (finite_operand_p): Same.
            (finite_operands_p): Same.
            (foperator_lt::fold_range): Same.
            (foperator_le::fold_range): Same.
            (foperator_gt::fold_range): Same.
            (foperator_ge::fold_range): Same.
            (foperator_unordered::fold_range): Same.
            (foperator_unordered::op1_range): Same.
            (foperator_ordered::fold_range): Same.
            * value-range.cc (frange::set_nan): Same.
            (frange::set_signbit): Same.
            (frange::union_): Same.
            (frange::intersect): Same.
            (frange::operator==): Same.
            (frange::singleton_p): Same.
            (frange::verify_range): Same.
            (range_tests_nan): Same.
            (range_tests_floats): Same.
            * value-range.h(frange::known_finite): New.
            (frange::maybe_inf): New.
            (frange::known_inf): New.
            (frange::maybe_nan): New.
            (frange::known_nan): New.
            (frange::known_signbit): New.

Diff:
---
 gcc/gimple-range-fold.cc |  19 +++----
 gcc/range-op-float.cc    |  26 +++++-----
 gcc/value-range.cc       | 126 +++++++++++++++++++++++++++--------------------
 gcc/value-range.h        |  78 ++++++++++++++++++++++++++++-
 4 files changed, 170 insertions(+), 79 deletions(-)

diff --git a/gcc/gimple-range-fold.cc b/gcc/gimple-range-fold.cc
index c9c7a2ccc70..85ed6f9d47e 100644
--- a/gcc/gimple-range-fold.cc
+++ b/gcc/gimple-range-fold.cc
@@ -1029,15 +1029,16 @@ fold_using_range::range_of_builtin_int_call (irange &r, gcall *call,
 	frange tmp;
 	if (src.get_operand (tmp, arg))
 	  {
-	    if (tmp.get_signbit ().varying_p ()
-		// FIXME: We don't support signed NANs yet.
-		|| !tmp.get_nan ().no_p ())
-	      return false;
-	    if (tmp.get_signbit ().yes_p ())
-	      r.set_nonzero (type);
-	    else
-	      r.set_zero (type);
-	    return true;
+	    bool signbit;
+	    if (tmp.known_signbit (signbit))
+	      {
+		if (signbit)
+		  r.set_nonzero (type);
+		else
+		  r.set_zero (type);
+		return true;
+	      }
+	    return false;
 	  }
 	break;
       }
diff --git a/gcc/range-op-float.cc b/gcc/range-op-float.cc
index 5fbbaa1fb36..0f928b6c098 100644
--- a/gcc/range-op-float.cc
+++ b/gcc/range-op-float.cc
@@ -167,7 +167,7 @@ frange_set_nan (frange &r, tree type)
 static inline bool
 finite_operand_p (const frange &op1)
 {
-  return flag_finite_math_only || op1.get_nan ().no_p ();
+  return flag_finite_math_only || !op1.maybe_nan ();
 }
 
 // Return TRUE if OP1 and OP2 are known to be free of NANs.
@@ -175,9 +175,7 @@ finite_operand_p (const frange &op1)
 static inline bool
 finite_operands_p (const frange &op1, const frange &op2)
 {
-  return (flag_finite_math_only
-	  || (op1.get_nan ().no_p ()
-	      && op2.get_nan ().no_p ()));
+  return flag_finite_math_only || (!op1.maybe_nan () && !op2.maybe_nan ());
 }
 
 // Floating version of relop_early_resolve that takes into account NAN
@@ -546,7 +544,7 @@ foperator_lt::fold_range (irange &r, tree type,
       else
 	r = range_true_and_false (type);
     }
-  else if (op1.get_nan ().yes_p () || op2.get_nan ().yes_p ())
+  else if (op1.known_nan () || op2.known_nan ())
     r = range_false (type);
   else
     r = range_true_and_false (type);
@@ -648,7 +646,7 @@ foperator_le::fold_range (irange &r, tree type,
       else
 	r = range_true_and_false (type);
     }
-  else if (op1.get_nan ().yes_p () || op2.get_nan ().yes_p ())
+  else if (op1.known_nan () || op2.known_nan ())
     r = range_false (type);
   else
     r = range_true_and_false (type);
@@ -742,7 +740,7 @@ foperator_gt::fold_range (irange &r, tree type,
       else
 	r = range_true_and_false (type);
     }
-  else if (op1.get_nan ().yes_p () || op2.get_nan ().yes_p ())
+  else if (op1.known_nan () || op2.known_nan ())
     r = range_false (type);
   else
     r = range_true_and_false (type);
@@ -844,7 +842,7 @@ foperator_ge::fold_range (irange &r, tree type,
       else
 	r = range_true_and_false (type);
     }
-  else if (op1.get_nan ().yes_p () || op2.get_nan ().yes_p ())
+  else if (op1.known_nan () || op2.known_nan ())
     r = range_false (type);
   else
     r = range_true_and_false (type);
@@ -927,10 +925,10 @@ foperator_unordered::fold_range (irange &r, tree type,
 				 relation_kind) const
 {
   // UNORDERED is TRUE if either operand is a NAN.
-  if (op1.get_nan ().yes_p () || op2.get_nan ().yes_p ())
+  if (op1.known_nan () || op2.known_nan ())
     r = range_true (type);
   // UNORDERED is FALSE if neither operand is a NAN.
-  else if (op1.get_nan ().no_p () && op2.get_nan ().no_p ())
+  else if (!op1.maybe_nan () && !op2.maybe_nan ())
     r = range_false (type);
   else
     r = range_true_and_false (type);
@@ -949,7 +947,7 @@ foperator_unordered::op1_range (frange &r, tree type,
       r.set_varying (type);
       // Since at least one operand must be NAN, if one of them is
       // not, the other must be.
-      if (op2.get_nan ().no_p ())
+      if (!op2.maybe_nan ())
 	frange_set_nan (r, type);
       break;
 
@@ -993,11 +991,9 @@ foperator_ordered::fold_range (irange &r, tree type,
 			       const frange &op1, const frange &op2,
 			       relation_kind) const
 {
-  // ORDERED is TRUE if neither operand is a NAN.
-  if (op1.get_nan ().no_p () && op2.get_nan ().no_p ())
+  if (!op1.maybe_nan () && !op2.maybe_nan ())
     r = range_true (type);
-  // ORDERED is FALSE if either operand is a NAN.
-  else if (op1.get_nan ().yes_p () || op2.get_nan ().yes_p ())
+  else if (op1.known_nan () || op2.known_nan ())
     r = range_false (type);
   else
     r = range_true_and_false (type);
diff --git a/gcc/value-range.cc b/gcc/value-range.cc
index c3f668a811a..adcaaa2a69a 100644
--- a/gcc/value-range.cc
+++ b/gcc/value-range.cc
@@ -274,7 +274,7 @@ frange::set_nan (fp_prop::kind k)
 {
   if (k == fp_prop::YES)
     {
-      if (get_nan ().no_p ())
+      if (!maybe_nan ())
 	{
 	  set_undefined ();
 	  return;
@@ -284,7 +284,7 @@ frange::set_nan (fp_prop::kind k)
       return;
     }
 
-  if (k == fp_prop::NO && get_nan ().yes_p ())
+  if (k == fp_prop::NO && known_nan ())
     {
       set_undefined ();
       return;
@@ -308,21 +308,18 @@ frange::set_signbit (fp_prop::kind k)
   gcc_checking_assert (m_type);
 
   // No additional adjustments are needed for a NAN.
-  if (get_nan ().yes_p ())
+  if (known_nan ())
     {
       m_props.set_signbit (k);
       return;
     }
   // Ignore sign changes when they're set correctly.
-  if (real_less (&m_max, &dconst0))
-    {
-      gcc_checking_assert (get_signbit ().yes_p ());
-      return;
-    }
-  if (real_less (&dconst0, &m_min))
+  if (!maybe_nan ())
     {
-      gcc_checking_assert (get_signbit ().no_p ());
-      return;
+      if (real_less (&m_max, &dconst0))
+	return;
+      if (real_less (&dconst0, &m_min))
+	return;
     }
   // Adjust the range depending on the sign bit.
   if (k == fp_prop::YES)
@@ -330,17 +327,22 @@ frange::set_signbit (fp_prop::kind k)
       // Crop the range to [-INF, 0].
       frange crop (m_type, dconstninf, dconst0);
       intersect (crop);
-      m_props.set_signbit (fp_prop::YES);
+      if (!undefined_p ())
+	m_props.set_signbit (fp_prop::YES);
     }
   else if (k == fp_prop::NO)
     {
       // Crop the range to [0, +INF].
       frange crop (m_type, dconst0, dconstinf);
       intersect (crop);
-      m_props.set_signbit (fp_prop::NO);
+      if (!undefined_p ())
+	m_props.set_signbit (fp_prop::NO);
     }
   else
-    m_props.set_signbit (fp_prop::VARYING);
+    {
+      m_props.set_signbit (fp_prop::VARYING);
+      normalize_kind ();
+    }
 
   if (flag_checking)
     verify_range ();
@@ -467,7 +469,7 @@ frange::union_ (const vrange &v)
 
   // If one side has a NAN, the union is the other side, plus the union
   // of the properties and the possibility of a NAN.
-  if (get_nan ().yes_p ())
+  if (known_nan ())
     {
       frange_props save = m_props;
       *this = r;
@@ -478,7 +480,7 @@ frange::union_ (const vrange &v)
 	verify_range ();
       return true;
     }
-  if (r.get_nan ().yes_p ())
+  if (r.known_nan ())
     {
       m_props.union_ (r.m_props);
       set_nan (fp_prop::VARYING);
@@ -525,7 +527,7 @@ frange::intersect (const vrange &v)
 
   // If two NANs are not exactly the same, drop to an unknown NAN,
   // otherwise there's nothing to do.
-  if (get_nan ().yes_p () && r.get_nan ().yes_p ())
+  if (known_nan () && r.known_nan ())
     {
       if (m_props == r.m_props)
 	return false;
@@ -534,7 +536,7 @@ frange::intersect (const vrange &v)
       return true;
     }
   // ?? Perhaps the intersection of a NAN and anything is a NAN ??.
-  if (get_nan ().yes_p () || r.get_nan ().yes_p ())
+  if (known_nan () || r.known_nan ())
     {
       set_varying (m_type);
       return true;
@@ -590,8 +592,7 @@ frange::operator== (const frange &src) const
       if (varying_p ())
 	return types_compatible_p (m_type, src.m_type);
 
-      if (m_props.get_nan ().yes_p ()
-	  || src.m_props.get_nan ().yes_p ())
+      if (known_nan () || src.known_nan ())
 	return false;
 
       return (real_identical (&m_min, &src.m_min)
@@ -621,6 +622,9 @@ frange::contains_p (tree cst) const
     {
       if (HONOR_SIGNED_ZEROS (m_type) && real_iszero (rv))
 	{
+	  // FIXME: This is still using get_signbit() instead of
+	  // known_signbit() because the latter bails on possible NANs
+	  // (for now).
 	  if (get_signbit ().yes_p ())
 	    return real_isneg (rv);
 	  else if (get_signbit ().no_p ())
@@ -644,22 +648,25 @@ frange::singleton_p (tree *result) const
   if (m_kind == VR_RANGE && real_identical (&m_min, &m_max))
     {
       // Return false for any singleton that may be a NAN.
-      if (HONOR_NANS (m_type) && !get_nan ().no_p ())
+      if (HONOR_NANS (m_type) && maybe_nan ())
 	return false;
 
       // Return the appropriate zero if known.
       if (HONOR_SIGNED_ZEROS (m_type) && zero_p ())
 	{
-	  if (get_signbit ().no_p ())
+	  bool signbit;
+	  if (known_signbit (signbit))
 	    {
-	      if (result)
-		*result = build_real (m_type, dconst0);
-	      return true;
-	    }
-	  if (get_signbit ().yes_p ())
-	    {
-	      if (result)
-		*result = build_real (m_type, real_value_negate (&dconst0));
+	      if (signbit)
+		{
+		  if (result)
+		    *result = build_real (m_type, real_value_negate (&dconst0));
+		}
+	      else
+		{
+		  if (result)
+		    *result = build_real (m_type, dconst0);
+		}
 	      return true;
 	    }
 	  return false;
@@ -701,7 +708,7 @@ frange::verify_range ()
     {
       // If either is a NAN, both must be a NAN.
       gcc_checking_assert (real_identical (&m_min, &m_max));
-      gcc_checking_assert (get_nan ().yes_p ());
+      gcc_checking_assert (known_nan ());
     }
   else
     // Make sure we don't have swapped ranges.
@@ -710,7 +717,7 @@ frange::verify_range ()
   // If we're absolutely sure we have a NAN, the endpoints should
   // reflect this, otherwise we'd have more than one way to represent
   // a NAN.
-  if (m_props.get_nan ().yes_p ())
+  if (known_nan ())
     {
       gcc_checking_assert (real_isnan (&m_min));
       gcc_checking_assert (real_isnan (&m_max));
@@ -718,10 +725,14 @@ frange::verify_range ()
   else
     {
       // Make sure the signbit and range agree.
-      if (m_props.get_signbit ().yes_p ())
-	gcc_checking_assert (real_compare (LE_EXPR, &m_max, &dconst0));
-      else if (m_props.get_signbit ().no_p ())
-	gcc_checking_assert (real_compare (GE_EXPR, &m_min, &dconst0));
+      bool signbit;
+      if (known_signbit (signbit))
+	{
+	  if (signbit)
+	    gcc_checking_assert (real_compare (LE_EXPR, &m_max, &dconst0));
+	  else
+	    gcc_checking_assert (real_compare (GE_EXPR, &m_min, &dconst0));
+	}
     }
 
   // If all the properties are clear, we better not span the entire
@@ -3637,7 +3648,7 @@ range_tests_nan ()
   ASSERT_FALSE (r0 == r0);
   ASSERT_TRUE (r0 != r0);
 
-  // [5,6] U NAN is [5,6] with an unknown NAN bit.
+  // [5,6] U NAN.
   r0 = frange_float ("5", "6");
   r0.set_nan (fp_prop::NO);
   r1 = frange_nan (float_type_node);
@@ -3646,7 +3657,7 @@ range_tests_nan ()
   real_from_string (&r, "6");
   ASSERT_TRUE (real_identical (&q, &r0.lower_bound ()));
   ASSERT_TRUE (real_identical (&r, &r0.upper_bound ()));
-  ASSERT_TRUE (r0.get_nan ().varying_p ());
+  ASSERT_TRUE (r0.maybe_nan ());
 
   // NAN U NAN = NAN
   r0 = frange_nan (float_type_node);
@@ -3654,7 +3665,7 @@ range_tests_nan ()
   r0.union_ (r1);
   ASSERT_TRUE (real_isnan (&r0.lower_bound ()));
   ASSERT_TRUE (real_isnan (&r1.upper_bound ()));
-  ASSERT_TRUE (r0.get_nan ().yes_p ());
+  ASSERT_TRUE (r0.known_nan ());
 
   // [INF, INF] ^ NAN = VARYING
   r0 = frange_nan (float_type_node);
@@ -3666,18 +3677,18 @@ range_tests_nan ()
   r0 = frange_nan (float_type_node);
   r1 = frange_nan (float_type_node);
   r0.intersect (r1);
-  ASSERT_TRUE (r0.get_nan ().yes_p ());
+  ASSERT_TRUE (r0.known_nan ());
 
   // VARYING ^ NAN = NAN.
   r0 = frange_nan (float_type_node);
   r1.set_varying (float_type_node);
   r0.intersect (r1);
-  ASSERT_TRUE (r0.get_nan ().yes_p ());
+  ASSERT_TRUE (r0.known_nan ());
 
   // Setting the NAN bit to yes, forces to range to [NAN, NAN].
   r0.set_varying (float_type_node);
   r0.set_nan (fp_prop::YES);
-  ASSERT_TRUE (r0.get_nan ().yes_p ());
+  ASSERT_TRUE (r0.known_nan ());
   ASSERT_TRUE (real_isnan (&r0.lower_bound ()));
   ASSERT_TRUE (real_isnan (&r0.upper_bound ()));
 }
@@ -3689,6 +3700,7 @@ range_tests_signed_zeros ()
   tree neg_zero = fold_build1 (NEGATE_EXPR, float_type_node, zero);
   REAL_VALUE_TYPE q, r;
   frange r0, r1;
+  bool signbit;
 
   // Since -0.0 == +0.0, a range of [-0.0, -0.0] should contain +0.0
   // and vice versa.
@@ -3722,7 +3734,7 @@ range_tests_signed_zeros ()
   r1 = frange (zero, zero);
   r1.set_signbit (fp_prop::YES);
   r0.union_ (r1);
-  ASSERT_TRUE (r0.zero_p () && r0.get_signbit ().varying_p ());
+  ASSERT_TRUE (r0.zero_p () && !r0.known_signbit (signbit));
 
   // NAN U [5,6] should be [5,6] with no sign info.
   r0 = frange_nan (float_type_node);
@@ -3732,13 +3744,14 @@ range_tests_signed_zeros ()
   real_from_string (&r, "6");
   ASSERT_TRUE (real_identical (&q, &r0.lower_bound ()));
   ASSERT_TRUE (real_identical (&r, &r0.upper_bound ()));
-  ASSERT_TRUE (r0.get_signbit ().varying_p ());
+  ASSERT_TRUE (!r0.known_signbit (signbit));
 }
 
 static void
 range_tests_signbit ()
 {
   frange r0, r1;
+  bool signbit;
 
   // Setting the signbit drops the range to [-INF, 0].
   r0.set_varying (float_type_node);
@@ -3750,35 +3763,42 @@ range_tests_signbit ()
   // the signbit property set.
   r0 = frange_float ("-5", "10");
   r0.set_signbit (fp_prop::YES);
-  ASSERT_TRUE (r0.get_signbit ().yes_p ());
+  r0.set_nan (fp_prop::NO);
+  ASSERT_TRUE (r0.known_signbit (signbit) && signbit);
   r1 = frange_float ("-5", "0");
   ASSERT_TRUE (real_identical (&r0.lower_bound (), &r1.lower_bound ()));
   ASSERT_TRUE (real_identical (&r0.upper_bound (), &r1.upper_bound ()));
 
   // Negative numbers should have the SIGNBIT set.
   r0 = frange_float ("-5", "-1");
-  ASSERT_TRUE (r0.get_signbit ().yes_p ());
+  r0.set_nan (fp_prop::NO);
+  ASSERT_TRUE (r0.known_signbit (signbit) && signbit);
   // Positive numbers should have the SIGNBIT clear.
   r0 = frange_float ("1", "10");
-  ASSERT_TRUE (r0.get_signbit ().no_p ());
+  r0.set_nan (fp_prop::NO);
+  ASSERT_TRUE (r0.known_signbit (signbit) && !signbit);
   // Numbers containing zero should have an unknown SIGNBIT.
   r0 = frange_float ("0", "10");
-  ASSERT_TRUE (r0.get_signbit ().varying_p ());
+  r0.set_nan (fp_prop::NO);
+  ASSERT_TRUE (!r0.known_signbit (signbit));
   // Numbers spanning both positive and negative should have an
   // unknown SIGNBIT.
   r0 = frange_float ("-10", "10");
-  ASSERT_TRUE (r0.get_signbit ().varying_p ());
+  r0.set_nan (fp_prop::NO);
+  ASSERT_TRUE (!r0.known_signbit (signbit));
   r0.set_varying (float_type_node);
-  ASSERT_TRUE (r0.get_signbit ().varying_p ());
+  ASSERT_TRUE (!r0.known_signbit (signbit));
 
   // Ignore signbit changes when the sign bit is obviously known from
   // the range.
   r0 = frange_float ("5", "10");
+  r0.set_nan (fp_prop::NO);
   r0.set_signbit (fp_prop::VARYING);
-  ASSERT_TRUE (r0.get_signbit ().no_p ());
+  ASSERT_TRUE (r0.known_signbit (signbit) && !signbit);
   r0 = frange_float ("-5", "-1");
   r0.set_signbit (fp_prop::NO);
-  ASSERT_TRUE (r0.get_signbit ().yes_p ());
+  r0.set_nan (fp_prop::NO);
+  ASSERT_TRUE (r0.undefined_p ());
 }
 
 static void
@@ -3795,7 +3815,7 @@ range_tests_floats ()
   // A range of [-INF,+INF] is actually VARYING if no other properties
   // are set.
   r0 = frange_float ("-Inf", "+Inf");
-  if (r0.get_nan ().varying_p ())
+  if (r0.maybe_nan ())
     ASSERT_TRUE (r0.varying_p ());
   // ...unless it has some special property...
   r0.set_nan (fp_prop::NO);
diff --git a/gcc/value-range.h b/gcc/value-range.h
index 645dc76c33a..f9a01ee7a05 100644
--- a/gcc/value-range.h
+++ b/gcc/value-range.h
@@ -330,6 +330,7 @@ private:
 class frange : public vrange
 {
   friend class frange_storage_slot;
+  friend class vrange_printer;
 public:
   frange ();
   frange (const frange &);
@@ -366,12 +367,20 @@ public:
   const REAL_VALUE_TYPE &lower_bound () const;
   const REAL_VALUE_TYPE &upper_bound () const;
 
+  // fpclassify like API
+  bool known_finite () const;
+  bool maybe_inf () const;
+  bool known_inf () const;
+  bool maybe_nan () const;
+  bool known_nan () const;
+  bool known_signbit (bool &signbit) const;
+
   // Accessors for FP properties.
-  fp_prop get_nan () const { return m_props.get_nan (); }
   void set_nan (fp_prop::kind f);
-  fp_prop get_signbit () const { return m_props.get_signbit (); }
   void set_signbit (fp_prop::kind);
 private:
+  fp_prop get_nan () const { return m_props.get_nan (); }
+  fp_prop get_signbit () const { return m_props.get_signbit (); }
   void verify_range ();
   bool normalize_kind ();
 
@@ -1187,4 +1196,69 @@ frange_nan (tree type)
   return frange (type, r, r);
 }
 
+// Return TRUE if range is known to be finite.
+
+inline bool
+frange::known_finite () const
+{
+  if (undefined_p () || varying_p () || m_kind == VR_ANTI_RANGE)
+    return false;
+  return (!real_isnan (&m_min)
+	  && !real_isinf (&m_min)
+	  && !real_isinf (&m_max));
+}
+
+// Return TRUE if range may be infinite.
+
+inline bool
+frange::maybe_inf () const
+{
+  if (undefined_p () || m_kind == VR_ANTI_RANGE)
+    return false;
+  if (varying_p ())
+    return true;
+  return real_isinf (&m_min) || real_isinf (&m_max);
+}
+
+// Return TRUE if range is known to be the [-INF,-INF] or [+INF,+INF].
+
+inline bool
+frange::known_inf () const
+{
+  return (m_kind == VR_RANGE
+	  && real_identical (&m_min, &m_max)
+	  && real_isinf (&m_min));
+}
+
+// Return TRUE if range is possibly a NAN.
+
+inline bool
+frange::maybe_nan () const
+{
+  return !get_nan ().no_p ();
+}
+
+// Return TRUE if range is a +NAN or -NAN.
+
+inline bool
+frange::known_nan () const
+{
+  return get_nan ().yes_p ();
+}
+
+// If the signbit for the range is known, set it in SIGNBIT and return
+// TRUE.
+
+inline bool
+frange::known_signbit (bool &signbit) const
+{
+  // FIXME: Signed NANs are not supported yet.
+  if (maybe_nan ())
+    return false;
+  if (get_signbit ().varying_p ())
+    return false;
+  signbit = get_signbit ().yes_p ();
+  return true;
+}
+
 #endif // GCC_VALUE_RANGE_H

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

only message in thread, other threads:[~2022-09-08 10:56 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-09-08 10:56 [gcc r13-2535] Implement known/maybe fpclassify like API for frange Aldy Hernandez

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