This is the mail archive of the egcs-patches@egcs.cygnus.com mailing list for the EGCS project.


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

64x32 cross bugfix



When cross compiling from a 64bit host to a 32bit target it is possible to
get incorrect RTL for certain addition/subtractions.

Consider the following tree node:

 <minus_expr 0x837d438
    type <pointer_type 0x838a2d0
        type <integer_type 0x8386014 char allocated from permanent_obstack
            permanent type_6 QI
            size <integer_cst 0x83860b4 constant permanent 8>
            align 8 symtab 0 alias set -1 precision 8
            min <integer_cst 0x8386074 constant permanent -128>
            max <integer_cst 0x8386094 constant permanent 127>
            pointer_to_this <pointer_type 0x838a2d0> reference_to_this <referenc
e_type 0x84422ec>>
        allocated from permanent_obstack
        unsigned permanent SI
        size <integer_cst 0x8385eb8 constant permanent 32>
        align 32 symtab 0 alias set -1
        pointer_to_this <pointer_type 0x83d228c>>
    allocated from momentary_obstack
    constant
    arg 0 <nop_expr 0x837d3b8 type <pointer_type 0x838a2d0>
        allocated from momentary_obstack
        constant
        arg 0 <addr_expr 0x837d098 type <pointer_type 0x865d6a0>
            allocated from momentary_obstack
            constant arg 0 <var_decl 0x85ce498 a>>>
    arg 1 <integer_cst 0x837d418 type <pointer_type 0x838a2d0> constant 8>>


This should turn into something like:

(plus:SI (symbol_ref a) (const_int -8))

But instead we can get:

(plus;SI (symbol_ref a) (const_int 4294967288)

Which is wrong.

After several discussions with Jim we agreed that the tree shown above was
OK as were equivalent trees created during the tree->rtl process (we
create a new tree which is an PLUS_EXPR of the symbol and the negated
constant).

Further digging exposed the real problem.  When we expand sums we do
something like this (expr.c::expand_expr)

              op1 = expand_expr (TREE_OPERAND (exp, 1), subtarget, VOIDmode,
                                 EXPAND_SUM);
              op1 = plus_constant (op1, TREE_INT_CST_LOW (TREE_OPERAND (exp, 0))


Note the second line -- we take the low part of the constant directly out of
the tree node and pass it along to plus_constant.

This is wrong as it does not sign extend the constant to fill a HOST_WIDE_INT.
ie, TREE_INT_CST_LOW in this case will be 0x00000000fffffff8.  So the resulting
rtl will look like

(plus (op1) (const_int fffffff8))

Which loses.

The proper fix is to 1. make sure all bits outside of the mode of OP1 are
masked off.  2. sign extend the constant from the mode of OP1 to the width
of a HOST_WIDE_INT.

This is precisely what varasm::immed_double_const is designed to do, and
converting that code to use immed_double_const fixes the bug.


	* expr.c (expand_expr, case PLUS_EXPR): Pass constants through
	immed_double_const to ensure they are properly truncated then
	sign extended.

Index: expr.c
===================================================================
RCS file: /egcs/carton/cvsfiles/egcs/gcc/expr.c,v
retrieving revision 1.150
diff -c -r1.150 expr.c
*** expr.c	1999/06/30 23:06:18	1.150
--- expr.c	1999/07/12 20:33:38
***************
*** 6972,6980 ****
  	      && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
  	      && TREE_CONSTANT (TREE_OPERAND (exp, 1)))
  	    {
  	      op1 = expand_expr (TREE_OPERAND (exp, 1), subtarget, VOIDmode,
  				 EXPAND_SUM);
! 	      op1 = plus_constant (op1, TREE_INT_CST_LOW (TREE_OPERAND (exp, 0)));
  	      if (modifier != EXPAND_SUM && modifier != EXPAND_INITIALIZER)
  		op1 = force_operand (op1, target);
  	      return op1;
--- 6972,6990 ----
  	      && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
  	      && TREE_CONSTANT (TREE_OPERAND (exp, 1)))
  	    {
+ 	      rtx constant_part;
+ 
  	      op1 = expand_expr (TREE_OPERAND (exp, 1), subtarget, VOIDmode,
  				 EXPAND_SUM);
! 	      /* Use immed_double_const to ensure that the constant is
! 		 truncated according to the mode of OP1, then sign extended
! 		 to a HOST_WIDE_INT.  Using the constant directly can result
! 		 in non-canonical RTL in a 64x32 cross compile.  */
! 	      constant_part
! 		= immed_double_const (TREE_INT_CST_LOW (TREE_OPERAND (exp, 0)),
! 				      (HOST_WIDE_INT) 0,
! 				      GET_MODE (op1));
! 	      op1 = plus_constant (op1, XINT (constant_part, 0));
  	      if (modifier != EXPAND_SUM && modifier != EXPAND_INITIALIZER)
  		op1 = force_operand (op1, target);
  	      return op1;
***************
*** 6984,6989 ****
--- 6994,7001 ----
  		   && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_INT
  		   && TREE_CONSTANT (TREE_OPERAND (exp, 0)))
  	    {
+ 	      rtx constant_part;
+ 
  	      op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode,
  				 EXPAND_SUM);
  	      if (! CONSTANT_P (op0))
***************
*** 6996,7002 ****
  		    goto binop2;
  		  goto both_summands;
  		}
! 	      op0 = plus_constant (op0, TREE_INT_CST_LOW (TREE_OPERAND (exp, 1)));
  	      if (modifier != EXPAND_SUM && modifier != EXPAND_INITIALIZER)
  		op0 = force_operand (op0, target);
  	      return op0;
--- 7008,7022 ----
  		    goto binop2;
  		  goto both_summands;
  		}
! 	      /* Use immed_double_const to ensure that the constant is
! 		 truncated according to the mode of OP1, then sign extended
! 		 to a HOST_WIDE_INT.  Using the constant directly can result
! 		 in non-canonical RTL in a 64x32 cross compile.  */
! 	      constant_part
! 		= immed_double_const (TREE_INT_CST_LOW (TREE_OPERAND (exp, 1)),
! 				      (HOST_WIDE_INT) 0,
! 				      GET_MODE (op0));
! 	      op0 = plus_constant (op0, XINT (constant_part, 0));
  	      if (modifier != EXPAND_SUM && modifier != EXPAND_INITIALIZER)
  		op0 = force_operand (op0, target);
  	      return op0;



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