public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] c++, v2: Add __builtin_clear_padding builtin - C++20 P0528R3 compiler side [PR88101]
@ 2020-11-19 16:30 Jakub Jelinek
  2020-11-19 22:31 ` Jakub Jelinek
  2020-11-20  9:19 ` Richard Biener
  0 siblings, 2 replies; 5+ messages in thread
From: Jakub Jelinek @ 2020-11-19 16:30 UTC (permalink / raw)
  To: Jason Merrill, Richard Biener, Jonathan Wakely, Thomas Rodgers
  Cc: gcc-patches

Hi!

This is the whole __builtin_clear_padding patchset merged into a single
patch, + 2 new changes - one is that fold_builtin_1 now folds the
1 argument (meant for users) __builtin_clear_padding into an internal
2 argument form, where the second argument is NULL of the first argument's
type, such that gimplifier's stripping of useless type conversions doesn't
change behavior, and handling NULLPTR_TYPE as all padding bits, because
lvalue-to-rvalue conversions with decltype(nullptr) type don't really read
anything from the memory and so we need to clear all the bits as padding.
Here is the full description:

The following patch implements __builtin_clear_padding builtin that clears
the padding bits in object representation (but preserves value
representation).  Inside of unions it clears only those padding bits that
are padding for all the union members (so that it never alters value
representation).

It handles trailing padding, padding in the middle of structs including
bitfields (PDP11 unhandled, I've never figured out how those bitfields
work), VLAs (doesn't handle variable length structures, but I think almost
nobody uses them and it isn't worth the extra complexity).  For VLAs and
sufficiently large arrays it uses runtime clearing loop instead of emitting
straight-line code (unless arrays are inside of a union).

The way I think this can be used for atomics is e.g. if the structures
are power of two sized and small enough that we use the hw atomics
for say compare_exchange __builtin_clear_padding could be called first on
the address of expected and desired arguments (for desired only if we want
to ensure that most of the time the atomic memory will have padding bits
cleared), then perform the weak cmpxchg and if that fails, we got the
value from the atomic memory; we can call __builtin_clear_padding on a copy
of that and then compare it with expected, and if it is the same with the
padding bits masked off, we can use the original with whatever random
padding bits in it as the new expected for next cmpxchg.
__builtin_clear_padding itself is not atomic and therefore it shouldn't
be called on the atomic memory itself, but compare_exchange*'s expected
argument is a reference and normally the implementation may store there
the current value from memory, so padding bits can be cleared in that,
and desired is passed by value rather than reference, so clearing is fine
too.

When using libatomic, we can use it either that way, or add new libatomic
APIs that accept another argument, pointer to the padding bit bitmask,
and construct that in the template as
  alignas (_T) unsigned char _mask[sizeof (_T)];
  std::memset (_mask, ~0, sizeof (_mask));
  __builtin_clear_padding ((_T *) _mask);
which will have bits cleared for padding bits and set for bits taking part
in the value representation.  Then libatomic could internally instead
of using memcmp compare
for (i = 0; i < N; i++) if ((val1[i] & mask[i]) != (val2[i] & mask[i]))

Tested on x86_64-linux, ok for trunk if it passes full bootstrap/regtest?

2020-11-19  Jakub Jelinek  <jakub@redhat.com>

	PR libstdc++/88101
gcc/
	* builtins.def (BUILT_IN_CLEAR_PADDING): New built-in function.
	* builtins.c (fold_builtin_1): Handle BUILT_IN_CLEAR_PADDING.
	* gimple-fold.c (clear_padding_unit, clear_padding_buf_size): New
	const variables.
	(struct clear_padding_struct): New type.
	(clear_padding_flush, clear_padding_add_padding,
	clear_padding_emit_loop, clear_padding_type,
	clear_padding_union, clear_padding_real_needs_padding_p,
	clear_padding_type_may_have_padding_p,
	gimple_fold_builtin_clear_padding): New functions.
	(gimple_fold_builtin): Handle BUILT_IN_CLEAR_PADDING.
	* doc/extend.texi (__builtin_clear_padding): Document.
gcc/c-family/
	* c-common.c (check_builtin_function_arguments): Handle
	BUILT_IN_CLEAR_PADDING.
gcc/testsuite/
	* c-c++-common/builtin-clear-padding-1.c: New test.
	* c-c++-common/torture/builtin-clear-padding-1.c: New test.
	* c-c++-common/torture/builtin-clear-padding-2.c: New test.
	* c-c++-common/torture/builtin-clear-padding-3.c: New test.
	* c-c++-common/torture/builtin-clear-padding-4.c: New test.
	* c-c++-common/torture/builtin-clear-padding-5.c: New test.
	* g++.dg/torture/builtin-clear-padding-1.C: New test.
	* g++.dg/torture/builtin-clear-padding-2.C: New test.
	* gcc.dg/builtin-clear-padding-1.c: New test.

--- gcc/builtins.def.jj	2020-11-18 09:38:28.481816977 +0100
+++ gcc/builtins.def	2020-11-19 16:15:50.573639579 +0100
@@ -839,6 +839,7 @@ DEF_EXT_LIB_BUILTIN    (BUILT_IN_CLEAR_C
 /* [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)
--- gcc/builtins.c.jj	2020-11-19 12:34:10.749514278 +0100
+++ gcc/builtins.c	2020-11-19 16:23:55.261250903 +0100
@@ -11189,6 +11189,13 @@ fold_builtin_1 (location_t loc, tree exp
 	return build_empty_stmt (loc);
       break;
 
+    case BUILT_IN_CLEAR_PADDING:
+      /* Remember the original type of the argument in an internal
+	 dummy second argument, as in GIMPLE pointer conversions are
+	 useless.  */
+      return build_call_expr_loc (loc, fndecl, 2, arg0,
+				  build_zero_cst (TREE_TYPE (arg0)));
+
     default:
       break;
     }
--- gcc/gimple-fold.c.jj	2020-11-18 09:38:29.056810417 +0100
+++ gcc/gimple-fold.c	2020-11-19 16:35:41.335398582 +0100
@@ -3948,6 +3948,693 @@ gimple_fold_builtin_realloc (gimple_stmt
   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;
+  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 (ptr_type_node,
+						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 (ptr_type_node, 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 (ptr_type_node,
+						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->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.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);
+	  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);
+	}
+      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 +4792,9 @@ gimple_fold_builtin (gimple_stmt_iterato
     case BUILT_IN_REALLOC:
       return gimple_fold_builtin_realloc (gsi);
 
+    case BUILT_IN_CLEAR_PADDING:
+      return gimple_fold_builtin_clear_padding (gsi);
+
     default:;
     }
 
--- gcc/doc/extend.texi.jj	2020-11-18 09:38:28.562816053 +0100
+++ gcc/doc/extend.texi	2020-11-19 16:15:50.578639523 +0100
@@ -13564,6 +13564,19 @@ initializers of variables usable in cons
 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
--- gcc/c-family/c-common.c.jj	2020-11-18 09:40:09.424665266 +0100
+++ gcc/c-family/c-common.c	2020-11-19 16:16:06.648461318 +0100
@@ -6178,6 +6178,39 @@ check_builtin_function_arguments (locati
 	}
       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;
     }
--- gcc/testsuite/c-c++-common/builtin-clear-padding-1.c.jj	2020-11-19 16:15:50.578639523 +0100
+++ gcc/testsuite/c-c++-common/builtin-clear-padding-1.c	2020-11-19 16:16:06.648461318 +0100
@@ -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" } */
+}
--- gcc/testsuite/c-c++-common/torture/builtin-clear-padding-1.c.jj	2020-11-19 16:15:50.578639523 +0100
+++ gcc/testsuite/c-c++-common/torture/builtin-clear-padding-1.c	2020-11-19 16:15:50.578639523 +0100
@@ -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;
+}
--- gcc/testsuite/c-c++-common/torture/builtin-clear-padding-2.c.jj	2020-11-19 16:15:50.578639523 +0100
+++ gcc/testsuite/c-c++-common/torture/builtin-clear-padding-2.c	2020-11-19 16:15:50.578639523 +0100
@@ -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;
+}
--- gcc/testsuite/c-c++-common/torture/builtin-clear-padding-3.c.jj	2020-11-19 16:15:50.579639512 +0100
+++ gcc/testsuite/c-c++-common/torture/builtin-clear-padding-3.c	2020-11-19 16:15:50.579639512 +0100
@@ -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;
+}
--- gcc/testsuite/c-c++-common/torture/builtin-clear-padding-4.c.jj	2020-11-19 16:17:07.550785893 +0100
+++ gcc/testsuite/c-c++-common/torture/builtin-clear-padding-4.c	2020-11-19 16:16:06.648461318 +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-19 16:17:07.551785882 +0100
+++ gcc/testsuite/c-c++-common/torture/builtin-clear-padding-5.c	2020-11-19 16:16:06.648461318 +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-1.c.jj	2020-11-19 16:15:50.579639512 +0100
+++ gcc/testsuite/gcc.dg/builtin-clear-padding-1.c	2020-11-19 16:15:50.579639512 +0100
@@ -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" } */
+}
--- gcc/testsuite/g++.dg/torture/builtin-clear-padding-1.C.jj	2020-11-19 16:17:07.551785882 +0100
+++ gcc/testsuite/g++.dg/torture/builtin-clear-padding-1.C	2020-11-19 16:16:14.380375575 +0100
@@ -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;
+}
--- gcc/testsuite/g++.dg/torture/builtin-clear-padding-2.C.jj	2020-11-19 16:56:35.602466744 +0100
+++ gcc/testsuite/g++.dg/torture/builtin-clear-padding-2.C	2020-11-19 16:59:42.434391419 +0100
@@ -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 ();
+}

	Jakub


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] c++, v2: Add __builtin_clear_padding builtin - C++20 P0528R3 compiler side [PR88101]
  2020-11-19 16:30 [PATCH] c++, v2: Add __builtin_clear_padding builtin - C++20 P0528R3 compiler side [PR88101] Jakub Jelinek
@ 2020-11-19 22:31 ` Jakub Jelinek
  2020-11-20  9:19 ` Richard Biener
  1 sibling, 0 replies; 5+ messages in thread
From: Jakub Jelinek @ 2020-11-19 22:31 UTC (permalink / raw)
  To: Jason Merrill, Richard Biener, Jonathan Wakely, Thomas Rodgers,
	gcc-patches

On Thu, Nov 19, 2020 at 05:30:06PM +0100, Jakub Jelinek via Gcc-patches wrote:
> Tested on x86_64-linux, ok for trunk if it passes full bootstrap/regtest?

Successfully bootstrapped/regtested on both x86_64-linux and i686-linux now.

	Jakub


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] c++, v2: Add __builtin_clear_padding builtin - C++20 P0528R3 compiler side [PR88101]
  2020-11-19 16:30 [PATCH] c++, v2: Add __builtin_clear_padding builtin - C++20 P0528R3 compiler side [PR88101] Jakub Jelinek
  2020-11-19 22:31 ` Jakub Jelinek
@ 2020-11-20  9:19 ` Richard Biener
  2020-11-20 10:55   ` [PATCH] c++, v3: " Jakub Jelinek
  1 sibling, 1 reply; 5+ messages in thread
From: Richard Biener @ 2020-11-20  9:19 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Jason Merrill, Jonathan Wakely, Thomas Rodgers, gcc-patches

On Thu, 19 Nov 2020, Jakub Jelinek wrote:

> Hi!
> 
> This is the whole __builtin_clear_padding patchset merged into a single
> patch, + 2 new changes - one is that fold_builtin_1 now folds the
> 1 argument (meant for users) __builtin_clear_padding into an internal
> 2 argument form, where the second argument is NULL of the first argument's
> type, such that gimplifier's stripping of useless type conversions doesn't
> change behavior, and handling NULLPTR_TYPE as all padding bits, because
> lvalue-to-rvalue conversions with decltype(nullptr) type don't really read
> anything from the memory and so we need to clear all the bits as padding.
> Here is the full description:
> 
> The following patch implements __builtin_clear_padding builtin that clears
> the padding bits in object representation (but preserves value
> representation).  Inside of unions it clears only those padding bits that
> are padding for all the union members (so that it never alters value
> representation).
> 
> It handles trailing padding, padding in the middle of structs including
> bitfields (PDP11 unhandled, I've never figured out how those bitfields
> work), VLAs (doesn't handle variable length structures, but I think almost
> nobody uses them and it isn't worth the extra complexity).  For VLAs and
> sufficiently large arrays it uses runtime clearing loop instead of emitting
> straight-line code (unless arrays are inside of a union).
> 
> The way I think this can be used for atomics is e.g. if the structures
> are power of two sized and small enough that we use the hw atomics
> for say compare_exchange __builtin_clear_padding could be called first on
> the address of expected and desired arguments (for desired only if we want
> to ensure that most of the time the atomic memory will have padding bits
> cleared), then perform the weak cmpxchg and if that fails, we got the
> value from the atomic memory; we can call __builtin_clear_padding on a copy
> of that and then compare it with expected, and if it is the same with the
> padding bits masked off, we can use the original with whatever random
> padding bits in it as the new expected for next cmpxchg.
> __builtin_clear_padding itself is not atomic and therefore it shouldn't
> be called on the atomic memory itself, but compare_exchange*'s expected
> argument is a reference and normally the implementation may store there
> the current value from memory, so padding bits can be cleared in that,
> and desired is passed by value rather than reference, so clearing is fine
> too.
> 
> When using libatomic, we can use it either that way, or add new libatomic
> APIs that accept another argument, pointer to the padding bit bitmask,
> and construct that in the template as
>   alignas (_T) unsigned char _mask[sizeof (_T)];
>   std::memset (_mask, ~0, sizeof (_mask));
>   __builtin_clear_padding ((_T *) _mask);
> which will have bits cleared for padding bits and set for bits taking part
> in the value representation.  Then libatomic could internally instead
> of using memcmp compare
> for (i = 0; i < N; i++) if ((val1[i] & mask[i]) != (val2[i] & mask[i]))
> 
> Tested on x86_64-linux, ok for trunk if it passes full bootstrap/regtest?
> 
> 2020-11-19  Jakub Jelinek  <jakub@redhat.com>
> 
> 	PR libstdc++/88101
> gcc/
> 	* builtins.def (BUILT_IN_CLEAR_PADDING): New built-in function.
> 	* builtins.c (fold_builtin_1): Handle BUILT_IN_CLEAR_PADDING.
> 	* gimple-fold.c (clear_padding_unit, clear_padding_buf_size): New
> 	const variables.
> 	(struct clear_padding_struct): New type.
> 	(clear_padding_flush, clear_padding_add_padding,
> 	clear_padding_emit_loop, clear_padding_type,
> 	clear_padding_union, clear_padding_real_needs_padding_p,
> 	clear_padding_type_may_have_padding_p,
> 	gimple_fold_builtin_clear_padding): New functions.
> 	(gimple_fold_builtin): Handle BUILT_IN_CLEAR_PADDING.
> 	* doc/extend.texi (__builtin_clear_padding): Document.
> gcc/c-family/
> 	* c-common.c (check_builtin_function_arguments): Handle
> 	BUILT_IN_CLEAR_PADDING.
> gcc/testsuite/
> 	* c-c++-common/builtin-clear-padding-1.c: New test.
> 	* c-c++-common/torture/builtin-clear-padding-1.c: New test.
> 	* c-c++-common/torture/builtin-clear-padding-2.c: New test.
> 	* c-c++-common/torture/builtin-clear-padding-3.c: New test.
> 	* c-c++-common/torture/builtin-clear-padding-4.c: New test.
> 	* c-c++-common/torture/builtin-clear-padding-5.c: New test.
> 	* g++.dg/torture/builtin-clear-padding-1.C: New test.
> 	* g++.dg/torture/builtin-clear-padding-2.C: New test.
> 	* gcc.dg/builtin-clear-padding-1.c: New test.
> 
> --- gcc/builtins.def.jj	2020-11-18 09:38:28.481816977 +0100
> +++ gcc/builtins.def	2020-11-19 16:15:50.573639579 +0100
> @@ -839,6 +839,7 @@ DEF_EXT_LIB_BUILTIN    (BUILT_IN_CLEAR_C
>  /* [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)
> --- gcc/builtins.c.jj	2020-11-19 12:34:10.749514278 +0100
> +++ gcc/builtins.c	2020-11-19 16:23:55.261250903 +0100
> @@ -11189,6 +11189,13 @@ fold_builtin_1 (location_t loc, tree exp
>  	return build_empty_stmt (loc);
>        break;
>  
> +    case BUILT_IN_CLEAR_PADDING:
> +      /* Remember the original type of the argument in an internal
> +	 dummy second argument, as in GIMPLE pointer conversions are
> +	 useless.  */
> +      return build_call_expr_loc (loc, fndecl, 2, arg0,
> +				  build_zero_cst (TREE_TYPE (arg0)));
> +

I'd rather make this change during gimplify_call_expr, I'm not
even sure at which point we'd hit the above (and if at all).

>      default:
>        break;
>      }
> --- gcc/gimple-fold.c.jj	2020-11-18 09:38:29.056810417 +0100
> +++ gcc/gimple-fold.c	2020-11-19 16:35:41.335398582 +0100
> @@ -3948,6 +3948,693 @@ gimple_fold_builtin_realloc (gimple_stmt
>    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;
> +  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 (ptr_type_node,
> +						buf->off + padding_end
> +						- padding_bytes));

You're using alias-set zero for all stores but since we're
effectively inspecting the layout of a type we could specify
that __builtin_clear_padding expects an object of said type
at the very location active as dynamic type.  So IMHO
less conservative but still sound would be to use
build_pointer_type (aggregate-type) in place of ptr_type_node.

I'm not sure it will make a difference but it might be useful
to parametrize 'ptr_type_node' in the IL generation?

Otherwise this looks OK.

Thanks,
Richard.

> +	  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 (ptr_type_node, 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 (ptr_type_node,
> +						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->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.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);
> +	  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);
> +	}
> +      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 +4792,9 @@ gimple_fold_builtin (gimple_stmt_iterato
>      case BUILT_IN_REALLOC:
>        return gimple_fold_builtin_realloc (gsi);
>  
> +    case BUILT_IN_CLEAR_PADDING:
> +      return gimple_fold_builtin_clear_padding (gsi);
> +
>      default:;
>      }
>  
> --- gcc/doc/extend.texi.jj	2020-11-18 09:38:28.562816053 +0100
> +++ gcc/doc/extend.texi	2020-11-19 16:15:50.578639523 +0100
> @@ -13564,6 +13564,19 @@ initializers of variables usable in cons
>  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
> --- gcc/c-family/c-common.c.jj	2020-11-18 09:40:09.424665266 +0100
> +++ gcc/c-family/c-common.c	2020-11-19 16:16:06.648461318 +0100
> @@ -6178,6 +6178,39 @@ check_builtin_function_arguments (locati
>  	}
>        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;
>      }
> --- gcc/testsuite/c-c++-common/builtin-clear-padding-1.c.jj	2020-11-19 16:15:50.578639523 +0100
> +++ gcc/testsuite/c-c++-common/builtin-clear-padding-1.c	2020-11-19 16:16:06.648461318 +0100
> @@ -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" } */
> +}
> --- gcc/testsuite/c-c++-common/torture/builtin-clear-padding-1.c.jj	2020-11-19 16:15:50.578639523 +0100
> +++ gcc/testsuite/c-c++-common/torture/builtin-clear-padding-1.c	2020-11-19 16:15:50.578639523 +0100
> @@ -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;
> +}
> --- gcc/testsuite/c-c++-common/torture/builtin-clear-padding-2.c.jj	2020-11-19 16:15:50.578639523 +0100
> +++ gcc/testsuite/c-c++-common/torture/builtin-clear-padding-2.c	2020-11-19 16:15:50.578639523 +0100
> @@ -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;
> +}
> --- gcc/testsuite/c-c++-common/torture/builtin-clear-padding-3.c.jj	2020-11-19 16:15:50.579639512 +0100
> +++ gcc/testsuite/c-c++-common/torture/builtin-clear-padding-3.c	2020-11-19 16:15:50.579639512 +0100
> @@ -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;
> +}
> --- gcc/testsuite/c-c++-common/torture/builtin-clear-padding-4.c.jj	2020-11-19 16:17:07.550785893 +0100
> +++ gcc/testsuite/c-c++-common/torture/builtin-clear-padding-4.c	2020-11-19 16:16:06.648461318 +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-19 16:17:07.551785882 +0100
> +++ gcc/testsuite/c-c++-common/torture/builtin-clear-padding-5.c	2020-11-19 16:16:06.648461318 +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-1.c.jj	2020-11-19 16:15:50.579639512 +0100
> +++ gcc/testsuite/gcc.dg/builtin-clear-padding-1.c	2020-11-19 16:15:50.579639512 +0100
> @@ -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" } */
> +}
> --- gcc/testsuite/g++.dg/torture/builtin-clear-padding-1.C.jj	2020-11-19 16:17:07.551785882 +0100
> +++ gcc/testsuite/g++.dg/torture/builtin-clear-padding-1.C	2020-11-19 16:16:14.380375575 +0100
> @@ -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;
> +}
> --- gcc/testsuite/g++.dg/torture/builtin-clear-padding-2.C.jj	2020-11-19 16:56:35.602466744 +0100
> +++ gcc/testsuite/g++.dg/torture/builtin-clear-padding-2.C	2020-11-19 16:59:42.434391419 +0100
> @@ -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 ();
> +}
> 
> 	Jakub
> 
> 
> 

-- 
Richard Biener <rguenther@suse.de>
SUSE Software Solutions Germany GmbH, Maxfeldstrasse 5, 90409 Nuernberg,
Germany; GF: Felix Imend

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH] c++, v3: Add __builtin_clear_padding builtin - C++20 P0528R3 compiler side [PR88101]
  2020-11-20  9:19 ` Richard Biener
@ 2020-11-20 10:55   ` Jakub Jelinek
  2020-11-20 11:54     ` Richard Biener
  0 siblings, 1 reply; 5+ messages in thread
From: Jakub Jelinek @ 2020-11-20 10:55 UTC (permalink / raw)
  To: Richard Biener
  Cc: Jason Merrill, Jonathan Wakely, Thomas Rodgers, gcc-patches

On Fri, Nov 20, 2020 at 09:19:31AM +0000, Richard Biener wrote:
> > --- gcc/builtins.c.jj	2020-11-19 12:34:10.749514278 +0100
> > +++ gcc/builtins.c	2020-11-19 16:23:55.261250903 +0100
> > @@ -11189,6 +11189,13 @@ fold_builtin_1 (location_t loc, tree exp
> >  	return build_empty_stmt (loc);
> >        break;
> >  
> > +    case BUILT_IN_CLEAR_PADDING:
> > +      /* Remember the original type of the argument in an internal
> > +	 dummy second argument, as in GIMPLE pointer conversions are
> > +	 useless.  */
> > +      return build_call_expr_loc (loc, fndecl, 2, arg0,
> > +				  build_zero_cst (TREE_TYPE (arg0)));
> > +
> 
> I'd rather make this change during gimplify_call_expr, I'm not
> even sure at which point we'd hit the above (and if at all).

As discussed on IRC, it was called from the FE folding or could be even from
gimplify_call_expr which also folds builtins and it wasn't recursing
because after it got second argument fold_builtin_1 wouldn't be called on it
anymore (but fold_builtin_2).
Anyway, I've implemented it now in gimplify_call_expr instead.

> You're using alias-set zero for all stores but since we're
> effectively inspecting the layout of a type we could specify
> that __builtin_clear_padding expects an object of said type
> at the very location active as dynamic type.  So IMHO
> less conservative but still sound would be to use
> build_pointer_type (aggregate-type) in place of ptr_type_node.
> 
> I'm not sure it will make a difference but it might be useful
> to parametrize 'ptr_type_node' in the IL generation?

Ok, added alias_type to the data structure which is passed around,
and initialized it to pointer to the toplevel type (except for VLAs,
in that case just to pointer to the VLA element type).

Here is the updated patch:

2020-11-20  Jakub Jelinek  <jakub@redhat.com>

	PR libstdc++/88101
gcc/
	* builtins.def (BUILT_IN_CLEAR_PADDING): New built-in function.
	* gimplify.c (gimplify_call_expr): Rewrite single argument
	BUILT_IN_CLEAR_PADDING into two-argument variant.
	* gimple-fold.c (clear_padding_unit, clear_padding_buf_size): New
	const variables.
	(struct clear_padding_struct): New type.
	(clear_padding_flush, clear_padding_add_padding,
	clear_padding_emit_loop, clear_padding_type,
	clear_padding_union, clear_padding_real_needs_padding_p,
	clear_padding_type_may_have_padding_p,
	gimple_fold_builtin_clear_padding): New functions.
	(gimple_fold_builtin): Handle BUILT_IN_CLEAR_PADDING.
	* doc/extend.texi (__builtin_clear_padding): Document.
gcc/c-family/
	* c-common.c (check_builtin_function_arguments): Handle
	BUILT_IN_CLEAR_PADDING.
gcc/testsuite/
	* c-c++-common/builtin-clear-padding-1.c: New test.
	* c-c++-common/torture/builtin-clear-padding-1.c: New test.
	* c-c++-common/torture/builtin-clear-padding-2.c: New test.
	* c-c++-common/torture/builtin-clear-padding-3.c: New test.
	* c-c++-common/torture/builtin-clear-padding-4.c: New test.
	* c-c++-common/torture/builtin-clear-padding-5.c: New test.
	* g++.dg/torture/builtin-clear-padding-1.C: New test.
	* g++.dg/torture/builtin-clear-padding-2.C: New test.
	* gcc.dg/builtin-clear-padding-1.c: New test.

--- gcc/builtins.def.jj	2020-11-19 20:00:47.116518082 +0100
+++ gcc/builtins.def	2020-11-20 10:51:44.715684363 +0100
@@ -839,6 +839,7 @@ DEF_EXT_LIB_BUILTIN    (BUILT_IN_CLEAR_C
 /* [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)
--- gcc/gimplify.c.jj	2020-11-20 08:43:52.262473979 +0100
+++ gcc/gimplify.c	2020-11-20 10:58:37.035125705 +0100
@@ -3384,6 +3384,20 @@ gimplify_call_expr (tree *expr_p, gimple
 	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:
         ;
       }
--- gcc/gimple-fold.c.jj	2020-11-19 20:00:47.191517285 +0100
+++ gcc/gimple-fold.c	2020-11-20 11:07:15.246395988 +0100
@@ -3948,6 +3948,698 @@ gimple_fold_builtin_realloc (gimple_stmt
   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_iterato
     case BUILT_IN_REALLOC:
       return gimple_fold_builtin_realloc (gsi);
 
+    case BUILT_IN_CLEAR_PADDING:
+      return gimple_fold_builtin_clear_padding (gsi);
+
     default:;
     }
 
--- gcc/doc/extend.texi.jj	2020-11-19 20:00:57.421408611 +0100
+++ gcc/doc/extend.texi	2020-11-20 10:51:44.719684319 +0100
@@ -13564,6 +13564,19 @@ initializers of variables usable in cons
 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
--- gcc/c-family/c-common.c.jj	2020-11-19 20:00:47.148517742 +0100
+++ gcc/c-family/c-common.c	2020-11-20 10:51:44.720684308 +0100
@@ -6178,6 +6178,39 @@ check_builtin_function_arguments (locati
 	}
       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;
     }
--- gcc/testsuite/c-c++-common/builtin-clear-padding-1.c.jj	2020-11-20 10:51:44.720684308 +0100
+++ gcc/testsuite/c-c++-common/builtin-clear-padding-1.c	2020-11-20 10:51:44.720684308 +0100
@@ -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" } */
+}
--- gcc/testsuite/c-c++-common/torture/builtin-clear-padding-1.c.jj	2020-11-20 10:51:44.720684308 +0100
+++ gcc/testsuite/c-c++-common/torture/builtin-clear-padding-1.c	2020-11-20 10:51:44.720684308 +0100
@@ -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;
+}
--- gcc/testsuite/c-c++-common/torture/builtin-clear-padding-2.c.jj	2020-11-20 10:51:44.720684308 +0100
+++ gcc/testsuite/c-c++-common/torture/builtin-clear-padding-2.c	2020-11-20 10:51:44.720684308 +0100
@@ -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;
+}
--- gcc/testsuite/c-c++-common/torture/builtin-clear-padding-3.c.jj	2020-11-20 10:51:44.720684308 +0100
+++ gcc/testsuite/c-c++-common/torture/builtin-clear-padding-3.c	2020-11-20 10:51:44.720684308 +0100
@@ -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;
+}
--- gcc/testsuite/c-c++-common/torture/builtin-clear-padding-4.c.jj	2020-11-20 10:51:44.720684308 +0100
+++ gcc/testsuite/c-c++-common/torture/builtin-clear-padding-4.c	2020-11-20 10:51:44.720684308 +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-20 10:51:44.720684308 +0100
+++ gcc/testsuite/c-c++-common/torture/builtin-clear-padding-5.c	2020-11-20 10:51:44.720684308 +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-1.c.jj	2020-11-20 10:51:44.720684308 +0100
+++ gcc/testsuite/gcc.dg/builtin-clear-padding-1.c	2020-11-20 10:51:44.720684308 +0100
@@ -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" } */
+}
--- gcc/testsuite/g++.dg/torture/builtin-clear-padding-1.C.jj	2020-11-20 10:51:44.721684296 +0100
+++ gcc/testsuite/g++.dg/torture/builtin-clear-padding-1.C	2020-11-20 10:51:44.721684296 +0100
@@ -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;
+}
--- gcc/testsuite/g++.dg/torture/builtin-clear-padding-2.C.jj	2020-11-20 10:51:44.721684296 +0100
+++ gcc/testsuite/g++.dg/torture/builtin-clear-padding-2.C	2020-11-20 10:51:44.721684296 +0100
@@ -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 ();
+}


	Jakub


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] c++, v3: Add __builtin_clear_padding builtin - C++20 P0528R3 compiler side [PR88101]
  2020-11-20 10:55   ` [PATCH] c++, v3: " Jakub Jelinek
@ 2020-11-20 11:54     ` Richard Biener
  0 siblings, 0 replies; 5+ messages in thread
From: Richard Biener @ 2020-11-20 11:54 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Jason Merrill, Jonathan Wakely, Thomas Rodgers, gcc-patches

On Fri, 20 Nov 2020, Jakub Jelinek wrote:

> On Fri, Nov 20, 2020 at 09:19:31AM +0000, Richard Biener wrote:
> > > --- gcc/builtins.c.jj	2020-11-19 12:34:10.749514278 +0100
> > > +++ gcc/builtins.c	2020-11-19 16:23:55.261250903 +0100
> > > @@ -11189,6 +11189,13 @@ fold_builtin_1 (location_t loc, tree exp
> > >  	return build_empty_stmt (loc);
> > >        break;
> > >  
> > > +    case BUILT_IN_CLEAR_PADDING:
> > > +      /* Remember the original type of the argument in an internal
> > > +	 dummy second argument, as in GIMPLE pointer conversions are
> > > +	 useless.  */
> > > +      return build_call_expr_loc (loc, fndecl, 2, arg0,
> > > +				  build_zero_cst (TREE_TYPE (arg0)));
> > > +
> > 
> > I'd rather make this change during gimplify_call_expr, I'm not
> > even sure at which point we'd hit the above (and if at all).
> 
> As discussed on IRC, it was called from the FE folding or could be even from
> gimplify_call_expr which also folds builtins and it wasn't recursing
> because after it got second argument fold_builtin_1 wouldn't be called on it
> anymore (but fold_builtin_2).
> Anyway, I've implemented it now in gimplify_call_expr instead.
> 
> > You're using alias-set zero for all stores but since we're
> > effectively inspecting the layout of a type we could specify
> > that __builtin_clear_padding expects an object of said type
> > at the very location active as dynamic type.  So IMHO
> > less conservative but still sound would be to use
> > build_pointer_type (aggregate-type) in place of ptr_type_node.
> > 
> > I'm not sure it will make a difference but it might be useful
> > to parametrize 'ptr_type_node' in the IL generation?
> 
> Ok, added alias_type to the data structure which is passed around,
> and initialized it to pointer to the toplevel type (except for VLAs,
> in that case just to pointer to the VLA element type).
> 
> Here is the updated patch.

LGTM.

Thanks,
Richard.

> 
> 2020-11-20  Jakub Jelinek  <jakub@redhat.com>
> 
> 	PR libstdc++/88101
> gcc/
> 	* builtins.def (BUILT_IN_CLEAR_PADDING): New built-in function.
> 	* gimplify.c (gimplify_call_expr): Rewrite single argument
> 	BUILT_IN_CLEAR_PADDING into two-argument variant.
> 	* gimple-fold.c (clear_padding_unit, clear_padding_buf_size): New
> 	const variables.
> 	(struct clear_padding_struct): New type.
> 	(clear_padding_flush, clear_padding_add_padding,
> 	clear_padding_emit_loop, clear_padding_type,
> 	clear_padding_union, clear_padding_real_needs_padding_p,
> 	clear_padding_type_may_have_padding_p,
> 	gimple_fold_builtin_clear_padding): New functions.
> 	(gimple_fold_builtin): Handle BUILT_IN_CLEAR_PADDING.
> 	* doc/extend.texi (__builtin_clear_padding): Document.
> gcc/c-family/
> 	* c-common.c (check_builtin_function_arguments): Handle
> 	BUILT_IN_CLEAR_PADDING.
> gcc/testsuite/
> 	* c-c++-common/builtin-clear-padding-1.c: New test.
> 	* c-c++-common/torture/builtin-clear-padding-1.c: New test.
> 	* c-c++-common/torture/builtin-clear-padding-2.c: New test.
> 	* c-c++-common/torture/builtin-clear-padding-3.c: New test.
> 	* c-c++-common/torture/builtin-clear-padding-4.c: New test.
> 	* c-c++-common/torture/builtin-clear-padding-5.c: New test.
> 	* g++.dg/torture/builtin-clear-padding-1.C: New test.
> 	* g++.dg/torture/builtin-clear-padding-2.C: New test.
> 	* gcc.dg/builtin-clear-padding-1.c: New test.
> 
> --- gcc/builtins.def.jj	2020-11-19 20:00:47.116518082 +0100
> +++ gcc/builtins.def	2020-11-20 10:51:44.715684363 +0100
> @@ -839,6 +839,7 @@ DEF_EXT_LIB_BUILTIN    (BUILT_IN_CLEAR_C
>  /* [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)
> --- gcc/gimplify.c.jj	2020-11-20 08:43:52.262473979 +0100
> +++ gcc/gimplify.c	2020-11-20 10:58:37.035125705 +0100
> @@ -3384,6 +3384,20 @@ gimplify_call_expr (tree *expr_p, gimple
>  	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:
>          ;
>        }
> --- gcc/gimple-fold.c.jj	2020-11-19 20:00:47.191517285 +0100
> +++ gcc/gimple-fold.c	2020-11-20 11:07:15.246395988 +0100
> @@ -3948,6 +3948,698 @@ gimple_fold_builtin_realloc (gimple_stmt
>    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_iterato
>      case BUILT_IN_REALLOC:
>        return gimple_fold_builtin_realloc (gsi);
>  
> +    case BUILT_IN_CLEAR_PADDING:
> +      return gimple_fold_builtin_clear_padding (gsi);
> +
>      default:;
>      }
>  
> --- gcc/doc/extend.texi.jj	2020-11-19 20:00:57.421408611 +0100
> +++ gcc/doc/extend.texi	2020-11-20 10:51:44.719684319 +0100
> @@ -13564,6 +13564,19 @@ initializers of variables usable in cons
>  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
> --- gcc/c-family/c-common.c.jj	2020-11-19 20:00:47.148517742 +0100
> +++ gcc/c-family/c-common.c	2020-11-20 10:51:44.720684308 +0100
> @@ -6178,6 +6178,39 @@ check_builtin_function_arguments (locati
>  	}
>        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;
>      }
> --- gcc/testsuite/c-c++-common/builtin-clear-padding-1.c.jj	2020-11-20 10:51:44.720684308 +0100
> +++ gcc/testsuite/c-c++-common/builtin-clear-padding-1.c	2020-11-20 10:51:44.720684308 +0100
> @@ -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" } */
> +}
> --- gcc/testsuite/c-c++-common/torture/builtin-clear-padding-1.c.jj	2020-11-20 10:51:44.720684308 +0100
> +++ gcc/testsuite/c-c++-common/torture/builtin-clear-padding-1.c	2020-11-20 10:51:44.720684308 +0100
> @@ -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;
> +}
> --- gcc/testsuite/c-c++-common/torture/builtin-clear-padding-2.c.jj	2020-11-20 10:51:44.720684308 +0100
> +++ gcc/testsuite/c-c++-common/torture/builtin-clear-padding-2.c	2020-11-20 10:51:44.720684308 +0100
> @@ -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;
> +}
> --- gcc/testsuite/c-c++-common/torture/builtin-clear-padding-3.c.jj	2020-11-20 10:51:44.720684308 +0100
> +++ gcc/testsuite/c-c++-common/torture/builtin-clear-padding-3.c	2020-11-20 10:51:44.720684308 +0100
> @@ -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;
> +}
> --- gcc/testsuite/c-c++-common/torture/builtin-clear-padding-4.c.jj	2020-11-20 10:51:44.720684308 +0100
> +++ gcc/testsuite/c-c++-common/torture/builtin-clear-padding-4.c	2020-11-20 10:51:44.720684308 +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-20 10:51:44.720684308 +0100
> +++ gcc/testsuite/c-c++-common/torture/builtin-clear-padding-5.c	2020-11-20 10:51:44.720684308 +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-1.c.jj	2020-11-20 10:51:44.720684308 +0100
> +++ gcc/testsuite/gcc.dg/builtin-clear-padding-1.c	2020-11-20 10:51:44.720684308 +0100
> @@ -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" } */
> +}
> --- gcc/testsuite/g++.dg/torture/builtin-clear-padding-1.C.jj	2020-11-20 10:51:44.721684296 +0100
> +++ gcc/testsuite/g++.dg/torture/builtin-clear-padding-1.C	2020-11-20 10:51:44.721684296 +0100
> @@ -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;
> +}
> --- gcc/testsuite/g++.dg/torture/builtin-clear-padding-2.C.jj	2020-11-20 10:51:44.721684296 +0100
> +++ gcc/testsuite/g++.dg/torture/builtin-clear-padding-2.C	2020-11-20 10:51:44.721684296 +0100
> @@ -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 ();
> +}
> 
> 
> 	Jakub
> 
> 
> 

-- 
Richard Biener <rguenther@suse.de>
SUSE Software Solutions Germany GmbH, Maxfeldstrasse 5, 90409 Nuernberg,
Germany; GF: Felix Imend

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2020-11-20 11:54 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-11-19 16:30 [PATCH] c++, v2: Add __builtin_clear_padding builtin - C++20 P0528R3 compiler side [PR88101] Jakub Jelinek
2020-11-19 22:31 ` Jakub Jelinek
2020-11-20  9:19 ` Richard Biener
2020-11-20 10:55   ` [PATCH] c++, v3: " Jakub Jelinek
2020-11-20 11:54     ` Richard Biener

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).