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

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

Updates for kernels with variable output length; stdout kernel

File size: 6.3 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    for (auto binding : mScalarInputs) {
40        addScalar(binding.scalarType, binding.scalarName);
41    }
42    for (auto binding : mScalarOutputs) {
43        addScalar(binding.scalarType, binding.scalarName);
44    }
45    for (auto binding : mInternalScalars) {
46        addScalar(binding.scalarType, binding.scalarName);
47    }
48    mKernelStateType = StructType::create(getGlobalContext(), mKernelFields, mKernelName);
49}
50
51std::unique_ptr<Module> KernelBuilder::createKernelModule() {
52    Module * saveModule = iBuilder->getModule();
53    IDISA::IDISA_Builder::InsertPoint savePoint = iBuilder->saveIP();
54    std::unique_ptr<Module> theModule = make_unique<Module>(mKernelName + "_" + iBuilder->getBitBlockTypeName(), getGlobalContext());
55    Module * m = theModule.get();
56    iBuilder->setModule(m);
57    generateKernel();
58    iBuilder->setModule(saveModule);
59    iBuilder->restoreIP(savePoint);
60    return theModule;
61}
62
63void KernelBuilder::generateKernel() {
64    IDISA::IDISA_Builder::InsertPoint savePoint = iBuilder->saveIP();
65    Module * m = iBuilder->getModule();
66
67    prepareKernel();  // possibly overriden by the KernelBuilder subtype
68    KernelInterface::addKernelDeclarations(m);
69    generateDoBlockMethod();     // must be implemented by the KernelBuilder subtype
70    generateFinalBlockMethod();  // possibly overriden by the KernelBuilder subtype
71
72    // Implement the accumulator get functions
73    for (auto binding : mScalarOutputs) {
74        auto fnName = mKernelName + accumulator_infix + binding.scalarName;
75        Function * accumFn = m->getFunction(fnName);
76        iBuilder->SetInsertPoint(BasicBlock::Create(iBuilder->getContext(), "get_" + binding.scalarName, accumFn, 0));
77        Value * self = &*(accumFn->arg_begin());
78        Value * ptr = iBuilder->CreateGEP(self, {iBuilder->getInt32(0), getScalarIndex(binding.scalarName)});
79        Value * retVal = iBuilder->CreateLoad(ptr);
80        iBuilder->CreateRet(retVal);
81    }
82    // Implement the initializer function
83    Function * initFunction = m->getFunction(mKernelName + init_suffix);
84    iBuilder->SetInsertPoint(BasicBlock::Create(iBuilder->getContext(), "Init_entry", initFunction, 0));
85   
86    Function::arg_iterator args = initFunction->arg_begin();
87    Value * self = &*(args++);
88    iBuilder->CreateStore(Constant::getNullValue(mKernelStateType), self);
89    for (auto binding : mScalarInputs) {
90        Value * parm = &*(args++);
91        Value * ptr = iBuilder->CreateGEP(self, {iBuilder->getInt32(0), getScalarIndex(binding.scalarName)});
92        iBuilder->CreateStore(parm, ptr);
93    }
94    iBuilder->CreateRetVoid();
95    iBuilder->restoreIP(savePoint);
96}
97
98//  The default finalBlock method simply dispatches to the doBlock routine.
99void KernelBuilder::generateFinalBlockMethod() {
100    IDISA::IDISA_Builder::InsertPoint savePoint = iBuilder->saveIP();
101    Module * m = iBuilder->getModule();
102    Function * doBlockFunction = m->getFunction(mKernelName + doBlock_suffix);
103    Function * finalBlockFunction = m->getFunction(mKernelName + finalBlock_suffix);
104    iBuilder->SetInsertPoint(BasicBlock::Create(iBuilder->getContext(), "fb_entry", finalBlockFunction, 0));
105    // Final Block arguments: self, remaining, then the standard DoBlock args.
106    Function::arg_iterator args = finalBlockFunction->arg_begin();
107    Value * self = &*(args++);
108    /* Skip "remaining" arg */ args++;
109    std::vector<Value *> doBlockArgs = {self};
110    while (args != finalBlockFunction->arg_end()){
111        doBlockArgs.push_back(&*args++);
112    }
113    Value * rslt = iBuilder->CreateCall(doBlockFunction, doBlockArgs);
114    if (mDoBlockReturnType->isVoidTy()) {
115        iBuilder->CreateRetVoid();
116    }
117    else {
118        iBuilder->CreateRet(rslt);
119    }
120    iBuilder->restoreIP(savePoint);
121}
122
123Value * KernelBuilder::getScalarIndex(std::string fieldName) {
124    const auto f = mInternalStateNameMap.find(fieldName);
125    if (LLVM_UNLIKELY(f == mInternalStateNameMap.end())) {
126        throw std::runtime_error("Kernel does not contain internal state: " + fieldName);
127    }
128    return f->second;
129}
130
131Value * KernelBuilder::getScalarField(Value * self, std::string fieldName) {
132    Value * ptr = iBuilder->CreateGEP(self, {iBuilder->getInt32(0), getScalarIndex(fieldName)});
133    return iBuilder->CreateLoad(ptr);
134}
135
136void KernelBuilder::setScalarField(Value * self, std::string fieldName, Value * newFieldVal) {
137    Value * ptr = iBuilder->CreateGEP(self, {iBuilder->getInt32(0), getScalarIndex(fieldName)});
138    iBuilder->CreateStore(newFieldVal, ptr);
139}
140
141
142Value * KernelBuilder::getParameter(Function * f, std::string paramName) {
143    for (Function::arg_iterator argIter = f->arg_begin(), end = f->arg_end(); argIter != end; argIter++) {
144        Value * arg = &*argIter;
145        if (arg->getName() == paramName) return arg;
146    }
147    throw std::runtime_error("Method does not have parameter: " + paramName);
148}
149
150
151
Note: See TracBrowser for help on using the repository browser.