From 2cec820e314fcefb229f1767062a99bec52921a9 Mon Sep 17 00:00:00 2001 From: Shoaib Meenai Date: Mon, 25 Nov 2024 20:27:58 -0800 Subject: [PATCH] [CIR][CIRGen] Support copy constructors with non-record arrays (#1165) If a record type contains an array of non-record types, we can generate a copy for it inside the copy constructor, as CodeGen does. CodeGen does so for arrays of record types where applicable as well, but we'll want to represent the construction of those explicitly, as outlined in https://github.com/llvm/clangir/issues/1055. --- clang/lib/CIR/CodeGen/CIRGenClass.cpp | 25 ++++++++++++++- clang/test/CIR/CodeGen/copy-constructor.cpp | 35 +++++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 clang/test/CIR/CodeGen/copy-constructor.cpp diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp b/clang/lib/CIR/CodeGen/CIRGenClass.cpp index c54d06c1f9d1..7e62a9fcaf10 100644 --- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp @@ -252,7 +252,30 @@ static void emitMemberInitializer(CIRGenFunction &CGF, CGF.getContext().getAsConstantArrayType(FieldType); if (Array && Constructor->isDefaulted() && Constructor->isCopyOrMoveConstructor()) { - llvm_unreachable("NYI"); + QualType baseElementTy = CGF.getContext().getBaseElementType(Array); + // NOTE(cir): CodeGen allows record types to be memcpy'd if applicable, + // whereas ClangIR wants to represent all object construction explicitly. + if (!baseElementTy->isRecordType()) { + unsigned srcArgIndex = + CGF.CGM.getCXXABI().getSrcArgforCopyCtor(Constructor, Args); + cir::LoadOp srcPtr = CGF.getBuilder().createLoad( + CGF.getLoc(MemberInit->getSourceLocation()), + CGF.GetAddrOfLocalVar(Args[srcArgIndex])); + LValue thisRhslv = CGF.MakeNaturalAlignAddrLValue(srcPtr, RecordTy); + LValue src = CGF.emitLValueForFieldInitialization(thisRhslv, Field, + Field->getName()); + + // Copy the aggregate. + CGF.emitAggregateCopy(LHS, src, FieldType, + CGF.getOverlapForFieldInit(Field), + LHS.isVolatileQualified()); + // Ensure that we destroy the objects if an exception is thrown later in + // the constructor. + QualType::DestructionKind dtorKind = FieldType.isDestructedType(); + assert(!CGF.needsEHCleanup(dtorKind) && + "Arrays of non-record types shouldn't need EH cleanup"); + return; + } } CGF.emitInitializerForField(Field, LHS, MemberInit->getInit()); diff --git a/clang/test/CIR/CodeGen/copy-constructor.cpp b/clang/test/CIR/CodeGen/copy-constructor.cpp new file mode 100644 index 000000000000..92e0887b02ef --- /dev/null +++ b/clang/test/CIR/CodeGen/copy-constructor.cpp @@ -0,0 +1,35 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.ll +// RUN: FileCheck --check-prefix=LLVM --input-file=%t.ll %s + +struct HasScalarArrayMember { + int arr[2][2]; + HasScalarArrayMember(const HasScalarArrayMember &); +}; + +// CIR-LABEL: cir.func @_ZN20HasScalarArrayMemberC2ERKS_( +// CIR-NEXT: %[[#THIS:]] = cir.alloca !cir.ptr +// CIR-NEXT: %[[#OTHER:]] = cir.alloca !cir.ptr +// CIR-NEXT: cir.store %arg0, %[[#THIS]] +// CIR-NEXT: cir.store %arg1, %[[#OTHER]] +// CIR-NEXT: %[[#THIS_LOAD:]] = cir.load %[[#THIS]] +// CIR-NEXT: %[[#THIS_ARR:]] = cir.get_member %[[#THIS_LOAD]][0] {name = "arr"} +// CIR-NEXT: %[[#OTHER_LOAD:]] = cir.load %[[#OTHER]] +// CIR-NEXT: %[[#OTHER_ARR:]] = cir.get_member %[[#OTHER_LOAD]][0] {name = "arr"} +// CIR-NEXT: cir.copy %[[#OTHER_ARR]] to %[[#THIS_ARR]] : !cir.ptr x 2>> +// CIR-NEXT: cir.return + +// LLVM-LABEL: define {{.*}} @_ZN20HasScalarArrayMemberC2ERKS_( +// LLVM-SAME: ptr %[[#ARG0:]], ptr %[[#ARG1:]]) +// LLVM-NEXT: %[[#THIS:]] = alloca ptr +// LLVM-NEXT: %[[#OTHER:]] = alloca ptr +// LLVM-NEXT: store ptr %[[#ARG0]], ptr %[[#THIS]] +// LLVM-NEXT: store ptr %[[#ARG1]], ptr %[[#OTHER]] +// LLVM-NEXT: %[[#THIS_LOAD:]] = load ptr, ptr %[[#THIS]] +// LLVM-NEXT: %[[#THIS_ARR:]] = getelementptr %struct.HasScalarArrayMember, ptr %[[#THIS_LOAD]], i32 0, i32 0 +// LLVM-NEXT: %[[#OTHER_LOAD:]] = load ptr, ptr %[[#OTHER]] +// LLVM-NEXT: %[[#OTHER_ARR:]] = getelementptr %struct.HasScalarArrayMember, ptr %[[#OTHER_LOAD]], i32 0, i32 0 +// LLVM-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr %[[#THIS_ARR]], ptr %[[#OTHER_ARR]], i32 16, i1 false) +// LLVM-NEXT: ret void +HasScalarArrayMember::HasScalarArrayMember(const HasScalarArrayMember &) = default;