2019-01-02 Martin Sebor <msebor@redhat.com>
Jeff Law <law@redhat.com>
+
+ * gimple-fold.c (gimple_fold_builtin_strlen): Use set_strlen_range
+ rather than set_range_info.
+ * tree-ssa-strlen.c (set_strlen_range): Extracted from
+ maybe_set_strlen_range. Handle potentially boundary crossing
+ cases more conservatively.
+ (maybe_set_strlen_range): Parts refactored into set_strlen_range.
+ Call set_strlen_range.
+ * tree-ssa-strlen.h (set_strlen_range): Add prototype.
+
PR middle-end/88663
* gimple-fold.c (get_range_strlen): Update prototype to no longer
need the flexp argument.
return true;
}
+ /* Set the strlen() range to [0, MAXLEN]. */
if (tree lhs = gimple_call_lhs (stmt))
- if (TREE_CODE (lhs) == SSA_NAME
- && INTEGRAL_TYPE_P (TREE_TYPE (lhs)))
- set_range_info (lhs, VR_RANGE, minlen, maxlen);
+ set_strlen_range (lhs, maxlen);
return false;
}
+2019-01-02 Martin Sebor <msebor@redhat.com>
+ Jeff Law <law@redhat.com>
+
+ * gcc.dg/strlenopt-36.c: Update.
+ * gcc.dg/strlenopt-45.c: Update.
+ * gcc.c-torture/execute/strlen-5.c: New test.
+ * gcc.c-torture/execute/strlen-6.c: New test.
+ * gcc.c-torture/execute/strlen-7.c: New test.
+
2019-01-02 Jakub Jelinek <jakub@redhat.com>
PR testsuite/87304
--- /dev/null
+/* Test to verify that even strictly undefined strlen() calls with
+ unterminated character arrays yield the "expected" results when
+ the terminating nul is present in a subsequent suobobject. */
+
+extern __SIZE_TYPE__ strlen (const char *);
+
+unsigned nfails;
+
+#define A(expr, N) \
+ do { \
+ const char *s = (expr); \
+ unsigned n = strlen (s); \
+ ((n == N) \
+ ? 0 \
+ : (__builtin_printf ("line %i: strlen (%s = \"%s\")" \
+ " == %u failed\n", \
+ __LINE__, #expr, s, N), \
+ ++nfails)); \
+ } while (0)
+
+
+int idx;
+
+
+const char ca[][4] = {
+ { '1', '2', '3', '4' }, { '5' },
+ { '1', '2', '3', '4' }, { '5', '6' },
+ { '1', '2', '3', '4' }, { '5', '6', '7' },
+ { '1', '2', '3', '4' }, { '5', '6', '7', '8' },
+ { '9' }
+};
+
+static void test_const_global_arrays (void)
+{
+ A (ca[0], 5);
+ A (&ca[0][0], 5);
+ A (&ca[0][1], 4);
+ A (&ca[0][3], 2);
+
+ int i = 0;
+ A (ca[i], 5);
+ A (&ca[i][0], 5);
+ A (&ca[i][1], 4);
+ A (&ca[i][3], 2);
+
+ int j = i;
+ A (&ca[i][i], 5);
+ A (&ca[i][j + 1], 4);
+ A (&ca[i][j + 2], 3);
+
+ A (&ca[idx][i], 5);
+ A (&ca[idx][j + 1], 4);
+ A (&ca[idx][j + 2], 3);
+
+ A (&ca[idx][idx], 5);
+ A (&ca[idx][idx + 1], 4);
+ A (&ca[idx][idx + 2], 3);
+
+ A (&ca[0][++j], 4);
+ A (&ca[0][++j], 3);
+ A (&ca[0][++j], 2);
+
+ if (j != 3)
+ ++nfails;
+}
+
+
+static void test_const_local_arrays (void)
+{
+ const char a[][4] = {
+ { '1', '2', '3', '4' }, { '5' },
+ { '1', '2', '3', '4' }, { '5', '6' },
+ { '1', '2', '3', '4' }, { '5', '6', '7' },
+ { '1', '2', '3', '4' }, { '5', '6', '7', '8' },
+ { '9' }
+ };
+
+ A (a[0], 5);
+ A (&a[0][0], 5);
+ A (&a[0][1], 4);
+ A (&a[0][3], 2);
+
+ int i = 0;
+ A (a[i], 5);
+ A (&a[i][0], 5);
+ A (&a[i][1], 4);
+ A (&a[i][3], 2);
+
+ int j = i;
+ A (&a[i][i], 5);
+ A (&a[i][j + 1], 4);
+ A (&a[i][j + 2], 3);
+
+ A (&a[idx][i], 5);
+ A (&a[idx][j + 1], 4);
+ A (&a[idx][j + 2], 3);
+
+ A (&a[idx][idx], 5);
+ A (&a[idx][idx + 1], 4);
+ A (&a[idx][idx + 2], 3);
+
+ A (&a[0][++j], 4);
+ A (&a[0][++j], 3);
+ A (&a[0][++j], 2);
+
+ if (j != 3)
+ ++nfails;
+}
+
+
+char va[][4] = {
+ { '1', '2', '3', '4' }, { '5' },
+ { '1', '2', '3', '4' }, { '5', '6' },
+ { '1', '2', '3', '4' }, { '5', '6', '7' },
+ { '1', '2', '3', '4' }, { '5', '6', '7', '8' },
+ { '9' }
+};
+
+static void test_nonconst_global_arrays (void)
+{
+ {
+ A (va[0], 5);
+ A (&va[0][0], 5);
+ A (&va[0][1], 4);
+ A (&va[0][3], 2);
+
+ int i = 0;
+ A (va[i], 5);
+ A (&va[i][0], 5);
+ A (&va[i][1], 4);
+ A (&va[i][3], 2);
+
+ int j = i;
+ A (&va[i][i], 5);
+ A (&va[i][j + 1], 4);
+ A (&va[i][j + 2], 3);
+
+ A (&va[idx][i], 5);
+ A (&va[idx][j + 1], 4);
+ A (&va[idx][j + 2], 3);
+
+ A (&va[idx][idx], 5);
+ A (&va[idx][idx + 1], 4);
+ A (&va[idx][idx + 2], 3);
+ }
+
+ {
+ A (va[2], 6);
+ A (&va[2][0], 6);
+ A (&va[2][1], 5);
+ A (&va[2][3], 3);
+
+ int i = 2;
+ A (va[i], 6);
+ A (&va[i][0], 6);
+ A (&va[i][1], 5);
+ A (&va[i][3], 3);
+
+ int j = i - 1;
+ A (&va[i][j - 1], 6);
+ A (&va[i][j], 5);
+ A (&va[i][j + 1], 4);
+
+ A (&va[idx + 2][i - 1], 5);
+ A (&va[idx + 2][j], 5);
+ A (&va[idx + 2][j + 1], 4);
+ }
+
+ int j = 0;
+
+ A (&va[0][++j], 4);
+ A (&va[0][++j], 3);
+ A (&va[0][++j], 2);
+
+ if (j != 3)
+ ++nfails;
+}
+
+
+static void test_nonconst_local_arrays (void)
+{
+ char a[][4] = {
+ { '1', '2', '3', '4' }, { '5' },
+ { '1', '2', '3', '4' }, { '5', '6' },
+ { '1', '2', '3', '4' }, { '5', '6', '7' },
+ { '1', '2', '3', '4' }, { '5', '6', '7', '8' },
+ { '9' }
+ };
+
+ A (a[0], 5);
+ A (&a[0][0], 5);
+ A (&a[0][1], 4);
+ A (&a[0][3], 2);
+
+ int i = 0;
+ A (a[i], 5);
+ A (&a[i][0], 5);
+ A (&a[i][1], 4);
+ A (&a[i][3], 2);
+
+ int j = i;
+ A (&a[i][i], 5);
+ A (&a[i][j + 1], 4);
+ A (&a[i][j + 2], 3);
+
+ A (&a[idx][i], 5);
+ A (&a[idx][j + 1], 4);
+ A (&a[idx][j + 2], 3);
+
+ A (&a[idx][idx], 5);
+ A (&a[idx][idx + 1], 4);
+ A (&a[idx][idx + 2], 3);
+
+ A (&a[0][++j], 4);
+ A (&a[0][++j], 3);
+ A (&a[0][++j], 2);
+
+ if (j != 3)
+ ++nfails;
+}
+
+
+struct MemArrays { char a[4], b[4]; };
+
+const struct MemArrays cma[] = {
+ { { '1', '2', '3', '4' }, { '5' } },
+ { { '1', '2', '3', '4' }, { '5', '6' } },
+ { { '1', '2', '3', '4' }, { '5', '6' } },
+ { { '1', '2', '3', '4' }, { '5', '6', '7' } },
+ { { '1', '2', '3', '4' }, { '5', '6', '7', '8' } },
+ { { '9' }, { '\0' } }
+};
+
+static void test_const_global_member_arrays (void)
+{
+ {
+ A (cma[0].a, 5);
+ A (&cma[0].a[0], 5);
+ A (&cma[0].a[1], 4);
+ A (&cma[0].a[2], 3);
+
+ int i = 0;
+ A (cma[i].a, 5);
+ A (&cma[i].a[0], 5);
+ A (&cma[i].a[1], 4);
+ A (&cma[i].a[2], 3);
+
+ int j = i;
+ A (&cma[i].a[j], 5);
+ A (&cma[i].a[j + 1], 4);
+ A (&cma[i].a[j + 2], 3);
+
+ A (&cma[idx].a[i], 5);
+ A (&cma[idx].a[j + 1], 4);
+ A (&cma[idx].a[j + 2], 3);
+
+ A (&cma[idx].a[idx], 5);
+ A (&cma[idx].a[idx + 1], 4);
+ A (&cma[idx].a[idx + 2], 3);
+ }
+
+ {
+ A (cma[1].a, 6);
+ A (&cma[1].a[0], 6);
+ A (&cma[1].a[1], 5);
+ A (&cma[1].a[2], 4);
+
+ int i = 1;
+ A (cma[i].a, 6);
+ A (&cma[i].a[0], 6);
+ A (&cma[i].a[1], 5);
+ A (&cma[i].a[2], 4);
+
+ int j = i - 1;
+ A (&cma[i].a[j], 6);
+ A (&cma[i].a[j + 1], 5);
+ A (&cma[i].a[j + 2], 4);
+
+ A (&cma[idx + 1].a[j], 6);
+ A (&cma[idx + 1].a[j + 1], 5);
+ A (&cma[idx + 1].a[j + 2], 4);
+
+ A (&cma[idx + 1].a[idx], 6);
+ A (&cma[idx + 1].a[idx + 1], 5);
+ A (&cma[idx + 1].a[idx + 2], 4);
+ }
+
+ {
+ A (cma[4].a, 9);
+ A (&cma[4].a[0], 9);
+ A (&cma[4].a[1], 8);
+ A (&cma[4].b[0], 5);
+
+ int i = 4;
+ A (cma[i].a, 9);
+ A (&cma[i].a[0], 9);
+ A (&cma[i].a[1], 8);
+ A (&cma[i].b[0], 5);
+
+ int j = i - 1;
+ A (&cma[i].a[j], 6);
+ A (&cma[i].a[j + 1], 5);
+ A (&cma[i].b[j - 2], 4);
+
+ A (&cma[idx + 4].a[j], 6);
+ A (&cma[idx + 4].a[j + 1], 5);
+ A (&cma[idx + 4].b[j - 2], 4);
+
+ A (&cma[idx + 4].a[idx], 9);
+ A (&cma[idx + 4].a[idx + 1], 8);
+ A (&cma[idx + 4].b[idx + 1], 4);
+ }
+}
+
+
+static void test_const_local_member_arrays (void)
+{
+ const struct MemArrays ma[] = {
+ { { '1', '2', '3', '4' }, { '5' } },
+ { { '1', '2', '3', '4' }, { '5', '6' } },
+ { { '1', '2', '3', '4' }, { '5', '6' } },
+ { { '1', '2', '3', '4' }, { '5', '6', '7' } },
+ { { '1', '2', '3', '4' }, { '5', '6', '7', '8' } },
+ { { '9' }, { '\0' } }
+ };
+
+ {
+ A (ma[0].a, 5);
+ A (&ma[0].a[0], 5);
+ A (&ma[0].a[1], 4);
+ A (&ma[0].a[2], 3);
+
+ int i = 0;
+ A (ma[i].a, 5);
+ A (&ma[i].a[0], 5);
+ A (&ma[i].a[1], 4);
+ A (&ma[i].a[2], 3);
+
+ int j = i;
+ A (&ma[i].a[j], 5);
+ A (&ma[i].a[j + 1], 4);
+ A (&ma[i].a[j + 2], 3);
+
+ A (&ma[idx].a[i], 5);
+ A (&ma[idx].a[j + 1], 4);
+ A (&ma[idx].a[j + 2], 3);
+
+ A (&ma[idx].a[idx], 5);
+ A (&ma[idx].a[idx + 1], 4);
+ A (&ma[idx].a[idx + 2], 3);
+ }
+
+ {
+ A (ma[1].a, 6);
+ A (&ma[1].a[0], 6);
+ A (&ma[1].a[1], 5);
+ A (&ma[1].a[2], 4);
+
+ int i = 1;
+ A (ma[i].a, 6);
+ A (&ma[i].a[0], 6);
+ A (&ma[i].a[1], 5);
+ A (&ma[i].a[2], 4);
+
+ int j = i - 1;
+ A (&ma[i].a[j], 6);
+ A (&ma[i].a[j + 1], 5);
+ A (&ma[i].a[j + 2], 4);
+
+ A (&ma[idx + 1].a[j], 6);
+ A (&ma[idx + 1].a[j + 1], 5);
+ A (&ma[idx + 1].a[j + 2], 4);
+
+ A (&ma[idx + 1].a[idx], 6);
+ A (&ma[idx + 1].a[idx + 1], 5);
+ A (&ma[idx + 1].a[idx + 2], 4);
+ }
+
+ {
+ A (ma[4].a, 9);
+ A (&ma[4].a[0], 9);
+ A (&ma[4].a[1], 8);
+ A (&ma[4].b[0], 5);
+
+ int i = 4;
+ A (ma[i].a, 9);
+ A (&ma[i].a[0], 9);
+ A (&ma[i].a[1], 8);
+ A (&ma[i].b[0], 5);
+
+ int j = i - 1;
+ A (&ma[i].a[j], 6);
+ A (&ma[i].a[j + 1], 5);
+ A (&ma[i].b[j - 2], 4);
+
+ A (&ma[idx + 4].a[j], 6);
+ A (&ma[idx + 4].a[j + 1], 5);
+ A (&ma[idx + 4].b[j - 2], 4);
+
+ A (&ma[idx + 4].a[idx], 9);
+ A (&ma[idx + 4].a[idx + 1], 8);
+ A (&ma[idx + 4].b[idx + 1], 4);
+ }
+}
+
+struct MemArrays vma[] = {
+ { { '1', '2', '3', '4' }, { '5' } },
+ { { '1', '2', '3', '4' }, { '5', '6' } },
+ { { '1', '2', '3', '4' }, { '5', '6' } },
+ { { '1', '2', '3', '4' }, { '5', '6', '7' } },
+ { { '1', '2', '3', '4' }, { '5', '6', '7', '8' } },
+ { { '9' }, { '\0' } }
+};
+
+static void test_nonconst_global_member_arrays (void)
+{
+ {
+ A (vma[0].a, 5);
+ A (&vma[0].a[0], 5);
+ A (&vma[0].a[1], 4);
+ A (&vma[0].a[2], 3);
+
+ int i = 0;
+ A (vma[i].a, 5);
+ A (&vma[i].a[0], 5);
+ A (&vma[i].a[1], 4);
+ A (&vma[i].a[2], 3);
+
+ int j = i;
+ A (&vma[i].a[j], 5);
+ A (&vma[i].a[j + 1], 4);
+ A (&vma[i].a[j + 2], 3);
+
+ A (&vma[idx].a[i], 5);
+ A (&vma[idx].a[j + 1], 4);
+ A (&vma[idx].a[j + 2], 3);
+
+ A (&vma[idx].a[idx], 5);
+ A (&vma[idx].a[idx + 1], 4);
+ A (&vma[idx].a[idx + 2], 3);
+ }
+
+ {
+ A (vma[1].a, 6);
+ A (&vma[1].a[0], 6);
+ A (&vma[1].a[1], 5);
+ A (&vma[1].a[2], 4);
+
+ int i = 1;
+ A (vma[i].a, 6);
+ A (&vma[i].a[0], 6);
+ A (&vma[i].a[1], 5);
+ A (&vma[i].a[2], 4);
+
+ int j = i - 1;
+ A (&vma[i].a[j], 6);
+ A (&vma[i].a[j + 1], 5);
+ A (&vma[i].a[j + 2], 4);
+
+ A (&vma[idx + 1].a[j], 6);
+ A (&vma[idx + 1].a[j + 1], 5);
+ A (&vma[idx + 1].a[j + 2], 4);
+
+ A (&vma[idx + 1].a[idx], 6);
+ A (&vma[idx + 1].a[idx + 1], 5);
+ A (&vma[idx + 1].a[idx + 2], 4);
+ }
+
+ {
+ A (vma[4].a, 9);
+ A (&vma[4].a[0], 9);
+ A (&vma[4].a[1], 8);
+ A (&vma[4].b[0], 5);
+
+ int i = 4;
+ A (vma[i].a, 9);
+ A (&vma[i].a[0], 9);
+ A (&vma[i].a[1], 8);
+ A (&vma[i].b[0], 5);
+
+ int j = i - 1;
+ A (&vma[i].a[j], 6);
+ A (&vma[i].a[j + 1], 5);
+ A (&vma[i].b[j - 2], 4);
+
+ A (&vma[idx + 4].a[j], 6);
+ A (&vma[idx + 4].a[j + 1], 5);
+ A (&vma[idx + 4].b[j - 2], 4);
+
+ A (&vma[idx + 4].a[idx], 9);
+ A (&vma[idx + 4].a[idx + 1], 8);
+ A (&vma[idx + 4].b[idx + 1], 4);
+ }
+}
+
+
+static void test_nonconst_local_member_arrays (void)
+{
+ struct MemArrays ma[] = {
+ { { '1', '2', '3', '4' }, { '5' } },
+ { { '1', '2', '3', '4' }, { '5', '6' } },
+ { { '1', '2', '3', '4' }, { '5', '6' } },
+ { { '1', '2', '3', '4' }, { '5', '6', '7' } },
+ { { '1', '2', '3', '4' }, { '5', '6', '7', '8' } },
+ { { '9' }, { '\0' } }
+ };
+
+ {
+ A (ma[0].a, 5);
+ A (&ma[0].a[0], 5);
+ A (&ma[0].a[1], 4);
+ A (&ma[0].a[2], 3);
+
+ int i = 0;
+ A (ma[i].a, 5);
+ A (&ma[i].a[0], 5);
+ A (&ma[i].a[1], 4);
+ A (&ma[i].a[2], 3);
+
+ int j = i;
+ A (&ma[i].a[j], 5);
+ A (&ma[i].a[j + 1], 4);
+ A (&ma[i].a[j + 2], 3);
+
+ A (&ma[idx].a[i], 5);
+ A (&ma[idx].a[j + 1], 4);
+ A (&ma[idx].a[j + 2], 3);
+
+ A (&ma[idx].a[idx], 5);
+ A (&ma[idx].a[idx + 1], 4);
+ A (&ma[idx].a[idx + 2], 3);
+ }
+
+ {
+ A (ma[1].a, 6);
+ A (&ma[1].a[0], 6);
+ A (&ma[1].a[1], 5);
+ A (&ma[1].a[2], 4);
+
+ int i = 1;
+ A (ma[i].a, 6);
+ A (&ma[i].a[0], 6);
+ A (&ma[i].a[1], 5);
+ A (&ma[i].a[2], 4);
+
+ int j = i - 1;
+ A (&ma[i].a[j], 6);
+ A (&ma[i].a[j + 1], 5);
+ A (&ma[i].a[j + 2], 4);
+
+ A (&ma[idx + 1].a[j], 6);
+ A (&ma[idx + 1].a[j + 1], 5);
+ A (&ma[idx + 1].a[j + 2], 4);
+
+ A (&ma[idx + 1].a[idx], 6);
+ A (&ma[idx + 1].a[idx + 1], 5);
+ A (&ma[idx + 1].a[idx + 2], 4);
+ }
+
+ {
+ A (ma[4].a, 9);
+ A (&ma[4].a[0], 9);
+ A (&ma[4].a[1], 8);
+ A (&ma[4].b[0], 5);
+
+ int i = 4;
+ A (ma[i].a, 9);
+ A (&ma[i].a[0], 9);
+ A (&ma[i].a[1], 8);
+ A (&ma[i].b[0], 5);
+
+ int j = i - 1;
+ A (&ma[i].a[j], 6);
+ A (&ma[i].a[j + 1], 5);
+ A (&ma[i].b[j - 2], 4);
+
+ A (&ma[idx + 4].a[j], 6);
+ A (&ma[idx + 4].a[j + 1], 5);
+ A (&ma[idx + 4].b[j - 2], 4);
+
+ A (&ma[idx + 4].a[idx], 9);
+ A (&ma[idx + 4].a[idx + 1], 8);
+ A (&ma[idx + 4].b[idx + 1], 4);
+ }
+}
+
+
+union UnionMemberArrays
+{
+ struct { char a[4], b[4]; } a;
+ struct { char a[8]; } c;
+};
+
+const union UnionMemberArrays cu = {
+ { { '1', '2', '3', '4' }, { '5', } }
+};
+
+static void test_const_union_member_arrays (void)
+{
+ A (cu.a.a, 5);
+ A (cu.a.b, 1);
+ A (cu.c.a, 5);
+
+ const union UnionMemberArrays clu = {
+ { { '1', '2', '3', '4' }, { '5', '6' } }
+ };
+
+ A (clu.a.a, 6);
+ A (clu.a.b, 2);
+ A (clu.c.a, 6);
+}
+
+
+union UnionMemberArrays vu = {
+ { { '1', '2', '3', '4' }, { '5', '6' } }
+};
+
+static void test_nonconst_union_member_arrays (void)
+{
+ A (vu.a.a, 6);
+ A (vu.a.b, 2);
+ A (vu.c.a, 6);
+
+ union UnionMemberArrays lvu = {
+ { { '1', '2', '3', '4' }, { '5', '6', '7' } }
+ };
+
+ A (lvu.a.a, 7);
+ A (lvu.a.b, 3);
+ A (lvu.c.a, 7);
+}
+
+
+int main (void)
+{
+ test_const_global_arrays ();
+ test_const_local_arrays ();
+
+ test_nonconst_global_arrays ();
+ test_nonconst_local_arrays ();
+
+ test_const_global_member_arrays ();
+ test_const_local_member_arrays ();
+
+ test_nonconst_global_member_arrays ();
+ test_nonconst_local_member_arrays ();
+
+ test_const_union_member_arrays ();
+ test_nonconst_union_member_arrays ();
+
+ if (nfails)
+ __builtin_abort ();
+}
--- /dev/null
+/* Test to verify that strlen() calls with conditional expressions
+ and unterminated arrays or pointers to such things as arguments
+ are evaluated without making assumptions about array sizes. */
+
+extern __SIZE_TYPE__ strlen (const char *);
+
+unsigned nfails;
+
+#define A(expr, N) \
+ do { \
+ const char *_s = (expr); \
+ unsigned _n = strlen (_s); \
+ ((_n == N) \
+ ? 0 \
+ : (__builtin_printf ("line %i: strlen ((%s) = (\"%s\"))" \
+ " == %u failed\n", \
+ __LINE__, #expr, _s, N), \
+ ++nfails)); \
+ } while (0)
+
+
+volatile int i0 = 0;
+
+const char ca[2][3] = { "12" };
+const char cb[2][3] = { { '1', '2', '3', }, { '4' } };
+
+char va[2][3] = { "123" };
+char vb[2][3] = { { '1', '2', '3', }, { '4', '5' } };
+
+const char *s = "123456";
+
+
+static void test_binary_cond_expr_global (void)
+{
+ A (i0 ? "1" : ca[0], 2);
+ A (i0 ? ca[0] : "123", 3);
+
+ /* The call to strlen (cb[0]) is strictly undefined because the array
+ isn't nul-terminated. This test verifies that the strlen range
+ optimization doesn't assume that the argument is necessarily nul
+ terminated.
+ Ditto for strlen (vb[0]). */
+ A (i0 ? "1" : cb[0], 4); /* GCC 8.2 failure */
+ A (i0 ? cb[0] : "12", 2);
+
+ A (i0 ? "1" : va[0], 3); /* GCC 8.2 failure */
+ A (i0 ? va[0] : "1234", 4);
+
+ A (i0 ? "1" : vb[0], 5); /* GCC 8.2 failure */
+ A (i0 ? vb[0] : "12", 2);
+}
+
+
+static void test_binary_cond_expr_local (void)
+{
+ const char lca[2][3] = { "12" };
+ const char lcb[2][3] = { { '1', '2', '3', }, { '4' } };
+
+ char lva[2][3] = { "123" };
+ char lvb[2][3] = { { '1', '2', '3', }, { '4', '5' } };
+
+ /* Also undefined as above. */
+ A (i0 ? "1" : lca[0], 2);
+ A (i0 ? lca[0] : "123", 3);
+
+ A (i0 ? "1" : lcb[0], 4); /* GCC 8.2 failure */
+ A (i0 ? lcb[0] : "12", 2);
+
+ A (i0 ? "1" : lva[0], 3); /* GCC 8.2 failure */
+ A (i0 ? lva[0] : "1234", 4);
+
+ A (i0 ? "1" : lvb[0], 5); /* GCC 8.2 failure */
+ A (i0 ? lvb[0] : "12", 2);
+}
+
+
+static void test_ternary_cond_expr (void)
+{
+ /* Also undefined. */
+ A (i0 == 0 ? s : i0 == 1 ? vb[0] : "123", 6);
+ A (i0 == 0 ? vb[0] : i0 == 1 ? s : "123", 5);
+ A (i0 == 0 ? "123" : i0 == 1 ? s : vb[0], 3);
+}
+
+
+const char (*pca)[3] = &ca[0];
+const char (*pcb)[3] = &cb[0];
+
+char (*pva)[3] = &va[0];
+char (*pvb)[3] = &vb[0];
+
+static void test_binary_cond_expr_arrayptr (void)
+{
+ /* Also undefined. */
+ A (i0 ? *pca : *pcb, 4); /* GCC 8.2 failure */
+ A (i0 ? *pcb : *pca, 2);
+
+ A (i0 ? *pva : *pvb, 5); /* GCC 8.2 failure */
+ A (i0 ? *pvb : *pva, 3);
+}
+
+
+int main (void)
+{
+ test_binary_cond_expr_global ();
+ test_binary_cond_expr_local ();
+
+ test_ternary_cond_expr ();
+ test_binary_cond_expr_arrayptr ();
+
+ if (nfails)
+ __builtin_abort ();
+}
--- /dev/null
+/* Test to verify that a strlen() call with a pointer to a dynamic type
+ doesn't make assumptions based on the static type of the original
+ pointer. See g++.dg/init/strlen.C for the corresponding C++ test. */
+
+struct A { int i; char a[1]; void (*p)(); };
+struct B { char a[sizeof (struct A) - __builtin_offsetof (struct A, a)]; };
+
+__attribute__ ((noipa)) void
+init (char *d, const char *s)
+{
+ __builtin_strcpy (d, s);
+}
+
+struct B b;
+
+__attribute__ ((noipa)) void
+test_dynamic_type (struct A *p)
+{
+ /* The following call is undefined because it writes past the end
+ of the p->a subobject, but the corresponding GIMPLE considers
+ it valid and there's apparently no way to distinguish invalid
+ cases from ones like it that might be valid. If/when GIMPLE
+ changes to make this possible this test can be removed. */
+ char *q = (char*)__builtin_memcpy (p->a, &b, sizeof b);
+
+ init (q, "foobar");
+
+ if (6 != __builtin_strlen (q))
+ __builtin_abort();
+}
+
+int main (void)
+{
+ struct A *p = (struct A*)__builtin_malloc (sizeof *p);
+ test_dynamic_type (p);
+ return 0;
+}
extern char a0[0]; /* Intentionally not tested here. */
extern char ax[]; /* Same. */
-struct MemArrays {
- char a7[7], a6[6], a5[5], a4[4], a3[3], a2[2], a1[1];
- char a0[0]; /* Not tested here. */
-};
-
-struct NestedMemArrays {
- struct { char a7[7]; } ma7;
- struct { char a6[6]; } ma6;
- struct { char a5[5]; } ma5;
- struct { char a4[4]; } ma4;
- struct { char a3[3]; } ma3;
- struct { char a2[2]; } ma2;
- struct { char a1[1]; } ma1;
- struct { char a0[0]; } ma0;
- char last;
-};
-
extern void failure_on_line (int);
#define TEST_FAIL(line) \
T (strlen (a1) == 0); */
}
-void test_memarray (struct MemArrays *ma)
-{
- T (strlen (ma->a7) < sizeof ma->a7);
- T (strlen (ma->a6) < sizeof ma->a6);
- T (strlen (ma->a5) < sizeof ma->a5);
- T (strlen (ma->a4) < sizeof ma->a4);
- T (strlen (ma->a3) < sizeof ma->a3);
-
- /* The following two calls are folded too early which defeats
- the strlen() optimization.
- T (strlen (ma->a2) == 1);
- T (strlen (ma->a1) == 0); */
-}
-
-/* Verify that the range of strlen(A) of a last struct member is
- set even when the array is the sole member of a struct as long
- as the struct itself is a member of another struct. The converse
- is tested in stlenopt-37.c. */
-void test_nested_memarray (struct NestedMemArrays *ma)
-{
- T (strlen (ma->ma7.a7) < sizeof ma->ma7.a7);
- T (strlen (ma->ma6.a6) < sizeof ma->ma6.a6);
- T (strlen (ma->ma5.a5) < sizeof ma->ma5.a5);
- T (strlen (ma->ma4.a4) < sizeof ma->ma4.a4);
- T (strlen (ma->ma3.a3) < sizeof ma->ma3.a3);
-
- /* The following two calls are folded too early which defeats
- the strlen() optimization.
- T (strlen (ma->ma2.a2) == 1);
- T (strlen (ma->ma1.a1) == 0); */
-}
-
/* { dg-final { scan-tree-dump-not "failure_on_line" "optimized" } } */
Test to verify that strnlen built-in expansion works correctly
in the absence of tree strlen optimization.
{ dg-do compile }
- { dg-options "-O2 -Wall -fdump-tree-optimized" } */
+ { dg-options "-O2 -Wall -Wno-stringop-overflow -fdump-tree-optimized" } */
#include "strlenopt.h"
ELIM (strnlen (a3_7[0], 1) < 2);
ELIM (strnlen (a3_7[0], 2) < 3);
ELIM (strnlen (a3_7[0], 3) < 4);
- ELIM (strnlen (a3_7[0], 9) < 8);
- ELIM (strnlen (a3_7[0], PTRDIFF_MAX) < 8);
- ELIM (strnlen (a3_7[0], SIZE_MAX) < 8);
- ELIM (strnlen (a3_7[0], -1) < 8);
+ ELIM (strnlen (a3_7[0], 9) <= 9);
+ ELIM (strnlen (a3_7[0], PTRDIFF_MAX) <= sizeof a3_7);
+ ELIM (strnlen (a3_7[0], SIZE_MAX) <= sizeof a3_7);
+ ELIM (strnlen (a3_7[0], -1) <= sizeof a3_7);
ELIM (strnlen (a3_7[2], 0) == 0);
ELIM (strnlen (a3_7[2], 1) < 2);
ELIM (strnlen (a3_7[2], 2) < 3);
ELIM (strnlen (a3_7[2], 3) < 4);
- ELIM (strnlen (a3_7[2], 9) < 8);
- ELIM (strnlen (a3_7[2], PTRDIFF_MAX) < 8);
- ELIM (strnlen (a3_7[2], SIZE_MAX) < 8);
- ELIM (strnlen (a3_7[2], -1) < 8);
+ ELIM (strnlen (a3_7[2], 9) <= 9);
+ ELIM (strnlen (a3_7[2], PTRDIFF_MAX) < sizeof a3_7);
+ ELIM (strnlen (a3_7[2], SIZE_MAX) < sizeof a3_7);
+ ELIM (strnlen (a3_7[2], -1) < sizeof a3_7);
ELIM (strnlen ((char*)a3_7, 0) == 0);
ELIM (strnlen ((char*)a3_7, 1) < 2);
ELIM (strnlen ((char*)a3_7, 3) < 4);
ELIM (strnlen ((char*)a3_7, 9) < 10);
ELIM (strnlen ((char*)a3_7, 19) < 20);
- ELIM (strnlen ((char*)a3_7, 21) < 22);
- ELIM (strnlen ((char*)a3_7, 23) < 22);
- ELIM (strnlen ((char*)a3_7, PTRDIFF_MAX) < 22);
- ELIM (strnlen ((char*)a3_7, SIZE_MAX) < 22);
- ELIM (strnlen ((char*)a3_7, -1) < 22);
+ ELIM (strnlen ((char*)a3_7, 21) <= sizeof a3_7);
+ ELIM (strnlen ((char*)a3_7, 23) <= sizeof a3_7);
+ ELIM (strnlen ((char*)a3_7, PTRDIFF_MAX) <= sizeof a3_7);
+ ELIM (strnlen ((char*)a3_7, SIZE_MAX) <= sizeof a3_7);
+ ELIM (strnlen ((char*)a3_7, -1) <= sizeof a3_7);
ELIM (strnlen (ax, 0) == 0);
ELIM (strnlen (ax, 1) < 2);
ELIM (strnlen (ax, 2) < 3);
ELIM (strnlen (ax, 9) < 10);
- ELIM (strnlen (a3, PTRDIFF_MAX) <= PTRDIFF_MAX);
- ELIM (strnlen (a3, SIZE_MAX) < PTRDIFF_MAX);
- ELIM (strnlen (a3, -1) < PTRDIFF_MAX);
-}
-
-struct MemArrays
-{
- char c;
- char a0[0];
- char a1[1];
- char a3[3];
- char a5[5];
- char a3_7[3][7];
- char ax[1];
-};
-
-void elim_strnlen_memarr_cst (struct MemArrays *p, int i)
-{
- ELIM (strnlen (&p->c, 0) == 0);
- ELIM (strnlen (&p->c, 1) < 2);
- ELIM (strnlen (&p->c, 9) == 0);
- ELIM (strnlen (&p->c, PTRDIFF_MAX) == 0);
- ELIM (strnlen (&p->c, SIZE_MAX) == 0);
- ELIM (strnlen (&p->c, -1) == 0);
-
- /* Other accesses to internal zero-length arrays are undefined. */
- ELIM (strnlen (p->a0, 0) == 0);
-
- ELIM (strnlen (p->a1, 0) == 0);
- ELIM (strnlen (p->a1, 1) < 2);
- ELIM (strnlen (p->a1, 9) == 0);
- ELIM (strnlen (p->a1, PTRDIFF_MAX) == 0);
- ELIM (strnlen (p->a1, SIZE_MAX) == 0);
- ELIM (strnlen (p->a1, -1) == 0);
-
- ELIM (strnlen (p->a3, 0) == 0);
- ELIM (strnlen (p->a3, 1) < 2);
- ELIM (strnlen (p->a3, 2) < 3);
- ELIM (strnlen (p->a3, 3) < 4);
- ELIM (strnlen (p->a3, 9) < 4);
- ELIM (strnlen (p->a3, PTRDIFF_MAX) < 4);
- ELIM (strnlen (p->a3, SIZE_MAX) < 4);
- ELIM (strnlen (p->a3, -1) < 4);
-
- ELIM (strnlen (p[i].a3, 0) == 0);
- ELIM (strnlen (p[i].a3, 1) < 2);
- ELIM (strnlen (p[i].a3, 2) < 3);
- ELIM (strnlen (p[i].a3, 3) < 4);
- ELIM (strnlen (p[i].a3, 9) < 4);
- ELIM (strnlen (p[i].a3, PTRDIFF_MAX) < 4);
- ELIM (strnlen (p[i].a3, SIZE_MAX) < 4);
- ELIM (strnlen (p[i].a3, -1) < 4);
-
- ELIM (strnlen (p->a3_7[0], 0) == 0);
- ELIM (strnlen (p->a3_7[0], 1) < 2);
- ELIM (strnlen (p->a3_7[0], 2) < 3);
- ELIM (strnlen (p->a3_7[0], 3) < 4);
- ELIM (strnlen (p->a3_7[0], 9) < 8);
- ELIM (strnlen (p->a3_7[0], PTRDIFF_MAX) < 8);
- ELIM (strnlen (p->a3_7[0], SIZE_MAX) < 8);
- ELIM (strnlen (p->a3_7[0], -1) < 8);
-
- ELIM (strnlen (p->a3_7[2], 0) == 0);
- ELIM (strnlen (p->a3_7[2], 1) < 2);
- ELIM (strnlen (p->a3_7[2], 2) < 3);
- ELIM (strnlen (p->a3_7[2], 3) < 4);
- ELIM (strnlen (p->a3_7[2], 9) < 8);
- ELIM (strnlen (p->a3_7[2], PTRDIFF_MAX) < 8);
- ELIM (strnlen (p->a3_7[2], SIZE_MAX) < 8);
- ELIM (strnlen (p->a3_7[2], -1) < 8);
-
- ELIM (strnlen (p->a3_7[i], 0) == 0);
- ELIM (strnlen (p->a3_7[i], 1) < 2);
- ELIM (strnlen (p->a3_7[i], 2) < 3);
- ELIM (strnlen (p->a3_7[i], 3) < 4);
-
-#if 0
- /* This is tranformed into strnlen ((char*)p + offsetof (a3_7[i]), N)
- which makes it impssible to determine the size of the array. */
- ELIM (strnlen (p->a3_7[i], 9) < 8);
- ELIM (strnlen (p->a3_7[i], PTRDIFF_MAX) < 8);
- ELIM (strnlen (p->a3_7[i], SIZE_MAX) < 8);
- ELIM (strnlen (p->a3_7[i], -1) < 8);
-#else
- ELIM (strnlen (p->a3_7[i], 9) < 10);
- ELIM (strnlen (p->a3_7[i], 19) < 20);
-#endif
-
- ELIM (strnlen ((char*)p->a3_7, 0) == 0);
- ELIM (strnlen ((char*)p->a3_7, 1) < 2);
- ELIM (strnlen ((char*)p->a3_7, 2) < 3);
- ELIM (strnlen ((char*)p->a3_7, 3) < 4);
- ELIM (strnlen ((char*)p->a3_7, 9) < 10);
- ELIM (strnlen ((char*)p->a3_7, 19) < 20);
- ELIM (strnlen ((char*)p->a3_7, 21) < 22);
- ELIM (strnlen ((char*)p->a3_7, 23) < 22);
- ELIM (strnlen ((char*)p->a3_7, PTRDIFF_MAX) < 22);
- ELIM (strnlen ((char*)p->a3_7, SIZE_MAX) < 22);
- ELIM (strnlen ((char*)p->a3_7, -1) < 22);
-
- ELIM (strnlen (p->ax, 0) == 0);
- ELIM (strnlen (p->ax, 1) < 2);
- ELIM (strnlen (p->ax, 2) < 3);
- ELIM (strnlen (p->ax, 9) < 10);
- ELIM (strnlen (p->a3, PTRDIFF_MAX) <= PTRDIFF_MAX);
- ELIM (strnlen (p->a3, SIZE_MAX) < PTRDIFF_MAX);
- ELIM (strnlen (p->a3, -1) < PTRDIFF_MAX);
+ ELIM (strnlen (ax, PTRDIFF_MAX) < PTRDIFF_MAX);
+ ELIM (strnlen (ax, SIZE_MAX) < PTRDIFF_MAX);
+ ELIM (strnlen (ax, -1) < PTRDIFF_MAX);
}
update_stmt (last.stmt);
}
-/* For an LHS that is an SSA_NAME and for strlen() or strnlen() argument
- SRC, set LHS range info to [0, min (N, BOUND)] if SRC refers to
- a character array A[N] with unknown length bounded by N, and for
- strnlen(), by min (N, BOUND). */
-
-static tree
-maybe_set_strlen_range (tree lhs, tree src, tree bound)
+/* For an LHS that is an SSA_NAME that is the result of a strlen()
+ call, or when BOUND is non-null, of a strnlen() call, set LHS
+ range info to [0, min (MAX, BOUND)] when the range includes more
+ than one value and return LHS. Otherwise, when the range
+ [MIN, MAX] is such that MIN == MAX, return the tree representation
+ of (MIN). The latter allows callers to fold suitable strnlen() calls
+ to constants. */
+
+tree
+set_strlen_range (tree lhs, wide_int max, tree bound /* = NULL_TREE */)
{
if (TREE_CODE (lhs) != SSA_NAME
|| !INTEGRAL_TYPE_P (TREE_TYPE (lhs)))
return NULL_TREE;
- if (TREE_CODE (src) == SSA_NAME)
- {
- gimple *def = SSA_NAME_DEF_STMT (src);
- if (is_gimple_assign (def)
- && gimple_assign_rhs_code (def) == ADDR_EXPR)
- src = gimple_assign_rhs1 (def);
- }
-
- wide_int max = wi::to_wide (TYPE_MAX_VALUE (ptrdiff_type_node));
wide_int min = wi::zero (max.get_precision ());
- if (TREE_CODE (src) == ADDR_EXPR)
- {
- /* The last array member of a struct can be bigger than its size
- suggests if it's treated as a poor-man's flexible array member. */
- src = TREE_OPERAND (src, 0);
- bool src_is_array = TREE_CODE (TREE_TYPE (src)) == ARRAY_TYPE;
- if (src_is_array
- && TREE_CODE (src) != MEM_REF
- && !array_at_struct_end_p (src))
- {
- tree type = TREE_TYPE (src);
- if (tree size = TYPE_SIZE_UNIT (type))
- if (size && TREE_CODE (size) == INTEGER_CST)
- max = wi::to_wide (size);
-
- /* For strlen() the upper bound above is equal to
- the longest string that can be stored in the array
- (i.e., it accounts for the terminating nul. For
- strnlen() bump up the maximum by one since the array
- need not be nul-terminated. */
- if (!bound && max != 0)
- --max;
- }
- else
- {
- if (TREE_CODE (src) == COMPONENT_REF && !src_is_array)
- src = TREE_OPERAND (src, 1);
- if (DECL_P (src))
- {
- /* Handle the unlikely case of strlen (&c) where c is some
- variable. */
- if (tree size = DECL_SIZE_UNIT (src))
- if (TREE_CODE (size) == INTEGER_CST)
- max = wi::to_wide (size);
- }
- }
- }
-
if (bound)
{
/* For strnlen, adjust MIN and MAX as necessary. If the bound
{
/* For a bound in a known range, adjust the range determined
above as necessary. For a bound in some anti-range or
- in an unknown range, use the range determined above. */
+ in an unknown range, use the range determined by callers. */
if (wi::ltu_p (minbound, min))
min = minbound;
if (wi::ltu_p (maxbound, max))
return lhs;
}
+/* For an LHS that is an SSA_NAME and for strlen() or strnlen() argument
+ SRC, set LHS range info to [0, min (N, BOUND)] if SRC refers to
+ a character array A[N] with unknown length bounded by N, and for
+ strnlen(), by min (N, BOUND). */
+
+static tree
+maybe_set_strlen_range (tree lhs, tree src, tree bound)
+{
+ if (TREE_CODE (lhs) != SSA_NAME
+ || !INTEGRAL_TYPE_P (TREE_TYPE (lhs)))
+ return NULL_TREE;
+
+ if (TREE_CODE (src) == SSA_NAME)
+ {
+ gimple *def = SSA_NAME_DEF_STMT (src);
+ if (is_gimple_assign (def)
+ && gimple_assign_rhs_code (def) == ADDR_EXPR)
+ src = gimple_assign_rhs1 (def);
+ }
+
+ /* The longest string is PTRDIFF_MAX - 1 bytes including the final
+ NUL so that the difference between a pointer to just past it and
+ one to its beginning is positive. */
+ wide_int max = wi::to_wide (TYPE_MAX_VALUE (ptrdiff_type_node)) - 2;
+
+ if (TREE_CODE (src) == ADDR_EXPR)
+ {
+ /* The last array member of a struct can be bigger than its size
+ suggests if it's treated as a poor-man's flexible array member. */
+ src = TREE_OPERAND (src, 0);
+ if (TREE_CODE (src) != MEM_REF
+ && !array_at_struct_end_p (src))
+ {
+ tree type = TREE_TYPE (src);
+ tree size = TYPE_SIZE_UNIT (type);
+ if (size
+ && TREE_CODE (size) == INTEGER_CST
+ && !integer_zerop (size))
+ {
+ /* Even though such uses of strlen would be undefined,
+ avoid relying on arrays of arrays in case some genius
+ decides to call strlen on an unterminated array element
+ that's followed by a terminated one. Likewise, avoid
+ assuming that a struct array member is necessarily
+ nul-terminated (the nul may be in the member that
+ follows). In those cases, assume that the length
+ of the string stored in such an array is bounded
+ by the size of the enclosing object if one can be
+ determined. */
+ tree base = get_base_address (src);
+ if (VAR_P (base))
+ {
+ if (tree size = DECL_SIZE_UNIT (base))
+ if (size
+ && TREE_CODE (size) == INTEGER_CST
+ && TREE_CODE (TREE_TYPE (base)) != POINTER_TYPE)
+ max = wi::to_wide (size);
+ }
+ }
+
+ /* For strlen() the upper bound above is equal to
+ the longest string that can be stored in the array
+ (i.e., it accounts for the terminating nul. For
+ strnlen() bump up the maximum by one since the array
+ need not be nul-terminated. */
+ if (!bound && max != 0)
+ --max;
+ }
+ }
+
+ return set_strlen_range (lhs, max, bound);
+}
+
/* Handle a strlen call. If strlen of the argument is known, replace
the strlen call with the known value, otherwise remember that strlen
of the argument is stored in the lhs SSA_NAME. */
extern bool is_strlen_related_p (tree, tree);
extern bool maybe_diag_stxncpy_trunc (gimple_stmt_iterator, tree, tree);
+extern tree set_strlen_range (tree, wide_int, tree = NULL_TREE);
#endif // GCC_TREE_SSA_STRLEN_H