diff --git a/llvm/test/Reduce/remove-funcs.ll b/llvm/test/Reduce/remove-funcs.ll --- a/llvm/test/Reduce/remove-funcs.ll +++ b/llvm/test/Reduce/remove-funcs.ll @@ -1,7 +1,7 @@ ; Test that llvm-reduce can remove uninteresting functions as well as ; their InstCalls. ; -; RUN: llvm-reduce --test %p/Inputs/interestingness-test.sh --test-arg %lli %s +; RUN: llvm-reduce --run-only functions --test %p/Inputs/interestingness-test.sh --test-arg %lli %s ; RUN: cat reduced.ll | FileCheck %s ; REQUIRES: plugins, shell diff --git a/llvm/test/Reduce/remove-global-vars.ll b/llvm/test/Reduce/remove-global-vars.ll --- a/llvm/test/Reduce/remove-global-vars.ll +++ b/llvm/test/Reduce/remove-global-vars.ll @@ -1,7 +1,7 @@ ; Test that llvm-reduce can remove uninteresting Global Variables as well as ; their uses (both direct and derived). ; -; RUN: llvm-reduce --test %p/Inputs/interestingness-test.sh --test-arg %lli %s +; RUN: llvm-reduce --run-only globals --test %p/Inputs/interestingness-test.sh --test-arg %lli %s ; RUN: cat reduced.ll | FileCheck %s ; REQUIRES: plugins, shell diff --git a/llvm/test/Reduce/remove-metadata.ll b/llvm/test/Reduce/remove-metadata.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Reduce/remove-metadata.ll @@ -0,0 +1,80 @@ +; Test that llvm-reduce can remove uninteresting metadata from an IR file. +; The "Reduce Metadata" pass erases named and unnamed metadata nodes, as well as debug functions. +; +; RUN: llvm-reduce --run-only metadata --test %p/Inputs/interestingness-test.sh --test-arg %lli %s +; RUN: cat reduced.ll | FileCheck %s +; REQUIRES: plugins, shell + +; CHECK-NOT: llvm. +; CHECK-NOT: !dbg +; CHECK-NOT: ! + +@x = dso_local global i32 0, align 4, !dbg !0 +@y = dso_local global i32 0, align 4, !dbg !6 +@.str = private unnamed_addr constant [4 x i8] c"%i\0A\00", align 1 + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local void @func(i32 %x) !dbg !13 { +entry: + %x.addr = alloca i32, align 4 + store i32 %x, i32* %x.addr, align 4 + call void @llvm.dbg.declare(metadata i32* %x.addr, metadata !16, metadata !DIExpression()), !dbg !17 + %0 = load i32, i32* %x.addr, align 4, !dbg !18 + %inc = add nsw i32 %0, 1, !dbg !18 + store i32 %inc, i32* %x.addr, align 4, !dbg !18 + ret void, !dbg !19 +} + +; Function Attrs: nounwind readnone speculatable +declare void @llvm.dbg.declare(metadata, metadata, metadata) + +; Function Attrs: noinline norecurse optnone uwtable +define dso_local i32 @main() !dbg !20 { +entry: + %retval = alloca i32, align 4 + store i32 0, i32* %retval, align 4 + store i32 10, i32* @x, align 4, !dbg !23 + %0 = load i32, i32* @x, align 4, !dbg !24 + %sub = sub nsw i32 %0, 5, !dbg !25 + store i32 %sub, i32* @y, align 4, !dbg !26 + %1 = load i32, i32* @x, align 4, !dbg !27 + %call = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i64 0, i64 0), i32 %1), !dbg !28 + ret i32 0, !dbg !29 +} + +declare dso_local i32 @printf(i8*, ...) + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!9, !10, !11} +!llvm.ident = !{!12} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "x", scope: !2, file: !3, line: 3, type: !8, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !3, producer: "clang version 9.0.0 (https://github.com/llvm/llvm-project.git ff0a72129a3e97bf7fddca38688808cd4f4e8f7b)", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5, nameTableKind: None) +!3 = !DIFile(filename: "ints.cpp", directory: "") +!4 = !{} +!5 = !{!0, !6} +!6 = !DIGlobalVariableExpression(var: !7, expr: !DIExpression()) +!7 = distinct !DIGlobalVariable(name: "y", scope: !2, file: !3, line: 4, type: !8, isLocal: false, isDefinition: true) +!8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!9 = !{i32 2, !"Dwarf Version", i32 4} +!10 = !{i32 2, !"Debug Info Version", i32 3} +!11 = !{i32 1, !"wchar_size", i32 4} +!12 = !{!"clang version 9.0.0 (https://github.com/llvm/llvm-project.git ff0a72129a3e97bf7fddca38688808cd4f4e8f7b)"} +!13 = distinct !DISubprogram(name: "func", linkageName: "func", scope: !3, file: !3, line: 6, type: !14, scopeLine: 6, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!14 = !DISubroutineType(types: !15) +!15 = !{null, !8} +!16 = !DILocalVariable(name: "x", arg: 1, scope: !13, file: !3, line: 6, type: !8) +!17 = !DILocation(line: 6, column: 15, scope: !13) +!18 = !DILocation(line: 7, column: 4, scope: !13) +!19 = !DILocation(line: 8, column: 1, scope: !13) +!20 = distinct !DISubprogram(name: "main", scope: !3, file: !3, line: 10, type: !21, scopeLine: 10, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!21 = !DISubroutineType(types: !22) +!22 = !{!8} +!23 = !DILocation(line: 11, column: 5, scope: !20) +!24 = !DILocation(line: 12, column: 7, scope: !20) +!25 = !DILocation(line: 12, column: 9, scope: !20) +!26 = !DILocation(line: 12, column: 5, scope: !20) +!27 = !DILocation(line: 13, column: 18, scope: !20) +!28 = !DILocation(line: 13, column: 3, scope: !20) +!29 = !DILocation(line: 14, column: 3, scope: !20) diff --git a/llvm/tools/llvm-reduce/CMakeLists.txt b/llvm/tools/llvm-reduce/CMakeLists.txt --- a/llvm/tools/llvm-reduce/CMakeLists.txt +++ b/llvm/tools/llvm-reduce/CMakeLists.txt @@ -16,8 +16,9 @@ llvm-reduce.cpp TestRunner.cpp deltas/Delta.cpp - deltas/RemoveFunctions.cpp - deltas/RemoveGlobalVars.cpp + deltas/ReduceFunctions.cpp + deltas/ReduceGlobalVars.cpp + deltas/ReduceMetadata.cpp DEPENDS intrinsics_gen diff --git a/llvm/tools/llvm-reduce/DeltaManager.h b/llvm/tools/llvm-reduce/DeltaManager.h --- a/llvm/tools/llvm-reduce/DeltaManager.h +++ b/llvm/tools/llvm-reduce/DeltaManager.h @@ -13,19 +13,23 @@ #include "TestRunner.h" #include "deltas/Delta.h" -#include "deltas/RemoveFunctions.h" -#include "deltas/RemoveGlobalVars.h" -#include "deltas/RemoveMetadata.h" +#include "deltas/ReduceFunctions.h" +#include "deltas/ReduceGlobalVars.h" +#include "deltas/ReduceMetadata.h" namespace llvm { -inline void runDeltaPasses(TestRunner &Tester) { - // TODO: Add option to only call certain delta passes - outs() << "Reducing functions...\n"; - removeFunctionsDeltaPass(Tester); - outs() << "Reducing GVs...\n"; - removeGlobalsDeltaPass(Tester); - // TODO: Implement the remaining Delta Passes +inline void runDeltaPasses(TestRunner &Tester, + std::vector DeltaPasses) { + for (auto &Pass : DeltaPasses) { + if (Pass == "functions") + reduceFunctionsDeltaPass(Tester); + else if (Pass == "globals") + reduceGlobalsDeltaPass(Tester); + else if (Pass == "metadata") + reduceMetadataDeltaPass(Tester); + // TODO: Implement the remaining Delta Passes + } } } // namespace llvm diff --git a/llvm/tools/llvm-reduce/deltas/Delta.h b/llvm/tools/llvm-reduce/deltas/Delta.h --- a/llvm/tools/llvm-reduce/deltas/Delta.h +++ b/llvm/tools/llvm-reduce/deltas/Delta.h @@ -30,6 +30,16 @@ int begin; int end; + /// Helper function to verify if a given Target-index is inside the Chunk + bool contains(int Index) const { return Index >= begin && Index <= end; } + + void print() const { + outs() << "[" << begin; + if (end - begin != 0) + outs() << "," << end; + outs() << "]"; + } + /// Operator when populating CurrentChunks in Generic Delta Pass friend bool operator!=(const Chunk &C1, const Chunk &C2) { return C1.begin != C2.begin && C1.end != C2.end; diff --git a/llvm/tools/llvm-reduce/deltas/Delta.cpp b/llvm/tools/llvm-reduce/deltas/Delta.cpp --- a/llvm/tools/llvm-reduce/deltas/Delta.cpp +++ b/llvm/tools/llvm-reduce/deltas/Delta.cpp @@ -53,10 +53,7 @@ for (auto C : Chunks) { if (!Oneline) outs() << '\t'; - outs() << "[" << C.begin; - if (C.end - C.begin != 0) - outs() << "," << C.end; - outs() << "]"; + C.print(); if (!Oneline) outs() << '\n'; } @@ -99,6 +96,9 @@ return SplitOne; } +/// Runs the Delta Debugging algorithm, splits the code into chunks and +/// reduces the amount of chunks that are considered interesting by the +/// given test. void llvm::runDeltaPass( TestRunner &Test, int Targets, std::function(std::vector, Module *)> @@ -125,9 +125,6 @@ if (!UninterestingChunks.count(C) && C != Chunks[I]) CurrentChunks.push_back(C); - if (CurrentChunks.empty()) - break; - // Generate Module with only Targets inside Current Chunks std::unique_ptr CurrentProgram = ExtractChunksFromModule(CurrentChunks, Test.getProgram()); @@ -136,7 +133,11 @@ createTmpFile(CurrentProgram.get(), Test.getTmpDir()); outs() << "Testing with: "; - printChunks(CurrentChunks, /*Oneline=*/true); + if (CurrentChunks.empty()) + outs() << "No Chunks"; + else + printChunks(CurrentChunks, /*Oneline=*/true); + outs() << " | " << sys::path::filename(CurrentFilepath); // Current Chunks aren't interesting diff --git a/llvm/tools/llvm-reduce/deltas/RemoveFunctions.h b/llvm/tools/llvm-reduce/deltas/ReduceFunctions.h rename from llvm/tools/llvm-reduce/deltas/RemoveFunctions.h rename to llvm/tools/llvm-reduce/deltas/ReduceFunctions.h --- a/llvm/tools/llvm-reduce/deltas/RemoveFunctions.h +++ b/llvm/tools/llvm-reduce/deltas/ReduceFunctions.h @@ -1,4 +1,4 @@ -//===- RemoveFunctions.h - Specialized Delta Pass -------------------------===// +//===- ReduceFunctions.h - 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. @@ -16,5 +16,5 @@ #include "llvm/Transforms/Utils/Cloning.h" namespace llvm { -void removeFunctionsDeltaPass(TestRunner &Test); +void reduceFunctionsDeltaPass(TestRunner &Test); } // namespace llvm diff --git a/llvm/tools/llvm-reduce/deltas/RemoveFunctions.cpp b/llvm/tools/llvm-reduce/deltas/ReduceFunctions.cpp rename from llvm/tools/llvm-reduce/deltas/RemoveFunctions.cpp rename to llvm/tools/llvm-reduce/deltas/ReduceFunctions.cpp --- a/llvm/tools/llvm-reduce/deltas/RemoveFunctions.cpp +++ b/llvm/tools/llvm-reduce/deltas/ReduceFunctions.cpp @@ -1,4 +1,4 @@ -//===- RemoveFunctions.cpp - Specialized Delta Pass -----------------------===// +//===- ReduceFunctions.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. @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -#include "RemoveFunctions.h" +#include "ReduceFunctions.h" /// Removes all the Defined Functions (as well as their calls) /// that aren't inside any of the desired Chunks. @@ -23,15 +23,14 @@ // Get functions inside desired chunks std::set FuncsToKeep; - int I = 0, FunctionCount = 1; + int I = 0, FunctionCount = 0; for (auto &F : *Clone) { if (!F.isDeclaration() && I < ChunksToKeep.size()) { - if (FunctionCount >= ChunksToKeep[I].begin && - FunctionCount <= ChunksToKeep[I].end) + ++FunctionCount; + if (ChunksToKeep[I].contains(FunctionCount)) FuncsToKeep.insert(&F); if (FunctionCount == ChunksToKeep[I].end) ++I; - ++FunctionCount; } } @@ -66,7 +65,7 @@ /// Counts the amount of non-declaration functions and prints their /// respective name & index -static int countFunctions(Module *Program) { +static int countDefinedFunctions(Module *Program) { // TODO: Silence index with --quiet flag outs() << "----------------------------\n"; outs() << "Function Index Reference:\n"; @@ -80,7 +79,8 @@ return FunctionCount; } -void llvm::removeFunctionsDeltaPass(TestRunner &Test) { - int FunctionCount = countFunctions(Test.getProgram()); - runDeltaPass(Test, FunctionCount, extractFunctionsFromModule); +void llvm::reduceFunctionsDeltaPass(TestRunner &Test) { + outs() << "*** Reducing Functions...\n"; + int Functions = countDefinedFunctions(Test.getProgram()); + runDeltaPass(Test, Functions, extractFunctionsFromModule); } \ No newline at end of file diff --git a/llvm/tools/llvm-reduce/deltas/RemoveGlobalVars.h b/llvm/tools/llvm-reduce/deltas/ReduceGlobalVars.h rename from llvm/tools/llvm-reduce/deltas/RemoveGlobalVars.h rename to llvm/tools/llvm-reduce/deltas/ReduceGlobalVars.h --- a/llvm/tools/llvm-reduce/deltas/RemoveGlobalVars.h +++ b/llvm/tools/llvm-reduce/deltas/ReduceGlobalVars.h @@ -1,4 +1,4 @@ -//===- RemoveGlobalVars.h - Specialized Delta Pass ------------------------===// +//===- ReduceGlobalVars.h - 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. @@ -17,5 +17,5 @@ #include "llvm/Transforms/Utils/Cloning.h" namespace llvm { -void removeGlobalsDeltaPass(TestRunner &Test); +void reduceGlobalsDeltaPass(TestRunner &Test); } // namespace llvm diff --git a/llvm/tools/llvm-reduce/deltas/RemoveGlobalVars.cpp b/llvm/tools/llvm-reduce/deltas/ReduceGlobalVars.cpp rename from llvm/tools/llvm-reduce/deltas/RemoveGlobalVars.cpp rename to llvm/tools/llvm-reduce/deltas/ReduceGlobalVars.cpp --- a/llvm/tools/llvm-reduce/deltas/RemoveGlobalVars.cpp +++ b/llvm/tools/llvm-reduce/deltas/ReduceGlobalVars.cpp @@ -1,4 +1,4 @@ -//===- RemoveGlobalVars.cpp - Specialized Delta Pass ----------------------===// +//===- ReduceGlobalVars.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. @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -#include "RemoveGlobalVars.h" +#include "ReduceGlobalVars.h" /// Goes over the users of a given instruction and erases them. /// For example, if @x is used by %call1 and %call2, both calls and the @@ -38,14 +38,14 @@ // Get GVs inside desired chunks std::set GVsToKeep; - int I = 0, GVCount = 1; + int I = 0, GVCount = 0; for (auto &GV : Clone->globals()) { if (GV.hasInitializer() && I < ChunksToKeep.size()) { - if (GVCount >= ChunksToKeep[I].begin && GVCount <= ChunksToKeep[I].end) + ++GVCount; + if (ChunksToKeep[I].contains(GVCount)) GVsToKeep.insert(&GV); if (GVCount == ChunksToKeep[I].end) ++I; - ++GVCount; } } @@ -88,7 +88,8 @@ return GVCount; } -void llvm::removeGlobalsDeltaPass(TestRunner &Test) { +void llvm::reduceGlobalsDeltaPass(TestRunner &Test) { + outs() << "*** Reducing GVs...\n"; int GVCount = countGVs(Test.getProgram()); runDeltaPass(Test, GVCount, extractGVsFromModule); } \ No newline at end of file diff --git a/llvm/tools/llvm-reduce/deltas/RemoveGlobalVars.h b/llvm/tools/llvm-reduce/deltas/ReduceMetadata.h rename from llvm/tools/llvm-reduce/deltas/RemoveGlobalVars.h rename to llvm/tools/llvm-reduce/deltas/ReduceMetadata.h --- a/llvm/tools/llvm-reduce/deltas/RemoveGlobalVars.h +++ b/llvm/tools/llvm-reduce/deltas/ReduceMetadata.h @@ -1,4 +1,4 @@ -//===- RemoveGlobalVars.h - Specialized Delta Pass ------------------------===// +//===- ReduceMetadata.h - 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. @@ -6,16 +6,19 @@ // //===----------------------------------------------------------------------===// // -// This file implements a function which calls the Generic Delta pass in order -// to reduce initialized Global Variables, as well as any derived uses in the -// provided Module. +// This file implements two functions used by the Generic Delta Debugging +// Algorithm, which are used to reduce Metadata tags, as well as debug +// functions. // //===----------------------------------------------------------------------===// #include "Delta.h" +#include "llvm/IR/Metadata.h" #include "llvm/IR/Value.h" #include "llvm/Transforms/Utils/Cloning.h" +#include +#include namespace llvm { -void removeGlobalsDeltaPass(TestRunner &Test); +void reduceMetadataDeltaPass(TestRunner &Test); } // namespace llvm diff --git a/llvm/tools/llvm-reduce/deltas/ReduceMetadata.cpp b/llvm/tools/llvm-reduce/deltas/ReduceMetadata.cpp new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-reduce/deltas/ReduceMetadata.cpp @@ -0,0 +1,169 @@ +//===- ReduceMetadata.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 Metadata tags, as well as debug +// functions. +// +//===----------------------------------------------------------------------===// + +#include "ReduceMetadata.h" + +/// Adds all Unnamed Metadata Nodes that are inside desired Chunks to set +template +static void getChunkMetadataNodes(T *MDUser, int &I, + const std::vector &ChunksToKeep, + std::set &SeenNodes, + std::set &NodesToKeep) { + SmallVector, 4> MDs; + MDUser->getAllMetadata(MDs); + for (auto &MD : MDs) { + SeenNodes.insert(MD.second); + if (I < ChunksToKeep.size()) { + if (ChunksToKeep[I].contains(SeenNodes.size())) + NodesToKeep.insert(MD.second); + if (ChunksToKeep[I].end == SeenNodes.size()) + ++I; + } + } +} + +/// Adds Nodes to seen, and if one of them is out-of-chunks, +template +static void eraseNodeIfOutsideChunk(T *MDUser, + std::set &NodesToKeep) { + SmallVector, 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); +} + +/// Erases function and its users. It also replaces the uses of any user with +/// "undef" (e.g. if a function call is stored somewhere it is ) +static void deleteFunctionAndCallers(Function *F) { + for (auto U : F->users()) { + U->replaceAllUsesWith(UndefValue::get(U->getType())); + if (auto *Inst = dyn_cast(U)) + Inst->eraseFromParent(); + } + F->eraseFromParent(); +} + +/// Removes all the Named and Unnamed Metadata Nodes (as well as any debug +/// functions) that aren't inside the desired Chunks. +/// @returns the Module stripped of out-of-chunk MDNodes +static std::unique_ptr +extractMetadataNodesFromModule(const std::vector &ChunksToKeep, + Module *Program) { + std::unique_ptr Clone = CloneModule(*Program); + + std::set SeenNodes; + std::set NodesToKeep; + int I = 0; + + // Add chunk MDNodes used by GVs, Functions, and Instructions to set + for (auto &GV : Clone->globals()) + getChunkMetadataNodes(&GV, I, ChunksToKeep, SeenNodes, NodesToKeep); + + for (auto &F : *Clone) { + getChunkMetadataNodes(&F, I, ChunksToKeep, SeenNodes, NodesToKeep); + for (auto &BB : F) + for (auto &Inst : BB) + getChunkMetadataNodes(&Inst, I, ChunksToKeep, SeenNodes, NodesToKeep); + } + + // Once more, go over metadata nodes, but deleting the ones outside chunks + for (auto &GV : Clone->globals()) + eraseNodeIfOutsideChunk(&GV, NodesToKeep); + + // And keeping a list of debug functions + std::vector DebugFuncs; + for (auto &F : *Clone) { + if (F.getName().startswith("llvm.")) + DebugFuncs.push_back(&F); + else + eraseNodeIfOutsideChunk(&F, NodesToKeep); + for (auto &BB : F) + for (auto &J : BB) + eraseNodeIfOutsideChunk(&J, NodesToKeep); + } + + // Go over debug functions and erase the ones out-of-chunk + int MetadataCount = SeenNodes.size(); + for (auto F : DebugFuncs) { + if (I < ChunksToKeep.size()) { + if (!ChunksToKeep[I].contains(MetadataCount)) + deleteFunctionAndCallers(F); + if (ChunksToKeep[I].end == MetadataCount) + ++I; + } else + deleteFunctionAndCallers(F); + } + + std::vector NamedNodesToDelete; + + // Delete any out-of-chunk named metadata nodes + for (auto &MD : Clone->named_metadata()) { + ++MetadataCount; + if (I < ChunksToKeep.size()) { + if (!ChunksToKeep[I].contains(MetadataCount)) + NamedNodesToDelete.push_back(&MD); + if (ChunksToKeep[I].end == SeenNodes.size()) + ++I; + } else + NamedNodesToDelete.push_back(&MD); + } + for (auto NN : NamedNodesToDelete) { + for (int J = 0, E = NN->getNumOperands(); J != E; ++J) + NN->setOperand(J, NULL); + NN->eraseFromParent(); + } + + return Clone; +} + +// Gets unnamed metadata nodes used by a given instruction/GV/function and adds +// them to the set of seen nodes +template +static void addMetadataToSet(T *MDUser, std::set &UnnamedNodes) { + SmallVector, 4> MDs; + MDUser->getAllMetadata(MDs); + for (auto &MD : MDs) + UnnamedNodes.insert(MD.second); +} + +/// Returns the amount of Named and Unnamed Metadata Nodes, and Debug functions +/// used in the module +static int countMetadataTargets(Module *Program) { + std::set UnnamedNodes; + int NamedMetadataNodes = Program->named_metadata_size(); + int DebugFunctionsCount = 0; + + // Get metadata nodes used by globals + for (auto &GV : Program->globals()) + addMetadataToSet(&GV, UnnamedNodes); + + // Do the same for nodes used by functions & instructions + for (auto &F : *Program) { + if (F.getName().startswith("llvm.")) + ++DebugFunctionsCount; + addMetadataToSet(&F, UnnamedNodes); + for (auto &BB : F) + for (auto &I : BB) + addMetadataToSet(&I, UnnamedNodes); + } + + return UnnamedNodes.size() + NamedMetadataNodes + DebugFunctionsCount; +} + +void llvm::reduceMetadataDeltaPass(TestRunner &Test) { + outs() << "*** Reducing Metadata...\n"; + int MDCount = countMetadataTargets(Test.getProgram()); + runDeltaPass(Test, MDCount, extractMetadataNodesFromModule); +} diff --git a/llvm/tools/llvm-reduce/llvm-reduce.cpp b/llvm/tools/llvm-reduce/llvm-reduce.cpp --- a/llvm/tools/llvm-reduce/llvm-reduce.cpp +++ b/llvm/tools/llvm-reduce/llvm-reduce.cpp @@ -53,6 +53,28 @@ cl::desc("WARNING: This option will replace your input file" "with the reduced version!")); +namespace { +std::vector DeltaPasses; + +struct DeltaPassOption { + void operator=(const std::string &Val) const { + if (Val.empty()) + return; + SmallVector PassNames; + StringRef(Val).split(PassNames, ',', -1, false); + for (auto PassName : PassNames) + DeltaPasses.push_back(PassName); + } +}; +} // namespace + +static DeltaPassOption DeltaPassOpt; + +static cl::opt> Passes( + "run-only", + cl::desc("Only run the specified delta passes (comma separated list)"), + cl::value_desc("passes"), cl::ZeroOrMore, cl::location(DeltaPassOpt)); + // Parses IR into a Module and verifies it static std::unique_ptr parseInputFile(StringRef Filename, LLVMContext &Ctxt) { @@ -102,7 +124,7 @@ Tester.setProgram(std::move(OriginalProgram)); // Try to reduce code - runDeltaPasses(Tester); + runDeltaPasses(Tester, DeltaPasses); StringRef ReducedFilename = sys::path::filename(Tester.getReducedFilepath()); if (ReducedFilename == InputFilename) {