source: icGREP/icgrep-devel/icgrep/kernels/swizzle.cpp @ 6186

Last change on this file since 6186 was 6186, checked in by cameron, 5 months ago

Various clean-ups

File size: 7.3 KB
Line 
1/*
2 *  Copyright (c) 2017 International Characters.
3 *  This software is licensed to the public under the Open Software License 3.0.
4 */
5
6#include "swizzle.h"
7#include <kernels/kernel_builder.h>
8#include <llvm/Support/raw_ostream.h>
9#include <string>
10#include <vector>
11
12using namespace llvm;
13
14namespace kernel {
15
16inline static bool is_power_2(const uint64_t n) {
17    return ((n & (n - 1)) == 0) && n;
18}
19
20LLVM_READNONE inline unsigned getBitStreamCount(const std::vector<StreamSet *> & inputs) {
21    unsigned count = 0;
22
23
24    for (StreamSet * input : inputs) {
25        count += input->getNumElements();
26    }
27    return count;
28}
29
30
31inline std::string makeSwizzleName(const std::vector<StreamSet *> & inputs, const std::vector<StreamSet *> & outputs, const unsigned fieldWidth) {
32    const auto inputStreamCount = getBitStreamCount(inputs);
33    const auto outputStreamCount = getBitStreamCount(outputs);
34    if (LLVM_UNLIKELY(inputStreamCount != outputStreamCount)) {
35        report_fatal_error("total number of input elements does not match the output elements");
36    }
37    std::string tmp;
38    raw_string_ostream out(tmp);
39    out << "swizzle" << fieldWidth << ':' << inputStreamCount << '_' << inputs.size() << '_' << outputs.size();
40    out.flush();
41    return tmp;
42}
43
44inline size_t ceil_udiv(const size_t n, const size_t m) {
45    return (n + m - 1) / m;
46}
47
48inline Bindings makeSwizzledInputs(const std::vector<StreamSet *> & inputs) {
49    Bindings bindings;
50    const auto n = inputs.size();
51    bindings.reserve(n);
52    const auto numElements = inputs[0]->getNumElements();
53    for (unsigned i = 0; i < n; ++i) {
54        if (LLVM_UNLIKELY(inputs[i]->getNumElements() != numElements)) {
55            report_fatal_error("not all inputs have the same number of elements");
56        }
57        bindings.emplace_back("inputGroup" + std::to_string(i), inputs[i]);
58    }
59    return bindings;
60}
61
62inline Bindings makeSwizzledOutputs(const std::vector<StreamSet *> & outputs, const unsigned fieldWidth) {
63    Bindings bindings;
64    const auto n = outputs.size();
65    bindings.reserve(n);
66    const auto numElements = outputs[0]->getNumElements();
67    for (unsigned i = 0; i < n; ++i) {
68        if (LLVM_UNLIKELY(outputs[i]->getNumElements() != numElements)) {
69            report_fatal_error("not all outputs have the same number of elements");
70        }
71        bindings.emplace_back("outputGroup" + std::to_string(i), outputs[i], FixedRate(1), BlockSize(fieldWidth));
72    }
73    return bindings;
74}
75
76SwizzleGenerator::SwizzleGenerator(const std::unique_ptr<kernel::KernelBuilder> &,
77                                   const std::vector<StreamSet *> & inputs,
78                                   const std::vector<StreamSet *> & outputs,
79                                   const unsigned fieldWidth)
80: BlockOrientedKernel(makeSwizzleName(inputs, outputs, fieldWidth),
81makeSwizzledInputs(inputs),
82makeSwizzledOutputs(outputs, fieldWidth),
83{}, {}, {})
84, mBitStreamCount(getBitStreamCount(inputs))
85, mFieldWidth(fieldWidth) {
86
87}
88
89void SwizzleGenerator::generateDoBlockMethod(const std::unique_ptr<kernel::KernelBuilder> & b) {
90       
91    // We may need a few passes depending on the swizzle factor
92
93    if (LLVM_UNLIKELY(!is_power_2(mFieldWidth))) {
94        report_fatal_error("fieldWidth must be a power of 2");
95    }
96    if (LLVM_UNLIKELY(mFieldWidth > b->getBitBlockWidth())) {
97        report_fatal_error("fieldWidth must be a power of 2");
98    }
99
100    const auto swizzleFactor = b->getBitBlockWidth() / mFieldWidth;
101    const auto passes = std::log2(swizzleFactor);
102    const auto swizzleGroups = ceil_udiv(mBitStreamCount, swizzleFactor);
103    const auto inputStreamsPerSet = ceil_udiv(mBitStreamCount, getNumOfStreamInputs());
104    const auto outputStreamsPerSet = ceil_udiv(mBitStreamCount, getNumOfStreamOutputs());
105
106    Value * sourceBlocks[swizzleFactor];
107    Value * targetBlocks[swizzleFactor];
108    for (unsigned grp = 0; grp < swizzleGroups; grp++) {
109        // First load all the data.       
110        for (unsigned i = 0; i < swizzleFactor; i++) {
111            const auto streamNo = grp * swizzleFactor + i;
112            if (streamNo < mBitStreamCount) {
113                const auto inputSetNo = streamNo / inputStreamsPerSet;
114                const auto j = streamNo % inputStreamsPerSet;
115                sourceBlocks[i] = b->loadInputStreamBlock("inputGroup" + std::to_string(inputSetNo), b->getInt32(j));
116            } else {
117                // Fill in the remaining logically required streams of the last swizzle group with null values.
118                sourceBlocks[i] = Constant::getNullValue(b->getBitBlockType());
119            }
120        }
121        // Now perform the swizzle passes.
122        for (unsigned p = 0; p < passes; p++) {
123            for (unsigned i = 0; i < swizzleFactor / 2; i++) {
124                targetBlocks[i * 2] = b->esimd_mergel(mFieldWidth, sourceBlocks[i], sourceBlocks[i + (swizzleFactor / 2)]);
125                targetBlocks[(i * 2) + 1] = b->esimd_mergeh(mFieldWidth, sourceBlocks[i], sourceBlocks[i + (swizzleFactor / 2)]);
126            }
127            for (unsigned i = 0; i < swizzleFactor; i++) {
128                sourceBlocks[i] = targetBlocks[i];
129            }
130        }
131        for (unsigned i = 0; i < swizzleFactor; i++) {
132            unsigned streamNo = grp * swizzleFactor + i;
133            unsigned outputSetNo = streamNo / outputStreamsPerSet;
134            unsigned j = streamNo % outputStreamsPerSet;
135            b->storeOutputStreamBlock("outputGroup" + std::to_string(outputSetNo), b->getInt32(j), b->bitCast(sourceBlocks[i]));
136        }
137    }
138}
139
140
141SwizzleByGather::SwizzleByGather(const std::unique_ptr<KernelBuilder> &iBuilder)
142: BlockOrientedKernel("swizzleByGather", {}, {}, {}, {}, {}){
143    for (unsigned i = 0; i < 2; i++) {
144        mInputStreamSets.push_back(Binding{iBuilder->getStreamSetTy(4, 1), "inputGroup" + std::to_string(i)});
145    }
146    for (unsigned i = 0; i < 1; i++) {
147        mOutputStreamSets.push_back(Binding{iBuilder->getStreamSetTy(8, 1), "outputGroup" + std::to_string(i), FixedRate(1)});
148    }
149}
150
151void SwizzleByGather::generateDoBlockMethod(const std::unique_ptr<kernel::KernelBuilder> &b) {
152    Value* outputStreamPtr = b->getOutputStreamBlockPtr("outputGroup0", b->getSize(0));
153
154    for (unsigned i = 0; i < 2; i++) {
155        std::vector<llvm::Value*> inputStream;
156        Value* inputPtr = b->getInputStreamBlockPtr("inputGroup" + std::to_string(i), b->getSize(0));
157
158        Value* inputBytePtr = b->CreatePointerCast(inputPtr, b->getInt8PtrTy());
159        Function *gatherFunc = Intrinsic::getDeclaration(b->getModule(), Intrinsic::x86_avx2_gather_d_q_256);
160        Value *addresses = ConstantVector::get(
161                {b->getInt32(0), b->getInt32(32), b->getInt32(64), b->getInt32(96)});
162
163        for (unsigned j = 0; j < 4; j++) {
164            Value *gather_result = b->CreateCall(
165                    gatherFunc,
166                    {
167                            UndefValue::get(b->getBitBlockType()),
168                            inputBytePtr,
169                            addresses,
170                            Constant::getAllOnesValue(b->getBitBlockType()),
171                            b->getInt8(1)
172                    }
173            );
174
175            inputBytePtr = b->CreateGEP(inputBytePtr, b->getInt32(8));
176
177            b->CreateStore(gather_result, outputStreamPtr);
178            outputStreamPtr = b->CreateGEP(outputStreamPtr, b->getSize(1));
179        }
180    }
181}
182
183}
Note: See TracBrowser for help on using the repository browser.