https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87690
Jim Wilson <wilson at gcc dot gnu.org> changed: What |Removed |Added ---------------------------------------------------------------------------- Status|UNCONFIRMED |NEW Last reconfirmed| |2018-10-23 Assignee|unassigned at gcc dot gnu.org |wilson at gcc dot gnu.org Ever confirmed|0 |1 --- Comment #4 from Jim Wilson <wilson at gcc dot gnu.org> --- I think the intent of the second rule is that float values are passed in the same regs as an integer value, and that it wasn't the intent that the promotion rule also applied to float values. The GCC RISC-V port is passing 32-bit floats as SFmode, which means only the low 32-bits of the value are valid. A struct with a single float field also gets treated as SFmode, so that we can directly access the float member. riscv_function_arg isn't checking the argument type, it is only checking the argument mode. Hence, a float and a struct with a single float member get handled exactly the same. Since they are passed the same, there is no conversion code to go from one to the other. We would have the exact same problem with a struct with a single int field, except that PROMOTE_MODE forces SImode to be handled as DImode, and the promote_mode function does check types, and only applies PROMOTE_MODE to integer types. Hence, a struct with single int field is SImode and an int is DImode, and we require conversion code which does the sign extension called for by the ABI. But PROMOTE_MODE is only sensible for integral types, so this can't solve the float problem. I don't see any point to trying to sign extend a 32-bit float in a 64-bit integer register. There are only a few useful things one can do to a float in an integer register, such as absolute value and signbit, and having the value be sign extended doesn't help there. For instance given #include <math.h> float sub (float a) { return fabs (a); } and compiling with riscv64-unknown-elf-gcc -O2 -S -mabi=lp64 -march=rv64i I get sub: slli a0,a0,33 srli a0,a0,33 ret The upper 32-bits of a0 are being treated as don't care bits for both the argument and the return value. They are ignored for the input value, and set to 0 for the return value. Having the value sign-extended doesn't make this code any simpler. I see that we aren't actually optimizing signbit as we should be, but again having it sign-extended doesn't give shorter code. Requiring that float values be sign extended in a 64-bit reg might require emitting extra instructions in some cases, which could reduce performance. So it also seems unwise from that point of view. Consider this testcase for instance struct float_struct { float v; float w;}; struct float_struct callee(float, float); struct float_struct caller(struct float_struct fs) { return callee(fs.v, fs.w); } Compiled with riscv64-unknown-elf-gcc -O2 -S -mabi=lp64 I get caller: addi sp,sp,-32 sd a0,8(sp) srli a1,a0,32 addi sp,sp,32 tail callee The 2-float struct is passed entirely in a0. Since the upper 32-bits of a float arg are don't care bits, we can pass a0 directly to callee unchanged. The second arg for callee is extracted from the upper bits of a0 with a logical shift that zero extends it. We could change the logical shift to an arithmetic shift at no cost. But sign-extending the float a0 would require adding two shift instructions. I am also concerned that there might be implementation problems trying to convince gcc to sign-extend floating point values in integer registers, as that isn't a natural thing to do. I think the simplest solution here is to update the psABI to indicate that float values in integer registers are not sign extended. Or alternatively that the sign extension rule only applies to integer types.