source: icGREP/icgrep-devel/icgrep/kernels/interface.cpp @ 5055

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

Bugfix: swap CreateStore? operands

File size: 11.1 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 "interface.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
15KernelInterface::KernelInterface(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                iBuilder(builder),
23                mKernelName(kernelName),
24                mStreamSetInputs(stream_inputs),
25                mStreamSetOutputs(stream_outputs),
26                mScalarInputs(scalar_parameters),
27                mScalarOutputs(scalar_outputs),
28                mInternalScalars(internal_scalars),
29                mKernelStateType(nullptr) {
30   
31    for (auto binding : scalar_parameters) {
32        addScalar(binding.scalarType, binding.scalarName);
33    }
34    for (auto binding : scalar_outputs) {
35        addScalar(binding.scalarType, binding.scalarName);
36    }
37    for (auto binding : internal_scalars) {
38        addScalar(binding.scalarType, binding.scalarName);
39    }
40}
41
42const std::string init_suffix = "_Init";
43const std::string doBlock_suffix = "_DoBlock";
44const std::string finalBlock_suffix = "_FinalBlock";
45const std::string accumulator_infix = "_get_";
46
47
48void KernelInterface::addScalar(Type * t, std::string scalarName) {
49    if (LLVM_UNLIKELY(mKernelStateType != nullptr)) {
50        throw std::runtime_error("Illegal addition of kernel field after kernel state finalized: " + scalarName);
51    }
52    unsigned index = mKernelFields.size();
53    mKernelFields.push_back(t);
54    mInternalStateNameMap.emplace(scalarName, iBuilder->getInt32(index));
55}
56
57void KernelInterface::finalizeKernelStateType() {
58    mKernelStateType = StructType::create(getGlobalContext(), mKernelFields, mKernelName);
59}
60
61void KernelInterface::addKernelDeclarations(Module * client) {
62    finalizeKernelStateType();
63    Type * selfType = PointerType::getUnqual(mKernelStateType);
64    // Create the accumulator get function prototypes
65    for (auto binding : mScalarOutputs) {
66        FunctionType * accumFnType = FunctionType::get(binding.scalarType, {selfType}, false);
67        std::string fnName = mKernelName + accumulator_infix + binding.scalarName;
68        Function * accumFn = Function::Create(accumFnType, GlobalValue::ExternalLinkage, fnName, client);
69        accumFn->setCallingConv(CallingConv::C);
70        accumFn->setDoesNotThrow();
71        Value * self = &*(accumFn->arg_begin());
72        self->setName("self");       
73    }
74    // Create the initialization function prototype
75
76    std::vector<Type *> initParameters = {selfType};
77    for (auto binding : mScalarInputs) {
78        initParameters.push_back(binding.scalarType);
79    }
80    FunctionType * initFunctionType = FunctionType::get(iBuilder->getVoidTy(), initParameters, false);
81    std::string initFnName = mKernelName + init_suffix;
82    Function * initFn = Function::Create(initFunctionType, GlobalValue::ExternalLinkage, initFnName, client);
83    initFn->setCallingConv(CallingConv::C);
84    initFn->setDoesNotThrow();
85    Function::arg_iterator initArgs = initFn->arg_begin();
86    Value * initArg = &*(initArgs++);
87    initArg->setName("self");
88    for (auto binding : mScalarInputs) {
89        initArg = &*(initArgs++);
90        initArg->setName(binding.scalarName);
91    }
92
93    // Create the doBlock and finalBlock function prototypes
94   
95    std::vector<Type *> doBlockParameters = {selfType};
96    std::vector<Type *> finalBlockParameters = {selfType, iBuilder->getInt64Ty()};
97    for (auto inputSet : mStreamSetInputs) {
98        Type * inputSetParmType = PointerType::getUnqual(inputSet.ssType.getStreamSetBlockType(iBuilder));
99        doBlockParameters.push_back(inputSetParmType);
100        finalBlockParameters.push_back(inputSetParmType);
101    }
102    for (auto outputSet : mStreamSetOutputs) {
103        Type * outputSetParmType = PointerType::getUnqual(outputSet.ssType.getStreamSetBlockType(iBuilder));
104        doBlockParameters.push_back(outputSetParmType);
105        finalBlockParameters.push_back(outputSetParmType);
106    }
107    FunctionType * doBlockFunctionType = FunctionType::get(iBuilder->getVoidTy(), doBlockParameters, false);
108    std::string doBlockName = mKernelName + doBlock_suffix;
109    Function * doBlockFn = Function::Create(doBlockFunctionType, GlobalValue::ExternalLinkage, doBlockName, client);
110    doBlockFn->setCallingConv(CallingConv::C);
111    doBlockFn->setDoesNotThrow();
112    for (int i = 1; i <= doBlockParameters.size(); i++) {
113        doBlockFn->setDoesNotCapture(i);
114    }
115   
116    FunctionType * finalBlockType = FunctionType::get(iBuilder->getVoidTy(), finalBlockParameters, false);
117    std::string finalBlockName = mKernelName + finalBlock_suffix;
118    Function * finalBlockFn = Function::Create(finalBlockType, GlobalValue::ExternalLinkage, finalBlockName, client);
119    finalBlockFn->setCallingConv(CallingConv::C);
120    finalBlockFn->setDoesNotThrow();
121    finalBlockFn->setDoesNotCapture(1);
122    // Parameter #2 is not a pointer; nocapture is irrelevant
123    for (int i = 3; i <= finalBlockParameters.size(); i++) {
124        finalBlockFn->setDoesNotCapture(i);
125    }
126   
127    Function::arg_iterator doBlockArgs = doBlockFn->arg_begin();
128    Function::arg_iterator finalBlockArgs = finalBlockFn->arg_begin();
129    Value * doBlockArg = &*(doBlockArgs++);
130    doBlockArg->setName("self");
131    Value * finalBlockArg = &*(finalBlockArgs++);
132    finalBlockArg->setName("self");
133    finalBlockArg = &*(finalBlockArgs++);
134    finalBlockArg->setName("remainingBytes");
135
136    for (auto inputSet : mStreamSetInputs) {
137        doBlockArg = &*(doBlockArgs++);
138        finalBlockArg = &*(finalBlockArgs++);
139        doBlockArg->setName(inputSet.ssName);
140        finalBlockArg->setName(inputSet.ssName);
141    }
142    for (auto outputSet : mStreamSetOutputs) {
143        doBlockArg = &*(doBlockArgs++);
144        finalBlockArg = &*(finalBlockArgs++);
145        doBlockArg->setName(outputSet.ssName);
146        finalBlockArg->setName(outputSet.ssName);
147    }
148}
149
150
151std::unique_ptr<Module> KernelInterface::createKernelModule() {
152    std::unique_ptr<Module> theModule = make_unique<Module>(mKernelName, getGlobalContext());
153    addKernelDeclarations(theModule.get());
154   
155    // Implement the accumulator get functions
156    for (auto binding : mScalarOutputs) {
157        auto fnName = mKernelName + "_get_" + binding.scalarName;
158        Function * accumFn = theModule->getFunction(fnName);
159        iBuilder->SetInsertPoint(BasicBlock::Create(iBuilder->getContext(), "get_" + binding.scalarName, accumFn, 0));
160        Value * self = &*(accumFn->arg_begin());
161        Value * ptr = iBuilder->CreateGEP(self, {iBuilder->getInt32(0), getScalarIndex(binding.scalarName)});
162        Value * retVal = iBuilder->CreateLoad(ptr);
163        iBuilder->CreateRet(retVal);
164    }
165   
166    // Implement the initializer function
167    Function * initFunction = theModule->getFunction(mKernelName + "_Init");
168    iBuilder->SetInsertPoint(BasicBlock::Create(iBuilder->getContext(), "Init_entry", initFunction, 0));
169   
170    Function::arg_iterator args = initFunction->arg_begin();
171    Value * self = &*(args++);
172    iBuilder->CreateStore(Constant::getNullValue(mKernelStateType), self);
173    for (auto binding : mScalarInputs) {
174        Value * parm = &*(args++);
175        Value * ptr = iBuilder->CreateGEP(self, {iBuilder->getInt32(0), getScalarIndex(binding.scalarName)});
176        iBuilder->CreateStore(parm, ptr);
177    }
178    iBuilder->CreateRetVoid();
179    return theModule;
180}
181
182Value * KernelInterface::getScalarIndex(std::string fieldName) {
183    const auto f = mInternalStateNameMap.find(fieldName);
184    if (LLVM_UNLIKELY(f == mInternalStateNameMap.end())) {
185        throw std::runtime_error("Kernel does not contain internal state: " + fieldName);
186    }
187    return f->second;
188}
189
190
191Value * KernelInterface::getScalarField(Value * self, std::string fieldName) {
192    Value * ptr = iBuilder->CreateGEP(self, {iBuilder->getInt32(0), getScalarIndex(fieldName)});
193    return iBuilder->CreateLoad(ptr);
194}
195
196void KernelInterface::setScalarField(Value * self, std::string fieldName, Value * newFieldVal) {
197    Value * ptr = iBuilder->CreateGEP(self, {iBuilder->getInt32(0), getScalarIndex(fieldName)});
198    iBuilder->CreateStore(newFieldVal, ptr);
199}
200
201
202Value * KernelInterface::getParameter(Function * f, std::string paramName) {
203    for (Function::arg_iterator argIter = f->arg_begin(), end = f->arg_end(); argIter != end; argIter++) {
204        Value * arg = &*argIter;
205        if (arg->getName() == paramName) return arg;
206    }
207    throw std::runtime_error("Method does not have parameter: " + paramName);
208}
209
210
211Value * KernelInterface::createInstance(std::vector<Value *> args) {
212    Value * kernelInstance = iBuilder->CreateAlloca(mKernelStateType);
213    Module * m = iBuilder->getModule();
214    std::vector<Value *> init_args = {kernelInstance};
215    for (auto a : args) {
216        init_args.push_back(a);
217    }
218    std::string initFnName = mKernelName + init_suffix;
219    Function * initMethod = m->getFunction(initFnName);
220    if (!initMethod) {
221        throw std::runtime_error("Cannot find " + initFnName);
222        //Or just zero-initialize???
223        //iBuilder->CreateStore(Constant::getNullValue(mKernelStateType), kernelInstance);
224        //return kernelInstance;
225    }
226    iBuilder->CreateCall(initMethod, init_args);
227    return kernelInstance;
228}
229
230Value * KernelInterface::createDoBlockCall(Value * self, std::vector<Value *> streamSets) {
231    Module * m = iBuilder->getModule();
232    std::string doBlockName = mKernelName + doBlock_suffix;
233    Function * doBlockMethod = m->getFunction(doBlockName);
234    if (!doBlockMethod) {
235        throw std::runtime_error("Cannot find " + doBlockName);
236    }
237    std::vector<Value *> args = {self};
238    for (auto ss : streamSets) {
239        args.push_back(ss);
240    }
241    return iBuilder->CreateCall(doBlockMethod, args);
242}
243
244Value * KernelInterface::createFinalBlockCall(Value * self, Value * remainingBytes, std::vector<Value *> streamSets) {
245    Module * m = iBuilder->getModule();
246    std::string finalBlockName = mKernelName + finalBlock_suffix;
247    Function * finalBlockMethod = m->getFunction(finalBlockName);
248    if (!finalBlockMethod) {
249        throw std::runtime_error("Cannot find " + finalBlockName);
250    }
251    std::vector<Value *> args = {self, remainingBytes};
252    for (auto ss : streamSets) {
253        args.push_back(ss);
254    }
255    return iBuilder->CreateCall(finalBlockMethod, args);
256}
257
258Value * KernelInterface::createGetAccumulatorCall(Value * self, std::string accumName) {
259    Module * m = iBuilder->getModule();
260    std::string fnName = mKernelName + accumulator_infix + accumName;
261    Function * accumMethod = m->getFunction(fnName);
262    if (!accumMethod) {
263        throw std::runtime_error("Cannot find " + fnName);
264    }
265    return iBuilder->CreateCall(accumMethod, {self});
266}
267
268
Note: See TracBrowser for help on using the repository browser.