| /* | |
| Copyright (c) 2014-2016 Intel Corporation. All Rights Reserved. | |
| Redistribution and use in source and binary forms, with or without | |
| modification, are permitted provided that the following conditions | |
| are met: | |
| * Redistributions of source code must retain the above copyright | |
| notice, this list of conditions and the following disclaimer. | |
| * Redistributions in binary form must reproduce the above copyright | |
| notice, this list of conditions and the following disclaimer in the | |
| documentation and/or other materials provided with the distribution. | |
| * Neither the name of Intel Corporation nor the names of its | |
| contributors may be used to endorse or promote products derived | |
| from this software without specific prior written permission. | |
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
| HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
| LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| */ | |
| #include "cean_util.h" | |
| #include "offload_common.h" | |
| // 1. allocate element of CeanReadRanges type | |
| // 2. initialized it for reading consequently contiguous ranges | |
| // described by "ap" argument | |
| CeanReadRanges * init_read_ranges_arr_desc(const Arr_Desc *ap) | |
| { | |
| CeanReadRanges * res; | |
| // find the max contiguous range | |
| int64_t rank = ap->rank - 1; | |
| int64_t length = ap->dim[rank].size; | |
| for (; rank >= 0; rank--) { | |
| if (ap->dim[rank].stride == 1) { | |
| length *= (ap->dim[rank].upper - ap->dim[rank].lower + 1); | |
| if (rank > 0 && length != ap->dim[rank - 1].size) { | |
| break; | |
| } | |
| } | |
| else { | |
| break; | |
| } | |
| } | |
| res =(CeanReadRanges *)malloc(sizeof(CeanReadRanges) + | |
| (ap->rank - rank) * sizeof(CeanReadDim)); | |
| if (res == NULL) | |
| LIBOFFLOAD_ERROR(c_malloc); | |
| res->arr_desc = const_cast<Arr_Desc*>(ap); | |
| res->current_number = 0; | |
| res->range_size = length; | |
| res->last_noncont_ind = rank; | |
| // calculate number of contiguous ranges inside noncontiguous dimensions | |
| int count = 1; | |
| bool prev_is_cont = true; | |
| int64_t offset = 0; | |
| for (; rank >= 0; rank--) { | |
| res->Dim[rank].count = count; | |
| res->Dim[rank].size = ap->dim[rank].stride * ap->dim[rank].size; | |
| count *= (prev_is_cont && ap->dim[rank].stride == 1? 1 : | |
| (ap->dim[rank].upper - ap->dim[rank].lower + | |
| ap->dim[rank].stride) / ap->dim[rank].stride); | |
| prev_is_cont = false; | |
| offset +=(ap->dim[rank].lower - ap->dim[rank].lindex) * | |
| ap->dim[rank].size; | |
| } | |
| res->range_max_number = count; | |
| res -> ptr = (void*)ap->base; | |
| res -> init_offset = offset; | |
| return res; | |
| } | |
| // check if ranges described by 1 argument could be transferred into ranges | |
| // described by 2-nd one | |
| bool cean_ranges_match( | |
| CeanReadRanges * read_rng1, | |
| CeanReadRanges * read_rng2 | |
| ) | |
| { | |
| return ( read_rng1 == NULL || read_rng2 == NULL || | |
| (read_rng1->range_size % read_rng2->range_size == 0 || | |
| read_rng2->range_size % read_rng1->range_size == 0)); | |
| } | |
| // Set next offset and length and returns true for next range. | |
| // Returns false if the ranges are over. | |
| bool get_next_range( | |
| CeanReadRanges * read_rng, | |
| int64_t *offset | |
| ) | |
| { | |
| if (++read_rng->current_number > read_rng->range_max_number) { | |
| read_rng->current_number = 0; | |
| return false; | |
| } | |
| int rank = 0; | |
| int num = read_rng->current_number - 1; | |
| int64_t cur_offset = 0; | |
| int num_loc; | |
| for (; rank <= read_rng->last_noncont_ind; rank++) { | |
| num_loc = num / read_rng->Dim[rank].count; | |
| cur_offset += num_loc * read_rng->Dim[rank].size; | |
| num = num % read_rng->Dim[rank].count; | |
| } | |
| *offset = cur_offset + read_rng->init_offset; | |
| return true; | |
| } | |
| bool is_arr_desc_contiguous(const Arr_Desc *ap) | |
| { | |
| int64_t rank = ap->rank - 1; | |
| int64_t length = ap->dim[rank].size; | |
| for (; rank >= 0; rank--) { | |
| if (ap->dim[rank].stride > 1 && | |
| ap->dim[rank].upper - ap->dim[rank].lower != 0) { | |
| return false; | |
| } | |
| else if (length != ap->dim[rank].size) { | |
| for (; rank >= 0; rank--) { | |
| if (ap->dim[rank].upper - ap->dim[rank].lower != 0) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| length *= (ap->dim[rank].upper - ap->dim[rank].lower + 1); | |
| } | |
| return true; | |
| } | |
| int64_t cean_get_transf_size(CeanReadRanges * read_rng) | |
| { | |
| return(read_rng->range_max_number * read_rng->range_size); | |
| } | |
| static uint64_t last_left, last_right; | |
| typedef void (*fpp)( | |
| const char *spaces, | |
| uint64_t low, | |
| uint64_t high, | |
| int esize, | |
| bool print_values | |
| ); | |
| static void generate_one_range( | |
| const char *spaces, | |
| uint64_t lrange, | |
| uint64_t rrange, | |
| fpp fp, | |
| int esize, | |
| bool print_values | |
| ) | |
| { | |
| OFFLOAD_TRACE(3, | |
| "%s generate_one_range(lrange=%p, rrange=%p, esize=%d)\n", | |
| spaces, (void*)lrange, (void*)rrange, esize); | |
| if (last_left == -1) { | |
| // First range | |
| last_left = lrange; | |
| } | |
| else { | |
| if (lrange == last_right+1) { | |
| // Extend previous range, don't print | |
| } | |
| else { | |
| (*fp)(spaces, last_left, last_right, esize, print_values); | |
| last_left = lrange; | |
| } | |
| } | |
| last_right = rrange; | |
| } | |
| static bool element_is_contiguous( | |
| uint64_t rank, | |
| const struct Dim_Desc *ddp | |
| ) | |
| { | |
| if (rank == 1) { | |
| return (ddp[0].lower == ddp[0].upper || ddp[0].stride == 1); | |
| } | |
| else { | |
| return ((ddp[0].size == (ddp[1].upper-ddp[1].lower+1)*ddp[1].size) && | |
| element_is_contiguous(rank-1, ddp++)); | |
| } | |
| } | |
| static void generate_mem_ranges_one_rank( | |
| const char *spaces, | |
| uint64_t base, | |
| uint64_t rank, | |
| const struct Dim_Desc *ddp, | |
| fpp fp, | |
| int esize, | |
| bool print_values | |
| ) | |
| { | |
| uint64_t lindex = ddp->lindex; | |
| uint64_t lower = ddp->lower; | |
| uint64_t upper = ddp->upper; | |
| uint64_t stride = ddp->stride; | |
| uint64_t size = ddp->size; | |
| OFFLOAD_TRACE(3, | |
| "%s " | |
| "generate_mem_ranges_one_rank(base=%p, rank=%lld, lindex=%lld, " | |
| "lower=%lld, upper=%lld, stride=%lld, size=%lld, esize=%d)\n", | |
| spaces, (void*)base, rank, lindex, lower, upper, stride, size, esize); | |
| if (element_is_contiguous(rank, ddp)) { | |
| uint64_t lrange, rrange; | |
| lrange = base + (lower-lindex)*size; | |
| rrange = lrange + (upper-lower+1)*size - 1; | |
| generate_one_range(spaces, lrange, rrange, fp, esize, print_values); | |
| } | |
| else { | |
| if (rank == 1) { | |
| for (int i=lower-lindex; i<=upper-lindex; i+=stride) { | |
| uint64_t lrange, rrange; | |
| lrange = base + i*size; | |
| rrange = lrange + size - 1; | |
| generate_one_range(spaces, lrange, rrange, | |
| fp, esize, print_values); | |
| } | |
| } | |
| else { | |
| for (int i=lower-lindex; i<=upper-lindex; i+=stride) { | |
| generate_mem_ranges_one_rank( | |
| spaces, base+i*size, rank-1, ddp+1, | |
| fp, esize, print_values); | |
| } | |
| } | |
| } | |
| } | |
| static void generate_mem_ranges( | |
| const char *spaces, | |
| const Arr_Desc *adp, | |
| bool deref, | |
| fpp fp, | |
| bool print_values | |
| ) | |
| { | |
| uint64_t esize; | |
| OFFLOAD_TRACE(3, | |
| "%s " | |
| "generate_mem_ranges(adp=%p, deref=%d, fp)\n", | |
| spaces, adp, deref); | |
| last_left = -1; | |
| last_right = -2; | |
| // Element size is derived from last dimension | |
| esize = adp->dim[adp->rank-1].size; | |
| generate_mem_ranges_one_rank( | |
| // For c_cean_var the base addr is the address of the data | |
| // For c_cean_var_ptr the base addr is dereferenced to get to the data | |
| spaces, deref ? *((uint64_t*)(adp->base)) : adp->base, | |
| adp->rank, &adp->dim[0], fp, esize, print_values); | |
| (*fp)(spaces, last_left, last_right, esize, print_values); | |
| } | |
| // returns offset and length of the data to be transferred | |
| void __arr_data_offset_and_length( | |
| const Arr_Desc *adp, | |
| int64_t &offset, | |
| int64_t &length | |
| ) | |
| { | |
| int64_t rank = adp->rank - 1; | |
| int64_t size = adp->dim[rank].size; | |
| int64_t r_off = 0; // offset from right boundary | |
| // find the rightmost dimension which takes just part of its | |
| // range. We define it if the size of left rank is not equal | |
| // the range's length between upper and lower boungaries | |
| while (rank > 0) { | |
| size *= (adp->dim[rank].upper - adp->dim[rank].lower + 1); | |
| if (size != adp->dim[rank - 1].size) { | |
| break; | |
| } | |
| rank--; | |
| } | |
| offset = (adp->dim[rank].lower - adp->dim[rank].lindex) * | |
| adp->dim[rank].size; | |
| // find gaps both from the left - offset and from the right - r_off | |
| for (rank--; rank >= 0; rank--) { | |
| offset += (adp->dim[rank].lower - adp->dim[rank].lindex) * | |
| adp->dim[rank].size; | |
| r_off += adp->dim[rank].size - | |
| (adp->dim[rank + 1].upper - adp->dim[rank + 1].lindex + 1) * | |
| adp->dim[rank + 1].size; | |
| } | |
| length = (adp->dim[0].upper - adp->dim[0].lindex + 1) * | |
| adp->dim[0].size - offset - r_off; | |
| } | |
| #if OFFLOAD_DEBUG > 0 | |
| static void print_range( | |
| const char *spaces, | |
| uint64_t low, | |
| uint64_t high, | |
| int esize, | |
| bool print_values | |
| ) | |
| { | |
| char buffer[1024]; | |
| char number[32]; | |
| OFFLOAD_TRACE(3, "%s print_range(low=%p, high=%p, esize=%d)\n", | |
| spaces, (void*)low, (void*)high, esize); | |
| if (console_enabled < 4 || !print_values) { | |
| return; | |
| } | |
| OFFLOAD_TRACE(4, "%s values:\n", spaces); | |
| int count = 0; | |
| buffer[0] = '\0'; | |
| while (low <= high) | |
| { | |
| switch (esize) | |
| { | |
| case 1: | |
| sprintf(number, "%d ", *((char *)low)); | |
| low += 1; | |
| break; | |
| case 2: | |
| sprintf(number, "%d ", *((short *)low)); | |
| low += 2; | |
| break; | |
| case 4: | |
| sprintf(number, "%d ", *((int *)low)); | |
| low += 4; | |
| break; | |
| default: | |
| sprintf(number, "0x%016x ", *((uint64_t *)low)); | |
| low += 8; | |
| break; | |
| } | |
| strcat(buffer, number); | |
| count++; | |
| if (count == 10) { | |
| OFFLOAD_TRACE(4, "%s %s\n", spaces, buffer); | |
| count = 0; | |
| buffer[0] = '\0'; | |
| } | |
| } | |
| if (count != 0) { | |
| OFFLOAD_TRACE(4, "%s %s\n", spaces, buffer); | |
| } | |
| } | |
| void __arr_desc_dump( | |
| const char *spaces, | |
| const char *name, | |
| const Arr_Desc *adp, | |
| bool deref, | |
| bool print_values | |
| ) | |
| { | |
| OFFLOAD_TRACE(2, "%s%s CEAN expression %p\n", spaces, name, adp); | |
| if (adp != 0) { | |
| OFFLOAD_TRACE(2, "%s base=%llx, rank=%lld\n", | |
| spaces, adp->base, adp->rank); | |
| for (int i = 0; i < adp->rank; i++) { | |
| OFFLOAD_TRACE(2, | |
| "%s dimension %d: size=%lld, lindex=%lld, " | |
| "lower=%lld, upper=%lld, stride=%lld\n", | |
| spaces, i, adp->dim[i].size, adp->dim[i].lindex, | |
| adp->dim[i].lower, adp->dim[i].upper, | |
| adp->dim[i].stride); | |
| } | |
| // For c_cean_var the base addr is the address of the data | |
| // For c_cean_var_ptr the base addr is dereferenced to get to the data | |
| generate_mem_ranges(spaces, adp, deref, &print_range, print_values); | |
| } | |
| } | |
| void noncont_struct_dump( | |
| const char *spaces, | |
| const char *name, | |
| struct NonContigDesc *desc_p) | |
| { | |
| OFFLOAD_TRACE(2, "%s%s NonCont Struct expression %p\n", | |
| spaces, name, desc_p->base); | |
| if (desc_p) { | |
| OFFLOAD_TRACE(2, "%s%s base=%p\n", spaces, name, desc_p->base); | |
| for (int i = 0; i < desc_p->interval_cnt; i++) { | |
| OFFLOAD_TRACE(2,"%s dimension %d: lower=%lld, size=%lld\n", | |
| spaces, i, desc_p->interval[i].lower, desc_p->interval[i].size); | |
| } | |
| } | |
| } | |
| int64_t get_noncont_struct_size(struct NonContigDesc *desc_p) | |
| { | |
| int index = desc_p->interval_cnt - 1; | |
| return(desc_p->interval[index].lower + desc_p->interval[index].size); | |
| } | |
| #endif // OFFLOAD_DEBUG |