[clang] [CIR] Lower global ConstArrayAttr with string elements to LLVM string constant (PR #194988)

via cfe-commits cfe-commits at lists.llvm.org
Wed Apr 29 17:46:51 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: David Rivera (RiverDave)

<details>
<summary>Changes</summary>


Fixes the lowering crash initially reported at: (https://github.com/llvm/llvm-project/pull/187636)

Apparently, `builder.getStringAttr(str)` produces a typeless StringAttr (NoneType). When embedded as the elements of a ConstArrayAttr global initializer, visitCirAttr tried to read the array type from the StringAttr itself rather than from the ConstArrayAttr, got null, and hit an assertion.

Added lowerInitializerForConstArray (matching the incubator's approach) to intercept ConstArrayAttr before visitCirAttr is reached. StringAttr elements are converted directly to an LLVM StringAttr, bypassing the region path entirely. ArrayAttr elements try lowerConstArrayAttr for a dense representation first, then fall back to the region path as before.


---
Full diff: https://github.com/llvm/llvm-project/pull/194988.diff


3 Files Affected:

- (modified) clang/include/clang/CIR/Dialect/IR/CIROps.td (+4) 
- (modified) clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp (+42-5) 
- (modified) clang/test/CIR/Lowering/array.cpp (+10) 


``````````diff
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..9882e6682df70 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,21 @@ 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]{{.*}}

``````````

</details>


https://github.com/llvm/llvm-project/pull/194988


More information about the cfe-commits mailing list