Skip to content

Commit a03e93e

Browse files
d-parksDavid Parks
andauthored
[flang] Add runtime support for Fortran intrinsic ERFC_SCALED (#95040)
Co-authored-by: David Parks <[email protected]>
1 parent 5ccdce9 commit a03e93e

File tree

9 files changed

+233
-0
lines changed

9 files changed

+233
-0
lines changed

flang/include/flang/Optimizer/Builder/IntrinsicCall.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,8 @@ struct IntrinsicLibrary {
204204
llvm::ArrayRef<fir::ExtendedValue>);
205205
fir::ExtendedValue genCAssociatedCPtr(mlir::Type,
206206
llvm::ArrayRef<fir::ExtendedValue>);
207+
mlir::Value genErfcScaled(mlir::Type resultType,
208+
llvm::ArrayRef<mlir::Value> args);
207209
void genCFPointer(llvm::ArrayRef<fir::ExtendedValue>);
208210
void genCFProcPointer(llvm::ArrayRef<fir::ExtendedValue>);
209211
fir::ExtendedValue genCFunLoc(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ class FirOpBuilder;
1818

1919
namespace fir::runtime {
2020

21+
/// Generate call to ErfcScaled intrinsic runtime routine.
22+
mlir::Value genErfcScaled(fir::FirOpBuilder &builder, mlir::Location loc,
23+
mlir::Value x);
24+
2125
/// Generate call to Exponent intrinsic runtime routine.
2226
mlir::Value genExponent(fir::FirOpBuilder &builder, mlir::Location loc,
2327
mlir::Type resultType, mlir::Value x);

flang/include/flang/Runtime/numeric.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,20 @@ CppTypeFor<TypeCategory::Integer, 16> RTDECL(Ceiling16_16)(
7373
#endif
7474
#endif
7575

76+
// ERFC_SCALED
77+
CppTypeFor<TypeCategory::Real, 4> RTDECL(ErfcScaled4)(
78+
CppTypeFor<TypeCategory::Real, 4>);
79+
CppTypeFor<TypeCategory::Real, 8> RTDECL(ErfcScaled8)(
80+
CppTypeFor<TypeCategory::Real, 8>);
81+
#if LDBL_MANT_DIG == 64
82+
CppTypeFor<TypeCategory::Real, 10> RTDECL(ErfcScaled10)(
83+
CppTypeFor<TypeCategory::Real, 10>);
84+
#endif
85+
#if LDBL_MANT_DIG == 113 || HAS_FLOAT128
86+
CppTypeFor<TypeCategory::Real, 16> RTDECL(ErfcScaled16)(
87+
CppTypeFor<TypeCategory::Real, 16>);
88+
#endif
89+
7690
// EXPONENT is defined to return default INTEGER; support INTEGER(4 & 8)
7791
CppTypeFor<TypeCategory::Integer, 4> RTDECL(Exponent4_4)(
7892
CppTypeFor<TypeCategory::Real, 4>);

flang/lib/Optimizer/Builder/IntrinsicCall.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@ static constexpr IntrinsicHandler handlers[]{
224224
{"boundary", asBox, handleDynamicOptional},
225225
{"dim", asValue}}},
226226
/*isElemental=*/false},
227+
{"erfc_scaled", &I::genErfcScaled},
227228
{"etime",
228229
&I::genEtime,
229230
{{{"values", asBox}, {"time", asBox}}},
@@ -5878,6 +5879,16 @@ mlir::Value IntrinsicLibrary::genRRSpacing(mlir::Type resultType,
58785879
fir::runtime::genRRSpacing(builder, loc, fir::getBase(args[0])));
58795880
}
58805881

5882+
// ERFC_SCALED
5883+
mlir::Value IntrinsicLibrary::genErfcScaled(mlir::Type resultType,
5884+
llvm::ArrayRef<mlir::Value> args) {
5885+
assert(args.size() == 1);
5886+
5887+
return builder.createConvert(
5888+
loc, resultType,
5889+
fir::runtime::genErfcScaled(builder, loc, fir::getBase(args[0])));
5890+
}
5891+
58815892
// SAME_TYPE_AS
58825893
fir::ExtendedValue
58835894
IntrinsicLibrary::genSameTypeAs(mlir::Type resultType,

flang/lib/Optimizer/Builder/Runtime/Numeric.cpp

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,28 @@ using namespace Fortran::runtime;
2222
// may not have them in their runtime library. This can occur in the
2323
// case of cross compilation, for example.
2424

25+
/// Placeholder for real*10 version of ErfcScaled Intrinsic
26+
struct ForcedErfcScaled10 {
27+
static constexpr const char *name = ExpandAndQuoteKey(RTNAME(ErfcScaled10));
28+
static constexpr fir::runtime::FuncTypeBuilderFunc getTypeModel() {
29+
return [](mlir::MLIRContext *ctx) {
30+
auto ty = mlir::FloatType::getF80(ctx);
31+
return mlir::FunctionType::get(ctx, {ty}, {ty});
32+
};
33+
}
34+
};
35+
36+
/// Placeholder for real*16 version of ErfcScaled Intrinsic
37+
struct ForcedErfcScaled16 {
38+
static constexpr const char *name = ExpandAndQuoteKey(RTNAME(ErfcScaled16));
39+
static constexpr fir::runtime::FuncTypeBuilderFunc getTypeModel() {
40+
return [](mlir::MLIRContext *ctx) {
41+
auto ty = mlir::FloatType::getF128(ctx);
42+
return mlir::FunctionType::get(ctx, {ty}, {ty});
43+
};
44+
}
45+
};
46+
2547
/// Placeholder for real*10 version of Exponent Intrinsic
2648
struct ForcedExponent10_4 {
2749
static constexpr const char *name = ExpandAndQuoteKey(RTNAME(Exponent10_4));
@@ -444,6 +466,30 @@ mlir::Value fir::runtime::genRRSpacing(fir::FirOpBuilder &builder,
444466
return builder.create<fir::CallOp>(loc, func, args).getResult(0);
445467
}
446468

469+
/// Generate call to ErfcScaled intrinsic runtime routine.
470+
mlir::Value fir::runtime::genErfcScaled(fir::FirOpBuilder &builder,
471+
mlir::Location loc, mlir::Value x) {
472+
mlir::func::FuncOp func;
473+
mlir::Type fltTy = x.getType();
474+
475+
if (fltTy.isF32())
476+
func = fir::runtime::getRuntimeFunc<mkRTKey(ErfcScaled4)>(loc, builder);
477+
else if (fltTy.isF64())
478+
func = fir::runtime::getRuntimeFunc<mkRTKey(ErfcScaled8)>(loc, builder);
479+
else if (fltTy.isF80())
480+
func = fir::runtime::getRuntimeFunc<ForcedErfcScaled10>(loc, builder);
481+
else if (fltTy.isF128())
482+
func = fir::runtime::getRuntimeFunc<ForcedErfcScaled16>(loc, builder);
483+
else
484+
fir::intrinsicTypeTODO(builder, fltTy, loc, "ERFC_SCALED");
485+
486+
auto funcTy = func.getFunctionType();
487+
llvm::SmallVector<mlir::Value> args = {
488+
builder.createConvert(loc, funcTy.getInput(0), x)};
489+
490+
return builder.create<fir::CallOp>(loc, func, args).getResult(0);
491+
}
492+
447493
/// Generate call to Scale intrinsic runtime routine.
448494
mlir::Value fir::runtime::genScale(fir::FirOpBuilder &builder,
449495
mlir::Location loc, mlir::Value x,

flang/runtime/numeric-templates.h

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,110 @@ template <int PREC, typename T> inline RT_API_ATTRS T Spacing(T x) {
354354
}
355355
}
356356

357+
// ERFC_SCALED (16.9.71)
358+
template <typename T> inline RT_API_ATTRS T ErfcScaled(T arg) {
359+
// Coefficients for approximation to erfc in the first interval.
360+
static const T a[5] = {3.16112374387056560e00, 1.13864154151050156e02,
361+
3.77485237685302021e02, 3.20937758913846947e03, 1.85777706184603153e-1};
362+
static const T b[4] = {2.36012909523441209e01, 2.44024637934444173e02,
363+
1.28261652607737228e03, 2.84423683343917062e03};
364+
365+
// Coefficients for approximation to erfc in the second interval.
366+
static const T c[9] = {5.64188496988670089e-1, 8.88314979438837594e00,
367+
6.61191906371416295e01, 2.98635138197400131e02, 8.81952221241769090e02,
368+
1.71204761263407058e03, 2.05107837782607147e03, 1.23033935479799725e03,
369+
2.15311535474403846e-8};
370+
static const T d[8] = {1.57449261107098347e01, 1.17693950891312499e02,
371+
5.37181101862009858e02, 1.62138957456669019e03, 3.29079923573345963e03,
372+
4.36261909014324716e03, 3.43936767414372164e03, 1.23033935480374942e03};
373+
374+
// Coefficients for approximation to erfc in the third interval.
375+
static const T p[6] = {3.05326634961232344e-1, 3.60344899949804439e-1,
376+
1.25781726111229246e-1, 1.60837851487422766e-2, 6.58749161529837803e-4,
377+
1.63153871373020978e-2};
378+
static const T q[5] = {2.56852019228982242e00, 1.87295284992346047e00,
379+
5.27905102951428412e-1, 6.05183413124413191e-2, 2.33520497626869185e-3};
380+
381+
constexpr T sqrtpi{1.7724538509078120380404576221783883301349L};
382+
constexpr T rsqrtpi{0.5641895835477562869480794515607725858440L};
383+
constexpr T epsilonby2{std::numeric_limits<T>::epsilon() * 0.5};
384+
constexpr T xneg{-26.628e0};
385+
constexpr T xhuge{6.71e7};
386+
constexpr T thresh{0.46875e0};
387+
constexpr T zero{0.0};
388+
constexpr T one{1.0};
389+
constexpr T four{4.0};
390+
constexpr T sixteen{16.0};
391+
constexpr T xmax{1.0 / (sqrtpi * std::numeric_limits<T>::min())};
392+
static_assert(xmax > xhuge, "xmax must be greater than xhuge");
393+
394+
T ysq;
395+
T xnum;
396+
T xden;
397+
T del;
398+
T result;
399+
400+
auto x{arg};
401+
auto y{std::fabs(x)};
402+
403+
if (y <= thresh) {
404+
// evaluate erf for |x| <= 0.46875
405+
ysq = zero;
406+
if (y > epsilonby2) {
407+
ysq = y * y;
408+
}
409+
xnum = a[4] * ysq;
410+
xden = ysq;
411+
for (int i{0}; i < 3; i++) {
412+
xnum = (xnum + a[i]) * ysq;
413+
xden = (xden + b[i]) * ysq;
414+
}
415+
result = x * (xnum + a[3]) / (xden + b[3]);
416+
result = one - result;
417+
result = std::exp(ysq) * result;
418+
return result;
419+
} else if (y <= four) {
420+
// evaluate erfc for 0.46875 < |x| <= 4.0
421+
xnum = c[8] * y;
422+
xden = y;
423+
for (int i{0}; i < 7; ++i) {
424+
xnum = (xnum + c[i]) * y;
425+
xden = (xden + d[i]) * y;
426+
}
427+
result = (xnum + c[7]) / (xden + d[7]);
428+
} else {
429+
// evaluate erfc for |x| > 4.0
430+
result = zero;
431+
if (y >= xhuge) {
432+
if (y < xmax) {
433+
result = rsqrtpi / y;
434+
}
435+
} else {
436+
ysq = one / (y * y);
437+
xnum = p[5] * ysq;
438+
xden = ysq;
439+
for (int i{0}; i < 4; ++i) {
440+
xnum = (xnum + p[i]) * ysq;
441+
xden = (xden + q[i]) * ysq;
442+
}
443+
result = ysq * (xnum + p[4]) / (xden + q[4]);
444+
result = (rsqrtpi - result) / y;
445+
}
446+
}
447+
// fix up for negative argument, erf, etc.
448+
if (x < zero) {
449+
if (x < xneg) {
450+
result = std::numeric_limits<T>::max();
451+
} else {
452+
ysq = trunc(x * sixteen) / sixteen;
453+
del = (x - ysq) * (x + ysq);
454+
y = std::exp((ysq * ysq)) * std::exp((del));
455+
result = (y + y) - result;
456+
}
457+
}
458+
return result;
459+
}
460+
357461
} // namespace Fortran::runtime
358462

359463
#endif // FORTRAN_RUNTIME_NUMERIC_TEMPLATES_H_

flang/runtime/numeric.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,27 @@ CppTypeFor<TypeCategory::Integer, 16> RTDEF(Ceiling16_16)(
316316
#endif
317317
#endif
318318

319+
CppTypeFor<TypeCategory::Real, 4> RTDEF(ErfcScaled4)(
320+
CppTypeFor<TypeCategory::Real, 4> x) {
321+
return ErfcScaled(x);
322+
}
323+
CppTypeFor<TypeCategory::Real, 8> RTDEF(ErfcScaled8)(
324+
CppTypeFor<TypeCategory::Real, 8> x) {
325+
return ErfcScaled(x);
326+
}
327+
#if LDBL_MANT_DIG == 64
328+
CppTypeFor<TypeCategory::Real, 10> RTDEF(ErfcScaled10)(
329+
CppTypeFor<TypeCategory::Real, 10> x) {
330+
return ErfcScaled(x);
331+
}
332+
#endif
333+
#if LDBL_MANT_DIG == 113
334+
CppTypeFor<TypeCategory::Real, 16> RTDEF(ErfcScaled16)(
335+
CppTypeFor<TypeCategory::Real, 16> x) {
336+
return ErfcScaled(x);
337+
}
338+
#endif
339+
319340
CppTypeFor<TypeCategory::Integer, 4> RTDEF(Exponent4_4)(
320341
CppTypeFor<TypeCategory::Real, 4> x) {
321342
return Exponent<CppTypeFor<TypeCategory::Integer, 4>>(x);
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
! RUN: bbc -emit-fir -hlfir=false %s -o - | FileCheck %s
2+
! RUN: %flang_fc1 -emit-fir -flang-deprecated-no-hlfir %s -o - | FileCheck %s
3+
4+
! CHECK-LABEL: func @_QPerfc_scaled4(
5+
! CHECK-SAME: %[[x:[^:]+]]: !fir.ref<f32>{{.*}}) -> f32
6+
function erfc_scaled4(x)
7+
real(kind=4) :: erfc_scaled4
8+
real(kind=4) :: x
9+
erfc_scaled4 = erfc_scaled(x);
10+
! CHECK: %[[a1:.*]] = fir.load %[[x]] : !fir.ref<f32>
11+
! CHECK: %{{.*}} = fir.call @_FortranAErfcScaled4(%[[a1]]) {{.*}}: (f32) -> f32
12+
end function erfc_scaled4
13+
14+
15+
! CHECK-LABEL: func @_QPerfc_scaled8(
16+
! CHECK-SAME: %[[x:[^:]+]]: !fir.ref<f64>{{.*}}) -> f64
17+
function erfc_scaled8(x)
18+
real(kind=8) :: erfc_scaled8
19+
real(kind=8) :: x
20+
erfc_scaled8 = erfc_scaled(x);
21+
! CHECK: %[[a1:.*]] = fir.load %[[x]] : !fir.ref<f64>
22+
! CHECK: %{{.*}} = fir.call @_FortranAErfcScaled8(%[[a1]]) {{.*}}: (f64) -> f64
23+
end function erfc_scaled8

flang/unittests/Runtime/Numeric.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,14 @@ TEST(Numeric, Floor) {
3131
EXPECT_EQ(RTNAME(Floor4_1)(Real<4>{0}), 0);
3232
}
3333

34+
TEST(Numeric, Erfc_scaled) {
35+
EXPECT_NEAR(RTNAME(ErfcScaled4)(Real<4>{20.0}), 0.02817434874, 1.0e-8);
36+
EXPECT_NEAR(RTNAME(ErfcScaled8)(Real<8>{20.0}), 0.02817434874, 1.0e-11);
37+
#if LDBL_MANT_DIG == 64
38+
EXPECT_NEAR(RTNAME(ErfcScaled10)(Real<10>{20.0}), 0.02817434874, 1.0e-8);
39+
#endif
40+
}
41+
3442
TEST(Numeric, Exponent) {
3543
EXPECT_EQ(RTNAME(Exponent4_4)(Real<4>{0}), 0);
3644
EXPECT_EQ(RTNAME(Exponent4_8)(Real<4>{1.0}), 1);

0 commit comments

Comments
 (0)