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

Last change on this file since 5486 was 5486, checked in by nmedfort, 2 years ago

Initial attempt to improve debugging capabilities with compilation stack traces on error.

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