Reimplement invocation of offline compilation program

This change reimplements offline compiler invocation, using a new command
line interface that allows the passing of relevant CL device information
to the offline compiler.  The information that is passed is as follows:

 * CL_DEVICE_ADDRESS_BITS
 * CL_DEVICE_EXTENSIONS
 * CL_DEVICE_IL_VERSION (with --compilation-mode=spir-v only)
 * CL_DEVICE_VERSION

The interface for the offline compiler script is as follows:

   usage: cl_offline_compiler --source FILE --output FILE
                              --cl-device-info FILE --mode MODE
                              -- [BUILD_OPTIONS [BUILD_OPTIONS ...]]

   positional arguments:
     BUILD_OPTIONS          additional options to pass to the compiler

   optional arguments:
     --source FILE          OpenCL C source file to compile
     --output FILE          SPIR-V or binary file to create
     --cl-device-info FILE  OpenCL device info file
     --mode                 compilation mode (spir-v or binary)

The OpenCL C version for compilation is now specified in BUILD_OPTIONS,
as normal for online compilation, i.e. with -cl-std=VERSION.

Signed-off-by: Stuart Brady <stuart.brady@arm.com>
This commit is contained in:
Stuart Brady
2019-07-01 17:21:27 +01:00
committed by Kévin Petit
parent 2c43504923
commit 0d96c198ee
9 changed files with 210 additions and 135 deletions

View File

@@ -0,0 +1,25 @@
The cl_offline_compiler program used for offline compilation must
implement the following interface.
usage: cl_offline_compiler --source FILE --output FILE
--cl-device-info FILE --mode MODE
-- [BUILD_OPTIONS [BUILD_OPTIONS ...]]
positional arguments:
BUILD_OPTIONS additional options to pass to the compiler
optional arguments:
--source FILE OpenCL C source file to compile
--output FILE SPIR-V or binary file to create
--cl-device-info FILE OpenCL device info file
--mode compilation mode (spir-v or binary)
The --cl-device-info file is a list of KEY=VALUE pairs containing device
information relevant to the mode of offline compilation in question.
It is of the following form:
# OpenCL device info affecting <SPIR-V|binary> offline compilation:
CL_DEVICE_ADDRESS_BITS=<32|64>
CL_DEVICE_EXTENSIONS="<space separated list of CL extensions>"
CL_DEVICE_IL_VERSION="<space separated list of IL versions>"
CL_DEVICE_VERSION="OpenCL <version> <vendor info>"

View File

@@ -76,3 +76,15 @@ char *alloc_and_get_device_extensions_string(cl_device_id device)
{ {
return (char *) alloc_and_get_device_info(device, CL_DEVICE_EXTENSIONS, "extensions string"); return (char *) alloc_and_get_device_info(device, CL_DEVICE_EXTENSIONS, "extensions string");
} }
/* Returns a newly allocated C string containing the supported IL version(s) for a device. */
char *alloc_and_get_device_il_version_string(cl_device_id device)
{
return (char *) alloc_and_get_device_info(device, CL_DEVICE_IL_VERSION, "IL version string");
}
/* Returns a newly allocated C string containing the supported OpenCL version for a device. */
char *alloc_and_get_device_version_string(cl_device_id device)
{
return (char *) alloc_and_get_device_info(device, CL_DEVICE_VERSION, "version string");
}

View File

@@ -31,6 +31,12 @@ int is_extension_available(cl_device_id device, const char *extensionName);
/* Returns a newly allocated C string containing the supported extensions list for a device. */ /* Returns a newly allocated C string containing the supported extensions list for a device. */
char *alloc_and_get_device_extensions_string(cl_device_id device); char *alloc_and_get_device_extensions_string(cl_device_id device);
/* Returns a newly allocated C string containing the supported IL version(s) for a device. */
char *alloc_and_get_device_il_version_string(cl_device_id device);
/* Returns a newly allocated C string containing the supported OpenCL version for a device. */
char *alloc_and_get_device_version_string(cl_device_id device);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif // __cplusplus #endif // __cplusplus

View File

@@ -39,6 +39,8 @@ std::string slash = "\\";
std::string slash = "/"; std::string slash = "/";
#endif #endif
static cl_int get_first_device_id(const cl_context context, cl_device_id &device);
std::string get_file_name(const std::string &baseName, int index, const std::string &extension) std::string get_file_name(const std::string &baseName, int index, const std::string &extension)
{ {
std::ostringstream fileName; std::ostringstream fileName;
@@ -231,6 +233,22 @@ static std::string get_offline_compilation_file_type_str(const CompilationMode c
} }
} }
static std::string get_compilation_mode_str(const CompilationMode compilationMode)
{
switch (compilationMode)
{
default:
assert(0 && "Invalid compilation mode");
abort();
case kOnline:
return "online";
case kBinary:
return "binary";
case kSpir_v:
return "spir-v";
}
}
#ifdef KHRONOS_OFFLINE_COMPILER #ifdef KHRONOS_OFFLINE_COMPILER
static std::string get_khronos_compiler_command(const cl_uint device_address_space_size, static std::string get_khronos_compiler_command(const cl_uint device_address_space_size,
const bool openclCXX, const bool openclCXX,
@@ -281,82 +299,118 @@ static std::string get_khronos_compiler_command(const cl_uint device_address_spa
} }
#endif // KHRONOS_OFFLINE_COMPILER #endif // KHRONOS_OFFLINE_COMPILER
static cl_int get_cl_device_info_str(const cl_device_id device, const cl_uint device_address_space_size,
const CompilationMode compilationMode, std::string &clDeviceInfo)
{
char *extensionsString = alloc_and_get_device_extensions_string(device);
if ( NULL == extensionsString )
{
/* An error message will have already been printed by alloc_and_get_device_info(),
* so we can just return, here. */
return -1;
}
BufferOwningPtr<char> extensionsStringBuf(extensionsString);
char *versionString = alloc_and_get_device_version_string(device);
if ( NULL == versionString )
{
/* An error message will have already been printed by alloc_and_get_device_info(),
* so we can just return, here. */
return -1;
}
BufferOwningPtr<char> versionStringBuf(versionString);
std::ostringstream clDeviceInfoStream;
std::string file_type = get_offline_compilation_file_type_str(compilationMode);
clDeviceInfoStream << "# OpenCL device info affecting " << file_type << " offline compilation:" << std::endl
<< "CL_DEVICE_ADDRESS_BITS=" << device_address_space_size << std::endl
<< "CL_DEVICE_EXTENSIONS=\"" << extensionsString << "\"" << std::endl;
/* We only need the device's supported IL version(s) when compiling IL
* that will be loaded with clCreateProgramWithIL() */
if (compilationMode == kSpir_v)
{
char *ilVersionString = alloc_and_get_device_il_version_string(device);
if ( NULL == ilVersionString )
{
/* An error message will have already been printed by alloc_and_get_device_info(),
* so we can just return, here. */
return -1;
}
BufferOwningPtr<char> versionStringBuf(ilVersionString);
clDeviceInfoStream << "CL_DEVICE_IL_VERSION=\"" << ilVersionString << "\"" << std::endl;
}
clDeviceInfoStream << "CL_DEVICE_VERSION=\"" << versionString << "\"" << std::endl;
clDeviceInfo = clDeviceInfoStream.str();
return CL_SUCCESS;
}
static int write_cl_device_info(const cl_device_id device, const cl_uint device_address_space_size,
const CompilationMode compilationMode, std::string &clDeviceInfoFilename)
{
std::string clDeviceInfo;
int error = get_cl_device_info_str(device, device_address_space_size, compilationMode, clDeviceInfo);
if (error != CL_SUCCESS)
{
return error;
}
cl_uint crc = crc32(clDeviceInfo.data(), clDeviceInfo.size());
/* Get the filename for the clDeviceInfo file.
* Note: the file includes the hash on its content, so it is usually unnecessary to delete it. */
std::ostringstream clDeviceInfoFilenameStream;
clDeviceInfoFilenameStream << gCompilationCachePath << slash << "clDeviceInfo-";
clDeviceInfoFilenameStream << std::hex << std::setfill('0') << std::setw(8) << crc << ".txt";
clDeviceInfoFilename = clDeviceInfoFilenameStream.str();
if ((size_t) get_file_size(clDeviceInfoFilename) == clDeviceInfo.size())
{
/* The CL device info file has already been created.
* Nothing to do. */
return 0;
}
/* The file does not exist or its length is not as expected. Create/overwrite it. */
std::ofstream ofs(clDeviceInfoFilename);
if (!ofs.good())
{
log_info("OfflineCompiler: can't create CL device info file: %s\n", clDeviceInfoFilename.c_str());
return -1;
}
ofs << clDeviceInfo;
ofs.close();
return CL_SUCCESS;
}
static std::string get_offline_compilation_command(const cl_uint device_address_space_size, static std::string get_offline_compilation_command(const cl_uint device_address_space_size,
const CompilationMode compilationMode, const CompilationMode compilationMode,
const std::string &bOptions, const std::string &bOptions,
const std::string &sourceFilename, const std::string &sourceFilename,
const std::string &outputFilename) const std::string &outputFilename,
const std::string &clDeviceInfoFilename)
{ {
std::ostringstream size_t_width_stream; std::ostringstream wrapperOptions;
size_t_width_stream << device_address_space_size;
std::string size_t_width_str = size_t_width_stream.str();
// set output type and default script wrapperOptions << gCompilationProgram
std::string outputTypeStr; << " --mode=" << get_compilation_mode_str(compilationMode)
std::string defaultScript; << " --source=" << sourceFilename
if (compilationMode == kBinary) << " --output=" << outputFilename
<< " --cl-device-info=" << clDeviceInfoFilename;
if (bOptions != "")
{ {
outputTypeStr = "binary"; // Add build options passed to this function
#if defined(_WIN32) wrapperOptions << " -- " << bOptions;
defaultScript = "..\\build_script_binary.py ";
#else
defaultScript = "../build_script_binary.py ";
#endif
}
else if (compilationMode == kSpir_v)
{
outputTypeStr = "spir_v";
#if defined(_WIN32)
defaultScript = "..\\build_script_spirv.py ";
#else
defaultScript = "../build_script_spirv.py ";
#endif
} }
// set script arguments return wrapperOptions.str();
std::string scriptArgs = sourceFilename + " " + outputFilename + " " + size_t_width_str + " " + outputTypeStr;
if (!bOptions.empty())
{
//search for 2.0 build options
std::string oclVersion;
std::string buildOptions20 = "-cl-std=CL2.0";
std::size_t found = bOptions.find(buildOptions20);
if (found != std::string::npos)
oclVersion = "20";
else
oclVersion = "12";
std::string bOptionsWRemovedStd20 = bOptions;
std::string::size_type i = bOptions.find(buildOptions20);
if (i != std::string::npos)
bOptionsWRemovedStd20.erase(i, buildOptions20.length());
//remove space before -cl-std=CL2.0 if it was first build option
size_t spacePos = bOptionsWRemovedStd20.find_last_of(" \t\r\n", i);
if (spacePos != std::string::npos && i == 0)
bOptionsWRemovedStd20.erase(spacePos, sizeof(char));
//remove space after -cl-std=CL2.0
spacePos = bOptionsWRemovedStd20.find_first_of(" \t\r\n", i - 1);
if (spacePos != std::string::npos)
bOptionsWRemovedStd20.erase(spacePos, sizeof(char));
if (!bOptionsWRemovedStd20.empty())
scriptArgs += " " + oclVersion + " \"" + bOptionsWRemovedStd20 + "\"";
else
scriptArgs += " " + oclVersion;
}
else
scriptArgs += " 12";
// set script command line
std::string scriptToRunString = defaultScript + scriptArgs;
return scriptToRunString;
} }
static int invoke_offline_compiler(const cl_device_id device, static int invoke_offline_compiler(const cl_device_id device,
@@ -385,8 +439,23 @@ static int invoke_offline_compiler(const cl_device_id device,
} }
else else
{ {
std::string clDeviceInfoFilename;
// See cl_offline_compiler-interface.txt for a description of the
// format of the CL device information file generated below, and
// the internal command line interface for invoking the offline
// compiler.
cl_int err = write_cl_device_info(device, device_address_space_size, compilationMode,
clDeviceInfoFilename);
if (err != CL_SUCCESS)
{
log_error("Failed writing CL device info file\n");
return err;
}
runString = get_offline_compilation_command(device_address_space_size, compilationMode, bOptions, runString = get_offline_compilation_command(device_address_space_size, compilationMode, bOptions,
sourceFilename, outputFilename); sourceFilename, outputFilename, clDeviceInfoFilename);
} }
// execute script // execute script

View File

@@ -27,9 +27,12 @@
using namespace std; using namespace std;
#define DEFAULT_COMPILATION_PROGRAM "cl_offline_compiler"
CompilationMode gCompilationMode = kOnline; CompilationMode gCompilationMode = kOnline;
CompilationCacheMode gCompilationCacheMode = kCacheModeCompileIfAbsent; CompilationCacheMode gCompilationCacheMode = kCacheModeCompileIfAbsent;
std::string gCompilationCachePath = "."; std::string gCompilationCachePath = ".";
std::string gCompilationProgram = DEFAULT_COMPILATION_PROGRAM;
void helpInfo () void helpInfo ()
{ {
@@ -47,6 +50,8 @@ void helpInfo ()
" force-read Force reading from the cache\n" " force-read Force reading from the cache\n"
" overwrite Disable reading from the cache\n" " overwrite Disable reading from the cache\n"
" --compilation-cache-path <path> Path for offline compiler output and CL source\n" " --compilation-cache-path <path> Path for offline compiler output and CL source\n"
" --compilation-program <prog> Program to use for offline compilation,\n"
" defaults to " DEFAULT_COMPILATION_PROGRAM "\n"
"\n"); "\n");
} }
@@ -158,6 +163,20 @@ int parseCustomParam (int argc, const char *argv[], const char *ignore)
return -1; return -1;
} }
} }
else if (!strcmp(argv[i], "--compilation-program"))
{
delArg++;
if ((i + 1) < argc)
{
delArg++;
gCompilationProgram = argv[i + 1];
}
else
{
log_error("Program argument for --compilation-program was not specified.\n");
return -1;
}
}
//cleaning parameters from argv tab //cleaning parameters from argv tab
for (int j = i; j < argc - delArg; j++) for (int j = i; j < argc - delArg; j++)

View File

@@ -36,6 +36,7 @@ enum CompilationCacheMode
extern CompilationMode gCompilationMode; extern CompilationMode gCompilationMode;
extern CompilationCacheMode gCompilationCacheMode; extern CompilationCacheMode gCompilationCacheMode;
extern std::string gCompilationCachePath; extern std::string gCompilationCachePath;
extern std::string gCompilationProgram;
extern int parseCustomParam (int argc, const char *argv[], const char *ignore = 0 ); extern int parseCustomParam (int argc, const char *argv[], const char *ignore = 0 );

View File

@@ -1,7 +0,0 @@
# Script parameters:
# 1 - input file
# 2 - output file
# 3 - architecture: 32 or 64
# 4 - one of the strings: binary, source, spir_v
# 5 - OpenCL version: 12, 20
# 6 - build options

View File

@@ -1,43 +0,0 @@
# Script parameters:
# 1 - input file
# 2 - output file
# 3 - architecture: 32 or 64
# 4 - one of the strings: binary, source, spir_v
# 5 - OpenCL version: 12, 20
# 6 - build options
import os
import sys
if len(sys.argv)<5:
print 'Usage: "build_script_spirv.py <input> <output> <arch> <output_type> <opencl_version> [build_options]"'
exit(1)
input_file = sys.argv[1]
output_file = sys.argv[2]
arch = sys.argv[3]
output_type = sys.argv[4]
ocl_version = sys.argv[5]
build_options = ''
if len(sys.argv) == 5:
build_options = sys.argv[6]
if arch == '32':
arch_string = ''
spir_arch = '__i386__'
else:
arch_string = '64'
spir_arch = '__x86_64__'
if ocl_version == '20':
oclc_version = '200'
spir_version = '2.0'
else:
oclc_version = '120'
spir_version = '1.2'
command = '%LLVMPATH%\\bin\\clang.exe -cc1 -include headers\\opencl_SPIR-' + spir_version + '.h -cl-std=CL' + spir_version +' -D__OPENCL_C_VERSION__=' + oclc_version + ' -fno-validate-pch -D__OPENCL_VERSION__=' + oclc_version + ' -x cl -cl-kernel-arg-info -O0 -emit-llvm-bc -triple spir' + arch_string + '-unknown-unknown -D' + spir_arch + ' -Dcl_khr_3d_image_writes -Dcl_khr_byte_addressable_store -Dcl_khr_d3d10_sharing -Dcl_khr_d3d11_sharing -Dcl_khr_depth_images -Dcl_khr_dx9_media_sharing -Dcl_khr_fp64 -Dcl_khr_global_int32_base_atomics -Dcl_khr_global_int32_extended_atomics -Dcl_khr_gl_depth_images -Dcl_khr_gl_event -Dcl_khr_gl_msaa_sharing -Dcl_khr_gl_sharing -Dcl_khr_icd -Dcl_khr_image2d_from_buffer -Dcl_khr_local_int32_base_atomics -Dcl_khr_local_int32_extended_atomics -Dcl_khr_mipmap_image -Dcl_khr_mipmap_image_writes -Dcl_khr_fp16 ' + build_options + ' -Dcl_khr_spir ' + input_file + ' -o intermediate.spir'
os.system(command)
command = '%LLVMPATH%\\bin\\llvm-spirv.exe intermediate.spir -o ' + output_file
os.system(command)

View File

@@ -4,19 +4,17 @@ from __future__ import print_function
import sys import sys
import os import os
import re
import traceback import traceback
if len(sys.argv) != 3: if len(sys.argv) != 3:
print('Usage: "generate_spirv_offline.py <compilation_cache_dir> <32|64>"') print('Usage: "generate_spirv_offline.py <compilation_cache_dir> <cl_device_info_file>"')
exit(1) exit(1)
compilation_cache_dir = sys.argv[1] compilation_cache_dir = sys.argv[1]
arch = sys.argv[2] cl_device_info_filename = sys.argv[2]
def generate_spirv(): def generate_spirv():
print("Generating SPIR-V files") print("Generating SPIR-V files")
ocl_version = '12';
build_options = '' build_options = ''
if os.path.exists(compilation_cache_dir): if os.path.exists(compilation_cache_dir):
@@ -24,23 +22,18 @@ def generate_spirv():
for file in files: for file in files:
if file.endswith('.cl'): if file.endswith('.cl'):
options_file_name = file[:-2] + "options" options_file_name = file[:-2] + "options"
ocl_version = '12'
if os.path.exists(os.path.join(root, options_file_name)): if os.path.exists(os.path.join(root, options_file_name)):
optFile = open (os.path.join(root, options_file_name), 'rU') optFile = open (os.path.join(root, options_file_name), 'r')
for line in optFile: build_options = optFile.readline().strip()
if re.search("-cl-std=CL2.0", line):
ocl_version = '20'
build_options = re.sub("-cl-std=CL2.0", "", line)
print(build_options) print(build_options)
source_filename = os.path.join(root, file) source_filename = os.path.join(root, file)
output_filename = os.path.join(root, file[:-2]) + "spv" + arch output_filename = os.path.join(root, file[:-2]) + "spv"
command_line = (".\\build_script_spirv.py" + command_line = ("cl_offline_compiler" +
" " + source_filename + " --source=" + source_filename +
" " + output_filename + " --output=" + output_filename +
" " + arch + " --cl-device-info=" + cl_device_info_filename +
" spir_v" + " --mode=spir-v -- " +
" " + ocl_version +
'"' + build_options + '"') '"' + build_options + '"')
print(command_line) print(command_line)
os.system(command_line) os.system(command_line)