diff --git a/meson.build b/meson.build index 1b10d85712e..24165943a14 100644 --- a/meson.build +++ b/meson.build @@ -288,7 +288,8 @@ if with_aco_tests and not with_amd_vk endif with_microsoft_clc = get_option('microsoft-clc').enabled() -with_clc = with_microsoft_clc +with_intel_clc = get_option('intel-clc').enabled() +with_clc = with_microsoft_clc or with_intel_clc with_libclc = with_clc with_spirv_to_dxil = get_option('spirv-to-dxil') diff --git a/meson_options.txt b/meson_options.txt index 5f463562b6e..e5df4735daa 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -509,3 +509,9 @@ option( value : false, description : 'Build vulkan drivers with BETA extensions enabled.' ) +option( + 'intel-clc', + type : 'feature', + value : 'disabled', + description : 'Build the intel-clc compiler (required for ray queries).' +) diff --git a/src/intel/compiler/intel_clc.c b/src/intel/compiler/intel_clc.c new file mode 100644 index 00000000000..7b6739e83b9 --- /dev/null +++ b/src/intel/compiler/intel_clc.c @@ -0,0 +1,437 @@ +/* + * Copyright © 2021 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "brw_compiler.h" +#include "brw_kernel.h" +#include "common/intel_disasm.h" +#include "compiler/clc/clc.h" +#include "compiler/glsl_types.h" +#include "dev/intel_debug.h" +#include "util/build_id.h" +#include "util/disk_cache.h" +#include "util/mesa-sha1.h" + +#include +#include +#include +#include +#include +#include +#include + +#define MAX_CLANG_ARGS 64 + +static struct disk_cache * +get_disk_cache(struct brw_compiler *compiler) +{ +#ifdef ENABLE_SHADER_CACHE + char renderer[14]; + ASSERTED int len = snprintf(renderer, sizeof(renderer), "brw_clc_%04x", + compiler->devinfo->pci_device_id); + assert(len == sizeof(renderer) - 2); + + const struct build_id_note *note = + build_id_find_nhdr_for_addr(get_disk_cache); + if (note == NULL) { + fprintf(stderr, "Failed to find build-id\n"); + abort(); + } + + unsigned build_id_len = build_id_length(note); + if (build_id_len < 20) { + fprintf(stderr, "build-id too short. It needs to be a SHA\n"); + abort(); + } + + struct mesa_sha1 sha1_ctx; + uint8_t sha1[20]; + _mesa_sha1_init(&sha1_ctx); + _mesa_sha1_update(&sha1_ctx, build_id_data(note), build_id_len); + _mesa_sha1_final(&sha1_ctx, sha1); + + char timestamp[41]; + _mesa_sha1_format(timestamp, sha1); + + const uint64_t driver_flags = brw_get_compiler_config_value(compiler); + + return disk_cache_create(renderer, timestamp, driver_flags); +#endif + return NULL; +} + +static void +compiler_log(void *data, unsigned *id, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); +} + +static void +msg_callback(void *priv, const char *msg) +{ + (void)priv; + fprintf(stderr, "%s", msg); +} + +static void +print_u32_data(FILE *fp, const char *prefix, const char *arr_name, + const uint32_t *data, size_t len) +{ + assert(len % 4 == 0); + fprintf(fp, "static const uint32_t %s_%s[] = {", prefix, arr_name); + for (unsigned i = 0; i < (len / 4); i++) { + if (i % 4 == 0) + fprintf(fp,"\n "); + + fprintf(fp, " 0x%08" PRIx32 ",", data[i]); + } + fprintf(fp, "\n};\n"); +} + +const char * +reloc_type_str(enum brw_shader_reloc_type type) +{ + switch (type) { +#define CASE(e) case e: return #e; + CASE(BRW_SHADER_RELOC_TYPE_U32) + CASE(BRW_SHADER_RELOC_TYPE_MOV_IMM) +#undef CASE + default: + unreachable("Unknown relocation type"); + } +} + +static void +print_cs_prog_data_fields(FILE *fp, const char *prefix, const char *pad, + const struct brw_cs_prog_data *cs_prog_data) +{ +#define PROG_DATA_FIELD(fmt, field) \ + fprintf(fp, "%s." #field " = " fmt ",\n", pad, cs_prog_data->field) + +#define PROG_DATA_BOOL_FIELD(field) \ + fprintf(fp, "%s." #field " = %s,\n", pad, \ + cs_prog_data->field ? "true" : "false") + + PROG_DATA_FIELD("%u", base.nr_params); + assert(cs_prog_data->base.stage == MESA_SHADER_COMPUTE); + fprintf(fp, "%s.base.stage = MESA_SHADER_COMPUTE,\n", pad); + assert(cs_prog_data->base.zero_push_reg == 0); + assert(cs_prog_data->base.push_reg_mask_param == 0); + PROG_DATA_FIELD("%u", base.curb_read_length); + PROG_DATA_FIELD("%u", base.total_scratch); + PROG_DATA_FIELD("%u", base.total_shared); + PROG_DATA_FIELD("%u", base.program_size); + PROG_DATA_FIELD("%u", base.const_data_size); + PROG_DATA_FIELD("%u", base.const_data_offset); + PROG_DATA_FIELD("%u", base.num_relocs); + fprintf(fp, "%s.base.relocs = %s_relocs,\n", pad, prefix); + assert(!cs_prog_data->base.has_ubo_pull); + assert(cs_prog_data->base.dispatch_grf_start_reg == 0); + assert(!cs_prog_data->base.use_alt_mode); + assert(cs_prog_data->base.param == 0); + PROG_DATA_BOOL_FIELD(base.uses_atomic_load_store); + fprintf(fp, "%s.local_size = { %u, %u, %u },\n", pad, + cs_prog_data->local_size[0], + cs_prog_data->local_size[1], + cs_prog_data->local_size[2]); + fprintf(fp, "%s.prog_offset = { %u, %u, %u },\n", pad, + cs_prog_data->prog_offset[0], + cs_prog_data->prog_offset[1], + cs_prog_data->prog_offset[2]); + PROG_DATA_FIELD("%u", prog_mask); + PROG_DATA_FIELD("%u", prog_spilled); + PROG_DATA_BOOL_FIELD(uses_barrier); + PROG_DATA_BOOL_FIELD(uses_num_work_groups); + assert(!cs_prog_data->uses_inline_data); + assert(!cs_prog_data->uses_btd_stack_ids); + PROG_DATA_FIELD("%u", push.per_thread.dwords); + PROG_DATA_FIELD("%u", push.per_thread.regs); + PROG_DATA_FIELD("%u", push.per_thread.size); + PROG_DATA_FIELD("%u", push.cross_thread.dwords); + PROG_DATA_FIELD("%u", push.cross_thread.regs); + PROG_DATA_FIELD("%u", push.cross_thread.size); + +#undef PROG_DATA_FIELD +#undef PROG_DATA_BOOL_FIELD +} + +static void +print_kernel(FILE *fp, const char *prefix, + const struct brw_kernel *kernel, + const struct intel_device_info *devinfo) +{ + struct mesa_sha1 sha1_ctx; + _mesa_sha1_init(&sha1_ctx); + +#define SHA1_UPDATE_VALUE(val) \ + _mesa_sha1_update(&sha1_ctx, &val, sizeof(val)) + + fprintf(fp, "#include \"intel/compiler/brw_kernel.h\"\n"); + fprintf(fp, "\n"); + + fprintf(fp, "static const struct brw_shader_reloc %s_relocs[] = {\n", + prefix); + for (unsigned i = 0; i < kernel->prog_data.base.num_relocs; i++) { + const struct brw_shader_reloc *reloc = &kernel->prog_data.base.relocs[i]; + fprintf(fp, " { %"PRIu32", %s, %"PRIu32", %"PRIu32" },\n", + reloc->id, reloc_type_str(reloc->type), + reloc->offset, reloc->delta); + } + fprintf(fp, "};\n"); + _mesa_sha1_update(&sha1_ctx, kernel->prog_data.base.relocs, + kernel->prog_data.base.num_relocs * + sizeof(kernel->prog_data.base.relocs[0])); + + /* Get rid of the pointers before we hash */ + struct brw_cs_prog_data cs_prog_data = kernel->prog_data; + cs_prog_data.base.relocs = NULL; + assert(cs_prog_data.base.param == NULL); + _mesa_sha1_update(&sha1_ctx, &cs_prog_data, sizeof(cs_prog_data)); + + SHA1_UPDATE_VALUE(kernel->args_size); + SHA1_UPDATE_VALUE(kernel->arg_count); + _mesa_sha1_update(&sha1_ctx, kernel->args, + kernel->arg_count * sizeof(kernel->args[0])); + + fprintf(fp, "static const struct brw_kernel_arg_desc %s_args[] = {\n", + prefix); + for (unsigned i = 0; i < kernel->arg_count; i++) { + fprintf(fp, " { %d, %d },\n", + kernel->args[i].offset, kernel->args[i].size); + } + fprintf(fp, "};\n\n"); + + _mesa_sha1_update(&sha1_ctx, kernel->code, + kernel->prog_data.base.program_size); + + fprintf(fp, "#if 0 /* BEGIN KERNEL ASSEMBLY */\n"); + fprintf(fp, "\n"); + intel_disassemble(devinfo, kernel->code, 0, fp); + fprintf(fp, "\n"); + fprintf(fp, "#endif /* END KERNEL ASSEMBLY */\n"); + print_u32_data(fp, prefix, "code", kernel->code, + kernel->prog_data.base.program_size); + + fprintf(fp, "static const struct brw_kernel %s = {\n", prefix); + fprintf(fp, " .prog_data = {\n"); + print_cs_prog_data_fields(fp, prefix, " ", &kernel->prog_data); + fprintf(fp, " },\n"); + fprintf(fp, " .args_size = %d,\n", (int)kernel->args_size); + fprintf(fp, " .arg_count = %d,\n", (int)kernel->arg_count); + fprintf(fp, " .args = %s_args,\n", prefix); + fprintf(fp, " .code = %s_code,\n", prefix); + fprintf(fp, "};\n"); + + unsigned char sha1[20]; + _mesa_sha1_final(&sha1_ctx, sha1); + char sha1_str[41]; + _mesa_sha1_format(sha1_str, sha1); + fprintf(fp, "const char *%s_sha1 = \"%s\";\n", prefix, sha1_str); +} + +static void +print_usage(char *exec_name, FILE *f) +{ + fprintf(f, +"Usage: %s [options] [clang args] file\n" +"Options:\n" +" -h --help Print this help.\n" +" -e, --entrypoint Specify the entry-point name.\n" +" -p, --platform Specify the target platform name.\n" +" --prefix Prefix for variable names in generated C code.\n" +" -g, --out Specify the output filename.\n" + , exec_name); +} + +#define OPT_PREFIX 1000 + +int main(int argc, char **argv) +{ + brw_process_intel_debug_variable(); + + static struct option long_options[] ={ + {"help", no_argument, 0, 'h'}, + {"entrypoint", required_argument, 0, 'e'}, + {"platform", required_argument, 0, 'p'}, + {"prefix", required_argument, 0, OPT_PREFIX}, + {"out", required_argument, 0, 'o'}, + {0, 0, 0, 0} + }; + + char *entry_point = NULL, *platform = NULL, *outfile = NULL, *prefix = NULL; + + int ch; + while ((ch = getopt_long(argc, argv, "he:p:o:", long_options, NULL)) != -1) + { + switch (ch) + { + case 'h': + print_usage(argv[0], stdout); + return 0; + case 'e': + entry_point = optarg; + break; + case 'p': + platform = optarg; + break; + case 'o': + outfile = optarg; + break; + case OPT_PREFIX: + prefix = optarg; + break; + default: + fprintf(stderr, "Unrecognized option \"%s\".\n", optarg); + print_usage(argv[0], stderr); + return 1; + } + } + + if (platform == NULL) { + fprintf(stderr, "No target platform name specified.\n"); + print_usage(argv[0], stderr); + return -1; + } + + int pci_id = intel_device_name_to_pci_device_id(platform); + if (pci_id < 0) { + fprintf(stderr, "Invalid target platform name: %s\n", platform); + return -1; + } + + struct intel_device_info _devinfo, *devinfo = &_devinfo; + if (!intel_get_device_info_from_pci_id(pci_id, devinfo)) { + fprintf(stderr, "Failed to get device information.\n"); + return -1; + } + + if (entry_point == NULL) { + fprintf(stderr, "No entry-point name specified.\n"); + print_usage(argv[0], stderr); + return -1; + } + + const char *infile = argv[optind]; + + const char * const *clang_args = (const char * const *)&argv[optind + 1]; + unsigned num_clang_args = argc - optind - 1; + + int fd = open(infile, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "Failed to open %s\n", infile); + return 1; + } + + off_t len = lseek(fd, 0, SEEK_END); + const void *map = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0); + close(fd); + if (map == MAP_FAILED) { + fprintf(stderr, "Failed to mmap the file: errno=%d, %s\n", + errno, strerror(errno)); + return 1; + } + + struct clc_compile_args clc_args = { + .source = { + .name = infile, + .value = map, + }, + .args = clang_args, + .num_args = num_clang_args, + }; + + struct clc_logger logger = { + .error = msg_callback, + .warning = msg_callback, + }; + + struct clc_binary out_spirv; + if (!clc_compile_c_to_spir(&clc_args, &logger, &out_spirv)) + return 1; + + const struct clc_binary *link_spirv_objs[] = { &out_spirv }; + struct clc_linker_args link_args = { + .in_objs = link_spirv_objs, + .num_in_objs = 1, + .create_library = true, + }; + struct clc_binary out_final_spirv; + if (!clc_link_spirv(&link_args, &logger, &out_final_spirv)) + return 1; + + struct clc_parsed_spirv spirv_info; + if (!clc_parse_spirv(&out_final_spirv, &logger, &spirv_info)) + return 1; + + const struct clc_kernel_info *kernel_info = NULL; + for (unsigned i = 0; i < spirv_info.num_kernels; i++) { + if (strcmp(spirv_info.kernels[i].name, entry_point) == 0) { + kernel_info = &spirv_info.kernels[i]; + break; + } + } + if (kernel_info == NULL) { + fprintf(stderr, "Kernel entrypoint %s not found\n", entry_point); + return 1; + } + + void *mem_ctx = ralloc_context(NULL); + struct brw_kernel kernel = {}; + char *error_str; + + struct brw_compiler *compiler = brw_compiler_create(mem_ctx, devinfo); + compiler->shader_debug_log = compiler_log; + compiler->shader_perf_log = compiler_log; + struct disk_cache *disk_cache = get_disk_cache(compiler); + + glsl_type_singleton_init_or_ref(); + + if (!brw_kernel_from_spirv(compiler, disk_cache, &kernel, NULL, mem_ctx, + out_final_spirv.data, out_final_spirv.size, + entry_point, &error_str)) { + fprintf(stderr, "Compile failed: %s\n", error_str); + return 1; + } + + glsl_type_singleton_decref(); + + char prefix_tmp[256]; + if (prefix == NULL) { + bool is_pt_5 = (devinfo->verx10 % 10) == 5; + snprintf(prefix_tmp, sizeof(prefix_tmp), "gfx%d%s_clc_%s", + devinfo->ver, is_pt_5 ? "5" : "", entry_point); + prefix = prefix_tmp; + } + + if (outfile != NULL) { + FILE *fp = fopen(outfile, "w"); + print_kernel(fp, prefix, &kernel, devinfo); + fclose(fp); + } else { + print_kernel(stdout, prefix, &kernel, devinfo); + } + + return 0; +} diff --git a/src/intel/compiler/meson.build b/src/intel/compiler/meson.build index c389dfd54cc..43818ba450a 100644 --- a/src/intel/compiler/meson.build +++ b/src/intel/compiler/meson.build @@ -163,6 +163,21 @@ libintel_compiler = static_library( build_by_default : false, ) +# For now this tool is only going to be used by Anv +if with_intel_clc + prog_intel_clc = executable( + 'intel_clc', + ['intel_clc.c'], + link_with : [ + libintel_compiler, libintel_common, libintel_dev, libisl, + ], + include_directories : [inc_include, inc_src, inc_mapi, inc_mesa, inc_gallium, inc_intel], + c_args : [pre_args, no_override_init_args], + dependencies : [idep_nir, idep_clc, idep_mesautil], + native : true, + ) +endif + if with_tests test( 'intel_compiler_tests',