/* * Copyright (c) Advanced Micro Devices, Inc., or its affiliates. * * SPDX-License-Identifier: MIT */ #pragma once #include "platform/context.hpp" #include "platform/object.hpp" #include "platform/memory.hpp" #include "amd_comgr/amd_comgr.h" namespace amd { namespace hsa { namespace loader { class Symbol; } // namespace loader namespace code { namespace Kernel { class Metadata; } // namespace Kernel } // namespace code } // namespace hsa } // namespace amd namespace amd { class Device; class Program; namespace option { class Options; } // namespace option } // namespace amd namespace amd::device { class ClBinary; class Kernel; struct SymbolInfo { int sym_type; std::vector* var_names; }; struct SymbolLoweredName { const char* name_expression; std::string* loweredName; }; //! A program object for a specific device. class Program { public: typedef std::pair binary_t; typedef std::pair finfo_t; typedef std::unordered_map kernels_t; // type of the program typedef enum { TYPE_NONE = 0, // uncompiled TYPE_COMPILED, // compiled TYPE_LIBRARY, // linked library TYPE_EXECUTABLE, // linked executable TYPE_INTERMEDIATE // intermediate } type_t; //! type of the input file typedef enum { FILE_TYPE_DEFAULT = 0, FILE_TYPE_OPENCL = 1, FILE_TYPE_LLVMIR_TEXT = 2, FILE_TYPE_LLVMIR_BINARY = 3, FILE_TYPE_SPIR_TEXT = 4, FILE_TYPE_SPIR_BINARY = 5, FILE_TYPE_AMDIL_TEXT = 6, FILE_TYPE_AMDIL_BINARY = 7, FILE_TYPE_X86_TEXT = 8, FILE_TYPE_X86_BINARY = 9, FILE_TYPE_CG = 10, FILE_TYPE_SOURCE = 11, FILE_TYPE_ISA = 12, FILE_TYPE_HEADER = 13, FILE_TYPE_RSLLVMIR_BINARY = 14, FILE_TYPE_SPIRV_BINARY = 15, FILE_TYPE_ASM_TEXT = 16, FILE_TYPE_LAST = 17 } file_type_t; private: //! The device target for this binary. amd::SharedReference device_; amd::Program& owner_; //!< owner of this program kernels_t kernels_; //!< The kernel entry points this binary. type_t type_; //!< type of this program std::vector initKernels_; //!< Init kernels std::vector finiKernels_; //!< Fini kernels bool runInitFiniKernel(const std::vector& kernels) const; protected: union { struct { uint32_t isNull_ : 1; //!< Null program no memory allocations uint32_t internal_ : 1; //!< Internal blit program uint32_t hasGlobalStores_ : 1; //!< Program has writable program scope variables uint32_t isHIP_ : 1; //!< Determine if the program is for HIP uint32_t coLoaded_ : 1; //!< Has the code objected been loaded uint32_t trapHandler_ : 1; //!< It is a trap handler for debugger }; uint32_t flags_; //!< Program flags }; ClBinary* clBinary_; //!< The CL program binary file std::string llvmBinary_; //!< LLVM IR binary code amd::Elf::ElfSections elfSectionType_; //!< LLVM IR binary code is in SPIR format std::string compileOptions_; //!< compile/build options. std::string linkOptions_; //!< link options. //!< the option arg passed in to clCompileProgram(), clLinkProgram(), //!< or clBuildProgram(), whichever is called last std::string lastBuildOptionsArg_; mutable std::string buildLog_; //!< build log. int32_t buildStatus_; //!< build status. int32_t buildError_; //!< build error size_t globalVariableTotalSize_; amd::option::Options* programOptions_; amd_comgr_metadata_node_t metadata_ = {}; //!< COMgr metadata uint32_t codeObjectVer_; //!< version of code object std::map kernelMetadataMap_; //!< Map of kernel metadata //! Sanitizer lock - lock when launching init/fini kernels static std::recursive_mutex initFiniLock_; public: //! Construct a section. Program(amd::Device& device, amd::Program& owner); //! Destroy this binary image. virtual ~Program(); //! Destroy all the kernels void clear(); amd::Program* owner() const { return &owner_; } //! Return the compiler options passed to build this program amd::option::Options* getCompilerOptions() const { return programOptions_; } //! Compile the device program. int32_t compile(const std::string& sourceCode, const std::vector& headers, const char** headerIncludeNames, const char* origOptions, amd::option::Options* options); //! Link the device program. int32_t link(const std::vector& inputPrograms, const char* origLinkOptions, amd::option::Options* linkOptions); //! Build the device program. int32_t build(const std::string& sourceCode, const char* origOptions, amd::option::Options* options); //! Load the device program. bool load(); //! Return the device object, associated with this program. const amd::Device& device() const { return device_(); } //! Return the compiler options used to build the program. const std::string& compileOptions() const { return compileOptions_; } const std::string& linkOptions() const { return linkOptions_; } //! Return the option arg passed in to clCompileProgram(), clLinkProgram(), //! or clBuildProgram(), whichever is called last const std::string lastBuildOptionsArg() const { return lastBuildOptionsArg_; } //! Return the build log. const std::string& buildLog() const { return buildLog_; } //! Return the build status. cl_build_status buildStatus() const { return buildStatus_; } //! Return the build error. int32_t buildError() const { return buildError_; } //! Return the symbols vector. const kernels_t& kernels() const { return kernels_; } kernels_t& kernels() { return kernels_; } //! Add kernel to the map and init fini kernel vector. void addKernel(Kernel* k); //! Return the binary image. inline const binary_t binary() const; inline binary_t binary(); inline finfo_t BinaryFd() const; inline std::string BinaryURI() const; //! Returns the CL program binary file ClBinary* clBinary() { return clBinary_; } const ClBinary* clBinary() const { return clBinary_; } bool setBinary(const char* binaryIn, size_t size, const device::Program* same_dev_prog = nullptr, amd::Os::FileDesc fdesc = amd::Os::FDescInit(), size_t foffset = 0, std::string uri = std::string()); type_t type() const { return type_; } void setGlobalVariableTotalSize(size_t size) { globalVariableTotalSize_ = size; } size_t globalVariableTotalSize() const { return globalVariableTotalSize_; } //! Returns TRUE if the program just compiled bool isNull() const { return isNull_; } //! Returns TRUE if the program used internally by runtime bool isInternal() const { return internal_; } //! Global variables are a part of the code segment bool hasGlobalStores() const { return hasGlobalStores_; } //! Return TRUE if the program has been loaded bool isCodeObjectLoaded() const { return coLoaded_; } //! Returns TRUE if the program is a trap handler for debugger support bool isTrapHandler() const { return trapHandler_; } amd_comgr_metadata_node_t metadata() const { return metadata_; } //! Get the kernel metadata const bool getKernelMetadata(const std::string& name, amd_comgr_metadata_node_t* meta) const { auto it = kernelMetadataMap_.find(name); if (it != kernelMetadataMap_.end()) { *meta = it->second; return true; } return false; } const uint32_t codeObjectVer() const { return codeObjectVer_; } //! Check if program is HIP based const bool isHIP() const { return (isHIP_ == 1); } //! Get mangled name of a name expresion const bool getLoweredNames(std::vector* mangledNames) const; //! Get demangled names bool getDemangledName(const std::string& mangledNames, std::string& demangledNames) const; bool getGlobalFuncFromCodeObj(std::vector* func_names) const; bool getGlobalVarFromCodeObj(std::vector* var_names) const; bool getUndefinedVarFromCodeObj(std::vector* var_names) const; virtual bool createGlobalVarObj(amd::Memory** amd_mem_obj, void** dptr, size_t* bytes, const char* globalName) const { ShouldNotReachHere(); return false; } //! Run kernels marked with "init" kind metadata bool runInitKernels(); //! Run kernels marked with "fini" kind metadata bool runFiniKernels(); protected: //! pre-compile setup bool initBuild(amd::option::Options* options); //! post-compile cleanup bool finiBuild(bool isBuildGood); /*! \brief Compiles GPU CL program to LLVM binary (compiler frontend) * * \return True if we successefully compiled a GPU program */ bool compileImpl(const std::string& sourceCode, //!< the program's source code const std::vector& headers, const char** headerIncludeNames, amd::option::Options* options //!< compile options's object ); //! Link the device program. bool linkImpl(amd::option::Options* options); //! Link the device programs. bool linkImpl(const std::vector& inputPrograms, amd::option::Options* options, bool createLibrary); virtual bool createBinary(amd::option::Options* options) = 0; //! Initialize Binary (used only for clCreateProgramWithBinary()). bool initClBinary(const char* binaryIn, size_t size, amd::Os::FileDesc fdesc = amd::Os::FDescInit(), size_t foffset = 0, std::string uri = std::string()); //! Initialize Binary virtual bool initClBinary(); //! Release the Binary void releaseClBinary(); virtual bool createKernels(void* binary, size_t binSize, bool useUniformWorkGroupSize, bool internalKernel) { return true; } virtual bool setKernels(void* binary, size_t binSize, amd::Os::FileDesc fdesc = amd::Os::FDescInit(), size_t foffset = 0, std::string uri = std::string()) { return true; } //! Returns all the options to be appended while passing to the compiler library std::vector ProcessOptions(amd::option::Options* options); //! Returns all the options to be appended while passing to the compiler library, //! flattened into one string. std::string ProcessOptionsFlattened(amd::option::Options* options); //! At linking time, get the set of compile options to be used from //! the set of input program, warn if they have inconsisten compile options. bool getCompileOptionsAtLinking(const std::vector& inputPrograms, const amd::option::Options* linkOptions); void setType(type_t newType) { type_ = newType; } /* \brief Returns the next stage to compile from, based on sections in binary, * also returns completeStages in a vector, which contains at least ACL_TYPE_DEFAULT, * sets needOptionsCheck to true if options check is needed to decide whether or not to recompile */ file_type_t getCompilationStagesFromBinary(std::vector& completeStages, bool& needOptionsCheck); /* \brief Returns the next stage to compile from, based on sections and options in binary */ file_type_t getNextCompilationStageFromBinary(amd::option::Options* options); //! Finds the total size of all global variables in the program bool FindGlobalVarSize(void* binary, size_t binSize); bool isElf(const char* bin) const { return amd::Elf::isElfMagic(bin); } virtual bool defineGlobalVar(const char* name, void* dptr) { ShouldNotReachHere(); return false; } bool getSymbolsFromCodeObj(std::vector* var_names, amd_comgr_symbol_type_t sym_type) const; bool getUndefinedVarInfo(std::string var_name, void** var_addr, size_t* var_size); bool defineUndefinedVars(); private: //! Dump the log data object to the build log, if a log data object is present void extractBuildLog(amd_comgr_data_set_t dataSet); //! Dump the code object data amd_comgr_status_t extractByteCodeBinary(const amd_comgr_data_set_t inDataSet, const amd_comgr_data_kind_t dataKind, const std::string& outFileName, char* outBinary[] = nullptr, size_t* outSize = nullptr); //! Create code object and add it into the data set amd_comgr_status_t addCodeObjData(const char* source, const size_t size, const amd_comgr_data_kind_t type, const char* name, amd_comgr_data_set_t* dataSet); //! Create action for the specified language, target and options amd_comgr_status_t createAction(const amd_comgr_language_t oclver, const std::vector& options, amd_comgr_action_info_t* action, bool* hasAction); //! Create the bitcode of the linked input dataset bool linkLLVMBitcode(const amd_comgr_data_set_t inputs, const std::vector& options, amd::option::Options* amdOptions, amd_comgr_data_set_t* output, char* binaryData[] = nullptr, size_t* binarySize = nullptr); //! Create the bitcode of the compiled input dataset bool compileToLLVMBitcode(const amd_comgr_data_set_t compileInputs, const std::vector& options, amd::option::Options* amdOptions, char* binaryData[], size_t* binarySize, const bool link_dev_libs = true); //! Compile and create the excutable of the input dataset bool compileAndLinkExecutable(const amd_comgr_data_set_t inputs, const std::vector& options, amd::option::Options* amdOptions, char* executable[], size_t* executableSize, file_type_t continueCompileFrom); //! Create the map for the kernel name and its metadata for fast access bool createKernelMetadataMap(void* binary, size_t binSize); bool trySubstObjFile(const char* SubstCfgFile, const std::string& sourceCode, const amd::option::Options* options); //! Disable default copy constructor Program(const Program&); //! Disable operator= Program& operator=(const Program&); }; class ComgrBinaryData { public: ComgrBinaryData() : binaryData_({0}), created_(false) {} ~ComgrBinaryData(); bool create(amd_comgr_data_kind_t kind, void* binary, size_t binSize); amd_comgr_data_t& data(); private: amd_comgr_data_t binaryData_; bool created_; }; } // namespace amd::device