// RUN: %libomp-compile -Wl,--export-dynamic && %libomp-run // REQUIRES: linux // Test OpenMP 6.0 memory management routines. // Test host runtime's basic support with an emulated offload runtime. #include #include #define NUM_DEVICES 4 // // Required offload runtime interfaces // extern int __tgt_get_num_devices(void) { return NUM_DEVICES; } extern int __tgt_get_mem_resources(int num_devices, const int *devices, int host, omp_memspace_handle_t memspace, int *resources) { int i; // We expect valid inputs within this test. int num_resources = num_devices; if (resources) { // Simple resouce ID mapping example in the backend (=device ID). // This does not represent any real backend. for (i = 0; i < num_devices; i++) resources[i] = devices[i]; } return num_resources; } extern void *__tgt_omp_alloc(size_t size, omp_allocator_handle_t allocator) { return malloc(size); } extern void __tgt_omp_free(void *ptr, omp_allocator_handle_t allocator) { free(ptr); } // Code above is also used by the corresponding Fortran test #define CHECK_OR_RET_FAIL(Expr) \ do { \ if (!(Expr)) \ return EXIT_FAILURE; \ } while (0) // Test user-initialized allocator with the given memory space static int test_user_allocator(omp_memspace_handle_t ms) { omp_allocator_handle_t al = omp_null_allocator; al = omp_init_allocator(ms, 0, NULL); CHECK_OR_RET_FAIL(al != omp_null_allocator); void *m = omp_alloc(1024, al); CHECK_OR_RET_FAIL(m != NULL); omp_free(m, al); omp_destroy_allocator(al); return EXIT_SUCCESS; } static int test_allocator(omp_allocator_handle_t al) { void *m = omp_alloc(1024, al); CHECK_OR_RET_FAIL(m != NULL); omp_free(m, al); omp_destroy_allocator(al); return EXIT_SUCCESS; } static int test_mem_space(void) { int i, count; int num_devices = omp_get_num_devices(); CHECK_OR_RET_FAIL(num_devices == NUM_DEVICES); int *all_devices = (int *)malloc(sizeof(int) * num_devices); for (i = 0; i < num_devices; i++) all_devices[i] = i; omp_memspace_handle_t predef = omp_default_mem_space; omp_memspace_handle_t ms1 = omp_null_mem_space; omp_memspace_handle_t ms2 = omp_null_mem_space; // Test the following API routines. // * omp_get_device_memspace // * omp_get_device_and_host_memspace // * omp_get_devices_memspace // * omp_get_devices_and_host_memspace // Test if runtime returns the same memory space handle for the same input. // Test if we can use the memory space to intialize allocator. for (i = 0; i < num_devices; i++) { ms1 = omp_get_device_memspace(i, predef); CHECK_OR_RET_FAIL(ms1 != omp_null_mem_space); ms2 = omp_get_device_memspace(i, predef); CHECK_OR_RET_FAIL(ms1 == ms2); CHECK_OR_RET_FAIL(test_user_allocator(ms1) == EXIT_SUCCESS); ms1 = ms2 = omp_null_mem_space; ms1 = omp_get_device_and_host_memspace(i, predef); CHECK_OR_RET_FAIL(ms1 != omp_null_mem_space); ms2 = omp_get_device_and_host_memspace(i, predef); CHECK_OR_RET_FAIL(ms1 == ms2); CHECK_OR_RET_FAIL(test_user_allocator(ms1) == EXIT_SUCCESS); ms1 = ms2 = omp_null_mem_space; for (count = 1; i + count <= num_devices; count++) { int *devices = &all_devices[i]; ms1 = omp_get_devices_memspace(count, devices, predef); CHECK_OR_RET_FAIL(ms1 != omp_null_mem_space); ms2 = omp_get_devices_memspace(count, devices, predef); CHECK_OR_RET_FAIL(ms1 == ms2); CHECK_OR_RET_FAIL(test_user_allocator(ms1) == EXIT_SUCCESS); ms1 = ms2 = omp_null_mem_space; ms1 = omp_get_devices_and_host_memspace(count, devices, predef); CHECK_OR_RET_FAIL(ms1 != omp_null_mem_space); ms2 = omp_get_devices_and_host_memspace(count, devices, predef); CHECK_OR_RET_FAIL(ms1 == ms2); CHECK_OR_RET_FAIL(test_user_allocator(ms1) == EXIT_SUCCESS); ms1 = ms2 = omp_null_mem_space; } } // Test the following API routines. // * omp_get_devices_all_memspace // Test if runtime returns the same memory space handle for the same input. ms1 = omp_get_devices_all_memspace(predef); CHECK_OR_RET_FAIL(ms1 != omp_null_mem_space); ms2 = omp_get_devices_all_memspace(predef); CHECK_OR_RET_FAIL(ms1 == ms2); free(all_devices); return EXIT_SUCCESS; } static int test_mem_allocator(void) { int i, count; int num_devices = omp_get_num_devices(); CHECK_OR_RET_FAIL(num_devices == NUM_DEVICES); int *all_devices = (int *)malloc(sizeof(int) * num_devices); for (i = 0; i < num_devices; i++) all_devices[i] = i; omp_memspace_handle_t predef = omp_default_mem_space; omp_allocator_handle_t al = omp_null_allocator; // Test the following API routines. // * omp_get_device_allocator // * omp_get_device_and_host_allocator // * omp_get_devices_allocator // * omp_get_devices_and_host_allocator for (i = 0; i < num_devices; i++) { al = omp_get_device_allocator(i, predef); CHECK_OR_RET_FAIL(al != omp_null_allocator); CHECK_OR_RET_FAIL(test_allocator(al) == EXIT_SUCCESS); al = omp_null_allocator; al = omp_get_device_and_host_allocator(i, predef); CHECK_OR_RET_FAIL(al != omp_null_allocator); CHECK_OR_RET_FAIL(test_allocator(al) == EXIT_SUCCESS); al = omp_null_allocator; for (count = 1; i + count <= num_devices; count++) { int *devices = &all_devices[i]; al = omp_get_devices_allocator(count, devices, predef); CHECK_OR_RET_FAIL(al != omp_null_allocator); CHECK_OR_RET_FAIL(test_allocator(al) == EXIT_SUCCESS); al = omp_null_allocator; al = omp_get_devices_and_host_allocator(count, devices, predef); CHECK_OR_RET_FAIL(al != omp_null_allocator); CHECK_OR_RET_FAIL(test_allocator(al) == EXIT_SUCCESS); al = omp_null_allocator; } } // Test the following API routines. // * omp_get_devices_all_allocator al = omp_get_devices_all_allocator(predef); CHECK_OR_RET_FAIL(al != omp_null_allocator); CHECK_OR_RET_FAIL(test_allocator(al) == EXIT_SUCCESS); free(all_devices); return EXIT_SUCCESS; } // Just test what we can expect from the emulated backend. static int test_sub_mem_space(void) { int i; omp_memspace_handle_t ms = omp_null_mem_space; ms = omp_get_devices_all_memspace(omp_default_mem_space); CHECK_OR_RET_FAIL(ms != omp_null_mem_space); int num_resources = omp_get_memspace_num_resources(ms); CHECK_OR_RET_FAIL(num_resources == NUM_DEVICES); // Check if single-resource sub memspace is correctly returned. for (i = 0; i < num_resources; i++) { omp_memspace_handle_t sub = omp_get_submemspace(ms, 1, &i); CHECK_OR_RET_FAIL(sub != omp_null_mem_space); CHECK_OR_RET_FAIL(sub != ms); int num_sub_resources = omp_get_memspace_num_resources(sub); CHECK_OR_RET_FAIL(num_sub_resources == 1); } // Check if all-resrouce sub memspace is correctly returned. int *resources = (int *)malloc(sizeof(int) * num_resources); for (i = 0; i < num_resources; i++) resources[i] = i; omp_memspace_handle_t sub = omp_get_submemspace(ms, num_resources, resources); CHECK_OR_RET_FAIL(sub != omp_null_mem_space); CHECK_OR_RET_FAIL(sub == ms); return EXIT_SUCCESS; } int main() { int rc = test_mem_space(); CHECK_OR_RET_FAIL(rc == EXIT_SUCCESS); rc = test_mem_allocator(); CHECK_OR_RET_FAIL(rc == EXIT_SUCCESS); rc = test_sub_mem_space(); CHECK_OR_RET_FAIL(rc == EXIT_SUCCESS); return rc; }