EnvironmentTests.cs 3.1 KB

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