Index: include/clang/AST/DeclCXX.h =================================================================== --- include/clang/AST/DeclCXX.h +++ include/clang/AST/DeclCXX.h @@ -357,6 +357,14 @@ /// \brief True when there are private non-static data members. bool HasPublicFields : 1; + /// \brief True is this class has already been checked as candidate + /// for writeonce semantics. + bool IsWriteOnceChecked : 1; + + /// \brief True if instances of this class are candidates + /// for writeonce semantics. + bool IsWriteOnceCandidate : 1; + /// \brief True if this class (or any subobject) has mutable fields. bool HasMutableFields : 1; @@ -766,6 +774,8 @@ return method_iterator(decls_end()); } + bool computeWriteOnceCandidacy() const; + /// Iterator access to constructor members. typedef specific_decl_iterator ctor_iterator; typedef llvm::iterator_range> Index: include/clang/AST/Type.h =================================================================== --- include/clang/AST/Type.h +++ include/clang/AST/Type.h @@ -709,6 +709,9 @@ return QualType::isConstant(*this, Ctx); } + /// \brief Determine if this satisfies 'writeonce' properties. + bool isWriteOnce(ASTContext &Ctx); + /// \brief Determine whether this is a Plain Old Data (POD) type (C++ 3.9p10). bool isPODType(ASTContext &Context) const; Index: lib/AST/DeclCXX.cpp =================================================================== --- lib/AST/DeclCXX.cpp +++ lib/AST/DeclCXX.cpp @@ -46,34 +46,53 @@ } CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D) - : UserDeclaredConstructor(false), UserDeclaredSpecialMembers(0), - Aggregate(true), PlainOldData(true), Empty(true), Polymorphic(false), - Abstract(false), IsStandardLayout(true), HasNoNonEmptyBases(true), - HasPrivateFields(false), HasProtectedFields(false), HasPublicFields(false), - HasMutableFields(false), HasVariantMembers(false), HasOnlyCMembers(true), - HasInClassInitializer(false), HasUninitializedReferenceMember(false), - NeedOverloadResolutionForMoveConstructor(false), - NeedOverloadResolutionForMoveAssignment(false), - NeedOverloadResolutionForDestructor(false), - DefaultedMoveConstructorIsDeleted(false), - DefaultedMoveAssignmentIsDeleted(false), - DefaultedDestructorIsDeleted(false), - HasTrivialSpecialMembers(SMF_All), - DeclaredNonTrivialSpecialMembers(0), - HasIrrelevantDestructor(true), - HasConstexprNonCopyMoveConstructor(false), - DefaultedDefaultConstructorIsConstexpr(true), - HasConstexprDefaultConstructor(false), - HasNonLiteralTypeFieldsOrBases(false), ComputedVisibleConversions(false), - UserProvidedDefaultConstructor(false), DeclaredSpecialMembers(0), - ImplicitCopyConstructorHasConstParam(true), - ImplicitCopyAssignmentHasConstParam(true), - HasDeclaredCopyConstructorWithConstParam(false), - HasDeclaredCopyAssignmentWithConstParam(false), - IsLambda(false), IsParsingBaseSpecifiers(false), NumBases(0), NumVBases(0), - Bases(), VBases(), - Definition(D), FirstFriend() { -} + : UserDeclaredConstructor(false), + UserDeclaredSpecialMembers(0), + Aggregate(true), + PlainOldData(true), + Empty(true), + Polymorphic(false), + Abstract(false), + IsStandardLayout(true), + HasNoNonEmptyBases(true), + HasPrivateFields(false), + HasProtectedFields(false), + HasPublicFields(false), + IsWriteOnceChecked(false), + IsWriteOnceCandidate(false), + HasMutableFields(false), + HasVariantMembers(false), + HasOnlyCMembers(true), + HasInClassInitializer(false), + HasUninitializedReferenceMember(false), + NeedOverloadResolutionForMoveConstructor(false), + NeedOverloadResolutionForMoveAssignment(false), + NeedOverloadResolutionForDestructor(false), + DefaultedMoveConstructorIsDeleted(false), + DefaultedMoveAssignmentIsDeleted(false), + DefaultedDestructorIsDeleted(false), + HasTrivialSpecialMembers(SMF_All), + DeclaredNonTrivialSpecialMembers(0), + HasIrrelevantDestructor(true), + HasConstexprNonCopyMoveConstructor(false), + DefaultedDefaultConstructorIsConstexpr(true), + HasConstexprDefaultConstructor(false), + HasNonLiteralTypeFieldsOrBases(false), + ComputedVisibleConversions(false), + UserProvidedDefaultConstructor(false), + DeclaredSpecialMembers(0), + ImplicitCopyConstructorHasConstParam(true), + ImplicitCopyAssignmentHasConstParam(true), + HasDeclaredCopyConstructorWithConstParam(false), + HasDeclaredCopyAssignmentWithConstParam(false), + IsLambda(false), + IsParsingBaseSpecifiers(false), + NumBases(0), + NumVBases(0), + Bases(), + VBases(), + Definition(D), + FirstFriend() {} CXXBaseSpecifier *CXXRecordDecl::DefinitionData::getBasesSlowCase() const { return Bases.get(Definition->getASTContext().getExternalSource()); @@ -1331,6 +1350,73 @@ return false; } +/// \brief A class is a candidate for 'writeonce' semantics if +/// none of its methods writes to memory. +bool CXXRecordDecl::computeWriteOnceCandidacy() const { + // The class must be defined and concrete. + if (!isCompleteDefinition() || isDependentType()) return false; + + // If this has already been checked, then return the stored value. + if (data().IsWriteOnceChecked) return data().IsWriteOnceCandidate; + + // Otherwise, mark that this is checked. + data().IsWriteOnceChecked = true; + assert(!data().IsWriteOnceCandidate && + "The candidacy should not have been set yet."); + + // Check each method, excluding constructors and destructors. + // NOTE: The invariant intrinsic calls are generated right after the + // construction of a given const object (which must be initialized) + // and right before its destruction at the end of its lifetime. + // Both constructors and destructors modify the allocated memory and + // and will not be called in the middle of an invariant_start/end + // pair, thus may not affect the reduction of loads. So, it does not + // help to handle constructors here as well. + for (auto *M : methods()) { + if (isa(M) || isa(M)) continue; + + // If we already know that this method does not write to memory, skip it. + // NOTE: CodeGenModule::ConstructAttributeList() marks declarations with + // ConstAttr and PureAttr attributes, respectively, as 'readnone' and + // 'readonly' (for LLVM). + if (M->hasAttr() || M->hasAttr()) continue; + + const FunctionDecl *Def = nullptr; + bool IsDefined = M->isDefined(Def); + + // If this is not defined and not virtual, then it may write to memory. + // So it's not a candidate for 'writeonce' semantics. + // If it is purely virtual then it must be overriden and its overriding + // method (in this context) must be a candidate for 'writeonce' semantics. + // Skip it. + if (!IsDefined) { + if (M->isPure()) { + assert(M->isVirtual() && "Not defined and pure implies virtual."); + continue; + } + return false; + } + + // If this is trivial, skip it. + if (Def->hasTrivialBody()) continue; + + // TODO: Any other case? + } + + // Check bases + for (const auto &I : bases()) { + const RecordType *Ty = I.getType()->getAs(); + assert(Ty && "No type?"); + CXXRecordDecl *Base = + cast_or_null(Ty->getDecl()->getDefinition()); + + if (!Base || !Base->computeWriteOnceCandidacy()) return false; + } + + data().IsWriteOnceCandidate = true; + return true; +} + void CXXRecordDecl::completeDefinition() { completeDefinition(nullptr); } Index: lib/AST/Type.cpp =================================================================== --- lib/AST/Type.cpp +++ lib/AST/Type.cpp @@ -1936,6 +1936,18 @@ } } +bool QualType::isWriteOnce(ASTContext &Context) { + // TODO: Include C objects as well? + if (Context.getLangOpts().CPlusPlus && isConstant(Context) && + !getTypePtr()->isReferenceType()) { + if (const CXXRecordDecl *Record = + Context.getBaseElementType(*this)->getAsCXXRecordDecl()) + return Record->computeWriteOnceCandidacy(); + return true; + } + return false; +} + bool QualType::isPODType(ASTContext &Context) const { // C++11 has a more relaxed definition of POD. if (Context.getLangOpts().CPlusPlus11) Index: lib/CodeGen/CGDecl.cpp =================================================================== --- lib/CodeGen/CGDecl.cpp +++ lib/CodeGen/CGDecl.cpp @@ -366,6 +366,8 @@ if (D.getInit()) var = AddInitializerToStaticVarDecl(D, var); + MarkWriteOnceWrittenRAII MWO(*this, &D, var); + var->setAlignment(alignment.getQuantity()); if (D.hasAttr()) @@ -525,6 +527,20 @@ CGF.EmitLifetimeEnd(Size, Addr); } }; + + /// A cleanup to call @llvm.invariant.end. + class CallInvariantEnd final : public EHScopeStack::Cleanup { + llvm::CallInst *StartInst; + llvm::Value *Addr; + llvm::Value *Size; // TODO: Is this even necessary? + public: + CallInvariantEnd(llvm::CallInst *C, llvm::Value *addr, llvm::Value *size) + : StartInst(C), Addr(addr), Size(size) {} + + void Emit(CodeGenFunction &CGF, Flags flags) override { + CGF.EmitInvariantEnd(StartInst, Size, Addr); + } + }; } /// EmitAutoVarWithLifetime - Does the setup required for an automatic @@ -840,12 +856,50 @@ canEmitInitWithFewStoresAfterMemset(Init, StoreBudget); } +/// Emit the code necessary to initialize the given global variable. +/// TODO: Use Address for Addr (to match lifetime intrisics). +MarkWriteOnceWrittenRAII::MarkWriteOnceWrittenRAII(CodeGenFunction &CGF, + const VarDecl *D, + llvm::GlobalVariable *GVAddr) + : CGF(CGF), GV(GVAddr), AI(nullptr) { + assert(D && "Invalid declaration."); + + llvm::Value *AddrPtr = nullptr; + if (GVAddr) { + assert(GV && GV == GVAddr && "Caller provides address for globals"); + AddrPtr = GVAddr; + } else { + CodeGen::Address Addr = CGF.GetAddrOfLocalVar(D); + AddrPtr = Addr.getPointer()->stripPointerCasts(); + AI = dyn_cast_or_null(AddrPtr); + } + + // Only GlobalVariable's and AllocaInst's can be writeonce. + // Exit if the given address is none of these. + if (!GV && !AI) return; + + // If the address is writeonce, then emit @llvm.invariant.start() intrinsic. + // Then, for non-global variables, push the emission of the + // @llvm.invariant.end() intrinsic onto the cleanup stack. + if ((AI || !GV->isConstant()) && D->getType().isWriteOnce(CGF.getContext())) + Args = CGF.EmitInvariantStart(*D, AddrPtr); +} + +MarkWriteOnceWrittenRAII::~MarkWriteOnceWrittenRAII() { + if (AI && Args.StartInst) { + assert(!GV && "Can't have it both ways!"); + CGF.EHStack.pushCleanup(NormalCleanup, Args.StartInst, + Args.Addr, Args.Size); + } +} + /// EmitAutoVarDecl - Emit code and set up an entry in LocalDeclMap for a /// variable declaration with auto, register, or no storage class specifier. /// These turn into simple stack objects, or GlobalValues depending on target. void CodeGenFunction::EmitAutoVarDecl(const VarDecl &D) { AutoVarEmission emission = EmitAutoVarAlloca(D); EmitAutoVarInit(emission); + MarkWriteOnceWrittenRAII MWO(*this, &D); EmitAutoVarCleanups(emission); } @@ -878,6 +932,47 @@ C->setDoesNotThrow(); } +/// Emit code to cause the variable at the given address to be considered as +/// constant from this point onwards. +CodeGenModule::InvariantArgs CodeGenFunction::EmitInvariantStart( + const VarDecl &D, llvm::Value *Addr) { + // Don't emit the intrinsic if we're not optimizing. + if (!CGM.getCodeGenOpts().OptimizationLevel) + return CodeGenModule::InvariantArgs{}; + + // Grab the llvm.invariant.start intrinsic. + llvm::Intrinsic::ID InvStartID = llvm::Intrinsic::invariant_start; + llvm::Constant *InvariantStart = CGM.getIntrinsic(InvStartID); + + // Emit a call with the size in bytes of the object. + CharUnits WidthChars = getContext().getTypeSizeInChars(D.getType()); + uint64_t Width = WidthChars.getQuantity(); + + llvm::Value *Size; + if (llvm::Constant *CAddr = dyn_cast(Addr)) { + Size = llvm::ConstantInt::getSigned(Int64Ty, Width); + Addr = llvm::ConstantExpr::getBitCast(CAddr, Int8PtrTy); + } else { + Size = llvm::ConstantInt::get(Int64Ty, Width); + Addr = Builder.CreateBitCast(Addr, Int8PtrTy); + } + llvm::CallInst *C = Builder.CreateCall(InvariantStart, {Size, Addr}); + C->setDoesNotThrow(); + + return CodeGenModule::InvariantArgs{C, Size, Addr}; +} + +void CodeGenFunction::EmitInvariantEnd(llvm::CallInst *Start, llvm::Value *Size, + llvm::Value *Addr) { + // Grab the llvm.invariant.end intrinsic. + llvm::Intrinsic::ID InvEndID = llvm::Intrinsic::invariant_end; + llvm::Constant *InvariantEnd = CGM.getIntrinsic(InvEndID); + + // Emit a call with the size in bytes of the object. + llvm::CallInst *C = Builder.CreateCall(InvariantEnd, {Start, Size, Addr}); + C->setDoesNotThrow(); +} + /// EmitAutoVarAlloca - Emit the alloca and debug information for a /// local variable. Does not emit initialization or destruction. CodeGenFunction::AutoVarEmission Index: lib/CodeGen/CGDeclCXX.cpp =================================================================== --- lib/CodeGen/CGDeclCXX.cpp +++ lib/CodeGen/CGDeclCXX.cpp @@ -109,26 +109,6 @@ CGM.getCXXABI().registerGlobalDtor(CGF, D, function, argument); } -/// Emit code to cause the variable at the given address to be considered as -/// constant from this point onwards. -static void EmitDeclInvariant(CodeGenFunction &CGF, const VarDecl &D, - llvm::Constant *Addr) { - // Don't emit the intrinsic if we're not optimizing. - if (!CGF.CGM.getCodeGenOpts().OptimizationLevel) - return; - - // Grab the llvm.invariant.start intrinsic. - llvm::Intrinsic::ID InvStartID = llvm::Intrinsic::invariant_start; - llvm::Constant *InvariantStart = CGF.CGM.getIntrinsic(InvStartID); - - // Emit a call with the size in bytes of the object. - CharUnits WidthChars = CGF.getContext().getTypeSizeInChars(D.getType()); - uint64_t Width = WidthChars.getQuantity(); - llvm::Value *Args[2] = { llvm::ConstantInt::getSigned(CGF.Int64Ty, Width), - llvm::ConstantExpr::getBitCast(Addr, CGF.Int8PtrTy)}; - CGF.Builder.CreateCall(InvariantStart, Args); -} - void CodeGenFunction::EmitCXXGlobalVarDeclInit(const VarDecl &D, llvm::Constant *DeclPtr, bool PerformInit) { @@ -168,10 +148,16 @@ PerformInit, this); if (PerformInit) EmitDeclInit(*this, D, DeclAddr); - if (CGM.isTypeConstant(D.getType(), true)) - EmitDeclInvariant(*this, D, DeclPtr); - else + if (CGM.isTypeConstant(D.getType(), true)) { + // Generate the @llvm.invariant.start intrinsic call if DeclPtr is + // not 'writeonce'. We'll do the same for 'writeonce' addresses later. + llvm::GlobalVariable *GV = dyn_cast(DeclPtr); + assert(GV && "Must point to a global variable."); + if (GV->isConstant() && !D.getType().isWriteOnce(getContext())) + (void)EmitInvariantStart(D, DeclPtr); + } else { EmitDeclDestroy(*this, D, DeclAddr); + } return; } @@ -489,6 +475,7 @@ } else { EmitCXXGlobalVarDeclInit(*D, Addr, PerformInit); } + MarkWriteOnceWrittenRAII MWO(*this, D, Addr); FinishFunction(); } Index: lib/CodeGen/CGExprAgg.cpp =================================================================== --- lib/CodeGen/CGExprAgg.cpp +++ lib/CodeGen/CGExprAgg.cpp @@ -1402,7 +1402,7 @@ // Optimize the slot if possible. CheckAggExprForMemSetUse(Slot, E, *this); - + AggExprEmitter(*this, Slot, Slot.isIgnored()).Visit(const_cast(E)); } Index: lib/CodeGen/CodeGenFunction.h =================================================================== --- lib/CodeGen/CodeGenFunction.h +++ lib/CodeGen/CodeGenFunction.h @@ -1904,6 +1904,10 @@ llvm::Value *EmitLifetimeStart(uint64_t Size, llvm::Value *Addr); void EmitLifetimeEnd(llvm::Value *Size, llvm::Value *Addr); + CodeGenModule::InvariantArgs EmitInvariantStart(const VarDecl &D, + llvm::Value *Addr); + void EmitInvariantEnd(llvm::CallInst *C, llvm::Value *Size, + llvm::Value *Addr); llvm::Value *EmitCXXNewExpr(const CXXNewExpr *E); void EmitCXXDeleteExpr(const CXXDeleteExpr *E); @@ -3218,6 +3222,18 @@ llvm::Value *GetValueForARMHint(unsigned BuiltinID); }; +class MarkWriteOnceWrittenRAII { + CodeGenFunction &CGF; + llvm::GlobalVariable *GV; + llvm::AllocaInst *AI; + CodeGenModule::InvariantArgs Args; + + public: + MarkWriteOnceWrittenRAII(CodeGenFunction &CGF, const VarDecl *D, + llvm::GlobalVariable *GVAddr = nullptr); + ~MarkWriteOnceWrittenRAII(); +}; + /// Helper class with most of the code for saving a value for a /// conditional expression cleanup. struct DominatingLLVMValue { Index: lib/CodeGen/CodeGenModule.h =================================================================== --- lib/CodeGen/CodeGenModule.h +++ lib/CodeGen/CodeGenModule.h @@ -259,6 +259,15 @@ typedef std::vector CtorList; + struct InvariantArgs { + llvm::CallInst *StartInst; + llvm::Value *Size; + llvm::Value *Addr; + InvariantArgs() : StartInst(nullptr), Size(nullptr), Addr(nullptr) {} + InvariantArgs(llvm::CallInst *C, llvm::Value *S, llvm::Value *A) + : StartInst(C), Size(S), Addr(A) {} + }; + private: ASTContext &Context; const LangOptions &LangOpts; Index: test/CodeGenCXX/const-invariant.cpp =================================================================== --- /dev/null +++ test/CodeGenCXX/const-invariant.cpp @@ -0,0 +1,402 @@ +// *** const objects. +// RUN: %clang -x c++ -emit-llvm -target x86_64-linux-gnu -S %s -O0 -o - -DCONST -DOBJ | FileCheck %s --check-prefix=CHECK-O0 +// RUN: %clang -x c++ -emit-llvm -target x86_64-linux-gnu -S %s -O2 -mllvm -disable-llvm-optzns -o - -DCONST -DOBJ | FileCheck %s --check-prefix=CHECK-L-CO --check-prefix=CHECK-L-CO-OBJ --check-prefix=CHECK-NG-CO +// RUN: %clang -x c++ -emit-llvm -target x86_64-linux-gnu -S %s -O2 -mllvm -disable-llvm-optzns -o - -DPART_GLOBAL -DCONST -DOBJ | FileCheck %s --check-prefix=CHECK-GP-CO --check-prefix=CHECK-NL-CO --check-prefix=CHECK-NL-CO-OBJ --check-prefix=CHECK-NG-CO +// RUN: %clang -x c++ -emit-llvm -target x86_64-linux-gnu -S %s -O2 -mllvm -disable-llvm-optzns -o - -DGLOBAL -DCONST -DOBJ | FileCheck %s --check-prefix=CHECK-G-CO --check-prefix=CHECK-G-CO-OBJ --check-prefix=CHECK-NL-CO --check-prefix=CHECK-NL-CO-OBJ + +// *** const variables of builtin type. +// RUN: %clang -x c++ -emit-llvm -target x86_64-linux-gnu -S %s -O0 -o - -DCONST -DINT | FileCheck %s --check-prefix=CHECK-O0 +// RUN: %clang -x c++ -emit-llvm -target x86_64-linux-gnu -S %s -O2 -mllvm -disable-llvm-optzns -o - -DCONST -DINT | FileCheck %s --check-prefix=CHECK-L-CO --check-prefix=CHECK-L-CO-INT --check-prefix=CHECK-NG-CO +// RUN: %clang -x c++ -emit-llvm -target x86_64-linux-gnu -S %s -O2 -mllvm -disable-llvm-optzns -o - -DPART_GLOBAL -DCONST -DINT | FileCheck %s --check-prefix=CHECK-GP-CO --check-prefix=CHECK-NL-CO --check-prefix=CHECK-NL-CO-INT --check-prefix=CHECK-NG-CO +// RUN: %clang -x c++ -emit-llvm -target x86_64-linux-gnu -S %s -O2 -mllvm -disable-llvm-optzns -o - -DGLOBAL -DCONST -DINT | FileCheck %s --check-prefix=CHECK-G-CO --check-prefix=CHECK-G-CO-INT --check-prefix=CHECK-NL-CO + +// *** non-const objects. +// RUN: %clang -x c++ -emit-llvm -target x86_64-linux-gnu -S %s -O2 -mllvm -disable-llvm-optzns -o - -DOBJ | FileCheck %s --check-prefix=CHECK-NC +// RUN: %clang -x c++ -emit-llvm -target x86_64-linux-gnu -S %s -O2 -mllvm -disable-llvm-optzns -o - -DPART_GLOBAL -DOBJ | FileCheck %s --check-prefix=CHECK-NC +// RUN: %clang -x c++ -emit-llvm -target x86_64-linux-gnu -S %s -O2 -mllvm -disable-llvm-optzns -o - -DGLOBAL -DOBJ | FileCheck %s --check-prefix=CHECK-NC + +// Check that llvm.invariant.start/end are properly generated on C++ const +// variables. + +// Do not produce markers at -O0. +// CHECK-O0-NOT: llvm.invariant.start +// CHECK-O0-NOT: llvm.invariant.end + +// Do not produce markers from non-const objects +// CHECK-NC-NOT: llvm.invariant.start +// CHECK-NC-NOT: llvm.invariant.end + +// const or not? +#ifdef CONST +#define Const const +#else +#define Const +#endif + +// object or builtin? +#if defined(OBJ) +struct A { + int a; + int b; + A(int a) : a(a) {} +}; + +// A with explicit destructor +struct D { + int a; + int b; + D(int a) : a(a) {} + ~D(); +}; + +// A with mutable data field. +struct M { + int a; + mutable int b; + M(int a) : a(a) {} + void f() {} +}; + +// A with non-trivial method that may write to memory. +struct F { + int a; + F(int a) : a(a) {} + void f(); +}; + +// A non-virtual base class. +struct Base { + int pa; + Base(int a); +}; +struct I : public Base { + int a; + int b; + I(int a); +}; + +// A virtual base class, not-writeonce. +struct VBase { + int pa; + VBase(int a); + virtual void f(); +}; +struct IV : public VBase { + int a; + int b; + IV(int a); +}; + +// A virtual base class, writeonce. +struct CVBase { + int pa; + CVBase(int a); + virtual void f() = 0; + virtual void g() {} +}; +struct CIV : public CVBase { + int a; + int b; + CIV(int a); + void f() {} +}; + +#define Type A // C++ class +#elif defined(INT) +#define Type int // Builtin +#endif + +// global, partial global or local? +#if defined(GLOBAL) +#undef LOCAL +#undef LOCAL_PARTIAL +#elif defined(PART_GLOBAL) +#undef LOCAL +#define LOCAL_PARTIAL +#else +#define LOCAL +#define LOCAL_PARTIAL +#endif + +// --------------- START --------------- + +const int one(); + +#ifndef LOCAL +Const Type i(one()); +#ifdef CONST +Const Type& i_r = Type(one()); +#endif +Const Type* i_p = new Type(one()); +Const Type* Const i_pc = new Type(one()); +#endif + +#ifndef LOCAL_PARTIAL +Const Type j = i; +Type k = i; +#endif + +#if defined(OBJ) && !defined(LOCAL) +Const D i_d(one()); +Const M i_m(one()); +Const F i_f(one()); +Const I i_i(one()); +Const IV i_iv(one()); +Const CIV i_civ(one()); +#endif + +// CHECK-NL-CO: @_ZL1i = internal global +// CHECK-NL-CO: @i_r = global +// CHECK-NL-CO: @_ZGR3i_r_ = internal global +// CHECK-NL-CO: @i_p = global +// CHECK-NL-CO: @_ZL4i_pc = internal global +// CHECK-G-CO: @k = global +// CHECK-NL-CO-OBJ: @_ZL3i_d = internal global +// CHECK-NL-CO-OBJ: @_ZL3i_m = internal global +// CHECK-NL-CO-OBJ: @_ZL3i_f = internal global +// CHECK-NL-CO-OBJ: @_ZL3i_i = internal global +// CHECK-NL-CO-OBJ: @_ZL4i_iv = internal global +// CHECK-NL-CO-OBJ: @_ZL5i_civ = internal global +// CHECK-G-CO: @_ZL1j = internal global + +// CHECK-NL-CO-OBJ: call void @_ZN1AC{{[0-9]+}}Ei({{.*}}* @_ZL1i, {{.*}}) +// CHECK-NL-CO-INT: store {{.*}}, {{.*}}* @_ZL1i +// CHECK-NL-CO: call {{.*}}@llvm.invariant.start(i64 {{[0-9]+}}, i8* bitcast ({{.*}} @_ZL1i to i8*)) +// 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 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, {{.*}}) +// CHECK-NL-CO-OBJ: call {{.*}}@llvm.invariant.start(i64 {{[0-9]+}}, i8* bitcast ({{.*}} @_ZL3i_i to i8*)) +// CHECK-NL-CO-OBJ: call void @_ZN2IVC{{[0-9]+}}Ei({{.*}}* @_ZL4i_iv, {{.*}}) +// CHECK-NL-CO-OBJ-NOT: call {{.*}}@llvm.invariant.start(i64 {{[0-9]+}}, i8* bitcast ({{.*}} @_ZL4i_iv to i8*)) +// CHECK-NL-CO-OBJ: call void @_ZN3CIVC{{[0-9]+}}Ei({{.*}}* @_ZL5i_civ, {{.*}}) +// CHECK-NL-CO-OBJ: call {{.*}}@llvm.invariant.start(i64 {{[0-9]+}}, i8* bitcast ({{.*}} @_ZL5i_civ to i8*)) + +void foo(const Type* const); +void bar(Type); +#if defined(OBJ) +void foo_d(const D* const); +void bar_d(D); +void foo_m(const M* const); +void bar_m(M); +void foo_f(const F* const); +void bar_f(F); +void foo_i(const I* const); +void bar_i(I); +void foo_iv(const IV* const); +void bar_iv(IV); +void foo_civ(const CIV* const); +void bar_civ(CIV); +#endif + +// Example 1: Duplicate loads. +void ex1() { +// CHECK: @_Z3ex1v( +#ifdef LOCAL + Const Type i(one()); +#endif + // CHECK-L-CO: %i = alloca {{.*}} + + // CHECK-L-CO-OBJ: call void @_ZN1AC{{[0-9]+}}Ei({{.*}}* %i, + // CHECK-L-CO-INT: store {{.*}}, {{.*}}* %i + // CHECK-L-CO: call {{.*}}@llvm.invariant.start(i64 {{[0-9]+}}, i8* + bar(i); + foo(&i); // Does not change i. + bar(i); + // CHECK-L-CO: call {{.*}}@llvm.invariant.end({{.*}}, i64 {{[0-9]+}}, i8* +} + +// Example 2: Unnecessary stores and loads. +void ex2() { +// CHECK: @_Z3ex2v( +#ifdef LOCAL + Const Type i(one()); +#endif +#ifdef LOCAL_PARTIAL + Const Type j = i; +#endif + // CHECK-L-CO: %i = alloca {{.*}} + // CHECK-NG-CO: %j = alloca {{.*}} + + // CHECK-L-CO-OBJ: call void @_ZN1AC{{[0-9]+}}Ei({{.*}}* %i, + // CHECK-L-CO-INT: store {{.*}}, {{.*}}* %i + // CHECK-L-CO: call {{.*}}@llvm.invariant.start(i64 {{[0-9]+}}, i8* + // CHECK-NG-CO: call {{.*}}@llvm.invariant.start(i64 {{[0-9]+}}, i8* + bar(i); + foo(&i); // Does not change i, nor j. + bar(j); + // CHECK-NG-CO: call {{.*}}@llvm.invariant.end({{.*}}, i64 {{[0-9]+}}, i8* + // CHECK-L-CO: call {{.*}}@llvm.invariant.end({{.*}}, i64 {{[0-9]+}}, i8* +} + +// Example 3: Necessary stores and loads. +void ex3() { +// CHECK: @_Z3ex3v( +#ifdef LOCAL + Const Type i(one()); +#endif +#ifdef LOCAL_PARTIAL + Type k = i; +#endif + // CHECK-L-CO: %i = alloca {{.*}} + // CHECK-NG-CO: %k = alloca {{.*}} + + // CHECK-L-CO-OBJ: call void @_ZN1AC{{[0-9]+}}Ei({{.*}}* %i, + // CHECK-L-CO-INT: store {{.*}}, {{.*}}* %i + // CHECK-L-CO: call {{.*}}@llvm.invariant.start(i64 {{[0-9]+}}, i8* + bar(i); + foo(&k); // Does not change i; May change k. + bar(k); + // CHECK-L-CO: call {{.*}}@llvm.invariant.end({{.*}}, i64 {{[0-9]+}}, i8* +} + +#if defined(OBJ) + +// Example 1 with explicit destructor: +// Call to invariant_end intrinsic must be generated before +// call to destructor. +void ex1_d() { +// CHECK: @_Z5ex1_dv( +#ifdef LOCAL + Const D i_d(one()); +#endif + // CHECK-L-CO-OBJ: %i_d = alloca {{.*}} + + // CHECK-L-CO-OBJ: call void @_ZN1DC{{[0-9]+}}Ei({{.*}}* %i_d, + // CHECK-L-CO-OBJ: call {{.*}}@llvm.invariant.start(i64 {{[0-9]+}}, i8* + bar_d(i_d); + foo_d(&i_d); // Does not change i_d. + bar_d(i_d); + // CHECK-L-CO-OBJ: call {{.*}}@llvm.invariant.end({{.*}}, i64 {{[0-9]+}}, i8* + // CHECK-L-CO-OBJ: call void @_ZN1DD1Ev( +} + +// Example 1 with mutable data field and a trivial method: +// Expect no change from Example 1. +void ex1_m() { +// CHECK: @_Z5ex1_mv( +#ifdef LOCAL + Const M i_m(one()); +#endif + // CHECK-L-CO-OBJ: %i_m = alloca {{.*}} + + // CHECK-L-CO-OBJ: call void @_ZN1MC{{[0-9]+}}Ei({{.*}}* %i_m, + // CHECK-L-CO-OBJ: call {{.*}}@llvm.invariant.start( + bar_m(i_m); + foo_m(&i_m); // May change i_m. + bar_m(i_m); + // CHECK-L-CO-OBJ: call {{.*}}@llvm.invariant.end( +} + +// Example 1 no non-trivial method that may write to memory: +// Expect no invariant intrinsic call generated. +void ex1_f() { +// CHECK: @_Z5ex1_fv( +#ifdef LOCAL + Const F i_f(one()); +#endif + // CHECK-L-CO-OBJ: %i_f = alloca {{.*}} + + // CHECK-L-CO-OBJ: call void @_ZN1FC{{[0-9]+}}Ei({{.*}}* %i_f, + // CHECK-L-CO-OBJ-NOT: call {{.*}}@llvm.invariant.start( + bar_f(i_f); + foo_f(&i_f); // May change i_f. + bar_f(i_f); + // CHECK-L-CO-OBJ-NOT: call {{.*}}@llvm.invariant.end( +} + +// Example 1 with type inheriting non-virtual base: +// Expect no change from Example 1. +void ex1_i() { +// CHECK: @_Z5ex1_iv( +#ifdef LOCAL + Const I i_i(one()); +#endif + // CHECK-L-CO-OBJ: %i_i = alloca {{.*}} + + // CHECK-L-CO-OBJ: call void @_ZN1IC{{[0-9]+}}Ei({{.*}}* %i_i, + // CHECK-L-CO-OBJ: call {{.*}}@llvm.invariant.start( + bar_i(i_i); + foo_i(&i_i); // Does not change i_i. + bar_i(i_i); + // CHECK-L-CO-OBJ: call {{.*}}@llvm.invariant.end( +} + +// Example 1 with type inheriting virtual non-writeonce base: +// Expect no invariant intrinsics. +void ex1_iv() { +// CHECK: @_Z6ex1_ivv( +#ifdef LOCAL + Const IV i_iv(one()); +#endif + // CHECK-L-CO-OBJ: %i_iv = alloca {{.*}} + + // CHECK-L-CO-OBJ: call void @_ZN2IVC{{[0-9]+}}Ei({{.*}}* %i_iv, + // CHECK-L-CO-OBJ-NOT: call {{.*}}@llvm.invariant.start( + bar_iv(i_iv); + foo_iv(&i_iv); // Does not change i_iv. + bar_iv(i_iv); + // CHECK-L-CO-OBJ-NOT: call {{.*}}@llvm.invariant.end( +} + +// Example 1 with type inheriting virtual writeonce base: +// Expect no change from Example 1. +void ex1_civ() { +// CHECK: @_Z7ex1_civv( +#ifdef LOCAL + Const CIV i_civ(one()); +#endif + // CHECK-L-CO-OBJ: %i_civ = alloca {{.*}} + + // CHECK-L-CO-OBJ: call void @_ZN3CIVC{{[0-9]+}}Ei({{.*}}* %i_civ, + // CHECK-L-CO-OBJ: call {{.*}}@llvm.invariant.start( + bar_civ(i_civ); + foo_civ(&i_civ); // Does not change i_civ. + bar_civ(i_civ); + // CHECK-L-CO-OBJ: call {{.*}}@llvm.invariant.end( +} + +#endif // #if defined(OBJ) + +// Example 1 with references and pointers: +// Expect no invariant intrinsic call generated on non const pointers. +void ex1_p() { +// CHECK: @_Z5ex1_pv( +#ifdef LOCAL +#ifdef CONST + Const Type& i_r = Type(one()); +#endif + Const Type* i_p = new Type(one()); + Const Type* Const i_pc = new Type(one()); +#endif + +#ifdef CONST + // CHECK-L-CO-OBJ: call void @_ZN1AC{{[0-9]+}}Ei({{.*}}* %{{.*.tmp}}, + // CHECK-L-CO-INT: store {{.*}}, {{.*}}* %{{.*.tmp}} + // CHECK-L-CO-NOT: call {{.*}}@llvm.invariant.start( + bar(i_r); + foo(&i_r); // May change i_r. + bar(i_r); +// CHECK-L-CO-NOT: call {{.*}}@llvm.invariant.end( +#endif + + // CHECK-L-CO: call {{.*}} i8* @_Znwm( + // CHECK-L-CO-NOT: call {{.*}}@llvm.invariant.start( + bar(*i_p); + foo(i_p); // May change *i_p. + bar(*i_p); + // CHECK-L-CO-NOT: call {{.*}}@llvm.invariant.end( + + // CHECK-L-CO: call {{.*}} i8* @_Znwm( + // CHECK-L-CO: call {{.*}}@llvm.invariant.start(i64 {{[0-9]+}}, i8* + bar(*i_pc); + foo(i_pc); // May change *i_p. + bar(*i_pc); + // CHECK-L-CO: call {{.*}}@llvm.invariant.end({{.*}}, i64 {{[0-9]+}}, i8* +} + +// CHECK-G-CO-OBJ: call {{.*}}@llvm.invariant.start(i64 {{[0-9]+}}, i8* bitcast ({{.*}} @_ZL1j to i8*)) +// CHECK-G-CO-INT: store {{.*}}, {{.*}}* @_ZL1j Index: test/CodeGenCXX/init-invariant.cpp =================================================================== --- test/CodeGenCXX/init-invariant.cpp +++ test/CodeGenCXX/init-invariant.cpp @@ -45,10 +45,10 @@ // CHECK: call {{.*}}@llvm.invariant.start(i64 4, i8* bitcast ({{.*}} @a to i8*)) // CHECK: call void @_ZN1BC1Ev({{.*}}* nonnull @b) -// CHECK-NOT: call {{.*}}@llvm.invariant.start(i64 4, i8* bitcast ({{.*}} @b to i8*)) +// CHECK: call {{.*}}@llvm.invariant.start(i64 4, i8* bitcast ({{.*}} @b to i8*)) // CHECK: call void @_ZN1CC1Ev({{.*}}* nonnull @c) -// CHECK-NOT: call {{.*}}@llvm.invariant.start(i64 4, i8* bitcast ({{.*}} @c to i8*)) +// CHECK: call {{.*}}@llvm.invariant.start(i64 4, i8* bitcast ({{.*}} @c to i8*)) // CHECK: call i32 @_Z1fv( // CHECK: store {{.*}}, i32* @d