Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion gcc/config/m68k/m68k-protos.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ extern enum reg_class m68k_secondary_reload_class (enum reg_class,
extern enum reg_class m68k_preferred_reload_class (rtx, enum reg_class);
extern void m68k_expand_prologue (void);
extern bool m68k_use_return_insn (void);
extern void m68k_expand_epilogue (bool);
extern void m68k_expand_epilogue (enum m68k_epilogue_kind);
extern const char *m68k_cpp_cpu_ident (const char *);
extern const char *m68k_cpp_cpu_family (const char *);
extern void init_68881_table (void);
Expand Down
102 changes: 77 additions & 25 deletions gcc/config/m68k/m68k.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1201,22 +1201,54 @@ m68k_use_return_insn (void)
return current_frame.offset == 0;
}

/* Emit RTL for the "epilogue" or "sibcall_epilogue" define_expand;
SIBCALL_P says which.
/* Return the subset of the integer registers saved by the prologue that an
epilogue of kind KIND has to restore. The EH_RETURN_DATA_REGNO save slots
are only filled in by the unwinder, so reloading them anywhere but on the
exception return path would clobber the function's return value. */

static unsigned int
m68k_epilogue_reg_mask (enum m68k_epilogue_kind kind)
{
unsigned int mask;
unsigned int i;

mask = current_frame.reg_mask;
if (crtl->calls_eh_return && kind != M68K_EPILOGUE_EH_RETURN)
for (i = 0; EH_RETURN_DATA_REGNO (i) != INVALID_REGNUM; i++)
mask &= ~(1u << (EH_RETURN_DATA_REGNO (i) - D0_REG));

return mask;
}

/* Emit RTL for the "epilogue", "sibcall_epilogue" or "eh_return_internal"
define_expand; KIND says which.

The function epilogue should not depend on the current stack pointer!
It should use the frame pointer only, if there is a frame pointer.
This is mandatory because of alloca; we also take advantage of it to
omit stack adjustments before returning. */

void
m68k_expand_epilogue (bool sibcall_p)
m68k_expand_epilogue (enum m68k_epilogue_kind kind)
{
HOST_WIDE_INT fsize, fsize_with_regs;
bool big, restore_from_sp;
unsigned int reg_mask, skipped_mask;
int reg_no;
HOST_WIDE_INT skipped_size;
bool post_increment_p;

m68k_compute_frame_layout ();

reg_mask = m68k_epilogue_reg_mask (kind);
reg_no = popcount_hwi (reg_mask);

/* The skipped registers are the lowest numbered saved ones, so their slots
form a block at the bottom of the save area. */
skipped_mask = current_frame.reg_mask & ~reg_mask;
gcc_assert ((skipped_mask & (skipped_mask + 1)) == 0);
skipped_size = (current_frame.reg_no - reg_no) * GET_MODE_SIZE (SImode);

fsize = current_frame.size;
big = false;
restore_from_sp = false;
Expand All @@ -1243,7 +1275,7 @@ m68k_expand_epilogue (bool sibcall_p)

if (current_frame.offset + fsize >= 0x8000
&& !restore_from_sp
&& (current_frame.reg_mask || current_frame.fpu_mask))
&& (reg_mask || current_frame.fpu_mask))
{
if (TARGET_COLDFIRE
&& (current_frame.reg_no >= MIN_MOVEM_REGS
Expand All @@ -1268,53 +1300,73 @@ m68k_expand_epilogue (bool sibcall_p)
}
}

if (current_frame.reg_no < MIN_MOVEM_REGS)
/* ColdFire's movem cannot post-increment, so the prologue folded the save
area into the frame allocation and the epilogue addresses it directly. */
post_increment_p = (restore_from_sp
&& !(TARGET_COLDFIRE
&& current_frame.reg_no >= MIN_MOVEM_REGS));

/* The skipped slots sit at the bottom of the save area, so step the stack
pointer past them before the post-increment reloads. */
if (post_increment_p && skipped_size != 0)
{
if (reg_mask == 0 && current_frame.fpu_no == 0)
fsize_with_regs += skipped_size;
else
emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
GEN_INT (skipped_size)));
}

if (reg_no < MIN_MOVEM_REGS)
{
/* Restore each register separately in the same order moveml does. */
int i;
HOST_WIDE_INT offset;

offset = current_frame.offset + fsize;
/* OFFSET is the position of the register within the save area. */
offset = skipped_size;
for (i = 0; i < 16; i++)
if (current_frame.reg_mask & (1 << i))
{
if (reg_mask & (1 << i))
{
rtx addr;

if (big)
{
/* Generate the address -OFFSET(%fp,%a1.l). */
addr = gen_rtx_REG (Pmode, A1_REG);
addr = gen_rtx_PLUS (Pmode, addr, frame_pointer_rtx);
addr = plus_constant (Pmode, addr, -offset);
addr = plus_constant (Pmode, addr,
offset - (current_frame.offset + fsize));
}
else if (restore_from_sp)
else if (post_increment_p)
addr = gen_rtx_POST_INC (Pmode, stack_pointer_rtx);
else if (restore_from_sp)
addr = plus_constant (Pmode, stack_pointer_rtx, offset);
else
addr = plus_constant (Pmode, frame_pointer_rtx, -offset);
addr = plus_constant (Pmode, frame_pointer_rtx,
offset - (current_frame.offset + fsize));
emit_move_insn (gen_rtx_REG (SImode, D0_REG + i),
gen_frame_mem (SImode, addr));
offset -= GET_MODE_SIZE (SImode);
offset += GET_MODE_SIZE (SImode);
}
}
else if (current_frame.reg_mask)
else if (reg_mask)
{
if (big)
m68k_emit_movem (gen_rtx_PLUS (Pmode,
gen_rtx_REG (Pmode, A1_REG),
frame_pointer_rtx),
-(current_frame.offset + fsize),
current_frame.reg_no, D0_REG,
current_frame.reg_mask, false, false);
skipped_size - (current_frame.offset + fsize),
reg_no, D0_REG, reg_mask, false, false);
else if (restore_from_sp)
m68k_emit_movem (stack_pointer_rtx, 0,
current_frame.reg_no, D0_REG,
current_frame.reg_mask, false,
!TARGET_COLDFIRE);
m68k_emit_movem (stack_pointer_rtx,
post_increment_p ? 0 : skipped_size,
reg_no, D0_REG, reg_mask, false,
post_increment_p);
else
m68k_emit_movem (frame_pointer_rtx,
-(current_frame.offset + fsize),
current_frame.reg_no, D0_REG,
current_frame.reg_mask, false, false);
skipped_size - (current_frame.offset + fsize),
reg_no, D0_REG, reg_mask, false, false);
}

if (current_frame.fpu_no > 0)
Expand Down Expand Up @@ -1364,12 +1416,12 @@ m68k_expand_epilogue (bool sibcall_p)
stack_pointer_rtx,
GEN_INT (fsize_with_regs)));

if (crtl->calls_eh_return)
if (kind == M68K_EPILOGUE_EH_RETURN)
emit_insn (gen_addsi3 (stack_pointer_rtx,
stack_pointer_rtx,
EH_RETURN_STACKADJ_RTX));

if (!sibcall_p)
if (kind != M68K_EPILOGUE_SIBCALL)
emit_jump_insn (ret_rtx);
}

Expand Down
10 changes: 10 additions & 0 deletions gcc/config/m68k/m68k.h
Original file line number Diff line number Diff line change
Expand Up @@ -738,6 +738,16 @@ __transfer_from_trampoline () \

#define EPILOGUE_USES(REGNO) m68k_epilogue_uses (REGNO)

/* The kind of return an epilogue is expanded for. Only the exception return
path restores the EH_RETURN_DATA_REGNO registers and applies the unwinder's
stack adjustment. */
enum m68k_epilogue_kind
{
M68K_EPILOGUE_NORMAL,
M68K_EPILOGUE_SIBCALL,
M68K_EPILOGUE_EH_RETURN
};

/* Describe how we implement __builtin_eh_return. */
#define EH_RETURN_DATA_REGNO(N) \
((N) < 2 ? (N) : INVALID_REGNUM)
Expand Down
28 changes: 26 additions & 2 deletions gcc/config/m68k/m68k.md
Original file line number Diff line number Diff line change
Expand Up @@ -6114,15 +6114,39 @@
[(return)]
""
{
m68k_expand_epilogue (false);
m68k_expand_epilogue (M68K_EPILOGUE_NORMAL);
DONE;
})

(define_expand "sibcall_epilogue"
[(return)]
""
{
m68k_expand_epilogue (true);
m68k_expand_epilogue (M68K_EPILOGUE_SIBCALL);
DONE;
})

;; The handler address overwrites the return address in the frame, so the
;; transfer of control is still an ordinary "rts"; only the epilogue differs.

(define_expand "eh_return"
[(use (match_operand:SI 0 "general_operand"))]
""
{
emit_move_insn (EH_RETURN_HANDLER_RTX, operands[0]);
emit_jump_insn (gen_eh_return_internal ());
emit_barrier ();
DONE;
})

(define_insn_and_split "eh_return_internal"
[(eh_return)]
""
"#"
"epilogue_completed"
[(const_int 0)]
{
m68k_expand_epilogue (M68K_EPILOGUE_EH_RETURN);
DONE;
})

Expand Down
100 changes: 100 additions & 0 deletions gcc/testsuite/gcc.target/m68k/pr126736.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/* { dg-do run } */
/* { dg-require-effective-target builtin_eh_return } */
/* { dg-options "-O2" } */

/* A function that calls __builtin_eh_return saves %d0 and %d1, the
EH_RETURN_DATA_REGNO registers. Only the exception return path may
restore them: on a normal return they hold the return value. */

extern void use (void *);

__attribute__((noipa))
int
leaf_frame (int should_unwind, long offset)
{
if (should_unwind)
__builtin_eh_return (offset, 0);
return 42;
}

/* Saves three registers, so the prologue uses moveml but the epilogue has
too few left to restore with it. */

__attribute__((noipa))
int
partial_moveml (int should_unwind, long offset)
{
register int a __asm__ ("%d2") = 7;

__asm__ ("" : "+r" (a));
if (should_unwind)
__builtin_eh_return (offset, 0);
return a + 35;
}

__attribute__((noipa))
int
full_moveml (int should_unwind, long offset)
{
register int a __asm__ ("%d2") = 1;
register int b __asm__ ("%d3") = 2;
register int c __asm__ ("%d4") = 3;
register int d __asm__ ("%a2") = 4;

__asm__ ("" : "+r" (a), "+r" (b), "+r" (c), "+r" (d));
if (should_unwind)
__builtin_eh_return (offset, 0);
return a + b + c + d + 32;
}

__attribute__((noipa))
int
alloca_frame (int should_unwind, long offset, int size)
{
char *buffer = __builtin_alloca (size);

use (buffer);
if (should_unwind)
__builtin_eh_return (offset, 0);
return 42;
}

/* A frame too big for a 16-bit offset, so the restores go through %a1. */

__attribute__((noipa))
int
big_frame (int should_unwind, long offset)
{
register int a __asm__ ("%d2") = 7;
volatile char buffer[0x9000];

buffer[0] = 1;
use ((void *) buffer);
__asm__ ("" : "+r" (a));
if (should_unwind)
__builtin_eh_return (offset, 0);
return a + buffer[0] + 34;
}

__attribute__((noinline, noclone))
void
use (void *p)
{
__asm__ ("" :: "r" (p) : "memory");
}

int
main (void)
{
if (leaf_frame (0, 0) != 42)
__builtin_abort ();
if (partial_moveml (0, 0) != 42)
__builtin_abort ();
if (full_moveml (0, 0) != 42)
__builtin_abort ();
if (alloca_frame (0, 0, 64) != 42)
__builtin_abort ();
if (big_frame (0, 0) != 42)
__builtin_abort ();
return 0;
}
Loading