save_world/rest_world for ppc-darwin.
Approved by Geoff Keating. From-SVN: r86191
This commit is contained in:
parent
790ac0e12a
commit
d62294f57e
@ -1,3 +1,29 @@
|
||||
2004-08-18 Fariborz Jahanian <fjahanian@apple.com>
|
||||
|
||||
* config/rs6000/altivec.md: Add new patterns for calls to
|
||||
save_world/rest_world functions.
|
||||
|
||||
* config/rs6000/rs6000-protos.h: (save_world_operation,
|
||||
restore_world_operation) new declarations.
|
||||
|
||||
* config/rs6000/rs6000.c: (struct rs6000_stack) new world_save_p
|
||||
field added.
|
||||
(rs6000_stack_info): Set world_save_p field.
|
||||
(save_world_operation): New function.
|
||||
(restore_world_operation): New function.
|
||||
(compute_save_world_info): New function.
|
||||
(rs6000_stack_info): Call compute_save_world_info.
|
||||
(rs6000_emit_prologue): Check for world_save_p and generate
|
||||
pattern to call save_world for saving all non-volatile and
|
||||
special registers.
|
||||
(rs6000_emit_epilogue): Check for world_save_p and generate
|
||||
pattern to call rest_world to restore saved registers.
|
||||
|
||||
config/rs6000/rs6000.h: macros FIRST_SAVED_ALTIVEC_REGNO,
|
||||
FIRST_SAVED_FP_REGNO, FIRST_SAVED_GP_REGNO defined.
|
||||
(rs6000_reg_names): New entries added for save_world_operation and
|
||||
restore_world_operation.
|
||||
|
||||
2004-08-18 Caroline Tice <ctice@apple.com>
|
||||
|
||||
* Makefile.in (STAGEFEEDBACK_FLAGS_TO_PASS) Add
|
||||
|
@ -295,6 +295,28 @@
|
||||
}"
|
||||
[(set_attr "type" "*")])
|
||||
|
||||
(define_insn "*save_world"
|
||||
[(match_parallel 0 "save_world_operation"
|
||||
[(clobber (match_operand:SI 1 "register_operand" "=l"))
|
||||
(use (match_operand:SI 2 "call_operand" "s"))])]
|
||||
"TARGET_MACHO && (DEFAULT_ABI == ABI_DARWIN) && TARGET_32BIT"
|
||||
{
|
||||
return "bl %z2";
|
||||
}
|
||||
[(set_attr "type" "branch")
|
||||
(set_attr "length" "4")])
|
||||
|
||||
(define_insn "*restore_world"
|
||||
[(match_parallel 0 "restore_world_operation"
|
||||
[(return)
|
||||
(use (match_operand:SI 1 "register_operand" "l"))
|
||||
(use (match_operand:SI 2 "call_operand" "s"))
|
||||
(clobber (match_operand:SI 3 "gpc_reg_operand" "=r"))])]
|
||||
"TARGET_MACHO && (DEFAULT_ABI == ABI_DARWIN) && TARGET_32BIT"
|
||||
{
|
||||
return "b %z2";
|
||||
})
|
||||
|
||||
;; Simple binary operations.
|
||||
|
||||
(define_insn "addv16qi3"
|
||||
|
@ -89,6 +89,8 @@ extern bool legitimate_constant_pool_address_p (rtx);
|
||||
extern int expand_block_clear (rtx[]);
|
||||
extern int expand_block_move (rtx[]);
|
||||
extern int load_multiple_operation (rtx, enum machine_mode);
|
||||
extern int save_world_operation (rtx, enum machine_mode);
|
||||
extern int restore_world_operation (rtx, enum machine_mode);
|
||||
extern const char * rs6000_output_load_multiple (rtx[]);
|
||||
extern int store_multiple_operation (rtx, enum machine_mode);
|
||||
extern int branch_comparison_operator (rtx, enum machine_mode);
|
||||
|
@ -79,6 +79,8 @@ typedef struct rs6000_stack {
|
||||
int toc_save_p; /* true if the TOC needs to be saved */
|
||||
int push_p; /* true if we need to allocate stack space */
|
||||
int calls_p; /* true if the function makes any calls */
|
||||
int world_save_p; /* true if we're saving *everything*:
|
||||
r13-r31, cr, f14-f31, vrsave, v20-v31 */
|
||||
enum rs6000_abi abi; /* which ABI to use */
|
||||
int gp_save_offset; /* offset to save GP regs from initial SP */
|
||||
int fp_save_offset; /* offset to save FP regs from initial SP */
|
||||
@ -714,6 +716,7 @@ static void rs6000_parse_tls_size_option (void);
|
||||
static void rs6000_parse_yes_no_option (const char *, const char *, int *);
|
||||
static int first_altivec_reg_to_save (void);
|
||||
static unsigned int compute_vrsave_mask (void);
|
||||
static void compute_save_world_info(rs6000_stack_t *info_ptr);
|
||||
static void is_altivec_return_reg (rtx, void *);
|
||||
static rtx generate_set_vrsave (rtx, rs6000_stack_t *, int);
|
||||
int easy_vector_constant (rtx, enum machine_mode);
|
||||
@ -8529,6 +8532,143 @@ expand_block_move (rtx operands[])
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* Return 1 if OP is suitable for a save_world call in prologue. It is
|
||||
known to be a PARALLEL. */
|
||||
int
|
||||
save_world_operation (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
|
||||
{
|
||||
int index;
|
||||
int i;
|
||||
rtx elt;
|
||||
int count = XVECLEN (op, 0);
|
||||
|
||||
if (count != 55)
|
||||
return 0;
|
||||
|
||||
index = 0;
|
||||
if (GET_CODE (XVECEXP (op, 0, index++)) != CLOBBER
|
||||
|| GET_CODE (XVECEXP (op, 0, index++)) != USE)
|
||||
return 0;
|
||||
|
||||
for (i=1; i <= 18; i++)
|
||||
{
|
||||
elt = XVECEXP (op, 0, index++);
|
||||
if (GET_CODE (elt) != SET
|
||||
|| GET_CODE (SET_DEST (elt)) != MEM
|
||||
|| ! memory_operand (SET_DEST (elt), DFmode)
|
||||
|| GET_CODE (SET_SRC (elt)) != REG
|
||||
|| GET_MODE (SET_SRC (elt)) != DFmode)
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i=1; i <= 12; i++)
|
||||
{
|
||||
elt = XVECEXP (op, 0, index++);
|
||||
if (GET_CODE (elt) != SET
|
||||
|| GET_CODE (SET_DEST (elt)) != MEM
|
||||
|| GET_CODE (SET_SRC (elt)) != REG
|
||||
|| GET_MODE (SET_SRC (elt)) != V4SImode)
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i=1; i <= 19; i++)
|
||||
{
|
||||
elt = XVECEXP (op, 0, index++);
|
||||
if (GET_CODE (elt) != SET
|
||||
|| GET_CODE (SET_DEST (elt)) != MEM
|
||||
|| ! memory_operand (SET_DEST (elt), Pmode)
|
||||
|| GET_CODE (SET_SRC (elt)) != REG
|
||||
|| GET_MODE (SET_SRC (elt)) != Pmode)
|
||||
return 0;
|
||||
}
|
||||
|
||||
elt = XVECEXP (op, 0, index++);
|
||||
if (GET_CODE (elt) != SET
|
||||
|| GET_CODE (SET_DEST (elt)) != MEM
|
||||
|| ! memory_operand (SET_DEST (elt), Pmode)
|
||||
|| GET_CODE (SET_SRC (elt)) != REG
|
||||
|| REGNO (SET_SRC (elt)) != CR2_REGNO
|
||||
|| GET_MODE (SET_SRC (elt)) != Pmode)
|
||||
return 0;
|
||||
|
||||
if (GET_CODE (XVECEXP (op, 0, index++)) != USE
|
||||
|| GET_CODE (XVECEXP (op, 0, index++)) != USE
|
||||
|| GET_CODE (XVECEXP (op, 0, index++)) != CLOBBER)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Return 1 if OP is suitable for a save_world call in prologue. It is
|
||||
known to be a PARALLEL. */
|
||||
int
|
||||
restore_world_operation (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
|
||||
{
|
||||
int index;
|
||||
int i;
|
||||
rtx elt;
|
||||
int count = XVECLEN (op, 0);
|
||||
|
||||
if (count != 59)
|
||||
return 0;
|
||||
|
||||
index = 0;
|
||||
if (GET_CODE (XVECEXP (op, 0, index++)) != RETURN
|
||||
|| GET_CODE (XVECEXP (op, 0, index++)) != USE
|
||||
|| GET_CODE (XVECEXP (op, 0, index++)) != USE
|
||||
|| GET_CODE (XVECEXP (op, 0, index++)) != CLOBBER)
|
||||
return 0;
|
||||
|
||||
elt = XVECEXP (op, 0, index++);
|
||||
if (GET_CODE (elt) != SET
|
||||
|| GET_CODE (SET_SRC (elt)) != MEM
|
||||
|| ! memory_operand (SET_SRC (elt), Pmode)
|
||||
|| GET_CODE (SET_DEST (elt)) != REG
|
||||
|| REGNO (SET_DEST (elt)) != CR2_REGNO
|
||||
|| GET_MODE (SET_DEST (elt)) != Pmode)
|
||||
return 0;
|
||||
|
||||
for (i=1; i <= 19; i++)
|
||||
{
|
||||
elt = XVECEXP (op, 0, index++);
|
||||
if (GET_CODE (elt) != SET
|
||||
|| GET_CODE (SET_SRC (elt)) != MEM
|
||||
|| ! memory_operand (SET_SRC (elt), Pmode)
|
||||
|| GET_CODE (SET_DEST (elt)) != REG
|
||||
|| GET_MODE (SET_DEST (elt)) != Pmode)
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i=1; i <= 12; i++)
|
||||
{
|
||||
elt = XVECEXP (op, 0, index++);
|
||||
if (GET_CODE (elt) != SET
|
||||
|| GET_CODE (SET_SRC (elt)) != MEM
|
||||
|| GET_CODE (SET_DEST (elt)) != REG
|
||||
|| GET_MODE (SET_DEST (elt)) != V4SImode)
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i=1; i <= 18; i++)
|
||||
{
|
||||
elt = XVECEXP (op, 0, index++);
|
||||
if (GET_CODE (elt) != SET
|
||||
|| GET_CODE (SET_SRC (elt)) != MEM
|
||||
|| ! memory_operand (SET_SRC (elt), DFmode)
|
||||
|| GET_CODE (SET_DEST (elt)) != REG
|
||||
|| GET_MODE (SET_DEST (elt)) != DFmode)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (GET_CODE (XVECEXP (op, 0, index++)) != CLOBBER
|
||||
|| GET_CODE (XVECEXP (op, 0, index++)) != CLOBBER
|
||||
|| GET_CODE (XVECEXP (op, 0, index++)) != CLOBBER
|
||||
|| GET_CODE (XVECEXP (op, 0, index++)) != CLOBBER
|
||||
|| GET_CODE (XVECEXP (op, 0, index++)) != USE)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* Return 1 if OP is a load multiple operation. It is known to be a
|
||||
PARALLEL and the first section will be tested. */
|
||||
@ -11362,6 +11502,57 @@ compute_vrsave_mask (void)
|
||||
return mask;
|
||||
}
|
||||
|
||||
/* For a very restricted set of circumstances, we can cut down the
|
||||
size of prologs/epilogs by calling our own save/restore-the-world
|
||||
routines. */
|
||||
|
||||
static void
|
||||
compute_save_world_info(rs6000_stack_t *info_ptr)
|
||||
{
|
||||
info_ptr->world_save_p =
|
||||
(DEFAULT_ABI == ABI_DARWIN)
|
||||
&& ! (current_function_calls_setjmp && flag_exceptions)
|
||||
&& info_ptr->first_fp_reg_save == FIRST_SAVED_FP_REGNO
|
||||
&& info_ptr->first_gp_reg_save == FIRST_SAVED_GP_REGNO
|
||||
&& info_ptr->first_altivec_reg_save == FIRST_SAVED_ALTIVEC_REGNO
|
||||
&& info_ptr->cr_save_p;
|
||||
|
||||
/* This will not work in conjunction with sibcalls. Make sure there
|
||||
are none. (This check is expensive, but seldom executed.) */
|
||||
if ( info_ptr->world_save_p )
|
||||
{
|
||||
rtx insn;
|
||||
for ( insn = get_last_insn_anywhere (); insn; insn = PREV_INSN (insn))
|
||||
if ( GET_CODE (insn) == CALL_INSN
|
||||
&& SIBLING_CALL_P (insn))
|
||||
{
|
||||
info_ptr->world_save_p = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (info_ptr->world_save_p)
|
||||
{
|
||||
/* Even if we're not touching VRsave, make sure there's room on the
|
||||
stack for it, if it looks like we're calling SAVE_WORLD, which
|
||||
will attempt to save it. */
|
||||
info_ptr->vrsave_size = 4;
|
||||
|
||||
/* "Save" the VRsave register too if we're saving the world. */
|
||||
if (info_ptr->vrsave_mask == 0)
|
||||
info_ptr->vrsave_mask = compute_vrsave_mask ();
|
||||
|
||||
/* Because the Darwin register save/restore routines only handle
|
||||
F14 .. F31 and V20 .. V31 as per the ABI, perform a consistancy
|
||||
check and abort if there's something worng. */
|
||||
if (info_ptr->first_fp_reg_save < FIRST_SAVED_FP_REGNO
|
||||
|| info_ptr->first_altivec_reg_save < FIRST_SAVED_ALTIVEC_REGNO)
|
||||
abort ();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
is_altivec_return_reg (rtx reg, void *xyes)
|
||||
{
|
||||
@ -11602,6 +11793,8 @@ rs6000_stack_info (void)
|
||||
else
|
||||
info_ptr->vrsave_size = 0;
|
||||
|
||||
compute_save_world_info (info_ptr);
|
||||
|
||||
/* Calculate the offsets. */
|
||||
switch (DEFAULT_ABI)
|
||||
{
|
||||
@ -12716,8 +12909,128 @@ rs6000_emit_prologue (void)
|
||||
rs6000_emit_stack_tie ();
|
||||
}
|
||||
|
||||
/* Handle world saves specially here. */
|
||||
if (info->world_save_p)
|
||||
{
|
||||
int i, j, sz;
|
||||
rtx treg;
|
||||
rtvec p;
|
||||
|
||||
/* save_world expects lr in r0. */
|
||||
if (info->lr_save_p)
|
||||
{
|
||||
insn = emit_move_insn (gen_rtx_REG (Pmode, 0),
|
||||
gen_rtx_REG (Pmode, LINK_REGISTER_REGNUM));
|
||||
RTX_FRAME_RELATED_P (insn) = 1;
|
||||
}
|
||||
|
||||
/* The SAVE_WORLD and RESTORE_WORLD routines make a number of
|
||||
assumptions about the offsets of various bits of the stack
|
||||
frame. Abort if things aren't what they should be. */
|
||||
if (info->gp_save_offset != -220
|
||||
|| info->fp_save_offset != -144
|
||||
|| info->lr_save_offset != 8
|
||||
|| info->cr_save_offset != 4
|
||||
|| !info->push_p
|
||||
|| !info->lr_save_p
|
||||
|| (current_function_calls_eh_return && info->ehrd_offset != -432)
|
||||
|| (info->vrsave_save_offset != -224
|
||||
|| info->altivec_save_offset != (-224 -16 -192)))
|
||||
abort ();
|
||||
|
||||
treg = gen_rtx_REG (SImode, 11);
|
||||
emit_move_insn (treg, GEN_INT (-info->total_size));
|
||||
|
||||
/* SAVE_WORLD takes the caller's LR in R0 and the frame size
|
||||
in R11. It also clobbers R12, so beware! */
|
||||
|
||||
/* Preserve CR2 for save_world prologues */
|
||||
sz = 6;
|
||||
sz += 32 - info->first_gp_reg_save;
|
||||
sz += 64 - info->first_fp_reg_save;
|
||||
sz += LAST_ALTIVEC_REGNO - info->first_altivec_reg_save + 1;
|
||||
p = rtvec_alloc (sz);
|
||||
j = 0;
|
||||
RTVEC_ELT (p, j++) = gen_rtx_CLOBBER (VOIDmode,
|
||||
gen_rtx_REG (Pmode,
|
||||
LINK_REGISTER_REGNUM));
|
||||
RTVEC_ELT (p, j++) = gen_rtx_USE (VOIDmode,
|
||||
gen_rtx_SYMBOL_REF (Pmode,
|
||||
"*save_world"));
|
||||
/* We do floats first so that the instruction pattern matches
|
||||
properly. */
|
||||
for (i = 0; i < 64 - info->first_fp_reg_save; i++)
|
||||
{
|
||||
rtx reg = gen_rtx_REG (DFmode, info->first_fp_reg_save + i);
|
||||
rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
|
||||
GEN_INT (info->fp_save_offset
|
||||
+ sp_offset + 8 * i));
|
||||
rtx mem = gen_rtx_MEM (DFmode, addr);
|
||||
set_mem_alias_set (mem, rs6000_sr_alias_set);
|
||||
|
||||
RTVEC_ELT (p, j++) = gen_rtx_SET (VOIDmode, mem, reg);
|
||||
}
|
||||
for (i = 0; info->first_altivec_reg_save + i <= LAST_ALTIVEC_REGNO; i++)
|
||||
{
|
||||
rtx reg = gen_rtx_REG (V4SImode, info->first_altivec_reg_save + i);
|
||||
rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
|
||||
GEN_INT (info->altivec_save_offset
|
||||
+ sp_offset + 16 * i));
|
||||
rtx mem = gen_rtx_MEM (V4SImode, addr);
|
||||
set_mem_alias_set (mem, rs6000_sr_alias_set);
|
||||
|
||||
RTVEC_ELT (p, j++) = gen_rtx_SET (VOIDmode, mem, reg);
|
||||
}
|
||||
for (i = 0; i < 32 - info->first_gp_reg_save; i++)
|
||||
{
|
||||
rtx reg = gen_rtx_REG (reg_mode, info->first_gp_reg_save + i);
|
||||
rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
|
||||
GEN_INT (info->gp_save_offset
|
||||
+ sp_offset + reg_size * i));
|
||||
rtx mem = gen_rtx_MEM (reg_mode, addr);
|
||||
set_mem_alias_set (mem, rs6000_sr_alias_set);
|
||||
|
||||
RTVEC_ELT (p, j++) = gen_rtx_SET (VOIDmode, mem, reg);
|
||||
}
|
||||
|
||||
{
|
||||
/* CR register traditionally saved as CR2. */
|
||||
rtx reg = gen_rtx_REG (reg_mode, CR2_REGNO);
|
||||
rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
|
||||
GEN_INT (info->cr_save_offset
|
||||
+ sp_offset));
|
||||
rtx mem = gen_rtx_MEM (reg_mode, addr);
|
||||
set_mem_alias_set (mem, rs6000_sr_alias_set);
|
||||
|
||||
RTVEC_ELT (p, j++) = gen_rtx_SET (VOIDmode, mem, reg);
|
||||
}
|
||||
/* Prevent any attempt to delete the setting of r0 and treg! */
|
||||
RTVEC_ELT (p, j++) = gen_rtx_USE (VOIDmode, gen_rtx_REG (Pmode, 0));
|
||||
RTVEC_ELT (p, j++) = gen_rtx_USE (VOIDmode, treg);
|
||||
RTVEC_ELT (p, j++) = gen_rtx_CLOBBER (VOIDmode, sp_reg_rtx);
|
||||
|
||||
insn = emit_insn (gen_rtx_PARALLEL (VOIDmode, p));
|
||||
rs6000_frame_related (insn, frame_ptr_rtx, info->total_size,
|
||||
NULL_RTX, NULL_RTX);
|
||||
|
||||
if (current_function_calls_eh_return)
|
||||
{
|
||||
unsigned int i;
|
||||
for (i = 0; ; ++i)
|
||||
{
|
||||
unsigned int regno = EH_RETURN_DATA_REGNO (i);
|
||||
if (regno == INVALID_REGNUM)
|
||||
break;
|
||||
emit_frame_save (frame_reg_rtx, frame_ptr_rtx, reg_mode, regno,
|
||||
info->ehrd_offset + sp_offset
|
||||
+ reg_size * (int) i,
|
||||
info->total_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Save AltiVec registers if needed. */
|
||||
if (TARGET_ALTIVEC_ABI && info->altivec_size != 0)
|
||||
if (! info->world_save_p && TARGET_ALTIVEC_ABI && info->altivec_size != 0)
|
||||
{
|
||||
int i;
|
||||
|
||||
@ -12758,7 +13071,7 @@ rs6000_emit_prologue (void)
|
||||
epilogue. */
|
||||
|
||||
if (TARGET_ALTIVEC && TARGET_ALTIVEC_VRSAVE
|
||||
&& info->vrsave_mask != 0)
|
||||
&& ! info->world_save_p && info->vrsave_mask != 0)
|
||||
{
|
||||
rtx reg, mem, vrsave;
|
||||
int offset;
|
||||
@ -12786,7 +13099,7 @@ rs6000_emit_prologue (void)
|
||||
}
|
||||
|
||||
/* If we use the link register, get it into r0. */
|
||||
if (info->lr_save_p)
|
||||
if (! info->world_save_p && info->lr_save_p)
|
||||
{
|
||||
insn = emit_move_insn (gen_rtx_REG (Pmode, 0),
|
||||
gen_rtx_REG (Pmode, LINK_REGISTER_REGNUM));
|
||||
@ -12794,7 +13107,7 @@ rs6000_emit_prologue (void)
|
||||
}
|
||||
|
||||
/* If we need to save CR, put it into r12. */
|
||||
if (info->cr_save_p && frame_reg_rtx != frame_ptr_rtx)
|
||||
if (! info->world_save_p && info->cr_save_p && frame_reg_rtx != frame_ptr_rtx)
|
||||
{
|
||||
rtx set;
|
||||
|
||||
@ -12816,7 +13129,7 @@ rs6000_emit_prologue (void)
|
||||
|
||||
/* Do any required saving of fpr's. If only one or two to save, do
|
||||
it ourselves. Otherwise, call function. */
|
||||
if (saving_FPRs_inline)
|
||||
if (! info->world_save_p && saving_FPRs_inline)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 64 - info->first_fp_reg_save; i++)
|
||||
@ -12827,7 +13140,7 @@ rs6000_emit_prologue (void)
|
||||
info->fp_save_offset + sp_offset + 8 * i,
|
||||
info->total_size);
|
||||
}
|
||||
else if (info->first_fp_reg_save != 64)
|
||||
else if (! info->world_save_p && info->first_fp_reg_save != 64)
|
||||
{
|
||||
int i;
|
||||
char rname[30];
|
||||
@ -12863,7 +13176,7 @@ rs6000_emit_prologue (void)
|
||||
|
||||
/* Save GPRs. This is done as a PARALLEL if we are using
|
||||
the store-multiple instructions. */
|
||||
if (using_store_multiple)
|
||||
if (! info->world_save_p && using_store_multiple)
|
||||
{
|
||||
rtvec p;
|
||||
int i;
|
||||
@ -12885,7 +13198,7 @@ rs6000_emit_prologue (void)
|
||||
rs6000_frame_related (insn, frame_ptr_rtx, info->total_size,
|
||||
NULL_RTX, NULL_RTX);
|
||||
}
|
||||
else
|
||||
else if (! info->world_save_p)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 32 - info->first_gp_reg_save; i++)
|
||||
@ -12944,7 +13257,7 @@ rs6000_emit_prologue (void)
|
||||
|
||||
/* ??? There's no need to emit actual instructions here, but it's the
|
||||
easiest way to get the frame unwind information emitted. */
|
||||
if (current_function_calls_eh_return)
|
||||
if (! info->world_save_p && current_function_calls_eh_return)
|
||||
{
|
||||
unsigned int i, regno;
|
||||
|
||||
@ -12979,7 +13292,7 @@ rs6000_emit_prologue (void)
|
||||
}
|
||||
|
||||
/* Save lr if we used it. */
|
||||
if (info->lr_save_p)
|
||||
if (! info->world_save_p && info->lr_save_p)
|
||||
{
|
||||
rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
|
||||
GEN_INT (info->lr_save_offset + sp_offset));
|
||||
@ -12994,7 +13307,7 @@ rs6000_emit_prologue (void)
|
||||
}
|
||||
|
||||
/* Save CR if we use any that must be preserved. */
|
||||
if (info->cr_save_p)
|
||||
if (! info->world_save_p && info->cr_save_p)
|
||||
{
|
||||
rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
|
||||
GEN_INT (info->cr_save_offset + sp_offset));
|
||||
@ -13027,7 +13340,7 @@ rs6000_emit_prologue (void)
|
||||
|
||||
/* Update stack and set back pointer unless this is V.4,
|
||||
for which it was done previously. */
|
||||
if (info->push_p
|
||||
if (! info->world_save_p && info->push_p
|
||||
&& !(DEFAULT_ABI == ABI_V4 || current_function_calls_eh_return))
|
||||
rs6000_emit_allocate_stack (info->total_size, FALSE);
|
||||
|
||||
@ -13196,6 +13509,101 @@ rs6000_emit_epilogue (int sibcall)
|
||||
|| rs6000_cpu == PROCESSOR_PPC750
|
||||
|| optimize_size);
|
||||
|
||||
if (info->world_save_p)
|
||||
{
|
||||
int i, j;
|
||||
char rname[30];
|
||||
const char *alloc_rname;
|
||||
rtvec p;
|
||||
|
||||
/* eh_rest_world_r10 will return to the location saved in the LR
|
||||
stack slot (which is not likely to be our caller.)
|
||||
Input: R10 -- stack adjustment. Clobbers R0, R11, R12, R7, R8.
|
||||
rest_world is similar, except any R10 parameter is ignored.
|
||||
The exception-handling stuff that was here in 2.95 is no
|
||||
longer necessary. */
|
||||
|
||||
p = rtvec_alloc (9
|
||||
+ 1
|
||||
+ 32 - info->first_gp_reg_save
|
||||
+ LAST_ALTIVEC_REGNO + 1 - info->first_altivec_reg_save
|
||||
+ 63 + 1 - info->first_fp_reg_save);
|
||||
|
||||
strcpy (rname, (current_function_calls_eh_return) ?
|
||||
"*eh_rest_world_r10" : "*rest_world");
|
||||
alloc_rname = ggc_strdup (rname);
|
||||
|
||||
j = 0;
|
||||
RTVEC_ELT (p, j++) = gen_rtx_RETURN (VOIDmode);
|
||||
RTVEC_ELT (p, j++) = gen_rtx_USE (VOIDmode,
|
||||
gen_rtx_REG (Pmode,
|
||||
LINK_REGISTER_REGNUM));
|
||||
RTVEC_ELT (p, j++)
|
||||
= gen_rtx_USE (VOIDmode, gen_rtx_SYMBOL_REF (Pmode, alloc_rname));
|
||||
/* The instruction pattern requires a clobber here;
|
||||
it is shared with the restVEC helper. */
|
||||
RTVEC_ELT (p, j++)
|
||||
= gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (Pmode, 11));
|
||||
|
||||
{
|
||||
/* CR register traditionally saved as CR2. */
|
||||
rtx reg = gen_rtx_REG (reg_mode, CR2_REGNO);
|
||||
rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
|
||||
GEN_INT (info->cr_save_offset));
|
||||
rtx mem = gen_rtx_MEM (reg_mode, addr);
|
||||
set_mem_alias_set (mem, rs6000_sr_alias_set);
|
||||
|
||||
RTVEC_ELT (p, j++) = gen_rtx_SET (VOIDmode, reg, mem);
|
||||
}
|
||||
|
||||
for (i = 0; i < 32 - info->first_gp_reg_save; i++)
|
||||
{
|
||||
rtx reg = gen_rtx_REG (reg_mode, info->first_gp_reg_save + i);
|
||||
rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
|
||||
GEN_INT (info->gp_save_offset
|
||||
+ reg_size * i));
|
||||
rtx mem = gen_rtx_MEM (reg_mode, addr);
|
||||
set_mem_alias_set (mem, rs6000_sr_alias_set);
|
||||
|
||||
RTVEC_ELT (p, j++) = gen_rtx_SET (VOIDmode, reg, mem);
|
||||
}
|
||||
for (i = 0; info->first_altivec_reg_save + i <= LAST_ALTIVEC_REGNO; i++)
|
||||
{
|
||||
rtx reg = gen_rtx_REG (V4SImode, info->first_altivec_reg_save + i);
|
||||
rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
|
||||
GEN_INT (info->altivec_save_offset
|
||||
+ 16 * i));
|
||||
rtx mem = gen_rtx_MEM (V4SImode, addr);
|
||||
set_mem_alias_set (mem, rs6000_sr_alias_set);
|
||||
|
||||
RTVEC_ELT (p, j++) = gen_rtx_SET (VOIDmode, reg, mem);
|
||||
}
|
||||
for (i = 0; info->first_fp_reg_save + i <= 63; i++)
|
||||
{
|
||||
rtx reg = gen_rtx_REG (DFmode, info->first_fp_reg_save + i);
|
||||
rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
|
||||
GEN_INT (info->fp_save_offset
|
||||
+ 8 * i));
|
||||
rtx mem = gen_rtx_MEM (DFmode, addr);
|
||||
set_mem_alias_set (mem, rs6000_sr_alias_set);
|
||||
|
||||
RTVEC_ELT (p, j++) = gen_rtx_SET (VOIDmode, reg, mem);
|
||||
}
|
||||
RTVEC_ELT (p, j++)
|
||||
= gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (Pmode, 0));
|
||||
RTVEC_ELT (p, j++)
|
||||
= gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (SImode, 12));
|
||||
RTVEC_ELT (p, j++)
|
||||
= gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (SImode, 7));
|
||||
RTVEC_ELT (p, j++)
|
||||
= gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (SImode, 8));
|
||||
RTVEC_ELT (p, j++)
|
||||
= gen_rtx_USE (VOIDmode, gen_rtx_REG (SImode, 10));
|
||||
emit_jump_insn (gen_rtx_PARALLEL (VOIDmode, p));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* If we have a frame pointer, a call to alloca, or a large stack
|
||||
frame, restore the old stack pointer using the backchain. Otherwise,
|
||||
we know what size to update it with. */
|
||||
|
@ -916,6 +916,10 @@ extern const char *rs6000_warn_altivec_long_switch;
|
||||
#define SPE_ACC_REGNO 111
|
||||
#define SPEFSCR_REGNO 112
|
||||
|
||||
#define FIRST_SAVED_ALTIVEC_REGNO (FIRST_ALTIVEC_REGNO+20)
|
||||
#define FIRST_SAVED_FP_REGNO (14+32)
|
||||
#define FIRST_SAVED_GP_REGNO 13
|
||||
|
||||
/* List the order in which to allocate registers. Each register must be
|
||||
listed once, even those in FIXED_REGISTERS.
|
||||
|
||||
@ -2587,6 +2591,8 @@ extern char rs6000_reg_names[][8]; /* register names (0 vs. %r0). */
|
||||
{"lmw_operation", {PARALLEL}}, \
|
||||
{"stmw_operation", {PARALLEL}}, \
|
||||
{"vrsave_operation", {PARALLEL}}, \
|
||||
{"save_world_operation", {PARALLEL}}, \
|
||||
{"restore_world_operation", {PARALLEL}}, \
|
||||
{"mfcr_operation", {PARALLEL}}, \
|
||||
{"mtcrf_operation", {PARALLEL}}, \
|
||||
{"branch_comparison_operator", {EQ, NE, LE, LT, GE, \
|
||||
|
Loading…
Reference in New Issue
Block a user