forked from OSchip/llvm-project
[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:
parent
6e345426de
commit
8650f05c97
|
@ -1977,7 +1977,7 @@ Instruction *InstCombinerImpl::foldItoFPtoI(CastInst &FI) {
|
||||||
// If the destination type is narrow, that means the intermediate FP value
|
// If the destination type is narrow, that means the intermediate FP value
|
||||||
// must be large enough to hold the source value exactly.
|
// must be large enough to hold the source value exactly.
|
||||||
// For example, (uint8_t)((float)(uint32_t 16777217) is undefined behavior.
|
// 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())
|
if (OutputSize > OpI->getType()->getFPMantissaWidth())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -190,11 +190,13 @@ define i32 @test17(i26 %A) {
|
||||||
ret i32 %C
|
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) {
|
define i54 @test18(i64 %A) {
|
||||||
; CHECK-LABEL: @test18(
|
; 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]]
|
; CHECK-NEXT: ret i54 [[C]]
|
||||||
;
|
;
|
||||||
%B = sitofp i64 %A to double
|
%B = sitofp i64 %A to double
|
||||||
|
@ -246,6 +248,8 @@ define i25 @low_masked_input(i25 %A) {
|
||||||
ret i25 %C
|
ret i25 %C
|
||||||
}
|
}
|
||||||
|
|
||||||
|
; Output is small enough to ensure exact cast (overflow produces poison).
|
||||||
|
|
||||||
define i11 @s32_half_s11(i32 %x) {
|
define i11 @s32_half_s11(i32 %x) {
|
||||||
; CHECK-LABEL: @s32_half_s11(
|
; CHECK-LABEL: @s32_half_s11(
|
||||||
; CHECK-NEXT: [[R:%.*]] = trunc i32 [[X:%.*]] to i11
|
; CHECK-NEXT: [[R:%.*]] = trunc i32 [[X:%.*]] to i11
|
||||||
|
@ -256,6 +260,8 @@ define i11 @s32_half_s11(i32 %x) {
|
||||||
ret i11 %r
|
ret i11 %r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
; Output is small enough to ensure exact cast (overflow produces poison).
|
||||||
|
|
||||||
define i11 @s32_half_u11(i32 %x) {
|
define i11 @s32_half_u11(i32 %x) {
|
||||||
; CHECK-LABEL: @s32_half_u11(
|
; CHECK-LABEL: @s32_half_u11(
|
||||||
; CHECK-NEXT: [[R:%.*]] = trunc i32 [[X:%.*]] to i11
|
; CHECK-NEXT: [[R:%.*]] = trunc i32 [[X:%.*]] to i11
|
||||||
|
@ -266,6 +272,8 @@ define i11 @s32_half_u11(i32 %x) {
|
||||||
ret i11 %r
|
ret i11 %r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
; Output is small enough to ensure exact cast (overflow produces poison).
|
||||||
|
|
||||||
define i11 @u32_half_s11(i32 %x) {
|
define i11 @u32_half_s11(i32 %x) {
|
||||||
; CHECK-LABEL: @u32_half_s11(
|
; CHECK-LABEL: @u32_half_s11(
|
||||||
; CHECK-NEXT: [[R:%.*]] = trunc i32 [[X:%.*]] to i11
|
; CHECK-NEXT: [[R:%.*]] = trunc i32 [[X:%.*]] to i11
|
||||||
|
@ -276,6 +284,8 @@ define i11 @u32_half_s11(i32 %x) {
|
||||||
ret i11 %r
|
ret i11 %r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
; Output is small enough to ensure exact cast (overflow produces poison).
|
||||||
|
|
||||||
define i11 @u32_half_u11(i32 %x) {
|
define i11 @u32_half_u11(i32 %x) {
|
||||||
; CHECK-LABEL: @u32_half_u11(
|
; CHECK-LABEL: @u32_half_u11(
|
||||||
; CHECK-NEXT: [[R:%.*]] = trunc i32 [[X:%.*]] to i11
|
; CHECK-NEXT: [[R:%.*]] = trunc i32 [[X:%.*]] to i11
|
||||||
|
@ -286,9 +296,12 @@ define i11 @u32_half_u11(i32 %x) {
|
||||||
ret i11 %r
|
ret i11 %r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
; Too many bits in output to ensure exact cast.
|
||||||
|
|
||||||
define i12 @s32_half_s12(i32 %x) {
|
define i12 @s32_half_s12(i32 %x) {
|
||||||
; CHECK-LABEL: @s32_half_s12(
|
; 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]]
|
; CHECK-NEXT: ret i12 [[R]]
|
||||||
;
|
;
|
||||||
%h = sitofp i32 %x to half
|
%h = sitofp i32 %x to half
|
||||||
|
@ -296,6 +309,8 @@ define i12 @s32_half_s12(i32 %x) {
|
||||||
ret i12 %r
|
ret i12 %r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
; Too many bits in output to ensure exact cast.
|
||||||
|
|
||||||
define i12 @s32_half_u12(i32 %x) {
|
define i12 @s32_half_u12(i32 %x) {
|
||||||
; CHECK-LABEL: @s32_half_u12(
|
; CHECK-LABEL: @s32_half_u12(
|
||||||
; CHECK-NEXT: [[H:%.*]] = sitofp i32 [[X:%.*]] to half
|
; CHECK-NEXT: [[H:%.*]] = sitofp i32 [[X:%.*]] to half
|
||||||
|
@ -307,9 +322,12 @@ define i12 @s32_half_u12(i32 %x) {
|
||||||
ret i12 %r
|
ret i12 %r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
; TODO: This is safe to convert to trunc.
|
||||||
|
|
||||||
define i12 @u32_half_s12(i32 %x) {
|
define i12 @u32_half_s12(i32 %x) {
|
||||||
; CHECK-LABEL: @u32_half_s12(
|
; 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]]
|
; CHECK-NEXT: ret i12 [[R]]
|
||||||
;
|
;
|
||||||
%h = uitofp i32 %x to half
|
%h = uitofp i32 %x to half
|
||||||
|
@ -317,6 +335,8 @@ define i12 @u32_half_s12(i32 %x) {
|
||||||
ret i12 %r
|
ret i12 %r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
; Too many bits in output to ensure exact cast.
|
||||||
|
|
||||||
define i12 @u32_half_u12(i32 %x) {
|
define i12 @u32_half_u12(i32 %x) {
|
||||||
; CHECK-LABEL: @u32_half_u12(
|
; CHECK-LABEL: @u32_half_u12(
|
||||||
; CHECK-NEXT: [[H:%.*]] = uitofp i32 [[X:%.*]] to half
|
; CHECK-NEXT: [[H:%.*]] = uitofp i32 [[X:%.*]] to half
|
||||||
|
|
Loading…
Reference in New Issue