[ShaderCompiler] Fix VReadlane scalar destination field (#344)

V_READLANE uses the gfx10 VOP3A vdst byte even though its result is scalar. Decode bits 0-7 and cover the public LLVM s5 and s101 encodings so the VOP3B sdst field cannot be confused with this opcode again.
This commit is contained in:
Peter Bonanni
2026-07-17 19:41:07 -04:00
committed by GitHub
parent ecbb0db9be
commit bcb0ebd991
2 changed files with 55 additions and 2 deletions
@@ -1872,8 +1872,9 @@ public static class Gen5ShaderTranslator
destinations = [Gen5Operand.Vector(word & 0xFF)]; destinations = [Gen5Operand.Vector(word & 0xFF)];
if (opcode == "VReadlaneB32") if (opcode == "VReadlaneB32")
{ {
// The scalar destination lives in the low vdst byte (bits 0-7); // V_READLANE uses the VOP3A vdst byte even though the
// bits 8-14 are the VOP3B carry-out sdst, which readlane lacks. // destination register is scalar. Bits 8-14 are the
// distinct sdst field used by VOP3B encodings.
destinations = [Gen5Operand.Scalar(word & 0xFF)]; destinations = [Gen5Operand.Scalar(word & 0xFF)];
} }
var isVop3B = IsVop3BOpcode((word >> 16) & 0x3FF); var isVop3B = IsVop3BOpcode((word >> 16) & 0x3FF);
@@ -0,0 +1,52 @@
// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
using System.Buffers.Binary;
using SharpEmu.HLE;
using SharpEmu.ShaderCompiler;
using Xunit;
namespace SharpEmu.Libs.Tests.ShaderCompiler;
public sealed class Gen5ShaderTranslatorTests
{
private const ulong ProgramAddress = 0x1_0000_0000;
[Theory]
[InlineData(0xD7600005u, 5u)]
[InlineData(0xD7600065u, 101u)]
public void VReadlaneB32DecodesScalarDestinationFromVdstByte(
uint instructionWord,
uint expectedDestination)
{
var memory = new FakeCpuMemory(ProgramAddress, 0x100);
Span<byte> code = stackalloc byte[3 * sizeof(uint)];
BinaryPrimitives.WriteUInt32LittleEndian(code, instructionWord);
BinaryPrimitives.WriteUInt32LittleEndian(code[sizeof(uint)..], 0x02000501u);
BinaryPrimitives.WriteUInt32LittleEndian(code[(2 * sizeof(uint))..], 0xBF810000u);
Assert.True(memory.TryWrite(ProgramAddress, code));
var context = new CpuContext(memory, Generation.Gen5);
Assert.True(
Gen5ShaderTranslator.TryDecodeProgram(
context,
ProgramAddress,
out var program,
out var error),
error);
var instruction = Assert.Single(
program.Instructions,
static item => item.Opcode == "VReadlaneB32");
Assert.Equal(Gen5ShaderEncoding.Vop3, instruction.Encoding);
var destination = Assert.Single(instruction.Destinations);
Assert.Equal(Gen5OperandKind.ScalarRegister, destination.Kind);
Assert.Equal(expectedDestination, destination.Value);
Assert.Equal(Gen5OperandKind.VectorRegister, instruction.Sources[0].Kind);
Assert.Equal(1u, instruction.Sources[0].Value);
Assert.Equal(Gen5OperandKind.ScalarRegister, instruction.Sources[1].Kind);
Assert.Equal(2u, instruction.Sources[1].Value);
}
}