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

Last change on this file since 5743 was 5743, checked in by cameron, 17 months ago

Propagate target triple and data layout in kernel module creation

File size: 66.9 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_rt.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(mKernelMap.count(name))) {
53        report_fatal_error(getName() + " already contains scalar field " + name);
54    }
55    const auto index = mKernelFields.size();
56    mKernelMap.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 getItemsPerStride
213 ** ------------------------------------------------------------------------------------------------------------- */
214std::pair<unsigned, unsigned> Kernel::getStreamRate(const Port p, const unsigned i) const {
215    const ProcessingRate & rate = (p == Port::Input) ? mStreamSetInputs[i].getRate() : mStreamSetOutputs[i].getRate();
216    unsigned min = 0, max = 0;
217    if (rate.isFixed()) {
218        min = max = rate.getRate();
219    } else if (rate.isBounded()) {
220        min = rate.getLowerBound();
221        max = rate.getUpperBound();
222    } else if (rate.isUnknown()) {
223        min = rate.getLowerBound();
224        max = 0;
225    } else if (rate.isExactlyRelative()) {
226        for (unsigned j = 0; j < mStreamSetInputs.size(); ++j) {
227            if (mStreamSetInputs[j].getName() == rate.getReference()) {
228                std::tie(min, max) = getStreamRate(Port::Input, j);
229                min = (min * rate.getNumerator()) / rate.getDenominator();
230                assert (max == 0 || (max * rate.getNumerator()) % rate.getDenominator() == 0);
231                max = (max * rate.getNumerator()) / rate.getDenominator();
232                return std::make_pair(min, max);
233            }
234        }
235        for (unsigned j = 0; j < mStreamSetOutputs.size(); ++j) {
236            if (mStreamSetOutputs[j].getName() == rate.getReference()) {
237                assert (p == Port::Output);
238                std::tie(min, max) = getStreamRate(Port::Output, j);
239                min = (min * rate.getNumerator()) / rate.getDenominator();
240                assert (max == 0 || (max * rate.getNumerator()) % rate.getDenominator() == 0);
241                max = (max * rate.getNumerator()) / rate.getDenominator();
242                return std::make_pair(min, max);
243            }
244        }
245        llvm_unreachable("Reference rate must be associated with an input or output!");
246    }
247    return std::make_pair(min, max);
248}
249
250/** ------------------------------------------------------------------------------------------------------------- *
251 * @brief addBaseKernelProperties
252 ** ------------------------------------------------------------------------------------------------------------- */
253void Kernel::addBaseKernelProperties(const std::unique_ptr<KernelBuilder> & idb) {
254   
255    const unsigned inputSetCount = mStreamSetInputs.size();
256    const unsigned outputSetCount = mStreamSetOutputs.size();
257   
258    assert (inputSetCount == mStreamSetInputBuffers.size());
259    assert (outputSetCount == mStreamSetOutputBuffers.size());
260
261    if (mStride == 0) {
262        // Set the default kernel stride.
263        mStride = idb->getBitBlockWidth();
264    }
265
266    IntegerType * const sizeTy = idb->getSizeTy();
267
268    for (unsigned i = 0; i < inputSetCount; i++) {
269        const Binding & b = mStreamSetInputs[i];
270        //const ProcessingRate & rate = b.getRate();
271        //if (rate.isBounded() || rate.isUnknown()) {
272            addScalar(sizeTy, b.getName() + PROCESSED_ITEM_COUNT_SUFFIX);
273        //}
274    }
275
276    for (unsigned i = 0; i < outputSetCount; i++) {
277        const Binding & b = mStreamSetOutputs[i];
278        //const ProcessingRate & rate = b.getRate();
279        //if (rate.isBounded() || rate.isUnknown()) {
280            addScalar(sizeTy, b.getName() + PRODUCED_ITEM_COUNT_SUFFIX);
281        //}
282    }
283
284    for (unsigned i = 0; i < inputSetCount; i++) {
285        mScalarInputs.emplace_back(mStreamSetInputBuffers[i]->getStreamSetHandle()->getType(), mStreamSetInputs[i].getName() + BUFFER_PTR_SUFFIX);
286    }
287    for (unsigned i = 0; i < outputSetCount; i++) {
288        mScalarInputs.emplace_back(mStreamSetOutputBuffers[i]->getStreamSetHandle()->getType(), mStreamSetOutputs[i].getName() + BUFFER_PTR_SUFFIX);
289    }
290    for (const auto & binding : mScalarInputs) {
291        addScalar(binding.getType(), binding.getName());
292    }
293    for (const auto & binding : mScalarOutputs) {
294        addScalar(binding.getType(), binding.getName());
295    }
296    if (mStreamMap.empty()) {
297        prepareStreamSetNameMap();
298    }
299    for (const auto & binding : mInternalScalars) {
300        addScalar(binding.getType(), binding.getName());
301    }
302    Type * const consumerSetTy = StructType::get(idb->getContext(), {sizeTy, sizeTy->getPointerTo()->getPointerTo()})->getPointerTo();
303    for (unsigned i = 0; i < mStreamSetOutputs.size(); i++) {
304        addScalar(consumerSetTy, mStreamSetOutputs[i].getName() + CONSUMER_SUFFIX);
305    }
306    addScalar(sizeTy, LOGICAL_SEGMENT_NO_SCALAR);
307    addScalar(idb->getInt1Ty(), TERMINATION_SIGNAL);
308    for (unsigned i = 0; i < mStreamSetOutputs.size(); i++) {
309        addScalar(sizeTy, mStreamSetOutputs[i].getName() + CONSUMED_ITEM_COUNT_SUFFIX);
310    }
311    // We compile in a 64-bit CPU cycle counter into every kernel.   It will remain unused
312    // in normal execution, but when codegen::EnableCycleCounter is specified, pipelines
313    // will be able to add instrumentation to cached modules without recompilation.
314    addScalar(idb->getInt64Ty(), CYCLECOUNT_SCALAR);
315
316}
317
318
319/** ------------------------------------------------------------------------------------------------------------- *
320 * @brief makeSignature
321 *
322 * Default kernel signature: generate the IR and emit as byte code.
323 ** ------------------------------------------------------------------------------------------------------------- */
324std::string Kernel::makeSignature(const std::unique_ptr<kernel::KernelBuilder> & idb) {
325    assert ("KernelBuilder does not have a valid IDISA Builder" && idb.get());
326    if (LLVM_UNLIKELY(hasSignature())) {
327        generateKernel(idb);
328        std::string signature;
329        raw_string_ostream OS(signature);
330        WriteBitcodeToFile(getModule(), OS);
331        return signature;
332    } else {
333        return getModule()->getModuleIdentifier();
334    }
335}
336
337
338/** ------------------------------------------------------------------------------------------------------------- *
339 * @brief generateKernel
340 ** ------------------------------------------------------------------------------------------------------------- */
341void Kernel::generateKernel(const std::unique_ptr<kernel::KernelBuilder> & idb) {
342    assert ("KernelBuilder does not have a valid IDISA Builder" && idb.get());
343    // If the module id cannot uniquely identify this kernel, "generateKernelSignature()" will have already
344    // generated the unoptimized IR.
345    if (!mIsGenerated) {
346        const auto m = idb->getModule();
347        const auto ip = idb->saveIP();
348        // const auto saveInstance = getInstance();
349        idb->setModule(mModule);
350        addKernelDeclarations(idb);
351        callGenerateInitializeMethod(idb);
352        callGenerateDoSegmentMethod(idb);
353        callGenerateFinalizeMethod(idb);
354        // setInstance(saveInstance);
355        idb->setModule(m);
356        idb->restoreIP(ip);
357        mIsGenerated = true;
358    }
359}
360
361
362/** ------------------------------------------------------------------------------------------------------------- *
363 * @brief callGenerateInitializeMethod
364 ** ------------------------------------------------------------------------------------------------------------- */
365inline void Kernel::callGenerateInitializeMethod(const std::unique_ptr<kernel::KernelBuilder> & idb) {
366    mCurrentMethod = getInitFunction(idb->getModule());
367    idb->SetInsertPoint(BasicBlock::Create(idb->getContext(), "entry", mCurrentMethod));
368    Function::arg_iterator args = mCurrentMethod->arg_begin();
369    setInstance(&*(args++));
370    idb->CreateStore(ConstantAggregateZero::get(mKernelStateType), getInstance());
371    for (const auto & binding : mScalarInputs) {
372        idb->setScalarField(binding.getName(), &*(args++));
373    }
374    for (const auto & binding : mStreamSetOutputs) {
375        idb->setConsumerLock(binding.getName(), &*(args++));
376    }
377    generateInitializeMethod(idb);
378    idb->CreateRetVoid();
379}
380
381/** ------------------------------------------------------------------------------------------------------------- *
382 * @brief callGenerateDoSegmentMethod
383 ** ------------------------------------------------------------------------------------------------------------- */
384inline void Kernel::callGenerateDoSegmentMethod(const std::unique_ptr<kernel::KernelBuilder> & idb) {
385    mCurrentMethod = getDoSegmentFunction(idb->getModule());
386    idb->SetInsertPoint(BasicBlock::Create(idb->getContext(), "entry", mCurrentMethod));
387    auto args = mCurrentMethod->arg_begin();
388    setInstance(&*(args++));
389    mIsFinal = &*(args++);
390    mAvailablePrincipleItemCount = nullptr;
391//    if (mHasPrincipleItemCount) {
392//        mAvailablePrincipleItemCount = &*(args++);
393//    }
394    const auto n = mStreamSetInputs.size();
395    mAvailableItemCount.resize(n, nullptr);
396    for (unsigned i = 0; i < n; i++) {
397//        const ProcessingRate & rate = mStreamSetInputs[i].getRate();
398//        Value * itemCount = nullptr;
399//        if (rate.isFixed()) {
400//            itemCount = mAvailablePrincipleItemCount;
401//            if (rate.getRate() != 1) {
402//                itemCount = idb->CreateMul(itemCount, ConstantInt::get(itemCount->getType(), rate.getRate()));
403//            }
404//        } else if (rate.isBounded() || rate.isUnknown()) {
405//            itemCount = &*(args++);
406//        } else if (rate.isRelative()) {
407//            for (unsigned j = 0; j < i; ++j) {
408//                if (mStreamSetInputs[j].getName() == rate.getReference()) {
409//                    itemCount = mAvailableItemCount[j];
410//                    break;
411//                }
412//            }
413//            if (LLVM_UNLIKELY(itemCount == nullptr)) {
414//                report_fatal_error(mStreamSetInputs[i].getName() + " is declared before " + rate.getReference());
415//            }
416//            if (rate.getNumerator() != 1) {
417//                itemCount = idb->CreateMul(itemCount, ConstantInt::get(itemCount->getType(), rate.getNumerator()));
418//            }
419//            if (rate.getDenominator() != 1) {
420//                itemCount = idb->CreateUDiv(itemCount, ConstantInt::get(itemCount->getType(), rate.getDenominator()));
421//            }
422//        }
423//        assert (itemCount);
424//        mAvailableItemCount[i] = itemCount;
425
426        assert (args != mCurrentMethod->arg_end());
427        mAvailableItemCount[i] = &*(args++);
428    }
429    assert (args == mCurrentMethod->arg_end());
430
431    generateKernelMethod(idb); // must be overridden by the Kernel subtype
432    mIsFinal = nullptr;
433    mAvailableItemCount.clear();
434    idb->CreateRetVoid();
435}
436
437
438/** ------------------------------------------------------------------------------------------------------------- *
439 * @brief callGenerateFinalizeMethod
440 ** ------------------------------------------------------------------------------------------------------------- */
441inline void Kernel::callGenerateFinalizeMethod(const std::unique_ptr<KernelBuilder> & idb) {
442    mCurrentMethod = getTerminateFunction(idb->getModule());
443    idb->SetInsertPoint(BasicBlock::Create(idb->getContext(), "entry", mCurrentMethod));
444    auto args = mCurrentMethod->arg_begin();
445    setInstance(&*(args++));
446    generateFinalizeMethod(idb); // may be overridden by the Kernel subtype
447    const auto n = mScalarOutputs.size();
448    if (n == 0) {
449        idb->CreateRetVoid();
450    } else {
451        Value * outputs[n];
452        for (unsigned i = 0; i < n; ++i) {
453            outputs[i] = idb->getScalarField(mScalarOutputs[i].getName());
454        }
455        if (n == 1) {
456            idb->CreateRet(outputs[0]);
457        } else {
458            idb->CreateAggregateRet(outputs, n);
459        }
460    }
461}
462
463
464/** ------------------------------------------------------------------------------------------------------------- *
465 * @brief getScalarIndex
466 ** ------------------------------------------------------------------------------------------------------------- */
467unsigned Kernel::getScalarIndex(const std::string & name) const {
468    const auto f = mKernelMap.find(name);
469    if (LLVM_UNLIKELY(f == mKernelMap.end())) {
470        assert (false);
471        report_fatal_error(getName() + " does not contain scalar: " + name);
472    }
473    return f->second;
474}
475
476
477/** ------------------------------------------------------------------------------------------------------------- *
478 * @brief createInstance
479 ** ------------------------------------------------------------------------------------------------------------- */
480Value * Kernel::createInstance(const std::unique_ptr<KernelBuilder> & idb) {
481    assert ("KernelBuilder does not have a valid IDISA Builder" && idb);
482    if (LLVM_UNLIKELY(mKernelStateType == nullptr)) {
483        report_fatal_error("Cannot instantiate " + getName() + " before calling prepareKernel()");
484    }
485    setInstance(idb->CreateCacheAlignedAlloca(mKernelStateType));
486    return getInstance();
487}
488
489
490/** ------------------------------------------------------------------------------------------------------------- *
491 * @brief initializeInstance
492 ** ------------------------------------------------------------------------------------------------------------- */
493void Kernel::initializeInstance(const std::unique_ptr<KernelBuilder> & idb) {
494    assert ("KernelBuilder does not have a valid IDISA Builder" && idb);
495    if (LLVM_UNLIKELY(getInstance() == nullptr)) {
496        report_fatal_error("Cannot initialize " + getName() + " before calling createInstance()");
497    }
498    std::vector<Value *> args;
499    args.reserve(1 + mInitialArguments.size() + mStreamSetInputBuffers.size() + (mStreamSetOutputBuffers.size() * 2));
500    args.push_back(getInstance());
501    for (unsigned i = 0; i < mInitialArguments.size(); ++i) {
502        Value * arg = mInitialArguments[i];
503        if (LLVM_UNLIKELY(arg == nullptr)) {
504            report_fatal_error(getName() + ": initial argument " + std::to_string(i)
505                               + " cannot be null when calling createInstance()");
506        }
507        args.push_back(arg);
508    }
509    for (unsigned i = 0; i < mStreamSetInputBuffers.size(); ++i) {
510        assert (mStreamSetInputBuffers[i]);
511        Value * arg = mStreamSetInputBuffers[i]->getStreamSetHandle();
512        if (LLVM_UNLIKELY(arg == nullptr)) {
513            report_fatal_error(getName() + ": input stream set " + std::to_string(i)
514                               + " was not allocated prior to calling createInstance()");
515        }
516        args.push_back(arg);
517    }
518    assert (mStreamSetInputs.size() == mStreamSetInputBuffers.size());
519    for (unsigned i = 0; i < mStreamSetOutputBuffers.size(); ++i) {
520        assert (mStreamSetOutputBuffers[i]);
521        Value * arg = mStreamSetOutputBuffers[i]->getStreamSetHandle();
522        if (LLVM_UNLIKELY(arg == nullptr)) {
523            report_fatal_error(getName() + ": output stream set " + std::to_string(i)
524                               + " was not allocated prior to calling createInstance()");
525        }
526        args.push_back(arg);
527    }
528    assert (mStreamSetOutputs.size() == mStreamSetOutputBuffers.size());
529    IntegerType * const sizeTy = idb->getSizeTy();
530    PointerType * const sizePtrTy = sizeTy->getPointerTo();
531    PointerType * const sizePtrPtrTy = sizePtrTy->getPointerTo();
532    StructType * const consumerTy = StructType::get(idb->getContext(), {sizeTy, sizePtrPtrTy});
533    for (unsigned i = 0; i < mStreamSetOutputBuffers.size(); ++i) {
534        const auto output = mStreamSetOutputBuffers[i];
535        const auto & consumers = output->getConsumers();
536        const auto n = consumers.size();
537        AllocaInst * const outputConsumers = idb->CreateAlloca(consumerTy);
538        Value * const consumerSegNoArray = idb->CreateAlloca(ArrayType::get(sizePtrTy, n));
539        for (unsigned i = 0; i < n; ++i) {
540            Kernel * const consumer = consumers[i];
541            assert ("all instances must be created prior to initialization of any instance" && consumer->getInstance());
542            idb->setKernel(consumer);
543            Value * const segmentNoPtr = idb->getScalarFieldPtr(LOGICAL_SEGMENT_NO_SCALAR);
544            idb->CreateStore(segmentNoPtr, idb->CreateGEP(consumerSegNoArray, { idb->getInt32(0), idb->getInt32(i) }));
545        }
546        idb->setKernel(this);
547        Value * const consumerCountPtr = idb->CreateGEP(outputConsumers, {idb->getInt32(0), idb->getInt32(0)});
548        idb->CreateStore(idb->getSize(n), consumerCountPtr);
549        Value * const consumerSegNoArrayPtr = idb->CreateGEP(outputConsumers, {idb->getInt32(0), idb->getInt32(1)});
550        idb->CreateStore(idb->CreatePointerCast(consumerSegNoArray, sizePtrPtrTy), consumerSegNoArrayPtr);
551        args.push_back(outputConsumers);
552    }
553    idb->CreateCall(getInitFunction(idb->getModule()), args);
554}
555
556/** ------------------------------------------------------------------------------------------------------------- *
557 * @brief finalizeInstance
558 ** ------------------------------------------------------------------------------------------------------------- */
559void Kernel::finalizeInstance(const std::unique_ptr<KernelBuilder> & idb) {
560    assert ("KernelBuilder does not have a valid IDISA Builder" && idb);
561    mOutputScalarResult = idb->CreateCall(getTerminateFunction(idb->getModule()), { getInstance() });
562}
563
564/** ------------------------------------------------------------------------------------------------------------- *
565 * @brief getStreamPort
566 ** ------------------------------------------------------------------------------------------------------------- */
567Kernel::StreamPort Kernel::getStreamPort(const std::string & name) const {
568    const auto f = mStreamMap.find(name);
569    if (LLVM_UNLIKELY(f == mStreamMap.end())) {
570        report_fatal_error(getName() + " does not contain stream set " + name);
571    }
572    return f->second;
573}
574
575/** ------------------------------------------------------------------------------------------------------------- *
576 * @brief generateKernelMethod
577 ** ------------------------------------------------------------------------------------------------------------- */
578void SegmentOrientedKernel::generateKernelMethod(const std::unique_ptr<KernelBuilder> & b) {
579
580    Constant * const log2BlockWidth = b->getSize(std::log2(b->getBitBlockWidth()));
581
582    const auto inputSetCount = mStreamSetInputs.size();
583    mStreamSetInputBufferPtr.resize(inputSetCount);
584    for (unsigned i = 0; i < inputSetCount; ++i) {
585        const auto & name = mStreamSetInputs[i].getName();
586        Value * ic = b->getProcessedItemCount(name);
587        Value * const blockIndex = b->CreateLShr(ic, log2BlockWidth);
588        mStreamSetInputBufferPtr[i] = b->getInputStreamPtr(name, blockIndex);
589    }
590
591    const auto outputSetCount = mStreamSetOutputs.size();
592    mStreamSetOutputBufferPtr.resize(outputSetCount);
593    for (unsigned i = 0; i < outputSetCount; ++i) {
594        const auto & name = mStreamSetOutputs[i].getName();
595        Value * ic = b->getProducedItemCount(name);
596        Value * const blockIndex = b->CreateLShr(ic, log2BlockWidth);
597        mStreamSetOutputBufferPtr[i] = b->getOutputStreamPtr(name, blockIndex);
598    }
599
600    generateDoSegmentMethod(b);
601
602}
603
604/** ------------------------------------------------------------------------------------------------------------- *
605 * @brief generateKernelMethod
606 ** ------------------------------------------------------------------------------------------------------------- */
607void MultiBlockKernel::generateKernelMethod(const std::unique_ptr<KernelBuilder> & kb) {
608
609    const auto inputSetCount = mStreamSetInputs.size();
610    const auto outputSetCount = mStreamSetOutputs.size();
611    const auto totalSetCount = inputSetCount + outputSetCount;
612
613    // Scan through and see if any of our input streams is marked as the principle
614
615    bool hasPrinciple = false;
616    unsigned principleInput = 0;
617
618    for (unsigned i = 0; i < inputSetCount; i++) {
619        for (const auto attr : mStreamSetInputs[i].getAttributes()) {
620            if (attr.isPrinciple()) {
621                hasPrinciple = true;
622                principleInput = i;
623                break;
624            }
625        }
626    }
627
628    // Now we iteratively process these blocks using the doMultiBlock method.
629    // In each iteration, we check how many linearly accessible / writable
630    // items can be processed with our current input / output buffers. If we
631    // cannot support an full stride, we check whether (a) there is enough
632    // input data to process but it is not linearly accessible, in which case
633    // we move the data into temporary buffers or (b) there is not enough data
634    // to process, in which case we abort unless IsFinal was set.
635
636    // Now proceed with creation of the doSegment method.
637    BasicBlock * const doSegmentLoop = kb->CreateBasicBlock("DoSegmentLoop");
638    kb->CreateBr(doSegmentLoop);
639
640    /// DO SEGMENT LOOP
641
642    kb->SetInsertPoint(doSegmentLoop);
643
644    // For each input buffer, determine the processedItemCount, the block pointer for the
645    // buffer block containing the next item, and the number of linearly available items.
646
647    Value * processedItemCount[inputSetCount];
648    Value * baseInputBuffer[inputSetCount];
649    Value * unprocessed[inputSetCount];
650    Value * linearlyAvailable[inputSetCount];
651    Value * readableStrides[inputSetCount];
652
653    Constant * const log2BlockWidth = kb->getSize(std::log2(kb->getBitBlockWidth()));
654
655    Value * numOfStrides = nullptr;
656
657    for (unsigned i = 0; i < inputSetCount; i++) {
658        const auto name = mStreamSetInputs[i].getName();
659        const ProcessingRate & rate = mStreamSetInputs[i].getRate();
660
661        processedItemCount[i] = kb->getProcessedItemCount(name);
662
663        assert (processedItemCount[i]->getType() == mAvailableItemCount[i]->getType());
664
665        Value * const blockIndex = kb->CreateLShr(processedItemCount[i], log2BlockWidth);
666        baseInputBuffer[i] = kb->getInputStreamPtr(name, blockIndex);
667
668        if (codegen::DebugOptionIsSet(codegen::EnableAsserts)) {
669            kb->CreateAssert(kb->CreateICmpUGE(mAvailableItemCount[i], processedItemCount[i]),
670                             "Processed item count cannot exceed the available item count");
671        }
672
673        unprocessed[i] = kb->CreateSub(mAvailableItemCount[i], processedItemCount[i]);
674
675        //kb->CallPrintInt(getName() + "_" + name + "_unprocessed", unprocessed[i]);
676
677        // INVESTIGATE: If the input rate of this stream is constant and known a priori, we could
678        // avoid checking whether it is linearly accessible. Should we have an attribute for this?
679
680        linearlyAvailable[i] = kb->getLinearlyAccessibleItems(name, processedItemCount[i], unprocessed[i]);
681
682        //kb->CallPrintInt(getName() + "_" + name + "_linearlyAvailable", linearlyAvailable[i]);
683
684        readableStrides[i] = nullptr;
685
686        if (rate.isFixed() || rate.isBounded()) {
687            Constant * const maxStrideSize = kb->getSize(rate.getUpperBound() * mStride);
688            readableStrides[i] = kb->CreateUDiv(linearlyAvailable[i], maxStrideSize);
689            if (numOfStrides) {
690                numOfStrides = kb->CreateUMin(numOfStrides, readableStrides[i]);
691            } else {
692                numOfStrides = readableStrides[i];
693            }
694        }
695    }
696
697    //kb->CallPrintInt(getName() + "_numOfStrides", numOfStrides);
698
699    // Now determine the linearly writeable blocks, based on available blocks reduced
700    // by limitations of output buffer space.
701
702    Value * producedItemCount[outputSetCount];
703    Value * baseOutputBuffer[outputSetCount];
704    Value * writableStrides[outputSetCount];
705    Value * linearlyWritable[outputSetCount];
706
707    for (unsigned i = 0; i < outputSetCount; i++) {
708        const auto & name = mStreamSetOutputs[i].getName();
709        const ProcessingRate & rate = mStreamSetOutputs[i].getRate();
710        producedItemCount[i] = kb->getProducedItemCount(name);
711
712        //kb->CallPrintInt(getName() + "_" + name + "_producedItemCount", producedItemCount[i]);
713
714        Value * const blockIndex = kb->CreateLShr(producedItemCount[i], log2BlockWidth);
715        baseOutputBuffer[i] = kb->getOutputStreamPtr(name, blockIndex);
716        linearlyWritable[i] = nullptr;
717        writableStrides[i] = nullptr;
718        if (rate.isFixed() || rate.isBounded()) {
719            linearlyWritable[i] = kb->getLinearlyWritableItems(name, producedItemCount[i]);
720
721            //kb->CallPrintInt(getName() + "_" + name + "_linearlyWritable", linearlyWritable[i]);
722
723            Constant * const maxStrideSize = kb->getSize(rate.getUpperBound() * mStride);
724            writableStrides[i] = kb->CreateUDiv(linearlyWritable[i], maxStrideSize);
725            if (numOfStrides) {
726                numOfStrides = kb->CreateUMin(numOfStrides, writableStrides[i]);
727            } else {
728                numOfStrides = writableStrides[i];
729            }
730        }
731    }
732
733    //kb->CallPrintInt(getName() + "_numOfStrides'", numOfStrides);
734
735    for (unsigned i = 0; i < inputSetCount; i++) {
736        const ProcessingRate & rate = mStreamSetInputs[i].getRate();
737        if (rate.isFixed()) {
738            mAvailableItemCount[i] = kb->CreateMul(numOfStrides, kb->getSize(rate.getRate() * mStride));
739        } else {
740            mAvailableItemCount[i] = linearlyAvailable[i];
741        }
742
743        //kb->CallPrintInt(getName() + "_" + mStreamSetInputs[i].getName() + "_avail", mAvailableItemCount[i]);
744    }
745
746    // Define and allocate the temporary buffer area.
747    Type * tempBuffers[totalSetCount];
748    for (unsigned i = 0; i < inputSetCount; ++i) {
749        Type * bufType = baseInputBuffer[i]->getType()->getPointerElementType();
750        assert (baseInputBuffer[i]->getType()->getPointerAddressSpace() == 0);
751        const ProcessingRate & rate = mStreamSetInputs[i].getRate();
752        unsigned count = 0;
753        if (rate.isFixed()) {
754            count = rate.getRate();
755        } else if (rate.isBounded()) {
756            count = rate.getUpperBound() + 2;
757        }
758        tempBuffers[i] = ArrayType::get(bufType, count);
759    }
760    for (unsigned i = 0; i < outputSetCount; i++) {
761        Type * const bufType = baseOutputBuffer[i]->getType()->getPointerElementType();
762        assert (baseOutputBuffer[i]->getType()->getPointerAddressSpace() == 0);
763        const ProcessingRate & rate = mStreamSetOutputs[i].getRate();
764        unsigned count = 0;
765        if (rate.isFixed()) {
766            count = rate.getRate();
767        } else if (rate.isBounded()) {
768            count = rate.getUpperBound() + 2;
769        }
770        tempBuffers[i + inputSetCount] = ArrayType::get(bufType, count);
771    }
772
773    Type * const tempParameterStructType = StructType::create(kb->getContext(), ArrayRef<Type *>(tempBuffers, totalSetCount));
774
775    Value * const tempBufferArea = kb->CreateCacheAlignedAlloca(tempParameterStructType);
776
777    BasicBlock * const temporaryBufferCheck = kb->CreateBasicBlock("temporaryBufferCheck");
778    BasicBlock * const doMultiBlock = kb->CreateBasicBlock("doMultiBlock");
779    BasicBlock * const copyToTemporaryBuffers = kb->CreateBasicBlock("copyToTemporaryBuffers");
780    BasicBlock * const segmentDone = kb->CreateBasicBlock("segmentDone");
781
782    Value * const hasFullStride = numOfStrides ? kb->CreateICmpNE(numOfStrides, kb->getSize(0)) : kb->getTrue();
783    kb->CreateCondBr(hasFullStride, doMultiBlock, temporaryBufferCheck);
784
785    // We use temporary buffers in 3 different cases that preclude full stride processing.
786
787    //  (a) One or more input buffers does not have a sufficient number of input items linearly available.
788    //  (b) One or more output buffers does not have sufficient linearly available buffer space.
789    //  (c) We have processed all the full strides of input and only the final block remains.
790
791    kb->SetInsertPoint(temporaryBufferCheck);
792
793    // Even if we copy the input data into a linear arrays, is there enough data to perform this stride?
794    // If not, proceed only if this is our final block.
795    Value * hasFullFragmentedStride = nullptr;
796    for (unsigned i = 0; i < inputSetCount; i++) {
797        const ProcessingRate & r = mStreamSetInputs[i].getRate();
798        if (r.isBounded() || (r.isUnknown() && r.getLowerBound() > 0)) {
799            const auto l = r.isBounded() ? r.getUpperBound() : r.getLowerBound();
800            Constant * const strideSize = kb->getSize(l * mStride);
801            Value * enoughAvail = kb->CreateICmpUGE(unprocessed[i], strideSize);
802            if (hasFullFragmentedStride) {
803                hasFullFragmentedStride = kb->CreateAnd(hasFullFragmentedStride, enoughAvail);
804            } else {
805                hasFullFragmentedStride = enoughAvail;
806            }
807        }
808    }
809
810    Value * hasFragmentedOrFinalStride = nullptr;
811    if (hasFullFragmentedStride) {
812        hasFragmentedOrFinalStride = kb->CreateOr(hasFullFragmentedStride, mIsFinal);
813        // Although this might be the final segment, we may have a full fragmented stride to process prior
814        // to the actual final stride.
815        mIsFinal = kb->CreateAnd(mIsFinal, kb->CreateNot(hasFullFragmentedStride));
816    } else {
817        hasFragmentedOrFinalStride = mIsFinal;
818    }
819    kb->CreateCondBr(hasFragmentedOrFinalStride, copyToTemporaryBuffers, segmentDone);
820
821    /// COPY TO TEMPORARY BUFFERS
822    kb->SetInsertPoint(copyToTemporaryBuffers);
823
824    kb->CreateAlignedStore(Constant::getNullValue(tempParameterStructType), tempBufferArea, kb->getCacheAlignment());
825
826    // For each input and output buffer, copy over necessary data starting from the last block boundary.
827
828    Value * temporaryInputBuffer[inputSetCount];
829    Value * temporaryAvailable[inputSetCount];
830
831    for (unsigned i = 0; i < inputSetCount; i++) {
832        temporaryInputBuffer[i] = baseInputBuffer[i];
833        if (readableStrides[i]) {
834            const auto name = mStreamSetInputs[i].getName();
835            const ProcessingRate & rate = mStreamSetInputs[i].getRate();
836            assert (rate.getUpperBound() > 0);
837            Constant * const maxStrideSize = kb->getSize(rate.getUpperBound() * mStride);
838            temporaryAvailable[i] = kb->CreateUMin(unprocessed[i], maxStrideSize);
839
840            BasicBlock * entry = kb->GetInsertBlock();
841            BasicBlock * copy = kb->CreateBasicBlock(name + "Copy");
842            BasicBlock * resume = kb->CreateBasicBlock(name + "ResumeCopy");
843            Value * const test = kb->CreateOr(kb->CreateICmpNE(readableStrides[i], kb->getSize(0)), mIsFinal);
844            kb->CreateCondBr(test, resume, copy);
845
846            kb->SetInsertPoint(copy);
847            Value * const tempBufferPtr = kb->CreateGEP(tempBufferArea, {kb->getInt32(0), kb->getInt32(i), kb->getInt32(0)});
848            assert (tempBufferPtr->getType() == baseInputBuffer[i]->getType());
849            Value * const neededItems = linearlyAvailable[i];
850            Value * const bytesCopied = kb->copy(name, tempBufferPtr, baseInputBuffer[i], neededItems);
851            Value * const nextInputPtr = kb->getRawInputPointer(name, kb->getSize(0));
852            Value * const remaining = kb->CreateSub(temporaryAvailable[i], neededItems);
853            Value * nextBufPtr = kb->CreatePointerCast(tempBufferPtr, kb->getInt8PtrTy());
854            nextBufPtr = kb->CreateGEP(nextBufPtr, bytesCopied);
855            kb->copy(name, nextBufPtr, nextInputPtr, remaining);
856
857            kb->CreateBr(resume);
858
859            kb->SetInsertPoint(resume);
860            PHINode * bufferPtr = kb->CreatePHI(baseInputBuffer[i]->getType(), 2);
861            bufferPtr->addIncoming(baseInputBuffer[i], entry);
862            bufferPtr->addIncoming(tempBufferPtr, copy);
863            temporaryInputBuffer[i] = bufferPtr;
864        }
865    }
866
867    Value * temporaryOutputBuffer[outputSetCount];
868    for (unsigned i = 0; i < outputSetCount; i++) {
869        temporaryOutputBuffer[i] = baseOutputBuffer[i];
870        if (writableStrides[i]) {
871            const auto name = mStreamSetOutputs[i].getName();
872
873            BasicBlock * const entry = kb->GetInsertBlock();
874            BasicBlock * const copy = kb->CreateBasicBlock(name + "Copy");
875            BasicBlock * const resume = kb->CreateBasicBlock(name + "ResumeCopy");
876
877            Value * const test = kb->CreateOr(kb->CreateICmpNE(writableStrides[i], kb->getSize(0)), mIsFinal);
878            kb->CreateCondBr(test, resume, copy);
879
880            kb->SetInsertPoint(copy);
881            Value * const tempBufferPtr = kb->CreateGEP(tempBufferArea,  {kb->getInt32(0), kb->getInt32(inputSetCount + i), kb->getInt32(0)});
882            assert (tempBufferPtr->getType() == baseOutputBuffer[i]->getType());
883            Value * const itemsToCopy = kb->CreateAnd(producedItemCount[i], kb->getSize(kb->getBitBlockWidth() - 1));
884            kb->copy(name, tempBufferPtr, baseOutputBuffer[i], itemsToCopy);
885            kb->CreateBr(resume);
886
887            kb->SetInsertPoint(resume);
888            PHINode * bufferPtr = kb->CreatePHI(tempBufferPtr->getType(), 2);
889            bufferPtr->addIncoming(baseOutputBuffer[i], entry);
890            bufferPtr->addIncoming(tempBufferPtr, copy);
891            temporaryOutputBuffer[i] = bufferPtr;
892        }
893    }
894
895    kb->CreateBr(doMultiBlock);
896    BasicBlock * const usingTemporaryBuffers = kb->GetInsertBlock();
897    doMultiBlock->moveAfter(usingTemporaryBuffers);
898
899    /// DO MULTI BLOCK
900
901    //  At this point we have verified the availability of one or more blocks of input data and output buffer space for all stream sets.
902    //  Now prepare the doMultiBlock call.
903    kb->SetInsertPoint(doMultiBlock);
904
905    PHINode * const isFinal = kb->CreatePHI(mIsFinal->getType(), 2);
906    isFinal->addIncoming(kb->getFalse(), doSegmentLoop);
907    isFinal->addIncoming(mIsFinal, usingTemporaryBuffers);
908    mIsFinal = isFinal;
909
910    mStreamSetInputBufferPtr.resize(inputSetCount);
911    for (unsigned i = 0; i < inputSetCount; ++i) {
912        assert (baseInputBuffer[i] && temporaryInputBuffer[i]);
913        if (baseInputBuffer[i] != temporaryInputBuffer[i]) {
914            PHINode * const avail = kb->CreatePHI(kb->getSizeTy(), 2);
915            avail->addIncoming(mAvailableItemCount[i], doSegmentLoop);
916            avail->addIncoming(temporaryAvailable[i], usingTemporaryBuffers);
917            mAvailableItemCount[i] = avail;
918            PHINode * const bufferPtr = kb->CreatePHI(baseInputBuffer[i]->getType(), 2);
919            bufferPtr->addIncoming(baseInputBuffer[i], doSegmentLoop);
920            assert (baseInputBuffer[i]->getType() == temporaryInputBuffer[i]->getType());
921            bufferPtr->addIncoming(temporaryInputBuffer[i], usingTemporaryBuffers);
922            temporaryInputBuffer[i] = bufferPtr;
923        }
924        mStreamSetInputBufferPtr[i] = temporaryInputBuffer[i];
925    }
926
927    mStreamSetOutputBufferPtr.resize(outputSetCount);
928    for (unsigned i = 0; i < outputSetCount; ++i) {
929        assert (baseOutputBuffer[i] && temporaryOutputBuffer[i]);
930        if (baseOutputBuffer[i] != temporaryOutputBuffer[i]) {
931            PHINode * const bufferPtr = kb->CreatePHI(baseOutputBuffer[i]->getType(), 2);
932            bufferPtr->addIncoming(baseOutputBuffer[i], doSegmentLoop);
933            assert (baseOutputBuffer[i]->getType() == temporaryOutputBuffer[i]->getType());
934            bufferPtr->addIncoming(temporaryOutputBuffer[i], usingTemporaryBuffers);
935            temporaryOutputBuffer[i] = bufferPtr;
936        }
937        mStreamSetOutputBufferPtr[i] = temporaryOutputBuffer[i];
938    }
939
940    // Now use the generateMultiBlockLogic method of the MultiBlockKernelBuilder subtype to
941    // provide the required multi-block kernel logic.
942    generateMultiBlockLogic(kb, numOfStrides);
943
944    // If we have no fixed rate inputs, we won't know when we're done parsing until we test
945    // whether any input data was processed.
946    bool mayMakeNoProgress = true;
947
948    // Update the processed item count of any Fixed input or output stream. While doing so, also
949    // calculate the LCM of their rates. The LCM is used to calculate the final item counts.
950
951    unsigned rateLCM = 1;
952
953    for (unsigned i = 0; i < inputSetCount; ++i) {
954        const ProcessingRate & rate = mStreamSetInputs[i].getRate();
955        if (rate.isFixed()) {
956            mayMakeNoProgress = false;
957            rateLCM = lcm(rateLCM, rate.getRate());
958            Value * const processed = mAvailableItemCount[i]; // kb->CreateMul(numOfStrides, kb->getSize(mStride * rate.getRate()));
959            Value * const ic = kb->CreateAdd(processedItemCount[i], processed);
960            kb->setProcessedItemCount(mStreamSetInputs[i].getName(), ic);
961        }
962    }
963
964    for (unsigned i = 0; i < outputSetCount; ++i) {
965        const ProcessingRate & rate = mStreamSetOutputs[i].getRate();
966        if (rate.isFixed()) {
967            rateLCM = lcm(rateLCM, rate.getRate());
968            Value * const produced = kb->CreateMul(numOfStrides, kb->getSize(mStride * rate.getRate()));
969            Value * const ic = kb->CreateAdd(producedItemCount[i], produced);
970            kb->setProducedItemCount(mStreamSetOutputs[i].getName(), ic);
971        }
972    }
973
974    BasicBlock * const finalStrideCheck = kb->CreateBasicBlock("finalStrideCheck");
975    BasicBlock * const finalStrideAdjustment = kb->CreateBasicBlock("finalStrideAdjustment");
976    BasicBlock * const standardCopyBack = kb->CreateBasicBlock("standardCopyBack");
977    BasicBlock * const temporaryBufferCopyBack = kb->CreateBasicBlock("temporaryBufferCopyBack");
978
979    kb->CreateLikelyCondBr(hasFullStride, standardCopyBack, finalStrideCheck);
980
981
982    /// FINAL STRIDE CHECK
983    kb->SetInsertPoint(finalStrideCheck);
984    kb->CreateUnlikelyCondBr(mIsFinal, finalStrideAdjustment, temporaryBufferCopyBack);
985
986    /// FINAL STRIDE ADJUSTMENT
987    kb->SetInsertPoint(finalStrideAdjustment);
988
989    // If this is our final stride, adjust the Fixed output item counts. The main loop assumes that
990    // the ITEM COUNT % FIXED RATE = 0 for all Fixed Input and Output streams. We correct that here
991    // to calculate them based on the actual input item counts.
992
993    // NOTE: This appears overly complex to avoid an integer overflow without reducing the maximum
994    // integer size. For each Fixed output stream, this calculates:
995
996    //       CEILING(MIN(Total Available Item Count / Fixed Input Rate) * Fixed Output Rate)
997
998    Value * basePreviouslyProcessedItemCount = nullptr;
999    Value * scaledInverseOfStrideItemCount = nullptr;
1000
1001    for (unsigned i = 0; i < inputSetCount; ++i) {
1002        const ProcessingRate & r = mStreamSetInputs[i].getRate();
1003        if (r.isFixed()) {
1004            assert (rateLCM % r.getRate() == 0);
1005            Value * const a = kb->CreateMul(mAvailableItemCount[i], kb->getSize(rateLCM / r.getRate())); // unprocessed
1006            Value * const p = kb->CreateUDiv(processedItemCount[i], kb->getSize(r.getRate()));
1007            if (scaledInverseOfStrideItemCount) {
1008                scaledInverseOfStrideItemCount = kb->CreateUMin(scaledInverseOfStrideItemCount, a);
1009                basePreviouslyProcessedItemCount = kb->CreateUMin(basePreviouslyProcessedItemCount, p);
1010            } else {
1011                scaledInverseOfStrideItemCount = a;
1012                basePreviouslyProcessedItemCount = p;
1013            }
1014        }
1015//        const auto name = mStreamSetInputs[i].getName();
1016//        Value * const processed = kb->CreateAdd(processedItemCount[i], unprocessed[i]);
1017//        kb->setProcessedItemCount(name, processed);
1018    }
1019
1020    for (unsigned i = 0; i < outputSetCount; ++i) {
1021        const auto name = mStreamSetOutputs[i].getName();
1022        const ProcessingRate & r = mStreamSetOutputs[i].getRate();
1023        Value * produced = nullptr;
1024        if (r.isFixed()) {
1025            assert (rateLCM % r.getRate() == 0);
1026            assert (basePreviouslyProcessedItemCount && scaledInverseOfStrideItemCount);
1027            Value * const p = kb->CreateMul(basePreviouslyProcessedItemCount, kb->getSize(r.getRate()));
1028            Value * const ic = kb->CreateUDivCeil(scaledInverseOfStrideItemCount, kb->getSize(rateLCM / r.getRate()));
1029            produced = kb->CreateAdd(p, ic);
1030        } else { // check if we have an attribute; if so, get the current produced count and adjust it
1031            bool noAttributes = true;
1032            for (const Attribute & attr : mStreamSetOutputs[i].getAttributes()) {
1033                if (attr.isAdd() || attr.isRoundUpTo()) {
1034                    noAttributes = false;
1035                    break;
1036                }
1037            }
1038            if (noAttributes) {
1039                continue;
1040            }
1041            produced = kb->getProducedItemCount(name);
1042        }
1043        for (const Attribute & attr : mStreamSetOutputs[i].getAttributes()) {
1044            if (attr.isAdd()) {
1045                produced = kb->CreateAdd(produced, kb->getSize(attr.getAmount()));
1046            } else if (attr.isRoundUpTo()) {
1047                produced = kb->CreateRoundUp(produced, kb->getSize(attr.getAmount()));
1048            }
1049        }
1050        kb->setProducedItemCount(name, produced);
1051    }
1052
1053    kb->CreateBr(temporaryBufferCopyBack);
1054
1055    /// TEMPORARY BUFFER COPY BACK
1056    kb->SetInsertPoint(temporaryBufferCopyBack);
1057
1058    // Copy back data to the actual output buffers.
1059    for (unsigned i = 0; i < outputSetCount; i++) {
1060
1061        if (baseOutputBuffer[i] != temporaryOutputBuffer[i]) {
1062
1063            const auto name = mStreamSetOutputs[i].getName();
1064
1065            BasicBlock * const copy = kb->CreateBasicBlock(name + "CopyBack");
1066            BasicBlock * const resume = kb->CreateBasicBlock(name + "ResumeCopyBack");
1067            Value * const usedTemporary = kb->CreateICmpNE(temporaryOutputBuffer[i], baseOutputBuffer[i]);
1068
1069            // If we used a temporary buffer ...
1070            kb->CreateCondBr(usedTemporary, copy, resume);
1071
1072            kb->SetInsertPoint(copy);
1073            Value * bytesCopied = kb->copy(name, baseOutputBuffer[i], temporaryOutputBuffer[i], linearlyWritable[i]);
1074            Value * nextOutputPtr = kb->getRawOutputPointer(name, kb->getSize(0));
1075            Value * producedCount = kb->getProducedItemCount(name);
1076
1077            Value * remaining = kb->CreateSub(producedCount, linearlyWritable[i]);
1078            Value * nextBufPtr = kb->CreatePointerCast(temporaryOutputBuffer[i], kb->getInt8PtrTy());
1079            nextBufPtr = kb->CreateGEP(nextBufPtr, bytesCopied);
1080
1081            kb->copy(name, nextOutputPtr, nextBufPtr, remaining);
1082            kb->CreateBr(resume);
1083
1084            kb->SetInsertPoint(resume);
1085        }
1086    }
1087
1088    //  We've dealt with the partial block processing and copied information back into the
1089    //  actual buffers.  If this isn't the final block, loop back for more multiblock processing.
1090    BasicBlock * setTermination = nullptr;
1091    if (hasNoTerminateAttribute()) {
1092        kb->CreateCondBr(mIsFinal, segmentDone, standardCopyBack);
1093    } else {
1094        setTermination = kb->CreateBasicBlock("setTermination");
1095        kb->CreateCondBr(mIsFinal, setTermination, standardCopyBack);
1096    }
1097
1098    /// STANDARD COPY BACK
1099    kb->SetInsertPoint(standardCopyBack);
1100
1101    // Do copybacks if necessary.
1102    for (unsigned i = 0; i < outputSetCount; i++) {
1103        if (mStreamSetOutputBuffers[i]->supportsCopyBack()) {
1104            const auto name = mStreamSetOutputs[i].getName();
1105            Value * newProduced = kb->getProducedItemCount(name);
1106            kb->CreateCopyBack(name, producedItemCount[i], newProduced);
1107        }
1108    }
1109
1110    // If it is possible to make no progress, verify we processed some of the input. If we haven't,
1111    // we're finished this segment.
1112    if (mayMakeNoProgress) {
1113        Value * madeProgress = nullptr;
1114        for (unsigned i = 0; i < inputSetCount; ++i) {
1115            Value * const processed = kb->getProcessedItemCount(mStreamSetInputs[i].getName());
1116            Value * const progress = kb->CreateICmpNE(processed, processedItemCount[i]);
1117            if (madeProgress) {
1118                madeProgress = kb->CreateOr(madeProgress, progress);
1119            } else {
1120                madeProgress = progress;
1121            }
1122        }
1123        assert (madeProgress);
1124        kb->CreateCondBr(madeProgress, doSegmentLoop, segmentDone);
1125    } else {
1126        kb->CreateBr(doSegmentLoop);
1127    }
1128
1129    if (hasNoTerminateAttribute()) {
1130        segmentDone->moveAfter(kb->GetInsertBlock());
1131    } else {
1132        /// SET TERMINATION
1133        setTermination->moveAfter(kb->GetInsertBlock());
1134        kb->SetInsertPoint(setTermination);
1135        kb->setTerminationSignal();
1136        kb->CreateBr(segmentDone);
1137        segmentDone->moveAfter(setTermination);
1138    }
1139
1140    kb->SetInsertPoint(segmentDone);
1141
1142}
1143
1144//bool MultiBlockKernel::requiresCopyBack(const ProcessingRate & rate) const {
1145//    if (rate.isBounded() || rate.isUnknown()) {
1146//        return true;
1147//    } else if (rate.isDirectlyRelative()) {
1148//        Port port; unsigned i;
1149//        std::tie(port, i) = getStreamPort(rate.getReference());
1150//        const auto & binding = (port == Port::Input) ? mStreamSetInputs[i] : mStreamSetOutputs[i];
1151//        return requiresCopyBack(binding.getRate());
1152//    }
1153//    return false;
1154//}
1155
1156//  The default doSegment method dispatches to the doBlock routine for
1157//  each block of the given number of blocksToDo, and then updates counts.
1158
1159void BlockOrientedKernel::generateMultiBlockLogic(const std::unique_ptr<KernelBuilder> & idb, llvm::Value * const numOfStrides) {
1160
1161    BasicBlock * const entryBlock = idb->GetInsertBlock();
1162    BasicBlock * const strideLoopCond = idb->CreateBasicBlock(getName() + "_strideLoopCond");
1163    mStrideLoopBody = idb->CreateBasicBlock(getName() + "_strideLoopBody");
1164    BasicBlock * const stridesDone = idb->CreateBasicBlock(getName() + "_stridesDone");
1165    BasicBlock * const doFinalBlock = idb->CreateBasicBlock(getName() + "_doFinalBlock");
1166    BasicBlock * const segmentDone = idb->CreateBasicBlock(getName() + "_segmentDone");
1167
1168    Value * baseTarget = nullptr;
1169    if (idb->supportsIndirectBr()) {
1170        baseTarget = idb->CreateSelect(mIsFinal, BlockAddress::get(doFinalBlock), BlockAddress::get(segmentDone));
1171    }
1172
1173    Constant * const log2BlockSize = idb->getSize(std::log2(idb->getBitBlockWidth()));
1174
1175    const auto inputSetCount = mStreamSetInputs.size();
1176    Value * baseProcessedIndex[inputSetCount];
1177    for (unsigned i = 0; i < inputSetCount; ++i) {
1178        const ProcessingRate & rate = mStreamSetInputs[i].getRate();
1179        if (rate.isFixed()) {
1180            baseProcessedIndex[i] = nullptr;
1181        } else {
1182            Value * ic = idb->getProcessedItemCount(mStreamSetInputs[i].getName());
1183            ic = idb->CreateLShr(ic, log2BlockSize);
1184            baseProcessedIndex[i] = ic;
1185        }
1186    }
1187
1188    const auto outputSetCount = mStreamSetOutputs.size();
1189    Value * baseProducedIndex[outputSetCount];
1190    for (unsigned i = 0; i < outputSetCount; ++i) {
1191        const ProcessingRate & rate = mStreamSetOutputs[i].getRate();
1192        if (rate.isFixed()) {
1193            baseProducedIndex[i] = nullptr;
1194        } else {
1195            Value * ic = idb->getProducedItemCount(mStreamSetOutputs[i].getName());
1196            ic = idb->CreateLShr(ic, log2BlockSize);
1197            baseProducedIndex[i] = ic;
1198        }
1199    }
1200
1201    Value * const numOfBlocksToProcess = idb->CreateMul(numOfStrides, idb->getSize(mStride / idb->getBitBlockWidth()));
1202
1203    idb->CreateBr(strideLoopCond);
1204
1205    /// BLOCK COND
1206
1207    idb->SetInsertPoint(strideLoopCond);
1208
1209    PHINode * branchTarget = nullptr;
1210    if (baseTarget) {
1211        branchTarget = idb->CreatePHI(baseTarget->getType(), 2, "branchTarget");
1212        branchTarget->addIncoming(baseTarget, entryBlock);
1213    }
1214
1215    PHINode * const blockIndex = idb->CreatePHI(idb->getSizeTy(), 2, "index");
1216    blockIndex->addIncoming(idb->getSize(0), entryBlock);
1217
1218    for (unsigned i = 0; i < inputSetCount; ++i) {
1219        Value * offset = blockIndex;
1220        if (baseProcessedIndex[i]) {
1221            offset = idb->getProcessedItemCount(mStreamSetInputs[i].getName());
1222            offset = idb->CreateLShr(offset, log2BlockSize);
1223            offset = idb->CreateSub(offset, baseProcessedIndex[i]);
1224        }
1225        mStreamSetInputBufferPtr[i] = idb->CreateGEP(mStreamSetInputBufferPtr[i], offset);
1226    }
1227
1228    for (unsigned i = 0; i < outputSetCount; ++i) {
1229        Value * offset = blockIndex;
1230        if (baseProducedIndex[i]) {
1231            offset = idb->getProducedItemCount(mStreamSetOutputs[i].getName());
1232            offset = idb->CreateLShr(offset, log2BlockSize);
1233            offset = idb->CreateSub(offset, baseProducedIndex[i]);
1234        }
1235        mStreamSetOutputBufferPtr[i] = idb->CreateGEP(mStreamSetOutputBufferPtr[i], offset);
1236    }
1237
1238    Value * const notDone = idb->CreateICmpULT(blockIndex, numOfBlocksToProcess);
1239    idb->CreateLikelyCondBr(notDone, mStrideLoopBody, stridesDone);
1240
1241    /// BLOCK BODY
1242
1243    idb->SetInsertPoint(mStrideLoopBody);
1244
1245    if (idb->supportsIndirectBr()) {
1246        mStrideLoopTarget = idb->CreatePHI(baseTarget->getType(), 2, "strideTarget");
1247        mStrideLoopTarget->addIncoming(branchTarget, strideLoopCond);
1248    }
1249
1250    /// GENERATE DO BLOCK METHOD
1251
1252    writeDoBlockMethod(idb);
1253
1254    BasicBlock * const bodyEnd = idb->GetInsertBlock();
1255    blockIndex->addIncoming(idb->CreateAdd(blockIndex, idb->getSize(1)), bodyEnd);
1256    if (branchTarget) {
1257        branchTarget->addIncoming(mStrideLoopTarget, bodyEnd);
1258    }
1259    idb->CreateBr(strideLoopCond);
1260
1261    stridesDone->moveAfter(bodyEnd);
1262
1263    /// STRIDE DONE
1264
1265    idb->SetInsertPoint(stridesDone);
1266
1267    // Now conditionally perform the final block processing depending on the doFinal parameter.
1268    if (branchTarget) {
1269        mStrideLoopBranch = idb->CreateIndirectBr(branchTarget, 3);
1270        mStrideLoopBranch->addDestination(doFinalBlock);
1271        mStrideLoopBranch->addDestination(segmentDone);
1272    } else {
1273        idb->CreateUnlikelyCondBr(mIsFinal, doFinalBlock, segmentDone);
1274    }
1275
1276    doFinalBlock->moveAfter(stridesDone);
1277
1278    idb->SetInsertPoint(doFinalBlock);
1279
1280    Value * remainingItems = nullptr;
1281    for (unsigned i = 0; i < inputSetCount; ++i) {
1282        const ProcessingRate & r = mStreamSetInputs[i].getRate();
1283        if (r.isFixed()) {
1284            Value * ic = idb->CreateUDiv(mAvailableItemCount[i], idb->getSize(r.getRate()));
1285            if (remainingItems) {
1286                remainingItems = idb->CreateUMax(remainingItems, ic);
1287            } else {
1288                remainingItems = ic;
1289            }
1290        }
1291    }
1292
1293    writeFinalBlockMethod(idb, remainingItems);
1294
1295    idb->CreateBr(segmentDone);
1296
1297    segmentDone->moveAfter(idb->GetInsertBlock());
1298
1299    idb->SetInsertPoint(segmentDone);
1300
1301    // Update the branch prediction metadata to indicate that the likely target will be segmentDone
1302    if (branchTarget) {
1303        MDBuilder mdb(idb->getContext());
1304        const auto destinations = mStrideLoopBranch->getNumDestinations();
1305        uint32_t weights[destinations];
1306        for (unsigned i = 0; i < destinations; ++i) {
1307            weights[i] = (mStrideLoopBranch->getDestination(i) == segmentDone) ? 100 : 1;
1308        }
1309        ArrayRef<uint32_t> bw(weights, destinations);
1310        mStrideLoopBranch->setMetadata(LLVMContext::MD_prof, mdb.createBranchWeights(bw));
1311    }
1312
1313}
1314
1315inline void BlockOrientedKernel::writeDoBlockMethod(const std::unique_ptr<KernelBuilder> & idb) {
1316
1317    Value * const self = getInstance();
1318    Function * const cp = mCurrentMethod;
1319    auto ip = idb->saveIP();
1320    std::vector<Value *> availableItemCount(0);
1321
1322    /// Check if the do block method is called and create the function if necessary
1323    if (!idb->supportsIndirectBr()) {
1324
1325        std::vector<Type *> params;
1326        params.reserve(1 + mAvailableItemCount.size());
1327        params.push_back(self->getType());
1328        for (Value * avail : mAvailableItemCount) {
1329            params.push_back(avail->getType());
1330        }
1331
1332        FunctionType * const type = FunctionType::get(idb->getVoidTy(), params, false);
1333        mCurrentMethod = Function::Create(type, GlobalValue::InternalLinkage, getName() + DO_BLOCK_SUFFIX, idb->getModule());
1334        mCurrentMethod->setCallingConv(CallingConv::C);
1335        mCurrentMethod->setDoesNotThrow();
1336        auto args = mCurrentMethod->arg_begin();
1337        args->setName("self");
1338        setInstance(&*args);
1339        availableItemCount.reserve(mAvailableItemCount.size());
1340        while (++args != mCurrentMethod->arg_end()) {
1341            availableItemCount.push_back(&*args);
1342        }
1343        assert (availableItemCount.size() == mAvailableItemCount.size());
1344        mAvailableItemCount.swap(availableItemCount);
1345        idb->SetInsertPoint(BasicBlock::Create(idb->getContext(), "entry", mCurrentMethod));
1346    }
1347
1348    generateDoBlockMethod(idb); // must be implemented by the BlockOrientedKernelBuilder subtype
1349
1350    if (!idb->supportsIndirectBr()) {
1351        // Restore the DoSegment function state then call the DoBlock method
1352        idb->CreateRetVoid();
1353        mDoBlockMethod = mCurrentMethod;
1354        idb->restoreIP(ip);
1355        setInstance(self);
1356        mCurrentMethod = cp;
1357        mAvailableItemCount.swap(availableItemCount);
1358        CreateDoBlockMethodCall(idb);
1359    }
1360
1361}
1362
1363inline void BlockOrientedKernel::writeFinalBlockMethod(const std::unique_ptr<KernelBuilder> & idb, Value * remainingItems) {
1364
1365    Value * const self = getInstance();
1366    Function * const cp = mCurrentMethod;
1367    Value * const remainingItemCount = remainingItems;
1368    auto ip = idb->saveIP();
1369    std::vector<Value *> availableItemCount(0);
1370
1371    if (!idb->supportsIndirectBr()) {
1372        std::vector<Type *> params;
1373        params.reserve(2 + mAvailableItemCount.size());
1374        params.push_back(self->getType());
1375        params.push_back(idb->getSizeTy());
1376        for (Value * avail : mAvailableItemCount) {
1377            params.push_back(avail->getType());
1378        }
1379        FunctionType * const type = FunctionType::get(idb->getVoidTy(), params, false);
1380        mCurrentMethod = Function::Create(type, GlobalValue::InternalLinkage, getName() + FINAL_BLOCK_SUFFIX, idb->getModule());
1381        mCurrentMethod->setCallingConv(CallingConv::C);
1382        mCurrentMethod->setDoesNotThrow();
1383        auto args = mCurrentMethod->arg_begin();
1384        args->setName("self");
1385        setInstance(&*args);
1386        remainingItems = &*(++args);
1387        remainingItems->setName("remainingItems");
1388        availableItemCount.reserve(mAvailableItemCount.size());
1389        while (++args != mCurrentMethod->arg_end()) {
1390            availableItemCount.push_back(&*args);
1391        }
1392        assert (availableItemCount.size() == mAvailableItemCount.size());
1393        mAvailableItemCount.swap(availableItemCount);
1394        idb->SetInsertPoint(BasicBlock::Create(idb->getContext(), "entry", mCurrentMethod));
1395    }
1396
1397    generateFinalBlockMethod(idb, remainingItems); // may be implemented by the BlockOrientedKernel subtype
1398
1399    if (!idb->supportsIndirectBr()) {
1400        idb->CreateRetVoid();
1401        idb->restoreIP(ip);
1402        setInstance(self);
1403        mAvailableItemCount.swap(availableItemCount);
1404        // Restore the DoSegment function state then call the DoFinal method
1405        std::vector<Value *> args;
1406        args.reserve(2 + mAvailableItemCount.size());
1407        args.push_back(self);
1408        args.push_back(remainingItemCount);
1409        for (Value * avail : mAvailableItemCount) {
1410            args.push_back(avail);
1411        }
1412        idb->CreateCall(mCurrentMethod, args);
1413        mCurrentMethod = cp;
1414    }
1415
1416}
1417
1418//  The default finalBlock method simply dispatches to the doBlock routine.
1419void BlockOrientedKernel::generateFinalBlockMethod(const std::unique_ptr<KernelBuilder> & idb, Value * /* remainingItems */) {
1420    CreateDoBlockMethodCall(idb);
1421}
1422
1423void BlockOrientedKernel::CreateDoBlockMethodCall(const std::unique_ptr<KernelBuilder> & idb) {
1424    if (idb->supportsIndirectBr()) {
1425        BasicBlock * bb = idb->CreateBasicBlock("resume");
1426        mStrideLoopBranch->addDestination(bb);
1427        mStrideLoopTarget->addIncoming(BlockAddress::get(bb), idb->GetInsertBlock());
1428        idb->CreateBr(mStrideLoopBody);
1429        bb->moveAfter(idb->GetInsertBlock());
1430        idb->SetInsertPoint(bb);
1431    } else {
1432        std::vector<Value *> args;
1433        args.reserve(1 + mAvailableItemCount.size());
1434        args.push_back(getInstance());
1435        for (Value * avail : mAvailableItemCount) {
1436            args.push_back(avail);
1437        }
1438        idb->CreateCall(mDoBlockMethod, args);
1439    }
1440}
1441
1442static inline std::string annotateKernelNameWithDebugFlags(std::string && name) {
1443    if (codegen::DebugOptionIsSet(codegen::EnableAsserts)) {
1444        name += "_EA";
1445    }
1446    name += "_O" + std::to_string((int)codegen::OptLevel);
1447    return name;
1448}
1449
1450// CONSTRUCTOR
1451Kernel::Kernel(std::string && kernelName,
1452               std::vector<Binding> && stream_inputs,
1453               std::vector<Binding> && stream_outputs,
1454               std::vector<Binding> && scalar_parameters,
1455               std::vector<Binding> && scalar_outputs,
1456               std::vector<Binding> && internal_scalars)
1457: KernelInterface(annotateKernelNameWithDebugFlags(std::move(kernelName))
1458                  , std::move(stream_inputs), std::move(stream_outputs)
1459                  , std::move(scalar_parameters), std::move(scalar_outputs)
1460                  , std::move(internal_scalars))
1461, mCurrentMethod(nullptr)
1462, mAvailablePrincipleItemCount(nullptr)
1463, mNoTerminateAttribute(false)
1464, mIsGenerated(false)
1465, mStride(0)
1466, mIsFinal(nullptr)
1467, mOutputScalarResult(nullptr) {
1468
1469}
1470
1471Kernel::~Kernel() {
1472
1473}
1474
1475// CONSTRUCTOR
1476BlockOrientedKernel::BlockOrientedKernel(std::string && kernelName,
1477                                         std::vector<Binding> && stream_inputs,
1478                                         std::vector<Binding> && stream_outputs,
1479                                         std::vector<Binding> && scalar_parameters,
1480                                         std::vector<Binding> && scalar_outputs,
1481                                         std::vector<Binding> && internal_scalars)
1482: MultiBlockKernel(std::move(kernelName), std::move(stream_inputs), std::move(stream_outputs), std::move(scalar_parameters), std::move(scalar_outputs), std::move(internal_scalars))
1483, mDoBlockMethod(nullptr)
1484, mStrideLoopBody(nullptr)
1485, mStrideLoopBranch(nullptr)
1486, mStrideLoopTarget(nullptr) {
1487
1488}
1489
1490// MULTI-BLOCK KERNEL CONSTRUCTOR
1491MultiBlockKernel::MultiBlockKernel(std::string && kernelName,
1492                                   std::vector<Binding> && stream_inputs,
1493                                   std::vector<Binding> && stream_outputs,
1494                                   std::vector<Binding> && scalar_parameters,
1495                                   std::vector<Binding> && scalar_outputs,
1496                                   std::vector<Binding> && internal_scalars)
1497: Kernel(std::move(kernelName), std::move(stream_inputs), std::move(stream_outputs), std::move(scalar_parameters), std::move(scalar_outputs), std::move(internal_scalars)) {
1498
1499}
1500
1501// CONSTRUCTOR
1502SegmentOrientedKernel::SegmentOrientedKernel(std::string && kernelName,
1503                                             std::vector<Binding> && stream_inputs,
1504                                             std::vector<Binding> && stream_outputs,
1505                                             std::vector<Binding> && scalar_parameters,
1506                                             std::vector<Binding> && scalar_outputs,
1507                                             std::vector<Binding> && internal_scalars)
1508: Kernel(std::move(kernelName), std::move(stream_inputs), std::move(stream_outputs), std::move(scalar_parameters), std::move(scalar_outputs), std::move(internal_scalars)) {
1509
1510}
1511
1512
1513}
Note: See TracBrowser for help on using the repository browser.