/ tests / A64 / fp_min_max.cpp
fp_min_max.cpp
  1  /* This file is part of the dynarmic project.
  2   * Copyright (c) 2022 MerryMage
  3   * SPDX-License-Identifier: 0BSD
  4   */
  5  
  6  #include <vector>
  7  
  8  #include <catch2/catch_test_macros.hpp>
  9  #include <mcl/stdint.hpp>
 10  
 11  #include "./testenv.h"
 12  
 13  using namespace Dynarmic;
 14  
 15  namespace {
 16  
 17  struct TestCase {
 18      u32 a;
 19      u32 b;
 20      u32 fmax;
 21      u32 fmaxnm;
 22      u32 fmin;
 23      u32 fminnm;
 24  };
 25  
 26  const std::vector test_cases{
 27      //                a           b        fmax      fmaxnm        fmin      fminnm
 28      TestCase{0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000},  // +0.0
 29      TestCase{0x80000000, 0x80000000, 0x80000000, 0x80000000, 0x80000000, 0x80000000},  // -0.0
 30      TestCase{0x3f800000, 0x3f800000, 0x3f800000, 0x3f800000, 0x3f800000, 0x3f800000},  // +1.0
 31      TestCase{0xbf800000, 0xbf800000, 0xbf800000, 0xbf800000, 0xbf800000, 0xbf800000},  // -1.0
 32      TestCase{0x7f800000, 0x7f800000, 0x7f800000, 0x7f800000, 0x7f800000, 0x7f800000},  // +Inf
 33      TestCase{0xff800000, 0xff800000, 0xff800000, 0xff800000, 0xff800000, 0xff800000},  // -Inf
 34      TestCase{0x7fc00041, 0x7fc00041, 0x7fc00041, 0x7fc00041, 0x7fc00041, 0x7fc00041},  // QNaN
 35      TestCase{0x7f800042, 0x7f800042, 0x7fc00042, 0x7fc00042, 0x7fc00042, 0x7fc00042},  // SNaN
 36      TestCase{0x00000000, 0x80000000, 0x00000000, 0x00000000, 0x80000000, 0x80000000},  // (+0.0, -0.0)
 37      TestCase{0x3f800000, 0xbf800000, 0x3f800000, 0x3f800000, 0xbf800000, 0xbf800000},  // (+1.0, -1.0)
 38      TestCase{0x3f800000, 0x7f800000, 0x7f800000, 0x7f800000, 0x3f800000, 0x3f800000},  // (+1.0, +Inf)
 39      TestCase{0x3f800000, 0xff800000, 0x3f800000, 0x3f800000, 0xff800000, 0xff800000},  // (+1.0, -Inf)
 40      TestCase{0x7f800000, 0xff800000, 0x7f800000, 0x7f800000, 0xff800000, 0xff800000},  // (+Inf, -Inf)
 41      TestCase{0x3f800000, 0x7fc00041, 0x7fc00041, 0x3f800000, 0x7fc00041, 0x3f800000},  // (+1.0, QNaN)
 42      TestCase{0x3f800000, 0x7f800042, 0x7fc00042, 0x7fc00042, 0x7fc00042, 0x7fc00042},  // (+1.0, SNaN)
 43      TestCase{0x7f800000, 0x7fc00041, 0x7fc00041, 0x7f800000, 0x7fc00041, 0x7f800000},  // (+Inf, QNaN)
 44      TestCase{0x7f800000, 0x7f800042, 0x7fc00042, 0x7fc00042, 0x7fc00042, 0x7fc00042},  // (+Inf, SNaN)
 45      TestCase{0x7fc00041, 0x7f800042, 0x7fc00042, 0x7fc00042, 0x7fc00042, 0x7fc00042},  // (QNaN, SNaN)
 46      TestCase{0xffa57454, 0xe343a6b3, 0xffe57454, 0xffe57454, 0xffe57454, 0xffe57454},
 47  };
 48  
 49  const std::vector unidirectional_test_cases{
 50      TestCase{0x7fc00041, 0x7fc00043, 0x7fc00041, 0x7fc00041, 0x7fc00041, 0x7fc00041},  // (QNaN, QNaN)
 51      TestCase{0x7f800042, 0x7f800044, 0x7fc00042, 0x7fc00042, 0x7fc00042, 0x7fc00042},  // (SNaN, SNaN)
 52  };
 53  
 54  constexpr u32 default_nan = 0x7fc00000;
 55  
 56  bool is_nan(u32 value) {
 57      return (value & 0x7f800000) == 0x7f800000 && (value & 0x007fffff) != 0;
 58  }
 59  
 60  u32 force_default_nan(u32 value) {
 61      return is_nan(value) ? default_nan : value;
 62  }
 63  
 64  template<typename Fn>
 65  void run_test(u32 instruction, Fn fn) {
 66      A64TestEnv env;
 67      A64::Jit jit{A64::UserConfig{&env}};
 68  
 69      env.code_mem.emplace_back(instruction);  // FMAX S0, S1, S2
 70      env.code_mem.emplace_back(0x14000000);   // B .
 71  
 72      for (const auto base_fpcr : {0, 0x01000000}) {
 73          for (const auto test_case : test_cases) {
 74              INFO(test_case.a);
 75              INFO(test_case.b);
 76  
 77              jit.SetFpcr(base_fpcr);
 78  
 79              jit.SetVector(0, {42, 0});
 80              jit.SetVector(1, {test_case.a, 0});
 81              jit.SetVector(2, {test_case.b, 0});
 82              jit.SetPC(0);
 83  
 84              env.ticks_left = 2;
 85              jit.Run();
 86  
 87              REQUIRE(jit.GetVector(0)[0] == fn(test_case));
 88  
 89              jit.SetVector(0, {42, 0});
 90              jit.SetVector(1, {test_case.b, 0});
 91              jit.SetVector(2, {test_case.a, 0});
 92              jit.SetPC(0);
 93  
 94              env.ticks_left = 2;
 95              jit.Run();
 96  
 97              REQUIRE(jit.GetVector(0)[0] == fn(test_case));
 98  
 99              jit.SetFpcr(base_fpcr | 0x02000000);
100  
101              jit.SetVector(0, {42, 0});
102              jit.SetVector(1, {test_case.a, 0});
103              jit.SetVector(2, {test_case.b, 0});
104              jit.SetPC(0);
105  
106              env.ticks_left = 2;
107              jit.Run();
108  
109              REQUIRE(jit.GetVector(0)[0] == force_default_nan(fn(test_case)));
110  
111              jit.SetVector(0, {42, 0});
112              jit.SetVector(1, {test_case.b, 0});
113              jit.SetVector(2, {test_case.a, 0});
114              jit.SetPC(0);
115  
116              env.ticks_left = 2;
117              jit.Run();
118  
119              REQUIRE(jit.GetVector(0)[0] == force_default_nan(fn(test_case)));
120          }
121  
122          for (const auto test_case : unidirectional_test_cases) {
123              INFO(test_case.a);
124              INFO(test_case.b);
125  
126              jit.SetFpcr(base_fpcr);
127  
128              jit.SetVector(0, {42, 0});
129              jit.SetVector(1, {test_case.a, 0});
130              jit.SetVector(2, {test_case.b, 0});
131              jit.SetPC(0);
132  
133              env.ticks_left = 2;
134              jit.Run();
135  
136              REQUIRE(jit.GetVector(0)[0] == fn(test_case));
137  
138              jit.SetFpcr(base_fpcr | 0x02000000);
139  
140              jit.SetVector(0, {42, 0});
141              jit.SetVector(1, {test_case.a, 0});
142              jit.SetVector(2, {test_case.b, 0});
143              jit.SetPC(0);
144  
145              env.ticks_left = 2;
146              jit.Run();
147  
148              REQUIRE(jit.GetVector(0)[0] == force_default_nan(fn(test_case)));
149          }
150      }
151  }
152  
153  }  // namespace
154  
155  TEST_CASE("A64: FMAX (scalar)", "[a64]") {
156      run_test(0x1e224820, [](const TestCase& test_case) { return test_case.fmax; });
157  }
158  
159  TEST_CASE("A64: FMIN (scalar)", "[a64]") {
160      run_test(0x1e225820, [](const TestCase& test_case) { return test_case.fmin; });
161  }
162  
163  TEST_CASE("A64: FMAXNM (scalar)", "[a64]") {
164      run_test(0x1e226820, [](const TestCase& test_case) { return test_case.fmaxnm; });
165  }
166  
167  TEST_CASE("A64: FMINNM (scalar)", "[a64]") {
168      run_test(0x1e227820, [](const TestCase& test_case) { return test_case.fminnm; });
169  }
170  
171  TEST_CASE("A64: FMAX (vector)", "[a64]") {
172      run_test(0x4e22f420, [](const TestCase& test_case) { return test_case.fmax; });
173  }
174  
175  TEST_CASE("A64: FMIN (vector)", "[a64]") {
176      run_test(0x4ea2f420, [](const TestCase& test_case) { return test_case.fmin; });
177  }
178  
179  TEST_CASE("A64: FMAXNM (vector)", "[a64]") {
180      run_test(0x4e22c420, [](const TestCase& test_case) { return test_case.fmaxnm; });
181  }
182  
183  TEST_CASE("A64: FMINNM (vector)", "[a64]") {
184      run_test(0x4ea2c420, [](const TestCase& test_case) { return test_case.fminnm; });
185  }