Index: lib/CodeGen/CGDecl.cpp =================================================================== --- lib/CodeGen/CGDecl.cpp +++ lib/CodeGen/CGDecl.cpp @@ -927,6 +927,8 @@ C->setDoesNotThrow(); } +/// This assumes that the fields of the given Record are assumed to be +/// non-writeonce by default. CodeGenFunction::OffsetsType& CodeGenFunction::ComputeInvariantOffsets(const CXXRecordDecl *Record) { ASTContext &Ctx = getContext(); @@ -951,11 +953,69 @@ uint64_t Offset = Ctx.getFieldOffset(Field); Args.push_back(llvm::ConstantInt::get(Int64Ty, Offset)); // Offset + + // If this writeonce type happens to be a record holding mutable + // fields, make sure to collect the offsets. + if (const CXXRecordDecl *RecField = + Ctx.getBaseElementType(FieldType)->getAsCXXRecordDecl()) { + if (RecField->hasMutableFields()) { + auto &FieldArgs = ComputeNonMutableOffsets(RecField); + Args.insert(Args.end(), FieldArgs.begin(), FieldArgs.end()); + } + } } else if (const CXXRecordDecl *RecField = Ctx.getBaseElementType(FieldType)->getAsCXXRecordDecl()) { auto &FieldArgs = ComputeInvariantOffsets(RecField); Args.insert(Args.end(), FieldArgs.begin(), FieldArgs.end()); } + + // Ignore non-writeonce non-record fields. + } + + return Args; +} + +/// This is similar to ComputeInvariantOffsets() but collect offsets of +/// non-mutable fields instead of those of writeonce fields. +/// It is meant to be used when the given Record is writeonce and its +/// fields are assumed to be writeonce by default. +CodeGenFunction::OffsetsType& +CodeGenFunction::ComputeNonMutableOffsets(const CXXRecordDecl *Record) { + ASTContext &Ctx = getContext(); + auto &OffsetsInfo = NonMutableOffsets.FindAndConstruct(Record).second; + OffsetsType &Args = OffsetsInfo.Offsets; + + // If this has already been computed, then return the stored value. + if (OffsetsInfo.Computed) return Args; + + // Otherwise, mark that this is computed. + OffsetsInfo.Computed = true; + assert(Args.empty() && "There should be no offset specified yet."); + + // Trace through fields collecting offsets of writeonce candidates. + for (const auto *Field : Record->fields()) { + assert(dyn_cast(Field) && "Field decls only."); + QualType FieldType = Field->getType(); + + if (Field->isMutable()) { + // Ignore mutable fields. + continue; + } else if (const CXXRecordDecl *RecField = + Ctx.getBaseElementType(FieldType)->getAsCXXRecordDecl()) { + if (RecField->hasMutableFields()) { + auto &FieldArgs = ComputeNonMutableOffsets(RecField); + Args.insert(Args.end(), FieldArgs.begin(), FieldArgs.end()); + continue; + } + } + + // This field is writeonce. + CharUnits WidthChars = Ctx.getTypeSizeInChars(FieldType); + uint64_t Width = WidthChars.getQuantity(); + Args.push_back(llvm::ConstantInt::get(Int64Ty, Width)); // Size + + uint64_t Offset = Ctx.getFieldOffset(Field); + Args.push_back(llvm::ConstantInt::get(Int64Ty, Offset)); // Offset } return Args; @@ -985,8 +1045,16 @@ } else if (Ty.isWriteOnce(Ctx)) { Args.Size = llvm::ConstantInt::get(Int64Ty, Width); Args.Addr = Builder.CreateBitCast(Addr, Int8PtrTy); + + // If this writeonce type happens to be a record holding mutable + // fields, make sure to collect the offsets. + if (const CXXRecordDecl *Record = + Ctx.getBaseElementType(Ty)->getAsCXXRecordDecl()) { + if (Record->hasMutableFields()) + Offsets = ComputeNonMutableOffsets(Record); + } } else if (const CXXRecordDecl *Record = - Ctx.getBaseElementType(Ty)->getAsCXXRecordDecl()) { + Ctx.getBaseElementType(Ty)->getAsCXXRecordDecl()) { Offsets = ComputeInvariantOffsets(Record); // If there are invariant offsets in this non-writeonce record, // then emit the intrinsic call with the offsets. Otherwise, Index: lib/CodeGen/CodeGenFunction.h =================================================================== --- lib/CodeGen/CodeGenFunction.h +++ lib/CodeGen/CodeGenFunction.h @@ -1929,12 +1929,20 @@ OffsetsInfoType() : Computed(false) { } }; - /// \brief A collection of invariant offsets per given record. + /// \brief A collection of invariant offsets per given record, + /// for non-const objects. llvm::DenseMap InvariantOffsets; + /// \brief A collection of non-mutable offsets per given record, + /// for const objects. + llvm::DenseMap NonMutableOffsets; + /// \brief Compute the invariant offsets of a given Record. OffsetsType& ComputeInvariantOffsets(const CXXRecordDecl *Record); + /// \brief Compute the non-mutable offsets of a given Record. + OffsetsType& ComputeNonMutableOffsets(const CXXRecordDecl *Record); + public: InvariantArgs EmitInvariantStart(const VarDecl &D, llvm::Value *Addr, bool IsGlobalConstant = true); Index: test/CodeGenCXX/const-invariant.cpp =================================================================== --- test/CodeGenCXX/const-invariant.cpp +++ test/CodeGenCXX/const-invariant.cpp @@ -165,7 +165,7 @@ // CHECK-NL-CO-OBJ: call void @_ZN1DC{{[0-9]+}}Ei({{.*}}* @_ZL3i_d, {{.*}}) // CHECK-NL-CO-OBJ: call {{.*}}@llvm.invariant.start(i64 {{[0-9]+}}, i8* bitcast ({{.*}} @_ZL3i_d to i8*)) // CHECK-NL-CO-OBJ: call void @_ZN1MC{{[0-9]+}}Ei({{.*}}* @_ZL3i_m, {{.*}}) -// CHECK-NL-CO-OBJ: call {{.*}}@llvm.invariant.start(i64 {{[0-9]+}}, i8* bitcast ({{.*}} @_ZL3i_m to i8*)) +// CHECK-NL-CO-OBJ: call {{.*}}@llvm.invariant.start(i64 {{[0-9]+}}, i8* bitcast ({{.*}} @_ZL3i_m to i8*), // CHECK-NL-CO-OBJ: call void @_ZN1FC{{[0-9]+}}Ei({{.*}}* @_ZL3i_f, {{.*}}) // CHECK-NL-CO-OBJ-NOT: call {{.*}}@llvm.invariant.start(i64 {{[0-9]+}}, i8* bitcast ({{.*}} @_ZL3i_f to i8*)) // CHECK-NL-CO-OBJ: call void @_ZN1IC{{[0-9]+}}Ei({{.*}}* @_ZL3i_i, {{.*}})