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

Last change on this file since 5395 was 5395, checked in by cameron, 2 years ago

Uniquify kernel names with buffer types/sizes; update u8u16 to use ParabixDriver?

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