summaryrefslogtreecommitdiff
path: root/gcc/config/sh/sh.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/config/sh/sh.c')
-rw-r--r--gcc/config/sh/sh.c220
1 files changed, 127 insertions, 93 deletions
diff --git a/gcc/config/sh/sh.c b/gcc/config/sh/sh.c
index 68e6034203c..3e85fcf3e6c 100644
--- a/gcc/config/sh/sh.c
+++ b/gcc/config/sh/sh.c
@@ -6487,7 +6487,9 @@ push_regs (HARD_REG_SET *mask, int interrupt_handler)
use_movml = true;
}
- if (use_movml)
+ if (sh_cfun_resbank_handler_p ())
+ ; /* Do nothing. */
+ else if (use_movml)
{
rtx x, mem, reg, set;
rtx sp_reg = gen_rtx_REG (SImode, STACK_POINTER_REGNUM);
@@ -7485,7 +7487,9 @@ sh_expand_epilogue (bool sibcall_p)
use_movml = true;
}
- if (use_movml)
+ if (sh_cfun_resbank_handler_p ())
+ ; /* Do nothing. */
+ else if (use_movml)
{
rtx sp_reg = gen_rtx_REG (SImode, STACK_POINTER_REGNUM);
@@ -9783,13 +9787,80 @@ legitimize_pic_address (rtx orig, enum machine_mode mode ATTRIBUTE_UNUSED,
return orig;
}
-/* Try machine-dependent ways of modifying an illegitimate address
- to be legitimate. If we find one, return the new, valid address.
- Otherwise, return X.
+/* Given a (logical) mode size and an offset in bytes, try to find a the
+ appropriate displacement value for a mov insn. On SH the displacements
+ are limited to max. 60 bytes for SImode, max. 30 bytes in HImode and max.
+ 15 bytes in QImode. To compensate this we create a new base address by
+ adding an adjustment value to it.
+
+ If the originally requested offset is greater than 127 we prefer using
+ values 124..127 over 128..131 to increase opportunities to use the
+ add #imm, Rn insn.
+
+ In some cases it is possible that a requested offset might seem unaligned
+ or inappropriate for the mode size, like offset = 2 and mode size = 4.
+ This is compensated by adjusting the base address so that the effective
+ address of the displacement move insn will be aligned.
+
+ This is not the best possible way of rebasing the base address, as it
+ does not look at other present displacement addressings around it.
+ In some cases this can create more base address adjustments than would
+ actually be necessary. */
+
+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)
+{
+ 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;
+
+ 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)
+ return res;
+
+ /* FIXME: HImode with displacement addressing is not supported yet.
+ Make it purposefully fail for now. */
+ if (mov_insn_size == 2)
+ return res;
- For the SH, if X is almost suitable for indexing, but the offset is
- out of range, convert it into a normal form so that CSE has a chance
- of reducing the number of address registers used. */
+ /* 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)
+ 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)
+ {
+ res.offset_adjust = GEN_INT (offset_adjust);
+ res.mov_disp = GEN_INT (offset - offset_adjust);
+ }
+
+ return res;
+}
+
+/* Try to modify an illegitimate address and make it legitimate.
+ If we find one, return the new, valid address.
+ Otherwise, return the original address. */
static rtx
sh_legitimize_address (rtx x, rtx oldx, enum machine_mode mode)
@@ -9797,66 +9868,33 @@ sh_legitimize_address (rtx x, rtx oldx, enum machine_mode mode)
if (flag_pic)
x = legitimize_pic_address (oldx, mode, NULL_RTX);
- if (GET_CODE (x) == PLUS
- && (GET_MODE_SIZE (mode) == 4
- || GET_MODE_SIZE (mode) == 8)
- && CONST_INT_P (XEXP (x, 1))
- && BASE_REGISTER_RTX_P (XEXP (x, 0))
- && ! TARGET_SHMEDIA
- && ! ((TARGET_SH4 || TARGET_SH2A_DOUBLE) && mode == DFmode)
- && ! (TARGET_SH2E && mode == SFmode))
- {
- rtx index_rtx = XEXP (x, 1);
- HOST_WIDE_INT offset = INTVAL (index_rtx), offset_base;
- rtx sum;
-
- /* On rare occasions, we might get an unaligned pointer
- that is indexed in a way to give an aligned address.
- Therefore, keep the lower two bits in offset_base. */
- /* Instead of offset_base 128..131 use 124..127, so that
- simple add suffices. */
- if (offset > 127)
- offset_base = ((offset + 4) & ~60) - 4;
- else
- offset_base = offset & ~60;
-
- /* Sometimes the normal form does not suit DImode. We
- could avoid that by using smaller ranges, but that
- would give less optimized code when SImode is
- prevalent. */
- if (GET_MODE_SIZE (mode) + offset - offset_base <= 64)
- {
- sum = expand_binop (Pmode, add_optab, XEXP (x, 0),
- GEN_INT (offset_base), NULL_RTX, 0,
- OPTAB_LIB_WIDEN);
+ if (TARGET_SHMEDIA)
+ return x;
- return gen_rtx_PLUS (Pmode, sum, GEN_INT (offset - offset_base));
- }
- }
+ if (((TARGET_SH4 || TARGET_SH2A_DOUBLE) && mode == DFmode)
+ || (TARGET_SH2E && mode == SFmode))
+ return x;
- /* This could be generalized for SImode, HImode, QImode displacement
- addressing. */
- if (mode == QImode && GET_CODE (x) == PLUS
- && BASE_REGISTER_RTX_P (XEXP (x, 0)) && CONST_INT_P (XEXP (x, 1)))
+ if (GET_CODE (x) == PLUS && CONST_INT_P (XEXP (x, 1))
+ && BASE_REGISTER_RTX_P (XEXP (x, 0)))
{
- rtx index_rtx = XEXP (x, 1);
- HOST_WIDE_INT offset = INTVAL (index_rtx);
- HOST_WIDE_INT offset_base = offset & ~15;
-
- if (offset - offset_base <= 16)
+ const int mode_sz = GET_MODE_SIZE (mode);
+ struct disp_adjust adj = sh_find_mov_disp_adjust (mode_sz,
+ INTVAL (XEXP (x, 1)));
+
+ if (adj.offset_adjust != NULL_RTX && adj.mov_disp != NULL_RTX)
{
rtx sum = expand_binop (Pmode, add_optab, XEXP (x, 0),
- GEN_INT (offset_base), NULL_RTX, 0,
- OPTAB_LIB_WIDEN);
-
- return gen_rtx_PLUS (Pmode, sum, GEN_INT (offset - offset_base));
+ adj.offset_adjust, NULL_RTX, 0,
+ OPTAB_LIB_WIDEN);
+ return gen_rtx_PLUS (Pmode, sum, adj.mov_disp);
}
}
return x;
}
-/* Attempt to replace *P, which is an address that needs reloading, with
+/* Attempt to replace *p, which is an address that needs reloading, with
a valid memory address for an operand of mode MODE.
Like for sh_legitimize_address, for the SH we try to get a normal form
of the address. That will allow inheritance of the address reloads. */
@@ -9866,75 +9904,71 @@ sh_legitimize_reload_address (rtx *p, enum machine_mode mode, int opnum,
int itype)
{
enum reload_type type = (enum reload_type) itype;
+ const int mode_sz = GET_MODE_SIZE (mode);
- if (GET_CODE (*p) == PLUS
- && (GET_MODE_SIZE (mode) == 4 || GET_MODE_SIZE (mode) == 8)
- && CONST_INT_P (XEXP (*p, 1))
+ if (TARGET_SHMEDIA)
+ return false;
+
+ if (GET_CODE (*p) == PLUS && CONST_INT_P (XEXP (*p, 1))
&& MAYBE_BASE_REGISTER_RTX_P (XEXP (*p, 0), true)
- && ! TARGET_SHMEDIA
- && ! (TARGET_SH4 && mode == DFmode)
&& ! (mode == PSImode && type == RELOAD_FOR_INPUT_ADDRESS)
&& (ALLOW_INDEXED_ADDRESS
|| XEXP (*p, 0) == stack_pointer_rtx
|| XEXP (*p, 0) == hard_frame_pointer_rtx))
{
- rtx index_rtx = XEXP (*p, 1);
- HOST_WIDE_INT offset = INTVAL (index_rtx), offset_base;
- rtx sum;
+ const HOST_WIDE_INT offset = INTVAL (XEXP (*p, 1));
+ struct disp_adjust adj = sh_find_mov_disp_adjust (mode_sz, offset);
if (TARGET_SH2A && mode == DFmode && (offset & 0x7))
{
push_reload (*p, NULL_RTX, p, NULL,
BASE_REG_CLASS, Pmode, VOIDmode, 0, 0, opnum, type);
- goto win;
+ return true;
}
+
if (TARGET_SH2E && mode == SFmode)
{
*p = copy_rtx (*p);
push_reload (*p, NULL_RTX, p, NULL,
BASE_REG_CLASS, Pmode, VOIDmode, 0, 0, opnum, type);
- goto win;
+ return true;
}
- /* Instead of offset_base 128..131 use 124..127, so that
- simple add suffices. */
- if (offset > 127)
- offset_base = ((offset + 4) & ~60) - 4;
- else
- offset_base = offset & ~60;
- /* Sometimes the normal form does not suit DImode. We could avoid
- that by using smaller ranges, but that would give less optimized
- code when SImode is prevalent. */
- if (GET_MODE_SIZE (mode) + offset - offset_base <= 64)
- {
- sum = gen_rtx_PLUS (Pmode, XEXP (*p, 0), GEN_INT (offset_base));
- *p = gen_rtx_PLUS (Pmode, sum, GEN_INT (offset - offset_base));
+
+ /* FIXME: Do not allow to legitimize QImode and HImode displacement
+ moves because then reload has a problem figuring the constraint
+ that the move insn target/source reg must be R0.
+ Or maybe some handling is wrong in sh_secondary_reload for this
+ to work properly? */
+ if ((mode_sz == 4 || mode_sz == 8)
+ && ! (TARGET_SH4 && mode == DFmode)
+ && adj.offset_adjust != NULL_RTX && adj.mov_disp != NULL_RTX)
+ {
+ rtx sum = gen_rtx_PLUS (Pmode, XEXP (*p, 0), adj.offset_adjust);
+ *p = gen_rtx_PLUS (Pmode, sum, adj.mov_disp);
push_reload (sum, NULL_RTX, &XEXP (*p, 0), NULL,
BASE_REG_CLASS, Pmode, VOIDmode, 0, 0, opnum, type);
- goto win;
+ return true;
}
}
+
/* We must re-recognize what we created before. */
- else if (GET_CODE (*p) == PLUS
- && (GET_MODE_SIZE (mode) == 4 || GET_MODE_SIZE (mode) == 8)
- && GET_CODE (XEXP (*p, 0)) == PLUS
- && CONST_INT_P (XEXP (XEXP (*p, 0), 1))
- && MAYBE_BASE_REGISTER_RTX_P (XEXP (XEXP (*p, 0), 0), true)
- && CONST_INT_P (XEXP (*p, 1))
- && ! TARGET_SHMEDIA
- && ! (TARGET_SH2E && mode == SFmode))
+ if (GET_CODE (*p) == PLUS
+ && (mode_sz == 4 || mode_sz == 8)
+ && GET_CODE (XEXP (*p, 0)) == PLUS
+ && CONST_INT_P (XEXP (XEXP (*p, 0), 1))
+ && MAYBE_BASE_REGISTER_RTX_P (XEXP (XEXP (*p, 0), 0), true)
+ && CONST_INT_P (XEXP (*p, 1))
+ && ! (TARGET_SH2E && mode == SFmode))
{
/* Because this address is so complex, we know it must have
been created by LEGITIMIZE_RELOAD_ADDRESS before; thus,
it is already unshared, and needs no further unsharing. */
push_reload (XEXP (*p, 0), NULL_RTX, &XEXP (*p, 0), NULL,
BASE_REG_CLASS, Pmode, VOIDmode, 0, 0, opnum, type);
- goto win;
+ return true;
}
return false;
-
- win:
- return true;
}
/* In the name of slightly smaller debug output, and to cater to