1 // 2 // Copyright 2012-2016 Francisco Jerez 3 // Copyright 2012-2016 Advanced Micro Devices, Inc. 4 // Copyright 2014-2016 Jan Vesely 5 // Copyright 2014-2015 Serge Martin 6 // Copyright 2015 Zoltan Gilian 7 // 8 // Permission is hereby granted, free of charge, to any person obtaining a 9 // copy of this software and associated documentation files (the "Software"), 10 // to deal in the Software without restriction, including without limitation 11 // the rights to use, copy, modify, merge, publish, distribute, sublicense, 12 // and/or sell copies of the Software, and to permit persons to whom the 13 // Software is furnished to do so, subject to the following conditions: 14 // 15 // The above copyright notice and this permission notice shall be included in 16 // all copies or substantial portions of the Software. 17 // 18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 22 // OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 23 // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 // OTHER DEALINGS IN THE SOFTWARE. 25 // 26 27 #include <llvm/IR/DiagnosticPrinter.h> 28 #include <llvm/IR/DiagnosticInfo.h> 29 #include <llvm/IR/LLVMContext.h> 30 #include <llvm/Support/raw_ostream.h> 31 #include <llvm/Transforms/IPO/PassManagerBuilder.h> 32 #include <llvm-c/Target.h> 33 34 #include <clang/CodeGen/CodeGenAction.h> 35 #include <clang/Lex/PreprocessorOptions.h> 36 #include <clang/Frontend/TextDiagnosticBuffer.h> 37 #include <clang/Frontend/TextDiagnosticPrinter.h> 38 #include <clang/Basic/TargetInfo.h> 39 40 // We need to include internal headers last, because the internal headers 41 // include CL headers which have #define's like: 42 // 43 //#define cl_khr_gl_sharing 1 44 //#define cl_khr_icd 1 45 // 46 // Which will break the compilation of clang/Basic/OpenCLOptions.h 47 48 #include "core/error.hpp" 49 #include "llvm/codegen.hpp" 50 #include "llvm/compat.hpp" 51 #include "llvm/invocation.hpp" 52 #include "llvm/metadata.hpp" 53 #include "llvm/util.hpp" 54 #include "util/algorithm.hpp" 55 56 57 using namespace clover; 58 using namespace clover::llvm; 59 60 using ::llvm::Function; 61 using ::llvm::LLVMContext; 62 using ::llvm::Module; 63 using ::llvm::raw_string_ostream; 64 65 namespace { 66 void init_targets()67 init_targets() { 68 static bool targets_initialized = false; 69 if (!targets_initialized) { 70 LLVMInitializeAllTargets(); 71 LLVMInitializeAllTargetInfos(); 72 LLVMInitializeAllTargetMCs(); 73 LLVMInitializeAllAsmPrinters(); 74 targets_initialized = true; 75 } 76 } 77 78 void diagnostic_handler(const::llvm::DiagnosticInfo & di,void * data)79 diagnostic_handler(const ::llvm::DiagnosticInfo &di, void *data) { 80 if (di.getSeverity() == ::llvm::DS_Error) { 81 raw_string_ostream os { *reinterpret_cast<std::string *>(data) }; 82 ::llvm::DiagnosticPrinterRawOStream printer { os }; 83 di.print(printer); 84 throw build_error(); 85 } 86 } 87 88 std::unique_ptr<LLVMContext> create_context(std::string & r_log)89 create_context(std::string &r_log) { 90 init_targets(); 91 std::unique_ptr<LLVMContext> ctx { new LLVMContext }; 92 compat::set_diagnostic_handler(*ctx, diagnostic_handler, &r_log); 93 return ctx; 94 } 95 96 std::unique_ptr<clang::CompilerInstance> create_compiler_instance(const target & target,const std::vector<std::string> & opts,std::string & r_log)97 create_compiler_instance(const target &target, 98 const std::vector<std::string> &opts, 99 std::string &r_log) { 100 std::unique_ptr<clang::CompilerInstance> c { new clang::CompilerInstance }; 101 clang::TextDiagnosticBuffer *diag_buffer = new clang::TextDiagnosticBuffer; 102 clang::DiagnosticsEngine diag { new clang::DiagnosticIDs, 103 new clang::DiagnosticOptions, diag_buffer }; 104 105 // Parse the compiler options. A file name should be present at the end 106 // and must have the .cl extension in order for the CompilerInvocation 107 // class to recognize it as an OpenCL source file. 108 const std::vector<const char *> copts = 109 map(std::mem_fn(&std::string::c_str), opts); 110 111 if (!clang::CompilerInvocation::CreateFromArgs( 112 c->getInvocation(), copts.data(), copts.data() + copts.size(), diag)) 113 throw invalid_build_options_error(); 114 115 diag_buffer->FlushDiagnostics(diag); 116 if (diag.hasErrorOccurred()) 117 throw invalid_build_options_error(); 118 119 c->getTargetOpts().CPU = target.cpu; 120 c->getTargetOpts().Triple = target.triple; 121 c->getLangOpts().NoBuiltin = true; 122 123 // This is a workaround for a Clang bug which causes the number 124 // of warnings and errors to be printed to stderr. 125 // http://www.llvm.org/bugs/show_bug.cgi?id=19735 126 c->getDiagnosticOpts().ShowCarets = false; 127 128 compat::set_lang_defaults(c->getInvocation(), c->getLangOpts(), 129 compat::ik_opencl, ::llvm::Triple(target.triple), 130 c->getPreprocessorOpts(), 131 clang::LangStandard::lang_opencl11); 132 133 c->createDiagnostics(new clang::TextDiagnosticPrinter( 134 *new raw_string_ostream(r_log), 135 &c->getDiagnosticOpts(), true)); 136 137 c->setTarget(clang::TargetInfo::CreateTargetInfo( 138 c->getDiagnostics(), c->getInvocation().TargetOpts)); 139 140 return c; 141 } 142 143 std::unique_ptr<Module> compile(LLVMContext & ctx,clang::CompilerInstance & c,const std::string & name,const std::string & source,const header_map & headers,const std::string & target,const std::string & opts,std::string & r_log)144 compile(LLVMContext &ctx, clang::CompilerInstance &c, 145 const std::string &name, const std::string &source, 146 const header_map &headers, const std::string &target, 147 const std::string &opts, std::string &r_log) { 148 c.getFrontendOpts().ProgramAction = clang::frontend::EmitLLVMOnly; 149 c.getHeaderSearchOpts().UseBuiltinIncludes = true; 150 c.getHeaderSearchOpts().UseStandardSystemIncludes = true; 151 c.getHeaderSearchOpts().ResourceDir = CLANG_RESOURCE_DIR; 152 153 // Add libclc generic search path 154 c.getHeaderSearchOpts().AddPath(LIBCLC_INCLUDEDIR, 155 clang::frontend::Angled, 156 false, false); 157 158 // Add libclc include 159 c.getPreprocessorOpts().Includes.push_back("clc/clc.h"); 160 161 // Add definition for the OpenCL version 162 c.getPreprocessorOpts().addMacroDef("__OPENCL_VERSION__=110"); 163 164 // clc.h requires that this macro be defined: 165 c.getPreprocessorOpts().addMacroDef("cl_clang_storage_class_specifiers"); 166 c.getPreprocessorOpts().addRemappedFile( 167 name, ::llvm::MemoryBuffer::getMemBuffer(source).release()); 168 169 if (headers.size()) { 170 const std::string tmp_header_path = "/tmp/clover/"; 171 172 c.getHeaderSearchOpts().AddPath(tmp_header_path, 173 clang::frontend::Angled, 174 false, false); 175 176 for (const auto &header : headers) 177 c.getPreprocessorOpts().addRemappedFile( 178 tmp_header_path + header.first, 179 ::llvm::MemoryBuffer::getMemBuffer(header.second).release()); 180 } 181 182 // Tell clang to link this file before performing any 183 // optimizations. This is required so that we can replace calls 184 // to the OpenCL C barrier() builtin with calls to target 185 // intrinsics that have the noduplicate attribute. This 186 // attribute will prevent Clang from creating illegal uses of 187 // barrier() (e.g. Moving barrier() inside a conditional that is 188 // no executed by all threads) during its optimizaton passes. 189 compat::add_link_bitcode_file(c.getCodeGenOpts(), 190 LIBCLC_LIBEXECDIR + target + ".bc"); 191 192 // Compile the code 193 clang::EmitLLVMOnlyAction act(&ctx); 194 if (!c.ExecuteAction(act)) 195 throw build_error(); 196 197 return act.takeModule(); 198 } 199 } 200 201 module 202 clover::llvm::compile_program(const std::string &source, 203 const header_map &headers, 204 const std::string &target, 205 const std::string &opts, 206 std::string &r_log) { 207 if (has_flag(debug::clc)) 208 debug::log(".cl", "// Options: " + opts + '\n' + source); 209 210 auto ctx = create_context(r_log); 211 auto c = create_compiler_instance(target, tokenize(opts + " input.cl"), 212 r_log); 213 auto mod = compile(*ctx, *c, "input.cl", source, headers, target, opts, 214 r_log); 215 216 if (has_flag(debug::llvm)) 217 debug::log(".ll", print_module_bitcode(*mod)); 218 219 return build_module_library(*mod, module::section::text_intermediate); 220 } 221 222 namespace { 223 void 224 optimize(Module &mod, unsigned optimization_level, 225 bool internalize_symbols) { 226 compat::pass_manager pm; 227 228 compat::add_data_layout_pass(pm); 229 230 // By default, the function internalizer pass will look for a function 231 // called "main" and then mark all other functions as internal. Marking 232 // functions as internal enables the optimizer to perform optimizations 233 // like function inlining and global dead-code elimination. 234 // 235 // When there is no "main" function in a module, the internalize pass will 236 // treat the module like a library, and it won't internalize any functions. 237 // Since there is no "main" function in our kernels, we need to tell 238 // the internalizer pass that this module is not a library by passing a 239 // list of kernel functions to the internalizer. The internalizer will 240 // treat the functions in the list as "main" functions and internalize 241 // all of the other functions. 242 if (internalize_symbols) 243 compat::add_internalize_pass(pm, map(std::mem_fn(&Function::getName), 244 get_kernels(mod))); 245 246 ::llvm::PassManagerBuilder pmb; 247 pmb.OptLevel = optimization_level; 248 pmb.LibraryInfo = new compat::target_library_info( 249 ::llvm::Triple(mod.getTargetTriple())); 250 pmb.populateModulePassManager(pm); 251 pm.run(mod); 252 } 253 254 std::unique_ptr<Module> 255 link(LLVMContext &ctx, const clang::CompilerInstance &c, 256 const std::vector<module> &modules, std::string &r_log) { 257 std::unique_ptr<Module> mod { new Module("link", ctx) }; 258 auto linker = compat::create_linker(*mod); 259 260 for (auto &m : modules) { 261 if (compat::link_in_module(*linker, 262 parse_module_library(m, ctx, r_log))) 263 throw build_error(); 264 } 265 266 return std::move(mod); 267 } 268 } 269 270 module 271 clover::llvm::link_program(const std::vector<module> &modules, 272 enum pipe_shader_ir ir, const std::string &target, 273 const std::string &opts, std::string &r_log) { 274 std::vector<std::string> options = tokenize(opts + " input.cl"); 275 const bool create_library = count("-create-library", options); 276 erase_if(equals("-create-library"), options); 277 278 auto ctx = create_context(r_log); 279 auto c = create_compiler_instance(target, options, r_log); 280 auto mod = link(*ctx, *c, modules, r_log); 281 282 optimize(*mod, c->getCodeGenOpts().OptimizationLevel, !create_library); 283 284 static std::atomic_uint seq(0); 285 const std::string id = "." + mod->getModuleIdentifier() + "-" + 286 std::to_string(seq++); 287 288 if (has_flag(debug::llvm)) 289 debug::log(id + ".ll", print_module_bitcode(*mod)); 290 291 if (create_library) { 292 return build_module_library(*mod, module::section::text_library); 293 294 } else if (ir == PIPE_SHADER_IR_LLVM) { 295 return build_module_bitcode(*mod, *c); 296 297 } else if (ir == PIPE_SHADER_IR_NATIVE) { 298 if (has_flag(debug::native)) 299 debug::log(id + ".asm", print_module_native(*mod, target)); 300 301 return build_module_native(*mod, target, *c, r_log); 302 303 } else { 304 unreachable("Unsupported IR."); 305 } 306 } 307