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

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

Refactored source kernels. icgrep from stdin should now be able to handle any file size.

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