Skip to content

Commit 92e0560

Browse files
authored
[flang] ieee_denorm (#132307)
Add support for the nonstandard ieee_denorm exception for real kinds 3, 4, 8 on x86 processors.
1 parent 7b3885d commit 92e0560

File tree

18 files changed

+405
-179
lines changed

18 files changed

+405
-179
lines changed

flang-rt/lib/runtime/exceptions.cpp

Lines changed: 60 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -76,22 +76,74 @@ uint32_t RTNAME(MapException)(uint32_t excepts) {
7676
return except_value;
7777
}
7878

79+
// The following exception processing routines have a libm call component,
80+
// and where available, an additional component for handling the nonstandard
81+
// ieee_denorm exception. The denorm component does not subsume the libm
82+
// component; both are needed.
83+
84+
void RTNAME(feclearexcept)(uint32_t excepts) {
85+
feclearexcept(excepts);
86+
#if defined(_MM_EXCEPT_DENORM)
87+
_mm_setcsr(_mm_getcsr() & ~(excepts & _MM_EXCEPT_MASK));
88+
#endif
89+
}
90+
void RTNAME(feraiseexcept)(uint32_t excepts) {
91+
feraiseexcept(excepts);
92+
#if defined(_MM_EXCEPT_DENORM)
93+
_mm_setcsr(_mm_getcsr() | (excepts & _MM_EXCEPT_MASK));
94+
#endif
95+
}
96+
uint32_t RTNAME(fetestexcept)(uint32_t excepts) {
97+
#if defined(_MM_EXCEPT_DENORM)
98+
return (_mm_getcsr() & _MM_EXCEPT_MASK & excepts) | fetestexcept(excepts);
99+
#else
100+
return fetestexcept(excepts);
101+
#endif
102+
}
103+
void RTNAME(fedisableexcept)(uint32_t excepts) {
104+
#ifdef __USE_GNU
105+
fedisableexcept(excepts);
106+
#endif
107+
#if defined(_MM_EXCEPT_DENORM)
108+
_mm_setcsr(_mm_getcsr() | ((excepts & _MM_EXCEPT_MASK) << 7));
109+
#endif
110+
}
111+
void RTNAME(feenableexcept)(uint32_t excepts) {
112+
#ifdef __USE_GNU
113+
feenableexcept(excepts);
114+
#endif
115+
#if defined(_MM_EXCEPT_DENORM)
116+
_mm_setcsr(_mm_getcsr() & ~((excepts & _MM_EXCEPT_MASK) << 7));
117+
#endif
118+
}
119+
uint32_t RTNAME(fegetexcept)() {
120+
uint32_t excepts = 0;
121+
#ifdef __USE_GNU
122+
excepts = fegetexcept();
123+
#endif
124+
#if defined(_MM_EXCEPT_DENORM)
125+
return (63 - ((_mm_getcsr() >> 7) & _MM_EXCEPT_MASK)) | excepts;
126+
#else
127+
return excepts;
128+
#endif
129+
}
130+
79131
// Check if the processor has the ability to control whether to halt or
80132
// continue execution when a given exception is raised.
81133
bool RTNAME(SupportHalting)([[maybe_unused]] uint32_t except) {
82134
#ifdef __USE_GNU
83135
except = RTNAME(MapException)(except);
84-
int currentSet = fegetexcept(), flipSet, ok;
136+
int currentSet = RTNAME(fegetexcept)(), flipSet;
85137
if (currentSet & except) {
86-
ok = fedisableexcept(except);
87-
flipSet = fegetexcept();
88-
ok |= feenableexcept(except);
138+
RTNAME(fedisableexcept)(except);
139+
flipSet = RTNAME(fegetexcept)();
140+
RTNAME(feenableexcept)(except);
89141
} else {
90-
ok = feenableexcept(except);
91-
flipSet = fegetexcept();
92-
ok |= fedisableexcept(except);
142+
RTNAME(feenableexcept)(except);
143+
flipSet = RTNAME(fegetexcept)();
144+
RTNAME(fedisableexcept)(except);
93145
}
94-
return ok != -1 && currentSet != flipSet;
146+
return currentSet != flipSet;
95147
#else
96148
return false;
97149
#endif

flang/include/flang/Evaluate/target.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,13 @@ class TargetCharacteristics {
5454
bool hasSubnormalFlushingControl(bool any = false) const;
5555
void set_hasSubnormalFlushingControl(int kind, bool yes = true);
5656

57+
// Check if a given real kind has support for raising a nonstandard
58+
// ieee_denorm exception.
59+
bool hasSubnormalExceptionSupport(int kind) const;
60+
// Check if all real kinds have support for the ieee_denorm exception.
61+
bool hasSubnormalExceptionSupport() const;
62+
void set_hasSubnormalExceptionSupport(int kind, bool yes = true);
63+
5764
Rounding roundingMode() const { return roundingMode_; }
5865
void set_roundingMode(Rounding);
5966

@@ -134,6 +141,7 @@ class TargetCharacteristics {
134141
bool haltingSupportIsUnknownAtCompileTime_{false};
135142
bool areSubnormalsFlushedToZero_{false};
136143
bool hasSubnormalFlushingControl_[maxKind + 1]{};
144+
bool hasSubnormalExceptionSupport_[maxKind + 1]{};
137145
Rounding roundingMode_{defaultRounding};
138146
std::size_t procedurePointerByteSize_{8};
139147
std::size_t procedurePointerAlignment_{8};

flang/include/flang/Optimizer/Builder/Runtime/Exceptions.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,23 @@ namespace fir::runtime {
2626
mlir::Value genMapExcept(fir::FirOpBuilder &builder, mlir::Location loc,
2727
mlir::Value excepts);
2828

29+
void genFeclearexcept(fir::FirOpBuilder &builder, mlir::Location loc,
30+
mlir::Value excepts);
31+
32+
void genFeraiseexcept(fir::FirOpBuilder &builder, mlir::Location loc,
33+
mlir::Value excepts);
34+
35+
mlir::Value genFetestexcept(fir::FirOpBuilder &builder, mlir::Location loc,
36+
mlir::Value excepts);
37+
38+
void genFedisableexcept(fir::FirOpBuilder &builder, mlir::Location loc,
39+
mlir::Value excepts);
40+
41+
void genFeenableexcept(fir::FirOpBuilder &builder, mlir::Location loc,
42+
mlir::Value excepts);
43+
44+
mlir::Value genFegetexcept(fir::FirOpBuilder &builder, mlir::Location loc);
45+
2946
mlir::Value genSupportHalting(fir::FirOpBuilder &builder, mlir::Location loc,
3047
mlir::Value excepts);
3148

flang/include/flang/Runtime/exceptions.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
//
77
//===----------------------------------------------------------------------===//
88

9-
// Map Fortran ieee_arithmetic module exceptions to fenv.h exceptions.
9+
// Support for floating point exceptions and related floating point environment
10+
// functionality.
1011

1112
#ifndef FORTRAN_RUNTIME_EXCEPTIONS_H_
1213
#define FORTRAN_RUNTIME_EXCEPTIONS_H_
@@ -25,6 +26,15 @@ extern "C" {
2526
// This mapping is done at runtime to support cross compilation.
2627
std::uint32_t RTNAME(MapException)(std::uint32_t excepts);
2728

29+
// Exception processing functions that call the corresponding libm functions,
30+
// and also include support for denormal exceptions where available.
31+
void RTNAME(feclearexcept)(std::uint32_t excepts);
32+
void RTNAME(feraiseexcept)(std::uint32_t excepts);
33+
std::uint32_t RTNAME(fetestexcept)(std::uint32_t excepts);
34+
void RTNAME(fedisableexcept)(std::uint32_t excepts);
35+
void RTNAME(feenableexcept)(std::uint32_t excepts);
36+
std::uint32_t RTNAME(fegetexcept)(void);
37+
2838
// Check if the processor has the ability to control whether to halt
2939
// or continue exeuction when a given exception is raised.
3040
bool RTNAME(SupportHalting)(uint32_t except);

flang/include/flang/Tools/TargetSetup.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ namespace Fortran::tools {
2929
targetCharacteristics.set_hasSubnormalFlushingControl(/*kind=*/3);
3030
targetCharacteristics.set_hasSubnormalFlushingControl(/*kind=*/4);
3131
targetCharacteristics.set_hasSubnormalFlushingControl(/*kind=*/8);
32+
// ieee_denorm exception support is nonstandard.
33+
targetCharacteristics.set_hasSubnormalExceptionSupport(/*kind=*/3);
34+
targetCharacteristics.set_hasSubnormalExceptionSupport(/*kind=*/4);
35+
targetCharacteristics.set_hasSubnormalExceptionSupport(/*kind=*/8);
3236
}
3337

3438
if (targetTriple.isARM() || targetTriple.isAArch64()) {

flang/lib/Evaluate/fold-logical.cpp

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -875,8 +875,38 @@ Expr<Type<TypeCategory::Logical, KIND>> FoldIntrinsicFunction(
875875
return Expr<T>{context.targetCharacteristics().ieeeFeatures().test(
876876
IeeeFeature::Divide)};
877877
} else if (name == "__builtin_ieee_support_flag") {
878-
return Expr<T>{context.targetCharacteristics().ieeeFeatures().test(
879-
IeeeFeature::Flags)};
878+
if (context.targetCharacteristics().ieeeFeatures().test(
879+
IeeeFeature::Flags)) {
880+
if (args[0]) {
881+
if (const auto *cst{UnwrapExpr<Constant<SomeDerived>>(args[0])}) {
882+
if (auto constr{cst->GetScalarValue()}) {
883+
if (StructureConstructorValues & values{constr->values()};
884+
values.size() == 1) {
885+
const Expr<SomeType> &value{values.begin()->second.value()};
886+
if (auto flag{ToInt64(value)}) {
887+
if (flag != _FORTRAN_RUNTIME_IEEE_DENORM) {
888+
// Check for suppport for standard exceptions.
889+
return Expr<T>{
890+
context.targetCharacteristics().ieeeFeatures().test(
891+
IeeeFeature::Flags)};
892+
} else if (args[1]) {
893+
// Check for nonstandard ieee_denorm exception support for
894+
// a given kind.
895+
return Expr<T>{context.targetCharacteristics()
896+
.hasSubnormalExceptionSupport(
897+
args[1]->GetType().value().kind())};
898+
} else {
899+
// Check for nonstandard ieee_denorm exception support for
900+
// all kinds.
901+
return Expr<T>{context.targetCharacteristics()
902+
.hasSubnormalExceptionSupport()};
903+
}
904+
}
905+
}
906+
}
907+
}
908+
}
909+
}
880910
} else if (name == "__builtin_ieee_support_halting") {
881911
if (!context.targetCharacteristics()
882912
.haltingSupportIsUnknownAtCompileTime()) {

flang/lib/Evaluate/target.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,30 @@ void TargetCharacteristics::set_hasSubnormalFlushingControl(
134134
hasSubnormalFlushingControl_[kind] = yes;
135135
}
136136

137+
// Check if a given real kind has (nonstandard) ieee_denorm exception control.
138+
bool TargetCharacteristics::hasSubnormalExceptionSupport(int kind) const {
139+
CHECK(kind > 0 && kind <= maxKind);
140+
CHECK(CanSupportType(TypeCategory::Real, kind));
141+
return hasSubnormalExceptionSupport_[kind];
142+
}
143+
144+
// Check if all real kinds have support for the ieee_denorm exception.
145+
bool TargetCharacteristics::hasSubnormalExceptionSupport() const {
146+
for (int kind{1}; kind <= maxKind; ++kind) {
147+
if (CanSupportType(TypeCategory::Real, kind) &&
148+
!hasSubnormalExceptionSupport_[kind]) {
149+
return false;
150+
}
151+
}
152+
return true;
153+
}
154+
155+
void TargetCharacteristics::set_hasSubnormalExceptionSupport(
156+
int kind, bool yes) {
157+
CHECK(kind > 0 && kind <= maxKind);
158+
hasSubnormalExceptionSupport_[kind] = yes;
159+
}
160+
137161
void TargetCharacteristics::set_roundingMode(Rounding rounding) {
138162
roundingMode_ = rounding;
139163
}

flang/lib/Optimizer/Builder/IntrinsicCall.cpp

Lines changed: 61 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4571,8 +4571,8 @@ void IntrinsicLibrary::genRaiseExcept(int excepts, mlir::Value cond) {
45714571
builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
45724572
}
45734573
mlir::Type i32Ty = builder.getIntegerType(32);
4574-
genRuntimeCall(
4575-
"feraiseexcept", i32Ty,
4574+
fir::runtime::genFeraiseexcept(
4575+
builder, loc,
45764576
fir::runtime::genMapExcept(
45774577
builder, loc, builder.createIntegerConstant(loc, i32Ty, excepts)));
45784578
if (cond)
@@ -4939,8 +4939,8 @@ void IntrinsicLibrary::genIeeeGetFlag(llvm::ArrayRef<fir::ExtendedValue> args) {
49394939
mlir::Value zero = builder.createIntegerConstant(loc, i32Ty, 0);
49404940
auto [fieldRef, ignore] = getFieldRef(builder, loc, flag);
49414941
mlir::Value field = builder.create<fir::LoadOp>(loc, fieldRef);
4942-
mlir::Value excepts = IntrinsicLibrary::genRuntimeCall(
4943-
"fetestexcept", i32Ty,
4942+
mlir::Value excepts = fir::runtime::genFetestexcept(
4943+
builder, loc,
49444944
fir::runtime::genMapExcept(
49454945
builder, loc, builder.create<fir::ConvertOp>(loc, i32Ty, field)));
49464946
mlir::Value logicalResult = builder.create<fir::ConvertOp>(
@@ -4963,8 +4963,7 @@ void IntrinsicLibrary::genIeeeGetHaltingMode(
49634963
mlir::Value zero = builder.createIntegerConstant(loc, i32Ty, 0);
49644964
auto [fieldRef, ignore] = getFieldRef(builder, loc, flag);
49654965
mlir::Value field = builder.create<fir::LoadOp>(loc, fieldRef);
4966-
mlir::Value haltSet =
4967-
IntrinsicLibrary::genRuntimeCall("fegetexcept", i32Ty, {});
4966+
mlir::Value haltSet = fir::runtime::genFegetexcept(builder, loc);
49684967
mlir::Value intResult = builder.create<mlir::arith::AndIOp>(
49694968
loc, haltSet,
49704969
fir::runtime::genMapExcept(
@@ -5712,9 +5711,11 @@ void IntrinsicLibrary::genIeeeSetFlagOrHaltingMode(
57125711
loc, builder.create<fir::ConvertOp>(loc, i1Ty, getBase(args[1])),
57135712
/*withElseRegion=*/true);
57145713
builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
5715-
genRuntimeCall(isFlag ? "feraiseexcept" : "feenableexcept", i32Ty, except);
5714+
(isFlag ? fir::runtime::genFeraiseexcept : fir::runtime::genFeenableexcept)(
5715+
builder, loc, builder.create<fir::ConvertOp>(loc, i32Ty, except));
57165716
builder.setInsertionPointToStart(&ifOp.getElseRegion().front());
5717-
genRuntimeCall(isFlag ? "feclearexcept" : "fedisableexcept", i32Ty, except);
5717+
(isFlag ? fir::runtime::genFeclearexcept : fir::runtime::genFedisableexcept)(
5718+
builder, loc, builder.create<fir::ConvertOp>(loc, i32Ty, except));
57185719
builder.setInsertionPointAfter(ifOp);
57195720
}
57205721

@@ -5805,24 +5806,61 @@ mlir::Value IntrinsicLibrary::genIeeeSignbit(mlir::Type resultType,
58055806
fir::ExtendedValue
58065807
IntrinsicLibrary::genIeeeSupportFlag(mlir::Type resultType,
58075808
llvm::ArrayRef<fir::ExtendedValue> args) {
5808-
// Check if a floating point exception flag is supported. A flag is
5809-
// supported either for all type kinds or none. An optional kind argument X
5810-
// is therefore ignored. Standard flags are all supported. The nonstandard
5811-
// DENORM extension is not supported, at least for now.
5809+
// Check if a floating point exception flag is supported.
58125810
assert(args.size() == 1 || args.size() == 2);
5811+
mlir::Type i1Ty = builder.getI1Type();
5812+
mlir::Type i32Ty = builder.getIntegerType(32);
58135813
auto [fieldRef, fieldTy] = getFieldRef(builder, loc, getBase(args[0]));
58145814
mlir::Value flag = builder.create<fir::LoadOp>(loc, fieldRef);
5815-
mlir::Value mask = builder.createIntegerConstant( // values are powers of 2
5815+
mlir::Value standardFlagMask = builder.createIntegerConstant(
58165816
loc, fieldTy,
58175817
_FORTRAN_RUNTIME_IEEE_INVALID | _FORTRAN_RUNTIME_IEEE_DIVIDE_BY_ZERO |
58185818
_FORTRAN_RUNTIME_IEEE_OVERFLOW | _FORTRAN_RUNTIME_IEEE_UNDERFLOW |
58195819
_FORTRAN_RUNTIME_IEEE_INEXACT);
5820-
return builder.createConvert(
5821-
loc, resultType,
5822-
builder.create<mlir::arith::CmpIOp>(
5823-
loc, mlir::arith::CmpIPredicate::ne,
5824-
builder.create<mlir::arith::AndIOp>(loc, flag, mask),
5825-
builder.createIntegerConstant(loc, fieldTy, 0)));
5820+
mlir::Value isStandardFlag = builder.create<mlir::arith::CmpIOp>(
5821+
loc, mlir::arith::CmpIPredicate::ne,
5822+
builder.create<mlir::arith::AndIOp>(loc, flag, standardFlagMask),
5823+
builder.createIntegerConstant(loc, fieldTy, 0));
5824+
fir::IfOp ifOp = builder.create<fir::IfOp>(loc, i1Ty, isStandardFlag,
5825+
/*withElseRegion=*/true);
5826+
// Standard flags are supported.
5827+
builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
5828+
builder.create<fir::ResultOp>(loc, builder.createBool(loc, true));
5829+
5830+
// TargetCharacteristics information for the nonstandard ieee_denorm flag
5831+
// is not available here. So use a runtime check restricted to possibly
5832+
// supported kinds.
5833+
builder.setInsertionPointToStart(&ifOp.getElseRegion().front());
5834+
bool mayBeSupported = false;
5835+
if (mlir::Value arg1 = getBase(args[1])) {
5836+
mlir::Type arg1Ty = arg1.getType();
5837+
if (fir::ReferenceType refTy = mlir::dyn_cast<fir::ReferenceType>(arg1Ty))
5838+
arg1Ty = refTy.getEleTy();
5839+
switch (mlir::dyn_cast<mlir::FloatType>(arg1Ty).getWidth()) {
5840+
case 16:
5841+
mayBeSupported = arg1Ty.isBF16(); // kind=3
5842+
break;
5843+
case 32: // kind=4
5844+
case 64: // kind=8
5845+
mayBeSupported = true;
5846+
break;
5847+
}
5848+
}
5849+
if (mayBeSupported) {
5850+
mlir::Value isDenorm = builder.create<mlir::arith::CmpIOp>(
5851+
loc, mlir::arith::CmpIPredicate::eq, flag,
5852+
builder.createIntegerConstant(loc, fieldTy,
5853+
_FORTRAN_RUNTIME_IEEE_DENORM));
5854+
mlir::Value result = builder.create<mlir::arith::AndIOp>(
5855+
loc, isDenorm,
5856+
fir::runtime::genSupportHalting(
5857+
builder, loc, builder.create<fir::ConvertOp>(loc, i32Ty, flag)));
5858+
builder.create<fir::ResultOp>(loc, result);
5859+
} else {
5860+
builder.create<fir::ResultOp>(loc, builder.createBool(loc, false));
5861+
}
5862+
builder.setInsertionPointAfter(ifOp);
5863+
return builder.createConvert(loc, resultType, ifOp.getResult(0));
58265864
}
58275865

58285866
// IEEE_SUPPORT_HALTING
@@ -5838,7 +5876,7 @@ fir::ExtendedValue IntrinsicLibrary::genIeeeSupportHalting(
58385876
return builder.createConvert(
58395877
loc, resultType,
58405878
fir::runtime::genSupportHalting(
5841-
builder, loc, {builder.create<fir::ConvertOp>(loc, i32Ty, field)}));
5879+
builder, loc, builder.create<fir::ConvertOp>(loc, i32Ty, field)));
58425880
}
58435881

58445882
// IEEE_SUPPORT_ROUNDING
@@ -5874,10 +5912,10 @@ fir::ExtendedValue IntrinsicLibrary::genIeeeSupportStandard(
58745912
// if halting control is supported, as that is the only support component
58755913
// that may not be available.
58765914
assert(args.size() <= 1);
5877-
mlir::Value nearest = builder.createIntegerConstant(
5878-
loc, builder.getIntegerType(32), _FORTRAN_RUNTIME_IEEE_NEAREST);
5915+
mlir::Value overflow = builder.createIntegerConstant(
5916+
loc, builder.getIntegerType(32), _FORTRAN_RUNTIME_IEEE_OVERFLOW);
58795917
return builder.createConvert(
5880-
loc, resultType, fir::runtime::genSupportHalting(builder, loc, nearest));
5918+
loc, resultType, fir::runtime::genSupportHalting(builder, loc, overflow));
58815919
}
58825920

58835921
// IEEE_UNORDERED

0 commit comments

Comments
 (0)