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

Last change on this file since 5706 was 5706, checked in by nmedfort, 21 months ago

First stage of MultiBlockKernel? and pipeline restructuring

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 (Kernel * const k : mPipeline) {
109        k->addKernelDeclarations(iBuilder);
110    }
111    for (Kernel * const k : mPipeline) {
112        k->createInstance(iBuilder);
113    }
114    for (Kernel * const 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.