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

Last change on this file since 5106 was 5106, checked in by cameron, 3 years ago

Use dynamic size_t determination

File size: 10.9 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 <llvm/IR/Module.h>
8#include <llvm/IR/Type.h>
9#include <llvm/IR/Value.h>
10#include <llvm/Support/raw_ostream.h>
11
12using namespace llvm;
13using namespace kernel;
14
15KernelBuilder::KernelBuilder(IDISA::IDISA_Builder * builder,
16                                 std::string kernelName,
17                                 std::vector<StreamSetBinding> stream_inputs,
18                                 std::vector<StreamSetBinding> stream_outputs,
19                                 std::vector<ScalarBinding> scalar_parameters,
20                                 std::vector<ScalarBinding> scalar_outputs,
21                                 std::vector<ScalarBinding> internal_scalars) :
22    KernelInterface(builder, kernelName, stream_inputs, stream_outputs, scalar_parameters, scalar_outputs, internal_scalars) {}
23
24void KernelBuilder::addScalar(Type * t, std::string scalarName) {
25    if (LLVM_UNLIKELY(mKernelStateType != nullptr)) {
26        throw std::runtime_error("Illegal addition of kernel field after kernel state finalized: " + scalarName);
27    }
28    unsigned index = mKernelFields.size();
29    mKernelFields.push_back(t);
30    mInternalStateNameMap.emplace(scalarName, index);
31}
32
33void KernelBuilder::setDoBlockReturnType(llvm::Type * t) {
34    mDoBlockReturnType = t;
35}
36
37void KernelBuilder::prepareKernel() {
38    if (!mDoBlockReturnType) mDoBlockReturnType = iBuilder->getVoidTy();
39    addScalar(iBuilder->getSizeTy(), blockNoScalar);
40    int streamSetNo = 0;
41    for (auto sSet : mStreamSetInputs) {
42        mScalarInputs.push_back(ScalarBinding{PointerType::get(sSet.ssType.getStreamSetBlockType(), 0), sSet.ssName + basePtrSuffix});
43        mStreamSetNameMap.emplace(sSet.ssName, streamSetNo);
44        streamSetNo++;
45    }
46    for (auto sSet : mStreamSetOutputs) {
47        mScalarInputs.push_back(ScalarBinding{PointerType::get(sSet.ssType.getStreamSetBlockType(), 0), sSet.ssName + basePtrSuffix});
48        mStreamSetNameMap.emplace(sSet.ssName, streamSetNo);
49        streamSetNo++;
50    }
51    for (auto binding : mScalarInputs) {
52        addScalar(binding.scalarType, binding.scalarName);
53    }
54    for (auto binding : mScalarOutputs) {
55        addScalar(binding.scalarType, binding.scalarName);
56    }
57    for (auto binding : mInternalScalars) {
58        addScalar(binding.scalarType, binding.scalarName);
59    }
60    mKernelStateType = StructType::create(getGlobalContext(), mKernelFields, mKernelName);
61}
62
63std::unique_ptr<Module> KernelBuilder::createKernelModule() {
64    Module * saveModule = iBuilder->getModule();
65    IDISA::IDISA_Builder::InsertPoint savePoint = iBuilder->saveIP();
66    std::unique_ptr<Module> theModule = make_unique<Module>(mKernelName + "_" + iBuilder->getBitBlockTypeName(), getGlobalContext());
67    Module * m = theModule.get();
68    iBuilder->setModule(m);
69    generateKernel();
70    iBuilder->setModule(saveModule);
71    iBuilder->restoreIP(savePoint);
72    return theModule;
73}
74
75void KernelBuilder::generateKernel() {
76    IDISA::IDISA_Builder::InsertPoint savePoint = iBuilder->saveIP();
77    Module * m = iBuilder->getModule();
78
79    prepareKernel();  // possibly overriden by the KernelBuilder subtype
80    KernelInterface::addKernelDeclarations(m);
81    generateDoBlockMethod();     // must be implemented by the KernelBuilder subtype
82    generateFinalBlockMethod();  // possibly overriden by the KernelBuilder subtype
83    generateDoSegmentMethod();
84
85    // Implement the accumulator get functions
86    for (auto binding : mScalarOutputs) {
87        auto fnName = mKernelName + accumulator_infix + binding.scalarName;
88        Function * accumFn = m->getFunction(fnName);
89        iBuilder->SetInsertPoint(BasicBlock::Create(iBuilder->getContext(), "get_" + binding.scalarName, accumFn, 0));
90        Value * self = &*(accumFn->arg_begin());
91        Value * ptr = iBuilder->CreateGEP(self, {iBuilder->getInt32(0), getScalarIndex(binding.scalarName)});
92        Value * retVal = iBuilder->CreateLoad(ptr);
93        iBuilder->CreateRet(retVal);
94    }
95    // Implement the initializer function
96    Function * initFunction = m->getFunction(mKernelName + init_suffix);
97    iBuilder->SetInsertPoint(BasicBlock::Create(iBuilder->getContext(), "Init_entry", initFunction, 0));
98   
99    Function::arg_iterator args = initFunction->arg_begin();
100    Value * self = &*(args++);
101    iBuilder->CreateStore(Constant::getNullValue(mKernelStateType), self);
102    for (auto binding : mScalarInputs) {
103        Value * parm = &*(args++);
104        Value * ptr = iBuilder->CreateGEP(self, {iBuilder->getInt32(0), getScalarIndex(binding.scalarName)});
105        iBuilder->CreateStore(parm, ptr);
106    }
107    iBuilder->CreateRetVoid();
108    iBuilder->restoreIP(savePoint);
109}
110
111//  The default finalBlock method simply dispatches to the doBlock routine.
112void KernelBuilder::generateFinalBlockMethod() {
113    IDISA::IDISA_Builder::InsertPoint savePoint = iBuilder->saveIP();
114    Module * m = iBuilder->getModule();
115    Function * doBlockFunction = m->getFunction(mKernelName + doBlock_suffix);
116    Function * finalBlockFunction = m->getFunction(mKernelName + finalBlock_suffix);
117    iBuilder->SetInsertPoint(BasicBlock::Create(iBuilder->getContext(), "fb_entry", finalBlockFunction, 0));
118    // Final Block arguments: self, remaining, then the standard DoBlock args.
119    Function::arg_iterator args = finalBlockFunction->arg_begin();
120    Value * self = &*(args++);
121    /* Skip "remaining" arg */ args++;
122    std::vector<Value *> doBlockArgs = {self};
123    while (args != finalBlockFunction->arg_end()){
124        doBlockArgs.push_back(&*args++);
125    }
126    Value * rslt = iBuilder->CreateCall(doBlockFunction, doBlockArgs);
127    if (mDoBlockReturnType->isVoidTy()) {
128        iBuilder->CreateRetVoid();
129    }
130    else {
131        iBuilder->CreateRet(rslt);
132    }
133    iBuilder->restoreIP(savePoint);
134}
135
136//  The default doSegment method simply dispatches to the doBlock routine.
137void KernelBuilder::generateDoSegmentMethod() {
138    IDISA::IDISA_Builder::InsertPoint savePoint = iBuilder->saveIP();
139    Module * m = iBuilder->getModule();
140    Function * doBlockFunction = m->getFunction(mKernelName + doBlock_suffix);
141    Function * doSegmentFunction = m->getFunction(mKernelName + doSegment_suffix);
142    iBuilder->SetInsertPoint(BasicBlock::Create(iBuilder->getContext(), "entry", doSegmentFunction, 0));
143    BasicBlock * entryBlock = iBuilder->GetInsertBlock();
144    BasicBlock * blockLoop = BasicBlock::Create(iBuilder->getContext(), "blockLoop", doSegmentFunction, 0);
145    BasicBlock * blocksDone = BasicBlock::Create(iBuilder->getContext(), "blocksDone", doSegmentFunction, 0);
146
147   
148    Function::arg_iterator args = doSegmentFunction->arg_begin();
149    Value * self = &*(args++);
150    Value * blocksToDo = &*(args);
151   
152    iBuilder->CreateBr(blockLoop);
153   
154    iBuilder->SetInsertPoint(blockLoop);
155    if (mDoBlockReturnType->isVoidTy()) {
156        PHINode * blocksRemaining = iBuilder->CreatePHI(iBuilder->getSizeTy(), 2, "blocksRemaining");
157        blocksRemaining->addIncoming(blocksToDo, entryBlock);
158       
159        Value * blockNo = getScalarField(self, blockNoScalar);
160       
161        iBuilder->CreateCall(doBlockFunction, {self});
162        setScalarField(self, blockNoScalar, iBuilder->CreateAdd(blockNo, ConstantInt::get(iBuilder->getSizeTy(), 1)));
163        blocksToDo = iBuilder->CreateSub(blocksRemaining, ConstantInt::get(iBuilder->getSizeTy(), 1));
164        blocksRemaining->addIncoming(blocksToDo, blockLoop);
165        Value * notDone = iBuilder->CreateICmpUGT(blocksToDo, ConstantInt::get(iBuilder->getSizeTy(), 0));
166        iBuilder->CreateCondBr(notDone, blockLoop, blocksDone);
167       
168        iBuilder->SetInsertPoint(blocksDone);
169        iBuilder->CreateRetVoid();
170    }
171    else {
172        PHINode * blocksRemaining = iBuilder->CreatePHI(iBuilder->getSizeTy(), 2, "blocksRemaining");
173        blocksRemaining->addIncoming(blocksToDo, entryBlock);
174        PHINode * total = iBuilder->CreatePHI(mDoBlockReturnType, 2, "resultTotal");
175        total->addIncoming(ConstantInt::getNullValue(mDoBlockReturnType), entryBlock);
176
177        Value * blockNo = getScalarField(self, blockNoScalar);
178        std::vector<Value *> doBlockArgs = {self};
179       
180        Value * rslt = iBuilder->CreateCall(doBlockFunction, {self});
181        setScalarField(self, blockNoScalar, iBuilder->CreateAdd(blockNo, ConstantInt::get(iBuilder->getSizeTy(), 1)));
182        blocksToDo = iBuilder->CreateSub(blocksRemaining, ConstantInt::get(iBuilder->getSizeTy(), 1));
183        blocksRemaining->addIncoming(blocksToDo, blockLoop);
184        Value * notDone = iBuilder->CreateICmpUGT(blocksToDo, ConstantInt::get(iBuilder->getSizeTy(), 0));
185        Value * totalSoFar = iBuilder->CreateAdd(total, rslt);
186        total->addIncoming(totalSoFar, blockLoop);
187        iBuilder->CreateCondBr(notDone, blockLoop, blocksDone);
188       
189        iBuilder->SetInsertPoint(blocksDone);
190        iBuilder->CreateRet(rslt);
191    }
192    iBuilder->restoreIP(savePoint);
193}
194
195Value * KernelBuilder::getScalarIndex(std::string fieldName) {
196    const auto f = mInternalStateNameMap.find(fieldName);
197    if (LLVM_UNLIKELY(f == mInternalStateNameMap.end())) {
198        throw std::runtime_error("Kernel does not contain internal state: " + fieldName);
199    }
200    return iBuilder->getInt32(f->second);
201}
202
203Value * KernelBuilder::getScalarField(Value * self, std::string fieldName) {
204    Value * ptr = iBuilder->CreateGEP(self, {iBuilder->getInt32(0), getScalarIndex(fieldName)});
205    return iBuilder->CreateLoad(ptr);
206}
207
208void KernelBuilder::setScalarField(Value * self, std::string fieldName, Value * newFieldVal) {
209    Value * ptr = iBuilder->CreateGEP(self, {iBuilder->getInt32(0), getScalarIndex(fieldName)});
210    iBuilder->CreateStore(newFieldVal, ptr);
211}
212
213
214Value * KernelBuilder::getParameter(Function * f, std::string paramName) {
215    for (Function::arg_iterator argIter = f->arg_begin(), end = f->arg_end(); argIter != end; argIter++) {
216        Value * arg = &*argIter;
217        if (arg->getName() == paramName) return arg;
218    }
219    throw std::runtime_error("Method does not have parameter: " + paramName);
220}
221
222unsigned KernelBuilder::getStreamSetIndex(std::string ssName) {
223    const auto f = mStreamSetNameMap.find(ssName);
224    if (LLVM_UNLIKELY(f == mStreamSetNameMap.end())) {
225        throw std::runtime_error("Kernel does not contain stream set: " + ssName);
226    }
227    return f->second;
228}
229
230Value * KernelBuilder::getStreamSetBasePtr(Value * self, std::string ssName) {
231    return getScalarField(self, ssName + basePtrSuffix);
232}
233
234Value * KernelBuilder::getStreamSetBlockPtr(Value * self, std::string ssName, Value * blockNo) {
235    Value * basePtr = getStreamSetBasePtr(self, ssName);
236    unsigned ssIndex = getStreamSetIndex(ssName);
237    if (ssIndex < mStreamSetInputs.size()) {
238        return mStreamSetInputs[ssIndex].ssType.getStreamSetBlockPointer(basePtr, blockNo);
239    }
240    else {
241        return mStreamSetOutputs[ssIndex - mStreamSetInputs.size()].ssType.getStreamSetBlockPointer(basePtr, blockNo);
242    }
243}
244
245
246
247
Note: See TracBrowser for help on using the repository browser.