On Wed, Aug 15, 2018 at 7:31 PM Aldy Hernandez <al...@redhat.com> wrote:
>
> I kept seeing the same patterns over and over in all this re-factoring:
>
> 1. extract value_range constants into pairs of wide ints
>
> 2. mimic symbolics as [-MIN, +MAX] (most of the time :))
>
> 3. perform some wide int operation on the wide int range
>
> 4. convert back to a value range
>
> So I've decided to clean this up even more.  Instead of passing a pair
> of wide ints plus sign, precision, and god knows what to every
> wide_int_range_* function, I've implemented a lighweight class
> (wi_range) for a pair of wide ints.  It is not meant for long term
> storage, but even so its footprint is minimal.
>
> The only extra bits are another 64-bit word per pair for keeping
> attributes such as precision, sign, overflow_wraps, and a few other
> things we'll need shortly.  In reality we only need one set of
> attributes for both sets of pairs, so we waste one 64-bit word.  I don't
> care.  They're short lived, and the resulting code looks an awful lot
> nicer.  For example, the shift code gets rewritten from this:
>
>        if (range_int_cst_p (&vr1)
>           && !vrp_shift_undefined_p (vr1, prec))
>         {
>           if (code == RSHIFT_EXPR)
>             {
>               if (vr0.type != VR_RANGE || symbolic_range_p (&vr0))
>                 {
>                   vr0.type = type = VR_RANGE;
>                   vr0.min = vrp_val_min (expr_type);
>                   vr0.max = vrp_val_max (expr_type);
>                 }
>               extract_range_from_multiplicative_op (vr, code, &vr0, &vr1);
>               return;
>             }
>           else if (code == LSHIFT_EXPR
>                    && range_int_cst_p (&vr0))
>             {
>               wide_int res_lb, res_ub;
>               if (wide_int_range_lshift (res_lb, res_ub, sign, prec,
>                                          wi::to_wide (vr0.min),
>                                          wi::to_wide (vr0.max),
>                                          wi::to_wide (vr1.min),
>                                          wi::to_wide (vr1.max),
>                                          TYPE_OVERFLOW_UNDEFINED (expr_type),
>                                          TYPE_OVERFLOW_WRAPS (expr_type)))
>                 {
>                   min = wide_int_to_tree (expr_type, res_lb);
>                   max = wide_int_to_tree (expr_type, res_ub);
>                   set_and_canonicalize_value_range (vr, VR_RANGE,
>                                                     min, max, NULL);
>                   return;
>                 }
>             }
>         }
>        set_value_range_to_varying (vr);
>
> into this:
>
>        wi_range w0 (&vr0, expr_type);
>        wi_range w1 (&vr1, expr_type);
>        if (!range_int_cst_p (&vr1)
>           || wi_range_shift_undefined_p (w1)
>           || (code == LSHIFT_EXPR
>               && !range_int_cst_p (&vr0)))
>         {
>           vr->set_varying ();
>           return;
>         }
>        bool success;
>        if (code == RSHIFT_EXPR)
>         success = wi_range_multiplicative_op (res, code, w0, w1);
>        else
>         success = wi_range_lshift (res, w0, w1);
>
>        if (success)
>         vr->set_and_canonicalize (res, expr_type);
>        else
>         vr->set_varying ();
>        return;

not sure whether I like the munging of lshift and right shift (what exactly gets
done is less clear in your version ...).  Having a light-weigth class for
the wi_range_ parameters is nice though.

> I also munged together the maybe/mustbe nonzero_bits into one structure.
>
> Finally, I've pontificated that wide_int_range_blah is too much typing.
> Everyone's RSI will thank me for rewriting the methods as wi_range_blah.
>
> I've tried to keep the functionality changes to a minimum.  However,
> there are some slight changes where I treat symbolics and VR_VARYING as
> [MIN, MAX] and let the constant code do its thing.  It is considerably
> easier to read.
>
> I am finally happy with the overall direction of this.  If this and the
> last one are acceptable, I think I only need to clean MIN/MAX_EXPR and
> ABS_EXPR and I'm basically done with what I need going forward.
>
> Phew...
>
> How does this look?

+struct wi_range
+{
+  wide_int min;
+  wide_int max;
+  /* This structure takes one additional 64-bit word apart from the
+     min/max bounds above.  It is laid out so that PRECISION and SIGN
+     can be accessed without any bit twiddling, since they're the most
+     commonly accessed fields.  */
+  unsigned short precision;
+  bool empty_p:1;
+  bool pointer_type_p:1;
+  bool overflow_wraps:1;
+  bool overflow_undefined:1;
+  signop sign;

isn't precision already available in min.get_precision () which is
required to be equal to max.get_precision ()?

+  wi_range () { empty_p = false; }

true I suppose?  Better not allow default construction?

empty_p doesn't seem to be documented, nor is pointer_type_p
or why that is necessary - maybe simply store a tree type
instead all of the bits?

+/* A structure to pass maybe and mustbe nonzero bitmasks.  This is for
+   use in wi_range_set_zero_nonzero_bits and friends.  */
+
+struct maybe_nonzero_bits
+{
+  /* If some bit is unset, it means that for all numbers in the range
+   the bit is 0, otherwise it might be 0 or 1.  */
+  wide_int may_be;
+
+  /* If some bit is set, it means that for all numbers in the range
+   the bit is 1, otherwise it might be 0 or 1.  */
+  wide_int must_be;
 };

eh - multiple changes in one patch ...

@@ -52,6 +54,58 @@ struct GTY((for_user)) value_range

   /* Dump value range to stderr.  */
   void dump ();
+
+  void set (const wi_range &r, tree type);
+  void set_and_canonicalize (const wi_range &r, tree type);
+  void set_undefined ();
+  void set_varying ();
+};

likewise ... you introduce those methods but not use them consistently.
Please leave out this change (I would object to this kind of change
if carried out through).

+/* Construct a new range from the contents of a value_range.  Varying
+   or symbolic ranges are normalized to the entire possible range.  */
+
+inline
+wi_range::wi_range (value_range *vr, tree type)
+{
+  precision = TYPE_PRECISION (type);
+  pointer_type_p = POINTER_TYPE_P (type);
+  overflow_wraps = TYPE_OVERFLOW_WRAPS (type);
+  overflow_undefined = TYPE_OVERFLOW_UNDEFINED (type);
+  sign = TYPE_SIGN (type);
+  empty_p = false;
+  if (vr->type == VR_VARYING || symbolic_range_p (vr))
+    {
+      min = wi::min_value (precision, sign);
+      max = wi::max_value (precision, sign);
+    }
+  else if (vr->type == VR_RANGE)
+    {
+      min = wi::to_wide (vr->min);
+      max = wi::to_wide (vr->max);
+    }
+  else
+    gcc_unreachable ();
+}

What about VR_ANTI_RANGE?  Note the above doesn't exactly look
"light-weight".

-/* Calculate TRUNC_MOD_EXPR on two ranges and store the result in
-   [WMIN,WMAX].  */
+/* Calculate TRUNC_MOD_EXPR on two ranges and store the result in RES.
+   Return TRUE if operation succeeded.  */

did I say multiple changes...?  I think this isn't a good one given "succeed"
doesn't imply that if not the result is UNDEFINED ...

As said above the shift handlign structrue is now unreadable :/  Given we do
different things for lshift vs. rshift it makes sense to simply split

   else if (code == RSHIFT_EXPR
           || code == LSHIFT_EXPR)
     {

even if that means duplicating a few lines.

Just to remind you review (and approval) isn't easier if you put unrelated stuff
into a patch ;)

Richard.

>
> Aldy

Reply via email to