diff --git a/llvm/test/Reduce/Inputs/remove-funcs.sh b/llvm/test/Reduce/Inputs/interestingness-test.sh rename from llvm/test/Reduce/Inputs/remove-funcs.sh rename to llvm/test/Reduce/Inputs/interestingness-test.sh 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/remove-funcs.sh --test-arg %lli %s +; RUN: llvm-reduce --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 new file mode 100644 --- /dev/null +++ b/llvm/test/Reduce/remove-global-vars.ll @@ -0,0 +1,57 @@ +; 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: cat reduced.ll | FileCheck %s +; REQUIRES: plugins, shell + +; CHECK-NOT: @uninteresting1 +@uninteresting1 = global i32 0, align 4 +; CHECK: @interesting +@interesting = global i32 5, align 4 +; CHECK-NOT: @uninteresting2 +@uninteresting2 = global i32 25, align 4 +; CHECK-NOT: @uninteresting3 +@uninteresting3 = global i32 50, align 4 +@.str = private unnamed_addr constant [4 x i8] c"%i\0A\00", align 1 + +define void @func(i32* dereferenceable(4) %x) { +entry: + %x.addr = alloca i32*, align 8 + store i32* %x, i32** %x.addr, align 8 + %0 = load i32*, i32** %x.addr, align 8 + %1 = load i32, i32* %0, align 4 + %inc = add nsw i32 %1, 1 + store i32 %inc, i32* %0, align 4 + ret void +} + +define i32 @main() { +entry: + %retval = alloca i32, align 4 + store i32 0, i32* %retval, align 4 + ; CHECK-NOT: load i32, i32* @uninteresting2, align 4 + %0 = load i32, i32* @uninteresting2, align 4 + store i32 %0, i32* @interesting, align 4 + ; CHECK-NOT: load i32, i32* @uninteresting3, align 4 + %1 = load i32, i32* @uninteresting3, align 4 + %dec = add nsw i32 %1, -1 + ; CHECK-NOT: store i32 %dec, i32* @uninteresting3, align 4 + store i32 %dec, i32* @uninteresting3, align 4 + ; CHECK: load i32, i32* @interesting, align 4 + %2 = load i32, i32* @interesting, align 4 + ; CHECK-NOT: load i32, i32* @uninteresting2, align 4 + %3 = load i32, i32* @uninteresting2, align 4 + %add = add nsw i32 %2, %3 + ; CHECK-NOT: store i32 %add, i32* @uninteresting1, align 4 + store i32 %add, i32* @uninteresting1, align 4 + ; CHECK: store i32 10, i32* @interesting, align 4 + store i32 10, i32* @interesting, align 4 + ; CHECK-NOT: call void @func(i32* dereferenceable(4) @uninteresting1) + call void @func(i32* dereferenceable(4) @uninteresting1) + %4 = load i32, i32* @interesting, align 4 + %call = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i64 0, i64 0), i32 %4) + ret i32 0 +} + +declare i32 @printf(i8*, ...) 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,7 +16,7 @@ llvm-reduce.cpp TestRunner.cpp deltas/RemoveFunctions.cpp - deltas/RemoveGlobals.cpp + deltas/RemoveGlobalVars.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 @@ -1,4 +1,4 @@ -//===- llvm-reduce.cpp - The LLVM Delta Reduction utility -----------------===// +//===- DeltaManager.h - Runs Delta Passes to reduce Input -----------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// // -// This class calls each specialized Delta pass by passing it as a template to +// This file calls each specialized Delta pass by passing it as a template to // the generic Delta Pass. // //===----------------------------------------------------------------------===// @@ -14,14 +14,17 @@ #include "TestRunner.h" #include "deltas/Delta.h" #include "deltas/RemoveFunctions.h" -#include "deltas/RemoveGlobals.h" +#include "deltas/RemoveGlobalVars.h" namespace llvm { inline void runDeltaPasses(TestRunner &Tester) { + // TODO: Add option to only call certain delta passes outs() << "Reducing functions...\n"; Delta::run(Tester); - // TODO: Implement the rest of the Delta Passes + outs() << "Reducing GVs...\n"; + Delta::run(Tester); + // TODO: Implement the remaining Delta Passes } } // namespace llvm diff --git a/llvm/tools/llvm-reduce/TestRunner.h b/llvm/tools/llvm-reduce/TestRunner.h --- a/llvm/tools/llvm-reduce/TestRunner.h +++ b/llvm/tools/llvm-reduce/TestRunner.h @@ -1,4 +1,4 @@ -//===- llvm-reduce.cpp - The LLVM Delta Reduction utility -----------------===// +//===-- tools/llvm-reduce/TestRunner.h ---------------------------*- C++ -*-===/ // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/llvm/tools/llvm-reduce/TestRunner.cpp b/llvm/tools/llvm-reduce/TestRunner.cpp --- a/llvm/tools/llvm-reduce/TestRunner.cpp +++ b/llvm/tools/llvm-reduce/TestRunner.cpp @@ -1,3 +1,11 @@ +//===-- TestRunner.cpp ----------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + #include "TestRunner.h" using 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 @@ -1,4 +1,4 @@ -//===- llvm-reduce.cpp - The LLVM Delta Reduction utility -----------------===// +//===- Delta.h - Delta Debugging Algorithm Implementation -----------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -54,8 +54,8 @@ return true; } -/// Creates a temporary (and unique) file inside the tmp folder and outputs -/// the module inside it. +/// Creates a temporary (and unique) file inside the tmp folder and writes +/// the given module IR. inline SmallString<128> createTmpFile(Module *M, StringRef TmpDir) { SmallString<128> UniqueFilepath; int UniqueFD; @@ -140,9 +140,9 @@ /// alongside a specialized delta pass (e.g. RemoveFunctions) passed as a /// template. /// This specialized pass implements two functions: -/// * getTargetCount, which returns the amount of targets (e.g. Functions) +/// * getTargetCount: Returns the amount of targets (e.g. Functions) /// there are in the Module. -/// * extractChunksFromModule, which clones the given Module and modifies it +/// * extractChunksFromModule: Clones the given Module and modifies it /// so it only contains Chunk Targets. /// /// Other implementations of the Delta Debugging algorithm can be found in the @@ -174,6 +174,9 @@ 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 = P::extractChunksFromModule(CurrentChunks, Test.getProgram()); @@ -213,6 +216,7 @@ if (ReducedProgram) Test.setProgram(std::move(ReducedProgram)); outs() << "Couldn't increase anymore.\n"; + outs() << "----------------------------\n"; } }; diff --git a/llvm/tools/llvm-reduce/deltas/RemoveFunctions.h b/llvm/tools/llvm-reduce/deltas/RemoveFunctions.h --- a/llvm/tools/llvm-reduce/deltas/RemoveFunctions.h +++ b/llvm/tools/llvm-reduce/deltas/RemoveFunctions.h @@ -1,4 +1,4 @@ -//===- llvm-reduce.cpp - The LLVM Delta Reduction utility -----------------===// +//===- RemoveFunctions.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,8 +6,9 @@ // //===----------------------------------------------------------------------===// // -// This file is a Specialized Delta Pass, which removes the functions that are -// not in the provided function-chunks. +// This file implements two functions used by the Generic Delta Debugging +// Algorithm, which are used to reduce functions (and any instruction that +// calls it) in the provided Module. // //===----------------------------------------------------------------------===// diff --git a/llvm/tools/llvm-reduce/deltas/RemoveFunctions.cpp b/llvm/tools/llvm-reduce/deltas/RemoveFunctions.cpp --- a/llvm/tools/llvm-reduce/deltas/RemoveFunctions.cpp +++ b/llvm/tools/llvm-reduce/deltas/RemoveFunctions.cpp @@ -1,4 +1,4 @@ -//===- llvm-reduce.cpp - The LLVM Delta Reduction utility -----------------===// +//===- RemoveFunctions.cpp ------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,8 +6,9 @@ // //===----------------------------------------------------------------------===// // -// This file is a Specialized Delta Pass, which removes the functions that are -// not in the provided function-chunks. +// This file implements two functions used by the Generic Delta Debugging +// Algorithm, which are used to reduce functions (and any instruction that +// calls it) in the provided Module. // //===----------------------------------------------------------------------===// diff --git a/llvm/tools/llvm-reduce/deltas/RemoveGlobalVars.h b/llvm/tools/llvm-reduce/deltas/RemoveGlobalVars.h new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-reduce/deltas/RemoveGlobalVars.h @@ -0,0 +1,33 @@ +//===- RemoveGlobalVars.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. +// 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 Global Variables, as well as their +// derived calls in the provided Module. +// +//===----------------------------------------------------------------------===// + +#include "Delta.h" +#include "llvm/Transforms/Utils/Cloning.h" +#include "llvm/IR/Value.h" +#include +#include + +namespace llvm { + +class RemoveGlobalVars { +public: + /// Outputs the number of initialized GVs in the given Module. + static int getTargetCount(Module *Program); + /// Clones module and returns one without non-chunk GVs + static std::unique_ptr extractChunksFromModule( + std::vector ChunksToKeep, + Module *Program); +}; + +} // namespace llvm diff --git a/llvm/tools/llvm-reduce/deltas/RemoveGlobalVars.cpp b/llvm/tools/llvm-reduce/deltas/RemoveGlobalVars.cpp new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-reduce/deltas/RemoveGlobalVars.cpp @@ -0,0 +1,94 @@ +//===- RemoveGlobalVars.cpp -----------------------------------------------===// +// +// 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 Global Variables, as well as their +// derived calls in the provided Module. +// +//===----------------------------------------------------------------------===// + +#include "RemoveGlobalVars.h" + +using namespace llvm; + +/// 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 +/// GV declaration would be erased. Moreover, if any of the calls are used +/// elsewhere, those uses would be erased and so on recursively. +static void eraseDerivedUsers(Instruction* Inst, + std::set &InstToRemove) { + if (!Inst->use_empty()) + for(auto U : Inst->users()) + if(auto *I = dyn_cast(U)) + eraseDerivedUsers(I, InstToRemove); + + InstToRemove.insert(Inst); + Inst->replaceAllUsesWith(UndefValue::get(Inst->getType())); +} + +/// Removes all the Initialized GVs (as well as their direct and indirect uses) +/// that aren't inside the desired Chunks. +/// @returns the Module stripped of out-of-chunk GVs +std::unique_ptr RemoveGlobalVars::extractChunksFromModule( + std::vector ChunksToKeep, + Module *Program) { + std::unique_ptr Clone = CloneModule(*Program); + + // Get functions inside desired chunks + std::set GVsToKeep; + int I = 0, GVCount = 1; + for(auto &GV : Clone->globals()) { + if (GV.hasInitializer()) { + if (GVCount >= ChunksToKeep[I].begin && + GVCount <= ChunksToKeep[I].end) { + GVsToKeep.insert(&GV); + } + if (GVCount >= ChunksToKeep[I].end) + ++I; + ++GVCount; + } + } + + // Delete out-of-chunk GVs as well as their respective uses + std::vector ToRemove; + std::set InstToRemove; + for (auto &GV : Clone->globals()) { + if (GV.hasInitializer() && !GVsToKeep.count(&GV)) { + // Delete any instructions that use the GV, both direct and indirectly + for (auto U : GV.users()) + if (auto *I = dyn_cast(U)) + eraseDerivedUsers(I, InstToRemove); + + GV.replaceAllUsesWith(UndefValue::get(GV.getType())); + ToRemove.push_back(&GV); + } + } + for (auto *GV : ToRemove) + GV->eraseFromParent(); + + for (auto *I : InstToRemove) + I->eraseFromParent(); + + return Clone; +} + +/// Counts the amount of initialized GVs and displays their +/// respective name & index +int RemoveGlobalVars::getTargetCount(Module *Program) { + // TODO: Silence index with --quiet flag + outs() << "----------------------------\n"; + outs() << "Chunk Index Reference:\n"; + int GVCount = 0; + for (auto &GV : Program->globals()) + if (GV.hasInitializer()) { + ++GVCount; + outs() << "\t" << GVCount << ": " << GV.getName() << "\n"; + } + outs() << "----------------------------\n"; + return GVCount; +} 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 @@ -8,8 +8,9 @@ // // This program tries to reduce an IR test case for a given interesting-ness // test. It runs multiple delta debugging passes in order to minimize the input -// file. It's worth noting that this is a *temporary* tool that will eventually -// be integrated into the bugpoint tool itself. +// file. It's worth noting that this is a part of the bugpoint redesign +// proposal, and thus a *temporary* tool that will eventually be integrated +// into the bugpoint tool itself. // //===----------------------------------------------------------------------===//