Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,12 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
return getConstant(loc, cir::IntAttr::get(ty, value));
}

mlir::Value getSignedInt(mlir::Location loc, int64_t val, unsigned numBits) {
auto type = cir::IntType::get(getContext(), numBits, /*isSigned=*/true);
return getConstAPInt(loc, type,
llvm::APInt(numBits, val, /*isSigned=*/true));
}

mlir::Value getUnsignedInt(mlir::Location loc, uint64_t val,
unsigned numBits) {
auto type = cir::IntType::get(getContext(), numBits, /*isSigned=*/false);
Expand Down
29 changes: 25 additions & 4 deletions clang/include/clang/CIR/Dialect/IR/CIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -607,8 +607,8 @@ def CIR_ConditionOp : CIR_Op<"condition", [
//===----------------------------------------------------------------------===//

defvar CIR_YieldableScopes = [
"ArrayCtor", "CaseOp", "DoWhileOp", "ForOp", "IfOp", "ScopeOp", "SwitchOp",
"TernaryOp", "WhileOp"
"ArrayCtor", "ArrayDtor", "CaseOp", "DoWhileOp", "ForOp", "IfOp", "ScopeOp",
"SwitchOp", "TernaryOp", "WhileOp"
];

def CIR_YieldOp : CIR_Op<"yield", [
Expand Down Expand Up @@ -2229,7 +2229,7 @@ def CIR_TrapOp : CIR_Op<"trap", [Terminator]> {
}

//===----------------------------------------------------------------------===//
// ArrayCtor
// ArrayCtor & ArrayDtor
//===----------------------------------------------------------------------===//

class CIR_ArrayInitDestroy<string mnemonic> : CIR_Op<mnemonic> {
Expand Down Expand Up @@ -2260,7 +2260,9 @@ def CIR_ArrayCtor : CIR_ArrayInitDestroy<"array.ctor"> {
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.
incoming argument for the current array element to initialize.

Example:

```mlir
cir.array.ctor(%0 : !cir.ptr<!cir.array<!rec_S x 42>>) {
Expand All @@ -2272,6 +2274,25 @@ def CIR_ArrayCtor : CIR_ArrayInitDestroy<"array.ctor"> {
}];
}

def CIR_ArrayDtor : CIR_ArrayInitDestroy<"array.dtor"> {
let summary = "Destroy array elements with C++ dtors";
let description = [{
Destroy each array element using the same C++ destructor. This
operation has one region, with one single block. The block has an
incoming argument for the current array element to destruct.

Example:

```mlir
cir.array.dtor(%0 : !cir.ptr<!cir.array<!rec_S x 42>>) {
^bb0(%arg0: !cir.ptr<!rec_S>):
cir.call @some_dtor(%arg0) : (!cir.ptr<!rec_S>) -> ()
cir.yield
}
```
}];
}

//===----------------------------------------------------------------------===//
// VecCreate
//===----------------------------------------------------------------------===//
Expand Down
62 changes: 59 additions & 3 deletions clang/lib/CIR/CodeGen/CIRGenDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,38 @@ void CIRGenFunction::emitNullabilityCheck(LValue lhs, mlir::Value rhs,
assert(!cir::MissingFeatures::sanitizers());
}

/// Destroys all the elements of the given array, beginning from last to first.
/// The array cannot be zero-length.
///
/// \param begin - a type* denoting the first element of the array
/// \param end - a type* denoting one past the end of the array
/// \param elementType - the element type of the array
/// \param destroyer - the function to call to destroy elements
void CIRGenFunction::emitArrayDestroy(mlir::Value begin, mlir::Value end,
QualType elementType,
CharUnits elementAlign,
Destroyer *destroyer) {
assert(!elementType->isArrayType());

// Differently from LLVM traditional codegen, use a higher level
// representation instead of lowering directly to a loop.
mlir::Type cirElementType = convertTypeForMem(elementType);
cir::PointerType ptrToElmType = builder.getPointerTo(cirElementType);

// Emit the dtor call that will execute for every array element.
cir::ArrayDtor::create(
builder, *currSrcLoc, begin, [&](mlir::OpBuilder &b, mlir::Location loc) {
auto arg = b.getInsertionBlock()->addArgument(ptrToElmType, loc);
Address curAddr = Address(arg, cirElementType, elementAlign);
assert(!cir::MissingFeatures::dtorCleanups());

// Perform the actual destruction there.
destroyer(*this, curAddr, elementType);

cir::YieldOp::create(builder, loc);
});
}

/// Immediately perform the destruction of the given object.
///
/// \param addr - the address of the object; a type*
Expand All @@ -658,10 +690,34 @@ void CIRGenFunction::emitNullabilityCheck(LValue lhs, mlir::Value rhs,
/// elements
void CIRGenFunction::emitDestroy(Address addr, QualType type,
Destroyer *destroyer) {
if (getContext().getAsArrayType(type))
cgm.errorNYI("emitDestroy: array type");
const ArrayType *arrayType = getContext().getAsArrayType(type);
if (!arrayType)
return destroyer(*this, addr, type);

mlir::Value length = emitArrayLength(arrayType, type, addr);

CharUnits elementAlign = addr.getAlignment().alignmentOfArrayElement(
getContext().getTypeSizeInChars(type));

auto constantCount = length.getDefiningOp<cir::ConstantOp>();
if (!constantCount) {
assert(!cir::MissingFeatures::vlas());
cgm.errorNYI("emitDestroy: variable length array");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a cir::MissingFeature::vla() here?

return;
}

auto constIntAttr = mlir::dyn_cast<cir::IntAttr>(constantCount.getValue());
// If it's constant zero, we can just skip the entire thing.
if (constIntAttr && constIntAttr.getUInt() == 0)
return;

mlir::Value begin = addr.getPointer();
mlir::Value end; // This will be used for future non-constant counts.
emitArrayDestroy(begin, end, type, elementAlign, destroyer);

return destroyer(*this, addr, type);
// If the array destroy didn't use the length op, we can erase it.
if (constantCount.use_empty())
Copy link
Contributor

@mmha mmha Jul 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can simplify the control flow a little if you invert the condition up there. Something like:

if(!constantCount) {
  assert(!cir::MissingFeature::vlas());
  cgm.errorNYI("emitDestroy: variable length array");
  return;
}

auto constIntAttr = mlir::dyn_cast<cir::IntAttr>(constantCount.getValue());
// ...and if it's constant zero, we can just skip the entire thing.
if (constIntAttr && constIntAttr.getUInt() == 0)
  return;
checkZeroLength = false;

mlir::Value begin = addr.getPointer();
mlir::Value end; // This will be used for future non-constant counts.
emitArrayDestroy(begin, end, type, elementAlign, destroyer, checkZeroLength);

if (constantCount.use_empty())
  constantCount.erase();

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will probably need to switch back to the if-else structure when we support VLAs, but for now your suggestion looks better. I'll make the change.

constantCount.erase();
}

CIRGenFunction::Destroyer *
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -848,6 +848,10 @@ class CIRGenFunction : public CIRGenTypeCache {
/// even if no aggregate location is provided.
RValue emitAnyExprToTemp(const clang::Expr *e);

void emitArrayDestroy(mlir::Value begin, mlir::Value end,
QualType elementType, CharUnits elementAlign,
Destroyer *destroyer);

mlir::Value emitArrayLength(const clang::ArrayType *arrayType,
QualType &baseType, Address &addr);
LValue emitArraySubscriptExpr(const clang::ArraySubscriptExpr *e);
Expand Down
68 changes: 45 additions & 23 deletions clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ struct LoweringPreparePass : public LoweringPrepareBase<LoweringPreparePass> {
void runOnOp(mlir::Operation *op);
void lowerCastOp(cir::CastOp op);
void lowerUnaryOp(cir::UnaryOp op);
void lowerArrayCtor(ArrayCtor op);
void lowerArrayDtor(cir::ArrayDtor op);
void lowerArrayCtor(cir::ArrayCtor op);

///
/// AST related
Expand Down Expand Up @@ -172,28 +173,30 @@ void LoweringPreparePass::lowerUnaryOp(cir::UnaryOp op) {
static void lowerArrayDtorCtorIntoLoop(cir::CIRBaseBuilderTy &builder,
clang::ASTContext *astCtx,
mlir::Operation *op, mlir::Type eltTy,
mlir::Value arrayAddr,
uint64_t arrayLen) {
mlir::Value arrayAddr, uint64_t arrayLen,
bool isCtor) {
// Generate loop to call into ctor/dtor for every element.
mlir::Location loc = op->getLoc();

// TODO: instead of fixed integer size, create alias for PtrDiffTy and unify
// with CIRGen stuff.
// TODO: instead of getting the size from the AST context, create alias for
// PtrDiffTy and unify with CIRGen stuff.
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);
uint64_t endOffset = isCtor ? arrayLen : arrayLen - 1;
mlir::Value endOffsetVal =
builder.getUnsignedInt(loc, endOffset, sizeTypeSize);

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);
auto begin = cir::CastOp::create(builder, loc, eltTy,
cir::CastKind::array_to_ptrdecay, arrayAddr);
mlir::Value end =
cir::PtrStrideOp::create(builder, loc, eltTy, begin, endOffsetVal);
mlir::Value start = isCtor ? begin : end;
mlir::Value stop = isCtor ? end : begin;

mlir::Value tmpAddr = builder.createAlloca(
loc, /*addr type*/ builder.getPointerTo(eltTy),
/*var type*/ eltTy, "__array_idx", builder.getAlignmentAttr(1));
builder.createStore(loc, begin, tmpAddr);
builder.createStore(loc, start, tmpAddr);

cir::DoWhileOp loop = builder.createDoWhile(
loc,
Expand All @@ -202,7 +205,7 @@ static void lowerArrayDtorCtorIntoLoop(cir::CIRBaseBuilderTy &builder,
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::ne,
currentElement, end);
currentElement, stop);
builder.createCondition(cmp);
},
/*bodyBuilder=*/
Expand All @@ -213,15 +216,19 @@ static void lowerArrayDtorCtorIntoLoop(cir::CIRBaseBuilderTy &builder,
op->walk([&](cir::CallOp c) { ctorCall = c; });
assert(ctorCall && "expected ctor call");

auto one = builder.create<cir::ConstantOp>(
loc, ptrDiffTy, cir::IntAttr::get(ptrDiffTy, 1));
// Array elements get constructed in order but destructed in reverse.
mlir::Value stride;
if (isCtor)
stride = builder.getUnsignedInt(loc, 1, sizeTypeSize);
else
stride = builder.getSignedInt(loc, -1, sizeTypeSize);

ctorCall->moveAfter(one);
ctorCall->moveBefore(stride.getDefiningOp());
ctorCall->setOperand(0, currentElement);
auto nextElement = cir::PtrStrideOp::create(builder, loc, eltTy,
currentElement, stride);

// Advance pointer and store them to temporary variable
auto nextElement =
builder.create<cir::PtrStrideOp>(loc, eltTy, currentElement, one);
// Store the element pointer to the temporary variable
builder.createStore(loc, nextElement, tmpAddr);
builder.createYield(loc);
});
Expand All @@ -230,6 +237,18 @@ static void lowerArrayDtorCtorIntoLoop(cir::CIRBaseBuilderTy &builder,
op->erase();
}

void LoweringPreparePass::lowerArrayDtor(cir::ArrayDtor op) {
CIRBaseBuilderTy builder(getContext());
builder.setInsertionPointAfter(op.getOperation());

mlir::Type eltTy = op->getRegion(0).getArgument(0).getType();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ArrayCtor asserts here !cir::MissingFeatures::vlas(), shouldn't it be asserted also here?

assert(!cir::MissingFeatures::vlas());
auto arrayLen =
mlir::cast<cir::ArrayType>(op.getAddr().getType().getPointee()).getSize();
lowerArrayDtorCtorIntoLoop(builder, astCtx, op, eltTy, op.getAddr(), arrayLen,
false);
}

void LoweringPreparePass::lowerArrayCtor(cir::ArrayCtor op) {
cir::CIRBaseBuilderTy builder(getContext());
builder.setInsertionPointAfter(op.getOperation());
Expand All @@ -238,13 +257,15 @@ void LoweringPreparePass::lowerArrayCtor(cir::ArrayCtor op) {
assert(!cir::MissingFeatures::vlas());
auto arrayLen =
mlir::cast<cir::ArrayType>(op.getAddr().getType().getPointee()).getSize();
lowerArrayDtorCtorIntoLoop(builder, astCtx, op, eltTy, op.getAddr(),
arrayLen);
lowerArrayDtorCtorIntoLoop(builder, astCtx, op, eltTy, op.getAddr(), arrayLen,
true);
}

void LoweringPreparePass::runOnOp(mlir::Operation *op) {
if (auto arrayCtor = dyn_cast<ArrayCtor>(op))
lowerArrayCtor(arrayCtor);
else if (auto arrayDtor = dyn_cast<cir::ArrayDtor>(op))
lowerArrayDtor(arrayDtor);
else if (auto cast = mlir::dyn_cast<cir::CastOp>(op))
lowerCastOp(cast);
else if (auto unary = mlir::dyn_cast<cir::UnaryOp>(op))
Expand All @@ -257,7 +278,8 @@ void LoweringPreparePass::runOnOperation() {
llvm::SmallVector<mlir::Operation *> opsToTransform;

op->walk([&](mlir::Operation *op) {
if (mlir::isa<cir::ArrayCtor, cir::CastOp, cir::UnaryOp>(op))
if (mlir::isa<cir::ArrayCtor, cir::ArrayDtor, cir::CastOp, cir::UnaryOp>(
op))
opsToTransform.push_back(op);
});

Expand Down
2 changes: 1 addition & 1 deletion clang/test/CIR/CodeGen/array-ctor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ void foo() {
// 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: %[[CONST1:.*]] = cir.const #cir.int<1> : !u64i
// 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
Expand Down
Loading