diff --git a/polly/include/polly/LinkAllPasses.h b/polly/include/polly/LinkAllPasses.h index 7960b4a95cd201..ccda4ef650f211 100644 --- a/polly/include/polly/LinkAllPasses.h +++ b/polly/include/polly/LinkAllPasses.h @@ -16,6 +16,7 @@ #include "polly/CodeGen/PPCGCodeGeneration.h" #include "polly/Config/config.h" +#include "polly/Support/DumpFunctionPass.h" #include "polly/Support/DumpModulePass.h" #include "llvm/ADT/StringRef.h" #include @@ -100,6 +101,7 @@ struct PollyForcePassLinking { polly::createForwardOpTreeWrapperPass(); polly::createDeLICMWrapperPass(); polly::createDumpModuleWrapperPass("", true); + polly::createDumpFunctionWrapperPass(""); polly::createSimplifyWrapperPass(0); polly::createPruneUnprofitableWrapperPass(); } diff --git a/polly/include/polly/Support/DumpFunctionPass.h b/polly/include/polly/Support/DumpFunctionPass.h new file mode 100644 index 00000000000000..21dfc632fc281a --- /dev/null +++ b/polly/include/polly/Support/DumpFunctionPass.h @@ -0,0 +1,43 @@ +//===------ DumpFunctionPass.cpp --------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Write a function to a file. +// +//===----------------------------------------------------------------------===// + +#ifndef POLLY_SUPPORT_DUMPFUNCTIONPASS_H +#define POLLY_SUPPORT_DUMPFUNCTIONPASS_H + +#include "llvm/IR/PassManager.h" +#include + +namespace llvm { +class ModulePass; +} // namespace llvm + +namespace polly { +llvm::FunctionPass *createDumpFunctionWrapperPass(std::string Suffix); + +/// A pass that isolates a function into a new Module and writes it into a file. +struct DumpFunctionPass : llvm::PassInfoMixin { + std::string Suffix; + + DumpFunctionPass(std::string Suffix) : Suffix(std::move(Suffix)) {} + + llvm::PreservedAnalyses run(llvm::Function &F, + llvm::FunctionAnalysisManager &AM); +}; + +} // namespace polly + +namespace llvm { +class PassRegistry; +void initializeDumpFunctionWrapperPassPass(llvm::PassRegistry &); +} // namespace llvm + +#endif /* POLLY_SUPPORT_DUMPFUNCTIONPASS_H */ diff --git a/polly/lib/CMakeLists.txt b/polly/lib/CMakeLists.txt index 65fed2634ab857..9a967b106ea65f 100644 --- a/polly/lib/CMakeLists.txt +++ b/polly/lib/CMakeLists.txt @@ -83,6 +83,7 @@ add_llvm_pass_plugin(Polly Support/ScopLocation.cpp Support/ISLTools.cpp Support/DumpModulePass.cpp + Support/DumpFunctionPass.cpp Support/VirtualInstruction.cpp Transform/Canonicalization.cpp Transform/CodePreparation.cpp diff --git a/polly/lib/Support/DumpFunctionPass.cpp b/polly/lib/Support/DumpFunctionPass.cpp new file mode 100644 index 00000000000000..313fc711593487 --- /dev/null +++ b/polly/lib/Support/DumpFunctionPass.cpp @@ -0,0 +1,121 @@ +//===------ DumpFunctionPass.cpp --------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Write a function to a file. +// +//===----------------------------------------------------------------------===// + +#include "polly/Support/DumpFunctionPass.h" +#include "llvm/IR/Module.h" +#include "llvm/Pass.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Transforms/IPO/GlobalDCE.h" +#include "llvm/Transforms/IPO/StripDeadPrototypes.h" +#include "llvm/Transforms/Utils/Cloning.h" + +#define DEBUG_TYPE "polly-dump-func" + +using namespace llvm; +using namespace polly; + +namespace { + +static void runDumpFunction(llvm::Function &F, StringRef Suffix) { + StringRef FName = F.getName(); + Module *M = F.getParent(); + + StringRef ModuleName = M->getName(); + StringRef Stem = sys::path::stem(ModuleName); + std::string Dumpfile = (Twine(Stem) + "-" + FName + Suffix + ".ll").str(); + LLVM_DEBUG(dbgs() << "Dumping function '" << FName << "' to '" << Dumpfile + << "'...\n"); + + ValueToValueMapTy VMap; + auto ShouldCloneDefinition = [&F](const GlobalValue *GV) -> bool { + return GV == &F; + }; + std::unique_ptr CM = CloneModule(*M, VMap, ShouldCloneDefinition); + + LLVM_DEBUG(dbgs() << "Global DCE...\n"); + + { + ModuleAnalysisManager MAM; + ModulePassManager MPM; + + PassInstrumentationCallbacks PIC; + MAM.registerPass([&] { return PassInstrumentationAnalysis(&PIC); }); + + MPM.addPass(GlobalDCEPass()); + MPM.addPass(StripDeadPrototypesPass()); + MPM.run(*CM, MAM); + } + + LLVM_DEBUG(dbgs() << "Write to file '" << Dumpfile << "'...\n"); + + std::unique_ptr Out; + std::error_code EC; + Out.reset(new ToolOutputFile(Dumpfile, EC, sys::fs::OF_None)); + if (EC) { + errs() << EC.message() << '\n'; + return; + } + + CM->print(Out->os(), nullptr); + Out->keep(); + LLVM_DEBUG(dbgs() << "Dump file " << Dumpfile << " written successfully\n"); +} + +class DumpFunctionWrapperPass : public FunctionPass { +private: + DumpFunctionWrapperPass(const DumpFunctionWrapperPass &) = delete; + const DumpFunctionWrapperPass & + operator=(const DumpFunctionWrapperPass &) = delete; + + std::string Suffix; + +public: + static char ID; + + explicit DumpFunctionWrapperPass() : FunctionPass(ID), Suffix("-dump") {} + + explicit DumpFunctionWrapperPass(std::string Suffix) + : FunctionPass(ID), Suffix(std::move(Suffix)) {} + + /// @name FunctionPass interface + //@{ + virtual void getAnalysisUsage(llvm::AnalysisUsage &AU) const override { + AU.setPreservesAll(); + } + + virtual bool runOnFunction(llvm::Function &F) override { + runDumpFunction(F, Suffix); + return false; + } + //@} +}; + +char DumpFunctionWrapperPass::ID; +} // namespace + +FunctionPass *polly::createDumpFunctionWrapperPass(std::string Suffix) { + return new DumpFunctionWrapperPass(std::move(Suffix)); +} + +llvm::PreservedAnalyses DumpFunctionPass::run(Function &F, + FunctionAnalysisManager &AM) { + runDumpFunction(F, Suffix); + return PreservedAnalyses::all(); +} + +INITIALIZE_PASS_BEGIN(DumpFunctionWrapperPass, "polly-dump-function", + "Polly - Dump Function", false, false) +INITIALIZE_PASS_END(DumpFunctionWrapperPass, "polly-dump-function", + "Polly - Dump Function", false, false) diff --git a/polly/lib/Support/RegisterPasses.cpp b/polly/lib/Support/RegisterPasses.cpp index f9475a2fe08b15..ebc0a7ee41ebe7 100644 --- a/polly/lib/Support/RegisterPasses.cpp +++ b/polly/lib/Support/RegisterPasses.cpp @@ -36,6 +36,7 @@ #include "polly/ScopDetection.h" #include "polly/ScopInfo.h" #include "polly/Simplify.h" +#include "polly/Support/DumpFunctionPass.h" #include "polly/Support/DumpModulePass.h" #include "llvm/Analysis/CFGPrinter.h" #include "llvm/IR/LegacyPassManager.h" @@ -610,19 +611,19 @@ static void buildLatePollyPipeline(FunctionPassManager &PM, return; if (DumpBefore) - report_fatal_error("Option -polly-dump-before not supported with NPM", - false); + PM.addPass(DumpFunctionPass("-before")); if (!DumpBeforeFile.empty()) - report_fatal_error("Option -polly-dump-before-file not supported with NPM", + report_fatal_error("Option -polly-dump-before-file at -polly-position=late " + "not supported with NPM", false); buildCommonPollyPipeline(PM, Level, EnableForOpt); if (DumpAfter) - report_fatal_error("Option -polly-dump-after not supported with NPM", - false); + PM.addPass(DumpFunctionPass("-after")); if (!DumpAfterFile.empty()) - report_fatal_error("Option -polly-dump-after-file not supported with NPM", + report_fatal_error("Option -polly-dump-after-file at -polly-position=late " + "not supported with NPM", false); } diff --git a/polly/test/Support/dumpfunction.ll b/polly/test/Support/dumpfunction.ll new file mode 100644 index 00000000000000..70b586bd244b1e --- /dev/null +++ b/polly/test/Support/dumpfunction.ll @@ -0,0 +1,94 @@ +; New pass manager +; RUN: opt %loadPolly -enable-new-pm=1 -O3 -polly -polly-position=before-vectorizer -polly-dump-before --disable-output %s +; RUN: FileCheck --input-file=dumpfunction-callee-before.ll --check-prefix=CHECK --check-prefix=CALLEE %s +; RUN: FileCheck --input-file=dumpfunction-caller-before.ll --check-prefix=CHECK --check-prefix=CALLER %s +; +; RUN: opt %loadPolly -enable-new-pm=1 -O3 -polly -polly-position=before-vectorizer -polly-dump-after --disable-output %s +; RUN: FileCheck --input-file=dumpfunction-callee-before.ll --check-prefix=CHECK --check-prefix=CALLEE %s +; RUN: FileCheck --input-file=dumpfunction-caller-before.ll --check-prefix=CHECK --check-prefix=CALLER %s + +; void callee(int n, double A[], int i) { +; for (int j = 0; j < n; j += 1) +; A[i+j] = 42.0; +; } +; +; void caller(int n, double A[]) { +; for (int i = 0; i < n; i += 1) +; callee(n, A, i); +; } + + +%unrelated_type = type { i32 } + +@callee_alias = dso_local unnamed_addr alias void(i32, double*, i32), void(i32, double*, i32 )* @callee + +define void @callee(i32 %n, double* noalias nonnull %A, i32 %i) { +entry: + br label %for + +for: + %j = phi i32 [0, %entry], [%j.inc, %inc] + %j.cmp = icmp slt i32 %j, %n + br i1 %j.cmp, label %body, label %exit + + body: + %idx = add i32 %i, %j + %arrayidx = getelementptr inbounds double, double* %A, i32 %idx + store double 42.0, double* %arrayidx + br label %inc + +inc: + %j.inc = add nuw nsw i32 %j, 1 + br label %for + +exit: + br label %return + +return: + ret void +} + + +define void @caller(i32 %n, double* noalias nonnull %A) { +entry: + br label %for + +for: + %i = phi i32 [0, %entry], [%j.inc, %inc] + %i.cmp = icmp slt i32 %i, %n + br i1 %i.cmp, label %body, label %exit + + body: + call void @callee_alias(i32 %n, double* %A, i32 %i) + br label %inc + +inc: + %j.inc = add nuw nsw i32 %i, 1 + br label %for + +exit: + br label %return + +return: + ret void +} + + +declare void @unrelated_decl() + + +!llvm.ident = !{!8} +!8 = !{!"xyxxy"} + + +; CHECK-NOT: unrelated_type + +; CALLEE-LABEL: @callee( +; CALLEE-NOT: @caller +; CALLEE-NOT: @unrelated_decl + +; CALLER-NOT: @callee( +; CALLER-LABEL: @caller( + +; CHECK-NOT: @unrelated_decl +; CHECK: xyxxy