Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

llvm.fma.f16 intrinsic is expanded incorrectly on targets without native half FMA support #98389

Open
beetrees opened this issue Jul 10, 2024 · 0 comments
Labels
floating-point Floating-point math llvm:SelectionDAG SelectionDAGISel as well miscompilation

Comments

@beetrees
Copy link
Contributor

Consider the following LLVM IR:

declare half @llvm.fma.f16(half %a, half %b, half %c)

define half @do_fma(half %a, half %b, half %c) {
    %res = call half @llvm.fma.f16(half %a, half %b, half %c)
    ret half %res
}

On targets without native half FMA support, LLVM turns this into the equivalent of:

declare float @llvm.fma.f32(float %a, float %b, float %c)

define half @do_fma(half %a, half %b, half %c) {
    %a_f32 = fpext half %a to float
    %b_f32 = fpext half %b to float
    %c_f32 = fpext half %c to float
    %res_f32 = call float @llvm.fma.f32(float %a_f32, float %b_f32, float %c_f32)
    %res = fptrunc float %res_f32 to half
    ret half %res
}

This is a miscompilation, however, as float does not have enough precision to do a fused-multiply-add for half without double rounding becoming an issue. For instance (raw bits of each half are in brackets): do_fma(48.34375 (0x520b), 0.000013887882 (0x00e9), 0.12438965 (0x2ff6)) = 0.12512207 (0x3001), but LLVM's lowering to float FMA gives an incorrect result of 0.125 (0x3000).

A correct lowering would need to use double (or larger): a double FMA is not required as double is large enough to represent the result of half * half without any rounding. In summary, a correct lowering would look something like this:

declare double @llvm.fmuladd.f64(double %a, double %b, double %c)

define half @do_fma(half %a, half %b, half %c) {
    %a_f64 = fpext half %a to double
    %b_f64 = fpext half %b to double
    %c_f64 = fpext half %c to double
    %res_f64 = call double @llvm.fmuladd.f64(double %a_f64, double %b_f64, double %c_f64)
    %res = fptrunc double %res_f64 to half
    ret half %res
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
floating-point Floating-point math llvm:SelectionDAG SelectionDAGISel as well miscompilation
Projects
None yet
Development

No branches or pull requests

2 participants