shader: elide bitcasts between identical physical types

This commit is contained in:
nmzik 2026-10-13 00:53:23 +02:00
parent 7e884f55e0
commit 2f88a0a3b2
2 changed files with 23 additions and 6 deletions

View File

@ -5,15 +5,17 @@
#include <bit>
namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter {
uint32_t EmitIdentity(ValueEmitContext& ctx, uint32_t value);
// Operand signatures distinguish SPIR-V IDs (uint32_t), raw literals/values
// (IR::Value), and instructions whose metadata or operand loads must stay lazy.
#define EMIT_NATIVE(name, opcode, type, ...) \
inline constexpr auto Emit##name = EmitNative<spv::opcode, IR::Type::type, __VA_ARGS__>;
EMIT_NATIVE(BitCastU16F16, OpBitcast, U32, uint32_t)
inline constexpr auto EmitBitCastU16F16 = EmitIdentity;
inline constexpr auto EmitBitCastF16U16 = EmitBitCastU16F16;
inline constexpr auto EmitConvertU32U16 = EmitBitCastU16F16;
inline constexpr auto EmitConvertU32U8 = EmitBitCastU16F16;
inline constexpr auto EmitBitCastU32F32 = EmitBitCastU16F16;
EMIT_NATIVE(BitCastU32F32, OpBitcast, U32, uint32_t)
EMIT_NATIVE(BitCastF32U32, OpBitcast, F32, uint32_t)
EMIT_NATIVE(BitCastU64F64, OpBitcast, U64, uint32_t)
EMIT_NATIVE(BitCastF64U64, OpBitcast, F64, uint32_t)
@ -207,7 +209,6 @@ uint32_t EmitFPSin(EmitterState& state, uint32_t arg0);
uint32_t EmitFPCos(EmitterState& state, uint32_t arg0);
#undef EMIT_NATIVE
uint32_t EmitIdentity(ValueEmitContext& ctx, uint32_t value);
void EmitVoid(ValueEmitContext&);
inline constexpr auto EmitReference = EmitVoid;
inline constexpr auto EmitReferenceU32 = EmitVoid;

View File

@ -1,3 +1,5 @@
#define SPV_ENABLE_UTILITY_CODE
#include "common/assert.h"
#include "common/emulatorConfig.h"
#include "common/file.h"
@ -1610,7 +1612,21 @@ void CheckRectListShaders() {
ValidateSpirv(name, position_only.evaluation);
}
void CheckSpirvText(const TestCase &test, const std::vector<u32> &spirv) {
void CheckSpirvEmission(const TestCase &test, const std::vector<u32> &spirv) {
std::vector<u32> result_types(spirv[3]);
for (size_t offset = 5; offset < spirv.size();) {
const auto words = std::span<const u32>(spirv).subspan(offset, spirv[offset] >> 16u);
const auto opcode = static_cast<spv::Op>(words[0] & 0xffffu);
bool has_result = false, has_type = false;
spv::HasResultAndType(opcode, &has_result, &has_type);
if (has_result && has_type) result_types[words[2]] = words[1];
if (opcode == spv::OpBitcast) {
Require(test.name, "bitcast changes representation",
words[1] != result_types[words[3]],
"emitted a redundant bitcast between identical SPIR-V types");
}
offset += words.size();
}
if (test.required_spirv.empty() && test.forbidden_spirv.empty()) {
return;
}
@ -1737,7 +1753,7 @@ CompiledShader CompileCase(const TestCase &test, u32 host_subgroup_size = 64) {
Require(test.name, "SPIR-V emit", !result.spirv.empty(),
"recompiler returned empty SPIR-V");
ValidateSpirv(test.name, result.spirv);
CheckSpirvText(test, result.spirv);
CheckSpirvEmission(test, result.spirv);
if (test.expected_buffer_resources) {
std::vector<u32> resources;
for (u32 i = 0; i < result.program.info.buffers.size(); ++i) {
@ -36184,7 +36200,7 @@ void CheckIndirectBufferStore(VulkanHarness &vulkan) {
} else {
test.required_spirv = {"OpSwitch"};
}
CheckSpirvText(test, compiled.spirv);
CheckSpirvEmission(test, compiled.spirv);
for (const bool swapped : {false, true}) {
if (layout != Layout::Shared && layout != Layout::IndexedNullChild && swapped) continue;