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

Last change on this file since 5351 was 5351, checked in by nmedfort, 3 years ago

Update to BlockOrientedKernel? to move the indirect branch out of the StrideLoopBody? and into StrideLoopDone? to simplify branch prediction.

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