This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: Reload bug & SRA oddness


On May  1, 2007, Roman Zippel <zippel@linux-m68k.org> wrote:

> Hi,
> On Tue, 1 May 2007, Alexandre Oliva wrote:

>> On Apr 30, 2007, Bernd Schmidt <bernds_cb1@t-online.de> wrote:
>> 
>> > As a consequence of all these problems, I'd like to see this patch
>> > reverted for now until it we know how to do it properly.
>> 
>> Here's a new patch that I hope will correct all the regressions,
>> including those on platforms I don't have access to.  Bootstrapped and
>> regtested on x86_64-linux-gnu.  Ok to install?

> This still breaks this m68k test case:
> http://gcc.gnu.org/ml/gcc/2007-04/msg00830.html

> BTW the previous patch broke the m68k bootstrap and this doesn't make me 
> very confident this one will do better.

Indeed, there was a serious bug in optimizing assignments to
BIT_FIELD_REFs that covered all fields scalarized into a single
variable, that affected only big-endian machines.

> A specific comment about the patch, it might be better to keep combining 
> multiple fields, if they don't match the alignment exactly (e.g. as in the 
> above test case).

In the patch below, alignment for access is inferred not from the
field, but from the enclosing object.  This should get us much better
access patterns for small objects that fit in registers, and use the
declaration alignment for other wider objects (that are less likely to
be scalarized anyway).

After this change, we get the optimal code that you quoted, even
without -malign-int.


Would you please give it a spin on m68k?  Bootstrap on
x86_64-linux-gnu hasn't completed yet, but it's already built Ada in
stage2, so I'm somewhat confident that this might work ;-)

If bootstrap and regression-testing completes, ok to install?

2007-04-30  Alexandre Oliva  <aoliva@redhat.com>

	PR middle-end/22156
	2007-04-05  Alexandre Oliva  <aoliva@redhat.com>
	* tree-sra.c (struct sra_elt): Add in_bitfld_block.
	(sra_hash_tree): Handle BIT_FIELD_REFs.
	(sra_elt_hash): Don't hash bitfld blocks.
	(sra_elt_eq): Skip them in parent compares as well.  Handle
	BIT_FIELD_REFs.
	(sra_walk_expr): Use a variable as both input and output if it's
	referenced in a BIT_FIELD_REF in a lhs.
	(build_element_name_1): Handle BIT_FIELD_REFs.
	(instantiate_element): Propagate nowarn from parents.
	Zero-initialize bit-field variables.
	(instantiate_missing_elements_1): Return the sra_elt.
	(canon_type_for_field): New.
	(try_instantiate_multiple_fields): New.  Infer widest mode
	from base decl.
	(instantiate_missing_elemnts): Use them.
	(generate_one_element_ref): Handle BIT_FIELD_REFs.
	(REPLDUP): New.
	(sra_build_elt_assignment): New.  Avoid need for needless
	initialization of maxalign and minalign.  Fix assignment to
	whole bitfield variables for big-endian machines.
	(generate_copy_inout): Use them.
	(generate_element_copy): Likewise.  Handle bitfld differences.
	(generate_element_zero): Don't recurse for blocks.  Use
	sra_build_elt_assignment.
	(generate_one_element_int): Take elt instead of var.  Use
	sra_build_elt_assignment.
	(generate_element_init_1): Adjust.
	(scalarize_use): Re-expand assignment to BIT_FIELD_REF of
	gimple_reg.  Use REPLDUP.
	(scalarize_copy): Use REPLDUP.
	(scalarize_ldst): Move assert before dereference.
	(dump_sra_elt_name): Handle BIT_FIELD_REFs.

Index: gcc/tree-sra.c
===================================================================
--- gcc/tree-sra.c.orig	2007-05-03 13:24:48.000000000 -0300
+++ gcc/tree-sra.c	2007-05-04 00:46:22.000000000 -0300
@@ -147,6 +147,10 @@ struct sra_elt
 
   /* True if there is BIT_FIELD_REF on the lhs with a vector. */
   bool is_vector_lhs;
+
+  /* 1 if the element is a field that is part of a block, 2 if the field
+     is the block itself, 0 if it's neither.  */
+  char in_bitfld_block;
 };
 
 #define IS_ELEMENT_FOR_GROUP(ELEMENT) (TREE_CODE (ELEMENT) == RANGE_EXPR)
@@ -461,6 +465,12 @@ sra_hash_tree (tree t)
       h = iterative_hash_expr (DECL_FIELD_BIT_OFFSET (t), h);
       break;
 
+    case BIT_FIELD_REF:
+      /* Don't take operand 0 into account, that's our parent.  */
+      h = iterative_hash_expr (TREE_OPERAND (t, 1), 0);
+      h = iterative_hash_expr (TREE_OPERAND (t, 2), h);
+      break;
+
     default:
       gcc_unreachable ();
     }
@@ -479,12 +489,14 @@ sra_elt_hash (const void *x)
 
   h = sra_hash_tree (e->element);
 
-  /* Take into account everything back up the chain.  Given that chain
-     lengths are rarely very long, this should be acceptable.  If we
-     truly identify this as a performance problem, it should work to
-     hash the pointer value "e->parent".  */
+  /* Take into account everything except bitfield blocks back up the
+     chain.  Given that chain lengths are rarely very long, this
+     should be acceptable.  If we truly identify this as a performance
+     problem, it should work to hash the pointer value
+     "e->parent".  */
   for (p = e->parent; p ; p = p->parent)
-    h = (h * 65521) ^ sra_hash_tree (p->element);
+    if (!p->in_bitfld_block)
+      h = (h * 65521) ^ sra_hash_tree (p->element);
 
   return h;
 }
@@ -497,8 +509,17 @@ sra_elt_eq (const void *x, const void *y
   const struct sra_elt *a = x;
   const struct sra_elt *b = y;
   tree ae, be;
+  const struct sra_elt *ap = a->parent;
+  const struct sra_elt *bp = b->parent;
 
-  if (a->parent != b->parent)
+  if (ap)
+    while (ap->in_bitfld_block)
+      ap = ap->parent;
+  if (bp)
+    while (bp->in_bitfld_block)
+      bp = bp->parent;
+
+  if (ap != bp)
     return false;
 
   ae = a->element;
@@ -533,6 +554,11 @@ sra_elt_eq (const void *x, const void *y
 	return false;
       return fields_compatible_p (ae, be);
 
+    case BIT_FIELD_REF:
+      return
+	tree_int_cst_equal (TREE_OPERAND (ae, 1), TREE_OPERAND (be, 1))
+	&& tree_int_cst_equal (TREE_OPERAND (ae, 2), TREE_OPERAND (be, 2));
+
     default:
       gcc_unreachable ();
     }
@@ -729,6 +755,7 @@ sra_walk_expr (tree *expr_p, block_stmt_
   tree inner = expr;
   bool disable_scalarization = false;
   bool use_all_p = false;
+  bool use_both_p = false;
 
   /* We're looking to collect a reference expression between EXPR and INNER,
      such that INNER is a scalarizable decl and all other nodes through EXPR
@@ -749,7 +776,11 @@ sra_walk_expr (tree *expr_p, block_stmt_
 	    if (disable_scalarization)
 	      elt->cannot_scalarize = true;
 	    else
-	      fns->use (elt, expr_p, bsi, is_output, use_all_p);
+	      {
+		if (use_both_p)
+		  fns->use (elt, expr_p, bsi, false, use_all_p);
+		fns->use (elt, expr_p, bsi, is_output, use_all_p);
+	      }
 	  }
 	return;
 
@@ -816,6 +847,11 @@ sra_walk_expr (tree *expr_p, block_stmt_
 	    if (elt)
 	      elt->is_vector_lhs = true;
 	  }
+	else if (is_output)
+	  /* If we're writing to a BIT-FIELD, we have to make sure the
+	     inner variable is back in place before we start modifying
+	     it.  */
+	  use_both_p = true;
 	/* A bit field reference (access to *multiple* fields simultaneously)
 	   is not currently scalarized.  Consider this an access to the
 	   complete outer element, to which walk_tree will bring us next.  */
@@ -884,11 +920,14 @@ sra_walk_asm_expr (tree expr, block_stmt
   sra_walk_tree_list (ASM_OUTPUTS (expr), bsi, true, fns);
 }
 
+static void sra_replace (block_stmt_iterator *bsi, tree list);
+static tree sra_build_elt_assignment (struct sra_elt *elt, tree src);
+
 /* Walk a GIMPLE_MODIFY_STMT and categorize the assignment appropriately.  */
 
 static void
 sra_walk_gimple_modify_stmt (tree expr, block_stmt_iterator *bsi,
-		      const struct sra_walk_fns *fns)
+			     const struct sra_walk_fns *fns)
 {
   struct sra_elt *lhs_elt, *rhs_elt;
   tree lhs, rhs;
@@ -1177,6 +1216,15 @@ build_element_name_1 (struct sra_elt *el
       sprintf (buffer, HOST_WIDE_INT_PRINT_DEC, TREE_INT_CST_LOW (t));
       obstack_grow (&sra_obstack, buffer, strlen (buffer));
     }
+  else if (TREE_CODE (t) == BIT_FIELD_REF)
+    {
+      sprintf (buffer, "B" HOST_WIDE_INT_PRINT_DEC,
+	       tree_low_cst (TREE_OPERAND (t, 2), 1));
+      obstack_grow (&sra_obstack, buffer, strlen (buffer));
+      sprintf (buffer, "F" HOST_WIDE_INT_PRINT_DEC,
+	       tree_low_cst (TREE_OPERAND (t, 1), 1));
+      obstack_grow (&sra_obstack, buffer, strlen (buffer));
+    }
   else
     {
       tree name = DECL_NAME (t);
@@ -1209,9 +1257,11 @@ instantiate_element (struct sra_elt *elt
 {
   struct sra_elt *base_elt;
   tree var, base;
+  bool nowarn = TREE_NO_WARNING (elt->element);
 
   for (base_elt = elt; base_elt->parent; base_elt = base_elt->parent)
-    continue;
+    if (!nowarn)
+      nowarn = TREE_NO_WARNING (base_elt->parent->element);
   base = base_elt->element;
 
   elt->replacement = var = make_rename_temp (elt->type, "SR");
@@ -1240,9 +1290,10 @@ instantiate_element (struct sra_elt *elt
       DECL_DEBUG_EXPR_IS_FROM (var) = 1;
       
       DECL_IGNORED_P (var) = 0;
-      TREE_NO_WARNING (var) = TREE_NO_WARNING (base);
-      if (elt->element && TREE_NO_WARNING (elt->element))
-	TREE_NO_WARNING (var) = 1;
+      TREE_NO_WARNING (var) = nowarn;
+
+      if (TREE_CODE (elt->element) == BIT_FIELD_REF)
+	DECL_INITIAL (var) = integer_zero_node;
     }
   else
     {
@@ -1337,7 +1388,7 @@ sum_instantiated_sizes (struct sra_elt *
 
 static void instantiate_missing_elements (struct sra_elt *elt);
 
-static void
+static struct sra_elt *
 instantiate_missing_elements_1 (struct sra_elt *elt, tree child, tree type)
 {
   struct sra_elt *sub = lookup_element (elt, child, type, INSERT);
@@ -1348,6 +1399,263 @@ instantiate_missing_elements_1 (struct s
     }
   else
     instantiate_missing_elements (sub);
+  return sub;
+}
+
+/* Obtain the canonical type for field F of ELEMENT.  */
+
+static tree
+canon_type_for_field (tree f, tree element)
+{
+  tree field_type = TREE_TYPE (f);
+
+  /* canonicalize_component_ref() unwidens some bit-field types (not
+     marked as DECL_BIT_FIELD in C++), so we must do the same, lest we
+     may introduce type mismatches.  */
+  if (INTEGRAL_TYPE_P (field_type)
+      && DECL_MODE (f) != TYPE_MODE (field_type))
+    field_type = TREE_TYPE (get_unwidened (build3 (COMPONENT_REF,
+						   field_type,
+						   element,
+						   f, NULL_TREE),
+					   NULL_TREE));
+
+  return field_type;
+}
+
+/* Look for adjacent fields of ELT starting at F that we'd like to
+   scalarize as a single variable.  Return the last field of the
+   group.  */
+
+static tree
+try_instantiate_multiple_fields (struct sra_elt *elt, tree f)
+{
+  unsigned HOST_WIDE_INT align, bit, size, alchk;
+  enum machine_mode mode;
+  tree first = f, prev;
+  tree type, var;
+  struct sra_elt *block;
+
+  if (!is_sra_scalar_type (TREE_TYPE (f))
+      || !host_integerp (DECL_FIELD_OFFSET (f), 1)
+      || !host_integerp (DECL_FIELD_BIT_OFFSET (f), 1)
+      || !host_integerp (DECL_SIZE (f), 1)
+      || lookup_element (elt, f, NULL, NO_INSERT))
+    return f;
+
+  /* Taking the alignment of elt->element is not enough, since it
+     might be just an array index or some such.  We shouldn't need to
+     initialize align here, but our optimizers don't always realize
+     that, if we leave the loop without initializing align, we'll fail
+     the assertion right after the loop.  */
+  align = (unsigned HOST_WIDE_INT)-1;
+  for (block = elt; block; block = block->parent)
+    if (DECL_P (block->element))
+      {
+	unsigned HOST_WIDE_INT dalign = DECL_ALIGN (block->element);
+
+	align = GET_MODE_BITSIZE (DECL_MODE (block->element));
+	if (align < dalign)
+	  align = dalign;
+	break;
+      }
+  gcc_assert (block);
+
+  bit = tree_low_cst (DECL_FIELD_OFFSET (f), 1) * BITS_PER_UNIT
+    + tree_low_cst (DECL_FIELD_BIT_OFFSET (f), 1);
+  size = tree_low_cst (DECL_SIZE (f), 1);
+
+  alchk = align - 1;
+  alchk = ~alchk;
+
+  if ((bit & alchk) != ((bit + size - 1) & alchk))
+    return f;
+
+  /* Find adjacent fields in the same alignment word.  */
+
+  for (prev = f, f = TREE_CHAIN (f);
+       f && TREE_CODE (f) == FIELD_DECL
+	 && is_sra_scalar_type (TREE_TYPE (f))
+	 && host_integerp (DECL_FIELD_OFFSET (f), 1)
+	 && host_integerp (DECL_FIELD_BIT_OFFSET (f), 1)
+	 && host_integerp (DECL_SIZE (f), 1)
+	 && !lookup_element (elt, f, NULL, NO_INSERT);
+       prev = f, f = TREE_CHAIN (f))
+    {
+      unsigned HOST_WIDE_INT nbit, nsize;
+
+      nbit = tree_low_cst (DECL_FIELD_OFFSET (f), 1) * BITS_PER_UNIT
+	+ tree_low_cst (DECL_FIELD_BIT_OFFSET (f), 1);
+      nsize = tree_low_cst (DECL_SIZE (f), 1);
+
+      if (bit + size == nbit)
+	{
+	  if ((bit & alchk) != ((nbit + nsize - 1) & alchk))
+	    break;
+	  size += nsize;
+	}
+      else if (nbit + nsize == bit)
+	{
+	  if ((nbit & alchk) != ((bit + size - 1) & alchk))
+	    break;
+	  bit = nbit;
+	  size += nsize;
+	}
+      else
+	break;
+    }
+
+  f = prev;
+
+  if (f == first)
+    return f;
+
+  gcc_assert ((bit & alchk) == ((bit + size - 1) & alchk));
+
+  /* Try to widen the bit range so as to cover padding bits as well.  */
+
+  if ((bit & ~alchk) || size != align)
+    {
+      unsigned HOST_WIDE_INT mbit = bit;
+      unsigned HOST_WIDE_INT msize = align;
+
+      for (f = TYPE_FIELDS (elt->type);
+	   f; f = TREE_CHAIN (f))
+	{
+	  unsigned HOST_WIDE_INT fbit, fsize;
+
+	  /* Skip the fields from first to prev.  */
+	  if (f == first)
+	    {
+	      f = prev;
+	      continue;
+	    }
+
+	  if (!(TREE_CODE (f) == FIELD_DECL
+		&& host_integerp (DECL_FIELD_OFFSET (f), 1)
+		&& host_integerp (DECL_FIELD_BIT_OFFSET (f), 1)))
+	    continue;
+
+	  fbit = tree_low_cst (DECL_FIELD_OFFSET (f), 1) * BITS_PER_UNIT
+	    + tree_low_cst (DECL_FIELD_BIT_OFFSET (f), 1);
+
+	  /* If we're past the selected word, we're fine.  */
+	  if ((bit & alchk) < (fbit & alchk))
+	    continue;
+
+	  if (host_integerp (DECL_SIZE (f), 1))
+	    fsize = tree_low_cst (DECL_SIZE (f), 1);
+	  else
+	    /* Assume a variable-sized field takes up all space till
+	       the end of the word.  ??? Endianness issues?  */
+	    fsize = align - (fbit & alchk);
+
+	  if ((fbit & alchk) < (bit & alchk))
+	    {
+	      /* A large field might start at a previous word and
+		 extend into the selected word.  Exclude those
+		 bits.  ??? Endianness issues? */
+	      HOST_WIDE_INT diff = fbit + fsize - mbit;
+
+	      if (diff <= 0)
+		continue;
+
+	      mbit += diff;
+	      msize -= diff;
+	    }
+	  else
+	    {
+	      /* Non-overlapping, great.  */
+	      if (fbit + fsize <= mbit
+		  || mbit + msize <= fbit)
+		continue;
+
+	      if (fbit <= mbit)
+		{
+		  unsigned HOST_WIDE_INT diff = fbit + fsize - mbit;
+		  mbit += diff;
+		  msize -= diff;
+		}
+	      else if (fbit > mbit)
+		msize -= (mbit + msize - fbit);
+	      else
+		gcc_unreachable ();
+	    }
+	}
+
+      bit = mbit;
+      size = msize;
+    }
+
+  /* Now we know the bit range we're interested in.  Find the smallest
+     machine mode we can use to access it.  */
+
+  for (mode = smallest_mode_for_size (size, MODE_INT);
+       ;
+       mode = GET_MODE_WIDER_MODE (mode))
+    {
+      gcc_assert (mode != VOIDmode);
+
+      alchk = GET_MODE_PRECISION (mode) - 1;
+      alchk = ~alchk;
+
+      if ((bit & alchk) == ((bit + size - 1) & alchk))
+	break;
+    }
+
+  gcc_assert (~alchk < align);
+
+  /* Create the field group as a single variable.  */
+
+  type = lang_hooks.types.type_for_mode (mode, 1);
+  gcc_assert (type);
+  var = build3 (BIT_FIELD_REF, type, NULL_TREE,
+		bitsize_int (size),
+		bitsize_int (bit));
+  BIT_FIELD_REF_UNSIGNED (var) = 1;
+
+  block = instantiate_missing_elements_1 (elt, var, type);
+  gcc_assert (block && block->is_scalar);
+
+  var = block->replacement;
+
+  if ((bit & ~alchk)
+      || (HOST_WIDE_INT)size != tree_low_cst (DECL_SIZE (var), 1))
+    {
+      block->replacement = build3 (BIT_FIELD_REF,
+				   TREE_TYPE (block->element), var,
+				   bitsize_int (size),
+				   bitsize_int (bit & ~alchk));
+      BIT_FIELD_REF_UNSIGNED (block->replacement) = 1;
+      TREE_NO_WARNING (block->replacement) = 1;
+    }
+
+  block->in_bitfld_block = 2;
+
+  /* Add the member fields to the group, such that they access
+     portions of the group variable.  */
+
+  for (f = first; f != TREE_CHAIN (prev); f = TREE_CHAIN (f))
+    {
+      tree field_type = canon_type_for_field (f, elt->element);
+      struct sra_elt *fld = lookup_element (block, f, field_type, INSERT);
+
+      gcc_assert (fld && fld->is_scalar && !fld->replacement);
+
+      fld->replacement = build3 (BIT_FIELD_REF, field_type, var,
+				 DECL_SIZE (f),
+				 bitsize_int
+				 ((TREE_INT_CST_LOW (DECL_FIELD_OFFSET (f))
+				   * BITS_PER_UNIT
+				   + (TREE_INT_CST_LOW
+				      (DECL_FIELD_BIT_OFFSET (f))))
+				  & ~alchk));
+      BIT_FIELD_REF_UNSIGNED (fld->replacement) = TYPE_UNSIGNED (field_type);
+      TREE_NO_WARNING (block->replacement) = 1;
+      fld->in_bitfld_block = 1;
+    }
+
+  return prev;
 }
 
 static void
@@ -1363,21 +1671,17 @@ instantiate_missing_elements (struct sra
 	for (f = TYPE_FIELDS (type); f ; f = TREE_CHAIN (f))
 	  if (TREE_CODE (f) == FIELD_DECL)
 	    {
-	      tree field_type = TREE_TYPE (f);
+	      tree last = try_instantiate_multiple_fields (elt, f);
 
-	      /* canonicalize_component_ref() unwidens some bit-field
-		 types (not marked as DECL_BIT_FIELD in C++), so we
-		 must do the same, lest we may introduce type
-		 mismatches.  */
-	      if (INTEGRAL_TYPE_P (field_type)
-		  && DECL_MODE (f) != TYPE_MODE (field_type))
-		field_type = TREE_TYPE (get_unwidened (build3 (COMPONENT_REF,
-							       field_type,
-							       elt->element,
-							       f, NULL_TREE),
-						       NULL_TREE));
+	      if (last != f)
+		{
+		  f = last;
+		  continue;
+		}
 
-	      instantiate_missing_elements_1 (elt, f, field_type);
+	      instantiate_missing_elements_1 (elt, f,
+					      canon_type_for_field
+					      (f, elt->element));
 	    }
 	break;
       }
@@ -1689,6 +1993,16 @@ generate_one_element_ref (struct sra_elt
       {
 	tree field = elt->element;
 
+	/* We can't test elt->in_bitfld_blk here because, when this is
+	   called from instantiate_element, we haven't set this field
+	   yet.  */
+	if (TREE_CODE (field) == BIT_FIELD_REF)
+	  {
+	    tree ret = copy_node (field);
+	    TREE_OPERAND (ret, 0) = base;
+	    return ret;
+	  }
+
 	/* Watch out for compatible records with differing field lists.  */
 	if (DECL_FIELD_CONTEXT (field) != TYPE_MAIN_VARIANT (TREE_TYPE (base)))
 	  field = find_compatible_field (TREE_TYPE (base), field);
@@ -1741,6 +2055,120 @@ sra_build_assignment (tree dst, tree src
   return build_gimple_modify_stmt (dst, src);
 }
 
+/* BIT_FIELD_REFs must not be shared.  sra_build_elt_assignment()
+   takes care of assignments, but we must create copies for uses.  */
+#define REPLDUP(t) (TREE_CODE (t) != BIT_FIELD_REF ? (t) : copy_node (t))
+
+static tree
+sra_build_elt_assignment (struct sra_elt *elt, tree src)
+{
+  tree dst = elt->replacement;
+  tree var, type, tmp, tmp2, tmp3;
+  tree list, stmt;
+  tree cst, cst2, mask;
+  tree minshift, maxshift;
+
+  if (TREE_CODE (dst) != BIT_FIELD_REF
+      || !elt->in_bitfld_block)
+    return sra_build_assignment (REPLDUP (dst), src);
+
+  var = TREE_OPERAND (dst, 0);
+
+  /* Try to widen the assignment to the entire variable.
+     We need the source to be a BIT_FIELD_REF as well, such that, for
+     BIT_FIELD_REF<d,sz,dp> = BIT_FIELD_REF<s,sz,sp>,
+     by design, conditions are met such that we can turn it into
+     d = BIT_FIELD_REF<s,dw,sp-dp>.  */
+  if (elt->in_bitfld_block == 2
+      && TREE_CODE (src) == BIT_FIELD_REF)
+    {
+      src = fold_build3 (BIT_FIELD_REF, TREE_TYPE (var),
+			 TREE_OPERAND (src, 0),
+			 DECL_SIZE (TREE_OPERAND (dst, 0)),
+			 size_binop (MINUS_EXPR, TREE_OPERAND (src, 2),
+				     TREE_OPERAND (dst, 2)));
+      BIT_FIELD_REF_UNSIGNED (src) = 1;
+
+      return sra_build_assignment (var, src);
+    }
+
+  if (!is_gimple_reg (var))
+    return sra_build_assignment (REPLDUP (dst), src);
+
+  list = alloc_stmt_list ();
+
+  cst = TREE_OPERAND (dst, 2);
+  cst2 = size_binop (PLUS_EXPR, TREE_OPERAND (dst, 1),
+		     TREE_OPERAND (dst, 2));
+
+  if (WORDS_BIG_ENDIAN)
+    {
+      maxshift = size_binop (MINUS_EXPR, DECL_SIZE (var), cst);
+      minshift = size_binop (MINUS_EXPR, DECL_SIZE (var), cst2);
+    }
+  else
+    {
+      maxshift = cst2;
+      minshift = cst;
+    }
+
+  type = TREE_TYPE (var);
+
+  mask = build_int_cst_wide (type, 1, 0);
+  cst = int_const_binop (LSHIFT_EXPR, mask, maxshift, 1);
+  cst2 = int_const_binop (LSHIFT_EXPR, mask, minshift, 1);
+  mask = int_const_binop (MINUS_EXPR, cst, cst2, 1);
+  mask = fold_build1 (BIT_NOT_EXPR, type, mask);
+
+  if (!WORDS_BIG_ENDIAN)
+    cst2 = TREE_OPERAND (dst, 2);
+
+  tmp = make_rename_temp (type, "SR");
+  stmt = build_gimple_modify_stmt (tmp,
+				   fold_build2 (BIT_AND_EXPR, type,
+						var, mask));
+  append_to_statement_list (stmt, &list);
+
+  if (is_gimple_reg (src))
+    tmp2 = src;
+  else
+    {
+      tmp2 = make_rename_temp (TREE_TYPE (src), "SR");
+      stmt = sra_build_assignment (tmp2, src);
+      append_to_statement_list (stmt, &list);
+    }
+
+  if (!TYPE_UNSIGNED (TREE_TYPE (tmp2))
+      || TYPE_MAIN_VARIANT (TREE_TYPE (tmp2)) != TYPE_MAIN_VARIANT (type))
+    {
+      tmp3 = make_rename_temp (type, "SR");
+      tmp2 = fold_build3 (BIT_FIELD_REF, type, tmp2, TREE_OPERAND (dst, 1),
+			  bitsize_int (0));
+      if (TREE_CODE (tmp2) == BIT_FIELD_REF)
+	BIT_FIELD_REF_UNSIGNED (tmp2) = 1;
+      stmt = sra_build_assignment (tmp3, tmp2);
+      append_to_statement_list (stmt, &list);
+      tmp2 = tmp3;
+    }
+
+  if (!integer_zerop (minshift))
+    {
+      tmp3 = make_rename_temp (type, "SR");
+      stmt = build_gimple_modify_stmt (tmp3,
+				       fold_build2 (LSHIFT_EXPR, type,
+						    tmp2, minshift));
+      append_to_statement_list (stmt, &list);
+      tmp2 = tmp3;
+    }
+
+  stmt = build_gimple_modify_stmt (var,
+				   fold_build2 (BIT_IOR_EXPR, type,
+						tmp, tmp2));
+  append_to_statement_list (stmt, &list);
+
+  return list;
+}
+
 /* Generate a set of assignment statements in *LIST_P to copy all
    instantiated elements under ELT to or from the equivalent structure
    rooted at EXPR.  COPY_OUT controls the direction of the copy, with
@@ -1771,9 +2199,9 @@ generate_copy_inout (struct sra_elt *elt
   else if (elt->replacement)
     {
       if (copy_out)
-	t = sra_build_assignment (elt->replacement, expr);
+	t = sra_build_elt_assignment (elt, expr);
       else
-	t = sra_build_assignment (expr, elt->replacement);
+	t = sra_build_assignment (expr, REPLDUP (elt->replacement));
       append_to_statement_list (t, list_p);
     }
   else
@@ -1798,6 +2226,19 @@ generate_element_copy (struct sra_elt *d
   FOR_EACH_ACTUAL_CHILD (dc, dst)
     {
       sc = lookup_element (src, dc->element, NULL, NO_INSERT);
+      if (!sc && dc->in_bitfld_block == 2)
+	{
+	  struct sra_elt *dcs;
+
+	  FOR_EACH_ACTUAL_CHILD (dcs, dc)
+	    {
+	      sc = lookup_element (src, dcs->element, NULL, NO_INSERT);
+	      gcc_assert (sc);
+	      generate_element_copy (dcs, sc, list_p);
+	    }
+
+	  continue;
+	}
       gcc_assert (sc);
       generate_element_copy (dc, sc, list_p);
     }
@@ -1808,7 +2249,7 @@ generate_element_copy (struct sra_elt *d
 
       gcc_assert (src->replacement);
 
-      t = sra_build_assignment (dst->replacement, src->replacement);
+      t = sra_build_elt_assignment (dst, REPLDUP (src->replacement));
       append_to_statement_list (t, list_p);
     }
 }
@@ -1829,8 +2270,9 @@ generate_element_zero (struct sra_elt *e
       return;
     }
 
-  FOR_EACH_ACTUAL_CHILD (c, elt)
-    generate_element_zero (c, list_p);
+  if (!elt->in_bitfld_block)
+    FOR_EACH_ACTUAL_CHILD (c, elt)
+      generate_element_zero (c, list_p);
 
   if (elt->replacement)
     {
@@ -1839,7 +2281,7 @@ generate_element_zero (struct sra_elt *e
       gcc_assert (elt->is_scalar);
       t = fold_convert (elt->type, integer_zero_node);
 
-      t = sra_build_assignment (elt->replacement, t);
+      t = sra_build_elt_assignment (elt, t);
       append_to_statement_list (t, list_p);
     }
 }
@@ -1848,10 +2290,10 @@ generate_element_zero (struct sra_elt *e
    Add the result to *LIST_P.  */
 
 static void
-generate_one_element_init (tree var, tree init, tree *list_p)
+generate_one_element_init (struct sra_elt *elt, tree init, tree *list_p)
 {
   /* The replacement can be almost arbitrarily complex.  Gimplify.  */
-  tree stmt = sra_build_assignment (var, init);
+  tree stmt = sra_build_elt_assignment (elt, init);
   gimplify_and_add (stmt, list_p);
 }
 
@@ -1880,7 +2322,7 @@ generate_element_init_1 (struct sra_elt 
     {
       if (elt->replacement)
 	{
-	  generate_one_element_init (elt->replacement, init, list_p);
+	  generate_one_element_init (elt, init, list_p);
 	  elt->visited = true;
 	}
       return result;
@@ -2041,23 +2483,45 @@ static void
 scalarize_use (struct sra_elt *elt, tree *expr_p, block_stmt_iterator *bsi,
 	       bool is_output, bool use_all)
 {
-  tree list = NULL, stmt = bsi_stmt (*bsi);
+  tree stmt = bsi_stmt (*bsi);
 
   if (elt->replacement)
     {
       /* If we have a replacement, then updating the reference is as
 	 simple as modifying the existing statement in place.  */
       if (is_output)
-	mark_all_v_defs (stmt);
-      *expr_p = elt->replacement;
+	{
+	  if (TREE_CODE (elt->replacement) == BIT_FIELD_REF
+	      && is_gimple_reg (TREE_OPERAND (elt->replacement, 0))
+	      && TREE_CODE (stmt) == GIMPLE_MODIFY_STMT
+	      && &GIMPLE_STMT_OPERAND (stmt, 0) == expr_p)
+	    {
+	      tree newstmt = sra_build_elt_assignment
+		(elt, GIMPLE_STMT_OPERAND (stmt, 1));
+	      if (TREE_CODE (newstmt) != STATEMENT_LIST)
+		{
+		  tree list = alloc_stmt_list ();
+		  append_to_statement_list (newstmt, &list);
+		  newstmt = list;
+		}
+	      sra_replace (bsi, newstmt);
+	      return;
+	    }
+
+	  mark_all_v_defs (stmt);
+	}
+      *expr_p = REPLDUP (elt->replacement);
       update_stmt (stmt);
     }
   else
     {
-      /* Otherwise we need some copies.  If ELT is being read, then we want
-	 to store all (modified) sub-elements back into the structure before
-	 the reference takes place.  If ELT is being written, then we want to
-	 load the changed values back into our shadow variables.  */
+      tree list = NULL;
+
+      /* Otherwise we need some copies.  If ELT is being read, then we
+	 want to store all (modified) sub-elements back into the
+	 structure before the reference takes place.  If ELT is being
+	 written, then we want to load the changed values back into
+	 our shadow variables.  */
       /* ??? We don't check modified for reads, we just always write all of
 	 the values.  We should be able to record the SSA number of the VOP
 	 for which the values were last read.  If that number matches the
@@ -2101,7 +2565,7 @@ scalarize_copy (struct sra_elt *lhs_elt,
       gcc_assert (TREE_CODE (stmt) == GIMPLE_MODIFY_STMT);
 
       GIMPLE_STMT_OPERAND (stmt, 0) = lhs_elt->replacement;
-      GIMPLE_STMT_OPERAND (stmt, 1) = rhs_elt->replacement;
+      GIMPLE_STMT_OPERAND (stmt, 1) = REPLDUP (rhs_elt->replacement);
       update_stmt (stmt);
     }
   else if (lhs_elt->use_block_copy || rhs_elt->use_block_copy)
@@ -2255,8 +2719,8 @@ scalarize_ldst (struct sra_elt *elt, tre
 
       mark_all_v_defs (stmt);
       generate_copy_inout (elt, is_output, other, &list);
-      mark_all_v_defs (list);
       gcc_assert (list);
+      mark_all_v_defs (list);
 
       /* Preserve EH semantics.  */
       if (stmt_ends_bb_p (stmt))
@@ -2352,6 +2816,10 @@ dump_sra_elt_name (FILE *f, struct sra_e
 	    fputc ('.', f);
 	  print_generic_expr (f, elt->element, dump_flags);
 	}
+      else if (TREE_CODE (elt->element) == BIT_FIELD_REF)
+	fprintf (f, "$B" HOST_WIDE_INT_PRINT_DEC "F" HOST_WIDE_INT_PRINT_DEC,
+		 tree_low_cst (TREE_OPERAND (elt->element, 2), 1),
+		 tree_low_cst (TREE_OPERAND (elt->element, 1), 1));
       else if (TREE_CODE (elt->element) == RANGE_EXPR)
 	fprintf (f, "["HOST_WIDE_INT_PRINT_DEC".."HOST_WIDE_INT_PRINT_DEC"]",
 		 TREE_INT_CST_LOW (TREE_OPERAND (elt->element, 0)),
-- 
Alexandre Oliva         http://www.lsd.ic.unicamp.br/~oliva/
FSF Latin America Board Member         http://www.fsfla.org/
Red Hat Compiler Engineer   aoliva@{redhat.com, gcc.gnu.org}
Free Software Evangelist  oliva@{lsd.ic.unicamp.br, gnu.org}

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]