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));
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");
}
-/* 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;
if (get_attr_length (insn) <= (TARGET_64BIT ? 6 : 4))
continue;
- regs_ever_live[RETURN_REGNUM] = 1;
+ *temp_used = 1;
if (TARGET_64BIT)
{
}
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);
}
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);
if (!validate_change (insn, label, target, 0))
abort ();
}
+
+ return new_literal;
}
}
}
+/* 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. */
{
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 *));
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;
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. */
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++)
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
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);
}
insn = emit_barrier_after (insn);
INSN_ADDRESSES_NEW (insn, -1);
+ /* Remove placeholder insn. */
+ remove_insn (pool->pool_insn);
+
return insn;
}
}
}
+ 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)
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
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)
/* ... 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);
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);
}
}
- /* 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
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)
/* 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. */
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)
{
s390_free_pool (pool_list);
pool_list = next;
}
-
- BITMAP_XFREE (far_labels);
}
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++)
}
}
+/* 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);
}