diff options
-rw-r--r-- | llvm/lib/Transforms/Utils/ValueMapper.cpp | 437 | ||||
-rw-r--r-- | llvm/unittests/Transforms/Utils/ValueMapperTest.cpp | 45 |
2 files changed, 374 insertions, 108 deletions
diff --git a/llvm/lib/Transforms/Utils/ValueMapper.cpp b/llvm/lib/Transforms/Utils/ValueMapper.cpp index 8d3fc94d08a..18b9df0211a 100644 --- a/llvm/lib/Transforms/Utils/ValueMapper.cpp +++ b/llvm/lib/Transforms/Utils/ValueMapper.cpp @@ -58,7 +58,10 @@ struct DelayedBasicBlock { TempBB(BasicBlock::Create(Old.getContext())) {} }; +class MDNodeMapper; class Mapper { + friend class MDNodeMapper; + ValueToValueMapTy &VM; RemapFlags Flags; ValueMapTypeRemapper *TypeMapper; @@ -66,7 +69,6 @@ class Mapper { SmallVector<DelayedGlobalValueInit, 8> DelayedInits; SmallVector<DelayedBasicBlock, 1> DelayedBBs; - SmallVector<MDNode *, 8> DistinctWorklist; public: Mapper(ValueToValueMapTy &VM, RemapFlags Flags, @@ -87,39 +89,146 @@ public: private: Value *mapBlockAddress(const BlockAddress &BA); - /// Map metadata helper. - /// - /// Co-recursively finds the mapping for MD. If this returns an MDNode, it's - /// possible that MDNode::isResolved() will return false. - Metadata *mapMetadataImpl(const Metadata *MD); - Metadata *mapMetadataOp(Metadata *Op); - /// Map metadata that doesn't require visiting operands. Optional<Metadata *> mapSimpleMetadata(const Metadata *MD); - /// Remap the operands of an MDNode. + Metadata *mapToMetadata(const Metadata *Key, Metadata *Val); + Metadata *mapToSelf(const Metadata *MD); +}; + +class MDNodeMapper { + Mapper &M; + + struct Data { + bool HasChangedOps = false; + bool HasChangedAddress = false; + unsigned ID = ~0u; + TempMDNode Placeholder; + }; + + SmallDenseMap<const Metadata *, Data, 32> Info; + SmallVector<std::pair<MDNode *, bool>, 16> Worklist; + SmallVector<MDNode *, 16> POT; + +public: + MDNodeMapper(Mapper &M) : M(M) {} + + /// Map a metadata node (and its transitive operands). + /// + /// This is the only entry point into MDNodeMapper. It works as follows: + /// + /// 1. \a createPOT(): use a worklist to perform a post-order traversal of + /// the transitively referenced unmapped nodes. /// - /// If \c Node is temporary, uniquing cycles are ignored. If \c Node is - /// distinct, uniquing cycles are resolved as they're found. + /// 2. \a propagateChangedOperands(): track which nodes will change + /// operands, and which will have new addresses in the mapped scheme. + /// Propagate the changes through the POT until fixed point, to pick up + /// uniquing cycles that need to change. /// - /// \pre \c Node.isDistinct() or \c Node.isTemporary(). - bool remapOperands(MDNode &Node); + /// 3. \a mapDistinctNodes(): map all the distinct nodes without touching + /// their operands. If RF_MoveDistinctMetadata, they get mapped to + /// themselves; otherwise, they get mapped to clones. + /// + /// 4. \a mapUniquedNodes(): map the uniqued nodes (bottom-up), lazily + /// creating temporaries for forward references as needed. + /// + /// 5. \a remapDistinctOperands(): remap the operands of the distinct nodes. + Metadata *map(const MDNode &FirstN); + +private: + /// Return \c true as long as there's work to do. + bool hasWork() const { return !Worklist.empty(); } - /// Map a distinct MDNode. + /// Get the current node in the worklist. + MDNode &getCurrentNode() const { return *Worklist.back().first; } + + /// Push a node onto the worklist. + /// + /// Adds \c N to \a Worklist and \a Info, unless it's already inserted. If + /// \c N.isDistinct(), \a Data::HasChangedAddress will be set based on \a + /// RF_MoveDistinctMDs. + /// + /// Returns the data for the node. /// - /// Whether distinct nodes change is independent of their operands. If \a - /// RF_MoveDistinctMDs, then they are reused, and their operands remapped in - /// place; effectively, they're moved from one graph to another. Otherwise, - /// they're cloned/duplicated, and the new copy's operands are remapped. - Metadata *mapDistinctNode(const MDNode *Node); + /// \post Data::HasChangedAddress iff !RF_MoveDistinctMDs && N.isDistinct(). + /// \post Worklist.back().first == &N. + /// \post Worklist.back().second == false. + Data &push(const MDNode &N); - /// Map a uniqued MDNode. + /// Map a node operand, and return true if it changes. /// - /// Uniqued nodes may not need to be recreated (they may map to themselves). - Metadata *mapUniquedNode(const MDNode *Node); + /// \post getMappedOp(Op) does not return None. + bool mapOperand(const Metadata *Op); - Metadata *mapToMetadata(const Metadata *Key, Metadata *Val); - Metadata *mapToSelf(const Metadata *MD); + /// Get a previously mapped node. + Optional<Metadata *> getMappedOp(const Metadata *Op) const; + + /// Try to pop a node off the worklist and store it in POT. + /// + /// Returns \c true if it popped; \c false if its operands need to be + /// visited. + /// + /// \post If Worklist.back().second == false: Worklist.back().second == true. + /// \post Else: Worklist.back() has been popped off and added to \a POT. + bool tryToPop(); + + /// Get a forward reference to a node to use as an operand. + /// + /// Returns \c Op if it's not changing; otherwise, lazily creates a temporary + /// node and returns it. + Metadata &getFwdReference(const Data &D, MDNode &Op); + + /// Create a post-order traversal from the given node. + /// + /// This traverses the metadata graph deeply enough to map \c FirstN. It + /// uses \a mapOperand() (indirectly, \a Mapper::mapSimplifiedNode()), so any + /// metadata that has already been mapped will not be part of the POT. + /// + /// \post \a POT is a post-order traversal ending with \c FirstN. + bool createPOT(const MDNode &FirstN); + + /// Propagate changed operands through post-order traversal. + /// + /// Until fixed point, iteratively update: + /// + /// - \a Data::HasChangedOps based on \a Data::HasChangedAddress of operands; + /// - \a Data::HasChangedAddress based on Data::HasChangedOps. + /// + /// This algorithm never changes \a Data::HasChangedAddress for distinct + /// nodes. + /// + /// \post \a POT is a post-order traversal ending with \c FirstN. + void propagateChangedOperands(); + + /// Map all distinct nodes in POT. + /// + /// \post \a getMappedOp() returns the correct node for every distinct node. + void mapDistinctNodes(); + + /// Map all uniqued nodes in POT with the correct operands. + /// + /// \pre Distinct nodes are mapped (\a mapDistinctNodes() has been called). + /// \post \a getMappedOp() returns the correct node for every node. + /// \post \a MDNode::operands() is correct for every uniqued node. + /// \post \a MDNode::isResolved() returns true for every node. + void mapUniquedNodes(); + + /// Re-map the operands for distinct nodes in POT. + /// + /// \pre Distinct nodes are mapped (\a mapDistinctNodes() has been called). + /// \pre Uniqued nodes are mapped (\a mapUniquedNodes() has been called). + /// \post \a MDNode::operands() is correct for every distinct node. + void remapDistinctOperands(); + + /// Remap a node's operands. + /// + /// Iterate through operands and update them in place using \a getMappedOp() + /// and \a getFwdReference(). + /// + /// \pre N.isDistinct() or N.isTemporary(). + /// \pre Distinct nodes are mapped (\a mapDistinctNodes() has been called). + /// \pre If \c N is distinct, all uniqued nodes are already mapped. + void remapOperands(const Data &D, MDNode &N); }; } // end namespace @@ -280,78 +389,218 @@ Metadata *Mapper::mapToSelf(const Metadata *MD) { return mapToMetadata(MD, const_cast<Metadata *>(MD)); } -Metadata *Mapper::mapMetadataOp(Metadata *Op) { +bool MDNodeMapper::mapOperand(const Metadata *Op) { + if (!Op) + return false; + + if (Optional<Metadata *> MappedOp = M.mapSimpleMetadata(Op)) { + assert(M.VM.getMappedMD(Op) && "Expected result to be memoized"); + return *MappedOp != Op; + } + + return push(*cast<MDNode>(Op)).HasChangedAddress; +} + +Optional<Metadata *> MDNodeMapper::getMappedOp(const Metadata *Op) const { if (!Op) return nullptr; - if (Metadata *MappedOp = mapMetadataImpl(Op)) - return MappedOp; - // Use identity map if MappedOp is null and we can ignore missing entries. - if (Flags & RF_IgnoreMissingEntries) + if (Optional<Metadata *> MappedOp = M.VM.getMappedMD(Op)) + return *MappedOp; + + return None; +} + +Metadata &MDNodeMapper::getFwdReference(const Data &D, MDNode &Op) { + auto Where = Info.find(&Op); + assert(Where != Info.end() && "Expected a valid reference"); + + auto &OpD = Where->second; + assert(OpD.ID > D.ID && "Expected a forward reference"); + + if (!OpD.HasChangedAddress) return Op; - return nullptr; + // Lazily construct a temporary node. + if (!OpD.Placeholder) + OpD.Placeholder = Op.clone(); + + return *OpD.Placeholder; } -/// Resolve uniquing cycles involving the given metadata. -static void resolveCycles(Metadata *MD) { - if (auto *N = dyn_cast_or_null<MDNode>(MD)) - if (!N->isResolved()) - N->resolveCycles(); +void MDNodeMapper::remapOperands(const Data &D, MDNode &N) { + for (unsigned I = 0, E = N.getNumOperands(); I != E; ++I) { + Metadata *Old = N.getOperand(I); + Metadata *New; + if (Optional<Metadata *> MappedOp = getMappedOp(Old)){ + New = *MappedOp; + } else { + assert(!N.isDistinct() && + "Expected all nodes to be pre-mapped for distinct operands"); + MDNode &OldN = *cast<MDNode>(Old); + assert(!OldN.isDistinct() && "Expected distinct nodes to be pre-mapped"); + New = &getFwdReference(D, OldN); + } + + if (Old != New) + N.replaceOperandWith(I, New); + } +} + +MDNodeMapper::Data &MDNodeMapper::push(const MDNode &N) { + auto Insertion = Info.insert(std::make_pair(&N, Data())); + auto &D = Insertion.first->second; + if (!Insertion.second) + return D; + + // Add to the worklist; check for distinct nodes that are required to be + // copied. + Worklist.push_back(std::make_pair(&const_cast<MDNode &>(N), false)); + D.HasChangedAddress = !(M.Flags & RF_MoveDistinctMDs) && N.isDistinct(); + return D; } -bool Mapper::remapOperands(MDNode &Node) { - assert(!Node.isUniqued() && "Expected temporary or distinct node"); - const bool IsDistinct = Node.isDistinct(); - - bool AnyChanged = false; - for (unsigned I = 0, E = Node.getNumOperands(); I != E; ++I) { - Metadata *Old = Node.getOperand(I); - Metadata *New = mapMetadataOp(Old); - if (Old != New) { - AnyChanged = true; - Node.replaceOperandWith(I, New); - - // Resolve uniquing cycles underneath distinct nodes on the fly so they - // don't infect later operands. - if (IsDistinct) - resolveCycles(New); +bool MDNodeMapper::tryToPop() { + if (!Worklist.back().second) { + Worklist.back().second = true; + return false; + } + + MDNode *N = Worklist.pop_back_val().first; + Info[N].ID = POT.size(); + POT.push_back(N); + return true; +} + +bool MDNodeMapper::createPOT(const MDNode &FirstN) { + bool AnyChanges = false; + + // Do a traversal of the unmapped subgraph, tracking whether operands change. + // In some cases, these changes will propagate naturally, but + // propagateChangedOperands() catches the general case. + AnyChanges |= push(FirstN).HasChangedAddress; + while (hasWork()) { + if (tryToPop()) + continue; + + MDNode &N = getCurrentNode(); + bool LocalChanges = false; + for (const Metadata *Op : N.operands()) + LocalChanges |= mapOperand(Op); + + if (!LocalChanges) + continue; + + AnyChanges = true; + auto &D = Info[&N]; + D.HasChangedOps = true; + + // Uniqued nodes change address when operands change. + if (!N.isDistinct()) + D.HasChangedAddress = true; + } + return AnyChanges; +} + +void MDNodeMapper::propagateChangedOperands() { + bool AnyChangedAddresses; + do { + AnyChangedAddresses = false; + for (MDNode *N : POT) { + auto &NI = Info[N]; + if (NI.HasChangedOps) + continue; + + if (!llvm::any_of(N->operands(), [&](const Metadata *Op) { + auto Where = Info.find(Op); + return Where != Info.end() && Where->second.HasChangedAddress; + })) + continue; + + NI.HasChangedOps = true; + if (!N->isDistinct()) { + NI.HasChangedAddress = true; + AnyChangedAddresses = true; + } } + } while (AnyChangedAddresses); +} + +void MDNodeMapper::mapDistinctNodes() { + // Map all the distinct nodes in POT. + for (MDNode *N : POT) { + if (!N->isDistinct()) + continue; + + if (M.Flags & RF_MoveDistinctMDs) + M.mapToSelf(N); + else + M.mapToMetadata(N, MDNode::replaceWithDistinct(N->clone())); } +} + +void MDNodeMapper::mapUniquedNodes() { + // Construct uniqued nodes, building forward references as necessary. + for (auto *N : POT) { + if (N->isDistinct()) + continue; + + auto &D = Info[N]; + assert(D.HasChangedAddress == D.HasChangedOps && + "Uniqued nodes should change address iff ops change"); + if (!D.HasChangedAddress) { + M.mapToSelf(N); + continue; + } - return AnyChanged; + TempMDNode ClonedN = D.Placeholder ? std::move(D.Placeholder) : N->clone(); + remapOperands(D, *ClonedN); + M.mapToMetadata(N, MDNode::replaceWithUniqued(std::move(ClonedN))); + } + + // Resolve cycles. + for (auto *N : POT) + if (!N->isResolved()) + N->resolveCycles(); } -Metadata *Mapper::mapDistinctNode(const MDNode *Node) { - assert(Node->isDistinct() && "Expected distinct node"); +void MDNodeMapper::remapDistinctOperands() { + for (auto *N : POT) { + if (!N->isDistinct()) + continue; - MDNode *NewMD; - if (Flags & RF_MoveDistinctMDs) - NewMD = const_cast<MDNode *>(Node); - else - NewMD = MDNode::replaceWithDistinct(Node->clone()); + auto &D = Info[N]; + if (!D.HasChangedOps) + continue; - // Remap operands later. - DistinctWorklist.push_back(NewMD); - return mapToMetadata(Node, NewMD); + assert(D.HasChangedAddress == !bool(M.Flags & RF_MoveDistinctMDs) && + "Distinct nodes should change address iff they cannot be moved"); + remapOperands(D, D.HasChangedAddress ? *cast<MDNode>(*getMappedOp(N)) : *N); + } } -Metadata *Mapper::mapUniquedNode(const MDNode *Node) { - assert(Node->isUniqued() && "Expected uniqued node"); - - // Create a temporary node and map it upfront in case we have a uniquing - // cycle. If necessary, this mapping will get updated by RAUW logic before - // returning. - auto ClonedMD = Node->clone(); - mapToMetadata(Node, ClonedMD.get()); - if (!remapOperands(*ClonedMD)) { - // No operands changed, so use the original. - ClonedMD->replaceAllUsesWith(const_cast<MDNode *>(Node)); - return const_cast<MDNode *>(Node); +Metadata *MDNodeMapper::map(const MDNode &FirstN) { + assert(!(M.Flags & RF_NoModuleLevelChanges) && + "MDNodeMapper::map assumes module-level changes"); + assert(POT.empty() && "MDNodeMapper::map is not re-entrant"); + + // Require resolved nodes whenever metadata might be remapped. + assert(FirstN.isResolved() && "Unexpected unresolved node"); + + // Return early if nothing at all changed. + if (!createPOT(FirstN)) { + for (const MDNode *N : POT) + M.mapToSelf(N); + return &const_cast<MDNode &>(FirstN); } - // Uniquify the cloned node. - return MDNode::replaceWithUniqued(std::move(ClonedMD)); + propagateChangedOperands(); + mapDistinctNodes(); + mapUniquedNodes(); + remapDistinctOperands(); + + // Return the original node, remapped. + return *getMappedOp(&FirstN); } Optional<Metadata *> Mapper::mapSimpleMetadata(const Metadata *MD) { @@ -389,21 +638,6 @@ Optional<Metadata *> Mapper::mapSimpleMetadata(const Metadata *MD) { return None; } -Metadata *Mapper::mapMetadataImpl(const Metadata *MD) { - assert(VM.mayMapMetadata() && "Unexpected co-recursion through mapValue"); - if (Optional<Metadata *> NewMD = mapSimpleMetadata(MD)) - return *NewMD; - - // Require resolved nodes whenever metadata might be remapped. - auto *Node = cast<MDNode>(MD); - assert(Node->isResolved() && "Unexpected unresolved node"); - - if (Node->isDistinct()) - return mapDistinctNode(Node); - - return mapUniquedNode(Node); -} - Metadata *llvm::MapMetadata(const Metadata *MD, ValueToValueMapTy &VM, RemapFlags Flags, ValueMapTypeRemapper *TypeMapper, ValueMaterializer *Materializer) { @@ -411,25 +645,13 @@ Metadata *llvm::MapMetadata(const Metadata *MD, ValueToValueMapTy &VM, } Metadata *Mapper::mapMetadata(const Metadata *MD) { - Metadata *NewMD = mapMetadataImpl(MD); - - // When there are no module-level changes, it's possible that the metadata - // graph has temporaries. Skip the logic to resolve cycles, since it's - // unnecessary (and invalid) in that case. - if (Flags & RF_NoModuleLevelChanges) - return NewMD; - - // Resolve cycles involving the entry metadata. - resolveCycles(NewMD); + if (Optional<Metadata *> NewMD = mapSimpleMetadata(MD)) + return *NewMD; - return NewMD; + return MDNodeMapper(*this).map(*cast<MDNode>(MD)); } Mapper::~Mapper() { - // Remap the operands of distinct MDNodes. - while (!DistinctWorklist.empty()) - remapOperands(*DistinctWorklist.pop_back_val()); - // Materialize global initializers. while (!DelayedInits.empty()) { auto Init = DelayedInits.pop_back_val(); @@ -443,8 +665,7 @@ Mapper::~Mapper() { DBB.TempBB->replaceAllUsesWith(BB ? BB : DBB.OldBB); } - // We don't expect any of these to grow after clearing. - assert(DistinctWorklist.empty()); + // We don't expect these to grow after clearing. assert(DelayedInits.empty()); assert(DelayedBBs.empty()); } diff --git a/llvm/unittests/Transforms/Utils/ValueMapperTest.cpp b/llvm/unittests/Transforms/Utils/ValueMapperTest.cpp index 3c7ef1b686a..865cb5d0d7c 100644 --- a/llvm/unittests/Transforms/Utils/ValueMapperTest.cpp +++ b/llvm/unittests/Transforms/Utils/ValueMapperTest.cpp @@ -16,6 +16,51 @@ using namespace llvm; namespace { +TEST(ValueMapperTest, MapMetadata) { + LLVMContext Context; + auto *U = MDTuple::get(Context, None); + + // The node should be unchanged. + ValueToValueMapTy VM; + EXPECT_EQ(U, MapMetadata(U, VM, RF_None)); +} + +TEST(ValueMapperTest, MapMetadataCycle) { + LLVMContext Context; + MDNode *U0; + MDNode *U1; + { + Metadata *Ops[] = {nullptr}; + auto T = MDTuple::getTemporary(Context, Ops); + Ops[0] = T.get(); + U0 = MDTuple::get(Context, Ops); + T->replaceOperandWith(0, U0); + U1 = MDNode::replaceWithUniqued(std::move(T)); + U0->resolveCycles(); + } + + EXPECT_TRUE(U0->isResolved()); + EXPECT_TRUE(U0->isUniqued()); + EXPECT_TRUE(U1->isResolved()); + EXPECT_TRUE(U1->isUniqued()); + EXPECT_EQ(U1, U0->getOperand(0)); + EXPECT_EQ(U0, U1->getOperand(0)); + + // Cycles shouldn't be duplicated. + { + ValueToValueMapTy VM; + EXPECT_EQ(U0, MapMetadata(U0, VM, RF_None)); + EXPECT_EQ(U1, MapMetadata(U1, VM, RF_None)); + } + + // Check the other order. + { + ValueToValueMapTy VM; + EXPECT_EQ(U1, MapMetadata(U1, VM, RF_None)); + EXPECT_EQ(U0, MapMetadata(U0, VM, RF_None)); + } +} + TEST(ValueMapperTest, MapMetadataUnresolved) { LLVMContext Context; TempMDTuple T = MDTuple::getTemporary(Context, None); |