Hi all, In aarch64 backend, ip0/ip1 register will be used in the prologue/epilogue as temporary register.
When the compiler is performing sibcall optimization. It has the chance to use ip0/ip1 register for indirect function call to hold the address. However, those two register might be clobbered by the epilogue code which makes the last sibcall instruction invalid. The following is an extreme example: 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 void (*f)(); int xx; void tailcall (int i) { int arr[5000]; xx = arr[i]; f(); } 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 As we can see, x16 is used in the indirect sibcall instruction. It is used as a temporary in the epilogue code as well. The register allocation is invalid. With the change, the register allocator is only allowed to use r0-r15, r18 for indirect sibcall instruction. For this particular case above, the compiler will ICE as there is not register could be used for this sibcall instruction. And I think it is better to fail instead of wrong code-generation. test.c:10:1: error: unable to generate reloads for: } ^ (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) ]) "test.c":9 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)))) aarch64-none-elf test without regressions. Okay to commit? The same issue affects gcc-6, gcc-7 as well. Backport are needed for those branches. Regards, Renlin gcc/ChangeLog: 2017-12-11 Renlin Li <renlin...@arm.com> PR target/83370 * config/aarch64/aarch64.c (aarch64_class_max_nregs): Handle TAILCALL_ADDR_REGS. (aarch64_register_move_cost): Likewise. * config/aarch64/aarch64.h (reg_class): Rename CALLER_SAVE_REGS to TAILCALL_ADDR_REGS. * config/aarch64/constraints.md (Ucs): Update register constraint.
diff --git a/gcc/config/aarch64/aarch64.h b/gcc/config/aarch64/aarch64.h index 93d29b84d47b7017661a2129d61e7d740bbf7c93..322b7f4628aa69cf331c12ff2c8df351890da9ef 100644 --- a/gcc/config/aarch64/aarch64.h +++ b/gcc/config/aarch64/aarch64.h @@ -446,7 +446,7 @@ extern unsigned aarch64_architecture_version; enum reg_class { NO_REGS, - CALLER_SAVE_REGS, + TAILCALL_ADDR_REGS, GENERAL_REGS, STACK_REG, POINTER_REGS, @@ -462,7 +462,7 @@ enum reg_class #define REG_CLASS_NAMES \ { \ "NO_REGS", \ - "CALLER_SAVE_REGS", \ + "TAILCALL_ADDR_REGS", \ "GENERAL_REGS", \ "STACK_REG", \ "POINTER_REGS", \ @@ -475,7 +475,7 @@ enum reg_class #define REG_CLASS_CONTENTS \ { \ { 0x00000000, 0x00000000, 0x00000000 }, /* NO_REGS */ \ - { 0x0007ffff, 0x00000000, 0x00000000 }, /* CALLER_SAVE_REGS */ \ + { 0x0004ffff, 0x00000000, 0x00000000 }, /* TAILCALL_ADDR_REGS */\ { 0x7fffffff, 0x00000000, 0x00000003 }, /* GENERAL_REGS */ \ { 0x80000000, 0x00000000, 0x00000000 }, /* STACK_REG */ \ { 0xffffffff, 0x00000000, 0x00000003 }, /* POINTER_REGS */ \ diff --git a/gcc/config/aarch64/aarch64.c b/gcc/config/aarch64/aarch64.c index 75a6c0d0421354d7c0759292947eb5d407f5b703..66d503ac6edf59a1ea2fa3675fbbe03d70769833 100644 --- a/gcc/config/aarch64/aarch64.c +++ b/gcc/config/aarch64/aarch64.c @@ -6060,7 +6060,7 @@ aarch64_class_max_nregs (reg_class_t regclass, machine_mode mode) { switch (regclass) { - case CALLER_SAVE_REGS: + case TAILCALL_ADDR_REGS: case POINTER_REGS: case GENERAL_REGS: case ALL_REGS: @@ -8226,10 +8226,10 @@ aarch64_register_move_cost (machine_mode mode, = aarch64_tune_params.regmove_cost; /* Caller save and pointer regs are equivalent to GENERAL_REGS. */ - if (to == CALLER_SAVE_REGS || to == POINTER_REGS) + if (to == TAILCALL_ADDR_REGS || to == POINTER_REGS) to = GENERAL_REGS; - if (from == CALLER_SAVE_REGS || from == POINTER_REGS) + if (from == TAILCALL_ADDR_REGS || from == POINTER_REGS) from = GENERAL_REGS; /* Moving between GPR and stack cost is the same as GP2GP. */ diff --git a/gcc/config/aarch64/constraints.md b/gcc/config/aarch64/constraints.md index af4143ef756464afac29d17f124b436520f90451..c3791aa89562a5d5542098d2f7951afc57901150 100644 --- a/gcc/config/aarch64/constraints.md +++ b/gcc/config/aarch64/constraints.md @@ -21,8 +21,8 @@ (define_register_constraint "k" "STACK_REG" "@internal The stack register.") -(define_register_constraint "Ucs" "CALLER_SAVE_REGS" - "@internal The caller save registers.") +(define_register_constraint "Ucs" "TAILCALL_ADDR_REGS" + "@internal The indirect tail call address registers") (define_register_constraint "w" "FP_REGS" "Floating point and SIMD vector registers.")