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.