From d497eb9f6cc4ca211acd70a7d571f448e2a897a4 Mon Sep 17 00:00:00 2001 From: Shoaib Meenai Date: Mon, 2 Dec 2024 17:34:08 -0800 Subject: [PATCH] Update [ghstack-poisoned] --- clang/include/clang/CIR/MissingFeatures.h | 1 + clang/lib/CIR/CodeGen/CIRGenClass.cpp | 59 +++++++++++--- clang/test/CIR/CodeGen/assign-operator.cpp | 92 ++++++++++++++++++++++ 3 files changed, 139 insertions(+), 13 deletions(-) diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index 886fc2426048..c248f17e0263 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -174,6 +174,7 @@ struct MissingFeatures { // ABIInfo queries. static bool useTargetLoweringABIInfo() { return false; } + static bool isEmptyFieldForLayout() { return false; } // Misc static bool cacheRecordLayouts() { return false; } diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp b/clang/lib/CIR/CodeGen/CIRGenClass.cpp index 7e62a9fcaf10..4389bbad0f1c 100644 --- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp @@ -98,15 +98,13 @@ class CopyingValueRepresentation { class FieldMemcpyizer { public: - FieldMemcpyizer(CIRGenFunction &CGF, const CXXRecordDecl *ClassDecl, + FieldMemcpyizer(CIRGenFunction &CGF, const CXXMethodDecl *MethodDecl, const VarDecl *SrcRec) - : CGF(CGF), ClassDecl(ClassDecl), - // SrcRec(SrcRec), + : CGF(CGF), MethodDecl(MethodDecl), ClassDecl(MethodDecl->getParent()), + SrcRec(SrcRec), RecLayout(CGF.getContext().getASTRecordLayout(ClassDecl)), FirstField(nullptr), LastField(nullptr), FirstFieldOffset(0), - LastFieldOffset(0), LastAddedFieldIndex(0) { - (void)SrcRec; - } + LastFieldOffset(0), LastAddedFieldIndex(0) {} bool isMemcpyableField(FieldDecl *F) const { // Never memcpy fields when we are adding poised paddings. @@ -115,11 +113,11 @@ class FieldMemcpyizer { Qualifiers Qual = F->getType().getQualifiers(); if (Qual.hasVolatile() || Qual.hasObjCLifetime()) return false; - return true; } void addMemcpyableField(FieldDecl *F) { + assert(!cir::MissingFeatures::isEmptyFieldForLayout()); if (F->isZeroSize(CGF.getContext())) return; if (!FirstField) @@ -148,18 +146,54 @@ class FieldMemcpyizer { return; } - llvm_unreachable("NYI"); + uint64_t firstByteOffset; + if (FirstField->isBitField()) { + const CIRGenRecordLayout &rl = + CGF.getTypes().getCIRGenRecordLayout(FirstField->getParent()); + const CIRGenBitFieldInfo &bfInfo = rl.getBitFieldInfo(FirstField); + // FirstFieldOffset is not appropriate for bitfields, + // we need to use the storage offset instead. + firstByteOffset = CGF.getContext().toBits(bfInfo.StorageOffset); + } else { + firstByteOffset = FirstFieldOffset; + } + + CharUnits memcpySize = getMemcpySize(firstByteOffset); + QualType recordTy = CGF.getContext().getTypeDeclType(ClassDecl); + Address thisPtr = CGF.LoadCXXThisAddress(); + LValue destLv = CGF.makeAddrLValue(thisPtr, recordTy); + LValue dest = CGF.emitLValueForFieldInitialization(destLv, FirstField, + FirstField->getName()); + cir::LoadOp srcPtr = CGF.getBuilder().createLoad( + CGF.getLoc(MethodDecl->getLocation()), CGF.GetAddrOfLocalVar(SrcRec)); + LValue srcLv = CGF.MakeNaturalAlignAddrLValue(srcPtr, recordTy); + LValue src = CGF.emitLValueForFieldInitialization(srcLv, FirstField, + FirstField->getName()); + + emitMemcpyIR(dest.isBitField() ? dest.getBitFieldAddress() + : dest.getAddress(), + src.isBitField() ? src.getBitFieldAddress() : src.getAddress(), + memcpySize); + reset(); } void reset() { FirstField = nullptr; } protected: CIRGenFunction &CGF; + const CXXMethodDecl *MethodDecl; const CXXRecordDecl *ClassDecl; private: void emitMemcpyIR(Address DestPtr, Address SrcPtr, CharUnits Size) { - llvm_unreachable("NYI"); + mlir::Location loc = CGF.getLoc(MethodDecl->getLocation()); + cir::ConstantOp sizeOp = + CGF.getBuilder().getConstInt(loc, CGF.SizeTy, Size.getQuantity()); + mlir::Value dest = + CGF.getBuilder().createBitcast(DestPtr.getPointer(), CGF.VoidPtrTy); + mlir::Value src = + CGF.getBuilder().createBitcast(SrcPtr.getPointer(), CGF.VoidPtrTy); + CGF.getBuilder().createMemCpy(loc, dest, src, sizeOp); } void addInitialField(FieldDecl *F) { @@ -192,7 +226,7 @@ class FieldMemcpyizer { } } - // const VarDecl *SrcRec; + const VarDecl *SrcRec; const ASTRecordLayout &RecLayout; FieldDecl *FirstField; FieldDecl *LastField; @@ -307,8 +341,7 @@ class ConstructorMemcpyizer : public FieldMemcpyizer { public: ConstructorMemcpyizer(CIRGenFunction &CGF, const CXXConstructorDecl *CD, FunctionArgList &Args) - : FieldMemcpyizer(CGF, CD->getParent(), - getTrivialCopySource(CGF, CD, Args)), + : FieldMemcpyizer(CGF, CD, getTrivialCopySource(CGF, CD, Args)), ConstructorDecl(CD), MemcpyableCtor(CD->isDefaulted() && CD->isCopyOrMoveConstructor() && CGF.getLangOpts().getGC() == LangOptions::NonGC), @@ -446,7 +479,7 @@ class AssignmentMemcpyizer : public FieldMemcpyizer { public: AssignmentMemcpyizer(CIRGenFunction &CGF, const CXXMethodDecl *AD, FunctionArgList &Args) - : FieldMemcpyizer(CGF, AD->getParent(), Args[Args.size() - 1]), + : FieldMemcpyizer(CGF, AD, Args[Args.size() - 1]), AssignmentsMemcpyable(CGF.getLangOpts().getGC() == LangOptions::NonGC) { assert(Args.size() == 2); } diff --git a/clang/test/CIR/CodeGen/assign-operator.cpp b/clang/test/CIR/CodeGen/assign-operator.cpp index 63fc25c5817f..55118f222c7f 100644 --- a/clang/test/CIR/CodeGen/assign-operator.cpp +++ b/clang/test/CIR/CodeGen/assign-operator.cpp @@ -99,3 +99,95 @@ int main() { // CHECK: %2 = cir.load %0 : !cir.ptr, !s32i // CHECK: cir.return %2 : !s32i // CHECK: } + +struct HasNonTrivialAssignOp { + HasNonTrivialAssignOp &operator=(const HasNonTrivialAssignOp &); +}; + +struct ContainsNonTrivial { + HasNonTrivialAssignOp start; + int i; + int *j; + HasNonTrivialAssignOp middle; + int k : 4; + int l : 4; + int m : 4; + HasNonTrivialAssignOp end; + ContainsNonTrivial &operator=(const ContainsNonTrivial &); +}; + +// CHECK-LABEL: cir.func @_ZN18ContainsNonTrivialaSERKS_( +// CHECK-NEXT: %[[#THIS:]] = cir.alloca !cir.ptr +// CHECK-NEXT: %[[#OTHER:]] = cir.alloca !cir.ptr +// CHECK-NEXT: %[[#RETVAL:]] = cir.alloca !cir.ptr +// CHECK-NEXT: cir.store %arg0, %[[#THIS]] +// CHECK-NEXT: cir.store %arg1, %[[#OTHER]] +// CHECK-NEXT: %[[#THIS_LOAD:]] = cir.load deref %[[#THIS]] +// CHECK-NEXT: %[[#THIS_START:]] = cir.get_member %[[#THIS_LOAD]][0] {name = "start"} +// CHECK-NEXT: %[[#OTHER_LOAD:]] = cir.load %[[#OTHER]] +// CHECK-NEXT: %[[#OTHER_START:]] = cir.get_member %[[#OTHER_LOAD]][0] {name = "start"} +// CHECK-NEXT: cir.call @_ZN21HasNonTrivialAssignOpaSERKS_(%[[#THIS_START]], %[[#OTHER_START]]) +// CHECK-NEXT: %[[#THIS_I:]] = cir.get_member %[[#THIS_LOAD]][2] {name = "i"} +// CHECK-NEXT: %[[#OTHER_LOAD:]] = cir.load %[[#OTHER]] +// CHECK-NEXT: %[[#OTHER_I:]] = cir.get_member %[[#OTHER_LOAD]][2] {name = "i"} +// CHECK-NEXT: %[[#MEMCPY_SIZE:]] = cir.const #cir.int<12> : !u64i +// CHECK-NEXT: %[[#THIS_I_CAST:]] = cir.cast(bitcast, %[[#THIS_I]] : !cir.ptr), !cir.ptr +// CHECK-NEXT: %[[#OTHER_I_CAST:]] = cir.cast(bitcast, %[[#OTHER_I]] : !cir.ptr), !cir.ptr +// CHECK-NEXT: cir.libc.memcpy %[[#MEMCPY_SIZE]] bytes from %[[#OTHER_I_CAST]] to %[[#THIS_I_CAST]] +// CHECK-NEXT: %[[#THIS_MIDDLE:]] = cir.get_member %[[#THIS_LOAD]][4] {name = "middle"} +// CHECK-NEXT: %[[#OTHER_LOAD:]] = cir.load %[[#OTHER]] +// CHECK-NEXT: %[[#OTHER_MIDDLE:]] = cir.get_member %[[#OTHER_LOAD]][4] {name = "middle"} +// CHECK-NEXT: cir.call @_ZN21HasNonTrivialAssignOpaSERKS_(%[[#THIS_MIDDLE]], %[[#OTHER_MIDDLE]]) +// CHECK-NEXT: %[[#THIS_K:]] = cir.get_member %[[#THIS_LOAD]][5] {name = "k"} +// CHECK-NEXT: %[[#OTHER_LOAD:]] = cir.load %[[#OTHER]] +// CHECK-NEXT: %[[#OTHER_K:]] = cir.get_member %[[#OTHER_LOAD]][5] {name = "k"} +// CHECK-NEXT: %[[#MEMCPY_SIZE:]] = cir.const #cir.int<2> : !u64i +// CHECK-NEXT: %[[#THIS_K_CAST:]] = cir.cast(bitcast, %[[#THIS_K]] : !cir.ptr), !cir.ptr +// CHECK-NEXT: %[[#OTHER_K_CAST:]] = cir.cast(bitcast, %[[#OTHER_K]] : !cir.ptr), !cir.ptr +// CHECK-NEXT: cir.libc.memcpy %[[#MEMCPY_SIZE]] bytes from %[[#OTHER_K_CAST]] to %[[#THIS_K_CAST]] +// CHECK-NEXT: %[[#THIS_END:]] = cir.get_member %[[#THIS_LOAD]][6] {name = "end"} +// CHECK-NEXT: %[[#OTHER_LOAD:]] = cir.load %[[#OTHER]] +// CHECK-NEXT: %[[#OTHER_END:]] = cir.get_member %[[#OTHER_LOAD]][6] {name = "end"} +// CHECK-NEXT: cir.call @_ZN21HasNonTrivialAssignOpaSERKS_(%[[#THIS_END]], %[[#OTHER_END]]) +// CHECK-NEXT: cir.store %[[#THIS_LOAD]], %[[#RETVAL]] +// CHECK-NEXT: %[[#RETVAL_LOAD:]] = cir.load %[[#RETVAL]] +// CHECK-NEXT: cir.return %[[#RETVAL_LOAD]] +// CHECK-NEXT: } +ContainsNonTrivial & +ContainsNonTrivial::operator=(const ContainsNonTrivial &) = default; + +struct Trivial { + int i; + int *j; + double k; + int l[3]; +}; + +// CHECK-LABEL: cir.func linkonce_odr @_ZN7TrivialaSERKS_( +// CHECK-NEXT: %[[#THIS:]] = cir.alloca !cir.ptr +// CHECK-NEXT: %[[#OTHER:]] = cir.alloca !cir.ptr +// CHECK-NEXT: %[[#RETVAL:]] = cir.alloca !cir.ptr +// CHECK-NEXT: cir.store %arg0, %[[#THIS]] +// CHECK-NEXT: cir.store %arg1, %[[#OTHER]] +// CHECK-NEXT: %[[#THIS_LOAD:]] = cir.load deref %[[#THIS]] +// CHECK-NEXT: %[[#THIS_I:]] = cir.get_member %[[#THIS_LOAD]][0] {name = "i"} +// CHECK-NEXT: %[[#OTHER_LOAD:]] = cir.load %[[#OTHER]] +// CHECK-NEXT: %[[#OTHER_I:]] = cir.get_member %[[#OTHER_LOAD]][0] {name = "i"} +// Note that tail padding bytes are not included. +// CHECK-NEXT: %[[#MEMCPY_SIZE:]] = cir.const #cir.int<36> : !u64i +// CHECK-NEXT: %[[#THIS_I_CAST:]] = cir.cast(bitcast, %[[#THIS_I]] : !cir.ptr), !cir.ptr +// CHECK-NEXT: %[[#OTHER_I_CAST:]] = cir.cast(bitcast, %[[#OTHER_I]] : !cir.ptr), !cir.ptr +// CHECK-NEXT: cir.libc.memcpy %[[#MEMCPY_SIZE]] bytes from %[[#OTHER_I_CAST]] to %[[#THIS_I_CAST]] +// CHECK-NEXT: cir.store %[[#THIS_LOAD]], %[[#RETVAL]] +// CHECK-NEXT: cir.br ^bb1 +// CHECK-NEXT: ^bb1: +// CHECK-NEXT: %[[#RETVAL_LOAD:]] = cir.load %[[#RETVAL]] +// CHECK-NEXT: cir.return %[[#RETVAL_LOAD]] +// CHECK-NEXT: } + +// We should explicitly call operator= even for trivial types. +// CHECK-LABEL: cir.func @_Z11copyTrivialR7TrivialS0_( +// CHECK: cir.call @_ZN7TrivialaSERKS_( +void copyTrivial(Trivial &a, Trivial &b) { + a = b; +}