mesa/src/compiler/nir/nir_lower_abort.c
Samuel Pitoiset 8c9995e7fa
Some checks are pending
macOS-CI / macOS-CI (dri) (push) Waiting to run
macOS-CI / macOS-CI (xlib) (push) Waiting to run
nir: add nir_lower_abort
Signed-off-by: Samuel Pitoiset <samuel.pitoiset@gmail.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/41651>
2026-05-27 06:37:03 +00:00

125 lines
4.7 KiB
C

/*
* Copyright © 2026 Valve 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 "nir.h"
#include "nir_builder.h"
#include "nir_builder_opcodes.h"
/* This pass lowers nir_abort() to global stores. The buffer layout is
* described as follows:
*
* uint32_t offset; (and total written size)
* uint8_t data[];
*
* For each message:
* uint64_t msg_data_size;
* uint8_t msg_data[msg_data_size];
*/
static bool
lower_abort_intrin(nir_builder *b, nir_intrinsic_instr *intrin, void *_options)
{
const nir_lower_abort_options *options = _options;
if (intrin->intrinsic != nir_intrinsic_abort)
return false;
b->cursor = nir_before_instr(&intrin->instr);
const unsigned ptr_bit_size =
options->ptr_bit_size ? options->ptr_bit_size : nir_get_ptr_bitsize(b->shader);
nir_def *buffer_addr;
if (options->buffer_addr) {
buffer_addr = nir_imm_intN_t(b, options->buffer_addr, ptr_bit_size);
} else {
buffer_addr = nir_load_abort_buffer_address(b, ptr_bit_size);
}
/* Atomic add a buffer size counter to determine where to write. */
nir_deref_instr *buffer =
nir_build_deref_cast(b, buffer_addr, nir_var_mem_global,
glsl_array_type(glsl_uint8_t_type(), 0, 1), 0);
nir_deref_instr *message_deref = nir_src_as_deref(intrin->src[0]);
const unsigned slot_header_size = sizeof(uint64_t);
const unsigned message_data_size = glsl_get_explicit_size(message_deref->type, true);
const unsigned slot_size = slot_header_size + message_data_size;
/* Increment the counter at the beginning of the buffer */
nir_deref_instr *counter = nir_build_deref_array_imm(b, buffer, 0);
counter = nir_build_deref_cast(b, &counter->def, nir_var_mem_global,
glsl_uint_type(), 0);
counter->cast.align_mul = 4;
nir_def *offset =
nir_deref_atomic(b, 32, &counter->def, nir_imm_int(b, slot_size),
.atomic_op = nir_atomic_op_iadd);
/* Check if we're still in-bounds */
nir_def *buffer_size;
if (options->max_buffer_size) {
buffer_size = nir_imm_int(b, options->max_buffer_size);
} else {
buffer_size = nir_load_abort_buffer_size(b);
}
nir_push_if(b, nir_ult(b, offset, nir_iadd_imm(b, buffer_size, -slot_size)));
{
nir_def *base = nir_u2uN(b, offset, ptr_bit_size);
/* Store the message data size as 64-bit. */
nir_deref_instr *message_data_size_deref =
nir_build_deref_cast(b, &nir_build_deref_array(b, buffer, base)->def,
nir_var_mem_global, glsl_uint64_t_type(), 0);
message_data_size_deref->cast.align_mul = 1;
nir_store_deref(b, message_data_size_deref, nir_imm_int64(b, message_data_size), ~0);
/* Store the message data. */
nir_def *dst_addr = nir_iadd_imm(b, base, slot_header_size);
nir_deref_instr *dst =
nir_build_deref_cast(b, &nir_build_deref_array(b, buffer, dst_addr)->def,
nir_var_mem_global, message_deref->type, 0);
nir_copy_deref(b, dst, message_deref);
}
nir_pop_if(b, NULL);
/* Halt is a jump instruction so can only appear at the end of a block.
* The abort might be in the middle of a block. So, wrap the halt and let
* control flow optimization clean up after us.
*/
nir_push_if(b, nir_imm_true(b));
{
nir_jump(b, nir_jump_halt);
}
nir_pop_if(b, NULL);
nir_instr_remove(&intrin->instr);
return true;
}
bool
nir_lower_abort(nir_shader *nir, const nir_lower_abort_options *options)
{
return nir_shader_intrinsics_pass(nir, lower_abort_intrin,
nir_metadata_none,
(void *)options);
}