source: icGREP/icgrep-devel/icgrep/kernels/kernel.cpp @ 5000

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

Redesigned buffer system to allow the pipeline to control selection of the current input and output streams; DoBlock? functions containing lookahead now take multiple input stream arguments. Selection and passing occurs automatically. Some work on Symbol Table.

File size: 15.0 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#include "kernel.h"
7#include <pablo/function.h>
8#include <IDISA/idisa_builder.h>
9#include <kernels/instance.h>
10#include <tuple>
11#include <boost/functional/hash_fwd.hpp>
12#include <unordered_map>
13
14using namespace llvm;
15using namespace pablo;
16
17namespace kernel {
18
19// sets name & sets internal state to the kernel superclass state
20KernelBuilder::KernelBuilder(IDISA::IDISA_Builder * builder, std::string && name, const unsigned defaultBufferSize)
21: iBuilder(builder)
22, mKernelName(name)
23, mDefaultBufferSize(defaultBufferSize)
24, mBitBlockType(builder->getBitBlockType())
25, mBlockNoIndex(0) {
26    assert (mDefaultBufferSize > 0);
27    mBlockNoIndex = iBuilder->getInt32(addInternalState(builder->getInt64Ty(), "BlockNo"));
28}
29
30/** ------------------------------------------------------------------------------------------------------------- *
31 * @brief addInternalState
32 ** ------------------------------------------------------------------------------------------------------------- */
33unsigned KernelBuilder::addInternalState(Type * const type) {
34    assert (type);
35    const unsigned index = mInternalState.size();
36    mInternalState.push_back(type);
37    return index;
38}
39
40unsigned KernelBuilder::addInternalState(llvm::Type * const type, std::string && name) {
41    if (LLVM_UNLIKELY(mInternalStateNameMap.count(name) != 0)) {
42        throw std::runtime_error("Kernel already contains internal state '" + name + "'");
43    }
44    const unsigned index = addInternalState(type);
45    mInternalStateNameMap.emplace(name, iBuilder->getInt32(index));
46    return index;
47}
48
49/** ------------------------------------------------------------------------------------------------------------- *
50 * @brief getInternalState
51 ** ------------------------------------------------------------------------------------------------------------- */
52Value * KernelBuilder::getInternalState(Value * const kernelState, disable_implicit_conversion<Value *> index) {
53    assert (index->getType()->isIntegerTy());   
54    assert (kernelState->getType()->getPointerElementType() == mKernelStateType);
55    return iBuilder->CreateGEP(kernelState, {iBuilder->getInt32(0), index});
56}
57
58Value * KernelBuilder::getInternalState(Value * const kernelState, const std::string & name) {
59    const auto f = mInternalStateNameMap.find(name);
60    if (LLVM_UNLIKELY(f == mInternalStateNameMap.end())) {
61        throw std::runtime_error("Kernel does not contain internal state " + name);
62    }
63    return getInternalState(kernelState, f->second);
64}
65
66/** ------------------------------------------------------------------------------------------------------------- *
67 * @brief setInternalState
68 ** ------------------------------------------------------------------------------------------------------------- */
69void KernelBuilder::setInternalState(Value * const kernelState, const std::string & name, Value * const value) {
70    Value * ptr = getInternalState(kernelState, name);
71    assert (ptr->getType()->getPointerElementType() == value->getType());
72    if (value->getType() == iBuilder->getBitBlockType()) {
73        iBuilder->CreateBlockAlignedStore(value, ptr);
74    } else {
75        iBuilder->CreateStore(value, ptr);
76    }
77}
78
79void KernelBuilder::setInternalState(Value * const kernelState, disable_implicit_conversion<Value *> index, Value * const value) {
80    Value * ptr = getInternalState(kernelState, index);
81    assert (ptr->getType()->getPointerElementType() == value->getType());
82    if (value->getType() == iBuilder->getBitBlockType()) {
83        iBuilder->CreateBlockAlignedStore(value, ptr);
84    } else {
85        iBuilder->CreateStore(value, ptr);
86    }
87}
88
89/** ------------------------------------------------------------------------------------------------------------- *
90 * @brief addInputStream
91 ** ------------------------------------------------------------------------------------------------------------- */
92void KernelBuilder::addInputStream(const unsigned fields, std::string && name) {
93    assert (fields > 0 && !name.empty());
94    mInputStreamName.push_back(name);
95    if (fields == 1) {
96        mInputStream.push_back(mBitBlockType);
97    } else {
98        mInputStream.push_back(ArrayType::get(mBitBlockType, fields));
99    }
100}
101
102void KernelBuilder::addInputStream(const unsigned fields) {
103    addInputStream(fields, std::move(mKernelName + "_InputStream_" + std::to_string(mInputStream.size())));
104}
105
106/** ------------------------------------------------------------------------------------------------------------- *
107 * @brief getInputStream
108 ** ------------------------------------------------------------------------------------------------------------- */
109Value * KernelBuilder::getInputStream(Value * const inputStreamSet, disable_implicit_conversion<Value *> index) {
110    assert ("Parameters cannot be null!" && (inputStreamSet != nullptr && index != nullptr));
111    assert ("Stream index must be an integer!" && index->getType()->isIntegerTy());
112    assert ("Illegal input stream set provided!" && inputStreamSet->getType()->getPointerElementType() == mInputStreamType);
113    if (LLVM_LIKELY(isa<ConstantInt>(index.get()) || getInputStreamType()->isArrayTy())) {
114        return iBuilder->CreateGEP(inputStreamSet, { iBuilder->getInt32(0), index });
115    }
116    #ifndef NDEBUG
117    iBuilder->getModule()->dump();
118    #endif
119    throw std::runtime_error("Cannot access the input stream with a non-constant value unless all input stream types are identical!");
120}
121
122/** ------------------------------------------------------------------------------------------------------------- *
123 * @brief addInputScalar
124 ** ------------------------------------------------------------------------------------------------------------- */
125void KernelBuilder::addInputScalar(Type * const type, std::string && name) {
126    assert (type && !name.empty());
127    mInputScalarName.push_back(name);
128    mInputScalar.push_back(type);
129}
130
131void KernelBuilder::addInputScalar(Type * const type) {
132    addInputScalar(type, std::move(mKernelName + "_InputScalar_" + std::to_string(mInputScalar.size())));
133}
134
135/** ------------------------------------------------------------------------------------------------------------- *
136 * @brief getInputScalar
137 ** ------------------------------------------------------------------------------------------------------------- */
138Value * KernelBuilder::getInputScalar(Value * const inputScalarSet, disable_implicit_conversion<Value *>) {
139    assert (inputScalarSet);
140    throw std::runtime_error("currently not supported!");
141}
142
143/** ------------------------------------------------------------------------------------------------------------- *
144 * @brief addOutputStream
145 ** ------------------------------------------------------------------------------------------------------------- */
146unsigned KernelBuilder::addOutputStream(const unsigned fields) {
147    assert (fields > 0);
148    const unsigned index = mOutputStream.size();
149    mOutputStream.push_back((fields == 1) ? mBitBlockType : ArrayType::get(mBitBlockType, fields));
150    return index;
151}
152
153/** ------------------------------------------------------------------------------------------------------------- *
154 * @brief addOutputScalar
155 ** ------------------------------------------------------------------------------------------------------------- */
156unsigned KernelBuilder::addOutputScalar(Type * const type) {
157    assert (type);
158    const unsigned index = mOutputScalar.size();
159    mOutputScalar.push_back(type);
160    return index;
161}
162
163/** ------------------------------------------------------------------------------------------------------------- *
164 * @brief getOutputStream
165 ** ------------------------------------------------------------------------------------------------------------- */
166Value * KernelBuilder::getOutputStream(Value * const outputStreamSet, disable_implicit_conversion<Value *> index) {
167    assert ("Parameters cannot be null!" && (outputStreamSet != nullptr && index != nullptr));
168    assert ("Stream index must be an integer!" && index->getType()->isIntegerTy());
169    assert ("Illegal output stream set provided!" && outputStreamSet->getType()->getPointerElementType() == getOutputStreamType());
170    if (LLVM_LIKELY(isa<ConstantInt>(index.get()) || getOutputStreamType()->isArrayTy())) {
171        return iBuilder->CreateGEP(outputStreamSet, { iBuilder->getInt32(0), index });
172    }
173    throw std::runtime_error("Cannot access the output stream with a non-constant value unless all output stream types are identical!");
174}
175
176/** ------------------------------------------------------------------------------------------------------------- *
177 * @brief getOutputScalar
178 ** ------------------------------------------------------------------------------------------------------------- */
179Value * KernelBuilder::getOutputScalar(Value * const outputScalarSet, disable_implicit_conversion<Value *> ) {
180    throw std::runtime_error("currently not supported!");
181}
182
183/** ------------------------------------------------------------------------------------------------------------- *
184 * @brief packDataTypes
185 ** ------------------------------------------------------------------------------------------------------------- */
186Type * KernelBuilder::packDataTypes(const std::vector<llvm::Type *> & types) {
187    if (types.empty()) {
188        return nullptr;
189    }
190    for (Type * type : types) {
191        if (type != types.front()) { // use canLosslesslyBitcastInto ?
192            return StructType::get(iBuilder->getContext(), types);
193        }
194    }
195    return ArrayType::get(types.front(), types.size());
196}
197
198/** ------------------------------------------------------------------------------------------------------------- *
199 * @brief prepareFunction
200 ** ------------------------------------------------------------------------------------------------------------- */
201Function * KernelBuilder::prepareFunction(std::vector<unsigned> && inputStreamOffsets) {
202
203    mKernelStateType = StructType::create(iBuilder->getContext(), mInternalState, mKernelName);
204    mInputScalarType = packDataTypes(mInputScalar);
205    mInputStreamType = packDataTypes(mInputStream);
206    mOutputScalarType = packDataTypes(mInputScalar);
207    mOutputStreamType = packDataTypes(mOutputStream);
208    mInputStreamOffsets = inputStreamOffsets;
209
210    std::vector<Type *> params;
211    params.push_back(mKernelStateType->getPointerTo());
212    if (mInputScalarType) {
213        params.push_back(mInputScalarType->getPointerTo());
214    }
215    if (mInputStreamType) {
216        for (unsigned i = 0; i < mInputStreamOffsets.size(); ++i) {
217            params.push_back(mInputStreamType->getPointerTo());
218        }
219    }
220    if (mOutputScalarType) {
221        params.push_back(mOutputScalarType->getPointerTo());
222    }
223    if (mOutputStreamType) {
224        params.push_back(mOutputStreamType->getPointerTo());
225    }
226
227    // A pointer value is captured if the function makes a copy of any part of the pointer that outlives
228    // the call (e.g., stored in a global or, depending on the context, when returned by the function.)
229    // Since this does not occur in either our DoBlock or Constructor, all parameters are marked nocapture.
230
231    FunctionType * const functionType = FunctionType::get(iBuilder->getVoidTy(), params, false);
232    mDoBlock = Function::Create(functionType, GlobalValue::ExternalLinkage, mKernelName + "_DoBlock", iBuilder->getModule());
233    mDoBlock->setCallingConv(CallingConv::C);
234    for (unsigned i = 1; i <= params.size(); ++i) {
235        mDoBlock->setDoesNotCapture(i);
236    }
237    mDoBlock->setDoesNotThrow();
238    Function::arg_iterator args = mDoBlock->arg_begin();
239    mKernelStateParam = args++;
240    mKernelStateParam->setName("this");
241    if (mInputScalarType) {
242        mInputScalarParam = args++;
243        mInputScalarParam->setName("inputScalars");
244    }
245    if (mInputStreamType) {
246        for (const unsigned offset : mInputStreamOffsets) {
247            Value * const inputStreamSet = args++;
248            inputStreamSet->setName("inputStreamSet" + std::to_string(offset));
249            mInputStreamParam.emplace(offset, inputStreamSet);
250        }
251    }
252    if (mOutputScalarType) {
253        mOutputScalarParam = args++;
254        mOutputScalarParam->setName("outputScalars");
255    }
256    if (mOutputStreamType) {
257        mOutputStreamParam = args;
258        mOutputStreamParam->setName("outputStreamSet");
259    }
260    iBuilder->SetInsertPoint(BasicBlock::Create(iBuilder->getContext(), "entry", mDoBlock, 0));
261    return mDoBlock;
262}
263
264/** ------------------------------------------------------------------------------------------------------------- *
265 * @brief finalize
266 ** ------------------------------------------------------------------------------------------------------------- */
267void KernelBuilder::finalize() {
268    // Finish the actual function
269    Value * blockNo = getBlockNo();
270    Value * value = iBuilder->CreateLoad(blockNo);
271    value = iBuilder->CreateAdd(value, ConstantInt::get(value->getType(), 1));
272    iBuilder->CreateStore(value, blockNo);
273    iBuilder->CreateRetVoid();
274
275    mKernelStateParam = nullptr;
276    mInputScalarParam = nullptr;
277    mInputStreamParam.clear();
278    mOutputScalarParam = nullptr;
279    mOutputStreamParam = nullptr;
280    iBuilder->ClearInsertionPoint();
281}
282
283/** ------------------------------------------------------------------------------------------------------------- *
284 * @brief instantiate
285 *
286 * Allocate and zero initialize the memory for this kernel and its output scalars and streams
287 ** ------------------------------------------------------------------------------------------------------------- */
288Instance * KernelBuilder::instantiate(std::pair<Value *, unsigned> && inputStreamSet, const unsigned outputBufferSize) {
289    AllocaInst * const kernelState = iBuilder->CreateAlloca(mKernelStateType);
290    iBuilder->CreateStore(Constant::getNullValue(mKernelStateType), kernelState);
291    AllocaInst * outputScalars = nullptr;
292    if (mOutputScalarType) {
293        outputScalars = iBuilder->CreateAlloca(mOutputScalarType);
294    }
295    AllocaInst * outputStreamSets = nullptr;
296    if (mOutputStreamType) {
297        outputStreamSets = iBuilder->CreateAlloca(mOutputStreamType, iBuilder->getInt32(outputBufferSize));
298    }
299    return new Instance(this, kernelState, nullptr, std::get<0>(inputStreamSet), std::get<1>(inputStreamSet), outputScalars, outputStreamSets, outputBufferSize);
300}
301
302/** ------------------------------------------------------------------------------------------------------------- *
303 * @brief instantiate
304 *
305 * Generate a new instance of this kernel and call the default constructor to initialize it
306 ** ------------------------------------------------------------------------------------------------------------- */
307Instance * KernelBuilder::instantiate(std::initializer_list<llvm::Value *> inputStreams) {   
308    throw std::runtime_error("Not supported!");
309//    AllocaInst * inputStruct = iBuilder->CreateAlloca(mInputStreamType);
310//    unsigned i = 0;
311//    for (Value * inputStream : inputStreams) {
312//        Value * ptr = iBuilder->CreateGEP(inputStruct, { iBuilder->getInt32(0), iBuilder->getInt32(i++)});
313//        iBuilder->CreateStore(iBuilder->CreatePointerCast(inputStream, ptr);
314//    }
315//    return instantiate(std::make_pair(inputStruct, 0));
316}
317
318} // end of namespace kernel
Note: See TracBrowser for help on using the repository browser.