| 1 | /* |
| 2 | * SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | * |
| 4 | * Check if we detect all memory accesses expected using plugin API. |
| 5 | * Used in conjunction with ./check-plugin-mem-access.sh check script. |
| 6 | * Output of this program is the list of patterns expected in plugin output. |
| 7 | * |
| 8 | * 8,16,32 load/store are tested for all arch. |
| 9 | * 64,128 load/store are tested for aarch64/x64. |
| 10 | * atomic operations (8,16,32,64) are tested for x64 only. |
| 11 | */ |
| 12 | |
| 13 | #include <pthread.h> |
| 14 | #include <stdint.h> |
| 15 | #include <stdio.h> |
| 16 | #include <stdlib.h> |
| 17 | |
| 18 | #if defined(__x86_64__) |
| 19 | #include <emmintrin.h> |
| 20 | #elif defined(__aarch64__) |
| 21 | #include <arm_neon.h> |
| 22 | #endif /* __x86_64__ */ |
| 23 | |
| 24 | static void *data; |
| 25 | |
| 26 | /* ,store_u8,.*,8,store,0xf1 */ |
| 27 | #define PRINT_EXPECTED(function, type, value, action) \ |
| 28 | do { \ |
| 29 | printf(",%s,.*,%d,%s,%s\n", \ |
| 30 | #function, (int) sizeof(type) * 8, action, value); \ |
| 31 | } \ |
| 32 | while (0) |
| 33 | |
| 34 | #define DEFINE_STORE(name, type, value) \ |
| 35 | \ |
| 36 | static void print_expected_store_##name(void) \ |
| 37 | { \ |
| 38 | PRINT_EXPECTED(store_##name, type, #value, "store"); \ |
| 39 | } \ |
| 40 | \ |
| 41 | static void store_##name(void) \ |
| 42 | { \ |
| 43 | *((type *)data) = value; \ |
| 44 | print_expected_store_##name(); \ |
| 45 | } |
| 46 | |
| 47 | #define DEFINE_ATOMIC_OP(name, type, value) \ |
| 48 | \ |
| 49 | static void print_expected_atomic_op_##name(void) \ |
| 50 | { \ |
| 51 | PRINT_EXPECTED(atomic_op_##name, type, "0x0*42", "load"); \ |
| 52 | PRINT_EXPECTED(atomic_op_##name, type, #value, "store"); \ |
| 53 | } \ |
| 54 | \ |
| 55 | static void atomic_op_##name(void) \ |
| 56 | { \ |
| 57 | *((type *)data) = 0x42; \ |
| 58 | __sync_val_compare_and_swap((type *)data, 0x42, value); \ |
| 59 | print_expected_atomic_op_##name(); \ |
| 60 | } |
| 61 | |
| 62 | #define DEFINE_LOAD(name, type, value) \ |
| 63 | \ |
| 64 | static void print_expected_load_##name(void) \ |
| 65 | { \ |
| 66 | PRINT_EXPECTED(load_##name, type, #value, "load"); \ |
| 67 | } \ |
| 68 | \ |
| 69 | static void load_##name(void) \ |
| 70 | { \ |
| 71 | \ |
| 72 | /* volatile forces load to be generated. */ \ |
| 73 | volatile type src = *((type *) data); \ |
| 74 | volatile type dest = src; \ |
| 75 | (void)src, (void)dest; \ |
| 76 | print_expected_load_##name(); \ |
| 77 | } |
| 78 | |
| 79 | DEFINE_STORE(u8, uint8_t, 0xf1) |
| 80 | DEFINE_LOAD(u8, uint8_t, 0xf1) |
| 81 | DEFINE_STORE(u16, uint16_t, 0xf123) |
| 82 | DEFINE_LOAD(u16, uint16_t, 0xf123) |
| 83 | DEFINE_STORE(u32, uint32_t, 0xff112233) |
| 84 | DEFINE_LOAD(u32, uint32_t, 0xff112233) |
| 85 | |
| 86 | #if defined(__x86_64__) || defined(__aarch64__) |
| 87 | DEFINE_STORE(u64, uint64_t, 0xf123456789abcdef) |
| 88 | DEFINE_LOAD(u64, uint64_t, 0xf123456789abcdef) |
| 89 | |
| 90 | static void print_expected_store_u128(void) |
| 91 | { |
| 92 | PRINT_EXPECTED(store_u128, __int128, |
| 93 | "0xf122334455667788f123456789abcdef", "store"); |
| 94 | } |
| 95 | |
| 96 | static void store_u128(void) |
| 97 | { |
| 98 | #ifdef __x86_64__ |
| 99 | _mm_store_si128(data, _mm_set_epi32(0xf1223344, 0x55667788, |
| 100 | 0xf1234567, 0x89abcdef)); |
| 101 | #else |
| 102 | const uint32_t init[4] = {0x89abcdef, 0xf1234567, 0x55667788, 0xf1223344}; |
| 103 | uint32x4_t vec = vld1q_u32(init); |
| 104 | vst1q_u32(data, vec); |
| 105 | #endif /* __x86_64__ */ |
| 106 | print_expected_store_u128(); |
| 107 | } |
| 108 | |
| 109 | static void print_expected_load_u128(void) |
| 110 | { |
| 111 | PRINT_EXPECTED(load_u128, __int128, |
| 112 | "0xf122334455667788f123456789abcdef", "load"); |
| 113 | } |
| 114 | |
| 115 | static void load_u128(void) |
| 116 | { |
| 117 | #ifdef __x86_64__ |
| 118 | __m128i var = _mm_load_si128(data); |
| 119 | #else |
| 120 | uint32x4_t var = vld1q_u32(data); |
| 121 | #endif |
| 122 | (void) var; |
| 123 | print_expected_load_u128(); |
| 124 | } |
| 125 | #endif /* __x86_64__ || __aarch64__ */ |
| 126 | |
| 127 | #if defined(__x86_64__) |
| 128 | DEFINE_ATOMIC_OP(u8, uint8_t, 0xf1) |
| 129 | DEFINE_ATOMIC_OP(u16, uint16_t, 0xf123) |
| 130 | DEFINE_ATOMIC_OP(u32, uint32_t, 0xff112233) |
| 131 | DEFINE_ATOMIC_OP(u64, uint64_t, 0xf123456789abcdef) |
| 132 | #endif /* __x86_64__ */ |
| 133 | |
| 134 | static void *f(void *p) |
| 135 | { |
| 136 | return NULL; |
| 137 | } |
| 138 | |
| 139 | int main(void) |
| 140 | { |
| 141 | /* |
| 142 | * We force creation of a second thread to enable cpu flag CF_PARALLEL. |
| 143 | * This will generate atomic operations when needed. |
| 144 | */ |
| 145 | pthread_t thread; |
| 146 | pthread_create(&thread, NULL, &f, NULL); |
| 147 | pthread_join(thread, NULL); |
| 148 | |
| 149 | /* allocate storage up to 128 bits */ |
| 150 | data = malloc(16); |
| 151 | |
| 152 | store_u8(); |
| 153 | load_u8(); |
| 154 | |
| 155 | store_u16(); |
| 156 | load_u16(); |
| 157 | |
| 158 | store_u32(); |
| 159 | load_u32(); |
| 160 | |
| 161 | #if defined(__x86_64__) || defined(__aarch64__) |
| 162 | store_u64(); |
| 163 | load_u64(); |
| 164 | |
| 165 | store_u128(); |
| 166 | load_u128(); |
| 167 | #endif /* __x86_64__ || __aarch64__ */ |
| 168 | |
| 169 | #if defined(__x86_64__) |
| 170 | atomic_op_u8(); |
| 171 | atomic_op_u16(); |
| 172 | atomic_op_u32(); |
| 173 | atomic_op_u64(); |
| 174 | #endif /* __x86_64__ */ |
| 175 | |
| 176 | free(data); |
| 177 | } |