Hi Kito, On 2025/09/11 16:43, Kito Cheng wrote: > Hi Tsukasa: > > LGTM! Thanks a lot for spotting this hidden bug and sending the patch! > Looks like LLVM has a similar issue too - seems we’ll need a fix there > as well :) > > Also, IIRC you have binutils commit access, so you should be able to > push this directly. Let me know if you can’t push to trunk.
Committed, thanks! Tsukasa > On Tue, Sep 9, 2025 at 9:56 AM Tsukasa OI <[email protected]> > wrote: >> >> From: Tsukasa OI <[email protected]> >> >> In general, tail call optimization requires that the callee's saved >> registers are a superset of the caller's. >> >> The Standard Vector Calling Convention Variant (assembler: .variant_cc) >> requires that a function with this calling convention preserves vector >> registers v1-v7 and v24-v31 across calls (i.e. callee-saved). However, >> the same set of registers are (function-local) temporary registers >> (i.e. caller-saved) on the normal (non-vector) calling convention. >> >> Even if a function with this calling convention variant calls another >> function with a non-vector calling convention, those vector registers >> are correctly clobbered -- except when the sibling (tail) call >> optimization occurs as it violates the general rule mentioned above. >> >> If this happens, following function body: >> >> 1. Save v1-v7 and v24-v31 for clobbering >> 2. Call another function with a non-vector calling convention >> (which may destroy v1-v7 and/or v24-v31) >> 3. Restore v1-v7 and v24-v31 >> 4. Return. >> >> may be incorrectly optimized into the following sequence: >> >> 1. Save v1-v7 and v24-v31 for clobbering >> 2. Restore v1-v7 and v24-v31 (?!) >> 3. Jump to another function with a non-vector calling convention >> (which may destroy v1-v7 and/or v24-v31). >> >> This commit suppresses cross CC sibling call optimization from >> the vector calling convention variant. >> >> gcc/ChangeLog: >> >> * config/riscv/riscv.cc (riscv_function_ok_for_sibcall): >> Suppress cross calling convention sibcall optimization from >> the vector calling convention variant. >> >> gcc/testsuite/ChangeLog: >> >> * gcc.target/riscv/rvv/base/abi-call-variant_cc-sibcall.c: New test. >> * >> gcc.target/riscv/rvv/base/abi-call-variant_cc-sibcall-indirect-1.c: Ditto. >> * >> gcc.target/riscv/rvv/base/abi-call-variant_cc-sibcall-indirect-2.c: Ditto. >> --- >> gcc/config/riscv/riscv.cc | 6 +++ >> .../abi-call-variant_cc-sibcall-indirect-1.c | 12 +++++ >> .../abi-call-variant_cc-sibcall-indirect-2.c | 12 +++++ >> .../rvv/base/abi-call-variant_cc-sibcall.c | 54 +++++++++++++++++++ >> 4 files changed, 84 insertions(+) >> create mode 100644 >> gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-variant_cc-sibcall-indirect-1.c >> create mode 100644 >> gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-variant_cc-sibcall-indirect-2.c >> create mode 100644 >> gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-variant_cc-sibcall.c >> >> diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc >> index bfd43fba1013..0dcc3029c6bf 100644 >> --- a/gcc/config/riscv/riscv.cc >> +++ b/gcc/config/riscv/riscv.cc >> @@ -11997,6 +11997,12 @@ riscv_function_ok_for_sibcall (tree decl >> ATTRIBUTE_UNUSED, >> if (cfun->machine->interrupt_handler_p) >> return false; >> >> + /* Don't use sibcall if a non-vector CC function is being called >> + from a vector CC function. */ >> + if ((riscv_cc) crtl->abi->id () == RISCV_CC_V >> + && (riscv_cc) expr_callee_abi (exp).id () != RISCV_CC_V) >> + return false; >> + >> /* Don't use sibcalls in the large model, because a sibcall instruction >> expanding and a epilogue expanding both use RISCV_PROLOGUE_TEMP >> register. */ >> diff --git >> a/gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-variant_cc-sibcall-indirect-1.c >> >> b/gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-variant_cc-sibcall-indirect-1.c >> new file mode 100644 >> index 000000000000..54ff106716f8 >> --- /dev/null >> +++ >> b/gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-variant_cc-sibcall-indirect-1.c >> @@ -0,0 +1,12 @@ >> +/* { dg-do compile } */ >> +/* { dg-options "-march=rv64gcv -mabi=lp64d -O2" } */ >> + >> +void __attribute__((riscv_vector_cc)) >> +f_try_sibcall_v2v_indirect (void __attribute__((riscv_vector_cc)) >> + (*func) (void)) >> +{ >> + func (); >> +} >> + >> +/* { dg-final { scan-assembler-times >> "\\.variant_cc\tf_try_sibcall_v2v_indirect\n" 1 } } */ >> +/* { dg-final { scan-assembler-times "\tjr\ta0\n" 1 } } */ >> diff --git >> a/gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-variant_cc-sibcall-indirect-2.c >> >> b/gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-variant_cc-sibcall-indirect-2.c >> new file mode 100644 >> index 000000000000..121ac0f57e0a >> --- /dev/null >> +++ >> b/gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-variant_cc-sibcall-indirect-2.c >> @@ -0,0 +1,12 @@ >> +/* { dg-do compile } */ >> +/* { dg-options "-march=rv64gcv -mabi=lp64d -O2" } */ >> + >> +void __attribute__((riscv_vector_cc)) >> +f_try_sibcall_v2n_indirect (void (*func) (void)) >> +{ >> + func (); >> +} >> + >> +/* { dg-final { scan-assembler-times >> "\\.variant_cc\tf_try_sibcall_v2n_indirect\n" 1 } } */ >> +/* { dg-final { scan-assembler-times "\tjalr\ta0\n" 1 } } */ >> +/* { dg-final { scan-assembler-times "\tjr\tra\n" 1 } } */ >> diff --git >> a/gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-variant_cc-sibcall.c >> b/gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-variant_cc-sibcall.c >> new file mode 100644 >> index 000000000000..ccfc38777f49 >> --- /dev/null >> +++ b/gcc/testsuite/gcc.target/riscv/rvv/base/abi-call-variant_cc-sibcall.c >> @@ -0,0 +1,54 @@ >> +/* { dg-do compile } */ >> +/* { dg-options "-march=rv64gcv -mabi=lp64d -O2" } */ >> + >> +void f_ext_n2n (void); >> +void f_ext_v2n (void); >> +void __attribute__((riscv_vector_cc)) f_ext_n2v (void); >> +void __attribute__((riscv_vector_cc)) f_ext_v2v (void); >> + >> +/* { dg-final { scan-assembler-times "\\.variant_cc\tf_ext_n2n\n" 0 } } */ >> +/* { dg-final { scan-assembler-times "\\.variant_cc\tf_ext_v2n\n" 0 } } */ >> +/* { dg-final { scan-assembler-times "\\.variant_cc\tf_ext_n2v\n" 1 } } */ >> +/* { dg-final { scan-assembler-times "\\.variant_cc\tf_ext_v2v\n" 1 } } */ >> + >> +void >> +f_try_sibcall_n2n (void) >> +{ >> + f_ext_n2n (); >> +} >> + >> +/* { dg-final { scan-assembler-times "\\.variant_cc\tf_try_sibcall_n2n\n" 0 >> } } */ >> +/* { dg-final { scan-assembler-times "\ttail\tf_ext_n2n\n" 1 } } */ >> +/* { dg-final { scan-assembler-times "\tcall\tf_ext_n2n\n" 0 } } */ >> + >> +void >> +f_try_sibcall_n2v (void) >> +{ >> + f_ext_n2v (); >> +} >> + >> +/* { dg-final { scan-assembler-times "\\.variant_cc\tf_try_sibcall_n2v\n" 0 >> } } */ >> +/* { dg-final { scan-assembler-times "\ttail\tf_ext_n2v\n" 1 } } */ >> +/* { dg-final { scan-assembler-times "\tcall\tf_ext_n2v\n" 0 } } */ >> + >> +void __attribute__((riscv_vector_cc)) >> +f_try_sibcall_v2n (void) >> +{ >> + /* Vector to normal: sibling call optimization shall be >> + suppressed to preserve caller's registers: v1-v7 and v24-v31. */ >> + f_ext_v2n (); >> +} >> + >> +/* { dg-final { scan-assembler-times "\\.variant_cc\tf_try_sibcall_v2n\n" 1 >> } } */ >> +/* { dg-final { scan-assembler-times "\ttail\tf_ext_v2n\n" 0 } } */ >> +/* { dg-final { scan-assembler-times "\tcall\tf_ext_v2n\n" 1 } } */ >> + >> +void __attribute__((riscv_vector_cc)) >> +f_try_sibcall_v2v (void) >> +{ >> + f_ext_v2v (); >> +} >> + >> +/* { dg-final { scan-assembler-times "\\.variant_cc\tf_try_sibcall_v2v\n" 1 >> } } */ >> +/* { dg-final { scan-assembler-times "\ttail\tf_ext_v2v\n" 1 } } */ >> +/* { dg-final { scan-assembler-times "\tcall\tf_ext_v2v\n" 0 } } */ >> -- >> 2.43.0 >> >
