[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