mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-31 23:19:44 +08:00
20dcb791dc
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
169 lines
4.8 KiB
C#
169 lines
4.8 KiB
C#
// Copyright (C) 2026 SharpEmu Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
using System.Collections.Generic;
|
|
using SharpEmu.ShaderCompiler;
|
|
using SharpEmu.ShaderCompiler.Ir;
|
|
using Xunit;
|
|
|
|
namespace SharpEmu.ShaderCompiler.Tests;
|
|
|
|
public sealed class Gen5ScalarSsaTests
|
|
{
|
|
private static Gen5ShaderInstruction Mov(uint pc, uint destination, uint literal) =>
|
|
new(
|
|
pc,
|
|
Gen5ShaderEncoding.Sop1,
|
|
"SMov",
|
|
[0u],
|
|
[new Gen5Operand(Gen5OperandKind.LiteralConstant, literal)],
|
|
[Gen5Operand.Scalar(destination)],
|
|
null);
|
|
|
|
private static Gen5ShaderInstruction Nop(uint pc) =>
|
|
new(pc, Gen5ShaderEncoding.Sop1, "SNop", [0u], [], [], null);
|
|
|
|
private static Gen5ShaderInstruction Branch(uint pc, string opcode, short wordOffset) =>
|
|
new(
|
|
pc,
|
|
Gen5ShaderEncoding.Sopp,
|
|
opcode,
|
|
[unchecked((uint)(ushort)wordOffset)],
|
|
[],
|
|
[],
|
|
null);
|
|
|
|
[Fact]
|
|
public void StraightLineMovResolvesToAConstant()
|
|
{
|
|
List<Gen5ShaderInstruction> program =
|
|
[
|
|
Mov(0, destination: 8, literal: 0x1234),
|
|
Nop(4),
|
|
];
|
|
|
|
var ssa = Gen5ScalarSsa.Build(program, userData: []);
|
|
var value = ssa.GetScalarAt(4, 8);
|
|
|
|
Assert.Equal(IrScalarState.Constant, value.State);
|
|
Assert.Equal(0x1234u, value.Constant);
|
|
}
|
|
|
|
[Fact]
|
|
public void UserDataSeedsTheEntryState()
|
|
{
|
|
List<Gen5ShaderInstruction> program = [Nop(0)];
|
|
|
|
var ssa = Gen5ScalarSsa.Build(program, userData: [0x40F55240, 0x00000004]);
|
|
|
|
Assert.Equal(IrScalarState.Constant, ssa.GetScalarAt(0, 0).State);
|
|
Assert.Equal(0x40F55240u, ssa.GetScalarAt(0, 0).Constant);
|
|
Assert.Equal(0x00000004u, ssa.GetScalarAt(0, 1).Constant);
|
|
}
|
|
|
|
[Fact]
|
|
public void ConflictingValuesFromTwoPathsBecomeMerged()
|
|
{
|
|
// if (cc) s8 = 0xAAAA else s8 = 0xBBBB; use s8
|
|
List<Gen5ShaderInstruction> program =
|
|
[
|
|
Branch(0, "SCbranchScc0", 2),
|
|
Mov(4, destination: 8, literal: 0xAAAA),
|
|
Branch(8, "SBranch", 1),
|
|
Mov(12, destination: 8, literal: 0xBBBB),
|
|
Nop(16),
|
|
];
|
|
|
|
var ssa = Gen5ScalarSsa.Build(program, userData: []);
|
|
|
|
Assert.True(ssa.Graph.HasControlFlow);
|
|
Assert.Equal(IrScalarState.Merged, ssa.GetScalarAt(16, 8).State);
|
|
}
|
|
|
|
[Fact]
|
|
public void SameValueOnBothPathsStaysResolved()
|
|
{
|
|
List<Gen5ShaderInstruction> program =
|
|
[
|
|
Branch(0, "SCbranchScc0", 2),
|
|
Mov(4, destination: 8, literal: 0xCAFE),
|
|
Branch(8, "SBranch", 1),
|
|
Mov(12, destination: 8, literal: 0xCAFE),
|
|
Nop(16),
|
|
];
|
|
|
|
var ssa = Gen5ScalarSsa.Build(program, userData: []);
|
|
var value = ssa.GetScalarAt(16, 8);
|
|
|
|
Assert.Equal(IrScalarState.Constant, value.State);
|
|
Assert.Equal(0xCAFEu, value.Constant);
|
|
}
|
|
|
|
[Fact]
|
|
public void RegisterWrittenOnOnlyOnePathIsMergedAtTheJoin()
|
|
{
|
|
List<Gen5ShaderInstruction> program =
|
|
[
|
|
Mov(0, destination: 8, literal: 0x1111),
|
|
Branch(4, "SCbranchScc0", 1),
|
|
Mov(8, destination: 8, literal: 0x2222),
|
|
Nop(12),
|
|
];
|
|
|
|
var ssa = Gen5ScalarSsa.Build(program, userData: []);
|
|
|
|
Assert.Equal(IrScalarState.Merged, ssa.GetScalarAt(12, 8).State);
|
|
}
|
|
|
|
[Fact]
|
|
public void ValueBeforeTheBranchIsStillResolved()
|
|
{
|
|
List<Gen5ShaderInstruction> program =
|
|
[
|
|
Mov(0, destination: 8, literal: 0x1111),
|
|
Branch(4, "SCbranchScc0", 1),
|
|
Mov(8, destination: 8, literal: 0x2222),
|
|
Nop(12),
|
|
];
|
|
|
|
var ssa = Gen5ScalarSsa.Build(program, userData: []);
|
|
var value = ssa.GetScalarAt(4, 8);
|
|
|
|
Assert.Equal(IrScalarState.Constant, value.State);
|
|
Assert.Equal(0x1111u, value.Constant);
|
|
}
|
|
|
|
[Fact]
|
|
public void MergeJoinIsReportedForDivergentBlocks()
|
|
{
|
|
List<Gen5ShaderInstruction> program =
|
|
[
|
|
Branch(0, "SCbranchScc0", 1),
|
|
Mov(4, destination: 8, literal: 1),
|
|
Nop(8),
|
|
];
|
|
|
|
var ssa = Gen5ScalarSsa.Build(program, userData: []);
|
|
|
|
Assert.True(ssa.IsInsideDivergentMerge(8));
|
|
Assert.False(ssa.IsInsideDivergentMerge(0));
|
|
}
|
|
|
|
[Fact]
|
|
public void LoopDoesNotHangTheFixpoint()
|
|
{
|
|
List<Gen5ShaderInstruction> program =
|
|
[
|
|
Mov(0, destination: 8, literal: 1),
|
|
Nop(4),
|
|
Branch(8, "SCbranchScc1", -3),
|
|
Nop(12),
|
|
];
|
|
|
|
var ssa = Gen5ScalarSsa.Build(program, userData: []);
|
|
|
|
Assert.NotEmpty(ssa.Graph.LoopHeaders);
|
|
Assert.Equal(IrScalarState.Constant, ssa.GetScalarAt(12, 8).State);
|
|
}
|
|
}
|