As GCC 12.2 does, Interrupt handler only saves Fa & Ft regs, not include Fs regs.
By changing RISCVCallingConv.td like this:
// Same as CSR_Interrupt, but including all 32-bit FP registers.
def CSR_XLEN_F32_Interrupt: CalleeSavedRegs<(add X1,
(sequence "X%u", 3, 9),
(sequence "X%u", 10, 11),
(sequence "X%u", 12, 17),
(sequence "X%u", 18, 27),
(sequence "X%u", 28, 31),
(sequence "F%u_F", 0, 7),
(sequence "F%u_F", 10, 11),
(sequence "F%u_F", 12, 17),
(sequence "F%u_F", 28, 31))>;
I get the wrong result, which is different with gcc’s,
60025e: dcf6 sw t4,120(sp)
600260: dafa sw t5,116(sp)
600262: d8fe sw t6,112(sp)
600264: f682 fsw ft0,108(sp)
600266: f486 fsw ft1,104(sp)
600268: f28a fsw ft2,100(sp)
60026a: f08e fsw ft3,96(sp)
60026c: ee92 fsw ft4,92(sp)
60026e: ec96 fsw ft5,88(sp)
600270: ea9a fsw ft6,84(sp)
600272: e89e fsw ft7,80(sp)
600274: e6aa fsw fa0,76(sp)
600276: e4ae fsw fa1,72(sp)
600278: e2b2 fsw fa2,68(sp)
60027a: e0b6 fsw fa3,64(sp)
60027c: fe3a fsw fa4,60(sp)
60027e: fc3e fsw fa5,56(sp)
600280: fa42 fsw fa6,52(sp)
600282: f846 fsw fa7,48(sp)
600284: f672 fsw ft8,44(sp)
600286: f476 fsw ft9,40(sp)
600288: f27a fsw ft10,36(sp)
60028a: f07e fsw ft11,32(sp)
60028c: 6541 lui a0,0x10
60028e: 00052503 lw a0,0(a0) # 10000 <a>
600292: 65c1 lui a1,0x10
600294: 0045a407 flw fs0,4(a1) # 10004 <b>
600298: d00574d3 fcvt.s.w fs1,a0
60029c: 40a00537 lui a0,0x40a00
6002a0: f0050953 fmv.w.x fs2,a0
6002a4: 49247443 fmadd.s fs0,fs0,fs2,fs1
6002a8: b881a027 fsw fs0,-1152(gp) # 10380 <c>
6002ac: 3791 jal 6001f0 <foo>
6002ae: 50ba lw ra,172(sp)
6002b0: 52aa lw t0,168(sp)
6002b2: 531a lw t1,164(sp)
6002b4: 538a lw t2,160(sp)
6002b6: 457a lw a0,156(sp)
6002b8: 45ea lw a1,152(sp)
In the prelogue, It indeed does not save Fs regs, but uses Fs reg in the rest program.
While gcc 12.2 only uses Fa/Ft regs.