diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 70e408047f8a797aca84db47456e6f382e87b49e..476d4609a610af7e705bb5702d528e1de37762c2 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -28,7 +28,7 @@ make:generic: - /^wip.*/ - /^WIP.*/ variables: - CFLAGS: "-std=c99 -pedantic -Wall -Wextra -Werror -Wno-unused-but-set-parameter" + CFLAGS: "-std=c99 -pedantic -Wall -Wextra -Werror -Wno-pointer-arith -Wno-unused-but-set-parameter" script: - ./autogen.sh - mkdir build @@ -48,7 +48,7 @@ make:out-of-tree: - /^wip.*/ - /^WIP.*/ variables: - CFLAGS: "-std=c99 -pedantic -Wall -Wextra -Werror -Wno-unused-but-set-parameter" + CFLAGS: "-std=c99 -pedantic -Wall -Wextra -Werror -Wno-pointer-arith -Wno-unused-but-set-parameter" script: - ./autogen.sh - mkdir out diff --git a/AUTHORS b/AUTHORS index a797e7b28e32779f341d7ce7709786e3fcb2010c..4e2748b2d2e563ed2fd460e0b065332f3880eb55 100644 --- a/AUTHORS +++ b/AUTHORS @@ -4,3 +4,4 @@ Brian Suchy Valentin Reis Nicolas Denoyelle Clement Foyer +Brice Videau diff --git a/doc/aml.doxy b/doc/aml.doxy index f063c9dba757ba8ff44202870ebb084d5e915cf1..2dd7793e1a0764868e61f59e9fb6b50b1ffc0373 100644 --- a/doc/aml.doxy +++ b/doc/aml.doxy @@ -792,6 +792,7 @@ WARN_LOGFILE = INPUT = ../include \ ../include/aml/area \ + ../include/aml/layout \ ../include/aml/tiling \ ../include/aml/dma \ ../include/aml/scratch \ diff --git a/doc/img/layout.png b/doc/img/layout.png new file mode 100644 index 0000000000000000000000000000000000000000..0ed52311c96321a36bc42e64acf1ca27fbf9c6c1 Binary files /dev/null and b/doc/img/layout.png differ diff --git a/doc/img/reshape.png b/doc/img/reshape.png new file mode 100644 index 0000000000000000000000000000000000000000..8c42663fc96092fc519a6edc9406d61848d2c394 Binary files /dev/null and b/doc/img/reshape.png differ diff --git a/doc/index.rst b/doc/index.rst index 160f9f0035563fcc17aaa4c7176b3b449d59a0bc..7a1c1316f99d21437d3e7756af4bb6641477d409 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -34,7 +34,8 @@ As of now, AML implements the following abstractions: :align: right * :doc:`Areas `, a set of addressable physical memories, -* :doc:`Tilings `, a description of tiling data structures, +* :doc:`Layout `, a description of data structures organization, +* :doc:`Tilings `, (soon to be replaced), * :doc:`DMAs `, an engine to asynchronously move data structures between areas, * :doc:`Scratchpads `, a stage-in, stage-out abstraction for prefetching. @@ -132,5 +133,6 @@ for more info. pages/areas pages/tilings + pages/layout pages/dmas pages/scratchs diff --git a/doc/pages/layout.rst b/doc/pages/layout.rst new file mode 100644 index 0000000000000000000000000000000000000000..329fc87e3581517fbd239e7eb4fa41a9c1d55e39 --- /dev/null +++ b/doc/pages/layout.rst @@ -0,0 +1,13 @@ +Layout: Description of Data Organization +======================================== + +.. doxygengroup:: aml_layout + +Implementations +--------------- + + .. toctree:: + + layout_dense + layout_native + diff --git a/doc/pages/layout_dense.rst b/doc/pages/layout_dense.rst new file mode 100644 index 0000000000000000000000000000000000000000..7a3726ee734f92db703fcbb25ce7681abaa30f7b --- /dev/null +++ b/doc/pages/layout_dense.rst @@ -0,0 +1,4 @@ +AML Dense Layouts +================= + +.. doxygengroup:: aml_layout_dense diff --git a/doc/pages/layout_native.rst b/doc/pages/layout_native.rst new file mode 100644 index 0000000000000000000000000000000000000000..f68b00538d4535beb355869c9c23aca205f02037 --- /dev/null +++ b/doc/pages/layout_native.rst @@ -0,0 +1,4 @@ +AML Layouts Native Ops +====================== + +.. doxygengroup:: aml_layout_native diff --git a/include/Makefile.am b/include/Makefile.am index b016483d57f5894088a7afd6b2ad245d8e5570df..f7a45914debbc70fad23261c1ed913a4df92c2a6 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -4,6 +4,12 @@ include_aml_areadir=$(includedir)/aml/area include_aml_area_HEADERS = \ aml/area/linux.h +include_aml_layoutdir=$(includedir)/aml/layout +include_aml_layout_HEADERS = \ + aml/layout/native.h \ + aml/layout/dense.h \ + aml/layout/reshape.h + include_aml_dmadir=$(includedir)/aml/dma include_aml_dma_HEADERS = \ aml/dma/linux-seq.h \ diff --git a/include/aml.h b/include/aml.h index cf6ffd0a37ed755008711e9b388c41411597b4da..c88dc4f264ee7d38099a350f4275b4a7d8eb89cc 100644 --- a/include/aml.h +++ b/include/aml.h @@ -6,7 +6,7 @@ * For more info, see https://xgitlab.cels.anl.gov/argo/aml * * SPDX-License-Identifier: BSD-3-Clause -*******************************************************************************/ + ******************************************************************************/ /** * \file aml.h @@ -393,7 +393,7 @@ int aml_tiling_create_iterator(struct aml_tiling *tiling, * @return 0 if successful; an error code otherwise. **/ void aml_tiling_destroy_iterator(struct aml_tiling *tiling, - struct aml_tiling_iterator **iterator); + struct aml_tiling_iterator **iterator); /** @@ -474,17 +474,17 @@ struct aml_dma_request; struct aml_dma_data; /** - aml_dma_ops is a structure containing operations for a specific - * aml_dma implementation. - * These operation are operation are detailed in the structure. - * They are specific in: - * - the type of aml_area source and destination, - * - the progress engine performing the operation, - * - the type of of source and destination data structures. - * - * Each different combination of these three points may require a different - * set of dma operations. - **/ + aml_dma_ops is a structure containing operations for a specific + * aml_dma implementation. + * These operation are operation are detailed in the structure. + * They are specific in: + * - the type of aml_area source and destination, + * - the progress engine performing the operation, + * - the type of of source and destination data structures. + * + * Each different combination of these three points may require a different + * set of dma operations. + **/ struct aml_dma_ops { /** * Initiate a data movement, from a source pointer to a destination @@ -585,6 +585,365 @@ int aml_dma_cancel(struct aml_dma *dma, struct aml_dma_request *req); //////////////////////////////////////////////////////////////////////////////// +/** + * @} + * @defgroup aml_layout "AML Layout" + * @brief Low level description of data orrganization at the byte granularity. + * + * Layout describes how contiguous element of a flat memory address space are + * organized into a multidimensional array of elements of a fixed size. + * The abstraction provide functions to build layouts, access elements, + * reshape a layout, or subset a layout. + * + * Layouts are characterized by: + * * A pointer to the data it describes + * * A set of dimensions on which data spans. + * * A stride in between elements of a dimension. + * * A pitch indicating the space between contiguous elements of a dimension. + * + * The figure below describes a 2D layout with a sub-layout + * (obtained with aml_layout_slice()) operation. The sub-layout has a stride + * of 1 element along the second dimension. The slice has an offset of 1 element + * along the same dimension, and its pitch is the pitch of the original + * layout. Calling aml_layout_deref() on this sublayout with appropriate + * coordinates will return a pointer to elements noted (coor_x, coord_y). + * @see aml_layout_slice() + * + * @image html layout.png "2D layout with a 2D slice." width=400cm + * + * Access to specific elements of a layout can be done with + * the aml_layout_deref() function. Access to an element is always done + * relatively to the dimensions order set by at creation time. + * However, internally, the library will store dimensions from the last + * dimension to the first dimension such that elements along the first dimension + * are contiguous in memory. This order is defined called with the value + * AML_LAYOUT_ORDER_FORTRAN. Therefore, AML provides access to elements + * without the overhead of user order choice through function suffixed + * with "native". + * @see aml_layout_deref() + * @see aml_layout_deref_native() + * @see aml_layout_dims_native() + * @see aml_layout_slice_native() + * + * The layout abstraction also provides a function to reshape data + * with a different set of dimensions. A reshaped layout will access + * the same data but with different coordinates as pictured in the + * figure below. + * @see aml_layout_reshape() + * + * @image html reshape.png "2D layout turned into a 3D layout." width=700cm + * + * @see aml_layout_dense + * @see aml_layout_pad + * @{ + **/ + +//////////////////////////////////////////////////////////////////////////////// + +struct aml_layout_ops; +struct aml_layout_data; + +/** Structure definition of AML layouts **/ +struct aml_layout { + /** Layout functions implementation **/ + struct aml_layout_ops *ops; + /** Implementation specific data of a layout**/ + struct aml_layout_data *data; +}; + +/** List of operators implemented by layouts. **/ +struct aml_layout_ops { + /** + * Layout must provide a way to access a specific element + * according to the provided dimensions. + * Coordinates bounds checking is done in the generic API. + * Coordinates provided by the user will match the order + * Of the dimensions provided by the user in the constructor. + * However, dimensions are always stored internally in the + * AML_LAYOUT_ORDER_FORTRAN order. + * @param data[in]: The non-NULL handle to layout internal data. + * @param coords[in]: The non-NULL coordinates on which to access data. + * Coordinates are checked to be valid in aml_layout_deref(). + * @return A pointer to the dereferenced element on success. + * @return NULL on failure with aml_errno set to the error reason. + **/ + void *(*deref)(const struct aml_layout_data *data, + const size_t *coords); + + /** + * Function for derefencing elements of a layout inside the library. + * Layout assumes data is always stored in AML_LAYOUT_ORDER_FORTRAN + * order. Coordinates provided by the library will match the same + * order, i.e last dimension first. + * @param data[in]: The non-NULL handle to layout internal data. + * @param coords[in]: The non-NULL coordinates on which to access data. + * The first coordinate should be the last dimensions and so on to the + * last, coordinate, last dimension. + * @return A pointer to the dereferenced element on success. + * @return NULL on failure with aml_errno set to the error reason. + **/ + void *(*deref_native)(const struct aml_layout_data *data, + const size_t *coords); + + /** + * Get the order in which dimensions of the layout are + * supposed to be accessed by the user. + * @param data[in]: The non-NULL handle to layout internal data. + * @return Order value. It is a bitmask with order bit set (or not set). + * Output value can be further checked against order AML_LAYOUT_ORDER + * flags by using the macro AML_LAYOUT_ORDER() on output value. + * @see AML_LAYOUT_ORDER() + **/ + int (*order)(const struct aml_layout_data *data); + + /** + * Return the layout dimensions in the user order. + * @param data[in]: The non-NULL handle to layout internal data. + * @param dims[out]: The non-NULL array of dimensions to fill. It is + * supposed to be large enough to contain ndims() elements. + * @return AML_SUCCESS on success, else an AML error code. + **/ + int (*dims)(const struct aml_layout_data *data, size_t *dims); + + /** + * Return the layout dimensions in the order they are actually stored + * in the library. + * @param data[in]: The non-NULL handle to layout internal data. + * @param dims[out]: The non-NULL array of dimensions to fill. It is + * supposed to be large enough to contain ndims() elements. + * @return AML_SUCCESS on success, else an AML error code. + **/ + int (*dims_native)(const struct aml_layout_data *data, + size_t *dims); + + /** + * Return the number of dimensions in a layout. + * @param data[in]: The non-NULL handle to layout internal data. + * @return The number of dimensions in the layout. + **/ + size_t (*ndims)(const struct aml_layout_data *data); + + /** + * Return the size of layout elements. + * @param data[in]: The non-NULL handle to layout internal data. + * @return The size of elements stored with this layout. + **/ + size_t (*element_size)(const struct aml_layout_data *data); + + /** + * Reshape the layout with different dimensions. + * Layout dimensions are checked in aml_layout_reshape() to store + * the exact same number of elements. + * @param data[in]: The non-NULL handle to layout internal data. + * @param output[out]: A non NULL pointer to a layout where to allocate + * a new layout resulting from the reshape operation. + * @param ndims[in]: The number of dimensions of the new layout. + * @param dims[in]: The number of elements along each dimension of + * the new layout. + * @return AML_SUCCESS on success, else an AML error code (<0). + **/ + int (*reshape)(const struct aml_layout_data *data, + struct aml_layout **output, + const size_t ndims, + const size_t *dims); + + /** + * Return a layout that is a subset of another layout. + * Slice arguments compatibility with the original layout are + * checked in aml_layout_slice(). + * @param data[in]: The non-NULL handle to layout internal data. + * @param output[out]: A non NULL pointer to a layout where to allocate + * a new layout resulting from the slice operation. + * @param dims[in]: The number of elements of the slice along each + * dimension . + * @param offsets[in]: The index of the first element of the slice + * in each dimension. + * @param strides[in]: The displacement (in number of elements) between + * elements of the slice. + * @return A newly allocated layout with the queried subset of the + * original layout on succes. + * @return NULL on error with aml_errno set to the failure reason. + **/ + int (*slice)(const struct aml_layout_data *data, + struct aml_layout **output, + const size_t *dims, + const size_t *offsets, + const size_t *strides); + + /** + * Return a layout that is a subset of another layout, assuming + * dimensions are stored with AML_LAYOUT_ORDER_FORTRAN. + * Slice arguments compatibility with the original layout are + * checked in aml_layout_slice(). + * @param data[in]: The non-NULL handle to layout internal data. + * @param output[out]: A non NULL pointer to a layout where to allocate + * a new layout resulting from the slice operation. + * @param dims[in]: The number of elements of the slice along each + * dimension . + * @param offsets[in]: The index of the first element of the slice + * in each dimension. + * @param strides[in]: The displacement (in number of elements) between + * elements of the slice. + * @return A newly allocated layout with the queried subset of the + * original layout on succes. + * @return NULL on error with aml_errno set to the failure reason. + **/ + int (*slice_native)(const struct aml_layout_data *data, + struct aml_layout **output, + const size_t *dims, + const size_t *offsets, + const size_t *strides); +}; + +/** + * Tag specifying user storage of dimensions inside a layout. + * Layout order is the first bit in an integer bitmask. + * @see AML_LAYOUT_ORDER() + * This tag will store dimensions in the order provided by the user, + * i.e elements of the last dimension will be contiguous in memory. + **/ +#define AML_LAYOUT_ORDER_C (0<<0) + +/** + * Tag specifying user storage of dimensions inside a layout. + * Layout order is the first bit in an integer bitmask. + * @see AML_LAYOUT_ORDER() + * This tag will store dimensions in the reversed order provided + * by the user, i.e elements of the first dimension will be contiguous + * in memory. This storage is the actual storage used by the library + * inside the structure. + **/ +#define AML_LAYOUT_ORDER_FORTRAN (1<<0) + +/** + * This is equivalent to AML_LAYOUT_ORDER_C. + * @see AML_LAYOUT_ORDER_C + **/ +#define AML_LAYOUT_ORDER_COLUMN_MAJOR (0<<0) + +/** + * This is equivalent to AML_LAYOUT_ORDER_FORTRAN. + * @see AML_LAYOUT_ORDER_FORTRAN + **/ +#define AML_LAYOUT_ORDER_ROW_MAJOR (1<<0) + +/** + * Get the order bit of an integer bitmask. + * The value can be further checked for equality + * with AML_LAYOUT_ORDER_* values. + * @param x: An integer with the first bit set + * to the order value. + * @return An integer containing only the bit order. + **/ +#define AML_LAYOUT_ORDER(x) ((x) & (1<<0)) + +/** + * Dereference an element of a layout by its coordinates. + * @param layout[in]: An initialized layout. + * @param coords[in]: The coordinates on which to access data. + * @return A pointer to the dereferenced element on success. + * @return NULL on failure with aml_errno set to the error reason: + * * AML_EINVAL if coordinate are out of bound + * * See specific implementation of layout for further information + * on possible error codes. + **/ +void *aml_layout_deref(const struct aml_layout *layout, + const size_t *coords); + +/** + * Equivalent to aml_layout_deref() but with bound checking + * on coordinates. + * @see aml_layout_deref() + **/ +void *aml_layout_deref_safe(const struct aml_layout *layout, + const size_t *coords); + +/** + * Get the order in which dimensions of the layout are supposed to be + * accessed by the user. + * @param layout[in]: An initialized layout. + * @return The order (>0) on success, an AML error (<0) on failure. + * @return On success, a bitmask with order bit set (or not set). + * Output value can be further checked against order AML_LAYOUT_ORDER + * flags by using the macro AML_LAYOUT_ORDER() on output value. + * @see AML_LAYOUT_ORDER() + **/ +int aml_layout_order(const struct aml_layout *layout); + +/** + * Return the layout dimensions in the user order. + * @param layout[in]: An initialized layout. + * @param dims[out]: The non-NULL array of dimensions to fill. It is + * supposed to be large enough to contain ndims() elements. + * @return AML_SUCCESS on success, else an AML error code. + **/ +int aml_layout_dims(const struct aml_layout *layout, size_t *dims); + +/** + * Return the number of dimensions in a layout. + * @param layout[in]: An initialized layout. + * @return The number of dimensions in the layout. + **/ +size_t aml_layout_ndims(const struct aml_layout *layout); + +/** + * @brief Return the size of layout elements. + * @param layout[in]: An initialized layout. + * @return The size of elements stored with this layout. + **/ +size_t aml_layout_element_size(const struct aml_layout *layout); + +/** + * @brief Reshape the layout with different dimensions. + * This function checks that the number of elements of + * the reshaped layout matches the number of elements + * in the original layout. Additional constraint may apply + * depending on the layout implementation. + * @param layout[in]: An initialized layout. + * @param reshaped_layout[out]: A newly allocated layout + * with the queried shape on succes. + * @param ndims[in]: The number of dimensions of the new layout. + * @param dims[in]: The number of elements along each dimension of + * the new layout. + * @return AML_SUCCESS on success. + * @return AML_EINVAL if reshape dimensions are not compatible + * with original layout dimensions. + * @return AML_ENOMEM if AML failed to allocate the new structure. + * @return Another aml_error code. Refer to the layout + * implementation of reshape function. + **/ +int aml_layout_reshape(const struct aml_layout *layout, + struct aml_layout **reshaped_layout, + const size_t ndims, + const size_t *dims); + +/** + * Return a layout that is a subset of another layout. + * The number of elements to subset along each dimension + * must be compatible with offsets and strides. + * This function checks that the amount of elements along + * each dimensions of the slice actually fits in the original + * layout. + * @param layout[in]: An initialized layout. + * @param reshaped_layout[out]: a pointer where to store a + * newly allocated layout with the queried subset of the + * original layout on succes. + * @param dims[in]: The number of elements of the slice along each + * dimension . + * @param offsets[in]: The index of the first element of the slice + * in each dimension. If NULL, offset is set to 0. + * @param strides[in]: The displacement (in number of elements) between + * elements of the slice. If NULL, stride is set to 1. + * @return AML_SUCCESS on success, else an AML error code (<0). + **/ +int aml_layout_slice(const struct aml_layout *layout, + struct aml_layout **reshaped_layout, + const size_t *dims, + const size_t *offsets, + const size_t *strides); + +//////////////////////////////////////////////////////////////////////////////// + /** * @} * @defgroup aml_scratch "AML Scratchpad" diff --git a/include/aml/layout/dense.h b/include/aml/layout/dense.h new file mode 100644 index 0000000000000000000000000000000000000000..e3f32c73583fa0729f14c4b595425663653f8fa0 --- /dev/null +++ b/include/aml/layout/dense.h @@ -0,0 +1,239 @@ +/******************************************************************************* + * Copyright 2019 UChicago Argonne, LLC. + * (c.f. AUTHORS, LICENSE) + * + * This file is part of the AML project. + * For more info, see https://xgitlab.cels.anl.gov/argo/aml + * + * SPDX-License-Identifier: BSD-3-Clause +*******************************************************************************/ + +#ifndef AML_AREA_LAYOUT_DENSE_H +#define AML_AREA_LAYOUT_DENSE_H + +/** + * @defgroup aml_layout_dense "AML Layout Dense" + * @brief Default aml layout. + * + * Dense layouts describe how a multi-dimensional dense data structure + * is collapsed into a linear (and contiguous) virtual address range. + * Dimensions of a layout may contain a stride (space between elements) + * on the virtual address space, and a pitch (distance between contiguous + * elements of the same dimension). + * + * #include + * @see aml_layout + * @{ + **/ + +/** + * Structure of a dense layout. + **/ +struct aml_layout_dense { + /** + * dimensions, in element size, of the data structure, + * by order of appearance in memory. + **/ + size_t *dims; + /** + * Offset between elements of the same dimension. + * Offset in number of elements. + **/ + size_t *stride; + /** + * distances between two elements of the next dimension + * (or total dimension of the layout in this dimension). + **/ + size_t *pitch; + /** + * cumulative distances between two elements in the same + * dimension (pitch[0] is the element size in bytes). + **/ + size_t *cpitch; + /** base pointer of the address range **/ + void *ptr; + /** number of dimensions **/ + size_t ndims; +}; + +/** + * Dense layout constructor. + * @param layout[out]: A pointer where to store a newly allocated layout. + * @param ptr[in]: The pointer to the data structure described by this layout. + * @param order[in]: The order in which dimensions are organized. + * Can be AML_LAYOUT_ORDER_COLUMN_MAJOR or AML_LAYOUT_ORDER_ROW_MAJOR. + * @param element_size[in]: The size of each element in layout. + * @param ndims[in]: The number of dimensions of the layout. + * @param dims[in]: The number of elements along each dimension of the layout. + * @param stride[in]: The space between elements (in number of elements), + * along each dimension. If NULL then the stride is set to one for each + * dimension. + * @param pitch[in]: The space between consecutive elements of the same + * dimension. If NULL, pitch is set to the number of elements in each dimension. + * @return -AML_ENOMEM if layout allocation failed. + * @return -AML_EINVAL if layout is NULL. + * @return AML_SUCCESS if creation succeeded. + * @see aml_layout_dense + **/ +int aml_layout_dense_create(struct aml_layout **layout, + void *ptr, + const int order, + const size_t element_size, + const size_t ndims, + const size_t *dims, + const size_t *stride, + const size_t *pitch); + +/** + * Function to free a dense layout. + * @param layout[inout]: The layout to deallocate. NULL on return. + **/ +void aml_layout_dense_destroy(struct aml_layout **layout); + +/** + * Deref operator for dense layout in AML_ORDER_COLUMN_MAJOR. + * Also used as the deref operator for this type of layout. + * Does not check its argument. If data is NULL, or coords are out + * of bounds, the behaviour of aml_layout_column_deref() is undefined. + * @see aml_layout_deref() + * @see aml_layout_deref_native() + **/ +void *aml_layout_column_deref(const struct aml_layout_data *data, + const size_t *coords); + +/** + * Layout operator for retrieving order of dimension storage. + * This function shall not fail. + * @see aml_layout_order() + **/ +int aml_layout_column_order(const struct aml_layout_data *data); + +/** + * Operator for retrieving the number of dimensions in the layout. + * Does not check data is not NULL. If data is NULL or not the good + * pointer type, the behaviour is undefined. + * @see aml_layout_ndims() + **/ +size_t aml_layout_dense_ndims(const struct aml_layout_data *data); + +/** + * Layout operator for retrieving layout elements size. + * Does not check data is not NULL. If data is NULL or not the good + * @see aml_layout_element_size() + **/ +size_t aml_layout_dense_element_size(const struct aml_layout_data *data); + +/** + * Operator for reshaping dense layouts with column major order. + * Does not check if the number of elements match. + * This should be done in aml_layout_reshape(). + * @return -AML_EINVAL if merge then split of dimensions + * cannot be done appropriatly. + * @return -AML_ENOMEM if the resulting layout cannot be allocated. + * @return AML_SUCCESS on successful reshape. + * @see aml_layout_reshape() + **/ +int aml_layout_column_reshape(const struct aml_layout_data *data, + struct aml_layout **output, + size_t ndims, + const size_t *dims); + +/** + * Operator for slicing dense layouts with column major order. + * Does not check if slice elements are out of bound. + * This should be done in aml_layout_slice(). + * @return -AML_ENOMEM if the resulting layout cannot be allocated. + * @return AML_SUCCESS on successful slicing. + * @see aml_layout_slice() + **/ +int aml_layout_column_slice(const struct aml_layout_data *data, + struct aml_layout **output, + const size_t *offsets, + const size_t *dims, + const size_t *strides); + +/** + * Operator for slicing dense layouts with column major order. + * Does not check if slice elements are out of bound. + * This should be done in aml_layout_slice(). + * @return -AML_ENOMEM if the resulting layout cannot be allocated. + * @return AML_SUCCESS on successful slicing. + * @see aml_layout_deref() + **/ +void *aml_layout_row_deref(const struct aml_layout_data *data, + const size_t *coords); + +/** + * Operator for retrieving layout order of a row major layout. + * This function shall not fail. + * @see aml_layout_order() + **/ +int aml_layout_row_order(const struct aml_layout_data *data); + +/** + * Operator for retrieving dimensions size of a layout with row major order. + * Does not check data is not NULL. If data is NULL or not the good + * pointer type, the behaviour is undefined. + * Arguments are supposed to be checked in aml_layout_dims(). + * @see aml_layout_dims() + **/ +int aml_layout_row_dims(const struct aml_layout_data *data, size_t *dims); + +/** + * Operator for reshaping dense layouts with row major order. + * Does not check if the number of elements match. + * This should be done in aml_layout_reshape(). + * @return -AML_EINVAL if merge then split of dimensions + * cannot be done appropriatly. + * @return -AML_ENOMEM if the resulting layout cannot be allocated. + * @return AML_SUCCESS on successful reshape. + * @see aml_layout_reshape() + **/ +int aml_layout_row_reshape(const struct aml_layout_data *data, + struct aml_layout **output, + const size_t ndims, + const size_t *dims); + +/** + * Operator for slicing dense layouts with row major order. + * Does not check if slice elements are out of bound. + * This should be done in aml_layout_slice(). + * @return -AML_ENOMEM if the resulting layout cannot be allocated. + * @return AML_SUCCESS on successful slicing. + * @see aml_layout_slice() + **/ +int aml_layout_row_slice(const struct aml_layout_data *data, + struct aml_layout **output, + const size_t *offsets, + const size_t *dims, + const size_t *strides); + +/** + * Operator for slicing dense layouts with row major order, + * without the overhead of user defined order, i.e using the internal + * library order. + * Does not check if slice elements are out of bound. + * This should be done in aml_layout_slice(). + * @return -AML_ENOMEM if the resulting layout cannot be allocated. + * @return AML_SUCCESS on successful slicing. + * @see aml_layout_slice() + **/ +int aml_layout_row_slice_native(const struct aml_layout_data *data, + struct aml_layout **output, + const size_t *offsets, + const size_t *dims, + const size_t *strides); + +/** + * Pre-existing operators for dense layout + * with AML_LAYOUT_ORDER_COLUMN_MAJOR order. + **/ +extern struct aml_layout_ops aml_layout_column_ops; + +/** + * Pre-existing operators for dense layout + * with AML_LAYOUT_ORDER_ROW_MAJOR order. + **/ +extern struct aml_layout_ops aml_layout_row_ops; + +#endif diff --git a/include/aml/layout/native.h b/include/aml/layout/native.h new file mode 100644 index 0000000000000000000000000000000000000000..8cd7b7babf63514cfac5f0d063a2229981f787c0 --- /dev/null +++ b/include/aml/layout/native.h @@ -0,0 +1,73 @@ +/************************************************************************* + * Copyright 2019 UChicago Argonne, LLC. + * (c.f. AUTHORS, LICENSE) + * + * This file is part of the AML project. + * For more info, see https://xgitlab.cels.anl.gov/argo/aml + * + * SPDX-License-Identifier: BSD-3-Clause +**************************************************************************/ + +#ifndef AML_AREA_LAYOUT_NATIVE_H +#define AML_AREA_LAYOUT_NATIVE_H + +/** + * @defgroup aml_layout_native "AML Layout Internal API" + * @brief Layout API for internal management of layouts. + * + * #include + * @{ + **/ + +/** + * Function for derefencing elements of a layout inside the library. + * Layout assumes data is always stored in AML_LAYOUT_ORDER_FORTRAN order. + * Coordinates provided by the library will match the same order, i.e + * last dimension first. + * @param layout[in]: An initialized layout. + * @param coords[in]: The coordinates on which to access data. + * The first coordinate should be the last dimensions and so on to the last, + * coordinate, last dimension. + * @return A pointer to the dereferenced element on success. + * @return NULL on failure with aml_errno set to the error reason. + **/ +void *aml_layout_deref_native(const struct aml_layout *layout, + const size_t *coords); + +/** + * Return the layout dimensions in the order they are actually stored + * in the library. + * @param layout[in]: An initialized layout. + * @param dims[out]: The non-NULL array of dimensions to fill. It is + * supposed to be large enough to contain ndims() elements. + * @return AML_SUCCESS on success, else an AML error code. + **/ +int aml_layout_dims_native(const struct aml_layout *layout, + size_t *dims); + + +/** + * Return a layout that is a subset of another layout. + * The number of elements to subset along each dimension + * must be compatible with offsets and strides. + * This function checks that the amount of elements along + * each dimensions of the slice actually fits in the original + * layout. + * @param layout[in]: An initialized layout. + * @param reshaped_layout[out]: a pointer where to store a + * newly allocated layout with the queried subset of the + * original layout on succes. + * @param offsets[in]: The index of the first element of the slice + * in each dimension. + * @param dims[in]: The number of elements of the slice along each + * dimension . + * @param strides[in]: The displacement (in number of elements) between + * elements of the slice. + * @return AML_SUCCESS on success, else an AML error code (<0). + **/ +int aml_layout_slice_native(const struct aml_layout *layout, + struct aml_layout **reshaped_layout, + const size_t *offsets, + const size_t *dims, + const size_t *strides); +#endif diff --git a/include/aml/layout/reshape.h b/include/aml/layout/reshape.h new file mode 100644 index 0000000000000000000000000000000000000000..e09db4375a331ffd35ad04f6f6c86d939a201ec0 --- /dev/null +++ b/include/aml/layout/reshape.h @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright 2019 UChicago Argonne, LLC. + * (c.f. AUTHORS, LICENSE) + * + * This file is part of the AML project. + * For more info, see https://xgitlab.cels.anl.gov/argo/aml + * + * SPDX-License-Identifier: BSD-3-Clause +*******************************************************************************/ + +#ifndef AML_AREA_LAYOUT_RESHAPE_H +#define AML_AREA_LAYOUT_RESHAPE_H + +/** + * @defgroup aml_layout_reshape "AML Layout Reshape" + * @brief Default aml layout. + * + * Layout for reshaping dense layouts when reshape method + * on dense layouts fails. + * + * #include + * @see aml_layout + * @{ + **/ + +/** + * Structure of reshape layout. + **/ +struct aml_layout_data_reshape { + struct aml_layout *target; + size_t ndims; + size_t target_ndims; + size_t *dims; + size_t *coffsets; + size_t *target_dims; +}; + +int aml_layout_reshape_create(struct aml_layout **layout, + struct aml_layout *target, + const int order, + const size_t ndims, + const size_t *dims); + +void aml_layout_reshape_destroy(struct aml_layout **l); + +extern struct aml_layout_ops aml_layout_reshape_row_ops; +extern struct aml_layout_ops aml_layout_reshape_column_ops; + +#endif diff --git a/src/Makefile.am b/src/Makefile.am index d97a35ecedccce809e6a0db68c8606bd682977df..0a537071f6535c66b1732e935b4190233601ef98 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -6,6 +6,11 @@ AREA_SOURCES = \ area/area.c \ area/linux.c +LAYOUT_SOURCES = \ + layout/layout.c \ + layout/dense.c \ + layout/reshape.c + DMA_SOURCES = \ dma/dma.c \ dma/dma_linux_par.c \ @@ -31,6 +36,7 @@ LIB_SOURCES = \ $(DMA_SOURCES) \ $(SCRATCH_SOURCES) \ $(TILING_SOURCES) \ + $(LAYOUT_SOURCES) \ $(UTILS_SOURCES) \ aml.c diff --git a/src/area/linux.c b/src/area/linux.c index 497f3c921ba6d2b885017a3b039e37ccac9c0b0b..12e58309bffc7fa928dda5091d39e3f47213f877 100644 --- a/src/area/linux.c +++ b/src/area/linux.c @@ -106,10 +106,10 @@ void *aml_area_linux_mmap(const struct aml_area_data *area_data, return out; } -int aml_area_linux_munmap( - __attribute__ ((unused)) const struct aml_area_data *area_data, +int aml_area_linux_munmap(const struct aml_area_data *area_data, void *ptr, const size_t size) { + (void)area_data; int err = munmap(ptr, size); if (err == -1) diff --git a/src/layout/dense.c b/src/layout/dense.c new file mode 100644 index 0000000000000000000000000000000000000000..3ff774baa937edc4d8f4c4cadfbad564c482df24 --- /dev/null +++ b/src/layout/dense.c @@ -0,0 +1,558 @@ +/******************************************************************************* + * Copyright 2019 UChicago Argonne, LLC. + * (c.f. AUTHORS, LICENSE) + * + * This file is part of the AML project. + * For more info, see https://xgitlab.cels.anl.gov/argo/aml + * + * SPDX-License-Identifier: BSD-3-Clause + ******************************************************************************/ + +#include "aml.h" +#include "aml/layout/native.h" +#include "aml/layout/dense.h" + +static struct aml_layout *aml_layout_dense_alloc(const size_t ndims) +{ + struct aml_layout_dense *data; + struct aml_layout *layout; + char *l; + + layout = malloc((sizeof(struct aml_layout) + + sizeof(struct aml_layout_dense) + + (ndims * 4) * sizeof(size_t))); + + if (layout == NULL) { + perror("malloc"); + return NULL; + } + + l = (char *) layout; + l += sizeof(*layout); + data = (struct aml_layout_dense *) l; + l += sizeof(*data); + data->dims = (size_t *) l; + + l += sizeof(*data->dims) * ndims; + data->stride = (size_t *) l; + for (size_t i = 0; i < ndims; i++) + data->stride[i] = 1; + + l += sizeof(*data->stride) * ndims; + data->pitch = (size_t *) l; + l += sizeof(*data->pitch) * ndims; + data->cpitch = (size_t *) l; + + data->ptr = NULL; + data->ndims = ndims; + layout->data = (struct aml_layout_data *) data; + return layout; +} + +static +void aml_layout_dense_init_cpitch(struct aml_layout *layout, + void *ptr, + const size_t ndims, + const size_t *dims, + const size_t *stride, + const size_t *cpitch) +{ + struct aml_layout_dense *data = + (struct aml_layout_dense *)layout->data; + data->ptr = ptr; + memcpy(data->dims, dims, ndims * sizeof(size_t)); + memcpy(data->stride, stride, ndims * sizeof(size_t)); + memcpy(data->cpitch, cpitch, (ndims + 1) * sizeof(size_t)); + for (size_t i = 0; i < ndims; i++) + data->pitch[i] = cpitch[i+1]/cpitch[i]; +} + +int aml_layout_dense_create(struct aml_layout **layout, + void *ptr, + const int order, + const size_t element_size, + const size_t ndims, + const size_t *dims, + const size_t *stride, + const size_t *pitch) +{ + + struct aml_layout *l; + struct aml_layout_dense *data; + + if (layout == NULL) + return -AML_EINVAL; + + l = aml_layout_dense_alloc(ndims); + if (l == NULL) + return -AML_ENOMEM; + + data = (struct aml_layout_dense *)l->data; + + data->ptr = ptr; + data->cpitch[0] = element_size; + + switch (AML_LAYOUT_ORDER(order)) { + + case AML_LAYOUT_ORDER_ROW_MAJOR: + l->ops = &aml_layout_row_ops; + for (size_t i = 0; i < ndims; i++) { + data->dims[i] = dims[ndims-i-1]; + if (stride) + data->stride[i] = stride[ndims-i-1]; + if (pitch) + data->pitch[i] = pitch[ndims-i-1]; + else + data->pitch[i] = dims[ndims-i-1]; + } + for (size_t i = 1; i <= ndims; i++) + data->cpitch[i] = data->cpitch[i-1]*pitch[ndims-i]; + break; + + case AML_LAYOUT_ORDER_COLUMN_MAJOR: + l->ops = &aml_layout_column_ops; + memcpy(data->dims, dims, ndims * sizeof(size_t)); + if (stride) + memcpy(data->stride, stride, ndims * sizeof(size_t)); + if (pitch) + memcpy(data->pitch, pitch, ndims * sizeof(size_t)); + else + memcpy(data->pitch, dims, ndims * sizeof(size_t)); + for (size_t i = 1; i <= ndims; i++) + data->cpitch[i] = data->cpitch[i-1]*pitch[i-1]; + break; + default: + free(l); + return -AML_EINVAL; + + } + + *layout = l; + return AML_SUCCESS; +} + +void aml_layout_dense_destroy(struct aml_layout **l) +{ + if (l == NULL || *l == NULL) + return; + free(*l); + *l = NULL; +} + +/******************************************************************************* + * COLUMN OPERATORS: + ******************************************************************************/ + +void *aml_layout_column_deref(const struct aml_layout_data *data, + const size_t *coords) +{ + char *ptr; + const struct aml_layout_dense *d; + + d = (const struct aml_layout_dense *) data; + ptr = (char *) d->ptr; + for (size_t i = 0; i < d->ndims; i++) + ptr += coords[i]*d->cpitch[i]*d->stride[i]; + return (void *)ptr; +} + +int aml_layout_column_order(const struct aml_layout_data *data) +{ + (void)data; + return AML_LAYOUT_ORDER_COLUMN_MAJOR; +} + +int aml_layout_column_dims(const struct aml_layout_data *data, size_t *dims) +{ + const struct aml_layout_dense *d; + + d = (const struct aml_layout_dense *) data; + assert(d != NULL); + assert(dims != NULL); + memcpy((void *)dims, (void *)d->dims, sizeof(size_t)*d->ndims); + return 0; +} + +size_t aml_layout_dense_ndims(const struct aml_layout_data *data) +{ + const struct aml_layout_dense *d; + + d = (const struct aml_layout_dense *) data; + return d->ndims; +} + +size_t aml_layout_dense_element_size(const struct aml_layout_data *data) +{ + const struct aml_layout_dense *d; + + d = (const struct aml_layout_dense *)data; + // element size is the pitch along the 1st dim. + return d->cpitch[0]; +} + + +/* Given a layout parameters (ndim, dims, stride, and cpitch), returns + * the representation of this layout that uses the least dimensions. + * The new parameter are returned in (new_ndims, new_dims, new_stride, + * and new_cpitch. + */ +static void merge_dims(const size_t ndims, + const size_t *dims, + const size_t *stride, + const size_t *cpitch, + size_t *new_ndims, + size_t *new_dims, + size_t *new_stride, + size_t *new_cpitch) +{ + size_t dim_index = 0; + size_t new_dim_index = 0; + + /* Greedy algorithm that tries to merge dimensions starting with the + * first */ + new_dims[new_dim_index] = dims[dim_index]; + new_cpitch[new_dim_index] = cpitch[dim_index]; + new_stride[new_dim_index] = stride[dim_index]; + /* While we haven't consumed all dimensions */ + while (dim_index < ndims - 1) { + /* Check if current dimension can be merged with the next. + * ie: current dimension is not padded && next dimension has + * no stride */ + if (dims[dim_index] * stride[dim_index] * cpitch[dim_index] == + cpitch[dim_index + 1] && stride[dim_index + 1] == 1) { + new_dims[new_dim_index] *= dims[dim_index + 1]; + /* Else add a new dimension with the same characteristic + * as the dimensions we were trying to merge */ + } else { + new_dim_index++; + new_dims[new_dim_index] = dims[dim_index + 1]; + new_cpitch[new_dim_index] = cpitch[dim_index + 1]; + new_stride[new_dim_index] = stride[dim_index + 1]; + } + dim_index++; + } + new_cpitch[new_dim_index + 1] = cpitch[dim_index + 1]; + *new_ndims = new_dim_index + 1; +} + + +/* Try to change the indexing dimensions of a layout to the + * given number of dimensions and dimensions. If the description + * is incompatible with the original layout returns -AML_EINVAL. + * Else returns AML_SUCCESS as well as new stride and cumulative pitch + * in n_stride and n_cpitch respectively. + */ +static int reshape_dims(const struct aml_layout_dense *d, + const size_t ndims, + const size_t *dims, + size_t *n_stride, + size_t *n_cpitch) +{ + size_t m_ndims; + size_t m_dims[d->ndims]; + size_t m_stride[d->ndims]; + size_t m_cpitch[d->ndims + 1]; + + /* First obtain a canonical representation of the layout + * that uses the least amount of dimensions. */ + merge_dims(d->ndims, d->dims, d->stride, d->cpitch, + &m_ndims, m_dims, m_stride, m_cpitch); + + size_t m_dim_index = 0; + + /* Greedy algorithm that tries to split the canonical + * representation into the given new dimensions starting from the + * first. The canonical representation is destroyed in the process. */ + n_cpitch[0] = m_cpitch[m_dim_index]; + for (size_t i = 0; i < ndims; i++) { + /* If the new dimension perfectly fits in the current merged + * dimensions, then the new stride and cumulative pitch are + * copied from the current merged dimension. The next merged + * dimension becomes current. */ + if (m_dims[m_dim_index] == dims[i]) { + n_stride[i] = m_stride[m_dim_index]; + n_cpitch[i + 1] = m_cpitch[m_dim_index + 1]; + m_dim_index++; + /* Else if the current merged dimension can be evenly split by + * the new dimension, we divide the current merged dimension by + * the new dimension, merged stride is consumed and becomes 1 + * and cumulative pitch is computed from the new stride and + * dimensions. */ + } else if (m_dims[m_dim_index] % dims[i] == 0) { + m_dims[m_dim_index] /= dims[i]; + n_stride[i] = m_stride[m_dim_index]; + n_cpitch[i + 1] = + n_cpitch[i] * dims[i] * m_stride[m_dim_index]; + m_stride[m_dim_index] = 1; + /* Else the new description is incompatible. */ + } else { + return -AML_EINVAL; + } + } + return AML_SUCCESS; +} + +int aml_layout_column_reshape(const struct aml_layout_data *data, + struct aml_layout **output, + size_t ndims, + const size_t *dims) +{ + int err; + struct aml_layout *layout; + const struct aml_layout_dense *d; + size_t stride[ndims]; + size_t cpitch[ndims + 1]; + + d = (const struct aml_layout_dense *)data; + + layout = aml_layout_dense_alloc(ndims); + if (layout == NULL) + return -AML_ENOMEM; + + err = reshape_dims(d, ndims, dims, stride, cpitch); + if (err != AML_SUCCESS) { + free(layout); + return err; + } + + aml_layout_dense_init_cpitch(layout, + d->ptr, + ndims, + dims, + stride, + cpitch); + layout->ops = &aml_layout_column_ops; + + *output = layout; + return AML_SUCCESS; +} + +int aml_layout_column_slice(const struct aml_layout_data *data, + struct aml_layout **output, + const size_t *dims, + const size_t *offsets, + const size_t *strides) +{ + struct aml_layout *layout; + const struct aml_layout_dense *d; + void *ptr; + + d = (const struct aml_layout_dense *)data; + ptr = aml_layout_column_deref(data, offsets); + + layout = aml_layout_dense_alloc(d->ndims); + if (layout == NULL) + return -AML_ENOMEM; + + size_t cpitch[d->ndims + 1]; + size_t new_strides[d->ndims]; + + cpitch[d->ndims] = d->cpitch[d->ndims]; + + + for (size_t i = 0; i < d->ndims; i++) { + cpitch[i] = d->cpitch[i]; + new_strides[i] = strides[i] * d->stride[i]; + cpitch[d->ndims] -= cpitch[i] * offsets[i] * d->stride[i]; + } + + aml_layout_dense_init_cpitch(layout, + ptr, + d->ndims, + dims, + new_strides, + cpitch); + layout->ops = &aml_layout_column_ops; + + *output = layout; + return AML_SUCCESS; +} + +struct aml_layout_ops aml_layout_column_ops = { + aml_layout_column_deref, + aml_layout_column_deref, + aml_layout_column_order, + aml_layout_column_dims, + aml_layout_column_dims, + aml_layout_dense_ndims, + aml_layout_dense_element_size, + aml_layout_column_reshape, + aml_layout_column_slice, + aml_layout_column_slice, +}; + +/******************************************************************************* + * ROW OPERATORS: + ******************************************************************************/ + +void *aml_layout_row_deref(const struct aml_layout_data *data, + const size_t *coords) +{ + const struct aml_layout_dense *d; + char *ptr; + + d = (const struct aml_layout_dense *)data; + ptr = (char *) d->ptr; + + for (size_t i = 0; i < d->ndims; i++) { + ptr += + coords[i] * + d->cpitch[d->ndims - i - 1] * + d->stride[d->ndims - i - 1]; + } + return (void *) ptr; +} + +int aml_layout_row_order(const struct aml_layout_data *data) +{ + (void) data; + return AML_LAYOUT_ORDER_ROW_MAJOR; +} + +int aml_layout_row_dims(const struct aml_layout_data *data, size_t *dims) +{ + const struct aml_layout_dense *d; + + d = (const struct aml_layout_dense *)data; + for (size_t i = 0; i < d->ndims; i++) + dims[i] = d->dims[d->ndims - i - 1]; + return 0; +} + +int aml_layout_row_reshape(const struct aml_layout_data *data, + struct aml_layout **output, + const size_t ndims, + const size_t *dims) +{ + struct aml_layout *layout; + const struct aml_layout_dense *d; + size_t stride[ndims]; + size_t cpitch[ndims + 1]; + size_t n_dims[ndims]; + int err; + + d = (const struct aml_layout_dense *)data; + layout = aml_layout_dense_alloc(ndims); + if (layout == NULL) + return -AML_ENOMEM; + + for (size_t i = 0; i < ndims; i++) + n_dims[ndims - i - 1] = dims[i]; + + err = reshape_dims(d, ndims, n_dims, stride, cpitch); + if (err != AML_SUCCESS) { + free(layout); + return err; + } + + aml_layout_dense_init_cpitch(layout, + d->ptr, + ndims, + n_dims, + stride, + cpitch); + + layout->ops = &aml_layout_row_ops; + *output = layout; + return AML_SUCCESS; +} + +int aml_layout_row_slice(const struct aml_layout_data *data, + struct aml_layout **output, + const size_t *dims, + const size_t *offsets, + const size_t *strides) +{ + struct aml_layout *layout; + const struct aml_layout_dense *d; + void *ptr; + + d = (const struct aml_layout_dense *)data; + + size_t cpitch[d->ndims + 1]; + size_t n_offsets[d->ndims]; + size_t n_dims[d->ndims]; + size_t n_strides[d->ndims]; + + layout = aml_layout_dense_alloc(d->ndims); + if (layout == NULL) + return -AML_ENOMEM; + + for (size_t i = 0; i < d->ndims; i++) { + n_offsets[i] = offsets[d->ndims - i - 1]; + n_dims[i] = dims[d->ndims - i - 1]; + n_strides[i] = strides[d->ndims - i - 1]; + } + + cpitch[d->ndims] = d->cpitch[d->ndims]; + for (size_t i = 0; i < d->ndims; i++) { + cpitch[i] = d->cpitch[i]; + n_strides[i] *= d->stride[i]; + cpitch[d->ndims] -= cpitch[i] * n_offsets[i] * d->stride[i]; + } + + ptr = aml_layout_column_deref(data, n_offsets); + aml_layout_dense_init_cpitch(layout, + ptr, + d->ndims, + n_dims, + n_strides, + cpitch); + layout->ops = &aml_layout_row_ops; + + *output = layout; + return AML_SUCCESS; +} + +int aml_layout_row_slice_native(const struct aml_layout_data *data, + struct aml_layout **output, + const size_t *offsets, + const size_t *dims, + const size_t *strides) +{ + struct aml_layout *layout; + const struct aml_layout_dense *d; + void *ptr; + + d = (const struct aml_layout_dense *)data; + + size_t cpitch[d->ndims + 1]; + size_t new_strides[d->ndims]; + + layout = aml_layout_dense_alloc(d->ndims); + if (layout == NULL) + return -AML_ENOMEM; + + cpitch[d->ndims] = d->cpitch[d->ndims]; + for (size_t i = 0; i < d->ndims; i++) { + cpitch[i] = d->cpitch[i]; + new_strides[i] = strides[i] * d->stride[i]; + cpitch[d->ndims] -= cpitch[i] * offsets[i] * d->stride[i]; + } + + ptr = aml_layout_column_deref(data, offsets); + aml_layout_dense_init_cpitch(layout, + ptr, + d->ndims, + dims, + new_strides, + cpitch); + layout->ops = &aml_layout_row_ops; + *output = layout; + + return AML_SUCCESS; +} + +struct aml_layout_ops aml_layout_row_ops = { + aml_layout_row_deref, + aml_layout_column_deref, + aml_layout_row_order, + aml_layout_row_dims, + aml_layout_row_dims, + aml_layout_dense_ndims, + aml_layout_dense_element_size, + aml_layout_row_reshape, + aml_layout_row_slice, + aml_layout_row_slice_native +}; + diff --git a/src/layout/layout.c b/src/layout/layout.c new file mode 100644 index 0000000000000000000000000000000000000000..dd8f79a66df9e4b3585d8c3707c26493d968d3c6 --- /dev/null +++ b/src/layout/layout.c @@ -0,0 +1,279 @@ +/******************************************************************************* + * Copyright 2019 UChicago Argonne, LLC. + * (c.f. AUTHORS, LICENSE) + * + * This file is part of the AML project. + * For more info, see https://xgitlab.cels.anl.gov/argo/aml + * + * SPDX-License-Identifier: BSD-3-Clause +*******************************************************************************/ + +#include "aml.h" + +static int +aml_check_layout_coords(const struct aml_layout *layout, + int (*get_dims)(const struct aml_layout_data *, + size_t *), const size_t *coords) +{ + size_t ndims = layout->ops->ndims(layout->data); + size_t dims[ndims]; + int err = AML_SUCCESS; + + err = get_dims(layout->data, dims); + if (err != AML_SUCCESS) + return err; + + while (ndims--) + if (coords[ndims] >= dims[ndims]) + return -AML_EINVAL; + + return AML_SUCCESS; +} + +void *aml_layout_deref(const struct aml_layout *layout, const size_t *coords) +{ + assert(layout != NULL && + layout->ops != NULL && + layout->ops->deref != NULL); + + return layout->ops->deref(layout->data, coords); +} + +void *aml_layout_deref_safe(const struct aml_layout *layout, + const size_t *coords) +{ + assert(layout != NULL && + layout->ops != NULL && + layout->ops->deref != NULL && + layout->ops->ndims != NULL && + layout->ops->dims != NULL); + + assert(aml_check_layout_coords(layout, + layout->ops->dims, + coords) == AML_SUCCESS); + + return layout->ops->deref(layout->data, coords); +} + +void *aml_layout_deref_native(const struct aml_layout *layout, + const size_t *coords) +{ + assert(layout != NULL && + layout->ops != NULL && + layout->ops->deref_native != NULL && + layout->ops->ndims != NULL && + layout->ops->dims_native != NULL); + + return layout->ops->deref_native(layout->data, coords); +} + +int aml_layout_order(const struct aml_layout *layout) +{ + assert(layout != NULL && + layout->ops != NULL && + layout->ops->order != NULL); + + return layout->ops->order(layout->data); +} + +int aml_layout_dims(const struct aml_layout *layout, size_t *dims) +{ + assert(layout != NULL && + layout->ops != NULL && + layout->ops->dims != NULL); + + return layout->ops->dims(layout->data, dims); +} + +int aml_layout_dims_native(const struct aml_layout *layout, size_t *dims) +{ + assert(layout != NULL && + layout->ops != NULL && + layout->ops->dims_native != NULL); + + return layout->ops->dims_native(layout->data, dims); +} + +size_t aml_layout_ndims(const struct aml_layout *layout) +{ + assert(layout != NULL && + layout->ops != NULL && + layout->ops->ndims != NULL); + + return layout->ops->ndims(layout->data); +} + +size_t aml_layout_element_size(const struct aml_layout *layout) +{ + assert(layout != NULL && + layout->ops != NULL && + layout->ops->element_size != NULL); + + return layout->ops->element_size(layout->data); +} + +static int aml_layout_check_elements(const struct aml_layout *layout, + const size_t ndims, + const size_t *dims) +{ + size_t layout_ndims; + size_t n = 0, m = 0; + + assert(layout->ops->ndims != NULL && + layout->ops->dims != NULL); + + layout_ndims = layout->ops->ndims(layout->data); + + size_t layout_dims[layout_ndims]; + + assert(layout->ops->dims(layout->data, layout_dims) == AML_SUCCESS); + + for (size_t i = 0; i < ndims; i++) + n *= dims[i]; + + for (size_t i = 0; i < layout_ndims; i++) + m *= layout_dims[i]; + + if (m != n) + return -AML_EINVAL; + + return AML_SUCCESS; +} + +int aml_layout_reshape(const struct aml_layout *layout, + struct aml_layout **output, + const size_t ndims, + const size_t *dims) +{ + assert(ndims != 0 && + output != NULL && + layout != NULL && + layout->ops != NULL); + + if (layout->ops->reshape == NULL) + return -AML_ENOTSUP; + + assert(aml_layout_check_elements(layout, ndims, dims) == AML_SUCCESS); + + struct aml_layout *result = NULL; + + int err = layout->ops->reshape(layout->data, &result, ndims, dims); + + if (err == AML_SUCCESS) + *output = result; + + return err; +} + +/** + * This function will collect the layout dimensions and check that + * the slice queried will fit into the layout. + **/ +static int +aml_check_layout_slice(const struct aml_layout *layout, + int (*get_dims)(const struct aml_layout_data *, + size_t *), + const size_t *dims, + const size_t *offsets, + const size_t *strides) +{ + assert(layout->ops->ndims != NULL && + layout->ops->dims != NULL); + + int err = AML_SUCCESS; + size_t ndims = layout->ops->ndims(layout->data); + size_t n_elements; + size_t layout_dims[ndims]; + + err = get_dims(layout->data, layout_dims); + if (err != AML_SUCCESS) + return err; + + for (size_t i = 0; i < ndims; i++) { + n_elements = offsets[i] + (dims[i]-1) * strides[i]; + + if (n_elements > layout_dims[i]) + return -AML_EINVAL; + } + + return AML_SUCCESS; +} + +int aml_layout_slice(const struct aml_layout *layout, + struct aml_layout **reshaped_layout, + const size_t *dims, + const size_t *offsets, + const size_t *strides) +{ + assert(layout != NULL && + layout->ops != NULL); + + if (layout->ops->slice == NULL) + return -AML_ENOTSUP; + + size_t ndims = aml_layout_ndims(layout); + struct aml_layout *result; + int err; + size_t _offsets[ndims]; + size_t _strides[ndims]; + + if (offsets) + memcpy(_offsets, offsets, ndims * sizeof(*offsets)); + else + for (size_t i = 0; i < ndims; i++) + _offsets[i] = 0; + + if (strides) + memcpy(_strides, strides, ndims * sizeof(*strides)); + else + for (size_t i = 0; i < ndims; i++) + _strides[i] = 1; + + assert(aml_check_layout_slice(layout, + layout->ops->dims, + dims, + _offsets, + _strides) == AML_SUCCESS); + + err = layout->ops->slice(layout->data, + &result, + dims, + _offsets, + _strides); + if (err == AML_SUCCESS) + *reshaped_layout = result; + + return err; +} + +int aml_layout_slice_native(const struct aml_layout *layout, + struct aml_layout **reshaped_layout, + const size_t *offsets, + const size_t *dims, + const size_t *strides) +{ + assert(layout != NULL && + layout->ops != NULL); + + if (layout->ops->slice_native == NULL) + return -AML_ENOTSUP; + + assert(layout->ops->ndims != NULL && + layout->ops->dims_native != NULL); + + struct aml_layout *result; + int err; + + assert(aml_check_layout_slice(layout, + layout->ops->dims_native, + dims, + offsets, + strides) == AML_SUCCESS); + + err = layout->ops->slice_native(layout->data, + &result, dims, offsets, strides); + if (err == AML_SUCCESS) + *reshaped_layout = result; + + return err; +} diff --git a/src/layout/reshape.c b/src/layout/reshape.c new file mode 100644 index 0000000000000000000000000000000000000000..ace5a43642820a80fcbdc6f58454ab9b92656b07 --- /dev/null +++ b/src/layout/reshape.c @@ -0,0 +1,259 @@ +/******************************************************************************* + * Copyright 2019 UChicago Argonne, LLC. + * (c.f. AUTHORS, LICENSE) + * + * This file is part of the AML project. + * For more info, see https://xgitlab.cels.anl.gov/argo/aml + * + * SPDX-License-Identifier: BSD-3-Clause +*******************************************************************************/ + +#include + +#include "aml.h" +#include "aml/layout/dense.h" +#include "aml/layout/reshape.h" + +static struct aml_layout *aml_layout_reshape_alloc(const size_t ndims, + const size_t target_ndims) +{ + struct aml_layout *l; + struct aml_layout_data_reshape *r; + char *c; + + l = malloc(sizeof(struct aml_layout) + + sizeof(struct aml_layout_data_reshape) + + 2 * ndims * sizeof(size_t) + + target_ndims * sizeof(size_t)); + if (l == NULL) + return NULL; + + c = (char *)l; + c += sizeof(*l); + r = (struct aml_layout_data_reshape *)c; + c += sizeof(*r); + r->dims = (size_t *)c; + c += ndims * sizeof(*r->dims); + r->coffsets = (size_t *)c; + c += ndims * sizeof(*r->coffsets); + r->target_dims = (size_t *)c; + + r->target = NULL; + r->target_ndims = target_ndims; + r->ndims = ndims; + l->data = (struct aml_layout_data *) r; + return l; +} + +int aml_layout_reshape_create(struct aml_layout **layout, + struct aml_layout *target, + const int order, + const size_t ndims, + const size_t *dims) +{ + struct aml_layout *output; + struct aml_layout_data_reshape *data; + size_t target_ndims; + size_t prod; + size_t target_prod; + + if (layout == NULL || target == NULL || ndims == 0) + return -AML_EINVAL; + + target_ndims = aml_layout_ndims(target); + output = aml_layout_reshape_alloc(ndims, target_ndims); + if (output == NULL) + return -AML_ENOMEM; + + data = (struct aml_layout_data_reshape *)output->data; + data->target = target; + + switch (AML_LAYOUT_ORDER(order)) { + case AML_LAYOUT_ORDER_ROW_MAJOR: + output->ops = &aml_layout_reshape_row_ops; + for (size_t i = 0; i < ndims; i++) + data->dims[i] = dims[ndims-i-1]; + break; + + case AML_LAYOUT_ORDER_COLUMN_MAJOR: + output->ops = &aml_layout_reshape_column_ops; + memcpy(data->dims, dims, ndims * sizeof(size_t)); + break; + default: + free(output); + return -AML_EINVAL; + + } + + size_t target_dims[target_ndims]; + + switch (aml_layout_order(target)) { + case AML_LAYOUT_ORDER_ROW_MAJOR: + aml_layout_dims(target, target_dims); + for (size_t i = 0; i < target_ndims; i++) + data->target_dims[i] = target_dims[target_ndims-i-1]; + break; + case AML_LAYOUT_ORDER_COLUMN_MAJOR: + aml_layout_dims(target, data->target_dims); + break; + default: + free(output); + return -AML_EINVAL; + } + + prod = 1; + for (size_t i = 0; i < ndims; i++) { + data->coffsets[i] = prod; + prod *= data->dims[i]; + } + target_prod = 1; + for (size_t i = 0; i < data->target_ndims; i++) + target_prod *= data->target_dims[i]; + + if (target_prod != prod) { + free(output); + return -AML_EINVAL; + } + + *layout = output; + return AML_SUCCESS; +} + +void aml_layout_reshape_destroy(struct aml_layout **layout) +{ + if (layout == NULL || *layout == NULL) + return; + free(*layout); + *layout = NULL; +} + +/******************************************************************************* + * COLUMN OPERATORS: + ******************************************************************************/ + +void *aml_layout_reshape_column_deref(const struct aml_layout_data *data, + const size_t *coords) +{ + const struct aml_layout_data_reshape *d; + + d = (const struct aml_layout_data_reshape *)data; + + size_t offset = 0; + size_t target_coords[d->target_ndims]; + + for (size_t i = 0; i < d->ndims; i++) + offset += coords[i] * d->coffsets[i]; + + for (size_t i = 0; i < d->target_ndims; i++) { + target_coords[i] = offset % d->target_dims[i]; + offset /= d->target_dims[i]; + } + + return d->target->ops->deref_native(d->target->data, target_coords); +} + +int aml_layout_reshape_column_order(const struct aml_layout_data *data) +{ + (void) data; + return AML_LAYOUT_ORDER_COLUMN_MAJOR; +} + +int aml_layout_reshape_column_dims(const struct aml_layout_data *data, + size_t *dims) +{ + const struct aml_layout_data_reshape *d; + + d = (const struct aml_layout_data_reshape *)data; + + memcpy((void *)dims, (void *)d->dims, sizeof(size_t)*d->ndims); + + return 0; +} + +size_t aml_layout_reshape_ndims(const struct aml_layout_data *data) +{ + const struct aml_layout_data_reshape *d; + + d = (const struct aml_layout_data_reshape *)data; + + return d->ndims; +} + +size_t aml_layout_reshape_element_size(const struct aml_layout_data *data) +{ + const struct aml_layout_data_reshape *d; + + d = (const struct aml_layout_data_reshape *) data; + + return aml_layout_element_size(d->target); +} + +struct aml_layout_ops aml_layout_reshape_column_ops = { + aml_layout_reshape_column_deref, + aml_layout_reshape_column_deref, + aml_layout_reshape_column_order, + aml_layout_reshape_column_dims, + aml_layout_reshape_column_dims, + aml_layout_reshape_ndims, + aml_layout_reshape_element_size, + NULL, + NULL, + NULL, +}; + +/******************************************************************************* + * ROW OPERATORS: + ******************************************************************************/ + +void *aml_layout_reshape_row_deref(const struct aml_layout_data *data, + const size_t *coords) +{ + const struct aml_layout_data_reshape *d; + + d = (const struct aml_layout_data_reshape *)data; + + size_t offset = 0; + size_t target_coords[d->target_ndims]; + + for (size_t i = 0; i < d->ndims; i++) + offset += coords[d->ndims - i - 1] * d->coffsets[i]; + + for (size_t i = 0; i < d->target_ndims; i++) { + target_coords[i] = offset % d->target_dims[i]; + offset /= d->target_dims[i]; + } + + return d->target->ops->deref_native(d->target->data, target_coords); +} + +int aml_layout_reshape_row_order(const struct aml_layout_data *data) +{ + (void) data; + return AML_LAYOUT_ORDER_ROW_MAJOR; +} + +int aml_layout_reshape_row_dims(const struct aml_layout_data *data, + size_t *dims) +{ + const struct aml_layout_data_reshape *d; + + d = (const struct aml_layout_data_reshape *)data; + + for (size_t i = 0; i < d->ndims; i++) + dims[i] = d->dims[d->ndims - i - 1]; + + return 0; +} + +struct aml_layout_ops aml_layout_reshape_row_ops = { + aml_layout_reshape_row_deref, + aml_layout_reshape_column_deref, + aml_layout_reshape_row_order, + aml_layout_reshape_row_dims, + aml_layout_reshape_column_dims, + aml_layout_reshape_ndims, + aml_layout_reshape_element_size, + NULL, + NULL, + NULL, +}; diff --git a/tests/Makefile.am b/tests/Makefile.am index 2409170da41dbaad5abfa2f1f1c9fc6905f2fd48..04dd98c556d21af6aab7b9af17fc29220f3b1642 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -16,6 +16,8 @@ AREA_TESTS = \ area/test_area \ area/test_linux +LAYOUT_TESTS = layout/test_layout + TILING_TESTS = tiling/test_tiling \ tiling/test_tiling_2d @@ -28,6 +30,7 @@ SCRATCH_TESTS = scratch/test_scratch_seq \ # unit tests UNIT_TESTS = $(UTILS_TESTS) \ $(TILING_TESTS) \ + $(LAYOUT_TESTS) \ $(BINDING_TESTS) \ $(AREA_TESTS) \ $(DMA_LINUX_TESTS) \ diff --git a/tests/layout/test_layout.c b/tests/layout/test_layout.c new file mode 100644 index 0000000000000000000000000000000000000000..1dfb2a13627d3199406f7ed28772d075225d0aab --- /dev/null +++ b/tests/layout/test_layout.c @@ -0,0 +1,593 @@ +#include +#include +#include +#include +#include + +void test_slice_contiguous(void) +{ + int memory[6*5*4]; + + size_t dims_col[3] = {4, 5, 6}; + size_t offsets_col[3] = {2, 2, 3}; + size_t new_dims_col[3] = {2, 3, 3}; + + size_t dims_row[3] = {6, 5, 4}; + size_t offsets_row[3] = {3, 2, 2}; + size_t new_dims_row[3] = {3, 3, 2}; + + size_t coords[3]; + void *ptr; + int val; + + int l = 0; + for(size_t i = 0; i < dims_col[2]; i++) + for(size_t j = 0; j < dims_col[1]; j++) + for(size_t k = 0; k < dims_col[0]; k++, l++) + memory[i*dims_col[1]*dims_col[0] + j*dims_col[0] + k] = l; + + struct aml_layout *a, *b; + + assert(aml_layout_dense_create(&a, + (void *) memory, + AML_LAYOUT_ORDER_COLUMN_MAJOR, + sizeof(int), + sizeof(dims_col)/sizeof(*dims_col), + dims_col, + NULL, + dims_col) == AML_SUCCESS); + + assert(aml_layout_slice(a, &b, new_dims_col, offsets_col, NULL) == AML_SUCCESS); + assert(AML_LAYOUT_ORDER_COLUMN_MAJOR == aml_layout_order(b)); + + + for(size_t i = 0; i < new_dims_col[0]; i++) + for(size_t j = 0; j < new_dims_col[1]; j++) + for(size_t k = 0; k < new_dims_col[2]; k++) + { + coords[0] = i; coords[1] = j; coords[2] = k; + + val = memory[ (i + offsets_col[0]) + + (j + offsets_col[1]) * dims_col[0] + + (k + offsets_col[2]) * dims_col[0] * dims_col[1] ]; + + ptr = aml_layout_deref_safe(b, coords); + //fprintf(stderr, "%d == %d\n", val, *(int *)ptr); + assert( val == *(int *)ptr); + } + free(a); + free(b); + + assert(aml_layout_dense_create(&a, + (void *) memory, + AML_LAYOUT_ORDER_ROW_MAJOR, + sizeof(int), + sizeof(dims_row)/sizeof(*dims_row), + dims_row, + NULL, + dims_row) == AML_SUCCESS); + + assert(aml_layout_slice(a, &b, new_dims_row, offsets_row, NULL) == AML_SUCCESS); + assert(AML_LAYOUT_ORDER_ROW_MAJOR == aml_layout_order(b)); + + for(size_t i = 0; i < new_dims_row[0]; i++) + for(size_t j = 0; j < new_dims_row[1]; j++) + for(size_t k = 0; k < new_dims_row[2]; k++) + { + coords[0] = i; coords[1] = j; coords[2] = k; + ptr = aml_layout_deref_safe(b, coords); + + val = memory[ (i + offsets_row[0]) * dims_row[1] * dims_row[2] + + (j + offsets_row[1]) * dims_row[2] + + (k + offsets_row[2]) ]; + + //fprintf(stderr, "%d == %d\n", val, *(int *)ptr); + assert( val == *(int *)ptr); + + } + free(a); + free(b); +} + +void test_slice_strided(void) +{ + int memory[12][5][8]; + + size_t stride[3] = {2, 1, 2}; + + size_t dims_col[3] = {4, 5, 6}; + size_t offsets_col[3] = {1, 2, 0}; + size_t new_dims_col[3] = {2, 3, 3}; + size_t new_stride_col[3] = {2, 1, 1}; + size_t pitch_col[3] = {8, 5, 12}; + + size_t dims_row[3] = {6, 5, 4}; + size_t pitch_row[3] = {12, 5, 8}; + size_t offsets_row[3] = {0, 2, 1}; + size_t new_dims_row[3] = {3, 3, 2}; + size_t new_stride_row[3] = {1, 1, 2}; + + size_t coords[3]; + void *ptr; + + int l = 0; + for(size_t i = 0; i < 12; i++) + for(size_t j = 0; j < 5; j++) + for(size_t k = 0; k < 8; k++, l++) + memory[i][j][k] = l; + + struct aml_layout *a, *b; + + assert(aml_layout_dense_create(&a, + (void *) memory, + AML_LAYOUT_ORDER_COLUMN_MAJOR, + sizeof(int), + 3, + dims_col, + stride, + pitch_col) == AML_SUCCESS); + + assert(aml_layout_slice(a, &b, new_dims_col, offsets_col, new_stride_col) == AML_SUCCESS); + + for(size_t i = 0; i < 3; i++) + for(size_t j = 0; j < 3; j++) + for(size_t k = 0; k < 2; k++){ + coords[0] = k; coords[1] = j; coords[2] = i; + ptr = aml_layout_deref_safe(b, coords); + + assert( memory[stride[2] * (offsets_col[2] + new_stride_col[2] * i)][ + stride[1] * (offsets_col[1] + new_stride_col[1] * j)][ + stride[0] * (offsets_col[0] + new_stride_col[0] * k)] == *(int *)ptr); + } + + free(a); + free(b); + + assert(aml_layout_dense_create(&a, + (void *) memory, + AML_LAYOUT_ORDER_ROW_MAJOR, + sizeof(int), + 3, + dims_row, + stride, + pitch_row) == AML_SUCCESS); + + assert(aml_layout_slice(a, &b, new_dims_row, offsets_row, new_stride_row) == AML_SUCCESS); + + for(size_t i = 0; i < 3; i++) + for(size_t j = 0; j < 3; j++) + for(size_t k = 0; k < 2; k++){ + coords[0] = i; coords[1] = j; coords[2] = k; + ptr = aml_layout_deref_safe(b, coords); + + assert( memory[stride[2] * (offsets_col[2] + new_stride_col[2] * i)][stride[1] * (offsets_col[1] + new_stride_col[1] * j)][stride[0] * (offsets_col[0] + new_stride_col[0] * k)] == *(int *)ptr); + } + + free(a); + free(b); + +} + +void test_reshape_contiguous(void) +{ + int memory[4*5*6]; + + size_t dims_col[3] = {4, 5, 6}; + size_t dims_row[3] = {6, 5, 4}; + + size_t stride[3] = {1, 1, 1}; + + size_t new_dims_col[2] = {24, 5}; + size_t new_dims_row[2] = {5, 24}; + + size_t coords[2]; + void *b_ptr, *c_ptr; + + int i; + for(i = 0; i < 4*5*6; i++) + memory[i] = i; + + struct aml_layout *a, *b, *c; + + assert(aml_layout_dense_create(&a, + (void *) memory, + AML_LAYOUT_ORDER_COLUMN_MAJOR, + sizeof(int), + 3, + dims_col, + stride, + dims_col) == AML_SUCCESS); + + assert(aml_layout_reshape(a, &b, 2, new_dims_col) == AML_SUCCESS); + assert(AML_LAYOUT_ORDER_COLUMN_MAJOR == aml_layout_order(b)); + + aml_layout_reshape_create(&c, + a, + AML_LAYOUT_ORDER_COLUMN_MAJOR, + 2, + new_dims_col); + assert(AML_LAYOUT_ORDER_COLUMN_MAJOR == aml_layout_order(c)); + + i = 0; + for(size_t j = 0; j < 5; j++) + for(size_t k = 0; k < 24; k++, i++) { + coords[0] = k; coords[1] = j; + b_ptr = aml_layout_deref_safe(b, coords); + c_ptr = aml_layout_deref_safe(c, coords); + assert(i == *(int *)b_ptr); + assert(i == *(int *)c_ptr); + } + + free(a); + free(b); + free(c); + + assert(aml_layout_dense_create(&a, + (void *) memory, + AML_LAYOUT_ORDER_ROW_MAJOR, + sizeof(int), + 3, + dims_row, + stride, + dims_row) == AML_SUCCESS); + assert(aml_layout_reshape(a, &b, 2, new_dims_row) == AML_SUCCESS); + assert(AML_LAYOUT_ORDER_ROW_MAJOR == aml_layout_order(b)); + + aml_layout_reshape_create(&c, + a, + AML_LAYOUT_ORDER_ROW_MAJOR, + 2, + new_dims_row); + assert(AML_LAYOUT_ORDER_ROW_MAJOR == aml_layout_order(c)); + + i = 0; + for(size_t j = 0; j < 5; j++) + for(size_t k = 0; k < 24; k++, i++) { + coords[0] = j; coords[1] = k; + b_ptr = aml_layout_deref_safe(b, coords); + c_ptr = aml_layout_deref_safe(c, coords); + assert(i == *(int *)b_ptr); + assert(i == *(int *)c_ptr); + } + + free(a); + free(b); + free(c); +} + +void test_reshape_discontiguous(void) +{ + int memory[7][6][5]; + + size_t dims_col[3] = {4, 5, 6}; + size_t dims_row[3] = {6, 5, 4}; + + size_t stride[3] = {1, 1, 1}; + + size_t pitch_col[3] = {5, 6, 7}; + size_t pitch_row[3] = {7, 6, 5}; + + size_t new_dims_col[5] = {2, 2, 5, 2, 3}; + size_t new_dims_row[5] = {3, 2, 5, 2, 2}; + + size_t coords[5]; + void * ptr; + + int i = 0; + for(int j = 0; j < 6; j++) + for(int k = 0; k < 5; k++) + for(int l = 0; l < 4; l++, i++) + memory[j][k][l] = i; + + struct aml_layout *a, *b, *c; + + assert(aml_layout_dense_create(&a, + (void *) memory, + AML_LAYOUT_ORDER_COLUMN_MAJOR, + sizeof(int), + 3, + dims_col, + stride, + pitch_col) == AML_SUCCESS); + + assert(aml_layout_reshape(a, &b, 5, new_dims_col) == AML_SUCCESS); + + aml_layout_reshape_create(&c, + a, + AML_LAYOUT_ORDER_COLUMN_MAJOR, + 5, + new_dims_col); + + i = 0; + for(size_t j = 0; j < 3; j++) + for(size_t k = 0; k < 2; k++) + for(size_t l = 0; l < 5; l++) + for(size_t m = 0; m < 2; m++) + for(size_t n = 0; n < 2; n++, i++) { + coords[0] = n; + coords[1] = m; + coords[2] = l; + coords[3] = k; + coords[4] = j; + ptr = aml_layout_deref_safe(b, coords); + assert(i == *(int *)ptr); + ptr = aml_layout_deref_safe(c, coords); + assert(i == *(int *)ptr); + } + + free(a); + free(b); + free(c); + + assert(aml_layout_dense_create(&a, + (void *) memory, + AML_LAYOUT_ORDER_ROW_MAJOR, + sizeof(int), + 3, + dims_row, + stride, + pitch_row) == AML_SUCCESS); + + assert(aml_layout_reshape(a, &b, 5, new_dims_row) == AML_SUCCESS); + + aml_layout_reshape_create(&c, + a, + AML_LAYOUT_ORDER_ROW_MAJOR, + 5, + new_dims_row); + + i = 0; + for(size_t j = 0; j < 3; j++) + for(size_t k = 0; k < 2; k++) + for(size_t l = 0; l < 5; l++) + for(size_t m = 0; m < 2; m++) + for(size_t n = 0; n < 2; n++, i++) { + coords[0] = j; + coords[1] = k; + coords[2] = l; + coords[3] = m; + coords[4] = n; + ptr = aml_layout_deref_safe(b, coords); + assert(i == *(int *)ptr); + ptr = aml_layout_deref_safe(c, coords); + assert(i == *(int *)ptr); + } + + free(a); + free(b); + free(c); +} + +void test_reshape_strided(void) +{ + int memory[12][5][8]; + + size_t dims_col[3] = {4, 5, 6}; + size_t dims_row[3] = {6, 5, 4}; + + size_t stride[3] = {2, 1, 2}; + + size_t pitch_col[3] = {8, 5, 12}; + size_t pitch_row[3] = {12, 5, 8}; + + size_t new_dims_col[4] = {2, 10, 2, 3}; + size_t new_dims_row[4] = {3, 2, 10, 2}; + + size_t coords[4]; + void *ptr; + + int i = 0; + for(int j = 0; j < 6; j++) + for(int k = 0; k < 5; k++) + for(int l = 0; l < 4; l++, i++) + memory[2*j][1*k][2*l] = i; + + struct aml_layout *a, *b, *c; + + assert(aml_layout_dense_create(&a, + (void *) memory, + AML_LAYOUT_ORDER_COLUMN_MAJOR, + sizeof(int), + 3, + dims_col, + stride, + pitch_col) == AML_SUCCESS); + + assert(aml_layout_reshape(a, &b, 4, new_dims_col) == AML_SUCCESS); + + aml_layout_reshape_create(&c, + a, + AML_LAYOUT_ORDER_COLUMN_MAJOR, + 4, + new_dims_col); + + i = 0; + for(size_t j = 0; j < 3; j++) + for(size_t k = 0; k < 2; k++) + for(size_t l = 0; l < 10; l++) + for(size_t m = 0; m < 2; m++, i++) { + coords[0] = m; + coords[1] = l; + coords[2] = k; + coords[3] = j; + ptr = aml_layout_deref_safe(b, coords); + assert(i == *(int *)ptr); + ptr = aml_layout_deref_safe(c, coords); + assert(i == *(int *)ptr); + } + + free(a); + free(b); + free(c); + + assert(aml_layout_dense_create(&a, + (void *) memory, + AML_LAYOUT_ORDER_ROW_MAJOR, + sizeof(int), + 3, + dims_row, + stride, + pitch_row) == AML_SUCCESS); + + assert(aml_layout_reshape(a, &b, 4, new_dims_row) == AML_SUCCESS); + + aml_layout_reshape_create(&c, + a, + AML_LAYOUT_ORDER_ROW_MAJOR, + 4, + new_dims_row); + + i = 0; + for(size_t j = 0; j < 3; j++) + for(size_t k = 0; k < 2; k++) + for(size_t l = 0; l < 10; l++) + for(size_t m = 0; m < 2; m++, i++) { + coords[0] = j; + coords[1] = k; + coords[2] = l; + coords[3] = m; + ptr = aml_layout_deref_safe(b, coords); + assert(i == *(int *)ptr); + ptr = aml_layout_deref_safe(b, coords); + assert(i == *(int *)ptr); + } + + free(a); + free(b); + free(c); +} + +void test_base(void) +{ + struct aml_layout *a, *b; + + /* padd the dims to the closest multiple of 2 */ + float memory[16][12][8][8][4]; + size_t pitch[5] = {4, 8, 8, 12, 16}; + size_t cpitch[6] = {4, 4*4, 4*4*8, 4*4*8*8, 4*4*8*8*12, 4*4*8*8*12*16}; + size_t dims[5] = {2, 3, 7, 11, 13}; + size_t stride[5] = {1, 2, 1, 1, 1}; + + size_t dims_col[5] = {2, 3, 7, 11, 13}; + size_t dims_row[5] = {13, 11, 7, 3, 2}; + + size_t pitch_col[5] = {4, 8, 8, 12, 16}; + size_t pitch_row[5] = {16, 12, 8, 8, 4}; + + size_t stride_col[5] = {1, 2, 1, 1, 1}; + size_t stride_row[5] = {1, 1, 1, 2, 1}; + + for(size_t i = 0; i < 4*8*8*12*16; i++) + ((float*)(&memory[0][0][0][0][0]))[i] = (float)i; + + + /* initialize column order layouts */ + assert(aml_layout_dense_create(&a, + (void *) memory, + AML_LAYOUT_ORDER_COLUMN_MAJOR, + sizeof(int), + 5, + dims_col, + stride_col, + pitch_col) == AML_SUCCESS); + assert(aml_layout_dense_create(&b, + (void *) memory, + AML_LAYOUT_ORDER_COLUMN_MAJOR, + sizeof(int), + 5, + dims_col, + stride_col, + pitch_col) == AML_SUCCESS); + + struct aml_layout_dense *adataptr; + struct aml_layout_dense *bdataptr; + + adataptr = (struct aml_layout_dense *)a->data; + bdataptr = (struct aml_layout_dense *)b->data; + assert( (intptr_t)(adataptr->stride) - (intptr_t)(adataptr->dims) + == 5*sizeof(size_t) ); + assert( (intptr_t)(adataptr->pitch) - (intptr_t)(adataptr->dims) + == 10*sizeof(size_t) ); + assert( (intptr_t)(adataptr->cpitch) - (intptr_t)(adataptr->dims) + == 15*sizeof(size_t) ); + + /* some simple checks */ + assert(!memcmp(adataptr->dims, dims, sizeof(size_t)*5)); + assert(!memcmp(adataptr->stride, stride, sizeof(size_t)*5)); + assert(!memcmp(adataptr->pitch, pitch, sizeof(size_t)*5)); + assert(!memcmp(adataptr->cpitch, cpitch, sizeof(size_t)*6)); + assert(!memcmp(bdataptr->dims, dims, sizeof(size_t)*5)); + assert(!memcmp(bdataptr->stride, stride, sizeof(size_t)*5)); + assert(!memcmp(bdataptr->pitch, pitch, sizeof(size_t)*5)); + assert(!memcmp(bdataptr->cpitch, cpitch, sizeof(size_t)*6)); + + /* test column major subroutines */ + size_t dims_res[5]; + size_t coords_test_col[5] = { 1, 2, 3, 4, 5 }; + void *test_addr; + void *res_addr = (void *)&memory[5][4][3][2*2][1]; + + aml_layout_dims(a, dims_res); + assert(!memcmp(dims_res, dims_col, sizeof(size_t)*5)); + test_addr = aml_layout_deref(a, coords_test_col); + assert(res_addr == test_addr); + assert(AML_LAYOUT_ORDER_COLUMN_MAJOR == aml_layout_order(a)); + + free(a); + + /* initialize row order layouts */ + aml_layout_dense_create(&a, + (void *)memory, + AML_LAYOUT_ORDER_ROW_MAJOR, + sizeof(float), + 5, dims_row, + stride_row, + pitch_row); + + adataptr = (struct aml_layout_dense *)a->data; + bdataptr = (struct aml_layout_dense *)b->data; + assert( (intptr_t)(adataptr->stride) - (intptr_t)(adataptr->dims) + == 5*sizeof(size_t) ); + assert( (intptr_t)(adataptr->pitch) - (intptr_t)(adataptr->dims) + == 10*sizeof(size_t) ); + assert( (intptr_t)(adataptr->cpitch) - (intptr_t)(adataptr->dims) + == 15*sizeof(size_t) ); + + /* some simple checks */ + assert(!memcmp(adataptr->dims, dims, sizeof(size_t)*5)); + assert(!memcmp(adataptr->stride, stride, sizeof(size_t)*5)); + assert(!memcmp(adataptr->pitch, pitch, sizeof(size_t)*5)); + assert(!memcmp(adataptr->cpitch, cpitch, sizeof(size_t)*6)); + assert(!memcmp(bdataptr->dims, dims, sizeof(size_t)*5)); + assert(!memcmp(bdataptr->stride, stride, sizeof(size_t)*5)); + assert(!memcmp(bdataptr->pitch, pitch, sizeof(size_t)*5)); + assert(!memcmp(bdataptr->cpitch, cpitch, sizeof(size_t)*6)); + + /* test row major subroutines */ + size_t coords_test_row[5] = { 5, 4, 3, 2, 1 }; + aml_layout_dims(a, dims_res); + assert(!memcmp(dims_res, dims_row, sizeof(size_t)*5)); + test_addr = aml_layout_deref(a, coords_test_row); + assert(res_addr == test_addr); + assert(AML_LAYOUT_ORDER_ROW_MAJOR == aml_layout_order(a)); + + free(a); +} + +int main(int argc, char *argv[]) +{ + /* library initialization */ + aml_init(&argc, &argv); + + test_base(); + test_reshape_contiguous(); + test_reshape_discontiguous(); + test_reshape_strided(); + + test_slice_contiguous(); + test_slice_strided(); + + aml_finalize(); + return 0; +} +