From 00b960c7ada70affc18fe7f4ad170a40c684c208 Mon Sep 17 00:00:00 2001 From: Aldy Hernandez Date: Wed, 21 Nov 2001 01:04:44 +0000 Subject: [PATCH] rs6000.c (direct_return): Check if we are saving altivec registers. * rs6000.c (direct_return): Check if we are saving altivec registers. (first_altivec_reg_to_save): New. Updated stack frame layout comments. (rs6000_stack_info): Calculate altivec register save size. Save link register if we saved some altivec registers. (rs6000_stack_info): Align save size to 16 if altivec abi or abi_darwin. (rs6000_stack_info): Calculate altivec register offsets. (rs6000_stack_info): Add altivec info to save_size. (debug_stack_info): Add altivec debug info. (rs6000_emit_prologue): Save altivec registers and vrsave. (compute_vrsave_mask): New. (altivec_expand_builtin): Remove unused variables. (rs6000_parse_abi_options): Add static qualifier. (rs6000_expand_builtin): Remove unused parameters. (altivec_expand_builtin): Cast bdesc_2arg to get rid of warning. (altivec_init_builtins): Same. (is_altivec_return_reg): New. (vrsave_operation): New. (ALTIVEC_REG_BIT): New. (generate_set_vrsave): New. * rs6000.md (get_vrsave): New. (set_vrsave): New. (*set_vrsave_internal): New. * rs6000.h (rs6000_stack): Add first_altivec_reg_save, altivec_save_offset, vrsave_save_offset, altive_size, vrsave_size, altivec_padding_size, vrsave_mask. (TOTAL_ALTIVEC_REGS): New. (EPILOGUE_USES): Add VRSAVE_REGNO. From-SVN: r47228 --- gcc/ChangeLog | 35 +++ gcc/config/rs6000/rs6000.c | 430 +++++++++++++++++++++++++++++++++--- gcc/config/rs6000/rs6000.h | 11 + gcc/config/rs6000/rs6000.md | 25 +++ 4 files changed, 471 insertions(+), 30 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 57c1677c6a9..417b2f9d6b2 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,38 @@ +2001-11-15 Aldy Hernandez + + * rs6000.c (direct_return): Check if we are saving altivec + registers. + (first_altivec_reg_to_save): New. + Updated stack frame layout comments. + (rs6000_stack_info): Calculate altivec register save size. + Save link register if we saved some altivec registers. + (rs6000_stack_info): Align save size to 16 if altivec abi or + abi_darwin. + (rs6000_stack_info): Calculate altivec register offsets. + (rs6000_stack_info): Add altivec info to save_size. + (debug_stack_info): Add altivec debug info. + (rs6000_emit_prologue): Save altivec registers and vrsave. + (compute_vrsave_mask): New. + (altivec_expand_builtin): Remove unused variables. + (rs6000_parse_abi_options): Add static qualifier. + (rs6000_expand_builtin): Remove unused parameters. + (altivec_expand_builtin): Cast bdesc_2arg to get rid of warning. + (altivec_init_builtins): Same. + (is_altivec_return_reg): New. + (vrsave_operation): New. + (ALTIVEC_REG_BIT): New. + (generate_set_vrsave): New. + + * rs6000.md (get_vrsave): New. + (set_vrsave): New. + (*set_vrsave_internal): New. + + * rs6000.h (rs6000_stack): Add first_altivec_reg_save, + altivec_save_offset, vrsave_save_offset, altive_size, vrsave_size, + altivec_padding_size, vrsave_mask. + (TOTAL_ALTIVEC_REGS): New. + (EPILOGUE_USES): Add VRSAVE_REGNO. + 2001-11-20 Jeff Law * unroll.c (copy_loop_body): Update LABEL_NUSES for the diff --git a/gcc/config/rs6000/rs6000.c b/gcc/config/rs6000/rs6000.c index b2bb934d578..6ed9a49dfa2 100644 --- a/gcc/config/rs6000/rs6000.c +++ b/gcc/config/rs6000/rs6000.c @@ -153,10 +153,15 @@ static int rs6000_issue_rate PARAMS ((void)); static void rs6000_init_builtins PARAMS ((tree)); static void altivec_init_builtins PARAMS ((void)); static rtx rs6000_expand_builtin PARAMS ((tree, rtx, rtx, enum machine_mode, int)); -static rtx altivec_expand_builtin PARAMS ((tree, rtx, rtx, enum machine_mode, int)); +static rtx altivec_expand_builtin PARAMS ((tree, rtx)); static rtx altivec_expand_binop_builtin PARAMS ((enum insn_code, tree, rtx)); static void rs6000_parse_abi_options PARAMS ((void)); +static int first_altivec_reg_to_save PARAMS ((void)); +static unsigned int compute_vrsave_mask PARAMS ((void)); +static void is_altivec_return_reg PARAMS ((rtx, void *)); +int vrsave_operation PARAMS ((rtx, enum machine_mode)); +static rtx generate_set_vrsave PARAMS ((rtx, rs6000_stack_t *)); /* Default register names. */ char rs6000_reg_names[][8] = @@ -234,6 +239,9 @@ static const char alt_reg_names[][8] = #undef TARGET_EXPAND_BUILTIN #define TARGET_EXPAND_BUILTIN rs6000_expand_builtin +/* The VRSAVE bitmask puts bit %v0 as the most significant bit. */ +#define ALTIVEC_REG_BIT(REGNO) (0x80000000 >> ((REGNO) - FIRST_ALTIVEC_REGNO)) + struct gcc_target targetm = TARGET_INITIALIZER; /* Override command line options. Mostly we process the processor @@ -496,7 +504,8 @@ rs6000_override_options (default_cpu) } /* Handle -mabi= options. */ -void rs6000_parse_abi_options () +static void +rs6000_parse_abi_options () { if (rs6000_abi_string == 0) return; @@ -585,8 +594,10 @@ direct_return () if (info->first_gp_reg_save == 32 && info->first_fp_reg_save == 64 + && info->first_altivec_reg_save == LAST_ALTIVEC_REGNO + 1 && ! info->lr_save_p && ! info->cr_save_p + && info->vrsave_mask == 0 && ! info->push_p) return 1; } @@ -3108,21 +3119,18 @@ altivec_expand_binop_builtin (icode, arglist, target) } static rtx -altivec_expand_builtin (exp, target, subtarget, mode, ignore) +altivec_expand_builtin (exp, target) tree exp; rtx target; - rtx subtarget; - enum machine_mode mode; - int ignore; { struct builtin_description *d; size_t i; enum insn_code icode; tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0); tree arglist = TREE_OPERAND (exp, 1); - tree arg0, arg1, arg2, arg3; - rtx op0, op1, op2, pat; - enum machine_mode tmode, mode0, mode1, mode2; + tree arg0, arg1; + rtx op0, op1, pat; + enum machine_mode tmode, mode0, mode1; unsigned int fcode = DECL_FUNCTION_CODE (fndecl); switch (fcode) @@ -3170,7 +3178,8 @@ altivec_expand_builtin (exp, target, subtarget, mode, ignore) } /* Handle simple binary operations. */ - for (i = 0, d = bdesc_2arg; i < sizeof (bdesc_2arg) / sizeof *d; i++, d++) + d = (struct builtin_description *) bdesc_2arg; + for (i = 0; i < sizeof (bdesc_2arg) / sizeof *d; i++, d++) if (d->code == fcode) return altivec_expand_binop_builtin (d->icode, arglist, target); @@ -3188,12 +3197,12 @@ static rtx rs6000_expand_builtin (exp, target, subtarget, mode, ignore) tree exp; rtx target; - rtx subtarget; - enum machine_mode mode; - int ignore; + rtx subtarget ATTRIBUTE_UNUSED; + enum machine_mode mode ATTRIBUTE_UNUSED; + int ignore ATTRIBUTE_UNUSED; { if (TARGET_ALTIVEC) - return altivec_expand_builtin (exp, target, subtarget, mode, ignore); + return altivec_expand_builtin (exp, target); abort (); } @@ -3322,7 +3331,8 @@ altivec_init_builtins (void) def_builtin (MASK_ALTIVEC, "__builtin_altivec_st_internal", void_ftype_pint_v4si, ALTIVEC_BUILTIN_ST_INTERNAL); /* Add the simple binary operators. */ - for (i = 0, d = bdesc_2arg; i < sizeof (bdesc_2arg) / sizeof *d; i++, d++) + d = (struct builtin_description *) bdesc_2arg; + for (i = 0; i < sizeof (bdesc_2arg) / sizeof *d; i++, d++) { enum machine_mode mode0, mode1, mode2; tree type; @@ -3831,6 +3841,41 @@ store_multiple_operation (op, mode) return 1; } +/* Return 1 for a parallel vrsave operation. */ + +int +vrsave_operation (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + int count = XVECLEN (op, 0); + unsigned int dest_regno, src_regno; + int i; + + if (count <= 1 + || GET_CODE (XVECEXP (op, 0, 0)) != SET + || GET_CODE (SET_DEST (XVECEXP (op, 0, 0))) != REG + || GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != UNSPEC) + return 0; + + dest_regno = REGNO (SET_DEST (XVECEXP (op, 0, 0))); + src_regno = REGNO (SET_SRC (XVECEXP (op, 0, 0))); + + if (dest_regno != VRSAVE_REGNO + && src_regno != VRSAVE_REGNO) + return 0; + + for (i = 1; i < count; i++) + { + rtx elt = XVECEXP (op, 0, i); + + if (GET_CODE (elt) != CLOBBER) + return 0; + } + + return 1; +} + /* Return 1 for an PARALLEL suitable for mtcrf. */ int @@ -5926,6 +5971,85 @@ first_fp_reg_to_save () return first_reg; } + +/* Similar, for AltiVec regs. */ + +static int +first_altivec_reg_to_save () +{ + int i; + + /* Stack frame remains as is unless we are in AltiVec ABI. */ + if (! TARGET_ALTIVEC_ABI) + return LAST_ALTIVEC_REGNO + 1; + + /* Find lowest numbered live register. */ + for (i = FIRST_ALTIVEC_REGNO + 20; i <= LAST_ALTIVEC_REGNO; ++i) + if (regs_ever_live[i]) + break; + + return i; +} + +/* Return a 32-bit mask of the AltiVec registers we need to set in + VRSAVE. Bit n of the return value is 1 if Vn is live. The MSB in + the 32-bit word is 0. */ + +static unsigned int +compute_vrsave_mask () +{ + unsigned int i, mask = 0; + + /* First, find out if we use _any_ altivec registers. */ + for (i = FIRST_ALTIVEC_REGNO; i <= LAST_ALTIVEC_REGNO; ++i) + if (regs_ever_live[i]) + mask |= ALTIVEC_REG_BIT (i); + + if (mask == 0) + return mask; + + /* Next, add all registers that are call-clobbered. We do this + because post-reload register optimizers such as regrename_optimize + may choose to use them. They never change the register class + chosen by reload, so cannot create new uses of altivec registers + if there were none before, so the early exit above is safe. */ + /* ??? Alternately, we could define HARD_REGNO_RENAME_OK to disallow + altivec registers not saved in the mask, which might well make the + adjustments below more effective in eliding the save/restore of + VRSAVE in small functions. */ + for (i = FIRST_ALTIVEC_REGNO; i <= LAST_ALTIVEC_REGNO; ++i) + if (call_used_regs[i]) + mask |= ALTIVEC_REG_BIT (i); + + /* Next, remove the argument registers from the set. These must + be in the VRSAVE mask set by the caller, so we don't need to add + them in again. More importantly, the mask we compute here is + used to generate CLOBBERs in the set_vrsave insn, and we do not + wish the argument registers to die. */ + for (i = cfun->args_info.vregno; i >= ALTIVEC_ARG_MIN_REG; --i) + mask &= ~ALTIVEC_REG_BIT (i); + + /* Similarly, remove the return value from the set. */ + { + bool yes = false; + diddle_return_value (is_altivec_return_reg, &yes); + if (yes) + mask &= ~ALTIVEC_REG_BIT (ALTIVEC_ARG_RETURN); + } + + return mask; +} + +static void +is_altivec_return_reg (reg, xyes) + rtx reg; + void *xyes; +{ + bool *yes = (bool *) xyes; + if (REGNO (reg) == ALTIVEC_ARG_RETURN) + *yes = true; +} + /* Calculate the stack information for the current function. This is complicated by having two separate calling sequences, the AIX calling @@ -5954,9 +6078,15 @@ first_fp_reg_to_save () +---------------------------------------+ | Float/int conversion temporary (X) | 24+P+A+L +---------------------------------------+ - | Save area for GP registers (G) | 24+P+A+X+L + | Save area for AltiVec registers (W) | 24+P+A+L+X + +---------------------------------------+ + | AltiVec alignment padding (Y) | 24+P+A+L+X+W +---------------------------------------+ - | Save area for FP registers (F) | 24+P+A+X+L+G + | Save area for VRSAVE register (Z) | 24+P+A+L+X+W+Y + +---------------------------------------+ + | Save area for GP registers (G) | 24+P+A+X+L+X+W+Y+Z + +---------------------------------------+ + | Save area for FP registers (F) | 24+P+A+X+L+X+W+Y+Z+G +---------------------------------------+ old SP->| back chain to caller's caller | +---------------------------------------+ @@ -5982,11 +6112,17 @@ first_fp_reg_to_save () +---------------------------------------+ | Float/int conversion temporary (X) | 8+P+A+V+L +---------------------------------------+ - | saved CR (C) | 8+P+A+V+L+X + | Save area for AltiVec registers (W) | 8+P+A+V+L+X + +---------------------------------------+ + | AltiVec alignment padding (Y) | 8+P+A+V+L+X+W + +---------------------------------------+ + | Save area for VRSAVE register (Z) | 8+P+A+V+L+X+W+Y + +---------------------------------------+ + | saved CR (C) | 8+P+A+V+L+X+W+Y+Z +---------------------------------------+ - | Save area for GP registers (G) | 8+P+A+V+L+X+C + | Save area for GP registers (G) | 8+P+A+V+L+X+W+Y+Z+C +---------------------------------------+ - | Save area for FP registers (F) | 8+P+A+V+L+X+C+G + | Save area for FP registers (F) | 8+P+A+V+L+X+W+Y+Z+C+G +---------------------------------------+ old SP->| back chain to caller's caller | +---------------------------------------+ @@ -6042,6 +6178,10 @@ rs6000_stack_info () info_ptr->first_fp_reg_save = first_fp_reg_to_save (); info_ptr->fp_size = 8 * (64 - info_ptr->first_fp_reg_save); + info_ptr->first_altivec_reg_save = first_altivec_reg_to_save (); + info_ptr->altivec_size = 16 * (LAST_ALTIVEC_REGNO + 1 + - info_ptr->first_altivec_reg_save); + /* Does this function call anything? */ info_ptr->calls_p = (! current_function_is_leaf || cfun->machine->ra_needs_full_frame); @@ -6054,6 +6194,7 @@ rs6000_stack_info () #endif || (info_ptr->first_fp_reg_save != 64 && !FP_SAVE_INLINE (info_ptr->first_fp_reg_save)) + || info_ptr->first_altivec_reg_save <= LAST_ALTIVEC_REGNO || (abi == ABI_V4 && current_function_calls_alloca) || (abi == ABI_SOLARIS && current_function_calls_alloca) || (DEFAULT_ABI == ABI_DARWIN @@ -6095,14 +6236,17 @@ rs6000_stack_info () info_ptr->vars_size = RS6000_ALIGN (get_frame_size (), 8); info_ptr->parm_size = RS6000_ALIGN (current_function_outgoing_args_size, 8); - info_ptr->save_size = RS6000_ALIGN (info_ptr->fp_size - + info_ptr->gp_size - + ehrd_size - + info_ptr->cr_size - + info_ptr->lr_size - + info_ptr->toc_size, 8); - if (DEFAULT_ABI == ABI_DARWIN) - info_ptr->save_size = RS6000_ALIGN (info_ptr->save_size, 16); + + if (TARGET_ALTIVEC_ABI) + { + info_ptr->vrsave_mask = compute_vrsave_mask (); + info_ptr->vrsave_size = info_ptr->vrsave_mask ? 4 : 0; + } + else + { + info_ptr->vrsave_mask = 0; + info_ptr->vrsave_size = 0; + } /* Calculate the offsets. */ switch (abi) @@ -6116,7 +6260,29 @@ rs6000_stack_info () case ABI_DARWIN: info_ptr->fp_save_offset = - info_ptr->fp_size; info_ptr->gp_save_offset = info_ptr->fp_save_offset - info_ptr->gp_size; - info_ptr->ehrd_offset = info_ptr->gp_save_offset - ehrd_size; + + if (TARGET_ALTIVEC_ABI) + { + info_ptr->vrsave_save_offset + = info_ptr->gp_save_offset - info_ptr->vrsave_size; + + /* Align stack so vector save area is on a quadword boundary. */ + if (info_ptr->altivec_size != 0) + info_ptr->altivec_padding_size + = 16 - (-info_ptr->vrsave_save_offset % 16); + else + info_ptr->altivec_padding_size = 0; + + info_ptr->altivec_save_offset + = info_ptr->vrsave_save_offset + - info_ptr->altivec_padding_size + - info_ptr->altivec_size; + + /* Adjust for AltiVec case. */ + info_ptr->ehrd_offset = info_ptr->altivec_save_offset - ehrd_size; + } + else + info_ptr->ehrd_offset = info_ptr->gp_save_offset - ehrd_size; info_ptr->cr_save_offset = reg_size; /* first word when 64-bit. */ info_ptr->lr_save_offset = 2*reg_size; break; @@ -6126,12 +6292,48 @@ rs6000_stack_info () info_ptr->fp_save_offset = - info_ptr->fp_size; info_ptr->gp_save_offset = info_ptr->fp_save_offset - info_ptr->gp_size; info_ptr->cr_save_offset = info_ptr->gp_save_offset - info_ptr->cr_size; - info_ptr->toc_save_offset = info_ptr->cr_save_offset - info_ptr->toc_size; + + if (TARGET_ALTIVEC_ABI) + { + info_ptr->vrsave_save_offset + = info_ptr->cr_save_offset - info_ptr->vrsave_size; + + /* Align stack so vector save area is on a quadword boundary. */ + if (info_ptr->altivec_size != 0) + info_ptr->altivec_padding_size + = 16 - (-info_ptr->vrsave_save_offset % 16); + else + info_ptr->altivec_padding_size = 0; + + info_ptr->altivec_save_offset + = info_ptr->vrsave_save_offset + - info_ptr->altivec_padding_size + - info_ptr->altivec_size; + + /* Adjust for AltiVec case. */ + info_ptr->toc_save_offset + = info_ptr->altivec_save_offset - info_ptr->toc_size; + } + else + info_ptr->toc_save_offset = info_ptr->cr_save_offset - info_ptr->toc_size; info_ptr->ehrd_offset = info_ptr->toc_save_offset - ehrd_size; info_ptr->lr_save_offset = reg_size; break; } + info_ptr->save_size = RS6000_ALIGN (info_ptr->fp_size + + info_ptr->gp_size + + info_ptr->altivec_size + + info_ptr->altivec_padding_size + + info_ptr->vrsave_size + + ehrd_size + + info_ptr->cr_size + + info_ptr->lr_size + + info_ptr->vrsave_size + + info_ptr->toc_size, + (TARGET_ALTIVEC_ABI || ABI_DARWIN) + ? 16 : 8); + total_raw_size = (info_ptr->vars_size + info_ptr->parm_size + info_ptr->save_size @@ -6173,6 +6375,12 @@ rs6000_stack_info () if (info_ptr->gp_size == 0) info_ptr->gp_save_offset = 0; + if (! TARGET_ALTIVEC_ABI || info_ptr->altivec_size == 0) + info_ptr->altivec_save_offset = 0; + + if (! TARGET_ALTIVEC_ABI || info_ptr->vrsave_mask == 0) + info_ptr->vrsave_save_offset = 0; + if (! info_ptr->lr_save_p) info_ptr->lr_save_offset = 0; @@ -6212,12 +6420,19 @@ debug_stack_info (info) fprintf (stderr, "\tABI = %5s\n", abi_string); + if (TARGET_ALTIVEC_ABI) + fprintf (stderr, "\tALTIVEC ABI extensions enabled.\n"); + if (info->first_gp_reg_save != 32) fprintf (stderr, "\tfirst_gp_reg_save = %5d\n", info->first_gp_reg_save); if (info->first_fp_reg_save != 64) fprintf (stderr, "\tfirst_fp_reg_save = %5d\n", info->first_fp_reg_save); + if (info->first_altivec_reg_save <= LAST_ALTIVEC_REGNO) + fprintf (stderr, "\tfirst_altivec_reg_save = %5d\n", + info->first_altivec_reg_save); + if (info->lr_save_p) fprintf (stderr, "\tlr_save_p = %5d\n", info->lr_save_p); @@ -6227,6 +6442,9 @@ debug_stack_info (info) if (info->toc_save_p) fprintf (stderr, "\ttoc_save_p = %5d\n", info->toc_save_p); + if (info->vrsave_mask) + fprintf (stderr, "\tvrsave_mask = 0x%x\n", info->vrsave_mask); + if (info->push_p) fprintf (stderr, "\tpush_p = %5d\n", info->push_p); @@ -6239,6 +6457,14 @@ debug_stack_info (info) if (info->fp_save_offset) fprintf (stderr, "\tfp_save_offset = %5d\n", info->fp_save_offset); + if (info->altivec_save_offset) + fprintf (stderr, "\taltivec_save_offset = %5d\n", + info->altivec_save_offset); + + if (info->vrsave_save_offset) + fprintf (stderr, "\tvrsave_save_offset = %5d\n", + info->vrsave_save_offset); + if (info->lr_save_offset) fprintf (stderr, "\tlr_save_offset = %5d\n", info->lr_save_offset); @@ -6272,6 +6498,16 @@ debug_stack_info (info) if (info->fp_size) fprintf (stderr, "\tfp_size = %5d\n", info->fp_size); + if (info->altivec_size) + fprintf (stderr, "\taltivec_size = %5d\n", info->altivec_size); + + if (info->vrsave_size) + fprintf (stderr, "\tvrsave_size = %5d\n", info->vrsave_size); + + if (info->altivec_padding_size) + fprintf (stderr, "\taltivec_padding_size= %5d\n", + info->altivec_padding_size); + if (info->lr_size) fprintf (stderr, "\tlr_size = %5d\n", info->lr_size); @@ -6806,6 +7042,36 @@ rs6000_frame_related (insn, reg, val, reg2, rreg) REG_NOTES (insn)); } +/* Returns an insn that has a vrsave set operation with the + appropriate CLOBBERs. */ + +static rtx +generate_set_vrsave (reg, info) + rtx reg; + rs6000_stack_t *info; +{ + int nclobs, i; + rtx insn, clobs[TOTAL_ALTIVEC_REGS + 1]; + + clobs[0] = gen_set_vrsave (reg); + + nclobs = 1; + + /* CLOBBER the registers in the mask. */ + + for (i = FIRST_ALTIVEC_REGNO; i <= LAST_ALTIVEC_REGNO; ++i) + if (info->vrsave_mask != 0 && ALTIVEC_REG_BIT (i) != 0) + clobs[nclobs++] = gen_rtx_CLOBBER (VOIDmode, + gen_rtx_REG (V4SImode, i)); + + insn = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (nclobs)); + + for (i = 0; i < nclobs; ++i) + XVECEXP (insn, 0, i) = clobs[i]; + + return insn; +} + /* Emit function prologue as insns. */ void @@ -7042,6 +7308,70 @@ rs6000_emit_prologue () if (info->push_p && DEFAULT_ABI != ABI_V4 && DEFAULT_ABI != ABI_SOLARIS) rs6000_emit_allocate_stack (info->total_size, FALSE); + /* Save AltiVec registers if needed. */ + if (TARGET_ALTIVEC_ABI && info->altivec_size != 0) + { + int i; + + /* There should be a non inline version of this, for when we + are saving lots of vector registers. */ + for (i = info->first_altivec_reg_save; i <= LAST_ALTIVEC_REGNO; ++i) + if (regs_ever_live[i] && ! call_used_regs[i]) + { + rtx addr, areg, savereg, mem; + + savereg = gen_rtx_REG (V4SImode, i); + + areg = gen_rtx_REG (Pmode, 0); + emit_move_insn + (areg, GEN_INT (info->altivec_save_offset + + sp_offset + + 16 * (i - info->first_altivec_reg_save))); + + /* AltiVec addressing mode is [reg+reg]. */ + addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, areg); + mem = gen_rtx_MEM (V4SImode, addr); + set_mem_alias_set (mem, rs6000_sr_alias_set); + + insn = emit_move_insn (mem, savereg); + rs6000_frame_related (insn, frame_ptr_rtx, info->total_size, + NULL_RTX, NULL_RTX); + } + } + + /* VRSAVE is a bit vector representing which AltiVec registers + are used. The OS uses this to determine which vector + registers to save on a context switch. We need to save + VRSAVE on the stack frame, add whatever AltiVec registers we + used in this function, and do the corresponding magic in the + epilogue. */ + + if (TARGET_ALTIVEC && info->vrsave_mask != 0) + { + rtx reg, addr, mem; + + /* Get VRSAVE onto a GPR. */ + reg = gen_rtx_REG (SImode, 12); + emit_insn (gen_get_vrsave (reg)); + + /* Save VRSAVE. */ + addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, + GEN_INT (info->vrsave_save_offset + sp_offset)); + mem = gen_rtx_MEM (SImode, addr); + set_mem_alias_set (mem, rs6000_sr_alias_set); + insn = emit_move_insn (mem, reg); + rs6000_frame_related (insn, frame_ptr_rtx, info->total_size, + NULL_RTX, NULL_RTX); + + /* Include the registers in the mask. */ + emit_insn (gen_iorsi3 (reg, reg, GEN_INT ((int) info->vrsave_mask))); + + insn = emit_insn (generate_set_vrsave (reg, info)); + + rs6000_frame_related (insn, frame_ptr_rtx, info->total_size, + NULL_RTX, NULL_RTX); + } + /* Set frame pointer, if needed. */ if (frame_pointer_needed) { @@ -7321,6 +7651,46 @@ rs6000_emit_epilogue (sibcall) mem); } + /* Restore AltiVec registers if needed. */ + if (TARGET_ALTIVEC_ABI && info->altivec_size != 0) + { + int i; + + for (i = info->first_altivec_reg_save; i <= LAST_ALTIVEC_REGNO; ++i) + if (regs_ever_live[i] && ! call_used_regs[i]) + { + rtx addr, areg, mem; + + areg = gen_rtx_REG (Pmode, 0); + emit_move_insn + (areg, GEN_INT (info->altivec_save_offset + + sp_offset + + 16 * (i - info->first_altivec_reg_save))); + + /* AltiVec addressing mode is [reg+reg]. */ + addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, areg); + mem = gen_rtx_MEM (V4SImode, addr); + set_mem_alias_set (mem, rs6000_sr_alias_set); + + emit_move_insn (gen_rtx_REG (V4SImode, i), mem); + } + } + + /* Restore VRSAVE if needed. */ + if (TARGET_ALTIVEC_ABI && info->vrsave_mask != 0) + { + rtx addr, mem, reg; + + addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, + GEN_INT (info->vrsave_save_offset + sp_offset)); + mem = gen_rtx_MEM (SImode, addr); + set_mem_alias_set (mem, rs6000_sr_alias_set); + reg = gen_rtx_REG (SImode, 12); + emit_move_insn (reg, mem); + + emit_insn (generate_set_vrsave (reg, info)); + } + /* If we saved cr, restore it here. Just those that were used. */ if (info->cr_save_p) { diff --git a/gcc/config/rs6000/rs6000.h b/gcc/config/rs6000/rs6000.h index b2401d9b757..71e41d2e307 100644 --- a/gcc/config/rs6000/rs6000.h +++ b/gcc/config/rs6000/rs6000.h @@ -736,6 +736,7 @@ extern int rs6000_debug_arg; /* debug argument handling */ #define XER_REGNO 76 #define FIRST_ALTIVEC_REGNO 77 #define LAST_ALTIVEC_REGNO 108 +#define TOTAL_ALTIVEC_REGS (LAST_ALTIVEC_REGNO - FIRST_ALTIVEC_REGNO) #define VRSAVE_REGNO 109 /* List the order in which to allocate registers. Each register must be @@ -1265,16 +1266,20 @@ extern enum rs6000_abi rs6000_current_abi; /* available for use by subtarget */ typedef struct rs6000_stack { int first_gp_reg_save; /* first callee saved GP register used */ int first_fp_reg_save; /* first callee saved FP register used */ + int first_altivec_reg_save; /* first callee saved AltiVec register used */ int lr_save_p; /* true if the link reg needs to be saved */ int cr_save_p; /* true if the CR reg needs to be saved */ + unsigned int vrsave_mask; /* mask of vec registers to save */ 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 */ 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 */ + int altivec_save_offset; /* offset to save AltiVec regs from inital SP */ int lr_save_offset; /* offset to save LR from initial SP */ int cr_save_offset; /* offset to save CR from initial SP */ + int vrsave_save_offset; /* offset to save VRSAVE from initial SP */ int toc_save_offset; /* offset to save the TOC pointer */ int varargs_save_offset; /* offset to save the varargs registers */ int ehrd_offset; /* offset to EH return data */ @@ -1286,8 +1291,12 @@ typedef struct rs6000_stack { int fixed_size; /* fixed size of stack frame */ int gp_size; /* size of saved GP registers */ int fp_size; /* size of saved FP registers */ + int altivec_size; /* size of saved AltiVec registers */ int cr_size; /* size to hold CR if not in save_size */ int lr_size; /* size to hold LR if not in save_size */ + int vrsave_size; /* size to hold VRSAVE if not in save_size */ + int altivec_padding_size; /* size of altivec alignment padding if + not in save_size */ int toc_size; /* size to hold TOC if not in save_size */ int total_size; /* total bytes allocated for stack */ } rs6000_stack_t; @@ -1685,6 +1694,7 @@ typedef struct rs6000_args #define EPILOGUE_USES(REGNO) \ ((reload_completed && (REGNO) == LINK_REGISTER_REGNUM) \ + || (REGNO) == VRSAVE_REGNO \ || (current_function_calls_eh_return \ && TARGET_AIX \ && (REGNO) == TOC_REGISTER)) @@ -2792,6 +2802,7 @@ do { \ CONST_DOUBLE, SYMBOL_REF}}, \ {"load_multiple_operation", {PARALLEL}}, \ {"store_multiple_operation", {PARALLEL}}, \ + {"vrsave_operation", {PARALLEL}}, \ {"branch_comparison_operator", {EQ, NE, LE, LT, GE, \ GT, LEU, LTU, GEU, GTU, \ UNORDERED, ORDERED, \ diff --git a/gcc/config/rs6000/rs6000.md b/gcc/config/rs6000/rs6000.md index 0292c57ec45..174d3e6c170 100644 --- a/gcc/config/rs6000/rs6000.md +++ b/gcc/config/rs6000/rs6000.md @@ -13622,6 +13622,31 @@ vor %0,%1,%1" [(set_attr "type" "altivec")]) +;; Copy VRSAVE into a GPR. +(define_insn "get_vrsave" + [(set (match_operand:SI 0 "register_operand" "=r") + (unspec:SI [(reg:SI 109)] 28))] + "TARGET_ALTIVEC" + "mfvrsave %0" + [(set_attr "type" "altivec")]) + +(define_insn "*set_vrsave_internal" + [(match_parallel 0 "vrsave_operation" + [(set (reg:SI 109) + (unspec:SI [(match_operand:SI 1 "register_operand" "r") + (reg:SI 109)] 30))])] + "TARGET_ALTIVEC" + "mtvrsave %1" + [(set_attr "type" "altivec")]) + +(define_insn "set_vrsave" + [(set (reg:SI 109) + (unspec:SI [(match_operand:SI 0 "register_operand" "r") + (reg:SI 109)] 30))] + "TARGET_ALTIVEC" + "mtvrsave %0" + [(set_attr "type" "altivec")]) + ;; Simple binary operations. (define_insn "altivec_vaddubm" -- 2.30.2