Page MenuHomePhabricator

[RISCV] Add ABI testing for Float16.
Needs ReviewPublic

Authored by HsiangKai on Oct 24 2021, 10:01 PM.

Diff Detail

Event Timeline

HsiangKai created this revision.Oct 24 2021, 10:01 PM
HsiangKai requested review of this revision.Oct 24 2021, 10:01 PM
Herald added a project: Restricted Project. · View Herald TranscriptOct 24 2021, 10:01 PM

It looks as though all checks are checking the same thing? Presumably this is expected? I wonder if allowing an extra combined check (--check-prefixes=CHECK,CHECK-ZFH-ILP32F or something) would make it more obvious when things *are* different between the different configs.

I'm not familiar with how Float16 is supposed to behave if the target doesn't advertise support zfh, but I come more from OpenCL where it's either fully supported or "storage-only", in which case I wouldn't expect a fadd to get past the frontend (or maybe it'd enforce promotion to float?). This isn't necessarily a blocker - I'm just showing the limits of my knowledge in this area.

It looks as though all checks are checking the same thing? Presumably this is expected? I wonder if allowing an extra combined check (--check-prefixes=CHECK,CHECK-ZFH-ILP32F or something) would make it more obvious when things *are* different between the different configs.

I'm not familiar with how Float16 is supposed to behave if the target doesn't advertise support zfh, but I come more from OpenCL where it's either fully supported or "storage-only", in which case I wouldn't expect a fadd to get past the frontend (or maybe it'd enforce promotion to float?). This isn't necessarily a blocker - I'm just showing the limits of my knowledge in this area.

If I remember correctly, the frontend doesn't know if the backend has support for half. It only has a single flag to say whether Float16 should be a valid type. I believe on AArch64 gcc does promote it in the frontend when there are no native instructions. clang does not. The effect of this is that clang rounds a lot more often than gcc when half isn't supported. gcc will promote an entire expression an only round at places like assignment.

rogfer01 added a comment.EditedOct 27 2021, 11:57 PM

I'm curious about how we handle _Float16 here.

My understanding from the psabi and the Zfh draft, don't we want under Zfh + ilp32f to pass the float16 directly in a FP register?

So for a case like this

typedef _Float16 FP;
typedef struct A { FP a; FP b; } A;

void foo(A);

void bar()
{
    A a = {1.0, 2.0};
    foo(a);
}

I'd expect Zfh + ilp32 to pass an i32 (which we already do)

define dso_local void @bar() local_unnamed_addr #0 {
  tail call void @foo(i32 1073757184) #2
  ret void
}

and Zfh + ilp32f (and I understand ilp32d too) do

define dso_local void @bar() local_unnamed_addr #0 {
  tail call void @foo(half 1.0e+0.0, half 2.0e+0.0)
  ret void
}

Perhaps it was already decided against using FP regs for _Float16?

I think a similar consideration can be done for rv64 + Zfh and lp64 / lp64f / lp64d, the last two using FP registers.

If this is the case, then I understand a parameter like

typedef struct A { _Float16 a; float b; } A;

would also be passed as two FP registers in ilp32f, ilp32d, lp64f and lp64d.

From the psABI aspect, we already included that by this PR, this PR added size and alignment for _Float16, and we didn't added extra rule for that, because we believe that's already covered by existing rule:


For soft float ABI (ilp32/lp64):

Floating-point reals are passed the same way as aggregates of the same size, complex floating-point numbers are passed the same way as a struct containing two floating-point reals. (This constraint changes when the integer calling convention is augmented by the hardware floating-point calling convention.)

-> _Float16 following same rule as float, pass argument and return value in GPR.


For hard float ABI (ilp32f/lp64f/ilp32d/lp64d):

A real floating-point argument is passed in a floating-point argument register if it is no more than FLEN bits wide and at least one floating-point argument register is available. Otherwise, it is passed according to the integer calling convention. When a floating-point argument narrower than FLEN bits is passed in a floating-point register, it is 1-extended (NaN-boxed) to FLEN bits.

-> _Float16 following same rule as float, pass argument and return value in FPR.


So basically same as @rogfer01's understanding and the rule is same between w/ and w/o zfh.

@jrtc27 could you take a look for this revision?