diff options
Diffstat (limited to 'gcc/config/sh')
-rw-r--r-- | gcc/config/sh/predicates.md | 5 | ||||
-rw-r--r-- | gcc/config/sh/sh.c | 32 | ||||
-rw-r--r-- | gcc/config/sh/sh.h | 34 | ||||
-rw-r--r-- | gcc/config/sh/sh.md | 55 |
4 files changed, 94 insertions, 32 deletions
diff --git a/gcc/config/sh/predicates.md b/gcc/config/sh/predicates.md index 92a7b689c84..3936ab2953f 100644 --- a/gcc/config/sh/predicates.md +++ b/gcc/config/sh/predicates.md @@ -791,9 +791,8 @@ /* Allow T_REG as shift count for dynamic shifts, although it is not really possible. It will then be copied to a general purpose reg. */ if (! TARGET_SHMEDIA) - return const_int_operand (op, mode) - || (TARGET_DYNSHIFT && (arith_reg_operand (op, mode) - || t_reg_operand (op, mode))); + return const_int_operand (op, mode) || arith_reg_operand (op, mode) + || (TARGET_DYNSHIFT && t_reg_operand (op, mode)); return (CONSTANT_P (op) ? (CONST_INT_P (op) diff --git a/gcc/config/sh/sh.c b/gcc/config/sh/sh.c index 10dad62b8f7..0abf28facc2 100644 --- a/gcc/config/sh/sh.c +++ b/gcc/config/sh/sh.c @@ -2871,35 +2871,35 @@ struct ashl_lshr_sequence static const struct ashl_lshr_sequence ashl_lshr_seq[32] = { - { 0, { 0 }, 0 }, + { 0, { 0 }, 0 }, // 0 { 1, { 1 }, LSHR_CLOBBERS_T }, { 1, { 2 }, 0 }, { 2, { 2, 1 }, LSHR_CLOBBERS_T }, - { 2, { 2, 2 }, 0 }, + { 2, { 2, 2 }, 0 }, // 4 { 3, { 2, 1, 2 }, LSHR_CLOBBERS_T }, { 3, { 2, 2, 2 }, 0 }, { 4, { 2, 2, 1, 2 }, LSHR_CLOBBERS_T }, - { 1, { 8 }, 0 }, + { 1, { 8 }, 0 }, // 8 { 2, { 8, 1 }, LSHR_CLOBBERS_T }, { 2, { 8, 2 }, 0 }, { 3, { 8, 1, 2 }, LSHR_CLOBBERS_T }, - { 3, { 8, 2, 2 }, 0 }, + { 3, { 8, 2, 2 }, 0 }, // 12 { 4, { 8, 2, 1, 2 }, LSHR_CLOBBERS_T }, { 3, { 8, -2, 8 }, 0 }, { 3, { 8, -1, 8 }, ASHL_CLOBBERS_T }, - { 1, { 16 }, 0 }, + { 1, { 16 }, 0 }, // 16 { 2, { 16, 1 }, LSHR_CLOBBERS_T }, { 2, { 16, 2 }, 0 }, { 3, { 16, 1, 2 }, LSHR_CLOBBERS_T }, - { 3, { 16, 2, 2 }, 0 }, + { 3, { 16, 2, 2 }, 0 }, // 20 { 4, { 16, 2, 1, 2 }, LSHR_CLOBBERS_T }, { 3, { 16, -2, 8 }, 0 }, { 3, { 16, -1, 8 }, ASHL_CLOBBERS_T }, - { 2, { 16, 8 }, 0 }, + { 2, { 16, 8 }, 0 }, // 24 { 3, { 16, 1, 8 }, LSHR_CLOBBERS_T }, { 3, { 16, 8, 2 }, 0 }, { 4, { 16, 8, 1, 2 }, LSHR_CLOBBERS_T }, - { 4, { 16, 8, 2, 2 }, 0 }, + { 4, { 16, 8, 2, 2 }, 0 }, // 28 { 4, { 16, -1, -2, 16 }, ASHL_CLOBBERS_T }, { 3, { 16, -2, 16 }, 0 }, @@ -2915,35 +2915,35 @@ static const struct ashl_lshr_sequence ashl_lshr_seq[32] = kind of sign or zero extension. */ static const struct ashl_lshr_sequence ext_ashl_lshr_seq[32] = { - { 0, { 0 }, 0 }, + { 0, { 0 }, 0 }, // 0 { 1, { 1 }, LSHR_CLOBBERS_T }, { 1, { 2 }, 0 }, { 2, { 2, 1 }, LSHR_CLOBBERS_T }, - { 2, { 2, 2 }, 0 }, + { 2, { 2, 2 }, 0 }, // 4 { 3, { 2, 1, 2 }, LSHR_CLOBBERS_T }, { 2, { 8, -2 }, 0 }, { 2, { 8, -1 }, ASHL_CLOBBERS_T }, - { 1, { 8 }, 0 }, + { 1, { 8 }, 0 }, // 8 { 2, { 8, 1 }, LSHR_CLOBBERS_T }, { 2, { 8, 2 }, 0 }, { 3, { 8, 1, 2 }, LSHR_CLOBBERS_T }, - { 3, { 8, 2, 2 }, 0 }, + { 3, { 8, 2, 2 }, 0 }, // 12 { 3, { 16, -2, -1 }, ASHL_CLOBBERS_T }, { 2, { 16, -2 }, 0 }, { 2, { 16, -1 }, ASHL_CLOBBERS_T }, - { 1, { 16 }, 0 }, + { 1, { 16 }, 0 }, // 16 { 2, { 16, 1 }, LSHR_CLOBBERS_T }, { 2, { 16, 2 }, 0 }, { 3, { 16, 1, 2 }, LSHR_CLOBBERS_T }, - { 3, { 16, 2, 2 }, 0 }, + { 3, { 16, 2, 2 }, 0 }, // 20 { 4, { 16, 2, 1, 2 }, LSHR_CLOBBERS_T }, { 3, { 16, -2, 8 }, 0 }, { 3, { 16, -1, 8 }, ASHL_CLOBBERS_T }, - { 2, { 16, 8 }, 0 }, + { 2, { 16, 8 }, 0 }, // 24 { 3, { 16, 1, 8 }, LSHR_CLOBBERS_T }, { 3, { 16, 8, 2 }, 0 }, { 4, { 16, 8, 1, 2 }, LSHR_CLOBBERS_T }, - { 4, { 16, 8, 2, 2 }, 0 }, + { 4, { 16, 8, 2, 2 }, 0 }, // 28 { 4, { 16, -1, -2, 16 }, ASHL_CLOBBERS_T }, { 3, { 16, -2, 16 }, 0 }, { 3, { 16, -1, 16 }, ASHL_CLOBBERS_T } diff --git a/gcc/config/sh/sh.h b/gcc/config/sh/sh.h index b36287276aa..d72379022bb 100644 --- a/gcc/config/sh/sh.h +++ b/gcc/config/sh/sh.h @@ -1932,19 +1932,27 @@ struct sh_args { like shad and shld. */ #define TARGET_DYNSHIFT (TARGET_SH3 || TARGET_SH2A) -#define SH_DYNAMIC_SHIFT_COST \ - (TARGET_HARD_SH4 ? 1 : TARGET_DYNSHIFT ? (optimize_size ? 1 : 2) : 20) - -/* Immediate shift counts are truncated by the output routines (or was it - the assembler?). Shift counts in a register are truncated by SH. Note - that the native compiler puts too large (> 32) immediate shift counts - into a register and shifts by the register, letting the SH decide what - to do instead of doing that itself. */ -/* ??? The library routines in lib1funcs.S truncate the shift count. - However, the SH3 has hardware shifts that do not truncate exactly as gcc - expects - the sign bit is significant - so it appears that we need to - leave this zero for correct SH3 code. */ -#define SHIFT_COUNT_TRUNCATED (! TARGET_SH3 && ! TARGET_SH2A) +/* The cost of using the dynamic shift insns (shad, shld) are the same + if they are available. If they are not available a library function will + be emitted instead, which is more expensive. */ +#define SH_DYNAMIC_SHIFT_COST (TARGET_DYNSHIFT ? 1 : 20) + +/* Defining SHIFT_COUNT_TRUNCATED tells the combine pass that code like + (X << (Y % 32)) for register X, Y is equivalent to (X << Y). + This is not generally true when hardware dynamic shifts (shad, shld) are + used, because they check the sign bit _before_ the modulo op. The sign + bit determines whether it is a left shift or a right shift: + if (Y < 0) + return X << (Y & 31); + else + return X >> (-Y) & 31); + + The dynamic shift library routines in lib1funcs.S do not use the sign bit + like the hardware dynamic shifts and truncate the shift count to 31. + We define SHIFT_COUNT_TRUNCATED to 0 and express the implied shift count + truncation in the library function call patterns, as this gives slightly + more compact code. */ +#define SHIFT_COUNT_TRUNCATED (0) /* CANONICALIZE_COMPARISON macro for the combine pass. */ #define CANONICALIZE_COMPARISON(CODE, OP0, OP1) \ diff --git a/gcc/config/sh/sh.md b/gcc/config/sh/sh.md index 8b44fbda496..c06a51011b7 100644 --- a/gcc/config/sh/sh.md +++ b/gcc/config/sh/sh.md @@ -4023,6 +4023,17 @@ label: operands[2])); DONE; } + + /* Expand a library call for the dynamic shift. */ + if (!CONST_INT_P (operands[2]) && !TARGET_DYNSHIFT) + { + emit_move_insn (gen_rtx_REG (SImode, R4_REG), operands[1]); + rtx funcaddr = gen_reg_rtx (Pmode); + function_symbol (funcaddr, "__ashlsi3_r0", SFUNC_STATIC); + emit_insn (gen_ashlsi3_d_call (operands[0], operands[2], funcaddr)); + + DONE; + } }) (define_insn "ashlsi3_k" @@ -4067,6 +4078,23 @@ label: } [(set_attr "type" "dyn_shift")]) +;; If dynamic shifts are not available use a library function. +;; By specifying the pattern we reduce the number of call clobbered regs. +;; In order to make combine understand the truncation of the shift amount +;; operand we have to allow it to use pseudo regs for the shift operands. +(define_insn "ashlsi3_d_call" + [(set (match_operand:SI 0 "arith_reg_dest" "=z") + (ashift:SI (reg:SI R4_REG) + (and:SI (match_operand:SI 1 "arith_reg_operand" "z") + (const_int 31)))) + (use (match_operand:SI 2 "arith_reg_operand" "r")) + (clobber (reg:SI T_REG)) + (clobber (reg:SI PR_REG))] + "TARGET_SH1 && !TARGET_DYNSHIFT" + "jsr @%2%#" + [(set_attr "type" "sfunc") + (set_attr "needs_delay_slot" "yes")]) + (define_insn_and_split "ashlsi3_n" [(set (match_operand:SI 0 "arith_reg_dest" "=r") (ashift:SI (match_operand:SI 1 "arith_reg_operand" "0") @@ -4512,6 +4540,16 @@ label: operands[2])); DONE; } + + /* Expand a library call for the dynamic shift. */ + if (!CONST_INT_P (operands[2]) && !TARGET_DYNSHIFT) + { + emit_move_insn (gen_rtx_REG (SImode, R4_REG), operands[1]); + rtx funcaddr = gen_reg_rtx (Pmode); + function_symbol (funcaddr, "__lshrsi3_r0", SFUNC_STATIC); + emit_insn (gen_lshrsi3_d_call (operands[0], operands[2], funcaddr)); + DONE; + } }) (define_insn "lshrsi3_k" @@ -4556,6 +4594,23 @@ label: } [(set_attr "type" "dyn_shift")]) +;; If dynamic shifts are not available use a library function. +;; By specifying the pattern we reduce the number of call clobbered regs. +;; In order to make combine understand the truncation of the shift amount +;; operand we have to allow it to use pseudo regs for the shift operands. +(define_insn "lshrsi3_d_call" + [(set (match_operand:SI 0 "arith_reg_dest" "=z") + (lshiftrt:SI (reg:SI R4_REG) + (and:SI (match_operand:SI 1 "arith_reg_operand" "z") + (const_int 31)))) + (use (match_operand:SI 2 "arith_reg_operand" "r")) + (clobber (reg:SI T_REG)) + (clobber (reg:SI PR_REG))] + "TARGET_SH1 && !TARGET_DYNSHIFT" + "jsr @%2%#" + [(set_attr "type" "sfunc") + (set_attr "needs_delay_slot" "yes")]) + (define_insn_and_split "lshrsi3_n" [(set (match_operand:SI 0 "arith_reg_dest" "=r") (lshiftrt:SI (match_operand:SI 1 "arith_reg_operand" "0") |