EnvironmentTests.cs 3.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. using ARMeilleure.Translation;
  2. using NUnit.Framework;
  3. using Ryujinx.Cpu.Jit;
  4. using Ryujinx.Tests.Memory;
  5. using System;
  6. using System.Runtime.CompilerServices;
  7. using System.Runtime.InteropServices;
  8. namespace Ryujinx.Tests.Cpu
  9. {
  10. internal class EnvironmentTests
  11. {
  12. #pragma warning disable IDE0052 // Remove unread private member
  13. private static Translator _translator;
  14. #pragma warning restore IDE0052
  15. private static void EnsureTranslator()
  16. {
  17. // Create a translator, as one is needed to register the signal handler or emit methods.
  18. _translator ??= new Translator(new JitMemoryAllocator(), new MockMemoryManager(), true);
  19. }
  20. [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
  21. private static float GetDenormal()
  22. {
  23. return BitConverter.Int32BitsToSingle(1);
  24. }
  25. [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
  26. private static float GetZero()
  27. {
  28. return BitConverter.Int32BitsToSingle(0);
  29. }
  30. /// <summary>
  31. /// This test ensures that managed methods do not reset floating point control flags.
  32. /// This is used to avoid changing control flags when running methods that don't require it, such as SVC calls, software memory...
  33. /// </summary>
  34. [Test]
  35. public void FpFlagsPInvoke()
  36. {
  37. EnsureTranslator();
  38. // Subnormal results are not flushed to zero by default.
  39. // This operation should not be allowed to do constant propagation, hence the methods that explicitly disallow inlining.
  40. Assert.AreNotEqual(GetDenormal() + GetZero(), 0f);
  41. bool methodCalled = false;
  42. bool isFz = false;
  43. var method = TranslatorTestMethods.GenerateFpFlagsPInvokeTest();
  44. // This method sets flush-to-zero and then calls the managed method.
  45. // Before and after setting the flags, it ensures subnormal addition works as expected.
  46. // It returns a positive result if any tests fail, and 0 on success (or if the platform cannot change FP flags)
  47. int result = method(Marshal.GetFunctionPointerForDelegate(ManagedMethod));
  48. // Subnormal results are not flushed to zero by default, which we should have returned to exiting the method.
  49. Assert.AreNotEqual(GetDenormal() + GetZero(), 0f);
  50. Assert.True(result == 0);
  51. Assert.True(methodCalled);
  52. Assert.True(isFz);
  53. return;
  54. void ManagedMethod()
  55. {
  56. // Floating point math should not modify fp flags.
  57. float test = 2f * 3.5f;
  58. if (test < 4f)
  59. {
  60. throw new Exception("Sanity check.");
  61. }
  62. isFz = GetDenormal() + GetZero() == 0f;
  63. try
  64. {
  65. if (test >= 4f)
  66. {
  67. throw new Exception("Always throws.");
  68. }
  69. }
  70. catch
  71. {
  72. // Exception handling should not modify fp flags.
  73. methodCalled = true;
  74. }
  75. }
  76. }
  77. }
  78. }