509bd691bb
ECR #304775 - Bug 10752 kernel caching feature (AMDIL and HSAIL path)
1. For the stage we want to cache, call getCacheEntry() followed by makeCacheEntry() if the get fails; otherwise directly return cached data.
a. Each device have a separate cache directory
b. It logs caching errors, so we can debug the cache and/or detect collisions
2. Implementeded cache size tracking, so we can evict old data when cache files are too large
3. Added file/path access permission control on both windows and linux
4. Have read/write file lock protection
5. -kcache-disable flag can be used to turn on/off the caching functionality
6. AMD_FORCE_KCACHE_TEST env variable is used for internal testing
TO DO:
1. Tracking of timestamps for cache entries
-LRU eviction when cache grows too large
2. Track cache entries per application
Affected files ...
... //depot/stg/opencl/drivers/opencl/compiler/lib/backends/common/cache.cpp#1 add
... //depot/stg/opencl/drivers/opencl/compiler/lib/backends/common/cache.hpp#1 add
... //depot/stg/opencl/drivers/opencl/compiler/lib/backends/common/frontend.cpp#34 edit
... //depot/stg/opencl/drivers/opencl/compiler/lib/backends/common/frontend_clang.cpp#20 edit
... //depot/stg/opencl/drivers/opencl/compiler/lib/backends/common/v0_8/if_acl.cpp#68 edit
... //depot/stg/opencl/drivers/opencl/compiler/lib/backends/gpu/amdil_be.cpp#43 edit
... //depot/stg/opencl/drivers/opencl/compiler/lib/backends/gpu/hsail_be.cpp#42 edit
... //depot/stg/opencl/drivers/opencl/compiler/lib/utils/OPTIONS.def#124 edit
... //depot/stg/opencl/drivers/opencl/tests/kcache/Makefile#1 add
... //depot/stg/opencl/drivers/opencl/tests/kcache/build/Makefile#1 add
... //depot/stg/opencl/drivers/opencl/tests/kcache/build/Makefile.kcache#1 add
... //depot/stg/opencl/drivers/opencl/tests/kcache/kCacheTest_std.txt#1 add
... //depot/stg/opencl/drivers/opencl/tests/kcache/kernel.cl#1 add
... //depot/stg/opencl/drivers/opencl/tests/kcache/main.cpp#1 add
[ROCm/clr commit: 4b6f2324d0]
1153 regels
34 KiB
C++
1153 regels
34 KiB
C++
#include <sys/stat.h>
|
|
#include "cache.hpp"
|
|
|
|
// Check if a file exists
|
|
//
|
|
// In:
|
|
// fileName - Path to file
|
|
//
|
|
// Out:
|
|
// none
|
|
//
|
|
// Returns:
|
|
// true if the file exists
|
|
// false otherwise
|
|
//
|
|
bool KernelCache::fileExists(const std::string &fileName)
|
|
{
|
|
struct stat info;
|
|
return 0 == stat(fileName.c_str(), &info);
|
|
}
|
|
|
|
// Wipe the cache folder structure
|
|
//
|
|
// In:
|
|
// none
|
|
//
|
|
// Out:
|
|
// none
|
|
//
|
|
// Returns:
|
|
// true if folder wipe is ok
|
|
// false otherwise
|
|
//
|
|
bool KernelCache::wipeCacheFolders()
|
|
{
|
|
for (int i = 0; i < 16; ++i) {
|
|
std::string dir = rootPath;
|
|
std::stringstream ss;
|
|
ss << amd::Os::fileSeparator() << std::hex << i;
|
|
dir += ss.str();
|
|
if (amd::Os::pathExists(dir)) {
|
|
if (false == amd::Os::removePath(dir)) {
|
|
errorMsg = "Error deleting directory in cache";
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Setup cache tree structure
|
|
//
|
|
// In:
|
|
// none
|
|
//
|
|
// Out:
|
|
// none
|
|
//
|
|
// Returns:
|
|
// true if folders setup is ok
|
|
// false otherwise
|
|
//
|
|
bool KernelCache::setUpCacheFolders()
|
|
{
|
|
// Directory structure is distributed as 16 * 16 in order to keep the file count per directory low
|
|
for (int i = 0; i < 16; ++i) {
|
|
for (int j = 0; j < 16; ++j) {
|
|
std::string dir = rootPath;
|
|
std::stringstream ss;
|
|
ss << amd::Os::fileSeparator() << std::hex << i << amd::Os::fileSeparator() << j;
|
|
dir += ss.str();
|
|
if (false == amd::Os::createPath(dir)) {
|
|
errorMsg = "Error creating directory in cache";
|
|
return false;
|
|
}
|
|
// Set folder to only owner accessible
|
|
if (!setAccessPermission(rootPath)) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
#if _WIN32
|
|
// Get Sid of account
|
|
// Caller will need to free the SID buffer (returned in sid)
|
|
//
|
|
// In:
|
|
// userName - accont name
|
|
//
|
|
// Out:
|
|
// sid - Sid of account
|
|
//
|
|
// Return:
|
|
// true if SID is obtained, false otherwise
|
|
//
|
|
bool KernelCache::getSid(TCHAR *username, PSID &sid)
|
|
{
|
|
if (username == NULL) {
|
|
errorMsg = "Invalid user name in getSid mehtod";
|
|
return false;
|
|
}
|
|
// If a buffer is too small, the count parameter will be set to the size needed.
|
|
const DWORD INITIAL_SIZE = 32;
|
|
SID_NAME_USE sidNameUse;
|
|
DWORD cbSid = 0, cchDomainName = 0;
|
|
DWORD dwSidBufferSize = INITIAL_SIZE, dwDomainBufferSize = INITIAL_SIZE;
|
|
// Create buffers for the SID and the domain name
|
|
sid = (PSID) new BYTE[dwSidBufferSize];
|
|
if (sid == NULL) {
|
|
errorMsg = "Failed to allocate space for SID";
|
|
return false;
|
|
}
|
|
memset(sid, 0, dwSidBufferSize);
|
|
TCHAR *wszDomainName = new TCHAR[dwDomainBufferSize];
|
|
if (wszDomainName == NULL) {
|
|
delete[] sid;
|
|
errorMsg = "Failed to allocate space for domain name";
|
|
return false;
|
|
}
|
|
memset(wszDomainName, 0, dwDomainBufferSize * sizeof(TCHAR));
|
|
// Obtain the SID for the account name passed
|
|
while (true) {
|
|
// Set the count variables to the buffer sizes and retrieve the SID
|
|
cbSid = dwSidBufferSize;
|
|
cchDomainName = dwDomainBufferSize;
|
|
if (LookupAccountName(NULL, username, sid, &cbSid, wszDomainName, &cchDomainName, &sidNameUse)) {
|
|
if (IsValidSid(sid) == FALSE) {
|
|
delete[] sid;
|
|
delete[] wszDomainName;
|
|
errorMsg = "The SID for the account is invalid";
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
DWORD dwErrorCode = GetLastError();
|
|
if (dwErrorCode == ERROR_INSUFFICIENT_BUFFER) {
|
|
if (cbSid > dwSidBufferSize) {
|
|
// Reallocate memory for the SID buffer
|
|
delete[] sid;
|
|
sid = (PSID) new BYTE[cbSid];
|
|
if (sid == NULL) {
|
|
delete[] wszDomainName;
|
|
errorMsg = "Failed to allocate space for SID";
|
|
return false;
|
|
}
|
|
memset(sid, 0, cbSid);
|
|
dwSidBufferSize = cbSid;
|
|
}
|
|
if (cchDomainName > dwDomainBufferSize) {
|
|
// Reallocate memory for the domain name buffer
|
|
delete[] wszDomainName;
|
|
wszDomainName = new TCHAR[cchDomainName];
|
|
if (wszDomainName == NULL) {
|
|
delete[] sid;
|
|
errorMsg = "Failed to allocate space for domain name";
|
|
}
|
|
memset(wszDomainName, 0, cchDomainName * sizeof(TCHAR));
|
|
dwDomainBufferSize = cchDomainName;
|
|
}
|
|
} else {
|
|
delete[] sid;
|
|
delete[] wszDomainName;
|
|
errorMsg = "Failed to get user security identifier for the account, GetLastError returned"
|
|
+ dwErrorCode;
|
|
return false;
|
|
}
|
|
}
|
|
delete[] wszDomainName;
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
// Set file to only owner accessible
|
|
//
|
|
// In:
|
|
// fileName - Path to file
|
|
// isFile - True if fileName is a file, false if it is a path; false by default
|
|
//
|
|
// Out:
|
|
// none
|
|
//
|
|
// Returns:
|
|
// true if access permission is under control
|
|
// false otherwise
|
|
//
|
|
bool KernelCache::setAccessPermission(const std::string fileName, bool isFile)
|
|
{
|
|
#if _WIN32
|
|
TCHAR username[UNLEN + 1];
|
|
DWORD username_len = UNLEN + 1;
|
|
if (!GetUserName(username, &username_len)) {
|
|
errorMsg = "Failed to get user name for the account";
|
|
return false;
|
|
}
|
|
PSID sid = NULL;
|
|
if (!getSid(username, sid)) {
|
|
return false;
|
|
}
|
|
if (SetNamedSecurityInfo((LPTSTR)(fileName.c_str()), SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION,
|
|
sid, NULL, NULL, NULL) != ERROR_SUCCESS ) {
|
|
delete[] sid;
|
|
errorMsg = "Failed to set user access permission";
|
|
return false;
|
|
}
|
|
delete[] sid;
|
|
#else
|
|
int ret = -1;
|
|
if (isFile) {
|
|
ret = chmod(fileName.c_str(), S_IRUSR | S_IWUSR);
|
|
} else {
|
|
ret = chmod(fileName.c_str(), S_IRUSR | S_IWUSR | S_IXUSR);
|
|
}
|
|
if (ret < 0) {
|
|
errorMsg = "Failed to set user access permission";
|
|
return false;
|
|
}
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
// Open a file and read its contents
|
|
// Caller will need to free the memory created by this function (returned in contents)
|
|
//
|
|
// In:
|
|
// fileName - Path to file
|
|
// fileSize - File size
|
|
//
|
|
// Out:
|
|
// conttents - Pointer to file contents
|
|
//
|
|
// Returns:
|
|
// true if the file is read successfully
|
|
// false otherwise
|
|
//
|
|
bool KernelCache::readFile(const std::string &fileName, char **contents, size_t &fileSize)
|
|
{
|
|
*contents = NULL;
|
|
#if _WIN32
|
|
HANDLE hFile = CreateFile(fileName.c_str(), GENERIC_READ, FILE_SHARE_READ,
|
|
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (hFile == INVALID_HANDLE_VALUE) {
|
|
errorMsg = "Error opening file for reading";
|
|
return false;
|
|
}
|
|
LARGE_INTEGER size;
|
|
if (!GetFileSizeEx(hFile, &size)) {
|
|
errorMsg = "Last error code is " + std::to_string(GetLastError());
|
|
CloseHandle(hFile);
|
|
return false;
|
|
}
|
|
char *memblock = new char [size.QuadPart];
|
|
if (memblock == NULL) {
|
|
errorMsg = "Out of memory";
|
|
CloseHandle(hFile);
|
|
return false;
|
|
}
|
|
DWORD dwBytesRead = 0;
|
|
if(FALSE == ReadFile(hFile, memblock, size.QuadPart, &dwBytesRead, NULL)) {
|
|
errorMsg = "Unable to read cache file";
|
|
CloseHandle(hFile);
|
|
return false;
|
|
}
|
|
if (dwBytesRead != size.QuadPart) {
|
|
errorMsg = "Error reading cache file";
|
|
CloseHandle(hFile);
|
|
return false;
|
|
}
|
|
CloseHandle(hFile);
|
|
*contents = memblock;
|
|
fileSize = size.QuadPart;
|
|
#else
|
|
FILE *cacheFile = fopen(fileName.c_str(), "rb");
|
|
if (cacheFile == NULL) {
|
|
errorMsg = "Error opening file for reading";
|
|
return false;
|
|
}
|
|
// Read lock for cache file
|
|
int fd = fileno(cacheFile);
|
|
if (fd == -1) {
|
|
errorMsg = "Error getting file descriptor";
|
|
fclose(cacheFile);
|
|
return false;
|
|
}
|
|
struct flock fl = {F_RDLCK, SEEK_SET, 0, 0};
|
|
if (fcntl(fd, F_SETLK, &fl) == -1) {
|
|
errorMsg = "Error setting file read lock";
|
|
fclose(cacheFile);
|
|
return false;
|
|
}
|
|
// Read the file
|
|
fseek(cacheFile, 0, SEEK_END);
|
|
size_t size = ftell(cacheFile);
|
|
rewind(cacheFile);
|
|
char *memblock = new char [size];
|
|
if (memblock == NULL) {
|
|
errorMsg = "Out of memory";
|
|
fclose(cacheFile);
|
|
return false;
|
|
}
|
|
size_t result = fread(memblock, sizeof(char), size, cacheFile);
|
|
// Unlock the file
|
|
fl.l_type = F_UNLCK;
|
|
if (fcntl(fd, F_SETLK, &fl) == -1) {
|
|
errorMsg = "Error unlock file read lock";
|
|
fclose(cacheFile);
|
|
return false;
|
|
}
|
|
if (result != size) {
|
|
errorMsg = "Error reading cache file";
|
|
fclose(cacheFile);
|
|
delete[] memblock;
|
|
return false;
|
|
}
|
|
fclose(cacheFile);
|
|
*contents = memblock;
|
|
fileSize = size;
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
// Open a file and write its contents
|
|
//
|
|
// In:
|
|
// fileName - Path to file
|
|
// data - Pointer to file contents
|
|
// size - Data size
|
|
//
|
|
// Out:
|
|
// none
|
|
//
|
|
// Returns:
|
|
// true if the file is written to file successfully
|
|
// false otherwise
|
|
//
|
|
bool KernelCache::writeFile(const std::string &fileName, const char *data, size_t size)
|
|
{
|
|
#if _WIN32
|
|
HANDLE hFile = CreateFile(fileName.c_str(), GENERIC_WRITE | WRITE_OWNER | READ_CONTROL,
|
|
0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (hFile == INVALID_HANDLE_VALUE) {
|
|
errorMsg = "Error opening file for writing";
|
|
return false;
|
|
}
|
|
// Write file exclusively
|
|
DWORD dwBytesWritten = 0;
|
|
if (FALSE == WriteFile(hFile, data, size, &dwBytesWritten, NULL)) {
|
|
errorMsg = "Unable to write to file";
|
|
CloseHandle(hFile);
|
|
return false;
|
|
}
|
|
if (dwBytesWritten != size) {
|
|
errorMsg = "Error writing cache file";
|
|
CloseHandle(hFile);
|
|
return false;
|
|
}
|
|
CloseHandle(hFile);
|
|
#else
|
|
FILE *cacheFile = fopen(fileName.c_str(), "wb");
|
|
if (cacheFile == NULL) {
|
|
errorMsg = "Error opening file for writing";
|
|
return false;
|
|
}
|
|
// Exclusive write lock for cache file
|
|
int fd = fileno(cacheFile);
|
|
if (fd == -1) {
|
|
errorMsg = "Error getting file descriptor";
|
|
fclose(cacheFile);
|
|
return false;
|
|
}
|
|
struct flock fl = {F_WRLCK, SEEK_SET, 0, 0};
|
|
if (fcntl(fd, F_SETLK, &fl) == -1) {
|
|
errorMsg = "Error setting file write lock";
|
|
std::cout << errorMsg << std::endl;
|
|
fclose(cacheFile);
|
|
return false;
|
|
}
|
|
// Write data to file
|
|
fwrite(data, sizeof(char), size, cacheFile);
|
|
// Unlock the file
|
|
fl.l_type = F_UNLCK;
|
|
if (fcntl(fd, F_SETLK, &fl) == -1) {
|
|
errorMsg = "Error unlock file write lock";
|
|
fclose(cacheFile);
|
|
return false;
|
|
}
|
|
fclose(cacheFile);
|
|
#endif
|
|
// Set file to only owner accessible
|
|
if (!setAccessPermission(fileName, true)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Set the cache's root path
|
|
//
|
|
// In:
|
|
// chipName - Chip name
|
|
//
|
|
// Out:
|
|
// none
|
|
//
|
|
// Returns:
|
|
// true if root path of cache is set successfully
|
|
// false otherwise
|
|
//
|
|
bool KernelCache::setRootPath(const std::string &chipName)
|
|
{
|
|
bool ok = false;
|
|
rootPath.clear();
|
|
#if _WIN32
|
|
// Set root path to <USER>\AppData\Local\AMD\CLCache
|
|
TCHAR userLocalAppDir[_MAX_PATH];
|
|
// Get path for user specific and non-roaming data
|
|
if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, userLocalAppDir))) {
|
|
rootPath = userLocalAppDir;
|
|
} else {
|
|
errorMsg = "User's local app dir is not found";
|
|
return false;
|
|
}
|
|
// Ok, we have <USER>\AppData\Local, let's check for the rest and create if needed
|
|
rootPath += "\\AMD";
|
|
if (!amd::Os::pathExists(rootPath)) {
|
|
ok = amd::Os::createPath(rootPath);
|
|
if (!ok) {
|
|
errorMsg = "Failed to create AMD directory";
|
|
return false;
|
|
}
|
|
}
|
|
rootPath += "\\CLCache";
|
|
if (!amd::Os::pathExists(rootPath)) {
|
|
ok = amd::Os::createPath(rootPath);
|
|
if (!ok) {
|
|
errorMsg = "Failed to create CLCache directory";
|
|
return false;
|
|
}
|
|
}
|
|
// Set folder to only owner accessible
|
|
if (!setAccessPermission(rootPath)) {
|
|
return false;
|
|
}
|
|
rootPath += "\\" + chipName;
|
|
if (!amd::Os::pathExists(rootPath)) {
|
|
ok = amd::Os::createPath(rootPath);
|
|
if (!ok) {
|
|
errorMsg = "Failed to create " + chipName + " directory";
|
|
return false;
|
|
}
|
|
}
|
|
// Set folder to only owner accessible
|
|
if (!setAccessPermission(rootPath)) {
|
|
return false;
|
|
}
|
|
#else
|
|
const char *homedir = getpwuid(getuid())->pw_dir;
|
|
if (homedir == NULL) {
|
|
errorMsg = "Failed to get HOME directory";
|
|
return false;
|
|
}
|
|
rootPath = homedir;
|
|
// Verify the path exists
|
|
if (!amd::Os::pathExists(rootPath)) {
|
|
errorMsg = "User's home directory is not created";
|
|
return false;
|
|
}
|
|
// Ok, we have <HOME>, let's check for the rest and create if needed
|
|
rootPath += "/.AMD";
|
|
if (!amd::Os::pathExists(rootPath)) {
|
|
ok = amd::Os::createPath(rootPath);
|
|
if (!ok) {
|
|
errorMsg = "Failed to create AMD directory";
|
|
return false;
|
|
}
|
|
}
|
|
rootPath += "/CLCache";
|
|
if (!amd::Os::pathExists(rootPath)) {
|
|
ok = amd::Os::createPath(rootPath);
|
|
if (!ok) {
|
|
errorMsg = "Failed to create CLCache directory";
|
|
return false;
|
|
}
|
|
}
|
|
// Set folder to only owner accessible
|
|
if (!setAccessPermission(rootPath)) {
|
|
return false;
|
|
}
|
|
rootPath += "/" + chipName;
|
|
if (!amd::Os::pathExists(rootPath)) {
|
|
ok = amd::Os::createPath(rootPath);
|
|
if (!ok) {
|
|
errorMsg = "Failed to create " + chipName + " directory";
|
|
return false;
|
|
}
|
|
}
|
|
// Set folder to only owner accessible
|
|
if (!setAccessPermission(rootPath)) {
|
|
return false;
|
|
}
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
// Set the cache version and size
|
|
//
|
|
// In:
|
|
// newVersion - New version for the cache
|
|
// newSize - New size for the cache
|
|
//
|
|
// Out:
|
|
// none
|
|
//
|
|
// Returns:
|
|
// true if successful
|
|
// false otherwise
|
|
//
|
|
bool KernelCache::setCacheInfo(unsigned int newVersion, unsigned int newSize)
|
|
{
|
|
bool ok = true;
|
|
char fileData[8];
|
|
char *dataPtr = fileData;
|
|
*(unsigned int *)dataPtr = newVersion;
|
|
dataPtr += sizeof(unsigned int);
|
|
*(unsigned int *)dataPtr = newSize;
|
|
ok = writeFile(indexName, fileData, sizeof(fileData));
|
|
if (!ok) {
|
|
errorMsg = "Failed to update the index file";
|
|
return false;
|
|
}
|
|
version = newVersion;
|
|
cacheSize = newSize;
|
|
return ok;
|
|
}
|
|
|
|
// Get the version and size of the cache
|
|
//
|
|
// In:
|
|
// none
|
|
//
|
|
// Out:
|
|
// none
|
|
//
|
|
// Returns:
|
|
// true if successful
|
|
// false otherwise
|
|
//
|
|
bool KernelCache::getCacheInfo()
|
|
{
|
|
bool ok = true;
|
|
indexName = rootPath;
|
|
indexName += amd::Os::fileSeparator();
|
|
indexName += "cacheDir";
|
|
// Check for cache index file
|
|
char *contents = NULL;
|
|
size_t size;
|
|
ok = readFile(indexName, &contents, size);
|
|
if (ok) {
|
|
char *tmp = contents;
|
|
if (size >= 8) {
|
|
version = *(unsigned int *)tmp;
|
|
tmp += sizeof(unsigned int);
|
|
cacheSize = *(unsigned int *)tmp;
|
|
} else {
|
|
errorMsg = "Index file truncated";
|
|
ok = false;
|
|
}
|
|
delete[] contents;
|
|
} else {
|
|
ok = setCacheInfo(-1, 0);
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
// Initialize the cache
|
|
//
|
|
// In:
|
|
// compilerVersion - Compiler version
|
|
//
|
|
// Out:
|
|
// none
|
|
//
|
|
// Returns:
|
|
// true if successful
|
|
// false otherwise
|
|
//
|
|
bool KernelCache::cacheInit(unsigned int compilerVersion, const std::string &chipName)
|
|
{
|
|
if (!setRootPath(chipName)) {
|
|
return false;
|
|
}
|
|
if (!getCacheInfo()) {
|
|
return false;
|
|
}
|
|
// Limit cache size to default cache size, and wipe out all cache files when it's exceed
|
|
// TODO: need to implement cache eviction policy
|
|
if (version != compilerVersion || cacheSize > DEFAULT_CACHE_SIZE) {
|
|
if (!wipeCacheFolders()) {
|
|
return false;
|
|
}
|
|
if (!setCacheInfo(compilerVersion, 0)) {
|
|
return false;
|
|
}
|
|
if (!setUpCacheFolders()) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Compute the hash value for a buffer of data along with the kernelName and buildOpts
|
|
//
|
|
// In:
|
|
// data - Data to hash
|
|
// size - Size of data
|
|
// buildOpts - Build options
|
|
// kernelName - Kernel name
|
|
//
|
|
// Out:
|
|
// none
|
|
//
|
|
// Returns:
|
|
// Hash value computed from the inputs
|
|
//
|
|
unsigned int KernelCache::computeHash(const KernelCacheData *data, const unsigned int numData,
|
|
const std::string &buildOpts, const std::string &kernelName)
|
|
{
|
|
unsigned int hashVal = 0;
|
|
// Two big prime numbers to start the multiplicative hash
|
|
unsigned int seed0 = 500321;
|
|
unsigned int seed1 = 72701;
|
|
for (unsigned int i = 0; i < numData; ++i) {
|
|
for (unsigned int j = 0; j < data[i].dataSize; ++j) {
|
|
hashVal = hashVal * seed0 + data[i].data[j];
|
|
seed0 *= seed1;
|
|
}
|
|
}
|
|
char *strData = (char *)buildOpts.c_str();
|
|
for (unsigned int i = 0; i < buildOpts.size(); ++i) {
|
|
hashVal = hashVal * seed0 + strData[i];
|
|
seed0 *= seed1;
|
|
}
|
|
strData = (char *)kernelName.c_str();
|
|
for (unsigned int i = 0; i < kernelName.size(); ++i) {
|
|
hashVal = hashVal * seed0 + strData[i];
|
|
seed0 *= seed1;
|
|
}
|
|
return hashVal;
|
|
}
|
|
|
|
// Parses a cache file and returns the contents
|
|
//
|
|
// In:
|
|
// fileData - File data we are to parse
|
|
// dataSize - Size of file data
|
|
//
|
|
// Out:
|
|
// srcData - Pointer to pointer to source data allocated here
|
|
// srcSize - Size of source data
|
|
// buildOpts - Build options from file
|
|
// kernelName - Kernel name from file
|
|
// dstData - Pointer to pointer to destination data allocation here
|
|
// dstSize - Size of destination data
|
|
// dstHash - Destination hash from file
|
|
//
|
|
// Returns:
|
|
// true if no errors during parsing
|
|
// false otherwise
|
|
//
|
|
bool KernelCache::parseFile(const char *fileData, const unsigned int dataSize,
|
|
KernelCacheData **srcData, unsigned int &srcNum, std::string &buildOpts,
|
|
std::string &kernelName, char **dstData, unsigned int &dstSize, unsigned int &dstHash)
|
|
{
|
|
unsigned int dataLeft = dataSize;
|
|
char *data = (char *)fileData;
|
|
errorMsg = "Error parsing file";
|
|
if ((dataLeft > 3) && ((fileData[0] != 'A') || (fileData[1] != 'M') || (fileData[2] != 'D'))) {
|
|
errorMsg = "Not a valid cache file";
|
|
return false;
|
|
}
|
|
data += 3;
|
|
dataLeft -= 3;
|
|
if (dataLeft < sizeof(unsigned int)) {
|
|
errorMsg = "Error in cache file";
|
|
return false;
|
|
}
|
|
dstHash = *(unsigned int*)data;
|
|
data += sizeof(unsigned int);
|
|
dataLeft -= sizeof(unsigned int);
|
|
if (dataLeft-- < 1 || *data++ != ESCAPE) {
|
|
errorMsg = "Error in cache file";
|
|
return false;
|
|
}
|
|
if (dataLeft < sizeof(unsigned int)) {
|
|
errorMsg = "Error in cache file";
|
|
return false;
|
|
}
|
|
srcNum = *(unsigned int*)data;
|
|
data += sizeof(unsigned int);
|
|
dataLeft -= sizeof(unsigned int);
|
|
if (dataLeft-- < 1 || *data++ != ESCAPE) {
|
|
errorMsg = "Error in cache file";
|
|
return false;
|
|
}
|
|
*srcData = new KernelCacheData [srcNum];
|
|
if (*srcData == NULL) {
|
|
errorMsg = "Failed to alloc srcData";
|
|
return false;
|
|
}
|
|
for (unsigned int i = 0; i < srcNum; ++i) {
|
|
if (dataLeft < sizeof(unsigned int)) {
|
|
delete[] *srcData;
|
|
*srcData = NULL;
|
|
srcNum = 0;
|
|
errorMsg = "Error in cache file";
|
|
return false;
|
|
}
|
|
(*srcData)[i].dataSize = *(unsigned int*)data;
|
|
data += sizeof(unsigned int);
|
|
dataLeft -= sizeof(unsigned int);
|
|
if (dataLeft-- < 1 || *data++ != ESCAPE) {
|
|
delete[] *srcData;
|
|
*srcData = NULL;
|
|
srcNum = 0;
|
|
errorMsg = "Error in cache file";
|
|
return false;
|
|
}
|
|
(*srcData)[i].data = data;
|
|
if (dataLeft < (*srcData)[i].dataSize) {
|
|
delete[] *srcData;
|
|
*srcData = NULL;
|
|
srcNum = 0;
|
|
errorMsg = "Error in cache file";
|
|
return false;
|
|
}
|
|
memcpy((*srcData)[i].data, data, (*srcData)[i].dataSize);
|
|
data += (*srcData)[i].dataSize;
|
|
dataLeft -= (*srcData)[i].dataSize;
|
|
if (dataLeft-- < 1 || *data++ != ESCAPE) {
|
|
delete[] *srcData;
|
|
*srcData = NULL;
|
|
srcNum = 0;
|
|
errorMsg = "Error in cache file";
|
|
return false;
|
|
}
|
|
}
|
|
unsigned int buildOptsSize = 0;
|
|
if (dataLeft < sizeof(unsigned int)) {
|
|
delete[] *srcData;
|
|
*srcData = NULL;
|
|
srcNum = 0;
|
|
errorMsg = "Error in cache file";
|
|
return false;
|
|
}
|
|
buildOptsSize = *(unsigned int*)data;
|
|
data += sizeof(unsigned int);
|
|
dataLeft -= sizeof(unsigned int);
|
|
if (dataLeft-- < 1 || *data++ != ESCAPE) {
|
|
delete[] *srcData;
|
|
*srcData = NULL;
|
|
srcNum = 0;
|
|
errorMsg = "Error in cache file";
|
|
return false;
|
|
}
|
|
buildOpts = data;
|
|
if (dataLeft < buildOptsSize) {
|
|
delete[] *srcData;
|
|
*srcData = NULL;
|
|
srcNum = 0;
|
|
errorMsg = "Error in cache file";
|
|
return false;
|
|
}
|
|
data += buildOptsSize;
|
|
dataLeft -= buildOptsSize;
|
|
if (dataLeft-- < 1 || *data++ != ESCAPE) {
|
|
delete[] *srcData;
|
|
*srcData = NULL;
|
|
srcNum = 0;
|
|
errorMsg = "Error in cache file";
|
|
return false;
|
|
}
|
|
unsigned int kernelNameSize;
|
|
if (dataLeft < sizeof(unsigned int)) {
|
|
delete[] *srcData;
|
|
*srcData = NULL;
|
|
srcNum = 0;
|
|
errorMsg = "Error in cache file";
|
|
return false;
|
|
}
|
|
kernelNameSize = *(unsigned int*)data;
|
|
data += sizeof(unsigned int);
|
|
dataLeft -= sizeof(unsigned int);
|
|
if (dataLeft-- < 1 || *data++ != ESCAPE) {
|
|
delete[] *srcData;
|
|
*srcData = NULL;
|
|
srcNum = 0;
|
|
errorMsg = "Error in cache file";
|
|
return false;
|
|
}
|
|
kernelName = data;
|
|
if (dataLeft < kernelNameSize) {
|
|
delete[] *srcData;
|
|
*srcData = NULL;
|
|
srcNum = 0;
|
|
errorMsg = "Error in cache file";
|
|
return false;
|
|
}
|
|
data += kernelNameSize;
|
|
dataLeft -= kernelNameSize;
|
|
if (dataLeft-- < 1 || *data++ != ESCAPE) {
|
|
delete[] *srcData;
|
|
*srcData = NULL;
|
|
srcNum = 0;
|
|
errorMsg = "Error in cache file";
|
|
return false;
|
|
}
|
|
if (dataLeft < sizeof(unsigned int)) {
|
|
delete[] *srcData;
|
|
*srcData = NULL;
|
|
srcNum = 0;
|
|
errorMsg = "Error in cache file";
|
|
return false;
|
|
}
|
|
dstSize = *(unsigned int*)data;
|
|
data += sizeof(unsigned int);
|
|
dataLeft -= sizeof(unsigned int);
|
|
if (dataLeft-- < 1 || *data++ != ESCAPE) {
|
|
delete[] *srcData;
|
|
*srcData = NULL;
|
|
srcNum = 0;
|
|
errorMsg = "Error in cache file";
|
|
return false;
|
|
}
|
|
*dstData = new char [dstSize];
|
|
if (!*dstData || dataLeft != dstSize) {
|
|
if (!*dstData) {
|
|
errorMsg = "Failed to alloc dstData";
|
|
} else {
|
|
errorMsg = "Error in cache file";
|
|
}
|
|
delete[] srcData;
|
|
srcData = NULL;
|
|
srcNum = 0;
|
|
return false;
|
|
}
|
|
memcpy(*dstData, data, dstSize);
|
|
errorMsg.clear();
|
|
return true;
|
|
}
|
|
|
|
// Control kernel cache test
|
|
//
|
|
// In:
|
|
// canUseCache - flag to indicate whether turn on/off cache test
|
|
//
|
|
// Out:
|
|
// none
|
|
//
|
|
bool KernelCache::internalKCacheTestSwitch(bool &canUseCache) {
|
|
#ifndef OPENCL_MAINLINE
|
|
const char *cache_test_switch = getenv("AMD_FORCE_KCACHE_TEST");
|
|
if (!(cache_test_switch && strcmp(cache_test_switch,"1") == 0)) {
|
|
canUseCache = false;
|
|
return false;
|
|
} else {
|
|
return true;
|
|
}
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
// Allocate memory for cache file and build file structure
|
|
//
|
|
// In:
|
|
// srcData - Source data
|
|
// srcSize - Source data size
|
|
// buildOpts - Build options
|
|
// kernelName - Kernel name
|
|
// dstData - Destination data
|
|
// dstSize - Destination data size
|
|
// dstHash - Hash of destination data, buildOpts and kernelName
|
|
//
|
|
// Out:
|
|
// fileData - Pointer to pointer to file data
|
|
// dataSize - Size of file data
|
|
//
|
|
// Returns:
|
|
// true if successful
|
|
// false otherwise
|
|
//
|
|
bool KernelCache::buildFile(const KernelCacheData *srcData, const unsigned int srcNum,
|
|
const std::string &buildOpts, const std::string &kernelName, const char *dstData,
|
|
const unsigned int dstSize, const unsigned int dstHash, char **fileData, unsigned int &dataSize)
|
|
{
|
|
bool ok = false;
|
|
const unsigned int buildOptsSize = buildOpts.size() + 1; // Add one for NULL terminator
|
|
const unsigned int kernelNameSize = kernelName.size() + 1; // Add one for NULL terminator
|
|
dataSize = 3 /* 'AMD' */ + sizeof(dstHash) + 1 /* ESC */ + sizeof(srcNum) + 1 /* ESC */
|
|
+ sizeof(buildOptsSize) + 1 /* ESC */
|
|
+ buildOptsSize /* buildOpts data with NULL termination */ + 1 /* ESC */
|
|
+ sizeof(kernelNameSize) + 1 /* ESC */
|
|
+ kernelNameSize /* kernelName with NULL termination */ + 1 /* ESC */
|
|
+ sizeof(dstSize) + 1 /* ESC */
|
|
+ dstSize /* dst data */;
|
|
for (unsigned int i = 0; i < srcNum; ++i) {
|
|
dataSize += sizeof(srcData[i].dataSize) + 1 + srcData[i].dataSize + 1;
|
|
}
|
|
*fileData = new char [dataSize];
|
|
if (fileData) {
|
|
char *tmp = *fileData;
|
|
tmp[0] = 'A';
|
|
tmp[1] = 'M';
|
|
tmp[2] = 'D';
|
|
tmp += 3;
|
|
*(unsigned int *)tmp = dstHash;
|
|
tmp += sizeof(dstHash);
|
|
*tmp++ = ESCAPE;
|
|
*(unsigned int *)tmp = srcNum;
|
|
tmp += sizeof(srcNum);
|
|
*tmp++ = ESCAPE;
|
|
for (unsigned int i = 0; i < srcNum; ++i) {
|
|
*(unsigned int *)tmp = srcData[i].dataSize;
|
|
tmp += sizeof(srcData[i].dataSize);
|
|
*tmp++ = ESCAPE;
|
|
memcpy(tmp, srcData[i].data, srcData[i].dataSize);
|
|
tmp += srcData[i].dataSize;
|
|
*tmp++ = ESCAPE;
|
|
}
|
|
*(unsigned int *)tmp = (unsigned int)buildOptsSize;
|
|
tmp += sizeof(unsigned int);
|
|
*tmp++ = ESCAPE;
|
|
memcpy(tmp, buildOpts.c_str(), buildOptsSize);
|
|
tmp += buildOptsSize;
|
|
*tmp++ = ESCAPE;
|
|
*(unsigned int *)tmp = (unsigned int)kernelNameSize;
|
|
tmp += sizeof(unsigned int);
|
|
*tmp++ = ESCAPE;
|
|
memcpy(tmp, kernelName.c_str(), kernelNameSize);
|
|
tmp += kernelNameSize;
|
|
*tmp++ = ESCAPE;
|
|
*(unsigned int*)tmp = dstSize;
|
|
tmp += sizeof(dstSize);
|
|
*tmp++ = ESCAPE;
|
|
memcpy(tmp, dstData, dstSize);
|
|
ok = true;
|
|
} else {
|
|
errorMsg = "Out of memory allocating fileData";
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
// Generate file path from a hash value
|
|
//
|
|
// In:
|
|
// hashVal - A hash value
|
|
// pathToFile - Path to the file
|
|
//
|
|
// Returns:
|
|
// nothing
|
|
//
|
|
void KernelCache::getFilePathFromHash(const unsigned int hashVal, std::string &pathToFile)
|
|
{
|
|
char textHash[9];
|
|
sprintf(textHash, "%08x", hashVal);
|
|
std::string fileName = textHash;
|
|
pathToFile = rootPath;
|
|
pathToFile += amd::Os::fileSeparator();
|
|
// First char determines first dir level
|
|
pathToFile += fileName[0];
|
|
pathToFile += amd::Os::fileSeparator();
|
|
// Second char determines second dir level
|
|
pathToFile += fileName[1];
|
|
pathToFile += amd::Os::fileSeparator();
|
|
// Rest of file name determines name
|
|
pathToFile += fileName.c_str() + 2;
|
|
}
|
|
|
|
// Use data, buildOpts and kernelName to generate a file name
|
|
//
|
|
// In:
|
|
// data - Pointer to data list
|
|
// numData - Size of the list
|
|
// buildOpts - Build options
|
|
// kernelName - Kernel name
|
|
//
|
|
// Out:
|
|
// pathToFile - Path to the file
|
|
//
|
|
// Returns:
|
|
// nothing
|
|
//
|
|
void KernelCache::makeFileName(const KernelCacheData *data, const unsigned int numData,
|
|
const std::string &buildOpts, const std::string &kernelName, std::string &pathToFile)
|
|
{
|
|
unsigned int hashVal;
|
|
hashVal = computeHash(data, numData, buildOpts, kernelName);
|
|
getFilePathFromHash(hashVal, pathToFile);
|
|
}
|
|
|
|
// Get the path to a cache entry based on the input data
|
|
//
|
|
// In:
|
|
// data - Pointer to data list
|
|
// numData - Size of the list
|
|
// buildOpts - Build options
|
|
// kernelName - Kernel name
|
|
//
|
|
// Out:
|
|
// pathToFile - Path to the file
|
|
//
|
|
// Returns:
|
|
// true if file exists, false otherwise
|
|
//
|
|
bool KernelCache::findCacheEntry(const KernelCacheData *data, const unsigned int numData,
|
|
const std::string &buildOpts, const std::string &kernelName, std::string &pathToFile)
|
|
{
|
|
makeFileName(data, numData, buildOpts, kernelName, pathToFile);
|
|
return fileExists(pathToFile);
|
|
}
|
|
|
|
// Use srcData, buildOpts and kernelName to find the corresponding cache entry, if it exists
|
|
//
|
|
// In:
|
|
// srcData - Source data
|
|
// srcNum - Number of source data
|
|
// buildOpts - Build options
|
|
// kernelName - Kernel name
|
|
//
|
|
// Out:
|
|
// dstData - Destination data
|
|
// dstSize - Destination size
|
|
// dstHash - Destination hash
|
|
//
|
|
// Returns:
|
|
// true if entry found
|
|
// false otherwise, check errorMsg for errors
|
|
//
|
|
bool KernelCache::getCacheEntry(const KernelCacheData *srcData, const unsigned int srcNum,
|
|
const std::string &buildOpts, const std::string &kernelName, char **dstData, unsigned int &dstSize,
|
|
unsigned int &dstHash)
|
|
{
|
|
std::string pathToFile;
|
|
*dstData = NULL;
|
|
dstSize = 0;
|
|
errorMsg.clear();
|
|
bool ok = findCacheEntry(srcData, srcNum, buildOpts, kernelName, pathToFile);
|
|
if (ok) {
|
|
// Found cache entry
|
|
char *contents;
|
|
size_t size;
|
|
ok = readFile(pathToFile, &contents, size);
|
|
if (ok) {
|
|
// Found entry in cache
|
|
KernelCacheData *fileSrcData;
|
|
unsigned int fileSrcNum;
|
|
std::string fileBuildOpts, fileKernelName;
|
|
ok = parseFile(contents, size, &fileSrcData, fileSrcNum, fileBuildOpts, fileKernelName, dstData, dstSize, dstHash);
|
|
if (srcNum == fileSrcNum) {
|
|
for (unsigned int i = 0; i < srcNum; ++i) {
|
|
if (fileSrcData[i].dataSize == srcData[i].dataSize) {
|
|
if (!compareData((const char *)fileSrcData[i].data, srcData[i].data, srcData[i].dataSize)) {
|
|
ok = false;
|
|
errorMsg = "Cache collision: Size matches, contents do not";
|
|
} else {
|
|
}
|
|
} else {
|
|
ok = false;
|
|
errorMsg = "Cache collision: Data size does not match";
|
|
}
|
|
}
|
|
if (ok == true) {
|
|
if (buildOpts.size() == fileBuildOpts.size()) {
|
|
if (memcmp(buildOpts.c_str(), fileBuildOpts.c_str(), buildOpts.size())) {
|
|
ok = false;
|
|
errorMsg = "Cache collision: Build opts do not match";
|
|
} else if (kernelName.size() == fileKernelName.size()) {
|
|
if (memcmp(kernelName.c_str(), fileKernelName.c_str(), kernelName.size())) {
|
|
ok = false;
|
|
errorMsg = "Cache collision: Kernel names do not match";
|
|
}
|
|
} else {
|
|
ok = false;
|
|
errorMsg = "Cache collision: Kernel name lengths do not match";
|
|
}
|
|
} else {
|
|
ok = false;
|
|
errorMsg = "Cache collision: Build options lengths do not match";
|
|
}
|
|
}
|
|
}
|
|
if (fileSrcData) {
|
|
delete[] fileSrcData;
|
|
}
|
|
}
|
|
delete[] contents;
|
|
}
|
|
if (!ok) {
|
|
delete[] *dstData;
|
|
dstData = NULL;
|
|
dstSize = 0;
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
// Use srcdata, buildOpts and kernelName to generate a cache entry
|
|
//
|
|
// In:
|
|
// srcData - Source data
|
|
// srcNum - Number of source data
|
|
// buildOpts - Build options
|
|
// kernelName - Kernel name
|
|
// dstData - Destination data
|
|
// dstSize - Destination size
|
|
//
|
|
// Returns:
|
|
// true if entry created
|
|
// false otherwise, check errorMsg for errors
|
|
//
|
|
bool KernelCache::makeCacheEntry(const KernelCacheData *srcData, const unsigned int srcNum,
|
|
const std::string &buildOpts, const std::string &kernelName, const char *dstData, const unsigned int dstSize)
|
|
{
|
|
bool ok;
|
|
std::string fileName;
|
|
char *fileData = NULL;
|
|
unsigned int fileSize;
|
|
errorMsg.clear();
|
|
makeFileName(srcData, srcNum, buildOpts, kernelName, fileName);
|
|
unsigned int dstHashVal = 0;
|
|
KernelCacheData cacheData;
|
|
cacheData.data = (char *)dstData;
|
|
cacheData.dataSize = dstSize;
|
|
dstHashVal = computeHash((const KernelCacheData *)&cacheData, 1, buildOpts, kernelName);
|
|
ok = buildFile(srcData, srcNum, buildOpts, kernelName, dstData, dstSize, dstHashVal, &fileData, fileSize);
|
|
if (!ok) {
|
|
// Return an error
|
|
return false;
|
|
}
|
|
ok = writeFile(fileName, fileData, fileSize);
|
|
delete[] fileData;
|
|
if (!ok) {
|
|
// Return an error
|
|
return false;
|
|
}
|
|
ok = setCacheInfo(version, cacheSize + fileSize);
|
|
if (!ok) {
|
|
errorMsg = "Cache version and size is not updated successfully";
|
|
// Return an error
|
|
return false;
|
|
}
|
|
return ok;
|
|
}
|