Index: llvm/include/llvm/IR/ChangePrinters.h =================================================================== --- /dev/null +++ llvm/include/llvm/IR/ChangePrinters.h @@ -0,0 +1,292 @@ +//===- ChangePrinters.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. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// \file +/// +/// This header defines classes that generate views of changes to the IR +/// in the opt pipeline of the new pass manager. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_CHANGEPRINTERS_H +#define LLVM_SUPPORT_CHANGEPRINTERS_H + +#include "llvm/ADT/Any.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/IR/PassInstrumentation.h" + +#include +#include + +namespace llvm { + +class Function; +class Module; +class BasicBlock; +class Any; + +} // namespace llvm + +// namespace for data structures and templates for change printers +namespace cp { + +// Base class for classes that report changes to the IR. +// It presents an interface for such classes and provides callbacks +// on various events as the new pass manager transforms the IR. +// It also provides filtering of information based on hidden options +// specifying which functions or passes are interesting. +// Callbacks are made for the following events/queries: +// 1. The initial IR processed. +// 2. To get the representation of the IR (of type \p T). +// 3. When a pass does not change the IR. +// 4. When a pass changes the IR (given both before and after representations +// of type \p T). +// 5. When an IR is invalidated. +// 6. When a pass is run on an IR that is not interesting (based on options). +// 7. When a pass is ignored (pass manager or adapter pass). +// 8. To compare two IR representations (of type \p T). +template class ChangePrinter { +protected: + ChangePrinter( + std::function F1, + std::function F2, + std::function F3, + std::function + F4, + std::function F5, + std::function F6, + std::function F7, + std::function F8) + : HandleInitialIR(F1), GenerateIRRepresentation(F2), OmitAfter(F3), + HandleAfter(F4), HandleInvalidated(F5), HandleFiltered(F6), + HandleIgnored(F7), Same(F8), InitialIR(true) {} + +public: + // Not virtual as classes are expected to be referenced as derived classes. + ~ChangePrinter() { + assert(BeforeStack.empty() && "Problem with Change Printer stack."); + } + + // Register standard callbacks that call out on the lambdas + void registerCallbacks(llvm::PassInstrumentationCallbacks &PIC) { + PIC.registerBeforePassCallback([this](llvm::StringRef P, llvm::Any IR) { + saveIRBeforePass(IR, P); + return true; + }); + + PIC.registerAfterPassCallback( + [this](llvm::StringRef P, llvm::Any IR, + const llvm::PreservedAnalyses &) { handleIRAfterPass(IR, P); }); + PIC.registerAfterPassInvalidatedCallback( + [this](llvm::StringRef P, const llvm::PreservedAnalyses &) { + handleInvalidatedPass(P); + }); + } + +protected: + // Determine if this pass/IR is interesting and if so, save the IR + // otherwise it is left on the stack without data + void saveIRBeforePass(llvm::Any IR, llvm::StringRef PassID); + // Compare the IR from before the pass after the pass. + void handleIRAfterPass(llvm::Any IR, llvm::StringRef PassID); + // Handle the situation where a pass is invalidated. + void handleInvalidatedPass(llvm::StringRef PassID); + +private: + // callback on the first IR processed + std::function HandleInitialIR; + // callback before and after a pass to get the representation of the IR + std::function + GenerateIRRepresentation; + // callback when the pass is not iteresting + std::function OmitAfter; + // callback when interesting IR has changed + std::function + HandleAfter; + // callback when an interesting pass is invalidated + std::function HandleInvalidated; + // callback when the IR or pass is not interesting + std::function HandleFiltered; + // callback when an ignored pass is encountered + std::function HandleIgnored; + // callback to compare the before and after representations of the IR + std::function Same; + + // stack of IRs before passes + std::vector BeforeStack; + // Is this the first IR seen? + bool InitialIR; +}; + +// The following template base classes contain the necessary code to +// create a change printer that uses string comparisons of the basic blocks +// that are created using Value::print (ie, similar to dump()). +// These classes allow one to associate extra data with each basic block +// and the structs representing the basic blocks are grouped into +// the structs representing the functions. Essentially, lambdas provided +// in the construction of a class derived from ChangePrinter use a +// class derived from IRComparer to compare representations of IRs. +// Lambdas in the derived instantiating classes are called appropriately +// when changes are detected after each pass to generate the output while the +// mechanics of detecting changes is handled in the base classes. The +// saving of extra data that may be desired for the lambdas is done +// in a class derived from IRComparer, as is the actual generation +// of the output in response to changes to the IR. +// +// This respects the filtering of passes and functions using -filter-passes +// and -filter-print-funcs. + +// Information that needs to be saved for a basic block in order to compare +// before and after the pass to determine if it was changed by a pass +template class BlockDataTemplate { +public: + BlockDataTemplate(const llvm::BasicBlock &B); + + bool operator!=(const BlockDataTemplate &that) const { + return Body.compare(that.Body) != 0; + } + // Return the label of the represented basic block. + llvm::StringRef getLabel() const { return Label; } + // Return the string representation of the basic block. + llvm::StringRef getBody() const { return Body; } + + // Return the associated data + const BlockDataType &getData() const { return Data; } + +protected: + std::string Label; + std::string Body; + + // Extra data associated with a basic block + BlockDataType Data; +}; + +// The data saved for comparing functions. +template class FuncDataTemplate { +public: + FuncDataTemplate(const llvm::Function &F); + + // Create data for a missing function that is present in the other IR + FuncDataTemplate(llvm::StringRef Name) { EntryBlockName = Name.str(); } + + bool operator==(const FuncDataTemplate &that) const; + + bool operator!=(const FuncDataTemplate &that) const { + return !(*this == that); + } + + // Iterator over the saved basic block data + typename llvm::StringMap>::const_iterator + begin() const { + return Blocks.begin(); + } + typename llvm::StringMap>::const_iterator + end() const { + return Blocks.end(); + } + + // Return the name of the entry block + std::string getEntryBlockName() const { return EntryBlockName; } + + // Return the block data for the basic block with label \p S. + const BlockDataTemplate * + getBlockData(const llvm::StringRef S) const { + return (Blocks.count(S) == 1) ? &Blocks.find(S)->getValue() : nullptr; + } + +protected: + std::string EntryBlockName; + llvm::StringMap> Blocks; +}; + +// A map of names to the saved data. +template class IRDataTemplate : public llvm::StringMap { +public: + IRDataTemplate() = default; + bool operator==(const IRDataTemplate &that); + bool operator!=(const IRDataTemplate &that) { return !(*this == that); } +}; + +// Template base class for a class that compares two IRs. The class is +// created with the 2 IRs to compare and then compare is called. The +// static function analyzeIR is used to build up the IR representation. +template +class IRComparer { +protected: + enum IRChangeDiffType { InBefore, InAfter, IsCommon }; + + IRComparer(IRDataType &Before, IRDataType &After, unsigned N) + : Before(Before), After(After), N(N) {} + +public: + // Comare the 2 IRs. In the case of a module IR, delegate to + // handleFunctionCompare in the derived class for each function; + // otherwise delegate to handleCompare in the derived class. + void compare(llvm::Any IR, llvm::StringRef Prefix, llvm::StringRef PassID, + llvm::StringRef Name); + + // Analyze \p IR and save the data in \p Data. + static bool analyzeIR(llvm::Any IR, IRDataType &Data); + +protected: + // Return the module when that is the appropriate level of + // comparison for \p IR. + static const llvm::Module *getModule(llvm::Any IR); + + // Generate the data for \p F into \p Data. + static bool generateFunctionData(IRDataType &Data, const llvm::Function &F); + + IRDataType &Before; + IRDataType &After; + unsigned N; +}; + +// An empty class that satisfies the requirements for being a template argument +// for the abover comparer template base classes. +class EmptyData { +public: + static void initialize(EmptyData &, const llvm::BasicBlock &) {} +}; + +using EmptyDataBlockData = BlockDataTemplate; +using EmptyDataFuncData = FuncDataTemplate; +using EmptyDataIRData = IRDataTemplate; + +// A template base class that handles printing banners and reporting +// when things have not changed or are filtered out. +template class IRChangePrinter : public ChangePrinter { +protected: + IRChangePrinter( + std::function F2, + std::function + F4, + std::function F8); + + // Print a module dump of the first IR that is changed. + void handleInitialIR(llvm::Any IR); + // Report that the IR was omitted because it did not change + void omitAfter(llvm::StringRef PassID, std::string &Name); + // Report that the pass was invalidated. + void handleInvalidated(llvm::StringRef PassID); + // Report that the IR was filtered out. + void handleFiltered(llvm::StringRef PassID, std::string &Name); + // Report that the pass was ignored. + void handleIgnored(llvm::StringRef PassID, std::string &Name); + // Make substitutions in \p S suitable for reporting changes + // after the pass and then print it. + + llvm::raw_ostream &Out; +}; + +} // namespace cp + +#endif Index: llvm/include/llvm/Passes/StandardInstrumentations.h =================================================================== --- llvm/include/llvm/Passes/StandardInstrumentations.h +++ llvm/include/llvm/Passes/StandardInstrumentations.h @@ -17,6 +17,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" +#include "llvm/IR/ChangePrinters.h" #include "llvm/IR/PassInstrumentation.h" #include "llvm/IR/PassTimingInfo.h" @@ -27,6 +28,18 @@ class Function; class Module; +class BasicBlock; +class Any; +class raw_ostream; + +/// Extracting Module out of \p IR unit. Also fills a textual description +/// of \p IR for use in header when printing. +Optional> unwrapModule(Any IR); + +/// Generic IR-printing helper that unpacks a pointer to IRUnit wrapped into +/// llvm::Any and does actual print job. +void unwrapAndPrint(raw_ostream &OS, Any IR, StringRef Banner, + bool ForceModule = false, bool Brief = false); /// Instrumentation to print IR before/after passes. /// @@ -74,89 +87,26 @@ bool DebugLogging; }; -// Base class for classes that report changes to the IR. -// It presents an interface for such classes and provides callbacks -// on various events as the new pass manager transforms the IR. -// It also provides filtering of information based on hidden options -// specifying which functions are interesting. -// Callbacks are made for the following events/queries: -// 1. The initial IR processed. -// 2. To get the representation of the IR (of type \p T). -// 3. When a pass does not change the IR. -// 4. When a pass changes the IR (given both before and after representations -// of type \p T). -// 5. When an IR is invalidated. -// 6. When a pass is run on an IR that is not interesting (based on options). -// 7. When a pass is ignored (pass manager or adapter pass). -// 8. To compare two IR representations (of type \p T). -template class ChangePrinter { -protected: - ChangePrinter(std::function F1, - std::function F2, - std::function F3, - std::function - F4, - std::function F5, - std::function F6, - std::function F7, - std::function F8) - : HandleInitialIR(F1), GenerateIRRepresentation(F2), OmitAfter(F3), - HandleAfter(F4), HandleInvalidated(F5), HandleFiltered(F6), - HandleIgnored(F7), Same(F8), InitialIR(true) {} - -public: - // Not virtual as classes are expected to be referenced as derived classes. - ~ChangePrinter() { - assert(BeforeStack.empty() && "Problem with Change Printer stack."); - } - - // Determine if this pass/IR is interesting and if so, save the IR - // otherwise it is left on the stack without data - void saveIRBeforePass(Any IR, StringRef PassID); - // Compare the IR from before the pass after the pass. - void handleIRAfterPass(Any IR, StringRef PassID); - // Handle the situation where a pass is invalidated. - void handleInvalidatedPass(StringRef PassID); - -private: - // callback on the first IR processed - std::function HandleInitialIR; - // callback before and after a pass to get the representation of the IR - std::function - GenerateIRRepresentation; - // callback when the pass is not iteresting - std::function OmitAfter; - // callback when interesting IR has changed - std::function - HandleAfter; - // callback when an interesting pass is invalidated - std::function HandleInvalidated; - // callback when the IR or pass is not interesting - std::function HandleFiltered; - // callback when an ignored pass is encountered - std::function HandleIgnored; - // callback to compare the before and after representations of the IR - std::function Same; - - // stack of IRs before passes - std::vector BeforeStack; - // Is this the first IR seen? - bool InitialIR; -}; - // A change printer based on the string representation of the IR as created // by unwrapAndPrint. The string representation is stored in a std::string // to preserve it as the IR changes in each pass. Note that the banner is // included in this representation but it is massaged before reporting. -class IRChangePrinter : public ChangePrinter { +class IRChangedPrinter : public cp::IRChangePrinter { public: - IRChangePrinter(); + IRChangedPrinter(); void registerCallbacks(PassInstrumentationCallbacks &PIC); +}; -protected: - raw_ostream &Out; +// A change printer that prints out in-line differences in the basic +// blocks of functions. When a pass changes the module, the changes to +// all basic blocks for each function in the module are reported. +// Changes to the IR that do not affect basic blocks are not reported as +// having changed the IR. The option -print-module-scope does not affect +// this change reporter. +class InLineChangePrinter : public cp::IRChangePrinter { +public: + InLineChangePrinter(); + void registerCallbacks(PassInstrumentationCallbacks &PIC); }; /// This class provides an interface to register all the standard pass @@ -166,7 +116,8 @@ PrintPassInstrumentation PrintPass; TimePassesHandler TimePasses; OptNoneInstrumentation OptNone; - IRChangePrinter PrintChangedIR; + IRChangedPrinter PrintChangedIR; + InLineChangePrinter PrintChanges; public: StandardInstrumentations(bool DebugLogging) : PrintPass(DebugLogging) {} Index: llvm/lib/IR/CMakeLists.txt =================================================================== --- llvm/lib/IR/CMakeLists.txt +++ llvm/lib/IR/CMakeLists.txt @@ -4,6 +4,7 @@ Attributes.cpp AutoUpgrade.cpp BasicBlock.cpp + ChangePrinters.cpp Comdat.cpp ConstantFold.cpp ConstantRange.cpp Index: llvm/lib/IR/ChangePrinters.cpp =================================================================== --- /dev/null +++ llvm/lib/IR/ChangePrinters.cpp @@ -0,0 +1,560 @@ +//===- Standard pass instrumentations handling ----------------*- 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 +// +//===----------------------------------------------------------------------===// +/// \file +/// +/// This file defines IR-printing pass instrumentation callbacks as well as +/// StandardInstrumentations class that manages standard pass instrumentations. +/// +//===----------------------------------------------------------------------===// + +#include "llvm/IR/ChangePrinters.h" +#include "llvm/ADT/Any.h" +#include "llvm/ADT/Optional.h" +#include "llvm/Analysis/LazyCallGraph.h" +#include "llvm/Analysis/LoopInfo.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/IRPrintingPasses.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/PassInstrumentation.h" +#include "llvm/Passes/StandardInstrumentations.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/raw_ostream.h" +#include +#include +#include + +using namespace llvm; + +// A hidden option that supports the -print-changed option. See +// the description for -print-changed for an explanation of the use +// of this option. Note that this option has no effect without -print-changed. +static cl::list + PrintPassesList("filter-passes", cl::value_desc("pass names"), + cl::desc("Only consider IR changes for passes whose names " + "match for the print-changed option"), + cl::CommaSeparated, cl::Hidden); + +// A hidden option that prints out the IR after passes, similar to +// -print-after-all except that it only prints the IR after passes that +// change the IR. Those passes that do not make changes to the IR are +// reported as not making any changes. In addition, the initial IR is +// also reported. Other hidden options affect the output from this +// option. -filter-passes will limit the output to the named passes +// that actually change the IR and other passes are reported as filtered out. +// The specified passes will either be reported as making no changes (with +// no IR reported) or the changed IR will be reported. Also, the +// -filter-print-funcs and -print-module-scope options will do similar +// filtering based on function name, reporting changed IRs as functions(or +// modules if -print-module-scope is specified) for a particular function +// or indicating that the IR has been filtered out. The exta options +// can be combined, allowing only changed IRs for certain passes on certain +// functions to be reported in different formats, with the rest being +// reported as filtered out. +static cl::opt PrintChanged("print-changed", + cl::desc("Print changed IRs"), + cl::init(false), cl::Hidden); + +// A hidden option -print-changes which prints in line differences of +// changed IR as they progress through the pipeline. +static cl::opt PrintChanges( + "print-changes", cl::Hidden, cl::init(false), + cl::desc("Print in-line differences of changes made by a pass")); + +namespace { + +// Return true when this is a pass for which printing of changes is desired. +bool isIgnored(StringRef PassID) { + return PassID.contains("PassManager<") || PassID.contains("PassAdaptor<"); +} + +// Return true when this is a defined function for which printing +// of changes is desired. +bool isInterestingFunction(const Function &F) { + return !F.isDeclaration() && llvm::isFunctionInPrintList(F.getName()); +} + +// Return true when this is a pass for which printing of changes is desired. +bool isInterestingPass(StringRef PassID) { + if (isIgnored(PassID)) + return false; + + static std::unordered_set PrintPassNames(PrintPassesList.begin(), + PrintPassesList.end()); + return PrintPassNames.empty() || PrintPassNames.count(PassID.str()); +} + +// Return true when this is a pass on IR for which printing +// of changes is desired. +bool isInteresting(Any IR, StringRef PassID) { + if (!isInterestingPass(PassID)) + return false; + if (any_isa(IR)) + return isInterestingFunction(*any_cast(IR)); + return true; +} + +// Copied/Modified from FuzzerUtilPosix.cpp +// Return true on success, false otherwise. +bool ExecuteCommand(const StringRef Cmd, std::string *CmdOutput) { + FILE *Pipe = popen(Cmd.str().c_str(), "r"); + if (!Pipe) + return false; + + if (CmdOutput) { + char TmpBuffer[128]; + while (fgets(TmpBuffer, sizeof(TmpBuffer), Pipe)) + CmdOutput->append(TmpBuffer); + } + + return pclose(Pipe) == 0; +} + +// Perform a linux based diff between \p Before and \p After, using +// \p OldLineFormat, \p NewLineFormat, and \p UnchangedLineFormat +// to control the formatting of the output. +std::string doLinuxDiff(StringRef Before, StringRef After, + StringRef OldLineFormat, StringRef NewLineFormat, + StringRef UnchangedLineFormat) { + StringRef SR[2]{Before, After}; + // Store the 2 bodies into temporary files and call diff on them + // to get the body of the node. + static std::string FileName[2]; + static int FD[2]{-1, -1}; + for (int I = 0; I < 2; ++I) { + if (FD[I] == -1) { + SmallVector SV; + std::error_code EC = + sys::fs::createTemporaryFile("tmpdiff", "txt", FD[I], SV); + if (EC) { + std::string errMsg = "Error: " + EC.message() + "\n"; + errs() << errMsg; + return errMsg; + } + FileName[I] = Twine(SV).str(); + } + + std::error_code EC = sys::fs::openFileForWrite(FileName[I], FD[I]); + if (EC) { + std::string errMsg = "Error: " + EC.message() + "\n"; + errs() << errMsg; + return errMsg; + } + + llvm::raw_fd_ostream OutStream(FD[I], /*shouldClose=*/true); + if (FD[I] == -1) { + std::string errMsg = + "Error opening file '" + FileName[I] + "' for writing.\n"; + errs() << errMsg; + return errMsg; + } + OutStream << SR[I]; + } + + SmallString<512> DiffCall = + formatv("diff -w -d --old-line-format='{2}' --new-line-format='{3}' " + "--unchanged-line-format='{4}' {0} {1}", + FileName[0], FileName[1], OldLineFormat, NewLineFormat, + UnchangedLineFormat); + std::string Diff; + ExecuteCommand(DiffCall, &Diff); + // Clean up. + for (unsigned I = 0; I < 2; ++I) { + std::error_code EC = sys::fs::remove(FileName[I]); + if (EC) { + std::string errMsg = "Error: " + EC.message() + "\n"; + errs() << errMsg; + return errMsg; + } + } + return Diff; +} + +} // namespace + +template +void cp::ChangePrinter::saveIRBeforePass(Any IR, StringRef PassID) { + // Always need to place something on the stack because invalidated passes + // are not given the IR so it cannot be determined whether the pass was for + // something that was filtered out. + BeforeStack.emplace_back(); + + if (!isInteresting(IR, PassID)) + return; + // Is this the initial IR? + if (InitialIR) { + InitialIR = false; + HandleInitialIR(IR); + } + + // Save the IR representation on the stack. + auto &Data = BeforeStack.back(); + GenerateIRRepresentation(IR, PassID, Data); +} + +template +void cp::ChangePrinter::handleIRAfterPass(Any IR, StringRef PassID) { + assert(!BeforeStack.empty() && "Unexpected empty stack encountered."); + std::string Name; + + // unwrapModule has inconsistent handling of names for function IRs. + if (any_isa(IR)) { + const Function *F = any_cast(IR); + Name = formatv(" (function: {0})", F->getName()).str(); + } else { + if (auto UM = unwrapModule(IR)) + Name = UM->second; + } + if (Name == "") + Name = " (module)"; + + if (isIgnored(PassID)) + HandleIgnored(PassID, Name); + else if (!isInteresting(IR, PassID)) + HandleFiltered(PassID, Name); + else { + // Get the before rep from the stack + T &Before = BeforeStack.back(); + // Create the after rep + T After; + GenerateIRRepresentation(IR, PassID, After); + + // was there a change in IR? + if (Same(Before, After)) + OmitAfter(PassID, Name); + else + HandleAfter(Name, PassID, Before, After, IR); + } + BeforeStack.pop_back(); +} + +template +void cp::ChangePrinter::handleInvalidatedPass(StringRef PassID) { + assert(!BeforeStack.empty() && "Unexpected empty stack encountered."); + + // Always flag it as invalidated as we cannot determine when + // a pass for a filtered function is invalidated since we do not + // get the IR in the callback. Also, the output is just alternate + // forms of the banner anyway. + HandleInvalidated(PassID); + BeforeStack.pop_back(); +} + +template +cp::BlockDataTemplate::BlockDataTemplate( + const llvm::BasicBlock &B) + : Label(B.getName().str()), Data() { + std::string Raw; + std::string Raw1; + llvm::raw_string_ostream SS(Body); + B.Value::print(SS, true); + BlockDataType::initialize(Data, B); +} + +template +cp::FuncDataTemplate::FuncDataTemplate(const llvm::Function &F) { + EntryBlockName = F.getEntryBlock().getName().str(); + for (auto &B : F) + Blocks.insert({B.getName(), B}); +} + +template +bool cp::FuncDataTemplate::operator==(const FuncDataTemplate &that) const { + for (auto &B : that) { + llvm::StringRef Label = B.getKey(); + if (Blocks.count(Label) == 0) + return false; + const BlockDataTemplate &ThisData = Blocks.find(Label)->getValue(); + const BlockDataTemplate &ThatData = B.getValue(); + if (ThisData != ThatData) + return false; + } + return true; +} + +template +bool cp::IRDataTemplate::operator==(const IRDataTemplate &that) { + if (llvm::StringMap::size() != that.size()) + return false; + for (auto &F : *this) { + llvm::StringRef Key = F.getKey(); + if (that.count(Key) == 0) + return false; + if (F.getValue() != that.find(Key)->getValue()) + return false; + } + return true; +} + +// A class that compares two IRs of type cp::EmptyDataIRData and does a +// linux diff between them. The added lines are prefixed with a '+', +// the removed lines are prefixed with a '-' and unchanged lines are +// prefixed with a space (to have things line up). The functions +// handleCompare and handleFunctionCompare are called by the base +// class with the appropriate parameters. +class InLineComparer + : public cp::IRComparer { +public: + InLineComparer(llvm::raw_ostream &OS, cp::EmptyDataIRData &Before, + cp::EmptyDataIRData &After) + : IRComparer(Before, After, 0), Out(OS) {} + + // Called by base class + void handleCompare(llvm::StringRef Name, llvm::StringRef Prefix, + llvm::StringRef PassID, unsigned, unsigned *, + const cp::EmptyDataFuncData &Before, + const cp::EmptyDataFuncData &After); + // Called by base class + void handleFunctionCompare(llvm::StringRef Name, llvm::StringRef Prefix, + llvm::StringRef PassID, unsigned, unsigned *, + const cp::EmptyDataFuncData &Before, + const cp::EmptyDataFuncData &After) { + Out << "\n*** IR for function " << Name << " ***\n"; + handleCompare(Name, Prefix, PassID, 0, nullptr, Before, After); + } + +protected: + llvm::raw_ostream &Out; +}; + +void InLineComparer::handleCompare(llvm::StringRef, llvm::StringRef Prefix, + llvm::StringRef PassID, unsigned, unsigned *, + const cp::EmptyDataFuncData &Before, + const cp::EmptyDataFuncData &After) { + for (auto &B : Before) { + const cp::EmptyDataBlockData &BeforeBD = B.getValue(); + const cp::EmptyDataBlockData *BD = After.getBlockData(BeforeBD.getLabel()); + if (BD) + // This block is in both + Out << doLinuxDiff(BeforeBD.getBody(), BD->getBody(), "-%l\n", "+%l\n", + " %l\n"); + else + // This has been removed + Out << doLinuxDiff(BeforeBD.getBody(), "\n", "-%l\n", "+%l\n", " %l\n"); + } + for (auto &B : After) { + const cp::EmptyDataBlockData &AfterBD = B.getValue(); + const cp::EmptyDataBlockData *BD = Before.getBlockData(AfterBD.getLabel()); + if (!BD) + // This block is new + Out << doLinuxDiff("\n", AfterBD.getBody(), "-%l\n", "+%l\n", " %l\n"); + } +} + +template +void cp::IRComparer::compare( + llvm::Any IR, llvm::StringRef Prefix, llvm::StringRef PassID, + llvm::StringRef Name) { + if (!getModule(IR)) { + // Not a module so just handle the single function. + assert(Before.size() == 1 && "Expected only one function."); + assert(After.size() == 1 && "Expected only one function."); + ((T *)this)->handleCompare(Name, Prefix, PassID, N, nullptr, + Before.begin()->getValue(), + After.begin()->getValue()); + return; + } + // Determine whether functions are common or not. + llvm::StringMap Funcs; + for (auto &B : Before) + Funcs.insert({B.getKey(), InBefore}); + for (auto &A : After) + if (Funcs.count(A.getKey()) == 1) + Funcs[A.getKey()] = IsCommon; + else + Funcs.insert({A.getKey(), InAfter}); + + // Set up an appropriate numbering system. + unsigned Minor = 0; + + for (auto &F : Funcs) { + Name = F.getKey(); + IRChangeDiffType CDDT = F.getValue(); + // If the function isn't common, create an empty version for the compare. + if (CDDT == InBefore) { + FuncDataType MissingFunc( + Before.find(Name)->getValue().getEntryBlockName()); + ((T *)this)->handleFunctionCompare(Name, Prefix, PassID, N, &Minor, + Before.find(Name)->getValue(), + MissingFunc); + } else if (CDDT == InAfter) { + FuncDataType MissingFunc( + After.find(Name)->getValue().getEntryBlockName()); + ((T *)this)->handleFunctionCompare(Name, Prefix, PassID, N, &Minor, + MissingFunc, + After.find(Name)->getValue()); + } else { + assert(CDDT == IsCommon && "Unexpected Diff type."); + ((T *)this)->handleFunctionCompare(Name, Prefix, PassID, N, &Minor, + Before.find(Name)->getValue(), + After.find(Name)->getValue()); + } + ++Minor; + } +} + +template +bool cp::IRComparer::analyzeIR(llvm::Any IR, + IRDataType &Data) { + if (const llvm::Module *M = getModule(IR)) { + // Create data for each existing/interesting function in the module. + bool OutputExists = false; + for (const llvm::Function &F : *M) + OutputExists = generateFunctionData(Data, F) | OutputExists; + return OutputExists; + } + + const llvm::Function *F = nullptr; + if (llvm::any_isa(IR)) + F = llvm::any_cast(IR); + else { + assert(llvm::any_isa(IR) && "Unknown IR unit."); + const llvm::Loop *L = llvm::any_cast(IR); + F = L->getHeader()->getParent(); + } + assert(F && "Unknown IR unit."); + return generateFunctionData(Data, *F); +} + +template +const llvm::Module * +cp::IRComparer::getModule(llvm::Any IR) { + if (llvm::any_isa(IR)) + return llvm::any_cast(IR); + if (llvm::any_isa(IR)) { + const llvm::LazyCallGraph::SCC *C = + llvm::any_cast(IR); + for (const llvm::LazyCallGraph::Node &N : *C) + return N.getFunction().getParent(); + } + return nullptr; +} + +template +bool cp::IRComparer::generateFunctionData( + IRDataType &Data, const llvm::Function &F) { + if (!F.isDeclaration() && llvm::isFunctionInPrintList(F.getName())) { + Data.insert({F.getName(), F}); + return true; + } + return false; +} + +template +cp::IRChangePrinter::IRChangePrinter( + std::function F2, + std::function + F4, + std::function F8) + : ChangePrinter( + [this](Any IR) -> void { handleInitialIR(IR); }, F2, + [this](StringRef PassID, std::string &Name) -> void { + omitAfter(PassID, Name); + }, + F4, [this](StringRef PassID) -> void { handleInvalidated(PassID); }, + [this](StringRef PassID, std::string &Name) -> void { + handleFiltered(PassID, Name); + }, + [this](StringRef PassID, std::string &Name) -> void { + handleIgnored(PassID, Name); + }, + F8), + Out(dbgs()) {} + +template void cp::IRChangePrinter::handleInitialIR(Any IR) { + StringRef Banner("*** IR Dump At Start: ***"); + unwrapAndPrint(Out, IR, Banner, true); +} + +template +void cp::IRChangePrinter::omitAfter(StringRef PassID, std::string &Name) { + Out << formatv("*** IR Dump After {0}{1} omitted because no change ***\n", + PassID, Name); +} + +template +void cp::IRChangePrinter::handleInvalidated(StringRef PassID) { + Out << formatv("*** IR Pass {0} invalidated ***\n", PassID); +} + +template +void cp::IRChangePrinter::handleFiltered(StringRef PassID, + std::string &Name) { + SmallString<20> Banner = + formatv("*** IR Dump After {0}{1} filtered out ***\n", PassID, Name); + Out << Banner; +} + +template +void cp::IRChangePrinter::handleIgnored(StringRef PassID, + std::string &Name) { + Out << formatv("*** IR Pass {0}{1} ignored ***\n", PassID, Name); +} + +IRChangedPrinter::IRChangedPrinter() + : cp::IRChangePrinter( + [](Any IR, StringRef PassID, std::string &Output) -> void { + raw_string_ostream OS(Output); + // use the after banner for all cases so it will match + SmallString<20> Banner = + formatv("*** IR Dump After {0} ***", PassID); + unwrapAndPrint(OS, IR, Banner, llvm::forcePrintModuleIR()); + OS.str(); + }, + [this](std::string &Name, StringRef PassID, std::string &Before, + std::string &After, Any IR) -> void { + assert(After.find("*** IR Dump") == 0 && + "Unexpected banner format."); + StringRef S = After; + StringRef Banner = + S.take_until([](char C) -> bool { return C == '\n'; }); + Out << Banner; + + // LazyCallGraph::SCC already has "(scc:..." in banner so only + // add in the name if it isn't already there. + if (Name.substr(0, 6).compare(" (scc:") != 0 && + !llvm::forcePrintModuleIR()) + Out << Name; + Out << S.substr(Banner.size()); + }, + [](std::string &S1, std::string &S2) -> bool { + return S1.compare(S2) == 0; + }) {} + +void IRChangedPrinter::registerCallbacks(PassInstrumentationCallbacks &PIC) { + if (PrintChanged) + ChangePrinter::registerCallbacks(PIC); +} + +InLineChangePrinter::InLineChangePrinter() + : cp::IRChangePrinter( + [](Any IR, StringRef PassID, cp::EmptyDataIRData &D) -> void { + InLineComparer::analyzeIR(IR, D); + }, + [this](std::string &Name, StringRef PassID, + cp::EmptyDataIRData &Before, cp::EmptyDataIRData &After, + Any IR) -> void { + if (Name == "") + Name = " (module)"; + SmallString<20> Banner = + formatv("*** IR Dump After {0} ***{1}\n", PassID, Name); + Out << Banner; + InLineComparer(Out, Before, After).compare(IR, "", PassID, Name); + Out << "\n"; + }, + [](cp::EmptyDataIRData &D1, cp::EmptyDataIRData &D2) -> bool { + return D1 == D2; + }) {} + +void InLineChangePrinter::registerCallbacks(PassInstrumentationCallbacks &PIC) { + if (PrintChanges) + ChangePrinter::registerCallbacks(PIC); +} Index: llvm/lib/Passes/StandardInstrumentations.cpp =================================================================== --- llvm/lib/Passes/StandardInstrumentations.cpp +++ llvm/lib/Passes/StandardInstrumentations.cpp @@ -27,6 +27,7 @@ #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/raw_ostream.h" #include +#include #include using namespace llvm; @@ -44,77 +45,8 @@ cl::desc("Print all pass management debugging information. " "`-debug-pass-manager` must also be specified")); -// A hidden option that prints out the IR after passes, similar to -// -print-after-all except that it only prints the IR after passes that -// change the IR. Those passes that do not make changes to the IR are -// reported as not making any changes. In addition, the initial IR is -// also reported. Other hidden options affect the output from this -// option. -filter-passes will limit the output to the named passes -// that actually change the IR and other passes are reported as filtered out. -// The specified passes will either be reported as making no changes (with -// no IR reported) or the changed IR will be reported. Also, the -// -filter-print-funcs and -print-module-scope options will do similar -// filtering based on function name, reporting changed IRs as functions(or -// modules if -print-module-scope is specified) for a particular function -// or indicating that the IR has been filtered out. The exta options -// can be combined, allowing only changed IRs for certain passes on certain -// functions to be reported in different formats, with the rest being -// reported as filtered out. -static cl::opt PrintChanged("print-changed", - cl::desc("Print changed IRs"), - cl::init(false), cl::Hidden); -// A hidden option that supports the -print-changed option. See -// the description for -print-changed for an explanation of the use -// of this option. Note that this option has no effect without -print-changed. -static cl::list - PrintPassesList("filter-passes", cl::value_desc("pass names"), - cl::desc("Only consider IR changes for passes whose names " - "match for the print-changed option"), - cl::CommaSeparated, cl::Hidden); - namespace { -/// Extracting Module out of \p IR unit. Also fills a textual description -/// of \p IR for use in header when printing. -Optional> unwrapModule(Any IR) { - if (any_isa(IR)) - return std::make_pair(any_cast(IR), std::string()); - - if (any_isa(IR)) { - const Function *F = any_cast(IR); - if (!llvm::isFunctionInPrintList(F->getName())) - return None; - const Module *M = F->getParent(); - return std::make_pair(M, formatv(" (function: {0})", F->getName()).str()); - } - - if (any_isa(IR)) { - const LazyCallGraph::SCC *C = any_cast(IR); - for (const LazyCallGraph::Node &N : *C) { - const Function &F = N.getFunction(); - if (!F.isDeclaration() && isFunctionInPrintList(F.getName())) { - const Module *M = F.getParent(); - return std::make_pair(M, formatv(" (scc: {0})", C->getName()).str()); - } - } - return None; - } - - if (any_isa(IR)) { - const Loop *L = any_cast(IR); - const Function *F = L->getHeader()->getParent(); - if (!isFunctionInPrintList(F->getName())) - return None; - const Module *M = F->getParent(); - std::string LoopName; - raw_string_ostream ss(LoopName); - L->getHeader()->printAsOperand(ss, false); - return std::make_pair(M, formatv(" (loop: {0})", ss.str()).str()); - } - - llvm_unreachable("Unknown IR unit"); -} - void printIR(raw_ostream &OS, const Function *F, StringRef Banner, StringRef Extra = StringRef(), bool Brief = false) { if (Brief) { @@ -177,10 +109,49 @@ llvm::printLoop(const_cast(*L), OS, std::string(Banner)); } -/// Generic IR-printing helper that unpacks a pointer to IRUnit wrapped into -/// llvm::Any and does actual print job. -void unwrapAndPrint(raw_ostream &OS, Any IR, StringRef Banner, - bool ForceModule = false, bool Brief = false) { +} // namespace + +Optional> llvm::unwrapModule(Any IR) { + if (any_isa(IR)) + return std::make_pair(any_cast(IR), std::string()); + + if (any_isa(IR)) { + const Function *F = any_cast(IR); + if (!llvm::isFunctionInPrintList(F->getName())) + return None; + const Module *M = F->getParent(); + return std::make_pair(M, formatv(" (function: {0})", F->getName()).str()); + } + + if (any_isa(IR)) { + const LazyCallGraph::SCC *C = any_cast(IR); + for (const LazyCallGraph::Node &N : *C) { + const Function &F = N.getFunction(); + if (!F.isDeclaration() && isFunctionInPrintList(F.getName())) { + const Module *M = F.getParent(); + return std::make_pair(M, formatv(" (scc: {0})", C->getName()).str()); + } + } + return None; + } + + if (any_isa(IR)) { + const Loop *L = any_cast(IR); + const Function *F = L->getHeader()->getParent(); + if (!isFunctionInPrintList(F->getName())) + return None; + const Module *M = F->getParent(); + std::string LoopName; + raw_string_ostream ss(LoopName); + L->getHeader()->printAsOperand(ss, false); + return std::make_pair(M, formatv(" (loop: {0})", ss.str()).str()); + } + + llvm_unreachable("Unknown IR unit"); +} + +void llvm::unwrapAndPrint(raw_ostream &OS, Any IR, StringRef Banner, + bool ForceModule, bool Brief) { if (ForceModule) { if (auto UnwrappedModule = unwrapModule(IR)) printIR(OS, UnwrappedModule->first, Banner, UnwrappedModule->second); @@ -218,195 +189,6 @@ llvm_unreachable("Unknown wrapped IR type"); } -// Return true when this is a pass for which printing of changes is desired. -bool isIgnored(StringRef PassID) { - return PassID.startswith("PassManager<") || PassID.contains("PassAdaptor<"); -} - -// Return true when this is a defined function for which printing -// of changes is desired. -bool isInterestingFunction(const Function &F) { - return !F.isDeclaration() && llvm::isFunctionInPrintList(F.getName()); -} - -// Return true when this is a pass for which printing of changes is desired. -bool isInterestingPass(StringRef PassID) { - if (isIgnored(PassID)) - return false; - - static std::unordered_set PrintPassNames(PrintPassesList.begin(), - PrintPassesList.end()); - return PrintPassNames.empty() || PrintPassNames.count(PassID.str()); -} - -// Return true when this is a pass on IR for which printing -// of changes is desired. -bool isInteresting(Any IR, StringRef PassID) { - if (!isInterestingPass(PassID)) - return false; - if (any_isa(IR)) - return isInterestingFunction(*any_cast(IR)); - return true; -} - -} // namespace - -template -void ChangePrinter::saveIRBeforePass(Any IR, StringRef PassID) { - // Always need to place something on the stack because invalidated passes - // are not given the IR so it cannot be determined whether the pass was for - // something that was filtered out. - BeforeStack.emplace_back(); - - if (!isInteresting(IR, PassID)) - return; - // Is this the initial IR? - if (InitialIR) { - InitialIR = false; - HandleInitialIR(IR); - } - - // Save the IR representation on the stack. - auto &Data = BeforeStack.back(); - GenerateIRRepresentation(IR, PassID, Data); -} - -template -void ChangePrinter::handleIRAfterPass(Any IR, StringRef PassID) { - assert(!BeforeStack.empty() && "Unexpected empty stack encountered."); - std::string Name; - - // unwrapModule has inconsistent handling of names for function IRs. - if (any_isa(IR)) { - const Function *F = any_cast(IR); - Name = formatv(" (function: {0})", F->getName()).str(); - } else { - if (auto UM = unwrapModule(IR)) - Name = UM->second; - } - if (Name == "") - Name = " (module)"; - - if (isIgnored(PassID)) - HandleIgnored(PassID, Name); - else if (!isInteresting(IR, PassID)) - HandleFiltered(PassID, Name); - else { - // Get the before rep from the stack - T &Before = BeforeStack.back(); - // Create the after rep - T After; - GenerateIRRepresentation(IR, PassID, After); - - // was there a change in IR? - if (Same(Before, After)) - OmitAfter(PassID, Name); - else - HandleAfter(Name, PassID, Before, After, IR); - } - BeforeStack.pop_back(); -} - -template -void ChangePrinter::handleInvalidatedPass(StringRef PassID) { - assert(!BeforeStack.empty() && "Unexpected empty stack encountered."); - - // Always flag it as invalidated as we cannot determine when - // a pass for a filtered function is invalidated since we do not - // get the IR in the callback. Also, the output is just alternate - // forms of the banner anyway. - HandleInvalidated(PassID); - BeforeStack.pop_back(); -} - -void handleInitialIR(Any IR, raw_ostream &Out) { - StringRef Banner("*** IR Dump At Start: ***"); - unwrapAndPrint(Out, IR, Banner, true); -} - -void generateOutput(Any IR, StringRef PassID, std::string &Output) { - raw_string_ostream OS(Output); - // use the after banner for all cases so it will match - SmallString<20> Banner = formatv("*** IR Dump After {0} ***", PassID); - unwrapAndPrint(OS, IR, Banner, llvm::forcePrintModuleIR()); - OS.str(); -} - -void omitAfter(StringRef PassID, std::string &Name, raw_ostream &Out) { - Out << formatv("*** IR Dump After {0}{1} omitted because no change ***\n", - PassID, Name); -} - -void handleAfter(std::string &Name, StringRef After, raw_ostream &Out) { - assert(After.find("*** IR Dump") == 0 && "Unexpected banner format."); - StringRef Banner = After.take_until([](char C) -> bool { return C == '\n'; }); - Out << Banner; - - // LazyCallGraph::SCC already has "(scc:..." in banner so only add - // in the name if it isn't already there. - if (Name.substr(0, 6).compare(" (scc:") != 0 && !llvm::forcePrintModuleIR()) - Out << Name; - - Out << After.substr(Banner.size()); -} - -void handleInvalidated(StringRef PassID, raw_ostream &Out) { - Out << formatv("*** IR Pass {0} invalidated ***\n", PassID); -} - -void handleFiltered(StringRef PassID, std::string &Name, raw_ostream &Out) { - SmallString<20> Banner = - formatv("*** IR Dump After {0}{1} filtered out ***\n", PassID, Name); - Out << Banner; -} - -void handleIgnored(StringRef PassID, std::string &Name, raw_ostream &Out) { - Out << formatv("*** IR Pass {0}{1} ignored ***\n", PassID, Name); -} - -bool sameIR(std::string &S1, std::string &S2) { return S1.compare(S2) == 0; } - -IRChangePrinter::IRChangePrinter() - : ChangePrinter( - [this](Any IR) -> void { ::handleInitialIR(IR, Out); }, - ::generateOutput, - [this](StringRef PassID, std::string &Name) -> void { - ::omitAfter(PassID, Name, Out); - }, - [this](std::string &Name, StringRef PassID, std::string &Before, - std::string &After, - Any IR) -> void { ::handleAfter(Name, After, Out); }, - [this](StringRef PassID) -> void { - ::handleInvalidated(PassID, Out); - }, - [this](StringRef PassID, std::string &Name) -> void { - ::handleFiltered(PassID, Name, Out); - }, - [this](StringRef PassID, std::string &Name) -> void { - ::handleIgnored(PassID, Name, Out); - }, - ::sameIR), - Out(dbgs()) {} - -void IRChangePrinter::registerCallbacks(PassInstrumentationCallbacks &PIC) { - if (!PrintChanged) - return; - - PIC.registerBeforePassCallback([this](StringRef P, Any IR) { - saveIRBeforePass(IR, P); - return true; - }); - - PIC.registerAfterPassCallback( - [this](StringRef P, Any IR, const PreservedAnalyses &) { - handleIRAfterPass(IR, P); - }); - PIC.registerAfterPassInvalidatedCallback( - [this](StringRef P, const PreservedAnalyses &) { - handleInvalidatedPass(P); - }); -} - PrintIRInstrumentation::~PrintIRInstrumentation() { assert(ModuleDescStack.empty() && "ModuleDescStack is not empty at exit"); } @@ -561,4 +343,5 @@ TimePasses.registerCallbacks(PIC); OptNone.registerCallbacks(PIC); PrintChangedIR.registerCallbacks(PIC); + PrintChanges.registerCallbacks(PIC); } Index: llvm/test/Other/change-printer.ll =================================================================== --- llvm/test/Other/change-printer.ll +++ llvm/test/Other/change-printer.ll @@ -1,8 +1,11 @@ ; Simple checks of -print-changed functionality ; +; -print-changed tests +; ; Note that (mostly) only the banners are checked. ; ; Simple functionality check. + ; RUN: opt -S -print-changed -passes=instsimplify 2>&1 -o /dev/null < %s | FileCheck %s --check-prefix=CHECK0 ; ; Check that only the passes that change the IR are printed and that the @@ -34,6 +37,36 @@ ; others (including g) are filtered out. Note that the second time ; instsimplify is run on f, it does not change the IR ; RUN: opt -S -print-changed -passes="instsimplify,instsimplify" -filter-print-funcs=f 2>&1 -o /dev/null < %s | FileCheck %s --check-prefix=CHECK9 +; +; print-changes tests +; +; Simple functionality check. +; RUN: opt -S -print-changes -passes=instsimplify 2>&1 -o /dev/null < %s | FileCheck %s --check-prefix=CHECK10 +; +; Check that only the passes that change the IR are printed and that the +; others (including g) are filtered out. +; RUN: opt -S -print-changes -passes=instsimplify -filter-print-funcs=f 2>&1 -o /dev/null < %s | FileCheck %s --check-prefix=CHECK11 +; +; Check that the reporting of IRs respects is not affected by +; -print-module-scope +; RUN: opt -S -print-changes -passes=instsimplify -print-module-scope 2>&1 -o /dev/null < %s | FileCheck %s --check-prefix=CHECK12 +; +; Check that reporting of multiple functions happens +; RUN: opt -S -print-changes -passes=instsimplify -filter-print-funcs="f,g" 2>&1 -o /dev/null < %s | FileCheck %s --check-prefix=CHECK13 +; +; Check that the reporting of IRs respects -filter-passes +; RUN: opt -S -print-changes -passes="instsimplify,no-op-function" -filter-passes="NoOpFunctionPass" 2>&1 -o /dev/null < %s | FileCheck %s --check-prefix=CHECK14 +; +; Check that the reporting of IRs respects -filter-passes with multiple passes +; RUN: opt -S -print-changes -passes="instsimplify,no-op-function" -filter-passes="NoOpFunctionPass,InstSimplifyPass" 2>&1 -o /dev/null < %s | FileCheck %s --check-prefix=CHECK15 +; +; Check that the reporting of IRs respects both -filter-passes and -filter-print-funcs +; RUN: opt -S -print-changes -passes="instsimplify,no-op-function" -filter-passes="NoOpFunctionPass,InstSimplifyPass" -filter-print-funcs=f 2>&1 -o /dev/null < %s | FileCheck %s --check-prefix=CHECK16 +; +; Check that repeated passes that change the IR are printed and that the +; others (including g) are filtered out. Note that only the first time +; instsimplify is run on f will result in changes +; RUN: opt -S -print-changes -passes="instsimplify,instsimplify" -filter-print-funcs=f 2>&1 -o /dev/null < %s | FileCheck %s --check-prefix=CHECK17 define i32 @g() { entry: @@ -107,3 +140,53 @@ ; CHECK9: *** IR Dump After InstSimplifyPass (function: g) filtered out *** ; CHECK9: *** IR Dump After InstSimplifyPass *** (function: f) ; CHECK9: *** IR Dump After InstSimplifyPass (function: f) omitted because no change *** + +; CHECK10: *** IR Dump At Start: *** +; CHECK10: ModuleID = '' +; CHECK10: *** IR Dump After VerifierPass (module) omitted because no change *** +; CHECK10: *** IR Dump After InstSimplifyPass *** (function: g) +; CHECK10: *** IR Pass PassManager (function: g) ignored *** +; CHECK10: *** IR Dump After InstSimplifyPass *** (function: f) +; CHECK10: *** IR Pass PassManager (function: f) ignored *** +; CHECK10: *** IR Pass ModuleToFunctionPassAdaptor > (module) ignored *** +; CHECK10: *** IR Dump After VerifierPass (module) omitted because no change *** +; CHECK10: *** IR Dump After PrintModulePass (module) omitted because no change *** + +; CHECK11: *** IR Dump At Start: *** +; CHECK11: *** IR Dump After InstSimplifyPass (function: g) filtered out *** +; CHECK11: *** IR Dump After InstSimplifyPass *** (function: f) + +; CHECK12: *** IR Dump At Start: *** +; CHECK12: ModuleID = '' +; CHECK12: *** IR Dump After InstSimplifyPass *** (function: g) +; CHECK12-NOT: ModuleID = '' +; CHECK12: *** IR Dump After InstSimplifyPass *** (function: f) +; CHECK12-NOT: ModuleID = '' + +; CHECK13: *** IR Dump At Start: *** +; CHECK13: *** IR Dump After InstSimplifyPass *** (function: g) +; CHECK13: *** IR Dump After InstSimplifyPass *** (function: f) + +; CHECK14: *** IR Dump After InstSimplifyPass (function: g) filtered out *** +; CHECK14: *** IR Dump At Start: *** (function: g) +; CHECK14: *** IR Dump After NoOpFunctionPass (function: g) omitted because no change *** +; CHECK14: *** IR Dump After InstSimplifyPass (function: f) filtered out *** +; CHECK14: *** IR Dump After NoOpFunctionPass (function: f) omitted because no change *** + +; CHECK15: *** IR Dump At Start: *** (function: g) +; CHECK15: *** IR Dump After InstSimplifyPass *** (function: g) +; CHECK15: *** IR Dump After NoOpFunctionPass (function: g) omitted because no change *** +; CHECK15: *** IR Dump After InstSimplifyPass *** (function: f) +; CHECK15: *** IR Dump After NoOpFunctionPass (function: f) omitted because no change *** + +; CHECK16: *** IR Dump After InstSimplifyPass (function: g) filtered out *** +; CHECK16: *** IR Dump After NoOpFunctionPass (function: g) filtered out *** +; CHECK16: *** IR Dump At Start: *** (function: f) +; CHECK16: *** IR Dump After InstSimplifyPass *** (function: f) +; CHECK16: *** IR Dump After NoOpFunctionPass (function: f) omitted because no change *** + +; CHECK17: *** IR Dump At Start: *** +; CHECK17: *** IR Dump After InstSimplifyPass (function: g) filtered out *** +; CHECK17: *** IR Dump After InstSimplifyPass (function: g) filtered out *** +; CHECK17: *** IR Dump After InstSimplifyPass *** (function: f) +; CHECK17: *** IR Dump After InstSimplifyPass (function: f) omitted because no change ***