This patch is a followup to the addition of support for -fstrict-volatile-bitfields (required by the ARM EABI); see this thread

http://gcc.gnu.org/ml/gcc-patches/2010-10/msg01889.html

for discussion of the original patch.

That patch only addressed the behavior when extracting the value of a volatile bit field, but the same problems affect storing values into a volatile bit field (or a field of a packed structure, which is effectively implemented as a bit field). This patch makes the code for bitfield stores mirror that for bitfield loads.

Although the fix is in target-independent code, it's really for ARM; hence the test case, which (without this patch) generates wrong code. Code to determine the access width was correctly preserving the user-specified width, but was incorrectly falling through to code that assumes word mode.

As well as regression testing on arm-none-eabi, I've bootstrapped and regression-tested this patch on x86_64 Linux. Earlier versions of this patch have also been present in our local 4.5 and 4.6 GCC trees for some time, so it's been well-tested on a variety of other platforms. OK to check in on mainline?

-Sandra


2012-08-21  Paul Brook  <p...@codesourcery.com>
            Joseph Myers <jos...@codesourcery.com>
            Sandra Loosemore  <san...@codesourcery.com>

        gcc/
        * expr.h (store_bit_field): Add packedp parameter to prototype.
        * expmed.c (store_bit_field, store_bit_field_1): Add packedp
        parameter.  Adjust all callers.
        (warn_misaligned_bitfield): New function, split from
        extract_fixed_bit_field.
        (store_fixed_bit_field): Add packedp parameter.  Fix wrong-code
        behavior for the combination of misaligned bitfield and
        -fstrict-volatile-bitfields.  Use warn_misaligned_bitfield.
        (extract_fixed_bit_field): Use warn_misaligned_bitfield.
        * expr.c: Adjust calls to store_bit_field.
        (expand_assignment): Identify accesses to packed structures.
        (store_field): Add packedp parameter.  Adjust callers.
        * calls.c: Adjust calls to store_bit_field.
        * ifcvt.c: Likewise.
        * config/s390/s390.c: Likewise.

        gcc/testsuite/
        * gcc.target/arm/volatile-bitfields-5.c: New test case.

Index: gcc/expr.h
===================================================================
--- gcc/expr.h	(revision 190541)
+++ gcc/expr.h	(working copy)
@@ -693,7 +693,7 @@ extern void store_bit_field (rtx, unsign
 			     unsigned HOST_WIDE_INT,
 			     unsigned HOST_WIDE_INT,
 			     unsigned HOST_WIDE_INT,
-			     enum machine_mode, rtx);
+			     bool, enum machine_mode, rtx);
 extern rtx extract_bit_field (rtx, unsigned HOST_WIDE_INT,
 			      unsigned HOST_WIDE_INT, int, bool, rtx,
 			      enum machine_mode, enum machine_mode);
Index: gcc/expmed.c
===================================================================
--- gcc/expmed.c	(revision 190541)
+++ gcc/expmed.c	(working copy)
@@ -50,7 +50,7 @@ static void store_fixed_bit_field (rtx, 
 				   unsigned HOST_WIDE_INT,
 				   unsigned HOST_WIDE_INT,
 				   unsigned HOST_WIDE_INT,
-				   rtx);
+				   rtx, bool);
 static void store_split_bit_field (rtx, unsigned HOST_WIDE_INT,
 				   unsigned HOST_WIDE_INT,
 				   unsigned HOST_WIDE_INT,
@@ -406,7 +406,7 @@ store_bit_field_1 (rtx str_rtx, unsigned
 		   unsigned HOST_WIDE_INT bitnum,
 		   unsigned HOST_WIDE_INT bitregion_start,
 		   unsigned HOST_WIDE_INT bitregion_end,
-		   enum machine_mode fieldmode,
+		   bool packedp, enum machine_mode fieldmode,
 		   rtx value, bool fallback_p)
 {
   unsigned int unit
@@ -638,7 +638,7 @@ store_bit_field_1 (rtx str_rtx, unsigned
 	  if (!store_bit_field_1 (op0, new_bitsize,
 				  bitnum + bit_offset,
 				  bitregion_start, bitregion_end,
-				  word_mode,
+				  false, word_mode,
 				  value_word, fallback_p))
 	    {
 	      delete_insns_since (last);
@@ -859,7 +859,7 @@ store_bit_field_1 (rtx str_rtx, unsigned
 	  tempreg = copy_to_reg (xop0);
 	  if (store_bit_field_1 (tempreg, bitsize, xbitpos,
 				 bitregion_start, bitregion_end,
-				 fieldmode, orig_value, false))
+				 false, fieldmode, orig_value, false))
 	    {
 	      emit_move_insn (xop0, tempreg);
 	      return true;
@@ -872,7 +872,7 @@ store_bit_field_1 (rtx str_rtx, unsigned
     return false;
 
   store_fixed_bit_field (op0, offset, bitsize, bitpos,
-			 bitregion_start, bitregion_end, value);
+			 bitregion_start, bitregion_end, value, packedp);
   return true;
 }
 
@@ -885,6 +885,8 @@ store_bit_field_1 (rtx str_rtx, unsigned
    These two fields are 0, if the C++ memory model does not apply,
    or we are not interested in keeping track of bitfield regions.
 
+   PACKEDP is true for fields with the packed attribute.
+
    FIELDMODE is the machine-mode of the FIELD_DECL node for this field.  */
 
 void
@@ -892,6 +894,7 @@ store_bit_field (rtx str_rtx, unsigned H
 		 unsigned HOST_WIDE_INT bitnum,
 		 unsigned HOST_WIDE_INT bitregion_start,
 		 unsigned HOST_WIDE_INT bitregion_end,
+		 bool packedp,
 		 enum machine_mode fieldmode,
 		 rtx value)
 {
@@ -924,9 +927,48 @@ store_bit_field (rtx str_rtx, unsigned H
 
   if (!store_bit_field_1 (str_rtx, bitsize, bitnum,
 			  bitregion_start, bitregion_end,
-			  fieldmode, value, true))
+			  packedp, fieldmode, value, true))
     gcc_unreachable ();
 }
+
+static void
+warn_misaligned_bitfield (bool struct_member, bool packedp)
+{
+  static bool informed_about_misalignment = false;
+  bool warned;
+
+  if (packedp)
+    {
+      if (struct_member)
+	warning_at (input_location, OPT_fstrict_volatile_bitfields,
+		    "multiple accesses to volatile structure member"
+		    " because of packed attribute");
+      else
+	warning_at (input_location, OPT_fstrict_volatile_bitfields,
+		    "multiple accesses to volatile structure bitfield"
+		    " because of packed attribute");
+    }
+  else
+    {
+      if (struct_member)
+	warned = warning_at (input_location, OPT_fstrict_volatile_bitfields,
+			     "mis-aligned access used for structure member");
+      else
+	warned = warning_at (input_location, OPT_fstrict_volatile_bitfields,
+			     "mis-aligned access used for structure bitfield");
+      if (! informed_about_misalignment && warned)
+	{
+	  informed_about_misalignment = true;
+	  inform (input_location,
+		  "When a volatile object spans multiple type-sized"
+		  " locations, the compiler must choose between using a"
+		  " single mis-aligned access to preserve the volatility,"
+		  " or using multiple aligned accesses to avoid runtime"
+		  " faults.  This code may fail at runtime if the hardware"
+		  " does not allow this access.");
+	}
+    }
+}
 
 /* Use shifts and boolean operations to store VALUE
    into a bit field of width BITSIZE
@@ -943,7 +985,8 @@ store_fixed_bit_field (rtx op0, unsigned
 		       unsigned HOST_WIDE_INT bitpos,
 		       unsigned HOST_WIDE_INT bitregion_start,
 		       unsigned HOST_WIDE_INT bitregion_end,
-		       rtx value)
+		       rtx value,
+		       bool packedp)
 {
   enum machine_mode mode;
   unsigned int total_bits = BITS_PER_WORD;
@@ -981,6 +1024,7 @@ store_fixed_bit_field (rtx op0, unsigned
 	 includes the entire field.  If such a mode would be larger than
 	 a word, we won't be doing the extraction the normal way.
 	 We don't want a mode bigger than the destination.  */
+      bool realign = true;
 
       mode = GET_MODE (op0);
       if (GET_MODE_BITSIZE (mode) == 0
@@ -991,7 +1035,25 @@ store_fixed_bit_field (rtx op0, unsigned
           && GET_MODE_BITSIZE (GET_MODE (op0)) > 0
 	  && GET_MODE_BITSIZE (GET_MODE (op0)) <= maxbits
 	  && flag_strict_volatile_bitfields > 0)
-	mode = GET_MODE (op0);
+	{
+	  /* We must use the specified access size.  */
+	  mode = GET_MODE (op0);
+	  total_bits = GET_MODE_BITSIZE (mode);
+	  if (bitpos + bitsize <= total_bits
+	      && bitpos + bitsize 
+		 + (offset % (total_bits / BITS_PER_UNIT)) * BITS_PER_UNIT
+		 > total_bits)
+	    {
+	      realign = false;
+	      if (STRICT_ALIGNMENT)
+		{
+		  warn_misaligned_bitfield (bitsize == GET_MODE_BITSIZE (mode),
+					    packedp);
+		  if (packedp)
+		    mode = VOIDmode;
+		}
+	    }
+	}
       else
 	mode = get_best_mode (bitsize, bitpos + offset * BITS_PER_UNIT,
 			      bitregion_start, bitregion_end,
@@ -1000,7 +1062,8 @@ store_fixed_bit_field (rtx op0, unsigned
       if (mode == VOIDmode)
 	{
 	  /* The only way this should occur is if the field spans word
-	     boundaries.  */
+	     boundaries, or container boundaries with
+	     -fstrict-volatile-bitfields.  */
 	  store_split_bit_field (op0, bitsize, bitpos + offset * BITS_PER_UNIT,
 				 bitregion_start, bitregion_end, value);
 	  return;
@@ -1018,12 +1081,15 @@ store_fixed_bit_field (rtx op0, unsigned
 		     * BITS_PER_UNIT);
 	}
 
-      /* Get ref to an aligned byte, halfword, or word containing the field.
-	 Adjust BITPOS to be position within a word,
-	 and OFFSET to be the offset of that word.
-	 Then alter OP0 to refer to that word.  */
-      bitpos += (offset % (total_bits / BITS_PER_UNIT)) * BITS_PER_UNIT;
-      offset -= (offset % (total_bits / BITS_PER_UNIT));
+      if (realign)
+	{
+	  /* Get ref to an aligned byte, halfword, or word containing the field.
+	     Adjust BITPOS to be position within a word,
+	     and OFFSET to be the offset of that word.
+	     Then alter OP0 to refer to that word.  */
+	  bitpos += (offset % (total_bits / BITS_PER_UNIT)) * BITS_PER_UNIT;
+	  offset -= (offset % (total_bits / BITS_PER_UNIT));
+	}
       op0 = adjust_address (op0, mode, offset);
     }
 
@@ -1250,7 +1316,8 @@ store_split_bit_field (rtx op0, unsigned
 	 it is just an out-of-bounds access.  Ignore it.  */
       if (word != const0_rtx)
 	store_fixed_bit_field (word, offset * unit / BITS_PER_UNIT, thissize,
-			       thispos, bitregion_start, bitregion_end, part);
+			       thispos, bitregion_start, bitregion_end, part,
+			       false);
       bitsdone += thissize;
     }
 }
@@ -1857,42 +1924,13 @@ extract_fixed_bit_field (enum machine_mo
 	{
 	  if (STRICT_ALIGNMENT)
 	    {
-	      static bool informed_about_misalignment = false;
-	      bool warned;
-
+	      warn_misaligned_bitfield (bitsize == total_bits, packedp);
 	      if (packedp)
 		{
-		  if (bitsize == total_bits)
-		    warned = warning_at (input_location, OPT_fstrict_volatile_bitfields,
-					 "multiple accesses to volatile structure member"
-					 " because of packed attribute");
-		  else
-		    warned = warning_at (input_location, OPT_fstrict_volatile_bitfields,
-					 "multiple accesses to volatile structure bitfield"
-					 " because of packed attribute");
-
 		  return extract_split_bit_field (op0, bitsize,
 						  bitpos + offset * BITS_PER_UNIT,
 						  unsignedp);
 		}
-
-	      if (bitsize == total_bits)
-		warned = warning_at (input_location, OPT_fstrict_volatile_bitfields,
-				     "mis-aligned access used for structure member");
-	      else
-		warned = warning_at (input_location, OPT_fstrict_volatile_bitfields,
-				     "mis-aligned access used for structure bitfield");
-
-	      if (! informed_about_misalignment && warned)
-		{
-		  informed_about_misalignment = true;
-		  inform (input_location,
-			  "when a volatile object spans multiple type-sized locations,"
-			  " the compiler must choose between using a single mis-aligned access to"
-			  " preserve the volatility, or using multiple aligned accesses to avoid"
-			  " runtime faults; this code may fail at runtime if the hardware does"
-			  " not allow this access");
-		}
 	    }
 	}
       else
Index: gcc/expr.c
===================================================================
--- gcc/expr.c	(revision 190541)
+++ gcc/expr.c	(working copy)
@@ -142,7 +142,7 @@ static void store_constructor (tree, rtx
 static rtx store_field (rtx, HOST_WIDE_INT, HOST_WIDE_INT,
 			unsigned HOST_WIDE_INT, unsigned HOST_WIDE_INT,
 			enum machine_mode,
-			tree, tree, alias_set_type, bool);
+			tree, tree, alias_set_type, bool, bool);
 
 static unsigned HOST_WIDE_INT highest_pow2_factor_for_target (const_tree, const_tree);
 
@@ -2076,7 +2076,7 @@ emit_group_store (rtx orig_dst, rtx src,
 	emit_move_insn (adjust_address (dest, mode, bytepos), tmps[i]);
       else
 	store_bit_field (dest, bytelen * BITS_PER_UNIT, bytepos * BITS_PER_UNIT,
-			 0, 0, mode, tmps[i]);
+			 0, 0, false, mode, tmps[i]);
     }
 
   /* Copy from the pseudo into the (probable) hard reg.  */
@@ -2170,7 +2170,8 @@ copy_blkmode_from_reg (rtx tgtblk, rtx s
 
       /* Use xbitpos for the source extraction (right justified) and
 	 bitpos for the destination store (left justified).  */
-      store_bit_field (dst, bitsize, bitpos % BITS_PER_WORD, 0, 0, copy_mode,
+      store_bit_field (dst, bitsize, bitpos % BITS_PER_WORD, 0, 0,
+		       false, copy_mode,
 		       extract_bit_field (src, bitsize,
 					  xbitpos % BITS_PER_WORD, 1, false,
 					  NULL_RTX, copy_mode, copy_mode));
@@ -2249,7 +2250,7 @@ copy_blkmode_to_reg (enum machine_mode m
       /* Use bitpos for the source extraction (left justified) and
 	 xbitpos for the destination store (right justified).  */
       store_bit_field (dst_word, bitsize, xbitpos % BITS_PER_WORD,
-		       0, 0, word_mode,
+		       0, 0, false, word_mode,
 		       extract_bit_field (src_word, bitsize,
 					  bitpos % BITS_PER_WORD, 1, false,
 					  NULL_RTX, word_mode, word_mode));
@@ -2933,7 +2934,8 @@ write_complex_part (rtx cplx, rtx val, b
 	gcc_assert (MEM_P (cplx) && ibitsize < BITS_PER_WORD);
     }
 
-  store_bit_field (cplx, ibitsize, imag_p ? ibitsize : 0, 0, 0, imode, val);
+  store_bit_field (cplx, ibitsize, imag_p ? ibitsize : 0, 0, 0,
+		   false, imode, val);
 }
 
 /* Extract one of the components of the complex value CPLX.  Extract the
@@ -4614,7 +4616,7 @@ expand_assignment (tree to, tree from, b
 	}
       else
 	store_bit_field (mem, GET_MODE_BITSIZE (mode),
-			 0, 0, 0, mode, reg);
+			 0, 0, 0, false, mode, reg);
       return;
     }
 
@@ -4759,14 +4761,14 @@ expand_assignment (tree to, tree from, b
 	    result = store_field (XEXP (to_rtx, 0), bitsize, bitpos,
 				  bitregion_start, bitregion_end,
 				  mode1, from, TREE_TYPE (tem),
-				  get_alias_set (to), nontemporal);
+				  get_alias_set (to), nontemporal, false);
 	  else if (bitpos >= mode_bitsize / 2)
 	    result = store_field (XEXP (to_rtx, 1), bitsize,
 				  bitpos - mode_bitsize / 2,
 				  bitregion_start, bitregion_end,
 				  mode1, from,
 				  TREE_TYPE (tem), get_alias_set (to),
-				  nontemporal);
+				  nontemporal, false);
 	  else if (bitpos == 0 && bitsize == mode_bitsize)
 	    {
 	      rtx from_rtx;
@@ -4788,7 +4790,7 @@ expand_assignment (tree to, tree from, b
 				    bitregion_start, bitregion_end,
 				    mode1, from,
 				    TREE_TYPE (tem), get_alias_set (to),
-				    nontemporal);
+				    nontemporal, false);
 	      emit_move_insn (XEXP (to_rtx, 0), read_complex_part (temp, false));
 	      emit_move_insn (XEXP (to_rtx, 1), read_complex_part (temp, true));
 	    }
@@ -4817,11 +4819,21 @@ expand_assignment (tree to, tree from, b
 					       to_rtx, to, from))
 	    result = NULL;
 	  else
-	    result = store_field (to_rtx, bitsize, bitpos,
-				  bitregion_start, bitregion_end,
-				  mode1, from,
-				  TREE_TYPE (tem), get_alias_set (to),
-				  nontemporal);
+	    {
+	      bool packedp = false;
+
+	      if (TREE_CODE(to) == COMPONENT_REF
+		  && (TYPE_PACKED (TREE_TYPE (TREE_OPERAND (to, 0)))
+		      || (TREE_CODE (TREE_OPERAND (to, 1)) == FIELD_DECL
+			  && DECL_PACKED (TREE_OPERAND (to, 1)))))
+		packedp = true;
+
+	      result = store_field (to_rtx, bitsize, bitpos,
+				    bitregion_start, bitregion_end,
+				    mode1, from,
+				    TREE_TYPE (tem), get_alias_set (to),
+				    nontemporal, packedp);
+	    }
 	}
 
       if (misalignp)
@@ -5220,7 +5232,7 @@ store_expr (tree exp, rtx target, int ca
 			      : BLOCK_OP_NORMAL));
 	  else if (GET_MODE (target) == BLKmode)
 	    store_bit_field (target, INTVAL (expr_size (exp)) * BITS_PER_UNIT,
-			     0, 0, 0, GET_MODE (temp), temp);
+			     0, 0, 0, false, GET_MODE (temp), temp);
 	  else
 	    convert_move (target, temp, unsignedp);
 	}
@@ -5688,7 +5700,7 @@ store_constructor_field (rtx target, uns
     }
   else
     store_field (target, bitsize, bitpos, 0, 0, mode, exp, type, alias_set,
-		 false);
+		 false, false);
 }
 
 /* Store the value of constructor EXP into the rtx TARGET.
@@ -6275,14 +6287,16 @@ store_constructor (tree exp, rtx target,
    (in general) be different from that for TARGET, since TARGET is a
    reference to the containing structure.
 
-   If NONTEMPORAL is true, try generating a nontemporal store.  */
+   If NONTEMPORAL is true, try generating a nontemporal store.
+
+   PACKEDP is true if this field has the packed attribute.  */
 
 static rtx
 store_field (rtx target, HOST_WIDE_INT bitsize, HOST_WIDE_INT bitpos,
 	     unsigned HOST_WIDE_INT bitregion_start,
 	     unsigned HOST_WIDE_INT bitregion_end,
 	     enum machine_mode mode, tree exp, tree type,
-	     alias_set_type alias_set, bool nontemporal)
+	     alias_set_type alias_set, bool nontemporal, bool packedp)
 {
   if (TREE_CODE (exp) == ERROR_MARK)
     return const0_rtx;
@@ -6315,7 +6329,8 @@ store_field (rtx target, HOST_WIDE_INT b
 
       store_field (blk_object, bitsize, bitpos,
 		   bitregion_start, bitregion_end,
-		   mode, exp, type, MEM_ALIAS_SET (blk_object), nontemporal);
+		   mode, exp, type, MEM_ALIAS_SET (blk_object), nontemporal,
+		   false);
 
       emit_move_insn (target, object);
 
@@ -6432,7 +6447,7 @@ store_field (rtx target, HOST_WIDE_INT b
       /* Store the value in the bitfield.  */
       store_bit_field (target, bitsize, bitpos,
 		       bitregion_start, bitregion_end,
-		       mode, temp);
+		       packedp, mode, temp);
 
       return const0_rtx;
     }
@@ -8024,7 +8039,7 @@ expand_expr_real_2 (sepops ops, rtx targ
 				 * BITS_PER_UNIT),
 				(HOST_WIDE_INT) GET_MODE_BITSIZE (mode)),
 			   0, 0, 0, TYPE_MODE (valtype), treeop0,
-			   type, 0, false);
+			   type, 0, false, false);
 	    }
 
 	  /* Return the entire union.  */
Index: gcc/calls.c
===================================================================
--- gcc/calls.c	(revision 190541)
+++ gcc/calls.c	(working copy)
@@ -1051,7 +1051,7 @@ store_unaligned_arguments_into_pseudos (
 
 	    bytes -= bitsize / BITS_PER_UNIT;
 	    store_bit_field (reg, bitsize, endian_correction, 0, 0,
-			     word_mode, word);
+			     false, word_mode, word);
 	  }
       }
 }
Index: gcc/ifcvt.c
===================================================================
--- gcc/ifcvt.c	(revision 190541)
+++ gcc/ifcvt.c	(working copy)
@@ -896,7 +896,7 @@ noce_emit_move_insn (rtx x, rtx y)
 		}
 
 	      gcc_assert (start < (MEM_P (op) ? BITS_PER_UNIT : BITS_PER_WORD));
-	      store_bit_field (op, size, start, 0, 0, GET_MODE (x), y);
+	      store_bit_field (op, size, start, 0, 0, false, GET_MODE (x), y);
 	      return;
 	    }
 
@@ -951,7 +951,7 @@ noce_emit_move_insn (rtx x, rtx y)
   outmode = GET_MODE (outer);
   bitpos = SUBREG_BYTE (outer) * BITS_PER_UNIT;
   store_bit_field (inner, GET_MODE_BITSIZE (outmode), bitpos,
-		   0, 0, outmode, y);
+		   0, 0, false, outmode, y);
 }
 
 /* Return sequence of instructions generated by if conversion.  This
Index: gcc/config/s390/s390.c
===================================================================
--- gcc/config/s390/s390.c	(revision 190541)
+++ gcc/config/s390/s390.c	(working copy)
@@ -4944,7 +4944,7 @@ s390_expand_atomic (enum machine_mode mo
     case SET:
       if (ac.aligned && MEM_P (val))
 	store_bit_field (new_rtx, GET_MODE_BITSIZE (mode), 0,
-			 0, 0, SImode, val);
+			 0, 0, false, SImode, val);
       else
 	{
 	  new_rtx = expand_simple_binop (SImode, AND, new_rtx, ac.modemaski,
Index: gcc/testsuite/gcc.target/arm/volatile-bitfields-5.c
===================================================================
--- gcc/testsuite/gcc.target/arm/volatile-bitfields-5.c	(revision 0)
+++ gcc/testsuite/gcc.target/arm/volatile-bitfields-5.c	(revision 0)
@@ -0,0 +1,37 @@
+/* { dg-require-effective-target arm_eabi } */
+/* { dg-do run } */
+/* { dg-options "-O2 -fno-strict-aliasing" } */
+
+#include <stdlib.h>
+
+#pragma pack(1)	// no space between structure members
+volatile typedef struct {
+  unsigned char byte;
+  /* Packed field members get converted to bitfields internally.
+     On most other these will either be split into smaller accesses,
+     or a single large aligned access.  With -fstrict-volatile-bitfields
+     store_bitfield was assuming a single aligned user-specified-size access
+     covered the whole field.  */
+  unsigned short halfword;
+} S;
+#pragma pack() // resume default structure packing
+
+void __attribute__((noinline)) foo(S *p)
+{
+  p->halfword++; /* { dg-message "note: When a volatile" } */
+  /* { dg-warning "mis-aligned access" "" { target *-*-* } 21 } */
+}
+
+int main()
+{
+/* Make sure the halfword is actually aligned in practice.  */
+  short buf[3];
+  buf[0] = 0x5a5a;
+  buf[1] = 0x42ff;
+  buf[2] = 0x5a5a;
+
+  foo((S *)(((char *)buf) + 1));
+  if (buf[0] != 0x5a5a || buf[1] != 0x4300 || buf[2] != 0x5a5a)
+    abort();
+  return 0;
+}

Reply via email to