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

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

ParabixDriver/ObjectCache? separate compilation and linking: initial check-in with wc

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