source: icGREP/icgrep-devel/icgrep/kernels/kernel_builder.cpp @ 5941

Last change on this file since 5941 was 5865, checked in by nmedfort, 16 months ago

More work on the pipeline I/O rate handling

File size: 28.4 KB
Line 
1#include "kernel_builder.h"
2#include <toolchain/toolchain.h>
3#include <kernels/kernel.h>
4#include <kernels/streamset.h>
5#include <llvm/Support/raw_ostream.h>
6#include <llvm/IR/Module.h>
7
8using namespace llvm;
9using namespace parabix;
10
11inline static bool is_power_2(const uint64_t n) {
12    return ((n & (n - 1)) == 0) && n;
13}
14
15namespace kernel {
16
17using Port = Kernel::Port;
18
19Value * KernelBuilder::getScalarFieldPtr(llvm::Value * const instance, Value * const index) {
20    if (LLVM_UNLIKELY(codegen::DebugOptionIsSet(codegen::EnableAsserts))) {
21        CreateAssert(instance, "getScalarFieldPtr: instance cannot be null!");
22    }
23    return CreateGEP(instance, {getInt32(0), index});
24}
25
26Value * KernelBuilder::getScalarFieldPtr(llvm::Value * const handle, const std::string & fieldName) {
27    return getScalarFieldPtr(handle, getInt32(mKernel->getScalarIndex(fieldName)));
28}
29
30llvm::Value * KernelBuilder::getScalarFieldPtr(llvm::Value * const index) {
31    return getScalarFieldPtr(mKernel->getInstance(), index);
32}
33
34llvm::Value *KernelBuilder:: getScalarFieldPtr(const std::string & fieldName) {
35    return getScalarFieldPtr(mKernel->getInstance(), fieldName);
36}
37
38Value * KernelBuilder::getScalarField(const std::string & fieldName) {
39    return CreateLoad(getScalarFieldPtr(fieldName), fieldName);
40}
41
42void KernelBuilder::setScalarField(const std::string & fieldName, Value * value) {
43    CreateStore(value, getScalarFieldPtr(fieldName));
44}
45
46Value * KernelBuilder::getStreamHandle(const std::string & name) {
47    Value * const ptr = getScalarField(name + Kernel::BUFFER_PTR_SUFFIX);
48    if (LLVM_UNLIKELY(codegen::DebugOptionIsSet(codegen::EnableAsserts))) {
49        CreateAssert(ptr, name + " handle cannot be null!");
50    }
51    return ptr;
52}
53
54LoadInst * KernelBuilder::acquireLogicalSegmentNo() {
55    return CreateAtomicLoadAcquire(getScalarFieldPtr(Kernel::LOGICAL_SEGMENT_NO_SCALAR));
56}
57
58void KernelBuilder::releaseLogicalSegmentNo(Value * nextSegNo) {
59    CreateAtomicStoreRelease(nextSegNo, getScalarFieldPtr(Kernel::LOGICAL_SEGMENT_NO_SCALAR));
60}
61
62Value * KernelBuilder::getCycleCountPtr() {
63    return getScalarFieldPtr(Kernel::CYCLECOUNT_SCALAR);
64}
65
66Value * KernelBuilder::getInternalItemCount(const std::string & name, const std::string & suffix) {
67    const ProcessingRate & rate = mKernel->getBinding(name).getRate();
68    Value * itemCount = nullptr;
69    if (LLVM_UNLIKELY(rate.isRelative())) {
70        Port port; unsigned index;
71        std::tie(port, index) = mKernel->getStreamPort(rate.getReference());
72        if (port == Port::Input) {
73            itemCount = getProcessedItemCount(rate.getReference());
74        } else {
75            itemCount = getProducedItemCount(rate.getReference());
76        }
77        const auto & r = rate.getRate();
78        if (r.numerator() != 1) {
79            itemCount = CreateMul(itemCount, ConstantInt::get(itemCount->getType(), r.numerator()));
80        }
81        if (r.denominator() != 1) {
82            itemCount = CreateExactUDiv(itemCount, ConstantInt::get(itemCount->getType(), r.denominator()));
83        }
84    } else if (LLVM_UNLIKELY(rate.isPopCount())) {
85        Port port; unsigned index;
86        std::tie(port, index) = mKernel->getStreamPort(rate.getReference());
87
88
89
90
91    } else {
92        itemCount = getScalarField(name + suffix);
93    }
94    return itemCount;
95}
96
97void KernelBuilder::setInternalItemCount(const std::string & name, const std::string & suffix, llvm::Value * const value) {
98    const ProcessingRate & rate = mKernel->getBinding(name).getRate();
99    if (LLVM_UNLIKELY(rate.isDerived())) {
100        assert (false);
101        report_fatal_error("Cannot set item count: " + name + " is a Derived rate");
102    }
103    if (codegen::DebugOptionIsSet(codegen::TraceCounts)) {
104        CallPrintIntToStderr(mKernel->getName() + ": " + name + suffix, value);
105    }
106    setScalarField(name + suffix, value);
107}
108
109
110Value * KernelBuilder::getAvailableItemCount(const std::string & name) {
111    const auto & inputs = mKernel->getStreamInputs();
112    for (unsigned i = 0; i < inputs.size(); ++i) {
113        if (inputs[i].getName() == name) {
114            return mKernel->getAvailableItemCount(i);
115        }
116    }
117    return nullptr;
118}
119
120Value * KernelBuilder::getTerminationSignal() {
121    return CreateICmpNE(getScalarField(Kernel::TERMINATION_SIGNAL), getSize(0));
122}
123
124void KernelBuilder::setTerminationSignal(llvm::Value * const value) {
125    assert (value->getType() == getInt1Ty());
126    if (codegen::DebugOptionIsSet(codegen::TraceCounts)) {
127        CallPrintIntToStderr(mKernel->getName() + ": setTerminationSignal", value);
128    }
129    setScalarField(Kernel::TERMINATION_SIGNAL, CreateZExt(value, getSizeTy()));
130}
131
132Value * KernelBuilder::getLinearlyAccessibleItems(const std::string & name, Value * fromPosition, Value * avail, bool reverse) {
133    const StreamSetBuffer * const buf = mKernel->getInputStreamSetBuffer(name);
134    return buf->getLinearlyAccessibleItems(this, getStreamHandle(name), fromPosition, avail, reverse);
135}
136
137Value * KernelBuilder::getLinearlyWritableItems(const std::string & name, Value * fromPosition, bool reverse) {
138    const StreamSetBuffer * const buf = mKernel->getOutputStreamSetBuffer(name);
139    return buf->getLinearlyWritableItems(this, getStreamHandle(name), fromPosition, getConsumedItemCount(name), reverse);
140}
141
142/** ------------------------------------------------------------------------------------------------------------- *
143 * @brief isConstantZero
144 ** ------------------------------------------------------------------------------------------------------------- */
145inline bool isConstantZero(Value * const v) {
146    return isa<ConstantInt>(v) && cast<ConstantInt>(v)->isNullValue();
147}
148
149/** ------------------------------------------------------------------------------------------------------------- *
150 * @brief isConstantOne
151 ** ------------------------------------------------------------------------------------------------------------- */
152inline bool isConstantOne(Value * const v) {
153    return isa<ConstantInt>(v) && cast<ConstantInt>(v)->isOne();
154}
155
156/** ------------------------------------------------------------------------------------------------------------- *
157 * @brief getItemWidth
158 ** ------------------------------------------------------------------------------------------------------------- */
159inline unsigned getItemWidth(const Type * ty) {
160    if (LLVM_LIKELY(isa<ArrayType>(ty))) {
161        ty = ty->getArrayElementType();
162    }
163    return cast<IntegerType>(ty->getVectorElementType())->getBitWidth();
164}
165
166inline static unsigned ceil_log2(const unsigned v) {
167    assert ("log2(0) is undefined!" && v != 0);
168    return (sizeof(unsigned) * CHAR_BIT) - __builtin_clz(v - 1U);
169}
170
171/** ------------------------------------------------------------------------------------------------------------- *
172 * @brief CreateStreamCpy
173 ** ------------------------------------------------------------------------------------------------------------- */
174void KernelBuilder::CreateStreamCpy(const std::string & name, Value * target, Value * targetOffset, Value * source, Value * sourceOffset, Value * itemsToCopy, const unsigned itemAlignment) {
175
176    assert (target && targetOffset);
177    assert (source && sourceOffset);
178    assert (target->getType() == source->getType());
179    assert (target->getType()->isPointerTy());
180    assert (isConstantZero(targetOffset) || isConstantZero(sourceOffset));
181    const StreamSetBuffer * const buffer = mKernel->getAnyStreamSetBuffer(name);
182    const auto itemWidth = getItemWidth(buffer->getBaseType());
183    assert ("invalid item width" && is_power_2(itemWidth));
184    const auto blockWidth = getBitBlockWidth();
185    // Although our item width may be n bits, if we know we're always processing m items per block, our field width
186    // (w.r.t the stream copy) would be n*m. By taking this into account we can optimize and simplify the copy code.
187    const auto fieldWidth = std::min(1U << ceil_log2(itemWidth * itemAlignment), blockWidth);
188    assert ((blockWidth % fieldWidth) == 0);
189
190    if (LLVM_LIKELY(itemWidth < fieldWidth)) {
191        const auto factor = fieldWidth / itemWidth;
192        Constant * const FACTOR = getSize(factor);
193        if (LLVM_UNLIKELY(codegen::DebugOptionIsSet(codegen::EnableAsserts))) {
194            const auto kernelName = mKernel->getName()+ ": " + name;
195            if (fieldWidth > 8) {
196                const auto alignment = (fieldWidth + 7) / 8;
197                ConstantInt * const ALIGNMENT = getSize(alignment);
198                CreateAssertZero(CreateURem(CreatePtrToInt(target, getSizeTy()), ALIGNMENT), kernelName + " target is misaligned (" + std::to_string(alignment) + ")");
199                CreateAssertZero(CreateURem(CreatePtrToInt(source, getSizeTy()), ALIGNMENT), kernelName + " source is misaligned (" + std::to_string(alignment) + ")");
200            }
201            CreateAssertZero(CreateURem(targetOffset, FACTOR), kernelName + " target offset is misaligned (" + std::to_string(factor) + ")");
202            CreateAssertZero(CreateURem(sourceOffset, FACTOR), kernelName + " source offset is misaligned (" + std::to_string(factor) + ")");
203        }
204        targetOffset = CreateUDiv(targetOffset, FACTOR);
205        sourceOffset = CreateUDiv(sourceOffset, FACTOR);
206    }
207
208    /*
209       Streams are conceptually modelled as:
210
211                                            BLOCKS
212
213                                      A     B     C     D
214           STREAM SET ELEMENT   1  |aaaaa|bbbbb|ccccc|dddd |
215                                2  |eeeee|fffff|ggggg|hhhh |
216                                3  |iiiii|jjjjj|kkkkk|llll |
217
218       But the memory layout is actually:
219
220           A_1   A_2   A_3   B_1   B_2   B_3   C_1   C_2   C_3   D_1   D_2   D_3
221
222         |aaaaa|eeeee|iiiii|bbbbb|fffff|jjjjj|ccccc|ggggg|kkkkk|dddd |hhhh |llll |
223
224
225       So if we're copying the entire stream set block or our stream set has one element, we can use memcpy.
226
227       One compilication here is when the BlockSize of a stream is not equal to the BitBlockWidth.
228
229
230    */
231
232    Value * const n = buffer->getStreamSetCount(this, getStreamHandle(name));
233    if (((isConstantOne(n) && fieldWidth >= 8) || fieldWidth == blockWidth || (isConstantZero(targetOffset) && isConstantZero(sourceOffset)))) {
234        if (LLVM_LIKELY(itemWidth < 8)) {
235            itemsToCopy = CreateUDivCeil(itemsToCopy, getSize(8 / itemWidth));
236        } else if (LLVM_UNLIKELY(itemWidth > 8)) {
237            itemsToCopy = CreateMul(itemsToCopy, getSize(itemWidth / 8));
238        }
239        if (!isConstantOne(n)) {
240            itemsToCopy = CreateMul(itemsToCopy, n);
241        }
242        PointerType * const ptrTy = getIntNTy(fieldWidth)->getPointerTo();
243        target = CreateGEP(CreatePointerCast(target, ptrTy), targetOffset);
244        source = CreateGEP(CreatePointerCast(source, ptrTy), sourceOffset);
245        const auto alignment = (fieldWidth + 7) / 8;
246        CreateMemCpy(target, source, itemsToCopy, alignment);
247
248    } else { // either the target offset or source offset is non-zero but not both
249        VectorType * const blockTy = getBitBlockType();
250        PointerType * const blockPtrTy = blockTy->getPointerTo();
251        Constant * const BLOCK_WIDTH = getSize(blockWidth);
252        target = CreatePointerCast(target, blockPtrTy);
253        target = CreateGEP(target, CreateUDiv(targetOffset, BLOCK_WIDTH));
254        source = CreatePointerCast(source, blockPtrTy);
255        source = CreateGEP(source, CreateUDiv(sourceOffset, BLOCK_WIDTH));
256        const auto alignment = blockWidth / 8;
257        Constant * const ZERO = getSize(0);
258        Constant * const ONE = getSize(1);
259
260        BasicBlock * const entry = GetInsertBlock();
261
262        // TODO: this code isn't correct. I was hoping to shift by fieldwidth units to give LLVM
263        // the ability to better select
264
265        if (isConstantZero(targetOffset)) {
266
267            /*
268                                                BLOCKS
269
270                                          A     B     C     D
271               SOURCE STREAM        1  |aaa--|bbbBB|cccCC|  dDD|
272                                    2  |eee--|fffFF|gggGG|  hHH|
273                                    3  |iii--|jjjJJ|kkkKK|  lLL|
274
275
276                                          A     B     C     D
277               TARGET STREAM        1  |BBaaa|CCbbb|DDccc|    d|
278                                    2  |FFeee|GGfff|HHggg|    h|
279                                    3  |JJiii|KKjjj|LLkkk|    l|
280            */
281
282            sourceOffset = CreateURem(sourceOffset, BLOCK_WIDTH);
283
284            Value * const borrowOffset = CreateSub(BLOCK_WIDTH, sourceOffset);
285            BasicBlock * const streamCopy = CreateBasicBlock();
286            BasicBlock * const streamCopyRemainingCond = CreateBasicBlock();
287            BasicBlock * const streamCopyRemaining = CreateBasicBlock();
288            BasicBlock * const streamCopyEnd = CreateBasicBlock();
289
290            Value * const blocksToCopy = CreateMul(CreateUDiv(itemsToCopy, BLOCK_WIDTH), n);
291            CreateCondBr(CreateICmpNE(blocksToCopy, ZERO), streamCopy, streamCopyRemainingCond);
292
293            SetInsertPoint(streamCopy);
294            PHINode * const i = CreatePHI(getSizeTy(), 2);
295            i->addIncoming(n, entry);
296            Value * Ai = CreateAlignedLoad(CreateGEP(source, CreateSub(i, n)), alignment);
297            Ai = mvmd_srl(fieldWidth, Ai, borrowOffset);
298            Value * Bi = CreateAlignedLoad(CreateGEP(source, i), alignment);
299            Bi = mvmd_sll(fieldWidth, Bi, sourceOffset);
300            CreateAlignedStore(CreateOr(Bi, Ai), CreateGEP(target, i), alignment);
301            Value * const next_i = CreateAdd(i, ONE);
302            i->addIncoming(next_i, streamCopy);
303            CreateCondBr(CreateICmpNE(next_i, blocksToCopy), streamCopy, streamCopyRemainingCond);
304
305            SetInsertPoint(streamCopyRemainingCond);
306            Value * const partialBlocksToCopy = CreateAdd(blocksToCopy, n);
307            Value * const remainingItemsToCopy = CreateURem(itemsToCopy, BLOCK_WIDTH);
308            CreateLikelyCondBr(CreateIsNotNull(remainingItemsToCopy), streamCopyRemaining, streamCopyEnd);
309
310            SetInsertPoint(streamCopyRemaining);
311            PHINode * const j = CreatePHI(getSizeTy(), 2);
312            j->addIncoming(blocksToCopy, streamCopyRemainingCond);
313            Value * Aj = CreateAlignedLoad(CreateGEP(source, j), alignment);
314            Aj = mvmd_srl(fieldWidth, Aj, borrowOffset);
315            CreateAlignedStore(Aj, CreateGEP(target, j), alignment);
316            Value * const next_j = CreateAdd(j, ONE);
317            j->addIncoming(next_j, streamCopyRemaining);
318            CreateCondBr(CreateICmpNE(next_j, partialBlocksToCopy), streamCopyRemaining, streamCopyEnd);
319
320            SetInsertPoint(streamCopyEnd);
321
322        } else if (isConstantZero(sourceOffset)) {
323
324            /*
325                                                BLOCKS
326
327                                          A     B     C     D
328               SOURCE STREAM        1  |AAAaa|BBBaa|CCCcc|    d|
329                                    2  |EEEee|FFFff|GGGgg|    h|
330                                    3  |IIIii|JJJjj|KKKkk|    l|
331
332
333                                          A     B     C     D
334               TARGET STREAM        1  |--XXX|-----|-----|-----|
335                                    2  |--YYY|-----|-----|-----|
336                                    3  |--ZZZ|-----|-----|-----|
337
338                                          A     B     C     D
339               OUTPUT STREAM        1  |aaXXX|bbAAA|ccBBB| dCCC|
340                                    2  |eeYYY|ffEEE|ggFFF| hGGG|
341                                    3  |iiZZZ|jjIII|kkJJJ| lKKK|
342
343            */
344
345            BasicBlock * const streamCopy = CreateBasicBlock();
346            BasicBlock * const streamCopyRemainingCond = CreateBasicBlock();
347            BasicBlock * const streamCopyRemaining = CreateBasicBlock();
348            BasicBlock * const streamCopyEnd = CreateBasicBlock();
349
350            targetOffset = CreateURem(targetOffset, BLOCK_WIDTH);
351
352            Value * const carryOffset = CreateSub(BLOCK_WIDTH, targetOffset);
353            Value * const mask = mvmd_srl(fieldWidth, Constant::getAllOnesValue(blockTy), carryOffset);
354            CreateBr(streamCopy);
355
356            SetInsertPoint(streamCopy);
357            PHINode * const i = CreatePHI(getSizeTy(), 2);
358            i->addIncoming(ZERO, entry);
359            Value * A0 = CreateAlignedLoad(CreateGEP(target, i), alignment);
360            A0 = CreateAnd(A0, mask);
361            Value * Ai = CreateAlignedLoad(CreateGEP(source, i), alignment);
362            Ai = mvmd_sll(fieldWidth, Ai, targetOffset);
363            CreateAlignedStore(CreateOr(Ai, A0), CreateGEP(target, i), alignment);
364            Value * const next_i = CreateAdd(i, ONE);
365            i->addIncoming(next_i, streamCopy);
366            CreateCondBr(CreateICmpNE(next_i, n), streamCopy, streamCopyRemainingCond);
367
368            SetInsertPoint(streamCopyRemainingCond);
369            Value * const blocksToCopy = CreateMul(CreateUDiv(itemsToCopy, BLOCK_WIDTH), n);
370            CreateCondBr(CreateICmpUGT(blocksToCopy, n), streamCopyRemaining, streamCopyEnd);
371
372            SetInsertPoint(streamCopyRemaining);
373            PHINode * const j = CreatePHI(getSizeTy(), 2);
374            j->addIncoming(n, streamCopyRemainingCond);
375            Value * Aj = CreateAlignedLoad(CreateGEP(source, CreateSub(j, n)), alignment);
376            Aj = mvmd_srl(fieldWidth, Aj, carryOffset);
377            Value * Bj = CreateAlignedLoad(CreateGEP(source, j), alignment);
378            Bj = mvmd_sll(fieldWidth, Bj, targetOffset);
379            CreateAlignedStore(CreateOr(Bj, Aj), CreateGEP(target, j), alignment);
380            Value * const next_j = CreateAdd(j, ONE);
381            j->addIncoming(next_j, streamCopyRemaining);
382            CreateCondBr(CreateICmpNE(next_j, blocksToCopy), streamCopyRemaining, streamCopyEnd);
383
384            SetInsertPoint(streamCopyEnd);
385        }
386    }
387}
388
389Value * KernelBuilder::getConsumerLock(const std::string & name) {
390    return getScalarField(name + Kernel::CONSUMER_SUFFIX);
391}
392
393void KernelBuilder::setConsumerLock(const std::string & name, Value * value) {
394    setScalarField(name + Kernel::CONSUMER_SUFFIX, value);
395}
396
397Value * KernelBuilder::loadInputStreamBlock(const std::string & name, Value * streamIndex) {
398    return CreateBlockAlignedLoad(getInputStreamBlockPtr(name, streamIndex));
399}
400
401Value * KernelBuilder::getInputStreamPackPtr(const std::string & name, Value * streamIndex, Value * packIndex) {
402    Value * const addr = mKernel->getStreamSetInputAddress(name);
403    if (addr) {
404        return CreateGEP(addr, {getInt32(0), streamIndex, packIndex});
405    } else {
406        const StreamSetBuffer * const buf = mKernel->getInputStreamSetBuffer(name);
407        Value * const blockIndex = CreateLShr(getProcessedItemCount(name), std::log2(getBitBlockWidth()));
408        return buf->getStreamPackPtr(this, getStreamHandle(name), getBaseAddress(name), streamIndex, blockIndex, packIndex, true);
409    }
410}
411
412Value * KernelBuilder::loadInputStreamPack(const std::string & name, Value * streamIndex, Value * packIndex) {
413    return CreateBlockAlignedLoad(getInputStreamPackPtr(name, streamIndex, packIndex));
414}
415
416Value * KernelBuilder::getInputStreamSetCount(const std::string & name) {
417    const StreamSetBuffer * const buf = mKernel->getInputStreamSetBuffer(name);
418    return buf->getStreamSetCount(this, getStreamHandle(name));
419}
420
421Value * KernelBuilder::getInputStreamBlockPtr(const std::string & name, Value * const streamIndex, Value * const blockOffset) {
422    Value * const addr = mKernel->getStreamSetInputAddress(name);
423    if (addr) {
424        return CreateGEP(addr, {blockOffset, streamIndex});
425    } else {
426        const StreamSetBuffer * const buf = mKernel->getInputStreamSetBuffer(name);
427        Value * blockIndex = CreateLShr(getProcessedItemCount(name), std::log2(getBitBlockWidth()));
428        blockIndex = CreateAdd(blockIndex, blockOffset);
429        return buf->getStreamBlockPtr(this, getStreamHandle(name), getBaseAddress(name), streamIndex, blockIndex, true);
430    }
431}
432
433Value * KernelBuilder::getOutputStreamBlockPtr(const std::string & name, Value * streamIndex, Value * const blockOffset) {
434    Value * const addr = mKernel->getStreamSetOutputAddress(name);
435    if (addr) {
436        return CreateGEP(addr, {blockOffset, streamIndex});
437    } else {
438        const StreamSetBuffer * const buf = mKernel->getOutputStreamSetBuffer(name);
439        Value * const blockIndex = CreateLShr(getProducedItemCount(name), std::log2(getBitBlockWidth()));
440        return buf->getStreamBlockPtr(this, getStreamHandle(name), getBaseAddress(name), streamIndex, blockIndex, false);
441    }
442}
443
444StoreInst * KernelBuilder::storeOutputStreamBlock(const std::string & name, Value * streamIndex, Value * toStore) {
445    Value * const ptr = getOutputStreamBlockPtr(name, streamIndex);
446    Type * const storeTy = toStore->getType();
447    Type * const ptrElemTy = ptr->getType()->getPointerElementType();
448    if (LLVM_UNLIKELY(storeTy != ptrElemTy)) {
449        if (LLVM_LIKELY(storeTy->canLosslesslyBitCastTo(ptrElemTy))) {
450            toStore = CreateBitCast(toStore, ptrElemTy);
451        } else {
452            std::string tmp;
453            raw_string_ostream out(tmp);
454            out << "invalid type conversion when calling storeOutputStreamBlock on " <<  name << ": ";
455            ptrElemTy->print(out);
456            out << " vs. ";
457            storeTy->print(out);
458        }
459    }
460    return CreateBlockAlignedStore(toStore, ptr);
461}
462
463Value * KernelBuilder::getOutputStreamPackPtr(const std::string & name, Value * streamIndex, Value * packIndex) {
464    Value * const addr = mKernel->getStreamSetOutputAddress(name);
465    if (addr) {
466        return CreateGEP(addr, {getInt32(0), streamIndex, packIndex});
467    } else {
468        const StreamSetBuffer * const buf = mKernel->getOutputStreamSetBuffer(name);
469        Value * const blockIndex = CreateLShr(getProducedItemCount(name), std::log2(getBitBlockWidth()));
470        return buf->getStreamPackPtr(this, getStreamHandle(name), getBaseAddress(name), streamIndex, blockIndex, packIndex, false);
471    }
472}
473
474StoreInst * KernelBuilder::storeOutputStreamPack(const std::string & name, Value * streamIndex, Value * packIndex, Value * toStore) {
475    Value * const ptr = getOutputStreamPackPtr(name, streamIndex, packIndex);
476    Type * const storeTy = toStore->getType();
477    Type * const ptrElemTy = ptr->getType()->getPointerElementType();
478    if (LLVM_UNLIKELY(storeTy != ptrElemTy)) {
479        if (LLVM_LIKELY(storeTy->canLosslesslyBitCastTo(ptrElemTy))) {
480            toStore = CreateBitCast(toStore, ptrElemTy);
481        } else {
482            std::string tmp;
483            raw_string_ostream out(tmp);
484            out << "invalid type conversion when calling storeOutputStreamPack on " <<  name << ": ";
485            ptrElemTy->print(out);
486            out << " vs. ";
487            storeTy->print(out);
488        }
489    }
490    return CreateBlockAlignedStore(toStore, ptr);
491}
492
493Value * KernelBuilder::getOutputStreamSetCount(const std::string & name) {
494    const StreamSetBuffer * const buf = mKernel->getOutputStreamSetBuffer(name);
495    return buf->getStreamSetCount(this, getStreamHandle(name));
496}
497
498Value * KernelBuilder::getRawInputPointer(const std::string & name, Value * absolutePosition) {
499    const StreamSetBuffer * const buf = mKernel->getInputStreamSetBuffer(name);
500    return buf->getRawItemPointer(this, getStreamHandle(name), absolutePosition);
501}
502
503Value * KernelBuilder::getRawOutputPointer(const std::string & name, Value * absolutePosition) {
504    const StreamSetBuffer * const buf = mKernel->getOutputStreamSetBuffer(name);
505    return buf->getRawItemPointer(this, getStreamHandle(name), absolutePosition);
506}
507
508Value * KernelBuilder::getBaseAddress(const std::string & name) {
509    return mKernel->getAnyStreamSetBuffer(name)->getBaseAddress(this, getStreamHandle(name));
510}
511
512void KernelBuilder::setBaseAddress(const std::string & name, Value * const addr) {
513    return mKernel->getAnyStreamSetBuffer(name)->setBaseAddress(this, getStreamHandle(name), addr);
514}
515
516Value * KernelBuilder::getBufferedSize(const std::string & name) {
517    return mKernel->getAnyStreamSetBuffer(name)->getBufferedSize(this, getStreamHandle(name));
518}
519
520void KernelBuilder::setBufferedSize(const std::string & name, Value * size) {
521    mKernel->getAnyStreamSetBuffer(name)->setBufferedSize(this, getStreamHandle(name), size);
522}
523
524Value * KernelBuilder::getCapacity(const std::string & name) {
525    return mKernel->getAnyStreamSetBuffer(name)->getCapacity(this, getStreamHandle(name));
526}
527
528void KernelBuilder::setCapacity(const std::string & name, Value * c) {
529    mKernel->getAnyStreamSetBuffer(name)->setCapacity(this, getStreamHandle(name), c);
530}
531
532Value * KernelBuilder::getBlockAddress(const std::string & name, Value * blockIndex) {
533    const StreamSetBuffer * const buf = mKernel->getAnyStreamSetBuffer(name);
534    return buf->getBlockAddress(this, getStreamHandle(name), blockIndex);
535}
536
537void KernelBuilder::protectOutputStream(const std::string & name, const bool readOnly) {
538    const StreamSetBuffer * const buf = mKernel->getOutputStreamSetBuffer(name);
539    Value * const handle = getStreamHandle(name);
540    Value * const base = buf->getBaseAddress(this, handle);
541    Value * sz = ConstantExpr::getSizeOf(buf->getType());
542    sz = CreateMul(sz, getInt64(buf->getBufferBlocks()));
543    sz = CreateMul(sz, CreateZExt(buf->getStreamSetCount(this, handle), getInt64Ty()));
544    CreateMProtect(base, sz, readOnly ? CBuilder::READ : (CBuilder::READ | CBuilder::WRITE));
545}
546   
547CallInst * KernelBuilder::createDoSegmentCall(const std::vector<Value *> & args) {
548    return mKernel->makeDoSegmentCall(*this, args);
549}
550
551Value * KernelBuilder::getAccumulator(const std::string & accumName) {
552    auto results = mKernel->mOutputScalarResult;
553    if (LLVM_UNLIKELY(results == nullptr)) {
554        report_fatal_error("Cannot get accumulator " + accumName + " until " + mKernel->getName() + " has terminated.");
555    }
556    const auto & outputs = mKernel->getScalarOutputs();
557    const auto n = outputs.size();
558    if (LLVM_UNLIKELY(n == 0)) {
559        report_fatal_error(mKernel->getName() + " has no output scalars.");
560    } else {
561        for (unsigned i = 0; i < n; ++i) {
562            const Binding & b = outputs[i];
563            if (b.getName() == accumName) {
564                if (n == 1) {
565                    return results;
566                } else {
567                    return CreateExtractValue(results, {i});
568                }
569            }
570        }
571        report_fatal_error(mKernel->getName() + " has no output scalar named " + accumName);
572    }
573}
574
575void KernelBuilder::doubleCapacity(const std::string & name) {
576    const StreamSetBuffer * const buf = mKernel->getAnyStreamSetBuffer(name);
577    return buf->doubleCapacity(this, getStreamHandle(name));
578}
579
580BasicBlock * KernelBuilder::CreateConsumerWait() {
581    const auto consumers = mKernel->getStreamOutputs();
582    BasicBlock * const entry = GetInsertBlock();
583    if (consumers.empty()) {
584        return entry;
585    } else {
586        Function * const parent = entry->getParent();
587        IntegerType * const sizeTy = getSizeTy();
588        ConstantInt * const zero = getInt32(0);
589        ConstantInt * const one = getInt32(1);
590        ConstantInt * const size0 = getSize(0);
591
592        Value * const segNo = acquireLogicalSegmentNo();
593        const auto n = consumers.size();
594        BasicBlock * load[n + 1];
595        BasicBlock * wait[n];
596        for (unsigned i = 0; i < n; ++i) {
597            load[i] = BasicBlock::Create(getContext(), consumers[i].getName() + "Load", parent);
598            wait[i] = BasicBlock::Create(getContext(), consumers[i].getName() + "Wait", parent);
599        }
600        load[n] = BasicBlock::Create(getContext(), "Resume", parent);
601        CreateBr(load[0]);
602        for (unsigned i = 0; i < n; ++i) {
603
604            SetInsertPoint(load[i]);
605            Value * const outputConsumers = getConsumerLock(consumers[i].getName());
606
607            Value * const consumerCount = CreateLoad(CreateGEP(outputConsumers, {zero, zero}));
608            Value * const consumerPtr = CreateLoad(CreateGEP(outputConsumers, {zero, one}));
609            Value * const noConsumers = CreateICmpEQ(consumerCount, size0);
610            CreateUnlikelyCondBr(noConsumers, load[i + 1], wait[i]);
611
612            SetInsertPoint(wait[i]);
613            PHINode * const consumerPhi = CreatePHI(sizeTy, 2);
614            consumerPhi->addIncoming(size0, load[i]);
615
616            Value * const conSegPtr = CreateLoad(CreateGEP(consumerPtr, consumerPhi));
617            Value * const processedSegmentCount = CreateAtomicLoadAcquire(conSegPtr);
618            Value * const ready = CreateICmpEQ(segNo, processedSegmentCount);
619            assert (ready->getType() == getInt1Ty());
620            Value * const nextConsumerIdx = CreateAdd(consumerPhi, CreateZExt(ready, sizeTy));
621            consumerPhi->addIncoming(nextConsumerIdx, wait[i]);
622            Value * const next = CreateICmpEQ(nextConsumerIdx, consumerCount);
623            CreateCondBr(next, load[i + 1], wait[i]);
624        }
625
626        BasicBlock * const exit = load[n];
627        SetInsertPoint(exit);
628        return exit;
629    }
630}
631
632}
Note: See TracBrowser for help on using the repository browser.