[flang] Define ERF, ERFC and ERFC_SCALED intrinsics with Q and D prefix (#125217)

`ERF`, `ERFC` and `ERFC_SCALED` intrinsics prefixed by `Q` and `D` are
missing. Codes such as `CP2K`(https://github.com/cp2k/cp2k) and
`TurboRVB`(https://github.com/sissaschool/turborvb) use these intrinsics
just like defined in the GNU standard and here:
https://www.ibm.com/docs/fr/xl-fortran-aix/16.1.0?topic=reference-intrinsic-procedures
These intrinsics are based on the existing intrinsics but apply a
restriction on the type kind.

- `DERF`, `DERFC` and `DERFC_SCALED` are for double précision only.
- `QERF`, `QERFC` and `QERFC_SCALED` are for quad précision only.
This commit is contained in:
Jean-Didier PAILLEUX
2025-04-01 08:07:26 +02:00
committed by GitHub
parent 091dcb8fc2
commit bae3577002
11 changed files with 176 additions and 5 deletions

View File

@@ -241,8 +241,14 @@ BESSEL_Y0(REAL(k) X) -> REAL(k)
BESSEL_Y1(REAL(k) X) -> REAL(k)
BESSEL_YN(INTEGER(n) N, REAL(k) X) -> REAL(k)
ERF(REAL(k) X) -> REAL(k)
DERF(REAL(8) X) -> REAL(8)
QERF(REAL(16) X) -> REAL(16)
ERFC(REAL(k) X) -> REAL(k)
DERFC(REAL(8) X) -> REAL(8)
QERFC(REAL(16) X) -> REAL(16)
ERFC_SCALED(REAL(k) X) -> REAL(k)
DERFC_SCALED(REAL(8) X) -> REAL(8)
QERFC_SCALED(REAL(16) X) -> REAL(16)
FRACTION(REAL(k) X) -> REAL(k)
GAMMA(REAL(k) X) -> REAL(k)
HYPOT(REAL(k) X, REAL(k) Y) -> REAL(k) = SQRT(X*X+Y*Y) without spurious overflow
@@ -810,7 +816,7 @@ otherwise an error message will be produced by f18 when attempting to fold relat
| C/C++ Host Type | Intrinsic Functions with Host Standard C++ Library Based Folding Support |
| --- | --- |
| float, double and long double | ACOS, ACOSH, ASINH, ATAN, ATAN2, ATANH, COS, COSH, ERF, ERFC, EXP, GAMMA, HYPOT, LOG, LOG10, LOG_GAMMA, MOD, SIN, SQRT, SINH, SQRT, TAN, TANH |
| float, double and long double | ACOS, ACOSH, ASINH, ATAN, ATAN2, ATANH, COS, COSH, DERF, DERFC, ERF, ERFC, EXP, GAMMA, HYPOT, LOG, LOG10, LOG_GAMMA, MOD, QERF, QERFC, SIN, SQRT, SINH, SQRT, TAN, TANH |
| std::complex for float, double and long double| ACOS, ACOSH, ASIN, ASINH, ATAN, ATANH, COS, COSH, EXP, LOG, SIN, SINH, SQRT, TAN, TANH |
On top of the default usage of C++ standard library functions for folding described
@@ -829,7 +835,7 @@ types related to host float and double types.
| C/C++ Host Type | Additional Intrinsic Function Folding Support with Libpgmath (Optional) |
| --- | --- |
|float and double| BESSEL_J0, BESSEL_J1, BESSEL_JN (elemental only), BESSEL_Y0, BESSEL_Y1, BESSEL_Yn (elemental only), ERFC_SCALED |
|float and double| BESSEL_J0, BESSEL_J1, BESSEL_JN (elemental only), BESSEL_Y0, BESSEL_Y1, BESSEL_Yn (elemental only), DERFC_SCALED, ERFC_SCALED, QERFC_SCALED |
Libpgmath comes in three variants (precise, relaxed and fast). So far, only the
precise version is used for intrinsic function folding in f18. It guarantees the greatest numerical precision.

View File

@@ -8,6 +8,7 @@
#include "flang/Evaluate/intrinsics.h"
#include "flang/Common/enum-set.h"
#include "flang/Common/float128.h"
#include "flang/Common/idioms.h"
#include "flang/Evaluate/check-expression.h"
#include "flang/Evaluate/common.h"
@@ -83,7 +84,7 @@ static constexpr CategorySet AnyType{IntrinsicType | DerivedType};
ENUM_CLASS(KindCode, none, defaultIntegerKind,
defaultRealKind, // is also the default COMPLEX kind
doublePrecision, defaultCharKind, defaultLogicalKind,
doublePrecision, quadPrecision, defaultCharKind, defaultLogicalKind,
greaterOrEqualToKind, // match kind value greater than or equal to a single
// explicit kind value
any, // matches any kind value; each instance is independent
@@ -139,6 +140,7 @@ static constexpr TypePattern DoublePrecision{
RealType, KindCode::doublePrecision};
static constexpr TypePattern DoublePrecisionComplex{
ComplexType, KindCode::doublePrecision};
static constexpr TypePattern QuadPrecision{RealType, KindCode::quadPrecision};
static constexpr TypePattern SubscriptInt{IntType, KindCode::subscript};
// Match any kind of some intrinsic or derived types
@@ -1199,6 +1201,9 @@ static const SpecificIntrinsicInterface specificIntrinsicFunction[]{
DoublePrecision},
"dim"},
{{"derf", {{"x", DoublePrecision}}, DoublePrecision}, "erf"},
{{"derfc", {{"x", DoublePrecision}}, DoublePrecision}, "erfc"},
{{"derfc_scaled", {{"x", DoublePrecision}}, DoublePrecision},
"erfc_scaled"},
{{"dexp", {{"x", DoublePrecision}}, DoublePrecision}, "exp"},
{{"dfloat", {{"a", AnyInt}}, DoublePrecision}, "real", true},
{{"dim", {{"x", DefaultReal}, {"y", DefaultReal}}, DefaultReal}},
@@ -1299,6 +1304,9 @@ static const SpecificIntrinsicInterface specificIntrinsicFunction[]{
"min", true, true},
{{"mod", {{"a", DefaultInt}, {"p", DefaultInt}}, DefaultInt}},
{{"nint", {{"a", DefaultReal}}, DefaultInt}},
{{"qerf", {{"x", QuadPrecision}}, QuadPrecision}, "erf"},
{{"qerfc", {{"x", QuadPrecision}}, QuadPrecision}, "erfc"},
{{"qerfc_scaled", {{"x", QuadPrecision}}, QuadPrecision}, "erfc_scaled"},
{{"sign", {{"a", DefaultReal}, {"b", DefaultReal}}, DefaultReal}},
{{"sin", {{"x", DefaultReal}}, DefaultReal}},
{{"sinh", {{"x", DefaultReal}}, DefaultReal}},
@@ -2033,6 +2041,9 @@ std::optional<SpecificCall> IntrinsicInterface::Match(
case KindCode::doublePrecision:
argOk = type->kind() == defaults.doublePrecisionKind();
break;
case KindCode::quadPrecision:
argOk = type->kind() == defaults.quadPrecisionKind();
break;
case KindCode::defaultCharKind:
argOk = type->kind() == defaults.GetDefaultKind(TypeCategory::Character);
break;
@@ -2343,6 +2354,18 @@ std::optional<SpecificCall> IntrinsicInterface::Match(
CHECK(FloatingType.test(*category));
resultType = DynamicType{*category, defaults.doublePrecisionKind()};
break;
case KindCode::quadPrecision:
CHECK(result.categorySet == CategorySet{*category});
CHECK(FloatingType.test(*category));
resultType = DynamicType{*category, defaults.quadPrecisionKind()};
if (!context.targetCharacteristics().CanSupportType(
*category, defaults.quadPrecisionKind())) {
messages.Say(
"%s(KIND=%jd) type not supported on this target."_err_en_US,
parser::ToUpperCaseLetters(EnumToString(*category)),
defaults.quadPrecisionKind());
}
break;
case KindCode::defaultLogicalKind:
CHECK(result.categorySet == LogicalType);
CHECK(*category == TypeCategory::Logical);
@@ -3341,6 +3364,7 @@ static DynamicType GetReturnType(const SpecificIntrinsicInterface &interface,
case KindCode::defaultIntegerKind:
break;
case KindCode::doublePrecision:
case KindCode::quadPrecision:
case KindCode::defaultRealKind:
category = TypeCategory::Real;
break;
@@ -3349,6 +3373,8 @@ static DynamicType GetReturnType(const SpecificIntrinsicInterface &interface,
}
int kind{interface.result.kindCode == KindCode::doublePrecision
? defaults.doublePrecisionKind()
: interface.result.kindCode == KindCode::quadPrecision
? defaults.quadPrecisionKind()
: defaults.GetDefaultKind(category)};
return DynamicType{category, kind};
}
@@ -3589,6 +3615,8 @@ DynamicType IntrinsicProcTable::Implementation::GetSpecificType(
TypeCategory category{set.LeastElement().value()};
if (pattern.kindCode == KindCode::doublePrecision) {
return DynamicType{category, defaults_.doublePrecisionKind()};
} else if (pattern.kindCode == KindCode::quadPrecision) {
return DynamicType{category, defaults_.quadPrecisionKind()};
} else if (category == TypeCategory::Character) {
// All character arguments to specific intrinsic functions are
// assumed-length.

View File

@@ -0,0 +1,16 @@
! RUN: bbc -emit-fir %s -o - --math-runtime=fast | FileCheck --check-prefixes=ALL,FAST %s
! RUN: %flang_fc1 -emit-fir -mllvm -math-runtime=fast %s -o - | FileCheck --check-prefixes=ALL,FAST %s
! RUN: bbc -emit-fir %s -o - --math-runtime=relaxed | FileCheck --check-prefixes=ALL,RELAXED %s
! RUN: %flang_fc1 -emit-fir -mllvm -math-runtime=relaxed %s -o - | FileCheck --check-prefixes=ALL,RELAXED %s
! RUN: bbc -emit-fir %s -o - --math-runtime=precise | FileCheck --check-prefixes=ALL,PRECISE %s
! RUN: %flang_fc1 -emit-fir -mllvm -math-runtime=precise %s -o - | FileCheck --check-prefixes=ALL,PRECISE %s
function dtest_real8(x)
real(8) :: x, dtest_real8
dtest_real8 = derf(x)
end function
! ALL-LABEL: @_QPdtest_real8
! FAST: {{%[A-Za-z0-9._]+}} = math.erf {{%[A-Za-z0-9._]+}} {{.*}}: f64
! RELAXED: {{%[A-Za-z0-9._]+}} = math.erf {{%[A-Za-z0-9._]+}} {{.*}}: f64
! PRECISE: {{%[A-Za-z0-9._]+}} = fir.call @erf({{%[A-Za-z0-9._]+}}) {{.*}}: (f64) -> f64

View File

@@ -4,6 +4,8 @@
! RUN: %flang_fc1 -emit-fir %s -o - | FileCheck %s
! CHECK: fir.call @_FortranAErfF128({{.*}}){{.*}}: (f128) -> f128
real(16) :: a, b
! CHECK: fir.call @_FortranAErfF128({{.*}}){{.*}}: (f128) -> f128
real(16) :: a, b, c
b = erf(a)
c = qerf(a)
end

View File

@@ -24,3 +24,13 @@ end function
! FAST: {{%[A-Za-z0-9._]+}} = math.erfc {{%[A-Za-z0-9._]+}} {{.*}}: f64
! RELAXED: {{%[A-Za-z0-9._]+}} = math.erfc {{%[A-Za-z0-9._]+}} {{.*}}: f64
! PRECISE: {{%[A-Za-z0-9._]+}} = fir.call @erfc({{%[A-Za-z0-9._]+}}) {{.*}}: (f64) -> f64
function dtest_real8(x)
real(8) :: x, dtest_real8
dtest_real8 = derfc(x)
end function
! ALL-LABEL: @_QPdtest_real8
! FAST: {{%[A-Za-z0-9._]+}} = math.erfc {{%[A-Za-z0-9._]+}} {{.*}}: f64
! RELAXED: {{%[A-Za-z0-9._]+}} = math.erfc {{%[A-Za-z0-9._]+}} {{.*}}: f64
! PRECISE: {{%[A-Za-z0-9._]+}} = fir.call @erfc({{%[A-Za-z0-9._]+}}) {{.*}}: (f64) -> f64

View File

@@ -4,6 +4,8 @@
! RUN: %flang_fc1 -emit-fir %s -o - | FileCheck %s
! CHECK: fir.call @_FortranAErfcF128({{.*}}){{.*}}: (f128) -> f128
real(16) :: a, b
! CHECK: fir.call @_FortranAErfcF128({{.*}}){{.*}}: (f128) -> f128
real(16) :: a, b, c
b = erfc(a)
c = qerfc(a)
end

View File

@@ -21,3 +21,14 @@ function erfc_scaled8(x)
! CHECK: %[[a1:.*]] = fir.load %[[x]] : !fir.ref<f64>
! CHECK: %{{.*}} = fir.call @_FortranAErfcScaled8(%[[a1]]) {{.*}}: (f64) -> f64
end function erfc_scaled8
! CHECK-LABEL: func @_QPderfc_scaled8(
! CHECK-SAME: %[[x:[^:]+]]: !fir.ref<f64>{{.*}}) -> f64
function derfc_scaled8(x)
real(kind=8) :: derfc_scaled8
real(kind=8) :: x
derfc_scaled8 = derfc_scaled(x);
! CHECK: %[[a1:.*]] = fir.load %[[x]] : !fir.ref<f64>
! CHECK: %{{.*}} = fir.call @_FortranAErfcScaled8(%[[a1]]) {{.*}}: (f64) -> f64
end function derfc_scaled8

View File

@@ -0,0 +1,9 @@
! REQUIRES: flang-supports-f128-math
! RUN: bbc -emit-fir %s -o - | FileCheck %s
! RUN: bbc --math-runtime=precise -emit-fir %s -o - | FileCheck %s
! RUN: %flang_fc1 -emit-fir %s -o - | FileCheck %s
! CHECK: fir.call @_FortranAErfcScaled16({{.*}}) {{.*}}: (f128) -> f128
real(16) :: a, b
b = qerfc_scaled(a)
end

View File

@@ -0,0 +1,29 @@
! RUN: not %flang_fc1 -fsyntax-only %s 2>&1 | FileCheck --check-prefix=ERROR %s
function derf8_error4(x)
real(kind=8) :: derf8_error4
real(kind=4) :: x
derf8_error4 = derf(x);
! ERROR: Actual argument for 'x=' has bad type or kind 'REAL(4)'
end function derf8_error4
function derf8_error16(x)
real(kind=8) :: derf8_error16
real(kind=16) :: x
derf8_error16 = derf(x);
! ERROR: Actual argument for 'x=' has bad type or kind 'REAL(16)'
end function derf8_error16
function qerf16_error4(x)
real(kind=16) :: qerf16_error4
real(kind=4) :: x
qerf16_error4 = qerf(x);
! ERROR: Actual argument for 'x=' has bad type or kind 'REAL(4)'
end function qerf16_error4
function qerf16_error8(x)
real(kind=16) :: qerf16_error8
real(kind=8) :: x
qerf16_error8 = qerf(x);
! ERROR: Actual argument for 'x=' has bad type or kind 'REAL(8)'
end function qerf16_error8

View File

@@ -0,0 +1,29 @@
! RUN: not %flang_fc1 -fsyntax-only %s 2>&1 | FileCheck --check-prefix=ERROR %s
function derfc8_error4(x)
real(kind=8) :: derfc8_error4
real(kind=4) :: x
derfc8_error4 = derfc(x);
! ERROR: Actual argument for 'x=' has bad type or kind 'REAL(4)'
end function derfc8_error4
function derfc8_error16(x)
real(kind=8) :: derfc8_error16
real(kind=16) :: x
derfc8_error16 = derfc(x);
! ERROR: Actual argument for 'x=' has bad type or kind 'REAL(16)'
end function derfc8_error16
function qerfc16_error4(x)
real(kind=16) :: qerfc16_error4
real(kind=4) :: x
qerfc16_error4 = qerfc(x);
! ERROR: Actual argument for 'x=' has bad type or kind 'REAL(4)'
end function qerfc16_error4
function qerfc16_error8(x)
real(kind=16) :: qerfc16_error8
real(kind=8) :: x
qerfc16_error8 = qerfc(x);
! ERROR: Actual argument for 'x=' has bad type or kind 'REAL(8)'
end function qerfc16_error8

View File

@@ -0,0 +1,29 @@
! RUN: not %flang_fc1 -fsyntax-only %s 2>&1 | FileCheck --check-prefix=ERROR %s
function derfc_scaled8_error4(x)
real(kind=8) :: derfc_scaled8_error4
real(kind=4) :: x
derfc_scaled8_error4 = derfc_scaled(x);
! ERROR: Actual argument for 'x=' has bad type or kind 'REAL(4)'
end function derfc_scaled8_error4
function derfc_scaled8_error16(x)
real(kind=8) :: derfc_scaled8_error16
real(kind=16) :: x
derfc_scaled8_error16 = derfc_scaled(x);
! ERROR: Actual argument for 'x=' has bad type or kind 'REAL(16)'
end function derfc_scaled8_error16
function qerfc_scaled16_error4(x)
real(kind=16) :: qerfc_scaled16_error4
real(kind=4) :: x
qerfc_scaled16_error4 = qerfc_scaled(x);
! ERROR: Actual argument for 'x=' has bad type or kind 'REAL(4)'
end function qerfc_scaled16_error4
function qerfc_scaled16_error8(x)
real(kind=16) :: qerfc_scaled16_error8
real(kind=8) :: x
qerfc_scaled16_error8 = qerfc_scaled(x);
! ERROR: Actual argument for 'x=' has bad type or kind 'REAL(8)'
end function qerfc_scaled16_error8