1
0
mirror of https://github.com/exaloop/codon.git synced 2025-06-03 15:03:52 +08:00

Refactor LLVM codegen

This commit is contained in:
A. R. Shajii 2021-10-21 14:08:11 -04:00
parent 06063eb1f2
commit 681df2069b
8 changed files with 373 additions and 283 deletions

View File

@ -122,6 +122,8 @@ set(CODON_HPPFILES
codon/sir/llvm/coro/Coroutines.h
codon/sir/llvm/llvisitor.h
codon/sir/llvm/llvm.h
codon/sir/llvm/memory_manager.h
codon/sir/llvm/optimize.h
codon/sir/module.h
codon/sir/sir.h
codon/sir/transform/cleanup/canonical.h
@ -240,6 +242,8 @@ set(CODON_CPPFILES
codon/sir/llvm/coro/CoroSplit.cpp
codon/sir/llvm/coro/Coroutines.cpp
codon/sir/llvm/llvisitor.cpp
codon/sir/llvm/memory_manager.cpp
codon/sir/llvm/optimize.cpp
codon/sir/module.cpp
codon/sir/transform/cleanup/canonical.cpp
codon/sir/transform/cleanup/dead_code.cpp

View File

@ -24,14 +24,27 @@ struct seq_str_t {
extern int seq_debug;
SEQ_FUNC void seq_init(int debug);
SEQ_FUNC bool seq_is_macos();
SEQ_FUNC seq_int_t seq_pid();
SEQ_FUNC seq_int_t seq_time();
SEQ_FUNC seq_int_t seq_time_monotonic();
SEQ_FUNC char **seq_env();
SEQ_FUNC void seq_assert_failed(seq_str_t file, seq_int_t line);
SEQ_FUNC void *seq_alloc(size_t n);
SEQ_FUNC void *seq_alloc_atomic(size_t n);
SEQ_FUNC void *seq_calloc(size_t m, size_t n);
SEQ_FUNC void *seq_calloc_atomic(size_t m, size_t n);
SEQ_FUNC void *seq_realloc(void *p, size_t n);
SEQ_FUNC void seq_free(void *p);
SEQ_FUNC void seq_register_finalizer(void *p, void (*f)(void *obj, void *data));
SEQ_FUNC void seq_gc_add_roots(void *start, void *end);
SEQ_FUNC void seq_gc_remove_roots(void *start, void *end);
SEQ_FUNC void seq_gc_clear_roots();
SEQ_FUNC void seq_gc_exclude_static_roots(void *start, void *end);
SEQ_FUNC void *seq_alloc_exc(int type, void *obj);
SEQ_FUNC void seq_throw(void *exc);
SEQ_FUNC _Unwind_Reason_Code seq_personality(int version, _Unwind_Action actions,
@ -49,7 +62,18 @@ SEQ_FUNC seq_str_t seq_str_byte(char c);
SEQ_FUNC seq_str_t seq_str_ptr(void *p);
SEQ_FUNC seq_str_t seq_str_tuple(seq_str_t *strs, seq_int_t n);
SEQ_FUNC void *seq_stdin();
SEQ_FUNC void *seq_stdout();
SEQ_FUNC void *seq_stderr();
SEQ_FUNC void seq_print(seq_str_t str);
SEQ_FUNC void seq_print_full(seq_str_t str, FILE *fo);
SEQ_FUNC void *seq_lock_new();
SEQ_FUNC bool seq_lock_acquire(void *lock, bool block, double timeout);
SEQ_FUNC void seq_lock_release(void *lock);
SEQ_FUNC void *seq_rlock_new();
SEQ_FUNC bool seq_rlock_acquire(void *lock, bool block, double timeout);
SEQ_FUNC void seq_rlock_release(void *lock);
#endif /* CODON_RUNTIME_LIB_H */

View File

@ -5,25 +5,12 @@
#include <unistd.h>
#include <utility>
#include "llvm/CodeGen/CommandFlags.h"
#include "codon/runtime/lib.h"
#include "codon/sir/dsl/codegen.h"
#include "codon/sir/llvm/coro/Coroutines.h"
#include "codon/sir/llvm/memory_manager.h"
#include "codon/sir/llvm/optimize.h"
#include "codon/util/common.h"
#define STR_HELPER(x) #x
#define STR(x) STR_HELPER(x)
#define CODON_VERSION_STRING() \
("codon version " STR(CODON_VERSION_MAJOR) "." STR(CODON_VERSION_MINOR) "." STR( \
CODON_VERSION_PATCH))
extern "C" void seq_gc_add_roots(void *start, void *end);
extern "C" void seq_gc_remove_roots(void *start, void *end);
extern "C" int64_t seq_exc_offset();
extern "C" uint64_t seq_exc_class();
static llvm::codegen::RegisterCodeGenFlags CFG;
namespace codon {
namespace ir {
namespace {
@ -57,140 +44,6 @@ const SrcInfo *getSrcInfo(const Node *x) {
llvm::Value *getDummyVoidValue(llvm::LLVMContext &context) {
return llvm::ConstantTokenNone::get(context);
}
std::unique_ptr<llvm::TargetMachine>
getTargetMachine(llvm::Triple triple, llvm::StringRef cpuStr,
llvm::StringRef featuresStr, const llvm::TargetOptions &options) {
std::string err;
const llvm::Target *target =
llvm::TargetRegistry::lookupTarget(llvm::codegen::getMArch(), triple, err);
if (!target)
return nullptr;
return std::unique_ptr<llvm::TargetMachine>(target->createTargetMachine(
triple.getTriple(), cpuStr, featuresStr, options,
llvm::codegen::getExplicitRelocModel(), llvm::codegen::getExplicitCodeModel(),
llvm::CodeGenOpt::Aggressive));
}
/**
* Simple extension of LLVM's SectionMemoryManager which catches data section
* allocations and registers them with the GC. This allows the GC to know not
* to collect globals even in JIT mode.
*/
class BoehmGCMemoryManager : public llvm::SectionMemoryManager {
private:
/// Vector of (start, end) address pairs registered with GC.
std::vector<std::pair<void *, void *>> roots;
uint8_t *allocateDataSection(uintptr_t size, unsigned alignment, unsigned sectionID,
llvm::StringRef sectionName, bool isReadOnly) override {
uint8_t *result = SectionMemoryManager::allocateDataSection(
size, alignment, sectionID, sectionName, isReadOnly);
void *start = result;
void *end = result + size;
seq_gc_add_roots(start, end);
roots.emplace_back(start, end);
return result;
}
public:
BoehmGCMemoryManager() : SectionMemoryManager(), roots() {}
~BoehmGCMemoryManager() override {
for (const auto &root : roots) {
seq_gc_remove_roots(root.first, root.second);
}
}
};
/**
* Sometimes coroutine lowering produces hard-to-analyze loops involving
* function pointer comparisons. This pass puts them into a somewhat
* easier-to-analyze form.
*/
struct CoroBranchSimplifier : public llvm::LoopPass {
static char ID;
CoroBranchSimplifier() : llvm::LoopPass(ID) {}
static llvm::Value *getNonNullOperand(llvm::Value *op1, llvm::Value *op2) {
auto *ptr = llvm::dyn_cast<llvm::PointerType>(op1->getType());
if (!ptr || !ptr->getElementType()->isFunctionTy())
return nullptr;
auto *c1 = llvm::dyn_cast<llvm::Constant>(op1);
auto *c2 = llvm::dyn_cast<llvm::Constant>(op2);
const bool isNull1 = (c1 && c1->isNullValue());
const bool isNull2 = (c2 && c2->isNullValue());
if (!(isNull1 ^ isNull2))
return nullptr;
return isNull1 ? op2 : op1;
}
bool runOnLoop(llvm::Loop *loop, llvm::LPPassManager &lpm) override {
if (auto *exit = loop->getExitingBlock()) {
if (auto *br = llvm::dyn_cast<llvm::BranchInst>(exit->getTerminator())) {
if (!br->isConditional() || br->getNumSuccessors() != 2 ||
loop->contains(br->getSuccessor(0)) || !loop->contains(br->getSuccessor(1)))
return false;
auto *cond = br->getCondition();
if (auto *cmp = llvm::dyn_cast<llvm::CmpInst>(cond)) {
if (cmp->getPredicate() != llvm::CmpInst::Predicate::ICMP_EQ)
return false;
if (auto *f = getNonNullOperand(cmp->getOperand(0), cmp->getOperand(1))) {
if (auto *sel = llvm::dyn_cast<llvm::SelectInst>(f)) {
if (auto *g =
getNonNullOperand(sel->getTrueValue(), sel->getFalseValue())) {
// If we can deduce that g is not null, we can replace the condition.
if (auto *phi = llvm::dyn_cast<llvm::PHINode>(g)) {
bool ok = true;
for (unsigned i = 0; i < phi->getNumIncomingValues(); i++) {
auto *phiBlock = phi->getIncomingBlock(i);
auto *phiValue = phi->getIncomingValue(i);
if (auto *c = llvm::dyn_cast<llvm::Constant>(phiValue)) {
if (c->isNullValue()) {
ok = false;
break;
}
} else {
// There is no way for the value to be null if the incoming phi
// value is predicated on this exit condition, which checks for a
// non-null function pointer.
if (phiBlock != exit || phiValue != f) {
ok = false;
break;
}
}
}
if (!ok)
return false;
br->setCondition(sel->getCondition());
return true;
}
}
}
}
}
}
}
return false;
}
};
void addCoroutineBranchSimplifier(const llvm::PassManagerBuilder &builder,
llvm::legacy::PassManagerBase &pm) {
pm.add(new CoroBranchSimplifier());
}
char CoroBranchSimplifier::ID = 0;
llvm::RegisterPass<CoroBranchSimplifier> X("coro-br-simpl",
"Coroutine Branch Simplifier");
} // namespace
llvm::DIFile *LLVMVisitor::DebugInfo::getFile(const std::string &path) {
@ -210,7 +63,7 @@ llvm::DIFile *LLVMVisitor::DebugInfo::getFile(const std::string &path) {
LLVMVisitor::LLVMVisitor(bool debug, const std::string &flags)
: util::ConstVisitor(), context(), builder(context), module(), func(nullptr),
block(nullptr), value(nullptr), vars(), funcs(), coro(), loops(), trycatch(),
db(debug, flags), machine(), plugins(nullptr) {
db(debug, flags), plugins(nullptr) {
llvm::InitializeAllTargets();
llvm::InitializeAllTargetMCs();
llvm::InitializeAllAsmPrinters();
@ -269,132 +122,9 @@ void LLVMVisitor::process(const Node *x) {
x->accept(*this);
}
void LLVMVisitor::verify() {
const bool broken = llvm::verifyModule(*module, &llvm::errs());
seqassert(!broken, "module broken");
}
void LLVMVisitor::dump(const std::string &filename) { writeToLLFile(filename, false); }
void LLVMVisitor::applyDebugTransformations() {
if (db.debug) {
// remove tail calls and fix linkage for stack traces
for (auto &f : *module) {
f.setLinkage(llvm::GlobalValue::ExternalLinkage);
if (f.hasFnAttribute(llvm::Attribute::AttrKind::AlwaysInline)) {
f.removeFnAttr(llvm::Attribute::AttrKind::AlwaysInline);
}
f.addFnAttr(llvm::Attribute::AttrKind::NoInline);
f.setHasUWTable();
f.addFnAttr("no-frame-pointer-elim", "true");
f.addFnAttr("no-frame-pointer-elim-non-leaf");
f.addFnAttr("no-jump-tables", "false");
for (auto &block : f.getBasicBlockList()) {
for (auto &inst : block) {
if (auto *call = llvm::dyn_cast<llvm::CallInst>(&inst)) {
call->setTailCall(false);
}
}
}
}
} else {
llvm::StripDebugInfo(*module);
}
}
void LLVMVisitor::runLLVMOptimizationPasses() {
applyDebugTransformations();
llvm::Triple moduleTriple(module->getTargetTriple());
std::string cpuStr, featuresStr;
const llvm::TargetOptions options =
llvm::codegen::InitTargetOptionsFromCodeGenFlags(moduleTriple);
llvm::TargetLibraryInfoImpl tlii(moduleTriple);
auto pm = std::make_unique<llvm::legacy::PassManager>();
auto fpm = std::make_unique<llvm::legacy::FunctionPassManager>(module.get());
pm->add(new llvm::TargetLibraryInfoWrapperPass(tlii));
if (moduleTriple.getArch()) {
cpuStr = llvm::codegen::getCPUStr();
featuresStr = llvm::codegen::getFeaturesStr();
machine = getTargetMachine(moduleTriple, cpuStr, featuresStr, options);
}
llvm::codegen::setFunctionAttributes(cpuStr, featuresStr, *module);
pm->add(llvm::createTargetTransformInfoWrapperPass(
machine ? machine->getTargetIRAnalysis() : llvm::TargetIRAnalysis()));
fpm->add(llvm::createTargetTransformInfoWrapperPass(
machine ? machine->getTargetIRAnalysis() : llvm::TargetIRAnalysis()));
if (machine) {
auto &ltm = dynamic_cast<llvm::LLVMTargetMachine &>(*machine);
llvm::Pass *tpc = ltm.createPassConfig(*pm);
pm->add(tpc);
}
unsigned optLevel = 3;
unsigned sizeLevel = 0;
llvm::PassManagerBuilder pmb;
if (!db.debug) {
pmb.OptLevel = optLevel;
pmb.SizeLevel = sizeLevel;
pmb.Inliner = llvm::createFunctionInliningPass(optLevel, sizeLevel, false);
pmb.DisableUnrollLoops = false;
pmb.LoopVectorize = true;
pmb.SLPVectorize = true;
// pmb.MergeFunctions = true;
} else {
pmb.OptLevel = 0;
}
if (machine) {
machine->adjustPassManager(pmb);
}
coro::addCoroutinePassesToExtensionPoints(pmb);
if (!db.debug) {
pmb.addExtension(llvm::PassManagerBuilder::EP_LateLoopOptimizations,
addCoroutineBranchSimplifier);
}
if (plugins) {
for (auto *plugin : *plugins) {
plugin->dsl->addLLVMPasses(&pmb, db.debug);
}
}
pmb.populateModulePassManager(*pm);
pmb.populateFunctionPassManager(*fpm);
fpm->doInitialization();
for (llvm::Function &f : *module) {
fpm->run(f);
}
fpm->doFinalization();
pm->run(*module);
applyDebugTransformations();
}
void LLVMVisitor::runLLVMPipeline() {
using namespace std::chrono;
auto t = high_resolution_clock::now();
verify();
runLLVMOptimizationPasses();
LOG_TIME("[T] llvm/opt = {:.1f}",
duration_cast<milliseconds>(high_resolution_clock::now() - t).count() /
1000.0);
if (!db.debug) {
t = high_resolution_clock::now();
runLLVMOptimizationPasses();
LOG_TIME("[T] llvm/opt2 = {:.1f}",
duration_cast<milliseconds>(high_resolution_clock::now() - t).count() /
1000.0);
}
verify();
}
void LLVMVisitor::runLLVMPipeline() { optimize(module.get(), db.debug, plugins); }
void LLVMVisitor::writeToObjectFile(const std::string &filename) {
runLLVMPipeline();
@ -406,6 +136,7 @@ void LLVMVisitor::writeToObjectFile(const std::string &filename) {
compilationError(err.message());
llvm::raw_pwrite_stream *os = &out->os();
auto machine = getTargetMachine(module.get());
auto &llvmtm = static_cast<llvm::LLVMTargetMachine &>(*machine);
auto *mmiwp = new llvm::MachineModuleInfoWrapperPass(&llvmtm);
llvm::legacy::PassManager pm;
@ -694,7 +425,8 @@ void LLVMVisitor::visit(const Module *x) {
db.builder = std::make_unique<llvm::DIBuilder>(*module);
llvm::DIFile *file = db.getFile(srcInfo->file);
db.unit = db.builder->createCompileUnit(llvm::dwarf::DW_LANG_C, file,
CODON_VERSION_STRING(), !db.debug, db.flags,
("codon version " CODON_VERSION), !db.debug,
db.flags,
/*RV=*/0);
module->addModuleFlag(llvm::Module::Warning, "Debug Info Version",
llvm::DEBUG_METADATA_VERSION);

View File

@ -129,8 +129,6 @@ private:
std::vector<TryCatchData> trycatch;
/// Debug information
DebugInfo db;
/// LLVM target machine
std::unique_ptr<llvm::TargetMachine> machine;
/// Plugin manager
PluginManager *plugins;
@ -175,8 +173,6 @@ private:
TryCatchData *getInnermostTryCatchBeforeLoop();
// LLVM passes
void applyDebugTransformations();
void runLLVMOptimizationPasses();
void runLLVMPipeline();
public:
@ -208,9 +204,6 @@ public:
/// @param node the node to compile
void process(const Node *node);
/// Performs LLVM's module verification on the contained module.
/// Causes an assertion failure if verification fails.
void verify();
/// Dumps the unoptimized module IR to a file.
/// @param filename name of file to write IR to
void dump(const std::string &filename = "_dump.ll");

View File

@ -0,0 +1,30 @@
#include "memory_manager.h"
#include "codon/runtime/lib.h"
namespace codon {
namespace ir {
BoehmGCMemoryManager::BoehmGCMemoryManager() : SectionMemoryManager(), roots() {}
uint8_t *BoehmGCMemoryManager::allocateDataSection(uintptr_t size, unsigned alignment,
unsigned sectionID,
llvm::StringRef sectionName,
bool isReadOnly) {
auto *result = SectionMemoryManager::allocateDataSection(size, alignment, sectionID,
sectionName, isReadOnly);
void *start = result;
void *end = result + size;
seq_gc_add_roots(start, end);
roots.emplace_back(start, end);
return result;
}
BoehmGCMemoryManager::~BoehmGCMemoryManager() {
for (const auto &root : roots) {
seq_gc_remove_roots(root.first, root.second);
}
}
} // namespace ir
} // namespace codon

View File

@ -0,0 +1,27 @@
#pragma once
#include <utility>
#include <vector>
#include "codon/sir/llvm/llvm.h"
namespace codon {
namespace ir {
/// Simple extension of LLVM's SectionMemoryManager which catches data section
/// allocations and registers them with the GC. This allows the GC to know not
/// to collect globals even in JIT mode.
class BoehmGCMemoryManager : public llvm::SectionMemoryManager {
private:
/// Vector of (start, end) address pairs registered with GC.
std::vector<std::pair<void *, void *>> roots;
uint8_t *allocateDataSection(uintptr_t size, unsigned alignment, unsigned sectionID,
llvm::StringRef sectionName, bool isReadOnly) override;
public:
BoehmGCMemoryManager();
~BoehmGCMemoryManager() override;
};
} // namespace ir
} // namespace codon

261
codon/sir/llvm/optimize.cpp Normal file
View File

@ -0,0 +1,261 @@
#include "optimize.h"
#include <chrono>
#include "codon/sir/llvm/coro/Coroutines.h"
#include "codon/util/common.h"
#include "llvm/CodeGen/CommandFlags.h"
static llvm::codegen::RegisterCodeGenFlags CFG;
namespace codon {
namespace ir {
std::unique_ptr<llvm::TargetMachine>
getTargetMachine(llvm::Triple triple, llvm::StringRef cpuStr,
llvm::StringRef featuresStr, const llvm::TargetOptions &options) {
std::string err;
const llvm::Target *target =
llvm::TargetRegistry::lookupTarget(llvm::codegen::getMArch(), triple, err);
if (!target)
return nullptr;
return std::unique_ptr<llvm::TargetMachine>(target->createTargetMachine(
triple.getTriple(), cpuStr, featuresStr, options,
llvm::codegen::getExplicitRelocModel(), llvm::codegen::getExplicitCodeModel(),
llvm::CodeGenOpt::Aggressive));
}
std::unique_ptr<llvm::TargetMachine> getTargetMachine(llvm::Module *module,
bool setFunctionAttributes) {
llvm::Triple moduleTriple(module->getTargetTriple());
std::string cpuStr, featuresStr;
const llvm::TargetOptions options =
llvm::codegen::InitTargetOptionsFromCodeGenFlags(moduleTriple);
llvm::TargetLibraryInfoImpl tlii(moduleTriple);
auto pm = std::make_unique<llvm::legacy::PassManager>();
auto fpm = std::make_unique<llvm::legacy::FunctionPassManager>(module);
pm->add(new llvm::TargetLibraryInfoWrapperPass(tlii));
if (moduleTriple.getArch()) {
cpuStr = llvm::codegen::getCPUStr();
featuresStr = llvm::codegen::getFeaturesStr();
auto machine = getTargetMachine(moduleTriple, cpuStr, featuresStr, options);
if (setFunctionAttributes)
llvm::codegen::setFunctionAttributes(cpuStr, featuresStr, *module);
return machine;
}
return {};
}
namespace {
void applyDebugTransformations(llvm::Module *module, bool debug) {
if (debug) {
// remove tail calls and fix linkage for stack traces
for (auto &f : *module) {
f.setLinkage(llvm::GlobalValue::ExternalLinkage);
if (f.hasFnAttribute(llvm::Attribute::AttrKind::AlwaysInline)) {
f.removeFnAttr(llvm::Attribute::AttrKind::AlwaysInline);
}
f.addFnAttr(llvm::Attribute::AttrKind::NoInline);
f.setHasUWTable();
f.addFnAttr("no-frame-pointer-elim", "true");
f.addFnAttr("no-frame-pointer-elim-non-leaf");
f.addFnAttr("no-jump-tables", "false");
for (auto &block : f.getBasicBlockList()) {
for (auto &inst : block) {
if (auto *call = llvm::dyn_cast<llvm::CallInst>(&inst)) {
call->setTailCall(false);
}
}
}
}
} else {
llvm::StripDebugInfo(*module);
}
}
/// Sometimes coroutine lowering produces hard-to-analyze loops involving
/// function pointer comparisons. This pass puts them into a somewhat
/// easier-to-analyze form.
struct CoroBranchSimplifier : public llvm::LoopPass {
static char ID;
CoroBranchSimplifier() : llvm::LoopPass(ID) {}
static llvm::Value *getNonNullOperand(llvm::Value *op1, llvm::Value *op2) {
auto *ptr = llvm::dyn_cast<llvm::PointerType>(op1->getType());
if (!ptr || !ptr->getElementType()->isFunctionTy())
return nullptr;
auto *c1 = llvm::dyn_cast<llvm::Constant>(op1);
auto *c2 = llvm::dyn_cast<llvm::Constant>(op2);
const bool isNull1 = (c1 && c1->isNullValue());
const bool isNull2 = (c2 && c2->isNullValue());
if (!(isNull1 ^ isNull2))
return nullptr;
return isNull1 ? op2 : op1;
}
bool runOnLoop(llvm::Loop *loop, llvm::LPPassManager &lpm) override {
if (auto *exit = loop->getExitingBlock()) {
if (auto *br = llvm::dyn_cast<llvm::BranchInst>(exit->getTerminator())) {
if (!br->isConditional() || br->getNumSuccessors() != 2 ||
loop->contains(br->getSuccessor(0)) || !loop->contains(br->getSuccessor(1)))
return false;
auto *cond = br->getCondition();
if (auto *cmp = llvm::dyn_cast<llvm::CmpInst>(cond)) {
if (cmp->getPredicate() != llvm::CmpInst::Predicate::ICMP_EQ)
return false;
if (auto *f = getNonNullOperand(cmp->getOperand(0), cmp->getOperand(1))) {
if (auto *sel = llvm::dyn_cast<llvm::SelectInst>(f)) {
if (auto *g =
getNonNullOperand(sel->getTrueValue(), sel->getFalseValue())) {
// If we can deduce that g is not null, we can replace the condition.
if (auto *phi = llvm::dyn_cast<llvm::PHINode>(g)) {
bool ok = true;
for (unsigned i = 0; i < phi->getNumIncomingValues(); i++) {
auto *phiBlock = phi->getIncomingBlock(i);
auto *phiValue = phi->getIncomingValue(i);
if (auto *c = llvm::dyn_cast<llvm::Constant>(phiValue)) {
if (c->isNullValue()) {
ok = false;
break;
}
} else {
// There is no way for the value to be null if the incoming phi
// value is predicated on this exit condition, which checks for a
// non-null function pointer.
if (phiBlock != exit || phiValue != f) {
ok = false;
break;
}
}
}
if (!ok)
return false;
br->setCondition(sel->getCondition());
return true;
}
}
}
}
}
}
}
return false;
}
};
void addCoroutineBranchSimplifier(const llvm::PassManagerBuilder &builder,
llvm::legacy::PassManagerBase &pm) {
pm.add(new CoroBranchSimplifier());
}
char CoroBranchSimplifier::ID = 0;
llvm::RegisterPass<CoroBranchSimplifier> X("coro-br-simpl",
"Coroutine Branch Simplifier");
void runLLVMOptimizationPasses(llvm::Module *module, bool debug,
PluginManager *plugins) {
applyDebugTransformations(module, debug);
llvm::Triple moduleTriple(module->getTargetTriple());
llvm::TargetLibraryInfoImpl tlii(moduleTriple);
auto pm = std::make_unique<llvm::legacy::PassManager>();
auto fpm = std::make_unique<llvm::legacy::FunctionPassManager>(module);
pm->add(new llvm::TargetLibraryInfoWrapperPass(tlii));
auto machine = getTargetMachine(module, /*setFunctionAttributes=*/true);
pm->add(llvm::createTargetTransformInfoWrapperPass(
machine ? machine->getTargetIRAnalysis() : llvm::TargetIRAnalysis()));
fpm->add(llvm::createTargetTransformInfoWrapperPass(
machine ? machine->getTargetIRAnalysis() : llvm::TargetIRAnalysis()));
if (machine) {
auto &ltm = dynamic_cast<llvm::LLVMTargetMachine &>(*machine);
llvm::Pass *tpc = ltm.createPassConfig(*pm);
pm->add(tpc);
}
unsigned optLevel = 3;
unsigned sizeLevel = 0;
llvm::PassManagerBuilder pmb;
if (!debug) {
pmb.OptLevel = optLevel;
pmb.SizeLevel = sizeLevel;
pmb.Inliner = llvm::createFunctionInliningPass(optLevel, sizeLevel, false);
pmb.DisableUnrollLoops = false;
pmb.LoopVectorize = true;
pmb.SLPVectorize = true;
// pmb.MergeFunctions = true;
} else {
pmb.OptLevel = 0;
}
if (machine) {
machine->adjustPassManager(pmb);
}
coro::addCoroutinePassesToExtensionPoints(pmb);
if (!debug) {
pmb.addExtension(llvm::PassManagerBuilder::EP_LateLoopOptimizations,
addCoroutineBranchSimplifier);
}
if (plugins) {
for (auto *plugin : *plugins) {
plugin->dsl->addLLVMPasses(&pmb, debug);
}
}
pmb.populateModulePassManager(*pm);
pmb.populateFunctionPassManager(*fpm);
fpm->doInitialization();
for (llvm::Function &f : *module) {
fpm->run(f);
}
fpm->doFinalization();
pm->run(*module);
applyDebugTransformations(module, debug);
}
void verify(llvm::Module *module) {
const bool broken = llvm::verifyModule(*module, &llvm::errs());
seqassert(!broken, "module broken");
}
} // namespace
void optimize(llvm::Module *module, bool debug, PluginManager *plugins) {
using std::chrono::duration_cast;
using std::chrono::high_resolution_clock;
using std::chrono::milliseconds;
auto t = high_resolution_clock::now();
verify(module);
runLLVMOptimizationPasses(module, debug, plugins);
LOG_TIME("[T] llvm/opt = {:.1f}",
duration_cast<milliseconds>(high_resolution_clock::now() - t).count() /
1000.0);
if (!debug) {
t = high_resolution_clock::now();
runLLVMOptimizationPasses(module, debug, plugins);
LOG_TIME("[T] llvm/opt2 = {:.1f}",
duration_cast<milliseconds>(high_resolution_clock::now() - t).count() /
1000.0);
}
verify(module);
}
} // namespace ir
} // namespace codon

19
codon/sir/llvm/optimize.h Normal file
View File

@ -0,0 +1,19 @@
#pragma once
#include <memory>
#include "codon/dsl/plugins.h"
#include "codon/sir/llvm/llvm.h"
namespace codon {
namespace ir {
std::unique_ptr<llvm::TargetMachine>
getTargetMachine(llvm::Triple triple, llvm::StringRef cpuStr,
llvm::StringRef featuresStr, const llvm::TargetOptions &options);
std::unique_ptr<llvm::TargetMachine>
getTargetMachine(llvm::Module *module, bool setFunctionAttributes = false);
void optimize(llvm::Module *module, bool debug, PluginManager *plugins = nullptr);
} // namespace ir
} // namespace codon