+2019-08-02 Richard Biener <rguenther@suse.de>
+
+ Backport from mainline
+ 2019-07-31 Richard Biener <rguenther@suse.de>
+
+ PR tree-optimization/91178
+ * tree-ssa-sccvn.c (vn_reference_maybe_forwprop_address):
+ Use tail-recursion.
+
+ 2019-07-17 Richard Biener <rguenther@suse.de>
+
+ PR tree-optimization/91178
+ * tree-ssa.c (release_defs_bitset): Iterate from higher to
+ lower SSA names to avoid quadratic behavior in the common case.
+ * tree-data-ref.c (split_constant_offset): Add limit argument
+ and pass it down. Initialize it from PARAM_SSA_NAME_DEF_CHAIN_LIMIT.
+ (split_constant_offset_1): Add limit argument and use it to
+ limit SSA def walking. Optimize the common plus/minus case.
+
+ 2019-07-12 Martin Sebor <msebor@redhat.com>
+
+ * doc/invoke.texi (ssa-name-def-chain-limit): Document new --param.
+ * params.def (PARAM_SSA_NAME_DEF_CHAIN_LIMIT): Add new --param.
+ * tree-vrp.c (vrp_prop::check_mem_ref): Use
+ PARAM_SSA_NAME_DEF_CHAIN_LIMIT.
+
2019-08-01 Jakub Jelinek <jakub@redhat.com>
Backported from mainline
discounting any instructions in inner loops that directly benefit
from versioning.
+@item ssa-name-def-chain-limit
+The maximum number of SSA_NAME assignments to follow in determining
+a property of a variable such as its value. This limits the number
+of iterations or recursive calls GCC performs when optimizing certain
+statements or when determining their validity prior to issuing
+diagnostics.
+
@end table
@end table
" loops.",
100, 0, 0)
+DEFPARAM(PARAM_SSA_NAME_DEF_CHAIN_LIMIT,
+ "ssa-name-def-chain-limit",
+ "The maximum number of SSA_NAME assignments to follow in determining "
+ "a value.",
+ 512, 0, 0)
+
/*
Local variables:
+2019-08-02 Richard Biener <rguenther@suse.de>
+
+ Backport from mainline
+ 2019-07-31 Richard Biener <rguenther@suse.de>
+
+ PR tree-optimization/91178
+ * gcc.dg/torture/pr91178-2.c: New testcase.
+
+ 2019-07-17 Richard Biener <rguenther@suse.de>
+
+ PR tree-optimization/91178
+ * gcc.dg/torture/pr91178.c: New testcase.
+
+ 2019-07-12 Martin Sebor <msebor@redhat.com>
+
+ * gcc.dg/Warray-bounds-43.c: New test.
+
2019-08-01 Jakub Jelinek <jakub@redhat.com>
Backported from mainline
--- /dev/null
+/* Test to verify that --param ssa_name_def_chain_limit can be used to
+ limit the maximum number of SSA_NAME assignments the warning follows.
+ { dg-do compile }
+ { dg-options "-O2 -Wall --param ssa-name-def-chain-limit=4" } */
+
+#define NOIPA __attribute__ ((noipa))
+
+const char a0[] = "";
+const char a1[] = "1";
+const char a2[] = "12";
+const char a3[] = "123";
+const char a4[] = "1234";
+const char a5[] = "12345";
+const char a6[] = "123456";
+const char a7[] = "1234567";
+const char a8[] = "12345678";
+const char a9[] = "123456789";
+
+void f (const char*, ...);
+
+int i0, i1, i2, i3, i4, i5, i6, i7, i8;
+
+NOIPA int g2 (int i)
+{
+ if (i < 1) i = 1;
+
+ const char *p0 = a9;
+ const char *p1 = p0 + i;
+ const char *p2 = p1 + i;
+
+ f (p0, p1, p2);
+
+ return p2[8]; // { dg-warning "\\\[-Warray-bounds]" }
+}
+
+NOIPA int g3 (int i)
+{
+ if (i < 1) i = 1;
+
+ const char *p0 = a9;
+ const char *p1 = p0 + i;
+ const char *p2 = p1 + i;
+ const char *p3 = p2 + i;
+
+ f (p0, p1, p2, p3);
+
+ return p3[7]; // { dg-warning "\\\[-Warray-bounds]" }
+}
+
+NOIPA int g4 (int i)
+{
+ if (i < 1) i = 1;
+
+ const char *p0 = a9;
+ const char *p1 = p0 + i;
+ const char *p2 = p1 + i;
+ const char *p3 = p2 + i;
+ const char *p4 = p3 + i;
+
+ f (p0, p1, p2, p3, p4);
+
+ return p4[6]; // { dg-warning "\\\[-Warray-bounds]" }
+}
+
+NOIPA int g5 (int i)
+{
+ if (i < 1) i = 1;
+
+ const char *p0 = a9;
+ const char *p1 = p0 + i;
+ const char *p2 = p1 + i;
+ const char *p3 = p2 + i;
+ const char *p4 = p3 + i;
+ const char *p5 = p4 + i;
+
+ f (p0, p1, p2, p3, p4, p5);
+
+ return p5[5];
+}
+
+NOIPA int g6 (int i)
+{
+ if (i < 1) i = 1;
+
+ const char *p0 = a9;
+ const char *p1 = p0 + i;
+ const char *p2 = p1 + i;
+ const char *p3 = p2 + i;
+ const char *p4 = p3 + i;
+ const char *p5 = p4 + i;
+ const char *p6 = p5 + i;
+
+ f (p0, p1, p2, p3, p4, p5, p6);
+
+ return p6[4];
+}
+
+NOIPA int g7 (int i)
+{
+ if (i < 1) i = 1;
+
+ const char *p0 = a9;
+ const char *p1 = p0 + i;
+ const char *p2 = p1 + i;
+ const char *p3 = p2 + i;
+ const char *p4 = p3 + i;
+ const char *p5 = p4 + i;
+ const char *p6 = p5 + i;
+ const char *p7 = p6 + i;
+
+ f (p0, p1, p2, p3, p4, p5, p6, p7);
+
+ return p7[3];
+}
+
+NOIPA int g8 (int i)
+{
+ if (i < 1) i = 1;
+
+ const char *p0 = a9;
+ const char *p1 = p0 + i;
+ const char *p2 = p1 + i;
+ const char *p3 = p2 + i;
+ const char *p4 = p3 + i;
+ const char *p5 = p4 + i;
+ const char *p6 = p5 + i;
+ const char *p7 = p6 + i;
+ const char *p8 = p7 + i;
+
+ f (p0, p1, p2, p3, p4, p5, p6, p7, p8);
+
+ return p8[2];
+}
--- /dev/null
+/* { dg-do compile } */
+
+int a[100][70304];
+int b[100];
+void c()
+{
+ for (int d = 2; d < 4; d++)
+ for (int e = 2; e <= 50; e++)
+ for (int f = 32; f <= 38; f++)
+ b[d + f] -= a[e][5];
+}
--- /dev/null
+/* { dg-do compile } */
+
+int a;
+extern int f[10][91125];
+int b[50];
+void c()
+{
+ for (int d = 6; d <= a; d++)
+ for (int e = 16; e <= 24; e++)
+ b[e] -= f[d][d];
+}
static void
split_constant_offset (tree exp, tree *var, tree *off,
- hash_map<tree, std::pair<tree, tree> > &cache);
+ hash_map<tree, std::pair<tree, tree> > &cache,
+ unsigned *limit);
/* Helper function for split_constant_offset. Expresses OP0 CODE OP1
(the type of the result is TYPE) as VAR + OFF, where OFF is a nonzero
static bool
split_constant_offset_1 (tree type, tree op0, enum tree_code code, tree op1,
tree *var, tree *off,
- hash_map<tree, std::pair<tree, tree> > &cache)
+ hash_map<tree, std::pair<tree, tree> > &cache,
+ unsigned *limit)
{
tree var0, var1;
tree off0, off1;
/* FALLTHROUGH */
case PLUS_EXPR:
case MINUS_EXPR:
- split_constant_offset (op0, &var0, &off0, cache);
- split_constant_offset (op1, &var1, &off1, cache);
+ if (TREE_CODE (op1) == INTEGER_CST)
+ {
+ split_constant_offset (op0, &var0, &off0, cache, limit);
+ *var = var0;
+ *off = size_binop (ocode, off0, fold_convert (ssizetype, op1));
+ return true;
+ }
+ split_constant_offset (op0, &var0, &off0, cache, limit);
+ split_constant_offset (op1, &var1, &off1, cache, limit);
*var = fold_build2 (code, type, var0, var1);
*off = size_binop (ocode, off0, off1);
return true;
if (TREE_CODE (op1) != INTEGER_CST)
return false;
- split_constant_offset (op0, &var0, &off0, cache);
+ split_constant_offset (op0, &var0, &off0, cache, limit);
*var = fold_build2 (MULT_EXPR, type, var0, op1);
*off = size_binop (MULT_EXPR, off0, fold_convert (ssizetype, op1));
return true;
if (poffset)
{
- split_constant_offset (poffset, &poffset, &off1, cache);
+ split_constant_offset (poffset, &poffset, &off1, cache, limit);
off0 = size_binop (PLUS_EXPR, off0, off1);
if (POINTER_TYPE_P (TREE_TYPE (base)))
base = fold_build_pointer_plus (base, poffset);
e = std::make_pair (op0, ssize_int (0));
}
+ if (*limit == 0)
+ return false;
+ --*limit;
+
var0 = gimple_assign_rhs1 (def_stmt);
var1 = gimple_assign_rhs2 (def_stmt);
bool res = split_constant_offset_1 (type, var0, subcode, var1,
- var, off, cache);
+ var, off, cache, limit);
if (res && use_cache)
*cache.get (op0) = std::make_pair (*var, *off);
return res;
/* Split the unconverted operand and try to prove that
wrapping isn't a problem. */
tree tmp_var, tmp_off;
- split_constant_offset (op0, &tmp_var, &tmp_off, cache);
+ split_constant_offset (op0, &tmp_var, &tmp_off, cache, limit);
/* See whether we have an SSA_NAME whose range is known
to be [A, B]. */
*off = wide_int_to_tree (ssizetype, diff);
}
else
- split_constant_offset (op0, &var0, off, cache);
+ split_constant_offset (op0, &var0, off, cache, limit);
*var = fold_convert (type, var0);
return true;
}
static void
split_constant_offset (tree exp, tree *var, tree *off,
- hash_map<tree, std::pair<tree, tree> > &cache)
+ hash_map<tree, std::pair<tree, tree> > &cache,
+ unsigned *limit)
{
tree type = TREE_TYPE (exp), op0, op1, e, o;
enum tree_code code;
code = TREE_CODE (exp);
extract_ops_from_tree (exp, &code, &op0, &op1);
- if (split_constant_offset_1 (type, op0, code, op1, &e, &o, cache))
+ if (split_constant_offset_1 (type, op0, code, op1, &e, &o, cache, limit))
{
*var = e;
*off = o;
void
split_constant_offset (tree exp, tree *var, tree *off)
{
+ unsigned limit = PARAM_VALUE (PARAM_SSA_NAME_DEF_CHAIN_LIMIT);
static hash_map<tree, std::pair<tree, tree> > *cache;
if (!cache)
cache = new hash_map<tree, std::pair<tree, tree> > (37);
- split_constant_offset (exp, var, off, *cache);
+ split_constant_offset (exp, var, off, *cache, &limit);
cache->empty ();
}
vn_reference_maybe_forwprop_address (vec<vn_reference_op_s> *ops,
unsigned int *i_p)
{
- unsigned int i = *i_p;
- vn_reference_op_t op = &(*ops)[i];
- vn_reference_op_t mem_op = &(*ops)[i - 1];
- gimple *def_stmt;
- enum tree_code code;
- poly_offset_int off;
-
- def_stmt = SSA_NAME_DEF_STMT (op->op0);
- if (!is_gimple_assign (def_stmt))
- return false;
-
- code = gimple_assign_rhs_code (def_stmt);
- if (code != ADDR_EXPR
- && code != POINTER_PLUS_EXPR)
- return false;
-
- off = poly_offset_int::from (wi::to_poly_wide (mem_op->op0), SIGNED);
+ bool changed = false;
+ vn_reference_op_t op;
- /* The only thing we have to do is from &OBJ.foo.bar add the offset
- from .foo.bar to the preceding MEM_REF offset and replace the
- address with &OBJ. */
- if (code == ADDR_EXPR)
+ do
{
- tree addr, addr_base;
- poly_int64 addr_offset;
-
- addr = gimple_assign_rhs1 (def_stmt);
- addr_base = get_addr_base_and_unit_offset (TREE_OPERAND (addr, 0),
- &addr_offset);
- /* If that didn't work because the address isn't invariant propagate
- the reference tree from the address operation in case the current
- dereference isn't offsetted. */
- if (!addr_base
- && *i_p == ops->length () - 1
- && known_eq (off, 0)
- /* This makes us disable this transform for PRE where the
- reference ops might be also used for code insertion which
- is invalid. */
- && default_vn_walk_kind == VN_WALKREWRITE)
+ unsigned int i = *i_p;
+ op = &(*ops)[i];
+ vn_reference_op_t mem_op = &(*ops)[i - 1];
+ gimple *def_stmt;
+ enum tree_code code;
+ poly_offset_int off;
+
+ def_stmt = SSA_NAME_DEF_STMT (op->op0);
+ if (!is_gimple_assign (def_stmt))
+ return changed;
+
+ code = gimple_assign_rhs_code (def_stmt);
+ if (code != ADDR_EXPR
+ && code != POINTER_PLUS_EXPR)
+ return changed;
+
+ off = poly_offset_int::from (wi::to_poly_wide (mem_op->op0), SIGNED);
+
+ /* The only thing we have to do is from &OBJ.foo.bar add the offset
+ from .foo.bar to the preceding MEM_REF offset and replace the
+ address with &OBJ. */
+ if (code == ADDR_EXPR)
{
- auto_vec<vn_reference_op_s, 32> tem;
- copy_reference_ops_from_ref (TREE_OPERAND (addr, 0), &tem);
- /* Make sure to preserve TBAA info. The only objects not
- wrapped in MEM_REFs that can have their address taken are
- STRING_CSTs. */
- if (tem.length () >= 2
- && tem[tem.length () - 2].opcode == MEM_REF)
+ tree addr, addr_base;
+ poly_int64 addr_offset;
+
+ addr = gimple_assign_rhs1 (def_stmt);
+ addr_base = get_addr_base_and_unit_offset (TREE_OPERAND (addr, 0),
+ &addr_offset);
+ /* If that didn't work because the address isn't invariant propagate
+ the reference tree from the address operation in case the current
+ dereference isn't offsetted. */
+ if (!addr_base
+ && *i_p == ops->length () - 1
+ && known_eq (off, 0)
+ /* This makes us disable this transform for PRE where the
+ reference ops might be also used for code insertion which
+ is invalid. */
+ && default_vn_walk_kind == VN_WALKREWRITE)
{
- vn_reference_op_t new_mem_op = &tem[tem.length () - 2];
- new_mem_op->op0
- = wide_int_to_tree (TREE_TYPE (mem_op->op0),
- wi::to_poly_wide (new_mem_op->op0));
+ auto_vec<vn_reference_op_s, 32> tem;
+ copy_reference_ops_from_ref (TREE_OPERAND (addr, 0), &tem);
+ /* Make sure to preserve TBAA info. The only objects not
+ wrapped in MEM_REFs that can have their address taken are
+ STRING_CSTs. */
+ if (tem.length () >= 2
+ && tem[tem.length () - 2].opcode == MEM_REF)
+ {
+ vn_reference_op_t new_mem_op = &tem[tem.length () - 2];
+ new_mem_op->op0
+ = wide_int_to_tree (TREE_TYPE (mem_op->op0),
+ wi::to_poly_wide (new_mem_op->op0));
+ }
+ else
+ gcc_assert (tem.last ().opcode == STRING_CST);
+ ops->pop ();
+ ops->pop ();
+ ops->safe_splice (tem);
+ --*i_p;
+ return true;
}
- else
- gcc_assert (tem.last ().opcode == STRING_CST);
- ops->pop ();
- ops->pop ();
- ops->safe_splice (tem);
- --*i_p;
- return true;
+ if (!addr_base
+ || TREE_CODE (addr_base) != MEM_REF
+ || (TREE_CODE (TREE_OPERAND (addr_base, 0)) == SSA_NAME
+ && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (TREE_OPERAND (addr_base,
+ 0))))
+ return changed;
+
+ off += addr_offset;
+ off += mem_ref_offset (addr_base);
+ op->op0 = TREE_OPERAND (addr_base, 0);
+ }
+ else
+ {
+ tree ptr, ptroff;
+ ptr = gimple_assign_rhs1 (def_stmt);
+ ptroff = gimple_assign_rhs2 (def_stmt);
+ if (TREE_CODE (ptr) != SSA_NAME
+ || SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ptr)
+ /* Make sure to not endlessly recurse.
+ See gcc.dg/tree-ssa/20040408-1.c for an example. Can easily
+ happen when we value-number a PHI to its backedge value. */
+ || SSA_VAL (ptr) == op->op0
+ || !poly_int_tree_p (ptroff))
+ return changed;
+
+ off += wi::to_poly_offset (ptroff);
+ op->op0 = ptr;
}
- if (!addr_base
- || TREE_CODE (addr_base) != MEM_REF
- || (TREE_CODE (TREE_OPERAND (addr_base, 0)) == SSA_NAME
- && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (TREE_OPERAND (addr_base, 0))))
- return false;
-
- off += addr_offset;
- off += mem_ref_offset (addr_base);
- op->op0 = TREE_OPERAND (addr_base, 0);
- }
- else
- {
- tree ptr, ptroff;
- ptr = gimple_assign_rhs1 (def_stmt);
- ptroff = gimple_assign_rhs2 (def_stmt);
- if (TREE_CODE (ptr) != SSA_NAME
- || SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ptr)
- /* Make sure to not endlessly recurse.
- See gcc.dg/tree-ssa/20040408-1.c for an example. Can easily
- happen when we value-number a PHI to its backedge value. */
- || SSA_VAL (ptr) == op->op0
- || !poly_int_tree_p (ptroff))
- return false;
- off += wi::to_poly_offset (ptroff);
- op->op0 = ptr;
+ mem_op->op0 = wide_int_to_tree (TREE_TYPE (mem_op->op0), off);
+ if (tree_fits_shwi_p (mem_op->op0))
+ mem_op->off = tree_to_shwi (mem_op->op0);
+ else
+ mem_op->off = -1;
+ /* ??? Can end up with endless recursion here!?
+ gcc.c-torture/execute/strcmp-1.c */
+ if (TREE_CODE (op->op0) == SSA_NAME)
+ op->op0 = SSA_VAL (op->op0);
+ if (TREE_CODE (op->op0) != SSA_NAME)
+ op->opcode = TREE_CODE (op->op0);
+
+ changed = true;
}
+ /* Tail-recurse. */
+ while (TREE_CODE (op->op0) == SSA_NAME);
- mem_op->op0 = wide_int_to_tree (TREE_TYPE (mem_op->op0), off);
- if (tree_fits_shwi_p (mem_op->op0))
- mem_op->off = tree_to_shwi (mem_op->op0);
- else
- mem_op->off = -1;
- /* ??? Can end up with endless recursion here!?
- gcc.c-torture/execute/strcmp-1.c */
- if (TREE_CODE (op->op0) == SSA_NAME)
- op->op0 = SSA_VAL (op->op0);
- if (TREE_CODE (op->op0) != SSA_NAME)
- op->opcode = TREE_CODE (op->op0);
-
- /* And recurse. */
- if (TREE_CODE (op->op0) == SSA_NAME)
- vn_reference_maybe_forwprop_address (ops, i_p);
- else if (TREE_CODE (op->op0) == ADDR_EXPR)
+ /* Fold a remaining *&. */
+ if (TREE_CODE (op->op0) == ADDR_EXPR)
vn_reference_fold_indirect (ops, i_p);
- return true;
+
+ return changed;
}
/* Optimize the reference REF to a constant if possible or return
/* Performing a topological sort is probably overkill, this will
most likely run in slightly superlinear time, rather than the
- pathological quadratic worst case. */
+ pathological quadratic worst case.
+ But iterate from max SSA name version to min one because
+ that mimics allocation order during code generation behavior best.
+ Use an array for this which we compact on-the-fly with a NULL
+ marker moving towards the end of the vector. */
+ auto_vec<tree, 16> names;
+ names.reserve (bitmap_count_bits (toremove) + 1);
+ names.quick_push (NULL_TREE);
+ EXECUTE_IF_SET_IN_BITMAP (toremove, 0, j, bi)
+ names.quick_push (ssa_name (j));
+
+ bitmap_tree_view (toremove);
while (!bitmap_empty_p (toremove))
{
- unsigned to_remove_bit = -1U;
- EXECUTE_IF_SET_IN_BITMAP (toremove, 0, j, bi)
+ j = names.length () - 1;
+ for (unsigned i = names.length () - 1; names[i];)
{
- if (to_remove_bit != -1U)
- {
- bitmap_clear_bit (toremove, to_remove_bit);
- to_remove_bit = -1U;
- }
-
bool remove_now = true;
- tree var = ssa_name (j);
+ tree var = names[i];
gimple *stmt;
imm_use_iterator uit;
gsi_remove (&gsi, true);
release_defs (def);
}
-
- to_remove_bit = j;
+ bitmap_clear_bit (toremove, SSA_NAME_VERSION (var));
}
+ else
+ --i;
+ if (--j != i)
+ names[i] = names[j];
}
- if (to_remove_bit != -1U)
- bitmap_clear_bit (toremove, to_remove_bit);
}
-
+ bitmap_list_view (toremove);
}
/* Verify virtual SSA form. */
The loop computes the range of the final offset for expressions such
as (A + i0 + ... + iN)[CSTOFF] where i0 through iN are SSA_NAMEs in
some range. */
- while (TREE_CODE (arg) == SSA_NAME)
+ const unsigned limit = PARAM_VALUE (PARAM_SSA_NAME_DEF_CHAIN_LIMIT);
+ for (unsigned n = 0; TREE_CODE (arg) == SSA_NAME && n < limit; ++n)
{
gimple *def = SSA_NAME_DEF_STMT (arg);
if (!is_gimple_assign (def))