mirror of
https://github.com/patchzyy/wiicompiled
synced 2026-09-10 17:16:47 -04:00
266 lines
9.4 KiB
C#
266 lines
9.4 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Translator.Core.Analysis.Ssa;
|
|
using Translator.Core.Analysis.Representation;
|
|
using Translator.Core.CodeGen;
|
|
using Translator.Core.Ir;
|
|
using Translator.Core.Representation;
|
|
using Xunit;
|
|
|
|
namespace Translator.Tests;
|
|
|
|
public class ScalarPairedBoundaryCodeGenTests
|
|
{
|
|
[Theory]
|
|
[InlineData("add", true)]
|
|
[InlineData("sub", true)]
|
|
[InlineData("mul", true)]
|
|
[InlineData("fdiv", true)]
|
|
[InlineData("fcmp", true)]
|
|
[InlineData("fabs", false)]
|
|
[InlineData("fneg", false)]
|
|
[InlineData("frsp", false)]
|
|
[InlineData("fsqrt", false)]
|
|
[InlineData("fctiw", false)]
|
|
[InlineData("fctiwz", false)]
|
|
public void NormalizesPairedOperandsForEveryScalarFloatOperator(
|
|
string operation,
|
|
bool consumesRightOperand)
|
|
{
|
|
var code = Emit(
|
|
new IrCall("f2", "PPC_PsSum0", new[]
|
|
{
|
|
IrValue.Register("f5"), IrValue.Register("f6"), IrValue.Register("f7")
|
|
}),
|
|
new IrCall("f3", "PPC_PsSum0", new[]
|
|
{
|
|
IrValue.Register("f8"), IrValue.Register("f9"), IrValue.Register("f10")
|
|
}),
|
|
new IrBinary(
|
|
"f4",
|
|
IrValue.Register("f2"),
|
|
consumesRightOperand ? IrValue.Register("f3") : IrValue.Imm(0),
|
|
operation));
|
|
|
|
Assert.Contains("PPC_PsToScalarInline(f2.d)", code, StringComparison.Ordinal);
|
|
if (consumesRightOperand)
|
|
Assert.Contains("PPC_PsToScalarInline(f3.d)", code, StringComparison.Ordinal);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("PPC_PsFromScalar", 1)]
|
|
[InlineData("PPC_Fadds", 2)]
|
|
[InlineData("PPC_Fsubs", 2)]
|
|
[InlineData("PPC_Fmuls", 2)]
|
|
[InlineData("PPC_Fdivs", 2)]
|
|
[InlineData("PPC_Fsqrt", 1)]
|
|
[InlineData("PPC_Frsqrte", 1)]
|
|
[InlineData("PPC_Fmadd", 3)]
|
|
[InlineData("PPC_Fmsub", 3)]
|
|
[InlineData("PPC_Fnmadd", 3)]
|
|
[InlineData("PPC_Fnmsub", 3)]
|
|
public void NormalizesEveryPairedScalarHelperOperand(string target, int argumentCount)
|
|
{
|
|
var arguments = Enumerable.Repeat(IrValue.Register("f1"), argumentCount).ToArray();
|
|
var code = Emit(
|
|
new IrCall("f1", "PPC_PsMul", new[]
|
|
{
|
|
IrValue.Register("f5"), IrValue.Register("f6")
|
|
}),
|
|
new IrCall("f2", target, arguments));
|
|
|
|
const string conversion = "PPC_PsToScalarInline(f1.d)";
|
|
Assert.Equal(
|
|
argumentCount,
|
|
code.Split(conversion, StringSplitOptions.None).Length - 1);
|
|
}
|
|
|
|
[Fact]
|
|
public void PreservesScalarDoubleInputForFrsqrte()
|
|
{
|
|
var code = Emit(new IrCall("f2", "PPC_Frsqrte", new[] { IrValue.Register("f1") }));
|
|
|
|
Assert.Contains("PPC_Frsqrte(f1.d)", code, StringComparison.Ordinal);
|
|
Assert.DoesNotContain("PPC_PsFromScalarInline(f1.d)", code, StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public void PreservesPairedArgumentsAtUnprovenIndirectCallBoundary()
|
|
{
|
|
var code = Emit(
|
|
new IrCall("f1", "PPC_PsMul", new[]
|
|
{
|
|
IrValue.Register("f5"), IrValue.Register("f6")
|
|
}),
|
|
new IrIndirectCall(string.Empty, IrValue.Register("ctr"), new[]
|
|
{
|
|
IrValue.Register("r3"), IrValue.Register("f1")
|
|
}),
|
|
new IrCall("f2", "PPC_PsNeg", new[] { IrValue.Register("f1") }));
|
|
var call = code.IndexOf("InvokeIndirectCpu(ctr, ctx);", StringComparison.Ordinal);
|
|
|
|
Assert.True(call >= 0, code);
|
|
Assert.DoesNotContain(
|
|
"f1.d = PPC_PsToScalarInline(f1.d);",
|
|
code[..call],
|
|
StringComparison.Ordinal);
|
|
Assert.Contains(
|
|
"PPC_PsNegInline(PPC_PsFromScalarInline(f1.d))",
|
|
code[call..],
|
|
StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public void PreservesUntouchedPairedPayloadAcrossIndirectCall()
|
|
{
|
|
var code = Emit(
|
|
new IrCall("f2", "PPC_PsMul", new[]
|
|
{
|
|
IrValue.Register("f5"), IrValue.Register("f6")
|
|
}),
|
|
new IrIndirectCall(string.Empty, IrValue.Register("ctr"), new[]
|
|
{
|
|
IrValue.Register("r3")
|
|
}),
|
|
new IrCall("f3", "PPC_PsNeg", new[] { IrValue.Register("f2") }));
|
|
var call = code.IndexOf("InvokeIndirectCpu(ctr, ctx);", StringComparison.Ordinal);
|
|
|
|
Assert.True(call >= 0, code);
|
|
Assert.Contains(
|
|
"PPC_PsNegInline(f2.d)",
|
|
code[call..],
|
|
StringComparison.Ordinal);
|
|
Assert.DoesNotContain(
|
|
"PPC_PsFromScalarInline(f2.d)",
|
|
code[call..],
|
|
StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public void UnprovenIndirectF1RemainsConventionalScalarForMixedConsumers()
|
|
{
|
|
var code = Emit(
|
|
new IrIndirectCall(string.Empty, IrValue.Register("ctr"), new[]
|
|
{
|
|
IrValue.Register("r3")
|
|
}),
|
|
new IrSetCrField(0, IrValue.Register("f1"), IrValue.Register("f14"), false),
|
|
new IrCall("f2", "PPC_PsNeg", new[] { IrValue.Register("f1") }));
|
|
var call = code.IndexOf("InvokeIndirectCpu(ctr, ctx);", StringComparison.Ordinal);
|
|
|
|
Assert.True(call >= 0, code);
|
|
Assert.Contains(
|
|
"SetCRFloatResident(cr, 0, f1.d, f14.d);",
|
|
code[call..],
|
|
StringComparison.Ordinal);
|
|
Assert.Contains(
|
|
"PPC_PsNegInline(PPC_PsFromScalarInline(f1.d))",
|
|
code[call..],
|
|
StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public void UnprovenDirectF1RemainsConventionalScalarForMixedConsumers()
|
|
{
|
|
var code = Emit(
|
|
new IrCall(string.Empty, "func_80002000", Array.Empty<IrValue>()),
|
|
new IrSetCrField(0, IrValue.Register("f1"), IrValue.Register("f14"), false),
|
|
new IrCall("f2", "PPC_PsNeg", new[] { IrValue.Register("f1") }));
|
|
var call = code.IndexOf("InvokeDirectCpu<0x80002000u>(ctx);", StringComparison.Ordinal);
|
|
|
|
Assert.True(call >= 0, code);
|
|
Assert.Contains(
|
|
"SetCRFloatResident(cr, 0, f1.d, f14.d);",
|
|
code[call..],
|
|
StringComparison.Ordinal);
|
|
Assert.Contains(
|
|
"PPC_PsNegInline(PPC_PsFromScalarInline(f1.d))",
|
|
code[call..],
|
|
StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public void PacksImmediateForPairedConsumer()
|
|
{
|
|
var code = Emit(new IrCall("f2", "PPC_PsNeg", new[] { IrValue.Imm(7) }));
|
|
|
|
Assert.Contains(
|
|
"PPC_PsNegInline(PPC_PsFromScalarInline(7))",
|
|
code,
|
|
StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public void PairedMoveCopiesBothArchitecturalLanes()
|
|
{
|
|
var code = Emit(new IrCall("f2", "PPC_PsMr", new[] { IrValue.Register("f7") }));
|
|
|
|
Assert.Contains(
|
|
"PpcSetPairedFprInline(f2, f7.d);",
|
|
code,
|
|
StringComparison.Ordinal);
|
|
Assert.DoesNotContain("f2.d = f7.d", code, StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public void EmitsArchitecturallyAccurateScalarFloatHelpers()
|
|
{
|
|
var code = Emit(
|
|
new IrCall("f2", "PPC_Fmuls", new[]
|
|
{
|
|
IrValue.Register("f3"), IrValue.Register("f4")
|
|
}),
|
|
new IrCall("f5", "PPC_Fmadd", new[]
|
|
{
|
|
IrValue.Register("f6"), IrValue.Register("f7"), IrValue.Register("f8")
|
|
}),
|
|
new IrCall("f9", "PPC_Fmsub", new[]
|
|
{
|
|
IrValue.Register("f10"), IrValue.Register("f11"), IrValue.Register("f12")
|
|
}),
|
|
new IrCall("f13", "PPC_Fnmadd", new[]
|
|
{
|
|
IrValue.Register("f14"), IrValue.Register("f15"), IrValue.Register("f16")
|
|
}),
|
|
new IrCall("f17", "PPC_Fnmsub", new[]
|
|
{
|
|
IrValue.Register("f18"), IrValue.Register("f19"), IrValue.Register("f20")
|
|
}),
|
|
new IrBinary(
|
|
"f21",
|
|
IrValue.Register("f22"),
|
|
IrValue.Imm(0),
|
|
"frsp"));
|
|
|
|
Assert.Contains("PpcFmulsInline(", code, StringComparison.Ordinal);
|
|
Assert.Contains("PpcFmaddInline(", code, StringComparison.Ordinal);
|
|
Assert.Contains("PpcFmsubInline(", code, StringComparison.Ordinal);
|
|
Assert.Contains("PpcFnmaddInline(", code, StringComparison.Ordinal);
|
|
Assert.Contains("PpcFnmsubInline(", code, StringComparison.Ordinal);
|
|
Assert.Contains("PpcForceSingleValueInline(", code, StringComparison.Ordinal);
|
|
}
|
|
|
|
private static string Emit(params IrInstruction[] instructions)
|
|
{
|
|
var body = instructions.Concat(new[] { new IrReturn(null) }).ToArray();
|
|
var function = new IrFunction(
|
|
"scalar_paired_boundary",
|
|
"entry",
|
|
new[] { new IrBasicBlock("entry", body) });
|
|
var types = Enumerable.Range(0, 32).ToDictionary(
|
|
index => $"f{index}",
|
|
_ => (ValueRepresentation)ValueRepresentation.Float64,
|
|
StringComparer.OrdinalIgnoreCase);
|
|
types["r3"] = ValueRepresentation.UInt32;
|
|
types["ctr"] = ValueRepresentation.UInt32;
|
|
return new CxxLinearCodeGenerator().Emit(
|
|
0x80006200,
|
|
new SsaTransformer().Convert(function),
|
|
new FunctionAbiClassification(
|
|
"scalar_paired_boundary",
|
|
ValueRepresentation.Void),
|
|
new RepresentationEnvironment(types));
|
|
}
|
|
}
|