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

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

First attempt at inlining all DoBlock? and FinalBlock? functions by using indirect jumps. Disabled for NVPTX until Linda can check whether they're supported by the LLVM NVPTX library.

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