Skip to content

Commit 3e9d369

Browse files
mmhaandykaylorxlauko
authored
[CIR] Add support for array constructors (#149142)
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 --------- Co-authored-by: Andy Kaylor <[email protected]> Co-authored-by: Henrich Lauko <[email protected]>
1 parent 862b9ea commit 3e9d369

File tree

13 files changed

+489
-33
lines changed

13 files changed

+489
-33
lines changed

clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,12 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
7575
return getConstant(loc, cir::IntAttr::get(ty, value));
7676
}
7777

78+
mlir::Value getUnsignedInt(mlir::Location loc, uint64_t val,
79+
unsigned numBits) {
80+
auto type = cir::IntType::get(getContext(), numBits, /*isSigned=*/false);
81+
return getConstAPInt(loc, type, llvm::APInt(numBits, val));
82+
}
83+
7884
// Creates constant null value for integral type ty.
7985
cir::ConstantOp getNullValue(mlir::Type ty, mlir::Location loc) {
8086
return getConstant(loc, getZeroInitAttr(ty));

clang/include/clang/CIR/Dialect/IR/CIROps.td

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -607,7 +607,7 @@ def CIR_ConditionOp : CIR_Op<"condition", [
607607
//===----------------------------------------------------------------------===//
608608

609609
defvar CIR_YieldableScopes = [
610-
"CaseOp", "DoWhileOp", "ForOp", "IfOp", "ScopeOp", "SwitchOp",
610+
"ArrayCtor", "CaseOp", "DoWhileOp", "ForOp", "IfOp", "ScopeOp", "SwitchOp",
611611
"TernaryOp", "WhileOp"
612612
];
613613

@@ -2228,6 +2228,50 @@ def CIR_TrapOp : CIR_Op<"trap", [Terminator]> {
22282228
let assemblyFormat = "attr-dict";
22292229
}
22302230

2231+
//===----------------------------------------------------------------------===//
2232+
// ArrayCtor
2233+
//===----------------------------------------------------------------------===//
2234+
2235+
class CIR_ArrayInitDestroy<string mnemonic> : CIR_Op<mnemonic> {
2236+
let arguments = (ins
2237+
Arg<CIR_PtrToArray, "array address", [MemWrite, MemRead]>:$addr
2238+
);
2239+
2240+
let regions = (region SizedRegion<1>:$body);
2241+
let assemblyFormat = [{
2242+
$addr `:` qualified(type($addr)) $body attr-dict
2243+
}];
2244+
2245+
let builders = [
2246+
OpBuilder<(ins "mlir::Value":$addr,
2247+
"llvm::function_ref<void(mlir::OpBuilder &, mlir::Location)>":$regionBuilder), [{
2248+
assert(regionBuilder && "builder callback expected");
2249+
mlir::OpBuilder::InsertionGuard guard($_builder);
2250+
mlir::Region *r = $_state.addRegion();
2251+
$_state.addOperands(ValueRange{addr});
2252+
$_builder.createBlock(r);
2253+
regionBuilder($_builder, $_state.location);
2254+
}]>
2255+
];
2256+
}
2257+
2258+
def CIR_ArrayCtor : CIR_ArrayInitDestroy<"array.ctor"> {
2259+
let summary = "Initialize array elements with C++ constructors";
2260+
let description = [{
2261+
Initialize each array element using the same C++ constructor. This
2262+
operation has one region, with one single block. The block has an
2263+
incoming argument for the current array index to initialize.
2264+
2265+
```mlir
2266+
cir.array.ctor(%0 : !cir.ptr<!cir.array<!rec_S x 42>>) {
2267+
^bb0(%arg0: !cir.ptr<!rec_S>):
2268+
cir.call @some_ctor(%arg0) : (!cir.ptr<!rec_S>) -> ()
2269+
cir.yield
2270+
}
2271+
```
2272+
}];
2273+
}
2274+
22312275
//===----------------------------------------------------------------------===//
22322276
// VecCreate
22332277
//===----------------------------------------------------------------------===//

clang/include/clang/CIR/Dialect/IR/CIRTypeConstraints.td

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,12 @@ def CIR_AnyIntOrFloatType : AnyTypeOf<[CIR_AnyFloatType, CIR_AnyIntType],
165165

166166
def CIR_AnyComplexType : CIR_TypeBase<"::cir::ComplexType", "complex type">;
167167

168+
//===----------------------------------------------------------------------===//
169+
// Array Type predicates
170+
//===----------------------------------------------------------------------===//
171+
172+
def CIR_AnyArrayType : CIR_TypeBase<"::cir::ArrayType", "array type">;
173+
168174
//===----------------------------------------------------------------------===//
169175
// Pointer Type predicates
170176
//===----------------------------------------------------------------------===//
@@ -216,6 +222,8 @@ def CIR_PtrToIntOrFloatType : CIR_PtrToType<CIR_AnyIntOrFloatType>;
216222

217223
def CIR_PtrToComplexType : CIR_PtrToType<CIR_AnyComplexType>;
218224

225+
def CIR_PtrToArray : CIR_PtrToType<CIR_AnyArrayType>;
226+
219227
//===----------------------------------------------------------------------===//
220228
// Vector Type predicates
221229
//===----------------------------------------------------------------------===//

clang/include/clang/CIR/Dialect/Passes.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ std::unique_ptr<Pass> createCIRFlattenCFGPass();
2525
std::unique_ptr<Pass> createCIRSimplifyPass();
2626
std::unique_ptr<Pass> createHoistAllocasPass();
2727
std::unique_ptr<Pass> createLoweringPreparePass();
28+
std::unique_ptr<Pass> createLoweringPreparePass(clang::ASTContext *astCtx);
2829

2930
void populateCIRPreLoweringPasses(mlir::OpPassManager &pm);
3031

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,7 @@ struct MissingFeatures {
254254
static bool dtorCleanups() { return false; }
255255
static bool vtableInitialization() { return false; }
256256
static bool msvcBuiltins() { return false; }
257+
static bool vlas() { return false; }
257258

258259
// Missing types
259260
static bool dataMemberType() { return false; }

clang/lib/CIR/CodeGen/CIRGenClass.cpp

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#include "CIRGenCXXABI.h"
1414
#include "CIRGenFunction.h"
15+
#include "CIRGenValue.h"
1516

1617
#include "clang/AST/ExprCXX.h"
1718
#include "clang/AST/RecordLayout.h"
@@ -311,6 +312,116 @@ void CIRGenFunction::emitInitializerForField(FieldDecl *field, LValue lhs,
311312
assert(!cir::MissingFeatures::requiresCleanups());
312313
}
313314

315+
/// Emit a loop to call a particular constructor for each of several members
316+
/// of an array.
317+
///
318+
/// \param ctor the constructor to call for each element
319+
/// \param arrayType the type of the array to initialize
320+
/// \param arrayBegin an arrayType*
321+
/// \param zeroInitialize true if each element should be
322+
/// zero-initialized before it is constructed
323+
void CIRGenFunction::emitCXXAggrConstructorCall(
324+
const CXXConstructorDecl *ctor, const clang::ArrayType *arrayType,
325+
Address arrayBegin, const CXXConstructExpr *e, bool newPointerIsChecked,
326+
bool zeroInitialize) {
327+
QualType elementType;
328+
mlir::Value numElements = emitArrayLength(arrayType, elementType, arrayBegin);
329+
emitCXXAggrConstructorCall(ctor, numElements, arrayBegin, e,
330+
newPointerIsChecked, zeroInitialize);
331+
}
332+
333+
/// Emit a loop to call a particular constructor for each of several members
334+
/// of an array.
335+
///
336+
/// \param ctor the constructor to call for each element
337+
/// \param numElements the number of elements in the array;
338+
/// may be zero
339+
/// \param arrayBase a T*, where T is the type constructed by ctor
340+
/// \param zeroInitialize true if each element should be
341+
/// zero-initialized before it is constructed
342+
void CIRGenFunction::emitCXXAggrConstructorCall(
343+
const CXXConstructorDecl *ctor, mlir::Value numElements, Address arrayBase,
344+
const CXXConstructExpr *e, bool newPointerIsChecked, bool zeroInitialize) {
345+
// It's legal for numElements to be zero. This can happen both
346+
// dynamically, because x can be zero in 'new A[x]', and statically,
347+
// because of GCC extensions that permit zero-length arrays. There
348+
// are probably legitimate places where we could assume that this
349+
// doesn't happen, but it's not clear that it's worth it.
350+
351+
// Optimize for a constant count.
352+
auto constantCount = dyn_cast<cir::ConstantOp>(numElements.getDefiningOp());
353+
if (constantCount) {
354+
auto constIntAttr = mlir::dyn_cast<cir::IntAttr>(constantCount.getValue());
355+
// Just skip out if the constant count is zero.
356+
if (constIntAttr && constIntAttr.getUInt() == 0)
357+
return;
358+
} else {
359+
// Otherwise, emit the check.
360+
cgm.errorNYI(e->getSourceRange(), "dynamic-length array expression");
361+
}
362+
363+
auto arrayTy = mlir::cast<cir::ArrayType>(arrayBase.getElementType());
364+
mlir::Type elementType = arrayTy.getElementType();
365+
cir::PointerType ptrToElmType = builder.getPointerTo(elementType);
366+
367+
// Tradional LLVM codegen emits a loop here. CIR lowers to a loop as part of
368+
// LoweringPrepare.
369+
370+
// The alignment of the base, adjusted by the size of a single element,
371+
// provides a conservative estimate of the alignment of every element.
372+
// (This assumes we never start tracking offsetted alignments.)
373+
//
374+
// Note that these are complete objects and so we don't need to
375+
// use the non-virtual size or alignment.
376+
QualType type = getContext().getTypeDeclType(ctor->getParent());
377+
CharUnits eltAlignment = arrayBase.getAlignment().alignmentOfArrayElement(
378+
getContext().getTypeSizeInChars(type));
379+
380+
// Zero initialize the storage, if requested.
381+
if (zeroInitialize)
382+
emitNullInitialization(*currSrcLoc, arrayBase, type);
383+
384+
// C++ [class.temporary]p4:
385+
// There are two contexts in which temporaries are destroyed at a different
386+
// point than the end of the full-expression. The first context is when a
387+
// default constructor is called to initialize an element of an array.
388+
// If the constructor has one or more default arguments, the destruction of
389+
// every temporary created in a default argument expression is sequenced
390+
// before the construction of the next array element, if any.
391+
{
392+
assert(!cir::MissingFeatures::runCleanupsScope());
393+
394+
// Evaluate the constructor and its arguments in a regular
395+
// partial-destroy cleanup.
396+
if (getLangOpts().Exceptions &&
397+
!ctor->getParent()->hasTrivialDestructor()) {
398+
cgm.errorNYI(e->getSourceRange(), "partial array cleanups");
399+
}
400+
401+
// Emit the constructor call that will execute for every array element.
402+
mlir::Value arrayOp =
403+
builder.createPtrBitcast(arrayBase.getPointer(), arrayTy);
404+
builder.create<cir::ArrayCtor>(
405+
*currSrcLoc, arrayOp, [&](mlir::OpBuilder &b, mlir::Location loc) {
406+
mlir::BlockArgument arg =
407+
b.getInsertionBlock()->addArgument(ptrToElmType, loc);
408+
Address curAddr = Address(arg, elementType, eltAlignment);
409+
assert(!cir::MissingFeatures::sanitizers());
410+
auto currAVS = AggValueSlot::forAddr(
411+
curAddr, type.getQualifiers(), AggValueSlot::IsDestructed,
412+
AggValueSlot::IsNotAliased, AggValueSlot::DoesNotOverlap,
413+
AggValueSlot::IsNotZeroed);
414+
emitCXXConstructorCall(ctor, Ctor_Complete,
415+
/*ForVirtualBase=*/false,
416+
/*Delegating=*/false, currAVS, e);
417+
builder.create<cir::YieldOp>(loc);
418+
});
419+
}
420+
421+
if (constantCount.use_empty())
422+
constantCount.erase();
423+
}
424+
314425
void CIRGenFunction::emitDelegateCXXConstructorCall(
315426
const CXXConstructorDecl *ctor, CXXCtorType ctorType,
316427
const FunctionArgList &args, SourceLocation loc) {

clang/lib/CIR/CodeGen/CIRGenExpr.cpp

Lines changed: 30 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1657,37 +1657,38 @@ void CIRGenFunction::emitCXXConstructExpr(const CXXConstructExpr *e,
16571657
return;
16581658
}
16591659

1660-
if (getContext().getAsArrayType(e->getType())) {
1661-
cgm.errorNYI(e->getSourceRange(), "emitCXXConstructExpr: array type");
1662-
return;
1663-
}
1660+
if (const ArrayType *arrayType = getContext().getAsArrayType(e->getType())) {
1661+
assert(!cir::MissingFeatures::sanitizers());
1662+
emitCXXAggrConstructorCall(cd, arrayType, dest.getAddress(), e, false);
1663+
} else {
16641664

1665-
clang::CXXCtorType type = Ctor_Complete;
1666-
bool forVirtualBase = false;
1667-
bool delegating = false;
1668-
1669-
switch (e->getConstructionKind()) {
1670-
case CXXConstructionKind::Complete:
1671-
type = Ctor_Complete;
1672-
break;
1673-
case CXXConstructionKind::Delegating:
1674-
// We should be emitting a constructor; GlobalDecl will assert this
1675-
type = curGD.getCtorType();
1676-
delegating = true;
1677-
break;
1678-
case CXXConstructionKind::VirtualBase:
1679-
// This should just set 'forVirtualBase' to true and fall through, but
1680-
// virtual base class support is otherwise missing, so this needs to wait
1681-
// until it can be tested.
1682-
cgm.errorNYI(e->getSourceRange(),
1683-
"emitCXXConstructExpr: virtual base constructor");
1684-
return;
1685-
case CXXConstructionKind::NonVirtualBase:
1686-
type = Ctor_Base;
1687-
break;
1688-
}
1665+
clang::CXXCtorType type = Ctor_Complete;
1666+
bool forVirtualBase = false;
1667+
bool delegating = false;
16891668

1690-
emitCXXConstructorCall(cd, type, forVirtualBase, delegating, dest, e);
1669+
switch (e->getConstructionKind()) {
1670+
case CXXConstructionKind::Complete:
1671+
type = Ctor_Complete;
1672+
break;
1673+
case CXXConstructionKind::Delegating:
1674+
// We should be emitting a constructor; GlobalDecl will assert this
1675+
type = curGD.getCtorType();
1676+
delegating = true;
1677+
break;
1678+
case CXXConstructionKind::VirtualBase:
1679+
// This should just set 'forVirtualBase' to true and fall through, but
1680+
// virtual base class support is otherwise missing, so this needs to wait
1681+
// until it can be tested.
1682+
cgm.errorNYI(e->getSourceRange(),
1683+
"emitCXXConstructExpr: virtual base constructor");
1684+
return;
1685+
case CXXConstructionKind::NonVirtualBase:
1686+
type = Ctor_Base;
1687+
break;
1688+
}
1689+
1690+
emitCXXConstructorCall(cd, type, forVirtualBase, delegating, dest, e);
1691+
}
16911692
}
16921693

16931694
RValue CIRGenFunction::emitReferenceBindingToExpr(const Expr *e) {

clang/lib/CIR/CodeGen/CIRGenFunction.cpp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -808,4 +808,48 @@ bool CIRGenFunction::shouldNullCheckClassCastValue(const CastExpr *ce) {
808808
return true;
809809
}
810810

811+
/// Computes the length of an array in elements, as well as the base
812+
/// element type and a properly-typed first element pointer.
813+
mlir::Value
814+
CIRGenFunction::emitArrayLength(const clang::ArrayType *origArrayType,
815+
QualType &baseType, Address &addr) {
816+
const clang::ArrayType *arrayType = origArrayType;
817+
818+
// If it's a VLA, we have to load the stored size. Note that
819+
// this is the size of the VLA in bytes, not its size in elements.
820+
if (isa<VariableArrayType>(arrayType)) {
821+
assert(cir::MissingFeatures::vlas());
822+
cgm.errorNYI(*currSrcLoc, "VLAs");
823+
return builder.getConstInt(*currSrcLoc, SizeTy, 0);
824+
}
825+
826+
uint64_t countFromCLAs = 1;
827+
QualType eltType;
828+
829+
auto cirArrayType = mlir::dyn_cast<cir::ArrayType>(addr.getElementType());
830+
831+
while (cirArrayType) {
832+
assert(isa<ConstantArrayType>(arrayType));
833+
countFromCLAs *= cirArrayType.getSize();
834+
eltType = arrayType->getElementType();
835+
836+
cirArrayType =
837+
mlir::dyn_cast<cir::ArrayType>(cirArrayType.getElementType());
838+
839+
arrayType = getContext().getAsArrayType(arrayType->getElementType());
840+
assert((!cirArrayType || arrayType) &&
841+
"CIR and Clang types are out-of-sync");
842+
}
843+
844+
if (arrayType) {
845+
// From this point onwards, the Clang array type has been emitted
846+
// as some other type (probably a packed struct). Compute the array
847+
// size, and just emit the 'begin' expression as a bitcast.
848+
cgm.errorNYI(*currSrcLoc, "length for non-array underlying types");
849+
}
850+
851+
baseType = eltType;
852+
return builder.getConstInt(*currSrcLoc, SizeTy, countFromCLAs);
853+
}
854+
811855
} // namespace clang::CIRGen

clang/lib/CIR/CodeGen/CIRGenFunction.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -766,6 +766,8 @@ class CIRGenFunction : public CIRGenTypeCache {
766766
/// even if no aggregate location is provided.
767767
RValue emitAnyExprToTemp(const clang::Expr *e);
768768

769+
mlir::Value emitArrayLength(const clang::ArrayType *arrayType,
770+
QualType &baseType, Address &addr);
769771
LValue emitArraySubscriptExpr(const clang::ArraySubscriptExpr *e);
770772

771773
Address emitArrayToPointerDecay(const Expr *array);
@@ -843,6 +845,16 @@ class CIRGenFunction : public CIRGenTypeCache {
843845
void emitCXXConstructExpr(const clang::CXXConstructExpr *e,
844846
AggValueSlot dest);
845847

848+
void emitCXXAggrConstructorCall(const CXXConstructorDecl *ctor,
849+
const clang::ArrayType *arrayType,
850+
Address arrayBegin, const CXXConstructExpr *e,
851+
bool newPointerIsChecked,
852+
bool zeroInitialize = false);
853+
void emitCXXAggrConstructorCall(const CXXConstructorDecl *ctor,
854+
mlir::Value numElements, Address arrayBase,
855+
const CXXConstructExpr *e,
856+
bool newPointerIsChecked,
857+
bool zeroInitialize);
846858
void emitCXXConstructorCall(const clang::CXXConstructorDecl *d,
847859
clang::CXXCtorType type, bool forVirtualBase,
848860
bool delegating, AggValueSlot thisAVS,

0 commit comments

Comments
 (0)