diff --git a/src/amd/compiler/aco_ir.cpp b/src/amd/compiler/aco_ir.cpp index 4cd79a8d746..089dedd7848 100644 --- a/src/amd/compiler/aco_ir.cpp +++ b/src/amd/compiler/aco_ir.cpp @@ -21,6 +21,7 @@ uint64_t debug_flags = 0; static const struct debug_control aco_debug_options[] = { {"validateir", DEBUG_VALIDATE_IR}, {"validatera", DEBUG_VALIDATE_RA}, + {"validate-livevars", DEBUG_VALIDATE_LIVE_VARS}, {"novalidateir", DEBUG_NO_VALIDATE_IR}, {"force-waitcnt", DEBUG_FORCE_WAITCNT}, {"force-waitdeps", DEBUG_FORCE_WAITDEPS}, diff --git a/src/amd/compiler/aco_ir.h b/src/amd/compiler/aco_ir.h index c32d35ba24f..f5d8b226cbb 100644 --- a/src/amd/compiler/aco_ir.h +++ b/src/amd/compiler/aco_ir.h @@ -31,6 +31,7 @@ extern uint64_t debug_flags; enum { DEBUG_VALIDATE_IR = 0x1, DEBUG_VALIDATE_RA = 0x2, + DEBUG_VALIDATE_LIVE_VARS = 0x4, DEBUG_FORCE_WAITCNT = 0x8, DEBUG_NO_VN = 0x10, DEBUG_NO_OPT = 0x20, @@ -2188,6 +2189,7 @@ bool print_asm(Program* program, std::vector& binary, unsigned exec_si bool validate_ir(Program* program); bool validate_cfg(Program* program); bool validate_ra(Program* program); +bool validate_live_vars(Program* program); void collect_presched_stats(Program* program); void collect_preasm_stats(Program* program); diff --git a/src/amd/compiler/aco_util.h b/src/amd/compiler/aco_util.h index ad4b697442f..68a6c686408 100644 --- a/src/amd/compiler/aco_util.h +++ b/src/amd/compiler/aco_util.h @@ -528,6 +528,23 @@ struct IDSet { explicit IDSet(monotonic_buffer_resource& m) : words(m) {} explicit IDSet(const IDSet& other, monotonic_buffer_resource& m) : words(other.words, m) {} + bool operator==(const IDSet& other) const + { + auto it = words.begin(); + for (auto block : other.words) { + if (block.second == block_t{0}) + continue; + while (it != words.end() && it->second == block_t{0}) + it++; + if (it == words.end() || block != *it) + return false; + it++; + } + + return true; + } + bool operator!=(const IDSet& other) const { return !(*this == other); } + private: static uint32_t get_first_set(const block_t& words) { diff --git a/src/amd/compiler/aco_validate.cpp b/src/amd/compiler/aco_validate.cpp index 5733b14b382..00a5f3676e8 100644 --- a/src/amd/compiler/aco_validate.cpp +++ b/src/amd/compiler/aco_validate.cpp @@ -942,6 +942,116 @@ validate_cfg(Program* program) return is_valid; } +bool +validate_live_vars(Program* program) +{ + if (!(debug_flags & DEBUG_VALIDATE_LIVE_VARS)) + return true; + + bool is_valid = true; + const int prev_num_waves = program->num_waves; + const monotonic_buffer_resource old_memory = std::move(program->live.memory); + const std::vector prev_live_in = std::move(program->live.live_in); + const RegisterDemand prev_max_demand = program->max_reg_demand; + std::vector block_demands(program->blocks.size()); + std::vector live_in_demands(program->blocks.size()); + std::vector> register_demands(program->blocks.size()); + + for (unsigned i = 0; i < program->blocks.size(); i++) { + Block& b = program->blocks[i]; + block_demands[i] = b.register_demand; + live_in_demands[i] = b.live_in_demand; + register_demands[i].reserve(b.instructions.size()); + for (unsigned j = 0; j < b.instructions.size(); j++) + register_demands[i].emplace_back(b.instructions[j]->register_demand); + } + + aco::live_var_analysis(program); + + /* Validate RegisterDemand calculation */ + for (unsigned i = 0; i < program->blocks.size(); i++) { + Block& b = program->blocks[i]; + + if (!(b.register_demand == block_demands[i])) { + is_valid = false; + aco_err(program, + "Register Demand not updated correctly for BB%d: got (%3u vgpr, %3u sgpr), but " + "should be (%3u vgpr, %3u sgpr)", + i, block_demands[i].vgpr, block_demands[i].sgpr, b.register_demand.vgpr, + b.register_demand.sgpr); + } + if (!(b.live_in_demand == live_in_demands[i])) { + is_valid = false; + aco_err(program, + "Live-in Demand not updated correctly for BB%d: got (%3u vgpr, %3u sgpr), but " + "should be (%3u vgpr, %3u sgpr)", + i, live_in_demands[i].vgpr, live_in_demands[i].sgpr, b.live_in_demand.vgpr, + b.live_in_demand.sgpr); + } + + for (unsigned j = 0; j < b.instructions.size(); j++) { + if (b.instructions[j]->register_demand == register_demands[i][j]) + continue; + + char* out; + size_t outsize; + struct u_memstream mem; + u_memstream_open(&mem, &out, &outsize); + FILE* const memf = u_memstream_get(&mem); + + fprintf(memf, + "Register Demand not updated correctly: got (%3u vgpr, %3u sgpr), but should be " + "(%3u vgpr, %3u sgpr): \n\t", + register_demands[i][j].vgpr, register_demands[i][j].sgpr, + b.instructions[j]->register_demand.vgpr, b.instructions[j]->register_demand.sgpr); + aco_print_instr(program->gfx_level, b.instructions[j].get(), memf, print_kill); + u_memstream_close(&mem); + + aco_err(program, "%s", out); + free(out); + + is_valid = false; + } + } + if (!(program->max_reg_demand == prev_max_demand) || program->num_waves != prev_num_waves) { + is_valid = false; + aco_err(program, + "Max Register Demand and Num Waves not updated correctly: got (%3u vgpr, %3u sgpr) " + "and %2u waves, but should be (%3u vgpr, %3u sgpr) and %2u waves", + prev_max_demand.vgpr, prev_max_demand.sgpr, prev_num_waves, + program->max_reg_demand.vgpr, program->max_reg_demand.sgpr, program->num_waves); + } + + /* Validate Live-in sets */ + for (unsigned i = 0; i < program->blocks.size(); i++) { + if (prev_live_in[i] != program->live.live_in[i]) { + char* out; + size_t outsize; + struct u_memstream mem; + u_memstream_open(&mem, &out, &outsize); + FILE* const memf = u_memstream_get(&mem); + + fprintf(memf, "Live-in set not updated correctly for BB%d:", i); + fprintf(memf, "\nMissing values: "); + for (unsigned t : program->live.live_in[i]) { + if (prev_live_in[i].count(t) == 0) + fprintf(memf, "%%%d, ", t); + } + fprintf(memf, "\nAdditional values: "); + for (unsigned t : prev_live_in[i]) { + if (program->live.live_in[i].count(t) == 0) + fprintf(memf, "%%%d, ", t); + } + u_memstream_close(&mem); + aco_err(program, "%s", out); + free(out); + is_valid = false; + } + } + + return is_valid; +} + /* RA validation */ namespace {