source: icGREP/icgrep-devel/icgrep/toolchain/cpudriver.cpp @ 5646

Last change on this file since 5646 was 5646, checked in by nmedfort, 22 months ago

Minor clean up. Bug fix for object cache when the same cached kernel is used twice in a single run. Improvement to RE Minimizer.

File size: 9.0 KB
Line 
1#include "cpudriver.h"
2
3#include <IR_Gen/idisa_target.h>
4#include <llvm/ExecutionEngine/ExecutionEngine.h>  // for EngineBuilder
5#include <llvm/IR/LegacyPassManager.h>             // for PassManager
6#include <llvm/IR/IRPrintingPasses.h>
7#include <llvm/InitializePasses.h>                 // for initializeCodeGen
8#include <llvm/PassRegistry.h>                     // for PassRegistry
9#include <llvm/Support/CodeGen.h>                  // for Level, Level::None
10#include <llvm/Support/Compiler.h>                 // for LLVM_UNLIKELY
11#include <llvm/Support/TargetSelect.h>
12#include <llvm/Target/TargetMachine.h>             // for TargetMachine, Tar...
13#include <llvm/Target/TargetOptions.h>             // for TargetOptions
14#include <llvm/Transforms/Scalar.h>
15#include <llvm/Transforms/Utils/Local.h>
16#include <toolchain/object_cache.h>
17#include <toolchain/toolchain.h>
18#include <toolchain/pipeline.h>
19#include <kernels/kernel_builder.h>
20#include <kernels/kernel.h>
21#include <llvm/IR/Verifier.h>
22
23#ifndef NDEBUG
24#define IN_DEBUG_MODE true
25#else
26#define IN_DEBUG_MODE false
27#endif
28
29using namespace llvm;
30using Kernel = kernel::Kernel;
31using StreamSetBuffer = parabix::StreamSetBuffer;
32using KernelBuilder = kernel::KernelBuilder;
33
34ParabixDriver::ParabixDriver(std::string && moduleName)
35: Driver(std::move(moduleName))
36, mTarget(nullptr)
37, mEngine(nullptr)
38, mCache(nullptr)
39, mIROutputStream(nullptr)
40, mASMOutputStream(nullptr) {
41
42    InitializeNativeTarget();
43    InitializeNativeTargetAsmPrinter();
44    InitializeNativeTargetAsmParser();
45
46    PassRegistry * Registry = PassRegistry::getPassRegistry();
47    initializeCore(*Registry);
48    initializeCodeGen(*Registry);
49    initializeLowerIntrinsicsPass(*Registry);
50
51    std::string errMessage;
52    EngineBuilder builder{std::unique_ptr<Module>(mMainModule)};
53    builder.setErrorStr(&errMessage);
54    builder.setUseOrcMCJITReplacement(true);
55    builder.setTargetOptions(codegen::Options);
56    builder.setVerifyModules(false);
57    builder.setOptLevel(codegen::OptLevel);
58
59    StringMap<bool> HostCPUFeatures;
60    if (sys::getHostCPUFeatures(HostCPUFeatures)) {
61        std::vector<std::string> attrs;
62        for (auto &flag : HostCPUFeatures) {
63            auto enabled = flag.second ? "+" : "-";
64            attrs.push_back(enabled + flag.first().str());
65        }
66        builder.setMAttrs(attrs);
67    }
68
69    mEngine = builder.create();
70    if (mEngine == nullptr) {
71        throw std::runtime_error("Could not create ExecutionEngine: " + errMessage);
72    }
73    mTarget = builder.selectTarget();   
74    if (LLVM_LIKELY(codegen::EnableObjectCache)) {
75        if (codegen::ObjectCacheDir) {
76            mCache = new ParabixObjectCache(codegen::ObjectCacheDir);
77        } else {
78            mCache = new ParabixObjectCache();
79        }
80        mEngine->setObjectCache(mCache);
81    }
82    mMainModule->setTargetTriple(mTarget->getTargetTriple().getTriple());
83    iBuilder.reset(IDISA::GetIDISA_Builder(*mContext));
84    iBuilder->setDriver(this);
85    iBuilder->setModule(mMainModule);
86}
87
88void ParabixDriver::makeKernelCall(Kernel * kernel, const std::vector<StreamSetBuffer *> & inputs, const std::vector<StreamSetBuffer *> & outputs) {
89    assert ("addKernelCall or makeKernelCall was already run on this kernel." && (kernel->getModule() == nullptr));
90    mPipeline.emplace_back(kernel);
91    kernel->bindPorts(inputs, outputs);
92    if (!mCache || !mCache->loadCachedObjectFile(iBuilder, kernel)) {
93        mUncachedKernel.push_back(kernel);
94    }
95    if (kernel->getModule() == nullptr) {
96        kernel->makeModule(iBuilder);
97    }
98    assert (kernel->getModule());
99}
100
101void ParabixDriver::generatePipelineIR() {
102
103    for (Kernel * const kernel : mUncachedKernel) {
104        kernel->prepareKernel(iBuilder);
105    }
106
107    // note: instantiation of all kernels must occur prior to initialization
108    for (const auto & k : mPipeline) {
109        k->addKernelDeclarations(iBuilder);
110    }
111    for (const auto & k : mPipeline) {
112        k->createInstance(iBuilder);
113    }
114    for (const auto & k : mPipeline) {
115        k->initializeInstance(iBuilder);
116    }
117    if (codegen::PipelineParallel) {
118        generateParallelPipeline(iBuilder, mPipeline);
119    } else if (codegen::SegmentPipelineParallel) {
120        generateSegmentParallelPipeline(iBuilder, mPipeline);
121    } else {
122        generatePipelineLoop(iBuilder, mPipeline);
123    }
124    for (const auto & k : mPipeline) {
125        k->finalizeInstance(iBuilder);
126    }
127}
128
129Function * ParabixDriver::addLinkFunction(Module * mod, llvm::StringRef name, FunctionType * type, void * functionPtr) const {
130    if (LLVM_UNLIKELY(mod == nullptr)) {
131        report_fatal_error("addLinkFunction(" + name + ") cannot be called until after addKernelCall or makeKernelCall");
132    }
133    Function * f = mod->getFunction(name);
134    if (LLVM_UNLIKELY(f == nullptr)) {
135        f = Function::Create(type, Function::ExternalLinkage, name, mod);
136        mEngine->updateGlobalMapping(f, functionPtr);
137    } else if (LLVM_UNLIKELY(f->getType() != type->getPointerTo())) {
138        report_fatal_error("Cannot link " + name + ": a function with a different signature already exists with that name in " + mod->getName());
139    }
140    return f;
141}
142
143void ParabixDriver::finalizeObject() {
144
145    legacy::PassManager PM;
146    if (LLVM_UNLIKELY(codegen::DebugOptionIsSet(codegen::ShowUnoptimizedIR))) {
147        if (LLVM_LIKELY(mIROutputStream == nullptr)) {
148            if (codegen::IROutputFilename) {
149                std::error_code error;
150                mIROutputStream = new raw_fd_ostream(codegen::IROutputFilename, error, sys::fs::OpenFlags::F_None);
151            } else {
152                mIROutputStream = new raw_fd_ostream(STDERR_FILENO, false, true);
153            }
154        }
155        PM.add(createPrintModulePass(*mIROutputStream));
156    }
157    if (IN_DEBUG_MODE || LLVM_UNLIKELY(codegen::DebugOptionIsSet(codegen::VerifyIR))) {
158        PM.add(createVerifierPass());
159    }
160    PM.add(createPromoteMemoryToRegisterPass());    // Promote stack variables to constants or PHI nodes
161    PM.add(createCFGSimplificationPass());          // Remove dead basic blocks and unnecessary branch statements / phi nodes
162    PM.add(createEarlyCSEPass());                   // Simple common subexpression elimination pass
163    PM.add(createInstructionCombiningPass());       // Simple peephole optimizations and bit-twiddling.
164    PM.add(createReassociatePass());                // Canonicalizes commutative expressions
165    PM.add(createGVNPass());                        // Global value numbering redundant expression elimination pass
166    PM.add(createCFGSimplificationPass());          // Repeat CFG Simplification to "clean up" any newly found redundant phi nodes
167
168    if (LLVM_UNLIKELY(codegen::DebugOptionIsSet(codegen::ShowIR))) {
169        if (LLVM_LIKELY(mIROutputStream == nullptr)) {
170            if (codegen::IROutputFilename) {
171                std::error_code error;
172                mIROutputStream = new raw_fd_ostream(codegen::IROutputFilename, error, sys::fs::OpenFlags::F_None);
173            } else {
174                mIROutputStream = new raw_fd_ostream(STDERR_FILENO, false, true);
175            }
176        }
177        PM.add(createPrintModulePass(*mIROutputStream));
178    }
179
180    #ifndef USE_LLVM_3_6
181    if (LLVM_UNLIKELY(codegen::DebugOptionIsSet(codegen::ShowASM))) {
182        if (codegen::ASMOutputFilename) {
183            std::error_code error;
184            mASMOutputStream = new raw_fd_ostream(codegen::ASMOutputFilename, error, sys::fs::OpenFlags::F_None);
185        } else {
186            mASMOutputStream = new raw_fd_ostream(STDERR_FILENO, false, true);
187        }
188        if (LLVM_UNLIKELY(mTarget->addPassesToEmitFile(PM, *mASMOutputStream, TargetMachine::CGFT_AssemblyFile))) {
189            report_fatal_error("LLVM error: could not add emit assembly pass");
190        }
191    }
192    #endif
193
194    Module * module = nullptr;
195    try {
196        for (Kernel * const kernel : mUncachedKernel) {
197            iBuilder->setKernel(kernel);
198            kernel->generateKernel(iBuilder);
199            module = kernel->getModule(); assert (module);
200            module->setTargetTriple(mMainModule->getTargetTriple());
201            PM.run(*module);
202        }
203        module = mMainModule;
204        iBuilder->setKernel(nullptr);
205        PM.run(*mMainModule);
206        for (Kernel * const kernel : mPipeline) {
207            if (LLVM_UNLIKELY(kernel->getModule() == nullptr)) {
208                report_fatal_error(kernel->getName() + " was neither loaded from cache nor generated prior to finalizeObject");
209            }
210            mEngine->addModule(std::unique_ptr<Module>(kernel->getModule()));
211        }
212        mEngine->finalizeObject();
213        if (mCache) mCache->cleanUpObjectCacheFiles();
214    } catch (const std::exception & e) {
215        report_fatal_error(module->getName() + ": " + e.what());
216    }
217
218}
219
220bool ParabixDriver::hasExternalFunction(llvm::StringRef functionName) const {
221    return mEngine->getPointerToNamedFunction(functionName, false) != nullptr;
222}
223
224void * ParabixDriver::getMain() {
225    return mEngine->getPointerToNamedFunction("Main");
226}
227
228ParabixDriver::~ParabixDriver() {
229    delete mEngine;
230    delete mCache;
231    delete mTarget;
232    delete mIROutputStream;
233    delete mASMOutputStream;
234}
Note: See TracBrowser for help on using the repository browser.