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]

[PATCH] Fix PR middle-end/14997


Hi,

This is an old PR about an ICE on 32-bit big-endian targets with a 
pathological unchecked conversion in Ada.  The testcase is:

with Interfaces.C; use Interfaces.C;
with Ada.Unchecked_Conversion;

procedure P is

   subtype C_ULong    is Interfaces.C.unsigned_long;
   type    C_Chtype   is new C_ULong;

   type Character_Attribute_Set is array (0 .. 47) of Boolean;
   pragma Pack (Character_Attribute_Set);
   
   type Attributed_Character is record
      Attr  : Character_Attribute_Set;
      Color : Character;
      Ch    : Character;
   end record;
   pragma Convention (C, Attributed_Character);
   
   for Attributed_Character use record
      Attr  at 0 range  0 .. 47;
      Color at 0 range 48 .. 55;
      Ch    at 0 range 56 .. 63;
   end record;
   for Attributed_Character'Size use 64;
   
   function Chtype_To_AttrChar is new
     Ada.Unchecked_Conversion (C_Chtype, Attributed_Character);
   
   X : constant C_Chtype := 0;
   Y : Character_Attribute_Set;
begin
   Y := Chtype_To_AttrChar (X).Attr;
end; 

The code attempts to extract a 48-bit member from a 32-bit constant after a 
unchecked conversion to a 64-bit record type.  This is translated into a 
COMPONENT_REF of VIEW_CONVERT_EXPR of a VAR_DECL.

The problem occurs in the normal_inner_ref RTL expander: we end up with 
bitsize=48 and a SImode REG as op0, so extract_bit_field is invoked and gets 
confused (str_rtx = reg:SI, bitsize = 48).

It turns out that the normal_inner_ref expander already contains code to deal 
with these pathological cases and forces the object to memory:

	/* If this is a constant, put it into a register if it is a
	   legitimate constant and OFFSET is 0 and memory if it isn't.  */
	if (CONSTANT_P (op0))
	  {
	    enum machine_mode mode = TYPE_MODE (TREE_TYPE (tem));
	    if (mode != BLKmode && LEGITIMATE_CONSTANT_P (op0)
		&& offset == 0)
	      op0 = force_reg (mode, op0);
	    else
	      op0 = validize_mem (force_const_mem (mode, op0));
	  }

 	/* Otherwise, if this object not in memory and we either have an
 	   offset or a BLKmode result, put it there.  This case can't occur in
 	   C, but can in Ada if we have unchecked conversion of an expression
 	   from a scalar type to an array or record type or for an
 	   ARRAY_RANGE_REF whose type is BLKmode.  */
	else if (!MEM_P (op0)
		 && (offset != 0
		     || (code == ARRAY_RANGE_REF && mode == BLKmode)))
	  {
	    tree nt = build_qualified_type (TREE_TYPE (tem),
					    (TYPE_QUALS (TREE_TYPE (tem))
					     | TYPE_QUAL_CONST));
	    rtx memloc = assign_temp (nt, 1, 1, 1);

	    emit_move_insn (memloc, op0);
	    op0 = memloc;
	  }

However it is predicated on (offset != 0) and the condition is false with the 
testcase.

The proposed fix is to add a condition on bitsize and bitpos to the predicate, 
essentially (bitpos + bitsize > GET_MODE_BITSIZE (mode)).  While a condition 
on bitsize in the second branch of the construct would be sufficient for the 
testcase, Olivier and I have run into slight variations that require the full 
change.


2005-09-06  Olivier Hainque  <hainque@adacore.com>
            Eric Botcazou  <ebotcazou@adacore.com>

	PR middle-end/14997
	* expr.c (expand_expr_real) <normal_inner_ref>: Force op0 to mem
	when we would be extracting outside its bit span (bitpos+bitsize
	larger than its mode), possible with some VIEW_CONVERT_EXPRs from
	Ada unchecked conversions.


-- 
Eric Botcazou
Index: expr.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/expr.c,v
retrieving revision 1.814
diff -u -p -r1.814 expr.c
--- expr.c	1 Sep 2005 01:06:45 -0000	1.814
+++ expr.c	6 Sep 2005 08:16:47 -0000
@@ -7178,25 +7178,30 @@ expand_expr_real_1 (tree exp, rtx target
 			  || modifier == EXPAND_STACK_PARM)
 			 ? modifier : EXPAND_NORMAL);
 
-	/* If this is a constant, put it into a register if it is a
-	   legitimate constant and OFFSET is 0 and memory if it isn't.  */
+	/* If this is a constant, put it into a register if it is a legitimate
+	   constant, OFFSET is 0, and we won't try to extract outside the
+	   register (in case we were passed a partially uninitialized object
+	   or a view_conversion to a larger size).  Force the constant to
+	   memory otherwise.  */
 	if (CONSTANT_P (op0))
 	  {
 	    enum machine_mode mode = TYPE_MODE (TREE_TYPE (tem));
 	    if (mode != BLKmode && LEGITIMATE_CONSTANT_P (op0)
-		&& offset == 0)
+		&& offset == 0
+		&& bitpos + bitsize <= GET_MODE_BITSIZE (mode))
 	      op0 = force_reg (mode, op0);
 	    else
 	      op0 = validize_mem (force_const_mem (mode, op0));
 	  }
 
- 	/* Otherwise, if this object not in memory and we either have an
- 	   offset or a BLKmode result, put it there.  This case can't occur in
- 	   C, but can in Ada if we have unchecked conversion of an expression
- 	   from a scalar type to an array or record type or for an
- 	   ARRAY_RANGE_REF whose type is BLKmode.  */
+	/* Otherwise, if this object not in memory and we either have an
+	   offset, a BLKmode result, or a reference outside the object, put it
+	   there.  Such cases can occur in Ada if we have unchecked conversion
+	   of an expression from a scalar type to an array or record type or
+	   for an ARRAY_RANGE_REF whose type is BLKmode.  */
 	else if (!MEM_P (op0)
 		 && (offset != 0
+		     || (bitpos + bitsize > GET_MODE_BITSIZE (GET_MODE (op0)))
 		     || (code == ARRAY_RANGE_REF && mode == BLKmode)))
 	  {
 	    tree nt = build_qualified_type (TREE_TYPE (tem),

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