broadcom/compiler: fix robust buffer access
authorIago Toral Quiroga <itoral@igalia.com>
Thu, 22 Sep 2022 07:18:18 +0000 (09:18 +0200)
committerMarge Bot <emma+marge@anholt.net>
Fri, 23 Sep 2022 06:27:54 +0000 (06:27 +0000)
Our implemention was bogus, it was only putting a cap on the offset
based on the aligned buffer size and this doesn't ensure the access
to the buffer happens within its valid range.

I think the only reason we have been passing the tests is that we
align all buffers sizes to 256B and the tests create buffers with a
size that is smaller than that (like 64B). When get the size of the
buffer from the shader, we get the actual bound range (so 64B in this
case) and by capping to that we don't ensure the access will stay
within that range, but we ensure it will stay within the underlying
memory bound to the buffer (256B), and this is fine by the spec,
however, I think if the actual buffer range was the same as the
underlying allocation we would fail the tests.

A valid behavior for robust buffer access on an out-of-bounds access
is to return any valid bytes within the buffer, so we can just
make that offset 0.

Reviewed-by: Alejandro PiƱeiro <apinheiro@igalia.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/18744>

src/broadcom/compiler/v3d_nir_lower_robust_buffer_access.c

index 260cfc9..1e9f190 100644 (file)
@@ -27,6 +27,7 @@
 static void
 rewrite_offset(nir_builder *b,
                nir_intrinsic_instr *instr,
+               uint32_t type_sz,
                uint32_t buffer_idx,
                uint32_t offset_src,
                nir_intrinsic_op buffer_size_op)
@@ -40,13 +41,20 @@ rewrite_offset(nir_builder *b,
         nir_ssa_dest_init(&size->instr, &size->dest, 1, 32, NULL);
         nir_builder_instr_insert(b, &size->instr);
 
-        /* All out TMU accesses are 32-bit aligned */
-        nir_ssa_def *aligned_buffer_size =
-                nir_iand(b, &size->dest.ssa, nir_imm_int(b, 0xfffffffc));
+        /* Compute the maximum offset being accessed and if it is
+         * out of bounds rewrite it to 0 to ensure the access is
+         * within bounds.
+         */
+        const uint32_t access_size = instr->num_components * type_sz;
+        nir_ssa_def *max_access_offset =
+                nir_iadd(b, instr->src[offset_src].ssa,
+                            nir_imm_int(b, access_size - 1));
+        nir_ssa_def *offset =
+                nir_bcsel(b, nir_uge(b, max_access_offset, &size->dest.ssa),
+                             nir_imm_int(b, 0),
+                             instr->src[offset_src].ssa);
 
         /* Rewrite offset */
-        nir_ssa_def *offset =
-                nir_umin(b, instr->src[offset_src].ssa, aligned_buffer_size);
         nir_instr_rewrite_src(&instr->instr, &instr->src[offset_src],
                               nir_src_for_ssa(offset));
 }
@@ -56,6 +64,7 @@ lower_load(struct v3d_compile *c,
            nir_builder *b,
            nir_intrinsic_instr *instr)
 {
+        uint32_t type_sz = nir_dest_bit_size(instr->dest) / 8;
         uint32_t index = nir_src_comp_as_uint(instr->src[0], 0);
 
         nir_intrinsic_op op;
@@ -67,7 +76,7 @@ lower_load(struct v3d_compile *c,
                 op = nir_intrinsic_get_ssbo_size;
         }
 
-        rewrite_offset(b, instr, index, 1, op);
+        rewrite_offset(b, instr, type_sz, index, 1, op);
 }
 
 static void
@@ -75,8 +84,9 @@ lower_store(struct v3d_compile *c,
             nir_builder *b,
             nir_intrinsic_instr *instr)
 {
+        uint32_t type_sz = nir_src_bit_size(instr->src[0]) / 8;
         uint32_t index = nir_src_comp_as_uint(instr->src[1], 0);
-        rewrite_offset(b, instr, index, 2, nir_intrinsic_get_ssbo_size);
+        rewrite_offset(b, instr, type_sz, index, 2, nir_intrinsic_get_ssbo_size);
 }
 
 static void
@@ -85,7 +95,7 @@ lower_atomic(struct v3d_compile *c,
              nir_intrinsic_instr *instr)
 {
         uint32_t index = nir_src_comp_as_uint(instr->src[0], 0);
-        rewrite_offset(b, instr, index, 1, nir_intrinsic_get_ssbo_size);
+        rewrite_offset(b, instr, 4, index, 1, nir_intrinsic_get_ssbo_size);
 }
 
 static void
@@ -93,10 +103,25 @@ lower_shared(struct v3d_compile *c,
              nir_builder *b,
              nir_intrinsic_instr *instr)
 {
+        uint32_t type_sz;
+        if (instr->intrinsic == nir_intrinsic_load_shared) {
+                type_sz = nir_dest_bit_size(instr->dest) / 8;
+        } else {
+                /* atomic */
+                type_sz = 4;
+        }
+
         b->cursor = nir_before_instr(&instr->instr);
-        nir_ssa_def *aligned_size =
-                nir_imm_int(b, c->s->info.shared_size & 0xfffffffc);
-        nir_ssa_def *offset = nir_umin(b, instr->src[0].ssa, aligned_size);
+        const uint32_t access_size = instr->num_components * type_sz;
+        nir_ssa_def *max_access_offset =
+                nir_iadd(b, instr->src[0].ssa,
+                            nir_imm_int(b, access_size - 1));
+        nir_ssa_def *offset =
+                nir_bcsel(b, nir_uge(b, max_access_offset,
+                                        nir_imm_int(b, c->s->info.shared_size)),
+                             nir_imm_int(b, 0),
+                             instr->src[0].ssa);
+
         nir_instr_rewrite_src(&instr->instr, &instr->src[0],
                               nir_src_for_ssa(offset));
 }