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 <mhaf...@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 <m...@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 <akay...@nvidia.com> Co-authored-by: Henrich Lauko <xla...@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 <mhaf...@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 <mhaf...@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 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits