From 05ccc586ffc857be626d482717f9ff78346e50d0 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sat, 9 May 2026 14:07:24 -0700 Subject: [PATCH] shader_recompiler: Remove shared memory bounds checking. (#4385) --- .../backend/spirv/emit_spirv_atomic.cpp | 22 +---- .../backend/spirv/emit_spirv_bounds.h | 90 ------------------- .../spirv/emit_spirv_context_get_set.cpp | 1 - .../spirv/emit_spirv_shared_memory.cpp | 53 +++-------- 4 files changed, 16 insertions(+), 150 deletions(-) delete mode 100644 src/shader_recompiler/backend/spirv/emit_spirv_bounds.h diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp index ded807ed7..806169217 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp @@ -1,8 +1,6 @@ // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "common/div_ceil.h" -#include "shader_recompiler/backend/spirv/emit_spirv_bounds.h" #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" #include "shader_recompiler/backend/spirv/spirv_emit_context.h" @@ -22,48 +20,36 @@ Id SharedAtomicU32(EmitContext& ctx, Id offset, Id value, Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id, Id)) { const Id shift_id{ctx.ConstU32(2U)}; const Id index{ctx.OpShiftRightLogical(ctx.U32[1], offset, shift_id)}; - const u32 num_elements{Common::DivCeil(ctx.runtime_info.cs_info.shared_memory_size, 4u)}; const Id pointer{ctx.EmitSharedMemoryAccess(ctx.shared_u32, ctx.shared_memory_u32, index)}; const auto [scope, semantics]{AtomicArgs(ctx)}; - return AccessBoundsCheck<32>(ctx, index, ctx.ConstU32(num_elements), [&] { - return (ctx.*atomic_func)(ctx.U32[1], pointer, scope, semantics, value); - }); + return (ctx.*atomic_func)(ctx.U32[1], pointer, scope, semantics, value); } Id SharedAtomicU32IncDec(EmitContext& ctx, Id offset, Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id)) { const Id shift_id{ctx.ConstU32(2U)}; const Id index{ctx.OpShiftRightLogical(ctx.U32[1], offset, shift_id)}; - const u32 num_elements{Common::DivCeil(ctx.runtime_info.cs_info.shared_memory_size, 4u)}; const Id pointer{ctx.EmitSharedMemoryAccess(ctx.shared_u32, ctx.shared_memory_u32, index)}; const auto [scope, semantics]{AtomicArgs(ctx)}; - return AccessBoundsCheck<32>(ctx, index, ctx.ConstU32(num_elements), [&] { - return (ctx.*atomic_func)(ctx.U32[1], pointer, scope, semantics); - }); + return (ctx.*atomic_func)(ctx.U32[1], pointer, scope, semantics); } Id SharedAtomicU64(EmitContext& ctx, Id offset, Id value, Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id, Id)) { const Id shift_id{ctx.ConstU32(3U)}; const Id index{ctx.OpShiftRightLogical(ctx.U32[1], offset, shift_id)}; - const u32 num_elements{Common::DivCeil(ctx.runtime_info.cs_info.shared_memory_size, 8u)}; const Id pointer{ctx.EmitSharedMemoryAccess(ctx.shared_u64, ctx.shared_memory_u64, index)}; const auto [scope, semantics]{AtomicArgs(ctx)}; - return AccessBoundsCheck<64>(ctx, index, ctx.ConstU32(num_elements), [&] { - return (ctx.*atomic_func)(ctx.U64, pointer, scope, semantics, value); - }); + return (ctx.*atomic_func)(ctx.U64, pointer, scope, semantics, value); } Id SharedAtomicU64IncDec(EmitContext& ctx, Id offset, Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id)) { const Id shift_id{ctx.ConstU32(3U)}; const Id index{ctx.OpShiftRightLogical(ctx.U32[1], offset, shift_id)}; - const u32 num_elements{Common::DivCeil(ctx.runtime_info.cs_info.shared_memory_size, 8u)}; const Id pointer{ctx.EmitSharedMemoryAccess(ctx.shared_u64, ctx.shared_memory_u64, index)}; const auto [scope, semantics]{AtomicArgs(ctx)}; - return AccessBoundsCheck<64>(ctx, index, ctx.ConstU32(num_elements), [&] { - return (ctx.*atomic_func)(ctx.U64, pointer, scope, semantics); - }); + return (ctx.*atomic_func)(ctx.U64, pointer, scope, semantics); } template diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_bounds.h b/src/shader_recompiler/backend/spirv/emit_spirv_bounds.h deleted file mode 100644 index e66467c6b..000000000 --- a/src/shader_recompiler/backend/spirv/emit_spirv_bounds.h +++ /dev/null @@ -1,90 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "shader_recompiler/backend/spirv/spirv_emit_context.h" - -namespace Shader::Backend::SPIRV { - -template -std::tuple ResolveTypeAndZero(EmitContext& ctx) { - Id result_type{}; - Id zero_value{}; - if constexpr (bit_size == 64 && num_components == 1 && !is_float) { - result_type = ctx.U64; - zero_value = ctx.u64_zero_value; - } else if constexpr (bit_size == 32) { - if (is_float) { - result_type = ctx.F32[num_components]; - zero_value = ctx.f32_zero_value; - } else { - result_type = ctx.U32[num_components]; - zero_value = ctx.u32_zero_value; - } - } else if constexpr (bit_size == 16 && num_components == 1 && !is_float) { - result_type = ctx.U16; - zero_value = ctx.u16_zero_value; - } else if constexpr (bit_size == 8 && num_components == 1 && !is_float) { - result_type = ctx.U8; - zero_value = ctx.u8_zero_value; - } else { - static_assert(false, "Type not supported."); - } - if (num_components > 1) { - std::array zero_ids; - zero_ids.fill(zero_value); - zero_value = ctx.ConstantComposite(result_type, zero_ids); - } - return {result_type, zero_value}; -} - -template -auto AccessBoundsCheck(EmitContext& ctx, Id index, Id buffer_size, auto emit_func) { - if (Sirit::ValidId(buffer_size)) { - // Bounds checking enabled, wrap in a conditional branch to make sure that - // the atomic is not mistakenly executed when the index is out of bounds. - auto compare_index = index; - if (num_components > 1) { - compare_index = ctx.OpIAdd(ctx.U32[1], index, ctx.ConstU32(num_components - 1)); - } - const Id in_bounds = ctx.OpULessThan(ctx.U1[1], compare_index, buffer_size); - const Id ib_label = ctx.OpLabel(); - const Id end_label = ctx.OpLabel(); - ctx.OpSelectionMerge(end_label, spv::SelectionControlMask::MaskNone); - ctx.OpBranchConditional(in_bounds, ib_label, end_label); - const auto last_label = ctx.last_label; - ctx.AddLabel(ib_label); - const auto ib_result = emit_func(); - ctx.OpBranch(end_label); - ctx.AddLabel(end_label); - if (Sirit::ValidId(ib_result)) { - const auto [result_type, zero_value] = - ResolveTypeAndZero(ctx); - return ctx.OpPhi(result_type, ib_result, ib_label, zero_value, last_label); - } else { - return Id{0}; - } - } - // Bounds checking not enabled, just perform the atomic operation. - return emit_func(); -} - -template -static Id LoadAccessBoundsCheck(EmitContext& ctx, Id index, Id buffer_size, Id result) { - if (Sirit::ValidId(buffer_size)) { - // Bounds checking enabled, wrap in a select. - auto compare_index = index; - if (num_components > 1) { - compare_index = ctx.OpIAdd(ctx.U32[1], index, ctx.ConstU32(num_components - 1)); - } - const Id in_bounds = ctx.OpULessThan(ctx.U1[1], compare_index, buffer_size); - const auto [result_type, zero_value] = - ResolveTypeAndZero(ctx); - return ctx.OpSelect(result_type, in_bounds, result, zero_value); - } - // Bounds checking not enabled, just return the plain value. - return result; -} - -} // namespace Shader::Backend::SPIRV diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp index 9c60b3d7a..47a87958c 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp @@ -3,7 +3,6 @@ #include "common/assert.h" #include "core/emulator_settings.h" -#include "shader_recompiler/backend/spirv/emit_spirv_bounds.h" #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" #include "shader_recompiler/backend/spirv/spirv_emit_context.h" #include "shader_recompiler/ir/attribute.h" diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_shared_memory.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_shared_memory.cpp index 731ccd55a..92231bbf1 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_shared_memory.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_shared_memory.cpp @@ -1,8 +1,6 @@ // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "common/div_ceil.h" -#include "shader_recompiler/backend/spirv/emit_spirv_bounds.h" #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" #include "shader_recompiler/backend/spirv/spirv_emit_context.h" @@ -11,70 +9,43 @@ namespace Shader::Backend::SPIRV { Id EmitLoadSharedU16(EmitContext& ctx, Id offset) { const Id shift_id{ctx.ConstU32(1U)}; const Id index{ctx.OpShiftRightLogical(ctx.U32[1], offset, shift_id)}; - const u32 num_elements{Common::DivCeil(ctx.runtime_info.cs_info.shared_memory_size, 2u)}; - - return AccessBoundsCheck<16>(ctx, index, ctx.ConstU32(num_elements), [&] { - const Id pointer = ctx.EmitSharedMemoryAccess(ctx.shared_u16, ctx.shared_memory_u16, index); - return ctx.OpLoad(ctx.U16, pointer); - }); + const Id pointer = ctx.EmitSharedMemoryAccess(ctx.shared_u16, ctx.shared_memory_u16, index); + return ctx.OpLoad(ctx.U16, pointer); } Id EmitLoadSharedU32(EmitContext& ctx, Id offset) { const Id shift_id{ctx.ConstU32(2U)}; const Id index{ctx.OpShiftRightLogical(ctx.U32[1], offset, shift_id)}; - const u32 num_elements{Common::DivCeil(ctx.runtime_info.cs_info.shared_memory_size, 4u)}; - - return AccessBoundsCheck<32>(ctx, index, ctx.ConstU32(num_elements), [&] { - const Id pointer = ctx.EmitSharedMemoryAccess(ctx.shared_u32, ctx.shared_memory_u32, index); - return ctx.OpLoad(ctx.U32[1], pointer); - }); + const Id pointer = ctx.EmitSharedMemoryAccess(ctx.shared_u32, ctx.shared_memory_u32, index); + return ctx.OpLoad(ctx.U32[1], pointer); } Id EmitLoadSharedU64(EmitContext& ctx, Id offset) { const Id shift_id{ctx.ConstU32(3U)}; const Id index{ctx.OpShiftRightLogical(ctx.U32[1], offset, shift_id)}; - const u32 num_elements{Common::DivCeil(ctx.runtime_info.cs_info.shared_memory_size, 8u)}; - - return AccessBoundsCheck<64>(ctx, index, ctx.ConstU32(num_elements), [&] { - const Id pointer = ctx.EmitSharedMemoryAccess(ctx.shared_u64, ctx.shared_memory_u64, index); - return ctx.OpLoad(ctx.U64, pointer); - }); + const Id pointer = ctx.EmitSharedMemoryAccess(ctx.shared_u64, ctx.shared_memory_u64, index); + return ctx.OpLoad(ctx.U64, pointer); } void EmitWriteSharedU16(EmitContext& ctx, Id offset, Id value) { const Id shift{ctx.ConstU32(1U)}; const Id index{ctx.OpShiftRightLogical(ctx.U32[1], offset, shift)}; - const u32 num_elements{Common::DivCeil(ctx.runtime_info.cs_info.shared_memory_size, 2u)}; - - AccessBoundsCheck<16>(ctx, index, ctx.ConstU32(num_elements), [&] { - const Id pointer = ctx.EmitSharedMemoryAccess(ctx.shared_u16, ctx.shared_memory_u16, index); - ctx.OpStore(pointer, value); - return Id{0}; - }); + const Id pointer = ctx.EmitSharedMemoryAccess(ctx.shared_u16, ctx.shared_memory_u16, index); + ctx.OpStore(pointer, value); } void EmitWriteSharedU32(EmitContext& ctx, Id offset, Id value) { const Id shift{ctx.ConstU32(2U)}; const Id index{ctx.OpShiftRightLogical(ctx.U32[1], offset, shift)}; - const u32 num_elements{Common::DivCeil(ctx.runtime_info.cs_info.shared_memory_size, 4u)}; - - AccessBoundsCheck<32>(ctx, index, ctx.ConstU32(num_elements), [&] { - const Id pointer = ctx.EmitSharedMemoryAccess(ctx.shared_u32, ctx.shared_memory_u32, index); - ctx.OpStore(pointer, value); - return Id{0}; - }); + const Id pointer = ctx.EmitSharedMemoryAccess(ctx.shared_u32, ctx.shared_memory_u32, index); + ctx.OpStore(pointer, value); } void EmitWriteSharedU64(EmitContext& ctx, Id offset, Id value) { const Id shift{ctx.ConstU32(3U)}; const Id index{ctx.OpShiftRightLogical(ctx.U32[1], offset, shift)}; - const u32 num_elements{Common::DivCeil(ctx.runtime_info.cs_info.shared_memory_size, 8u)}; - - AccessBoundsCheck<64>(ctx, index, ctx.ConstU32(num_elements), [&] { - const Id pointer = ctx.EmitSharedMemoryAccess(ctx.shared_u64, ctx.shared_memory_u64, index); - ctx.OpStore(pointer, value); - return Id{0}; - }); + const Id pointer = ctx.EmitSharedMemoryAccess(ctx.shared_u64, ctx.shared_memory_u64, index); + ctx.OpStore(pointer, value); } } // namespace Shader::Backend::SPIRV