-
Notifications
You must be signed in to change notification settings - Fork 14.6k
[CIR] Add support for array constructors #149142
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
bff8d99
a91a7c9
7eaff32
d6b005d
0dbd46a
e191c51
903e2c0
f1ac289
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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,116 @@ 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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it possible to add a test for the zero case? |
||
return; | ||
} else { | ||
// Otherwise, emit the check. | ||
cgm.errorNYI(e->getSourceRange(), "dynamic-length array expression"); | ||
} | ||
|
||
auto arrayTy = mlir::cast<cir::ArrayType>(arrayBase.getElementType()); | ||
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. | ||
mlir::Value arrayOp = | ||
builder.createPtrBitcast(arrayBase.getPointer(), arrayTy); | ||
builder.create<cir::ArrayCtor>( | ||
*currSrcLoc, arrayOp, [&](mlir::OpBuilder &b, mlir::Location loc) { | ||
mlir::BlockArgument 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) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -808,4 +808,48 @@ 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)) { | ||
assert(cir::MissingFeatures::vlas()); | ||
cgm.errorNYI(*currSrcLoc, "VLAs"); | ||
return builder.getConstInt(*currSrcLoc, SizeTy, 0); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unrelated to this change, but we seem to have a coding standard inconsistency with SizeTy. |
||
} | ||
|
||
uint64_t countFromCLAs = 1; | ||
QualType eltType; | ||
|
||
auto cirArrayType = mlir::dyn_cast<cir::ArrayType>(addr.getElementType()); | ||
|
||
while (cirArrayType) { | ||
assert(isa<ConstantArrayType>(arrayType)); | ||
countFromCLAs *= cirArrayType.getSize(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe this is handling a multi-dimensional array. Can you add a test for that? |
||
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; | ||
return builder.getConstInt(*currSrcLoc, SizeTy, countFromCLAs); | ||
} | ||
|
||
} // namespace clang::CIRGen |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add example here please.