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

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

Initial doSegment support; pipeline generation

File size: 9.7 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, iBuilder->getInt32(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->getInt64Ty(), blockNoScalar);
40    for (auto sSet : mStreamSetInputs) {
41        mScalarInputs.push_back(ScalarBinding{PointerType::get(sSet.ssType.getStreamSetBlockType(iBuilder), 0), sSet.ssName + basePtrSuffix});
42        mScalarInputs.push_back(ScalarBinding{iBuilder->getInt64Ty(), sSet.ssName + blkMaskSuffix});
43        //Or possibly add as internal state, with code in init function:  addScalar(iBuilder->getInt64Ty(), sSet.ssName + blkMaskSuffix);
44    }
45    for (auto sSet : mStreamSetOutputs) {
46        mScalarInputs.push_back(ScalarBinding{PointerType::get(sSet.ssType.getStreamSetBlockType(iBuilder), 0), sSet.ssName + basePtrSuffix});
47        mScalarInputs.push_back(ScalarBinding{iBuilder->getInt64Ty(), sSet.ssName + blkMaskSuffix});
48        //Or possibly add as internal state, with code in init function:  addScalar(iBuilder->getInt64Ty(), sSet.ssName + blkMaskSuffix);
49    }
50    for (auto binding : mScalarInputs) {
51        addScalar(binding.scalarType, binding.scalarName);
52    }
53    for (auto binding : mScalarOutputs) {
54        addScalar(binding.scalarType, binding.scalarName);
55    }
56    for (auto binding : mInternalScalars) {
57        addScalar(binding.scalarType, binding.scalarName);
58    }
59    mKernelStateType = StructType::create(getGlobalContext(), mKernelFields, mKernelName);
60}
61
62std::unique_ptr<Module> KernelBuilder::createKernelModule() {
63    Module * saveModule = iBuilder->getModule();
64    IDISA::IDISA_Builder::InsertPoint savePoint = iBuilder->saveIP();
65    std::unique_ptr<Module> theModule = make_unique<Module>(mKernelName + "_" + iBuilder->getBitBlockTypeName(), getGlobalContext());
66    Module * m = theModule.get();
67    iBuilder->setModule(m);
68    generateKernel();
69    iBuilder->setModule(saveModule);
70    iBuilder->restoreIP(savePoint);
71    return theModule;
72}
73
74void KernelBuilder::generateKernel() {
75    IDISA::IDISA_Builder::InsertPoint savePoint = iBuilder->saveIP();
76    Module * m = iBuilder->getModule();
77
78    prepareKernel();  // possibly overriden by the KernelBuilder subtype
79    KernelInterface::addKernelDeclarations(m);
80    generateDoBlockMethod();     // must be implemented by the KernelBuilder subtype
81    generateFinalBlockMethod();  // possibly overriden by the KernelBuilder subtype
82    generateDoSegmentMethod();
83
84    // Implement the accumulator get functions
85    for (auto binding : mScalarOutputs) {
86        auto fnName = mKernelName + accumulator_infix + binding.scalarName;
87        Function * accumFn = m->getFunction(fnName);
88        iBuilder->SetInsertPoint(BasicBlock::Create(iBuilder->getContext(), "get_" + binding.scalarName, accumFn, 0));
89        Value * self = &*(accumFn->arg_begin());
90        Value * ptr = iBuilder->CreateGEP(self, {iBuilder->getInt32(0), getScalarIndex(binding.scalarName)});
91        Value * retVal = iBuilder->CreateLoad(ptr);
92        iBuilder->CreateRet(retVal);
93    }
94    // Implement the initializer function
95    Function * initFunction = m->getFunction(mKernelName + init_suffix);
96    iBuilder->SetInsertPoint(BasicBlock::Create(iBuilder->getContext(), "Init_entry", initFunction, 0));
97   
98    Function::arg_iterator args = initFunction->arg_begin();
99    Value * self = &*(args++);
100    iBuilder->CreateStore(Constant::getNullValue(mKernelStateType), self);
101    for (auto binding : mScalarInputs) {
102        Value * parm = &*(args++);
103        Value * ptr = iBuilder->CreateGEP(self, {iBuilder->getInt32(0), getScalarIndex(binding.scalarName)});
104        iBuilder->CreateStore(parm, ptr);
105    }
106    iBuilder->CreateRetVoid();
107    iBuilder->restoreIP(savePoint);
108}
109
110//  The default finalBlock method simply dispatches to the doBlock routine.
111void KernelBuilder::generateFinalBlockMethod() {
112    IDISA::IDISA_Builder::InsertPoint savePoint = iBuilder->saveIP();
113    Module * m = iBuilder->getModule();
114    Function * doBlockFunction = m->getFunction(mKernelName + doBlock_suffix);
115    Function * finalBlockFunction = m->getFunction(mKernelName + finalBlock_suffix);
116    iBuilder->SetInsertPoint(BasicBlock::Create(iBuilder->getContext(), "fb_entry", finalBlockFunction, 0));
117    // Final Block arguments: self, remaining, then the standard DoBlock args.
118    Function::arg_iterator args = finalBlockFunction->arg_begin();
119    Value * self = &*(args++);
120    /* Skip "remaining" arg */ args++;
121    std::vector<Value *> doBlockArgs = {self};
122    while (args != finalBlockFunction->arg_end()){
123        doBlockArgs.push_back(&*args++);
124    }
125    Value * rslt = iBuilder->CreateCall(doBlockFunction, doBlockArgs);
126    if (mDoBlockReturnType->isVoidTy()) {
127        iBuilder->CreateRetVoid();
128    }
129    else {
130        iBuilder->CreateRet(rslt);
131    }
132    iBuilder->restoreIP(savePoint);
133}
134
135//  The default doSegment method simply dispatches to the doBlock routine.
136void KernelBuilder::generateDoSegmentMethod() {
137    IDISA::IDISA_Builder::InsertPoint savePoint = iBuilder->saveIP();
138    Module * m = iBuilder->getModule();
139    Function * doBlockFunction = m->getFunction(mKernelName + doBlock_suffix);
140    Function * doSegmentFunction = m->getFunction(mKernelName + doSegment_suffix);
141    iBuilder->SetInsertPoint(BasicBlock::Create(iBuilder->getContext(), "entry", doSegmentFunction, 0));
142    BasicBlock * entryBlock = iBuilder->GetInsertBlock();
143    BasicBlock * blockLoop = BasicBlock::Create(iBuilder->getContext(), "blockLoop", doSegmentFunction, 0);
144    BasicBlock * blocksDone = BasicBlock::Create(iBuilder->getContext(), "blocksDone", doSegmentFunction, 0);
145
146   
147    Function::arg_iterator args = doSegmentFunction->arg_begin();
148    Value * self = &*(args++);
149    Value * blocksToDo = &*(args);
150   
151    std::vector<Value *> basePtrs;
152    std::vector<Value *> blockMasks;
153    for (auto sSet : mStreamSetInputs) {
154        basePtrs.push_back(getScalarField(self, sSet.ssName + basePtrSuffix));
155        blockMasks.push_back(getScalarField(self, sSet.ssName + blkMaskSuffix));
156    }
157    for (auto sSet : mStreamSetOutputs) {
158        basePtrs.push_back(getScalarField(self, sSet.ssName + basePtrSuffix));
159        blockMasks.push_back(getScalarField(self, sSet.ssName + blkMaskSuffix));
160    }
161   
162    iBuilder->CreateBr(blockLoop);
163   
164    iBuilder->SetInsertPoint(blockLoop);
165    PHINode * blocksRemaining = iBuilder->CreatePHI(iBuilder->getInt64Ty(), 2, "blocksRemaining");
166    blocksRemaining->addIncoming(blocksToDo, entryBlock);
167   
168    Value * blockNo = getScalarField(self, blockNoScalar);
169    std::vector<Value *> doBlockArgs = {self};
170
171    for (unsigned i = 0; i < basePtrs.size(); i++) {
172        doBlockArgs.push_back(iBuilder->CreateGEP(basePtrs[i], iBuilder->CreateAnd(blockNo, blockMasks[i])));
173    }
174    Value * rslt = iBuilder->CreateCall(doBlockFunction, doBlockArgs);
175    setScalarField(self, blockNoScalar, iBuilder->CreateAdd(blockNo, iBuilder->getInt64(1)));
176    blocksToDo = iBuilder->CreateSub(blocksRemaining, iBuilder->getInt64(1));
177    blocksRemaining->addIncoming(blocksToDo, blockLoop);
178    Value * notDone = iBuilder->CreateICmpUGT(blocksToDo, iBuilder->getInt64(0));
179    iBuilder->CreateCondBr(notDone, blockLoop, blocksDone);
180   
181    iBuilder->SetInsertPoint(blocksDone);
182    if (mDoBlockReturnType->isVoidTy()) {
183        iBuilder->CreateRetVoid();
184    }
185    else {
186        iBuilder->CreateRet(rslt);
187    }
188    iBuilder->restoreIP(savePoint);
189}
190
191
192
193Value * KernelBuilder::getScalarIndex(std::string fieldName) {
194    const auto f = mInternalStateNameMap.find(fieldName);
195    if (LLVM_UNLIKELY(f == mInternalStateNameMap.end())) {
196        throw std::runtime_error("Kernel does not contain internal state: " + fieldName);
197    }
198    return f->second;
199}
200
201Value * KernelBuilder::getScalarField(Value * self, std::string fieldName) {
202    Value * ptr = iBuilder->CreateGEP(self, {iBuilder->getInt32(0), getScalarIndex(fieldName)});
203    return iBuilder->CreateLoad(ptr);
204}
205
206void KernelBuilder::setScalarField(Value * self, std::string fieldName, Value * newFieldVal) {
207    Value * ptr = iBuilder->CreateGEP(self, {iBuilder->getInt32(0), getScalarIndex(fieldName)});
208    iBuilder->CreateStore(newFieldVal, ptr);
209}
210
211
212Value * KernelBuilder::getParameter(Function * f, std::string paramName) {
213    for (Function::arg_iterator argIter = f->arg_begin(), end = f->arg_end(); argIter != end; argIter++) {
214        Value * arg = &*argIter;
215        if (arg->getName() == paramName) return arg;
216    }
217    throw std::runtime_error("Method does not have parameter: " + paramName);
218}
219
220
221
Note: See TracBrowser for help on using the repository browser.