[clang] [CIR] Lower global ConstArrayAttr with string elements to LLVM string constant (PR #194988)
David Rivera via cfe-commits
cfe-commits at lists.llvm.org
Wed Apr 29 20:50:02 PDT 2026
https://github.com/RiverDave updated https://github.com/llvm/llvm-project/pull/194988
>From 2387cc1063759d7892e422cf0fdd47e94019c5e5 Mon Sep 17 00:00:00 2001
From: David Rivera <davidriverg at gmail.com>
Date: Wed, 29 Apr 2026 20:32:52 -0400
Subject: [PATCH 1/3] [CIR] Lower global ConstArrayAttr with string elements to
LLVM string constant
---
clang/include/clang/CIR/Dialect/IR/CIROps.td | 4 ++
.../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 45 ++++++++++++++++---
clang/test/CIR/Lowering/array.cpp | 10 +++++
3 files changed, 54 insertions(+), 5 deletions(-)
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index b30dd980f5569..6ef0a6d7d635f 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -2929,6 +2929,10 @@ def CIR_GlobalOp : CIR_Op<"global", [
cir::GlobalOp op, mlir::Attribute init,
mlir::ConversionPatternRewriter &rewriter) const;
+ mlir::LogicalResult lowerInitializerForConstArray(
+ cir::GlobalOp op, mlir::Attribute &init,
+ mlir::ConversionPatternRewriter &rewriter) const;
+
void setupRegionInitializedLLVMGlobalOp(
cir::GlobalOp op, mlir::ConversionPatternRewriter &rewriter) const;
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 32be32f7e319f..0c4d8fd93490c 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -2424,6 +2424,33 @@ CIRToLLVMGlobalOpLowering::matchAndRewriteRegionInitializedGlobal(
return mlir::success();
}
+mlir::LogicalResult CIRToLLVMGlobalOpLowering::lowerInitializerForConstArray(
+ cir::GlobalOp op, mlir::Attribute &init,
+ mlir::ConversionPatternRewriter &rewriter) const {
+ auto constArr = mlir::cast<cir::ConstArrayAttr>(init);
+
+ // Initializer is a constant array: convert it to a compatible LLVM init.
+ if (auto strAttr = mlir::dyn_cast<mlir::StringAttr>(constArr.getElts())) {
+ llvm::SmallString<256> literal(strAttr.getValue());
+ if (constArr.getTrailingZerosNum())
+ literal.append(constArr.getTrailingZerosNum(), '\0');
+ init = rewriter.getStringAttr(literal);
+ return mlir::success();
+ }
+
+ if (mlir::isa<mlir::ArrayAttr>(constArr.getElts())) {
+ // If failed to use a compact attribute as an initializer, we initialize
+ // elements individually.
+ if (auto val = lowerConstArrayAttr(constArr, getTypeConverter()))
+ init = val.value();
+ return mlir::success();
+ }
+
+ return op.emitError() << "unsupported lowering for #cir.const_array with "
+ "value "
+ << constArr.getElts();
+}
+
mlir::LogicalResult CIRToLLVMGlobalOpLowering::matchAndRewrite(
cir::GlobalOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const {
@@ -2469,11 +2496,19 @@ 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())) {
+ } else if (mlir::isa<cir::ConstArrayAttr>(init.value())) {
+ if (mlir::failed(lowerInitializerForConstArray(op, init.value(), rewriter)))
+ return mlir::failure();
+ // If lowerInitializerForConstArray converted the initializer to a
+ // non-CIR attribute (e.g. StringAttr), fall through to direct global
+ // emission below. Otherwise use the region initializer path.
+ if (mlir::isa<cir::ConstArrayAttr>(init.value()))
+ 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/test/CIR/Lowering/array.cpp b/clang/test/CIR/Lowering/array.cpp
index 92d03041d2324..59bd5ab6e5ce6 100644
--- a/clang/test/CIR/Lowering/array.cpp
+++ b/clang/test/CIR/Lowering/array.cpp
@@ -1,6 +1,16 @@
// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll
// RUN: FileCheck --input-file=%t-cir.ll %s
+// Global char arrays with string initializers must lower to a direct
+// [N x i8] c"..." constant, not through an initializer region.
+// CHECK-DAG: @str = constant [6 x i8] c"hello\00"
+extern const char str[] = "hello";
+
+// Binary blob (unsigned char): bytes plus trailing null must also produce a
+// direct constant without an initializer region.
+// CHECK-DAG: @blob = constant [6 x i8] c"\7FELF\00\00"
+extern const unsigned char blob[] = "\x7f\x45\x4c\x46\x00";
+
// CHECK-DAG: @[[FUNC2_ARR:.*]] = private constant [2 x i32] [i32 5, i32 0]
// CHECK-DAG: @[[FUNC3_ARR:.*]] = private constant [2 x i32] [i32 5, i32 6]
// CHECK-DAG: @[[FUNC4_ARR:.*]] = private constant [2 x [1 x i32]] {{.*}}[1 x i32] [i32 5], [1 x i32] [i32 6]{{.*}}
>From e20557e039723c6e7de5ae4b8f3223f9cb8d991f Mon Sep 17 00:00:00 2001
From: David Rivera <davidriverg at gmail.com>
Date: Wed, 29 Apr 2026 20:36:55 -0400
Subject: [PATCH 2/3] fix fmt
---
clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 0c4d8fd93490c..9882e6682df70 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -2497,18 +2497,20 @@ mlir::LogicalResult CIRToLLVMGlobalOpLowering::matchAndRewrite(
return mlir::failure();
}
} else if (mlir::isa<cir::ConstArrayAttr>(init.value())) {
- if (mlir::failed(lowerInitializerForConstArray(op, init.value(), rewriter)))
+ if (mlir::failed(
+ lowerInitializerForConstArray(op, init.value(), rewriter)))
return mlir::failure();
// If lowerInitializerForConstArray converted the initializer to a
// non-CIR attribute (e.g. StringAttr), fall through to direct global
// emission below. Otherwise use the region initializer path.
if (mlir::isa<cir::ConstArrayAttr>(init.value()))
- return matchAndRewriteRegionInitializedGlobal(op, init.value(), rewriter);
+ 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())) {
+ 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.
>From ad284472f1115c52954242a521bddd26fdcb1047 Mon Sep 17 00:00:00 2001
From: David Rivera <davidriverg at gmail.com>
Date: Wed, 29 Apr 2026 23:49:46 -0400
Subject: [PATCH 3/3] [CIR] Handle unsuported dense attributes conversion
---
clang/include/clang/CIR/LoweringHelpers.h | 4 +--
clang/lib/CIR/Lowering/LoweringHelpers.cpp | 41 ++++++++++++++--------
2 files changed, 29 insertions(+), 16 deletions(-)
diff --git a/clang/include/clang/CIR/LoweringHelpers.h b/clang/include/clang/CIR/LoweringHelpers.h
index 66e99c7e84416..505be67f7c5f5 100644
--- a/clang/include/clang/CIR/LoweringHelpers.h
+++ b/clang/include/clang/CIR/LoweringHelpers.h
@@ -24,11 +24,11 @@ template <> mlir::APInt getZeroInitFromType(mlir::Type ty);
template <> mlir::APFloat getZeroInitFromType(mlir::Type ty);
template <typename AttrTy, typename StorageTy>
-void convertToDenseElementsAttrImpl(cir::ConstArrayAttr attr,
+bool convertToDenseElementsAttrImpl(cir::ConstArrayAttr attr,
llvm::SmallVectorImpl<StorageTy> &values);
template <typename AttrTy, typename StorageTy>
-mlir::DenseElementsAttr
+std::optional<mlir::DenseElementsAttr>
convertToDenseElementsAttr(cir::ConstArrayAttr attr,
const llvm::SmallVectorImpl<int64_t> &dims,
mlir::Type type);
diff --git a/clang/lib/CIR/Lowering/LoweringHelpers.cpp b/clang/lib/CIR/Lowering/LoweringHelpers.cpp
index 0786579a601b1..1ca6f18d75388 100644
--- a/clang/lib/CIR/Lowering/LoweringHelpers.cpp
+++ b/clang/lib/CIR/Lowering/LoweringHelpers.cpp
@@ -56,8 +56,9 @@ template <> mlir::APFloat getZeroInitFromType(mlir::Type ty) {
/// \param currentDims the shpae of tensor we're going to convert to
/// \param dimIndex the current dimension we're processing
/// \param currentIndex the current index in the values array
+/// \returns true on success, false if an unsupported element kind was found
template <typename AttrTy, typename StorageTy>
-void convertToDenseElementsAttrImpl(
+bool convertToDenseElementsAttrImpl(
cir::ConstArrayAttr attr, llvm::SmallVectorImpl<StorageTy> &values,
const llvm::SmallVectorImpl<int64_t> ¤tDims, int64_t dimIndex,
int64_t currentIndex) {
@@ -67,7 +68,7 @@ void convertToDenseElementsAttrImpl(
auto intAttr = cir::IntAttr::get(arrayType.getElementType(), element);
values[currentIndex++] = mlir::dyn_cast<AttrTy>(intAttr).getValue();
}
- return;
+ return true;
}
}
@@ -84,8 +85,9 @@ void convertToDenseElementsAttrImpl(
}
if (auto subArrayAttr = mlir::dyn_cast<cir::ConstArrayAttr>(eltAttr)) {
- convertToDenseElementsAttrImpl<AttrTy>(subArrayAttr, values, currentDims,
- dimIndex, currentIndex);
+ if (!convertToDenseElementsAttrImpl<AttrTy>(
+ subArrayAttr, values, currentDims, dimIndex, currentIndex))
+ return false;
currentIndex += elementsSizeInCurrentDim;
continue;
}
@@ -95,12 +97,15 @@ void convertToDenseElementsAttrImpl(
continue;
}
- llvm_unreachable("unknown element in ConstArrayAttr");
+ // Unsupported element kind (e.g. PoisonAttr): signal failure so the
+ // caller can fall back to the region-based initializer path.
+ return false;
}
+ return true;
}
template <typename AttrTy, typename StorageTy>
-mlir::DenseElementsAttr convertToDenseElementsAttr(
+std::optional<mlir::DenseElementsAttr> convertToDenseElementsAttr(
cir::ConstArrayAttr attr, const llvm::SmallVectorImpl<int64_t> &dims,
mlir::Type elementType, mlir::Type convertedElementType) {
unsigned vectorSize = 1;
@@ -108,8 +113,10 @@ mlir::DenseElementsAttr convertToDenseElementsAttr(
vectorSize *= dim;
auto values = llvm::SmallVector<StorageTy, 8>(
vectorSize, getZeroInitFromType<StorageTy>(elementType));
- convertToDenseElementsAttrImpl<AttrTy>(attr, values, dims, /*currentDim=*/0,
- /*initialIndex=*/0);
+ if (!convertToDenseElementsAttrImpl<AttrTy>(attr, values, dims,
+ /*currentDim=*/0,
+ /*initialIndex=*/0))
+ return std::nullopt;
return mlir::DenseElementsAttr::get(
mlir::RankedTensorType::get(dims, convertedElementType),
llvm::ArrayRef(values));
@@ -135,13 +142,19 @@ lowerConstArrayAttr(cir::ConstArrayAttr constArr,
if (mlir::isa<mlir::StringAttr>(constArr.getElts()))
return convertStringAttrToDenseElementsAttr(constArr,
converter->convertType(type));
- if (mlir::isa<cir::IntType>(type))
- return convertToDenseElementsAttr<cir::IntAttr, mlir::APInt>(
- constArr, dims, type, converter->convertType(type));
+ if (mlir::isa<cir::IntType>(type)) {
+ if (auto denseAttr = convertToDenseElementsAttr<cir::IntAttr, mlir::APInt>(
+ constArr, dims, type, converter->convertType(type)))
+ return *denseAttr;
+ return std::nullopt;
+ }
- if (mlir::isa<cir::FPTypeInterface>(type))
- return convertToDenseElementsAttr<cir::FPAttr, mlir::APFloat>(
- constArr, dims, type, converter->convertType(type));
+ if (mlir::isa<cir::FPTypeInterface>(type)) {
+ if (auto denseAttr = convertToDenseElementsAttr<cir::FPAttr, mlir::APFloat>(
+ constArr, dims, type, converter->convertType(type)))
+ return *denseAttr;
+ return std::nullopt;
+ }
return std::nullopt;
}
More information about the cfe-commits
mailing list