source: icGREP/icgrep-devel/icgrep/kernels/kernel.h @ 5390

Last change on this file since 5390 was 5390, checked in by nmedfort, 2 years ago

Minor changes in preparation of incorporating a consumed stream set position into select kernels.

File size: 14.1 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#ifndef KERNEL_BUILDER_H
7#define KERNEL_BUILDER_H
8
9#include <string>           // for string
10#include <memory>           // for unique_ptr
11#include "interface.h"      // for KernelInterface
12#include <boost/container/flat_map.hpp>
13#include <IR_Gen/idisa_builder.h>
14
15//namespace llvm { class ConstantInt; }
16#include <llvm/IR/Constants.h>
17namespace llvm { class Function; }
18namespace llvm { class IntegerType; }
19namespace llvm { class LoadInst; }
20namespace llvm { class Type; }
21namespace llvm { class Value; }
22namespace parabix { class StreamSetBuffer; }
23
24namespace kernel {
25   
26class KernelBuilder : public KernelInterface {
27    using NameMap = boost::container::flat_map<std::string, unsigned>;
28public:
29   
30    // Create a module for the kernel, including the kernel state type declaration and
31    // the full implementation of all required methods.     
32    //
33    std::unique_ptr<llvm::Module> createKernelModule(const std::vector<parabix::StreamSetBuffer *> & inputs, const std::vector<parabix::StreamSetBuffer *> & outputs);
34   
35    // Generate the Kernel to the current module (iBuilder->getModule()).
36    void generateKernel(const std::vector<parabix::StreamSetBuffer *> & inputs, const std::vector<parabix::StreamSetBuffer *> & outputs);
37   
38    void createInstance() override;
39
40    llvm::Value * getProducedItemCount(llvm::Value * instance, const std::string & name, llvm::Value * doFinal = nullptr) const final;
41
42    void setProducedItemCount(llvm::Value * instance, const std::string & name, llvm::Value * value) const final;
43
44    llvm::Value * getConsumedItemCount(llvm::Value * instance, const std::string & name) const final;
45
46    void setConsumedItemCount(llvm::Value * instance, const std::string & name, llvm::Value * value) const final;
47
48    llvm::Value * getProcessedItemCount(llvm::Value * instance, const std::string & name) const final;
49
50    void setProcessedItemCount(llvm::Value * instance, const std::string & name, llvm::Value * value) const final;
51
52    virtual void reserveBytes(llvm::Value * instance, const std::string & name, llvm::Value * requested) const;
53
54    bool hasNoTerminateAttribute() { return mNoTerminateAttribute;}
55   
56    llvm::Value * getTerminationSignal(llvm::Value * instance) const final;
57
58    void setTerminationSignal(llvm::Value * instance) const final;
59
60    // Get the value of a scalar field for a given instance.
61    llvm::Value * getScalarField(llvm::Value * instance, const std::string & fieldName) const;
62
63    llvm::Value * getScalarField(llvm::Value * instance, llvm::Value * index) const;
64
65    // Set the value of a scalar field for a given instance.
66    void setScalarField(llvm::Value *instance, const std::string & fieldName, llvm::Value * value) const;
67
68    void setScalarField(llvm::Value * instance, llvm::Value * index, llvm::Value * value) const;
69
70    // Synchronization actions for executing a kernel for a particular logical segment.
71    //
72    // Before the segment is processed, acquireLogicalSegmentNo must be used to load
73    // the segment number of the kernel state to ensure that the previous segment is
74    // complete (by checking that the acquired segment number is equal to the desired segment
75    // number).
76    // After all segment processing actions for the kernel are complete, and any necessary
77    // data has been extracted from the kernel for further pipeline processing, the
78    // segment number must be incremented and stored using releaseLogicalSegmentNo.
79    llvm::LoadInst * acquireLogicalSegmentNo(llvm::Value * instance) const;
80
81    void releaseLogicalSegmentNo(llvm::Value * instance, llvm::Value * newFieldVal) const;
82
83    // Get a parameter by name.
84    llvm::Argument * getParameter(llvm::Function * f, const std::string & name) const;
85
86    inline llvm::IntegerType * getSizeTy() const {
87        return getBuilder()->getSizeTy();
88    }
89
90    inline llvm::Type * getStreamTy(const unsigned FieldWidth = 1) {
91        return getBuilder()->getStreamTy(FieldWidth);
92    }
93   
94    inline llvm::Type * getStreamSetTy(const unsigned NumElements = 1, const unsigned FieldWidth = 1) {
95        return getBuilder()->getStreamSetTy(NumElements, FieldWidth);
96    }
97   
98    virtual ~KernelBuilder() = 0;
99   
100    const std::vector<const parabix::StreamSetBuffer *> & getStreamSetInputBuffers() const { return mStreamSetInputBuffers; }
101
102    const std::vector<const parabix::StreamSetBuffer *> & getStreamSetOutputBuffers() const { return mStreamSetOutputBuffers; }
103
104    llvm::Value * createDoSegmentCall(const std::vector<llvm::Value *> & args) const;
105
106    llvm::Value * createGetAccumulatorCall(llvm::Value * self, const std::string & accumName) const;
107
108protected:
109
110    // Constructor
111    KernelBuilder(IDISA::IDISA_Builder * builder,
112                    std::string && kernelName,
113                    std::vector<Binding> && stream_inputs,
114                    std::vector<Binding> && stream_outputs,
115                    std::vector<Binding> && scalar_parameters,
116                    std::vector<Binding> && scalar_outputs,
117                    std::vector<Binding> && internal_scalars);
118
119    //
120    // Kernel builder subtypes define their logic of kernel construction
121    // in terms of 3 virtual methods for
122    // (a) preparing the Kernel state data structure
123    // (b) defining the logic of the doBlock function, and
124    // (c) defining the logic of the finalBlock function.
125    //
126    // Note: the kernel state data structure must only be finalized after
127    // all scalar fields have been added.   If there are no fields to
128    // be added, the default method for preparing kernel state may be used.
129   
130    void setNoTerminateAttribute(const bool noTerminate = true) {
131        mNoTerminateAttribute = noTerminate;
132    }
133
134    void prepareKernelSignature();
135
136    virtual void prepareKernel();
137
138    virtual void generateInitMethod() { }
139   
140    virtual void generateDoSegmentMethod(llvm::Value * doFinal, const std::vector<llvm::Value *> & producerPos) = 0;
141
142    // Add an additional scalar field to the KernelState struct.
143    // Must occur before any call to addKernelDeclarations or createKernelModule.
144    unsigned addScalar(llvm::Type * type, const std::string & name);
145
146    unsigned addUnnamedScalar(llvm::Type * type);
147
148    unsigned getScalarCount() const;
149
150    // Run-time access of Kernel State and parameters of methods for
151    // use in implementing kernels.
152   
153    // Get the index of a named scalar field within the kernel state struct.
154    llvm::ConstantInt * getScalarIndex(const std::string & name) const;
155
156    // Get the value of a scalar field for a given instance.
157    llvm::Value * getScalarField(const std::string & fieldName) const {
158        return getScalarField(getSelf(), fieldName);
159    }
160
161    llvm::Value * getScalarField(llvm::Value * index) const {
162        return getScalarField(getSelf(), index);
163    }
164
165    // Set the value of a scalar field for a given instance.
166    void setScalarField(const std::string & fieldName, llvm::Value * value) const {
167        return setScalarField(getSelf(), fieldName, value);
168    }
169
170    void setScalarField(llvm::Value * index, llvm::Value * value) const {
171        return setScalarField(getSelf(), index, value);
172    }
173
174    llvm::Value * getInputStreamBlockPtr(const std::string & name, llvm::Value * streamIndex) const;
175
176    llvm::Value * loadInputStreamBlock(const std::string & name, llvm::Value * streamIndex) const;
177   
178    llvm::Value * getInputStreamPackPtr(const std::string & name, llvm::Value * streamIndex, llvm::Value * packIndex) const;
179   
180    llvm::Value * loadInputStreamPack(const std::string & name, llvm::Value * streamIndex, llvm::Value * packIndex) const;
181   
182    llvm::Value * getInputStreamSetCount(const std::string & name) const;
183
184    llvm::Value * getOutputStreamBlockPtr(const std::string & name, llvm::Value * streamIndex) const;
185   
186    void storeOutputStreamBlock(const std::string & name, llvm::Value * streamIndex, llvm::Value * toStore) const;
187   
188    llvm::Value * getOutputStreamPackPtr(const std::string & name, llvm::Value * streamIndex, llvm::Value * packIndex) const;
189   
190    void storeOutputStreamPack(const std::string & name, llvm::Value * streamIndex, llvm::Value * packIndex, llvm::Value * toStore) const;
191
192    llvm::Value * getOutputStreamSetCount(const std::string & name) const;
193
194    llvm::Value * getAdjustedInputStreamBlockPtr(llvm::Value * blockAdjustment, const std::string & name, llvm::Value * streamIndex) const;
195
196    llvm::Value * getRawInputPointer(const std::string & name, llvm::Value * streamIndex, llvm::Value * absolutePosition) const;
197
198    llvm::Value * getRawOutputPointer(const std::string & name, llvm::Value * streamIndex, llvm::Value * absolutePosition) const;
199
200    void reserveBytes(const std::string & name, llvm::Value * requested) const {
201        reserveBytes(getSelf(), name, requested);
202    }
203
204    llvm::Value * getScalarFieldPtr(const std::string & name) const {
205        return getScalarFieldPtr(getSelf(), name);
206    }
207
208    llvm::Value * getScalarFieldPtr(llvm::Value * index) const {
209        return getScalarFieldPtr(getSelf(), index);
210    }
211
212    inline llvm::Value * getProducedItemCount(const std::string & name) const {
213        return getProducedItemCount(getSelf(), name);
214    }
215
216    inline void setProducedItemCount(const std::string & name, llvm::Value * value) const {
217        setProducedItemCount(getSelf(), name, value);
218    }
219
220    inline llvm::Value * getConsumedItemCount(const std::string & name) const {
221        return getConsumedItemCount(getSelf(), name);
222    }
223
224    inline void setConsumedItemCount(const std::string & name, llvm::Value * value) const {
225        setConsumedItemCount(getSelf(), name, value);
226    }
227
228    inline llvm::Value * getProcessedItemCount(const std::string & name) const {
229        return getProcessedItemCount(getSelf(), name);
230    }
231
232    inline void setProcessedItemCount(const std::string & name, llvm::Value * value) const {
233        setProcessedItemCount(getSelf(), name, value);
234    }
235
236    llvm::Value * getTerminationSignal() const {
237        return getTerminationSignal(getSelf());
238    }
239
240    void setTerminationSignal() const {
241        return setTerminationSignal(getSelf());
242    }
243
244    llvm::Value * getSelf() const {
245        return mSelf;
246    }
247
248    llvm::BasicBlock * CreateBasicBlock(std::string && name) const;
249
250    // Stream set helpers.
251
252    llvm::Value * getStreamSetBufferPtr(const std::string & name) const;
253
254    llvm::Value * getScalarFieldPtr(llvm::Value * instance, const std::string & name) const;
255
256    llvm::Value * getScalarFieldPtr(llvm::Value * instance, llvm::Value * index) const;
257
258    unsigned getStreamSetIndex(const std::string & name) const;
259
260    const parabix::StreamSetBuffer * getInputStreamSetBuffer(const std::string & name) const {
261        const auto index = getStreamSetIndex(name);
262        assert (index < mStreamSetInputBuffers.size());
263        return mStreamSetInputBuffers[index];
264    }
265
266    const parabix::StreamSetBuffer * getOutputStreamSetBuffer(const std::string & name) const {
267        const auto index = getStreamSetIndex(name);
268        assert (index < mStreamSetOutputBuffers.size());
269        return mStreamSetOutputBuffers[index];
270    }
271
272    void callGenerateInitMethod();
273
274    void callGenerateDoSegmentMethod();
275
276private:
277
278    llvm::Value * computeBlockIndex(const std::vector<Binding> & binding, const std::string & name, llvm::Value * itemCount) const;
279
280protected:
281
282    llvm::Value *                                   mSelf;
283    llvm::Function *                                mCurrentMethod;
284
285    std::vector<llvm::Type *>                       mKernelFields;
286    NameMap                                         mKernelMap;
287    NameMap                                         mStreamSetNameMap;
288    std::vector<const parabix::StreamSetBuffer *>   mStreamSetInputBuffers;
289    std::vector<const parabix::StreamSetBuffer *>   mStreamSetOutputBuffers;
290    bool                                            mNoTerminateAttribute;
291
292};
293
294class SegmentOrientedKernel : public KernelBuilder {
295protected:
296
297    SegmentOrientedKernel(IDISA::IDISA_Builder * builder,
298                          std::string && kernelName,
299                          std::vector<Binding> && stream_inputs,
300                          std::vector<Binding> && stream_outputs,
301                          std::vector<Binding> && scalar_parameters,
302                          std::vector<Binding> && scalar_outputs,
303                          std::vector<Binding> && internal_scalars);
304
305    virtual ~SegmentOrientedKernel() { }
306
307};
308
309class BlockOrientedKernel : public KernelBuilder {
310protected:
311
312    void CreateDoBlockMethodCall();
313
314    // Each kernel builder subtype must provide its own logic for generating
315    // doBlock calls.
316    virtual void generateDoBlockMethod() = 0;
317
318    // Each kernel builder subtypre must also specify the logic for processing the
319    // final block of stream data, if there is any special processing required
320    // beyond simply calling the doBlock function.   In the case that the final block
321    // processing may be trivially implemented by dispatching to the doBlock method
322    // without additional preparation, the default generateFinalBlockMethod need
323    // not be overridden.
324
325    virtual void generateFinalBlockMethod(llvm::Value * remainingItems);
326
327    void generateDoSegmentMethod(llvm::Value * doFinal, const std::vector<llvm::Value *> & producerPos) override final;
328
329    BlockOrientedKernel(IDISA::IDISA_Builder * builder,
330                        std::string && kernelName,
331                        std::vector<Binding> && stream_inputs,
332                        std::vector<Binding> && stream_outputs,
333                        std::vector<Binding> && scalar_parameters,
334                        std::vector<Binding> && scalar_outputs,
335                        std::vector<Binding> && internal_scalars);
336
337    virtual ~BlockOrientedKernel() { }
338
339private:
340
341    bool useIndirectBr() const {
342        return iBuilder->supportsIndirectBr();
343    }
344
345    void writeDoBlockMethod();
346
347    void writeFinalBlockMethod(llvm::Value * remainingItems);
348
349private:
350
351    llvm::Function *        mDoBlockMethod;
352    llvm::BasicBlock *      mStrideLoopBody;
353    llvm::IndirectBrInst *  mStrideLoopBranch;
354    llvm::PHINode *         mStrideLoopTarget;
355};
356
357
358}
359#endif
Note: See TracBrowser for help on using the repository browser.