[Mlir-commits] [flang] [mlir] [Flang][OpenACC] add loop annotations to acc.loop (PR #216769)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Mon Aug 17 09:49:46 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-openacc

Author: Scott Manley (rscottmanley)

<details>
<summary>Changes</summary>

Add loop annotations that come from directives to acc.loop just like other loops

---

Patch is 23.73 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/216769.diff


8 Files Affected:

- (modified) flang/docs/Directives.md (+3-2) 
- (added) flang/include/flang/Lower/Support/LoopAnnotation.h (+36) 
- (modified) flang/lib/Lower/Bridge.cpp (+48-157) 
- (modified) flang/lib/Lower/CMakeLists.txt (+1) 
- (modified) flang/lib/Lower/OpenACC.cpp (+46-6) 
- (added) flang/lib/Lower/Support/LoopAnnotation.cpp (+156) 
- (added) flang/test/Lower/OpenACC/acc-loop-directive-annotation.f90 (+17) 
- (modified) mlir/lib/Dialect/OpenACC/Utils/OpenACCUtilsTiling.cpp (+2) 


``````````diff
diff --git a/flang/docs/Directives.md b/flang/docs/Directives.md
index 45080acb778e3..e74db041d01f3 100644
--- a/flang/docs/Directives.md
+++ b/flang/docs/Directives.md
@@ -171,8 +171,9 @@ directives. Directives on loops are used to specify additional transformation to
 be performed by the compiler like enabling vectorisation, unrolling, interchange
 etc.
 
-Currently loop directives are not accepted in the presence of OpenMP or OpenACC
-constructs on the loop. This should be implemented as it is used in some
+Currently loop directives are accepted in the presence of OpenACC loop and
+combined constructs on the loop. Loop directives with OpenMP constructs on the
+loop are not yet accepted. This should be implemented as it is used in some
 applications.
 
 ### Array Expressions
diff --git a/flang/include/flang/Lower/Support/LoopAnnotation.h b/flang/include/flang/Lower/Support/LoopAnnotation.h
new file mode 100644
index 0000000000000..9ff1ec5c521fe
--- /dev/null
+++ b/flang/include/flang/Lower/Support/LoopAnnotation.h
@@ -0,0 +1,36 @@
+//===-- Lower/Support/LoopAnnotation.h -- loop annotation attrs -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Helpers to lower Fortran `!dir$` loop directives to LLVM LoopAnnotationAttr.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef FORTRAN_LOWER_SUPPORT_LOOPANNOTATION_H
+#define FORTRAN_LOWER_SUPPORT_LOOPANNOTATION_H
+
+#include "mlir/Dialect/LLVMIR/LLVMAttrs.h"
+#include "llvm/ADT/ArrayRef.h"
+
+namespace Fortran {
+namespace parser {
+struct CompilerDirective;
+} // namespace parser
+
+namespace lower {
+
+/// Build an LLVM loop annotation attribute from Fortran compiler directives
+/// associated with a loop. Returns null if \p dirs does not contain any
+/// recognized loop directives.
+mlir::LLVM::LoopAnnotationAttr genLoopAnnotationAttr(
+    mlir::MLIRContext *context,
+    llvm::ArrayRef<const Fortran::parser::CompilerDirective *> dirs);
+
+} // namespace lower
+} // namespace Fortran
+
+#endif // FORTRAN_LOWER_SUPPORT_LOOPANNOTATION_H
diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index bff6b51e50e18..a0f39d51727d0 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -30,6 +30,7 @@
 #include "flang/Lower/PFTBuilder.h"
 #include "flang/Lower/Runtime.h"
 #include "flang/Lower/StatementContext.h"
+#include "flang/Lower/Support/LoopAnnotation.h"
 #include "flang/Lower/Support/ReductionProcessor.h"
 #include "flang/Lower/Support/Utils.h"
 #include "flang/Optimizer/Builder/BoxValue.h"
@@ -2859,164 +2860,21 @@ class FirConverter : public Fortran::lower::AbstractConverter {
   // For unroll directives without a value, force full unrolling.
   // For unroll directives with a value, if the value is greater than 1,
   // force unrolling with the given factor. Otherwise, disable unrolling.
-  mlir::LLVM::LoopUnrollAttr
-  genLoopUnrollAttr(std::optional<std::uint64_t> directiveArg) {
-    mlir::BoolAttr falseAttr =
-        mlir::BoolAttr::get(builder->getContext(), false);
-    mlir::BoolAttr trueAttr = mlir::BoolAttr::get(builder->getContext(), true);
-    mlir::IntegerAttr countAttr;
-    mlir::BoolAttr fullUnrollAttr;
-    bool shouldUnroll = true;
-    if (directiveArg.has_value()) {
-      auto unrollingFactor = directiveArg.value();
-      if (unrollingFactor == 0 || unrollingFactor == 1) {
-        shouldUnroll = false;
-      } else {
-        countAttr =
-            builder->getIntegerAttr(builder->getI64Type(), unrollingFactor);
-      }
-    } else {
-      fullUnrollAttr = trueAttr;
-    }
-
-    mlir::BoolAttr disableAttr = shouldUnroll ? falseAttr : trueAttr;
-    return mlir::LLVM::LoopUnrollAttr::get(
-        builder->getContext(), /*disable=*/disableAttr, /*count=*/countAttr, {},
-        /*full=*/fullUnrollAttr, {}, {}, {});
-  }
-
-  // Enabling unroll and jamming directive without a value.
-  // For directives with a value, if the value is greater than 1,
-  // force unrolling with the given factor. Otherwise, disable unrolling and
-  // jamming.
-  mlir::LLVM::LoopUnrollAndJamAttr
-  genLoopUnrollAndJamAttr(std::optional<std::uint64_t> count) {
-    mlir::BoolAttr falseAttr =
-        mlir::BoolAttr::get(builder->getContext(), false);
-    mlir::BoolAttr trueAttr = mlir::BoolAttr::get(builder->getContext(), true);
-    mlir::IntegerAttr countAttr;
-    bool shouldUnroll = true;
-    if (count.has_value()) {
-      auto unrollingFactor = count.value();
-      if (unrollingFactor == 0 || unrollingFactor == 1) {
-        shouldUnroll = false;
-      } else {
-        countAttr =
-            builder->getIntegerAttr(builder->getI64Type(), unrollingFactor);
-      }
-    }
-
-    mlir::BoolAttr disableAttr = shouldUnroll ? falseAttr : trueAttr;
-    return mlir::LLVM::LoopUnrollAndJamAttr::get(
-        builder->getContext(), /*disable=*/disableAttr, /*count*/ countAttr, {},
-        {}, {}, {}, {});
-  }
-
-  // Enabling loop vectorization attribute.
-  mlir::LLVM::LoopVectorizeAttr
-  genLoopVectorizeAttr(mlir::BoolAttr disableAttr,
-                       mlir::BoolAttr scalableEnable,
-                       mlir::IntegerAttr vectorWidth) {
-    mlir::LLVM::LoopVectorizeAttr va;
-    if (disableAttr)
-      va = mlir::LLVM::LoopVectorizeAttr::get(
-          builder->getContext(),
-          /*disable=*/disableAttr, /*predicate=*/{},
-          /*scalableEnable=*/scalableEnable,
-          /*vectorWidth=*/vectorWidth, {}, {}, {});
-    return va;
-  }
-
+  // Enabling unroll and jamming / vectorization is handled in
+  // genLoopAnnotationAttr.
   void addLoopAnnotationAttr(
       IncrementLoopInfo &info,
       llvm::SmallVectorImpl<const Fortran::parser::CompilerDirective *> &dirs) {
-    mlir::BoolAttr disableVecAttr;
-    mlir::BoolAttr scalableEnable;
-    mlir::IntegerAttr vectorWidth;
-    mlir::LLVM::LoopUnrollAttr ua;
-    mlir::LLVM::LoopUnrollAndJamAttr uja;
-    llvm::SmallVector<mlir::LLVM::AccessGroupAttr> aga;
-    bool has_attrs = false;
-    for (const auto *dir : dirs) {
-      Fortran::common::visit(
-          Fortran::common::visitors{
-              [&](const Fortran::parser::CompilerDirective::VectorAlways &) {
-                disableVecAttr =
-                    mlir::BoolAttr::get(builder->getContext(), false);
-                has_attrs = true;
-              },
-              [&](const Fortran::parser::CompilerDirective::VectorLength &vl) {
-                using Kind =
-                    Fortran::parser::CompilerDirective::VectorLength::Kind;
-                Kind kind = std::get<Kind>(vl.t);
-                uint64_t length = std::get<uint64_t>(vl.t);
-                disableVecAttr =
-                    mlir::BoolAttr::get(builder->getContext(), false);
-                if (length != 0)
-                  vectorWidth =
-                      builder->getIntegerAttr(builder->getI64Type(), length);
-                switch (kind) {
-                case Kind::Scalable:
-                  scalableEnable =
-                      mlir::BoolAttr::get(builder->getContext(), true);
-                  break;
-                case Kind::Fixed:
-                  scalableEnable =
-                      mlir::BoolAttr::get(builder->getContext(), false);
-                  break;
-                case Kind::Auto:
-                  break;
-                }
-                has_attrs = true;
-              },
-              [&](const Fortran::parser::CompilerDirective::Unroll &u) {
-                ua = genLoopUnrollAttr(u.v);
-                has_attrs = true;
-              },
-              [&](const Fortran::parser::CompilerDirective::UnrollAndJam &u) {
-                uja = genLoopUnrollAndJamAttr(u.v);
-                has_attrs = true;
-              },
-              [&](const Fortran::parser::CompilerDirective::NoVector &u) {
-                disableVecAttr =
-                    mlir::BoolAttr::get(builder->getContext(), true);
-                has_attrs = true;
-              },
-              [&](const Fortran::parser::CompilerDirective::NoUnroll &u) {
-                ua = genLoopUnrollAttr(/*unrollingFactor=*/0);
-                has_attrs = true;
-              },
-              [&](const Fortran::parser::CompilerDirective::NoUnrollAndJam &u) {
-                uja = genLoopUnrollAndJamAttr(/*unrollingFactor=*/0);
-                has_attrs = true;
-              },
-              [&](const Fortran::parser::CompilerDirective::IVDep &iv) {
-                aga.push_back(
-                    mlir::LLVM::AccessGroupAttr::get(builder->getContext()));
-                has_attrs = true;
-              },
-              [&](const Fortran::parser::CompilerDirective::Simd &simd) {
-                disableVecAttr =
-                    mlir::BoolAttr::get(builder->getContext(), false);
-                has_attrs = true;
-              },
-              [&](const auto &) {}},
-          dir->u);
-    }
-    mlir::LLVM::LoopVectorizeAttr va =
-        genLoopVectorizeAttr(disableVecAttr, scalableEnable, vectorWidth);
-    mlir::LLVM::LoopAnnotationAttr la = mlir::LLVM::LoopAnnotationAttr::get(
-        builder->getContext(), {}, /*vectorize=*/va, {}, /*unroll*/ ua,
-        /*unroll_and_jam*/ uja, {}, {}, {}, {}, {}, {}, {}, {}, {},
-        /*parallelAccesses*/ aga);
-    if (has_attrs) {
-      if (auto loopOp = mlir::dyn_cast<fir::DoLoopOp>(info.loopOp))
-        loopOp.setLoopAnnotationAttr(la);
+    mlir::LLVM::LoopAnnotationAttr la =
+        Fortran::lower::genLoopAnnotationAttr(builder->getContext(), dirs);
+    if (!la)
+      return;
+    if (auto loopOp = mlir::dyn_cast<fir::DoLoopOp>(info.loopOp))
+      loopOp.setLoopAnnotationAttr(la);
 
-      if (auto doConcurrentOp =
-              mlir::dyn_cast<fir::DoConcurrentLoopOp>(info.loopOp))
-        doConcurrentOp.setLoopAnnotationAttr(la);
-    }
+    if (auto doConcurrentOp =
+            mlir::dyn_cast<fir::DoConcurrentLoopOp>(info.loopOp))
+      doConcurrentOp.setLoopAnnotationAttr(la);
   }
 
   /// Generate FIR to begin a structured or unstructured increment loop nest.
@@ -3548,11 +3406,44 @@ class FirConverter : public Fortran::lower::AbstractConverter {
 
   void attachDirectiveToLoop(const Fortran::parser::CompilerDirective &dir,
                              Fortran::lower::pft::Evaluation *e) {
-    while (e->isDirective())
+    auto attachToDoStmt = [&](Fortran::lower::pft::Evaluation *doEval) {
+      if (doEval && doEval->isA<Fortran::parser::NonLabelDoStmt>())
+        doEval->dirs.push_back(&dir);
+    };
+
+    // Dive into an OpenACC loop/combined construct to find its NonLabelDoStmt.
+    auto findDoStmtInOpenACC = [](Fortran::lower::pft::Evaluation *eval)
+        -> Fortran::lower::pft::Evaluation * {
+      const auto *acc = eval->getIf<Fortran::parser::OpenACCConstruct>();
+      if (!acc)
+        return nullptr;
+      if (!std::holds_alternative<Fortran::parser::OpenACCLoopConstruct>(
+              acc->u) &&
+          !std::holds_alternative<Fortran::parser::OpenACCCombinedConstruct>(
+              acc->u))
+        return nullptr;
+      if (!eval->hasNestedEvaluations())
+        return nullptr;
+      for (Fortran::lower::pft::Evaluation &nested :
+           eval->getNestedEvaluations()) {
+        if (nested.isA<Fortran::parser::DoConstruct>() &&
+            nested.hasNestedEvaluations())
+          return &nested.getFirstNestedEvaluation();
+      }
+      return nullptr;
+    };
+
+    while (e->isDirective()) {
+      if (Fortran::lower::pft::Evaluation *doStmt = findDoStmtInOpenACC(e)) {
+        attachToDoStmt(doStmt);
+        return;
+      }
       e = e->lexicalSuccessor;
+      if (!e)
+        return;
+    }
 
-    if (e->isA<Fortran::parser::NonLabelDoStmt>())
-      e->dirs.push_back(&dir);
+    attachToDoStmt(e);
   }
 
   void markCurrentFuncAsAlwaysInline(
diff --git a/flang/lib/Lower/CMakeLists.txt b/flang/lib/Lower/CMakeLists.txt
index 584e4a28e5641..246b6e2f5adff 100644
--- a/flang/lib/Lower/CMakeLists.txt
+++ b/flang/lib/Lower/CMakeLists.txt
@@ -30,6 +30,7 @@ add_flang_library(FortranLower
   OpenMP/Utils.cpp
   PFTBuilder.cpp
   Runtime.cpp
+  Support/LoopAnnotation.cpp
   Support/PrivateReductionUtils.cpp
   Support/ReductionProcessor.cpp
   Support/Utils.cpp
diff --git a/flang/lib/Lower/OpenACC.cpp b/flang/lib/Lower/OpenACC.cpp
index 2ea3659e8064f..73f1c18bfaac2 100644
--- a/flang/lib/Lower/OpenACC.cpp
+++ b/flang/lib/Lower/OpenACC.cpp
@@ -20,6 +20,7 @@
 #include "flang/Lower/Mangler.h"
 #include "flang/Lower/PFTBuilder.h"
 #include "flang/Lower/StatementContext.h"
+#include "flang/Lower/Support/LoopAnnotation.h"
 #include "flang/Lower/Support/Utils.h"
 #include "flang/Lower/SymbolMap.h"
 #include "flang/Optimizer/Builder/BoxValue.h"
@@ -2235,6 +2236,32 @@ static bool hasEarlyReturn(Fortran::lower::pft::Evaluation &eval) {
   return hasReturnStmt;
 }
 
+/// Return the NonLabelDoStmt evaluation associated with an OpenACC loop or
+/// a DoConstruct being lowered as an acc.loop.
+static Fortran::lower::pft::Evaluation *
+getAccLoopDoStmtEval(Fortran::lower::pft::Evaluation &eval) {
+  Fortran::lower::pft::Evaluation *e = &eval;
+  if (e->isA<Fortran::parser::OpenACCConstruct>()) {
+    if (!e->hasNestedEvaluations())
+      return nullptr;
+    e = nullptr;
+    for (Fortran::lower::pft::Evaluation &nested :
+         eval.getNestedEvaluations()) {
+      if (nested.isA<Fortran::parser::DoConstruct>()) {
+        e = &nested;
+        break;
+      }
+    }
+    if (!e)
+      return nullptr;
+  }
+  if (e->isA<Fortran::parser::DoConstruct>() && e->hasNestedEvaluations())
+    return &e->getFirstNestedEvaluation();
+  if (e->isA<Fortran::parser::NonLabelDoStmt>())
+    return e;
+  return nullptr;
+}
+
 static mlir::acc::LoopOp createLoopOp(
     Fortran::lower::AbstractConverter &converter,
     mlir::Location currentLocation,
@@ -2503,12 +2530,15 @@ static mlir::acc::LoopOp createLoopOp(
     loopOp.setCombinedAttr(mlir::acc::CombinedConstructsTypeAttr::get(
         builder.getContext(), *combinedConstructs));
 
-  // TODO: retrieve directives from NonLabelDoStmt pft::Evaluation, and add them
-  // as attribute to the acc.loop as an extra attribute. It is not quite clear
-  // how useful these $dir are in acc contexts, but they could still provide
-  // more information about the loop acc codegen. They can be obtained by
-  // looking for the first lexicalSuccessor of eval that is a NonLabelDoStmt,
-  // and using the related `dirs` member.
+  // Apply `!dir$` loop directives associated with the DO statement as a
+  // discardable LLVM loop annotation attribute on the acc.loop.
+  if (Fortran::lower::pft::Evaluation *doStmtEval =
+          getAccLoopDoStmtEval(eval)) {
+    if (mlir::LLVM::LoopAnnotationAttr la =
+            Fortran::lower::genLoopAnnotationAttr(builder.getContext(),
+                                                  doStmtEval->dirs))
+      loopOp->setDiscardableAttr("loopAnnotation", la);
+  }
 
   return loopOp;
 }
@@ -5436,5 +5466,15 @@ mlir::Operation *Fortran::lower::genOpenACCLoopFromDoConstruct(
     llvm_unreachable("Unexpected loop par mode");
   }
 
+  // Apply `!dir$` loop directives associated with the DO statement as a
+  // discardable LLVM loop annotation attribute on the acc.loop.
+  if (Fortran::lower::pft::Evaluation *doStmtEval =
+          getAccLoopDoStmtEval(eval)) {
+    if (mlir::LLVM::LoopAnnotationAttr la =
+            Fortran::lower::genLoopAnnotationAttr(builder.getContext(),
+                                                  doStmtEval->dirs))
+      loopOp->setDiscardableAttr("loopAnnotation", la);
+  }
+
   return loopOp;
 }
diff --git a/flang/lib/Lower/Support/LoopAnnotation.cpp b/flang/lib/Lower/Support/LoopAnnotation.cpp
new file mode 100644
index 0000000000000..976d7495e78dc
--- /dev/null
+++ b/flang/lib/Lower/Support/LoopAnnotation.cpp
@@ -0,0 +1,156 @@
+//===-- Lower/Support/LoopAnnotation.cpp ----------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "flang/Lower/Support/LoopAnnotation.h"
+#include "flang/Common/idioms.h"
+#include "flang/Parser/parse-tree.h"
+#include "mlir/IR/BuiltinAttributes.h"
+
+static mlir::LLVM::LoopUnrollAttr
+genLoopUnrollAttr(mlir::MLIRContext *context,
+                  std::optional<std::uint64_t> directiveArg) {
+  mlir::BoolAttr falseAttr = mlir::BoolAttr::get(context, false);
+  mlir::BoolAttr trueAttr = mlir::BoolAttr::get(context, true);
+  mlir::IntegerAttr countAttr;
+  mlir::BoolAttr fullUnrollAttr;
+  bool shouldUnroll = true;
+  if (directiveArg.has_value()) {
+    auto unrollingFactor = directiveArg.value();
+    if (unrollingFactor == 0 || unrollingFactor == 1) {
+      shouldUnroll = false;
+    } else {
+      countAttr = mlir::IntegerAttr::get(mlir::IntegerType::get(context, 64),
+                                         unrollingFactor);
+    }
+  } else {
+    fullUnrollAttr = trueAttr;
+  }
+
+  mlir::BoolAttr disableAttr = shouldUnroll ? falseAttr : trueAttr;
+  return mlir::LLVM::LoopUnrollAttr::get(context, /*disable=*/disableAttr,
+                                         /*count=*/countAttr, {},
+                                         /*full=*/fullUnrollAttr, {}, {}, {});
+}
+
+static mlir::LLVM::LoopUnrollAndJamAttr
+genLoopUnrollAndJamAttr(mlir::MLIRContext *context,
+                        std::optional<std::uint64_t> count) {
+  mlir::BoolAttr falseAttr = mlir::BoolAttr::get(context, false);
+  mlir::BoolAttr trueAttr = mlir::BoolAttr::get(context, true);
+  mlir::IntegerAttr countAttr;
+  bool shouldUnroll = true;
+  if (count.has_value()) {
+    auto unrollingFactor = count.value();
+    if (unrollingFactor == 0 || unrollingFactor == 1) {
+      shouldUnroll = false;
+    } else {
+      countAttr = mlir::IntegerAttr::get(mlir::IntegerType::get(context, 64),
+                                         unrollingFactor);
+    }
+  }
+
+  mlir::BoolAttr disableAttr = shouldUnroll ? falseAttr : trueAttr;
+  return mlir::LLVM::LoopUnrollAndJamAttr::get(context, /*disable=*/disableAttr,
+                                               /*count*/ countAttr, {}, {}, {},
+                                               {}, {});
+}
+
+static mlir::LLVM::LoopVectorizeAttr
+genLoopVectorizeAttr(mlir::MLIRContext *context, mlir::BoolAttr disableAttr,
+                     mlir::BoolAttr scalableEnable,
+                     mlir::IntegerAttr vectorWidth) {
+  mlir::LLVM::LoopVectorizeAttr va;
+  if (disableAttr)
+    va = mlir::LLVM::LoopVectorizeAttr::get(context,
+                                            /*disable=*/disableAttr,
+                                            /*predicate=*/{},
+                                            /*scalableEnable=*/scalableEnable,
+                                            /*vectorWidth=*/vectorWidth, {}, {},
+                                            {});
+  return va;
+}
+
+mlir::LLVM::LoopAnnotationAttr Fortran::lower::genLoopAnnotationAttr(
+    mlir::MLIRContext *context,
+    llvm::ArrayRef<const Fortran::parser::CompilerDirective *> dirs) {
+  mlir::BoolAttr disableVecAttr;
+  mlir::BoolAttr scalableEnable;
+  mlir::IntegerAttr vectorWidth;
+  mlir::LLVM::LoopUnrollAttr ua;
+  mlir::LLVM::LoopUnrollAndJamAttr uja;
+  llvm::SmallVector<mlir::LLVM::AccessGroupAttr> aga;
+  bool hasAttrs = false;
+  for (const auto *dir : dirs) {
+    Fortran::common::visit(
+        Fortran::common::visitors{
+            [&](const Fortran::parser::CompilerDirective::VectorAlways &) {
+              disableVecAttr = mlir::BoolAttr::get(context, false);
+              hasAttrs = true;
+            },
+            [&](const Fortran::parser::CompilerDirective::VectorLength &vl) {
+              using Kind =
+                  Fortran::parser::CompilerDirective::VectorLength::Kind;
+              Kind kind = std::get<Kind>(vl.t);
+              uint64_t length = std::get<uint64_t>(vl.t);
+              disableV...
[truncated]

``````````

</details>


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


More information about the Mlir-commits mailing list