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

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

Continued refactoring work.

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