source: icGREP/icgrep-devel/icgrep/kernels/kernel.cpp @ 5755

Last change on this file since 5755 was 5755, checked in by nmedfort, 16 months ago

Bug fixes and simplified MultiBlockKernel? logic

File size: 70.1 KB
Line 
1/*
2 *  Copyright (c) 2016-7 International Characters.
3 *  This software is licensed to the public under the Open Software License 3.0.
4 */
5
6#include "kernel.h"
7#include <toolchain/toolchain.h>
8#include <kernels/streamset.h>
9#include <llvm/IR/Constants.h>
10#include <llvm/IR/Function.h>
11#include <llvm/IR/Instructions.h>
12#include <llvm/IR/MDBuilder.h>
13#include <llvm/IR/Module.h>
14#include <llvm/Support/raw_ostream.h>
15#if LLVM_VERSION_INTEGER < LLVM_4_0_0
16#include <llvm/Bitcode/ReaderWriter.h>
17#else
18#include <llvm/Bitcode/BitcodeWriter.h>
19#endif
20#include <llvm/Transforms/Utils/Local.h>
21#include <kernels/streamset.h>
22#include <sstream>
23#include <kernels/kernel_builder.h>
24#include <boost/math/common_factor.hpp>
25#include <llvm/Support/Debug.h>
26
27using namespace llvm;
28using namespace parabix;
29using namespace boost::math;
30
31namespace kernel {
32
33const std::string Kernel::DO_BLOCK_SUFFIX = "_DoBlock";
34const std::string Kernel::FINAL_BLOCK_SUFFIX = "_FinalBlock";
35const std::string Kernel::MULTI_BLOCK_SUFFIX = "_MultiBlock";
36const std::string Kernel::LOGICAL_SEGMENT_NO_SCALAR = "logicalSegNo";
37const std::string Kernel::PROCESSED_ITEM_COUNT_SUFFIX = "_processedItemCount";
38const std::string Kernel::CONSUMED_ITEM_COUNT_SUFFIX = "_consumedItemCount";
39const std::string Kernel::PRODUCED_ITEM_COUNT_SUFFIX = "_producedItemCount";
40const std::string Kernel::TERMINATION_SIGNAL = "terminationSignal";
41const std::string Kernel::BUFFER_PTR_SUFFIX = "_bufferPtr";
42const std::string Kernel::CONSUMER_SUFFIX = "_consumerLocks";
43const std::string Kernel::CYCLECOUNT_SCALAR = "CPUcycles";
44
45/** ------------------------------------------------------------------------------------------------------------- *
46 * @brief addScalar
47 ** ------------------------------------------------------------------------------------------------------------- */
48unsigned Kernel::addScalar(Type * const type, const std::string & name) {
49    if (LLVM_UNLIKELY(mKernelStateType != nullptr)) {
50        report_fatal_error("Cannot add field " + name + " to " + getName() + " after kernel state finalized");
51    }
52    if (LLVM_UNLIKELY(mKernelFieldMap.count(name))) {
53        report_fatal_error(getName() + " already contains scalar field " + name);
54    }
55    const auto index = mKernelFields.size();
56    mKernelFieldMap.emplace(name, index);
57    mKernelFields.push_back(type);
58    return index;
59}
60
61
62/** ------------------------------------------------------------------------------------------------------------- *
63 * @brief addUnnamedScalar
64 ** ------------------------------------------------------------------------------------------------------------- */
65unsigned Kernel::addUnnamedScalar(Type * const type) {
66    if (LLVM_UNLIKELY(mKernelStateType != nullptr)) {
67        report_fatal_error("Cannot add unnamed field  to " + getName() + " after kernel state finalized");
68    }
69    const auto index = mKernelFields.size();
70    mKernelFields.push_back(type);
71    return index;
72}
73
74
75/** ------------------------------------------------------------------------------------------------------------- *
76 * @brief prepareStreamSetNameMap
77 ** ------------------------------------------------------------------------------------------------------------- */
78void Kernel::prepareStreamSetNameMap() {
79    for (unsigned i = 0; i < mStreamSetInputs.size(); i++) {
80        mStreamMap.emplace(mStreamSetInputs[i].getName(), std::make_pair(Port::Input, i));
81    }
82    for (unsigned i = 0; i < mStreamSetOutputs.size(); i++) {
83        mStreamMap.emplace(mStreamSetOutputs[i].getName(), std::make_pair(Port::Output, i));
84    }
85}
86
87
88/** ------------------------------------------------------------------------------------------------------------- *
89 * @brief bindPorts
90 ** ------------------------------------------------------------------------------------------------------------- */
91void Kernel::bindPorts(const StreamSetBuffers & inputs, const StreamSetBuffers & outputs) {
92    assert (mModule == nullptr);
93    assert (mStreamSetInputBuffers.empty());
94    assert (mStreamSetOutputBuffers.empty());
95
96    if (LLVM_UNLIKELY(mStreamSetInputs.size() != inputs.size())) {
97        report_fatal_error(getName() + ": expected " + std::to_string(mStreamSetInputs.size()) +
98                           " input stream sets but was given "
99                           + std::to_string(inputs.size()));
100    }
101
102    for (unsigned i = 0; i < inputs.size(); ++i) {
103        StreamSetBuffer * const buf = inputs[i];
104        if (LLVM_UNLIKELY(buf == nullptr)) {
105            report_fatal_error(getName() + ": input stream set " + std::to_string(i)
106                               + " cannot be null");
107        }
108        buf->addConsumer(this);
109    }
110
111    if (LLVM_UNLIKELY(mStreamSetOutputs.size() != outputs.size())) {
112        report_fatal_error(getName() + ": expected " + std::to_string(mStreamSetOutputs.size())
113                           + " output stream sets but was given "
114                           + std::to_string(outputs.size()));
115    }
116
117    for (unsigned i = 0; i < outputs.size(); ++i) {
118        StreamSetBuffer * const buf = outputs[i];
119        if (LLVM_UNLIKELY(buf == nullptr)) {
120            report_fatal_error(getName() + ": output stream set " + std::to_string(i) + " cannot be null");
121        }
122        if (LLVM_LIKELY(buf->getProducer() == nullptr)) {
123            buf->setProducer(this);
124        } else {
125            report_fatal_error(getName() + ": output stream set " + std::to_string(i)
126                               + " is already produced by kernel " + buf->getProducer()->getName());
127        }
128    }
129
130    mStreamSetInputBuffers.assign(inputs.begin(), inputs.end());
131    mStreamSetOutputBuffers.assign(outputs.begin(), outputs.end());
132}
133
134
135/** ------------------------------------------------------------------------------------------------------------- *
136 * @brief getCacheName
137 ** ------------------------------------------------------------------------------------------------------------- */
138std::string Kernel::getCacheName(const std::unique_ptr<KernelBuilder> & idb) const {
139    std::stringstream cacheName;
140    cacheName << getName() << '_' << idb->getBuilderUniqueName();
141    for (const StreamSetBuffer * b: mStreamSetInputBuffers) {
142        cacheName <<  ':' <<  b->getUniqueID();
143    }
144    for (const StreamSetBuffer * b: mStreamSetOutputBuffers) {
145        cacheName <<  ':' <<  b->getUniqueID();
146    }
147    return cacheName.str();
148}
149
150
151/** ------------------------------------------------------------------------------------------------------------- *
152 * @brief setModule
153 ** ------------------------------------------------------------------------------------------------------------- */
154Module * Kernel::setModule(Module * const module) {
155    assert (mModule == nullptr || mModule == module);
156    assert (module != nullptr);
157    mModule = module;
158    return mModule;
159}
160
161
162/** ------------------------------------------------------------------------------------------------------------- *
163 * @brief makeModule
164 ** ------------------------------------------------------------------------------------------------------------- */
165Module * Kernel::makeModule(const std::unique_ptr<kernel::KernelBuilder> & idb) {
166    Module * m = new Module(getCacheName(idb), idb->getContext());
167    m->setTargetTriple(idb->getModule()->getTargetTriple());
168    m->setDataLayout(idb->getModule()->getDataLayout());
169    return setModule(m);
170}
171
172
173/** ------------------------------------------------------------------------------------------------------------- *
174 * @brief prepareKernel
175 ** ------------------------------------------------------------------------------------------------------------- */
176void Kernel::prepareKernel(const std::unique_ptr<KernelBuilder> & idb) {
177    assert ("KernelBuilder does not have a valid IDISA Builder" && idb);
178    if (LLVM_UNLIKELY(mKernelStateType != nullptr)) {
179        report_fatal_error(getName() + ": cannot prepare kernel after kernel state finalized");
180    }
181    addBaseKernelProperties(idb);
182    addInternalKernelProperties(idb);
183    // NOTE: StructType::create always creates a new type even if an identical one exists.
184    if (LLVM_UNLIKELY(mModule == nullptr)) {
185        makeModule(idb);
186    }
187    mKernelStateType = mModule->getTypeByName(getName());
188    if (LLVM_LIKELY(mKernelStateType == nullptr)) {
189        mKernelStateType = StructType::create(idb->getContext(), mKernelFields, getName());
190        assert (mKernelStateType);
191    }
192}
193
194
195/** ------------------------------------------------------------------------------------------------------------- *
196 * @brief prepareCachedKernel
197 ** ------------------------------------------------------------------------------------------------------------- */
198void Kernel::prepareCachedKernel(const std::unique_ptr<KernelBuilder> & idb) {
199    assert ("KernelBuilder does not have a valid IDISA Builder" && idb);
200    if (LLVM_UNLIKELY(mKernelStateType != nullptr)) {
201        report_fatal_error(getName() + ": cannot prepare kernel after kernel state finalized");
202    }
203    assert (getModule());
204    addBaseKernelProperties(idb);
205    mKernelStateType = getModule()->getTypeByName(getName());
206    if (LLVM_UNLIKELY(mKernelStateType == nullptr)) {
207        report_fatal_error("Kernel definition for " + getName() + " could not be found in the cache object");
208    }
209}
210
211/** ------------------------------------------------------------------------------------------------------------- *
212 * @brief addBaseKernelProperties
213 ** ------------------------------------------------------------------------------------------------------------- */
214void Kernel::addBaseKernelProperties(const std::unique_ptr<KernelBuilder> & idb) {
215
216    if (mStreamMap.empty()) {
217        prepareStreamSetNameMap();
218    }
219
220    normalizeStreamProcessingRates();
221
222    const unsigned inputSetCount = mStreamSetInputs.size();
223    const unsigned outputSetCount = mStreamSetOutputs.size();
224
225    assert (inputSetCount == mStreamSetInputBuffers.size());
226    assert (outputSetCount == mStreamSetOutputBuffers.size());
227
228    if (mStride == 0) {
229        // Set the default kernel stride.
230        mStride = idb->getBitBlockWidth();
231    }
232
233    IntegerType * const sizeTy = idb->getSizeTy();
234
235    for (unsigned i = 0; i < inputSetCount; i++) {
236        const Binding & b = mStreamSetInputs[i];
237        //const ProcessingRate & rate = b.getRate();
238        //if (rate.isBounded() || rate.isUnknown()) {
239            addScalar(sizeTy, b.getName() + PROCESSED_ITEM_COUNT_SUFFIX);
240        //}
241    }
242
243    for (unsigned i = 0; i < outputSetCount; i++) {
244        const Binding & b = mStreamSetOutputs[i];
245        //const ProcessingRate & rate = b.getRate();
246        //if (rate.isBounded() || rate.isUnknown()) {
247            addScalar(sizeTy, b.getName() + PRODUCED_ITEM_COUNT_SUFFIX);
248        //}
249    }
250
251    for (unsigned i = 0; i < inputSetCount; i++) {
252        mScalarInputs.emplace_back(mStreamSetInputBuffers[i]->getStreamSetHandle()->getType(), mStreamSetInputs[i].getName() + BUFFER_PTR_SUFFIX);
253    }
254    for (unsigned i = 0; i < outputSetCount; i++) {
255        mScalarInputs.emplace_back(mStreamSetOutputBuffers[i]->getStreamSetHandle()->getType(), mStreamSetOutputs[i].getName() + BUFFER_PTR_SUFFIX);
256    }
257    for (const auto & binding : mScalarInputs) {
258        addScalar(binding.getType(), binding.getName());
259    }
260    for (const auto & binding : mScalarOutputs) {
261        addScalar(binding.getType(), binding.getName());
262    }
263    for (const auto & binding : mInternalScalars) {
264        addScalar(binding.getType(), binding.getName());
265    }
266    Type * const consumerSetTy = StructType::get(idb->getContext(), {sizeTy, sizeTy->getPointerTo()->getPointerTo()})->getPointerTo();
267    for (unsigned i = 0; i < mStreamSetOutputs.size(); i++) {
268        addScalar(consumerSetTy, mStreamSetOutputs[i].getName() + CONSUMER_SUFFIX);
269    }
270    addScalar(sizeTy, LOGICAL_SEGMENT_NO_SCALAR);
271    addScalar(idb->getInt1Ty(), TERMINATION_SIGNAL);
272    for (unsigned i = 0; i < mStreamSetOutputs.size(); i++) {
273        addScalar(sizeTy, mStreamSetOutputs[i].getName() + CONSUMED_ITEM_COUNT_SUFFIX);
274    }
275    // We compile in a 64-bit CPU cycle counter into every kernel.   It will remain unused
276    // in normal execution, but when codegen::EnableCycleCounter is specified, pipelines
277    // will be able to add instrumentation to cached modules without recompilation.
278    addScalar(idb->getInt64Ty(), CYCLECOUNT_SCALAR);
279
280}
281
282
283/** ------------------------------------------------------------------------------------------------------------- *
284 * @brief makeSignature
285 *
286 * Default kernel signature: generate the IR and emit as byte code.
287 ** ------------------------------------------------------------------------------------------------------------- */
288std::string Kernel::makeSignature(const std::unique_ptr<kernel::KernelBuilder> & idb) {
289    assert ("KernelBuilder does not have a valid IDISA Builder" && idb.get());
290    if (LLVM_UNLIKELY(hasSignature())) {
291        generateKernel(idb);
292        std::string signature;
293        raw_string_ostream OS(signature);
294        WriteBitcodeToFile(getModule(), OS);
295        return signature;
296    } else {
297        return getModule()->getModuleIdentifier();
298    }
299}
300
301
302/** ------------------------------------------------------------------------------------------------------------- *
303 * @brief generateKernel
304 ** ------------------------------------------------------------------------------------------------------------- */
305void Kernel::generateKernel(const std::unique_ptr<kernel::KernelBuilder> & idb) {
306    assert ("KernelBuilder does not have a valid IDISA Builder" && idb.get());
307    // If the module id cannot uniquely identify this kernel, "generateKernelSignature()" will have already
308    // generated the unoptimized IR.
309    if (!mIsGenerated) {
310        const auto m = idb->getModule();
311        const auto ip = idb->saveIP();
312        // const auto saveInstance = getInstance();
313        idb->setModule(mModule);
314        addKernelDeclarations(idb);
315        callGenerateInitializeMethod(idb);
316        callGenerateDoSegmentMethod(idb);
317        callGenerateFinalizeMethod(idb);
318        // setInstance(saveInstance);
319        idb->setModule(m);
320        idb->restoreIP(ip);
321        mIsGenerated = true;
322    }
323}
324
325
326/** ------------------------------------------------------------------------------------------------------------- *
327 * @brief callGenerateInitializeMethod
328 ** ------------------------------------------------------------------------------------------------------------- */
329inline void Kernel::callGenerateInitializeMethod(const std::unique_ptr<kernel::KernelBuilder> & idb) {
330    mCurrentMethod = getInitFunction(idb->getModule());
331    idb->SetInsertPoint(BasicBlock::Create(idb->getContext(), "entry", mCurrentMethod));
332    Function::arg_iterator args = mCurrentMethod->arg_begin();
333    setInstance(&*(args++));
334    idb->CreateStore(ConstantAggregateZero::get(mKernelStateType), getInstance());
335    for (const auto & binding : mScalarInputs) {
336        idb->setScalarField(binding.getName(), &*(args++));
337    }
338    for (const auto & binding : mStreamSetOutputs) {
339        idb->setConsumerLock(binding.getName(), &*(args++));
340    }
341    generateInitializeMethod(idb);
342    idb->CreateRetVoid();
343}
344
345/** ------------------------------------------------------------------------------------------------------------- *
346 * @brief callGenerateDoSegmentMethod
347 ** ------------------------------------------------------------------------------------------------------------- */
348inline void Kernel::callGenerateDoSegmentMethod(const std::unique_ptr<kernel::KernelBuilder> & idb) {
349    mCurrentMethod = getDoSegmentFunction(idb->getModule());
350    idb->SetInsertPoint(BasicBlock::Create(idb->getContext(), "entry", mCurrentMethod));
351    auto args = mCurrentMethod->arg_begin();
352    setInstance(&*(args++));
353    mIsFinal = &*(args++);
354    mAvailablePrincipalItemCount = nullptr;
355    const auto n = mStreamSetInputs.size();
356    mAvailableItemCount.resize(n, nullptr);
357    for (unsigned i = 0; i < n; i++) {
358        assert (args != mCurrentMethod->arg_end());
359        mAvailableItemCount[i] = &*(args++);
360    }
361    assert (args == mCurrentMethod->arg_end());
362    generateKernelMethod(idb); // must be overridden by the Kernel subtype
363    mIsFinal = nullptr;
364    mAvailableItemCount.clear();
365    idb->CreateRetVoid();
366}
367
368
369/** ------------------------------------------------------------------------------------------------------------- *
370 * @brief callGenerateFinalizeMethod
371 ** ------------------------------------------------------------------------------------------------------------- */
372inline void Kernel::callGenerateFinalizeMethod(const std::unique_ptr<KernelBuilder> & idb) {
373    mCurrentMethod = getTerminateFunction(idb->getModule());
374    idb->SetInsertPoint(BasicBlock::Create(idb->getContext(), "entry", mCurrentMethod));
375    auto args = mCurrentMethod->arg_begin();
376    setInstance(&*(args++));
377    generateFinalizeMethod(idb); // may be overridden by the Kernel subtype
378    const auto n = mScalarOutputs.size();
379    if (n == 0) {
380        idb->CreateRetVoid();
381    } else {
382        Value * outputs[n];
383        for (unsigned i = 0; i < n; ++i) {
384            outputs[i] = idb->getScalarField(mScalarOutputs[i].getName());
385        }
386        if (n == 1) {
387            idb->CreateRet(outputs[0]);
388        } else {
389            idb->CreateAggregateRet(outputs, n);
390        }
391    }
392}
393
394
395/** ------------------------------------------------------------------------------------------------------------- *
396 * @brief getScalarIndex
397 ** ------------------------------------------------------------------------------------------------------------- */
398unsigned Kernel::getScalarIndex(const std::string & name) const {
399    const auto f = mKernelFieldMap.find(name);
400    if (LLVM_UNLIKELY(f == mKernelFieldMap.end())) {
401        assert (false);
402        report_fatal_error(getName() + " does not contain scalar: " + name);
403    }
404    return f->second;
405}
406
407
408/** ------------------------------------------------------------------------------------------------------------- *
409 * @brief createInstance
410 ** ------------------------------------------------------------------------------------------------------------- */
411Value * Kernel::createInstance(const std::unique_ptr<KernelBuilder> & idb) {
412    assert ("KernelBuilder does not have a valid IDISA Builder" && idb);
413    if (LLVM_UNLIKELY(mKernelStateType == nullptr)) {
414        report_fatal_error("Cannot instantiate " + getName() + " before calling prepareKernel()");
415    }
416    setInstance(idb->CreateCacheAlignedAlloca(mKernelStateType));
417    return getInstance();
418}
419
420
421/** ------------------------------------------------------------------------------------------------------------- *
422 * @brief initializeInstance
423 ** ------------------------------------------------------------------------------------------------------------- */
424void Kernel::initializeInstance(const std::unique_ptr<KernelBuilder> & idb) {
425    assert ("KernelBuilder does not have a valid IDISA Builder" && idb);
426    if (LLVM_UNLIKELY(getInstance() == nullptr)) {
427        report_fatal_error("Cannot initialize " + getName() + " before calling createInstance()");
428    }
429    std::vector<Value *> args;
430    args.reserve(1 + mInitialArguments.size() + mStreamSetInputBuffers.size() + (mStreamSetOutputBuffers.size() * 2));
431    args.push_back(getInstance());
432    for (unsigned i = 0; i < mInitialArguments.size(); ++i) {
433        Value * arg = mInitialArguments[i];
434        if (LLVM_UNLIKELY(arg == nullptr)) {
435            report_fatal_error(getName() + ": initial argument " + std::to_string(i)
436                               + " cannot be null when calling createInstance()");
437        }
438        args.push_back(arg);
439    }
440    for (unsigned i = 0; i < mStreamSetInputBuffers.size(); ++i) {
441        assert (mStreamSetInputBuffers[i]);
442        Value * arg = mStreamSetInputBuffers[i]->getStreamSetHandle();
443        if (LLVM_UNLIKELY(arg == nullptr)) {
444            report_fatal_error(getName() + ": input stream set " + std::to_string(i)
445                               + " was not allocated prior to calling createInstance()");
446        }
447        args.push_back(arg);
448    }
449    assert (mStreamSetInputs.size() == mStreamSetInputBuffers.size());
450    for (unsigned i = 0; i < mStreamSetOutputBuffers.size(); ++i) {
451        assert (mStreamSetOutputBuffers[i]);
452        Value * arg = mStreamSetOutputBuffers[i]->getStreamSetHandle();
453        if (LLVM_UNLIKELY(arg == nullptr)) {
454            report_fatal_error(getName() + ": output stream set " + std::to_string(i)
455                               + " was not allocated prior to calling createInstance()");
456        }
457        args.push_back(arg);
458    }
459    assert (mStreamSetOutputs.size() == mStreamSetOutputBuffers.size());
460    IntegerType * const sizeTy = idb->getSizeTy();
461    PointerType * const sizePtrTy = sizeTy->getPointerTo();
462    PointerType * const sizePtrPtrTy = sizePtrTy->getPointerTo();
463    StructType * const consumerTy = StructType::get(idb->getContext(), {sizeTy, sizePtrPtrTy});
464    for (unsigned i = 0; i < mStreamSetOutputBuffers.size(); ++i) {
465        const auto output = mStreamSetOutputBuffers[i];
466        const auto & consumers = output->getConsumers();
467        const auto n = consumers.size();
468        AllocaInst * const outputConsumers = idb->CreateAlloca(consumerTy);
469        Value * const consumerSegNoArray = idb->CreateAlloca(ArrayType::get(sizePtrTy, n));
470        for (unsigned i = 0; i < n; ++i) {
471            Kernel * const consumer = consumers[i];
472            assert ("all instances must be created prior to initialization of any instance" && consumer->getInstance());
473            idb->setKernel(consumer);
474            Value * const segmentNoPtr = idb->getScalarFieldPtr(LOGICAL_SEGMENT_NO_SCALAR);
475            idb->CreateStore(segmentNoPtr, idb->CreateGEP(consumerSegNoArray, { idb->getInt32(0), idb->getInt32(i) }));
476        }
477        idb->setKernel(this);
478        Value * const consumerCountPtr = idb->CreateGEP(outputConsumers, {idb->getInt32(0), idb->getInt32(0)});
479        idb->CreateStore(idb->getSize(n), consumerCountPtr);
480        Value * const consumerSegNoArrayPtr = idb->CreateGEP(outputConsumers, {idb->getInt32(0), idb->getInt32(1)});
481        idb->CreateStore(idb->CreatePointerCast(consumerSegNoArray, sizePtrPtrTy), consumerSegNoArrayPtr);
482        args.push_back(outputConsumers);
483    }
484    idb->CreateCall(getInitFunction(idb->getModule()), args);
485}
486
487/** ------------------------------------------------------------------------------------------------------------- *
488 * @brief finalizeInstance
489 ** ------------------------------------------------------------------------------------------------------------- */
490void Kernel::finalizeInstance(const std::unique_ptr<KernelBuilder> & idb) {
491    assert ("KernelBuilder does not have a valid IDISA Builder" && idb);
492    mOutputScalarResult = idb->CreateCall(getTerminateFunction(idb->getModule()), { getInstance() });
493}
494
495/** ------------------------------------------------------------------------------------------------------------- *
496 * @brief getStreamPort
497 ** ------------------------------------------------------------------------------------------------------------- */
498Kernel::StreamPort Kernel::getStreamPort(const std::string & name) const {
499    const auto f = mStreamMap.find(name);
500    if (LLVM_UNLIKELY(f == mStreamMap.end())) {
501        report_fatal_error(getName() + " does not contain stream set " + name);
502    }
503    return f->second;
504}
505
506/** ------------------------------------------------------------------------------------------------------------- *
507 * @brief getStreamPort
508 ** ------------------------------------------------------------------------------------------------------------- */
509const Binding & Kernel::getBinding(const std::string & name) const {
510    Port port; unsigned index;
511    std::tie(port, index) = getStreamPort(name);
512    return (port == Port::Input) ? getStreamInput(index) : getStreamOutput(index);
513}
514
515/** ------------------------------------------------------------------------------------------------------------- *
516 * @brief normalizeRelativeToFixedProcessingRate
517 ** ------------------------------------------------------------------------------------------------------------- */
518bool Kernel::normalizeRelativeToFixedProcessingRate(const ProcessingRate & base, ProcessingRate & toUpdate) {
519    if (base.isFixed()) {
520        return true;
521    } else if (LLVM_UNLIKELY(base.isRelative())) {
522        const auto & ref = getBinding(base.getReference()).getRate();
523        if (normalizeRelativeToFixedProcessingRate(ref, toUpdate)) {
524            toUpdate.getRate() *= ref.getRate();
525            return true;
526        }
527    }
528    return false;
529}
530
531/** ------------------------------------------------------------------------------------------------------------- *
532 * @brief normalizeStreamProcessingRates
533 *
534 * If we allow a stream to be transitively relative to a fixed rate stream, it complicates detection of fixed
535 * rate streams later. Find any such occurance and transform them. This implies, however, that a fixed rate
536 * stream could have a rational processing rate (which should not occur normally.)
537 ** ------------------------------------------------------------------------------------------------------------- */
538inline void Kernel::normalizeStreamProcessingRates() {
539    for (Binding & input : mStreamSetInputs) {
540        normalizeRelativeToFixedProcessingRate(input.getRate(), input.getRate());
541    }
542    for (Binding & output : mStreamSetOutputs) {
543        normalizeRelativeToFixedProcessingRate(output.getRate(), output.getRate());
544    }
545    // TODO: we want to consume whole units. Once the pipeline is able to schedule kernels based on their stride
546    // and input/output rates, modify them here.
547}
548
549/** ------------------------------------------------------------------------------------------------------------- *
550 * @brief generateKernelMethod
551 ** ------------------------------------------------------------------------------------------------------------- */
552void SegmentOrientedKernel::generateKernelMethod(const std::unique_ptr<KernelBuilder> & b) {
553    const auto inputSetCount = mStreamSetInputs.size();
554    mStreamSetInputBaseAddress.resize(inputSetCount);
555    for (unsigned i = 0; i < inputSetCount; ++i) {
556        mStreamSetInputBaseAddress[i] = nullptr;
557    }
558    const auto outputSetCount = mStreamSetOutputs.size();
559    mStreamSetOutputBaseAddress.resize(outputSetCount);
560    for (unsigned i = 0; i < outputSetCount; ++i) {
561        mStreamSetOutputBaseAddress[i] = nullptr;
562    }
563    generateDoSegmentMethod(b);
564}
565
566/** ------------------------------------------------------------------------------------------------------------- *
567 * @brief requiresBufferedFinalStride
568 ** ------------------------------------------------------------------------------------------------------------- */
569inline bool requiresBufferedFinalStride(const Binding & b) {
570    if (LLVM_LIKELY(isa<ArrayType>(b.getType()))) {
571        return b.getType()->getArrayNumElements() == 1;
572    }
573    return true;
574}
575
576/** ------------------------------------------------------------------------------------------------------------- *
577 * @brief getItemWidth
578 ** ------------------------------------------------------------------------------------------------------------- */
579inline unsigned getItemWidth(const Binding & b) {
580    Type * ty = b.getType();
581    if (LLVM_LIKELY(isa<ArrayType>(ty))) {
582        ty = ty->getArrayElementType();
583    }
584    return cast<IntegerType>(ty->getVectorElementType())->getBitWidth();
585}
586
587/** ------------------------------------------------------------------------------------------------------------- *
588 * @brief getLowerBound
589 ** ------------------------------------------------------------------------------------------------------------- */
590ProcessingRate::RateValue MultiBlockKernel::getLowerBound(const ProcessingRate & rate) const {
591    if (rate.isFixed() || rate.isBounded()) {
592        return rate.getLowerBound();
593    } else if (rate.isRelative()) {
594        return rate.getRate() * getLowerBound(getBinding(rate.getReference()).getRate());
595    } else { // if (rate.isUnknown())
596        return 0;
597    }
598}
599
600/** ------------------------------------------------------------------------------------------------------------- *
601 * @brief getUpperBound
602 ** ------------------------------------------------------------------------------------------------------------- */
603ProcessingRate::RateValue MultiBlockKernel::getUpperBound(const ProcessingRate &rate) const {
604    if (rate.isFixed() || rate.isBounded()) {
605        return rate.getUpperBound();
606    } else if (rate.isRelative()) {
607        return rate.getRate() * getUpperBound(getBinding(rate.getReference()).getRate());
608    } else { // if (rate.isUnknown())
609        return 0;
610    }
611}
612
613/** ------------------------------------------------------------------------------------------------------------- *
614 * @brief getUpperBound
615 ** ------------------------------------------------------------------------------------------------------------- */
616bool MultiBlockKernel::isTransitivelyUnknownRate(const ProcessingRate & rate) const {
617    if (rate.isUnknown()) {
618        return true;
619    } else if (rate.isDerived()) {
620        return isTransitivelyUnknownRate(getBinding(rate.getReference()).getRate());
621    }
622    return false;
623}
624
625/** ------------------------------------------------------------------------------------------------------------- *
626 * @brief roundUp
627 ** ------------------------------------------------------------------------------------------------------------- */
628unsigned roundUp(const ProcessingRate::RateValue & r) {
629    if (LLVM_LIKELY(r.denominator() == 1)) {
630        return r.numerator();
631    } else {
632        return (r.numerator() + r.denominator() - 1) / r.denominator();
633    }
634}
635
636/** ------------------------------------------------------------------------------------------------------------- *
637 * @brief getItemAlignment
638 ** ------------------------------------------------------------------------------------------------------------- */
639inline unsigned MultiBlockKernel::getItemAlignment(const Binding & binding) const {
640    const auto & rate = binding.getRate();
641    if (rate.isFixed()) {
642        const auto & r = rate.getRate();
643        const auto n = (r.numerator() * mStride);
644        if (LLVM_LIKELY(r.denominator() == 1)) {
645            return n;
646        } else if (LLVM_LIKELY((n % r.denominator()) == 0)) {
647            return n / r.denominator();
648        }
649    }
650    return 1; // ∀x GCD(x, x + 1) = 1
651}
652
653/** ------------------------------------------------------------------------------------------------------------- *
654 * @brief getStrideSize
655 ** ------------------------------------------------------------------------------------------------------------- */
656llvm::Value * MultiBlockKernel::getStrideSize(const std::unique_ptr<KernelBuilder> & b, const ProcessingRate & rate) {
657    // NOTE: if we ever support feedback loops, using upper bound could lead to a deadlock due to data starvation
658    const auto r = getUpperBound(rate);
659    if (r.numerator() == 0) {
660        return nullptr;
661    } else {
662        assert ((r.numerator() * mStride) % r.denominator() == 0);
663        return b->getSize((r.numerator() * mStride) / r.denominator());
664    }
665}
666
667/** ------------------------------------------------------------------------------------------------------------- *
668 * @brief generateKernelMethod
669 ** ------------------------------------------------------------------------------------------------------------- */
670void MultiBlockKernel::generateKernelMethod(const std::unique_ptr<KernelBuilder> & b) {
671
672    if (LLVM_UNLIKELY((mStride % b->getBitBlockWidth()) != 0)) {
673        report_fatal_error(getName() + ": the Stride (" + std::to_string(mStride) + ") of MultiBlockKernel "
674                           "must be a multiple of the BitBlockWidth (" + std::to_string(b->getBitBlockWidth()) + ")");
675    }
676
677    const auto inputSetCount = mStreamSetInputs.size();
678    const auto outputSetCount = mStreamSetOutputs.size();
679
680    // Define and allocate the temporary buffer area in the prolog.
681    const auto alignment = b->getBitBlockWidth() / 8;
682    Value * temporaryInputBuffer[inputSetCount];
683    for (unsigned i = 0; i < inputSetCount; ++i) {
684
685        // TODO: if this is a fixed rate input stream and the pipeline guarantees it will not call the kernel unless
686        // there is sufficient input and all buffers will be sized sufficiently for the input, we ought to be able to
687        // avoid the temporary buffer checks.
688
689        const ProcessingRate & rate = mStreamSetInputs[i].getRate();
690        Type * const ty = mStreamSetInputBuffers[i]->getStreamSetBlockType();
691        const auto ub = getUpperBound(rate);
692        if (ub.numerator() == 0) {
693            report_fatal_error("MultiBlock kernels do not support unknown rate input streams or streams relative to an unknown rate input.");
694        } else {           
695            temporaryInputBuffer[i] = b->CreateAlignedAlloca(ty, alignment, b->getSize(roundUp(ub)));
696            Type * const sty = temporaryInputBuffer[i]->getType()->getPointerElementType();
697            b->CreateStore(Constant::getNullValue(sty), temporaryInputBuffer[i]);
698        }       
699    }
700
701    Value * temporaryOutputBuffer[outputSetCount];
702    for (unsigned i = 0; i < outputSetCount; i++) {
703        const ProcessingRate & rate = mStreamSetOutputs[i].getRate();
704        Type * const ty = mStreamSetOutputBuffers[i]->getStreamSetBlockType();
705        if (LLVM_UNLIKELY(isTransitivelyUnknownRate(rate))) {
706            temporaryOutputBuffer[i] = nullptr;
707        } else {           
708            auto ub = getUpperBound(rate);
709            if (LLVM_UNLIKELY(mStreamSetOutputBuffers[i]->supportsCopyBack() && requiresCopyBack(rate))) {
710                ub += mStreamSetOutputBuffers[i]->overflowSize();
711            }
712            temporaryOutputBuffer[i] = b->CreateAlignedAlloca(ty, alignment, b->getSize(roundUp(ub)));
713            Type * const sty = temporaryOutputBuffer[i]->getType()->getPointerElementType();
714            b->CreateStore(Constant::getNullValue(sty), temporaryOutputBuffer[i]);
715        }
716    }
717
718    // Now we iteratively process these blocks using the doMultiBlock method.
719    // In each iteration, we check how many linearly accessible / writable
720    // items can be processed with our current input / output buffers. If we
721    // cannot support an full stride, we check whether (a) there is enough
722    // input data to process but it is not linearly accessible, in which case
723    // we move the data into temporary buffers or (b) there is not enough data
724    // to process, in which case we abort unless IsFinal was set.
725
726    Constant * const ZERO = b->getSize(0);
727    Constant * const ONE = b->getSize(1);
728    Constant * const LOG_2_BLOCK_WIDTH = b->getSize(std::log2(b->getBitBlockWidth()));
729    Constant * const BLOCK_WIDTH_MASK = b->getSize(b->getBitBlockWidth() - 1);
730
731    // Now proceed with creation of the doSegment method.
732    BasicBlock * const segmentLoop = b->CreateBasicBlock("SegmentLoop");
733
734    b->CreateBr(segmentLoop);
735
736    /// DO SEGMENT LOOP
737
738    b->SetInsertPoint(segmentLoop);
739
740    // For each input buffer, get the initial processed item count, base input pointer, and the number of
741    // linearly available strides.
742    Value * numOfStrides = nullptr;
743    mInitialAvailableItemCount.resize(inputSetCount);
744    mInitialProcessedItemCount.resize(inputSetCount);
745    mStreamSetInputBaseAddress.resize(inputSetCount);
746    Value * inputStrideSize[inputSetCount];
747    for (unsigned i = 0; i < inputSetCount; i++) {
748        const auto & input = mStreamSetInputs[i];
749        const auto & name = input.getName();
750        const ProcessingRate & rate = input.getRate();
751        Value * const ic = b->getProcessedItemCount(name);
752        mInitialProcessedItemCount[i] = ic;
753        b->CreateAssert(b->CreateICmpUGE(mAvailableItemCount[i], ic), "processed item count cannot exceed the available item count");
754        assert (ic->getType() == mAvailableItemCount[i]->getType());
755        Value * const unprocessed = b->CreateSub(mAvailableItemCount[i], ic);
756        mStreamSetInputBaseAddress[i]  = b->getBlockAddress(name, b->CreateLShr(ic, LOG_2_BLOCK_WIDTH));
757        mInitialAvailableItemCount[i] = mAvailableItemCount[i];
758        mAvailableItemCount[i] = b->getLinearlyAccessibleItems(name, ic, unprocessed);
759        // Are our linearly accessible items sufficient for a stride?
760        inputStrideSize[i] = getStrideSize(b, rate);
761        Value * accessibleStrides = b->CreateUDiv(mAvailableItemCount[i], inputStrideSize[i]);
762        if (!rate.isFixed() || requiresBufferedFinalStride(input)) {
763
764            // Since we trust that the pipeline won't call this kernel unless there is enough data to process a stride, whenever
765            // we discover that there isn't enough linearly available data, optimistically copy the data to the temporary buffer.
766
767            BasicBlock * const entry = b->GetInsertBlock();
768            BasicBlock * const copyFromBack = b->CreateBasicBlock(name + "CopyFromBack");
769            BasicBlock * const copyFromFront = b->CreateBasicBlock(name + "CopyFromFront");
770            BasicBlock * const resume = b->CreateBasicBlock(name + "Resume");
771
772            b->CreateUnlikelyCondBr(b->CreateICmpEQ(accessibleStrides, ZERO), copyFromBack, resume);
773
774            b->SetInsertPoint(copyFromBack);
775            Value * const temporaryAvailable = b->CreateUMin(unprocessed, inputStrideSize[i]);
776            b->CreateAssert(b->CreateICmpULE(mAvailableItemCount[i], temporaryAvailable), "linearly available cannot be greater than temporarily available");
777            Value * const tempBufferPtr = temporaryInputBuffer[i];
778            Value * const offset = b->CreateAnd(ic, BLOCK_WIDTH_MASK);
779            const auto alignment = getItemAlignment(mStreamSetInputs[i]);
780            b->CreateStreamCpy(name, tempBufferPtr, ZERO, mStreamSetInputBaseAddress[i] , offset, mAvailableItemCount[i], alignment);
781            Value * const temporaryStrides = b->CreateSelect(b->CreateICmpULT(unprocessed, inputStrideSize[i]), ZERO, ONE);
782            BasicBlock * const copyToBackEnd = b->GetInsertBlock();
783            b->CreateCondBr(b->CreateICmpNE(mAvailableItemCount[i], temporaryAvailable), copyFromFront, resume);
784
785            b->SetInsertPoint(copyFromFront);
786            Value * const remaining = b->CreateSub(temporaryAvailable, mAvailableItemCount[i]);
787            Value * const baseAddress = b->getBaseAddress(name);
788            b->CreateStreamCpy(name, tempBufferPtr, mAvailableItemCount[i], baseAddress, ZERO, remaining, alignment);
789            BasicBlock * const copyToFrontEnd = b->GetInsertBlock();
790            b->CreateBr(resume);
791
792            b->SetInsertPoint(resume);
793            PHINode * const bufferPtr = b->CreatePHI(mStreamSetInputBaseAddress[i] ->getType(), 3);
794            bufferPtr->addIncoming(mStreamSetInputBaseAddress[i] , entry);
795            bufferPtr->addIncoming(tempBufferPtr, copyToBackEnd);
796            bufferPtr->addIncoming(tempBufferPtr, copyToFrontEnd);
797            mStreamSetInputBaseAddress[i] = bufferPtr;
798
799            PHINode * const phiAvailItemCount = b->CreatePHI(b->getSizeTy(), 3);
800            phiAvailItemCount->addIncoming(mAvailableItemCount[i], entry);
801            phiAvailItemCount->addIncoming(temporaryAvailable, copyToBackEnd);
802            phiAvailItemCount->addIncoming(temporaryAvailable, copyToFrontEnd);
803            mAvailableItemCount[i] = phiAvailItemCount;
804
805            PHINode * const phiNumOfStrides = b->CreatePHI(b->getSizeTy(), 2);
806            phiNumOfStrides->addIncoming(accessibleStrides, entry);
807            phiNumOfStrides->addIncoming(temporaryStrides, copyToBackEnd);
808            phiNumOfStrides->addIncoming(temporaryStrides, copyToFrontEnd);
809            accessibleStrides = phiNumOfStrides;
810        }
811        numOfStrides = b->CreateUMin(numOfStrides, accessibleStrides);
812    }
813
814    // Now determine the linearly writeable strides
815    Value * linearlyWritable[outputSetCount];
816    Value * baseOutputBuffer[outputSetCount];
817    Value * outputStrideSize[outputSetCount];
818    mInitialProducedItemCount.resize(outputSetCount);
819    mStreamSetOutputBaseAddress.resize(outputSetCount);
820    for (unsigned i = 0; i < outputSetCount; i++) {
821        const auto & output = mStreamSetOutputs[i];
822        const auto & name = output.getName();
823        const ProcessingRate & rate = output.getRate();
824        Value * const ic = b->getProducedItemCount(name);
825        baseOutputBuffer[i] = b->getBlockAddress(name, b->CreateLShr(ic, LOG_2_BLOCK_WIDTH));
826        assert (baseOutputBuffer[i]->getType()->isPointerTy());
827        linearlyWritable[i] = b->getLinearlyWritableItems(name, ic);
828        mInitialProducedItemCount[i] = ic;
829        outputStrideSize[i] = nullptr;
830        if (temporaryOutputBuffer[i]) {
831            outputStrideSize[i] = getStrideSize(b, rate);
832            // Is the number of linearly writable items sufficient for a stride?
833            Value * writableStrides = b->CreateUDiv(linearlyWritable[i], outputStrideSize[i]);
834            if (!rate.isFixed() || requiresBufferedFinalStride(output)) {
835                Value * const requiresCopy = b->CreateICmpEQ(writableStrides, ZERO);
836                assert (temporaryOutputBuffer[i]->getType() == baseOutputBuffer[i]->getType());
837                baseOutputBuffer[i] = b->CreateSelect(requiresCopy, temporaryOutputBuffer[i], baseOutputBuffer[i]);
838                writableStrides = b->CreateSelect(requiresCopy, ONE, writableStrides);
839            }
840            numOfStrides = b->CreateUMin(numOfStrides, writableStrides);
841            assert (temporaryOutputBuffer[i]->getType() == baseOutputBuffer[i]->getType());
842        }
843        mStreamSetOutputBaseAddress[i] = baseOutputBuffer[i];
844    }
845
846    Value * const initiallyFinal = mIsFinal;
847    if (LLVM_LIKELY(numOfStrides != nullptr)) {
848        mIsFinal = b->CreateAnd(mIsFinal, b->CreateICmpEQ(numOfStrides, ZERO));
849        Value * const processStride = b->CreateOr(b->CreateICmpNE(numOfStrides, ZERO), mIsFinal);
850        b->CreateAssert(processStride, getName() + " does not have sufficient input data or output space for one stride");
851        for (unsigned i = 0; i < inputSetCount; ++i) {
852            const ProcessingRate & rate = mStreamSetInputs[i].getRate();
853            if (rate.isFixed() && mStreamSetInputs[i].notDeferred()) {
854                mAvailableItemCount[i] = b->CreateSelect(mIsFinal, mAvailableItemCount[i], b->CreateMul(numOfStrides, inputStrideSize[i]));
855            }
856        }
857    }
858
859    //  We have one or more blocks of input data and output buffer space for all stream sets.
860    generateMultiBlockLogic(b, numOfStrides);
861
862    for (unsigned i = 0; i < inputSetCount; ++i) {
863        const ProcessingRate & rate = mStreamSetInputs[i].getRate();
864        if (rate.isFixed() && mStreamSetInputs[i].notDeferred()) {
865            Value * const ic = b->CreateAdd(mInitialProcessedItemCount[i], mAvailableItemCount[i]);
866            b->setProcessedItemCount(mStreamSetInputs[i].getName(), ic);
867        }
868    }
869
870    for (unsigned i = 0; i < outputSetCount; ++i) {
871        const ProcessingRate & rate = mStreamSetOutputs[i].getRate();
872        if (rate.isFixed()) {
873            assert (mStreamSetOutputs[i].notDeferred());
874            Value * const produced = b->CreateMul(numOfStrides, outputStrideSize[i]);
875            Value * const ic = b->CreateAdd(mInitialProducedItemCount[i], produced);
876            b->setProducedItemCount(mStreamSetOutputs[i].getName(), ic);
877        }
878    }
879
880    BasicBlock * const handleFinalBlock = b->CreateBasicBlock("HandleFinalBlock");
881    BasicBlock * const temporaryBufferCopyBack = b->CreateBasicBlock("TemporaryBufferCopyBack");
882    BasicBlock * const strideDone = b->CreateBasicBlock("MultiBlockDone");
883
884    b->CreateLikelyCondBr(b->CreateICmpNE(numOfStrides, ZERO), temporaryBufferCopyBack, handleFinalBlock);
885
886
887    /// FINAL STRIDE ADJUSTMENT
888    b->SetInsertPoint(handleFinalBlock);
889
890    // If this is our final stride, adjust the Fixed output item counts. The main loop assumes that
891    // the ITEM COUNT % FIXED RATE = 0 for all Fixed Input and Output streams. We correct that here
892    // to calculate them based on the actual input item counts.
893
894    reviseFinalProducedItemCounts(b);
895
896    b->CreateBr(temporaryBufferCopyBack);
897
898    /// TEMPORARY BUFFER COPY BACK
899    b->SetInsertPoint(temporaryBufferCopyBack);
900
901    // Copy back data to the actual output buffers.
902    for (unsigned i = 0; i < outputSetCount; i++) {
903        Value * const tempBuffer = temporaryOutputBuffer[i];
904        if (LLVM_UNLIKELY(tempBuffer == nullptr)) {
905            continue;
906        }
907        Value * const baseBuffer = baseOutputBuffer[i];
908        assert ("stack overflow" && (tempBuffer->getType() == baseBuffer->getType()));
909        const auto & name = mStreamSetOutputs[i].getName();
910        BasicBlock * const copyToBack = b->CreateBasicBlock(name + "CopyToBack");
911        BasicBlock * const copyToFront = b->CreateBasicBlock(name + "CopyToFront");
912        BasicBlock * const resume = b->CreateBasicBlock(name + "ResumeCopyBack");
913        // If we used a temporary buffer, copy it back to the original output buffer
914        b->CreateCondBr(b->CreateICmpEQ(tempBuffer, baseBuffer), copyToBack, resume);
915
916        b->SetInsertPoint(copyToBack);       
917        Value * const offset = b->CreateAnd(mInitialProducedItemCount[i], BLOCK_WIDTH_MASK);
918        Value * const newProducedItemCount = b->getProducedItemCount(name);
919        Value * const newlyProduced = b->CreateSub(newProducedItemCount, mInitialProducedItemCount[i]);
920        Value * const toWrite = b->CreateUMin(newlyProduced, linearlyWritable[i]);
921        const auto alignment = getItemAlignment(mStreamSetOutputs[i]);
922        b->CreateStreamCpy(name, baseBuffer, offset, tempBuffer, ZERO, toWrite, alignment);
923        // If we required a temporary output buffer, we will probably need to write to the beginning of the buffer as well.
924        b->CreateLikelyCondBr(b->CreateICmpULT(toWrite, newlyProduced), copyToFront, resume);
925
926        b->SetInsertPoint(copyToFront);
927        Value * const remaining = b->CreateSub(newlyProduced, toWrite);
928        Value * const baseAddress = b->getBaseAddress(name);
929        b->CreateStreamCpy(name, baseAddress, ZERO, tempBuffer, toWrite, remaining, alignment);
930        b->CreateBr(resume);
931
932        b->SetInsertPoint(resume);
933    }
934
935    strideDone->moveAfter(b->GetInsertBlock());
936
937    BasicBlock * const segmentDone = b->CreateBasicBlock("SegmentDone");
938    //  We've dealt with the partial block processing and copied information back into the
939    //  actual buffers.  If this isn't the final block, loop back for more multiblock processing.
940    if (hasNoTerminateAttribute()) {
941        b->CreateCondBr(mIsFinal, segmentDone, strideDone);
942    } else {
943        BasicBlock * const setTermination = b->CreateBasicBlock("setTermination");
944        b->CreateCondBr(mIsFinal, setTermination, strideDone);
945
946        b->SetInsertPoint(setTermination);
947        b->setTerminationSignal();
948        b->CreateBr(segmentDone);       
949    }
950
951    /// STRIDE DONE
952    b->SetInsertPoint(strideDone);
953
954    // do we have enough data for another stride?
955    Value * pendingStrides = nullptr;
956    for (unsigned i = 0; i < inputSetCount; ++i) {
957        Value * const processed = b->getProcessedItemCount(mStreamSetInputs[i].getName());
958        Value * const remaining = b->CreateSub(mInitialAvailableItemCount[i], processed);
959        Value * const remainingStrides = b->CreateUDiv(remaining, inputStrideSize[i]);
960        pendingStrides = b->CreateUMin(pendingStrides, remainingStrides);
961    }
962
963    // do we have enough room for another stride?
964    for (unsigned i = 0; i < outputSetCount; ++i) {
965        const ProcessingRate & rate = mStreamSetOutputs[i].getRate();
966        const auto & name = mStreamSetOutputs[i].getName();
967        Value * const newProduced = b->getProducedItemCount(name);
968        // If this output has a Fixed/Bounded rate, determine whether we have room for another stride.
969        if (LLVM_LIKELY(outputStrideSize[i] != nullptr)) {
970            Value * const unconsumed = b->CreateSub(newProduced, b->getConsumedItemCount(name));
971            Value * const remaining = b->CreateSub(b->getCapacity(name), unconsumed);
972            Value * const remainingStrides = b->CreateUDiv(remaining, outputStrideSize[i]);
973            pendingStrides = b->CreateUMin(pendingStrides, remainingStrides);
974        }
975        // Do copybacks if necessary.
976        if (mStreamSetOutputBuffers[i]->supportsCopyBack() && requiresCopyBack(rate)) {
977            b->CreateCopyBack(name, mInitialProducedItemCount[i], newProduced);
978        }
979    }
980
981    Value * const hasMoreStrides = b->CreateOr(b->CreateICmpNE(pendingStrides, ZERO), initiallyFinal);
982    b->CreateCondBr(hasMoreStrides, segmentLoop, segmentDone);
983
984    /// SEGMENT DONE
985    segmentDone->moveAfter(b->GetInsertBlock());
986    b->SetInsertPoint(segmentDone);
987
988}
989
990/** ------------------------------------------------------------------------------------------------------------- *
991 * @brief requiresCopyBack
992 ** ------------------------------------------------------------------------------------------------------------- */
993bool MultiBlockKernel::requiresCopyBack(const ProcessingRate & rate) const {
994    if (rate.isBounded() || rate.isUnknown()) {
995        return true;
996    } else if (rate.isRelative()) {
997        return requiresCopyBack(getBinding(rate.getReference()).getRate());
998    }
999    return false;
1000}
1001
1002/** ------------------------------------------------------------------------------------------------------------- *
1003 * @brief CreateUDivCeil
1004 ** ------------------------------------------------------------------------------------------------------------- */
1005inline Value * CreateUDivCeil(const std::unique_ptr<KernelBuilder> & b, Value * const number, const ProcessingRate::RateValue divisor, const Twine & Name = "") {
1006    Constant * const n = ConstantInt::get(number->getType(), divisor.numerator());
1007    if (LLVM_LIKELY(divisor.denominator() == 1)) {
1008        return b->CreateUDivCeil(number, n, Name);
1009    } else {
1010        //   âŒŠ(num + ratio - 1) / ratio⌋
1011        // = ⌊(num - 1) / (n/d)⌋ + (ratio/ratio)
1012        // = ⌊(d * (num - 1)) / n⌋ + 1
1013        Constant * const ONE = ConstantInt::get(number->getType(), 1);
1014        Constant * const d = ConstantInt::get(number->getType(), divisor.denominator());
1015        return b->CreateAdd(b->CreateUDiv(b->CreateMul(b->CreateSub(number, ONE), d), n), ONE, Name);
1016    }
1017}
1018
1019
1020/** ------------------------------------------------------------------------------------------------------------- *
1021 * @brief reviseFinalProducedItemCounts
1022 ** ------------------------------------------------------------------------------------------------------------- */
1023void MultiBlockKernel::reviseFinalProducedItemCounts(const std::unique_ptr<KernelBuilder> & b) {
1024
1025    if (LLVM_UNLIKELY(mStreamSetInputs.empty())) {
1026        return;
1027    }
1028
1029    const auto inputSetCount = mStreamSetInputs.size();
1030
1031    ProcessingRate::RateValue rateLCM(1);
1032    unsigned first = 0;
1033    unsigned last = inputSetCount;
1034
1035    for (unsigned i = 0; i < inputSetCount; ++i) {
1036        const ProcessingRate & pr = mStreamSetInputs[i].getRate();
1037        if (pr.isFixed()) {
1038            rateLCM = lcm(rateLCM, pr.getRate());
1039            if (mStreamSetInputs[i].isPrincipal()) {
1040                assert ("A kernel cannot have multiple principle input streams" && (first == 0 && last == inputSetCount));
1041                first = i;
1042                last = i + 1;
1043            }
1044        }       
1045    }
1046
1047    bool noFixedRateOutput = true;
1048
1049    for (const Binding & output : mStreamSetOutputs) {
1050        const ProcessingRate & pr = output.getRate();
1051        if (pr.isFixed()) {
1052            rateLCM = lcm(rateLCM, pr.getRate());
1053            noFixedRateOutput = false;
1054        }
1055    }
1056
1057    if (noFixedRateOutput) {
1058        return;
1059    }
1060
1061    Value * baseInitialProcessedItemCount = nullptr;
1062    Value * scaledInverseOfAvailItemCount = nullptr;
1063
1064    // For each Fixed output stream, this calculates:
1065
1066    //    CEILING(MIN(Available Item Count / Fixed Input Rate) * Fixed Output Rate)
1067
1068    // But avoids the possibility of overflow errors (assuming that each processed item count does not overflow)
1069
1070    for (unsigned i = first; i < last; ++i) {
1071        const ProcessingRate & pr = mStreamSetInputs[i].getRate();
1072        if (pr.isFixed()) {
1073            Value * p = mInitialProcessedItemCount[i];
1074            Value * a = b->CreateSub(mInitialAvailableItemCount[i], p);
1075            const auto & rate = pr.getRate();
1076            if (LLVM_UNLIKELY(rateLCM != rate)) {
1077                const auto factor = rateLCM / rate;
1078                if (LLVM_UNLIKELY(factor.numerator() > 1)) {
1079                    a = b->CreateMul(a, b->getSize(factor.numerator()));
1080                }
1081                if (LLVM_UNLIKELY(factor.denominator() > 1)) {
1082                    a = b->CreateUDiv(a, b->getSize(factor.denominator()));
1083                }
1084            }
1085            if (LLVM_UNLIKELY(rate.denominator() > 1)) {
1086                p = b->CreateMul(p, b->getSize(rate.denominator()));
1087            }
1088            if (LLVM_UNLIKELY(rate.numerator() > 1)) {
1089                p = b->CreateUDiv(p, b->getSize(rate.numerator()));
1090            }
1091            if (scaledInverseOfAvailItemCount) {
1092                scaledInverseOfAvailItemCount = b->CreateUMin(scaledInverseOfAvailItemCount, a);
1093                baseInitialProcessedItemCount = b->CreateUMin(baseInitialProcessedItemCount, p);
1094            } else {
1095                scaledInverseOfAvailItemCount = a;
1096                baseInitialProcessedItemCount = p;
1097            }
1098        }
1099    }
1100
1101    for (const Binding & output : mStreamSetOutputs) {
1102        const auto name = output.getName();
1103        const ProcessingRate & pr = output.getRate();
1104        Value * produced = nullptr;
1105        if (pr.isFixed() && output.notDeferred()) {
1106            assert (baseInitialProcessedItemCount && scaledInverseOfAvailItemCount);
1107            const auto rate = pr.getRate();
1108            Value * p = baseInitialProcessedItemCount;
1109            if (LLVM_UNLIKELY(rate.numerator() != 1)) {
1110                p = b->CreateMul(p, b->getSize(rate.numerator()));
1111            }
1112            if (LLVM_UNLIKELY(rate.denominator() != 1)) {
1113                p = b->CreateUDiv(p, b->getSize(rate.denominator()));
1114            }
1115            Value * const ic = CreateUDivCeil(b, scaledInverseOfAvailItemCount, rateLCM / pr.getRate());
1116            produced = b->CreateAdd(p, ic);
1117        } else { // check if we have an attribute; if so, get the current produced count and adjust it
1118            bool noAttributes = true;
1119            for (const Attribute & attr : output.getAttributes()) {
1120                if (attr.isAdd() || attr.isRoundUpTo()) {
1121                    noAttributes = false;
1122                    break;
1123                }
1124            }
1125            if (noAttributes) {
1126                continue;
1127            }
1128            produced = b->getProducedItemCount(name);
1129        }
1130        for (const Attribute & attr : output.getAttributes()) {
1131            if (attr.isAdd()) {
1132                produced = b->CreateAdd(produced, b->getSize(attr.getAmount()));
1133            } else if (attr.isRoundUpTo()) {
1134                produced = b->CreateRoundUp(produced, b->getSize(attr.getAmount()));
1135            }
1136        }
1137        b->setProducedItemCount(name, produced);
1138    }
1139
1140}
1141
1142/** ------------------------------------------------------------------------------------------------------------- *
1143 * @brief generateMultiBlockLogic
1144 ** ------------------------------------------------------------------------------------------------------------- */
1145Value * BlockOrientedKernel::generateMultiBlockLogic(const std::unique_ptr<KernelBuilder> & b, Value * const numOfBlocks) {
1146
1147    if (LLVM_UNLIKELY(mStride != b->getBitBlockWidth())) {
1148        report_fatal_error(getName() + ": the Stride (" + std::to_string(mStride) + ") of BlockOrientedKernel "
1149                           "equal to the BitBlockWidth (" + std::to_string(b->getBitBlockWidth()) + ")");
1150    }
1151
1152    Constant * const LOG_2_BLOCK_WIDTH = b->getSize(std::log2(b->getBitBlockWidth()));
1153
1154    BasicBlock * const entryBlock = b->GetInsertBlock();
1155    mStrideLoopBody = b->CreateBasicBlock(getName() + "_strideLoopBody");
1156    BasicBlock * const stridesDone = b->CreateBasicBlock(getName() + "_stridesDone");
1157    BasicBlock * const doFinalBlock = b->CreateBasicBlock(getName() + "_doFinalBlock");
1158    BasicBlock * const segmentDone = b->CreateBasicBlock(getName() + "_segmentDone");
1159    b->CreateAssert(b->CreateXor(b->CreateIsNotNull(numOfBlocks), mIsFinal),
1160                    "numOfStrides cannot be 0 unless this is the final stride and must be 0 if it is");
1161    const auto inputSetCount = mStreamSetInputs.size();
1162    Value * baseProcessedIndex[inputSetCount];
1163    Value * baseInputAddress[inputSetCount];
1164    for (unsigned i = 0; i < inputSetCount; i++) {
1165        const ProcessingRate & rate = mStreamSetInputs[i].getRate();
1166        if (LLVM_UNLIKELY(!rate.isFixed())) {
1167            Value * const ic = mInitialProcessedItemCount[i];
1168            baseProcessedIndex[i] = b->CreateLShr(ic, LOG_2_BLOCK_WIDTH);
1169        }
1170        baseInputAddress[i] = mStreamSetInputBaseAddress[i];
1171    }
1172
1173    const auto outputSetCount = mStreamSetOutputs.size();
1174    Value * baseProducedIndex[outputSetCount];
1175    Value * baseOutputAddress[inputSetCount];
1176    for (unsigned i = 0; i < outputSetCount; i++) {
1177        const ProcessingRate & rate = mStreamSetOutputs[i].getRate();
1178        if (LLVM_UNLIKELY(!rate.isFixed())) {
1179            Value * const ic = b->getProducedItemCount(mStreamSetOutputs[i].getName());
1180            baseProducedIndex[i] = b->CreateLShr(ic, LOG_2_BLOCK_WIDTH);
1181        }
1182        baseOutputAddress[i] = mStreamSetOutputBaseAddress[i];
1183    }
1184
1185    b->CreateUnlikelyCondBr(mIsFinal, doFinalBlock, mStrideLoopBody);
1186
1187    /// BLOCK BODY
1188
1189    b->SetInsertPoint(mStrideLoopBody);
1190
1191    if (b->supportsIndirectBr()) {
1192        Value * const baseTarget = BlockAddress::get(segmentDone);
1193        mStrideLoopTarget = b->CreatePHI(baseTarget->getType(), 2, "strideTarget");
1194        mStrideLoopTarget->addIncoming(baseTarget, entryBlock);
1195    }
1196
1197    mStrideBlockIndex = b->CreatePHI(b->getSizeTy(), 2);
1198    mStrideBlockIndex->addIncoming(b->getSize(0), entryBlock);
1199
1200    /// GENERATE DO BLOCK METHOD
1201
1202    for (unsigned i = 0; i < inputSetCount; ++i) {
1203        Value * index = mStrideBlockIndex;
1204        const ProcessingRate & rate = mStreamSetInputs[i].getRate();
1205        if (LLVM_UNLIKELY(!rate.isFixed())) {
1206            Value * ic = b->getProcessedItemCount(mStreamSetInputs[i].getName());
1207            index = b->CreateSub(b->CreateLShr(ic, LOG_2_BLOCK_WIDTH), baseProcessedIndex[i]);
1208        }
1209        mStreamSetInputBaseAddress[i] = b->CreateGEP(mStreamSetInputBaseAddress[i], index);
1210    }
1211
1212    for (unsigned i = 0; i < outputSetCount; ++i) {
1213        Value * index = mStrideBlockIndex;
1214        const ProcessingRate & rate = mStreamSetOutputs[i].getRate();
1215        if (LLVM_UNLIKELY(!rate.isFixed())) {
1216            Value * ic = b->getProducedItemCount(mStreamSetOutputs[i].getName());
1217            index = b->CreateSub(b->CreateLShr(ic, LOG_2_BLOCK_WIDTH), baseProducedIndex[i]);
1218        }
1219        mStreamSetOutputBaseAddress[i] = b->CreateGEP(mStreamSetOutputBaseAddress[i], index);
1220    }
1221
1222    writeDoBlockMethod(b);
1223
1224    BasicBlock * const bodyEnd = b->GetInsertBlock();
1225    if (mStrideLoopTarget) {
1226        mStrideLoopTarget->addIncoming(mStrideLoopTarget, bodyEnd);
1227    }
1228
1229    Value * const nextIndex = b->CreateAdd(mStrideBlockIndex, b->getSize(1));
1230    mStrideBlockIndex->addIncoming(nextIndex, bodyEnd);
1231    Value * const notDone = b->CreateICmpULT(nextIndex, numOfBlocks);
1232    b->CreateCondBr(notDone, mStrideLoopBody, stridesDone);
1233
1234    stridesDone->moveAfter(bodyEnd);
1235
1236    /// STRIDE DONE
1237
1238    b->SetInsertPoint(stridesDone);
1239
1240    // Now conditionally perform the final block processing depending on the doFinal parameter.
1241    if (mStrideLoopTarget) {
1242        mStrideLoopBranch = b->CreateIndirectBr(mStrideLoopTarget, 3);
1243        mStrideLoopBranch->addDestination(doFinalBlock);
1244        mStrideLoopBranch->addDestination(segmentDone);
1245    } else {
1246        b->CreateUnlikelyCondBr(mIsFinal, doFinalBlock, segmentDone);
1247    }
1248
1249    doFinalBlock->moveAfter(stridesDone);
1250
1251    /// DO FINAL BLOCK
1252
1253    b->SetInsertPoint(doFinalBlock);
1254    for (unsigned i = 0; i < inputSetCount; ++i) {
1255        mStreamSetInputBaseAddress[i] = baseInputAddress[i];
1256    }
1257
1258    for (unsigned i = 0; i < outputSetCount; ++i) {
1259        mStreamSetOutputBaseAddress[i] = baseOutputAddress[i];
1260    }
1261
1262    writeFinalBlockMethod(b, getRemainingItems(b));
1263
1264    b->CreateBr(segmentDone);
1265
1266    segmentDone->moveAfter(b->GetInsertBlock());
1267
1268    b->SetInsertPoint(segmentDone);
1269
1270    // Update the branch prediction metadata to indicate that the likely target will be segmentDone
1271    if (mStrideLoopTarget) {
1272        MDBuilder mdb(b->getContext());
1273        const auto destinations = mStrideLoopBranch->getNumDestinations();
1274        uint32_t weights[destinations];
1275        for (unsigned i = 0; i < destinations; ++i) {
1276            weights[i] = (mStrideLoopBranch->getDestination(i) == segmentDone) ? 100 : 1;
1277        }
1278        ArrayRef<uint32_t> bw(weights, destinations);
1279        mStrideLoopBranch->setMetadata(LLVMContext::MD_prof, mdb.createBranchWeights(bw));
1280    }
1281
1282    return numOfBlocks;
1283}
1284
1285/** ------------------------------------------------------------------------------------------------------------- *
1286 * @brief getRemainingItems
1287 ** ------------------------------------------------------------------------------------------------------------- */
1288Value * BlockOrientedKernel::getRemainingItems(const std::unique_ptr<KernelBuilder> & b) {
1289    Value * remainingItems = nullptr;
1290    const auto count = mStreamSetInputs.size();
1291    if (count == 1) {
1292        return mAvailableItemCount[0];
1293    } else {
1294        for (unsigned i = 0; i < count; i++) {
1295            if (mStreamSetInputs[i].isPrincipal()) {
1296                return mAvailableItemCount[i];
1297            }
1298        }
1299        for (unsigned i = 0; i < count; ++i) {
1300            const ProcessingRate & r = mStreamSetInputs[i].getRate();
1301            if (r.isFixed()) {
1302                Value * ic = CreateUDivCeil(b, mAvailableItemCount[i], r.getRate());
1303                if (remainingItems) {
1304                    remainingItems = b->CreateUMin(remainingItems, ic);
1305                } else {
1306                    remainingItems = ic;
1307                }
1308            }
1309        }
1310    }
1311    return remainingItems;
1312}
1313
1314/** ------------------------------------------------------------------------------------------------------------- *
1315 * @brief writeDoBlockMethod
1316 ** ------------------------------------------------------------------------------------------------------------- */
1317inline void BlockOrientedKernel::writeDoBlockMethod(const std::unique_ptr<KernelBuilder> & b) {
1318
1319    Value * const self = getInstance();
1320    Function * const cp = mCurrentMethod;
1321    auto ip = b->saveIP();
1322    std::vector<Value *> availableItemCount(0);
1323
1324    /// Check if the do block method is called and create the function if necessary
1325    if (!b->supportsIndirectBr()) {
1326
1327        std::vector<Type *> params;
1328        params.reserve(1 + mAvailableItemCount.size());
1329        params.push_back(self->getType());
1330        for (Value * avail : mAvailableItemCount) {
1331            params.push_back(avail->getType());
1332        }
1333
1334        FunctionType * const type = FunctionType::get(b->getVoidTy(), params, false);
1335        mCurrentMethod = Function::Create(type, GlobalValue::InternalLinkage, getName() + DO_BLOCK_SUFFIX, b->getModule());
1336        mCurrentMethod->setCallingConv(CallingConv::C);
1337        mCurrentMethod->setDoesNotThrow();
1338        auto args = mCurrentMethod->arg_begin();
1339        args->setName("self");
1340        setInstance(&*args);
1341        availableItemCount.reserve(mAvailableItemCount.size());
1342        while (++args != mCurrentMethod->arg_end()) {
1343            availableItemCount.push_back(&*args);
1344        }
1345        assert (availableItemCount.size() == mAvailableItemCount.size());
1346        mAvailableItemCount.swap(availableItemCount);
1347        b->SetInsertPoint(BasicBlock::Create(b->getContext(), "entry", mCurrentMethod));
1348    }
1349
1350    generateDoBlockMethod(b); // must be implemented by the BlockOrientedKernelBuilder subtype
1351
1352    if (!b->supportsIndirectBr()) {
1353        // Restore the DoSegment function state then call the DoBlock method
1354        b->CreateRetVoid();
1355        mDoBlockMethod = mCurrentMethod;
1356        b->restoreIP(ip);
1357        setInstance(self);
1358        mCurrentMethod = cp;
1359        mAvailableItemCount.swap(availableItemCount);
1360        CreateDoBlockMethodCall(b);
1361    }
1362
1363}
1364
1365/** ------------------------------------------------------------------------------------------------------------- *
1366 * @brief writeFinalBlockMethod
1367 ** ------------------------------------------------------------------------------------------------------------- */
1368inline void BlockOrientedKernel::writeFinalBlockMethod(const std::unique_ptr<KernelBuilder> & b, Value * remainingItems) {
1369
1370    Value * const self = getInstance();
1371    Function * const cp = mCurrentMethod;
1372    Value * const remainingItemCount = remainingItems;
1373    auto ip = b->saveIP();
1374    std::vector<Value *> availableItemCount(0);
1375
1376    if (!b->supportsIndirectBr()) {
1377        std::vector<Type *> params;
1378        params.reserve(2 + mAvailableItemCount.size());
1379        params.push_back(self->getType());
1380        params.push_back(b->getSizeTy());
1381        for (Value * avail : mAvailableItemCount) {
1382            params.push_back(avail->getType());
1383        }
1384        FunctionType * const type = FunctionType::get(b->getVoidTy(), params, false);
1385        mCurrentMethod = Function::Create(type, GlobalValue::InternalLinkage, getName() + FINAL_BLOCK_SUFFIX, b->getModule());
1386        mCurrentMethod->setCallingConv(CallingConv::C);
1387        mCurrentMethod->setDoesNotThrow();
1388        auto args = mCurrentMethod->arg_begin();
1389        args->setName("self");
1390        setInstance(&*args);
1391        remainingItems = &*(++args);
1392        remainingItems->setName("remainingItems");
1393        availableItemCount.reserve(mAvailableItemCount.size());
1394        while (++args != mCurrentMethod->arg_end()) {
1395            availableItemCount.push_back(&*args);
1396        }
1397        assert (availableItemCount.size() == mAvailableItemCount.size());
1398        mAvailableItemCount.swap(availableItemCount);
1399        b->SetInsertPoint(BasicBlock::Create(b->getContext(), "entry", mCurrentMethod));
1400    }
1401
1402    generateFinalBlockMethod(b, remainingItems); // may be implemented by the BlockOrientedKernel subtype
1403
1404    if (!b->supportsIndirectBr()) {
1405        b->CreateRetVoid();
1406        b->restoreIP(ip);
1407        setInstance(self);
1408        mAvailableItemCount.swap(availableItemCount);
1409        // Restore the DoSegment function state then call the DoFinal method
1410        std::vector<Value *> args;
1411        args.reserve(2 + mAvailableItemCount.size());
1412        args.push_back(self);
1413        args.push_back(remainingItemCount);
1414        args.insert(args.end(), mAvailableItemCount.begin(), mAvailableItemCount.end());
1415        b->CreateCall(mCurrentMethod, args);
1416        mCurrentMethod = cp;
1417    }
1418
1419}
1420
1421/** ------------------------------------------------------------------------------------------------------------- *
1422 * @brief generateFinalBlockMethod
1423 ** ------------------------------------------------------------------------------------------------------------- */
1424void BlockOrientedKernel::generateFinalBlockMethod(const std::unique_ptr<KernelBuilder> & b, Value * /* remainingItems */) {
1425    //  The default finalBlock method simply dispatches to the doBlock routine.
1426    CreateDoBlockMethodCall(b);
1427}
1428
1429void BlockOrientedKernel::CreateDoBlockMethodCall(const std::unique_ptr<KernelBuilder> & b) {
1430    if (b->supportsIndirectBr()) {
1431        BasicBlock * const bb = b->CreateBasicBlock("resume");
1432        mStrideLoopBranch->addDestination(bb);
1433        BasicBlock * const current = b->GetInsertBlock();
1434        mStrideLoopTarget->addIncoming(BlockAddress::get(bb), current);
1435        mStrideBlockIndex->addIncoming(b->getSize(0), current);
1436        b->CreateBr(mStrideLoopBody);
1437        bb->moveAfter(current);
1438        b->SetInsertPoint(bb);
1439    } else {
1440        std::vector<Value *> args;
1441        args.reserve(1 + mAvailableItemCount.size());
1442        args.push_back(getInstance());
1443        args.insert(args.end(), mAvailableItemCount.begin(), mAvailableItemCount.end());
1444        b->CreateCall(mDoBlockMethod, args);
1445    }
1446}
1447
1448static inline std::string annotateKernelNameWithDebugFlags(std::string && name) {
1449    if (LLVM_UNLIKELY(codegen::DebugOptionIsSet(codegen::EnableAsserts))) {
1450        name += "_EA";
1451    }
1452    name += "_O" + std::to_string((int)codegen::OptLevel);
1453    return name;
1454}
1455
1456// CONSTRUCTOR
1457Kernel::Kernel(std::string && kernelName,
1458               Bindings && stream_inputs,
1459               Bindings && stream_outputs,
1460               Bindings && scalar_parameters,
1461               Bindings && scalar_outputs,
1462               Bindings && internal_scalars)
1463: KernelInterface(annotateKernelNameWithDebugFlags(std::move(kernelName))
1464                  , std::move(stream_inputs), std::move(stream_outputs)
1465                  , std::move(scalar_parameters), std::move(scalar_outputs)
1466                  , std::move(internal_scalars))
1467, mCurrentMethod(nullptr)
1468, mAvailablePrincipalItemCount(nullptr)
1469, mNoTerminateAttribute(false)
1470, mIsGenerated(false)
1471, mStride(0)
1472, mIsFinal(nullptr)
1473, mOutputScalarResult(nullptr) {
1474
1475}
1476
1477Kernel::~Kernel() {
1478
1479}
1480
1481// MULTI-BLOCK KERNEL CONSTRUCTOR
1482MultiBlockKernel::MultiBlockKernel(std::string && kernelName,
1483                                   Bindings && stream_inputs,
1484                                   Bindings && stream_outputs,
1485                                   Bindings && scalar_parameters,
1486                                   Bindings && scalar_outputs,
1487                                   Bindings && internal_scalars)
1488: Kernel(std::move(kernelName), std::move(stream_inputs), std::move(stream_outputs), std::move(scalar_parameters), std::move(scalar_outputs), std::move(internal_scalars)) {
1489
1490}
1491
1492// CONSTRUCTOR
1493BlockOrientedKernel::BlockOrientedKernel(std::string && kernelName,
1494                                         Bindings && stream_inputs,
1495                                         Bindings && stream_outputs,
1496                                         Bindings && scalar_parameters,
1497                                         Bindings && scalar_outputs,
1498                                         Bindings && internal_scalars)
1499: MultiBlockKernel(std::move(kernelName), std::move(stream_inputs), std::move(stream_outputs), std::move(scalar_parameters), std::move(scalar_outputs), std::move(internal_scalars))
1500, mDoBlockMethod(nullptr)
1501, mStrideLoopBody(nullptr)
1502, mStrideLoopBranch(nullptr)
1503, mStrideLoopTarget(nullptr)
1504, mStrideBlockIndex(nullptr) {
1505
1506}
1507
1508// CONSTRUCTOR
1509SegmentOrientedKernel::SegmentOrientedKernel(std::string && kernelName,
1510                                             Bindings && stream_inputs,
1511                                             Bindings && stream_outputs,
1512                                             Bindings && scalar_parameters,
1513                                             Bindings && scalar_outputs,
1514                                             Bindings && internal_scalars)
1515: Kernel(std::move(kernelName), std::move(stream_inputs), std::move(stream_outputs), std::move(scalar_parameters), std::move(scalar_outputs), std::move(internal_scalars)) {
1516
1517}
1518
1519
1520}
Note: See TracBrowser for help on using the repository browser.