mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2026-06-03 22:45:00 -06:00
shader_recompiler: Remove shared memory bounds checking. (#4385)
This commit is contained in:
parent
05df651cd8
commit
05ccc586ff
@ -1,8 +1,6 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// 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/emit_spirv_instructions.h"
|
||||||
#include "shader_recompiler/backend/spirv/spirv_emit_context.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)) {
|
Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id, Id)) {
|
||||||
const Id shift_id{ctx.ConstU32(2U)};
|
const Id shift_id{ctx.ConstU32(2U)};
|
||||||
const Id index{ctx.OpShiftRightLogical(ctx.U32[1], offset, shift_id)};
|
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 Id pointer{ctx.EmitSharedMemoryAccess(ctx.shared_u32, ctx.shared_memory_u32, index)};
|
||||||
const auto [scope, semantics]{AtomicArgs(ctx)};
|
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 SharedAtomicU32IncDec(EmitContext& ctx, Id offset,
|
||||||
Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id)) {
|
Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id)) {
|
||||||
const Id shift_id{ctx.ConstU32(2U)};
|
const Id shift_id{ctx.ConstU32(2U)};
|
||||||
const Id index{ctx.OpShiftRightLogical(ctx.U32[1], offset, shift_id)};
|
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 Id pointer{ctx.EmitSharedMemoryAccess(ctx.shared_u32, ctx.shared_memory_u32, index)};
|
||||||
const auto [scope, semantics]{AtomicArgs(ctx)};
|
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 SharedAtomicU64(EmitContext& ctx, Id offset, Id value,
|
||||||
Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id, Id)) {
|
Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id, Id)) {
|
||||||
const Id shift_id{ctx.ConstU32(3U)};
|
const Id shift_id{ctx.ConstU32(3U)};
|
||||||
const Id index{ctx.OpShiftRightLogical(ctx.U32[1], offset, shift_id)};
|
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 Id pointer{ctx.EmitSharedMemoryAccess(ctx.shared_u64, ctx.shared_memory_u64, index)};
|
||||||
const auto [scope, semantics]{AtomicArgs(ctx)};
|
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 SharedAtomicU64IncDec(EmitContext& ctx, Id offset,
|
||||||
Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id)) {
|
Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id)) {
|
||||||
const Id shift_id{ctx.ConstU32(3U)};
|
const Id shift_id{ctx.ConstU32(3U)};
|
||||||
const Id index{ctx.OpShiftRightLogical(ctx.U32[1], offset, shift_id)};
|
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 Id pointer{ctx.EmitSharedMemoryAccess(ctx.shared_u64, ctx.shared_memory_u64, index)};
|
||||||
const auto [scope, semantics]{AtomicArgs(ctx)};
|
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 <bool is_float = false>
|
template <bool is_float = false>
|
||||||
|
|||||||
@ -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 <u32 bit_size, u32 num_components = 1, bool is_float = false>
|
|
||||||
std::tuple<Id, Id> 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<Id, num_components> zero_ids;
|
|
||||||
zero_ids.fill(zero_value);
|
|
||||||
zero_value = ctx.ConstantComposite(result_type, zero_ids);
|
|
||||||
}
|
|
||||||
return {result_type, zero_value};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <u32 bit_size, u32 num_components = 1, bool is_float = false>
|
|
||||||
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<bit_size, num_components, is_float>(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 <u32 bit_size, u32 num_components = 1, bool is_float = false>
|
|
||||||
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<bit_size, num_components, is_float>(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
|
|
||||||
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "core/emulator_settings.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/emit_spirv_instructions.h"
|
||||||
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
|
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
|
||||||
#include "shader_recompiler/ir/attribute.h"
|
#include "shader_recompiler/ir/attribute.h"
|
||||||
|
|||||||
@ -1,8 +1,6 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// 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/emit_spirv_instructions.h"
|
||||||
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
|
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
|
||||||
|
|
||||||
@ -11,70 +9,43 @@ namespace Shader::Backend::SPIRV {
|
|||||||
Id EmitLoadSharedU16(EmitContext& ctx, Id offset) {
|
Id EmitLoadSharedU16(EmitContext& ctx, Id offset) {
|
||||||
const Id shift_id{ctx.ConstU32(1U)};
|
const Id shift_id{ctx.ConstU32(1U)};
|
||||||
const Id index{ctx.OpShiftRightLogical(ctx.U32[1], offset, shift_id)};
|
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)};
|
const Id pointer = ctx.EmitSharedMemoryAccess(ctx.shared_u16, ctx.shared_memory_u16, index);
|
||||||
|
return ctx.OpLoad(ctx.U16, pointer);
|
||||||
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);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Id EmitLoadSharedU32(EmitContext& ctx, Id offset) {
|
Id EmitLoadSharedU32(EmitContext& ctx, Id offset) {
|
||||||
const Id shift_id{ctx.ConstU32(2U)};
|
const Id shift_id{ctx.ConstU32(2U)};
|
||||||
const Id index{ctx.OpShiftRightLogical(ctx.U32[1], offset, shift_id)};
|
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);
|
||||||
|
return ctx.OpLoad(ctx.U32[1], pointer);
|
||||||
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);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Id EmitLoadSharedU64(EmitContext& ctx, Id offset) {
|
Id EmitLoadSharedU64(EmitContext& ctx, Id offset) {
|
||||||
const Id shift_id{ctx.ConstU32(3U)};
|
const Id shift_id{ctx.ConstU32(3U)};
|
||||||
const Id index{ctx.OpShiftRightLogical(ctx.U32[1], offset, shift_id)};
|
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);
|
||||||
|
return ctx.OpLoad(ctx.U64, pointer);
|
||||||
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);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitWriteSharedU16(EmitContext& ctx, Id offset, Id value) {
|
void EmitWriteSharedU16(EmitContext& ctx, Id offset, Id value) {
|
||||||
const Id shift{ctx.ConstU32(1U)};
|
const Id shift{ctx.ConstU32(1U)};
|
||||||
const Id index{ctx.OpShiftRightLogical(ctx.U32[1], offset, shift)};
|
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)};
|
const Id pointer = ctx.EmitSharedMemoryAccess(ctx.shared_u16, ctx.shared_memory_u16, index);
|
||||||
|
ctx.OpStore(pointer, value);
|
||||||
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};
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitWriteSharedU32(EmitContext& ctx, Id offset, Id value) {
|
void EmitWriteSharedU32(EmitContext& ctx, Id offset, Id value) {
|
||||||
const Id shift{ctx.ConstU32(2U)};
|
const Id shift{ctx.ConstU32(2U)};
|
||||||
const Id index{ctx.OpShiftRightLogical(ctx.U32[1], offset, shift)};
|
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)};
|
const Id pointer = ctx.EmitSharedMemoryAccess(ctx.shared_u32, ctx.shared_memory_u32, index);
|
||||||
|
ctx.OpStore(pointer, value);
|
||||||
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};
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitWriteSharedU64(EmitContext& ctx, Id offset, Id value) {
|
void EmitWriteSharedU64(EmitContext& ctx, Id offset, Id value) {
|
||||||
const Id shift{ctx.ConstU32(3U)};
|
const Id shift{ctx.ConstU32(3U)};
|
||||||
const Id index{ctx.OpShiftRightLogical(ctx.U32[1], offset, shift)};
|
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)};
|
const Id pointer = ctx.EmitSharedMemoryAccess(ctx.shared_u64, ctx.shared_memory_u64, index);
|
||||||
|
ctx.OpStore(pointer, value);
|
||||||
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};
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Shader::Backend::SPIRV
|
} // namespace Shader::Backend::SPIRV
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user