
Previously `#pragma STDC FENV_ACCESS ON` always set dynamic rounding mode and strict exception handling. It is not correct in the presence of other pragmas that also modify rounding mode and exception handling. For example, the effect of previous pragma FENV_ROUND could be cancelled, which is not conformant with the C standard. Also `#pragma STDC FENV_ACCESS OFF` turned off only FEnvAccess flag, leaving rounding mode and exception handling unchanged, which is incorrect in general case. Concrete rounding and exception mode depend on a combination of several factors like various pragmas and command-line options. During the review of this patch an idea was proposed that the semantic actions associated with such pragmas should only set appropriate flags. Actual rounding mode and exception handling should be calculated taking into account the state of all relevant options. In such implementation the pragma FENV_ACCESS should not override properties set by other pragmas but should set them if such setting is absent. To implement this approach the following main changes are made: - Field `FPRoundingMode` is removed from `LangOptions`. Actually there are no options that set it to arbitrary rounding mode, the choice was only `dynamic` or `tonearest`. Instead, a new boolean flag `RoundingMath` is added, with the same meaning as the corresponding command-line option. - Type `FPExceptionModeKind` now has possible value `FPE_Default`. It does not represent any particular exception mode but indicates that such mode was not set and default value should be used. It allows to distinguish the case: { #pragma STDC FENV_ACCESS ON ... } where the pragma must set FPE_Strict, from the case: { #pragma clang fp exceptions(ignore) #pragma STDC FENV_ACCESS ON ... } where exception mode should remain `FPE_Ignore`. - Class `FPOptions` has now methods `getRoundingMode` and `getExceptionMode`, which calculates the respective properties from other specified FP properties. - Class `LangOptions` has now methods `getDefaultRoundingMode` and `getDefaultExceptionMode`, which calculates default modes from the specified options and should be used instead of `getRoundingMode` and `getFPExceptionMode` of the same class. Differential Revision: https://reviews.llvm.org/D126364
227 lines
8.6 KiB
C
227 lines
8.6 KiB
C
// RUN: %clang_cc1 -fexperimental-strict-floating-point -ffp-exception-behavior=strict -triple %itanium_abi_triple -emit-llvm %s -o - | FileCheck --check-prefixes=CHECK,STRICT %s
|
|
// RUN: %clang_cc1 -fexperimental-strict-floating-point -ffp-exception-behavior=strict -triple %itanium_abi_triple -emit-llvm %s -o - -fms-extensions -DMS | FileCheck --check-prefixes=CHECK,STRICT %s
|
|
// RUN: %clang_cc1 -fexperimental-strict-floating-point -triple %itanium_abi_triple -emit-llvm %s -o - | FileCheck --check-prefixes=CHECK,DEFAULT %s
|
|
|
|
|
|
float func_00(float x, float y) {
|
|
return x + y;
|
|
}
|
|
// CHECK-LABEL: @func_00
|
|
// STRICT: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict")
|
|
// DEFAULT: fadd float
|
|
|
|
|
|
#ifdef MS
|
|
#pragma fenv_access (on)
|
|
#else
|
|
#pragma STDC FENV_ACCESS ON
|
|
#endif
|
|
|
|
float func_01(float x, float y) {
|
|
return x + y;
|
|
}
|
|
// CHECK-LABEL: @func_01
|
|
// CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict")
|
|
|
|
|
|
float func_02(float x, float y) {
|
|
#pragma float_control(except, off)
|
|
#pragma STDC FENV_ACCESS OFF
|
|
return x + y;
|
|
}
|
|
// CHECK-LABEL: @func_02
|
|
// CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore")
|
|
|
|
|
|
float func_03(float x, float y) {
|
|
return x + y;
|
|
}
|
|
// CHECK-LABEL: @func_03
|
|
// CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict")
|
|
|
|
|
|
#ifdef MS
|
|
#pragma fenv_access (off)
|
|
#else
|
|
#pragma STDC FENV_ACCESS OFF
|
|
#endif
|
|
|
|
float func_04(float x, float y) {
|
|
#pragma float_control(except, off)
|
|
return x + y;
|
|
}
|
|
// CHECK-LABEL: @func_04
|
|
// STRICT: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore")
|
|
// DEFAULT: fadd float
|
|
|
|
|
|
float func_04a(float x, float y) {
|
|
#pragma float_control(except, on)
|
|
return x + y;
|
|
}
|
|
// CHECK-LABEL: @func_04a
|
|
// CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict")
|
|
|
|
|
|
float func_05(float x, float y) {
|
|
#pragma STDC FENV_ACCESS ON
|
|
return x + y;
|
|
}
|
|
// CHECK-LABEL: @func_05
|
|
// CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict")
|
|
|
|
|
|
float func_06(float x, float y) {
|
|
#pragma float_control(except, off)
|
|
return x + y;
|
|
}
|
|
// CHECK-LABEL: @func_06
|
|
// STRICT: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore")
|
|
// DEFAULT: fadd float
|
|
|
|
|
|
float func_07(float x, float y) {
|
|
x -= y;
|
|
if (x) {
|
|
#pragma STDC FENV_ACCESS ON
|
|
y *= 2.0F;
|
|
}
|
|
return y + 4.0F;
|
|
}
|
|
// CHECK-LABEL: @func_07
|
|
// STRICT: call float @llvm.experimental.constrained.fsub.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict")
|
|
// STRICT: call float @llvm.experimental.constrained.fmul.f32(float {{.*}}, float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict")
|
|
// STRICT: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict")
|
|
// DEFAULT: call float @llvm.experimental.constrained.fsub.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore")
|
|
// DEFAULT: call float @llvm.experimental.constrained.fmul.f32(float {{.*}}, float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict")
|
|
// DEFAULT: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore")
|
|
|
|
|
|
float func_08(float x, float y) {
|
|
#pragma STDC FENV_ROUND FE_UPWARD
|
|
#pragma STDC FENV_ACCESS ON
|
|
return x + y;
|
|
}
|
|
// CHECK-LABEL: @func_08
|
|
// CHECK: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.upward", metadata !"fpexcept.strict")
|
|
|
|
|
|
float func_09(float x, float y) {
|
|
#pragma STDC FENV_ROUND FE_TONEAREST
|
|
#pragma STDC FENV_ACCESS ON
|
|
return x + y;
|
|
}
|
|
// CHECK-LABEL: @func_09
|
|
// CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict")
|
|
|
|
|
|
float func_10(float x, float y) {
|
|
#pragma STDC FENV_ROUND FE_TONEAREST
|
|
#pragma clang fp exceptions(ignore)
|
|
#pragma STDC FENV_ACCESS ON
|
|
return x + y;
|
|
}
|
|
// CHECK-LABEL: @func_10
|
|
// CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore")
|
|
|
|
|
|
float func_11(float x, float y) {
|
|
#pragma STDC FENV_ROUND FE_TONEAREST
|
|
#pragma clang fp exceptions(ignore)
|
|
#pragma STDC FENV_ACCESS OFF
|
|
return x + y;
|
|
}
|
|
// CHECK-LABEL: @func_11
|
|
// STRICT: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore")
|
|
// DEFAULT: fadd float
|
|
|
|
|
|
float func_12(float x, float y) {
|
|
#pragma clang fp exceptions(maytrap)
|
|
#pragma STDC FENV_ACCESS ON
|
|
return x + y;
|
|
}
|
|
// CHECK-LABEL: @func_12
|
|
// CHECK: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.maytrap")
|
|
|
|
|
|
float func_13(float x, float y) {
|
|
#pragma clang fp exceptions(maytrap)
|
|
#pragma STDC FENV_ROUND FE_UPWARD
|
|
#pragma STDC FENV_ACCESS ON
|
|
return x + y;
|
|
}
|
|
// CHECK-LABEL: @func_13
|
|
// CHECK: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.upward", metadata !"fpexcept.maytrap")
|
|
|
|
|
|
float func_14(float x, float y, float z) {
|
|
#pragma STDC FENV_ACCESS ON
|
|
float res = x * y;
|
|
{
|
|
#pragma STDC FENV_ACCESS OFF
|
|
return res + z;
|
|
}
|
|
}
|
|
// CHECK-LABEL: @func_14
|
|
// STRICT: call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict")
|
|
// STRICT: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict")
|
|
// DEFAULT: call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict")
|
|
// DEFAULT: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore")
|
|
|
|
|
|
float func_15(float x, float y, float z) {
|
|
#pragma STDC FENV_ROUND FE_TOWARDZERO
|
|
#pragma STDC FENV_ACCESS ON
|
|
float res = x * y;
|
|
{
|
|
#pragma STDC FENV_ACCESS OFF
|
|
return res + z;
|
|
}
|
|
}
|
|
// CHECK-LABEL: @func_15
|
|
// STRICT: call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.towardzero", metadata !"fpexcept.strict")
|
|
// STRICT: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.towardzero", metadata !"fpexcept.strict")
|
|
// DEFAULT: call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.towardzero", metadata !"fpexcept.strict")
|
|
// DEFAULT: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.towardzero", metadata !"fpexcept.ignore")
|
|
|
|
|
|
float func_16(float x, float y) {
|
|
x -= y;
|
|
{
|
|
#pragma STDC FENV_ROUND FE_TONEAREST
|
|
#pragma STDC FENV_ACCESS ON
|
|
y *= 2.0F;
|
|
}
|
|
{
|
|
#pragma STDC FENV_ACCESS ON
|
|
return y + 4.0F;
|
|
}
|
|
}
|
|
// CHECK-LABEL: @func_16
|
|
// STRICT: call float @llvm.experimental.constrained.fsub.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict")
|
|
// STRICT: call float @llvm.experimental.constrained.fmul.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict")
|
|
// STRICT: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict")
|
|
// DEFAULT: call float @llvm.experimental.constrained.fsub.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore")
|
|
// DEFAULT: call float @llvm.experimental.constrained.fmul.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict")
|
|
// DEFAULT: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict")
|
|
|
|
|
|
float func_17(float x, float y) {
|
|
#pragma STDC FENV_ROUND FE_DYNAMIC
|
|
#pragma STDC FENV_ACCESS ON
|
|
return x + y;
|
|
}
|
|
// CHECK-LABEL: @func_17
|
|
// CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict")
|
|
|
|
|
|
float func_18(float x, float y) {
|
|
#pragma STDC FENV_ROUND FE_DYNAMIC
|
|
return x + y;
|
|
}
|
|
// CHECK-LABEL: @func_18
|
|
// STRICT: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict")
|
|
// DEFAULT: fadd float
|
|
|