| /* |
| * Copyright © 2018 Google |
| * |
| * 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 "aco_interface.h" |
| #include "aco_ir.h" |
| #include "vulkan/radv_shader.h" |
| #include "vulkan/radv_shader_args.h" |
| |
| #include <iostream> |
| #include <sstream> |
| |
| static radv_compiler_statistic_info statistic_infos[] = { |
| [aco::statistic_hash] = {"Hash", "CRC32 hash of code and constant data"}, |
| [aco::statistic_instructions] = {"Instructions", "Instruction count"}, |
| [aco::statistic_copies] = {"Copies", "Copy instructions created for pseudo-instructions"}, |
| [aco::statistic_branches] = {"Branches", "Branch instructions"}, |
| [aco::statistic_cycles] = {"Busy Cycles", "Estimate of busy cycles"}, |
| [aco::statistic_vmem_clauses] = {"VMEM Clause", "Number of VMEM clauses (includes 1-sized clauses)"}, |
| [aco::statistic_smem_clauses] = {"SMEM Clause", "Number of SMEM clauses (includes 1-sized clauses)"}, |
| [aco::statistic_vmem_score] = {"VMEM Score", "Average VMEM def-use distances"}, |
| [aco::statistic_smem_score] = {"SMEM Score", "Average SMEM def-use distances"}, |
| [aco::statistic_sgpr_presched] = {"Pre-Sched SGPRs", "SGPR usage before scheduling"}, |
| [aco::statistic_vgpr_presched] = {"Pre-Sched VGPRs", "VGPR usage before scheduling"}, |
| }; |
| |
| static void validate(aco::Program *program) |
| { |
| if (!(aco::debug_flags & aco::DEBUG_VALIDATE)) |
| return; |
| |
| bool is_valid = aco::validate(program, stderr); |
| assert(is_valid); |
| } |
| |
| void aco_compile_shader(unsigned shader_count, |
| struct nir_shader *const *shaders, |
| struct radv_shader_binary **binary, |
| struct radv_shader_args *args) |
| { |
| aco::init(); |
| |
| ac_shader_config config = {0}; |
| std::unique_ptr<aco::Program> program{new aco::Program}; |
| |
| program->collect_statistics = args->options->record_stats; |
| if (program->collect_statistics) |
| memset(program->statistics, 0, sizeof(program->statistics)); |
| |
| /* Instruction Selection */ |
| if (args->is_gs_copy_shader) |
| aco::select_gs_copy_shader(program.get(), shaders[0], &config, args); |
| else |
| aco::select_program(program.get(), shader_count, shaders, &config, args); |
| if (args->options->dump_preoptir) { |
| std::cerr << "After Instruction Selection:\n"; |
| aco_print_program(program.get(), stderr); |
| } |
| |
| /* Phi lowering */ |
| aco::lower_phis(program.get()); |
| aco::dominator_tree(program.get()); |
| validate(program.get()); |
| |
| /* Optimization */ |
| aco::value_numbering(program.get()); |
| aco::optimize(program.get()); |
| |
| /* cleanup and exec mask handling */ |
| aco::setup_reduce_temp(program.get()); |
| aco::insert_exec_mask(program.get()); |
| validate(program.get()); |
| |
| /* spilling and scheduling */ |
| aco::live live_vars = aco::live_var_analysis(program.get(), args->options); |
| aco::spill(program.get(), live_vars, args->options); |
| |
| std::string llvm_ir; |
| if (args->options->record_ir) { |
| char *data = NULL; |
| size_t size = 0; |
| FILE *f = open_memstream(&data, &size); |
| if (f) { |
| aco_print_program(program.get(), f); |
| fputc(0, f); |
| fclose(f); |
| } |
| |
| llvm_ir = std::string(data, data + size); |
| free(data); |
| } |
| |
| if (program->collect_statistics) |
| aco::collect_presched_stats(program.get()); |
| aco::schedule_program(program.get(), live_vars); |
| validate(program.get()); |
| |
| /* Register Allocation */ |
| aco::register_allocation(program.get(), live_vars.live_out); |
| if (args->options->dump_shader) { |
| std::cerr << "After RA:\n"; |
| aco_print_program(program.get(), stderr); |
| } |
| |
| if (aco::validate_ra(program.get(), args->options, stderr)) { |
| std::cerr << "Program after RA validation failure:\n"; |
| aco_print_program(program.get(), stderr); |
| abort(); |
| } |
| |
| validate(program.get()); |
| |
| /* Lower to HW Instructions */ |
| aco::ssa_elimination(program.get()); |
| aco::lower_to_hw_instr(program.get()); |
| |
| /* Insert Waitcnt */ |
| aco::insert_wait_states(program.get()); |
| aco::insert_NOPs(program.get()); |
| |
| if (program->collect_statistics) |
| aco::collect_preasm_stats(program.get()); |
| |
| /* Assembly */ |
| std::vector<uint32_t> code; |
| unsigned exec_size = aco::emit_program(program.get(), code); |
| |
| if (program->collect_statistics) |
| aco::collect_postasm_stats(program.get(), code); |
| |
| bool get_disasm = args->options->dump_shader || args->options->record_ir; |
| |
| size_t size = llvm_ir.size(); |
| |
| std::string disasm; |
| if (get_disasm) { |
| std::ostringstream stream; |
| aco::print_asm(program.get(), code, exec_size / 4u, stream); |
| stream << '\0'; |
| disasm = stream.str(); |
| size += disasm.size(); |
| } |
| |
| size_t stats_size = 0; |
| if (program->collect_statistics) |
| stats_size = sizeof(radv_compiler_statistics) + aco::num_statistics * sizeof(uint32_t); |
| size += stats_size; |
| |
| size += code.size() * sizeof(uint32_t) + sizeof(radv_shader_binary_legacy); |
| /* We need to calloc to prevent unintialized data because this will be used |
| * directly for the disk cache. Uninitialized data can appear because of |
| * padding in the struct or because legacy_binary->data can be at an offset |
| * from the start less than sizeof(radv_shader_binary_legacy). */ |
| radv_shader_binary_legacy* legacy_binary = (radv_shader_binary_legacy*) calloc(size, 1); |
| |
| legacy_binary->base.type = RADV_BINARY_TYPE_LEGACY; |
| legacy_binary->base.stage = shaders[shader_count-1]->info.stage; |
| legacy_binary->base.is_gs_copy_shader = args->is_gs_copy_shader; |
| legacy_binary->base.total_size = size; |
| |
| if (program->collect_statistics) { |
| radv_compiler_statistics *statistics = (radv_compiler_statistics *)legacy_binary->data; |
| statistics->count = aco::num_statistics; |
| statistics->infos = statistic_infos; |
| memcpy(statistics->values, program->statistics, aco::num_statistics * sizeof(uint32_t)); |
| } |
| legacy_binary->stats_size = stats_size; |
| |
| memcpy(legacy_binary->data + legacy_binary->stats_size, code.data(), code.size() * sizeof(uint32_t)); |
| legacy_binary->exec_size = exec_size; |
| legacy_binary->code_size = code.size() * sizeof(uint32_t); |
| |
| legacy_binary->config = config; |
| legacy_binary->disasm_size = 0; |
| legacy_binary->ir_size = llvm_ir.size(); |
| |
| llvm_ir.copy((char*) legacy_binary->data + legacy_binary->stats_size + legacy_binary->code_size, llvm_ir.size()); |
| |
| if (get_disasm) { |
| disasm.copy((char*) legacy_binary->data + legacy_binary->stats_size + legacy_binary->code_size + llvm_ir.size(), disasm.size()); |
| legacy_binary->disasm_size = disasm.size(); |
| } |
| |
| *binary = (radv_shader_binary*) legacy_binary; |
| } |