-
Notifications
You must be signed in to change notification settings - Fork 13.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[flang] Define ERF, ERFC and ERFC_SCALED intrinsics with Q and D prefix #125217
[flang] Define ERF, ERFC and ERFC_SCALED intrinsics with Q and D prefix #125217
Conversation
@llvm/pr-subscribers-flang-semantics @llvm/pr-subscribers-flang-fir-hlfir Author: Jean-Didier PAILLEUX (JDPailleux) Changes
Full diff: https://github.com/llvm/llvm-project/pull/125217.diff 11 Files Affected:
diff --git a/flang/docs/Intrinsics.md b/flang/docs/Intrinsics.md
index 5b671d1b2c7408..49d2e3bcad6bc4 100644
--- a/flang/docs/Intrinsics.md
+++ b/flang/docs/Intrinsics.md
@@ -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.
diff --git a/flang/lib/Evaluate/intrinsics.cpp b/flang/lib/Evaluate/intrinsics.cpp
index 1c7e564f706ad4..3a79d95c983921 100644
--- a/flang/lib/Evaluate/intrinsics.cpp
+++ b/flang/lib/Evaluate/intrinsics.cpp
@@ -83,7 +83,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 +139,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
@@ -428,6 +429,9 @@ static const IntrinsicInterface genericIntrinsicFunction[]{
{"shift", AnyInt, Rank::dimRemovedOrScalar}, OptionalDIM},
SameType, Rank::conformable, IntrinsicClass::transformationalFunction},
{"dble", {{"a", AnyNumeric, Rank::elementalOrBOZ}}, DoublePrecision},
+ {"derf", {{"x", DoublePrecision}}, DoublePrecision},
+ {"derfc", {{"x", DoublePrecision}}, DoublePrecision},
+ {"derfc_scaled", {{"x", DoublePrecision}}, DoublePrecision},
{"digits",
{{"x", AnyIntUnsignedOrReal, Rank::anyOrAssumedRank,
Optionality::required, common::Intent::In,
@@ -830,6 +834,9 @@ static const IntrinsicInterface genericIntrinsicFunction[]{
DefaultInt, Rank::scalar, IntrinsicClass::inquiryFunction},
{"present", {{"a", Addressable, Rank::anyOrAssumedRank}}, DefaultLogical,
Rank::scalar, IntrinsicClass::inquiryFunction},
+ {"qerf", {{"x", QuadPrecision}}, QuadPrecision},
+ {"qerfc", {{"x", QuadPrecision}}, QuadPrecision},
+ {"qerfc_scaled", {{"x", QuadPrecision}}, QuadPrecision},
{"radix",
{{"x", AnyIntOrReal, Rank::anyOrAssumedRank, Optionality::required,
common::Intent::In, {ArgFlag::canBeMoldNull}}},
@@ -2012,6 +2019,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;
@@ -2317,6 +2327,11 @@ 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()};
+ break;
case KindCode::defaultLogicalKind:
CHECK(result.categorySet == LogicalType);
CHECK(*category == TypeCategory::Logical);
@@ -3312,6 +3327,7 @@ static DynamicType GetReturnType(const SpecificIntrinsicInterface &interface,
case KindCode::defaultIntegerKind:
break;
case KindCode::doublePrecision:
+ case KindCode::quadPrecision:
case KindCode::defaultRealKind:
category = TypeCategory::Real;
break;
@@ -3320,6 +3336,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};
}
@@ -3560,6 +3578,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.
diff --git a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
index 970fb2b1c2f701..7f3777baa26786 100644
--- a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
+++ b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
@@ -235,6 +235,10 @@ static constexpr IntrinsicHandler handlers[]{
{"values", asBox, handleDynamicOptional}}},
/*isElemental=*/false},
{"dble", &I::genConversion},
+ {"derfc_scaled",
+ &I::genErfcScaled,
+ {{{"x", asValue}}},
+ /*isElemental=*/true},
{"dim", &I::genDim},
{"dot_product",
&I::genDotProduct,
@@ -554,6 +558,10 @@ static constexpr IntrinsicHandler handlers[]{
{"dim", asValue},
{"mask", asBox, handleDynamicOptional}}},
/*isElemental=*/false},
+ {"qerfc_scaled",
+ &I::genErfcScaled,
+ {{{"x", asValue}}},
+ /*isElemental=*/true},
{"random_init",
&I::genRandomInit,
{{{"repeatable", asValue}, {"image_distinct", asValue}}},
@@ -1177,6 +1185,8 @@ static constexpr MathOperation mathOperations[] = {
{"cosh", "ccosh", genFuncType<Ty::Complex<8>, Ty::Complex<8>>, genLibCall},
{"cosh", RTNAME_STRING(CCoshF128), FuncTypeComplex16Complex16,
genLibF128Call},
+ {"derf", "erf", genFuncType<Ty::Real<8>, Ty::Real<8>>, genLibCall},
+ {"derfc", "erfc", genFuncType<Ty::Real<8>, Ty::Real<8>>, genLibCall},
{"divc",
{},
genFuncType<Ty::Complex<2>, Ty::Complex<2>, Ty::Complex<2>>,
@@ -1358,6 +1368,8 @@ static constexpr MathOperation mathOperations[] = {
genFuncType<Ty::Complex<8>, Ty::Complex<8>, Ty::Integer<8>>, genLibCall},
{"pow", RTNAME_STRING(cqpowk), FuncTypeComplex16Complex16Integer8,
genLibF128Call},
+ {"qerf", RTNAME_STRING(ErfF128), FuncTypeReal16Real16, genLibF128Call},
+ {"qerfc", RTNAME_STRING(ErfcF128), FuncTypeReal16Real16, genLibF128Call},
{"remainder", "remainderf",
genFuncType<Ty::Real<4>, Ty::Real<4>, Ty::Real<4>>, genLibCall},
{"remainder", "remainder",
diff --git a/flang/test/Lower/Intrinsics/erf.f90 b/flang/test/Lower/Intrinsics/erf.f90
new file mode 100644
index 00000000000000..19220cfa7f88a5
--- /dev/null
+++ b/flang/test/Lower/Intrinsics/erf.f90
@@ -0,0 +1,22 @@
+! RUN: bbc -emit-fir %s -o - --math-runtime=fast | FileCheck --check-prefixes=ALL %s
+! RUN: %flang_fc1 -emit-fir -mllvm -math-runtime=fast %s -o - | FileCheck --check-prefixes=ALL %s
+! RUN: bbc -emit-fir %s -o - --math-runtime=relaxed | FileCheck --check-prefixes=ALL %s
+! RUN: %flang_fc1 -emit-fir -mllvm -math-runtime=relaxed %s -o - | FileCheck --check-prefixes=ALL %s
+! RUN: bbc -emit-fir %s -o - --math-runtime=precise | FileCheck --check-prefixes=ALL %s
+! RUN: %flang_fc1 -emit-fir -mllvm -math-runtime=precise %s -o - | FileCheck --check-prefixes=ALL %s
+
+function dtest_real8(x)
+ real(8) :: x, dtest_real8
+ dtest_real8 = derf(x)
+end function
+
+! ALL-LABEL: @_QPdtest_real8
+! ALL: {{%[A-Za-z0-9._]+}} = fir.call @erf({{%[A-Za-z0-9._]+}}) {{.*}}: (f64) -> f64
+
+function qtest_real16(x)
+ real(16) :: x, qtest_real16
+ qtest_real16 = qerf(x)
+end function
+
+! ALL-LABEL: @_QPqtest_real16
+! CHECK: %{{.*}} = fir.call @_FortranAErfF128(%[[a1]]) {{.*}}: (f128) -> f128
diff --git a/flang/test/Lower/Intrinsics/erf_real16.f90 b/flang/test/Lower/Intrinsics/erf_real16.f90
index da408169461713..e9cc6175c1284c 100644
--- a/flang/test/Lower/Intrinsics/erf_real16.f90
+++ b/flang/test/Lower/Intrinsics/erf_real16.f90
@@ -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
diff --git a/flang/test/Lower/Intrinsics/erfc.f90 b/flang/test/Lower/Intrinsics/erfc.f90
index 0f1e05400f5f29..578287897f5ab3 100644
--- a/flang/test/Lower/Intrinsics/erfc.f90
+++ b/flang/test/Lower/Intrinsics/erfc.f90
@@ -20,3 +20,19 @@ function test_real8(x)
! ALL-LABEL: @_QPtest_real8
! ALL: {{%[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
+! ALL: {{%[A-Za-z0-9._]+}} = fir.call @erfc({{%[A-Za-z0-9._]+}}) {{.*}}: (f64) -> f64
+
+function qtest_real16(x)
+ real(16) :: x, qtest_real16
+ qtest_real16 = qerfc(x)
+end function
+
+! ALL-LABEL: @_QPqtest_real16
+! CHECK: %{{.*}} = fir.call @_FortranAErfcF128(%[[a1]]) {{.*}}: (f128) -> f128
diff --git a/flang/test/Lower/Intrinsics/erfc_real16.f90 b/flang/test/Lower/Intrinsics/erfc_real16.f90
index 7e3daa27768c7c..d63c4d80df043c 100644
--- a/flang/test/Lower/Intrinsics/erfc_real16.f90
+++ b/flang/test/Lower/Intrinsics/erfc_real16.f90
@@ -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
diff --git a/flang/test/Lower/Intrinsics/erfc_scaled.f90 b/flang/test/Lower/Intrinsics/erfc_scaled.f90
index ab5e90cb2409ea..730348e2ea97fc 100644
--- a/flang/test/Lower/Intrinsics/erfc_scaled.f90
+++ b/flang/test/Lower/Intrinsics/erfc_scaled.f90
@@ -21,3 +21,23 @@ 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
+
+! CHECK-LABEL: func @_QPqerfc_scaled16(
+! CHECK: %[[a1:.*]] = fir.load %[[x]] : !fir.ref<f128>
+function qerfc_scaled16(x)
+ real(kind=16) :: qerfc_scaled16
+ real(kind=16) :: x
+ qerfc_scaled16 = qerfc_scaled(x);
+! CHECK: %{{.*}} = fir.call @_FortranAErfcScaled16(%[[a1]]) {{.*}}: (f128) -> f128
+end function qerfc_scaled16
diff --git a/flang/test/Semantics/erf.f90 b/flang/test/Semantics/erf.f90
new file mode 100644
index 00000000000000..591b4c31992d1f
--- /dev/null
+++ b/flang/test/Semantics/erf.f90
@@ -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
diff --git a/flang/test/Semantics/erfc.f90 b/flang/test/Semantics/erfc.f90
new file mode 100644
index 00000000000000..ae3273bcc7e315
--- /dev/null
+++ b/flang/test/Semantics/erfc.f90
@@ -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
diff --git a/flang/test/Semantics/erfc_scaled.f90 b/flang/test/Semantics/erfc_scaled.f90
new file mode 100644
index 00000000000000..5e6cd502c7db71
--- /dev/null
+++ b/flang/test/Semantics/erfc_scaled.f90
@@ -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
|
1912115
to
a38a7b6
Compare
Thank you for the work! It looks good to me. @klausler can you please also take a look? |
a38a7b6
to
bda5f94
Compare
Thank you for your feedbacks. I've updated the PR accordingly. |
bda5f94
to
9b04319
Compare
Hi, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you!
ERF
,ERFC
andERFC_SCALED
intrinsics prefixed byQ
andD
are missing. Codes such asCP2K
(https://github.com/cp2k/cp2k) andTurboRVB
(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-proceduresThese intrinsics are based on the existing intrinsics but apply a restriction on the type kind.
DERF
,DERFC
andDERFC_SCALED
are for double précision only.QERF
,QERFC
andQERFC_SCALED
are for quad précision only.