[clang] [CIR] Add support for array constructors (PR #149142)
Morris Hafner via cfe-commits
cfe-commits at lists.llvm.org
Thu Jul 17 08:35:20 PDT 2025
https://github.com/mmha updated https://github.com/llvm/llvm-project/pull/149142
>From 2a78522d151c3a970fccef94724a31fe14547f9d Mon Sep 17 00:00:00 2001
From: Morris Hafner <mhafner at nvidia.com>
Date: Wed, 16 Jul 2025 18:48:52 +0200
Subject: [PATCH 1/4] [CIR] Add support for array constructors
This patch upstreams support for creating arrays of classes that require calling a constructor.
* Adds the ArrayCtor operation
* New lowering pass for lowering ArrayCtor to a loop
---
clang/include/clang/CIR/Dialect/IR/CIROps.td | 38 +++++-
.../CIR/Dialect/IR/CIRTypeConstraints.td | 8 ++
clang/lib/CIR/CodeGen/CIRGenClass.cpp | 110 ++++++++++++++++++
clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 59 +++++-----
clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 45 +++++++
clang/lib/CIR/CodeGen/CIRGenFunction.h | 12 ++
.../Dialect/Transforms/LoweringPrepare.cpp | 86 +++++++++++++-
clang/test/CIR/CodeGen/array-ctor.cpp | 70 +++++++++++
clang/test/CIR/IR/array-ctor.cir | 29 +++++
9 files changed, 426 insertions(+), 31 deletions(-)
create mode 100644 clang/test/CIR/CodeGen/array-ctor.cpp
create mode 100644 clang/test/CIR/IR/array-ctor.cir
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index d19cd83d78b40..4c30a54bc650f 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -607,7 +607,7 @@ def CIR_ConditionOp : CIR_Op<"condition", [
//===----------------------------------------------------------------------===//
defvar CIR_YieldableScopes = [
- "CaseOp", "DoWhileOp", "ForOp", "IfOp", "ScopeOp", "SwitchOp",
+ "ArrayCtor", "CaseOp", "DoWhileOp", "ForOp", "IfOp", "ScopeOp", "SwitchOp",
"TernaryOp", "WhileOp"
];
@@ -2219,6 +2219,42 @@ def CIR_TrapOp : CIR_Op<"trap", [Terminator]> {
let assemblyFormat = "attr-dict";
}
+//===----------------------------------------------------------------------===//
+// ArrayCtor
+//===----------------------------------------------------------------------===//
+
+class CIR_ArrayInitDestroy<string mnemonic> : CIR_Op<mnemonic> {
+ let arguments = (ins
+ Arg<CIR_PtrToArray, "array address", [MemWrite, MemRead]>:$addr
+ );
+
+ let regions = (region SizedRegion<1>:$body);
+ let assemblyFormat = [{
+ `(` $addr `:` qualified(type($addr)) `)` $body attr-dict
+ }];
+
+ let builders = [
+ OpBuilder<(ins "mlir::Value":$addr,
+ "llvm::function_ref<void(mlir::OpBuilder &, mlir::Location)>":$regionBuilder), [{
+ assert(regionBuilder && "builder callback expected");
+ mlir::OpBuilder::InsertionGuard guard($_builder);
+ mlir::Region *r = $_state.addRegion();
+ $_state.addOperands(ValueRange{addr});
+ $_builder.createBlock(r);
+ regionBuilder($_builder, $_state.location);
+ }]>
+ ];
+}
+
+def CIR_ArrayCtor : CIR_ArrayInitDestroy<"array.ctor"> {
+ let summary = "Initialize array elements with C++ constructors";
+ let description = [{
+ Initialize each array element using the same C++ constructor. This
+ operation has one region, with one single block. The block has an
+ incoming argument for the current array index to initialize.
+ }];
+}
+
//===----------------------------------------------------------------------===//
// VecCreate
//===----------------------------------------------------------------------===//
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRTypeConstraints.td b/clang/include/clang/CIR/Dialect/IR/CIRTypeConstraints.td
index 2bf77583465a6..d7d55dfbc0654 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRTypeConstraints.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRTypeConstraints.td
@@ -165,6 +165,12 @@ def CIR_AnyIntOrFloatType : AnyTypeOf<[CIR_AnyFloatType, CIR_AnyIntType],
def CIR_AnyComplexType : CIR_TypeBase<"::cir::ComplexType", "complex type">;
+//===----------------------------------------------------------------------===//
+// Array Type predicates
+//===----------------------------------------------------------------------===//
+
+def CIR_AnyArrayType : CIR_TypeBase<"::cir::ArrayType", "array type">;
+
//===----------------------------------------------------------------------===//
// Pointer Type predicates
//===----------------------------------------------------------------------===//
@@ -216,6 +222,8 @@ def CIR_PtrToIntOrFloatType : CIR_PtrToType<CIR_AnyIntOrFloatType>;
def CIR_PtrToComplexType : CIR_PtrToType<CIR_AnyComplexType>;
+def CIR_PtrToArray : CIR_PtrToType<CIR_AnyArrayType>;
+
//===----------------------------------------------------------------------===//
// Vector Type predicates
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
index 8667bb60d114e..26ace1c366780 100644
--- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
@@ -12,6 +12,7 @@
#include "CIRGenCXXABI.h"
#include "CIRGenFunction.h"
+#include "CIRGenValue.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/RecordLayout.h"
@@ -311,6 +312,115 @@ void CIRGenFunction::emitInitializerForField(FieldDecl *field, LValue lhs,
assert(!cir::MissingFeatures::requiresCleanups());
}
+/// Emit a loop to call a particular constructor for each of several members
+/// of an array.
+///
+/// \param ctor the constructor to call for each element
+/// \param arrayType the type of the array to initialize
+/// \param arrayBegin an arrayType*
+/// \param zeroInitialize true if each element should be
+/// zero-initialized before it is constructed
+void CIRGenFunction::emitCXXAggrConstructorCall(
+ const CXXConstructorDecl *ctor, const clang::ArrayType *arrayType,
+ Address arrayBegin, const CXXConstructExpr *e, bool newPointerIsChecked,
+ bool zeroInitialize) {
+ QualType elementType;
+ mlir::Value numElements = emitArrayLength(arrayType, elementType, arrayBegin);
+ emitCXXAggrConstructorCall(ctor, numElements, arrayBegin, e,
+ newPointerIsChecked, zeroInitialize);
+}
+
+/// Emit a loop to call a particular constructor for each of several members
+/// of an array.
+///
+/// \param ctor the constructor to call for each element
+/// \param numElements the number of elements in the array;
+/// may be zero
+/// \param arrayBase a T*, where T is the type constructed by ctor
+/// \param zeroInitialize true if each element should be
+/// zero-initialized before it is constructed
+void CIRGenFunction::emitCXXAggrConstructorCall(
+ const CXXConstructorDecl *ctor, mlir::Value numElements, Address arrayBase,
+ const CXXConstructExpr *e, bool newPointerIsChecked, bool zeroInitialize) {
+ // It's legal for numElements to be zero. This can happen both
+ // dynamically, because x can be zero in 'new A[x]', and statically,
+ // because of GCC extensions that permit zero-length arrays. There
+ // are probably legitimate places where we could assume that this
+ // doesn't happen, but it's not clear that it's worth it.
+
+ // Optimize for a constant count.
+ auto constantCount = dyn_cast<cir::ConstantOp>(numElements.getDefiningOp());
+ if (constantCount) {
+ auto constIntAttr = mlir::dyn_cast<cir::IntAttr>(constantCount.getValue());
+ // Just skip out if the constant count is zero.
+ if (constIntAttr && constIntAttr.getUInt() == 0)
+ return;
+ // Otherwise, emit the check.
+ } else {
+ cgm.errorNYI(e->getSourceRange(), "dynamic-length array expression");
+ }
+
+ auto arrayTy = mlir::dyn_cast<cir::ArrayType>(arrayBase.getElementType());
+ assert(arrayTy && "expected array type");
+ mlir::Type elementType = arrayTy.getElementType();
+ cir::PointerType ptrToElmType = builder.getPointerTo(elementType);
+
+ // Tradional LLVM codegen emits a loop here. CIR lowers to a loop as part of
+ // LoweringPrepare.
+
+ // The alignment of the base, adjusted by the size of a single element,
+ // provides a conservative estimate of the alignment of every element.
+ // (This assumes we never start tracking offsetted alignments.)
+ //
+ // Note that these are complete objects and so we don't need to
+ // use the non-virtual size or alignment.
+ QualType type = getContext().getTypeDeclType(ctor->getParent());
+ CharUnits eltAlignment = arrayBase.getAlignment().alignmentOfArrayElement(
+ getContext().getTypeSizeInChars(type));
+
+ // Zero initialize the storage, if requested.
+ if (zeroInitialize)
+ emitNullInitialization(*currSrcLoc, arrayBase, type);
+
+ // C++ [class.temporary]p4:
+ // There are two contexts in which temporaries are destroyed at a different
+ // point than the end of the full-expression. The first context is when a
+ // default constructor is called to initialize an element of an array.
+ // If the constructor has one or more default arguments, the destruction of
+ // every temporary created in a default argument expression is sequenced
+ // before the construction of the next array element, if any.
+ {
+ assert(!cir::MissingFeatures::runCleanupsScope());
+
+ // Evaluate the constructor and its arguments in a regular
+ // partial-destroy cleanup.
+ if (getLangOpts().Exceptions &&
+ !ctor->getParent()->hasTrivialDestructor()) {
+ cgm.errorNYI(e->getSourceRange(), "partial array cleanups");
+ }
+
+ // Emit the constructor call that will execute for every array element.
+ auto arrayOp = builder.createPtrBitcast(arrayBase.getPointer(), arrayTy);
+ builder.create<cir::ArrayCtor>(
+ *currSrcLoc, arrayOp, [&](mlir::OpBuilder &b, mlir::Location loc) {
+ auto arg = b.getInsertionBlock()->addArgument(ptrToElmType, loc);
+ Address curAddr = Address(arg, elementType, eltAlignment);
+ assert(!cir::MissingFeatures::sanitizers());
+ auto currAVS = AggValueSlot::forAddr(
+ curAddr, type.getQualifiers(), AggValueSlot::IsDestructed,
+ AggValueSlot::IsNotAliased, AggValueSlot::DoesNotOverlap,
+ AggValueSlot::IsNotZeroed);
+ emitCXXConstructorCall(ctor, Ctor_Complete,
+ /*ForVirtualBase=*/false,
+ /*Delegating=*/false, currAVS, e);
+ builder.create<cir::YieldOp>(loc);
+ });
+ }
+
+ if (constantCount.use_empty())
+ constantCount.erase();
+}
+
void CIRGenFunction::emitDelegateCXXConstructorCall(
const CXXConstructorDecl *ctor, CXXCtorType ctorType,
const FunctionArgList &args, SourceLocation loc) {
diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index 51da48d330f55..9f4d883066055 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -1594,37 +1594,38 @@ void CIRGenFunction::emitCXXConstructExpr(const CXXConstructExpr *e,
return;
}
- if (getContext().getAsArrayType(e->getType())) {
- cgm.errorNYI(e->getSourceRange(), "emitCXXConstructExpr: array type");
- return;
- }
+ if (const ArrayType *arrayType = getContext().getAsArrayType(e->getType())) {
+ assert(!cir::MissingFeatures::sanitizers());
+ emitCXXAggrConstructorCall(cd, arrayType, dest.getAddress(), e, false);
+ } else {
- clang::CXXCtorType type = Ctor_Complete;
- bool forVirtualBase = false;
- bool delegating = false;
-
- switch (e->getConstructionKind()) {
- case CXXConstructionKind::Complete:
- type = Ctor_Complete;
- break;
- case CXXConstructionKind::Delegating:
- // We should be emitting a constructor; GlobalDecl will assert this
- type = curGD.getCtorType();
- delegating = true;
- break;
- case CXXConstructionKind::VirtualBase:
- // This should just set 'forVirtualBase' to true and fall through, but
- // virtual base class support is otherwise missing, so this needs to wait
- // until it can be tested.
- cgm.errorNYI(e->getSourceRange(),
- "emitCXXConstructExpr: virtual base constructor");
- return;
- case CXXConstructionKind::NonVirtualBase:
- type = Ctor_Base;
- break;
- }
+ clang::CXXCtorType type = Ctor_Complete;
+ bool forVirtualBase = false;
+ bool delegating = false;
- emitCXXConstructorCall(cd, type, forVirtualBase, delegating, dest, e);
+ switch (e->getConstructionKind()) {
+ case CXXConstructionKind::Complete:
+ type = Ctor_Complete;
+ break;
+ case CXXConstructionKind::Delegating:
+ // We should be emitting a constructor; GlobalDecl will assert this
+ type = curGD.getCtorType();
+ delegating = true;
+ break;
+ case CXXConstructionKind::VirtualBase:
+ // This should just set 'forVirtualBase' to true and fall through, but
+ // virtual base class support is otherwise missing, so this needs to wait
+ // until it can be tested.
+ cgm.errorNYI(e->getSourceRange(),
+ "emitCXXConstructExpr: virtual base constructor");
+ return;
+ case CXXConstructionKind::NonVirtualBase:
+ type = Ctor_Base;
+ break;
+ }
+
+ emitCXXConstructorCall(cd, type, forVirtualBase, delegating, dest, e);
+ }
}
RValue CIRGenFunction::emitReferenceBindingToExpr(const Expr *e) {
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index e532b9d855843..bdc1df41c26e8 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -805,4 +805,49 @@ bool CIRGenFunction::shouldNullCheckClassCastValue(const CastExpr *ce) {
return true;
}
+/// Computes the length of an array in elements, as well as the base
+/// element type and a properly-typed first element pointer.
+mlir::Value
+CIRGenFunction::emitArrayLength(const clang::ArrayType *origArrayType,
+ QualType &baseType, Address &addr) {
+ const clang::ArrayType *arrayType = origArrayType;
+
+ // If it's a VLA, we have to load the stored size. Note that
+ // this is the size of the VLA in bytes, not its size in elements.
+ if (isa<VariableArrayType>(arrayType)) {
+ cgm.errorNYI(*currSrcLoc, "VLAs");
+ return builder.getConstInt(*currSrcLoc, SizeTy, 0);
+ }
+
+ uint64_t countFromCLAs = 1;
+ QualType eltType;
+
+ auto cirArrayType = mlir::dyn_cast<cir::ArrayType>(addr.getElementType());
+
+ while (cirArrayType) {
+ assert(isa<ConstantArrayType>(arrayType));
+ countFromCLAs *= cirArrayType.getSize();
+ eltType = arrayType->getElementType();
+
+ cirArrayType =
+ mlir::dyn_cast<cir::ArrayType>(cirArrayType.getElementType());
+
+ arrayType = getContext().getAsArrayType(arrayType->getElementType());
+ assert((!cirArrayType || arrayType) &&
+ "CIR and Clang types are out-of-sync");
+ }
+
+ if (arrayType) {
+ // From this point onwards, the Clang array type has been emitted
+ // as some other type (probably a packed struct). Compute the array
+ // size, and just emit the 'begin' expression as a bitcast.
+ cgm.errorNYI(*currSrcLoc, "length for non-array underlying types");
+ }
+
+ baseType = eltType;
+ auto numElements = builder.getConstInt(*currSrcLoc, SizeTy, countFromCLAs);
+
+ return numElements;
+}
+
} // namespace clang::CIRGen
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 3baabba5adfe1..b4f79338ae81e 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -761,6 +761,8 @@ class CIRGenFunction : public CIRGenTypeCache {
/// even if no aggregate location is provided.
RValue emitAnyExprToTemp(const clang::Expr *e);
+ mlir::Value emitArrayLength(const clang::ArrayType *arrayType,
+ QualType &baseType, Address &addr);
LValue emitArraySubscriptExpr(const clang::ArraySubscriptExpr *e);
Address emitArrayToPointerDecay(const Expr *array);
@@ -837,6 +839,16 @@ class CIRGenFunction : public CIRGenTypeCache {
void emitCXXConstructExpr(const clang::CXXConstructExpr *e,
AggValueSlot dest);
+ void emitCXXAggrConstructorCall(const CXXConstructorDecl *ctor,
+ const clang::ArrayType *arrayType,
+ Address arrayBegin, const CXXConstructExpr *e,
+ bool newPointerIsChecked,
+ bool zeroInitialize = false);
+ void emitCXXAggrConstructorCall(const CXXConstructorDecl *ctor,
+ mlir::Value numElements, Address arrayBase,
+ const CXXConstructExpr *e,
+ bool newPointerIsChecked,
+ bool zeroInitialize);
void emitCXXConstructorCall(const clang::CXXConstructorDecl *d,
clang::CXXCtorType type, bool forVirtualBase,
bool delegating, AggValueSlot thisAVS,
diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
index 5493b86a0a321..d32d47761b485 100644
--- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
@@ -8,6 +8,8 @@
#include "PassDetail.h"
#include "clang/AST/ASTContext.h"
+#include "clang/AST/CharUnits.h"
+#include "clang/CIR/Dialect/Builder/CIRBaseBuilder.h"
#include "clang/CIR/Dialect/IR/CIRDialect.h"
#include "clang/CIR/Dialect/Passes.h"
@@ -22,15 +24,97 @@ struct LoweringPreparePass : public LoweringPrepareBase<LoweringPreparePass> {
void runOnOperation() override;
void runOnOp(Operation *op);
+ void lowerArrayCtor(ArrayCtor op);
};
} // namespace
-void LoweringPreparePass::runOnOp(Operation *op) {}
+void LoweringPreparePass::runOnOp(Operation *op) {
+ if (auto arrayCtor = dyn_cast<ArrayCtor>(op)) {
+ lowerArrayCtor(arrayCtor);
+ }
+}
+
+static void lowerArrayDtorCtorIntoLoop(CIRBaseBuilderTy &builder,
+ mlir::Operation *op, mlir::Type eltTy,
+ mlir::Value arrayAddr,
+ uint64_t arrayLen) {
+ // Generate loop to call into ctor/dtor for every element.
+ Location loc = op->getLoc();
+
+ // TODO: instead of fixed integer size, create alias for PtrDiffTy and unify
+ // with CIRGen stuff.
+ auto ptrDiffTy =
+ cir::IntType::get(builder.getContext(), 64, /*isSigned=*/false);
+ auto numArrayElementsConst = builder.create<cir::ConstantOp>(
+ loc, ptrDiffTy, cir::IntAttr::get(ptrDiffTy, arrayLen));
+
+ auto begin = builder.create<cir::CastOp>(
+ loc, eltTy, cir::CastKind::array_to_ptrdecay, arrayAddr);
+ mlir::Value end = builder.create<cir::PtrStrideOp>(loc, eltTy, begin,
+ numArrayElementsConst);
+
+ mlir::Value tmpAddr = builder.createAlloca(
+ loc, /*addr type*/ builder.getPointerTo(eltTy),
+ /*var type*/ eltTy, "__array_idx", builder.getAlignmentAttr(1));
+ builder.createStore(loc, begin, tmpAddr);
+
+ cir::DoWhileOp loop = builder.createDoWhile(
+ loc,
+ /*condBuilder=*/
+ [&](mlir::OpBuilder &b, mlir::Location loc) {
+ auto currentElement = b.create<cir::LoadOp>(loc, eltTy, tmpAddr);
+ mlir::Type boolTy = cir::BoolType::get(b.getContext());
+ auto cmp = builder.create<cir::CmpOp>(loc, boolTy, cir::CmpOpKind::eq,
+ currentElement, end);
+ builder.createCondition(cmp);
+ },
+ /*bodyBuilder=*/
+ [&](mlir::OpBuilder &b, mlir::Location loc) {
+ auto currentElement = b.create<cir::LoadOp>(loc, eltTy, tmpAddr);
+
+ CallOp ctorCall;
+ op->walk([&](CallOp c) { ctorCall = c; });
+ assert(ctorCall && "expected ctor call");
+
+ auto one = builder.create<cir::ConstantOp>(
+ loc, ptrDiffTy, cir::IntAttr::get(ptrDiffTy, 1));
+
+ ctorCall->moveAfter(one);
+ ctorCall->setOperand(0, currentElement);
+
+ // Advance pointer and store them to temporary variable
+ auto nextElement =
+ builder.create<cir::PtrStrideOp>(loc, eltTy, currentElement, one);
+ builder.createStore(loc, nextElement, tmpAddr);
+ builder.createYield(loc);
+ });
+
+ op->replaceAllUsesWith(loop);
+ op->erase();
+}
+
+void LoweringPreparePass::lowerArrayCtor(ArrayCtor op) {
+ CIRBaseBuilderTy builder(getContext());
+ builder.setInsertionPointAfter(op.getOperation());
+
+ Type eltTy = op->getRegion(0).getArgument(0).getType();
+ auto arrayLen =
+ mlir::cast<cir::ArrayType>(op.getAddr().getType().getPointee()).getSize();
+ lowerArrayDtorCtorIntoLoop(builder, op, eltTy, op.getAddr(), arrayLen);
+}
void LoweringPreparePass::runOnOperation() {
+ Operation *op = getOperation();
+
llvm::SmallVector<Operation *> opsToTransform;
+ op->walk([&](Operation *op) {
+ if (isa<ArrayCtor>(op)) {
+ opsToTransform.push_back(op);
+ }
+ });
+
for (auto *o : opsToTransform)
runOnOp(o);
}
diff --git a/clang/test/CIR/CodeGen/array-ctor.cpp b/clang/test/CIR/CodeGen/array-ctor.cpp
new file mode 100644
index 0000000000000..0a2661248f908
--- /dev/null
+++ b/clang/test/CIR/CodeGen/array-ctor.cpp
@@ -0,0 +1,70 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value -fclangir -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG
+
+struct S {
+ S();
+};
+
+void foo() {
+ S s[42];
+}
+
+// CIR: cir.func dso_local @_Z3foov()
+// CIR: %[[ARRAY:.*]] = cir.alloca !cir.array<!rec_S x 42>, !cir.ptr<!cir.array<!rec_S x 42>>, ["s", init]
+// CIR: %[[CONST42:.*]] = cir.const #cir.int<42> : !u64i
+// CIR: %[[DECAY:.*]] = cir.cast(array_to_ptrdecay, %[[ARRAY]] : !cir.ptr<!cir.array<!rec_S x 42>>), !cir.ptr<!rec_S>
+// CIR: %[[END_PTR:.*]] = cir.ptr_stride(%[[DECAY]] : !cir.ptr<!rec_S>, %[[CONST42]] : !u64i), !cir.ptr<!rec_S>
+// CIR: %[[ITER:.*]] = cir.alloca !cir.ptr<!rec_S>, !cir.ptr<!cir.ptr<!rec_S>>, ["__array_idx"]
+// CIR: cir.store %[[DECAY]], %[[ITER]] : !cir.ptr<!rec_S>, !cir.ptr<!cir.ptr<!rec_S>>
+// CIR: cir.do {
+// CIR: %[[CURRENT:.*]] = cir.load %[[ITER]] : !cir.ptr<!cir.ptr<!rec_S>>, !cir.ptr<!rec_S>
+// CIR: %[[CONST1:.*]] = cir.const #cir.int<1> : !u64i
+// CIR: cir.call @_ZN1SC1Ev(%[[CURRENT]]) : (!cir.ptr<!rec_S>) -> ()
+// CIR: %[[NEXT:.*]] = cir.ptr_stride(%[[CURRENT]] : !cir.ptr<!rec_S>, %[[CONST1]] : !u64i), !cir.ptr<!rec_S>
+// CIR: cir.store %[[NEXT]], %[[ITER]] : !cir.ptr<!rec_S>, !cir.ptr<!cir.ptr<!rec_S>>
+// CIR: cir.yield
+// CIR: } while {
+// CIR: %[[CURRENT2:.*]] = cir.load %[[ITER]] : !cir.ptr<!cir.ptr<!rec_S>>, !cir.ptr<!rec_S>
+// CIR: %[[CMP:.*]] = cir.cmp(eq, %[[CURRENT2]], %[[END_PTR]]) : !cir.ptr<!rec_S>, !cir.bool
+// CIR: cir.condition(%[[CMP]])
+// CIR: }
+// CIR: cir.return
+// CIR: }
+
+// LLVM: define dso_local void @_Z3foov()
+// LLVM: %[[ARRAY:.*]] = alloca [42 x %struct.S]
+// LLVM: %[[START:.*]] = getelementptr %struct.S, ptr %[[ARRAY]], i32 0
+// LLVM: %[[END:.*]] = getelementptr %struct.S, ptr %[[START]], i64 42
+// LLVM: %[[ITER:.*]] = alloca ptr
+// LLVM: store ptr %[[START]], ptr %[[ITER]]
+// LLVM: br label %[[LOOP:.*]]
+// LLVM: [[COND:.*]]:
+// LLVM: %[[CURRENT_CHECK:.*]] = load ptr, ptr %[[ITER]]
+// LLVM: %[[DONE:.*]] = icmp eq ptr %[[CURRENT_CHECK]], %[[END]]
+// LLVM: br i1 %[[DONE]], label %[[LOOP]], label %[[EXIT:.*]]
+// LLVM: [[LOOP]]:
+// LLVM: %[[CURRENT:.*]] = load ptr, ptr %[[ITER]]
+// LLVM: call void @_ZN1SC1Ev(ptr %[[CURRENT]])
+// LLVM: %[[NEXT:.*]] = getelementptr %struct.S, ptr %[[CURRENT]], i64 1
+// LLVM: store ptr %[[NEXT]], ptr %[[ITER]]
+// LLVM: br label %[[COND]]
+// LLVM: [[EXIT]]:
+// LLVM: ret void
+
+// OGCG: define dso_local void @_Z3foov()
+// OGCG: %[[ARRAY:.*]] = alloca [42 x %struct.S]
+// OGCG: %[[START:.*]] = getelementptr{{.*}} %struct.S{{.*}}
+// OGCG: %[[END:.*]] = getelementptr{{.*}} %struct.S{{.*}} i64 42
+// OGCG: br label %[[LOOP:.*]]
+// OGCG: [[LOOP]]:
+// OGCG: %[[CURRENT:.*]] = phi ptr [ %[[START]], %{{.*}} ], [ %[[NEXT:.*]], %[[LOOP]] ]
+// OGCG: call void @_ZN1SC1Ev(ptr{{.*}})
+// OGCG: %[[NEXT]] = getelementptr{{.*}} %struct.S{{.*}} i64 1
+// OGCG: %[[DONE:.*]] = icmp eq ptr %[[NEXT]], %[[END]]
+// OGCG: br i1 %[[DONE]], label %[[EXIT:.*]], label %[[LOOP]]
+// OGCG: [[EXIT]]:
+// OGCG: ret void
diff --git a/clang/test/CIR/IR/array-ctor.cir b/clang/test/CIR/IR/array-ctor.cir
new file mode 100644
index 0000000000000..a039d7d2ca6b0
--- /dev/null
+++ b/clang/test/CIR/IR/array-ctor.cir
@@ -0,0 +1,29 @@
+
+// RUN: cir-opt %s | FileCheck %s
+
+!u8i = !cir.int<u, 8>
+!rec_S = !cir.record<struct "S" padded {!u8i}>
+
+module {
+ cir.func private @_ZN1SC1Ev(!cir.ptr<!rec_S>)
+ cir.func dso_local @_Z3foov() {
+ %0 = cir.alloca !cir.array<!rec_S x 42>, !cir.ptr<!cir.array<!rec_S x 42>>, ["s", init] {alignment = 16 : i64}
+ cir.array.ctor(%0 : !cir.ptr<!cir.array<!rec_S x 42>>) {
+ ^bb0(%arg0: !cir.ptr<!rec_S>):
+ cir.call @_ZN1SC1Ev(%arg0) : (!cir.ptr<!rec_S>) -> ()
+ cir.yield
+ }
+ cir.return
+ }
+
+ // CHECK: cir.func private @_ZN1SC1Ev(!cir.ptr<!rec_S>)
+ // CHECK: cir.func dso_local @_Z3foov() {
+ // CHECK: %0 = cir.alloca !cir.array<!rec_S x 42>, !cir.ptr<!cir.array<!rec_S x 42>>, ["s", init] {alignment = 16 : i64}
+ // CHECK: cir.array.ctor(%0 : !cir.ptr<!cir.array<!rec_S x 42>>) {
+ // CHECK: ^bb0(%arg0: !cir.ptr<!rec_S>):
+ // CHECK: cir.call @_ZN1SC1Ev(%arg0) : (!cir.ptr<!rec_S>) -> ()
+ // CHECK: cir.yield
+ // CHECK: }
+ // CHECK: cir.return
+ // CHECK: }
+}
>From 6b5693ec85b7f23cb734bb33db81b10e92c5ca58 Mon Sep 17 00:00:00 2001
From: Morris Hafner <mmha at users.noreply.github.com>
Date: Thu, 17 Jul 2025 15:06:16 +0200
Subject: [PATCH 2/4] Apply suggestions from code review
Co-authored-by: Andy Kaylor <akaylor at nvidia.com>
Co-authored-by: Henrich Lauko <xlauko at mail.muni.cz>
---
clang/lib/CIR/CodeGen/CIRGenClass.cpp | 5 ++-
clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 2 +-
.../Dialect/Transforms/LoweringPrepare.cpp | 31 +++++++++----------
3 files changed, 17 insertions(+), 21 deletions(-)
diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
index 26ace1c366780..ee61daf616fb0 100644
--- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
@@ -360,8 +360,7 @@ void CIRGenFunction::emitCXXAggrConstructorCall(
cgm.errorNYI(e->getSourceRange(), "dynamic-length array expression");
}
- auto arrayTy = mlir::dyn_cast<cir::ArrayType>(arrayBase.getElementType());
- assert(arrayTy && "expected array type");
+ auto arrayTy = mlir::cast<cir::ArrayType>(arrayBase.getElementType());
mlir::Type elementType = arrayTy.getElementType();
cir::PointerType ptrToElmType = builder.getPointerTo(elementType);
@@ -400,7 +399,7 @@ void CIRGenFunction::emitCXXAggrConstructorCall(
}
// Emit the constructor call that will execute for every array element.
- auto arrayOp = builder.createPtrBitcast(arrayBase.getPointer(), arrayTy);
+ mlir::Value arrayOp = builder.createPtrBitcast(arrayBase.getPointer(), arrayTy);
builder.create<cir::ArrayCtor>(
*currSrcLoc, arrayOp, [&](mlir::OpBuilder &b, mlir::Location loc) {
auto arg = b.getInsertionBlock()->addArgument(ptrToElmType, loc);
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index bdc1df41c26e8..bb09b9876aea1 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -845,7 +845,7 @@ CIRGenFunction::emitArrayLength(const clang::ArrayType *origArrayType,
}
baseType = eltType;
- auto numElements = builder.getConstInt(*currSrcLoc, SizeTy, countFromCLAs);
+ cir::ConstantOp numElements = builder.getConstInt(*currSrcLoc, SizeTy, countFromCLAs);
return numElements;
}
diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
index d32d47761b485..50d8667111dcb 100644
--- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
@@ -24,30 +24,27 @@ struct LoweringPreparePass : public LoweringPrepareBase<LoweringPreparePass> {
void runOnOperation() override;
void runOnOp(Operation *op);
- void lowerArrayCtor(ArrayCtor op);
+ void lowerArrayCtor(cir::ArrayCtor op);
};
} // namespace
-void LoweringPreparePass::runOnOp(Operation *op) {
+void LoweringPreparePass::runOnOp(mlir::Operation *op) {
if (auto arrayCtor = dyn_cast<ArrayCtor>(op)) {
lowerArrayCtor(arrayCtor);
}
}
-static void lowerArrayDtorCtorIntoLoop(CIRBaseBuilderTy &builder,
+static void lowerArrayDtorCtorIntoLoop(cir::CIRBaseBuilderTy &builder,
mlir::Operation *op, mlir::Type eltTy,
mlir::Value arrayAddr,
uint64_t arrayLen) {
// Generate loop to call into ctor/dtor for every element.
- Location loc = op->getLoc();
+ mlir::Location loc = op->getLoc();
// TODO: instead of fixed integer size, create alias for PtrDiffTy and unify
// with CIRGen stuff.
- auto ptrDiffTy =
- cir::IntType::get(builder.getContext(), 64, /*isSigned=*/false);
- auto numArrayElementsConst = builder.create<cir::ConstantOp>(
- loc, ptrDiffTy, cir::IntAttr::get(ptrDiffTy, arrayLen));
+ cir::ConstantOp numArrayElementsConst = builder.getUnsignedInt(loc, 64, arrayLen);
auto begin = builder.create<cir::CastOp>(
loc, eltTy, cir::CastKind::array_to_ptrdecay, arrayAddr);
@@ -73,8 +70,8 @@ static void lowerArrayDtorCtorIntoLoop(CIRBaseBuilderTy &builder,
[&](mlir::OpBuilder &b, mlir::Location loc) {
auto currentElement = b.create<cir::LoadOp>(loc, eltTy, tmpAddr);
- CallOp ctorCall;
- op->walk([&](CallOp c) { ctorCall = c; });
+ cir::CallOp ctorCall;
+ op->walk([&](cir::CallOp c) { ctorCall = c; });
assert(ctorCall && "expected ctor call");
auto one = builder.create<cir::ConstantOp>(
@@ -94,23 +91,23 @@ static void lowerArrayDtorCtorIntoLoop(CIRBaseBuilderTy &builder,
op->erase();
}
-void LoweringPreparePass::lowerArrayCtor(ArrayCtor op) {
- CIRBaseBuilderTy builder(getContext());
+void LoweringPreparePass::lowerArrayCtor(cir::ArrayCtor op) {
+ cir::CIRBaseBuilderTy builder(getContext());
builder.setInsertionPointAfter(op.getOperation());
- Type eltTy = op->getRegion(0).getArgument(0).getType();
+ mlir::Type eltTy = op->getRegion(0).getArgument(0).getType();
auto arrayLen =
mlir::cast<cir::ArrayType>(op.getAddr().getType().getPointee()).getSize();
lowerArrayDtorCtorIntoLoop(builder, op, eltTy, op.getAddr(), arrayLen);
}
void LoweringPreparePass::runOnOperation() {
- Operation *op = getOperation();
+ mlir::Operation *op = getOperation();
- llvm::SmallVector<Operation *> opsToTransform;
+ llvm::SmallVector<mlir::Operation *> opsToTransform;
- op->walk([&](Operation *op) {
- if (isa<ArrayCtor>(op)) {
+ op->walk([&](mlir::Operation *op) {
+ if (mlir::isa<cir::ArrayCtor>(op)) {
opsToTransform.push_back(op);
}
});
>From 7a33bb64aa0db96588b6451f4d704990b4bb7ed3 Mon Sep 17 00:00:00 2001
From: Morris Hafner <mhafner at nvidia.com>
Date: Thu, 17 Jul 2025 15:34:27 +0200
Subject: [PATCH 3/4] Fix compilation errors after applying suggestions
---
.../CIR/Dialect/Builder/CIRBaseBuilder.h | 6 +++++
clang/include/clang/CIR/Dialect/Passes.h | 1 +
.../Dialect/Transforms/LoweringPrepare.cpp | 25 +++++++++++++++++--
clang/lib/CIR/Lowering/CIRPasses.cpp | 2 +-
4 files changed, 31 insertions(+), 3 deletions(-)
diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
index 277c278fd38b7..bd358ce956785 100644
--- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
+++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
@@ -75,6 +75,12 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
return getConstant(loc, cir::IntAttr::get(ty, value));
}
+ mlir::Value getUnsignedInt(mlir::Location loc, uint64_t val,
+ unsigned numBits) {
+ auto type = cir::IntType::get(getContext(), numBits, /*isSigned=*/false);
+ return getConstAPInt(loc, type, llvm::APInt(numBits, val));
+ }
+
// Creates constant null value for integral type ty.
cir::ConstantOp getNullValue(mlir::Type ty, mlir::Location loc) {
return getConstant(loc, getZeroInitAttr(ty));
diff --git a/clang/include/clang/CIR/Dialect/Passes.h b/clang/include/clang/CIR/Dialect/Passes.h
index 02210ec0a8336..7a202b1e04ef9 100644
--- a/clang/include/clang/CIR/Dialect/Passes.h
+++ b/clang/include/clang/CIR/Dialect/Passes.h
@@ -25,6 +25,7 @@ std::unique_ptr<Pass> createCIRFlattenCFGPass();
std::unique_ptr<Pass> createCIRSimplifyPass();
std::unique_ptr<Pass> createHoistAllocasPass();
std::unique_ptr<Pass> createLoweringPreparePass();
+std::unique_ptr<Pass> createLoweringPreparePass(clang::ASTContext *astCtx);
void populateCIRPreLoweringPasses(mlir::OpPassManager &pm);
diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
index 50d8667111dcb..6563f12948ca9 100644
--- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
@@ -25,6 +25,14 @@ struct LoweringPreparePass : public LoweringPrepareBase<LoweringPreparePass> {
void runOnOp(Operation *op);
void lowerArrayCtor(cir::ArrayCtor op);
+
+ ///
+ /// AST related
+ /// -----------
+
+ clang::ASTContext *astCtx;
+
+ void setASTContext(clang::ASTContext *c) { astCtx = c; }
};
} // namespace
@@ -36,6 +44,7 @@ void LoweringPreparePass::runOnOp(mlir::Operation *op) {
}
static void lowerArrayDtorCtorIntoLoop(cir::CIRBaseBuilderTy &builder,
+ clang::ASTContext *astCtx,
mlir::Operation *op, mlir::Type eltTy,
mlir::Value arrayAddr,
uint64_t arrayLen) {
@@ -44,7 +53,11 @@ static void lowerArrayDtorCtorIntoLoop(cir::CIRBaseBuilderTy &builder,
// TODO: instead of fixed integer size, create alias for PtrDiffTy and unify
// with CIRGen stuff.
- cir::ConstantOp numArrayElementsConst = builder.getUnsignedInt(loc, 64, arrayLen);
+ const unsigned sizeTypeSize =
+ astCtx->getTypeSize(astCtx->getSignedSizeType());
+ auto ptrDiffTy =
+ cir::IntType::get(builder.getContext(), sizeTypeSize, /*isSigned=*/false);
+ mlir::Value numArrayElementsConst = builder.getUnsignedInt(loc, arrayLen, 64);
auto begin = builder.create<cir::CastOp>(
loc, eltTy, cir::CastKind::array_to_ptrdecay, arrayAddr);
@@ -98,7 +111,8 @@ void LoweringPreparePass::lowerArrayCtor(cir::ArrayCtor op) {
mlir::Type eltTy = op->getRegion(0).getArgument(0).getType();
auto arrayLen =
mlir::cast<cir::ArrayType>(op.getAddr().getType().getPointee()).getSize();
- lowerArrayDtorCtorIntoLoop(builder, op, eltTy, op.getAddr(), arrayLen);
+ lowerArrayDtorCtorIntoLoop(builder, astCtx, op, eltTy, op.getAddr(),
+ arrayLen);
}
void LoweringPreparePass::runOnOperation() {
@@ -119,3 +133,10 @@ void LoweringPreparePass::runOnOperation() {
std::unique_ptr<Pass> mlir::createLoweringPreparePass() {
return std::make_unique<LoweringPreparePass>();
}
+
+std::unique_ptr<Pass>
+mlir::createLoweringPreparePass(clang::ASTContext *astCtx) {
+ auto pass = std::make_unique<LoweringPreparePass>();
+ pass->setASTContext(astCtx);
+ return std::move(pass);
+}
diff --git a/clang/lib/CIR/Lowering/CIRPasses.cpp b/clang/lib/CIR/Lowering/CIRPasses.cpp
index 5607abc98e319..bb9781be897eb 100644
--- a/clang/lib/CIR/Lowering/CIRPasses.cpp
+++ b/clang/lib/CIR/Lowering/CIRPasses.cpp
@@ -31,7 +31,7 @@ mlir::LogicalResult runCIRToCIRPasses(mlir::ModuleOp theModule,
if (enableCIRSimplify)
pm.addPass(mlir::createCIRSimplifyPass());
- pm.addPass(mlir::createLoweringPreparePass());
+ pm.addPass(mlir::createLoweringPreparePass(&astContext));
pm.enableVerifier(enableVerifier);
(void)mlir::applyPassManagerCLOptions(pm);
>From d013307229ad53cd403992be8f7d1f2681b065d3 Mon Sep 17 00:00:00 2001
From: Morris Hafner <mhafner at nvidia.com>
Date: Thu, 17 Jul 2025 17:35:06 +0200
Subject: [PATCH 4/4] Address some review feedback
---
clang/include/clang/CIR/Dialect/IR/CIROps.td | 8 ++++++++
clang/include/clang/CIR/MissingFeatures.h | 1 +
clang/lib/CIR/CodeGen/CIRGenClass.cpp | 5 +++--
clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 1 +
clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp | 2 ++
clang/test/CIR/CodeGen/array-ctor.cpp | 8 ++++++++
6 files changed, 23 insertions(+), 2 deletions(-)
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 4c30a54bc650f..4d1de0c7e0023 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -2252,6 +2252,14 @@ def CIR_ArrayCtor : CIR_ArrayInitDestroy<"array.ctor"> {
Initialize each array element using the same C++ constructor. This
operation has one region, with one single block. The block has an
incoming argument for the current array index to initialize.
+
+ ```mlir
+ cir.array.ctor(%0 : !cir.ptr<!cir.array<!rec_S x 42>>) {
+ ^bb0(%arg0: !cir.ptr<!rec_S>):
+ cir.call @_ZN1SC1Ev(%arg0) : (!cir.ptr<!rec_S>) -> ()
+ cir.yield
+ }
+ ```
}];
}
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index 182e4b6784d2f..ce56460cafab0 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -255,6 +255,7 @@ struct MissingFeatures {
static bool completeDtors() { return false; }
static bool vtableInitialization() { return false; }
static bool msvcBuiltins() { return false; }
+ static bool vlas() { return false; }
// Missing types
static bool dataMemberType() { return false; }
diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
index ee61daf616fb0..aa569489498f4 100644
--- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
@@ -355,8 +355,8 @@ void CIRGenFunction::emitCXXAggrConstructorCall(
// Just skip out if the constant count is zero.
if (constIntAttr && constIntAttr.getUInt() == 0)
return;
- // Otherwise, emit the check.
} else {
+ // Otherwise, emit the check.
cgm.errorNYI(e->getSourceRange(), "dynamic-length array expression");
}
@@ -399,7 +399,8 @@ void CIRGenFunction::emitCXXAggrConstructorCall(
}
// Emit the constructor call that will execute for every array element.
- mlir::Value arrayOp = builder.createPtrBitcast(arrayBase.getPointer(), arrayTy);
+ mlir::Value arrayOp =
+ builder.createPtrBitcast(arrayBase.getPointer(), arrayTy);
builder.create<cir::ArrayCtor>(
*currSrcLoc, arrayOp, [&](mlir::OpBuilder &b, mlir::Location loc) {
auto arg = b.getInsertionBlock()->addArgument(ptrToElmType, loc);
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index bb09b9876aea1..dad4f0b0493d2 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -815,6 +815,7 @@ CIRGenFunction::emitArrayLength(const clang::ArrayType *origArrayType,
// If it's a VLA, we have to load the stored size. Note that
// this is the size of the VLA in bytes, not its size in elements.
if (isa<VariableArrayType>(arrayType)) {
+ assert(cir::MissingFeatures::vlas());
cgm.errorNYI(*currSrcLoc, "VLAs");
return builder.getConstInt(*currSrcLoc, SizeTy, 0);
}
diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
index 6563f12948ca9..fb215239ff218 100644
--- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
@@ -12,6 +12,7 @@
#include "clang/CIR/Dialect/Builder/CIRBaseBuilder.h"
#include "clang/CIR/Dialect/IR/CIRDialect.h"
#include "clang/CIR/Dialect/Passes.h"
+#include "clang/CIR/MissingFeatures.h"
#include <memory>
@@ -109,6 +110,7 @@ void LoweringPreparePass::lowerArrayCtor(cir::ArrayCtor op) {
builder.setInsertionPointAfter(op.getOperation());
mlir::Type eltTy = op->getRegion(0).getArgument(0).getType();
+ assert(!cir::MissingFeatures::vlas());
auto arrayLen =
mlir::cast<cir::ArrayType>(op.getAddr().getType().getPointee()).getSize();
lowerArrayDtorCtorIntoLoop(builder, astCtx, op, eltTy, op.getAddr(),
diff --git a/clang/test/CIR/CodeGen/array-ctor.cpp b/clang/test/CIR/CodeGen/array-ctor.cpp
index 0a2661248f908..d927db3189270 100644
--- a/clang/test/CIR/CodeGen/array-ctor.cpp
+++ b/clang/test/CIR/CodeGen/array-ctor.cpp
@@ -68,3 +68,11 @@ void foo() {
// OGCG: br i1 %[[DONE]], label %[[EXIT:.*]], label %[[LOOP]]
// OGCG: [[EXIT]]:
// OGCG: ret void
+
+void zero_sized() {
+ int s[0];
+}
+
+// CIR: cir.func dso_local @_Z10zero_sizedv()
+// CIR-NOT: cir.do
+// CIR: cir.return
More information about the cfe-commits
mailing list