/* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil ; -*- */ /* * (C) 2011 by Argonne National Laboratory. * See COPYRIGHT in top-level directory. */ #include "mpiimpl.h" /* -- Begin Profiling Symbol Block for routine MPI_Get_elements_x */ #if defined(HAVE_PRAGMA_WEAK) #pragma weak MPI_Get_elements_x = PMPI_Get_elements_x #elif defined(HAVE_PRAGMA_HP_SEC_DEF) #pragma _HP_SECONDARY_DEF PMPI_Get_elements_x MPI_Get_elements_x #elif defined(HAVE_PRAGMA_CRI_DUP) #pragma _CRI duplicate MPI_Get_elements_x as PMPI_Get_elements_x #elif defined(HAVE_WEAK_ATTRIBUTE) int MPI_Get_elements_x(const MPI_Status * status, MPI_Datatype datatype, MPI_Count * count) __attribute__ ((weak, alias("PMPI_Get_elements_x"))); #endif /* -- End Profiling Symbol Block */ /* Internal helper routines. If you want to get the number of elements from * within the MPI library, call MPIR_Get_elements_x_impl instead. */ PMPI_LOCAL MPI_Count MPIR_Type_get_basic_type_elements(MPI_Count * bytes_p, MPI_Count count, MPI_Datatype datatype); PMPI_LOCAL MPI_Count MPIR_Type_get_elements(MPI_Count * bytes_p, MPI_Count count, MPI_Datatype datatype); /* Define MPICH_MPI_FROM_PMPI if weak symbols are not supported to build the MPI routines */ #ifndef MPICH_MPI_FROM_PMPI #undef MPI_Get_elements_x #define MPI_Get_elements_x PMPI_Get_elements_x /* any non-MPI functions go here, especially non-static ones */ /* MPIR_Type_get_basic_type_elements() * * Arguments: * - bytes_p - input/output byte count * - count - maximum number of this type to subtract from the bytes; a count * of -1 indicates use as many as we like * - datatype - input datatype * * Returns number of elements available given the two constraints of number of * bytes and count of types. Also reduces the byte count by the amount taken * up by the types. * * Assumptions: * - the type passed to this function must be a basic *or* a pairtype * (which aren't basic types) * - the count is not zero (otherwise we can't tell between a "no more * complete types" case and a "zero count" case) * * As per section 4.9.3 of the MPI 1.1 specification, the two-part reduction * types are to be treated as structs of the constituent types. So we have to * do something special to handle them correctly in here. * * As per section 3.12.5 get_count and get_elements report the same value for * basic datatypes; I'm currently interpreting this to *not* include these * reduction types, as they are considered structs. */ PMPI_LOCAL MPI_Count MPIR_Type_get_basic_type_elements(MPI_Count * bytes_p, MPI_Count count, MPI_Datatype datatype) { MPI_Count elements, usable_bytes, used_bytes, type1_sz, type2_sz; if (count == 0) return 0; /* determine the maximum number of bytes we should take from the * byte count. */ if (count < 0) { usable_bytes = *bytes_p; } else { usable_bytes = MPL_MIN(*bytes_p, count * MPIR_Datatype_get_basic_size(datatype)); } switch (datatype) { /* we don't get valid fortran datatype handles in all cases... */ #ifdef HAVE_FORTRAN_BINDING case MPI_2REAL: type1_sz = type2_sz = MPIR_Datatype_get_basic_size(MPI_REAL); break; case MPI_2DOUBLE_PRECISION: type1_sz = type2_sz = MPIR_Datatype_get_basic_size(MPI_DOUBLE_PRECISION); break; case MPI_2INTEGER: type1_sz = type2_sz = MPIR_Datatype_get_basic_size(MPI_INTEGER); break; #endif case MPI_2INT: type1_sz = type2_sz = MPIR_Datatype_get_basic_size(MPI_INT); break; case MPI_FLOAT_INT: type1_sz = MPIR_Datatype_get_basic_size(MPI_FLOAT); type2_sz = MPIR_Datatype_get_basic_size(MPI_INT); break; case MPI_DOUBLE_INT: type1_sz = MPIR_Datatype_get_basic_size(MPI_DOUBLE); type2_sz = MPIR_Datatype_get_basic_size(MPI_INT); break; case MPI_LONG_INT: type1_sz = MPIR_Datatype_get_basic_size(MPI_LONG); type2_sz = MPIR_Datatype_get_basic_size(MPI_INT); break; case MPI_SHORT_INT: type1_sz = MPIR_Datatype_get_basic_size(MPI_SHORT); type2_sz = MPIR_Datatype_get_basic_size(MPI_INT); break; case MPI_LONG_DOUBLE_INT: type1_sz = MPIR_Datatype_get_basic_size(MPI_LONG_DOUBLE); type2_sz = MPIR_Datatype_get_basic_size(MPI_INT); break; default: /* all other types. this is more complicated than * necessary for handling these types, but it puts us in the * same code path for all the basics, so we stick with it. */ type1_sz = type2_sz = MPIR_Datatype_get_basic_size(datatype); break; } /* determine the number of elements in the region */ elements = 2 * (usable_bytes / (type1_sz + type2_sz)); if (usable_bytes % (type1_sz + type2_sz) >= type1_sz) elements++; /* determine how many bytes we used up with those elements */ used_bytes = ((elements / 2) * (type1_sz + type2_sz)); if (elements % 2 == 1) used_bytes += type1_sz; *bytes_p -= used_bytes; return elements; } /* MPIR_Type_get_elements * * Arguments: * - bytes_p - input/output byte count * - count - maximum number of this type to subtract from the bytes; a count * of <0 indicates use as many as we like * - datatype - input datatype * * Returns number of elements available given the two constraints of number of * bytes and count of types. Also reduces the byte count by the amount taken * up by the types. * * This is called from MPI_Get_elements() when it sees a type with multiple * element types (datatype_ptr->element_sz = -1). This function calls itself too. */ PMPI_LOCAL MPI_Count MPIR_Type_get_elements(MPI_Count * bytes_p, MPI_Count count, MPI_Datatype datatype) { MPIR_Datatype *datatype_ptr = NULL; MPIR_Datatype_get_ptr(datatype, datatype_ptr); /* invalid if builtin */ /* if we have gotten down to a type with only one element type, * call MPIR_Type_get_basic_type_elements() and return. */ if (HANDLE_GET_KIND(datatype) == HANDLE_KIND_BUILTIN || datatype == MPI_FLOAT_INT || datatype == MPI_DOUBLE_INT || datatype == MPI_LONG_INT || datatype == MPI_SHORT_INT || datatype == MPI_LONG_DOUBLE_INT) { return MPIR_Type_get_basic_type_elements(bytes_p, count, datatype); } else if (datatype_ptr->builtin_element_size >= 0) { MPI_Datatype basic_type = MPI_DATATYPE_NULL; MPIR_Datatype_get_basic_type(datatype_ptr->basic_type, basic_type); return MPIR_Type_get_basic_type_elements(bytes_p, count * datatype_ptr->n_builtin_elements, basic_type); } else { /* we have bytes left and still don't have a single element size; must * recurse. */ int i, j, *ints; MPI_Count typecount = 0, nr_elements = 0, last_nr_elements; MPI_Aint *aints; MPI_Datatype *types; /* Establish locations of arrays */ MPIR_Type_access_contents(datatype_ptr->handle, &ints, &aints, &types); if (!ints || !aints || !types) return MPI_ERR_TYPE; switch (datatype_ptr->contents->combiner) { case MPI_COMBINER_NAMED: case MPI_COMBINER_DUP: case MPI_COMBINER_RESIZED: return MPIR_Type_get_elements(bytes_p, count, *types); break; case MPI_COMBINER_CONTIGUOUS: case MPI_COMBINER_VECTOR: case MPI_COMBINER_HVECTOR_INTEGER: case MPI_COMBINER_HVECTOR: /* count is first in ints array */ return MPIR_Type_get_elements(bytes_p, count * (*ints), *types); break; case MPI_COMBINER_INDEXED_BLOCK: case MPI_COMBINER_HINDEXED_BLOCK: /* count is first in ints array, blocklength is second */ return MPIR_Type_get_elements(bytes_p, count * ints[0] * ints[1], *types); break; case MPI_COMBINER_INDEXED: case MPI_COMBINER_HINDEXED_INTEGER: case MPI_COMBINER_HINDEXED: for (i = 0; i < (*ints); i++) { /* add up the blocklengths to get a max. # of the next type */ typecount += ints[i + 1]; } return MPIR_Type_get_elements(bytes_p, count * typecount, *types); break; case MPI_COMBINER_STRUCT_INTEGER: case MPI_COMBINER_STRUCT: /* In this case we can't simply multiply the count of the next * type by the count of the current type, because we need to * cycle through the types just as the struct would. thus the * nested loops. * * We need to keep going until we get less elements than expected * or we run out of bytes. */ last_nr_elements = 1; /* seed value */ for (j = 0; (count < 0 || j < count) && *bytes_p > 0 && last_nr_elements > 0; j++) { /* recurse on each type; bytes are reduced in calls */ for (i = 0; i < (*ints); i++) { /* skip zero-count elements of the struct */ if (ints[i + 1] == 0) continue; last_nr_elements = MPIR_Type_get_elements(bytes_p, ints[i + 1], types[i]); nr_elements += last_nr_elements; MPIR_Assert(last_nr_elements >= 0); if (last_nr_elements < ints[i + 1]) break; } } return nr_elements; break; case MPI_COMBINER_SUBARRAY: case MPI_COMBINER_DARRAY: case MPI_COMBINER_F90_REAL: case MPI_COMBINER_F90_COMPLEX: case MPI_COMBINER_F90_INTEGER: default: /* --BEGIN ERROR HANDLING-- */ MPIR_Assert(0); return -1; break; /* --END ERROR HANDLING-- */ } } } #undef FUNCNAME #define FUNCNAME MPIR_Get_elements_x_impl #undef FCNAME #define FCNAME MPL_QUOTE(FUNCNAME) /* MPIR_Get_elements_x_impl * * Arguments: * - byte_count - input/output byte count * - datatype - input datatype * - elements - Number of basic elements this byte_count would contain * * Returns number of elements available given the two constraints of number of * bytes and count of types. Also reduces the byte count by the amount taken * up by the types. */ int MPIR_Get_elements_x_impl(MPI_Count * byte_count, MPI_Datatype datatype, MPI_Count * elements) { int mpi_errno = MPI_SUCCESS; MPIR_Datatype *datatype_ptr = NULL; if (HANDLE_GET_KIND(datatype) != HANDLE_KIND_BUILTIN) { MPIR_Datatype_get_ptr(datatype, datatype_ptr); } /* three cases: * - nice, simple, single element type * - derived type with a zero size * - type with multiple element types (nastiest) */ if (HANDLE_GET_KIND(datatype) == HANDLE_KIND_BUILTIN || (datatype_ptr->builtin_element_size != -1 && datatype_ptr->size > 0)) { /* QUESTION: WHAT IF SOMEONE GAVE US AN MPI_UB OR MPI_LB??? */ /* in both cases we do not limit the number of types that might * be in bytes */ if (HANDLE_GET_KIND(datatype) != HANDLE_KIND_BUILTIN) { MPI_Datatype basic_type = MPI_DATATYPE_NULL; MPIR_Datatype_get_basic_type(datatype_ptr->basic_type, basic_type); *elements = MPIR_Type_get_basic_type_elements(byte_count, -1, basic_type); } else { /* Behaves just like MPI_Get_Count in the predefined case */ MPI_Count size; MPIR_Datatype_get_size_macro(datatype, size); if ((*byte_count % size) != 0) *elements = MPI_UNDEFINED; else *elements = MPIR_Type_get_basic_type_elements(byte_count, -1, datatype); } MPIR_Assert(*byte_count >= 0); } else if (datatype_ptr->size == 0) { if (*byte_count > 0) { /* --BEGIN ERROR HANDLING-- */ /* datatype size of zero and count > 0 should never happen. */ (*elements) = MPI_UNDEFINED; /* --END ERROR HANDLING-- */ } else { /* This is ambiguous. However, discussions on MPI Forum * reached a consensus that this is the correct return * value */ (*elements) = 0; } } else { /* derived type with weird element type or weird size */ MPIR_Assert(datatype_ptr->builtin_element_size == -1); *elements = MPIR_Type_get_elements(byte_count, -1, datatype); } return mpi_errno; } #endif /* MPICH_MPI_FROM_PMPI */ #undef FUNCNAME #define FUNCNAME MPI_Get_elements_x #undef FCNAME #define FCNAME MPL_QUOTE(FUNCNAME) /* N.B. "count" is the name mandated by the MPI-3 standard, but it should * probably be called "elements" instead and is handled that way in the _impl * routine [goodell@ 2012-11-05 */ /*@ MPI_Get_elements_x - Returns the number of basic elements in a datatype Input Parameters: + status - return status of receive operation (Status) - datatype - datatype used by receive operation (handle) Output Parameters: . count - number of received basic elements (integer) .N ThreadSafe .N Fortran .N Errors @*/ int MPI_Get_elements_x(const MPI_Status * status, MPI_Datatype datatype, MPI_Count * count) { int mpi_errno = MPI_SUCCESS; MPI_Count byte_count; MPIR_FUNC_TERSE_STATE_DECL(MPID_STATE_MPI_GET_ELEMENTS_X); MPID_THREAD_CS_ENTER(GLOBAL, MPIR_THREAD_GLOBAL_ALLFUNC_MUTEX); MPIR_FUNC_TERSE_ENTER(MPID_STATE_MPI_GET_ELEMENTS_X); /* Validate parameters, especially handles needing to be converted */ #ifdef HAVE_ERROR_CHECKING { MPID_BEGIN_ERROR_CHECKS; { MPIR_ERRTEST_DATATYPE(datatype, "datatype", mpi_errno); /* TODO more checks may be appropriate */ if (mpi_errno != MPI_SUCCESS) goto fn_fail; } MPID_END_ERROR_CHECKS; } #endif /* HAVE_ERROR_CHECKING */ /* Convert MPI object handles to object pointers */ /* Validate parameters and objects (post conversion) */ #ifdef HAVE_ERROR_CHECKING { MPID_BEGIN_ERROR_CHECKS; { if (HANDLE_GET_KIND(datatype) != HANDLE_KIND_BUILTIN) { MPIR_Datatype *datatype_ptr = NULL; MPIR_Datatype_get_ptr(datatype, datatype_ptr); MPIR_Datatype_valid_ptr(datatype_ptr, mpi_errno); MPIR_Datatype_committed_ptr(datatype_ptr, mpi_errno); } /* TODO more checks may be appropriate (counts, in_place, buffer aliasing, etc) */ if (mpi_errno != MPI_SUCCESS) goto fn_fail; } MPID_END_ERROR_CHECKS; } #endif /* HAVE_ERROR_CHECKING */ /* ... body of routine ... */ byte_count = MPIR_STATUS_GET_COUNT(*status); mpi_errno = MPIR_Get_elements_x_impl(&byte_count, datatype, count); if (mpi_errno) MPIR_ERR_POP(mpi_errno); /* ... end of body of routine ... */ fn_exit: MPIR_FUNC_TERSE_EXIT(MPID_STATE_MPI_GET_ELEMENTS_X); MPID_THREAD_CS_EXIT(GLOBAL, MPIR_THREAD_GLOBAL_ALLFUNC_MUTEX); return mpi_errno; fn_fail: /* --BEGIN ERROR HANDLING-- */ #ifdef HAVE_ERROR_CHECKING { mpi_errno = MPIR_Err_create_code(mpi_errno, MPIR_ERR_RECOVERABLE, FCNAME, __LINE__, MPI_ERR_OTHER, "**mpi_get_elements_x", "**mpi_get_elements_x %p %D %p", status, datatype, count); } #endif mpi_errno = MPIR_Err_return_comm(NULL, FCNAME, mpi_errno); goto fn_exit; /* --END ERROR HANDLING-- */ }