fix expansion of addr_expr

Richard Henderson rth@redhat.com
Mon Aug 30 19:05:00 GMT 2004


In testing my &a.b non-expansion patch with c++, I found several 
cases in the c++ testsuite for which expand_expr would produce
completely incorrect results.

I believe this to be primarily due to the block that tries to be
too smart and copy the object to a new stack slot and return the
address of the stack slot.  To my mind, this is insane.

I know that this is intended to support Ada records with packed
fields.  I think the Ada front end is going to have to handle this
in the gimplifier, introducing the new temporary there rather than
in the rtl expander.

Tested on i686-linux and committed.


r~



        * expr.c (expand_expr_addr_expr): New.
        (expand_expr_real_1) <case ADDR_EXPR>: Use it.

Index: gcc/expr.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/expr.c,v
retrieving revision 1.711
diff -u -p -r1.711 expr.c
--- gcc/expr.c	26 Aug 2004 00:24:33 -0000	1.711
+++ gcc/expr.c	30 Aug 2004 18:02:52 -0000
@@ -6063,6 +6063,142 @@ expand_operands (tree exp0, tree exp1, r
 }
 
 
+/* A subroutine of expand_expr.  Evaluate the address of EXP.
+   The TARGET, TMODE and MODIFIER arguments are as for expand_expr.  */
+
+static rtx
+expand_expr_addr_expr (tree exp, rtx target, enum machine_mode tmode,
+		       enum expand_modifier modifier)
+{
+  rtx result, subtarget;
+  tree inner, offset;
+  HOST_WIDE_INT bitsize, bitpos;
+  int volatilep, unsignedp;
+  enum machine_mode mode1;
+
+  /* If we are taking the address of a constant and are at the top level,
+     we have to use output_constant_def since we can't call force_const_mem
+     at top level.  */
+  /* ??? This should be considered a front-end bug.  We should not be
+     generating ADDR_EXPR of something that isn't an LVALUE.  The only
+     exception here is STRING_CST.  */
+  if (TREE_CODE (exp) == CONSTRUCTOR
+      || TREE_CODE_CLASS (TREE_CODE (exp)) == 'c')
+    return XEXP (output_constant_def (exp, 0), 0);
+
+  /* Everything must be something allowed by is_gimple_addressable.  */
+  switch (TREE_CODE (exp))
+    {
+    case INDIRECT_REF:
+      /* This case will happen via recursion for &a->b.  */
+      return expand_expr (TREE_OPERAND (exp, 0), target, tmode, EXPAND_NORMAL);
+
+    case CONST_DECL:
+      /* Recurse and make the output_constant_def clause above handle this.  */
+      return expand_expr_addr_expr (DECL_INITIAL (exp), target,
+				    tmode, modifier);
+
+    case REALPART_EXPR:
+      /* The real part of the complex number is always first, therefore
+	 the address is the same as the address of the parent object.  */
+      offset = 0;
+      bitpos = 0;
+      inner = TREE_OPERAND (exp, 0);
+      break;
+
+    case IMAGPART_EXPR:
+      /* The imaginary part of the complex number is always second.
+	 The expresion is therefore always offset by the size of the
+	 scalar type.  */
+      offset = 0;
+      bitpos = GET_MODE_BITSIZE (TYPE_MODE (TREE_TYPE (exp)));
+      inner = TREE_OPERAND (exp, 0);
+      break;
+
+    default:
+      /* If the object is a DECL, then expand it for its rtl.  Don't bypass
+	 expand_expr, as that can have various side effects; LABEL_DECLs for
+	 example, may not have their DECL_RTL set yet.  Assume language
+	 specific tree nodes can be expanded in some interesting way.  */
+      if (DECL_P (exp)
+	  || TREE_CODE (exp) >= LAST_AND_UNUSED_TREE_CODE)
+	{
+	  result = expand_expr (exp, target, tmode,
+				modifier == EXPAND_INITIALIZER
+				? EXPAND_INITIALIZER : EXPAND_CONST_ADDRESS);
+
+	  /* If the DECL isn't in memory, then the DECL wasn't properly
+	     marked TREE_ADDRESSABLE, which will be either a front-end
+	     or a tree optimizer bug.  */
+	  if (GET_CODE (result) != MEM)
+	    abort ();
+	  result = XEXP (result, 0);
+
+	  /* ??? Is this needed anymore?  */
+	  if (!TREE_USED (exp) == 0)
+	    {
+	      assemble_external (exp);
+	      TREE_USED (exp) = 1;
+	    }
+
+	  if (modifier != EXPAND_INITIALIZER
+	      && modifier != EXPAND_CONST_ADDRESS)
+	    result = force_operand (result, target);
+	  return result;
+	}
+
+      inner = get_inner_reference (exp, &bitsize, &bitpos, &offset,
+				   &mode1, &unsignedp, &volatilep);
+      break;
+    }
+
+  /* We must have made progress.  */
+  if (inner == exp)
+    abort ();
+
+  subtarget = offset || bitpos ? NULL_RTX : target;
+  result = expand_expr_addr_expr (inner, subtarget, tmode, modifier);
+
+  if (tmode == VOIDmode)
+    {
+      tmode = GET_MODE (result);
+      if (tmode == VOIDmode)
+	tmode = Pmode;
+    }
+
+  if (offset)
+    {
+      rtx tmp;
+
+      if (modifier != EXPAND_NORMAL)
+	result = force_operand (result, NULL);
+      tmp = expand_expr (offset, NULL, tmode, EXPAND_NORMAL);
+
+      if (modifier == EXPAND_SUM)
+	result = gen_rtx_PLUS (tmode, result, tmp);
+      else
+	{
+	  subtarget = bitpos ? NULL_RTX : target;
+	  result = expand_simple_binop (tmode, PLUS, result, tmp, subtarget,
+					1, OPTAB_LIB_WIDEN);
+	}
+    }
+
+  if (bitpos)
+    {
+      /* Someone beforehand should have rejected taking the address
+	 of such an object.  */
+      if (bitpos % BITS_PER_UNIT != 0)
+	abort ();
+
+      result = plus_constant (result, bitpos / BITS_PER_UNIT);
+      if (modifier < EXPAND_SUM)
+	result = force_operand (result, target);
+    }
+
+  return result;
+}
+
 /* expand_expr: generate code for computing expression EXP.
    An rtx for the computed value is returned.  The value is never null.
    In the case of a void EXP, const0_rtx is returned.
@@ -7952,135 +8088,8 @@ expand_expr_real_1 (tree exp, rtx target
       return const0_rtx;
 
     case ADDR_EXPR:
-      if (modifier == EXPAND_STACK_PARM)
-	target = 0;
-      /* If we are taking the address of something erroneous, just
-	 return a zero.  */
-      if (TREE_CODE (TREE_OPERAND (exp, 0)) == ERROR_MARK)
-	return const0_rtx;
-      /* If we are taking the address of a constant and are at the
-	 top level, we have to use output_constant_def since we can't
-	 call force_const_mem at top level.  */
-      else if (cfun == 0
-	       && (TREE_CODE (TREE_OPERAND (exp, 0)) == CONSTRUCTOR
-		   || (TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND (exp, 0)))
-		       == 'c')))
-	op0 = XEXP (output_constant_def (TREE_OPERAND (exp, 0), 0), 0);
-      else
-	{
-	  /* We make sure to pass const0_rtx down if we came in with
-	     ignore set, to avoid doing the cleanups twice for something.  */
-	  op0 = expand_expr (TREE_OPERAND (exp, 0),
-			     ignore ? const0_rtx : NULL_RTX, VOIDmode,
-			     (modifier == EXPAND_INITIALIZER
-			      ? modifier : EXPAND_CONST_ADDRESS));
-
-	  /* If we are going to ignore the result, OP0 will have been set
-	     to const0_rtx, so just return it.  Don't get confused and
-	     think we are taking the address of the constant.  */
-	  if (ignore)
-	    return op0;
-
-	  /* We would like the object in memory.  If it is a constant, we can
-	     have it be statically allocated into memory.  For a non-constant,
-	     we need to allocate some memory and store the value into it.  */
-
-	  if (CONSTANT_P (op0))
-	    op0 = force_const_mem (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))),
-				   op0);
-	  else if (REG_P (op0) || GET_CODE (op0) == SUBREG
-		   || GET_CODE (op0) == CONCAT || GET_CODE (op0) == PARALLEL
-		   || GET_CODE (op0) == LO_SUM)
-	    {
-	      /* If this object is in a register, it can't be BLKmode.  */
-	      tree inner_type = TREE_TYPE (TREE_OPERAND (exp, 0));
-	      rtx memloc = assign_temp (inner_type, 1, 1, 1);
-
-	      if (GET_CODE (op0) == PARALLEL)
-	        /* Handle calls that pass values in multiple
-		   non-contiguous locations.  The Irix 6 ABI has examples
-		   of this.  */
-		emit_group_store (memloc, op0, inner_type,
-				  int_size_in_bytes (inner_type));
-	      else
-		emit_move_insn (memloc, op0);
-
-	      op0 = memloc;
-	    }
-
-	  if (!MEM_P (op0))
-	    abort ();
-
-	  mark_temp_addr_taken (op0);
-	  if (modifier == EXPAND_SUM || modifier == EXPAND_INITIALIZER)
-	    {
-	      op0 = XEXP (op0, 0);
-	      if (GET_MODE (op0) == Pmode && mode == ptr_mode)
-		op0 = convert_memory_address (ptr_mode, op0);
-	      return op0;
-	    }
-
-	  /* If OP0 is not aligned as least as much as the type requires, we
-	     need to make a temporary, copy OP0 to it, and take the address of
-	     the temporary.  We want to use the alignment of the type, not of
-	     the operand.  Note that this is incorrect for FUNCTION_TYPE, but
-	     the test for BLKmode means that can't happen.  The test for
-	     BLKmode is because we never make mis-aligned MEMs with
-	     non-BLKmode.
-
-	     We don't need to do this at all if the machine doesn't have
-	     strict alignment.  */
-	  if (STRICT_ALIGNMENT && GET_MODE (op0) == BLKmode
-	      && (TYPE_ALIGN (TREE_TYPE (TREE_OPERAND (exp, 0)))
-		  > MEM_ALIGN (op0))
-	      && MEM_ALIGN (op0) < BIGGEST_ALIGNMENT)
-	    {
-	      tree inner_type = TREE_TYPE (TREE_OPERAND (exp, 0));
-	      rtx new;
-
-	      if (TYPE_ALIGN_OK (inner_type))
-		abort ();
-
-	      if (TREE_ADDRESSABLE (inner_type))
-		{
-		  /* We can't make a bitwise copy of this object, so fail.  */
-		  error ("cannot take the address of an unaligned member");
-		  return const0_rtx;
-		}
-
-	      new = assign_stack_temp_for_type
-		(TYPE_MODE (inner_type),
-		 MEM_SIZE (op0) ? INTVAL (MEM_SIZE (op0))
-		 : int_size_in_bytes (inner_type),
-		 1, build_qualified_type (inner_type,
-					  (TYPE_QUALS (inner_type)
-					   | TYPE_QUAL_CONST)));
-
-	      emit_block_move (new, op0, expr_size (TREE_OPERAND (exp, 0)),
-			       (modifier == EXPAND_STACK_PARM
-				? BLOCK_OP_CALL_PARM : BLOCK_OP_NORMAL));
-
-	      op0 = new;
-	    }
-
-	  op0 = force_operand (XEXP (op0, 0), target);
-	}
-
-      if (flag_force_addr
-	  && !REG_P (op0)
-	  && modifier != EXPAND_CONST_ADDRESS
-	  && modifier != EXPAND_INITIALIZER
-	  && modifier != EXPAND_SUM)
-	op0 = force_reg (Pmode, op0);
-
-      if (REG_P (op0)
-	  && ! REG_USERVAR_P (op0))
-	mark_reg_pointer (op0, TYPE_ALIGN (TREE_TYPE (type)));
-
-      if (GET_MODE (op0) == Pmode && mode == ptr_mode)
-	op0 = convert_memory_address (ptr_mode, op0);
-
-      return op0;
+      return expand_expr_addr_expr (TREE_OPERAND (exp, 0), target,
+				    tmode, modifier);
 
     /* COMPLEX type for Extended Pascal & Fortran  */
     case COMPLEX_EXPR:



More information about the Gcc-patches mailing list