From dae3a66ca63bf7ff7c06ce1ed3b2be93f712fb64 Mon Sep 17 00:00:00 2001 From: Andy Kaylor Date: Wed, 26 Nov 2025 11:30:40 -0800 Subject: [PATCH 1/4] [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(attrType)) { + if (!::mlir::isa(opType)) + return success(); + return op->emitOpError("undef expects non-void type"); + } + if (mlir::isa(attrType)) { if (!mlir::isa(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::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(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::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 {{.*}}) +// CIR: %[[THIS:.*]] = cir.alloca !cir.ptr, !cir.ptr>, ["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 +// CIR: cir.call @_ZNK3$_0clEv(%[[LAMBDA]]) : (!cir.ptr) -> () + +// 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 Date: Wed, 26 Nov 2025 12:12:53 -0800 Subject: [PATCH 2/4] 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 // CIR: cir.call @_ZNK3$_0clEv(%[[LAMBDA]]) : (!cir.ptr) -> () -// 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 Date: Wed, 26 Nov 2025 12:15:59 -0800 Subject: [PATCH 3/4] 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(init))); + assert( + (isa( + 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. From 45d2d3f3c364491e6a61fd9d0db066ecd6f7c5b8 Mon Sep 17 00:00:00 2001 From: Andy Kaylor Date: Wed, 26 Nov 2025 14:13:47 -0800 Subject: [PATCH 4/4] Address review feedback --- clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 4 ++-- clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index 92fd591e3a37d..f1bacff7fc691 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -330,8 +330,8 @@ static LogicalResult checkConstantTypes(mlir::Operation *op, mlir::Type opType, "zero expects struct, array, vector, or complex type"); } - if (isa(attrType)) { - if (!::mlir::isa(opType)) + if (mlir::isa(attrType)) { + if (!mlir::isa(opType)) return success(); return op->emitOpError("undef expects non-void type"); } diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index 1b9cf30b6fd2e..0c34d87734c3e 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -2057,7 +2057,7 @@ CIRToLLVMGlobalOpLowering::matchAndRewriteRegionInitializedGlobal( assert( (isa( + cir::TypeInfoAttr, cir::UndefAttr, cir::VTableAttr, cir::ZeroAttr>( init))); // TODO(cir): once LLVM's dialect has proper equivalent attributes this