llvm/clang/test/CodeGen/fp-floatcontrol-pragma.cpp

// RUN: %clang_cc1 -fexperimental-strict-floating-point -DEXCEPT=1 \
// RUN: -fcxx-exceptions -triple x86_64-linux-gnu -emit-llvm -o - %s \
// RUN: | FileCheck -check-prefix=CHECK-NS %s

// RUN: %clang_cc1 -fexperimental-strict-floating-point \
// RUN: -triple x86_64-linux-gnu -emit-llvm -o - %s | FileCheck %s \
// RUN: -check-prefixes=CHECK-DEFAULT,CHECK-CONST-ARGS

// RUN: %clang_cc1 -fexperimental-strict-floating-point -DFENV_ON=1 \
// RUN: -triple x86_64-linux-gnu -emit-llvm -o - %s \
// RUN: | FileCheck -check-prefix=CHECK-FENV %s

// RUN: %clang_cc1 -fexperimental-strict-floating-point -DNF128 \
// RUN: -triple %itanium_abi_triple -O3 -emit-llvm -o - %s \
// RUN: | FileCheck -check-prefix=CHECK-O3 %s

// RUN: %clang_cc1 -fexperimental-strict-floating-point \
// RUN: -triple x86_64-linux-gnu -emit-llvm -o - %s -ffp-eval-method=source \
// RUN: | FileCheck %s -check-prefixes=CHECK-SOURCE,CHECK-CONST-ARGS

// RUN: %clang_cc1 -fexperimental-strict-floating-point \
// RUN: -triple x86_64-linux-gnu -emit-llvm -o - %s -ffp-eval-method=double \
// RUN: | FileCheck %s -check-prefixes=CHECK-DOUBLE,CHECK-CONST-ARGS

// RUN: %clang_cc1 -fexperimental-strict-floating-point \
// RUN: -triple x86_64-linux-gnu -emit-llvm -o - %s -ffp-eval-method=extended \
// RUN: -mlong-double-80 | FileCheck %s \
// RUN: -check-prefixes=CHECK-EXTENDED,CHECK-CONST-ARGS

// RUN: %clang_cc1 -fexperimental-strict-floating-point \
// RUN: -triple i386-linux-gnu -emit-llvm -o - %s -ffp-eval-method=source \
// RUN: | FileCheck %s -check-prefix=CHECK-SOURCE

// RUN: %clang_cc1 -fexperimental-strict-floating-point -triple i386-linux-gnu \
// RUN: -emit-llvm -o - %s -ffp-eval-method=double | FileCheck %s \
// RUN: -check-prefix=CHECK-DOUBLE

// RUN: %clang_cc1 -fexperimental-strict-floating-point -triple i386-linux-gnu \
// RUN: -emit-llvm -o - %s -ffp-eval-method=extended -mlong-double-80 \
// RUN: | FileCheck %s -check-prefix=CHECK-EXTENDED

// RUN: %clang_cc1 -triple powerpc-unknown-aix -DNF128 -emit-llvm -o - %s \
// RUN: | FileCheck %s -check-prefix=CHECK-AIX

bool f() {
  // CHECK: define {{.*}}f{{.*}}
  return __FLT_EVAL_METHOD__ < 0 &&
         __FLT_EVAL_METHOD__ == -1;
  // CHECK: ret {{.*}} true
}

// Verify float_control(precise, off) enables fast math flags on fp operations.
float fp_precise_1(float a, float b, float c) {
// CHECK-O3: _Z12fp_precise_1fff
// CHECK-O3: %[[M:.+]] = fmul fast float{{.*}}
// CHECK-O3: fadd fast float %[[M]], %c
#pragma float_control(precise, off)
  return a * b + c;
}

// Is float_control state cleared on exiting compound statements?
float fp_precise_2(float a, float b, float c) {
  // CHECK-O3: _Z12fp_precise_2fff
  // CHECK-O3: %[[M:.+]] = fmul float{{.*}}
  // CHECK-O3: fadd float %[[M]], %c
  {
#pragma float_control(precise, off)
  }
  return a * b + c;
}

// Does float_control survive template instantiation?
class Foo {};
Foo operator+(Foo, Foo);

template <typename T>
T template_muladd(T a, T b, T c) {
#pragma float_control(precise, off)
  return a * b + c;
}

float fp_precise_3(float a, float b, float c) {
  // CHECK-O3: _Z12fp_precise_3fff
  // CHECK-O3: %[[M:.+]] = fmul fast float{{.*}}
  // CHECK-O3: fadd fast float %[[M]], %c
  return template_muladd<float>(a, b, c);
}

template <typename T>
class fp_precise_4 {
  float method(float a, float b, float c) {
#pragma float_control(precise, off)
    return a * b + c;
  }
};

template class fp_precise_4<int>;
// CHECK-O3: _ZN12fp_precise_4IiE6methodEfff
// CHECK-O3: %[[M:.+]] = fmul fast float{{.*}}
// CHECK-O3: fadd fast float %[[M]], %c

// Check file-scoped float_control
#pragma float_control(push)
#pragma float_control(precise, off)
float fp_precise_5(float a, float b, float c) {
  // CHECK-O3: _Z12fp_precise_5fff
  // CHECK-O3: %[[M:.+]] = fmul fast float{{.*}}
  // CHECK-O3: fadd fast float %[[M]], %c
  return a * b + c;
}
#pragma float_control(pop)

float fff(float x, float y) {
// CHECK-LABEL: define{{.*}} float @_Z3fffff{{.*}}
// CHECK: entry
#pragma float_control(except, on)
  float z;
  z = z * z;
  //CHECK: llvm.experimental.constrained.fmul{{.*}}
  {
    z = x * y;
    //CHECK: llvm.experimental.constrained.fmul{{.*}}
  }
  {
// This pragma has no effect since if there are any fp intrin in the
// function then all the operations need to be fp intrin
#pragma float_control(except, off)
    z = z + x * y;
    //CHECK: llvm.experimental.constrained.fmul{{.*}}
  }
  z = z * z;
  //CHECK: llvm.experimental.constrained.fmul{{.*}}
  return z;
}
float check_precise(float x, float y) {
  // CHECK-LABEL: define{{.*}} float @_Z13check_preciseff{{.*}}
  float z;
  {
#pragma float_control(precise, on)
    z = x * y + z;
    //CHECK: llvm.fmuladd{{.*}}
  }
  {
#pragma float_control(precise, off)
    z = x * y + z;
    //CHECK: fmul fast float
    //CHECK: fadd fast float
  }
  return z;
}

float fma_test2(float a, float b, float c) {
// CHECK-LABEL define{{.*}} float @_Z9fma_test2fff{{.*}}
#pragma float_control(precise, off)
  float x = a * b + c;
  //CHECK: fmuladd
  return x;
}

float fma_test1(float a, float b, float c) {
// CHECK-LABEL define{{.*}} float @_Z9fma_test1fff{{.*}}
#pragma float_control(precise, on)
  float x = a * b + c;
  //CHECK: fmuladd
  return x;
}

#pragma float_control(push)
#pragma float_control(precise, on)
struct Distance {};
Distance operator+(Distance, Distance);

template <class T>
T add(T lhs, T rhs) {
#pragma float_control(except, on)
  return lhs + rhs;
}
#pragma float_control(pop)

float test_OperatorCall() {
  return add(1.0f, 2.0f);
  //CHECK: llvm.experimental.constrained.fadd{{.*}}fpexcept.strict
}
// CHECK-LABEL define{{.*}} float  {{.*}}test_OperatorCall{{.*}}

#if FENV_ON
#pragma STDC FENV_ACCESS ON
#endif
// CHECK-LABEL: define {{.*}}callt{{.*}}

void callt() {
  volatile float z;
  z = z * z;
  //CHECK-FENV: llvm.experimental.constrained.fmul{{.*}}
}

// CHECK-LABEL: define {{.*}}myAdd{{.*}}
float myAdd(int i, float f) {
  if (i<0)
  return 1.0 + 2.0;
  // Check that floating point constant folding doesn't occur if
  // #pragma STC FENV_ACCESS is enabled.
  //CHECK-FENV: llvm.experimental.constrained.fadd{{.*}}double 1.0{{.*}}double 2.0{{.*}}
  //CHECK: store float 3.0{{.*}}retval{{.*}}
  static double v = 1.0 / 3.0;
  //CHECK-FENV: llvm.experimental.constrained.fptrunc.f32.f64{{.*}}
  //CHECK-NOT: fdiv
  return v;
}

#if EXCEPT
namespace ns {
// Check that pragma float_control can appear in namespace.
#pragma float_control(except, on, push)
float exc_on(double x, float zero) {
// CHECK-NS: define {{.*}}exc_on{{.*}}
  {} try {
    x = 1.0 / zero; /* division by zero, the result unused */
//CHECK-NS: llvm.experimental.constrained.fdiv{{.*}}
  } catch (...) {}
  return zero;
}
}

// Check pragma is still effective after namespace closes
float exc_still_on(double x, float zero) {
// CHECK-NS: define {{.*}}exc_still_on{{.*}}
  {} try {
    x = 1.0 / zero; /* division by zero, the result unused */
//CHECK-NS: llvm.experimental.constrained.fdiv{{.*}}
  } catch (...) {}
  return zero;
}

#pragma float_control(pop)
float exc_off(double x, float zero) {
// CHECK-NS: define {{.*}}exc_off{{.*}}
  {} try {
    x = 1.0 / zero; /* division by zero, the result unused */
//CHECK-NS: fdiv double
  } catch (...) {}
  return zero;
}

namespace fc_template_namespace {
#pragma float_control(except, on, push)
template <class T>
T exc_on(double x, T zero) {
// CHECK-NS: define {{.*}}fc_template_namespace{{.*}}
  {} try {
    x = 1.0 / zero; /* division by zero, the result unused */
//CHECK-NS: llvm.experimental.constrained.fdiv{{.*}}
  } catch (...) {}
  return zero;
}
}

#pragma float_control(pop)
float xx(double x, float z) {
  return fc_template_namespace::exc_on<float>(x, z);
}
#endif // EXCEPT

float try_lam(float x, unsigned n) {
// CHECK: define {{.*}}try_lam{{.*}}class.anon{{.*}}
  float result;
  auto t =
        // Lambda expression begins
        [](float a, float b) {
#pragma float_control( except, on)
            return a * b;
//CHECK: llvm.experimental.constrained.fmul{{.*}}fpexcept.strict
        } // end of lambda expression
  (1.0f,2.0f);
  result = x + t;
  return result;
}

float mySub(float x, float y) {
  // CHECK: define {{.*}}float {{.*}}mySub{{.*}}
  // CHECK-NS: fsub float
  // CHECK-SOURCE: fsub float
  // CHECK-DOUBLE: fpext float
  // CHECK-DOUBLE: fpext float
  // CHECK-DOUBLE: fsub double
  // CHECK-DOUBLE: fptrunc double {{.*}} to float
  // CHECK-EXTENDED: fpext float
  // CHECK-EXTENDED: fpext float
  // CHECK-EXTENDED: fsub double
  // CHECK-EXTENDED: fptrunc double {{.*}} to float
  return x - y;
}

float mySubSource(float x, float y) {
// CHECK: define {{.*}}float {{.*}}mySubSource{{.*}}
#pragma clang fp eval_method(source)
  return x - y;
  // CHECK: fsub float
}

float mySubExtended(float x, float y) {
// CHECK: define {{.*}}float {{.*}}mySubExtended{{.*}}
#pragma clang fp eval_method(extended)
  return x - y;
  // CHECK: fpext float
  // CHECK: fpext float
  // CHECK: fsub x86_fp80
  // CHECK: fptrunc x86_fp80 {{.*}} to float
  // CHECK-AIX: fsub double
  // CHECK-AIX: fptrunc double
}

float mySubDouble(float x, float y) {
// CHECK: define {{.*}}float {{.*}}mySubDouble{{.*}}
#pragma clang fp eval_method(double)
  return x - y;
  // CHECK: fpext float
  // CHECK: fpext float
  // CHECK: fsub double
  // CHECK: fptrunc double {{.*}} to float
}

#ifndef NF128
__float128 mySub128(__float128 x, __float128 y) {
  // CHECK: define {{.*}}mySub128{{.*}}
  // Expect no fpext since fp128 is already widest
  // CHECK: load fp128
  // CHECK-NEXT: load fp128
  // CHECK-NEXT: fsub fp128
  // CHECK-NEXT: ret fp128
  return x - y;
}
#endif

void mySubfp16(__fp16 *res, __fp16 *x, __fp16 *y) {
  // CHECK: define {{.*}}mySubfp16{{.*}}
  *res = *x - *y;
  // CHECK: load half
  // CHECK-NEXT: load half
  // CHECK-NEXT: fpext half{{.*}}
  // CHECK-NEXT: load half
  // CHECK-NEXT: load half
  // CHECK-NS: fpext half{{.*}} to float
  // CHECK-DEFAULT: fpext half{{.*}} to float
  // CHECK-DOUBLE: fpext half{{.*}} to float
  // CHECK-EXTENDED: fpext half{{.*}} to float
  // CHECK-NEXT: fsub
  // CHECK-NEXT: fptrunc {{.*}}to half
  // CHECK-NS: fptrunc float {{.*}} to half
  // CHECK-DOUBLE: fptrunc float {{.*}} to half
  // CHECK-EXTENDED: fptrunc float {{.*}} to half
}

float Div(float x, float y, float z) {
  // CHECK: define{{.*}}float {{.*}}Div{{.*}}
  // CHECK-CONST-ARGS: fdiv float
  return x / (y / z);
}

float DivExtended(float x, float y, float z) {
// CHECK: define{{.*}}float {{.*}}DivExtended{{.*}}
#pragma clang fp eval_method(extended)
  // CHECK-CONST-ARGS: fdiv x86_fp80
  // CHECK-CONST-ARGS: fptrunc x86_fp80
  return x / (y / z);
}

float DivDouble(float x, float y, float z) {
// CHECK: define{{.*}}float {{.*}}DivDouble{{.*}}
#pragma clang fp eval_method(double)
  // CHECK-CONST-ARGS: fdiv double
  // CHECK-CONST-ARGS: fptrunc double
  return x / (y / z);
}

float DivSource(float x, float y, float z) {
// CHECK: define{{.*}}float {{.*}}DivSource{{.*}}
#pragma clang fp eval_method(source)
  // CHECK-CONST-ARGS: fdiv float
  return x / (y / z);
}

int main() {
  float f = Div(4.2f, 1.0f, 3.0f);
  float fextended = DivExtended(4.2f, 1.0f, 3.0f);
  float fdouble = DivDouble(4.2f, 1.0f, 3.0f);
  float fsource = DivSource(4.2f, 1.0f, 3.0f);
  // CHECK: store float
}