[InstCombine] fix miscompile when casting int->FP->int

As shown in https://github.com/llvm/llvm-project/issues/55150 -
the existing fold may be wrong when converting to a signed value.
This is a quick fix to avoid the miscompile.

I added tests/comments for all of the signed/unsigned combinations
at either side of the boundary width, and tried to confirm with Alive2:
https://alive2.llvm.org/ce/z/3p9DSu

There are already some TODO items in the test file that suggest
possible refinements, so the regression with ui->FP->si is probably ok.
It seems unlikely that we'd see these kind of edge cases with
non-byte-width integer types in real code. The potential miscompile
went undetected for several years.

This and 747c6a0c73 fixes #55150.

Differential Revision: https://reviews.llvm.org/D124692
This commit is contained in:
Sanjay Patel 2022-05-07 08:22:08 -04:00
parent 6e345426de
commit 8650f05c97
2 changed files with 25 additions and 5 deletions

View File

@ -1977,7 +1977,7 @@ Instruction *InstCombinerImpl::foldItoFPtoI(CastInst &FI) {
// If the destination type is narrow, that means the intermediate FP value
// must be large enough to hold the source value exactly.
// For example, (uint8_t)((float)(uint32_t 16777217) is undefined behavior.
int OutputSize = (int)DestType->getScalarSizeInBits() - IsOutputSigned;
int OutputSize = (int)DestType->getScalarSizeInBits();
if (OutputSize > OpI->getType()->getFPMantissaWidth())
return nullptr;
}

View File

@ -190,11 +190,13 @@ define i32 @test17(i26 %A) {
ret i32 %C
}
; This can fold because the 54-bit output is signed and we discard the sign bit.
; This can't fold because the 54-bit output is big enough to hold an input
; that was rounded when converted to double.
define i54 @test18(i64 %A) {
; CHECK-LABEL: @test18(
; CHECK-NEXT: [[C:%.*]] = trunc i64 [[A:%.*]] to i54
; CHECK-NEXT: [[B:%.*]] = sitofp i64 [[A:%.*]] to double
; CHECK-NEXT: [[C:%.*]] = fptosi double [[B]] to i54
; CHECK-NEXT: ret i54 [[C]]
;
%B = sitofp i64 %A to double
@ -246,6 +248,8 @@ define i25 @low_masked_input(i25 %A) {
ret i25 %C
}
; Output is small enough to ensure exact cast (overflow produces poison).
define i11 @s32_half_s11(i32 %x) {
; CHECK-LABEL: @s32_half_s11(
; CHECK-NEXT: [[R:%.*]] = trunc i32 [[X:%.*]] to i11
@ -256,6 +260,8 @@ define i11 @s32_half_s11(i32 %x) {
ret i11 %r
}
; Output is small enough to ensure exact cast (overflow produces poison).
define i11 @s32_half_u11(i32 %x) {
; CHECK-LABEL: @s32_half_u11(
; CHECK-NEXT: [[R:%.*]] = trunc i32 [[X:%.*]] to i11
@ -266,6 +272,8 @@ define i11 @s32_half_u11(i32 %x) {
ret i11 %r
}
; Output is small enough to ensure exact cast (overflow produces poison).
define i11 @u32_half_s11(i32 %x) {
; CHECK-LABEL: @u32_half_s11(
; CHECK-NEXT: [[R:%.*]] = trunc i32 [[X:%.*]] to i11
@ -276,6 +284,8 @@ define i11 @u32_half_s11(i32 %x) {
ret i11 %r
}
; Output is small enough to ensure exact cast (overflow produces poison).
define i11 @u32_half_u11(i32 %x) {
; CHECK-LABEL: @u32_half_u11(
; CHECK-NEXT: [[R:%.*]] = trunc i32 [[X:%.*]] to i11
@ -286,9 +296,12 @@ define i11 @u32_half_u11(i32 %x) {
ret i11 %r
}
; Too many bits in output to ensure exact cast.
define i12 @s32_half_s12(i32 %x) {
; CHECK-LABEL: @s32_half_s12(
; CHECK-NEXT: [[R:%.*]] = trunc i32 [[X:%.*]] to i12
; CHECK-NEXT: [[H:%.*]] = sitofp i32 [[X:%.*]] to half
; CHECK-NEXT: [[R:%.*]] = fptosi half [[H]] to i12
; CHECK-NEXT: ret i12 [[R]]
;
%h = sitofp i32 %x to half
@ -296,6 +309,8 @@ define i12 @s32_half_s12(i32 %x) {
ret i12 %r
}
; Too many bits in output to ensure exact cast.
define i12 @s32_half_u12(i32 %x) {
; CHECK-LABEL: @s32_half_u12(
; CHECK-NEXT: [[H:%.*]] = sitofp i32 [[X:%.*]] to half
@ -307,9 +322,12 @@ define i12 @s32_half_u12(i32 %x) {
ret i12 %r
}
; TODO: This is safe to convert to trunc.
define i12 @u32_half_s12(i32 %x) {
; CHECK-LABEL: @u32_half_s12(
; CHECK-NEXT: [[R:%.*]] = trunc i32 [[X:%.*]] to i12
; CHECK-NEXT: [[H:%.*]] = uitofp i32 [[X:%.*]] to half
; CHECK-NEXT: [[R:%.*]] = fptosi half [[H]] to i12
; CHECK-NEXT: ret i12 [[R]]
;
%h = uitofp i32 %x to half
@ -317,6 +335,8 @@ define i12 @u32_half_s12(i32 %x) {
ret i12 %r
}
; Too many bits in output to ensure exact cast.
define i12 @u32_half_u12(i32 %x) {
; CHECK-LABEL: @u32_half_u12(
; CHECK-NEXT: [[H:%.*]] = uitofp i32 [[X:%.*]] to half