public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] c++: Add __builtin_clear_padding builtin - C++20 P0528R3 compiler side [PR88101]
@ 2020-11-16 11:57 Jakub Jelinek
  2020-11-16 21:13 ` [PATCH] c++: __builtin_clear_padding builtin follow-up [PR88101] Jakub Jelinek
       [not found] ` <1AD8DAAC-A7A1-4A19-8538-4092C1F8C327@comcast.net>
  0 siblings, 2 replies; 4+ messages in thread
From: Jakub Jelinek @ 2020-11-16 11:57 UTC (permalink / raw)
  To: Jason Merrill, Richard Biener, Jonathan Wakely; +Cc: gcc-patches

Hi!

Working virtually out of Baker Island - AoE timezone.

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), etc.
What isn't handled yet (plan to fix soon) are VLAs, which should be
implementable by adding runtime loop(s) performing the element clearing.
Also, I haven't managed yet to implement a code-size optimization, if
array types are large enough and have more than one element, again perform
a runtime loop for the clearing.

Tested on x86_64-linux, i686-linux and powerpc64-linux, ok for trunk?

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

	PR libstdc++/88101
gcc/
	* builtins.def (BUILT_IN_CLEAR_PADDING): New built-in function.
	* 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_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.
	* gcc.dg/builtin-clear-padding-1.c: New test.

--- gcc/builtins.def.jj	2020-11-09 02:25:52.217020675 -1200
+++ gcc/builtins.def	2020-11-13 22:26:43.549287472 -1200
@@ -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/c-family/c-common.c.jj	2020-11-13 00:50:23.267552644 -1200
+++ gcc/c-family/c-common.c	2020-11-15 05:29:12.441618995 -1200
@@ -6171,6 +6171,25 @@ 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;
+	    }
+	  return true;
+	}
+      return false;
+
     default:
       return true;
     }
--- gcc/gimple-fold.c.jj	2020-11-05 22:14:19.926201410 -1200
+++ gcc/gimple-fold.c	2020-11-15 23:20:29.551779949 -1200
@@ -3948,6 +3948,603 @@ 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);
+
+/* 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 = int_size_in_bytes (TREE_TYPE (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));
+	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;
+    }
+}
+
+/* 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 = int_size_in_bytes (type);
+  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 = int_size_in_bytes (TREE_TYPE (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));
+		cur_pos += fldsz;
+	      }
+	  }
+      gcc_assert (sz >= cur_pos);
+      clear_padding_add_padding (buf, sz - cur_pos);
+      break;
+    case ARRAY_TYPE:
+      HOST_WIDE_INT nelts;
+      nelts = int_size_in_bytes (TREE_TYPE (type));
+      nelts = sz / nelts;
+      if (nelts > 1 && sz > 8 * UNITS_PER_WORD && buf->union_ptr == NULL)
+	{
+	  /* FIXME: If ARRAY_TYPE has nelts > 1 and sz over certain
+	     small threshold, instead call clear_padding_flush (&buf, true);
+	     and emit a runtime loop filling each array element separately.  */
+	}
+      for (HOST_WIDE_INT i = 0; i < nelts; i++)
+	clear_padding_type (buf, TREE_TYPE (type));
+      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:
+      clear_padding_type (buf, TREE_TYPE (type));
+      clear_padding_type (buf, TREE_TYPE (type));
+      break;
+    case VECTOR_TYPE:
+      nelts = TYPE_VECTOR_SUBPARTS (type).to_constant ();
+      for (HOST_WIDE_INT i = 0; i < nelts; i++)
+	clear_padding_type (buf, TREE_TYPE (type));
+      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);
+  tree ptr = gimple_call_arg (stmt, 0);
+  tree type = TREE_TYPE (TREE_TYPE (ptr));
+  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)
+    {
+      /* FIXME: Handle VLAs.  These can be handled by adding runtime
+	 loop (or multiple for multiple VLAs) calling clear_padding_type
+	 only on each non-VLA element.  */
+      sorry_at (loc, "%s does not support VLAs yet",
+		"__builtin_clear_padding");
+    }
+  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);
+      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 +4702,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-13 21:40:11.126410770 -1200
+++ gcc/doc/extend.texi	2020-11-15 23:34:03.193609155 -1200
@@ -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/testsuite/c-c++-common/builtin-clear-padding-1.c.jj	2020-11-15 23:37:23.709347369 -1200
+++ gcc/testsuite/c-c++-common/builtin-clear-padding-1.c	2020-11-15 23:39:37.778835083 -1200
@@ -0,0 +1,17 @@
+/* PR libstdc++/88101 */
+/* { dg-do compile } */
+
+struct S;
+
+void
+foo (struct S *p, void *q, char *r)
+{
+  __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);
+}
--- gcc/testsuite/c-c++-common/torture/builtin-clear-padding-1.c.jj	2020-11-15 06:54:02.415860651 -1200
+++ gcc/testsuite/c-c++-common/torture/builtin-clear-padding-1.c	2020-11-15 21:10:53.126924166 -1200
@@ -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-15 19:40:01.096659838 -1200
+++ gcc/testsuite/c-c++-common/torture/builtin-clear-padding-2.c	2020-11-15 19:33:51.487772285 -1200
@@ -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-15 23:00:06.036540255 -1200
+++ gcc/testsuite/c-c++-common/torture/builtin-clear-padding-3.c	2020-11-15 23:02:37.487839371 -1200
@@ -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/gcc.dg/builtin-clear-padding-1.c.jj	2020-11-15 23:40:03.976539576 -1200
+++ gcc/testsuite/gcc.dg/builtin-clear-padding-1.c	2020-11-15 23:42:47.378696420 -1200
@@ -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" } */
+}

	Jakub


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

* [PATCH] c++: __builtin_clear_padding builtin follow-up [PR88101]
  2020-11-16 11:57 [PATCH] c++: Add __builtin_clear_padding builtin - C++20 P0528R3 compiler side [PR88101] Jakub Jelinek
@ 2020-11-16 21:13 ` Jakub Jelinek
  2020-11-18  8:48   ` [PATCH] c++: __builtin_clear_padding builtin C++ tail padding fix [PR88101] Jakub Jelinek
       [not found] ` <1AD8DAAC-A7A1-4A19-8538-4092C1F8C327@comcast.net>
  1 sibling, 1 reply; 4+ messages in thread
From: Jakub Jelinek @ 2020-11-16 21:13 UTC (permalink / raw)
  To: Jason Merrill, Richard Biener, Jonathan Wakely, gcc-patches

On Sun, Nov 15, 2020 at 11:57:55PM -1200, Jakub Jelinek via Gcc-patches wrote:
> Tested on x86_64-linux, i686-linux and powerpc64-linux, ok for trunk?

Here is an incremental patch that resolves the remaining FIXMEs, in
particular implements VLAs (except for variable length structures)
and for larger fixed sized arrays or members with larger array types
uses runtime loops for the clearing (unless inside of a union).
Furthermore, I've added diagnostics about last argument being const whatever *
(similarly to e.g. __builtin_*_overflow) and also about _Atomic whatever *.

Bootstrapped/regtested on x86_64-linux, ok for trunk?

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

	PR libstdc++/88101
gcc/
	* gimple-fold.c (clear_padding_emit_loop): New function.
	(clear_padding_type): Optimize larger ARRAY_TYPEs not inside of a
	union by clearing the padding inside of a runtime loop.
	(gimple_fold_builtin_clear_padding): Handle VLAs.
gcc/c-family/
	* c-common.c (check_builtin_function_arguments)
	<case BUILT_IN_CLEAR_PADDING>: Diagnose pointer to const or pointer
	to _Atomic arguments.
gcc/testsuite/
	* c-c++-common/builtin-clear-padding-1.c (struct T): New type.
	(foo): Add test for const struct T * argument.
	* c-c++-common/torture/builtin-clear-padding-4.c: New test.
	* c-c++-common/torture/builtin-clear-padding-5.c: New test.
	* gcc.dg/builtin-clear-padding-2.c: New test.

--- gcc/gimple-fold.c.jj	2020-11-16 12:20:29.551779949 +0100
+++ gcc/gimple-fold.c	2020-11-16 18:47:42.997770758 +0100
@@ -4323,6 +4323,39 @@ clear_padding_type_may_have_padding_p (t
     }
 }
 
+/* Emit a runtime loop:
+   for (; buf.base != end; buf.base += sz)
+     __builtin_clear_padding (buf.base);  */
+
+static void
+clear_padding_emit_loop (clear_padding_struct *buf, tree type, tree end)
+{
+  tree l1 = create_artificial_label (buf->loc);
+  tree l2 = create_artificial_label (buf->loc);
+  tree l3 = create_artificial_label (buf->loc);
+  gimple *g = gimple_build_goto (l2);
+  gimple_set_location (g, buf->loc);
+  gsi_insert_before (buf->gsi, g, GSI_SAME_STMT);
+  g = gimple_build_label (l1);
+  gimple_set_location (g, buf->loc);
+  gsi_insert_before (buf->gsi, g, GSI_SAME_STMT);
+  clear_padding_type (buf, type);
+  clear_padding_flush (buf, true);
+  g = gimple_build_assign (buf->base, POINTER_PLUS_EXPR, buf->base,
+			   size_int (buf->sz));
+  gimple_set_location (g, buf->loc);
+  gsi_insert_before (buf->gsi, g, GSI_SAME_STMT);
+  g = gimple_build_label (l2);
+  gimple_set_location (g, buf->loc);
+  gsi_insert_before (buf->gsi, g, GSI_SAME_STMT);
+  g = gimple_build_cond (NE_EXPR, buf->base, end, l1, l3);
+  gimple_set_location (g, buf->loc);
+  gsi_insert_before (buf->gsi, g, GSI_SAME_STMT);
+  g = gimple_build_label (l3);
+  gimple_set_location (g, buf->loc);
+  gsi_insert_before (buf->gsi, g, GSI_SAME_STMT);
+}
+
 /* Clear padding bits for TYPE.  Called recursively from
    gimple_fold_builtin_clear_padding.  */
 
@@ -4423,11 +4456,41 @@ clear_padding_type (clear_padding_struct
       HOST_WIDE_INT nelts;
       nelts = int_size_in_bytes (TREE_TYPE (type));
       nelts = sz / nelts;
-      if (nelts > 1 && sz > 8 * UNITS_PER_WORD && buf->union_ptr == NULL)
+      if (nelts > 1
+	  && sz > 8 * UNITS_PER_WORD
+	  && buf->union_ptr == NULL
+	  && clear_padding_type_may_have_padding_p (TREE_TYPE (type)))
 	{
-	  /* FIXME: If ARRAY_TYPE has nelts > 1 and sz over certain
-	     small threshold, instead call clear_padding_flush (&buf, true);
-	     and emit a runtime loop filling each array element separately.  */
+	  /* For sufficiently large array of more than one elements,
+	     emit a runtime loop to keep code size manageable.  */
+	  tree base = buf->base;
+	  unsigned int prev_align = buf->align;
+	  HOST_WIDE_INT off = buf->off + buf->size;
+	  HOST_WIDE_INT prev_sz = buf->sz;
+	  clear_padding_flush (buf, true);
+	  tree elttype = TREE_TYPE (type);
+	  buf->base = create_tmp_var (build_pointer_type (elttype));
+	  tree end = make_ssa_name (TREE_TYPE (buf->base));
+	  gimple *g = gimple_build_assign (buf->base, POINTER_PLUS_EXPR,
+					   base, size_int (off));
+	  gimple_set_location (g, buf->loc);
+	  gsi_insert_before (buf->gsi, g, GSI_SAME_STMT);
+	  g = gimple_build_assign (end, POINTER_PLUS_EXPR, buf->base,
+				   size_int (sz));
+	  gimple_set_location (g, buf->loc);
+	  gsi_insert_before (buf->gsi, g, GSI_SAME_STMT);
+	  buf->sz = sz / nelts;
+	  buf->align = TYPE_ALIGN (elttype);
+	  buf->off = 0;
+	  buf->size = 0;
+	  clear_padding_emit_loop (buf, elttype, end);
+	  buf->base = base;
+	  buf->sz = prev_sz;
+	  buf->align = prev_align;
+	  buf->size = off % UNITS_PER_WORD;
+	  buf->off = off - buf->size;
+	  memset (buf->buf, 0, buf->size);
+	  break;
 	}
       for (HOST_WIDE_INT i = 0; i < nelts; i++)
 	clear_padding_type (buf, TREE_TYPE (type));
@@ -4514,11 +4577,28 @@ gimple_fold_builtin_clear_padding (gimpl
     ;
   else if (TREE_CODE (type) == ARRAY_TYPE && buf.sz < 0)
     {
-      /* FIXME: Handle VLAs.  These can be handled by adding runtime
-	 loop (or multiple for multiple VLAs) calling clear_padding_type
-	 only on each non-VLA element.  */
-      sorry_at (loc, "%s does not support VLAs yet",
-		"__builtin_clear_padding");
+      tree sz = TYPE_SIZE_UNIT (type);
+      tree elttype = type;
+      /* Only supports C/C++ VLAs and flattens all the VLA levels.  */
+      while (TREE_CODE (elttype) == ARRAY_TYPE
+	     && int_size_in_bytes (elttype) < 0)
+	elttype = TREE_TYPE (elttype);
+      HOST_WIDE_INT eltsz = int_size_in_bytes (elttype);
+      gcc_assert (eltsz >= 0);
+      if (eltsz)
+	{
+	  buf.base = create_tmp_var (build_pointer_type (elttype));
+	  tree end = make_ssa_name (TREE_TYPE (buf.base));
+	  gimple *g = gimple_build_assign (buf.base, ptr);
+	  gimple_set_location (g, loc);
+	  gsi_insert_before (gsi, g, GSI_SAME_STMT);
+	  g = gimple_build_assign (end, POINTER_PLUS_EXPR, buf.base, sz);
+	  gimple_set_location (g, loc);
+	  gsi_insert_before (gsi, g, GSI_SAME_STMT);
+	  buf.sz = eltsz;
+	  buf.align = TYPE_ALIGN (elttype);
+	  clear_padding_emit_loop (&buf, elttype, end);
+	}
     }
   else
     {
--- gcc/c-family/c-common.c.jj	2020-11-15 18:29:12.441618995 +0100
+++ gcc/c-family/c-common.c	2020-11-16 19:33:56.458507220 +0100
@@ -6186,6 +6186,20 @@ check_builtin_function_arguments (locati
 			"%qE points to incomplete type", 1, fndecl);
 	      return false;
 	    }
+	  else if (TYPE_READONLY (TREE_TYPE (TREE_TYPE (args[0]))))
+	    {
+	      error_at (ARG_LOCATION (0), "argument %u in call to function %qE "
+			"has pointer to %qs type (%qT)", 1, fndecl, "const",
+			TREE_TYPE (args[0]));
+	      return false;
+	    }
+	  else if (TYPE_ATOMIC (TREE_TYPE (TREE_TYPE (args[0]))))
+	    {
+	      error_at (ARG_LOCATION (0), "argument %u in call to function %qE "
+			"has pointer to %qs type (%qT)", 1, fndecl,
+			"_Atomic", TREE_TYPE (args[0]));
+	      return false;
+	    }
 	  return true;
 	}
       return false;
--- gcc/testsuite/c-c++-common/builtin-clear-padding-1.c.jj	2020-11-16 12:39:37.000000000 +0100
+++ gcc/testsuite/c-c++-common/builtin-clear-padding-1.c	2020-11-16 19:35:08.628693878 +0100
@@ -2,9 +2,10 @@
 /* { dg-do compile } */
 
 struct S;
+struct T { char a; long long b; };
 
 void
-foo (struct S *p, void *q, char *r)
+foo (struct S *p, void *q, char *r, const struct T *s)
 {
   __builtin_clear_padding ();		/* { dg-error "too few arguments to function '__builtin_clear_padding'" } */
   __builtin_clear_padding (1);		/* { dg-error "argument 1 in call to function '__builtin_clear_padding' does not have pointer type" } */
@@ -14,4 +15,5 @@ foo (struct S *p, void *q, char *r)
   __builtin_clear_padding (p);		/* { dg-error "argument 1 in call to function '__builtin_clear_padding' points to incomplete type" } */
   __builtin_clear_padding (q);		/* { dg-error "argument 1 in call to function '__builtin_clear_padding' points to incomplete type" } */
   __builtin_clear_padding (r);
+  __builtin_clear_padding (s);		/* { dg-error "argument 1 in call to function '__builtin_clear_padding' has pointer to 'const' type" } */
 }
--- gcc/testsuite/c-c++-common/torture/builtin-clear-padding-4.c.jj	2020-11-16 17:40:47.111049570 +0100
+++ gcc/testsuite/c-c++-common/torture/builtin-clear-padding-4.c	2020-11-16 17:40:53.074982330 +0100
@@ -0,0 +1,59 @@
+/* PR libstdc++/88101 */
+
+struct S { char a; short b; char c; };
+
+__attribute__((noipa)) void
+foo (int m, int n, int o)
+{
+  long double a1[m];
+  long double a2[m];
+  struct S b1[m][n];
+  struct S b2[m][n];
+  struct S c1[m][n][o];
+  struct S c2[m][n][o];
+  int i, j, k;
+  __builtin_memset (&a1, 0, sizeof (a1));
+  __builtin_memset (&a2, ~0, sizeof (a2));
+  __builtin_memset (&b1, 0, sizeof (b1));
+  __builtin_memset (&b2, ~0, sizeof (b2));
+  __builtin_memset (&c1, 0, sizeof (c1));
+  __builtin_memset (&c2, ~0, sizeof (c2));
+  for (i = 0; i < m; i++)
+    {
+      a1[i] = 13.132L;
+      a2[i] = 13.132L;
+      for (j = 0; j < n; j++)
+	{
+	  b1[i][j].a = -1;
+	  b1[i][j].b = -1;
+	  b1[i][j].c = -1;
+	  b2[i][j].a = -1;
+	  b2[i][j].b = -1;
+	  b2[i][j].c = -1;
+	  for (k = 0; k < o; k++)
+	    {
+	      c1[i][j][k].a = -1;
+	      c1[i][j][k].b = -1;
+	      c1[i][j][k].c = -1;
+	      c2[i][j][k].a = -1;
+	      c2[i][j][k].b = -1;
+	      c2[i][j][k].c = -1;
+	    }
+	}
+    }
+  __builtin_clear_padding (&a2);
+  __builtin_clear_padding (&b2);
+  __builtin_clear_padding (&c2);
+  if (__builtin_memcmp (&a1, &a2, sizeof (a1))
+      || __builtin_memcmp (&b1, &b2, sizeof (b1))
+      || __builtin_memcmp (&c1, &c2, sizeof (c1)))
+    __builtin_abort ();
+}
+
+int
+main ()
+{
+  foo (5, 3, 4);
+  foo (17, 2, 1);
+  return 0;
+}
--- gcc/testsuite/c-c++-common/torture/builtin-clear-padding-5.c.jj	2020-11-16 18:51:02.463521254 +0100
+++ gcc/testsuite/c-c++-common/torture/builtin-clear-padding-5.c	2020-11-16 17:58:58.385746596 +0100
@@ -0,0 +1,49 @@
+/* PR libstdc++/88101 */
+
+struct S { char a; short b; char c; } s1[24], s2[24];
+struct T { char a; long long b; char c; struct S d[3]; long long e; char f; } t1, t2;
+struct U { char a; long long b; char c; struct S d[25]; long long e; char f; } u1, u2;
+
+__attribute__((noipa)) void
+foo (struct S *s, struct T *t, struct U *u)
+{
+  int i;
+  t->a = -1; t->b = -1; t->c = -1; t->e = -1; t->f = -1;
+  u->a = -1; u->b = -1; u->c = -1; u->e = -1; u->f = -1;
+  for (i = 0; i < 24; i++)
+    {
+      s[i].a = -1;
+      s[i].b = -1;
+      s[i].c = -1;
+    }
+  for (i = 0; i < 3; i++)
+    {
+      t->d[i].a = -1;
+      t->d[i].b = -1;
+      t->d[i].c = -1;
+    }
+  for (i = 0; i < 25; i++)
+    {
+      u->d[i].a = -1;
+      u->d[i].b = -1;
+      u->d[i].c = -1;
+    }
+}
+
+int
+main ()
+{
+  __builtin_memset (&s2, -1, sizeof (s2));
+  __builtin_memset (&t2, -1, sizeof (t2));
+  __builtin_memset (&u2, -1, sizeof (u2));
+  foo (&s1[0], &t1, &u1);
+  foo (&s2[0], &t2, &u2);
+  __builtin_clear_padding (&s2);
+  __builtin_clear_padding (&t2);
+  __builtin_clear_padding (&u2);
+  if (__builtin_memcmp (&s1, &s2, sizeof (s1))
+      || __builtin_memcmp (&t1, &t2, sizeof (t1))
+      || __builtin_memcmp (&u1, &u2, sizeof (u1)))
+    __builtin_abort ();
+  return 0;
+}
--- gcc/testsuite/gcc.dg/builtin-clear-padding-2.c.jj	2020-11-16 19:36:17.221920848 +0100
+++ gcc/testsuite/gcc.dg/builtin-clear-padding-2.c	2020-11-16 19:36:44.158617264 +0100
@@ -0,0 +1,11 @@
+/* PR libstdc++/88101 */
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+struct T { char a; long long b; };
+
+void
+foo (_Atomic struct T *p)
+{
+  __builtin_clear_padding (p);		/* { dg-error "argument 1 in call to function '__builtin_clear_padding' has pointer to '_Atomic' type" } */
+}


	Jakub


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

* [PATCH] c++: __builtin_clear_padding builtin C++ tail padding fix [PR88101]
  2020-11-16 21:13 ` [PATCH] c++: __builtin_clear_padding builtin follow-up [PR88101] Jakub Jelinek
@ 2020-11-18  8:48   ` Jakub Jelinek
  0 siblings, 0 replies; 4+ messages in thread
From: Jakub Jelinek @ 2020-11-18  8:48 UTC (permalink / raw)
  To: Jason Merrill, Richard Biener, Jonathan Wakely, gcc-patches

On Mon, Nov 16, 2020 at 10:13:52PM +0100, Jakub Jelinek via Gcc-patches wrote:
> On Sun, Nov 15, 2020 at 11:57:55PM -1200, Jakub Jelinek via Gcc-patches wrote:
> > Tested on x86_64-linux, i686-linux and powerpc64-linux, ok for trunk?
> 
> Here is an incremental patch that resolves the remaining FIXMEs, in
> particular implements VLAs (except for variable length structures)
> and for larger fixed sized arrays or members with larger array types
> uses runtime loops for the clearing (unless inside of a union).
> Furthermore, I've added diagnostics about last argument being const whatever *
> (similarly to e.g. __builtin_*_overflow) and also about _Atomic whatever *.

And another incrementaly patch, I've noticed ICEs on the following testcase.
For C++ we need to take DECL_SIZE_UNIT (field) as the size of the fields rather than
their TYPE_SIZE_UNIT (TREE_TYPE (field)), because in C++ tail padding is
often reused for other fields.

Bootstrapped/regtested (on top of the earlier 2 patches) on x86_64-linux and
i686-linux, ok for trunk?

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

	PR libstdc++/88101
	* gimple-fold.c (clear_padding_type): Add sz argument,
	don't set it to int_size_in_bytes.  Adjust recursive calls.
	In RECORD_TYPEs, use DECL_SIZE_UNIT for fldsz.
	(clear_padding_union): Use DECL_SIZE_UNIT for fldsz and
	pass it to clear_padding_type.
	(clear_padding_emit_loop): Adjust clear_padding_type caller.
	(gimple_fold_builtin_clear_padding): Likewise.

	* g++.dg/torture/builtin-clear-padding-1.C: New test.

--- gcc/gimple-fold.c.jj	2020-11-16 18:47:42.997770758 +0100
+++ gcc/gimple-fold.c	2020-11-17 10:37:50.014877514 +0100
@@ -4211,7 +4211,7 @@ clear_padding_add_padding (clear_padding
     }
 }
 
-static void clear_padding_type (clear_padding_struct *, tree);
+static void clear_padding_type (clear_padding_struct *, tree, HOST_WIDE_INT);
 
 /* Clear padding bits of union type TYPE.  */
 
@@ -4253,12 +4253,12 @@ clear_padding_union (clear_padding_struc
   for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
     if (TREE_CODE (field) == FIELD_DECL)
       {
-	HOST_WIDE_INT fldsz = int_size_in_bytes (TREE_TYPE (field));
+	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));
+	clear_padding_type (union_buf, TREE_TYPE (field), fldsz);
 	clear_padding_add_padding (union_buf, sz - fldsz);
 	clear_padding_flush (union_buf, true);
       }
@@ -4339,7 +4339,7 @@ clear_padding_emit_loop (clear_padding_s
   g = gimple_build_label (l1);
   gimple_set_location (g, buf->loc);
   gsi_insert_before (buf->gsi, g, GSI_SAME_STMT);
-  clear_padding_type (buf, type);
+  clear_padding_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));
@@ -4360,9 +4360,8 @@ clear_padding_emit_loop (clear_padding_s
    gimple_fold_builtin_clear_padding.  */
 
 static void
-clear_padding_type (clear_padding_struct *buf, tree type)
+clear_padding_type (clear_padding_struct *buf, tree type, HOST_WIDE_INT sz)
 {
-  HOST_WIDE_INT sz = int_size_in_bytes (type);
   switch (TREE_CODE (type))
     {
     case RECORD_TYPE:
@@ -4441,11 +4440,11 @@ clear_padding_type (clear_padding_struct
 	    else
 	      {
 		HOST_WIDE_INT pos = int_byte_position (field);
-		HOST_WIDE_INT fldsz = int_size_in_bytes (TREE_TYPE (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));
+		clear_padding_type (buf, TREE_TYPE (field), fldsz);
 		cur_pos += fldsz;
 	      }
 	  }
@@ -4453,9 +4452,9 @@ clear_padding_type (clear_padding_struct
       clear_padding_add_padding (buf, sz - cur_pos);
       break;
     case ARRAY_TYPE:
-      HOST_WIDE_INT nelts;
-      nelts = int_size_in_bytes (TREE_TYPE (type));
-      nelts = sz / nelts;
+      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
@@ -4479,7 +4478,7 @@ clear_padding_type (clear_padding_struct
 				   size_int (sz));
 	  gimple_set_location (g, buf->loc);
 	  gsi_insert_before (buf->gsi, g, GSI_SAME_STMT);
-	  buf->sz = sz / nelts;
+	  buf->sz = fldsz;
 	  buf->align = TYPE_ALIGN (elttype);
 	  buf->off = 0;
 	  buf->size = 0;
@@ -4493,7 +4492,7 @@ clear_padding_type (clear_padding_struct
 	  break;
 	}
       for (HOST_WIDE_INT i = 0; i < nelts; i++)
-	clear_padding_type (buf, TREE_TYPE (type));
+	clear_padding_type (buf, TREE_TYPE (type), fldsz);
       break;
     case UNION_TYPE:
       clear_padding_union (buf, type, sz);
@@ -4519,13 +4518,15 @@ clear_padding_type (clear_padding_struct
       buf->size += sz;
       break;
     case COMPLEX_TYPE:
-      clear_padding_type (buf, TREE_TYPE (type));
-      clear_padding_type (buf, TREE_TYPE (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));
+	clear_padding_type (buf, TREE_TYPE (type), fldsz);
       break;
     default:
       gcc_assert ((size_t) sz <= clear_padding_unit);
@@ -4609,7 +4610,7 @@ gimple_fold_builtin_clear_padding (gimpl
 	  gimple_set_location (g, loc);
 	  gsi_insert_before (gsi, g, GSI_SAME_STMT);
 	}
-      clear_padding_type (&buf, type);
+      clear_padding_type (&buf, type, buf.sz);
       clear_padding_flush (&buf, true);
     }
 
--- gcc/testsuite/g++.dg/torture/builtin-clear-padding-1.C.jj	2020-11-17 10:35:24.792522768 +0100
+++ gcc/testsuite/g++.dg/torture/builtin-clear-padding-1.C	2020-11-17 10:35:10.689682539 +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;
+}


	Jakub


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

* PDP endian bitfields
       [not found] ` <1AD8DAAC-A7A1-4A19-8538-4092C1F8C327@comcast.net>
@ 2020-11-20 16:26   ` Jakub Jelinek
  0 siblings, 0 replies; 4+ messages in thread
From: Jakub Jelinek @ 2020-11-20 16:26 UTC (permalink / raw)
  To: Paul Koning; +Cc: gcc-patches

On Mon, Nov 16, 2020 at 01:50:20PM -0500, Paul Koning wrote:
> > On Nov 16, 2020, at 6:57 AM, Jakub Jelinek via Gcc-patches <gcc-patches@gcc.gnu.org> wrote:
> > Working virtually out of Baker Island - AoE timezone.
> > 
> > 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), etc.
> 
> That reminds me of a similar comment on a commit a while ago.  I'd like to
> take care of this, but I'm not sure what questions need to be answered to
> do so.  Can you point me in the right direction?

There are now many of them.
grep -C3 '\(BYTES\|WORDS\)_BIG_ENDIAN [!=]= \(BYTES\|WORDS\)_BIG_ENDIAN' gcc/{,*/}*.{c,h,cc}
will show various cases (ignore the ones mentioning FLOAT_*).

Some are just optimizations, so they can be ignored, others in features not
really supported on pdp11 anyway (e.g. asan), but e.g. this
__builtin_clear_padding I've just checked in today, or the
__builtin_bit_cast that is pending review need extra work for PDP11.
And if you can figure out what to do with the optimizations too, e.g. sccvn
can now on big end little endian but not pdp endian handle propagation of
constants through memory including bitfields.  Or e.g.
store merging too.

In the __builtin_clear_padding case, basically all that is needed is find
out which bits in the target memory order are padding bits and which are
occupied by bitfields.  The code has the FIELD_DECL and needs to set certain
bits in the target memory image to 0 for bits in the bitfield and keep other
bits set if they are parts of other bitfields or padding.
For big and little endian, the code uses int_byte_position (field)
to find the starting byte,
tree_to_uhwi (DECL_FIELD_BIT_OFFSET (field)) % BITS_PER_UNIT
to determine how many (perhaps padding) bits are before that bitfield
in the first byte and TYPE_PRECISION (TREE_TYPE (field)) to determine
the bitsize of the bitfield.
I have no idea what needs to be done for PDP11 endian, the testsuite already
includes testcases that could cover it, or new ones similar to the existing
ones can be added if there are other special cases that need to be checked
(such as e.g. char : N bitfields if they are handled differently etc.).

	Jakub


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

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

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-11-16 11:57 [PATCH] c++: Add __builtin_clear_padding builtin - C++20 P0528R3 compiler side [PR88101] Jakub Jelinek
2020-11-16 21:13 ` [PATCH] c++: __builtin_clear_padding builtin follow-up [PR88101] Jakub Jelinek
2020-11-18  8:48   ` [PATCH] c++: __builtin_clear_padding builtin C++ tail padding fix [PR88101] Jakub Jelinek
     [not found] ` <1AD8DAAC-A7A1-4A19-8538-4092C1F8C327@comcast.net>
2020-11-20 16:26   ` PDP endian bitfields Jakub Jelinek

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).