diff --git a/llvm/lib/Transforms/ApproxScheduler/ApproxScheduler.cpp b/llvm/lib/Transforms/ApproxScheduler/ApproxScheduler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..41d8343417a389e9e04692876d8b519e9247bb3c --- /dev/null +++ b/llvm/lib/Transforms/ApproxScheduler/ApproxScheduler.cpp @@ -0,0 +1,259 @@ +//===------------------------ InPlaceDFGAnalysis.cpp ----------------------===// +// +// +// +// The LLVM Compiler Infrastructure +// +// +// +// This file is distributed under the University of Illinois Open Source +// +// License. See LICENSE.TXT for details. +// +// +// +//===----------------------------------------------------------------------===// + +#define DEBUG_TYPE "ApproxScheduler" + +#include "llvm/IR/Module.h" +#include "llvm/Pass.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/InPlaceDFG/InPlaceDFGAnalysis.h" +#include "llvm/SupportVISC/DFG2LLVM.h" +#include "llvm/IR/InstrTypes.h" +#include <unordered_map> +#include <dirent.h> +#include <stdio.h> +#include <sstream> +#include <fstream> + + +using namespace llvm; +using namespace builddfg; +using namespace dfg2llvm; +using namespace inplacedfg; + + +namespace { + +static cl::opt<std::string> category_input("category", cl::desc(" Hardware-agnostic ranking cateogy {log, linear, quad} ")); +static cl::opt<int> rank_input("rank", cl::desc(" Hardware-agostic rank given by autotuner ")); + + +struct ApproxMetrics{ + std::string op_name; + std::string category; + unsigned int rank; // rank given by autotuner + double approx_level; + // Relative L-norm metrics + double relative_l1; + double relative_l2; + double relative_linf; + // Mean L-norm metrics + double mean_l1; + double mean_l2; + double mean_linf; +}; + + + + +struct ApproxSchedulerWrapperPass : public ModulePass { + static char ID; // Pass identification, replacement for typeid + ApproxSchedulerWrapperPass() : ModulePass(ID) {} + +public: + // Functions + bool runOnModule(Module &M); + void getAnalysisUsage(AnalysisUsage &AU) const; +}; + + +// Visitor for Code generation traversal (tree traversal for now) +class ApproxScheduler : public CodeGenTraversal { + +private: + + int rank; // Rank to use for scheduling - ranks added in operand bundles + std::string category; // category = {log, linear, quad} + + // Virtual Functions + void init() {} + void initRuntimeAPI() {} + void codeGen(DFInternalNode* N); + void codeGen(DFLeafNode* N); + bool rankMatches(OperandBundleUse opBundle, std::string category, int rank); + ApproxMetrics* getApproxInfo(Instruction* I); + ApproxMetrics* loadApproxMetrics(OperandBundleUse opBundle); + + // Tracks the id of the tensor op processed + unsigned int currentID; + +public: + // Constructor + ApproxScheduler(Module &_M, BuildDFG &_DFG, std::string category, int rank); + void run(); + +}; + + + +void ApproxSchedulerWrapperPass::getAnalysisUsage(AnalysisUsage &AU) const { + AU.addRequired<BuildDFG>(); + AU.addPreserved<BuildDFG>(); +} + + +bool ApproxSchedulerWrapperPass::runOnModule(Module &M) { + + BuildDFG &DFG = getAnalysis<BuildDFG>(); + + std::string category = category_input.getValue(); + int rank = rank_input.getValue(); + + ApproxScheduler scheduler(M, DFG, category, rank); + scheduler.run(); + + return true; +} + + + +ApproxScheduler::ApproxScheduler(Module &_M, BuildDFG &_DFG, std::string category, int rank) : + CodeGenTraversal(_M, _DFG){ + + this->category = category; + this->rank = rank; +} + + +void ApproxScheduler::run() { + + errs() << "\n NOTE: Approximation-based scheduling transform \n"; + std::vector<DFInternalNode*> Roots = DFG.getRoots(); + + // Iterate over all the DFGs + for (auto rootNode: Roots) { + this->visit(rootNode); + } + + return; +} + + +/*** Analysis of internal node ***/ +void ApproxScheduler::codeGen(DFInternalNode* N) { + DEBUG(errs() << "Analysing Node: " << N->getFuncPointer()->getName() << "\n"); +} + + +ApproxMetrics* ApproxScheduler::loadApproxMetrics(OperandBundleUse opBundle){ + +} + + +bool ApproxScheduler::rankMatches(OperandBundleUse opBundle, std::string category_in, int rank_in){ + + // Extracting value of the 'category' attribute + GlobalVariable* gv = dyn_cast<GlobalVariable>(opBundle.Inputs[1].get()); + ConstantDataArray* constString = dyn_cast<ConstantDataArray>(gv->getInitializer()); + std::string category = std::string(constString->getAsCString().data()); + errs()<<"*category = "<<category<<"\n"; + + int rank = dyn_cast<ConstantInt>(opBundle.Inputs[3].get())->getZExtValue(); + errs()<<"-rank = "<<rank<<"\n"; + + if(category == category_in && rank == rank_in) + return true; + else + return false; + + /*for(unsigned int j = 0; j < bundleUse.Inputs.size(); j++){ + Value* bundleVal = bundleUse.Inputs[j].get(); + errs()<<"Val = "<<*bundleVal<<"\n"; + } */ +} + + +ApproxMetrics* ApproxScheduler::getApproxInfo(Instruction* I){ + + CallSite* CS = new CallSite(I); + if(CS->hasOperandBundles()){ + errs()<<"CallSite has OperandBundles \n"; + + for(unsigned int i = 0; i < CS->getNumOperandBundles(); i++){ + OperandBundleUse bundleUse = CS->getOperandBundleAt(i); + errs()<<"bundleUse -> getTagName() = "<<bundleUse.getTagName()<<"\n"; + + if(rankMatches(bundleUse, category, rank)){ + return loadApproxMetrics(bundleUse); + } + + /*for(unsigned int j = 0; j < bundleUse.Inputs.size(); j++){ + Value* bundleVal = bundleUse.Inputs[j].get(); + errs()<<"Val = "<<*bundleVal<<"\n"; + } + */ + + } + } + else{ + errs()<<"DOES NOT have OperandBundles \n"; + } + + assert("No Bundle Matched the provided rank and Category! \n"); + +} + + +/*** Analysis of leaf node ***/ +void ApproxScheduler::codeGen(DFLeafNode* N) { + DEBUG(errs() << "Analysing Node: " << N->getFuncPointer()->getName() << "\n"); + + // Skip code generation if it is a dummy node + if(N->isDummyNode()) { + DEBUG(errs() << "Skipping dummy node\n"); + return; + } + + // Abort code generation if it is an allocation node + if(N->isAllocationNode()) { + assert(false && "Allocation Node not expected in ApproxHPVM"); + return; + } + + Function *F = N->getFuncPointer(); + Module* M = F->getParent(); + + /**** Reading all tensor operations in the DFG Leaf Node ****/ + for (inst_iterator i = inst_begin(F), e = inst_end(F); i != e; ++i) { + Instruction *I = &(*i); + errs()<<*I<<"\n"; + + if (BuildDFG::isViscIntrinsic(I)) { + IntrinsicInst* II = dyn_cast<IntrinsicInst>(I); + // FIXME: The assumption of only tensor instrinsics is restrictive + assert((II->getCalledFunction()->getName()).startswith("llvm.visc.tensor") + && "Only HPVM tensor intrinsics allowed in ApproxHPVM leaf nodes\n"); + + // NOTE: Get tensorOp name - the scheduling decisions are made per-operation type + std::string intrinsic_id = std::string(II->getCalledFunction()->getName().data()); + ApproxMetrics* approx_metrics = getApproxInfo(I); + + } + + } + +} + +char ApproxSchedulerWrapperPass::ID = 0; +static RegisterPass<ApproxSchedulerWrapperPass> X("approx-scheduler", + "Select target compute unit based on aprroximation metrics", + false /* does not modify the CFG */, + false /* not transformation, just analysis */); + + + +} // End of namespace + diff --git a/llvm/lib/Transforms/ApproxScheduler/CMakeLists.txt b/llvm/lib/Transforms/ApproxScheduler/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..267ad1d859ccac891b956c4e2210e42ce25e4aed --- /dev/null +++ b/llvm/lib/Transforms/ApproxScheduler/CMakeLists.txt @@ -0,0 +1,12 @@ +if(WIN32 OR CYGWIN) + set(LLVM_LINK_COMPONENTS Core Support) +endif() + +add_llvm_loadable_module( ApproxScheduler + ApproxScheduler.cpp + + DEPENDS + intrinsics_gen + PLUGIN_TOOL + opt + ) diff --git a/llvm/lib/Transforms/ApproxScheduler/LLVMBuild.txt b/llvm/lib/Transforms/ApproxScheduler/LLVMBuild.txt new file mode 100644 index 0000000000000000000000000000000000000000..ccd8479c2e538bba60678debe01d05011658a5f4 --- /dev/null +++ b/llvm/lib/Transforms/ApproxScheduler/LLVMBuild.txt @@ -0,0 +1,21 @@ +;===- ./lib/Transforms/LocalMem/LLVMBuild.txt ------------------*- Conf -*--===; +; +; The LLVM Compiler Infrastructure +; +; This file is distributed under the University of Illinois Open Source +; License. See LICENSE.TXT for details. +; +;===------------------------------------------------------------------------===; +; +; This is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[component_0] +type = Library +name = ApproxScheduler +parent = Transforms diff --git a/llvm/lib/Transforms/CMakeLists.txt b/llvm/lib/Transforms/CMakeLists.txt index b7ab50b6f798a0d1818172474c93a8ad5275f00e..a189075fff07ece58c2da01909fde4f77cf8954d 100644 --- a/llvm/lib/Transforms/CMakeLists.txt +++ b/llvm/lib/Transforms/CMakeLists.txt @@ -17,6 +17,7 @@ add_subdirectory(DFG2LLVM_X86) add_subdirectory(LocalMem) add_subdirectory(InPlaceDFG) add_subdirectory(InsertApproxInfo) +add_subdirectory(ApproxScheduler) add_subdirectory(GenVISC) add_subdirectory(MergeDFN) add_subdirectory(FuseHPVMTensorNodes) diff --git a/llvm/test/VISC/DNN_Benchmarks/benchmarks/lenet/bin/approximate.py b/llvm/test/VISC/DNN_Benchmarks/benchmarks/lenet/bin/approximate.py index e07db1c0dfd5c99de5eaa6a992249054f72fab3c..c77d43bf8ff554b77623ed0ea291d4590e52cb3a 100644 --- a/llvm/test/VISC/DNN_Benchmarks/benchmarks/lenet/bin/approximate.py +++ b/llvm/test/VISC/DNN_Benchmarks/benchmarks/lenet/bin/approximate.py @@ -16,17 +16,23 @@ num_flags = 14 # FIXME: Auto-extract the number of tensor ops from the bitcode f error_range = 9 +def change_dir(): + os.chdir(bench_build_dir) + print os.getcwd() -def build_binaries(): +def setup_env(): + os.environ["LD_LIBRARY_PATH"] = os.environ["LD_LIBRARY_PATH"] + ":" + os.environ["LLVM_BUILD_ROOT"] + "/lib" + print os.environ["LD_LIBRARY_PATH"] + +def build_binaries(): subprocess.call("make", shell=True) def run_autotuner(): # Change build directory to benchmark build directory - os.chdir(bench_build_dir) - print os.getcwd() + change_dir() LLVM_SRC_ROOT = os.environ["LLVM_SRC_ROOT"] autotuner_cmd = "python " + LLVM_SRC_ROOT + "projects/hpvm-tensor-rt/opentuner/autotuner/approxhpvm_tuner.py " + \ @@ -44,25 +50,38 @@ def run_autotuner(): def add_approx_info(): - os.chdir(bench_build_dir) - print os.getcwd() - os.environ["LD_LIBRARY_PATH"] = os.environ["LD_LIBRARY_PATH"] + ":" + os.environ["LLVM_BUILD_ROOT"] + "/lib" - print os.environ["LD_LIBRARY_PATH"] - + # Change directory and setup env variables + change_dir() + setup_env() + + subprocess.call("which opt", shell=True) + approxinfo_cmd = "opt -load LLVMBuildDFG.so -load InsertApproxInfo.so -insert-approxinfo --results-dir " + \ result_dir + " " + " " + \ - visc_file_name + " -o " + visc_file_name + "_approx" + visc_file_name + " -S -o " + visc_file_name + "_approx.ll" - print approxinfo_cmd - + print approxinfo_cmd subprocess.call(approxinfo_cmd, shell=True) + + + +def run_scheduler(): + + change_dir() + setup_env() + + sched_cmd = "opt -load LLVMBuildDFG.so -load ApproxScheduler.so -approx-scheduler --category quad --rank 4 " + \ + visc_file_name + "_approx.ll" + " -S -o " + visc_file_name + "_sched_out.ll" + print sched_cmd + subprocess.call(sched_cmd, shell=True) if __name__ == "__main__": #build_binaries() - run_autotuner() - add_approx_info() + #run_autotuner() + #add_approx_info() + run_scheduler()