From 51cc9c2f8c154fb79a7fb14d9ea3ccc4f3fd5726 Mon Sep 17 00:00:00 2001 From: vpykhtin Date: Thu, 3 Dec 2020 16:36:58 +0300 Subject: [PATCH] SWDEV-1 - OpenCL binary substituion feature based on source program text hash matching. This patch allows to substitute binary for the opencl program. It supposed to be used as: 1. Run the opencl program with -save-temps. 2. Open the cl temp and find the following text in the program header: Hash to override: Source: 0xd66bcfa20e69e605 Source + clang options: 0x656a9dd8aedcbfb6 3. Create config file (ascii text) with a pair(s): where hash is the hex value from step 2 (without leading 0x), you can use either hash depending on what you're going to match: only the source text of the program or along with it's clang options. 4. Set the env variable AMD_OCL_SUBST_OBJFILE to the path of your config file. 5. Rerun the opencl program. Change-Id: I977c80fe529ea14458194918c6ddfbe2de6a8857 --- rocclr/device/devprogram.cpp | 98 ++++++++++++++++++++++++++++++++++++ rocclr/device/devprogram.hpp | 4 ++ rocclr/utils/flags.hpp | 2 + 3 files changed, 104 insertions(+) diff --git a/rocclr/device/devprogram.cpp b/rocclr/device/devprogram.cpp index 5b8adb74ce..a0bb55e9a7 100644 --- a/rocclr/device/devprogram.cpp +++ b/rocclr/device/devprogram.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -605,6 +606,17 @@ bool Program::compileAndLinkExecutable(const amd_comgr_data_set_t inputs, } #endif // defined(USE_COMGR_LIBRARY) +static std::size_t getOCLSourceHash(const std::string& sourceCode) { + return std::hash()(sourceCode); +} + +static std::size_t getOCLOptionsHash(const amd::option::Options &options) { + std::string opts; + for (const std::string& S : options.clangOptions) + opts.append(S); + return std::hash()(opts); +} + bool Program::compileImplLC(const std::string& sourceCode, const std::vector& headers, const char** headerIncludeNames, amd::option::Options* options, @@ -705,9 +717,15 @@ bool Program::compileImplLC(const std::string& sourceCode, std::ofstream f(options->getDumpFileName(".cl").c_str(), std::ios::trunc); if (f.is_open()) { + auto srcHash = getOCLSourceHash(sourceCode); + auto optHash = getOCLOptionsHash(*options); + f << "/* Compiler options:\n" "-c -emit-llvm -target amdgcn-amd-amdhsa -x cl " << driverOptionsOStrStr.str() << " -include opencl-c.h " + << "\nHash to override:" + << "\n Source: 0x" << std::setbase(16) << srcHash + << "\n Source + clang options: 0x" << (srcHash ^ optHash) << "\n*/\n\n" << sourceCode; f.close(); @@ -1664,9 +1682,89 @@ int32_t Program::link(const std::vector& inputPrograms, const char* or } // ================================================================================================ +static std::pair +getSubstBinFileName(const char *SubstCfgFile, size_t srcHash, size_t optHash) { + using namespace std; + const size_t srcAndOptHash = srcHash ^ optHash; + ifstream cfgFile(SubstCfgFile); + if (cfgFile.good()) { + string line; + while(getline(cfgFile, line)) { + istringstream ss(line); + size_t hash; + ss >> setbase(16) >> hash; + if (ss.fail() || !isspace(ss.peek())) + continue; + + if (hash == srcAndOptHash || hash == srcHash) { + ss >> ws; + string objFileName; + getline(ss, objFileName); // get the rest of line with spaces + return make_pair(objFileName, hash); + } + } + } else + return make_pair(string(), (size_t)1); + return make_pair(string(), (size_t)0); +} + +bool Program::trySubstObjFile(const char *SubstCfgFile, + const std::string& sourceCode, + const amd::option::Options* options) { + std::string buffer; + std::ostringstream str(buffer); + + size_t srcHash = getOCLSourceHash(sourceCode); + size_t optHash = getOCLOptionsHash(*options); + auto substRes = getSubstBinFileName(SubstCfgFile, srcHash, optHash); + if (substRes.first.empty()) { + switch(substRes.second) { + default: break; + case 1: + str << "Subst failure: cannot open config file " << SubstCfgFile << std::endl; + break; + } + buildLog_ += str.str(); + return false; + } + + uint8_t *binary = nullptr; + size_t binSize = 0; + std::ifstream binFile(substRes.first, std::ios::binary | std::ios::ate); + if (binFile.good()) { + binSize = binFile.tellg(); + binFile.seekg(0, std::ios::beg); + binary = new (std::nothrow) uint8_t[binSize]; + if (binary && !binFile.read(reinterpret_cast(binary), binSize)) { + delete[] binary; + binary = nullptr; + } + } + + if (!binary) { + buildStatus_ = CL_BUILD_ERROR; + buildError_ = CL_BUILD_PROGRAM_FAILURE; + str << "Subst failure: cannot read binary file " << substRes.first << '\n'; + } else { + setKernels(binary, binSize); + buildStatus_ = CL_BUILD_SUCCESS; + buildError_ = 0; + str << "Substituted program hash 0x" + << std::setbase(16) << substRes.second + << " with " << substRes.first << '\n'; + } + buildLog_ += str.str(); + return true; +} + int32_t Program::build(const std::string& sourceCode, const char* origOptions, amd::option::Options* options, const std::vector& preCompiledHeaders) { + if (AMD_OCL_SUBST_OBJFILE != NULL && + trySubstObjFile(AMD_OCL_SUBST_OBJFILE, sourceCode, options)) { + return buildError(); + } + uint64_t start_time = 0; if (options->oVariables->EnableBuildTiming) { buildLog_ = "\nStart timing major build components.....\n\n"; diff --git a/rocclr/device/devprogram.hpp b/rocclr/device/devprogram.hpp index cd2a3c74f6..5b1f74a027 100644 --- a/rocclr/device/devprogram.hpp +++ b/rocclr/device/devprogram.hpp @@ -457,6 +457,10 @@ class Program : public amd::HeapObject { bool createKernelMetadataMap(); #endif + bool trySubstObjFile(const char *SubstCfgFile, + const std::string& sourceCode, + const amd::option::Options* options); + //! Disable default copy constructor Program(const Program&); diff --git a/rocclr/utils/flags.hpp b/rocclr/utils/flags.hpp index 9c826e1ccb..0ceb67de39 100644 --- a/rocclr/utils/flags.hpp +++ b/rocclr/utils/flags.hpp @@ -86,6 +86,8 @@ release(cstring, AMD_OCL_LINK_OPTIONS_APPEND, 0, \ "Append clLinkProgram()'s options") \ release(cstring, AMD_OCL_SC_LIB, 0, \ "Set shader compiler shared library name or path") \ +debug(cstring, AMD_OCL_SUBST_OBJFILE, 0, \ + "Specify binary substitution config file for OpenCL") \ debug(bool, AMD_OCL_ENABLE_MESSAGE_BOX, false, \ "Enable the error dialog on Windows") \ release(size_t, GPU_PINNED_XFER_SIZE, 32, \