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

Last change on this file since 5551 was 5551, checked in by cameron, 23 months ago

Bug fix

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