aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc')
-rw-r--r--gcc/builtins.def1
-rw-r--r--gcc/c-family/c-common.c33
-rw-r--r--gcc/doc/extend.texi13
-rw-r--r--gcc/gimple-fold.c695
-rw-r--r--gcc/gimplify.c14
-rw-r--r--gcc/testsuite/c-c++-common/builtin-clear-padding-1.c19
-rw-r--r--gcc/testsuite/c-c++-common/torture/builtin-clear-padding-1.c47
-rw-r--r--gcc/testsuite/c-c++-common/torture/builtin-clear-padding-2.c24
-rw-r--r--gcc/testsuite/c-c++-common/torture/builtin-clear-padding-3.c65
-rw-r--r--gcc/testsuite/c-c++-common/torture/builtin-clear-padding-4.c59
-rw-r--r--gcc/testsuite/c-c++-common/torture/builtin-clear-padding-5.c49
-rw-r--r--gcc/testsuite/g++.dg/torture/builtin-clear-padding-1.C31
-rw-r--r--gcc/testsuite/g++.dg/torture/builtin-clear-padding-2.C34
-rw-r--r--gcc/testsuite/gcc.dg/builtin-clear-padding-1.c10
14 files changed, 1094 insertions, 0 deletions
diff --git a/gcc/builtins.def b/gcc/builtins.def
index b4494c7..04dbe79 100644
--- a/gcc/builtins.def
+++ b/gcc/builtins.def
@@ -839,6 +839,7 @@ DEF_EXT_LIB_BUILTIN (BUILT_IN_CLEAR_CACHE, "__clear_cache", BT_FN_VOID_PTR_PT
/* [trans-mem]: Adjust BUILT_IN_TM_CALLOC if BUILT_IN_CALLOC is changed. */
DEF_LIB_BUILTIN (BUILT_IN_CALLOC, "calloc", BT_FN_PTR_SIZE_SIZE, ATTR_MALLOC_WARN_UNUSED_RESULT_SIZE_1_2_NOTHROW_LEAF_LIST)
DEF_GCC_BUILTIN (BUILT_IN_CLASSIFY_TYPE, "classify_type", BT_FN_INT_VAR, ATTR_LEAF_LIST)
+DEF_GCC_BUILTIN (BUILT_IN_CLEAR_PADDING, "clear_padding", BT_FN_VOID_VAR, ATTR_NOTHROW_NONNULL_TYPEGENERIC_LEAF)
DEF_GCC_BUILTIN (BUILT_IN_CLZ, "clz", BT_FN_INT_UINT, ATTR_CONST_NOTHROW_LEAF_LIST)
DEF_GCC_BUILTIN (BUILT_IN_CLZIMAX, "clzimax", BT_FN_INT_UINTMAX, ATTR_CONST_NOTHROW_LEAF_LIST)
DEF_GCC_BUILTIN (BUILT_IN_CLZL, "clzl", BT_FN_INT_ULONG, ATTR_CONST_NOTHROW_LEAF_LIST)
diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index 729bd85..5d1e4ef 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -6178,6 +6178,39 @@ check_builtin_function_arguments (location_t loc, vec<location_t> arg_loc,
}
return false;
+ case BUILT_IN_CLEAR_PADDING:
+ if (builtin_function_validate_nargs (loc, fndecl, nargs, 1))
+ {
+ if (!POINTER_TYPE_P (TREE_TYPE (args[0])))
+ {
+ error_at (ARG_LOCATION (0), "argument %u in call to function "
+ "%qE does not have pointer type", 1, fndecl);
+ return false;
+ }
+ else if (!COMPLETE_TYPE_P (TREE_TYPE (TREE_TYPE (args[0]))))
+ {
+ error_at (ARG_LOCATION (0), "argument %u in call to function "
+ "%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;
+
default:
return true;
}
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index cdf9108..f026942 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -13564,6 +13564,19 @@ initializers of variables usable in constant expressions. For more details
refer to the latest revision of the C++ standard.
@end deftypefn
+@deftypefn {Built-in Function} void __builtin_clear_padding (@var{ptr})
+The built-in function @code{__builtin_clear_padding} function clears
+padding bits inside of the object representation of object pointed by
+@var{ptr}, which has to be a pointer. The value representation of the
+object is not affected. The type of the object is assumed to be the type
+the pointer points to. Inside of a union, the only cleared bits are
+bits that are padding bits for all the union members.
+
+This built-in-function is useful if the padding bits of an object might
+have intederminate values and the object representation needs to be
+bitwise compared to some other object, for example for atomic operations.
+@end deftypefn
+
@deftypefn {Built-in Function} long __builtin_expect (long @var{exp}, long @var{c})
@opindex fprofile-arcs
You may use @code{__builtin_expect} to provide the compiler with
diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
index ca38a31..5d60bef 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -3948,6 +3948,698 @@ gimple_fold_builtin_realloc (gimple_stmt_iterator *gsi)
return false;
}
+/* Number of bytes into which any type but aggregate or vector types
+ should fit. */
+static constexpr size_t clear_padding_unit
+ = MAX_BITSIZE_MODE_ANY_MODE / BITS_PER_UNIT;
+/* Buffer size on which __builtin_clear_padding folding code works. */
+static const size_t clear_padding_buf_size = 32 * clear_padding_unit;
+
+/* Data passed through __builtin_clear_padding folding. */
+struct clear_padding_struct {
+ location_t loc;
+ tree base;
+ tree alias_type;
+ gimple_stmt_iterator *gsi;
+ /* Alignment of buf->base + 0. */
+ unsigned align;
+ /* Offset from buf->base. Should be always a multiple of UNITS_PER_WORD. */
+ HOST_WIDE_INT off;
+ /* Number of padding bytes before buf->off that don't have padding clear
+ code emitted yet. */
+ HOST_WIDE_INT padding_bytes;
+ /* The size of the whole object. Never emit code to touch
+ buf->base + buf->sz or following bytes. */
+ HOST_WIDE_INT sz;
+ /* Number of bytes recorded in buf->buf. */
+ size_t size;
+ /* When inside union, instead of emitting code we and bits inside of
+ the union_ptr array. */
+ unsigned char *union_ptr;
+ /* Set bits mean padding bits that need to be cleared by the builtin. */
+ unsigned char buf[clear_padding_buf_size + clear_padding_unit];
+};
+
+/* Emit code to clear padding requested in BUF->buf - set bits
+ in there stand for padding that should be cleared. FULL is true
+ if everything from the buffer should be flushed, otherwise
+ it can leave up to 2 * clear_padding_unit bytes for further
+ processing. */
+
+static void
+clear_padding_flush (clear_padding_struct *buf, bool full)
+{
+ gcc_assert ((clear_padding_unit % UNITS_PER_WORD) == 0);
+ if (!full && buf->size < 2 * clear_padding_unit)
+ return;
+ gcc_assert ((buf->off % UNITS_PER_WORD) == 0);
+ size_t end = buf->size;
+ if (!full)
+ end = ((end - clear_padding_unit - 1) / clear_padding_unit
+ * clear_padding_unit);
+ size_t padding_bytes = buf->padding_bytes;
+ if (buf->union_ptr)
+ {
+ /* Inside of a union, instead of emitting any code, instead
+ clear all bits in the union_ptr buffer that are clear
+ in buf. Whole padding bytes don't clear anything. */
+ for (size_t i = 0; i < end; i++)
+ {
+ if (buf->buf[i] == (unsigned char) ~0)
+ padding_bytes++;
+ else
+ {
+ padding_bytes = 0;
+ buf->union_ptr[buf->off + i] &= buf->buf[i];
+ }
+ }
+ if (full)
+ {
+ buf->off = 0;
+ buf->size = 0;
+ buf->padding_bytes = 0;
+ }
+ else
+ {
+ memmove (buf->buf, buf->buf + end, buf->size - end);
+ buf->off += end;
+ buf->size -= end;
+ buf->padding_bytes = padding_bytes;
+ }
+ return;
+ }
+ size_t wordsize = UNITS_PER_WORD;
+ for (size_t i = 0; i < end; i += wordsize)
+ {
+ size_t nonzero_first = wordsize;
+ size_t nonzero_last = 0;
+ bool all_ones = true;
+ if ((unsigned HOST_WIDE_INT) (buf->off + i + wordsize)
+ > (unsigned HOST_WIDE_INT) buf->sz)
+ {
+ gcc_assert (wordsize > 1);
+ wordsize /= 2;
+ i -= wordsize;
+ continue;
+ }
+ for (size_t j = i; j < i + wordsize && j < end; j++)
+ {
+ if (buf->buf[j])
+ {
+ if (nonzero_first == wordsize)
+ {
+ nonzero_first = j - i;
+ nonzero_last = j - i;
+ }
+ if (nonzero_last != j - i)
+ all_ones = false;
+ nonzero_last = j + 1 - i;
+ }
+ if (buf->buf[j] != 0 && buf->buf[j] != (unsigned char) ~0)
+ all_ones = false;
+ }
+ if (padding_bytes)
+ {
+ if (nonzero_first == 0
+ && nonzero_last == wordsize
+ && all_ones)
+ {
+ /* All bits are padding and we had some padding
+ before too. Just extend it. */
+ padding_bytes += wordsize;
+ continue;
+ }
+ size_t padding_end = i;
+ if (all_ones && nonzero_first == 0)
+ {
+ padding_bytes += nonzero_last;
+ padding_end += nonzero_last;
+ nonzero_first = wordsize;
+ nonzero_last = 0;
+ }
+ tree atype = build_array_type_nelts (char_type_node, padding_bytes);
+ tree dst = build2_loc (buf->loc, MEM_REF, atype, buf->base,
+ build_int_cst (buf->alias_type,
+ buf->off + padding_end
+ - padding_bytes));
+ tree src = build_constructor (atype, NULL);
+ gimple *g = gimple_build_assign (dst, src);
+ gimple_set_location (g, buf->loc);
+ gsi_insert_before (buf->gsi, g, GSI_SAME_STMT);
+ padding_bytes = 0;
+ buf->padding_bytes = 0;
+ }
+ if (nonzero_first == wordsize)
+ /* All bits in a word are 0, there are no padding bits. */
+ continue;
+ if (all_ones && nonzero_last == wordsize)
+ {
+ /* All bits between nonzero_first and end of word are padding
+ bits, start counting padding_bytes. */
+ padding_bytes = nonzero_last - nonzero_first;
+ continue;
+ }
+ for (size_t eltsz = 1; eltsz <= wordsize; eltsz <<= 1)
+ {
+ if (nonzero_last - nonzero_first <= eltsz
+ && ((nonzero_first & ~(eltsz - 1))
+ == ((nonzero_last - 1) & ~(eltsz - 1))))
+ {
+ tree type;
+ if (eltsz == 1)
+ type = char_type_node;
+ else
+ type = lang_hooks.types.type_for_size (eltsz * BITS_PER_UNIT,
+ 0);
+ size_t start = nonzero_first & ~(eltsz - 1);
+ HOST_WIDE_INT off = buf->off + i + start;
+ tree atype = type;
+ if (eltsz > 1 && buf->align < TYPE_ALIGN (type))
+ atype = build_aligned_type (type, buf->align);
+ tree dst = build2_loc (buf->loc, MEM_REF, atype, buf->base,
+ build_int_cst (buf->alias_type, off));
+ tree src;
+ gimple *g;
+ if (all_ones
+ && nonzero_first == start
+ && nonzero_last == start + eltsz)
+ src = build_zero_cst (type);
+ else
+ {
+ src = make_ssa_name (type);
+ g = gimple_build_assign (src, unshare_expr (dst));
+ gimple_set_location (g, buf->loc);
+ gsi_insert_before (buf->gsi, g, GSI_SAME_STMT);
+ tree mask = native_interpret_expr (type,
+ buf->buf + i + start,
+ eltsz);
+ gcc_assert (mask && TREE_CODE (mask) == INTEGER_CST);
+ mask = fold_build1 (BIT_NOT_EXPR, type, mask);
+ tree src_masked = make_ssa_name (type);
+ g = gimple_build_assign (src_masked, BIT_AND_EXPR,
+ src, mask);
+ gimple_set_location (g, buf->loc);
+ gsi_insert_before (buf->gsi, g, GSI_SAME_STMT);
+ src = src_masked;
+ }
+ g = gimple_build_assign (dst, src);
+ gimple_set_location (g, buf->loc);
+ gsi_insert_before (buf->gsi, g, GSI_SAME_STMT);
+ break;
+ }
+ }
+ }
+ if (full)
+ {
+ if (padding_bytes)
+ {
+ tree atype = build_array_type_nelts (char_type_node, padding_bytes);
+ tree dst = build2_loc (buf->loc, MEM_REF, atype, buf->base,
+ build_int_cst (buf->alias_type,
+ buf->off + end
+ - padding_bytes));
+ tree src = build_constructor (atype, NULL);
+ gimple *g = gimple_build_assign (dst, src);
+ gimple_set_location (g, buf->loc);
+ gsi_insert_before (buf->gsi, g, GSI_SAME_STMT);
+ }
+ size_t end_rem = end % UNITS_PER_WORD;
+ buf->off += end - end_rem;
+ buf->size = end_rem;
+ memset (buf->buf, 0, buf->size);
+ buf->padding_bytes = 0;
+ }
+ else
+ {
+ memmove (buf->buf, buf->buf + end, buf->size - end);
+ buf->off += end;
+ buf->size -= end;
+ buf->padding_bytes = padding_bytes;
+ }
+}
+
+/* Append PADDING_BYTES padding bytes. */
+
+static void
+clear_padding_add_padding (clear_padding_struct *buf,
+ HOST_WIDE_INT padding_bytes)
+{
+ if (padding_bytes == 0)
+ return;
+ if ((unsigned HOST_WIDE_INT) padding_bytes + buf->size
+ > (unsigned HOST_WIDE_INT) clear_padding_buf_size)
+ clear_padding_flush (buf, false);
+ if ((unsigned HOST_WIDE_INT) padding_bytes + buf->size
+ > (unsigned HOST_WIDE_INT) clear_padding_buf_size)
+ {
+ memset (buf->buf + buf->size, ~0, clear_padding_buf_size - buf->size);
+ padding_bytes -= clear_padding_buf_size - buf->size;
+ buf->size = clear_padding_buf_size;
+ clear_padding_flush (buf, false);
+ gcc_assert (buf->padding_bytes);
+ /* At this point buf->buf[0] through buf->buf[buf->size - 1]
+ is guaranteed to be all ones. */
+ padding_bytes += buf->size;
+ buf->size = padding_bytes % UNITS_PER_WORD;
+ memset (buf->buf, ~0, buf->size);
+ buf->off += padding_bytes - buf->size;
+ buf->padding_bytes += padding_bytes - buf->size;
+ }
+ else
+ {
+ memset (buf->buf + buf->size, ~0, padding_bytes);
+ buf->size += padding_bytes;
+ }
+}
+
+static void clear_padding_type (clear_padding_struct *, tree, HOST_WIDE_INT);
+
+/* Clear padding bits of union type TYPE. */
+
+static void
+clear_padding_union (clear_padding_struct *buf, tree type, HOST_WIDE_INT sz)
+{
+ clear_padding_struct *union_buf;
+ HOST_WIDE_INT start_off = 0, next_off = 0;
+ size_t start_size = 0;
+ if (buf->union_ptr)
+ {
+ start_off = buf->off + buf->size;
+ next_off = start_off + sz;
+ start_size = start_off % UNITS_PER_WORD;
+ start_off -= start_size;
+ clear_padding_flush (buf, true);
+ union_buf = buf;
+ }
+ else
+ {
+ if (sz + buf->size > clear_padding_buf_size)
+ clear_padding_flush (buf, false);
+ union_buf = XALLOCA (clear_padding_struct);
+ union_buf->loc = buf->loc;
+ union_buf->base = NULL_TREE;
+ union_buf->alias_type = NULL_TREE;
+ union_buf->gsi = NULL;
+ union_buf->align = 0;
+ union_buf->off = 0;
+ union_buf->padding_bytes = 0;
+ union_buf->sz = sz;
+ union_buf->size = 0;
+ if (sz + buf->size <= clear_padding_buf_size)
+ union_buf->union_ptr = buf->buf + buf->size;
+ else
+ union_buf->union_ptr = XNEWVEC (unsigned char, sz);
+ memset (union_buf->union_ptr, ~0, sz);
+ }
+
+ for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
+ if (TREE_CODE (field) == FIELD_DECL)
+ {
+ HOST_WIDE_INT fldsz = tree_to_shwi (DECL_SIZE_UNIT (field));
+ gcc_assert (union_buf->size == 0);
+ union_buf->off = start_off;
+ union_buf->size = start_size;
+ memset (union_buf->buf, ~0, start_size);
+ clear_padding_type (union_buf, TREE_TYPE (field), fldsz);
+ clear_padding_add_padding (union_buf, sz - fldsz);
+ clear_padding_flush (union_buf, true);
+ }
+
+ if (buf == union_buf)
+ {
+ buf->off = next_off;
+ buf->size = next_off % UNITS_PER_WORD;
+ buf->off -= buf->size;
+ memset (buf->buf, ~0, buf->size);
+ }
+ else if (sz + buf->size <= clear_padding_buf_size)
+ buf->size += sz;
+ else
+ {
+ unsigned char *union_ptr = union_buf->union_ptr;
+ while (sz)
+ {
+ clear_padding_flush (buf, false);
+ HOST_WIDE_INT this_sz
+ = MIN ((unsigned HOST_WIDE_INT) sz,
+ clear_padding_buf_size - buf->size);
+ memcpy (buf->buf + buf->size, union_ptr, this_sz);
+ buf->size += this_sz;
+ union_ptr += this_sz;
+ sz -= this_sz;
+ }
+ XDELETE (union_buf->union_ptr);
+ }
+}
+
+/* The only known floating point formats with padding bits are the
+ IEEE extended ones. */
+
+static bool
+clear_padding_real_needs_padding_p (tree type)
+{
+ const struct real_format *fmt = REAL_MODE_FORMAT (TYPE_MODE (type));
+ return (fmt->b == 2
+ && fmt->signbit_ro == fmt->signbit_rw
+ && (fmt->signbit_ro == 79 || fmt->signbit_ro == 95));
+}
+
+/* Return true if TYPE might contain any padding bits. */
+
+static bool
+clear_padding_type_may_have_padding_p (tree type)
+{
+ switch (TREE_CODE (type))
+ {
+ case RECORD_TYPE:
+ case UNION_TYPE:
+ return true;
+ case ARRAY_TYPE:
+ case COMPLEX_TYPE:
+ case VECTOR_TYPE:
+ return clear_padding_type_may_have_padding_p (TREE_TYPE (type));
+ case REAL_TYPE:
+ return clear_padding_real_needs_padding_p (type);
+ default:
+ return false;
+ }
+}
+
+/* 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, buf->sz);
+ 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. */
+
+static void
+clear_padding_type (clear_padding_struct *buf, tree type, HOST_WIDE_INT sz)
+{
+ switch (TREE_CODE (type))
+ {
+ case RECORD_TYPE:
+ HOST_WIDE_INT cur_pos;
+ cur_pos = 0;
+ for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
+ if (TREE_CODE (field) == FIELD_DECL)
+ {
+ if (DECL_BIT_FIELD (field))
+ {
+ if (DECL_NAME (field) == NULL_TREE)
+ continue;
+ HOST_WIDE_INT fldsz = TYPE_PRECISION (TREE_TYPE (field));
+ if (fldsz == 0)
+ continue;
+ HOST_WIDE_INT pos = int_byte_position (field);
+ HOST_WIDE_INT bpos
+ = tree_to_uhwi (DECL_FIELD_BIT_OFFSET (field));
+ bpos %= BITS_PER_UNIT;
+ HOST_WIDE_INT end
+ = ROUND_UP (bpos + fldsz, BITS_PER_UNIT) / BITS_PER_UNIT;
+ if (pos + end > cur_pos)
+ {
+ clear_padding_add_padding (buf, pos + end - cur_pos);
+ cur_pos = pos + end;
+ }
+ gcc_assert (cur_pos > pos
+ && ((unsigned HOST_WIDE_INT) buf->size
+ >= (unsigned HOST_WIDE_INT) cur_pos - pos));
+ unsigned char *p = buf->buf + buf->size - (cur_pos - pos);
+ if (BYTES_BIG_ENDIAN != WORDS_BIG_ENDIAN)
+ sorry_at (buf->loc, "PDP11 bit-field handling unsupported"
+ " in %qs", "__builtin_clear_padding");
+ else if (BYTES_BIG_ENDIAN)
+ {
+ /* Big endian. */
+ if (bpos + fldsz <= BITS_PER_UNIT)
+ *p &= ~(((1 << fldsz) - 1)
+ << (BITS_PER_UNIT - bpos - fldsz));
+ else
+ {
+ if (bpos)
+ {
+ *p &= ~(((1U << BITS_PER_UNIT) - 1) >> bpos);
+ p++;
+ fldsz -= BITS_PER_UNIT - bpos;
+ }
+ memset (p, 0, fldsz / BITS_PER_UNIT);
+ p += fldsz / BITS_PER_UNIT;
+ fldsz %= BITS_PER_UNIT;
+ if (fldsz)
+ *p &= ((1U << BITS_PER_UNIT) - 1) >> fldsz;
+ }
+ }
+ else
+ {
+ /* Little endian. */
+ if (bpos + fldsz <= BITS_PER_UNIT)
+ *p &= ~(((1 << fldsz) - 1) << bpos);
+ else
+ {
+ if (bpos)
+ {
+ *p &= ~(((1 << BITS_PER_UNIT) - 1) << bpos);
+ p++;
+ fldsz -= BITS_PER_UNIT - bpos;
+ }
+ memset (p, 0, fldsz / BITS_PER_UNIT);
+ p += fldsz / BITS_PER_UNIT;
+ fldsz %= BITS_PER_UNIT;
+ if (fldsz)
+ *p &= ~((1 << fldsz) - 1);
+ }
+ }
+ }
+ else
+ {
+ HOST_WIDE_INT pos = int_byte_position (field);
+ HOST_WIDE_INT fldsz = tree_to_shwi (DECL_SIZE_UNIT (field));
+ gcc_assert (pos >= 0 && fldsz >= 0 && pos >= cur_pos);
+ clear_padding_add_padding (buf, pos - cur_pos);
+ cur_pos = pos;
+ clear_padding_type (buf, TREE_TYPE (field), fldsz);
+ cur_pos += fldsz;
+ }
+ }
+ gcc_assert (sz >= cur_pos);
+ clear_padding_add_padding (buf, sz - cur_pos);
+ break;
+ case ARRAY_TYPE:
+ HOST_WIDE_INT nelts, fldsz;
+ fldsz = int_size_in_bytes (TREE_TYPE (type));
+ nelts = sz / fldsz;
+ if (nelts > 1
+ && sz > 8 * UNITS_PER_WORD
+ && buf->union_ptr == NULL
+ && clear_padding_type_may_have_padding_p (TREE_TYPE (type)))
+ {
+ /* 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 = fldsz;
+ 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), fldsz);
+ break;
+ case UNION_TYPE:
+ clear_padding_union (buf, type, sz);
+ break;
+ case REAL_TYPE:
+ gcc_assert ((size_t) sz <= clear_padding_unit);
+ if ((unsigned HOST_WIDE_INT) sz + buf->size > clear_padding_buf_size)
+ clear_padding_flush (buf, false);
+ if (clear_padding_real_needs_padding_p (type))
+ {
+ /* Use native_interpret_expr + native_encode_expr to figure out
+ which bits are padding. */
+ memset (buf->buf + buf->size, ~0, sz);
+ tree cst = native_interpret_expr (type, buf->buf + buf->size, sz);
+ gcc_assert (cst && TREE_CODE (cst) == REAL_CST);
+ int len = native_encode_expr (cst, buf->buf + buf->size, sz);
+ gcc_assert (len > 0 && (size_t) len == (size_t) sz);
+ for (size_t i = 0; i < (size_t) sz; i++)
+ buf->buf[buf->size + i] ^= ~0;
+ }
+ else
+ memset (buf->buf + buf->size, 0, sz);
+ buf->size += sz;
+ break;
+ case COMPLEX_TYPE:
+ fldsz = int_size_in_bytes (TREE_TYPE (type));
+ clear_padding_type (buf, TREE_TYPE (type), fldsz);
+ clear_padding_type (buf, TREE_TYPE (type), fldsz);
+ break;
+ case VECTOR_TYPE:
+ nelts = TYPE_VECTOR_SUBPARTS (type).to_constant ();
+ fldsz = int_size_in_bytes (TREE_TYPE (type));
+ for (HOST_WIDE_INT i = 0; i < nelts; i++)
+ clear_padding_type (buf, TREE_TYPE (type), fldsz);
+ break;
+ case NULLPTR_TYPE:
+ gcc_assert ((size_t) sz <= clear_padding_unit);
+ if ((unsigned HOST_WIDE_INT) sz + buf->size > clear_padding_buf_size)
+ clear_padding_flush (buf, false);
+ memset (buf->buf + buf->size, ~0, sz);
+ buf->size += sz;
+ break;
+ default:
+ gcc_assert ((size_t) sz <= clear_padding_unit);
+ if ((unsigned HOST_WIDE_INT) sz + buf->size > clear_padding_buf_size)
+ clear_padding_flush (buf, false);
+ memset (buf->buf + buf->size, 0, sz);
+ buf->size += sz;
+ break;
+ }
+}
+
+/* Fold __builtin_clear_padding builtin. */
+
+static bool
+gimple_fold_builtin_clear_padding (gimple_stmt_iterator *gsi)
+{
+ gimple *stmt = gsi_stmt (*gsi);
+ gcc_assert (gimple_call_num_args (stmt) == 2);
+ tree ptr = gimple_call_arg (stmt, 0);
+ tree typearg = gimple_call_arg (stmt, 1);
+ tree type = TREE_TYPE (TREE_TYPE (typearg));
+ location_t loc = gimple_location (stmt);
+ clear_padding_struct buf;
+ gimple_stmt_iterator gsiprev = *gsi;
+ /* This should be folded during the lower pass. */
+ gcc_assert (!gimple_in_ssa_p (cfun) && cfun->cfg == NULL);
+ gcc_assert (COMPLETE_TYPE_P (type));
+ gsi_prev (&gsiprev);
+
+ buf.loc = loc;
+ buf.base = ptr;
+ buf.alias_type = NULL_TREE;
+ buf.gsi = gsi;
+ buf.align = get_pointer_alignment (ptr);
+ unsigned int talign = min_align_of_type (type) * BITS_PER_UNIT;
+ buf.align = MAX (buf.align, talign);
+ buf.off = 0;
+ buf.padding_bytes = 0;
+ buf.size = 0;
+ buf.sz = int_size_in_bytes (type);
+ buf.union_ptr = NULL;
+ if (buf.sz < 0 && int_size_in_bytes (strip_array_types (type)) < 0)
+ sorry_at (loc, "%s not supported for variable length aggregates",
+ "__builtin_clear_padding");
+ /* The implementation currently assumes 8-bit host and target
+ chars which is the case for all currently supported targets
+ and hosts and is required e.g. for native_{encode,interpret}* APIs. */
+ else if (CHAR_BIT != 8 || BITS_PER_UNIT != 8)
+ sorry_at (loc, "%s not supported on this target",
+ "__builtin_clear_padding");
+ else if (!clear_padding_type_may_have_padding_p (type))
+ ;
+ else if (TREE_CODE (type) == ARRAY_TYPE && buf.sz < 0)
+ {
+ 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);
+ buf.alias_type = build_pointer_type (elttype);
+ clear_padding_emit_loop (&buf, elttype, end);
+ }
+ }
+ else
+ {
+ if (!is_gimple_mem_ref_addr (buf.base))
+ {
+ buf.base = make_ssa_name (TREE_TYPE (ptr));
+ gimple *g = gimple_build_assign (buf.base, ptr);
+ gimple_set_location (g, loc);
+ gsi_insert_before (gsi, g, GSI_SAME_STMT);
+ }
+ buf.alias_type = build_pointer_type (type);
+ clear_padding_type (&buf, type, buf.sz);
+ clear_padding_flush (&buf, true);
+ }
+
+ gimple_stmt_iterator gsiprev2 = *gsi;
+ gsi_prev (&gsiprev2);
+ if (gsi_stmt (gsiprev) == gsi_stmt (gsiprev2))
+ gsi_replace (gsi, gimple_build_nop (), true);
+ else
+ {
+ gsi_remove (gsi, true);
+ *gsi = gsiprev2;
+ }
+ return true;
+}
+
/* Fold the non-target builtin at *GSI and return whether any simplification
was made. */
@@ -4105,6 +4797,9 @@ gimple_fold_builtin (gimple_stmt_iterator *gsi)
case BUILT_IN_REALLOC:
return gimple_fold_builtin_realloc (gsi);
+ case BUILT_IN_CLEAR_PADDING:
+ return gimple_fold_builtin_clear_padding (gsi);
+
default:;
}
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index fd0b520..53ec9ec 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -3384,6 +3384,20 @@ gimplify_call_expr (tree *expr_p, gimple_seq *pre_p, bool want_value)
cfun->calls_eh_return = true;
break;
+ case BUILT_IN_CLEAR_PADDING:
+ if (call_expr_nargs (*expr_p) == 1)
+ {
+ /* Remember the original type of the argument in an internal
+ dummy second argument, as in GIMPLE pointer conversions are
+ useless. */
+ p = CALL_EXPR_ARG (*expr_p, 0);
+ *expr_p
+ = build_call_expr_loc (EXPR_LOCATION (*expr_p), fndecl, 2, p,
+ build_zero_cst (TREE_TYPE (p)));
+ return GS_OK;
+ }
+ break;
+
default:
;
}
diff --git a/gcc/testsuite/c-c++-common/builtin-clear-padding-1.c b/gcc/testsuite/c-c++-common/builtin-clear-padding-1.c
new file mode 100644
index 0000000..8b036bf
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/builtin-clear-padding-1.c
@@ -0,0 +1,19 @@
+/* PR libstdc++/88101 */
+/* { dg-do compile } */
+
+struct S;
+struct T { char a; long long b; };
+
+void
+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" } */
+ __builtin_clear_padding (&p);
+ __builtin_clear_padding (&p, 1); /* { dg-error "too many arguments to function '__builtin_clear_padding'" } */
+ __builtin_clear_padding (&p, &p); /* { dg-error "too many arguments to function '__builtin_clear_padding'" } */
+ __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" } */
+}
diff --git a/gcc/testsuite/c-c++-common/torture/builtin-clear-padding-1.c b/gcc/testsuite/c-c++-common/torture/builtin-clear-padding-1.c
new file mode 100644
index 0000000..6b01a56
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/builtin-clear-padding-1.c
@@ -0,0 +1,47 @@
+/* PR libstdc++/88101 */
+
+int i1, i2;
+long double l1, l2;
+struct S { char a; short b; char c; int d; char e; long long f; char g; long double h; } s1, s2;
+struct T { int a; struct S b[3]; int c; } t1, t2;
+struct U { int a : 3; int : 2; int b : 15; int : 14; int c : 1; int : 0; int : 3; int d : 2; int : 3; int e : 13; int : 3; signed char f; } u1, u2;
+
+__attribute__((noipa)) void
+foo (int *i, long double *l, struct S *s, struct T *t, struct U *u)
+{
+ *i = 123;
+ *l = -123.456L;
+ s->a = 1; s->b = 2; s->c = 3; s->d = 4; s->e = 5; s->f = 6; s->g = 7; s->h = 18.52L;
+ t->a = 8; t->c = 9;
+ t->b[0].a = 11; t->b[0].b = 12; t->b[0].c = 13; t->b[0].d = 14;
+ t->b[0].e = 15; t->b[0].f = 16; t->b[0].g = 17; t->b[0].h = 18.26L;
+ t->b[1].a = 21; t->b[1].b = 22; t->b[1].c = 23; t->b[1].d = 24;
+ t->b[1].e = 25; t->b[1].f = 26; t->b[1].g = 27; t->b[1].h = 28.26L;
+ t->b[2].a = 31; t->b[2].b = 32; t->b[2].c = 33; t->b[2].d = 34;
+ t->b[2].e = 35; t->b[2].f = 36; t->b[2].g = 37; t->b[2].h = 38.26L;
+ u->a = -1; u->b = -1; u->c = -1; u->d = -1; u->e = -1; u->f = -1;
+}
+
+int
+main ()
+{
+ __builtin_memset (&i2, -1, sizeof (i2));
+ __builtin_memset (&l2, -1, sizeof (i2));
+ __builtin_memset (&s2, -1, sizeof (s2));
+ __builtin_memset (&t2, -1, sizeof (t2));
+ __builtin_memset (&u2, -1, sizeof (u2));
+ foo (&i1, &l1, &s1, &t1, &u1);
+ foo (&i2, &l2, &s2, &t2, &u2);
+ __builtin_clear_padding (&i2);
+ __builtin_clear_padding (&l2);
+ __builtin_clear_padding (&s2);
+ __builtin_clear_padding (&t2);
+ __builtin_clear_padding (&u2);
+ if (__builtin_memcmp (&i1, &i2, sizeof (i1))
+ || __builtin_memcmp (&l1, &l2, sizeof (l1))
+ || __builtin_memcmp (&s1, &s2, sizeof (s1))
+ || __builtin_memcmp (&t1, &t2, sizeof (t1))
+ || __builtin_memcmp (&u1, &u2, sizeof (u1)))
+ __builtin_abort ();
+ return 0;
+}
diff --git a/gcc/testsuite/c-c++-common/torture/builtin-clear-padding-2.c b/gcc/testsuite/c-c++-common/torture/builtin-clear-padding-2.c
new file mode 100644
index 0000000..1188bc0
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/builtin-clear-padding-2.c
@@ -0,0 +1,24 @@
+/* PR libstdc++/88101 */
+
+typedef int T __attribute__((aligned (16384)));
+struct S { char a; short b; long double c; T d; T e; long long f; };
+
+__attribute__((noipa)) void
+foo (struct S *s)
+{
+ s->a = -1; s->b = -1; s->c = -18.52L; s->d = -1; s->e = -1; s->f = -1;
+}
+
+int
+main ()
+{
+ struct S s1, s2;
+ __builtin_memset (&s1, 0, sizeof (s1));
+ __builtin_memset (&s2, -1, sizeof (s2));
+ foo (&s1);
+ foo (&s2);
+ __builtin_clear_padding (&s2);
+ if (__builtin_memcmp (&s1, &s2, sizeof (s1)))
+ __builtin_abort ();
+ return 0;
+}
diff --git a/gcc/testsuite/c-c++-common/torture/builtin-clear-padding-3.c b/gcc/testsuite/c-c++-common/torture/builtin-clear-padding-3.c
new file mode 100644
index 0000000..edb7c8e
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/builtin-clear-padding-3.c
@@ -0,0 +1,65 @@
+/* PR libstdc++/88101 */
+
+union V { char a; signed char b; unsigned char c; };
+struct T { char a; int b; union V c; };
+union U { int a; long double b; struct T c; };
+struct S { char a; union U b; long long c; char d; } s1, s2;
+
+__attribute__((noipa)) void
+foo (struct S *s, int x)
+{
+ s->a = -1; s->c = -1; s->d = -1;
+ switch (x)
+ {
+ case 0:
+ s->b.a = -1;
+ break;
+ case 1:
+ s->b.b = -12345.25L;
+ break;
+ case 2:
+ s->b.c.a = -1;
+ s->b.c.b = -1;
+ s->b.c.c.b = -1;
+ break;
+ }
+}
+
+int
+main ()
+{
+ __builtin_memset (&s1, 0, sizeof (s1));
+ __builtin_memset (&s2, -1, sizeof (s2));
+ foo (&s1, 0);
+ foo (&s2, 0);
+ __builtin_clear_padding (&s2);
+ if (s2.b.a != (char) -1)
+ __builtin_abort ();
+ __builtin_clear_padding (&s2.b.a);
+ __builtin_memset (&s2.b.a + 1, 0, sizeof (union U) - sizeof (s2.b.a));
+ if (__builtin_memcmp (&s1, &s2, sizeof (s1)))
+ __builtin_abort ();
+ __builtin_memset (&s1, 0, sizeof (s1));
+ __builtin_memset (&s2, -1, sizeof (s2));
+ foo (&s1, 1);
+ foo (&s2, 1);
+ __builtin_clear_padding (&s2);
+ if (s2.b.b != -12345.25L)
+ __builtin_abort ();
+ __builtin_clear_padding (&s2.b.b);
+ __builtin_memset (&s2.b.b + 1, 0, sizeof (union U) - sizeof (s2.b.b));
+ if (__builtin_memcmp (&s1, &s2, sizeof (s1)))
+ __builtin_abort ();
+ __builtin_memset (&s1, 0, sizeof (s1));
+ __builtin_memset (&s2, -1, sizeof (s2));
+ foo (&s1, 2);
+ foo (&s2, 2);
+ __builtin_clear_padding (&s2);
+ if (s2.b.c.a != (char) -1 || s2.b.c.b != -1 || s2.b.c.c.b != -1)
+ __builtin_abort ();
+ __builtin_clear_padding (&s2.b.c);
+ __builtin_memset (&s2.b.c + 1, 0, sizeof (union U) - sizeof (s2.b.c));
+ if (__builtin_memcmp (&s1, &s2, sizeof (s1)))
+ __builtin_abort ();
+ return 0;
+}
diff --git a/gcc/testsuite/c-c++-common/torture/builtin-clear-padding-4.c b/gcc/testsuite/c-c++-common/torture/builtin-clear-padding-4.c
new file mode 100644
index 0000000..d24f3b5
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/builtin-clear-padding-4.c
@@ -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;
+}
diff --git a/gcc/testsuite/c-c++-common/torture/builtin-clear-padding-5.c b/gcc/testsuite/c-c++-common/torture/builtin-clear-padding-5.c
new file mode 100644
index 0000000..d5dbafe
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/builtin-clear-padding-5.c
@@ -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;
+}
diff --git a/gcc/testsuite/g++.dg/torture/builtin-clear-padding-1.C b/gcc/testsuite/g++.dg/torture/builtin-clear-padding-1.C
new file mode 100644
index 0000000..625a047
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/builtin-clear-padding-1.C
@@ -0,0 +1,31 @@
+/* PR libstdc++/88101 */
+
+struct S {} s1, s2;
+struct T : public S { char a; short b; char c; } t1, t2;
+struct U : public T { char d; long long e; char f; } u1, u2;
+
+__attribute__((noipa)) void
+foo (T *t, U *u)
+{
+ int i;
+ t->a = -1; t->b = -1; t->c = -1;
+ u->a = -1; u->b = -1; u->c = -1; u->d = -1; u->e = -1; u->f = -1;
+}
+
+int
+main ()
+{
+ __builtin_memset (&s2, -1, sizeof (s2));
+ __builtin_memset (&t2, -1, sizeof (t2));
+ __builtin_memset (&u2, -1, sizeof (u2));
+ foo (&t1, &u1);
+ foo (&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;
+}
diff --git a/gcc/testsuite/g++.dg/torture/builtin-clear-padding-2.C b/gcc/testsuite/g++.dg/torture/builtin-clear-padding-2.C
new file mode 100644
index 0000000..19cc78f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/builtin-clear-padding-2.C
@@ -0,0 +1,34 @@
+/* PR libstdc++/88101 */
+
+#include <new>
+
+struct S { char a; short b; char c; long long d; char e; decltype (nullptr) f; char g; };
+alignas (S) unsigned char buf1[sizeof (S)];
+alignas (S) unsigned char buf2[sizeof (S)];
+
+template <int N>
+void
+foo ()
+{
+ __builtin_clear_padding ((S *) buf2);
+}
+
+void
+bar (S *s)
+{
+ s->a = -1; s->b = -1; s->c = -1; s->d = -1; s->e = -1; s->g = -1;
+}
+
+int
+main ()
+{
+ S *s1 = new (buf1) S;
+ S *s2 = new (buf2) S;
+ __builtin_memset (s1, 0, sizeof (S));
+ __builtin_memset (s2, ~0, sizeof (S));
+ bar (s1);
+ bar (s2);
+ foo <0> ();
+ if (__builtin_memcmp (s1, s2, sizeof (S)) != 0)
+ __builtin_abort ();
+}
diff --git a/gcc/testsuite/gcc.dg/builtin-clear-padding-1.c b/gcc/testsuite/gcc.dg/builtin-clear-padding-1.c
new file mode 100644
index 0000000..27ffc0a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-clear-padding-1.c
@@ -0,0 +1,10 @@
+/* PR libstdc++/88101 */
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+void
+foo (int n)
+{
+ struct S { char a; int b[n]; long long c; } s;
+ __builtin_clear_padding (&s); /* { dg-message "unimplemented: __builtin_clear_padding not supported for variable length aggregates" } */
+}