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

Last change on this file since 5630 was 5630, checked in by nmedfort, 21 months ago

Partial check-in for avoidance of compiling Pablo/LLVM code to determine the Kernel struct type when using a cached object. Inactive RE alternation minimization check in.

File size: 65.1 KB
Line 
1/*
2 *  Copyright (c) 2016 International Characters.
3 *  This software is licensed to the public under the Open Software License 3.0.
4 */
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    Value * excessItems = kb->CreateURem(itemsToDo, strideSize);
905
906    //  Now we iteratively process these blocks using the doMultiBlock method.
907    //  In each iteration, we process the maximum number of linearly accessible
908    //  blocks on the principal input, reduced to ensure that the corresponding
909    //  data is linearly available at the specified processing rates for the other inputs,
910    //  and that each of the output buffers has sufficient linearly available space
911    //  (using overflow areas, if necessary) for the maximum output that can be
912    //  produced.
913
914    kb->CreateBr(doSegmentOuterLoop);
915    kb->SetInsertPoint(doSegmentOuterLoop);
916    PHINode * const stridesRemaining = kb->CreatePHI(kb->getSizeTy(), 2, "stridesRemaining");
917    stridesRemaining->addIncoming(fullStridesToDo, entry);
918
919    // For each input buffer, determine the processedItemCount, the block pointer for the
920    // buffer block containing the next item, and the number of linearly available items.
921
922    Value * processedItemCount[inputSetCount];
923    Value * inputBlockPtr[inputSetCount];
924    std::vector<Value *> producedItemCount;
925    std::vector<Value *> outputBlockPtr;
926
927    //  Now determine the linearly available blocks, based on blocks remaining reduced
928    //  by limitations of linearly available input buffer space.
929    Value * linearlyAvailStrides = stridesRemaining;
930    for (unsigned i = 0; i < inputSetCount; i++) {
931        Value * p = kb->getProcessedItemCount(mStreamSetInputs[i].name);
932        Value * blkNo = kb->CreateUDiv(p, blockSize);
933        Value * b = kb->getInputStreamBlockPtr(mStreamSetInputs[i].name, kb->getInt32(0));
934        // processedItemCount.push_back(p);
935        processedItemCount[i] = p;
936        // inputBlockPtr.push_back(b);
937        inputBlockPtr[i] = b;
938        auto & rate = mStreamSetInputs[i].rate;
939        if (rate.isUnknownRate()) continue;  // No calculation possible for unknown rates.
940        Value * maxReferenceItems = nullptr;
941        if ((rate.isFixedRatio()) && (rate.getRatioNumerator() == rate.getRatioDenominator())) {
942            maxReferenceItems = kb->CreateMul(kb->getLinearlyAccessibleBlocks(mStreamSetInputs[i].name, blkNo), blockSize);
943
944        } else {
945            Value * linearlyAvailItems = kb->getLinearlyAccessibleItems(mStreamSetInputs[i].name, p);
946            maxReferenceItems = rate.CreateMaxReferenceItemsCalculation(kb.get(), linearlyAvailItems);
947        }
948        Value * maxStrides = kb->CreateUDiv(maxReferenceItems, strideSize);
949        linearlyAvailStrides = kb->CreateSelect(kb->CreateICmpULT(maxStrides, linearlyAvailStrides), maxStrides, linearlyAvailStrides);
950    }
951
952    //  Now determine the linearly writeable blocks, based on available blocks reduced
953    //  by limitations of output buffer space.
954    Value * linearlyWritableStrides = linearlyAvailStrides;
955    for (unsigned i = 0; i < outputSetCount; i++) {
956        Value * p = kb->getProducedItemCount(mStreamSetOutputs[i].name);
957        Value * blkNo = kb->CreateUDiv(p, blockSize);
958        Value * b = kb->getOutputStreamBlockPtr(mStreamSetOutputs[i].name, kb->getInt32(0));
959        producedItemCount.push_back(p);
960        outputBlockPtr.push_back(b);
961        auto & rate = mStreamSetOutputs[i].rate;
962        if (rate.isUnknownRate()) continue;  // No calculation possible for unknown rates.
963        Value * maxReferenceItems = nullptr;
964        if ((rate.isFixedRatio()) && (rate.getRatioNumerator() == rate.getRatioDenominator())) {
965            maxReferenceItems = kb->CreateMul(kb->getLinearlyWritableBlocks(mStreamSetOutputs[i].name, blkNo), blockSize);
966        } else {
967            Value * writableItems = kb->getLinearlyWritableItems(mStreamSetOutputs[i].name, p);
968            maxReferenceItems = rate.CreateMaxReferenceItemsCalculation(kb.get(), writableItems);
969        }
970        Value * maxStrides = kb->CreateUDiv(maxReferenceItems, strideSize);
971        linearlyWritableStrides = kb->CreateSelect(kb->CreateICmpULT(maxStrides, linearlyWritableStrides), maxStrides, linearlyWritableStrides);
972    }
973    Value * const haveFullStrides = kb->CreateICmpUGT(linearlyWritableStrides, kb->getSize(0));
974    kb->CreateCondBr(haveFullStrides, doMultiBlockCall, tempBlockCheck);
975
976    //  At this point we have verified the availability of one or more blocks of input data and output buffer space for all stream sets.
977    //  Now prepare the doMultiBlock call.
978    kb->SetInsertPoint(doMultiBlockCall);
979
980    Value * linearlyAvailItems = kb->CreateMul(linearlyWritableStrides, strideSize);
981
982    std::vector<Value *> doMultiBlockArgs;
983    doMultiBlockArgs.push_back(getInstance());
984    doMultiBlockArgs.push_back(linearlyAvailItems);
985    for (unsigned i = 1; i < mStreamSetInputs.size(); i++) {
986        if (!mIsDerived[i]) {
987            Value * avail = kb->CreateSub(mAvailableItemCount[i], processedItemCount[i]);
988            Value * linearlyAvail = kb->getLinearlyAccessibleItems(mStreamSetInputs[i].name, processedItemCount[i]);
989            doMultiBlockArgs.push_back(kb->CreateSelect(kb->CreateICmpULT(avail, linearlyAvail), avail, linearlyAvail));
990        }
991    }
992    for (unsigned i = 0; i < mStreamSetInputs.size(); i++) {
993        Value * bufPtr = kb->CreatePointerCast(inputBlockPtr[i], mStreamSetInputBuffers[i]->getPointerType());
994        doMultiBlockArgs.push_back(bufPtr);
995    }
996    for (unsigned i = 0; i < mStreamSetOutputs.size(); i++) {
997        Value * bufPtr = kb->CreatePointerCast(outputBlockPtr[i], mStreamSetOutputBuffers[i]->getPointerType());
998        doMultiBlockArgs.push_back(bufPtr);
999    }
1000
1001    kb->CreateCall(multiBlockFunction, doMultiBlockArgs);
1002
1003    // Do copybacks if necessary.
1004    unsigned priorIdx = 0;
1005    for (unsigned i = 0; i < mStreamSetOutputs.size(); i++) {       
1006        if (auto cb = dyn_cast<SwizzledCopybackBuffer>(mStreamSetOutputBuffers[i]))  {
1007            Value * log2BlockSize = kb->getSize(std::log2(kb->getBitBlockWidth()));
1008            BasicBlock * copyBack = kb->CreateBasicBlock(mStreamSetOutputs[i].name + "_copyBack");
1009            BasicBlock * done = kb->CreateBasicBlock(mStreamSetOutputs[i].name + "_copyBackDone");
1010            Value * newlyProduced = kb->CreateSub(kb->getProducedItemCount(mStreamSetOutputs[i].name), producedItemCount[i]);
1011            Value * priorBlock = kb->CreateLShr(producedItemCount[i], log2BlockSize);
1012            Value * priorOffset = kb->CreateAnd(producedItemCount[i], kb->getSize(kb->getBitBlockWidth() - 1));
1013            Value * instance = kb->getStreamSetBufferPtr(mStreamSetOutputs[i].name);
1014            Value * accessibleBlocks = kb->getLinearlyAccessibleBlocks(mStreamSetOutputs[i].name, priorBlock);
1015            Value * accessible = kb->CreateSub(kb->CreateShl(accessibleBlocks, log2BlockSize), priorOffset);
1016            Value * wraparound = kb->CreateICmpULT(accessible, newlyProduced);
1017            kb->CreateCondBr(wraparound, copyBack, done);
1018            kb->SetInsertPoint(copyBack);
1019            Value * copyItems = kb->CreateSub(newlyProduced, accessible);
1020            cb->createCopyBack(kb.get(), instance, copyItems);
1021            kb->CreateBr(done);
1022            kb->SetInsertPoint(done);
1023            priorIdx++;
1024        }
1025        if (auto cb = dyn_cast<CircularCopybackBuffer>(mStreamSetOutputBuffers[i]))  {
1026            BasicBlock * copyBack = kb->CreateBasicBlock(mStreamSetOutputs[i].name + "_copyBack");
1027            BasicBlock * done = kb->CreateBasicBlock(mStreamSetOutputs[i].name + "_copyBackDone");
1028            Value * instance = kb->getStreamSetBufferPtr(mStreamSetOutputs[i].name);
1029            Value * newlyProduced = kb->CreateSub(kb->getProducedItemCount(mStreamSetOutputs[i].name), producedItemCount[i]);
1030            Value * accessible = kb->getLinearlyAccessibleItems(mStreamSetOutputs[i].name, producedItemCount[i]);
1031            Value * wraparound = kb->CreateICmpULT(accessible, newlyProduced);
1032            kb->CreateCondBr(wraparound, copyBack, done);
1033            kb->SetInsertPoint(copyBack);
1034            Value * copyItems = kb->CreateSub(newlyProduced, accessible);
1035            cb->createCopyBack(kb.get(), instance, copyItems);
1036            kb->CreateBr(done);
1037            kb->SetInsertPoint(done);
1038            priorIdx++;
1039        }
1040    }
1041
1042    Value * nowProcessed = kb->CreateAdd(processedItemCount[0], linearlyAvailItems);
1043    kb->setProcessedItemCount(mStreamSetInputs[0].name, nowProcessed);
1044    Value * reducedStridesToDo = kb->CreateSub(stridesRemaining, linearlyWritableStrides);
1045    stridesRemaining->addIncoming(reducedStridesToDo, kb->GetInsertBlock());
1046    kb->CreateBr(doSegmentOuterLoop);
1047
1048
1049    //
1050    // We use temporary buffers in 3 different cases that preclude full block processing.
1051    // (a) One or more input buffers does not have a sufficient number of input items linearly available.
1052    // (b) One or more output buffers does not have sufficient linearly available buffer space.
1053    // (c) We have processed all the full blocks of input and only the excessItems remain.
1054    // In each case we set up temporary buffers for input and output and then
1055    // call the Multiblock routine.
1056    //
1057
1058    kb->SetInsertPoint(tempBlockCheck);
1059    Value * const haveStrides = kb->CreateICmpUGT(stridesRemaining, kb->getSize(0));
1060    kb->CreateCondBr(kb->CreateOr(mIsFinal, haveStrides), doTempBufferBlock, segmentDone);
1061
1062    kb->SetInsertPoint(doTempBufferBlock);
1063    Value * tempBlockItems = kb->CreateSelect(haveStrides, strideSize, excessItems);
1064    Value * doFinal = kb->CreateNot(haveStrides);
1065
1066    // Begin constructing the doMultiBlock args.
1067    std::vector<Value *> tempArgs;
1068    tempArgs.push_back(getInstance());
1069    tempArgs.push_back(tempBlockItems);
1070    // For non-derived inputs, add the available items.
1071    for (unsigned i = 1; i < mStreamSetInputs.size(); i++) {
1072        if (!mIsDerived[i]) {
1073            Value * avail = kb->CreateSub(mAvailableItemCount[i], processedItemCount[i]);
1074            tempArgs.push_back(kb->CreateSelect(kb->CreateICmpULT(avail, strideSize), avail, strideSize));
1075        }
1076    }
1077    //
1078    // Define and allocate the temporary buffer area.
1079    //
1080    Type * tempBuffers[totalSetCount];
1081    for (unsigned i = 0; i < inputSetCount; ++i) {
1082        Type * bufType = mStreamSetInputBuffers[i]->getStreamSetBlockType();
1083        tempBuffers[i] = ArrayType::get(bufType, maxBlocksToCopy[i]);
1084    }
1085    for (unsigned i = 0; i < outputSetCount; i++) {
1086        Type * bufType = mStreamSetOutputBuffers[i]->getStreamSetBlockType();
1087        tempBuffers[i + inputSetCount] = ArrayType::get(bufType, maxBlocksToCopy[i + inputSetCount]);
1088    }
1089    Type * tempParameterStructType = StructType::create(kb->getContext(), ArrayRef<Type *>(tempBuffers, totalSetCount), "tempBuf");
1090    // Prepare the temporary buffer area.
1091    Value * tempParameterArea = kb->CreateCacheAlignedAlloca(tempParameterStructType);
1092    kb->CreateMemZero(tempParameterArea, ConstantExpr::getSizeOf(tempParameterStructType));
1093    // For each input and output buffer, copy over necessary data starting from the last block boundary.
1094    Value * itemCountNeeded[inputSetCount];
1095    itemCountNeeded[0] = tempBlockItems;
1096    Value * finalItemCountNeeded[inputSetCount];
1097
1098    for (unsigned i = 0; i < inputSetCount; i++) {
1099        Type * bufPtrType = mStreamSetInputBuffers[i]->getPointerType();
1100        if (mItemsPerStride[i] != 0) {
1101            Value * tempBufPtr = kb->CreateGEP(tempParameterArea, {kb->getInt32(0), kb->getInt32(i)});
1102            tempBufPtr = kb->CreatePointerCast(tempBufPtr, bufPtrType);
1103            ConstantInt * strideItems = kb->getSize(mItemsPerStride[i]);
1104            Value * strideBasePos = kb->CreateSub(processedItemCount[i], kb->CreateURem(processedItemCount[i], strideItems));
1105            Value * blockBasePos = strideBasePos;
1106            if (mItemsPerStride[i] & (bitBlockWidth - 1)) {
1107                blockBasePos = kb->CreateAnd(strideBasePos, blockBaseMask);
1108            }
1109
1110            // The number of items to copy is determined by the processing rate requirements.
1111            if (i >= 1) {
1112                auto & rate = mStreamSetInputs[i].rate;
1113                std::string refSet = mStreamSetInputs[i].rate.referenceStreamSet();
1114                Port port; unsigned ssIdx;
1115                std::tie(port, ssIdx) = getStreamPort(refSet);
1116                itemCountNeeded[i] = rate.CreateRatioCalculation(kb.get(), itemCountNeeded[ssIdx], doFinal);
1117            }
1118            finalItemCountNeeded[i] = kb->CreateAdd(itemCountNeeded[i], processedItemCount[i]);
1119
1120            Value * inputPtr = kb->CreatePointerCast(kb->getRawInputPointer(mStreamSetInputs[i].name, kb->getInt32(0), blockBasePos), bufPtrType);
1121           
1122            if (maxBlocksToCopy[i] == 1) {
1123                // copy one block
1124                mStreamSetInputBuffers[i]->createBlockCopy(kb.get(), tempBufPtr, inputPtr, kb->getSize(1));
1125            }
1126            else {
1127                Value * neededItems = kb->CreateSub(finalItemCountNeeded[i], blockBasePos);
1128                Value * availFromBase = kb->getLinearlyAccessibleItems(mStreamSetInputs[i].name, blockBasePos);
1129                Value * allAvail = kb->CreateICmpULE(neededItems, availFromBase);
1130                Value * copyItems1 = kb->CreateSelect(allAvail, neededItems, availFromBase);
1131                //mStreamSetInputBuffers[i]->createBlockAlignedCopy(kb.get(), tempBufPtr, inputPtr, copyItems1);
1132                Value * copyBlocks1 = kb->CreateUDivCeil(copyItems1, blockSize);
1133                mStreamSetInputBuffers[i]->createBlockCopy(kb.get(), tempBufPtr, inputPtr, copyBlocks1);
1134                BasicBlock * copyRemaining = kb->CreateBasicBlock("copyRemaining");
1135                BasicBlock * copyDone = kb->CreateBasicBlock("copyDone");
1136                kb->CreateCondBr(allAvail, copyDone, copyRemaining);
1137                kb->SetInsertPoint(copyRemaining);
1138                Value * copyItems2 = kb->CreateSub(neededItems, copyItems1);
1139                Value * copyBlocks2 = kb->CreateUDivCeil(copyItems2, blockSize);
1140                //Value * nextBasePos = kb->CreateAdd(blockBasePos, copyItems1);
1141                Value * nextBasePos = kb->CreateAdd(blockBasePos, kb->CreateMul(copyBlocks2, blockSize));
1142                Value * nextInputPtr = kb->CreatePointerCast(kb->getRawInputPointer(mStreamSetInputs[i].name, kb->getInt32(0), nextBasePos), bufPtrType);
1143                Value * nextBufPtr = kb->CreateGEP(tempBufPtr, kb->CreateUDiv(copyItems1, blockSize));
1144                //mStreamSetInputBuffers[i]->createBlockAlignedCopy(kb.get(), nextBufPtr, nextInputPtr, copyItems2);
1145                mStreamSetInputBuffers[i]->createBlockCopy(kb.get(), nextBufPtr, nextInputPtr, copyBlocks2);
1146                kb->CreateBr(copyDone);
1147                kb->SetInsertPoint(copyDone);
1148            }
1149            tempArgs.push_back(tempBufPtr);
1150        } else {
1151            Value * bufPtr = kb->getInputStreamBlockPtr(mStreamSetInputs[i].name, kb->getInt32(0));
1152            bufPtr = kb->CreatePointerCast(bufPtr, mStreamSetInputBuffers[i]->getPointerType());
1153            tempArgs.push_back(bufPtr);           
1154        }
1155    }
1156    Value * outputBasePos[outputSetCount];
1157    for (unsigned i = 0; i < outputSetCount; i++) {
1158        Value * tempBufPtr = kb->CreateGEP(tempParameterArea,  {kb->getInt32(0), kb->getInt32(inputSetCount + i)});
1159        Type * bufPtrType = mStreamSetOutputBuffers[i]->getPointerType();
1160        tempBufPtr = kb->CreatePointerCast(tempBufPtr, bufPtrType);
1161        producedItemCount[i] = kb->getProducedItemCount(mStreamSetOutputs[i].name);
1162        outputBasePos[i] = kb->CreateAnd(producedItemCount[i], blockBaseMask);
1163        //mStreamSetOutputBuffers[i]->createBlockAlignedCopy(kb.get(), tempBufPtr, outputBlockPtr[i], kb->CreateSub(producedItemCount[i], outputBasePos[i]));
1164        Value * copyBlocks = kb->CreateUDivCeil(kb->CreateSub(producedItemCount[i], outputBasePos[i]), blockSize);
1165        mStreamSetOutputBuffers[i]->createBlockCopy(kb.get(), tempBufPtr, outputBlockPtr[i], copyBlocks);
1166        tempArgs.push_back(tempBufPtr);
1167    }
1168
1169    kb->CreateCall(multiBlockFunction, tempArgs);
1170   
1171    //  The items have been processed and output generated to the temporary areas.
1172    //  Update the processed item count (and hence all the counts derived automatically
1173    //  therefrom).
1174    kb->setProcessedItemCount(mStreamSetInputs[0].name, finalItemCountNeeded[0]);
1175   
1176    // Copy back data to the actual output buffers.
1177    for (unsigned i = 0; i < mStreamSetOutputBuffers.size(); i++) {
1178        Value * tempBufPtr = kb->CreateGEP(tempParameterArea,  {kb->getInt32(0), kb->getInt32(mStreamSetInputs.size() + i)});
1179        tempBufPtr = kb->CreatePointerCast(tempBufPtr, mStreamSetOutputBuffers[i]->getPointerType());
1180        Value * finalOutputItems = kb->getProducedItemCount(mStreamSetOutputs[i].name);
1181        Value * copyItems = kb->CreateSub(finalOutputItems, outputBasePos[i]);
1182        // Round up to exact multiple of block size.
1183        //copyItems = kb->CreateAnd(kb->CreateAdd(copyItems, kb->getSize(bitBlockWidth - 1)), blockBaseMask);
1184        Value * writableFromBase = kb->getLinearlyWritableItems(mStreamSetOutputs[i].name, outputBasePos[i]); // must be a whole number of blocks.
1185        Value * allWritable = kb->CreateICmpULE(copyItems, writableFromBase);
1186        Value * copyItems1 = kb->CreateSelect(allWritable, copyItems, writableFromBase);
1187        //mStreamSetOutputBuffers[i]->createBlockAlignedCopy(kb.get(), outputBlockPtr[i], tempBufPtr, copyItems1);
1188        Value * copyBlocks1 = kb->CreateUDivCeil(copyItems1, blockSize);
1189        mStreamSetOutputBuffers[i]->createBlockCopy(kb.get(), outputBlockPtr[i], tempBufPtr, copyBlocks1);
1190        BasicBlock * copyBackRemaining = kb->CreateBasicBlock("copyBackRemaining");
1191        BasicBlock * copyBackDone = kb->CreateBasicBlock("copyBackDone");
1192        kb->CreateCondBr(allWritable, copyBackDone, copyBackRemaining);
1193        kb->SetInsertPoint(copyBackRemaining);
1194        Value * copyItems2 = kb->CreateSub(copyItems, copyItems1);
1195        Value * nextBasePos = kb->CreateAdd(outputBasePos[i], copyItems1);
1196        Type * bufPtrType = mStreamSetOutputBuffers[i]->getPointerType();
1197        Value * nextOutputPtr = kb->CreatePointerCast(kb->getRawOutputPointer(mStreamSetOutputs[i].name, kb->getInt32(0), nextBasePos), bufPtrType);
1198        tempBufPtr = kb->CreateGEP(tempBufPtr, kb->CreateUDiv(copyItems1, blockSize));
1199        //mStreamSetOutputBuffers[i]->createBlockAlignedCopy(kb.get(), nextOutputPtr, tempBufPtr, copyItems2);
1200        Value * copyBlocks2 = kb->CreateUDivCeil(copyItems2, blockSize);
1201        mStreamSetOutputBuffers[i]->createBlockCopy(kb.get(), nextOutputPtr, tempBufPtr, copyBlocks2);
1202        kb->CreateBr(copyBackDone);
1203        kb->SetInsertPoint(copyBackDone);
1204    }
1205
1206    //  We've dealt with the partial block processing and copied information back into the
1207    //  actual buffers.  If this isn't the final block, loop back for more multiblock processing.
1208    //
1209    stridesRemaining->addIncoming(kb->CreateSub(stridesRemaining, kb->CreateZExt(haveStrides, kb->getSizeTy())), kb->GetInsertBlock());
1210    BasicBlock * setTermination = kb->CreateBasicBlock("mBsetTermination");
1211    kb->CreateCondBr(haveStrides, doSegmentOuterLoop, setTermination);
1212    kb->SetInsertPoint(setTermination);
1213    kb->setTerminationSignal();
1214    kb->CreateBr(segmentDone);
1215    kb->SetInsertPoint(segmentDone);
1216}
1217
1218void Kernel::finalizeInstance(const std::unique_ptr<KernelBuilder> & idb) {
1219    assert ("KernelBuilder does not have a valid IDISA Builder" && idb);
1220    mOutputScalarResult = idb->CreateCall(getTerminateFunction(idb->getModule()), { getInstance() });
1221}
1222
1223Kernel::StreamPort Kernel::getStreamPort(const std::string & name) const {
1224    const auto f = mStreamMap.find(name);
1225    if (LLVM_UNLIKELY(f == mStreamMap.end())) {
1226        report_fatal_error(getName() + " does not contain stream set " + name);
1227    }
1228    return f->second;
1229}
1230
1231static inline std::string annotateKernelNameWithDebugFlags(std::string && name) {
1232    if (codegen::EnableAsserts) {
1233        name += "_EA";
1234    }
1235    name += "_O" + std::to_string((int)codegen::OptLevel);
1236    return name;
1237}
1238
1239// CONSTRUCTOR
1240Kernel::Kernel(std::string && kernelName,
1241               std::vector<Binding> && stream_inputs,
1242               std::vector<Binding> && stream_outputs,
1243               std::vector<Binding> && scalar_parameters,
1244               std::vector<Binding> && scalar_outputs,
1245               std::vector<Binding> && internal_scalars)
1246: KernelInterface(annotateKernelNameWithDebugFlags(std::move(kernelName))
1247                  , std::move(stream_inputs), std::move(stream_outputs)
1248                  , std::move(scalar_parameters), std::move(scalar_outputs)
1249                  , std::move(internal_scalars))
1250, mCurrentMethod(nullptr)
1251, mNoTerminateAttribute(false)
1252, mIsGenerated(false)
1253, mIsFinal(nullptr)
1254, mOutputScalarResult(nullptr)
1255, mStride(0) {
1256
1257}
1258
1259Kernel::~Kernel() {
1260
1261}
1262
1263// CONSTRUCTOR
1264BlockOrientedKernel::BlockOrientedKernel(std::string && kernelName,
1265                                         std::vector<Binding> && stream_inputs,
1266                                         std::vector<Binding> && stream_outputs,
1267                                         std::vector<Binding> && scalar_parameters,
1268                                         std::vector<Binding> && scalar_outputs,
1269                                         std::vector<Binding> && internal_scalars)
1270: Kernel(std::move(kernelName), std::move(stream_inputs), std::move(stream_outputs), std::move(scalar_parameters), std::move(scalar_outputs), std::move(internal_scalars))
1271, mDoBlockMethod(nullptr)
1272, mStrideLoopBody(nullptr)
1273, mStrideLoopBranch(nullptr)
1274, mStrideLoopTarget(nullptr) {
1275
1276}
1277
1278// CONSTRUCTOR
1279MultiBlockKernel::MultiBlockKernel(std::string && kernelName,
1280                                   std::vector<Binding> && stream_inputs,
1281                                   std::vector<Binding> && stream_outputs,
1282                                   std::vector<Binding> && scalar_parameters,
1283                                   std::vector<Binding> && scalar_outputs,
1284                                   std::vector<Binding> && internal_scalars)
1285: Kernel(std::move(kernelName), std::move(stream_inputs), std::move(stream_outputs), std::move(scalar_parameters), std::move(scalar_outputs), std::move(internal_scalars)) {
1286}
1287
1288// CONSTRUCTOR
1289SegmentOrientedKernel::SegmentOrientedKernel(std::string && kernelName,
1290                                             std::vector<Binding> && stream_inputs,
1291                                             std::vector<Binding> && stream_outputs,
1292                                             std::vector<Binding> && scalar_parameters,
1293                                             std::vector<Binding> && scalar_outputs,
1294                                             std::vector<Binding> && internal_scalars)
1295: Kernel(std::move(kernelName), std::move(stream_inputs), std::move(stream_outputs), std::move(scalar_parameters), std::move(scalar_outputs), std::move(internal_scalars)) {
1296   
1297}
1298 
1299   
1300void applyOutputBufferExpansions(const std::unique_ptr<KernelBuilder> & kb,
1301                                 std::vector<Value *> inputAvailable,
1302                                 Value * doFinal) {
1303    auto kernel = kb->getKernel();
1304    const unsigned inputSetCount = inputAvailable.size();
1305    if (inputSetCount == 0) return;  //  Cannot calculate buffer items expected from input.
1306    auto & outputs = kernel->getStreamSetOutputBuffers();
1307    const unsigned outputSetCount = outputs.size();
1308
1309    Constant * blockSize = kb->getSize(kb->getBitBlockWidth());
1310    Value * newlyAvailInputItems[inputSetCount];
1311    Value * requiredOutputBufferSpace[outputSetCount];
1312    for (unsigned i = 0; i < inputSetCount; i++) {
1313        Value * processed = kb->getProcessedItemCount(kernel->getStreamInput(i).name);
1314        newlyAvailInputItems[i] = kb->CreateSub(inputAvailable[i], processed);
1315    }
1316    //kb->GetInsertBlock()->dump();
1317    for (unsigned i = 0; i < outputSetCount; i++) {
1318        requiredOutputBufferSpace[i] = nullptr;
1319        auto & rate = kernel->getStreamOutput(i).rate;
1320        if (rate.isUnknownRate()) continue;  // No calculations possible.
1321        Kernel::Port port; unsigned ssIdx;
1322        std::tie(port, ssIdx) = kernel->getStreamPort(rate.referenceStreamSet());
1323        if (port == Kernel::Port::Output) {
1324            requiredOutputBufferSpace[i] = rate.CreateRatioCalculation(kb.get(), requiredOutputBufferSpace[ssIdx], doFinal);
1325        }
1326        else {
1327            requiredOutputBufferSpace[i] = rate.CreateRatioCalculation(kb.get(), newlyAvailInputItems[ssIdx], doFinal);
1328        }
1329        if (auto db = dyn_cast<DynamicBuffer>(outputs[i])) {
1330            Value * handle = db->getStreamSetHandle();
1331            // This buffer can be expanded.
1332            Value * producedBlock = kb->CreateUDivCeil(kb->getProducedItemCount(kernel->getStreamOutput(i).name), blockSize);
1333            Value * consumedBlock = kb->CreateUDiv(kb->getConsumedItemCount(kernel->getStreamOutput(i).name), blockSize);
1334            Value * blocksInUse = kb->CreateSub(producedBlock, consumedBlock);
1335            Value * blocksRequired = kb->CreateAdd(blocksInUse, kb->CreateUDivCeil(requiredOutputBufferSpace[i], blockSize));
1336            Value * spaceRequired = kb->CreateMul(blocksRequired, blockSize);
1337            Value * expansionNeeded = kb->CreateICmpUGT(spaceRequired, db->getBufferedSize(kb.get(), handle));
1338            BasicBlock * doExpand = kb->CreateBasicBlock("doExpand");
1339            BasicBlock * bufferReady = kb->CreateBasicBlock("bufferReady");
1340            kb->CreateCondBr(expansionNeeded, doExpand, bufferReady);
1341            kb->SetInsertPoint(doExpand);
1342            db->doubleCapacity(kb.get(), handle);
1343            // Ensure that capacity is sufficient by successive doubling, if necessary.
1344            expansionNeeded = kb->CreateICmpUGT(spaceRequired, db->getBufferedSize(kb.get(), handle));
1345            kb->CreateCondBr(expansionNeeded, doExpand, bufferReady);
1346            kb->SetInsertPoint(bufferReady);
1347        }
1348    }
1349
1350}
1351
1352}
Note: See TracBrowser for help on using the repository browser.