https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86387

            Bug ID: 86387
           Summary: [RISCV][ABI] GCC fails to sign/zero-ext integers as
                    necessary for return of int+fp structs in with
                    hard-float ABIs
           Product: gcc
           Version: 9.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: target
          Assignee: unassigned at gcc dot gnu.org
          Reporter: asb at lowrisc dot org
  Target Milestone: ---

The psABI is documented here:
https://github.com/riscv/riscv-elf-psabi-doc/blob/master/riscv-elf.md

Structs of containing one int and one fp value are passed in int+fp registers,
as described here:
"""
A struct containing one floating-point real and one integer, in either order,
is passed in a floating-point register and an integer register, with the
integer zero- or sign-extended as though it were a scalar, provided the
floating-point real is no more than FLEN bits wide and the integer is no more
than XLEN bits wide, and at least one floating-point argument register and at
least one integer argument register is available. Otherwise, it is passed
according to the integer calling convention.
"""

Scalar sign/zero-extension is specified as "When passed in registers, scalars
narrower than XLEN bits are widened according to the sign of their type up to
32 bits, then sign-extended to XLEN bits."

As for return, the document states "Values are returned in the same manner as a
first named argument of the same type would be passed."

Assume a struct consisting of a uint8 and a float:
struct s_u8_f { uint8_t i; float f; };

By the above rules if passed as the first argument to a function, we would
expect to see i in a0 (zero-extended), and f in fa0. As specified above, we
expect the same if this struct is returned by value.

GCC does not sign or zero-extend as it should as demonstrated in the program
below.

$ cat foo.c 
#include <stdint.h>

struct s_u8_f { uint8_t i; float f; };
struct s_i8_f { int8_t i;  float f; };

struct s_u8_f should_zext_arg(int8_t i, float f) {
  struct s_u8_f s;
  s.i = i;
  s.f = f;
  return s;
}

struct s_i8_f should_sext_arg(uint8_t i, float f) {
  struct s_i8_f s;
  s.i = i;
  s.f = f;
  return s;
}

$ ./riscv32-unknown-elf-gcc -march=rv32imf -mabi=ilp32f foo.c -S -o - -O1
        .file   "foo.c"
        .option nopic
        .text
        .align  2
        .globl  should_zext_arg
        .type   should_zext_arg, @function
should_zext_arg:
        addi    sp,sp,-32
        addi    sp,sp,32
        jr      ra
        .size   should_zext_arg, .-should_zext_arg
        .align  2
        .globl  should_sext_arg
        .type   should_sext_arg, @function
should_sext_arg:
        addi    sp,sp,-32
        addi    sp,sp,32
        jr      ra
        .size   should_sext_arg, .-should_sext_arg
        .ident  "GCC: (GNU) 9.0.0 20180703 (experimental)"

Reply via email to