using System; using System.Collections.Generic; using Translator.Core.Analysis.Representation; using Translator.Core.Analysis.Ssa; using Translator.Core.CodeGen; using Translator.Core.Ir; using Translator.Core.Representation; using Xunit; namespace Translator.Tests; /// /// Coverage for the descriptor-driven arithmetic inline emitters, going through the public /// C++ generator (not the private emitter directly) so lookup and output are tested together. /// public sealed class InlineArithmeticTableCodeGenTests { public static IEnumerable PairedCases() { yield return Case("PPC_PsMerge00", "PPC_PsMerge00Inline"); yield return Case("PPC_PsMerge01", "PPC_PsMerge01Inline"); yield return Case("PPC_PsMerge10", "PPC_PsMerge10Inline"); yield return Case("PPC_PsMerge11", "PPC_PsMerge11Inline"); yield return Case("PPC_PsAdd", "PPC_PsAddInline"); yield return Case("PPC_PsAddNoNi", "PPC_PsAddNoNiInline"); yield return Case("PPC_PsSub", "PPC_PsSubInline"); yield return Case("PPC_PsSubNoNi", "PPC_PsSubNoNiInline"); yield return Case("PPC_PsDiv", "PPC_PsDivInline"); yield return Case("PPC_PsMul", "PPC_PsMulInline"); yield return Case("PPC_PsMulNoNi", "PPC_PsMulNoNiInline"); yield return Case("PPC_PsNeg", "PPC_PsNegInline"); yield return Case("PPC_PsAbs", "PPC_PsAbsInline"); yield return Case("PPC_PsMuls0", "PPC_PsMuls0Inline"); yield return Case("PPC_PsMuls1", "PPC_PsMuls1Inline"); yield return Case("PPC_PsMadd", "PPC_PsMaddInline"); yield return Case("PPC_PsMaddNoNi", "PPC_PsMaddNoNiInline"); yield return Case("PPC_PsMsub", "PPC_PsMsubInline"); yield return Case("PPC_PsMsubNoNi", "PPC_PsMsubNoNiInline"); yield return Case("PPC_PsNmsub", "PPC_PsNmsubInline"); yield return Case("PPC_PsNmsubNoNi", "PPC_PsNmsubNoNiInline"); yield return Case("PPC_PsNmadd", "PPC_PsNmaddInline"); yield return Case("PPC_PsMadds0", "PPC_PsMadds0Inline"); yield return Case("PPC_PsMadds1", "PPC_PsMadds1Inline"); yield return Case("PPC_PsSum0", "PPC_PsSum0Inline"); yield return Case("PPC_PsSum1", "PPC_PsSum1Inline"); } public static IEnumerable ScalarCases() { yield return Case("PPC_Fadds", "PpcForceSingleValueInline"); yield return Case("PPC_FaddsNoNi", "static_cast"); yield return Case("PPC_Fsubs", "PpcForceSingleValueInline"); yield return Case("PPC_FsubsNoNi", "static_cast"); yield return Case("PPC_Fmuls", "PpcFmulsInline"); yield return Case("PPC_FmulsNoNi", "PpcFmulsNoNiInline"); yield return Case("PPC_Fdivs", "PpcForceSingleValueInline"); yield return Case("PPC_FdivsNoNi", "static_cast"); yield return Case("PPC_Fsqrt", "std::sqrt"); yield return Case("PPC_Fmadd", "PpcFmaddInline"); yield return Case("PPC_Fmsub", "PpcFmsubInline"); yield return Case("PPC_Fnmadd", "PpcFnmaddInline"); yield return Case("PPC_Fnmsub", "PpcFnmsubInline"); } [Theory] [MemberData(nameof(PairedCases))] public void PairedArithmeticTargetsUseExpectedInlineHelper(string target, string expected) { var code = EmitCall( target, "f0", IrValue.Register("f1"), IrValue.Register("f2"), IrValue.Register("f3")); Assert.Contains(expected + "(", code, StringComparison.Ordinal); } [Theory] [MemberData(nameof(ScalarCases))] public void ScalarArithmeticTargetsUseExpectedInlineHelper(string target, string expected) { var code = EmitCall( target, "f0", IrValue.Register("f1"), IrValue.Register("f2"), IrValue.Register("f3")); Assert.Contains(expected, code, StringComparison.Ordinal); } [Fact] public void PairedTablePreservesOperandOrderAndNoNiPacking() { var code = EmitCall( "PPC_PsMaddNoNi", "f0", IrValue.Imm(1), IrValue.Imm(2), IrValue.Imm(3)); Assert.Contains( "PPC_PsMaddNoNiInline(PPC_PsFromScalarNoNiInline(1), PPC_PsFromScalarNoNiInline(2), PPC_PsFromScalarNoNiInline(3))", code, StringComparison.Ordinal); } [Fact] public void ScalarTablePreservesFmaOperandOrder() { var code = EmitCall( "PPC_Fmsub", "f0", IrValue.Imm(1), IrValue.Imm(2), IrValue.Imm(3)); Assert.Contains("PpcFmsubInline(1, 2, 3)", code, StringComparison.Ordinal); } [Fact] public void TableTargetsRemainCaseInsensitive() { var code = EmitCall( "ppc_psaddnoni", "f0", IrValue.Imm(1), IrValue.Imm(2)); Assert.Contains("PPC_PsAddNoNiInline(", code, StringComparison.Ordinal); } [Fact] public void TableTargetsKeepMissingDestinationSuppression() { var code = EmitCall( "PPC_PsAdd", string.Empty, IrValue.Imm(1), IrValue.Imm(2)); Assert.DoesNotContain("PPC_PsAddInline(", code, StringComparison.Ordinal); Assert.DoesNotContain("PPC_PsAdd(", code, StringComparison.Ordinal); } [Fact] public void TableTargetsKeepMissingArgumentLiteralZeroFallback() { var code = EmitCall("PPC_PsAdd", "f0", IrValue.Imm(1)); Assert.Contains( "PPC_PsAddInline(PPC_PsFromScalarInline(1), 0)", code, StringComparison.Ordinal); } private static object[] Case(string target, string expected) => new object[] { target, expected }; private static string EmitCall(string target, string destination, params IrValue[] arguments) { var function = new IrFunction( "inline_table_probe", "entry", new[] { new IrBasicBlock( "entry", new IrInstruction[] { new IrCall(destination, target, arguments), new IrReturn(null) }) }); var types = new RepresentationEnvironment(new Dictionary { ["f0"] = ValueRepresentation.Float64, ["f1"] = ValueRepresentation.Float64, ["f2"] = ValueRepresentation.Float64, ["f3"] = ValueRepresentation.Float64 }); var signature = new FunctionAbiClassification("inline_table_probe", ValueRepresentation.Void); return new CxxLinearCodeGenerator().Emit( 0x80040000, new SsaTransformer().Convert(function), signature, types); } }