Запуск конвейера оптимизации по умолчанию с использованием современного LLVM.

Я использую LLVM 7, и у меня есть llvm::Module, который я хотел бы оптимизировать с помощью стандартного конвейера оптимизации. К сожалению, нет функции llvm::runDefaultOptimizations, которую я мог бы вызвать. Кажется, существует огромное количество способов оптимизировать модуль в LLVM. Мои поиски по этой теме нашли много старых/устаревших API и несколько примеров, которые не работают в моей системе.

Я хочу запустить все стандартные оптимизации на уровне -O3 с наименьшими возможными трудностями. Я не хочу вручную перечислять все проходы или даже писать цикл for. Я думал, что llvm::PassBuilder::buildModuleOptimizationPipeline может быть решением, но я получаю сообщение об ошибке компоновщика, когда пытаюсь использовать эту функцию, что я считаю действительно странным.


person Indiana Kernick    schedule 12.12.2018    source источник


Ответы (2)


В итоге я взял исходный код инструмента opt (найден здесь) и удалив все, что мне не нужно. Я закончил с этим:

#include <llvm/IR/Verifier.h>
#include <llvm/Transforms/IPO.h>
#include <llvm/IR/LegacyPassManager.h>
#include <llvm/Target/TargetMachine.h>
#include <llvm/Analysis/TargetLibraryInfo.h>
#include <llvm/Analysis/TargetTransformInfo.h>
#include <llvm/Transforms/IPO/PassManagerBuilder.h>

namespace {

void addOptPasses(
  llvm::legacy::PassManagerBase &passes,
  llvm::legacy::FunctionPassManager &fnPasses,
  llvm::TargetMachine *machine
) {
  llvm::PassManagerBuilder builder;
  builder.OptLevel = 3;
  builder.SizeLevel = 0;
  builder.Inliner = llvm::createFunctionInliningPass(3, 0, false);
  builder.LoopVectorize = true;
  builder.SLPVectorize = true;
  machine->adjustPassManager(builder);

  builder.populateFunctionPassManager(fnPasses);
  builder.populateModulePassManager(passes);
}

void addLinkPasses(llvm::legacy::PassManagerBase &passes) {
  llvm::PassManagerBuilder builder;
  builder.VerifyInput = true;
  builder.Inliner = llvm::createFunctionInliningPass(3, 0, false);
  builder.populateLTOPassManager(passes);
}

}

void optimizeModule(llvm::TargetMachine *machine, llvm::Module *module) {
  module->setTargetTriple(machine->getTargetTriple().str());
  module->setDataLayout(machine->createDataLayout());

  llvm::legacy::PassManager passes;
  passes.add(new llvm::TargetLibraryInfoWrapperPass(machine->getTargetTriple()));
  passes.add(llvm::createTargetTransformInfoWrapperPass(machine->getTargetIRAnalysis()));

  llvm::legacy::FunctionPassManager fnPasses(module);
  fnPasses.add(llvm::createTargetTransformInfoWrapperPass(machine->getTargetIRAnalysis()));

  addOptPasses(passes, fnPasses, machine);
  addLinkPasses(passes);

  fnPasses.doInitialization();
  for (llvm::Function &func : *module) {
    fnPasses.run(func);
  }
  fnPasses.doFinalization();

  passes.add(llvm::createVerifierPass());
  passes.run(*module);
}

Это примерно эквивалентно передаче -O3 в opt. Он использует кое-что из legacy, но я не возражаю.

person Indiana Kernick    schedule 14.12.2018
comment
Вы уверены, что это эквивалентно пропуску O3? На данный момент у меня нет моей установки. Вы пытались скомпилировать файл большего размера, используя последнюю версию clang с O3 и инструмент LLVM dif? - person JKRT; 14.12.2018
comment
@JKT Это не должно быть точно таким же. Это действительно хорошая работа по оптимизации нескольких тестов, которые я ему дал. Он превратил некоторые вложенные циклы в целочисленное умножение! Это как волшебство! - person Indiana Kernick; 14.12.2018
comment
Я правильно понимаю, но тогда это следует уточнить в ответе на случай, если кто-то хочет получить правильную стандартную функциональность O3, как указано в вопросе. Например, добавьте примерно эквивалент последней строки - person JKRT; 14.12.2018
comment
@JKT Я не сравнивал их должным образом, так что они могут быть одинаковыми, а могут и нет. Я не уверен. - person Indiana Kernick; 14.12.2018
comment
Я посмотрю на это когда-нибудь на следующей неделе, здорово, что у вас все получилось! - person JKRT; 14.12.2018
comment
@JKT Если подумать, они, вероятно, очень похожи, но в некоторых случаях не совсем одинаковы. - person Indiana Kernick; 14.12.2018
comment
@ Kerndo73 Я сохраню свой более сложный ответ на этот вопрос для дальнейшего использования, я видел ваше редактирование - person JKRT; 14.12.2018

Чтобы увидеть стандартные проходы для LLVM, вы можете попробовать проверить подклассы интерфейса Pass. Насколько мне известно, в самом LLVM API нет прохода, который бы запускал специальные проходы clang. Для этого вам нужно посмотреть clang.

Чтобы выяснить, какие именно проходы вы хотели бы добавить, посмотрите на

llvm-as < /dev/null | opt -O3 -disable-output -debug-pass=Arguments  

См. Где найти последовательность оптимизации для clang -OX?

Тем не менее, есть некоторые проблемы с поиском API, который вы используете, и так далее. То же самое можно применить и к Clang -O3.

Что вы можете сделать, если это возможно для вашего проекта, так это сгенерировать LLVM IR в файл на диске, а затем скомпилировать неоптимизированный LLVM IR с clang отдельно с флагом O3.

Вот как вы можете запустить некоторые проходы, используя устаревший диспетчер проходов. Предполагая, что у вас есть контекст LLVM.

 module = llvm::make_unique<llvm::Module>("module",context); //Context is your LLVM context.
 functionPassMngr = llvm::make_unique<llvm::legacy::FunctionPassManager>(module.get());
 functionPassMngr->add(llvm::createPromoteMemoryToRegisterPass()); //SSA conversion
 functionPassMngr->add(llvm::createCFGSimplificationPass()); //Dead code elimination
 functionPassMngr->add(llvm::createSROAPass());
 functionPassMngr->add(llvm::createLoopSimplifyCFGPass());
 functionPassMngr->add(llvm::createConstantPropagationPass());
 functionPassMngr->add(llvm::createNewGVNPass());//Global value numbering
 functionPassMngr->add(llvm::createReassociatePass());
 functionPassMngr->add(llvm::createPartiallyInlineLibCallsPass()); //Inline standard calls
 functionPassMngr->add(llvm::createDeadCodeEliminationPass());
 functionPassMngr->add(llvm::createCFGSimplificationPass()); //Cleanup
 functionPassMngr->add(llvm::createInstructionCombiningPass());
 functionPassMngr->add(llvm::createFlattenCFGPass()); //Flatten the control flow graph.

Затем они могут запускаться

functionPassMngr->run(getLLVMFunc());

Если бы getLLVMFunc вернул бы llvm::Function*, который вы сейчас генерируете. Обратите внимание, что здесь я использую устаревший менеджер проходов, причина в том, что clang использует устаревший менеджер проходов внутри.

person JKRT    schedule 12.12.2018
comment
Это не очень полезно. Если бы вы могли написать пример того, как выполнять некоторые проходы в модуле, это было бы полезно. Есть PassManagers, PassBuilders, PassManagerBuilders, FunctionPassManagers и legacy::FunctionPassManagers. Это все очень запутанно, и я не могу найти какие-либо полезные ресурсы по этому вопросу. Я тоже не очень понимаю Loop pass. - person Indiana Kernick; 13.12.2018
comment
В библиотеках LLVM нет набора стандартных проходов, в Clang есть проходы clang, о которых вы говорите. Другие вещи - это действительно другие вопросы LLVM. - person JKRT; 13.12.2018
comment
Я имею в виду, что определенные проходы для Clang не могут быть запущены напрямую с использованием только LLVM C++ API, вам нужно выяснить эти проходы из вашей конкретной версии clang, а затем добавить их вручную. Один из способов запуска этих проходов — отправить ваш LLVM IR в файл, а затем запустить clang. Тогда вам не нужно было бы писать цикл или вручную перечислять что-либо - person JKRT; 13.12.2018
comment
У меня установлен API Clang C++ с LLVM. Я мог бы найти там где-нибудь оптимизатор LLVM IR и вызвать его напрямую, а не через оболочку. - person Indiana Kernick; 13.12.2018
comment
Да, это возможно. Речь идет только о включении правильных библиотек. llvm.org/docs/WritingAnLLVMPass.html содержит некоторую полезную информацию о пропусках. - person JKRT; 13.12.2018
comment
Чтобы увидеть, что делают разные проходы, см. в чем разница между modulepassmanagers functionpassmanager и b"> stackoverflow.com/questions/50257850/ - person JKRT; 13.12.2018