Index: lib/Transforms/IPO/GlobalOpt.cpp =================================================================== --- lib/Transforms/IPO/GlobalOpt.cpp +++ lib/Transforms/IPO/GlobalOpt.cpp @@ -2254,6 +2254,98 @@ GV->setInitializer(EvaluateStoreInto(GV->getInitializer(), Val, CE, 2)); } +/// Given a map of address -> value, where addresses are expected to be some form +/// of either a global or a constant GEP, set the initializer for the address to +/// be the value. This performs mostly the same function as CommitValueTo() +/// and EvaluateStoreInto() but is optimized to be more efficient for the common +/// case where the set of addresses are GEPs sharing the same underlying global, +/// processing the GEPs in batches rather than individually. +static void BatchCommitValueTo(const DenseMap &Mem) { + SmallVector, 32> GVs; + SmallVector, 32> CEs; + CEs.reserve(Mem.size()); + + bool UseBatch = true; + for (const auto &I : Mem) { + if (auto *GV = dyn_cast(I.first)) { + GVs.push_back(std::make_pair(GV, I.second)); + } else { + ConstantExpr *GEP = cast(I.first); + if (GEP->getNumOperands() > 3) { + // We don't handle the deeply recursive case using the batch method. + UseBatch = false; + break; + } + CEs.push_back(std::make_pair(cast(I.first), I.second)); + } + } + + // The algorithm below doesn't handle cases like nested structs, so use the + // slower fully general method if we have to. + if (!UseBatch) { + for (const auto &I : Mem) + CommitValueTo(I.second, I.first); + } + + for (auto GVPair : GVs) { + assert(GVPair.first->hasInitializer()); + GVPair.first->setInitializer(GVPair.second); + } + + if (CEs.empty()) + return; + + // We cache a single global's initializer elements in the case where the + // subsequent address/val pair uses the same one. This avoids throwing away and + // rebuilding the constant struct/vector/array just because one element is + // modified at a time. + SmallVector Elts; + Elts.reserve(CEs.size()); + GlobalVariable *CurrentGV = nullptr; + + auto ValidateOrCommitCache = [&](GlobalVariable *GV, bool ForceCommit) { + Constant *Init = GV->getInitializer(); + Type *Ty = Init->getType(); + if (GV != CurrentGV || ForceCommit) { + if (CurrentGV || ForceCommit) { + assert(CurrentGV && "Expected a GV to commit to!"); + // We have a valid cache that needs to be committed. + if (StructType *STy = dyn_cast(Ty)) + CurrentGV->setInitializer(ConstantStruct::get(STy, Elts)); + else if (ArrayType *ArrTy = dyn_cast(Ty)) + CurrentGV->setInitializer(ConstantArray::get(ArrTy, Elts)); + else + CurrentGV->setInitializer(ConstantVector::get(Elts)); + } + // Need to clear and set up cache for new initializer. + CurrentGV = GV; + Elts.clear(); + unsigned NumElts; + if (auto *STy = dyn_cast(Ty)) + NumElts = STy->getNumElements(); + else + NumElts = cast(Ty)->getNumElements(); + for (unsigned i = 0, e = NumElts; i != e; ++i) + Elts.push_back(Init->getAggregateElement(i)); + } + }; + + for (auto CEPair : CEs) { + ConstantExpr *GEP = CEPair.first; + Constant *Val = CEPair.second; + + GlobalVariable *GV = cast(GEP->getOperand(0)); + ValidateOrCommitCache(GV, false); + ConstantInt *CU = cast(GEP->getOperand(2)); + Elts[CU->getZExtValue()] = Val; + } + // The last initializer in the list needs to be committed, others + // will be committed on a new initializer being processed. + auto LastPair = std::prev(CEs.end()); + GlobalVariable *GV = cast(LastPair->first->getOperand(0)); + ValidateOrCommitCache(GV, true); +} + /// Evaluate static constructors in the function, if we can. Return true if we /// can, false otherwise. static bool EvaluateStaticConstructor(Function *F, const DataLayout &DL, @@ -2271,8 +2363,7 @@ DEBUG(dbgs() << "FULLY EVALUATED GLOBAL CTOR FUNCTION '" << F->getName() << "' to " << Eval.getMutatedMemory().size() << " stores.\n"); - for (const auto &I : Eval.getMutatedMemory()) - CommitValueTo(I.second, I.first); + BatchCommitValueTo(Eval.getMutatedMemory()); for (GlobalVariable *GV : Eval.getInvariants()) GV->setConstant(true); }