19 #include <clang/AST/AST.h>
20 #include <clang/AST/ASTConsumer.h>
21 #include <clang/AST/RecursiveASTVisitor.h>
22 #include <clang/Basic/DiagnosticDriver.h>
23 #include <clang/Driver/Compilation.h>
24 #include <clang/Driver/Driver.h>
25 #include <clang/Frontend/CompilerInstance.h>
26 #include <clang/Frontend/FrontendActions.h>
27 #include <clang/Frontend/TextDiagnosticPrinter.h>
28 #include <clang/Parse/ParseAST.h>
29 #include <clang/Tooling/CommonOptionsParser.h>
30 #include <clang/Tooling/Tooling.h>
31 #include <llvm/Support/Program.h>
32 #include <llvm/Support/raw_ostream.h>
33 #include <boost/process/search_path.hpp>
37 #include "clang/Basic/Version.h"
39 #if LLVM_VERSION_MAJOR >= 11
40 #include <llvm/Support/Host.h>
46 using namespace clang;
47 using namespace clang::tooling;
59 SourceManager& s_manager,
61 : ast_file_(ast_file), source_manager_(s_manager), context_(context) {
62 source_manager_.getDiagnostics().setShowColors(
false);
68 if (getMainFileName() == getFuncDeclFileName(f)) {
69 auto printing_policy = context_.getPrintingPolicy();
70 printing_policy.FullyQualifiedName = 1;
71 printing_policy.UseVoidForZeroParams = 1;
72 printing_policy.PolishForDeclaration = 1;
73 printing_policy.TerseOutput = 1;
74 f->print(ast_file_, printing_policy);
84 auto f_entry = source_manager_.getFileEntryForID(source_manager_.getMainFileID());
85 return f_entry->getName().str();
89 SourceLocation spell_loc = source_manager_.getSpellingLoc(f->getLocation());
90 PresumedLoc p_loc = source_manager_.getPresumedLoc(spell_loc);
92 return std::string(p_loc.getFilename());
106 SourceManager& s_manager,
108 : visitor_(ast_file, s_manager, context) {}
113 for (DeclGroupRef::iterator b = decl_reference.begin(), e = decl_reference.end();
117 visitor_.TraverseDecl(*b);
134 StringRef file)
override {
135 return std::make_unique<DeclASTConsumer>(
136 ast_file_, instance.getSourceManager(), instance.getASTContext());
145 #if LLVM_VERSION_MAJOR >= 10
147 #define CREATE_FRONTEND_ACTION(ast_file_) std::make_unique<HandleDeclAction>(ast_file_)
150 #define CREATE_FRONTEND_ACTION(ast_file_) new HandleDeclAction(ast_file_)
153 ToolFactory(llvm::raw_fd_ostream& ast_file) : ast_file_(ast_file) {}
168 std::array<char, 128> buffer;
170 std::unique_ptr<FILE, decltype(&heavyai::pclose)> pipe(
heavyai::popen(cmd.c_str(),
"r"),
173 throw std::runtime_error(
"heavyai::popen(\"" + cmd +
"\") failed!");
175 while (fgets(buffer.data(), buffer.size(), pipe.get()) !=
nullptr) {
176 result += buffer.data();
182 std::string cmd = clang_path +
" --version";
184 if (result.empty()) {
185 throw std::runtime_error(
186 "Invalid clang binary path detected, cannot find clang binary. Is clang "
189 int major, minor, patchlevel;
190 auto count = sscanf(result.substr(result.find(
"clang version")).c_str(),
191 "clang version %d.%d.%d",
196 throw std::runtime_error(
"Failed to get clang version from output:\n" + result +
199 return {major, minor, patchlevel};
213 llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> diag_options);
218 llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs>
diag_id;
226 llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> diag_options =
227 new DiagnosticOptions();
229 throw std::runtime_error(
230 "Failed to initialize UDF compiler diagnostic options. Aborting UDF compiler "
231 "initialization. Is clang/clang++ installed?");
236 UdfClangDriver::UdfClangDriver(
237 const std::string& clang_path,
238 llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> diag_options)
239 : diag_options(diag_options)
240 , diag_client(new TextDiagnosticPrinter(llvm::errs(), diag_options.get()))
241 , diag_id(new clang::DiagnosticIDs())
242 , diags(diag_id, diag_options.get(), diag_client)
243 , diag_client_owner(diags.takeClient())
244 , the_driver(clang_path.c_str(), llvm::sys::getDefaultTargetTriple(), diags)
248 if (!boost::filesystem::exists(
the_driver.ResourceDir)) {
250 <<
" does not exist";
255 std::string clang_resource_dir =
exec_output(clang_path +
" -print-resource-dir");
258 clang_resource_dir.erase(
259 std::find_if(clang_resource_dir.rbegin(),
260 clang_resource_dir.rend(),
261 [](
unsigned char ch) {
return !std::isspace(ch); })
263 clang_resource_dir.end());
265 if (clang_resource_dir !=
the_driver.ResourceDir) {
266 LOG(
WARNING) <<
"Resetting clang driver ResourceDir to " << clang_resource_dir
272 #if LLVM_VERSION_MAJOR >= 10
274 #
if LLVM_VERSION_MAJOR >= 14
275 clang::diag::warn_drv_new_cuda_version,
277 clang::diag::warn_drv_unknown_cuda_version,
279 clang::diag::Severity::Ignored,
280 clang::SourceLocation());
285 diags.setSeverity(clang::diag::err_drv_no_cuda_libdevice,
286 clang::diag::Severity::Ignored,
287 clang::SourceLocation());
291 if (clang_path_override.empty()) {
292 const auto clang_path = (llvm::sys::findProgramByName(
"clang++").get());
293 if (clang_path.empty()) {
294 throw std::runtime_error(
295 "Unable to find clang++ to compile user defined functions");
299 if (!boost::filesystem::exists(clang_path_override)) {
300 throw std::runtime_error(
"Path provided for udf compiler " + clang_path_override +
304 if (boost::filesystem::is_directory(clang_path_override)) {
305 throw std::runtime_error(
"Path provided for udf compiler " + clang_path_override +
306 " is not to the clang++ executable.");
309 return clang_path_override;
315 const std::string& clang_path_override)
318 , target_arch_(target_arch)
326 const std::string& clang_path_override,
327 const std::vector<std::string> clang_options)
329 , clang_options_(clang_options)
331 , target_arch_(target_arch)
339 const std::string& udf_file_name)
const {
340 LOG(
INFO) <<
"UDFCompiler filename to compile: " << udf_file_name;
341 if (!boost::filesystem::exists(udf_file_name)) {
342 throw std::runtime_error(
"User defined function file " + udf_file_name +
350 std::string cpu_file_name =
"";
351 std::string cuda_file_name =
"";
357 cuda_file_name = compileToNVVMIR(udf_file_name);
358 }
catch (
const std::exception& e) {
360 <<
"Failed to generate GPU IR for UDF " + udf_file_name +
361 ", attempting to use CPU compiled IR for GPU.\nUDF Compiler exception: " +
365 return std::make_pair(cpu_file_name, cuda_file_name);
371 if (path ==
"." || path ==
"..") {
375 size_t pos = path.find_last_of(
"\\/.");
376 if (pos != std::string::npos && path[pos] ==
'.') {
377 return path.substr(0, pos);
384 size_t i = s.rfind(
'.', s.length());
385 if (1 != std::string::npos) {
386 return (s.substr(i + 1, s.length() - i));
391 std::string::size_type i = s.rfind(
'.', s.length());
393 if (i != std::string::npos) {
409 const std::vector<std::string>& command_line)
const {
411 auto the_driver(compiler_driver.getClangDriver());
413 std::vector<const char*> clang_command_opts;
414 clang_command_opts.reserve(command_line.size() +
clang_options_.size());
417 std::end(command_line),
418 std::back_inserter(clang_command_opts),
419 [&](
const std::string& str) {
return str.c_str(); });
425 std::back_inserter(clang_command_opts),
426 [&](
const std::string& str) {
return str.c_str(); });
429 std::unique_ptr<driver::Compilation> compilation(
430 the_driver->BuildCompilation(clang_command_opts));
432 throw std::runtime_error(
"failed to build compilation object!");
434 auto [clang_version_major, clang_version_minor, clang_version_patchlevel] =
435 compiler_driver.getClangVersion();
436 if (clang_version_major != CLANG_VERSION_MAJOR
440 || CLANG_VERSION_MAJOR == 9
447 auto& jobs = compilation->getJobs();
449 auto& job = *jobs.begin();
451 std::string cmd = job.getExecutable();
453 std::string last =
"";
455 for (
auto& arg : job.getArguments()) {
456 const std::string& s = arg;
466 if (s ==
"-include") {
470 if (last ==
"-include") {
471 if (s !=
"__clang_cuda_runtime_wrapper.h") {
472 cmd +=
" -include " + s;
479 if (s ==
"-fcuda-is-device") {
484 if constexpr (CLANG_VERSION_MAJOR == 9) {
485 if (clang_version_major > 9) {
487 if (s ==
"-masm-verbose" || s ==
"-fuse-init-array" ||
488 s ==
"-dwarf-column-info" || s ==
"-momit-leaf-frame-pointer" ||
489 s ==
"-fdiagnostics-show-option" || s ==
"-mdisable-fp-elim") {
493 if (s ==
"-fmessage-length") {
501 if constexpr (CLANG_VERSION_MAJOR == 10) {
502 if (clang_version_major > 10) {
504 if (s ==
"-masm-verbose" || s ==
"-dwarf-column-info" ||
505 s ==
"-fdiagnostics-show-option") {
509 if (s ==
"-fmessage-length") {
517 if constexpr (CLANG_VERSION_MAJOR >= 10) {
518 if (clang_version_major < 10) {
520 if (s ==
"-fno-rounding-math" || s.rfind(
"-mframe-pointer=", 0) == 0 ||
521 s.rfind(
"-fgnuc-version=", 0) == 0) {
528 if constexpr (CLANG_VERSION_MAJOR == 11) {
529 if (clang_version_major < 11) {
531 if (s ==
"-fno-verbose-asm") {
535 if (s ==
"-aux-target-cpu") {
554 llvm::SmallVector<std::pair<int, const driver::Command*>, 10> failing_commands;
555 int res = the_driver->ExecuteCompilation(*compilation, failing_commands);
557 for (
const std::pair<int, const driver::Command*>& p : failing_commands) {
559 the_driver->generateCompilationDiagnostics(*compilation, *p.second);
567 std::string UdfCompiler::compileToNVVMIR(
const std::string& udf_file_name)
const {
579 command_line.emplace_back(
"--cuda-gpu-arch=" +
581 command_line.emplace_back(
"--cuda-device-only");
582 command_line.emplace_back(
"-xcuda");
583 command_line.emplace_back(
"--no-cuda-version-check");
585 if (cuda_path !=
"") {
586 command_line.emplace_back(
"--cuda-path=" + cuda_path);
589 command_line.emplace_back(udf_file_name);
592 boost::filesystem::remove(gpu_out_filename);
596 if (!status && !boost::filesystem::exists(gpu_out_filename)) {
597 throw std::runtime_error(
598 "Failed to generate GPU UDF IR in CUDA mode with error code " +
601 return gpu_out_filename;
619 throw std::runtime_error(
"Failed to compile CPU UDF (status code " +
622 if (!boost::filesystem::exists(cpu_out_filename)) {
623 throw std::runtime_error(
"udf compile did not produce output file " +
626 return cpu_out_filename;
631 std::string resource_path = the_driver.getClangDriver()->ResourceDir;
632 std::string include_option =
633 std::string(
"-I") + resource_path + std::string(
"/include");
635 std::vector<std::string> arg_vector;
636 arg_vector.emplace_back(
"astparser");
637 arg_vector.emplace_back(file_name);
638 arg_vector.emplace_back(
"--");
639 arg_vector.emplace_back(
"-DNO_BOOST");
640 arg_vector.emplace_back(include_option);
641 arg_vector.emplace_back(
"-std=c++17");
647 std::vector<const char*> arg_vec2;
649 arg_vector.begin(), arg_vector.end(), std::back_inserter(arg_vec2),
convert);
651 int num_args = arg_vec2.size();
652 #if LLVM_VERSION_MAJOR > 12
654 ClangTool tool(op->getCompilations(), op->getSourcePathList());
657 ClangTool tool(op.getCompilations(), op.getSourcePathList());
660 std::string out_name(file_name);
661 std::string file_ext(
"ast");
665 llvm::raw_fd_ostream out_file(
666 llvm::StringRef(out_name), out_error_info, llvm::sys::fs::OF_None);
668 auto factory = std::make_unique<ToolFactory>(out_file);
669 const auto result = tool.run(factory.get());
671 throw std::runtime_error(
672 "Unable to create AST file for udf compilation (error code " +
678 auto ast_file_name = udf_file_name;
680 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)
torch::Tensor f(torch::Tensor x, torch::Tensor W_target, torch::Tensor b_target)
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)