https://github.com/adams381 updated https://github.com/llvm/llvm-project/pull/198427
>From 5240882812847cdf572e7e7e6e8b6ca781fc0005 Mon Sep 17 00:00:00 2001 From: Adam Smith <[email protected]> Date: Mon, 18 May 2026 16:43:16 -0700 Subject: [PATCH 1/4] [mlir][LLVMIR] Allow address-of-global as a leaf in array constants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Large aggregate globals built as nested llvm.insertvalue chains force LLVMModuleTranslation to call ConstantFoldInsertValueInstruction on each step, rebuilding the whole ConstantArray each time (O(N²) in the number of elements). CIR and other frontends hit this when lowering big tables of pointers (e.g. gcc insn-automata.cc). Extend llvm.mlir.constant verification so pointer-array aggregates may use FlatSymbolRefAttr, ZeroAttr, or UndefAttr leaves (not only function refs). Teach ModuleTranslation::getLLVMConstant to resolve those symbol refs against globals already registered in the module, and add a translate test that checks a single LLVM constant array initializer. --- .../mlir/Target/LLVMIR/ModuleTranslation.h | 11 ++++ mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp | 14 ++++- mlir/lib/Target/LLVMIR/ModuleTranslation.cpp | 53 +++++++++++++++---- mlir/test/Dialect/LLVMIR/invalid.mlir | 10 ++++ .../LLVMIR/llvmir-global-addressof-leaf.mlir | 19 +++++++ 5 files changed, 96 insertions(+), 11 deletions(-) create mode 100644 mlir/test/Target/LLVMIR/llvmir-global-addressof-leaf.mlir diff --git a/mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h b/mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h index 2516818b320aa..c5c4b105ee152 100644 --- a/mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h +++ b/mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h @@ -262,6 +262,11 @@ class ModuleTranslation { return globalsMapping.lookup(op); } + /// Finds an LLVM IR global value by the mlir.global symbol name. + llvm::GlobalValue *lookupGlobal(StringRef name) const { + return globalsByNameMapping.lookup(name); + } + /// Finds an LLVM IR global value that corresponds to the given MLIR operation /// defining a global alias value. llvm::GlobalValue *lookupAlias(Operation *op) { @@ -470,6 +475,12 @@ class ModuleTranslation { /// Mappings between llvm.mlir.global definitions and corresponding globals. DenseMap<Operation *, llvm::GlobalValue *> globalsMapping; + /// Name-keyed mirror of `globalsMapping`, populated alongside it during + /// `convertGlobalsAndAliases`. Lets `getLLVMConstant` resolve + /// `FlatSymbolRefAttr` leaves of aggregate constants that name a global + /// (mirroring how `functionMapping` resolves function names). + llvm::StringMap<llvm::GlobalValue *> globalsByNameMapping; + /// Mappings between llvm.mlir.alias definitions and corresponding global /// aliases. DenseMap<Operation *, llvm::GlobalValue *> aliasesMapping; diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp index 2b603efadb476..63bd9f8a3d625 100644 --- a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp +++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp @@ -3469,7 +3469,19 @@ static LogicalResult verifyStructArrayConstant(LLVM::ConstantOp op, // from a symbol and cannot be represented in a DenseElementsAttr, but no MLIR // user needs this so far, and it seems better to avoid people misusing the // ArrayAttr for simple types. - auto structType = dyn_cast<LLVM::LLVMStructType>(arrayType.getElementType()); + Type elementType = arrayType.getElementType(); + if (isa<LLVM::LLVMPointerType>(elementType)) { + for (auto [idx, elementAttr] : llvm::enumerate(arrayAttr)) { + if (isa<FlatSymbolRefAttr, LLVM::ZeroAttr, LLVM::UndefAttr, + LLVM::PoisonAttr>(elementAttr)) + continue; + return op.emitOpError() + << "pointer array element at index " << idx + << " must be a flat symbol reference, zero, undef, or poison"; + } + return success(); + } + auto structType = dyn_cast<LLVM::LLVMStructType>(elementType); if (!structType) return op.emitOpError() << "for array with an array attribute must have a " "struct element type"; diff --git a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp index 9d0ce97ddbbdf..8b1c2bcf6e1d7 100644 --- a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp +++ b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp @@ -618,9 +618,15 @@ llvm::Constant *mlir::LLVM::detail::getLLVMConstant( } return llvm::ConstantFP::get(llvmType, floatAttr.getValue()); } - if (auto funcAttr = dyn_cast<FlatSymbolRefAttr>(attr)) - return llvm::ConstantExpr::getBitCast( - moduleTranslation.lookupFunction(funcAttr.getValue()), llvmType); + if (auto symAttr = dyn_cast<FlatSymbolRefAttr>(attr)) { + StringRef name = symAttr.getValue(); + if (llvm::Function *func = moduleTranslation.lookupFunction(name)) + return llvm::ConstantExpr::getBitCast(func, llvmType); + if (llvm::GlobalValue *global = moduleTranslation.lookupGlobal(name)) + return llvm::ConstantExpr::getBitCast(global, llvmType); + emitError(loc, "unknown symbol reference '") << name << "' in constant"; + return nullptr; + } if (auto splatAttr = dyn_cast<SplatElementsAttr>(attr)) { llvm::Type *elementType; uint64_t numElements; @@ -1197,16 +1203,16 @@ LogicalResult ModuleTranslation::convertGlobalsAndAliases() { for (auto op : getModuleBody(mlirModule).getOps<LLVM::GlobalOp>()) { llvm::Type *type = convertType(op.getType()); llvm::Constant *cst = nullptr; - if (op.getValueOrNull()) { + const bool deferValueAttrToPass2 = op.getValueOrNull() && + !op.getInitializerBlock() && + !isa<StringAttr>(op.getValueOrNull()); + if (op.getValueOrNull() && !deferValueAttrToPass2) { // String attributes are treated separately because they cannot appear as // in-function constants and are thus not supported by getLLVMConstant. if (auto strAttr = dyn_cast_or_null<StringAttr>(op.getValueOrNull())) { cst = llvm::ConstantDataArray::getString( llvmModule->getContext(), strAttr.getValue(), /*AddNull=*/false); type = cst->getType(); - } else if (!(cst = getLLVMConstant(type, op.getValueOrNull(), op.getLoc(), - *this))) { - return failure(); } } @@ -1216,10 +1222,14 @@ LogicalResult ModuleTranslation::convertGlobalsAndAliases() { // external to have initializers. If MLIR does not provide an initializer, // default to undef. bool dropInitializer = shouldDropGlobalInitializer(linkage, cst); - if (!dropInitializer && !cst) - cst = llvm::UndefValue::get(type); - else if (dropInitializer && cst) + if (!deferValueAttrToPass2) { + if (!dropInitializer && !cst) + cst = llvm::UndefValue::get(type); + else if (dropInitializer && cst) + cst = nullptr; + } else { cst = nullptr; + } auto *var = new llvm::GlobalVariable( *llvmModule, type, op.getConstant(), linkage, cst, op.getSymName(), @@ -1249,6 +1259,7 @@ LogicalResult ModuleTranslation::convertGlobalsAndAliases() { var->setVisibility(convertVisibilityToLLVM(op.getVisibility_())); globalsMapping.try_emplace(op, var); + globalsByNameMapping.try_emplace(op.getSymName(), var); // Add debug information if present. if (op.getDbgExprs()) { @@ -1306,6 +1317,28 @@ LogicalResult ModuleTranslation::convertGlobalsAndAliases() { var->addAttributes(*convertedTargetSpecificAttrs); } + // Value-attribute initializers may reference other globals by symbol name. + // Register every global above before materializing those constants. + for (auto op : getModuleBody(mlirModule).getOps<LLVM::GlobalOp>()) { + if (!op.getValueOrNull() || op.getInitializerBlock() || + isa<StringAttr>(op.getValueOrNull())) + continue; + + llvm::Type *type = convertType(op.getType()); + llvm::Constant *cst = + getLLVMConstant(type, op.getValueOrNull(), op.getLoc(), *this); + if (!cst) + return failure(); + + auto linkage = convertLinkageToLLVM(op.getLinkage()); + bool dropInitializer = shouldDropGlobalInitializer(linkage, cst); + auto *var = cast<llvm::GlobalVariable>(lookupGlobal(op)); + if (dropInitializer) + var->setInitializer(nullptr); + else + var->setInitializer(cst); + } + // Create all llvm::GlobalAlias for (auto op : getModuleBody(mlirModule).getOps<LLVM::AliasOp>()) { llvm::Type *type = convertType(op.getType()); diff --git a/mlir/test/Dialect/LLVMIR/invalid.mlir b/mlir/test/Dialect/LLVMIR/invalid.mlir index cb5148a0ca9ae..e80094df1eed2 100644 --- a/mlir/test/Dialect/LLVMIR/invalid.mlir +++ b/mlir/test/Dialect/LLVMIR/invalid.mlir @@ -1957,6 +1957,16 @@ llvm.mlir.global internal constant @bad_array_attr_simple_type() : !llvm.array<2 // ----- +llvm.mlir.global internal constant @bad_ptr_array_attr_leaf() : !llvm.array<2 x ptr> { + // expected-error@below {{pointer array element at index 0 must be a flat symbol reference, zero, undef, or poison}} + %0 = llvm.mlir.constant([1 : i32, @s0]) : !llvm.array<2 x ptr> + llvm.return %0 : !llvm.array<2 x ptr> +} + +llvm.mlir.global private constant @s0("a\00") {addr_space = 0 : i32} : !llvm.array<2 x i8> + +// ----- + llvm.func @inlineAsmMustTail(%arg0: i32, %arg1 : !llvm.ptr) { // expected-error@+1 {{op tail call kind 'musttail' is not supported}} %8 = llvm.inline_asm tail_call_kind = <musttail> "foo", "=r,=r,r" %arg0 : (i32) -> !llvm.struct<(i8, i8)> diff --git a/mlir/test/Target/LLVMIR/llvmir-global-addressof-leaf.mlir b/mlir/test/Target/LLVMIR/llvmir-global-addressof-leaf.mlir new file mode 100644 index 0000000000000..93b43537ed71f --- /dev/null +++ b/mlir/test/Target/LLVMIR/llvmir-global-addressof-leaf.mlir @@ -0,0 +1,19 @@ +// RUN: mlir-translate -mlir-to-llvmir %s | FileCheck %s + +// Per-element string globals referenced by the array-of-pointers global below. +llvm.mlir.global private constant @s0("a\00") {addr_space = 0 : i32} : !llvm.array<2 x i8> +llvm.mlir.global private constant @s1("b\00") {addr_space = 0 : i32} : !llvm.array<2 x i8> +llvm.mlir.global private constant @s2("c\00") {addr_space = 0 : i32} : !llvm.array<2 x i8> + +// Single llvm.mlir.constant with a FlatSymbolRefAttr leaf per array element, +// emitted inside the global's initializer region. This shape lets MLIR's +// translator route the lowering through llvm::ConstantArray::get once +// (the existing array-of-ArrayAttr path in getLLVMConstant), avoiding the +// O(N^2) per-element ConstantFoldInsertValueInstruction blowup that a chain +// of llvm.insertvalue ops would otherwise force on large arrays. +llvm.mlir.global external constant @ptrs() : !llvm.array<3 x ptr> { + %0 = llvm.mlir.constant([@s0, @s1, @s2]) : !llvm.array<3 x ptr> + llvm.return %0 : !llvm.array<3 x ptr> +} + +// CHECK: @ptrs = constant [3 x ptr] [ptr @s0, ptr @s1, ptr @s2] >From 28cce6cb25599e6beb38347fde7641ee20f2a632 Mon Sep 17 00:00:00 2001 From: Adam Smith <[email protected]> Date: Mon, 18 May 2026 16:44:51 -0700 Subject: [PATCH 2/4] [CIR] Lower pointer const_array globals without insertvalue chains When a cir.global initializer is a const_array of global_view or null pointer elements with no trailing zero padding, emit the global with a single aggregate value attribute instead of an initializer region full of llvm.insertvalue ops. That pairs with the MLIR change that lets llvm.mlir.global carry [@g0, @g1, ...] on pointer arrays and translates them to one llvm::ConstantArray. Indexed global_view elements and non-attribute representable cases still use the existing insertvalue lowering path. --- clang/include/clang/CIR/LoweringHelpers.h | 3 +- .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 20 ++++- clang/lib/CIR/Lowering/LoweringHelpers.cpp | 90 ++++++++++++++++++- .../global-pointer-array-fast-lowering.cpp | 16 ++++ .../CIR/Lowering/const-array-of-pointers.cir | 18 ++++ 5 files changed, 144 insertions(+), 3 deletions(-) create mode 100644 clang/test/CIR/CodeGen/global-pointer-array-fast-lowering.cpp create mode 100644 clang/test/CIR/Lowering/const-array-of-pointers.cir diff --git a/clang/include/clang/CIR/LoweringHelpers.h b/clang/include/clang/CIR/LoweringHelpers.h index 66e99c7e84416..3f3e939621c37 100644 --- a/clang/include/clang/CIR/LoweringHelpers.h +++ b/clang/include/clang/CIR/LoweringHelpers.h @@ -35,7 +35,8 @@ convertToDenseElementsAttr(cir::ConstArrayAttr attr, std::optional<mlir::Attribute> lowerConstArrayAttr(cir::ConstArrayAttr constArr, - const mlir::TypeConverter *converter); + const mlir::TypeConverter *converter, + mlir::ModuleOp moduleOp = {}); mlir::Value getConstAPInt(mlir::OpBuilder &bld, mlir::Location loc, mlir::Type typ, const llvm::APInt &val); diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index 2720dd2500a94..ddf4df9ff42bc 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -483,7 +483,6 @@ mlir::Value CIRAttrToValue::visitCirAttr(cir::ConstArrayAttr attr) { // Iteratively lower each constant element of the array. if (auto arrayAttr = mlir::dyn_cast<mlir::ArrayAttr>(attr.getElts())) { for (auto [idx, elt] : llvm::enumerate(arrayAttr)) { - mlir::DataLayout dataLayout(parentOp->getParentOfType<mlir::ModuleOp>()); mlir::Value init = visit(elt); result = mlir::LLVM::InsertValueOp::create(rewriter, loc, result, init, idx); @@ -2510,6 +2509,25 @@ mlir::LogicalResult CIRToLLVMGlobalOpLowering::matchAndRewrite( cir::ConstComplexAttr, cir::GlobalViewAttr, cir::TypeInfoAttr, cir::UndefAttr, cir::PoisonAttr, cir::VTableAttr, cir::ZeroAttr>(init.value())) { + if (auto constArr = mlir::dyn_cast<cir::ConstArrayAttr>(init.value())) { + if (!hasTrailingZeros(constArr)) { + mlir::Type elTy = constArr.getType(); + while (auto arrTy = mlir::dyn_cast<cir::ArrayType>(elTy)) + elTy = arrTy.getElementType(); + if (mlir::isa<cir::PointerType>(elTy)) { + mlir::ModuleOp module = op->getParentOfType<mlir::ModuleOp>(); + if (std::optional<mlir::Attribute> bulkInit = + lowerConstArrayAttr(constArr, typeConverter, module)) { + mlir::SymbolRefAttr comdatAttr = getComdatAttr(op, rewriter); + rewriter.replaceOpWithNewOp<mlir::LLVM::GlobalOp>( + op, llvmType, isConst, linkage, symbol, bulkInit.value(), + alignment, addrSpace, isDsoLocal, isThreadLocal, comdatAttr, + attributes); + return mlir::success(); + } + } + } + } // 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 0786579a601b1..5244b84548945 100644 --- a/clang/lib/CIR/Lowering/LoweringHelpers.cpp +++ b/clang/lib/CIR/Lowering/LoweringHelpers.cpp @@ -12,6 +12,8 @@ #include "clang/CIR/LoweringHelpers.h" #include "mlir/Dialect/LLVMIR/LLVMDialect.h" +#include "mlir/Dialect/LLVMIR/LLVMTypes.h" +#include "mlir/IR/SymbolTable.h" #include "clang/CIR/MissingFeatures.h" mlir::DenseElementsAttr @@ -115,9 +117,71 @@ mlir::DenseElementsAttr convertToDenseElementsAttr( llvm::ArrayRef(values)); } +/// Return true when \p gv can be lowered to a \c FlatSymbolRefAttr leaf without +/// addrspacecast or bitcast (mirrors \c CIRAttrToValue::visitCirAttr). +static bool globalViewMatchesPointerLeaf(cir::GlobalViewAttr gv, + mlir::ModuleOp moduleOp, + const mlir::TypeConverter *converter) { + if (gv.getIndices() || mlir::isa<cir::IntType, cir::VPtrType>(gv.getType())) + return false; + + auto ptrTy = mlir::dyn_cast<cir::PointerType>(gv.getType()); + if (!ptrTy) + return false; + + unsigned sourceAddrSpace = 0; + mlir::Type sourceType; + auto sourceSymbol = + mlir::SymbolTable::lookupSymbolIn(moduleOp, gv.getSymbol()); + if (auto llvmSymbol = mlir::dyn_cast<mlir::LLVM::GlobalOp>(sourceSymbol)) { + sourceType = llvmSymbol.getType(); + sourceAddrSpace = llvmSymbol.getAddrSpace(); + } else if (auto cirSymbol = mlir::dyn_cast<cir::GlobalOp>(sourceSymbol)) { + sourceType = converter->convertType(cirSymbol.getSymType()); + if (auto targetAS = mlir::dyn_cast_if_present<cir::TargetAddressSpaceAttr>( + cirSymbol.getAddrSpaceAttr())) + sourceAddrSpace = targetAS.getValue(); + } else { + return false; + } + + auto llvmDstTy = converter->convertType<mlir::LLVM::LLVMPointerType>(ptrTy); + if (llvmDstTy.getAddressSpace() != sourceAddrSpace) + return false; + + mlir::Type llvmEltTy = converter->convertType(ptrTy.getPointee()); + if (llvmEltTy == sourceType) + return true; + if (auto arrTy = mlir::dyn_cast<mlir::LLVM::LLVMArrayType>(sourceType)) + return llvmEltTy == arrTy.getElementType(); + return false; +} + +/// Lower a single pointer-element of a \c cir.const_array to an LLVM-dialect +/// constant leaf suitable for a bulk \c llvm.mlir.constant. Only handles +/// address-of-global without indices and null pointers; indexed global views +/// must use the per-element \c llvm.insertvalue fallback. +static std::optional<mlir::Attribute> +lowerPointerElementAttr(mlir::Attribute elt, mlir::MLIRContext *ctx, + mlir::ModuleOp moduleOp, + const mlir::TypeConverter *converter) { + if (auto gv = mlir::dyn_cast<cir::GlobalViewAttr>(elt)) { + if (!moduleOp || !globalViewMatchesPointerLeaf(gv, moduleOp, converter)) + return std::nullopt; + return gv.getSymbol(); + } + if (auto nullPtr = mlir::dyn_cast<cir::ConstPtrAttr>(elt)) { + if (nullPtr.isNullValue()) + return mlir::LLVM::ZeroAttr::get(ctx); + return std::nullopt; + } + return std::nullopt; +} + std::optional<mlir::Attribute> lowerConstArrayAttr(cir::ConstArrayAttr constArr, - const mlir::TypeConverter *converter) { + const mlir::TypeConverter *converter, + mlir::ModuleOp moduleOp) { // Ensure ConstArrayAttr has a type. const auto typedConstArr = mlir::cast<mlir::TypedAttr>(constArr); @@ -132,6 +196,13 @@ lowerConstArrayAttr(cir::ConstArrayAttr constArr, type = arrayType.getElementType(); } + if (auto eltsArr = mlir::dyn_cast<mlir::ArrayAttr>(constArr.getElts())) { + for (mlir::Attribute elt : eltsArr) { + if (mlir::isa<cir::PoisonAttr>(elt)) + return std::nullopt; + } + } + if (mlir::isa<mlir::StringAttr>(constArr.getElts())) return convertStringAttrToDenseElementsAttr(constArr, converter->convertType(type)); @@ -143,6 +214,23 @@ lowerConstArrayAttr(cir::ConstArrayAttr constArr, return convertToDenseElementsAttr<cir::FPAttr, mlir::APFloat>( constArr, dims, type, converter->convertType(type)); + if (mlir::isa<cir::PointerType>(type)) { + auto eltsArr = mlir::dyn_cast<mlir::ArrayAttr>(constArr.getElts()); + if (!eltsArr) + return std::nullopt; + llvm::SmallVector<mlir::Attribute> lowered; + lowered.reserve(eltsArr.size()); + mlir::MLIRContext *ctx = constArr.getContext(); + for (mlir::Attribute elt : eltsArr) { + std::optional<mlir::Attribute> llvmElt = + lowerPointerElementAttr(elt, ctx, moduleOp, converter); + if (!llvmElt) + return std::nullopt; + lowered.push_back(*llvmElt); + } + return mlir::ArrayAttr::get(ctx, lowered); + } + return std::nullopt; } diff --git a/clang/test/CIR/CodeGen/global-pointer-array-fast-lowering.cpp b/clang/test/CIR/CodeGen/global-pointer-array-fast-lowering.cpp new file mode 100644 index 0000000000000..ecb4fb8e4722d --- /dev/null +++ b/clang/test/CIR/CodeGen/global-pointer-array-fast-lowering.cpp @@ -0,0 +1,16 @@ +// 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=OGCG --input-file=%t.ll %s + +const char *names[] = { "a", "b", "c" }; +int len() { return sizeof(names)/sizeof(*names); } + +// CIR: cir.global {{.*}}@names = #cir.const_array<[#cir.global_view<@".str"> : !cir.ptr<!s8i>, #cir.global_view<@".str.1"> : !cir.ptr<!s8i>, #cir.global_view<@".str.2"> : !cir.ptr<!s8i>]> + +// LLVM: @names = {{.*}}global [3 x ptr] [ptr @.str{{.*}}, ptr @.str{{.*}}, ptr @.str{{.*}}] +// LLVM-NOT: insertvalue + +// OGCG: @names = {{.*}}global [3 x ptr] [ptr @.str{{.*}}, ptr @.str{{.*}}, ptr @.str{{.*}}] diff --git a/clang/test/CIR/Lowering/const-array-of-pointers.cir b/clang/test/CIR/Lowering/const-array-of-pointers.cir new file mode 100644 index 0000000000000..47797c0cf3271 --- /dev/null +++ b/clang/test/CIR/Lowering/const-array-of-pointers.cir @@ -0,0 +1,18 @@ +// RUN: cir-opt %s --cir-to-llvm -o - | FileCheck %s + +!s32i = !cir.int<s, 32> + +module attributes {cir.triple = "x86_64-unknown-linux-gnu"} { + cir.global "private" constant cir_private dso_local @g0 = #cir.int<0> : !s32i + cir.global "private" constant cir_private dso_local @g1 = #cir.int<1> : !s32i + cir.global "private" constant cir_private dso_local @g2 = #cir.int<2> : !s32i + + cir.global constant external dso_local @ptrs = #cir.const_array<[ + #cir.global_view<@g0> : !cir.ptr<!s32i>, + #cir.global_view<@g1> : !cir.ptr<!s32i>, + #cir.global_view<@g2> : !cir.ptr<!s32i> + ]> : !cir.array<!cir.ptr<!s32i> x 3> +} + +// CHECK-LABEL: llvm.mlir.global external constant @ptrs( +// CHECK-NOT: llvm.insertvalue >From 7c831ffdb94f4ed05caff5790a6b6e552fb99944 Mon Sep 17 00:00:00 2001 From: Adam Smith <[email protected]> Date: Wed, 20 May 2026 10:46:01 -0700 Subject: [PATCH 3/4] [CIR] Extend bulk const_array global lowering for review on #198427 Review follow-up: allow the fast llvm.mlir.global aggregate path for const_array globals whose leaves are integers or floats (not only pointers), document when bulk lowering is skipped, speed up hasTrailingZeros on large arrays, recurse poison checks through nested const_array attributes, and hoist const_array handling out of the shared region-initializer branch. Tests cover int and multi-dim int codegen, trailing-zero and address-space mismatch fallbacks (mlir insertvalue regions), and multi-dim int bulk lowering in cir-opt. --- .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 79 ++++++++++++------- clang/lib/CIR/Lowering/LoweringHelpers.cpp | 24 ++++-- .../global-pointer-array-fast-lowering.cpp | 16 +++- .../const-array-bulk-lowering-fallbacks.cir | 29 +++++++ 4 files changed, 113 insertions(+), 35 deletions(-) create mode 100644 clang/test/CIR/Lowering/const-array-bulk-lowering-fallbacks.cir diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index ddf4df9ff42bc..b14d4ae3a9051 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -1958,13 +1958,33 @@ mlir::LogicalResult CIRToLLVMStoreOpLowering::matchAndRewrite( return mlir::LogicalResult::success(); } +static mlir::Type getConstArrayLeafType(mlir::Type ty) { + while (auto arrTy = mlir::dyn_cast<cir::ArrayType>(ty)) + ty = arrTy.getElementType(); + return ty; +} + +static bool isBulkLowerableConstArrayLeaf(mlir::Type leafTy) { + return mlir::isa<cir::PointerType, cir::IntType, cir::FPTypeInterface>( + leafTy); +} + bool hasTrailingZeros(cir::ConstArrayAttr attr) { - auto array = mlir::dyn_cast<mlir::ArrayAttr>(attr.getElts()); - return attr.hasTrailingZeros() || - (array && std::count_if(array.begin(), array.end(), [](auto elt) { - auto ar = dyn_cast<cir::ConstArrayAttr>(elt); - return ar && hasTrailingZeros(ar); - })); + if (attr.hasTrailingZeros()) + return true; + + auto elts = mlir::dyn_cast<mlir::ArrayAttr>(attr.getElts()); + if (!elts) + return false; + + auto cirArrTy = mlir::dyn_cast<cir::ArrayType>(attr.getType()); + if (!cirArrTy || !mlir::isa<cir::ArrayType>(cirArrTy.getElementType())) + return false; + + return llvm::any_of(elts, [](mlir::Attribute elt) { + auto nested = mlir::dyn_cast<cir::ConstArrayAttr>(elt); + return nested && hasTrailingZeros(nested); + }); } mlir::LogicalResult CIRToLLVMConstantOpLowering::matchAndRewrite( @@ -2504,30 +2524,33 @@ mlir::LogicalResult CIRToLLVMGlobalOpLowering::matchAndRewrite( op.emitError() << "unsupported initializer '" << init.value() << "'"; return mlir::failure(); } - } else if (mlir::isa<cir::ConstArrayAttr, cir::ConstVectorAttr, - cir::ConstRecordAttr, cir::ConstPtrAttr, - cir::ConstComplexAttr, cir::GlobalViewAttr, - cir::TypeInfoAttr, cir::UndefAttr, cir::PoisonAttr, - cir::VTableAttr, cir::ZeroAttr>(init.value())) { - if (auto constArr = mlir::dyn_cast<cir::ConstArrayAttr>(init.value())) { - if (!hasTrailingZeros(constArr)) { - mlir::Type elTy = constArr.getType(); - while (auto arrTy = mlir::dyn_cast<cir::ArrayType>(elTy)) - elTy = arrTy.getElementType(); - if (mlir::isa<cir::PointerType>(elTy)) { - mlir::ModuleOp module = op->getParentOfType<mlir::ModuleOp>(); - if (std::optional<mlir::Attribute> bulkInit = - lowerConstArrayAttr(constArr, typeConverter, module)) { - mlir::SymbolRefAttr comdatAttr = getComdatAttr(op, rewriter); - rewriter.replaceOpWithNewOp<mlir::LLVM::GlobalOp>( - op, llvmType, isConst, linkage, symbol, bulkInit.value(), - alignment, addrSpace, isDsoLocal, isThreadLocal, comdatAttr, - attributes); - return mlir::success(); - } - } + } else if (auto constArr = + mlir::dyn_cast<cir::ConstArrayAttr>(init.value())) { + // Bulk-emit llvm.mlir.global when lowerConstArrayAttr can build the + // whole initializer as one aggregate attribute (no insertvalue + // region). Skip arrays with trailing-zero compression: those need + // per-element zero/undef setup. Leaf type must match what + // lowerConstArrayAttr handles (pointers, integers, floats today). + if (!hasTrailingZeros(constArr) && + isBulkLowerableConstArrayLeaf( + getConstArrayLeafType(constArr.getType()))) { + mlir::ModuleOp module = op->getParentOfType<mlir::ModuleOp>(); + if (std::optional<mlir::Attribute> bulkInit = + lowerConstArrayAttr(constArr, typeConverter, module)) { + mlir::SymbolRefAttr comdatAttr = getComdatAttr(op, rewriter); + rewriter.replaceOpWithNewOp<mlir::LLVM::GlobalOp>( + op, llvmType, isConst, linkage, symbol, bulkInit.value(), + alignment, addrSpace, isDsoLocal, isThreadLocal, comdatAttr, + attributes); + return mlir::success(); } } + return matchAndRewriteRegionInitializedGlobal(op, init.value(), rewriter); + } else if (mlir::isa<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 5244b84548945..a89816949ef43 100644 --- a/clang/lib/CIR/Lowering/LoweringHelpers.cpp +++ b/clang/lib/CIR/Lowering/LoweringHelpers.cpp @@ -142,6 +142,8 @@ static bool globalViewMatchesPointerLeaf(cir::GlobalViewAttr gv, cirSymbol.getAddrSpaceAttr())) sourceAddrSpace = targetAS.getValue(); } else { + // cir.func and other symbols not yet lowered to globals cannot be used as + // bulk constant leaves; those cases keep the insertvalue fallback. return false; } @@ -178,6 +180,20 @@ lowerPointerElementAttr(mlir::Attribute elt, mlir::MLIRContext *ctx, return std::nullopt; } +static bool containsPoison(mlir::Attribute attr) { + if (mlir::isa<cir::PoisonAttr>(attr)) + return true; + if (auto elts = mlir::dyn_cast<mlir::ArrayAttr>(attr)) + return llvm::any_of(elts, containsPoison); + 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, containsPoison); + } + return false; +} + std::optional<mlir::Attribute> lowerConstArrayAttr(cir::ConstArrayAttr constArr, const mlir::TypeConverter *converter, @@ -196,12 +212,8 @@ lowerConstArrayAttr(cir::ConstArrayAttr constArr, type = arrayType.getElementType(); } - if (auto eltsArr = mlir::dyn_cast<mlir::ArrayAttr>(constArr.getElts())) { - for (mlir::Attribute elt : eltsArr) { - if (mlir::isa<cir::PoisonAttr>(elt)) - return std::nullopt; - } - } + if (containsPoison(constArr)) + return std::nullopt; if (mlir::isa<mlir::StringAttr>(constArr.getElts())) return convertStringAttrToDenseElementsAttr(constArr, diff --git a/clang/test/CIR/CodeGen/global-pointer-array-fast-lowering.cpp b/clang/test/CIR/CodeGen/global-pointer-array-fast-lowering.cpp index ecb4fb8e4722d..1252ccfa72de1 100644 --- a/clang/test/CIR/CodeGen/global-pointer-array-fast-lowering.cpp +++ b/clang/test/CIR/CodeGen/global-pointer-array-fast-lowering.cpp @@ -6,7 +6,11 @@ // RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s const char *names[] = { "a", "b", "c" }; -int len() { return sizeof(names)/sizeof(*names); } +const int table[] = { 0, 1, 2, 3 }; +const int matrix[2][2] = { { 1, 2 }, { 3, 4 } }; +int len() { + return sizeof(names) / sizeof(*names) + table[0] + matrix[1][1]; +} // CIR: cir.global {{.*}}@names = #cir.const_array<[#cir.global_view<@".str"> : !cir.ptr<!s8i>, #cir.global_view<@".str.1"> : !cir.ptr<!s8i>, #cir.global_view<@".str.2"> : !cir.ptr<!s8i>]> @@ -14,3 +18,13 @@ int len() { return sizeof(names)/sizeof(*names); } // LLVM-NOT: insertvalue // OGCG: @names = {{.*}}global [3 x ptr] [ptr @.str{{.*}}, ptr @.str{{.*}}, ptr @.str{{.*}}] + +// CIR: cir.global {{.*}}table = #cir.const_array<[#cir.int<0> : !s32i, #cir.int<1> : !s32i, #cir.int<2> : !s32i, #cir.int<3> : !s32i]> +// LLVM: {{.*}}table{{.*}} = {{.*}}constant [4 x i32] [i32 0, i32 1, i32 2, i32 3] +// LLVM-NOT: insertvalue +// OGCG: {{.*}}table{{.*}} = {{.*}}constant [4 x i32] [i32 0, i32 1, i32 2, i32 3] + +// CIR: cir.global {{.*}}matrix = #cir.const_array< +// LLVM: {{.*}}matrix{{.*}} = {{.*}}constant [2 x [2 x i32]] +// LLVM-NOT: insertvalue +// OGCG: {{.*}}matrix{{.*}} = {{.*}}constant [2 x [2 x i32]] diff --git a/clang/test/CIR/Lowering/const-array-bulk-lowering-fallbacks.cir b/clang/test/CIR/Lowering/const-array-bulk-lowering-fallbacks.cir new file mode 100644 index 0000000000000..1e305e64c86d2 --- /dev/null +++ b/clang/test/CIR/Lowering/const-array-bulk-lowering-fallbacks.cir @@ -0,0 +1,29 @@ +// RUN: cir-opt %s --cir-to-llvm -o - | FileCheck %s + +!s32i = !cir.int<s, 32> +!s8i = !cir.int<s, 8> + +module attributes {cir.triple = "x86_64-unknown-linux-gnu"} { + cir.global external @tz_arr = + #cir.const_array<"ab" : !cir.array<!s8i x 2>, trailing_zeros> : + !cir.array<!s8i x 8> + + cir.global external target_address_space(1) @as1 = #cir.int<1> : !s32i + cir.global external @as_mismatch = + #cir.const_array<[#cir.global_view<@as1> : !cir.ptr<!s32i>]> : + !cir.array<!cir.ptr<!s32i> x 1> + + cir.global external @matrix = #cir.const_array<[ + #cir.const_array<[#cir.int<1> : !s32i, #cir.int<2> : !s32i]> : !cir.array<!s32i x 2>, + #cir.const_array<[#cir.int<3> : !s32i, #cir.int<4> : !s32i]> : !cir.array<!s32i x 2> + ]> : !cir.array<!cir.array<!s32i x 2> x 2> +} + +// CHECK-LABEL: llvm.mlir.global external @tz_arr +// CHECK: llvm.insertvalue + +// CHECK-LABEL: llvm.mlir.global external @as_mismatch +// CHECK: llvm.insertvalue + +// CHECK-LABEL: llvm.mlir.global external @matrix( +// CHECK-SAME: dense< >From 8fa2f0d6a94afc6425a969d69ea6dcad8f2d0635 Mon Sep 17 00:00:00 2001 From: Adam Smith <[email protected]> Date: Wed, 20 May 2026 13:03:47 -0700 Subject: [PATCH 4/4] [CIR] Add bool bulk globals and #198427 fallback tests Extend bulk const_array global lowering to cir.bool leaves and add review-requested coverage: multi-dim pointer-array insertvalue fallback (@ptr2d) and bool constant-array codegen without insertvalue chains. --- .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 6 +++--- clang/lib/CIR/Lowering/LoweringHelpers.cpp | 13 +++++++++++- .../global-pointer-array-fast-lowering.cpp | 9 ++++++++- .../const-array-bulk-lowering-fallbacks.cir | 20 +++++++++++++++++++ 4 files changed, 43 insertions(+), 5 deletions(-) diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index b14d4ae3a9051..ba0b62ed710d1 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -1965,8 +1965,8 @@ static mlir::Type getConstArrayLeafType(mlir::Type ty) { } static bool isBulkLowerableConstArrayLeaf(mlir::Type leafTy) { - return mlir::isa<cir::PointerType, cir::IntType, cir::FPTypeInterface>( - leafTy); + return mlir::isa<cir::PointerType, cir::IntType, cir::BoolType, + cir::FPTypeInterface>(leafTy); } bool hasTrailingZeros(cir::ConstArrayAttr attr) { @@ -2530,7 +2530,7 @@ mlir::LogicalResult CIRToLLVMGlobalOpLowering::matchAndRewrite( // whole initializer as one aggregate attribute (no insertvalue // region). Skip arrays with trailing-zero compression: those need // per-element zero/undef setup. Leaf type must match what - // lowerConstArrayAttr handles (pointers, integers, floats today). + // lowerConstArrayAttr handles (pointers, integers, bools, floats). if (!hasTrailingZeros(constArr) && isBulkLowerableConstArrayLeaf( getConstArrayLeafType(constArr.getType()))) { diff --git a/clang/lib/CIR/Lowering/LoweringHelpers.cpp b/clang/lib/CIR/Lowering/LoweringHelpers.cpp index a89816949ef43..3f6b9ac0dcdf4 100644 --- a/clang/lib/CIR/Lowering/LoweringHelpers.cpp +++ b/clang/lib/CIR/Lowering/LoweringHelpers.cpp @@ -35,7 +35,8 @@ convertStringAttrToDenseElementsAttr(cir::ConstArrayAttr attr, } template <> mlir::APInt getZeroInitFromType(mlir::Type ty) { - assert(mlir::isa<cir::IntType>(ty) && "expected int type"); + if (mlir::isa<cir::BoolType>(ty)) + return mlir::APInt::getZero(1); const auto intTy = mlir::cast<cir::IntType>(ty); return mlir::APInt::getZero(intTy.getWidth()); } @@ -80,6 +81,12 @@ void convertToDenseElementsAttrImpl( auto arrayAttr = mlir::cast<mlir::ArrayAttr>(attr.getElts()); for (auto eltAttr : arrayAttr) { + if (auto boolAttr = mlir::dyn_cast<cir::BoolAttr>(eltAttr)) { + if constexpr (std::is_same_v<StorageTy, mlir::APInt>) { + values[currentIndex++] = mlir::APInt(1, (uint64_t)boolAttr.getValue()); + continue; + } + } if (auto valueAttr = mlir::dyn_cast<AttrTy>(eltAttr)) { values[currentIndex++] = valueAttr.getValue(); continue; @@ -222,6 +229,10 @@ lowerConstArrayAttr(cir::ConstArrayAttr constArr, return convertToDenseElementsAttr<cir::IntAttr, mlir::APInt>( constArr, dims, type, converter->convertType(type)); + if (mlir::isa<cir::BoolType>(type)) + return convertToDenseElementsAttr<cir::IntAttr, mlir::APInt>( + constArr, dims, type, converter->convertType(type)); + if (mlir::isa<cir::FPTypeInterface>(type)) return convertToDenseElementsAttr<cir::FPAttr, mlir::APFloat>( constArr, dims, type, converter->convertType(type)); diff --git a/clang/test/CIR/CodeGen/global-pointer-array-fast-lowering.cpp b/clang/test/CIR/CodeGen/global-pointer-array-fast-lowering.cpp index 1252ccfa72de1..8cab446ad63ac 100644 --- a/clang/test/CIR/CodeGen/global-pointer-array-fast-lowering.cpp +++ b/clang/test/CIR/CodeGen/global-pointer-array-fast-lowering.cpp @@ -8,8 +8,10 @@ const char *names[] = { "a", "b", "c" }; const int table[] = { 0, 1, 2, 3 }; const int matrix[2][2] = { { 1, 2 }, { 3, 4 } }; +const bool flags[] = { true, false, true }; int len() { - return sizeof(names) / sizeof(*names) + table[0] + matrix[1][1]; + return sizeof(names) / sizeof(*names) + table[0] + matrix[1][1] + + flags[2]; } // CIR: cir.global {{.*}}@names = #cir.const_array<[#cir.global_view<@".str"> : !cir.ptr<!s8i>, #cir.global_view<@".str.1"> : !cir.ptr<!s8i>, #cir.global_view<@".str.2"> : !cir.ptr<!s8i>]> @@ -28,3 +30,8 @@ int len() { // LLVM: {{.*}}matrix{{.*}} = {{.*}}constant [2 x [2 x i32]] // LLVM-NOT: insertvalue // OGCG: {{.*}}matrix{{.*}} = {{.*}}constant [2 x [2 x i32]] + +// CIR: cir.global {{.*}}flags = #cir.const_array<[#true, #false, #true]> : !cir.array<!cir.bool x 3> +// LLVM: {{.*}}flags{{.*}} = {{.*}}constant [3 x i8] +// LLVM-NOT: insertvalue +// OGCG: {{.*}}flags{{.*}} = {{.*}}constant [3 x i8] diff --git a/clang/test/CIR/Lowering/const-array-bulk-lowering-fallbacks.cir b/clang/test/CIR/Lowering/const-array-bulk-lowering-fallbacks.cir index 1e305e64c86d2..41864da616e3c 100644 --- a/clang/test/CIR/Lowering/const-array-bulk-lowering-fallbacks.cir +++ b/clang/test/CIR/Lowering/const-array-bulk-lowering-fallbacks.cir @@ -17,6 +17,23 @@ module attributes {cir.triple = "x86_64-unknown-linux-gnu"} { #cir.const_array<[#cir.int<1> : !s32i, #cir.int<2> : !s32i]> : !cir.array<!s32i x 2>, #cir.const_array<[#cir.int<3> : !s32i, #cir.int<4> : !s32i]> : !cir.array<!s32i x 2> ]> : !cir.array<!cir.array<!s32i x 2> x 2> + + cir.global "private" constant cir_private dso_local @s0 = + #cir.const_array<"x" : !cir.array<!s8i x 1>, trailing_zeros> : + !cir.array<!s8i x 2> + cir.global "private" constant cir_private dso_local @s1 = + #cir.const_array<"y" : !cir.array<!s8i x 1>, trailing_zeros> : + !cir.array<!s8i x 2> + cir.global external @ptr2d = #cir.const_array<[ + #cir.const_array<[ + #cir.global_view<@s0> : !cir.ptr<!s8i>, + #cir.global_view<@s1> : !cir.ptr<!s8i> + ]> : !cir.array<!cir.ptr<!s8i> x 2>, + #cir.const_array<[ + #cir.global_view<@s0> : !cir.ptr<!s8i>, + #cir.global_view<@s1> : !cir.ptr<!s8i> + ]> : !cir.array<!cir.ptr<!s8i> x 2> + ]> : !cir.array<!cir.array<!cir.ptr<!s8i> x 2> x 2> } // CHECK-LABEL: llvm.mlir.global external @tz_arr @@ -27,3 +44,6 @@ module attributes {cir.triple = "x86_64-unknown-linux-gnu"} { // CHECK-LABEL: llvm.mlir.global external @matrix( // CHECK-SAME: dense< + +// CHECK-LABEL: llvm.mlir.global external @ptr2d +// CHECK: llvm.insertvalue _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
