As introduced in D99148, RISC-V uses the softPromoteHalf legalisation for fp16 values without zfh, with logic ensuring that f16 values are passed in lower bits of FPRs (see D98670) when F or D support is present. This legalisation produces ISD::FP_TO_FP16 and ISD::FP16_TO_FP nodes which (as described in ISDOpcodes.h) provide a "semi-softened interface for dealing with f16 (as an i16)". i.e. the return type of the FP_TO_FP16 is an integer rather than a float (and the arg of FP16_TO_FP is an integer). The remainder of the description focuses primarily on FP_TO_FP16 for ease of explanation.
FP_TO_FP16 is lowered to a libcall to __truncsfhf2 (float) or __truncdfhf2 (double). As of D92241, _Float16 is used as the return type of these libcalls if the host compiler accepts _Float16 in a test input (i.e. dst_t is set to _Float16). _Float16 is enabled for the RISC-V target as of D105001 and so the return value should be passed in an FPR on hard float ABIs.
This patch fixes the ABI issue in what appears to be a minimally invasive way - leaving the softPromoteHalf logic undisturbed, and lowering FP_TO_FP16 to an f32-returning libcall, converting its result to an XLen integer value. Alternate strategies might be possible involving custom legalisation of FP_ROUND with an f16 target.
As can be seen in the test changes, the custom lowering for FP16_TO_FP means the libcall is no longer tail-callable.
Outstanding issues:
- Redundant fmv.x.w and fmv.w.x pairs produced in FP16_TO_FP lowering.
- Missing STRICT variants.
I think we usually use the British spelling "legalisation"?