Re: [RFC] > WIDE_INT_MAX_PREC support in wide_int and widest_int
On 9/29/23 04:37, Jakub Jelinek wrote: On Thu, Sep 28, 2023 at 11:53:53AM -0400, Aldy Hernandez wrote: ipa_bits is even worse, because unlike niter analysis, I think it is very much desirable to support IPA VRP of all supported _BitInt sizes. Shall we perhaps use trailing_wide_int storage in there, or conditionally rwidest_int vs. INTEGER_CSTs for stuff that doesn't fit, something else? BTW, we already track value/mask pairs in the irange, so I think ipa_bits should ultimately disappear. Doing so would probably simplify the code base. Well, having irange in GC memory would be equally bad, it does have non-trivial destructors (plus isn't meant to be space efficient either, right?). Correct, irange is not space efficient by a long shot. Any GC and long term requirements should be stored through the value-range-storage.h mechanism. I already converted the ipa_vr ranges that live in GC memory to vrange_storage. See the ipa_vr class. So I think you could just nuke the ipa_bits and use the ranges already in ipa_vr. Though, perhaps we should use value-range-storage.h for that now that it can store value/mask pair as well? Either tweak it on the IPA side such that everything is stored together (both the IPA VRP and IPA bit CCP) Right. Aldy or say use vrange_storage with zero (or one dummy) ranges + the value/mask pair. Jakub
Re: [PATCH] ipa: Remove ipa_bits
On Thu, Oct 5, 2023, 8:26 a.m. Jakub Jelinek wrote: > Hi! > > The following patch removes ipa_bits struct pointer/vector from ipa > jump functions and ipa cp transformations. > > The reason is because the struct uses widest_int to represent > mask/value pair, which in the RFC patches to allow larger precisions > for wide_int/widest_int is GC unfriendly because those types become > non-trivially default constructible/copyable/destructible. > One option would be to use trailing_wide_int for that instead, but > as pointed out by Aldy, irange_storage which we already use under > the hood for ipa_vr when type of parameter is integral or pointer > already stores the mask/value pair because VRP now does the bit cp > as well. > So, this patch just uses m_vr to store both the value range and > the bitmask. There is still separate propagation of the > ipcp_bits_lattice from propagation of the ipcp_vr_lattice, but > when storing we merge the two into the same container. > > I've bootstrapped/regtested a slightly older version of this > patch on x86_64-linux and i686-linux and that version regressed > +FAIL: gcc.dg/ipa/propalign-3.c scan-ipa-dump-not cp "align:" > +FAIL: gcc.dg/ipa/propalign-3.c scan-tree-dump optimized "fail_the_test" > +FAIL: gcc.dg/ipa/propbits-1.c scan-ipa-dump cp "Adjusting mask for param > 0 to 0x7" > +FAIL: gcc.dg/ipa/propbits-2.c scan-ipa-dump cp "Adjusting mask for param > 0 to 0xf" > The last 2 were solely about the earlier patch not actually copying > the if (dump_file) dumping of message that we set some mask for some > parameter (since then added in the @@ -5985,6 +5741,77 @@ hunk). > The first testcase is a test for -fno-ipa-bit-cp disabling bit cp > for alignments. For integral types I'm afraid it is a lost case > when -fno-ipa-bit-cp -fipa-vrp is on when value ranges track bit cp > as well, but for pointer alignments I've added > && opt_for_fn (cs->caller->decl, flag_ipa_bit_cp) > and > && opt_for_fn (node->decl, flag_ipa_bit_cp) > guards such that even just -fno-ipa-bit-cp disables it (alternatively > we could just add -fno-ipa-vrp to propalign-3.c dg-options). > > Ok for trunk if this passes another bootstrap/regtest? > Or defer until it is really needed (when the wide_int/widest_int > changes are about to be committed)? > Up to the maintainers, but this looks like a nice cleanup that has merit on its own. It's exactly what I had in mind when I worked on IPA earlier this cycle. Thanks. Aldy > 2023-10-05 Jakub Jelinek > > * ipa-prop.h (ipa_bits): Remove. > (struct ipa_jump_func): Remove bits member. > (struct ipcp_transformation): Remove bits member, adjust > ctor and dtor. > (ipa_get_ipa_bits_for_value): Remove. > * ipa-prop.cc (struct ipa_bit_ggc_hash_traits): Remove. > (ipa_bits_hash_table): Remove. > (ipa_print_node_jump_functions_for_edge): Don't print bits. > (ipa_get_ipa_bits_for_value): Remove. > (ipa_set_jfunc_bits): Remove. > (ipa_compute_jump_functions_for_edge): For pointers query > pointer alignment before ipa_set_jfunc_vr and update_bitmask > in there. For integral types, just rely on bitmask already > being handled in value ranges. > (ipa_check_create_edge_args): Don't create ipa_bits_hash_table. > (ipcp_transformation_initialize): Neither here. > (ipcp_transformation_t::duplicate): Don't copy bits vector. > (ipa_write_jump_function): Don't stream bits here. > (ipa_read_jump_function): Neither here. > (useful_ipcp_transformation_info_p): Don't test bits vec. > (write_ipcp_transformation_info): Don't stream bits here. > (read_ipcp_transformation_info): Neither here. > (ipcp_get_parm_bits): Get mask and value from m_vr rather > than bits. > (ipcp_update_bits): Remove. > (ipcp_update_vr): For pointers, set_ptr_info_alignment from > bitmask stored in value range. > (ipcp_transform_function): Don't test bits vector, don't call > ipcp_update_bits. > * ipa-cp.cc (propagate_bits_across_jump_function): Don't use > jfunc->bits, instead get mask and value from jfunc->m_vr. > (ipcp_store_bits_results): Remove. > (ipcp_store_vr_results): Incorporate parts of > ipcp_store_bits_results here, merge the bitmasks with value > range if both are supplied. > (ipcp_driver): Don't call ipcp_store_bits_results. > * ipa-sra.cc (zap_useless_ipcp_results): Remove *ts->bits > clearing. > > --- gcc/ipa-prop.h.jj 2023-10-05 11:32:40.172739988 +0200 > +++ gcc/ipa-prop.h 2023-10-05 11:36:45.405378086 +0200 > @@ -292,18 +292,6 @@ public: >array_slice m_elts; > }; > > -/* Information about zero/non-zero bits. */ > -class GTY(()) ipa_bits > -{ > -public: > - /* The propagated value. */ > - widest_int value; > - /* Mask corresponding to the value. > - Similar to ccp_latt
Re: [patch] jump threading multiple paths that start from the same BB
On 07/02/2018 07:08 AM, Christophe Lyon wrote: On 11/07/2017 10:33 AM, Aldy Hernandez wrote: While poking around in the backwards threader I noticed that we bail if we have already seen a starting BB. /* Do not jump-thread twice from the same block. */ if (bitmap_bit_p (threaded_blocks, entry->src->index) This limitation discards paths that are sub-paths of paths that have already been threaded. The following patch scans the remaining to-be-threaded paths to identify if any of them start from the same point, and are thus sub-paths of the just-threaded path. By removing the common prefix of blocks in upcoming threadable paths, and then rewiring first non-common block appropriately, we expose new threading opportunities, since we are no longer starting from the same BB. We also simplify the would-be threaded paths, because we don't duplicate already duplicated paths. [snip] Hi, I've noticed a regression on aarch64: FAIL: gcc.dg/tree-ssa/ssa-dom-thread-7.c scan-tree-dump thread3 "Jumps threaded: 3" very likely caused by this patch (appeared between 262282 and 262294) Christophe The test needs to be adjusted here. The long story is that the aarch64 IL is different at thread3 time in that it has 2 profitable sub-paths that can now be threaded with my patch. This is causing the threaded count to be 5 for aarch64, versus 3 for x86 64. Previously we couldn't thread these in aarch64, so the backwards threader would bail. One can see the different threading opportunities by sticking debug_all_paths() at the top of thread_through_all_blocks(). You will notice that aarch64 has far more candidates to begin with. The IL on the x86 backend, has no paths that start on the same BB. The aarch64, on the other hand, has many to choose from: path: 52 -> 56, 56 -> 57, 57 -> 58, 58 -> 10, 10 -> 11, path: 51 -> 56, 56 -> 57, 57 -> 58, 58 -> 10, 10 -> 16, path: 53 -> 56, 56 -> 57, 57 -> 58, 58 -> 10, 10 -> 16, path: 52 -> 56, 56 -> 57, 57 -> 58, 58 -> 10, 10 -> 11, 11 -> 35, path: 51 -> 56, 56 -> 57, 57 -> 58, 58 -> 10, 10 -> 11, 11 -> 35, path: 53 -> 56, 56 -> 57, 57 -> 58, 58 -> 10, 10 -> 11, 11 -> 35, path: 52 -> 56, 56 -> 57, 57 -> 58, 58 -> 10, 10 -> 16, 16 -> 17, path: 51 -> 56, 56 -> 57, 57 -> 58, 58 -> 10, 10 -> 16, 16 -> 17, path: 53 -> 56, 56 -> 57, 57 -> 58, 58 -> 10, 10 -> 16, 16 -> 19, Some of these prove unprofitable, but 2 more than before are profitable now. BTW, I see another threading related failure on aarch64 which is unrelated to my patch, and was previously there: FAIL: gcc.dg/tree-ssa/ssa-dom-thread-7.c scan-tree-dump-not vrp2 "Jumps threaded" This is probably another IL incompatibility between architectures. Anyways... the attached path fixes the regression. I have added a note to the test explaining the IL differences. We really should rewrite all the threading tests (I am NOT volunteering ;-)). OK for trunk? Aldy gcc/testsuite/ * gcc.dg/tree-ssa/ssa-dom-thread-7.c: Adjust test because aarch64 has a slightly different IL that provides more threading opportunities. diff --git a/gcc/testsuite/gcc.dg/tree-ssa/ssa-dom-thread-7.c b/gcc/testsuite/gcc.dg/tree-ssa/ssa-dom-thread-7.c index 9ee8d12010b..e395de26ec0 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/ssa-dom-thread-7.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/ssa-dom-thread-7.c @@ -2,11 +2,16 @@ /* { dg-options "-O2 -fdump-tree-thread1-stats -fdump-tree-thread2-stats -fdump-tree-dom2-stats -fdump-tree-thread3-stats -fdump-tree-dom3-stats -fdump-tree-vrp2-stats -fno-guess-branch-probability" } */ /* { dg-final { scan-tree-dump "Jumps threaded: 16" "thread1" } } */ /* { dg-final { scan-tree-dump "Jumps threaded: 9" "thread2" } } */ -/* { dg-final { scan-tree-dump "Jumps threaded: 3" "thread3" } } */ /* { dg-final { scan-tree-dump "Jumps threaded: 1" "dom2" } } */ /* { dg-final { scan-tree-dump-not "Jumps threaded" "dom3" } } */ /* { dg-final { scan-tree-dump-not "Jumps threaded" "vrp2" } } */ +/* Most architectures get 3 threadable paths here, whereas aarch64 and + possibly others get 5. We really should rewrite threading tests to + test a specific IL sequence, not gobs of code whose IL can vary + from architecture to architecture. */ +/* { dg-final { scan-tree-dump "Jumps threaded: \[35\]" "thread3" } } */ + enum STATE { S0=0, SI,
Re: extract_range_from_binary* cleanups for VRP
On 07/03/2018 08:16 AM, Martin Liška wrote: Hi. It caused UBSAN errors: $ cat ubsan.i int a; void d() { int c, b = 8 - a; } $ /home/marxin/Programming/gcc2/objdir/./gcc/xgcc -B/home/marxin/Programming/gcc2/objdir/./gcc/ ubsan.i -c -O2 ../../gcc/tree-vrp.c:1715:26: runtime error: load of value 255, which is not a valid value for type 'bool' #0 0x3246ca2 in extract_range_from_binary_expr_1(value_range*, tree_code, tree_node*, value_range*, value_range*) ../../gcc/tree-vrp.c:1715 #1 0x34aa8b6 in vr_values::extract_range_from_binary_expr(value_range*, tree_code, tree_node*, tree_node*, tree_node*) ../../gcc/vr-values.c:794 #2 0x34b45fa in vr_values::extract_range_from_assignment(value_range*, gassign*) ../../gcc/vr-values.c:1455 #3 0x494cfd5 in evrp_range_analyzer::record_ranges_from_stmt(gimple*, bool) ../../gcc/gimple-ssa-evrp-analyze.c:293 #4 0x4942548 in evrp_dom_walker::before_dom_children(basic_block_def*) ../../gcc/gimple-ssa-evrp.c:139 #5 0x487652b in dom_walker::walk(basic_block_def*) ../../gcc/domwalk.c:353 #6 0x49470f9 in execute_early_vrp ../../gcc/gimple-ssa-evrp.c:310 #7 0x49470f9 in execute ../../gcc/gimple-ssa-evrp.c:347 #8 0x1fc4a0e in execute_one_pass(opt_pass*) ../../gcc/passes.c:2446 #9 0x1fc8b47 in execute_pass_list_1 ../../gcc/passes.c:2535 #10 0x1fc8b8e in execute_pass_list_1 ../../gcc/passes.c:2536 #11 0x1fc8c68 in execute_pass_list(function*, opt_pass*) ../../gcc/passes.c:2546 #12 0x2004c85 in do_per_function_toporder(void (*)(function*, void*), void*) ../../gcc/passes.c:1688 #13 0x2005e9a in execute_ipa_pass_list(opt_pass*) ../../gcc/passes.c:2894 #14 0xfcfa79 in ipa_passes ../../gcc/cgraphunit.c:2400 #15 0xfcfa79 in symbol_table::compile() ../../gcc/cgraphunit.c:2536 #16 0xfdc52a in symbol_table::finalize_compilation_unit() ../../gcc/cgraphunit.c:2696 #17 0x25115e4 in compile_file ../../gcc/toplev.c:479 #18 0x9278af in do_compile ../../gcc/toplev.c:2086 #19 0x9278af in toplev::main(int, char**) ../../gcc/toplev.c:2221 #20 0x92a79a in main ../../gcc/main.c:39 #21 0x7659c11a in __libc_start_main ../csu/libc-start.c:308 #22 0x92a8c9 in _start (/home/marxin/Programming/gcc2/objdir/gcc/cc1+0x92a8c9) It's because neg_min_op0, or any other from: bool neg_min_op0, neg_min_op1, neg_max_op0, neg_max_op1; I see. After this spaghetti... if (vr0.type == VR_RANGE && vr1.type == VR_RANGE && (TREE_CODE (min_op0) == INTEGER_CST || (sym_min_op0 = get_single_symbol (min_op0, &neg_min_op0, &min_op0))) && (TREE_CODE (min_op1) == INTEGER_CST || (sym_min_op1 = get_single_symbol (min_op1, &neg_min_op1, &min_op1))) && (!(sym_min_op0 && sym_min_op1) || (sym_min_op0 == sym_min_op1 && neg_min_op0 == (minus_p ? neg_min_op1 : !neg_min_op1))) && (TREE_CODE (max_op0) == INTEGER_CST || (sym_max_op0 = get_single_symbol (max_op0, &neg_max_op0, &max_op0))) && (TREE_CODE (max_op1) == INTEGER_CST || (sym_max_op1 = get_single_symbol (max_op1, &neg_max_op1, &max_op1))) && (!(sym_max_op0 && sym_max_op1) || (sym_max_op0 == sym_max_op1 && neg_max_op0 == (minus_p ? neg_max_op1 : !neg_max_op1 ...we would never actually use the neg*op* variables inside the adjust_symbolic_bound code. Does this patch fix the problem on your end? If so, OK for trunk? gcc/ * tree-vrp.c (extract_range_from_binary_expr_1): Initialize neg_*_op* variables. diff --git a/gcc/tree-vrp.c b/gcc/tree-vrp.c index c966334acbc..65865a7f5b6 100644 --- a/gcc/tree-vrp.c +++ b/gcc/tree-vrp.c @@ -1661,6 +1661,8 @@ extract_range_from_binary_expr_1 (value_range *vr, tree sym_max_op1 = NULL_TREE; bool neg_min_op0, neg_min_op1, neg_max_op0, neg_max_op1; + neg_min_op0 = neg_min_op1 = neg_max_op0 = neg_max_op1 = false; + /* If we have a PLUS or MINUS with two VR_RANGEs, either constant or single-symbolic ranges, try to compute the precise resulting range, but only if we know that this resulting range will also be constant
Re: [patch] jump threading multiple paths that start from the same BB
On 07/03/2018 08:16 PM, Jeff Law wrote: On 07/03/2018 03:31 AM, Aldy Hernandez wrote: On 07/02/2018 07:08 AM, Christophe Lyon wrote: On 11/07/2017 10:33 AM, Aldy Hernandez wrote: While poking around in the backwards threader I noticed that we bail if we have already seen a starting BB. /* Do not jump-thread twice from the same block. */ if (bitmap_bit_p (threaded_blocks, entry->src->index) This limitation discards paths that are sub-paths of paths that have already been threaded. The following patch scans the remaining to-be-threaded paths to identify if any of them start from the same point, and are thus sub-paths of the just-threaded path. By removing the common prefix of blocks in upcoming threadable paths, and then rewiring first non-common block appropriately, we expose new threading opportunities, since we are no longer starting from the same BB. We also simplify the would-be threaded paths, because we don't duplicate already duplicated paths. [snip] Hi, I've noticed a regression on aarch64: FAIL: gcc.dg/tree-ssa/ssa-dom-thread-7.c scan-tree-dump thread3 "Jumps threaded: 3" very likely caused by this patch (appeared between 262282 and 262294) Christophe The test needs to be adjusted here. The long story is that the aarch64 IL is different at thread3 time in that it has 2 profitable sub-paths that can now be threaded with my patch. This is causing the threaded count to be 5 for aarch64, versus 3 for x86 64. Previously we couldn't thread these in aarch64, so the backwards threader would bail. One can see the different threading opportunities by sticking debug_all_paths() at the top of thread_through_all_blocks(). You will notice that aarch64 has far more candidates to begin with. The IL on the x86 backend, has no paths that start on the same BB. The aarch64, on the other hand, has many to choose from: path: 52 -> 56, 56 -> 57, 57 -> 58, 58 -> 10, 10 -> 11, path: 51 -> 56, 56 -> 57, 57 -> 58, 58 -> 10, 10 -> 16, path: 53 -> 56, 56 -> 57, 57 -> 58, 58 -> 10, 10 -> 16, path: 52 -> 56, 56 -> 57, 57 -> 58, 58 -> 10, 10 -> 11, 11 -> 35, path: 51 -> 56, 56 -> 57, 57 -> 58, 58 -> 10, 10 -> 11, 11 -> 35, path: 53 -> 56, 56 -> 57, 57 -> 58, 58 -> 10, 10 -> 11, 11 -> 35, path: 52 -> 56, 56 -> 57, 57 -> 58, 58 -> 10, 10 -> 16, 16 -> 17, path: 51 -> 56, 56 -> 57, 57 -> 58, 58 -> 10, 10 -> 16, 16 -> 17, path: 53 -> 56, 56 -> 57, 57 -> 58, 58 -> 10, 10 -> 16, 16 -> 19, Some of these prove unprofitable, but 2 more than before are profitable now. BTW, I see another threading related failure on aarch64 which is unrelated to my patch, and was previously there: FAIL: gcc.dg/tree-ssa/ssa-dom-thread-7.c scan-tree-dump-not vrp2 "Jumps threaded" This is probably another IL incompatibility between architectures. Anyways... the attached path fixes the regression. I have added a note to the test explaining the IL differences. We really should rewrite all the threading tests (I am NOT volunteering ;-)). OK for trunk? Aldy curr.patch gcc/testsuite/ * gcc.dg/tree-ssa/ssa-dom-thread-7.c: Adjust test because aarch64 has a slightly different IL that provides more threading opportunities. OK. WRT rewriting the tests. I'd certainly agree that we don't have the right set of knobs to allow us to characterize the target nor do we have the right dumping/scanning facilities to describe and query the CFG changes. The fact that the IL changes so much across targets is a sign that target dependency (probably BRANCH_COST) is twiddling the gimple we generate. I strongly suspect we'd be a lot better off if we tackled the BRANCH_COST problem first. Huh. I've always accepted differing IL between architectures as a necessary evil for things like auto-vectorization and the like. What's the ideal plan here? A knob to set default values for target dependent variables that can affect IL layout? Then we could pass -fthis-is-an-IL-test and things be normalized? Thanks. Aldy
calculate overflow type in wide int arithmetic
The reason for this patch are the changes showcased in tree-vrp.c. Basically I'd like to discourage rolling our own overflow and underflow calculation when doing wide int arithmetic. We should have a centralized place for this, that is-- in the wide int code itself ;-). The only cases I care about are plus/minus, which I have implemented, but we also get division for free, since AFAICT, division can only positive overflow: -MIN / -1 => +OVERFLOW Multiplication OTOH, can underflow, but I've not implemented it because we have no uses for it. I have added a note in the code explaining this. Originally I tried to only change plus/minus, but that made code that dealt with plus/minus in addition to div or mult a lot uglier. You'd have to special case "int overflow_for_add_stuff" and "bool overflow_for_everything_else". Changing everything to int, makes things consistent. Note: I have left poly-int as is, with its concept of yes/no for overflow. I can adapt this as well if desired. Tested on x86-64 Linux. OK for trunk? gcc/ * tree-vrp.c (vrp_int_const_binop): Change overflow type to int. (combine_bound): Use wide-int overflow calculation instead of rolling our own. * calls.c (maybe_warn_alloc_args_overflow): Change overflow type to int. * fold-const.c (int_const_binop_2): Same. (extract_muldiv_1): Same. (fold_div_compare): Same. (fold_abs_const): Same. * match.pd: Same. * poly-int.h (add): Same. (sub): Same. (neg): Same. (mul): Same. * predict.c (predict_iv_comparison): Same. * profile-count.c (slow_safe_scale_64bit): Same. * simplify-rtx.c (simplify_const_binary_operation): Same. * tree-chrec.c (tree_fold_binomial): Same. * tree-data-ref.c (split_constant_offset_1): Same. * tree-if-conv.c (idx_within_array_bound): Same. * tree-scalar-evolution.c (iv_can_overflow_p): Same. * tree-ssa-phiopt.c (minmax_replacement): Same. * tree-vect-loop.c (is_nonwrapping_integer_induction): Same. * tree-vect-stmts.c (vect_truncate_gather_scatter_offset): Same. * vr-values.c (vr_values::adjust_range_with_scev): Same. * wide-int.cc (wi::add_large): Same. (wi::mul_internal): Same. (wi::sub_large): Same. (wi::divmod_internal): Same. * wide-int.h: Change overflow type to int for neg, add, mul, smul, umul, div_trunc, div_floor, div_ceil, div_round, mod_trunc, mod_ceil, mod_round, add_large, sub_large, mul_internal, divmod_internal. gcc/cp/ * decl.c (build_enumerator): Change overflow type to int. * init.c (build_new_1): Same. diff --git a/gcc/calls.c b/gcc/calls.c index 1970f1c51dd..14c34cca883 100644 --- a/gcc/calls.c +++ b/gcc/calls.c @@ -1517,7 +1517,7 @@ maybe_warn_alloc_args_overflow (tree fn, tree exp, tree args[2], int idx[2]) wide_int x = wi::to_wide (argrange[0][0], szprec); wide_int y = wi::to_wide (argrange[1][0], szprec); - bool vflow; + int vflow; wide_int prod = wi::umul (x, y, &vflow); if (vflow) diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 0ea3c4a3490..dccca1502b3 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -14628,7 +14628,6 @@ build_enumerator (tree name, tree value, tree enumtype, tree attributes, if (TYPE_VALUES (enumtype)) { tree prev_value; - bool overflowed; /* C++03 7.2/4: If no initializer is specified for the first enumerator, the type is an unspecified integral @@ -14642,6 +14641,7 @@ build_enumerator (tree name, tree value, tree enumtype, tree attributes, value = error_mark_node; else { + int overflowed; tree type = TREE_TYPE (prev_value); signop sgn = TYPE_SIGN (type); widest_int wi = wi::add (wi::to_widest (prev_value), 1, sgn, @@ -14668,7 +14668,7 @@ incremented enumerator value is too large for %") : G_("\ incremented enumerator value is too large for %")); } if (type == NULL_TREE) - overflowed = true; + overflowed = 1; else value = wide_int_to_tree (type, wi); } diff --git a/gcc/cp/init.c b/gcc/cp/init.c index 76ce0b829dd..85df1a2efb9 100644 --- a/gcc/cp/init.c +++ b/gcc/cp/init.c @@ -2943,7 +2943,7 @@ build_new_1 (vec **placement, tree type, tree nelts, tree inner_nelts_cst = maybe_constant_value (inner_nelts); if (TREE_CODE (inner_nelts_cst) == INTEGER_CST) { - bool overflow; + int overflow; offset_int result = wi::mul (wi::to_offset (inner_nelts_cst), inner_nelts_count, SIGNED, &overflow); if (overflow) @@ -3072,7 +3072,7 @@ build_new_1 (vec **placement, tree type, tree nelts, maximum object size and is safe even if we choose not to use a cookie after all. */ max_size -= wi::to_offset (cookie_size); - bool overflow; + int overflow; inner_size = wi::mul (wi::to_offset (size), inner_nelts_count, SIGNED, &overflow); if (overflow || wi::gtu_p (inner_size, max_size)) diff --git a/gcc/fold-const.c b/gcc/fold-const.c index 8476c223e4f..5cfd5edd77d 100644 --- a/gcc/fold-const.c +++ b/gcc/fold-c
Re: calculate overflow type in wide int arithmetic
On 07/05/2018 05:50 AM, Richard Biener wrote: On Thu, Jul 5, 2018 at 9:35 AM Aldy Hernandez wrote: The reason for this patch are the changes showcased in tree-vrp.c. Basically I'd like to discourage rolling our own overflow and underflow calculation when doing wide int arithmetic. We should have a centralized place for this, that is-- in the wide int code itself ;-). The only cases I care about are plus/minus, which I have implemented, but we also get division for free, since AFAICT, division can only positive overflow: -MIN / -1 => +OVERFLOW Multiplication OTOH, can underflow, but I've not implemented it because we have no uses for it. I have added a note in the code explaining this. Originally I tried to only change plus/minus, but that made code that dealt with plus/minus in addition to div or mult a lot uglier. You'd have to special case "int overflow_for_add_stuff" and "bool overflow_for_everything_else". Changing everything to int, makes things consistent. Note: I have left poly-int as is, with its concept of yes/no for overflow. I can adapt this as well if desired. Tested on x86-64 Linux. OK for trunk? looks all straight-forward but the following: else if (op1) { if (minus_p) - { - wi = -wi::to_wide (op1); - - /* Check for overflow. */ - if (sgn == SIGNED - && wi::neg_p (wi::to_wide (op1)) - && wi::neg_p (wi)) - ovf = 1; - else if (sgn == UNSIGNED && wi::to_wide (op1) != 0) - ovf = -1; - } + wi = wi::neg (wi::to_wide (op1)); else wi = wi::to_wide (op1); you fail to handle - -INT_MIN. Woah, very good catch. I previously had this implemented as wi::sub(0, op1, &ovf) which was calculating overflow correctly but when I implemented the overflow type in wi::neg I missed this. Thanks. Given the fact that for multiplication (or others, didn't look too close) you didn't implement the direction indicator I wonder if it would be more appropriate to do enum ovfl { OVFL_NONE = 0, OVFL_UNDERFLOW = -1, OVFL_OVERFLOW = 1, OVFL_UNKNOWN = 2 }; and tell us the "truth" here? Excellent idea...though it came with lots of typing :). Fixed. BTW, if I understand correctly, I've implemented the overflow types correctly for everything but multiplication (which we have no users for and I return OVF_UNKNOWN). I have indicated this in comments. Also, for division I did nothing special, as we can only +OVERFLOW. Hopefully if (overflow) will still work with that. It does. Otherwise can you please add a toplevel comment to wide-int.h as to what the overflow result semantically is for a) SIGNED and b) UNSIGNED operations? Done. Let me know if the current comment is what you had in mind. OK for trunk? gcc/ * tree-vrp.c (vrp_int_const_binop): Change overflow type to overflow_type. (combine_bound): Use wide-int overflow calculation instead of rolling our own. * calls.c (maybe_warn_alloc_args_overflow): Change overflow type to overflow_type. * fold-const.c (int_const_binop_2): Same. (extract_muldiv_1): Same. (fold_div_compare): Same. (fold_abs_const): Same. * match.pd: Same. * poly-int.h (add): Same. (sub): Same. (neg): Same. (mul): Same. * predict.c (predict_iv_comparison): Same. * profile-count.c (slow_safe_scale_64bit): Same. * simplify-rtx.c (simplify_const_binary_operation): Same. * tree-chrec.c (tree_fold_binomial): Same. * tree-data-ref.c (split_constant_offset_1): Same. * tree-if-conv.c (idx_within_array_bound): Same. * tree-scalar-evolution.c (iv_can_overflow_p): Same. * tree-ssa-phiopt.c (minmax_replacement): Same. * tree-vect-loop.c (is_nonwrapping_integer_induction): Same. * tree-vect-stmts.c (vect_truncate_gather_scatter_offset): Same. * vr-values.c (vr_values::adjust_range_with_scev): Same. * wide-int.cc (wi::add_large): Same. (wi::mul_internal): Same. (wi::sub_large): Same. (wi::divmod_internal): Same. * wide-int.h: Change overflow type to overflow_type for neg, add, mul, smul, umul, div_trunc, div_floor, div_ceil, div_round, mod_trunc, mod_ceil, mod_round, add_large, sub_large, mul_internal, divmod_internal. (overflow_type): New enum. gcc/cp/ * decl.c (build_enumerator): Change overflow type to overflow_type. * init.c (build_new_1): Same. diff --git a/gcc/calls.c b/gcc/calls.c index 1970f1c51dd..2a08822d310 100644 --- a/gcc/calls.c +++ b/gcc/calls.c @@ -1517,7 +1517,7 @@ maybe_warn_alloc_args_overflow (tree fn, tree exp, tree args[2], int idx[2]) wide_int x = wi::to_wide (argrange[0][0], szprec); wide_int y = wi::to_wide (argrange[1][0], szprec); - bool vflow; + wi::overflow_type vflow; wide_int prod = wi::umul (x, y, &vflow); if (vflow) diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 0ea3c4a3490..93773fa632d 100644 --- a/gcc/cp/decl.c +++ b/gcc
Re: [patch] jump threading multiple paths that start from the same BB
On 07/05/2018 03:27 PM, Jeff Law wrote: On 07/04/2018 02:12 AM, Aldy Hernandez wrote: On 07/03/2018 08:16 PM, Jeff Law wrote: On 07/03/2018 03:31 AM, Aldy Hernandez wrote: On 07/02/2018 07:08 AM, Christophe Lyon wrote: On 11/07/2017 10:33 AM, Aldy Hernandez wrote: While poking around in the backwards threader I noticed that we bail if we have already seen a starting BB. /* Do not jump-thread twice from the same block. */ if (bitmap_bit_p (threaded_blocks, entry->src->index) This limitation discards paths that are sub-paths of paths that have already been threaded. The following patch scans the remaining to-be-threaded paths to identify if any of them start from the same point, and are thus sub-paths of the just-threaded path. By removing the common prefix of blocks in upcoming threadable paths, and then rewiring first non-common block appropriately, we expose new threading opportunities, since we are no longer starting from the same BB. We also simplify the would-be threaded paths, because we don't duplicate already duplicated paths. [snip] Hi, I've noticed a regression on aarch64: FAIL: gcc.dg/tree-ssa/ssa-dom-thread-7.c scan-tree-dump thread3 "Jumps threaded: 3" very likely caused by this patch (appeared between 262282 and 262294) Christophe The test needs to be adjusted here. The long story is that the aarch64 IL is different at thread3 time in that it has 2 profitable sub-paths that can now be threaded with my patch. This is causing the threaded count to be 5 for aarch64, versus 3 for x86 64. Previously we couldn't thread these in aarch64, so the backwards threader would bail. One can see the different threading opportunities by sticking debug_all_paths() at the top of thread_through_all_blocks(). You will notice that aarch64 has far more candidates to begin with. The IL on the x86 backend, has no paths that start on the same BB. The aarch64, on the other hand, has many to choose from: path: 52 -> 56, 56 -> 57, 57 -> 58, 58 -> 10, 10 -> 11, path: 51 -> 56, 56 -> 57, 57 -> 58, 58 -> 10, 10 -> 16, path: 53 -> 56, 56 -> 57, 57 -> 58, 58 -> 10, 10 -> 16, path: 52 -> 56, 56 -> 57, 57 -> 58, 58 -> 10, 10 -> 11, 11 -> 35, path: 51 -> 56, 56 -> 57, 57 -> 58, 58 -> 10, 10 -> 11, 11 -> 35, path: 53 -> 56, 56 -> 57, 57 -> 58, 58 -> 10, 10 -> 11, 11 -> 35, path: 52 -> 56, 56 -> 57, 57 -> 58, 58 -> 10, 10 -> 16, 16 -> 17, path: 51 -> 56, 56 -> 57, 57 -> 58, 58 -> 10, 10 -> 16, 16 -> 17, path: 53 -> 56, 56 -> 57, 57 -> 58, 58 -> 10, 10 -> 16, 16 -> 19, Some of these prove unprofitable, but 2 more than before are profitable now. BTW, I see another threading related failure on aarch64 which is unrelated to my patch, and was previously there: FAIL: gcc.dg/tree-ssa/ssa-dom-thread-7.c scan-tree-dump-not vrp2 "Jumps threaded" This is probably another IL incompatibility between architectures. Anyways... the attached path fixes the regression. I have added a note to the test explaining the IL differences. We really should rewrite all the threading tests (I am NOT volunteering ;-)). OK for trunk? Aldy curr.patch gcc/testsuite/ * gcc.dg/tree-ssa/ssa-dom-thread-7.c: Adjust test because aarch64 has a slightly different IL that provides more threading opportunities. OK. WRT rewriting the tests. I'd certainly agree that we don't have the right set of knobs to allow us to characterize the target nor do we have the right dumping/scanning facilities to describe and query the CFG changes. The fact that the IL changes so much across targets is a sign that target dependency (probably BRANCH_COST) is twiddling the gimple we generate. I strongly suspect we'd be a lot better off if we tackled the BRANCH_COST problem first. Huh. I've always accepted differing IL between architectures as a necessary evil for things like auto-vectorization and the like. Yes. We've made a conscious decision that introducing target dependencies for the autovectorizer makes sense. BRANCH_COST on the other hand is a different beast :-) What's the ideal plan here? A knob to set default values for target dependent variables that can affect IL layout? Then we could pass -fthis-is-an-IL-test and things be normalized? Well, lots of things. I'd like decisions about how to expand branches deferred until rtl expansion. Kai was poking at this in the past but never really got any traction. For the record, the problem in this testcase is that switch lowering is riddled with back end specific knowledge (GET_MODE_SIZE uses as well as some rtx cost hacks). Many tests should turn into gimple IL tests. Yeah, though for tests like the threading ones, they're already sufficiently convoluted that turning them into gimple IL t
Re: [patch] jump threading multiple paths that start from the same BB
On 07/09/2018 04:29 AM, Richard Biener wrote: On Mon, Jul 9, 2018 at 9:19 AM Aldy Hernandez wrote: On 07/05/2018 03:27 PM, Jeff Law wrote: On 07/04/2018 02:12 AM, Aldy Hernandez wrote: Many tests should turn into gimple IL tests. Yeah, though for tests like the threading ones, they're already sufficiently convoluted that turning them into gimple IL tests will make them even harder to read. Oh well, I guess? To make it easier to transition to gimple IL tests, I suppose we could add -fdump-tree-*-something-more-suitable-for-gimplefe ;-). There is -fdump-tree-*-gimple ;) Ahh!!! Thanks. Is it by design that the gimple fe is sufficiently different from what we dump for -fdump-tree (ahem, things like phi arguments, probabilities, etc)? Well. The only reason is that I didnt' want to adjust all testcases so I introduced the -gimple dump modifier... Note there's still some "magic" fiddling needed to make the GIMPLE FE grok -gimple dump files as well as working around limitations with the current way of having SSA/CFG testcases. I'll keep it in mind as I deal with threading going forward. And I'd like a better framework for testing what we're doing to the IL. Sigh. Me too. Well, the GIMPLE FE is supposed to be that - it's just not perfect (or rather incomplete). Hey, it's pretty good IMO :). Thanks. Aldy
Re: [PATCH] add support for strnlen (PR 81384)
{ dg-do run } { do-options "-O2 -fno-tree-strlen" } */ I don't think this is doing anything. If you look at the test run you can see that -fno-tree-strlen is never passed (I think you actually mean -fno-optimize-strlen for that matter). Also, the builtins.exp harness runs your test for an assortment of other flags, not just -O2. This test is failing on my range branch for -Og, because expand_builtin_strnlen() needs range info: + wide_int min, max; + enum value_range_type rng = get_range_info (bound, &min, &max); + if (rng != VR_RANGE) +return NULL_RTX; but interestingly enough, it seems to be calculated in the sprintf pass as part of the DOM walk: /* First record ranges generated by this statement. */ evrp_range_analyzer.record_ranges_from_stmt (stmt, false); It feels wrong that the sprintf warning pass is generating range info that you may later depend on at rtl expansion time (and for a totally unrelated thing-- strlen expansion). I don't know if this is just a quirk of builtins.exp calling your test with flags you didn't intend, but the inconsistency could cause problems in the future. Errr, or my present ;-). Would it be too much to ask for you to either fix the flags being passed down to the test, or better yet, find some non-sprintf dependent way of calculating range info earlier? Aldy On Mon, Jun 18, 2018 at 6:35 PM Martin Sebor wrote: > > On 06/12/2018 03:11 PM, Jeff Law wrote: > > On 06/05/2018 03:43 PM, Martin Sebor wrote: > >> The attached patch adds basic support for handling strnlen > >> as a built-in function. It touches the strlen pass where > >> it folds constant results of the function, and builtins.c > >> to add simple support for expanding strnlen calls with known > >> results. It also changes calls.c to detect excessive bounds > >> to the function and unsafe calls with arguments declared > >> attribute nonstring. > >> > >> A side-effect of the strlen change I should call out is that > >> strlen() calls to all zero-length arrays that aren't considered > >> flexible array members (i.e., internal members or non-members) > >> are folded into zero. No warning is issued for such invalid > >> uses of zero-length arrays but based on the responses to my > >> question Re: aliasing between internal zero-length-arrays and > >> other members(*) it sounds like one would be appropriate. > >> I will see about adding one in a separate patch. > >> > >> Martin > >> > >> [*] https://gcc.gnu.org/ml/gcc/2018-06/msg00046.html > >> > >> gcc-81384.diff > >> > >> > >> PR tree-optimization/81384 - built-in form of strnlen missing > >> > >> gcc/ChangeLog: > >> > >> PR tree-optimization/81384 > >> * builtin-types.def (BT_FN_SIZE_CONST_STRING_SIZE): New. > >> * builtins.c (expand_builtin_strnlen): New function. > >> (expand_builtin): Call it. > >> (fold_builtin_n): Avoid setting TREE_NO_WARNING. > >> * builtins.def (BUILT_IN_STRNLEN): New. > >> * calls.c (maybe_warn_nonstring_arg): Handle BUILT_IN_STRNLEN. > >> Warn for bounds in excess of maximum object size. > >> * tree-ssa-strlen.c (maybe_set_strlen_range): Return tree representing > >> single-value ranges. Handle strnlen. > >> (handle_builtin_strlen): Handle strnlen. > >> (strlen_check_and_optimize_stmt): Same. > >> > >> gcc/testsuite/ChangeLog: > >> > >> PR tree-optimization/81384 > >> * gcc.c-torture/execute/builtins/lib/strnlen.c: New test. > >> * gcc.c-torture/execute/builtins/strnlen-lib.c: New test. > >> * gcc.c-torture/execute/builtins/strnlen.c: New test. > >> * gcc.dg/attr-nonstring-2.c: New test. > >> * gcc.dg/attr-nonstring-3.c: New test. > >> * gcc.dg/attr-nonstring-4.c: New test. > >> * gcc.dg/strlenopt-44.c: New test. > >> * gcc.dg/strlenopt.h (strnlen): Declare. > >> > >> diff --git a/gcc/builtin-types.def b/gcc/builtin-types.def > >> index 5365bef..1f15350 100644 > >> --- a/gcc/builtin-types.def > >> +++ b/gcc/builtin-types.def > >> @@ -322,6 +322,8 @@ DEF_FUNCTION_TYPE_2 (BT_FN_STRING_CONST_STRING_INT, > >> BT_STRING, BT_CONST_STRING, BT_INT) > >> DEF_FUNCTION_TYPE_2 (BT_FN_STRING_CONST_STRING_SIZE, > >> BT_STRING, BT_CONST_STRING, BT_SIZE) > >> +DEF_FUNCTION_TYPE_2 (BT_FN_SIZE_CONST_STRING_SIZE, > >> + BT_SIZE, BT_CONST_STRING, BT_SIZE) > >> DEF_FUNCTION_TYPE_2 (BT_FN_INT_CONST_STRING_FILEPTR, > >> BT_INT, BT_CONST_STRING, BT_FILEPTR) > >> DEF_FUNCTION_TYPE_2 (BT_FN_INT_INT_FILEPTR, > > I believe Jakub already suggested these change and you ack'd that. > > > > You have some hunks which will need updating now that the CHKP/MPX bits > > are gone. > > > > So OK after the cleanups noted above and a fresh bootstrap & regression > > test cycle. > > > > Done. I also added documentation for the built-in, reran GCC > and Glibc tests with no regressions, and committed 261705. > > Martin
Re: calculate overflow type in wide int arithmetic
On 07/09/2018 07:16 AM, Rainer Orth wrote: and several more. This seems to be the only backend that uses the additional bool * argument to wi::neg etc. Fixed as follows, bootstrapped on sparc-sun-solaris2.11. Thanks. Sorry for the oversight. Aldy
abstract wide int binop code from VRP
Howdy! Attached are more cleanups to VRP getting rid of some repetitive code, as well as abstracting wide int handling code into their own functions. There should be no change to existing functionality. You may notice that I have removed the PLUS/MINUS_EXPR handling in vrp_int_const_binop, even from the new abstracted code: - /* For addition, the operands must be of the same sign -to yield an overflow. Its sign is therefore that -of one of the operands, for example the first. */ - || (code == PLUS_EXPR && sgn1 >= 0) - /* For subtraction, operands must be of -different signs to yield an overflow. Its sign is -therefore that of the first operand or the opposite of -that of the second operand. A first operand of 0 counts -as positive here, for the corner case 0 - (-INF), which -overflows, but must yield +INF. */ - || (code == MINUS_EXPR && sgn1 >= 0) This code is actually unreachable, as the switch above this snippet was already aborting if code was not one of the shift or mult/div operators. Oh yeah, don't blame me for the cryptic comment to range_easy_mask_min_mask(). That machine language comment was already there ;-). OK pending one more round of tests? Aldy gcc/ * fold-const.c (int_const_binop_2): Abstract wide int code to... (wide_int_binop): ...here. * fold-const.h (wide_int_binop): New. * tree-vrp.c (vrp_int_const_binop): Call wide_int_binop. Remove useless PLUS/MINUS_EXPR case. (zero_nonzero_bits_from_vr): Move wide int code... (zero_nonzero_bits_from_bounds): ...here. (extract_range_from_binary_expr_1): Move mask optimization code... (range_easy_mask_min_max): ...here. * tree-vrp.h (zero_nonzero_bits_from_bounds): New. (range_easy_mask_min_max): New. diff --git a/gcc/fold-const.c b/gcc/fold-const.c index 5b94c700c81..35171c5de08 100644 --- a/gcc/fold-const.c +++ b/gcc/fold-const.c @@ -966,21 +966,18 @@ int_binop_types_match_p (enum tree_code code, const_tree type1, const_tree type2 && TYPE_MODE (type1) == TYPE_MODE (type2); } -/* Subroutine of int_const_binop_1 that handles two INTEGER_CSTs. */ - -static tree -int_const_binop_2 (enum tree_code code, const_tree parg1, const_tree parg2, - int overflowable) -{ - wide_int res; - tree t; - tree type = TREE_TYPE (parg1); - signop sign = TYPE_SIGN (type); - wi::overflow_type overflow = wi::OVF_NONE; +/* Perform binary tree operation CODE on ARG1 and ARG2 and return the + result in RES. If an overflow occurs, it is stored in OVERFLOW. - wi::tree_to_wide_ref arg1 = wi::to_wide (parg1); - wide_int arg2 = wi::to_wide (parg2, TYPE_PRECISION (type)); + Return TRUE if the operation is handled and was successful. */ +bool +wide_int_binop (enum tree_code code, + wide_int &res, const wide_int &arg1, const wide_int &arg2, + signop sign, wi::overflow_type &overflow) +{ + wide_int tmp; + overflow = wi::OVF_NONE; switch (code) { case BIT_IOR_EXPR: @@ -999,37 +996,41 @@ int_const_binop_2 (enum tree_code code, const_tree parg1, const_tree parg2, case LSHIFT_EXPR: if (wi::neg_p (arg2)) { - arg2 = -arg2; + tmp = -arg2; if (code == RSHIFT_EXPR) code = LSHIFT_EXPR; else code = RSHIFT_EXPR; } + else +tmp = arg2; if (code == RSHIFT_EXPR) /* It's unclear from the C standard whether shifts can overflow. The following code ignores overflow; perhaps a C standard interpretation ruling is needed. */ - res = wi::rshift (arg1, arg2, sign); + res = wi::rshift (arg1, tmp, sign); else - res = wi::lshift (arg1, arg2); + res = wi::lshift (arg1, tmp); break; case RROTATE_EXPR: case LROTATE_EXPR: if (wi::neg_p (arg2)) { - arg2 = -arg2; + tmp = -arg2; if (code == RROTATE_EXPR) code = LROTATE_EXPR; else code = RROTATE_EXPR; } + else +tmp = arg2; if (code == RROTATE_EXPR) - res = wi::rrotate (arg1, arg2); + res = wi::rrotate (arg1, tmp); else - res = wi::lrotate (arg1, arg2); + res = wi::lrotate (arg1, tmp); break; case PLUS_EXPR: @@ -1051,49 +1052,49 @@ int_const_binop_2 (enum tree_code code, const_tree parg1, const_tree parg2, case TRUNC_DIV_EXPR: case EXACT_DIV_EXPR: if (arg2 == 0) - return NULL_TREE; + return false; res = wi::div_trunc (arg1, arg2, sign, &overflow); break; case FLOOR_DIV_EXPR: if (arg2 == 0) - return NULL_TREE; + return false; res = wi::div_floor (arg1, arg2, sign, &overflow); break; case CEIL_DIV_EXPR: if (arg2 == 0) - return NULL_TREE; + return false; res = wi::div_ceil (arg1, arg2, sign, &overflow); break; case ROUND_DIV_EXPR: if (arg2 == 0) - return NULL_TREE; + return false; res = wi::div_round (arg1, arg2, sign, &overflow);
Re: [patch] jump threading multiple paths that start from the same BB
On 07/09/2018 03:56 PM, Jeff Law wrote: On 07/09/2018 01:19 AM, Aldy Hernandez wrote: I'd like decisions about how to expand branches deferred until rtl expansion. Kai was poking at this in the past but never really got any traction. For the record, the problem in this testcase is that switch lowering is riddled with back end specific knowledge (GET_MODE_SIZE uses as well as some rtx cost hacks). Yea. Switch lowering is going to have some of these as well, though I think BRANCH_COST is more pervasive. Many tests should turn into gimple IL tests. Yeah, though for tests like the threading ones, they're already sufficiently convoluted that turning them into gimple IL tests will make them even harder to read. Oh well, I guess? It might make them harder to read, but it would guarantee consistent gimple fed into the optimizer across our targets which in turn ought to result in consistent behavior by the optimizer which in turn should simplify the test and make them more consistent over time. Ok. When I submit my queued up range based changes to the threader I'll see if I can convert a big chunk of the threader tests to gimple IL.
abstract gimple_call_nonnull*() from vr-values
Ho hum, more abstractions. No change in functionality. OK for trunk? gcc/ * vr-values.c (gimple_stmt_nonzero_p): Abstract common code to... * gimple.c (gimple_call_nonnull_result_p): ...here... (gimple_call_nonnull_arg): ...and here. * gimple.h (gimple_call_nonnull_result_p): New. (gimple_call_nonnull_arg): New. diff --git a/gcc/gimple.c b/gcc/gimple.c index afdf583256c..8d56a966cc1 100644 --- a/gcc/gimple.c +++ b/gcc/gimple.c @@ -1548,6 +1548,57 @@ gimple_call_return_flags (const gcall *stmt) } +/* Return true if call STMT is known to return a non-zero result. */ + +bool +gimple_call_nonnull_result_p (gcall *call) +{ + tree fndecl = gimple_call_fndecl (call); + if (!fndecl) +return false; + if (flag_delete_null_pointer_checks && !flag_check_new + && DECL_IS_OPERATOR_NEW (fndecl) + && !TREE_NOTHROW (fndecl)) +return true; + + /* References are always non-NULL. */ + if (flag_delete_null_pointer_checks + && TREE_CODE (TREE_TYPE (fndecl)) == REFERENCE_TYPE) +return true; + + if (flag_delete_null_pointer_checks + && lookup_attribute ("returns_nonnull", + TYPE_ATTRIBUTES (gimple_call_fntype (call +return true; + return gimple_alloca_call_p (call); +} + + +/* If CALL returns a non-null result in an argument, return that arg. */ + +tree +gimple_call_nonnull_arg (gcall *call) +{ + tree fndecl = gimple_call_fndecl (call); + if (!fndecl) +return NULL_TREE; + + unsigned rf = gimple_call_return_flags (call); + if (rf & ERF_RETURNS_ARG) +{ + unsigned argnum = rf & ERF_RETURN_ARG_MASK; + if (argnum < gimple_call_num_args (call)) + { + tree arg = gimple_call_arg (call, argnum); + if (SSA_VAR_P (arg) + && infer_nonnull_range_by_attribute (call, arg)) + return arg; + } +} + return NULL_TREE; +} + + /* Return true if GS is a copy assignment. */ bool diff --git a/gcc/gimple.h b/gcc/gimple.h index 32e1908c534..a5dda9369bc 100644 --- a/gcc/gimple.h +++ b/gcc/gimple.h @@ -1488,6 +1488,8 @@ bool gimple_call_same_target_p (const gimple *, const gimple *); int gimple_call_flags (const gimple *); int gimple_call_arg_flags (const gcall *, unsigned); int gimple_call_return_flags (const gcall *); +bool gimple_call_nonnull_result_p (gcall *); +tree gimple_call_nonnull_arg (gcall *); bool gimple_assign_copy_p (gimple *); bool gimple_assign_ssa_name_copy_p (gimple *); bool gimple_assign_unary_nop_p (gimple *); diff --git a/gcc/vr-values.c b/gcc/vr-values.c index 32f64e047af..bba170f341b 100644 --- a/gcc/vr-values.c +++ b/gcc/vr-values.c @@ -313,35 +313,9 @@ gimple_stmt_nonzero_p (gimple *stmt) return gimple_assign_nonzero_p (stmt); case GIMPLE_CALL: { - tree fndecl = gimple_call_fndecl (stmt); - if (!fndecl) return false; - if (flag_delete_null_pointer_checks && !flag_check_new - && DECL_IS_OPERATOR_NEW (fndecl) - && !TREE_NOTHROW (fndecl)) - return true; - /* References are always non-NULL. */ - if (flag_delete_null_pointer_checks - && TREE_CODE (TREE_TYPE (fndecl)) == REFERENCE_TYPE) - return true; - if (flag_delete_null_pointer_checks && - lookup_attribute ("returns_nonnull", - TYPE_ATTRIBUTES (gimple_call_fntype (stmt - return true; - - gcall *call_stmt = as_a (stmt); - unsigned rf = gimple_call_return_flags (call_stmt); - if (rf & ERF_RETURNS_ARG) - { - unsigned argnum = rf & ERF_RETURN_ARG_MASK; - if (argnum < gimple_call_num_args (call_stmt)) - { - tree arg = gimple_call_arg (call_stmt, argnum); - if (SSA_VAR_P (arg) - && infer_nonnull_range_by_attribute (stmt, arg)) - return true; - } - } - return gimple_alloca_call_p (stmt); +gcall *call_stmt = as_a (stmt); + return (gimple_call_nonnull_result_p (call_stmt) + || gimple_call_nonnull_arg (call_stmt)); } default: gcc_unreachable ();
allow thread_through_all_blocks() to start from the same initial BB
I believe I missed this companion patch when I submitted... Subject: jump threading multiple paths that start from the same BB The attached patch changes thread_through_all_blocks to allow threads that start from the same basic block as another thread. OK for trunk? gcc/ * tree-ssa-threadupdate.c (thread_through_all_blocks): Do not jump thread twice from the same starting edge. diff --git a/gcc/tree-ssa-threadupdate.c b/gcc/tree-ssa-threadupdate.c index 17f9b89d6a7..8080dff76d0 100644 --- a/gcc/tree-ssa-threadupdate.c +++ b/gcc/tree-ssa-threadupdate.c @@ -2428,6 +2428,7 @@ thread_through_all_blocks (bool may_peel_loop_headers) unsigned int i; struct loop *loop; auto_bitmap threaded_blocks; + hash_set visited_starting_edges; if (!paths.exists ()) { @@ -2473,10 +2474,17 @@ thread_through_all_blocks (bool may_peel_loop_headers) continue; } - /* Do not jump-thread twice from the same block. */ - if (bitmap_bit_p (threaded_blocks, entry->src->index) - /* We may not want to realize this jump thread path - for various reasons. So check it first. */ + /* Do not jump-thread twice from the same starting edge. + + Previously we only checked that we weren't threading twice + from the same BB, but that was too restrictive. Imagine a + path that starts from GIMPLE_COND(x_123 == 0,...), where both + edges out of this conditional yield paths that can be + threaded (for example, both lead to an x_123==0 or x_123!=0 + conditional further down the line. */ + if (visited_starting_edges.contains (entry) + /* We may not want to realize this jump thread path for + various reasons. So check it first. */ || !valid_jump_thread_path (path)) { /* Remove invalid FSM jump-thread paths. */ @@ -2496,7 +2504,7 @@ thread_through_all_blocks (bool may_peel_loop_headers) { /* We do not update dominance info. */ free_dominance_info (CDI_DOMINATORS); - bitmap_set_bit (threaded_blocks, entry->src->index); + visited_starting_edges.add (entry); retval = true; thread_stats.num_threaded_edges++; } @@ -2514,7 +2522,7 @@ thread_through_all_blocks (bool may_peel_loop_headers) edge entry = (*path)[0]->e; /* Do not jump-thread twice from the same block. */ - if (bitmap_bit_p (threaded_blocks, entry->src->index)) + if (visited_starting_edges.contains (entry)) { delete_jump_thread_path (path); paths.unordered_remove (i); @@ -2523,8 +2531,6 @@ thread_through_all_blocks (bool may_peel_loop_headers) i++; } - bitmap_clear (threaded_blocks); - mark_threaded_blocks (threaded_blocks); initialize_original_copy_tables ();
Re: abstract wide int binop code from VRP
Hmmm, I think we can do better, and since this hasn't been reviewed yet, I don't think anyone will mind the adjustment to the patch ;-). I really hate int_const_binop_SOME_RANDOM_NUMBER. We should abstract them into properly named poly_int_binop, wide_int_binop, and tree_binop, and then use a default argument for int_const_binop() to get things going. Sorry for more changes in flight, but I thought we could benefit from more cleanups :). OK for trunk pending tests? Aldy On 07/10/2018 04:31 AM, Aldy Hernandez wrote: Howdy! Attached are more cleanups to VRP getting rid of some repetitive code, as well as abstracting wide int handling code into their own functions. There should be no change to existing functionality. You may notice that I have removed the PLUS/MINUS_EXPR handling in vrp_int_const_binop, even from the new abstracted code: - /* For addition, the operands must be of the same sign - to yield an overflow. Its sign is therefore that - of one of the operands, for example the first. */ - || (code == PLUS_EXPR && sgn1 >= 0) - /* For subtraction, operands must be of - different signs to yield an overflow. Its sign is - therefore that of the first operand or the opposite of - that of the second operand. A first operand of 0 counts - as positive here, for the corner case 0 - (-INF), which - overflows, but must yield +INF. */ - || (code == MINUS_EXPR && sgn1 >= 0) This code is actually unreachable, as the switch above this snippet was already aborting if code was not one of the shift or mult/div operators. Oh yeah, don't blame me for the cryptic comment to range_easy_mask_min_mask(). That machine language comment was already there ;-). OK pending one more round of tests? Aldy gcc/ * fold-const.c (int_const_binop_1): Abstract... (wide_int_binop): ...wide int code here. (poly_int_binop): ...poly int code here. (tree_binop): ...tree code here. * fold-const.h (wide_int_binop): New. * tree-vrp.c (vrp_int_const_binop): Call wide_int_binop. Remove useless PLUS/MINUS_EXPR case. (zero_nonzero_bits_from_vr): Move wide int code... (zero_nonzero_bits_from_bounds): ...here. (extract_range_from_binary_expr_1): Move mask optimization code... (range_easy_mask_min_max): ...here. * tree-vrp.h (zero_nonzero_bits_from_bounds): New. (range_easy_mask_min_max): New. diff --git a/gcc/fold-const.c b/gcc/fold-const.c index 97c435fa5e0..4614b8adcf4 100644 --- a/gcc/fold-const.c +++ b/gcc/fold-const.c @@ -966,21 +966,17 @@ int_binop_types_match_p (enum tree_code code, const_tree type1, const_tree type2 && TYPE_MODE (type1) == TYPE_MODE (type2); } -/* Subroutine of int_const_binop_1 that handles two INTEGER_CSTs. */ +/* Combine two wide ints ARG1 and ARG2 under operation CODE to produce + a new constant in RES. Return FALSE if we don't know how to + evaluate CODE at compile-time. */ -static tree -int_const_binop_2 (enum tree_code code, const_tree parg1, const_tree parg2, - int overflowable) +bool +wide_int_binop (enum tree_code code, + wide_int &res, const wide_int &arg1, const wide_int &arg2, + signop sign, wi::overflow_type &overflow) { - wide_int res; - tree t; - tree type = TREE_TYPE (parg1); - signop sign = TYPE_SIGN (type); - wi::overflow_type overflow = wi::OVF_NONE; - - wi::tree_to_wide_ref arg1 = wi::to_wide (parg1); - wide_int arg2 = wi::to_wide (parg2, TYPE_PRECISION (type)); - + wide_int tmp; + overflow = wi::OVF_NONE; switch (code) { case BIT_IOR_EXPR: @@ -999,37 +995,41 @@ int_const_binop_2 (enum tree_code code, const_tree parg1, const_tree parg2, case LSHIFT_EXPR: if (wi::neg_p (arg2)) { - arg2 = -arg2; + tmp = -arg2; if (code == RSHIFT_EXPR) code = LSHIFT_EXPR; else code = RSHIFT_EXPR; } + else +tmp = arg2; if (code == RSHIFT_EXPR) /* It's unclear from the C standard whether shifts can overflow. The following code ignores overflow; perhaps a C standard interpretation ruling is needed. */ - res = wi::rshift (arg1, arg2, sign); + res = wi::rshift (arg1, tmp, sign); else - res = wi::lshift (arg1, arg2); + res = wi::lshift (arg1, tmp); break; case RROTATE_EXPR: case LROTATE_EXPR: if (wi::neg_p (arg2)) { - arg2 = -arg2; + tmp = -arg2; if (code == RROTATE_EXPR) code = LROTATE_EXPR; else code = RROTATE_EXPR; } + else +tmp = arg2; if (code == RROTATE_EXPR) - res = wi::rrotate (arg1, arg2); + res = wi::rrotate (arg1, tmp); else - res = wi::lrotate (arg1, arg2); + res = wi::lrotate (arg1, tmp); break; case PLUS_EXPR: @@ -1051,49 +1051,49 @@ int_const_binop_2 (enum tree_code code, const_tree parg1, const_tree parg2, case TRUNC_DIV_E
Re: abstract wide int binop code from VRP
On 07/11/2018 08:52 AM, Richard Biener wrote: On Wed, Jul 11, 2018 at 8:48 AM Aldy Hernandez wrote: Hmmm, I think we can do better, and since this hasn't been reviewed yet, I don't think anyone will mind the adjustment to the patch ;-). I really hate int_const_binop_SOME_RANDOM_NUMBER. We should abstract them into properly named poly_int_binop, wide_int_binop, and tree_binop, and then use a default argument for int_const_binop() to get things going. Sorry for more changes in flight, but I thought we could benefit from more cleanups :). OK for trunk pending tests? Much of GCC pre-dates function overloading / default args ;) Heh...and ANSI C. Looks OK but can you please rename your tree_binop to int_cst_binop? Or maybe inline it into int_const_binop, also sharing the force_fit_type () tail with poly_int_binop? I tried both, but inlining looked cleaner :). Done. What about mixed INTEGER_CST / poly_int constants? Shouldn't it be if (neither-poly-nor-integer-cst (arg1 || arg2)) return NULL_TREE; if (poly_int_tree (arg1) || poly_int_tree (arg2)) poly-int-stuff else if (INTEGER_CST && INTEGER_CST) wide-int-stuff ? I see that is a pre-existing issue but if you are at refactoring... wi::to_poly_wide should handle INTEGER_CST operands just fine I hope. This aborted: gcc_assert (NUM_POLY_INT_COEFFS != 1); but even taking it out made the bootstrap die somewhere else. If it's ok, I'd rather not tackle this now, as I have some more cleanups that are pending on this. If you feel strongly, I could do it at a later time. OK pending tests? Aldy gcc/ * fold-const.c (int_const_binop_1): Abstract... (wide_int_binop): ...wide int code here. (poly_int_binop): ...poly int code here. Abstract the rest of int_const_binop_1 into int_const_binop. * fold-const.h (wide_int_binop): New. * tree-vrp.c (vrp_int_const_binop): Call wide_int_binop. Remove useless PLUS/MINUS_EXPR case. (zero_nonzero_bits_from_vr): Move wide int code... (zero_nonzero_bits_from_bounds): ...here. (extract_range_from_binary_expr_1): Move mask optimization code... (range_easy_mask_min_max): ...here. * tree-vrp.h (zero_nonzero_bits_from_bounds): New. (range_easy_mask_min_max): New. diff --git a/gcc/fold-const.c b/gcc/fold-const.c index 97c435fa5e0..ad8c0a69f63 100644 --- a/gcc/fold-const.c +++ b/gcc/fold-const.c @@ -966,21 +966,17 @@ int_binop_types_match_p (enum tree_code code, const_tree type1, const_tree type2 && TYPE_MODE (type1) == TYPE_MODE (type2); } -/* Subroutine of int_const_binop_1 that handles two INTEGER_CSTs. */ +/* Combine two wide ints ARG1 and ARG2 under operation CODE to produce + a new constant in RES. Return FALSE if we don't know how to + evaluate CODE at compile-time. */ -static tree -int_const_binop_2 (enum tree_code code, const_tree parg1, const_tree parg2, - int overflowable) +bool +wide_int_binop (enum tree_code code, + wide_int &res, const wide_int &arg1, const wide_int &arg2, + signop sign, wi::overflow_type &overflow) { - wide_int res; - tree t; - tree type = TREE_TYPE (parg1); - signop sign = TYPE_SIGN (type); - wi::overflow_type overflow = wi::OVF_NONE; - - wi::tree_to_wide_ref arg1 = wi::to_wide (parg1); - wide_int arg2 = wi::to_wide (parg2, TYPE_PRECISION (type)); - + wide_int tmp; + overflow = wi::OVF_NONE; switch (code) { case BIT_IOR_EXPR: @@ -999,37 +995,41 @@ int_const_binop_2 (enum tree_code code, const_tree parg1, const_tree parg2, case LSHIFT_EXPR: if (wi::neg_p (arg2)) { - arg2 = -arg2; + tmp = -arg2; if (code == RSHIFT_EXPR) code = LSHIFT_EXPR; else code = RSHIFT_EXPR; } + else +tmp = arg2; if (code == RSHIFT_EXPR) /* It's unclear from the C standard whether shifts can overflow. The following code ignores overflow; perhaps a C standard interpretation ruling is needed. */ - res = wi::rshift (arg1, arg2, sign); + res = wi::rshift (arg1, tmp, sign); else - res = wi::lshift (arg1, arg2); + res = wi::lshift (arg1, tmp); break; case RROTATE_EXPR: case LROTATE_EXPR: if (wi::neg_p (arg2)) { - arg2 = -arg2; + tmp = -arg2; if (code == RROTATE_EXPR) code = LROTATE_EXPR; else code = RROTATE_EXPR; } + else +tmp = arg2; if (code == RROTATE_EXPR) - res = wi::rrotate (arg1, arg2); + res = wi::rrotate (arg1, tmp); else - res = wi::lrotate (arg1, arg2); + res = wi::lrotate (arg1, tmp); break; case PLUS_EXPR: @@ -1051,49 +1051,49 @@ int_const_binop_2 (enum tree_code code, const_tree parg1, const_tree parg2, case TRUNC_DIV_EXPR: case EXACT_DIV_EXPR: if (arg2 == 0) - return NULL_TREE; + return false; res = wi::div_trunc (arg1, arg2, sign, &overflow); break; case F
Re: abstract wide int binop code from VRP
On 07/11/2018 01:33 PM, Richard Sandiford wrote: Aldy Hernandez writes: On 07/11/2018 08:52 AM, Richard Biener wrote: On Wed, Jul 11, 2018 at 8:48 AM Aldy Hernandez wrote: Hmmm, I think we can do better, and since this hasn't been reviewed yet, I don't think anyone will mind the adjustment to the patch ;-). I really hate int_const_binop_SOME_RANDOM_NUMBER. We should abstract them into properly named poly_int_binop, wide_int_binop, and tree_binop, and then use a default argument for int_const_binop() to get things going. Sorry for more changes in flight, but I thought we could benefit from more cleanups :). OK for trunk pending tests? Much of GCC pre-dates function overloading / default args ;) Heh...and ANSI C. Looks OK but can you please rename your tree_binop to int_cst_binop? Or maybe inline it into int_const_binop, also sharing the force_fit_type () tail with poly_int_binop? I tried both, but inlining looked cleaner :). Done. What about mixed INTEGER_CST / poly_int constants? Shouldn't it be if (neither-poly-nor-integer-cst (arg1 || arg2)) return NULL_TREE; if (poly_int_tree (arg1) || poly_int_tree (arg2)) poly-int-stuff else if (INTEGER_CST && INTEGER_CST) wide-int-stuff ? I see that is a pre-existing issue but if you are at refactoring... wi::to_poly_wide should handle INTEGER_CST operands just fine I hope. This aborted: gcc_assert (NUM_POLY_INT_COEFFS != 1); but even taking it out made the bootstrap die somewhere else. If it's ok, I'd rather not tackle this now, as I have some more cleanups that are pending on this. If you feel strongly, I could do it at a later time. OK pending tests? LGTM FWIW, just some nits: -/* Subroutine of int_const_binop_1 that handles two INTEGER_CSTs. */ +/* Combine two wide ints ARG1 and ARG2 under operation CODE to produce + a new constant in RES. Return FALSE if we don't know how to + evaluate CODE at compile-time. */ -static tree -int_const_binop_2 (enum tree_code code, const_tree parg1, const_tree parg2, - int overflowable) +bool +wide_int_binop (enum tree_code code, + wide_int &res, const wide_int &arg1, const wide_int &arg2, + signop sign, wi::overflow_type &overflow) { IMO we should avoid pass-back by reference like the plague. :-) It's especially confusing when the code does things like: case FLOOR_DIV_EXPR: if (arg2 == 0) return false; res = wi::div_floor (arg1, arg2, sign, &overflow); break; > > It looked at first like it was taking the address of a local variable > and failing to propagate the information back up. > > I think we should stick to using pointers for this kind of thing. > Hmmm, I kinda like them. It just takes some getting used to, but generally yields cleaner code as you don't have to keep using '*' everywhere. Plus, the callee can assume the pointer is non-zero. -/* Combine two integer constants PARG1 and PARG2 under operation CODE - to produce a new constant. Return NULL_TREE if we don't know how +/* Combine two poly int's ARG1 and ARG2 under operation CODE to + produce a new constant in RES. Return FALSE if we don't know how to evaluate CODE at compile-time. */ -static tree -int_const_binop_1 (enum tree_code code, const_tree arg1, const_tree arg2, - int overflowable) +static bool +poly_int_binop (poly_wide_int &res, enum tree_code code, + const_tree arg1, const_tree arg2, + signop sign, wi::overflow_type &overflow) { Would be good to be consistent about the order of the result and code arguments. Here it's "result, code" (which seems better IMO), but in wide_int_binop it's "code, result". Agreed. Fixed. +/* Combine two integer constants PARG1 and PARG2 under operation CODE + to produce a new constant. Return NULL_TREE if we don't know how + to evaluate CODE at compile-time. */ + tree -int_const_binop (enum tree_code code, const_tree arg1, const_tree arg2) +int_const_binop (enum tree_code code, const_tree arg1, const_tree arg2, +int overflowable) s/PARG/ARG/g in comment. Fixed. { - return int_const_binop_1 (code, arg1, arg2, 1); + bool success = false; + poly_wide_int poly_res; + tree type = TREE_TYPE (arg1); + signop sign = TYPE_SIGN (type); + wi::overflow_type overflow = wi::OVF_NONE; + + if (TREE_CODE (arg1) == INTEGER_CST && TREE_CODE (arg2) == INTEGER_CST) +{ + wide_int warg1 = wi::to_wide (arg1), res; + wide_int warg2 = wi::to_wide (arg2, TYPE_PRECISION (type)); + success = wide_int_binop (code, res, warg1, warg2, sign, overflow); + poly_res = res; +} + else if (poly_int_tree_p (arg1) && poly_int_tree_p (arg2)) +success = poly_int_binop (poly_res, code,
Re: abstract wide int binop code from VRP
On 07/12/2018 04:29 AM, Richard Sandiford wrote: Aldy Hernandez writes: On 07/11/2018 01:33 PM, Richard Sandiford wrote: Aldy Hernandez writes: On 07/11/2018 08:52 AM, Richard Biener wrote: On Wed, Jul 11, 2018 at 8:48 AM Aldy Hernandez wrote: Hmmm, I think we can do better, and since this hasn't been reviewed yet, I don't think anyone will mind the adjustment to the patch ;-). I really hate int_const_binop_SOME_RANDOM_NUMBER. We should abstract them into properly named poly_int_binop, wide_int_binop, and tree_binop, and then use a default argument for int_const_binop() to get things going. Sorry for more changes in flight, but I thought we could benefit from more cleanups :). OK for trunk pending tests? Much of GCC pre-dates function overloading / default args ;) Heh...and ANSI C. Looks OK but can you please rename your tree_binop to int_cst_binop? Or maybe inline it into int_const_binop, also sharing the force_fit_type () tail with poly_int_binop? I tried both, but inlining looked cleaner :). Done. What about mixed INTEGER_CST / poly_int constants? Shouldn't it be if (neither-poly-nor-integer-cst (arg1 || arg2)) return NULL_TREE; if (poly_int_tree (arg1) || poly_int_tree (arg2)) poly-int-stuff else if (INTEGER_CST && INTEGER_CST) wide-int-stuff ? I see that is a pre-existing issue but if you are at refactoring... wi::to_poly_wide should handle INTEGER_CST operands just fine I hope. This aborted: gcc_assert (NUM_POLY_INT_COEFFS != 1); but even taking it out made the bootstrap die somewhere else. If it's ok, I'd rather not tackle this now, as I have some more cleanups that are pending on this. If you feel strongly, I could do it at a later time. OK pending tests? LGTM FWIW, just some nits: -/* Subroutine of int_const_binop_1 that handles two INTEGER_CSTs. */ +/* Combine two wide ints ARG1 and ARG2 under operation CODE to produce + a new constant in RES. Return FALSE if we don't know how to + evaluate CODE at compile-time. */ -static tree -int_const_binop_2 (enum tree_code code, const_tree parg1, const_tree parg2, - int overflowable) +bool +wide_int_binop (enum tree_code code, + wide_int &res, const wide_int &arg1, const wide_int &arg2, + signop sign, wi::overflow_type &overflow) { IMO we should avoid pass-back by reference like the plague. :-) It's especially confusing when the code does things like: case FLOOR_DIV_EXPR: if (arg2 == 0) return false; res = wi::div_floor (arg1, arg2, sign, &overflow); break; > > It looked at first like it was taking the address of a local variable > and failing to propagate the information back up. > > I think we should stick to using pointers for this kind of thing. > Hmmm, I kinda like them. It just takes some getting used to, but generally yields cleaner code as you don't have to keep using '*' everywhere. Plus, the callee can assume the pointer is non-zero. But it can assume that for "*" too. The problem isn't getting used to them. I've worked on codebases where this is the norm before and had to live with it. It's just always felt a mistake even then. E.g. compare: int_const_binop_1 (code, arg1, arg2, overflowable); and: wide_int_binop (code, res, arg1, arg2, sign, overflow); There's just no visual clue to tell you that "overflowable" is an input and "overflow" is an output. ("overflowable" could well be an output from the raw meaning: "the calculation might have induced an overflow, but we're not sure".) I wouldn't mind so much if we had a convention that the outputs had a suffix to make it clear that they were outputs. But that would be more typing than "*". Perhaps a proposal for the coding conventions document? ;-) Aldy
Re: abstract wide int binop code from VRP
On 07/13/2018 03:02 AM, Richard Biener wrote: On Thu, Jul 12, 2018 at 10:12 AM Aldy Hernandez wrote: So besides the general discussion about references/pointers for out parameters let's stay consistet within related APIs. This means wide_int_binop should have a wide_int wide_int_binop (enum tree_code, const wide_int &, const wide_int &, signop, wi::overflow_type *) signature. Notice how I elided the out wide_int parameter to be the return value which means the function isn't supposed to fail which means gcc_unreachable () for "unhandled" tree codes. wide_int_binop was returning failure for: case CEIL_DIV_EXPR: if (arg2 == 0) return false; res = wi::div_ceil (arg1, arg2, sign, &overflow); break; case ROUND_DIV_EXPR: if (arg2 == 0) return false; res = wi::div_round (arg1, arg2, sign, &overflow); break; etc How do you suggest we indicate success/failure to the caller? Aldy It's more like an exceptional state anyway. The same goes for the poly_int_binop signature. The already existing wi::accumulate_overflow should probably be re-done as wi::overflow_type wi::accumulate_overflow (wi::overflow_type, wi::overflow_type); Richard. Thanks for the review! Aldy
cleanup cross product code in VRP
Howdy! I've abstracted out the cross product calculations into its own function, and have adapted it to deal with wide ints so it's more reusable. It required some shuffling around, and implementing things a bit different, but things should be behave as before. I also renamed vrp_int_const_binop to make its intent clearer, especially now that it's really just a wrapper to wide_int_binop that deals with overflow. (If wide_int_binop_overflow is generally useful, perhaps we could merge it with wide_int_overflow.) Tested on x86-64 Linux. OK? commit eb55ab7557efb23bc6dc34d00eadabf2a73a4995 Author: Aldy Hernandez Date: Mon Jul 16 14:15:37 2018 +0200 * tree-vrp.c (wide_int_binop_overflow): Rename from vrp_int_const_binop. Rewrite to work on trees. (extract_range_from_multiplicative_op_1): Abstract code to... (wide_int_range_min_max): ...here. (wide_int_range_cross_product): ...and here. * tree-vrp.h (wide_int_range_cross_product): New. diff --git a/gcc/tree-vrp.c b/gcc/tree-vrp.c index 2e1ee86a161..36349228c10 100644 --- a/gcc/tree-vrp.c +++ b/gcc/tree-vrp.c @@ -968,64 +968,43 @@ value_range_constant_singleton (value_range *vr) indeterminate. */ static bool -vrp_int_const_binop (enum tree_code code, tree val1, tree val2, wide_int *res) +wide_int_binop_overflow (wide_int &res, + enum tree_code code, + const wide_int &w0, const wide_int &w1, + signop sign, bool overflow_undefined) { - wi::overflow_type overflow = wi::OVF_NONE; - signop sign = TYPE_SIGN (TREE_TYPE (val1)); - wide_int w1 = wi::to_wide (val1); - wide_int w2 = wi::to_wide (val2); - - switch (code) -{ -case RSHIFT_EXPR: -case LSHIFT_EXPR: - w2 = wi::to_wide (val2, TYPE_PRECISION (TREE_TYPE (val1))); - /* FALLTHRU */ -case MULT_EXPR: -case TRUNC_DIV_EXPR: -case EXACT_DIV_EXPR: -case FLOOR_DIV_EXPR: -case CEIL_DIV_EXPR: -case ROUND_DIV_EXPR: - if (!wide_int_binop (*res, code, w1, w2, sign, &overflow)) - return false; - break; - -default: - gcc_unreachable (); -} + wi::overflow_type overflow; + if (!wide_int_binop (res, code, w0, w1, sign, &overflow)) +return false; /* If the operation overflowed return -INF or +INF depending on the operation and the combination of signs of the operands. */ - if (overflow - && TYPE_OVERFLOW_UNDEFINED (TREE_TYPE (val1))) -{ - int sign1 = tree_int_cst_sgn (val1); - int sign2 = tree_int_cst_sgn (val2); - - /* Notice that we only need to handle the restricted set of - operations handled by extract_range_from_binary_expr. - Among them, only multiplication, addition and subtraction - can yield overflow without overflown operands because we - are working with integral types only... except in the - case VAL1 = -INF and VAL2 = -1 which overflows to +INF - for division too. */ - - /* For multiplication, the sign of the overflow is given - by the comparison of the signs of the operands. */ - if ((code == MULT_EXPR && sign1 == sign2) + if (overflow && overflow_undefined) +{ + switch (code) + { + case MULT_EXPR: + /* For multiplication, the sign of the overflow is given + by the comparison of the signs of the operands. */ + if (sign == UNSIGNED || w0.sign_mask () == w1.sign_mask ()) + res = wi::max_value (w0.get_precision (), sign); + else + res = wi::min_value (w0.get_precision (), sign); + return true; + + case TRUNC_DIV_EXPR: + case FLOOR_DIV_EXPR: + case CEIL_DIV_EXPR: + case EXACT_DIV_EXPR: + case ROUND_DIV_EXPR: /* For division, the only case is -INF / -1 = +INF. */ - || code == TRUNC_DIV_EXPR - || code == FLOOR_DIV_EXPR - || code == CEIL_DIV_EXPR - || code == EXACT_DIV_EXPR - || code == ROUND_DIV_EXPR) - *res = wi::max_value (TYPE_PRECISION (TREE_TYPE (val1)), sign); - else - *res = wi::min_value (TYPE_PRECISION (TREE_TYPE (val1)), sign); - return true; -} + res = wi::max_value (w0.get_precision (), sign); + return true; + default: + gcc_unreachable (); + } +} return !overflow; } @@ -1127,6 +1106,72 @@ ranges_from_anti_range (value_range *ar, return vr0->type != VR_UNDEFINED; } +/* Order 2 sets of wide int ranges (w0/w1, w2/w3) and set MIN/MAX + accordingly. */ + +static void +wide_int_range_min_max (wide_int &min, wide_int &max, + wide_int &w0, wide_int &w1, wide_int &w2, wide_int &w3, + signop sign) +{ + /* Order pairs w0,w1 and w2,w3. */ + if (wi::gt_p (w0, w1, sign)) +std::swap (w0, w1); + if (wi::gt_p (w2, w3, sign)) +std::swap (w2, w3); + + /* Choose min and max from the ordered pairs. */ + min = wi::min (w0, w2, sign); + max = wi::max (w1, w3, sign); +} + +/* Calculate the cross product of two sets of ranges (VR0 and VR1) and + store the result in [RES_LB, RES_UB]. + +
Re: cleanup cross product code in VRP
Hi again! Well, since this hasn't been reviewed and I'm about to overhaul the TYPE_OVERFLOW_WRAPS code anyhow, might as well lump it all in one patch. On 07/16/2018 09:19 AM, Aldy Hernandez wrote: Howdy! I've abstracted out the cross product calculations into its own function, and have adapted it to deal with wide ints so it's more reusable. It required some shuffling around, and implementing things a bit different, but things should be behave as before. I also renamed vrp_int_const_binop to make its intent clearer, especially now that it's really just a wrapper to wide_int_binop that deals with overflow. (If wide_int_binop_overflow is generally useful, perhaps we could merge it with wide_int_overflow.) This is the same as the previous patch, plus I'm abstracting the TYPE_OVERFLOW_WRAPS code as well. With this, the code dealing with MULT_EXPR in vrp gets reduced to handling value_range specific stuff. Yay code re-use! A few notes: This is dead code. I've removed it: - /* If we have an unsigned MULT_EXPR with two VR_ANTI_RANGEs, -drop to VR_VARYING. It would take more effort to compute a -precise range for such a case. For example, if we have -op0 == 65536 and op1 == 65536 with their ranges both being -~[0,0] on a 32-bit machine, we would have op0 * op1 == 0, so -we cannot claim that the product is in ~[0,0]. Note that we -are guaranteed to have vr0.type == vr1.type at this -point. */ - if (vr0.type == VR_ANTI_RANGE - && !TYPE_OVERFLOW_UNDEFINED (expr_type)) - { - set_value_range_to_varying (vr); - return; - } Also, the vrp_int typedef has a weird name, especially when we have widest2_int in gimple-fold.c that does the exact thing. I've moved the common code to wide-int.h and tree.h so we can all share :). At some point we could move the wide_int_range* and wide_int_binop* code into its own file. Tested on x86-64 Linux. OK? gcc/ * wide-int.h (widest2_int): New. * gimple-fold.c (arith_overflowed_p): Use it. * tree.h (widest2_int_cst): New. * tree-vrp.c (wide_int_binop_overflow): Rename from vrp_int_const_binop. Rewrite to work on trees. (extract_range_from_multiplicative_op_1): Abstract code to... (wide_int_range_min_max): ...here. (wide_int_range_cross_product): ...and here. (extract_range_from_binary_expr_1): Abstract overflow code to... (wide_int_range_cross_product_wrapping): ...here. * tree-vrp.h (wide_int_range_cross_product): New. (wide_int_range_cross_product_wrapping): New. diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c index a6b42834d32..027ca4da97c 100644 --- a/gcc/gimple-fold.c +++ b/gcc/gimple-fold.c @@ -3986,9 +3986,6 @@ bool arith_overflowed_p (enum tree_code code, const_tree type, const_tree arg0, const_tree arg1) { - typedef FIXED_WIDE_INT (WIDE_INT_MAX_PRECISION * 2) widest2_int; - typedef generic_wide_int > -widest2_int_cst; widest2_int warg0 = widest2_int_cst (arg0); widest2_int warg1 = widest2_int_cst (arg1); widest2_int wres; diff --git a/gcc/tree-vrp.c b/gcc/tree-vrp.c index 2e1ee86a161..41274b3898c 100644 --- a/gcc/tree-vrp.c +++ b/gcc/tree-vrp.c @@ -968,64 +968,43 @@ value_range_constant_singleton (value_range *vr) indeterminate. */ static bool -vrp_int_const_binop (enum tree_code code, tree val1, tree val2, wide_int *res) +wide_int_binop_overflow (wide_int &res, + enum tree_code code, + const wide_int &w0, const wide_int &w1, + signop sign, bool overflow_undefined) { - wi::overflow_type overflow = wi::OVF_NONE; - signop sign = TYPE_SIGN (TREE_TYPE (val1)); - wide_int w1 = wi::to_wide (val1); - wide_int w2 = wi::to_wide (val2); - - switch (code) -{ -case RSHIFT_EXPR: -case LSHIFT_EXPR: - w2 = wi::to_wide (val2, TYPE_PRECISION (TREE_TYPE (val1))); - /* FALLTHRU */ -case MULT_EXPR: -case TRUNC_DIV_EXPR: -case EXACT_DIV_EXPR: -case FLOOR_DIV_EXPR: -case CEIL_DIV_EXPR: -case ROUND_DIV_EXPR: - if (!wide_int_binop (*res, code, w1, w2, sign, &overflow)) - return false; - break; - -default: - gcc_unreachable (); -} + wi::overflow_type overflow; + if (!wide_int_binop (res, code, w0, w1, sign, &overflow)) +return false; /* If the operation overflowed return -INF or +INF depending on the operation and the combination of signs of the operands. */ - if (overflow - && TYPE_OVERFLOW_UNDEFINED (TREE_TYPE (val1))) -{ - int sign1 = tree_int_cst_sgn (val1); - int sign2 = tree_int_cst_sgn (val2); - - /* Notice that we only need to handle the restricted set of - operations handled by extract_range_from_binary_expr. - Among them, only multiplication, addition and subtraction - can yield overflow without overflown operands because we - are working with integral types only... except in th
Re: cleanup cross product code in VRP
On 07/19/2018 04:18 AM, Richard Biener wrote: On Wed, Jul 18, 2018 at 2:05 PM Aldy Hernandez wrote: Hi again! Well, since this hasn't been reviewed and I'm about to overhaul the TYPE_OVERFLOW_WRAPS code anyhow, might as well lump it all in one patch. On 07/16/2018 09:19 AM, Aldy Hernandez wrote: Howdy! I've abstracted out the cross product calculations into its own function, and have adapted it to deal with wide ints so it's more reusable. It required some shuffling around, and implementing things a bit different, but things should be behave as before. I also renamed vrp_int_const_binop to make its intent clearer, especially now that it's really just a wrapper to wide_int_binop that deals with overflow. (If wide_int_binop_overflow is generally useful, perhaps we could merge it with wide_int_overflow.) This is the same as the previous patch, plus I'm abstracting the TYPE_OVERFLOW_WRAPS code as well. With this, the code dealing with MULT_EXPR in vrp gets reduced to handling value_range specific stuff. Yay code re-use! A few notes: This is dead code. I've removed it: - /* If we have an unsigned MULT_EXPR with two VR_ANTI_RANGEs, -drop to VR_VARYING. It would take more effort to compute a -precise range for such a case. For example, if we have -op0 == 65536 and op1 == 65536 with their ranges both being -~[0,0] on a 32-bit machine, we would have op0 * op1 == 0, so -we cannot claim that the product is in ~[0,0]. Note that we -are guaranteed to have vr0.type == vr1.type at this -point. */ - if (vr0.type == VR_ANTI_RANGE - && !TYPE_OVERFLOW_UNDEFINED (expr_type)) - { - set_value_range_to_varying (vr); - return; - } Also, the vrp_int typedef has a weird name, especially when we have widest2_int in gimple-fold.c that does the exact thing. I've moved the common code to wide-int.h and tree.h so we can all share :). At some point we could move the wide_int_range* and wide_int_binop* code into its own file. Yes. Sometime within the next couple rounds I'll come up with a file name that doesn't hurt my eyes. It seems that the hardest part of programming is actually coming up with sensible file and variable names :-/. Tested on x86-64 Linux. OK? +bool +wide_int_range_cross_product_wrapping (wide_int &res_lb, + wide_int &res_ub, please rename this to sth like wide_int_range_mult_wrapping because it only handles multiplication to not confuse it with the other function. Done. Thanks. Aldy Otherwise OK (and sorry for the delay in reviewing). Thanks, Richard.
abstract remaining wide int operations in VRP
...well, most of them anyhow... I got tired of submitting these piecemeal, and it's probably easier to review them in one go. There should be no difference in functionality, barring an extra call to set_and_canonicalize_value_range (instead of set_value_range) due to the way I've organized multiplication and lshifts for maximal sharing. This also gets rid of some repetitive stuff. I've also added a value_range::dump like wide_int::dump. It makes debugging a lot easier. My next patch will move all the wide_int_range_* stuff into wide-int-range.[hc]. I'm really liking how this is turning out, BTW: a *lot* cleaner, less code duplication, and shareable to boot :). OK pending one more round of tests? Aldy gcc/ * tree-vrp (zero_nonzero_bits_from_bounds): Rename to... (wide_int_set_zero_nonzero_bits): ...this. (zero_nonzero_bits_from_vr): Rename to... (vrp_set_zero_nonzero_bits): ...this. (extract_range_from_multiplicative_op_1): Abstract wide int code... (wide_int_range_multiplicative_op): ...here. (extract_range_from_binary_expr_1): Extract wide int binary operations into their own functions. (wide_int_range_lshift): New. (wide_int_range_can_optimize_bit_op): New. (wide_int_range_shift_undefined_p): New. (wide_int_range_bit_xor): New. (wide_int_range_bit_ior): New. (wide_int_range_bit_and): New. (wide_int_range_trunc_mod): New. (extract_range_into_wide_ints): New. (vrp_shift_undefined_p): New. (extract_range_from_multiplicative_op): New. (vrp_can_optimize_bit_op): New. * tree-vrp.h (value_range::dump): New. (wide_int_range_multiplicative_op): New. (wide_int_range_lshift):New. (wide_int_range_shift_undefined_p): New. (wide_int_range_bit_xor): New. (wide_int_range_bit_ior): New. (wide_int_range_bit_and): New. (wide_int_range_trunc_mod): New. (zero_nonzero_bits_from_bounds): Rename to... (wide_int_set_zero_nonzero_bits): ...this. (zero_nonzero_bits_from_vr): Rename to... (vrp_set_zero_nonzero_bits): ...this. (range_easy_mask_min_max): Rename to... (wide_int_range_can_optimize_bit_op): this. * vr-values.c (simplify_bit_ops_using_ranges): Rename zero_nonzero_bits_from_vr into vrp_set_zero_nonzero_bits. diff --git a/gcc/tree-vrp.c b/gcc/tree-vrp.c index 7ab8898b453..619be7d07be 100644 --- a/gcc/tree-vrp.c +++ b/gcc/tree-vrp.c @@ -1008,45 +1008,54 @@ wide_int_binop_overflow (wide_int &res, return !overflow; } -/* For range [LB, UB] compute two wide_int bitmasks. In *MAY_BE_NONZERO - bitmask, if some bit is unset, it means for all numbers in the range - the bit is 0, otherwise it might be 0 or 1. In *MUST_BE_NONZERO - bitmask, if some bit is set, it means for all numbers in the range - the bit is 1, otherwise it might be 0 or 1. */ +/* For range [LB, UB] compute two wide_int bit masks. + + In the MAY_BE_NONZERO bit mask, 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. + + In the MUST_BE_NONZERO bit mask, 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. */ void -zero_nonzero_bits_from_bounds (signop sign, - const wide_int &lb, const wide_int &ub, - wide_int *may_be_nonzero, - wide_int *must_be_nonzero) +wide_int_set_zero_nonzero_bits (signop sign, +const wide_int &lb, const wide_int &ub, +wide_int &may_be_nonzero, +wide_int &must_be_nonzero) { - *may_be_nonzero = wi::minus_one (lb.get_precision ()); - *must_be_nonzero = wi::zero (lb.get_precision ()); + may_be_nonzero = wi::minus_one (lb.get_precision ()); + must_be_nonzero = wi::zero (lb.get_precision ()); if (wi::eq_p (lb, ub)) { - *may_be_nonzero = lb; - *must_be_nonzero = *may_be_nonzero; + may_be_nonzero = lb; + must_be_nonzero = may_be_nonzero; } else if (wi::ge_p (lb, 0, sign) || wi::lt_p (ub, 0, sign)) { wide_int xor_mask = lb ^ ub; - *may_be_nonzero = lb | ub; - *must_be_nonzero = lb & ub; + may_be_nonzero = lb | ub; + must_be_nonzero = lb & ub; if (xor_mask != 0) { wide_int mask = wi::mask (wi::floor_log2 (xor_mask), false, -may_be_nonzero->get_precision ()); - *may_be_nonzero = *may_be_nonzero | mask; - *must_be_nonzero = wi::bit_and_not (*must_be_nonzero, mask); +may_be_nonzero.get_precision ()); + may_be_nonzero = may_be_nonzero | mask; + must_be_nonzero = wi::bit_and_not (must_be_nonzero, mask); } } } -/* Like zero_nonzero_bits_from_bounds, but use the range in value_range VR. */ +/* Value range wrapper for wide_int_set_zero_nonzero_bits. + + Compute MAY_BE_NONZERO and MUST_BE_NONZERO bit masks for range in VR. + + Return TRUE if VR was a constant range and we were able to compute + the bit masks. */ bool -zero_nonzero_bits_from_vr (const tree expr_type, +vrp_set_zero_nonzero_bits (const tree expr_type, value_range *vr, wide_int *may_be_nonzero, wide_
[COMMITTED] [range-ops] Remove unneeded parameters from rv_fold.
Now that the floating point version of rv_fold calculates its result in an frange, we can remove the superfluous LB, UB, and MAYBE_NAN arguments. gcc/ChangeLog: * range-op-float.cc (range_operator::fold_range): Remove superfluous code. (range_operator::rv_fold): Remove unneeded arguments. (operator_plus::rv_fold): Same. (operator_minus::rv_fold): Same. (operator_mult::rv_fold): Same. (operator_div::rv_fold): Same. * range-op-mixed.h: Remove lb, ub, and maybe_nan arguments from rv_fold methods. * range-op.h: Same. --- gcc/range-op-float.cc | 72 +-- gcc/range-op-mixed.h | 6 gcc/range-op.h| 2 -- 3 files changed, 14 insertions(+), 66 deletions(-) diff --git a/gcc/range-op-float.cc b/gcc/range-op-float.cc index cb2c0a61a1a..ffa3dec133e 100644 --- a/gcc/range-op-float.cc +++ b/gcc/range-op-float.cc @@ -63,41 +63,14 @@ range_operator::fold_range (frange &r, tree type, } frange res; - REAL_VALUE_TYPE lb, ub; - bool maybe_nan; - rv_fold (res, type, - lb, ub, maybe_nan, + rv_fold (r, type, op1.lower_bound (), op1.upper_bound (), op2.lower_bound (), op2.upper_bound (), trio.op1_op2 ()); - // Handle possible NANs by saturating to the appropriate INF if only - // one end is a NAN. If both ends are a NAN, just return a NAN. - bool lb_nan = real_isnan (&lb); - bool ub_nan = real_isnan (&ub); - if (lb_nan && ub_nan) -{ - r.set_nan (type); - gcc_checking_assert (r == res); - return true; -} - if (lb_nan) -lb = dconstninf; - else if (ub_nan) -ub = dconstinf; - - r.set (type, lb, ub); - - if (lb_nan || ub_nan || maybe_nan - || op1.maybe_isnan () - || op2.maybe_isnan ()) -// Keep the default NAN (with a varying sign) set by the setter. -; - else -r.clear_nan (); - + if (r.known_isnan ()) +return true; if (op1.maybe_isnan () || op2.maybe_isnan ()) -res.update_nan (); - gcc_checking_assert (r == res); +r.update_nan (); // If the result has overflowed and flag_trapping_math, folding this // operation could elide an overflow or division by zero exception. @@ -130,17 +103,11 @@ range_operator::fold_range (frange &r, tree type, // UB, the final range has the possibility of a NAN. void range_operator::rv_fold (frange &r, tree type, -REAL_VALUE_TYPE &lb, -REAL_VALUE_TYPE &ub, -bool &maybe_nan, const REAL_VALUE_TYPE &, const REAL_VALUE_TYPE &, const REAL_VALUE_TYPE &, const REAL_VALUE_TYPE &, relation_kind) const { - lb = dconstninf; - ub = dconstinf; - maybe_nan = true; r.set (type, dconstninf, dconstinf, nan_state (true)); } @@ -2479,14 +2446,15 @@ operator_plus::op2_range (frange &r, tree type, void operator_plus::rv_fold (frange &r, tree type, - REAL_VALUE_TYPE &lb, REAL_VALUE_TYPE &ub, - bool &maybe_nan, const REAL_VALUE_TYPE &lh_lb, const REAL_VALUE_TYPE &lh_ub, const REAL_VALUE_TYPE &rh_lb, const REAL_VALUE_TYPE &rh_ub, relation_kind) const { + REAL_VALUE_TYPE lb, ub; + bool maybe_nan = false; + frange_arithmetic (PLUS_EXPR, type, lb, lh_lb, rh_lb, dconstninf); frange_arithmetic (PLUS_EXPR, type, ub, lh_ub, rh_ub, dconstinf); @@ -2496,8 +2464,6 @@ operator_plus::rv_fold (frange &r, tree type, // [+INF] + [-INF] = NAN else if (real_isinf (&lh_ub, false) && real_isinf (&rh_lb, true)) maybe_nan = true; - else -maybe_nan = false; // Handle possible NANs by saturating to the appropriate INF if only // one end is a NAN. If both ends are a NAN, just return a NAN. @@ -2543,14 +2509,15 @@ operator_minus::op2_range (frange &r, tree type, void operator_minus::rv_fold (frange &r, tree type, -REAL_VALUE_TYPE &lb, REAL_VALUE_TYPE &ub, -bool &maybe_nan, const REAL_VALUE_TYPE &lh_lb, const REAL_VALUE_TYPE &lh_ub, const REAL_VALUE_TYPE &rh_lb, const REAL_VALUE_TYPE &rh_ub, relation_kind) const { + REAL_VALUE_TYPE lb, ub; + bool maybe_nan = false; + frange_arithmetic (MINUS_EXPR, type, lb, lh_lb, rh_ub, dconstninf); frange_arithmetic (MINUS_EXPR, type, ub, lh_ub, rh_lb, dconstinf); @@ -2560,8 +2527,6 @@ operator_minus::rv_fold (frange &r, tree type, // [-INF] - [-INF] = NAN else if (real_isinf (&lh_lb, true) && real_isinf (&rh_lb, true)) maybe_nan = true; - else -maybe_nan = false; // Handle possible NANs by saturating to the appropriate INF if only // one end is a NAN. If both end
[COMMITTED] [range-ops] Add frange& argument to rv_fold.
The floating point version of rv_fold returns its result in 3 pieces: the lower bound, the upper bound, and a maybe_nan bit. It is cleaner to return everything in an frange, thus bringing the floating point version of rv_fold in line with the integer version. This first patch adds an frange argument, while keeping the current functionality, and asserting that we get the same results. In a follow-up patch I will nuke the now useless 3 arguments. Splitting this into two patches makes it easier to bisect any problems if any should arise. gcc/ChangeLog: * range-op-float.cc (range_operator::fold_range): Pass frange argument to rv_fold. (range_operator::rv_fold): Add frange argument. (operator_plus::rv_fold): Same. (operator_minus::rv_fold): Same. (operator_mult::rv_fold): Same. (operator_div::rv_fold): Same. * range-op-mixed.h: Add frange argument to rv_fold methods. * range-op.h: Same. --- gcc/range-op-float.cc | 120 +- gcc/range-op-mixed.h | 15 +++--- gcc/range-op.h| 4 +- 3 files changed, 107 insertions(+), 32 deletions(-) diff --git a/gcc/range-op-float.cc b/gcc/range-op-float.cc index 0951bd385a9..cb2c0a61a1a 100644 --- a/gcc/range-op-float.cc +++ b/gcc/range-op-float.cc @@ -62,9 +62,11 @@ range_operator::fold_range (frange &r, tree type, return true; } + frange res; REAL_VALUE_TYPE lb, ub; bool maybe_nan; - rv_fold (lb, ub, maybe_nan, type, + rv_fold (res, type, + lb, ub, maybe_nan, op1.lower_bound (), op1.upper_bound (), op2.lower_bound (), op2.upper_bound (), trio.op1_op2 ()); @@ -75,6 +77,7 @@ range_operator::fold_range (frange &r, tree type, if (lb_nan && ub_nan) { r.set_nan (type); + gcc_checking_assert (r == res); return true; } if (lb_nan) @@ -92,6 +95,10 @@ range_operator::fold_range (frange &r, tree type, else r.clear_nan (); + if (op1.maybe_isnan () || op2.maybe_isnan ()) +res.update_nan (); + gcc_checking_assert (r == res); + // If the result has overflowed and flag_trapping_math, folding this // operation could elide an overflow or division by zero exception. // Avoid returning a singleton +-INF, to keep the propagators (DOM @@ -122,19 +129,19 @@ range_operator::fold_range (frange &r, tree type, // MAYBE_NAN is set to TRUE if, in addition to any result in LB or // UB, the final range has the possibility of a NAN. void -range_operator::rv_fold (REAL_VALUE_TYPE &lb, - REAL_VALUE_TYPE &ub, - bool &maybe_nan, - tree type ATTRIBUTE_UNUSED, - const REAL_VALUE_TYPE &lh_lb ATTRIBUTE_UNUSED, - const REAL_VALUE_TYPE &lh_ub ATTRIBUTE_UNUSED, - const REAL_VALUE_TYPE &rh_lb ATTRIBUTE_UNUSED, - const REAL_VALUE_TYPE &rh_ub ATTRIBUTE_UNUSED, - relation_kind) const +range_operator::rv_fold (frange &r, tree type, +REAL_VALUE_TYPE &lb, +REAL_VALUE_TYPE &ub, +bool &maybe_nan, +const REAL_VALUE_TYPE &, +const REAL_VALUE_TYPE &, +const REAL_VALUE_TYPE &, +const REAL_VALUE_TYPE &, relation_kind) const { lb = dconstninf; ub = dconstinf; maybe_nan = true; + r.set (type, dconstninf, dconstinf, nan_state (true)); } bool @@ -2471,8 +2478,9 @@ operator_plus::op2_range (frange &r, tree type, } void -operator_plus::rv_fold (REAL_VALUE_TYPE &lb, REAL_VALUE_TYPE &ub, - bool &maybe_nan, tree type, +operator_plus::rv_fold (frange &r, tree type, + REAL_VALUE_TYPE &lb, REAL_VALUE_TYPE &ub, + bool &maybe_nan, const REAL_VALUE_TYPE &lh_lb, const REAL_VALUE_TYPE &lh_ub, const REAL_VALUE_TYPE &rh_lb, @@ -2490,6 +2498,21 @@ operator_plus::rv_fold (REAL_VALUE_TYPE &lb, REAL_VALUE_TYPE &ub, maybe_nan = true; else maybe_nan = false; + + // Handle possible NANs by saturating to the appropriate INF if only + // one end is a NAN. If both ends are a NAN, just return a NAN. + bool lb_nan = real_isnan (&lb); + bool ub_nan = real_isnan (&ub); + if (lb_nan && ub_nan) +{ + r.set_nan (type); + return; +} + if (lb_nan) +lb = dconstninf; + else if (ub_nan) +ub = dconstinf; + r.set (type, lb, ub, nan_state (maybe_nan)); } @@ -2519,8 +2542,9 @@ operator_minus::op2_range (frange &r, tree type, } void -operator_minus::rv_fold (REAL_VALUE_TYPE &lb, REAL_VALUE_TYPE &ub, -bool &maybe_nan, tree type, +operator_minus::rv_fold (frange &r, tree type, +REAL_VALU
[PATCH] [range-op] Remove unused variable in fold_range.
Missed this... gcc/ChangeLog: * range-op-float.cc (range_operator::fold_range): Delete unused variable. --- gcc/range-op-float.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/gcc/range-op-float.cc b/gcc/range-op-float.cc index ffa3dec133e..75816942f8c 100644 --- a/gcc/range-op-float.cc +++ b/gcc/range-op-float.cc @@ -62,7 +62,6 @@ range_operator::fold_range (frange &r, tree type, return true; } - frange res; rv_fold (r, type, op1.lower_bound (), op1.upper_bound (), op2.lower_bound (), op2.upper_bound (), trio.op1_op2 ()); -- 2.41.0
[committed] add debugging routines for assert_info structure
We have a way of dumping the asserts in the asserts_for on-the-side structure, but we have no way of dumping the assert_info structure that evrp uses. Being able to dump these are quite useful in debugging. Committed as obvious. commit 9244efff3f7ccce390c70c83d9fc46cef6152bb4 Author: Aldy Hernandez Date: Mon Nov 4 12:14:56 2019 +0100 Implement debugging functions for assert_info's. diff --git a/gcc/ChangeLog b/gcc/ChangeLog index f29939a0dd8..d00348891bd 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,8 @@ +2019-11-04 Aldy Hernandez + + * tree-vrp.c (dump_assert_info): New. + (dump_asserts_info): New. + 2019-11-04 Tamar Christina * tree-vect-slp.c (vectorize_slp_instance_root_stmt): Initialize rstmt. diff --git a/gcc/tree-vrp.c b/gcc/tree-vrp.c index da6b6151b4a..7c35802dacc 100644 --- a/gcc/tree-vrp.c +++ b/gcc/tree-vrp.c @@ -2114,6 +2114,45 @@ debug_all_asserts (void) dump_all_asserts (stderr); } +/* Dump assert_info structure. */ + +void +dump_assert_info (FILE *file, const assert_info &assert) +{ + fprintf (file, "Assert for: "); + print_generic_expr (file, assert.name); + fprintf (file, "\n\tPREDICATE: expr=["); + print_generic_expr (file, assert.expr); + fprintf (file, "] %s ", get_tree_code_name (assert.comp_code)); + fprintf (file, "val=["); + print_generic_expr (file, assert.val); + fprintf (file, "]\n\n"); +} + +DEBUG_FUNCTION void +debug (const assert_info &assert) +{ + dump_assert_info (stderr, assert); +} + +/* Dump a vector of assert_info's. */ + +void +dump_asserts_info (FILE *file, const vec &asserts) +{ + for (unsigned i = 0; i < asserts.length (); ++i) +{ + dump_assert_info (file, asserts[i]); + fprintf (file, "\n"); +} +} + +DEBUG_FUNCTION void +debug (const vec &asserts) +{ + dump_asserts_info (stderr, asserts); +} + /* Push the assert info for NAME, EXPR, COMP_CODE and VAL to ASSERTS. */ static void
[committed] handle VR_UNDEFINED in normalize_addresses
We are currently ICEing when calling normalize_address on an undefined range because undefines do not have types. Committed as obvious. commit 12cdfbe483c8981ba724582c1faa1432e762b549 Author: Aldy Hernandez Date: Mon Nov 4 12:16:37 2019 +0100 Handle VR_UNDEFINED in value_range_base::normalize_addresses(). diff --git a/gcc/ChangeLog b/gcc/ChangeLog index d00348891bd..407470f0ed6 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,8 @@ +2019-11-04 Aldy Hernandez + + * tree-vrp.c (value_range_base::normalize_addresses): Handle + VR_UNDEFINED. + 2019-11-04 Aldy Hernandez * tree-vrp.c (dump_assert_info): New. diff --git a/gcc/tree-vrp.c b/gcc/tree-vrp.c index 7c35802dacc..c0c1e87a259 100644 --- a/gcc/tree-vrp.c +++ b/gcc/tree-vrp.c @@ -6130,6 +6130,9 @@ value_range::union_ (const value_range *other) value_range_base value_range_base::normalize_addresses () const { + if (undefined_p ()) +return *this; + if (!POINTER_TYPE_P (type ()) || range_has_numeric_bounds_p (this)) return *this;
[committed] remove unused range_int_cst_singleton_p
This function is no longer being called. The behavior in it is handled by the API (value_range_base::singleton_p). Committed as obvious. commit 9d22f224ec66efb0abf1394bff7e3d0080498417 Author: Aldy Hernandez Date: Mon Nov 4 12:46:00 2019 +0100 Remove unused range_int_cst_singleton_p. diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 407470f0ed6..548f2fca970 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,8 @@ +2019-11-04 Aldy Hernandez + + * tree-vrp.c (range_int_cst_singleton_p): Remove. + * tree-vrp.h (range_int_cst_singleton_p): Remove. + 2019-11-04 Aldy Hernandez * tree-vrp.c (value_range_base::normalize_addresses): Handle diff --git a/gcc/tree-vrp.c b/gcc/tree-vrp.c index c0c1e87a259..070db903147 100644 --- a/gcc/tree-vrp.c +++ b/gcc/tree-vrp.c @@ -945,15 +945,6 @@ range_int_cst_p (const value_range_base *vr) return (vr->kind () == VR_RANGE && range_has_numeric_bounds_p (vr)); } -/* Return true if VR is a INTEGER_CST singleton. */ - -bool -range_int_cst_singleton_p (const value_range_base *vr) -{ - return (range_int_cst_p (vr) - && tree_int_cst_equal (vr->min (), vr->max ())); -} - /* Return the single symbol (an SSA_NAME) contained in T if any, or NULL_TREE otherwise. We only handle additive operations and set NEG to true if the symbol is negated and INV to the invariant part, if any. */ diff --git a/gcc/tree-vrp.h b/gcc/tree-vrp.h index 4bfdfeb8f79..1fde88fe0fe 100644 --- a/gcc/tree-vrp.h +++ b/gcc/tree-vrp.h @@ -282,7 +282,6 @@ extern bool infer_value_range (gimple *, tree, tree_code *, tree *); extern bool vrp_bitmap_equal_p (const_bitmap, const_bitmap); extern bool range_int_cst_p (const value_range_base *); -extern bool range_int_cst_singleton_p (const value_range_base *); extern int compare_values (tree, tree); extern int compare_values_warnv (tree, tree, bool *);
always handle pointers in vrp_val*{min,max}
When I added the range-ops code, I had to teach vrp_val_is*{min,max} about pointers, since it would just ignore them and return NULL. I was overly cautious about changing existing behavior and decided to add a handle_pointers argument, with a default value of FALSE, and just changed the needed callers on a need-to basis. Little by little we've been changing every caller to TRUE, and it's becoming increasingly obvious that we should always handle pointers. I can't think of a reason why we wouldn't want to handle them, and if there is ever one, surely the caller can avoid calling these functions. OK for trunk? commit 4c4ce7228754d847daa3b99e4ee0d4c466512d1a Author: Aldy Hernandez Date: Mon Nov 4 17:55:57 2019 +0100 Remove handle_pointers argument from all the vrp_val*{min,max} functions. Always assume pointers should be handled. diff --git a/gcc/ChangeLog b/gcc/ChangeLog index c585360b537..a9870475ea7 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,26 @@ +2019-11-04 Aldy Hernandez + + * tree-vrp.h (vrp_val_min): Remove handle_pointers argument. + (vrp_val_max): Same. + (vrp_val_is_min): Same. + (vrp_val_is_max): Same. + (value_range_base::nonzero_p): Remove last argument to + vrp_val_is_max. + * tree-vrp.c (vrp_val_min): Remove handle_pointers argument. + (vrp_val_max): Same. + (vrp_val_is_min): Same. + (vrp_val_is_max): Same. + (value_range_base::set_varying): Remove last argument to vrp_val*. + (value_range_base::dump): Same. + (value_range_base::set): Same. + (value_range_base::normalize_symbolics): Same. + (value_range_base::num_pairs): Same. + (value_range_base::lower_bound): Same. + (value_range_base::upper_bound): Same. + (ranges_from_anti_range): Remove handle_pointers argument. + (value_range_base::singleton_p): Remove last argument to + ranges_from_anti_range. + 2019-11-04 Aldy Hernandez * tree-vrp.c (range_int_cst_singleton_p): Remove. diff --git a/gcc/tree-vrp.c b/gcc/tree-vrp.c index 070db903147..2d3e76af2a8 100644 --- a/gcc/tree-vrp.c +++ b/gcc/tree-vrp.c @@ -71,8 +71,7 @@ along with GCC; see the file COPYING3. If not see static bool ranges_from_anti_range (const value_range_base *ar, - value_range_base *vr0, value_range_base *vr1, - bool handle_pointers = false); + value_range_base *vr0, value_range_base *vr1); /* Set of SSA names found live during the RPO traversal of the function for still active basic-blocks. */ @@ -310,8 +309,8 @@ value_range_base::set_varying (tree type) m_kind = VR_VARYING; if (supports_type_p (type)) { - m_min = vrp_val_min (type, true); - m_max = vrp_val_max (type, true); + m_min = vrp_val_min (type); + m_max = vrp_val_max (type); } else /* We can't do anything range-wise with these types. */ @@ -382,7 +381,7 @@ value_range_base::singleton_p (tree *result) const if (num_pairs () == 1) { value_range_base vr0, vr1; - ranges_from_anti_range (this, &vr0, &vr1, true); + ranges_from_anti_range (this, &vr0, &vr1); return vr0.singleton_p (result); } } @@ -429,7 +428,7 @@ value_range_base::dump (FILE *file) const fprintf (file, ", "); if (supports_type_p (ttype) - && vrp_val_is_max (max (), true) + && vrp_val_is_max (max ()) && TYPE_PRECISION (ttype) != 1) fprintf (file, "+INF"); else @@ -574,11 +573,11 @@ static assert_locus **asserts_for; /* Return the maximum value for TYPE. */ tree -vrp_val_max (const_tree type, bool handle_pointers) +vrp_val_max (const_tree type) { if (INTEGRAL_TYPE_P (type)) return TYPE_MAX_VALUE (type); - if (POINTER_TYPE_P (type) && handle_pointers) + if (POINTER_TYPE_P (type)) { wide_int max = wi::max_value (TYPE_PRECISION (type), TYPE_SIGN (type)); return wide_int_to_tree (const_cast (type), max); @@ -589,11 +588,11 @@ vrp_val_max (const_tree type, bool handle_pointers) /* Return the minimum value for TYPE. */ tree -vrp_val_min (const_tree type, bool handle_pointers) +vrp_val_min (const_tree type) { if (INTEGRAL_TYPE_P (type)) return TYPE_MIN_VALUE (type); - if (POINTER_TYPE_P (type) && handle_pointers) + if (POINTER_TYPE_P (type)) return build_zero_cst (const_cast (type)); return NULL_TREE; } @@ -604,9 +603,9 @@ vrp_val_min (const_tree type, bool handle_pointers) is not == to the integer constant with the same value in the type. */ bool -vrp_val_is_max (const_tree val, bool handle_pointers) +vrp_val_is_max (const_tree val) { - tree type_max = vrp_val_max (TREE_TYPE (val), handle_pointers); + tree type_max = vrp_val_max (TREE_TYPE (val)); return (val == type_max || (type_max != NULL_TREE && operand_equal_p (val, type_max, 0))); @@ -615,9 +614,9 @@ vrp_val_is_max (const_tree val, bool handle_pointers) /* Return whether VAL is equal to the minimum value of its type. */ bool -vrp_val_is_
[committed] do not create non-canonical ranges in value_range_base::invert
value_range_base::invert is twiddling the range kind in place. You can't do that, because you may create non-canonical ranges. Fixed by using the canonicalizing constructor. Committed as obvious. commit 7a1b10ff2446e3e7800a19e8970bfe57f894cda9 Author: Aldy Hernandez Date: Mon Nov 4 21:15:47 2019 +0100 Use the value_range_base constructors in value_range_base::invert to make sure we build canonically correct ranges. diff --git a/gcc/ChangeLog b/gcc/ChangeLog index c585360b537..6bdd5ffddbd 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,8 @@ +2019-11-04 Aldy Hernandez + + * tree-vrp.c (value_range_base::invert): Use constructors to build + range. + 2019-11-04 Aldy Hernandez * tree-vrp.c (range_int_cst_singleton_p): Remove. diff --git a/gcc/tree-vrp.c b/gcc/tree-vrp.c index 070db903147..085308e519f 100644 --- a/gcc/tree-vrp.c +++ b/gcc/tree-vrp.c @@ -6286,10 +6286,12 @@ value_range_base::contains_p (tree cst) const void value_range_base::invert () { + /* We can't just invert VR_RANGE and VR_ANTI_RANGE because we may + create non-canonical ranges. Use the constructors instead. */ if (m_kind == VR_RANGE) -m_kind = VR_ANTI_RANGE; +*this = value_range_base (VR_ANTI_RANGE, m_min, m_max); else if (m_kind == VR_ANTI_RANGE) -m_kind = VR_RANGE; +*this = value_range_base (VR_RANGE, m_min, m_max); else gcc_unreachable (); }
do not special case pointers while canonicalizing ranges
There's no need to special case pointers when converting suitable VR_ANTI_RANGE's into VR_RANGE's now that vrp_val*{min, max} handle pointers by default. OK?
Re: do not special case pointers while canonicalizing ranges
And now with patch! On 11/4/19 11:15 PM, Aldy Hernandez wrote: There's no need to special case pointers when converting suitable VR_ANTI_RANGE's into VR_RANGE's now that vrp_val*{min, max} handle pointers by default. OK? commit 2a8a783d542158405d2b90b5361669a8aa56ea83 Author: Aldy Hernandez Date: Mon Nov 4 21:19:36 2019 +0100 Do not special case pointers in value_range_base::set. diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 32af8bb3e9a..6fbbf87e294 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,7 @@ +2019-11-04 Aldy Hernandez + + * tree-vrp.c (value_range_base::set): Do not special case pointers. + 2019-11-04 Aldy Hernandez * tree-vrp.h (vrp_val_min): Remove handle_pointers argument. diff --git a/gcc/tree-vrp.c b/gcc/tree-vrp.c index 0a0d7d760a7..452895bfc24 100644 --- a/gcc/tree-vrp.c +++ b/gcc/tree-vrp.c @@ -793,10 +793,8 @@ value_range_base::set (enum value_range_kind kind, tree min, tree max) { /* For -fstrict-enums we may receive out-of-range ranges so consider values < -INF and values > INF as -INF/INF as well. */ - bool is_min = (INTEGRAL_TYPE_P (type) - && tree_int_cst_compare (min, TYPE_MIN_VALUE (type)) <= 0); - bool is_max = (INTEGRAL_TYPE_P (type) - && tree_int_cst_compare (max, TYPE_MAX_VALUE (type)) >= 0); + bool is_min = vrp_val_is_min (min); + bool is_max = vrp_val_is_max (max); if (is_min && is_max) { @@ -816,10 +814,7 @@ value_range_base::set (enum value_range_kind kind, tree min, tree max) min = max = vrp_val_min (TREE_TYPE (min)); kind = VR_RANGE; } - else if (is_min - /* Allow non-zero pointers to be normalized to [1,MAX]. */ - || (POINTER_TYPE_P (TREE_TYPE (min)) - && integer_zerop (min))) + else if (is_min) { tree one = build_int_cst (TREE_TYPE (max), 1); min = int_const_binop (PLUS_EXPR, max, one);
handle symbolics when comparing ranges
value_range_base::operator== was originally lifted from a world where symbolics didn't exist (the ranger branch), but symbolics do exist in mainline. Although this isn't causing a problem yet, as soon as someone tries to compare non numeric ranges, we'll die. Using operand_equal_p simplifies the comparison code drastically. I suppose if/when we get multiple sub-ranges in value_range_base, we'll have to adapt this function again to compare things piece wise. For now, let's keep things simple. OK pending tests? commit d1177659bd26ac8122410dee092f5ca427b4558b Author: Aldy Hernandez Date: Mon Nov 4 21:20:26 2019 +0100 Use operand_equal_p in value_range_base::operator== so we can handle symbolics without dying. diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 6fbbf87e294..3ebe7fd4348 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,8 @@ +2019-11-04 Aldy Hernandez + + * tree-vrp.c (value_range_base::operator==): Use operand_equal_p + instead of wide-int's, to properly handle symbolics. + 2019-11-04 Aldy Hernandez * tree-vrp.c (value_range_base::set): Do not special case pointers. diff --git a/gcc/tree-vrp.c b/gcc/tree-vrp.c index 452895bfc24..e683339f3cd 100644 --- a/gcc/tree-vrp.c +++ b/gcc/tree-vrp.c @@ -6336,16 +6336,12 @@ value_range_base::operator== (const value_range_base &r) const if (undefined_p ()) return r.undefined_p (); - if (num_pairs () != r.num_pairs () - || !range_compatible_p (type (), r.type ())) + if (!range_compatible_p (type (), r.type ())) return false; - for (unsigned p = 0; p < num_pairs (); p++) -if (wi::ne_p (lower_bound (p), r.lower_bound (p)) - || wi::ne_p (upper_bound (p), r.upper_bound (p))) - return false; - - return true; + return (m_kind == r.m_kind + && operand_equal_p (m_min, r.m_min, 0) + && operand_equal_p (m_max, r.m_max, 0)); } /* Visit all arguments for PHI node PHI that flow through executable
Re: handle symbolics when comparing ranges
On 11/4/19 11:45 PM, Andrew MacLeod wrote: On 11/4/19 5:23 PM, Aldy Hernandez wrote: value_range_base::operator== was originally lifted from a world where symbolics didn't exist (the ranger branch), but symbolics do exist in mainline. Although this isn't causing a problem yet, as soon as someone tries to compare non numeric ranges, we'll die. Using operand_equal_p simplifies the comparison code drastically. I suppose if/when we get multiple sub-ranges in value_range_base, we'll have to adapt this function again to compare things piece wise. For now, let's keep things simple. OK pending tests? Oh, we brought over the multiple sub-range bits to value_range_base... yeah we can remove that and just check for operand equality. we'll deal with multiple subranges when thats a thing. Is this any different than just calling value_range_base::equal_p()? Ooops, indeed, that's the same thing. Adjusted. OK pending tests? commit eb3ca5b2eeb84233a485981e140c6f910deb4bcc Author: Aldy Hernandez Date: Mon Nov 4 21:20:26 2019 +0100 Use value_range_base::equal_p in value_range_base::operator== so we can handle symbolics without dying. diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 254b3950a8e..b9f61791270 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,8 @@ +2019-11-04 Aldy Hernandez + + * tree-vrp.c (value_range_base::operator==): Use equal_p to + properly handle symbolics. + 2019-11-04 Aldy Hernandez * tree-vrp.c (value_range_base::set): Do not special case pointers. diff --git a/gcc/tree-vrp.c b/gcc/tree-vrp.c index 452895bfc24..9d6a4ebc7fe 100644 --- a/gcc/tree-vrp.c +++ b/gcc/tree-vrp.c @@ -6333,19 +6333,7 @@ range_compatible_p (tree t1, tree t2) bool value_range_base::operator== (const value_range_base &r) const { - if (undefined_p ()) -return r.undefined_p (); - - if (num_pairs () != r.num_pairs () - || !range_compatible_p (type (), r.type ())) -return false; - - for (unsigned p = 0; p < num_pairs (); p++) -if (wi::ne_p (lower_bound (p), r.lower_bound (p)) - || wi::ne_p (upper_bound (p), r.upper_bound (p))) - return false; - - return true; + return equal_p (r); } /* Visit all arguments for PHI node PHI that flow through executable
Re: handle symbolics when comparing ranges
On 11/5/19 3:55 AM, Andrew MacLeod wrote: On 11/4/19 6:05 PM, Aldy Hernandez wrote: On 11/4/19 11:45 PM, Andrew MacLeod wrote: On 11/4/19 5:23 PM, Aldy Hernandez wrote: value_range_base::operator== was originally lifted from a world where symbolics didn't exist (the ranger branch), but symbolics do exist in mainline. Although this isn't causing a problem yet, as soon as someone tries to compare non numeric ranges, we'll die. Using operand_equal_p simplifies the comparison code drastically. I suppose if/when we get multiple sub-ranges in value_range_base, we'll have to adapt this function again to compare things piece wise. For now, let's keep things simple. OK pending tests? Oh, we brought over the multiple sub-range bits to value_range_base... yeah we can remove that and just check for operand equality. we'll deal with multiple subranges when thats a thing. Is this any different than just calling value_range_base::equal_p()? Ooops, indeed, that's the same thing. Adjusted. OK pending tests? yeah, approved. Final committed patch attached. I had to remove the now unused range_compatible_p function, as it's no longer necessary. Thanks. commit 10eefa1934bb1833c85d1c445f4da01b933c0909 Author: Aldy Hernandez Date: Mon Nov 4 21:20:26 2019 +0100 Use value_range_base::equal_p in value_range_base::operator== so we can handle symbolics without dying. diff --git a/gcc/ChangeLog b/gcc/ChangeLog index e726ff6d0a0..1c2fff16295 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,9 @@ +2019-11-05 Aldy Hernandez + + * tree-vrp.c (value_range_base::operator==): Use equal_p to + properly handle symbolics. + (range_compatible_p): Remove. + 2019-11-04 Kamlesh Kumar * common.opt (-fabi-version): Document =14. diff --git a/gcc/tree-vrp.c b/gcc/tree-vrp.c index 452895bfc24..a6d44e9dc6d 100644 --- a/gcc/tree-vrp.c +++ b/gcc/tree-vrp.c @@ -6319,33 +6319,10 @@ value_range_base::intersect (const value_range_base &r) dump_flags |= TDF_DETAILS; } -/* Return TRUE if two types are compatible for range operations. */ - -static bool -range_compatible_p (tree t1, tree t2) -{ - if (POINTER_TYPE_P (t1) && POINTER_TYPE_P (t2)) -return true; - - return types_compatible_p (t1, t2); -} - bool value_range_base::operator== (const value_range_base &r) const { - if (undefined_p ()) -return r.undefined_p (); - - if (num_pairs () != r.num_pairs () - || !range_compatible_p (type (), r.type ())) -return false; - - for (unsigned p = 0; p < num_pairs (); p++) -if (wi::ne_p (lower_bound (p), r.lower_bound (p)) - || wi::ne_p (upper_bound (p), r.upper_bound (p))) - return false; - - return true; + return equal_p (r); } /* Visit all arguments for PHI node PHI that flow through executable
[committed] make vrp_bitmap_equal_p static
This function is only used once, so there's no need for it to be externally visible. Committed as obvious. commit 53363b9234db6e3b696abc53b24ea1e0d2547038 Author: Aldy Hernandez Date: Tue Nov 5 05:10:41 2019 +0100 Move vrp_bitmap_equal_p above its only use and make it static. diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 1c2fff16295..f492ea6da0c 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,9 @@ +2019-11-05 Aldy Hernandez + + * tree-vrp.h (vrp_bitmap_equal_p): Remove. + * tree-vrp.c (vrp_bitmap_equal_p): Move before use and make + static. + 2019-11-05 Aldy Hernandez * tree-vrp.c (value_range_base::operator==): Use equal_p to diff --git a/gcc/tree-vrp.c b/gcc/tree-vrp.c index a6d44e9dc6d..e926670b962 100644 --- a/gcc/tree-vrp.c +++ b/gcc/tree-vrp.c @@ -254,6 +254,18 @@ value_range_base::equal_p (const value_range_base &other) const && vrp_operand_equal_p (m_max, other.m_max)); } +/* Return true if the bitmaps B1 and B2 are equal. */ + +static bool +vrp_bitmap_equal_p (const_bitmap b1, const_bitmap b2) +{ + return (b1 == b2 + || ((!b1 || bitmap_empty_p (b1)) + && (!b2 || bitmap_empty_p (b2))) + || (b1 && b2 + && bitmap_equal_p (b1, b2))); +} + /* Returns TRUE if THIS == OTHER. Ignores the equivalence bitmap if IGNORE_EQUIVS is TRUE. */ @@ -910,18 +922,6 @@ vrp_operand_equal_p (const_tree val1, const_tree val2) return true; } -/* Return true, if the bitmaps B1 and B2 are equal. */ - -bool -vrp_bitmap_equal_p (const_bitmap b1, const_bitmap b2) -{ - return (b1 == b2 - || ((!b1 || bitmap_empty_p (b1)) - && (!b2 || bitmap_empty_p (b2))) - || (b1 && b2 - && bitmap_equal_p (b1, b2))); -} - static bool range_has_numeric_bounds_p (const value_range_base *vr) { diff --git a/gcc/tree-vrp.h b/gcc/tree-vrp.h index 5cd94733188..3861634fb7e 100644 --- a/gcc/tree-vrp.h +++ b/gcc/tree-vrp.h @@ -279,8 +279,6 @@ extern void register_edge_assert_for (tree, edge, enum tree_code, extern bool stmt_interesting_for_vrp (gimple *); extern bool infer_value_range (gimple *, tree, tree_code *, tree *); -extern bool vrp_bitmap_equal_p (const_bitmap, const_bitmap); - extern bool range_int_cst_p (const value_range_base *); extern int compare_values (tree, tree);
[committed] move vrp_set_zero_nonzero_bits into vr-values.c
This function is only used in vr-values.c, so it doesn't need to be externally visible or defined in tree-vrp.c. In moving it I noticed that wide_int_range_set_zero_nonzero_bits is redundant, as we can grab the version in range-op. I've also renamed the function to vr_set_zero_nonzero_bits (removed the "P"), as it's more a value_range thing than a VRP thing. Committed as obvious. commit a59ec913fa95849a356d00d211510b29eab5565f Author: Aldy Hernandez Date: Tue Nov 5 09:48:41 2019 +0100 Move vrp_set_zero_nonzero_bits from tree-vrp.c into vr-values.c, and make it use wi_set_zero_nonzero_bits. Remove the now redundant wide_int_range_set_zero_nonzero_bits. diff --git a/gcc/ChangeLog b/gcc/ChangeLog index f492ea6da0c..786dee727dc 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,14 @@ +2019-11-05 Aldy Hernandez + + * range-op.cc (wi_set_zero_nonzero_bits): Remove static qualifier. + * range-op.h (wi_set_zero_nonzero_bits): New prototype. + * tree-vrp.h (vrp_set_zero_nonzero_bits): Remove. + * tree-vrp.c (wide_int_range_set_zero_nonzero_bits): Remove. + (vrp_set_zero_nonzero_bits): Move to... + * vr-values.c (vr_set_zero_nonzero_bits): ...here. + (vr_values::simplify_bit_ops_using_ranges): Rename + vrp_set_zero_nonzero_bits to vr_set_zero_nonzero_bits. + 2019-11-05 Aldy Hernandez * tree-vrp.h (vrp_bitmap_equal_p): Remove. diff --git a/gcc/range-op.cc b/gcc/range-op.cc index fc31485384b..56e8a20ad9e 100644 --- a/gcc/range-op.cc +++ b/gcc/range-op.cc @@ -1847,7 +1847,7 @@ wi_optimize_and_or (value_range_base &r, // for all numbers in the range the bit is 1, otherwise it might be 0 // or 1. -static void +void wi_set_zero_nonzero_bits (tree type, const wide_int &lb, const wide_int &ub, wide_int &maybe_nonzero, diff --git a/gcc/range-op.h b/gcc/range-op.h index f6510758163..e531b918263 100644 --- a/gcc/range-op.h +++ b/gcc/range-op.h @@ -82,7 +82,10 @@ protected: }; extern range_operator *range_op_handler (enum tree_code code, tree type); - extern void range_cast (value_range_base &, tree type); +extern void wi_set_zero_nonzero_bits (tree type, + const wide_int &, const wide_int &, + wide_int &maybe_nonzero, + wide_int &mustbe_nonzero); #endif // GCC_RANGE_OP_H diff --git a/gcc/tree-vrp.c b/gcc/tree-vrp.c index e926670b962..e1d5c7cb98c 100644 --- a/gcc/tree-vrp.c +++ b/gcc/tree-vrp.c @@ -1260,69 +1260,6 @@ value_range_base::value_inside_range (tree val) const return !!cmp2; } -/* For range [LB, UB] compute two wide_int bit masks. - - In the MAY_BE_NONZERO bit mask, 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. - - In the MUST_BE_NONZERO bit mask, 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. */ - -static inline void -wide_int_range_set_zero_nonzero_bits (signop sign, - const wide_int &lb, const wide_int &ub, - wide_int &may_be_nonzero, - wide_int &must_be_nonzero) -{ - may_be_nonzero = wi::minus_one (lb.get_precision ()); - must_be_nonzero = wi::zero (lb.get_precision ()); - - if (wi::eq_p (lb, ub)) -{ - may_be_nonzero = lb; - must_be_nonzero = may_be_nonzero; -} - else if (wi::ge_p (lb, 0, sign) || wi::lt_p (ub, 0, sign)) -{ - wide_int xor_mask = lb ^ ub; - may_be_nonzero = lb | ub; - must_be_nonzero = lb & ub; - if (xor_mask != 0) - { - wide_int mask = wi::mask (wi::floor_log2 (xor_mask), false, -may_be_nonzero.get_precision ()); - may_be_nonzero = may_be_nonzero | mask; - must_be_nonzero = wi::bit_and_not (must_be_nonzero, mask); - } -} -} - -/* value_range wrapper for wide_int_range_set_zero_nonzero_bits above. - - Return TRUE if VR was a constant range and we were able to compute - the bit masks. */ - -bool -vrp_set_zero_nonzero_bits (const tree expr_type, - const value_range_base *vr, - wide_int *may_be_nonzero, - wide_int *must_be_nonzero) -{ - if (!range_int_cst_p (vr)) -{ - *may_be_nonzero = wi::minus_one (TYPE_PRECISION (expr_type)); - *must_be_nonzero = wi::zero (TYPE_PRECISION (expr_type)); - return false; -} - wide_int_range_set_zero_nonzero_bits (TYPE_SIGN (expr_type), - wi::to_wide (vr->min ()), - wi::to_wide (vr->max ()), - *may_be_nonzero, *must_be_nonzero); - return true; -} - /* Create two value-ranges in *VR0 and *VR1 from the anti-range *AR so that *VR0 U *VR1 == *AR. Returns true if that is possible, false otherwise. If *AR can be represented with a single range diff --git a/gcc/tree-vrp.h b/gcc/tree-vrp.h index 3861634fb7e..0bf33caba85 100644 --- a/gcc/tree-vrp.h +++ b/gcc/tree-vrp.h @@ -299,8 +299,6 @@ void range_fold_binary_expr (value_range_base *, enum tree_code, tree type,
make range_int_cst_p work with any numeric range (VR_ANTI_RANGE, etc)
The function range_int_cst_p only works with VR_RANGE's at the moment. This is silly because VR_ANTI_RANGE and even VR_VARYING can contain numeric bounds. I have fixed this oversight and have made the function return the bounds in MIN/MAX. This simplifies a lot of code, because there is no longer a need to special case VR_VARYING and VR_ANTI_RANGE, as well as pick at the individual range components outside of the API. The patch has the pleasant side-effect of bringing more things into the API fold. Basically, any access to either value_range::min(), max(), or kind(), is suspect and a big hint that the code should be rewritten to use the API (contains_p, varying_p, zero_p, etc). One of the primary culprits of API noncompliance is the sprintf and strlen warning code. Mind you, not due to negligence on the author, but because we had no value-range API when Martin added the passes. I realize it's nobody's responsibility to fix older value-range code, and I'll probably end up doing it myself (next cycle??), but I could definitely use a hand from the experts, as it's intricate and delicate code. Speak of which, in converting dump_strlen_info() to use the new range_int_cst_p, I noticed a lot of the code disappeared if we used the API. Martin, if you'd prefer not to dump varying, undefined, etc, let me know and we can gate that call to vr.dump(). I took the liberty because it was simple, clean, and hidden away in an internal debugging helper. OK for trunk? commit cba4b59ef2e0e6821d63cfa959d201f22534eb69 Author: Aldy Hernandez Date: Tue Nov 5 10:54:22 2019 +0100 Make range_int_cst_p work with any numeric range, not just VR_RANGE. This includes VR_ANTI_RANGE as well as VR_VARYING, as they can also have numeric bounds. Add two new arguments to return the MIN/MAX bounds. Remove the now redundant range_has_numeric_bounds_p. diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 562b69d1aab..5f12d166176 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -48,6 +48,30 @@ * fold-const.c (operand_compare::hash_operand): Remove FIELD_DECL handling. +2019-11-05 Aldy Hernandez + + * gimple-ssa-sprintf.c (get_int_range): Call range_int_cst_p with + min/max arguments. + (format_integer): Same. + (handle_printf_call): Same. + * tree-ssa-strlen.c (compare_nonzero_chars): Same. + (dump_strlen_info): Same. + (get_range_strlen_dynamic): Same. + (count_nonzero_bytes): Same. + * tree-vrp.c (range_has_numeric_bounds_p): Remove. + (extract_range_from_pointer_plus_expr): Call range_int_cst_p with + min/max arguments. + (value_range_base::normalize_addresses): Use range_int_cst_p + instead of removed range_has_numeric_bounds_p. + (range_int_cst_p): New MIN/MAX arguments. + * tree-vrp.h (range_int_cst_p): Add two new arguments. + * vr-values.c (r_values::check_for_binary_op_overflow): Call + range_int_cst_p with min/max arguments. + (vr_values::simplify_div_or_mod_using_ranges): Same. + (vr_set_zero_nonzero_bits): Same. + (range_fits_type_p): Use value_range_base::supports_type_p instead + of open-coding the test. + 2019-11-05 Aldy Hernandez * tree-vrp.h (vrp_bitmap_equal_p): Remove. diff --git a/gcc/gimple-ssa-sprintf.c b/gcc/gimple-ssa-sprintf.c index b548bbd95e3..0029cfc258c 100644 --- a/gcc/gimple-ssa-sprintf.c +++ b/gcc/gimple-ssa-sprintf.c @@ -1023,18 +1023,12 @@ get_int_range (tree arg, HOST_WIDE_INT *pmin, HOST_WIDE_INT *pmax, /* Try to determine the range of values of the integer argument. */ const value_range *vr = CONST_CAST (class vr_values *, vr_values)->get_value_range (arg); + tree min, max; - if (range_int_cst_p (vr)) + if (!vr->varying_p () && range_int_cst_p (vr, &min, &max)) { - HOST_WIDE_INT type_min - = (TYPE_UNSIGNED (argtype) - ? tree_to_uhwi (TYPE_MIN_VALUE (argtype)) - : tree_to_shwi (TYPE_MIN_VALUE (argtype))); - - HOST_WIDE_INT type_max = tree_to_uhwi (TYPE_MAX_VALUE (argtype)); - - *pmin = TREE_INT_CST_LOW (vr->min ()); - *pmax = TREE_INT_CST_LOW (vr->max ()); + *pmin = TREE_INT_CST_LOW (min); + *pmax = TREE_INT_CST_LOW (max); if (*pmin < *pmax) { @@ -1044,8 +1038,12 @@ get_int_range (tree arg, HOST_WIDE_INT *pmin, HOST_WIDE_INT *pmax, and its upper bound is in excess of TYPE_MAX. In that (invalid) case disregard the range and use that of the expected type instead. */ + HOST_WIDE_INT type_min + = (TYPE_UNSIGNED (argtype) + ? tree_to_uhwi (TYPE_MIN_VALUE (argtype)) + : tree_to_shwi (TYPE_MIN_VALUE (argtype))); + HOST_WIDE_INT type_max = tree_to_uhwi (TYPE_MAX_VALUE (argtype)); knownrange = type_min < *pmin || *pmax < type_max; - unknown = false; } } @@ -1326,11 +1324,8 @@ format_integer (const directive &dir, tree arg, const vr_values *vr_values) const value_range *vr = CONST_CAST (class vr_values
Re: make range_int_cst_p work with any numeric range (VR_ANTI_RANGE, etc)
On 11/5/19 3:27 PM, Richard Biener wrote: On Tue, Nov 5, 2019 at 2:15 PM Aldy Hernandez wrote: The function range_int_cst_p only works with VR_RANGE's at the moment. This is silly because VR_ANTI_RANGE and even VR_VARYING can contain numeric bounds. I have fixed this oversight and have made the function return the bounds in MIN/MAX. This simplifies a lot of code, because there is no longer a need to special case VR_VARYING and VR_ANTI_RANGE, as well as pick at the individual range components outside of the API. The patch has the pleasant side-effect of bringing more things into the API fold. Basically, any access to either value_range::min(), max(), or kind(), is suspect and a big hint that the code should be rewritten to use the API (contains_p, varying_p, zero_p, etc). One of the primary culprits of API noncompliance is the sprintf and strlen warning code. Mind you, not due to negligence on the author, but because we had no value-range API when Martin added the passes. I realize it's nobody's responsibility to fix older value-range code, and I'll probably end up doing it myself (next cycle??), but I could definitely use a hand from the experts, as it's intricate and delicate code. Speak of which, in converting dump_strlen_info() to use the new range_int_cst_p, I noticed a lot of the code disappeared if we used the API. Martin, if you'd prefer not to dump varying, undefined, etc, let me know and we can gate that call to vr.dump(). I took the liberty because it was simple, clean, and hidden away in an internal debugging helper. OK for trunk? No. It's a semantic change, no? Don't we for VR_ANTI_RANGE always get [-INF, +INF] back then? Likewise for VARYING? What do we get for UNDEFINED? I think callers are not prepared for this and expect it to return true for "useful" ranges only. If you want this, use a new name, like get_range_bounds (). Also not sure why min/max need to be INTEGER_CST, why not _always_ return something (that is, the fucntion should never need to return false). The patch doesn't look like an improvement, it just adds to confusion. Perhaps you're right, and as Andrew points out, the API already provides {lower,upper}_bound(). I'll shelf this for now, and clean up calls to min(), max(), and kind() at a later time. Thanks. Aldy
[committed] fix indentation issues with value_range_equiv patch
Darn. I thought I had fixed all of them. Committed as obvious. commit 3420097161f9cdc1df27f6ffd058076df3f4f874 Author: Aldy Hernandez Date: Tue Nov 5 17:16:47 2019 +0100 Fix indentation inconsistencies introduced by previous patch. diff --git a/gcc/ChangeLog b/gcc/ChangeLog index d5e0f6135e5..6ab1cc48627 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,12 @@ +2019-11-05 Aldy Hernandez + + * tree-vrp.c (value_range::value_range): Fix whitespace. + (defined_ranges_p): Same. + (range_fold_binary_symbolics_p): Same. + (value_range::intersect_helper): Same. + (value_range::union_helper): Same. + * tree-vrp.h (range_fold_binary_expr): Same. + 2019-11-05 Aldy Hernandez * gimple-fold.c, gimple-loop-versioning.cc, diff --git a/gcc/tree-vrp.c b/gcc/tree-vrp.c index 1328707a154..2879caaf42a 100644 --- a/gcc/tree-vrp.c +++ b/gcc/tree-vrp.c @@ -135,10 +135,8 @@ value_range::value_range (tree type) set_varying (type); } -value_range::value_range (enum value_range_kind kind, -tree type, -const wide_int &wmin, -const wide_int &wmax) +value_range::value_range (enum value_range_kind kind, tree type, + const wide_int &wmin, const wide_int &wmax) { tree min = wide_int_to_tree (type, wmin); tree max = wide_int_to_tree (type, wmax); @@ -147,8 +145,7 @@ value_range::value_range (enum value_range_kind kind, } value_range::value_range (tree type, -const wide_int &wmin, -const wide_int &wmax) + const wide_int &wmin, const wide_int &wmax) { tree min = wide_int_to_tree (type, wmin); tree max = wide_int_to_tree (type, wmax); @@ -1743,8 +1740,7 @@ supported_types_p (value_range *vr, static bool defined_ranges_p (value_range *vr, - const value_range *vr0, - const value_range *vr1 = NULL) + const value_range *vr0, const value_range *vr1 = NULL) { if (vr0->undefined_p () && (!vr1 || vr1->undefined_p ())) { @@ -1770,8 +1766,7 @@ static bool range_fold_binary_symbolics_p (value_range *vr, tree_code code, tree expr_type, - const value_range *vr0, - const value_range *vr1) + const value_range *vr0, const value_range *vr1) { if (vr0->symbolic_p () || vr1->symbolic_p ()) { @@ -5827,8 +5822,7 @@ intersect_ranges (enum value_range_kind *vr0type, ranges. This may not be the smallest possible such range. */ value_range -value_range::intersect_helper (const value_range *vr0, -const value_range *vr1) +value_range::intersect_helper (const value_range *vr0, const value_range *vr1) { /* If either range is VR_VARYING the other one wins. */ if (vr1->varying_p ()) @@ -5941,8 +5935,7 @@ value_range_equiv::intersect (const value_range_equiv *other) smallest possible such range. */ value_range -value_range::union_helper (const value_range *vr0, -const value_range *vr1) +value_range::union_helper (const value_range *vr0, const value_range *vr1) { /* VR0 has the resulting range if VR1 is undefined or VR0 is varying. */ if (vr1->undefined_p () diff --git a/gcc/tree-vrp.h b/gcc/tree-vrp.h index ff01c2abd50..766fb635ab8 100644 --- a/gcc/tree-vrp.h +++ b/gcc/tree-vrp.h @@ -293,8 +293,7 @@ extern tree vrp_val_max (const_tree); void range_fold_unary_expr (value_range *, enum tree_code, tree type, const value_range *, tree op0_type); void range_fold_binary_expr (value_range *, enum tree_code, tree type, - const value_range *, - const value_range *); + const value_range *, const value_range *); extern bool vrp_operand_equal_p (const_tree, const_tree); extern enum value_range_kind intersect_range_with_nonzero_bits
[committed] place value_range_kind at the end of value_range constructors
This patch rewrites the value_range constructors so that value_range_kind is at the end, and defaults to VR_RANGE. This reduces the constructors by 2, and simplifies a lot of the common cases to read: value_range foo(x, y); blah.set(x,y); instead of: value_range foo(VR_RANGE, x, y); blah.set(VR_RANGE x, y); Pre-approved by Andrew. Will commit as soon as tests finish. Aldy commit 272ccc1bc90c551a9c550565ad200edd235c2c0e Author: Aldy Hernandez Date: Wed Nov 13 15:57:32 2019 +0100 Rewrite value_range constructors to the value_range_kind is at the end, and defaults to VR_RANGE. Similarly for set() methods. diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 77ede4d0b2d..982418ec3c6 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,48 @@ +2019-11-13 Aldy Hernandez + + * gimple-fold.c (size_must_be_zero_p): Rewrite use of value_range + constructors and set methods so value_range_kind is the last + argument and defaults to VR_RANGE. + * gimple-ssa-evrp-analyze.c (record_ranges_from_stmt): Same. + * ipa-cp.c (propagate_vr_across_jump_function): Same. + * ipa-prop.c (ipa_get_value_range): Same. + (ipa_compute_jump_functions_for_edge): Same. + * range-op.cc (value_range_from_overflowed_bounds): Same. + (operator_cast::op1_range): Same. + (range_tests): Same. + * range.cc (range_nonzero): Same. + * tree-ssanames.c (get_range_info): Same. + * tree-vrp.c (value_range_equiv::set): Same. + (value_range::value_range): Same. + (value_range_equiv::value_range_equiv): Same. + (value_range_equiv::update): Same. + (value_range_equiv::deep_copy): Same. + (value_range_equiv::move): Same. + (value_range_equiv::set_undefined): Same. + (value_range::set): Same. + (value_range::set_nonzero): Same. + (ranges_from_anti_range): Same. + (extract_range_from_plus_minus_expr): Same. + (value_range::intersect_helper): Same. + (value_range_equiv::intersect): Same. + (value_range::union_helper): Same. + (value_range_equiv::union_): Same. + (value_range::normalize_symbolics): Same. + (value_range::invert): Same. + (determine_value_range_1): Same. + * tree-vrp.h (class value_range): Same. + (class value_range_equiv): Same. + * vr-values.c (set_value_range_to_nonnegative): Same. + (set_value_range_to_truthvalue): Same. + (vr_values::update_value_range): Same. + (vr_values::extract_range_for_var_from_comparison_expr): Same. + (vr_values::extract_range_from_binary_expr): Same. + (vr_values::extract_range_from_comparison): Same. + (vr_values::extract_range_basic): Same. + (vr_values::adjust_range_with_scev): Same. + (vr_values::vrp_evaluate_conditional_warnv_with_ops): Same. + (vr_values::extract_range_from_phi_node): Same. + 2019-11-12 Andre Vieira * tree-vect-loop.c (vect_transform_loop): Don't overwrite epilogues diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c index 1dfb2387533..e176be80efb 100644 --- a/gcc/gimple-fold.c +++ b/gcc/gimple-fold.c @@ -677,8 +677,7 @@ size_must_be_zero_p (tree size) /* Compute the value of SSIZE_MAX, the largest positive value that can be stored in ssize_t, the signed counterpart of size_t. */ wide_int ssize_max = wi::lshift (wi::one (prec), prec - 1) - 1; - value_range valid_range (VR_RANGE, - build_int_cst (type, 0), + value_range valid_range (build_int_cst (type, 0), wide_int_to_tree (type, ssize_max)); value_range vr; get_range_info (size, vr); diff --git a/gcc/gimple-ssa-evrp-analyze.c b/gcc/gimple-ssa-evrp-analyze.c index 3a9fa75a678..5840767a799 100644 --- a/gcc/gimple-ssa-evrp-analyze.c +++ b/gcc/gimple-ssa-evrp-analyze.c @@ -334,7 +334,7 @@ evrp_range_analyzer::record_ranges_from_stmt (gimple *stmt, bool temporary) bitmaps. Ugh. */ value_range_equiv *new_vr = vr_values->allocate_value_range_equiv (); - new_vr->set (vr.kind (), vr.min (), vr.max ()); + new_vr->set (vr.min (), vr.max (), NULL, vr.kind ()); vr.equiv_clear (); push_value_range (output, new_vr); } diff --git a/gcc/ipa-cp.c b/gcc/ipa-cp.c index b1d899976e8..825d7f3e6ba 100644 --- a/gcc/ipa-cp.c +++ b/gcc/ipa-cp.c @@ -2003,7 +2003,7 @@ propagate_vr_across_jump_function (cgraph_edge *cs, ipa_jump_func *jfunc, if (TREE_OVERFLOW_P (val)) val = drop_tree_overflow (val); - value_range tmpvr (VR_RANGE, val, val); + value_range tmpvr (val, val); return dest_lat->meet_with (&tmpvr); } } diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c index a6c135f242b..514dea6acf1 100644 --- a/gcc/ipa-prop.c +++ b/gcc/ipa-prop.c @@ -1821,9 +1821,9 @@ ipa_get_value_range (value_range *tmp) value_ranges. */ static value_range * -ipa_get_value_range (enum value_range_kind type, tree min, tree max) +ipa_get_value_range (enum value_range_kind kind, tree min, tree max) { - value_range tmp (type, min, max); + value_range tmp (min, max, kind); return ipa_get_value_range (&tmp); } @@ -1913,16 +1913,16 @@ ipa_compute_jump_functions_for_edge (struct ipa_func_bo
extract independent value_range bits to value-range.cc
tree-vrp.* is large and difficult to follow, in part because it has a hodgepodge of different interdependent things (value ranges bits, equivalences stuff, actual value range propagation things, etc etc). This patch pulls out the value_range functionality into its own files: value-range.h and value-range.cc. There are no functional changes, but I did shuffle the order of functions around for easier reading. While the old tree-vrp.c had everything spread out, the new value-range.cc file has functions grouped more or less by functionality (constructors first, setters next, predicates next, etc). I did pull out vrp_val* and vrp_operand_equal* because value_range depends on it. I didn't change the name to avoid churn. OK pending tests? Aldy commit 8ccfc09bf5b663b1a309ea5f404b92d6bcfa0f0b Author: Aldy Hernandez Date: Wed Nov 13 17:17:21 2019 +0100 Move plain value_range things to value-range.[hc]*. diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 004711204a8..9b555e7a8b9 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,5 +1,26 @@ 2019-11-13 Aldy Hernandez + * Makefile.in (OBJS): Add value-range.o. + (GTFILES): Add value-range.h. + * gengtype.c (open_base_files): Add value-range.h to list of + header files. + * tree-vrp.c: Move the following value_range related functions: + ranges_from_anti_range, value_range, check, equal_p, symbolic_p, + constant_p, set_undefined, set_varying, may_contain_p, + singleton_p, type, dump, dump_value_range, debug, vrp_val_max, + vrp_val_min, vrp_val_is_min, vrp_val_is_max, set, set_nonzero, + set_zero, vrp_operand_equal_p, range_has_numeric_bounds_p, + value_inside_range, ranges_from_anti_range, union_ranges, + intersect_ranges, intersect_helper, union_helper, union_, + normalize_addresses, normalize_symbolics, num_pairs, lower_bound, + upper_bound, contains_p, invert, intersect... + * value-range.cc: ...to here. + * tree-vrp.h: Move class value_range, enum_value_range_kind, and + associated inline methods from here... + * value-range.h: ...to here. + +(2019-11-13 Aldy Hernandez + * gimple-fold.c (size_must_be_zero_p): Rewrite use of value_range constructors and set methods so value_range_kind is the last argument and defaults to VR_RANGE. diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 0004d46b93d..7d3c13230e4 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1602,6 +1602,7 @@ OBJS = \ typed-splay-tree.o \ unique-ptr-tests.o \ valtrack.o \ + value-range.o \ value-prof.o \ var-tracking.o \ varasm.o \ @@ -2589,6 +2590,7 @@ GTFILES = $(CPPLIB_H) $(srcdir)/input.h $(srcdir)/coretypes.h \ $(srcdir)/target-globals.h \ $(srcdir)/ipa-predicate.h \ $(srcdir)/ipa-fnsummary.h \ + $(srcdir)/value-range.h \ $(srcdir)/vtable-verify.c \ $(srcdir)/asan.c \ $(srcdir)/ubsan.c \ diff --git a/gcc/gengtype.c b/gcc/gengtype.c index 53317337cf8..fa95776876d 100644 --- a/gcc/gengtype.c +++ b/gcc/gengtype.c @@ -1717,6 +1717,7 @@ open_base_files (void) "explow.h", "calls.h", "memmodel.h", "emit-rtl.h", "varasm.h", "stmt.h", "expr.h", "alloc-pool.h", "cselib.h", "insn-addr.h", "optabs.h", "libfuncs.h", "debug.h", "internal-fn.h", "gimple-fold.h", + "value-range.h", "tree-eh.h", "gimple-iterator.h", "gimple-ssa.h", "tree-cfg.h", "tree-vrp.h", "tree-phinodes.h", "ssa-iterators.h", "stringpool.h", "tree-ssanames.h", "tree-ssa-loop.h", "tree-ssa-loop-ivopts.h", diff --git a/gcc/tree-vrp.c b/gcc/tree-vrp.c index 610b65dedec..c47c65bd294 100644 --- a/gcc/tree-vrp.c +++ b/gcc/tree-vrp.c @@ -68,10 +68,6 @@ along with GCC; see the file COPYING3. If not see #include "builtins.h" #include "range-op.h" -static bool -ranges_from_anti_range (const value_range *ar, - value_range *vr0, value_range *vr1); - /* Set of SSA names found live during the RPO traversal of the function for still active basic-blocks. */ static sbitmap *live; @@ -111,11 +107,6 @@ value_range_equiv::set (tree min, tree max, bitmap equiv, check (); } -value_range::value_range (tree min, tree max, value_range_kind kind) -{ - set (min, max, kind); -} - value_range_equiv::value_range_equiv (tree min, tree max, bitmap equiv, value_range_kind kind) { @@ -129,21 +120,6 @@ value_range_equiv::value_range_equiv (const value_range &other) set (other.min(), other.max (), NULL, other.kind ()); } -value_range::value_range (tree type) -{ - set_varying (type); -} - -value_range::value_range (tree type, - const wide_int &wmin, const wide_int &wmax, - enum value_range_kind kind) -{ - tree min = wide_int_to_tree (type, wmin); - tree max = wide_int_to_tree (type, wmax); -
Re: [PATCH] middle-end/114604 - ranger allocates bitmap without initialized obstack
On Mon, Apr 8, 2024 at 11:50 AM Richard Biener wrote: > > The following fixes ranger bitmap allocation when invoked from IPA > context where the global bitmap obstack possibly isn't initialized. > Instead of trying to use one of the ranger obstacks the following > simply initializes the global bitmap obstack around an active ranger. > > Bootstrapped and tested on x86_64-unknown-linux-gnu with bitmap_alloc > instrumentation (all ICEs gone, so ranger was the only offender). > > OK? > > Thanks, > Richard. > > PR middle-end/114604 > * gimple-range.cc (enable_ranger): Initialize the global > bitmap obstack. > (disable_ranger): Release it. > --- > gcc/gimple-range.cc | 4 > 1 file changed, 4 insertions(+) > > diff --git a/gcc/gimple-range.cc b/gcc/gimple-range.cc > index c16b776c1e3..4d3b1ce8588 100644 > --- a/gcc/gimple-range.cc > +++ b/gcc/gimple-range.cc > @@ -689,6 +689,8 @@ enable_ranger (struct function *fun, bool use_imm_uses) > { >gimple_ranger *r; > > + bitmap_obstack_initialize (NULL); > + >gcc_checking_assert (!fun->x_range_query); >r = new gimple_ranger (use_imm_uses); >fun->x_range_query = r; > @@ -705,6 +707,8 @@ disable_ranger (struct function *fun) >gcc_checking_assert (fun->x_range_query); >delete fun->x_range_query; >fun->x_range_query = NULL; > + > + bitmap_obstack_release (NULL); Are you not allowed to initialize/use obstacks unless bitmap_obstack_initialize(NULL) is called? If so, wouldn't it be better to lazily initialize it downstream (bitmap_alloc, or whomever needs it initialized)? Unless I'm misunderstanding something, it doesn't seem like ranger is the correct place to fix this. Aldy
Re: [PATCH] middle-end/114604 - ranger allocates bitmap without initialized obstack
On Mon, Apr 8, 2024 at 5:54 PM Jakub Jelinek wrote: > > On Mon, Apr 08, 2024 at 05:40:23PM +0200, Aldy Hernandez wrote: > > > PR middle-end/114604 > > > * gimple-range.cc (enable_ranger): Initialize the global > > > bitmap obstack. > > > (disable_ranger): Release it. > > > --- > > > gcc/gimple-range.cc | 4 > > > 1 file changed, 4 insertions(+) > > > > > > diff --git a/gcc/gimple-range.cc b/gcc/gimple-range.cc > > > index c16b776c1e3..4d3b1ce8588 100644 > > > --- a/gcc/gimple-range.cc > > > +++ b/gcc/gimple-range.cc > > > @@ -689,6 +689,8 @@ enable_ranger (struct function *fun, bool > > > use_imm_uses) > > > { > > >gimple_ranger *r; > > > > > > + bitmap_obstack_initialize (NULL); > > > + > > >gcc_checking_assert (!fun->x_range_query); > > >r = new gimple_ranger (use_imm_uses); > > >fun->x_range_query = r; > > > @@ -705,6 +707,8 @@ disable_ranger (struct function *fun) > > >gcc_checking_assert (fun->x_range_query); > > >delete fun->x_range_query; > > >fun->x_range_query = NULL; > > > + > > > + bitmap_obstack_release (NULL); > > > > Are you not allowed to initialize/use obstacks unless > > bitmap_obstack_initialize(NULL) is called? > > You can use it with some other obstack, just not the default one. > > > If so, wouldn't it be > > better to lazily initialize it downstream (bitmap_alloc, or whomever > > needs it initialized)? > > No, you still need to decide where is the safe point to release it. > Unlike the non-default bitmap_obstack_initialize/bitmap_obstack_release, > the default one can nest (has associated nesting counter). So, the above > patch just says that ranger starts using the default obstack in > enable_ranger and stops using it in disable_ranger and anything ranger > associated in the obstack can be freed at that point. I thought ranger never used the default one: $ grep bitmap_obstack_initialize *value* *range* value-relation.cc: bitmap_obstack_initialize (&m_bitmaps); value-relation.cc: bitmap_obstack_initialize (&m_bitmaps); gimple-range-cache.cc: bitmap_obstack_initialize (&m_bitmaps); gimple-range-gori.cc: bitmap_obstack_initialize (&m_bitmaps); gimple-range-infer.cc: bitmap_obstack_initialize (&m_bitmaps); gimple-range-phi.cc: bitmap_obstack_initialize (&m_bitmaps); or even: $ grep obstack.*NULL *value* *range* value-range-storage.cc:obstack_free (&m_obstack, NULL); value-relation.cc: obstack_free (&m_chain_obstack, NULL); value-relation.cc: obstack_free (&m_chain_obstack, NULL); gimple-range-infer.cc: obstack_free (&m_list_obstack, NULL); value-range-storage.cc:obstack_free (&m_obstack, NULL); I'm obviously missing something here. Aldy
Re: [PATCH] middle-end/114604 - ranger allocates bitmap without initialized obstack
On Mon, Apr 8, 2024 at 6:29 PM Richard Biener wrote: > > > > > Am 08.04.2024 um 18:09 schrieb Aldy Hernandez : > > > > On Mon, Apr 8, 2024 at 5:54 PM Jakub Jelinek wrote: > >> > >> On Mon, Apr 08, 2024 at 05:40:23PM +0200, Aldy Hernandez wrote: > >>>>PR middle-end/114604 > >>>>* gimple-range.cc (enable_ranger): Initialize the global > >>>>bitmap obstack. > >>>>(disable_ranger): Release it. > >>>> --- > >>>> gcc/gimple-range.cc | 4 > >>>> 1 file changed, 4 insertions(+) > >>>> > >>>> diff --git a/gcc/gimple-range.cc b/gcc/gimple-range.cc > >>>> index c16b776c1e3..4d3b1ce8588 100644 > >>>> --- a/gcc/gimple-range.cc > >>>> +++ b/gcc/gimple-range.cc > >>>> @@ -689,6 +689,8 @@ enable_ranger (struct function *fun, bool > >>>> use_imm_uses) > >>>> { > >>>> gimple_ranger *r; > >>>> > >>>> + bitmap_obstack_initialize (NULL); > >>>> + > >>>> gcc_checking_assert (!fun->x_range_query); > >>>> r = new gimple_ranger (use_imm_uses); > >>>> fun->x_range_query = r; > >>>> @@ -705,6 +707,8 @@ disable_ranger (struct function *fun) > >>>> gcc_checking_assert (fun->x_range_query); > >>>> delete fun->x_range_query; > >>>> fun->x_range_query = NULL; > >>>> + > >>>> + bitmap_obstack_release (NULL); > >>> > >>> Are you not allowed to initialize/use obstacks unless > >>> bitmap_obstack_initialize(NULL) is called? > >> > >> You can use it with some other obstack, just not the default one. > >> > >>> If so, wouldn't it be > >>> better to lazily initialize it downstream (bitmap_alloc, or whomever > >>> needs it initialized)? > >> > >> No, you still need to decide where is the safe point to release it. > >> Unlike the non-default bitmap_obstack_initialize/bitmap_obstack_release, > >> the default one can nest (has associated nesting counter). So, the above > >> patch just says that ranger starts using the default obstack in > >> enable_ranger and stops using it in disable_ranger and anything ranger > >> associated in the obstack can be freed at that point. > > > > I thought ranger never used the default one: > > > > $ grep bitmap_obstack_initialize *value* *range* > > value-relation.cc: bitmap_obstack_initialize (&m_bitmaps); > > value-relation.cc: bitmap_obstack_initialize (&m_bitmaps); > > gimple-range-cache.cc: bitmap_obstack_initialize (&m_bitmaps); > > gimple-range-gori.cc: bitmap_obstack_initialize (&m_bitmaps); > > gimple-range-infer.cc: bitmap_obstack_initialize (&m_bitmaps); > > gimple-range-phi.cc: bitmap_obstack_initialize (&m_bitmaps); > > > > or even: > > > > $ grep obstack.*NULL *value* *range* > > value-range-storage.cc:obstack_free (&m_obstack, NULL); > > value-relation.cc: obstack_free (&m_chain_obstack, NULL); > > value-relation.cc: obstack_free (&m_chain_obstack, NULL); > > gimple-range-infer.cc: obstack_free (&m_list_obstack, NULL); > > value-range-storage.cc:obstack_free (&m_obstack, NULL); > > > > I'm obviously missing something here. > > Look for BITMAP_ALLOC (NULL) in the backtrace in the PR Ahh! Thanks. A few default obstack uses snuck in while I wasn't looking. $ grep BITMAP_ALLOC.*NULL *range* gimple-range-cache.cc: m_propfail = BITMAP_ALLOC (NULL); gimple-range-cache.h: inline ssa_lazy_cache () { active_p = BITMAP_ALLOC (NULL); } gimple-range.cc: m_pop_list = BITMAP_ALLOC (NULL); I wonder if it would be cleaner to just change these to use named obstacks. Andrew, is there a reason we were using the default obstack for these? For reference, they are class update_list used in the ranger cache, ssa_lazy_cache, and dom_ranger. Aldy
Re: [PATCH] middle-end/114604 - ranger allocates bitmap without initialized obstack
BTW, I'm not opposed to this patch. Thank you for tracking this down, and feel free to commit as is if y'all PMs agree it's OK. I just wanted to know if there's a better way going forward. I can certainly put it on my TODO list once stage1 opens again. And no, there probably isn't an obstack for those classes, but I wonder if we should have a class local one, as we do for the rest of the classes. Aldy On Mon, Apr 8, 2024 at 7:47 PM Richard Biener wrote: > > > > > Am 08.04.2024 um 18:40 schrieb Aldy Hernandez : > > > > On Mon, Apr 8, 2024 at 6:29 PM Richard Biener wrote: > >> > >> > >> > >>>> Am 08.04.2024 um 18:09 schrieb Aldy Hernandez : > >>> > >>> On Mon, Apr 8, 2024 at 5:54 PM Jakub Jelinek wrote: > >>>> > >>>> On Mon, Apr 08, 2024 at 05:40:23PM +0200, Aldy Hernandez wrote: > >>>>>> PR middle-end/114604 > >>>>>> * gimple-range.cc (enable_ranger): Initialize the global > >>>>>> bitmap obstack. > >>>>>> (disable_ranger): Release it. > >>>>>> --- > >>>>>> gcc/gimple-range.cc | 4 > >>>>>> 1 file changed, 4 insertions(+) > >>>>>> > >>>>>> diff --git a/gcc/gimple-range.cc b/gcc/gimple-range.cc > >>>>>> index c16b776c1e3..4d3b1ce8588 100644 > >>>>>> --- a/gcc/gimple-range.cc > >>>>>> +++ b/gcc/gimple-range.cc > >>>>>> @@ -689,6 +689,8 @@ enable_ranger (struct function *fun, bool > >>>>>> use_imm_uses) > >>>>>> { > >>>>>> gimple_ranger *r; > >>>>>> > >>>>>> + bitmap_obstack_initialize (NULL); > >>>>>> + > >>>>>> gcc_checking_assert (!fun->x_range_query); > >>>>>> r = new gimple_ranger (use_imm_uses); > >>>>>> fun->x_range_query = r; > >>>>>> @@ -705,6 +707,8 @@ disable_ranger (struct function *fun) > >>>>>> gcc_checking_assert (fun->x_range_query); > >>>>>> delete fun->x_range_query; > >>>>>> fun->x_range_query = NULL; > >>>>>> + > >>>>>> + bitmap_obstack_release (NULL); > >>>>> > >>>>> Are you not allowed to initialize/use obstacks unless > >>>>> bitmap_obstack_initialize(NULL) is called? > >>>> > >>>> You can use it with some other obstack, just not the default one. > >>>> > >>>>> If so, wouldn't it be > >>>>> better to lazily initialize it downstream (bitmap_alloc, or whomever > >>>>> needs it initialized)? > >>>> > >>>> No, you still need to decide where is the safe point to release it. > >>>> Unlike the non-default bitmap_obstack_initialize/bitmap_obstack_release, > >>>> the default one can nest (has associated nesting counter). So, the above > >>>> patch just says that ranger starts using the default obstack in > >>>> enable_ranger and stops using it in disable_ranger and anything ranger > >>>> associated in the obstack can be freed at that point. > >>> > >>> I thought ranger never used the default one: > >>> > >>> $ grep bitmap_obstack_initialize *value* *range* > >>> value-relation.cc: bitmap_obstack_initialize (&m_bitmaps); > >>> value-relation.cc: bitmap_obstack_initialize (&m_bitmaps); > >>> gimple-range-cache.cc: bitmap_obstack_initialize (&m_bitmaps); > >>> gimple-range-gori.cc: bitmap_obstack_initialize (&m_bitmaps); > >>> gimple-range-infer.cc: bitmap_obstack_initialize (&m_bitmaps); > >>> gimple-range-phi.cc: bitmap_obstack_initialize (&m_bitmaps); > >>> > >>> or even: > >>> > >>> $ grep obstack.*NULL *value* *range* > >>> value-range-storage.cc:obstack_free (&m_obstack, NULL); > >>> value-relation.cc: obstack_free (&m_chain_obstack, NULL); > >>> value-relation.cc: obstack_free (&m_chain_obstack, NULL); > >>> gimple-range-infer.cc: obstack_free (&m_list_obstack, NULL); > >>> value-range-storage.cc:obstack_free (&m_obstack, NULL); > >>> > >>> I'm obviously missing something here. > >> > >> Look for BITMAP_ALLOC (NULL) in the backtrace in the PR > > > > Ahh! Thanks. > > > > A few default obstack uses snuck in while I wasn't looking. > > > > $ grep BITMAP_ALLOC.*NULL *range* > > gimple-range-cache.cc: m_propfail = BITMAP_ALLOC (NULL); > > gimple-range-cache.h: inline ssa_lazy_cache () { active_p = > > BITMAP_ALLOC (NULL); } > > gimple-range.cc: m_pop_list = BITMAP_ALLOC (NULL); > > > > I wonder if it would be cleaner to just change these to use named obstacks. > > I didn’t find any obvious obstack to use, but sure. This was the easiest fix > ;) > > Richard > > > Andrew, is there a reason we were using the default obstack for these? > > For reference, they are class update_list used in the ranger cache, > > ssa_lazy_cache, and dom_ranger. > > > > Aldy > > >
[PATCH] ranger: Grow BBs in relation oracle as needed [PR113735]
The relation oracle grows the internal vector of SSAs as needed, but due to an oversight was not growing the basic block vector. This fixes the oversight. OK for trunk? PR tree-optimization/113735 gcc/testsuite/ChangeLog: * gcc.dg/tree-ssa/pr113735.c: New test. gcc/ChangeLog: * value-relation.cc (equiv_oracle::add_equiv_to_block): Call limit_check(). --- gcc/testsuite/gcc.dg/tree-ssa/pr113735.c | 19 +++ gcc/value-relation.cc| 1 + 2 files changed, 20 insertions(+) create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr113735.c diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr113735.c b/gcc/testsuite/gcc.dg/tree-ssa/pr113735.c new file mode 100644 index 000..7b864999277 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr113735.c @@ -0,0 +1,19 @@ +// { dg-do compile { target bitint } } +// { dg-options "-O1" } + +char b; +void bar (void); + +#if __BITINT_MAXWIDTH__ >= 6110 +void +foo (_BitInt(6110) j) +{ + for (;;) +{ + _BitInt(10) k = b % j; + for (j = 6; j; --j) +if (k) + bar (); +} +} +#endif diff --git a/gcc/value-relation.cc b/gcc/value-relation.cc index 27f9ad61c0e..619ee5f0867 100644 --- a/gcc/value-relation.cc +++ b/gcc/value-relation.cc @@ -718,6 +718,7 @@ equiv_oracle::add_equiv_to_block (basic_block bb, bitmap equiv_set) // Check if this is the first time a block has an equivalence added. // and create a header block. And set the summary for this block. + limit_check (bb); if (!m_equiv[bb->index]) { ptr = (equiv_chain *) obstack_alloc (&m_chain_obstack, -- 2.43.0
Re: [PATCH] range-op: Fix up ABSU_EXPR handling [PR113756]
LGTM. Up to the release managers. Thanks for tracking this down. Aldy On Tue, Feb 6, 2024 at 9:43 PM Jakub Jelinek wrote: > > Hi! > > ABSU_EXPR unary expr is special because it has a signed integer > argument and unsigned integer result (of the same precision). > > The following testcase is miscompiled since ABSU_EXPR handling has > been added to range-op because it uses widest_int::from with the > result sign (i.e. UNSIGNED) rather than the operand sign (i.e. SIGNED), > so e.g. for the 32-bit int argument mask ends up 0xffc1 or something > similar and even when it has most significant bit in the precision set, > in widest_int (tree-ssa-ccp.cc really should stop using widest_int, but > that is I think stage1 task) it doesn't appear to be negative and so > bit_value_unop ABSU_EXPR doesn't set the resulting mask/value from > oring of the argument and its negation. > > Fixed thusly, not doing that for GIMPLE_BINARY_RHS because I don't know > about a binary op that would need something similar. > > Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? > > 2024-02-06 Jakub Jelinek > > PR tree-optimization/113756 > * range-op.cc (update_known_bitmask): For GIMPLE_UNARY_RHS, > use TYPE_SIGN (lh.type ()) instead of sign for widest_int::from > of lh_bits value and mask. > > * gcc.dg/pr113756.c: New test. > > --- gcc/range-op.cc.jj 2024-01-03 11:51:28.199777434 +0100 > +++ gcc/range-op.cc 2024-02-06 16:51:55.549127825 +0100 > @@ -435,8 +435,10 @@ update_known_bitmask (irange &r, tree_co >bit_value_unop (code, sign, prec, &widest_value, &widest_mask, > TYPE_SIGN (lh.type ()), > TYPE_PRECISION (lh.type ()), > - widest_int::from (lh_bits.value (), sign), > - widest_int::from (lh_bits.mask (), sign)); > + widest_int::from (lh_bits.value (), > + TYPE_SIGN (lh.type ())), > + widest_int::from (lh_bits.mask (), > + TYPE_SIGN (lh.type (; >break; > case GIMPLE_BINARY_RHS: >bit_value_binop (code, sign, prec, &widest_value, &widest_mask, > --- gcc/testsuite/gcc.dg/pr113756.c.jj 2024-02-06 17:00:52.835679796 +0100 > +++ gcc/testsuite/gcc.dg/pr113756.c 2024-02-06 17:00:31.159980326 +0100 > @@ -0,0 +1,36 @@ > +/* PR tree-optimization/113756 */ > +/* { dg-do run { target int32plus } } */ > +/* { dg-options "-O2" } */ > + > +int d, e, i, k, l = -8; > +signed char h, j; > + > +int > +bar (int n, int o, int p3) > +{ > + int a = o - p3, b = n - p3, c = a + b, f = -b, g = c < 0 ? -c : c; > + return a <= f && a <= g ? o : p3; > +} > + > +void > +foo (int *n, unsigned short o) > +{ > + unsigned p = 8896; > + for (; e >= 0; e--) > +p = 5377; > + for (; h <= 0; h++) > +for (; j <= 0; j++) > + { > + *n = 1611581749; > + i = bar (34, p - 5294, *n - 1611581687); > + k = i + p + 65535 + o + *n - 1611718251; > + if (k != 0) > + __builtin_abort (); > + } > +} > + > +int > +main () > +{ > + foo (&l, l); > +} > > Jakub >
[COMMITTED] [frange] Add op2_range for operator_not_equal.
We're missing an op2_range entry for operator_not_equal so GORI can calculate an outgoing edge. The false side of != is true and guarantees we don't have a NAN, so it's important to get this right. We eventually get it through an intersection of various ranges in ranger, but it's best to get things correct as early as possible. gcc/ChangeLog: * range-op-float.cc (operator_not_equal::op2_range): New. * range-op-mixed.h: Add operator_not_equal::op2_range. gcc/testsuite/ChangeLog: * gcc.dg/tree-ssa/vrp-float-13.c: New test. --- gcc/range-op-float.cc| 8 gcc/range-op-mixed.h | 3 +++ gcc/testsuite/gcc.dg/tree-ssa/vrp-float-13.c | 16 3 files changed, 27 insertions(+) create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/vrp-float-13.c diff --git a/gcc/range-op-float.cc b/gcc/range-op-float.cc index cc729e12a9e..91d3096fdac 100644 --- a/gcc/range-op-float.cc +++ b/gcc/range-op-float.cc @@ -900,6 +900,14 @@ operator_not_equal::op1_range (frange &r, tree type, return true; } +bool +operator_not_equal::op2_range (frange &r, tree type, + const irange &lhs, + const frange &op1, + relation_trio trio) const +{ + return op1_range (r, type, lhs, op1, trio); +} // Check if the LHS range indicates a relation between OP1 and OP2. diff --git a/gcc/range-op-mixed.h b/gcc/range-op-mixed.h index ef562326c1f..f7ff47b2725 100644 --- a/gcc/range-op-mixed.h +++ b/gcc/range-op-mixed.h @@ -164,6 +164,9 @@ public: bool op2_range (irange &r, tree type, const irange &lhs, const irange &op1, relation_trio = TRIO_VARYING) const final override; + bool op2_range (frange &r, tree type, + const irange &lhs, const frange &op1, + relation_trio = TRIO_VARYING) const final override; relation_kind op1_op2_relation (const irange &lhs, const irange &, const irange &) const final override; diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-13.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-13.c new file mode 100644 index 000..f5a0164dd91 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-13.c @@ -0,0 +1,16 @@ +// { dg-do compile } +// { dg-options "-O2 -fno-thread-jumps -fdisable-tree-fre1 -fdump-tree-evrp-details" } + +void a(float, float); +void b(float, float); + +void foo(float x, float y) +{ + if (x != y) +a (x,y); + else if (x < y) +b (x,y); +} + +// Test that the false side of if(x != y) has a range for y. +// { dg-final { scan-tree-dump "2->4 \\(F\\) y_3\\(D\\)" "evrp" } } -- 2.41.0
gcc-patches@gcc.gnu.org
We can set_nan() with a nan_state so it's good form to have the analogous form for update_nan(). gcc/ChangeLog: * value-range.h (frange::update_nan): New. --- gcc/value-range.h | 28 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/gcc/value-range.h b/gcc/value-range.h index da04be00ab4..a792c593faa 100644 --- a/gcc/value-range.h +++ b/gcc/value-range.h @@ -1257,36 +1257,40 @@ frange::set_undefined () verify_range (); } -// Set the NAN bit and adjust the range. +// Set the NAN bits to NAN and adjust the range. inline void -frange::update_nan () +frange::update_nan (const nan_state &nan) { gcc_checking_assert (!undefined_p ()); if (HONOR_NANS (m_type)) { - m_pos_nan = true; - m_neg_nan = true; + m_pos_nan = nan.pos_p (); + m_neg_nan = nan.neg_p (); normalize_kind (); if (flag_checking) verify_range (); } } +// Set the NAN bit to +-NAN. + +inline void +frange::update_nan () +{ + gcc_checking_assert (!undefined_p ()); + nan_state nan (true); + update_nan (nan); +} + // Like above, but set the sign of the NAN. inline void frange::update_nan (bool sign) { gcc_checking_assert (!undefined_p ()); - if (HONOR_NANS (m_type)) -{ - m_pos_nan = !sign; - m_neg_nan = sign; - normalize_kind (); - if (flag_checking) - verify_range (); -} + nan_state nan (/*pos=*/!sign, /*neg=*/sign); + update_nan (nan); } inline bool -- 2.41.0
[COMMITTED] [frange] Remove redundant known_isnan() checks.
The known_isnan() method is a subset of maybe_isnan(). This patch removes redundant calls to known_isnan(). gcc/ChangeLog: * range-op-float.cc (operator_lt::op1_range): Remove known_isnan check. (operator_lt::op2_range): Same. (operator_le::op1_range): Same. (operator_le::op2_range): Same. (operator_gt::op1_range): Same. (operator_gt::op2_range): Same. (operator_ge::op1_range): Same. (operator_ge::op2_range): Same. (foperator_unordered_lt::op1_range): Same. (foperator_unordered_lt::op2_range): Same. (foperator_unordered_le::op1_range): Same. (foperator_unordered_le::op2_range): Same. (foperator_unordered_gt::op1_range): Same. (foperator_unordered_gt::op2_range): Same. (foperator_unordered_ge::op1_range): Same. (foperator_unordered_ge::op2_range): Same. --- gcc/range-op-float.cc | 32 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/gcc/range-op-float.cc b/gcc/range-op-float.cc index 91d3096fdac..5eb1d9c06e3 100644 --- a/gcc/range-op-float.cc +++ b/gcc/range-op-float.cc @@ -981,7 +981,7 @@ operator_lt::op1_range (frange &r, case BRS_FALSE: // On the FALSE side of x < NAN, we know nothing about x. - if (op2.known_isnan () || op2.maybe_isnan ()) + if (op2.maybe_isnan ()) r.set_varying (type); else build_ge (r, type, op2); @@ -1018,7 +1018,7 @@ operator_lt::op2_range (frange &r, case BRS_FALSE: // On the FALSE side of NAN < x, we know nothing about x. - if (op1.known_isnan () || op1.maybe_isnan ()) + if (op1.maybe_isnan ()) r.set_varying (type); else build_le (r, type, op1); @@ -1091,7 +1091,7 @@ operator_le::op1_range (frange &r, case BRS_FALSE: // On the FALSE side of x <= NAN, we know nothing about x. - if (op2.known_isnan () || op2.maybe_isnan ()) + if (op2.maybe_isnan ()) r.set_varying (type); else build_gt (r, type, op2); @@ -1124,7 +1124,7 @@ operator_le::op2_range (frange &r, case BRS_FALSE: // On the FALSE side of NAN <= x, we know nothing about x. - if (op1.known_isnan () || op1.maybe_isnan ()) + if (op1.maybe_isnan ()) r.set_varying (type); else if (op1.undefined_p ()) return false; @@ -1210,7 +1210,7 @@ operator_gt::op1_range (frange &r, case BRS_FALSE: // On the FALSE side of x > NAN, we know nothing about x. - if (op2.known_isnan () || op2.maybe_isnan ()) + if (op2.maybe_isnan ()) r.set_varying (type); else if (op2.undefined_p ()) return false; @@ -1249,7 +1249,7 @@ operator_gt::op2_range (frange &r, case BRS_FALSE: // On The FALSE side of NAN > x, we know nothing about x. - if (op1.known_isnan () || op1.maybe_isnan ()) + if (op1.maybe_isnan ()) r.set_varying (type); else if (op1.undefined_p ()) return false; @@ -1323,7 +1323,7 @@ operator_ge::op1_range (frange &r, case BRS_FALSE: // On the FALSE side of x >= NAN, we know nothing about x. - if (op2.known_isnan () || op2.maybe_isnan ()) + if (op2.maybe_isnan ()) r.set_varying (type); else if (op2.undefined_p ()) return false; @@ -1357,7 +1357,7 @@ operator_ge::op2_range (frange &r, tree type, case BRS_FALSE: // On the FALSE side of NAN >= x, we know nothing about x. - if (op1.known_isnan () || op1.maybe_isnan ()) + if (op1.maybe_isnan ()) r.set_varying (type); else if (op1.undefined_p ()) return false; @@ -1720,7 +1720,7 @@ foperator_unordered_lt::op1_range (frange &r, tree type, switch (get_bool_state (r, lhs, type)) { case BRS_TRUE: - if (op2.known_isnan () || op2.maybe_isnan ()) + if (op2.maybe_isnan ()) r.set_varying (type); else if (op2.undefined_p ()) return false; @@ -1754,7 +1754,7 @@ foperator_unordered_lt::op2_range (frange &r, tree type, switch (get_bool_state (r, lhs, type)) { case BRS_TRUE: - if (op1.known_isnan () || op1.maybe_isnan ()) + if (op1.maybe_isnan ()) r.set_varying (type); else if (op1.undefined_p ()) return false; @@ -1832,7 +1832,7 @@ foperator_unordered_le::op1_range (frange &r, tree type, switch (get_bool_state (r, lhs, type)) { case BRS_TRUE: - if (op2.known_isnan () || op2.maybe_isnan ()) + if (op2.maybe_isnan ()) r.set_varying (type); else if (op2.undefined_p ()) return false; @@ -1865,7 +1865,7 @@ foperator_unordered_le::op2_range (frange &r, switch (get_bool_state (r, lhs, type)) { case BRS_TRUE: - if (op1.known_isnan () || op1.maybe_isnan ()) + if (op1.maybe_isnan ()) r.set_varying (type); else if (op1.undefined_p ()) return false; @@ -1945,7 +1945,7 @@ foperator_unordered_gt::op1_range
Re: [PATCH] [frange] Relax floating point relational folding.
Hi Jakub. I wasn't ignoring you, just quietly thinking, making sure we weren't missing anything. In the process I cleaned everything, which hopefully makes it easier to see why we don't need relationals (the key is to look at frelop_early_resolve() and the op1/op2_range entries which clear the NAN bits). I am committing the patch below, and as I say in it: I don't mean this patch as a hard-no against implementing the unordered relations Jakub preferred, but seeing that it's looking cleaner and trivially simple without the added burden of more enums, I'd like to flesh it out completely and then discuss if we still think new codes are needed. At least now we have tests :). Please let me know if you have any test cases you think we may be missing. FYI, I'm still not done with the unordered folders. Tested on x86-64 Linux. Committed. Aldy On Mon, Aug 28, 2023 at 3:01 AM Jakub Jelinek wrote: > > On Wed, Aug 23, 2023 at 05:22:00PM +0200, Aldy Hernandez via Gcc-patches > wrote: > > BTW, we batted some ideas on how to get this work, and it seems this > > is the cleaner route with the special cases nestled in the operators > > themselves. Another idea is to add unordered relations, but that > > would require bloating the various tables adding spots for VREL_UNEQ, > > VREL_UNLT, etc, plus adding relations for VREL_UNORDERED so the > > intersects work correctly. I'm not wed to either one, and we can > > certainly revisit this if it becomes burdensome to maintain (or to get > > right). > > My strong preference would be to have the full set of operations, > i.e. VREL_LTGT, VREL_{,UN}ORDERED, VREL_UN{LT,LE,GT,GE,EQ}, then everything > will fall out of this cleanly, not just some common special cases, but > also unions of them, intersections etc. > The only important question is if you want to differentiate VREL_* > for floating point comparisions with possible NANs vs. other comparisons > in the callers, then one needs effectively e.g. 2 sets of rr_* tables > in value-relation.cc and what exactly say VREL_EQ inverts to etc. is then > dependent on the context (this is what we do at the tree level normally, > e.g. fold-const.cc (invert_tree_comparison) has honor_nans argument), > or whether it would be a completely new set of value relations, so > even for EQ/NE etc. one would use VRELF_ or something similar. > > Jakub > From 220a58d9abbb1f403e8f79cd42ad01b7c9b10ae9 Mon Sep 17 00:00:00 2001 From: Aldy Hernandez Date: Mon, 18 Sep 2023 21:41:08 -0400 Subject: [PATCH] [frange] Clean up floating point relational folding. The following patch removes all the special casing from the floating point relational folding code. Now all the code relating to folding of relationals is in frelop_early_resolve() and in operator_not_equal::fold_range() which requires a small tweak. I have written new relational tests, and moved them to gcc.dg/tree-ssa/vrp-float-relations-* for easy reference. In the tests it's easy to see the type of things we need to handle: (a) if (x != y) if (x == y) link_error (); (b) if (a != b) if (a != b) // Foldable as true. (c) /* We can thread BB2->BB4->BB5 even though we have no knowledge of the NANness of either x_1 or a_5. */ __BB(4): x_1 = __PHI (__BB2: a_5(D), __BB3: b_4(D)); if (x_1 __UNEQ a_5(D)) (d) /* Even though x_1 and a_4 are equivalent on the BB2->BB4 path, we cannot fold the conditional because of possible NANs: */ __BB(4): # x_1 = __PHI (__BB2: a_4(D), __BB3: 8.0e+0(3)); if (x_1 == a_4(D)) (e) if (cond) x = a; else x = 8.0; /* We can fold this as false on the path coming out of cond==1, regardless of NANs on either "x" or "a". */ if (x < a) stuff (); [etc, etc] We can implement everything without either special casing, get_identity_relation(), or adding new unordered relationals. The basic idea is that if we accurately reflect NANs in op[12]_range, this information gets propagated to the relevant edges, and there's no need for unordered relations (VREL_UN*), because the information is in the range itself. This information is then used in frelop_early_resolve() to fold certain combinations. I don't mean this patch as a hard-no against implementing the unordered relations Jakub preferred, but seeing that it's looking cleaner and trivially simple without the added burden of more enums, I'd like to flesh it out completely and then discuss if we still think new codes are needed. More testcases or corner cases are highly welcome. In follow-up patches I will finish up unordered relation folding, and come up with suitable tests. gcc/ChangeLog: * range-op-float.cc (frelop_early_resolve): Clean-up and remove special casing. (operator_not_equal::fold_range): Handle VREL_EQ. (operator_lt
[PATCH] [frange] Remove special casing from unordered operators.
In coming up with testcases for the unordered folders, I realized that we were already handling them correctly, even in the absence of my work in this area lately. All of the unordered fold_range() methods try to fold with the ordered variants first, and if they return TRUE, we are guaranteed to be able to fold, even in the presence of NANs. For example: if (x_5 >= y_8) if (x_5 __UNLE y_8) On the true side of the first conditional we know that either x_5 < y_8 or that one or more operands is a NAN. Since UNLE_EXPR returns true for precisely this scenario, we can fold as true. This is handled in the fold_range() methods as follows: if (!range_op_handler (LE_EXPR).fold_range (r, type, op1_no_nan, op2_no_nan, trio)) return false; // The result is the same as the ordered version when the // comparison is true or when the operands cannot be NANs. if (!maybe_isnan (op1, op2) || r == range_true (type)) return true; This code has been there since the last release, and makes the special casing I am deleting obsolete. I have added tests to make sure we keep track of this behavior. I will commit this pending tests. gcc/ChangeLog: * range-op-float.cc (foperator_unordered_ge::fold_range): Remove special casing. (foperator_unordered_gt::fold_range): Same. (foperator_unordered_lt::fold_range): Same. (foperator_unordered_le::fold_range): Same. gcc/testsuite/ChangeLog: * gcc.dg/tree-ssa/vrp-float-relations-5.c: New test. * gcc.dg/tree-ssa/vrp-float-relations-6.c: New test. --- gcc/range-op-float.cc | 20 ++- .../gcc.dg/tree-ssa/vrp-float-relations-5.c | 54 +++ .../gcc.dg/tree-ssa/vrp-float-relations-6.c | 54 +++ 3 files changed, 112 insertions(+), 16 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/vrp-float-relations-5.c create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/vrp-float-relations-6.c diff --git a/gcc/range-op-float.cc b/gcc/range-op-float.cc index 399deee5d8a..0951bd385a9 100644 --- a/gcc/range-op-float.cc +++ b/gcc/range-op-float.cc @@ -1644,10 +1644,7 @@ public: const frange &op1, const frange &op2, relation_trio trio = TRIO_VARYING) const final override { -relation_kind rel = trio.op1_op2 (); - -if (op1.known_isnan () || op2.known_isnan () - || rel == VREL_LT) +if (op1.known_isnan () || op2.known_isnan ()) { r = range_true (type); return true; @@ -1759,10 +1756,7 @@ public: const frange &op1, const frange &op2, relation_trio trio = TRIO_VARYING) const final override { -relation_kind rel = trio.op1_op2 (); - -if (op1.known_isnan () || op2.known_isnan () - || rel == VREL_LE) +if (op1.known_isnan () || op2.known_isnan ()) { r = range_true (type); return true; @@ -1870,10 +1864,7 @@ public: const frange &op1, const frange &op2, relation_trio trio = TRIO_VARYING) const final override { -relation_kind rel = trio.op1_op2 (); - -if (op1.known_isnan () || op2.known_isnan () - || rel == VREL_GT) +if (op1.known_isnan () || op2.known_isnan ()) { r = range_true (type); return true; @@ -1985,10 +1976,7 @@ public: const frange &op1, const frange &op2, relation_trio trio = TRIO_VARYING) const final override { -relation_kind rel = trio.op1_op2 (); - -if (op1.known_isnan () || op2.known_isnan () - || rel == VREL_GE) +if (op1.known_isnan () || op2.known_isnan ()) { r = range_true (type); return true; diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-relations-5.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-relations-5.c new file mode 100644 index 000..2bd06c6fbf7 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-relations-5.c @@ -0,0 +1,54 @@ +// { dg-do compile } +// { dg-options "-O2 -fgimple -fdump-tree-evrp" } + +void link_error(); + +void __GIMPLE (ssa,startwith("evrp")) +foo1 (float x, float y) +{ + __BB(2): + if (x_4(D) <= y_5(D)) +goto __BB5; + else +goto __BB3; + + __BB(3): + // Relation at this point is VREL_GT. + if (x_4(D) __UNGE y_5(D)) +goto __BB5; + else +goto __BB4; + + __BB(4): + link_error (); + goto __BB5; + + __BB(5): + return; +} + +void __GIMPLE (ssa,startwith("evrp")) +foo2 (float x, float y) +{ + __BB(2): + if (x_4(D) <= y_5(D)) +goto __BB5; + else +goto __BB3; + + __BB(3): + // Relation at this point is VREL_GT. + if (x_4(D) __UNGT y_5(D)) +goto __BB5; + else +goto __BB4; + + __BB(4): + link_error (); + goto __BB5; + + __BB(5): + return; +} + +// { dg-final { scan-tree-dump-not "link_error" "evrp" } } diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-relations-6.c b/gcc/testsui
Re: [PATCH] [frange] Remove special casing from unordered operators.
On 9/20/23 11:12, Aldy Hernandez wrote: In coming up with testcases for the unordered folders, I realized that we were already handling them correctly, even in the absence of my work in this area lately. All of the unordered fold_range() methods try to fold with the ordered variants first, and if they return TRUE, we are guaranteed to be able to fold, even in the presence of NANs. For example: if (x_5 >= y_8) if (x_5 __UNLE y_8) On the true side of the first conditional we know that either x_5 < y_8 or that one or more operands is a NAN. Since UNLE_EXPR returns true for precisely this scenario, we can fold as true. Ugh, that should've been the false edge of the first conditional, thus: if (x_5 >= y_8) { } else { // Relation at this point is: x_5 < y_8 // or either x_5 or y_8 is a NAN. if (x_5 __UNLE y_8) link_error(); } The second conditional is foldable because LT U NAN is a subset of __UNLE (which is LE U NAN). The patch still stands though :). Aldy
[PATCH] [testsuite] Remove undefined behavior from gcc.dg/tree-ssa/pr44306.c
In auditing the DOM code to see what the scoped tables catch that ranger doesn't, I've run accross this test, which seems to have uninitialized reads from both j and present[]. >From the original PR, it looks like this came from a reduction of a failing test in SPEC's 464.h264ref. A google search of the CalculateQuant8Param() in the test yields: https://github.com/microsoft/test-suite/blob/master/MultiSource/Applications/JM/lencod/q_matrix.c Assuming the above source is similar to the original testcase, it looks like both "j" and "present" were initialized before use, so our testcase just got reduced a bit too far. I tried to build the offending commit to see if my adjustments to the test still caused it to fail: commit e1449456c0a88f5b3122db5452f7e91f5a9535f6 (HEAD -> master) Author: Sebastian Pop Date: Wed May 26 16:46:59 2010 + Reorganize the analysis of basic block predication. ...but alas it no longer builds with a recent compiler. Perhaps someone has a ./cc1 of that revision around to verify? OK? gcc/testsuite/ChangeLog: * gcc.dg/tree-ssa/pr44306.c: Remove undefined behavior. --- gcc/testsuite/gcc.dg/tree-ssa/pr44306.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr44306.c b/gcc/testsuite/gcc.dg/tree-ssa/pr44306.c index 1ea04ce3a98..d322fe048b5 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/pr44306.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr44306.c @@ -8,10 +8,10 @@ int LevelScale8x8Luma_Inter[6][8][8]; int InvLevelScale8x8Luma_Intra[6][8][8]; int InvLevelScale8x8Luma_Inter[6][8][8]; short UseDefaultScalingMatrix8x8Flag[2]; -void CalculateQuant8Param() +int present[2]; +void CalculateQuant8Param(int j) { - int i, j, k, temp; - int present[2]; + int i, k, temp; for(k=0; j<8; j++) for(i=0; i<8; i++) { -- 2.41.0
[PATCH] Remove unused hybrid_* operators in range-ops.
Now that the dust has settled on the prange work, we can remove the hybrid operators. I will push this once tests complete. gcc/ChangeLog: * range-op-ptr.cc (class hybrid_and_operator): Remove. (class hybrid_or_operator): Same. (class hybrid_min_operator): Same. (class hybrid_max_operator): Same. --- gcc/range-op-ptr.cc | 156 1 file changed, 156 deletions(-) diff --git a/gcc/range-op-ptr.cc b/gcc/range-op-ptr.cc index 9421d3cd21d..1f41236e710 100644 --- a/gcc/range-op-ptr.cc +++ b/gcc/range-op-ptr.cc @@ -612,162 +612,6 @@ operator_pointer_diff::op1_op2_relation_effect (irange &lhs_range, tree type, rel); } -// -- -// Hybrid operators for the 4 operations which integer and pointers share, -// but which have different implementations. Simply check the type in -// the call and choose the appropriate method. -// Once there is a PRANGE signature, simply add the appropriate -// prototypes in the rmixed range class, and remove these hybrid classes. - -class hybrid_and_operator : public operator_bitwise_and -{ -public: - using range_operator::update_bitmask; - using range_operator::op1_range; - using range_operator::op2_range; - using range_operator::lhs_op1_relation; - bool op1_range (irange &r, tree type, - const irange &lhs, const irange &op2, - relation_trio rel = TRIO_VARYING) const final override -{ - if (INTEGRAL_TYPE_P (type)) - return operator_bitwise_and::op1_range (r, type, lhs, op2, rel); - else - return false; -} - bool op2_range (irange &r, tree type, - const irange &lhs, const irange &op1, - relation_trio rel = TRIO_VARYING) const final override -{ - if (INTEGRAL_TYPE_P (type)) - return operator_bitwise_and::op2_range (r, type, lhs, op1, rel); - else - return false; -} - relation_kind lhs_op1_relation (const irange &lhs, - const irange &op1, const irange &op2, - relation_kind rel) const final override -{ - if (!lhs.undefined_p () && INTEGRAL_TYPE_P (lhs.type ())) - return operator_bitwise_and::lhs_op1_relation (lhs, op1, op2, rel); - else - return VREL_VARYING; -} - void update_bitmask (irange &r, const irange &lh, - const irange &rh) const final override -{ - if (!r.undefined_p () && INTEGRAL_TYPE_P (r.type ())) - operator_bitwise_and::update_bitmask (r, lh, rh); -} - - void 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 final override -{ - if (INTEGRAL_TYPE_P (type)) - return operator_bitwise_and::wi_fold (r, type, lh_lb, lh_ub, - rh_lb, rh_ub); - else - return op_pointer_and.wi_fold (r, type, lh_lb, lh_ub, rh_lb, rh_ub); -} -} op_hybrid_and; - -// Temporary class which dispatches routines to either the INT version or -// the pointer version depending on the type. Once PRANGE is a range -// class, we can remove the hybrid. - -class hybrid_or_operator : public operator_bitwise_or -{ -public: - using range_operator::update_bitmask; - using range_operator::op1_range; - using range_operator::op2_range; - using range_operator::lhs_op1_relation; - bool op1_range (irange &r, tree type, - const irange &lhs, const irange &op2, - relation_trio rel = TRIO_VARYING) const final override -{ - if (INTEGRAL_TYPE_P (type)) - return operator_bitwise_or::op1_range (r, type, lhs, op2, rel); - else - return op_pointer_or.op1_range (r, type, lhs, op2, rel); -} - bool op2_range (irange &r, tree type, - const irange &lhs, const irange &op1, - relation_trio rel = TRIO_VARYING) const final override -{ - if (INTEGRAL_TYPE_P (type)) - return operator_bitwise_or::op2_range (r, type, lhs, op1, rel); - else - return op_pointer_or.op2_range (r, type, lhs, op1, rel); -} - void update_bitmask (irange &r, const irange &lh, - const irange &rh) const final override -{ - if (!r.undefined_p () && INTEGRAL_TYPE_P (r.type ())) - operator_bitwise_or::update_bitmask (r, lh, rh); -} - - void 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 final override -{ - if (INTEGRAL_TYPE_P (type)) - return operator_bitwise_or::wi_fold (r, type, lh_lb, lh_ub, - rh_lb, rh_ub); - else - return op_pointer_or.wi_fold (r, type, lh_lb, lh_ub, rh_lb, rh_ub); -} -} op_h
[COMMITTED] [prange] Default pointers_handled_p() to false.
The pointers_handled_p() method is an internal range-op helper to help catch dispatch type mismatches for pointer operands. This is what caught the IPA mismatch in PR114985. This method is only a temporary measure to catch any incompatibilities in the current pointer range-op entries. This patch returns true for any *new* entries in the range-op table, as the current ones are already fleshed out. This keeps us from having to implement this boilerplate function for any new range-op entries. PR tree-optimization/114995 * range-op-ptr.cc (range_operator::pointers_handled_p): Default to true. --- gcc/range-op-ptr.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gcc/range-op-ptr.cc b/gcc/range-op-ptr.cc index 65cca65103a..2f47f3354ed 100644 --- a/gcc/range-op-ptr.cc +++ b/gcc/range-op-ptr.cc @@ -58,7 +58,7 @@ bool range_operator::pointers_handled_p (range_op_dispatch_type ATTRIBUTE_UNUSED, unsigned dispatch ATTRIBUTE_UNUSED) const { - return false; + return true; } bool -- 2.45.0
Re: [PATCH] Adjust range type of calls into fold_range for IPA passes [PR114985]
Any thoughts on this? If no one objects, I'll re-enable prange tomorrow. Aldy On Sat, May 11, 2024 at 11:43 AM Aldy Hernandez wrote: > > I have pushed a few cleanups to make it easier to move forward without > disturbing passes which are affected by IPA's mixing up the range > types. As I explained in my previous patch, this restores the default > behavior of silently returning VARYING when a range operator is > unsupported in either a particular operator, or in the dispatch code. > > I would like to re-enable prange support, as IPA was already broken > before the prange work, and the debugging trap can be turned off to > analyze (#define TRAP_ON_UNHANDLED_POINTER_OPERATORS 1). > > I have re-tested the effects of re-enabling prange in current trunk: > > 1. x86-64/32 bootstraps with no regressions with and without the trap. > 2. ppc64le bootstraps with no regressions, but fails with the trap. > 3. aarch64 bootstraps, but fails with the trap (no space on compile > farm to run tests) > 4. sparc: bootstrap already broken, so I can't test. > > So, for the above 4 architectures things work as before, and we have a > PR to track the IPA problem which doesn't seem to affect neither > bootstrap nor tests. > > Does this sound reasonable? > > Aldy > > On Fri, May 10, 2024 at 12:26 PM Richard Biener > wrote: > > > > On Fri, May 10, 2024 at 11:24 AM Aldy Hernandez wrote: > > > > > > There are various calls into fold_range() that have the wrong type > > > associated with the range temporary used to hold the result. This > > > used to work, because we could store either integers or pointers in a > > > Value_Range, but is no longer the case with prange's. Now you must > > > explicitly state which type of range the temporary will hold before > > > storing into it. You can change this at a later time with set_type(), > > > but you must always have a type before using the temporary, and it > > > must match what fold_range() returns. > > > > > > This patch adjusts the IPA code to restore the previous functionality, > > > so I can re-enable the prange code, but I do question whether the > > > previous code was correct. I have added appropriate comments to help > > > the maintainers, but someone with more knowledge should revamp this > > > going forward. > > > > > > The basic problem is that pointer comparisons return a boolean, but > > > the IPA code is initializing the resulting range as a pointer. This > > > wasn't a problem, because fold_range() would previously happily force > > > the range into an integer one, and everything would work. But now we > > > must initialize the range to an integer before calling into > > > fold_range. The thing is, that the failing case sets the result back > > > into a pointer, which is just weird but existing behavior. I have > > > documented this in the code. > > > > > > if (!handler > > > || !op_res.supports_type_p (vr_type) > > > || !handler.fold_range (op_res, vr_type, srcvr, op_vr)) > > > /* For comparison operators, the type here may be > > >different than the range type used in fold_range above. > > >For example, vr_type may be a pointer, whereas the type > > >returned by fold_range will always be a boolean. > > > > > >This shouldn't cause any problems, as the set_varying > > >below will happily change the type of the range in > > >op_res, and then the cast operation in > > >ipa_vr_operation_and_type_effects will ultimately leave > > >things in the desired type, but it is confusing. > > > > > >Perhaps the original intent was to use the type of > > >op_res here? */ > > > op_res.set_varying (vr_type); > > > > > > BTW, this is not to say that the original gimple IR was wrong, but that > > > IPA is setting the range type of the result of fold_range() to the type of > > > the operands, which does not necessarily match in the case of a > > > comparison. > > > > > > I am just restoring previous behavior here, but I do question whether it > > > was right to begin with. > > > > > > Testing currently in progress on x86-64 and ppc64le with prange enabled. > > > > > > OK pending tests? > > > > I think this "intermediate"
[COMMITTED] Cleanup prange sanity checks.
The pointers_handled_p() code was a temporary sanity check, and not even a good one, since we have a cleaner way of checking type mismatches with operand_check_p. This patch removes all the code, and adds an explicit type check for relational operators, which are the main problem in PR114985. Adding this check makes it clear where the type mismatch is happening in IPA, even without prange. I've added code to skip the range folding if the types don't match what the operator expects. In order to reproduce the latent bug, just remove the operand_check_p calls. Tested on x86-64 and ppc64le with and without prange support. gcc/ChangeLog: PR tree-optimization/114985 * gimple-range-op.cc: Remove pointers_handled_p. * ipa-cp.cc (ipa_value_range_from_jfunc): Skip range folding if operands don't match. (propagate_vr_across_jump_function): Same. * range-op-mixed.h: Remove pointers_handled_p and tweak operand_check_p. * range-op-ptr.cc (range_operator::pointers_handled_p): Remove. (pointer_plus_operator::pointers_handled_p): Remove. (class operator_pointer_diff): Remove pointers_handled_p. (operator_pointer_diff::pointers_handled_p): Remove. (operator_identity::pointers_handled_p): Remove. (operator_cst::pointers_handled_p): Remove. (operator_cast::pointers_handled_p): Remove. (operator_min::pointers_handled_p): Remove. (operator_max::pointers_handled_p): Remove. (operator_addr_expr::pointers_handled_p): Remove. (operator_bitwise_and::pointers_handled_p): Remove. (operator_bitwise_or::pointers_handled_p): Remove. (operator_equal::pointers_handled_p): Remove. (operator_not_equal::pointers_handled_p): Remove. (operator_lt::pointers_handled_p): Remove. (operator_le::pointers_handled_p): Remove. (operator_gt::pointers_handled_p): Remove. (operator_ge::pointers_handled_p): Remove. * range-op.cc (TRAP_ON_UNHANDLED_POINTER_OPERATORS): Remove. (range_op_handler::lhs_op1_relation): Remove pointers_handled_p checks. (range_op_handler::lhs_op2_relation): Same. (range_op_handler::op1_op2_relation): Same. * range-op.h: Remove RO_* declarations. --- gcc/gimple-range-op.cc | 24 gcc/ipa-cp.cc | 12 ++ gcc/range-op-mixed.h | 38 ++ gcc/range-op-ptr.cc| 259 - gcc/range-op.cc| 43 +-- gcc/range-op.h | 17 --- 6 files changed, 25 insertions(+), 368 deletions(-) diff --git a/gcc/gimple-range-op.cc b/gcc/gimple-range-op.cc index 55dfbb23ce2..7321342b00d 100644 --- a/gcc/gimple-range-op.cc +++ b/gcc/gimple-range-op.cc @@ -329,19 +329,6 @@ public: r = lhs; return true; } - virtual bool pointers_handled_p (range_op_dispatch_type type, - unsigned dispatch) const - { -switch (type) - { - case DISPATCH_FOLD_RANGE: - return dispatch == RO_PPP; - case DISPATCH_OP1_RANGE: - return dispatch == RO_PPP; - default: - return true; - } - } } op_cfn_pass_through_arg1; // Implement range operator for CFN_BUILT_IN_SIGNBIT. @@ -1132,17 +1119,6 @@ public: r.set (type, wi::zero (TYPE_PRECISION (type)), max - 2); return true; } - virtual bool pointers_handled_p (range_op_dispatch_type type, - unsigned dispatch) const - { -switch (type) - { - case DISPATCH_FOLD_RANGE: - return dispatch == RO_IPI; - default: - return true; - } - } } op_cfn_strlen; diff --git a/gcc/ipa-cp.cc b/gcc/ipa-cp.cc index 5781f50c854..09cab761822 100644 --- a/gcc/ipa-cp.cc +++ b/gcc/ipa-cp.cc @@ -1740,6 +1740,11 @@ ipa_value_range_from_jfunc (vrange &vr, if (!handler || !op_res.supports_type_p (vr_type) + /* Sometimes we try to fold comparison operators using a +pointer type to hold the result instead of a boolean +type. Avoid trapping in the sanity check in +fold_range until this is fixed. */ + || !handler.operand_check_p (vr_type, srcvr.type (), op_vr.type ()) || !handler.fold_range (op_res, vr_type, srcvr, op_vr)) op_res.set_varying (vr_type); @@ -2547,6 +2552,13 @@ propagate_vr_across_jump_function (cgraph_edge *cs, ipa_jump_func *jfunc, if (!handler || !ipa_supports_p (operand_type) + /* Sometimes we try to fold comparison operators using a +pointer type to hold the result instead of a boolean +type. Avoid trapping in the sanity check in +fold_range until this is fixed. */ + || !handler.operand_check_p (operand_type, + src_lats->m_value_range.m_vr.type (), +
[COMMITTED] Use a boolean type when folding conditionals in simplify_using_ranges.
In adding some traps for PR114985 I noticed that the conditional folding code in simplify_using_ranges was using the wrong type. This cleans up the oversight. gcc/ChangeLog: PR tree-optimization/114985 * vr-values.cc (simplify_using_ranges::fold_cond_with_ops): Use boolean type when folding conditionals. --- gcc/vr-values.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/gcc/vr-values.cc b/gcc/vr-values.cc index 0572bf6c8c7..e6ea9592574 100644 --- a/gcc/vr-values.cc +++ b/gcc/vr-values.cc @@ -316,10 +316,9 @@ simplify_using_ranges::fold_cond_with_ops (enum tree_code code, || !query->range_of_expr (r1, op1, s)) return NULL_TREE; - tree type = TREE_TYPE (op0); int_range<1> res; range_op_handler handler (code); - if (handler && handler.fold_range (res, type, r0, r1)) + if (handler && handler.fold_range (res, boolean_type_node, r0, r1)) { if (res == range_true ()) return boolean_true_node; -- 2.45.0
[COMMITTED] Revert "Revert: "Enable prange support.""
This reverts commit d7bb8eaade3cd3aa70715c8567b4d7b08098e699 and enables prange support again. --- gcc/gimple-range-cache.cc | 4 ++-- gcc/gimple-range-fold.cc | 4 ++-- gcc/gimple-range-fold.h | 2 +- gcc/gimple-range-infer.cc | 2 +- gcc/gimple-range-op.cc| 2 +- gcc/gimple-range-path.cc | 2 +- gcc/gimple-ssa-warn-access.cc | 2 +- gcc/ipa-cp.h | 2 +- gcc/range-op-ptr.cc | 4 gcc/range-op.cc | 18 -- gcc/tree-ssa-structalias.cc | 2 +- gcc/value-range.cc| 1 + gcc/value-range.h | 4 ++-- 13 files changed, 18 insertions(+), 31 deletions(-) diff --git a/gcc/gimple-range-cache.cc b/gcc/gimple-range-cache.cc index 72ac2552311..bdd2832873a 100644 --- a/gcc/gimple-range-cache.cc +++ b/gcc/gimple-range-cache.cc @@ -274,10 +274,10 @@ sbr_sparse_bitmap::sbr_sparse_bitmap (tree t, vrange_allocator *allocator, // Pre-cache zero and non-zero values for pointers. if (POINTER_TYPE_P (t)) { - int_range<2> nonzero; + prange nonzero; nonzero.set_nonzero (t); m_range[1] = m_range_allocator->clone (nonzero); - int_range<2> zero; + prange zero; zero.set_zero (t); m_range[2] = m_range_allocator->clone (zero); } diff --git a/gcc/gimple-range-fold.cc b/gcc/gimple-range-fold.cc index 9c4ad1ee7b9..a9c8c4d03e6 100644 --- a/gcc/gimple-range-fold.cc +++ b/gcc/gimple-range-fold.cc @@ -597,7 +597,7 @@ fold_using_range::fold_stmt (vrange &r, gimple *s, fur_source &src, tree name) // Process addresses. if (gimple_code (s) == GIMPLE_ASSIGN && gimple_assign_rhs_code (s) == ADDR_EXPR) -return range_of_address (as_a (r), s, src); +return range_of_address (as_a (r), s, src); gimple_range_op_handler handler (s); if (handler) @@ -757,7 +757,7 @@ fold_using_range::range_of_range_op (vrange &r, // If a range cannot be calculated, set it to VARYING and return true. bool -fold_using_range::range_of_address (irange &r, gimple *stmt, fur_source &src) +fold_using_range::range_of_address (prange &r, gimple *stmt, fur_source &src) { gcc_checking_assert (gimple_code (stmt) == GIMPLE_ASSIGN); gcc_checking_assert (gimple_assign_rhs_code (stmt) == ADDR_EXPR); diff --git a/gcc/gimple-range-fold.h b/gcc/gimple-range-fold.h index 7cbe15d05e5..c7c599bfc93 100644 --- a/gcc/gimple-range-fold.h +++ b/gcc/gimple-range-fold.h @@ -157,7 +157,7 @@ protected: fur_source &src); bool range_of_call (vrange &r, gcall *call, fur_source &src); bool range_of_cond_expr (vrange &r, gassign* cond, fur_source &src); - bool range_of_address (irange &r, gimple *s, fur_source &src); + bool range_of_address (prange &r, gimple *s, fur_source &src); bool range_of_phi (vrange &r, gphi *phi, fur_source &src); void range_of_ssa_name_with_loop_info (vrange &, tree, class loop *, gphi *, fur_source &src); diff --git a/gcc/gimple-range-infer.cc b/gcc/gimple-range-infer.cc index c8e8b9b60ac..d5e1aa14275 100644 --- a/gcc/gimple-range-infer.cc +++ b/gcc/gimple-range-infer.cc @@ -123,7 +123,7 @@ gimple_infer_range::add_nonzero (tree name) { if (!gimple_range_ssa_p (name)) return; - int_range<2> nz; + prange nz; nz.set_nonzero (TREE_TYPE (name)); add_range (name, nz); } diff --git a/gcc/gimple-range-op.cc b/gcc/gimple-range-op.cc index 7321342b00d..aec3f39ec0e 100644 --- a/gcc/gimple-range-op.cc +++ b/gcc/gimple-range-op.cc @@ -1107,7 +1107,7 @@ class cfn_strlen : public range_operator { public: using range_operator::fold_range; - virtual bool fold_range (irange &r, tree type, const irange &, + virtual bool fold_range (irange &r, tree type, const prange &, const irange &, relation_trio) const { wide_int max = irange_val_max (ptrdiff_type_node); diff --git a/gcc/gimple-range-path.cc b/gcc/gimple-range-path.cc index 96c6ac6b6a5..f1a12f76144 100644 --- a/gcc/gimple-range-path.cc +++ b/gcc/gimple-range-path.cc @@ -443,7 +443,7 @@ path_range_query::compute_ranges_in_block (basic_block bb) void path_range_query::adjust_for_non_null_uses (basic_block bb) { - int_range_max r; + prange r; bitmap_iterator bi; unsigned i; diff --git a/gcc/gimple-ssa-warn-access.cc b/gcc/gimple-ssa-warn-access.cc index 2c10d19e7f3..0cd5b6d6ef4 100644 --- a/gcc/gimple-ssa-warn-access.cc +++ b/gcc/gimple-ssa-warn-access.cc @@ -4213,7 +4213,7 @@ pass_waccess::check_pointer_uses (gimple *stmt, tree ptr, where the realloc call is known to have failed are valid. Ignore pointers that nothing is known about. Those could have escaped along with their nullness. */ - value_range vr; + prange vr; if (m_ptr_qry.rvals->range_of_expr (vr, realloc_lhs, use_stmt)) { if (vr.zero_p ()) diff --git a/gcc/ipa-cp.h b/gcc/ipa-cp.h in
Re: [COMMITTED] Revert "Revert: "Enable prange support.""
Wait, what's the preferred way of reverting a patch? I followed what I saw in: commit 04ee1f788ceaa4c7f777ff3b9441ae076191439c Author: Jeff Law Date: Mon May 13 21:42:38 2024 -0600 Revert "[PATCH v2 1/3] RISC-V: movmem for RISCV with V extension" This reverts commit df15eb15b5f820321c81efc75f0af13ff8c0dd5b. and here: commit 0c6dd4b0973738ce43e76b468a002ab5eb58aaf4 Author: YunQiang Su Date: Mon May 13 14:15:38 2024 +0800 Revert "MIPS: Support constraint 'w' for MSA instruction" This reverts commit 9ba01240864ac446052d97692e2199539b7c76d8. and here: commit f6ce85502eb2e4e7bbd9b3c6c1c065a004f8f531 Author: Hans-Peter Nilsson Date: Wed May 8 04:11:20 2024 +0200 Revert "Revert "testsuite/gcc.target/cris/pr93372-2.c: Handle xpass from combine improvement"" This reverts commit 39f81924d88e3cc197fc3df74204c9b5e01e12f7. etc etc. Next time, would you like me to add manual changelog entries? My apologies, I thought what I did was the blessed way of doing things. Aldy On Thu, May 16, 2024 at 12:08 PM Jakub Jelinek wrote: > > On Thu, May 16, 2024 at 12:01:01PM +0200, Aldy Hernandez wrote: > > This reverts commit d7bb8eaade3cd3aa70715c8567b4d7b08098e699 and enables > > prange > > support again. > > Please don't do this. > This breaks ChangeLog generation, will need to handle it tomorrow by hand > again. > Both the ammendments to the git (cherry-pick -x or revert) added message > lines > This reverts commit COMMITHASH. > and > (cherry picked from commit COMMITHASH) > and revert of revert. > > Jakub >
[COMMITTED] [prange] Avoid looking at type() for undefined ranges
Undefined ranges have no type. This patch fixes the thinko. gcc/ChangeLog: PR middle-end/115128 * ipa-cp.cc (ipa_value_range_from_jfunc): Check for undefined_p before looking at type. (propagate_vr_across_jump_function): Same. gcc/testsuite/ChangeLog: * gcc.dg/tree-ssa/pr115128.c: New test. --- gcc/ipa-cp.cc| 4 +++ gcc/testsuite/gcc.dg/tree-ssa/pr115128.c | 31 2 files changed, 35 insertions(+) create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr115128.c diff --git a/gcc/ipa-cp.cc b/gcc/ipa-cp.cc index 09cab761822..408166b8044 100644 --- a/gcc/ipa-cp.cc +++ b/gcc/ipa-cp.cc @@ -1744,6 +1744,8 @@ ipa_value_range_from_jfunc (vrange &vr, pointer type to hold the result instead of a boolean type. Avoid trapping in the sanity check in fold_range until this is fixed. */ + || srcvr.undefined_p () + || op_vr.undefined_p () || !handler.operand_check_p (vr_type, srcvr.type (), op_vr.type ()) || !handler.fold_range (op_res, vr_type, srcvr, op_vr)) op_res.set_varying (vr_type); @@ -2556,6 +2558,8 @@ propagate_vr_across_jump_function (cgraph_edge *cs, ipa_jump_func *jfunc, pointer type to hold the result instead of a boolean type. Avoid trapping in the sanity check in fold_range until this is fixed. */ + || src_lats->m_value_range.m_vr.undefined_p () + || op_vr.undefined_p () || !handler.operand_check_p (operand_type, src_lats->m_value_range.m_vr.type (), op_vr.type ()) diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr115128.c b/gcc/testsuite/gcc.dg/tree-ssa/pr115128.c new file mode 100644 index 000..14bd4dbd6e5 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr115128.c @@ -0,0 +1,31 @@ +/* { dg-do compile } */ +/* { dg-options "-O3 -w" } */ + +long XXH3_len_4to8_64b_len, XXH3_len_0to16_64b___trans_tmp_3, XXH3_mix2Accs_acc, +XXH3_64bits_internal___trans_tmp_8; +typedef unsigned long XXH3_hashLong64_f(); +void *XXH3_64bits_internal_input; +int XXH3_64bits_internal___trans_tmp_1; +void XXH3_mul128_fold64(); +static void XXH3_mergeAccs(unsigned long) { + for (;;) +XXH3_mul128_fold64(XXH3_mix2Accs_acc); +} +static __attribute__((noinline)) unsigned long +XXH3_hashLong_64b_default(void *, unsigned long len) { + XXH3_mergeAccs(len * 7); +} +__attribute__((always_inline)) long +XXH3_64bits_internal(unsigned long len, XXH3_hashLong64_f f_hashLong) { + if (len <= 16) { +long keyed = +XXH3_64bits_internal___trans_tmp_1 ^ XXH3_len_0to16_64b___trans_tmp_3; +XXH3_mul128_fold64(keyed, XXH3_len_4to8_64b_len); +return XXH3_64bits_internal___trans_tmp_8; + } + f_hashLong(XXH3_64bits_internal_input, len); +} +static void XXH_INLINE_XXH3_64bits(unsigned long len) { + XXH3_64bits_internal(len, XXH3_hashLong_64b_default); +} +void __cmplog_rtn_hook() { XXH_INLINE_XXH3_64bits(sizeof(long)); } -- 2.45.0
[COMMITTED] [prange] Drop range to VARYING if the bitmask intersection made it so [PR115131]
If the intersection of the bitmasks made the range span the entire domain, normalize the range to VARYING. gcc/ChangeLog: PR middle-end/115131 * value-range.cc (prange::intersect): Set VARYING if intersection of bitmasks made the range span the entire domain. (range_tests_misc): New test. --- gcc/value-range.cc | 21 + 1 file changed, 21 insertions(+) diff --git a/gcc/value-range.cc b/gcc/value-range.cc index 334ffb70fbc..b38d6159a85 100644 --- a/gcc/value-range.cc +++ b/gcc/value-range.cc @@ -589,6 +589,11 @@ prange::intersect (const vrange &v) irange_bitmask new_bitmask = get_bitmask_from_range (m_type, m_min, m_max); m_bitmask.intersect (new_bitmask); m_bitmask.intersect (r.m_bitmask); + if (varying_compatible_p ()) +{ + set_varying (type ()); + return true; +} if (flag_checking) verify_range (); @@ -2889,6 +2894,22 @@ range_tests_misc () p0.invert (); ASSERT_TRUE (p0 == p1); + // The intersection of: + //[0, +INF] MASK 0xff..00 VALUE 0xf8 + //[0, +INF] MASK 0xff..00 VALUE 0x00 + // is [0, +INF] MASK 0xff..ff VALUE 0x00, which is VARYING. + // Test that we normalized to VARYING. + unsigned prec = TYPE_PRECISION (voidp); + p0.set_varying (voidp); + wide_int mask = wi::mask (8, true, prec); + wide_int value = wi::uhwi (0xf8, prec); + irange_bitmask bm (wi::uhwi (0xf8, prec), mask); + p0.update_bitmask (bm); + p1.set_varying (voidp); + bm = irange_bitmask (wi::zero (prec), mask); + p1.update_bitmask (bm); + p0.intersect (p1); + // [10,20] U [15, 30] => [10, 30]. r0 = range_int (10, 20); r1 = range_int (15, 30); -- 2.45.0
[COMMITTED] [prange] Use type agnostic range in phiopt [PR115191]
Fix a use of int_range_max in phiopt that should be a type agnostic range, because it could be either a pointer or an int. PR tree-optimization/115191 gcc/ChangeLog: * tree-ssa-phiopt.cc (value_replacement): Use Value_Range instead of int_range_max. gcc/testsuite/ChangeLog: * gcc.dg/tree-ssa/pr115191.c: New test. --- gcc/testsuite/gcc.dg/tree-ssa/pr115191.c | 10 ++ gcc/tree-ssa-phiopt.cc | 5 ++--- 2 files changed, 12 insertions(+), 3 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr115191.c diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr115191.c b/gcc/testsuite/gcc.dg/tree-ssa/pr115191.c new file mode 100644 index 000..43f780aa3b8 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr115191.c @@ -0,0 +1,10 @@ +// { dg-do compile } +// { dg-options "-O1 -w" } + +typedef void *SCM; +void set_socket_io_ports(); +void STk_socket_accept(SCM line_buffered) { + if (!line_buffered) +line_buffered = (SCM)3; + set_socket_io_ports(line_buffered != 1); +} diff --git a/gcc/tree-ssa-phiopt.cc b/gcc/tree-ssa-phiopt.cc index 918cf50b589..65f63eb0652 100644 --- a/gcc/tree-ssa-phiopt.cc +++ b/gcc/tree-ssa-phiopt.cc @@ -1326,12 +1326,11 @@ value_replacement (basic_block cond_bb, basic_block middle_bb, { /* After the optimization PHI result can have value which it couldn't have previously. */ - int_range_max r; + Value_Range r (TREE_TYPE (phires)); if (get_global_range_query ()->range_of_expr (r, phires, phi)) { - wide_int warg = wi::to_wide (carg); - int_range<2> tmp (TREE_TYPE (carg), warg, warg); + Value_Range tmp (carg, carg); r.union_ (tmp); reset_flow_sensitive_info (phires); set_range_info (phires, r); -- 2.45.0
[COMMITTED] Remove value_range typedef.
Now that pointers and integers have been disambiguated from irange, and all the pointer range temporaries use prange, we can reclaim value_range as a general purpose range container. This patch removes the typedef, in favor of int_range_max, thus providing slightly better ranges in places. I have also used int_range<1> or <2> when it's known ahead of time how big the range will be, thus saving a few words. In a follow-up patch I will rename the Value_Range temporary to value_range. No change in performance. gcc/ChangeLog: * builtins.cc (expand_builtin_strnlen): Replace value_range use with int_range_max or irange when appropriate. (determine_block_size): Same. * fold-const.cc (minmax_from_comparison): Same. * gimple-array-bounds.cc (check_out_of_bounds_and_warn): Same. (array_bounds_checker::check_array_ref): Same. * gimple-fold.cc (size_must_be_zero_p): Same. * gimple-predicate-analysis.cc (find_var_cmp_const): Same. * gimple-ssa-sprintf.cc (get_int_range): Same. (format_integer): Same. (try_substitute_return_value): Same. (handle_printf_call): Same. * gimple-ssa-warn-restrict.cc (builtin_memref::extend_offset_range): Same. * graphite-sese-to-poly.cc (add_param_constraints): Same. * internal-fn.cc (get_min_precision): Same. * match.pd: Same. * pointer-query.cc (get_size_range): Same. * range-op.cc (get_shift_range): Same. (operator_trunc_mod::op1_range): Same. (operator_trunc_mod::op2_range): Same. * range.cc (range_negatives): Same. * range.h (range_positives): Same. (range_negatives): Same. * tree-affine.cc (expr_to_aff_combination): Same. * tree-data-ref.cc (compute_distributive_range): Same. (nop_conversion_for_offset_p): Same. (split_constant_offset): Same. (split_constant_offset_1): Same. (dr_step_indicator): Same. * tree-dfa.cc (get_ref_base_and_extent): Same. * tree-scalar-evolution.cc (iv_can_overflow_p): Same. * tree-ssa-math-opts.cc (optimize_spaceship): Same. * tree-ssa-pre.cc (insert_into_preds_of_block): Same. * tree-ssa-reassoc.cc (optimize_range_tests_to_bit_test): Same. * tree-ssa-strlen.cc (compare_nonzero_chars): Same. (dump_strlen_info): Same. (get_range_strlen_dynamic): Same. (set_strlen_range): Same. (maybe_diag_stxncpy_trunc): Same. (strlen_pass::get_len_or_size): Same. (strlen_pass::handle_builtin_string_cmp): Same. (strlen_pass::count_nonzero_bytes_addr): Same. (strlen_pass::handle_integral_assign): Same. * tree-switch-conversion.cc (bit_test_cluster::emit): Same. * tree-vect-loop-manip.cc (vect_gen_vector_loop_niters): Same. (vect_do_peeling): Same. * tree-vect-patterns.cc (vect_get_range_info): Same. (vect_recog_divmod_pattern): Same. * tree.cc (get_range_pos_neg): Same. * value-range.cc (debug): Remove value_range variants. * value-range.h (value_range): Remove typedef. * vr-values.cc (simplify_using_ranges::op_with_boolean_value_range_p): Replace value_range use with int_range_max or irange when appropriate. (check_for_binary_op_overflow): Same. (simplify_using_ranges::legacy_fold_cond_overflow): Same. (find_case_label_ranges): Same. (simplify_using_ranges::simplify_abs_using_ranges): Same. (test_for_singularity): Same. (simplify_using_ranges::simplify_compare_using_ranges_1): Same. (simplify_using_ranges::simplify_casted_compare): Same. (simplify_using_ranges::simplify_switch_using_ranges): Same. (simplify_conversion_using_ranges): Same. (simplify_using_ranges::two_valued_val_range_p): Same. --- gcc/builtins.cc | 4 ++-- gcc/fold-const.cc| 4 ++-- gcc/gimple-array-bounds.cc | 4 ++-- gcc/gimple-fold.cc | 4 ++-- gcc/gimple-predicate-analysis.cc | 2 +- gcc/gimple-ssa-sprintf.cc| 8 +++ gcc/gimple-ssa-warn-restrict.cc | 2 +- gcc/graphite-sese-to-poly.cc | 2 +- gcc/internal-fn.cc | 2 +- gcc/match.pd | 22 +- gcc/pointer-query.cc | 2 +- gcc/range-op.cc | 21 +- gcc/range.cc | 10 - gcc/range.h | 4 ++-- gcc/tree-affine.cc | 2 +- gcc/tree-data-ref.cc | 28 +++ gcc/tree-dfa.cc | 2 +- gcc/tree-scalar-evolution.cc | 2 +- gcc/tree-ssa-math-opts.cc| 2 +- gcc/tree-ssa-pre.cc | 2 +- gcc/tree-ssa-reassoc.cc | 2 +- gcc/tree-ssa-strlen.cc | 22 +- gcc/tree-switch-conversion.cc| 2 +- gcc/tree-vect-loop-manip.cc
Re: [PATCH] tree-optimization/116166 - forward jump-threading going wild
[I'm slowly coming up to speed here after my absence, so please bear with me...] I suspect there's a few things going on here, both in the forward and the backwards threader. For the forward threader, you mention some very good points in the PR. First, there's unnecessary recursion in simplify_control_stmt_condition_1 that ranger should be able to handle on its own. Secondly, since we're doing a DOM walk, we should be able to re-use most of the path_ranger's cache instead of having to reset all of it on every path, especially when we're just adding empty blocks. I can investigate both of these things. The end game here is to get rid of the forward threader, so we should really find out why the backwards threader is choking so bad. I suspect whatever the case is, will affect both threaders. I thought you had added some limits in the search space last cycle? Are they not being triggered? For the record, the reason we can't get rid of the forward threader yet (apart from having to fix whatever is going on in PR114855 at -O2 :)), is that we still rely on the pointer equivalency tracking with the DOM equiv lookup tables. Prange does not yet handle pointer equivs. Also, we'd need to audit to make sure frange handles whatever floating point operations were being simplified in the DOM equiv lookup as well. I suspect not much, but we still need to make sure. Minor nit, wouldn't it be cleaner for "limit" to be a class local variable instead of passing it around as a function parameter? Thanks for all your work here. Aldy On Tue, Aug 6, 2024 at 3:12 PM Richard Biener wrote: > > Currently the forward threader isn't limited as to the search space > it explores and with it now using path-ranger for simplifying > conditions it runs into it became pretty slow for degenerate cases > like compiling insn-emit.cc for RISC-V esp. when compiling for > a host with LOGICAL_OP_NON_SHORT_CIRCUIT disabled. > > The following makes the forward threader honor the search space > limit I introduced for the backward threader. This reduces > compile-time from minutes to seconds for the testcase in PR116166. > > Note this wasn't necessary before we had ranger but with ranger > the work we do is quadatic in the length of the threading path > we build up (the same is true for the backwards threader). > > Bootstrap and regtest running on x86_64-unknown-linux-gnu. > > OK if that succeeds? > > Thanks, > Richard. > > PR tree-optimization/116166 > * tree-ssa-threadedge.h (jump_threader::thread_around_empty_blocks): > Add limit parameter. > (jump_threader::thread_through_normal_block): Likewise. > * tree-ssa-threadedge.cc (jump_threader::thread_around_empty_blocks): > Honor and decrement limit parameter. > (jump_threader::thread_through_normal_block): Likewise. > (jump_threader::thread_across_edge): Initialize limit from > param_max_jump_thread_paths and pass it down to workers. > --- > gcc/tree-ssa-threadedge.cc | 30 ++ > gcc/tree-ssa-threadedge.h | 4 ++-- > 2 files changed, 24 insertions(+), 10 deletions(-) > > diff --git a/gcc/tree-ssa-threadedge.cc b/gcc/tree-ssa-threadedge.cc > index 7f82639b8ec..0aa2aa85143 100644 > --- a/gcc/tree-ssa-threadedge.cc > +++ b/gcc/tree-ssa-threadedge.cc > @@ -786,13 +786,17 @@ propagate_threaded_block_debug_into (basic_block dest, > basic_block src) > bool > jump_threader::thread_around_empty_blocks (vec *path, >edge taken_edge, > - bitmap visited) > + bitmap visited, unsigned &limit) > { >basic_block bb = taken_edge->dest; >gimple_stmt_iterator gsi; >gimple *stmt; >tree cond; > > + if (limit == 0) > +return false; > + --limit; > + >/* The key property of these blocks is that they need not be duplicated > when threading. Thus they cannot have visible side effects such > as PHI nodes. */ > @@ -830,7 +834,8 @@ jump_threader::thread_around_empty_blocks > (vec *path, > m_registry->push_edge (path, taken_edge, > EDGE_NO_COPY_SRC_BLOCK); > m_state->append_path (taken_edge->dest); > bitmap_set_bit (visited, taken_edge->dest->index); > - return thread_around_empty_blocks (path, taken_edge, visited); > + return thread_around_empty_blocks (path, taken_edge, visited, > +limit); > } > } > > @@ -872,7 +877,7 @@ jump_threader::thread_around_empty_blocks > (vec *path, >m_registry->push_edge (path, taken_edge, EDGE_NO_COPY_SRC_BLOCK); >m_state->append_path (taken_edge->dest); > > - thread_around_empty_blocks (path, taken_edge, visited); > + thread_around_empty_blocks (path, taken_edge, visited, limit); >return true; > } > > @@ -899,8 +904,13 @@ jump_threader::thread_ar
[COMMITTED] Rename Value_Range to value_range.
Now that all remaining users of value_range have been renamed to int_range<>, we can reclaim value_range as a temporary, thus removing the annoying CamelCase. gcc/ChangeLog: * data-streamer-in.cc (streamer_read_value_range): Rename Value_Range to value_range. * data-streamer.h (streamer_read_value_range): Same. * gimple-pretty-print.cc (dump_ssaname_info): Same. * gimple-range-cache.cc (ssa_block_ranges::dump): Same. (ssa_lazy_cache::merge): Same. (block_range_cache::dump): Same. (ssa_cache::merge_range): Same. (ssa_cache::dump): Same. (ranger_cache::edge_range): Same. (ranger_cache::propagate_cache): Same. (ranger_cache::fill_block_cache): Same. (ranger_cache::resolve_dom): Same. (ranger_cache::range_from_dom): Same. (ranger_cache::register_inferred_value): Same. * gimple-range-fold.cc (op1_range): Same. (op2_range): Same. (fold_relations): Same. (fold_using_range::range_of_range_op): Same. (fold_using_range::range_of_phi): Same. (fold_using_range::range_of_call): Same. (fold_using_range::condexpr_adjust): Same. (fold_using_range::range_of_cond_expr): Same. (fur_source::register_outgoing_edges): Same. * gimple-range-fold.h (gimple_range_type): Same. (gimple_range_ssa_p): Same. * gimple-range-gori.cc (gori_compute::compute_operand_range): Same. (gori_compute::logical_combine): Same. (gori_compute::refine_using_relation): Same. (gori_compute::compute_operand1_range): Same. (gori_compute::compute_operand2_range): Same. (gori_compute::compute_operand1_and_operand2_range): Same. (gori_calc_operands): Same. (gori_name_helper): Same. * gimple-range-infer.cc (gimple_infer_range::check_assume_func): Same. (gimple_infer_range::gimple_infer_range): Same. (infer_range_manager::maybe_adjust_range): Same. (infer_range_manager::add_range): Same. * gimple-range-infer.h: Same. * gimple-range-op.cc (gimple_range_op_handler::gimple_range_op_handler): Same. (gimple_range_op_handler::calc_op1): Same. (gimple_range_op_handler::calc_op2): Same. (gimple_range_op_handler::maybe_builtin_call): Same. * gimple-range-path.cc (path_range_query::internal_range_of_expr): Same. (path_range_query::ssa_range_in_phi): Same. (path_range_query::compute_ranges_in_phis): Same. (path_range_query::compute_ranges_in_block): Same. (path_range_query::add_to_exit_dependencies): Same. * gimple-range-trace.cc (debug_seed_ranger): Same. * gimple-range.cc (gimple_ranger::range_of_expr): Same. (gimple_ranger::range_on_entry): Same. (gimple_ranger::range_on_edge): Same. (gimple_ranger::range_of_stmt): Same. (gimple_ranger::prefill_stmt_dependencies): Same. (gimple_ranger::register_inferred_ranges): Same. (gimple_ranger::register_transitive_inferred_ranges): Same. (gimple_ranger::export_global_ranges): Same. (gimple_ranger::dump_bb): Same. (assume_query::calculate_op): Same. (assume_query::calculate_phi): Same. (assume_query::dump): Same. (dom_ranger::range_of_stmt): Same. * ipa-cp.cc (ipcp_vr_lattice::meet_with_1): Same. (ipa_vr_operation_and_type_effects): Same. (ipa_value_range_from_jfunc): Same. (propagate_bits_across_jump_function): Same. (propagate_vr_across_jump_function): Same. (ipcp_store_vr_results): Same. * ipa-cp.h: Same. * ipa-fnsummary.cc (evaluate_conditions_for_known_args): Same. (evaluate_properties_for_edge): Same. * ipa-prop.cc (struct ipa_vr_ggc_hash_traits): Same. (ipa_vr::get_vrange): Same. (ipa_vr::streamer_read): Same. (ipa_vr::streamer_write): Same. (ipa_vr::dump): Same. (ipa_set_jfunc_vr): Same. (ipa_compute_jump_functions_for_edge): Same. (ipcp_get_parm_bits): Same. (ipcp_update_vr): Same. (ipa_record_return_value_range): Same. (ipa_return_value_range): Same. * ipa-prop.h (ipa_return_value_range): Same. (ipa_record_return_value_range): Same. * range-op.h (range_cast): Same. * tree-ssa-dom.cc (dom_opt_dom_walker::set_global_ranges_from_unreachable_edges): Same. (cprop_operand): Same. * tree-ssa-loop-ch.cc (loop_static_stmt_p): Same. * tree-ssa-loop-niter.cc (record_nonwrapping_iv): Same. * tree-ssa-loop-split.cc (split_at_bb_p): Same. * tree-ssa-phiopt.cc (value_replacement): Same. * tree-ssa-strlen.cc (get_range): Same. * tree-ssa-threadedge.cc (hybrid_jt_simplifier::simplify): Same. (hybrid_jt_simplifier::compute_exit_dependencies): Same. * tree-ssanames.cc (set_rang
Re: [PATCH] middle-end/114604 - ranger allocates bitmap without initialized obstack
Hi. I came around to this, and whipped up the proposed patch. However, it does seem a bit verbose, and I'm wondering if it's cleaner to just leave things as they are. The attached patch passes tests and there's no difference in performance. I am wondering, whether it's better to get rid of all/most of the local obstacks we use in ranger, and just use the global (NULL) one? Thoughts? Aldy On Mon, Apr 8, 2024 at 7:47 PM Richard Biener wrote: > > > > > Am 08.04.2024 um 18:40 schrieb Aldy Hernandez : > > > > On Mon, Apr 8, 2024 at 6:29 PM Richard Biener wrote: > >> > >> > >> > >>>> Am 08.04.2024 um 18:09 schrieb Aldy Hernandez : > >>> > >>> On Mon, Apr 8, 2024 at 5:54 PM Jakub Jelinek wrote: > >>>> > >>>> On Mon, Apr 08, 2024 at 05:40:23PM +0200, Aldy Hernandez wrote: > >>>>>> PR middle-end/114604 > >>>>>> * gimple-range.cc (enable_ranger): Initialize the global > >>>>>> bitmap obstack. > >>>>>> (disable_ranger): Release it. > >>>>>> --- > >>>>>> gcc/gimple-range.cc | 4 > >>>>>> 1 file changed, 4 insertions(+) > >>>>>> > >>>>>> diff --git a/gcc/gimple-range.cc b/gcc/gimple-range.cc > >>>>>> index c16b776c1e3..4d3b1ce8588 100644 > >>>>>> --- a/gcc/gimple-range.cc > >>>>>> +++ b/gcc/gimple-range.cc > >>>>>> @@ -689,6 +689,8 @@ enable_ranger (struct function *fun, bool > >>>>>> use_imm_uses) > >>>>>> { > >>>>>> gimple_ranger *r; > >>>>>> > >>>>>> + bitmap_obstack_initialize (NULL); > >>>>>> + > >>>>>> gcc_checking_assert (!fun->x_range_query); > >>>>>> r = new gimple_ranger (use_imm_uses); > >>>>>> fun->x_range_query = r; > >>>>>> @@ -705,6 +707,8 @@ disable_ranger (struct function *fun) > >>>>>> gcc_checking_assert (fun->x_range_query); > >>>>>> delete fun->x_range_query; > >>>>>> fun->x_range_query = NULL; > >>>>>> + > >>>>>> + bitmap_obstack_release (NULL); > >>>>> > >>>>> Are you not allowed to initialize/use obstacks unless > >>>>> bitmap_obstack_initialize(NULL) is called? > >>>> > >>>> You can use it with some other obstack, just not the default one. > >>>> > >>>>> If so, wouldn't it be > >>>>> better to lazily initialize it downstream (bitmap_alloc, or whomever > >>>>> needs it initialized)? > >>>> > >>>> No, you still need to decide where is the safe point to release it. > >>>> Unlike the non-default bitmap_obstack_initialize/bitmap_obstack_release, > >>>> the default one can nest (has associated nesting counter). So, the above > >>>> patch just says that ranger starts using the default obstack in > >>>> enable_ranger and stops using it in disable_ranger and anything ranger > >>>> associated in the obstack can be freed at that point. > >>> > >>> I thought ranger never used the default one: > >>> > >>> $ grep bitmap_obstack_initialize *value* *range* > >>> value-relation.cc: bitmap_obstack_initialize (&m_bitmaps); > >>> value-relation.cc: bitmap_obstack_initialize (&m_bitmaps); > >>> gimple-range-cache.cc: bitmap_obstack_initialize (&m_bitmaps); > >>> gimple-range-gori.cc: bitmap_obstack_initialize (&m_bitmaps); > >>> gimple-range-infer.cc: bitmap_obstack_initialize (&m_bitmaps); > >>> gimple-range-phi.cc: bitmap_obstack_initialize (&m_bitmaps); > >>> > >>> or even: > >>> > >>> $ grep obstack.*NULL *value* *range* > >>> value-range-storage.cc:obstack_free (&m_obstack, NULL); > >>> value-relation.cc: obstack_free (&m_chain_obstack, NULL); > >>> value-relation.cc: obstack_free (&m_chain_obstack, NULL); > >>> gimple-range-infer.cc: obstack_free (&m_list_obstack, NULL); > >>> value-range-storage.cc:obstack_free (&m_obstack, NULL); > >>> > >>> I'm obviously missing something here. > >> > >> Look for BITMAP_ALLOC (NULL) in the back
Re: [PATCH] middle-end/114604 - ranger allocates bitmap without initialized obstack
On 6/20/24 4:36 PM, Richard Biener wrote: Am 20.06.2024 um 16:05 schrieb Andrew MacLeod : On 6/20/24 05:31, Richard Biener wrote: On Thu, 20 Jun 2024, Aldy Hernandez wrote: Hi. I came around to this, and whipped up the proposed patch. However, it does seem a bit verbose, and I'm wondering if it's cleaner to just leave things as they are. The attached patch passes tests and there's no difference in performance. I am wondering, whether it's better to get rid of all/most of the local obstacks we use in ranger, and just use the global (NULL) one? Thoughts? It really depends on how much garbage ranger is expected to create on the obstack - the global obstack is released after each pass. But ranger instances are also not expected to be created multiple times each pass, right? Typically correct. Although the path ranger also creates a normal ranger,. Different components also have their own obstacks, mostly because they can be used independent of ranger. I didn't want to add artificial dependencies just for a obstack sharing. I was unaware of how the global one worked at that point. Do they get stacked if another global obstack is initialized? And is there any danger in that case twe could accidentally have a sequence like: obstack1 created by ranger GORI allocates bitmap from obstack1 obstack2 created by the pass that decided to use ranger GORI allocates bitmap2.. comes from obstack2 obstack2 destroyed by the pass. GORI tries to use bitmap2 .. its now unallocated. If so, this reeks of the obstack problems we had back in the late 90's when obstacks were generally stacked. Tracking down objects still in use from freed obstacks was a nightmare. That is one of the reason general stacked obstacks fell out of favour for a long time, and why i only ever use local named one. It seems to me that components managing their own obstacks ensures this does not happen. If, however, that is not longer a problem for some reason, then I have no strong feelings either way either. The global obstack is special, it’s init keeps a reference count. So yes, a local obstack is cleaner. Ok, since a local obstack is cleaner and a global one has the potential to introduce subtle bugs, I have rebased the patch against current mainline and will commit the attached if it passes tests. Thanks for everyone's feedback. AldyFrom cd7b03ba43a74ae808a3005ff0e66cd8fabdaea3 Mon Sep 17 00:00:00 2001 From: Aldy Hernandez Date: Wed, 19 Jun 2024 11:42:16 +0200 Subject: [PATCH] Avoid global bitmap space in ranger. gcc/ChangeLog: * gimple-range-cache.cc (update_list::update_list): Add m_bitmaps. (update_list::~update_list): Initialize m_bitmaps. * gimple-range-cache.h (ssa_lazy_cache): Add m_bitmaps. * gimple-range.cc (enable_ranger): Remove global bitmap initialization. (disable_ranger): Remove global bitmap release. --- gcc/gimple-range-cache.cc | 6 -- gcc/gimple-range-cache.h | 9 +++-- gcc/gimple-range.cc | 4 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/gcc/gimple-range-cache.cc b/gcc/gimple-range-cache.cc index d84fd1ca0e8..6979a14cbaa 100644 --- a/gcc/gimple-range-cache.cc +++ b/gcc/gimple-range-cache.cc @@ -906,6 +906,7 @@ private: vec m_update_list; int m_update_head; bitmap m_propfail; + bitmap_obstack m_bitmaps; }; // Create an update list. @@ -915,7 +916,8 @@ update_list::update_list () m_update_list.create (0); m_update_list.safe_grow_cleared (last_basic_block_for_fn (cfun) + 64); m_update_head = -1; - m_propfail = BITMAP_ALLOC (NULL); + bitmap_obstack_initialize (&m_bitmaps); + m_propfail = BITMAP_ALLOC (&m_bitmaps); } // Destroy an update list. @@ -923,7 +925,7 @@ update_list::update_list () update_list::~update_list () { m_update_list.release (); - BITMAP_FREE (m_propfail); + bitmap_obstack_release (&m_bitmaps); } // Add BB to the list of blocks to update, unless it's already in the list. diff --git a/gcc/gimple-range-cache.h b/gcc/gimple-range-cache.h index 63410d5437e..0ea34d3f686 100644 --- a/gcc/gimple-range-cache.h +++ b/gcc/gimple-range-cache.h @@ -78,8 +78,12 @@ protected: class ssa_lazy_cache : public ssa_cache { public: - inline ssa_lazy_cache () { active_p = BITMAP_ALLOC (NULL); } - inline ~ssa_lazy_cache () { BITMAP_FREE (active_p); } + inline ssa_lazy_cache () + { +bitmap_obstack_initialize (&m_bitmaps); +active_p = BITMAP_ALLOC (&m_bitmaps); + } + inline ~ssa_lazy_cache () { bitmap_obstack_release (&m_bitmaps); } inline bool empty_p () const { return bitmap_empty_p (active_p); } virtual bool has_range (tree name) const; virtual bool set_range (tree name, const vrange &r); @@ -89,6 +93,7 @@ public: virtual void clear (); void merge (const ssa_lazy_cache &); protected: + bitmap_obstack m_bitmaps; bitmap active_p; }; diff --git a/gcc/gimple-range.cc b/gc
[COMMITTED 03/16] Make some Value_Range's explicitly integer.
Fix some Value_Range's that we know ahead of time will be only integers. This avoids using the polymorphic Value_Range unnecessarily gcc/ChangeLog: * gimple-ssa-warn-access.cc (check_nul_terminated_array): Make Value_Range an int_range. (memmodel_to_uhwi): Same * tree-ssa-loop-niter.cc (refine_value_range_using_guard): Same. (determine_value_range): Same. (infer_loop_bounds_from_signedness): Same. (scev_var_range_cant_overflow): Same. --- gcc/gimple-ssa-warn-access.cc | 4 ++-- gcc/tree-ssa-loop-niter.cc| 12 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/gcc/gimple-ssa-warn-access.cc b/gcc/gimple-ssa-warn-access.cc index dedaae27b31..450c1caa765 100644 --- a/gcc/gimple-ssa-warn-access.cc +++ b/gcc/gimple-ssa-warn-access.cc @@ -330,7 +330,7 @@ check_nul_terminated_array (GimpleOrTree expr, tree src, tree bound) wide_int bndrng[2]; if (bound) { - Value_Range r (TREE_TYPE (bound)); + int_range<2> r (TREE_TYPE (bound)); get_range_query (cfun)->range_of_expr (r, bound); @@ -2816,7 +2816,7 @@ memmodel_to_uhwi (tree ord, gimple *stmt, unsigned HOST_WIDE_INT *cstval) { /* Use the range query to determine constant values in the absence of constant propagation (such as at -O0). */ - Value_Range rng (TREE_TYPE (ord)); + int_range<2> rng (TREE_TYPE (ord)); if (!get_range_query (cfun)->range_of_expr (rng, ord, stmt) || !rng.singleton_p (&ord)) return false; diff --git a/gcc/tree-ssa-loop-niter.cc b/gcc/tree-ssa-loop-niter.cc index c6d010f6d89..cbc9dbc5a1f 100644 --- a/gcc/tree-ssa-loop-niter.cc +++ b/gcc/tree-ssa-loop-niter.cc @@ -214,7 +214,7 @@ refine_value_range_using_guard (tree type, tree var, get_type_static_bounds (type, mint, maxt); mpz_init (minc1); mpz_init (maxc1); - Value_Range r (TREE_TYPE (varc1)); + int_range<2> r (TREE_TYPE (varc1)); /* Setup range information for varc1. */ if (integer_zerop (varc1)) { @@ -368,7 +368,7 @@ determine_value_range (class loop *loop, tree type, tree var, mpz_t off, gphi_iterator gsi; /* Either for VAR itself... */ - Value_Range var_range (TREE_TYPE (var)); + int_range<2> var_range (TREE_TYPE (var)); get_range_query (cfun)->range_of_expr (var_range, var); if (var_range.varying_p () || var_range.undefined_p ()) rtype = VR_VARYING; @@ -382,7 +382,7 @@ determine_value_range (class loop *loop, tree type, tree var, mpz_t off, /* Or for PHI results in loop->header where VAR is used as PHI argument from the loop preheader edge. */ - Value_Range phi_range (TREE_TYPE (var)); + int_range<2> phi_range (TREE_TYPE (var)); for (gsi = gsi_start_phis (loop->header); !gsi_end_p (gsi); gsi_next (&gsi)) { gphi *phi = gsi.phi (); @@ -408,7 +408,7 @@ determine_value_range (class loop *loop, tree type, tree var, mpz_t off, involved. */ if (wi::gt_p (minv, maxv, sgn)) { - Value_Range vr (TREE_TYPE (var)); + int_range<2> vr (TREE_TYPE (var)); get_range_query (cfun)->range_of_expr (vr, var); if (vr.varying_p () || vr.undefined_p ()) rtype = VR_VARYING; @@ -4367,7 +4367,7 @@ infer_loop_bounds_from_signedness (class loop *loop, gimple *stmt) low = lower_bound_in_type (type, type); high = upper_bound_in_type (type, type); - Value_Range r (TREE_TYPE (def)); + int_range<2> r (TREE_TYPE (def)); get_range_query (cfun)->range_of_expr (r, def); if (!r.varying_p () && !r.undefined_p ()) { @@ -5426,7 +5426,7 @@ scev_var_range_cant_overflow (tree var, tree step, class loop *loop) if (!def_bb || !dominated_by_p (CDI_DOMINATORS, loop->latch, def_bb)) return false; - Value_Range r (TREE_TYPE (var)); + int_range<2> r (TREE_TYPE (var)); get_range_query (cfun)->range_of_expr (r, var); if (r.varying_p () || r.undefined_p ()) return false; -- 2.44.0
[COMMITTED 06/16] Remove GTY support for vrange and derived classes.
Now that we have a vrange storage class to save ranges in long-term memory, there is no need for GTY markers for any of the vrange classes, since they should never live in GC. gcc/ChangeLog: * value-range-storage.h: Remove friends. * value-range.cc (gt_ggc_mx): Remove. (gt_pch_nx): Remove. * value-range.h (class vrange): Remove GTY markers. (class irange): Same. (class int_range): Same. (class frange): Same. (gt_ggc_mx): Remove. (gt_pch_nx): Remove. --- gcc/value-range-storage.h | 4 --- gcc/value-range.cc| 73 --- gcc/value-range.h | 46 +++- 3 files changed, 4 insertions(+), 119 deletions(-) diff --git a/gcc/value-range-storage.h b/gcc/value-range-storage.h index d94c520aa73..5756de7e32d 100644 --- a/gcc/value-range-storage.h +++ b/gcc/value-range-storage.h @@ -75,10 +75,6 @@ private: static size_t size (const irange &r); const unsigned short *lengths_address () const; unsigned short *write_lengths_address (); - friend void gt_ggc_mx_irange_storage (void *); - friend void gt_pch_p_14irange_storage (void *, void *, - gt_pointer_operator, void *); - friend void gt_pch_nx_irange_storage (void *); // The shared precision of each number. unsigned short m_precision; diff --git a/gcc/value-range.cc b/gcc/value-range.cc index 926f7b707ea..b901c864a7b 100644 --- a/gcc/value-range.cc +++ b/gcc/value-range.cc @@ -2165,79 +2165,6 @@ vrp_operand_equal_p (const_tree val1, const_tree val2) return true; } -void -gt_ggc_mx (irange *x) -{ - if (!x->undefined_p ()) -gt_ggc_mx (x->m_type); -} - -void -gt_pch_nx (irange *x) -{ - if (!x->undefined_p ()) -gt_pch_nx (x->m_type); -} - -void -gt_pch_nx (irange *x, gt_pointer_operator op, void *cookie) -{ - for (unsigned i = 0; i < x->m_num_ranges; ++i) -{ - op (&x->m_base[i * 2], NULL, cookie); - op (&x->m_base[i * 2 + 1], NULL, cookie); -} -} - -void -gt_ggc_mx (frange *x) -{ - gt_ggc_mx (x->m_type); -} - -void -gt_pch_nx (frange *x) -{ - gt_pch_nx (x->m_type); -} - -void -gt_pch_nx (frange *x, gt_pointer_operator op, void *cookie) -{ - op (&x->m_type, NULL, cookie); -} - -void -gt_ggc_mx (vrange *x) -{ - if (is_a (*x)) -return gt_ggc_mx ((irange *) x); - if (is_a (*x)) -return gt_ggc_mx ((frange *) x); - gcc_unreachable (); -} - -void -gt_pch_nx (vrange *x) -{ - if (is_a (*x)) -return gt_pch_nx ((irange *) x); - if (is_a (*x)) -return gt_pch_nx ((frange *) x); - gcc_unreachable (); -} - -void -gt_pch_nx (vrange *x, gt_pointer_operator op, void *cookie) -{ - if (is_a (*x)) -gt_pch_nx ((irange *) x, op, cookie); - else if (is_a (*x)) -gt_pch_nx ((frange *) x, op, cookie); - else -gcc_unreachable (); -} - #define DEFINE_INT_RANGE_INSTANCE(N) \ template int_range::int_range(tree_node *, \ const wide_int &,\ diff --git a/gcc/value-range.h b/gcc/value-range.h index 991ffeafcb8..2650ded6d10 100644 --- a/gcc/value-range.h +++ b/gcc/value-range.h @@ -72,7 +72,7 @@ enum value_range_discriminator // if (f.supports_type_p (type)) ... //} -class GTY((user)) vrange +class vrange { template friend bool is_a (vrange &); friend class Value_Range; @@ -279,7 +279,7 @@ irange_bitmask::intersect (const irange_bitmask &orig_src) // An integer range without any storage. -class GTY((user)) irange : public vrange +class irange : public vrange { friend value_range_kind get_legacy_range (const irange &, tree &, tree &); friend class irange_storage; @@ -350,10 +350,6 @@ protected: // Hard limit on max ranges allowed. static const int HARD_MAX_RANGES = 255; private: - friend void gt_ggc_mx (irange *); - friend void gt_pch_nx (irange *); - friend void gt_pch_nx (irange *, gt_pointer_operator, void *); - bool varying_compatible_p () const; bool intersect_bitmask (const irange &r); bool union_bitmask (const irange &r); @@ -379,7 +375,7 @@ protected: // HARD_MAX_RANGES. This new storage is freed upon destruction. template -class GTY((user)) int_range : public irange +class int_range : public irange { public: int_range (); @@ -484,13 +480,10 @@ nan_state::neg_p () const // The representation is a type with a couple of endpoints, unioned // with the set of { -NAN, +Nan }. -class GTY((user)) frange : public vrange +class frange : public vrange { friend class frange_storage; friend class vrange_printer; - friend void gt_ggc_mx (frange *); - friend void gt_pch_nx (frange *); - friend void gt_pch_nx (frange *, gt_pointer_operator, void *); public: frange (); frange (const frange &); @@ -991,37 +984,6 @@ range_includes_zero_p (const irange *vr) return vr->contains_p (zero); } -extern void gt_ggc_mx (vrange *); -extern v
[COMMITTED 05/16] Move bitmask routines to vrange base class.
Any range can theoretically have a bitmask of set bits. This patch moves the bitmask accessors to the base class. This cleans up some users in IPA*, and will provide a cleaner interface when prange is in place. gcc/ChangeLog: * ipa-cp.cc (propagate_bits_across_jump_function): Access bitmask through base class. (ipcp_store_vr_results): Same. * ipa-prop.cc (ipa_compute_jump_functions_for_edge): Same. (ipcp_get_parm_bits): Same. (ipcp_update_vr): Same. * range-op-mixed.h (update_known_bitmask): Change argument to vrange. * range-op.cc (update_known_bitmask): Same. * value-range.cc (vrange::update_bitmask): New. (irange::set_nonzero_bits): Move to vrange class. (irange::get_nonzero_bits): Same. * value-range.h (class vrange): Add update_bitmask, get_bitmask, get_nonzero_bits, and set_nonzero_bits. (class irange): Make bitmask methods virtual overrides. (class Value_Range): Add get_bitmask and update_bitmask. --- gcc/ipa-cp.cc| 9 +++-- gcc/ipa-prop.cc | 10 -- gcc/range-op-mixed.h | 2 +- gcc/range-op.cc | 4 ++-- gcc/value-range.cc | 16 ++-- gcc/value-range.h| 14 +- 6 files changed, 33 insertions(+), 22 deletions(-) diff --git a/gcc/ipa-cp.cc b/gcc/ipa-cp.cc index b7add455bd5..a688dced5c9 100644 --- a/gcc/ipa-cp.cc +++ b/gcc/ipa-cp.cc @@ -2485,8 +2485,7 @@ propagate_bits_across_jump_function (cgraph_edge *cs, int idx, jfunc->m_vr->get_vrange (vr); if (!vr.undefined_p () && !vr.varying_p ()) { - irange &r = as_a (vr); - irange_bitmask bm = r.get_bitmask (); + irange_bitmask bm = vr.get_bitmask (); widest_int mask = widest_int::from (bm.mask (), TYPE_SIGN (parm_type)); widest_int value @@ -6346,14 +6345,13 @@ ipcp_store_vr_results (void) { Value_Range tmp = plats->m_value_range.m_vr; tree type = ipa_get_type (info, i); - irange &r = as_a (tmp); irange_bitmask bm (wide_int::from (bits->get_value (), TYPE_PRECISION (type), TYPE_SIGN (type)), wide_int::from (bits->get_mask (), TYPE_PRECISION (type), TYPE_SIGN (type))); - r.update_bitmask (bm); + tmp.update_bitmask (bm); ipa_vr vr (tmp); ts->m_vr->quick_push (vr); } @@ -6368,14 +6366,13 @@ ipcp_store_vr_results (void) tree type = ipa_get_type (info, i); Value_Range tmp; tmp.set_varying (type); - irange &r = as_a (tmp); irange_bitmask bm (wide_int::from (bits->get_value (), TYPE_PRECISION (type), TYPE_SIGN (type)), wide_int::from (bits->get_mask (), TYPE_PRECISION (type), TYPE_SIGN (type))); - r.update_bitmask (bm); + tmp.update_bitmask (bm); ipa_vr vr (tmp); ts->m_vr->quick_push (vr); } diff --git a/gcc/ipa-prop.cc b/gcc/ipa-prop.cc index 374e998aa64..b57f9750431 100644 --- a/gcc/ipa-prop.cc +++ b/gcc/ipa-prop.cc @@ -2381,8 +2381,7 @@ ipa_compute_jump_functions_for_edge (struct ipa_func_body_info *fbi, irange_bitmask bm (value, mask); if (!addr_nonzero) vr.set_varying (TREE_TYPE (arg)); - irange &r = as_a (vr); - r.update_bitmask (bm); + vr.update_bitmask (bm); ipa_set_jfunc_vr (jfunc, vr); } else if (addr_nonzero) @@ -5785,8 +5784,8 @@ ipcp_get_parm_bits (tree parm, tree *value, widest_int *mask) vr[i].get_vrange (tmp); if (tmp.undefined_p () || tmp.varying_p ()) return false; - irange &r = as_a (tmp); - irange_bitmask bm = r.get_bitmask (); + irange_bitmask bm; + bm = tmp.get_bitmask (); *mask = widest_int::from (bm.mask (), TYPE_SIGN (TREE_TYPE (parm))); *value = wide_int_to_tree (TREE_TYPE (parm), bm.value ()); return true; @@ -5857,8 +5856,7 @@ ipcp_update_vr (struct cgraph_node *node, ipcp_transformation *ts) if (POINTER_TYPE_P (TREE_TYPE (parm)) && opt_for_fn (node->decl, flag_ipa_bit_cp)) { - irange &r = as_a (tmp); - irange_bitmask bm = r.get_bitmask (); + irange_bitmask bm = tmp.get_bitmask (); unsigned tem = bm.mask ().to_uhwi (); unsigned HOST_WIDE_I
[COMMITTED 04/16] Add tree versions of lower and upper bounds to vrange.
This patch adds vrange::lbound() and vrange::ubound() that return trees. These can be used in generic code that is type agnostic, and avoids special casing for pointers and integers in places where we handle both. It also cleans up a wart in the Value_Range class. gcc/ChangeLog: * tree-ssa-loop-niter.cc (refine_value_range_using_guard): Convert bound to wide_int. * value-range.cc (Value_Range::lower_bound): Remove. (Value_Range::upper_bound): Remove. (unsupported_range::lbound): New. (unsupported_range::ubound): New. (frange::lbound): New. (frange::ubound): New. (irange::lbound): New. (irange::ubound): New. * value-range.h (class vrange): Add lbound() and ubound(). (class irange): Same. (class frange): Same. (class unsupported_range): Same. (class Value_Range): Rename lower_bound and upper_bound to lbound and ubound respectively. --- gcc/tree-ssa-loop-niter.cc | 4 +-- gcc/value-range.cc | 56 -- gcc/value-range.h | 13 +++-- 3 files changed, 48 insertions(+), 25 deletions(-) diff --git a/gcc/tree-ssa-loop-niter.cc b/gcc/tree-ssa-loop-niter.cc index cbc9dbc5a1f..adbc1936982 100644 --- a/gcc/tree-ssa-loop-niter.cc +++ b/gcc/tree-ssa-loop-niter.cc @@ -4067,7 +4067,7 @@ record_nonwrapping_iv (class loop *loop, tree base, tree step, gimple *stmt, Value_Range base_range (TREE_TYPE (orig_base)); if (get_range_query (cfun)->range_of_expr (base_range, orig_base) && !base_range.undefined_p ()) - max = base_range.upper_bound (); + max = wi::to_wide (base_range.ubound ()); extreme = fold_convert (unsigned_type, low); if (TREE_CODE (orig_base) == SSA_NAME && TREE_CODE (high) == INTEGER_CST @@ -4090,7 +4090,7 @@ record_nonwrapping_iv (class loop *loop, tree base, tree step, gimple *stmt, Value_Range base_range (TREE_TYPE (orig_base)); if (get_range_query (cfun)->range_of_expr (base_range, orig_base) && !base_range.undefined_p ()) - min = base_range.lower_bound (); + min = wi::to_wide (base_range.lbound ()); extreme = fold_convert (unsigned_type, high); if (TREE_CODE (orig_base) == SSA_NAME && TREE_CODE (low) == INTEGER_CST diff --git a/gcc/value-range.cc b/gcc/value-range.cc index 632d77305cc..ccac517d4c4 100644 --- a/gcc/value-range.cc +++ b/gcc/value-range.cc @@ -37,26 +37,6 @@ irange::accept (const vrange_visitor &v) const v.visit (*this); } -// Convenience function only available for integers and pointers. - -wide_int -Value_Range::lower_bound () const -{ - if (is_a (*m_vrange)) -return as_a (*m_vrange).lower_bound (); - gcc_unreachable (); -} - -// Convenience function only available for integers and pointers. - -wide_int -Value_Range::upper_bound () const -{ - if (is_a (*m_vrange)) -return as_a (*m_vrange).upper_bound (); - gcc_unreachable (); -} - void Value_Range::dump (FILE *out) const { @@ -211,6 +191,18 @@ unsupported_range::operator= (const vrange &r) return *this; } +tree +unsupported_range::lbound () const +{ + return NULL; +} + +tree +unsupported_range::ubound () const +{ + return NULL; +} + // Assignment operator for generic ranges. Copying incompatible types // is not allowed. @@ -957,6 +949,18 @@ frange::set_nonnegative (tree type) set (type, dconst0, frange_val_max (type)); } +tree +frange::lbound () const +{ + return build_real (type (), lower_bound ()); +} + +tree +frange::ubound () const +{ + return build_real (type (), upper_bound ()); +} + // Here we copy between any two irange's. irange & @@ -2086,6 +2090,18 @@ irange::union_bitmask (const irange &r) return true; } +tree +irange::lbound () const +{ + return wide_int_to_tree (type (), lower_bound ()); +} + +tree +irange::ubound () const +{ + return wide_int_to_tree (type (), upper_bound ()); +} + void irange_bitmask::verify_mask () const { diff --git a/gcc/value-range.h b/gcc/value-range.h index b7c83982385..f216f1b82c1 100644 --- a/gcc/value-range.h +++ b/gcc/value-range.h @@ -96,6 +96,8 @@ public: virtual void set_nonnegative (tree type) = 0; virtual bool fits_p (const vrange &r) const = 0; virtual ~vrange () { } + virtual tree lbound () const = 0; + virtual tree ubound () const = 0; bool varying_p () const; bool undefined_p () const; @@ -298,6 +300,8 @@ public: wide_int lower_bound (unsigned = 0) const; wide_int upper_bound (unsigned) const; wide_int upper_bound () const; + virtual tree lbound () const override; + virtual tree ubound () const override; // Predicates. virtual bool zero_p () const override; @@ -419,6 +423,8 @@ public: void set_nonnegative (tree type) final override; bool fits_p (const vrange &) const final override; unsupported_range& operator= (const vrange &r); + tree lbound () const final override; + tree ubo
[COMMITTED 11/16] Move get_bitmask_from_range out of irange class.
prange will also have bitmasks, so it will need to use get_bitmask_from_range. gcc/ChangeLog: * value-range.cc (get_bitmask_from_range): Move out of irange class. (irange::get_bitmask): Call function instead of internal method. * value-range.h (class irange): Remove get_bitmask_from_range. --- gcc/value-range.cc | 52 +++--- gcc/value-range.h | 1 - 2 files changed, 26 insertions(+), 27 deletions(-) diff --git a/gcc/value-range.cc b/gcc/value-range.cc index 44929b210aa..d9689bd469f 100644 --- a/gcc/value-range.cc +++ b/gcc/value-range.cc @@ -31,6 +31,30 @@ along with GCC; see the file COPYING3. If not see #include "fold-const.h" #include "gimple-range.h" +// Return the bitmask inherent in a range. + +static irange_bitmask +get_bitmask_from_range (tree type, + const wide_int &min, const wide_int &max) +{ + unsigned prec = TYPE_PRECISION (type); + + // All the bits of a singleton are known. + if (min == max) +{ + wide_int mask = wi::zero (prec); + wide_int value = min; + return irange_bitmask (value, mask); +} + + wide_int xorv = min ^ max; + + if (xorv != 0) +xorv = wi::mask (prec - wi::clz (xorv), false, prec); + + return irange_bitmask (wi::zero (prec), min | xorv); +} + void irange::accept (const vrange_visitor &v) const { @@ -1881,31 +1905,6 @@ irange::invert () verify_range (); } -// Return the bitmask inherent in the range. - -irange_bitmask -irange::get_bitmask_from_range () const -{ - unsigned prec = TYPE_PRECISION (type ()); - wide_int min = lower_bound (); - wide_int max = upper_bound (); - - // All the bits of a singleton are known. - if (min == max) -{ - wide_int mask = wi::zero (prec); - wide_int value = lower_bound (); - return irange_bitmask (value, mask); -} - - wide_int xorv = min ^ max; - - if (xorv != 0) -xorv = wi::mask (prec - wi::clz (xorv), false, prec); - - return irange_bitmask (wi::zero (prec), min | xorv); -} - // Remove trailing ranges that this bitmask indicates can't exist. void @@ -2027,7 +2026,8 @@ irange::get_bitmask () const // in the mask. // // See also the note in irange_bitmask::intersect. - irange_bitmask bm = get_bitmask_from_range (); + irange_bitmask bm += get_bitmask_from_range (type (), lower_bound (), upper_bound ()); if (!m_bitmask.unknown_p ()) bm.intersect (m_bitmask); return bm; diff --git a/gcc/value-range.h b/gcc/value-range.h index d2e8fd5a4d9..ede90a496d8 100644 --- a/gcc/value-range.h +++ b/gcc/value-range.h @@ -352,7 +352,6 @@ private: bool varying_compatible_p () const; bool intersect_bitmask (const irange &r); bool union_bitmask (const irange &r); - irange_bitmask get_bitmask_from_range () const; bool set_range_from_bitmask (); bool intersect (const wide_int& lb, const wide_int& ub); -- 2.44.0
[COMMITTED 02/16] Add a virtual vrange destructor.
Richi mentioned in PR113476 that it would be cleaner to move the destructor from int_range to the base class. Although this isn't strictly necessary, as there are no users, it is good to future proof things, and the overall impact is miniscule. gcc/ChangeLog: * value-range.h (vrange::~vrange): New. (int_range::~int_range): Make final override. --- gcc/value-range.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gcc/value-range.h b/gcc/value-range.h index e7f61950a24..b7c83982385 100644 --- a/gcc/value-range.h +++ b/gcc/value-range.h @@ -95,6 +95,7 @@ public: virtual void set_zero (tree type) = 0; virtual void set_nonnegative (tree type) = 0; virtual bool fits_p (const vrange &r) const = 0; + virtual ~vrange () { } bool varying_p () const; bool undefined_p () const; @@ -382,7 +383,7 @@ public: int_range (tree type); int_range (const int_range &); int_range (const irange &); - virtual ~int_range (); + ~int_range () final override; int_range& operator= (const int_range &); protected: int_range (tree, tree, value_range_kind = VR_RANGE); -- 2.44.0
[COMMITTED 09/16] Verify that reading back from vrange_storage doesn't drop bits.
We have a sanity check in the irange storage code to make sure that reading back a cache entry we have just written to yields exactly the same range. There's no need to do this only for integers. This patch moves the code to a more generic place. However, doing so tickles a latent bug in the frange code where a range is being pessimized from [0.0, 1.0] to [-0.0, 1.0]. Exclude checking frange's until this bug is fixed. gcc/ChangeLog: * value-range-storage.cc (irange_storage::set_irange): Move verification code from here... (vrange_storage::set_vrange): ...to here. --- gcc/value-range-storage.cc | 20 +--- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/gcc/value-range-storage.cc b/gcc/value-range-storage.cc index f00474ad0e6..09a29776a0e 100644 --- a/gcc/value-range-storage.cc +++ b/gcc/value-range-storage.cc @@ -165,6 +165,19 @@ vrange_storage::set_vrange (const vrange &r) } else gcc_unreachable (); + + // Verify that reading back from the cache didn't drop bits. + if (flag_checking + // FIXME: Avoid checking frange, as it currently pessimizes some ranges: + // + // gfortran.dg/pr49472.f90 pessimizes [0.0, 1.0] into [-0.0, 1.0]. + && !is_a (r) + && !r.undefined_p ()) +{ + Value_Range tmp (r); + get_vrange (tmp, r.type ()); + gcc_checking_assert (tmp == r); +} } // Restore R from storage. @@ -306,13 +319,6 @@ irange_storage::set_irange (const irange &r) irange_bitmask bm = r.m_bitmask; write_wide_int (val, len, bm.value ()); write_wide_int (val, len, bm.mask ()); - - if (flag_checking) -{ - int_range_max tmp; - get_irange (tmp, r.type ()); - gcc_checking_assert (tmp == r); -} } static inline void -- 2.44.0
[COMMITTED 14/16] Move print_irange_* out of vrange_printer class.
Move some code out of the irange pretty printers so it can be shared with pointers. gcc/ChangeLog: * value-range-pretty-print.cc (print_int_bound): New. (print_irange_bitmasks): New. (vrange_printer::print_irange_bound): Remove. (vrange_printer::print_irange_bitmasks): Remove. * value-range-pretty-print.h: Remove print_irange_bitmasks and print_irange_bound --- gcc/value-range-pretty-print.cc | 83 - gcc/value-range-pretty-print.h | 2 - 2 files changed, 41 insertions(+), 44 deletions(-) diff --git a/gcc/value-range-pretty-print.cc b/gcc/value-range-pretty-print.cc index c75cbea3955..b6d23dce6d2 100644 --- a/gcc/value-range-pretty-print.cc +++ b/gcc/value-range-pretty-print.cc @@ -30,6 +30,44 @@ along with GCC; see the file COPYING3. If not see #include "gimple-range.h" #include "value-range-pretty-print.h" +static void +print_int_bound (pretty_printer *pp, const wide_int &bound, tree type) +{ + wide_int type_min = wi::min_value (TYPE_PRECISION (type), TYPE_SIGN (type)); + wide_int type_max = wi::max_value (TYPE_PRECISION (type), TYPE_SIGN (type)); + + if (INTEGRAL_TYPE_P (type) + && !TYPE_UNSIGNED (type) + && bound == type_min + && TYPE_PRECISION (type) != 1) +pp_string (pp, "-INF"); + else if (bound == type_max && TYPE_PRECISION (type) != 1) +pp_string (pp, "+INF"); + else +pp_wide_int (pp, bound, TYPE_SIGN (type)); +} + +static void +print_irange_bitmasks (pretty_printer *pp, const irange_bitmask &bm) +{ + if (bm.unknown_p ()) +return; + + pp_string (pp, " MASK "); + char buf[WIDE_INT_PRINT_BUFFER_SIZE], *p; + unsigned len_mask, len_val; + if (print_hex_buf_size (bm.mask (), &len_mask) + | print_hex_buf_size (bm.value (), &len_val)) +p = XALLOCAVEC (char, MAX (len_mask, len_val)); + else +p = buf; + print_hex (bm.mask (), p); + pp_string (pp, p); + pp_string (pp, " VALUE "); + print_hex (bm.value (), p); + pp_string (pp, p); +} + void vrange_printer::visit (const unsupported_range &r) const { @@ -66,51 +104,12 @@ vrange_printer::visit (const irange &r) const for (unsigned i = 0; i < r.num_pairs (); ++i) { pp_character (pp, '['); - print_irange_bound (r.lower_bound (i), r.type ()); + print_int_bound (pp, r.lower_bound (i), r.type ()); pp_string (pp, ", "); - print_irange_bound (r.upper_bound (i), r.type ()); + print_int_bound (pp, r.upper_bound (i), r.type ()); pp_character (pp, ']'); } - print_irange_bitmasks (r); -} - -void -vrange_printer::print_irange_bound (const wide_int &bound, tree type) const -{ - wide_int type_min = wi::min_value (TYPE_PRECISION (type), TYPE_SIGN (type)); - wide_int type_max = wi::max_value (TYPE_PRECISION (type), TYPE_SIGN (type)); - - if (INTEGRAL_TYPE_P (type) - && !TYPE_UNSIGNED (type) - && bound == type_min - && TYPE_PRECISION (type) != 1) -pp_string (pp, "-INF"); - else if (bound == type_max && TYPE_PRECISION (type) != 1) -pp_string (pp, "+INF"); - else -pp_wide_int (pp, bound, TYPE_SIGN (type)); -} - -void -vrange_printer::print_irange_bitmasks (const irange &r) const -{ - irange_bitmask bm = r.m_bitmask; - if (bm.unknown_p ()) -return; - - pp_string (pp, " MASK "); - char buf[WIDE_INT_PRINT_BUFFER_SIZE], *p; - unsigned len_mask, len_val; - if (print_hex_buf_size (bm.mask (), &len_mask) - | print_hex_buf_size (bm.value (), &len_val)) -p = XALLOCAVEC (char, MAX (len_mask, len_val)); - else -p = buf; - print_hex (bm.mask (), p); - pp_string (pp, p); - pp_string (pp, " VALUE "); - print_hex (bm.value (), p); - pp_string (pp, p); + print_irange_bitmasks (pp, r.m_bitmask); } void diff --git a/gcc/value-range-pretty-print.h b/gcc/value-range-pretty-print.h index ca85fd6157c..44cd6e81298 100644 --- a/gcc/value-range-pretty-print.h +++ b/gcc/value-range-pretty-print.h @@ -29,8 +29,6 @@ public: void visit (const irange &) const override; void visit (const frange &) const override; private: - void print_irange_bound (const wide_int &w, tree type) const; - void print_irange_bitmasks (const irange &) const; void print_frange_nan (const frange &) const; void print_real_value (tree type, const REAL_VALUE_TYPE &r) const; -- 2.44.0
[COMMITTED 08/16] Change range_includes_zero_p argument to a reference.
Make range_includes_zero_p take an argument instead of a pointer for consistency in the range-op code. gcc/ChangeLog: * gimple-range-op.cc (cfn_clz::fold_range): Change range_includes_zero_p argument to a reference. (cfn_ctz::fold_range): Same. * range-op.cc (operator_plus::lhs_op1_relation): Same. * value-range.h (range_includes_zero_p): Same. --- gcc/gimple-range-op.cc | 6 +++--- gcc/range-op.cc| 2 +- gcc/value-range.h | 10 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/gcc/gimple-range-op.cc b/gcc/gimple-range-op.cc index a98f7db62a7..9c50c00549e 100644 --- a/gcc/gimple-range-op.cc +++ b/gcc/gimple-range-op.cc @@ -853,7 +853,7 @@ public: // __builtin_ffs* and __builtin_popcount* return [0, prec]. int prec = TYPE_PRECISION (lh.type ()); // If arg is non-zero, then ffs or popcount are non-zero. -int mini = range_includes_zero_p (&lh) ? 0 : 1; +int mini = range_includes_zero_p (lh) ? 0 : 1; int maxi = prec; // If some high bits are known to be zero, decrease the maximum. @@ -945,7 +945,7 @@ cfn_clz::fold_range (irange &r, tree type, const irange &lh, if (mini == -2) mini = 0; } - else if (!range_includes_zero_p (&lh)) + else if (!range_includes_zero_p (lh)) { mini = 0; maxi = prec - 1; @@ -1007,7 +1007,7 @@ cfn_ctz::fold_range (irange &r, tree type, const irange &lh, mini = -2; } // If arg is non-zero, then use [0, prec - 1]. - if (!range_includes_zero_p (&lh)) + if (!range_includes_zero_p (lh)) { mini = 0; maxi = prec - 1; diff --git a/gcc/range-op.cc b/gcc/range-op.cc index aeff55cfd78..6ea7d624a9b 100644 --- a/gcc/range-op.cc +++ b/gcc/range-op.cc @@ -1657,7 +1657,7 @@ operator_plus::lhs_op1_relation (const irange &lhs, } // If op2 does not contain 0, then LHS and OP1 can never be equal. - if (!range_includes_zero_p (&op2)) + if (!range_includes_zero_p (op2)) return VREL_NE; return VREL_VARYING; diff --git a/gcc/value-range.h b/gcc/value-range.h index 2650ded6d10..62f123e2a4b 100644 --- a/gcc/value-range.h +++ b/gcc/value-range.h @@ -972,16 +972,16 @@ irange::contains_p (tree cst) const } inline bool -range_includes_zero_p (const irange *vr) +range_includes_zero_p (const irange &vr) { - if (vr->undefined_p ()) + if (vr.undefined_p ()) return false; - if (vr->varying_p ()) + if (vr.varying_p ()) return true; - wide_int zero = wi::zero (TYPE_PRECISION (vr->type ())); - return vr->contains_p (zero); + wide_int zero = wi::zero (TYPE_PRECISION (vr.type ())); + return vr.contains_p (zero); } // Constructors for irange -- 2.44.0
[COMMITTED 10/16] Accept a vrange in get_legacy_range.
In preparation for prange, make get_legacy_range take a generic vrange, not just an irange. gcc/ChangeLog: * value-range.cc (get_legacy_range): Make static and add another version of get_legacy_range that takes a vrange. * value-range.h (class irange): Remove unnecessary friendship with get_legacy_range. (get_legacy_range): Accept a vrange. --- gcc/value-range.cc | 17 - gcc/value-range.h | 3 +-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/gcc/value-range.cc b/gcc/value-range.cc index b901c864a7b..44929b210aa 100644 --- a/gcc/value-range.cc +++ b/gcc/value-range.cc @@ -1004,7 +1004,7 @@ irange::operator= (const irange &src) return *this; } -value_range_kind +static value_range_kind get_legacy_range (const irange &r, tree &min, tree &max) { if (r.undefined_p ()) @@ -1041,6 +1041,21 @@ get_legacy_range (const irange &r, tree &min, tree &max) return VR_RANGE; } +// Given a range in V, return an old-style legacy range consisting of +// a value_range_kind with a MIN/MAX. This is to maintain +// compatibility with passes that still depend on VR_ANTI_RANGE, and +// only works for integers and pointers. + +value_range_kind +get_legacy_range (const vrange &v, tree &min, tree &max) +{ + if (is_a (v)) +return get_legacy_range (as_a (v), min, max); + + gcc_unreachable (); + return VR_UNDEFINED; +} + /* Set value range to the canonical form of {VRTYPE, MIN, MAX, EQUIV}. This means adjusting VRTYPE, MIN and MAX representing the case of a wrapping range with MAX < MIN covering [MIN, type_max] U [type_min, MAX] diff --git a/gcc/value-range.h b/gcc/value-range.h index 62f123e2a4b..d2e8fd5a4d9 100644 --- a/gcc/value-range.h +++ b/gcc/value-range.h @@ -281,7 +281,6 @@ irange_bitmask::intersect (const irange_bitmask &orig_src) class irange : public vrange { - friend value_range_kind get_legacy_range (const irange &, tree &, tree &); friend class irange_storage; friend class vrange_printer; public: @@ -886,7 +885,7 @@ Value_Range::supports_type_p (const_tree type) return irange::supports_p (type) || frange::supports_p (type); } -extern value_range_kind get_legacy_range (const irange &, tree &min, tree &max); +extern value_range_kind get_legacy_range (const vrange &, tree &min, tree &max); extern void dump_value_range (FILE *, const vrange *); extern bool vrp_operand_equal_p (const_tree, const_tree); inline REAL_VALUE_TYPE frange_val_min (const_tree type); -- 2.44.0
[COMMITTED 01/16] Make vrange an abstract class.
Explicitly make vrange an abstract class. This involves fleshing out the unsupported_range overrides which we were inheriting by default from vrange. gcc/ChangeLog: * value-range.cc (unsupported_range::accept): Move down. (vrange::contains_p): Rename to... (unsupported_range::contains_p): ...this. (vrange::singleton_p): Rename to... (unsupported_range::singleton_p): ...this. (vrange::set): Rename to... (unsupported_range::set): ...this. (vrange::type): Rename to... (unsupported_range::type): ...this. (vrange::supports_type_p): Rename to... (unsupported_range::supports_type_p): ...this. (vrange::set_undefined): Rename to... (unsupported_range::set_undefined): ...this. (vrange::set_varying): Rename to... (unsupported_range::set_varying): ...this. (vrange::union_): Rename to... (unsupported_range::union_): ...this. (vrange::intersect): Rename to... (unsupported_range::intersect): ...this. (vrange::zero_p): Rename to... (unsupported_range::zero_p): ...this. (vrange::nonzero_p): Rename to... (unsupported_range::nonzero_p): ...this. (vrange::set_nonzero): Rename to... (unsupported_range::set_nonzero): ...this. (vrange::set_zero): Rename to... (unsupported_range::set_zero): ...this. (vrange::set_nonnegative): Rename to... (unsupported_range::set_nonnegative): ...this. (vrange::fits_p): Rename to... (unsupported_range::fits_p): ...this. (unsupported_range::operator=): New. (frange::fits_p): New. * value-range.h (class vrange): Make an abstract class. (class unsupported_range): Declare override methods. --- gcc/value-range.cc | 62 ++ gcc/value-range.h | 53 --- 2 files changed, 73 insertions(+), 42 deletions(-) diff --git a/gcc/value-range.cc b/gcc/value-range.cc index 70375f7abf9..632d77305cc 100644 --- a/gcc/value-range.cc +++ b/gcc/value-range.cc @@ -37,12 +37,6 @@ irange::accept (const vrange_visitor &v) const v.visit (*this); } -void -unsupported_range::accept (const vrange_visitor &v) const -{ - v.visit (*this); -} - // Convenience function only available for integers and pointers. wide_int @@ -86,52 +80,58 @@ debug (const irange_bitmask &bm) fprintf (stderr, "\n"); } -// Default vrange definitions. +// Definitions for unsupported_range. + +void +unsupported_range::accept (const vrange_visitor &v) const +{ + v.visit (*this); +} bool -vrange::contains_p (tree) const +unsupported_range::contains_p (tree) const { return varying_p (); } bool -vrange::singleton_p (tree *) const +unsupported_range::singleton_p (tree *) const { return false; } void -vrange::set (tree min, tree, value_range_kind) +unsupported_range::set (tree min, tree, value_range_kind) { set_varying (TREE_TYPE (min)); } tree -vrange::type () const +unsupported_range::type () const { return void_type_node; } bool -vrange::supports_type_p (const_tree) const +unsupported_range::supports_type_p (const_tree) const { return false; } void -vrange::set_undefined () +unsupported_range::set_undefined () { m_kind = VR_UNDEFINED; } void -vrange::set_varying (tree) +unsupported_range::set_varying (tree) { m_kind = VR_VARYING; } bool -vrange::union_ (const vrange &r) +unsupported_range::union_ (const vrange &r) { if (r.undefined_p () || varying_p ()) return false; @@ -145,7 +145,7 @@ vrange::union_ (const vrange &r) } bool -vrange::intersect (const vrange &r) +unsupported_range::intersect (const vrange &r) { if (undefined_p () || r.varying_p ()) return false; @@ -164,41 +164,53 @@ vrange::intersect (const vrange &r) } bool -vrange::zero_p () const +unsupported_range::zero_p () const { return false; } bool -vrange::nonzero_p () const +unsupported_range::nonzero_p () const { return false; } void -vrange::set_nonzero (tree type) +unsupported_range::set_nonzero (tree type) { set_varying (type); } void -vrange::set_zero (tree type) +unsupported_range::set_zero (tree type) { set_varying (type); } void -vrange::set_nonnegative (tree type) +unsupported_range::set_nonnegative (tree type) { set_varying (type); } bool -vrange::fits_p (const vrange &) const +unsupported_range::fits_p (const vrange &) const { return true; } +unsupported_range & +unsupported_range::operator= (const vrange &r) +{ + if (r.undefined_p ()) +set_undefined (); + else if (r.varying_p ()) +set_varying (void_type_node); + else +gcc_unreachable (); + return *this; +} + // Assignment operator for generic ranges. Copying incompatible types // is not allowed. @@ -359,6 +371,12 @@ frange::accept (const vrange_visitor &v) const v.visit (*this); } +bool +frange::fits_p (con
[COMMITTED 13/16] Accept any vrange in range_includes_zero_p.
Accept a vrange, as this will be used for either integers or pointers. gcc/ChangeLog: * value-range.h (range_includes_zero_p): Accept vrange. --- gcc/value-range.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/gcc/value-range.h b/gcc/value-range.h index ede90a496d8..0ab717697f0 100644 --- a/gcc/value-range.h +++ b/gcc/value-range.h @@ -970,7 +970,7 @@ irange::contains_p (tree cst) const } inline bool -range_includes_zero_p (const irange &vr) +range_includes_zero_p (const vrange &vr) { if (vr.undefined_p ()) return false; @@ -978,8 +978,7 @@ range_includes_zero_p (const irange &vr) if (vr.varying_p ()) return true; - wide_int zero = wi::zero (TYPE_PRECISION (vr.type ())); - return vr.contains_p (zero); + return vr.contains_p (build_zero_cst (vr.type ())); } // Constructors for irange -- 2.44.0
[COMMITTED 15/16] Remove range_zero and range_nonzero.
Remove legacy range_zero and range_nonzero as they return by value, which make it not work in a separate irange and prange world. Also, we already have set_zero and set_nonzero methods in vrange. gcc/ChangeLog: * range-op-ptr.cc (pointer_plus_operator::wi_fold): Use method range setters instead of out of line functions. (pointer_min_max_operator::wi_fold): Same. (pointer_and_operator::wi_fold): Same. (pointer_or_operator::wi_fold): Same. * range-op.cc (operator_negate::fold_range): Same. (operator_addr_expr::fold_range): Same. (range_op_cast_tests): Same. * range.cc (range_zero): Remove. (range_nonzero): Remove. * range.h (range_zero): Remove. (range_nonzero): Remove. * value-range.cc (range_tests_misc): Use method instead of out of line function. --- gcc/range-op-ptr.cc | 14 +++--- gcc/range-op.cc | 14 -- gcc/range.cc| 14 -- gcc/range.h | 2 -- gcc/value-range.cc | 7 --- 5 files changed, 19 insertions(+), 32 deletions(-) diff --git a/gcc/range-op-ptr.cc b/gcc/range-op-ptr.cc index 2c85d75b5e8..7343ef635f3 100644 --- a/gcc/range-op-ptr.cc +++ b/gcc/range-op-ptr.cc @@ -101,10 +101,10 @@ pointer_plus_operator::wi_fold (irange &r, tree type, && !TYPE_OVERFLOW_WRAPS (type) && (flag_delete_null_pointer_checks || !wi::sign_mask (rh_ub))) -r = range_nonzero (type); +r.set_nonzero (type); else if (lh_lb == lh_ub && lh_lb == 0 && rh_lb == rh_ub && rh_lb == 0) -r = range_zero (type); +r.set_zero (type); else r.set_varying (type); } @@ -150,9 +150,9 @@ pointer_min_max_operator::wi_fold (irange &r, tree type, // are varying. if (!wi_includes_zero_p (type, lh_lb, lh_ub) && !wi_includes_zero_p (type, rh_lb, rh_ub)) -r = range_nonzero (type); +r.set_nonzero (type); else if (wi_zero_p (type, lh_lb, lh_ub) && wi_zero_p (type, rh_lb, rh_ub)) -r = range_zero (type); +r.set_zero (type); else r.set_varying (type); } @@ -175,7 +175,7 @@ pointer_and_operator::wi_fold (irange &r, tree type, // For pointer types, we are really only interested in asserting // whether the expression evaluates to non-NULL. if (wi_zero_p (type, lh_lb, lh_ub) || wi_zero_p (type, lh_lb, lh_ub)) -r = range_zero (type); +r.set_zero (type); else r.set_varying (type); } @@ -236,9 +236,9 @@ pointer_or_operator::wi_fold (irange &r, tree type, // whether the expression evaluates to non-NULL. if (!wi_includes_zero_p (type, lh_lb, lh_ub) && !wi_includes_zero_p (type, rh_lb, rh_ub)) -r = range_nonzero (type); +r.set_nonzero (type); else if (wi_zero_p (type, lh_lb, lh_ub) && wi_zero_p (type, rh_lb, rh_ub)) -r = range_zero (type); +r.set_zero (type); else r.set_varying (type); } diff --git a/gcc/range-op.cc b/gcc/range-op.cc index 6ea7d624a9b..ab3a4f0b200 100644 --- a/gcc/range-op.cc +++ b/gcc/range-op.cc @@ -4364,9 +4364,11 @@ operator_negate::fold_range (irange &r, tree type, { if (empty_range_varying (r, type, lh, rh)) return true; - // -X is simply 0 - X. - return range_op_handler (MINUS_EXPR).fold_range (r, type, - range_zero (type), lh); + +// -X is simply 0 - X. + int_range<1> zero; + zero.set_zero (type); + return range_op_handler (MINUS_EXPR).fold_range (r, type, zero, lh); } bool @@ -4391,7 +4393,7 @@ operator_addr_expr::fold_range (irange &r, tree type, // Return a non-null pointer of the LHS type (passed in op2). if (lh.zero_p ()) -r = range_zero (type); +r.set_zero (type); else if (lh.undefined_p () || contains_zero_p (lh)) r.set_varying (type); else @@ -4675,7 +4677,7 @@ range_op_cast_tests () if (TYPE_PRECISION (integer_type_node) > TYPE_PRECISION (short_integer_type_node)) { - r0 = range_nonzero (integer_type_node); + r0.set_nonzero (integer_type_node); range_cast (r0, short_integer_type_node); r1 = int_range<1> (short_integer_type_node, min_limit (short_integer_type_node), @@ -4687,7 +4689,7 @@ range_op_cast_tests () // // NONZERO signed 16-bits is [-MIN_16,-1][1, +MAX_16]. // Converting this to 32-bits signed is [-MIN_16,-1][1, +MAX_16]. - r0 = range_nonzero (short_integer_type_node); + r0.set_nonzero (short_integer_type_node); range_cast (r0, integer_type_node); r1 = int_range<1> (integer_type_node, INT (-32768), INT (-1)); r2 = int_range<1> (integer_type_node, INT (1), INT (32767)); diff --git a/gcc/range.cc b/gcc/range.cc index c68f387f71c..b362e0f12e0 100644 --- a/gcc/range.cc +++ b/gcc/range.cc @@ -29,20 +29,6 @@ along with GCC; see the file COPYING3. If not see #include "ssa.h" #include "range.h" -value_range -range_zero (tree type) -{ - wide_int zero = wi::zero (TYPE_PRECISION (type)); - return value_range (type, ze
[COMMITTED 12/16] Make some integer specific ranges generic Value_Range's.
There are some irange uses that should be Value_Range, because they can be either integers or pointers. This will become a problem when prange comes live. gcc/ChangeLog: * tree-ssa-loop-split.cc (split_at_bb_p): Make int_range a Value_Range. * tree-ssa-strlen.cc (get_range): Same. * value-query.cc (range_query::get_tree_range): Handle both integers and pointers. * vr-values.cc (simplify_using_ranges::fold_cond_with_ops): Make r0 and r1 Value_Range's. --- gcc/tree-ssa-loop-split.cc | 6 +++--- gcc/tree-ssa-strlen.cc | 2 +- gcc/value-query.cc | 4 +--- gcc/vr-values.cc | 3 ++- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/gcc/tree-ssa-loop-split.cc b/gcc/tree-ssa-loop-split.cc index a770ea371a2..a6be0cef7b0 100644 --- a/gcc/tree-ssa-loop-split.cc +++ b/gcc/tree-ssa-loop-split.cc @@ -144,18 +144,18 @@ split_at_bb_p (class loop *loop, basic_block bb, tree *border, affine_iv *iv, value range. */ else { - int_range<2> r; + Value_Range r (TREE_TYPE (op0)); get_global_range_query ()->range_of_expr (r, op0, stmt); if (!r.varying_p () && !r.undefined_p () && TREE_CODE (op1) == INTEGER_CST) { wide_int val = wi::to_wide (op1); - if (known_eq (val, r.lower_bound ())) + if (known_eq (val, wi::to_wide (r.lbound ( { code = (code == EQ_EXPR) ? LE_EXPR : GT_EXPR; break; } - else if (known_eq (val, r.upper_bound ())) + else if (known_eq (val, wi::to_wide (r.ubound ( { code = (code == EQ_EXPR) ? GE_EXPR : LT_EXPR; break; diff --git a/gcc/tree-ssa-strlen.cc b/gcc/tree-ssa-strlen.cc index e09c9cc081f..61c3da22322 100644 --- a/gcc/tree-ssa-strlen.cc +++ b/gcc/tree-ssa-strlen.cc @@ -215,7 +215,7 @@ get_range (tree val, gimple *stmt, wide_int minmax[2], rvals = get_range_query (cfun); } - value_range vr; + Value_Range vr (TREE_TYPE (val)); if (!rvals->range_of_expr (vr, val, stmt)) return NULL_TREE; diff --git a/gcc/value-query.cc b/gcc/value-query.cc index eda71dc89d3..052b7511565 100644 --- a/gcc/value-query.cc +++ b/gcc/value-query.cc @@ -156,11 +156,9 @@ range_query::get_tree_range (vrange &r, tree expr, gimple *stmt) { case INTEGER_CST: { - irange &i = as_a (r); if (TREE_OVERFLOW_P (expr)) expr = drop_tree_overflow (expr); - wide_int w = wi::to_wide (expr); - i.set (TREE_TYPE (expr), w, w); + r.set (expr, expr); return true; } diff --git a/gcc/vr-values.cc b/gcc/vr-values.cc index ff68d40c355..0572bf6c8c7 100644 --- a/gcc/vr-values.cc +++ b/gcc/vr-values.cc @@ -310,7 +310,8 @@ tree simplify_using_ranges::fold_cond_with_ops (enum tree_code code, tree op0, tree op1, gimple *s) { - int_range_max r0, r1; + Value_Range r0 (TREE_TYPE (op0)); + Value_Range r1 (TREE_TYPE (op1)); if (!query->range_of_expr (r0, op0, s) || !query->range_of_expr (r1, op1, s)) return NULL_TREE; -- 2.44.0
[COMMITTED 07/16] Make fold_cond_with_ops use a boolean type for range_true/range_false.
Conditional operators are always boolean, regardless of their operands. Getting the type wrong is not currently a problem, but will be when prange's can no longer store an integer. gcc/ChangeLog: * vr-values.cc (simplify_using_ranges::fold_cond_with_ops): Remove type from range_true and range_false. --- gcc/vr-values.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gcc/vr-values.cc b/gcc/vr-values.cc index a7e291a16e5..ff68d40c355 100644 --- a/gcc/vr-values.cc +++ b/gcc/vr-values.cc @@ -320,9 +320,9 @@ simplify_using_ranges::fold_cond_with_ops (enum tree_code code, range_op_handler handler (code); if (handler && handler.fold_range (res, type, r0, r1)) { - if (res == range_true (type)) + if (res == range_true ()) return boolean_true_node; - if (res == range_false (type)) + if (res == range_false ()) return boolean_false_node; } return NULL; -- 2.44.0
[COMMITTED 16/16] Callers of irange_bitmask must normalize value/mask pairs.
As per the documentation, irange_bitmask must have the unknown bits in the mask set to 0 in the value field. Even though we say we must have normalized value/mask bits, we don't enforce it, opting to normalize on the fly in union and intersect. Avoiding this lazy enforcing as well as the extra saving/restoring involved in returning the changed status, gives us a performance increase of 1.25% for VRP and 1.51% for ipa-CP. gcc/ChangeLog: * tree-ssa-ccp.cc (ccp_finalize): Normalize before calling set_bitmask. * value-range.cc (irange::intersect_bitmask): Calculate changed irange_bitmask bits on our own. (irange::union_bitmask): Same. (irange_bitmask::verify_mask): Verify that bits are normalized. * value-range.h (irange_bitmask::union_): Do not normalize. Remove return value. (irange_bitmask::intersect): Same. --- gcc/tree-ssa-ccp.cc | 1 + gcc/value-range.cc | 7 +-- gcc/value-range.h | 24 ++-- 3 files changed, 12 insertions(+), 20 deletions(-) diff --git a/gcc/tree-ssa-ccp.cc b/gcc/tree-ssa-ccp.cc index f6a5cd0ee6e..3749126b5f7 100644 --- a/gcc/tree-ssa-ccp.cc +++ b/gcc/tree-ssa-ccp.cc @@ -1024,6 +1024,7 @@ ccp_finalize (bool nonzero_p) unsigned int precision = TYPE_PRECISION (TREE_TYPE (val->value)); wide_int value = wi::to_wide (val->value); wide_int mask = wide_int::from (val->mask, precision, UNSIGNED); + value = value & ~mask; set_bitmask (name, value, mask); } } diff --git a/gcc/value-range.cc b/gcc/value-range.cc index a27de5534e1..ca6d521c625 100644 --- a/gcc/value-range.cc +++ b/gcc/value-range.cc @@ -2067,7 +2067,8 @@ irange::intersect_bitmask (const irange &r) irange_bitmask bm = get_bitmask (); irange_bitmask save = bm; - if (!bm.intersect (r.get_bitmask ())) + bm.intersect (r.get_bitmask ()); + if (save == bm) return false; m_bitmask = bm; @@ -2099,7 +2100,8 @@ irange::union_bitmask (const irange &r) irange_bitmask bm = get_bitmask (); irange_bitmask save = bm; - if (!bm.union_ (r.get_bitmask ())) + bm.union_ (r.get_bitmask ()); + if (save == bm) return false; m_bitmask = bm; @@ -2133,6 +2135,7 @@ void irange_bitmask::verify_mask () const { gcc_assert (m_value.get_precision () == m_mask.get_precision ()); + gcc_checking_assert (wi::bit_and (m_mask, m_value) == 0); } void diff --git a/gcc/value-range.h b/gcc/value-range.h index 0ab717697f0..11c73faca1b 100644 --- a/gcc/value-range.h +++ b/gcc/value-range.h @@ -139,8 +139,8 @@ public: void set_unknown (unsigned prec); bool unknown_p () const; unsigned get_precision () const; - bool union_ (const irange_bitmask &src); - bool intersect (const irange_bitmask &src); + void union_ (const irange_bitmask &src); + void intersect (const irange_bitmask &src); bool operator== (const irange_bitmask &src) const; bool operator!= (const irange_bitmask &src) const { return !(*this == src); } void verify_mask () const; @@ -233,29 +233,18 @@ irange_bitmask::operator== (const irange_bitmask &src) const return m_value == src.m_value && m_mask == src.m_mask; } -inline bool -irange_bitmask::union_ (const irange_bitmask &orig_src) +inline void +irange_bitmask::union_ (const irange_bitmask &src) { - // Normalize mask. - irange_bitmask src (orig_src.m_value & ~orig_src.m_mask, orig_src.m_mask); - m_value &= ~m_mask; - - irange_bitmask save (*this); m_mask = (m_mask | src.m_mask) | (m_value ^ src.m_value); m_value = m_value & src.m_value; if (flag_checking) verify_mask (); - return *this != save; } -inline bool -irange_bitmask::intersect (const irange_bitmask &orig_src) +inline void +irange_bitmask::intersect (const irange_bitmask &src) { - // Normalize mask. - irange_bitmask src (orig_src.m_value & ~orig_src.m_mask, orig_src.m_mask); - m_value &= ~m_mask; - - irange_bitmask save (*this); // If we have two known bits that are incompatible, the resulting // bit is undefined. It is unclear whether we should set the entire // range to UNDEFINED, or just a subset of it. For now, set the @@ -274,7 +263,6 @@ irange_bitmask::intersect (const irange_bitmask &orig_src) } if (flag_checking) verify_mask (); - return *this != save; } // An integer range without any storage. -- 2.44.0
[PATCH 00/16] prange supporting patchset
In this cycle, we will be contributing ranges for pointers (prange), to disambiguate pointers from integers in a range. Initially they will behave exactly as they do now, with just two integer end points and a bitmask, but eventually we will track points-to info in a less hacky manner than what we do with the pointer equivalency class (pointer_equiv_analyzer). This first set of patches implements a bunch of little cleanups and set-ups that will make it easier to drop in prange in a week or two. The patches in this set are non-intrusive, and don't touch code that changes much in the release, so they should be safe to push now. There should be no change in behavior in any of these patches. All patches have been tested on x86-64 Linux. Aldy Hernandez (16): Make vrange an abstract class. Add a virtual vrange destructor. Make some Value_Range's explicitly integer. Add tree versions of lower and upper bounds to vrange. Move bitmask routines to vrange base class. Remove GTY support for vrange and derived classes. Make fold_cond_with_ops use a boolean type for range_true/range_false. Change range_includes_zero_p argument to a reference. Verify that reading back from vrange_storage doesn't drop bits. Accept a vrange in get_legacy_range. Move get_bitmask_from_range out of irange class. Make some integer specific ranges generic Value_Range's. Accept any vrange in range_includes_zero_p. Move print_irange_* out of vrange_printer class. Remove range_zero and range_nonzero. Callers of irange_bitmask must normalize value/mask pairs. gcc/gimple-range-op.cc | 6 +- gcc/gimple-ssa-warn-access.cc | 4 +- gcc/ipa-cp.cc | 9 +- gcc/ipa-prop.cc | 10 +- gcc/range-op-mixed.h| 2 +- gcc/range-op-ptr.cc | 14 +- gcc/range-op.cc | 20 ++- gcc/range.cc| 14 -- gcc/range.h | 2 - gcc/tree-ssa-ccp.cc | 1 + gcc/tree-ssa-loop-niter.cc | 16 +- gcc/tree-ssa-loop-split.cc | 6 +- gcc/tree-ssa-strlen.cc | 2 +- gcc/value-query.cc | 4 +- gcc/value-range-pretty-print.cc | 83 +- gcc/value-range-pretty-print.h | 2 - gcc/value-range-storage.cc | 20 ++- gcc/value-range-storage.h | 4 - gcc/value-range.cc | 284 gcc/value-range.h | 166 --- gcc/vr-values.cc| 7 +- 21 files changed, 310 insertions(+), 366 deletions(-) -- 2.44.0
[PATCH] Minor range type fixes for IPA in preparation for prange.
The polymorphic Value_Range object takes a tree type at construction so it can determine what type of range to use (currently irange or frange). It seems a few of the types are slightly off. This isn't a problem now, because IPA only cares about integers and pointers, which can both live in an irange. However, with prange coming about, we need to get the type right, because you can't store an integer in a pointer range or vice versa. Also, in preparation for prange, the irange::supports_p() idiom will become: irange::supports_p () || prange::supports_p() To avoid changing all these palces, I've added an inline function we can later change and change everything at once. Finally, there's a Value_Range::supports_type_p() && irange::supports_p() in the code. The latter is a subset of the former, so there's no need to check both. OK for trunk? gcc/ChangeLog: * ipa-cp.cc (ipa_vr_operation_and_type_effects): Use ipa_supports_p. (ipa_value_range_from_jfunc): Change Value_Range type. (propagate_vr_across_jump_function): Same. * ipa-cp.h (ipa_supports_p): New. * ipa-fnsummary.cc (evaluate_conditions_for_known_args): Change Value_Range type. * ipa-prop.cc (ipa_compute_jump_functions_for_edge): Use ipa_supports_p. (ipcp_get_parm_bits): Same. --- gcc/ipa-cp.cc| 14 +++--- gcc/ipa-cp.h | 8 gcc/ipa-fnsummary.cc | 2 +- gcc/ipa-prop.cc | 8 +++- 4 files changed, 19 insertions(+), 13 deletions(-) diff --git a/gcc/ipa-cp.cc b/gcc/ipa-cp.cc index a688dced5c9..5781f50c854 100644 --- a/gcc/ipa-cp.cc +++ b/gcc/ipa-cp.cc @@ -1649,7 +1649,7 @@ ipa_vr_operation_and_type_effects (vrange &dst_vr, enum tree_code operation, tree dst_type, tree src_type) { - if (!irange::supports_p (dst_type) || !irange::supports_p (src_type)) + if (!ipa_supports_p (dst_type) || !ipa_supports_p (src_type)) return false; range_op_handler handler (operation); @@ -1720,7 +1720,7 @@ ipa_value_range_from_jfunc (vrange &vr, if (TREE_CODE_CLASS (operation) == tcc_unary) { - Value_Range res (vr_type); + Value_Range res (parm_type); if (ipa_vr_operation_and_type_effects (res, srcvr, @@ -1733,7 +1733,7 @@ ipa_value_range_from_jfunc (vrange &vr, Value_Range op_res (vr_type); Value_Range res (vr_type); tree op = ipa_get_jf_pass_through_operand (jfunc); - Value_Range op_vr (vr_type); + Value_Range op_vr (TREE_TYPE (op)); range_op_handler handler (operation); ipa_range_set_and_normalize (op_vr, op); @@ -2527,7 +2527,7 @@ propagate_vr_across_jump_function (cgraph_edge *cs, ipa_jump_func *jfunc, if (src_lats->m_value_range.bottom_p ()) return dest_lat->set_to_bottom (); - Value_Range vr (operand_type); + Value_Range vr (param_type); if (TREE_CODE_CLASS (operation) == tcc_unary) ipa_vr_operation_and_type_effects (vr, src_lats->m_value_range.m_vr, @@ -2540,16 +2540,16 @@ propagate_vr_across_jump_function (cgraph_edge *cs, ipa_jump_func *jfunc, { tree op = ipa_get_jf_pass_through_operand (jfunc); Value_Range op_vr (TREE_TYPE (op)); - Value_Range op_res (operand_type); + Value_Range op_res (param_type); range_op_handler handler (operation); ipa_range_set_and_normalize (op_vr, op); if (!handler - || !op_res.supports_type_p (operand_type) + || !ipa_supports_p (operand_type) || !handler.fold_range (op_res, operand_type, src_lats->m_value_range.m_vr, op_vr)) - op_res.set_varying (operand_type); + op_res.set_varying (param_type); ipa_vr_operation_and_type_effects (vr, op_res, diff --git a/gcc/ipa-cp.h b/gcc/ipa-cp.h index 7ff74fb5c98..abeaaa4053e 100644 --- a/gcc/ipa-cp.h +++ b/gcc/ipa-cp.h @@ -291,4 +291,12 @@ public: bool values_equal_for_ipcp_p (tree x, tree y); +/* Return TRUE if IPA supports ranges of TYPE. */ + +static inline bool +ipa_supports_p (tree type) +{ + return irange::supports_p (type); +} + #endif /* IPA_CP_H */ diff --git a/gcc/ipa-fnsummary.cc b/gcc/ipa-fnsummary.cc index dff40cd8aa5..1dbf5278149 100644 --- a/gcc/ipa-fnsummary.cc +++ b/gcc/ipa-fnsummary.cc @@ -515,7 +515,7 @@ evaluate_conditions_for_known_args (struct cgraph_node *node, } else if (!op->val[1]) { - Value_Range op0 (op->type); + Value_Range op0 (TREE_TYPE (op->val[0])); range_op_handler handler (op->code); ipa_range_set_and_normalize (op0, op->val[0]); diff --git a/gcc/ip
Re: [COMMITTED 03/16] Make some Value_Range's explicitly integer.
Ughhh, you're right. Thanks for spotting this. I'm testing the attached patch and will commit if it passes tests. Aldy On Tue, Apr 30, 2024 at 9:46 AM Richard Biener wrote: > > On Sun, Apr 28, 2024 at 9:07 PM Aldy Hernandez wrote: > > > > Fix some Value_Range's that we know ahead of time will be only > > integers. This avoids using the polymorphic Value_Range unnecessarily > > But isn't Value_Range a variable-size irange but int_range<2> doesn't > support more than two sub-ranges? > > So it doesn't look obvious that this isn't actually a regression? > > Richard. > > > gcc/ChangeLog: > > > > * gimple-ssa-warn-access.cc (check_nul_terminated_array): Make > > Value_Range an int_range. > > (memmodel_to_uhwi): Same > > * tree-ssa-loop-niter.cc (refine_value_range_using_guard): Same. > > (determine_value_range): Same. > > (infer_loop_bounds_from_signedness): Same. > > (scev_var_range_cant_overflow): Same. > > --- > > gcc/gimple-ssa-warn-access.cc | 4 ++-- > > gcc/tree-ssa-loop-niter.cc| 12 ++-- > > 2 files changed, 8 insertions(+), 8 deletions(-) > > > > diff --git a/gcc/gimple-ssa-warn-access.cc b/gcc/gimple-ssa-warn-access.cc > > index dedaae27b31..450c1caa765 100644 > > --- a/gcc/gimple-ssa-warn-access.cc > > +++ b/gcc/gimple-ssa-warn-access.cc > > @@ -330,7 +330,7 @@ check_nul_terminated_array (GimpleOrTree expr, tree > > src, tree bound) > >wide_int bndrng[2]; > >if (bound) > > { > > - Value_Range r (TREE_TYPE (bound)); > > + int_range<2> r (TREE_TYPE (bound)); > > > >get_range_query (cfun)->range_of_expr (r, bound); > > > > @@ -2816,7 +2816,7 @@ memmodel_to_uhwi (tree ord, gimple *stmt, unsigned > > HOST_WIDE_INT *cstval) > > { > >/* Use the range query to determine constant values in the absence > > of constant propagation (such as at -O0). */ > > - Value_Range rng (TREE_TYPE (ord)); > > + int_range<2> rng (TREE_TYPE (ord)); > >if (!get_range_query (cfun)->range_of_expr (rng, ord, stmt) > > || !rng.singleton_p (&ord)) > > return false; > > diff --git a/gcc/tree-ssa-loop-niter.cc b/gcc/tree-ssa-loop-niter.cc > > index c6d010f6d89..cbc9dbc5a1f 100644 > > --- a/gcc/tree-ssa-loop-niter.cc > > +++ b/gcc/tree-ssa-loop-niter.cc > > @@ -214,7 +214,7 @@ refine_value_range_using_guard (tree type, tree var, > >get_type_static_bounds (type, mint, maxt); > >mpz_init (minc1); > >mpz_init (maxc1); > > - Value_Range r (TREE_TYPE (varc1)); > > + int_range<2> r (TREE_TYPE (varc1)); > >/* Setup range information for varc1. */ > >if (integer_zerop (varc1)) > > { > > @@ -368,7 +368,7 @@ determine_value_range (class loop *loop, tree type, > > tree var, mpz_t off, > >gphi_iterator gsi; > > > >/* Either for VAR itself... */ > > - Value_Range var_range (TREE_TYPE (var)); > > + int_range<2> var_range (TREE_TYPE (var)); > >get_range_query (cfun)->range_of_expr (var_range, var); > >if (var_range.varying_p () || var_range.undefined_p ()) > > rtype = VR_VARYING; > > @@ -382,7 +382,7 @@ determine_value_range (class loop *loop, tree type, > > tree var, mpz_t off, > > > >/* Or for PHI results in loop->header where VAR is used as > > PHI argument from the loop preheader edge. */ > > - Value_Range phi_range (TREE_TYPE (var)); > > + int_range<2> phi_range (TREE_TYPE (var)); > >for (gsi = gsi_start_phis (loop->header); !gsi_end_p (gsi); gsi_next > > (&gsi)) > > { > > gphi *phi = gsi.phi (); > > @@ -408,7 +408,7 @@ determine_value_range (class loop *loop, tree type, > > tree var, mpz_t off, > > involved. */ > > if (wi::gt_p (minv, maxv, sgn)) > > { > > - Value_Range vr (TREE_TYPE (var)); > > + int_range<2> vr (TREE_TYPE (var)); > > get_range_query (cfun)->range_of_expr (vr, var); > > if (vr.varying_p () || vr.undefined_p ()) > > rtype = VR_VARYING; > > @@ -4367,7 +4367,7 @@ infer_loop_bounds_from_signedness (class loop *loop, > > gimple *stmt) > > > >low = lower_bound_in_type (type, type); > >hi
[COMMITTED] Cleanups to unsupported_range.
Here are some cleanups to unsupported_range so the assignment operator takes an unsupported_range and behaves like the other ranges. This makes subsequent cleanups easier. gcc/ChangeLog: * value-range.cc (unsupported_range::union_): Cast vrange to unsupported_range. (unsupported_range::intersect): Same. (unsupported_range::operator=): Make argument an unsupported_range. * value-range.h: New constructor. --- gcc/value-range.cc | 10 +++--- gcc/value-range.h | 7 ++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/gcc/value-range.cc b/gcc/value-range.cc index ca6d521c625..7250115261f 100644 --- a/gcc/value-range.cc +++ b/gcc/value-range.cc @@ -147,8 +147,10 @@ unsupported_range::set_varying (tree) } bool -unsupported_range::union_ (const vrange &r) +unsupported_range::union_ (const vrange &v) { + const unsupported_range &r = as_a (v); + if (r.undefined_p () || varying_p ()) return false; if (undefined_p () || r.varying_p ()) @@ -161,8 +163,10 @@ unsupported_range::union_ (const vrange &r) } bool -unsupported_range::intersect (const vrange &r) +unsupported_range::intersect (const vrange &v) { + const unsupported_range &r = as_a (v); + if (undefined_p () || r.varying_p ()) return false; if (r.undefined_p ()) @@ -216,7 +220,7 @@ unsupported_range::fits_p (const vrange &) const } unsupported_range & -unsupported_range::operator= (const vrange &r) +unsupported_range::operator= (const unsupported_range &r) { if (r.undefined_p ()) set_undefined (); diff --git a/gcc/value-range.h b/gcc/value-range.h index 11c73faca1b..471f362f388 100644 --- a/gcc/value-range.h +++ b/gcc/value-range.h @@ -389,6 +389,11 @@ public: { set_undefined (); } + unsupported_range (const unsupported_range &src) +: vrange (VR_UNKNOWN) + { +unsupported_range::operator= (src); + } void set (tree min, tree, value_range_kind = VR_RANGE) final override; tree type () const final override; bool supports_type_p (const_tree) const final override; @@ -405,7 +410,7 @@ public: void set_zero (tree type) final override; void set_nonnegative (tree type) final override; bool fits_p (const vrange &) const final override; - unsupported_range& operator= (const vrange &r); + unsupported_range& operator= (const unsupported_range &r); tree lbound () const final override; tree ubound () const final override; }; -- 2.44.0
[COMMITTED] Reduce startup costs for Value_Range.
Value_Range is our polymorphic temporary that can hold any range. It is used for type agnostic code where it isn't known ahead of time, what the type of the range will be (irange, france, etc). Currently, there is a temporary of each type in the object, which means we need to construct each range for every temporary. This isn't scaling well now that prange is about to add yet another range type. This patch removes each range, opting to use in-place new for a byte buffer sufficiently large to hold ranges of any type. It reduces the memory footprint by 14% for every Value_Range temporary (from 792 to 680 bytes), and we are guaranteed it will never again grow as we add more range types (strings, complex numbers, etc). Surprisingly, it improves VRP performance by 6.61% and overall compilation by 0.44%, which is a lot more than we bargained for when we started working on prange performance. There is a slight change in semantics for Value_Range. The default constructor does not initialize the object at all. It must be manually initialized with either Value_Range::set_type(), or by assigning a range to it. This means that IPA's m_known_value_ranges must be initialized at allocation, instead of depending on the empty constructor to initialize it to VR_UNDEFINED for unsupported_range. I have taken the time to properly document both the class, and each method. If anything isn't clear, please let me know so I can adjust it accordingly. gcc/ChangeLog: * ipa-fnsummary.cc (evaluate_properties_for_edge): Initialize Value_Range's. * value-range.h (class Value_Range): Add a buffer and remove m_irange and m_frange. (Value_Range::Value_Range): Call init. (Value_Range::set_type): Same. (Value_Range::init): Use in place new to initialize buffer. (Value_Range::operator=): Tidy. --- gcc/ipa-fnsummary.cc | 8 ++- gcc/value-range.h| 127 --- 2 files changed, 76 insertions(+), 59 deletions(-) diff --git a/gcc/ipa-fnsummary.cc b/gcc/ipa-fnsummary.cc index dff40cd8aa5..668a01ef175 100644 --- a/gcc/ipa-fnsummary.cc +++ b/gcc/ipa-fnsummary.cc @@ -681,8 +681,12 @@ evaluate_properties_for_edge (struct cgraph_edge *e, bool inline_p, if (!vr.undefined_p () && !vr.varying_p ()) { if (!avals->m_known_value_ranges.length ()) - avals->m_known_value_ranges.safe_grow_cleared (count, -true); + { + avals->m_known_value_ranges.safe_grow_cleared (count, + true); + for (int i = 0; i < count; ++i) + avals->m_known_value_ranges[i].set_type (void_type_node); + } avals->m_known_value_ranges[i] = vr; } } diff --git a/gcc/value-range.h b/gcc/value-range.h index 471f362f388..f1c638f8cd0 100644 --- a/gcc/value-range.h +++ b/gcc/value-range.h @@ -684,6 +684,16 @@ typedef int_range<2> value_range; // This is an "infinite" precision range object for use in temporary // calculations for any of the handled types. The object can be // transparently used as a vrange. +// +// Using any of the various constructors initializes the object +// appropriately, but the default constructor is uninitialized and +// must be initialized either with set_type() or by assigning into it. +// +// Assigning between incompatible types is allowed. For example if a +// temporary holds an irange, you can assign an frange into it, and +// all the right things will happen. However, before passing this +// object to a function accepting a vrange, the correct type must be +// set. If it isn't, you can do so with set_type(). class Value_Range { @@ -693,6 +703,7 @@ public: Value_Range (tree type); Value_Range (tree, tree, value_range_kind kind = VR_RANGE); Value_Range (const Value_Range &); + ~Value_Range (); void set_type (tree type); vrange& operator= (const vrange &); Value_Range& operator= (const Value_Range &); @@ -726,16 +737,29 @@ public: void accept (const vrange_visitor &v) const { m_vrange->accept (v); } private: void init (tree type); - unsupported_range m_unsupported; + void init (const vrange &); + vrange *m_vrange; - int_range_max m_irange; - frange m_frange; + // The buffer must be at least the size of the largest range. + static_assert (sizeof (int_range_max) > sizeof (frange)); + char m_buffer[sizeof (int_range_max)]; }; +// The default constructor is uninitialized and must be initialized +// with either set_type() or with an assignment into it. + inline Value_Range::Value_Range () { - m_vrange = &m_unsupported; + m_vrange = NULL; +} + +// Copy constructor. + +inline +Value_Range::V
[PATCH] [ranger] Force buffer alignment in Value_Range [PR114912]
Sparc requires strict alignment and is choking on the byte vector in Value_Range. Is this the right approach, or is there a more canonical way of forcing alignment? If this is correct, OK for trunk? gcc/ChangeLog: * value-range.h (class Value_Range): Use a union. --- gcc/value-range.h | 24 +++- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/gcc/value-range.h b/gcc/value-range.h index 934eec9e386..31af7888018 100644 --- a/gcc/value-range.h +++ b/gcc/value-range.h @@ -740,9 +740,14 @@ private: void init (const vrange &); vrange *m_vrange; - // The buffer must be at least the size of the largest range. - static_assert (sizeof (int_range_max) > sizeof (frange), ""); - char m_buffer[sizeof (int_range_max)]; + union { +// The buffer must be at least the size of the largest range, and +// be aligned on a word boundary for strict alignment targets +// such as sparc. +static_assert (sizeof (int_range_max) > sizeof (frange), ""); +char m_buffer[sizeof (int_range_max)]; +void *align; + } u; }; // The default constructor is uninitialized and must be initialized @@ -816,11 +821,11 @@ Value_Range::init (tree type) gcc_checking_assert (TYPE_P (type)); if (irange::supports_p (type)) -m_vrange = new (&m_buffer) int_range_max (); +m_vrange = new (&u.m_buffer) int_range_max (); else if (frange::supports_p (type)) -m_vrange = new (&m_buffer) frange (); +m_vrange = new (&u.m_buffer) frange (); else -m_vrange = new (&m_buffer) unsupported_range (); +m_vrange = new (&u.m_buffer) unsupported_range (); } // Initialize object with a copy of R. @@ -829,11 +834,12 @@ inline void Value_Range::init (const vrange &r) { if (is_a (r)) -m_vrange = new (&m_buffer) int_range_max (as_a (r)); +m_vrange = new (&u.m_buffer) int_range_max (as_a (r)); else if (is_a (r)) -m_vrange = new (&m_buffer) frange (as_a (r)); +m_vrange = new (&u.m_buffer) frange (as_a (r)); else -m_vrange = new (&m_buffer) unsupported_range (as_a (r)); +m_vrange + = new (&u.m_buffer) unsupported_range (as_a (r)); } // Assignment operator. Copying incompatible types is allowed. That -- 2.44.0
Re: [PATCH] [ranger] Force buffer alignment in Value_Range [PR114912]
Ahh, that is indeed cleaner, and there's no longer a need to assert the sizeof of individual ranges. It looks like a default constructor is needed for the buffer now, but only for the default constructor of Value_Range. I have verified that the individual range constructors are not called on initialization to Value_Range, which was the original point of the patch. I have also run our performance suite, and there are no changes to VRP or overall. I would appreciate a review from someone more C++ savvy than me :). OK for trunk? On Fri, May 3, 2024 at 11:32 AM Andrew Pinski wrote: > > On Fri, May 3, 2024 at 2:24 AM Aldy Hernandez wrote: > > > > Sparc requires strict alignment and is choking on the byte vector in > > Value_Range. Is this the right approach, or is there a more canonical > > way of forcing alignment? > > I think the suggestion was to change over to use an union and use the > types directly in the union (anonymous unions and unions containing > non-PODs are part of C++11). > That is: > union { > int_range_max int_range; > frange fload_range; > unsupported_range un_range; > }; > ... > m_vrange = new (&int_range) int_range_max (); > ... > > Also the canonical way of forcing alignment in C++ is to use aliagnas > as my patch in https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114912 > did. > Also I suspect the alignment is not word alignment but rather the > alignment of HOST_WIDE_INT which is not always the same as the > alignment of the pointer but bigger and that is why it is failing on > sparc (32bit rather than 64bit). > > Thanks, > Andrew Pinski > > > > > If this is correct, OK for trunk? > > > > gcc/ChangeLog: > > > > * value-range.h (class Value_Range): Use a union. > > --- > > gcc/value-range.h | 24 +++- > > 1 file changed, 15 insertions(+), 9 deletions(-) > > > > diff --git a/gcc/value-range.h b/gcc/value-range.h > > index 934eec9e386..31af7888018 100644 > > --- a/gcc/value-range.h > > +++ b/gcc/value-range.h > > @@ -740,9 +740,14 @@ private: > >void init (const vrange &); > > > >vrange *m_vrange; > > - // The buffer must be at least the size of the largest range. > > - static_assert (sizeof (int_range_max) > sizeof (frange), ""); > > - char m_buffer[sizeof (int_range_max)]; > > + union { > > +// The buffer must be at least the size of the largest range, and > > +// be aligned on a word boundary for strict alignment targets > > +// such as sparc. > > +static_assert (sizeof (int_range_max) > sizeof (frange), ""); > > +char m_buffer[sizeof (int_range_max)]; > > +void *align; > > + } u; > > }; > > > > // The default constructor is uninitialized and must be initialized > > @@ -816,11 +821,11 @@ Value_Range::init (tree type) > >gcc_checking_assert (TYPE_P (type)); > > > >if (irange::supports_p (type)) > > -m_vrange = new (&m_buffer) int_range_max (); > > +m_vrange = new (&u.m_buffer) int_range_max (); > >else if (frange::supports_p (type)) > > -m_vrange = new (&m_buffer) frange (); > > +m_vrange = new (&u.m_buffer) frange (); > >else > > -m_vrange = new (&m_buffer) unsupported_range (); > > +m_vrange = new (&u.m_buffer) unsupported_range (); > > } > > > > // Initialize object with a copy of R. > > @@ -829,11 +834,12 @@ inline void > > Value_Range::init (const vrange &r) > > { > >if (is_a (r)) > > -m_vrange = new (&m_buffer) int_range_max (as_a (r)); > > +m_vrange = new (&u.m_buffer) int_range_max (as_a (r)); > >else if (is_a (r)) > > -m_vrange = new (&m_buffer) frange (as_a (r)); > > +m_vrange = new (&u.m_buffer) frange (as_a (r)); > >else > > -m_vrange = new (&m_buffer) unsupported_range (as_a > > (r)); > > +m_vrange > > + = new (&u.m_buffer) unsupported_range (as_a (r)); > > } > > > > // Assignment operator. Copying incompatible types is allowed. That > > -- > > 2.44.0 > > > From a022147e7a9a93d4fc8919aca77ed7fabc99eff7 Mon Sep 17 00:00:00 2001 From: Aldy Hernandez Date: Fri, 3 May 2024 11:17:32 +0200 Subject: [PATCH] [ranger] Force buffer alignment in Value_Range [PR114912] gcc/ChangeLog: * value-range.h (class Value_Range): Use a union. --- gcc/value-range.h | 24 +++- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/gcc/value-range.h b/gcc/value-range.h index 934eec9e386..f12
[PATCH 00/23] prange: pointer ranges
This patchset implements prange, a range class for pointers. This is meant to be a drop-in replacement for pointer ranges, so we can pull them out of irange, simplifying both irange and prange in the process. Initially we have two integer endpoints and the usual value/mask bitmasks as this is how irange currently implements them, but the end goal is to provide points-to info to replace the hacky pointer equivalency we do in class pointer_equiv_analyzer. I have split up the patchset into tiny pieces to make it easier to track any problems. I have also disabled it by default, choosing to wait a few days until the dust has settled. In a few days I will throw the switch enabling pranges, which will make it invalid for irange's to hold pointers. Once pranges are enabled, I will do some minor cleanups like removing pointer support from range-ops, etc. The performance of prange is a wash for VRP and a 7% improvement for IPA-cp. This is taking into account the unrelated 2% hit we take due to inlining as discussed here: https://gcc.gnu.org/pipermail/gcc/2024-May/243898.html Also, as part of this work, we improved VRP by 6% (on top of the above numbers): https://gcc.gnu.org/pipermail/gcc-patches/2024-May/650320.html So things are looking relatively good. The memory usage will also decrease, both by the 14% reduction in Value_Range, and by prange's being smaller than say int_range_max or int_range<3>. Tested and benchmarked on x86-64 Linux. Aldy Hernandez (23): Minimal prange class showing inlining degradation to VRP. Implement basic prange class. Add streaming support for prange. Add storage support for prange. Add hashing support for prange. Add prange implementation for get_legacy_range. Implement range-op dispatch for prange. Implement operator_identity for prange. Implement operator_cst for prange. Implement operator_cast for prange. Implement operator_min and operator_max for prange. Implement operator_addr_expr for prange. Implement pointer_plus_operator for prange. Implement operator_pointer_diff for prange. Implement operator_bitwise_and for prange. Implement operator_bitwise_or for prange. Implement operator_not_equal for prange. Implement operator_equal for prange. Implement operator_lt for prange. Implement operator_le for prange. Implement operator_gt for prange. Implement operator_ge for prange Add prange entries in gimple-range-op.cc. gcc/data-streamer-in.cc | 12 + gcc/data-streamer-out.cc| 10 + gcc/gimple-range-op.cc | 36 + gcc/range-op-mixed.h| 156 gcc/range-op-ptr.cc | 1545 +++ gcc/range-op.cc | 124 +++ gcc/range-op.h | 111 +++ gcc/value-range-pretty-print.cc | 25 + gcc/value-range-pretty-print.h |1 + gcc/value-range-storage.cc | 117 +++ gcc/value-range-storage.h | 33 + gcc/value-range.cc | 354 ++- gcc/value-range.h | 214 - 13 files changed, 2730 insertions(+), 8 deletions(-) -- 2.44.0
[COMMITTED 03/23] Add streaming support for prange.
gcc/ChangeLog: * data-streamer-in.cc (streamer_read_value_range): Add prange support. * data-streamer-out.cc (streamer_write_vrange): Same. --- gcc/data-streamer-in.cc | 12 gcc/data-streamer-out.cc | 10 ++ 2 files changed, 22 insertions(+) diff --git a/gcc/data-streamer-in.cc b/gcc/data-streamer-in.cc index 3a0d3c6ad0f..12cb10e42c0 100644 --- a/gcc/data-streamer-in.cc +++ b/gcc/data-streamer-in.cc @@ -268,6 +268,18 @@ streamer_read_value_range (class lto_input_block *ib, data_in *data_in, } return; } + if (is_a (vr)) +{ + prange &r = as_a (vr); + wide_int lb = streamer_read_wide_int (ib); + wide_int ub = streamer_read_wide_int (ib); + r.set (type, lb, ub); + wide_int value = streamer_read_wide_int (ib); + wide_int mask = streamer_read_wide_int (ib); + irange_bitmask bm (value, mask); + r.update_bitmask (bm); + return; +} gcc_unreachable (); } diff --git a/gcc/data-streamer-out.cc b/gcc/data-streamer-out.cc index 07cc6bd2018..c237e30f704 100644 --- a/gcc/data-streamer-out.cc +++ b/gcc/data-streamer-out.cc @@ -450,6 +450,16 @@ streamer_write_vrange (struct output_block *ob, const vrange &v) } return; } + if (is_a (v)) +{ + const prange &r = as_a (v); + streamer_write_wide_int (ob, r.lower_bound ()); + streamer_write_wide_int (ob, r.upper_bound ()); + irange_bitmask bm = r.get_bitmask (); + streamer_write_wide_int (ob, bm.value ()); + streamer_write_wide_int (ob, bm.mask ()); + return; +} gcc_unreachable (); } -- 2.44.0
[COMMITTED 04/23] Add storage support for prange.
gcc/ChangeLog: * value-range-storage.cc (vrange_allocator::clone_varying): Add prange support. (vrange_allocator::clone_undefined): Same. (vrange_storage::alloc): Same. (vrange_storage::set_vrange): Same. (vrange_storage::get_vrange): Same. (vrange_storage::fits_p): Same. (vrange_storage::equal_p): Same. (prange_storage::alloc): New. (prange_storage::prange_storage): New. (prange_storage::set_prange): New. (prange_storage::get_prange): New. (prange_storage::equal_p): New. (prange_storage::fits_p): New. * value-range-storage.h (class prange_storage): Add prange support. --- gcc/value-range-storage.cc | 117 + gcc/value-range-storage.h | 33 +++ 2 files changed, 150 insertions(+) diff --git a/gcc/value-range-storage.cc b/gcc/value-range-storage.cc index 09a29776a0e..bbae0da4772 100644 --- a/gcc/value-range-storage.cc +++ b/gcc/value-range-storage.cc @@ -118,6 +118,8 @@ vrange_allocator::clone_varying (tree type) { if (irange::supports_p (type)) return irange_storage::alloc (*m_alloc, int_range <1> (type)); + if (prange::supports_p (type)) +return prange_storage::alloc (*m_alloc, prange (type)); if (frange::supports_p (type)) return frange_storage::alloc (*m_alloc, frange (type)); return NULL; @@ -128,6 +130,8 @@ vrange_allocator::clone_undefined (tree type) { if (irange::supports_p (type)) return irange_storage::alloc (*m_alloc, int_range<1> ()); + if (prange::supports_p (type)) +return prange_storage::alloc (*m_alloc, prange ()); if (frange::supports_p (type)) return frange_storage::alloc (*m_alloc, frange ()); return NULL; @@ -141,6 +145,8 @@ vrange_storage::alloc (vrange_internal_alloc &allocator, const vrange &r) { if (is_a (r)) return irange_storage::alloc (allocator, as_a (r)); + if (is_a (r)) +return prange_storage::alloc (allocator, as_a (r)); if (is_a (r)) return frange_storage::alloc (allocator, as_a (r)); return NULL; @@ -157,6 +163,12 @@ vrange_storage::set_vrange (const vrange &r) gcc_checking_assert (s->fits_p (as_a (r))); s->set_irange (as_a (r)); } + else if (is_a (r)) +{ + prange_storage *s = static_cast (this); + gcc_checking_assert (s->fits_p (as_a (r))); + s->set_prange (as_a (r)); +} else if (is_a (r)) { frange_storage *s = static_cast (this); @@ -190,6 +202,11 @@ vrange_storage::get_vrange (vrange &r, tree type) const const irange_storage *s = static_cast (this); s->get_irange (as_a (r), type); } + else if (is_a (r)) +{ + const prange_storage *s = static_cast (this); + s->get_prange (as_a (r), type); +} else if (is_a (r)) { const frange_storage *s = static_cast (this); @@ -209,6 +226,11 @@ vrange_storage::fits_p (const vrange &r) const const irange_storage *s = static_cast (this); return s->fits_p (as_a (r)); } + if (is_a (r)) +{ + const prange_storage *s = static_cast (this); + return s->fits_p (as_a (r)); +} if (is_a (r)) { const frange_storage *s = static_cast (this); @@ -230,6 +252,11 @@ vrange_storage::equal_p (const vrange &r) const const irange_storage *s = static_cast (this); return s->equal_p (as_a (r)); } + if (is_a (r)) +{ + const prange_storage *s = static_cast (this); + return s->equal_p (as_a (r)); +} if (is_a (r)) { const frange_storage *s = static_cast (this); @@ -559,6 +586,96 @@ frange_storage::fits_p (const frange &) const return true; } +// +// prange_storage implementation +// + +prange_storage * +prange_storage::alloc (vrange_internal_alloc &allocator, const prange &r) +{ + // Assume all pointers are the same size. + unsigned prec = TYPE_PRECISION (TREE_TYPE (null_pointer_node)); + gcc_checking_assert (r.undefined_p () || TYPE_PRECISION (r.type ()) == prec); + + typedef trailing_wide_ints twi; + size_t size = sizeof (prange_storage) + twi::extra_size (prec); + prange_storage *p = static_cast (allocator.alloc (size)); + new (p) prange_storage (r); + return p; +} + +// Initialize the storage with R. + +prange_storage::prange_storage (const prange &r) +{ + // It is the caller's responsibility to allocate enough space such + // that the precision fits. + unsigned prec = TYPE_PRECISION (TREE_TYPE (null_pointer_node)); + m_trailing_ints.set_precision (prec); + + set_prange (r); +} + +void +prange_storage::set_prange (const prange &r) +{ + if (r.undefined_p ()) +m_kind = VR_UNDEFINED; + else if (r.varying_p ()) +m_kind = VR_VARYING; + else +{ + m_kind = VR_RANGE; + set_low (r.lower_bound ()
[COMMITTED 02/23] Implement basic prange class.
This provides a bare prange class with bounds and bitmasks. It will be a drop-in replacement for pointer ranges, so we can pull their support from irange. The range-op code will be contributed as a follow-up. The code is disabled by default, as irange::supports_p still accepts pointers: inline bool irange::supports_p (const_tree type) { return INTEGRAL_TYPE_P (type) || POINTER_TYPE_P (type); } Once the prange operators are implemented in range-ops, pointer support will be removed from irange to activate pranges. gcc/ChangeLog: * value-range-pretty-print.cc (vrange_printer::visit): New. * value-range-pretty-print.h: Declare prange visit() method. * value-range.cc (vrange::operator=): Add prange support. (vrange::operator==): Same. (prange::accept): New. (prange::set_nonnegative): New. (prange::set): New. (prange::contains_p): New. (prange::singleton_p): New. (prange::lbound): New. (prange::ubound): New. (prange::union_): New. (prange::intersect): New. (prange::operator=): New. (prange::operator==): New. (prange::invert): New. (prange::verify_range): New. (prange::update_bitmask): New. (range_tests_misc): Use prange. * value-range.h (enum value_range_discriminator): Add VR_PRANGE. (class prange): New. (Value_Range::init): Add prange support. (Value_Range::operator=): Same. (Value_Range::supports_type_p): Same. (prange::prange): New. (prange::supports_p): New. (prange::supports_type_p): New. (prange::set_undefined): New. (prange::set_varying): New. (prange::set_nonzero): New. (prange::set_zero): New. (prange::contains_p): New. (prange::zero_p): New. (prange::nonzero_p): New. (prange::type): New. (prange::lower_bound): New. (prange::upper_bound): New. (prange::varying_compatible_p): New. (prange::get_bitmask): New. (prange::fits_p): New. --- gcc/value-range-pretty-print.cc | 25 +++ gcc/value-range-pretty-print.h | 1 + gcc/value-range.cc | 303 +++- gcc/value-range.h | 199 ++--- 4 files changed, 500 insertions(+), 28 deletions(-) diff --git a/gcc/value-range-pretty-print.cc b/gcc/value-range-pretty-print.cc index b6d23dce6d2..b11d6494774 100644 --- a/gcc/value-range-pretty-print.cc +++ b/gcc/value-range-pretty-print.cc @@ -112,6 +112,31 @@ vrange_printer::visit (const irange &r) const print_irange_bitmasks (pp, r.m_bitmask); } +void +vrange_printer::visit (const prange &r) const +{ + pp_string (pp, "[prange] "); + if (r.undefined_p ()) +{ + pp_string (pp, "UNDEFINED"); + return; +} + dump_generic_node (pp, r.type (), 0, TDF_NONE | TDF_NOUID, false); + pp_character (pp, ' '); + if (r.varying_p ()) +{ + pp_string (pp, "VARYING"); + return; +} + + pp_character (pp, '['); + print_int_bound (pp, r.lower_bound (), r.type ()); + pp_string (pp, ", "); + print_int_bound (pp, r.upper_bound (), r.type ()); + pp_character (pp, ']'); + print_irange_bitmasks (pp, r.m_bitmask); +} + void vrange_printer::print_real_value (tree type, const REAL_VALUE_TYPE &r) const { diff --git a/gcc/value-range-pretty-print.h b/gcc/value-range-pretty-print.h index 44cd6e81298..5522aad0673 100644 --- a/gcc/value-range-pretty-print.h +++ b/gcc/value-range-pretty-print.h @@ -27,6 +27,7 @@ public: vrange_printer (pretty_printer *pp_) : pp (pp_) { } void visit (const unsupported_range &) const override; void visit (const irange &) const override; + void visit (const prange &) const override; void visit (const frange &) const override; private: void print_frange_nan (const frange &) const; diff --git a/gcc/value-range.cc b/gcc/value-range.cc index 7250115261f..84113ccfbd0 100644 --- a/gcc/value-range.cc +++ b/gcc/value-range.cc @@ -251,6 +251,8 @@ vrange::operator= (const vrange &src) { if (is_a (src)) as_a (*this) = as_a (src); + else if (is_a (src)) +as_a (*this) = as_a (src); else if (is_a (src)) as_a (*this) = as_a (src); else @@ -268,6 +270,8 @@ vrange::operator== (const vrange &src) const { if (is_a (src)) return as_a (*this) == as_a (src); + if (is_a (src)) +return as_a (*this) == as_a (src); if (is_a (src)) return as_a (*this) == as_a (src); gcc_unreachable (); @@ -397,6 +401,294 @@ irange::set_nonnegative (tree type) wi::to_wide (TYPE_MAX_VALUE (type))); } +// Prange implementation. + +void +prange::accept (const vrange_visitor &v) const +{ + v.visit (*this); +} + +void +prange::set_nonnegative (tree type) +{ + set (type, + wi::zero (TYPE_PRECISION (type)), + wi::max_value (TYPE_PRECISION (type), UNSIGNED)); +} + +void +prange::set (tree min, tree max, value_range_kind kind)
[COMMITTED 07/23] Implement range-op dispatch for prange.
This patch adds the range-op dispatch code for prange, and adds some temporary sanity checks (for flag_checking only) to make sure we handle all the pointer/integer variants. In order to make sure I got all the combinations right, I started with a clean slate, trapping on all pointer operands. Then I added support for each one piecemeal. To verify the work, I added a pointers_handled_p() helper that is implemented for each range-op entry and returns TRUE iff the operator can handle a given combination of pointers. If this helper returns false, we will trap, because it indicates an operator that was not implemented. This is temporary checking code, and I will rip it out once the the dust has settled in a few days. gcc/ChangeLog: * range-op-mixed.h: Add using declarator for all classes. * range-op-ptr.cc (range_operator::pointers_handled_p): New. (range_operator::fold_range): New. (range_operator::op1_op2_relation_effect): New. (range_operator::op1_range): New. (range_operator::op2_range): New. (range_operator::op1_op2_relation): New. (range_operator::lhs_op1_relation): New. (range_operator::update_bitmask): New. (class pointer_plus_operator): New. (class operator_pointer_diff): New. (class hybrid_min_operator): New. (class hybrid_max_operator): New. * range-op.cc: Add RO_PPP, RO_PPI, RO_IPP, RO_IPI, RO_PIP, RO_PII. (range_op_handler::discriminator_fail): New. (has_pointer_operand_p): New. (range_op_handler::fold_range): Add pointer support. (range_op_handler::op1_range): Same. (range_op_handler::op2_range): Same. (range_op_handler::lhs_op1_relation): Same. (range_op_handler::lhs_op2_relation): Same. (range_op_handler::op1_op2_relation): Same. (class operator_div): Add using. (class operator_lshift): Add using. (class operator_rshift):Add using. (class operator_trunc_mod):Add using. (class operator_absu):Add using. * range-op.h (enum range_op_dispatch_type): New. Add extern definitions for RO_*. --- gcc/range-op-mixed.h | 19 gcc/range-op-ptr.cc | 220 +++ gcc/range-op.cc | 124 gcc/range-op.h | 111 ++ 4 files changed, 474 insertions(+) diff --git a/gcc/range-op-mixed.h b/gcc/range-op-mixed.h index 3ee7c9d6e0d..8163a4b53ca 100644 --- a/gcc/range-op-mixed.h +++ b/gcc/range-op-mixed.h @@ -111,6 +111,7 @@ public: using range_operator::op1_range; using range_operator::op2_range; using range_operator::op1_op2_relation; + using range_operator::update_bitmask; bool fold_range (irange &r, tree type, const irange &op1, const irange &op2, relation_trio = TRIO_VARYING) const final override; @@ -150,6 +151,7 @@ public: using range_operator::op1_range; using range_operator::op2_range; using range_operator::op1_op2_relation; + using range_operator::update_bitmask; bool fold_range (irange &r, tree type, const irange &op1, const irange &op2, relation_trio = TRIO_VARYING) const final override; @@ -189,6 +191,7 @@ public: using range_operator::op1_range; using range_operator::op2_range; using range_operator::op1_op2_relation; + using range_operator::update_bitmask; bool fold_range (irange &r, tree type, const irange &op1, const irange &op2, relation_trio = TRIO_VARYING) const final override; @@ -225,6 +228,7 @@ public: using range_operator::op1_range; using range_operator::op2_range; using range_operator::op1_op2_relation; + using range_operator::update_bitmask; bool fold_range (irange &r, tree type, const irange &op1, const irange &op2, relation_trio = TRIO_VARYING) const final override; @@ -264,6 +268,7 @@ public: using range_operator::op1_range; using range_operator::op2_range; using range_operator::op1_op2_relation; + using range_operator::update_bitmask; bool fold_range (irange &r, tree type, const irange &op1, const irange &op2, relation_trio = TRIO_VARYING) const final override; @@ -302,6 +307,7 @@ public: using range_operator::op1_range; using range_operator::op2_range; using range_operator::op1_op2_relation; + using range_operator::update_bitmask; bool fold_range (irange &r, tree type, const irange &op1, const irange &op2, relation_trio = TRIO_VARYING) const final override; @@ -376,6 +382,7 @@ public: using range_operator::fold_range; using range_operator::op1_range; using range_operator::lhs_op1_relation; + using range_operator::update_bitmask; bool fold_range (irange &r, tree type, const irange &op1, const irange &op2, relatio