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 }