19 #include <clang/AST/AST.h>
20 #include <clang/AST/ASTConsumer.h>
21 #include <clang/AST/RecursiveASTVisitor.h>
22 #include <clang/Driver/Compilation.h>
23 #include <clang/Driver/Driver.h>
24 #include <clang/Frontend/CompilerInstance.h>
25 #include <clang/Frontend/FrontendActions.h>
26 #include <clang/Frontend/TextDiagnosticPrinter.h>
27 #include <clang/Parse/ParseAST.h>
28 #include <clang/Tooling/CommonOptionsParser.h>
29 #include <clang/Tooling/Tooling.h>
30 #include <llvm/Support/Program.h>
31 #include <llvm/Support/raw_ostream.h>
32 #include <boost/process/search_path.hpp>
36 #include "clang/Basic/Version.h"
38 #if LLVM_VERSION_MAJOR >= 11
39 #include <llvm/Support/Host.h>
45 using namespace clang;
46 using namespace clang::tooling;
58 SourceManager& s_manager,
60 : ast_file_(ast_file), source_manager_(s_manager), context_(context) {
61 source_manager_.getDiagnostics().setShowColors(
false);
67 if (getMainFileName() == getFuncDeclFileName(f)) {
68 auto printing_policy = context_.getPrintingPolicy();
69 printing_policy.FullyQualifiedName = 1;
70 printing_policy.UseVoidForZeroParams = 1;
71 printing_policy.PolishForDeclaration = 1;
72 printing_policy.TerseOutput = 1;
73 f->print(ast_file_, printing_policy);
83 auto f_entry = source_manager_.getFileEntryForID(source_manager_.getMainFileID());
84 return f_entry->getName().str();
88 SourceLocation spell_loc = source_manager_.getSpellingLoc(f->getLocation());
89 PresumedLoc p_loc = source_manager_.getPresumedLoc(spell_loc);
91 return std::string(p_loc.getFilename());
105 SourceManager& s_manager,
107 : visitor_(ast_file, s_manager, context) {}
112 for (DeclGroupRef::iterator b = decl_reference.begin(), e = decl_reference.end();
116 visitor_.TraverseDecl(*b);
133 StringRef file)
override {
134 return std::make_unique<DeclASTConsumer>(
135 ast_file_, instance.getSourceManager(), instance.getASTContext());
144 #if LLVM_VERSION_MAJOR >= 10
146 #define CREATE_FRONTEND_ACTION(ast_file_) std::make_unique<HandleDeclAction>(ast_file_)
149 #define CREATE_FRONTEND_ACTION(ast_file_) new HandleDeclAction(ast_file_)
152 ToolFactory(llvm::raw_fd_ostream& ast_file) : ast_file_(ast_file) {}
165 std::array<char, 128> buffer;
167 std::unique_ptr<FILE, decltype(&heavyai::pclose)> pipe(
heavyai::popen(cmd.c_str(),
"r"),
170 throw std::runtime_error(
"heavyai::popen(\"" + cmd +
"\") failed!");
172 while (fgets(buffer.data(), buffer.size(), pipe.get()) !=
nullptr) {
173 result += buffer.data();
179 std::string cmd = clang_path +
" --version";
181 if (result.empty()) {
182 throw std::runtime_error(
183 "Invalid clang binary path detected, cannot find clang binary. Is clang "
186 int major, minor, patchlevel;
187 auto count = sscanf(result.substr(result.find(
"clang version")).c_str(),
188 "clang version %d.%d.%d",
193 throw std::runtime_error(
"Failed to get clang version from output:\n" + result +
196 return {major, minor, patchlevel};
210 llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> diag_options);
215 llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs>
diag_id;
223 llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> diag_options =
224 new DiagnosticOptions();
226 throw std::runtime_error(
227 "Failed to initialize UDF compiler diagnostic options. Aborting UDF compiler "
228 "initialization. Is clang/clang++ installed?");
233 UdfClangDriver::UdfClangDriver(
234 const std::string& clang_path,
235 llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> diag_options)
236 : diag_options(diag_options)
237 , diag_client(new TextDiagnosticPrinter(llvm::errs(), diag_options.get()))
238 , diag_id(new clang::DiagnosticIDs())
239 , diags(diag_id, diag_options.get(), diag_client)
240 , diag_client_owner(diags.takeClient())
241 , the_driver(clang_path.c_str(), llvm::sys::getDefaultTargetTriple(), diags)
245 if (!boost::filesystem::exists(
the_driver.ResourceDir)) {
247 <<
" does not exist";
252 std::string clang_resource_dir =
exec_output(clang_path +
" -print-resource-dir");
255 clang_resource_dir.erase(
256 std::find_if(clang_resource_dir.rbegin(),
257 clang_resource_dir.rend(),
258 [](
unsigned char ch) {
return !std::isspace(ch); })
260 clang_resource_dir.end());
262 if (clang_resource_dir !=
the_driver.ResourceDir) {
263 LOG(
WARNING) <<
"Resetting clang driver ResourceDir to " << clang_resource_dir
270 if (clang_path_override.empty()) {
271 const auto clang_path = (llvm::sys::findProgramByName(
"clang++").get());
272 if (clang_path.empty()) {
273 throw std::runtime_error(
274 "Unable to find clang++ to compile user defined functions");
278 if (!boost::filesystem::exists(clang_path_override)) {
279 throw std::runtime_error(
"Path provided for udf compiler " + clang_path_override +
283 if (boost::filesystem::is_directory(clang_path_override)) {
284 throw std::runtime_error(
"Path provided for udf compiler " + clang_path_override +
285 " is not to the clang++ executable.");
288 return clang_path_override;
294 const std::string& clang_path_override)
297 , target_arch_(target_arch)
305 const std::string& clang_path_override,
306 const std::vector<std::string> clang_options)
308 , clang_options_(clang_options)
310 , target_arch_(target_arch)
318 const std::string& udf_file_name)
const {
319 LOG(
INFO) <<
"UDFCompiler filename to compile: " << udf_file_name;
320 if (!boost::filesystem::exists(udf_file_name)) {
321 throw std::runtime_error(
"User defined function file " + udf_file_name +
329 std::string cpu_file_name =
"";
330 std::string cuda_file_name =
"";
336 cuda_file_name = compileToNVVMIR(udf_file_name);
337 }
catch (
const std::exception& e) {
339 <<
"Failed to generate GPU IR for UDF " + udf_file_name +
340 ", attempting to use CPU compiled IR for GPU.\nUDF Compiler exception: " +
344 return std::make_pair(cpu_file_name, cuda_file_name);
350 if (path ==
"." || path ==
"..") {
354 size_t pos = path.find_last_of(
"\\/.");
355 if (pos != std::string::npos && path[pos] ==
'.') {
356 return path.substr(0, pos);
363 size_t i = s.rfind(
'.', s.length());
364 if (1 != std::string::npos) {
365 return (s.substr(i + 1, s.length() - i));
370 std::string::size_type i = s.rfind(
'.', s.length());
372 if (i != std::string::npos) {
388 const std::vector<std::string>& command_line)
const {
390 auto the_driver(compiler_driver.getClangDriver());
392 std::vector<const char*> clang_command_opts;
393 clang_command_opts.reserve(command_line.size() +
clang_options_.size());
396 std::end(command_line),
397 std::back_inserter(clang_command_opts),
398 [&](
const std::string& str) {
return str.c_str(); });
404 std::back_inserter(clang_command_opts),
405 [&](
const std::string& str) {
return str.c_str(); });
408 std::unique_ptr<driver::Compilation> compilation(
409 the_driver->BuildCompilation(clang_command_opts));
411 throw std::runtime_error(
"failed to build compilation object!");
413 auto [clang_version_major, clang_version_minor, clang_version_patchlevel] =
414 compiler_driver.getClangVersion();
415 if (clang_version_major != CLANG_VERSION_MAJOR
419 || CLANG_VERSION_MAJOR == 9
426 auto& jobs = compilation->getJobs();
428 auto& job = *jobs.begin();
430 std::string cmd = job.getExecutable();
432 std::string last =
"";
434 for (
auto& arg : job.getArguments()) {
435 const std::string& s = arg;
445 if (s ==
"-include") {
449 if (last ==
"-include") {
450 if (s !=
"__clang_cuda_runtime_wrapper.h") {
451 cmd +=
" -include " + s;
458 if (s ==
"-fcuda-is-device") {
463 if constexpr (CLANG_VERSION_MAJOR == 9) {
464 if (clang_version_major > 9) {
466 if (s ==
"-masm-verbose" || s ==
"-fuse-init-array" ||
467 s ==
"-dwarf-column-info" || s ==
"-momit-leaf-frame-pointer" ||
468 s ==
"-fdiagnostics-show-option" || s ==
"-mdisable-fp-elim") {
472 if (s ==
"-fmessage-length") {
480 if constexpr (CLANG_VERSION_MAJOR == 10) {
481 if (clang_version_major > 10) {
483 if (s ==
"-masm-verbose" || s ==
"-dwarf-column-info" ||
484 s ==
"-fdiagnostics-show-option") {
488 if (s ==
"-fmessage-length") {
496 if constexpr (CLANG_VERSION_MAJOR >= 10) {
497 if (clang_version_major < 10) {
499 if (s ==
"-fno-rounding-math" || s.rfind(
"-mframe-pointer=", 0) == 0 ||
500 s.rfind(
"-fgnuc-version=", 0) == 0) {
507 if constexpr (CLANG_VERSION_MAJOR == 11) {
508 if (clang_version_major < 11) {
510 if (s ==
"-fno-verbose-asm") {
514 if (s ==
"-aux-target-cpu") {
533 llvm::SmallVector<std::pair<int, const driver::Command*>, 10> failing_commands;
534 int res = the_driver->ExecuteCompilation(*compilation, failing_commands);
536 for (
const std::pair<int, const driver::Command*>& p : failing_commands) {
538 the_driver->generateCompilationDiagnostics(*compilation, *p.second);
546 std::string UdfCompiler::compileToNVVMIR(
const std::string& udf_file_name)
const {
558 command_line.emplace_back(
"--cuda-gpu-arch=" +
560 command_line.emplace_back(
"--cuda-device-only");
561 command_line.emplace_back(
"-xcuda");
562 command_line.emplace_back(
"--no-cuda-version-check");
564 if (cuda_path !=
"") {
565 command_line.emplace_back(
"--cuda-path=" + cuda_path);
568 command_line.emplace_back(udf_file_name);
571 boost::filesystem::remove(gpu_out_filename);
575 if (!status && !boost::filesystem::exists(gpu_out_filename)) {
576 throw std::runtime_error(
577 "Failed to generate GPU UDF IR in CUDA mode with error code " +
580 return gpu_out_filename;
598 throw std::runtime_error(
"Failed to compile CPU UDF (status code " +
601 if (!boost::filesystem::exists(cpu_out_filename)) {
602 throw std::runtime_error(
"udf compile did not produce output file " +
605 return cpu_out_filename;
610 std::string resource_path = the_driver.getClangDriver()->ResourceDir;
611 std::string include_option =
612 std::string(
"-I") + resource_path + std::string(
"/include");
614 std::vector<std::string> arg_vector;
615 arg_vector.emplace_back(
"astparser");
616 arg_vector.emplace_back(file_name);
617 arg_vector.emplace_back(
"--");
618 arg_vector.emplace_back(
"-DNO_BOOST");
619 arg_vector.emplace_back(include_option);
620 arg_vector.emplace_back(
"-std=c++17");
626 std::vector<const char*> arg_vec2;
628 arg_vector.begin(), arg_vector.end(), std::back_inserter(arg_vec2),
convert);
630 int num_args = arg_vec2.size();
631 #if LLVM_VERSION_MAJOR > 12
633 ClangTool tool(op->getCompilations(), op->getSourcePathList());
636 ClangTool tool(op.getCompilations(), op.getSourcePathList());
639 std::string out_name(file_name);
640 std::string file_ext(
"ast");
643 std::error_code out_error_info;
644 llvm::raw_fd_ostream out_file(
645 llvm::StringRef(out_name), out_error_info, llvm::sys::fs::OF_None);
647 auto factory = std::make_unique<ToolFactory>(out_file);
648 const auto result = tool.run(factory.get());
650 throw std::runtime_error(
651 "Unable to create AST file for udf compilation (error code " +
657 auto ast_file_name = udf_file_name;
659 return ast_file_name;
SourceManager & source_manager_
std::unique_ptr< clang::DiagnosticConsumer > diag_client_owner
std::unique_ptr< ASTConsumer > CreateASTConsumer(CompilerInstance &instance, StringRef file) override
clang::DiagnosticsEngine diags
std::vector< std::string > clang_options_
std::string getFuncDeclFileName(FunctionDecl *f) const
static std::string getAstFileName(const std::string &udf_file_name)
llvm::IntrusiveRefCntPtr< clang::DiagnosticIDs > diag_id
int compileFromCommandLine(const std::vector< std::string > &command_line) const
std::string get_clang_path(const std::string &clang_path_override)
bool HandleTopLevelDecl(DeclGroupRef decl_reference) override
clang::driver::Driver the_driver
std::string exec_output(std::string cmd)
int32_t pclose(::FILE *fh)
FunctionDeclVisitor(llvm::raw_fd_ostream &ast_file, SourceManager &s_manager, ASTContext &context)
std::pair< FILE *, std::string > create(const std::string &basePath, const int fileId, const size_t pageSize, const size_t numPages)
std::tuple< int, int, int > get_clang_version(const std::string &clang_path)
::FILE * popen(const char *command, const char *type)
std::string get_cuda_home(void)
std::tuple< int, int, int > clang_version
void generateAST(const std::string &file_name) const
void init(LogOptions const &log_opts)
DEVICE auto copy(ARGS &&...args)
clang::DiagnosticConsumer * diag_client
std::string remove_file_extension(const std::string &path)
OUTPUT transform(INPUT const &input, FUNC const &func)
std::string getMainFileName() const
static std::string deviceArchToSM(const NvidiaDeviceArch arch)
void replace_extension(std::string &s, const std::string &new_ext)
llvm::IntrusiveRefCntPtr< clang::DiagnosticOptions > diag_options
UdfCompiler(CudaMgr_Namespace::NvidiaDeviceArch target_arch, const std::string &clang_path_override="")
static std::string genNVVMIRFilename(const std::string &udf_file_name)
#define CREATE_FRONTEND_ACTION(ast_file_)
std::string compileToLLVMIR(const std::string &udf_file_name) const
HandleDeclAction(llvm::raw_fd_ostream &ast_file)
static std::string genLLVMIRFilename(const std::string &udf_file_name)
std::string get_file_ext(const std::string &s)
FunctionDeclVisitor visitor_
llvm::raw_fd_ostream & ast_file_
clang::driver::Driver * getClangDriver()
llvm::raw_fd_ostream & ast_file_
bool VisitFunctionDecl(FunctionDecl *f)
~HandleDeclAction() override
std::pair< std::string, std::string > compileUdf(const std::string &udf_file_name) const
const char * convert(const std::string &s)
std::tuple< int, int, int > getClangVersion() const
static llvm::cl::OptionCategory ToolingSampleCategory("UDF Tooling")
DeclASTConsumer(llvm::raw_fd_ostream &ast_file, SourceManager &s_manager, ASTContext &context)