Paolo Bonzini | 6046c62 | 2013-06-20 16:19:32 +0200 | [diff] [blame] | 1 | /* |
| 2 | * Test Int128 arithmetic |
| 3 | * |
| 4 | * This work is licensed under the terms of the GNU LGPL, version 2 or later. |
| 5 | * See the COPYING.LIB file in the top-level directory. |
| 6 | * |
| 7 | */ |
| 8 | |
Paolo Bonzini | 6046c62 | 2013-06-20 16:19:32 +0200 | [diff] [blame] | 9 | #include "qemu/osdep.h" |
Peter Maydell | 681c28a | 2016-02-08 18:08:51 +0000 | [diff] [blame] | 10 | #include "qemu/int128.h" |
Paolo Bonzini | 6046c62 | 2013-06-20 16:19:32 +0200 | [diff] [blame] | 11 | |
Peter Maydell | 7edd9dd | 2014-02-26 23:39:46 +0000 | [diff] [blame] | 12 | /* clang doesn't support __noclone__ but it does have a mechanism for |
| 13 | * telling us this. We assume that if we don't have __has_attribute() |
| 14 | * then this is GCC and that GCC always supports __noclone__. |
| 15 | */ |
| 16 | #if defined(__has_attribute) |
| 17 | #if !__has_attribute(__noclone__) |
| 18 | #define ATTRIBUTE_NOCLONE |
| 19 | #endif |
| 20 | #endif |
| 21 | #ifndef ATTRIBUTE_NOCLONE |
| 22 | #define ATTRIBUTE_NOCLONE __attribute__((__noclone__)) |
| 23 | #endif |
| 24 | |
Paolo Bonzini | 6046c62 | 2013-06-20 16:19:32 +0200 | [diff] [blame] | 25 | static uint32_t tests[8] = { |
| 26 | 0x00000000, 0x00000001, 0x7FFFFFFE, 0x7FFFFFFF, |
| 27 | 0x80000000, 0x80000001, 0xFFFFFFFE, 0xFFFFFFFF, |
| 28 | }; |
| 29 | |
| 30 | #define LOW 3ULL |
| 31 | #define HIGH (1ULL << 63) |
| 32 | #define MIDDLE (-1ULL & ~LOW & ~HIGH) |
| 33 | |
| 34 | static uint64_t expand16(unsigned x) |
| 35 | { |
| 36 | return (x & LOW) | ((x & 4) ? MIDDLE : 0) | (x & 0x8000 ? HIGH : 0); |
| 37 | } |
| 38 | |
| 39 | static Int128 expand(uint32_t x) |
| 40 | { |
| 41 | uint64_t l, h; |
| 42 | l = expand16(x & 65535); |
| 43 | h = expand16(x >> 16); |
Richard Henderson | 0846beb | 2016-06-29 15:52:10 -0700 | [diff] [blame] | 44 | return (Int128) int128_make128(l, h); |
Paolo Bonzini | 6046c62 | 2013-06-20 16:19:32 +0200 | [diff] [blame] | 45 | }; |
| 46 | |
| 47 | static void test_and(void) |
| 48 | { |
| 49 | int i, j; |
| 50 | |
| 51 | for (i = 0; i < ARRAY_SIZE(tests); ++i) { |
| 52 | for (j = 0; j < ARRAY_SIZE(tests); ++j) { |
| 53 | Int128 a = expand(tests[i]); |
| 54 | Int128 b = expand(tests[j]); |
| 55 | Int128 r = expand(tests[i] & tests[j]); |
| 56 | Int128 s = int128_and(a, b); |
Richard Henderson | 0846beb | 2016-06-29 15:52:10 -0700 | [diff] [blame] | 57 | g_assert_cmpuint(int128_getlo(r), ==, int128_getlo(s)); |
| 58 | g_assert_cmpuint(int128_gethi(r), ==, int128_gethi(s)); |
Paolo Bonzini | 6046c62 | 2013-06-20 16:19:32 +0200 | [diff] [blame] | 59 | } |
| 60 | } |
| 61 | } |
| 62 | |
| 63 | static void test_add(void) |
| 64 | { |
| 65 | int i, j; |
| 66 | |
| 67 | for (i = 0; i < ARRAY_SIZE(tests); ++i) { |
| 68 | for (j = 0; j < ARRAY_SIZE(tests); ++j) { |
| 69 | Int128 a = expand(tests[i]); |
| 70 | Int128 b = expand(tests[j]); |
| 71 | Int128 r = expand(tests[i] + tests[j]); |
| 72 | Int128 s = int128_add(a, b); |
Richard Henderson | 0846beb | 2016-06-29 15:52:10 -0700 | [diff] [blame] | 73 | g_assert_cmpuint(int128_getlo(r), ==, int128_getlo(s)); |
| 74 | g_assert_cmpuint(int128_gethi(r), ==, int128_gethi(s)); |
Paolo Bonzini | 6046c62 | 2013-06-20 16:19:32 +0200 | [diff] [blame] | 75 | } |
| 76 | } |
| 77 | } |
| 78 | |
| 79 | static void test_sub(void) |
| 80 | { |
| 81 | int i, j; |
| 82 | |
| 83 | for (i = 0; i < ARRAY_SIZE(tests); ++i) { |
| 84 | for (j = 0; j < ARRAY_SIZE(tests); ++j) { |
| 85 | Int128 a = expand(tests[i]); |
| 86 | Int128 b = expand(tests[j]); |
| 87 | Int128 r = expand(tests[i] - tests[j]); |
| 88 | Int128 s = int128_sub(a, b); |
Richard Henderson | 0846beb | 2016-06-29 15:52:10 -0700 | [diff] [blame] | 89 | g_assert_cmpuint(int128_getlo(r), ==, int128_getlo(s)); |
| 90 | g_assert_cmpuint(int128_gethi(r), ==, int128_gethi(s)); |
Paolo Bonzini | 6046c62 | 2013-06-20 16:19:32 +0200 | [diff] [blame] | 91 | } |
| 92 | } |
| 93 | } |
| 94 | |
| 95 | static void test_neg(void) |
| 96 | { |
| 97 | int i; |
| 98 | |
| 99 | for (i = 0; i < ARRAY_SIZE(tests); ++i) { |
| 100 | Int128 a = expand(tests[i]); |
| 101 | Int128 r = expand(-tests[i]); |
| 102 | Int128 s = int128_neg(a); |
Richard Henderson | 0846beb | 2016-06-29 15:52:10 -0700 | [diff] [blame] | 103 | g_assert_cmpuint(int128_getlo(r), ==, int128_getlo(s)); |
| 104 | g_assert_cmpuint(int128_gethi(r), ==, int128_gethi(s)); |
Paolo Bonzini | 6046c62 | 2013-06-20 16:19:32 +0200 | [diff] [blame] | 105 | } |
| 106 | } |
| 107 | |
| 108 | static void test_nz(void) |
| 109 | { |
| 110 | int i, j; |
| 111 | |
| 112 | for (i = 0; i < ARRAY_SIZE(tests); ++i) { |
| 113 | for (j = 0; j < ARRAY_SIZE(tests); ++j) { |
| 114 | Int128 a = expand(tests[i]); |
| 115 | g_assert_cmpuint(int128_nz(a), ==, tests[i] != 0); |
| 116 | } |
| 117 | } |
| 118 | } |
| 119 | |
| 120 | static void test_le(void) |
| 121 | { |
| 122 | int i, j; |
| 123 | |
| 124 | for (i = 0; i < ARRAY_SIZE(tests); ++i) { |
| 125 | for (j = 0; j < ARRAY_SIZE(tests); ++j) { |
| 126 | /* Signed comparison */ |
| 127 | int32_t a = (int32_t) tests[i]; |
| 128 | int32_t b = (int32_t) tests[j]; |
| 129 | g_assert_cmpuint(int128_le(expand(a), expand(b)), ==, a <= b); |
| 130 | } |
| 131 | } |
| 132 | } |
| 133 | |
| 134 | static void test_lt(void) |
| 135 | { |
| 136 | int i, j; |
| 137 | |
| 138 | for (i = 0; i < ARRAY_SIZE(tests); ++i) { |
| 139 | for (j = 0; j < ARRAY_SIZE(tests); ++j) { |
| 140 | /* Signed comparison */ |
| 141 | int32_t a = (int32_t) tests[i]; |
| 142 | int32_t b = (int32_t) tests[j]; |
| 143 | g_assert_cmpuint(int128_lt(expand(a), expand(b)), ==, a < b); |
| 144 | } |
| 145 | } |
| 146 | } |
| 147 | |
| 148 | static void test_ge(void) |
| 149 | { |
| 150 | int i, j; |
| 151 | |
| 152 | for (i = 0; i < ARRAY_SIZE(tests); ++i) { |
| 153 | for (j = 0; j < ARRAY_SIZE(tests); ++j) { |
| 154 | /* Signed comparison */ |
| 155 | int32_t a = (int32_t) tests[i]; |
| 156 | int32_t b = (int32_t) tests[j]; |
| 157 | g_assert_cmpuint(int128_ge(expand(a), expand(b)), ==, a >= b); |
| 158 | } |
| 159 | } |
| 160 | } |
| 161 | |
| 162 | static void test_gt(void) |
| 163 | { |
| 164 | int i, j; |
| 165 | |
| 166 | for (i = 0; i < ARRAY_SIZE(tests); ++i) { |
| 167 | for (j = 0; j < ARRAY_SIZE(tests); ++j) { |
| 168 | /* Signed comparison */ |
| 169 | int32_t a = (int32_t) tests[i]; |
| 170 | int32_t b = (int32_t) tests[j]; |
| 171 | g_assert_cmpuint(int128_gt(expand(a), expand(b)), ==, a > b); |
| 172 | } |
| 173 | } |
| 174 | } |
| 175 | |
| 176 | /* Make sure to test undefined behavior at runtime! */ |
| 177 | |
Peter Maydell | 7edd9dd | 2014-02-26 23:39:46 +0000 | [diff] [blame] | 178 | static void __attribute__((__noinline__)) ATTRIBUTE_NOCLONE |
Paolo Bonzini | 6046c62 | 2013-06-20 16:19:32 +0200 | [diff] [blame] | 179 | test_rshift_one(uint32_t x, int n, uint64_t h, uint64_t l) |
| 180 | { |
| 181 | Int128 a = expand(x); |
| 182 | Int128 r = int128_rshift(a, n); |
Richard Henderson | 0846beb | 2016-06-29 15:52:10 -0700 | [diff] [blame] | 183 | g_assert_cmpuint(int128_getlo(r), ==, l); |
| 184 | g_assert_cmpuint(int128_gethi(r), ==, h); |
Paolo Bonzini | 6046c62 | 2013-06-20 16:19:32 +0200 | [diff] [blame] | 185 | } |
| 186 | |
| 187 | static void test_rshift(void) |
| 188 | { |
| 189 | test_rshift_one(0x00010000U, 64, 0x0000000000000000ULL, 0x0000000000000001ULL); |
| 190 | test_rshift_one(0x80010000U, 64, 0xFFFFFFFFFFFFFFFFULL, 0x8000000000000001ULL); |
| 191 | test_rshift_one(0x7FFE0000U, 64, 0x0000000000000000ULL, 0x7FFFFFFFFFFFFFFEULL); |
| 192 | test_rshift_one(0xFFFE0000U, 64, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFEULL); |
| 193 | test_rshift_one(0x00010000U, 60, 0x0000000000000000ULL, 0x0000000000000010ULL); |
| 194 | test_rshift_one(0x80010000U, 60, 0xFFFFFFFFFFFFFFF8ULL, 0x0000000000000010ULL); |
| 195 | test_rshift_one(0x00018000U, 60, 0x0000000000000000ULL, 0x0000000000000018ULL); |
| 196 | test_rshift_one(0x80018000U, 60, 0xFFFFFFFFFFFFFFF8ULL, 0x0000000000000018ULL); |
| 197 | test_rshift_one(0x7FFE0000U, 60, 0x0000000000000007ULL, 0xFFFFFFFFFFFFFFE0ULL); |
| 198 | test_rshift_one(0xFFFE0000U, 60, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFE0ULL); |
| 199 | test_rshift_one(0x7FFE8000U, 60, 0x0000000000000007ULL, 0xFFFFFFFFFFFFFFE8ULL); |
| 200 | test_rshift_one(0xFFFE8000U, 60, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFE8ULL); |
| 201 | test_rshift_one(0x00018000U, 0, 0x0000000000000001ULL, 0x8000000000000000ULL); |
| 202 | test_rshift_one(0x80018000U, 0, 0x8000000000000001ULL, 0x8000000000000000ULL); |
| 203 | test_rshift_one(0x7FFE0000U, 0, 0x7FFFFFFFFFFFFFFEULL, 0x0000000000000000ULL); |
| 204 | test_rshift_one(0xFFFE0000U, 0, 0xFFFFFFFFFFFFFFFEULL, 0x0000000000000000ULL); |
| 205 | test_rshift_one(0x7FFE8000U, 0, 0x7FFFFFFFFFFFFFFEULL, 0x8000000000000000ULL); |
| 206 | test_rshift_one(0xFFFE8000U, 0, 0xFFFFFFFFFFFFFFFEULL, 0x8000000000000000ULL); |
| 207 | } |
| 208 | |
| 209 | int main(int argc, char **argv) |
| 210 | { |
| 211 | g_test_init(&argc, &argv, NULL); |
| 212 | g_test_add_func("/int128/int128_and", test_and); |
| 213 | g_test_add_func("/int128/int128_add", test_add); |
| 214 | g_test_add_func("/int128/int128_sub", test_sub); |
| 215 | g_test_add_func("/int128/int128_neg", test_neg); |
| 216 | g_test_add_func("/int128/int128_nz", test_nz); |
| 217 | g_test_add_func("/int128/int128_le", test_le); |
| 218 | g_test_add_func("/int128/int128_lt", test_lt); |
| 219 | g_test_add_func("/int128/int128_ge", test_ge); |
| 220 | g_test_add_func("/int128/int128_gt", test_gt); |
| 221 | g_test_add_func("/int128/int128_rshift", test_rshift); |
| 222 | return g_test_run(); |
| 223 | } |