[CIR] Upstream global initialization for ArrayType (#131657)

This change adds global initialization for ArrayType

Issue #130197
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
index 85f6a00..fef2906 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h
+++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
@@ -45,6 +45,42 @@
     return false;
   }
 
+  // Return true if the value is a null constant such as null pointer, (+0.0)
+  // for floating-point or zero initializer
+  bool isNullValue(mlir::Attribute attr) const {
+    if (mlir::isa<cir::ZeroAttr>(attr))
+      return true;
+
+    if (const auto ptrVal = mlir::dyn_cast<cir::ConstPtrAttr>(attr))
+      return ptrVal.isNullValue();
+
+    if (const auto intVal = mlir::dyn_cast<cir::IntAttr>(attr))
+      return intVal.isNullValue();
+
+    if (const auto boolVal = mlir::dyn_cast<cir::BoolAttr>(attr))
+      return !boolVal.getValue();
+
+    if (auto fpAttr = mlir::dyn_cast<cir::FPAttr>(attr)) {
+      auto fpVal = fpAttr.getValue();
+      bool ignored;
+      llvm::APFloat fv(+0.0);
+      fv.convert(fpVal.getSemantics(), llvm::APFloat::rmNearestTiesToEven,
+                 &ignored);
+      return fv.bitwiseIsEqual(fpVal);
+    }
+
+    if (const auto arrayVal = mlir::dyn_cast<cir::ConstArrayAttr>(attr)) {
+      if (mlir::isa<mlir::StringAttr>(arrayVal.getElts()))
+        return false;
+      for (const auto elt : mlir::cast<mlir::ArrayAttr>(arrayVal.getElts())) {
+        if (!isNullValue(elt))
+          return false;
+      }
+      return true;
+    }
+    return false;
+  }
+
   bool isInt(mlir::Type i) { return mlir::isa<cir::IntType>(i); }
 
   // Creates constant nullptr for pointer type ty.
diff --git a/clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h b/clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h
index 5b22a8e5..ca4e607 100644
--- a/clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h
+++ b/clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h
@@ -20,7 +20,6 @@
 
 #include "CIRGenFunction.h"
 #include "CIRGenModule.h"
-#include "llvm/ADT/SmallVector.h"
 
 namespace clang::CIRGen {
 
@@ -41,6 +40,9 @@
   /// block addresses or PredefinedExprs.
   ConstantEmitter(CIRGenFunction &cgf) : cgm(cgf.cgm), cgf(&cgf) {}
 
+  ConstantEmitter(CIRGenModule &cgm, CIRGenFunction *cgf = nullptr)
+      : cgm(cgm), cgf(cgf) {}
+
   ConstantEmitter(const ConstantEmitter &other) = delete;
   ConstantEmitter &operator=(const ConstantEmitter &other) = delete;
 
@@ -66,7 +68,7 @@
   mlir::Attribute emitAbstract(SourceLocation loc, const APValue &value,
                                QualType t);
 
-  mlir::Attribute tryEmitConstantExpr(const ConstantExpr *CE);
+  mlir::Attribute tryEmitConstantExpr(const ConstantExpr *ce);
 
   // These are private helper routines of the constant emitter that
   // can't actually be private because things are split out into helper
diff --git a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
index 27ed011..a93e8db 100644
--- a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
@@ -225,7 +225,6 @@
   }
   assert(!cir::MissingFeatures::emitNullabilityCheck());
   emitStoreThroughLValue(RValue::get(value), lvalue, true);
-  return;
 }
 
 void CIRGenFunction::emitExprAsInit(const Expr *init, const ValueDecl *d,
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
index 1ea7f62..fc49d6d 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
@@ -158,13 +158,63 @@
 
 // TODO(cir): this can be shared with LLVM's codegen
 static QualType getNonMemoryType(CIRGenModule &cgm, QualType type) {
-  if (auto at = type->getAs<AtomicType>()) {
+  if (const auto *at = type->getAs<AtomicType>()) {
     return cgm.getASTContext().getQualifiedType(at->getValueType(),
                                                 type.getQualifiers());
   }
   return type;
 }
 
+static mlir::Attribute
+emitArrayConstant(CIRGenModule &cgm, mlir::Type desiredType,
+                  mlir::Type commonElementType, unsigned arrayBound,
+                  SmallVectorImpl<mlir::TypedAttr> &elements,
+                  mlir::TypedAttr filler) {
+  const CIRGenBuilderTy &builder = cgm.getBuilder();
+
+  unsigned nonzeroLength = arrayBound;
+  if (elements.size() < nonzeroLength && builder.isNullValue(filler))
+    nonzeroLength = elements.size();
+
+  if (nonzeroLength == elements.size()) {
+    while (nonzeroLength > 0 &&
+           builder.isNullValue(elements[nonzeroLength - 1]))
+      --nonzeroLength;
+  }
+
+  if (nonzeroLength == 0)
+    return cir::ZeroAttr::get(builder.getContext(), desiredType);
+
+  const unsigned trailingZeroes = arrayBound - nonzeroLength;
+
+  // Add a zeroinitializer array filler if we have lots of trailing zeroes.
+  if (trailingZeroes >= 8) {
+    assert(elements.size() >= nonzeroLength &&
+           "missing initializer for non-zero element");
+  } else if (elements.size() != arrayBound) {
+    elements.resize(arrayBound, filler);
+
+    if (filler.getType() != commonElementType)
+      commonElementType = {};
+  }
+
+  if (commonElementType) {
+    SmallVector<mlir::Attribute, 4> eles;
+    eles.reserve(elements.size());
+
+    for (const auto &element : elements)
+      eles.push_back(element);
+
+    return cir::ConstArrayAttr::get(
+        cir::ArrayType::get(builder.getContext(), commonElementType,
+                            arrayBound),
+        mlir::ArrayAttr::get(builder.getContext(), eles));
+  }
+
+  cgm.errorNYI("array with different type elements");
+  return {};
+}
+
 //===----------------------------------------------------------------------===//
 //                             ConstantEmitter
 //===----------------------------------------------------------------------===//
@@ -271,16 +321,57 @@
         cgm.getASTContext().getTargetInfo().useFP16ConversionIntrinsics()) {
       cgm.errorNYI("ConstExprEmitter::tryEmitPrivate half");
       return {};
-    } else {
-      mlir::Type ty = cgm.convertType(destType);
-      assert(mlir::isa<cir::CIRFPTypeInterface>(ty) &&
-             "expected floating-point type");
-      return cgm.getBuilder().getAttr<cir::FPAttr>(ty, init);
     }
+
+    mlir::Type ty = cgm.convertType(destType);
+    assert(mlir::isa<cir::CIRFPTypeInterface>(ty) &&
+           "expected floating-point type");
+    return cgm.getBuilder().getAttr<cir::FPAttr>(ty, init);
   }
   case APValue::Array: {
-    cgm.errorNYI("ConstExprEmitter::tryEmitPrivate array");
-    return {};
+    const ArrayType *arrayTy = cgm.getASTContext().getAsArrayType(destType);
+    const QualType arrayElementTy = arrayTy->getElementType();
+    const unsigned numElements = value.getArraySize();
+    const unsigned numInitElts = value.getArrayInitializedElts();
+
+    mlir::Attribute filler;
+    if (value.hasArrayFiller()) {
+      filler = tryEmitPrivate(value.getArrayFiller(), arrayElementTy);
+      if (!filler)
+        return {};
+    }
+
+    SmallVector<mlir::TypedAttr, 16> elements;
+    if (filler && builder.isNullValue(filler))
+      elements.reserve(numInitElts + 1);
+    else
+      elements.reserve(numInitElts);
+
+    mlir::Type commonElementType;
+    for (unsigned i = 0; i < numInitElts; ++i) {
+      const APValue &arrayElement = value.getArrayInitializedElt(i);
+      const mlir::Attribute element =
+          tryEmitPrivateForMemory(arrayElement, arrayElementTy);
+      if (!element)
+        return {};
+
+      const mlir::TypedAttr elementTyped = mlir::cast<mlir::TypedAttr>(element);
+      if (i == 0)
+        commonElementType = elementTyped.getType();
+      else if (elementTyped.getType() != commonElementType) {
+        commonElementType = {};
+      }
+
+      elements.push_back(elementTyped);
+    }
+
+    mlir::TypedAttr typedFiller = llvm::cast_or_null<mlir::TypedAttr>(filler);
+    if (filler && !typedFiller)
+      cgm.errorNYI("array filler should always be typed");
+
+    mlir::Type desiredType = cgm.convertType(destType);
+    return emitArrayConstant(cgm, desiredType, commonElementType, numElements,
+                             elements, typedFiller);
   }
   case APValue::Vector: {
     cgm.errorNYI("ConstExprEmitter::tryEmitPrivate vector");
@@ -290,9 +381,23 @@
     cgm.errorNYI("ConstExprEmitter::tryEmitPrivate member pointer");
     return {};
   }
-  case APValue::LValue:
-    cgm.errorNYI("ConstExprEmitter::tryEmitPrivate lvalue");
+  case APValue::LValue: {
+
+    if (value.getLValueBase()) {
+      cgm.errorNYI("non-null pointer initialization");
+    } else {
+
+      mlir::Type desiredType = cgm.convertType(destType);
+      if (const cir::PointerType ptrType =
+              mlir::dyn_cast<cir::PointerType>(desiredType)) {
+        return builder.getConstPtrAttr(ptrType,
+                                       value.getLValueOffset().getQuantity());
+      } else {
+        llvm_unreachable("non-pointer variable initialized with a pointer");
+      }
+    }
     return {};
+  }
   case APValue::Struct:
   case APValue::Union:
     cgm.errorNYI("ConstExprEmitter::tryEmitPrivate struct or union");
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index 36bfc2c..9776a4e 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -11,6 +11,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "CIRGenModule.h"
+#include "CIRGenConstantEmitter.h"
 #include "CIRGenFunction.h"
 
 #include "clang/AST/ASTContext.h"
@@ -128,7 +129,8 @@
 
 void CIRGenModule::emitGlobalVarDefinition(const clang::VarDecl *vd,
                                            bool isTentative) {
-  mlir::Type type = convertType(vd->getType());
+  const QualType astTy = vd->getType();
+  const mlir::Type type = convertType(vd->getType());
   if (clang::IdentifierInfo *identifier = vd->getIdentifier()) {
     auto varOp = builder.create<cir::GlobalOp>(getLoc(vd->getSourceRange()),
                                                identifier->getName(), type);
@@ -141,38 +143,8 @@
     if (initExpr) {
       mlir::Attribute initializer;
       if (APValue *value = initDecl->evaluateValue()) {
-        switch (value->getKind()) {
-        case APValue::Int: {
-          if (mlir::isa<cir::BoolType>(type))
-            initializer =
-                builder.getCIRBoolAttr(value->getInt().getZExtValue());
-          else
-            initializer = builder.getAttr<cir::IntAttr>(type, value->getInt());
-          break;
-        }
-        case APValue::Float: {
-          initializer = builder.getAttr<cir::FPAttr>(type, value->getFloat());
-          break;
-        }
-        case APValue::LValue: {
-          if (value->getLValueBase()) {
-            errorNYI(initExpr->getSourceRange(),
-                     "non-null pointer initialization");
-          } else {
-            if (auto ptrType = mlir::dyn_cast<cir::PointerType>(type)) {
-              initializer = builder.getConstPtrAttr(
-                  ptrType, value->getLValueOffset().getQuantity());
-            } else {
-              llvm_unreachable(
-                  "non-pointer variable initialized with a pointer");
-            }
-          }
-          break;
-        }
-        default:
-          errorNYI(initExpr->getSourceRange(), "unsupported initializer kind");
-          break;
-        }
+        ConstantEmitter emitter(*this);
+        initializer = emitter.tryEmitPrivateForMemory(*value, astTy);
       } else {
         errorNYI(initExpr->getSourceRange(), "non-constant initializer");
       }
diff --git a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp
index 8e8f7d5..a8d9f6a 100644
--- a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp
@@ -191,6 +191,115 @@
 }
 
 //===----------------------------------------------------------------------===//
+// CIR ConstArrayAttr
+//===----------------------------------------------------------------------===//
+
+LogicalResult
+ConstArrayAttr::verify(function_ref<::mlir::InFlightDiagnostic()> emitError,
+                       Type type, Attribute elts, int trailingZerosNum) {
+
+  if (!(mlir::isa<ArrayAttr>(elts) || mlir::isa<StringAttr>(elts)))
+    return emitError() << "constant array expects ArrayAttr or StringAttr";
+
+  if (auto strAttr = mlir::dyn_cast<StringAttr>(elts)) {
+    const auto arrayTy = mlir::cast<ArrayType>(type);
+    const auto intTy = mlir::dyn_cast<IntType>(arrayTy.getEltType());
+
+    // TODO: add CIR type for char.
+    if (!intTy || intTy.getWidth() != 8) {
+      emitError() << "constant array element for string literals expects "
+                     "!cir.int<u, 8> element type";
+      return failure();
+    }
+    return success();
+  }
+
+  assert(mlir::isa<ArrayAttr>(elts));
+  const auto arrayAttr = mlir::cast<mlir::ArrayAttr>(elts);
+  const auto arrayTy = mlir::cast<ArrayType>(type);
+
+  // Make sure both number of elements and subelement types match type.
+  if (arrayTy.getSize() != arrayAttr.size() + trailingZerosNum)
+    return emitError() << "constant array size should match type size";
+  return success();
+}
+
+Attribute ConstArrayAttr::parse(AsmParser &parser, Type type) {
+  mlir::FailureOr<Type> resultTy;
+  mlir::FailureOr<Attribute> resultVal;
+
+  // Parse literal '<'
+  if (parser.parseLess())
+    return {};
+
+  // Parse variable 'value'
+  resultVal = FieldParser<Attribute>::parse(parser);
+  if (failed(resultVal)) {
+    parser.emitError(
+        parser.getCurrentLocation(),
+        "failed to parse ConstArrayAttr parameter 'value' which is "
+        "to be a `Attribute`");
+    return {};
+  }
+
+  // ArrayAttrrs have per-element type, not the type of the array...
+  if (mlir::isa<ArrayAttr>(*resultVal)) {
+    // Array has implicit type: infer from const array type.
+    if (parser.parseOptionalColon().failed()) {
+      resultTy = type;
+    } else { // Array has explicit type: parse it.
+      resultTy = FieldParser<Type>::parse(parser);
+      if (failed(resultTy)) {
+        parser.emitError(
+            parser.getCurrentLocation(),
+            "failed to parse ConstArrayAttr parameter 'type' which is "
+            "to be a `::mlir::Type`");
+        return {};
+      }
+    }
+  } else {
+    auto ta = mlir::cast<TypedAttr>(*resultVal);
+    resultTy = ta.getType();
+    if (mlir::isa<mlir::NoneType>(*resultTy)) {
+      parser.emitError(parser.getCurrentLocation(),
+                       "expected type declaration for string literal");
+      return {};
+    }
+  }
+
+  unsigned zeros = 0;
+  if (parser.parseOptionalComma().succeeded()) {
+    if (parser.parseOptionalKeyword("trailing_zeros").succeeded()) {
+      unsigned typeSize =
+          mlir::cast<cir::ArrayType>(resultTy.value()).getSize();
+      mlir::Attribute elts = resultVal.value();
+      if (auto str = mlir::dyn_cast<mlir::StringAttr>(elts))
+        zeros = typeSize - str.size();
+      else
+        zeros = typeSize - mlir::cast<mlir::ArrayAttr>(elts).size();
+    } else {
+      return {};
+    }
+  }
+
+  // Parse literal '>'
+  if (parser.parseGreater())
+    return {};
+
+  return parser.getChecked<ConstArrayAttr>(
+      parser.getCurrentLocation(), parser.getContext(), resultTy.value(),
+      resultVal.value(), zeros);
+}
+
+void ConstArrayAttr::print(AsmPrinter &printer) const {
+  printer << "<";
+  printer.printStrippedAttrOrType(getElts());
+  if (getTrailingZerosNum())
+    printer << ", trailing_zeros";
+  printer << ">";
+}
+
+//===----------------------------------------------------------------------===//
 // CIR Dialect
 //===----------------------------------------------------------------------===//
 
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 56247e2..e3872bf 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -149,6 +149,12 @@
     return success();
   }
 
+  if (isa<cir::ZeroAttr>(attrType)) {
+    if (::mlir::isa<cir::ArrayType>(opType))
+      return success();
+    return op->emitOpError("zero expects struct or array type");
+  }
+
   if (mlir::isa<cir::BoolAttr>(attrType)) {
     if (!mlir::isa<cir::BoolType>(opType))
       return op->emitOpError("result type (")
@@ -166,6 +172,9 @@
     return success();
   }
 
+  if (mlir::isa<cir::ConstArrayAttr>(attrType))
+    return success();
+
   assert(isa<TypedAttr>(attrType) && "What else could we be looking at here?");
   return op->emitOpError("global with type ")
          << cast<TypedAttr>(attrType).getType() << " not yet supported";
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index db94cf5..83226ed 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -128,6 +128,21 @@
   return value;
 }
 
+static mlir::Value
+emitCirAttrToMemory(mlir::Operation *parentOp, mlir::Attribute attr,
+                    mlir::ConversionPatternRewriter &rewriter,
+                    const mlir::TypeConverter *converter,
+                    mlir::DataLayout const &dataLayout) {
+
+  mlir::Value loweredValue =
+      lowerCirAttrAsValue(parentOp, attr, rewriter, converter);
+  if (auto boolAttr = mlir::dyn_cast<cir::BoolAttr>(attr)) {
+    return emitToMemory(rewriter, dataLayout, boolAttr.getType(), loweredValue);
+  }
+
+  return loweredValue;
+}
+
 mlir::LLVM::Linkage convertLinkage(cir::GlobalLinkageKind linkage) {
   using CIR = cir::GlobalLinkageKind;
   using LLVM = mlir::LLVM::Linkage;
@@ -184,14 +199,16 @@
 
   mlir::Value visit(mlir::Attribute attr) {
     return llvm::TypeSwitch<mlir::Attribute, mlir::Value>(attr)
-        .Case<cir::IntAttr, cir::FPAttr, cir::ConstPtrAttr>(
-            [&](auto attrT) { return visitCirAttr(attrT); })
+        .Case<cir::IntAttr, cir::FPAttr, cir::ConstArrayAttr, cir::ConstPtrAttr,
+              cir::ZeroAttr>([&](auto attrT) { return visitCirAttr(attrT); })
         .Default([&](auto attrT) { return mlir::Value(); });
   }
 
   mlir::Value visitCirAttr(cir::IntAttr intAttr);
   mlir::Value visitCirAttr(cir::FPAttr fltAttr);
   mlir::Value visitCirAttr(cir::ConstPtrAttr ptrAttr);
+  mlir::Value visitCirAttr(cir::ConstArrayAttr attr);
+  mlir::Value visitCirAttr(cir::ZeroAttr attr);
 
 private:
   mlir::Operation *parentOp;
@@ -199,6 +216,18 @@
   const mlir::TypeConverter *converter;
 };
 
+/// Switches on the type of attribute and calls the appropriate conversion.
+mlir::Value lowerCirAttrAsValue(mlir::Operation *parentOp,
+                                const mlir::Attribute attr,
+                                mlir::ConversionPatternRewriter &rewriter,
+                                const mlir::TypeConverter *converter) {
+  CIRAttrToValue valueConverter(parentOp, rewriter, converter);
+  mlir::Value value = valueConverter.visit(attr);
+  if (!value)
+    llvm_unreachable("unhandled attribute type");
+  return value;
+}
+
 /// IntAttr visitor.
 mlir::Value CIRAttrToValue::visitCirAttr(cir::IntAttr intAttr) {
   mlir::Location loc = parentOp->getLoc();
@@ -228,6 +257,42 @@
       loc, converter->convertType(fltAttr.getType()), fltAttr.getValue());
 }
 
+// ConstArrayAttr visitor
+mlir::Value CIRAttrToValue::visitCirAttr(cir::ConstArrayAttr attr) {
+  mlir::Type llvmTy = converter->convertType(attr.getType());
+  mlir::Location loc = parentOp->getLoc();
+  mlir::Value result;
+
+  if (auto zeros = attr.getTrailingZerosNum()) {
+    mlir::Type arrayTy = attr.getType();
+    result = rewriter.create<mlir::LLVM::ZeroOp>(
+        loc, converter->convertType(arrayTy));
+  } else {
+    result = rewriter.create<mlir::LLVM::UndefOp>(loc, llvmTy);
+  }
+
+  // Iteratively lower each constant element of the array.
+  if (auto arrayAttr = mlir::dyn_cast<mlir::ArrayAttr>(attr.getElts())) {
+    for (auto [idx, elt] : llvm::enumerate(arrayAttr)) {
+      mlir::DataLayout dataLayout(parentOp->getParentOfType<mlir::ModuleOp>());
+      mlir::Value init = visit(elt);
+      result =
+          rewriter.create<mlir::LLVM::InsertValueOp>(loc, result, init, idx);
+    }
+  } else {
+    llvm_unreachable("unexpected ConstArrayAttr elements");
+  }
+
+  return result;
+}
+
+/// ZeroAttr visitor.
+mlir::Value CIRAttrToValue::visitCirAttr(cir::ZeroAttr attr) {
+  auto loc = parentOp->getLoc();
+  return rewriter.create<mlir::LLVM::ZeroOp>(
+      loc, converter->convertType(attr.getType()));
+}
+
 // This class handles rewriting initializer attributes for types that do not
 // require region initialization.
 class GlobalInitAttrRewriter {
@@ -705,7 +770,7 @@
     cir::GlobalOp op, mlir::Attribute init,
     mlir::ConversionPatternRewriter &rewriter) const {
   // TODO: Generalize this handling when more types are needed here.
-  assert(isa<cir::ConstPtrAttr>(init));
+  assert((isa<cir::ConstArrayAttr, cir::ConstPtrAttr, cir::ZeroAttr>(init)));
 
   // TODO(cir): once LLVM's dialect has proper equivalent attributes this
   // should be updated. For now, we use a custom op to initialize globals
@@ -758,7 +823,8 @@
         op.emitError() << "unsupported initializer '" << init.value() << "'";
         return mlir::failure();
       }
-    } else if (mlir::isa<cir::ConstPtrAttr>(init.value())) {
+    } else if (mlir::isa<cir::ConstArrayAttr, cir::ConstPtrAttr, 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/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
index b96e161..d49ab81 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
@@ -20,6 +20,12 @@
 
 namespace direct {
 
+/// Convert a CIR attribute to an LLVM attribute. May use the datalayout for
+/// lowering attributes to-be-stored in memory.
+mlir::Value lowerCirAttrAsValue(mlir::Operation *parentOp, mlir::Attribute attr,
+                                mlir::ConversionPatternRewriter &rewriter,
+                                const mlir::TypeConverter *converter);
+
 mlir::LLVM::Linkage convertLinkage(cir::GlobalLinkageKind linkage);
 
 class CIRToLLVMCastOpLowering : public mlir::OpConversionPattern<cir::CastOp> {