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.

Reply via email to