Index: include/clang/AST/Type.h =================================================================== --- include/clang/AST/Type.h +++ include/clang/AST/Type.h @@ -709,6 +709,8 @@ return QualType::isConstant(*this, Ctx); } + 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/Type.cpp =================================================================== --- lib/AST/Type.cpp +++ lib/AST/Type.cpp @@ -1913,6 +1913,19 @@ } } +bool QualType::isWriteOnce(ASTContext &Context) { + + if (!Context.getLangOpts().CPlusPlus || + !isConstant(Context) || getTypePtr()->isReferenceType()) + return false; + + if (const CXXRecordDecl *Record + = Context.getBaseElementType(*this)->getAsCXXRecordDecl()) + return !Record->hasMutableFields(); + + return true; +} + 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 @@ -524,6 +524,20 @@ CGF.EmitLifetimeEnd(Size, Addr); } }; + + /// A cleanup to call @llvm.invariant.end. + class CallInvariantEnd : 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 @@ -842,12 +856,38 @@ canEmitInitWithFewStoresAfterMemset(Init, StoreBudget); } + +/// Emit the code necessary to initialize the given global variable. +void CodeGenFunction::MarkWriteOnceWritten(const VarDecl& D, + llvm::Value *Addr) { + auto *GV = dyn_cast(Addr); + auto *AI = dyn_cast(Addr); + + // 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 ((GV && GV->isWriteOnce()) || (AI && AI->isWriteOnce())) { + CodeGenModule::InvariantArgs Args = EmitInvariantStart(D, Addr); + + if (AI && Args.StartInst) { + assert(!GV && "Can't have it both ways."); + 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); + MarkWriteOnceWritten(D, emission.Address); EmitAutoVarCleanups(emission); } @@ -880,6 +920,49 @@ 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{nullptr, 0, nullptr}; + + // 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 { + //Width = CGM.getDataLayout().getTypeAllocSize(LTy); + 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 @@ -949,7 +1032,8 @@ // to this variable. Set it to zero to indicate that NRVO was not // applied. llvm::Value *Zero = Builder.getFalse(); - llvm::Value *NRVOFlag = CreateTempAlloca(Zero->getType(), "nrvo"); + llvm::AllocaInst *NRVOFlag = CreateTempAlloca(Zero->getType(), "nrvo"); + NRVOFlag->setWriteOnce(Ty.isWriteOnce(getContext())); EnsureInsertPoint(); Builder.CreateStore(Zero, NRVOFlag); @@ -964,6 +1048,7 @@ llvm::AllocaInst *Alloc = CreateTempAlloca(LTy); Alloc->setName(D.getName()); + Alloc->setWriteOnce(Ty.isWriteOnce(getContext())); CharUnits allocaAlignment = alignment; if (isByRef) @@ -986,7 +1071,8 @@ if (!DidCallStackSave) { // Save the stack. - llvm::Value *Stack = CreateTempAlloca(Int8PtrTy, "saved_stack"); + llvm::AllocaInst *Stack = CreateTempAlloca(Int8PtrTy, "saved_stack"); + Stack->setWriteOnce(Ty.isWriteOnce(getContext())); llvm::Value *F = CGM.getIntrinsic(llvm::Intrinsic::stacksave); llvm::Value *V = Builder.CreateCall(F); @@ -1009,6 +1095,7 @@ // Allocate memory for the array. llvm::AllocaInst *vla = Builder.CreateAlloca(llvmTy, elementCount, "vla"); vla->setAlignment(alignment.getQuantity()); + vla->setWriteOnce(Ty.isWriteOnce(getContext())); DeclPtr = vla; } @@ -1678,6 +1765,7 @@ llvm::AllocaInst *Alloc = CreateTempAlloca(ConvertTypeForMem(Ty), D.getName() + ".addr"); Alloc->setAlignment(getContext().getDeclAlign(&D).getQuantity()); + Alloc->setWriteOnce(Ty.isWriteOnce(getContext())); LValue lv = MakeAddrLValue(Alloc, Ty, getContext().getDeclAlign(&D)); EmitStoreOfScalar(Arg, lv, /* isInitialization */ true); LocalAddr = Builder.CreateLoad(Alloc); @@ -1721,6 +1809,7 @@ llvm::AllocaInst *Alloc = CreateTempAlloca(ConvertTypeForMem(Ty), D.getName() + ".addr"); Alloc->setAlignment(Align.getQuantity()); + Alloc->setWriteOnce(Ty.isWriteOnce(getContext())); DeclPtr = Alloc; DoStore = true; } Index: lib/CodeGen/CGDeclCXX.cpp =================================================================== --- lib/CodeGen/CGDeclCXX.cpp +++ lib/CodeGen/CGDeclCXX.cpp @@ -112,26 +112,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) { @@ -169,9 +149,13 @@ PerformInit, this); if (PerformInit) EmitDeclInit(*this, D, DeclPtr); - 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 a writeonce. We'll do the same for writeonce addresses later. + llvm::GlobalVariable* GV = dyn_cast(DeclPtr); + if (GV && !GV->isWriteOnce()) + (void) EmitInvariantStart(D, DeclPtr); + } else EmitDeclDestroy(*this, D, DeclPtr); return; } @@ -491,6 +475,7 @@ } else { EmitCXXGlobalVarDeclInit(*D, Addr, PerformInit); } + MarkWriteOnceWritten(*D, Addr); FinishFunction(); } Index: lib/CodeGen/CGExprAgg.cpp =================================================================== --- lib/CodeGen/CGExprAgg.cpp +++ lib/CodeGen/CGExprAgg.cpp @@ -1408,7 +1408,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 @@ -1788,6 +1788,8 @@ llvm::Value *EmitLifetimeStart(uint64_t Size, llvm::Value *Addr); void EmitLifetimeEnd(llvm::Value *Size, 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); @@ -2760,6 +2762,9 @@ AddInitializerToStaticVarDecl(const VarDecl &D, llvm::GlobalVariable *GV); + void MarkWriteOnceWritten(const VarDecl& D, llvm::Value *Addr); + CodeGenModule::InvariantArgs EmitInvariantStart(const VarDecl &D, + llvm::Value *Addr); /// EmitCXXGlobalVarDeclInit - Create the initializer for a C++ /// variable with global storage. Index: lib/CodeGen/CodeGenModule.h =================================================================== --- lib/CodeGen/CodeGenModule.h +++ lib/CodeGen/CodeGenModule.h @@ -277,6 +277,14 @@ typedef std::vector CtorList; + struct InvariantArgs { + llvm::CallInst *StartInst; + llvm::Value *Size; + llvm::Value *Addr; + InvariantArgs(llvm::CallInst *C, llvm::Value *size, llvm::Value *addr) + : StartInst(C), Size(size), Addr(addr) {} + }; + private: ASTContext &Context; const LangOptions &LangOpts; Index: lib/CodeGen/CodeGenModule.cpp =================================================================== --- lib/CodeGen/CodeGenModule.cpp +++ lib/CodeGen/CodeGenModule.cpp @@ -2115,6 +2115,9 @@ // common vars aren't constant even if declared const. GV->setConstant(false); + // Mark const variables 'writeonce', if global is not 'constant'. + GV->setWriteOnce(!GV->isConstant() && D->getType().isWriteOnce(Context)); + setNonAliasAttributes(D, GV); if (D->getTLSKind() && !GV->isThreadLocal()) {