How CLANG decides a floating point constant should be float or double?

Hi,

I am checking the possibility about removing the fpexp and fptrunc in the below example:

*** IR Dump After InstCombinePass on _Z4testf ***
define dso_local noundef float @_Z4testf(float noundef %a) local_unnamed_addr #0 {
entry:
  %conv = fpext float %a to double
  %call = call contract double @sqrt(double noundef %conv) #3
  %div = fdiv contract double 1.000000e+00, %call
  %conv1 = fptrunc double %div to float
  %mul = fmul contract float %conv1, %a
  %call2 = call contract noundef float @_Z3fooff(float noundef %mul, float noundef %conv1)
  ret float %call2
}

(Coming from below source code and compiling with clang -O3 -S -emit-llvm t.cpp)

#include<cmath>
extern float foo(float, float);
float test(float a)
{
  float f1 = 1.0f / sqrt(a);
  float f2 = f1 * a;
  return foo(f2, f1);
}

I am thinking why the constant 1.0f is emitted as a double type in the CLANG . If the 1.0f is a float type like the case below, means there will be a fptrunc for the sqrt result, optimizeDoubleFP() can be able to convert the sqrt to sqrtf and then the fpext and fptrunc can be saved.

Case where CLANG emits the 1.0f as the float type, so the instcombine pass can remove the fpexp and fptrunc:

#include<cmath>
extern float foo(float, float);
float test(float a)
{
  float f1 = sqrt(a);
  float f2 = 1.0f / f1;
  return foo(f1, f2);
}
*** IR Dump After PromotePass on _Z4testf ***
define dso_local noundef float @_Z4testf(float noundef %a) local_unnamed_addr #0 {
entry:
  %conv = fpext float %a to double
  %call = call contract double @sqrt(double noundef %conv) #3
  %conv1 = fptrunc double %call to float
  %div = fdiv contract float 1.000000e+00, %conv1
  %call2 = call contract noundef float @_Z3fooff(float noundef %conv1, float noundef %div)
  ret float %call2
}
*** IR Dump After InstCombinePass on _Z4testf ***
define dso_local noundef float @_Z4testf(float noundef %a) local_unnamed_addr #0 {
entry:
  %sqrtf = call contract float @sqrtf(float noundef %a) #1
  %div = fdiv contract float 1.000000e+00, %sqrtf
  %call2 = call contract noundef float @_Z3fooff(float noundef %sqrtf, float noundef %div)
  ret float %call2
}

Thanks.

1.0f is promoted to double according to the usual arithmetic conversion rules.

fpext/fptrunc pair can only be removed (and sqrt replaced with sqrtf) if the compiler can prove that the result of sqrt is representable in float type.

One should just use the correct sqrt overload (std::sqrt in C++ or sqrt macro from <tgmath.h> in C).

1 Like

Thanks. I see what happens for these two 1.0f