emit-rtl.c (try_split): Handle 1-1 splits of call insns properly.

* emit-rtl.c (try_split): Handle 1-1 splits of call insns properly.

        * config/ia64/ia64.c (TARGET_FUNCTION_OK_FOR_SIBCALL): New.
        (ia64_gp_save_reg): Remove.
        (struct ia64_frame_info): Move to the beginning of the file;
        add reg_save_gp.
        (ia64_expand_call): Rearrange for new call patterns.
        (ia64_reload_gp): New.
        (ia64_split_call): New.
        (ia64_compute_frame_size): Allocate reg_save_gp.
        (ia64_expand_prologue): Save reg_save_gp.
        (ia64_expand_epilogue): Don't restore gp.
        (ia64_hard_regno_rename_ok): Remove R4 hack.
        (ia64_function_ok_for_sibcall): New.
        (ia64_output_mi_thunk): Set reload_completed, no_new_pseudos;
        call try_split on sibcall pattern.
        * config/ia64/ia64-protos.h: Update.
        * config/ia64/ia64.md (call_nogp, call_value_nogp, sibcall_nogp):
        Rename from nopic versions.  Confiscate 2nd argument to call as
        a marker.
        (call_pic, call_value_pic, sibcall_pic): Remove.
        (call_gp, call_value_gp, sibcall_gp): New.
        (builtin_setjmp_setup): Remove.
        (builtin_setjmp_receiver): Call ia64_reload_gp.

From-SVN: r64303
This commit is contained in:
Richard Henderson 2003-03-13 01:02:51 -08:00 committed by Richard Henderson
parent 7e38bf41d9
commit 599aedd920
5 changed files with 482 additions and 323 deletions

View File

@ -1,3 +1,30 @@
2003-03-13 Richard Henderson <rth@redhat.com>
* emit-rtl.c (try_split): Handle 1-1 splits of call insns properly.
* config/ia64/ia64.c (TARGET_FUNCTION_OK_FOR_SIBCALL): New.
(ia64_gp_save_reg): Remove.
(struct ia64_frame_info): Move to the beginning of the file;
add reg_save_gp.
(ia64_expand_call): Rearrange for new call patterns.
(ia64_reload_gp): New.
(ia64_split_call): New.
(ia64_compute_frame_size): Allocate reg_save_gp.
(ia64_expand_prologue): Save reg_save_gp.
(ia64_expand_epilogue): Don't restore gp.
(ia64_hard_regno_rename_ok): Remove R4 hack.
(ia64_function_ok_for_sibcall): New.
(ia64_output_mi_thunk): Set reload_completed, no_new_pseudos;
call try_split on sibcall pattern.
* config/ia64/ia64-protos.h: Update.
* config/ia64/ia64.md (call_nogp, call_value_nogp, sibcall_nogp):
Rename from nopic versions. Confiscate 2nd argument to call as
a marker.
(call_pic, call_value_pic, sibcall_pic): Remove.
(call_gp, call_value_gp, sibcall_gp): New.
(builtin_setjmp_setup): Remove.
(builtin_setjmp_receiver): Call ia64_reload_gp.
2003-03-12 Nathanael Nerode <neroden@gcc.gnu.org>
* config/dsp16xx/dsp16xx-protos.h, config/dsp16xx/dsp16xx.c,

View File

@ -77,11 +77,12 @@ extern int basereg_operand PARAMS((rtx, enum machine_mode));
extern rtx ia64_expand_move PARAMS ((rtx, rtx));
extern int ia64_move_ok PARAMS((rtx, rtx));
extern int ia64_depz_field_mask PARAMS((rtx, rtx));
extern rtx ia64_gp_save_reg PARAMS((int));
extern rtx ia64_split_timode PARAMS((rtx[], rtx, rtx));
extern rtx spill_tfmode_operand PARAMS((rtx, int));
extern rtx ia64_expand_compare PARAMS((enum rtx_code, enum machine_mode));
extern void ia64_expand_call PARAMS((rtx, rtx, rtx, int));
extern void ia64_split_call PARAMS((rtx, rtx, rtx, rtx, rtx, int, int));
extern void ia64_reload_gp PARAMS((void));
extern HOST_WIDE_INT ia64_initial_elimination_offset PARAMS((int, int));
extern void ia64_expand_prologue PARAMS((void));

View File

@ -123,6 +123,38 @@ unsigned int ia64_section_threshold;
TRUE if we do insn bundling instead of insn scheduling. */
int bundling_p = 0;
/* Structure to be filled in by ia64_compute_frame_size with register
save masks and offsets for the current function. */
struct ia64_frame_info
{
HOST_WIDE_INT total_size; /* size of the stack frame, not including
the caller's scratch area. */
HOST_WIDE_INT spill_cfa_off; /* top of the reg spill area from the cfa. */
HOST_WIDE_INT spill_size; /* size of the gr/br/fr spill area. */
HOST_WIDE_INT extra_spill_size; /* size of spill area for others. */
HARD_REG_SET mask; /* mask of saved registers. */
unsigned int gr_used_mask; /* mask of registers in use as gr spill
registers or long-term scratches. */
int n_spilled; /* number of spilled registers. */
int reg_fp; /* register for fp. */
int reg_save_b0; /* save register for b0. */
int reg_save_pr; /* save register for prs. */
int reg_save_ar_pfs; /* save register for ar.pfs. */
int reg_save_ar_unat; /* save register for ar.unat. */
int reg_save_ar_lc; /* save register for ar.lc. */
int reg_save_gp; /* save register for gp. */
int n_input_regs; /* number of input registers used. */
int n_local_regs; /* number of local registers used. */
int n_output_regs; /* number of output registers used. */
int n_rotate_regs; /* number of rotating registers used. */
char need_regstk; /* true if a .regstk directive needed. */
char initialized; /* true if the data is finalized. */
};
/* Current frame information calculated by ia64_compute_frame_size. */
static struct ia64_frame_info current_frame_info;
static int ia64_use_dfa_pipeline_interface PARAMS ((void));
static int ia64_first_cycle_multipass_dfa_lookahead PARAMS ((void));
@ -147,6 +179,7 @@ static rtx gen_fr_spill_x PARAMS ((rtx, rtx, rtx));
static rtx gen_fr_restore_x PARAMS ((rtx, rtx, rtx));
static enum machine_mode hfa_element_mode PARAMS ((tree, int));
static bool ia64_function_ok_for_sibcall PARAMS ((tree, tree));
static bool ia64_rtx_costs PARAMS ((rtx, int, int, int *));
static void fix_range PARAMS ((const char *));
static struct machine_function * ia64_init_machine_status PARAMS ((void));
@ -313,6 +346,9 @@ static const struct attribute_spec ia64_attribute_table[] =
#define TARGET_HAVE_TLS true
#endif
#undef TARGET_FUNCTION_OK_FOR_SIBCALL
#define TARGET_FUNCTION_OK_FOR_SIBCALL ia64_function_ok_for_sibcall
#undef TARGET_ASM_OUTPUT_MI_THUNK
#define TARGET_ASM_OUTPUT_MI_THUNK ia64_output_mi_thunk
#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
@ -1317,46 +1353,6 @@ ia64_expand_move (op0, op1)
return op1;
}
rtx
ia64_gp_save_reg (setjmp_p)
int setjmp_p;
{
rtx save = cfun->machine->ia64_gp_save;
if (save != NULL)
{
/* We can't save GP in a pseudo if we are calling setjmp, because
pseudos won't be restored by longjmp. For now, we save it in r4. */
/* ??? It would be more efficient to save this directly into a stack
slot. Unfortunately, the stack slot address gets cse'd across
the setjmp call because the NOTE_INSN_SETJMP note is in the wrong
place. */
/* ??? Get the barf bag, Virginia. We've got to replace this thing
in place, since this rtx is used in exception handling receivers.
Moreover, we must get this rtx out of regno_reg_rtx or reload
will do the wrong thing. */
unsigned int old_regno = REGNO (save);
if (setjmp_p && old_regno != GR_REG (4))
{
REGNO (save) = GR_REG (4);
regno_reg_rtx[old_regno] = gen_rtx_raw_REG (DImode, old_regno);
}
}
else
{
if (setjmp_p)
save = gen_rtx_REG (DImode, GR_REG (4));
else if (! optimize)
save = gen_rtx_REG (DImode, LOC_REG (0));
else
save = gen_reg_rtx (DImode);
cfun->machine->ia64_gp_save = save;
}
return save;
}
/* Split a post-reload TImode reference into two DImode components. */
rtx
@ -1494,67 +1490,148 @@ void
ia64_expand_call (retval, addr, nextarg, sibcall_p)
rtx retval;
rtx addr;
rtx nextarg;
rtx nextarg ATTRIBUTE_UNUSED;
int sibcall_p;
{
rtx insn, b0, pfs, gp_save, narg_rtx, dest;
bool indirect_p;
int narg;
rtx insn, b0;
addr = XEXP (addr, 0);
b0 = gen_rtx_REG (DImode, R_BR (0));
pfs = gen_rtx_REG (DImode, AR_PFS_REGNUM);
if (! nextarg)
narg = 0;
else if (IN_REGNO_P (REGNO (nextarg)))
narg = REGNO (nextarg) - IN_REG (0);
else
narg = REGNO (nextarg) - OUT_REG (0);
narg_rtx = GEN_INT (narg);
/* ??? Should do this for functions known to bind local too. */
if (TARGET_NO_PIC || TARGET_AUTO_PIC)
{
if (sibcall_p)
insn = gen_sibcall_nopic (addr, narg_rtx, b0, pfs);
insn = gen_sibcall_nogp (addr);
else if (! retval)
insn = gen_call_nopic (addr, narg_rtx, b0);
insn = gen_call_nogp (addr, b0);
else
insn = gen_call_value_nopic (retval, addr, narg_rtx, b0);
emit_call_insn (insn);
return;
insn = gen_call_value_nogp (retval, addr, b0);
insn = emit_call_insn (insn);
}
indirect_p = ! symbolic_operand (addr, VOIDmode);
if (sibcall_p || (TARGET_CONST_GP && !indirect_p))
gp_save = NULL_RTX;
else
gp_save = ia64_gp_save_reg (setjmp_operand (addr, VOIDmode));
if (gp_save)
emit_move_insn (gp_save, pic_offset_table_rtx);
/* If this is an indirect call, then we have the address of a descriptor. */
if (indirect_p)
{
dest = force_reg (DImode, gen_rtx_MEM (DImode, addr));
emit_move_insn (pic_offset_table_rtx,
gen_rtx_MEM (DImode, plus_constant (addr, 8)));
if (sibcall_p)
insn = gen_sibcall_gp (addr);
else if (! retval)
insn = gen_call_gp (addr, b0);
else
insn = gen_call_value_gp (retval, addr, b0);
insn = emit_call_insn (insn);
use_reg (&CALL_INSN_FUNCTION_USAGE (insn), pic_offset_table_rtx);
}
else
dest = addr;
if (sibcall_p)
insn = gen_sibcall_pic (dest, narg_rtx, b0, pfs);
else if (! retval)
insn = gen_call_pic (dest, narg_rtx, b0);
{
use_reg (&CALL_INSN_FUNCTION_USAGE (insn), b0);
use_reg (&CALL_INSN_FUNCTION_USAGE (insn),
gen_rtx_REG (DImode, AR_PFS_REGNUM));
}
}
void
ia64_reload_gp ()
{
rtx tmp;
if (current_frame_info.reg_save_gp)
tmp = gen_rtx_REG (DImode, current_frame_info.reg_save_gp);
else
insn = gen_call_value_pic (retval, dest, narg_rtx, b0);
{
HOST_WIDE_INT offset;
offset = (current_frame_info.spill_cfa_off
+ current_frame_info.spill_size);
if (frame_pointer_needed)
{
tmp = hard_frame_pointer_rtx;
offset = -offset;
}
else
{
tmp = stack_pointer_rtx;
offset = current_frame_info.total_size - offset;
}
if (CONST_OK_FOR_I (offset))
emit_insn (gen_adddi3 (pic_offset_table_rtx,
tmp, GEN_INT (offset)));
else
{
emit_move_insn (pic_offset_table_rtx, GEN_INT (offset));
emit_insn (gen_adddi3 (pic_offset_table_rtx,
pic_offset_table_rtx, tmp));
}
tmp = gen_rtx_MEM (DImode, pic_offset_table_rtx);
}
emit_move_insn (pic_offset_table_rtx, tmp);
}
void
ia64_split_call (retval, addr, retaddr, scratch_r, scratch_b,
noreturn_p, sibcall_p)
rtx retval, addr, retaddr, scratch_r, scratch_b;
int noreturn_p, sibcall_p;
{
rtx insn;
bool is_desc = false;
/* If we find we're calling through a register, then we're actually
calling through a descriptor, so load up the values. */
if (REG_P (addr))
{
rtx tmp;
bool addr_dead_p;
/* ??? We are currently constrained to *not* use peep2, because
we can legitimiately change the global lifetime of the GP
(in the form of killing where previously live). This is
because a call through a descriptor doesn't use the previous
value of the GP, while a direct call does, and we do not
commit to either form until the split here.
That said, this means that we lack precise life info for
whether ADDR is dead after this call. This is not terribly
important, since we can fix things up essentially for free
with the POST_DEC below, but it's nice to not use it when we
can immediately tell it's not necessary. */
addr_dead_p = ((noreturn_p || sibcall_p
|| TEST_HARD_REG_BIT (regs_invalidated_by_call,
REGNO (addr)))
&& !FUNCTION_ARG_REGNO_P (REGNO (addr)));
/* Load the code address into scratch_b. */
tmp = gen_rtx_POST_INC (Pmode, addr);
tmp = gen_rtx_MEM (Pmode, tmp);
emit_move_insn (scratch_r, tmp);
emit_move_insn (scratch_b, scratch_r);
/* Load the GP address. If ADDR is not dead here, then we must
revert the change made above via the POST_INCREMENT. */
if (!addr_dead_p)
tmp = gen_rtx_POST_DEC (Pmode, addr);
else
tmp = addr;
tmp = gen_rtx_MEM (Pmode, tmp);
emit_move_insn (pic_offset_table_rtx, tmp);
is_desc = true;
addr = scratch_b;
}
if (sibcall_p)
insn = gen_sibcall_nogp (addr);
else if (retval)
insn = gen_call_value_nogp (retval, addr, retaddr);
else
insn = gen_call_nogp (addr, retaddr);
emit_call_insn (insn);
if (gp_save)
emit_move_insn (pic_offset_table_rtx, gp_save);
if ((!TARGET_CONST_GP || is_desc) && !noreturn_p && !sibcall_p)
ia64_reload_gp ();
}
/* Begin the assembly file. */
@ -1593,39 +1670,6 @@ emit_safe_across_calls (f)
fputc ('\n', f);
}
/* Structure to be filled in by ia64_compute_frame_size with register
save masks and offsets for the current function. */
struct ia64_frame_info
{
HOST_WIDE_INT total_size; /* size of the stack frame, not including
the caller's scratch area. */
HOST_WIDE_INT spill_cfa_off; /* top of the reg spill area from the cfa. */
HOST_WIDE_INT spill_size; /* size of the gr/br/fr spill area. */
HOST_WIDE_INT extra_spill_size; /* size of spill area for others. */
HARD_REG_SET mask; /* mask of saved registers. */
unsigned int gr_used_mask; /* mask of registers in use as gr spill
registers or long-term scratches. */
int n_spilled; /* number of spilled registers. */
int reg_fp; /* register for fp. */
int reg_save_b0; /* save register for b0. */
int reg_save_pr; /* save register for prs. */
int reg_save_ar_pfs; /* save register for ar.pfs. */
int reg_save_ar_unat; /* save register for ar.unat. */
int reg_save_ar_lc; /* save register for ar.lc. */
int n_input_regs; /* number of input registers used. */
int n_local_regs; /* number of local registers used. */
int n_output_regs; /* number of output registers used. */
int n_rotate_regs; /* number of rotating registers used. */
char need_regstk; /* true if a .regstk directive needed. */
char initialized; /* true if the data is finalized. */
};
/* Current frame information calculated by ia64_compute_frame_size. */
static struct ia64_frame_info current_frame_info;
/* Helper function for ia64_compute_frame_size: find an appropriate general
register to spill some special register to. SPECIAL_SPILL_MASK contains
bits in GR0 to GR31 that have already been allocated by this routine.
@ -1867,6 +1911,17 @@ ia64_compute_frame_size (size)
extra_spill_size += 8;
n_spilled += 1;
}
/* Similarly for gp. Note that if we're calling setjmp, the stacked
registers are clobbered, so we fall back to the stack. */
current_frame_info.reg_save_gp
= (current_function_calls_setjmp ? 0 : find_gr_spill (1));
if (current_frame_info.reg_save_gp == 0)
{
SET_HARD_REG_BIT (mask, GR_REG (1));
spill_size += 8;
n_spilled += 1;
}
}
else
{
@ -2570,6 +2625,19 @@ ia64_expand_prologue ()
}
}
if (current_frame_info.reg_save_gp)
{
insn = emit_move_insn (gen_rtx_REG (DImode,
current_frame_info.reg_save_gp),
pic_offset_table_rtx);
/* We don't know for sure yet if this is actually needed, since
we've not split the PIC call patterns. If all of the calls
are indirect, and not followed by any uses of the gp, then
this save is dead. Allow it to go away. */
REG_NOTES (insn)
= gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, const0_rtx, REG_NOTES (insn));
}
/* We should now be at the base of the gr/br/fr spill area. */
if (cfa_off != (current_frame_info.spill_cfa_off
+ current_frame_info.spill_size))
@ -2751,8 +2819,13 @@ ia64_expand_epilogue (sibcall_p)
+ current_frame_info.spill_size))
abort ();
/* The GP may be stored on the stack in the prologue, but it's
never restored in the epilogue. Skip the stack slot. */
if (TEST_HARD_REG_BIT (current_frame_info.mask, GR_REG (1)))
cfa_off -= 8;
/* Restore all general registers. */
for (regno = GR_REG (1); regno <= GR_REG (31); ++regno)
for (regno = GR_REG (2); regno <= GR_REG (31); ++regno)
if (TEST_HARD_REG_BIT (current_frame_info.mask, regno))
{
reg = gen_rtx_REG (DImode, regno);
@ -2940,10 +3013,6 @@ ia64_hard_regno_rename_ok (from, to)
if (PR_REGNO_P (from) && PR_REGNO_P (to))
return (from & 1) == (to & 1);
/* Reg 4 contains the saved gp; we can't reliably rename this. */
if (from == GR_REG (4) && current_function_calls_setjmp)
return 0;
return 1;
}
@ -3572,6 +3641,23 @@ ia64_function_arg_pass_by_reference (cum, mode, type, named)
{
return type && TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST;
}
/* True if it is OK to do sibling call optimization for the specified
call expression EXP. DECL will be the called function, or NULL if
this is an indirect call. */
static bool
ia64_function_ok_for_sibcall (decl, exp)
tree decl, exp;
{
/* Direct calls are always ok. */
if (decl)
return true;
/* If TARGET_CONST_GP is in effect, then our caller expects us to
return with our current GP. This means that we'll always have
a GP reload after an indirect call. */
return !ia64_epilogue_uses (R_GR (1));
}
/* Implement va_arg. */
@ -8419,6 +8505,9 @@ ia64_output_mi_thunk (file, thunk, delta, vcall_offset, function)
{
rtx this, insn, funexp;
reload_completed = 1;
no_new_pseudos = 1;
/* Set things up as ia64_expand_prologue might. */
last_scratch_gr_reg = 15;
@ -8481,18 +8570,27 @@ ia64_output_mi_thunk (file, thunk, delta, vcall_offset, function)
ia64_expand_call (NULL_RTX, funexp, NULL_RTX, 1);
insn = get_last_insn ();
SIBLING_CALL_P (insn) = 1;
/* Code generation for calls relies on splitting. */
reload_completed = 1;
try_split (PATTERN (insn), insn, 0);
emit_barrier ();
/* Run just enough of rest_of_compilation to get the insns emitted.
There's not really enough bulk here to make other passes such as
instruction scheduling worth while. Note that use_thunk calls
assemble_start_function and assemble_end_function. */
insn = get_insns ();
emit_all_insn_group_barriers (NULL, insn);
shorten_branches (insn);
final_start_function (insn, file, 1);
final (insn, file, 1, 0);
final_end_function ();
reload_completed = 0;
no_new_pseudos = 0;
}
#include "gt-ia64.h"

View File

@ -4671,7 +4671,7 @@
(use (match_operand 3 "" ""))]
""
{
ia64_expand_call (NULL_RTX, operands[0], operands[2], 0);
ia64_expand_call (NULL_RTX, operands[0], operands[2], false);
DONE;
})
@ -4682,7 +4682,7 @@
(use (match_operand 3 "" ""))]
""
{
ia64_expand_call (NULL_RTX, operands[0], operands[2], 1);
ia64_expand_call (NULL_RTX, operands[0], operands[2], true);
DONE;
})
@ -4701,7 +4701,7 @@
(use (match_operand 4 "" ""))]
""
{
ia64_expand_call (operands[0], operands[1], operands[3], 0);
ia64_expand_call (operands[0], operands[1], operands[3], false);
DONE;
})
@ -4713,7 +4713,7 @@
(use (match_operand 4 "" ""))]
""
{
ia64_expand_call (operands[0], operands[1], operands[3], 1);
ia64_expand_call (operands[0], operands[1], operands[3], true);
DONE;
})
@ -4745,59 +4745,125 @@
DONE;
})
(define_insn "call_nopic"
[(call (mem:DI (match_operand:DI 0 "call_operand" "b,i"))
(match_operand 1 "" ""))
(define_insn "call_nogp"
[(call (mem:DI (match_operand:DI 0 "call_operand" "?b,i"))
(const_int 0))
(clobber (match_operand:DI 1 "register_operand" "=b,b"))]
""
"br.call%+.many %1 = %0"
[(set_attr "itanium_class" "br,scall")])
(define_insn "call_value_nogp"
[(set (match_operand 0 "" "")
(call (mem:DI (match_operand:DI 1 "call_operand" "?b,i"))
(const_int 0)))
(clobber (match_operand:DI 2 "register_operand" "=b,b"))]
""
"br.call%+.many %2 = %0"
"br.call%+.many %2 = %1"
[(set_attr "itanium_class" "br,scall")])
(define_insn "call_value_nopic"
[(set (match_operand 0 "" "")
(call (mem:DI (match_operand:DI 1 "call_operand" "b,i"))
(match_operand 2 "" "")))
(clobber (match_operand:DI 3 "register_operand" "=b,b"))]
""
"br.call%+.many %3 = %1"
[(set_attr "itanium_class" "br,scall")])
(define_insn "sibcall_nopic"
[(call (mem:DI (match_operand:DI 0 "call_operand" "b,i"))
(match_operand 1 "" ""))
(use (match_operand:DI 2 "register_operand" "=b,b"))
(use (match_operand:DI 3 "ar_pfs_reg_operand" ""))]
(define_insn "sibcall_nogp"
[(call (mem:DI (match_operand:DI 0 "call_operand" "?b,i"))
(const_int 0))]
""
"br%+.many %0"
[(set_attr "itanium_class" "br,scall")])
(define_insn "call_pic"
[(call (mem (match_operand 0 "call_operand" "b,i"))
(match_operand 1 "" ""))
(use (unspec [(reg:DI 1)] UNSPEC_PIC_CALL))
(clobber (match_operand:DI 2 "register_operand" "=b,b"))]
(define_insn "call_gp"
[(call (mem (match_operand 0 "call_operand" "?r,i"))
(const_int 1))
(clobber (match_operand:DI 1 "register_operand" "=b,b"))
(clobber (match_scratch:DI 2 "=&r,X"))
(clobber (match_scratch:DI 3 "=b,X"))]
""
"br.call%+.many %2 = %0"
"#"
[(set_attr "itanium_class" "br,scall")])
(define_insn "call_value_pic"
;; Irritatingly, we don't have access to INSN within the split body.
;; See commentary in ia64_split_call as to why these aren't peep2.
(define_split
[(call (mem (match_operand 0 "call_operand" ""))
(const_int 1))
(clobber (match_operand:DI 1 "register_operand" ""))
(clobber (match_scratch:DI 2 ""))
(clobber (match_scratch:DI 3 ""))]
"reload_completed && find_reg_note (insn, REG_NORETURN, NULL_RTX)"
[(const_int 0)]
{
ia64_split_call (NULL_RTX, operands[0], operands[1], operands[2],
operands[3], true, false);
DONE;
})
(define_split
[(call (mem (match_operand 0 "call_operand" ""))
(const_int 1))
(clobber (match_operand:DI 1 "register_operand" ""))
(clobber (match_scratch:DI 2 ""))
(clobber (match_scratch:DI 3 ""))]
"reload_completed"
[(const_int 0)]
{
ia64_split_call (NULL_RTX, operands[0], operands[1], operands[2],
operands[3], false, false);
DONE;
})
(define_insn "call_value_gp"
[(set (match_operand 0 "" "")
(call (mem:DI (match_operand:DI 1 "call_operand" "b,i"))
(match_operand 2 "" "")))
(use (unspec [(reg:DI 1)] UNSPEC_PIC_CALL))
(clobber (match_operand:DI 3 "register_operand" "=b,b"))]
(call (mem:DI (match_operand:DI 1 "call_operand" "?r,i"))
(const_int 1)))
(clobber (match_operand:DI 2 "register_operand" "=b,b"))
(clobber (match_scratch:DI 3 "=&r,X"))
(clobber (match_scratch:DI 4 "=b,X"))]
""
"br.call%+.many %3 = %1"
"#"
[(set_attr "itanium_class" "br,scall")])
(define_insn "sibcall_pic"
[(call (mem:DI (match_operand:DI 0 "call_operand" "bi"))
(match_operand 1 "" ""))
(use (unspec [(reg:DI 1)] UNSPEC_PIC_CALL))
(use (match_operand:DI 2 "register_operand" "=b"))
(use (match_operand:DI 3 "ar_pfs_reg_operand" ""))]
(define_split
[(set (match_operand 0 "" "")
(call (mem:DI (match_operand:DI 1 "call_operand" ""))
(const_int 1)))
(clobber (match_operand:DI 2 "register_operand" ""))
(clobber (match_scratch:DI 3 ""))
(clobber (match_scratch:DI 4 ""))]
"reload_completed && find_reg_note (insn, REG_NORETURN, NULL_RTX)"
[(const_int 0)]
{
ia64_split_call (operands[0], operands[1], operands[2], operands[3],
operands[4], true, false);
DONE;
})
(define_split
[(set (match_operand 0 "" "")
(call (mem:DI (match_operand:DI 1 "call_operand" ""))
(const_int 1)))
(clobber (match_operand:DI 2 "register_operand" ""))
(clobber (match_scratch:DI 3 ""))
(clobber (match_scratch:DI 4 ""))]
"reload_completed"
[(const_int 0)]
{
ia64_split_call (operands[0], operands[1], operands[2], operands[3],
operands[4], false, false);
DONE;
})
(define_insn_and_split "sibcall_gp"
[(call (mem:DI (match_operand:DI 0 "call_operand" "?r,i"))
(const_int 1))
(clobber (match_scratch:DI 1 "=&r,X"))
(clobber (match_scratch:DI 2 "=b,X"))]
""
"br%+.many %0"
"#"
"reload_completed"
[(const_int 0)]
{
ia64_split_call (NULL_RTX, operands[0], NULL_RTX, operands[1],
operands[2], true, true);
DONE;
}
[(set_attr "itanium_class" "br")])
(define_insn "return_internal"
@ -5263,21 +5329,11 @@
DONE;
})
;; The rest of the setjmp processing happens with the nonlocal_goto expander.
;; ??? This is not tested.
(define_expand "builtin_setjmp_setup"
[(use (match_operand:DI 0 "" ""))]
""
{
emit_move_insn (ia64_gp_save_reg (0), gen_rtx_REG (DImode, GR_REG (1)));
DONE;
})
(define_expand "builtin_setjmp_receiver"
[(use (match_operand:DI 0 "" ""))]
""
{
emit_move_insn (gen_rtx_REG (DImode, GR_REG (1)), ia64_gp_save_reg (0));
ia64_reload_gp ();
DONE;
})

View File

@ -3375,6 +3375,8 @@ try_split (pat, trial, last)
rtx tem;
rtx note, seq;
int probability;
rtx insn_last, insn;
int njumps = 0;
if (any_condjump_p (trial)
&& (note = find_reg_note (trial, REG_BR_PROB, 0)))
@ -3393,172 +3395,147 @@ try_split (pat, trial, last)
after = NEXT_INSN (after);
}
if (seq)
if (!seq)
return trial;
/* Avoid infinite loop if any insn of the result matches
the original pattern. */
insn_last = seq;
while (1)
{
/* Sometimes there will be only one insn in that list, this case will
normally arise only when we want it in turn to be split (SFmode on
the 29k is an example). */
if (NEXT_INSN (seq) != NULL_RTX)
if (INSN_P (insn_last)
&& rtx_equal_p (PATTERN (insn_last), pat))
return trial;
if (!NEXT_INSN (insn_last))
break;
insn_last = NEXT_INSN (insn_last);
}
/* Mark labels. */
for (insn = insn_last; insn ; insn = PREV_INSN (insn))
{
if (GET_CODE (insn) == JUMP_INSN)
{
rtx insn_last, insn;
int njumps = 0;
/* Avoid infinite loop if any insn of the result matches
the original pattern. */
insn_last = seq;
while (1)
mark_jump_label (PATTERN (insn), insn, 0);
njumps++;
if (probability != -1
&& any_condjump_p (insn)
&& !find_reg_note (insn, REG_BR_PROB, 0))
{
if (INSN_P (insn_last)
&& rtx_equal_p (PATTERN (insn_last), pat))
return trial;
if (NEXT_INSN (insn_last) == NULL_RTX)
break;
insn_last = NEXT_INSN (insn_last);
/* We can preserve the REG_BR_PROB notes only if exactly
one jump is created, otherwise the machine description
is responsible for this step using
split_branch_probability variable. */
if (njumps != 1)
abort ();
REG_NOTES (insn)
= gen_rtx_EXPR_LIST (REG_BR_PROB,
GEN_INT (probability),
REG_NOTES (insn));
}
}
}
/* Mark labels. */
/* If we are splitting a CALL_INSN, look for the CALL_INSN
in SEQ and copy our CALL_INSN_FUNCTION_USAGE to it. */
if (GET_CODE (trial) == CALL_INSN)
{
for (insn = insn_last; insn ; insn = PREV_INSN (insn))
if (GET_CODE (insn) == CALL_INSN)
{
CALL_INSN_FUNCTION_USAGE (insn)
= CALL_INSN_FUNCTION_USAGE (trial);
SIBLING_CALL_P (insn) = SIBLING_CALL_P (trial);
}
}
/* Copy notes, particularly those related to the CFG. */
for (note = REG_NOTES (trial); note; note = XEXP (note, 1))
{
switch (REG_NOTE_KIND (note))
{
case REG_EH_REGION:
insn = insn_last;
while (insn != NULL_RTX)
{
if (GET_CODE (insn) == CALL_INSN
|| (flag_non_call_exceptions
&& may_trap_p (PATTERN (insn))))
REG_NOTES (insn)
= gen_rtx_EXPR_LIST (REG_EH_REGION,
XEXP (note, 0),
REG_NOTES (insn));
insn = PREV_INSN (insn);
}
break;
case REG_NORETURN:
case REG_SETJMP:
case REG_ALWAYS_RETURN:
insn = insn_last;
while (insn != NULL_RTX)
{
if (GET_CODE (insn) == CALL_INSN)
REG_NOTES (insn)
= gen_rtx_EXPR_LIST (REG_NOTE_KIND (note),
XEXP (note, 0),
REG_NOTES (insn));
insn = PREV_INSN (insn);
}
break;
case REG_NON_LOCAL_GOTO:
insn = insn_last;
while (insn != NULL_RTX)
{
if (GET_CODE (insn) == JUMP_INSN)
{
mark_jump_label (PATTERN (insn), insn, 0);
njumps++;
if (probability != -1
&& any_condjump_p (insn)
&& !find_reg_note (insn, REG_BR_PROB, 0))
{
/* We can preserve the REG_BR_PROB notes only if exactly
one jump is created, otherwise the machine description
is responsible for this step using
split_branch_probability variable. */
if (njumps != 1)
abort ();
REG_NOTES (insn)
= gen_rtx_EXPR_LIST (REG_BR_PROB,
GEN_INT (probability),
REG_NOTES (insn));
}
}
REG_NOTES (insn)
= gen_rtx_EXPR_LIST (REG_NOTE_KIND (note),
XEXP (note, 0),
REG_NOTES (insn));
insn = PREV_INSN (insn);
}
break;
/* If we are splitting a CALL_INSN, look for the CALL_INSN
in SEQ and copy our CALL_INSN_FUNCTION_USAGE to it. */
if (GET_CODE (trial) == CALL_INSN)
{
insn = insn_last;
while (insn != NULL_RTX)
{
if (GET_CODE (insn) == CALL_INSN)
CALL_INSN_FUNCTION_USAGE (insn)
= CALL_INSN_FUNCTION_USAGE (trial);
insn = PREV_INSN (insn);
}
}
/* Copy notes, particularly those related to the CFG. */
for (note = REG_NOTES (trial); note; note = XEXP (note, 1))
{
switch (REG_NOTE_KIND (note))
{
case REG_EH_REGION:
insn = insn_last;
while (insn != NULL_RTX)
{
if (GET_CODE (insn) == CALL_INSN
|| (flag_non_call_exceptions
&& may_trap_p (PATTERN (insn))))
REG_NOTES (insn)
= gen_rtx_EXPR_LIST (REG_EH_REGION,
XEXP (note, 0),
REG_NOTES (insn));
insn = PREV_INSN (insn);
}
break;
case REG_NORETURN:
case REG_SETJMP:
case REG_ALWAYS_RETURN:
insn = insn_last;
while (insn != NULL_RTX)
{
if (GET_CODE (insn) == CALL_INSN)
REG_NOTES (insn)
= gen_rtx_EXPR_LIST (REG_NOTE_KIND (note),
XEXP (note, 0),
REG_NOTES (insn));
insn = PREV_INSN (insn);
}
break;
case REG_NON_LOCAL_GOTO:
insn = insn_last;
while (insn != NULL_RTX)
{
if (GET_CODE (insn) == JUMP_INSN)
REG_NOTES (insn)
= gen_rtx_EXPR_LIST (REG_NOTE_KIND (note),
XEXP (note, 0),
REG_NOTES (insn));
insn = PREV_INSN (insn);
}
break;
default:
break;
}
}
/* If there are LABELS inside the split insns increment the
usage count so we don't delete the label. */
if (GET_CODE (trial) == INSN)
{
insn = insn_last;
while (insn != NULL_RTX)
{
if (GET_CODE (insn) == INSN)
mark_label_nuses (PATTERN (insn));
insn = PREV_INSN (insn);
}
}
tem = emit_insn_after_scope (seq, trial, INSN_SCOPE (trial));
delete_insn (trial);
if (has_barrier)
emit_barrier_after (tem);
/* Recursively call try_split for each new insn created; by the
time control returns here that insn will be fully split, so
set LAST and continue from the insn after the one returned.
We can't use next_active_insn here since AFTER may be a note.
Ignore deleted insns, which can be occur if not optimizing. */
for (tem = NEXT_INSN (before); tem != after; tem = NEXT_INSN (tem))
if (! INSN_DELETED_P (tem) && INSN_P (tem))
tem = try_split (PATTERN (tem), tem, 1);
default:
break;
}
/* Avoid infinite loop if the result matches the original pattern. */
else if (rtx_equal_p (PATTERN (seq), pat))
return trial;
else
{
PATTERN (trial) = PATTERN (seq);
INSN_CODE (trial) = -1;
try_split (PATTERN (trial), trial, last);
}
/* Return either the first or the last insn, depending on which was
requested. */
return last
? (after ? PREV_INSN (after) : last_insn)
: NEXT_INSN (before);
}
return trial;
/* If there are LABELS inside the split insns increment the
usage count so we don't delete the label. */
if (GET_CODE (trial) == INSN)
{
insn = insn_last;
while (insn != NULL_RTX)
{
if (GET_CODE (insn) == INSN)
mark_label_nuses (PATTERN (insn));
insn = PREV_INSN (insn);
}
}
tem = emit_insn_after_scope (seq, trial, INSN_SCOPE (trial));
delete_insn (trial);
if (has_barrier)
emit_barrier_after (tem);
/* Recursively call try_split for each new insn created; by the
time control returns here that insn will be fully split, so
set LAST and continue from the insn after the one returned.
We can't use next_active_insn here since AFTER may be a note.
Ignore deleted insns, which can be occur if not optimizing. */
for (tem = NEXT_INSN (before); tem != after; tem = NEXT_INSN (tem))
if (! INSN_DELETED_P (tem) && INSN_P (tem))
tem = try_split (PATTERN (tem), tem, 1);
/* Return either the first or the last insn, depending on which was
requested. */
return last
? (after ? PREV_INSN (after) : last_insn)
: NEXT_INSN (before);
}
/* Make and return an INSN rtx, initializing all its slots.