diff --git a/llvm/test/Reduce/remove-operand-bundles.ll b/llvm/test/Reduce/remove-operand-bundles.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Reduce/remove-operand-bundles.ll @@ -0,0 +1,41 @@ +; Test that llvm-reduce can remove uninteresting operand bundles from calls. +; +; RUN: rm -rf %t +; RUN: llvm-reduce --test FileCheck --test-arg --check-prefixes=CHECK-ALL,CHECK-INTERESTINGNESS --test-arg %s --test-arg --input-file %s -o %t +; RUN: cat %t | FileCheck --check-prefixes=CHECK-ALL,CHECK-FINAL %s + +; CHECK-ALL: declare void @f1() +; CHECK-ALL: declare void @f2() +; CHECK-ALL: declare void @f3() +declare void @f1() +declare void @f2() +declare void @f3() + +; CHECK-FINAL-LABEL: define void @interesting(i32 %arg0, i32 %arg1, i32 %arg2) { +; CHECK-FINAL-NEXT: entry: +; CHECK-FINAL-NEXT: call void @f1() [ "bundle0"(), "align"(i32 %arg0), "whatever0"() ] +; CHECK-FINAL-NEXT: call void @f2() +; CHECK-FINAL-NEXT: call void @f3() [ "align"(i32 %arg2) ] +; CHECK-FINAL-NEXT: ret void +; CHECK-FINAL-NEXT: } +define void @interesting(i32 %arg0, i32 %arg1, i32 %arg2) { +entry: +; CHECK-INTERESTINGNESS-LABEL: @interesting( + +; CHECK-INTERESTINGNESS: call void @f1() +; CHECK-INTERESTINGNESS: "bundle0"() +; CHECK-INTERESTINGNESS: "align"(i32 %arg0) +; CHECK-INTERESTINGNESS: "whatever0"() + +; CHECK-INTERESTINGNESS: call void @f2() + +; CHECK-INTERESTINGNESS: call void @f3() +; CHECK-INTERESTINGNESS: "align"(i32 %arg2) + +; CHECK-INTERESTINGNESS: ret + + call void @f1() [ "bundle0"(), "align"(i32 %arg0), "whatever0"() ] + call void @f2() [ "align"(i32 %arg1), "whatever1"(), "bundle1"() ] + call void @f3() [ "whatever2"(), "bundle2"(), "align"(i32 %arg2) ] + ret void +} 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 @@ -11,15 +11,16 @@ ) add_llvm_tool(llvm-reduce - llvm-reduce.cpp TestRunner.cpp deltas/Delta.cpp - deltas/ReduceFunctions.cpp - deltas/ReduceGlobalVars.cpp - deltas/ReduceMetadata.cpp deltas/ReduceArguments.cpp deltas/ReduceBasicBlocks.cpp + deltas/ReduceFunctions.cpp + deltas/ReduceGlobalVars.cpp deltas/ReduceInstructions.cpp + deltas/ReduceMetadata.cpp + deltas/ReduceOperandBundles.cpp + llvm-reduce.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 @@ -17,8 +17,9 @@ #include "deltas/ReduceBasicBlocks.h" #include "deltas/ReduceFunctions.h" #include "deltas/ReduceGlobalVars.h" -#include "deltas/ReduceMetadata.h" #include "deltas/ReduceInstructions.h" +#include "deltas/ReduceMetadata.h" +#include "deltas/ReduceOperandBundles.h" namespace llvm { @@ -30,6 +31,7 @@ reduceMetadataDeltaPass(Tester); reduceArgumentsDeltaPass(Tester); reduceInstructionsDeltaPass(Tester); + reduceOperandBundesDeltaPass(Tester); // TODO: Implement the remaining Delta Passes } diff --git a/llvm/tools/llvm-reduce/deltas/ReduceOperandBundles.h b/llvm/tools/llvm-reduce/deltas/ReduceOperandBundles.h new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-reduce/deltas/ReduceOperandBundles.h @@ -0,0 +1,20 @@ +//===- ReduceOperandBundes.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 a function which calls the Generic Delta pass in order +// to reduce uninteresting operand bundes from calls. +// +//===----------------------------------------------------------------------===// + +namespace llvm { + +class TestRunner; + +void reduceOperandBundesDeltaPass(TestRunner &Test); + +} // namespace llvm diff --git a/llvm/tools/llvm-reduce/deltas/ReduceOperandBundles.cpp b/llvm/tools/llvm-reduce/deltas/ReduceOperandBundles.cpp new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-reduce/deltas/ReduceOperandBundles.cpp @@ -0,0 +1,161 @@ +//===- ReduceOperandBundes.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 a function which calls the Generic Delta pass in order +// to reduce uninteresting operand bundes from calls. +// +//===----------------------------------------------------------------------===// + +#include "ReduceOperandBundles.h" +#include "Delta.h" +#include "TestRunner.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/ScopeExit.h" +#include "llvm/ADT/Sequence.h" +#include "llvm/ADT/iterator_range.h" +#include "llvm/IR/InstVisitor.h" +#include "llvm/IR/InstrTypes.h" +#include "llvm/Support/raw_ostream.h" +#include +#include +#include + +namespace { +class Module; +} // namespace + +using namespace llvm; + +namespace { + +/// Provides opaque interface for querying into ChunksToKeep without having to +/// actually understand what is going on. +struct Oracle { + /// Out of all the features that we promised to be, + /// how many have we already processed? 1-based! + int Index = 1; + + /// The actual workhorse, contains the knowledge whether or not + /// some particular feature should be preserved this time. + ArrayRef ChunksToKeep; + +public: + Oracle(ArrayRef ChunksToKeep_) : ChunksToKeep(ChunksToKeep_) {} + + /// Should be called for each feature on which we are operating. + /// Name is self-explanatory - if returns true, then it should be preserved. + bool shouldKeep() { + if (ChunksToKeep.empty()) + return false; // All further features are to be discarded. + + // Does the current (front) chunk contain such a feature? + bool ShouldKeep = ChunksToKeep.front().contains(Index); + auto _ = make_scope_exit([&]() { ++Index; }); // Next time - next feature. + + // Is this the last feature in the chunk? + if (ChunksToKeep.front().end == Index) + ChunksToKeep = ChunksToKeep.drop_front(); // Onto next chunk. + + return ShouldKeep; + } +}; + +/// Given ChunksToKeep, produce a map of calls and indexes of operand bundles +/// to be preserved for each call. +class OperandBundleRemapper : public InstVisitor { + Oracle O; + +public: + DenseMap> CallsToRefine; + + explicit OperandBundleRemapper(ArrayRef ChunksToKeep) + : O(ChunksToKeep) {} + + /// So far only CallBase sub-classes can have operand bundles. + /// Let's see which of the operand bundles of this call are to be kept. + void visitCallBase(CallBase &Call) { + if (!Call.hasOperandBundles()) + return; // No bundles to begin with. + + // Insert this call into map, we will likely want to rebuild it. + auto &OperandBundlesToKeepIndexes = CallsToRefine[&Call]; + OperandBundlesToKeepIndexes.reserve(Call.getNumOperandBundles()); + + // Enumerate every operand bundle on this call. + for_each(seq(0U, Call.getNumOperandBundles()), [&](unsigned BundleIndex) { + if (O.shouldKeep()) // Should we keep this one? + OperandBundlesToKeepIndexes.emplace_back(BundleIndex); + }); + } +}; + +struct OperandBundleCounter : public InstVisitor { + /// How many features (in this case, operand bundles) did we count, total? + int OperandBundeCount = 0; + + OperandBundleCounter() {} + + /// So far only CallBase sub-classes can have operand bundles. + void visitCallBase(CallBase &Call) { + // Just accumulate the total number of operand bundles. + OperandBundeCount += Call.getNumOperandBundles(); + } +}; + +} // namespace + +static void maybeRewriteCallWithDifferentBundles( + CallBase *OrigCall, ArrayRef OperandBundlesToKeepIndexes) { + if (OperandBundlesToKeepIndexes.size() == OrigCall->getNumOperandBundles()) + return; // Not modifying operand bundles of this call after all. + + std::vector NewBundles; + NewBundles.reserve(OperandBundlesToKeepIndexes.size()); + + // Actually copy over the bundles that we want to keep. + transform(OperandBundlesToKeepIndexes, std::back_inserter(NewBundles), + [OrigCall](unsigned Index) { + return OperandBundleDef(OrigCall->getOperandBundleAt(Index)); + }); + + // Finally actually replace the bundles on the call. + CallBase *NewCall = CallBase::Create(OrigCall, NewBundles, OrigCall); + OrigCall->replaceAllUsesWith(NewCall); + OrigCall->eraseFromParent(); +} + +/// Removes out-of-chunk operand bundles from calls. +static void extractOperandBundesFromModule(std::vector ChunksToKeep, + Module *Program) { + OperandBundleRemapper R(ChunksToKeep); + R.visit(Program); + + for_each(R.CallsToRefine, [](const auto &P) { + return maybeRewriteCallWithDifferentBundles(P.first, P.second); + }); +} + +/// Counts the amount of operand bundles. +static int countOperandBundes(Module *Program) { + OperandBundleCounter C; + + // TODO: Silence index with --quiet flag + outs() << "----------------------------\n"; + C.visit(Program); + outs() << "Number of operand bundles: " << C.OperandBundeCount << "\n"; + + return C.OperandBundeCount; +} + +void llvm::reduceOperandBundesDeltaPass(TestRunner &Test) { + outs() << "*** Reducing OperandBundes...\n"; + int OperandBundeCount = countOperandBundes(Test.getProgram()); + runDeltaPass(Test, OperandBundeCount, extractOperandBundesFromModule); +} diff --git a/llvm/utils/gn/secondary/llvm/tools/llvm-reduce/BUILD.gn b/llvm/utils/gn/secondary/llvm/tools/llvm-reduce/BUILD.gn --- a/llvm/utils/gn/secondary/llvm/tools/llvm-reduce/BUILD.gn +++ b/llvm/utils/gn/secondary/llvm/tools/llvm-reduce/BUILD.gn @@ -17,6 +17,7 @@ "deltas/ReduceGlobalVars.cpp", "deltas/ReduceInstructions.cpp", "deltas/ReduceMetadata.cpp", + "deltas/ReduceOperandBundes.cpp", "llvm-reduce.cpp", ] }