diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td index 356fac33a5733..f5f4f28f8993c 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td @@ -1469,25 +1469,32 @@ 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)); }]> ]; + 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(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..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()); @@ -745,8 +750,8 @@ mlir::LogicalResult CIRGenFunction::emitLabel(const clang::LabelDecl &d) { builder.setInsertionPointToEnd(labelBlock); auto func = cast(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(attrType)) + if (mlir::isa(attrType)) return success(); assert(isa(attrType) && "What else could we be looking at here?"); @@ -2190,8 +2190,7 @@ static ParseResult parseGlobalOpTypeAndInitialValue(OpAsmParser &parser, assert(mlir::isa(initialValueAttr) && "Non-typed attrs shouldn't appear here."); - auto typedAttr = mlir::cast(initialValueAttr); - opTy = typedAttr.getType(); + opTy = mlir::cast(initialValueAttr).getType(); } // Parse destructor, example: 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(initVal)) { + assert(convertedTy == ptrTy && "BlockAddrInfo type should not change"); + return blockAddr; + } + auto constPtr = mlir::cast_if_present(initVal); if (!constPtr) return {}; 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 @@ -27,7 +29,8 @@ struct GotoSolverPass : public impl::GotoSolverBase { 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 labels; llvm::SmallVector 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(&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> 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..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()); @@ -2439,17 +2468,21 @@ 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 // to the appropriate value. const mlir::Location loc = op.getLoc(); setupRegionInitializedLLVMGlobalOp(op, rewriter); - 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(); @@ -2555,7 +2588,8 @@ mlir::LogicalResult CIRToLLVMGlobalOpLowering::matchAndRewrite( return mlir::success(); } return matchAndRewriteRegionInitializedGlobal(op, init.value(), rewriter); - } else if (mlir::isa(init.value())) { @@ -3721,8 +3755,9 @@ void ConvertCIRToLLVMPass::runOnOperation() { /// repeated O(M) module-wide symbol scans for every call site. mlir::SymbolTableCollection symbolTables; mlir::RewritePatternSet patterns(&getContext()); - patterns.add( - converter, patterns.getContext(), dl, blockInfoAddr); + patterns.add(converter, patterns.getContext(), dl, + blockInfoAddr); patterns.add( converter, patterns.getContext(), dl, symbolTables); diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h index c0abb40b7304e..059d6a9778be3 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h @@ -22,11 +22,15 @@ 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. +/// 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); + 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 new file mode 100644 index 0000000000000..d820db4221b66 --- /dev/null +++ b/clang/test/CIR/CodeGen/const-label-addr.c @@ -0,0 +1,78 @@ +// 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 + +// CIR: cir.global "private" internal dso_local @d.s = #cir.const_record<{#cir.block_addr_info<@d, "A"> : !cir.ptr, #cir.block_addr_info<@d, "B"> : !cir.ptr}> : !rec_S +// CIR: cir.global "private" internal dso_local @c.tbl = #cir.const_array<[#cir.block_addr_info<@c, "A"> : !cir.ptr, #cir.block_addr_info<@c, "A"> : !cir.ptr, #cir.block_addr_info<@c, "B"> : !cir.ptr]> : !cir.array x 3> +// CIR: cir.global "private" internal dso_local @a.a = #cir.block_addr_info<@a, "A"> : !cir.ptr + +// 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:; + static void *a = &&A; +} + +// 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: cir.return + +// 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 + +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