Fichiers
rocm-systems/rocclr/runtime/device/hsa/hsaprogram.cpp
T
2014-07-04 16:17:05 -04:00

769 lignes
28 KiB
C++

//
// Copyright (c) 2008 Advanced Micro Devices, Inc. All rights reserved.
//
#ifndef WITHOUT_FSA_BACKEND
#include "device/hsa/hsaprogram.hpp"
#include "compiler/lib/loaders/elf/elf.hpp"
#include "compiler/lib/utils/options.hpp"
#include "runtime/device/hsa/hsakernel.hpp"
#include "runtime/device/hsa/hsacompilerlib.hpp"
#include "runtime/device/hsa/oclhsa_common.hpp"
#include "utils/bif_section_labels.hpp"
#include <string>
#include <vector>
#include <cstring>
#include <fstream>
#include <sstream>
#include <iostream>
#include <istream>
#endif // WITHOUT_FSA_BACKEND
namespace oclhsa {
#ifndef WITHOUT_FSA_BACKEND
/* Temporary log function for the compiler library */
static void logFunction(const char *msg, size_t size) {
std::cout << "Compiler Library log :" << msg << std::endl;
}
FSAILProgram::~FSAILProgram() {
unloadBrig();
acl_error error;
// Free the elf binary
if (binaryElf_ != NULL) {
error = g_complibApi._aclBinaryFini(binaryElf_);
if (error != ACL_SUCCESS) {
LogWarning( "Error while destroying the acl binary \n" );
}
}
}
FSAILProgram::FSAILProgram(oclhsa::NullDevice& device): device::Program(device),
llvmBinary_(),
binaryElf_(NULL),
device_(device),
isBrigLoaded_(false)
{
memset(&binOpts_, 0, sizeof(binOpts_));
binOpts_.struct_size = sizeof(binOpts_);
//binOpts_.elfclass = LP64_SWITCH( ELFCLASS32, ELFCLASS64 );
//Setting as 32 bit because hsail64 returns an invalid aclTargetInfo
//when aclGetTargetInfo is called - EPR# 377910
binOpts_.elfclass = ELFCLASS32;
binOpts_.bitness = ELFDATA2LSB;
binOpts_.alloc = &::malloc;
binOpts_.dealloc = &::free;
}
bool FSAILProgram::initClBinary(char *binaryIn, size_t size) { // Save the
// original
// binary that
// isn't owned
// by ClBinary
clBinary()->saveOrigBinary(binaryIn, size);
char *bin = binaryIn;
size_t sz = size;
int encryptCode;
char *decryptedBin;
size_t decryptedSize;
if (!clBinary()->decryptElf(binaryIn, size,
&decryptedBin, &decryptedSize, &encryptCode)) {
return false;
}
if (decryptedBin != NULL) {
// It is decrypted binary.
bin = decryptedBin;
sz = decryptedSize;
}
// Both 32-bit and 64-bit are allowed!
if (!amd::isElfMagic(bin)) {
// Invalid binary.
if (decryptedBin != NULL) {
delete[]decryptedBin;
}
return false;
}
clBinary()->setFlags(encryptCode);
return clBinary()->setBinary(bin, sz, (decryptedBin != NULL));
}
bool FSAILProgram::initBuild(amd::option::Options *options) {
if (!device::Program::initBuild(options)) {
return false;
}
// Need to get device information from CAL !?!?
// Needs the device pointer from CAL to send to options class
//
// Shreyas: Commenting this might cause a bug - keeping this fro now
// options->setPerBuildInfo("hsa",
// binary_.getEncryptCode()
// );
// Elf Binary setup
std::string outFileName;
// true means fsail required
clBinary()->init(options, true);
if (options->isDumpFlagSet(amd::option::DUMP_BIF)) {
outFileName = options->getDumpFileName(".bin");
}
bool useELF64 = getCompilerOptions()->oVariables->EnableGpuElf64;
if (!clBinary()->setElfOut(useELF64 ? ELFCLASS64 : ELFCLASS32,
(outFileName.size() >
0) ? outFileName.c_str() : NULL)) {
LogError("Setup elf out for gpu failed");
return false;
}
return true;
}
// ! post-compile setup for GPU
bool FSAILProgram::finiBuild(bool isBuildGood) {
clBinary()->resetElfOut();
clBinary()->resetElfIn();
if (!isBuildGood) {
// Prevent the encrypted binary form leaking out
clBinary()->setBinary(NULL, 0);
}
return device::Program::finiBuild(isBuildGood);
}
static char *readFile(std::string source_filename, size_t &size) {
FILE *fp = ::fopen(source_filename.c_str(), "rb");
unsigned int length;
size_t offset = 0;
char *ptr;
if (!fp) {
return NULL;
}
// obtain file size.
::fseek(fp, 0, SEEK_END);
length = ::ftell(fp);
::rewind(fp);
ptr = reinterpret_cast<char *>(malloc(offset + length + 1));
if (length != fread(&ptr[offset], 1, length, fp)) {
free(ptr);
return NULL;
}
ptr[offset + length] = '\0';
size = offset + length;
::fclose(fp);
return ptr;
}
aclType FSAILProgram::getNextCompilationStageFromBinary() {
acl_error errorCode;
size_t secSize = 0;
aclType from = ACL_TYPE_DEFAULT;
// Checking llvmir in .llvmir section
bool isLlvmirText = true;
const void *llvmirText = g_complibApi._aclExtractSection(device().compiler(),
binaryElf_,
&secSize,
aclLLVMIR,
&errorCode);
if (errorCode != ACL_SUCCESS) {
isLlvmirText = false;
}
// Checking compile & link options in .comment section
bool isOpts = true;
const void *opts = g_complibApi._aclExtractSection(device().compiler(),
binaryElf_,
&secSize,
aclCOMMENT,
&errorCode);
if (errorCode != ACL_SUCCESS) {
isOpts = false;
}
if (isLlvmirText) {
from = ACL_TYPE_LLVMIR_BINARY;
} else {
if (!isLlvmirText) {
buildLog_ +="Error while linking : \
Invalid binary (Missing LLVMIR section)\n" ;
}
if (!isOpts) {
buildLog_ +="Warning while linking : \
Invalid binary (Missing COMMENT section)\n" ;
}
return ACL_TYPE_DEFAULT;
}
bool isHsailText = true;
// Checking HSAIL in .cg section
const void *hsailText = g_complibApi._aclExtractSection(device().compiler(),
binaryElf_,
&secSize,
aclCODEGEN,
&errorCode);
if (errorCode != ACL_SUCCESS) {
isHsailText = false;
}
// Checking BRIG STRTAB in .brig_strtab section
bool isBrigStrtab = true;
const void *brigStrtab = g_complibApi._aclExtractSection(device().compiler(),
binaryElf_,
&secSize,
aclBRIGstrs,
&errorCode);
if (errorCode != ACL_SUCCESS) {
isBrigStrtab = false;
}
// Checking BRIG DIRECTIVES in .brig_directives section
bool isBrigDirs = true;
const void *brigDirs = g_complibApi._aclExtractSection(device().compiler(),
binaryElf_,
&secSize,
aclBRIGdirs,
&errorCode);
if (errorCode != ACL_SUCCESS) {
isBrigDirs = false;
}
// Checking BRIG CODE in .brig_code section
bool isBrigCode = true;
const void *brigCode = g_complibApi._aclExtractSection(device().compiler(),
binaryElf_,
&secSize,
aclBRIGcode,
&errorCode);
if (errorCode != ACL_SUCCESS) {
isBrigCode = false;
}
// Checking BRIG OPERANDS in .brig_operands section
bool isBrigOps = true;
const void *brigOps = g_complibApi._aclExtractSection(device().compiler(),
binaryElf_,
&secSize,
aclBRIGoprs,
&errorCode);
if (errorCode != ACL_SUCCESS) {
isBrigOps = false;
}
if (isHsailText && isBrigStrtab && isBrigDirs && isBrigCode && isBrigOps) {
from = ACL_TYPE_HSAIL_BINARY;
} else if (!isHsailText && !isBrigStrtab && !isBrigDirs && !isBrigCode && !isBrigOps) {
from = ACL_TYPE_LLVMIR_BINARY;
} else {
if (!isHsailText) {
buildLog_ +="Error while linking : \
Invalid binary (Missing CG section)\n" ;
}
if (!isBrigStrtab) {
buildLog_ +="Error while linking : \
Invalid binary (Missing BRIG_STRTAB section)\n" ;
}
if (!isBrigDirs) {
buildLog_ +="Error while linking : \
Invalid binary (Missing BRIG_DIRECTIVES section)\n" ;
}
if (!isBrigCode) {
buildLog_ +="Error while linking : \
Invalid binary (Missing BRIG_CODE section)\n" ;
}
if (!isBrigOps) {
buildLog_ +="Error while linking : \
Invalid binary (Missing BRIG_OPERANDS section)\n" ;
}
return ACL_TYPE_DEFAULT;
}
// Checking ISA in .text section
bool isShaderIsa = true;
const void *shaderIsa = g_complibApi._aclExtractSection(device().compiler(),
binaryElf_,
&secSize,
aclTEXT,
&errorCode);
if (errorCode != ACL_SUCCESS) {
isShaderIsa = false;
}
if (isShaderIsa && from == ACL_TYPE_LLVMIR_BINARY) {
from = ACL_TYPE_DEFAULT;
}
return from;
}
bool FSAILProgram::updateAclBinaryWithKernelIsaAndDebug(std::string kernelName){
assert(brig_.loadmap_section != NULL);
aclBinary * internalAclBinary = reinterpret_cast<aclBinary*>(brig_.loadmap_section);
std::string openClKernelName("&__OpenCL_" + kernelName + "_kernel");
const oclBIFSymbolStruct* isaSymbolStruct = findBIF30SymStruct(symISABinary);
assert(isaSymbolStruct && "symbol not found");
std::string kernelIsaSymbol = isaSymbolStruct->str[PRE] +
openClKernelName + isaSymbolStruct->str[POST];
const oclBIFSymbolStruct* debugSymbolStruct = findBIF30SymStruct(symDebugInfo);
assert(debugSymbolStruct && "symbol not found");
//For debug symbols, the PRE is used for BRIG debug and the POST is used for
//ISA debug
std::string kernelIsaDebugSymbol = debugSymbolStruct->str[POST] + openClKernelName;
//Extract the ISA section
size_t symbolSize;
acl_error errorCode;
const void* isaSymbol = g_complibApi._aclExtractSymbol(device().compiler(),
internalAclBinary,
&symbolSize,
aclTEXT,
kernelIsaSymbol.c_str(),
&errorCode);
if (errorCode != ACL_SUCCESS) {
buildLog_ += "Failed to extract ISA for kernel";
return false;
}
//Insert the ISA section
errorCode = g_complibApi._aclInsertSymbol(device().compiler(),
binaryElf_,
isaSymbol,
symbolSize,
aclTEXT,
kernelIsaSymbol.c_str());
if (errorCode != ACL_SUCCESS) {
buildLog_ += "Failed to insert ISA for kernel";
return false;
}
const void* debugSymbol = g_complibApi._aclExtractSymbol(device().compiler(),
internalAclBinary,
&symbolSize,
aclHSADEBUG,
kernelIsaDebugSymbol.c_str(),
&errorCode);
//If debug information is available
if (errorCode == ACL_SUCCESS) {
//Update binary with the debug section for the kernel
errorCode = g_complibApi._aclInsertSymbol(device().compiler(),
binaryElf_,
debugSymbol,
symbolSize,
aclHSADEBUG,
kernelIsaDebugSymbol.c_str());
if (errorCode != ACL_SUCCESS) {
buildLog_ += "Failed to insert debug information for kernel";
return false;
}
}
return true;
}
bool FSAILProgram::ExtractSymbolAndCopy(aclSections id,
const char *symbol_name,
void** address_to_copy,
size_t* symbol_size_bytes,
bool verify) {
acl_error error_code;
*symbol_size_bytes = 0;
const void* symbol_data = g_complibApi._aclExtractSymbol(
device().compiler(),
binaryElf_,
symbol_size_bytes,
id,
symbol_name,
&error_code);
//If the section is not mandatory and the section does not exist
//skip this section
if (error_code != ACL_SUCCESS) {
if (!verify) {
return true;
}
std::string error = "Could not find Brig Directive in BIFF: ";
error += symbol_name;
LogError(error.c_str());
buildLog_ += error;
return false;
}
*address_to_copy = malloc(*symbol_size_bytes);
if (*address_to_copy == NULL) {
LogError(" Failed to allocate memory");
return false;
}
memcpy(*address_to_copy, symbol_data, *symbol_size_bytes);
return true;
}
bool FSAILProgram::saveBinaryAndSetType(type_t type) {
//Write binary to memory
void *rawBinary = NULL;
size_t size;
if (g_complibApi._aclWriteToMem(binaryElf_, &rawBinary, &size)
!= ACL_SUCCESS) {
buildLog_ += "Failed to write binary to memory \n";
return false;
}
clBinary()->saveBIFBinary((char*)rawBinary, size);
//Set the type of binary
setType(type);
//Free memory containing rawBinary
binaryElf_->binOpts.dealloc(rawBinary);
return true;
}
bool FSAILProgram::linkImpl(const std::vector<Program *> &inputPrograms,
amd::option::Options *options,
bool createLibrary) {
std::vector<device::Program *>::const_iterator it
= inputPrograms.begin();
std::vector<device::Program *>::const_iterator itEnd
= inputPrograms.end();
acl_error errorCode;
// For each program we need to extract the LLVMIR and create
// aclBinary for each
std::vector<aclBinary *> binaries_to_link;
for (size_t i = 0; it != itEnd; ++it, ++i) {
FSAILProgram *program = (FSAILProgram *)*it;
// Check if the program was created with clCreateProgramWIthBinary
binary_t binary = program->binary();
if ((binary.first != NULL) && (binary.second > 0)) {
// Binary already exists -- we can also check if there is no
// opencl source code
// Need to check if LLVMIR exists in the binary
// If LLVMIR does not exist then is it valid
// We need to pull out all the compiled kernels
// We cannot do this at present because we need at least
// Hsail text to pull the kernels oout
void *mem = const_cast<void *>(binary.first);
binaryElf_ = g_complibApi._aclReadFromMem(mem,
binary.second,
&errorCode);
if (errorCode != ACL_SUCCESS) {
LogWarning("Error while linking : Could not read from raw binary");
return false;
}
}
// At this stage each FSAILProgram contains a valid binary_elf
// Check if LLVMIR is in the binary
// @TODO - Memory leak , cannot free this buffer
// need to fix this.. File EPR on compiler library
size_t llvmirSize = 0;
const void *llvmirText = g_complibApi._aclExtractSection(device().compiler(),
binaryElf_,
&llvmirSize,
aclLLVMIR,
&errorCode);
if (errorCode != ACL_SUCCESS) {
buildLog_ +="Error while linking : \
Invalid binary (Missing LLVMIR section)" ;
return false;
}
// Create a new aclBinary for each LLVMIR and save it in a list
aclBIFVersion ver = g_complibApi._aclBinaryVersion(binaryElf_);
aclBinary *bin = g_complibApi._aclCreateFromBinary(binaryElf_, ver);
binaries_to_link.push_back(bin);
}
// At this stage each FSAILProgram in the list has an aclBinary initialized
// and contains LLVMIR
// We can now go ahead and link them.
if (binaries_to_link.size() > 1) {
errorCode = g_complibApi._aclLink(device().compiler(),
binaries_to_link[0],
binaries_to_link.size() - 1,
&binaries_to_link[1],
ACL_TYPE_LLVMIR_BINARY,
"-create-library",
NULL);
}
else {
errorCode = g_complibApi._aclLink(device().compiler(),
binaries_to_link[0],
0,
NULL,
ACL_TYPE_LLVMIR_BINARY,
"-create-library",
NULL);
}
if (errorCode != ACL_SUCCESS) {
buildLog_ += "Failed to link programs";
return false;
}
// Store the newly linked aclBinary for this program.
binaryElf_ = binaries_to_link[0];
// Free all the other aclBinaries
for (size_t i = 1; i < binaries_to_link.size(); i++) {
g_complibApi._aclBinaryFini(binaries_to_link[i]);
}
if (createLibrary) {
saveBinaryAndSetType(TYPE_LIBRARY);
return true;
}
// Now call linkImpl with the new options
return linkImpl(options);
}
bool FSAILProgram::loadBrig() {
//Copy all the sections into BRIG
memset(&brig_, 0 ,sizeof(HsaBrig));
bool codeStatus = ExtractSymbolAndCopy(aclBRIGcode,
"__BRIG__code",
&brig_.code_section,
&brig_.code_section_byte_size,
true
);
bool oprStatus = ExtractSymbolAndCopy(aclBRIGoprs,
"__BRIG__operands",
&brig_.operand_section,
&brig_.operand_section_byte_size,
true
);
bool dirStatus = ExtractSymbolAndCopy(aclBRIGdirs,
"__BRIG__directives",
&brig_.directive_section,
&brig_.directive_section_byte_size,
true
);
bool strStatus = ExtractSymbolAndCopy(aclBRIGstrs,
"__BRIG__strtab",
&brig_.string_section,
&brig_.string_section_byte_size,
true
);
bool dbgStatus = ExtractSymbolAndCopy(aclHSADEBUG ,
"__debug_brig__",
&brig_.debug_section,
&brig_.debug_section_byte_size,
false
);
if (!codeStatus || !oprStatus || !dirStatus || !strStatus || !dbgStatus) {
LogError("Failed to Extract one or more BRIG sections");
buildLog_ += "Error: Failed to Extract one or more BRIG sections";
return false;
}
if(hsacoreapi->HsaLoadBrig(device_.getBackendDevice(), &brig_)
!= kHsaStatusSuccess){
return false;
}
isBrigLoaded_ = true;
return true;
}
bool FSAILProgram::unloadBrig() {
if (isBrigLoaded_ == true) {
HsaStatus status = hsacoreapi->HsaUnloadBrig(&brig_);
if (status != kHsaStatusSuccess){
return false;
}
//Destroy the BRIG
free(brig_.code_section);
free(brig_.directive_section);
free(brig_.operand_section);
free(brig_.string_section);
free(brig_.debug_section);
}
return true;
}
bool FSAILProgram::linkImpl(amd::option::Options *options) {
acl_error errorCode;
aclType continueCompileFrom = ACL_TYPE_LLVMIR_BINARY;
//If the binaryElf_ is not set then program must have been created
// using clCreateProgramWithBinary
if (!binaryElf_) {
binary_t binary = this->binary();
if ((binary.first != NULL) && (binary.second > 0)) {
// Binary already exists -- we can also check if there is no
// opencl source code
// Need to check if LLVMIR exists in the binary
// If LLVMIR does not exist then is it valid
// We need to pull out all the compiled kernels
// We cannot do this at present because we need at least
// Hsail text to pull the kernels oout
void *mem = const_cast<void *>(binary.first);
binaryElf_ = g_complibApi._aclReadFromMem(mem,
binary.second,
&errorCode);
if (errorCode != ACL_SUCCESS) {
buildLog_ += "Error while converting to BRIG: aclBinary init failure \n" ;
LogWarning("aclBinaryInit failed");
return false;
}
// Check that all needed section also exist in binaryElf_
// No any validity checks here
continueCompileFrom = getNextCompilationStageFromBinary();
if (ACL_TYPE_DEFAULT == continueCompileFrom) {
return false;
}
if (ACL_TYPE_HSAIL_BINARY == continueCompileFrom) {
// Save binary in the interface class
// Also load compile & link options from binary into Program class members:
// compileOptions_ & linkOptions_
setBinary(static_cast<char*>(mem), binary.second);
// Compare options loaded from binary with current ones
// If they differ then recompile from ACL_TYPE_LLVMIR_BINARY
// @TODO It is needed to compare options taking into account that:
// 1. options are order independent;
// 2. (may be not trivial) compare only options that affect binary
std::string curOptions = options->origOptionStr + hsailOptions();
if (compileOptions_ + linkOptions_ != curOptions) {
continueCompileFrom = ACL_TYPE_LLVMIR_BINARY;
}
}
}
}
// Compilation from ACL_TYPE_LLVMIR_BINARY to ACL_TYPE_CG in cases:
// 1. if the program is not created with binary;
// 2. if the program is created with binary and contains only .llvmir & .comment
// 3. if the program is created with binary, contains all brig sections,
// but the binary's compile & link options differ from current ones (recompilation);
if (ACL_TYPE_LLVMIR_BINARY == continueCompileFrom) {
std::string curOptions = options->origOptionStr + hsailOptions();
errorCode = g_complibApi._aclCompile(device().compiler(),
binaryElf_,
curOptions.c_str(),
ACL_TYPE_LLVMIR_BINARY,
ACL_TYPE_CG,
logFunction);
}
if (errorCode != ACL_SUCCESS) {
buildLog_ += "Error while converting to BRIG: Compiling LLVMIR to BRIG \n" ;
return false;
}
//Stop compilation if it is an offline device - HSA runtime does not
//support ISA compiled offline
if (!dev().isOnline()) {
return true;
}
size_t fsailSize;
const void *hsailText = g_complibApi._aclExtractSection(device().compiler(),
binaryElf_,
&fsailSize,
aclCODEGEN,
&errorCode);
if (errorCode != ACL_SUCCESS) {
buildLog_ += "Error while reading out the HSAIL from the ELF" ;
return false;
}
const HsaDevice *hsaDevice = dev().getBackendDevice();
if (!loadBrig()) {
buildLog_ += "Error while loading BRIG" ;
return false;
}
std::string hsailProgram((char*)hsailText, fsailSize);
fsailProgram_ = hsailProgram;
// We pull out all the kernel names in a very ugly manner
//Todo(sramalin) : check if this has been fixed in the compiler library
// If not file an EPR
if (!fsailProgram_.empty()) {
// Find out the name of the kernel. Works for multiple kernels
std::vector<std::string> kernelNames;
int pos = 0;
while (true) {
std::string findString = "kernel &";
size_t kernelNPos = fsailProgram_.find(findString, pos);
if (kernelNPos == std::string::npos) {
break;
}
size_t kernelEndNPos = fsailProgram_.find("l(", kernelNPos);
pos = kernelEndNPos + 1;
if (kernelEndNPos == std::string::npos) {
break;
}
// "kernel &" is 8
// "__OpenCL_" is 9
// "_kerne" is 6
// We can drop all this with a compiler tweak later
std::string kernelName = fsailProgram_.substr(kernelNPos + 8 + 9,
kernelEndNPos -
(kernelNPos + 8 + 9) - 6);
kernelNames.push_back(kernelName);
Kernel *aKernel = new oclhsa::Kernel(kernelName,
this,
&brig_,
options->origOptionStr + hsailOptions());
if (!aKernel->init() ) {
return false;
}
aKernel->setUniformWorkGroupSize(options
->oVariables->UniformWorkGroupSize);
//Update the binary in the FSAILProgram to save the ISA and debug
// information. This is so the debugger and the profiler can use the
// a single aclBinary for all their needs.
if (!updateAclBinaryWithKernelIsaAndDebug(kernelName)) {
return false;
}
kernels()[kernelName] = aKernel;
}
saveBinaryAndSetType(TYPE_EXECUTABLE);
buildLog_ += g_complibApi._aclGetCompilerLog(device().compiler());
return true;
}
return false;
}
bool FSAILProgram::createBinary(amd::option::Options *options) {
return false;
}
bool FSAILProgram::initClBinary() {
if (clBinary_ == NULL) {
clBinary_ = new ClBinary(static_cast<const Device &>(device()));
if (clBinary_ == NULL) {
return false;
}
}
return true;
}
void FSAILProgram::releaseClBinary() {
if (clBinary_ != NULL) {
delete clBinary_;
clBinary_ = NULL;
}
}
std::string FSAILProgram::hsailOptions() {
std::string hsailOptions;
//Set options for the standard device specific options
//This is just for legacy compiler code
// All our devices support these options now
hsailOptions.append(" -DFP_FAST_FMAF=1");
hsailOptions.append(" -DFP_FAST_FMA=1");
//TODO(sramalin) : Query the device for opencl version
// and only set if -cl-std wasn't specified in
// original build options (app)
//hsailOptions.append(" -cl-std=CL1.2");
//check if the host is 64 bit or 32 bit
LP64_ONLY(hsailOptions.append(" -m64"));
//Now append each extension supported by the device
// one by one
std::string token;
std::istringstream iss("");
iss.str(device().info().extensions_);
while (getline(iss, token, ' ')) {
if (!token.empty()) {
hsailOptions.append(" -D");
hsailOptions.append(token);
hsailOptions.append("=1");
}
}
return hsailOptions;
}
#endif // WITHOUT_FSA_BACKEND
} // namespace hsa