From aee4e0dbfa31eabba0faf4f5a6d7b04373d9914c Mon Sep 17 00:00:00 2001 From: Ulrich Weigand Date: Tue, 15 Oct 2002 16:27:34 +0000 Subject: [PATCH] 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 --- gcc/ChangeLog | 43 +++ gcc/config/s390/s390.c | 791 ++++++++++++++++++++++++++++++++-------- gcc/config/s390/s390.h | 1 - gcc/config/s390/s390.md | 33 +- 4 files changed, 692 insertions(+), 176 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 00f190e9cf9..b7b666732af 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,46 @@ +2002-10-15 Ulrich Weigand + + * 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 * sh.md (movv8qi_i+2): Don't split if source is -1. diff --git a/gcc/config/s390/s390.c b/gcc/config/s390/s390.c index 50dbc3672f5..d84b72714e6 100644 --- a/gcc/config/s390/s390.c +++ b/gcc/config/s390/s390.c @@ -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); } diff --git a/gcc/config/s390/s390.h b/gcc/config/s390/s390.h index 58ad2e71829..4b992c38b1c 100644 --- a/gcc/config/s390/s390.h +++ b/gcc/config/s390/s390.h @@ -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) \ { \ diff --git a/gcc/config/s390/s390.md b/gcc/config/s390/s390.md index 40a7807a8b4..e998c4de763 100644 --- a/gcc/config/s390/s390.md +++ b/gcc/config/s390/s390.md @@ -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. -- 2.30.2