diff --git a/src/asahi/compiler/agx_compile.c b/src/asahi/compiler/agx_compile.c index 0f189f4cb38..287947ce3e1 100644 --- a/src/asahi/compiler/agx_compile.c +++ b/src/asahi/compiler/agx_compile.c @@ -38,6 +38,7 @@ static const struct debug_named_value agx_debug_options[] = { {"shaderdb", AGX_DBG_SHADERDB, "Print statistics"}, {"verbose", AGX_DBG_VERBOSE, "Disassemble verbosely"}, {"internal", AGX_DBG_INTERNAL, "Dump even internal shaders"}, + {"novalidate",AGX_DBG_NOVALIDATE,"Skip IR validation in debug builds"}, DEBUG_NAMED_VALUE_END }; @@ -1790,11 +1791,14 @@ agx_compile_shader_nir(nir_shader *nir, agx_foreach_block(ctx, block) block->index = ctx->num_blocks++; + agx_validate(ctx, "IR translation"); + if (agx_debug & AGX_DBG_SHADERS && !skip_internal) agx_print_shader(ctx, stdout); agx_optimizer(ctx); agx_dce(ctx); + agx_validate(ctx, "Optimization"); if (agx_debug & AGX_DBG_SHADERS && !skip_internal) agx_print_shader(ctx, stdout); diff --git a/src/asahi/compiler/agx_compiler.h b/src/asahi/compiler/agx_compiler.h index 60fb9baee3e..1a2860fb308 100644 --- a/src/asahi/compiler/agx_compiler.h +++ b/src/asahi/compiler/agx_compiler.h @@ -44,6 +44,7 @@ enum agx_dbg { AGX_DBG_SHADERDB = BITFIELD_BIT(2), AGX_DBG_VERBOSE = BITFIELD_BIT(3), AGX_DBG_INTERNAL = BITFIELD_BIT(4), + AGX_DBG_NOVALIDATE = BITFIELD_BIT(5), }; extern int agx_debug; @@ -735,6 +736,12 @@ void agx_dce(agx_context *ctx); void agx_ra(agx_context *ctx); void agx_pack_binary(agx_context *ctx, struct util_dynarray *emission); +#ifndef NDEBUG +void agx_validate(agx_context *ctx, const char *after_str); +#else +static inline void agx_validate(UNUSED agx_context *ctx, UNUSED const char *after_str) { return; } +#endif + unsigned agx_write_registers(agx_instr *I, unsigned d); struct agx_copy { diff --git a/src/asahi/compiler/agx_validate.c b/src/asahi/compiler/agx_validate.c new file mode 100644 index 00000000000..874409ac4b1 --- /dev/null +++ b/src/asahi/compiler/agx_validate.c @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2022 Alyssa Rosenzweig + * Copyright (C) 2021 Collabora, Ltd. + * + * 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 MERCHANTAagxLITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIAagxLITY, 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 "agx_compiler.h" + +/* Validatation doesn't make sense in release builds */ +#ifndef NDEBUG + +#define agx_validate_assert(stmt) if (!(stmt)) { return false; } + +/* + * If a block contains phi nodes, they must come at the start of the block. If a + * block contains control flow, it must come after a p_logical_end marker. + * Therefore the form of a valid block is: + * + * Phi nodes + * General instructions + * Logical end + * Control flow instructions + * + * Validate that this form is satisfied. + * + * XXX: This only applies before we delete the logical end instructions, maybe + * that should be deferred though? + */ +enum agx_block_state { + AGX_BLOCK_STATE_PHI = 0, + AGX_BLOCK_STATE_BODY = 1, + AGX_BLOCK_STATE_CF = 2 +}; + +static bool +agx_validate_block_form(agx_block *block) +{ + enum agx_block_state state = AGX_BLOCK_STATE_PHI; + + agx_foreach_instr_in_block(block, I) { + switch (I->op) { + case AGX_OPCODE_PHI: + agx_validate_assert(state == AGX_BLOCK_STATE_PHI); + break; + + default: + agx_validate_assert(state != AGX_BLOCK_STATE_CF); + state = AGX_BLOCK_STATE_BODY; + break; + + case AGX_OPCODE_P_LOGICAL_END: + agx_validate_assert(state != AGX_BLOCK_STATE_CF); + state = AGX_BLOCK_STATE_CF; + break; + + case AGX_OPCODE_JMP_EXEC_ANY: + case AGX_OPCODE_JMP_EXEC_NONE: + case AGX_OPCODE_POP_EXEC: + case AGX_OPCODE_IF_ICMP: + case AGX_OPCODE_ELSE_ICMP: + case AGX_OPCODE_WHILE_ICMP: + case AGX_OPCODE_IF_FCMP: + case AGX_OPCODE_ELSE_FCMP: + case AGX_OPCODE_WHILE_FCMP: + agx_validate_assert(state == AGX_BLOCK_STATE_CF); + break; + } + } + + return true; +} + +void +agx_validate(agx_context *ctx, const char *after) +{ + bool fail = false; + + if (agx_debug & AGX_DBG_NOVALIDATE) + return; + + agx_foreach_block(ctx, block) { + if (!agx_validate_block_form(block)) { + fprintf(stderr, "Invalid block form after %s\n", after); + agx_print_block(block, stdout); + fail = true; + } + } + + /* TODO: Validate more invariants */ + + if (fail) { + agx_print_shader(ctx, stderr); + exit(1); + } +} + +#endif /* NDEBUG */ diff --git a/src/asahi/compiler/meson.build b/src/asahi/compiler/meson.build index eef957bd0ce..5a3880b3a9c 100644 --- a/src/asahi/compiler/meson.build +++ b/src/asahi/compiler/meson.build @@ -30,6 +30,7 @@ libasahi_agx_files = files( 'agx_optimizer.c', 'agx_register_allocate.c', 'agx_uniforms.c', + 'agx_validate.c', ) agx_opcodes_h = custom_target(