@@ -31,6 +31,7 @@ extern int arm_no_early_alu_shift_dep (rtx, rtx);
extern int arm_no_early_alu_shift_value_dep (rtx, rtx);
extern int arm_no_early_mul_dep (rtx, rtx);
extern int arm_no_early_store_addr_dep (rtx, rtx);
+extern bool arm_rtx_shift_left_p (rtx);
/* RTX cost table definitions. These are used when tuning for speed rather
than for size and should reflect the _additional_ cost over the cost
@@ -40,7 +40,7 @@ typedef struct
/* Return TRUE if X is either an arithmetic shift left, or
is a multiplication by a power of two. */
-static bool
+bool
arm_rtx_shift_left_p (rtx x)
{
enum rtx_code code = GET_CODE (x);
@@ -9203,6 +9203,29 @@ arm_new_rtx_costs (rtx x, enum rtx_code code, enum rtx_code outer_code,
*cost = LIBCALL_COST (2);
return false;
+#define HANDLE_NARROW_SHIFT_ARITH(OP, IDX) \
+ do \
+ { \
+ shift_op = shifter_op_p (XEXP (x, IDX), &shift_reg); \
+ if (shift_op != NULL \
+ && arm_rtx_shift_left_p (XEXP (x, IDX))) \
+ { \
+ if (shift_reg) \
+ { \
+ if (speed_p) \
+ *cost += extra_cost->alu.arith_shift_reg; \
+ *cost += rtx_cost (shift_reg, ASHIFT, 1, speed_p); \
+ } \
+ else if (speed_p) \
+ *cost += extra_cost->alu.arith_shift; \
+ \
+ *cost += (rtx_cost (shift_op, ASHIFT, 0, speed_p) \
+ + rtx_cost (XEXP (x, 1 - IDX), \
+ OP, 1, speed_p)); \
+ return true; \
+ } \
+ } \
+ while (0);
case MINUS:
if (TARGET_HARD_FLOAT && GET_MODE_CLASS (mode) == MODE_FLOAT
&& (mode == SFmode || !TARGET_VFP_SINGLE))
@@ -9309,6 +9332,15 @@ arm_new_rtx_costs (rtx x, enum rtx_code code, enum rtx_code outer_code,
if (GET_MODE_CLASS (mode) == MODE_INT
&& GET_MODE_SIZE (mode) < 4)
{
+ rtx shift_op, shift_reg;
+ shift_reg = NULL;
+
+ /* We check both sides of the MINUS for shifter operands since,
+ unlike PLUS, it's not commutative. */
+
+ HANDLE_NARROW_SHIFT_ARITH (MINUS, 0)
+ HANDLE_NARROW_SHIFT_ARITH (MINUS, 1)
+
/* Slightly disparage, as we might need to widen the result. */
*cost = 1 + COSTS_N_INSNS (1);
if (speed_p)
@@ -9408,11 +9440,20 @@ arm_new_rtx_costs (rtx x, enum rtx_code code, enum rtx_code outer_code,
return false;
}
+ /* Narrow modes can be synthesized in SImode, but the range
+ of useful sub-operations is limited. Check for shift operations
+ on one of the operands. Only left shifts can be used in the
+ narrow modes. */
if (GET_MODE_CLASS (mode) == MODE_INT
&& GET_MODE_SIZE (mode) < 4)
{
- /* Narrow modes can be synthesized in SImode, but the range
- of useful sub-operations is limited. */
+ rtx shift_op, shift_reg;
+ shift_reg = NULL;
+
+ HANDLE_NARROW_SHIFT_ARITH (PLUS, 0)
+
+#undef HANDLE_NARROW_SHIFT_ARITH
+
if (CONST_INT_P (XEXP (x, 1)))
{
int insns = arm_gen_constant (PLUS, SImode, NULL_RTX,