source: icGREP/icgrep-devel/icgrep/toolchain/toolchain.cpp @ 5444

Last change on this file since 5444 was 5444, checked in by cameron, 2 years ago

VerifyIR option

File size: 17.4 KB
Line 
1/*
2 *  Copyright (c) 2016 International Characters.
3 *  This software is licensed to the public under the Open Software License 3.0.
4 *  icgrep is a trademark of International Characters.
5 */
6
7#include "toolchain.h"
8#include <IR_Gen/idisa_target.h>
9#include <llvm/CodeGen/CommandFlags.h>             // for InitTargetOptionsF...
10#include <llvm/ExecutionEngine/ExecutionEngine.h>  // for EngineBuilder
11#include <llvm/Support/CommandLine.h>              // for OptionCategory
12#include <llvm/Support/TargetSelect.h>             // for InitializeNativeTa...
13#include <llvm/Support/raw_ostream.h>              // for errs, raw_ostream
14#include <llvm/IR/LegacyPassManager.h>             // for PassManager
15#include <llvm/IR/IRPrintingPasses.h>
16#include <llvm/InitializePasses.h>                 // for initializeCodeGen
17#include <llvm/PassRegistry.h>                     // for PassRegistry
18#include <llvm/Support/CodeGen.h>                  // for Level, Level::None
19#include <llvm/Support/Compiler.h>                 // for LLVM_UNLIKELY
20#include <llvm/Target/TargetMachine.h>             // for TargetMachine, Tar...
21#include <llvm/Target/TargetOptions.h>             // for TargetOptions
22#include <llvm/Transforms/Scalar.h>
23#include <llvm/Transforms/Utils/Local.h>
24#include <llvm/IR/Module.h>
25#include <toolchain/object_cache.h>
26#include <toolchain/pipeline.h>
27#include <kernels/kernel_builder.h>
28#include <kernels/kernel.h>
29#include <sys/stat.h>
30#include <thread>
31#include <boost/lockfree/queue.hpp>
32#include <llvm/IR/Verifier.h>
33
34using namespace llvm;
35using namespace parabix;
36
37namespace codegen {
38
39static cl::OptionCategory CodeGenOptions("Code Generation Options", "These options control code generation.");
40
41static cl::bits<DebugFlags>
42DebugOptions(cl::values(clEnumVal(ShowIR, "Print generated LLVM IR."),
43                        clEnumVal(VerifyIR, "Run the IR verification pass."),
44#ifndef USE_LLVM_3_6
45                        clEnumVal(ShowASM, "Print assembly code."),
46#endif
47                        clEnumVal(SerializeThreads, "Force segment threads to run sequentially."),
48                        clEnumValEnd), cl::cat(CodeGenOptions));
49
50static cl::opt<std::string> IROutputFilename("dump-generated-IR-output", cl::init(""), cl::desc("output IR filename"), cl::cat(CodeGenOptions));
51#ifndef USE_LLVM_3_6
52static cl::opt<std::string> ASMOutputFilename("asm-output", cl::init(""), cl::desc("output ASM filename"), cl::cat(CodeGenOptions));
53static cl::opt<bool> AsmVerbose("asm-verbose",
54                                cl::desc("Add comments to directives."),
55                                cl::init(true), cl::cat(CodeGenOptions));
56#endif
57
58char OptLevel;
59static cl::opt<char, true> OptLevelOption("O", cl::desc("Optimization level. [-O0, -O1, -O2, or -O3] (default = '-O1')"), cl::location(OptLevel),
60                              cl::cat(CodeGenOptions), cl::Prefix, cl::ZeroOrMore, cl::init('1'));
61
62
63static cl::opt<bool> EnableObjectCache("enable-object-cache", cl::init(true), cl::desc("Enable object caching"), cl::cat(CodeGenOptions));
64
65static cl::opt<std::string> ObjectCacheDir("object-cache-dir", cl::init(""), cl::desc("Path to the object cache diretory"), cl::cat(CodeGenOptions));
66
67
68int BlockSize;
69int SegmentSize;
70int BufferSegments;
71int ThreadNum;
72bool EnableAsserts;
73bool EnableCycleCounter;
74#ifndef NDEBUG
75#define IN_DEBUG_MODE true
76#else
77#define IN_DEBUG_MODE false
78#endif
79
80static cl::opt<int, true> BlockSizeOption("BlockSize", cl::location(BlockSize), cl::init(0), cl::desc("specify a block size (defaults to widest SIMD register width in bits)."), cl::cat(CodeGenOptions));
81static cl::opt<int, true> SegmentSizeOption("segment-size", cl::location(SegmentSize), cl::desc("Segment Size"), cl::value_desc("positive integer"), cl::init(1));
82static cl::opt<int, true> BufferSegmentsOption("buffer-segments", cl::location(BufferSegments), cl::desc("Buffer Segments"), cl::value_desc("positive integer"), cl::init(1));
83static cl::opt<int, true> ThreadNumOption("thread-num", cl::location(ThreadNum), cl::desc("Number of threads used for segment pipeline parallel"), cl::value_desc("positive integer"), cl::init(2));
84static cl::opt<bool, true> EnableAssertsOption("ea", cl::location(EnableAsserts), cl::desc("Enable Asserts"), cl::init(IN_DEBUG_MODE));
85static cl::opt<bool, true> EnableCycleCountOption("ShowKernelCycles", cl::location(EnableCycleCounter), cl::desc("Count and report CPU cycles per kernel"), cl::init(false), cl::cat(CodeGenOptions));
86
87const cl::OptionCategory * codegen_flags() {return &CodeGenOptions;}
88
89bool DebugOptionIsSet(DebugFlags flag) {return DebugOptions.isSet(flag);}
90
91static cl::opt<bool> pipelineParallel("enable-pipeline-parallel", cl::desc("Enable multithreading with pipeline parallelism."), cl::cat(CodeGenOptions));
92   
93static cl::opt<bool> segmentPipelineParallel("enable-segment-pipeline-parallel", cl::desc("Enable multithreading with segment pipeline parallelism."), cl::cat(CodeGenOptions));
94
95}
96
97void printParabixVersion () {
98    raw_ostream &OS = outs();
99    OS << "Parabix (http://parabix.costar.sfu.ca/):\n  " << "Parabix revision " << PARABIX_VERSION << "\n";
100}
101
102void AddParabixVersionPrinter() {
103    cl::AddExtraVersionPrinter(&printParabixVersion);
104}
105
106void setAllFeatures(EngineBuilder &builder) {
107    StringMap<bool> HostCPUFeatures;
108    if (sys::getHostCPUFeatures(HostCPUFeatures)) {
109        std::vector<std::string> attrs;
110        for (auto &flag : HostCPUFeatures) {
111            auto enabled = flag.second ? "+" : "-";
112            attrs.push_back(enabled + flag.first().str());
113        }
114        builder.setMAttrs(attrs);
115    }
116}
117
118bool AVX2_available() {
119    StringMap<bool> HostCPUFeatures;
120    if (sys::getHostCPUFeatures(HostCPUFeatures)) {
121        auto f = HostCPUFeatures.find("avx2");
122        return ((f != HostCPUFeatures.end()) && f->second);
123    }
124    return false;
125}
126
127ParabixDriver::ParabixDriver(std::string && moduleName)
128: mContext(new llvm::LLVMContext())
129, mMainModule(new Module(moduleName, *mContext))
130, iBuilder(nullptr)
131, mTarget(nullptr)
132, mEngine(nullptr)
133, mCache(nullptr) {
134
135    InitializeNativeTarget();
136    InitializeNativeTargetAsmPrinter();
137    InitializeNativeTargetAsmParser();
138
139    PassRegistry * Registry = PassRegistry::getPassRegistry();
140    initializeCore(*Registry);
141    initializeCodeGen(*Registry);
142    initializeLowerIntrinsicsPass(*Registry);
143
144    std::string errMessage;
145    EngineBuilder builder{std::unique_ptr<Module>(mMainModule)};
146    builder.setErrorStr(&errMessage);
147    TargetOptions opts = InitTargetOptionsFromCodeGenFlags();
148    opts.MCOptions.AsmVerbose = codegen::AsmVerbose;
149
150    builder.setTargetOptions(opts);
151    builder.setVerifyModules(false);
152    CodeGenOpt::Level optLevel = CodeGenOpt::Level::None;
153    switch (codegen::OptLevel) {
154        case '0': optLevel = CodeGenOpt::None; break;
155        case '1': optLevel = CodeGenOpt::Less; break;
156        case '2': optLevel = CodeGenOpt::Default; break;
157        case '3': optLevel = CodeGenOpt::Aggressive; break;
158        default: errs() << codegen::OptLevel << " is an invalid optimization level.\n";
159    }
160    builder.setOptLevel(optLevel);
161
162    setAllFeatures(builder);
163    mEngine = builder.create();
164    if (mEngine == nullptr) {
165        throw std::runtime_error("Could not create ExecutionEngine: " + errMessage);
166    }
167    mTarget = builder.selectTarget();
168    if (LLVM_LIKELY(codegen::EnableObjectCache && codegen::DebugOptions.getBits() == 0)) {
169        if (codegen::ObjectCacheDir.empty()) {
170            mCache = new ParabixObjectCache();
171        } else {
172            mCache = new ParabixObjectCache(codegen::ObjectCacheDir);
173        }
174        assert (mCache);
175        mEngine->setObjectCache(mCache);
176    }
177
178    mMainModule->setTargetTriple(mTarget->getTargetTriple().getTriple());
179
180    iBuilder.reset(IDISA::GetIDISA_Builder(mMainModule));
181    iBuilder->setDriver(this);
182    iBuilder->setModule(mMainModule);
183}
184
185ExternalBuffer * ParabixDriver::addExternalBuffer(std::unique_ptr<ExternalBuffer> b) {
186    mOwnedBuffers.emplace_back(std::move(b));
187    return cast<ExternalBuffer>(mOwnedBuffers.back().get());
188}
189
190StreamSetBuffer * ParabixDriver::addBuffer(std::unique_ptr<StreamSetBuffer> b) {
191    b->allocateBuffer(iBuilder);
192    mOwnedBuffers.emplace_back(std::move(b));
193    return mOwnedBuffers.back().get();
194}
195
196kernel::Kernel * ParabixDriver::addKernelInstance(std::unique_ptr<kernel::Kernel> kb) {
197    mOwnedKernels.emplace_back(std::move(kb));
198    return mOwnedKernels.back().get();
199}
200
201void ParabixDriver::addKernelCall(kernel::Kernel & kb, const std::vector<StreamSetBuffer *> & inputs, const std::vector<StreamSetBuffer *> & outputs) {
202    assert ("addKernelCall or makeKernelCall was already run on this kernel." && (kb.getModule() == nullptr));
203    mPipeline.emplace_back(&kb);
204    kb.createKernelStub(iBuilder, inputs, outputs);
205}
206
207void ParabixDriver::makeKernelCall(kernel::Kernel * kb, const std::vector<StreamSetBuffer *> & inputs, const std::vector<StreamSetBuffer *> & outputs) {
208    assert ("addKernelCall or makeKernelCall was already run on this kernel." && (kb->getModule() == nullptr));
209    mPipeline.emplace_back(kb);   
210    kb->createKernelStub(iBuilder, inputs, outputs);
211}
212
213void ParabixDriver::generatePipelineIR() {
214    #ifndef NDEBUG
215    if (LLVM_UNLIKELY(mPipeline.empty())) {
216        report_fatal_error("Pipeline cannot be empty");
217    } else {
218        for (auto i = mPipeline.begin(); i != mPipeline.end(); ++i) {
219            for (auto j = i; ++j != mPipeline.end(); ) {
220                if (LLVM_UNLIKELY(*i == *j)) {
221                    report_fatal_error("Kernel instances cannot occur twice in the pipeline");
222                }
223            }
224        }
225    }
226    #endif
227    // note: instantiation of all kernels must occur prior to initialization
228    for (const auto & k : mPipeline) {
229        k->addKernelDeclarations(iBuilder);
230    }
231    for (const auto & k : mPipeline) {
232        k->createInstance(iBuilder);
233    }
234    for (const auto & k : mPipeline) {
235        k->initializeInstance(iBuilder);
236    }
237    if (codegen::pipelineParallel) {
238        generateParallelPipeline(iBuilder, mPipeline);
239    } else if (codegen::segmentPipelineParallel) {
240        generateSegmentParallelPipeline(iBuilder, mPipeline);
241    } else {
242        codegen::ThreadNum = 1;
243        generatePipelineLoop(iBuilder, mPipeline);
244    }
245    for (const auto & k : mPipeline) {
246        k->finalizeInstance(iBuilder);
247    }
248}
249
250Function * ParabixDriver::LinkFunction(Module * mod, llvm::StringRef name, FunctionType * type, void * functionPtr) const {
251    assert ("addKernelCall or makeKernelCall must be called before LinkFunction" && (mod != nullptr));
252    Function * f = cast<Function>(mod->getOrInsertFunction(name, type));
253    mEngine->addGlobalMapping(f, functionPtr);
254    return f;
255}
256
257void ParabixDriver::linkAndFinalize() {
258    Module * module = nullptr;
259    try {
260
261        legacy::PassManager PM;
262#ifndef NDEBUG
263        PM.add(createVerifierPass());
264#else
265        if (LLVM_UNLIKELY(codegen::DebugOptionIsSet(codegen::VerifyIR))) {
266            PM.add(createVerifierPass());
267        }
268#endif
269        PM.add(createPromoteMemoryToRegisterPass()); //Force the use of mem2reg to promote stack variables.
270        PM.add(createReassociatePass());             //Reassociate expressions.
271        PM.add(createGVNPass());                     //Eliminate common subexpressions.
272        PM.add(createInstructionCombiningPass());    //Simple peephole optimizations and bit-twiddling.
273        PM.add(createCFGSimplificationPass());
274
275        std::unique_ptr<raw_fd_ostream> IROutputStream(nullptr);
276        if (LLVM_UNLIKELY(codegen::DebugOptionIsSet(codegen::ShowIR))) {
277            if (codegen::IROutputFilename.empty()) {
278                IROutputStream.reset(new raw_fd_ostream(STDERR_FILENO, false, false));
279            } else {
280                std::error_code error;
281                IROutputStream.reset(new raw_fd_ostream(codegen::IROutputFilename, error, sys::fs::OpenFlags::F_None));
282            }
283            PM.add(createPrintModulePass(*IROutputStream));
284        }
285
286        #ifndef USE_LLVM_3_6
287        std::unique_ptr<raw_fd_ostream> ASMOutputStream(nullptr);
288        if (LLVM_UNLIKELY(codegen::DebugOptionIsSet(codegen::ShowASM))) {
289            if (codegen::ASMOutputFilename.empty()) {
290                ASMOutputStream.reset(new raw_fd_ostream(STDERR_FILENO, false, false));
291            } else {
292                std::error_code error;
293                ASMOutputStream.reset(new raw_fd_ostream(codegen::ASMOutputFilename, error, sys::fs::OpenFlags::F_None));
294            }
295            if (LLVM_UNLIKELY(mTarget->addPassesToEmitFile(PM, *ASMOutputStream, TargetMachine::CGFT_AssemblyFile))) {
296                report_fatal_error("LLVM error: could not add emit assembly pass");
297            }
298        }
299        #endif
300
301        for (kernel::Kernel * const kernel : mPipeline) {
302            iBuilder->setKernel(kernel);
303            module = kernel->getModule();
304            bool uncachedObject = true;
305            if (mCache && mCache->loadCachedObjectFile(iBuilder, kernel)) {
306                uncachedObject = false;
307            }
308            if (uncachedObject) {
309                module->setTargetTriple(mMainModule->getTargetTriple());
310                kernel->generateKernel(iBuilder);
311                PM.run(*module);
312            }
313            mEngine->addModule(std::unique_ptr<Module>(module));
314        }
315
316        iBuilder->setKernel(nullptr);
317        module = mMainModule;
318        PM.run(*module);
319
320        mEngine->finalizeObject();
321
322    } catch (...) {
323        module->dump();
324        report_fatal_error("LLVM error: link or finalize.");
325    }
326}
327
328const std::unique_ptr<kernel::KernelBuilder> & ParabixDriver::getBuilder() {
329    return iBuilder;
330}
331
332void * ParabixDriver::getPointerToMain() {
333    return mEngine->getPointerToNamedFunction("Main");
334}
335
336ParabixDriver::~ParabixDriver() {
337    delete mCache;
338}
339
340
341//void ParabixDriver::linkAndFinalize() {
342
343//    using KernelQueue = boost::lockfree::queue<kernel::KernelBuilder *>;
344
345//    legacy::PassManager PM;
346//    #ifndef NDEBUG
347//    PM.add(createVerifierPass());
348//    #endif
349//    PM.add(createPromoteMemoryToRegisterPass()); //Force the use of mem2reg to promote stack variables.
350//    PM.add(createReassociatePass());             //Reassociate expressions.
351//    PM.add(createGVNPass());                     //Eliminate common subexpressions.
352//    PM.add(createInstructionCombiningPass());    //Simple peephole optimizations and bit-twiddling.
353//    PM.add(createCFGSimplificationPass());
354
355//    raw_fd_ostream * IROutputStream = nullptr;
356//    if (LLVM_UNLIKELY(codegen::DebugOptionIsSet(codegen::ShowIR))) {
357//        if (codegen::IROutputFilename.empty()) {
358//            IROutputStream = new raw_fd_ostream(STDERR_FILENO, false, false);
359//        } else {
360//            std::error_code error;
361//            IROutputStream = new raw_fd_ostream(codegen::IROutputFilename, error, sys::fs::OpenFlags::F_None);
362//        }
363//        PM.add(createPrintModulePass(*IROutputStream));
364//        codegen::Jobs = 1; // TODO: set Jobs to 1 for now; these should be updated to pipe to a temporary buffer when Jobs > 1
365//    }
366
367//    #ifndef USE_LLVM_3_6
368//    raw_fd_ostream * ASMOutputStream = nullptr;
369//    if (LLVM_UNLIKELY(codegen::DebugOptionIsSet(codegen::ShowASM))) {
370//        if (codegen::ASMOutputFilename.empty()) {
371//            ASMOutputStream = new raw_fd_ostream(STDERR_FILENO, false, false);
372//        } else {
373//            std::error_code error;
374//            ASMOutputStream = new raw_fd_ostream(codegen::ASMOutputFilename, error, sys::fs::OpenFlags::F_None);
375//        }
376//        if (LLVM_UNLIKELY(mTarget->addPassesToEmitFile(PM, *ASMOutputStream, TargetMachine::CGFT_AssemblyFile))) {
377//            report_fatal_error("LLVM error: could not add emit assembly pass");
378//        }
379//        codegen::Jobs = 1; // TODO: set Jobs to 1 for now; these should be updated to pipe to a temporary buffer when Jobs > 1
380//    }
381//    #endif
382
383//    KernelQueue Q(mPipeline.size() + 1);
384//    for (kernel::KernelBuilder * kb : mPipeline) {
385//        assert (kb);
386//        Q.unsynchronized_push(kb);
387//    }
388
389//    std::thread compilation_thread[codegen::Jobs];
390//    for (int i = 0; i < codegen::Jobs; ++i) {
391//        compilation_thread[i] = std::thread([&]{
392//            kernel::KernelBuilder * kb = nullptr;
393//            Module * m = nullptr;
394//            try {
395//                while (Q.pop(kb)) {
396//                    m = kb->getModule();
397//                    bool uncachedObject = true;
398//                    if (mCache && mCache->loadCachedObjectFile(kb)) {
399//                        uncachedObject = false;
400//                    }
401//                    if (uncachedObject) {
402//                        Module * const cm = iBuilder->getModule();
403//                        iBuilder->setModule(m);
404//                        kb->generateKernel();
405//                        PM.run(*m);
406//                        iBuilder->setModule(cm);
407//                    }
408//                    mEngine->addModule(std::unique_ptr<Module>(m));
409//                }
410//            } catch (...) {
411//                // clear the queue
412//                while (Q.pop(kb));
413//                // dump the result the module to the console
414//                if (m) m->dump();
415//                throw;
416//            }
417//        });
418//    }
419
420//    PM.run(*mMainModule);
421//    for (int i = 0; i < codegen::Jobs; ++i) {
422//        compilation_thread[i].join();
423//    }
424//    mEngine->finalizeObject();
425
426//    delete IROutputStream;
427//    #ifndef USE_LLVM_3_6
428//    delete ASMOutputStream;
429//    #endif
430
431//}
432
433
434//            std::unique_ptr<IDISA::IDISA_Builder> idb(IDISA::GetIDISA_Builder(kb->getModule()));
435//            idb->setDriver(this);
436//            kb->setBuilder(idb.get());
Note: See TracBrowser for help on using the repository browser.