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

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

Potential bug fix for 32-bit. Modified MRemap to check for Linux OS support. Added MMapAdvise to CBuilder.

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