On Sun, Nov 15, 2020 at 11:57:55PM -1200, Jakub Jelinek via Gcc-patches wrote: > Tested on x86_64-linux, i686-linux and powerpc64-linux, ok for trunk?
Here is an incremental patch that resolves the remaining FIXMEs, in particular implements VLAs (except for variable length structures) and for larger fixed sized arrays or members with larger array types uses runtime loops for the clearing (unless inside of a union). Furthermore, I've added diagnostics about last argument being const whatever * (similarly to e.g. __builtin_*_overflow) and also about _Atomic whatever *. Bootstrapped/regtested on x86_64-linux, ok for trunk? 2020-11-16 Jakub Jelinek <ja...@redhat.com> PR libstdc++/88101 gcc/ * gimple-fold.c (clear_padding_emit_loop): New function. (clear_padding_type): Optimize larger ARRAY_TYPEs not inside of a union by clearing the padding inside of a runtime loop. (gimple_fold_builtin_clear_padding): Handle VLAs. gcc/c-family/ * c-common.c (check_builtin_function_arguments) <case BUILT_IN_CLEAR_PADDING>: Diagnose pointer to const or pointer to _Atomic arguments. gcc/testsuite/ * c-c++-common/builtin-clear-padding-1.c (struct T): New type. (foo): Add test for const struct T * argument. * c-c++-common/torture/builtin-clear-padding-4.c: New test. * c-c++-common/torture/builtin-clear-padding-5.c: New test. * gcc.dg/builtin-clear-padding-2.c: New test. --- gcc/gimple-fold.c.jj 2020-11-16 12:20:29.551779949 +0100 +++ gcc/gimple-fold.c 2020-11-16 18:47:42.997770758 +0100 @@ -4323,6 +4323,39 @@ clear_padding_type_may_have_padding_p (t } } +/* Emit a runtime loop: + for (; buf.base != end; buf.base += sz) + __builtin_clear_padding (buf.base); */ + +static void +clear_padding_emit_loop (clear_padding_struct *buf, tree type, tree end) +{ + tree l1 = create_artificial_label (buf->loc); + tree l2 = create_artificial_label (buf->loc); + tree l3 = create_artificial_label (buf->loc); + gimple *g = gimple_build_goto (l2); + gimple_set_location (g, buf->loc); + gsi_insert_before (buf->gsi, g, GSI_SAME_STMT); + g = gimple_build_label (l1); + gimple_set_location (g, buf->loc); + gsi_insert_before (buf->gsi, g, GSI_SAME_STMT); + clear_padding_type (buf, type); + clear_padding_flush (buf, true); + g = gimple_build_assign (buf->base, POINTER_PLUS_EXPR, buf->base, + size_int (buf->sz)); + gimple_set_location (g, buf->loc); + gsi_insert_before (buf->gsi, g, GSI_SAME_STMT); + g = gimple_build_label (l2); + gimple_set_location (g, buf->loc); + gsi_insert_before (buf->gsi, g, GSI_SAME_STMT); + g = gimple_build_cond (NE_EXPR, buf->base, end, l1, l3); + gimple_set_location (g, buf->loc); + gsi_insert_before (buf->gsi, g, GSI_SAME_STMT); + g = gimple_build_label (l3); + gimple_set_location (g, buf->loc); + gsi_insert_before (buf->gsi, g, GSI_SAME_STMT); +} + /* Clear padding bits for TYPE. Called recursively from gimple_fold_builtin_clear_padding. */ @@ -4423,11 +4456,41 @@ clear_padding_type (clear_padding_struct HOST_WIDE_INT nelts; nelts = int_size_in_bytes (TREE_TYPE (type)); nelts = sz / nelts; - if (nelts > 1 && sz > 8 * UNITS_PER_WORD && buf->union_ptr == NULL) + if (nelts > 1 + && sz > 8 * UNITS_PER_WORD + && buf->union_ptr == NULL + && clear_padding_type_may_have_padding_p (TREE_TYPE (type))) { - /* FIXME: If ARRAY_TYPE has nelts > 1 and sz over certain - small threshold, instead call clear_padding_flush (&buf, true); - and emit a runtime loop filling each array element separately. */ + /* For sufficiently large array of more than one elements, + emit a runtime loop to keep code size manageable. */ + tree base = buf->base; + unsigned int prev_align = buf->align; + HOST_WIDE_INT off = buf->off + buf->size; + HOST_WIDE_INT prev_sz = buf->sz; + clear_padding_flush (buf, true); + tree elttype = TREE_TYPE (type); + buf->base = create_tmp_var (build_pointer_type (elttype)); + tree end = make_ssa_name (TREE_TYPE (buf->base)); + gimple *g = gimple_build_assign (buf->base, POINTER_PLUS_EXPR, + base, size_int (off)); + gimple_set_location (g, buf->loc); + gsi_insert_before (buf->gsi, g, GSI_SAME_STMT); + g = gimple_build_assign (end, POINTER_PLUS_EXPR, buf->base, + size_int (sz)); + gimple_set_location (g, buf->loc); + gsi_insert_before (buf->gsi, g, GSI_SAME_STMT); + buf->sz = sz / nelts; + buf->align = TYPE_ALIGN (elttype); + buf->off = 0; + buf->size = 0; + clear_padding_emit_loop (buf, elttype, end); + buf->base = base; + buf->sz = prev_sz; + buf->align = prev_align; + buf->size = off % UNITS_PER_WORD; + buf->off = off - buf->size; + memset (buf->buf, 0, buf->size); + break; } for (HOST_WIDE_INT i = 0; i < nelts; i++) clear_padding_type (buf, TREE_TYPE (type)); @@ -4514,11 +4577,28 @@ gimple_fold_builtin_clear_padding (gimpl ; else if (TREE_CODE (type) == ARRAY_TYPE && buf.sz < 0) { - /* FIXME: Handle VLAs. These can be handled by adding runtime - loop (or multiple for multiple VLAs) calling clear_padding_type - only on each non-VLA element. */ - sorry_at (loc, "%s does not support VLAs yet", - "__builtin_clear_padding"); + tree sz = TYPE_SIZE_UNIT (type); + tree elttype = type; + /* Only supports C/C++ VLAs and flattens all the VLA levels. */ + while (TREE_CODE (elttype) == ARRAY_TYPE + && int_size_in_bytes (elttype) < 0) + elttype = TREE_TYPE (elttype); + HOST_WIDE_INT eltsz = int_size_in_bytes (elttype); + gcc_assert (eltsz >= 0); + if (eltsz) + { + buf.base = create_tmp_var (build_pointer_type (elttype)); + tree end = make_ssa_name (TREE_TYPE (buf.base)); + gimple *g = gimple_build_assign (buf.base, ptr); + gimple_set_location (g, loc); + gsi_insert_before (gsi, g, GSI_SAME_STMT); + g = gimple_build_assign (end, POINTER_PLUS_EXPR, buf.base, sz); + gimple_set_location (g, loc); + gsi_insert_before (gsi, g, GSI_SAME_STMT); + buf.sz = eltsz; + buf.align = TYPE_ALIGN (elttype); + clear_padding_emit_loop (&buf, elttype, end); + } } else { --- gcc/c-family/c-common.c.jj 2020-11-15 18:29:12.441618995 +0100 +++ gcc/c-family/c-common.c 2020-11-16 19:33:56.458507220 +0100 @@ -6186,6 +6186,20 @@ check_builtin_function_arguments (locati "%qE points to incomplete type", 1, fndecl); return false; } + else if (TYPE_READONLY (TREE_TYPE (TREE_TYPE (args[0])))) + { + error_at (ARG_LOCATION (0), "argument %u in call to function %qE " + "has pointer to %qs type (%qT)", 1, fndecl, "const", + TREE_TYPE (args[0])); + return false; + } + else if (TYPE_ATOMIC (TREE_TYPE (TREE_TYPE (args[0])))) + { + error_at (ARG_LOCATION (0), "argument %u in call to function %qE " + "has pointer to %qs type (%qT)", 1, fndecl, + "_Atomic", TREE_TYPE (args[0])); + return false; + } return true; } return false; --- gcc/testsuite/c-c++-common/builtin-clear-padding-1.c.jj 2020-11-16 12:39:37.000000000 +0100 +++ gcc/testsuite/c-c++-common/builtin-clear-padding-1.c 2020-11-16 19:35:08.628693878 +0100 @@ -2,9 +2,10 @@ /* { dg-do compile } */ struct S; +struct T { char a; long long b; }; void -foo (struct S *p, void *q, char *r) +foo (struct S *p, void *q, char *r, const struct T *s) { __builtin_clear_padding (); /* { dg-error "too few arguments to function '__builtin_clear_padding'" } */ __builtin_clear_padding (1); /* { dg-error "argument 1 in call to function '__builtin_clear_padding' does not have pointer type" } */ @@ -14,4 +15,5 @@ foo (struct S *p, void *q, char *r) __builtin_clear_padding (p); /* { dg-error "argument 1 in call to function '__builtin_clear_padding' points to incomplete type" } */ __builtin_clear_padding (q); /* { dg-error "argument 1 in call to function '__builtin_clear_padding' points to incomplete type" } */ __builtin_clear_padding (r); + __builtin_clear_padding (s); /* { dg-error "argument 1 in call to function '__builtin_clear_padding' has pointer to 'const' type" } */ } --- gcc/testsuite/c-c++-common/torture/builtin-clear-padding-4.c.jj 2020-11-16 17:40:47.111049570 +0100 +++ gcc/testsuite/c-c++-common/torture/builtin-clear-padding-4.c 2020-11-16 17:40:53.074982330 +0100 @@ -0,0 +1,59 @@ +/* PR libstdc++/88101 */ + +struct S { char a; short b; char c; }; + +__attribute__((noipa)) void +foo (int m, int n, int o) +{ + long double a1[m]; + long double a2[m]; + struct S b1[m][n]; + struct S b2[m][n]; + struct S c1[m][n][o]; + struct S c2[m][n][o]; + int i, j, k; + __builtin_memset (&a1, 0, sizeof (a1)); + __builtin_memset (&a2, ~0, sizeof (a2)); + __builtin_memset (&b1, 0, sizeof (b1)); + __builtin_memset (&b2, ~0, sizeof (b2)); + __builtin_memset (&c1, 0, sizeof (c1)); + __builtin_memset (&c2, ~0, sizeof (c2)); + for (i = 0; i < m; i++) + { + a1[i] = 13.132L; + a2[i] = 13.132L; + for (j = 0; j < n; j++) + { + b1[i][j].a = -1; + b1[i][j].b = -1; + b1[i][j].c = -1; + b2[i][j].a = -1; + b2[i][j].b = -1; + b2[i][j].c = -1; + for (k = 0; k < o; k++) + { + c1[i][j][k].a = -1; + c1[i][j][k].b = -1; + c1[i][j][k].c = -1; + c2[i][j][k].a = -1; + c2[i][j][k].b = -1; + c2[i][j][k].c = -1; + } + } + } + __builtin_clear_padding (&a2); + __builtin_clear_padding (&b2); + __builtin_clear_padding (&c2); + if (__builtin_memcmp (&a1, &a2, sizeof (a1)) + || __builtin_memcmp (&b1, &b2, sizeof (b1)) + || __builtin_memcmp (&c1, &c2, sizeof (c1))) + __builtin_abort (); +} + +int +main () +{ + foo (5, 3, 4); + foo (17, 2, 1); + return 0; +} --- gcc/testsuite/c-c++-common/torture/builtin-clear-padding-5.c.jj 2020-11-16 18:51:02.463521254 +0100 +++ gcc/testsuite/c-c++-common/torture/builtin-clear-padding-5.c 2020-11-16 17:58:58.385746596 +0100 @@ -0,0 +1,49 @@ +/* PR libstdc++/88101 */ + +struct S { char a; short b; char c; } s1[24], s2[24]; +struct T { char a; long long b; char c; struct S d[3]; long long e; char f; } t1, t2; +struct U { char a; long long b; char c; struct S d[25]; long long e; char f; } u1, u2; + +__attribute__((noipa)) void +foo (struct S *s, struct T *t, struct U *u) +{ + int i; + t->a = -1; t->b = -1; t->c = -1; t->e = -1; t->f = -1; + u->a = -1; u->b = -1; u->c = -1; u->e = -1; u->f = -1; + for (i = 0; i < 24; i++) + { + s[i].a = -1; + s[i].b = -1; + s[i].c = -1; + } + for (i = 0; i < 3; i++) + { + t->d[i].a = -1; + t->d[i].b = -1; + t->d[i].c = -1; + } + for (i = 0; i < 25; i++) + { + u->d[i].a = -1; + u->d[i].b = -1; + u->d[i].c = -1; + } +} + +int +main () +{ + __builtin_memset (&s2, -1, sizeof (s2)); + __builtin_memset (&t2, -1, sizeof (t2)); + __builtin_memset (&u2, -1, sizeof (u2)); + foo (&s1[0], &t1, &u1); + foo (&s2[0], &t2, &u2); + __builtin_clear_padding (&s2); + __builtin_clear_padding (&t2); + __builtin_clear_padding (&u2); + if (__builtin_memcmp (&s1, &s2, sizeof (s1)) + || __builtin_memcmp (&t1, &t2, sizeof (t1)) + || __builtin_memcmp (&u1, &u2, sizeof (u1))) + __builtin_abort (); + return 0; +} --- gcc/testsuite/gcc.dg/builtin-clear-padding-2.c.jj 2020-11-16 19:36:17.221920848 +0100 +++ gcc/testsuite/gcc.dg/builtin-clear-padding-2.c 2020-11-16 19:36:44.158617264 +0100 @@ -0,0 +1,11 @@ +/* PR libstdc++/88101 */ +/* { dg-do compile } */ +/* { dg-options "" } */ + +struct T { char a; long long b; }; + +void +foo (_Atomic struct T *p) +{ + __builtin_clear_padding (p); /* { dg-error "argument 1 in call to function '__builtin_clear_padding' has pointer to '_Atomic' type" } */ +} Jakub