Skip to content

LLVM miscompiles consecutive half operations by using too much precision on several backends #97975

Description

@beetrees

Consider the following IR:

define half @f(half %a, half %b, half %c) {
    %d = fadd half %a, %b
    %e = fadd half %d, %c
    ret half %e
}

On backends without native half support, LLVM generally lowers half by converting to float, performing the desired operation, and then converting back to half. For this to be a valid lowering, a conversion back to f16 must occur after each operation, otherwise the excess precision of float will affect the result. For example 65504.0 + 65504.0 + -65504.0 equals infinity if each operation is done at half precision, but will result in 65504.0 if the intermediate value is not rounded to a half but instead kept as a float. This excess runtime precision can lead to miscompilations similar to #89885.

However, it appears that on several backends LLVM will fail to round the intermediate result of consecutive half operations, leading to these kind of bugs. I'm filing this as a single issue rather than a separate issue for each backend as I believe this is an issue with LLVM's default handling of half operations rather than a problem in any specific backend (for instance, AFAIK the only backend-specific code LoongArch has for handling half was added in #94456).

By inspecting the assembly LLVM emits, I've discovered that at least the following backends appear to be affected (in brackets are a specific target triple that I've checked):

I also noticed that 32-bit ARM has this problem in LLVM 18 but not on the main branch. Looking through the issue tracker, it seems it's likely that this was fixed by #80440.

Related to #97981.

Activity

  1. llvmbot commented on Jul 7, 2024

    @llvmbot
    Member

    @llvm/issue-subscribers-backend-powerpc

    Author: None (beetrees)

    Details Consider the following IR: ```llvm define half @f(half %a, half %b, half %c) { %d = fadd half %a, %b %e = fadd half %d, %c ret half %e } ```

    On backends without native half support, LLVM generally lowers half by converting to float, performing the desired operation, and then converting back to half. For this to be a valid lowering, a conversion back to f16 must occur after each operation, otherwise the excess precision of float will affect the result. For example 65504.0 + 65504.0 + -65504.0 equals infinity if each operation is done at half precision, but will result in 65504.0 if the intermediate value is not rounded to a half but instead kept as a float. This excess runtime precision can lead to miscompilations similar to #89885.

    However, it appears that on several backends LLVM will fail to round the intermediate result of consecutive half operations, leading to these kind of bugs. I'm filing this as a single issue rather than a separate issue for each backend as I believe this is an issue with LLVM's default handling of half operations rather than a problem in any specific backend (for instance, AFAIK the only backend-specific code LoongArch has for handling half was added in #94456).

    By inspecting the assembly LLVM emits, I've discovered that at least the following backends appear to be affected (in brackets are a specific target triple that I've checked):

    I also noticed that 32-bit ARM has this problem in LLVM 18 but not on the main branch. Looking through the issue tracker, it seems it's likely that this was fixed by #80440.

  2. llvmbot commented on Jul 7, 2024

    @llvmbot
    Member

    @llvm/issue-subscribers-backend-hexagon

    Author: None (beetrees)

    Details Consider the following IR: ```llvm define half @f(half %a, half %b, half %c) { %d = fadd half %a, %b %e = fadd half %d, %c ret half %e } ```

    On backends without native half support, LLVM generally lowers half by converting to float, performing the desired operation, and then converting back to half. For this to be a valid lowering, a conversion back to f16 must occur after each operation, otherwise the excess precision of float will affect the result. For example 65504.0 + 65504.0 + -65504.0 equals infinity if each operation is done at half precision, but will result in 65504.0 if the intermediate value is not rounded to a half but instead kept as a float. This excess runtime precision can lead to miscompilations similar to #89885.

    However, it appears that on several backends LLVM will fail to round the intermediate result of consecutive half operations, leading to these kind of bugs. I'm filing this as a single issue rather than a separate issue for each backend as I believe this is an issue with LLVM's default handling of half operations rather than a problem in any specific backend (for instance, AFAIK the only backend-specific code LoongArch has for handling half was added in #94456).

    By inspecting the assembly LLVM emits, I've discovered that at least the following backends appear to be affected (in brackets are a specific target triple that I've checked):

    I also noticed that 32-bit ARM has this problem in LLVM 18 but not on the main branch. Looking through the issue tracker, it seems it's likely that this was fixed by #80440.

  3. 132 remaining items

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions