diff options
author | Oleg Endo <olegendo@gcc.gnu.org> | 2012-04-05 18:43:45 +0000 |
---|---|---|
committer | Oleg Endo <olegendo@gcc.gnu.org> | 2012-04-05 18:43:45 +0000 |
commit | 9b9ad23f888c646982731e8c9c797885559bdb4f (patch) | |
tree | ee5004d6ad7de25c4591a8c0bcbc8a8e817248bb | |
parent | 14f986ed7a69f4f21b7312328fbef1747b12b6db (diff) | |
download | gcc-9b9ad23f888c646982731e8c9c797885559bdb4f.zip gcc-9b9ad23f888c646982731e8c9c797885559bdb4f.tar.gz gcc-9b9ad23f888c646982731e8c9c797885559bdb4f.tar.bz2 |
re PR target/50751 (SH Target: Displacement addressing does not work for QImode and HImode)
PR target/50751
* config/sh/sh.c (sh_find_mov_disp_adjust): Take machine_mode as the
first argument instead of mode size. Move displacement calculations
to ...
(mov_insn_size, max_mov_insn_displacement, mov_insn_alignment_mask):
... these new functions.
(disp_adjust): Remove max_mov_disp field.
(sh_legitimate_index_p): Use max_mov_insn_displacement and
mov_insn_alignment_mask.
(sh_address_cost): Use max_mov_insn_displacement.
From-SVN: r186169
-rw-r--r-- | gcc/ChangeLog | 13 | ||||
-rw-r--r-- | gcc/config/sh/sh.c | 221 |
2 files changed, 150 insertions, 84 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 8fbf1ab..e24e0d2 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,16 @@ +2012-04-05 Oleg Endo <olegendo@gcc.gnu.org> + + PR target/50751 + * config/sh/sh.c (sh_find_mov_disp_adjust): Take machine_mode as the + first argument instead of mode size. Move displacement calculations + to ... + (mov_insn_size, max_mov_insn_displacement, mov_insn_alignment_mask): + ... these new functions. + (disp_adjust): Remove max_mov_disp field. + (sh_legitimate_index_p): Use max_mov_insn_displacement and + mov_insn_alignment_mask. + (sh_address_cost): Use max_mov_insn_displacement. + 2012-04-05 Andrew Stubbs <ams@codesourcery.com> * config/arm/arm.md (arch): Add neon_onlya8 and neon_nota8. diff --git a/gcc/config/sh/sh.c b/gcc/config/sh/sh.c index 3e85fcf..d81bceb 100644 --- a/gcc/config/sh/sh.c +++ b/gcc/config/sh/sh.c @@ -302,6 +302,9 @@ static void sh_trampoline_init (rtx, tree, rtx); static rtx sh_trampoline_adjust_address (rtx); static void sh_conditional_register_usage (void); static bool sh_legitimate_constant_p (enum machine_mode, rtx); +static int mov_insn_size (enum machine_mode, bool); +static int max_mov_insn_displacement (enum machine_mode, bool); +static int mov_insn_alignment_mask (enum machine_mode, bool); static void sh_init_sync_libfuncs (void) ATTRIBUTE_UNUSED; @@ -3129,22 +3132,103 @@ sh_rtx_costs (rtx x, int code, int outer_code, int opno ATTRIBUTE_UNUSED, } } -/* Compute the cost of an address. For the SH, all valid addresses are - the same cost. Use a slightly higher cost for reg + reg addressing, - since it increases pressure on r0. */ +/* Determine the size of the fundamental move insn that will be used + for the specified mode. */ + +static inline int +mov_insn_size (enum machine_mode mode, bool consider_sh2a) +{ + const int mode_sz = GET_MODE_SIZE (mode); + + if ((consider_sh2a && TARGET_SH2A_DOUBLE && mode == DFmode) + || (TARGET_FMOVD && mode == DFmode)) + return mode_sz; + else + { + /* The max. available mode for actual move insns is SImode. + Larger accesses will be split into multiple loads/stores. */ + const int max_mov_sz = GET_MODE_SIZE (SImode); + return mode_sz >= max_mov_sz ? max_mov_sz : mode_sz; + } +} + +/* Determine the maximum possible displacement for a move insn for the + specified mode. */ + +static int +max_mov_insn_displacement (enum machine_mode mode, bool consider_sh2a) +{ + /* The 4 byte displacement move insns are the same as the 2 byte + versions but take a 12 bit displacement. All we need to do is to + scale the max. displacement value accordingly. */ + const int disp_scale = consider_sh2a ? (4095 / 15) : 1; + + /* FIXME: HImode with displacement addressing is not supported yet. + Make it purposefully fail for now. */ + if (mode == HImode) + return 0; + + /* SH2A supports FPU move insns with 12 bit displacements. + Other variants to do not support any kind of displacements for + FPU move insns. */ + if (! consider_sh2a && TARGET_FPU_ANY && GET_MODE_CLASS (mode) == MODE_FLOAT) + return 0; + else + { + const int mov_insn_sz = mov_insn_size (mode, consider_sh2a); + const int mode_sz = GET_MODE_SIZE (mode); + int r = 15 * mov_insn_sz * disp_scale; + + /* If the mov insn will be split into multiple loads/stores, the + maximum possible displacement is a bit smaller. */ + if (mode_sz > mov_insn_sz) + r -= mode_sz - mov_insn_sz; + return r; + } +} + +/* Determine the alignment mask for a move insn of the + specified mode. */ + +static inline int +mov_insn_alignment_mask (enum machine_mode mode, bool consider_sh2a) +{ + const int mov_insn_sz = mov_insn_size (mode, consider_sh2a); + return mov_insn_sz > 0 ? (mov_insn_sz - 1) : 0; +} + +/* Compute the cost of an address. */ static int -sh_address_cost (rtx X, - bool speed ATTRIBUTE_UNUSED) +sh_address_cost (rtx x, bool speed ATTRIBUTE_UNUSED) { - /* SH2A supports 4 byte displacement mov insns with higher offsets. - Consider those as more expensive than 2 byte insns. */ - if (DISP_ADDR_P (X) && GET_MODE (X) == QImode) - return DISP_ADDR_OFFSET (X) < 16 ? 0 : 1; + /* 'reg + disp' addressing. */ + if (DISP_ADDR_P (x)) + { + const HOST_WIDE_INT offset = DISP_ADDR_OFFSET (x); + const enum machine_mode mode = GET_MODE (x); + + /* The displacement would fit into a 2 byte move insn. */ + if (offset > 0 && offset <= max_mov_insn_displacement (mode, false)) + return 0; + + /* The displacement would fit into a 4 byte move insn (SH2A). */ + if (TARGET_SH2A + && offset > 0 && offset <= max_mov_insn_displacement (mode, true)) + return 1; - return (GET_CODE (X) == PLUS - && ! CONSTANT_P (XEXP (X, 1)) - && ! TARGET_SHMEDIA ? 1 : 0); + /* The displacement is probably out of range and will require extra + calculations. */ + return 2; + } + + /* 'reg + reg' addressing. Account a slightly higher cost because of + increased pressure on R0. */ + if (GET_CODE (x) == PLUS && ! CONSTANT_P (XEXP (x, 1)) + && ! TARGET_SHMEDIA) + return 1; + + return 0; } /* Code to expand a shift. */ @@ -9593,67 +9677,41 @@ sh_insn_length_adjustment (rtx insn) /* Return TRUE for a valid displacement for the REG+disp addressing with MODE. */ -/* ??? The SH2e does not have the REG+disp addressing mode when loading values - into the FRx registers. We implement this by setting the maximum offset - to zero when the value is SFmode. This also restricts loading of SFmode - values into the integer registers, but that can't be helped. */ - -/* The SH allows a displacement in a QI or HI amode, but only when the - other operand is R0. GCC doesn't handle this very well, so we forgot - all of that. - - A legitimate index for a QI or HI is 0, SI can be any number 0..63, - DI can be any number 0..60. */ - bool sh_legitimate_index_p (enum machine_mode mode, rtx op) { - if (CONST_INT_P (op)) - { - if (TARGET_SHMEDIA) - { - int size; - - /* Check if this is the address of an unaligned load / store. */ - if (mode == VOIDmode) - return CONST_OK_FOR_I06 (INTVAL (op)); + if (! CONST_INT_P (op)) + return false; - size = GET_MODE_SIZE (mode); - return (!(INTVAL (op) & (size - 1)) - && INTVAL (op) >= -512 * size - && INTVAL (op) < 512 * size); - } + if (TARGET_SHMEDIA) + { + int size; - if (TARGET_SH2A) - { - if (mode == QImode && (unsigned) INTVAL (op) < 4096) - return true; - } + /* Check if this is the address of an unaligned load / store. */ + if (mode == VOIDmode) + return CONST_OK_FOR_I06 (INTVAL (op)); - if (mode == QImode && (unsigned) INTVAL (op) < 16) - return true; + size = GET_MODE_SIZE (mode); + return (!(INTVAL (op) & (size - 1)) + && INTVAL (op) >= -512 * size + && INTVAL (op) < 512 * size); + } + else + { + const HOST_WIDE_INT offset = INTVAL (op); + const int max_disp = max_mov_insn_displacement (mode, TARGET_SH2A); + const int align_mask = mov_insn_alignment_mask (mode, TARGET_SH2A); - if ((GET_MODE_SIZE (mode) == 4 - && (unsigned) INTVAL (op) < 64 - && !(INTVAL (op) & 3) - && !(TARGET_SH2E && mode == SFmode)) - || (GET_MODE_SIZE (mode) == 4 - && (unsigned) INTVAL (op) < 16383 - && !(INTVAL (op) & 3) && TARGET_SH2A)) - return true; + /* If the mode does not support any displacement always return false. + Even though an index of '0' is actually always valid, it will cause + troubles when e.g. a DFmode move is split into two SFmode moves, + where one SFmode move will have index '0' and the other move will + have index '4'. */ + if (max_disp < 1) + return false; - if ((GET_MODE_SIZE (mode) == 8 - && (unsigned) INTVAL (op) < 60 - && !(INTVAL (op) & 3) - && !((TARGET_SH4 || TARGET_SH2A) && mode == DFmode)) - || ((GET_MODE_SIZE (mode)==8) - && (unsigned) INTVAL (op) < 8192 - && !(INTVAL (op) & (TARGET_SH2A_DOUBLE ? 7 : 3)) - && (TARGET_SH2A && mode == DFmode))) - return true; + return offset >= 0 && offset <= max_disp && (offset & align_mask) == 0; } - - return false; } /* Recognize an RTL expression that is a valid memory address for @@ -9811,45 +9869,41 @@ struct disp_adjust { rtx offset_adjust; rtx mov_disp; - int max_mov_disp; }; static struct disp_adjust -sh_find_mov_disp_adjust (int mode_sz, HOST_WIDE_INT offset) +sh_find_mov_disp_adjust (enum machine_mode mode, HOST_WIDE_INT offset) { - struct disp_adjust res = { NULL_RTX, NULL_RTX, 0 }; - - /* The max. available mode for actual move insns is SImode. - Larger accesses will be split into multiple loads/stores. */ - const int max_mov_sz = GET_MODE_SIZE (SImode); - - const int mov_insn_size = mode_sz >= max_mov_sz ? max_mov_sz : mode_sz; - const HOST_WIDE_INT max_disp = 15 * mov_insn_size; - HOST_WIDE_INT align_modifier = offset > 127 ? mov_insn_size : 0; + struct disp_adjust res = { NULL_RTX, NULL_RTX }; + /* Do not try to use SH2A's large displacements here, because this would + effectively disable the small displacement insns. */ + const int mode_sz = GET_MODE_SIZE (mode); + const int mov_insn_sz = mov_insn_size (mode, false); + const int max_disp = max_mov_insn_displacement (mode, false); + const int max_disp_next = max_disp + mov_insn_sz; + HOST_WIDE_INT align_modifier = offset > 127 ? mov_insn_sz : 0; HOST_WIDE_INT offset_adjust; /* In some cases this actually does happen and we must check for it. */ - if (mode_sz < 1 || mode_sz > 8) + if (mode_sz < 1 || mode_sz > 8 || max_disp < 1) return res; /* FIXME: HImode with displacement addressing is not supported yet. Make it purposefully fail for now. */ - if (mov_insn_size == 2) + if (mov_insn_sz == 2) return res; /* Keeps the previous behavior for QImode displacement addressing. This just decides how the offset is re-based. Removing this special case will result in slightly bigger code on average, but it's not that bad actually. */ - if (mov_insn_size == 1) + if (mov_insn_sz == 1) align_modifier = 0; - res.max_mov_disp = max_disp + mov_insn_size; - offset_adjust = ((offset + align_modifier) & ~max_disp) - align_modifier; - if (mode_sz + offset - offset_adjust <= res.max_mov_disp) + if (mode_sz + offset - offset_adjust <= max_disp_next) { res.offset_adjust = GEN_INT (offset_adjust); res.mov_disp = GEN_INT (offset - offset_adjust); @@ -9878,8 +9932,7 @@ sh_legitimize_address (rtx x, rtx oldx, enum machine_mode mode) if (GET_CODE (x) == PLUS && CONST_INT_P (XEXP (x, 1)) && BASE_REGISTER_RTX_P (XEXP (x, 0))) { - const int mode_sz = GET_MODE_SIZE (mode); - struct disp_adjust adj = sh_find_mov_disp_adjust (mode_sz, + struct disp_adjust adj = sh_find_mov_disp_adjust (mode, INTVAL (XEXP (x, 1))); if (adj.offset_adjust != NULL_RTX && adj.mov_disp != NULL_RTX) @@ -9917,7 +9970,7 @@ sh_legitimize_reload_address (rtx *p, enum machine_mode mode, int opnum, || XEXP (*p, 0) == hard_frame_pointer_rtx)) { const HOST_WIDE_INT offset = INTVAL (XEXP (*p, 1)); - struct disp_adjust adj = sh_find_mov_disp_adjust (mode_sz, offset); + struct disp_adjust adj = sh_find_mov_disp_adjust (mode, offset); if (TARGET_SH2A && mode == DFmode && (offset & 0x7)) { |