[clang] 292670a - [CIR] Handle label address difference (#205437)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Aug 13 16:50:29 PDT 2026
Author: Andy Kaylor
Date: 2026-08-13T16:50:25-07:00
New Revision: 292670acdb5116b85ad38fe55d585fadd4bd479e
URL: https://github.com/llvm/llvm-project/commit/292670acdb5116b85ad38fe55d585fadd4bd479e
DIFF: https://github.com/llvm/llvm-project/commit/292670acdb5116b85ad38fe55d585fadd4bd479e.diff
LOG: [CIR] Handle label address difference (#205437)
This change adds handling for emitting AddrLabelDiff constants. These
constants can be used to initialize static variables within a function
by computing the difference between the addresses of two labels. These
constant values are represented in the AST using the APValue class. The
code generator needs to emit them as initializers for the global
corresponding to the static variable.
This change introduces a new CIR attribute type, BlockAddrDiffAttr to
represent this constant value, deferring the label block address
resolution until we lower the initializer to the LLVM dialect.
Assisted-by: Cursor / claude-opus-4.8
Added:
Modified:
clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
clang/lib/CIR/Dialect/IR/CIRDialect.cpp
clang/lib/CIR/Dialect/Transforms/GotoSolver.cpp
clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
clang/lib/CIR/Lowering/LoweringHelpers.cpp
clang/test/CIR/CodeGen/const-label-addr.c
Removed:
################################################################################
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
index f6f08d29cffe5..29e82b9392b82 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
@@ -1785,6 +1785,53 @@ def CIR_BlockAddrInfoAttr
let canHaveIllegalCXXABIType = 0;
}
+//===----------------------------------------------------------------------===//
+// CIR_BlockAddrDiffAttr
+//===----------------------------------------------------------------------===//
+
+def CIR_BlockAddrDiffAttr
+ : CIR_ValueLikeAttr<"BlockAddrDiff", "block_addr_
diff "> {
+ let summary = "Difference between two block addresses";
+ let description = [{
+ This attribute represents the constant
diff erence between the addresses of
+ two basic blocks within the same function. It is produced for GCC's "labels
+ as values" extension when the
diff erence of two label addresses appears in a
+ constant context, e.g. `&&lhs - &&rhs`.
+
+ Both labels belong to the function referenced by `func`. The value is the
+ address of `lhs_label` minus the address of `rhs_label`, truncated to the
+ attribute's integer type.
+
+ Example:
+ ```
+ cir.global "private" internal @b.ar =
+ #cir.block_addr_
diff <@b, "l2", "l1"> : !s32i
+ ```
+ }];
+
+ let parameters = (ins
+ AttributeSelfTypeParameter<"", "cir::IntType">:$type,
+ "mlir::FlatSymbolRefAttr":$func,
+ "mlir::StringAttr":$lhs_label,
+ "mlir::StringAttr":$rhs_label);
+
+ let assemblyFormat = "`<` $func `,` $lhs_label `,` $rhs_label `>`";
+
+ let builders = [
+ AttrBuilder<(ins "cir::IntType":$type,
+ "llvm::StringRef":$func_name,
+ "llvm::StringRef":$lhs_label_name,
+ "llvm::StringRef":$rhs_label_name), [{
+ return $_get($_ctxt, type,
+ mlir::FlatSymbolRefAttr::get($_ctxt, func_name),
+ mlir::StringAttr::get($_ctxt, lhs_label_name),
+ mlir::StringAttr::get($_ctxt, rhs_label_name));
+ }]>
+ ];
+
+ let canHaveIllegalCXXABIType = 0;
+}
+
//===----------------------------------------------------------------------===//
// Side Effect
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
index 45868fba962aa..93d4fee235bad 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
@@ -1513,10 +1513,24 @@ mlir::Attribute ConstantEmitter::tryEmitPrivate(const APValue &value,
cir::FPAttr::get(complexElemTy, imag));
}
case APValue::FixedPoint:
- case APValue::AddrLabelDiff:
- cgm.errorNYI(
- "ConstExprEmitter::tryEmitPrivate fixed point, addr label
diff ");
+ cgm.errorNYI("ConstExprEmitter::tryEmitPrivate fixed point");
return {};
+ case APValue::AddrLabelDiff: {
+ const AddrLabelExpr *lhsExpr = value.getAddrLabelDiffLHS();
+ const AddrLabelExpr *rhsExpr = value.getAddrLabelDiffRHS();
+
+ // Both labels belong to the function currently being emitted. The actual
+ // subtraction (ptrtoint of each block address, subtract, then truncate to
+ // the result type) is deferred to the LowerToLLVM pass, which is where
+ // block addresses are resolved to concrete basic blocks.
+ mlir::Type resultType = cgm.getTypes().convertType(destType);
+ auto intResultType = mlir::cast<cir::IntType>(resultType);
+ auto func = cast<cir::FuncOp>(cgf->curFn);
+ return cir::BlockAddrDiffAttr::get(
+ builder.getContext(), intResultType, func.getSymName(),
+ lhsExpr->getLabel()->getName(), rhsExpr->getLabel()->getName());
+ }
+
case APValue::Matrix:
cgm.errorNYI("ConstExprEmitter::tryEmitPrivate matrix");
return {};
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index a759e087afdc8..1117a8da0c4c1 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -617,10 +617,11 @@ static LogicalResult checkConstantTypes(mlir::Operation *op, mlir::Type opType,
return success();
}
- if (mlir::isa<cir::BlockAddrInfoAttr, cir::ConstArrayAttr,
- cir::ConstVectorAttr, cir::ConstComplexAttr,
- cir::ConstRecordAttr, cir::GlobalViewAttr, cir::PoisonAttr,
- cir::TypeInfoAttr, cir::VTableAttr>(attrType))
+ if (mlir::isa<cir::BlockAddrDiffAttr, cir::BlockAddrInfoAttr,
+ cir::ConstArrayAttr, cir::ConstVectorAttr,
+ cir::ConstComplexAttr, cir::ConstRecordAttr,
+ cir::GlobalViewAttr, cir::PoisonAttr, cir::TypeInfoAttr,
+ cir::VTableAttr>(attrType))
return success();
assert(isa<TypedAttr>(attrType) && "What else could we be looking at here?");
diff --git a/clang/lib/CIR/Dialect/Transforms/GotoSolver.cpp b/clang/lib/CIR/Dialect/Transforms/GotoSolver.cpp
index 2ecef988ca58e..5f911f3de10a0 100644
--- a/clang/lib/CIR/Dialect/Transforms/GotoSolver.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/GotoSolver.cpp
@@ -128,6 +128,14 @@ void GotoSolverPass::runOnOperation() {
globalBlockAddrLabels[info.getFunc().getValue()].insert(
info.getLabel());
});
+ // A block-address
diff erence attribute references two labels in the same
+ // function; keep both alive.
+ namedAttr.getValue().walk([&](cir::BlockAddrDiffAttr
diff ) {
+ llvm::SmallSetVector<StringRef, 4> &labels =
+ globalBlockAddrLabels[
diff .getFunc().getValue()];
+ labels.insert(
diff .getLhsLabel().getValue());
+ labels.insert(
diff .getRhsLabel().getValue());
+ });
}
});
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index dadfde1865133..4ae4b7693d982 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -723,6 +723,58 @@ mlir::Value CIRAttrToValue::visitCirAttr(cir::BlockAddrInfoAttr blockAddrInfo) {
return blockAddressOp;
}
+/// BlockAddrDiffAttr visitor.
+mlir::Value CIRAttrToValue::visitCirAttr(cir::BlockAddrDiffAttr blockAddrDiff) {
+ assert(blockInfoAddr &&
+ "block address lowering requires LLVMBlockAddressInfo");
+ // A block-address
diff erence initializer is lowered to the
diff erence of the
+ // two block addresses: trunc(ptrtoint(lhs) - ptrtoint(rhs)). Just like a
+ // single block address, each referenced block tag may not have been emitted
+ // yet, in which case it is recorded as unresolved and patched up later in
+ // resolveBlockAddressOp.
+ mlir::Location loc = parentOp->getLoc();
+ mlir::DataLayout layout(parentOp->getParentOfType<mlir::ModuleOp>());
+ mlir::MLIRContext *ctx = rewriter.getContext();
+ auto ptrTy = mlir::LLVM::LLVMPointerType::get(ctx);
+
+ auto emitBlockAddr = [&](mlir::StringAttr label) -> mlir::Value {
+ auto info = cir::BlockAddrInfoAttr::get(
+ ctx, blockAddrDiff.getFunc().getValue(), label.getValue());
+ mlir::LLVM::BlockTagOp matchLabel = blockInfoAddr->lookupBlockTag(info);
+ mlir::LLVM::BlockTagAttr tagAttr =
+ matchLabel ? matchLabel.getTag() : mlir::LLVM::BlockTagAttr{};
+ auto blkAddr = mlir::LLVM::BlockAddressAttr::get(
+ ctx, blockAddrDiff.getFunc(), tagAttr);
+ auto addrOp =
+ mlir::LLVM::BlockAddressOp::create(rewriter, loc, ptrTy, blkAddr);
+ if (!matchLabel)
+ blockInfoAddr->addUnresolvedBlockAddress(addrOp, info);
+ return addrOp;
+ };
+
+ mlir::Value lhsAddr = emitBlockAddr(blockAddrDiff.getLhsLabel());
+ mlir::Value rhsAddr = emitBlockAddr(blockAddrDiff.getRhsLabel());
+
+ // Compute the
diff erence in a pointer-sized integer, then truncate to the
+ // initializer's type. LLVM is sensitive about the exact format of the
+ // address-of-label
diff erence, so the truncation must happen after the
+ // subtraction.
+ mlir::Type intptrTy =
+ rewriter.getIntegerType(layout.getTypeSizeInBits(ptrTy));
+ mlir::Value lhsInt =
+ mlir::LLVM::PtrToIntOp::create(rewriter, loc, intptrTy, lhsAddr);
+ mlir::Value rhsInt =
+ mlir::LLVM::PtrToIntOp::create(rewriter, loc, intptrTy, rhsAddr);
+ mlir::Value
diff Val =
+ mlir::LLVM::SubOp::create(rewriter, loc, lhsInt, rhsInt);
+
+ mlir::Type resultTy = converter->convertType(blockAddrDiff.getType());
+ mlir::Value result =
diff Val;
+ if (resultTy != intptrTy)
+ result = mlir::LLVM::TruncOp::create(rewriter, loc, resultTy,
diff Val);
+ return result;
+}
+
// ConstArrayAttr visitor
mlir::Value CIRAttrToValue::visitCirAttr(cir::ConstArrayAttr attr) {
mlir::Type llvmTy = converter->convertType(attr.getType());
@@ -2868,10 +2920,12 @@ CIRToLLVMGlobalOpLowering::matchAndRewriteRegionInitializedGlobal(
cir::GlobalOp op, mlir::Attribute init,
mlir::ConversionPatternRewriter &rewriter) const {
// TODO: Generalize this handling when more types are needed here.
- assert((isa<cir::BlockAddrInfoAttr, cir::ConstArrayAttr, cir::ConstRecordAttr,
- cir::ConstVectorAttr, cir::ConstPtrAttr, cir::ConstComplexAttr,
- cir::GlobalViewAttr, cir::TypeInfoAttr, cir::UndefAttr,
- cir::PoisonAttr, cir::VTableAttr, cir::ZeroAttr>(init)));
+ assert(
+ (isa<cir::BlockAddrDiffAttr, cir::BlockAddrInfoAttr, cir::ConstArrayAttr,
+ cir::ConstRecordAttr, cir::ConstVectorAttr, cir::ConstPtrAttr,
+ cir::ConstComplexAttr, cir::GlobalViewAttr, cir::TypeInfoAttr,
+ cir::UndefAttr, cir::PoisonAttr, cir::VTableAttr, cir::ZeroAttr>(
+ init)));
// TODO(cir): once LLVM's dialect has proper equivalent attributes this
// should be updated. For now, we use a custom op to initialize globals
@@ -3000,11 +3054,12 @@ mlir::LogicalResult CIRToLLVMGlobalOpLowering::matchAndRewrite(
return mlir::success();
}
return matchAndRewriteRegionInitializedGlobal(op, init.value(), rewriter);
- } else if (mlir::isa<cir::BlockAddrInfoAttr, cir::ConstVectorAttr,
- cir::ConstRecordAttr, cir::ConstPtrAttr,
- cir::ConstComplexAttr, cir::GlobalViewAttr,
- cir::TypeInfoAttr, cir::UndefAttr, cir::PoisonAttr,
- cir::VTableAttr, cir::ZeroAttr>(init.value())) {
+ } else if (mlir::isa<cir::BlockAddrDiffAttr, cir::BlockAddrInfoAttr,
+ cir::ConstVectorAttr, cir::ConstRecordAttr,
+ cir::ConstPtrAttr, cir::ConstComplexAttr,
+ cir::GlobalViewAttr, cir::TypeInfoAttr, cir::UndefAttr,
+ cir::PoisonAttr, cir::VTableAttr, cir::ZeroAttr>(
+ init.value())) {
// TODO(cir): once LLVM's dialect has proper equivalent attributes this
// should be updated. For now, we use a custom op to initialize globals
// to the appropriate value.
diff --git a/clang/lib/CIR/Lowering/LoweringHelpers.cpp b/clang/lib/CIR/Lowering/LoweringHelpers.cpp
index 59677a1aacefd..3b927ad759c7a 100644
--- a/clang/lib/CIR/Lowering/LoweringHelpers.cpp
+++ b/clang/lib/CIR/Lowering/LoweringHelpers.cpp
@@ -216,6 +216,25 @@ static bool containsPoison(mlir::Attribute attr) {
return false;
}
+/// Block-address attributes (address-of-label and label
diff erences) are
+/// lowered to relocation expressions that cannot be materialized as part of a
+/// dense/aggregate constant attribute; they require the per-element
+/// insertvalue region lowering. Return true if \p attr contains any such
+/// element.
+static bool containsBlockAddress(mlir::Attribute attr) {
+ if (mlir::isa<cir::BlockAddrInfoAttr, cir::BlockAddrDiffAttr>(attr))
+ return true;
+ if (auto elts = mlir::dyn_cast<mlir::ArrayAttr>(attr))
+ return llvm::any_of(elts, containsBlockAddress);
+ if (auto constArr = mlir::dyn_cast<cir::ConstArrayAttr>(attr)) {
+ if (mlir::isa<mlir::StringAttr>(constArr.getElts()))
+ return false;
+ if (auto elts = mlir::dyn_cast<mlir::ArrayAttr>(constArr.getElts()))
+ return llvm::any_of(elts, containsBlockAddress);
+ }
+ return false;
+}
+
static std::optional<mlir::Attribute> lowerConstRecordMemberAttr(
mlir::Attribute attr, mlir::SymbolTableCollection &symbolTables,
const mlir::TypeConverter *converter, mlir::ModuleOp moduleOp);
@@ -240,6 +259,11 @@ std::optional<mlir::Attribute> lowerConstArrayAttr(
if (containsPoison(constArr))
return std::nullopt;
+ // Block-address initializers cannot be represented as a dense/aggregate
+ // constant attribute; fall back to the per-element insertvalue lowering.
+ if (containsBlockAddress(constArr))
+ return std::nullopt;
+
if (mlir::isa<mlir::StringAttr>(constArr.getElts()))
return convertStringAttrToDenseElementsAttr(constArr,
converter->convertType(type));
diff --git a/clang/test/CIR/CodeGen/const-label-addr.c b/clang/test/CIR/CodeGen/const-label-addr.c
index d820db4221b66..756569fe58020 100644
--- a/clang/test/CIR/CodeGen/const-label-addr.c
+++ b/clang/test/CIR/CodeGen/const-label-addr.c
@@ -5,13 +5,21 @@
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
// RUN: FileCheck --check-prefix=LLVM --input-file=%t.ll %s
+// CIR: cir.global "private" internal dso_local @g.ar = #cir.block_addr_
diff <@g, "l2", "l1"> : !s64i
+// CIR: cir.global "private" internal dso_local @f.s = #cir.const_record<{#cir.block_addr_
diff <@f, "A", "B"> : !s32i, #cir.block_addr_
diff <@f, "B", "A"> : !s32i}> : !rec_S2
+// CIR: cir.global "private" internal dso_local @e.arr = #cir.const_array<[#cir.block_addr_
diff <@e, "l2", "l1"> : !s32i, #cir.block_addr_
diff <@e, "l3", "l2"> : !s32i]> : !cir.array<!s32i x 2>
// CIR: cir.global "private" internal dso_local @d.s = #cir.const_record<{#cir.block_addr_info<@d, "A"> : !cir.ptr<!void>, #cir.block_addr_info<@d, "B"> : !cir.ptr<!void>}> : !rec_S
// CIR: cir.global "private" internal dso_local @c.tbl = #cir.const_array<[#cir.block_addr_info<@c, "A"> : !cir.ptr<!void>, #cir.block_addr_info<@c, "A"> : !cir.ptr<!void>, #cir.block_addr_info<@c, "B"> : !cir.ptr<!void>]> : !cir.array<!cir.ptr<!void> x 3>
+// CIR: cir.global "private" internal dso_local @b.ar = #cir.block_addr_
diff <@b, "l2", "l1"> : !s32i
// CIR: cir.global "private" internal dso_local @a.a = #cir.block_addr_info<@a, "A"> : !cir.ptr<!void>
+// LLVM-DAG: @e.arr = internal global [2 x i32] [i32 trunc (i{{..}} sub (i{{..}} ptrtoint (ptr blockaddress(@e, %[[E_L2:.*]]) to i{{..}}), i{{..}} ptrtoint (ptr blockaddress(@e, %[[E_L1:.*]]) to i{{..}})) to i32), i32 trunc (i{{..}} sub (i{{..}} ptrtoint (ptr blockaddress(@e, %[[E_L3:.*]]) to i{{..}}), i{{..}} ptrtoint (ptr blockaddress(@e, %[[E_L2]]) to i{{..}})) to i32)], align 4
// LLVM-DAG: @a.a = internal global ptr blockaddress(@a, %[[A_BLOCK:.*]]), align 8
+// LLVM-DAG: @b.ar = internal global {{.*}} sub (i{{..}} ptrtoint (ptr blockaddress(@b, %[[LABEL_L2:.*]]) to i{{..}}), i{{..}} ptrtoint (ptr blockaddress(@b, %[[LABEL_L1:.*]]) to i{{..}}))
// LLVM-DAG: @c.tbl = internal global [3 x ptr] [ptr blockaddress(@c, %[[C_A:.*]]), ptr blockaddress(@c, %[[C_A]]), ptr blockaddress(@c, %[[C_B:.*]])], align 16
// LLVM-DAG: @d.s = internal global %struct.S { ptr blockaddress(@d, %[[D_A:.*]]), ptr blockaddress(@d, %[[D_B:.*]]) }, align 8
+// LLVM-DAG: @f.s = internal global %struct.S2 { i32 trunc (i{{..}} sub (i{{..}} ptrtoint (ptr blockaddress(@f, %[[F_A:.*]]) to i{{..}}), i{{..}} ptrtoint (ptr blockaddress(@f, %[[F_B:.*]]) to i{{..}})) to i32), i32 trunc (i{{..}} sub (i{{..}} ptrtoint (ptr blockaddress(@f, %[[F_B]]) to i{{..}}), i{{..}} ptrtoint (ptr blockaddress(@f, %[[F_A]]) to i{{..}})) to i32) }, align 4
+// LLVM-DAG: @g.ar = internal global {{.*}} sub (i{{..}} ptrtoint (ptr blockaddress(@g, %[[LABEL_GL2:.*]]) to i{{..}}), i{{..}} ptrtoint (ptr blockaddress(@g, %[[LABEL_GL1:.*]]) to i{{..}}))
void a(void) {
A:;
@@ -30,6 +38,27 @@ A:;
// LLVM: [[A_BLOCK]]:
// LLVM: ret void
+// PR14005
+int b(void) {
+ static int ar = &&l2 - &&l1;
+l1:
+ return 10;
+l2:
+ return 11;
+}
+
+// CIR: cir.func{{.*}} @b
+// CIR: %[[B_AR:.*]] = cir.get_global @b.ar
+// CIR: [[LABEL_L1:.*]]:
+// CIR: cir.label "l1"
+// CIR: [[LABEL_L2:.*]]:
+// CIR: cir.label "l2"
+
+// LLVM: define dso_local i32 @b()
+// LLVM: br label %[[LABEL_L1]]
+// LLVM: [[LABEL_L1]]:
+// LLVM: [[LABEL_L2]]:
+
void c(int x) {
static void *tbl[3] = {&&A, &&A, &&B};
int idx = x > 2 ? 2 : x;
@@ -76,3 +105,70 @@ B:;
// LLVM: br label %[[D_B]]
// LLVM: [[D_B]]:
// LLVM: ret void
+
+int e(void) {
+ static int arr[2] = {&&l2 - &&l1, &&l3 - &&l2};
+l1:
+ return 10;
+l2:
+ return 11;
+l3:
+ return 11;
+}
+
+// CIR: cir.func{{.*}} @e
+// CIR: %[[E_ARR:.*]] = cir.get_global @e.arr
+// CIR: [[E_L1:.*]]:
+// CIR: cir.label "l1"
+// CIR: [[E_L2:.*]]:
+// CIR: cir.label "l2"
+// CIR: [[E_L3:.*]]:
+// CIR: cir.label "l3"
+
+// LLVM: define dso_local i32 @e()
+// LLVM: br label %[[E_L1]]
+// LLVM: [[E_L1]]:
+// LLVM: [[E_L2]]:
+// LLVM: [[E_L3]]:
+
+struct S2 { int a, b; };
+void f(void) {
+A:;
+B:;
+ static struct S2 s = {&&A - &&B, &&B - &&A};
+}
+
+// CIR: cir.func{{.*}} @f
+// CIR: [[F_A:.*]]:
+// CIR: cir.label "A"
+// CIR: [[F_B:.*]]:
+// CIR: cir.label "B"
+// CIR: %[[S2:.*]] = cir.get_global @f.s
+// CIR: cir.return
+
+// LLVM: define dso_local void @f()
+// LLVM: br label %[[F_A]]
+// LLVM: [[F_A]]:
+// LLVM: br label %[[F_B]]
+// LLVM: [[F_B]]:
+// LLVM: ret void
+
+int g(void) {
+ static long ar = &&l2 - &&l1;
+l1:
+ return 10;
+l2:
+ return 11;
+}
+
+// CIR: cir.func{{.*}} @g
+// CIR: %[[G_AR:.*]] = cir.get_global @g.ar
+// CIR: [[LABEL_GL1:.*]]:
+// CIR: cir.label "l1"
+// CIR: [[LABEL_GL2:.*]]:
+// CIR: cir.label "l2"
+
+// LLVM: define dso_local i32 @g()
+// LLVM: br label %[[LABEL_GL1]]
+// LLVM: [[LABEL_GL1]]:
+// LLVM: [[LABEL_GL2]]:
More information about the cfe-commits
mailing list