From bd3647bf76d1d207eaa80c7c6b5a460fecc327a9 Mon Sep 17 00:00:00 2001
From: Jakub Jelinek <jakub@redhat.com>
Date: Thu, 23 May 2013 11:17:34 +0200
Subject: [PATCH] re PR rtl-optimization/57344 (wrong code with pragma pack(1)
 and -O1 on x86)

	PR middle-end/57344
	* expmed.c (store_split_bit_field): If op0 is a REG or
	SUBREG of a REG, don't lower unit.  Handle unit not being
	always BITS_PER_WORD.

	* gcc.c-torture/execute/pr57344-1.c: New test.
	* gcc.c-torture/execute/pr57344-2.c: New test.
	* gcc.c-torture/execute/pr57344-3.c: New test.
	* gcc.c-torture/execute/pr57344-4.c: New test.

From-SVN: r199238
---
 gcc/ChangeLog                                 |  7 ++++
 gcc/expmed.c                                  | 18 +++++++----
 gcc/testsuite/ChangeLog                       |  8 +++++
 .../gcc.c-torture/execute/pr57344-1.c         | 32 +++++++++++++++++++
 .../gcc.c-torture/execute/pr57344-2.c         | 32 +++++++++++++++++++
 .../gcc.c-torture/execute/pr57344-3.c         | 28 ++++++++++++++++
 .../gcc.c-torture/execute/pr57344-4.c         | 28 ++++++++++++++++
 7 files changed, 147 insertions(+), 6 deletions(-)
 create mode 100644 gcc/testsuite/gcc.c-torture/execute/pr57344-1.c
 create mode 100644 gcc/testsuite/gcc.c-torture/execute/pr57344-2.c
 create mode 100644 gcc/testsuite/gcc.c-torture/execute/pr57344-3.c
 create mode 100644 gcc/testsuite/gcc.c-torture/execute/pr57344-4.c

diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 503215a741e5..a4bf07a1f92a 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,10 @@
+2013-05-23  Jakub Jelinek  <jakub@redhat.com>
+
+	PR middle-end/57344
+	* expmed.c (store_split_bit_field): If op0 is a REG or
+	SUBREG of a REG, don't lower unit.  Handle unit not being
+	always BITS_PER_WORD.
+
 2013-05-23  Richard Biener  <rguenther@suse.de>
 
 	PR rtl-optimization/57341
diff --git a/gcc/expmed.c b/gcc/expmed.c
index c85e68cf617a..daadd3def5d0 100644
--- a/gcc/expmed.c
+++ b/gcc/expmed.c
@@ -1094,10 +1094,14 @@ store_split_bit_field (rtx op0, unsigned HOST_WIDE_INT bitsize,
       thispos = (bitpos + bitsdone) % unit;
 
       /* When region of bytes we can touch is restricted, decrease
-	 UNIT close to the end of the region as needed.  */
+	 UNIT close to the end of the region as needed.  If op0 is a REG
+	 or SUBREG of REG, don't do this, as there can't be data races
+	 on a register and we can expand shorter code in some cases.  */
       if (bitregion_end
 	  && unit > BITS_PER_UNIT
-	  && bitpos + bitsdone - thispos + unit > bitregion_end + 1)
+	  && bitpos + bitsdone - thispos + unit > bitregion_end + 1
+	  && !REG_P (op0)
+	  && (GET_CODE (op0) != SUBREG || !REG_P (SUBREG_REG (op0))))
 	{
 	  unit = unit / 2;
 	  continue;
@@ -1147,14 +1151,15 @@ store_split_bit_field (rtx op0, unsigned HOST_WIDE_INT bitsize,
 	 the current word starting from the base register.  */
       if (GET_CODE (op0) == SUBREG)
 	{
-	  int word_offset = (SUBREG_BYTE (op0) / UNITS_PER_WORD) + offset;
+	  int word_offset = (SUBREG_BYTE (op0) / UNITS_PER_WORD)
+			    + (offset * unit / BITS_PER_WORD);
 	  enum machine_mode sub_mode = GET_MODE (SUBREG_REG (op0));
 	  if (sub_mode != BLKmode && GET_MODE_SIZE (sub_mode) < UNITS_PER_WORD)
 	    word = word_offset ? const0_rtx : op0;
 	  else
 	    word = operand_subword_force (SUBREG_REG (op0), word_offset,
 					  GET_MODE (SUBREG_REG (op0)));
-	  offset = 0;
+	  offset &= BITS_PER_WORD / unit - 1;
 	}
       else if (REG_P (op0))
 	{
@@ -1162,8 +1167,9 @@ store_split_bit_field (rtx op0, unsigned HOST_WIDE_INT bitsize,
 	  if (op0_mode != BLKmode && GET_MODE_SIZE (op0_mode) < UNITS_PER_WORD)
 	    word = offset ? const0_rtx : op0;
 	  else
-	    word = operand_subword_force (op0, offset, GET_MODE (op0));
-	  offset = 0;
+	    word = operand_subword_force (op0, offset * unit / BITS_PER_WORD,
+					  GET_MODE (op0));
+	  offset &= BITS_PER_WORD / unit - 1;
 	}
       else
 	word = op0;
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index d1c0dda31865..6485a5cfbe01 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,11 @@
+2013-05-23  Jakub Jelinek  <jakub@redhat.com>
+
+	PR middle-end/57344
+	* gcc.c-torture/execute/pr57344-1.c: New test.
+	* gcc.c-torture/execute/pr57344-2.c: New test.
+	* gcc.c-torture/execute/pr57344-3.c: New test.
+	* gcc.c-torture/execute/pr57344-4.c: New test.
+
 2013-05-23  Richard Biener  <rguenther@suse.de>
 
 	PR rtl-optimization/57341
diff --git a/gcc/testsuite/gcc.c-torture/execute/pr57344-1.c b/gcc/testsuite/gcc.c-torture/execute/pr57344-1.c
new file mode 100644
index 000000000000..66893269efa5
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/execute/pr57344-1.c
@@ -0,0 +1,32 @@
+/* PR middle-end/57344 */
+
+struct __attribute__((packed)) S
+{
+  int a : 11;
+#if __SIZEOF_INT__ * __CHAR_BIT__ >= 32
+  int b : 22;
+#else
+  int b : 13;
+#endif
+  char c;
+  int : 0;
+} s[2];
+int i;
+
+__attribute__((noinline, noclone)) void
+foo (int x)
+{
+  if (x != -3161)
+    __builtin_abort ();
+  asm volatile ("" : : : "memory");
+}
+
+int
+main ()
+{
+  struct S t = { 0, -3161L };
+  s[1] = t;
+  for (; i < 1; i++)
+    foo (s[1].b);
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.c-torture/execute/pr57344-2.c b/gcc/testsuite/gcc.c-torture/execute/pr57344-2.c
new file mode 100644
index 000000000000..9bf60cab8f05
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/execute/pr57344-2.c
@@ -0,0 +1,32 @@
+/* PR middle-end/57344 */
+
+struct __attribute__((packed)) S
+{
+  int a : 27;
+#if __SIZEOF_INT__ * __CHAR_BIT__ >= 32
+  int b : 22;
+#else
+  int b : 13;
+#endif
+  char c;
+  int : 0;
+} s[2];
+int i;
+
+__attribute__((noinline, noclone)) void
+foo (int x)
+{
+  if (x != -3161)
+    __builtin_abort ();
+  asm volatile ("" : : : "memory");
+}
+
+int
+main ()
+{
+  struct S t = { 0, -3161L };
+  s[1] = t;
+  for (; i < 1; i++)
+    foo (s[1].b);
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.c-torture/execute/pr57344-3.c b/gcc/testsuite/gcc.c-torture/execute/pr57344-3.c
new file mode 100644
index 000000000000..f9daea6d3a93
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/execute/pr57344-3.c
@@ -0,0 +1,28 @@
+/* PR middle-end/57344 */
+
+struct __attribute__((packed)) S
+{
+  long long int a : 43;
+  long long int b : 22;
+  char c;
+  long long int : 0;
+} s[2];
+int i;
+
+__attribute__((noinline, noclone)) void
+foo (long long int x)
+{
+  if (x != -3161LL)
+    __builtin_abort ();
+  asm volatile ("" : : : "memory");
+}
+
+int
+main ()
+{
+  struct S t = { 0, -3161LL };
+  s[1] = t;
+  for (; i < 1; i++)
+    foo (s[1].b);
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.c-torture/execute/pr57344-4.c b/gcc/testsuite/gcc.c-torture/execute/pr57344-4.c
new file mode 100644
index 000000000000..0b2bf152490f
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/execute/pr57344-4.c
@@ -0,0 +1,28 @@
+/* PR middle-end/57344 */
+
+struct __attribute__((packed)) S
+{
+  long long int a : 59;
+  long long int b : 54;
+  char c;
+  long long int : 0;
+} s[2];
+int i;
+
+__attribute__((noinline, noclone)) void
+foo (long long int x)
+{
+  if (x != -1220975898975746LL)
+    __builtin_abort ();
+  asm volatile ("" : : : "memory");
+}
+
+int
+main ()
+{
+  struct S t = { 0, -1220975898975746LL };
+  s[1] = t;
+  for (; i < 1; i++)
+    foo (s[1].b);
+  return 0;
+}
-- 
GitLab