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

Last change on this file since 5638 was 5638, checked in by cameron, 19 months ago

Modifying multiblock kernel builder for non-fixed input stream sets - temp check-in

File size: 65.9 KB
Line 
1/*
2 *  Copyright (c) 2016 International Characters.
3 *  This software is licensed to the public under the Open Software License 3.0.
4 */
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#include <llvm/Bitcode/ReaderWriter.h>
16#include <llvm/Transforms/Utils/Local.h>
17#include <kernels/streamset.h>
18#include <sstream>
19#include <kernels/kernel_builder.h>
20#include <llvm/Support/Debug.h>
21
22using namespace llvm;
23using namespace parabix;
24
25namespace kernel {
26
27const std::string Kernel::DO_BLOCK_SUFFIX = "_DoBlock";
28const std::string Kernel::FINAL_BLOCK_SUFFIX = "_FinalBlock";
29const std::string Kernel::MULTI_BLOCK_SUFFIX = "_MultiBlock";
30const std::string Kernel::LOGICAL_SEGMENT_NO_SCALAR = "logicalSegNo";
31const std::string Kernel::PROCESSED_ITEM_COUNT_SUFFIX = "_processedItemCount";
32const std::string Kernel::CONSUMED_ITEM_COUNT_SUFFIX = "_consumedItemCount";
33const std::string Kernel::PRODUCED_ITEM_COUNT_SUFFIX = "_producedItemCount";
34const std::string Kernel::TERMINATION_SIGNAL = "terminationSignal";
35const std::string Kernel::BUFFER_PTR_SUFFIX = "_bufferPtr";
36const std::string Kernel::CONSUMER_SUFFIX = "_consumerLocks";
37const std::string Kernel::CYCLECOUNT_SCALAR = "CPUcycles";
38
39unsigned Kernel::addScalar(Type * const type, const std::string & name) {
40    if (LLVM_UNLIKELY(mKernelStateType != nullptr)) {
41        report_fatal_error("Cannot add field " + name + " to " + getName() + " after kernel state finalized");
42    }
43    if (LLVM_UNLIKELY(mKernelMap.count(name))) {
44        report_fatal_error(getName() + " already contains scalar field " + name);
45    }
46    const auto index = mKernelFields.size();
47    mKernelMap.emplace(name, index);
48    mKernelFields.push_back(type);
49    return index;
50}
51
52unsigned Kernel::addUnnamedScalar(Type * const type) {
53    if (LLVM_UNLIKELY(mKernelStateType != nullptr)) {
54        report_fatal_error("Cannot add unnamed field  to " + getName() + " after kernel state finalized");
55    }
56    const auto index = mKernelFields.size();
57    mKernelFields.push_back(type);
58    return index;
59}
60
61void Kernel::prepareStreamSetNameMap() {
62    for (unsigned i = 0; i < mStreamSetInputs.size(); i++) {
63        mStreamMap.emplace(mStreamSetInputs[i].name, std::make_pair(Port::Input, i));
64    }
65    for (unsigned i = 0; i < mStreamSetOutputs.size(); i++) {
66        mStreamMap.emplace(mStreamSetOutputs[i].name, std::make_pair(Port::Output, i));
67    }
68}
69
70void Kernel::bindPorts(const StreamSetBuffers & inputs, const StreamSetBuffers & outputs) {
71    assert (mModule == nullptr);
72    assert (mStreamSetInputBuffers.empty());
73    assert (mStreamSetOutputBuffers.empty());
74
75    if (LLVM_UNLIKELY(mStreamSetInputs.size() != inputs.size())) {
76        report_fatal_error(getName() + ": expected " + std::to_string(mStreamSetInputs.size()) +
77                           " input stream sets but was given "
78                           + std::to_string(inputs.size()));
79    }
80
81    for (unsigned i = 0; i < inputs.size(); ++i) {
82        StreamSetBuffer * const buf = inputs[i];
83        if (LLVM_UNLIKELY(buf == nullptr)) {
84            report_fatal_error(getName() + ": input stream set " + std::to_string(i)
85                               + " cannot be null");
86        }
87        buf->addConsumer(this);
88    }
89
90    if (LLVM_UNLIKELY(mStreamSetOutputs.size() != outputs.size())) {
91        report_fatal_error(getName() + ": expected " + std::to_string(mStreamSetOutputs.size())
92                           + " output stream sets but was given "
93                           + std::to_string(outputs.size()));
94    }
95
96    for (unsigned i = 0; i < outputs.size(); ++i) {
97        StreamSetBuffer * const buf = outputs[i];
98        if (LLVM_UNLIKELY(buf == nullptr)) {
99            report_fatal_error(getName() + ": output stream set " + std::to_string(i) + " cannot be null");
100        }
101        if (LLVM_LIKELY(buf->getProducer() == nullptr)) {
102            buf->setProducer(this);
103        } else {
104            report_fatal_error(getName() + ": output stream set " + std::to_string(i)
105                               + " is already produced by kernel " + buf->getProducer()->getName());
106        }
107    }
108
109    mStreamSetInputBuffers.assign(inputs.begin(), inputs.end());
110    mStreamSetOutputBuffers.assign(outputs.begin(), outputs.end());
111}
112
113std::string Kernel::getCacheName(const std::unique_ptr<KernelBuilder> & idb) const {
114    std::stringstream cacheName;
115    cacheName << getName() << '_' << idb->getBuilderUniqueName();
116    for (const StreamSetBuffer * b: mStreamSetInputBuffers) {
117        cacheName <<  ':' <<  b->getUniqueID();
118    }
119    for (const StreamSetBuffer * b: mStreamSetOutputBuffers) {
120        cacheName <<  ':' <<  b->getUniqueID();
121    }
122    return cacheName.str();
123}
124
125Module * Kernel::setModule(Module * const module) {
126    assert (mModule == nullptr || mModule == module);
127    assert (module != nullptr);
128    mModule = module;
129    return mModule;
130}
131
132Module * Kernel::makeModule(const std::unique_ptr<kernel::KernelBuilder> & idb) {
133    return setModule(new Module(getCacheName(idb), idb->getContext()));
134}
135
136void Kernel::prepareKernel(const std::unique_ptr<KernelBuilder> & idb) {
137    assert ("KernelBuilder does not have a valid IDISA Builder" && idb);
138    if (LLVM_UNLIKELY(mKernelStateType != nullptr)) {
139        report_fatal_error("Cannot prepare kernel after kernel state finalized");
140    }
141    const auto blockSize = idb->getBitBlockWidth();
142    if (mStride == 0) {
143        // Set the default kernel stride.
144        mStride = blockSize;
145    }
146    const auto requiredBlocks = codegen::SegmentSize + ((blockSize + mLookAheadPositions - 1) / blockSize);
147
148    IntegerType * const sizeTy = idb->getSizeTy();
149
150    for (unsigned i = 0; i < mStreamSetInputs.size(); i++) {
151        if ((mStreamSetInputBuffers[i]->getBufferBlocks() != 0) && (mStreamSetInputBuffers[i]->getBufferBlocks() < requiredBlocks)) {
152            //report_fatal_error(getName() + ": " + mStreamSetInputs[i].name + " requires buffer size " + std::to_string(requiredBlocks));
153        }
154        mScalarInputs.emplace_back(mStreamSetInputBuffers[i]->getStreamSetHandle()->getType(), mStreamSetInputs[i].name + BUFFER_PTR_SUFFIX);
155        if ((i == 0) || !mStreamSetInputs[i].rate.isExact()) {
156            addScalar(sizeTy, mStreamSetInputs[i].name + PROCESSED_ITEM_COUNT_SUFFIX);
157        }
158    }
159
160    for (unsigned i = 0; i < mStreamSetOutputs.size(); i++) {
161        mScalarInputs.emplace_back(mStreamSetOutputBuffers[i]->getStreamSetHandle()->getType(), mStreamSetOutputs[i].name + BUFFER_PTR_SUFFIX);
162        if ((mStreamSetInputs.empty() && (i == 0)) || !mStreamSetOutputs[i].rate.isExact()) {
163            addScalar(sizeTy, mStreamSetOutputs[i].name + PRODUCED_ITEM_COUNT_SUFFIX);
164        }
165    }
166    for (const auto & binding : mScalarInputs) {
167        addScalar(binding.type, binding.name);
168    }
169    for (const auto & binding : mScalarOutputs) {
170        addScalar(binding.type, binding.name);
171    }
172    if (mStreamMap.empty()) {
173        prepareStreamSetNameMap();
174    }
175    for (const auto & binding : mInternalScalars) {
176        addScalar(binding.type, binding.name);
177    }
178
179    Type * const consumerSetTy = StructType::get(sizeTy, sizeTy->getPointerTo()->getPointerTo(), nullptr)->getPointerTo();
180    for (unsigned i = 0; i < mStreamSetOutputs.size(); i++) {
181        addScalar(consumerSetTy, mStreamSetOutputs[i].name + CONSUMER_SUFFIX);
182    }
183
184    addScalar(sizeTy, LOGICAL_SEGMENT_NO_SCALAR);
185    addScalar(idb->getInt1Ty(), TERMINATION_SIGNAL);
186
187    for (unsigned i = 0; i < mStreamSetOutputs.size(); i++) {
188        addScalar(sizeTy, mStreamSetOutputs[i].name + CONSUMED_ITEM_COUNT_SUFFIX);
189    }
190
191    // We compile in a 64-bit CPU cycle counter into every kernel.   It will remain unused
192    // in normal execution, but when codegen::EnableCycleCounter is specified, pipelines
193    // will be able to add instrumentation to cached modules without recompilation.
194    addScalar(idb->getInt64Ty(), CYCLECOUNT_SCALAR);
195    addInternalKernelProperties(idb);
196    // NOTE: StructType::create always creates a new type even if an identical one exists.
197    if (LLVM_UNLIKELY(mModule == nullptr)) {
198        setModule(new Module(getCacheName(idb), idb->getContext()));
199    }
200    mKernelStateType = mModule->getTypeByName(getName());
201    if (LLVM_LIKELY(mKernelStateType == nullptr)) {
202        mKernelStateType = StructType::create(idb->getContext(), mKernelFields, getName());
203    }
204    processingRateAnalysis();
205}
206
207void Kernel::prepareCachedKernel(const std::unique_ptr<KernelBuilder> & idb) {
208
209    assert ("KernelBuilder does not have a valid IDISA Builder" && idb);
210    if (LLVM_UNLIKELY(mKernelStateType != nullptr)) {
211        report_fatal_error("Cannot prepare kernel after kernel state finalized");
212    }
213    assert (getModule());
214    const auto blockSize = idb->getBitBlockWidth();
215    if (mStride == 0) {
216        // Set the default kernel stride.
217        mStride = blockSize;
218    }
219    const auto requiredBlocks = codegen::SegmentSize + ((blockSize + mLookAheadPositions - 1) / blockSize);
220
221    IntegerType * const sizeTy = idb->getSizeTy();
222    for (unsigned i = 0; i < mStreamSetInputs.size(); i++) {
223        if ((mStreamSetInputBuffers[i]->getBufferBlocks() != 0) && (mStreamSetInputBuffers[i]->getBufferBlocks() < requiredBlocks)) {
224            //report_fatal_error(getName() + ": " + mStreamSetInputs[i].name + " requires buffer size " + std::to_string(requiredBlocks));
225        }
226        mScalarInputs.emplace_back(mStreamSetInputBuffers[i]->getStreamSetHandle()->getType(), mStreamSetInputs[i].name + BUFFER_PTR_SUFFIX);
227        if ((i == 0) || !mStreamSetInputs[i].rate.isExact()) {
228            addScalar(sizeTy, mStreamSetInputs[i].name + PROCESSED_ITEM_COUNT_SUFFIX);
229        }
230    }
231
232    for (unsigned i = 0; i < mStreamSetOutputs.size(); i++) {
233        mScalarInputs.emplace_back(mStreamSetOutputBuffers[i]->getStreamSetHandle()->getType(), mStreamSetOutputs[i].name + BUFFER_PTR_SUFFIX);
234        if ((mStreamSetInputs.empty() && (i == 0)) || !mStreamSetOutputs[i].rate.isExact()) {
235            addScalar(sizeTy, mStreamSetOutputs[i].name + PRODUCED_ITEM_COUNT_SUFFIX);
236        }
237    }
238    for (const auto & binding : mScalarInputs) {
239        addScalar(binding.type, binding.name);
240    }
241    for (const auto & binding : mScalarOutputs) {
242        addScalar(binding.type, binding.name);
243    }
244    if (mStreamMap.empty()) {
245        prepareStreamSetNameMap();
246    }
247    for (const auto & binding : mInternalScalars) {
248        addScalar(binding.type, binding.name);
249    }
250
251    Type * const consumerSetTy = StructType::get(sizeTy, sizeTy->getPointerTo()->getPointerTo(), nullptr)->getPointerTo();
252    for (unsigned i = 0; i < mStreamSetOutputs.size(); i++) {
253        addScalar(consumerSetTy, mStreamSetOutputs[i].name + CONSUMER_SUFFIX);
254    }
255
256    addScalar(sizeTy, LOGICAL_SEGMENT_NO_SCALAR);
257    addScalar(idb->getInt1Ty(), TERMINATION_SIGNAL);
258
259    for (unsigned i = 0; i < mStreamSetOutputs.size(); i++) {
260        addScalar(sizeTy, mStreamSetOutputs[i].name + CONSUMED_ITEM_COUNT_SUFFIX);
261    }
262
263    // We compile in a 64-bit CPU cycle counter into every kernel.   It will remain unused
264    // in normal execution, but when codegen::EnableCycleCounter is specified, pipelines
265    // will be able to add instrumentation to cached modules without recompilation.
266    addScalar(idb->getInt64Ty(), CYCLECOUNT_SCALAR);
267    mKernelStateType = getModule()->getTypeByName(getName());
268    assert (mKernelStateType);
269    processingRateAnalysis();
270}
271   
272void Kernel::processingRateAnalysis() {
273   
274    const unsigned inputSetCount = mStreamSetInputs.size();
275    const unsigned outputSetCount = mStreamSetOutputs.size();
276    const unsigned totalSetCount = inputSetCount + outputSetCount;
277   
278    mItemsPerStride.resize(totalSetCount);
279    mIsDerived.resize(totalSetCount);
280
281    mItemsPerStride[0] = mStride;
282    mIsDerived[0] = true;
283   
284    for (unsigned i = 0; i < inputSetCount; i++) {
285        // Default reference stream set is the principal input stream set.
286        auto & rate = mStreamSetInputs[i].rate;
287        if (rate.referenceStreamSet() == "") {
288            rate.setReferenceStreamSet(mStreamSetInputs[0].name);
289        }
290        Port port; unsigned ssIdx;
291        std::tie(port, ssIdx) = getStreamPort(rate.referenceStreamSet());
292        if ((port == Port::Output) || (ssIdx > i) || ((ssIdx == i) && (i > 0))) {
293            report_fatal_error(getName() + ": input set " + mStreamSetInputs[i].name + ": forward or circular rate dependency");
294        }
295        if ((rate.isExact() || rate.isMaxRatio()) && mIsDerived[ssIdx]) {
296            if ((mItemsPerStride[ssIdx] % rate.getRatioDenominator()) != 0) {
297                report_fatal_error(getName() + ": " + mStreamSetInputs[i].name + " processing rate denominator does not exactly divide items per stride.");
298            }
299            mItemsPerStride[i] = rate.calculateRatio(mItemsPerStride[ssIdx]);
300            mIsDerived[i] = rate.isExact();
301        }
302        else {
303            mIsDerived[i] = false;
304            mItemsPerStride[i] = 0;  // For unknown input rate, no items will be copied to temp buffers.
305        }
306    }
307   
308    for (unsigned i = inputSetCount; i < totalSetCount; i++) {
309        auto & rate = mStreamSetOutputs[i-inputSetCount].rate;
310        // Default reference stream set is the principal input stream set for the principal output stream set.
311        // Default reference stream set is the principal output stream set for other output stream sets.
312        if (rate.referenceStreamSet() == "") {
313            if ((mStreamSetInputs.size() > 0) && (i == inputSetCount)) {
314                rate.setReferenceStreamSet(mStreamSetInputs[0].name);
315            }
316            else {
317                rate.setReferenceStreamSet(mStreamSetOutputs[0].name);
318            }
319        }
320        Port port; unsigned ssIdx;
321        std::tie(port, ssIdx) = getStreamPort(rate.referenceStreamSet());
322        if (port == Port::Output) ssIdx += inputSetCount;
323        if ((ssIdx > i) || ((ssIdx == i) && (i > 0))) {
324            report_fatal_error(getName() + ": output set " + mStreamSetOutputs[i].name + ": forward or circular rate dependency");
325        }
326        if ((rate.isExact() || rate.isMaxRatio()) && mIsDerived[ssIdx]) {
327            if ((mItemsPerStride[ssIdx] % rate.getRatioDenominator()) != 0) {
328                report_fatal_error(getName() + ": " + mStreamSetOutputs[i-inputSetCount].name + " processing rate denominator does not exactly divide items per stride.");
329            }
330            mItemsPerStride[i] = rate.calculateRatio(mItemsPerStride[ssIdx]);
331            mIsDerived[i] = rate.isExact();
332        }
333        else {
334            mIsDerived[i] = false;
335            mItemsPerStride[i] = 0;  // For unknown output rate, no items will be copied to temp buffers.
336        }
337    }
338}
339
340
341// Default kernel signature: generate the IR and emit as byte code.
342std::string Kernel::makeSignature(const std::unique_ptr<kernel::KernelBuilder> & idb) {
343    assert ("KernelBuilder does not have a valid IDISA Builder" && idb.get());
344    if (LLVM_UNLIKELY(hasSignature())) {
345        generateKernel(idb);
346        std::string signature;
347        raw_string_ostream OS(signature);
348        WriteBitcodeToFile(getModule(), OS);
349        return signature;
350    } else {
351        return getModule()->getModuleIdentifier();
352    }
353}
354
355void Kernel::generateKernel(const std::unique_ptr<kernel::KernelBuilder> & idb) {
356    assert ("KernelBuilder does not have a valid IDISA Builder" && idb.get());
357    // If the module id cannot uniquely identify this kernel, "generateKernelSignature()" will have already
358    // generated the unoptimized IR.
359    if (!mIsGenerated) {
360        const auto m = idb->getModule();
361        const auto ip = idb->saveIP();
362        // const auto saveInstance = getInstance();
363        idb->setModule(mModule);
364        addKernelDeclarations(idb);
365        callGenerateInitializeMethod(idb);
366        callGenerateDoSegmentMethod(idb);
367        callGenerateFinalizeMethod(idb);
368        // setInstance(saveInstance);
369        idb->setModule(m);
370        idb->restoreIP(ip);
371        mIsGenerated = true;
372    }
373}
374
375inline void Kernel::callGenerateInitializeMethod(const std::unique_ptr<kernel::KernelBuilder> & idb) {
376    mCurrentMethod = getInitFunction(idb->getModule());
377    idb->SetInsertPoint(BasicBlock::Create(idb->getContext(), "entry", mCurrentMethod));
378    Function::arg_iterator args = mCurrentMethod->arg_begin();
379    setInstance(&*(args++));
380    idb->CreateStore(ConstantAggregateZero::get(mKernelStateType), getInstance());
381    for (const auto & binding : mScalarInputs) {
382        idb->setScalarField(binding.name, &*(args++));
383    }
384    for (const auto & binding : mStreamSetOutputs) {
385        idb->setConsumerLock(binding.name, &*(args++));
386    }
387    generateInitializeMethod(idb);
388    idb->CreateRetVoid();
389}
390
391inline void Kernel::callGenerateDoSegmentMethod(const std::unique_ptr<kernel::KernelBuilder> & idb) {
392    mCurrentMethod = getDoSegmentFunction(idb->getModule());
393    idb->SetInsertPoint(BasicBlock::Create(idb->getContext(), "entry", mCurrentMethod));
394    auto args = mCurrentMethod->arg_begin();
395    setInstance(&*(args++));
396    mIsFinal = &*(args++);
397    const auto n = mStreamSetInputs.size();
398    mAvailableItemCount.resize(n, nullptr);
399    for (unsigned i = 0; i < mStreamSetInputs.size(); i++) {
400        mAvailableItemCount[i] = &*(args++);
401    }
402    generateDoSegmentMethod(idb); // must be overridden by the KernelBuilder subtype
403    mIsFinal = nullptr;
404    mAvailableItemCount.clear();
405    idb->CreateRetVoid();
406}
407
408inline void Kernel::callGenerateFinalizeMethod(const std::unique_ptr<KernelBuilder> & idb) {
409    mCurrentMethod = getTerminateFunction(idb->getModule());
410    idb->SetInsertPoint(BasicBlock::Create(idb->getContext(), "entry", mCurrentMethod));
411    auto args = mCurrentMethod->arg_begin();
412    setInstance(&*(args++));
413    generateFinalizeMethod(idb); // may be overridden by the KernelBuilder subtype
414    const auto n = mScalarOutputs.size();
415    if (n == 0) {
416        idb->CreateRetVoid();
417    } else {
418        Value * outputs[n];
419        for (unsigned i = 0; i < n; ++i) {
420            outputs[i] = idb->getScalarField(mScalarOutputs[i].name);
421        }
422        if (n == 1) {
423            idb->CreateRet(outputs[0]);
424        } else {
425            idb->CreateAggregateRet(outputs, n);
426        }
427    }
428}
429
430unsigned Kernel::getScalarIndex(const std::string & name) const {
431    const auto f = mKernelMap.find(name);
432    if (LLVM_UNLIKELY(f == mKernelMap.end())) {
433        report_fatal_error(getName() + " does not contain scalar: " + name);
434    }
435    return f->second;
436}
437
438Value * Kernel::createInstance(const std::unique_ptr<KernelBuilder> & idb) {
439    assert ("KernelBuilder does not have a valid IDISA Builder" && idb);
440    if (LLVM_UNLIKELY(mKernelStateType == nullptr)) {
441        report_fatal_error("Cannot instantiate " + getName() + " before calling prepareKernel()");
442    }
443    setInstance(idb->CreateCacheAlignedAlloca(mKernelStateType));
444    return getInstance();
445}
446
447void Kernel::initializeInstance(const std::unique_ptr<KernelBuilder> & idb) {
448    assert ("KernelBuilder does not have a valid IDISA Builder" && idb);
449    if (LLVM_UNLIKELY(getInstance() == nullptr)) {
450        report_fatal_error("Cannot initialize " + getName() + " before calling createInstance()");
451    }
452    std::vector<Value *> args;
453    args.reserve(1 + mInitialArguments.size() + mStreamSetInputBuffers.size() + (mStreamSetOutputBuffers.size() * 2));
454    args.push_back(getInstance());
455    for (unsigned i = 0; i < mInitialArguments.size(); ++i) {
456        Value * arg = mInitialArguments[i];
457        if (LLVM_UNLIKELY(arg == nullptr)) {
458            report_fatal_error(getName() + ": initial argument " + std::to_string(i)
459                               + " cannot be null when calling createInstance()");
460        }
461        args.push_back(arg);
462    }
463    for (unsigned i = 0; i < mStreamSetInputBuffers.size(); ++i) {
464        assert (mStreamSetInputBuffers[i]);
465        Value * arg = mStreamSetInputBuffers[i]->getStreamSetHandle();
466        if (LLVM_UNLIKELY(arg == nullptr)) {
467            report_fatal_error(getName() + ": input stream set " + std::to_string(i)
468                               + " was not allocated prior to calling createInstance()");
469        }
470        args.push_back(arg);
471    }
472    assert (mStreamSetInputs.size() == mStreamSetInputBuffers.size());
473    for (unsigned i = 0; i < mStreamSetOutputBuffers.size(); ++i) {
474        assert (mStreamSetOutputBuffers[i]);
475        Value * arg = mStreamSetOutputBuffers[i]->getStreamSetHandle();
476        if (LLVM_UNLIKELY(arg == nullptr)) {
477            report_fatal_error(getName() + ": output stream set " + std::to_string(i)
478                               + " was not allocated prior to calling createInstance()");
479        }
480        args.push_back(arg);
481    }
482    assert (mStreamSetOutputs.size() == mStreamSetOutputBuffers.size());
483    IntegerType * const sizeTy = idb->getSizeTy();
484    PointerType * const sizePtrTy = sizeTy->getPointerTo();
485    PointerType * const sizePtrPtrTy = sizePtrTy->getPointerTo();
486    StructType * const consumerTy = StructType::get(sizeTy, sizePtrPtrTy, nullptr);
487    for (unsigned i = 0; i < mStreamSetOutputBuffers.size(); ++i) {
488        const auto output = mStreamSetOutputBuffers[i];
489        const auto & consumers = output->getConsumers();
490        const auto n = consumers.size();
491        AllocaInst * const outputConsumers = idb->CreateAlloca(consumerTy);
492        Value * const consumerSegNoArray = idb->CreateAlloca(ArrayType::get(sizePtrTy, n));
493        for (unsigned i = 0; i < n; ++i) {
494            Kernel * const consumer = consumers[i];
495            assert ("all instances must be created prior to initialization of any instance" && consumer->getInstance());
496            idb->setKernel(consumer);
497            Value * const segmentNoPtr = idb->getScalarFieldPtr(LOGICAL_SEGMENT_NO_SCALAR);
498            idb->CreateStore(segmentNoPtr, idb->CreateGEP(consumerSegNoArray, { idb->getInt32(0), idb->getInt32(i) }));
499        }
500        idb->setKernel(this);
501        Value * const consumerCountPtr = idb->CreateGEP(outputConsumers, {idb->getInt32(0), idb->getInt32(0)});
502        idb->CreateStore(idb->getSize(n), consumerCountPtr);
503        Value * const consumerSegNoArrayPtr = idb->CreateGEP(outputConsumers, {idb->getInt32(0), idb->getInt32(1)});
504        idb->CreateStore(idb->CreatePointerCast(consumerSegNoArray, sizePtrPtrTy), consumerSegNoArrayPtr);
505        args.push_back(outputConsumers);
506    }
507    idb->CreateCall(getInitFunction(idb->getModule()), args);
508}
509
510//  The default doSegment method dispatches to the doBlock routine for
511//  each block of the given number of blocksToDo, and then updates counts.
512
513void BlockOrientedKernel::generateDoSegmentMethod(const std::unique_ptr<KernelBuilder> & idb) {
514    BasicBlock * const entryBlock = idb->GetInsertBlock();
515    BasicBlock * const strideLoopCond = idb->CreateBasicBlock(getName() + "_strideLoopCond");
516    mStrideLoopBody = idb->CreateBasicBlock(getName() + "_strideLoopBody");
517    BasicBlock * const stridesDone = idb->CreateBasicBlock(getName() + "_stridesDone");
518    BasicBlock * const doFinalBlock = idb->CreateBasicBlock(getName() + "_doFinalBlock");
519    BasicBlock * const segmentDone = idb->CreateBasicBlock(getName() + "_segmentDone");
520
521    Value * baseTarget = nullptr;
522    if (idb->supportsIndirectBr()) {
523        baseTarget = idb->CreateSelect(mIsFinal, BlockAddress::get(doFinalBlock), BlockAddress::get(segmentDone));
524    }
525
526    ConstantInt * stride = idb->getSize(idb->getStride());
527    Value * availablePos = mAvailableItemCount[0];
528    Value * processed = idb->getProcessedItemCount(mStreamSetInputs[0].name);
529    Value * itemsAvail = idb->CreateSub(availablePos, processed);
530    Value * stridesToDo = idb->CreateUDiv(itemsAvail, stride);
531
532    idb->CreateBr(strideLoopCond);
533
534    idb->SetInsertPoint(strideLoopCond);
535
536    PHINode * branchTarget = nullptr;
537    if (idb->supportsIndirectBr()) {
538        branchTarget = idb->CreatePHI(baseTarget->getType(), 2, "branchTarget");
539        branchTarget->addIncoming(baseTarget, entryBlock);
540    }
541
542    PHINode * const stridesRemaining = idb->CreatePHI(idb->getSizeTy(), 2, "stridesRemaining");
543    stridesRemaining->addIncoming(stridesToDo, entryBlock);
544    // NOTE: stridesRemaining may go to a negative number in the final block if the generateFinalBlockMethod(...)
545    // calls CreateDoBlockMethodCall(). Do *not* replace the comparator with an unsigned one!
546    Value * notDone = idb->CreateICmpSGT(stridesRemaining, idb->getSize(0));
547    idb->CreateLikelyCondBr(notDone, mStrideLoopBody, stridesDone);
548
549    idb->SetInsertPoint(mStrideLoopBody);
550
551    if (idb->supportsIndirectBr()) {
552        mStrideLoopTarget = idb->CreatePHI(baseTarget->getType(), 2, "strideTarget");
553        mStrideLoopTarget->addIncoming(branchTarget, strideLoopCond);
554    }
555
556    /// GENERATE DO BLOCK METHOD
557
558    writeDoBlockMethod(idb);
559
560    /// UPDATE PROCESSED COUNTS
561
562    processed = idb->getProcessedItemCount(mStreamSetInputs[0].name);
563    Value * itemsDone = idb->CreateAdd(processed, stride);
564    idb->setProcessedItemCount(mStreamSetInputs[0].name, itemsDone);
565
566    stridesRemaining->addIncoming(idb->CreateSub(stridesRemaining, idb->getSize(1)), idb->GetInsertBlock());
567
568    BasicBlock * bodyEnd = idb->GetInsertBlock();
569    if (idb->supportsIndirectBr()) {
570        branchTarget->addIncoming(mStrideLoopTarget, bodyEnd);
571    }
572    idb->CreateBr(strideLoopCond);
573
574    stridesDone->moveAfter(bodyEnd);
575
576    idb->SetInsertPoint(stridesDone);
577
578    // Now conditionally perform the final block processing depending on the doFinal parameter.
579    if (idb->supportsIndirectBr()) {
580        mStrideLoopBranch = idb->CreateIndirectBr(branchTarget, 3);
581        mStrideLoopBranch->addDestination(doFinalBlock);
582        mStrideLoopBranch->addDestination(segmentDone);
583    } else {
584        idb->CreateUnlikelyCondBr(mIsFinal, doFinalBlock, segmentDone);
585    }
586
587    doFinalBlock->moveAfter(stridesDone);
588
589    idb->SetInsertPoint(doFinalBlock);
590
591    Value * remainingItems = idb->CreateSub(mAvailableItemCount[0], idb->getProcessedItemCount(mStreamSetInputs[0].name));
592
593    writeFinalBlockMethod(idb, remainingItems);
594
595    itemsDone = mAvailableItemCount[0];
596    idb->setProcessedItemCount(mStreamSetInputs[0].name, itemsDone);
597    idb->setTerminationSignal();
598    idb->CreateBr(segmentDone);
599
600    segmentDone->moveAfter(idb->GetInsertBlock());
601
602    idb->SetInsertPoint(segmentDone);
603
604    // Update the branch prediction metadata to indicate that the likely target will be segmentDone
605    if (idb->supportsIndirectBr()) {
606        MDBuilder mdb(idb->getContext());
607        const auto destinations = mStrideLoopBranch->getNumDestinations();
608        uint32_t weights[destinations];
609        for (unsigned i = 0; i < destinations; ++i) {
610            weights[i] = (mStrideLoopBranch->getDestination(i) == segmentDone) ? 100 : 1;
611        }
612        ArrayRef<uint32_t> bw(weights, destinations);
613        mStrideLoopBranch->setMetadata(LLVMContext::MD_prof, mdb.createBranchWeights(bw));
614    }
615
616}
617
618inline void BlockOrientedKernel::writeDoBlockMethod(const std::unique_ptr<KernelBuilder> & idb) {
619
620    Value * const self = getInstance();
621    Function * const cp = mCurrentMethod;
622    auto ip = idb->saveIP();
623    std::vector<Value *> availableItemCount(0);
624
625    /// Check if the do block method is called and create the function if necessary   
626    if (!idb->supportsIndirectBr()) {
627
628        std::vector<Type *> params;
629        params.reserve(1 + mAvailableItemCount.size());
630        params.push_back(self->getType());
631        for (Value * avail : mAvailableItemCount) {
632            params.push_back(avail->getType());
633        }
634
635        FunctionType * const type = FunctionType::get(idb->getVoidTy(), params, false);
636        mCurrentMethod = Function::Create(type, GlobalValue::InternalLinkage, getName() + DO_BLOCK_SUFFIX, idb->getModule());
637        mCurrentMethod->setCallingConv(CallingConv::C);
638        mCurrentMethod->setDoesNotThrow();
639        mCurrentMethod->setDoesNotCapture(1);
640        auto args = mCurrentMethod->arg_begin();
641        args->setName("self");
642        setInstance(&*args);
643        availableItemCount.reserve(mAvailableItemCount.size());
644        while (++args != mCurrentMethod->arg_end()) {
645            availableItemCount.push_back(&*args);
646        }
647        assert (availableItemCount.size() == mAvailableItemCount.size());
648        mAvailableItemCount.swap(availableItemCount);
649        idb->SetInsertPoint(BasicBlock::Create(idb->getContext(), "entry", mCurrentMethod));
650    }
651
652    std::vector<Value *> priorProduced;
653    for (unsigned i = 0; i < mStreamSetOutputs.size(); i++) {
654        if (isa<CircularCopybackBuffer>(mStreamSetOutputBuffers[i]) || isa<SwizzledCopybackBuffer>(mStreamSetOutputBuffers[i]))  {
655            priorProduced.push_back(idb->getProducedItemCount(mStreamSetOutputs[i].name));
656        }
657    }
658
659    generateDoBlockMethod(idb); // must be implemented by the BlockOrientedKernelBuilder subtype
660
661    unsigned priorIdx = 0;
662    for (unsigned i = 0; i < mStreamSetOutputs.size(); i++) {
663        Value * log2BlockSize = idb->getSize(std::log2(idb->getBitBlockWidth()));
664        if (SwizzledCopybackBuffer * const cb = dyn_cast<SwizzledCopybackBuffer>(mStreamSetOutputBuffers[i]))  {
665            BasicBlock * copyBack = idb->CreateBasicBlock(mStreamSetOutputs[i].name + "_copyBack");
666            BasicBlock * done = idb->CreateBasicBlock(mStreamSetOutputs[i].name + "_copyBackDone");
667            Value * newlyProduced = idb->CreateSub(idb->getProducedItemCount(mStreamSetOutputs[i].name), priorProduced[priorIdx]);
668            Value * priorBlock = idb->CreateLShr(priorProduced[priorIdx], log2BlockSize);
669            Value * priorOffset = idb->CreateAnd(priorProduced[priorIdx], idb->getSize(idb->getBitBlockWidth() - 1));
670            Value * instance = idb->getStreamSetBufferPtr(mStreamSetOutputs[i].name);
671            Value * accessibleBlocks = idb->getLinearlyAccessibleBlocks(mStreamSetOutputs[i].name, priorBlock);
672            Value * accessible = idb->CreateSub(idb->CreateShl(accessibleBlocks, log2BlockSize), priorOffset);
673            Value * wraparound = idb->CreateICmpULT(accessible, newlyProduced);
674            idb->CreateCondBr(wraparound, copyBack, done);
675            idb->SetInsertPoint(copyBack);
676            Value * copyItems = idb->CreateSub(newlyProduced, accessible);
677            cb->createCopyBack(idb.get(), instance, copyItems);
678            idb->CreateBr(done);
679            idb->SetInsertPoint(done);
680            priorIdx++;
681        }
682        if (CircularCopybackBuffer * const cb = dyn_cast<CircularCopybackBuffer>(mStreamSetOutputBuffers[i]))  {
683            BasicBlock * copyBack = idb->CreateBasicBlock(mStreamSetOutputs[i].name + "_copyBack");
684            BasicBlock * done = idb->CreateBasicBlock(mStreamSetOutputs[i].name + "_copyBackDone");
685            Value * instance = idb->getStreamSetBufferPtr(mStreamSetOutputs[i].name);
686            Value * newlyProduced = idb->CreateSub(idb->getProducedItemCount(mStreamSetOutputs[i].name), priorProduced[priorIdx]);
687            Value * accessible = idb->getLinearlyAccessibleItems(mStreamSetOutputs[i].name, priorProduced[priorIdx]);
688            Value * wraparound = idb->CreateICmpULT(accessible, newlyProduced);
689            idb->CreateCondBr(wraparound, copyBack, done);
690            idb->SetInsertPoint(copyBack);
691            Value * copyItems = idb->CreateSub(newlyProduced, accessible);
692            cb->createCopyBack(idb.get(), instance, copyItems);
693            idb->CreateBr(done);
694            idb->SetInsertPoint(done);
695            priorIdx++;
696        }
697    }
698
699    if (!idb->supportsIndirectBr()) {
700        // Restore the DoSegment function state then call the DoBlock method
701        idb->CreateRetVoid();
702        mDoBlockMethod = mCurrentMethod;
703        idb->restoreIP(ip);
704        setInstance(self);
705        mCurrentMethod = cp;
706        mAvailableItemCount.swap(availableItemCount);
707        CreateDoBlockMethodCall(idb);
708    }
709
710}
711
712inline void BlockOrientedKernel::writeFinalBlockMethod(const std::unique_ptr<KernelBuilder> & idb, Value * remainingItems) {
713
714    Value * const self = getInstance();
715    Function * const cp = mCurrentMethod;
716    Value * const remainingItemCount = remainingItems;
717    auto ip = idb->saveIP();
718    std::vector<Value *> availableItemCount(0);
719
720    if (!idb->supportsIndirectBr()) {
721        std::vector<Type *> params;
722        params.reserve(2 + mAvailableItemCount.size());
723        params.push_back(self->getType());
724        params.push_back(idb->getSizeTy());
725        for (Value * avail : mAvailableItemCount) {
726            params.push_back(avail->getType());
727        }
728        FunctionType * const type = FunctionType::get(idb->getVoidTy(), params, false);
729        mCurrentMethod = Function::Create(type, GlobalValue::InternalLinkage, getName() + FINAL_BLOCK_SUFFIX, idb->getModule());
730        mCurrentMethod->setCallingConv(CallingConv::C);
731        mCurrentMethod->setDoesNotThrow();
732        mCurrentMethod->setDoesNotCapture(1);
733        auto args = mCurrentMethod->arg_begin();
734        args->setName("self");
735        setInstance(&*args);
736        remainingItems = &*(++args);
737        remainingItems->setName("remainingItems");
738        availableItemCount.reserve(mAvailableItemCount.size());
739        while (++args != mCurrentMethod->arg_end()) {
740            availableItemCount.push_back(&*args);
741        }
742        assert (availableItemCount.size() == mAvailableItemCount.size());
743        mAvailableItemCount.swap(availableItemCount);
744        idb->SetInsertPoint(BasicBlock::Create(idb->getContext(), "entry", mCurrentMethod));
745    }
746
747    generateFinalBlockMethod(idb, remainingItems); // may be implemented by the BlockOrientedKernel subtype
748
749    RecursivelyDeleteTriviallyDeadInstructions(remainingItems); // if remainingItems was not used, this will eliminate it.
750
751    if (!idb->supportsIndirectBr()) {
752        idb->CreateRetVoid();
753        idb->restoreIP(ip);
754        setInstance(self);
755        mAvailableItemCount.swap(availableItemCount);
756        // Restore the DoSegment function state then call the DoFinal method
757        std::vector<Value *> args;
758        args.reserve(2 + mAvailableItemCount.size());
759        args.push_back(self);
760        args.push_back(remainingItemCount);
761        for (Value * avail : mAvailableItemCount) {
762            args.push_back(avail);
763        }
764        idb->CreateCall(mCurrentMethod, args);
765        mCurrentMethod = cp;
766    }
767
768}
769
770//  The default finalBlock method simply dispatches to the doBlock routine.
771void BlockOrientedKernel::generateFinalBlockMethod(const std::unique_ptr<KernelBuilder> & idb, Value * /* remainingItems */) {
772    CreateDoBlockMethodCall(idb);
773}
774
775void BlockOrientedKernel::CreateDoBlockMethodCall(const std::unique_ptr<KernelBuilder> & idb) {
776    if (idb->supportsIndirectBr()) {
777        BasicBlock * bb = idb->CreateBasicBlock("resume");
778        mStrideLoopBranch->addDestination(bb);
779        mStrideLoopTarget->addIncoming(BlockAddress::get(bb), idb->GetInsertBlock());
780        idb->CreateBr(mStrideLoopBody);
781        bb->moveAfter(idb->GetInsertBlock());
782        idb->SetInsertPoint(bb);
783    } else {
784        std::vector<Value *> args;
785        args.reserve(1 + mAvailableItemCount.size());
786        args.push_back(getInstance());
787        for (Value * avail : mAvailableItemCount) {
788            args.push_back(avail);
789        }
790        idb->CreateCall(mDoBlockMethod, args);
791    }
792}
793
794void MultiBlockKernel::generateDoSegmentMethod(const std::unique_ptr<KernelBuilder> & kb) {
795
796    // Stream set and buffer analysis.  When near the end of buffers
797    // or for final block processing, data for each streamset may need
798    // to be copied into temporary buffers to ensure linear access.
799    // Data is always copied as a number of whole blocks, dependent
800    // on the stream set processing rate.
801   
802    const unsigned bitBlockWidth = kb->getBitBlockWidth();
803    const unsigned inputSetCount = mStreamSetInputs.size();
804    const unsigned outputSetCount = mStreamSetOutputs.size();
805    const unsigned totalSetCount = inputSetCount + outputSetCount;
806   
807    int maxBlocksToCopy[totalSetCount];
808    for (unsigned i = 0; i < totalSetCount; i++) {
809        if (mIsDerived[i]) {
810            if (mItemsPerStride[i] % bitBlockWidth == 0) {
811                maxBlocksToCopy[i] = mItemsPerStride[i] / bitBlockWidth;
812            }
813            else {
814                // May not be block aligned, can overlap partial blocks at both ends.
815                maxBlocksToCopy[i] = mItemsPerStride[i]/bitBlockWidth + 2;
816            }
817        }
818        else {
819            // For variable input stream sets, we make a single stride of items
820            // available, if possible, but this stride could be nonaligned.
821            maxBlocksToCopy[i] = mStride / bitBlockWidth + 2;
822        }
823    }
824    auto ip = kb->saveIP();
825    Function * const cp = mCurrentMethod;
826    const auto saveInstance = getInstance();
827
828    // First prepare the multi-block method that will be used.
829
830    std::vector<Type *> multiBlockParmTypes;
831    multiBlockParmTypes.push_back(mKernelStateType->getPointerTo());
832    multiBlockParmTypes.push_back(kb->getSizeTy());
833    for (unsigned i = 1; i < mStreamSetInputs.size(); i++) {
834        if (!mIsDerived[i]) multiBlockParmTypes.push_back(kb->getSizeTy());
835    }
836    for (auto buffer : mStreamSetInputBuffers) {
837        multiBlockParmTypes.push_back(buffer->getPointerType());
838    }
839    for (auto buffer : mStreamSetOutputBuffers) {
840        multiBlockParmTypes.push_back(buffer->getPointerType());
841    }
842
843    FunctionType * const type = FunctionType::get(kb->getVoidTy(), multiBlockParmTypes, false);
844    Function * multiBlockFunction = Function::Create(type, GlobalValue::InternalLinkage, getName() + MULTI_BLOCK_SUFFIX, kb->getModule());
845    multiBlockFunction->setCallingConv(CallingConv::C);
846    multiBlockFunction->setDoesNotThrow();
847    mCurrentMethod = multiBlockFunction;
848    kb->SetInsertPoint(BasicBlock::Create(kb->getContext(), "multiBlockEntry", multiBlockFunction, 0));
849
850    auto args = multiBlockFunction->arg_begin();
851    args->setName("self");
852    setInstance(&*args);
853    (++args)->setName("itemsToDo");
854    for (unsigned i = 1; i < mStreamSetInputs.size(); i++) {
855        if (!mIsDerived[i]) (++args)->setName(mStreamSetInputs[i].name + "_availItems");
856    }
857    for (auto binding : mStreamSetInputs) {
858        (++args)->setName(binding.name + "BufPtr");
859    }
860    for (auto binding : mStreamSetOutputs) {
861        (++args)->setName(binding.name + "BufPtr");
862    }
863
864    // Now use the generateMultiBlockLogic method of the MultiBlockKernelBuilder subtype to
865    // provide the required multi-block kernel logic.
866    generateMultiBlockLogic(kb);
867
868    kb->CreateRetVoid();
869
870    kb->restoreIP(ip);
871    mCurrentMethod = cp;
872    setInstance(saveInstance);
873
874    // Now proceed with creation of the doSegment method.
875
876    BasicBlock * const entry = kb->GetInsertBlock();
877    BasicBlock * const doSegmentOuterLoop = kb->CreateBasicBlock(getName() + "_doSegmentOuterLoop");
878    BasicBlock * const doMultiBlockCall = kb->CreateBasicBlock(getName() + "_doMultiBlockCall");
879    BasicBlock * const tempBlockCheck = kb->CreateBasicBlock(getName() + "_tempBlockCheck");
880    BasicBlock * const doTempBufferBlock = kb->CreateBasicBlock(getName() + "_doTempBufferBlock");
881    BasicBlock * const segmentDone = kb->CreateBasicBlock(getName() + "_segmentDone");
882
883    Value * blockBaseMask = kb->CreateNot(kb->getSize(kb->getBitBlockWidth() - 1));
884    ConstantInt * blockSize = kb->getSize(kb->getBitBlockWidth());
885    ConstantInt * strideSize = kb->getSize(mStride);
886   
887    Value * availablePos = mAvailableItemCount[0];
888    Value * itemsAvail = availablePos;
889
890    //  Make sure that corresponding data is available depending on processing rate
891    //  for all derived input stream sets.
892    for (unsigned i = 1; i < mStreamSetInputs.size(); i++) {
893        Value * a = mAvailableItemCount[i];
894        auto & rate = mStreamSetInputs[i].rate;
895        if (mIsDerived[i]) {
896            Value * maxItems = rate.CreateMaxReferenceItemsCalculation(kb.get(), a);
897            itemsAvail = kb->CreateSelect(kb->CreateICmpULT(itemsAvail, maxItems), itemsAvail, maxItems);
898        }
899    }
900
901    Value * processed = kb->getProcessedItemCount(mStreamSetInputs[0].name);
902    Value * itemsToDo = kb->CreateSub(itemsAvail, processed);
903    Value * fullStridesToDo = kb->CreateUDiv(itemsToDo, strideSize);
904
905    //  Now we iteratively process these blocks using the doMultiBlock method.
906    //  In each iteration, we process the maximum number of linearly accessible
907    //  blocks on the principal input, reduced to ensure that the corresponding
908    //  data is linearly available at the specified processing rates for the other inputs,
909    //  and that each of the output buffers has sufficient linearly available space
910    //  (using overflow areas, if necessary) for the maximum output that can be
911    //  produced.
912
913    kb->CreateBr(doSegmentOuterLoop);
914    kb->SetInsertPoint(doSegmentOuterLoop);
915    PHINode * const stridesRemaining = kb->CreatePHI(kb->getSizeTy(), 2, "stridesRemaining");
916    stridesRemaining->addIncoming(fullStridesToDo, entry);
917
918    // For each input buffer, determine the processedItemCount, the block pointer for the
919    // buffer block containing the next item, and the number of linearly available items.
920
921    Value * processedItemCount[inputSetCount];
922    Value * inputBlockPtr[inputSetCount];
923    std::vector<Value *> producedItemCount;
924    std::vector<Value *> outputBlockPtr;
925
926    //  Now determine the linearly available blocks, based on blocks remaining reduced
927    //  by limitations of linearly available input buffer space.
928    Value * linearlyAvailStrides = stridesRemaining;
929    for (unsigned i = 0; i < inputSetCount; i++) {
930        Value * p = kb->getProcessedItemCount(mStreamSetInputs[i].name);
931        Value * blkNo = kb->CreateUDiv(p, blockSize);
932        Value * b = kb->getInputStreamBlockPtr(mStreamSetInputs[i].name, kb->getInt32(0));
933        // processedItemCount.push_back(p);
934        processedItemCount[i] = p;
935        // inputBlockPtr.push_back(b);
936        inputBlockPtr[i] = b;
937        auto & rate = mStreamSetInputs[i].rate;
938        if (rate.isUnknownRate()) continue;  // No calculation possible for unknown rates.
939        Value * linearlyAvailItems = kb->CreateMul(kb->getLinearlyAccessibleBlocks(mStreamSetInputs[i].name, blkNo), blockSize);
940        Value * maxReferenceItems = rate.CreateMaxReferenceItemsCalculation(kb.get(), linearlyAvailItems);
941        Value * maxStrides = kb->CreateUDiv(maxReferenceItems, strideSize);
942        linearlyAvailStrides = kb->CreateSelect(kb->CreateICmpULT(maxStrides, linearlyAvailStrides), maxStrides, linearlyAvailStrides);
943    }
944
945    //  Now determine the linearly writeable blocks, based on available blocks reduced
946    //  by limitations of output buffer space.
947    Value * linearlyWritableStrides = linearlyAvailStrides;
948    for (unsigned i = 0; i < outputSetCount; i++) {
949        Value * p = kb->getProducedItemCount(mStreamSetOutputs[i].name);
950        Value * blkNo = kb->CreateUDiv(p, blockSize);
951        Value * b = kb->getOutputStreamBlockPtr(mStreamSetOutputs[i].name, kb->getInt32(0));
952        producedItemCount.push_back(p);
953        outputBlockPtr.push_back(b);
954        auto & rate = mStreamSetOutputs[i].rate;
955        if (rate.isUnknownRate()) continue;  // No calculation possible for unknown rates.
956        Value * writableItems = kb->CreateMul(kb->getLinearlyWritableBlocks(mStreamSetOutputs[i].name, blkNo), blockSize);
957        Value * maxReferenceItems = rate.CreateMaxReferenceItemsCalculation(kb.get(), writableItems);
958        Value * maxStrides = kb->CreateUDiv(maxReferenceItems, strideSize);
959        linearlyWritableStrides = kb->CreateSelect(kb->CreateICmpULT(maxStrides, linearlyWritableStrides), maxStrides, linearlyWritableStrides);
960    }
961    Value * const haveFullStrides = kb->CreateICmpUGT(linearlyWritableStrides, kb->getSize(0));
962    kb->CreateCondBr(haveFullStrides, doMultiBlockCall, tempBlockCheck);
963
964    //  At this point we have verified the availability of one or more blocks of input data and output buffer space for all stream sets.
965    //  Now prepare the doMultiBlock call.
966    kb->SetInsertPoint(doMultiBlockCall);
967
968    Value * linearlyAvailItems = kb->CreateMul(linearlyWritableStrides, strideSize);
969
970    std::vector<Value *> doMultiBlockArgs;
971    doMultiBlockArgs.push_back(getInstance());
972    doMultiBlockArgs.push_back(linearlyAvailItems);
973    for (unsigned i = 1; i < mStreamSetInputs.size(); i++) {
974        if (!mIsDerived[i]) {
975            Value * avail = kb->CreateSub(mAvailableItemCount[i], processedItemCount[i]);
976            Value * linearlyAvail = kb->getLinearlyAccessibleItems(mStreamSetInputs[i].name, processedItemCount[i]);
977            doMultiBlockArgs.push_back(kb->CreateSelect(kb->CreateICmpULT(avail, linearlyAvail), avail, linearlyAvail));
978        }
979    }
980    for (unsigned i = 0; i < mStreamSetInputs.size(); i++) {
981        Value * bufPtr = kb->CreatePointerCast(inputBlockPtr[i], mStreamSetInputBuffers[i]->getPointerType());
982        doMultiBlockArgs.push_back(bufPtr);
983    }
984    for (unsigned i = 0; i < mStreamSetOutputs.size(); i++) {
985        Value * bufPtr = kb->CreatePointerCast(outputBlockPtr[i], mStreamSetOutputBuffers[i]->getPointerType());
986        doMultiBlockArgs.push_back(bufPtr);
987    }
988
989    kb->CreateCall(multiBlockFunction, doMultiBlockArgs);
990
991    // Do copybacks if necessary.
992    unsigned priorIdx = 0;
993    for (unsigned i = 0; i < mStreamSetOutputs.size(); i++) {       
994        if (auto cb = dyn_cast<SwizzledCopybackBuffer>(mStreamSetOutputBuffers[i]))  {
995            Value * log2BlockSize = kb->getSize(std::log2(kb->getBitBlockWidth()));
996            BasicBlock * copyBack = kb->CreateBasicBlock(mStreamSetOutputs[i].name + "_copyBack");
997            BasicBlock * done = kb->CreateBasicBlock(mStreamSetOutputs[i].name + "_copyBackDone");
998            Value * newlyProduced = kb->CreateSub(kb->getProducedItemCount(mStreamSetOutputs[i].name), producedItemCount[i]);
999            Value * priorBlock = kb->CreateLShr(producedItemCount[i], log2BlockSize);
1000            Value * priorOffset = kb->CreateAnd(producedItemCount[i], kb->getSize(kb->getBitBlockWidth() - 1));
1001            Value * instance = kb->getStreamSetBufferPtr(mStreamSetOutputs[i].name);
1002            Value * accessibleBlocks = kb->getLinearlyAccessibleBlocks(mStreamSetOutputs[i].name, priorBlock);
1003            Value * accessible = kb->CreateSub(kb->CreateShl(accessibleBlocks, log2BlockSize), priorOffset);
1004            Value * wraparound = kb->CreateICmpULT(accessible, newlyProduced);
1005            kb->CreateCondBr(wraparound, copyBack, done);
1006            kb->SetInsertPoint(copyBack);
1007            Value * copyItems = kb->CreateSub(newlyProduced, accessible);
1008            cb->createCopyBack(kb.get(), instance, copyItems);
1009            kb->CreateBr(done);
1010            kb->SetInsertPoint(done);
1011            priorIdx++;
1012        }
1013        if (auto cb = dyn_cast<CircularCopybackBuffer>(mStreamSetOutputBuffers[i]))  {
1014            BasicBlock * copyBack = kb->CreateBasicBlock(mStreamSetOutputs[i].name + "_copyBack");
1015            BasicBlock * done = kb->CreateBasicBlock(mStreamSetOutputs[i].name + "_copyBackDone");
1016            Value * instance = kb->getStreamSetBufferPtr(mStreamSetOutputs[i].name);
1017            Value * newlyProduced = kb->CreateSub(kb->getProducedItemCount(mStreamSetOutputs[i].name), producedItemCount[i]);
1018            Value * accessible = kb->getLinearlyAccessibleItems(mStreamSetOutputs[i].name, producedItemCount[i]);
1019            Value * wraparound = kb->CreateICmpULT(accessible, newlyProduced);
1020            kb->CreateCondBr(wraparound, copyBack, done);
1021            kb->SetInsertPoint(copyBack);
1022            Value * copyItems = kb->CreateSub(newlyProduced, accessible);
1023            cb->createCopyBack(kb.get(), instance, copyItems);
1024            kb->CreateBr(done);
1025            kb->SetInsertPoint(done);
1026            priorIdx++;
1027        }
1028    }
1029
1030    if (mIsDerived[0]) {
1031        Value * reducedStridesToDo = kb->CreateSub(stridesRemaining, linearlyWritableStrides);
1032        stridesRemaining->addIncoming(reducedStridesToDo, kb->GetInsertBlock());
1033        Value * nowProcessed = kb->CreateAdd(processedItemCount[0], linearlyAvailItems);
1034        kb->setProcessedItemCount(mStreamSetInputs[0].name, nowProcessed);
1035        kb->CreateBr(doSegmentOuterLoop);
1036    }
1037    else {
1038        // Processed item count updated by the kernel itself.
1039        Value * nowProcessed = kb->getProcessedItemCount(mStreamSetInputs[0].name);
1040        Value * remainingItemsToDo = kb->CreateSub(itemsAvail, nowProcessed);
1041        Value * reducedStridesToDo = kb->CreateUDiv(remainingItemsToDo, nowProcessed);
1042        stridesRemaining->addIncoming(reducedStridesToDo, kb->GetInsertBlock());
1043        // If we didn't make progress, we have gone as far as we can in this segment.
1044        kb->CreateCondBr(kb->CreateICmpUGT(nowProcessed, processedItemCount[0]), doSegmentOuterLoop, segmentDone);
1045    }
1046
1047    //
1048    // We use temporary buffers in 3 different cases that preclude full block processing.
1049    // (a) One or more input buffers does not have a sufficient number of input items linearly available.
1050    // (b) One or more output buffers does not have sufficient linearly available buffer space.
1051    // (c) We have processed all the full blocks of input and only the excessItems remain.
1052    // In each case we set up temporary buffers for input and output and then
1053    // call the Multiblock routine.
1054    //
1055
1056    kb->SetInsertPoint(tempBlockCheck);
1057    Value * const haveStrides = kb->CreateICmpUGT(stridesRemaining, kb->getSize(0));
1058    kb->CreateCondBr(kb->CreateOr(mIsFinal, haveStrides), doTempBufferBlock, segmentDone);
1059
1060    kb->SetInsertPoint(doTempBufferBlock);
1061    Value * excessItems = kb->CreateSub(itemsAvail, kb->getProcessedItemCount(mStreamSetInputs[0].name));
1062    Value * tempBlockItems = kb->CreateSelect(haveStrides, strideSize, excessItems);
1063    Value * doFinal = kb->CreateNot(haveStrides);
1064
1065    // Begin constructing the doMultiBlock args.
1066    std::vector<Value *> tempArgs;
1067    tempArgs.push_back(getInstance());
1068    tempArgs.push_back(tempBlockItems);
1069    // For non-derived inputs, add the available items.
1070    for (unsigned i = 1; i < mStreamSetInputs.size(); i++) {
1071        if (!mIsDerived[i]) {
1072            Value * avail = kb->CreateSub(mAvailableItemCount[i], processedItemCount[i]);
1073            tempArgs.push_back(kb->CreateSelect(kb->CreateICmpULT(avail, strideSize), avail, strideSize));
1074        }
1075    }
1076    //
1077    // Define and allocate the temporary buffer area.
1078    //
1079    Type * tempBuffers[totalSetCount];
1080    for (unsigned i = 0; i < inputSetCount; ++i) {
1081        Type * bufType = mStreamSetInputBuffers[i]->getStreamSetBlockType();
1082        tempBuffers[i] = ArrayType::get(bufType, maxBlocksToCopy[i]);
1083    }
1084    for (unsigned i = 0; i < outputSetCount; i++) {
1085        Type * bufType = mStreamSetOutputBuffers[i]->getStreamSetBlockType();
1086        tempBuffers[i + inputSetCount] = ArrayType::get(bufType, maxBlocksToCopy[i + inputSetCount]);
1087    }
1088    Type * tempParameterStructType = StructType::create(kb->getContext(), ArrayRef<Type *>(tempBuffers, totalSetCount), "tempBuf");
1089    // Prepare the temporary buffer area.
1090    Value * tempParameterArea = kb->CreateCacheAlignedAlloca(tempParameterStructType);
1091    kb->CreateMemZero(tempParameterArea, ConstantExpr::getSizeOf(tempParameterStructType));
1092    // For each input and output buffer, copy over necessary data starting from the last block boundary.
1093    Value * itemCountNeeded[inputSetCount];
1094    itemCountNeeded[0] = tempBlockItems;
1095    Value * finalItemCountNeeded[inputSetCount];
1096
1097    for (unsigned i = 0; i < inputSetCount; i++) {
1098        Type * bufPtrType = mStreamSetInputBuffers[i]->getPointerType();
1099        if (mItemsPerStride[i] != 0) {
1100            Value * tempBufPtr = kb->CreateGEP(tempParameterArea, {kb->getInt32(0), kb->getInt32(i)});
1101            tempBufPtr = kb->CreatePointerCast(tempBufPtr, bufPtrType);
1102            ConstantInt * strideItems = kb->getSize(mItemsPerStride[i]);
1103            Value * strideBasePos = kb->CreateSub(processedItemCount[i], kb->CreateURem(processedItemCount[i], strideItems));
1104            Value * blockBasePos = strideBasePos;
1105            if (mItemsPerStride[i] & (bitBlockWidth - 1)) {
1106                blockBasePos = kb->CreateAnd(strideBasePos, blockBaseMask);
1107            }
1108
1109            // The number of items to copy is determined by the processing rate requirements.
1110            if (i >= 1) {
1111                auto & rate = mStreamSetInputs[i].rate;
1112                std::string refSet = mStreamSetInputs[i].rate.referenceStreamSet();
1113                Port port; unsigned ssIdx;
1114                std::tie(port, ssIdx) = getStreamPort(refSet);
1115                itemCountNeeded[i] = rate.CreateRatioCalculation(kb.get(), itemCountNeeded[ssIdx], doFinal);
1116            }
1117            finalItemCountNeeded[i] = kb->CreateAdd(itemCountNeeded[i], processedItemCount[i]);
1118
1119            Value * inputPtr = kb->CreatePointerCast(kb->getRawInputPointer(mStreamSetInputs[i].name, kb->getInt32(0), blockBasePos), bufPtrType);
1120           
1121            if (maxBlocksToCopy[i] == 1) {
1122                // copy one block
1123                mStreamSetInputBuffers[i]->createBlockCopy(kb.get(), tempBufPtr, inputPtr, kb->getSize(1));
1124            }
1125            else {
1126                Value * neededItems = kb->CreateSub(finalItemCountNeeded[i], blockBasePos);
1127                Value * availFromBase = kb->getLinearlyAccessibleItems(mStreamSetInputs[i].name, blockBasePos);
1128                Value * allAvail = kb->CreateICmpULE(neededItems, availFromBase);
1129                Value * copyItems1 = kb->CreateSelect(allAvail, neededItems, availFromBase);
1130                //mStreamSetInputBuffers[i]->createBlockAlignedCopy(kb.get(), tempBufPtr, inputPtr, copyItems1);
1131                Value * copyBlocks1 = kb->CreateUDivCeil(copyItems1, blockSize);
1132                mStreamSetInputBuffers[i]->createBlockCopy(kb.get(), tempBufPtr, inputPtr, copyBlocks1);
1133                BasicBlock * copyRemaining = kb->CreateBasicBlock("copyRemaining");
1134                BasicBlock * copyDone = kb->CreateBasicBlock("copyDone");
1135                kb->CreateCondBr(allAvail, copyDone, copyRemaining);
1136                kb->SetInsertPoint(copyRemaining);
1137                Value * copyItems2 = kb->CreateSub(neededItems, copyItems1);
1138                Value * copyBlocks2 = kb->CreateUDivCeil(copyItems2, blockSize);
1139                //Value * nextBasePos = kb->CreateAdd(blockBasePos, copyItems1);
1140                Value * nextBasePos = kb->CreateAdd(blockBasePos, kb->CreateMul(copyBlocks2, blockSize));
1141                Value * nextInputPtr = kb->CreatePointerCast(kb->getRawInputPointer(mStreamSetInputs[i].name, kb->getInt32(0), nextBasePos), bufPtrType);
1142                Value * nextBufPtr = kb->CreateGEP(tempBufPtr, kb->CreateUDiv(copyItems1, blockSize));
1143                //mStreamSetInputBuffers[i]->createBlockAlignedCopy(kb.get(), nextBufPtr, nextInputPtr, copyItems2);
1144                mStreamSetInputBuffers[i]->createBlockCopy(kb.get(), nextBufPtr, nextInputPtr, copyBlocks2);
1145                kb->CreateBr(copyDone);
1146                kb->SetInsertPoint(copyDone);
1147            }
1148            tempArgs.push_back(tempBufPtr);
1149        } else {
1150            Value * bufPtr = kb->getInputStreamBlockPtr(mStreamSetInputs[i].name, kb->getInt32(0));
1151            bufPtr = kb->CreatePointerCast(bufPtr, mStreamSetInputBuffers[i]->getPointerType());
1152            tempArgs.push_back(bufPtr);           
1153        }
1154    }
1155    Value * outputBasePos[outputSetCount];
1156    for (unsigned i = 0; i < outputSetCount; i++) {
1157        Value * tempBufPtr = kb->CreateGEP(tempParameterArea,  {kb->getInt32(0), kb->getInt32(inputSetCount + i)});
1158        Type * bufPtrType = mStreamSetOutputBuffers[i]->getPointerType();
1159        tempBufPtr = kb->CreatePointerCast(tempBufPtr, bufPtrType);
1160        producedItemCount[i] = kb->getProducedItemCount(mStreamSetOutputs[i].name);
1161        outputBasePos[i] = kb->CreateAnd(producedItemCount[i], blockBaseMask);
1162        //mStreamSetOutputBuffers[i]->createBlockAlignedCopy(kb.get(), tempBufPtr, outputBlockPtr[i], kb->CreateSub(producedItemCount[i], outputBasePos[i]));
1163        Value * copyBlocks = kb->CreateUDivCeil(kb->CreateSub(producedItemCount[i], outputBasePos[i]), blockSize);
1164        mStreamSetOutputBuffers[i]->createBlockCopy(kb.get(), tempBufPtr, outputBlockPtr[i], copyBlocks);
1165        tempArgs.push_back(tempBufPtr);
1166    }
1167
1168    kb->CreateCall(multiBlockFunction, tempArgs);
1169   
1170    //  The items have been processed and output generated to the temporary areas.
1171    //  Update the processed item count (and hence all the counts derived automatically
1172    //  therefrom).
1173    if (mIsDerived[0]) {
1174        kb->setProcessedItemCount(mStreamSetInputs[0].name, finalItemCountNeeded[0]);
1175    }
1176   
1177    // Copy back data to the actual output buffers.
1178    for (unsigned i = 0; i < mStreamSetOutputBuffers.size(); i++) {
1179        Value * tempBufPtr = kb->CreateGEP(tempParameterArea,  {kb->getInt32(0), kb->getInt32(mStreamSetInputs.size() + i)});
1180        tempBufPtr = kb->CreatePointerCast(tempBufPtr, mStreamSetOutputBuffers[i]->getPointerType());
1181        Value * finalOutputItems = kb->getProducedItemCount(mStreamSetOutputs[i].name);
1182        Value * copyItems = kb->CreateSub(finalOutputItems, outputBasePos[i]);
1183        // Round up to exact multiple of block size.
1184        //copyItems = kb->CreateAnd(kb->CreateAdd(copyItems, kb->getSize(bitBlockWidth - 1)), blockBaseMask);
1185        Value * writableFromBase = kb->getLinearlyWritableItems(mStreamSetOutputs[i].name, outputBasePos[i]); // must be a whole number of blocks.
1186        Value * allWritable = kb->CreateICmpULE(copyItems, writableFromBase);
1187        Value * copyItems1 = kb->CreateSelect(allWritable, copyItems, writableFromBase);
1188        //mStreamSetOutputBuffers[i]->createBlockAlignedCopy(kb.get(), outputBlockPtr[i], tempBufPtr, copyItems1);
1189        Value * copyBlocks1 = kb->CreateUDivCeil(copyItems1, blockSize);
1190        mStreamSetOutputBuffers[i]->createBlockCopy(kb.get(), outputBlockPtr[i], tempBufPtr, copyBlocks1);
1191        BasicBlock * copyBackRemaining = kb->CreateBasicBlock("copyBackRemaining");
1192        BasicBlock * copyBackDone = kb->CreateBasicBlock("copyBackDone");
1193        kb->CreateCondBr(allWritable, copyBackDone, copyBackRemaining);
1194        kb->SetInsertPoint(copyBackRemaining);
1195        Value * copyItems2 = kb->CreateSub(copyItems, copyItems1);
1196        Value * nextBasePos = kb->CreateAdd(outputBasePos[i], copyItems1);
1197        Type * bufPtrType = mStreamSetOutputBuffers[i]->getPointerType();
1198        Value * nextOutputPtr = kb->CreatePointerCast(kb->getRawOutputPointer(mStreamSetOutputs[i].name, kb->getInt32(0), nextBasePos), bufPtrType);
1199        tempBufPtr = kb->CreateGEP(tempBufPtr, kb->CreateUDiv(copyItems1, blockSize));
1200        //mStreamSetOutputBuffers[i]->createBlockAlignedCopy(kb.get(), nextOutputPtr, tempBufPtr, copyItems2);
1201        Value * copyBlocks2 = kb->CreateUDivCeil(copyItems2, blockSize);
1202        mStreamSetOutputBuffers[i]->createBlockCopy(kb.get(), nextOutputPtr, tempBufPtr, copyBlocks2);
1203        kb->CreateBr(copyBackDone);
1204        kb->SetInsertPoint(copyBackDone);
1205    }
1206
1207    //  We've dealt with the partial block processing and copied information back into the
1208    //  actual buffers.  If this isn't the final block, loop back for more multiblock processing.
1209    //
1210    BasicBlock * setTermination = kb->CreateBasicBlock("mBsetTermination");
1211    if (mIsDerived[0]) {
1212        stridesRemaining->addIncoming(kb->CreateSub(stridesRemaining, kb->CreateZExt(haveStrides, kb->getSizeTy())), kb->GetInsertBlock());
1213        kb->CreateCondBr(haveStrides, doSegmentOuterLoop, setTermination);
1214    }
1215    else {
1216        Value * nowProcessed = kb->getProcessedItemCount(mStreamSetInputs[0].name);
1217        Value * remainingItemsToDo = kb->CreateSub(itemsAvail, nowProcessed);
1218        Value * reducedStridesToDo = kb->CreateUDiv(remainingItemsToDo, nowProcessed);
1219        stridesRemaining->addIncoming(reducedStridesToDo, kb->GetInsertBlock());
1220        Value * haveStrides = kb->CreateICmpUGT(reducedStridesToDo, kb->getSize(0));
1221        kb->CreateCondBr(haveStrides, doSegmentOuterLoop, setTermination);
1222    }   
1223    kb->SetInsertPoint(setTermination);
1224    kb->setTerminationSignal();
1225    kb->CreateBr(segmentDone);
1226    kb->SetInsertPoint(segmentDone);
1227}
1228
1229void Kernel::finalizeInstance(const std::unique_ptr<KernelBuilder> & idb) {
1230    assert ("KernelBuilder does not have a valid IDISA Builder" && idb);
1231    mOutputScalarResult = idb->CreateCall(getTerminateFunction(idb->getModule()), { getInstance() });
1232}
1233
1234Kernel::StreamPort Kernel::getStreamPort(const std::string & name) const {
1235    const auto f = mStreamMap.find(name);
1236    if (LLVM_UNLIKELY(f == mStreamMap.end())) {
1237        report_fatal_error(getName() + " does not contain stream set " + name);
1238    }
1239    return f->second;
1240}
1241
1242static inline std::string annotateKernelNameWithDebugFlags(std::string && name) {
1243    if (codegen::EnableAsserts) {
1244        name += "_EA";
1245    }
1246    name += "_O" + std::to_string((int)codegen::OptLevel);
1247    return name;
1248}
1249
1250// CONSTRUCTOR
1251Kernel::Kernel(std::string && kernelName,
1252               std::vector<Binding> && stream_inputs,
1253               std::vector<Binding> && stream_outputs,
1254               std::vector<Binding> && scalar_parameters,
1255               std::vector<Binding> && scalar_outputs,
1256               std::vector<Binding> && internal_scalars)
1257: KernelInterface(annotateKernelNameWithDebugFlags(std::move(kernelName))
1258                  , std::move(stream_inputs), std::move(stream_outputs)
1259                  , std::move(scalar_parameters), std::move(scalar_outputs)
1260                  , std::move(internal_scalars))
1261, mCurrentMethod(nullptr)
1262, mNoTerminateAttribute(false)
1263, mIsGenerated(false)
1264, mIsFinal(nullptr)
1265, mOutputScalarResult(nullptr)
1266, mStride(0) {
1267
1268}
1269
1270Kernel::~Kernel() {
1271
1272}
1273
1274// CONSTRUCTOR
1275BlockOrientedKernel::BlockOrientedKernel(std::string && kernelName,
1276                                         std::vector<Binding> && stream_inputs,
1277                                         std::vector<Binding> && stream_outputs,
1278                                         std::vector<Binding> && scalar_parameters,
1279                                         std::vector<Binding> && scalar_outputs,
1280                                         std::vector<Binding> && internal_scalars)
1281: Kernel(std::move(kernelName), std::move(stream_inputs), std::move(stream_outputs), std::move(scalar_parameters), std::move(scalar_outputs), std::move(internal_scalars))
1282, mDoBlockMethod(nullptr)
1283, mStrideLoopBody(nullptr)
1284, mStrideLoopBranch(nullptr)
1285, mStrideLoopTarget(nullptr) {
1286
1287}
1288
1289// CONSTRUCTOR
1290MultiBlockKernel::MultiBlockKernel(std::string && kernelName,
1291                                   std::vector<Binding> && stream_inputs,
1292                                   std::vector<Binding> && stream_outputs,
1293                                   std::vector<Binding> && scalar_parameters,
1294                                   std::vector<Binding> && scalar_outputs,
1295                                   std::vector<Binding> && internal_scalars)
1296: Kernel(std::move(kernelName), std::move(stream_inputs), std::move(stream_outputs), std::move(scalar_parameters), std::move(scalar_outputs), std::move(internal_scalars)) {
1297}
1298
1299// CONSTRUCTOR
1300SegmentOrientedKernel::SegmentOrientedKernel(std::string && kernelName,
1301                                             std::vector<Binding> && stream_inputs,
1302                                             std::vector<Binding> && stream_outputs,
1303                                             std::vector<Binding> && scalar_parameters,
1304                                             std::vector<Binding> && scalar_outputs,
1305                                             std::vector<Binding> && internal_scalars)
1306: Kernel(std::move(kernelName), std::move(stream_inputs), std::move(stream_outputs), std::move(scalar_parameters), std::move(scalar_outputs), std::move(internal_scalars)) {
1307   
1308}
1309 
1310   
1311void applyOutputBufferExpansions(const std::unique_ptr<KernelBuilder> & kb,
1312                                 std::vector<Value *> inputAvailable,
1313                                 Value * doFinal) {
1314    auto kernel = kb->getKernel();
1315    const unsigned inputSetCount = inputAvailable.size();
1316    if (inputSetCount == 0) return;  //  Cannot calculate buffer items expected from input.
1317    auto & outputs = kernel->getStreamSetOutputBuffers();
1318    const unsigned outputSetCount = outputs.size();
1319
1320    Constant * blockSize = kb->getSize(kb->getBitBlockWidth());
1321    Value * newlyAvailInputItems[inputSetCount];
1322    Value * requiredOutputBufferSpace[outputSetCount];
1323    for (unsigned i = 0; i < inputSetCount; i++) {
1324        Value * processed = kb->getProcessedItemCount(kernel->getStreamInput(i).name);
1325        newlyAvailInputItems[i] = kb->CreateSub(inputAvailable[i], processed);
1326    }
1327    //kb->GetInsertBlock()->dump();
1328    for (unsigned i = 0; i < outputSetCount; i++) {
1329        requiredOutputBufferSpace[i] = nullptr;
1330        auto & rate = kernel->getStreamOutput(i).rate;
1331        if (rate.isUnknownRate()) continue;  // No calculations possible.
1332        Kernel::Port port; unsigned ssIdx;
1333        std::tie(port, ssIdx) = kernel->getStreamPort(rate.referenceStreamSet());
1334        if (port == Kernel::Port::Output) {
1335            requiredOutputBufferSpace[i] = rate.CreateRatioCalculation(kb.get(), requiredOutputBufferSpace[ssIdx], doFinal);
1336        }
1337        else {
1338            requiredOutputBufferSpace[i] = rate.CreateRatioCalculation(kb.get(), newlyAvailInputItems[ssIdx], doFinal);
1339        }
1340        if (auto db = dyn_cast<DynamicBuffer>(outputs[i])) {
1341            Value * handle = db->getStreamSetHandle();
1342            // This buffer can be expanded.
1343            Value * producedBlock = kb->CreateUDivCeil(kb->getProducedItemCount(kernel->getStreamOutput(i).name), blockSize);
1344            Value * consumedBlock = kb->CreateUDiv(kb->getConsumedItemCount(kernel->getStreamOutput(i).name), blockSize);
1345            Value * blocksInUse = kb->CreateSub(producedBlock, consumedBlock);
1346            Value * blocksRequired = kb->CreateAdd(blocksInUse, kb->CreateUDivCeil(requiredOutputBufferSpace[i], blockSize));
1347            Value * spaceRequired = kb->CreateMul(blocksRequired, blockSize);
1348            Value * expansionNeeded = kb->CreateICmpUGT(spaceRequired, db->getBufferedSize(kb.get(), handle));
1349            BasicBlock * doExpand = kb->CreateBasicBlock("doExpand");
1350            BasicBlock * bufferReady = kb->CreateBasicBlock("bufferReady");
1351            kb->CreateCondBr(expansionNeeded, doExpand, bufferReady);
1352            kb->SetInsertPoint(doExpand);
1353            db->doubleCapacity(kb.get(), handle);
1354            // Ensure that capacity is sufficient by successive doubling, if necessary.
1355            expansionNeeded = kb->CreateICmpUGT(spaceRequired, db->getBufferedSize(kb.get(), handle));
1356            kb->CreateCondBr(expansionNeeded, doExpand, bufferReady);
1357            kb->SetInsertPoint(bufferReady);
1358        }
1359    }
1360
1361}
1362
1363}
Note: See TracBrowser for help on using the repository browser.