summaryrefslogtreecommitdiff
path: root/gcc/config/s390/s390.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/config/s390/s390.c')
-rw-r--r--gcc/config/s390/s390.c207
1 files changed, 176 insertions, 31 deletions
diff --git a/gcc/config/s390/s390.c b/gcc/config/s390/s390.c
index acec015ddbb..f748374d511 100644
--- a/gcc/config/s390/s390.c
+++ b/gcc/config/s390/s390.c
@@ -204,6 +204,7 @@ static int s390_branch_condition_mask PARAMS ((rtx));
static const char *s390_branch_condition_mnemonic PARAMS ((rtx, int));
static int check_mode PARAMS ((rtx, enum machine_mode *));
static int general_s_operand PARAMS ((rtx, enum machine_mode, int));
+static int s390_short_displacement PARAMS ((rtx));
static int s390_decompose_address PARAMS ((rtx, struct s390_address *));
static rtx get_thread_pointer PARAMS ((void));
static rtx legitimize_tls_address PARAMS ((rtx, rtx));
@@ -228,6 +229,11 @@ static rtx restore_gprs PARAMS ((rtx, int, int, int));
static int s390_function_arg_size PARAMS ((enum machine_mode, tree));
static bool s390_function_arg_float PARAMS ((enum machine_mode, tree));
static struct machine_function * s390_init_machine_status PARAMS ((void));
+
+/* Check whether integer displacement is in range. */
+#define DISP_IN_RANGE(d) \
+ (TARGET_LONG_DISPLACEMENT? ((d) >= -524288 && (d) <= 524287) \
+ : ((d) >= 0 && (d) <= 4095))
/* Return true if SET either doesn't set the CC register, or else
the source and destination have matching CC modes and that
@@ -1103,12 +1109,17 @@ larl_operand (op, mode)
return 0;
op = XEXP (op, 0);
- /* Allow adding *even* constants. */
+ /* Allow adding *even* in-range constants. */
if (GET_CODE (op) == PLUS)
{
if (GET_CODE (XEXP (op, 1)) != CONST_INT
|| (INTVAL (XEXP (op, 1)) & 1) != 0)
return 0;
+#if HOST_BITS_PER_WIDE_INT > 32
+ if (INTVAL (XEXP (op, 1)) >= (HOST_WIDE_INT)1 << 32
+ || INTVAL (XEXP (op, 1)) < -((HOST_WIDE_INT)1 << 32))
+ return 0;
+#endif
op = XEXP (op, 0);
}
@@ -1221,24 +1232,121 @@ s_imm_operand (op, mode)
return general_s_operand (op, mode, 1);
}
-/* Return true if OP is a valid operand for a 'Q' constraint.
- This differs from s_operand in that only memory operands
- without index register are accepted, nothing else. */
+/* Return true if DISP is a valid short displacement. */
+
+static int
+s390_short_displacement (disp)
+ rtx disp;
+{
+ /* No displacement is OK. */
+ if (!disp)
+ return 1;
+
+ /* Integer displacement in range. */
+ if (GET_CODE (disp) == CONST_INT)
+ return INTVAL (disp) >= 0 && INTVAL (disp) < 4096;
+
+ /* GOT offset is not OK, the GOT can be large. */
+ if (GET_CODE (disp) == CONST
+ && GET_CODE (XEXP (disp, 0)) == UNSPEC
+ && XINT (XEXP (disp, 0), 1) == 110)
+ return 0;
+
+ /* All other symbolic constants are literal pool references,
+ which are OK as the literal pool must be small. */
+ if (GET_CODE (disp) == CONST)
+ return 1;
+
+ return 0;
+}
+
+/* Return true if OP is a valid operand for a C constraint. */
int
-q_constraint (op)
- register rtx op;
+s390_extra_constraint (op, c)
+ rtx op;
+ int c;
{
struct s390_address addr;
- if (GET_CODE (op) != MEM)
- return 0;
+ switch (c)
+ {
+ case 'Q':
+ if (GET_CODE (op) != MEM)
+ return 0;
+ if (!s390_decompose_address (XEXP (op, 0), &addr))
+ return 0;
+ if (addr.indx)
+ return 0;
- if (!s390_decompose_address (XEXP (op, 0), &addr))
- return 0;
+ if (TARGET_LONG_DISPLACEMENT)
+ {
+ if (!s390_short_displacement (addr.disp))
+ return 0;
+ }
+ break;
- if (addr.indx)
- return 0;
+ case 'R':
+ if (GET_CODE (op) != MEM)
+ return 0;
+
+ if (TARGET_LONG_DISPLACEMENT)
+ {
+ if (!s390_decompose_address (XEXP (op, 0), &addr))
+ return 0;
+ if (!s390_short_displacement (addr.disp))
+ return 0;
+ }
+ break;
+
+ case 'S':
+ if (!TARGET_LONG_DISPLACEMENT)
+ return 0;
+ if (GET_CODE (op) != MEM)
+ return 0;
+ if (!s390_decompose_address (XEXP (op, 0), &addr))
+ return 0;
+ if (addr.indx)
+ return 0;
+ if (s390_short_displacement (addr.disp))
+ return 0;
+ break;
+
+ case 'T':
+ if (!TARGET_LONG_DISPLACEMENT)
+ return 0;
+ if (GET_CODE (op) != MEM)
+ return 0;
+ /* Any invalid address here will be fixed up by reload,
+ so accept it for the most generic constraint. */
+ if (s390_decompose_address (XEXP (op, 0), &addr)
+ && s390_short_displacement (addr.disp))
+ return 0;
+ break;
+
+ case 'U':
+ if (TARGET_LONG_DISPLACEMENT)
+ {
+ if (!s390_decompose_address (op, &addr))
+ return 0;
+ if (!s390_short_displacement (addr.disp))
+ return 0;
+ }
+ break;
+
+ case 'W':
+ if (!TARGET_LONG_DISPLACEMENT)
+ return 0;
+ /* Any invalid address here will be fixed up by reload,
+ so accept it for the most generic constraint. */
+ if (s390_decompose_address (op, &addr)
+ && s390_short_displacement (addr.disp))
+ return 0;
+ break;
+
+ default:
+ return 0;
+ }
return 1;
}
@@ -1673,6 +1781,11 @@ int
legitimate_reload_constant_p (op)
register rtx op;
{
+ /* Accept la(y) operands. */
+ if (GET_CODE (op) == CONST_INT
+ && DISP_IN_RANGE (INTVAL (op)))
+ return 1;
+
/* Accept l(g)hi operands. */
if (GET_CODE (op) == CONST_INT
&& CONST_OK_FOR_LETTER_P (INTVAL (op), 'K'))
@@ -2016,7 +2129,7 @@ s390_decompose_address (addr, out)
this is fixed up by reload in any case. */
if (base != arg_pointer_rtx && indx != arg_pointer_rtx)
{
- if (INTVAL (disp) < 0 || INTVAL (disp) >= 4096)
+ if (!DISP_IN_RANGE (INTVAL (disp)))
return FALSE;
}
}
@@ -2395,7 +2508,7 @@ legitimize_pic_address (orig, reg)
pair of LARL and LA. */
rtx temp = reg? reg : gen_reg_rtx (Pmode);
- if (INTVAL (op1) < 0 || INTVAL (op1) >= 4096)
+ if (!DISP_IN_RANGE (INTVAL (op1)))
{
int even = INTVAL (op1) - 1;
op0 = gen_rtx_PLUS (Pmode, op0, GEN_INT (even));
@@ -2766,8 +2879,8 @@ legitimize_address (x, oldx, mode)
change later anyway. */
if (GET_CODE (constant_term) == CONST_INT
- && (INTVAL (constant_term) < 0
- || INTVAL (constant_term) >= 4096)
+ && !TARGET_LONG_DISPLACEMENT
+ && !DISP_IN_RANGE (INTVAL (constant_term))
&& !(REG_P (x) && REGNO_PTR_FRAME_P (REGNO (x))))
{
HOST_WIDE_INT lower = INTVAL (constant_term) & 0xfff;
@@ -5050,7 +5163,7 @@ s390_fixup_clobbered_return_reg (return_reg)
reg = stack_pointer_rtx;
off = GEN_INT (cfun->machine->frame_size + REGNO (return_reg) * UNITS_PER_WORD);
- if (INTVAL (off) >= 4096)
+ if (!DISP_IN_RANGE (INTVAL (off)))
{
off = force_const_mem (Pmode, off);
new_insn = gen_rtx_SET (Pmode, return_reg, off);
@@ -5534,11 +5647,21 @@ s390_emit_prologue ()
/* Substract frame size from stack pointer. */
- frame_off = GEN_INT (-cfun->machine->frame_size);
- if (!CONST_OK_FOR_LETTER_P (-cfun->machine->frame_size, 'K'))
- frame_off = force_const_mem (Pmode, frame_off);
+ if (DISP_IN_RANGE (INTVAL (frame_off)))
+ {
+ insn = gen_rtx_SET (VOIDmode, stack_pointer_rtx,
+ gen_rtx_PLUS (Pmode, stack_pointer_rtx,
+ frame_off));
+ insn = emit_insn (insn);
+ }
+ else
+ {
+ if (!CONST_OK_FOR_LETTER_P (INTVAL (frame_off), 'K'))
+ frame_off = force_const_mem (Pmode, frame_off);
+
+ insn = emit_insn (gen_add2_insn (stack_pointer_rtx, frame_off));
+ }
- insn = emit_insn (gen_add2_insn (stack_pointer_rtx, frame_off));
RTX_FRAME_RELATED_P (insn) = 1;
REG_NOTES (insn) =
gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
@@ -5697,8 +5820,8 @@ s390_emit_epilogue ()
{
/* Nothing to restore. */
}
- else if (cfun->machine->frame_size + area_bottom >= 0
- && cfun->machine->frame_size + area_top <= 4096)
+ else if (DISP_IN_RANGE (cfun->machine->frame_size + area_bottom)
+ && DISP_IN_RANGE (cfun->machine->frame_size + area_top-1))
{
/* Area is in range. */
offset = cfun->machine->frame_size;
@@ -5710,10 +5833,19 @@ s390_emit_epilogue ()
offset = area_bottom < 0 ? -area_bottom : 0;
frame_off = GEN_INT (cfun->machine->frame_size - offset);
- if (!CONST_OK_FOR_LETTER_P (INTVAL (frame_off), 'K'))
- frame_off = force_const_mem (Pmode, frame_off);
+ if (DISP_IN_RANGE (INTVAL (frame_off)))
+ {
+ insn = gen_rtx_SET (VOIDmode, frame_pointer,
+ gen_rtx_PLUS (Pmode, frame_pointer, frame_off));
+ insn = emit_insn (insn);
+ }
+ else
+ {
+ if (!CONST_OK_FOR_LETTER_P (INTVAL (frame_off), 'K'))
+ frame_off = force_const_mem (Pmode, frame_off);
- insn = emit_insn (gen_add2_insn (frame_pointer, frame_off));
+ insn = emit_insn (gen_add2_insn (frame_pointer, frame_off));
+ }
}
/* Restore call saved fprs. */
@@ -6649,8 +6781,10 @@ s390_output_mi_thunk (file, thunk, delta, vcall_offset, function)
if (TARGET_64BIT)
{
/* Setup literal pool pointer if required. */
- if (!CONST_OK_FOR_LETTER_P (delta, 'K')
- || !CONST_OK_FOR_LETTER_P (vcall_offset, 'K'))
+ if ((!DISP_IN_RANGE (delta)
+ && !CONST_OK_FOR_LETTER_P (delta, 'K'))
+ || (!DISP_IN_RANGE (vcall_offset)
+ && !CONST_OK_FOR_LETTER_P (vcall_offset, 'K')))
{
op[5] = gen_label_rtx ();
output_asm_insn ("larl\t%4,%5", op);
@@ -6661,6 +6795,8 @@ s390_output_mi_thunk (file, thunk, delta, vcall_offset, function)
{
if (CONST_OK_FOR_LETTER_P (delta, 'J'))
output_asm_insn ("la\t%1,%2(%1)", op);
+ else if (DISP_IN_RANGE (delta))
+ output_asm_insn ("lay\t%1,%2(%1)", op);
else if (CONST_OK_FOR_LETTER_P (delta, 'K'))
output_asm_insn ("aghi\t%1,%2", op);
else
@@ -6673,7 +6809,7 @@ s390_output_mi_thunk (file, thunk, delta, vcall_offset, function)
/* Perform vcall adjustment. */
if (vcall_offset)
{
- if (CONST_OK_FOR_LETTER_P (vcall_offset, 'J'))
+ if (DISP_IN_RANGE (vcall_offset))
{
output_asm_insn ("lg\t%4,0(%1)", op);
output_asm_insn ("ag\t%1,%3(%4)", op);
@@ -6720,8 +6856,10 @@ s390_output_mi_thunk (file, thunk, delta, vcall_offset, function)
{
/* Setup base pointer if required. */
if (!vcall_offset
- || !CONST_OK_FOR_LETTER_P (delta, 'K')
- || !CONST_OK_FOR_LETTER_P (vcall_offset, 'K'))
+ || (!DISP_IN_RANGE (delta)
+ && !CONST_OK_FOR_LETTER_P (delta, 'K'))
+ || (!DISP_IN_RANGE (delta)
+ && !CONST_OK_FOR_LETTER_P (vcall_offset, 'K')))
{
op[5] = gen_label_rtx ();
output_asm_insn ("basr\t%4,0", op);
@@ -6734,6 +6872,8 @@ s390_output_mi_thunk (file, thunk, delta, vcall_offset, function)
{
if (CONST_OK_FOR_LETTER_P (delta, 'J'))
output_asm_insn ("la\t%1,%2(%1)", op);
+ else if (DISP_IN_RANGE (delta))
+ output_asm_insn ("lay\t%1,%2(%1)", op);
else if (CONST_OK_FOR_LETTER_P (delta, 'K'))
output_asm_insn ("ahi\t%1,%2", op);
else
@@ -6751,6 +6891,11 @@ s390_output_mi_thunk (file, thunk, delta, vcall_offset, function)
output_asm_insn ("lg\t%4,0(%1)", op);
output_asm_insn ("a\t%1,%3(%4)", op);
}
+ else if (DISP_IN_RANGE (vcall_offset))
+ {
+ output_asm_insn ("lg\t%4,0(%1)", op);
+ output_asm_insn ("ay\t%1,%3(%4)", op);
+ }
else if (CONST_OK_FOR_LETTER_P (vcall_offset, 'K'))
{
output_asm_insn ("lhi\t%4,%3", op);