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

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

Continued refactoring work. PabloKernel? now abstract base type with a 'generatePabloMethod' hook to generate Pablo code.

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