source: icGREP/icgrep-devel/icgrep/kernels/pipeline/pipeline_kernel.cpp @ 6237

Last change on this file since 6237 was 6237, checked in by nmedfort, 7 months ago

Re-enabled segment pipeline parallelism; moved logical segment number into pipeline kernel.

File size: 15.1 KB
Line 
1#include <kernels/pipeline_kernel.h>
2#include <kernels/relationship.h>
3#include "pipeline_compiler.hpp"
4#include "pipeline_analysis.hpp"
5#include "buffer_management_logic.hpp"
6#include "consumer_logic.hpp"
7#include "core_logic.hpp"
8#include "kernel_logic.hpp"
9#include "cycle_counter_logic.hpp"
10#include "popcount_logic.hpp"
11#include "pipeline_logic.hpp"
12#include <llvm/IR/Function.h>
13
14// NOTE: the pipeline kernel is primarily a proxy for the pipeline compiler. Ideally, by making some kernels
15// a "family", the pipeline kernel will be compiled once for the lifetime of a program. Thus we can avoid even
16// constructing any data structures for the pipeline in normal usage.
17
18namespace kernel {
19
20#warning make sure all virtual methods are proxied for when only one kernel exists in the pipeline
21
22/** ------------------------------------------------------------------------------------------------------------- *
23 * @brief addInternalKernelProperties
24 ** ------------------------------------------------------------------------------------------------------------- */
25void PipelineKernel::addInternalKernelProperties(const std::unique_ptr<kernel::KernelBuilder> & b) {
26    if (LLVM_UNLIKELY(isProxy())) {
27        mKernels[0]->addInternalKernelProperties(b);
28    } else { // add handles for each of unique streams
29        mCompiler = llvm::make_unique<PipelineCompiler>(b, this);
30        mCompiler->addPipelineKernelProperties(b);
31    }
32}
33
34/** ------------------------------------------------------------------------------------------------------------- *
35 * @brief initializeInstance
36 ** ------------------------------------------------------------------------------------------------------------- */
37void PipelineKernel::initializeInstance(const std::unique_ptr<KernelBuilder> & b, std::vector<Value *> & args) {
38
39    if (LLVM_UNLIKELY(isProxy())) {
40        mKernels[0]->initializeInstance(b, args);
41    } else {
42        assert (args[0] && "cannot initialize before creation");
43        assert (args[0]->getType()->getPointerElementType() == mKernelStateType);
44        b->setKernel(this);
45
46        // append the kernel pointers for any kernel belonging to a family
47        Module * const m = b->getModule();
48        for (auto & kernel : mKernels) {
49            if (kernel->hasFamilyName()) {
50                kernel->addKernelDeclarations(b);
51                Value * const handle = kernel->createInstance(b);
52                PointerType * const voidPtrTy = b->getVoidPtrTy();
53                args.push_back(b->CreatePointerCast(handle, voidPtrTy));
54                args.push_back(b->CreatePointerCast(kernel->getInitFunction(m), voidPtrTy));
55                args.push_back(b->CreatePointerCast(kernel->getDoSegmentFunction(m), voidPtrTy));
56                args.push_back(b->CreatePointerCast(kernel->getTerminateFunction(m), voidPtrTy));
57            }
58        }
59
60        Value * const init = getInitFunction(m);
61        assert (cast<FunctionType>(init->getType()->getPointerElementType())->getNumParams() == args.size());
62        b->CreateCall(init, args);
63    }
64}
65
66/** ------------------------------------------------------------------------------------------------------------- *
67 * @brief generateInitializeMethod
68 ** ------------------------------------------------------------------------------------------------------------- */
69void PipelineKernel::generateInitializeMethod(const std::unique_ptr<KernelBuilder> & b) {
70    if (LLVM_UNLIKELY(isProxy())) {
71        mKernels[0]->generateInitializeMethod(b);
72    } else {
73        // TODO: this isn't sufficient for composable PipelineKernel objects since would want to
74        // allocate memory once during initialization but have the buffer/kernel struct visible in
75        // the main kernel logic. This can be solved by heap allocating all structs or somehow
76        // passing the structs via the function call but only reentrant pipelines require this
77        // to maintain state.
78        mCompiler->generateInitializeMethod(b);
79    }
80}
81
82/** ------------------------------------------------------------------------------------------------------------- *
83 * @brief generateKernelMethod
84 ** ------------------------------------------------------------------------------------------------------------- */
85void PipelineKernel::generateKernelMethod(const std::unique_ptr<KernelBuilder> & b) {
86    if (LLVM_UNLIKELY(isProxy())) {
87        mKernels[0]->generateKernelMethod(b);
88    } else {
89        if (mNumOfThreads == 1) {
90            mCompiler->generateSingleThreadKernelMethod(b);
91        } else {
92            mCompiler->generateMultiThreadKernelMethod(b, mNumOfThreads);
93        }
94    }
95}
96
97/** ------------------------------------------------------------------------------------------------------------- *
98 * @brief finalizeInstance
99 ** ------------------------------------------------------------------------------------------------------------- */
100Value * PipelineKernel::finalizeInstance(const std::unique_ptr<KernelBuilder> & b) {
101    assert (mHandle && "was not set");
102    if (LLVM_UNLIKELY(isProxy())) {
103        return mKernels[0]->finalizeInstance(b);
104    } else {
105        Value * result = b->CreateCall(getTerminateFunction(b->getModule()), { mHandle });
106        mHandle = nullptr;
107        if (mOutputScalars.empty()) {
108            assert (!result || result->getType()->isVoidTy());
109            result = nullptr;
110        }
111        return result;
112    }
113}
114
115/** ------------------------------------------------------------------------------------------------------------- *
116 * @brief generateFinalizeMethod
117 ** ------------------------------------------------------------------------------------------------------------- */
118void PipelineKernel::generateFinalizeMethod(const std::unique_ptr<KernelBuilder> & b) {
119    if (LLVM_UNLIKELY(isProxy())) {
120        mKernels[0]->generateFinalizeMethod(b);
121    } else {
122        mCompiler->generateFinalizeMethod(b);
123    }
124}
125
126/** ------------------------------------------------------------------------------------------------------------- *
127 * @brief getFinalOutputScalars
128 ** ------------------------------------------------------------------------------------------------------------- */
129std::vector<Value *> PipelineKernel::getFinalOutputScalars(const std::unique_ptr<KernelBuilder> & b) {
130    if (LLVM_UNLIKELY(isProxy())) {
131        return mKernels[0]->getFinalOutputScalars(b);
132    } else {
133        return mCompiler->getFinalOutputScalars(b);
134    }
135}
136
137/** ------------------------------------------------------------------------------------------------------------- *
138 * @brief linkExternalMethods
139 ** ------------------------------------------------------------------------------------------------------------- */
140void PipelineKernel::linkExternalMethods(const std::unique_ptr<KernelBuilder> & b) {
141    if (LLVM_UNLIKELY(isProxy())) {
142        return mKernels[0]->linkExternalMethods(b);
143    } else {
144        for (const auto & k : mKernels) {
145            k->linkExternalMethods(b);
146        }
147        for (CallBinding & call : mCallBindings) {
148            call.Callee = b->LinkFunction(call.Name, call.Type, call.FunctionPointer);
149        }
150    }
151}
152
153/** ------------------------------------------------------------------------------------------------------------- *
154 * @brief addAdditionalFunctions
155 ** ------------------------------------------------------------------------------------------------------------- */
156void PipelineKernel::addAdditionalFunctions(const std::unique_ptr<KernelBuilder> & b) {
157    if (hasStaticMain()) {
158        addOrDeclareMainFunction(b, AddExternal);
159    }
160}
161
162/** ------------------------------------------------------------------------------------------------------------- *
163 * @brief hasStaticMain
164 ** ------------------------------------------------------------------------------------------------------------- */
165bool PipelineKernel::hasStaticMain() const {
166    for (Kernel * k : mKernels) {
167        if (k->hasFamilyName()) {
168            return false;
169        }
170    }
171    return true;
172}
173
174/** ------------------------------------------------------------------------------------------------------------- *
175 * @brief makeMainFunction
176 ** ------------------------------------------------------------------------------------------------------------- */
177Function * PipelineKernel::addOrDeclareMainFunction(const std::unique_ptr<kernel::KernelBuilder> & b, const MainMethodGenerationType method) {
178
179    b->setKernel(this);
180
181    // maintain consistency with the Kernel interface
182    const auto numOfDoSegArgs = (getNumOfStreamInputs() + getNumOfStreamOutputs()) * 2;
183    std::vector<Type *> paramTypes;
184    paramTypes.reserve(numOfDoSegArgs + getNumOfScalarInputs());
185    for (const auto & input : getInputStreamSetBuffers()) {
186        paramTypes.push_back(input->getType()->getPointerTo());
187        paramTypes.push_back(b->getSizeTy());
188    }
189    for (const auto & output : getOutputStreamSetBuffers()) {
190        paramTypes.push_back(output->getType()->getPointerTo());
191        paramTypes.push_back(b->getSizeTy());
192    }
193    for (const auto & input : getInputScalarBindings()) {
194        if (LLVM_LIKELY(!input.hasAttribute(AttrId::Family))) {
195            paramTypes.push_back(input.getType());
196        }
197    }
198    const auto numOfInitArgs = (paramTypes.size() - numOfDoSegArgs);
199
200    // get the finalize method output type and set its return type as this function's return type
201    Module * const m = b->getModule();
202    Function * const tf = getTerminateFunction(m);
203    FunctionType * const mainFunctionType = FunctionType::get(tf->getReturnType(), paramTypes, false);
204
205    auto linkageType = (method == AddInternal) ? Function::InternalLinkage : Function::ExternalLinkage;
206
207    Function * const main = Function::Create(mainFunctionType, linkageType, getName() + "_main", m);
208    main->setCallingConv(CallingConv::C);
209
210    if (method != DeclareExternal) {
211
212        b->SetInsertPoint(BasicBlock::Create(b->getContext(), "entry", main));
213        auto arg = main->arg_begin();
214
215        // TODO: Even if a kernel is in a family, it may have a different kernel struct. Make sure this works here.
216        Value * const handle = createInstance(b);
217        setHandle(b, handle);
218
219        std::vector<Value *> segmentArgs;
220        segmentArgs.reserve(numOfDoSegArgs + 2);
221        segmentArgs.push_back(handle);
222        segmentArgs.push_back(b->getSize(0));
223        for (unsigned i = 0; i < numOfDoSegArgs; ++i) {
224            segmentArgs.push_back(&*arg++);
225        }
226
227        std::vector<Value *> initArgs;
228        initArgs.reserve(numOfInitArgs + 1);
229        initArgs.push_back(handle);
230        for (unsigned i = 0; i < numOfInitArgs; ++i) {
231            initArgs.push_back(&*arg++);
232        }
233        assert (arg == main->arg_end());
234
235        // initialize the kernel
236        initializeInstance(b, initArgs);
237
238        // call the pipeline kernel
239        Function * const doSegment = getDoSegmentFunction(m);
240        assert (doSegment->getFunctionType()->getNumParams() == segmentArgs.size());
241        b->CreateCall(doSegment, segmentArgs);
242
243        // call and return the final output value(s)
244        b->CreateRet(finalizeInstance(b));
245    }
246
247    return main;
248}
249
250
251/** ------------------------------------------------------------------------------------------------------------- *
252 * @brief getName
253 *
254 * The name of the pipeline kernel the list of family names of each owned kernel followed by the description of the
255 * relationships between each.
256 ** ------------------------------------------------------------------------------------------------------------- */
257const std::string PipelineKernel::getName() const {
258    if (LLVM_UNLIKELY(isProxy())) {
259        return mKernels[0]->getName();
260    }
261    return mKernelName;
262}
263
264/** ------------------------------------------------------------------------------------------------------------- *
265 * @brief setInputStreamSetAt
266 ** ------------------------------------------------------------------------------------------------------------- */
267void PipelineKernel::setInputStreamSetAt(const unsigned i, StreamSet * const value) {
268    mInputStreamSets[i].setRelationship(value);
269}
270
271/** ------------------------------------------------------------------------------------------------------------- *
272 * @brief setOutputStreamSetAt
273 ** ------------------------------------------------------------------------------------------------------------- */
274void PipelineKernel::setOutputStreamSetAt(const unsigned i, StreamSet * const value) {
275    mOutputStreamSets[i].setRelationship(value);
276}
277
278/** ------------------------------------------------------------------------------------------------------------- *
279 * @brief setInputScalarAt
280 ** ------------------------------------------------------------------------------------------------------------- */
281void PipelineKernel::setInputScalarAt(const unsigned i, Scalar * const value) {
282    mInputScalars[i].setRelationship(value);
283}
284
285/** ------------------------------------------------------------------------------------------------------------- *
286 * @brief setOutputScalarAt
287 ** ------------------------------------------------------------------------------------------------------------- */
288void PipelineKernel::setOutputScalarAt(const unsigned i, Scalar * const value) {
289    mOutputScalars[i].setRelationship(value);
290}
291
292/** ------------------------------------------------------------------------------------------------------------- *
293 * @brief makeKernelName
294 ** ------------------------------------------------------------------------------------------------------------- */
295std::string PipelineKernel::makeKernelName(const Kernel * const kernel, const unsigned kernelIndex) {
296    std::string tmp;
297    raw_string_ostream out(tmp);
298    out << '@';
299    out << kernel->getName();
300    out << '.';
301    out << kernelIndex;
302    out.flush();
303    return tmp;
304}
305
306/** ------------------------------------------------------------------------------------------------------------- *
307 * @brief makeBufferName
308 ** ------------------------------------------------------------------------------------------------------------- */
309std::string PipelineKernel::makeBufferName(const Kernel * const kernel, const unsigned kernelIndex, const Binding & binding) {
310    std::string tmp;
311    raw_string_ostream out(tmp);
312    out << '@';
313    out << kernel->getName();
314    out << '_';
315    out << binding.getName();
316    out << '.';
317    out << kernelIndex;
318    out.flush();
319    return tmp;
320}
321
322/** ------------------------------------------------------------------------------------------------------------- *
323 * @brief constructor
324 ** ------------------------------------------------------------------------------------------------------------- */
325PipelineKernel::PipelineKernel(std::string && signature, const unsigned numOfThreads,
326                               Kernels && kernels, CallBindings && callBindings,
327                               Bindings && stream_inputs, Bindings && stream_outputs,
328                               Bindings &&scalar_inputs, Bindings && scalar_outputs)
329: Kernel("p" + std::to_string(numOfThreads) + "_" + getStringHash(signature),
330         std::move(stream_inputs), std::move(stream_outputs),
331         std::move(scalar_inputs), std::move(scalar_outputs), {})
332, mNumOfThreads(numOfThreads)
333, mKernels(std::move(kernels))
334, mCallBindings(std::move(callBindings))
335, mSignature(std::move(signature)) {
336
337}
338
339
340PipelineKernel::~PipelineKernel() {
341
342}
343
344}
Note: See TracBrowser for help on using the repository browser.