/* Set, if some of the fprs 8-15 need to be saved (64 bit abi). */
int save_fprs_p;
- /* Set if return address needs to be saved because the current
- function uses __builtin_return_addr (0). */
+ /* Set if return address needs to be saved. */
bool save_return_addr_p;
/* Number of first and last gpr to be saved, restored. */
static int get_some_local_dynamic_name_1 (rtx *, void *);
static int reg_used_in_mem_p (int, rtx);
static int addr_generation_dependency_p (rtx, rtx);
-static int s390_split_branches (rtx, bool *);
+static int s390_split_branches (void);
static void find_constant_pool_ref (rtx, rtx *);
static void replace_constant_pool_ref (rtx *, rtx, rtx);
static rtx find_ltrel_base (rtx);
static void replace_ltrel_base (rtx *, rtx);
-static void s390_optimize_prolog (bool, bool);
+static void s390_optimize_prolog (bool);
static int find_unused_clobbered_reg (void);
static void s390_frame_info (void);
static rtx save_fpr (rtx, int, int);
/* Split all branches that exceed the maximum distance.
- Returns true if this created a new literal pool entry.
-
- Code generated by this routine is allowed to use
- TEMP_REG as temporary scratch register. If this is
- done, TEMP_USED is set to true. */
+ Returns true if this created a new literal pool entry. */
static int
-s390_split_branches (rtx temp_reg, bool *temp_used)
+s390_split_branches (void)
{
+ rtx temp_reg = gen_rtx_REG (Pmode, RETURN_REGNUM);
int new_literal = 0;
rtx insn, pat, tmp, target;
rtx *label;
else
continue;
- if (get_attr_length (insn) <= (TARGET_CPU_ZARCH ? 6 : 4))
+ if (get_attr_length (insn) <= 4)
continue;
- *temp_used = 1;
+ /* We are going to use the return register as scratch register,
+ make sure it will be saved/restored by the prologue/epilogue. */
+ cfun->machine->save_return_addr_p = 1;
- if (TARGET_CPU_ZARCH)
- {
- tmp = emit_insn_before (gen_rtx_SET (Pmode, temp_reg, *label), insn);
- INSN_ADDRESSES_NEW (tmp, -1);
-
- target = temp_reg;
- }
- else if (!flag_pic)
+ if (!flag_pic)
{
new_literal = 1;
tmp = force_const_mem (Pmode, *label);
/* Rework the prolog/epilog to avoid saving/restoring
registers unnecessarily. BASE_USED specifies whether
- the literal pool base register needs to be saved,
- TEMP_USED specifies whether the return register needs
- to be saved. */
+ the literal pool base register needs to be saved. */
static void
-s390_optimize_prolog (bool base_used, bool temp_used)
+s390_optimize_prolog (bool base_used)
{
int save_first, save_last, restore_first, restore_last;
int i, j;
/* Recompute regs_ever_live data for special registers. */
regs_ever_live[BASE_REGISTER] = base_used;
- regs_ever_live[RETURN_REGNUM] = temp_used;
+ regs_ever_live[RETURN_REGNUM] = cfun->machine->save_return_addr_p;
regs_ever_live[STACK_POINTER_REGNUM] = cfun->machine->frame_size > 0;
- /* In non-leaf functions, the prolog/epilog code relies
- on RETURN_REGNUM being saved in any case. We also need
- to save the return register if __builtin_return_address (0)
- was used in the current function. */
- if (!current_function_is_leaf
- || cfun->machine->save_return_addr_p)
- regs_ever_live[RETURN_REGNUM] = 1;
-
/* Find first and last gpr to be saved. */
/* If all special registers are in fact used, there's nothing we
can do, so no point in walking the insn list. */
if (i <= BASE_REGISTER && j >= BASE_REGISTER
- && i <= RETURN_REGNUM && j >= RETURN_REGNUM)
+ && (TARGET_CPU_ZARCH || (i <= RETURN_REGNUM && j >= RETURN_REGNUM)))
return;
if (GET_CODE (insn) != INSN)
continue;
- if (GET_CODE (PATTERN (insn)) != PARALLEL)
- continue;
- if (store_multiple_operation (PATTERN (insn), VOIDmode))
+ if (GET_CODE (PATTERN (insn)) == PARALLEL
+ && store_multiple_operation (PATTERN (insn), VOIDmode))
{
set = XVECEXP (PATTERN (insn), 0, 0);
first = REGNO (SET_SRC (set));
if (GET_CODE (base) != REG || off < 0)
continue;
- if (first > BASE_REGISTER && first > RETURN_REGNUM)
+ if (first > BASE_REGISTER || last < BASE_REGISTER)
continue;
- if (last < BASE_REGISTER && last < RETURN_REGNUM)
+
+ if (save_first != -1)
+ {
+ new_insn = save_gprs (base, off, save_first, save_last);
+ new_insn = emit_insn_before (new_insn, insn);
+ INSN_ADDRESSES_NEW (new_insn, -1);
+ }
+
+ remove_insn (insn);
+ continue;
+ }
+
+ if (GET_CODE (PATTERN (insn)) == SET
+ && GET_CODE (SET_SRC (PATTERN (insn))) == REG
+ && REGNO (SET_SRC (PATTERN (insn))) == BASE_REGISTER
+ && GET_CODE (SET_DEST (PATTERN (insn))) == MEM)
+ {
+ set = PATTERN (insn);
+ offset = const0_rtx;
+ base = eliminate_constant_term (XEXP (SET_DEST (set), 0), &offset);
+ off = INTVAL (offset) - BASE_REGISTER * UNITS_PER_WORD;
+
+ if (GET_CODE (base) != REG || off < 0)
continue;
if (save_first != -1)
}
remove_insn (insn);
+ continue;
}
- if (load_multiple_operation (PATTERN (insn), VOIDmode))
+ if (GET_CODE (PATTERN (insn)) == PARALLEL
+ && load_multiple_operation (PATTERN (insn), VOIDmode))
{
set = XVECEXP (PATTERN (insn), 0, 0);
first = REGNO (SET_DEST (set));
if (GET_CODE (base) != REG || off < 0)
continue;
- if (first > BASE_REGISTER && first > RETURN_REGNUM)
+ if (first > BASE_REGISTER || last < BASE_REGISTER)
continue;
- if (last < BASE_REGISTER && last < RETURN_REGNUM)
+
+ if (restore_first != -1)
+ {
+ new_insn = restore_gprs (base, off, restore_first, restore_last);
+ new_insn = emit_insn_before (new_insn, insn);
+ INSN_ADDRESSES_NEW (new_insn, -1);
+ }
+
+ remove_insn (insn);
+ continue;
+ }
+
+ if (GET_CODE (PATTERN (insn)) == SET
+ && GET_CODE (SET_DEST (PATTERN (insn))) == REG
+ && REGNO (SET_DEST (PATTERN (insn))) == BASE_REGISTER
+ && GET_CODE (SET_SRC (PATTERN (insn))) == MEM)
+ {
+ set = PATTERN (insn);
+ offset = const0_rtx;
+ base = eliminate_constant_term (XEXP (SET_SRC (set), 0), &offset);
+ off = INTVAL (offset) - BASE_REGISTER * UNITS_PER_WORD;
+
+ if (GET_CODE (base) != REG || off < 0)
continue;
if (restore_first != -1)
}
remove_insn (insn);
+ continue;
}
}
}
static void
s390_reorg (void)
{
- rtx temp_reg = gen_rtx_REG (Pmode, RETURN_REGNUM);
rtx base_reg = gen_rtx_REG (Pmode, BASE_REGISTER);
- bool temp_used = false;
bool base_used = false;
bool pool_overflow = false;
/* Split out-of-range branches. If this has created new
literal pool entries, cancel current chunk list and
- recompute it. */
- if (s390_split_branches (temp_reg, &temp_used))
+ recompute it. zSeries machines have large branch
+ instructions, so we never need to split a branch. */
+ if (!TARGET_CPU_ZARCH && s390_split_branches ())
{
if (pool_overflow)
s390_chunkify_cancel (pool);
break;
}
- s390_optimize_prolog (base_used, temp_used);
+ s390_optimize_prolog (base_used);
}
static void
s390_frame_info (void)
{
- char gprs_ever_live[16];
int i, j;
HOST_WIDE_INT fsize = get_frame_size ();
|| current_function_stdarg)
cfun->machine->frame_size += STARTING_FRAME_OFFSET;
+ /* If we use the return register, we'll need to make sure
+ it is going to be saved/restored. */
+
+ if (!current_function_is_leaf
+ || regs_ever_live[RETURN_REGNUM])
+ cfun->machine->save_return_addr_p = 1;
+
/* Find first and last gpr to be saved. Note that at this point,
- we assume the return register and the base register always
- need to be saved. This is done because the usage of these
+ we assume the base register and -on S/390- the return register
+ always need to be saved. This is done because the usage of these
register might change even after the prolog was emitted.
If it turns out later that we really don't need them, the
prolog/epilog code is modified again. */
- for (i = 0; i < 16; i++)
- gprs_ever_live[i] = regs_ever_live[i] && !global_regs[i];
-
- if (flag_pic)
- gprs_ever_live[PIC_OFFSET_TABLE_REGNUM] =
- regs_ever_live[PIC_OFFSET_TABLE_REGNUM];
- gprs_ever_live[BASE_REGISTER] = 1;
- gprs_ever_live[RETURN_REGNUM] = 1;
- gprs_ever_live[STACK_POINTER_REGNUM] = cfun->machine->frame_size > 0;
+ regs_ever_live[BASE_REGISTER] = 1;
+ if (!TARGET_CPU_ZARCH || cfun->machine->save_return_addr_p)
+ regs_ever_live[RETURN_REGNUM] = 1;
+ regs_ever_live[STACK_POINTER_REGNUM] = cfun->machine->frame_size > 0;
for (i = 6; i < 16; i++)
- if (gprs_ever_live[i])
- break;
+ if (regs_ever_live[i])
+ if (!global_regs[i]
+ || i == STACK_POINTER_REGNUM
+ || i == RETURN_REGNUM
+ || i == BASE_REGISTER
+ || (flag_pic && i == (int)PIC_OFFSET_TABLE_REGNUM))
+ break;
for (j = 15; j > i; j--)
- if (gprs_ever_live[j])
- break;
-
+ if (regs_ever_live[j])
+ if (!global_regs[j]
+ || j == STACK_POINTER_REGNUM
+ || j == RETURN_REGNUM
+ || j == BASE_REGISTER
+ || (flag_pic && j == (int)PIC_OFFSET_TABLE_REGNUM))
+ break;
/* Save / Restore from gpr i to j. */
cfun->machine->first_save_gpr = i;
/* Fetch return address from stack before load multiple,
this will do good for scheduling. */
- if (!current_function_is_leaf)
+ if (cfun->machine->save_return_addr_p)
{
int return_regnum = find_unused_clobbered_reg();
if (!return_regnum)
GPRs 6-15 are always call-saved.
GPR 12 is fixed if used as GOT pointer.
GPR 13 is always fixed (as literal pool pointer).
- GPR 14 is always fixed (as return address).
+ GPR 14 is always fixed on S/390 machines (as return address).
GPR 15 is always fixed (as stack pointer).
The 'fake' hard registers are call-clobbered and fixed.
fixed_regs[PIC_OFFSET_TABLE_REGNUM] = 1; \
call_used_regs[PIC_OFFSET_TABLE_REGNUM] = 1; \
} \
+ if (TARGET_CPU_ZARCH) \
+ { \
+ fixed_regs[RETURN_REGNUM] = 0; \
+ call_used_regs[RETURN_REGNUM] = 0; \
+ } \
if (TARGET_64BIT) \
{ \
for (i = 24; i < 32; i++) \
/* Preferred register allocation order. */
#define REG_ALLOC_ORDER \
-{ 1, 2, 3, 4, 5, 0, 14, 13, 12, 11, 10, 9, 8, 7, 6, \
+{ 1, 2, 3, 4, 5, 0, 13, 12, 11, 10, 9, 8, 7, 6, 14, \
16, 17, 18, 19, 20, 21, 22, 23, \
24, 25, 26, 27, 28, 29, 30, 31, \
15, 32, 33, 34 }