aboutsummaryrefslogtreecommitdiff
path: root/llvm/tools/llvm-reduce/deltas/ReduceDistinctMetadata.cpp
blob: 833a941b89f44aca8b8b4b9028fd4ec6f08aa492 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
//===- ReduceDistinctMetadata.cpp - Specialized Delta Pass ----------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file implements two functions used by the Generic Delta Debugging
// Algorithm, which are used to reduce unnamed distinct metadata nodes.
//
//===----------------------------------------------------------------------===//

#include "ReduceDistinctMetadata.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/SmallVector.h"
#include <queue>

using namespace llvm;

// Traverse the graph breadth-first and try to remove unnamed metadata nodes
static void
reduceNodes(MDNode *Root,
            SetVector<std::pair<unsigned int, MDNode *>> &NodesToDelete,
            MDNode *TemporaryNode, Oracle &O, Module &Program) {
  std::queue<MDNode *> NodesToTraverse{};
  // Keep track of visited nodes not to get into loops
  SetVector<MDNode *> VisitedNodes{};
  NodesToTraverse.push(Root);

  while (!NodesToTraverse.empty()) {
    MDNode *CurrentNode = NodesToTraverse.front();
    NodesToTraverse.pop();

    // Mark the nodes for removal
    for (unsigned int I = 0; I < CurrentNode->getNumOperands(); ++I) {
      if (MDNode *Operand =
              dyn_cast_or_null<MDNode>(CurrentNode->getOperand(I).get())) {
        // Check whether node has been visited
        if (VisitedNodes.insert(Operand))
          NodesToTraverse.push(Operand);
        // Delete the node only if it is distinct
        if (Operand->isDistinct()) {
          // Add to removal list
          NodesToDelete.insert(std::make_pair(I, CurrentNode));
        }
      }
    }

    // Remove the nodes
    for (auto [PositionToReplace, Node] : NodesToDelete) {
      if (!O.shouldKeep())
        Node->replaceOperandWith(PositionToReplace, TemporaryNode);
    }
    NodesToDelete.clear();
  }
}

// After reducing metadata, we need to remove references to the temporary node,
// this is also done with BFS
static void cleanUpTemporaries(NamedMDNode &NamedNode, MDTuple *TemporaryTuple,
                               Module &Program) {
  std::queue<MDTuple *> NodesToTraverse{};
  SetVector<MDTuple *> VisitedNodes{};

  // Push all first level operands of the named node to the queue
  for (auto I = NamedNode.op_begin(); I != NamedNode.op_end(); ++I) {
    // If the node hasn't been traversed yet, add it to the queue of nodes to
    // traverse.
    if (MDTuple *TupleI = dyn_cast_or_null<MDTuple>((*I))) {
      if (VisitedNodes.insert(TupleI))
        NodesToTraverse.push(TupleI);
    }
  }

  while (!NodesToTraverse.empty()) {
    MDTuple *CurrentTuple = NodesToTraverse.front();
    NodesToTraverse.pop();

    // Shift all of the interesting elements to the left, pop remaining
    // afterwards
    if (CurrentTuple->isDistinct()) {
      // Do resizing and cleaning operations only if the node is distinct,
      // as resizing is not supported for unique nodes and is redundant.
      unsigned int NotToRemove = 0;
      for (unsigned int I = 0; I < CurrentTuple->getNumOperands(); ++I) {
        Metadata *Operand = CurrentTuple->getOperand(I).get();
        // If current operand is not the temporary node, move it to the front
        // and increase notToRemove so that it will be saved
        if (Operand != TemporaryTuple) {
          Metadata *TemporaryMetadata =
              CurrentTuple->getOperand(NotToRemove).get();
          CurrentTuple->replaceOperandWith(NotToRemove, Operand);
          CurrentTuple->replaceOperandWith(I, TemporaryMetadata);
          ++NotToRemove;
        }
      }

      // Remove all the uninteresting elements
      unsigned int OriginalOperands = CurrentTuple->getNumOperands();
      for (unsigned int I = 0; I < OriginalOperands - NotToRemove; ++I)
        CurrentTuple->pop_back();
    }

    // Push the remaining nodes into the queue
    for (unsigned int I = 0; I < CurrentTuple->getNumOperands(); ++I) {
      MDTuple *Operand =
          dyn_cast_or_null<MDTuple>(CurrentTuple->getOperand(I).get());
      if (Operand && VisitedNodes.insert(Operand))
        // If the node hasn't been traversed yet, add it to the queue of nodes
        // to traverse.
        NodesToTraverse.push(Operand);
    }
  }
}

void llvm::reduceDistinctMetadataDeltaPass(Oracle &O,
                                           ReducerWorkItem &WorkItem) {
  Module &Program = WorkItem.getModule();
  MDTuple *TemporaryTuple =
      MDTuple::getDistinct(Program.getContext(), SmallVector<Metadata *, 1>{});
  SetVector<std::pair<unsigned int, MDNode *>> NodesToDelete{};
  for (NamedMDNode &NamedNode :
       Program.named_metadata()) { // Iterate over the named nodes
    for (unsigned int I = 0; I < NamedNode.getNumOperands();
         ++I) { // Iterate over first level unnamed nodes..
      if (MDTuple *Operand = dyn_cast_or_null<MDTuple>(NamedNode.getOperand(I)))
        reduceNodes(Operand, NodesToDelete, TemporaryTuple, O, Program);
    }
  }
  for (NamedMDNode &NamedNode : Program.named_metadata())
    cleanUpTemporaries(NamedNode, TemporaryTuple, Program);
}