Bug 86387 - [RISCV][ABI] GCC fails to sign/zero-ext integers as necessary for passing/returning int+fp structs on hard-float ABIs
Summary: [RISCV][ABI] GCC fails to sign/zero-ext integers as necessary for passing/ret...
Status: RESOLVED WONTFIX
Alias: None
Product: gcc
Classification: Unclassified
Component: target (show other bugs)
Version: 9.0
: P3 normal
Target Milestone: ---
Assignee: Jim Wilson
URL:
Keywords: ABI, wrong-code
Depends on:
Blocks:
 
Reported: 2018-07-03 11:20 UTC by Alex Bradbury
Modified: 2021-05-05 18:21 UTC (History)
1 user (show)

See Also:
Host:
Target: riscv
Build:
Known to work:
Known to fail:
Last reconfirmed: 2018-07-03 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Alex Bradbury 2018-07-03 11:20:30 UTC
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)"
Comment 1 Alex Bradbury 2018-07-03 12:55:18 UTC
Changing title as this bug affects both return and argument passing. I could create a separate bug report if preferred, but the issues seem so closely related it might make most sense to keep here.

No sign or zero-extension takes place in the following example, but as-per the ABI it should.
 
I actually sought clarification on this aspect of the ABI last September, to perhaps this is a regression in GCC behaviour? https://github.com/riscv/riscv-elf-psabi-doc/issues/37

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

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

void must_receive_zext_arg(struct s_u8_f);
void must_receive_sext_arg(struct s_i8_f);

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

void should_sext_arg(uint8_t i, float f) {
  struct s_i8_f s;
  s.i = i;
  s.f = f;
  must_receive_sext_arg(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
	sw	ra,28(sp)
	call	must_receive_zext_arg
	lw	ra,28(sp)
	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
	sw	ra,28(sp)
	call	must_receive_sext_arg
	lw	ra,28(sp)
	addi	sp,sp,32
	jr	ra
	.size	should_sext_arg, .-should_sext_arg
	.ident	"GCC: (GNU) 9.0.0 20180703 (experimental)"
Comment 2 Jim Wilson 2018-07-03 18:29:07 UTC
I don't see any gcc regression.  I think the psABI spec was updated without checking gcc.  Unfortunately, fixing gcc would cause a non-backward compatible ABI change.  Given that we fixed the ABI back in January when we upstreamed glibc, we can't change it now without causing a lot of trouble.  Hence, the easy solution is to fix the paABI spec to match what gcc actually emits.
Comment 3 Jim Wilson 2021-05-05 18:21:34 UTC
This was fixed with a psABI change in Nov 2018 and we forgot to update this bug report.
    https://github.com/riscv/riscv-elf-psabi-doc/pull/74