From f4a97da4072a2ee4aca3c668a9fa113c06fdef8d Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Thu, 21 Aug 2008 00:11:50 +0000 Subject: [PATCH] Fix an N^2 issue handling constant resolution due to RAUW in large arrays this speeds up the bcreader from 6.67s to 0.12s on a testcase Daniel provided. rdar://6158117 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@55090 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Bitcode/Reader/BitcodeReader.cpp | 109 +++++++++++++++++++++++++-- lib/Bitcode/Reader/BitcodeReader.h | 36 +++++++-- 2 files changed, 132 insertions(+), 13 deletions(-) diff --git a/lib/Bitcode/Reader/BitcodeReader.cpp b/lib/Bitcode/Reader/BitcodeReader.cpp index a842bd3643d..ee9e1154120 100644 --- a/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/lib/Bitcode/Reader/BitcodeReader.cpp @@ -133,6 +133,15 @@ namespace { : ConstantExpr(Ty, Instruction::UserOp1, &Op<0>(), 1) { Op<0>() = UndefValue::get(Type::Int32Ty); } + + /// @brief Methods to support type inquiry through isa, cast, and dyn_cast. + static inline bool classof(const ConstantPlaceHolder *) { return true; } + static bool classof(const Value *V) { + return isa(V) && + cast(V)->getOpcode() == Instruction::UserOp1; + } + + /// Provide fast operand accessors DECLARE_TRANSPARENT_OPERAND_ACCESSORS(Value); }; @@ -205,6 +214,85 @@ Value *BitcodeReaderValueList::getValueFwdRef(unsigned Idx, const Type *Ty) { return V; } +/// ResolveConstantForwardRefs - Once all constants are read, this method bulk +/// resolves any forward references. The idea behind this is that we sometimes +/// get constants (such as large arrays) which reference *many* forward ref +/// constants. Replacing each of these causes a lot of thrashing when +/// building/reuniquing the constant. Instead of doing this, we look at all the +/// uses and rewrite all the place holders at once for any constant that uses +/// a placeholder. +void BitcodeReaderValueList::ResolveConstantForwardRefs() { + // Sort the values by-pointer so that they are efficient to look up with a + // binary search. + std::sort(ResolveConstants.begin(), ResolveConstants.end()); + + SmallVector NewOps; + + while (!ResolveConstants.empty()) { + Value *RealVal = getOperand(ResolveConstants.back().second); + Constant *Placeholder = ResolveConstants.back().first; + ResolveConstants.pop_back(); + + // Loop over all users of the placeholder, updating them to reference the + // new value. If they reference more than one placeholder, update them all + // at once. + while (!Placeholder->use_empty()) { + User *U = Placeholder->use_back(); + // If the using object isn't uniqued, just update the operands. This + // handles instructions and initializers for global variables. + if (!isa(U) || isa(U)) { + U->replaceUsesOfWith(Placeholder, RealVal); + continue; + } + + // Otherwise, we have a constant that uses the placeholder. Replace that + // constant with a new constant that has *all* placeholder uses updated. + Constant *UserC = cast(U); + for (User::op_iterator I = UserC->op_begin(), E = UserC->op_end(); + I != E; ++I) { + Value *NewOp; + if (!isa(*I)) { + // Not a placeholder reference. + NewOp = *I; + } else if (*I == Placeholder) { + // Common case is that it just references this one placeholder. + NewOp = RealVal; + } else { + // Otherwise, look up the placeholder in ResolveConstants. + ResolveConstantsTy::iterator It = + std::lower_bound(ResolveConstants.begin(), ResolveConstants.end(), + std::pair(cast(*I), + 0)); + assert(It != ResolveConstants.end() && It->first == *I); + NewOp = this->getOperand(It->second); + } + + NewOps.push_back(cast(NewOp)); + } + + // Make the new constant. + Constant *NewC; + if (ConstantArray *UserCA = dyn_cast(UserC)) { + NewC = ConstantArray::get(UserCA->getType(), &NewOps[0], NewOps.size()); + } else if (isa(UserC)) { + NewC = ConstantStruct::get(&NewOps[0], NewOps.size()); + } else if (isa(UserC)) { + NewC = ConstantVector::get(&NewOps[0], NewOps.size()); + } else { + // Must be a constant expression. + NewC = cast(UserC)->getWithOperands(&NewOps[0], + NewOps.size()); + } + + UserC->replaceAllUsesWith(NewC); + UserC->destroyConstant(); + NewOps.clear(); + } + + delete Placeholder; + } +} + const Type *BitcodeReader::getTypeByID(unsigned ID, bool isTypeTable) { // If the TypeID is in range, return it. @@ -602,14 +690,8 @@ bool BitcodeReader::ParseConstants() { unsigned NextCstNo = ValueList.size(); while (1) { unsigned Code = Stream.ReadCode(); - if (Code == bitc::END_BLOCK) { - if (NextCstNo != ValueList.size()) - return Error("Invalid constant reference!"); - - if (Stream.ReadBlockEnd()) - return Error("Error at end of constants block"); - return false; - } + if (Code == bitc::END_BLOCK) + break; if (Code == bitc::ENTER_SUBBLOCK) { // No known subblocks, always skip them. @@ -852,6 +934,17 @@ bool BitcodeReader::ParseConstants() { ValueList.AssignValue(V, NextCstNo); ++NextCstNo; } + + if (NextCstNo != ValueList.size()) + return Error("Invalid constant reference!"); + + if (Stream.ReadBlockEnd()) + return Error("Error at end of constants block"); + + // Once all the constants have been read, go through and resolve forward + // references. + ValueList.ResolveConstantForwardRefs(); + return false; } /// RememberAndSkipFunctionBody - When we see the block for a function body, diff --git a/lib/Bitcode/Reader/BitcodeReader.h b/lib/Bitcode/Reader/BitcodeReader.h index 9f62efec293..7bd05cfd2d2 100644 --- a/lib/Bitcode/Reader/BitcodeReader.h +++ b/lib/Bitcode/Reader/BitcodeReader.h @@ -32,9 +32,22 @@ namespace llvm { class BitcodeReaderValueList : public User { unsigned Capacity; + + /// ResolveConstants - As we resolve forward-referenced constants, we add + /// information about them to this vector. This allows us to resolve them in + /// bulk instead of resolving each reference at a time. See the code in + /// ResolveConstantForwardRefs for more information about this. + /// + /// The key of this vector is the placeholder constant, the value is the slot + /// number that holds the resolved value. + typedef std::vector > ResolveConstantsTy; + ResolveConstantsTy ResolveConstants; public: BitcodeReaderValueList() : User(Type::VoidTy, Value::ArgumentVal, 0, 0) , Capacity(0) {} + ~BitcodeReaderValueList() { + assert(ResolveConstants.empty() && "Constants not resolved?"); + } /// Provide fast operand accessors DECLARE_TRANSPARENT_OPERAND_ACCESSORS(Value); @@ -50,6 +63,7 @@ public: } void clear() { + assert(ResolveConstants.empty() && "Constants not resolved?"); if (OperandList) dropHungoffUses(OperandList); Capacity = 0; } @@ -73,15 +87,26 @@ public: if (Idx == size()) { push_back(V); } else if (Value *OldV = getOperand(Idx)) { - // If there was a forward reference to this value, replace it. - setOperand(Idx, V); - OldV->replaceAllUsesWith(V); - delete OldV; + // Handle constants and non-constants (e.g. instrs) differently for + // efficiency. + if (Constant *PHC = dyn_cast(OldV)) { + ResolveConstants.push_back(std::make_pair(PHC, Idx)); + setOperand(Idx, V); + } else { + // If there was a forward reference to this value, replace it. + setOperand(Idx, V); + OldV->replaceAllUsesWith(V); + delete OldV; + } } else { initVal(Idx, V); } } + /// ResolveConstantForwardRefs - Once all constants are read, this method bulk + /// resolves any forward references. + void ResolveConstantForwardRefs(); + private: void initVal(unsigned Idx, Value *V) { if (Idx >= size()) { @@ -94,7 +119,8 @@ private: }; template <> -struct OperandTraits : HungoffOperandTraits { +struct OperandTraits + : HungoffOperandTraits { }; DEFINE_TRANSPARENT_OPERAND_ACCESSORS(BitcodeReaderValueList, Value)