| 1 | // RUN: %libomp-compile -Wl,--export-dynamic && %libomp-run |
| 2 | |
| 3 | // REQUIRES: linux |
| 4 | |
| 5 | // Test OpenMP 6.0 memory management routines. |
| 6 | // Test host runtime's basic support with an emulated offload runtime. |
| 7 | |
| 8 | #include <stdlib.h> |
| 9 | #include <omp.h> |
| 10 | |
| 11 | #define NUM_DEVICES 4 |
| 12 | |
| 13 | // |
| 14 | // Required offload runtime interfaces |
| 15 | // |
| 16 | extern int __tgt_get_num_devices(void) { return NUM_DEVICES; } |
| 17 | |
| 18 | extern int __tgt_get_mem_resources(int num_devices, const int *devices, |
| 19 | int host, omp_memspace_handle_t memspace, |
| 20 | int *resources) { |
| 21 | int i; |
| 22 | // We expect valid inputs within this test. |
| 23 | int num_resources = num_devices; |
| 24 | if (resources) { |
| 25 | // Simple resouce ID mapping example in the backend (=device ID). |
| 26 | // This does not represent any real backend. |
| 27 | for (i = 0; i < num_devices; i++) |
| 28 | resources[i] = devices[i]; |
| 29 | } |
| 30 | return num_resources; |
| 31 | } |
| 32 | |
| 33 | extern void *__tgt_omp_alloc(size_t size, omp_allocator_handle_t allocator) { |
| 34 | return malloc(size: size); |
| 35 | } |
| 36 | |
| 37 | extern void __tgt_omp_free(void *ptr, omp_allocator_handle_t allocator) { |
| 38 | free(ptr: ptr); |
| 39 | } |
| 40 | |
| 41 | // Code above is also used by the corresponding Fortran test |
| 42 | |
| 43 | #define CHECK_OR_RET_FAIL(Expr) \ |
| 44 | do { \ |
| 45 | if (!(Expr)) \ |
| 46 | return EXIT_FAILURE; \ |
| 47 | } while (0) |
| 48 | |
| 49 | // Test user-initialized allocator with the given memory space |
| 50 | static int test_user_allocator(omp_memspace_handle_t ms) { |
| 51 | omp_allocator_handle_t al = omp_null_allocator; |
| 52 | al = omp_init_allocator(ms, 0, NULL); |
| 53 | CHECK_OR_RET_FAIL(al != omp_null_allocator); |
| 54 | void *m = omp_alloc(1024, al); |
| 55 | CHECK_OR_RET_FAIL(m != NULL); |
| 56 | omp_free(m, al); |
| 57 | omp_destroy_allocator(al); |
| 58 | return EXIT_SUCCESS; |
| 59 | } |
| 60 | |
| 61 | static int test_allocator(omp_allocator_handle_t al) { |
| 62 | void *m = omp_alloc(1024, al); |
| 63 | CHECK_OR_RET_FAIL(m != NULL); |
| 64 | omp_free(m, al); |
| 65 | omp_destroy_allocator(al); |
| 66 | return EXIT_SUCCESS; |
| 67 | } |
| 68 | |
| 69 | static int test_mem_space(void) { |
| 70 | int i, count; |
| 71 | int num_devices = omp_get_num_devices(); |
| 72 | CHECK_OR_RET_FAIL(num_devices == NUM_DEVICES); |
| 73 | |
| 74 | int *all_devices = (int *)malloc(size: sizeof(int) * num_devices); |
| 75 | for (i = 0; i < num_devices; i++) |
| 76 | all_devices[i] = i; |
| 77 | |
| 78 | omp_memspace_handle_t predef = omp_default_mem_space; |
| 79 | omp_memspace_handle_t ms1 = omp_null_mem_space; |
| 80 | omp_memspace_handle_t ms2 = omp_null_mem_space; |
| 81 | |
| 82 | // Test the following API routines. |
| 83 | // * omp_get_device_memspace |
| 84 | // * omp_get_device_and_host_memspace |
| 85 | // * omp_get_devices_memspace |
| 86 | // * omp_get_devices_and_host_memspace |
| 87 | // Test if runtime returns the same memory space handle for the same input. |
| 88 | // Test if we can use the memory space to intialize allocator. |
| 89 | for (i = 0; i < num_devices; i++) { |
| 90 | ms1 = omp_get_device_memspace(i, predef); |
| 91 | CHECK_OR_RET_FAIL(ms1 != omp_null_mem_space); |
| 92 | ms2 = omp_get_device_memspace(i, predef); |
| 93 | CHECK_OR_RET_FAIL(ms1 == ms2); |
| 94 | CHECK_OR_RET_FAIL(test_user_allocator(ms1) == EXIT_SUCCESS); |
| 95 | ms1 = ms2 = omp_null_mem_space; |
| 96 | |
| 97 | ms1 = omp_get_device_and_host_memspace(i, predef); |
| 98 | CHECK_OR_RET_FAIL(ms1 != omp_null_mem_space); |
| 99 | ms2 = omp_get_device_and_host_memspace(i, predef); |
| 100 | CHECK_OR_RET_FAIL(ms1 == ms2); |
| 101 | CHECK_OR_RET_FAIL(test_user_allocator(ms1) == EXIT_SUCCESS); |
| 102 | ms1 = ms2 = omp_null_mem_space; |
| 103 | |
| 104 | for (count = 1; i + count <= num_devices; count++) { |
| 105 | int *devices = &all_devices[i]; |
| 106 | ms1 = omp_get_devices_memspace(count, devices, predef); |
| 107 | CHECK_OR_RET_FAIL(ms1 != omp_null_mem_space); |
| 108 | ms2 = omp_get_devices_memspace(count, devices, predef); |
| 109 | CHECK_OR_RET_FAIL(ms1 == ms2); |
| 110 | CHECK_OR_RET_FAIL(test_user_allocator(ms1) == EXIT_SUCCESS); |
| 111 | ms1 = ms2 = omp_null_mem_space; |
| 112 | |
| 113 | ms1 = omp_get_devices_and_host_memspace(count, devices, predef); |
| 114 | CHECK_OR_RET_FAIL(ms1 != omp_null_mem_space); |
| 115 | ms2 = omp_get_devices_and_host_memspace(count, devices, predef); |
| 116 | CHECK_OR_RET_FAIL(ms1 == ms2); |
| 117 | CHECK_OR_RET_FAIL(test_user_allocator(ms1) == EXIT_SUCCESS); |
| 118 | ms1 = ms2 = omp_null_mem_space; |
| 119 | } |
| 120 | } |
| 121 | |
| 122 | // Test the following API routines. |
| 123 | // * omp_get_devices_all_memspace |
| 124 | // Test if runtime returns the same memory space handle for the same input. |
| 125 | ms1 = omp_get_devices_all_memspace(predef); |
| 126 | CHECK_OR_RET_FAIL(ms1 != omp_null_mem_space); |
| 127 | ms2 = omp_get_devices_all_memspace(predef); |
| 128 | CHECK_OR_RET_FAIL(ms1 == ms2); |
| 129 | |
| 130 | free(ptr: all_devices); |
| 131 | |
| 132 | return EXIT_SUCCESS; |
| 133 | } |
| 134 | |
| 135 | static int test_mem_allocator(void) { |
| 136 | int i, count; |
| 137 | int num_devices = omp_get_num_devices(); |
| 138 | CHECK_OR_RET_FAIL(num_devices == NUM_DEVICES); |
| 139 | |
| 140 | int *all_devices = (int *)malloc(size: sizeof(int) * num_devices); |
| 141 | for (i = 0; i < num_devices; i++) |
| 142 | all_devices[i] = i; |
| 143 | |
| 144 | omp_memspace_handle_t predef = omp_default_mem_space; |
| 145 | omp_allocator_handle_t al = omp_null_allocator; |
| 146 | |
| 147 | // Test the following API routines. |
| 148 | // * omp_get_device_allocator |
| 149 | // * omp_get_device_and_host_allocator |
| 150 | // * omp_get_devices_allocator |
| 151 | // * omp_get_devices_and_host_allocator |
| 152 | for (i = 0; i < num_devices; i++) { |
| 153 | al = omp_get_device_allocator(i, predef); |
| 154 | CHECK_OR_RET_FAIL(al != omp_null_allocator); |
| 155 | CHECK_OR_RET_FAIL(test_allocator(al) == EXIT_SUCCESS); |
| 156 | al = omp_null_allocator; |
| 157 | |
| 158 | al = omp_get_device_and_host_allocator(i, predef); |
| 159 | CHECK_OR_RET_FAIL(al != omp_null_allocator); |
| 160 | CHECK_OR_RET_FAIL(test_allocator(al) == EXIT_SUCCESS); |
| 161 | al = omp_null_allocator; |
| 162 | |
| 163 | for (count = 1; i + count <= num_devices; count++) { |
| 164 | int *devices = &all_devices[i]; |
| 165 | al = omp_get_devices_allocator(count, devices, predef); |
| 166 | CHECK_OR_RET_FAIL(al != omp_null_allocator); |
| 167 | CHECK_OR_RET_FAIL(test_allocator(al) == EXIT_SUCCESS); |
| 168 | al = omp_null_allocator; |
| 169 | |
| 170 | al = omp_get_devices_and_host_allocator(count, devices, predef); |
| 171 | CHECK_OR_RET_FAIL(al != omp_null_allocator); |
| 172 | CHECK_OR_RET_FAIL(test_allocator(al) == EXIT_SUCCESS); |
| 173 | al = omp_null_allocator; |
| 174 | } |
| 175 | } |
| 176 | |
| 177 | // Test the following API routines. |
| 178 | // * omp_get_devices_all_allocator |
| 179 | al = omp_get_devices_all_allocator(predef); |
| 180 | CHECK_OR_RET_FAIL(al != omp_null_allocator); |
| 181 | CHECK_OR_RET_FAIL(test_allocator(al) == EXIT_SUCCESS); |
| 182 | |
| 183 | free(ptr: all_devices); |
| 184 | |
| 185 | return EXIT_SUCCESS; |
| 186 | } |
| 187 | |
| 188 | // Just test what we can expect from the emulated backend. |
| 189 | static int test_sub_mem_space(void) { |
| 190 | int i; |
| 191 | omp_memspace_handle_t ms = omp_null_mem_space; |
| 192 | ms = omp_get_devices_all_memspace(omp_default_mem_space); |
| 193 | CHECK_OR_RET_FAIL(ms != omp_null_mem_space); |
| 194 | int num_resources = omp_get_memspace_num_resources(ms); |
| 195 | CHECK_OR_RET_FAIL(num_resources == NUM_DEVICES); |
| 196 | |
| 197 | // Check if single-resource sub memspace is correctly returned. |
| 198 | for (i = 0; i < num_resources; i++) { |
| 199 | omp_memspace_handle_t sub = omp_get_submemspace(ms, 1, &i); |
| 200 | CHECK_OR_RET_FAIL(sub != omp_null_mem_space); |
| 201 | CHECK_OR_RET_FAIL(sub != ms); |
| 202 | int num_sub_resources = omp_get_memspace_num_resources(sub); |
| 203 | CHECK_OR_RET_FAIL(num_sub_resources == 1); |
| 204 | } |
| 205 | |
| 206 | // Check if all-resrouce sub memspace is correctly returned. |
| 207 | int *resources = (int *)malloc(size: sizeof(int) * num_resources); |
| 208 | for (i = 0; i < num_resources; i++) |
| 209 | resources[i] = i; |
| 210 | omp_memspace_handle_t sub = omp_get_submemspace(ms, num_resources, resources); |
| 211 | CHECK_OR_RET_FAIL(sub != omp_null_mem_space); |
| 212 | CHECK_OR_RET_FAIL(sub == ms); |
| 213 | |
| 214 | return EXIT_SUCCESS; |
| 215 | } |
| 216 | |
| 217 | int main() { |
| 218 | int rc = test_mem_space(); |
| 219 | CHECK_OR_RET_FAIL(rc == EXIT_SUCCESS); |
| 220 | |
| 221 | rc = test_mem_allocator(); |
| 222 | CHECK_OR_RET_FAIL(rc == EXIT_SUCCESS); |
| 223 | |
| 224 | rc = test_sub_mem_space(); |
| 225 | CHECK_OR_RET_FAIL(rc == EXIT_SUCCESS); |
| 226 | |
| 227 | return rc; |
| 228 | } |
| 229 | |