CppADCodeGen  HEAD
A C++ Algorithmic Differentiation Package with Source Code Generation
llvm_model_library_processor.hpp
1 #ifndef CPPAD_CG_LLVM_MODEL_LIBRARY_PROCESSOR_INCLUDED
2 #define CPPAD_CG_LLVM_MODEL_LIBRARY_PROCESSOR_INCLUDED
3 /* --------------------------------------------------------------------------
4  * CppADCodeGen: C++ Algorithmic Differentiation with Source Code Generation:
5  * Copyright (C) 2016 Ciengis
6  * Copyright (C) 2019 Joao Leal
7  *
8  * CppADCodeGen is distributed under multiple licenses:
9  *
10  * - Eclipse Public License Version 1.0 (EPL1), and
11  * - GNU General Public License Version 3 (GPL3).
12  *
13  * EPL1 terms and conditions can be found in the file "epl-v10.txt", while
14  * terms and conditions for the GPL3 can be found in the file "gpl3.txt".
15  * ----------------------------------------------------------------------------
16  * Author: Joao Leal
17  */
18 
19 #include <cppad/cg/model/llvm/llvm_base_model_library_processor.hpp>
20 
21 namespace CppAD {
22 namespace cg {
23 
29 template<class Base>
30 class LlvmModelLibraryProcessor : public LlvmBaseModelLibraryProcessor<Base> {
31 protected:
32  const std::string _version;
33  std::vector<std::string> _includePaths;
34  std::shared_ptr<llvm::LLVMContext> _context; // should be deleted after _linker and _module (it must come first)
35  std::unique_ptr<llvm::Linker> _linker;
36  std::unique_ptr<llvm::Module> _module;
37 public:
38 
45  LlvmBaseModelLibraryProcessor<Base>(librarySourceGen),
46  _version("3.8") {
47  }
48 
49  virtual ~LlvmModelLibraryProcessor() = default;
50 
54  inline const std::string& getVersion() const {
55  return _version;
56  }
57 
61  inline void setIncludePaths(const std::vector<std::string>& includePaths) {
62  _includePaths = includePaths;
63  }
64 
68  inline const std::vector<std::string>& getIncludePaths() const {
69  return _includePaths;
70  }
71 
76  std::unique_ptr<LlvmModelLibrary<Base>> create() {
77 #if 0
78  llvm::sys::Path clangPath = llvm::sys::Program::FindProgramByName("clang");
79  // Arguments to pass to the clang driver:
80  // clang getinmemory.c -lcurl -v
82  args.push_back(clangPath.c_str());
83  args.push_back(inputPath.c_str());
84  args.push_back("-l");
85  args.push_back("curl");
86  args.push_back("-v"); // verbose
87 
88  // The clang driver needs a DiagnosticsEngine so it can report problems
89  clang::TextDiagnosticPrinter *DiagClient = new clang::TextDiagnosticPrinter(llvm::errs(), clang::DiagnosticOptions());
90  clang::IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagID(new clang::DiagnosticIDs());
91  clang::DiagnosticsEngine Diags(DiagID, DiagClient);
92 
93  // Create the clang driver
94  clang::driver::Driver TheDriver(args[0], llvm::sys::getDefaultTargetTriple(), outputPath, true, Diags);
95 #endif
96  // backup output format so that it can be restored
97  OStreamConfigRestore coutb(std::cout);
98 
99  _linker.reset(nullptr);
100 
101  this->modelLibraryHelper_->startingJob("", JobTimer::JIT_MODEL_LIBRARY);
102 
103  llvm::InitializeAllTargetMCs();
104  llvm::InitializeAllTargets();
105  llvm::InitializeAllAsmPrinters();
106 
107  _context.reset(new llvm::LLVMContext());
108 
109  const std::map<std::string, ModelCSourceGen<Base>*>& models = this->modelLibraryHelper_->getModels();
110  for (const auto& p : models) {
111  const std::map<std::string, std::string>& modelSources = this->getSources(*p.second);
112  createLlvmModules(modelSources);
113  }
114 
115  const std::map<std::string, std::string>& sources = this->getLibrarySources();
116  createLlvmModules(sources);
117 
118  const std::map<std::string, std::string>& customSource = this->modelLibraryHelper_->getCustomSources();
119  createLlvmModules(customSource);
120 
121  llvm::InitializeNativeTarget();
122 
123  std::unique_ptr<LlvmModelLibrary<Base>> lib(new LlvmModelLibrary3_8<Base>(std::move(_module), _context));
124 
125  this->modelLibraryHelper_->finishedJob();
126 
127  return lib;
128  }
129 
135  std::unique_ptr<LlvmModelLibrary<Base>> create(ClangCompiler<Base>& clang) {
136  using namespace llvm;
137 
138  // backup output format so that it can be restored
139  OStreamConfigRestore coutb(std::cout);
140 
141  _linker.release();
142 
143  std::unique_ptr<LlvmModelLibrary<Base>> lib;
144 
145  this->modelLibraryHelper_->startingJob("", JobTimer::JIT_MODEL_LIBRARY);
146 
147  try {
151  const std::set<std::string>& bcFiles = this->createBitCode(clang, "3.8");
152 
156  llvm::InitializeAllTargets();
157  llvm::InitializeAllAsmPrinters();
158 
159  _context.reset(new llvm::LLVMContext());
160 
161  std::unique_ptr<Module> linkerModule;
162 
163  for (const std::string& itbc : bcFiles) {
164  // load bitcode file
165 
166  ErrorOr<std::unique_ptr<MemoryBuffer>> buffer = MemoryBuffer::getFile(itbc);
167  if (buffer.get() == nullptr) {
168  throw CGException(buffer.getError().message());
169  }
170 
171  // create the module
172  ErrorOr<std::unique_ptr<Module>> module = llvm::parseBitcodeFile(buffer.get()->getMemBufferRef(), *_context.get());
173  if(module.get() == nullptr) {
174  throw CGException(module.getError().message());
175  }
176 
177  // link modules together
178  if (_linker.get() == nullptr) {
179  linkerModule.reset(module.get().release());
180  _linker.reset(new llvm::Linker(*linkerModule)); // module not destroyed
181  } else {
182  if (_linker->linkInModule(std::move(module.get()))) { // module destroyed
183  throw CGException("Failed to link");
184  }
185  }
186  }
187 
188  llvm::InitializeNativeTarget();
189 
190  // voila
191  lib.reset(new LlvmModelLibrary3_8<Base>(std::move(linkerModule), _context));
192 
193  } catch (...) {
194  clang.cleanup();
195  throw;
196  }
197  clang.cleanup();
198 
199  this->modelLibraryHelper_->finishedJob();
200 
201  return lib;
202  }
203 
204  static inline std::unique_ptr<LlvmModelLibrary<Base>> create(ModelLibraryCSourceGen<Base>& modelLibraryHelper) {
205  LlvmModelLibraryProcessor<Base> p(modelLibraryHelper);
206  return p.create();
207  }
208 
209 protected:
210 
211  virtual void createLlvmModules(const std::map<std::string, std::string>& sources) {
212  for (const auto& p : sources) {
213  createLlvmModule(p.first, p.second);
214  }
215  }
216 
217  virtual void createLlvmModule(const std::string& filename,
218  const std::string& source) {
219  using namespace llvm;
220  using namespace clang;
221 
222  ArrayRef<StringRef> paths;
223  llvm::sys::findProgramByName("clang", paths);
224 
225  static const char* argv [] = {"program", "-Wall", "-x", "c", "string-input"}; // -Wall or -v flag is required to avoid an error inside createInvocationFromCommandLine()
226  static const int argc = sizeof (argv) / sizeof (argv[0]);
227 
228  IntrusiveRefCntPtr<DiagnosticOptions> diagOpts = new DiagnosticOptions();
229  TextDiagnosticPrinter* diagClient = new TextDiagnosticPrinter(llvm::errs(), &*diagOpts); // will be owned by diags
230  IntrusiveRefCntPtr<DiagnosticIDs> diagID(new DiagnosticIDs());
231  IntrusiveRefCntPtr<DiagnosticsEngine> diags(new DiagnosticsEngine(diagID, &*diagOpts, diagClient));
232 
233  ArrayRef<const char*> args(argv + 1, // skip program name
234  argc - 1);
235  std::unique_ptr<CompilerInvocation> invocation(createInvocationFromCommandLine(args, diags));
236  if (invocation.get() == nullptr)
237  throw CGException("Failed to create compiler invocation");
238  CompilerInvocation::setLangDefaults(*invocation->getLangOpts(), IK_C,
239  LangStandard::lang_unspecified);
240  invocation->getFrontendOpts().DisableFree = false; // make sure we free memory (by default it does not)
241 
242  // Create a compiler instance to handle the actual work.
243  CompilerInstance compiler;
244  compiler.setInvocation(invocation.release());
245 
246 
247  // Create the compilers actual diagnostics engine.
248  compiler.createDiagnostics(); //compiler.createDiagnostics(argc, const_cast<char**> (argv));
249  if (!compiler.hasDiagnostics())
250  throw CGException("No diagnostics");
251 
252  // Create memory buffer with source text
253  std::unique_ptr<llvm::MemoryBuffer> buffer = llvm::MemoryBuffer::getMemBufferCopy(source, "SIMPLE_BUFFER");
254  if (buffer.get() == nullptr)
255  throw CGException("Failed to create memory buffer");
256 
257  // Remap auxiliary name "string-input" to memory buffer
258  PreprocessorOptions& po = compiler.getInvocation().getPreprocessorOpts();
259  po.addRemappedFile("string-input", buffer.release());
260 
261  HeaderSearchOptions& hso = compiler.getInvocation().getHeaderSearchOpts();
262  std::string iClangHeaders = this->findInternalClangCHeaders("3.8", hso.ResourceDir);
263  if(!iClangHeaders.empty()) {
264  hso.AddPath(llvm::StringRef(iClangHeaders), clang::frontend::Angled, false, false);
265  }
266 
267  for (size_t s = 0; s < _includePaths.size(); s++)
268  hso.AddPath(llvm::StringRef(_includePaths[s]), clang::frontend::Angled, false, false);
269 
270  // Create and execute the frontend to generate an LLVM bitcode module.
271  clang::EmitLLVMOnlyAction action(_context.get());
272  if (!compiler.ExecuteAction(action))
273  throw CGException("Failed to emit LLVM bitcode");
274 
275  std::unique_ptr<llvm::Module> module = action.takeModule();
276  if (module.get() == nullptr)
277  throw CGException("No module");
278 
279  if (_linker.get() == nullptr) {
280  _module.reset(module.release());
281  _linker.reset(new llvm::Linker(*_module.get()));
282  } else {
283  if (_linker->linkInModule(std::move(module))) {
284  throw CGException("LLVM failed to link module");
285  }
286  }
287 
288  // NO delete module;
289  // NO delete invocation;
290  //llvm::llvm_shutdown();
291  }
292 
293 };
294 
295 } // END cg namespace
296 } // END CppAD namespace
297 
298 #endif
std::unique_ptr< LlvmModelLibrary< Base > > create()
std::unique_ptr< LlvmModelLibrary< Base > > create(ClangCompiler< Base > &clang)
const std::vector< std::string > & getIncludePaths() const
const std::set< std::string > & createBitCode(ClangCompiler< Base > &clang, const std::string &version)
void setIncludePaths(const std::vector< std::string > &includePaths)
LlvmModelLibraryProcessor(ModelLibraryCSourceGen< Base > &librarySourceGen)