Index: include/llvm/IR/CallSite.h =================================================================== --- include/llvm/IR/CallSite.h +++ include/llvm/IR/CallSite.h @@ -288,7 +288,15 @@ } void setOnlyReadsMemory() { CALLSITE_DELEGATE_SETTER(setOnlyReadsMemory()); - } + }/* + bool onlyReadsMemory_old() const { + //CALLSITE_DELEGATE_GETTER(onlyReadsMemory()); + + InstrTy *II = getInstruction(); + return isCall() + ? cast(II)->onlyReadsMemory_old() + : cast(II)->onlyReadsMemory(); + }*/ /// @brief Determine if the call can access memmory only using pointers based /// on its arguments. @@ -350,6 +358,8 @@ } bool onlyReadsMemory(unsigned ArgNo) const { + //llvm::errs()<<"* CallSite::onlyReadsMemory() -- ***\n"; + assert(false && "Are we ever calling this?"); return paramHasAttr(ArgNo + 1, Attribute::ReadOnly) || paramHasAttr(ArgNo + 1, Attribute::ReadNone); } Index: include/llvm/IR/GlobalVariable.h =================================================================== --- include/llvm/IR/GlobalVariable.h +++ include/llvm/IR/GlobalVariable.h @@ -41,6 +41,7 @@ void setParent(Module *parent); bool isConstantGlobal : 1; // Is this a global constant? + bool isWriteOnceGlobal : 1; // Is this a writeonce value? bool isExternallyInitializedConstant : 1; // Is this a global whose value // can change from its initial // value before global @@ -144,6 +145,9 @@ bool isConstant() const { return isConstantGlobal; } void setConstant(bool Val) { isConstantGlobal = Val; } + bool isWriteOnce() const { return isWriteOnceGlobal; } + void setWriteOnce(bool Val) { isWriteOnceGlobal = Val; } + bool isExternallyInitialized() const { return isExternallyInitializedConstant; } Index: include/llvm/IR/Instructions.h =================================================================== --- include/llvm/IR/Instructions.h +++ include/llvm/IR/Instructions.h @@ -75,6 +75,7 @@ /// class AllocaInst : public UnaryInstruction { Type *AllocatedType; + bool isWriteOnceLocal; protected: // Note: Instruction needs to be a friend here to call cloneImpl. @@ -132,6 +133,9 @@ } void setAlignment(unsigned Align); + bool isWriteOnce() const { return isWriteOnceLocal; } + void setWriteOnce(bool Val) { isWriteOnceLocal = Val; } + /// isStaticAlloca - Return true if this alloca is in the entry block of the /// function and is a constant size. If so, the code generator will fold it /// into the prolog/epilog code, so it is basically free. @@ -1587,8 +1591,14 @@ addAttribute(AttributeSet::FunctionIndex, Attribute::ReadNone); } + bool hasOnlyWriteOnceArgs() const; + /// \brief Determine if the call does not access or only reads memory. bool onlyReadsMemory() const { + return doesNotAccessMemory() || hasFnAttr(Attribute::ReadOnly) || + hasOnlyWriteOnceArgs(); + } + bool onlyReadsMemory_old() const { return doesNotAccessMemory() || hasFnAttr(Attribute::ReadOnly); } void setOnlyReadsMemory() { Index: lib/AsmParser/LLLexer.cpp =================================================================== --- lib/AsmParser/LLLexer.cpp +++ lib/AsmParser/LLLexer.cpp @@ -496,7 +496,7 @@ KEYWORD(true); KEYWORD(false); KEYWORD(declare); KEYWORD(define); - KEYWORD(global); KEYWORD(constant); + KEYWORD(global); KEYWORD(constant); KEYWORD(writeonce); KEYWORD(private); KEYWORD(internal); Index: lib/AsmParser/LLParser.h =================================================================== --- lib/AsmParser/LLParser.h +++ lib/AsmParser/LLParser.h @@ -256,7 +256,7 @@ bool ParseDeclare(); bool ParseDefine(); - bool ParseGlobalType(bool &IsConstant); + bool ParseGlobalType(bool &IsConstant, bool &IsWriteOnce); bool ParseUnnamedGlobal(); bool ParseNamedGlobal(); bool ParseGlobal(const std::string &Name, LocTy Loc, unsigned Linkage, Index: lib/AsmParser/LLParser.cpp =================================================================== --- lib/AsmParser/LLParser.cpp +++ lib/AsmParser/LLParser.cpp @@ -393,8 +393,8 @@ /// ParseGlobalType /// ::= 'constant' -/// ::= 'global' -bool LLParser::ParseGlobalType(bool &IsConstant) { +/// ::= 'global' OptionalWriteOnce +bool LLParser::ParseGlobalType(bool &IsConstant, bool &IsWriteOnce) { if (Lex.getKind() == lltok::kw_constant) IsConstant = true; else if (Lex.getKind() == lltok::kw_global) @@ -404,6 +404,10 @@ return TokError("expected 'global' or 'constant'"); } Lex.Lex(); + + if (!IsConstant && EatIfPresent(lltok::kw_writeonce)) + IsWriteOnce = true; + return false; } @@ -747,7 +751,7 @@ "symbol with local linkage must have default visibility"); unsigned AddrSpace; - bool IsConstant, IsExternallyInitialized; + bool IsConstant, IsWriteOnce, IsExternallyInitialized; LocTy IsExternallyInitializedLoc; LocTy TyLoc; @@ -756,7 +760,7 @@ ParseOptionalToken(lltok::kw_externally_initialized, IsExternallyInitialized, &IsExternallyInitializedLoc) || - ParseGlobalType(IsConstant) || + ParseGlobalType(IsConstant, IsWriteOnce) || ParseType(Ty, TyLoc)) return true; @@ -813,6 +817,7 @@ if (Init) GV->setInitializer(Init); GV->setConstant(IsConstant); + GV->setWriteOnce(IsWriteOnce); GV->setLinkage((GlobalValue::LinkageTypes)Linkage); GV->setVisibility((GlobalValue::VisibilityTypes)Visibility); GV->setDLLStorageClass((GlobalValue::DLLStorageClassTypes)DLLStorageClass); @@ -5353,6 +5358,7 @@ Type *Ty = nullptr; bool IsInAlloca = EatIfPresent(lltok::kw_inalloca); + bool IsWriteOnce = EatIfPresent(lltok::kw_writeonce); if (ParseType(Ty, TyLoc)) return true; @@ -5377,6 +5383,7 @@ AllocaInst *AI = new AllocaInst(Ty, Size, Alignment); AI->setUsedWithInAlloca(IsInAlloca); + AI->setWriteOnce(IsWriteOnce); Inst = AI; return AteExtraComma ? InstExtraComma : InstNormal; } Index: lib/AsmParser/LLToken.h =================================================================== --- lib/AsmParser/LLToken.h +++ lib/AsmParser/LLToken.h @@ -34,7 +34,7 @@ kw_x, kw_true, kw_false, kw_declare, kw_define, - kw_global, kw_constant, + kw_global, kw_constant, kw_writeonce, kw_private, kw_internal, Index: lib/IR/AsmWriter.cpp =================================================================== --- lib/IR/AsmWriter.cpp +++ lib/IR/AsmWriter.cpp @@ -2398,6 +2398,11 @@ Out << "addrspace(" << AddressSpace << ") "; if (GV->isExternallyInitialized()) Out << "externally_initialized "; Out << (GV->isConstant() ? "constant " : "global "); + if (GV->isWriteOnce()) { + assert(!GV->isConstant() && + "'constant' globals can't be 'writeonce'"); + Out << "writeonce "; + } TypePrinter.print(GV->getType()->getElementType(), Out); if (GV->hasInitializer()) { @@ -2946,6 +2951,8 @@ Out << ' '; if (AI->isUsedWithInAlloca()) Out << "inalloca "; + if (AI->isWriteOnce()) + Out << "writeonce "; TypePrinter.print(AI->getAllocatedType(), Out); // Explicitly write the array size if the code is broken, if it's an array Index: lib/IR/Globals.cpp =================================================================== --- lib/IR/Globals.cpp +++ lib/IR/Globals.cpp @@ -151,6 +151,7 @@ OperandTraits::op_begin(this), InitVal != nullptr, Link, Name), isConstantGlobal(constant), + isWriteOnceGlobal(false), isExternallyInitializedConstant(isExternallyInitialized) { setThreadLocalMode(TLMode); if (InitVal) { @@ -169,6 +170,7 @@ OperandTraits::op_begin(this), InitVal != nullptr, Link, Name), isConstantGlobal(constant), + isWriteOnceGlobal(false), isExternallyInitializedConstant(isExternallyInitialized) { setThreadLocalMode(TLMode); if (InitVal) { Index: lib/IR/Instruction.cpp =================================================================== --- lib/IR/Instruction.cpp +++ lib/IR/Instruction.cpp @@ -429,7 +429,7 @@ case Instruction::AtomicRMW: return true; case Instruction::Call: - return !cast(this)->onlyReadsMemory(); + return !cast(this)->onlyReadsMemory_old(); case Instruction::Invoke: return !cast(this)->onlyReadsMemory(); case Instruction::Load: Index: lib/IR/Instructions.cpp =================================================================== --- lib/IR/Instructions.cpp +++ lib/IR/Instructions.cpp @@ -319,14 +319,64 @@ setAttributes(PAL); } +// FIXME: Move in llvm::Value and propagate writeonce through +// different instructions. +static bool isWriteOnce(Value* Arg) { + + // FIXME: A special case, eventhough constants are not + // necessarily writeonce; because not all operands ought + // to be checked in the more general case below. + if (dyn_cast(Arg)) + return true; + + if (GlobalVariable* GV = dyn_cast(Arg)) + return GV->isWriteOnce(); + if (AllocaInst* AI = dyn_cast(Arg)) + return AI->isWriteOnce(); + + // FIXME: Not all operands ought to be checked here. + if (User* U = dyn_cast(Arg)) { + auto Ops = U->operands(); + for(auto Op = Ops.begin(), OpEnd = Ops.end(); Op != OpEnd; ++Op) { + if (isWriteOnce(Op->get())) + continue; + return false; + } + return U->getNumOperands(); + } + + // FIXME: Handle other Value kinds: Argument, BasicBlock, InlineAsm, etc... + return false; +} + bool CallInst::paramHasAttr(unsigned i, Attribute::AttrKind A) const { - if (AttributeList.hasAttribute(i, A)) + if (A == Attribute::ReadOnly) + if (AttributeList.hasAttribute(i,A)) return true; + if (A == Attribute::ReadOnly) + if (A == Attribute::ReadOnly) { + Value* Arg = getArgOperand(i-1); + if (isWriteOnce(Arg)) + return true; + } + if (A == Attribute::ReadOnly) if (const Function *F = getCalledFunction()) - return F->getAttributes().hasAttribute(i, A); + return F->getAttributes().hasAttribute(i,A); return false; } +bool CallInst::hasOnlyWriteOnceArgs() const { + auto Args = arg_operands(); + for(auto Arg = Args.begin(), ArgEnd = Args.end(); Arg != ArgEnd; ++Arg) { + if (isWriteOnce(Arg->get())) + continue; + return false; + } + + // return false if the call has no argument. + return getNumArgOperands(); +} + /// IsConstantOne - Return true only if val is constant int 1 static bool IsConstantOne(Value *val) { assert(val && "IsConstantOne does not work with nullptr val"); @@ -551,6 +601,8 @@ bool InvokeInst::paramHasAttr(unsigned i, Attribute::AttrKind A) const { if (AttributeList.hasAttribute(i, A)) return true; + //if (A == Attribute::ReadOnly && isWriteOnce(getArgOperand(i-1))) + // return true; if (const Function *F = getCalledFunction()) return F->getAttributes().hasAttribute(i, A); return false; @@ -824,7 +876,7 @@ const Twine &Name, Instruction *InsertBefore) : UnaryInstruction(PointerType::getUnqual(Ty), Alloca, getAISize(Ty->getContext(), ArraySize), InsertBefore), - AllocatedType(Ty) { + AllocatedType(Ty), isWriteOnceLocal(false) { setAlignment(Align); assert(!Ty->isVoidTy() && "Cannot allocate void!"); setName(Name); @@ -834,7 +886,7 @@ const Twine &Name, BasicBlock *InsertAtEnd) : UnaryInstruction(PointerType::getUnqual(Ty), Alloca, getAISize(Ty->getContext(), ArraySize), InsertAtEnd), - AllocatedType(Ty) { + AllocatedType(Ty), isWriteOnceLocal(false) { setAlignment(Align); assert(!Ty->isVoidTy() && "Cannot allocate void!"); setName(Name); Index: lib/Linker/LinkModules.cpp =================================================================== --- lib/Linker/LinkModules.cpp +++ lib/Linker/LinkModules.cpp @@ -1112,6 +1112,7 @@ if (DGVar && SGVar && DGVar->isDeclaration() && SGVar->isDeclaration() && (!DGVar->isConstant() || !SGVar->isConstant())) NewGVar->setConstant(false); + NewGVar->setWriteOnce(DGVar->isWriteOnce() && SGVar->isWriteOnce()); } // Make sure to remember this mapping. Index: lib/Transforms/IPO/GlobalOpt.cpp =================================================================== --- lib/Transforms/IPO/GlobalOpt.cpp +++ lib/Transforms/IPO/GlobalOpt.cpp @@ -1785,6 +1785,10 @@ DEBUG(dbgs() << "MARKING CONSTANT: " << *GV << "\n"); GV->setConstant(true); + // 'constant' globals can't be 'writeonce'. + if (GV->isWriteOnce()) + GV->setWriteOnce(false); + // Clean up any obviously simplifiable users now. CleanupConstantGlobalUsers(GV, GV->getInitializer(), DL, TLI); @@ -2700,8 +2704,13 @@ Eval.getMutatedMemory().begin(), E = Eval.getMutatedMemory().end(); I != E; ++I) CommitValueTo(I->second, I->first); - for (GlobalVariable *GV : Eval.getInvariants()) + for (GlobalVariable *GV : Eval.getInvariants()) { GV->setConstant(true); + + // 'constant' globals can't be 'writeonce'. + if (GV->isWriteOnce()) + GV->setWriteOnce(false); + } } return EvalSuccess; Index: lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp =================================================================== --- lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp +++ lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp @@ -94,7 +94,6 @@ unsigned ArgNo = CS.getArgumentNo(&U); if (CS.isInAllocaArgument(ArgNo)) return false; - // If this is a readonly/readnone call site, then we know it is just a // load (but one that potentially returns the value itself), so we can // ignore it if we know that the value isn't captured. Index: lib/Transforms/Utils/CloneModule.cpp =================================================================== --- lib/Transforms/Utils/CloneModule.cpp +++ lib/Transforms/Utils/CloneModule.cpp @@ -52,6 +52,7 @@ (GlobalVariable*) nullptr, I->getThreadLocalMode(), I->getType()->getAddressSpace()); + GV->setWriteOnce(I->isWriteOnce()); GV->copyAttributesFrom(I); VMap[I] = GV; } Index: lib/Transforms/Utils/CtorUtils.cpp =================================================================== --- lib/Transforms/Utils/CtorUtils.cpp +++ lib/Transforms/Utils/CtorUtils.cpp @@ -52,6 +52,7 @@ CA, "", GCL->getThreadLocalMode()); GCL->getParent()->getGlobalList().insert(GCL, NGV); NGV->takeName(GCL); + NGV->setWriteOnce(GCL->isWriteOnce()); // Nuke the old list, replacing any uses with the new one. if (!GCL->use_empty()) {