mirror of
https://github.com/par274/sharpemu.git
synced 2026-08-31 13:51:04 +08:00
VMovrelsB32 opcode fix (#825)
This commit is contained in:
@@ -101,6 +101,12 @@ public static partial class Gen5SpirvTranslator
|
||||
return false;
|
||||
}
|
||||
|
||||
if (instruction.Opcode is "VMovrelsB32" or "VMovreldB32" or
|
||||
"VMovrelsdB32" or "VMovrelsd2B32")
|
||||
{
|
||||
return TryEmitMoveRelative(instruction, destination, out error);
|
||||
}
|
||||
|
||||
uint result;
|
||||
switch (instruction.Opcode)
|
||||
{
|
||||
@@ -1013,6 +1019,76 @@ public static partial class Gen5SpirvTranslator
|
||||
return true;
|
||||
}
|
||||
|
||||
// V_MOVREL*_B32: register-relative moves. M0 is added at run time to the
|
||||
// source and/or destination register number encoded in the instruction,
|
||||
// which is how shader compilers implement a dynamically indexed array
|
||||
// that stayed in registers instead of being spilled to memory. Astro Bot
|
||||
// ships pixel shaders that index a small register-resident table this
|
||||
// way; without this the whole shader fails to translate.
|
||||
//
|
||||
// V_MOVRELS_B32 vdst = vgpr[src0 + M0]
|
||||
// V_MOVRELD_B32 vgpr[vdst + M0] = src0
|
||||
// V_MOVRELSD_B32 vgpr[vdst + M0] = vgpr[src0 + M0]
|
||||
// V_MOVRELSD_2_B32 vgpr[vdst + M0[25:16]] = vgpr[src0 + M0[9:0]]
|
||||
//
|
||||
// The relative forms address the VGPR file relative to the wave's own
|
||||
// allocation base, which is exactly what the private register array
|
||||
// models, so the encoded number and M0 simply add.
|
||||
private bool TryEmitMoveRelative(
|
||||
Gen5ShaderInstruction instruction,
|
||||
uint destination,
|
||||
out string error)
|
||||
{
|
||||
error = string.Empty;
|
||||
if (instruction.Sources.Count == 0)
|
||||
{
|
||||
error = $"missing source for {instruction.Opcode}";
|
||||
return false;
|
||||
}
|
||||
|
||||
var m0 = LoadS(M0ScalarRegister);
|
||||
uint sourceOffset;
|
||||
uint destinationOffset;
|
||||
if (instruction.Opcode == "VMovrelsd2B32")
|
||||
{
|
||||
sourceOffset = BitwiseAnd(m0, UInt(0x3FF));
|
||||
destinationOffset = BitwiseAnd(ShiftRightLogical(m0, UInt(16)), UInt(0x3FF));
|
||||
}
|
||||
else
|
||||
{
|
||||
sourceOffset = m0;
|
||||
destinationOffset = m0;
|
||||
}
|
||||
|
||||
uint value;
|
||||
if (instruction.Opcode == "VMovreldB32")
|
||||
{
|
||||
// Only the destination is relative here; src0 is an ordinary
|
||||
// operand and may be an SGPR or an inline/literal constant.
|
||||
value = GetRawSource(instruction, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
var source = instruction.Sources[0];
|
||||
if (source.Kind != Gen5OperandKind.VectorRegister)
|
||||
{
|
||||
error = $"{instruction.Opcode} source must be a vector register";
|
||||
return false;
|
||||
}
|
||||
|
||||
value = LoadVDynamic(IAdd(UInt(source.Value), sourceOffset));
|
||||
}
|
||||
|
||||
if (instruction.Opcode == "VMovrelsB32")
|
||||
{
|
||||
StoreV(destination, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
StoreVDynamic(IAdd(UInt(destination), destinationOffset), value);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Packed f16 (VOP3P) arithmetic. Each source register holds two f16 values,
|
||||
// one per result lane. Every f16<->f32 conversion is done with the explicit
|
||||
// integer sequences below (EmitHalfToFloat / EmitFloatToHalf) instead of
|
||||
|
||||
@@ -176,6 +176,11 @@ public static partial class Gen5SpirvTranslator
|
||||
private const uint ImageDescriptorDwords = 8;
|
||||
private const uint SamplerDescriptorDwords = 4;
|
||||
private const int ScalarRegisterCount = 128;
|
||||
|
||||
// M0. Used as the runtime index added to the register numbers encoded in
|
||||
// the V_MOVREL* instructions, and as the LDS/GDS base elsewhere.
|
||||
private const uint M0ScalarRegister = 124;
|
||||
|
||||
private const long InitialScalarDefinition = -1;
|
||||
private const long ConflictingScalarDefinition = -2;
|
||||
private const long UnreachableScalarDefinition = -3;
|
||||
@@ -5090,6 +5095,33 @@ public static partial class Gen5SpirvTranslator
|
||||
_vectorRegisters,
|
||||
UInt(register));
|
||||
|
||||
// The V_MOVREL* opcodes address the VGPR file with a register number that
|
||||
// is only known at run time (encoded number + M0), so the access chain
|
||||
// takes a computed index instead of a constant. The index is masked to
|
||||
// the array bounds: SPIR-V leaves an out-of-range Private access chain
|
||||
// undefined, and a mask costs nothing next to the surrounding load.
|
||||
private uint DynamicVectorPointer(uint registerIndex) =>
|
||||
_module.AddInstruction(
|
||||
SpirvOp.AccessChain,
|
||||
_privateUintPointer,
|
||||
_vectorRegisters,
|
||||
BitwiseAnd(registerIndex, UInt(VectorRegisterCount - 1)));
|
||||
|
||||
private uint LoadVDynamic(uint registerIndex) =>
|
||||
Load(_uintType, DynamicVectorPointer(registerIndex));
|
||||
|
||||
private void StoreVDynamic(uint registerIndex, uint value)
|
||||
{
|
||||
var pointer = DynamicVectorPointer(registerIndex);
|
||||
value = _module.AddInstruction(
|
||||
SpirvOp.Select,
|
||||
_uintType,
|
||||
Load(_boolType, _exec),
|
||||
value,
|
||||
Load(_uintType, pointer));
|
||||
Store(pointer, value);
|
||||
}
|
||||
|
||||
private uint PackedHalfPointer(uint register) =>
|
||||
_module.AddInstruction(
|
||||
SpirvOp.AccessChain,
|
||||
|
||||
@@ -947,6 +947,7 @@ public static class Gen5ShaderTranslator
|
||||
0x42 => "VMovreldB32",
|
||||
0x43 => "VMovrelsB32",
|
||||
0x44 => "VMovrelsdB32",
|
||||
0x48 => "VMovrelsd2B32",
|
||||
_ => string.Empty,
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user