* config/arm/arm.c (thumb2_size_rtx_costs): New. (arm_rtx_costs): Call above for Thumb-2. 2010-07-26 Julian Brown Merge from Sourcery G++ 4.4: 2010-02-23 Julian Brown 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,