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

Last change on this file since 5599 was 5599, checked in by cameron, 22 months ago

Bug fixes for multiblock kernel/radix64

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