On 10/17/22 11:44, Jakub Jelinek via Gcc-patches wrote: > > Added 2 tests for nested assumptions (one with a simple assumption > nested in complex one, one with side-effects one nested in complex one). > > So far lightly tested, will test fully overnight. > > 2022-10-17 Jakub Jelinek > OK, new prototype update.  I have moved the code to a new "class assume_query" which is still in gimple-range.h We don't need all the heavy lifting of a full on ranger, just an ssa-name table and a gori_compute module.  GORI requires a range_query class, so assume_query derived form that. patch1 is the same/similar infer processing that looks for assume calls and adds any ranges as a side effect. I removed the hack and now its simply returns whetever the global value is, so its no net effect. patch2 is the assume_query class.  When created, it will "process" the current function up front. It currently only works on function with a single integral return statement.  It should never fail, it'll simpy return VARYING for anything it cant determine a value for, or if its an inappropriate function. When its create, you can then query using   bool assume_range_p (vrange &r, tree name); the value of any ssa-name in the function.   They are all pre-calculated, so this is simply picking up any value that was saved. So the good stuff! *Whats now supported?*  It begins with the return statement having a value of [1,1] and begins going back to defs, and evaluating any operands on that statement. PHIs are now processed.   - If an argument is symbolic, we try substituting the LHS for the argument, and go to its def.  continuing the process.   - If an argument is a constant, we check if its compatible with the LHS.       - If the intersection is undefined, then that edge cannot be executed, and it is ignored       - if the is defined, then we examine the predecessor block to see if the exit condition which took this edge provides any useful into. And finally, when the def chain terminates and we cant go any further, it also checks to see if this block has a single predecessor, and if so, if taking the edge to get here was the result of a condition that supplies yet more range info. Caveats.  Im not doing a lot of error checking. If I encounter the definition of an ssa-name a second time, I simply stop.  This avoid infinite loops, and re-doing the same work.  Its unclear to me in a complex case if we might need to do some value merging at merge points, but at this point, we make no attempt.  I'd need to find a case where this was problematic, and I see none yet. *What can you expect? *Using a testcase sample attr-assume-7.C  (and patch 3 which hacks VRP to spit out what values it would find)  it provides the following values: bar()   :   x_3 = x_2(D) + 1;   _4 = x_2(D) <= 41;   return _4; for an assume function, x_2(D) would have a range of [irange] int [-INF, 41] for an assume function, _4 would have a range of [irange] bool [1, 1] baz() | :   x_4 = x_3(D) + 1;   w.0_7 = w;   w.1_8 = w.0_7 + 1;   w = w.1_8;   if (x_4 == 51)     goto ; [INV]   else     goto ; [INV]   :   // predicted unlikely by early return (on trees) predictor.   goto ; [INV]   :   if (x_4 == 53)     goto ; [INV]   else     goto ; [INV]   :   // predicted unlikely by goto predictor.   goto ; [INV]   :   if (x_4 == 64)     goto ; [INV]   else     goto ; [INV]   :   _12 = __cxa_allocate_exception (4);   MEM[(int *)_12] = 1;   __cxa_throw (_12, &_ZTIi, 0B);   :   _10 = x_4 == 43;   :   # _1 = PHI <0(3), _10(8), 0(5)>   return _1; for an assume function, _1 would have a range of [irange] bool [1, 1] for an assume function, x_3(D) would have a range of [irange] int [42, 42] NONZERO 0x2a for an assume function, x_4 would have a range of [irange] int [43, 43] NONZERO 0x2b for an assume function, _10 would have a range of [irange] bool [1, 1] qux()   :   _4 = s.a;   if (_4 == 42)     goto ; [INV]   else     goto ; [INV]   :   _5 = s.b;   if (_5 == 43)     goto ; [INV]   else     goto ; [INV]   :   :   # iftmp.2_1 = PHI <1(3), 0(4)>   return iftmp.2_1; for an assume function, iftmp.2_1 would have a range of [irange] bool [1, 1] for an assume function, _4 would have a range of [irange] int [42, 42] NONZERO 0x2a for an assume function, _5 would have a range of [irange] int [43, 43] NONZERO 0x2b S::foo()   :   _5 = this_4(D)->a;   if (_5 == 42)     goto ; [INV]   else     goto ; [INV]   :   _6 = this_4(D)->b;   if (_6 == 43)     goto ; [INV]   else     goto ; [INV]   :   :   # iftmp.3_1 = PHI <1(3), 0(4)>   return iftmp.3_1; for an assume function, iftmp.3_1 would have a range of [irange] bool [1, 1] for an assume function, _5 would have a range of [irange] int [42, 42] NONZERO 0x2a for an assume function, _6 would have a range of [irange] int [43, 43] NONZERO 0x2b corge()   :   if (x_2(D) <= 41)     goto ; [INV]   else     goto ; [INV]   :   __builtin_unreachable ();   :   _3 = x_2(D) >= -41;   return _3; for an assume function, x_2(D) would have a range of [irange] int [-41, +INF] for an assume function, _3 would have a range of [irange] bool [1, 1] --------------------------------------------------------------------------------------------------------- THis seems to provide a reasonable amount of functionality.  As you can see by the qux() and S:foo() cases, if you can figure out how to map structures and pointers as parameters, we can produce the ranges "produced" when they are loaded.  ie we know _4 is [42,42] and that it was loaded from s.a:   _4 = s.a; Presumably what is needed is a pass (which can be anywhere you want) which invokes an assume_query, and then tries to map the parameter values to ssa-names. Anyway, gives you something to experiement with.   If you would find a different interface useful, let me know, or if there are limitations or other expansions we might need.   This seems like something reasonable for you to start working with? Let me know what you think.   This all bootstraps fine, I could check it into the code base if it helps. Andrew