Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support LLVM new PassManager #8390

Merged
merged 2 commits into from
Apr 2, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
223 changes: 135 additions & 88 deletions src/zig_llvm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#pragma GCC diagnostic ignored "-Winit-list-lifetime"
#endif

#include <llvm/Analysis/AliasAnalysis.h>
#include <llvm/Analysis/TargetLibraryInfo.h>
#include <llvm/Analysis/TargetTransformInfo.h>
#include <llvm/Bitcode/BitcodeWriter.h>
Expand All @@ -30,9 +31,12 @@
#include <llvm/IR/Instructions.h>
#include <llvm/IR/LegacyPassManager.h>
#include <llvm/IR/Module.h>
#include <llvm/IR/PassManager.h>
#include <llvm/IR/Verifier.h>
#include <llvm/InitializePasses.h>
#include <llvm/MC/SubtargetFeature.h>
#include <llvm/Passes/PassBuilder.h>
#include <llvm/Passes/StandardInstrumentations.h>
#include <llvm/Object/Archive.h>
#include <llvm/Object/ArchiveWriter.h>
#include <llvm/Object/COFF.h>
Expand All @@ -54,6 +58,9 @@
#include <llvm/Transforms/Instrumentation/ThreadSanitizer.h>
#include <llvm/Transforms/Scalar.h>
#include <llvm/Transforms/Utils.h>
#include <llvm/Transforms/Utils/AddDiscriminators.h>
#include <llvm/Transforms/Utils/CanonicalizeAliases.h>
#include <llvm/Transforms/Utils/NameAnonGlobals.h>

#include <lld/Common/Driver.h>

Expand Down Expand Up @@ -188,131 +195,171 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM
bool is_small, bool time_report, bool tsan, bool lto,
const char *asm_filename, const char *bin_filename, const char *llvm_ir_filename)
{
// TODO: Maybe we should collect time trace rather than using timer
// to get a more hierarchical timeline view
Comment on lines +198 to +199
Copy link
Member

@andrewrk andrewrk Apr 2, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you make this a github issue instead of a TODO comment?

edit: eh it's fine. will catch it with #363 eventually.

TimePassesIsEnabled = time_report;

raw_fd_ostream *dest_asm = nullptr;
raw_fd_ostream *dest_bin = nullptr;
raw_fd_ostream *dest_asm_ptr = nullptr;
raw_fd_ostream *dest_bin_ptr = nullptr;

if (asm_filename) {
std::error_code EC;
dest_asm = new(std::nothrow) raw_fd_ostream(asm_filename, EC, sys::fs::F_None);
dest_asm_ptr = new(std::nothrow) raw_fd_ostream(asm_filename, EC, sys::fs::F_None);
if (EC) {
*error_message = strdup((const char *)StringRef(EC.message()).bytes_begin());
return true;
}
}
if (bin_filename) {
std::error_code EC;
dest_bin = new(std::nothrow) raw_fd_ostream(bin_filename, EC, sys::fs::F_None);
dest_bin_ptr = new(std::nothrow) raw_fd_ostream(bin_filename, EC, sys::fs::F_None);
if (EC) {
*error_message = strdup((const char *)StringRef(EC.message()).bytes_begin());
return true;
}
}

TargetMachine* target_machine = reinterpret_cast<TargetMachine*>(targ_machine_ref);
target_machine->setO0WantsFastISel(true);

Module* module = unwrap(module_ref);

PassManagerBuilder *PMBuilder = new(std::nothrow) PassManagerBuilder();
if (PMBuilder == nullptr) {
*error_message = strdup("memory allocation failure");
return true;
std::unique_ptr<raw_fd_ostream> dest_asm(dest_asm_ptr),
dest_bin(dest_bin_ptr);

TargetMachine &target_machine = *reinterpret_cast<TargetMachine*>(targ_machine_ref);
target_machine.setO0WantsFastISel(true);

Module &module = *unwrap(module_ref);

// Pipeline configurations
PipelineTuningOptions pipeline_opts;
pipeline_opts.LoopUnrolling = !is_debug;
pipeline_opts.SLPVectorization = !is_debug;
pipeline_opts.LoopVectorization = !is_debug;
pipeline_opts.LoopInterleaving = !is_debug;
pipeline_opts.MergeFunctions = !is_debug;

// Instrumentations
PassInstrumentationCallbacks instr_callbacks;
StandardInstrumentations std_instrumentations(false);
std_instrumentations.registerCallbacks(instr_callbacks);

PassBuilder pass_builder(false, &target_machine, pipeline_opts,
None, &instr_callbacks);
using OptimizationLevel = typename PassBuilder::OptimizationLevel;

LoopAnalysisManager loop_am;
FunctionAnalysisManager function_am;
CGSCCAnalysisManager cgscc_am;
ModuleAnalysisManager module_am;

// Register the AA manager first so that our version is the one used
function_am.registerPass([&] {
return pass_builder.buildDefaultAAPipeline();
});

Triple target_triple(module.getTargetTriple());
auto tlii = std::make_unique<TargetLibraryInfoImpl>(target_triple);
function_am.registerPass([&] { return TargetLibraryAnalysis(*tlii); });

// Initialize the AnalysisManagers
pass_builder.registerModuleAnalyses(module_am);
pass_builder.registerCGSCCAnalyses(cgscc_am);
pass_builder.registerFunctionAnalyses(function_am);
pass_builder.registerLoopAnalyses(loop_am);
pass_builder.crossRegisterProxies(loop_am, function_am,
cgscc_am, module_am);

// IR verification
if (assertions_on) {
// Verify the input
pass_builder.registerPipelineStartEPCallback(
[](ModulePassManager &module_pm, OptimizationLevel OL) {
module_pm.addPass(VerifierPass());
});
// Verify the output
pass_builder.registerOptimizerLastEPCallback(
[](ModulePassManager &module_pm, OptimizationLevel OL) {
module_pm.addPass(VerifierPass());
});
}
PMBuilder->OptLevel = target_machine->getOptLevel();
PMBuilder->SizeLevel = is_small ? 2 : 0;

PMBuilder->DisableTailCalls = is_debug;
PMBuilder->DisableUnrollLoops = is_debug;
PMBuilder->SLPVectorize = !is_debug;
PMBuilder->LoopVectorize = !is_debug;
PMBuilder->LoopsInterleaved = !PMBuilder->DisableUnrollLoops;
PMBuilder->RerollLoops = !is_debug;
// Leaving NewGVN as default (off) because when on it caused issue #673
//PMBuilder->NewGVN = !is_debug;
PMBuilder->DisableGVNLoadPRE = is_debug;
PMBuilder->VerifyInput = assertions_on;
PMBuilder->VerifyOutput = assertions_on;
PMBuilder->MergeFunctions = !is_debug;
PMBuilder->PrepareForLTO = lto;
PMBuilder->PrepareForThinLTO = false;
PMBuilder->PerformThinLTO = false;

TargetLibraryInfoImpl tlii(Triple(module->getTargetTriple()));
PMBuilder->LibraryInfo = &tlii;

// Passes for either debug or release build
if (is_debug) {
PMBuilder->Inliner = createAlwaysInlinerLegacyPass(false);
// NOTE: Always inliner will go away (in debug build)
// when the self-hosted compiler becomes mature.
pass_builder.registerPipelineStartEPCallback(
[](ModulePassManager &module_pm, OptimizationLevel OL) {
module_pm.addPass(AlwaysInlinerPass());
});
} else {
target_machine->adjustPassManager(*PMBuilder);

PMBuilder->addExtension(PassManagerBuilder::EP_EarlyAsPossible, addDiscriminatorsPass);
PMBuilder->Inliner = createFunctionInliningPass(PMBuilder->OptLevel, PMBuilder->SizeLevel, false);
pass_builder.registerPipelineStartEPCallback(
[](ModulePassManager &module_pm, OptimizationLevel OL) {
module_pm.addPass(
createModuleToFunctionPassAdaptor(AddDiscriminatorsPass()));
});
}

// Thread sanitizer
if (tsan) {
PMBuilder->addExtension(PassManagerBuilder::EP_OptimizerLast, addThreadSanitizerPass);
PMBuilder->addExtension(PassManagerBuilder::EP_EnabledOnOptLevel0, addThreadSanitizerPass);
pass_builder.registerOptimizerLastEPCallback(
[](ModulePassManager &module_pm, OptimizationLevel level) {
module_pm.addPass(ThreadSanitizerPass());
});
}

// Set up the per-function pass manager.
legacy::FunctionPassManager FPM = legacy::FunctionPassManager(module);
auto tliwp = new(std::nothrow) TargetLibraryInfoWrapperPass(tlii);
FPM.add(tliwp);
FPM.add(createTargetTransformInfoWrapperPass(target_machine->getTargetIRAnalysis()));
if (assertions_on) {
FPM.add(createVerifierPass());
ModulePassManager module_pm;
OptimizationLevel opt_level;
// Setting up the optimization level
if (is_debug)
opt_level = OptimizationLevel::O0;
else if (is_small)
opt_level = OptimizationLevel::Oz;
else
opt_level = OptimizationLevel::O3;

// Initialize the PassManager
if (lto) {
module_pm = pass_builder.buildLTOPreLinkDefaultPipeline(opt_level);
module_pm.addPass(CanonicalizeAliasesPass());
module_pm.addPass(NameAnonGlobalPass());
} else {
module_pm = pass_builder.buildPerModuleDefaultPipeline(opt_level);
}
PMBuilder->populateFunctionPassManager(FPM);

{
// Set up the per-module pass manager.
legacy::PassManager MPM;
MPM.add(createTargetTransformInfoWrapperPass(target_machine->getTargetIRAnalysis()));
PMBuilder->populateModulePassManager(MPM);

// Set output passes.
if (dest_bin && !lto) {
if (target_machine->addPassesToEmitFile(MPM, *dest_bin, nullptr, CGFT_ObjectFile)) {
*error_message = strdup("TargetMachine can't emit an object file");
return true;
}
}
if (dest_asm) {
if (target_machine->addPassesToEmitFile(MPM, *dest_asm, nullptr, CGFT_AssemblyFile)) {
*error_message = strdup("TargetMachine can't emit an assembly file");
return true;
}
}

// run per function optimization passes
FPM.doInitialization();
for (Function &F : *module)
if (!F.isDeclaration())
FPM.run(F);
FPM.doFinalization();

MPM.run(*module);
// Unfortunately we don't have new PM for code generation
legacy::PassManager codegen_pm;
codegen_pm.add(
createTargetTransformInfoWrapperPass(target_machine.getTargetIRAnalysis()));

if (llvm_ir_filename) {
if (LLVMPrintModuleToFile(module_ref, llvm_ir_filename, error_message)) {
return true;
}
if (dest_bin && !lto) {
if (target_machine.addPassesToEmitFile(codegen_pm, *dest_bin, nullptr, CGFT_ObjectFile)) {
*error_message = strdup("TargetMachine can't emit an object file");
return true;
}
if (dest_bin && lto) {
WriteBitcodeToFile(*module, *dest_bin);
}
if (dest_asm) {
if (target_machine.addPassesToEmitFile(codegen_pm, *dest_asm, nullptr, CGFT_AssemblyFile)) {
*error_message = strdup("TargetMachine can't emit an assembly file");
return true;
}
}

// Optimization phase
module_pm.run(module, module_am);

if (time_report) {
TimerGroup::printAll(errs());
// Code generation phase
codegen_pm.run(module);

if (llvm_ir_filename) {
if (LLVMPrintModuleToFile(module_ref, llvm_ir_filename, error_message)) {
return true;
}
}

// MPM goes out of scope and writes to the out streams
if (dest_bin && lto) {
WriteBitcodeToFile(module, *dest_bin);
}

delete dest_asm;
delete dest_bin;
if (time_report) {
TimerGroup::printAll(errs());
}

return false;
}
Expand Down