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

Last change on this file since 5446 was 5446, checked in by nmedfort, 2 years ago

Refactoring work + correction for getRawItemPointer

File size: 16.1 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
37using Kernel = kernel::Kernel;
38using KernelBuilder = kernel::KernelBuilder;
39
40#ifndef NDEBUG
41#define IN_DEBUG_MODE true
42#else
43#define IN_DEBUG_MODE false
44#endif
45
46namespace codegen {
47
48static cl::OptionCategory CodeGenOptions("Code Generation Options", "These options control code generation.");
49
50static cl::bits<DebugFlags>
51DebugOptions(cl::values(clEnumVal(ShowIR, "Print generated LLVM IR."),
52                        clEnumVal(VerifyIR, "Run the IR verification pass."),
53#ifndef USE_LLVM_3_6
54                        clEnumVal(ShowASM, "Print assembly code."),
55#endif
56                        clEnumVal(SerializeThreads, "Force segment threads to run sequentially."),
57                        clEnumValEnd), cl::cat(CodeGenOptions));
58
59static cl::opt<std::string> IROutputFilename("dump-generated-IR-output", cl::init(""), cl::desc("output IR filename"), cl::cat(CodeGenOptions));
60#ifndef USE_LLVM_3_6
61static cl::opt<std::string> ASMOutputFilename("asm-output", cl::init(""), cl::desc("output ASM filename"), cl::cat(CodeGenOptions));
62static cl::opt<bool> AsmVerbose("asm-verbose",
63                                cl::desc("Add comments to directives."),
64                                cl::init(true), cl::cat(CodeGenOptions));
65#endif
66
67char OptLevel;
68static cl::opt<char, true> OptLevelOption("O", cl::desc("Optimization level. [-O0, -O1, -O2, or -O3] (default = '-O1')"), cl::location(OptLevel),
69                              cl::cat(CodeGenOptions), cl::Prefix, cl::ZeroOrMore, cl::init('1'));
70
71
72static cl::opt<bool> EnableObjectCache("enable-object-cache", cl::init(true), cl::desc("Enable object caching"), cl::cat(CodeGenOptions));
73
74static cl::opt<std::string> ObjectCacheDir("object-cache-dir", cl::init(""), cl::desc("Path to the object cache diretory"), cl::cat(CodeGenOptions));
75
76
77int BlockSize;
78int SegmentSize;
79int BufferSegments;
80int ThreadNum;
81bool EnableAsserts;
82bool EnableCycleCounter;
83
84static 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));
85static cl::opt<int, true> SegmentSizeOption("segment-size", cl::location(SegmentSize), cl::desc("Segment Size"), cl::value_desc("positive integer"), cl::init(1));
86static cl::opt<int, true> BufferSegmentsOption("buffer-segments", cl::location(BufferSegments), cl::desc("Buffer Segments"), cl::value_desc("positive integer"), cl::init(1));
87static 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));
88static cl::opt<bool, true> EnableAssertsOption("ea", cl::location(EnableAsserts), cl::desc("Enable Asserts"), cl::init(IN_DEBUG_MODE));
89static cl::opt<bool, true> EnableCycleCountOption("ShowKernelCycles", cl::location(EnableCycleCounter), cl::desc("Count and report CPU cycles per kernel"), cl::init(false), cl::cat(CodeGenOptions));
90
91const cl::OptionCategory * codegen_flags() {return &CodeGenOptions;}
92
93bool DebugOptionIsSet(DebugFlags flag) {return DebugOptions.isSet(flag);}
94
95static cl::opt<bool> pipelineParallel("enable-pipeline-parallel", cl::desc("Enable multithreading with pipeline parallelism."), cl::cat(CodeGenOptions));
96   
97static cl::opt<bool> segmentPipelineParallel("enable-segment-pipeline-parallel", cl::desc("Enable multithreading with segment pipeline parallelism."), cl::cat(CodeGenOptions));
98
99}
100
101void printParabixVersion () {
102    raw_ostream &OS = outs();
103    OS << "Parabix (http://parabix.costar.sfu.ca/):\n  " << "Parabix revision " << PARABIX_VERSION << "\n";
104}
105
106void AddParabixVersionPrinter() {
107    cl::AddExtraVersionPrinter(&printParabixVersion);
108}
109
110void setAllFeatures(EngineBuilder &builder) {
111    StringMap<bool> HostCPUFeatures;
112    if (sys::getHostCPUFeatures(HostCPUFeatures)) {
113        std::vector<std::string> attrs;
114        for (auto &flag : HostCPUFeatures) {
115            auto enabled = flag.second ? "+" : "-";
116            attrs.push_back(enabled + flag.first().str());
117        }
118        builder.setMAttrs(attrs);
119    }
120}
121
122bool AVX2_available() {
123    StringMap<bool> HostCPUFeatures;
124    if (sys::getHostCPUFeatures(HostCPUFeatures)) {
125        auto f = HostCPUFeatures.find("avx2");
126        return ((f != HostCPUFeatures.end()) && f->second);
127    }
128    return false;
129}
130
131ParabixDriver::ParabixDriver(std::string && moduleName)
132: mContext(new llvm::LLVMContext())
133, mMainModule(new Module(moduleName, *mContext))
134, iBuilder(nullptr)
135, mTarget(nullptr)
136, mEngine(nullptr)
137, mCache(nullptr) {
138
139    InitializeNativeTarget();
140    InitializeNativeTargetAsmPrinter();
141    InitializeNativeTargetAsmParser();
142
143    PassRegistry * Registry = PassRegistry::getPassRegistry();
144    initializeCore(*Registry);
145    initializeCodeGen(*Registry);
146    initializeLowerIntrinsicsPass(*Registry);
147
148    std::string errMessage;
149    EngineBuilder builder{std::unique_ptr<Module>(mMainModule)};
150    builder.setErrorStr(&errMessage);
151    TargetOptions opts = InitTargetOptionsFromCodeGenFlags();
152    opts.MCOptions.AsmVerbose = codegen::AsmVerbose;
153    builder.setTargetOptions(opts);
154    builder.setVerifyModules(IN_DEBUG_MODE || codegen::DebugOptionIsSet(codegen::VerifyIR));
155    CodeGenOpt::Level optLevel = CodeGenOpt::Level::None;
156    switch (codegen::OptLevel) {
157        case '0': optLevel = CodeGenOpt::None; break;
158        case '1': optLevel = CodeGenOpt::Less; break;
159        case '2': optLevel = CodeGenOpt::Default; break;
160        case '3': optLevel = CodeGenOpt::Aggressive; break;
161        default: errs() << codegen::OptLevel << " is an invalid optimization level.\n";
162    }
163    builder.setOptLevel(optLevel);
164    setAllFeatures(builder);
165    mEngine = builder.create();
166    if (mEngine == nullptr) {
167        throw std::runtime_error("Could not create ExecutionEngine: " + errMessage);
168    }
169    mTarget = builder.selectTarget();
170    if (LLVM_LIKELY(codegen::EnableObjectCache && codegen::DebugOptions.getBits() == 0)) {
171        if (codegen::ObjectCacheDir.empty()) {
172            mCache = new ParabixObjectCache();
173        } else {
174            mCache = new ParabixObjectCache(codegen::ObjectCacheDir);
175        }
176        assert (mCache);
177        mEngine->setObjectCache(mCache);
178    }
179
180    mMainModule->setTargetTriple(mTarget->getTargetTriple().getTriple());
181
182    iBuilder.reset(IDISA::GetIDISA_Builder(*mContext, mMainModule->getTargetTriple()));
183    iBuilder->setDriver(this);
184    iBuilder->setModule(mMainModule);
185}
186
187ExternalBuffer * ParabixDriver::addExternalBuffer(std::unique_ptr<ExternalBuffer> b) {
188    mOwnedBuffers.emplace_back(std::move(b));
189    return cast<ExternalBuffer>(mOwnedBuffers.back().get());
190}
191
192StreamSetBuffer * ParabixDriver::addBuffer(std::unique_ptr<StreamSetBuffer> b) {
193    b->allocateBuffer(iBuilder);
194    mOwnedBuffers.emplace_back(std::move(b));
195    return mOwnedBuffers.back().get();
196}
197
198Kernel * ParabixDriver::addKernelInstance(std::unique_ptr<Kernel> kb) {
199    mOwnedKernels.emplace_back(std::move(kb));
200    return mOwnedKernels.back().get();
201}
202
203void ParabixDriver::addKernelCall(Kernel & kb, const std::vector<StreamSetBuffer *> & inputs, const std::vector<StreamSetBuffer *> & outputs) {
204    assert ("addKernelCall or makeKernelCall was already run on this kernel." && (kb.getModule() == nullptr));
205    mPipeline.emplace_back(&kb);
206    kb.bindPorts(inputs, outputs);
207    kb.makeModule(iBuilder);
208}
209
210void ParabixDriver::makeKernelCall(Kernel * kb, const std::vector<StreamSetBuffer *> & inputs, const std::vector<StreamSetBuffer *> & outputs) {
211    assert ("addKernelCall or makeKernelCall was already run on this kernel." && (kb->getModule() == nullptr));
212    mPipeline.emplace_back(kb);   
213    kb->bindPorts(inputs, outputs);
214    kb->makeModule(iBuilder);
215}
216
217void ParabixDriver::generatePipelineIR() {
218    #ifndef NDEBUG
219    if (LLVM_UNLIKELY(mPipeline.empty())) {
220        report_fatal_error("Pipeline cannot be empty");
221    } else {
222        for (auto i = mPipeline.begin(); i != mPipeline.end(); ++i) {
223            for (auto j = i; ++j != mPipeline.end(); ) {
224                if (LLVM_UNLIKELY(*i == *j)) {
225                    report_fatal_error("Kernel instances cannot occur twice in the pipeline");
226                }
227            }
228        }
229    }
230    #endif
231    // note: instantiation of all kernels must occur prior to initialization
232    for (const auto & k : mPipeline) {
233        k->addKernelDeclarations(iBuilder);
234    }
235    for (const auto & k : mPipeline) {
236        k->createInstance(iBuilder);
237    }
238    for (const auto & k : mPipeline) {
239        k->initializeInstance(iBuilder);
240    }
241    if (codegen::pipelineParallel) {
242        generateParallelPipeline(iBuilder, mPipeline);
243    } else if (codegen::segmentPipelineParallel) {
244        generateSegmentParallelPipeline(iBuilder, mPipeline);
245    } else {
246        codegen::ThreadNum = 1;
247        generatePipelineLoop(iBuilder, mPipeline);
248    }
249    for (const auto & k : mPipeline) {
250        k->finalizeInstance(iBuilder);
251    }
252}
253
254Function * ParabixDriver::LinkFunction(Module * mod, llvm::StringRef name, FunctionType * type, void * functionPtr) const {
255    assert ("addKernelCall or makeKernelCall must be called before LinkFunction" && (mod != nullptr));
256    Function * f = cast<Function>(mod->getOrInsertFunction(name, type));
257    mEngine->addGlobalMapping(f, functionPtr);
258    return f;
259}
260
261void ParabixDriver::linkAndFinalize() {
262
263//    using WorkQueue = boost::lockfree::queue<Kernel *>;
264
265    legacy::PassManager PM;
266    if (IN_DEBUG_MODE || LLVM_UNLIKELY(codegen::DebugOptionIsSet(codegen::VerifyIR))) {
267        PM.add(createVerifierPass());
268    }
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//    unsigned threadCount = std::thread::hardware_concurrency();
276
277    std::unique_ptr<raw_fd_ostream> IROutputStream(nullptr);
278    if (LLVM_UNLIKELY(codegen::DebugOptionIsSet(codegen::ShowIR))) {
279//        threadCount = 1; // If we're dumping IR, disable seperate compilation
280        if (codegen::IROutputFilename.empty()) {
281            IROutputStream.reset(new raw_fd_ostream(STDERR_FILENO, false, false));
282        } else {
283            std::error_code error;
284            IROutputStream.reset(new raw_fd_ostream(codegen::IROutputFilename, error, sys::fs::OpenFlags::F_None));
285        }
286        PM.add(createPrintModulePass(*IROutputStream));
287    }
288
289    #ifndef USE_LLVM_3_6
290    std::unique_ptr<raw_fd_ostream> ASMOutputStream(nullptr);
291    if (LLVM_UNLIKELY(codegen::DebugOptionIsSet(codegen::ShowASM))) {
292//        threadCount = 1; // If we're dumping ASM, disable seperate compilation
293        if (codegen::ASMOutputFilename.empty()) {
294            ASMOutputStream.reset(new raw_fd_ostream(STDERR_FILENO, false, false));
295        } else {
296            std::error_code error;
297            ASMOutputStream.reset(new raw_fd_ostream(codegen::ASMOutputFilename, error, sys::fs::OpenFlags::F_None));
298        }
299        if (LLVM_UNLIKELY(mTarget->addPassesToEmitFile(PM, *ASMOutputStream, TargetMachine::CGFT_AssemblyFile))) {
300            report_fatal_error("LLVM error: could not add emit assembly pass");
301        }
302    }
303    #endif
304
305    try {
306
307//    if (threadCount > 1) {
308
309//        WorkQueue Q(mPipeline.size());
310//        for (Kernel * kernel : mPipeline) {
311//            Q.unsynchronized_push(kernel); assert (kernel);
312//        }
313
314//        std::thread compilation_thread[threadCount - 1];
315//        for (unsigned i = 0; i < (threadCount - 1); ++i) {
316//            compilation_thread[i] = std::thread([&]{
317
318//                llvm::LLVMContext C;
319//                std::unique_ptr<KernelBuilder> kb(IDISA::GetIDISA_Builder(C, mMainModule->getTargetTriple()));
320//                kb->setDriver(this);
321
322//                Kernel * kernel = nullptr;
323//                while (Q.pop(kernel)) {
324//                    kb->setKernel(kernel);
325//                    Module * module = kernel->getModule();
326//                    bool uncachedObject = true;
327//                    if (mCache && mCache->loadCachedObjectFile(kb, kernel)) {
328//                        uncachedObject = false;
329//                    }
330//                    if (uncachedObject) {
331//                        module->setTargetTriple(mMainModule->getTargetTriple());
332//                        kernel->generateKernel(kb);
333//                        // PM.run(*module);
334//                        mEngine->generateCodeForModule(module);
335//                    }
336//                    // mEngine->addModule(std::unique_ptr<Module>(module));
337//                }
338//            });
339//        }
340
341//        // PM.run(*mMainModule);
342
343//        Kernel * kernel = nullptr;
344//        while (Q.pop(kernel)) {
345//            iBuilder->setKernel(kernel);
346//            Module * module = kernel->getModule();
347//            bool uncachedObject = true;
348//            if (mCache && mCache->loadCachedObjectFile(iBuilder, kernel)) {
349//                uncachedObject = false;
350//            }
351//            if (uncachedObject) {
352//                module->setTargetTriple(mMainModule->getTargetTriple());
353//                kernel->generateKernel(iBuilder);
354//                // PM.run(*module);
355//            }
356//            mEngine->addModule(std::unique_ptr<Module>(module));
357//            mEngine->generateCodeForModule(module);
358//        }
359
360//        for (unsigned i = 0; i < (threadCount - 1); ++i) {
361//            compilation_thread[i].join();
362//        }
363
364//        iBuilder->setKernel(nullptr);
365
366//    } else { // single threaded
367
368        for (Kernel * const kernel : mPipeline) {
369
370            iBuilder->setKernel(kernel);
371            Module * module = kernel->getModule();
372            bool uncachedObject = true;
373            if (mCache && mCache->loadCachedObjectFile(iBuilder, kernel)) {
374                uncachedObject = false;
375            }
376            if (uncachedObject) {
377                module->setTargetTriple(mMainModule->getTargetTriple());
378                kernel->generateKernel(iBuilder);
379                PM.run(*module);
380            }
381            mEngine->addModule(std::unique_ptr<Module>(module));
382            mEngine->generateCodeForModule(module);
383        }
384
385        iBuilder->setKernel(nullptr);
386        PM.run(*mMainModule);
387
388//    }
389
390    mEngine->finalizeObject();
391
392    } catch (const std::exception & e) {
393        report_fatal_error(e.what());
394    }
395
396}
397
398const std::unique_ptr<KernelBuilder> & ParabixDriver::getBuilder() {
399    return iBuilder;
400}
401
402void * ParabixDriver::getPointerToMain() {
403    return mEngine->getPointerToNamedFunction("Main");
404}
405
406ParabixDriver::~ParabixDriver() {
407    delete mCache;
408}
Note: See TracBrowser for help on using the repository browser.