Skip to content

Commit

Permalink
[llvm-reduce] Reduce metadata references.
Browse files Browse the repository at this point in the history
The ReduceMetadata pass before this patch removed metadata on a per-MDNode (or NamedMDNode) basis. Either all references to an MDNode are kept, or all of them are removed. However, MDNodes are uniqued, meaning that references to MDNodes with the same data become references to the same MDNodes. As a consequence, e.g. tbaa references to the same type will all have the same MDNode reference and hence make it impossible to reduce only keeping metadata on those memory access for which they are interesting.
Moreover, MDNodes can also be referenced by some intrinsics or other MDNodes. These references were not considered for removal leading to the possibility that MDNodes are not actually removed even if selected to be removed by the oracle.

This patch changes ReduceMetadata to reduces based on removable metadata references instead. MDNodes without references implicitly dropped anyway. References by intrinsic calls should be removed by ReduceOperands or ReduceInstructions. References in other MDNodes cannot be removed as it would violate the immutability of MDNodes.

Additionally, ReduceMetadata pass before this patch used `setMetadata(I, NULL)` to remove references, where `I` is the index in the array returned by `getAllMetadata`. However, `setMetadata` expects a MDKind (such as `MD_tbaa`) as first argument. `getAllMetadata` does not return those in consecutive order (otherwise it would not need to be a `std::pair` with `first` representing the MDKind).

Reviewed By: aeubanks, swamulism

Differential Revision: https://reviews.llvm.org/D110534
  • Loading branch information
Meinersbur committed Sep 29, 2021
1 parent 26db178 commit d9562a8
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 77 deletions.
29 changes: 29 additions & 0 deletions llvm/test/tools/llvm-reduce/remove-metadata-args.ll
@@ -0,0 +1,29 @@
; RUN: llvm-reduce %s -o %t --delta-passes=metadata --test FileCheck --test-arg %s --test-arg --check-prefix=EXCITING --test-arg --input-file
; RUN: FileCheck %s --input-file %t --check-prefix=REDUCED

; All exciting stuff must remain in the reduced file.
; EXCITING-DAG: ExcitingGlobal = global i32 0, !md !0
; EXCITING-DAG: define void @ExcitingFunc() !md !0
; EXCITING-DAG: store i32 0, i32* @ExcitingGlobal, align 4, !md !0
; EXCITING-DAG: !ExcitingNamedMD = !{!0}

; Boring stuff's metadata must have been removed.
; REDUCED-NOT: Boring{{.*}} !md !0
; REDUCED-NOT: !md !0 {{.*}}Boring


@ExcitingGlobal = global i32 0, !md !0
@BoringGlobal = global i32 0, !md !0

define void @ExcitingFunc() !md !0 {
store i32 0, i32* @ExcitingGlobal, align 4, !md !0
store i32 0, i32* @BoringGlobal, align 4, !md !0
ret void
}

declare !md !0 void @BoringFunc()

!ExcitingNamedMD = !{!0}
!BoringNamedMD = !{!0}

!0 = !{!"my metadata"}
127 changes: 50 additions & 77 deletions llvm/tools/llvm-reduce/deltas/ReduceMetadata.cpp
Expand Up @@ -13,110 +13,83 @@

#include "ReduceMetadata.h"
#include "Delta.h"
#include "llvm/ADT/Sequence.h"
#include "llvm/ADT/SmallVector.h"
#include <set>
#include "llvm/IR/InstIterator.h"
#include <vector>

using namespace llvm;

/// Adds all Unnamed Metadata Nodes that are inside desired Chunks to set
template <class T>
static void getChunkMetadataNodes(T &MDUser, Oracle &O,
std::set<MDNode *> &SeenNodes,
std::set<MDNode *> &NodesToKeep) {
SmallVector<std::pair<unsigned, MDNode *>, 4> MDs;
MDUser.getAllMetadata(MDs);
for (auto &MD : MDs) {
SeenNodes.insert(MD.second);
if (O.shouldKeep())
NodesToKeep.insert(MD.second);
}
}

/// Erases out-of-chunk unnamed metadata nodes from its user
template <class T>
static void eraseMetadataIfOutsideChunk(T &MDUser,
const std::set<MDNode *> &NodesToKeep) {
SmallVector<std::pair<unsigned, MDNode *>, 4> MDs;
MDUser.getAllMetadata(MDs);
for (int I = 0, E = MDs.size(); I != E; ++I)
if (!NodesToKeep.count(MDs[I].second))
MDUser.setMetadata(I, NULL);
}

/// Removes all the Named and Unnamed Metadata Nodes, as well as any debug
/// functions that aren't inside the desired Chunks.
static void extractMetadataFromModule(const std::vector<Chunk> &ChunksToKeep,
Module *Program) {
Oracle O(ChunksToKeep);

std::set<MDNode *> SeenNodes;
std::set<MDNode *> NodesToKeep;

// Add chunk MDNodes used by GVs, Functions, and Instructions to set
for (auto &GV : Program->globals())
getChunkMetadataNodes(GV, O, SeenNodes, NodesToKeep);

for (auto &F : *Program) {
getChunkMetadataNodes(F, O, SeenNodes, NodesToKeep);
for (auto &BB : F)
for (auto &Inst : BB)
getChunkMetadataNodes(Inst, O, SeenNodes, NodesToKeep);
}

// Once more, go over metadata nodes, but deleting the ones outside chunks
for (auto &GV : Program->globals())
eraseMetadataIfOutsideChunk(GV, NodesToKeep);

for (auto &F : *Program) {
eraseMetadataIfOutsideChunk(F, NodesToKeep);
for (auto &BB : F)
for (auto &Inst : BB)
eraseMetadataIfOutsideChunk(Inst, NodesToKeep);
}


// Get out-of-chunk Named metadata nodes
std::vector<NamedMDNode *> NamedNodesToDelete;
for (auto &MD : Program->named_metadata())
SmallVector<NamedMDNode *> NamedNodesToDelete;
for (NamedMDNode &MD : Program->named_metadata())
if (!O.shouldKeep())
NamedNodesToDelete.push_back(&MD);

for (auto *NN : NamedNodesToDelete) {
for (int I = 0, E = NN->getNumOperands(); I != E; ++I)
for (NamedMDNode *NN : NamedNodesToDelete) {
for (auto I : seq<unsigned>(0, NN->getNumOperands()))
NN->setOperand(I, NULL);
NN->eraseFromParent();
}
}

// Gets unnamed metadata nodes used by a given instruction/GV/function and adds
// them to the set of seen nodes
template <class T>
static void addMetadataToSet(T &MDUser, std::set<MDNode *> &UnnamedNodes) {
SmallVector<std::pair<unsigned, MDNode *>, 4> MDs;
MDUser.getAllMetadata(MDs);
for (auto &MD : MDs)
UnnamedNodes.insert(MD.second);
// Delete out-of-chunk metadata attached to globals.
SmallVector<std::pair<unsigned, MDNode *>> MDs;
for (GlobalVariable &GV : Program->globals()) {
GV.getAllMetadata(MDs);
for (std::pair<unsigned, MDNode *> &MD : MDs)
if (!O.shouldKeep())
GV.setMetadata(MD.first, NULL);
}

for (Function &F : *Program) {
// Delete out-of-chunk metadata attached to functions.
F.getAllMetadata(MDs);
for (std::pair<unsigned, MDNode *> &MD : MDs)
if (!O.shouldKeep())
F.setMetadata(MD.first, NULL);

// Delete out-of-chunk metadata attached to instructions.
for (Instruction &I : instructions(F)) {
I.getAllMetadata(MDs);
for (std::pair<unsigned, MDNode *> &MD : MDs)
if (!O.shouldKeep())
I.setMetadata(MD.first, NULL);
}
}
}

/// Returns the amount of Named and Unnamed Metadata Nodes
static int countMetadataTargets(Module *Program) {
std::set<MDNode *> UnnamedNodes;
int NamedMetadataNodes = Program->named_metadata_size();

// Get metadata nodes used by globals
for (auto &GV : Program->globals())
addMetadataToSet(GV, UnnamedNodes);
// Get metadata attached to globals.
int GlobalMetadataArgs = 0;
SmallVector<std::pair<unsigned, MDNode *>> MDs;
for (GlobalVariable &GV : Program->globals()) {
GV.getAllMetadata(MDs);
GlobalMetadataArgs += MDs.size();
}

// Do the same for nodes used by functions & instructions
for (auto &F : *Program) {
addMetadataToSet(F, UnnamedNodes);
for (auto &BB : F)
for (auto &I : BB)
addMetadataToSet(I, UnnamedNodes);
// Get metadata attached to functions & instructions.
int FunctionMetadataArgs = 0;
int InstructionMetadataArgs = 0;
for (Function &F : *Program) {
F.getAllMetadata(MDs);
FunctionMetadataArgs += MDs.size();

for (Instruction &I : instructions(F)) {
I.getAllMetadata(MDs);
InstructionMetadataArgs += MDs.size();
}
}

return UnnamedNodes.size() + NamedMetadataNodes;
return NamedMetadataNodes + GlobalMetadataArgs + FunctionMetadataArgs +
InstructionMetadataArgs;
}

void llvm::reduceMetadataDeltaPass(TestRunner &Test) {
Expand Down

0 comments on commit d9562a8

Please sign in to comment.