Skip to content

Commit 2a78522

Browse files
committed
[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
1 parent 97922a7 commit 2a78522

File tree

9 files changed

+426
-31
lines changed

9 files changed

+426
-31
lines changed

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

Lines changed: 37 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

@@ -2219,6 +2219,42 @@ def CIR_TrapOp : CIR_Op<"trap", [Terminator]> {
22192219
let assemblyFormat = "attr-dict";
22202220
}
22212221

2222+
//===----------------------------------------------------------------------===//
2223+
// ArrayCtor
2224+
//===----------------------------------------------------------------------===//
2225+
2226+
class CIR_ArrayInitDestroy<string mnemonic> : CIR_Op<mnemonic> {
2227+
let arguments = (ins
2228+
Arg<CIR_PtrToArray, "array address", [MemWrite, MemRead]>:$addr
2229+
);
2230+
2231+
let regions = (region SizedRegion<1>:$body);
2232+
let assemblyFormat = [{
2233+
`(` $addr `:` qualified(type($addr)) `)` $body attr-dict
2234+
}];
2235+
2236+
let builders = [
2237+
OpBuilder<(ins "mlir::Value":$addr,
2238+
"llvm::function_ref<void(mlir::OpBuilder &, mlir::Location)>":$regionBuilder), [{
2239+
assert(regionBuilder && "builder callback expected");
2240+
mlir::OpBuilder::InsertionGuard guard($_builder);
2241+
mlir::Region *r = $_state.addRegion();
2242+
$_state.addOperands(ValueRange{addr});
2243+
$_builder.createBlock(r);
2244+
regionBuilder($_builder, $_state.location);
2245+
}]>
2246+
];
2247+
}
2248+
2249+
def CIR_ArrayCtor : CIR_ArrayInitDestroy<"array.ctor"> {
2250+
let summary = "Initialize array elements with C++ constructors";
2251+
let description = [{
2252+
Initialize each array element using the same C++ constructor. This
2253+
operation has one region, with one single block. The block has an
2254+
incoming argument for the current array index to initialize.
2255+
}];
2256+
}
2257+
22222258
//===----------------------------------------------------------------------===//
22232259
// VecCreate
22242260
//===----------------------------------------------------------------------===//

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/lib/CIR/CodeGen/CIRGenClass.cpp

Lines changed: 110 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,115 @@ 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+
// Otherwise, emit the check.
359+
} else {
360+
cgm.errorNYI(e->getSourceRange(), "dynamic-length array expression");
361+
}
362+
363+
auto arrayTy = mlir::dyn_cast<cir::ArrayType>(arrayBase.getElementType());
364+
assert(arrayTy && "expected array type");
365+
mlir::Type elementType = arrayTy.getElementType();
366+
cir::PointerType ptrToElmType = builder.getPointerTo(elementType);
367+
368+
// Tradional LLVM codegen emits a loop here. CIR lowers to a loop as part of
369+
// LoweringPrepare.
370+
371+
// The alignment of the base, adjusted by the size of a single element,
372+
// provides a conservative estimate of the alignment of every element.
373+
// (This assumes we never start tracking offsetted alignments.)
374+
//
375+
// Note that these are complete objects and so we don't need to
376+
// use the non-virtual size or alignment.
377+
QualType type = getContext().getTypeDeclType(ctor->getParent());
378+
CharUnits eltAlignment = arrayBase.getAlignment().alignmentOfArrayElement(
379+
getContext().getTypeSizeInChars(type));
380+
381+
// Zero initialize the storage, if requested.
382+
if (zeroInitialize)
383+
emitNullInitialization(*currSrcLoc, arrayBase, type);
384+
385+
// C++ [class.temporary]p4:
386+
// There are two contexts in which temporaries are destroyed at a different
387+
// point than the end of the full-expression. The first context is when a
388+
// default constructor is called to initialize an element of an array.
389+
// If the constructor has one or more default arguments, the destruction of
390+
// every temporary created in a default argument expression is sequenced
391+
// before the construction of the next array element, if any.
392+
{
393+
assert(!cir::MissingFeatures::runCleanupsScope());
394+
395+
// Evaluate the constructor and its arguments in a regular
396+
// partial-destroy cleanup.
397+
if (getLangOpts().Exceptions &&
398+
!ctor->getParent()->hasTrivialDestructor()) {
399+
cgm.errorNYI(e->getSourceRange(), "partial array cleanups");
400+
}
401+
402+
// Emit the constructor call that will execute for every array element.
403+
auto arrayOp = builder.createPtrBitcast(arrayBase.getPointer(), arrayTy);
404+
builder.create<cir::ArrayCtor>(
405+
*currSrcLoc, arrayOp, [&](mlir::OpBuilder &b, mlir::Location loc) {
406+
auto arg = b.getInsertionBlock()->addArgument(ptrToElmType, loc);
407+
Address curAddr = Address(arg, elementType, eltAlignment);
408+
assert(!cir::MissingFeatures::sanitizers());
409+
auto currAVS = AggValueSlot::forAddr(
410+
curAddr, type.getQualifiers(), AggValueSlot::IsDestructed,
411+
AggValueSlot::IsNotAliased, AggValueSlot::DoesNotOverlap,
412+
AggValueSlot::IsNotZeroed);
413+
emitCXXConstructorCall(ctor, Ctor_Complete,
414+
/*ForVirtualBase=*/false,
415+
/*Delegating=*/false, currAVS, e);
416+
builder.create<cir::YieldOp>(loc);
417+
});
418+
}
419+
420+
if (constantCount.use_empty())
421+
constantCount.erase();
422+
}
423+
314424
void CIRGenFunction::emitDelegateCXXConstructorCall(
315425
const CXXConstructorDecl *ctor, CXXCtorType ctorType,
316426
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
@@ -1594,37 +1594,38 @@ void CIRGenFunction::emitCXXConstructExpr(const CXXConstructExpr *e,
15941594
return;
15951595
}
15961596

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

1602-
clang::CXXCtorType type = Ctor_Complete;
1603-
bool forVirtualBase = false;
1604-
bool delegating = false;
1605-
1606-
switch (e->getConstructionKind()) {
1607-
case CXXConstructionKind::Complete:
1608-
type = Ctor_Complete;
1609-
break;
1610-
case CXXConstructionKind::Delegating:
1611-
// We should be emitting a constructor; GlobalDecl will assert this
1612-
type = curGD.getCtorType();
1613-
delegating = true;
1614-
break;
1615-
case CXXConstructionKind::VirtualBase:
1616-
// This should just set 'forVirtualBase' to true and fall through, but
1617-
// virtual base class support is otherwise missing, so this needs to wait
1618-
// until it can be tested.
1619-
cgm.errorNYI(e->getSourceRange(),
1620-
"emitCXXConstructExpr: virtual base constructor");
1621-
return;
1622-
case CXXConstructionKind::NonVirtualBase:
1623-
type = Ctor_Base;
1624-
break;
1625-
}
1602+
clang::CXXCtorType type = Ctor_Complete;
1603+
bool forVirtualBase = false;
1604+
bool delegating = false;
16261605

1627-
emitCXXConstructorCall(cd, type, forVirtualBase, delegating, dest, e);
1606+
switch (e->getConstructionKind()) {
1607+
case CXXConstructionKind::Complete:
1608+
type = Ctor_Complete;
1609+
break;
1610+
case CXXConstructionKind::Delegating:
1611+
// We should be emitting a constructor; GlobalDecl will assert this
1612+
type = curGD.getCtorType();
1613+
delegating = true;
1614+
break;
1615+
case CXXConstructionKind::VirtualBase:
1616+
// This should just set 'forVirtualBase' to true and fall through, but
1617+
// virtual base class support is otherwise missing, so this needs to wait
1618+
// until it can be tested.
1619+
cgm.errorNYI(e->getSourceRange(),
1620+
"emitCXXConstructExpr: virtual base constructor");
1621+
return;
1622+
case CXXConstructionKind::NonVirtualBase:
1623+
type = Ctor_Base;
1624+
break;
1625+
}
1626+
1627+
emitCXXConstructorCall(cd, type, forVirtualBase, delegating, dest, e);
1628+
}
16281629
}
16291630

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

clang/lib/CIR/CodeGen/CIRGenFunction.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -805,4 +805,49 @@ bool CIRGenFunction::shouldNullCheckClassCastValue(const CastExpr *ce) {
805805
return true;
806806
}
807807

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

clang/lib/CIR/CodeGen/CIRGenFunction.h

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

764+
mlir::Value emitArrayLength(const clang::ArrayType *arrayType,
765+
QualType &baseType, Address &addr);
764766
LValue emitArraySubscriptExpr(const clang::ArraySubscriptExpr *e);
765767

766768
Address emitArrayToPointerDecay(const Expr *array);
@@ -837,6 +839,16 @@ class CIRGenFunction : public CIRGenTypeCache {
837839
void emitCXXConstructExpr(const clang::CXXConstructExpr *e,
838840
AggValueSlot dest);
839841

842+
void emitCXXAggrConstructorCall(const CXXConstructorDecl *ctor,
843+
const clang::ArrayType *arrayType,
844+
Address arrayBegin, const CXXConstructExpr *e,
845+
bool newPointerIsChecked,
846+
bool zeroInitialize = false);
847+
void emitCXXAggrConstructorCall(const CXXConstructorDecl *ctor,
848+
mlir::Value numElements, Address arrayBase,
849+
const CXXConstructExpr *e,
850+
bool newPointerIsChecked,
851+
bool zeroInitialize);
840852
void emitCXXConstructorCall(const clang::CXXConstructorDecl *d,
841853
clang::CXXCtorType type, bool forVirtualBase,
842854
bool delegating, AggValueSlot thisAVS,

0 commit comments

Comments
 (0)