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

via flang-commits flang-commits at lists.llvm.org
Fri Jan 31 05:00:58 PST 2025


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-flang-fir-hlfir

Author: Jean-Didier PAILLEUX (JDPailleux)

<details>
<summary>Changes</summary>

`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.

---
Full diff: https://github.com/llvm/llvm-project/pull/125217.diff


11 Files Affected:

- (modified) flang/docs/Intrinsics.md (+8-2) 
- (modified) flang/lib/Evaluate/intrinsics.cpp (+21-1) 
- (modified) flang/lib/Optimizer/Builder/IntrinsicCall.cpp (+12) 
- (added) flang/test/Lower/Intrinsics/erf.f90 (+22) 
- (modified) flang/test/Lower/Intrinsics/erf_real16.f90 (+3-1) 
- (modified) flang/test/Lower/Intrinsics/erfc.f90 (+16) 
- (modified) flang/test/Lower/Intrinsics/erfc_real16.f90 (+3-1) 
- (modified) flang/test/Lower/Intrinsics/erfc_scaled.f90 (+20) 
- (added) flang/test/Semantics/erf.f90 (+29) 
- (added) flang/test/Semantics/erfc.f90 (+29) 
- (added) flang/test/Semantics/erfc_scaled.f90 (+29) 


``````````diff
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

``````````

</details>


https://github.com/llvm/llvm-project/pull/125217


More information about the flang-commits mailing list