https://github.com/andykaylor updated https://github.com/llvm/llvm-project/pull/203644
>From 37ade481246799fd4732fcb72de7840237addc48 Mon Sep 17 00:00:00 2001 From: Andy Kaylor <[email protected]> Date: Fri, 12 Jun 2026 13:38:25 -0700 Subject: [PATCH 1/4] [CIR] Implement support for emitting label address constants The evalloop.c test in the llvm-test-suite single source tests contains a static array that is initialized with the address of labels within the enclosing function. This wasn't implemented in CIR. This change adds an implementation. The constant emitter change was trivial. We just needed to create a #cir.block_addr_info attribute. However, using that attribute as an initializer for a global requires some additional handling and special lowering for the initializer. The goto solver also needed to be updated to consider uses of labels in global initializers. The test case here was copied over directly from classic codegen. The original test has an additional test case for the difference between two label addresses. Support for that case will be added in a future change. Assisted-by: Cursor / claude-opus-4.8 --- .../include/clang/CIR/Dialect/IR/CIRAttrs.td | 26 +++++++++---- clang/include/clang/CIR/Dialect/IR/CIROps.td | 3 ++ clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp | 8 +++- clang/lib/CIR/CodeGen/CIRGenStmt.cpp | 4 +- clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 11 +++--- .../lib/CIR/Dialect/Transforms/GotoSolver.cpp | 31 ++++++++++++++-- .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 37 +++++++++++++++---- clang/test/CIR/CodeGen/const-label-addr.c | 25 +++++++++++++ 8 files changed, 118 insertions(+), 27 deletions(-) create mode 100644 clang/test/CIR/CodeGen/const-label-addr.c diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td index 356fac33a5733..94d17ccd5dc74 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td @@ -1469,25 +1469,37 @@ def CIR_UnwindAttr : CIR_UnitAttr<"Unwind", "unwind"> { // CIR_BlockAddrInfoAttr //===----------------------------------------------------------------------===// -def CIR_BlockAddrInfoAttr : CIR_Attr<"BlockAddrInfo", "block_addr_info"> { - let summary = "Block Addres attribute"; +def CIR_BlockAddrInfoAttr + : CIR_ValueLikeAttr<"BlockAddrInfo", "block_addr_info"> { + let summary = "Block address attribute"; let description = [{ This attribute is used to represent the address of a basic block within a function. It combines the symbol reference to a function with the name of a label inside that function. }]; - let parameters = (ins "mlir::FlatSymbolRefAttr":$func, - "mlir::StringAttr":$label); + let parameters = (ins + AttributeSelfTypeParameter< + "", "cir::PointerType", + "cir::PointerType::get(cir::VoidType::get($_ctxt))">:$type, + "mlir::FlatSymbolRefAttr":$func, + "mlir::StringAttr":$label); let assemblyFormat = "`<` $func `,` $label `>`"; let builders = [ AttrBuilder<(ins "llvm::StringRef":$func_name, - "llvm::StringRef":$label_name - ), [{ - return $_get($_ctxt, mlir::FlatSymbolRefAttr::get($_ctxt, func_name), + "llvm::StringRef":$label_name), [{ + return $_get($_ctxt, + cir::PointerType::get(cir::VoidType::get($_ctxt)), + mlir::FlatSymbolRefAttr::get($_ctxt, func_name), mlir::StringAttr::get($_ctxt, label_name)); }]> ]; + + // Block addresses require deferred basic-block resolution during the + // LowerToLLVM pass, so they are not handled by the generic attribute-to-value + // lowering. + let hasAttrToValueLowering = 0; + let canHaveIllegalCXXABIType = 0; } diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index f48c5c7ee5209..355d4cb047a04 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -3177,6 +3177,9 @@ def CIR_GlobalOp : CIR_Op<"global", [ mlir::SymbolRefAttr getComdatAttr(cir::GlobalOp &op, mlir::OpBuilder &builder) const; }]; + + let customLLVMLoweringConstructorDecl = + LoweringBuilders<(ins "LLVMBlockAddressInfo &":$blockInfoAddr)>; } //===----------------------------------------------------------------------===// diff --git a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp index 8ee29484ce64b..6c64d7571795a 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp @@ -1239,6 +1239,8 @@ struct ConstantLValue { : value(nullptr), hasOffsetApplied(false) {} /*implicit*/ ConstantLValue(cir::GlobalViewAttr address) : value(address), hasOffsetApplied(false) {} + /*implicit*/ ConstantLValue(cir::BlockAddrInfoAttr address) + : value(address), hasOffsetApplied(true) {} ConstantLValue() : value(nullptr), hasOffsetApplied(false) {} }; @@ -1519,8 +1521,10 @@ ConstantLValueEmitter::VisitPredefinedExpr(const PredefinedExpr *e) { ConstantLValue ConstantLValueEmitter::VisitAddrLabelExpr(const AddrLabelExpr *e) { - cgm.errorNYI(e->getSourceRange(), "ConstantLValueEmitter: addr label expr"); - return {}; + auto func = cast<cir::FuncOp>(emitter.cgf->curFn); + return cir::BlockAddrInfoAttr::get(cgm.getBuilder().getContext(), + func.getSymName(), + e->getLabel()->getName()); } ConstantLValue ConstantLValueEmitter::VisitCallExpr(const CallExpr *e) { diff --git a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp index 922140a93aa5a..101ecfab21b2d 100644 --- a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp @@ -745,8 +745,8 @@ mlir::LogicalResult CIRGenFunction::emitLabel(const clang::LabelDecl &d) { builder.setInsertionPointToEnd(labelBlock); auto func = cast<cir::FuncOp>(curFn); cgm.mapBlockAddress(cir::BlockAddrInfoAttr::get(builder.getContext(), - func.getSymNameAttr(), - label.getLabelAttr()), + func.getSymName(), + label.getLabel()), label); // FIXME: emit debug info for labels, incrementProfileCounter assert(!cir::MissingFeatures::incrementProfileCounter()); diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index bf7efc2172fe9..b26fb9cec6d80 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -583,10 +583,10 @@ static LogicalResult checkConstantTypes(mlir::Operation *op, mlir::Type opType, return success(); } - if (mlir::isa<cir::ConstArrayAttr, cir::ConstVectorAttr, - cir::ConstComplexAttr, cir::ConstRecordAttr, - cir::GlobalViewAttr, cir::PoisonAttr, cir::TypeInfoAttr, - cir::VTableAttr>(attrType)) + if (mlir::isa<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?"); @@ -2190,8 +2190,7 @@ static ParseResult parseGlobalOpTypeAndInitialValue(OpAsmParser &parser, assert(mlir::isa<mlir::TypedAttr>(initialValueAttr) && "Non-typed attrs shouldn't appear here."); - auto typedAttr = mlir::cast<mlir::TypedAttr>(initialValueAttr); - opTy = typedAttr.getType(); + opTy = mlir::cast<mlir::TypedAttr>(initialValueAttr).getType(); } // Parse destructor, example: diff --git a/clang/lib/CIR/Dialect/Transforms/GotoSolver.cpp b/clang/lib/CIR/Dialect/Transforms/GotoSolver.cpp index d590ccce1f540..e2a561cb3a003 100644 --- a/clang/lib/CIR/Dialect/Transforms/GotoSolver.cpp +++ b/clang/lib/CIR/Dialect/Transforms/GotoSolver.cpp @@ -9,6 +9,8 @@ #include "clang/CIR/Dialect/IR/CIRDialect.h" #include "clang/CIR/Dialect/Passes.h" #include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringSet.h" #include "llvm/Support/TimeProfiler.h" #include <memory> @@ -27,7 +29,8 @@ struct GotoSolverPass : public impl::GotoSolverBase<GotoSolverPass> { void runOnOperation() override; }; -static void process(cir::FuncOp func) { +static void process(cir::FuncOp func, + const llvm::StringSet<> &globalBlockAddrLabel) { mlir::OpBuilder rewriter(func.getContext()); llvm::StringMap<Block *> labels; llvm::SmallVector<cir::GotoOp, 4> gotos; @@ -46,7 +49,11 @@ static void process(cir::FuncOp func) { for (auto &lab : labels) { StringRef labelName = lab.getKey(); Block *block = lab.getValue(); - if (!blockAddrLabel.contains(labelName)) { + // Keep labels whose address is taken either by a cir.block_address op in + // this function or by a block-address attribute used elsewhere (e.g. in a + // global initializer). + if (!blockAddrLabel.contains(labelName) && + !globalBlockAddrLabel.contains(labelName)) { // erase the LabelOp inside the block if safe if (auto lab = dyn_cast<cir::LabelOp>(&block->front())) { lab.erase(); @@ -65,7 +72,25 @@ static void process(cir::FuncOp func) { void GotoSolverPass::runOnOperation() { llvm::TimeTraceScope scope("Goto Solver"); - getOperation()->walk(&process); + + // Block addresses can also appear in attributes outside of any function body, + // such as global variable initializers. Collect, per target function, the + // labels referenced this way so their LabelOps are not erased below. + llvm::StringMap<llvm::StringSet<>> globalBlockAddrLabels; + getOperation()->walk([&](mlir::Operation *op) { + for (const mlir::NamedAttribute &namedAttr : op->getAttrs()) { + namedAttr.getValue().walk([&](cir::BlockAddrInfoAttr info) { + globalBlockAddrLabels[info.getFunc().getValue()].insert( + info.getLabel()); + }); + } + }); + + static const llvm::StringSet<> emptySet; + getOperation()->walk([&](cir::FuncOp func) { + auto it = globalBlockAddrLabels.find(func.getSymName()); + process(func, it == globalBlockAddrLabels.end() ? emptySet : it->second); + }); } } // namespace diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index af729e95c7709..7339e54d556c2 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -2439,16 +2439,37 @@ 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::UndefAttr, cir::PoisonAttr, - cir::VTableAttr, cir::ZeroAttr>(init))); + 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))); // 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. const mlir::Location loc = op.getLoc(); setupRegionInitializedLLVMGlobalOp(op, rewriter); + + // A block address initializer is lowered to an llvm.blockaddress op that + // references a block tag inside the target function. The matching block tag + // may not have been emitted yet, in which case the address is recorded as + // unresolved and patched up later in resolveBlockAddressOp. + if (auto blockAddrInfo = mlir::dyn_cast<cir::BlockAddrInfoAttr>(init)) { + mlir::LLVM::BlockTagOp matchLabel = + blockInfoAddr.lookupBlockTag(blockAddrInfo); + mlir::LLVM::BlockTagAttr tagAttr = + matchLabel ? matchLabel.getTag() : mlir::LLVM::BlockTagAttr{}; + auto blkAddr = mlir::LLVM::BlockAddressAttr::get( + rewriter.getContext(), blockAddrInfo.getFunc(), tagAttr); + auto blockAddressOp = mlir::LLVM::BlockAddressOp::create( + rewriter, loc, mlir::LLVM::LLVMPointerType::get(rewriter.getContext()), + blkAddr); + if (!matchLabel) + blockInfoAddr.addUnresolvedBlockAddress(blockAddressOp, blockAddrInfo); + mlir::LLVM::ReturnOp::create(rewriter, loc, blockAddressOp); + return mlir::success(); + } + CIRAttrToValue valueConverter(op, rewriter, typeConverter); mlir::Value value = valueConverter.visit(init); mlir::LLVM::ReturnOp::create(rewriter, loc, value); @@ -2555,7 +2576,8 @@ mlir::LogicalResult CIRToLLVMGlobalOpLowering::matchAndRewrite( return mlir::success(); } return matchAndRewriteRegionInitializedGlobal(op, init.value(), rewriter); - } else if (mlir::isa<cir::ConstVectorAttr, cir::ConstPtrAttr, + } 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())) { @@ -3721,8 +3743,9 @@ void ConvertCIRToLLVMPass::runOnOperation() { /// repeated O(M) module-wide symbol scans for every call site. mlir::SymbolTableCollection symbolTables; mlir::RewritePatternSet patterns(&getContext()); - patterns.add<CIRToLLVMBlockAddressOpLowering, CIRToLLVMLabelOpLowering>( - converter, patterns.getContext(), dl, blockInfoAddr); + patterns.add<CIRToLLVMBlockAddressOpLowering, CIRToLLVMGlobalOpLowering, + CIRToLLVMLabelOpLowering>(converter, patterns.getContext(), dl, + blockInfoAddr); patterns.add<CIRToLLVMCallOpLowering, CIRToLLVMTryCallOpLowering>( converter, patterns.getContext(), dl, symbolTables); diff --git a/clang/test/CIR/CodeGen/const-label-addr.c b/clang/test/CIR/CodeGen/const-label-addr.c new file mode 100644 index 0000000000000..8541b23d3d4f6 --- /dev/null +++ b/clang/test/CIR/CodeGen/const-label-addr.c @@ -0,0 +1,25 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll +// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s +// 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 + +void a(void) { +A:; + static void *a = &&A; +} + +// CIR: cir.global "private" internal dso_local @a.a = #cir.block_addr_info<@a, "A"> : !cir.ptr<!void> +// CIR: cir.func{{.*}} @a() +// CIR: cir.br ^[[A_BLOCK:bb[0-9]+]] +// CIR: ^[[A_BLOCK]]: +// CIR: cir.label "A" +// CIR: %[[STATIC_A:.*]] = cir.get_global @a.a : !cir.ptr<!cir.ptr<!void>> +// CIR: cir.return + +// LLVM: @a.a = internal global ptr blockaddress(@a, %[[A_BLOCK:.*]]), align 8 +// LLVM: define dso_local void @a() +// LLVM: br label %[[A_BLOCK]] +// LLVM: [[A_BLOCK]]: +// LLVM: ret void >From 9d89f9d97923e6a7831a909e2b8ded892716ce28 Mon Sep 17 00:00:00 2001 From: Andy Kaylor <[email protected]> Date: Mon, 15 Jun 2026 17:24:55 -0700 Subject: [PATCH 2/4] Add handling for arrays of labels --- .../include/clang/CIR/Dialect/IR/CIRAttrs.td | 5 -- clang/lib/CIR/CodeGen/CIRGenStmt.cpp | 9 ++- .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 62 +++++++++++-------- .../CIR/Lowering/DirectToLLVM/LowerToLLVM.h | 13 ++-- clang/test/CIR/CodeGen/const-label-addr.c | 33 +++++++++- 5 files changed, 84 insertions(+), 38 deletions(-) diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td index 94d17ccd5dc74..f5f4f28f8993c 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td @@ -1495,11 +1495,6 @@ def CIR_BlockAddrInfoAttr }]> ]; - // Block addresses require deferred basic-block resolution during the - // LowerToLLVM pass, so they are not handled by the generic attribute-to-value - // lowering. - let hasAttrToValueLowering = 0; - let canHaveIllegalCXXABIType = 0; } diff --git a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp index 101ecfab21b2d..47c94cb4ec535 100644 --- a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp @@ -706,8 +706,13 @@ mlir::LogicalResult CIRGenFunction::emitGotoStmt(const clang::GotoStmt &s) { mlir::LogicalResult CIRGenFunction::emitIndirectGotoStmt(const IndirectGotoStmt &s) { mlir::Value val = emitScalarExpr(s.getTarget()); - assert(indirectGotoBlock && - "If you jumping to a indirect branch should be alareadye emitted"); + if (!indirectGotoBlock) { + // If the target labels were emitted as constants, we have more work to do. + // This diagnostic is here to flag the condition, but the changes may end + // up being implemented elsewhere. + cgm.errorNYI(s.getSourceRange(), "Indirect goto without a goto block"); + return mlir::failure(); + } cir::BrOp::create(builder, getLoc(s.getSourceRange()), indirectGotoBlock, val); builder.createBlock(builder.getBlock()->getParent()); diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index 7339e54d556c2..7cb15f8c5e8a3 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -285,8 +285,10 @@ class CIRAttrToValue { public: CIRAttrToValue(mlir::Operation *parentOp, mlir::ConversionPatternRewriter &rewriter, - const mlir::TypeConverter *converter) - : parentOp(parentOp), rewriter(rewriter), converter(converter) {} + const mlir::TypeConverter *converter, + LLVMBlockAddressInfo *blockInfoAddr = nullptr) + : parentOp(parentOp), rewriter(rewriter), converter(converter), + blockInfoAddr(blockInfoAddr) {} #define GET_CIR_ATTR_TO_VALUE_VISITOR_DECLS #include "clang/CIR/Dialect/IR/CIRLowering.inc" @@ -296,14 +298,18 @@ class CIRAttrToValue { mlir::Operation *parentOp; mlir::ConversionPatternRewriter &rewriter; const mlir::TypeConverter *converter; + // Only available when lowering global initializers that may contain block + // address attributes. Used to resolve a BlockAddrInfoAttr to its block tag. + LLVMBlockAddressInfo *blockInfoAddr; }; /// Switches on the type of attribute and calls the appropriate conversion. mlir::Value lowerCirAttrAsValue(mlir::Operation *parentOp, const mlir::Attribute attr, mlir::ConversionPatternRewriter &rewriter, - const mlir::TypeConverter *converter) { - CIRAttrToValue valueConverter(parentOp, rewriter, converter); + const mlir::TypeConverter *converter, + LLVMBlockAddressInfo *blockInfoAddr) { + CIRAttrToValue valueConverter(parentOp, rewriter, converter, blockInfoAddr); mlir::Value value = valueConverter.visit(attr); if (!value) llvm_unreachable("unhandled attribute type"); @@ -475,6 +481,29 @@ mlir::Value CIRAttrToValue::visitCirAttr(cir::ConstPtrAttr ptrAttr) { rewriter, loc, converter->convertType(ptrAttr.getType()), ptrVal); } +/// BlockAddrInfoAttr visitor. +mlir::Value CIRAttrToValue::visitCirAttr(cir::BlockAddrInfoAttr blockAddrInfo) { + assert(blockInfoAddr && + "block address lowering requires LLVMBlockAddressInfo"); + // A block address is lowered to an llvm.blockaddress op that references a + // block tag inside the target function. The matching block tag may not have + // been emitted yet, in which case the address is recorded as unresolved and + // patched up later in resolveBlockAddressOp. + mlir::Location loc = parentOp->getLoc(); + mlir::LLVM::BlockTagOp matchLabel = + blockInfoAddr->lookupBlockTag(blockAddrInfo); + mlir::LLVM::BlockTagAttr tagAttr = + matchLabel ? matchLabel.getTag() : mlir::LLVM::BlockTagAttr{}; + auto blkAddr = mlir::LLVM::BlockAddressAttr::get( + rewriter.getContext(), blockAddrInfo.getFunc(), tagAttr); + auto blockAddressOp = mlir::LLVM::BlockAddressOp::create( + rewriter, loc, mlir::LLVM::LLVMPointerType::get(rewriter.getContext()), + blkAddr); + if (!matchLabel) + blockInfoAddr->addUnresolvedBlockAddress(blockAddressOp, blockAddrInfo); + return blockAddressOp; +} + // ConstArrayAttr visitor mlir::Value CIRAttrToValue::visitCirAttr(cir::ConstArrayAttr attr) { mlir::Type llvmTy = converter->convertType(attr.getType()); @@ -2450,27 +2479,10 @@ CIRToLLVMGlobalOpLowering::matchAndRewriteRegionInitializedGlobal( const mlir::Location loc = op.getLoc(); setupRegionInitializedLLVMGlobalOp(op, rewriter); - // A block address initializer is lowered to an llvm.blockaddress op that - // references a block tag inside the target function. The matching block tag - // may not have been emitted yet, in which case the address is recorded as - // unresolved and patched up later in resolveBlockAddressOp. - if (auto blockAddrInfo = mlir::dyn_cast<cir::BlockAddrInfoAttr>(init)) { - mlir::LLVM::BlockTagOp matchLabel = - blockInfoAddr.lookupBlockTag(blockAddrInfo); - mlir::LLVM::BlockTagAttr tagAttr = - matchLabel ? matchLabel.getTag() : mlir::LLVM::BlockTagAttr{}; - auto blkAddr = mlir::LLVM::BlockAddressAttr::get( - rewriter.getContext(), blockAddrInfo.getFunc(), tagAttr); - auto blockAddressOp = mlir::LLVM::BlockAddressOp::create( - rewriter, loc, mlir::LLVM::LLVMPointerType::get(rewriter.getContext()), - blkAddr); - if (!matchLabel) - blockInfoAddr.addUnresolvedBlockAddress(blockAddressOp, blockAddrInfo); - mlir::LLVM::ReturnOp::create(rewriter, loc, blockAddressOp); - return mlir::success(); - } - - CIRAttrToValue valueConverter(op, rewriter, typeConverter); + // Pass blockInfoAddr so that block address initializers (either as the whole + // initializer or nested inside an aggregate) can be resolved by the + // BlockAddrInfoAttr visitor. + CIRAttrToValue valueConverter(op, rewriter, typeConverter, &blockInfoAddr); mlir::Value value = valueConverter.visit(init); mlir::LLVM::ReturnOp::create(rewriter, loc, value); return mlir::success(); diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h index c0abb40b7304e..cf6ec19e0ebbd 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h @@ -22,11 +22,16 @@ namespace cir { namespace direct { +struct LLVMBlockAddressInfo; + /// Convert a CIR attribute to an LLVM attribute. May use the datalayout for -/// lowering attributes to-be-stored in memory. -mlir::Value lowerCirAttrAsValue(mlir::Operation *parentOp, mlir::Attribute attr, - mlir::ConversionPatternRewriter &rewriter, - const mlir::TypeConverter *converter); +/// lowering attributes to-be-stored in memory. When the attribute may contain +/// block address attributes, `blockInfoAddr` is used to resolve them. +mlir::Value +lowerCirAttrAsValue(mlir::Operation *parentOp, mlir::Attribute attr, + mlir::ConversionPatternRewriter &rewriter, + const mlir::TypeConverter *converter, + LLVMBlockAddressInfo *blockInfoAddr = nullptr); mlir::LLVM::Linkage convertLinkage(cir::GlobalLinkageKind linkage); diff --git a/clang/test/CIR/CodeGen/const-label-addr.c b/clang/test/CIR/CodeGen/const-label-addr.c index 8541b23d3d4f6..ea27f0204e2e6 100644 --- a/clang/test/CIR/CodeGen/const-label-addr.c +++ b/clang/test/CIR/CodeGen/const-label-addr.c @@ -5,12 +5,17 @@ // 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 @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 @a.a = #cir.block_addr_info<@a, "A"> : !cir.ptr<!void> + +// LLVM-DAG: @a.a = internal global ptr blockaddress(@a, %[[A_BLOCK:.*]]), align 8 +// 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 + void a(void) { A:; static void *a = &&A; } -// CIR: cir.global "private" internal dso_local @a.a = #cir.block_addr_info<@a, "A"> : !cir.ptr<!void> // CIR: cir.func{{.*}} @a() // CIR: cir.br ^[[A_BLOCK:bb[0-9]+]] // CIR: ^[[A_BLOCK]]: @@ -18,8 +23,32 @@ A:; // CIR: %[[STATIC_A:.*]] = cir.get_global @a.a : !cir.ptr<!cir.ptr<!void>> // CIR: cir.return -// LLVM: @a.a = internal global ptr blockaddress(@a, %[[A_BLOCK:.*]]), align 8 // LLVM: define dso_local void @a() // LLVM: br label %[[A_BLOCK]] // LLVM: [[A_BLOCK]]: // LLVM: ret void + +void c(int x) { + static void *tbl[3] = {&&A, &&A, &&B}; + int idx = x > 2 ? 2 : x; +A: + void *p = tbl[idx]; +B: +} + +// CIR: cir.func{{.*}} @c +// CIR: %[[C_TBL:.*]] = cir.get_global @c.tbl +// CIR: [[LABEL_A:.*]]: +// CIR: cir.label "A" +// CIR: %[[P:.*]] = cir.get_element %[[C_TBL]][%{{.*}}] +// CIR: [[LABEL_B:.*]]: +// CIR: cir.label "B" + +// LLVM: define dso_local void @c(i32 noundef %{{.*}}) +// LLVM: br label %[[C_A]] +// LLVM: [[C_A]]: +// LLVM: %[[TARGET:.*]] = getelementptr{{.*}} [3 x ptr], ptr @c.tbl +// LLVM: %[[P:.*]] = load ptr, ptr %[[TARGET]], align 8 +// LLVM: br label %[[C_B]] +// LLVM: [[C_B]]: +// LLVM: ret void >From aebebd72c35f0b3f4b8305f4668e798950e9a579 Mon Sep 17 00:00:00 2001 From: Andy Kaylor <[email protected]> Date: Mon, 15 Jun 2026 17:31:52 -0700 Subject: [PATCH 3/4] Fix formatting --- clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h index cf6ec19e0ebbd..059d6a9778be3 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h @@ -27,11 +27,10 @@ struct LLVMBlockAddressInfo; /// Convert a CIR attribute to an LLVM attribute. May use the datalayout for /// lowering attributes to-be-stored in memory. When the attribute may contain /// block address attributes, `blockInfoAddr` is used to resolve them. -mlir::Value -lowerCirAttrAsValue(mlir::Operation *parentOp, mlir::Attribute attr, - mlir::ConversionPatternRewriter &rewriter, - const mlir::TypeConverter *converter, - LLVMBlockAddressInfo *blockInfoAddr = nullptr); +mlir::Value lowerCirAttrAsValue(mlir::Operation *parentOp, mlir::Attribute attr, + mlir::ConversionPatternRewriter &rewriter, + const mlir::TypeConverter *converter, + LLVMBlockAddressInfo *blockInfoAddr = nullptr); mlir::LLVM::Linkage convertLinkage(cir::GlobalLinkageKind linkage); >From 6a0a22629a20ec82740677c13390de090b4f49b2 Mon Sep 17 00:00:00 2001 From: Andy Kaylor <[email protected]> Date: Mon, 22 Jun 2026 10:51:01 -0700 Subject: [PATCH 4/4] Add handling for struct initialization --- .../CIR/Dialect/Transforms/CXXABILowering.cpp | 6 +++++ clang/test/CIR/CodeGen/const-label-addr.c | 24 +++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp b/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp index 0bcfe124723e6..704ebbeb1ecd9 100644 --- a/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp +++ b/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp @@ -485,6 +485,12 @@ static mlir::TypedAttr lowerInitialValue(const LowerModule *lowerModule, return cir::GlobalViewAttr::get(convertedTy, gva.getSymbol(), gva.getIndices()); + if (auto blockAddr = + mlir::dyn_cast_if_present<cir::BlockAddrInfoAttr>(initVal)) { + assert(convertedTy == ptrTy && "BlockAddrInfo type should not change"); + return blockAddr; + } + auto constPtr = mlir::cast_if_present<cir::ConstPtrAttr>(initVal); if (!constPtr) return {}; diff --git a/clang/test/CIR/CodeGen/const-label-addr.c b/clang/test/CIR/CodeGen/const-label-addr.c index ea27f0204e2e6..d820db4221b66 100644 --- a/clang/test/CIR/CodeGen/const-label-addr.c +++ b/clang/test/CIR/CodeGen/const-label-addr.c @@ -5,11 +5,13 @@ // 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 @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 @a.a = #cir.block_addr_info<@a, "A"> : !cir.ptr<!void> // LLVM-DAG: @a.a = internal global ptr blockaddress(@a, %[[A_BLOCK:.*]]), align 8 // 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 void a(void) { A:; @@ -52,3 +54,25 @@ void c(int x) { // LLVM: br label %[[C_B]] // LLVM: [[C_B]]: // LLVM: ret void + +struct S { void *a, *b; }; +void d(void) { +A:; +B:; + static struct S s = {&&A, &&B}; +} + +// CIR: cir.func{{.*}} @d +// CIR: [[LABEL_A:.*]]: +// CIR: cir.label "A" +// CIR: [[LABEL_B:.*]]: +// CIR: cir.label "B" +// CIR: %[[S:.*]] = cir.get_global @d.s +// CIR: cir.return + +// LLVM: define dso_local void @d() +// LLVM: br label %[[D_A]] +// LLVM: [[D_A]]: +// LLVM: br label %[[D_B]] +// LLVM: [[D_B]]: +// LLVM: ret void _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
