/* Test "subset" subarray mappings { dg-additional-options "-DOPENACC_RUNTIME" } using OpenACC Runtime Library routines, { dg-additional-options "-DPOINTERS" } using pointers. */ /* { dg-skip-if "" { *-*-* } { "-DACC_MEM_SHARED=1" } } */ #if OPENACC_RUNTIME #elif OPENACC_DIRECTIVES #else # error #endif #if POINTERS #elif ARRAYS #else # error #endif #include #include #include #include #include #include #include static bool cb_ev_alloc_expected; static size_t cb_ev_alloc_bytes; static const void *cb_ev_alloc_device_ptr; static void cb_ev_alloc (acc_prof_info *prof_info, acc_event_info *event_info, acc_api_info *api_info) { assert (cb_ev_alloc_expected); cb_ev_alloc_expected = false; cb_ev_alloc_bytes = event_info->data_event.bytes; cb_ev_alloc_device_ptr = event_info->data_event.device_ptr; } static bool cb_ev_free_expected; static const void *cb_ev_free_device_ptr; static void cb_ev_free (acc_prof_info *prof_info, acc_event_info *event_info, acc_api_info *api_info) { assert (cb_ev_free_expected); cb_ev_free_expected = false; cb_ev_free_device_ptr = event_info->data_event.device_ptr; } /* Match the alignment processing that 'libgomp/target.c:gomp_map_vars_internal' is doing; simplified, not considering special alignment requirements of certain data types. */ static size_t aligned_size (size_t tgt_size) { size_t tgt_align = sizeof (void *); return tgt_size + tgt_align - 1; } static const void * aligned_address (const void *tgt_start) { size_t tgt_align = sizeof (void *); return (void *) (((uintptr_t) tgt_start + tgt_align - 1) & ~(tgt_align - 1)); } #define SIZE 1024 #define SUBSET 32 static void f1 (void) { cb_ev_alloc_expected = false; cb_ev_free_expected = false; acc_prof_register (acc_ev_alloc, cb_ev_alloc, acc_reg); acc_prof_register (acc_ev_free, cb_ev_free, acc_reg); #if POINTERS char* myblock = (char *) malloc (SIZE); #else char myblock[SIZE]; #endif int i; void *dst; for (i = 0; i < SIZE; i++) myblock[i] = i; cb_ev_alloc_expected = true; #if OPENACC_RUNTIME dst = acc_copyin (myblock, SIZE); #else # if POINTERS # pragma acc enter data copyin (myblock[0:SIZE]) # else # pragma acc enter data copyin (myblock) # endif dst = acc_deviceptr (myblock); #endif assert (dst); assert (!cb_ev_alloc_expected); assert (cb_ev_alloc_bytes == aligned_size (SIZE)); assert (aligned_address (cb_ev_alloc_device_ptr) == dst); for (i = 0; i < SIZE; i += SUBSET) { void *partdst = acc_deviceptr (&myblock[i]); assert ((uintptr_t) partdst == (uintptr_t) dst + i); assert (acc_hostptr (partdst) == &myblock[i]); } for (i = 0; i < SIZE; i += SUBSET) { void *partdst; #if OPENACC_RUNTIME partdst = acc_pcopyin (&myblock[i], SUBSET); #else # pragma acc enter data pcopyin (myblock[i:SUBSET]) partdst = acc_deviceptr (&myblock[i]); #endif assert ((uintptr_t) partdst == (uintptr_t) dst + i); } /* Dereference first half. */ for (i = 0; i < 512; i += SUBSET) { assert (acc_is_present (&myblock[i], SUBSET)); assert (acc_is_present (myblock, SIZE)); #if OPENACC_RUNTIME acc_delete (&myblock[i], SUBSET); #else # pragma acc exit data delete (myblock[i:SUBSET]) #endif assert (acc_is_present (&myblock[i], SUBSET)); assert (acc_is_present (myblock, SIZE)); } /* Dereference all. */ #if OPENACC_RUNTIME acc_delete (myblock, SIZE); #else # if POINTERS # pragma acc exit data delete (myblock[0:SIZE]) # else # pragma acc exit data delete (myblock) # endif #endif /* Expect it's still present. */ assert (acc_is_present (myblock, SIZE)); /* Dereference second half. */ for (i = 512; i < SIZE; i += SUBSET) { bool last = i >= SIZE - SUBSET; assert (acc_is_present (&myblock[i], SUBSET)); assert (acc_is_present (myblock, SIZE)); if (last) cb_ev_free_expected = true; #if OPENACC_RUNTIME acc_delete (&myblock[i], SUBSET); #else # pragma acc exit data delete (myblock[i:SUBSET]) #endif assert (!cb_ev_free_expected); if (last) assert (cb_ev_free_device_ptr == cb_ev_alloc_device_ptr); assert (acc_is_present (&myblock[i], SUBSET) != last); assert (acc_is_present (myblock, SIZE) != last); } /* Expect it's all gone now. */ for (i = 512; i < SIZE; i += SUBSET) assert (!acc_is_present (&myblock[i], SUBSET)); assert (!acc_is_present (myblock, SIZE)); assert (!acc_is_present (myblock, 1)); #if POINTERS free (myblock); #endif acc_prof_unregister (acc_ev_alloc, cb_ev_alloc, acc_reg); acc_prof_unregister (acc_ev_free, cb_ev_free, acc_reg); } static void f2 (void) { cb_ev_alloc_expected = false; cb_ev_free_expected = false; acc_prof_register (acc_ev_alloc, cb_ev_alloc, acc_reg); acc_prof_register (acc_ev_free, cb_ev_free, acc_reg); #if POINTERS char *block1 = (char *) malloc (SIZE); char *block2 = (char *) malloc (SIZE); char *block3 = (char *) malloc (SIZE); #else char block1[SIZE]; char block2[SIZE]; char block3[SIZE]; #endif int i; for (i = 0; i < SIZE; i++) block1[i] = block2[i] = block3[i] = i; cb_ev_alloc_expected = true; #if POINTERS # pragma acc data copyin(block1[0:SIZE], block2[0:SIZE], block3[0:SIZE]) #else # pragma acc data copyin(block1, block2, block3) #endif { void *block1_d = acc_deviceptr (block1); void *block2_d = acc_deviceptr (block2); void *block3_d = acc_deviceptr (block3); assert (!cb_ev_alloc_expected); /* 'block1', 'block2', 'block3' get mapped in one device memory object, in reverse order. */ assert (cb_ev_alloc_bytes == aligned_size (3 * SIZE)); assert ((void *) ((uintptr_t) aligned_address (cb_ev_alloc_device_ptr) + 2 * SIZE) == block1_d); assert ((void *) ((uintptr_t) aligned_address (cb_ev_alloc_device_ptr) + 1 * SIZE) == block2_d); assert ((void *) ((uintptr_t) aligned_address (cb_ev_alloc_device_ptr) + 0 * SIZE) == block3_d); for (i = 0; i < SIZE; i += SUBSET) { void *block2_part_d; #if OPENACC_RUNTIME block2_part_d = acc_pcopyin (&block2[i], SUBSET); #else # pragma acc enter data pcopyin (block2[i:SUBSET]) block2_part_d = acc_deviceptr (&block2[i]); #endif assert ((uintptr_t) block2_part_d == (uintptr_t) block2_d + i); } } /* The mappings have been removed, but the device memory object has not yet been 'free'd. */ assert (!acc_is_present (block1, SIZE)); assert (acc_is_present (block2, SIZE)); assert (!acc_is_present (block3, SIZE)); for (i = 0; i < SIZE; i += SUBSET) { bool last = i >= SIZE - SUBSET; assert (acc_is_present (block2, SIZE)); if (last) cb_ev_free_expected = true; #if OPENACC_RUNTIME acc_delete (&block2[i], SUBSET); #else # pragma acc exit data delete (block2[i:SUBSET]) #endif assert (!cb_ev_free_expected); if (last) assert (cb_ev_free_device_ptr == cb_ev_alloc_device_ptr); } assert (!acc_is_present (block1, SIZE)); assert (!acc_is_present (block2, SIZE)); assert (!acc_is_present (block3, SIZE)); #if POINTERS free (block1); free (block2); free (block3); #endif acc_prof_unregister (acc_ev_alloc, cb_ev_alloc, acc_reg); acc_prof_unregister (acc_ev_free, cb_ev_free, acc_reg); } static void f3 () { cb_ev_alloc_expected = false; cb_ev_free_expected = false; acc_prof_register (acc_ev_alloc, cb_ev_alloc, acc_reg); acc_prof_register (acc_ev_free, cb_ev_free, acc_reg); #if POINTERS char *h = (char *) malloc (SIZE); #else char h[SIZE]; #endif char *d1; cb_ev_alloc_expected = true; #if OPENACC_RUNTIME d1 = (char *) acc_present_or_create (h, SIZE); #else # if POINTERS # pragma acc enter data present_or_create (h[0:SIZE]) # else # pragma acc enter data present_or_create (h) # endif d1 = (char *) acc_deviceptr (h); #endif assert (d1); assert (!cb_ev_alloc_expected); assert (cb_ev_alloc_bytes == aligned_size (SIZE)); assert (aligned_address (cb_ev_alloc_device_ptr) == d1); assert (acc_is_present (h, SIZE)); assert (acc_is_present (&h[2], SIZE - 2)); char *d2; #if OPENACC_RUNTIME d2 = (char *) acc_present_or_create (&h[2], SIZE - 2); #else # pragma acc enter data present_or_create (h[2:SIZE - 2]) d2 = (char *) acc_deviceptr (&h[2]); #endif assert (d2); assert (d1 == d2 - 2); assert (acc_is_present (h, SIZE)); assert (acc_is_present (&h[2], SIZE - 2)); d2 = (char *) acc_deviceptr (&h[2]); assert (d1 == d2 - 2); #if OPENACC_RUNTIME acc_delete (&h[2], SIZE - 2); #else # pragma acc exit data delete (h[2:SIZE - 2]) #endif assert (acc_is_present (h, SIZE)); assert (acc_is_present (&h[2], SIZE - 2)); cb_ev_free_expected = true; #if OPENACC_RUNTIME acc_delete (h, SIZE); #else # if POINTERS # pragma acc exit data delete (h[0:SIZE]) # else # pragma acc exit data delete (h) # endif #endif assert (!cb_ev_free_expected); assert (cb_ev_free_device_ptr == cb_ev_alloc_device_ptr); assert (!acc_is_present (h, SIZE)); assert (!acc_is_present (&h[2], SIZE - 2)); assert (!acc_is_present (h, 1)); # if POINTERS free (h); #endif acc_prof_unregister (acc_ev_alloc, cb_ev_alloc, acc_reg); acc_prof_unregister (acc_ev_free, cb_ev_free, acc_reg); } /* Based on what used to be 'libgomp.oacc-c-c++-common/lib-22.c'. */ static void f_lib_22 (void) { cb_ev_alloc_expected = false; cb_ev_free_expected = false; acc_prof_register (acc_ev_alloc, cb_ev_alloc, acc_reg); acc_prof_register (acc_ev_free, cb_ev_free, acc_reg); const int c0 = 0; const int c1 = 1; #if POINTERS char *h = (char *) malloc (SIZE); #else char h[SIZE]; #endif memset (h, c0, SIZE); void *d; cb_ev_alloc_expected = true; #if OPENACC_RUNTIME d = acc_copyin (h, SIZE); #else # if POINTERS # pragma acc enter data copyin (h[0:SIZE]) # else # pragma acc enter data copyin (h) # endif d = acc_deviceptr (h); #endif assert (d); assert (!cb_ev_alloc_expected); assert (cb_ev_alloc_bytes == aligned_size (SIZE)); assert (aligned_address (cb_ev_alloc_device_ptr) == d); /* Overwrite the local memory. */ memset (h, c1, SIZE); /* Now 'copyout' not the whole but only a "subset" subarray, missing one SUBSET at the beginning, and half a SUBSET at the end... */ cb_ev_free_expected = true; #if OPENACC_RUNTIME acc_copyout (h + SUBSET, SIZE - SUBSET - SUBSET / 2); #else # pragma acc exit data copyout (h[SUBSET:SIZE - SUBSET - SUBSET / 2]) #endif /* ..., yet, expect the device memory object to be 'free'd... */ assert (!cb_ev_free_expected); assert (cb_ev_free_device_ptr == cb_ev_alloc_device_ptr); /* ..., and the mapping to be removed... */ assert (!acc_is_present (h, SIZE)); assert (!acc_is_present (&h[SUBSET], SIZE - SUBSET - SUBSET / 2)); assert (!acc_is_present (h, 1)); /* ..., but the 'copyout'ed device memory to correspond to just the "subset" subarray. */ for (size_t i = 0; i < SIZE; ++i) { if (i < SUBSET) assert (h[i] == c1); else if (i < SIZE - SUBSET / 2) assert (h[i] == c0); else if (i < SIZE) assert (h[i] == c1); } #if POINTERS free (h); #endif acc_prof_unregister (acc_ev_alloc, cb_ev_alloc, acc_reg); acc_prof_unregister (acc_ev_free, cb_ev_free, acc_reg); } /* Based on what used to be 'libgomp.oacc-c-c++-common/lib-30.c'. */ static void f_lib_30 (void) { cb_ev_alloc_expected = false; cb_ev_free_expected = false; acc_prof_register (acc_ev_alloc, cb_ev_alloc, acc_reg); acc_prof_register (acc_ev_free, cb_ev_free, acc_reg); #if POINTERS char *h = (char *) malloc (SIZE); #else char h[SIZE]; #endif memset (h, 0, SIZE); void *d; cb_ev_alloc_expected = true; #if OPENACC_RUNTIME d = acc_create (h, SIZE); #else # if POINTERS # pragma acc enter data create (h[0:SIZE]) # else # pragma acc enter data create (h) # endif d = acc_deviceptr (h); #endif assert (d); assert (!cb_ev_alloc_expected); assert (cb_ev_alloc_bytes == aligned_size (SIZE)); assert (aligned_address (cb_ev_alloc_device_ptr) == d); /* We 'delete' not the whole but only a "subset" subarray... */ cb_ev_free_expected = true; #if OPENACC_RUNTIME acc_delete (h, SIZE - SUBSET); #else # pragma acc exit data delete (h[0:SIZE - SUBSET]) #endif /* ..., yet, expect the device memory object to be 'free'd... */ assert (!cb_ev_free_expected); assert (cb_ev_free_device_ptr == cb_ev_alloc_device_ptr); /* ..., and the mapping to be removed. */ assert (!acc_is_present (h, SIZE)); assert (!acc_is_present (h, SIZE - SUBSET)); assert (!acc_is_present (h, 1)); #if POINTERS free (h); #endif acc_prof_unregister (acc_ev_alloc, cb_ev_alloc, acc_reg); acc_prof_unregister (acc_ev_free, cb_ev_free, acc_reg); } int main () { f1 (); f2 (); f3 (); f_lib_22 (); f_lib_30 (); return 0; }