├── .gitignore ├── CMakeLists.txt ├── License ├── README.md └── src ├── driver ├── AmdCompiler.cpp ├── AmdCompiler.h └── CMakeLists.txt ├── roc-cl ├── CMakeLists.txt └── roc-cl.cpp ├── test ├── CMakeLists.txt ├── defined.cl ├── extern_function1.cl ├── extern_function2.cl ├── include │ └── include.h ├── includer.cl └── simple.cl └── unittest ├── CMakeLists.txt └── Tests.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | ## 3 | ## The University of Illinois/NCSA 4 | ## Open Source License (NCSA) 5 | ## 6 | ## Copyright (c) 2016, Advanced Micro Devices, Inc. All rights reserved. 7 | ## 8 | ## Developed by: 9 | ## 10 | ## AMD Research and AMD HSA Software Development 11 | ## 12 | ## Advanced Micro Devices, Inc. 13 | ## 14 | ## www.amd.com 15 | ## 16 | ## Permission is hereby granted, free of charge, to any person obtaining a copy 17 | ## of this software and associated documentation files (the "Software"), to 18 | ## deal with the Software without restriction, including without limitation 19 | ## the rights to use, copy, modify, merge, publish, distribute, sublicense, 20 | ## and#or sell copies of the Software, and to permit persons to whom the 21 | ## Software is furnished to do so, subject to the following conditions: 22 | ## 23 | ## - Redistributions of source code must retain the above copyright notice, 24 | ## this list of conditions and the following disclaimers. 25 | ## - Redistributions in binary form must reproduce the above copyright 26 | ## notice, this list of conditions and the following disclaimers in 27 | ## the documentation and#or other materials provided with the distribution. 28 | ## - Neither the names of Advanced Micro Devices, Inc, 29 | ## nor the names of its contributors may be used to endorse or promote 30 | ## products derived from this Software without specific prior written 31 | ## permission. 32 | ## 33 | ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 34 | ## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 35 | ## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 36 | ## THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 37 | ## OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 38 | ## ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 39 | ## DEALINGS WITH THE SOFTWARE. 40 | ## 41 | ################################################################################ 42 | 43 | cmake_minimum_required(VERSION 2.8) 44 | 45 | ## Include common cmake modules 46 | include ( GNUInstallDirs ) 47 | 48 | # Build ROCm-OpenCL-Driver with ccache if the package is present. 49 | set(ROCM_OPENCL_DRIVER_CCACHE_BUILD OFF CACHE BOOL "Set to ON for a ccache enabled build") 50 | if(ROCM_OPENCL_DRIVER_CCACHE_BUILD) 51 | find_program(CCACHE_PROGRAM ccache) 52 | if(CCACHE_PROGRAM) 53 | set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ${CCACHE_PROGRAM}) 54 | else() 55 | message(FATAL_ERROR "Unable to find the program ccache. Set ROCM_OPENCL_DRIVER_CCACHE_BUILD to OFF") 56 | endif() 57 | endif() 58 | 59 | # Check if ROCm-OpenCL-Driver is built as a standalone project. 60 | if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) 61 | project(ROCm-OpenCL-Driver) 62 | 63 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 64 | set(CMAKE_INSTALL_PREFIX ${CMAKE_BINARY_DIR}/dist CACHE INTERNAL "Prefix prepended to install directories") 65 | endif() 66 | 67 | list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake_modules) 68 | 69 | find_package(LLVM REQUIRED PATHS ${LLVM_DIR} "/opt/rocm/llvm" NO_DEFAULT_PATH) 70 | 71 | list(APPEND CMAKE_MODULE_PATH ${LLVM_CMAKE_DIR}) 72 | include(AddLLVM) 73 | else() 74 | include_directories(${ROCM_OCL_INCLUDES}) 75 | endif() 76 | 77 | if(UNIX) 78 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -fno-rtti -Wall") 79 | add_definitions(-D__STDC_LIMIT_MACROS) 80 | add_definitions(-D__STDC_CONSTANT_MACROS) 81 | endif() 82 | 83 | enable_testing() 84 | 85 | add_subdirectory(src/driver) 86 | add_subdirectory(src/roc-cl) 87 | add_subdirectory(src/test) 88 | add_subdirectory(src/unittest) 89 | -------------------------------------------------------------------------------- /License: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Advanced Micro Devices, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## OVERVIEW 2 | 3 | ROCm OpenCL Driver is a thin wrapper over Clang Driver API. It provides C++ interface to compilation services. 4 | 5 | ## DEPRECATION NOTICE 6 | 7 | AMD is deprecating ROCm-OpenCL-Driver. We will no longer develop any new feature in ROCm-OpenCL-Driver and we will stop maintaining it after its final release, which is planned for December 2019. If your application was developed with the ROCm-OpenCL-Driver, we would encourage you to transition it to [ROCm-CompilerSupport](https://github.com/RadeonOpenCompute/ROCm-CompilerSupport), which provides similar functionality. 8 | 9 | ## BUILDING 10 | 11 | This project requires reasonably recent LLVM/Clang build (April 2016 trunk). 12 | 13 | Use out-of-source CMake build and create separate directory to run CMake. 14 | 15 | The following build steps are performed: 16 | 17 | mkdir -p build 18 | cd build 19 | export LLVM_DIR=... (path to LLVM dist) 20 | cmake -DLLVM_DIR=$LLVM_DIR .. 21 | make 22 | make test 23 | 24 | This assumes that LLVM_DIR points to dist directory, so we recommend to make sure LLVM is configured 25 | with -DCMAKE_INSTALL_PREFIX=dist and 'make install' is run. 26 | -------------------------------------------------------------------------------- /src/driver/AmdCompiler.cpp: -------------------------------------------------------------------------------- 1 | #include "AmdCompiler.h" 2 | #include 3 | #include 4 | #include 5 | 6 | #include "llvm/Support/FileSystem.h" 7 | #include "llvm/Support/Program.h" 8 | #include "llvm/Support/raw_ostream.h" 9 | #include "llvm/ADT/Triple.h" 10 | #include "llvm/Support/ManagedStatic.h" 11 | #include "llvm/Support/raw_ostream.h" 12 | #include "llvm/Support/VirtualFileSystem.h" 13 | 14 | #include "clang/Basic/DiagnosticOptions.h" 15 | #include "clang/Frontend/TextDiagnosticPrinter.h" 16 | #include "clang/Driver/Compilation.h" 17 | #include "clang/Driver/Driver.h" 18 | 19 | // implicitly needed 20 | 21 | #include "Disassembler/CodeObjectDisassembler.h" 22 | 23 | #include "llvm/MC/MCAsmInfo.h" 24 | #include "llvm/MC/MCContext.h" 25 | #include "llvm/MC/MCDisassembler/MCDisassembler.h" 26 | #include "llvm/MC/MCInstPrinter.h" 27 | #include "llvm/MC/MCInstrInfo.h" 28 | #include "llvm/MC/MCObjectFileInfo.h" 29 | #include "llvm/MC/MCObjectWriter.h" 30 | #include "llvm/MC/MCRegisterInfo.h" 31 | #include "llvm/MC/MCStreamer.h" 32 | #include "llvm/MC/MCSubtargetInfo.h" 33 | #include "llvm/Object/Binary.h" 34 | #include "llvm/Support/CommandLine.h" 35 | #include "llvm/Support/Error.h" 36 | #include "llvm/Support/MemoryBuffer.h" 37 | #include "llvm/Support/PrettyStackTrace.h" 38 | #include "llvm/Support/Signals.h" 39 | #include "llvm/Support/TargetRegistry.h" 40 | #include "llvm/Support/TargetSelect.h" 41 | #include "llvm/Support/raw_ostream.h" 42 | 43 | // in-process headers 44 | #include "clang/Driver/Tool.h" 45 | #include "clang/Frontend/CompilerInvocation.h" 46 | #include "clang/Frontend/CompilerInstance.h" 47 | #include "clang/CodeGen/CodeGenAction.h" 48 | #include "clang/CodeGen/BackendUtil.h" 49 | #include "llvm/IR/DiagnosticInfo.h" 50 | #include "llvm/IR/DiagnosticPrinter.h" 51 | #include "llvm/IR/LLVMContext.h" 52 | #include "llvm/IR/Module.h" 53 | #include "llvm/IR/Verifier.h" 54 | #include "llvm/IRReader/IRReader.h" 55 | #include "llvm/Linker/Linker.h" 56 | #include "llvm/Support/ToolOutputFile.h" 57 | #include "llvm/Bitcode/BitcodeWriter.h" 58 | #include "lld/Common/Driver.h" 59 | 60 | // in-process assembler 61 | #include "clang/Basic/Diagnostic.h" 62 | #include "clang/Driver/DriverDiagnostic.h" 63 | #include "clang/Driver/Options.h" 64 | #include "clang/Frontend/FrontendDiagnostic.h" 65 | #include "clang/Frontend/Utils.h" 66 | #include "llvm/Option/Arg.h" 67 | #include "llvm/Option/ArgList.h" 68 | #include "llvm/Option/OptTable.h" 69 | #include "llvm/MC/MCAsmBackend.h" 70 | #include "llvm/MC/MCCodeEmitter.h" 71 | #include "llvm/MC/MCParser/MCAsmParser.h" 72 | #include "llvm/MC/MCParser/MCTargetAsmParser.h" 73 | #include 74 | #include 75 | 76 | #include 77 | #include 78 | #include 79 | 80 | #ifdef _WIN32 81 | #define NODRAWTEXT // avoids #define of DT_INTERNAL 82 | #define WIN32_LEAN_AND_MEAN 1 83 | #include 84 | #include 85 | #include 86 | #else 87 | #include 88 | #include 89 | #include 90 | #include 91 | #include 92 | #endif 93 | 94 | #define QUOTE(s) #s 95 | #define STRING(s) QUOTE(s) 96 | #define AMDGCN_TRIPLE amdgcn-amd-amdhsa 97 | 98 | using namespace llvm; 99 | using namespace llvm::object; 100 | using namespace llvm::opt; 101 | using namespace clang; 102 | using namespace clang::driver; 103 | using namespace clang::driver::options; 104 | 105 | namespace amd { 106 | namespace opencl_driver { 107 | 108 | class AMDGPUCompiler; 109 | 110 | class LinkerDiagnosticInfo : public DiagnosticInfo { 111 | private: 112 | const Twine &Message; 113 | 114 | public: 115 | LinkerDiagnosticInfo(DiagnosticSeverity Severity, const Twine &Message) 116 | : DiagnosticInfo(DK_Linker, Severity), Message(Message) {} 117 | 118 | void print(DiagnosticPrinter &DP) const override { DP << Message; } 119 | }; 120 | 121 | const char* DataTypeExt(DataType type) { 122 | switch (type) { 123 | case DT_CL: return "cl"; 124 | case DT_CL_HEADER: return 0; 125 | case DT_LLVM_BC: return "bc"; 126 | case DT_LLVM_LL: return "ll"; 127 | case DT_EXECUTABLE: return "bc"; 128 | case DT_MAP: return "map"; 129 | case DT_INTERNAL: return 0; 130 | case DT_ASSEMBLY: return "s"; 131 | default: assert(false); return 0; 132 | } 133 | } 134 | 135 | bool File::WriteData(const char* ptr, size_t size) { 136 | using namespace std; 137 | ofstream out(Name().c_str(), ios::out | ios::trunc | ios::binary); 138 | if (!out.good()) { return false; } 139 | out.write(ptr, size); 140 | if (!out.good()) { return false; } 141 | return true; 142 | } 143 | 144 | bool FileExists(const std::string& name) { 145 | FILE* f = fopen(name.c_str(), "r"); 146 | if (f) { 147 | fclose(f); 148 | return true; 149 | } 150 | return false; 151 | } 152 | 153 | bool File::Exists() const { 154 | return FileExists(Name()); 155 | } 156 | 157 | class TempFile : public File { 158 | public: 159 | TempFile(Compiler* comp, DataType type, const std::string& name) 160 | : File(comp, type, name) {} 161 | ~TempFile(); 162 | }; 163 | 164 | class TempDir : public File { 165 | public: 166 | TempDir(Compiler* comp, const std::string& name) 167 | : File(comp, DT_INTERNAL, name) {} 168 | ~TempDir(); 169 | }; 170 | 171 | FileReference* BufferReference::ToInputFile(File *parent) { 172 | File* f; 173 | f = compiler->NewTempFile(Type(), Id(), parent); 174 | if (!f) { return 0; } 175 | if (!f->WriteData(ptr, size)) { return 0; } 176 | return f; 177 | } 178 | 179 | bool FileReference::ReadToString(std::string& s) { 180 | using namespace std; 181 | ifstream in(Name().c_str(), ios::in | ios::binary | ios::ate); 182 | if (!in.good()) { return false; } 183 | streampos size = in.tellg(); 184 | in.seekg(0, ios::beg); 185 | s.reserve(size); 186 | s.assign(std::istreambuf_iterator(in), std::istreambuf_iterator()); 187 | if (!in.good()) { return false; } 188 | return true; 189 | } 190 | 191 | File* BufferReference::ToOutputFile(File *parent) { 192 | assert(false); 193 | return 0; 194 | } 195 | 196 | FileReference* Buffer::ToInputFile(File *parent) { 197 | File* f = compiler->NewTempFile(Type()); 198 | if (!f->WriteData(&buf[0], buf.size())) { delete f; return 0; } 199 | return f; 200 | } 201 | 202 | File* Buffer::ToOutputFile(File* parent) { 203 | File* f = compiler->NewTempFile(Type(), "", parent); 204 | return f; 205 | } 206 | 207 | bool Buffer::ReadOutputFile(File* f) { 208 | using namespace std; 209 | ifstream in(f->Name().c_str(), ios::in | ios::binary | ios::ate); 210 | if (!in.good()) { return false; } 211 | streampos size = in.tellg(); 212 | in.seekg(0, ios::beg); 213 | buf.reserve(size); 214 | buf.assign(std::istreambuf_iterator(in), std::istreambuf_iterator()); 215 | if (!in.good()) { return false; } 216 | return true; 217 | } 218 | 219 | class TempFiles { 220 | private: 221 | #ifdef _WIN32 222 | char tempDir[MAX_PATH]; 223 | #else // _WIN32 224 | const char* tempDir; 225 | #endif // _WIN32 226 | 227 | public: 228 | TempFiles() { 229 | #ifdef _WIN32 230 | if (!GetTempPath(MAX_PATH, tempDir)) { 231 | assert(!"GetTempPath failed"); 232 | } 233 | #else 234 | tempDir = getenv("TMPDIR"); 235 | #ifdef P_tmpdir 236 | if (!tempDir) { 237 | tempDir = P_tmpdir; 238 | } 239 | #endif // P_tmpdir 240 | if (!tempDir) { 241 | tempDir = "/tmp"; 242 | } 243 | #endif 244 | } 245 | 246 | static const TempFiles& Instance() { 247 | static TempFiles instance; 248 | return instance; 249 | } 250 | 251 | #ifdef _WIN32 252 | #define getpid _getpid 253 | #endif 254 | 255 | std::string NewTempName(const char* dir, const char* prefix, const char* ext, bool pid = true) const { 256 | static std::atomic_size_t counter(1); 257 | if (!dir) { dir = tempDir; } 258 | std::ostringstream name; 259 | name << dir << "/" << prefix << getpid() << "_" << counter++; 260 | if (ext) { name << "." << ext; } 261 | return name.str(); 262 | } 263 | }; 264 | 265 | class AMDGPUCompiler : public Compiler { 266 | private: 267 | struct AMDGPUCompilerDiagnosticHandler : public DiagnosticHandler { 268 | AMDGPUCompiler *Compiler = nullptr; 269 | 270 | AMDGPUCompilerDiagnosticHandler(AMDGPUCompiler *Compiler) 271 | : Compiler(Compiler) {} 272 | 273 | bool handleDiagnostics(const DiagnosticInfo &DI) override { 274 | assert(Compiler && "Compiler cannot be nullptr"); 275 | if (Compiler->GetLogLevel() < LL_VERBOSE) { return true; } 276 | unsigned Severity = DI.getSeverity(); 277 | switch (Severity) { 278 | case DS_Error: 279 | Compiler->OS << "ERROR: "; 280 | break; 281 | default: 282 | llvm_unreachable("Only expecting errors"); 283 | } 284 | DiagnosticPrinterRawOStream DP(errs()); 285 | DI.print(DP); 286 | Compiler->OS << "\n"; 287 | return true; 288 | } 289 | }; 290 | 291 | // Helper class for representing a single invocation of the assembler. 292 | struct AssemblerInvocation { 293 | enum FileType { 294 | FT_Asm, // Assembly (.s) output, transliterate mode. 295 | FT_Null, // No output, for timing purposes. 296 | FT_Obj // Object file output. 297 | }; 298 | FileType OutputType = FT_Asm; 299 | std::string Triple; 300 | // If given, the name of the target CPU to determine which instructions are legal. 301 | std::string CPU; 302 | // The list of target specific features to enable or disable -- this should 303 | // be a list of strings starting with '+' or '-'. 304 | std::vector Features; 305 | // The list of symbol definitions. 306 | std::vector SymbolDefs; 307 | std::vector IncludePaths; 308 | unsigned NoInitialTextSection : 1; 309 | unsigned SaveTemporaryLabels : 1; 310 | unsigned GenDwarfForAssembly : 1; 311 | unsigned RelaxELFRelocations : 1; 312 | unsigned DwarfVersion = 0; 313 | std::string DwarfDebugFlags; 314 | std::string DwarfDebugProducer; 315 | std::string DebugCompilationDir; 316 | llvm::DebugCompressionType CompressDebugSections = llvm::DebugCompressionType::None; 317 | std::string MainFileName; 318 | std::string InputFile = "-"; 319 | std::vector LLVMArgs; 320 | std::string OutputPath = "-"; 321 | unsigned OutputAsmVariant = 0; 322 | unsigned ShowEncoding : 1; 323 | unsigned ShowInst : 1; 324 | unsigned RelaxAll : 1; 325 | unsigned NoExecStack : 1; 326 | unsigned FatalWarnings : 1; 327 | unsigned IncrementalLinkerCompatible : 1; 328 | // The name of the relocation model to use. 329 | std::string RelocationModel; 330 | AssemblerInvocation() 331 | : NoInitialTextSection(0), 332 | SaveTemporaryLabels(0), 333 | GenDwarfForAssembly(0), 334 | RelaxELFRelocations(0), 335 | ShowEncoding(0), 336 | ShowInst(0), 337 | RelaxAll(0), 338 | NoExecStack(0), 339 | FatalWarnings(0), 340 | IncrementalLinkerCompatible(0) {} 341 | }; 342 | 343 | std::string output; 344 | llvm::raw_string_ostream OS; 345 | IntrusiveRefCntPtr diagOpts; 346 | TextDiagnosticPrinter* diagClient; 347 | IntrusiveRefCntPtr diagID; 348 | DiagnosticsEngine diags; 349 | std::vector datas; 350 | std::string llvmBin; 351 | std::string llvmLinkExe; 352 | File* compilerTempDir; 353 | bool inprocess; 354 | LogLevel logLevel; 355 | bool printlog; 356 | bool keeptmp; 357 | const std::string clangJobName = "clang"; 358 | const std::string clangasJobName = "clang::as"; 359 | const std::string linkerJobName = "amdgpu::Linker"; 360 | const std::string clangDriverName = "clang Driver"; 361 | 362 | template 363 | inline T* AddData(T* d) { datas.push_back(d); return d; } 364 | void StartWithCommonArgs(std::vector& args); 365 | void TransformOptionsForAssembler(const std::vector& options, std::vector& transformed_options); 366 | void ResetOptionsToDefault(); 367 | // Filter out job arguments contradictory to in-process compilation 368 | ArgStringList GetJobArgsFitered(const Command& job); 369 | // Parse -mllvm options 370 | bool ParseLLVMOptions(const std::vector& options); 371 | bool PrepareCompiler(CompilerInstance& clang, const Command& job); 372 | bool PrepareAssembler(AssemblerInvocation &Opts, const Command& job); 373 | bool ExecuteCompiler(CompilerInstance& clang, BackendAction action); 374 | bool ExecuteAssembler(AssemblerInvocation &Opts); 375 | bool CreateAssemblerInvocationFromArgs(AssemblerInvocation &Opts, ArrayRef Argv); 376 | std::unique_ptr GetAssemblerOutputStream(AssemblerInvocation &Opts, bool Binary); 377 | void InitDriver(std::unique_ptr& driver); 378 | bool InvokeDriver(ArrayRef args); 379 | bool InvokeTool(ArrayRef args, const std::string& sToolName); 380 | void PrintOptions(ArrayRef args, const std::string& sToolName, bool isInProcess); 381 | void PrintJobs(const JobList &jobs); 382 | void PrintPhase(const std::string& phase, bool isInProcess); 383 | bool Return(bool retValue); 384 | void FlushLog(); 385 | File* CompilerTempDir(); 386 | bool IsVar(const std::string& sEnvVar, bool bVar); 387 | bool EmitLinkerError(LLVMContext &context, const Twine &message); 388 | std::string JoinFileName(const std::string& p1, const std::string& p2); 389 | 390 | FileReference* ToInputFile(Data* input, File *parent); 391 | 392 | File* ToOutputFile(Data* output, File *parent); 393 | 394 | bool CompileToLLVMBitcode(Data* input, Data* output, const std::vector& options); 395 | 396 | bool CompileAndLinkExecutable(Data* input, Data* output, const std::vector& options); 397 | 398 | bool DumpExecutableAsText(Buffer* exec, File* dump) override; 399 | 400 | public: 401 | AMDGPUCompiler(const std::string& llvmBin); 402 | 403 | ~AMDGPUCompiler(); 404 | 405 | const std::string& Output() override; 406 | 407 | FileReference* NewFileReference(DataType type, const std::string& path, File* parent = 0) override; 408 | 409 | File* NewFile(DataType type, const std::string& name, File* parent = 0) override; 410 | 411 | File* NewTempFile(DataType type, const std::string& name = "", File* parent = 0) override; 412 | 413 | File* NewTempDir(File* parent = 0) override; 414 | 415 | BufferReference* NewBufferReference(DataType type, const char* ptr, size_t size, const std::string& id) override; 416 | 417 | Buffer* NewBuffer(DataType type) override; 418 | 419 | bool CompileToLLVMBitcode(const std::vector& inputs, Data* output, const std::vector& options) override; 420 | 421 | bool LinkLLVMBitcode(const std::vector& inputs, Data* output, const std::vector& options) override; 422 | 423 | bool CompileAndLinkExecutable(const std::vector& inputs, Data* output, const std::vector& options) override; 424 | 425 | void SetInProcess(bool binprocess = true) override; 426 | 427 | bool IsInProcess() override { return IsVar("AMD_OCL_IN_PROCESS", inprocess); } 428 | 429 | void SetKeepTmp(bool bkeeptmp = true) override { keeptmp = bkeeptmp; } 430 | 431 | bool IsKeepTmp() override { return IsVar("AMD_OCL_KEEP_TMP", keeptmp); } 432 | 433 | void SetPrintLog(bool bprintlog = true) override { printlog = bprintlog; } 434 | 435 | bool IsPrintLog() override { return IsVar("AMD_OCL_PRINT_LOG", printlog); } 436 | 437 | void SetLogLevel(LogLevel ll) override { logLevel = ll; } 438 | 439 | LogLevel GetLogLevel() override; 440 | }; 441 | 442 | TempFile::~TempFile() { 443 | if (compiler->IsKeepTmp()) { return; } 444 | std::remove(Name().c_str()); 445 | } 446 | 447 | TempDir::~TempDir() { 448 | if (compiler->IsKeepTmp()) { return; } 449 | #ifdef _WIN32 450 | RemoveDirectory(Name().c_str()); 451 | #else // _WIN32 452 | rmdir(Name().c_str()); 453 | #endif // _WIN32 454 | } 455 | 456 | File* AMDGPUCompiler::CompilerTempDir() { 457 | if (!compilerTempDir) { compilerTempDir = NewTempDir(); } 458 | return compilerTempDir; 459 | } 460 | 461 | void AMDGPUCompiler::SetInProcess(bool binprocess) { 462 | inprocess = binprocess; 463 | if (IsInProcess()) { 464 | LLVMInitializeAMDGPUAsmParser(); 465 | LLVMInitializeAMDGPUAsmPrinter(); 466 | } 467 | } 468 | 469 | std::string AMDGPUCompiler::JoinFileName(const std::string& p1, const std::string& p2) { 470 | std::string r; 471 | if (!p1.empty()) { r += p1; r += "/"; } 472 | r += p2; 473 | return r; 474 | } 475 | 476 | std::unique_ptr 477 | AMDGPUCompiler::GetAssemblerOutputStream(AssemblerInvocation &Opts, bool Binary) { 478 | if (Opts.OutputPath.empty()) { 479 | Opts.OutputPath = "-"; 480 | } 481 | // Make sure that the Out file gets unlinked from the disk if we get a SIGINT. 482 | if (Opts.OutputPath != "-") { 483 | sys::RemoveFileOnSignal(Opts.OutputPath); 484 | } 485 | std::error_code EC; 486 | auto Out = std::make_unique(Opts.OutputPath, EC, (Binary ? sys::fs::F_None : sys::fs::F_Text)); 487 | if (EC) { 488 | diags.Report(diag::err_fe_unable_to_open_output) << Opts.OutputPath << EC.message(); 489 | return nullptr; 490 | } 491 | return Out; 492 | } 493 | 494 | bool AMDGPUCompiler::CreateAssemblerInvocationFromArgs(AssemblerInvocation &Opts, ArrayRef Argv) { 495 | bool Success = true; 496 | // Parse the arguments. 497 | const OptTable &OptTbl = getDriverOptTable(); 498 | const unsigned IncludedFlagsBitmask = options::CC1AsOption; 499 | unsigned MissingArgIndex, MissingArgCount; 500 | InputArgList Args = OptTbl.ParseArgs(Argv, MissingArgIndex, MissingArgCount, IncludedFlagsBitmask); 501 | // Check for missing argument error. 502 | if (MissingArgCount) { 503 | diags.Report(diag::err_drv_missing_argument) << Args.getArgString(MissingArgIndex) << MissingArgCount; 504 | Success = false; 505 | } 506 | // Issue errors on unknown arguments. 507 | for (const Arg *A : Args.filtered(OPT_UNKNOWN)) { 508 | diags.Report(diag::err_drv_unknown_argument) << A->getAsString(Args); 509 | Success = false; 510 | } 511 | // Construct the invocation. 512 | // Target Options 513 | Opts.Triple = llvm::Triple::normalize(Args.getLastArgValue(OPT_triple)); 514 | Opts.CPU = Args.getLastArgValue(OPT_target_cpu); 515 | Opts.Features = Args.getAllArgValues(OPT_target_feature); 516 | // Use the default target triple if unspecified. 517 | if (Opts.Triple.empty()) { 518 | Opts.Triple = llvm::sys::getDefaultTargetTriple(); 519 | } 520 | // Language Options 521 | Opts.IncludePaths = Args.getAllArgValues(OPT_I); 522 | Opts.NoInitialTextSection = Args.hasArg(OPT_n); 523 | Opts.SaveTemporaryLabels = Args.hasArg(OPT_msave_temp_labels); 524 | // Any DebugInfoKind implies GenDwarfForAssembly. 525 | Opts.GenDwarfForAssembly = Args.hasArg(OPT_debug_info_kind_EQ); 526 | if (const Arg *A = Args.getLastArg(OPT_compress_debug_sections, OPT_compress_debug_sections_EQ)) { 527 | if (A->getOption().getID() == OPT_compress_debug_sections) { 528 | // TODO: be more clever about the compression type auto-detection 529 | Opts.CompressDebugSections = llvm::DebugCompressionType::GNU; 530 | } else { 531 | Opts.CompressDebugSections = 532 | llvm::StringSwitch(A->getValue()) 533 | .Case("none", llvm::DebugCompressionType::None) 534 | .Case("zlib", llvm::DebugCompressionType::Z) 535 | .Case("zlib-gnu", llvm::DebugCompressionType::GNU) 536 | .Default(llvm::DebugCompressionType::None); 537 | } 538 | } 539 | Opts.RelaxELFRelocations = Args.hasArg(OPT_mrelax_relocations); 540 | Opts.DwarfVersion = getLastArgIntValue(Args, OPT_dwarf_version_EQ, 2, diags); 541 | Opts.DwarfDebugFlags = Args.getLastArgValue(OPT_dwarf_debug_flags); 542 | Opts.DwarfDebugProducer = Args.getLastArgValue(OPT_dwarf_debug_producer); 543 | Opts.DebugCompilationDir = Args.getLastArgValue(OPT_fdebug_compilation_dir); 544 | Opts.MainFileName = Args.getLastArgValue(OPT_main_file_name); 545 | // Frontend Options 546 | if (Args.hasArg(OPT_INPUT)) { 547 | bool First = true; 548 | for (const Arg *A : Args.filtered(OPT_INPUT)) { 549 | if (First) { 550 | Opts.InputFile = A->getValue(); 551 | First = false; 552 | } else { 553 | diags.Report(diag::err_drv_unknown_argument) << A->getAsString(Args); 554 | Success = false; 555 | } 556 | } 557 | } 558 | Opts.LLVMArgs = Args.getAllArgValues(OPT_mllvm); 559 | Opts.OutputPath = Args.getLastArgValue(OPT_o); 560 | if (Arg *A = Args.getLastArg(OPT_filetype)) { 561 | StringRef Name = A->getValue(); 562 | unsigned OutputType = StringSwitch(Name) 563 | .Case("asm", AssemblerInvocation::FT_Asm) 564 | .Case("null", AssemblerInvocation::FT_Null) 565 | .Case("obj", AssemblerInvocation::FT_Obj) 566 | .Default(~0U); 567 | if (OutputType == ~0U) { 568 | diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Name; 569 | Success = false; 570 | } else { 571 | Opts.OutputType = AssemblerInvocation::FileType(OutputType); 572 | } 573 | } 574 | // Transliterate Options 575 | Opts.OutputAsmVariant = getLastArgIntValue(Args, OPT_output_asm_variant, 0, diags); 576 | Opts.ShowEncoding = Args.hasArg(OPT_show_encoding); 577 | Opts.ShowInst = Args.hasArg(OPT_show_inst); 578 | // Assemble Options 579 | Opts.RelaxAll = Args.hasArg(OPT_mrelax_all); 580 | Opts.NoExecStack = Args.hasArg(OPT_mno_exec_stack); 581 | Opts.FatalWarnings = Args.hasArg(OPT_massembler_fatal_warnings); 582 | Opts.RelocationModel = Args.getLastArgValue(OPT_mrelocation_model, "pic"); 583 | Opts.IncrementalLinkerCompatible = Args.hasArg(OPT_mincremental_linker_compatible); 584 | Opts.SymbolDefs = Args.getAllArgValues(OPT_defsym); 585 | return Success; 586 | } 587 | 588 | bool AMDGPUCompiler::ExecuteAssembler(AssemblerInvocation &Opts) { 589 | // Get the target specific parser. 590 | std::string Error; 591 | const Target *TheTarget = TargetRegistry::lookupTarget(Opts.Triple, Error); 592 | if (!TheTarget) { 593 | return diags.Report(diag::err_target_unknown_triple) << Opts.Triple; 594 | } 595 | ErrorOr> Buffer = MemoryBuffer::getFileOrSTDIN(Opts.InputFile); 596 | if (std::error_code EC = Buffer.getError()) { 597 | Error = EC.message(); 598 | return diags.Report(diag::err_fe_error_reading) << Opts.InputFile; 599 | } 600 | SourceMgr SrcMgr; 601 | // Tell SrcMgr about this buffer, which is what the parser will pick up. 602 | SrcMgr.AddNewSourceBuffer(std::move(*Buffer), SMLoc()); 603 | // Record the location of the include directories so that the lexer can find it later. 604 | SrcMgr.setIncludeDirs(Opts.IncludePaths); 605 | std::unique_ptr MRI(TheTarget->createMCRegInfo(Opts.Triple)); 606 | assert(MRI && "Unable to create target register info!"); 607 | std::unique_ptr MAI(TheTarget->createMCAsmInfo(*MRI, Opts.Triple)); 608 | assert(MAI && "Unable to create target asm info!"); 609 | // Ensure MCAsmInfo initialization occurs before any use, otherwise sections 610 | // may be created with a combination of default and explicit settings. 611 | MAI->setCompressDebugSections(Opts.CompressDebugSections); 612 | MAI->setRelaxELFRelocations(Opts.RelaxELFRelocations); 613 | bool IsBinary = Opts.OutputType == AssemblerInvocation::FT_Obj; 614 | std::unique_ptr FDOS = GetAssemblerOutputStream(Opts, IsBinary); 615 | if (!FDOS) { return true; } 616 | // FIXME: This is not pretty. MCContext has a ptr to MCObjectFileInfo and 617 | // MCObjectFileInfo needs a MCContext reference in order to initialize itself. 618 | std::unique_ptr MOFI(new MCObjectFileInfo()); 619 | MCContext Ctx(MAI.get(), MRI.get(), MOFI.get(), &SrcMgr); 620 | bool PIC = false; 621 | if (Opts.RelocationModel == "static") { 622 | PIC = false; 623 | } else if (Opts.RelocationModel == "pic") { 624 | PIC = true; 625 | } else { 626 | assert(Opts.RelocationModel == "dynamic-no-pic" && "Invalid PIC model!"); 627 | PIC = false; 628 | } 629 | MOFI->InitMCObjectFileInfo(llvm::Triple(Opts.Triple), PIC, Ctx); 630 | if (Opts.SaveTemporaryLabels) { Ctx.setAllowTemporaryLabels(false); } 631 | if (Opts.GenDwarfForAssembly) { Ctx.setGenDwarfForAssembly(true); } 632 | if (!Opts.DwarfDebugFlags.empty()) { Ctx.setDwarfDebugFlags(StringRef(Opts.DwarfDebugFlags)); } 633 | if (!Opts.DwarfDebugProducer.empty()) { Ctx.setDwarfDebugProducer(StringRef(Opts.DwarfDebugProducer)); } 634 | if (!Opts.DebugCompilationDir.empty()) { Ctx.setCompilationDir(Opts.DebugCompilationDir); } 635 | if (!Opts.MainFileName.empty()) { Ctx.setMainFileName(StringRef(Opts.MainFileName)); } 636 | Ctx.setDwarfVersion(Opts.DwarfVersion); 637 | // Build up the feature string from the target feature list. 638 | std::string FS; 639 | if (!Opts.Features.empty()) { 640 | FS = Opts.Features[0]; 641 | for (unsigned i = 1, e = Opts.Features.size(); i != e; ++i) { 642 | FS += "," + Opts.Features[i]; 643 | } 644 | } 645 | std::unique_ptr Str; 646 | std::unique_ptr MCII(TheTarget->createMCInstrInfo()); 647 | std::unique_ptr STI(TheTarget->createMCSubtargetInfo(Opts.Triple, Opts.CPU, FS)); 648 | raw_pwrite_stream *Out = FDOS.get(); 649 | std::unique_ptr BOS; 650 | // FIXME: There is a bit of code duplication with addPassesToEmitFile. 651 | if (Opts.OutputType == AssemblerInvocation::FT_Asm) { 652 | MCInstPrinter *IP = TheTarget->createMCInstPrinter(llvm::Triple(Opts.Triple), Opts.OutputAsmVariant, *MAI, *MCII, *MRI); 653 | std::unique_ptr MCE; 654 | std::unique_ptr MAB; 655 | if (Opts.ShowEncoding) { 656 | MCE.reset(TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx)); 657 | MCTargetOptions Options; 658 | MAB.reset(TheTarget->createMCAsmBackend(*STI, *MRI, Options)); 659 | } 660 | auto FOut = std::make_unique(*Out); 661 | Str.reset(TheTarget->createAsmStreamer(Ctx, std::move(FOut), 662 | /*asmverbose*/ true, /*useDwarfDirectory*/ true, IP, std::move(MCE), 663 | std::move(MAB), Opts.ShowInst)); 664 | } else if (Opts.OutputType == AssemblerInvocation::FT_Null) { 665 | Str.reset(createNullStreamer(Ctx)); 666 | } else { 667 | assert(Opts.OutputType == AssemblerInvocation::FT_Obj && "Invalid file type!"); 668 | if (!FDOS->supportsSeeking()) { 669 | BOS = std::make_unique(*FDOS); 670 | Out = BOS.get(); 671 | } 672 | MCCodeEmitter *CE = TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx); 673 | MCTargetOptions Options; 674 | MCAsmBackend *MAB = TheTarget->createMCAsmBackend(*STI, *MRI, Options); 675 | llvm::Triple T(Opts.Triple); 676 | Str.reset(TheTarget->createMCObjectStreamer(T, Ctx, 677 | std::unique_ptr(MAB), MAB->createObjectWriter(*Out), 678 | std::unique_ptr(CE), *STI, Opts.RelaxAll, 679 | Opts.IncrementalLinkerCompatible, /*DWARFMustBeAtTheEnd*/ true)); 680 | Str.get()->InitSections(Opts.NoExecStack); 681 | } 682 | bool Failed = false; 683 | std::unique_ptr Parser(createMCAsmParser(SrcMgr, Ctx, *Str.get(), *MAI)); 684 | // FIXME: init MCTargetOptions from sanitizer flags here. 685 | MCTargetOptions Options; 686 | std::unique_ptr TAP(TheTarget->createMCAsmParser(*STI, *Parser, *MCII, Options)); 687 | if (!TAP) { 688 | Failed = diags.Report(diag::err_target_unknown_triple) << Opts.Triple; 689 | } 690 | // Set values for symbols, if any. 691 | for (auto &S : Opts.SymbolDefs) { 692 | auto Pair = StringRef(S).split('='); 693 | auto Sym = Pair.first; 694 | auto Val = Pair.second; 695 | int64_t Value; 696 | // We have already error checked this in the driver. 697 | Val.getAsInteger(0, Value); 698 | Ctx.setSymbolValue(Parser->getStreamer(), Sym, Value); 699 | } 700 | if (!Failed) { 701 | Parser->setTargetParser(*TAP.get()); 702 | Failed = Parser->Run(Opts.NoInitialTextSection); 703 | } 704 | // Close Streamer first. It might have a reference to the output stream. 705 | Str.reset(); 706 | // Close the output stream early. 707 | BOS.reset(); 708 | FDOS.reset(); 709 | // Delete output file if there were errors. 710 | if (Failed && Opts.OutputPath != "-") { 711 | sys::fs::remove(Opts.OutputPath); 712 | } 713 | return Failed; 714 | } 715 | 716 | bool AMDGPUCompiler::ExecuteCompiler(CompilerInstance& clang, BackendAction action) { 717 | std::unique_ptr Act; 718 | switch (action) { 719 | case Backend_EmitBC: 720 | Act = std::unique_ptr(new EmitBCAction()); 721 | break; 722 | case Backend_EmitObj: 723 | Act = std::unique_ptr(new EmitObjAction()); 724 | break; 725 | default: { return false; } 726 | } 727 | if (!Act.get()) { return false; } 728 | if (!clang.ExecuteAction(*Act)) { return false; } 729 | return true; 730 | } 731 | 732 | void AMDGPUCompiler::InitDriver(std::unique_ptr& driver) { 733 | driver->CCPrintOptions = !!::getenv("CC_PRINT_OPTIONS"); 734 | driver->setTitle("AMDGPU OpenCL driver"); 735 | driver->setCheckInputsExist(false); 736 | } 737 | 738 | void AMDGPUCompiler::StartWithCommonArgs(std::vector& args) { 739 | // Workaround for flawed Driver::BuildCompilation(...) implementation, 740 | // which eliminates 1st argument, cause it actually awaits argv[0]. 741 | args.clear(); 742 | args.push_back(""); 743 | } 744 | 745 | ArgStringList AMDGPUCompiler::GetJobArgsFitered(const Command& job) { 746 | std::string arg; 747 | std::string sJobName(job.getCreator().getName()); 748 | if (sJobName == clangJobName) { 749 | arg = "-disable-free"; 750 | } else if (sJobName == clangasJobName) { 751 | arg = "-cc1as"; 752 | } else { 753 | return std::move(job.getArguments()); 754 | } 755 | ArgStringList args(job.getArguments()); 756 | ArgStringList::iterator it = std::find(args.begin(), args.end(), arg); 757 | if (it != args.end()) { 758 | args.erase(it); 759 | } 760 | return args; 761 | } 762 | 763 | bool AMDGPUCompiler::ParseLLVMOptions(const std::vector& options) { 764 | if (options.empty()) { return true; } 765 | std::vector args; 766 | for (auto A : options) { 767 | args.push_back(""); 768 | args.push_back(A.c_str()); 769 | if (!cl::ParseCommandLineOptions(args.size(), &args[0], "-mllvm options parsing")) { return false; } 770 | args.clear(); 771 | } 772 | return true; 773 | } 774 | 775 | void AMDGPUCompiler::ResetOptionsToDefault() { 776 | cl::ResetAllOptionOccurrences(); 777 | for (auto SC : cl::getRegisteredSubcommands()) { 778 | for (auto &OM : SC->OptionsMap) { 779 | cl::Option *O = OM.second; 780 | O->setDefault(); 781 | } 782 | } 783 | } 784 | 785 | bool AMDGPUCompiler::PrepareCompiler(CompilerInstance& clang, const Command& job) { 786 | clang.createDiagnostics(); 787 | if (!clang.hasDiagnostics()) { return false; } 788 | ResetOptionsToDefault(); 789 | const ArgStringList args = GetJobArgsFitered(job); 790 | if (!CompilerInvocation::CreateFromArgs(clang.getInvocation(), args, 791 | clang.getDiagnostics())) { return false; } 792 | if (!ParseLLVMOptions(clang.getFrontendOpts().LLVMArgs)) { return false; } 793 | return true; 794 | } 795 | 796 | bool AMDGPUCompiler::PrepareAssembler(AssemblerInvocation &Opts, const Command& job) { 797 | ResetOptionsToDefault(); 798 | if (!CreateAssemblerInvocationFromArgs(Opts, llvm::makeArrayRef(GetJobArgsFitered(job)))) { return false; } 799 | if (diags.hasErrorOccurred()) { return false; } 800 | if (!ParseLLVMOptions(Opts.LLVMArgs)) { return false; } 801 | return true; 802 | } 803 | 804 | bool AMDGPUCompiler::IsVar(const std::string& sEnvVar, bool bVar) { 805 | const char* env = getenv(sEnvVar.c_str()); 806 | if (env) { 807 | if (env[0] != '0') { return true; } 808 | else { return false; } 809 | } 810 | return bVar; 811 | } 812 | 813 | LogLevel AMDGPUCompiler::GetLogLevel() { 814 | const char* log_level = getenv("AMD_OCL_LOG_LEVEL"); 815 | if (log_level) { 816 | std::stringstream ss(log_level); 817 | unsigned ll; 818 | ss >> ll; 819 | switch (ll) { 820 | default: 821 | case LL_QUIET: return LL_QUIET; 822 | case LL_ERRORS: return LL_ERRORS; 823 | case LL_LLVM_ONLY: return LL_LLVM_ONLY; 824 | case LL_VERBOSE: return LL_VERBOSE; 825 | } 826 | } 827 | return logLevel; 828 | } 829 | 830 | const std::string& AMDGPUCompiler::Output() { 831 | output = {}; 832 | if (GetLogLevel() > LL_QUIET) { OS.flush(); } 833 | return output; 834 | } 835 | 836 | void AMDGPUCompiler::FlushLog() { 837 | if (!IsPrintLog()) 838 | return; 839 | static std::mutex m_screen; 840 | m_screen.lock(); 841 | std::cout << Output(); 842 | m_screen.unlock(); 843 | } 844 | 845 | bool AMDGPUCompiler::Return(bool retValue) { 846 | FlushLog(); 847 | return retValue; 848 | } 849 | 850 | void AMDGPUCompiler::PrintJobs(const JobList &jobs) { 851 | if (GetLogLevel() < LL_VERBOSE || jobs.empty()) { return; } 852 | OS << "\n[AMD OCL] " << jobs.size() << " job" << (jobs.size() == 1 ? "" : "s") << ":\n"; 853 | int i = 1; 854 | for (auto const & J : jobs) { 855 | std::string sJobName(J.getCreator().getName()); 856 | OS << (i > 1 ? "\n" : "") << " JOB [" << i << "] " << sJobName << "\n"; 857 | for (auto A : GetJobArgsFitered(J)) { 858 | OS << " " << A << "\n"; 859 | } 860 | ++i; 861 | } 862 | OS << "\n"; 863 | FlushLog(); 864 | } 865 | 866 | void AMDGPUCompiler::PrintPhase(const std::string& phase, bool isInProcess) { 867 | if (GetLogLevel() < LL_VERBOSE) { return; } 868 | OS << "\n[AMD OCL] " << "Phase: " << phase << (isInProcess ? " [in-process]" : "") << "\n"; 869 | FlushLog(); 870 | } 871 | 872 | void AMDGPUCompiler::PrintOptions(ArrayRef args, const std::string& sToolName, bool isInProcess) { 873 | if (GetLogLevel() < LL_VERBOSE) { return; } 874 | OS << "\n[AMD OCL] " << sToolName << (isInProcess ? " [in-process]" : "") << " options:\n"; 875 | for (const char* A : args) { 876 | OS << " " << A << "\n"; 877 | } 878 | OS << "\n"; 879 | FlushLog(); 880 | } 881 | 882 | AMDGPUCompiler::AMDGPUCompiler(const std::string& llvmBin_) 883 | : OS(output), 884 | diagOpts(new DiagnosticOptions()), 885 | diagClient(new TextDiagnosticPrinter(OS, &*diagOpts)), 886 | diags(diagID, &*diagOpts, &*diagClient), 887 | llvmBin(llvmBin_), 888 | llvmLinkExe(llvmBin + "/llvm-link"), 889 | compilerTempDir(0), 890 | inprocess(true), 891 | logLevel(LL_ERRORS), 892 | printlog(false), 893 | keeptmp(false) { 894 | LLVMInitializeAMDGPUTarget(); 895 | LLVMInitializeAMDGPUTargetInfo(); 896 | LLVMInitializeAMDGPUTargetMC(); 897 | LLVMInitializeAMDGPUDisassembler(); 898 | if (IsInProcess()) { 899 | LLVMInitializeAMDGPUAsmParser(); 900 | LLVMInitializeAMDGPUAsmPrinter(); 901 | } 902 | } 903 | 904 | AMDGPUCompiler::~AMDGPUCompiler() { 905 | for (size_t i = datas.size(); i > 0; --i) { 906 | delete datas[i-1]; 907 | } 908 | } 909 | 910 | bool AMDGPUCompiler::InvokeDriver(ArrayRef args) { 911 | std::unique_ptr driver(new Driver(llvmBin + "/clang", STRING(AMDGCN_TRIPLE), diags)); 912 | InitDriver(driver); 913 | std::unique_ptr C(driver->BuildCompilation(args)); 914 | PrintJobs(C->getJobs()); 915 | File* out = NewTempFile(DT_INTERNAL); 916 | File* err = NewTempFile(DT_INTERNAL); 917 | Optional Redirects[] = 918 | {None, StringRef(out->Name()), StringRef(err->Name())}; 919 | C->Redirect(Redirects); 920 | int Res = 0; 921 | SmallVector, 4> failingCommands; 922 | if (C.get()) { 923 | Res = driver->ExecuteCompilation(*C, failingCommands); 924 | } 925 | for (const auto &P : failingCommands) { 926 | int CommandRes = P.first; 927 | const Command *failingCommand = P.second; 928 | if (!Res) { Res = CommandRes; } 929 | // If result status is < 0, then the driver command signalled an error. 930 | // If result status is 70, then the driver command reported a fatal error. 931 | // On Windows, abort will return an exit code of 3. In these cases, 932 | // generate additional diagnostic information if possible. 933 | bool DiagnoseCrash = CommandRes < 0 || CommandRes == 70; 934 | #ifdef LLVM_ON_WIN32 935 | DiagnoseCrash |= CommandRes == 3; 936 | #endif 937 | if (DiagnoseCrash) { 938 | driver->generateCompilationDiagnostics(*C, *failingCommand); 939 | break; 940 | } 941 | } 942 | std::string outStr, errStr; 943 | out->ReadToString(outStr); 944 | err->ReadToString(errStr); 945 | if (GetLogLevel() >= LL_LLVM_ONLY && !outStr.empty()) { OS << outStr; } 946 | if (GetLogLevel() >= LL_ERRORS && !errStr.empty()) { OS << errStr; } 947 | #ifdef LLVM_ON_WIN32 948 | // Exit status should not be negative on Win32, unless abnormal termination. 949 | // Once abnormal termiation was caught, negative status should not be 950 | // propagated. 951 | if (Res < 0) 952 | Res = 1; 953 | #endif 954 | return Res == 0; 955 | } 956 | 957 | bool AMDGPUCompiler::InvokeTool(ArrayRef args, const std::string& sToolName) { 958 | PrintOptions(args, sToolName, false); 959 | SmallVector args1; 960 | args1.push_back(sToolName.c_str()); 961 | for (const char *arg : args) { args1.push_back(arg); } 962 | args1.push_back(nullptr); 963 | File* out = NewTempFile(DT_INTERNAL); 964 | File* err = NewTempFile(DT_INTERNAL); 965 | Optional Redirects[] = 966 | {None, StringRef(out->Name()), StringRef(err->Name())}; 967 | Optional> Env; 968 | auto Args = llvm::toStringRefArray(args1.data()); 969 | int res = llvm::sys::ExecuteAndWait(sToolName, Args, Env, Redirects); 970 | std::string outStr, errStr; 971 | out->ReadToString(outStr); 972 | err->ReadToString(errStr); 973 | if (GetLogLevel() >= LL_LLVM_ONLY && !outStr.empty()) { OS << outStr; } 974 | if (GetLogLevel() >= LL_ERRORS && !errStr.empty()) { OS << errStr; } 975 | return res == 0; 976 | } 977 | 978 | FileReference* AMDGPUCompiler::ToInputFile(Data* input, File *parent) { 979 | return input->ToInputFile(parent); 980 | } 981 | 982 | File* AMDGPUCompiler::ToOutputFile(Data* output, File* parent) { 983 | return output->ToOutputFile(parent); 984 | } 985 | 986 | File* AMDGPUCompiler::NewFile(DataType type, const std::string& name, File* parent) { 987 | std::string fname = parent ? JoinFileName(parent->Name(), name) : name; 988 | return AddData(new File(this, type, fname)); 989 | } 990 | 991 | FileReference* AMDGPUCompiler::NewFileReference(DataType type, const std::string& name, File* parent) { 992 | std::string fname = parent ? JoinFileName(parent->Name(), name) : name; 993 | return AddData(new FileReference(this, type, fname)); 994 | } 995 | 996 | File* AMDGPUCompiler::NewTempFile(DataType type, const std::string& name, File* parent) { 997 | if (!parent) { parent = CompilerTempDir(); } 998 | const char* dir = parent->Name().c_str(); 999 | const char* ext = DataTypeExt(type); 1000 | bool pid = !parent; 1001 | std::string fname = name.empty() ? 1002 | TempFiles::Instance().NewTempName(dir, "t_", ext, pid) : 1003 | JoinFileName(parent->Name(), name); 1004 | if (FileExists(fname)) { return 0; } 1005 | return AddData(new TempFile(this, type, fname)); 1006 | } 1007 | 1008 | File* AMDGPUCompiler::NewTempDir(File* parent) { 1009 | const char* dir = parent ? parent->Name().c_str() : 0; 1010 | bool pid = !parent; 1011 | std::string name = TempFiles::Instance().NewTempName(dir, "AMD_", 0, pid); 1012 | #ifdef _WIN32 1013 | CreateDirectory(name.c_str(), NULL); 1014 | #else // _WIN32 1015 | mkdir(name.c_str(), 0700); 1016 | #endif // _WIN32 1017 | return AddData(new TempDir(this, name)); 1018 | } 1019 | 1020 | BufferReference* AMDGPUCompiler::NewBufferReference(DataType type, const char* ptr, size_t size, const std::string& id) { 1021 | return AddData(new BufferReference(this, type, ptr, size, id)); 1022 | } 1023 | 1024 | Buffer* AMDGPUCompiler::NewBuffer(DataType type) { 1025 | return AddData(new Buffer(this, type)); 1026 | } 1027 | 1028 | bool AMDGPUCompiler::CompileToLLVMBitcode(Data* input, Data* output, const std::vector& options) { 1029 | PrintPhase("CompileToLLVMBitcode", IsInProcess()); 1030 | std::vector args; 1031 | StartWithCommonArgs(args); 1032 | if (input->Type() == DT_ASSEMBLY) { 1033 | return false; 1034 | } else { 1035 | args.push_back("-x"); 1036 | args.push_back("cl"); 1037 | } 1038 | args.push_back("-c"); 1039 | args.push_back("-emit-llvm"); 1040 | FileReference* inputFile = ToInputFile(input, CompilerTempDir()); 1041 | if (!inputFile) 1042 | return Return(false); 1043 | args.push_back(inputFile->Name().c_str()); 1044 | 1045 | File* bcFile = ToOutputFile(output, CompilerTempDir()); 1046 | if (!bcFile) 1047 | return Return(false); 1048 | 1049 | args.push_back("-o"); 1050 | args.push_back(bcFile->Name().c_str()); 1051 | for (const std::string& s : options) { 1052 | args.push_back(s.c_str()); 1053 | } 1054 | PrintOptions(args, clangDriverName, IsInProcess()); 1055 | if (IsInProcess()) { 1056 | std::unique_ptr driver(new Driver("", STRING(AMDGCN_TRIPLE), diags)); 1057 | InitDriver(driver); 1058 | std::unique_ptr C(driver->BuildCompilation(args)); 1059 | const JobList &Jobs = C->getJobs(); 1060 | PrintJobs(Jobs); 1061 | CompilerInstance Clang; 1062 | if (!PrepareCompiler(Clang, *Jobs.begin())) { return Return(false); } 1063 | if (!ExecuteCompiler(Clang, Backend_EmitBC)) { return Return(false); } 1064 | } else { 1065 | if (!InvokeDriver(args)) { return Return(false); } 1066 | } 1067 | return Return(output->ReadOutputFile(bcFile)); 1068 | } 1069 | 1070 | const std::vector emptyOptions; 1071 | 1072 | bool AMDGPUCompiler::CompileToLLVMBitcode(const std::vector& inputs, Data* output, const std::vector& options) { 1073 | if (inputs.size() == 1) { 1074 | return CompileToLLVMBitcode(inputs[0], output, options); 1075 | } else { 1076 | std::vector bcFiles; 1077 | std::vector xoptions; 1078 | File* includeDir = 0; 1079 | for (Data* input : inputs) { 1080 | if (input->Type() == DT_CL_HEADER) { 1081 | if (!includeDir) { 1082 | includeDir = NewTempDir(CompilerTempDir()); 1083 | xoptions.push_back("-I" + includeDir->Name()); 1084 | } 1085 | ToInputFile(input, includeDir); 1086 | } 1087 | } 1088 | for (const std::string& o : options) { xoptions.push_back(o); } 1089 | for (Data* input : inputs) { 1090 | if (input->Type() == DT_CL_HEADER) { continue; } 1091 | File* bcFile = NewTempFile(DT_LLVM_BC); 1092 | if (!CompileToLLVMBitcode(input, bcFile, xoptions)) { return false; } 1093 | bcFiles.push_back(bcFile); 1094 | } 1095 | return LinkLLVMBitcode(bcFiles, output, emptyOptions); 1096 | } 1097 | } 1098 | 1099 | bool AMDGPUCompiler::EmitLinkerError(LLVMContext &context, const Twine &message) { 1100 | context.diagnose(LinkerDiagnosticInfo(DS_Error, message)); 1101 | return false; 1102 | } 1103 | 1104 | bool AMDGPUCompiler::LinkLLVMBitcode(const std::vector& inputs, Data* output, const std::vector& options) { 1105 | PrintPhase("LinkLLVMBitcode", IsInProcess()); 1106 | std::vector args; 1107 | for (Data* input : inputs) { 1108 | FileReference* inputFile = ToInputFile(input, CompilerTempDir()); 1109 | args.push_back(inputFile->Name().c_str()); 1110 | } 1111 | File* outputFile = ToOutputFile(output, CompilerTempDir()); 1112 | if (!options.empty()) { 1113 | std::vector argv; 1114 | for (auto option : options) { 1115 | if (IsInProcess()) { 1116 | argv.push_back(""); 1117 | argv.push_back(option.c_str()); 1118 | if (!cl::ParseCommandLineOptions(argv.size(), &argv[0], "llvm linker")) { 1119 | return Return(false); 1120 | } 1121 | argv.clear(); 1122 | } else { 1123 | args.push_back(option.c_str()); 1124 | } 1125 | } 1126 | } 1127 | if (!IsInProcess()) { 1128 | args.push_back("-o"); 1129 | args.push_back(outputFile->Name().c_str()); 1130 | } 1131 | if (IsInProcess()) { 1132 | PrintOptions(args, "llvm linker", IsInProcess()); 1133 | LLVMContext context; 1134 | context.setDiagnosticHandler( 1135 | std::make_unique(this), true); 1136 | auto Composite = std::make_unique("composite", context); 1137 | Linker L(*Composite); 1138 | unsigned ApplicableFlags = Linker::Flags::None; 1139 | for (auto arg : args) { 1140 | SMDiagnostic error; 1141 | auto m = getLazyIRFileModule(arg, error, context); 1142 | if (!m.get()) { 1143 | return Return(EmitLinkerError(context, "The module '" + Twine(arg) + "' loading failed.")); 1144 | } 1145 | if (verifyModule(*m, &errs())) { 1146 | return Return(EmitLinkerError(context, "The loaded module '" + Twine(arg) + "' to link is broken.")); 1147 | } 1148 | if (GetLogLevel() >= LL_LLVM_ONLY) { 1149 | OS << "[AMD OCL] Linking in '" << arg << "'" << "\n"; 1150 | } 1151 | if (L.linkInModule(std::move(m), ApplicableFlags)) { 1152 | return Return(EmitLinkerError(context, "The module '" + Twine(arg) + "' is not linked.")); 1153 | } 1154 | } 1155 | if (verifyModule(*Composite, &errs())) { 1156 | return Return(EmitLinkerError(context, "The linked module '" + outputFile->Name() + "' is broken.")); 1157 | } 1158 | std::error_code ec; 1159 | llvm::ToolOutputFile out(outputFile->Name(), ec, sys::fs::F_None); 1160 | WriteBitcodeToFile(*Composite.get(), out.os()); 1161 | out.keep(); 1162 | } else { 1163 | if (!InvokeTool(args, llvmLinkExe)) { return Return(false); } 1164 | } 1165 | return Return(output->ReadOutputFile(outputFile)); 1166 | } 1167 | 1168 | void AMDGPUCompiler::TransformOptionsForAssembler(const std::vector& options, std::vector& transformed_options) { 1169 | transformed_options.push_back("-x"); 1170 | transformed_options.push_back("assembler"); 1171 | transformed_options.push_back("-integrated-as"); 1172 | const std::string swa = "-Wa,"; 1173 | const std::string sdef = swa + "-defsym,"; 1174 | const std::string sinc = swa +"-I,"; 1175 | std::string transformed_option; 1176 | bool bcompound = false; 1177 | bool btransform = false; 1178 | for (auto &option : options) { 1179 | btransform = false; 1180 | if (bcompound) { 1181 | transformed_option += option; 1182 | bcompound = false; 1183 | btransform = true; 1184 | } else { 1185 | if (option.find("-D") == 0) { 1186 | transformed_option = sdef; 1187 | btransform = true; 1188 | } else if (option.find("-I") == 0) { 1189 | transformed_option = sinc; 1190 | btransform = true; 1191 | } 1192 | if (btransform) { 1193 | if (option.size() > 2) { 1194 | transformed_option += option.substr(2); 1195 | } else { 1196 | bcompound = true; 1197 | continue; 1198 | } 1199 | } 1200 | } 1201 | if (btransform) { 1202 | transformed_options.push_back(transformed_option); 1203 | } else { 1204 | transformed_options.push_back(option); 1205 | } 1206 | } 1207 | } 1208 | 1209 | bool AMDGPUCompiler::CompileAndLinkExecutable(Data* input, Data* output, const std::vector& options) { 1210 | PrintPhase("CompileAndLinkExecutable", IsInProcess()); 1211 | std::vector args; 1212 | StartWithCommonArgs(args); 1213 | FileReference* inputFile = ToInputFile(input, CompilerTempDir()); 1214 | args.push_back(inputFile->Name().c_str()); 1215 | File* outputFile = ToOutputFile(output, CompilerTempDir()); 1216 | args.push_back("-o"); args.push_back(outputFile->Name().c_str()); 1217 | std::vector transformed_options; 1218 | const std::vector* opts = &options; 1219 | if (input->Type() == DT_ASSEMBLY) { 1220 | TransformOptionsForAssembler(options, transformed_options); 1221 | opts = &transformed_options; 1222 | } 1223 | for (auto &option : *opts) { 1224 | args.push_back(option.c_str()); 1225 | } 1226 | PrintOptions(args, clangDriverName, IsInProcess()); 1227 | if (IsInProcess()) { 1228 | std::unique_ptr driver(new Driver("", STRING(AMDGCN_TRIPLE), diags)); 1229 | InitDriver(driver); 1230 | std::unique_ptr C(driver->BuildCompilation(args)); 1231 | const JobList &Jobs = C->getJobs(); 1232 | PrintJobs(Jobs); 1233 | int i = 1; 1234 | for (auto const & J : Jobs) { 1235 | if (Jobs.size() == 2) { 1236 | std::string sJobName(J.getCreator().getName()); 1237 | if (i == 1 && (sJobName == clangJobName || sJobName == clangasJobName)) { 1238 | switch (input->Type()) { 1239 | case DT_ASSEMBLY: { 1240 | AssemblerInvocation Asm; 1241 | if (!PrepareAssembler(Asm, J)) { return Return(false); } 1242 | if (ExecuteAssembler(Asm)) { return Return(false); } 1243 | break; 1244 | } 1245 | default: { 1246 | CompilerInstance Clang; 1247 | if (!PrepareCompiler(Clang, J)) { return Return(false); } 1248 | if (!ExecuteCompiler(Clang, Backend_EmitObj)) { return Return(false); } 1249 | break; 1250 | } 1251 | } 1252 | } else if (i == 2 && sJobName == linkerJobName) { 1253 | llvm::opt::ArgStringList Args(J.getArguments()); 1254 | Args.insert(Args.begin(), ""); 1255 | ArrayRef ArgRefs = llvm::makeArrayRef(Args); 1256 | static std::mutex m_screen; 1257 | m_screen.lock(); 1258 | bool lldRet = lld::elf::link(ArgRefs, false, OS); 1259 | m_screen.unlock(); 1260 | if (!lldRet) { return Return(false); } 1261 | } else { return Return(false); } 1262 | i++; 1263 | } else { return Return(false); } 1264 | } 1265 | } else { 1266 | if (!InvokeDriver(args)) { return Return(false); } 1267 | } 1268 | return Return(output->ReadOutputFile(outputFile)); 1269 | } 1270 | 1271 | bool AMDGPUCompiler::CompileAndLinkExecutable(const std::vector& inputs, Data* output, const std::vector& options) { 1272 | if (inputs.size() == 1) { 1273 | return CompileAndLinkExecutable(inputs[0], output, options); 1274 | } else { 1275 | File* bcFile = NewTempFile(DT_LLVM_BC); 1276 | if (!CompileToLLVMBitcode(inputs, bcFile, options)) { return false; } 1277 | return CompileAndLinkExecutable(bcFile, output, options); 1278 | } 1279 | } 1280 | 1281 | bool AMDGPUCompiler::DumpExecutableAsText(Buffer* exec, File* dump) { 1282 | Triple TheTriple(STRING(AMDGCN_TRIPLE)); 1283 | const std::string TripleStr = TheTriple.normalize(); 1284 | 1285 | StringRef execRef(exec->Ptr(), exec->Size()); 1286 | std::unique_ptr execBuffer(MemoryBuffer::getMemBuffer(execRef, "", false)); 1287 | Expected> execBinary = createBinary(*execBuffer); 1288 | if (!execBinary) { return false; } 1289 | std::unique_ptr Binary(execBinary.get().release()); 1290 | if (!Binary) { return false; } 1291 | // setup context 1292 | 1293 | std::string Error; 1294 | const Target *TheTarget = llvm::TargetRegistry::lookupTarget(TripleStr, 1295 | Error); 1296 | if (!TheTarget) 1297 | report_fatal_error("Could not lookup target: " + Twine(Error)); 1298 | 1299 | std::unique_ptr MRI(TheTarget->createMCRegInfo(TripleStr)); 1300 | if (!MRI) { return false; } 1301 | std::unique_ptr AsmInfo(TheTarget->createMCAsmInfo(*MRI, TripleStr)); 1302 | if (!AsmInfo) { return false; } 1303 | std::unique_ptr MII(TheTarget->createMCInstrInfo()); 1304 | if (!MII) { return false; } 1305 | MCObjectFileInfo MOFI; 1306 | MCContext Ctx(AsmInfo.get(), MRI.get(), &MOFI); 1307 | MOFI.InitMCObjectFileInfo(TheTriple, false, Ctx); 1308 | int AsmPrinterVariant = AsmInfo->getAssemblerDialect(); 1309 | MCInstPrinter *IP(TheTarget->createMCInstPrinter(TheTriple, 1310 | AsmPrinterVariant, 1311 | *AsmInfo, *MII, *MRI)); 1312 | if (!IP) { report_fatal_error("error: no instruction printer"); } 1313 | std::error_code EC; 1314 | raw_fd_ostream FO(dump->Name(), EC, sys::fs::F_None); 1315 | auto FOut = std::make_unique(FO); 1316 | std::unique_ptr MCS( 1317 | TheTarget->createAsmStreamer(Ctx, std::move(FOut), true, false, IP, 1318 | nullptr, nullptr, false)); 1319 | CodeObjectDisassembler CODisasm(&Ctx, TripleStr, IP, MCS->getTargetStreamer()); 1320 | EC = CODisasm.Disassemble(Binary->getMemoryBufferRef(), errs()); 1321 | if (EC) { return false; } 1322 | return true; 1323 | } 1324 | 1325 | Compiler* CompilerFactory::CreateAMDGPUCompiler(const std::string& llvmBin) { 1326 | return new AMDGPUCompiler(llvmBin); 1327 | } 1328 | 1329 | } 1330 | } 1331 | -------------------------------------------------------------------------------- /src/driver/AmdCompiler.h: -------------------------------------------------------------------------------- 1 | #ifndef AMD_COMPILER_DRIVER_H 2 | #define AMD_COMPILER_DRIVER_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace amd { 9 | namespace opencl_driver { 10 | 11 | /* 12 | * DataType is a kind of input, output or intermediate represenation 13 | * to the compiler. 14 | */ 15 | enum DataType { 16 | DT_CL, 17 | DT_CL_HEADER, 18 | DT_LLVM_BC, 19 | DT_LLVM_LL, 20 | DT_EXECUTABLE, 21 | DT_MAP, 22 | DT_INTERNAL, 23 | DT_ASSEMBLY, 24 | }; 25 | 26 | enum LogLevel { 27 | LL_QUIET = 0, 28 | LL_ERRORS, 29 | LL_LLVM_ONLY, 30 | LL_VERBOSE, 31 | }; 32 | 33 | class FileReference; 34 | class File; 35 | class Compiler; 36 | 37 | /* 38 | * Data is a container for input, output or intermediate representation. 39 | * 40 | * id is used for DT_CL_HEADER to specify the header name. 41 | */ 42 | class Data { 43 | private: 44 | std::string id; 45 | DataType type; 46 | 47 | protected: 48 | Compiler* compiler; 49 | 50 | public: 51 | Data(Compiler* comp, DataType type_, const std::string& id_ = "") 52 | : id(id_), type(type_), compiler(comp) {} 53 | virtual ~Data() {} 54 | DataType Type() const { return type; } 55 | const std::string& Id() const { return id; } 56 | virtual bool IsReadOnly() const = 0; 57 | virtual FileReference* ToInputFile(File *parent) = 0; 58 | virtual File* ToOutputFile(File *parent) = 0; 59 | virtual bool ReadOutputFile(File* f) = 0; 60 | Compiler* GetCompiler() { return compiler; } 61 | }; 62 | 63 | bool FileExists(const std::string& name); 64 | 65 | /* 66 | * FileReference is a reference to some file. 67 | */ 68 | class FileReference : public Data { 69 | private: 70 | std::string name; 71 | 72 | public: 73 | FileReference(Compiler* comp, DataType type, const std::string& name_) 74 | : Data(comp, type), 75 | name(name_) {} 76 | 77 | bool IsReadOnly() const override { return true; } 78 | const std::string& Name() const { return name; } 79 | FileReference* ToInputFile(File *parent) override { return this; } 80 | File* ToOutputFile(File *parent) override { assert(false); return 0; } 81 | bool ReadOutputFile(File* f) override { assert(false); return false; } 82 | bool ReadToString(std::string& s); 83 | bool Exists() const; 84 | }; 85 | 86 | /* 87 | * File is a file that can also be modified. 88 | */ 89 | class File : public FileReference { 90 | public: 91 | File(Compiler* comp, DataType type, const std::string& name) 92 | : FileReference(comp, type, name) {} 93 | 94 | bool IsReadOnly() const override { return false; } 95 | File* ToOutputFile(File *parent) override { return this; } 96 | bool ReadOutputFile(File* f) override { assert(this == f); return true; } 97 | bool WriteData(const char* ptr, size_t size); 98 | bool Exists() const; 99 | }; 100 | 101 | /* 102 | * BufferReference provides read-only access to user-managed buffer. 103 | */ 104 | class BufferReference : public Data { 105 | private: 106 | const char* ptr; 107 | size_t size; 108 | 109 | public: 110 | BufferReference(Compiler* comp, DataType type, const char* ptr_, size_t size_, const std::string& id) 111 | : Data(comp, type, id), 112 | ptr(ptr_), size(size_) {} 113 | 114 | bool IsReadOnly() const override { return true; } 115 | const char* Ptr() const { return ptr; } 116 | size_t Size() const { return size; } 117 | FileReference* ToInputFile(File *parent) override; 118 | File* ToOutputFile(File *parent) override; 119 | bool ReadOutputFile(File* f) override { assert(false); return false; } 120 | }; 121 | 122 | /* 123 | * Buffer is a modifiable buffer backed by its own storage. 124 | */ 125 | class Buffer : public Data { 126 | private: 127 | std::vector buf; 128 | 129 | public: 130 | Buffer(Compiler* comp, DataType type) 131 | : Data(comp, type) {} 132 | 133 | bool IsReadOnly() const override { return false; } 134 | std::vector& Buf() { return buf; } 135 | const std::vector& Buf() const { return buf; } 136 | const char* Ptr() const { return &buf[0]; } 137 | size_t Size() const { return buf.size(); } 138 | bool IsEmpty() const { return buf.size() == 0; } 139 | FileReference* ToInputFile(File *parent) override; 140 | File* ToOutputFile(File *parent) override; 141 | bool ReadOutputFile(File* f) override; 142 | }; 143 | 144 | /* 145 | * Compiler may be used to invoke different phases OpenCL compiler. 146 | * 147 | * All Data instances and corresponding resources created by Compiler instance, 148 | * including files * and buffers are destroyed this compiler instance is destroyed. 149 | * Additionally, as debug information may contain references to intermediate files. 150 | * 151 | * The lifetime of Compiler instance should be normally same as lifetime 152 | * of OpenCL program that invokes it. 153 | * 154 | * Compiler is not guaranteed to be thread safe. 155 | */ 156 | class Compiler { 157 | public: 158 | virtual ~Compiler() {} 159 | 160 | /* 161 | * Return output of this compiler. 162 | */ 163 | virtual const std::string& Output() = 0; 164 | 165 | /* 166 | * Create new FileReference with given type and pointing to file with given name. 167 | * 168 | * If parent is specified, name is assumed to be relative to parent, otherwise absolute. 169 | */ 170 | virtual FileReference* NewFileReference(DataType type, const std::string& name, File* parent = 0) = 0; 171 | 172 | /* 173 | * Create new FileReference with given type and pointing to file with given name. 174 | * 175 | * If parent is specified, name is assumed to be relative to parent, otherwise absolute. 176 | * 177 | * The file will not be automatically deleted when Compiler instance is destroyed. 178 | */ 179 | virtual File* NewFile(DataType type, const std::string& name, File* parent = 0) = 0; 180 | 181 | /* 182 | * Create File* pointing to new temporary file name. 183 | * 184 | * If name is specified, it is used as temporary file name. 185 | * 186 | * If parent is specified, name is assumed to be relative to parent. Otherwise, it is 187 | * under temporary directory associated with this Compiler instance. 188 | * 189 | * The file will be automatically deleted when Compiler instance is destroyed. 190 | */ 191 | virtual File* NewTempFile(DataType type, const std::string& name = "", File* parent = 0) = 0; 192 | 193 | /* 194 | * Create File* pointing to new temporary directory. 195 | * 196 | * If parent is specified, name is assumed to be relative to parent. Otherwise, it is 197 | * under temporary directory associated with this Compiler instance. 198 | * 199 | * The directory will be automatically deleted when Compiler instance is destroyed. 200 | */ 201 | virtual File* NewTempDir(File* parent = 0) = 0; 202 | 203 | /* 204 | * Create BufferReference. 205 | */ 206 | virtual BufferReference* NewBufferReference(DataType type, const char* ptr, size_t size, const std::string& id = "") = 0; 207 | 208 | /* 209 | * Create new Buffer (initially empty). 210 | */ 211 | virtual Buffer* NewBuffer(DataType type) = 0; 212 | 213 | /* 214 | * Compile several inputs to LLVM Bitcode. 215 | * 216 | * Each input should have one of types DT_CL, DT_CL_HEADER, DT_LLVM_BC, DT_LLVM_LL. 217 | * output should have one of types DT_LLVM_BC or DT_LLVM_LL. 218 | * 219 | * Returns true on success or false on failure. 220 | */ 221 | virtual bool CompileToLLVMBitcode(const std::vector& inputs, Data* output, const std::vector& options) = 0; 222 | 223 | /* 224 | * Link several LLVM Bitcode inputs. 225 | * 226 | * Each input should have one of types DT_LLVM_BC, DT_LLVM_LL. 227 | * output should have one of types DT_LLVM_BC, DT_LLVM_LL 228 | * 229 | * Returns true on success or false on failure. 230 | */ 231 | virtual bool LinkLLVMBitcode(const std::vector& inputs, Data* output, const std::vector& options) = 0; 232 | 233 | /* 234 | * Compile several inputs directly to executable object. 235 | 236 | * Each input should have one of types DT_CL, DT_CL_HEADER, DT_LLVM_BC, DT_LLVM_LL. 237 | * output should have type DT_EXECUTABLE. 238 | * 239 | * Returns true on success or false on failure. 240 | */ 241 | virtual bool CompileAndLinkExecutable(const std::vector& inputs, Data* output, const std::vector& options) = 0; 242 | 243 | /* 244 | * Dumps Executable as text to the specified file. 245 | */ 246 | virtual bool DumpExecutableAsText(Buffer* exec, File* dump) = 0; 247 | 248 | /* 249 | * Change compilation mode (In-process compilation/spawn compilation processes) 250 | */ 251 | virtual void SetInProcess(bool binprocess = true) = 0; 252 | 253 | /* 254 | * Checks whether compilation is in-process or not. 255 | */ 256 | virtual bool IsInProcess() = 0; 257 | 258 | /* 259 | * Enables or disables keeping compiler's temporary files. 260 | */ 261 | virtual void SetKeepTmp(bool bkeeptmp = true) = 0; 262 | 263 | /* 264 | * Checks whether compiler's temporary files are kept or not. 265 | */ 266 | virtual bool IsKeepTmp() = 0; 267 | 268 | /* 269 | * Enables or disables printing compiler's log to stout. 270 | */ 271 | virtual void SetPrintLog(bool bprintlog = true) = 0; 272 | 273 | /* 274 | * Checks whether to print compiler's log to stout or not. 275 | */ 276 | virtual bool IsPrintLog() = 0; 277 | 278 | /* 279 | * Sets logging level. 280 | */ 281 | virtual void SetLogLevel(LogLevel ll) = 0; 282 | 283 | /* 284 | * Gets logging level. 285 | */ 286 | virtual LogLevel GetLogLevel() = 0; 287 | }; 288 | 289 | /* 290 | * CompilerFactory is used to create Compiler's. 291 | * 292 | * Normally there is single instance of CompilerFactory. 293 | * 294 | * CompilerFactory does not own Compiler's that it creates. They should be 295 | * destroyed with delete. 296 | */ 297 | class CompilerFactory { 298 | public: 299 | /* 300 | * Create new instance of OpenCL compiler with AMDGPU backend. 301 | */ 302 | Compiler* CreateAMDGPUCompiler(const std::string& llvmBin); 303 | }; 304 | 305 | } 306 | } 307 | 308 | #endif // AMD_COMPILER_DRIVER_H 309 | -------------------------------------------------------------------------------- /src/driver/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | ## 3 | ## The University of Illinois/NCSA 4 | ## Open Source License (NCSA) 5 | ## 6 | ## Copyright (c) 2016, Advanced Micro Devices, Inc. All rights reserved. 7 | ## 8 | ## Developed by: 9 | ## 10 | ## AMD Research and AMD HSA Software Development 11 | ## 12 | ## Advanced Micro Devices, Inc. 13 | ## 14 | ## www.amd.com 15 | ## 16 | ## Permission is hereby granted, free of charge, to any person obtaining a copy 17 | ## of this software and associated documentation files (the "Software"), to 18 | ## deal with the Software without restriction, including without limitation 19 | ## the rights to use, copy, modify, merge, publish, distribute, sublicense, 20 | ## and#or sell copies of the Software, and to permit persons to whom the 21 | ## Software is furnished to do so, subject to the following conditions: 22 | ## 23 | ## - Redistributions of source code must retain the above copyright notice, 24 | ## this list of conditions and the following disclaimers. 25 | ## - Redistributions in binary form must reproduce the above copyright 26 | ## notice, this list of conditions and the following disclaimers in 27 | ## the documentation and#or other materials provided with the distribution. 28 | ## - Neither the names of Advanced Micro Devices, Inc, 29 | ## nor the names of its contributors may be used to endorse or promote 30 | ## products derived from this Software without specific prior written 31 | ## permission. 32 | ## 33 | ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 34 | ## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 35 | ## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 36 | ## THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 37 | ## OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 38 | ## ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 39 | ## DEALINGS WITH THE SOFTWARE. 40 | ## 41 | ################################################################################ 42 | 43 | file(GLOB sources 44 | ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp 45 | ${CMAKE_CURRENT_SOURCE_DIR}/*.h 46 | ) 47 | 48 | include_directories(${LLVM_INCLUDE_DIR}) 49 | include_directories(${LLVM_CONFIG_INCLUDE_DIR}) 50 | include_directories(${LLVM_MAIN_INCLUDE_DIR}) 51 | include_directories(${LLVM_INCLUDE_DIR}/llvm/Target/AMDGPU) 52 | include_directories(${LLD_INCLUDE_DIR}) 53 | 54 | link_directories(${LLVM_LIBRARY_DIRS}) 55 | add_library(opencl_driver ${sources}) 56 | set_target_properties(opencl_driver PROPERTIES POSITION_INDEPENDENT_CODE ON) 57 | 58 | llvm_map_components_to_libnames(llvm_libs 59 | AllTargetsAsmPrinters 60 | AllTargetsDescs 61 | AllTargetsDisassemblers 62 | AllTargetsInfos 63 | BitWriter 64 | CodeGen 65 | IRReader 66 | Linker 67 | MC 68 | MCDisassembler 69 | MCParser 70 | Object 71 | Symbolize 72 | Core 73 | Option 74 | Support 75 | AMDGPUCodeGen 76 | AMDGPUAsmParser 77 | ) 78 | 79 | if (WIN32) 80 | target_link_libraries(opencl_driver version) 81 | endif (WIN32) 82 | 83 | target_link_libraries(opencl_driver 84 | clangDriver 85 | clangFrontend 86 | clangEdit 87 | clangLex 88 | clangBasic 89 | clangCodeGen 90 | clangSerialization 91 | lldELF 92 | lldCore 93 | LLVMDebugInfoDWARF 94 | ) 95 | target_link_libraries(opencl_driver ${llvm_libs}) 96 | target_include_directories(opencl_driver PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) 97 | 98 | install(TARGETS opencl_driver DESTINATION ${CMAKE_INSTALL_LIBDIR} ) 99 | install(FILES AmdCompiler.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) 100 | -------------------------------------------------------------------------------- /src/roc-cl/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | ## 3 | ## The University of Illinois/NCSA 4 | ## Open Source License (NCSA) 5 | ## 6 | ## Copyright (c) 2016, Advanced Micro Devices, Inc. All rights reserved. 7 | ## 8 | ## Developed by: 9 | ## 10 | ## AMD Research and AMD HSA Software Development 11 | ## 12 | ## Advanced Micro Devices, Inc. 13 | ## 14 | ## www.amd.com 15 | ## 16 | ## Permission is hereby granted, free of charge, to any person obtaining a copy 17 | ## of this software and associated documentation files (the "Software"), to 18 | ## deal with the Software without restriction, including without limitation 19 | ## the rights to use, copy, modify, merge, publish, distribute, sublicense, 20 | ## and#or sell copies of the Software, and to permit persons to whom the 21 | ## Software is furnished to do so, subject to the following conditions: 22 | ## 23 | ## - Redistributions of source code must retain the above copyright notice, 24 | ## this list of conditions and the following disclaimers. 25 | ## - Redistributions in binary form must reproduce the above copyright 26 | ## notice, this list of conditions and the following disclaimers in 27 | ## the documentation and#or other materials provided with the distribution. 28 | ## - Neither the names of Advanced Micro Devices, Inc, 29 | ## nor the names of its contributors may be used to endorse or promote 30 | ## products derived from this Software without specific prior written 31 | ## permission. 32 | ## 33 | ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 34 | ## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 35 | ## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 36 | ## THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 37 | ## OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 38 | ## ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 39 | ## DEALINGS WITH THE SOFTWARE. 40 | ## 41 | ################################################################################ 42 | 43 | cmake_minimum_required(VERSION 2.6) 44 | 45 | file(GLOB sources 46 | ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp 47 | ${CMAKE_CURRENT_SOURCE_DIR}/*.h 48 | ) 49 | 50 | include_directories(${LLVM_INCLUDE_DIR}) 51 | include_directories(${LLVM_CONFIG_INCLUDE_DIR}) 52 | include_directories(${LLVM_MAIN_INCLUDE_DIR}) 53 | link_directories(${LLVM_LIBRARY_DIRS}) 54 | add_executable(roc-cl ${sources}) 55 | target_link_libraries(roc-cl opencl_driver) 56 | 57 | install(TARGETS roc-cl RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ) 58 | -------------------------------------------------------------------------------- /src/roc-cl/roc-cl.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "AmdCompiler.h" 3 | #include "llvm/Support/CommandLine.h" 4 | #include "llvm/Support/FormattedStream.h" 5 | 6 | using namespace llvm; 7 | 8 | enum ActionType { 9 | AC_NotSet, 10 | AC_CompileToLLVMBitcode, 11 | AC_LinkLLVMBitcode, 12 | AC_CompileAndLinkExecutable, 13 | }; 14 | 15 | static cl::opt 16 | Action(cl::desc("Action to perform:"), 17 | cl::init(AC_NotSet), 18 | cl::values( 19 | clEnumValN(AC_CompileToLLVMBitcode, "compile_to_llvm", "Compile to LLVM bitcode"), 20 | clEnumValN(AC_LinkLLVMBitcode, "link_llvm", "Link LLVM bitcode"), 21 | clEnumValN(AC_CompileAndLinkExecutable, "compile_and_link", "Compile and link executable") 22 | ) 23 | ); 24 | 25 | static cl::opt 26 | LLVMBin("llvmbin", cl::desc("LLVM binary directory")); 27 | 28 | static cl::list 29 | InputFilenames(cl::Positional, cl::desc(""),cl::ZeroOrMore); 30 | 31 | static cl::opt 32 | OutputFilename("o", cl::desc("Output filename"), 33 | cl::value_desc("filename")); 34 | 35 | static cl::list 36 | OtherOptions(cl::Sink, cl::desc("")); 37 | 38 | using namespace amd::opencl_driver; 39 | using namespace llvm; 40 | 41 | int main(int argc, char* argv[]) 42 | { 43 | cl::ParseCommandLineOptions(argc, argv, "AMD OpenCL Compiler"); 44 | 45 | CompilerFactory compilerFactory; 46 | 47 | std::unique_ptr compiler(compilerFactory.CreateAMDGPUCompiler(LLVMBin)); 48 | 49 | std::vector inputs; 50 | 51 | for (const std::string& inputFile : InputFilenames) { 52 | inputs.push_back(compiler->NewFileReference(DT_CL, inputFile)); 53 | } 54 | 55 | std::vector options = OtherOptions; 56 | Data* output; 57 | 58 | bool res; 59 | 60 | switch (Action) { 61 | case AC_NotSet: 62 | default: 63 | errs() << "Error: action is not specified.\n"; res = false; 64 | break; 65 | case AC_CompileToLLVMBitcode: 66 | output = compiler->NewFile(DT_LLVM_BC, OutputFilename); 67 | res = compiler->CompileToLLVMBitcode(inputs, output, options); 68 | break; 69 | case AC_LinkLLVMBitcode: 70 | output = compiler->NewFile(DT_LLVM_BC, OutputFilename); 71 | res = compiler->LinkLLVMBitcode(inputs, output, options); 72 | break; 73 | case AC_CompileAndLinkExecutable: 74 | output = compiler->NewFile(DT_EXECUTABLE, OutputFilename); 75 | res = compiler->CompileAndLinkExecutable(inputs, output, options); 76 | break; 77 | } 78 | 79 | std::string compilerOutput = compiler->Output(); 80 | if (!compilerOutput.empty()) { 81 | outs() << compilerOutput << '\n'; 82 | } 83 | 84 | return res ? 0 : 1; 85 | } 86 | -------------------------------------------------------------------------------- /src/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | ## 3 | ## The University of Illinois/NCSA 4 | ## Open Source License (NCSA) 5 | ## 6 | ## Copyright (c) 2016, Advanced Micro Devices, Inc. All rights reserved. 7 | ## 8 | ## Developed by: 9 | ## 10 | ## AMD Research and AMD HSA Software Development 11 | ## 12 | ## Advanced Micro Devices, Inc. 13 | ## 14 | ## www.amd.com 15 | ## 16 | ## Permission is hereby granted, free of charge, to any person obtaining a copy 17 | ## of this software and associated documentation files (the "Software"), to 18 | ## deal with the Software without restriction, including without limitation 19 | ## the rights to use, copy, modify, merge, publish, distribute, sublicense, 20 | ## and#or sell copies of the Software, and to permit persons to whom the 21 | ## Software is furnished to do so, subject to the following conditions: 22 | ## 23 | ## - Redistributions of source code must retain the above copyright notice, 24 | ## this list of conditions and the following disclaimers. 25 | ## - Redistributions in binary form must reproduce the above copyright 26 | ## notice, this list of conditions and the following disclaimers in 27 | ## the documentation and#or other materials provided with the distribution. 28 | ## - Neither the names of Advanced Micro Devices, Inc, 29 | ## nor the names of its contributors may be used to endorse or promote 30 | ## products derived from this Software without specific prior written 31 | ## permission. 32 | ## 33 | ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 34 | ## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 35 | ## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 36 | ## THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 37 | ## OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 38 | ## ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 39 | ## DEALINGS WITH THE SOFTWARE. 40 | ## 41 | ################################################################################ 42 | 43 | cmake_minimum_required(VERSION 2.6) 44 | 45 | file(GLOB sources 46 | ${CMAKE_CURRENT_SOURCE_DIR}/*.cl 47 | ) 48 | 49 | set(CL $) 50 | set(LLVMBIN ${LLVM_TOOLS_BINARY_DIR}) 51 | 52 | macro(roc_cl_test name) 53 | string(REGEX REPLACE ":" "_" dir ${CMAKE_CURRENT_BINARY_DIR}/${name}) 54 | file(MAKE_DIRECTORY ${dir}) 55 | add_test( 56 | NAME ${name} 57 | COMMAND ${CL} -llvmbin ${LLVMBIN} ${ARGN} 58 | WORKING_DIRECTORY ${dir} 59 | ) 60 | endmacro() 61 | 62 | set(SIMPLE ${CMAKE_CURRENT_SOURCE_DIR}/simple.cl) 63 | roc_cl_test(simple:compile_to_llvm -compile_to_llvm ${SIMPLE} -o out.bc) 64 | roc_cl_test(simple:compile_to_llvm-g -compile_to_llvm ${SIMPLE} -g -o out.bc) 65 | roc_cl_test(simple:compile_to_llvm-fembed-bitcode -compile_to_llvm ${SIMPLE} -fembed-bitcode -o out.bc) 66 | roc_cl_test(simple:compile_to_llvm-cl-opt-disable -compile_to_llvm ${SIMPLE} -cl-opt-disable -o out.bc) 67 | roc_cl_test(simple:compile_to_llvm-cl-strict-aliasing -compile_to_llvm ${SIMPLE} -cl-strict-aliasing -o out.bc) 68 | roc_cl_test(simple:compile_to_llvm-cl-single-precision-constant -compile_to_llvm ${SIMPLE} -cl-single-precision-constant -o out.bc) 69 | roc_cl_test(simple:compile_to_llvm-cl-finite-math-only -compile_to_llvm ${SIMPLE} -cl-finite-math-only -o out.bc) 70 | roc_cl_test(simple:compile_to_llvm-cl-kernel-arg-info -compile_to_llvm ${SIMPLE} -cl-kernel-arg-info -o out.bc) 71 | roc_cl_test(simple:compile_to_llvm-cl-unsafe-math-optimizations -compile_to_llvm ${SIMPLE} -cl-unsafe-math-optimizations -o out.bc) 72 | roc_cl_test(simple:compile_to_llvm-cl-fast-relaxed-math -compile_to_llvm ${SIMPLE} -cl-fast-relaxed-math -o out.bc) 73 | roc_cl_test(simple:compile_to_llvm-cl-mad-enable -compile_to_llvm ${SIMPLE} -cl-mad-enable -o out.bc) 74 | roc_cl_test(simple:compile_to_llvm-cl-no-signed-zeros -compile_to_llvm ${SIMPLE} -cl-no-signed-zeros -o out.bc) 75 | roc_cl_test(simple:compile_to_llvm-cl-std-1.2 -compile_to_llvm ${SIMPLE} -cl-std=CL1.2 -o out.bc) 76 | roc_cl_test(simple:compile_to_llvm-cl-std-2.0 -compile_to_llvm ${SIMPLE} -cl-std=CL2.0 -o out.bc) 77 | roc_cl_test(simple:compile_to_llvm-cl-denorms-are-zero -compile_to_llvm ${SIMPLE} -cl-denorms-are-zero -o out.bc) 78 | roc_cl_test(simple:compile_to_llvm-cl-fp32-correctly-rounded-divide-sqrt -compile_to_llvm ${SIMPLE} -cl-fp32-correctly-rounded-divide-sqrt -o out.bc) 79 | roc_cl_test(simple:compile_and_link -compile_and_link ${SIMPLE} -o out.co) 80 | 81 | set(EXTERN_FUNCTION ${CMAKE_CURRENT_SOURCE_DIR}/extern_function1.cl ${CMAKE_CURRENT_SOURCE_DIR}/extern_function2.cl) 82 | roc_cl_test(extern_function:compile_to_llvm -compile_to_llvm ${EXTERN_FUNCTION} -o out.bc -cl-std=CL1.2) 83 | roc_cl_test(extern_function:compile_and_link -compile_and_link ${EXTERN_FUNCTION} -o out.co -cl-std=CL1.2) 84 | 85 | set(DEFINED ${CMAKE_CURRENT_SOURCE_DIR}/defined.cl) 86 | roc_cl_test(defined:compile_to_llvm ${DEFINED} -compile_to_llvm -DDEF=10 -o out.co) 87 | roc_cl_test(defined:compile_and_link ${DEFINED} -compile_and_link -DDEF=10 -o out.co) 88 | 89 | set(INCLUDER ${CMAKE_CURRENT_SOURCE_DIR}/includer.cl) 90 | set(INCLUDE ${CMAKE_CURRENT_SOURCE_DIR}/include) 91 | roc_cl_test(include-I1:compile_to_llvm -compile_to_llvm ${INCLUDER} -I${INCLUDE} -o out.bc) 92 | roc_cl_test(include-I1:compile_and_link -compile_and_link ${INCLUDER} -I${INCLUDE} -o out.co) 93 | #roc_cl_test(include-I2:compile_to_llvm -compile_to_llvm ${INCLUDER} -I ${INCLUDE} -o out.bc) 94 | #oc_cl_test(include-I2:compile_and_link -compile_and_link ${INCLUDER} -I ${INCLUDE} -o out.co) 95 | -------------------------------------------------------------------------------- /src/test/defined.cl: -------------------------------------------------------------------------------- 1 | kernel void test_kernel(global int* out) 2 | { 3 | out[0] = DEF; 4 | } 5 | -------------------------------------------------------------------------------- /src/test/extern_function1.cl: -------------------------------------------------------------------------------- 1 | extern int test_function(); 2 | 3 | kernel void test_kernel(global int* out) 4 | { 5 | out[0] = test_function(); 6 | } 7 | 8 | -------------------------------------------------------------------------------- /src/test/extern_function2.cl: -------------------------------------------------------------------------------- 1 | int test_function() 2 | { 3 | return 5; 4 | } 5 | -------------------------------------------------------------------------------- /src/test/include/include.h: -------------------------------------------------------------------------------- 1 | int test_function() 2 | { 3 | return 5; 4 | } 5 | -------------------------------------------------------------------------------- /src/test/includer.cl: -------------------------------------------------------------------------------- 1 | #include "include.h" 2 | 3 | kernel void test_kernel(global int* out) 4 | { 5 | out[0] = test_function(); 6 | } 7 | 8 | -------------------------------------------------------------------------------- /src/test/simple.cl: -------------------------------------------------------------------------------- 1 | kernel void test_kernel(global int* out) 2 | { 3 | out[0] = 4; 4 | } 5 | 6 | -------------------------------------------------------------------------------- /src/unittest/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | ## 3 | ## The University of Illinois/NCSA 4 | ## Open Source License (NCSA) 5 | ## 6 | ## Copyright (c) 2016, Advanced Micro Devices, Inc. All rights reserved. 7 | ## 8 | ## Developed by: 9 | ## 10 | ## AMD Research and AMD HSA Software Development 11 | ## 12 | ## Advanced Micro Devices, Inc. 13 | ## 14 | ## www.amd.com 15 | ## 16 | ## Permission is hereby granted, free of charge, to any person obtaining a copy 17 | ## of this software and associated documentation files (the "Software"), to 18 | ## deal with the Software without restriction, including without limitation 19 | ## the rights to use, copy, modify, merge, publish, distribute, sublicense, 20 | ## and#or sell copies of the Software, and to permit persons to whom the 21 | ## Software is furnished to do so, subject to the following conditions: 22 | ## 23 | ## - Redistributions of source code must retain the above copyright notice, 24 | ## this list of conditions and the following disclaimers. 25 | ## - Redistributions in binary form must reproduce the above copyright 26 | ## notice, this list of conditions and the following disclaimers in 27 | ## the documentation and#or other materials provided with the distribution. 28 | ## - Neither the names of Advanced Micro Devices, Inc, 29 | ## nor the names of its contributors may be used to endorse or promote 30 | ## products derived from this Software without specific prior written 31 | ## permission. 32 | ## 33 | ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 34 | ## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 35 | ## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 36 | ## THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 37 | ## OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 38 | ## ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 39 | ## DEALINGS WITH THE SOFTWARE. 40 | ## 41 | ################################################################################ 42 | 43 | cmake_minimum_required(VERSION 2.6) 44 | 45 | find_package(Threads REQUIRED) 46 | include(ExternalProject) 47 | 48 | ExternalProject_Add( 49 | googletest 50 | GIT_REPOSITORY https://github.com/google/googletest 51 | TIMEOUT 10 52 | CMAKE_ARGS 53 | -DBUILD_GTEST=ON -DBUILD_GMOCK=OFF 54 | -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS} 55 | -Dgtest_force_shared_crt=ON 56 | # Disable install step 57 | INSTALL_COMMAND "" 58 | # Wrap download, configure and build steps in a script to log output 59 | LOG_DOWNLOAD ON 60 | LOG_CONFIGURE ON 61 | LOG_BUILD ON) 62 | 63 | ExternalProject_Get_Property(googletest source_dir) 64 | include_directories(${source_dir}/googletest/include) 65 | ExternalProject_Get_Property(googletest binary_dir) 66 | link_directories(${binary_dir}/lib) 67 | 68 | #include(${GTEST_INCLUDE_DIRS}) 69 | file(GLOB sources ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) 70 | 71 | link_directories(${LLVM_LIBRARY_DIRS}) 72 | add_executable(roc-cl-unittest ${sources}) 73 | add_dependencies(roc-cl-unittest googletest) 74 | target_link_libraries(roc-cl-unittest opencl_driver) 75 | target_link_libraries(roc-cl-unittest gtest gtest_main) 76 | target_link_libraries(roc-cl-unittest ${CMAKE_THREAD_LIBS_INIT}) 77 | 78 | add_test(NAME roc-cl-unittest 79 | COMMAND $) 80 | 81 | set_property(TEST roc-cl-unittest PROPERTY ENVIRONMENT 82 | "LLVM_BIN=${LLVM_BINARY_DIR}/bin;TEST_DIR=${CMAKE_SOURCE_DIR}/src/test") 83 | -------------------------------------------------------------------------------- /src/unittest/Tests.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "gtest/gtest.h" 3 | #include "AmdCompiler.h" 4 | 5 | using namespace amd::opencl_driver; 6 | 7 | static std::string joinf(const std::string& p1, const std::string& p2) 8 | { 9 | std::string r; 10 | if (!p1.empty()) { r += p1; r += "/"; } 11 | r += p2; 12 | return r; 13 | } 14 | 15 | class AMDGPUCompilerTest : public ::testing::Test { 16 | protected: 17 | std::string llvmBin; 18 | std::string testDir; 19 | std::vector emptyOptions; 20 | std::vector defaultOptions; 21 | 22 | virtual void SetUp() { 23 | ASSERT_NE(getenv("LLVM_BIN"), nullptr); 24 | llvmBin = getenv("LLVM_BIN"); 25 | ASSERT_NE(getenv("TEST_DIR"), nullptr); 26 | testDir = getenv("TEST_DIR"); 27 | compiler = compilerFactory.CreateAMDGPUCompiler(llvmBin); 28 | defaultOptions.push_back("-cl-std=CL1.2"); 29 | } 30 | 31 | virtual void TearDown() { 32 | std::string compilerOutput = compiler->Output(); 33 | if (!compilerOutput.empty()) { 34 | std::cout << "Compiler output:" << std::endl 35 | << compilerOutput << std::endl; 36 | } 37 | delete compiler; 38 | } 39 | 40 | FileReference* TestDirInputFile(DataType type, const std::string& name) { 41 | return compiler->NewFileReference(type, joinf(testDir, name)); 42 | } 43 | 44 | File* TmpOutputFile(DataType type) { 45 | return compiler->NewTempFile(type); 46 | } 47 | 48 | Data* NewClSource(const char* s) { 49 | return compiler->NewBufferReference(DT_CL, s, strlen(s)); 50 | } 51 | 52 | CompilerFactory compilerFactory; 53 | Compiler* compiler; 54 | }; 55 | 56 | static const std::string simpleCl = "simple.cl"; 57 | static const std::string externFunction1Cl = "extern_function1.cl"; 58 | static const std::string externFunction2Cl = "extern_function2.cl"; 59 | static const std::string outBc = "out.bc"; 60 | static const std::string out1Bc = "out1.bc"; 61 | static const std::string out2Bc = "out2.bc"; 62 | static const std::string outCo = "out.co"; 63 | 64 | static const char* simpleSource = 65 | "kernel void test_kernel(global int* out) \n" 66 | "{ \n" 67 | " out[0] = 4; \n" 68 | "} \n" 69 | ; 70 | 71 | static const char* externFunction1 = 72 | "extern int test_function(); \n" 73 | " \n" 74 | "kernel void test_kernel(global int* out) \n" 75 | "{ \n" 76 | " out[0] = test_function(); \n" 77 | "} \n" 78 | ; 79 | 80 | static const char* externFunction2 = 81 | "int test_function() \n" 82 | "{ \n" 83 | " return 5; \n" 84 | "} \n" 85 | ; 86 | 87 | static const char* includer = 88 | "#include \"include.h\" \n" 89 | " \n" 90 | "kernel void test_kernel(global int* out) \n" 91 | "{ \n" 92 | " out[0] = test_function(); \n" 93 | "} \n" 94 | ; 95 | 96 | static const char* include = 97 | "int test_function() \n" 98 | "{ \n" 99 | " return 5; \n" 100 | "} \n" 101 | ; 102 | 103 | static const char* includeInvalid = 104 | "Invalid include file (should not be used)." 105 | ; 106 | 107 | static const char* defined = 108 | "kernel void test_kernel(global int* out) \n" 109 | "{ \n" 110 | " out[0] = DEF; \n" 111 | "} \n" 112 | ; 113 | 114 | static const char* invalidCL = 115 | "kernel void test() { ExpectedErrorInCLSource; } \n" 116 | ; 117 | 118 | static const char* invalidBC= 119 | "ExpectedInvalidBitcode \n" 120 | ; 121 | 122 | TEST_F(AMDGPUCompilerTest, OutputEmpty) 123 | { 124 | EXPECT_EQ(compiler->Output().length(), 0U); 125 | } 126 | 127 | TEST_F(AMDGPUCompilerTest, CompileToLLVMBitcode_File_To_File) 128 | { 129 | FileReference* f = TestDirInputFile(DT_CL, simpleCl); 130 | ASSERT_NE(f, nullptr); 131 | File* out = TmpOutputFile(DT_LLVM_BC); 132 | ASSERT_NE(out, nullptr); 133 | std::vector inputs; 134 | inputs.push_back(f); 135 | ASSERT_TRUE(compiler->CompileToLLVMBitcode(inputs, out, defaultOptions)); 136 | ASSERT_TRUE(out->Exists()); 137 | } 138 | 139 | TEST_F(AMDGPUCompilerTest, CompileToLLVMBitcode_Buffer_To_Buffer) 140 | { 141 | Data* src = NewClSource(simpleSource); 142 | ASSERT_NE(src, nullptr); 143 | Buffer* out = compiler->NewBuffer(DT_LLVM_BC); 144 | ASSERT_NE(out, nullptr); 145 | std::vector inputs; 146 | inputs.push_back(src); 147 | ASSERT_TRUE(compiler->CompileToLLVMBitcode(inputs, out, defaultOptions)); 148 | ASSERT_TRUE(!out->IsEmpty()); 149 | } 150 | 151 | TEST_F(AMDGPUCompilerTest, CompileToLLVMBitcode_Include_I1) 152 | { 153 | Data* src = NewClSource(includer); 154 | ASSERT_NE(src, nullptr); 155 | Buffer* out = compiler->NewBuffer(DT_LLVM_BC); 156 | ASSERT_NE(out, nullptr); 157 | std::vector inputs; 158 | inputs.push_back(src); 159 | std::vector options; 160 | options.push_back("-I"); 161 | options.push_back(joinf(testDir, "include")); 162 | ASSERT_TRUE(compiler->CompileToLLVMBitcode(inputs, out, options)); 163 | ASSERT_TRUE(!out->IsEmpty()); 164 | } 165 | 166 | TEST_F(AMDGPUCompilerTest, CompileToLLVMBitcode_Include_I2) 167 | { 168 | Data* src = NewClSource(includer); 169 | ASSERT_NE(src, nullptr); 170 | Buffer* out = compiler->NewBuffer(DT_LLVM_BC); 171 | ASSERT_NE(out, nullptr); 172 | std::vector inputs; 173 | inputs.push_back(src); 174 | std::vector options; 175 | options.push_back("-I" + joinf(testDir, "include")); 176 | ASSERT_TRUE(compiler->CompileToLLVMBitcode(inputs, out, options)); 177 | ASSERT_TRUE(!out->IsEmpty()); 178 | } 179 | 180 | TEST_F(AMDGPUCompilerTest, CompileAndLinkExecutable_File_To_File) 181 | { 182 | FileReference* f = TestDirInputFile(DT_CL, simpleCl); 183 | ASSERT_NE(f, nullptr); 184 | File* out = TmpOutputFile(DT_EXECUTABLE); 185 | ASSERT_NE(out, nullptr); 186 | std::vector inputs; 187 | inputs.push_back(f); 188 | ASSERT_TRUE(compiler->CompileAndLinkExecutable(inputs, out, defaultOptions)); 189 | ASSERT_TRUE(out->Exists()); 190 | } 191 | 192 | TEST_F(AMDGPUCompilerTest, CompileAndLinkExecutable_Buffer_To_Buffer) 193 | { 194 | Data* src = NewClSource(simpleSource); 195 | ASSERT_NE(src, nullptr); 196 | Buffer* out = compiler->NewBuffer(DT_LLVM_BC); 197 | ASSERT_NE(out, nullptr); 198 | std::vector inputs; 199 | inputs.push_back(src); 200 | ASSERT_TRUE(compiler->CompileAndLinkExecutable(inputs, out, defaultOptions)); 201 | ASSERT_TRUE(!out->IsEmpty()); 202 | } 203 | 204 | TEST_F(AMDGPUCompilerTest, CompileAndLink_CLs_File_To_File) 205 | { 206 | std::vector inputs; 207 | FileReference* ef1 = TestDirInputFile(DT_CL, externFunction1Cl); 208 | ASSERT_NE(ef1, nullptr); 209 | FileReference* ef2 = TestDirInputFile(DT_CL, externFunction2Cl); 210 | ASSERT_NE(ef2, nullptr); 211 | 212 | inputs.push_back(ef1); 213 | inputs.push_back(ef2); 214 | File* out = TmpOutputFile(DT_EXECUTABLE); 215 | ASSERT_NE(out, nullptr); 216 | ASSERT_TRUE(compiler->CompileAndLinkExecutable(inputs, out, defaultOptions)); 217 | ASSERT_TRUE(out->Exists()); 218 | } 219 | 220 | TEST_F(AMDGPUCompilerTest, CompileAndLink_CLs_Buffer_To_Buffer) 221 | { 222 | std::vector inputs; 223 | Data* src1 = NewClSource(externFunction1); 224 | ASSERT_NE(src1, nullptr); 225 | Data* src2 = NewClSource(externFunction2); 226 | ASSERT_NE(src2, nullptr); 227 | 228 | inputs.push_back(src1); 229 | inputs.push_back(src2); 230 | Buffer* out = compiler->NewBuffer(DT_EXECUTABLE); 231 | ASSERT_NE(out, nullptr); 232 | ASSERT_TRUE(compiler->CompileAndLinkExecutable(inputs, out, defaultOptions)); 233 | ASSERT_TRUE(!out->IsEmpty()); 234 | } 235 | 236 | TEST_F(AMDGPUCompilerTest, LinkLLVMBitcode_File_To_File) 237 | { 238 | std::vector inputs; 239 | FileReference* ef1 = TestDirInputFile(DT_CL, externFunction1Cl); 240 | ASSERT_NE(ef1, nullptr); 241 | FileReference* ef2 = TestDirInputFile(DT_CL, externFunction2Cl); 242 | ASSERT_NE(ef2, nullptr); 243 | 244 | inputs.clear(); 245 | inputs.push_back(ef1); 246 | File* out1 = TmpOutputFile(DT_LLVM_BC); 247 | ASSERT_NE(out1, nullptr); 248 | ASSERT_TRUE(compiler->CompileToLLVMBitcode(inputs, out1, defaultOptions)); 249 | ASSERT_TRUE(out1->Exists()); 250 | 251 | inputs.clear(); 252 | inputs.push_back(ef2); 253 | File* out2 = TmpOutputFile(DT_LLVM_BC); 254 | ASSERT_NE(out2, nullptr); 255 | ASSERT_TRUE(compiler->CompileToLLVMBitcode(inputs, out2, defaultOptions)); 256 | ASSERT_TRUE(out2->Exists()); 257 | 258 | inputs.clear(); 259 | inputs.push_back(out1); 260 | inputs.push_back(out2); 261 | File* out = TmpOutputFile(DT_LLVM_BC); 262 | ASSERT_NE(out, nullptr); 263 | ASSERT_TRUE(compiler->LinkLLVMBitcode(inputs, out, emptyOptions)); 264 | ASSERT_TRUE(out->Exists()); 265 | } 266 | 267 | TEST_F(AMDGPUCompilerTest, LinkLLVMBitcode_Buffer_To_Buffer) 268 | { 269 | std::vector inputs; 270 | Data* src1 = NewClSource(externFunction1); 271 | ASSERT_NE(src1, nullptr); 272 | Data* src2 = NewClSource(externFunction2); 273 | ASSERT_NE(src2, nullptr); 274 | 275 | inputs.clear(); 276 | inputs.push_back(src1); 277 | Buffer* out1 = compiler->NewBuffer(DT_LLVM_BC); 278 | ASSERT_NE(out1, nullptr); 279 | ASSERT_TRUE(compiler->CompileToLLVMBitcode(inputs, out1, defaultOptions)); 280 | ASSERT_TRUE(!out1->IsEmpty()); 281 | 282 | inputs.clear(); 283 | inputs.push_back(src2); 284 | Buffer* out2 = compiler->NewBuffer(DT_LLVM_BC); 285 | ASSERT_NE(out2, nullptr); 286 | ASSERT_TRUE(compiler->CompileToLLVMBitcode(inputs, out2, defaultOptions)); 287 | ASSERT_TRUE(!out2->IsEmpty()); 288 | 289 | inputs.clear(); 290 | inputs.push_back(out1); 291 | inputs.push_back(out2); 292 | Buffer* out = compiler->NewBuffer(DT_LLVM_BC); 293 | ASSERT_NE(out, nullptr); 294 | ASSERT_TRUE(compiler->LinkLLVMBitcode(inputs, out, emptyOptions)); 295 | ASSERT_TRUE(!out->IsEmpty()); 296 | } 297 | 298 | TEST_F(AMDGPUCompilerTest, CompileAndLink_BCs_File_To_File) 299 | { 300 | std::vector inputs; 301 | FileReference* ef1 = TestDirInputFile(DT_CL, externFunction1Cl); 302 | ASSERT_NE(ef1, nullptr); 303 | FileReference* ef2 = TestDirInputFile(DT_CL, externFunction2Cl); 304 | ASSERT_NE(ef2, nullptr); 305 | 306 | inputs.clear(); 307 | inputs.push_back(ef1); 308 | File* out1 = TmpOutputFile(DT_LLVM_BC); 309 | ASSERT_NE(out1, nullptr); 310 | ASSERT_TRUE(compiler->CompileToLLVMBitcode(inputs, out1, defaultOptions)); 311 | ASSERT_TRUE(out1->Exists()); 312 | 313 | inputs.clear(); 314 | inputs.push_back(ef2); 315 | File* out2 = TmpOutputFile(DT_LLVM_BC); 316 | ASSERT_NE(out2, nullptr); 317 | ASSERT_TRUE(compiler->CompileToLLVMBitcode(inputs, out2, defaultOptions)); 318 | ASSERT_TRUE(out2->Exists()); 319 | 320 | inputs.clear(); 321 | inputs.push_back(out1); 322 | inputs.push_back(out2); 323 | File* out = TmpOutputFile(DT_EXECUTABLE); 324 | ASSERT_NE(out, nullptr); 325 | ASSERT_TRUE(compiler->CompileAndLinkExecutable(inputs, out, defaultOptions)); 326 | ASSERT_TRUE(out->Exists()); 327 | } 328 | 329 | TEST_F(AMDGPUCompilerTest, CompileAndLink_BCs_Buffer_To_Buffer) 330 | { 331 | std::vector inputs; 332 | Data* src1 = NewClSource(externFunction1); 333 | ASSERT_NE(src1, nullptr); 334 | Data* src2 = NewClSource(externFunction2); 335 | ASSERT_NE(src2, nullptr); 336 | 337 | inputs.clear(); 338 | inputs.push_back(src1); 339 | Buffer* out1 = compiler->NewBuffer(DT_LLVM_BC); 340 | ASSERT_NE(out1, nullptr); 341 | ASSERT_TRUE(compiler->CompileToLLVMBitcode(inputs, out1, defaultOptions)); 342 | ASSERT_TRUE(!out1->IsEmpty()); 343 | 344 | inputs.clear(); 345 | inputs.push_back(src2); 346 | Buffer* out2 = compiler->NewBuffer(DT_LLVM_BC); 347 | ASSERT_NE(out2, nullptr); 348 | ASSERT_TRUE(compiler->CompileToLLVMBitcode(inputs, out2, defaultOptions)); 349 | ASSERT_TRUE(!out2->IsEmpty()); 350 | 351 | inputs.clear(); 352 | inputs.push_back(out1); 353 | inputs.push_back(out2); 354 | Buffer* out = compiler->NewBuffer(DT_EXECUTABLE); 355 | ASSERT_NE(out, nullptr); 356 | ASSERT_TRUE(compiler->CompileAndLinkExecutable(inputs, out, defaultOptions)); 357 | ASSERT_TRUE(!out->IsEmpty()); 358 | } 359 | 360 | TEST_F(AMDGPUCompilerTest, CompileAndLink_Linked_Buffer_To_Buffer) 361 | { 362 | std::vector inputs; 363 | Data* src1 = NewClSource(externFunction1); 364 | ASSERT_NE(src1, nullptr); 365 | Data* src2 = NewClSource(externFunction2); 366 | ASSERT_NE(src2, nullptr); 367 | 368 | inputs.clear(); 369 | inputs.push_back(src1); 370 | Buffer* out1 = compiler->NewBuffer(DT_LLVM_BC); 371 | ASSERT_NE(out1, nullptr); 372 | ASSERT_TRUE(compiler->CompileToLLVMBitcode(inputs, out1, defaultOptions)); 373 | ASSERT_TRUE(!out1->IsEmpty()); 374 | 375 | inputs.clear(); 376 | inputs.push_back(src2); 377 | Buffer* out2 = compiler->NewBuffer(DT_LLVM_BC); 378 | ASSERT_NE(out2, nullptr); 379 | ASSERT_TRUE(compiler->CompileToLLVMBitcode(inputs, out2, defaultOptions)); 380 | ASSERT_TRUE(!out2->IsEmpty()); 381 | 382 | inputs.clear(); 383 | inputs.push_back(out1); 384 | inputs.push_back(out2); 385 | Buffer* out = compiler->NewBuffer(DT_LLVM_BC); 386 | ASSERT_NE(out, nullptr); 387 | ASSERT_TRUE(compiler->LinkLLVMBitcode(inputs, out, emptyOptions)); 388 | ASSERT_TRUE(!out->IsEmpty()); 389 | 390 | inputs.clear(); 391 | inputs.push_back(out); 392 | Buffer* out3 = compiler->NewBuffer(DT_EXECUTABLE); 393 | ASSERT_NE(out1, nullptr); 394 | ASSERT_TRUE(compiler->CompileAndLinkExecutable(inputs, out3, defaultOptions)); 395 | ASSERT_TRUE(!out->IsEmpty()); 396 | } 397 | 398 | 399 | TEST_F(AMDGPUCompilerTest, CompileToLLVMBitcode_EmbeddedInclude) 400 | { 401 | Data* src = NewClSource(includer); 402 | ASSERT_NE(src, nullptr); 403 | Data* inc = compiler->NewBufferReference(DT_CL_HEADER, include, strlen(include), "include.h"); 404 | Buffer* out = compiler->NewBuffer(DT_LLVM_BC); 405 | ASSERT_NE(out, nullptr); 406 | std::vector inputs; 407 | inputs.push_back(src); 408 | inputs.push_back(inc); 409 | ASSERT_TRUE(compiler->CompileToLLVMBitcode(inputs, out, defaultOptions)); 410 | ASSERT_TRUE(!out->IsEmpty()); 411 | } 412 | 413 | TEST_F(AMDGPUCompilerTest, CompileAndLink_EmbeddedInclude) 414 | { 415 | Data* src = NewClSource(includer); 416 | ASSERT_NE(src, nullptr); 417 | Data* inc = compiler->NewBufferReference(DT_CL_HEADER, include, strlen(include), "include.h"); 418 | Buffer* out = compiler->NewBuffer(DT_EXECUTABLE); 419 | ASSERT_NE(out, nullptr); 420 | std::vector inputs; 421 | inputs.push_back(src); 422 | inputs.push_back(inc); 423 | ASSERT_TRUE(compiler->CompileAndLinkExecutable(inputs, out, defaultOptions)); 424 | ASSERT_TRUE(!out->IsEmpty()); 425 | } 426 | 427 | TEST_F(AMDGPUCompilerTest, CompileToLLVMBitcode_EmbeddedIncludeOverride) 428 | { 429 | Data* src = NewClSource(includer); 430 | ASSERT_NE(src, nullptr); 431 | Data* inc = compiler->NewBufferReference(DT_CL_HEADER, include, strlen(include), "include.h"); 432 | Data* inc2 = compiler->NewBufferReference(DT_CL_HEADER, includeInvalid, strlen(includeInvalid), "include.h"); 433 | Buffer* out = compiler->NewBuffer(DT_LLVM_BC); 434 | ASSERT_NE(out, nullptr); 435 | std::vector inputs; 436 | inputs.push_back(src); 437 | inputs.push_back(inc); 438 | inputs.push_back(inc2); 439 | ASSERT_TRUE(compiler->CompileToLLVMBitcode(inputs, out, defaultOptions)); 440 | ASSERT_TRUE(!out->IsEmpty()); 441 | } 442 | 443 | TEST_F(AMDGPUCompilerTest, CompileAndLink_EmbeddedIncludeOverride) 444 | { 445 | Data* src = NewClSource(includer); 446 | ASSERT_NE(src, nullptr); 447 | Data* inc = compiler->NewBufferReference(DT_CL_HEADER, include, strlen(include), "include.h"); 448 | Data* inc2 = compiler->NewBufferReference(DT_CL_HEADER, includeInvalid, strlen(includeInvalid), "include.h"); 449 | Buffer* out = compiler->NewBuffer(DT_EXECUTABLE); 450 | ASSERT_NE(out, nullptr); 451 | std::vector inputs; 452 | inputs.push_back(src); 453 | inputs.push_back(inc); 454 | inputs.push_back(inc2); 455 | ASSERT_TRUE(compiler->CompileAndLinkExecutable(inputs, out, defaultOptions)); 456 | ASSERT_TRUE(!out->IsEmpty()); 457 | } 458 | 459 | TEST_F(AMDGPUCompilerTest, CompileToLLVMBitcode_Define1) 460 | { 461 | Data* src = compiler->NewBufferReference(DT_CL, defined, strlen(defined)); 462 | ASSERT_NE(src, nullptr); 463 | Buffer* out = compiler->NewBuffer(DT_LLVM_BC); 464 | ASSERT_NE(out, nullptr); 465 | std::vector inputs; 466 | inputs.push_back(src); 467 | std::vector options; 468 | options.push_back("-DDEF=10"); 469 | ASSERT_TRUE(compiler->CompileToLLVMBitcode(inputs, out, options)); 470 | ASSERT_TRUE(!out->IsEmpty()); 471 | } 472 | 473 | TEST_F(AMDGPUCompilerTest, CompileAndLink_Define1) 474 | { 475 | Data* src = compiler->NewBufferReference(DT_CL, defined, strlen(defined)); 476 | ASSERT_NE(src, nullptr); 477 | Buffer* out = compiler->NewBuffer(DT_EXECUTABLE); 478 | ASSERT_NE(out, nullptr); 479 | std::vector inputs; 480 | inputs.push_back(src); 481 | std::vector options; 482 | options.push_back("-DDEF=10"); 483 | ASSERT_TRUE(compiler->CompileAndLinkExecutable(inputs, out, options)); 484 | ASSERT_TRUE(!out->IsEmpty()); 485 | } 486 | 487 | TEST_F(AMDGPUCompilerTest, CompileToLLVMBitcode_Define2) 488 | { 489 | Data* src = compiler->NewBufferReference(DT_CL, defined, strlen(defined)); 490 | ASSERT_NE(src, nullptr); 491 | Buffer* out = compiler->NewBuffer(DT_LLVM_BC); 492 | ASSERT_NE(out, nullptr); 493 | std::vector inputs; 494 | inputs.push_back(src); 495 | std::vector options; 496 | options.push_back("-D"); 497 | options.push_back("DEF=10"); 498 | ASSERT_TRUE(compiler->CompileToLLVMBitcode(inputs, out, options)); 499 | ASSERT_TRUE(!out->IsEmpty()); 500 | } 501 | 502 | TEST_F(AMDGPUCompilerTest, CompileAndLink_Define2) 503 | { 504 | Data* src = compiler->NewBufferReference(DT_CL, defined, strlen(defined)); 505 | ASSERT_NE(src, nullptr); 506 | Buffer* out = compiler->NewBuffer(DT_EXECUTABLE); 507 | ASSERT_NE(out, nullptr); 508 | std::vector inputs; 509 | inputs.push_back(src); 510 | std::vector options; 511 | options.push_back("-D"); 512 | options.push_back("DEF=10"); 513 | ASSERT_TRUE(compiler->CompileAndLinkExecutable(inputs, out, options)); 514 | ASSERT_TRUE(!out->IsEmpty()); 515 | } 516 | 517 | TEST_F(AMDGPUCompilerTest, CompileToLLVMBitcode_Error_InvalidCL) 518 | { 519 | Data* src = compiler->NewBufferReference(DT_CL, invalidCL, strlen(invalidCL)); 520 | ASSERT_NE(src, nullptr); 521 | Buffer* out = compiler->NewBuffer(DT_LLVM_BC); 522 | ASSERT_NE(out, nullptr); 523 | std::vector inputs; 524 | inputs.push_back(src); 525 | ASSERT_FALSE(compiler->CompileToLLVMBitcode(inputs, out, defaultOptions)); 526 | ASSERT_TRUE(out->IsEmpty()); 527 | ASSERT_TRUE(!compiler->Output().empty()); 528 | } 529 | 530 | TEST_F(AMDGPUCompilerTest, CompileAndLink_Error_InvalidCL) 531 | { 532 | Data* src = compiler->NewBufferReference(DT_CL, invalidCL, strlen(invalidCL)); 533 | ASSERT_NE(src, nullptr); 534 | Buffer* out = compiler->NewBuffer(DT_LLVM_BC); 535 | ASSERT_NE(out, nullptr); 536 | std::vector inputs; 537 | inputs.push_back(src); 538 | ASSERT_FALSE(compiler->CompileAndLinkExecutable(inputs, out, defaultOptions)); 539 | ASSERT_TRUE(out->IsEmpty()); 540 | ASSERT_TRUE(!compiler->Output().empty()); 541 | } 542 | 543 | TEST_F(AMDGPUCompilerTest, LinkLLVMBitcode_Error_InvalidBC) 544 | { 545 | Data* src = compiler->NewBufferReference(DT_LLVM_BC, invalidBC, strlen(invalidBC)); 546 | std::vector inputs; 547 | 548 | inputs.push_back(src); 549 | inputs.push_back(src); 550 | Buffer* out = compiler->NewBuffer(DT_LLVM_BC); 551 | ASSERT_NE(out, nullptr); 552 | ASSERT_FALSE(compiler->LinkLLVMBitcode(inputs, out, emptyOptions)); 553 | ASSERT_TRUE(out->IsEmpty()); 554 | ASSERT_TRUE(!compiler->Output().empty()); 555 | } 556 | --------------------------------------------------------------------------------