struct machine_function GTY(())
{
- /* Label of start of initial literal pool. */
- rtx literal_pool_label;
-
/* Set, if some of the fprs 8-15 need to be saved (64 bit abi). */
int save_fprs_p;
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 (int);
+static void s390_optimize_prolog (bool, bool);
static int find_unused_clobbered_reg (void);
static void s390_frame_info (void);
static rtx save_fpr (rtx, int, int);
switch (GET_CODE (op))
{
- /* Constants that we are sure will be forced to the
- literal pool in reload are OK as s-operand. Note
- that we cannot call s390_preferred_reload_class here
- because it might not be known yet at this point
- whether the current function is a leaf or not. */
+ /* Constants are OK as s-operand if ALLOW_IMMEDIATE
+ is true and we are still before reload. */
case CONST_INT:
case CONST_DOUBLE:
if (!allow_immediate || reload_completed)
- break;
- if (!legitimate_reload_constant_p (op))
- return 1;
- if (!TARGET_64BIT)
- return 1;
- break;
+ return 0;
+ return 1;
/* Memory operands are OK unless they already use an
index register. */
case MEM:
if (GET_CODE (XEXP (op, 0)) == ADDRESSOF)
return 1;
- if (s390_decompose_address (XEXP (op, 0), &addr)
- && !addr.indx)
- return 1;
- break;
+ if (!s390_decompose_address (XEXP (op, 0), &addr))
+ return 0;
+ if (addr.indx)
+ return 0;
+ /* Do not allow literal pool references unless ALLOW_IMMEDIATE
+ is true. This prevents compares between two literal pool
+ entries from being accepted. */
+ if (!allow_immediate
+ && addr.base && REGNO (addr.base) == BASE_REGISTER)
+ return 0;
+ return 1;
default:
break;
output_operand_lossage ("invalid UNSPEC as operand (1)");
switch (XINT (x, 1))
{
- case UNSPEC_LTREL_OFFSET:
- s390_output_symbolic_const (file, XVECEXP (x, 0, 0));
- fprintf (file, "-");
- s390_output_symbolic_const (file, cfun->machine->literal_pool_label);
- break;
case UNSPEC_GOTENT:
s390_output_symbolic_const (file, XVECEXP (x, 0, 0));
fprintf (file, "@GOTENT");
if (GET_CODE (x) == UNSPEC
&& XINT (x, 1) == UNSPEC_LTREL_BASE)
return;
+ /* Likewise POOL_ENTRY insns. */
+ if (GET_CODE (x) == UNSPEC_VOLATILE
+ && XINT (x, 1) == UNSPECV_POOL_ENTRY)
+ return;
if (GET_CODE (x) == SYMBOL_REF
&& CONSTANT_POOL_ADDRESS_P (x))
int size;
};
-static struct constant_pool * s390_chunkify_start (void);
-static void s390_chunkify_finish (struct constant_pool *);
+static struct constant_pool * s390_mainpool_start (void);
+static void s390_mainpool_finish (struct constant_pool *, rtx base_reg);
+static void s390_mainpool_cancel (struct constant_pool *);
+
+static struct constant_pool * s390_chunkify_start (rtx base_reg);
+static void s390_chunkify_finish (struct constant_pool *, rtx base_reg);
static void s390_chunkify_cancel (struct constant_pool *);
static struct constant_pool *s390_start_pool (struct constant_pool **, rtx);
static struct constant_pool *s390_find_pool (struct constant_pool *, rtx);
static void s390_add_constant (struct constant_pool *, rtx, enum machine_mode);
static rtx s390_find_constant (struct constant_pool *, rtx, enum machine_mode);
-static rtx s390_dump_pool (struct constant_pool *);
+static rtx s390_dump_pool (struct constant_pool *, bool);
+static struct constant_pool *s390_alloc_pool (void);
static void s390_free_pool (struct constant_pool *);
/* Create new constant pool covering instructions starting at INSN
s390_start_pool (struct constant_pool **pool_list, rtx insn)
{
struct constant_pool *pool, **prev;
- int i;
- pool = (struct constant_pool *) xmalloc (sizeof *pool);
- pool->next = NULL;
- for (i = 0; i < NR_C_MODES; i++)
- pool->constants[i] = NULL;
-
- pool->label = gen_label_rtx ();
+ pool = s390_alloc_pool ();
pool->first_insn = insn;
- pool->pool_insn = NULL_RTX;
- pool->insns = BITMAP_XMALLOC ();
- pool->size = 0;
for (prev = pool_list; *prev; prev = &(*prev)->next)
;
return offset;
}
-/* Dump out the constants in POOL. */
+/* Dump out the constants in POOL. If REMOTE_LABEL is true,
+ do not emit the pool base label. */
static rtx
-s390_dump_pool (struct constant_pool *pool)
+s390_dump_pool (struct constant_pool *pool, bool remote_label)
{
struct constant *c;
rtx 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);
+ if (!remote_label)
+ {
+ insn = emit_label_after (pool->label, insn);
+ INSN_ADDRESSES_NEW (insn, -1);
+ }
/* Dump constants in descending alignment requirement order,
ensuring proper alignment for every constant. */
return insn;
}
+/* Allocate new constant_pool structure. */
+
+static struct constant_pool *
+s390_alloc_pool (void)
+{
+ struct constant_pool *pool;
+ int i;
+
+ pool = (struct constant_pool *) xmalloc (sizeof *pool);
+ pool->next = NULL;
+ for (i = 0; i < NR_C_MODES; i++)
+ pool->constants[i] = NULL;
+
+ pool->label = gen_label_rtx ();
+ pool->first_insn = NULL_RTX;
+ pool->pool_insn = NULL_RTX;
+ pool->insns = BITMAP_XMALLOC ();
+ pool->size = 0;
+
+ return pool;
+}
+
/* Free all memory used by POOL. */
static void
}
-/* Chunkify the literal pool if required. */
+/* Collect main literal pool. Return NULL on overflow. */
+
+static struct constant_pool *
+s390_mainpool_start (void)
+{
+ struct constant_pool *pool;
+ rtx insn;
+
+ pool = s390_alloc_pool ();
+
+ for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+ {
+ if (GET_CODE (insn) == INSN
+ && GET_CODE (PATTERN (insn)) == UNSPEC_VOLATILE
+ && XINT (PATTERN (insn), 1) == UNSPECV_MAIN_POOL)
+ {
+ if (pool->pool_insn)
+ abort ();
+ pool->pool_insn = insn;
+ }
+
+ if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN)
+ {
+ rtx pool_ref = NULL_RTX;
+ find_constant_pool_ref (PATTERN (insn), &pool_ref);
+ if (pool_ref)
+ {
+ rtx constant = get_pool_constant (pool_ref);
+ enum machine_mode mode = get_pool_mode (pool_ref);
+ s390_add_constant (pool, constant, mode);
+ }
+ }
+ }
+
+ if (!pool->pool_insn)
+ abort ();
+
+ if (pool->size >= 4096)
+ {
+ s390_free_pool (pool);
+ pool = NULL;
+ }
+
+ return pool;
+}
+
+/* POOL holds the main literal pool as collected by s390_mainpool_start.
+ Modify the current function to output the pool constants as well as
+ the pool register setup instruction. BASE_REG is the register to
+ be used as pool base register. */
+
+static void
+s390_mainpool_finish (struct constant_pool *pool, rtx base_reg)
+{
+ rtx insn;
+
+ /* If the pool is empty, we're done. */
+ if (pool->size == 0)
+ {
+ remove_insn (pool->pool_insn);
+ s390_free_pool (pool);
+ return;
+ }
+
+ /* We need correct insn addresses. */
+ shorten_branches (get_insns ());
+
+ /* In 64-bit, we use a LARL to load the pool register. The pool is
+ located in the .rodata section, so we emit it after the function. */
+ if (TARGET_64BIT)
+ {
+ insn = gen_main_base_64 (base_reg, pool->label);
+ insn = emit_insn_after (insn, pool->pool_insn);
+ INSN_ADDRESSES_NEW (insn, -1);
+ remove_insn (pool->pool_insn);
+
+ insn = get_last_insn ();
+ pool->pool_insn = emit_insn_after (gen_pool (const0_rtx), insn);
+ INSN_ADDRESSES_NEW (pool->pool_insn, -1);
+
+ s390_dump_pool (pool, 0);
+ }
+
+ /* In 31-bit, if the total size of the function's code plus literal pool
+ does not exceed 4096 bytes, we use BASR to set up a function base
+ pointer, and emit the literal pool at the end of the function. */
+ else if (INSN_ADDRESSES (INSN_UID (get_last_insn ()))
+ + pool->size + 8 /* alignment slop */ < 4096)
+ {
+ insn = gen_main_base_31_small (base_reg, pool->label);
+ insn = emit_insn_after (insn, pool->pool_insn);
+ INSN_ADDRESSES_NEW (insn, -1);
+ remove_insn (pool->pool_insn);
+
+ insn = emit_label_after (pool->label, insn);
+ INSN_ADDRESSES_NEW (insn, -1);
+
+ insn = get_last_insn ();
+ pool->pool_insn = emit_insn_after (gen_pool (const0_rtx), insn);
+ INSN_ADDRESSES_NEW (pool->pool_insn, -1);
+
+ s390_dump_pool (pool, 1);
+ }
+
+ /* Otherwise, we emit an inline literal pool and use BASR to branch
+ over it, setting up the pool register at the same time. */
+ else
+ {
+ rtx pool_end = gen_label_rtx ();
+
+ insn = gen_main_base_31_large (base_reg, pool->label, pool_end);
+ insn = emit_insn_after (insn, pool->pool_insn);
+ INSN_ADDRESSES_NEW (insn, -1);
+ remove_insn (pool->pool_insn);
+
+ insn = emit_label_after (pool->label, insn);
+ INSN_ADDRESSES_NEW (insn, -1);
+
+ pool->pool_insn = emit_insn_after (gen_pool (const0_rtx), insn);
+ INSN_ADDRESSES_NEW (pool->pool_insn, -1);
+
+ insn = emit_label_after (pool_end, pool->pool_insn);
+ INSN_ADDRESSES_NEW (insn, -1);
+
+ s390_dump_pool (pool, 1);
+ }
+
+
+ /* Replace all literal pool references. */
+
+ for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+ {
+ if (INSN_P (insn))
+ replace_ltrel_base (&PATTERN (insn), base_reg);
+
+ 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 (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;
+ }
+ }
+ }
+
+
+ /* Free the pool. */
+ s390_free_pool (pool);
+}
+
+/* POOL holds the main literal pool as collected by s390_mainpool_start.
+ We have decided we cannot use this pool, so revert all changes
+ to the current function that were done by s390_mainpool_start. */
+static void
+s390_mainpool_cancel (struct constant_pool *pool)
+{
+ /* We didn't actually change the instruction stream, so simply
+ free the pool memory. */
+ s390_free_pool (pool);
+}
+
+
+/* Chunkify the literal pool. BASE_REG is to be used as pool
+ register. */
#define S390_POOL_CHUNK_MIN 0xc00
#define S390_POOL_CHUNK_MAX 0xe00
static struct constant_pool *
-s390_chunkify_start (void)
+s390_chunkify_start (rtx base_reg)
{
- 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;
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 NULL;
-
/* We need correct insn addresses. */
shorten_branches (get_insns ());
/* 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. */
+ all changes to the current function as required. BASE_REG is
+ to be used as pool base register. */
static void
-s390_chunkify_finish (struct constant_pool *pool_list)
+s390_chunkify_finish (struct constant_pool *pool_list, rtx base_reg)
{
- rtx base_reg = gen_rtx_REG (Pmode, BASE_REGISTER);
struct constant_pool *curr_pool = NULL;
rtx insn;
/* Dump out all literal pools. */
for (curr_pool = pool_list; curr_pool; curr_pool = curr_pool->next)
- s390_dump_pool (curr_pool);
+ s390_dump_pool (curr_pool, 0);
/* Free pool list. */
}
-/* Index of constant pool chunk that is currently being processed.
- Set to -1 before function output has started. */
-int s390_pool_count = -1;
-
-/* Number of elements of current constant pool. */
-int s390_nr_constants;
-
-/* Output main constant pool to stdio stream FILE. */
-
-void
-s390_output_constant_pool (rtx start_label, rtx end_label)
-{
- if (TARGET_64BIT)
- {
- readonly_data_section ();
- ASM_OUTPUT_ALIGN (asm_out_file, 3);
- targetm.asm_out.internal_label (asm_out_file, "L",
- CODE_LABEL_NUMBER (start_label));
- }
- else
- {
- targetm.asm_out.internal_label (asm_out_file, "L",
- CODE_LABEL_NUMBER (start_label));
- ASM_OUTPUT_ALIGN (asm_out_file, 2);
- }
-
- s390_pool_count = 0;
- output_constant_pool (current_function_name, current_function_decl);
- s390_pool_count = -1;
- if (TARGET_64BIT)
- function_section (current_function_decl);
- else
- {
- ASM_OUTPUT_ALIGN (asm_out_file, 1);
- targetm.asm_out.internal_label (asm_out_file, "L",
- CODE_LABEL_NUMBER (end_label));
- }
-}
-
/* Output to FILE the constant pool entry EXP in mode MODE
with alignment ALIGN. */
/* Rework the prolog/epilog to avoid saving/restoring
- 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. */
+ 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. */
static void
-s390_optimize_prolog (int temp_regno)
+s390_optimize_prolog (bool base_used, bool temp_used)
{
int save_first, save_last, restore_first, restore_last;
int i, j;
rtx insn, new_insn, next_insn;
/* Recompute regs_ever_live data for special registers. */
- regs_ever_live[BASE_REGISTER] = 0;
- regs_ever_live[RETURN_REGNUM] = 0;
+ regs_ever_live[BASE_REGISTER] = base_used;
+ regs_ever_live[RETURN_REGNUM] = temp_used;
regs_ever_live[STACK_POINTER_REGNUM] = cfun->machine->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. We also need
to save the return register if __builtin_return_address (0)
|| cfun->machine->save_return_addr_p)
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. */
s390_reorg (void)
{
rtx temp_reg = gen_rtx_REG (Pmode, RETURN_REGNUM);
- bool temp_used = 0;
+ rtx base_reg = gen_rtx_REG (Pmode, BASE_REGISTER);
+ bool temp_used = false;
+ bool base_used = false;
+ bool pool_overflow = false;
/* Make sure all splits have been performed; splits after
machine_dependent_reorg might confuse insn length counts. */
split_all_insns_noflow ();
- /* There are two problematic situations we need to correct:
+ /* In small leaf functions, try to use an unused call-clobbered
+ register as base register to avoid save/restore overhead. */
+ if (current_function_is_leaf && !regs_ever_live[5])
+ base_reg = gen_rtx_REG (Pmode, 5);
+
+
+ /* Install the main literal pool and the associated base
+ register load insns.
+
+ In addition, 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
for (;;)
{
- struct constant_pool *pool_list;
+ struct constant_pool *pool = NULL;
+
+ /* Collect the literal pool. */
+ if (!pool_overflow)
+ {
+ pool = s390_mainpool_start ();
+ if (!pool)
+ pool_overflow = true;
+ }
- /* Try to chunkify the literal pool. */
- pool_list = s390_chunkify_start ();
+ /* If literal pool overflowed, start to chunkify it. */
+ if (pool_overflow)
+ pool = s390_chunkify_start (base_reg);
/* 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);
+ if (pool_overflow)
+ s390_chunkify_cancel (pool);
+ else
+ s390_mainpool_cancel (pool);
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);
+ Finish up literal pool related changes. */
+ if ((pool_overflow || pool->size > 0)
+ && REGNO (base_reg) == BASE_REGISTER)
+ base_used = true;
+
+ if (pool_overflow)
+ s390_chunkify_finish (pool, base_reg);
+ else
+ s390_mainpool_finish (pool, base_reg);
break;
}
- s390_optimize_prolog (temp_used? RETURN_REGNUM : -1);
+ s390_optimize_prolog (base_used, temp_used);
}
{
rtx insn, addr;
rtx temp_reg;
- rtx pool_start_label, pool_end_label;
int i;
/* Compute frame_info. */
cfun->machine->first_save_gpr, cfun->machine->last_save_gpr);
emit_insn (insn);
- /* Dump constant pool and set constant pool register. */
-
- pool_start_label = gen_label_rtx();
- pool_end_label = gen_label_rtx();
- cfun->machine->literal_pool_label = pool_start_label;
+ /* Dummy insn to mark literal pool slot. */
- if (TARGET_64BIT)
- insn = emit_insn (gen_literal_pool_64 (gen_rtx_REG (Pmode, BASE_REGISTER),
- pool_start_label, pool_end_label));
- else
- insn = emit_insn (gen_literal_pool_31 (gen_rtx_REG (Pmode, BASE_REGISTER),
- pool_start_label, pool_end_label));
+ emit_insn (gen_main_pool ());
/* Save fprs for variable args. */