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

Last change on this file since 5630 was 5630, checked in by nmedfort, 23 months ago

Partial check-in for avoidance of compiling Pablo/LLVM code to determine the Kernel struct type when using a cached object. Inactive RE alternation minimization check in.

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