> The assume function can have many arguments (one is created for each > automatic var referenced or set by the condition), so it would be nice to > track all of them rather than just hardcoding the first. And, the argument > doesn't necessarily have to be a scalar, so perhaps later on we could derive > ranges even for structure members etc. if needed. Or just handle > assume_function in IPA-SRA somehow at least. > > The C++23 paper mentions > [[assume(size > 0)]]; > [[assume(size % 32 == 0)]]; > [[assume(std::isfinite(data[i]))]]; > [[assume(*pn >= 1)]]; > [[assume(i == 42)]]; > [[assume(++i == 43)]]; > [[assume((std::cin >> i, i == 42))]]; > [[assume(++almost_last == last)]]; > among other things, the first and fifth are already handled the > if (!cond) __builtin_unreachable (); way and so work even without this > patch, the (std::cin >> i, i == 42) case is not worth bothering for now > and the rest would be single block assumptions that can be handled easily > (except the last one which would have record type arguments and so we'd need > SRA). > > Jakub I put together an initial prototype, attached is the 2 patches so far. I applied this on top of one of your sets of patches to try it out.     The first patch has the initial simple version, and the second patch hacks VRP to add a loop over all the ssa-names in the function and show what assume_range_p would  return for them. First, I added another routine to ranger: *bool gimple_ranger::assume_range_p (vrange &r, tree name)* This is the routine that is called to determine what the range of NAME is at the end of the function if the function returns [1,1]. It is painfully simple, only working on names in the definition chain of the return variable. It returns TRUE if it finds a non-varying result.   I will next expand on this to look back in the CFG and be more flexible. To apply any assumed values, I added a routine to be called *bool query_assume_call (vrange &r, tree assume_id, tree name);* This routine would be what is called to lookup if there is any range associated with NAME in the assume function ASSUME_ID.    I hacked one up to return [42, 42] for any integer query just for POC.  You'd need to supply this routine somewhere instead. As the ASSUME function has no defs, we can't produce results for the parameters in normal ways, so I leverage the inferred range code.  When doing a stmt walk, when VRP is done processing a stmt, it applies any side effects of the statement going forward. The gimple_inferred_range constructor now also looks for assume calls, and calls query_assume_call () on each argument, and if it gets back TRUE, applies an inferred range record for that range at that stmt.  (This also means those ASSUME ranges will only show up in a VRP walk.) These seems like it might be functional enough for you to experiment with. For the simple int bar (int x) {   [[assume (++x == 43)]];   return x; } The VRP hack for ther assume function shows : for an assume function, x_2(D) would have a range of [irange] int [42, 42] NONZERO 0x2a. I also tried it for bool foo1 (int x, int y) { return x < 10 || x > 20 || x == 12; } or an assume function, x_5(D) would have a range of [irange] int [-INF, 9][12, 12][21, +INF] bool foo2 (int x, int y) { return (x >= 10 && x <= 20) || x == 2; } for an assume function, x_5(D) would have a range of [irange] int [2, 2][10, 20] NONZERO 0x1f for: int bar (int x) {   [[assume (++x == 43)]];   return x; } As for propagating assumed values, the hacked up version returning 42 shows it propagates into the return: query_assume_call injection _Z3bari._assume.0 assume inferred range of x_1(D) to [irange] int [42, 42] NONZERO 0x2a int bar (int x) {   :   .ASSUME (_Z3bari._assume.0, x_1(D));   return 42; } So in principle, I think theres enough infrastructure there to get going.  You can query parameter ranges by creating a ranger, and querying the parameters via *assume_range_p () *.  You can do that anywhere, as the hack I put in vrp shows, it creates a new ranger, simply queries each SSA_NAME, then disposes of the ranger before invoking VRP on a fresh ranger.  The you just wire up a proper *query_assume_call(*) to return those ranges. Thats the basic APi to deal with... call one function, supply another.  Does this model seem like it would work OK for you?  Or do we need to tweak it? I am planning to extend assume_range_p to handle other basic blocks, as well as pick up a few of the extra things that outgoing_edge_range_p does. Andrew PS. It also seems to me that the assume_range_p() infrastructure may have some other uses when it comes to inlining or LTO or IPA.    This particular version works with a return value of [1,1], but that value is manually supplied to GORI by the routine.  If any other pass has some reason to know that the return value was within a certain range, we could use that and query what the incoming ranges of any parameter might have to be. Just a thought.