From 1d0bafd9b0e507476c84a95d5d0f5d82ff7100f3 Mon Sep 17 00:00:00 2001
From: Eric Botcazou <ebotcazou@adacore.com>
Date: Tue, 3 Apr 2012 17:59:07 +0000
Subject: [PATCH] expr.c (get_bit_range): Add OFFSET parameter and adjust
 BITPOS.

	* expr.c (get_bit_range): Add OFFSET parameter and adjust BITPOS.
	Change type of BITOFFSET to signed.  Make sure the lower bound of
	the computed range is non-negative by adjusting OFFSET and BITPOS.
	(expand_assignment): Adjust call to get_bit_range.

From-SVN: r186110
---
 gcc/ChangeLog                        |  7 ++++++
 gcc/expr.c                           | 35 ++++++++++++++++++++++------
 gcc/testsuite/ChangeLog              |  5 ++++
 gcc/testsuite/gnat.dg/pack18.adb     | 12 ++++++++++
 gcc/testsuite/gnat.dg/pack18_pkg.ads | 21 +++++++++++++++++
 5 files changed, 73 insertions(+), 7 deletions(-)
 create mode 100644 gcc/testsuite/gnat.dg/pack18.adb
 create mode 100644 gcc/testsuite/gnat.dg/pack18_pkg.ads

diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index e1fc73b86fde..e20c0e437cd7 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,10 @@
+2012-04-03  Eric Botcazou  <ebotcazou@adacore.com>
+
+	* expr.c (get_bit_range): Add OFFSET parameter and adjust BITPOS.
+	Change type of BITOFFSET to signed.  Make sure the lower bound of
+	the computed range is non-negative by adjusting OFFSET and BITPOS.
+	(expand_assignment): Adjust call to get_bit_range.
+
 2012-04-03  Sandeep Kumar Singh  <Sandeep.Singh2@kpitcummins.com>
 
 	* h8300/h8300.c (h8300_current_function_monitor_function_p):
diff --git a/gcc/expr.c b/gcc/expr.c
index 56ec3fa8e2ae..6caee3be7ac1 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -4431,19 +4431,22 @@ optimize_bitfield_assignment_op (unsigned HOST_WIDE_INT bitsize,
 /* In the C++ memory model, consecutive bit fields in a structure are
    considered one memory location.
 
-   Given a COMPONENT_REF EXP at bit position BITPOS, this function
+   Given a COMPONENT_REF EXP at position (BITPOS, OFFSET), this function
    returns the bit range of consecutive bits in which this COMPONENT_REF
-   belongs in.  The values are returned in *BITSTART and *BITEND.
-   If the access does not need to be restricted 0 is returned in
+   belongs.  The values are returned in *BITSTART and *BITEND.  *BITPOS
+   and *OFFSET may be adjusted in the process.
+
+   If the access does not need to be restricted, 0 is returned in both
    *BITSTART and *BITEND.  */
 
 static void
 get_bit_range (unsigned HOST_WIDE_INT *bitstart,
 	       unsigned HOST_WIDE_INT *bitend,
 	       tree exp,
-	       HOST_WIDE_INT bitpos)
+	       HOST_WIDE_INT *bitpos,
+	       tree *offset)
 {
-  unsigned HOST_WIDE_INT bitoffset;
+  HOST_WIDE_INT bitoffset;
   tree field, repr;
 
   gcc_assert (TREE_CODE (exp) == COMPONENT_REF);
@@ -4490,7 +4493,25 @@ get_bit_range (unsigned HOST_WIDE_INT *bitstart,
   bitoffset += (tree_low_cst (DECL_FIELD_BIT_OFFSET (field), 1)
 		- tree_low_cst (DECL_FIELD_BIT_OFFSET (repr), 1));
 
-  *bitstart = bitpos - bitoffset;
+  /* If the adjustment is larger than bitpos, we would have a negative bit
+     position for the lower bound and this may wreak havoc later.  This can
+     occur only if we have a non-null offset, so adjust offset and bitpos
+     to make the lower bound non-negative.  */
+  if (bitoffset > *bitpos)
+    {
+      HOST_WIDE_INT adjust = bitoffset - *bitpos;
+
+      gcc_assert ((adjust % BITS_PER_UNIT) == 0);
+      gcc_assert (*offset != NULL_TREE);
+
+      *bitpos += adjust;
+      *offset
+	= size_binop (MINUS_EXPR, *offset, size_int (adjust / BITS_PER_UNIT));
+      *bitstart = 0;
+    }
+  else
+    *bitstart = *bitpos - bitoffset;
+
   *bitend = *bitstart + tree_low_cst (DECL_SIZE (repr), 1) - 1;
 }
 
@@ -4595,7 +4616,7 @@ expand_assignment (tree to, tree from, bool nontemporal)
 
       if (TREE_CODE (to) == COMPONENT_REF
 	  && DECL_BIT_FIELD_TYPE (TREE_OPERAND (to, 1)))
-	get_bit_range (&bitregion_start, &bitregion_end, to, bitpos);
+	get_bit_range (&bitregion_start, &bitregion_end, to, &bitpos, &offset);
 
       /* If we are going to use store_bit_field and extract_bit_field,
 	 make sure to_rtx will be safe for multiple use.  */
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index c757d42d177d..ca5c35adce8d 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,8 @@
+2012-04-03  Eric Botcazou  <ebotcazou@adacore.com>
+
+	* gnat.dg/pack18.adb: New test.
+	* gnat.dg/pack18_pkg.ads: New helper.
+
 2012-04-03  Richard Guenther  <rguenther@suse.de>
 
 	PR tree-optimization/52808
diff --git a/gcc/testsuite/gnat.dg/pack18.adb b/gcc/testsuite/gnat.dg/pack18.adb
new file mode 100644
index 000000000000..857a6f9787e2
--- /dev/null
+++ b/gcc/testsuite/gnat.dg/pack18.adb
@@ -0,0 +1,12 @@
+-- { dg-do run }
+
+with Pack18_Pkg; use Pack18_Pkg;
+
+procedure Pack18 is
+   use Pack18_Pkg.Attributes_Tables;
+   Table : Instance;
+begin
+   Init (Table);
+   Set_Last (Table, 1);
+   Table.Table (Last (Table)).N := 0;
+end;
diff --git a/gcc/testsuite/gnat.dg/pack18_pkg.ads b/gcc/testsuite/gnat.dg/pack18_pkg.ads
new file mode 100644
index 000000000000..2b63fbab0638
--- /dev/null
+++ b/gcc/testsuite/gnat.dg/pack18_pkg.ads
@@ -0,0 +1,21 @@
+with GNAT.Dynamic_Tables;
+
+package Pack18_Pkg is
+
+   type String_Access is access String;
+
+   type Rec is record
+      S : String_Access;
+      B : Boolean;
+      N : Natural;
+   end record;
+   pragma Pack (Rec);
+
+   package Attributes_Tables is new GNAT.Dynamic_Tables
+     (Table_Component_Type => Rec,
+      Table_Index_Type     => Natural,
+      Table_Low_Bound      => 1,
+      Table_Initial        => 200,
+      Table_Increment      => 200);
+
+end Pack18_Pkg;
-- 
GitLab