From d0f75fdeab2b7ecd768e2dc02a4b3b665b94dd28 Mon Sep 17 00:00:00 2001 From: Iago Toral Quiroga Date: Wed, 13 Dec 2023 12:43:20 +0100 Subject: [PATCH] broadcom: lower null pointers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We only support the variablePointersStorageBuffer feature of variable pointers, which basically ensures that pointers may only target one buffer. This means that a particular pointer may change where it points within a given buffer but it cannot change its value to point to some other buffer. This is a requirement from us since we expect buffer indices on buffer loads and stores to be constant, so we can't have a buffer load come through a pointer that may be assigned to different buffers, since in that case the buffer index would need to come from bcsel. There is, however, a small complication: the spec still allows pointers to be null, and NIR defines null pointers to use 0xffffffff for both the buffer index and the offset, which will cause a problem in a scenario like this: int *b = ... if (cond) { b = null; discard; } ubo_load(b); Here the buffer index for the ubo load may come from a bcsel choosing between the null pointer (0xffffffff) and the valid address (let's say 0), so we don't have a constant and we assert fail. This change detects this scenario and upon finding it will rewrite the buffer index on the null pointer branch of the bcsel to match that of the valid branch so that later optimizations passes can remove the bcsel and we end up with a constant index. This is fine because a null pointer dereference is undefined behavior and it is not something we should see in valid applications. Reviewed-by: Alejandro PiƱeiro Part-of: --- src/broadcom/compiler/vir.c | 76 +++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/src/broadcom/compiler/vir.c b/src/broadcom/compiler/vir.c index 48acb314ac7..48d08a9ee0c 100644 --- a/src/broadcom/compiler/vir.c +++ b/src/broadcom/compiler/vir.c @@ -605,6 +605,81 @@ lower_tex_packing_cb(const nir_tex_instr *tex, const void *data) nir_lower_tex_packing_16 : nir_lower_tex_packing_none; } +static bool +v3d_nir_lower_null_pointers_cb(nir_builder *b, + nir_intrinsic_instr *intr, + void *_state) +{ + uint32_t buffer_src_idx; + + switch (intr->intrinsic) { + case nir_intrinsic_load_ubo: + case nir_intrinsic_load_ssbo: + buffer_src_idx = 0; + break; + case nir_intrinsic_store_ssbo: + buffer_src_idx = 1; + break; + default: + return false; + } + + /* If index if constant we are good */ + nir_src *src = &intr->src[buffer_src_idx]; + if (nir_src_is_const(*src)) + return false; + + /* Otherwise, see if it comes from a bcsel including a null pointer */ + if (src->ssa->parent_instr->type != nir_instr_type_alu) + return false; + + nir_alu_instr *alu = nir_instr_as_alu(src->ssa->parent_instr); + if (alu->op != nir_op_bcsel) + return false; + + /* A null pointer is specified using block index 0xffffffff */ + int32_t null_src_idx = -1; + for (int i = 1; i < 3; i++) { + /* FIXME: since we are running this before optimization maybe + * we need to also handle the case where we may have bcsel + * chain that we need to recurse? + */ + if (!nir_src_is_const(alu->src[i].src)) + continue; + if (nir_src_comp_as_uint(alu->src[i].src, 0) != 0xffffffff) + continue; + + /* One of the bcsel srcs is a null pointer reference */ + null_src_idx = i; + break; + } + + if (null_src_idx < 0) + return false; + + assert(null_src_idx == 1 || null_src_idx == 2); + int32_t copy_src_idx = null_src_idx == 1 ? 2 : 1; + + /* Rewrite the null pointer reference so we use the same buffer index + * as the other bcsel branch. This will allow optimization to remove + * the bcsel and we should then end up with a constant buffer index + * like we need. + */ + b->cursor = nir_before_instr(&alu->instr); + nir_def *copy = nir_mov(b, alu->src[copy_src_idx].src.ssa); + nir_src_rewrite(&alu->src[null_src_idx].src, copy); + + return true; +} + +static bool +v3d_nir_lower_null_pointers(nir_shader *s) +{ + return nir_shader_intrinsics_pass(s, v3d_nir_lower_null_pointers_cb, + nir_metadata_block_index | + nir_metadata_dominance, NULL); +} + static void v3d_lower_nir(struct v3d_compile *c) { @@ -656,6 +731,7 @@ v3d_lower_nir(struct v3d_compile *c) 0, glsl_get_natural_size_align_bytes); NIR_PASS(_, c->s, v3d_nir_lower_scratch); + NIR_PASS(_, c->s, v3d_nir_lower_null_pointers); } static void