2010-07-02 Daniel Jacobowitz Julian Brown Sandra Loosemore gcc/ * config/arm/arm.c (arm_canonicalize_comparison): Canonicalize DImode comparisons. Adjust to take both operands. (arm_select_cc_mode): Handle DImode comparisons. (arm_gen_compare_reg): Generate a scratch register for DImode comparisons which require one. Use xor for Thumb equality checks. (arm_const_double_by_immediates): New. (arm_print_operand): Allow 'Q' and 'R' for constants. (get_arm_condition_code): Handle new CC_CZmode and CC_NCVmode. * config/arm/arm.h (CANONICALIZE_COMPARISON): Always use arm_canonicalize_comparison. * config/arm/arm-modes.def: Add CC_CZmode and CC_NCVmode. * config/arm/arm-protos.h (arm_canonicalize_comparison): Update prototype. (arm_const_double_by_immediates): Declare. * config/arm/constraints.md (Di): New constraint. * config/arm/predicates.md (arm_immediate_di_operand) (arm_di_operand, cmpdi_operand): New. * config/arm/arm.md (cbranchdi4): Handle non-Cirrus also. (*arm_cmpdi_insn, *arm_cmpdi_unsigned) (*arm_cmpdi_zero, *thumb_cmpdi_zero): New insns. (cstoredi4): Handle non-Cirrus also. gcc/testsuite/ * gcc.c-torture/execute/20100416-1.c: New test case. 2010-07-08 Sandra Loosemore Backport from upstream (originally from Sourcery G++ 4.4): 2010-07-02 Sandra Loosemore gcc/ === modified file 'gcc/config/arm/arm-modes.def' Index: gcc-4.5.3/gcc/config/arm/arm-modes.def =================================================================== --- gcc-4.5.3.orig/gcc/config/arm/arm-modes.def +++ gcc-4.5.3/gcc/config/arm/arm-modes.def @@ -35,10 +35,16 @@ ADJUST_FLOAT_FORMAT (HF, ((arm_fp16_form CC_NOOVmode should be used with SImode integer equalities. CC_Zmode should be used if only the Z flag is set correctly CC_Nmode should be used if only the N (sign) flag is set correctly + CC_CZmode should be used if only the C and Z flags are correct + (used for DImode unsigned comparisons). + CC_NCVmode should be used if only the N, C, and V flags are correct + (used for DImode signed comparisons). CCmode should be used otherwise. */ CC_MODE (CC_NOOV); CC_MODE (CC_Z); +CC_MODE (CC_CZ); +CC_MODE (CC_NCV); CC_MODE (CC_SWP); CC_MODE (CCFP); CC_MODE (CCFPE); Index: gcc-4.5.3/gcc/config/arm/arm-protos.h =================================================================== --- gcc-4.5.3.orig/gcc/config/arm/arm-protos.h +++ gcc-4.5.3/gcc/config/arm/arm-protos.h @@ -49,8 +49,7 @@ extern int arm_hard_regno_mode_ok (unsig extern int const_ok_for_arm (HOST_WIDE_INT); extern int arm_split_constant (RTX_CODE, enum machine_mode, rtx, HOST_WIDE_INT, rtx, rtx, int); -extern RTX_CODE arm_canonicalize_comparison (RTX_CODE, enum machine_mode, - rtx *); +extern RTX_CODE arm_canonicalize_comparison (RTX_CODE, rtx *, rtx *); extern int legitimate_pic_operand_p (rtx); extern rtx legitimize_pic_address (rtx, enum machine_mode, rtx); extern rtx legitimize_tls_address (rtx, rtx); @@ -116,6 +115,7 @@ extern void arm_reload_in_hi (rtx *); extern void arm_reload_out_hi (rtx *); extern int arm_const_double_inline_cost (rtx); extern bool arm_const_double_by_parts (rtx); +extern bool arm_const_double_by_immediates (rtx); extern const char *fp_immediate_constant (rtx); extern void arm_emit_call_insn (rtx, rtx); extern const char *output_call (rtx *); Index: gcc-4.5.3/gcc/config/arm/arm.c =================================================================== --- gcc-4.5.3.orig/gcc/config/arm/arm.c +++ gcc-4.5.3/gcc/config/arm/arm.c @@ -3191,13 +3191,82 @@ arm_gen_constant (enum rtx_code code, en immediate value easier to load. */ enum rtx_code -arm_canonicalize_comparison (enum rtx_code code, enum machine_mode mode, - rtx * op1) +arm_canonicalize_comparison (enum rtx_code code, rtx *op0, rtx *op1) { - unsigned HOST_WIDE_INT i = INTVAL (*op1); - unsigned HOST_WIDE_INT maxval; + enum machine_mode mode; + unsigned HOST_WIDE_INT i, maxval; + + mode = GET_MODE (*op0); + if (mode == VOIDmode) + mode = GET_MODE (*op1); + maxval = (((unsigned HOST_WIDE_INT) 1) << (GET_MODE_BITSIZE(mode) - 1)) - 1; + /* For DImode, we have GE/LT/GEU/LTU comparisons. In ARM mode + we can also use cmp/cmpeq for GTU/LEU. GT/LE must be either + reversed or (for constant OP1) adjusted to GE/LT. Similarly + for GTU/LEU in Thumb mode. */ + if (mode == DImode) + { + rtx tem; + + /* To keep things simple, always use the Cirrus cfcmp64 if it is + available. */ + if (TARGET_ARM && TARGET_HARD_FLOAT && TARGET_MAVERICK) + return code; + + if (code == GT || code == LE + || (!TARGET_ARM && (code == GTU || code == LEU))) + { + /* Missing comparison. First try to use an available + comparison. */ + if (GET_CODE (*op1) == CONST_INT) + { + i = INTVAL (*op1); + switch (code) + { + case GT: + case LE: + if (i != maxval + && arm_const_double_by_immediates (GEN_INT (i + 1))) + { + *op1 = GEN_INT (i + 1); + return code == GT ? GE : LT; + } + break; + case GTU: + case LEU: + if (i != ~((unsigned HOST_WIDE_INT) 0) + && arm_const_double_by_immediates (GEN_INT (i + 1))) + { + *op1 = GEN_INT (i + 1); + return code == GTU ? GEU : LTU; + } + break; + default: + gcc_unreachable (); + } + } + + /* If that did not work, reverse the condition. */ + tem = *op0; + *op0 = *op1; + *op1 = tem; + return swap_condition (code); + } + + return code; + } + + /* Comparisons smaller than DImode. Only adjust comparisons against + an out-of-range constant. */ + if (GET_CODE (*op1) != CONST_INT + || const_ok_for_arm (INTVAL (*op1)) + || const_ok_for_arm (- INTVAL (*op1))) + return code; + + i = INTVAL (*op1); + switch (code) { case EQ: @@ -9913,6 +9982,55 @@ arm_select_cc_mode (enum rtx_code op, rt && (rtx_equal_p (XEXP (x, 0), y) || rtx_equal_p (XEXP (x, 1), y))) return CC_Cmode; + if (GET_MODE (x) == DImode || GET_MODE (y) == DImode) + { + /* To keep things simple, always use the Cirrus cfcmp64 if it is + available. */ + if (TARGET_ARM && TARGET_HARD_FLOAT && TARGET_MAVERICK) + return CCmode; + + switch (op) + { + case EQ: + case NE: + /* A DImode comparison against zero can be implemented by + or'ing the two halves together. */ + if (y == const0_rtx) + return CC_Zmode; + + /* We can do an equality test in three Thumb instructions. */ + if (!TARGET_ARM) + return CC_Zmode; + + /* FALLTHROUGH */ + + case LTU: + case LEU: + case GTU: + case GEU: + /* DImode unsigned comparisons can be implemented by cmp + + cmpeq without a scratch register. Not worth doing in + Thumb-2. */ + if (TARGET_ARM) + return CC_CZmode; + + /* FALLTHROUGH */ + + case LT: + case LE: + case GT: + case GE: + /* DImode signed and unsigned comparisons can be implemented + by cmp + sbcs with a scratch register, but that does not + set the Z flag - we must reverse GT/LE/GTU/LEU. */ + gcc_assert (op != EQ && op != NE); + return CC_NCVmode; + + default: + gcc_unreachable (); + } + } + return CCmode; } @@ -9922,10 +10040,39 @@ arm_select_cc_mode (enum rtx_code op, rt rtx arm_gen_compare_reg (enum rtx_code code, rtx x, rtx y) { - enum machine_mode mode = SELECT_CC_MODE (code, x, y); - rtx cc_reg = gen_rtx_REG (mode, CC_REGNUM); + enum machine_mode mode; + rtx cc_reg; + int dimode_comparison = GET_MODE (x) == DImode || GET_MODE (y) == DImode; - emit_set_insn (cc_reg, gen_rtx_COMPARE (mode, x, y)); + /* We might have X as a constant, Y as a register because of the predicates + used for cmpdi. If so, force X to a register here. */ + if (dimode_comparison && !REG_P (x)) + x = force_reg (DImode, x); + + mode = SELECT_CC_MODE (code, x, y); + cc_reg = gen_rtx_REG (mode, CC_REGNUM); + + if (dimode_comparison + && !(TARGET_HARD_FLOAT && TARGET_MAVERICK) + && mode != CC_CZmode) + { + rtx clobber, set; + + /* To compare two non-zero values for equality, XOR them and + then compare against zero. Not used for ARM mode; there + CC_CZmode is cheaper. */ + if (mode == CC_Zmode && y != const0_rtx) + { + x = expand_binop (DImode, xor_optab, x, y, NULL_RTX, 0, OPTAB_WIDEN); + y = const0_rtx; + } + /* A scratch register is required. */ + clobber = gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (SImode)); + set = gen_rtx_SET (VOIDmode, cc_reg, gen_rtx_COMPARE (mode, x, y)); + emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, set, clobber))); + } + else + emit_set_insn (cc_reg, gen_rtx_COMPARE (mode, x, y)); return cc_reg; } @@ -11254,6 +11401,34 @@ arm_const_double_by_parts (rtx val) return false; } +/* Return true if it is possible to inline both the high and low parts + of a 64-bit constant into 32-bit data processing instructions. */ +bool +arm_const_double_by_immediates (rtx val) +{ + enum machine_mode mode = GET_MODE (val); + rtx part; + + if (mode == VOIDmode) + mode = DImode; + + part = gen_highpart_mode (SImode, mode, val); + + gcc_assert (GET_CODE (part) == CONST_INT); + + if (!const_ok_for_arm (INTVAL (part))) + return false; + + part = gen_lowpart (SImode, val); + + gcc_assert (GET_CODE (part) == CONST_INT); + + if (!const_ok_for_arm (INTVAL (part))) + return false; + + return true; +} + /* Scan INSN and note any of its operands that need fixing. If DO_PUSHES is false we do not actually push any of the fixups needed. The function returns TRUE if any fixups were needed/pushed. @@ -15150,8 +15325,18 @@ arm_print_operand (FILE *stream, rtx x, the value being loaded is big-wordian or little-wordian. The order of the two register loads can matter however, if the address of the memory location is actually held in one of the registers - being overwritten by the load. */ + being overwritten by the load. + + The 'Q' and 'R' constraints are also available for 64-bit + constants. */ case 'Q': + if (GET_CODE (x) == CONST_INT || GET_CODE (x) == CONST_DOUBLE) + { + rtx part = gen_lowpart (SImode, x); + fprintf (stream, "#" HOST_WIDE_INT_PRINT_DEC, INTVAL (part)); + return; + } + if (GET_CODE (x) != REG || REGNO (x) > LAST_ARM_REGNUM) { output_operand_lossage ("invalid operand for code '%c'", code); @@ -15162,6 +15347,18 @@ arm_print_operand (FILE *stream, rtx x, return; case 'R': + if (GET_CODE (x) == CONST_INT || GET_CODE (x) == CONST_DOUBLE) + { + enum machine_mode mode = GET_MODE (x); + rtx part; + + if (mode == VOIDmode) + mode = DImode; + part = gen_highpart_mode (SImode, mode, x); + fprintf (stream, "#" HOST_WIDE_INT_PRINT_DEC, INTVAL (part)); + return; + } + if (GET_CODE (x) != REG || REGNO (x) > LAST_ARM_REGNUM) { output_operand_lossage ("invalid operand for code '%c'", code); @@ -15854,6 +16051,28 @@ get_arm_condition_code (rtx comparison) default: gcc_unreachable (); } + case CC_CZmode: + switch (comp_code) + { + case NE: return ARM_NE; + case EQ: return ARM_EQ; + case GEU: return ARM_CS; + case GTU: return ARM_HI; + case LEU: return ARM_LS; + case LTU: return ARM_CC; + default: gcc_unreachable (); + } + + case CC_NCVmode: + switch (comp_code) + { + case GE: return ARM_GE; + case LT: return ARM_LT; + case GEU: return ARM_CS; + case LTU: return ARM_CC; + default: gcc_unreachable (); + } + case CCmode: switch (comp_code) { Index: gcc-4.5.3/gcc/config/arm/arm.h =================================================================== --- gcc-4.5.3.orig/gcc/config/arm/arm.h +++ gcc-4.5.3/gcc/config/arm/arm.h @@ -2253,19 +2253,7 @@ extern int making_const_table; : reverse_condition (code)) #define CANONICALIZE_COMPARISON(CODE, OP0, OP1) \ - do \ - { \ - if (GET_CODE (OP1) == CONST_INT \ - && ! (const_ok_for_arm (INTVAL (OP1)) \ - || (const_ok_for_arm (- INTVAL (OP1))))) \ - { \ - rtx const_op = OP1; \ - CODE = arm_canonicalize_comparison ((CODE), GET_MODE (OP0), \ - &const_op); \ - OP1 = const_op; \ - } \ - } \ - while (0) + (CODE) = arm_canonicalize_comparison (CODE, &(OP0), &(OP1)) /* The arm5 clz instruction returns 32. */ #define CLZ_DEFINED_VALUE_AT_ZERO(MODE, VALUE) ((VALUE) = 32, 1) Index: gcc-4.5.3/gcc/config/arm/arm.md =================================================================== --- gcc-4.5.3.orig/gcc/config/arm/arm.md +++ gcc-4.5.3/gcc/config/arm/arm.md @@ -6718,17 +6718,45 @@ operands[3])); DONE;" ) -;; this uses the Cirrus DI compare instruction (define_expand "cbranchdi4" [(set (pc) (if_then_else (match_operator 0 "arm_comparison_operator" - [(match_operand:DI 1 "cirrus_fp_register" "") - (match_operand:DI 2 "cirrus_fp_register" "")]) + [(match_operand:DI 1 "cmpdi_operand" "") + (match_operand:DI 2 "cmpdi_operand" "")]) (label_ref (match_operand 3 "" "")) (pc)))] - "TARGET_ARM && TARGET_HARD_FLOAT && TARGET_MAVERICK" - "emit_jump_insn (gen_cbranch_cc (operands[0], operands[1], operands[2], - operands[3])); DONE;" + "TARGET_32BIT" + "{ + rtx swap = NULL_RTX; + enum rtx_code code = GET_CODE (operands[0]); + + /* We should not have two constants. */ + gcc_assert (GET_MODE (operands[1]) == DImode + || GET_MODE (operands[2]) == DImode); + + /* Flip unimplemented DImode comparisons to a form that + arm_gen_compare_reg can handle. */ + switch (code) + { + case GT: + swap = gen_rtx_LT (VOIDmode, operands[2], operands[1]); break; + case LE: + swap = gen_rtx_GE (VOIDmode, operands[2], operands[1]); break; + case GTU: + swap = gen_rtx_LTU (VOIDmode, operands[2], operands[1]); break; + case LEU: + swap = gen_rtx_GEU (VOIDmode, operands[2], operands[1]); break; + default: + break; + } + if (swap) + emit_jump_insn (gen_cbranch_cc (swap, operands[2], operands[1], + operands[3])); + else + emit_jump_insn (gen_cbranch_cc (operands[0], operands[1], operands[2], + operands[3])); + DONE; + }" ) (define_insn "*cbranchsi4_insn" @@ -7880,6 +7908,52 @@ (const_string "alu_shift_reg")))] ) +;; DImode comparisons. The generic code generates branches that +;; if-conversion can not reduce to a conditional compare, so we do +;; that directly. + +(define_insn "*arm_cmpdi_insn" + [(set (reg:CC_NCV CC_REGNUM) + (compare:CC_NCV (match_operand:DI 0 "s_register_operand" "r") + (match_operand:DI 1 "arm_di_operand" "rDi"))) + (clobber (match_scratch:SI 2 "=r"))] + "TARGET_32BIT && !(TARGET_HARD_FLOAT && TARGET_MAVERICK)" + "cmp\\t%Q0, %Q1\;sbcs\\t%2, %R0, %R1" + [(set_attr "conds" "set") + (set_attr "length" "8")] +) + +(define_insn "*arm_cmpdi_unsigned" + [(set (reg:CC_CZ CC_REGNUM) + (compare:CC_CZ (match_operand:DI 0 "s_register_operand" "r") + (match_operand:DI 1 "arm_di_operand" "rDi")))] + "TARGET_ARM" + "cmp%?\\t%R0, %R1\;cmpeq\\t%Q0, %Q1" + [(set_attr "conds" "set") + (set_attr "length" "8")] +) + +(define_insn "*arm_cmpdi_zero" + [(set (reg:CC_Z CC_REGNUM) + (compare:CC_Z (match_operand:DI 0 "s_register_operand" "r") + (const_int 0))) + (clobber (match_scratch:SI 1 "=r"))] + "TARGET_32BIT" + "orr%.\\t%1, %Q0, %R0" + [(set_attr "conds" "set")] +) + +(define_insn "*thumb_cmpdi_zero" + [(set (reg:CC_Z CC_REGNUM) + (compare:CC_Z (match_operand:DI 0 "s_register_operand" "l") + (const_int 0))) + (clobber (match_scratch:SI 1 "=l"))] + "TARGET_THUMB1" + "orr\\t%1, %Q0, %R0" + [(set_attr "conds" "set") + (set_attr "length" "2")] +) + ;; Cirrus SF compare instruction (define_insn "*cirrus_cmpsf" [(set (reg:CCFP CC_REGNUM) @@ -8183,17 +8257,44 @@ operands[2], operands[3])); DONE;" ) -;; this uses the Cirrus DI compare instruction (define_expand "cstoredi4" [(set (match_operand:SI 0 "s_register_operand" "") (match_operator:SI 1 "arm_comparison_operator" - [(match_operand:DI 2 "cirrus_fp_register" "") - (match_operand:DI 3 "cirrus_fp_register" "")]))] - "TARGET_ARM && TARGET_HARD_FLOAT && TARGET_MAVERICK" - "emit_insn (gen_cstore_cc (operands[0], operands[1], - operands[2], operands[3])); DONE;" -) + [(match_operand:DI 2 "cmpdi_operand" "") + (match_operand:DI 3 "cmpdi_operand" "")]))] + "TARGET_32BIT" + "{ + rtx swap = NULL_RTX; + enum rtx_code code = GET_CODE (operands[1]); + /* We should not have two constants. */ + gcc_assert (GET_MODE (operands[2]) == DImode + || GET_MODE (operands[3]) == DImode); + + /* Flip unimplemented DImode comparisons to a form that + arm_gen_compare_reg can handle. */ + switch (code) + { + case GT: + swap = gen_rtx_LT (VOIDmode, operands[3], operands[2]); break; + case LE: + swap = gen_rtx_GE (VOIDmode, operands[3], operands[2]); break; + case GTU: + swap = gen_rtx_LTU (VOIDmode, operands[3], operands[2]); break; + case LEU: + swap = gen_rtx_GEU (VOIDmode, operands[3], operands[2]); break; + default: + break; + } + if (swap) + emit_insn (gen_cstore_cc (operands[0], swap, operands[3], + operands[2])); + else + emit_insn (gen_cstore_cc (operands[0], operands[1], operands[2], + operands[3])); + DONE; + }" +) (define_expand "cstoresi_eq0_thumb1" [(parallel Index: gcc-4.5.3/gcc/config/arm/constraints.md =================================================================== --- gcc-4.5.3.orig/gcc/config/arm/constraints.md +++ gcc-4.5.3/gcc/config/arm/constraints.md @@ -29,7 +29,7 @@ ;; in Thumb-1 state: I, J, K, L, M, N, O ;; The following multi-letter normal constraints have been used: -;; in ARM/Thumb-2 state: Da, Db, Dc, Dn, Dl, DL, Dv, Dy +;; in ARM/Thumb-2 state: Da, Db, Dc, Dn, Dl, DL, Dv, Dy, Di ;; in Thumb-1 state: Pa, Pb ;; in Thumb-2 state: Ps, Pt @@ -191,6 +191,13 @@ (match_test "TARGET_32BIT && arm_const_double_inline_cost (op) == 4 && !(optimize_size || arm_ld_sched)"))) +(define_constraint "Di" + "@internal + In ARM/Thumb-2 state a const_int or const_double where both the high + and low SImode words can be generated as immediates in 32-bit instructions." + (and (match_code "const_double,const_int") + (match_test "TARGET_32BIT && arm_const_double_by_immediates (op)"))) + (define_constraint "Dn" "@internal In ARM/Thumb-2 state a const_vector which can be loaded with a Neon vmov Index: gcc-4.5.3/gcc/config/arm/predicates.md =================================================================== --- gcc-4.5.3.orig/gcc/config/arm/predicates.md +++ gcc-4.5.3/gcc/config/arm/predicates.md @@ -101,6 +101,12 @@ (and (match_code "const_int") (match_test "const_ok_for_arm (INTVAL (op))"))) +;; A constant value which fits into two instructions, each taking +;; an arithmetic constant operand for one of the words. +(define_predicate "arm_immediate_di_operand" + (and (match_code "const_int,const_double") + (match_test "arm_const_double_by_immediates (op)"))) + (define_predicate "arm_neg_immediate_operand" (and (match_code "const_int") (match_test "const_ok_for_arm (-INTVAL (op))"))) @@ -130,6 +136,10 @@ (ior (match_operand 0 "arm_rhs_operand") (match_operand 0 "arm_not_immediate_operand"))) +(define_predicate "arm_di_operand" + (ior (match_operand 0 "s_register_operand") + (match_operand 0 "arm_immediate_di_operand"))) + ;; True if the operand is a memory reference which contains an ;; offsettable address. (define_predicate "offsettable_memory_operand" @@ -538,3 +548,12 @@ (and (match_code "const_int") (match_test "INTVAL (op) >= 0 && INTVAL (op) <= 15"))) +;; Predicates for named expanders that overlap multiple ISAs. + +(define_predicate "cmpdi_operand" + (if_then_else (match_test "TARGET_HARD_FLOAT && TARGET_MAVERICK") + (and (match_test "TARGET_ARM") + (match_operand 0 "cirrus_fp_register")) + (and (match_test "TARGET_32BIT") + (match_operand 0 "arm_di_operand")))) + Index: gcc-4.5.3/gcc/testsuite/gcc.c-torture/execute/20100416-1.c =================================================================== --- /dev/null +++ gcc-4.5.3/gcc/testsuite/gcc.c-torture/execute/20100416-1.c @@ -0,0 +1,40 @@ +void abort(void); + +int +movegt(int x, int y, long long a) +{ + int i; + int ret = 0; + for (i = 0; i < y; i++) + { + if (a >= (long long) 0xf000000000000000LL) + ret = x; + else + ret = y; + } + return ret; +} + +struct test +{ + long long val; + int ret; +} tests[] = { + { 0xf000000000000000LL, -1 }, + { 0xefffffffffffffffLL, 1 }, + { 0xf000000000000001LL, -1 }, + { 0x0000000000000000LL, -1 }, + { 0x8000000000000000LL, 1 }, +}; + +int +main() +{ + int i; + for (i = 0; i < sizeof (tests) / sizeof (tests[0]); i++) + { + if (movegt (-1, 1, tests[i].val) != tests[i].ret) + abort (); + } + return 0; +}