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

Last change on this file since 5856 was 5856, checked in by nmedfort, 18 months ago

Revised pipeline structure to better control I/O rates

File size: 28.8 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
166/** ------------------------------------------------------------------------------------------------------------- *
167 * @brief getFieldWidth
168 ** ------------------------------------------------------------------------------------------------------------- */
169inline unsigned getFieldWidth(const unsigned bitWidth, const unsigned blockWidth) {
170    for (unsigned k = 16; k <= blockWidth; k *= 2) {
171        if ((bitWidth & (k - 1)) != 0) {
172            return k / 2;
173        }
174    }
175    return blockWidth;
176}
177
178/** ------------------------------------------------------------------------------------------------------------- *
179 * @brief CreateStreamCpy
180 ** ------------------------------------------------------------------------------------------------------------- */
181void KernelBuilder::CreateStreamCpy(const std::string & name, Value * target, Value * targetOffset, Value * source, Value * sourceOffset, Value * itemsToCopy, const unsigned itemAlignment) {
182
183    assert (target && targetOffset);
184    assert (source && sourceOffset);
185    assert (target->getType() == source->getType());
186    assert (target->getType()->isPointerTy());
187    assert (isConstantZero(targetOffset) || isConstantZero(sourceOffset));
188
189    const StreamSetBuffer * const buf = mKernel->getAnyStreamSetBuffer(name);
190
191    const auto itemWidth = getItemWidth(buf->getBaseType());
192    assert ("invalid item width" && is_power_2(itemWidth));
193    const auto blockWidth = getBitBlockWidth();
194    // Although our item width may be n bits, if we know we're always processing m items per block, our field width
195    // (w.r.t the stream copy) would be n*m. By taking this into account we can optimize and simplify the copy code.
196    const auto fieldWidth = getFieldWidth(itemWidth * itemAlignment, blockWidth);
197    const auto alignment = (fieldWidth + 7) / 8;
198
199    if (LLVM_LIKELY(itemWidth < fieldWidth)) {
200        const auto factor = fieldWidth / itemWidth;
201        Constant * const FACTOR = getSize(factor);
202        if (LLVM_UNLIKELY(codegen::DebugOptionIsSet(codegen::EnableAsserts))) {
203            ConstantInt * const ALIGNMENT = getSize(alignment);
204            const auto kernelName = mKernel->getName()+ ": " + name;
205            CreateAssertZero(CreateURem(CreatePtrToInt(target, getSizeTy()), ALIGNMENT), kernelName + " target is misaligned (" + std::to_string(alignment) + ")");
206            CreateAssertZero(CreateURem(targetOffset, FACTOR), kernelName + " target offset is misaligned (" + std::to_string(factor) + ")");
207            CreateAssertZero(CreateURem(CreatePtrToInt(source, getSizeTy()), ALIGNMENT), kernelName + " source is misaligned (" + std::to_string(alignment) + ")");
208            CreateAssertZero(CreateURem(sourceOffset, FACTOR), kernelName + " source offset is misaligned (" + std::to_string(factor) + ")");
209        }
210        targetOffset = CreateUDiv(targetOffset, FACTOR);
211        sourceOffset = CreateUDiv(sourceOffset, FACTOR);
212    }
213
214    /*
215       Streams are conceptually modelled as:
216
217                                            BLOCKS
218
219                                      A     B     C     D
220           STREAM SET ELEMENT   1  |aaaaa|bbbbb|ccccc|dddd |
221                                2  |eeeee|fffff|ggggg|hhhh |
222                                3  |iiiii|jjjjj|kkkkk|llll |
223
224       But the memory layout is actually:
225
226           A_1   A_2   A_3   B_1   B_2   B_3   C_1   C_2   C_3   D_1   D_2   D_3
227
228         |aaaaa|eeeee|iiiii|bbbbb|fffff|jjjjj|ccccc|ggggg|kkkkk|dddd |hhhh |llll |
229
230
231       So if we're copying the entire stream set block or our stream set has one element, we can use memcpy.
232
233       One compilication here is when the BlockSize of a stream is not equal to the BitBlockWidth.
234
235
236    */
237
238    Type * const fieldWidthTy = getIntNTy(fieldWidth);
239
240    Value * n = buf->getStreamSetCount(this, getStreamHandle(name));
241
242    if (isConstantOne(n) || fieldWidth == blockWidth || (isConstantZero(targetOffset) && isConstantZero(sourceOffset))) {
243        if (LLVM_LIKELY(itemWidth < 8)) {
244            itemsToCopy = CreateUDivCeil(itemsToCopy, getSize(8 / itemWidth));
245        } else if (LLVM_UNLIKELY(itemWidth > 8)) {
246            itemsToCopy = CreateMul(itemsToCopy, getSize(itemWidth / 8));
247        }
248        if (!isConstantOne(n)) {
249            itemsToCopy = CreateMul(itemsToCopy, n);
250        }
251        PointerType * const ptrTy = fieldWidthTy->getPointerTo();
252        target = CreateGEP(CreatePointerCast(target, ptrTy), targetOffset);
253        source = CreateGEP(CreatePointerCast(source, ptrTy), sourceOffset);
254        CreateMemCpy(target, source, itemsToCopy, alignment);
255
256    } else { // either the target offset or source offset is non-zero but not both
257
258        VectorType * const blockTy = getBitBlockType();
259        PointerType * const blockPtrTy = blockTy->getPointerTo();
260
261        target = CreatePointerCast(target, blockPtrTy, "target");
262        source = CreatePointerCast(source, blockPtrTy, "source");
263
264        assert ((blockWidth % fieldWidth) == 0);
265
266        VectorType * const shiftTy = VectorType::get(fieldWidthTy, blockWidth / fieldWidth);
267        Constant * const width = getSize(blockWidth / itemWidth);
268        Constant * const ZERO = getSize(0);
269        Constant * const ONE = getSize(1);
270        BasicBlock * const entry = GetInsertBlock();
271
272        if (isConstantZero(targetOffset)) {
273
274            /*
275                                                BLOCKS
276
277                                          A     B     C     D
278               SOURCE STREAM        1  |aaa--|bbbBB|cccCC|  dDD|
279                                    2  |eee--|fffFF|gggGG|  hHH|
280                                    3  |iii--|jjjJJ|kkkKK|  lLL|
281
282
283                                          A     B     C     D
284               TARGET STREAM        1  |BBaaa|CCbbb|DDccc|    d|
285                                    2  |FFeee|GGfff|HHggg|    h|
286                                    3  |JJiii|KKjjj|LLkkk|    l|
287             */
288
289            Value * const blocksToCopy = CreateMul(CreateUDiv(itemsToCopy, width), n);
290            Value * const offset = CreateURem(sourceOffset, width);
291            Value * const offsetVector = simd_fill(fieldWidth, CreateTrunc(offset, fieldWidthTy));
292            Value * const remaining = CreateSub(width, offset);
293            Value * const remainingVector = simd_fill(fieldWidth, CreateTrunc(remaining, fieldWidthTy));
294
295            BasicBlock * const streamCopy = CreateBasicBlock(name + "PullCopy");
296            BasicBlock * const streamCopyRemaining = CreateBasicBlock(name + "PullCopyRemaining");
297            BasicBlock * const streamCopyEnd = CreateBasicBlock(name + "PullCopyEnd");
298
299            CreateCondBr(CreateICmpNE(blocksToCopy, ZERO), streamCopy, streamCopyRemaining);
300
301            SetInsertPoint(streamCopy);
302            PHINode * const i = CreatePHI(getSizeTy(), 2);
303            i->addIncoming(n, entry);
304            Value * prior = CreateAlignedLoad(CreateGEP(source, CreateSub(i, n)), alignment);
305            prior = CreateBitCast(CreateLShr(CreateBitCast(prior, shiftTy), offsetVector), blockTy);
306            Value * value = CreateAlignedLoad(CreateGEP(source, i), alignment);
307            value = CreateBitCast(CreateShl(CreateBitCast(value, shiftTy), remainingVector), blockTy);
308            CreateAlignedStore(CreateOr(value, prior), CreateGEP(target, i), alignment);
309            Value * const next_i = CreateAdd(i, ONE);
310            i->addIncoming(next_i, streamCopy);
311            CreateCondBr(CreateICmpNE(next_i, blocksToCopy), streamCopy, streamCopyRemaining);
312
313            SetInsertPoint(streamCopyRemaining);
314            PHINode * const j = CreatePHI(getSizeTy(), 2);
315            j->addIncoming(blocksToCopy, entry);
316            j->addIncoming(blocksToCopy, streamCopy);
317            Value * final = CreateAlignedLoad(CreateGEP(source, j), alignment);
318            final = CreateBitCast(CreateLShr(CreateBitCast(final, shiftTy), offsetVector), blockTy);
319            CreateAlignedStore(final, CreateGEP(target, j), alignment);
320            Value * const next_j = CreateAdd(j, ONE);
321            j->addIncoming(next_j, streamCopyRemaining);
322            CreateCondBr(CreateICmpNE(next_j, CreateAdd(blocksToCopy, n)), streamCopyRemaining, streamCopyEnd);
323
324            SetInsertPoint(streamCopyEnd);
325
326        } else if (isConstantZero(sourceOffset)) {
327
328            /*
329                                                BLOCKS
330
331                                          A     B     C     D
332               SOURCE STREAM        1  |AAAaa|BBBaa|CCCcc|    d|
333                                    2  |EEEee|FFFff|GGGgg|    h|
334                                    3  |IIIii|JJJjj|KKKkk|    l|
335
336
337                                          A     B     C     D
338               TARGET STREAM        1  |aa---|bbAAA|ccBBB| dCCC|
339                                    2  |ee---|ffEEE|ggFFF| hGGG|
340                                    3  |ii---|jjIII|kkJJJ| lKKK|
341
342            */
343
344            BasicBlock * const streamCopy = CreateBasicBlock(name + "PushCopy");
345            BasicBlock * const streamCopyRemainingCond = CreateBasicBlock(name + "PushCopyRemainingCond");
346            BasicBlock * const streamCopyRemaining = CreateBasicBlock(name + "PushCopyRemaining");
347            BasicBlock * const streamCopyEnd = CreateBasicBlock(name + "PushCopyEnd");
348
349            Value * const pos = CreateURem(targetOffset, width);
350            Value * const copied = CreateSub(width, pos);
351            Value * const copiedVector = simd_fill(fieldWidth, CreateTrunc(copied, fieldWidthTy));
352            Value * const mask = CreateLShr(Constant::getAllOnesValue(shiftTy), copiedVector);
353            Value * const offsetVector = simd_fill(fieldWidth, CreateTrunc(pos, fieldWidthTy));
354
355            CreateBr(streamCopy);
356
357            SetInsertPoint(streamCopy);
358            PHINode * const i = CreatePHI(getSizeTy(), 2);
359            i->addIncoming(ZERO, entry);
360            Value * priorTargetValue = CreateAlignedLoad(CreateGEP(target, i), alignment);
361            priorTargetValue = CreateBitCast(CreateAnd(CreateBitCast(priorTargetValue, shiftTy), mask), blockTy);
362            Value * sourceValue = CreateAlignedLoad(CreateGEP(source, i), alignment);
363            sourceValue = CreateBitCast(CreateShl(CreateBitCast(sourceValue, shiftTy), offsetVector), blockTy);
364            CreateAlignedStore(CreateOr(sourceValue, priorTargetValue), CreateGEP(target, i), alignment);
365            Value * const next_i = CreateAdd(i, ONE);
366            i->addIncoming(next_i, streamCopy);
367            CreateCondBr(CreateICmpNE(next_i, n), streamCopy, streamCopyRemainingCond);
368
369            SetInsertPoint(streamCopyRemainingCond);
370            Value * const blocksToCopy = CreateMul(CreateUDiv(CreateSub(itemsToCopy, copied), width), n);
371            CreateCondBr(CreateICmpULT(copied, itemsToCopy), streamCopyRemaining, streamCopyEnd);
372
373            SetInsertPoint(streamCopyRemaining);
374            PHINode * const j = CreatePHI(getSizeTy(), 2);
375            j->addIncoming(n, streamCopyRemainingCond);
376            Value * prior = CreateAlignedLoad(CreateGEP(source, CreateSub(j, n)), alignment);
377            prior = CreateBitCast(CreateShl(CreateBitCast(prior, shiftTy), offsetVector), blockTy);
378            Value * value = CreateAlignedLoad(CreateGEP(source, j), alignment);
379            value = CreateBitCast(CreateLShr(CreateBitCast(value, shiftTy), copiedVector), blockTy);
380            CreateAlignedStore(CreateOr(value, prior), CreateGEP(target, j), alignment);
381            Value * const next_j = CreateAdd(j, ONE);
382            j->addIncoming(next_j, streamCopyRemaining);
383            CreateCondBr(CreateICmpNE(next_j, blocksToCopy), streamCopyRemaining, streamCopyEnd);
384
385            SetInsertPoint(streamCopyEnd);
386        }
387    }
388}
389
390Value * KernelBuilder::getConsumerLock(const std::string & name) {
391    return getScalarField(name + Kernel::CONSUMER_SUFFIX);
392}
393
394void KernelBuilder::setConsumerLock(const std::string & name, Value * value) {
395    setScalarField(name + Kernel::CONSUMER_SUFFIX, value);
396}
397
398Value * KernelBuilder::loadInputStreamBlock(const std::string & name, Value * streamIndex) {
399    return CreateBlockAlignedLoad(getInputStreamBlockPtr(name, streamIndex));
400}
401
402Value * KernelBuilder::getInputStreamPackPtr(const std::string & name, Value * streamIndex, Value * packIndex) {
403    Value * const addr = mKernel->getStreamSetInputAddress(name);
404    if (addr) {
405        return CreateGEP(addr, {getInt32(0), streamIndex, packIndex});
406    } else {
407        const StreamSetBuffer * const buf = mKernel->getInputStreamSetBuffer(name);
408        Value * const blockIndex = CreateLShr(getProcessedItemCount(name), std::log2(getBitBlockWidth()));
409        return buf->getStreamPackPtr(this, getStreamHandle(name), getBaseAddress(name), streamIndex, blockIndex, packIndex, true);
410    }
411}
412
413Value * KernelBuilder::loadInputStreamPack(const std::string & name, Value * streamIndex, Value * packIndex) {
414    return CreateBlockAlignedLoad(getInputStreamPackPtr(name, streamIndex, packIndex));
415}
416
417Value * KernelBuilder::getInputStreamSetCount(const std::string & name) {
418    const StreamSetBuffer * const buf = mKernel->getInputStreamSetBuffer(name);
419    return buf->getStreamSetCount(this, getStreamHandle(name));
420}
421
422Value * KernelBuilder::getInputStreamBlockPtr(const std::string & name, Value * const streamIndex, Value * const blockOffset) {
423    Value * const addr = mKernel->getStreamSetInputAddress(name);
424    if (addr) {
425        return CreateGEP(addr, {blockOffset, streamIndex});
426    } else {
427        const StreamSetBuffer * const buf = mKernel->getInputStreamSetBuffer(name);
428        Value * blockIndex = CreateLShr(getProcessedItemCount(name), std::log2(getBitBlockWidth()));
429        blockIndex = CreateAdd(blockIndex, blockOffset);
430        return buf->getStreamBlockPtr(this, getStreamHandle(name), getBaseAddress(name), streamIndex, blockIndex, true);
431    }
432}
433
434Value * KernelBuilder::getOutputStreamBlockPtr(const std::string & name, Value * streamIndex, Value * const blockOffset) {
435    Value * const addr = mKernel->getStreamSetOutputAddress(name);
436    if (addr) {
437        return CreateGEP(addr, {blockOffset, streamIndex});
438    } else {
439        const StreamSetBuffer * const buf = mKernel->getOutputStreamSetBuffer(name);
440        Value * const blockIndex = CreateLShr(getProducedItemCount(name), std::log2(getBitBlockWidth()));
441        return buf->getStreamBlockPtr(this, getStreamHandle(name), getBaseAddress(name), streamIndex, blockIndex, false);
442    }
443}
444
445StoreInst * KernelBuilder::storeOutputStreamBlock(const std::string & name, Value * streamIndex, Value * toStore) {
446    Value * const ptr = getOutputStreamBlockPtr(name, streamIndex);
447    Type * const storeTy = toStore->getType();
448    Type * const ptrElemTy = ptr->getType()->getPointerElementType();
449    if (LLVM_UNLIKELY(storeTy != ptrElemTy)) {
450        if (LLVM_LIKELY(storeTy->canLosslesslyBitCastTo(ptrElemTy))) {
451            toStore = CreateBitCast(toStore, ptrElemTy);
452        } else {
453            std::string tmp;
454            raw_string_ostream out(tmp);
455            out << "invalid type conversion when calling storeOutputStreamBlock on " <<  name << ": ";
456            ptrElemTy->print(out);
457            out << " vs. ";
458            storeTy->print(out);
459        }
460    }
461    return CreateBlockAlignedStore(toStore, ptr);
462}
463
464Value * KernelBuilder::getOutputStreamPackPtr(const std::string & name, Value * streamIndex, Value * packIndex) {
465    Value * const addr = mKernel->getStreamSetOutputAddress(name);
466    if (addr) {
467        return CreateGEP(addr, {getInt32(0), streamIndex, packIndex});
468    } else {
469        const StreamSetBuffer * const buf = mKernel->getOutputStreamSetBuffer(name);
470        Value * const blockIndex = CreateLShr(getProducedItemCount(name), std::log2(getBitBlockWidth()));
471        return buf->getStreamPackPtr(this, getStreamHandle(name), getBaseAddress(name), streamIndex, blockIndex, packIndex, false);
472    }
473}
474
475StoreInst * KernelBuilder::storeOutputStreamPack(const std::string & name, Value * streamIndex, Value * packIndex, Value * toStore) {
476    Value * const ptr = getOutputStreamPackPtr(name, streamIndex, packIndex);
477    Type * const storeTy = toStore->getType();
478    Type * const ptrElemTy = ptr->getType()->getPointerElementType();
479    if (LLVM_UNLIKELY(storeTy != ptrElemTy)) {
480        if (LLVM_LIKELY(storeTy->canLosslesslyBitCastTo(ptrElemTy))) {
481            toStore = CreateBitCast(toStore, ptrElemTy);
482        } else {
483            std::string tmp;
484            raw_string_ostream out(tmp);
485            out << "invalid type conversion when calling storeOutputStreamPack on " <<  name << ": ";
486            ptrElemTy->print(out);
487            out << " vs. ";
488            storeTy->print(out);
489        }
490    }
491    return CreateBlockAlignedStore(toStore, ptr);
492}
493
494Value * KernelBuilder::getOutputStreamSetCount(const std::string & name) {
495    const StreamSetBuffer * const buf = mKernel->getOutputStreamSetBuffer(name);
496    return buf->getStreamSetCount(this, getStreamHandle(name));
497}
498
499Value * KernelBuilder::getRawInputPointer(const std::string & name, Value * absolutePosition) {
500    const StreamSetBuffer * const buf = mKernel->getInputStreamSetBuffer(name);
501    return buf->getRawItemPointer(this, getStreamHandle(name), absolutePosition);
502}
503
504Value * KernelBuilder::getRawOutputPointer(const std::string & name, Value * absolutePosition) {
505    const StreamSetBuffer * const buf = mKernel->getOutputStreamSetBuffer(name);
506    return buf->getRawItemPointer(this, getStreamHandle(name), absolutePosition);
507}
508
509Value * KernelBuilder::getBaseAddress(const std::string & name) {
510    return mKernel->getAnyStreamSetBuffer(name)->getBaseAddress(this, getStreamHandle(name));
511}
512
513void KernelBuilder::setBaseAddress(const std::string & name, Value * const addr) {
514    return mKernel->getAnyStreamSetBuffer(name)->setBaseAddress(this, getStreamHandle(name), addr);
515}
516
517Value * KernelBuilder::getBufferedSize(const std::string & name) {
518    return mKernel->getAnyStreamSetBuffer(name)->getBufferedSize(this, getStreamHandle(name));
519}
520
521void KernelBuilder::setBufferedSize(const std::string & name, Value * size) {
522    mKernel->getAnyStreamSetBuffer(name)->setBufferedSize(this, getStreamHandle(name), size);
523}
524
525Value * KernelBuilder::getCapacity(const std::string & name) {
526    return mKernel->getAnyStreamSetBuffer(name)->getCapacity(this, getStreamHandle(name));
527}
528
529void KernelBuilder::setCapacity(const std::string & name, Value * c) {
530    mKernel->getAnyStreamSetBuffer(name)->setCapacity(this, getStreamHandle(name), c);
531}
532
533Value * KernelBuilder::getBlockAddress(const std::string & name, Value * blockIndex) {
534    const StreamSetBuffer * const buf = mKernel->getAnyStreamSetBuffer(name);
535    return buf->getBlockAddress(this, getStreamHandle(name), blockIndex);
536}
537
538void KernelBuilder::protectOutputStream(const std::string & name, const bool readOnly) {
539    const StreamSetBuffer * const buf = mKernel->getOutputStreamSetBuffer(name);
540    Value * const handle = getStreamHandle(name);
541    Value * const base = buf->getBaseAddress(this, handle);
542    Value * sz = ConstantExpr::getSizeOf(buf->getType());
543    sz = CreateMul(sz, getInt64(buf->getBufferBlocks()));
544    sz = CreateMul(sz, CreateZExt(buf->getStreamSetCount(this, handle), getInt64Ty()));
545    CreateMProtect(base, sz, readOnly ? CBuilder::READ : (CBuilder::READ | CBuilder::WRITE));
546}
547   
548CallInst * KernelBuilder::createDoSegmentCall(const std::vector<Value *> & args) {
549    return mKernel->makeDoSegmentCall(*this, args);
550}
551
552Value * KernelBuilder::getAccumulator(const std::string & accumName) {
553    auto results = mKernel->mOutputScalarResult;
554    if (LLVM_UNLIKELY(results == nullptr)) {
555        report_fatal_error("Cannot get accumulator " + accumName + " until " + mKernel->getName() + " has terminated.");
556    }
557    const auto & outputs = mKernel->getScalarOutputs();
558    const auto n = outputs.size();
559    if (LLVM_UNLIKELY(n == 0)) {
560        report_fatal_error(mKernel->getName() + " has no output scalars.");
561    } else {
562        for (unsigned i = 0; i < n; ++i) {
563            const Binding & b = outputs[i];
564            if (b.getName() == accumName) {
565                if (n == 1) {
566                    return results;
567                } else {
568                    return CreateExtractValue(results, {i});
569                }
570            }
571        }
572        report_fatal_error(mKernel->getName() + " has no output scalar named " + accumName);
573    }
574}
575
576void KernelBuilder::doubleCapacity(const std::string & name) {
577    const StreamSetBuffer * const buf = mKernel->getAnyStreamSetBuffer(name);
578    return buf->doubleCapacity(this, getStreamHandle(name));
579}
580
581BasicBlock * KernelBuilder::CreateConsumerWait() {
582    const auto consumers = mKernel->getStreamOutputs();
583    BasicBlock * const entry = GetInsertBlock();
584    if (consumers.empty()) {
585        return entry;
586    } else {
587        Function * const parent = entry->getParent();
588        IntegerType * const sizeTy = getSizeTy();
589        ConstantInt * const zero = getInt32(0);
590        ConstantInt * const one = getInt32(1);
591        ConstantInt * const size0 = getSize(0);
592
593        Value * const segNo = acquireLogicalSegmentNo();
594        const auto n = consumers.size();
595        BasicBlock * load[n + 1];
596        BasicBlock * wait[n];
597        for (unsigned i = 0; i < n; ++i) {
598            load[i] = BasicBlock::Create(getContext(), consumers[i].getName() + "Load", parent);
599            wait[i] = BasicBlock::Create(getContext(), consumers[i].getName() + "Wait", parent);
600        }
601        load[n] = BasicBlock::Create(getContext(), "Resume", parent);
602        CreateBr(load[0]);
603        for (unsigned i = 0; i < n; ++i) {
604
605            SetInsertPoint(load[i]);
606            Value * const outputConsumers = getConsumerLock(consumers[i].getName());
607
608            Value * const consumerCount = CreateLoad(CreateGEP(outputConsumers, {zero, zero}));
609            Value * const consumerPtr = CreateLoad(CreateGEP(outputConsumers, {zero, one}));
610            Value * const noConsumers = CreateICmpEQ(consumerCount, size0);
611            CreateUnlikelyCondBr(noConsumers, load[i + 1], wait[i]);
612
613            SetInsertPoint(wait[i]);
614            PHINode * const consumerPhi = CreatePHI(sizeTy, 2);
615            consumerPhi->addIncoming(size0, load[i]);
616
617            Value * const conSegPtr = CreateLoad(CreateGEP(consumerPtr, consumerPhi));
618            Value * const processedSegmentCount = CreateAtomicLoadAcquire(conSegPtr);
619            Value * const ready = CreateICmpEQ(segNo, processedSegmentCount);
620            assert (ready->getType() == getInt1Ty());
621            Value * const nextConsumerIdx = CreateAdd(consumerPhi, CreateZExt(ready, sizeTy));
622            consumerPhi->addIncoming(nextConsumerIdx, wait[i]);
623            Value * const next = CreateICmpEQ(nextConsumerIdx, consumerCount);
624            CreateCondBr(next, load[i + 1], wait[i]);
625        }
626
627        BasicBlock * const exit = load[n];
628        SetInsertPoint(exit);
629        return exit;
630    }
631}
632
633}
Note: See TracBrowser for help on using the repository browser.