Hi! This patch is an attempt to warn about undefined behavior in simple loops with known constant number of latch executions, which probably is the most common case where gcc 4.8 started to turn finite loops involving undefined behavior before reaching last iteration into endless loops.
Of course the patch doesn't guarantee warning for all such mistakes, and for loops without known constant number of latch executions it is question on what exactly we should warn about. Honza has some prototype patch, perhaps that could be done if loop->warned_aggressive_loop_optimizations is still false. To avoid false positives in some cases (e.g. exp_ch7.adb during bootstrap), I had to start warning only later on (with the advantage that at that point loops are preserved and thus we can set flag in the loop structure whether we've warned already). I had to tweak a couple of testcases (pr49518.c clearly can result in undefined behavior conditionally, longbranch2.C also had obvious undefined behavior (I've verified that r80000 cc1plus generated exactly same size and insn type code with original and fixed testcase, the only thing that differed were immediates in some instructions due to different field sizes). In cunroll-10.c we no longer do anything as it had known constant number of latch executions, thus no undefined behavior discovery is performed. In libgcc, the DW_CFA_GNU_window_save handling code was hardcoding SPARC register window setup, but that is also undefined behavior on targets that have fewer than 32 DWARF_FRAME_REGISTERS. I've just shut it up there, after all DW_CFA_GNU_window_save is only ever used on SPARC anyway. I've bootstrapped/regtested the patch together with a debugging patch to show where the last hunk in estimate_numbers_of_iterations_loop actually changed anything. It happened in ../../gcc/ada/exp_ch7.adb:3540 (see the PR, in dead code), we'd need a copyprop pass with cfgcleanup before cunrolli to avoid that, then in {sse4_2,avx}-vpcmp{i,e}strm-{1,2}.c /usr/src/gcc/gcc/testsuite/gcc.target/i386/sse4_2-pcmpstr.h:339 (reduced testcase -O2: short t[8]; static inline void bar (int x, int y) { short s[8]; int i, d = (x & 1) == 0 ? 16 : 8; if (x & 0x40) for (i = 0; i < d; i++) if (d == 8) s[i] = (y & (1 << i)) ? -1 : 0; __builtin_memcpy (t, s, sizeof s); } void foo (int y) { bar (0x26, y); } ) - here number_of_latch_iterations returns 0, apparently because it finds out that the loop will be never executed. Then gcc.dg/torture/pr49518.c:11 (see above, ok) and gcc.dg/pr53265.c (expected, many). So, I think we're fine. Ok for 4.8? 2013-03-13 Jakub Jelinek <ja...@redhat.com> PR tree-optimization/53265 * common.opt (Waggressive-loop-optimizations): New option. * tree-ssa-loop-niter.c: Include tree-pass.h. (do_warn_aggressive_loop_optimizations): New function. (record_estimate): Call it. Don't add !is_exit bounds to loop->bounds if number_of_latch_executions returned constant. (estimate_numbers_of_iterations_loop): Call number_of_latch_executions early. If number_of_latch_executions returned constant, set nb_iterations_upper_bound back to it. * cfgloop.h (struct loop): Add warned_aggressive_loop_optimizations field. * Makefile.in (tree-ssa-loop-niter.o): Depend on $(TREE_PASS_H). * doc/invoke.texi (-Wno-aggressive-loop-optimizations): Document. * gcc.dg/pr53265.c: New test. * gcc.dg/torture/pr49518.c: Add -Wno-aggressive-loop-optimizations to dg-options. * g++.dg/opt/longbranch2.C (EBCOTLut): Double sizes of a2 and a3 arrays. * gcc.dg/tree-ssa/cunroll-10.c (main): Rename to foo. Add argument n, use it as high bound instead of 4. * unwind-dw2.c (execute_cfa_program): Avoid -Waggressive-array-optimizations warnings for DW_CFA_GNU_window_save on targets with DWARF_FRAME_REGISTERS < 32. * testsuite/libmudflap.c/fail37-frag.c: Add optimization barrier. --- gcc/common.opt.jj 2013-03-12 09:59:34.692099982 +0100 +++ gcc/common.opt 2013-03-12 13:47:08.922161154 +0100 @@ -505,6 +505,10 @@ Waggregate-return Common Var(warn_aggregate_return) Warning Warn about returning structures, unions or arrays +Waggressive-loop-optimizations +Common Var(warn_aggressive_loop_optimizations) Init(1) Warning +Warn if a loop with constant number of iterations triggers undefined behavior + Warray-bounds Common Var(warn_array_bounds) Warning Warn if an array is accessed out of bounds --- gcc/tree-ssa-loop-niter.c.jj 2013-03-12 09:59:34.740099692 +0100 +++ gcc/tree-ssa-loop-niter.c 2013-03-13 11:19:02.100060913 +0100 @@ -37,6 +37,7 @@ along with GCC; see the file COPYING3. #include "flags.h" #include "diagnostic-core.h" #include "tree-inline.h" +#include "tree-pass.h" #define SWAP(X, Y) do { affine_iv *tmp = (X); (X) = (Y); (Y) = tmp; } while (0) @@ -2525,6 +2526,40 @@ record_niter_bound (struct loop *loop, d loop->nb_iterations_estimate = loop->nb_iterations_upper_bound; } +/* Emit a -Waggressive-loop-optimizations warning if needed. */ + +static void +do_warn_aggressive_loop_optimizations (struct loop *loop, + double_int i_bound, gimple stmt) +{ + /* Don't warn if the loop doesn't have known constant bound. */ + if (!loop->nb_iterations + || TREE_CODE (loop->nb_iterations) != INTEGER_CST + || !warn_aggressive_loop_optimizations + /* To avoid warning multiple times for the same loop, + only start warning when we preserve loops. */ + || (cfun->curr_properties & PROP_loops) == 0 + /* Only warn once per loop. */ + || loop->warned_aggressive_loop_optimizations + /* Only warn if undefined behavior gives us lower estimate than the + known constant bound. */ + || i_bound.ucmp (tree_to_double_int (loop->nb_iterations)) >= 0 + /* And undefined behavior happens unconditionally. */ + || !dominated_by_p (CDI_DOMINATORS, loop->latch, gimple_bb (stmt))) + return; + + edge e = single_exit (loop); + if (e == NULL) + return; + + gimple estmt = last_stmt (e->src); + warning_at (gimple_location (stmt), OPT_Waggressive_loop_optimizations, + "iteration %E invokes undefined behavior", + double_int_to_tree (TREE_TYPE (loop->nb_iterations), i_bound)); + inform (gimple_location (estmt), "containing loop"); + loop->warned_aggressive_loop_optimizations = true; +} + /* Records that AT_STMT is executed at most BOUND + 1 times in LOOP. IS_EXIT is true if the loop is exited immediately after STMT, and this exit is taken at last when the STMT is executed BOUND + 1 times. @@ -2560,8 +2595,12 @@ record_estimate (struct loop *loop, tree return; /* If we have a guaranteed upper bound, record it in the appropriate - list. */ - if (upper) + list, unless this is an !is_exit bound (i.e. undefined behavior in + at_stmt) in a loop with known constant number of iterations. */ + if (upper + && (is_exit + || loop->nb_iterations == NULL_TREE + || TREE_CODE (loop->nb_iterations) != INTEGER_CST)) { struct nb_iter_bound *elt = ggc_alloc_nb_iter_bound (); @@ -2591,6 +2630,8 @@ record_estimate (struct loop *loop, tree if (i_bound.ult (delta)) return; + if (upper && !is_exit) + do_warn_aggressive_loop_optimizations (loop, i_bound, at_stmt); record_niter_bound (loop, i_bound, realistic, upper); } @@ -3311,6 +3352,11 @@ estimate_numbers_of_iterations_loop (str /* Force estimate compuation but leave any existing upper bound in place. */ loop->any_estimate = false; + /* Ensure that loop->nb_iterations is computed if possible. If it turns out + to be constant, we avoid undefined behavior implied bounds and instead + diagnose those loops with -Waggressive-loop-optimizations. */ + number_of_latch_executions (loop); + exits = get_loop_exit_edges (loop); likely_exit = single_likely_exit (loop); FOR_EACH_VEC_ELT (exits, i, ex) @@ -3345,6 +3391,17 @@ estimate_numbers_of_iterations_loop (str bound = gcov_type_to_double_int (nit); record_niter_bound (loop, bound, true, false); } + + /* If we know the exact number of iterations of this loop, try to + not break code with undefined behavior by not recording smaller + maximum number of iterations. */ + if (loop->nb_iterations + && TREE_CODE (loop->nb_iterations) == INTEGER_CST) + { + loop->any_upper_bound = true; + loop->nb_iterations_upper_bound + = tree_to_double_int (loop->nb_iterations); + } } /* Sets NIT to the estimated number of executions of the latch of the --- gcc/cfgloop.h.jj 2013-03-12 09:59:34.677100070 +0100 +++ gcc/cfgloop.h 2013-03-12 13:47:08.919161106 +0100 @@ -159,6 +159,10 @@ struct GTY ((chain_next ("%h.next"))) lo /* True if the loop can be parallel. */ bool can_be_parallel; + /* True if -Waggressive-loop-optimizations warned about this loop + already. */ + bool warned_aggressive_loop_optimizations; + /* An integer estimation of the number of iterations. Estimate_state describes what is the state of the estimation. */ enum loop_estimation estimate_state; --- gcc/Makefile.in.jj 2013-03-12 09:59:34.988098249 +0100 +++ gcc/Makefile.in 2013-03-12 13:47:08.919161106 +0100 @@ -2446,7 +2446,7 @@ tree-ssa-loop-niter.o : tree-ssa-loop-ni $(SYSTEM_H) $(TREE_H) $(TM_P_H) $(CFGLOOP_H) $(PARAMS_H) \ $(TREE_INLINE_H) $(DIAGNOSTIC_H) $(TM_H) coretypes.h dumpfile.h \ $(DIAGNOSTIC_CORE_H) $(FLAGS_H) $(TREE_DATA_REF_H) \ - $(BASIC_BLOCK_H) $(GGC_H) intl.h $(GIMPLE_PRETTY_PRINT_H) + $(BASIC_BLOCK_H) $(GGC_H) intl.h $(GIMPLE_PRETTY_PRINT_H) $(TREE_PASS_H) tree-ssa-loop-ivcanon.o : tree-ssa-loop-ivcanon.c $(TREE_FLOW_H) $(CONFIG_H) \ $(SYSTEM_H) $(TREE_H) $(TM_P_H) $(CFGLOOP_H) $(PARAMS_H) \ $(TREE_INLINE_H) $(DIAGNOSTIC_H) $(TM_H) coretypes.h \ --- gcc/doc/invoke.texi.jj 2013-03-12 14:22:01.000000000 +0100 +++ gcc/doc/invoke.texi 2013-03-12 16:45:04.580807698 +0100 @@ -232,7 +232,8 @@ Objective-C and Objective-C++ Dialects}. @xref{Warning Options,,Options to Request or Suppress Warnings}. @gccoptlist{-fsyntax-only -fmax-errors=@var{n} -Wpedantic @gol -pedantic-errors @gol --w -Wextra -Wall -Waddress -Waggregate-return -Warray-bounds @gol +-w -Wextra -Wall -Waddress -Waggregate-return @gol +-Waggressive-loop-optimizations -Warray-bounds @gol -Wno-attributes -Wno-builtin-macro-redefined @gol -Wc++-compat -Wc++11-compat -Wcast-align -Wcast-qual @gol -Wchar-subscripts -Wclobbered -Wcomment @gol @@ -4423,6 +4424,12 @@ Warn if any functions that return struct called. (In languages where you can return an array, this also elicits a warning.) +@item -Wno-aggressive-loop-optimizations +@opindex Wno-aggressive-loop-optimizations +@opindex Waggressive-loop-optimizations +Warn if in a loop with constant number of iterations the compiler detects +undefined behavior in some statement during one or more of the iterations. + @item -Wno-attributes @opindex Wno-attributes @opindex Wattributes --- gcc/testsuite/gcc.dg/pr53265.c.jj 2013-03-12 13:47:08.922161154 +0100 +++ gcc/testsuite/gcc.dg/pr53265.c 2013-03-13 11:23:52.592338941 +0100 @@ -0,0 +1,156 @@ +/* PR tree-optimization/53265 */ +/* { dg-do compile } */ +/* { dg-options "-O2 -Wall" } */ + +void bar (void *); +int baz (int); + +void +fn1 (void) +{ + unsigned int a[128]; + int i; + + for (i = 0; i < 128; ++i) /* { dg-message "note: containing loop" } */ + a[i] = i * 0x02000001; /* { dg-warning "invokes undefined behavior" } */ + bar (a); +} + +void +fn2 (void) +{ + unsigned long long a[128]; + int i; + + for (i = 0; i < 128; i++) /* { dg-message "note: containing loop" } */ + a[i] = (i + 1LL) * 0x0123456789ABCDEFLL; /* { dg-warning "invokes undefined behavior" } */ + bar (a); +} + +void +fn3 (void) +{ + unsigned char a[16], b[16], c[16]; + int i; + + bar (b); + for (i = 0; i < (int) (sizeof (a) / sizeof (a[0])); i++) /* { dg-message "note: containing loop" } */ + { + c[i + 8] = b[i]; /* { dg-warning "invokes undefined behavior" } */ + a[i + 8] = b[i + 8]; + } + bar (a); + bar (c); +} + +void +fn4 (void) +{ + unsigned int *a[32], *o, i; + + bar (a); + for (i = 0; i <= sizeof (a) / sizeof (a[0]); i++) /* { dg-message "note: containing loop" "" { xfail *-*-* } } */ + { + o = a[i]; /* { dg-warning "invokes undefined behavior" "" { xfail *-*-* } } */ + bar (o); + } +} + +void +fn5 (void) +{ + unsigned short a[23940]; + unsigned int b[1140]; + int j; + + bar (b); + for (j = 0; j < 1140; j++) /* { dg-message "note: containing loop" } */ + a[23940 + j - 950] = b[j]; /* { dg-warning "invokes undefined behavior" } */ + bar (a); +} + +void +fn6 (void) +{ + double a[4][3], b[12]; + int i; + bar (b); + for (i = 0; i < 12; i++) /* { dg-message "note: containing loop" } */ + a[0][i] = b[i] / 10000.0; /* { dg-warning "invokes undefined behavior" } */ + bar (a); +} + +void +fn7 (void) +{ + int a[16], b, c; + bar (a); + for (b = a[c = 0]; c < 16; b = a[++c]) /* { dg-warning "invokes undefined behavior" "" { xfail *-*-* } } */ + baz (b); +} + +/* { dg-message "note: containing loop" "" { xfail *-*-* } 88 } */ + +const void *va, *vb, *vc, *vd, *ve; +const void *vf[4]; +void +fn8 (void) +{ + unsigned long i; + vf[0] = va; vf[1] = vb; vf[2] = vc; vf[3] = vd; + for (i = 0; i < (sizeof (vf) / sizeof (vf[0])); i++) + if (!vf[i]) + vf[i] = ve; +} + +int wa, wb[53][5], wc[53][5]; + +void +fn9 (void) +{ + int i, j, k; + for (i = 0; i < 53; i++) + for (j = 16 / (((wa & 1) != 0) ? 8 : 4); j > 0; j--) + { + int d = 1; + if (wb[i][j] == 0 || wc[i][1] != 0) + continue; + for (k = 0; k < j; k++) + if (wc[i + k][1]) + { + d = 0; + break; + } + if (!d) + continue; + wc[i][j] = baz (0); + } +} + +int xa[18]; + +void +fn10 (void) +{ + int i; + for (i = 16; i < 32; i++) /* { dg-message "note: containing loop" } */ + xa[i] = 26; /* { dg-warning "invokes undefined behavior" } */ +} + +__attribute__((noinline)) static void +fn11 (int x) +{ + int i = 1; + if (x > 1) + do + baz (i); + while (++i != x); /* { dg-bogus "invokes undefined behavior" } */ +} + +void +fn12 (void) +{ + fn11 (1); + fn11 (1); + fn11 (1); +} --- gcc/testsuite/gcc.dg/torture/pr49518.c (revision 196628) +++ gcc/testsuite/gcc.dg/torture/pr49518.c (working copy) @@ -1,4 +1,5 @@ /* { dg-do compile } */ +/* { dg-options "-Wno-aggressive-loop-optimizations" } */ int a, b; struct S { unsigned int s, t, u; } c, d = { 0, 1, 0 }; --- gcc/testsuite/g++.dg/opt/longbranch2.C.jj 2011-07-11 10:39:36.000000000 +0200 +++ gcc/testsuite/g++.dg/opt/longbranch2.C 2013-03-13 15:02:59.814892282 +0100 @@ -15,8 +15,8 @@ public: class EBCOTLut : public JKeeper { unsigned char a1[1<<8]; - unsigned char a2[1<<8]; - unsigned char a3[1<<8]; + unsigned char a2[1<<9]; + unsigned char a3[1<<9]; long a4[1<<9]; public: EBCOTLut(void); --- gcc/testsuite/gcc.dg/tree-ssa/cunroll-10.c.jj 2012-11-05 08:55:20.000000000 +0100 +++ gcc/testsuite/gcc.dg/tree-ssa/cunroll-10.c 2013-03-13 18:48:20.000000000 +0100 @@ -2,10 +2,11 @@ /* { dg-options "-O3 -Warray-bounds -fdump-tree-cunroll-details" } */ int a[3]; int b[4]; -main() +int +foo (int n) { int i; - for (i=0;i<4;i++) + for (i=0;i<n;i++) if (b[i]==2) a[i]++; } --- libgcc/unwind-dw2.c.jj 2013-02-05 09:06:55.000000000 +0100 +++ libgcc/unwind-dw2.c 2013-03-12 16:41:08.665181831 +0100 @@ -1128,11 +1128,12 @@ execute_cfa_program (const unsigned char case DW_CFA_GNU_window_save: /* ??? Hardcoded for SPARC register window configuration. */ - for (reg = 16; reg < 32; ++reg) - { - fs->regs.reg[reg].how = REG_SAVED_OFFSET; - fs->regs.reg[reg].loc.offset = (reg - 16) * sizeof (void *); - } + if (DWARF_FRAME_REGISTERS >= 32) + for (reg = 16; reg < 32; ++reg) + { + fs->regs.reg[reg].how = REG_SAVED_OFFSET; + fs->regs.reg[reg].loc.offset = (reg - 16) * sizeof (void *); + } break; case DW_CFA_GNU_args_size: --- libmudflap/testsuite/libmudflap.c/fail37-frag.c.jj 2008-09-05 12:57:41.000000000 +0200 +++ libmudflap/testsuite/libmudflap.c/fail37-frag.c 2013-03-13 11:32:36.925228158 +0100 @@ -13,7 +13,11 @@ main () { int i; for (i = 0; i < 5; i++) - x.s[i].f = 0; + { + /* Optimization barrier. Prevent gcc from seeing the undefined behavior. */ + __asm ("" : "+r" (i)); + x.s[i].f = 0; + } exit (0); } /* { dg-output "mudflap violation 1.*" } */ Jakub