Commit aee4e0db by Ulrich Weigand Committed by Ulrich Weigand

s390.c (s390_split_branches): Add return value.

	* config/s390/s390.c (s390_split_branches): Add return
	value.  Add parameters TEMP_REG and TEMP_USED.  Use unspec 104.

	(find_base_register_in_addr): New function.
	(find_base_register_ref): New function.
	(replace_base_register_ref): New function.

	(struct constant_pool): Add members pool_insn, insns, and anchor.
	Remove member last_insn.
	(s390_start_pool): Initialize them.
	(s390_end_pool): Emit pool placeholder insn.
	(s390_add_pool_insn): New function.
	(s390_find_pool): Use insns bitmap instead of addresses.
	(s390_dump_pool): Replace placeholder insn.  Emit anchor.
	Replace unspec 104 by local-pool-relative references.
	(s390_output_constant_pool): Output anchor label if required.
	(s390_output_symbolic_const): Handle unspec 104 and 105.
	(s390_add_pool): Remove, replace by ...
	(s390_add_constant, s390_find_constant): ... these new functions.
	(s390_add_anchor): New function.

	(s390_chunkify_pool): Delete, replace by ...
	(s390_chunkify_start, s390_chunkify_finish,
	s390_chunkify_cancel): ... these new functions.
	(s390_optimize_prolog): Add parameter TEMP_REGNO.
	Recompute register live data for special registers.
	(s390_fixup_clobbered_return_reg): New function.
	(s390_machine_dependent_reorg): Rewrite to use new
	s390_chunkify_... routines.

	config/s390/s390.md ("reload_base"): Rename to ...
	("reload_base_31"): ... this.
	("reload_base_64"): New insn.
	("reload_base2"): Remove.
	("reload_anchor"): New insn.
	("pool"): New insn.

	s390.c (s390_pool_overflow): Remove.
	s390.h (s390_pool_overflow): Likewise.
	s390.md ("cjump", "icjump", "doloop_si"): Remove s390_pool_overflow.

From-SVN: r58168
parent 6742d92b
2002-10-15 Ulrich Weigand <uweigand@de.ibm.com>
* config/s390/s390.c (s390_split_branches): Add return
value. Add parameters TEMP_REG and TEMP_USED. Use unspec 104.
(find_base_register_in_addr): New function.
(find_base_register_ref): New function.
(replace_base_register_ref): New function.
(struct constant_pool): Add members pool_insn, insns, and anchor.
Remove member last_insn.
(s390_start_pool): Initialize them.
(s390_end_pool): Emit pool placeholder insn.
(s390_add_pool_insn): New function.
(s390_find_pool): Use insns bitmap instead of addresses.
(s390_dump_pool): Replace placeholder insn. Emit anchor.
Replace unspec 104 by local-pool-relative references.
(s390_output_constant_pool): Output anchor label if required.
(s390_output_symbolic_const): Handle unspec 104 and 105.
(s390_add_pool): Remove, replace by ...
(s390_add_constant, s390_find_constant): ... these new functions.
(s390_add_anchor): New function.
(s390_chunkify_pool): Delete, replace by ...
(s390_chunkify_start, s390_chunkify_finish,
s390_chunkify_cancel): ... these new functions.
(s390_optimize_prolog): Add parameter TEMP_REGNO.
Recompute register live data for special registers.
(s390_fixup_clobbered_return_reg): New function.
(s390_machine_dependent_reorg): Rewrite to use new
s390_chunkify_... routines.
config/s390/s390.md ("reload_base"): Rename to ...
("reload_base_31"): ... this.
("reload_base_64"): New insn.
("reload_base2"): Remove.
("reload_anchor"): New insn.
("pool"): New insn.
s390.c (s390_pool_overflow): Remove.
s390.h (s390_pool_overflow): Likewise.
s390.md ("cjump", "icjump", "doloop_si"): Remove s390_pool_overflow.
Tue Oct 15 16:51:04 2002 J"orn Rennecke <joern.rennecke@superh.com>
* sh.md (movv8qi_i+2): Don't split if source is -1.
......
......@@ -130,11 +130,14 @@ static int general_s_operand PARAMS ((rtx, enum machine_mode, int));
static int s390_decompose_address PARAMS ((rtx, struct s390_address *));
static int reg_used_in_mem_p PARAMS ((int, rtx));
static int addr_generation_dependency_p PARAMS ((rtx, rtx));
static void s390_split_branches PARAMS ((void));
static int s390_split_branches PARAMS ((rtx, bool *));
static void find_constant_pool_ref PARAMS ((rtx, rtx *));
static void replace_constant_pool_ref PARAMS ((rtx *, rtx, rtx));
static void s390_chunkify_pool PARAMS ((void));
static void s390_optimize_prolog PARAMS ((void));
static int find_base_register_in_addr PARAMS ((struct s390_address *));
static bool find_base_register_ref PARAMS ((rtx));
static void replace_base_register_ref PARAMS ((rtx *, rtx));
static void s390_optimize_prolog PARAMS ((int));
static bool s390_fixup_clobbered_return_reg PARAMS ((rtx));
static int find_unused_clobbered_reg PARAMS ((void));
static void s390_frame_info PARAMS ((struct s390_frame *));
static rtx save_fpr PARAMS ((rtx, int, int));
......@@ -2569,9 +2572,14 @@ s390_output_symbolic_const (file, x)
switch (XINT (x, 1))
{
case 100:
case 104:
s390_output_symbolic_const (file, XVECEXP (x, 0, 0));
fprintf (file, "-.LT%d", current_function_funcdef_no);
break;
case 105:
fprintf (file, ".LT%d-", current_function_funcdef_no);
s390_output_symbolic_const (file, XVECEXP (x, 0, 0));
break;
case 110:
s390_output_symbolic_const (file, XVECEXP (x, 0, 0));
fprintf (file, "@GOT12");
......@@ -2985,12 +2993,19 @@ s390_adjust_priority (insn, priority)
}
/* Split all branches that exceed the maximum distance. */
/* 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. */
static void
s390_split_branches ()
static int
s390_split_branches (temp_reg, temp_used)
rtx temp_reg;
bool *temp_used;
{
rtx temp_reg = gen_rtx_REG (Pmode, RETURN_REGNUM);
int new_literal = 0;
rtx insn, pat, tmp, target;
rtx *label;
......@@ -3030,7 +3045,7 @@ s390_split_branches ()
if (get_attr_length (insn) <= (TARGET_64BIT ? 6 : 4))
continue;
regs_ever_live[RETURN_REGNUM] = 1;
*temp_used = 1;
if (TARGET_64BIT)
{
......@@ -3041,6 +3056,7 @@ s390_split_branches ()
}
else if (!flag_pic)
{
new_literal = 1;
tmp = force_const_mem (Pmode, *label);
tmp = emit_insn_before (gen_rtx_SET (Pmode, temp_reg, tmp), insn);
INSN_ADDRESSES_NEW (tmp, -1);
......@@ -3049,7 +3065,8 @@ s390_split_branches ()
}
else
{
tmp = gen_rtx_UNSPEC (SImode, gen_rtvec (1, *label), 100);
new_literal = 1;
tmp = gen_rtx_UNSPEC (SImode, gen_rtvec (1, *label), 104);
tmp = gen_rtx_CONST (SImode, tmp);
tmp = force_const_mem (SImode, tmp);
tmp = emit_insn_before (gen_rtx_SET (Pmode, temp_reg, tmp), insn);
......@@ -3062,6 +3079,8 @@ s390_split_branches ()
if (!validate_change (insn, label, target, 0))
abort ();
}
return new_literal;
}
......@@ -3179,6 +3198,153 @@ replace_constant_pool_ref (x, ref, addr)
}
}
/* Check whether ADDR is an address that uses the base register,
without actually constituting a literal pool access. (This happens
in 31-bit PIC mode, where the base register is used as anchor for
relative addressing of local symbols.)
Returns 1 if the base register occupies the base slot,
returns 2 if the base register occupies the index slot,
returns 0 if the address is not of this form. */
static int
find_base_register_in_addr (addr)
struct s390_address *addr;
{
/* If DISP is complex, we might have a literal pool reference. */
if (addr->disp && GET_CODE (addr->disp) != CONST_INT)
return 0;
if (addr->base && REG_P (addr->base) && REGNO (addr->base) == BASE_REGISTER)
return 1;
if (addr->indx && REG_P (addr->indx) && REGNO (addr->indx) == BASE_REGISTER)
return 2;
return 0;
}
/* Return true if X contains an address that uses the base register,
without actually constituting a literal pool access. */
static bool
find_base_register_ref (x)
rtx x;
{
bool retv = FALSE;
struct s390_address addr;
int i, j;
const char *fmt;
/* Addresses can only occur inside a MEM ... */
if (GET_CODE (x) == MEM)
{
if (s390_decompose_address (XEXP (x, 0), &addr)
&& find_base_register_in_addr (&addr))
return TRUE;
}
/* ... or a load-address type pattern. */
if (GET_CODE (x) == SET && GET_CODE (SET_DEST (x)) == REG)
{
if (s390_decompose_address (SET_SRC (x), &addr)
&& find_base_register_in_addr (&addr))
return TRUE;
}
fmt = GET_RTX_FORMAT (GET_CODE (x));
for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
{
retv |= find_base_register_ref (XEXP (x, i));
}
else if (fmt[i] == 'E')
{
for (j = 0; j < XVECLEN (x, i); j++)
retv |= find_base_register_ref (XVECEXP (x, i, j));
}
}
return retv;
}
/* If X contains an address that uses the base register,
without actually constituting a literal pool access,
replace the base register with REPL in all such cases.
Handles both MEMs and load address patterns. */
static void
replace_base_register_ref (x, repl)
rtx *x;
rtx repl;
{
struct s390_address addr;
rtx new_addr;
int i, j, pos;
const char *fmt;
/* Addresses can only occur inside a MEM ... */
if (GET_CODE (*x) == MEM)
{
if (s390_decompose_address (XEXP (*x, 0), &addr)
&& (pos = find_base_register_in_addr (&addr)))
{
if (pos == 1)
addr.base = repl;
else
addr.indx = repl;
new_addr = addr.base;
if (addr.indx)
new_addr = gen_rtx_PLUS (Pmode, new_addr, addr.indx);
if (addr.disp)
new_addr = gen_rtx_PLUS (Pmode, new_addr, addr.disp);
*x = replace_equiv_address (*x, new_addr);
return;
}
}
/* ... or a load-address type pattern. */
if (GET_CODE (*x) == SET && GET_CODE (SET_DEST (*x)) == REG)
{
if (s390_decompose_address (SET_SRC (*x), &addr)
&& (pos = find_base_register_in_addr (&addr)))
{
if (pos == 1)
addr.base = repl;
else
addr.indx = repl;
new_addr = addr.base;
if (addr.indx)
new_addr = gen_rtx_PLUS (Pmode, new_addr, addr.indx);
if (addr.disp)
new_addr = gen_rtx_PLUS (Pmode, new_addr, addr.disp);
SET_SRC (*x) = new_addr;
return;
}
}
fmt = GET_RTX_FORMAT (GET_CODE (*x));
for (i = GET_RTX_LENGTH (GET_CODE (*x)) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
{
replace_base_register_ref (&XEXP (*x, i), repl);
}
else if (fmt[i] == 'E')
{
for (j = 0; j < XVECLEN (*x, i); j++)
replace_base_register_ref (&XVECEXP (*x, i, j), repl);
}
}
}
/* We keep a list of constants we which we have to add to internal
constant tables in the middle of large functions. */
......@@ -3210,17 +3376,26 @@ struct constant_pool
{
struct constant_pool *next;
rtx first_insn;
rtx last_insn;
rtx pool_insn;
bitmap insns;
struct constant *constants[NR_C_MODES];
rtx label;
int size;
bool anchor;
};
static struct constant_pool * s390_chunkify_start PARAMS ((rtx, bool *));
static void s390_chunkify_finish PARAMS ((struct constant_pool *, rtx));
static void s390_chunkify_cancel PARAMS ((struct constant_pool *));
static struct constant_pool *s390_start_pool PARAMS ((struct constant_pool **, rtx));
static void s390_end_pool PARAMS ((struct constant_pool *, rtx));
static void s390_add_pool_insn PARAMS ((struct constant_pool *, rtx));
static struct constant_pool *s390_find_pool PARAMS ((struct constant_pool *, rtx));
static rtx s390_add_pool PARAMS ((struct constant_pool *, rtx, enum machine_mode));
static void s390_add_constant PARAMS ((struct constant_pool *, rtx, enum machine_mode));
static rtx s390_find_constant PARAMS ((struct constant_pool *, rtx, enum machine_mode));
static void s390_add_anchor PARAMS ((struct constant_pool *));
static rtx s390_dump_pool PARAMS ((struct constant_pool *));
static void s390_free_pool PARAMS ((struct constant_pool *));
......@@ -3242,9 +3417,11 @@ s390_start_pool (pool_list, insn)
pool->label = gen_label_rtx ();
pool->first_insn = insn;
pool->last_insn = NULL_RTX;
pool->pool_insn = NULL_RTX;
pool->insns = BITMAP_XMALLOC ();
pool->size = 0;
pool->anchor = FALSE;
for (prev = pool_list; *prev; prev = &(*prev)->next)
;
*prev = pool;
......@@ -3252,14 +3429,31 @@ s390_start_pool (pool_list, insn)
return pool;
}
/* End range of instructions covered by POOL at INSN. */
/* End range of instructions covered by POOL at INSN and emit
placeholder insn representing the pool. */
static void
s390_end_pool (pool, insn)
struct constant_pool *pool;
rtx insn;
{
pool->last_insn = insn;
rtx pool_size = GEN_INT (pool->size + 8 /* alignment slop */);
if (!insn)
insn = get_last_insn ();
pool->pool_insn = emit_insn_after (gen_pool (pool_size), insn);
INSN_ADDRESSES_NEW (pool->pool_insn, -1);
}
/* Add INSN to the list of insns covered by POOL. */
static void
s390_add_pool_insn (pool, insn)
struct constant_pool *pool;
rtx insn;
{
bitmap_set_bit (pool->insns, INSN_UID (insn));
}
/* Return pool out of POOL_LIST that covers INSN. */
......@@ -3269,33 +3463,24 @@ s390_find_pool (pool_list, insn)
struct constant_pool *pool_list;
rtx insn;
{
int addr = INSN_ADDRESSES (INSN_UID (insn));
struct constant_pool *pool;
if (addr == -1)
return NULL;
for (pool = pool_list; pool; pool = pool->next)
if (INSN_ADDRESSES (INSN_UID (pool->first_insn)) <= addr
&& (pool->last_insn == NULL_RTX
|| INSN_ADDRESSES (INSN_UID (pool->last_insn)) > addr))
if (bitmap_bit_p (pool->insns, INSN_UID (insn)))
break;
return pool;
}
/* Add constant VAL of mode MODE to the constant pool POOL.
Return an RTX describing the distance from the start of
the pool to the location of the new constant. */
/* Add constant VAL of mode MODE to the constant pool POOL. */
static rtx
s390_add_pool (pool, val, mode)
static void
s390_add_constant (pool, val, mode)
struct constant_pool *pool;
rtx val;
enum machine_mode mode;
{
struct constant *c;
rtx offset;
int i;
for (i = 0; i < NR_C_MODES; i++)
......@@ -3317,13 +3502,54 @@ s390_add_pool (pool, val, mode)
pool->constants[i] = c;
pool->size += GET_MODE_SIZE (mode);
}
}
offset = gen_rtx_MINUS (Pmode, gen_rtx_LABEL_REF (Pmode, c->label),
gen_rtx_LABEL_REF (Pmode, pool->label));
/* Find constant VAL of mode MODE in the constant pool POOL.
Return an RTX describing the distance from the start of
the pool to the location of the new constant. */
static rtx
s390_find_constant (pool, val, mode)
struct constant_pool *pool;
rtx val;
enum machine_mode mode;
{
struct constant *c;
rtx offset;
int i;
for (i = 0; i < NR_C_MODES; i++)
if (constant_modes[i] == mode)
break;
if (i == NR_C_MODES)
abort ();
for (c = pool->constants[i]; c != NULL; c = c->next)
if (rtx_equal_p (val, c->value))
break;
if (c == NULL)
abort ();
offset = gen_rtx_MINUS (Pmode, gen_rtx_LABEL_REF (Pmode, c->label),
gen_rtx_LABEL_REF (Pmode, pool->label));
offset = gen_rtx_CONST (Pmode, offset);
return offset;
}
/* Set 'anchor' flag in POOL. */
static void
s390_add_anchor (pool)
struct constant_pool *pool;
{
if (!pool->anchor)
{
pool->anchor = TRUE;
pool->size += 4;
}
}
/* Dump out the constants in POOL. */
static rtx
......@@ -3334,31 +3560,47 @@ s390_dump_pool (pool)
rtx insn;
int i;
/* Select location to put literal pool. */
if (TARGET_64BIT)
insn = get_last_insn ();
else
insn = pool->last_insn? pool->last_insn : get_last_insn ();
/* Pool start insn switches to proper section
and guarantees necessary alignment. */
if (TARGET_64BIT)
insn = emit_insn_after (gen_pool_start_64 (), insn);
insn = emit_insn_after (gen_pool_start_64 (), pool->pool_insn);
else
insn = emit_insn_after (gen_pool_start_31 (), insn);
insn = emit_insn_after (gen_pool_start_31 (), pool->pool_insn);
INSN_ADDRESSES_NEW (insn, -1);
insn = emit_label_after (pool->label, insn);
INSN_ADDRESSES_NEW (insn, -1);
/* Emit anchor if we need one. */
if (pool->anchor)
{
rtx anchor = gen_rtx_LABEL_REF (VOIDmode, pool->label);
anchor = gen_rtx_UNSPEC (VOIDmode, gen_rtvec (1, anchor), 105);
anchor = gen_rtx_CONST (VOIDmode, anchor);
insn = emit_insn_after (gen_consttable_si (anchor), insn);
INSN_ADDRESSES_NEW (insn, -1);
}
/* Dump constants in descending alignment requirement order,
ensuring proper alignment for every constant. */
for (i = 0; i < NR_C_MODES; i++)
for (c = pool->constants[i]; c; c = c->next)
{
/* Convert 104 unspecs to pool-relative references. */
rtx value = c->value;
if (GET_CODE (value) == CONST
&& GET_CODE (XEXP (value, 0)) == UNSPEC
&& XINT (XEXP (value, 0), 1) == 104
&& XVECLEN (XEXP (value, 0), 0) == 1)
{
value = gen_rtx_MINUS (Pmode, XVECEXP (XEXP (value, 0), 0, 0),
gen_rtx_LABEL_REF (VOIDmode, pool->label));
value = gen_rtx_CONST (VOIDmode, value);
}
insn = emit_label_after (c->label, insn);
INSN_ADDRESSES_NEW (insn, -1);
insn = emit_insn_after (gen_consttable[i] (c->value), insn);
insn = emit_insn_after (gen_consttable[i] (value), insn);
INSN_ADDRESSES_NEW (insn, -1);
}
......@@ -3373,6 +3615,9 @@ s390_dump_pool (pool)
insn = emit_barrier_after (insn);
INSN_ADDRESSES_NEW (insn, -1);
/* Remove placeholder insn. */
remove_insn (pool->pool_insn);
return insn;
}
......@@ -3395,65 +3640,86 @@ s390_free_pool (pool)
}
}
BITMAP_XFREE (pool->insns);
free (pool);
}
/* Used in s390.md for branch length calculation. */
int s390_pool_overflow = 0;
/* Chunkify the literal pool if required. */
/* Chunkify the literal pool if required.
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. */
#define S390_POOL_CHUNK_MIN 0xc00
#define S390_POOL_CHUNK_MAX 0xe00
static void
s390_chunkify_pool ()
static struct constant_pool *
s390_chunkify_start (temp_reg, temp_used)
rtx temp_reg;
bool *temp_used;
{
rtx base_reg = gen_rtx_REG (Pmode,
TARGET_64BIT? BASE_REGISTER : RETURN_REGNUM);
rtx base_reg = gen_rtx_REG (Pmode, BASE_REGISTER);
struct constant_pool *curr_pool = NULL, *pool_list = NULL;
int extra_size = 0;
bitmap far_labels;
rtx insn;
rtx (*gen_reload_base) PARAMS ((rtx, rtx)) =
TARGET_64BIT? gen_reload_base_64 : gen_reload_base_31;
/* Do we need to chunkify the literal pool? */
if (get_pool_size () < S390_POOL_CHUNK_MAX)
return;
if (!TARGET_64BIT)
regs_ever_live[RETURN_REGNUM] = 1;
return NULL;
/* We need correct insn addresses. */
shorten_branches (get_insns ());
/* Scan all insns and move literals to pool chunks.
Replace all occurrances of literal pool references
by explicit references to pool chunk entries. */
Also, emit anchor reload insns before every insn that uses
the literal pool base register as anchor pointer. */
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
{
if (GET_CODE (insn) == INSN)
if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN)
{
rtx addr, pool_ref = NULL_RTX;
rtx pool_ref = NULL_RTX;
find_constant_pool_ref (PATTERN (insn), &pool_ref);
if (pool_ref)
{
if (!curr_pool)
curr_pool = s390_start_pool (&pool_list, insn);
addr = s390_add_pool (curr_pool, get_pool_constant (pool_ref),
get_pool_mode (pool_ref));
s390_add_constant (curr_pool, get_pool_constant (pool_ref),
get_pool_mode (pool_ref));
s390_add_pool_insn (curr_pool, insn);
}
else if (!TARGET_64BIT && flag_pic
&& find_base_register_ref (PATTERN (insn)))
{
rtx new = gen_reload_anchor (temp_reg, base_reg);
new = emit_insn_before (new, insn);
INSN_ADDRESSES_NEW (new, INSN_ADDRESSES (INSN_UID (insn)));
extra_size += 8;
*temp_used = 1;
if (!curr_pool)
curr_pool = s390_start_pool (&pool_list, new);
addr = gen_rtx_PLUS (Pmode, base_reg, addr);
replace_constant_pool_ref (&PATTERN (insn), pool_ref, addr);
INSN_CODE (insn) = -1;
s390_add_anchor (curr_pool);
s390_add_pool_insn (curr_pool, insn);
}
}
if (GET_CODE (insn) == JUMP_INSN || GET_CODE (insn) == CODE_LABEL)
if (curr_pool)
s390_add_pool_insn (curr_pool, insn);
if (!curr_pool
|| INSN_ADDRESSES_SIZE () <= (size_t) INSN_UID (insn)
|| INSN_ADDRESSES (INSN_UID (insn)) == -1)
......@@ -3464,7 +3730,7 @@ s390_chunkify_pool ()
if (curr_pool->size < S390_POOL_CHUNK_MAX)
continue;
s390_end_pool (curr_pool, insn);
s390_end_pool (curr_pool, NULL_RTX);
curr_pool = NULL;
}
else
......@@ -3477,11 +3743,8 @@ s390_chunkify_pool ()
Those will have an effect on code size, which we need to
consider here. This calculation makes rather pessimistic
worst-case assumptions. */
if (GET_CODE (insn) == CODE_LABEL
|| GET_CODE (insn) == JUMP_INSN)
if (GET_CODE (insn) == CODE_LABEL)
extra_size += 6;
else if (GET_CODE (insn) == CALL_INSN)
extra_size += 4;
if (chunk_size < S390_POOL_CHUNK_MIN
&& curr_pool->size < S390_POOL_CHUNK_MIN)
......@@ -3497,12 +3760,22 @@ s390_chunkify_pool ()
/* ... so if we don't find one in time, create one. */
else if ((chunk_size > S390_POOL_CHUNK_MAX
|| curr_pool->size > S390_POOL_CHUNK_MAX)
&& (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN))
|| curr_pool->size > S390_POOL_CHUNK_MAX))
{
int addr = INSN_ADDRESSES (INSN_UID (insn));
rtx label, jump, barrier;
/* We can insert the barrier only after a 'real' insn. */
if (GET_CODE (insn) != INSN && GET_CODE (insn) != CALL_INSN)
continue;
if (get_attr_length (insn) == 0)
continue;
/* Don't separate insns created by s390_split_branches. */
if (GET_CODE (insn) == INSN
&& GET_CODE (PATTERN (insn)) == SET
&& rtx_equal_p (SET_DEST (PATTERN (insn)), temp_reg))
continue;
label = gen_label_rtx ();
jump = emit_jump_insn_after (gen_jump (label), insn);
barrier = emit_barrier_after (jump);
......@@ -3510,8 +3783,8 @@ s390_chunkify_pool ()
JUMP_LABEL (jump) = label;
LABEL_NUSES (label) = 1;
INSN_ADDRESSES_NEW (jump, addr+1);
INSN_ADDRESSES_NEW (barrier, addr+1);
INSN_ADDRESSES_NEW (jump, -1);
INSN_ADDRESSES_NEW (barrier, -1);
INSN_ADDRESSES_NEW (insn, -1);
s390_end_pool (curr_pool, barrier);
......@@ -3521,10 +3794,8 @@ s390_chunkify_pool ()
}
}
/* Dump out all literal pools. */
for (curr_pool = pool_list; curr_pool; curr_pool = curr_pool->next)
s390_dump_pool (curr_pool);
if (curr_pool)
s390_end_pool (curr_pool, NULL_RTX);
/* Find all labels that are branched into
......@@ -3563,20 +3834,7 @@ s390_chunkify_pool ()
if (GET_CODE (pat) == SET)
{
rtx label = 0;
if (GET_CODE (SET_SRC (pat)) == LABEL_REF)
{
label = XEXP (SET_SRC (pat), 0);
}
else if (GET_CODE (SET_SRC (pat)) == IF_THEN_ELSE)
{
if (GET_CODE (XEXP (SET_SRC (pat), 1)) == LABEL_REF)
label = XEXP (XEXP (SET_SRC (pat), 1), 0);
else if (GET_CODE (XEXP (SET_SRC (pat), 2)) == LABEL_REF)
label = XEXP (XEXP (SET_SRC (pat), 2), 0);
}
rtx label = JUMP_LABEL (insn);
if (label)
{
if (s390_find_pool (pool_list, label)
......@@ -3617,19 +3875,11 @@ s390_chunkify_pool ()
/* Insert base register reload insns before every pool. */
for (curr_pool = pool_list; curr_pool; curr_pool = curr_pool->next)
if (TARGET_64BIT)
{
rtx pool_ref = gen_rtx_LABEL_REF (Pmode, curr_pool->label);
rtx new_insn = gen_rtx_SET (Pmode, base_reg, pool_ref);
rtx insn = curr_pool->first_insn;
INSN_ADDRESSES_NEW (emit_insn_before (new_insn, insn), -1);
}
else
{
rtx new_insn = gen_reload_base (base_reg, curr_pool->label);
rtx insn = curr_pool->first_insn;
INSN_ADDRESSES_NEW (emit_insn_before (new_insn, insn), -1);
}
{
rtx new_insn = gen_reload_base (base_reg, curr_pool->label);
rtx insn = curr_pool->first_insn;
INSN_ADDRESSES_NEW (emit_insn_before (new_insn, insn), -1);
}
/* Insert base register reload insns at every far label. */
......@@ -3640,60 +3890,137 @@ s390_chunkify_pool ()
struct constant_pool *pool = s390_find_pool (pool_list, insn);
if (pool)
{
if (TARGET_64BIT)
{
rtx pool_ref = gen_rtx_LABEL_REF (Pmode, pool->label);
rtx new_insn = gen_rtx_SET (Pmode, base_reg, pool_ref);
INSN_ADDRESSES_NEW (emit_insn_after (new_insn, insn), -1);
}
else
{
rtx new_insn = gen_reload_base (base_reg, pool->label);
INSN_ADDRESSES_NEW (emit_insn_after (new_insn, insn), -1);
}
rtx new_insn = gen_reload_base (base_reg, pool->label);
INSN_ADDRESSES_NEW (emit_insn_after (new_insn, insn), -1);
}
}
/* Insert base register reload insns after every call if necessary. */
if (REGNO (base_reg) == RETURN_REGNUM)
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
if (GET_CODE (insn) == CALL_INSN)
{
struct constant_pool *pool = s390_find_pool (pool_list, insn);
if (pool)
{
rtx new_insn = gen_reload_base2 (base_reg, pool->label);
INSN_ADDRESSES_NEW (emit_insn_after (new_insn, insn), -1);
}
}
BITMAP_XFREE (far_labels);
/* Recompute insn addresses. */
s390_pool_overflow = 1;
init_insn_lengths ();
shorten_branches (get_insns ());
s390_pool_overflow = 0;
/* Insert base register reload insns after far branches. */
return pool_list;
}
if (!TARGET_64BIT)
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
if (GET_CODE (insn) == JUMP_INSN
&& GET_CODE (PATTERN (insn)) == SET
&& get_attr_length (insn) >= 12)
{
struct constant_pool *pool = s390_find_pool (pool_list, insn);
if (pool)
/* POOL_LIST is a chunk list as prepared by s390_chunkify_start.
After we have decided to use this list, finish implementing
all changes to the current function as required.
Code generated by this routine is allowed to use
TEMP_REG as temporary scratch register. */
static void
s390_chunkify_finish (pool_list, temp_reg)
struct constant_pool *pool_list;
rtx temp_reg;
{
rtx base_reg = gen_rtx_REG (Pmode, BASE_REGISTER);
struct constant_pool *curr_pool = NULL;
rtx insn;
/* Replace all literal pool references. */
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
{
curr_pool = s390_find_pool (pool_list, insn);
if (!curr_pool)
continue;
if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN)
{
rtx addr, pool_ref = NULL_RTX;
find_constant_pool_ref (PATTERN (insn), &pool_ref);
if (pool_ref)
{
addr = s390_find_constant (curr_pool, get_pool_constant (pool_ref),
get_pool_mode (pool_ref));
addr = gen_rtx_PLUS (Pmode, base_reg, addr);
replace_constant_pool_ref (&PATTERN (insn), pool_ref, addr);
INSN_CODE (insn) = -1;
}
else if (!TARGET_64BIT && flag_pic
&& find_base_register_ref (PATTERN (insn)))
{
rtx new_insn = gen_reload_base (base_reg, pool->label);
INSN_ADDRESSES_NEW (emit_insn_after (new_insn, insn), -1);
replace_base_register_ref (&PATTERN (insn), temp_reg);
}
}
}
/* Dump out all literal pools. */
for (curr_pool = pool_list; curr_pool; curr_pool = curr_pool->next)
s390_dump_pool (curr_pool);
/* Free pool list. */
while (pool_list)
{
struct constant_pool *next = pool_list->next;
s390_free_pool (pool_list);
pool_list = next;
}
}
/* POOL_LIST is a chunk list as prepared by s390_chunkify_start.
We have decided we cannot use this list, so revert all changes
to the current function that were done by s390_chunkify_start. */
static void
s390_chunkify_cancel (pool_list)
struct constant_pool *pool_list;
{
struct constant_pool *curr_pool = NULL;
rtx insn;
/* Remove all pool placeholder insns. */
for (curr_pool = pool_list; curr_pool; curr_pool = curr_pool->next)
{
/* Did we insert an extra barrier? Remove it. */
rtx barrier = PREV_INSN (curr_pool->pool_insn);
rtx jump = barrier? PREV_INSN (barrier) : NULL_RTX;
rtx label = NEXT_INSN (curr_pool->pool_insn);
if (jump && GET_CODE (jump) == JUMP_INSN
&& barrier && GET_CODE (barrier) == BARRIER
&& label && GET_CODE (label) == CODE_LABEL
&& GET_CODE (PATTERN (jump)) == SET
&& SET_DEST (PATTERN (jump)) == pc_rtx
&& GET_CODE (SET_SRC (PATTERN (jump))) == LABEL_REF
&& XEXP (SET_SRC (PATTERN (jump)), 0) == label)
{
remove_insn (jump);
remove_insn (barrier);
remove_insn (label);
}
remove_insn (curr_pool->pool_insn);
}
/* Remove all base/anchor register reload insns. */
for (insn = get_insns (); insn; )
{
rtx next_insn = NEXT_INSN (insn);
if (GET_CODE (insn) == INSN
&& GET_CODE (PATTERN (insn)) == SET
&& GET_CODE (SET_SRC (PATTERN (insn))) == UNSPEC
&& (XINT (SET_SRC (PATTERN (insn)), 1) == 210
|| XINT (SET_SRC (PATTERN (insn)), 1) == 211))
remove_insn (insn);
/* Free all memory. */
insn = next_insn;
}
/* Free pool list. */
while (pool_list)
{
......@@ -3701,8 +4028,6 @@ s390_chunkify_pool ()
s390_free_pool (pool_list);
pool_list = next;
}
BITMAP_XFREE (far_labels);
}
......@@ -3745,19 +4070,51 @@ s390_output_constant_pool (file)
else
fprintf (file, ".LTN%d:\n", current_function_funcdef_no);
}
/* If no pool required, at least output the anchor label. */
else if (!TARGET_64BIT && flag_pic)
fprintf (file, ".LT%d:\n", current_function_funcdef_no);
}
/* Rework the prolog/epilog to avoid saving/restoring
registers unnecessarily. */
registers unnecessarily. If TEMP_REGNO is nonnegative,
it specifies the number of a caller-saved register used
as temporary scratch register by code emitted during
machine dependent reorg. */
static void
s390_optimize_prolog ()
s390_optimize_prolog (temp_regno)
int temp_regno;
{
int save_first, save_last, restore_first, restore_last;
int i, j;
rtx insn, new_insn, next_insn;
struct s390_frame frame;
s390_frame_info (&frame);
/* Recompute regs_ever_live data for special registers. */
regs_ever_live[BASE_REGISTER] = 0;
regs_ever_live[RETURN_REGNUM] = 0;
regs_ever_live[STACK_POINTER_REGNUM] = frame.frame_size > 0;
/* If there is (possibly) any pool entry, we need to
load the base register.
??? FIXME: this should be more precise. */
if (get_pool_size ())
regs_ever_live[BASE_REGISTER] = 1;
/* In non-leaf functions, the prolog/epilog code relies
on RETURN_REGNUM being saved in any case. */
if (!current_function_is_leaf)
regs_ever_live[RETURN_REGNUM] = 1;
/* We need to save/restore the temporary register. */
if (temp_regno >= 0)
regs_ever_live[temp_regno] = 1;
/* Find first and last gpr to be saved. */
for (i = 6; i < 16; i++)
......@@ -3865,34 +4222,146 @@ s390_optimize_prolog ()
}
}
/* Check whether any insn in the function makes use of the original
value of RETURN_REG (e.g. for __builtin_return_address).
If so, insert an insn reloading that value.
Return true if any such insn was found. */
static bool
s390_fixup_clobbered_return_reg (return_reg)
rtx return_reg;
{
bool replacement_done = 0;
rtx insn;
struct s390_frame frame;
s390_frame_info (&frame);
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
{
rtx reg, off, new_insn;
if (GET_CODE (insn) != INSN)
continue;
if (!reg_referenced_p (return_reg, PATTERN (insn)))
continue;
if (GET_CODE (PATTERN (insn)) == PARALLEL
&& store_multiple_operation (PATTERN (insn), VOIDmode))
continue;
if (frame.frame_pointer_p)
reg = hard_frame_pointer_rtx;
else
reg = stack_pointer_rtx;
off = GEN_INT (frame.frame_size + REGNO (return_reg) * UNITS_PER_WORD);
if (INTVAL (off) >= 4096)
{
off = force_const_mem (Pmode, off);
new_insn = gen_rtx_SET (Pmode, return_reg, off);
new_insn = emit_insn_before (new_insn, insn);
INSN_ADDRESSES_NEW (new_insn, -1);
off = return_reg;
}
new_insn = gen_rtx_MEM (Pmode, gen_rtx_PLUS (Pmode, reg, off));
new_insn = gen_rtx_SET (Pmode, return_reg, new_insn);
new_insn = emit_insn_before (new_insn, insn);
INSN_ADDRESSES_NEW (new_insn, -1);
replacement_done = 1;
}
return replacement_done;
}
/* Perform machine-dependent processing. */
void
s390_machine_dependent_reorg (first)
rtx first ATTRIBUTE_UNUSED;
{
struct s390_frame frame;
s390_frame_info (&frame);
bool fixed_up_clobbered_return_reg = 0;
rtx temp_reg = gen_rtx_REG (Pmode, RETURN_REGNUM);
bool temp_used = 0;
/* Recompute regs_ever_live data for special registers. */
regs_ever_live[BASE_REGISTER] = 0;
regs_ever_live[RETURN_REGNUM] = 0;
regs_ever_live[STACK_POINTER_REGNUM] = frame.frame_size > 0;
/* Make sure all splits have been performed; splits after
machine_dependent_reorg might confuse insn length counts. */
split_all_insns_noflow ();
/* If there is (possibly) any pool entry, we need to
load the base register.
??? FIXME: this should be more precise. */
if (get_pool_size ())
regs_ever_live[BASE_REGISTER] = 1;
/* In non-leaf functions, the prolog/epilog code relies
on RETURN_REGNUM being saved in any case. */
if (!current_function_is_leaf)
regs_ever_live[RETURN_REGNUM] = 1;
/* There are two problematic situations we need to correct:
- the literal pool might be > 4096 bytes in size, so that
some of its elements cannot be directly accessed
- a branch target might be > 64K away from the branch, so that
it is not possible to use a PC-relative instruction.
To fix those, we split the single literal pool into multiple
pool chunks, reloading the pool base register at various
points throughout the function to ensure it always points to
the pool chunk the following code expects, and / or replace
PC-relative branches by absolute branches.
However, the two problems are interdependent: splitting the
literal pool can move a branch further away from its target,
causing the 64K limit to overflow, and on the other hand,
replacing a PC-relative branch by an absolute branch means
we need to put the branch target address into the literal
pool, possibly causing it to overflow.
So, we loop trying to fix up both problems until we manage
to satisfy both conditions at the same time. Note that the
loop is guaranteed to terminate as every pass of the loop
strictly decreases the total number of PC-relative branches
in the function. (This is not completely true as there
might be branch-over-pool insns introduced by chunkify_start.
Those never need to be split however.) */
for (;;)
{
struct constant_pool *pool_list;
/* Try to chunkify the literal pool. */
pool_list = s390_chunkify_start (temp_reg, &temp_used);
/* 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))
{
if (pool_list)
s390_chunkify_cancel (pool_list);
continue;
}
/* Check whether we have clobbered a use of the return
register (e.g. for __builtin_return_address). If so,
add insns reloading the register where necessary. */
if (temp_used && !fixed_up_clobbered_return_reg
&& s390_fixup_clobbered_return_reg (temp_reg))
{
fixed_up_clobbered_return_reg = 1;
s390_chunkify_pool ();
s390_split_branches ();
s390_optimize_prolog ();
/* The fixup insns might have caused a jump to overflow. */
if (pool_list)
s390_chunkify_cancel (pool_list);
continue;
}
/* If we made it up to here, both conditions are satisfied.
Finish up pool chunkification if required. */
if (pool_list)
s390_chunkify_finish (pool_list, temp_reg);
break;
}
s390_optimize_prolog (temp_used? RETURN_REGNUM : -1);
}
......
......@@ -1299,7 +1299,6 @@ extern struct rtx_def *s390_compare_op0, *s390_compare_op1;
extern int s390_pool_count;
extern int s390_nr_constants;
extern int s390_pool_overflow;
#define ASM_OUTPUT_POOL_PROLOGUE(FILE, FUNNAME, fndecl, size) \
{ \
......
......@@ -5724,9 +5724,6 @@
(const_int 4)
(ne (symbol_ref "TARGET_64BIT") (const_int 0))
(const_int 6)
(ne (symbol_ref "s390_pool_overflow") (const_int 0))
(if_then_else (eq (symbol_ref "flag_pic") (const_int 0))
(const_int 12) (const_int 14))
(eq (symbol_ref "flag_pic") (const_int 0))
(const_int 6)] (const_int 8)))])
......@@ -5776,9 +5773,6 @@
(const_int 4)
(ne (symbol_ref "TARGET_64BIT") (const_int 0))
(const_int 6)
(ne (symbol_ref "s390_pool_overflow") (const_int 0))
(if_then_else (eq (symbol_ref "flag_pic") (const_int 0))
(const_int 12) (const_int 14))
(eq (symbol_ref "flag_pic") (const_int 0))
(const_int 6)] (const_int 8)))])
......@@ -5888,9 +5882,6 @@
(const_int 4)
(ne (symbol_ref "TARGET_64BIT") (const_int 0))
(const_int 10)
(ne (symbol_ref "s390_pool_overflow") (const_int 0))
(if_then_else (eq (symbol_ref "flag_pic") (const_int 0))
(const_int 12) (const_int 14))
(eq (symbol_ref "flag_pic") (const_int 0))
(const_int 6)] (const_int 8)))])
......@@ -6707,7 +6698,7 @@
[(set_attr "op_type" "NN")
(set_attr "length" "0")])
(define_insn "reload_base"
(define_insn "reload_base_31"
[(set (match_operand:SI 0 "register_operand" "=a")
(unspec:SI [(label_ref (match_operand 1 "" ""))] 210))]
"!TARGET_64BIT"
......@@ -6716,15 +6707,29 @@
(set_attr "type" "la")
(set_attr "length" "6")])
(define_insn "reload_base2"
(define_insn "reload_base_64"
[(set (match_operand:DI 0 "register_operand" "=a")
(unspec:DI [(label_ref (match_operand 1 "" ""))] 210))]
"TARGET_64BIT"
"larl\\t%0,%1"
[(set_attr "op_type" "RIL")
(set_attr "type" "la")])
(define_insn "reload_anchor"
[(set (match_operand:SI 0 "register_operand" "=a")
(unspec:SI [(label_ref (match_operand 1 "" ""))] 211))]
(unspec:SI [(match_operand:SI 1 "register_operand" "a")] 211))]
"!TARGET_64BIT"
"la\\t%0,%1-.(%0)"
"l\\t%0,0(%1)\;la\\t%0,0(%0,%1)"
[(set_attr "op_type" "NN")
(set_attr "type" "la")
(set_attr "length" "4")])
(set_attr "length" "8")])
(define_insn "pool"
[(unspec_volatile [(match_operand 0 "const_int_operand" "n")] 220)]
""
"* abort ();"
[(set_attr "op_type" "NN")
(set (attr "length") (symbol_ref "INTVAL (operands[0])"))])
;;
;; Insns related to generating the function prologue and epilogue.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment