[clang] [CIR] Add undef handling to enable global lambdas (PR #169721)
Andy Kaylor via cfe-commits
cfe-commits at lists.llvm.org
Wed Nov 26 12:16:27 PST 2025
https://github.com/andykaylor updated https://github.com/llvm/llvm-project/pull/169721
>From dae3a66ca63bf7ff7c06ce1ed3b2be93f712fb64 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Wed, 26 Nov 2025 11:30:40 -0800
Subject: [PATCH 1/3] [CIR] Add undef handling to enable global lambdas
This change adds undef handling that was needed to enable global lambdas.
There was no lambda-specific code needed, but the global lambda handling
needed to initialize a global with an undef value.
[CIR] Handle undef init of struct
This adds handling for a case where Clang initializes a struct to
undef with a constant copy. This required adding support for undef
constants and lowering undef attributes to LLVM IR.
---
clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 6 ++++
.../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 16 +++++++--
clang/test/CIR/CodeGen/lambda.cpp | 33 +++++++++++++++++++
3 files changed, 52 insertions(+), 3 deletions(-)
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 6bf543cf794b7..92fd591e3a37d 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -330,6 +330,12 @@ static LogicalResult checkConstantTypes(mlir::Operation *op, mlir::Type opType,
"zero expects struct, array, vector, or complex type");
}
+ if (isa<cir::UndefAttr>(attrType)) {
+ if (!::mlir::isa<cir::VoidType>(opType))
+ return success();
+ return op->emitOpError("undef expects non-void type");
+ }
+
if (mlir::isa<cir::BoolAttr>(attrType)) {
if (!mlir::isa<cir::BoolType>(opType))
return op->emitOpError("result type (")
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index cd923a15af132..9e62317bff5f6 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -240,7 +240,7 @@ class CIRAttrToValue {
.Case<cir::IntAttr, cir::FPAttr, cir::ConstComplexAttr,
cir::ConstArrayAttr, cir::ConstRecordAttr, cir::ConstVectorAttr,
cir::ConstPtrAttr, cir::GlobalViewAttr, cir::TypeInfoAttr,
- cir::VTableAttr, cir::ZeroAttr>(
+ cir::UndefAttr, cir::VTableAttr, cir::ZeroAttr>(
[&](auto attrT) { return visitCirAttr(attrT); })
.Default([&](auto attrT) { return mlir::Value(); });
}
@@ -254,6 +254,7 @@ class CIRAttrToValue {
mlir::Value visitCirAttr(cir::ConstVectorAttr attr);
mlir::Value visitCirAttr(cir::GlobalViewAttr attr);
mlir::Value visitCirAttr(cir::TypeInfoAttr attr);
+ mlir::Value visitCirAttr(cir::UndefAttr attr);
mlir::Value visitCirAttr(cir::VTableAttr attr);
mlir::Value visitCirAttr(cir::ZeroAttr attr);
@@ -591,6 +592,13 @@ mlir::Value CIRAttrToValue::visitCirAttr(cir::TypeInfoAttr typeInfoAttr) {
return result;
}
+/// UndefAttr visitor.
+mlir::Value CIRAttrToValue::visitCirAttr(cir::UndefAttr undefAttr) {
+ mlir::Location loc = parentOp->getLoc();
+ return mlir::LLVM::UndefOp::create(
+ rewriter, loc, converter->convertType(undefAttr.getType()));
+}
+
// VTableAttr visitor.
mlir::Value CIRAttrToValue::visitCirAttr(cir::VTableAttr vtableArr) {
mlir::Type llvmTy = converter->convertType(vtableArr.getType());
@@ -2048,7 +2056,8 @@ CIRToLLVMGlobalOpLowering::matchAndRewriteRegionInitializedGlobal(
// TODO: Generalize this handling when more types are needed here.
assert((isa<cir::ConstArrayAttr, cir::ConstRecordAttr, cir::ConstVectorAttr,
cir::ConstPtrAttr, cir::ConstComplexAttr, cir::GlobalViewAttr,
- cir::TypeInfoAttr, cir::VTableAttr, cir::ZeroAttr>(init)));
+ cir::TypeInfoAttr, cir::VTableAttr, cir::UndefAttr,
+ 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
@@ -2106,7 +2115,8 @@ mlir::LogicalResult CIRToLLVMGlobalOpLowering::matchAndRewrite(
} else if (mlir::isa<cir::ConstArrayAttr, cir::ConstVectorAttr,
cir::ConstRecordAttr, cir::ConstPtrAttr,
cir::ConstComplexAttr, cir::GlobalViewAttr,
- cir::TypeInfoAttr, cir::VTableAttr, cir::ZeroAttr>(
+ cir::TypeInfoAttr, cir::UndefAttr, 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
diff --git a/clang/test/CIR/CodeGen/lambda.cpp b/clang/test/CIR/CodeGen/lambda.cpp
index 91380b9bea296..84d446727d6e3 100644
--- a/clang/test/CIR/CodeGen/lambda.cpp
+++ b/clang/test/CIR/CodeGen/lambda.cpp
@@ -8,6 +8,39 @@
// We declare anonymous record types to represent lambdas. Rather than trying to
// to match the declarations, we establish variables for these when they are used.
+auto global_lambda = [](){};
+void use_global_lambda() {
+ global_lambda();
+}
+
+// CIR: cir.global "private" internal dso_local @global_lambda = #cir.undef : ![[REC_LAM_GLOBAL_LAMBDA:.*]] {alignment = 1 : i64}
+// CIR: cir.func lambda internal private dso_local @_ZNK3$_0clEv(%[[THIS_ARG:.*]]: !cir.ptr<![[REC_LAM_GLOBAL_LAMBDA]]> {{.*}})
+// CIR: %[[THIS:.*]] = cir.alloca !cir.ptr<![[REC_LAM_GLOBAL_LAMBDA]]>, !cir.ptr<!cir.ptr<![[REC_LAM_GLOBAL_LAMBDA]]>>, ["this", init]
+// CIR: cir.store %[[THIS_ARG]], %[[THIS]]
+// CIR: cir.load %[[THIS]]
+//
+// CIR: cir.func {{.*}} @_Z17use_global_lambdav()
+// CIR: %[[LAMBDA:.*]] = cir.get_global @global_lambda : !cir.ptr<![[REC_LAM_GLOBAL_LAMBDA]]>
+// CIR: cir.call @_ZNK3$_0clEv(%[[LAMBDA]]) : (!cir.ptr<![[REC_LAM_GLOBAL_LAMBDA]]>) -> ()
+
+// LLVM: @global_lambda = internal global %class.anon.0 undef, align 1
+// LLVM: define internal void @"_ZNK3$_0clEv"(ptr %[[THIS_ARG:.*]])
+// LLVM: %[[THIS_ADDR:.*]] = alloca ptr
+// LLVM: store ptr %[[THIS_ARG]], ptr %[[THIS_ADDR]]
+// LLVM: %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]]
+//
+// LLVM: define dso_local void @_Z17use_global_lambdav()
+// LLVM: call void @"_ZNK3$_0clEv"(ptr @global_lambda)
+
+// OGCG: @global_lambda = internal global %class.anon undef, align 1
+// OGCG: define dso_local void @_Z17use_global_lambdav()
+// OGCG: call void @"_ZNK3$_0clEv"(ptr noundef nonnull align 1 dereferenceable(1) @global_lambda)
+//
+// OGCG: define internal void @"_ZNK3$_0clEv"(ptr {{.*}} %[[THIS_ARG:.*]])
+// OGCG: %[[THIS_ADDR:.*]] = alloca ptr
+// OGCG: store ptr %[[THIS_ARG]], ptr %[[THIS_ADDR]]
+// OGCG: %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]]
+
void fn() {
auto a = [](){};
a();
>From fd1f4a2b5bd9ec04636e76f86af346bee6e43ba6 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Wed, 26 Nov 2025 12:12:53 -0800
Subject: [PATCH 2/3] Minor test update
---
clang/test/CIR/CodeGen/lambda.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/test/CIR/CodeGen/lambda.cpp b/clang/test/CIR/CodeGen/lambda.cpp
index 84d446727d6e3..1d06496a85530 100644
--- a/clang/test/CIR/CodeGen/lambda.cpp
+++ b/clang/test/CIR/CodeGen/lambda.cpp
@@ -23,7 +23,7 @@ void use_global_lambda() {
// CIR: %[[LAMBDA:.*]] = cir.get_global @global_lambda : !cir.ptr<![[REC_LAM_GLOBAL_LAMBDA]]>
// CIR: cir.call @_ZNK3$_0clEv(%[[LAMBDA]]) : (!cir.ptr<![[REC_LAM_GLOBAL_LAMBDA]]>) -> ()
-// LLVM: @global_lambda = internal global %class.anon.0 undef, align 1
+// LLVM: @global_lambda = internal global %[[REC_LAM_GLOBAL_LAMBDA:.*]] undef, align 1
// LLVM: define internal void @"_ZNK3$_0clEv"(ptr %[[THIS_ARG:.*]])
// LLVM: %[[THIS_ADDR:.*]] = alloca ptr
// LLVM: store ptr %[[THIS_ARG]], ptr %[[THIS_ADDR]]
@@ -32,7 +32,7 @@ void use_global_lambda() {
// LLVM: define dso_local void @_Z17use_global_lambdav()
// LLVM: call void @"_ZNK3$_0clEv"(ptr @global_lambda)
-// OGCG: @global_lambda = internal global %class.anon undef, align 1
+// OGCG: @global_lambda = internal global %[[REC_LAM_GLOBAL_LAMBDA:.*]] undef, align 1
// OGCG: define dso_local void @_Z17use_global_lambdav()
// OGCG: call void @"_ZNK3$_0clEv"(ptr noundef nonnull align 1 dereferenceable(1) @global_lambda)
//
>From a2e68083c34ae51bf457ec2a6b17f3ab40357d7f Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Wed, 26 Nov 2025 12:15:59 -0800
Subject: [PATCH 3/3] Fix formatting
---
clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 9e62317bff5f6..1b9cf30b6fd2e 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -2054,10 +2054,11 @@ 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::ConstArrayAttr, cir::ConstRecordAttr, cir::ConstVectorAttr,
- cir::ConstPtrAttr, cir::ConstComplexAttr, cir::GlobalViewAttr,
- cir::TypeInfoAttr, cir::VTableAttr, cir::UndefAttr,
- cir::ZeroAttr>(init)));
+ assert(
+ (isa<cir::ConstArrayAttr, cir::ConstRecordAttr, cir::ConstVectorAttr,
+ cir::ConstPtrAttr, cir::ConstComplexAttr, cir::GlobalViewAttr,
+ cir::TypeInfoAttr, cir::VTableAttr, cir::UndefAttr, 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
@@ -2116,8 +2117,7 @@ mlir::LogicalResult CIRToLLVMGlobalOpLowering::matchAndRewrite(
cir::ConstRecordAttr, cir::ConstPtrAttr,
cir::ConstComplexAttr, cir::GlobalViewAttr,
cir::TypeInfoAttr, cir::UndefAttr, cir::VTableAttr,
- cir::ZeroAttr>(
- init.value())) {
+ 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.
More information about the cfe-commits
mailing list