[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 17:37:11 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/2] [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/2] 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.



More information about the cfe-commits mailing list