//===----------------------------------------------------------------------===//
#include "ReducerWorkItem.h"
+#include "TestRunner.h"
+#include "llvm/Analysis/ModuleSummaryAnalysis.h"
+#include "llvm/Analysis/ProfileSummaryInfo.h"
#include "llvm/Bitcode/BitcodeReader.h"
+#include "llvm/Bitcode/BitcodeWriter.h"
#include "llvm/CodeGen/CommandFlags.h"
#include "llvm/CodeGen/MIRParser/MIRParser.h"
#include "llvm/CodeGen/MIRPrinter.h"
#include "llvm/IR/Verifier.h"
#include "llvm/IRReader/IRReader.h"
#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Passes/PassBuilder.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/MemoryBufferRef.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/ToolOutputFile.h"
#include "llvm/Support/WithColor.h"
#include "llvm/Target/TargetMachine.h"
+#include "llvm/Transforms/IPO/ThinLTOBitcodeWriter.h"
#include "llvm/Transforms/Utils/Cloning.h"
#include <optional>
cl::desc("Set the target triple"),
cl::cat(LLVMReduceOptions));
-void readBitcode(ReducerWorkItem &M, MemoryBufferRef Data, LLVMContext &Ctx,
- StringRef ToolName);
+static cl::opt<bool> TmpFilesAsBitcode(
+ "write-tmp-files-as-bitcode",
+ cl::desc("Always write temporary files as bitcode instead of textual IR"),
+ cl::init(false), cl::cat(LLVMReduceOptions));
static void cloneFrameInfo(
MachineFrameInfo &DstMFI, const MachineFrameInfo &SrcMFI,
InitializeAllAsmParsers();
}
-std::pair<std::unique_ptr<ReducerWorkItem>, bool>
-parseReducerWorkItem(StringRef ToolName, StringRef Filename, LLVMContext &Ctxt,
- std::unique_ptr<TargetMachine> &TM, bool IsMIR) {
- bool IsBitcode = false;
- Triple TheTriple;
+void ReducerWorkItem::print(raw_ostream &ROS, void *p) const {
+ if (MMI) {
+ printMIR(ROS, *M);
+ for (Function &F : *M) {
+ if (auto *MF = MMI->getMachineFunction(F))
+ printMIR(ROS, *MF);
+ }
+ } else {
+ M->print(ROS, /*AssemblyAnnotationWriter=*/nullptr,
+ /*ShouldPreserveUseListOrder=*/true);
+ }
+}
- auto MMM = std::make_unique<ReducerWorkItem>();
+bool ReducerWorkItem::verify(raw_fd_ostream *OS) const {
+ if (verifyModule(*M, OS))
+ return true;
- if (IsMIR) {
- initializeTargetInfo();
+ if (!MMI)
+ return false;
- auto FileOrErr = MemoryBuffer::getFileOrSTDIN(Filename, /*IsText=*/true);
- if (std::error_code EC = FileOrErr.getError()) {
- WithColor::error(errs(), ToolName) << EC.message() << '\n';
- return {nullptr, false};
+ for (const Function &F : getModule()) {
+ if (const MachineFunction *MF = MMI->getMachineFunction(F)) {
+ if (!MF->verify(nullptr, "", /*AbortOnError=*/false))
+ return true;
}
+ }
- std::unique_ptr<MIRParser> MParser =
- createMIRParser(std::move(FileOrErr.get()), Ctxt);
-
- auto SetDataLayout = [&](StringRef DataLayoutTargetTriple,
- StringRef OldDLStr) -> std::optional<std::string> {
- // If we are supposed to override the target triple, do so now.
- std::string IRTargetTriple = DataLayoutTargetTriple.str();
- if (!TargetTriple.empty())
- IRTargetTriple = Triple::normalize(TargetTriple);
- TheTriple = Triple(IRTargetTriple);
- if (TheTriple.getTriple().empty())
- TheTriple.setTriple(sys::getDefaultTargetTriple());
-
- std::string Error;
- const Target *TheTarget =
- TargetRegistry::lookupTarget(codegen::getMArch(), TheTriple, Error);
- if (!TheTarget) {
- WithColor::error(errs(), ToolName) << Error;
- exit(1);
- }
+ return false;
+}
- // Hopefully the MIR parsing doesn't depend on any options.
- TargetOptions Options;
- std::optional<Reloc::Model> RM = codegen::getExplicitRelocModel();
- std::string CPUStr = codegen::getCPUStr();
- std::string FeaturesStr = codegen::getFeaturesStr();
- TM = std::unique_ptr<TargetMachine>(TheTarget->createTargetMachine(
- TheTriple.getTriple(), CPUStr, FeaturesStr, Options, RM,
- codegen::getExplicitCodeModel(), CodeGenOpt::Default));
- assert(TM && "Could not allocate target machine!");
+bool ReducerWorkItem::isReduced(const TestRunner &Test) const {
+ const bool UseBitcode = Test.inputIsBitcode() || TmpFilesAsBitcode;
- return TM->createDataLayout().getStringRepresentation();
- };
+ SmallString<128> CurrentFilepath;
- std::unique_ptr<Module> M = MParser->parseIRModule(SetDataLayout);
- LLVMTargetMachine *LLVMTM = static_cast<LLVMTargetMachine *>(TM.get());
+ // Write ReducerWorkItem to tmp file
+ int FD;
+ std::error_code EC = sys::fs::createTemporaryFile(
+ "llvm-reduce", isMIR() ? "mir" : (UseBitcode ? "bc" : "ll"), FD,
+ CurrentFilepath,
+ UseBitcode && !isMIR() ? sys::fs::OF_None : sys::fs::OF_Text);
+ if (EC) {
+ errs() << "Error making unique filename: " << EC.message() << "!\n";
+ exit(1);
+ }
- MMM->MMI = std::make_unique<MachineModuleInfo>(LLVMTM);
- MParser->parseMachineFunctions(*M, *MMM->MMI);
- MMM->M = std::move(M);
- } else {
- SMDiagnostic Err;
- ErrorOr<std::unique_ptr<MemoryBuffer>> MB = MemoryBuffer::getFileOrSTDIN(Filename);
- if (std::error_code EC = MB.getError()) {
- WithColor::error(errs(), ToolName) << Filename << ": " << EC.message() << "\n";
- return {nullptr, false};
- }
+ ToolOutputFile Out(CurrentFilepath, FD);
- if (!isBitcode((const unsigned char *)(*MB)->getBufferStart(),
- (const unsigned char *)(*MB)->getBufferEnd())) {
- std::unique_ptr<Module> Result = parseIRFile(Filename, Err, Ctxt);
- if (!Result) {
- Err.print(ToolName.data(), errs());
- return {nullptr, false};
- }
- MMM->M = std::move(Result);
- } else {
- IsBitcode = true;
- readBitcode(*MMM, MemoryBufferRef(**MB), Ctxt, ToolName);
+ // FIXME: This should be UseBitcode
+ writeOutput(Out.os(), TmpFilesAsBitcode);
- if (MMM->LTOInfo->IsThinLTO && MMM->LTOInfo->EnableSplitLTOUnit)
- initializeTargetInfo();
- }
+ Out.os().close();
+ if (Out.os().has_error()) {
+ errs() << "Error emitting bitcode to file '" << CurrentFilepath
+ << "': " << Out.os().error().message();
+ exit(1);
}
- if (verifyReducerWorkItem(*MMM, &errs())) {
- WithColor::error(errs(), ToolName)
- << Filename << " - input module is broken!\n";
- return {nullptr, false};
- }
- return {std::move(MMM), IsBitcode};
+
+ // Current Chunks aren't interesting
+ return Test.run(CurrentFilepath);
}
std::unique_ptr<ReducerWorkItem>
-cloneReducerWorkItem(const ReducerWorkItem &MMM, const TargetMachine *TM) {
+ReducerWorkItem::clone(const TargetMachine *TM) const {
auto CloneMMM = std::make_unique<ReducerWorkItem>();
if (TM) {
// We're assuming the Module IR contents are always unchanged by MIR
// reductions, and can share it as a constant.
- CloneMMM->M = MMM.M;
+ CloneMMM->M = M;
// MachineModuleInfo contains a lot of other state used during codegen which
// we won't be using here, but we should be able to ignore it (although this
static_cast<const LLVMTargetMachine *>(TM);
CloneMMM->MMI = std::make_unique<MachineModuleInfo>(LLVMTM);
- for (const Function &F : MMM.getModule()) {
- if (auto *MF = MMM.MMI->getMachineFunction(F))
+ for (const Function &F : getModule()) {
+ if (auto *MF = MMI->getMachineFunction(F))
CloneMMM->MMI->insertFunction(F, cloneMF(MF, *CloneMMM->MMI));
}
} else {
- CloneMMM->M = CloneModule(*MMM.M);
+ CloneMMM->M = CloneModule(*M);
}
return CloneMMM;
}
-bool verifyReducerWorkItem(const ReducerWorkItem &MMM, raw_fd_ostream *OS) {
- if (verifyModule(*MMM.M, OS))
- return true;
-
- if (!MMM.MMI)
- return false;
-
- for (const Function &F : MMM.getModule()) {
- if (const MachineFunction *MF = MMM.MMI->getMachineFunction(F)) {
- if (!MF->verify(nullptr, "", /*AbortOnError=*/false))
- return true;
- }
- }
-
- return false;
-}
-
-void ReducerWorkItem::print(raw_ostream &ROS, void *p) const {
- if (MMI) {
- printMIR(ROS, *M);
- for (Function &F : *M) {
- if (auto *MF = MMI->getMachineFunction(F))
- printMIR(ROS, *MF);
- }
- } else {
- M->print(ROS, /*AssemblyAnnotationWriter=*/nullptr,
- /*ShouldPreserveUseListOrder=*/true);
- }
-}
-
/// Try to produce some number that indicates a function is getting smaller /
/// simpler.
static uint64_t computeMIRComplexityScoreImpl(const MachineFunction &MF) {
return Score;
}
+
+void ReducerWorkItem::writeOutput(raw_ostream &OS, bool EmitBitcode) const {
+ // Requesting bitcode emission with mir is nonsense, so just ignore it.
+ if (EmitBitcode && !isMIR())
+ writeBitcode(OS);
+ else
+ print(OS, /*AnnotationWriter=*/nullptr);
+}
+
+void ReducerWorkItem::readBitcode(MemoryBufferRef Data, LLVMContext &Ctx,
+ StringRef ToolName) {
+ Expected<BitcodeFileContents> IF = llvm::getBitcodeFileContents(Data);
+ if (!IF) {
+ WithColor::error(errs(), ToolName) << IF.takeError();
+ exit(1);
+ }
+ BitcodeModule BM = IF->Mods[0];
+ Expected<BitcodeLTOInfo> LI = BM.getLTOInfo();
+ Expected<std::unique_ptr<Module>> MOrErr = BM.parseModule(Ctx);
+ if (!LI || !MOrErr) {
+ WithColor::error(errs(), ToolName) << IF.takeError();
+ exit(1);
+ }
+ LTOInfo = std::make_unique<BitcodeLTOInfo>(*LI);
+ M = std::move(MOrErr.get());
+}
+
+void ReducerWorkItem::writeBitcode(raw_ostream &OutStream) const {
+ if (LTOInfo && LTOInfo->IsThinLTO && LTOInfo->EnableSplitLTOUnit) {
+ PassBuilder PB;
+ LoopAnalysisManager LAM;
+ FunctionAnalysisManager FAM;
+ CGSCCAnalysisManager CGAM;
+ ModuleAnalysisManager MAM;
+ PB.registerModuleAnalyses(MAM);
+ PB.registerCGSCCAnalyses(CGAM);
+ PB.registerFunctionAnalyses(FAM);
+ PB.registerLoopAnalyses(LAM);
+ PB.crossRegisterProxies(LAM, FAM, CGAM, MAM);
+ ModulePassManager MPM;
+ MPM.addPass(ThinLTOBitcodeWriterPass(OutStream, nullptr));
+ MPM.run(*M, MAM);
+ } else {
+ std::unique_ptr<ModuleSummaryIndex> Index;
+ if (LTOInfo && LTOInfo->HasSummary) {
+ ProfileSummaryInfo PSI(*M);
+ Index = std::make_unique<ModuleSummaryIndex>(
+ buildModuleSummaryIndex(*M, nullptr, &PSI));
+ }
+ WriteBitcodeToFile(getModule(), OutStream, Index.get());
+ }
+}
+
+std::pair<std::unique_ptr<ReducerWorkItem>, bool>
+llvm::parseReducerWorkItem(StringRef ToolName, StringRef Filename,
+ LLVMContext &Ctxt,
+ std::unique_ptr<TargetMachine> &TM, bool IsMIR) {
+ bool IsBitcode = false;
+ Triple TheTriple;
+
+ auto MMM = std::make_unique<ReducerWorkItem>();
+
+ if (IsMIR) {
+ initializeTargetInfo();
+
+ auto FileOrErr = MemoryBuffer::getFileOrSTDIN(Filename, /*IsText=*/true);
+ if (std::error_code EC = FileOrErr.getError()) {
+ WithColor::error(errs(), ToolName) << EC.message() << '\n';
+ return {nullptr, false};
+ }
+
+ std::unique_ptr<MIRParser> MParser =
+ createMIRParser(std::move(FileOrErr.get()), Ctxt);
+
+ auto SetDataLayout = [&](StringRef DataLayoutTargetTriple,
+ StringRef OldDLStr) -> std::optional<std::string> {
+ // If we are supposed to override the target triple, do so now.
+ std::string IRTargetTriple = DataLayoutTargetTriple.str();
+ if (!TargetTriple.empty())
+ IRTargetTriple = Triple::normalize(TargetTriple);
+ TheTriple = Triple(IRTargetTriple);
+ if (TheTriple.getTriple().empty())
+ TheTriple.setTriple(sys::getDefaultTargetTriple());
+
+ std::string Error;
+ const Target *TheTarget =
+ TargetRegistry::lookupTarget(codegen::getMArch(), TheTriple, Error);
+ if (!TheTarget) {
+ WithColor::error(errs(), ToolName) << Error;
+ exit(1);
+ }
+
+ // Hopefully the MIR parsing doesn't depend on any options.
+ TargetOptions Options;
+ std::optional<Reloc::Model> RM = codegen::getExplicitRelocModel();
+ std::string CPUStr = codegen::getCPUStr();
+ std::string FeaturesStr = codegen::getFeaturesStr();
+ TM = std::unique_ptr<TargetMachine>(TheTarget->createTargetMachine(
+ TheTriple.getTriple(), CPUStr, FeaturesStr, Options, RM,
+ codegen::getExplicitCodeModel(), CodeGenOpt::Default));
+ assert(TM && "Could not allocate target machine!");
+
+ return TM->createDataLayout().getStringRepresentation();
+ };
+
+ std::unique_ptr<Module> M = MParser->parseIRModule(SetDataLayout);
+ LLVMTargetMachine *LLVMTM = static_cast<LLVMTargetMachine *>(TM.get());
+
+ MMM->MMI = std::make_unique<MachineModuleInfo>(LLVMTM);
+ MParser->parseMachineFunctions(*M, *MMM->MMI);
+ MMM->M = std::move(M);
+ } else {
+ SMDiagnostic Err;
+ ErrorOr<std::unique_ptr<MemoryBuffer>> MB =
+ MemoryBuffer::getFileOrSTDIN(Filename);
+ if (std::error_code EC = MB.getError()) {
+ WithColor::error(errs(), ToolName)
+ << Filename << ": " << EC.message() << "\n";
+ return {nullptr, false};
+ }
+
+ if (!isBitcode((const unsigned char *)(*MB)->getBufferStart(),
+ (const unsigned char *)(*MB)->getBufferEnd())) {
+ std::unique_ptr<Module> Result = parseIRFile(Filename, Err, Ctxt);
+ if (!Result) {
+ Err.print(ToolName.data(), errs());
+ return {nullptr, false};
+ }
+ MMM->M = std::move(Result);
+ } else {
+ IsBitcode = true;
+ MMM->readBitcode(MemoryBufferRef(**MB), Ctxt, ToolName);
+
+ if (MMM->LTOInfo->IsThinLTO && MMM->LTOInfo->EnableSplitLTOUnit)
+ initializeTargetInfo();
+ }
+ }
+ if (MMM->verify(&errs())) {
+ WithColor::error(errs(), ToolName)
+ << Filename << " - input module is broken!\n";
+ return {nullptr, false};
+ }
+ return {std::move(MMM), IsBitcode};
+}
#include "Delta.h"
#include "ReducerWorkItem.h"
+#include "TestRunner.h"
#include "Utils.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Analysis/ModuleSummaryAnalysis.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/MemoryBufferRef.h"
#include "llvm/Support/ThreadPool.h"
-#include "llvm/Support/ToolOutputFile.h"
#include <fstream>
#include <set>
cl::desc("Number of times to divide chunks prior to first test"),
cl::cat(LLVMReduceOptions));
-static cl::opt<bool> TmpFilesAsBitcode(
- "write-tmp-files-as-bitcode",
- cl::desc("Always write temporary files as bitcode instead of textual IR"),
- cl::init(false), cl::cat(LLVMReduceOptions));
-
#ifdef LLVM_ENABLE_THREADS
static cl::opt<unsigned> NumJobs(
"j",
unsigned NumJobs = 1;
#endif
-void writeBitcode(const ReducerWorkItem &M, raw_ostream &OutStream);
-
-void readBitcode(ReducerWorkItem &M, MemoryBufferRef Data, LLVMContext &Ctx,
- StringRef ToolName);
-
-bool isReduced(const ReducerWorkItem &M, const TestRunner &Test) {
- const bool UseBitcode = Test.inputIsBitcode() || TmpFilesAsBitcode;
-
- SmallString<128> CurrentFilepath;
-
- // Write ReducerWorkItem to tmp file
- int FD;
- std::error_code EC = sys::fs::createTemporaryFile(
- "llvm-reduce", M.isMIR() ? "mir" : (UseBitcode ? "bc" : "ll"), FD,
- CurrentFilepath,
- UseBitcode && !M.isMIR() ? sys::fs::OF_None : sys::fs::OF_Text);
- if (EC) {
- errs() << "Error making unique filename: " << EC.message() << "!\n";
- exit(1);
- }
-
- ToolOutputFile Out(CurrentFilepath, FD);
-
- if (TmpFilesAsBitcode)
- writeBitcode(M, Out.os());
- else
- M.print(Out.os(), /*AnnotationWriter=*/nullptr);
-
- Out.os().close();
- if (Out.os().has_error()) {
- errs() << "Error emitting bitcode to file '" << CurrentFilepath
- << "': " << Out.os().error().message();
- exit(1);
- }
-
- // Current Chunks aren't interesting
- return Test.run(CurrentFilepath);
-}
-
/// Splits Chunks in half and prints them.
/// If unable to split (when chunk size is 1) returns false.
static bool increaseGranularity(std::vector<Chunk> &Chunks) {
ExtractChunksFromModule(O, *Clone);
// Some reductions may result in invalid IR. Skip such reductions.
- if (verifyReducerWorkItem(*Clone, &errs())) {
+ if (Clone->verify(&errs())) {
if (AbortOnInvalidReduction) {
errs() << "Invalid reduction, aborting.\n";
Clone->print(errs());
errs() << "\n";
}
- if (!isReduced(*Clone, Test)) {
+ if (!Clone->isReduced(Test)) {
// Program became non-reduced, so this chunk appears to be interesting.
if (Verbose)
errs() << "\n";
LLVMContext Ctx;
auto CloneMMM = std::make_unique<ReducerWorkItem>();
MemoryBufferRef Data(OriginalBC, "<bc file>");
- readBitcode(*CloneMMM, Data, Ctx, Test.getToolName());
+ CloneMMM->readBitcode(Data, Ctx, Test.getToolName());
SmallString<0> Result;
if (std::unique_ptr<ReducerWorkItem> ChunkResult =
Test, ExtractChunksFromModule, UninterestingChunks,
ChunksStillConsideredInteresting)) {
raw_svector_ostream BCOS(Result);
- writeBitcode(*ChunkResult, BCOS);
+ ChunkResult->writeBitcode(BCOS);
// Communicate that the task reduced a chunk.
AnyReduced = true;
}
/// reduction pass where no change must be made to the module.
void llvm::runDeltaPass(TestRunner &Test, ReductionFunc ExtractChunksFromModule,
StringRef Message) {
- assert(!verifyReducerWorkItem(Test.getProgram(), &errs()) &&
+ assert(!Test.getProgram().verify(&errs()) &&
"input module is broken before making changes");
errs() << "*** " << Message << "...\n";
ExtractChunksFromModule(Counter, Test.getProgram());
Targets = Counter.count();
- assert(!verifyReducerWorkItem(Test.getProgram(), &errs()) &&
+ assert(!Test.getProgram().verify(&errs()) &&
"input module is broken after counting chunks");
- assert(isReduced(Test.getProgram(), Test) &&
+ assert(Test.getProgram().isReduced(Test) &&
"input module no longer interesting after counting chunks");
#ifndef NDEBUG
std::vector<Chunk> NoChunks = {{0, INT_MAX}};
Oracle NoChunksCounter(NoChunks);
std::unique_ptr<ReducerWorkItem> Clone =
- cloneReducerWorkItem(Test.getProgram(), Test.getTargetMachine());
+ Test.getProgram().clone(Test.getTargetMachine());
ExtractChunksFromModule(NoChunksCounter, *Clone);
assert(Targets == NoChunksCounter.count() &&
"number of chunks changes when reducing");
SmallString<0> OriginalBC;
if (NumJobs > 1) {
raw_svector_ostream BCOS(OriginalBC);
- writeBitcode(Test.getProgram(), BCOS);
+ Test.getProgram().writeBitcode(BCOS);
}
SharedTaskQueue TaskQueue;
Result = std::make_unique<ReducerWorkItem>();
MemoryBufferRef Data(StringRef(Res), "<bc file>");
- readBitcode(*Result, Data, Test.getProgram().M->getContext(),
- Test.getToolName());
+ Result->readBitcode(Data, Test.getProgram().M->getContext(),
+ Test.getToolName());
break;
}
// Forward I to the last chunk processed in parallel.
I += NumChunksProcessed - 1;
} else {
- Result = CheckChunk(
- *I,
- cloneReducerWorkItem(Test.getProgram(), Test.getTargetMachine()),
- Test, ExtractChunksFromModule, UninterestingChunks,
- ChunksStillConsideredInteresting);
+ Result =
+ CheckChunk(*I, Test.getProgram().clone(Test.getTargetMachine()),
+ Test, ExtractChunksFromModule, UninterestingChunks,
+ ChunksStillConsideredInteresting);
}
if (!Result)