compute-runtime/offline_compiler/decoder/binary_encoder.cpp

306 lines
11 KiB
C++

/*
* Copyright (C) 2018 Intel Corporation
*
* SPDX-License-Identifier: MIT
*
*/
#include "binary_encoder.h"
#include "CL/cl.h"
#include "elf/writer.h"
#include "helper.h"
#include "runtime/helpers/file_io.h"
#include <algorithm>
#include <cstring>
#include <fstream>
void BinaryEncoder::calculatePatchListSizes(std::vector<std::string> &ptmFile) {
size_t patchListPos = 0;
for (size_t i = 0; i < ptmFile.size(); ++i) {
if (ptmFile[i].find("PatchListSize") != std::string::npos) {
patchListPos = i;
} else if (ptmFile[i].find("PATCH_TOKEN") != std::string::npos) {
uint32_t calcSize = 0;
i++;
while (i < ptmFile.size() && ptmFile[i].find("Kernel #") == std::string::npos) {
if (ptmFile[i].find(':') == std::string::npos) {
if (ptmFile[i].find("Hex") != std::string::npos) {
calcSize += static_cast<uint32_t>(std::count(ptmFile[i].begin(), ptmFile[i].end(), ' '));
} else {
calcSize += std::atoi(&ptmFile[i][1]);
}
}
i++;
}
uint32_t size = static_cast<uint32_t>(std::stoul(ptmFile[patchListPos].substr(ptmFile[patchListPos].find_last_of(' ') + 1)));
if (size != calcSize) {
printf("Warning! Calculated PatchListSize ( %u ) differs from file ( %u ) - changing it. Line %d\n", calcSize, size, static_cast<int>(patchListPos + 1));
ptmFile[patchListPos] = ptmFile[patchListPos].substr(0, ptmFile[patchListPos].find_last_of(' ') + 1);
ptmFile[patchListPos] += std::to_string(calcSize);
}
}
}
}
int BinaryEncoder::copyBinaryToBinary(const std::string &srcFileName, std::ostream &outBinary) {
std::ifstream ifs(srcFileName, std::ios::binary);
if (!ifs.good()) {
printf("Cannot open %s.\n", srcFileName.c_str());
return -1;
}
ifs.seekg(0, ifs.end);
auto length = static_cast<size_t>(ifs.tellg());
ifs.seekg(0, ifs.beg);
std::vector<char> binary(length);
ifs.read(binary.data(), length);
outBinary.write(binary.data(), length);
return 0;
}
int BinaryEncoder::createElf() {
CLElfLib::CElfWriter elfWriter(CLElfLib::E_EH_TYPE::EH_TYPE_OPENCL_EXECUTABLE, CLElfLib::E_EH_MACHINE::EH_MACHINE_NONE, 0);
//Build Options
if (fileExists(pathToDump + "build.bin")) {
auto binary = readBinaryFile(pathToDump + "build.bin");
std::string data(binary.begin(), binary.end());
elfWriter.addSection(CLElfLib::SSectionNode(CLElfLib::E_SH_TYPE::SH_TYPE_OPENCL_OPTIONS,
CLElfLib::E_SH_FLAG::SH_FLAG_NONE,
"BuildOptions",
data,
static_cast<uint32_t>(data.size())));
} else {
printf("Warning! Missing build section.\n");
}
//LLVM or SPIRV
if (fileExists(pathToDump + "llvm.bin")) {
auto binary = readBinaryFile(pathToDump + "llvm.bin");
std::string data(binary.begin(), binary.end());
elfWriter.addSection(CLElfLib::SSectionNode(CLElfLib::E_SH_TYPE::SH_TYPE_OPENCL_LLVM_BINARY,
CLElfLib::E_SH_FLAG::SH_FLAG_NONE,
"Intel(R) OpenCL LLVM Object",
data,
static_cast<uint32_t>(data.size())));
} else if (fileExists(pathToDump + "spirv.bin")) {
auto binary = readBinaryFile(pathToDump + "spirv.bin");
std::string data(binary.begin(), binary.end());
elfWriter.addSection(CLElfLib::SSectionNode(CLElfLib::E_SH_TYPE::SH_TYPE_SPIRV,
CLElfLib::E_SH_FLAG::SH_FLAG_NONE,
"SPIRV Object",
data,
static_cast<uint32_t>(data.size())));
} else {
printf("Warning! Missing llvm/spirv section.\n");
}
//Device Binary
if (fileExists(pathToDump + "device_binary.bin")) {
auto binary = readBinaryFile(pathToDump + "device_binary.bin");
std::string data(binary.begin(), binary.end());
elfWriter.addSection(CLElfLib::SSectionNode(CLElfLib::E_SH_TYPE::SH_TYPE_OPENCL_DEV_BINARY,
CLElfLib::E_SH_FLAG::SH_FLAG_NONE,
"Intel(R) OpenCL Device Binary",
data,
static_cast<uint32_t>(data.size())));
} else {
printf("Missing device_binary.bin\n");
return -1;
}
//Resolve Elf Binary
std::vector<char> elfBinary;
elfWriter.resolveBinary(elfBinary);
std::ofstream elfFile(elfName, std::ios::binary);
if (!elfFile.good()) {
printf("Couldn't create %s.\n", elfName.c_str());
return -1;
}
elfFile.write(elfBinary.data(), elfBinary.size());
return 0;
}
void BinaryEncoder::printHelp() {
printf("Usage:\n-dump <path to dumping folder> -out <new elf file>\n");
printf("e.g. -dump C:/my_folder/dump -out C:/my_folder/new_binary.bin\n");
}
int BinaryEncoder::encode() {
std::vector<std::string> ptmFile;
readFileToVectorOfStrings(ptmFile, pathToDump + "PTM.txt");
calculatePatchListSizes(ptmFile);
std::ofstream deviceBinary(pathToDump + "device_binary.bin", std::ios::binary);
if (!deviceBinary.good()) {
printf("Error! Couldn't create device_binary.bin.\n");
return -1;
}
int retVal = processBinary(ptmFile, deviceBinary);
deviceBinary.close();
if (retVal != CL_SUCCESS) {
return retVal;
}
return createElf();
}
int BinaryEncoder::processBinary(const std::vector<std::string> &ptmFile, std::ostream &deviceBinary) {
size_t i = 0;
while (i < ptmFile.size()) {
if (ptmFile[i].find("Kernel #") != std::string::npos) {
if (processKernel(++i, ptmFile, deviceBinary)) {
printf("Error while processing kernel!\n");
return -1;
}
} else if (writeDeviceBinary(ptmFile[i++], deviceBinary)) {
printf("Error while writing to binary!\n");
return -1;
}
}
return 0;
}
int BinaryEncoder::processKernel(size_t &i, const std::vector<std::string> &ptmFile, std::ostream &deviceBinary) {
uint32_t kernelNameSize = 0;
while (i < ptmFile.size()) {
if (ptmFile[i].find("KernelName ") != std::string::npos) {
break;
} else if (ptmFile[i].find("KernelNameSize") != std::string::npos) {
std::stringstream ss(ptmFile[i]);
ss.ignore(32, ' ');
ss.ignore(32, ' ');
ss >> kernelNameSize;
}
if (writeDeviceBinary(ptmFile[i++], deviceBinary)) {
printf("Error while writing to binary.\n");
return -1;
}
}
//KernelName
if (i == ptmFile.size()) {
printf("Couldn't find KernelName line.\n");
return -1;
}
std::string kernelName(ptmFile[i], ptmFile[i].find(' ') + 1);
i++;
deviceBinary.write(kernelName.c_str(), kernelName.size());
for (auto j = kernelName.size(); j < kernelNameSize; ++j) {
uint8_t nullByte = 0;
deviceBinary.write(reinterpret_cast<const char *>(&nullByte), sizeof(uint8_t));
}
// Writing KernelHeap, DynamicStateHeap, SurfaceStateHeap
if (fileExists(pathToDump + kernelName + "_GeneralStateHeap.bin")) {
printf("Warning! Adding GeneralStateHeap.\n");
if (copyBinaryToBinary(pathToDump + kernelName + "_GeneralStateHeap.bin", deviceBinary)) {
printf("Error! Couldn't copy %s_GeneralStateHeap.bin\n", kernelName.c_str());
return -1;
}
}
if (copyBinaryToBinary(pathToDump + kernelName + "_KernelHeap.bin", deviceBinary)) {
printf("Error! Couldn't copy %s_KernelHeap.bin\n", kernelName.c_str());
return -1;
}
if (copyBinaryToBinary(pathToDump + kernelName + "_DynamicStateHeap.bin", deviceBinary)) {
printf("Error! Couldn't copy %s_DynamicStateHeap.bin\n", kernelName.c_str());
return -1;
}
if (copyBinaryToBinary(pathToDump + kernelName + "_SurfaceStateHeap.bin", deviceBinary)) {
printf("Error! Couldn't copy %s_SurfaceStateHeap.bin\n", kernelName.c_str());
return -1;
}
return 0;
}
int BinaryEncoder::validateInput(uint32_t argc, const char **argv) {
if (!strcmp(argv[argc - 1], "--help")) {
printHelp();
return -1;
} else {
for (uint32_t i = 2; i < argc - 1; ++i) {
if (!strcmp(argv[i], "-dump")) {
pathToDump = std::string(argv[++i]);
addSlash(pathToDump);
} else if (!strcmp(argv[i], "-out")) {
elfName = std::string(argv[++i]);
} else {
printf("Unknown argument %s\n", argv[i]);
printHelp();
return -1;
}
}
if (pathToDump.empty()) {
printf("Path to dump folder can't be empty.\n");
printHelp();
return -1;
} else if (elfName.find(".bin") == std::string::npos) {
printf(".bin extension is expected for binary file.\n");
printHelp();
return -1;
}
}
return 0;
}
template <typename T>
void BinaryEncoder::write(std::stringstream &in, std::ostream &deviceBinary) {
T val;
in >> val;
deviceBinary.write(reinterpret_cast<const char *>(&val), sizeof(T));
}
template <>
void BinaryEncoder::write<uint8_t>(std::stringstream &in, std::ostream &deviceBinary) {
uint8_t val;
uint16_t help;
in >> help;
val = static_cast<uint8_t>(help);
deviceBinary.write(reinterpret_cast<const char *>(&val), sizeof(uint8_t));
}
template void BinaryEncoder::write<uint16_t>(std::stringstream &in, std::ostream &deviceBinary);
template void BinaryEncoder::write<uint32_t>(std::stringstream &in, std::ostream &deviceBinary);
template void BinaryEncoder::write<uint64_t>(std::stringstream &in, std::ostream &deviceBinary);
int BinaryEncoder::writeDeviceBinary(const std::string &line, std::ostream &deviceBinary) {
if (line.find(':') != std::string::npos) {
return 0;
} else if (line.find("Hex") != std::string::npos) {
std::stringstream ss(line);
ss.ignore(32, ' ');
uint16_t tmp;
uint8_t byte;
while (!ss.eof()) {
ss >> std::hex >> tmp;
byte = static_cast<uint8_t>(tmp);
deviceBinary.write(reinterpret_cast<const char *>(&byte), sizeof(uint8_t));
}
} else {
std::stringstream ss(line);
uint16_t size;
std::string name;
ss >> size;
ss >> name;
switch (size) {
case 1:
write<uint8_t>(ss, deviceBinary);
break;
case 2:
write<uint16_t>(ss, deviceBinary);
break;
case 4:
write<uint32_t>(ss, deviceBinary);
break;
case 8:
write<uint64_t>(ss, deviceBinary);
break;
default:
printf("Unknown size in line: %s\n", line.c_str());
return -1;
}
}
return 0;
}