From: Keno Fischer Date: Fri, 4 Dec 2015 21:56:46 +0000 (+0000) Subject: [llc/opt] Add an option to run all passes twice X-Git-Url: http://plrg.eecs.uci.edu/git/?p=oota-llvm.git;a=commitdiff_plain;h=6f41c1352b5e860ce6fd737f1d69a13f810c0311;hp=b590f81c153265bc3ce6173161642b7fbc5b8a1c [llc/opt] Add an option to run all passes twice Summary: Lately, I have submitted a number of patches to fix bugs that only occurred when using the same pass manager to compile multiple modules (generally these bugs are failure to reset some persistent state). Unfortunately I don't think there is currently a way to test that from the command line. This adds a very simple flag to both llc and opt, under which the tools will simply re-run their respective pass pipelines using the same pass manager on (a clone of the same module). Additionally, we verify that both outputs are bitwise the same. Reviewers: yaron.keren Subscribers: loladiro, yaron.keren, kcc, llvm-commits Differential Revision: http://reviews.llvm.org/D14965 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@254774 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/test/MC/ELF/empty-twice.ll b/test/MC/ELF/empty-twice.ll new file mode 100644 index 00000000000..c24bd629c41 --- /dev/null +++ b/test/MC/ELF/empty-twice.ll @@ -0,0 +1,6 @@ +; Check that there is no persistent state in the ELF emitter that crashes us +; when we try to reuse the pass manager +; RUN: llc -compile-twice -filetype=obj %s -o - + +target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32" +target triple = "i386-pc-linux-gnu" diff --git a/test/Other/opt-twice.ll b/test/Other/opt-twice.ll new file mode 100644 index 00000000000..6bff52e34e3 --- /dev/null +++ b/test/Other/opt-twice.ll @@ -0,0 +1,14 @@ +; The pass here doesn't matter (we use deadargelim), but test +; that the -run-twice options exists, generates output, and +; doesn't crash +; RUN: opt -run-twice -deadargelim -S < %s | FileCheck %s + +; CHECK: define internal void @test +define internal {} @test() { + ret {} undef +} + +define void @caller() { + call {} @test() + ret void +} diff --git a/tools/llc/llc.cpp b/tools/llc/llc.cpp index c51c012391b..531aba1f64b 100644 --- a/tools/llc/llc.cpp +++ b/tools/llc/llc.cpp @@ -45,6 +45,7 @@ #include "llvm/Support/ToolOutputFile.h" #include "llvm/Target/TargetMachine.h" #include "llvm/Target/TargetSubtargetInfo.h" +#include "llvm/Transforms/Utils/Cloning.h" #include using namespace llvm; @@ -96,6 +97,12 @@ static cl::opt AsmVerbose("asm-verbose", cl::desc("Add comments to directives."), cl::init(true)); +static cl::opt + CompileTwice("compile-twice", cl::Hidden, + cl::desc("Run everything twice, re-using the same pass " + "manager and verify the the result is the same."), + cl::init(false)); + static int compileModule(char **, LLVMContext &); static std::unique_ptr @@ -325,10 +332,15 @@ static int compileModule(char **argv, LLVMContext &Context) { { raw_pwrite_stream *OS = &Out->os(); - std::unique_ptr BOS; - if (FileType != TargetMachine::CGFT_AssemblyFile && - !Out->os().supportsSeeking()) { - BOS = make_unique(*OS); + + // Manually do the buffering rather than using buffer_ostream, + // so we can memcmp the contents in CompileTwice mode + SmallVector Buffer; + std::unique_ptr BOS; + if ((FileType != TargetMachine::CGFT_AssemblyFile && + !Out->os().supportsSeeking()) || + CompileTwice) { + BOS = make_unique(Buffer); OS = BOS.get(); } @@ -378,7 +390,39 @@ static int compileModule(char **argv, LLVMContext &Context) { // Before executing passes, print the final values of the LLVM options. cl::PrintOptionValues(); + // If requested, run the pass manager over the same module again, + // to catch any bugs due to persistent state in the passes. Note that + // opt has the same functionality, so it may be worth abstracting this out + // in the future. + SmallVector CompileTwiceBuffer; + if (CompileTwice) { + std::unique_ptr M2(llvm::CloneModule(M.get())); + PM.run(*M2); + CompileTwiceBuffer = Buffer; + Buffer.clear(); + } + PM.run(*M); + + // Compare the two outputs and make sure they're the same + if (CompileTwice) { + if (Buffer.size() != CompileTwiceBuffer.size() || + (memcmp(Buffer.data(), CompileTwiceBuffer.data(), Buffer.size()) != + 0)) { + errs() + << "Running the pass manager twice changed the output.\n" + "Writing the result of the second run to the specified output\n" + "To generate the one-run comparison binary, just run without\n" + "the compile-twice option\n"; + Out->os() << Buffer; + Out->keep(); + return 1; + } + } + + if (BOS) { + Out->os() << Buffer; + } } // Declare success. diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp index 5fe2f034c6e..c1510a7fb25 100644 --- a/tools/opt/opt.cpp +++ b/tools/opt/opt.cpp @@ -28,6 +28,7 @@ #include "llvm/IR/DebugInfo.h" #include "llvm/IR/IRPrintingPasses.h" #include "llvm/IR/LLVMContext.h" +#include "llvm/IR/LegacyPassManager.h" #include "llvm/IR/LegacyPassNameParser.h" #include "llvm/IR/Module.h" #include "llvm/IR/Verifier.h" @@ -36,7 +37,6 @@ #include "llvm/LinkAllIR.h" #include "llvm/LinkAllPasses.h" #include "llvm/MC/SubtargetFeature.h" -#include "llvm/IR/LegacyPassManager.h" #include "llvm/Support/Debug.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Host.h" @@ -51,6 +51,7 @@ #include "llvm/Support/ToolOutputFile.h" #include "llvm/Target/TargetMachine.h" #include "llvm/Transforms/IPO/PassManagerBuilder.h" +#include "llvm/Transforms/Utils/Cloning.h" #include #include using namespace llvm; @@ -190,6 +191,11 @@ static cl::opt PreserveAssemblyUseListOrder( cl::desc("Preserve use-list order when writing LLVM assembly."), cl::init(false), cl::Hidden); +static cl::opt + RunTwice("run-twice", + cl::desc("Run all passes twice, re-using the same pass manager."), + cl::init(false), cl::Hidden); + static inline void addPass(legacy::PassManagerBase &PM, Pass *P) { // Add the pass to the pass manager... PM.add(P); @@ -582,14 +588,25 @@ int main(int argc, char **argv) { if (!NoVerify && !VerifyEach) Passes.add(createVerifierPass()); + // In run twice mode, we want to make sure the output is bit-by-bit + // equivalent if we run the pass manager again, so setup two buffers and + // a stream to write to them. Note that llc does something similar and it + // may be worth to abstract this out in the future. + SmallVector Buffer; + SmallVector CompileTwiceBuffer; + std::unique_ptr BOS; + raw_ostream *OS = &Out->os(); + if (RunTwice) { + BOS = make_unique(Buffer); + OS = BOS.get(); + } + // Write bitcode or assembly to the output as the last step... if (!NoOutput && !AnalyzeOnly) { if (OutputAssembly) - Passes.add( - createPrintModulePass(Out->os(), "", PreserveAssemblyUseListOrder)); + Passes.add(createPrintModulePass(*OS, "", PreserveAssemblyUseListOrder)); else - Passes.add( - createBitcodeWriterPass(Out->os(), PreserveBitcodeUseListOrder)); + Passes.add(createBitcodeWriterPass(*OS, PreserveBitcodeUseListOrder)); } // Before executing passes, print the final values of the LLVM options. @@ -598,6 +615,27 @@ int main(int argc, char **argv) { // Now that we have all of the passes ready, run them. Passes.run(*M); + // If requested, run all passes again with the same pass manager to catch + // bugs caused by persistent state in the passes + if (RunTwice) { + CompileTwiceBuffer = Buffer; + Buffer.clear(); + std::unique_ptr M2(CloneModule(M.get())); + Passes.run(*M2); + if (Buffer.size() != CompileTwiceBuffer.size() || + (memcmp(Buffer.data(), CompileTwiceBuffer.data(), Buffer.size()) != + 0)) { + errs() << "Running the pass manager twice changed the output.\n" + "Writing the result of the second run to the specified output." + "To generate the one-run comparison binary, just run without\n" + "the compile-twice option\n"; + Out->os() << BOS->str(); + Out->keep(); + return 1; + } + Out->os() << BOS->str(); + } + // Declare success. if (!NoOutput || PrintBreakpoints) Out->keep();