// Copyright (C) by Josh Blum. See LICENSE.txt for licensing information. #include #include #include #include #include #include #include #include #include //defined in module loader std::string get_gras_runtime_include_path(void); #ifdef HAVE_CLANG #include #include #include #include #include #include #include #endif //HAVE_CLANG #ifdef HAVE_LLVM #include #include #include #include #include #include #include #include #include #include #endif //HAVE_LLVM /*********************************************************************** * Storage for execution engines **********************************************************************/ #ifdef HAVE_LLVM struct ExecutionEngineMonitor { ExecutionEngineMonitor(void) { boost::mutex::scoped_lock l(mutex); } ~ExecutionEngineMonitor(void) { boost::mutex::scoped_lock l(mutex); for (size_t i = 0; i < ees.size(); i++) { ees[i]->runStaticConstructorsDestructors(true); ees[i].reset(); } ees.clear(); } void add(boost::shared_ptr ee) { boost::mutex::scoped_lock l(mutex); ee->runStaticConstructorsDestructors(false); ees.push_back(ee); } llvm::LLVMContext &get_context(void) { return context; } boost::mutex mutex; llvm::LLVMContext context; std::vector > ees; }; static ExecutionEngineMonitor &get_eemon(void) { static ExecutionEngineMonitor eemon; return eemon; } #endif //HAVE_LLVM /*********************************************************************** * Helper function to call a clang compliation -- execs clang **********************************************************************/ #ifdef HAVE_LLVM static llvm::Module *call_clang_exe(const std::string &source_file, const std::vector &flags) { //make up bitcode file path char bitcode_file[L_tmpnam]; if (std::tmpnam(bitcode_file) == NULL) throw std::runtime_error("GRAS compiler: tmp bitcode file path failed"); //begin command setup std::vector cmd; llvm::sys::Path clangPath = llvm::sys::Program::FindProgramByName("clang"); cmd.push_back(clangPath.str()); cmd.push_back("-emit-llvm"); //inject source cmd.push_back("-c"); cmd.push_back("-x"); cmd.push_back("c++"); cmd.push_back(source_file); //inject output cmd.push_back("-o"); cmd.push_back(bitcode_file); //inject args... BOOST_FOREACH(const std::string &flag, flags) { cmd.push_back(flag.c_str()); } //format command string std::string command; BOOST_FOREACH(const std::string &c, cmd) { command += c + " "; } std::cout << " " << command << std::endl; const int ret = system(command.c_str()); if (ret != 0) { throw std::runtime_error("GRAS compiler: error system exec clang"); } //readback bitcode for result std::ifstream bitcode_fstream(bitcode_file); const std::string bitcode((std::istreambuf_iterator(bitcode_fstream)), std::istreambuf_iterator()); //create a memory buffer from the bitcode boost::shared_ptr buffer(llvm::MemoryBuffer::getMemBuffer(bitcode)); //parse the bitcode into a module std::string error; llvm::Module *module = llvm::ParseBitcodeFile(buffer.get(), get_eemon().get_context(), &error); if (not error.empty()) throw std::runtime_error("GRAS compiler: ParseBitcodeFile " + error); return module; } #endif //HAVE_LLVM /*********************************************************************** * Helper function to call a clang compliation -- uses clang API **********************************************************************/ /* #ifdef HAVE_CLANG static llvm::Module *call_clang_api(const std::string &source_file, const std::vector &flags) { //begin command setup std::vector args; //inject source args.push_back("-c"); args.push_back("-x"); args.push_back("c++"); args.push_back(source_file.c_str()); //inject args... BOOST_FOREACH(const std::string &flag, flags) { args.push_back(flag.c_str()); } //http://fdiv.net/2012/08/15/compiling-code-clang-api // The compiler invocation needs a DiagnosticsEngine so it can report problems clang::DiagnosticOptions *DiagOpts = new clang::DiagnosticOptions(); clang::TextDiagnosticPrinter *DiagClient = new clang::TextDiagnosticPrinter(llvm::errs(), DiagOpts); llvm::IntrusiveRefCntPtr DiagID(new clang::DiagnosticIDs()); clang::DiagnosticsEngine Diags(DiagID, DiagOpts, DiagClient); // Create the compiler invocation clang::LangOptions LangOpts; llvm::OwningPtr CI(new clang::CompilerInvocation()); //CI->setLangDefaults(LangOpts, clang::IK_CXX, clang::LangStandard::lang_gnucxx98); clang::CompilerInvocation::CreateFromArgs(*CI, &args[0], &args[0] + args.size(), Diags); // Print the argument list std::cout << "clang "; BOOST_FOREACH(const char *arg, args) { std::cout << arg << " "; } std::cout << std::endl; // Create the compiler instance clang::CompilerInstance Clang; Clang.setInvocation(CI.take()); // Get ready to report problems Clang.createDiagnostics(args.size(), &args[0]); if (not Clang.hasDiagnostics()) throw std::runtime_error("ClangBlock::createDiagnostics"); // Create an action and make the compiler instance carry it out clang::CodeGenAction *Act = new clang::EmitLLVMOnlyAction(); if (not Clang.ExecuteAction(*Act)) throw std::runtime_error("ClangBlock::EmitLLVMOnlyAction"); return Act->takeModule(); } #endif //HAVE_CLANG */ /*********************************************************************** * factory compile implementation **********************************************************************/ #ifdef HAVE_LLVM void gras::jit_factory(const std::string &source, const std::vector &flags_) { //write source to tmp file char source_file[L_tmpnam]; if (std::tmpnam(source_file) == NULL) throw std::runtime_error("GRAS compiler: tmp source file path failed"); std::ofstream source_fstream(source_file); source_fstream << source; source_fstream.close(); llvm::InitializeNativeTarget(); llvm::llvm_start_multithreaded(); //use clang to compile source into bitcode std::cout << "GRAS compiler: compile source into bitcode..." << std::endl; std::vector flags = flags_; flags.push_back("-I"+get_gras_runtime_include_path()); //add root include path llvm::Module *module = call_clang_exe(source_file, flags); //create execution engine std::string error; boost::shared_ptr ee(llvm::ExecutionEngine::create(module, false, &error)); if (not error.empty()) throw std::runtime_error("GRAS compiler: ExecutionEngine " + error); std::cout << "GRAS compiler: execute static constructors..." << std::endl; get_eemon().add(ee); } #else //HAVE_LLVM void gras::jit_factory(const std::string &, const std::vector &) { throw std::runtime_error("GRAS compiler not built with Clang support!"); } #endif //HAVE_LLVM