Skip to content

Commit de1cd92

Browse files
authored
[CIR] Implement support for zero-initialized builtin type allocation (#1304)
This handles initialization of array new allocations in the simple case where the entire allocated memory block can be initialized with a memset to zero.
1 parent c4e5842 commit de1cd92

File tree

4 files changed

+125
-3
lines changed

4 files changed

+125
-3
lines changed

clang/lib/CIR/CodeGen/CIRGenBuilder.h

+4
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,10 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
535535
// Constant creation helpers
536536
// -------------------------
537537
//
538+
cir::ConstantOp getUInt8(uint8_t c, mlir::Location loc) {
539+
auto uInt8Ty = getUInt8Ty();
540+
return create<cir::ConstantOp>(loc, uInt8Ty, cir::IntAttr::get(uInt8Ty, c));
541+
}
538542
cir::ConstantOp getSInt32(int32_t c, mlir::Location loc) {
539543
auto sInt32Ty = getSInt32Ty();
540544
return create<cir::ConstantOp>(loc, sInt32Ty,

clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp

+93-2
Original file line numberDiff line numberDiff line change
@@ -876,11 +876,96 @@ void CIRGenFunction::emitNewArrayInitializer(
876876
unsigned InitListElements = 0;
877877

878878
const Expr *Init = E->getInitializer();
879+
QualType::DestructionKind DtorKind = ElementType.isDestructedType();
879880
CleanupDeactivationScope deactivation(*this);
880881

882+
CharUnits ElementSize = getContext().getTypeSizeInChars(ElementType);
883+
CharUnits ElementAlign =
884+
BeginPtr.getAlignment().alignmentOfArrayElement(ElementSize);
885+
886+
// Attempt to perform zero-initialization using memset.
887+
auto TryMemsetInitialization = [&]() -> bool {
888+
auto Loc = NumElements.getLoc();
889+
890+
// FIXME: If the type is a pointer-to-data-member under the Itanium ABI,
891+
// we can initialize with a memset to -1.
892+
if (!CGM.getTypes().isZeroInitializable(ElementType))
893+
return false;
894+
895+
// Optimization: since zero initialization will just set the memory
896+
// to all zeroes, generate a single memset to do it in one shot.
897+
898+
// Subtract out the size of any elements we've already initialized.
899+
auto RemainingSize = AllocSizeWithoutCookie;
900+
if (InitListElements) {
901+
llvm_unreachable("NYI");
902+
}
903+
904+
// Create the memset.
905+
auto CastOp =
906+
builder.createPtrBitcast(CurPtr.getPointer(), builder.getVoidTy());
907+
builder.createMemSet(Loc, CastOp, builder.getUInt8(0, Loc), RemainingSize);
908+
return true;
909+
};
910+
881911
const InitListExpr *ILE = dyn_cast<InitListExpr>(Init);
882-
if (ILE) {
883-
llvm_unreachable("NYI");
912+
const CXXParenListInitExpr *CPLIE = nullptr;
913+
const StringLiteral *SL = nullptr;
914+
const ObjCEncodeExpr *OCEE = nullptr;
915+
const Expr *IgnoreParen = nullptr;
916+
if (!ILE) {
917+
IgnoreParen = Init->IgnoreParenImpCasts();
918+
CPLIE = dyn_cast<CXXParenListInitExpr>(IgnoreParen);
919+
SL = dyn_cast<StringLiteral>(IgnoreParen);
920+
OCEE = dyn_cast<ObjCEncodeExpr>(IgnoreParen);
921+
}
922+
923+
// If the initializer is an initializer list, first do the explicit elements.
924+
if (ILE || CPLIE || SL || OCEE) {
925+
// Initializing from a (braced) string literal is a special case; the init
926+
// list element does not initialize a (single) array element.
927+
if ((ILE && ILE->isStringLiteralInit()) || SL || OCEE) {
928+
llvm_unreachable("NYI");
929+
}
930+
931+
ArrayRef<const Expr *> InitExprs =
932+
ILE ? ILE->inits() : CPLIE->getInitExprs();
933+
InitListElements = InitExprs.size();
934+
935+
// If this is a multi-dimensional array new, we will initialize multiple
936+
// elements with each init list element.
937+
QualType AllocType = E->getAllocatedType();
938+
if (const ConstantArrayType *CAT = dyn_cast_or_null<ConstantArrayType>(
939+
AllocType->getAsArrayTypeUnsafe())) {
940+
llvm_unreachable("NYI");
941+
}
942+
943+
// Enter a partial-destruction Cleanup if necessary.
944+
if (DtorKind) {
945+
llvm_unreachable("NYI");
946+
}
947+
948+
CharUnits StartAlign = CurPtr.getAlignment();
949+
for (const Expr *IE : InitExprs) {
950+
llvm_unreachable("NYI");
951+
}
952+
953+
// The remaining elements are filled with the array filler expression.
954+
Init = ILE ? ILE->getArrayFiller() : CPLIE->getArrayFiller();
955+
956+
// Extract the initializer for the individual array elements by pulling
957+
// out the array filler from all the nested initializer lists. This avoids
958+
// generating a nested loop for the initialization.
959+
while (Init && Init->getType()->isConstantArrayType()) {
960+
auto *SubILE = dyn_cast<InitListExpr>(Init);
961+
if (!SubILE)
962+
break;
963+
assert(SubILE->getNumInits() == 0 && "explicit inits in array filler?");
964+
Init = SubILE->getArrayFiller();
965+
}
966+
967+
// Switch back to initializing one base element at a time.
968+
CurPtr = CurPtr.withElementType(BeginPtr.getElementType());
884969
}
885970

886971
// If all elements have already been initialized, skip any further
@@ -911,6 +996,12 @@ void CIRGenFunction::emitNewArrayInitializer(
911996
llvm_unreachable("NYI");
912997
}
913998

999+
// If this is value-initialization, we can usually use memset.
1000+
if (isa<ImplicitValueInitExpr>(Init)) {
1001+
if (TryMemsetInitialization())
1002+
return;
1003+
llvm_unreachable("NYI");
1004+
}
9141005
llvm_unreachable("NYI");
9151006
}
9161007

clang/test/CIR/CodeGen/new.cpp

+18-1
Original file line numberDiff line numberDiff line change
@@ -143,4 +143,21 @@ void t_constant_size_nontrivial2() {
143143
// CHECK: %9 = cir.cast(bitcast, %8 : !cir.ptr<!u8i>), !cir.ptr<!ty_D>
144144
// CHECK: cir.store %9, %0 : !cir.ptr<!ty_D>, !cir.ptr<!cir.ptr<!ty_D>>
145145
// CHECK: cir.return
146-
// CHECK: }
146+
// CHECK: }
147+
148+
void t_constant_size_memset_init() {
149+
auto p = new int[16] {};
150+
}
151+
152+
// In this test, NUM_ELEMENTS isn't used because no cookie is needed and there
153+
// are no constructor calls needed.
154+
155+
// CHECK: cir.func @_Z27t_constant_size_memset_initv()
156+
// CHECK: %[[#NUM_ELEMENTS:]] = cir.const #cir.int<16> : !u64i
157+
// CHECK: %[[#ALLOCATION_SIZE:]] = cir.const #cir.int<64> : !u64i
158+
// CHECK: %[[#ALLOC_PTR:]] = cir.call @_Znam(%[[#ALLOCATION_SIZE]]) : (!u64i) -> !cir.ptr<!void>
159+
// CHECK: %[[#ELEM_PTR:]] = cir.cast(bitcast, %[[#ALLOC_PTR]] : !cir.ptr<!void>), !cir.ptr<!s32i>
160+
// CHECK: %[[#VOID_PTR:]] = cir.cast(bitcast, %[[#ELEM_PTR]] : !cir.ptr<!s32i>), !cir.ptr<!void>
161+
// CHECK: %[[#ZERO:]] = cir.const #cir.int<0> : !u8i
162+
// CHECK: %[[#ZERO_I32:]] = cir.cast(integral, %[[#ZERO]] : !u8i), !s32i
163+
// CHECK: cir.libc.memset %[[#ALLOCATION_SIZE]] bytes from %[[#VOID_PTR]] set to %[[#ZERO_I32]] : !cir.ptr<!void>, !s32i, !u64i

clang/test/CIR/Lowering/new.cpp

+10
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,13 @@ void t_constant_size_nontrivial2() {
5757
// LLVM: store i64 3, ptr %[[COOKIE_PTR]], align 8
5858
// LLVM: %[[ALLOCATED_PTR:.*]] = getelementptr i8, ptr %[[COOKIE_PTR]], i64 8
5959
// LLVM: store ptr %[[ALLOCATED_PTR]], ptr %[[ALLOCA]], align 8
60+
61+
void t_constant_size_memset_init() {
62+
auto p = new int[16] {};
63+
}
64+
65+
// LLVM: @_Z27t_constant_size_memset_initv()
66+
// LLVM: %[[ALLOCA:.*]] = alloca ptr, i64 1, align 8
67+
// LLVM: %[[ADDR:.*]] = call ptr @_Znam(i64 64)
68+
// LLVM: call void @llvm.memset.p0.i64(ptr %[[ADDR]], i8 0, i64 64, i1 false)
69+
// LLVM: store ptr %[[ADDR]], ptr %[[ALLOCA]], align 8

0 commit comments

Comments
 (0)