Repository: IITH-Compilers/IR2Vec (language: python)

Issue:
Expose IR2Vec as library



Discussion / hints:

Fix patch:
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index 315e293e..44a6faed 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -36,4 +36,6 @@ jobs:
     - uses: actions/upload-artifact@v2
       with:
         name: ir2vec
-        path: build/bin/ir2vec
+        path: |
+          build/bin
+          build/lib
diff --git a/README.md b/README.md
index 766cab44..5d9f6b27 100644
--- a/README.md
+++ b/README.md
@@ -14,9 +14,11 @@ Please see [here](https://compilers.cse.iith.ac.in/projects/ir2vec/) for more de
 
 ## Table Of Contents
 * [Requirements](#requirements)
-* [Binaries](#binaries---artifacts)
+* [Binaries and Libraries - Artifacts](#binaries-and-libraries---artifacts)
 * [Building from Source](#building-from-source)
-* [How to generate IR2Vec program representations?](#how-to-generate-ir2vec-program-representations)
+* [Generating program representations](#generating-program-representations)
+   * [Using Binary](#using-binary)
+   * [Using Libraries](#using-libraries)
 * [Experiments](#experiments)
 * [Citation](#citation)
 * [Contributions](#contributions)
@@ -36,8 +38,8 @@ Please see [here](https://compilers.cse.iith.ac.in/projects/ir2vec/) for more de
 
 (Experiments are done on an Ubuntu 18.04 machine)
 
-## Binaries - Artifacts
-Binaries are autogenerated for every relevant checkin using GitHub Actions. Such generated artifacts are tagged along with the successful runs of `Publish` workflow and can be found [here](https://github.com/IITH-Compilers/IR2Vec/actions?query=workflow%3APublish).
+## Binaries and Libraries - Artifacts
+Binaries and Libraries (.a and .so) are autogenerated for every relevant checkin using GitHub Actions. Such generated artifacts are tagged along with the successful runs of `Publish` workflow and can be found [here](https://github.com/IITH-Compilers/IR2Vec/actions?query=workflow%3APublish).
 
 ## Building from source
 1. `mkdir build && cd build`
@@ -48,17 +50,20 @@ Binaries are autogenerated for every relevant checkin using GitHub Actions. Such
     2. `mkdir eigen-build && cd eigen-build`
     3. `cmake ../eigen-3.3.7 && make`
     4. `cd ../`
-3. `cmake -DLT_LLVM_INSTALL_DIR=<path_to_LLVM_build_dir> -DEigen3_DIR=<path_to_eigen_build_dir> ../src`
-4. `make`
+3. `cmake -DLT_LLVM_INSTALL_DIR=<path_to_LLVM_build_dir> -DEigen3_DIR=<path_to_eigen_build_dir> [-DCMAKE_INSTALL_PREFIX=<install_dir>] ../src`
+4. `make [&& make install]`
 
-This process would generate `ir2vec` binary under `build/bin` directory.
+This process would generate `ir2vec` binary under `build/bin` directory, `libIR2Vec.a` and `libIR2Vec.so` under `build/lib` directory.
 
 Generating embeddings for some programs may need more stack space, so set `ulimit -s unlimited` in each session or, you can add this command in `.bashrc`.
 
 To ensure the correctness, run `make verify-all`
 
-## How to generate IR2Vec program representations?
+## Generating program representations
+`IR2Vec` can be used either as a stand alone tool using binary, or can be integrated with any third party tools using libraries. Please see below for the usage
+instructions.
 
+### Using Binary
 > ir2vec -\<mode\> -vocab \<seedEmbedding-file-path\> -o \<output-file\> -level \<p|f\> -class \<class-number\> \<input-ll-file\>
 
 #### Command-Line options
@@ -91,6 +96,25 @@ Please use `--help` for further details.
 #### Symbolic Embeddings
  * `` ir2vec -sym -vocab vocabulary/seedEmbeddingVocab-300-llvm10.txt -o <output_file> -level <p|f> -class <class-number>  <input_ll_file>``
 
+## Using Libraries
+The libraries can be installed by passing the installation location to the `CMAKE_INSTALL_PREFIX` flag during `cmake` followed by `make install`.
+The interfaces are available in [`IR2Vec.h`](./src/include/IR2Vec.h). External projects that would like to use `IR2Vec` can access the functionality
+using these exposed interfaces on including `IR2Vec.h` from the installed location after linking statically or dynamically.
+
+* If the project does not use LLVM, LLVM dependencies have to be linked and included separately.
+* Please ensure that the IR2Vec libraries are compiled with compatible LLVM.
+   * If you are getting errors, please recompile IR2Vec by passing the current LLVM install directory path to `LT_LLVM_INSTALL_DIR` during cmake.
+
+The following template can be used to link IR2vec libraries on a cmake based project.
+
+```cmake
+set(IR2VEC_INSTALL_DIR "" CACHE PATH "IR2Vec installation directory")
+include_directories("${IR2VEC_INSTALL_DIR}/include")
+target_link_libraries(<your_executable_or_library> PUBLIC ${IR2VEC_INSTALL_DIR}/lib/<libIR2Vec.a or libIR2Vec.so>)
+```
+
+And then pass the location of IR2Vec's install prefix to `DIR2VEC_INSTALL_DIR` during cmake.
+
 ## Experiments
 
 ### Note
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 02219c69..9e3f5603 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,5 +1,8 @@
 cmake_minimum_required(VERSION 3.4.3)
-project(ir2vec  VERSION 0.1.0)
+project(ir2vec VERSION 0.1.0)
+
+set(IR2VEC_LIB "IR2Vec")
+set(IR2VEC_LIB_STATIC "IR2Vec_Static")
 
 set(LT_LLVM_INSTALL_DIR "" CACHE PATH "LLVM installation directory")
 list(APPEND CMAKE_PREFIX_PATH "${LT_LLVM_INSTALL_DIR}/lib/cmake/llvm/")
@@ -14,7 +17,6 @@ message(STATUS "Found Eigen3 in: ${Eigen3_DIR}")
 set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
 
 include_directories(SYSTEM ${LLVM_INCLUDE_DIRS})
-add_definitions(${LLVM_DEFINITIONS})
 
 configure_file (./include/version.h.cmake version.h @ONLY)
 include_directories(./include ${CMAKE_CURRENT_BINARY_DIR})
@@ -23,20 +25,39 @@ set(CMAKE_CXX_STANDARD 14 CACHE STRING "")
 
 # LLVM is normally built without RTTI. Be consistent with that.
 if(NOT LLVM_ENABLE_RTTI)
-          set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
-  endif()
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
+endif()
 
-file(GLOB IR2VEC_SRC "*.cpp")
+set(commonsrc FlowAware.cpp Symbolic.cpp utils.cpp)
+set(libsrc libIR2Vec.cpp ${commonsrc})
+set(binsrc CollectIR.cpp IR2Vec.cpp)
 
 # llvm_map_components_to_libnames(llvm_libs all)
 llvm_map_components_to_libnames(llvm_libs support core irreader analysis)
 
+add_executable(${PROJECT_NAME} ${binsrc})
+target_link_libraries (${PROJECT_NAME} ${llvm_libs} objlib)
+target_include_directories(${PROJECT_NAME} PRIVATE .)
 
-add_executable(${PROJECT_NAME} ${IR2VEC_SRC})
-target_link_libraries (${PROJECT_NAME} ${llvm_libs} Eigen3::Eigen)
+add_library(objlib OBJECT ${libsrc})
+set_property(TARGET objlib PROPERTY POSITION_INDEPENDENT_CODE 1)
+target_link_libraries (objlib Eigen3::Eigen)
 
-add_subdirectory(test-suite)
+add_library(${IR2VEC_LIB} SHARED $<TARGET_OBJECTS:objlib>)
+add_library(${IR2VEC_LIB_STATIC} STATIC $<TARGET_OBJECTS:objlib>)
+set_target_properties(${IR2VEC_LIB} ${IR2VEC_LIB_STATIC} PROPERTIES
+    VERSION ${PROJECT_VERSION}
+    SOVERSION 1
+    PUBLIC_HEADER "./include/IR2Vec.h"
+    OUTPUT_NAME ${IR2VEC_LIB}
+    LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib
+    ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
 
+install(TARGETS ${IR2VEC_LIB} ${IR2VEC_LIB_STATIC}
+    LIBRARY DESTINATION lib
+    PUBLIC_HEADER DESTINATION include)
+
+add_subdirectory(test-suite)
 
 add_custom_target(verify-symbolic
  COMMAND bash ./sanity_check.sh SYM llvm10
diff --git a/src/CollectIR.cpp b/src/CollectIR.cpp
index a81639dd..1863368c 100644
--- a/src/CollectIR.cpp
+++ b/src/CollectIR.cpp
@@ -15,10 +15,11 @@
 
 using namespace llvm;
 
-void CollectIR::generateTriplets() {
+void CollectIR::generateTriplets(std::ostream &out) {
   for (Function &F : M)
     for (BasicBlock &B : F)
       traverseBasicBlock(B);
+  out << res;
 }
 
 void CollectIR::traverseBasicBlock(BasicBlock &B) {
diff --git a/src/FlowAware.cpp b/src/FlowAware.cpp
index fdce65b0..dfacd9fb 100644
--- a/src/FlowAware.cpp
+++ b/src/FlowAware.cpp
@@ -93,7 +93,9 @@ Vector IR2Vec_FA::getValue(std::string key) {
   return vec;
 }
 
-void IR2Vec_FA::generateFlowAwareEncodings() {
+void IR2Vec_FA::generateFlowAwareEncodings(std::ostream &o,
+                                           std::ostream &missCount,
+                                           std::ostream &cyclicCount) {
   collectWriteDefsMap(M);
 
   int noOfFunc = 0;
diff --git a/src/IR2Vec.cpp b/src/IR2Vec.cpp
index 536f15b4..44556dd7 100644
--- a/src/IR2Vec.cpp
+++ b/src/IR2Vec.cpp
@@ -8,12 +8,83 @@
 #include "CollectIR.h"
 #include "FlowAware.h"
 #include "Symbolic.h"
+#include "version.h"
+
+#include "llvm/Support/CommandLine.h"
+
+using namespace llvm;
+using namespace IR2Vec;
+
+cl::OptionCategory category("IR2Vec Options");
+
+cl::opt<bool> cl_sym("sym", cl::Optional,
+                     cl::desc("Generate Symbolic Encodings"), cl::init(false),
+                     cl::cat(category));
+
+cl::opt<bool> cl_fa("fa", cl::Optional,
+                    cl::desc("Generate Flow-Aware Encodings"), cl::init(false),
+                    cl::cat(category));
+
+cl::opt<bool> cl_collectIR(
+    "collectIR", cl::Optional,
+    cl::desc("Generate triplets for training seed embedding vocabulary"),
+    cl::init(false), cl::cat(category));
+
+cl::opt<std::string> cl_vocab("vocab", cl::Optional, cl::init(""),
+                              cl::desc("Use embeddings from file path"),
+                              cl::cat(category));
+
+cl::opt<std::string> cl_iname(cl::Positional, cl::desc("Input file path"),
+                              cl::Required, cl::cat(category));
+
+cl::opt<std::string> cl_oname("o", cl::Required, cl::desc("Output file path"),
+                              cl::cat(category));
+
+cl::opt<char>
+    cl_level("level", cl::Optional, cl::init(0),
+             cl::desc("Level of encoding - p = Program; f = Function"),
+             cl::cat(category));
+
+cl::opt<int> cl_cls("class", cl::Hidden, cl::Optional, cl::init(-1),
+                    cl::desc("Class information"), cl::cat(category));
+
+cl::opt<float> cl_WO("wo", cl::Hidden, cl::Optional, cl::init(1),
+                     cl::desc("Weight of Opcode"), cl::cat(category));
+
+cl::opt<float> cl_WA("wa", cl::Hidden, cl::Optional, cl::init(0.2),
+                     cl::desc("Weight of arguments"), cl::cat(category));
+
+cl::opt<float> cl_WT("wt", cl::Hidden, cl::Optional, cl::init(0.5),
+                     cl::desc("Weight of types"), cl::cat(category));
+
+cl::opt<bool> cl_debug("debug-ir2vec", cl::Optional,
+                       cl::desc("Diagnostics for debugging"), cl::init(false),
+                       cl::cat(category));
+
+void printVersion(raw_ostream &ostream) {
+  ostream << "\033[1;35m"
+          << "IR2Vec Version : " << IR2VEC_VERSION << "\033[0m\n";
+  cl::PrintVersionMessage();
+}
 
 int main(int argc, char **argv) {
   cl::SetVersionPrinter(printVersion);
   cl::HideUnrelatedOptions(category);
   cl::ParseCommandLineOptions(argc, argv);
 
+  fa = cl_fa;
+  sym = cl_sym;
+  collectIR = cl_collectIR;
+  vocab = cl_vocab;
+  iname = cl_iname;
+  oname = cl_oname;
+  level = cl_level;
+  cls = cl_cls;
+  WO = cl_WO;
+  WA = cl_WA;
+  WT = cl_WT;
+  debug = cl_debug;
+
   bool failed = false;
   if (!((sym ^ fa) ^ collectIR)) {
     errs() << "Either of sym, fa or collectIR should be specified\n";
@@ -44,18 +115,26 @@ int main(int argc, char **argv) {
   auto M = getLLVMIR();
 
   if (fa) {
-    IR2Vec_FA FA(M);
-    FA.generateFlowAwareEncodings();
+    IR2Vec_FA FA(*M);
+    std::ofstream o, missCount, cyclicCount;
+    o.open(oname, std::ios_base::app);
+    missCount.open("missCount_" + oname, std::ios_base::app);
+    cyclicCount.open("cyclicCount_" + oname, std::ios_base::app);
+    FA.generateFlowAwareEncodings(o, missCount, cyclicCount);
   }
 
   else if (sym) {
-    IR2Vec_Symbolic SYM(M);
-    SYM.generateSymbolicEncodings();
+    IR2Vec_Symbolic SYM(*M);
+    std::ofstream o;
+    o.open(oname, std::ios_base::app);
+    SYM.generateSymbolicEncodings(o);
   }
 
   else if (collectIR) {
     CollectIR cir(M);
-    cir.generateTriplets();
+    std::ofstream o;
+    o.open(oname, std::ios_base::app);
+    cir.generateTriplets(o);
   }
   return 0;
 }
diff --git a/src/Symbolic.cpp b/src/Symbolic.cpp
index 7ac1340b..9e7b2b52 100644
--- a/src/Symbolic.cpp
+++ b/src/Symbolic.cpp
@@ -20,6 +20,7 @@
 #include <queue>
 
 using namespace llvm;
+using namespace IR2Vec;
 using abi::__cxa_demangle;
 
 Vector IR2Vec_Symbolic::getValue(std::string key) {
@@ -31,7 +32,7 @@ Vector IR2Vec_Symbolic::getValue(std::string key) {
   return vec;
 }
 
-void IR2Vec_Symbolic::generateSymbolicEncodings() {
+void IR2Vec_Symbolic::generateSymbolicEncodings(std::ostream &o) {
   int noOfFunc = 0;
 
   for (auto &f : M) {
diff --git a/src/include/CollectIR.h b/src/include/CollectIR.h
index fc9f662c..028f8537 100644
--- a/src/include/CollectIR.h
+++ b/src/include/CollectIR.h
@@ -7,36 +7,26 @@
 #ifndef __COLLECT_IR__
 #define __COLLECT_IR__
 
-#include "IR2Vec.h"
+#include "utils.h"
 #include "llvm/IR/BasicBlock.h"
 #include "llvm/Pass.h"
-#include "llvm/Support/CommandLine.h"
 #include "llvm/Support/raw_ostream.h"
 #include <fstream>
 #include <map>
 
-using namespace llvm;
-using namespace IR2Vec;
-
 class CollectIR {
 
 private:
   void collectData();
-  std::ofstream out;
   std::string res;
-  Module &M;
+  llvm::Module &M;
 
-  void traverseBasicBlock(BasicBlock &B);
+  void traverseBasicBlock(llvm::BasicBlock &B);
 
 public:
-  CollectIR(std::unique_ptr<llvm::Module> &M) : M{*M} {
-    res = "";
-    out.open(oname, std::ios_base::app);
-  }
-
-  ~CollectIR() { out << res; }
+  CollectIR(std::unique_ptr<llvm::Module> &M) : M{*M} { res = ""; }
 
-  void generateTriplets();
+  void generateTriplets(std::ostream &out);
 };
 
 #endif
diff --git a/src/include/FlowAware.h b/src/include/FlowAware.h
index ffec1049..e1f1e4dc 100644
--- a/src/include/FlowAware.h
+++ b/src/include/FlowAware.h
@@ -7,7 +7,7 @@
 #ifndef __IR2Vec_FA_H__
 #define __IR2Vec_FA_H__
 
-#include "IR2Vec.h"
+#include "utils.h"
 
 #include "llvm/ADT/MapVector.h"
 #include "llvm/Analysis/LoopInfo.h"
@@ -15,64 +15,65 @@
 #include "llvm/IR/Dominators.h"
 #include "llvm/IR/Function.h"
 #include "llvm/Pass.h"
-#include "llvm/Support/CommandLine.h"
 #include "llvm/Support/raw_ostream.h"
 
 #include <fstream>
 
-using namespace llvm;
-using namespace IR2Vec;
-
 class IR2Vec_FA {
 
 private:
-  Module &M;
+  llvm::Module &M;
   std::string res;
-  Vector pgmVector;
+  IR2Vec::Vector pgmVector;
   unsigned dataMissCounter;
   unsigned cyclicCounter;
-  std::ofstream o, missCount, cyclicCount;
 
-  SmallDenseMap<StringRef, unsigned> memWriteOps;
-  SmallDenseMap<const Instruction *, Vector> instVecMap;
-  SmallDenseMap<const Instruction *, bool> livelinessMap;
-  SmallDenseMap<StringRef, unsigned> memAccessOps;
+  llvm::SmallDenseMap<llvm::StringRef, unsigned> memWriteOps;
+  llvm::SmallDenseMap<const llvm::Instruction *, IR2Vec::Vector> instVecMap;
+  llvm::SmallDenseMap<const llvm::Instruction *, bool> livelinessMap;
+  llvm::SmallDenseMap<llvm::StringRef, unsigned> memAccessOps;
 
-  SmallMapVector<Function *, Vector, 16> funcVecMap;
+  llvm::SmallMapVector<llvm::Function *, IR2Vec::Vector, 16> funcVecMap;
 
-  SmallMapVector<const Instruction *, SmallVector<const Instruction *, 10>, 16>
+  llvm::SmallMapVector<const llvm::Instruction *,
+                       llvm::SmallVector<const llvm::Instruction *, 10>, 16>
       writeDefsMap;
 
-  SmallVector<const Instruction *, 20> instSolvedBySolver;
-
-  Vector getValue(std::string key);
-  void collectWriteDefsMap(const Module &M);
-  void getTransitiveUse(const Instruction *root, const Instruction *def,
-                        SmallVector<const Instruction *, 100> &visitedList,
-                        SmallVector<const Instruction *, 10> toAppend = {});
-  SmallVector<const Instruction *, 10> getReachingDefs(const Instruction *,
-                                                       unsigned i);
-
-  void inst2Vec(const Instruction &I, SmallVector<Function *, 15> &funcStack,
-                SmallMapVector<const Instruction *, Vector, 16> &instValMap);
-
-  void bb2Vec(BasicBlock &B, SmallVector<Function *, 15> &funcStack);
-  Vector func2Vec(Function &F, SmallVector<Function *, 15> &funcStack);
-  void transitiveKillAndUpdate(Instruction *I, Vector val, bool avg = false);
-  void killAndUpdate(Instruction *I, Vector val);
-  bool isMemOp(StringRef opcode, unsigned &operand,
-               SmallDenseMap<StringRef, unsigned> map);
+  llvm::SmallVector<const llvm::Instruction *, 20> instSolvedBySolver;
+
+  IR2Vec::Vector getValue(std::string key);
+  void collectWriteDefsMap(const llvm::Module &M);
+  void getTransitiveUse(
+      const llvm::Instruction *root, const llvm::Instruction *def,
+      llvm::SmallVector<const llvm::Instruction *, 100> &visitedList,
+      llvm::SmallVector<const llvm::Instruction *, 10> toAppend = {});
+  llvm::SmallVector<const llvm::Instruction *, 10>
+  getReachingDefs(const llvm::Instruction *, unsigned i);
+
+  void inst2Vec(const llvm::Instruction &I,
+                llvm::SmallVector<llvm::Function *, 15> &funcStack,
+                llvm::SmallMapVector<const llvm::Instruction *, IR2Vec::Vector,
+                                     16> &instValMap);
+
+  void bb2Vec(llvm::BasicBlock &B,
+              llvm::SmallVector<llvm::Function *, 15> &funcStack);
+  IR2Vec::Vector func2Vec(llvm::Function &F,
+                          llvm::SmallVector<llvm::Function *, 15> &funcStack);
+  void transitiveKillAndUpdate(llvm::Instruction *I, IR2Vec::Vector val,
+                               bool avg = false);
+  void killAndUpdate(llvm::Instruction *I, IR2Vec::Vector val);
+  bool isMemOp(llvm::StringRef opcode, unsigned &operand,
+               llvm::SmallDenseMap<llvm::StringRef, unsigned> map);
   std::string splitAndPipeFunctionName(std::string s);
 
   // For Debugging
-  void print(Vector t, unsigned pos) { outs() << t[pos]; }
+  void print(IR2Vec::Vector t, unsigned pos) { llvm::outs() << t[pos]; }
 
 public:
-  IR2Vec_FA(std::unique_ptr<llvm::Module> &M) : M{*M} {
-    pgmVector = Vector(DIM, 0);
+  IR2Vec_FA(llvm::Module &M) : M{M} {
+    pgmVector = IR2Vec::Vector(DIM, 0);
     res = "";
-    o.open(oname, std::ios_base::app);
-    collectDataFromVocab(vocab, opcMap);
+    IR2Vec::collectDataFromVocab(opcMap);
 
     memWriteOps.try_emplace("store", 1);
     memWriteOps.try_emplace("cmpxchg", 0);
@@ -83,14 +84,14 @@ class IR2Vec_FA {
 
     dataMissCounter = 0;
     cyclicCounter = 0;
-    missCount.open("missCount_" + oname, std::ios_base::app);
-    cyclicCount.open("cyclicCount_" + oname, std::ios_base::app);
   }
 
-  void generateFlowAwareEncodings();
-  std::map<std::string, Vector> opcMap;
+  void generateFlowAwareEncodings(std::ostream &o, std::ostream &missCount,
+                                  std::ostream &cyclicCount);
+  std::map<std::string, IR2Vec::Vector> opcMap;
 
-  SmallDenseMap<const Instruction *, Vector> getInstVecMap() {
+  llvm::SmallDenseMap<const llvm::Instruction *, IR2Vec::Vector>
+  getInstVecMap() {
     return instVecMap;
   }
 };
diff --git a/src/include/IR2Vec.h b/src/include/IR2Vec.h
index 92705693..61454474 100644
--- a/src/include/IR2Vec.h
+++ b/src/include/IR2Vec.h
@@ -7,46 +7,15 @@
 #ifndef __IR2Vec__
 #define __IR2Vec__
 
-#include "llvm/ADT/SmallVector.h"
-#include <llvm/IR/Module.h>
-#include <llvm/IRReader/IRReader.h>
-#include <llvm/Support/CommandLine.h>
-#include <llvm/Support/SourceMgr.h>
-#include <llvm/Support/raw_ostream.h>
+#include "llvm/IR/Module.h"
+#include <string>
 
 namespace IR2Vec {
 
-#define IR2VEC_DEBUG(X)                                                        \
-  ({                                                                           \
-    if (debug) {                                                               \
-      X;                                                                       \
-    }                                                                          \
-  })
-
-#define DIM 300
-using Vector = llvm::SmallVector<double, DIM>;
-
-extern llvm::cl::OptionCategory category;
-extern llvm::cl::opt<bool> fa;
-extern llvm::cl::opt<bool> sym;
-extern llvm::cl::opt<bool> collectIR;
-extern llvm::cl::opt<std::string> vocab;
-extern llvm::cl::opt<std::string> iname;
-extern llvm::cl::opt<std::string> oname;
-extern llvm::cl::opt<char> level;
-extern llvm::cl::opt<int> cls;
-extern llvm::cl::opt<float> WO;
-extern llvm::cl::opt<float> WA;
-extern llvm::cl::opt<float> WT;
-extern llvm::cl::opt<bool> debug;
-
-std::unique_ptr<llvm::Module> getLLVMIR();
-void printVersion(llvm::raw_ostream &ostream);
-
-void collectDataFromVocab(std::string vocab,
-                          std::map<std::string, Vector> &opcMap);
-void scaleVector(Vector &vec, float factor);
+enum IR2VecMode { FlowAware, Symbolic };
 
+int generateIR2VecEncodings(llvm::Module &M, IR2VecMode mode, std::string vocab,
+                            char level, int cls = -1, float WO = 1,
+                            float WA = 0.2, float WT = 0.5);
 } // namespace IR2Vec
-
 #endif
diff --git a/src/include/Symbolic.h b/src/include/Symbolic.h
index 7f999276..fde91f39 100644
--- a/src/include/Symbolic.h
+++ b/src/include/Symbolic.h
@@ -7,41 +7,39 @@
 #ifndef __IR2Vec_Symbolic_H__
 #define __IR2Vec_Symbolic_H__
 
-#include "IR2Vec.h"
+#include "utils.h"
+
 #include "llvm/IR/BasicBlock.h"
 #include "llvm/IR/Function.h"
 #include "llvm/Pass.h"
-#include "llvm/Support/CommandLine.h"
 #include "llvm/Support/raw_ostream.h"
 
 #include <fstream>
 #include <map>
-using namespace llvm;
-using namespace IR2Vec;
 
 class IR2Vec_Symbolic {
 
 private:
-  Module &M;
-  Vector pgmVector;
-
-  Vector getValue(std::string key);
-  Vector bb2Vec(BasicBlock &B, SmallVector<Function *, 15> &funcStack);
-  Vector func2Vec(Function &F, SmallVector<Function *, 15> &funcStack);
-  std::ofstream o;
+  llvm::Module &M;
+  IR2Vec::Vector pgmVector;
+
+  IR2Vec::Vector getValue(std::string key);
+  IR2Vec::Vector bb2Vec(llvm::BasicBlock &B,
+                        llvm::SmallVector<llvm::Function *, 15> &funcStack);
+  IR2Vec::Vector func2Vec(llvm::Function &F,
+                          llvm::SmallVector<llvm::Function *, 15> &funcStack);
   std::string res;
-  SmallDenseMap<Function *, Vector> funcVecMap;
-  std::map<std::string, Vector> opcMap;
+  llvm::SmallDenseMap<llvm::Function *, IR2Vec::Vector> funcVecMap;
+  std::map<std::string, IR2Vec::Vector> opcMap;
 
 public:
-  IR2Vec_Symbolic(std::unique_ptr<llvm::Module> &M) : M{*M} {
-    pgmVector = Vector(DIM, 0);
+  IR2Vec_Symbolic(llvm::Module &M) : M{M} {
+    pgmVector = IR2Vec::Vector(DIM, 0);
     res = "";
-    o.open(oname, std::ios_base::app);
-    collectDataFromVocab(vocab, opcMap);
+    IR2Vec::collectDataFromVocab(opcMap);
   }
 
-  void generateSymbolicEncodings();
+  void generateSymbolicEncodings(std::ostream &o);
 };
 
 #endif
diff --git a/src/include/utils.h b/src/include/utils.h
new file mode 100644
index 00000000..f465cc7f
--- /dev/null
+++ b/src/include/utils.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2021, S. VenkataKeerthy, Rohit Aggarwal
+// Department of Computer Science and Engineering, IIT Hyderabad
+//
+// This software is available under the BSD 4-Clause License. Please see LICENSE
+// file in the top-level directory for more details.
+//
+#ifndef __IR2Vec_Utils__
+#define __IR2Vec_Utils__
+
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IRReader/IRReader.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <map>
+
+namespace IR2Vec {
+
+#define IR2VEC_DEBUG(X)                                                        \
+  ({                                                                           \
+    if (IR2Vec::debug) {                                                       \
+      X;                                                                       \
+    }                                                                          \
+  })
+
+#define DIM 300
+using Vector = llvm::SmallVector<double, DIM>;
+
+extern bool fa;
+extern bool sym;
+extern bool collectIR;
+extern std::string vocab;
+extern std::string iname;
+extern std::string oname;
+extern char level;
+extern int cls;
+extern float WO;
+extern float WA;
+extern float WT;
+extern bool debug;
+
+std::unique_ptr<llvm::Module> getLLVMIR();
+void collectDataFromVocab(std::map<std::string, Vector> &opcMap);
+void scaleVector(Vector &vec, float factor);
+} // namespace IR2Vec
+
+#endif
diff --git a/src/libIR2Vec.cpp b/src/libIR2Vec.cpp
new file mode 100644
index 00000000..0cd53aec
--- /dev/null
+++ b/src/libIR2Vec.cpp
@@ -0,0 +1,46 @@
+// Copyright (c) 2021, S. VenkataKeerthy, Rohit Aggarwal
+// Department of Computer Science and Engineering, IIT Hyderabad
+//
+// This software is available under the BSD 4-Clause License. Please see LICENSE
+// file in the top-level directory for more details.
+//
+#include "CollectIR.h"
+#include "FlowAware.h"
+#include "IR2Vec.h"
+#include "Symbolic.h"
+#include "utils.h"
+
+#include "llvm/IR/Module.h"
+
+int IR2Vec::generateIR2VecEncodings(llvm::Module &M, IR2Vec::IR2VecMode mode,
+                                    std::string vocab, char level, int cls,
+                                    float WO, float WA, float WT) {
+  // llvm::cl::HideUnrelatedOptions(category);
+  // llvm::cl::ParseCommandLineOptions(0, nullptr);
+
+  IR2Vec::vocab = vocab;
+  IR2Vec::level = level;
+  IR2Vec::cls = cls;
+  IR2Vec::WO = WO;
+  IR2Vec::WA = WA;
+  IR2Vec::WT = WT;
+  //   std::unique_ptr<llvm::Module> &M = m;
+
+  if (mode == IR2Vec::IR2VecMode::FlowAware) {
+    IR2Vec_FA FA(M);
+    std::ofstream o, missCount, cyclicCount;
+    o.open("oname", std::ios_base::app);
+    missCount.open("missCount_oname", std::ios_base::app);
+    cyclicCount.open("cyclicCount_oname", std::ios_base::app);
+    FA.generateFlowAwareEncodings(o, missCount, cyclicCount);
+  }
+
+  else if (mode == IR2Vec::IR2VecMode::Symbolic) {
+    IR2Vec_Symbolic SYM(M);
+    std::ofstream o;
+    o.open("oname", std::ios_base::app);
+    SYM.generateSymbolicEncodings(o);
+  }
+
+  return 0;
+}
diff --git a/src/utils.cpp b/src/utils.cpp
index 5c49a50d..c8e9ae3c 100644
--- a/src/utils.cpp
+++ b/src/utils.cpp
@@ -4,63 +4,25 @@
 // This software is available under the BSD 4-Clause License. Please see LICENSE
 // file in the top-level directory for more details.
 //
+#include "utils.h"
 #include "IR2Vec.h"
-#include "version.h"
 #include <fstream>
 
 using namespace llvm;
 using namespace IR2Vec;
 
-cl::OptionCategory IR2Vec::category("IR2Vec Options");
-
-cl::opt<bool> IR2Vec::sym("sym", cl::Optional,
-                          cl::desc("Generate Symbolic Encodings"),
-                          cl::init(false), cl::cat(IR2Vec::category));
-
-cl::opt<bool> IR2Vec::fa("fa", cl::Optional,
-                         cl::desc("Generate Flow-Aware Encodings"),
-                         cl::init(false), cl::cat(IR2Vec::category));
-
-cl::opt<bool> IR2Vec::collectIR(
-    "collectIR", cl::Optional,
-    cl::desc("Generate triplets for training seed embedding vocabulary"),
-    cl::init(false), cl::cat(IR2Vec::category));
-
-cl::opt<std::string> IR2Vec::vocab("vocab", cl::Optional, cl::init(""),
-                                   cl::desc("Use embeddings from file path"),
-                                   cl::cat(IR2Vec::category));
-
-cl::opt<std::string> IR2Vec::iname(cl::Positional, cl::desc("Input file path"),
-                                   cl::Required, cl::cat(IR2Vec::category));
-
-cl::opt<std::string> IR2Vec::oname("o", cl::Required,
-                                   cl::desc("Output file path"),
-                                   cl::cat(IR2Vec::category));
-
-cl::opt<char>
-    IR2Vec::level("level", cl::Optional, cl::init(0),
-                  cl::desc("Level of encoding - p = Program; f = Function"),
-                  cl::cat(IR2Vec::category));
-
-cl::opt<int> IR2Vec::cls("class", cl::Hidden, cl::Optional, cl::init(-1),
-                         cl::desc("Class information"),
-                         cl::cat(IR2Vec::category));
-
-cl::opt<float> IR2Vec::WO("wo", cl::Hidden, cl::Optional, cl::init(1),
-                          cl::desc("Weight of Opcode"),
-                          cl::cat(IR2Vec::category));
-
-cl::opt<float> IR2Vec::WA("wa", cl::Hidden, cl::Optional, cl::init(0.2),
-                          cl::desc("Weight of arguments"),
-                          cl::cat(IR2Vec::category));
-
-cl::opt<float> IR2Vec::WT("wt", cl::Hidden, cl::Optional, cl::init(0.5),
-                          cl::desc("Weight of types"),
-                          cl::cat(IR2Vec::category));
-
-cl::opt<bool> IR2Vec::debug("debug-ir2vec", cl::Optional,
-                            cl::desc("Diagnostics for debugging"),
-                            cl::init(false), cl::cat(IR2Vec::category));
+bool IR2Vec::fa;
+bool IR2Vec::sym;
+bool IR2Vec::collectIR;
+std::string IR2Vec::vocab;
+std::string IR2Vec::iname;
+std::string IR2Vec::oname;
+char IR2Vec::level;
+int IR2Vec::cls;
+float IR2Vec::WO;
+float IR2Vec::WA;
+float IR2Vec::WT;
+bool IR2Vec::debug;
 
 std::unique_ptr<Module> IR2Vec::getLLVMIR() {
   SMDiagnostic err;
@@ -74,14 +36,7 @@ std::unique_ptr<Module> IR2Vec::getLLVMIR() {
   return M;
 }
 
-void IR2Vec::printVersion(raw_ostream &ostream) {
-  ostream << "\033[1;35m"
-          << "IR2Vec Version : " << IR2VEC_VERSION << "\033[0m\n";
-  cl::PrintVersionMessage();
-}
-
-void IR2Vec::collectDataFromVocab(std::string vocab,
-                                  std::map<std::string, Vector> &opcMap) {
+void IR2Vec::collectDataFromVocab(std::map<std::string, Vector> &opcMap) {
   errs() << "Reading from " + vocab + "\n";
   std::ifstream i(vocab);
   std::string delimiter = ":";


Test patch:
diff --git a/src/test-suite/sanity_check.sh.cmake b/src/test-suite/sanity_check.sh.cmake
index 6ea7ea48..52bcbb42 100644
--- a/src/test-suite/sanity_check.sh.cmake
+++ b/src/test-suite/sanity_check.sh.cmake
@@ -48,7 +48,7 @@ then
     d=$(diff <(sed -e 's/^ *#[0-9]* *//g' ${ORIG_FILE}) <(sed -e 's/^ *#[0-9]* *//g' ${TEMP}/${VIR_FILE}))
     if [ "$d" == "" ]
     then
-        echo -e "${GREEN}${BOLD}[Test Passed] Vectors of  Oracle and Current version are Indentical.${NC}"
+        echo -e "${GREEN}${BOLD}[Test Passed] Vectors of  Oracle and Current version are Identical.${NC}"
         exit 0
     else
         echo -e "$(tput bold)${RED}[Test Failed] Vectors of  Oracle and Current version are Different.${NC}"