CppADCodeGen  HEAD
A C++ Algorithmic Differentiation Package with Source Code Generation
llvm_base_model_library_processor_impl.hpp
1 #ifndef CPPAD_CG_LLVM_BASE_MODEL_LIBRARY_PROCESSOR_IMPL_INCLUDED
2 #define CPPAD_CG_LLVM_BASE_MODEL_LIBRARY_PROCESSOR_IMPL_INCLUDED
3 /* --------------------------------------------------------------------------
4  * CppADCodeGen: C++ Algorithmic Differentiation with Source Code Generation:
5  * Copyright (C) 2017 Ciengis
6  * Copyright (C) 2018 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>
31 protected:
32  const std::string _version;
33  std::vector<std::string> _includePaths;
34  std::shared_ptr<llvm::LLVMContext> _context; // must 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  std::string version) :
46  LlvmBaseModelLibraryProcessor<Base>(librarySourceGen),
47  _version(std::move(version)) {
48  }
49 
50  virtual ~LlvmBaseModelLibraryProcessorImpl() = default;
51 
55  inline const std::string& getVersion() const {
56  return _version;
57  }
58 
62  inline void setIncludePaths(const std::vector<std::string>& includePaths) {
63  _includePaths = includePaths;
64  }
65 
69  inline const std::vector<std::string>& getIncludePaths() const {
70  return _includePaths;
71  }
72 
77  std::unique_ptr<LlvmModelLibrary<Base>> create() {
78  // backup output format so that it can be restored
79  OStreamConfigRestore coutb(std::cout);
80 
81  _linker.reset(nullptr);
82 
83  this->modelLibraryHelper_->startingJob("", JobTimer::JIT_MODEL_LIBRARY);
84 
85  llvm::InitializeAllTargetMCs();
86  llvm::InitializeAllTargets();
87  llvm::InitializeAllAsmPrinters();
88 
89  _context.reset(new llvm::LLVMContext());
90 
91  const std::map<std::string, ModelCSourceGen<Base>*>& models = this->modelLibraryHelper_->getModels();
92  for (const auto& p : models) {
93  const std::map<std::string, std::string>& modelSources = this->getSources(*p.second);
94  createLlvmModules(modelSources);
95  }
96 
97  const std::map<std::string, std::string>& sources = this->getLibrarySources();
98  createLlvmModules(sources);
99 
100  const std::map<std::string, std::string>& customSource = this->modelLibraryHelper_->getCustomSources();
101  createLlvmModules(customSource);
102 
103  llvm::InitializeNativeTarget();
104 
105  std::unique_ptr<LlvmModelLibrary<Base>> lib(new LlvmModelLibraryImpl<Base>(std::move(_module), _context));
106 
107  this->modelLibraryHelper_->finishedJob();
108 
109  return lib;
110  }
111 
119  std::unique_ptr<LlvmModelLibrary<Base>> create(ClangCompiler<Base>& clang) {
120  using namespace llvm;
121 
122  // backup output format so that it can be restored
123  OStreamConfigRestore coutb(std::cout);
124 
125  _linker.release();
126 
127  std::unique_ptr<LlvmModelLibrary<Base>> lib;
128 
129  this->modelLibraryHelper_->startingJob("", JobTimer::JIT_MODEL_LIBRARY);
130 
131  try {
135  const std::set<std::string>& bcFiles = this->createBitCode(clang, _version);
136 
140  llvm::InitializeAllTargets();
141  llvm::InitializeAllAsmPrinters();
142 
143  _context.reset(new llvm::LLVMContext());
144 
145  std::unique_ptr<Module> linkerModule;
146 
147  for (const std::string& itbc : bcFiles) {
148  // load bitcode file
149 
150  ErrorOr<std::unique_ptr<MemoryBuffer>> buffer = MemoryBuffer::getFile(itbc);
151  if (buffer.get() == nullptr) {
152  throw CGException(buffer.getError().message());
153  }
154 
155  // create the module
156  Expected<std::unique_ptr<Module>> moduleOrError = llvm::parseBitcodeFile(buffer.get()->getMemBufferRef(), *_context.get());
157  if (!moduleOrError) {
158  std::ostringstream error;
159  size_t nError = 0;
160  handleAllErrors(moduleOrError.takeError(), [&](ErrorInfoBase& eib) {
161  if (nError > 0) error << "; ";
162  error << eib.message();
163  nError++;
164  });
165  throw CGException(error.str());
166  }
167 
168  // link modules together
169  if (_linker.get() == nullptr) {
170  linkerModule = std::move(moduleOrError.get());
171  _linker.reset(new llvm::Linker(*linkerModule)); // module not destroyed
172  } else {
173  if (_linker->linkInModule(std::move(moduleOrError.get()))) { // module destroyed
174  throw CGException("Failed to link");
175  }
176  }
177  }
178 
179  llvm::InitializeNativeTarget();
180 
181  // voila
182  lib.reset(new LlvmModelLibraryImpl<Base>(std::move(linkerModule), _context));
183 
184  } catch (...) {
185  clang.cleanup();
186  throw;
187  }
188  clang.cleanup();
189 
190  this->modelLibraryHelper_->finishedJob();
191 
192  return lib;
193  }
194 
195 protected:
196 
197  virtual void createLlvmModules(const std::map<std::string, std::string>& sources) {
198  for (const auto& p : sources) {
199  createLlvmModule(p.first, p.second);
200  }
201  }
202 
203  virtual void createLlvmModule(const std::string& filename,
204  const std::string& source) {
205  using namespace llvm;
206  using namespace clang;
207 
208  ArrayRef<StringRef> paths;
209  llvm::sys::findProgramByName("clang", paths);
210 
211  IntrusiveRefCntPtr<DiagnosticOptions> diagOpts = new DiagnosticOptions();
212  auto* diagClient = new TextDiagnosticPrinter(llvm::errs(), &*diagOpts); // will be owned by diags
213  IntrusiveRefCntPtr<DiagnosticIDs> diagID(new DiagnosticIDs());
214  IntrusiveRefCntPtr<DiagnosticsEngine> diags(new DiagnosticsEngine(diagID, &*diagOpts, diagClient));
215 
216  std::vector<const char*> args {"-Wall", "-x", "c", "string-input"}; // -Wall or -v flag is required to avoid an error inside createInvocationFromCommandLine()
217  std::shared_ptr<CompilerInvocation> invocation(createInvocationFromCommandLine(args, diags));
218  if (invocation == nullptr)
219  throw CGException("Failed to create compiler invocation");
220 
221  //invocation->TargetOpts->Triple = llvm::sys::getDefaultTargetTriple();
222 
223  CompilerInvocation::setLangDefaults(*invocation->getLangOpts(),
224 #if LLVM_VERSION_MAJOR >= 10
225  InputKind(clang::Language::C),
226 #else
227  InputKind::C,
228 #endif
229  llvm::Triple(invocation->TargetOpts->Triple),
230  invocation->getPreprocessorOpts(),
231  LangStandard::lang_unspecified);
232  invocation->getFrontendOpts().DisableFree = false; // make sure we free memory (by default it does not)
233 
234  // Create a compiler instance to handle the actual work.
235  CompilerInstance compiler;
236  compiler.setInvocation(invocation);
237 
238 
239  // Create the compilers actual diagnostics engine.
240  compiler.createDiagnostics(); //compiler.createDiagnostics(argc, const_cast<char**> (argv));
241  if (!compiler.hasDiagnostics())
242  throw CGException("No diagnostics");
243 
244  // Create memory buffer with source text
245  std::unique_ptr<llvm::MemoryBuffer> buffer = llvm::MemoryBuffer::getMemBufferCopy(source, "SIMPLE_BUFFER");
246  if (buffer == nullptr)
247  throw CGException("Failed to create memory buffer");
248 
249  // Remap auxiliary name "string-input" to memory buffer
250  PreprocessorOptions& po = compiler.getInvocation().getPreprocessorOpts();
251  po.addRemappedFile("string-input", buffer.release());
252 
253  HeaderSearchOptions& hso = compiler.getInvocation().getHeaderSearchOpts();
254  std::string iClangHeaders = this->findInternalClangCHeaders(_version, hso.ResourceDir);
255  if(!iClangHeaders.empty()) {
256  hso.AddPath(llvm::StringRef(iClangHeaders), clang::frontend::Angled, false, false);
257  }
258 
259  for (size_t s = 0; s < _includePaths.size(); s++)
260  hso.AddPath(llvm::StringRef(_includePaths[s]), clang::frontend::Angled, false, false);
261 
262  // Create and execute the frontend to generate an LLVM bitcode module.
263  clang::EmitLLVMOnlyAction action(_context.get());
264  if (!compiler.ExecuteAction(action))
265  throw CGException("Failed to emit LLVM bitcode");
266 
267  std::unique_ptr<llvm::Module> module = action.takeModule();
268  if (module == nullptr)
269  throw CGException("No module");
270 
271  if (_linker == nullptr) {
272  _module = std::move(module);
273  _linker.reset(new llvm::Linker(*_module.get()));
274  } else {
275  if (_linker->linkInModule(std::move(module))) {
276  throw CGException("LLVM failed to link module");
277  }
278  }
279 
280  // NO delete module;
281  // NO delete invocation;
282  //llvm::llvm_shutdown();
283  }
284 
285 };
286 
287 } // END cg namespace
288 } // END CppAD namespace
289 
290 #endif
void setIncludePaths(const std::vector< std::string > &includePaths)
STL namespace.
const std::set< std::string > & createBitCode(ClangCompiler< Base > &clang, const std::string &version)
LlvmBaseModelLibraryProcessorImpl(ModelLibraryCSourceGen< Base > &librarySourceGen, std::string version)
std::unique_ptr< LlvmModelLibrary< Base > > create(ClangCompiler< Base > &clang)