Skip to content

Commit

Permalink
[DAGCombine] (float)((int) f) --> ftrunc (PR36617)
Browse files Browse the repository at this point in the history
This was originally committed at rL328921 and reverted at rL329920 to
investigate failures in Chrome. This time I've added to the ReleaseNotes
to warn users of the potential of exposing UB and let me repeat that
here for more exposure:

  Optimization of floating-point casts is improved. This may cause surprising
  results for code that is relying on undefined behavior. Code sanitizers can
  be used to detect affected patterns such as this:

    int main() {
      float x = 4294967296.0f;
      x = (float)((int)x);
      printf("junk in the ftrunc: %f\n", x);
      return 0;
    }

    $ clang -O1 ftrunc.c -fsanitize=undefined ; ./a.out
    ftrunc.c:5:15: runtime error: 4.29497e+09 is outside the range of 
                   representable values of type 'int'
    junk in the ftrunc: 0.000000


Original commit message:

fptosi / fptoui round towards zero, and that's the same behavior as ISD::FTRUNC,
so replace a pair of casts with the equivalent node. We don't have to account for
special cases (NaN, INF) because out-of-range casts are undefined.

Differential Revision: https://reviews.llvm.org/D44909

llvm-svn: 330437
  • Loading branch information
rotateright committed Apr 20, 2018
1 parent 863ffeb commit 3d453ad
Show file tree
Hide file tree
Showing 11 changed files with 123 additions and 331 deletions.
20 changes: 20 additions & 0 deletions llvm/docs/ReleaseNotes.rst
Expand Up @@ -61,6 +61,26 @@ Non-comprehensive list of changes in this release
* The optimization flag to merge constants (-fmerge-all-constants) is no longer
applied by default.

* Optimization of floating-point casts is improved. This may cause surprising
results for code that is relying on undefined behavior. Code sanitizers can
be used to detect affected patterns such as this:

.. code-block:: c
int main() {
float x = 4294967296.0f;
x = (float)((int)x);
printf("junk in the ftrunc: %f\n", x);
return 0;
}
.. code-block:: bash
clang -O1 ftrunc.c -fsanitize=undefined ; ./a.out
ftrunc.c:5:15: runtime error: 4.29497e+09 is outside the range of representable values of type 'int'
junk in the ftrunc: 0.000000
* Note..

.. NOTE
Expand Down
3 changes: 2 additions & 1 deletion llvm/include/llvm/CodeGen/ISDOpcodes.h
Expand Up @@ -495,7 +495,8 @@ namespace ISD {
ZERO_EXTEND_VECTOR_INREG,

/// FP_TO_[US]INT - Convert a floating point value to a signed or unsigned
/// integer.
/// integer. These have the same semantics as fptosi and fptoui in IR. If
/// the FP value cannot fit in the integer type, the results are undefined.
FP_TO_SINT,
FP_TO_UINT,

Expand Down
18 changes: 18 additions & 0 deletions llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
Expand Up @@ -10890,6 +10890,15 @@ SDValue DAGCombiner::visitSINT_TO_FP(SDNode *N) {
}
}

// fptosi rounds towards zero, so converting from FP to integer and back is
// the same as an 'ftrunc': sitofp (fptosi X) --> ftrunc X
// We only do this if the target has legal ftrunc, otherwise we'd likely be
// replacing casts with a libcall.
if (N0.getOpcode() == ISD::FP_TO_SINT &&
N0.getOperand(0).getValueType() == VT &&
TLI.isOperationLegal(ISD::FTRUNC, VT))
return DAG.getNode(ISD::FTRUNC, SDLoc(N), VT, N0.getOperand(0));

return SDValue();
}

Expand Down Expand Up @@ -10929,6 +10938,15 @@ SDValue DAGCombiner::visitUINT_TO_FP(SDNode *N) {
}
}

// fptoui rounds towards zero, so converting from FP to integer and back is
// the same as an 'ftrunc': uitofp (fptoui X) --> ftrunc X
// We only do this if the target has legal ftrunc, otherwise we'd likely be
// replacing casts with a libcall.
if (N0.getOpcode() == ISD::FP_TO_UINT &&
N0.getOperand(0).getValueType() == VT &&
TLI.isOperationLegal(ISD::FTRUNC, VT))
return DAG.getNode(ISD::FTRUNC, SDLoc(N), VT, N0.getOperand(0));

return SDValue();
}

Expand Down
12 changes: 4 additions & 8 deletions llvm/test/CodeGen/AArch64/ftrunc.ll
Expand Up @@ -4,8 +4,7 @@
define float @trunc_unsigned_f32(float %x) {
; CHECK-LABEL: trunc_unsigned_f32:
; CHECK: // %bb.0:
; CHECK-NEXT: fcvtzu w8, s0
; CHECK-NEXT: ucvtf s0, w8
; CHECK-NEXT: frintz s0, s0
; CHECK-NEXT: ret
%i = fptoui float %x to i32
%r = uitofp i32 %i to float
Expand All @@ -15,8 +14,7 @@ define float @trunc_unsigned_f32(float %x) {
define double @trunc_unsigned_f64(double %x) {
; CHECK-LABEL: trunc_unsigned_f64:
; CHECK: // %bb.0:
; CHECK-NEXT: fcvtzu x8, d0
; CHECK-NEXT: ucvtf d0, x8
; CHECK-NEXT: frintz d0, d0
; CHECK-NEXT: ret
%i = fptoui double %x to i64
%r = uitofp i64 %i to double
Expand All @@ -26,8 +24,7 @@ define double @trunc_unsigned_f64(double %x) {
define float @trunc_signed_f32(float %x) {
; CHECK-LABEL: trunc_signed_f32:
; CHECK: // %bb.0:
; CHECK-NEXT: fcvtzs w8, s0
; CHECK-NEXT: scvtf s0, w8
; CHECK-NEXT: frintz s0, s0
; CHECK-NEXT: ret
%i = fptosi float %x to i32
%r = sitofp i32 %i to float
Expand All @@ -37,8 +34,7 @@ define float @trunc_signed_f32(float %x) {
define double @trunc_signed_f64(double %x) {
; CHECK-LABEL: trunc_signed_f64:
; CHECK: // %bb.0:
; CHECK-NEXT: fcvtzs x8, d0
; CHECK-NEXT: scvtf d0, x8
; CHECK-NEXT: frintz d0, d0
; CHECK-NEXT: ret
%i = fptosi double %x to i64
%r = sitofp i64 %i to double
Expand Down
42 changes: 42 additions & 0 deletions llvm/test/CodeGen/ARM/ftrunc.ll
@@ -0,0 +1,42 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
; RUN: llc -mtriple=armv7-eabi < %s | FileCheck %s

define float @trunc_unsigned_f32(float %x) nounwind {
; CHECK-LABEL: trunc_unsigned_f32:
; CHECK: @ %bb.0:
; CHECK-NEXT: vmov s0, r0
; CHECK-NEXT: vcvt.u32.f32 s0, s0
; CHECK-NEXT: vcvt.f32.u32 s0, s0
; CHECK-NEXT: vmov r0, s0
; CHECK-NEXT: bx lr
%i = fptoui float %x to i32
%r = uitofp i32 %i to float
ret float %r
}

define double @trunc_unsigned_f64_i64(double %x) nounwind {
; CHECK-LABEL: trunc_unsigned_f64_i64:
; CHECK: @ %bb.0:
; CHECK-NEXT: .save {r11, lr}
; CHECK-NEXT: push {r11, lr}
; CHECK-NEXT: bl __aeabi_d2ulz
; CHECK-NEXT: bl __aeabi_ul2d
; CHECK-NEXT: pop {r11, pc}
%i = fptoui double %x to i64
%r = uitofp i64 %i to double
ret double %r
}

define double @trunc_unsigned_f64_i32(double %x) nounwind {
; CHECK-LABEL: trunc_unsigned_f64_i32:
; CHECK: @ %bb.0:
; CHECK-NEXT: vmov d16, r0, r1
; CHECK-NEXT: vcvt.u32.f64 s0, d16
; CHECK-NEXT: vcvt.f64.u32 d16, s0
; CHECK-NEXT: vmov r0, r1, d16
; CHECK-NEXT: bx lr
%i = fptoui double %x to i32
%r = uitofp i32 %i to double
ret double %r
}

13 changes: 1 addition & 12 deletions llvm/test/CodeGen/PowerPC/fp-int128-fp-combine.ll
Expand Up @@ -5,18 +5,7 @@
define float @f_i128_f(float %v) {
; CHECK-LABEL: f_i128_f:
; CHECK: # %bb.0: # %entry
; CHECK-NEXT: mflr 0
; CHECK-NEXT: std 0, 16(1)
; CHECK-NEXT: stdu 1, -32(1)
; CHECK-NEXT: .cfi_def_cfa_offset 32
; CHECK-NEXT: .cfi_offset lr, 16
; CHECK-NEXT: bl __fixsfti
; CHECK-NEXT: nop
; CHECK-NEXT: bl __floattisf
; CHECK-NEXT: nop
; CHECK-NEXT: addi 1, 1, 32
; CHECK-NEXT: ld 0, 16(1)
; CHECK-NEXT: mtlr 0
; CHECK-NEXT: friz 1, 1
; CHECK-NEXT: blr
entry:
%a = fptosi float %v to i128
Expand Down
12 changes: 4 additions & 8 deletions llvm/test/CodeGen/PowerPC/fp-to-int-to-fp.ll
Expand Up @@ -11,8 +11,7 @@ entry:
ret float %conv1

; FPCVT-LABEL: @fool
; FPCVT: fctidz [[REG1:[0-9]+]], 1
; FPCVT: fcfids 1, [[REG1]]
; FPCVT: friz 1, 1
; FPCVT: blr

; PPC64-LABEL: @fool
Expand All @@ -30,8 +29,7 @@ entry:
ret double %conv1

; FPCVT-LABEL: @foodl
; FPCVT: fctidz [[REG1:[0-9]+]], 1
; FPCVT: fcfid 1, [[REG1]]
; FPCVT: friz 1, 1
; FPCVT: blr

; PPC64-LABEL: @foodl
Expand All @@ -48,8 +46,7 @@ entry:
ret float %conv1

; FPCVT-LABEL: @fooul
; FPCVT: fctiduz [[REG1:[0-9]+]], 1
; FPCVT: fcfidus 1, [[REG1]]
; FPCVT: friz 1, 1
; FPCVT: blr
}

Expand All @@ -61,8 +58,7 @@ entry:
ret double %conv1

; FPCVT-LABEL: @fooudl
; FPCVT: fctiduz [[REG1:[0-9]+]], 1
; FPCVT: fcfidu 1, [[REG1]]
; FPCVT: friz 1, 1
; FPCVT: blr
}

Expand Down
12 changes: 4 additions & 8 deletions llvm/test/CodeGen/PowerPC/ftrunc-vec.ll
Expand Up @@ -4,8 +4,7 @@
define <4 x float> @truncf32(<4 x float> %a) {
; CHECK-LABEL: truncf32:
; CHECK: # %bb.0:
; CHECK-NEXT: xvcvspsxws 0, 34
; CHECK-NEXT: xvcvsxwsp 34, 0
; CHECK-NEXT: xvrspiz 34, 34
; CHECK-NEXT: blr
%t0 = fptosi <4 x float> %a to <4 x i32>
%t1 = sitofp <4 x i32> %t0 to <4 x float>
Expand All @@ -15,8 +14,7 @@ define <4 x float> @truncf32(<4 x float> %a) {
define <2 x double> @truncf64(<2 x double> %a) {
; CHECK-LABEL: truncf64:
; CHECK: # %bb.0:
; CHECK-NEXT: xvcvdpsxds 34, 34
; CHECK-NEXT: xvcvsxddp 34, 34
; CHECK-NEXT: xvrdpiz 34, 34
; CHECK-NEXT: blr
%t0 = fptosi <2 x double> %a to <2 x i64>
%t1 = sitofp <2 x i64> %t0 to <2 x double>
Expand All @@ -26,8 +24,7 @@ define <2 x double> @truncf64(<2 x double> %a) {
define <4 x float> @truncf32u(<4 x float> %a) {
; CHECK-LABEL: truncf32u:
; CHECK: # %bb.0:
; CHECK-NEXT: xvcvspuxws 0, 34
; CHECK-NEXT: xvcvuxwsp 34, 0
; CHECK-NEXT: xvrspiz 34, 34
; CHECK-NEXT: blr
%t0 = fptoui <4 x float> %a to <4 x i32>
%t1 = uitofp <4 x i32> %t0 to <4 x float>
Expand All @@ -37,8 +34,7 @@ define <4 x float> @truncf32u(<4 x float> %a) {
define <2 x double> @truncf64u(<2 x double> %a) {
; CHECK-LABEL: truncf64u:
; CHECK: # %bb.0:
; CHECK-NEXT: xvcvdpuxds 34, 34
; CHECK-NEXT: xvcvuxddp 34, 34
; CHECK-NEXT: xvrdpiz 34, 34
; CHECK-NEXT: blr
%t0 = fptoui <2 x double> %a to <2 x i64>
%t1 = uitofp <2 x i64> %t0 to <2 x double>
Expand Down
24 changes: 4 additions & 20 deletions llvm/test/CodeGen/PowerPC/no-extra-fp-conv-ldst.ll
Expand Up @@ -36,11 +36,7 @@ entry:
ret float %conv1

; CHECK-LABEL: @foo
; CHECK-DAG: fctiwz [[REG2:[0-9]+]], 1
; CHECK-DAG: addi [[REG1:[0-9]+]], 1,
; CHECK: stfiwx [[REG2]], 0, [[REG1]]
; CHECK: lfiwax [[REG3:[0-9]+]], 0, [[REG1]]
; CHECK: fcfids 1, [[REG3]]
; CHECK: friz 1, 1
; CHECK: blr
}

Expand All @@ -52,11 +48,7 @@ entry:
ret double %conv1

; CHECK-LABEL: @food
; CHECK-DAG: fctiwz [[REG2:[0-9]+]], 1
; CHECK-DAG: addi [[REG1:[0-9]+]], 1,
; CHECK: stfiwx [[REG2]], 0, [[REG1]]
; CHECK: lfiwax [[REG3:[0-9]+]], 0, [[REG1]]
; CHECK: fcfid 1, [[REG3]]
; CHECK: friz 1, 1
; CHECK: blr
}

Expand All @@ -68,11 +60,7 @@ entry:
ret float %conv1

; CHECK-LABEL: @foou
; CHECK-DAG: fctiwuz [[REG2:[0-9]+]], 1
; CHECK-DAG: addi [[REG1:[0-9]+]], 1,
; CHECK: stfiwx [[REG2]], 0, [[REG1]]
; CHECK: lfiwzx [[REG3:[0-9]+]], 0, [[REG1]]
; CHECK: fcfidus 1, [[REG3]]
; CHECK: friz 1, 1
; CHECK: blr
}

Expand All @@ -84,11 +72,7 @@ entry:
ret double %conv1

; CHECK-LABEL: @fooud
; CHECK-DAG: fctiwuz [[REG2:[0-9]+]], 1
; CHECK-DAG: addi [[REG1:[0-9]+]], 1,
; CHECK: stfiwx [[REG2]], 0, [[REG1]]
; CHECK: lfiwzx [[REG3:[0-9]+]], 0, [[REG1]]
; CHECK: fcfidu 1, [[REG3]]
; CHECK: friz 1, 1
; CHECK: blr
}

Expand Down
6 changes: 2 additions & 4 deletions llvm/test/CodeGen/X86/2011-10-19-widen_vselect.ll
Expand Up @@ -71,8 +71,7 @@ define void @full_test() {
; X32-NEXT: subl $60, %esp
; X32-NEXT: .cfi_def_cfa_offset 64
; X32-NEXT: movsd {{.*#+}} xmm2 = mem[0],zero
; X32-NEXT: cvttps2dq %xmm2, %xmm0
; X32-NEXT: cvtdq2ps %xmm0, %xmm1
; X32-NEXT: roundps $11, %xmm2, %xmm1
; X32-NEXT: xorps %xmm0, %xmm0
; X32-NEXT: cmpltps %xmm2, %xmm0
; X32-NEXT: movaps {{.*#+}} xmm3 = <1,1,u,u>
Expand All @@ -93,8 +92,7 @@ define void @full_test() {
; X64-LABEL: full_test:
; X64: # %bb.0: # %entry
; X64-NEXT: movsd {{.*#+}} xmm2 = mem[0],zero
; X64-NEXT: cvttps2dq %xmm2, %xmm0
; X64-NEXT: cvtdq2ps %xmm0, %xmm1
; X64-NEXT: roundps $11, %xmm2, %xmm1
; X64-NEXT: xorps %xmm0, %xmm0
; X64-NEXT: cmpltps %xmm2, %xmm0
; X64-NEXT: movaps {{.*#+}} xmm3 = <1,1,u,u>
Expand Down

0 comments on commit 3d453ad

Please sign in to comment.