	* config/arm/arm.c (thumb2_size_rtx_costs): New.
	(arm_rtx_costs): Call above for Thumb-2.

2010-07-26  Julian Brown  <julian@codesourcery.com>

	Merge from Sourcery G++ 4.4:

	2010-02-23  Julian Brown  <julian@codesourcery.com>

	gcc/
 	* calls.c (precompute_register_parameters): Avoid generating a
 	register move if optimizing for size.
 

=== modified file 'gcc/config/arm/arm.c'
--- old/gcc/config/arm/arm.c	2010-08-13 10:43:42 +0000
+++ new/gcc/config/arm/arm.c	2010-08-13 10:55:28 +0000
@@ -141,6 +141,7 @@
 static bool arm_have_conditional_execution (void);
 static bool arm_rtx_costs_1 (rtx, enum rtx_code, int*, bool);
 static bool arm_size_rtx_costs (rtx, enum rtx_code, enum rtx_code, int *);
+static bool thumb2_size_rtx_costs (rtx, enum rtx_code, enum rtx_code, int *);
 static bool arm_slowmul_rtx_costs (rtx, enum rtx_code, enum rtx_code, int *, bool);
 static bool arm_fastmul_rtx_costs (rtx, enum rtx_code, enum rtx_code, int *, bool);
 static bool arm_xscale_rtx_costs (rtx, enum rtx_code, enum rtx_code, int *, bool);
@@ -7316,14 +7317,372 @@
     }
 }
 
+static bool
+thumb2_size_rtx_costs (rtx x, enum rtx_code code, enum rtx_code outer_code,
+		     int *total)
+{
+  /* Attempt to give a lower cost to RTXs which can optimistically be
+     represented as short insns, assuming that the right conditions will hold
+     later (e.g. low registers will be chosen if a short insn requires them).
+
+     Note that we don't make wide insns cost twice as much as narrow insns,
+     because we can't prove that a particular RTX will actually use a narrow
+     insn, because not enough information is available (e.g., we don't know
+     which hard registers pseudos will be assigned).  Consider these to be
+     "expected" sizes/weightings.
+
+     (COSTS_NARROW_INSNS has the same weight as COSTS_N_INSNS.)  */
+
+#define COSTS_NARROW_INSNS(N) ((N) * 4)
+#define COSTS_WIDE_INSNS(N) ((N) * 6)
+#define THUMB2_LIBCALL_COST COSTS_WIDE_INSNS (2)
+  enum machine_mode mode = GET_MODE (x);
+
+  switch (code)
+    {
+    case MEM:
+      if (REG_P (XEXP (x, 0)))
+	{
+	  /* Hopefully this will use a narrow ldm/stm insn.  */
+	  *total = COSTS_NARROW_INSNS (1);
+	  return true;
+	}
+      else if ((GET_CODE (XEXP (x, 0)) == SYMBOL_REF
+		&& CONSTANT_POOL_ADDRESS_P (XEXP (x, 0)))
+	       || reg_mentioned_p (virtual_stack_vars_rtx, XEXP (x, 0))
+	       || reg_mentioned_p (stack_pointer_rtx, XEXP (x, 0)))
+	{
+	  *total = COSTS_NARROW_INSNS (ARM_NUM_REGS (mode));
+	  return true;
+	}
+      else if (GET_CODE (XEXP (x, 0)) == PLUS)
+	{
+	  rtx plus = XEXP (x, 0);
+
+	  if (GET_CODE (XEXP (plus, 1)) == CONST_INT)
+	    {
+	      HOST_WIDE_INT cst = INTVAL (XEXP (plus, 1));
+
+	      if (cst >= 0 && cst < 256)
+		*total = COSTS_NARROW_INSNS (ARM_NUM_REGS (mode));
+	      else
+		*total = COSTS_WIDE_INSNS (ARM_NUM_REGS (mode));
+
+	      *total += rtx_cost (XEXP (plus, 0), code, false);
+
+	      return true;
+	    }
+	}
+
+      *total = COSTS_NARROW_INSNS (ARM_NUM_REGS (mode));
+      return false;
+
+    case DIV:
+    case MOD:
+    case UDIV:
+    case UMOD:
+      if (arm_arch_hwdiv)
+	*total = COSTS_WIDE_INSNS (1);
+      else
+	*total = THUMB2_LIBCALL_COST;
+      return false;
+
+    case ROTATE:
+      if (mode == SImode && REG_P (XEXP (x, 1)))
+	{
+	  *total = COSTS_WIDE_INSNS (1) + COSTS_NARROW_INSNS (1)
+		   + rtx_cost (XEXP (x, 0), code, false);
+	  return true;
+	}
+      /* Fall through */
+
+    case ASHIFT:
+    case LSHIFTRT:
+    case ASHIFTRT:
+      if (mode == DImode && GET_CODE (XEXP (x, 1)) == CONST_INT)
+	{
+	  *total = COSTS_WIDE_INSNS (3) + rtx_cost (XEXP (x, 0), code, false);
+	  return true;
+	}
+      else if (mode == SImode)
+	{
+	  *total = COSTS_NARROW_INSNS (1);
+	  return false;
+	}
+
+      /* Needs a libcall.  */
+      *total = THUMB2_LIBCALL_COST;
+      return false;
+
+    case ROTATERT:
+      if (mode == DImode && GET_CODE (XEXP (x, 1)) == CONST_INT)
+	{
+	  *total = COSTS_WIDE_INSNS (3) + rtx_cost (XEXP (x, 0), code, false);
+	  return true;
+	}
+      else if (mode == SImode)
+	{
+	  if (GET_CODE (XEXP (x, 1)) == CONST_INT)
+	    *total = COSTS_WIDE_INSNS (1) + rtx_cost (XEXP (x, 0), code, false);
+	  else
+	    *total = COSTS_NARROW_INSNS (1)
+		     + rtx_cost (XEXP (x, 0), code, false);
+	  return true;
+	}
+
+      /* Needs a libcall.  */
+      *total = THUMB2_LIBCALL_COST;
+      return false;
+
+    case MINUS:
+      if (TARGET_HARD_FLOAT && GET_MODE_CLASS (mode) == MODE_FLOAT
+	  && (mode == SFmode || !TARGET_VFP_SINGLE))
+	{
+	  *total = COSTS_WIDE_INSNS (1);
+	  return false;
+	}
+
+      if (mode == SImode)
+	{
+	  enum rtx_code subcode0 = GET_CODE (XEXP (x, 0));
+	  enum rtx_code subcode1 = GET_CODE (XEXP (x, 1));
+
+	  if (subcode0 == ROTATE || subcode0 == ROTATERT || subcode0 == ASHIFT
+	      || subcode0 == LSHIFTRT || subcode0 == ASHIFTRT
+	      || subcode1 == ROTATE || subcode1 == ROTATERT
+	      || subcode1 == ASHIFT || subcode1 == LSHIFTRT
+	      || subcode1 == ASHIFTRT)
+	    {
+	      /* It's just the cost of the two operands.  */
+	      *total = 0;
+	      return false;
+	    }
+
+	  if (subcode1 == CONST_INT)
+	    {
+	      HOST_WIDE_INT cst = INTVAL (XEXP (x, 1));
+
+	      if (cst >= 0 && cst < 256)
+		*total = COSTS_NARROW_INSNS (1);
+	      else
+		*total = COSTS_WIDE_INSNS (1);
+
+	      *total += rtx_cost (XEXP (x, 0), code, false);
+
+	      return true;
+	    }
+
+	  *total = COSTS_NARROW_INSNS (1);
+	  return false;
+	}
+
+      *total = COSTS_WIDE_INSNS (ARM_NUM_REGS (mode));
+      return false;
+
+    case PLUS:
+      if (TARGET_HARD_FLOAT && GET_MODE_CLASS (mode) == MODE_FLOAT
+	  && (mode == SFmode || !TARGET_VFP_SINGLE))
+	{
+	  *total = COSTS_WIDE_INSNS (1);
+	  return false;
+	}
+
+      /* Fall through */
+    case AND: case XOR: case IOR:
+      if (mode == SImode)
+	{
+	  enum rtx_code subcode = GET_CODE (XEXP (x, 0));
+
+	  if (subcode == ROTATE || subcode == ROTATERT || subcode == ASHIFT
+	      || subcode == LSHIFTRT || subcode == ASHIFTRT
+	      || (code == AND && subcode == NOT))
+	    {
+	      /* It's just the cost of the two operands.  */
+	      *total = 0;
+	      return false;
+	    }
+
+	  if (code == PLUS && GET_CODE (XEXP (x, 1)) == CONST_INT)
+	    {
+	      HOST_WIDE_INT cst = INTVAL (XEXP (x, 1));
+
+	      if ((reg_mentioned_p (virtual_stack_vars_rtx, XEXP (x, 0))
+		   || reg_mentioned_p (stack_pointer_rtx, XEXP (x, 0)))
+		  && cst > -512 && cst < 1024)
+		/* Only approximately correct, depending on destination
+		   register.  */
+		*total = COSTS_NARROW_INSNS (1);
+	      else if (cst > -256 && cst < 256)
+		*total = COSTS_NARROW_INSNS (1);
+	      else
+		*total = COSTS_WIDE_INSNS (1);
+
+	      *total += rtx_cost (XEXP (x, 0), code, false);
+
+	      return true;
+	    }
+
+	  if (subcode == MULT
+	      && power_of_two_operand (XEXP (XEXP (x, 0), 1), mode))
+	    {
+	      *total = COSTS_WIDE_INSNS (1)
+		       + rtx_cost (XEXP (x, 1), code, false);
+	      return true;
+	    }
+	}
+
+      *total = COSTS_NARROW_INSNS (ARM_NUM_REGS (mode));
+      return false;
+
+    case MULT:
+      if (mode == SImode && GET_CODE (XEXP (x, 1)) != CONST_INT)
+	{
+	  /* Might be using muls.  */
+	  *total = COSTS_NARROW_INSNS (1);
+	  return false;
+	}
+      *total = COSTS_WIDE_INSNS (ARM_NUM_REGS (mode));
+      return false;
+
+    case NEG:
+      if (TARGET_HARD_FLOAT && GET_MODE_CLASS (mode) == MODE_FLOAT
+	  && (mode == SFmode || !TARGET_VFP_SINGLE))
+	{
+	  *total = COSTS_WIDE_INSNS (1);
+	  return false;
+	}
+
+      /* Fall through */
+    case NOT:
+      if (mode == SImode)
+	{
+	  *total = COSTS_NARROW_INSNS (1);
+	  return false;
+	}
+      *total = COSTS_WIDE_INSNS (ARM_NUM_REGS (mode));
+      return false;
+
+    case IF_THEN_ELSE:
+      *total = COSTS_NARROW_INSNS (1);
+      return false;
+
+    case COMPARE:
+      if (cc_register (XEXP (x, 0), VOIDmode))
+	*total = 0;
+      else
+	*total = COSTS_NARROW_INSNS (1);
+      return false;
+
+    case ABS:
+      if (TARGET_HARD_FLOAT && GET_MODE_CLASS (mode) == MODE_FLOAT
+	  && (mode == SFmode || !TARGET_VFP_SINGLE))
+	*total = COSTS_WIDE_INSNS (1);
+      else
+	*total = COSTS_NARROW_INSNS (ARM_NUM_REGS (mode)) * 2;
+      return false;
+
+    case SIGN_EXTEND:
+      if (GET_MODE_SIZE (mode) <= 4)
+	*total = GET_CODE (XEXP (x, 0)) == MEM ? 0 : COSTS_NARROW_INSNS (1);
+      else
+	*total = COSTS_NARROW_INSNS (1)
+		 + COSTS_WIDE_INSNS (ARM_NUM_REGS (mode));
+      return false;
+
+    case ZERO_EXTEND:
+      if (GET_MODE_SIZE (mode) > 4)
+	*total = COSTS_WIDE_INSNS (ARM_NUM_REGS (mode) - 1);
+      else if (GET_CODE (XEXP (x, 0)) == MEM)
+	*total = 0;
+      else
+	*total = COSTS_NARROW_INSNS (1);
+      return false;
+
+    case CONST_INT:
+      {
+	HOST_WIDE_INT cst = INTVAL (x);
+
+	switch (outer_code)
+	  {
+	  case PLUS:
+	    if (cst > -256 && cst < 256)
+	      *total = 0;
+	    else
+	      /* See note about optabs below.  */
+	      *total = COSTS_N_INSNS (1);
+	    return true;
+
+	  case MINUS:
+	  case COMPARE:
+	    if (cst >= 0 && cst < 256)
+	      *total = 0;
+	    else
+	      /* See note about optabs below.  */
+	      *total = COSTS_N_INSNS (1);
+	    return true;
+
+	  case ASHIFT:
+	  case ASHIFTRT:
+	  case LSHIFTRT:
+	    *total = 0;
+	    return true;
+
+	  default:
+	    /* Constants are compared explicitly against COSTS_N_INSNS (1) in
+	       optabs.c, creating an alternative, larger code sequence for more
+	       expensive constants).  So, it doesn't pay to make some constants
+	       cost more than this.  */
+	    *total = COSTS_N_INSNS (1);
+	  }
+	return true;
+      }
+
+    case CONST:
+    case LABEL_REF:
+    case SYMBOL_REF:
+      *total = COSTS_WIDE_INSNS (2);
+      return true;
+
+    case CONST_DOUBLE:
+      *total = COSTS_WIDE_INSNS (4);
+      return true;
+
+    case HIGH:
+    case LO_SUM:
+      /* We prefer constant pool entries to MOVW/MOVT pairs, so bump the
+       cost of these slightly.  */
+      *total = COSTS_WIDE_INSNS (1) + 1;
+      return true;
+
+    default:
+      if (mode != VOIDmode)
+	*total = COSTS_WIDE_INSNS (ARM_NUM_REGS (mode));
+      else
+	/* A guess (inherited from arm_size_rtx_costs).  */
+	*total = COSTS_WIDE_INSNS (4);
+      return false;
+    }
+
+  return true;
+#undef THUMB2_LIBCALL_COST
+#undef COSTS_WIDE_INSNS
+#undef COSTS_NARROW_INSNS
+}
+
 /* RTX costs when optimizing for size.  */
 static bool
 arm_rtx_costs (rtx x, int code, int outer_code, int *total,
 	       bool speed)
 {
   if (!speed)
-    return arm_size_rtx_costs (x, (enum rtx_code) code,
-			       (enum rtx_code) outer_code, total);
+    {
+      if (TARGET_THUMB2)
+	return thumb2_size_rtx_costs (x, (enum rtx_code) code,
+				      (enum rtx_code) outer_code, total);
+      else
+	return arm_size_rtx_costs (x, (enum rtx_code) code,
+				   (enum rtx_code) outer_code, total);
+    }
   else
     return all_cores[(int)arm_tune].rtx_costs (x, (enum rtx_code) code,
 					       (enum rtx_code) outer_code,

