re PR target/50751 (SH Target: Displacement addressing does not work for QImode and HImode)

PR target/50751
	* config/sh/sh.c (sh_legitimize_address, sh_legitimize_reload_address):
	Rearrange conditional logic.  Move displacement address calculations
	to ...
	(sh_find_mov_disp_adjust): ... this new function.

From-SVN: r185894
This commit is contained in:
Oleg Endo 2012-03-27 20:14:44 +00:00
parent 719e1e80d4
commit 8c6d71c06b
2 changed files with 129 additions and 91 deletions

View File

@ -1,3 +1,11 @@
2012-03-27 Oleg Endo <olegendo@gcc.gnu.org>
PR target/50751
* config/sh/sh.c (sh_legitimize_address, sh_legitimize_reload_address):
Rearrange conditional logic. Move displacement address calculations
to ...
(sh_find_mov_disp_adjust): ... this new function.
2012-03-27 H.J. Lu <hongjiu.lu@intel.com> 2012-03-27 H.J. Lu <hongjiu.lu@intel.com>
* config/arm/arm.opt (mapcs): Remove MaskExists. * config/arm/arm.opt (mapcs): Remove MaskExists.

View File

@ -9783,13 +9783,80 @@ legitimize_pic_address (rtx orig, enum machine_mode mode ATTRIBUTE_UNUSED,
return orig; return orig;
} }
/* Try machine-dependent ways of modifying an illegitimate address /* Given a (logical) mode size and an offset in bytes, try to find a the
to be legitimate. If we find one, return the new, valid address. appropriate displacement value for a mov insn. On SH the displacements
Otherwise, return X. 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.
For the SH, if X is almost suitable for indexing, but the offset is If the originally requested offset is greater than 127 we prefer using
out of range, convert it into a normal form so that CSE has a chance values 124..127 over 128..131 to increase opportunities to use the
of reducing the number of address registers used. */ 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;
/* 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 static rtx
sh_legitimize_address (rtx x, rtx oldx, enum machine_mode mode) sh_legitimize_address (rtx x, rtx oldx, enum machine_mode mode)
@ -9797,66 +9864,33 @@ sh_legitimize_address (rtx x, rtx oldx, enum machine_mode mode)
if (flag_pic) if (flag_pic)
x = legitimize_pic_address (oldx, mode, NULL_RTX); x = legitimize_pic_address (oldx, mode, NULL_RTX);
if (GET_CODE (x) == PLUS if (TARGET_SHMEDIA)
&& (GET_MODE_SIZE (mode) == 4 return x;
|| GET_MODE_SIZE (mode) == 8)
&& CONST_INT_P (XEXP (x, 1)) if (((TARGET_SH4 || TARGET_SH2A_DOUBLE) && mode == DFmode)
&& BASE_REGISTER_RTX_P (XEXP (x, 0)) || (TARGET_SH2E && mode == SFmode))
&& ! TARGET_SHMEDIA return x;
&& ! ((TARGET_SH4 || TARGET_SH2A_DOUBLE) && mode == DFmode)
&& ! (TARGET_SH2E && mode == SFmode)) if (GET_CODE (x) == PLUS && CONST_INT_P (XEXP (x, 1))
&& BASE_REGISTER_RTX_P (XEXP (x, 0)))
{ {
rtx index_rtx = XEXP (x, 1); const int mode_sz = GET_MODE_SIZE (mode);
HOST_WIDE_INT offset = INTVAL (index_rtx), offset_base; struct disp_adjust adj = sh_find_mov_disp_adjust (mode_sz,
rtx sum; INTVAL (XEXP (x, 1)));
/* On rare occasions, we might get an unaligned pointer if (adj.offset_adjust != NULL_RTX && adj.mov_disp != NULL_RTX)
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);
return gen_rtx_PLUS (Pmode, sum, GEN_INT (offset - offset_base));
}
}
/* 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)))
{
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)
{ {
rtx sum = expand_binop (Pmode, add_optab, XEXP (x, 0), rtx sum = expand_binop (Pmode, add_optab, XEXP (x, 0),
GEN_INT (offset_base), NULL_RTX, 0, adj.offset_adjust, NULL_RTX, 0,
OPTAB_LIB_WIDEN); OPTAB_LIB_WIDEN);
return gen_rtx_PLUS (Pmode, sum, adj.mov_disp);
return gen_rtx_PLUS (Pmode, sum, GEN_INT (offset - offset_base));
} }
} }
return x; 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. 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 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. */ of the address. That will allow inheritance of the address reloads. */
@ -9866,75 +9900,71 @@ sh_legitimize_reload_address (rtx *p, enum machine_mode mode, int opnum,
int itype) int itype)
{ {
enum reload_type type = (enum reload_type) itype; enum reload_type type = (enum reload_type) itype;
const int mode_sz = GET_MODE_SIZE (mode);
if (GET_CODE (*p) == PLUS if (TARGET_SHMEDIA)
&& (GET_MODE_SIZE (mode) == 4 || GET_MODE_SIZE (mode) == 8) return false;
&& CONST_INT_P (XEXP (*p, 1))
if (GET_CODE (*p) == PLUS && CONST_INT_P (XEXP (*p, 1))
&& MAYBE_BASE_REGISTER_RTX_P (XEXP (*p, 0), true) && MAYBE_BASE_REGISTER_RTX_P (XEXP (*p, 0), true)
&& ! TARGET_SHMEDIA
&& ! (TARGET_SH4 && mode == DFmode)
&& ! (mode == PSImode && type == RELOAD_FOR_INPUT_ADDRESS) && ! (mode == PSImode && type == RELOAD_FOR_INPUT_ADDRESS)
&& (ALLOW_INDEXED_ADDRESS && (ALLOW_INDEXED_ADDRESS
|| XEXP (*p, 0) == stack_pointer_rtx || XEXP (*p, 0) == stack_pointer_rtx
|| XEXP (*p, 0) == hard_frame_pointer_rtx)) || XEXP (*p, 0) == hard_frame_pointer_rtx))
{ {
rtx index_rtx = XEXP (*p, 1); const HOST_WIDE_INT offset = INTVAL (XEXP (*p, 1));
HOST_WIDE_INT offset = INTVAL (index_rtx), offset_base; struct disp_adjust adj = sh_find_mov_disp_adjust (mode_sz, offset);
rtx sum;
if (TARGET_SH2A && mode == DFmode && (offset & 0x7)) if (TARGET_SH2A && mode == DFmode && (offset & 0x7))
{ {
push_reload (*p, NULL_RTX, p, NULL, push_reload (*p, NULL_RTX, p, NULL,
BASE_REG_CLASS, Pmode, VOIDmode, 0, 0, opnum, type); BASE_REG_CLASS, Pmode, VOIDmode, 0, 0, opnum, type);
goto win; return true;
} }
if (TARGET_SH2E && mode == SFmode) if (TARGET_SH2E && mode == SFmode)
{ {
*p = copy_rtx (*p); *p = copy_rtx (*p);
push_reload (*p, NULL_RTX, p, NULL, push_reload (*p, NULL_RTX, p, NULL,
BASE_REG_CLASS, Pmode, VOIDmode, 0, 0, opnum, type); 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. */ /* FIXME: Do not allow to legitimize QImode and HImode displacement
if (offset > 127) moves because then reload has a problem figuring the constraint
offset_base = ((offset + 4) & ~60) - 4; that the move insn target/source reg must be R0.
else Or maybe some handling is wrong in sh_secondary_reload for this
offset_base = offset & ~60; to work properly? */
/* Sometimes the normal form does not suit DImode. We could avoid if ((mode_sz == 4 || mode_sz == 8)
that by using smaller ranges, but that would give less optimized && ! (TARGET_SH4 && mode == DFmode)
code when SImode is prevalent. */ && adj.offset_adjust != NULL_RTX && adj.mov_disp != NULL_RTX)
if (GET_MODE_SIZE (mode) + offset - offset_base <= 64)
{ {
sum = gen_rtx_PLUS (Pmode, XEXP (*p, 0), GEN_INT (offset_base)); rtx sum = gen_rtx_PLUS (Pmode, XEXP (*p, 0), adj.offset_adjust);
*p = gen_rtx_PLUS (Pmode, sum, GEN_INT (offset - offset_base)); *p = gen_rtx_PLUS (Pmode, sum, adj.mov_disp);
push_reload (sum, NULL_RTX, &XEXP (*p, 0), NULL, push_reload (sum, NULL_RTX, &XEXP (*p, 0), NULL,
BASE_REG_CLASS, Pmode, VOIDmode, 0, 0, opnum, type); BASE_REG_CLASS, Pmode, VOIDmode, 0, 0, opnum, type);
goto win; return true;
} }
} }
/* We must re-recognize what we created before. */ /* We must re-recognize what we created before. */
else if (GET_CODE (*p) == PLUS if (GET_CODE (*p) == PLUS
&& (GET_MODE_SIZE (mode) == 4 || GET_MODE_SIZE (mode) == 8) && (mode_sz == 4 || mode_sz == 8)
&& GET_CODE (XEXP (*p, 0)) == PLUS && GET_CODE (XEXP (*p, 0)) == PLUS
&& CONST_INT_P (XEXP (XEXP (*p, 0), 1)) && CONST_INT_P (XEXP (XEXP (*p, 0), 1))
&& MAYBE_BASE_REGISTER_RTX_P (XEXP (XEXP (*p, 0), 0), true) && MAYBE_BASE_REGISTER_RTX_P (XEXP (XEXP (*p, 0), 0), true)
&& CONST_INT_P (XEXP (*p, 1)) && CONST_INT_P (XEXP (*p, 1))
&& ! TARGET_SHMEDIA && ! (TARGET_SH2E && mode == SFmode))
&& ! (TARGET_SH2E && mode == SFmode))
{ {
/* Because this address is so complex, we know it must have /* Because this address is so complex, we know it must have
been created by LEGITIMIZE_RELOAD_ADDRESS before; thus, been created by LEGITIMIZE_RELOAD_ADDRESS before; thus,
it is already unshared, and needs no further unsharing. */ it is already unshared, and needs no further unsharing. */
push_reload (XEXP (*p, 0), NULL_RTX, &XEXP (*p, 0), NULL, push_reload (XEXP (*p, 0), NULL_RTX, &XEXP (*p, 0), NULL,
BASE_REG_CLASS, Pmode, VOIDmode, 0, 0, opnum, type); BASE_REG_CLASS, Pmode, VOIDmode, 0, 0, opnum, type);
goto win; return true;
} }
return false; return false;
win:
return true;
} }
/* In the name of slightly smaller debug output, and to cater to /* In the name of slightly smaller debug output, and to cater to