https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83370
Bug ID: 83370
Summary: [AARCH64]Tailcall register may be corrupted by
epilogue code
Product: gcc
Version: 8.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: target
Assignee: unassigned at gcc dot gnu.org
Reporter: renlin at gcc dot gnu.org
Target Milestone: ---
The following example generates incorrect code:
void (*f)();
int xx;
void tailcall (int i)
{
int arr[5000];
xx = arr[i];
f();
}
When built with -O2 -ffixed-x0 -ffixed-x1 -ffixed-x2 -ffixed-x3 -ffixed-x4
-ffixed-x5 -ffixed-x6 -ffixed-x7 -ffixed-x8 -ffixed-x9 -ffixed-x10 -ffixed-x11
-ffixed-x12 -ffixed-x13 -ffixed-x14 -ffixed-x15 -ffixed-x17 -ffixed-x18
tailcall:
mov x16, 20016
sub sp, sp, x16
adrp x16, .LANCHOR0
stp x19, x30, [sp]
add x19, sp, 16
ldr s0, [x19, w0, sxtw 2]
ldp x19, x30, [sp]
str s0, [x16, #:lo12:.LANCHOR0]
mov x16, 20016
add sp, sp, x16
br x16 // oops
So the issue is there is nothing in the tail call instruction that prevents it
from using IP0/IP1 which are used as temporaries in the epilogue. We use the
temporary for frames of 4-64KB, so this issue is more likely today (previously
temporary was used only in frames larger than 16MBytes).
The problem appears to be that while we have explicit clobbers in a tailcall,
they are after the call, not before it:
(call_insn/j 16 12 17 2 (parallel [
(call (mem:DI (reg/f:DI 84 [ f ]) [0 *f.0_2 S8 A8])
(const_int 0 [0]))
(return)
]) "tailcall.c":13 42 {*sibcall_insn}
(expr_list:REG_DEAD (reg/f:DI 84 [ f ])
(expr_list:REG_CALL_DECL (nil)
(nil)))
(expr_list (clobber (reg:DI 17 x17))
(expr_list (clobber (reg:DI 16 x16))
(nil))))
This issues affects gcc-5, gcc-6, gcc-7 and current trunk.