[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> &currentDims, 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