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

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

Updated all projects to use ParabixDriver?. Deprecated original pipeline generation methods. Enabled LLVM optimizations, IR and ASM printing for Kernel modules. Enabled object cache by default. Begun work on moving consumed position information back to producing kernels.

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