On 2/22/23 08:06, Tamar Christina wrote: > Hi Andrew, > >> all the range-op integer code is in gcc/range-op.cc.  As this is a basic >> binary operation, you should be able to get away with implementing a >> single routine,  wi_fold () which adds 2 wide int bounds  together and >> returns a result.  THis si the implelemntaion for operator_plus. >> >> void >> operator_plus::wi_fold (irange &r, tree type, >>                         const wide_int &lh_lb, const wide_int &lh_ub, >>                         const wide_int &rh_lb, const wide_int &rh_ub) const >> { >>   wi::overflow_type ov_lb, ov_ub; >>   signop s = TYPE_SIGN (type); >>   wide_int new_lb = wi::add (lh_lb, rh_lb, s, &ov_lb); >>   wide_int new_ub = wi::add (lh_ub, rh_ub, s, &ov_ub); >>   value_range_with_overflow (r, type, new_lb, new_ub, ov_lb, ov_ub); >> } >> >> >> you shouldn't have to do any of the overflow stuff at the end, just take >> the 2 sets of wide int, double their precision to start, add them >> together (it cant possible overflow right) and then return an >> int_range<2> with those bounds... >> ie >> >> void >> operator_plus::wi_fold (irange &r, tree type, >>                         const wide_int &lh_lb, const wide_int &lh_ub, >>                         const wide_int &rh_lb, const wide_int &rh_ub) const >> { >>   wi::overflow_type ov_lb, ov_ub; >>   signop s = TYPE_SIGN (type); >> >>   // Do whatever wideint magic is required to do this adds in higher >> precision >>   wide_int new_lb = wi::add (lh_lb, rh_lb, s, &ov_lb); >>   wide_int new_ub = wi::add (lh_ub, rh_ub, s, &ov_ub); >> >>   r = int_range<2> (type, new_lb, new_ub); >> } >> > So I've been working on adding support for widening plus and widening multiplication, > and my examples all work now.. but during bootstrap I hit a problem. > > Say you have a mixed sign widening multiplication, such as in: > > int decMultiplyOp_zacc, decMultiplyOp_iacc; > int *decMultiplyOp_lp; > void decMultiplyOp() { > decMultiplyOp_lp = &decMultiplyOp_zacc; > for (; decMultiplyOp_lp < &decMultiplyOp_zacc + decMultiplyOp_iacc; > decMultiplyOp_lp++) > *decMultiplyOp_lp = 0; > } > > Eventually the pointer arithmetic will generate: > > intD.7 decMultiplyOp_iacc.2_13; > long unsigned intD.11 _15; > _15 = decMultiplyOp_iacc.2_13 w* 4; > and it'll try to get the range from this. > > My implementation is just: > > void > operator_widen_mult::wi_fold (irange &r, tree type, > const wide_int &lh_lb, const wide_int &lh_ub, > const wide_int &rh_lb, const wide_int &rh_ub) const > { > signop s = TYPE_SIGN (type); > > wide_int lh_wlb = wide_int::from (lh_lb, wi::get_precision (lh_lb) * 2, s); > wide_int rh_wlb = wide_int::from (rh_lb, wi::get_precision (rh_lb) * 2, s); > wide_int lh_wub = wide_int::from (lh_ub, wi::get_precision (lh_ub) * 2, s); > wide_int rh_wub = wide_int::from (rh_ub, wi::get_precision (rh_ub) * 2, s); > > /* We don't expect a widening multiplication to be able to overflow but range > calculations for multiplications are complicated. After widening the > operands lets call the base class. */ > return operator_mult::wi_fold (r, type, lh_wlb, lh_wub, rh_wlb, rh_wub); > } > > But in this case the operands are different types and the wi_fold only gets the > type of the operation. The issue is that when increasing the precision for lh_* > I need to sign extend the value and not zero extend, but I don't seem to have > enough context here to know that I do. I'm missing the type of the operands. > > For non-widening operations this doesn't matter as the precision stays the same. > > Is there a way to get the information I need? > > we haven't had this situation before, if I understand it correctly: The LHS is a different type than both the operands, and your problem is you need to know the sign of at least operand1 in order to know whether to zero extend or to sign extend it?  huh. haven't run into that with any other bit of IL before :-P Let me think about it.  I am loathe to change range-ops itself, but we may be able to leverage the builtin-function approach to dealing with something non-standard. At least for the moment to keep you going. For the builtins, we provide a range-ops handler, *after* we look at the operands from within a gimple-context where we can still see the types, and  choose an appropriate handler.  so I'm thinking we provide 2 handlers, operator_widen_mult_signed operator_widen_mult_unsigned chosen based on whether to sign extned or zero extend op1. look at the type of operand one, and return the appropriate handler. Let me give you a skeleton.  I *think* this should do it. you can provide 2 versions of  operator_widen_mult in range-ops (so you can still inherit from operator_mult).  The should be exported I think, and the appropriate one should be called I think... Give it a try and see if it works :-P.