using ARMeilleure.CodeGen.X86; using ARMeilleure.IntermediateRepresentation; using ARMeilleure.State; using ARMeilleure.Translation; using System; using System.Runtime.InteropServices; using static ARMeilleure.IntermediateRepresentation.Operand.Factory; namespace ARMeilleure.Translation { public static class TranslatorTestMethods { public delegate int FpFlagsPInvokeTest(IntPtr managedMethod); private static bool SetPlatformFtz(EmitterContext context, bool ftz) { if (Optimizations.UseSse2) { Operand mxcsr = context.AddIntrinsicInt(Intrinsic.X86Stmxcsr); if (ftz) { mxcsr = context.BitwiseOr(mxcsr, Const((int)(Mxcsr.Ftz | Mxcsr.Um | Mxcsr.Dm))); } else { mxcsr = context.BitwiseAnd(mxcsr, Const(~(int)Mxcsr.Ftz)); } context.AddIntrinsicNoRet(Intrinsic.X86Ldmxcsr, mxcsr); return true; } else if (Optimizations.UseAdvSimd) { Operand fpcr = context.AddIntrinsicInt(Intrinsic.Arm64MrsFpcr); if (ftz) { fpcr = context.BitwiseOr(fpcr, Const((int)FPCR.Fz)); } else { fpcr = context.BitwiseAnd(fpcr, Const(~(int)FPCR.Fz)); } context.AddIntrinsicNoRet(Intrinsic.Arm64MsrFpcr, fpcr); return true; } else { return false; } } private static Operand FpBitsToInt(EmitterContext context, Operand fp) { Operand vec = context.VectorInsert(context.VectorZero(), fp, 0); return context.VectorExtract(OperandType.I32, vec, 0); } public static FpFlagsPInvokeTest GenerateFpFlagsPInvokeTest() { EmitterContext context = new EmitterContext(); Operand methodAddress = context.Copy(context.LoadArgument(OperandType.I64, 0)); // Verify that default dotnet fp state does not flush to zero. // This is required for SoftFloat to function. // Denormal + zero != 0 Operand denormal = ConstF(BitConverter.Int32BitsToSingle(1)); // 1.40129846432e-45 Operand zeroF = ConstF(0f); Operand zero = Const(0); Operand result = context.Add(zeroF, denormal); // Must not be zero. Operand correct1Label = Label(); context.BranchIfFalse(correct1Label, context.ICompareEqual(FpBitsToInt(context, result), zero)); context.Return(Const(1)); context.MarkLabel(correct1Label); // Set flush to zero flag. If unsupported by the backend, just return true. if (!SetPlatformFtz(context, true)) { context.Return(Const(0)); } // Denormal + zero == 0 Operand resultFz = context.Add(zeroF, denormal); // Must equal zero. Operand correct2Label = Label(); context.BranchIfTrue(correct2Label, context.ICompareEqual(FpBitsToInt(context, resultFz), zero)); SetPlatformFtz(context, false); context.Return(Const(2)); context.MarkLabel(correct2Label); // Call a managed method. This method should not change Fz state. context.Call(methodAddress, OperandType.None); // Denormal + zero == 0 Operand resultFz2 = context.Add(zeroF, denormal); // Must equal zero. Operand correct3Label = Label(); context.BranchIfTrue(correct3Label, context.ICompareEqual(FpBitsToInt(context, resultFz2), zero)); SetPlatformFtz(context, false); context.Return(Const(3)); context.MarkLabel(correct3Label); // Success. SetPlatformFtz(context, false); context.Return(Const(0)); // Compile and return the function. ControlFlowGraph cfg = context.GetControlFlowGraph(); OperandType[] argTypes = new OperandType[] { OperandType.I64 }; return Compiler.Compile(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map(); } } }