static int s390_decompose_address PARAMS ((rtx, struct s390_address *, int));
static int reg_used_in_mem_p PARAMS ((int, rtx));
static int addr_generation_dependency_p PARAMS ((rtx, rtx));
-static int other_chunk PARAMS ((int *, int, int));
-static int far_away PARAMS ((int, int));
-static rtx check_and_change_labels PARAMS ((rtx, int *));
-static void s390_final_chunkify PARAMS ((int));
+static void s390_split_branches PARAMS ((void));
+static void s390_chunkify_pool PARAMS ((void));
static int save_fprs_p PARAMS ((void));
static int find_unused_clobbered_reg PARAMS ((void));
static void s390_frame_info PARAMS ((struct s390_frame *));
processed. */
rtx s390_pool_start_insn = NULL_RTX;
-/* UID of last insn using the constant pool chunk that is currently
- being processed. */
-static int pool_stop_uid;
-
/* Called from the ASM_OUTPUT_POOL_PROLOGUE macro to
prepare for printing a literal pool chunk to stdio stream FILE.
function_section (fndecl);
}
-/* Return true if OTHER_ADDR is in different chunk than MY_ADDR.
- LTORG points to a list of all literal pools inserted
- into the current function. */
+/* Split all branches that exceed the maximum distance. */
-static int
-other_chunk (ltorg, my_addr, other_addr)
- int *ltorg;
- int my_addr;
- int other_addr;
+static void
+s390_split_branches (void)
{
- int ad, i=0, j=0;
-
- while ((ad = ltorg[i++])) {
- if (INSN_ADDRESSES (ad) >= my_addr)
- break;
- }
-
- while ((ad = ltorg[j++])) {
- if (INSN_ADDRESSES (ad) > other_addr)
- break;
- }
-
- if (i==j)
- return 0;
-
- return 1;
-}
+ rtx temp_reg = gen_rtx_REG (Pmode, RETURN_REGNUM);
+ rtx insn, pat, label, target, jump, tmp;
-/* Return true if OTHER_ADDR is too far away from MY_ADDR
- to use a relative branch instruction. */
+ /* In 64-bit mode we can jump +- 4GB. */
-static int
-far_away (my_addr, other_addr)
- int my_addr;
- int other_addr;
-{
- /* In 64 bit mode we can jump +- 4GB. */
if (TARGET_64BIT)
- return 0;
- if (abs (my_addr - other_addr) > S390_REL_MAX)
- return 1;
- return 0;
-}
+ return;
-/* Go through all insns in the current function (starting
- at INSN), replacing branch insn if necessary. A branch
- needs to be modified if either the distance to the
- target is too far to use a relative branch, or if the
- target uses a different literal pool than the origin.
- LTORG_UIDS points to a list of all literal pool insns
- that have been inserted. */
+ /* Find all branches that exceed 64KB, and split them. */
-static rtx
-check_and_change_labels (insn, ltorg_uids)
- rtx insn;
- int *ltorg_uids;
-{
- rtx temp_reg = gen_rtx_REG (Pmode, RETURN_REGNUM);
- rtx target, jump, cjump;
- rtx pattern, tmp, body, label1;
- int addr0, addr1;
+ for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+ {
+ if (GET_CODE (insn) != JUMP_INSN)
+ continue;
- if (GET_CODE (insn) != JUMP_INSN)
- return insn;
+ pat = PATTERN (insn);
+ if (GET_CODE (pat) != SET)
+ continue;
- pattern = PATTERN (insn);
-
- addr0 = INSN_ADDRESSES (INSN_UID (insn));
- if (GET_CODE (pattern) == SET)
- {
- body = XEXP (pattern, 1);
- if (GET_CODE (body) == LABEL_REF)
+ if (GET_CODE (SET_SRC (pat)) == LABEL_REF)
{
- addr1 = INSN_ADDRESSES (INSN_UID (XEXP (body, 0)));
-
- if (other_chunk (ltorg_uids, addr0, addr1))
- {
- SYMBOL_REF_USED (XEXP (body, 0)) = 1;
- }
- if (far_away (addr0, addr1))
- {
- if (flag_pic)
- {
- target = gen_rtx_UNSPEC (SImode, gen_rtvec (1, body), 100);
- target = gen_rtx_CONST (SImode, target);
- target = force_const_mem (SImode, target);
- jump = gen_rtx_REG (Pmode, BASE_REGISTER);
- jump = gen_rtx_PLUS (Pmode, jump, temp_reg);
- }
- else
- {
- target = force_const_mem (Pmode, body);
- jump = temp_reg;
- }
-
- emit_insn_before (gen_movsi (temp_reg, target), insn);
- tmp = emit_jump_insn_before (gen_indirect_jump (jump), insn);
- remove_insn (insn);
- INSN_ADDRESSES_NEW (tmp, -1);
- return tmp;
- }
+ label = SET_SRC (pat);
}
- else if (GET_CODE (body) == IF_THEN_ELSE)
+ else if (GET_CODE (SET_SRC (pat)) == IF_THEN_ELSE)
{
- if (GET_CODE (XEXP (body, 1)) == LABEL_REF)
- {
- addr1 = INSN_ADDRESSES (INSN_UID (XEXP (XEXP (body, 1), 0)));
-
- if (other_chunk (ltorg_uids, addr0, addr1))
- {
- SYMBOL_REF_USED (XEXP (XEXP (body, 1), 0)) = 1;
- }
-
- if (far_away (addr0, addr1))
- {
- if (flag_pic)
- {
- target = gen_rtx_UNSPEC (SImode, gen_rtvec (1, XEXP (body, 1)), 100);
- target = gen_rtx_CONST (SImode, target);
- target = force_const_mem (SImode, target);
- jump = gen_rtx_REG (Pmode, BASE_REGISTER);
- jump = gen_rtx_PLUS (Pmode, jump, temp_reg);
- }
- else
- {
- target = force_const_mem (Pmode, XEXP (body, 1));
- jump = temp_reg;
- }
-
- label1 = gen_label_rtx ();
- cjump = gen_rtx_LABEL_REF (VOIDmode, label1);
- cjump = gen_rtx_IF_THEN_ELSE (VOIDmode, XEXP (body, 0), pc_rtx, cjump);
- cjump = gen_rtx_SET (VOIDmode, pc_rtx, cjump);
- emit_jump_insn_before (cjump, insn);
- emit_insn_before (gen_movsi (temp_reg, target), insn);
- tmp = emit_jump_insn_before (gen_indirect_jump (jump), insn);
- INSN_ADDRESSES_NEW (emit_label_before (label1, insn), -1);
- remove_insn (insn);
- return tmp;
- }
- }
- else if (GET_CODE (XEXP (body, 2)) == LABEL_REF)
- {
- addr1 = INSN_ADDRESSES (INSN_UID (XEXP (XEXP (body, 2), 0)));
-
- if (other_chunk (ltorg_uids, addr0, addr1))
- {
- SYMBOL_REF_USED (XEXP (XEXP (body, 2), 0)) = 1;
- }
-
- if (far_away (addr0, addr1))
- {
- if (flag_pic)
- {
- target = gen_rtx_UNSPEC (SImode, gen_rtvec (1, XEXP (body, 2)), 100);
- target = gen_rtx_CONST (SImode, target);
- target = force_const_mem (SImode, target);
- jump = gen_rtx_REG (Pmode, BASE_REGISTER);
- jump = gen_rtx_PLUS (Pmode, jump, temp_reg);
- }
- else
- {
- target = force_const_mem (Pmode, XEXP (body, 2));
- jump = temp_reg;
- }
-
- label1 = gen_label_rtx ();
- cjump = gen_rtx_LABEL_REF (VOIDmode, label1);
- cjump = gen_rtx_IF_THEN_ELSE (VOIDmode, XEXP (body, 0), cjump, pc_rtx);
- cjump = gen_rtx_SET (VOIDmode, pc_rtx, cjump);
- emit_jump_insn_before (cjump, insn);
- emit_insn_before (gen_movsi (temp_reg, target), insn);
- tmp = emit_jump_insn_before (gen_indirect_jump (jump), insn);
- INSN_ADDRESSES_NEW (emit_label_before (label1, insn), -1);
- remove_insn (insn);
- return tmp;
- }
- }
+ if (GET_CODE (XEXP (SET_SRC (pat), 1)) == LABEL_REF)
+ label = XEXP (SET_SRC (pat), 1);
+ else if (GET_CODE (XEXP (SET_SRC (pat), 2)) == LABEL_REF)
+ label = XEXP (SET_SRC (pat), 2);
+ else
+ continue;
+ }
+ else
+ continue;
+
+ if (get_attr_length (insn) == 4)
+ continue;
+
+ if (flag_pic)
+ {
+ target = gen_rtx_UNSPEC (SImode, gen_rtvec (1, label), 100);
+ target = gen_rtx_CONST (SImode, target);
+ target = force_const_mem (SImode, target);
+ jump = gen_rtx_REG (Pmode, BASE_REGISTER);
+ jump = gen_rtx_PLUS (Pmode, jump, temp_reg);
}
- }
- else if (GET_CODE (pattern) == ADDR_VEC ||
- GET_CODE (pattern) == ADDR_DIFF_VEC)
- {
- int i, diff_vec_p = GET_CODE (pattern) == ADDR_DIFF_VEC;
- int len = XVECLEN (pattern, diff_vec_p);
-
- for (i = 0; i < len; i++)
+ else
{
- addr1 = INSN_ADDRESSES (INSN_UID (XEXP (XVECEXP (pattern, diff_vec_p, i), 0)));
- if (other_chunk (ltorg_uids, addr0, addr1))
- {
- SYMBOL_REF_USED (XEXP (XVECEXP (pattern, diff_vec_p, i), 0)) = 1;
- }
+ target = force_const_mem (Pmode, label);
+ jump = temp_reg;
+ }
+
+ if (GET_CODE (SET_SRC (pat)) == IF_THEN_ELSE)
+ {
+ if (GET_CODE (XEXP (SET_SRC (pat), 1)) == LABEL_REF)
+ jump = gen_rtx_IF_THEN_ELSE (VOIDmode, XEXP (SET_SRC (pat), 0),
+ jump, pc_rtx);
+ else
+ jump = gen_rtx_IF_THEN_ELSE (VOIDmode, XEXP (SET_SRC (pat), 0),
+ pc_rtx, jump);
}
+
+ tmp = emit_insn_before (gen_rtx_SET (Pmode, temp_reg, target), insn);
+ INSN_ADDRESSES_NEW (tmp, -1);
+
+ tmp = emit_jump_insn_before (gen_rtx_SET (VOIDmode, pc_rtx, jump), insn);
+ INSN_ADDRESSES_NEW (tmp, -1);
+
+ remove_insn (insn);
+ insn = tmp;
}
- return insn;
}
-/* Called from s390_function_prologue to make final adjustments
- before outputting code. CHUNKIFY specifies whether we need
- to use multiple literal pools (because the total size of the
- literals exceeds 4K). */
+/* Chunkify the literal pool if required. */
-static void
-s390_final_chunkify (chunkify)
- int chunkify;
+static void
+s390_chunkify_pool (void)
{
- rtx insn, ninsn, tmp;
- int addr, naddr = 0, uids;
- int chunk_max = 0;
+ int *ltorg_uids, max_ltorg, chunk, last_addr;
+ rtx insn;
- int size = insn_current_address;
+ /* Do we need to chunkify the literal pool? */
- int *ltorg_uids;
- int max_ltorg=0;
+ if (get_pool_size () <= S390_POOL_MAX)
+ return;
- ltorg_uids = alloca (size / 1024 + 1024);
- memset (ltorg_uids, 0, size / 1024 + 1024);
+ /* Find all insns where a literal pool chunk must be inserted. */
- if (chunkify == 1)
- {
- chunk_max = size * 2048 / get_pool_size ();
- chunk_max = chunk_max > S390_CHUNK_MAX
- ? S390_CHUNK_MAX : chunk_max;
- }
-
- for (insn=get_insns (); insn;insn = next_real_insn (insn))
+ ltorg_uids = alloca (insn_current_address / 1024 + 1024);
+ max_ltorg = 0;
+
+ last_addr = 0;
+ for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
{
- if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
+ if (INSN_ADDRESSES (INSN_UID (insn)) - last_addr < S390_CHUNK_MAX)
continue;
-
- addr = INSN_ADDRESSES (INSN_UID (insn));
- if ((ninsn = next_real_insn (insn)))
+ if (INSN_ADDRESSES (INSN_UID (insn)) - last_addr > S390_CHUNK_OV)
+ abort ();
+
+ if (GET_CODE (insn) == CODE_LABEL
+ && !(GET_CODE (NEXT_INSN (insn)) == JUMP_INSN
+ && (GET_CODE (PATTERN (NEXT_INSN (insn))) == ADDR_VEC
+ || GET_CODE (PATTERN (NEXT_INSN (insn))) == ADDR_DIFF_VEC)))
{
- naddr = INSN_ADDRESSES (INSN_UID (ninsn));
+ ltorg_uids[max_ltorg++] = INSN_UID (prev_real_insn (insn));
+ last_addr = INSN_ADDRESSES (ltorg_uids[max_ltorg-1]);
+ continue;
}
-
- if (chunkify && (addr / chunk_max != naddr / chunk_max))
+
+ if (GET_CODE (insn) == CALL_INSN)
{
- for (tmp = insn; tmp; tmp = NEXT_INSN (tmp))
- {
- if (GET_CODE (tmp) == CODE_LABEL &&
- GET_CODE (NEXT_INSN (tmp)) != JUMP_INSN)
- {
- ltorg_uids[max_ltorg++] = INSN_UID (prev_real_insn (tmp));
- break;
- }
- if (GET_CODE (tmp) == CALL_INSN)
- {
- ltorg_uids[max_ltorg++] = INSN_UID (tmp);
- break;
- }
- if (INSN_ADDRESSES (INSN_UID (tmp)) - naddr > S390_CHUNK_OV)
- {
- debug_rtx (insn);
- debug_rtx (tmp);
- fprintf (stderr, "s390 multiple literalpool support:\n No code label between this insn %X %X",
- naddr, INSN_ADDRESSES (INSN_UID (tmp)));
- abort ();
- }
- }
- if (tmp == NULL)
- {
- warning ("no code label found");
- }
- }
+ ltorg_uids[max_ltorg++] = INSN_UID (insn);
+ last_addr = INSN_ADDRESSES (ltorg_uids[max_ltorg-1]);
+ continue;
+ }
}
- ltorg_uids[max_ltorg] = 0;
- if (max_ltorg > 0)
- {
- for (insn = get_insns (), uids = 0; insn; insn = next_real_insn (insn))
- if (INSN_UID (insn) == ltorg_uids[uids])
- {
- INSN_ADDRESSES_NEW (emit_insn_after (gen_ltorg (
- gen_rtx_CONST_INT (Pmode, ltorg_uids[++uids])),
- insn), -1);
- }
+ ltorg_uids[max_ltorg] = insn_current_address + 1;
- init_insn_lengths ();
- shorten_branches (get_insns ());
- }
+ /* Find and mark all labels that are branched into
+ from an insn belonging to a different chunk. */
- for (insn = get_insns (); insn; insn = next_real_insn (insn))
+ chunk = last_addr = 0;
+ for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
{
- if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
- continue;
if (GET_CODE (insn) == JUMP_INSN)
- insn = check_and_change_labels (insn, ltorg_uids);
+ {
+ rtx pat = PATTERN (insn);
+ 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);
+ }
+
+ if (label)
+ {
+ if (INSN_ADDRESSES (INSN_UID (label)) <= last_addr
+ || INSN_ADDRESSES (INSN_UID (label)) > ltorg_uids[chunk])
+ SYMBOL_REF_USED (label) = 1;
+ }
+ }
+ else if (GET_CODE (pat) == ADDR_VEC
+ || GET_CODE (pat) == ADDR_DIFF_VEC)
+ {
+ int i, diff_p = GET_CODE (pat) == ADDR_DIFF_VEC;
+
+ for (i = 0; i < XVECLEN (pat, diff_p); i++)
+ {
+ rtx label = XEXP (XVECEXP (pat, diff_p, i), 0);
+
+ if (INSN_ADDRESSES (INSN_UID (label)) <= last_addr
+ || INSN_ADDRESSES (INSN_UID (label)) > ltorg_uids[chunk])
+ SYMBOL_REF_USED (label) = 1;
+ }
+ }
+ }
+
+ if (INSN_UID (insn) == ltorg_uids[chunk])
+ {
+ last_addr = ltorg_uids[chunk++];
+ }
}
- if (chunkify)
+ /* Insert literal pools and base register reload insns. */
+
+ chunk = 0;
+ for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
{
- for (insn=get_insns (); insn;insn = next_insn (insn))
- {
- if (GET_CODE (insn) == CODE_LABEL)
+ if (INSN_UID (insn) == ltorg_uids[chunk])
+ {
+ rtx new_insn = gen_ltorg (GEN_INT (chunk++));
+ INSN_ADDRESSES_NEW (emit_insn_after (new_insn, insn), -1);
+ }
+
+ if (GET_CODE (insn) == CODE_LABEL && SYMBOL_REF_USED (insn))
{
- if (SYMBOL_REF_USED (insn))
- {
- INSN_ADDRESSES_NEW (emit_insn_after (gen_reload_base (
- gen_rtx_LABEL_REF (Pmode, XEXP (insn, 0))), insn), -1);
- }
+ rtx new_insn = gen_reload_base (insn);
+ INSN_ADDRESSES_NEW (emit_insn_after (new_insn, insn), -1);
}
- }
}
- pool_stop_uid = ltorg_uids[0];
+
+ /* Recompute insn addresses. */
+
+ init_insn_lengths ();
+ shorten_branches (get_insns ());
}
/* Return true if INSN is a 'ltorg' insn. */
rtx stop;
{
s390_pool_start_insn = act_insn;
- pool_stop_uid = INTVAL (stop);
s390_pool_count++;
output_constant_pool (current_function_name, current_function_decl);
function_section (current_function_decl);
FILE *file ATTRIBUTE_UNUSED;
HOST_WIDE_INT lsize ATTRIBUTE_UNUSED;
{
- if (get_pool_size () > S390_POOL_MAX)
- s390_final_chunkify (1);
- else
- s390_final_chunkify (0);
+ s390_chunkify_pool ();
+ s390_split_branches ();
}
/* Output the function epilogue assembly code to the