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:
parent
7e38bf41d9
commit
599aedd920
5 changed files with 482 additions and 323 deletions
|
@ -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,
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
})
|
||||
|
||||
|
|
279
gcc/emit-rtl.c
279
gcc/emit-rtl.c
|
@ -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.
|
||||
|
|
Loading…
Add table
Reference in a new issue