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

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

Continued work on processing stdin input. Partial integration of ParabixDriver? methods into icgrep and editd. Object cache does not currently work for recursive REs.

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