| // Copyright 2020 The IREE Authors | |
| // | |
| // Licensed 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 | |
| #include "compiler/plugins/target/LLVMCPU/LLVMIRPasses.h" | |
| #include "llvm/Analysis/AliasAnalysis.h" | |
| #include "llvm/IR/IRBuilder.h" | |
| #include "llvm/IR/LegacyPassManager.h" | |
| #include "llvm/IR/Module.h" | |
| #include "llvm/IR/PassManager.h" | |
| #include "llvm/IR/Verifier.h" | |
| #include "llvm/MC/TargetRegistry.h" | |
| #include "llvm/Passes/PassBuilder.h" | |
| #include "llvm/Passes/StandardInstrumentations.h" | |
| #include "llvm/Support/CodeGen.h" | |
| #include "llvm/Support/Error.h" | |
| #include "llvm/Support/raw_ostream.h" | |
| #include "llvm/TargetParser/Host.h" | |
| #include "llvm/Transforms/Instrumentation/AddressSanitizer.h" | |
| #include "llvm/Transforms/Instrumentation/ThreadSanitizer.h" | |
| namespace mlir::iree_compiler::IREE::HAL { | |
| LogicalResult runLLVMIRPasses(const LLVMTarget &target, | |
| llvm::TargetMachine *machine, | |
| llvm::Module *module) { | |
| llvm::LoopAnalysisManager loopAnalysisManager; | |
| llvm::FunctionAnalysisManager functionAnalysisManager; | |
| llvm::CGSCCAnalysisManager cGSCCAnalysisManager; | |
| llvm::ModuleAnalysisManager moduleAnalysisManager; | |
| llvm::PassInstrumentationCallbacks passInstrumentationCallbacks; | |
| llvm::StandardInstrumentations standardInstrumentations( | |
| module->getContext(), | |
| /*DebugLogging=*/false); | |
| standardInstrumentations.registerCallbacks(passInstrumentationCallbacks); | |
| llvm::PassBuilder passBuilder(machine, target.pipelineTuningOptions, {}, | |
| &passInstrumentationCallbacks); | |
| llvm::AAManager aa = passBuilder.buildDefaultAAPipeline(); | |
| functionAnalysisManager.registerPass([&] { return std::move(aa); }); | |
| passBuilder.registerModuleAnalyses(moduleAnalysisManager); | |
| passBuilder.registerCGSCCAnalyses(cGSCCAnalysisManager); | |
| passBuilder.registerFunctionAnalyses(functionAnalysisManager); | |
| passBuilder.registerLoopAnalyses(loopAnalysisManager); | |
| passBuilder.crossRegisterProxies(loopAnalysisManager, functionAnalysisManager, | |
| cGSCCAnalysisManager, moduleAnalysisManager); | |
| switch (target.sanitizerKind) { | |
| case SanitizerKind::kNone: | |
| break; | |
| case SanitizerKind::kAddress: { | |
| passBuilder.registerOptimizerLastEPCallback( | |
| [](llvm::ModulePassManager &modulePassManager, | |
| llvm::OptimizationLevel Level, llvm::ThinOrFullLTOPhase) { | |
| llvm::AddressSanitizerOptions opts; | |
| // Can use Never or Always, just not the default Runtime, which | |
| // introduces a reference to | |
| // __asan_option_detect_stack_use_after_return, causing linker | |
| // errors, and anyway we wouldn't really want bother to with a | |
| // runtime switch for that. | |
| opts.UseAfterReturn = llvm::AsanDetectStackUseAfterReturnMode::Always; | |
| bool moduleUseAfterScope = false; | |
| bool useOdrIndicator = false; | |
| modulePassManager.addPass(llvm::AddressSanitizerPass( | |
| opts, moduleUseAfterScope, useOdrIndicator)); | |
| }); | |
| } break; | |
| case SanitizerKind::kThread: { | |
| passBuilder.registerOptimizerLastEPCallback( | |
| [](llvm::ModulePassManager &modulePassManager, | |
| llvm::OptimizationLevel Level, llvm::ThinOrFullLTOPhase) { | |
| modulePassManager.addPass(llvm::ModuleThreadSanitizerPass()); | |
| modulePassManager.addPass(llvm::createModuleToFunctionPassAdaptor( | |
| llvm::ThreadSanitizerPass())); | |
| }); | |
| } break; | |
| } | |
| if (target.optimizerOptLevel != llvm::OptimizationLevel::O0 || | |
| target.sanitizerKind != SanitizerKind::kNone) { | |
| llvm::ModulePassManager modulePassManager; | |
| modulePassManager = | |
| passBuilder.buildPerModuleDefaultPipeline(target.optimizerOptLevel); | |
| modulePassManager.run(*module, moduleAnalysisManager); | |
| } | |
| if (llvm::verifyModule(*module)) | |
| return failure(); | |
| return success(); | |
| } | |
| LogicalResult runEmitObjFilePasses(llvm::TargetMachine *machine, | |
| llvm::Module *module, | |
| llvm::CodeGenFileType fileType, | |
| std::string *objData) { | |
| llvm::SmallVector<char, 0> stream_buffer; | |
| { | |
| llvm::raw_svector_ostream ostream(stream_buffer); | |
| // TODO(ataei): Use non legacy pass mamanger for this. | |
| llvm::legacy::PassManager passManager; | |
| passManager.add( | |
| new llvm::TargetLibraryInfoWrapperPass(machine->getTargetTriple())); | |
| if (machine->addPassesToEmitFile(passManager, ostream, | |
| /*DwoOut=*/nullptr, fileType)) { | |
| return failure(); | |
| } | |
| passManager.run(*module); | |
| } | |
| // TODO(ataei): This is a work around stream truncation when directly write to | |
| // string. | |
| *objData = std::string(stream_buffer.begin(), stream_buffer.end()); | |
| return success(); | |
| } | |
| } // namespace mlir::iree_compiler::IREE::HAL |