2008-04-14 Alan Modra PR target/35907 * config/rs6000/rs6000.c (rs6000_emit_epilogue): Restore Altivec registers using saved backchain as base instead of sp. Restore Altivec registers and VRSAVE before increasing sp if they are saved below red zone. * gcc.target/powerpc/pr35907.c: New test. --- gcc/config/rs6000/rs6000.c.jj 2008-04-16 09:43:10.000000000 +0200 +++ gcc/config/rs6000/rs6000.c 2008-04-16 10:05:34.000000000 +0200 @@ -16380,11 +16380,23 @@ rs6000_emit_epilogue (int sibcall) if (info->push_p) sp_offset = info->total_size; - /* Restore AltiVec registers if needed. */ - if (TARGET_ALTIVEC_ABI && info->altivec_size != 0) + /* Restore AltiVec registers if we must do so before adjusting the + stack. */ + if (TARGET_ALTIVEC_ABI + && info->altivec_size != 0 + && DEFAULT_ABI != ABI_V4 + && info->altivec_save_offset < (TARGET_32BIT ? -220 : -288)) { int i; + if (use_backchain_to_restore_sp) + { + frame_reg_rtx = gen_rtx_REG (Pmode, 11); + emit_move_insn (frame_reg_rtx, + gen_rtx_MEM (Pmode, sp_reg_rtx)); + sp_offset = 0; + } + for (i = info->first_altivec_reg_save; i <= LAST_ALTIVEC_REGNO; ++i) if (info->vrsave_mask & ALTIVEC_REG_BIT (i)) { @@ -16404,19 +16416,54 @@ rs6000_emit_epilogue (int sibcall) } } + /* Restore VRSAVE if we must do so before adjusting the stack. */ + if (TARGET_ALTIVEC + && TARGET_ALTIVEC_VRSAVE + && info->vrsave_mask != 0 + && DEFAULT_ABI != ABI_V4 + && info->vrsave_save_offset < (TARGET_32BIT ? -220 : -288)) + { + rtx addr, mem, reg; + + if (use_backchain_to_restore_sp + && frame_reg_rtx == sp_reg_rtx) + { + frame_reg_rtx = gen_rtx_REG (Pmode, 11); + emit_move_insn (frame_reg_rtx, + gen_rtx_MEM (Pmode, sp_reg_rtx)); + sp_offset = 0; + } + + addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, + GEN_INT (info->vrsave_save_offset + sp_offset)); + mem = gen_frame_mem (SImode, addr); + reg = gen_rtx_REG (SImode, 12); + emit_move_insn (reg, mem); + + emit_insn (generate_set_vrsave (reg, info, 1)); + } + /* 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. */ if (use_backchain_to_restore_sp) { - /* Under V.4, don't reset the stack pointer until after we're done - loading the saved registers. */ - if (DEFAULT_ABI == ABI_V4) - frame_reg_rtx = gen_rtx_REG (Pmode, 11); + if (frame_reg_rtx != sp_reg_rtx) + { + emit_move_insn (sp_reg_rtx, frame_reg_rtx); + frame_reg_rtx = sp_reg_rtx; + } + else + { + /* Under V.4, don't reset the stack pointer until after we're done + loading the saved registers. */ + if (DEFAULT_ABI == ABI_V4) + frame_reg_rtx = gen_rtx_REG (Pmode, 11); - emit_move_insn (frame_reg_rtx, - gen_rtx_MEM (Pmode, sp_reg_rtx)); - sp_offset = 0; + emit_move_insn (frame_reg_rtx, + gen_rtx_MEM (Pmode, sp_reg_rtx)); + sp_offset = 0; + } } else if (info->push_p && DEFAULT_ABI != ABI_V4 @@ -16430,9 +16477,39 @@ rs6000_emit_epilogue (int sibcall) sp_offset = 0; } - /* Restore VRSAVE if needed. */ - if (TARGET_ALTIVEC && TARGET_ALTIVEC_VRSAVE - && info->vrsave_mask != 0) + /* Restore AltiVec registers if we have not done so already. */ + if (TARGET_ALTIVEC_ABI + && info->altivec_size != 0 + && (DEFAULT_ABI == ABI_V4 + || info->altivec_save_offset >= (TARGET_32BIT ? -220 : -288))) + { + int i; + + for (i = info->first_altivec_reg_save; i <= LAST_ALTIVEC_REGNO; ++i) + if (info->vrsave_mask & ALTIVEC_REG_BIT (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_frame_mem (V4SImode, addr); + + emit_move_insn (gen_rtx_REG (V4SImode, i), mem); + } + } + + /* Restore VRSAVE if we have not done so already. */ + if (TARGET_ALTIVEC + && TARGET_ALTIVEC_VRSAVE + && info->vrsave_mask != 0 + && (DEFAULT_ABI == ABI_V4 + || info->vrsave_save_offset >= (TARGET_32BIT ? -220 : -288))) { rtx addr, mem, reg; --- gcc/testsuite/gcc.target/powerpc/pr35907.c.jj 2008-04-16 10:04:23.000000000 +0200 +++ gcc/testsuite/gcc.target/powerpc/pr35907.c 2008-04-16 10:04:58.000000000 +0200 @@ -0,0 +1,60 @@ +/* PR target/35907 */ +/* { dg-do run { target powerpc*-*-* } } */ +/* { dg-require-effective-target powerpc_altivec_ok } */ +/* { dg-options "-O2 -maltivec" } */ + +#include "altivec_check.h" + +#define vector __attribute__((vector_size (16))) +union +{ + vector int k; + int c[16]; +} u, v, w; +vector int m; + +void __attribute__((noinline)) +bar (void *i, vector int j) +{ + asm volatile ("" : : "r" (i), "r" (&j) : "memory"); +} + +int __attribute__((noinline)) +foo (int i, vector int j) +{ + char *p = __builtin_alloca (64 + i); + m += u.k; + v.k = m; + w.k = j; + if (__builtin_memcmp (&v.c, &w.c, 16) != 0) + __builtin_abort (); + j += u.k; + bar (p, j); + j += u.k; + bar (p, j); + return 0; +} + +void +main1 (void) +{ + vector int l; + int i; + for (i = 0; i < 4; i++) + u.c[i] = i; + l = u.k; + if (foo (64, l)) + __builtin_abort (); + l += u.k; + if (foo (64, l)) + __builtin_abort (); +} + +int +main () +{ + altivec_check (); + main1 (); + exit (0); +} +