diff options
Diffstat (limited to 'compute_kernel_writer')
88 files changed, 11981 insertions, 0 deletions
diff --git a/compute_kernel_writer/CMakeLists.txt b/compute_kernel_writer/CMakeLists.txt new file mode 100644 index 0000000000..69a4cdd51f --- /dev/null +++ b/compute_kernel_writer/CMakeLists.txt @@ -0,0 +1,189 @@ +# Copyright (c) 2023 Arm Limited. +# +# SPDX-License-Identifier: MIT +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +cmake_minimum_required(VERSION 3.14 FATAL_ERROR) + +#--------------------------------------------------------------------- +# Compute Kernel Writer Project + +project(ComputeKernelWriter + VERSION 1.0.0 + LANGUAGES CXX +) + +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +include(GNUInstallDirs) + +message(STATUS "${CMAKE_PROJECT_NAME} ${CMAKE_PROJECT_VERSION}") + +#--------------------------------------------------------------------- +# Options + +option(CKW_ENABLE_OPENCL "Enable OpenCL code generation" OFF) +option(CKW_ENABLE_ASSERTS "Enable assertions. Always enabled in Debug builds" OFF) +option(CKW_BUILD_TESTING "Build the Compute Kernel Writer validation test suite" OFF) +option(CKW_BUILD_PROTOTYPE "Build the prototype implementation of kernel writer." OFF) +option(CKW_CCACHE "Use compiler cache for faster recompilation" OFF) + +#--------------------------------------------------------------------- +# Build configuration + +get_property(CKW_IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) + +# Allow only Release or Debug builds +if(NOT CKW_IS_MULTI_CONFIG) # Single-config generators + if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release CACHE STRING "Options: Release (default) or Debug" FORCE) + endif() +else() # Multi-config generators + list(REMOVE_ITEM CMAKE_CONFIGURATION_TYPES RelWithDebInfo MinSizeRel) +endif() + +# Simplistic CCache setup +if(CKW_CCACHE) + find_program(CCACHE_FOUND ccache) + if(CCACHE_FOUND) + set(CMAKE_C_COMPILER_LAUNCHER ${CACHE_FOUND}) + set(CMAKE_CXX_COMPILER_LAUNCHER ${CACHE_FOUND}) + endif() +endif() + +#--------------------------------------------------------------------- +# Library targets + +set(CKW_CXX_FLAGS + -Wall + -Werror + -Wextra + -Wdisabled-optimization + -Wformat=2 + -Winit-self + -Wstrict-overflow=2 + -Wswitch-default + -Woverloaded-virtual + -Wformat-security + -Wctor-dtor-privacy + -Wsign-promo + -Weffc++ + -pedantic +) +set(GNU_WARNINGS + -Wlogical-op + -Wstrict-null-sentinel +) +set(CKW_ASSERTS_OPTS + -fstack-protector-strong +) + +add_library(ckw) +target_compile_options(ckw + PUBLIC + ${CKW_CXX_FLAGS} + "$<$<CXX_COMPILER_ID:GNU>:${GNU_WARNINGS}>" + "$<$<CONFIG:Debug>:${CKW_ASSERTS_OPTS}>" + "$<$<BOOL:${CKW_ENABLE_ASSERTS}>:${CKW_ASSERTS_OPTS}>" + # Set CMAKE_CXX_FLAGS last so user can overwrite options + ${CMAKE_CXX_FLAGS} + PRIVATE + # Always optimize for binary size + $<$<CONFIG:Release>:-Os> +) + +target_compile_definitions(ckw PUBLIC + $<$<CONFIG:Debug>:COMPUTE_KERNEL_WRITER_DEBUG_ENABLED> + $<$<CONFIG:Debug>:COMPUTE_KERNEL_WRITER_ASSERTS_ENABLED> + $<$<BOOL:${CKW_ENABLE_ASSERTS}>:COMPUTE_KERNEL_WRITER_ASSERTS_ENABLED> + $<$<BOOL:${CKW_ENABLE_OPENCL}>:COMPUTE_KERNEL_WRITER_OPENCL_ENABLED> +) + +target_sources(ckw PRIVATE + src/types/ConstantData.cpp + src/types/DataTypeHelpers.cpp + src/Error.cpp + src/Helpers.cpp + src/ITile.cpp + src/Kernel.cpp + src/KernelArgument.cpp + src/KernelWriter.cpp + src/Tensor3dMapper.cpp + src/TensorInfo.cpp + src/TensorOperand.cpp + src/TensorSampler.cpp + src/TensorUtils.cpp + src/TileInfo.cpp + src/TileOperand.cpp + src/TileView.cpp +) + +if(CKW_ENABLE_OPENCL) + target_sources(ckw PRIVATE + src/cl/CLTensorArgument.cpp + src/cl/CLTensorComponent.cpp + src/cl/CLHelpers.cpp + src/cl/CLTile.cpp + src/cl/CLKernelWriter.cpp + src/cl/helpers/CLMemoryOpBufferHelper.cpp + src/cl/helpers/CLMemoryOpImage2dHelper.cpp + ) +endif() + +target_include_directories(ckw + PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include + PRIVATE ${CMAKE_CURRENT_LIST_DIR} +) + +#--------------------------------------------------------------------- +# Validation tests + +if(CKW_BUILD_TESTING) + add_executable(ckw_validation + validation/Validation.cpp + ) + + target_link_libraries(ckw_validation PRIVATE ckw) + target_include_directories(ckw_validation + PRIVATE ${CMAKE_CURRENT_LIST_DIR} + ) +endif() + +#--------------------------------------------------------------------- +# Prototype + +if(CKW_BUILD_PROTOTYPE) + add_subdirectory(prototype) +endif() + +#--------------------------------------------------------------------- +# Installing + +install(TARGETS ckw + CONFIGURATIONS Release + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} +) + +install(DIRECTORY include/ckw + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} +) diff --git a/compute_kernel_writer/README.md b/compute_kernel_writer/README.md new file mode 100644 index 0000000000..2c7636ca9f --- /dev/null +++ b/compute_kernel_writer/README.md @@ -0,0 +1,97 @@ +# Compute Kernel Writer + +Compute Kernel Writer is a tile-based, just-in-time code writer for deep learning and computer vision applications. +This tool offers a C++ interface to allow developers to write functions without a return type (called "kernels") +using their preferred programming language (at the moment, only OpenCL is supported). +The library is specifically designed to be lightweight and to offer an intuitive API for efficient code writing. + +## Getting started + +The fastest way to get started with Compute Kernel Writer is to build and run the test suite. +The following subsections show you how to do this. + +### Dependencies + +This project requires the following dependencies, obtainable via your preferred package manager, to be installed and +available on your system. + +* `build-essential` +* `cmake >= 3.14` +* (Optional) `ninja-build` + +In addition, the guide makes use of the following toolchains: + +* (Optional) `Arm GNU toolchain` available to download from + the [Arm Developer](https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads) website +* (Optional) `Android NDK toolset` available to download from + the [Android Developer](https://developer.android.com/ndk/downloads/index.html) website + +### Building and running tests + +#### Native compilation + +You can quickly compile the library on your computer by using the following commands: + +```shell +mkdir -p build && cd build +CXX=g++ cmake -G Ninja -DBUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=Release -DCKW_ENABLE_OPENCL=ON -DCKW_ENABLE_ASSERTS=ON -DCKW_BUILD_TESTING=ON -S .. +cmake --build . +``` + +The preceding commands build the library in release mode (`-DCMAKE_BUILD_TYPE=Release`) and targets OpenCL code +generation (`-DCKW_ENABLE_OPENCL=ON`). +In addition, code assertions are enabled (`-DCKW_ENABLE_ASSERTS=ON`) and the test suite is +built (`-DCKW_BUILD_TESTING=ON`). +Alternatively, choose to build a static instead of a shared library by setting `-DBUILD_SHARED_LIBS=OFF`. + +#### Cross-compile to Linux AArch64 + +The Arm GNU toolchain can be used to cross-compile the project to a Linux system with an AArch64 processor, like a +Raspberry Pi, using an x86_64 Linux host machine. + +```shell +mkdir -p build && cd build +CXX=aarch64-none-linux-gnu-g++ cmake -G Ninja -DBUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=Release -DCKW_ENABLE_OPENCL=ON -DCKW_ENABLE_ASSERTS=ON -DCKW_BUILD_TESTING=ON -S .. +cmake --build . +``` + +The build configuration is identical to the previous step but now requires specifying the target triple in the CXX +compiler (`CXX=aarch64-none-linux-gnu-g++`) to generate binaries for the target platform. + +#### Cross-compile to Android AArch64 + +Cross-compiling for Android systems requires the Android NDK toolset. The downloaded NDK contains the toolchain file +necessary for cross-compiling the project. + +```shell +mkdir -p build && cd build +cmake -G Ninja -DBUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=Release -DCKW_ENABLE_OPENCL=ON -DCKW_ENABLE_ASSERTS=ON -DCKW_BUILD_TESTING=ON -DCMAKE_TOOLCHAIN_FILE=<NDK>/build/cmake/android.toolchain.cmake -S .. +cmake --build . +``` + +This build re-uses the same build configuration as before, but this time does not require specifying the CXX compiler as +this (and other target-specific information) is handled by the toolchain file (`-DCMAKE_TOOLCHAIN_FILE`). + +#### Run the validation test suite + +Confirm the project has been built successfully by running the validation test suite. + +```shell +./ckw_validation +``` + +### List of build options + +This project can be configured with the following build options. Enable options by passing them to the CMake command, +preceded with `-D`. + +| Option | Description | +|:---------------------|:------------------------------------------------------------------------------------------------------------------------------------------| +| BUILD_SHARED_LIBS | Controls whether to build static or shared libraries. | +| CMAKE_BUILD_TYPE | The project build type or configuration. Choose from Release or Debug. <br/>The release build will always build for smallest binary size. | +| CKW_ENABLE_OPENCL | Enable OpenCL code generation. | +| CKW_ENABLE_ASSERTS | Enable assertions. Always enabled for Debug builds. | +| CKW_BUILD_TESTING | Build the validation test suite. | +| CKW_BUILD_PROTOTYPE | Build the prototype implementation. | +| CKW_CCACHE | Use compiler cache for faster recompilation. | +| CMAKE_TOOLCHAIN_FILE | When cross-compiling, set this variable to the path of the CMake toolchain file. | diff --git a/compute_kernel_writer/include/ckw/Error.h b/compute_kernel_writer/include/ckw/Error.h new file mode 100644 index 0000000000..6b80778957 --- /dev/null +++ b/compute_kernel_writer/include/ckw/Error.h @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef CKW_INCLUDE_CKW_ERROR_H +#define CKW_INCLUDE_CKW_ERROR_H + +#include <stdexcept> +#include <string> + +namespace ckw +{ +/** Creates the error message + * + * @param[in] file File in which the error occurred. + * @param[in] func Function in which the error occurred. + * @param[in] line Line in which the error occurred. + * @param[in] msg Message to display before abandoning. + * + * @return status containing the error + */ +std::string +create_error_msg(const std::string &file, const std::string &func, const std::string &line, const std::string &msg); + +/** Print the given message then throw an std::runtime_error. + * + * @param[in] msg Message to display. + */ +#define COMPUTE_KERNEL_WRITER_ERROR_ON_MSG(msg) \ + do \ + { \ + const std::string arg0(__FILE__); \ + const std::string arg1(__func__); \ + const std::string arg2(std::to_string(__LINE__)); \ + const std::string arg3(msg); \ + std::runtime_error(create_error_msg(arg0, arg1, arg2, arg3)); \ + } while (false) + +/** Mark the variables as unused. + * + * @param[in] ... Variables which are unused. + */ +#define CKW_UNUSED(...) ckw::ignore_unused(__VA_ARGS__) // NOLINT + +/** Mark the variables as unused. + * + * @param[in] ... Variables which are unused. + */ +template <typename... T> +inline void ignore_unused(T &&...) +{ +} + +/** Throw an std::runtime_error with the specified message. + * + * @param[in] msg The error message. + */ +#define CKW_THROW_MSG(msg) \ + do \ + { \ + const std::string file(__FILE__); \ + const std::string func(__func__); \ + const std::string line(std::to_string(__LINE__)); \ + const std::string message(msg); \ + \ + throw std::runtime_error(ckw::create_error_msg(file, func, line, message)); \ + } while (false) + +#ifdef COMPUTE_KERNEL_WRITER_ASSERTS_ENABLED + +/** If the condition is not met, throw an std::runtime_error with the specified message if assertion is enabled. + * + * @param[in] cond The condition that is expected to be true. + * @param[in] msg The error message when the condition is not met. + */ +#define CKW_ASSERT_MSG(cond, msg) \ + do \ + { \ + if (!(cond)) \ + { \ + CKW_THROW_MSG(msg); \ + } \ + } while (false) + +#else // COMPUTE_KERNEL_WRITER_ASSERTS_ENABLED + +#define CKW_ASSERT_MSG(cond, msg) + +#endif // COMPUTE_KERNEL_WRITER_ASSERTS_ENABLED + +/** If the condition is not met, throw an std::runtime_error if assertion is enabled. + * + * @param[in] cond The condition that is expected to be true. + */ +#define CKW_ASSERT(cond) CKW_ASSERT_MSG(cond, #cond) + +/** If the precondition is met but the condition is not met, throw an std::runtime_error if assertion is enabled. + * + * @param[in] precond The precondition that triggers the check. + * @param[in] cond The condition that is expected to be true if precondition is true. + */ +#define CKW_ASSERT_IF(precond, cond) CKW_ASSERT(!(precond) || (cond)) + +/** Throw an std::runtime_error with the specified message if assertion is enabled. + * + * @param[in] msg The error message when the condition is not met. + */ +#define CKW_ASSERT_FAILED_MSG(msg) CKW_ASSERT_MSG(false, msg) + +} // namespace ckw + +#endif // CKW_INCLUDE_CKW_ERROR_H diff --git a/compute_kernel_writer/include/ckw/Kernel.h b/compute_kernel_writer/include/ckw/Kernel.h new file mode 100644 index 0000000000..f9b7bbb82e --- /dev/null +++ b/compute_kernel_writer/include/ckw/Kernel.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef CKW_INCLUDE_CKW_KERNEL_H +#define CKW_INCLUDE_CKW_KERNEL_H + +#include "ckw/KernelArgument.h" + +#include <string> +#include <vector> + +namespace ckw +{ + +// Forward Declerations +class TileInfo; +class TileOperand; + +enum class TargetLanguage; + +/** The kernel that has been emitted by the kernel writer. + * + * It contains all the necessary information to compile and execute the kernel. + */ +class Kernel +{ +public: + virtual ~Kernel(); + + /** Initialize a new instance of @ref Kernel class with all emitted kernel information. + * + * @param[in] language The target language of the kernel. + * @param[in] arguments The list of kernel arguments. + * @param[in] source_code The source code of the kernel. + */ + Kernel(TargetLanguage language, const std::vector<KernelArgument> &arguments, const std::string &source_code); + + /** Get the target language. */ + TargetLanguage target_language() const; + + /** Get the list of arguments. */ + const std::vector<KernelArgument> &arguments() const; + + /** Get the source code. */ + const std::string &source_code() const; + +private: + TargetLanguage _language; + std::vector<KernelArgument> _arguments; + std::string _source_code; +}; + +} // namespace ckw + +#endif // CKW_INCLUDE_CKW_KERNEL_H diff --git a/compute_kernel_writer/include/ckw/KernelArgument.h b/compute_kernel_writer/include/ckw/KernelArgument.h new file mode 100644 index 0000000000..7e9bcbf1ee --- /dev/null +++ b/compute_kernel_writer/include/ckw/KernelArgument.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef CKW_INCLUDE_CKW_KERNELARGUMENT_H +#define CKW_INCLUDE_CKW_KERNELARGUMENT_H + +#include "ckw/types/TensorComponentType.h" +#include "ckw/types/TensorStorageType.h" + +#include <cstdint> + +namespace ckw +{ + +/** A kernel argument which can be either a tensor storage or a tensor component. */ +class KernelArgument +{ +public: + /** The type of kernel argument. */ + enum class Type : int32_t + { + /** The argument that provides the read and/or write access to the tensor data. + * + * See @ref ckw::TensorStorageType to see the list of supported storage type. + */ + TensorStorage, + + /** The argument that provides extra information about the tensor. + * + * See @ref ckw::TensorComponentType to see the list of supported component. + */ + TensorComponent, + }; + + /** Initialize a new instance of kernel argument class for a tensor storage argument. */ + KernelArgument(int32_t tensor_id, TensorStorageType storage_type); + + /** Initialize a new instance of kernel argument class for a tensor component argument. */ + KernelArgument(int32_t tensor_id, TensorComponentType component_type); + + /** Get the type of kernel argument. */ + Type type() const; + + /** Get the argument ID. + * + * This method can be used to get the tensor info ID of both tensor storage and tensor component arguments. + */ + int32_t id() const; + + /** Get the type of tensor storage. + * + * This method can only be used for tensor storage argument. + */ + TensorStorageType tensor_storage_type() const; + + /** Get the tensor component type. + * + * This method can only be used for tensor component argument. + */ + TensorComponentType tensor_component_type() const; + +private: + Type _type; + int32_t _id; + + union SubId + { + int32_t unknown; + TensorStorageType tensor_storage_type; + TensorComponentType tensor_component_type; + }; + + SubId _sub_id{0}; +}; + +} // namespace ckw + +#endif // CKW_INCLUDE_CKW_KERNELARGUMENT_H diff --git a/compute_kernel_writer/include/ckw/KernelWriter.h b/compute_kernel_writer/include/ckw/KernelWriter.h new file mode 100644 index 0000000000..da41b940d7 --- /dev/null +++ b/compute_kernel_writer/include/ckw/KernelWriter.h @@ -0,0 +1,418 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef CKW_INCLUDE_CKW_KERNELWRITER_H +#define CKW_INCLUDE_CKW_KERNELWRITER_H + +#include "ckw/Kernel.h" +#include "ckw/TensorInfo.h" +#include "ckw/TensorOperand.h" +#include "ckw/TensorSampler.h" +#include "ckw/TileInfo.h" +#include "ckw/TileOperand.h" +#include "ckw/types/ConstantData.h" +#include "ckw/types/ConvertPolicy.h" +#include "ckw/types/DataType.h" +#include "ckw/types/Operators.h" +#include "ckw/types/TargetArchitecture.h" +#include "ckw/types/TargetLanguage.h" +#include "ckw/types/TensorComponentType.h" +#include "ckw/types/TensorDataLayout.h" +#include "ckw/types/TensorSamplerTypes.h" +#include "ckw/types/TensorStorageType.h" + +#include <functional> +#include <memory> +#include <string> +#include <tuple> + +namespace ckw +{ + +/** Forward Declarations */ +class TileArea; + +/** A kernel writer. + * + * This class is used to construct a new kernel by defining arguments, declaring variable and writing code. + * + * Use @ref KernelWriter::create_instance method to create the kernel writer for the specific target architecture and language. + * + * After having finished constructing the kernel, call @ref KernelWriter::emit_kernel to get the kernel object. + */ +class KernelWriter +{ +public: + // ============================================================================================= + // Construtors and destructor + // ============================================================================================= + + /** Initialize a new instance of @ref KernelWriter class for the specific architecture and language. + * + * Supported target architectures and languages: + * + * Architecture | Languages | + * ------------------------------|------------------------------| + * GpuArmMaliValhall | OpenCL | + * + * @param[in] architecture The architecture on which the kernel is executed. + * @param[in] language The language to write the kernel. + */ + static std::unique_ptr<KernelWriter> create_instance(TargetArchitecture architecture, TargetLanguage language); + + /** Destructor */ + virtual ~KernelWriter(); + + // ============================================================================================= + // Data processing + // ============================================================================================= + + /** Write assignment statement: `<dst> = <src>;`. + * + * @param[in] dst The destination tile. + * @param[in] src The source tile. + */ + virtual void op_assign(const TileOperand &dst, const TileOperand &src) = 0; + + /** Write the cast statement: `<dst> = convert_<dst.type><policy>(<src>);`. + * + * @param[in] dst The destination tile. + * @param[in] src The source tile. + * @param[in] policy The policy governing the behavior of the cast. + */ + virtual void op_cast(const TileOperand &dst, const TileOperand &src, ConvertPolicy policy) = 0; + + /** Write the unary expression statement: `<dst> = <op> <src>;`. + * + * @param[in] dst The destination tile. + * @param[in] op The unary operator. + * @param[in] src The source tile. + */ + virtual void op_unary(const TileOperand &dst, UnaryOp op, const TileOperand &src) = 0; + + /** Write the binary expression statement: `<dst> = <op>(<first>, <second>);`. + * + * @param[in] dst The destination tile. + * @param[in] op The binary operator. + * @param[in] first The first source tile. + * @param[in] second The second source tile. + */ + virtual void + op_binary(const TileOperand &dst, BinaryOp op, const TileOperand &first, const TileOperand &second) = 0; + + /** Write ternary expression statement: `<dst> = <op>(<first>, <second>, <third>);`. + * + * @param[in] dst The destination tile. + * @param[in] op The ternary operator. + * @param[in] first The first source tile. + * @param[in] second The second source tile. + * @param[in] third The third source tile. + */ + virtual void op_ternary(const TileOperand &dst, + TernaryOp op, + const TileOperand &first, + const TileOperand &second, + const TileOperand &third) = 0; + + // ============================================================================================= + // Flow control + // ============================================================================================= + + /** Write if block: `if(<lhs> <op> <rhs>) { <body> }`. + * + * @param[in] lhs The LHS tile of the condition. + * @param[in] op The relational binary operator. + * @param[in] rhs The RHS tile of the condition. + * @param[in] body The function that writes the body of the if block. + */ + virtual void + op_if(const TileOperand &lhs, BinaryOp op, const TileOperand &rhs, const std::function<void()> &body) = 0; + + /** Write else-if block: `else if(<lhs> <op> <rhs>) { <body> }`. + * + * @param[in] lhs The LHS tile of the condition. + * @param[in] op The relational binary operator. + * @param[in] rhs The RHS tile of the condition. + * @param[in] body The function that writes the body of the else-if block. + */ + virtual void + op_else_if(const TileOperand &lhs, BinaryOp op, const TileOperand &rhs, const std::function<void()> &body) = 0; + + /** Write an else block: `else { <body> }`. + * + * @param[in] body The function that writes the body of the else block. + */ + virtual void op_else(const std::function<void()> &body) = 0; + + /** Write for-loop block: `for(; <var> <cond_op> <cond_value>; <update_var> <update_op> <update_value>) { body }`. + * + * @param[in] var The scalar tile used in loop condition. + * @param[in] cond_op The relational binary operator used in loop condition. + * @param[in] cond_value The value which the variable is compared against. + * @param[in] update_var The scalar tile which is updated each iteration. + * @param[in] update_op The assignment operator used for updating the update value. + * @param[in] update_value The value which is updated at every iteration. + * @param[in] body The function that writes the body of the for-loop block. + */ + virtual void op_for_loop(const TileOperand &var, + BinaryOp cond_op, + const TileOperand &cond_value, + const TileOperand &update_var, + AssignmentOp update_op, + const TileOperand &update_value, + const std::function<void()> &body) = 0; + + /** Write the return statement. */ + virtual void op_return() = 0; + + // ============================================================================================= + // Misc + // ============================================================================================= + + /** Write the statement to get the global ID of the specified dimension. + * + * @param[in] dst The tile to write the global ID into. + * @param[in] dim The dimension. + */ + virtual void op_get_global_id(const TileOperand &dst, int32_t dim) = 0; + + /** Write the line comment in debug build. + * + * This function does not take effect on release build. + * + * The comment must only contain one line (i.e. no newline character is allowed). + * + * @param[in] text The comment to be written. + */ + virtual void op_comment(const std::string &text) = 0; + + /** Write the statement to print out the value of all the specified tiles. + * + * The printing statement is constructed so that the prefix and each of the operand are printed in separate lines. + * The format for each operand varies depending on whether it is a 2D tile, a vector or a scalar value. + * + * Example output of the printing statement when it is executed: + * + * prefix + * scalar_name = scalar_value + * vector_name = [vector_value_0, vector_value_1, vector_value_2] + * tile_name = [[tile_value_00, tile_value_01], [tile_value_10, tile_value_11]] + * + * @param[in] prefix The first string to be printed out before the list of operands. + * @param[in] operands The list of tiles to be included in the printing statement. + */ + virtual void op_print(const std::string &prefix, const std::vector<TileOperand> &operands) = 0; + + /** Write the given raw code to kernel source code + * It's used to address the cases where the user needs to + * explicitly add a code where it's not (yet) supported by + * the kernel writer utility calls. + * + * @param[in] raw_code raw code to write as string + */ + virtual void op_write_raw_code(const std::string &raw_code) = 0; + + // ============================================================================================= + // Code generation + // ============================================================================================= + + /** Emit the kernel object. + * + * @param[in] name The name of the kernel object to be generated. + */ + virtual std::unique_ptr<Kernel> emit_kernel(const std::string &name) = 0; + + // ============================================================================================= + // Tensor and tile declaration + // ============================================================================================= + + /** Declare a tensor argument. + * + * @param[in] name The name of the tensor. + * @param[in] info The tensor info. + * + * @return The @ref TensorOperand object. + */ + virtual TensorOperand declare_tensor_argument(const std::string &name, const TensorInfo &info) = 0; + + /** Declare a tile given its name and tile info + * + * @param[in] name Name of the tile + * @param[in] tile_info Shape and data type of the tile + * + * @return The created tile operand + */ + virtual TileOperand declare_tile(const std::string &name, const TileInfo &tile_info) = 0; + + /** Declare a constant tile given a @ref:ConstantData object + * + * @param[in] data a @ref ckw::ConstantData object that has the values and the + * underlying data type of the constant tile + * + * @return The created constant tile operand + */ + virtual TileOperand declare_constant_tile(const ConstantData &data) = 0; + + /** Load the data from the tensor memory to the tile using the sampling information. + * + * @param[in] tile_op The tile to be loaded. + * @param[in] tensor_op The tensor to be read. + * @param[in] sampler The tensor sampling information. + * @param[in] x x-coordinate + * @param[in] y y-coordinate + * @param[in] z z-coordinate + * @param[in] batch batch + */ + virtual void op_load(const TileOperand &tile_op, + const TensorOperand &tensor_op, + TensorSampler &sampler, + const TileOperand &x, + const TileOperand &y, + const TileOperand &z, + const TileOperand &batch) = 0; + + /** Load the data from the tensor memory to the tile in a dilated way using the sampling information. + * + * Similar to @ref KernelWriter::op_load() and + * + * @param[in] dilation_x Dilation while reading in x-dimension + * @param[in] dilation_y Dilation while reading in y-dimension + */ + virtual void op_load_dilated(const TileOperand &tile_op, + const TensorOperand &tensor_op, + TensorSampler &sampler, + const TileOperand &x, + const TileOperand &y, + const TileOperand &z, + const TileOperand &batch, + const TileOperand &dilation_x, + const TileOperand &dilation_y) = 0; + + /** Store the data to the tensor memory from the tile using the sampling information. + * + * Similar to @ref KernelWriter::op_load() + */ + virtual void op_store(const TensorOperand &tensor_op, + const TileOperand &tile_op, + TensorSampler &sampler, + const TileOperand &x, + const TileOperand &y, + const TileOperand &z, + const TileOperand &batch) = 0; + + /** Store the data to the tensor memory from the tile in a dilated way using the sampling information. + * + * Similar to @ref KernelWriter::op_load_dilated() + */ + virtual void op_store_dilated(const TensorOperand &tensor_op, + const TileOperand &tile_op, + TensorSampler &sampler, + const TileOperand &x, + const TileOperand &y, + const TileOperand &z, + const TileOperand &batch, + const TileOperand &dilation_x, + const TileOperand &dilation_y) = 0; + + /** Load the data from the tensor memory to the tile using the indirect buffer approach and respecting the sampling information. + * + * @param[in] tile_op The tile to be loaded. + * @param[in] tensor_op The tensor to be read. + * @param[in] sampler The tensor sampling information. + * @param[in] x x-coordinate + * @param[in] y y-coordinate + * @param[in] z z-coordinate + * @param[in] batch batch + */ + virtual void op_load_indirect(const TileOperand &tile_op, + const TensorOperand &tensor_op, + TensorSampler &sampler, + const TileOperand &x, + const TileOperand &y, + const TileOperand &z, + const TileOperand &batch_op) = 0; + + // ============================================================================================= + // ID space management + // ============================================================================================= + + /** Create the new unique ID space and return the value. + * + * This function changes the ID space to a new number which hasn't been used since the creation + * of this kernel writer object. + * + * @return The new ID space value. + */ + int32_t new_id_space(); + + /** Get the current ID space. */ + int32_t id_space() const; + +protected: + /** Set the current ID space. + * + * @param[in] value The ID space to be used. + */ + KernelWriter &id_space(int32_t value); + + /** Write the body code using the specified function. + * + * This function makes sure that a new ID space is created before and then is used solely + * by the specified body writing function. + * The ID space will not be reused after that. + * + * @param[in] body The function that writes the body code. + */ + void write_body(const std::function<void()> &body); + +protected: + /** Generate full variable name by prefixing it with id space */ + std::string generate_full_name(const std::string &name) const; + + /** Create a new tile operand referring to the specified tile object. */ + static TileOperand create_tile_operand(ITile &tile); + + /** Get the reference to the tile object and the active area from the tile operand. */ + static std::tuple<ITile &, TileArea> get_tile(const TileOperand &operand); + + /** Create a new tensor operand from a tensor object. */ + static TensorOperand create_tensor_operand(ITensor &tensor); + + /** Get the reference to tensor object from the tensor operand. */ + static ITensor &get_tensor(const TensorOperand &operand); + + /** Get the values of a constant data object. */ + static const std::vector<std::vector<std::string>> &get_values(const ConstantData &data); + + /** Get the data type of a constant data object. */ + static DataType get_data_type(const ConstantData &data); + +private: + int32_t _id_space{0}; + int32_t _last_created_id_space{0}; +}; + +} // namespace ckw + +#endif // CKW_INCLUDE_CKW_KERNELWRITER_H diff --git a/compute_kernel_writer/include/ckw/TensorInfo.h b/compute_kernel_writer/include/ckw/TensorInfo.h new file mode 100644 index 0000000000..5c87cb5b12 --- /dev/null +++ b/compute_kernel_writer/include/ckw/TensorInfo.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef COMPUTE_KERNEL_WRITER_INCLUDE_CKW_TENSORINFO_H +#define COMPUTE_KERNEL_WRITER_INCLUDE_CKW_TENSORINFO_H + +#include "ckw/types/DataType.h" +#include "ckw/types/TensorDataLayout.h" + +#include <array> +#include <cstdint> + +namespace ckw +{ + +/** Compute Kernel Writer tensor shape + * The value -1 for the tensor dimension is reserved to dynamic dimensions. + */ +using TensorShape = std::array<int32_t, 5>; + +/** Tensor dimension value reserved to dynamic dimensions */ +constexpr int32_t kDynamicTensorDimensionValue = -1; + +/** Compute Kernel Writer tensor info */ +class TensorInfo +{ +public: + /** Default constructor */ + TensorInfo() = default; + /** Constructor + * + * @param[in] dt Tensor data type + * @param[in] shape Tensor shape + * @param[in] dl Tensor data layout + * @param[in] id Tensor id. The id is used to keep track of the user tensor binded. Through the id, + * the user can know what tensor has been used by the Compute Kernel Writer. + * Possible id values: + * - greater than or equal to 0: bind a user specific tensors + * - less than 0: bind a virtual tensor (tile) + */ + TensorInfo(DataType dt, const TensorShape &shape, TensorDataLayout dl, int32_t id); + + /** Set shape */ + TensorInfo &shape(const TensorShape &shape); + + /** Get shape */ + TensorShape shape() const; + + /** Set data type */ + TensorInfo &data_type(DataType dt); + + /** Get data type */ + DataType data_type() const; + + /** Set data layout */ + TensorInfo &data_layout(TensorDataLayout dl); + + /** Get data layout */ + TensorDataLayout data_layout() const; + + /** Set id */ + TensorInfo &id(int32_t id); + + /** Get layout */ + int32_t id() const; + +private: + TensorShape _shape{{0}}; + DataType _dt{DataType::Unknown}; + TensorDataLayout _dl{TensorDataLayout::Unknown}; + int32_t _id{-1}; +}; +} // namespace ckw + +#endif /* COMPUTE_KERNEL_WRITER_INCLUDE_CKW_TENSORINFO_H */ diff --git a/compute_kernel_writer/include/ckw/TensorOperand.h b/compute_kernel_writer/include/ckw/TensorOperand.h new file mode 100644 index 0000000000..a3e53d1314 --- /dev/null +++ b/compute_kernel_writer/include/ckw/TensorOperand.h @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef CKW_INCLUDE_CKW_TENSOROPERAND_H +#define CKW_INCLUDE_CKW_TENSOROPERAND_H + +#include "ckw/TileOperand.h" + +namespace ckw +{ + +class ITensor; +class TensorInfo; + +/** A tensor operand provides access to the tensor info, tensor storages for load/store operations + * and tensor components (e.g. shape, strides, etc.) in the form of @ref TileOperand objects. + */ +class TensorOperand +{ +public: + // _tensor field is completely hidden from the public API to avoid any misuse. + // Only kernel writer class interacts with tensor operand hence we allow it to access this field. + friend class KernelWriter; + + /** Create an empty tensor operand. + * + * The new tensor operand doesn't refer to any tensor therefore it is not useable. + */ + TensorOperand(); + + /** Check if the tensor operand contains a tensor and therefore useable. */ + bool is_valid() const; + + /** Get the tensor info. */ + const TensorInfo &info() const; + + /** Get the operand that contains the stride in dimension 0 of the tensor. */ + TileOperand stride0(); + + /** Get the operand that contains the stride in dimension 1 of the tensor. */ + TileOperand stride1(); + + /** Get the operand that contains the stride in dimension 2 of the tensor. */ + TileOperand stride2(); + + /** Get the operand that contains the stride in dimension 3 of the tensor. */ + TileOperand stride3(); + + /** Get the operand that contains the stride in dimension 4 of the tensor. */ + TileOperand stride4(); + + /** Get the operand that contains the size of dimension 0 of the tensor. */ + TileOperand dim0(); + + /** Get the operand that contains the size of dimension 1 of the tensor. */ + TileOperand dim1(); + + /** Get the operand that contains the size of dimension 2 of the tensor. */ + TileOperand dim2(); + + /** Get the operand that contains the size of dimension 3 of the tensor. */ + TileOperand dim3(); + + /** Get the operand that contains the size of dimension 4 of the tensor. */ + TileOperand dim4(); + + /** Get the operand that contains the size of dimensions 1 and 2 collapsed. */ + TileOperand dim1_dim2(); + + /** Get the operand that contains the size of dimensions 1, 2 and 3 collapsed. */ + TileOperand dim1_dim2_dim3(); + + /** Get the operand that contains the size of dimensions 2 and 3 collapsed. */ + TileOperand dim2_dim3(); + + /** Get the operand that contains the offset in bytes to the first element. */ + TileOperand offset_first_element_in_bytes(); + +private: + /** Initialize a new instance of @ref TensorOperand class for a tensor. */ + TensorOperand(ITensor &tensor); + + ITensor *_tensor; +}; + +} // namespace ckw + +#endif // CKW_INCLUDE_CKW_TENSOROPERAND_H diff --git a/compute_kernel_writer/include/ckw/TensorSampler.h b/compute_kernel_writer/include/ckw/TensorSampler.h new file mode 100644 index 0000000000..117e8de2cf --- /dev/null +++ b/compute_kernel_writer/include/ckw/TensorSampler.h @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef CKW_INCLUDE_CKW_TENSORSAMPLER_H +#define CKW_INCLUDE_CKW_TENSORSAMPLER_H + +#include "ckw/types/TensorSamplerTypes.h" +#include "ckw/types/TensorStorageType.h" + +namespace ckw +{ + +/** Tensor sampler + * + * It contains information about how the tensor is sampled. It can be used to + * tell how a tile should be stored to tensor memory, and how a tensor should be + * sampled to get the values stored in a tile. Where to sample the tensor is + * defined with the coordinates respecting the addressing modes, storage type and + * the tensor format defined in this class. + */ +class TensorSampler +{ +public: + /** Initialize a new instance of @ref TensorSampler class. */ + TensorSampler(); + + /** Initialize a new instance of @ref TensorSampler class. + * + * @param[in] storage Tensor storage to load/store the tensor from/to + * @param[in] format The tensor data format. + * @param[in] address_mode_x The address mode of the x dimension. + * @param[in] address_mode_y The address mode of the y dimension. + * @param[in] address_mode_z The address mode of the z dimension. + */ + TensorSampler(TensorStorageType storage, + TensorSamplerFormat format, + TensorSamplerAddressModeX address_mode_x, + TensorSamplerAddressModeY address_mode_y, + TensorSamplerAddressModeZ address_mode_z); + + /** Get the storage for the tensor */ + TensorStorageType storage() const; + + /** Set the storage for the tensor */ + TensorSampler &storage(TensorStorageType storage); + + /** Get the format of the tensor. */ + TensorSamplerFormat format() const; + + /** Set the format of the tensor. */ + TensorSampler &format(TensorSamplerFormat format); + + /** Get the address mode of the x dimension. */ + TensorSamplerAddressModeX address_mode_x() const; + + /** Set the address mode of the x dimension. */ + TensorSampler &address_mode_x(TensorSamplerAddressModeX address_mode_x); + + /** Get the address mode of the y dimension. */ + TensorSamplerAddressModeY address_mode_y() const; + + /** Set the address mode of the y dimension. */ + TensorSampler &address_mode_y(TensorSamplerAddressModeY address_mode_y); + + /** Get the address mode of the z dimension. */ + TensorSamplerAddressModeZ address_mode_z() const; + + /** Set the address mode of the z dimension. */ + TensorSampler &address_mode_z(TensorSamplerAddressModeZ address_mode_z); + +private: + TensorStorageType _storage{TensorStorageType::BufferUint8Ptr}; + TensorSamplerFormat _format{TensorSamplerFormat::Unknown}; + TensorSamplerAddressModeX _address_mode_x{TensorSamplerAddressModeX::Unknown}; + TensorSamplerAddressModeY _address_mode_y{TensorSamplerAddressModeY::Unknown}; + TensorSamplerAddressModeZ _address_mode_z{TensorSamplerAddressModeZ::Unknown}; +}; + +} // namespace ckw + +#endif // CKW_PROTOTYPE_INCLUDE_CKW_TENSORSAMPLER_H diff --git a/compute_kernel_writer/include/ckw/TileInfo.h b/compute_kernel_writer/include/ckw/TileInfo.h new file mode 100644 index 0000000000..678bb7aaf6 --- /dev/null +++ b/compute_kernel_writer/include/ckw/TileInfo.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef COMPUTE_KERNEL_WRITER_INCLUDE_CKW_TILEINFO +#define COMPUTE_KERNEL_WRITER_INCLUDE_CKW_TILEINFO + +#include "ckw/types/DataType.h" + +#include <array> +#include <cstdint> + +namespace ckw +{ +// Constants to access the tile width and height in the TileShape +constexpr int32_t kTileWidthIdx = 0; +constexpr int32_t kTileHeightIdx = 1; + +/** Compute Kernel Writer tile shape. It is used to define the shape of the tile */ +using TileShape = std::array<int32_t, 2>; + +/** Compute Kernel Writer tile info */ +class TileInfo +{ +public: + /** Constructor used to initialize a scalar variable with a given data type + * + * @param[in] dt Tile data type + */ + TileInfo(DataType dt); + + /** Constructor used to initialize a vector with a given data type and vector length. + * + * @param[in] dt Tile data type + * @param[in] w Tile width (or vector length) + */ + TileInfo(DataType dt, int32_t w); + + /** Constructor used to initialize a tile with a given data type and tile sizes. + * + * @param[in] dt Tile data type + * @param[in] h Tile height + * @param[in] w Tile width + */ + TileInfo(DataType dt, int32_t h, int32_t w); + + /** Set width */ + TileInfo &width(int32_t w); + + /** Get width */ + int32_t width() const; + + /** Set height */ + TileInfo &height(int32_t h); + + /** Get height */ + int32_t height() const; + + /** Set data type */ + TileInfo &data_type(DataType dt); + + /** Get data type */ + DataType data_type() const; + +private: + DataType _dt{DataType::Unknown}; + TileShape _shape{}; +}; + +} // namespace ckw + +#endif /* COMPUTE_KERNEL_WRITER_INCLUDE_CKW_TILEINFO */ diff --git a/compute_kernel_writer/include/ckw/TileOperand.h b/compute_kernel_writer/include/ckw/TileOperand.h new file mode 100644 index 0000000000..556d589bc0 --- /dev/null +++ b/compute_kernel_writer/include/ckw/TileOperand.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef CKW_INCLUDE_CKW_TILEOPERAND_H +#define CKW_INCLUDE_CKW_TILEOPERAND_H + +#include <cstdint> + +namespace ckw +{ + +class KernelWriter; +class TensorOperand; +class ITile; +class TileInfo; + +/** A tile operand refers to a tile object that can be used for kernel writing. */ +class TileOperand +{ +public: + // The constructor and _tile field is completely hidden from the public API to avoid any misuse. + // Only kernel writer and tensor operand classes create and interact with tile operand hence we allow them to access this field. + friend class KernelWriter; + friend class TensorOperand; + + /** Create an empty tile operand. + * + * The new tile operand doesn't refer to any tile therefore it is not useable. + */ + TileOperand(); + + /** Check if the tile operand contains a tile and therefore useable. */ + bool is_valid() const; + + /** Get the tile info. */ + const TileInfo &tile_info() const; + + /** Get a row vector of the current tile operand. + * + * @param[in] row The index of the row to be accessed in the current tile operand. + * + * @return A new tile operand referring to a row of the current tile operand. + */ + TileOperand row(int32_t row) const; + + /** Get a scalar element of the current tile operand. + * + * @param[in] row The index of the row to be accessed in the current tile operand. + * @param[in] col The index of the column to be accessed in the current tile operand. + * + * @return A new tile operand referring to a scalar element of the current tile operand. + */ + TileOperand scalar(int32_t row, int32_t col) const; + +private: + // These are hidden from the public API to avoid any misuse. + + /** Initialize a new instance of @ref TileOperand class for the given tile. */ + TileOperand(ITile &tile); + + /** Initialize a new instance of @ref TileOperand class that is the sub-tile of the given tile. */ + TileOperand(const TileOperand &operand, int32_t row_start, int32_t row_end, int32_t col_start, int32_t col_end); + + /** Get a sub-tile of the current tile operand. + * + * The range of rows and columns is defined by pairs of start and end indices, inclusive lower and exclusive upper. + * In other words, any row and column indices satisfying the following conditions will be part of the sub-tile: + * + * row_start <= row_index < row_end + * col_start <= col_index < col_end + * + * @param[in] row_start The start index of the row range. + * @param[in] row_end The end index of the row range. + * @param[in] col_start The start index of the column range. + * @param[in] col_end The end index of the column range. + * + * @return A new tile operand refering to the same tile but with the new active area. + */ + TileOperand tile(int32_t row_start, int32_t row_end, int32_t col_start, int32_t col_end) const; + + ITile *_tile; + + int32_t _row_start; + int32_t _row_end; + int32_t _col_start; + int32_t _col_end; +}; + +} // namespace ckw + +#endif // CKW_INCLUDE_CKW_TILEOPERAND_H diff --git a/compute_kernel_writer/include/ckw/types/ConstantData.h b/compute_kernel_writer/include/ckw/types/ConstantData.h new file mode 100644 index 0000000000..ea95049c9e --- /dev/null +++ b/compute_kernel_writer/include/ckw/types/ConstantData.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef CKW_INCLUDE_CKW_TYPES_CONSTANTDATA_H +#define CKW_INCLUDE_CKW_TYPES_CONSTANTDATA_H + +#include "ckw/Error.h" +#include "ckw/types/DataType.h" + +#include <algorithm> +#include <cstdint> +#include <initializer_list> +#include <iomanip> +#include <iterator> +#include <sstream> +#include <string> +#include <type_traits> +#include <vector> + +namespace ckw +{ +// Forward Declarations +class KernelWriter; + +class ConstantData +{ + using String = std::string; + using StringVector = std::vector<String>; + +public: + /** Templated constructor */ + template <typename T> + ConstantData(std::initializer_list<std::initializer_list<T>> values, DataType data_type); + + /** Templated constructor */ + template <typename T> + ConstantData(const std::vector<std::vector<T>> &values, DataType data_type); + +private: + /** Validate the given data type and the template type + * + * @param[in] data_type data type + * + * @return true if user provided data type and the template type are conformant + */ + template <typename T> + bool validate(DataType data_type); + + /** Get the constant data as a 2d vector of string values + * + * @return a 2d vector of strings that has the string-converted values + */ + const std::vector<StringVector> &values() const; + + /** Get the underlying data type of the constant values + * + * @return a @ref ckw::DataType object that represents the underlying data type + */ + DataType data_type() const; + + // Friends + friend class KernelWriter; + +private: + // Data members + std::vector<StringVector> _values{}; + DataType _data_type{}; +}; + +} // namespace ckw + +#endif // CKW_INCLUDE_CKW_TYPES_CONSTANTDATA_H diff --git a/compute_kernel_writer/include/ckw/types/ConvertPolicy.h b/compute_kernel_writer/include/ckw/types/ConvertPolicy.h new file mode 100644 index 0000000000..43a37ff118 --- /dev/null +++ b/compute_kernel_writer/include/ckw/types/ConvertPolicy.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef CKW_INCLUDE_CKW_TYPES_CONVERTPOLICY_H +#define CKW_INCLUDE_CKW_TYPES_CONVERTPOLICY_H + +#include <cstdint> + +namespace ckw +{ + +enum class ConvertPolicy : int32_t +{ + None = 0, // No policy specified. + Saturate = 1, // Saturated. +}; + +} // namespace ckw + +#endif // CKW_INCLUDE_CKW_TYPES_CONVERTPOLICY_H diff --git a/compute_kernel_writer/include/ckw/types/DataType.h b/compute_kernel_writer/include/ckw/types/DataType.h new file mode 100644 index 0000000000..3447dd61d6 --- /dev/null +++ b/compute_kernel_writer/include/ckw/types/DataType.h @@ -0,0 +1,50 @@ +/* +* Copyright (c) 2023 Arm Limited. +* +* SPDX-License-Identifier: MIT +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to +* deal in the Software without restriction, including without limitation the +* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +* sell copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#ifndef CKW_INCLUDE_CKW_DATATYPE_H +#define CKW_INCLUDE_CKW_DATATYPE_H + +#include <cstdint> + +namespace ckw +{ + +/** Compute Kernel Writer data types. This data type is used by the code variables and tensor arguments. */ +enum class DataType : int32_t +{ + Unknown = 0x00, + Fp32 = 0x11, + Fp16 = 0x12, + Int32 = 0x21, + Int16 = 0x22, + Int8 = 0x24, + Uint32 = 0x31, + Uint16 = 0x32, + Uint8 = 0x34, + Bool = 0x41 +}; + +} // namespace ckw + +#endif //CKW_INCLUDE_CKW_DATATYPE_H diff --git a/compute_kernel_writer/include/ckw/types/MemoryOperation.h b/compute_kernel_writer/include/ckw/types/MemoryOperation.h new file mode 100644 index 0000000000..f93f60c51a --- /dev/null +++ b/compute_kernel_writer/include/ckw/types/MemoryOperation.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef CKW_INCLUDE_CKW_TYPES_MEMORYOPERATION +#define CKW_INCLUDE_CKW_TYPES_MEMORYOPERATION + +namespace ckw +{ +enum class MemoryOperation +{ + Load = 1, + Store = 2 +}; +} // namespace ckw + +#endif /* CKW_INCLUDE_CKW_TYPES_MEMORYOPERATION */ diff --git a/compute_kernel_writer/include/ckw/types/Operators.h b/compute_kernel_writer/include/ckw/types/Operators.h new file mode 100644 index 0000000000..77b0519422 --- /dev/null +++ b/compute_kernel_writer/include/ckw/types/Operators.h @@ -0,0 +1,101 @@ +/* +* Copyright (c) 2023 Arm Limited. +* +* SPDX-License-Identifier: MIT +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to +* deal in the Software without restriction, including without limitation the +* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +* sell copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#ifndef CKW_INCLUDE_CKW_TYPES_OPERATORS_H +#define CKW_INCLUDE_CKW_TYPES_OPERATORS_H + +#include <cstdint> + +namespace ckw +{ + +/** Unary operators and functions. */ +enum class UnaryOp : int32_t +{ + LogicalNot = 0x0000, // ! + BitwiseNot = 0x0001, // ~ + + Exp = 0x0010, + Tanh = 0x0011, + Sqrt = 0x0012, + Erf = 0x0013, + Fabs = 0x0014, + Log = 0x0015, + Round = 0x0016, + Floor = 0x0017, +}; + +/** Assignment operators. */ +enum class AssignmentOp : int32_t +{ + Increment = 0x0000, // += + Decrement = 0x0001, // -= +}; + +/** Binary operators. */ +enum class BinaryOp : int32_t +{ + // Elementwise + Add = 0x0000, // + + Sub = 0x0001, // - + Mul = 0x0002, // * + Div = 0x0003, // / + Mod = 0x0004, // % + + // Relational + Equal = 0x1000, // == + Less = 0x1001, // < + LessEqual = 0x1002, // <= + Greater = 0x1003, // > + GreaterEqual = 0x1004, // >= + + // Algebra + MatMul_Nt_Nt = 0x2000, // X + MatMul_Nt_T = 0x2001, // X + MatMul_T_Nt = 0x2002, // X + MatMul_T_T = 0x2003, // X + Dot = 0x2004, // . + + // Logical + LogicalAnd = 0x3000, // && + LogicalOr = 0x3001, // || + + // Bitwise + BitwiseXOR = 0x4000, // ^ + + // Functions + Min = 0x8000, + Max = 0x8001, +}; + +/** Ternary operators. */ +enum class TernaryOp : int32_t +{ + Select = 0x0000, + Clamp = 0x0001, +}; + +} // namespace ckw + +#endif // CKW_INCLUDE_CKW_TYPES_OPERATORS_H diff --git a/compute_kernel_writer/include/ckw/types/TargetArchitecture.h b/compute_kernel_writer/include/ckw/types/TargetArchitecture.h new file mode 100644 index 0000000000..25662a01f0 --- /dev/null +++ b/compute_kernel_writer/include/ckw/types/TargetArchitecture.h @@ -0,0 +1,40 @@ +/* +* Copyright (c) 2023 Arm Limited. +* +* SPDX-License-Identifier: MIT +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to +* deal in the Software without restriction, including without limitation the +* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +* sell copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#ifndef CKW_INCLUDE_CKW_TYPES_TARGETARCHITECTURE_H +#define CKW_INCLUDE_CKW_TYPES_TARGETARCHITECTURE_H + +namespace ckw +{ + +/** Target platform architecture. */ +enum class TargetArchitecture +{ + Unknown, + GpuArmMaliValhall, +}; + +} // namespace ckw + +#endif // CKW_INCLUDE_CKW_TYPES_TARGETARCHITECTURE_H diff --git a/compute_kernel_writer/include/ckw/types/TargetLanguage.h b/compute_kernel_writer/include/ckw/types/TargetLanguage.h new file mode 100644 index 0000000000..1f507573dd --- /dev/null +++ b/compute_kernel_writer/include/ckw/types/TargetLanguage.h @@ -0,0 +1,40 @@ +/* +* Copyright (c) 2023 Arm Limited. +* +* SPDX-License-Identifier: MIT +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to +* deal in the Software without restriction, including without limitation the +* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +* sell copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#ifndef CKW_INCLUDE_CKW_TYPES_TARGETLANGUAGE_H +#define CKW_INCLUDE_CKW_TYPES_TARGETLANGUAGE_H + +namespace ckw +{ + +/** Target language. */ +enum class TargetLanguage +{ + Unknown, + OpenCL +}; + +} // namespace ckw + +#endif // CKW_INCLUDE_CKW_TYPES_TARGETLANGUAGE_H diff --git a/compute_kernel_writer/include/ckw/types/TensorComponentType.h b/compute_kernel_writer/include/ckw/types/TensorComponentType.h new file mode 100644 index 0000000000..7a5031d8c0 --- /dev/null +++ b/compute_kernel_writer/include/ckw/types/TensorComponentType.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef CKW_INCLUDE_CKW_TYPES_TENSORCOMPONENTTYPE_H +#define CKW_INCLUDE_CKW_TYPES_TENSORCOMPONENTTYPE_H + +#include <cstdint> + +namespace ckw +{ + +/** Compute Kernel Writer tensor component. + * + * The tensor components are used to access specific backend-agnostic tensor arguments, + * such as the tensor dimensions and tensor strides. + * The tensor component is represented as an unsigned integer. The value of the integer value + * is assigned to retrieve the information through the @ref TensorComponentBitmask. + */ +enum class TensorComponentType : uint32_t +{ + Unknown = 0x00000000, + OffsetFirstElement = 0x01000000, + Stride0 = 0x02000001, + Stride1 = 0x02000002, + Stride2 = 0x02000003, + Stride3 = 0x02000004, + Stride4 = 0x02000005, + Dim0 = 0x04000001, + Dim1 = 0x04000002, + Dim2 = 0x04000003, + Dim3 = 0x04000004, + Dim4 = 0x04000005, + Dim1xDim2 = 0x08000032, + Dim2xDim3 = 0x08000043, + Dim1xDim2xDim3 = 0x08000432 +}; + +} // namespace ckw + +#endif // CKW_INCLUDE_CKW_TYPES_TENSORCOMPONENTTYPE_H diff --git a/compute_kernel_writer/include/ckw/types/TensorDataLayout.h b/compute_kernel_writer/include/ckw/types/TensorDataLayout.h new file mode 100644 index 0000000000..532b299910 --- /dev/null +++ b/compute_kernel_writer/include/ckw/types/TensorDataLayout.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef CKW_INCLUDE_CKW_TYPES_TENSORDATALAYOUT_H +#define CKW_INCLUDE_CKW_TYPES_TENSORDATALAYOUT_H + +namespace ckw +{ + +/** Compute Kernel Writer tensor data layout (or memory format) */ +enum class TensorDataLayout +{ + Unknown, + Nhwc, + Ndhwc +}; + +/** Compute Kernel Writer tensor data layout component */ +enum class TensorDataLayoutComponent +{ + Unknown, + N, + D, + H, + W, + C, +}; + +} // namespace ckw + +#endif // CKW_INCLUDE_CKW_TYPES_TENSORDATALAYOUT_H diff --git a/compute_kernel_writer/include/ckw/types/TensorSamplerTypes.h b/compute_kernel_writer/include/ckw/types/TensorSamplerTypes.h new file mode 100644 index 0000000000..512d0b4501 --- /dev/null +++ b/compute_kernel_writer/include/ckw/types/TensorSamplerTypes.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef CKW_INCLUDE_CKW_TYPES_TENSORSAMPLERTYPES_H +#define CKW_INCLUDE_CKW_TYPES_TENSORSAMPLERTYPES_H + +#include <cstdint> + +namespace ckw +{ + +// This enum class defines how the dimensions of a 3d tensor is mapped into x,y and z coordianates. +enum class TensorSamplerFormat : int32_t +{ + Unknown = 0, + Dim0_Dim1xDim2_1 = 1, // Original dimensions 1 and 2 are collapsed onto y-axis + Dim0_Dim1_Dim2 = 2 // Original dimensions stays as they're defined. No collapsing. +}; + +/** Tensor sampler address mode enum class for X dimension + * + * The following address modes are available in total: + * Unknown + * None : The user guarantees that the coordinate is always in-bound + * OverlappingMin : (FIXED shapes only) Reduce the load/store length when x == 0 (MIN). The load length will be width % original length + * Leftover elements can be handled using overlapping. This involves processing some of the elements in the array twice. + * ClampToBorderMaxOnly : Clamp to max value allowed in the corresponding dimension, and construct an if/else guard to prevent out of bound access, + * e.g. if( y < size-of-dimension-y ){ <do the operation> } + * SkipLessThanZero : Skip loading/storing if the index is less than 0 + * + * Individual dimensions choose which adddress mode to implement in their respective enum classes. + */ +enum class TensorSamplerAddressModeX : int32_t +{ + Unknown = 0, + None = 1, + OverlappingMin = 2 +}; + +/** + * Similar to @ref TensorSamplerAddressModeX + */ +enum class TensorSamplerAddressModeY : int32_t +{ + Unknown = 0, + None = 1, + OverlappingMin = 2, + ClampToBorderMaxOnly = 3, + SkipLessThanZero = 4 +}; + +/** + * Similar to @ref TensorSamplerAddressModeX + */ +enum class TensorSamplerAddressModeZ : int32_t +{ + Unknown = 0, + None = 1, +}; + +} // namespace ckw + +#endif // CKW_INCLUDE_CKW_TYPES_TENSORSAMPLERTYPES_H diff --git a/compute_kernel_writer/include/ckw/types/TensorStorageType.h b/compute_kernel_writer/include/ckw/types/TensorStorageType.h new file mode 100644 index 0000000000..5a2f17d520 --- /dev/null +++ b/compute_kernel_writer/include/ckw/types/TensorStorageType.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef CKW_INCLUDE_CKW_TYPES_TENSORSTORAGETYPE_H +#define CKW_INCLUDE_CKW_TYPES_TENSORSTORAGETYPE_H + +#include <cstdint> + +namespace ckw +{ + +/** Compute Kernel Writer tensor storage. + * The tensor storage represents the type of tensor memory object. + */ +enum class TensorStorageType : uint32_t +{ + Unknown = 0x00000000, + BufferUint8Ptr = 0x01000000, + Texture2dReadOnly = 0x02000001, + Texture2dWriteOnly = 0x02000010, +}; + +} // namespace ckw + +#endif // CKW_INCLUDE_CKW_TYPES_TENSORSTORAGETYPE_H diff --git a/compute_kernel_writer/src/Error.cpp b/compute_kernel_writer/src/Error.cpp new file mode 100644 index 0000000000..e1e4bffcec --- /dev/null +++ b/compute_kernel_writer/src/Error.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "ckw/Error.h" + +#include <string> + +namespace ckw +{ +std::string +create_error_msg(const std::string &file, const std::string &func, const std::string &line, const std::string &msg) +{ + std::string err; + err += "[COMPUTE_KERNEL_WRITER][ERROR]:"; + err += " " + file + ":" + line; + err += " " + func; + err += " " + msg; + return err; +} +} // namespace ckw diff --git a/compute_kernel_writer/src/Helpers.cpp b/compute_kernel_writer/src/Helpers.cpp new file mode 100644 index 0000000000..82d4c4e917 --- /dev/null +++ b/compute_kernel_writer/src/Helpers.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "src/Helpers.h" + +#include "ckw/Error.h" + +namespace ckw +{ +std::string dec_to_hex_as_string(int32_t dec) +{ + switch (dec) + { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + return std::to_string(dec); + case 10: + return "A"; + case 11: + return "B"; + case 12: + return "C"; + case 13: + return "D"; + case 14: + return "E"; + case 15: + return "F"; + default: + COMPUTE_KERNEL_WRITER_ERROR_ON_MSG("Unsupported decimal number"); + return ""; + } +} +} // namespace ckw diff --git a/compute_kernel_writer/src/Helpers.h b/compute_kernel_writer/src/Helpers.h new file mode 100644 index 0000000000..16c06d60e7 --- /dev/null +++ b/compute_kernel_writer/src/Helpers.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef COMPUTE_KERNEL_WRITER_SRC_HELPERS_H +#define COMPUTE_KERNEL_WRITER_SRC_HELPERS_H + +#include <cstdint> +#include <string> + +/** Generic helper functions */ +namespace ckw +{ +/** Helper function to convert a decimal number passed as int32_t variable to hexadecimal number as string + * + * @param[in] dec Decimal number. It must be >= 0 and < 16 + * + * @return the OpenCL datatype as a string + */ +std::string dec_to_hex_as_string(int32_t dec); + +/** Helper function to clamp a value between min_val and max_val + * + * @param[in] val Value to clamp + * @param[in] min_val Lower value + * @param[in] max_val Upper value + * + * @return the clamped value + */ +template <typename T> +T clamp(const T &val, const T &min_val, const T &max_val) +{ + return std::max(min_val, std::min(val, max_val)); +} +} // namespace ckw +#endif /* COMPUTE_KERNEL_WRITER_SRC_HELPERS_H */ diff --git a/compute_kernel_writer/src/ITensor.h b/compute_kernel_writer/src/ITensor.h new file mode 100644 index 0000000000..4c1c56fd35 --- /dev/null +++ b/compute_kernel_writer/src/ITensor.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef CKW_SRC_ITENSOR_H +#define CKW_SRC_ITENSOR_H + +#include "src/ITensorArgument.h" + +namespace ckw +{ + +/** The generic class for all tensor objects in CKW. + * + * Tensors in CKW are always kernel arguments consisting of: + * - Essential information such as name, tensor info, etc. + * - Tensor storage access: allowing load/store operation to perform. + * - Tensor component access: allowing interaction with tensor information such as shape, strides, etc. in the form of tile objects. + */ +class ITensor : public ITensorArgument, public ITensorStorageAccess, public ITensorComponentAccess +{ +}; + +} // namespace ckw + +#endif // CKW_SRC_ITENSOR_H
\ No newline at end of file diff --git a/compute_kernel_writer/src/ITensorArgument.h b/compute_kernel_writer/src/ITensorArgument.h new file mode 100644 index 0000000000..ece45a4dc4 --- /dev/null +++ b/compute_kernel_writer/src/ITensorArgument.h @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef CKW_SRC_ITENSORARGUMENT_H +#define CKW_SRC_ITENSORARGUMENT_H + +#include "ckw/TensorInfo.h" +#include "ckw/types/TensorComponentType.h" +#include "ckw/types/TensorStorageType.h" + +#include "src/ITile.h" + +#include <string> +#include <vector> + +namespace ckw +{ + +class ITensorComponent; + +/** Tensor storage variable */ +struct TensorStorageVariable +{ + std::string val{""}; /** Tensor storage as a string */ + TensorStorageType type{TensorStorageType::Unknown}; /** Tensor storage type */ +}; + +/** Tensor argument base class. + * A tensor is a multidimensional array used to store data. To access an element (or multiple elements) from a tensor, + * the following information are required: + * -# The data memory object. For example, the pointer to the array + * -# The tensor components, such as the size of each tensor dimension, or the number of elements in bytes contained in each dimension (also known as the "stride") + */ +class ITensorArgument +{ +public: + virtual ~ITensorArgument() = default; + /** Method to get the name of the tensor argument. + * + * @return the name of the tensor argument + */ + std::string name() const + { + return _basename; + } + + /** Method to get the tensor info + * + * @return the @ref TensorInfo + */ + TensorInfo &info() + { + return _info; + } + + /** Method to get the tensor info + * + * @return the @ref TensorInfo + */ + const TensorInfo &info() const + { + return _info; + } + +protected: + TensorInfo _info{}; // Tensor info + std::string _basename{""}; // Tensor name +}; + +/** Tensor component argument base class */ +class ITensorComponentAccess +{ +public: + virtual ~ITensorComponentAccess() = default; + /** Method to get the tensor component variable as a tile. + * + * @param[in] x The tensor component to query + * + * @return the tensor component variable as a @ref ITile. + */ + virtual ITile &component(TensorComponentType x) = 0; + /** Method to get all tensor components needed to access the data in the tensor + * + * The tensor components returned by this method must be all passed as kernel argument + * + * @return a vector containing all the tensor components as pointers to @ref ITensorComponent objects. + */ + virtual std::vector<const ITensorComponent *> components() const = 0; +}; + +/** Tensor storage argument base class */ +class ITensorStorageAccess +{ +public: + virtual ~ITensorStorageAccess() = default; + /** Method to get the tensor storage as a string + * + * @param[in] x The tensor storage to query + * + * @return the tensor storage as a @ref TensorStorageVariable + */ + virtual TensorStorageVariable &storage(TensorStorageType x) = 0; + /** Method to get all tensor storages needed to access the data in the tensor + * + * The tensor storages returned by this method must be all passed as kernel argument + * + * @return a vector containing all the tensor storages as @ref TensorStorageVariable objects + */ + virtual std::vector<TensorStorageVariable> storages() const = 0; +}; + +} // namespace ckw + +#endif // CKW_SRC_ITENSORARGUMENT_H diff --git a/compute_kernel_writer/src/ITensorComponent.h b/compute_kernel_writer/src/ITensorComponent.h new file mode 100644 index 0000000000..f9c9d8fd81 --- /dev/null +++ b/compute_kernel_writer/src/ITensorComponent.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef CKW_SRC_ITENSORCOMPONENT_H +#define CKW_SRC_ITENSORCOMPONENT_H + +#include "ckw/types/TensorComponentType.h" + +#include "src/ITile.h" + +namespace ckw +{ + +/** A tensor component provides access to tensor information such as shape, strides, etc. in the form of @ref ITile objects. */ +class ITensorComponent +{ +public: + /** Destructor. */ + virtual ~ITensorComponent() = default; + + /** Get the tile variable for the component. */ + virtual ITile &tile() = 0; + + /** Get the const tile variable for the component. */ + virtual const ITile &tile() const = 0; + + /** Get the component type. */ + virtual TensorComponentType component_type() const = 0; +}; + +} // namespace ckw + +#endif // CKW_SRC_ITENSORCOMPONENT_H diff --git a/compute_kernel_writer/src/ITile.cpp b/compute_kernel_writer/src/ITile.cpp new file mode 100644 index 0000000000..eeb7816068 --- /dev/null +++ b/compute_kernel_writer/src/ITile.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "src/ITile.h" + +namespace ckw +{ + +bool ITile::is_scalar() const +{ + return info().width() == 1 && info().height() == 1; +} + +} // namespace ckw diff --git a/compute_kernel_writer/src/ITile.h b/compute_kernel_writer/src/ITile.h new file mode 100644 index 0000000000..8eaac5ac12 --- /dev/null +++ b/compute_kernel_writer/src/ITile.h @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef CKW_SRC_ITILE_H +#define CKW_SRC_ITILE_H + +#include "ckw/TileInfo.h" + +#include <string> +#include <vector> + +namespace ckw +{ +/** Compute Kernel Writer tile container. It contains the variables stored in the tile as a string */ +using TileContainer = std::vector<std::vector<std::string>>; + +/** Tile descriptor which reports the underlying datatype and vector length */ +struct TileVariableDescriptor +{ + DataType dt{DataType::Unknown}; /** Data type */ + int32_t len{1}; /** Number of elements in a single variable. For example, 1 for scalar */ +}; + +/** Tile variable */ +struct TileVariable +{ + std::string str{""}; /** Tile variable as a string */ + TileVariableDescriptor desc{}; /** Tile value descriptor which reports the datatype and vector length */ +}; + +/** Interface to provide support for scalar access for a Tile. + */ +class IScalarAccess +{ +public: + virtual ~IScalarAccess() = default; + + /** Method to get the scalar variable from a tile as a string + * @param[in] row Tile row. If out-of-bound, the row is clamped to the nearest valid edge + * @param[in] col Tile column. If out-of-bound, the column is clamped to the nearest valid edge + * + * @return the @ref TileVariable + */ + virtual TileVariable scalar(int32_t row, int32_t col) const = 0; +}; + +/** Interface to provide support for vector access for a tile. + */ +class IVectorAccess +{ +public: + virtual ~IVectorAccess() = default; + + /** Method to get the vector variable from a tile. + * The user can query the list of supported vector lengths through the supported_vector_lengths() method. + * + * @param[in] row Tile row. If out-of-bound, the row is clamped to the nearest valid edge + * + * @return the vector variable as a @ref TileVariable + */ + virtual TileVariable vector(int32_t row) const = 0; + + /** Method to get a sub-vector variable. The length of the sub-vector must be supported by the derived IVectorAccess class + * + * @param[in] row Tile row. If out-of-bound, the row is clamped to the nearest valid edge + * @param[in] col_start Tile starting column to get the sub-vector. If out-of-bound, the derived IVectorAccess class may throw an assert. + * @param[in] width The width of the sub-vector. The width must be supported by the derived IVectorAccess class and the last element must be in-bound. + * + * @return the vector variable as a @ref TileVariable + */ + virtual TileVariable vector(int32_t row, int32_t col_start, int32_t width) const = 0; + + /** Method to get the supported vector length. + * + * @return a vector containing the supported vector lengths + */ + virtual std::vector<int32_t> supported_vector_lengths() const = 0; +}; + +/** Tile base class. + * A Tile is a collection of variables (either program variables or constants) used to express a 2D data. + */ +class ITile : public IScalarAccess +{ +public: + virtual ~ITile() = default; + + /** Method to get all TileVariable objects + * + * @return a vector containing all @ref TileVariable objects + */ + virtual std::vector<TileVariable> all() const = 0; + + /** Method to get the name of the tile. + * + * @return the name of the tile + */ + virtual const std::string &name() const = 0; + + /** Method to get the tile info + * + * @return the @ref TileInfo + */ + virtual const TileInfo &info() const = 0; + + /** Method to know whether the tile is assignable or not. + * For example, a constant tile is not assignable. + * + * @return true if the tile is assignable + */ + virtual bool is_assignable() const = 0; + + /** Get whether the tile is scalar, i.e. the width and height are both 1. + * + * @return true if the tile is scalar. + */ + bool is_scalar() const; +}; +} // namespace ckw + +#endif // CKW_SRC_ITILE_H diff --git a/compute_kernel_writer/src/Kernel.cpp b/compute_kernel_writer/src/Kernel.cpp new file mode 100644 index 0000000000..12389b3816 --- /dev/null +++ b/compute_kernel_writer/src/Kernel.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "ckw/Kernel.h" + +#include "ckw/types/TargetLanguage.h" + +namespace ckw +{ + +Kernel::~Kernel() = default; + +Kernel::Kernel(TargetLanguage language, const std::vector<KernelArgument> &arguments, const std::string &source_code) + : _language(language), _arguments(arguments), _source_code(source_code) +{ +} + +TargetLanguage Kernel::target_language() const +{ + return _language; +} + +const std::vector<KernelArgument> &Kernel::arguments() const +{ + return _arguments; +} + +const std::string &Kernel::source_code() const +{ + return _source_code; +} + +} // namespace ckw diff --git a/compute_kernel_writer/src/KernelArgument.cpp b/compute_kernel_writer/src/KernelArgument.cpp new file mode 100644 index 0000000000..a640d36507 --- /dev/null +++ b/compute_kernel_writer/src/KernelArgument.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "ckw/KernelArgument.h" + +#include "ckw/Error.h" + +namespace ckw +{ + +KernelArgument::KernelArgument(int32_t tensor_id, TensorStorageType storage_type) + : _type(Type::TensorStorage), _id(tensor_id) +{ + _sub_id.tensor_storage_type = storage_type; +} + +KernelArgument::KernelArgument(int32_t tensor_id, TensorComponentType component_type) + : _type(Type::TensorComponent), _id(tensor_id) +{ + _sub_id.tensor_component_type = component_type; +} + +KernelArgument::Type KernelArgument::type() const +{ + return _type; +} + +int32_t KernelArgument::id() const +{ + return _id; +} + +TensorStorageType KernelArgument::tensor_storage_type() const +{ + CKW_ASSERT(_type == Type::TensorStorage); + + return _sub_id.tensor_storage_type; +} + +TensorComponentType KernelArgument::tensor_component_type() const +{ + CKW_ASSERT(_type == Type::TensorComponent); + + return _sub_id.tensor_component_type; +} + +} // namespace ckw diff --git a/compute_kernel_writer/src/KernelWriter.cpp b/compute_kernel_writer/src/KernelWriter.cpp new file mode 100644 index 0000000000..92a36746ce --- /dev/null +++ b/compute_kernel_writer/src/KernelWriter.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "ckw/KernelWriter.h" + +#include "ckw/Error.h" +#include "ckw/TileOperand.h" +#include "ckw/types/TargetArchitecture.h" +#include "ckw/types/TargetLanguage.h" + +#include "src/cl/CLKernelWriter.h" +#include "src/cl/CLTensorArgument.h" +#include "src/cl/CLTile.h" +#include "src/TileView.h" + +#include <tuple> + +namespace ckw +{ + +KernelWriter::~KernelWriter() = default; + +std::unique_ptr<KernelWriter> KernelWriter::create_instance(TargetArchitecture architecture, TargetLanguage language) +{ + CKW_UNUSED(architecture); + switch (language) + { + case TargetLanguage::OpenCL: + // Currently this is the oldest and the only supported GPU architecture. + CKW_ASSERT(architecture == TargetArchitecture::GpuArmMaliValhall); + return std::make_unique<CLKernelWriter>(); + + default: + CKW_THROW_MSG("Language not supported!"); + } +} + +int32_t KernelWriter::new_id_space() +{ + _id_space = ++_last_created_id_space; + + return _id_space; +} + +int32_t KernelWriter::id_space() const +{ + return _id_space; +} + +KernelWriter &KernelWriter::id_space(int32_t value) +{ + CKW_ASSERT(value <= _last_created_id_space); + + _id_space = value; + + return *this; +} + +void KernelWriter::write_body(const std::function<void()> &body) +{ + const auto curr_id_space = id_space(); + new_id_space(); + body(); + id_space(curr_id_space); +} + +std::string KernelWriter::generate_full_name(const std::string &name) const +{ + return "G" + std::to_string(id_space()) + "__" + name; +} + +TileOperand KernelWriter::create_tile_operand(ITile &tile) +{ + return TileOperand(tile); +} + +std::tuple<ITile &, TileArea> KernelWriter::get_tile(const TileOperand &operand) +{ + return {*operand._tile, {operand._row_start, operand._row_end, operand._col_start, operand._col_end}}; +} + +TensorOperand KernelWriter::create_tensor_operand(ITensor &tensor) +{ + return TensorOperand(tensor); +} + +ITensor &KernelWriter::get_tensor(const TensorOperand &operand) +{ + CKW_ASSERT(operand._tensor != nullptr); + return *operand._tensor; +} + +const std::vector<std::vector<std::string>> &KernelWriter::get_values(const ConstantData &data) +{ + return data.values(); +} + +DataType KernelWriter::get_data_type(const ConstantData &data) +{ + return data.data_type(); +} + +} // namespace ckw diff --git a/compute_kernel_writer/src/Tensor3dMapper.cpp b/compute_kernel_writer/src/Tensor3dMapper.cpp new file mode 100644 index 0000000000..acef6412a4 --- /dev/null +++ b/compute_kernel_writer/src/Tensor3dMapper.cpp @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "Tensor3dMapper.h" + +#include "ckw/Error.h" +#include "ckw/types/TensorSamplerTypes.h" + +#include "src/ITensor.h" +#include "src/ITile.h" + +namespace ckw +{ +Tensor3dMapper::Tensor3dMapper(ITensor *tensor, TensorSamplerFormat format) : _tensor(tensor), _format(format) +{ +} + +TileVariable Tensor3dMapper::dim_x() const +{ + switch (_format) + { + case TensorSamplerFormat::Dim0_Dim1xDim2_1: + case TensorSamplerFormat::Dim0_Dim1_Dim2: + return _tensor->component(TensorComponentType::Dim0).scalar(0, 0); + default: + CKW_THROW_MSG("Unsupported tensor format"); + return _tensor->component(TensorComponentType::Unknown).scalar(0, 0); + } +} + +TileVariable Tensor3dMapper::dim_y() const +{ + switch (_format) + { + case TensorSamplerFormat::Dim0_Dim1xDim2_1: + return _tensor->component(TensorComponentType::Dim1xDim2).scalar(0, 0); + case TensorSamplerFormat::Dim0_Dim1_Dim2: + return _tensor->component(TensorComponentType::Dim1).scalar(0, 0); + default: + CKW_THROW_MSG("Unsupported tensor format"); + return _tensor->component(TensorComponentType::Unknown).scalar(0, 0); + } +} + +TileVariable Tensor3dMapper::dim_z() const +{ + TileVariable dim_one; + + switch (_format) + { + case TensorSamplerFormat::Dim0_Dim1xDim2_1: + dim_one = _tensor->component(TensorComponentType::Dim3).scalar(0, 0); + dim_one.str = "1"; + return dim_one; + case TensorSamplerFormat::Dim0_Dim1_Dim2: + return _tensor->component(TensorComponentType::Dim2).scalar(0, 0); + default: + CKW_THROW_MSG("Unsupported tensor format"); + return _tensor->component(TensorComponentType::Unknown).scalar(0, 0); + } +} + +TileVariable Tensor3dMapper::dim_batch() const +{ + TileVariable dim_one; + + switch (_format) + { + case TensorSamplerFormat::Dim0_Dim1xDim2_1: + case TensorSamplerFormat::Dim0_Dim1_Dim2: + return _tensor->component(TensorComponentType::Dim3).scalar(0, 0); + default: + CKW_THROW_MSG("Unsupported tensor format"); + return _tensor->component(TensorComponentType::Unknown).scalar(0, 0); + } +} + +TileVariable Tensor3dMapper::stride_x() const +{ + switch (_format) + { + case TensorSamplerFormat::Dim0_Dim1xDim2_1: + case TensorSamplerFormat::Dim0_Dim1_Dim2: + return _tensor->component(TensorComponentType::Stride0).scalar(0, 0); + default: + CKW_THROW_MSG("Unsupported tensor format"); + return _tensor->component(TensorComponentType::Unknown).scalar(0, 0); + } +} + +TileVariable Tensor3dMapper::stride_y() const +{ + switch (_format) + { + case TensorSamplerFormat::Dim0_Dim1xDim2_1: + case TensorSamplerFormat::Dim0_Dim1_Dim2: + return _tensor->component(TensorComponentType::Stride1).scalar(0, 0); + default: + CKW_THROW_MSG("Unsupported tensor format"); + return _tensor->component(TensorComponentType::Unknown).scalar(0, 0); + } +} + +TileVariable Tensor3dMapper::stride_z() const +{ + TileVariable stride_zero; + + switch (_format) + { + case TensorSamplerFormat::Dim0_Dim1xDim2_1: + stride_zero = _tensor->component(TensorComponentType::Stride3).scalar(0, 0); + stride_zero.str = "0"; + return stride_zero; + case TensorSamplerFormat::Dim0_Dim1_Dim2: + return _tensor->component(TensorComponentType::Stride2).scalar(0, 0); + default: + CKW_THROW_MSG("Unsupported tensor format"); + return _tensor->component(TensorComponentType::Unknown).scalar(0, 0); + } +} + +TileVariable Tensor3dMapper::stride_batch() const +{ + switch (_format) + { + case TensorSamplerFormat::Dim0_Dim1xDim2_1: + case TensorSamplerFormat::Dim0_Dim1_Dim2: + return _tensor->component(TensorComponentType::Stride3).scalar(0, 0); + default: + CKW_THROW_MSG("Unsupported tensor format"); + return _tensor->component(TensorComponentType::Unknown).scalar(0, 0); + } +} +} // namespace ckw diff --git a/compute_kernel_writer/src/Tensor3dMapper.h b/compute_kernel_writer/src/Tensor3dMapper.h new file mode 100644 index 0000000000..e94b595193 --- /dev/null +++ b/compute_kernel_writer/src/Tensor3dMapper.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef CKW_SRC_TENSOR3DMAPPER_H +#define CKW_SRC_TENSOR3DMAPPER_H + +#include <string> + +namespace ckw +{ +// Forward declarations +class ITensor; +enum class TensorSamplerFormat; +struct TileVariable; + +/** This internal-only class is responsible to map an Nd tensor spatial dimensions to a 3d tensor spatial dimensions with the + * help of TensorSamplerFormat. + * Attention: The batch is not considered as a spatial dimension and it is treated as an offset + * + * The aim of the dimensionality reduction is primarily to reduce + * the address calculation to: + * x + y * stride_y + z * stride_z + offset, where offset is determined by the batch (for example, b * stride_batch). + * + */ +class Tensor3dMapper +{ +public: + /** Constructor */ + Tensor3dMapper(ITensor *tensor, TensorSamplerFormat format); + + /** Get dimension x as string */ + TileVariable dim_x() const; + + /** Get dimension y as string */ + TileVariable dim_y() const; + + /** Get dimension z as string */ + TileVariable dim_z() const; + + /** Get batch dimension as string */ + TileVariable dim_batch() const; + + /** Get stride for dimension x as string */ + TileVariable stride_x() const; + + /** Get stride for dimension y as string */ + TileVariable stride_y() const; + + /** Get stride for dimension z as string */ + TileVariable stride_z() const; + + /** Get stride for batch dimension as string */ + TileVariable stride_batch() const; + +private: + ITensor *_tensor; + TensorSamplerFormat _format; +}; +} // namespace ckw + +#endif /* CKW_SRC_TENSOR3DMAPPER_H */ diff --git a/compute_kernel_writer/src/TensorInfo.cpp b/compute_kernel_writer/src/TensorInfo.cpp new file mode 100644 index 0000000000..561c126469 --- /dev/null +++ b/compute_kernel_writer/src/TensorInfo.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "ckw/TensorInfo.h" + +namespace ckw +{ +TensorInfo::TensorInfo(DataType dt, const TensorShape &shape, TensorDataLayout dl, int32_t id) + : _shape(shape), _dt(dt), _dl(dl), _id(id) +{ +} + +TensorInfo &TensorInfo::shape(const TensorShape &shape) +{ + _shape = shape; + return *this; +} + +TensorShape TensorInfo::shape() const +{ + return _shape; +} + +TensorInfo &TensorInfo::data_type(DataType dt) +{ + _dt = dt; + return *this; +} + +DataType TensorInfo::data_type() const +{ + return _dt; +} + +TensorInfo &TensorInfo::data_layout(TensorDataLayout dl) +{ + _dl = dl; + return *this; +} + +TensorDataLayout TensorInfo::data_layout() const +{ + return _dl; +} + +TensorInfo &TensorInfo::id(int32_t id) +{ + _id = id; + return *this; +} + +int32_t TensorInfo::id() const +{ + return _id; +} +} // namespace ckw diff --git a/compute_kernel_writer/src/TensorOperand.cpp b/compute_kernel_writer/src/TensorOperand.cpp new file mode 100644 index 0000000000..94997537d8 --- /dev/null +++ b/compute_kernel_writer/src/TensorOperand.cpp @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "ckw/TensorOperand.h" + +#include "ckw/Error.h" + +#include "src/ITensor.h" + +namespace ckw +{ + +TensorOperand::TensorOperand() : _tensor(nullptr) +{ +} + +TensorOperand::TensorOperand(ITensor &tensor) : _tensor(&tensor) +{ +} + +bool TensorOperand::is_valid() const +{ + return _tensor != nullptr; +} + +const TensorInfo &TensorOperand::info() const +{ + CKW_ASSERT(is_valid() == true); + return _tensor->info(); +} + +TileOperand TensorOperand::stride0() +{ + CKW_ASSERT(is_valid() == true); + return TileOperand(_tensor->component(TensorComponentType::Stride0)); +} + +TileOperand TensorOperand::stride1() +{ + CKW_ASSERT(is_valid() == true); + return TileOperand(_tensor->component(TensorComponentType::Stride1)); +} + +TileOperand TensorOperand::stride2() +{ + CKW_ASSERT(is_valid() == true); + return TileOperand(_tensor->component(TensorComponentType::Stride2)); +} + +TileOperand TensorOperand::stride3() +{ + CKW_ASSERT(is_valid() == true); + return TileOperand(_tensor->component(TensorComponentType::Stride3)); +} + +TileOperand TensorOperand::stride4() +{ + CKW_ASSERT(is_valid() == true); + return TileOperand(_tensor->component(TensorComponentType::Stride4)); +} + +TileOperand TensorOperand::dim0() +{ + return TileOperand(_tensor->component(TensorComponentType::Dim0)); +} + +TileOperand TensorOperand::dim1() +{ + CKW_ASSERT(is_valid() == true); + return TileOperand(_tensor->component(TensorComponentType::Dim1)); +} + +TileOperand TensorOperand::dim2() +{ + CKW_ASSERT(is_valid() == true); + return TileOperand(_tensor->component(TensorComponentType::Dim2)); +} + +TileOperand TensorOperand::dim3() +{ + CKW_ASSERT(is_valid() == true); + return TileOperand(_tensor->component(TensorComponentType::Dim3)); +} + +TileOperand TensorOperand::dim4() +{ + CKW_ASSERT(is_valid() == true); + return TileOperand(_tensor->component(TensorComponentType::Dim4)); +} + +TileOperand TensorOperand::dim1_dim2() +{ + CKW_ASSERT(is_valid() == true); + return TileOperand(_tensor->component(TensorComponentType::Dim1xDim2)); +} + +TileOperand TensorOperand::dim1_dim2_dim3() +{ + CKW_ASSERT(is_valid() == true); + return TileOperand(_tensor->component(TensorComponentType::Dim1xDim2xDim3)); +} + +TileOperand TensorOperand::dim2_dim3() +{ + CKW_ASSERT(is_valid() == true); + return TileOperand(_tensor->component(TensorComponentType::Dim2xDim3)); +} + +TileOperand TensorOperand::offset_first_element_in_bytes() +{ + CKW_ASSERT(is_valid() == true); + return TileOperand(_tensor->component(TensorComponentType::OffsetFirstElement)); +} + +} // namespace ckw diff --git a/compute_kernel_writer/src/TensorSampler.cpp b/compute_kernel_writer/src/TensorSampler.cpp new file mode 100644 index 0000000000..e81c5f9d66 --- /dev/null +++ b/compute_kernel_writer/src/TensorSampler.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "ckw/TensorSampler.h" + +namespace ckw +{ + +TensorSampler::TensorSampler() = default; + +TensorSampler::TensorSampler(TensorStorageType storage, + TensorSamplerFormat format, + TensorSamplerAddressModeX address_mode_x, + TensorSamplerAddressModeY address_mode_y, + TensorSamplerAddressModeZ address_mode_z) + : _storage(storage), + _format(format), + _address_mode_x(address_mode_x), + _address_mode_y(address_mode_y), + _address_mode_z(address_mode_z) +{ +} + +TensorStorageType TensorSampler::storage() const +{ + return _storage; +} + +TensorSampler &TensorSampler::storage(TensorStorageType storage) +{ + _storage = storage; + return *this; +} + +/** Get the format of the tensor. */ +TensorSamplerFormat TensorSampler::format() const +{ + return _format; +} + +/** Set the format of the tensor. */ +TensorSampler &TensorSampler::format(TensorSamplerFormat format) +{ + _format = format; + return *this; +} + +/** Get the address mode of the x dimension. */ +TensorSamplerAddressModeX TensorSampler::address_mode_x() const +{ + return _address_mode_x; +} + +/** Set the address mode of the x-dimension. */ +TensorSampler &TensorSampler::address_mode_x(TensorSamplerAddressModeX address_mode_x) +{ + _address_mode_x = address_mode_x; + return *this; +} + +/** Get the address mode of the y dimension. */ +TensorSamplerAddressModeY TensorSampler::address_mode_y() const +{ + return _address_mode_y; +} + +/** Set the address mode of the y dimension. */ +TensorSampler &TensorSampler::address_mode_y(TensorSamplerAddressModeY address_mode_y) +{ + _address_mode_y = address_mode_y; + return *this; +} + +/** Get the address mode of the z dimension. */ +TensorSamplerAddressModeZ TensorSampler::address_mode_z() const +{ + return _address_mode_z; +} + +/** Set the address mode of the z dimension. */ +TensorSampler &TensorSampler::address_mode_z(TensorSamplerAddressModeZ address_mode_z) +{ + _address_mode_z = address_mode_z; + return *this; +} + +} // namespace ckw diff --git a/compute_kernel_writer/src/TensorUtils.cpp b/compute_kernel_writer/src/TensorUtils.cpp new file mode 100644 index 0000000000..17fc9547ae --- /dev/null +++ b/compute_kernel_writer/src/TensorUtils.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "src/TensorUtils.h" + +#include "ckw/Error.h" +#include "ckw/TensorInfo.h" +#include "ckw/types/TensorComponentType.h" + +namespace ckw +{ +TensorComponentType get_tensor_dimension(TensorDataLayout layout, TensorDataLayoutComponent component) +{ + switch (layout) + { + case TensorDataLayout::Nhwc: + switch (component) + { + case TensorDataLayoutComponent::C: + return TensorComponentType::Dim0; + case TensorDataLayoutComponent::W: + return TensorComponentType::Dim1; + case TensorDataLayoutComponent::H: + return TensorComponentType::Dim2; + case TensorDataLayoutComponent::N: + return TensorComponentType::Dim3; + default: + COMPUTE_KERNEL_WRITER_ERROR_ON_MSG("Unsupported tensor component for NHWC"); + return TensorComponentType::Unknown; + } + case TensorDataLayout::Ndhwc: + switch (component) + { + case TensorDataLayoutComponent::C: + return TensorComponentType::Dim0; + case TensorDataLayoutComponent::W: + return TensorComponentType::Dim1; + case TensorDataLayoutComponent::H: + return TensorComponentType::Dim2; + case TensorDataLayoutComponent::D: + return TensorComponentType::Dim3; + case TensorDataLayoutComponent::N: + return TensorComponentType::Dim4; + default: + COMPUTE_KERNEL_WRITER_ERROR_ON_MSG("Unsupported tensor component for NDHWC"); + return TensorComponentType::Unknown; + } + default: + COMPUTE_KERNEL_WRITER_ERROR_ON_MSG("Unsupported tensor data layout"); + return TensorComponentType::Unknown; + } +} + +TensorComponentType get_tensor_stride(TensorDataLayout layout, TensorDataLayoutComponent component) +{ + switch (layout) + { + case TensorDataLayout::Nhwc: + switch (component) + { + case TensorDataLayoutComponent::C: + return TensorComponentType::Stride0; + case TensorDataLayoutComponent::W: + return TensorComponentType::Stride1; + case TensorDataLayoutComponent::H: + return TensorComponentType::Stride2; + case TensorDataLayoutComponent::N: + return TensorComponentType::Stride3; + default: + COMPUTE_KERNEL_WRITER_ERROR_ON_MSG("Unsupported tensor component for NHWC"); + return TensorComponentType::Unknown; + } + case TensorDataLayout::Ndhwc: + switch (component) + { + case TensorDataLayoutComponent::C: + return TensorComponentType::Stride0; + case TensorDataLayoutComponent::W: + return TensorComponentType::Stride1; + case TensorDataLayoutComponent::H: + return TensorComponentType::Stride2; + case TensorDataLayoutComponent::D: + return TensorComponentType::Stride3; + case TensorDataLayoutComponent::N: + return TensorComponentType::Stride4; + default: + COMPUTE_KERNEL_WRITER_ERROR_ON_MSG("Unsupported tensor component for NDHWC"); + return TensorComponentType::Unknown; + } + default: + COMPUTE_KERNEL_WRITER_ERROR_ON_MSG("Unsupported tensor data layout"); + return TensorComponentType::Unknown; + } +} +} // namespace ckw diff --git a/compute_kernel_writer/src/TensorUtils.h b/compute_kernel_writer/src/TensorUtils.h new file mode 100644 index 0000000000..bb0af5c0b9 --- /dev/null +++ b/compute_kernel_writer/src/TensorUtils.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef CKW_SRC_TENSORUTILS_H +#define CKW_SRC_TENSORUTILS_H + +#include <cstdint> + +/** Tensor specific utility functions */ +namespace ckw +{ +// Forward declarations +enum class TensorDataLayout; +enum class TensorDataLayoutComponent; +enum class TensorComponentType : uint32_t; + +/** Get tensor dimension from a given data layout and data layout component + * + * @param[in] layout Layout of the tensor + * @param[in] component Data layout component + * + * @return the @ref TensorComponent + */ +TensorComponentType get_tensor_dimension(TensorDataLayout layout, TensorDataLayoutComponent component); + +/** Get tensor stride from a given data layout and data layout component + * + * @param[in] layout Layout of the tensor + * @param[in] component Data layout component + * + * @return the @ref TensorComponent + */ +TensorComponentType get_tensor_stride(TensorDataLayout layout, TensorDataLayoutComponent component); +} // namespace ckw + +#endif // CKW_SRC_TENSORUTILS_H diff --git a/compute_kernel_writer/src/TileInfo.cpp b/compute_kernel_writer/src/TileInfo.cpp new file mode 100644 index 0000000000..273266eedc --- /dev/null +++ b/compute_kernel_writer/src/TileInfo.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "ckw/TileInfo.h" + +namespace ckw +{ +TileInfo::TileInfo(DataType dt) : _dt(dt), _shape({{1, 1}}) +{ +} + +TileInfo::TileInfo(DataType dt, int32_t w) : _dt(dt), _shape({{w, 1}}) +{ +} + +TileInfo::TileInfo(DataType dt, int32_t h, int32_t w) : _dt(dt), _shape({{w, h}}) +{ +} + +TileInfo &TileInfo::width(int32_t w) +{ + _shape[kTileWidthIdx] = w; + return *this; +} + +int32_t TileInfo::width() const +{ + return _shape[kTileWidthIdx]; +} + +TileInfo &TileInfo::height(int32_t h) +{ + _shape[kTileHeightIdx] = h; + return *this; +} + +int32_t TileInfo::height() const +{ + return _shape[kTileHeightIdx]; +} + +TileInfo &TileInfo::data_type(DataType dt) +{ + _dt = dt; + return *this; +} + +DataType TileInfo::data_type() const +{ + return _dt; +} +} // namespace ckw diff --git a/compute_kernel_writer/src/TileOperand.cpp b/compute_kernel_writer/src/TileOperand.cpp new file mode 100644 index 0000000000..8ced6cfe3f --- /dev/null +++ b/compute_kernel_writer/src/TileOperand.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "ckw/TileOperand.h" + +#include "ckw/Error.h" + +#include "src/ITile.h" + +namespace ckw +{ + +TileOperand::TileOperand() : _tile(nullptr), _row_start(0), _row_end(0), _col_start(0), _col_end(0) +{ +} + +TileOperand::TileOperand(ITile &tile) + : _tile(&tile), _row_start(0), _row_end(tile.info().height()), _col_start(0), _col_end(tile.info().width()) +{ +} + +TileOperand::TileOperand( + const TileOperand &operand, int32_t row_start, int32_t row_end, int32_t col_start, int32_t col_end) + : _tile(operand._tile), _row_start(row_start), _row_end(row_end), _col_start(col_start), _col_end(col_end) +{ + CKW_ASSERT(row_start >= 0 && row_start < _tile->info().height()); + CKW_ASSERT(row_end > row_start && row_end <= _tile->info().height()); + CKW_ASSERT(col_start >= 0 && col_start < _tile->info().width()); + CKW_ASSERT(col_end > col_start && col_end <= _tile->info().width()); +} + +bool TileOperand::is_valid() const +{ + return _tile != nullptr; +} + +const TileInfo &TileOperand::tile_info() const +{ + return _tile->info(); +} + +TileOperand TileOperand::tile(int32_t row_start, int32_t row_end, int32_t col_start, int32_t col_end) const +{ + CKW_ASSERT(row_start >= 0 && _row_start + row_start < _row_end); + CKW_ASSERT(row_end > row_start && _row_start + row_end <= _row_end); + CKW_ASSERT(col_start >= 0 && _col_start + col_start < _col_end); + CKW_ASSERT(col_end > col_start && _col_start + col_end <= _col_end); + + return TileOperand(*this, _row_start + row_start, _row_start + row_end, _col_start + col_start, + _col_start + col_end); +} + +TileOperand TileOperand::row(int32_t row) const +{ + CKW_ASSERT(row >= 0 && _row_start + row < _row_end); + + return tile(_row_start + row, _row_start + row + 1, _col_start, _col_end); +} + +TileOperand TileOperand::scalar(int32_t row, int32_t col) const +{ + CKW_ASSERT(row >= 0 && _row_start + row < _row_end); + CKW_ASSERT(col >= 0 && _col_start + col < _col_end); + + return tile(_row_start + row, _row_start + row + 1, _col_start + col, _col_start + col + 1); +} + +} // namespace ckw diff --git a/compute_kernel_writer/src/TileView.cpp b/compute_kernel_writer/src/TileView.cpp new file mode 100644 index 0000000000..ea803f92f4 --- /dev/null +++ b/compute_kernel_writer/src/TileView.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "src/TileView.h" + +#include <cstdint> + +namespace ckw +{ + +TileArea::TileArea(int32_t row_start, int32_t row_end, int32_t col_start, int32_t col_end) + : _row_start(row_start), _row_end(row_end), _col_start(col_start), _col_end(col_end) +{ +} + +int32_t TileArea::row_start() const +{ + return _row_start; +} + +int32_t TileArea::row_end() const +{ + return _row_end; +} + +int32_t TileArea::col_start() const +{ + return _col_start; +} + +int32_t TileArea::col_end() const +{ + return _col_end; +} + +} // namespace ckw diff --git a/compute_kernel_writer/src/TileView.h b/compute_kernel_writer/src/TileView.h new file mode 100644 index 0000000000..42854ac823 --- /dev/null +++ b/compute_kernel_writer/src/TileView.h @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef CKW_SRC_TILEVIEW_H +#define CKW_SRC_TILEVIEW_H + +#include "ckw/Error.h" +#include "ckw/types/DataType.h" + +#include "src/ITile.h" + +#include <cstdint> + +namespace ckw +{ + +/** A rectangular active area of a tile. */ +class TileArea +{ +public: + /** Create a new tile rectangular active area. + * + * The range of rows and columns is defined by pairs of start and end indices, inclusive lower and exclusive upper. + * In other word, any row and column indices satisfied the following conditions will be part of the active area: + * + * row_start <= row_index < row_end + * col_start <= col_index < col_end + * + * @param[in] row_start The start index of the row range. + * @param[in] row_end The end index of the row range. + * @param[in] col_start The start index of the column range. + * @param[in] col_end The end index of the column range. + */ + TileArea(int32_t row_start, int32_t row_end, int32_t col_start, int32_t col_end); + + /** Get the start row index. */ + int32_t row_start() const; + + /** Get the end row (exclusive) index. */ + int32_t row_end() const; + + /** Get the start column index. */ + int32_t col_start() const; + + /** Get the end column (exclusive) index. */ + int32_t col_end() const; + +private: + int32_t _row_start; + int32_t _row_end; + int32_t _col_start; + int32_t _col_end; +}; + +/** A rectangular view of a tile. */ +template <typename T> +class TileView +{ +public: + /** Default constructor */ + TileView() : _tile(nullptr), _area(0, 0, 0, 0) + { + } + /** Create a tile view that refers to the whole tile. + * + * @param[in] tile The tile object. + */ + TileView(const T &tile) : _tile(&tile), _area(0, tile.info().height(), 0, tile.info().width()) + { + } + + /** Create a new rectangular view of the given tile. + * + * @param[in] tile The tile object. + * @param[in] area The rectangular active area. + */ + TileView(const T &tile, const TileArea &area) : _tile(&tile), _area(area) + { + } + + /** Get the tile object. + * + * The caller must guarantee that the tile view refers to the whole tile. + */ + const T &full_tile() const + { + CKW_ASSERT(is_full_tile()); + + return *_tile; + } + + /** Get the data type of the tile. */ + DataType data_type() const + { + return _tile->info().data_type(); + } + + /** Get the start row index. */ + int32_t row_start() const + { + return _area.row_start(); + } + + /** Get the end row index. */ + int32_t row_end() const + { + return _area.row_end(); + } + + /** Get the start column index. */ + int32_t col_start() const + { + return _area.col_start(); + } + + /** Get the end column index. */ + int32_t col_end() const + { + return _area.col_end(); + } + + /** Get the height of the tile view. */ + int32_t height() const + { + return _area.row_end() - _area.row_start(); + } + + /** Get the width of the tile view. */ + int32_t width() const + { + return _area.col_end() - _area.col_start(); + } + + /** See @ref IVectorAccess::vector. */ + TileVariable vector(int32_t row) const + { + return _tile->vector(row_start() + row, col_start(), width()); + } + + /** See @ref IScalarAccess::scalar. */ + TileVariable scalar(int32_t row, int32_t col) const + { + return _tile->scalar(row_start() + row, col_start() + col); + } + + /** Get the name of the tile. */ + const std::string &name() const + { + return _tile->name(); + } + + /** Get whether the tile view is a scalar element. */ + bool is_scalar() const + { + return height() == 1 && width() == 1; + } + + /** Get whether the tile view refers to the whole tile. */ + bool is_full_tile() const + { + return row_start() == 0 && row_end() == _tile->info().height() && col_start() == 0 && + col_end() == _tile->info().width(); + } + + /** Set the rectangular active area. + * + * @param[in] area The rectangular active area. + */ + TileView &area(const TileArea &area) + { + _area = area; + return *this; + } + + /** Get the tile area */ + TileArea area() const + { + return _area; + } + +private: + const T *_tile; + TileArea _area; +}; + +} // namespace ckw + +#endif // CKW_SRC_TILEVIEW_H diff --git a/compute_kernel_writer/src/cl/CLHelpers.cpp b/compute_kernel_writer/src/cl/CLHelpers.cpp new file mode 100644 index 0000000000..252c5cdfcb --- /dev/null +++ b/compute_kernel_writer/src/cl/CLHelpers.cpp @@ -0,0 +1,353 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "src/cl/CLHelpers.h" + +#include "ckw/Error.h" +#include "ckw/types/DataType.h" +#include "ckw/types/Operators.h" +#include "ckw/types/TensorStorageType.h" + +#include "src/types/DataTypeHelpers.h" + +namespace ckw +{ +bool cl_validate_vector_length(int32_t len) +{ + bool valid_vector_length = true; + if (len < 1 || len > 16 || (len > 4 && len < 8) || (len > 8 && len < 16)) + { + valid_vector_length = false; + } + return valid_vector_length; +} + +std::string cl_get_variable_datatype_as_string(DataType dt, int32_t len) +{ + if (cl_validate_vector_length(len) == false) + { + CKW_THROW_MSG("Unsupported vector length"); + return ""; + } + + std::string res; + switch (dt) + { + case DataType::Fp32: + res += "float"; + break; + case DataType::Fp16: + res += "half"; + break; + case DataType::Int8: + res += "char"; + break; + case DataType::Uint8: + res += "uchar"; + break; + case DataType::Uint16: + res += "ushort"; + break; + case DataType::Int16: + res += "short"; + break; + case DataType::Uint32: + res += "uint"; + break; + case DataType::Int32: + res += "int"; + break; + case DataType::Bool: + res += "bool"; + break; + default: + CKW_THROW_MSG("Unsupported datatype"); + return ""; + } + + if (len > 1) + { + res += std::to_string(len); + } + + return res; +} + +int32_t cl_round_up_to_nearest_valid_vector_width(int32_t width) +{ + switch (width) + { + case 1: + return 1; + case 2: + return 2; + case 3: + return 3; + case 4: + return 4; + case 5: + case 6: + case 7: + case 8: + return 8; + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + case 16: + return 16; + default: + CKW_THROW_MSG("Unsupported width to convert to OpenCL vector"); + return 0; + } +} + +std::string cl_get_variable_storagetype_as_string(TensorStorageType storage) +{ + std::string res; + switch (storage) + { + case TensorStorageType::BufferUint8Ptr: + res += "__global uchar*"; + break; + case TensorStorageType::Texture2dReadOnly: + res += "__read_only image2d_t"; + break; + case TensorStorageType::Texture2dWriteOnly: + res += "__write_only image2d_t"; + break; + default: + CKW_THROW_MSG("Unsupported storage type"); + } + + return res; +} + +std::string cl_get_assignment_op_as_string(AssignmentOp op) +{ + switch (op) + { + case AssignmentOp::Increment: + return "+="; + + case AssignmentOp::Decrement: + return "-="; + + default: + CKW_THROW_MSG("Unsupported assignment operator!"); + } +} + +std::tuple<bool, std::string> cl_get_unary_op(UnaryOp op) +{ + switch (op) + { + case UnaryOp::LogicalNot: + return {false, "!"}; + + case UnaryOp::BitwiseNot: + return {false, "~"}; + + case UnaryOp::Exp: + return {true, "exp"}; + + case UnaryOp::Tanh: + return {true, "tanh"}; + + case UnaryOp::Sqrt: + return {true, "sqrt"}; + + case UnaryOp::Erf: + return {true, "erf"}; + + case UnaryOp::Fabs: + return {true, "fabs"}; + + case UnaryOp::Log: + return {true, "log"}; + + case UnaryOp::Round: + return {true, "round"}; + + case UnaryOp::Floor: + return {true, "floor"}; + + default: + CKW_THROW_MSG("Unsupported unary operation!"); + } +} + +std::tuple<bool, std::string> cl_get_binary_op(BinaryOp op, DataType data_type) +{ + const auto is_float = is_data_type_float(data_type); + + switch (op) + { + case BinaryOp::Add: + return {false, "+"}; + + case BinaryOp::Sub: + return {false, "-"}; + + case BinaryOp::Mul: + return {false, "*"}; + + case BinaryOp::Div: + return {false, "/"}; + + case BinaryOp::Mod: + return {false, "%"}; + + case BinaryOp::Equal: + return {false, "=="}; + + case BinaryOp::Less: + return {false, "<"}; + + case BinaryOp::LessEqual: + return {false, "<="}; + + case BinaryOp::Greater: + return {false, ">"}; + + case BinaryOp::GreaterEqual: + return {false, ">="}; + + case BinaryOp::LogicalAnd: + return {false, "&&"}; + + case BinaryOp::LogicalOr: + return {false, "||"}; + + case BinaryOp::BitwiseXOR: + return {false, "^"}; + + case BinaryOp::Min: + return {true, is_float ? "fmin" : "min"}; + + case BinaryOp::Max: + return {true, is_float ? "fmax" : "max"}; + + default: + CKW_THROW_MSG("Unsupported binary operator/function!"); + } +} + +std::tuple<bool, std::string> cl_get_ternary_op(TernaryOp op) +{ + switch (op) + { + case TernaryOp::Select: + return {true, "select"}; + + case TernaryOp::Clamp: + return {true, "clamp"}; + + default: + CKW_THROW_MSG("Unsupported ternary function!"); + } +} + +std::string cl_data_type_rounded_up_to_valid_vector_width(DataType dt, int32_t width) +{ + std::string data_type; + const int32_t w = cl_round_up_to_nearest_valid_vector_width(width); + data_type += cl_get_variable_datatype_as_string(dt, 1); + if (w != 1) + { + data_type += std::to_string(w); + } + return data_type; +} + +std::vector<int32_t> cl_decompose_vector_width(int32_t vector_width) +{ + std::vector<int32_t> x; + + switch (vector_width) + { + case 0: + break; + case 1: + case 2: + case 3: + case 4: + case 8: + case 16: + x.push_back(vector_width); + break; + case 5: + x.push_back(4); + x.push_back(1); + break; + case 6: + x.push_back(4); + x.push_back(2); + break; + case 7: + x.push_back(4); + x.push_back(3); + break; + case 9: + x.push_back(8); + x.push_back(1); + break; + case 10: + x.push_back(8); + x.push_back(2); + break; + case 11: + x.push_back(8); + x.push_back(3); + break; + case 12: + x.push_back(8); + x.push_back(4); + break; + case 13: + x.push_back(8); + x.push_back(4); + x.push_back(1); + break; + case 14: + x.push_back(8); + x.push_back(4); + x.push_back(2); + break; + case 15: + x.push_back(8); + x.push_back(4); + x.push_back(3); + break; + + default: + CKW_THROW_MSG("Vector width is too large"); + } + return x; +} + +} // namespace ckw diff --git a/compute_kernel_writer/src/cl/CLHelpers.h b/compute_kernel_writer/src/cl/CLHelpers.h new file mode 100644 index 0000000000..370ffc700c --- /dev/null +++ b/compute_kernel_writer/src/cl/CLHelpers.h @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef CKW_SRC_CL_CLHELPERS_H +#define CKW_SRC_CL_CLHELPERS_H + +#include "ckw/types/Operators.h" + +#include <cstdint> +#include <string> +#include <tuple> +#include <vector> + +/** OpenCL specific helper functions */ +namespace ckw +{ +// Forward declarations +enum class DataType; +enum class TensorStorageType : uint32_t; + +/** Helper function to validate the vector length of OpenCL vector data types + * + * @param[in] len Vector length + * + * @return true if the vector lenght is valid. It returns false, otherwise. + */ +bool cl_validate_vector_length(int32_t len); + +/** Helper function to return the OpenCL datatype as a string from a @ref DataType and vector length as int32_t variable + * + * @param[in] dt Datatype + * @param[in] len Vector length + * + * @return the OpenCL datatype as a string + */ +std::string cl_get_variable_datatype_as_string(DataType dt, int32_t len); + +/** Return the assignment operator in OpenCL language. + * + * @param[in] op The assignment operator. + * + * @return The operator in OpenCL language as a string. + */ +std::string cl_get_assignment_op_as_string(AssignmentOp op); + +/** Return the information about the unary operation. + * + * The result contains: + * - is_func: true if it's a function and false if it's an unary operator in OpenCL language. + * - str: the function name or the operator in OpenCL language. + * + * @param[in] op The unary operator. + * + * @return The information about the unary operation. + */ +std::tuple<bool, std::string> cl_get_unary_op(UnaryOp op); + +/** Return the information about the binary operation. + * + * The result contains: + * - is_func: true if it's a function and false if it's an binary operator in OpenCL language. + * - str: the function name or the operator in OpenCL language. + * + * @param[in] op The binary operator. + * @param[in] data_type The input data type. + * + * @return The information about the binary operation. + */ +std::tuple<bool, std::string> cl_get_binary_op(BinaryOp op, DataType data_type); + +/** Return the information about the ternary operation. + * + * The result contains: + * - is_func: true if it's a function and false if it's a ternary operator in OpenCL language. + * - str: the function name or the operator in OpenCL language. + * + * @param[in] op The ternary operator. + * + * @return The information about the ternary operation. + */ +std::tuple<bool, std::string> cl_get_ternary_op(TernaryOp op); + +/** Helper function to return the OpenCL vector size that accommodate the the desired width + * + * @param[in] width The desired width + * + * @return the OpenCL vector size +*/ +int32_t cl_round_up_to_nearest_valid_vector_width(int32_t width); + +/** Helper function to return the OpenCL storage type as a string from a @ref TensorStorage + * + * @param[in] storage Storage type + * + * @return the OpenCL storage type as a string + */ +std::string cl_get_variable_storagetype_as_string(TensorStorageType storage); + +/** Helper function to decompose a vector width into a summation of valid OpenCL vector widths. + * + * @param[in] vector_width Vector width to be decomposed + * + * @return a vector of OpenCL vector widths + */ +std::vector<int32_t> cl_decompose_vector_width(int32_t vector_width); + +/** Helper function to get OpenCL data type from the data type enum and width + * It'll round up the given vector width to the nearest valid OpenCL vector width. + * + * @param[in] dt data type enum + * @param[in] width vector width + * + * @return a string representation of the data type + */ +std::string cl_data_type_rounded_up_to_valid_vector_width(DataType dt, int32_t width); +} // namespace ckw + +#endif /* CKW_SRC_CL_CLHELPERS_H */ diff --git a/compute_kernel_writer/src/cl/CLKernelWriter.cpp b/compute_kernel_writer/src/cl/CLKernelWriter.cpp new file mode 100644 index 0000000000..91512bde23 --- /dev/null +++ b/compute_kernel_writer/src/cl/CLKernelWriter.cpp @@ -0,0 +1,833 @@ +/* + * Copyright (c) 2023-2024 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "src/cl/CLKernelWriter.h" + +#include "ckw/Error.h" +#include "ckw/Kernel.h" +#include "ckw/TensorSampler.h" +#include "ckw/TileOperand.h" +#include "ckw/types/DataType.h" +#include "ckw/types/MemoryOperation.h" +#include "ckw/types/TargetLanguage.h" + +#include "src/cl/CLHelpers.h" +#include "src/cl/CLTensorArgument.h" +#include "src/cl/CLTile.h" +#include "src/cl/helpers/CLMemoryOpBufferHelper.h" +#include "src/cl/helpers/CLMemoryOpImage2dHelper.h" +#include "src/cl/helpers/ICLMemoryOpHelper.h" +#include "src/ITensorComponent.h" +#include "src/TileView.h" +#include "src/types/DataTypeHelpers.h" + +#include <algorithm> +#include <cstdint> +#include <tuple> +#include <vector> + +namespace +{ +std::string generate_cl_extensions() +{ + std::string ext = R"( +#if defined(cl_khr_fp16) +#pragma OPENCL EXTENSION cl_khr_fp16 : enable +#endif // defined(cl_khr_fp16) + +#if defined(cl_arm_printf) +#pragma OPENCL EXTENSION cl_arm_printf : enable +#endif // defined(cl_arm_printf); + +#define inf (INFINITY) +)"; + return ext; +} +} // namespace + +namespace ckw +{ + +CLKernelWriter::CLKernelWriter() = default; +CLKernelWriter::~CLKernelWriter() = default; + +std::unique_ptr<Kernel> CLKernelWriter::emit_kernel(const std::string &name) +{ + std::string code; + code += generate_cl_extensions(); + code += "__kernel void "; + code += name; + code += "\n(\n"; + + // Create the list of arguments. + std::vector<KernelArgument> arguments; + + for (const auto &tensor : _tensors) + { + const auto tensor_id = tensor->info().id(); + + const auto storages = tensor->storages(); + const auto components = tensor->components(); + + for (const auto &storage : storages) + { + code += cl_get_variable_storagetype_as_string(storage.type); + code += " "; + code += storage.val; + code += ",\n"; + + arguments.emplace_back(tensor_id, storage.type); + } + + for (const auto &component : components) + { + const auto &tile = component->tile(); + const auto &tile_info = tile.info(); + + CKW_ASSERT(tile.is_scalar()); + + code += cl_get_variable_datatype_as_string(tile_info.data_type(), 1); + code += " "; + code += tile.name(); + code += ",\n"; + + arguments.emplace_back(tensor_id, component->component_type()); + } + } + + if (code.size() >= 2 && code[code.size() - 2] == ',' && code[code.size() - 1] == '\n') + { + // Remove the last comma in the argument list. + code.pop_back(); + code[code.size() - 1] = '\n'; + } + + code += ")\n{\n"; + + code += _body_source_code; + + code += "}\n"; + + return std::make_unique<Kernel>(TargetLanguage::OpenCL, arguments, code); +} + +void CLKernelWriter::op_assign(const TileOperand &dst, const TileOperand &src) +{ + const auto dst_view = to_cl_tile_view(dst); + const auto src_view = to_cl_tile_view(src); + + const auto dst_w = dst_view.width(); + const auto dst_h = dst_view.height(); + const auto src_w = src_view.width(); + + const auto data_type_str = cl_get_variable_datatype_as_string(dst_view.data_type(), dst_w); + + const auto broadcast_src_x = dst_w != 1 && src_w == 1; + const std::string src_prefix = broadcast_src_x ? "(" + data_type_str + ")" : ""; + + CKW_ASSERT_MSG(src_view.data_type() == dst_view.data_type(), "Source and destination type must match."); + CKW_ASSERT_MSG(src_view.height() == dst_h || src_view.height() == 1, + "Tile height must match or source is broadcasting in y dimension."); + CKW_ASSERT_MSG(src_w == dst_w || src_w == 1, "Tile width must match or source is broadcasting in x dimension."); + + // Broadcasting on y dimension is automatic (see CLTile::vector). + for (int32_t y = 0; y < dst_h; ++y) + { + append_code(dst_view.vector(y).str, " = ", src_prefix, src_view.vector(y).str, ";\n"); + } +} + +void CLKernelWriter::op_cast(const TileOperand &dst, const TileOperand &src, ConvertPolicy policy) +{ + const auto dst_view = to_cl_tile_view(dst); + const auto src_view = to_cl_tile_view(src); + + const auto dst_w = dst_view.width(); + const auto dst_h = dst_view.height(); + const auto src_w = src_view.width(); + + const auto dst_type = dst_view.data_type(); + + const auto convert_type_str = cl_get_variable_datatype_as_string(dst_type, src_w); + const auto dst_type_str = cl_get_variable_datatype_as_string(dst_type, dst_w); + + const std::string sat = policy == ConvertPolicy::Saturate ? "_sat" : ""; + + CKW_ASSERT_IF(policy == ConvertPolicy::Saturate, !is_data_type_float(dst_type)); + + const auto broadcast_x = dst_w != 1 && src_w == 1; + const std::string prefix = broadcast_x ? "(" + dst_type_str + ")" : ""; + + CKW_ASSERT_MSG(src_view.height() == dst_h || src_view.height() == 1, + "Tile height must match or source is broadcasting in y dimension."); + CKW_ASSERT_MSG(src_w == dst_w || src_w == 1, "Tile width must match or source is broadcasting in x dimension."); + + // Broadcasting on y dimension is automatic (see CLTile::vector). + if (src_view.data_type() == dst_view.data_type()) + { + for (int32_t y = 0; y < dst_h; ++y) + { + append_code(dst_view.vector(y).str, " = ", src_view.vector(y).str, ";\n"); + } + } + else + { + for (int32_t y = 0; y < dst_h; ++y) + { + append_code(dst_view.vector(y).str, " = ", prefix, "convert_", convert_type_str, sat, "(", + src_view.vector(y).str, ");\n"); + } + } +} + +void CLKernelWriter::op_unary(const TileOperand &dst, UnaryOp op, const TileOperand &src) +{ + const auto dst_view = to_cl_tile_view(dst); + const auto src_view = to_cl_tile_view(src); + + const auto dst_w = dst_view.width(); + const auto dst_h = dst_view.height(); + const auto src_w = src_view.width(); + + const auto data_type_str = cl_get_variable_datatype_as_string(dst_view.data_type(), dst_w); + const auto broadcast_src_x = dst_w != 1 && src_w == 1; + + const std::string src_prefix = broadcast_src_x ? "(" + data_type_str + ")" : ""; + + const auto op_info = cl_get_unary_op(op); + const auto op_is_func = std::get<0>(op_info); + const auto &op_name = std::get<1>(op_info); + const auto op_prefix = op_is_func ? op_name + "(" : op_name; + const auto op_suffix = op_is_func ? ")" : ""; + + CKW_ASSERT_MSG(src_view.data_type() == dst_view.data_type(), "Source and destination type must match."); + CKW_ASSERT_MSG(src_view.height() == dst_h || src_view.height() == 1, + "Tile height must match or source is broadcasting in y dimension."); + CKW_ASSERT_MSG(src_w == dst_w || src_w == 1, "Tile width must match or source is broadcasting in x dimension."); + + // Broadcasting on y dimension is automatic (see CLTile::vector). + for (int32_t y = 0; y < dst_h; ++y) + { + append_code(dst_view.vector(y).str, " = ", src_prefix, op_prefix, src_view.vector(y).str, op_suffix, ";\n"); + } +} + +void CLKernelWriter::op_binary(const TileOperand &dst, BinaryOp op, const TileOperand &first, const TileOperand &second) +{ + const auto dst_view = to_cl_tile_view(dst); + const auto lhs_view = to_cl_tile_view(first); + const auto rhs_view = to_cl_tile_view(second); + + const auto dst_w = dst_view.width(); + const auto dst_h = dst_view.height(); + const auto lhs_w = lhs_view.width(); + const auto rhs_w = rhs_view.width(); + + const auto data_type = lhs_view.data_type(); + + CKW_ASSERT_MSG(lhs_view.data_type() == rhs_view.data_type(), "LHS and RHS type must match."); + + if (op == BinaryOp::MatMul_Nt_T) + { + CKW_ASSERT_MSG(lhs_view.height() == dst_h, "LHS tile height must match the DST tile height"); + CKW_ASSERT_MSG(rhs_view.height() == dst_w, "RHS tile height must match the DST tile width"); + CKW_ASSERT_MSG(lhs_view.width() == rhs_view.width(), "LHS tile width must match the LHS tile width"); + + CKW_ASSERT(is_data_type_float(data_type)); + + for (int32_t y = 0; y < dst_h; ++y) + { + for (int32_t x = 0; x < dst_w; ++x) + { + for (int32_t k = 0; k < lhs_w; ++k) + { + append_code(dst_view.scalar(y, x).str, " = fma(", lhs_view.scalar(y, k).str, ", ", + rhs_view.scalar(x, k).str, ", ", dst_view.scalar(y, x).str, ");\n"); + } + } + } + } + else + { + CKW_ASSERT_MSG(lhs_view.height() == dst_h || lhs_view.height() == 1, + "LHS tile height must match or source is broadcasting in y dimension."); + CKW_ASSERT_MSG(rhs_view.height() == dst_h || rhs_view.height() == 1, + "RHS tile height must match or source is broadcasting in y dimension."); + + CKW_ASSERT_MSG(lhs_w == dst_w || lhs_w == 1, + "LHS tile width must match destination or LHS is broadcasting in x dimension."); + CKW_ASSERT_MSG(rhs_w == dst_w || rhs_w == 1, + "RHS tile width must match destination or RHS is broadcasting in x dimension."); + + const auto op_info = cl_get_binary_op(op, data_type); + const auto op_is_func = std::get<0>(op_info); + const auto &op_name = std::get<1>(op_info); + + const auto data_type_str = cl_get_variable_datatype_as_string(data_type, dst_w); + + const auto broadcast_lhs_x = dst_w != 1 && lhs_w == 1; + const auto broadcast_rhs_x = dst_w != 1 && rhs_w == 1; + + const std::string lhs_prefix = broadcast_lhs_x ? "(" + data_type_str + ")" : ""; + const std::string rhs_prefix = broadcast_rhs_x ? "(" + data_type_str + ")" : ""; + + const std::string op_prefix = op_is_func ? " = " + op_name + "(" : " = "; + const std::string op_separator = op_is_func ? ", " : " " + op_name + " "; + const std::string op_suffix = op_is_func ? ");\n" : ";\n"; + + // Broadcasting on y dimension is automatic (see CLTile::vector). + for (int32_t y = 0; y < dst_h; ++y) + { + append_code(dst_view.vector(y).str, op_prefix, lhs_prefix, lhs_view.vector(y).str, op_separator, rhs_prefix, + rhs_view.vector(y).str, op_suffix); + } + } +} + +void CLKernelWriter::op_ternary( + const TileOperand &dst, TernaryOp op, const TileOperand &first, const TileOperand &second, const TileOperand &third) +{ + const auto dst_view = to_cl_tile_view(dst); + const auto first_view = to_cl_tile_view(first); + const auto second_view = to_cl_tile_view(second); + const auto third_view = to_cl_tile_view(third); + + const auto dst_w = dst_view.width(); + const auto dst_h = dst_view.height(); + const auto first_w = first_view.width(); + const auto second_w = second_view.width(); + const auto third_w = third_view.width(); + + const auto data_type = dst_view.data_type(); + const auto data_type_str = cl_get_variable_datatype_as_string(data_type, dst_w); + + const auto op_info = cl_get_ternary_op(op); + const auto op_is_func = std::get<0>(op_info); + const auto &op_name = std::get<1>(op_info); + + const auto broadcast_first_x = dst_w != 1 && first_w == 1; + const auto broadcast_second_x = dst_w != 1 && second_w == 1; + const auto broadcast_third_x = dst_w != 1 && third_w == 1; + + const std::string first_prefix = broadcast_first_x ? "(" + data_type_str + ")" : ""; + const std::string second_prefix = broadcast_second_x ? "(" + data_type_str + ")" : ""; + const std::string third_prefix = broadcast_third_x ? "(" + data_type_str + ")" : ""; + + CKW_UNUSED(op_is_func); + CKW_ASSERT_MSG(op_is_func, "The only supported ternary operator is function."); + CKW_ASSERT_MSG(second_view.data_type() == dst_view.data_type(), "2nd source and destination type must match."); + CKW_ASSERT_MSG(third_view.data_type() == dst_view.data_type(), "3rd source and destination type must match."); + + CKW_ASSERT_MSG(first_view.height() == dst_h || first_view.height() == 1, + "1st tile height must match or source is broadcasting in y dimension."); + CKW_ASSERT_MSG(second_view.height() == dst_h || second_view.height() == 1, + "2nd tile height must match or source is broadcasting in y dimension."); + CKW_ASSERT_MSG(third_view.height() == dst_h || third_view.height() == 1, + "3rd tile height must match or source is broadcasting in y dimension."); + + CKW_ASSERT_MSG(first_w == dst_w || first_w == 1, + "1st tile width must match or source is broadcasting in x dimension."); + CKW_ASSERT_MSG(second_w == dst_w || second_w == 1, + "2nd tile width must match or source is broadcasting in x dimension."); + CKW_ASSERT_MSG(third_w == dst_w || third_w == 1, + "3rd tile width must match or source is broadcasting in x dimension."); + + // Broadcasting on y dimension is automatic (see CLTile::vector). + for (int32_t y = 0; y < dst_h; ++y) + { + append_code(dst_view.vector(y).str, " = ", op_name, "(", first_prefix, first_view.vector(y).str, ", ", + second_prefix, second_view.vector(y).str, ", ", third_prefix, third_view.vector(y).str, ");\n"); + } +} + +void CLKernelWriter::op_if_generic( + const TileOperand &lhs, BinaryOp op, const TileOperand &rhs, const std::function<void()> &body, bool is_else_if) +{ + const auto lhs_view = to_cl_tile_view(lhs); + const auto rhs_view = to_cl_tile_view(rhs); + + const auto op_name = std::get<1>(cl_get_binary_op(op, lhs_view.data_type())); + CKW_ASSERT(op == BinaryOp::Less || op == BinaryOp::LessEqual || op == BinaryOp::Equal || + op == BinaryOp::GreaterEqual || op == BinaryOp::Greater); + + CKW_ASSERT(lhs_view.is_scalar()); + CKW_ASSERT(rhs_view.is_scalar()); + + if (is_else_if) + { + append_code("else "); + } + + append_code("if (", lhs_view.scalar(0, 0).str, " ", op_name, " ", rhs_view.scalar(0, 0).str, ")\n{\n"); + write_body(body); + append_code("}\n"); +} + +void CLKernelWriter::op_if(const TileOperand &lhs, + BinaryOp op, + const TileOperand &rhs, + const std::function<void()> &body) +{ + op_if_generic(lhs, op, rhs, body, false /* is_else_if */); +} + +void CLKernelWriter::op_else_if(const TileOperand &lhs, + BinaryOp op, + const TileOperand &rhs, + const std::function<void()> &body) +{ + op_if_generic(lhs, op, rhs, body, true /* is_else_if */); +} + +void CLKernelWriter::op_else(const std::function<void()> &body) +{ + append_code("else\n{\n"); + write_body(body); + append_code("}\n"); +} + +void CLKernelWriter::op_for_loop(const TileOperand &var, + BinaryOp cond_op, + const TileOperand &cond_value, + const TileOperand &update_var, + AssignmentOp update_op, + const TileOperand &update_value, + const std::function<void()> &body) +{ + const auto var_view = to_cl_tile_view(var); + const auto cond_value_view = to_cl_tile_view(cond_value); + const auto update_var_view = to_cl_tile_view(update_var); + const auto update_value_view = to_cl_tile_view(update_value); + + CKW_ASSERT(var_view.is_scalar()); + CKW_ASSERT(cond_value_view.is_scalar()); + CKW_ASSERT(update_var_view.is_scalar()); + CKW_ASSERT(update_value_view.is_scalar()); + + CKW_ASSERT(var_view.data_type() == cond_value_view.data_type()); + CKW_ASSERT(update_var_view.data_type() == update_value_view.data_type()); + + const auto cond_op_name = std::get<1>(cl_get_binary_op(cond_op, var_view.data_type())); + CKW_ASSERT(cond_op == BinaryOp::Less || cond_op == BinaryOp::LessEqual || cond_op == BinaryOp::Equal || + cond_op == BinaryOp::GreaterEqual || cond_op == BinaryOp::Greater); + + append_code("for (; ", var_view.scalar(0, 0).str, " ", cond_op_name, " ", cond_value_view.scalar(0, 0).str, "; ", + update_var_view.scalar(0, 0).str, " ", cl_get_assignment_op_as_string(update_op), " ", + update_value_view.scalar(0, 0).str, ")\n{\n"); + write_body(body); + append_code("}\n"); +} + +void CLKernelWriter::op_return() +{ + append_code("return;\n"); +} + +void CLKernelWriter::op_get_global_id(const TileOperand &dst, int32_t dim) +{ + const auto tile_view = to_cl_tile_view(dst); + + CKW_ASSERT(tile_view.is_scalar()); + CKW_ASSERT(tile_view.data_type() == DataType::Int32 || tile_view.data_type() == DataType::Uint32); + + CKW_ASSERT(dim >= 0 && dim <= 2); + + append_code(tile_view.scalar(0, 0).str, " = get_global_id(", std::to_string(dim), ");\n"); +} + +void CLKernelWriter::op_print(const std::string &prefix, const std::vector<TileOperand> &operands) +{ + std::string format_code; + std::string args_code; + + for (auto &op : operands) + { + const auto tile_view = to_cl_tile_view(op); + + const auto name = tile_view.name(); + const auto width = tile_view.width(); + const auto height = tile_view.height(); + const auto data_type = tile_view.data_type(); + + // Construct the format specifier to print out one row of the tile. + std::string row_format("%"); + + if (width > 1) + { + row_format += "v" + std::to_string(width); + } + + switch (data_type) + { + case DataType::Fp32: + row_format += "hlg"; + break; + case DataType::Fp16: + row_format += "hg"; + break; + case DataType::Int32: + case DataType::Bool: + row_format += (width > 1) ? "hli" : "i"; + break; + case DataType::Int16: + row_format += "hi"; + break; + case DataType::Int8: + row_format += "hhi"; + break; + case DataType::Uint32: + row_format += (width > 1) ? "hlu" : "u"; + break; + case DataType::Uint16: + row_format += "hu"; + break; + case DataType::Uint8: + row_format += "hhu"; + break; + default: + CKW_THROW_MSG("Unsupported data type!"); + } + + if (width > 1) + { + row_format = "[" + row_format + "]"; + } + + // Construct the format specifier for the printf statement. + format_code += name + " = "; + + if (height == 1) + { + format_code += row_format; + } + else + { + format_code += "[" + row_format; + for (int32_t row = 1; row < height; ++row) + { + format_code += ", " + row_format; + } + format_code += "]"; + } + + format_code += "\\n"; + + // Construct the variable arguments for the printf statement. + for (int32_t row = 0; row < height; ++row) + { + args_code += ", " + tile_view.vector(row).str; + } + } + + append_code("printf(\"", prefix, "\\n", format_code, "\"", args_code, ");\n"); +} + +void CLKernelWriter::op_comment(const std::string &text) +{ +#ifdef COMPUTE_KERNEL_WRITER_DEBUG_ENABLED + + CKW_ASSERT(text.find("\n") == text.npos); + CKW_ASSERT(text.find("\r") == text.npos); + + append_code("// ", text, "\n"); + +#else // COMPUTE_KERNEL_WRITER_DEBUG_ENABLED + + CKW_UNUSED(text); + +#endif // COMPUTE_KERNEL_WRITER_DEBUG_ENABLED +} + +const std::string &CLKernelWriter::body_source_code() const +{ + return _body_source_code; +} + +TensorOperand CLKernelWriter::declare_tensor_argument(const std::string &name, const TensorInfo &info) +{ + const auto fullname = generate_full_name(name); + + auto tensor = std::make_unique<CLTensorArgument>(fullname, info, false /* return_dims_by_value */); + const auto operand = create_tensor_operand(*tensor); + + _tensors.insert(std::move(tensor)); + + return operand; +} + +TileOperand CLKernelWriter::declare_tile(const std::string &name, const TileInfo &tile_info) +{ + const std::string fullname = generate_full_name(name); + + const int32_t height = tile_info.height(); + const int32_t width = tile_info.width(); + const DataType data_type = tile_info.data_type(); + + CKW_ASSERT_MSG(std::find_if(_tiles.begin(), _tiles.end(), + [=](const std::unique_ptr<CLTile> &e) + { return e->name() == fullname; }) == _tiles.end(), + "There is already a tile with name: " + fullname); + + auto tile = std::make_unique<CLTile>(fullname, tile_info); + + for (int32_t row = 0; row < height; ++row) + { + const std::string cl_type = cl_get_variable_datatype_as_string(data_type, width); + append_code(cl_type, " ", tile->vector(row).str, ";\n"); + } + + const auto operand = create_tile_operand(*tile); + + _tiles.insert(std::move(tile)); + + return operand; +} + +TileOperand CLKernelWriter::declare_constant_tile(const ConstantData &data) +{ + auto tile = std::make_unique<CLTile>(get_values(data), get_data_type(data)); + const TileOperand operand = create_tile_operand(*tile); + _constant_tiles.insert(std::move(tile)); + + return operand; +} + +void CLKernelWriter::op_write_raw_code(const std::string &raw_code) +{ + append_code(raw_code); +} + +TileView<CLTile> CLKernelWriter::to_cl_tile_view(const TileOperand &operand) const +{ + const auto tile_and_area = get_tile(operand); + ITile &tile = std::get<0>(tile_and_area); + const TileArea area = std::get<1>(tile_and_area); + +#ifdef COMPUTE_KERNEL_WRITER_ASSERTS_ENABLED + // Check if the tile is a CLTile created by this kernel writer. + + { + bool found = false; + + for (const auto &t : _tiles) + { + if (&tile == t.get()) + { + found = true; + break; + } + } + + for (const auto &t : _constant_tiles) + { + if (&tile == t.get()) + { + found = true; + break; + } + } + + if (!found) + { + for (const auto &t : _tensors) + { + const auto components = t->components(); + + for (const auto component : components) + { + if (&tile == &component->tile()) + { + found = true; + break; + } + } + + if (found) + { + break; + } + } + } + + CKW_ASSERT_MSG(found, "The tile is not found!"); + } +#endif // COMPUTE_KERNEL_WRITER_ASSERTS_ENABLED + + return {static_cast<CLTile &>(tile), area}; +} + +void CLKernelWriter::op_load(const TileOperand &tile_op, + const TensorOperand &tensor_op, + TensorSampler &sampler, + const TileOperand &x, + const TileOperand &y, + const TileOperand &z, + const TileOperand &batch) +{ + const CLTile dilation_x({{"1"}}, DataType::Int32); + const CLTile dilation_y({{"1"}}, DataType::Int32); + + op_load_store(MemoryOperation::Load, tile_op, tensor_op, sampler, x, y, z, batch, dilation_x, dilation_y, + false /* indirect buffer */); +} + +void CLKernelWriter::op_load_dilated(const TileOperand &tile_op, + const TensorOperand &tensor_op, + TensorSampler &sampler, + const TileOperand &x, + const TileOperand &y, + const TileOperand &z, + const TileOperand &batch, + const TileOperand &dilation_x, + const TileOperand &dilation_y) +{ + const auto dil_x_view = to_cl_tile_view(dilation_x); + const auto dil_y_view = to_cl_tile_view(dilation_y); + + op_load_store(MemoryOperation::Load, tile_op, tensor_op, sampler, x, y, z, batch, dil_x_view, dil_y_view, + false /* indirect buffer */); +} + +void CLKernelWriter::op_store(const TensorOperand &tensor_op, + const TileOperand &tile_op, + TensorSampler &sampler, + const TileOperand &x, + const TileOperand &y, + const TileOperand &z, + const TileOperand &batch) +{ + const CLTile dilation_x({{"1"}}, DataType::Int32); + const CLTile dilation_y({{"1"}}, DataType::Int32); + + op_load_store(MemoryOperation::Store, tile_op, tensor_op, sampler, x, y, z, batch, dilation_x, dilation_y, + false /* indirect buffer */); +} + +void CLKernelWriter::op_store_dilated(const TensorOperand &tensor_op, + const TileOperand &tile_op, + TensorSampler &sampler, + const TileOperand &x, + const TileOperand &y, + const TileOperand &z, + const TileOperand &batch, + const TileOperand &dilation_x, + const TileOperand &dilation_y) +{ + const auto dil_x_view = to_cl_tile_view(dilation_x); + const auto dil_y_view = to_cl_tile_view(dilation_y); + + op_load_store(MemoryOperation::Store, tile_op, tensor_op, sampler, x, y, z, batch, dil_x_view, dil_y_view, + false /* indirect buffer */); +} + +void CLKernelWriter::op_load_indirect(const TileOperand &tile_op, + const TensorOperand &tensor_op, + TensorSampler &sampler, + const TileOperand &x, + const TileOperand &y, + const TileOperand &z, + const TileOperand &batch) +{ + const CLTile dilation_x({{"1"}}, DataType::Int32); + const CLTile dilation_y({{"1"}}, DataType::Int32); + + op_load_store(MemoryOperation::Load, tile_op, tensor_op, sampler, x, y, z, batch, dilation_x, dilation_y, + true /* indirect buffer */); +} + +void CLKernelWriter::op_load_store(MemoryOperation op, + const TileOperand &tile_op, + const TensorOperand &tensor_op, + TensorSampler &sampler, + const TileOperand &x, + const TileOperand &y, + const TileOperand &z, + const TileOperand &batch, + const TileView<CLTile> &dilation_x, + const TileView<CLTile> &dilation_y, + bool indirect_buffer) +{ + CKW_UNUSED(dilation_x); + CKW_ASSERT(dilation_x.is_scalar()); + CKW_ASSERT(dilation_y.is_scalar()); + CKW_ASSERT(dilation_x.scalar(0, 0).str == "((int)(1))"); // Dilation in x dimension is not implemented yet + + if (indirect_buffer) + { + CKW_ASSERT(dilation_y.scalar(0, 0).str == "((int)(1))" && dilation_x.scalar(0, 0).str == "((int)(1))"); + } + + ITensor &tensor = get_tensor(tensor_op); + + const auto tile = to_cl_tile_view(tile_op); + const auto x_tile = to_cl_tile_view(x).full_tile(); + const auto y_tile = to_cl_tile_view(y).full_tile(); + const auto z_tile = to_cl_tile_view(z).full_tile(); + const auto batch_tile = to_cl_tile_view(batch).full_tile(); + + std::unique_ptr<ICLMemoryOpHelper> helper; + switch (sampler.storage()) + { + case TensorStorageType::BufferUint8Ptr: + helper = std::make_unique<CLMemoryOpBufferHelper>(this, &tensor, &sampler, op, tile); + break; + case TensorStorageType::Texture2dReadOnly: + case TensorStorageType::Texture2dWriteOnly: + helper = std::make_unique<CLMemoryOpImage2dHelper>(this, &tensor, &sampler, op, tile); + break; + default: + CKW_THROW_MSG("Unsupported tensor storage"); + } + + CKW_ASSERT(x_tile.is_scalar()); + CKW_ASSERT(z_tile.is_scalar()); + CKW_ASSERT_IF(indirect_buffer, y_tile.info().width() == 1); + CKW_ASSERT_IF(!indirect_buffer, y_tile.is_scalar()); + CKW_ASSERT(batch_tile.is_scalar()); + + helper->initialize(&x_tile, &z_tile, &batch_tile); + + for (int row = 0; row < tile.height(); ++row) + { + if (!indirect_buffer) + { + std::string coord_y = y_tile.scalar(0, 0).str + " + " + std::to_string(row); + + if (dilation_y.scalar(0, 0).str != "((int)(1))") + { + coord_y += " * " + dilation_y.scalar(0, 0).str; + } + + helper->write_row(row, coord_y); + } + else + { + helper->write_row(row, y_tile.scalar(row, 0).str); + } + } + + helper->finalize(); +} + +} // namespace ckw diff --git a/compute_kernel_writer/src/cl/CLKernelWriter.h b/compute_kernel_writer/src/cl/CLKernelWriter.h new file mode 100644 index 0000000000..6485bae512 --- /dev/null +++ b/compute_kernel_writer/src/cl/CLKernelWriter.h @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef CKW_SRC_CL_CLKERNELWRITER_H +#define CKW_SRC_CL_CLKERNELWRITER_H + +#include "ckw/KernelWriter.h" + +#include "src/TileView.h" + +#include <memory> +#include <set> +#include <string> +#include <utility> + +namespace ckw +{ + +// Forward Declarations +class CLTile; +class CLTensorArgument; +class ConstantData; +class TensorOperand; +class TensorSampler; +class TileOperand; + +enum class DataType; +enum class MemoryOperation; + +/** OpenCL kernel writer. */ +class CLKernelWriter : public KernelWriter +{ +public: + // ============================================================================================= + // Construtors and destructor + // ============================================================================================= + + /** Initialize a new instance of @ref CLKernelWriter class. */ + CLKernelWriter(); + + /** Destructor */ + ~CLKernelWriter(); + + // ============================================================================================= + // Data processing + // ============================================================================================= + + void op_assign(const TileOperand &dst, const TileOperand &src) override; + + void op_cast(const TileOperand &dst, const TileOperand &src, ConvertPolicy policy) override; + + void op_unary(const TileOperand &dst, UnaryOp op, const TileOperand &src) override; + + void op_binary(const TileOperand &dst, BinaryOp op, const TileOperand &first, const TileOperand &second) override; + + void op_ternary(const TileOperand &dst, + TernaryOp op, + const TileOperand &first, + const TileOperand &second, + const TileOperand &third) override; + + // ============================================================================================= + // Flow control + // ============================================================================================= + + void op_if(const TileOperand &lhs, BinaryOp op, const TileOperand &rhs, const std::function<void()> &body) override; + + void + op_else_if(const TileOperand &lhs, BinaryOp op, const TileOperand &rhs, const std::function<void()> &body) override; + + void op_else(const std::function<void()> &body) override; + + void op_for_loop(const TileOperand &var, + BinaryOp cond_op, + const TileOperand &cond_value, + const TileOperand &update_var, + AssignmentOp update_op, + const TileOperand &update_value, + const std::function<void()> &body) override; + + void op_return() override; + + // ============================================================================================= + // Misc + // ============================================================================================= + + void op_get_global_id(const TileOperand &dst, int32_t dim) override; + + void op_comment(const std::string &text) override; + + void op_write_raw_code(const std::string &raw_code) override; + + void op_print(const std::string &prefix, const std::vector<TileOperand> &operands) override; + + // ============================================================================================= + // Code generation + // ============================================================================================= + + std::unique_ptr<Kernel> emit_kernel(const std::string &name) override; + + // ============================================================================================= + // Tensor and tile declaration + // ============================================================================================= + + TensorOperand declare_tensor_argument(const std::string &name, const TensorInfo &info) override; + + /** Declare a tile given name and tile information + * + * Similar to @ref KernelWriter::declare_tile() + */ + TileOperand declare_tile(const std::string &name, const TileInfo &tile_info) override; + + /** Declare a constant tile given a @ref:ConstantData object + * + * Similar to @ref KernelWriter::declare_constant_tile() + */ + TileOperand declare_constant_tile(const ConstantData &data) override; + + // ============================================================================================= + // Memory Operations + // ============================================================================================= + + void op_load(const TileOperand &tile_op, + const TensorOperand &tensor_op, + TensorSampler &sampler, + const TileOperand &x, + const TileOperand &y, + const TileOperand &z, + const TileOperand &batch) override; + + void op_load_dilated(const TileOperand &tile_op, + const TensorOperand &tensor_op, + TensorSampler &sampler, + const TileOperand &x, + const TileOperand &y, + const TileOperand &z, + const TileOperand &batch, + const TileOperand &dilation_x, + const TileOperand &dilation_y) override; + + void op_store(const TensorOperand &tensor_op, + const TileOperand &tile_op, + TensorSampler &sampler, + const TileOperand &x, + const TileOperand &y, + const TileOperand &z, + const TileOperand &batch) override; + + void op_store_dilated(const TensorOperand &tensor_op, + const TileOperand &tile_op, + TensorSampler &sampler, + const TileOperand &x, + const TileOperand &y, + const TileOperand &z, + const TileOperand &batch, + const TileOperand &dilation_x, + const TileOperand &dilation_y) override; + + void op_load_indirect(const TileOperand &tile_op, + const TensorOperand &tensor_op, + TensorSampler &sampler, + const TileOperand &x, + const TileOperand &y, + const TileOperand &z, + const TileOperand &batch) override; + +protected: + /** Return a tile view containing a reference to @ref CLTile object and the active area. + * + * This function performs appropriate check before doing type casting. + */ + TileView<CLTile> to_cl_tile_view(const TileOperand &operand) const; + + /** Append the specified code to the kernel body source code. */ + template <typename T, typename... TArgs> + void append_code(T &&code, TArgs &&...args) + { + append_code(std::forward<T>(code)); + append_code(std::forward<TArgs>(args)...); + } + + /** Append the specified code to the kernel body source code. */ + template <typename T> + void append_code(T &&code) + { + _body_source_code += std::forward<T>(code); + } + + /** Get the current kernel body source code. */ + const std::string &body_source_code() const; + + // For helper functions +private: + /** Helper method to consolidate all load/store logic in this class */ + void op_load_store(MemoryOperation op, + const TileOperand &tile_op, + const TensorOperand &tensor_op, + TensorSampler &sampler, + const TileOperand &x, + const TileOperand &y, + const TileOperand &z, + const TileOperand &batch, + const TileView<CLTile> &dilation_x, + const TileView<CLTile> &dilation_y, + bool indirect_buffer); + + /** This function is the generic function to write both `if` and `else if` blocks. + * + * It is used for both @ref CLKernelWriter::op_if and @ref CLKernelWriter::op_else_if. + * + * @param[in] lhs The LHS tile of the condition. + * @param[in] op The relational binary operator. + * @param[in] rhs The RHS tile of the condition. + * @param[in] body The function that writes the body of the else-if block. + * @param[in] is_else_if True if this is an `else if` block, otherwise this is an `if` block. + */ + void op_if_generic(const TileOperand &lhs, + BinaryOp op, + const TileOperand &rhs, + const std::function<void()> &body, + bool is_else_if); + + // For attributes +private: + /** This string contains the kernel body source code, not the full CL source code. + * The full source code will only be generated when the user calls @ref KernelWriter::emit_kernel. + * + * In order to add code to this, use @ref CLKernelWriter::append_code. + * Do not attempt to concatenate and alter this string directly. + */ + std::string _body_source_code{}; + + std::set<std::unique_ptr<CLTensorArgument>> _tensors{}; + std::set<std::unique_ptr<CLTile>> _tiles{}; + std::set<std::unique_ptr<CLTile>> _constant_tiles{}; +}; + +} // namespace ckw + +#endif // CKW_SRC_CL_CLKERNELWRITER_H diff --git a/compute_kernel_writer/src/cl/CLTensorArgument.cpp b/compute_kernel_writer/src/cl/CLTensorArgument.cpp new file mode 100644 index 0000000000..e53de2830d --- /dev/null +++ b/compute_kernel_writer/src/cl/CLTensorArgument.cpp @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "src/cl/CLTensorArgument.h" + +#include "ckw/Error.h" + +#include "src/cl/CLHelpers.h" +#include "src/cl/CLTensorComponent.h" +#include "src/ITensorArgument.h" +#include "src/ITensorComponent.h" +#include "src/types/TensorComponentType.h" + +#include <algorithm> +#include <vector> + +namespace ckw +{ +CLTensorArgument::CLTensorArgument(const std::string &name, const TensorInfo &info, bool return_dims_by_value) +{ + _return_dims_by_value = return_dims_by_value; + _basename = name; + _info = info; +} + +CLTensorArgument::~CLTensorArgument() = default; + +CLTensorComponent &CLTensorArgument::cl_component(TensorComponentType x) +{ + // Return the component if it has already been created. + { + const auto it = + std::find_if(_components_used.begin(), _components_used.end(), + [=](const std::unique_ptr<CLTensorComponent> &item) { return item->component_type() == x; }); + + if (it != _components_used.end()) + { + return **it; + } + } + + if (_return_dims_by_value) + { + uint32_t component_type = static_cast<uint32_t>(x); + + const bool is_dimension = (component_type & static_cast<uint32_t>(TensorComponentBitmask::Dimension)) != 0; + const bool is_folded_dimensions = + (component_type & static_cast<uint32_t>(TensorComponentBitmask::FoldedDimensions)) != 0; + + constexpr auto bitmask_all = static_cast<uint32_t>(TensorComponentIndexBitmask::All); + constexpr auto bitmask_index_0 = static_cast<uint32_t>(TensorComponentIndexBitmask::Index0); +#ifdef COMPUTE_KERNEL_WRITER_ASSERTS_ENABLED + constexpr auto bitmask_index_1 = static_cast<uint32_t>(TensorComponentIndexBitmask::Index1); + constexpr auto bitmask_index_2 = static_cast<uint32_t>(TensorComponentIndexBitmask::Index2); + constexpr auto bitmask_index_3 = static_cast<uint32_t>(TensorComponentIndexBitmask::Index3); +#endif // COMPUTE_KERNEL_WRITER_ASSERTS_ENABLED + + // Make sure that the encoding of component type hasn't changed and each nibble is 4 bits apart. + CKW_ASSERT(bitmask_all == (bitmask_index_0 | bitmask_index_1 | bitmask_index_2 | bitmask_index_3)); + CKW_ASSERT(bitmask_index_0 == bitmask_index_1 >> 4); + CKW_ASSERT(bitmask_index_1 == bitmask_index_2 >> 4); + CKW_ASSERT(bitmask_index_2 == bitmask_index_3 >> 4); + + // If we have a dimension or folded dimensions, we can return the corresponding value if it is not dynamic (not equal to -1) + if (is_dimension == true || is_folded_dimensions == true) + { + component_type = component_type & bitmask_all; + + int32_t idx = 1; + for (int32_t i = 0; i < tensor_component_index_max_count; ++i) + { + uint32_t dim_idx = component_type & bitmask_index_0; + + if (dim_idx == 0) + { + // Stop at the first nibble containing 0 + break; + } + + // Subtract - 1. Please refer to the TensorComponentIndexBitmask documentation + dim_idx -= 1; + + // Get the dimension value + const int32_t dim_val = _info.shape()[dim_idx]; + + if (dim_val == kDynamicTensorDimensionValue) + { + // We cannot return the dimension by value if it is dynamic. + // Therefore, force the idx variable to kDynamicTensorDimensionValue and break the loop. + idx = kDynamicTensorDimensionValue; + break; + } + + idx *= dim_val; + + // Go to the next nibble + component_type >>= 4; + } + + if (idx != kDynamicTensorDimensionValue) + { + _components_used.emplace_back(std::make_unique<CLTensorComponent>(*this, x, idx)); + + return *_components_used.back(); + } + } + } + + _components_used.emplace_back(std::make_unique<CLTensorComponent>(*this, x)); + + return *_components_used.back(); +} + +ITile &CLTensorArgument::component(TensorComponentType x) +{ + return cl_component(x); +} + +TensorStorageVariable &CLTensorArgument::storage(TensorStorageType x) +{ + // Return the storage if it has already been created. + { + const auto it = std::find_if(_storages_used.begin(), _storages_used.end(), + [=](const TensorStorageVariable &item) { return item.type == x; }); + + if (it != _storages_used.end()) + { + return *it; + } + } + + TensorStorageVariable t; + t.val = create_storage_name(x); + t.type = x; + + _storages_used.emplace_back(t); + + return _storages_used.back(); +} + +std::string CLTensorArgument::create_storage_name(TensorStorageType x) const +{ + std::string var_name = _basename; + + switch (x) + { + case TensorStorageType::BufferUint8Ptr: + var_name += "_ptr"; + break; + case TensorStorageType::Texture2dReadOnly: + case TensorStorageType::Texture2dWriteOnly: + var_name += "_img2d"; + break; + default: + CKW_ASSERT_FAILED_MSG("Unsupported tensor storage"); + return ""; + } + + return var_name; +} + +std::vector<TensorStorageVariable> CLTensorArgument::storages() const +{ + std::vector<TensorStorageVariable> storages; + storages.reserve(_storages_used.size()); + + std::copy(_storages_used.begin(), _storages_used.end(), std::back_inserter(storages)); + + return storages; +} + +std::vector<const ITensorComponent *> CLTensorArgument::components() const +{ + std::vector<const ITensorComponent *> components; + + for (const auto &component : _components_used) + { + if (component->is_assignable()) + { + components.push_back(component.get()); + } + } + + return components; +} +} // namespace ckw diff --git a/compute_kernel_writer/src/cl/CLTensorArgument.h b/compute_kernel_writer/src/cl/CLTensorArgument.h new file mode 100644 index 0000000000..a79cf340bb --- /dev/null +++ b/compute_kernel_writer/src/cl/CLTensorArgument.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef CKW_SRC_CL_CLTENSORARGUMENT_H +#define CKW_SRC_CL_CLTENSORARGUMENT_H + +#include "ckw/types/TensorComponentType.h" +#include "ckw/types/TensorStorageType.h" + +#include "src/cl/CLTensorComponent.h" +#include "src/ITensor.h" + +#include <memory> +#include <string> +#include <vector> + +namespace ckw +{ +// Forward declarations +class TensorInfo; + +class ITensorComponent; + +/** OpenCL specific tensor argument + * Internally, the object keeps track of the components and storages used to minimize the number + * of kernel arguments required. Therefore, if we create this object but we do not access any components + * or storages, the storages() and components() method will return an empty list. +*/ +class CLTensorArgument : public ITensor +{ +public: + /** Constructor + * + * @param[in] name Tensor name + * @param[in] info Tensor info + * @param[in] return_dims_by_value Flag to return the dimensions by value whenever it is possible. + * True, if the dimensions should be returned as value instead as variable. + */ + CLTensorArgument(const std::string &name, const TensorInfo &info, bool return_dims_by_value); + + /** Destructor. */ + ~CLTensorArgument(); + + /** Get a tensor component of the given type. + * + * This function is for internal use as it returns a reference to @ref CLTensorComponent object. + * It provides rich functionalities and doesn't require unnecessary casting + * unlike @ref CLTensorComponent::component which is for the public API and only returns + * a reference to a generic @ref ITile object. + */ + CLTensorComponent &cl_component(TensorComponentType component_type); + + // Inherited method overridden + TensorStorageVariable &storage(TensorStorageType x) override; + ITile &component(TensorComponentType x) override; + std::vector<TensorStorageVariable> storages() const override; + std::vector<const ITensorComponent *> components() const override; + +private: + std::string create_storage_name(TensorStorageType x) const; + + bool _return_dims_by_value{false}; + std::vector<TensorStorageVariable> _storages_used{}; + std::vector<std::unique_ptr<CLTensorComponent>> _components_used{}; +}; + +} // namespace ckw + +#endif // CKW_SRC_CL_CLTENSORARGUMENT_H diff --git a/compute_kernel_writer/src/cl/CLTensorComponent.cpp b/compute_kernel_writer/src/cl/CLTensorComponent.cpp new file mode 100644 index 0000000000..dbe2036768 --- /dev/null +++ b/compute_kernel_writer/src/cl/CLTensorComponent.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "src/cl/CLTensorComponent.h" + +#include "ckw/Error.h" +#include "ckw/types/TensorComponentType.h" + +#include "src/cl/CLTensorArgument.h" +#include "src/cl/CLTile.h" + +namespace ckw +{ + +namespace +{ + +std::string create_component_name(const std::string &name, TensorComponentType x) +{ + std::string var_name(name); + + switch (x) + { + case TensorComponentType::OffsetFirstElement: + var_name += "_offset_first_element"; + break; + case TensorComponentType::Stride0: + var_name += "_stride0"; + break; + case TensorComponentType::Stride1: + var_name += "_stride1"; + break; + case TensorComponentType::Stride2: + var_name += "_stride2"; + break; + case TensorComponentType::Stride3: + var_name += "_stride3"; + break; + case TensorComponentType::Stride4: + var_name += "_stride4"; + break; + case TensorComponentType::Dim0: + var_name += "_dim0"; + break; + case TensorComponentType::Dim1: + var_name += "_dim1"; + break; + case TensorComponentType::Dim2: + var_name += "_dim2"; + break; + case TensorComponentType::Dim3: + var_name += "_dim3"; + break; + case TensorComponentType::Dim4: + var_name += "_dim4"; + break; + case TensorComponentType::Dim1xDim2: + var_name += "_dim1xdim2"; + break; + case TensorComponentType::Dim2xDim3: + var_name += "_dim2xdim3"; + break; + case TensorComponentType::Dim1xDim2xDim3: + var_name += "_dim1xdim2xdim3"; + break; + default: + CKW_THROW_MSG("Unsupported tensor component"); + return ""; + } + + return var_name; +} + +} // namespace + +CLTensorComponent::CLTensorComponent(const CLTensorArgument &tensor, TensorComponentType component_type) + : CLTile(create_component_name(tensor.name(), component_type), TileInfo(DataType::Int32)), + _component_type(component_type) +{ +} + +CLTensorComponent::CLTensorComponent(const CLTensorArgument &tensor, TensorComponentType component_type, int32_t value) + : CLTile({{std::to_string(value)}}, DataType::Int32), _component_type(component_type) +{ + CKW_UNUSED(tensor); +} + +CLTensorComponent::~CLTensorComponent() = default; + +ITile &CLTensorComponent::tile() +{ + return *this; +} + +const ITile &CLTensorComponent::tile() const +{ + return *this; +} + +TensorComponentType CLTensorComponent::component_type() const +{ + return _component_type; +} + +} // namespace ckw diff --git a/compute_kernel_writer/src/cl/CLTensorComponent.h b/compute_kernel_writer/src/cl/CLTensorComponent.h new file mode 100644 index 0000000000..731597ebbf --- /dev/null +++ b/compute_kernel_writer/src/cl/CLTensorComponent.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef CKW_SRC_CL_CLTENSORCOMPONENT_H +#define CKW_SRC_CL_CLTENSORCOMPONENT_H + +#include "ckw/types/TensorComponentType.h" + +#include "src/cl/CLTile.h" +#include "src/ITensorComponent.h" + +namespace ckw +{ + +class CLTensorArgument; + +/** A tensor component object that can be used as a tile. + * + * The tensor component is created by @ref CLTensorArgument object when it is used + * either by the user or internally by a kernel writer operation. + * It allows the user to perform operation on tensor component just like any other tile. + * + * Because of the nature of tensor component, it's always a scalar tile of 32-bit integer. + * + * To find the list of all tensor components, see @ref TensorComponentType. + */ +class CLTensorComponent : public CLTile, public ITensorComponent +{ +public: + /** Initialize a new instance of @ref CLTensorComponent class for dynamic component. + * + * @param[in] tensor The tensor to which this component belongs. + * @param[in] component_type The tensor component type. + */ + CLTensorComponent(const CLTensorArgument &tensor, TensorComponentType component_type); + + /** Initialize a new instance of @ref CLTensorComponent class for compile-time constant component. + * + * @param[in] tensor The tensor to which this component belongs. + * @param[in] component_type The tensor component type. + * @param[in] value The value of the component. + */ + CLTensorComponent(const CLTensorArgument &tensor, TensorComponentType component_type, int32_t value); + + /** Destructor. */ + virtual ~CLTensorComponent(); + + ITile &tile() override; + + const ITile &tile() const override; + + TensorComponentType component_type() const override; + +private: + TensorComponentType _component_type{TensorComponentType::Unknown}; +}; + +} // namespace ckw + +#endif // CKW_SRC_CL_CLTENSORCOMPONENT_H diff --git a/compute_kernel_writer/src/cl/CLTile.cpp b/compute_kernel_writer/src/cl/CLTile.cpp new file mode 100644 index 0000000000..f6e271e813 --- /dev/null +++ b/compute_kernel_writer/src/cl/CLTile.cpp @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "src/cl/CLTile.h" + +#include "ckw/Error.h" +#include "ckw/TileInfo.h" + +#include "src/cl/CLHelpers.h" +#include "src/Helpers.h" + +#include <algorithm> +#include <vector> + +namespace ckw +{ +CLTile::CLTile(const std::string &name, const TileInfo &info) : _is_constant(false) +{ + validate_tile_info(info); + + _basename = name; + _info = info; +} + +CLTile::CLTile(const TileContainer &vals, DataType dt) : _is_constant(true) +{ + const int32_t w = vals[0].size(); + const int32_t h = vals.size(); + + _info.width(w); + _info.height(h); + _info.data_type(dt); + + validate_tile_info(_info); + + _vals = TileContainer(h, std::vector<std::string>(w)); + + for (int32_t y = 0; y < h; ++y) + { + for (int32_t x = 0; x < w; ++x) + { + _vals[y][x] = vals[y][x]; + } + } +} + +const std::string &CLTile::name() const +{ + return _basename; +} + +const TileInfo &CLTile::info() const +{ + return _info; +} + +TileVariable CLTile::scalar(int32_t row, int32_t col) const +{ + // Clamp to nearest valid edge + col = clamp(col, static_cast<int32_t>(0), _info.width() - 1); + row = clamp(row, static_cast<int32_t>(0), _info.height() - 1); + + if (_is_constant) + { + // We can use the vector method to retrieve the scalar variable stored in the constant tile + return vector(row, col, 1); + } + else + { + TileVariable t; + t.str = create_var_name(row); + t.desc.dt = _info.data_type(); + t.desc.len = 1; + + // This check is required because if the width has only one element, we cannot use .s0 + if (_info.width() != 1) + { + // Automatic broadcasting + t.str += ".s" + dec_to_hex_as_string(col); + } + + return t; + } +} + +TileVariable CLTile::vector(int32_t row) const +{ + // Clamp to nearest valid edge + row = clamp(row, static_cast<int32_t>(0), _info.height() - 1); + + if (_is_constant) + { + return vector(row, 0, _info.width()); + } + else + { + TileVariable t; + t.str = create_var_name(row); + t.desc.dt = _info.data_type(); + t.desc.len = _info.width(); + return t; + } +} + +TileVariable CLTile::vector(int32_t row, int32_t col_start, int32_t width) const +{ + CKW_ASSERT(col_start >= 0 && col_start < _info.width()); + CKW_ASSERT(col_start + width <= _info.width()); + + // Validate the new vector length + cl_validate_vector_length(width); + + // Clamp to nearest valid edge + row = clamp(row, static_cast<int32_t>(0), _info.height() - 1); + + TileVariable t; + t.desc.dt = _info.data_type(); + t.desc.len = width; + + if (_is_constant) + { + // The vector has the following form: ((data_typeN)(val0, val1,..., ValN-1)) + t.str = "((" + cl_get_variable_datatype_as_string(t.desc.dt, width) + ")"; + t.str += "("; + + int32_t col = col_start; + for (; col < width - 1; ++col) + { + t.str += _vals[row][col]; + t.str += ", "; + } + t.str += _vals[row][col]; + t.str += "))"; + } + else + { + t.str = create_var_name(row); + + if (_info.width() != 1 && _info.width() != width) + { + t.str += ".s"; + for (int i = 0; i < width; ++i) + { + t.str += dec_to_hex_as_string(col_start + i); + } + } + } + + return t; +} + +std::vector<TileVariable> CLTile::all() const +{ + std::vector<TileVariable> vars; + + if (_is_constant) + { + for (int32_t y = 0; y < _info.height(); ++y) + { + for (int32_t x = 0; x < _info.width(); ++x) + { + // We can use the vector method to retrieve all the scalar variables stored in the constant tile + TileVariable t = vector(y, x, 1); + vars.push_back(t); + } + } + } + else + { + for (int32_t y = 0; y < _info.height(); ++y) + { + TileVariable t; + t.str = create_var_name(y); + t.desc.dt = _info.data_type(); + t.desc.len = _info.width(); + vars.push_back(t); + } + } + + return vars; +} + +bool CLTile::is_assignable() const +{ + return !_is_constant; +} + +std::string CLTile::create_var_name(int32_t row) const +{ + std::string var_name = _basename; + + // If a scalar variable, we do not append the row index + if (_info.height() > 1) + { + var_name += "__"; + var_name += std::to_string(row); + } + + return var_name; +} + +std::vector<int32_t> CLTile::supported_vector_lengths() const +{ + return std::vector<int32_t>{1, 2, 3, 4, 8, 16}; +} + +void CLTile::validate_tile_info(const TileInfo &info) const +{ + CKW_UNUSED(info); + CKW_ASSERT_MSG(cl_validate_vector_length(info.width()), "Unsupported TileInfo width"); + CKW_ASSERT_MSG(info.data_type() != DataType::Unknown, "DataType::Unknown is not supported"); +} + +} // namespace ckw diff --git a/compute_kernel_writer/src/cl/CLTile.h b/compute_kernel_writer/src/cl/CLTile.h new file mode 100644 index 0000000000..498cf51034 --- /dev/null +++ b/compute_kernel_writer/src/cl/CLTile.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef COMPUTE_KERNEL_WRITER_SRC_CL_CLTILE_H +#define COMPUTE_KERNEL_WRITER_SRC_CL_CLTILE_H + +#include "src/ITile.h" + +#include <string> + +namespace ckw +{ +// Forward declarations +class TileInfo; + +/** OpenCL specific tile */ +class CLTile : public ITile, public IVectorAccess +{ +public: + /** Initialize a new instance of @ref CLTile class for variable tile. + * + * @param[in] name Tile name + * @param[in] info Tile info + */ + CLTile(const std::string &name, const TileInfo &info); + + /** Initialize a new instane of @ref CLTile class for compile-time constant tile. + * + * @note A constant tile does not need a name since this object does not return variable's name but rather + * values stored as string type + * + * @param[in] vals The tile container with the constant values as std::string + * @param[in] dt Datatype of the values stored in the tile container + */ + CLTile(const TileContainer &vals, DataType dt); + + // Inherited method overridden + const std::string &name() const override; + + const TileInfo &info() const override; + + TileVariable scalar(int32_t row, int32_t col) const override; + + TileVariable vector(int32_t row) const override; + + TileVariable vector(int32_t row, int32_t col_start, int32_t width) const override; + + std::vector<TileVariable> all() const override; + + bool is_assignable() const override; + + std::vector<int32_t> supported_vector_lengths() const override; + +private: + void validate_tile_info(const TileInfo &info) const; + + std::string create_var_name(int32_t row) const; + + TileInfo _info{DataType::Unknown}; + std::string _basename{""}; + bool _is_constant{false}; + TileContainer _vals{}; +}; +} // namespace ckw + +#endif /* COMPUTE_KERNEL_WRITER_SRC_CL_CLTILE_H */ diff --git a/compute_kernel_writer/src/cl/helpers/CLMemoryOpBufferHelper.cpp b/compute_kernel_writer/src/cl/helpers/CLMemoryOpBufferHelper.cpp new file mode 100644 index 0000000000..7d16f35fbe --- /dev/null +++ b/compute_kernel_writer/src/cl/helpers/CLMemoryOpBufferHelper.cpp @@ -0,0 +1,353 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "src/cl/helpers/CLMemoryOpBufferHelper.h" + +#include "ckw/Error.h" +#include "ckw/TensorSampler.h" +#include "ckw/types/MemoryOperation.h" +#include "ckw/types/TensorStorageType.h" + +#include "src/cl/CLHelpers.h" +#include "src/cl/CLKernelWriter.h" +#include "src/cl/CLTensorArgument.h" +#include "src/cl/CLTile.h" +#include "src/ITensor.h" +#include "src/Tensor3dMapper.h" +#include "src/TileView.h" + +namespace ckw +{ +bool CLMemoryOpBufferHelper::validate(const CLKernelWriter *writer, + const ITensor *tensor, + const TensorSampler *sampler, + const Tensor3dMapper *mapper, + MemoryOperation op, + const TileView<CLTile> &dst) +{ + CKW_UNUSED(writer, tensor, mapper, op, dst); + + if (sampler->storage() != TensorStorageType::BufferUint8Ptr) + { + return false; + } + return true; +} + +/** Initialization and Finalizing Logic + * + * The meanings of if/elses in different dimensions and how they're constructed: + * - x: partial load/store + * - y: no load/store operation + * - z: no load/store operation + * if(x) + * { + * if(z) + * { + * if(y) + * { + * // full load/store width + * } + * else + * { + * // no load/store + * } + * } + * else + * { + * // no load/store + * } + * } + * else + * { + * if(z) + * { + * if(y) + * { + * // partial load/store width + * } + * else + * { + * // no load/store + * } + * } + * else + * { + * // no load/store + * } + * } + * + * In general, initialize() writes if conditions, and finalize() writes else conditions. + * The outermost block is x, then z and then y. This is why, if/else's covering for y are initialized + * at each row write. In some addressing modes, such as None, no if/else conditions are written. + */ +void CLMemoryOpBufferHelper::initialize(const CLTile *x, const CLTile *z, const CLTile *b) +{ + CKW_ASSERT(validate(_writer, _tensor, _sampler, _mapper.get(), _op, _dst)); + + _coord_x = x->scalar(0, 0).str; + _coord_z = z->scalar(0, 0).str; + _coord_b = b->scalar(0, 0).str; + _coord_orig_z = _coord_z; + + out_of_bound_initialize_x(_coord_x); + out_of_bound_initialize_z(_coord_z); +} + +void CLMemoryOpBufferHelper::write_row(int32_t row_id, const std::string &coord_y) +{ + // The only check required is on Y. + out_of_bound_initialize_y(coord_y); + + const std::string dst = _dst.vector(row_id).str; + const std::string address = to_buffer_address(_coord_x, coord_y, _coord_z, _coord_b); + const std::string ls_buf = to_statement(_op, _ls_width_full, dst, address); + + _writer->op_write_raw_code(ls_buf); + _writer->op_write_raw_code(";\n"); + + out_of_bound_finalize_y(dst); + + // The left over load/store will be written in the finalize stage + if (_ls_width_part.size() != 0) + { + int32_t col_start = 0; + const TileArea original_area = _dst.area(); + + for (int32_t partial_width : _ls_width_part) + { + // Set the active area + const TileArea area(original_area.row_start(), original_area.row_end(), col_start, + col_start + partial_width); + _dst.area(area); + + const std::string dst = _dst.vector(row_id).str; + const std::string coord_x = _coord_x + " + " + std::to_string(col_start); + const std::string address = to_buffer_address(coord_x, coord_y, _coord_z, _coord_b); + const std::string statement = to_statement(_op, partial_width, dst, address); + _leftovers_x.emplace_back(dst, coord_y, statement); + + col_start += partial_width; + } + // Restore the original area + _dst.area(original_area); + } +} + +void CLMemoryOpBufferHelper::finalize() +{ + out_of_bound_finalize_z(); + out_of_bound_finalize_x(); +} + +void CLMemoryOpBufferHelper::out_of_bound_initialize_x(const std::string &coord) +{ + if (_sampler->address_mode_x() == TensorSamplerAddressModeX::OverlappingMin) + { + TensorInfo tensor_info = _tensor->info(); + TensorShape shape = tensor_info.shape(); + + _ls_width_part = cl_decompose_vector_width(shape[0] % _ls_width_full); + if (_ls_width_part.size() != 0) + { + _writer->op_write_raw_code("if(" + coord + " > 0)\n{\n"); + } + } +} + +void CLMemoryOpBufferHelper::out_of_bound_finalize_x() +{ + if (_sampler->address_mode_x() == TensorSamplerAddressModeX::OverlappingMin) + { + if (_ls_width_part.size() != 0) + { + _writer->op_write_raw_code("}\nelse\n{\n"); + + out_of_bound_initialize_z(_coord_orig_z); + for (LeftoverDescriptor leftover_desc : _leftovers_x) + { + out_of_bound_initialize_y(leftover_desc.coord); + _writer->op_write_raw_code(leftover_desc.statement); + _writer->op_write_raw_code(";\n"); + out_of_bound_finalize_y(leftover_desc.dst); + } + out_of_bound_finalize_z(); + _writer->op_write_raw_code("}\n"); + } + } +} + +void CLMemoryOpBufferHelper::out_of_bound_initialize_y(const std::string &coord) +{ + std::string max = ""; + + const TensorSamplerAddressModeY address_mode_y = _sampler->address_mode_y(); + + switch (address_mode_y) + { + case TensorSamplerAddressModeY::ClampToBorderMaxOnly: + // Not to be moved outside the case because it marks the relevant tensor component as used even if we dont't use the variable + max = _mapper->dim_y().str; + _writer->op_write_raw_code("if(" + coord + " < " + max + ")\n{\n"); + break; + case TensorSamplerAddressModeY::SkipLessThanZero: + _writer->op_write_raw_code("if(" + coord + " >= 0)\n{\n"); + break; + case TensorSamplerAddressModeY::None: + break; + default: + CKW_THROW_MSG("Unsupported address mode for Y dimension"); + } +} + +void CLMemoryOpBufferHelper::out_of_bound_finalize_y(const std::string &dst) +{ + const TensorSamplerAddressModeY address_mode_y = _sampler->address_mode_y(); + + switch (address_mode_y) + { + case TensorSamplerAddressModeY::ClampToBorderMaxOnly: + _writer->op_write_raw_code("}\nelse\n{\n"); + _writer->op_write_raw_code(dst); + _writer->op_write_raw_code(" = 0.0f;\n}\n"); + break; + case TensorSamplerAddressModeY::SkipLessThanZero: + _writer->op_write_raw_code("}\n"); + break; + case TensorSamplerAddressModeY::None: + break; + default: + CKW_THROW_MSG("Unsupported address mode for Y dimension"); + } +} + +void CLMemoryOpBufferHelper::out_of_bound_initialize_z(const std::string &coord) +{ + CKW_UNUSED(coord); + + const TensorSamplerAddressModeZ address_mode_z = _sampler->address_mode_z(); + switch (address_mode_z) + { + case TensorSamplerAddressModeZ::None: + break; + default: + CKW_THROW_MSG("Unsupported address mode for Z dimension"); + } +} + +void CLMemoryOpBufferHelper::out_of_bound_finalize_z() +{ + const TensorSamplerAddressModeZ address_mode_z = _sampler->address_mode_z(); + + switch (address_mode_z) + { + case TensorSamplerAddressModeZ::None: + break; + default: + CKW_THROW_MSG("Unsupported address mode for Z dimension"); + } +} + +std::string CLMemoryOpBufferHelper::to_statement(MemoryOperation op, + int32_t vector_width, + const std::string &data, + const std::string &address) const +{ + switch (op) + { + case MemoryOperation::Load: + if (vector_width != 1) + { + return data + " = vload" + std::to_string(vector_width) + "(0, " + address + ")"; + } + else + { + return data + " = *(" + address + ")"; + } + break; + case MemoryOperation::Store: + if (vector_width != 1) + { + return "vstore" + std::to_string(vector_width) + "(" + data + ", 0, " + address + ")"; + } + else + { + return "*(" + address + ") = " + data; + } + break; + default: + CKW_THROW_MSG("Unsupported MemoryOperation"); + } + + return ""; +} + +std::string CLMemoryOpBufferHelper::to_buffer_address(const std::string &x, + const std::string &y, + const std::string &z, + const std::string &b) const +{ + TensorStorageType tensor_storage = _sampler->storage(); + CKW_ASSERT(tensor_storage == TensorStorageType::BufferUint8Ptr); + + const std::string ptr_buf = _tensor->storage(tensor_storage).val; + const std::string dst_type = cl_data_type_rounded_up_to_valid_vector_width(_dst.data_type(), 1); + + std::string address; + address += "(__global "; + address += dst_type; + address += "*)("; + address += ptr_buf; + if (x != "0" && (_mapper->dim_x().str != "1")) + { + address += " + ("; + address += x + ") * sizeof(" + dst_type + ")"; + } + if (y != "0") + { + const std::string stride_y = _mapper->stride_y().str; + address += " + ("; + address += y + ")"; + address += " * "; + address += stride_y; + } + if (z != "0" && (_mapper->dim_z().str != "1")) + { + const std::string stride_z = _mapper->stride_z().str; + address += " + ("; + address += z + ")"; + address += " * "; + address += stride_z; + } + if (b != "0" && (_mapper->dim_batch().str != "1")) + { + const std::string stride_b = _mapper->stride_batch().str; + address += " + ("; + address += b + ")"; + address += " * "; + address += stride_b; + } + address += ")"; + return address; +} +} // namespace ckw diff --git a/compute_kernel_writer/src/cl/helpers/CLMemoryOpBufferHelper.h b/compute_kernel_writer/src/cl/helpers/CLMemoryOpBufferHelper.h new file mode 100644 index 0000000000..a6b3272f32 --- /dev/null +++ b/compute_kernel_writer/src/cl/helpers/CLMemoryOpBufferHelper.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef CKW_SRC_CL_HELPERS_CLMEMORYOPBUFFERHELPER_H +#define CKW_SRC_CL_HELPERS_CLMEMORYOPBUFFERHELPER_H + +#include "src/cl/helpers/ICLMemoryOpHelper.h" + +#include <cstdint> +#include <string> +#include <vector> + +namespace ckw +{ + +// Forward Declarations +class CLKernelWriter; +class CLTile; +template <class CLTile> +class TileView; +enum class MemoryOperation; + +/** Helper class to write memory operations (like load/store) in OpenCL + */ +class CLMemoryOpBufferHelper : public ICLMemoryOpHelper +{ +public: + /** Constructor similar to @ref ICLMemoryOpHelper() */ + CLMemoryOpBufferHelper(CLKernelWriter *writer, + ITensor *tensor, + TensorSampler *sampler, + MemoryOperation op, + const TileView<CLTile> &dst) + : ICLMemoryOpHelper(writer, tensor, sampler, op, dst) + { + } + + /** Copy constructor */ + CLMemoryOpBufferHelper(const CLMemoryOpBufferHelper &) = delete; + + /** Assignment operator overload */ + CLMemoryOpBufferHelper &operator=(const CLMemoryOpBufferHelper &) = delete; + + // Methods overridden + void initialize(const CLTile *x, const CLTile *z, const CLTile *b) override; + void write_row(int32_t row_id, const std::string &coord_y) override; + void finalize() override; + +private: + struct LeftoverDescriptor + { + LeftoverDescriptor(const std::string &dst, const std::string &coord, const std::string &statement) + : dst(dst), coord(coord), statement(statement) + { + } + + std::string dst{}; // Describes the destination tile or part of it + std::string coord{}; // Describes the coordinate to be used in boundary checks + std::string statement{}; // Describes the memory operation statement + }; + + std::vector<int32_t> _ls_width_part{}; + std::vector<LeftoverDescriptor> _leftovers_x{}; + std::string _coord_orig_z{}; + + static bool validate(const CLKernelWriter *writer, + const ITensor *tensor, + const TensorSampler *sampler, + const Tensor3dMapper *mapper, + MemoryOperation op, + const TileView<CLTile> &dst); + + void out_of_bound_initialize_x(const std::string &coord); + void out_of_bound_finalize_x(); + void out_of_bound_initialize_y(const std::string &coord); + void out_of_bound_finalize_y(const std::string &dst); + void out_of_bound_initialize_z(const std::string &coord); + void out_of_bound_finalize_z(); + + std::string + to_statement(MemoryOperation op, int32_t vector_width, const std::string &data, const std::string &address) const; + std::string + to_buffer_address(const std::string &x, const std::string &y, const std::string &z, const std::string &b) const; +}; +} // namespace ckw + +#endif // CKW_SRC_CL_HELPERS_CLMEMORYOPBUFFERHELPER_H diff --git a/compute_kernel_writer/src/cl/helpers/CLMemoryOpImage2dHelper.cpp b/compute_kernel_writer/src/cl/helpers/CLMemoryOpImage2dHelper.cpp new file mode 100644 index 0000000000..f392cd89cc --- /dev/null +++ b/compute_kernel_writer/src/cl/helpers/CLMemoryOpImage2dHelper.cpp @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "src/cl/helpers/CLMemoryOpImage2dHelper.h" + +#include "ckw/Error.h" +#include "ckw/TensorSampler.h" +#include "ckw/types/MemoryOperation.h" +#include "ckw/types/TensorStorageType.h" + +#include "src/cl/CLKernelWriter.h" +#include "src/cl/CLTensorArgument.h" +#include "src/cl/CLTile.h" +#include "src/ITensor.h" +#include "src/Tensor3dMapper.h" +#include "src/TileView.h" + +namespace ckw +{ +void CLMemoryOpImage2dHelper::initialize(const CLTile *x, const CLTile *z, const CLTile *b) +{ + _coord_x = x->scalar(0, 0).str; + _coord_z = z->scalar(0, 0).str; + _coord_b = b->scalar(0, 0).str; +} + +void CLMemoryOpImage2dHelper::write_row(int32_t row_id, const std::string &coord_y) +{ + // The only check required is on Y. + out_of_bound_initialize_y(coord_y); + + const std::string dst = _dst.vector(row_id).str; + const std::string sampler = to_ls_image2d_sampler(); + const std::string coord = to_ls_image2d_address(_coord_x, coord_y, _coord_z, _coord_b); + const std::string ls_buf = to_ls_image2d(_op, _ls_width_full, dst, sampler, coord); + + _writer->op_write_raw_code(ls_buf + ";\n"); + + out_of_bound_finalize_y(); +} + +void CLMemoryOpImage2dHelper::finalize() +{ +} + +bool CLMemoryOpImage2dHelper::validate(const CLKernelWriter *writer, + const ITensor *tensor, + const TensorSampler *sampler, + const Tensor3dMapper *mapper, + MemoryOperation op, + const TileView<CLTile> &dst) +{ + CKW_UNUSED(writer, tensor, mapper); + + if (dst.width() != 4) + { + return false; + } + if (sampler->address_mode_x() != TensorSamplerAddressModeX::None) + { + return false; + } + if (sampler->address_mode_z() != TensorSamplerAddressModeZ::None) + { + return false; + } + if (sampler->storage() != TensorStorageType::Texture2dReadOnly && op == MemoryOperation::Load) + { + return false; + } + if (sampler->storage() != TensorStorageType::Texture2dWriteOnly && op == MemoryOperation::Store) + { + return false; + } + if ((dst.data_type() != DataType::Fp32) && (dst.data_type() != DataType::Fp16)) + { + return false; + } + return true; +} + +void CLMemoryOpImage2dHelper::out_of_bound_initialize_y(const std::string &coord) +{ + CKW_UNUSED(coord); + + const TensorSamplerAddressModeY address_mode_y = _sampler->address_mode_y(); + switch (address_mode_y) + { + case TensorSamplerAddressModeY::SkipLessThanZero: + _writer->op_write_raw_code("if(" + coord + " >= 0)\n{\n"); + break; + case TensorSamplerAddressModeY::ClampToBorderMaxOnly: + case TensorSamplerAddressModeY::None: + break; + default: + CKW_THROW_MSG("Unsupported address mode for Y dimension"); + } +} + +void CLMemoryOpImage2dHelper::out_of_bound_finalize_y() +{ + const TensorSamplerAddressModeY address_mode_y = _sampler->address_mode_y(); + switch (address_mode_y) + { + case TensorSamplerAddressModeY::SkipLessThanZero: + _writer->op_write_raw_code("}\n"); + break; + case TensorSamplerAddressModeY::ClampToBorderMaxOnly: + case TensorSamplerAddressModeY::None: + break; + default: + CKW_THROW_MSG("Unsupported address mode for Y dimension"); + } +} + +std::string CLMemoryOpImage2dHelper::to_ls_image2d(MemoryOperation op, + int32_t vector_width, + const std::string &data, + const std::string &sampler, + const std::string &address) const +{ + CKW_UNUSED(vector_width); + CKW_ASSERT_MSG(_dst.data_type() == DataType::Fp32 || _dst.data_type() == DataType::Fp16, + "Image2d only supports floating-point data type"); + + const TensorStorageType tensor_storage = _sampler->storage(); + const std::string image2d_obj = _tensor->storage(tensor_storage).val; + const std::string post_fix = _dst.data_type() == DataType::Fp32 ? "f" : "h"; + + switch (op) + { + case MemoryOperation::Load: + return data + " = read_image" + post_fix + "(" + image2d_obj + ", " + sampler + ", " + address + ")"; + break; + case MemoryOperation::Store: + return "write_image" + post_fix + "(" + image2d_obj + ", " + address + ", " + data + ")"; + default: + CKW_THROW_MSG("Unsupported MemoryOperation"); + } +} + +std::string CLMemoryOpImage2dHelper::to_ls_image2d_sampler() const +{ + const auto address_mode_y = _sampler->address_mode_y(); + + switch (address_mode_y) + { + case TensorSamplerAddressModeY::None: + return "CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_NONE | CLK_FILTER_NEAREST"; + case TensorSamplerAddressModeY::SkipLessThanZero: + case TensorSamplerAddressModeY::ClampToBorderMaxOnly: + return "CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP | CLK_FILTER_NEAREST"; + default: + CKW_THROW_MSG("Unsupported address_mode_coord"); + } +} + +std::string CLMemoryOpImage2dHelper::to_ls_image2d_address(const std::string &x, + const std::string &y, + const std::string &z, + const std::string &b) const +{ + std::string coord_x = "(" + x + ") >> 2"; + std::string coord_y = "("; + + if (y != "0") + { + coord_y += y; + } + if (z != "0" && (_mapper->dim_z().str != "1")) + { + const std::string dim = _mapper->dim_y().str; + coord_y += " + ("; + coord_y += z + ")"; + coord_y += " * "; + coord_y += dim; + } + if (b != "0" && (_mapper->dim_batch().str != "1")) + { + const std::string dim0 = _mapper->dim_y().str; + const std::string dim1 = _mapper->dim_z().str; + coord_y += " + ("; + coord_y += b + ")"; + coord_y += " * "; + coord_y += dim0; + coord_y += " * "; + coord_y += dim1; + } + coord_y += ")"; + return "(int2)(" + coord_x + ", " + coord_y + ")"; +} + +} // namespace ckw diff --git a/compute_kernel_writer/src/cl/helpers/CLMemoryOpImage2dHelper.h b/compute_kernel_writer/src/cl/helpers/CLMemoryOpImage2dHelper.h new file mode 100644 index 0000000000..6c42c132d9 --- /dev/null +++ b/compute_kernel_writer/src/cl/helpers/CLMemoryOpImage2dHelper.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef CKW_SRC_CL_HELPERS_CLMEMORYOPIMAGE2DHELPER_H +#define CKW_SRC_CL_HELPERS_CLMEMORYOPIMAGE2DHELPER_H + +#include "src/cl/helpers/ICLMemoryOpHelper.h" + +#include <string> + +namespace ckw +{ + +// Forward Declarations +class CLKernelWriter; +class CLTile; +template <class CLTile> +class TileView; +enum class MemoryOperation; + +/** Helper class to write memory operations (like load/store) in OpenCL for Image2d type */ +class CLMemoryOpImage2dHelper : public ICLMemoryOpHelper +{ +public: + /** Constructor similar to @ref ICLMemoryOpHelper() */ + CLMemoryOpImage2dHelper(CLKernelWriter *writer, + ITensor *tensor, + TensorSampler *sampler, + MemoryOperation op, + const TileView<CLTile> &dst) + : ICLMemoryOpHelper(writer, tensor, sampler, op, dst) + { + } + + /** Copy constructor */ + CLMemoryOpImage2dHelper(const CLMemoryOpImage2dHelper &) = delete; + + /** Assignment operator overload */ + CLMemoryOpImage2dHelper &operator=(const CLMemoryOpImage2dHelper &) = delete; + + // Methods overridden + void initialize(const CLTile *x, const CLTile *z, const CLTile *b) override; + void write_row(int32_t row_id, const std::string &coord_y) override; + void finalize() override; + +private: + static bool validate(const CLKernelWriter *writer, + const ITensor *tensor, + const TensorSampler *sampler, + const Tensor3dMapper *mapper, + MemoryOperation op, + const TileView<CLTile> &dst); + + void out_of_bound_initialize_y(const std::string &coord); + void out_of_bound_finalize_y(); + + std::string to_ls_image2d(MemoryOperation op, + int32_t vector_width, + const std::string &data, + const std::string &sampler, + const std::string &address) const; + std::string to_ls_image2d_sampler() const; + std::string + to_ls_image2d_address(const std::string &x, const std::string &y, const std::string &z, const std::string &b) const; +}; +} // namespace ckw + +#endif // CKW_SRC_CL_HELPERS_CLMEMORYOPIMAGE2DHELPER_H diff --git a/compute_kernel_writer/src/cl/helpers/ICLMemoryOpHelper.h b/compute_kernel_writer/src/cl/helpers/ICLMemoryOpHelper.h new file mode 100644 index 0000000000..a5b679ac03 --- /dev/null +++ b/compute_kernel_writer/src/cl/helpers/ICLMemoryOpHelper.h @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef CKW_SRC_CL_HELPERS_ICLMEMORYOPHELPER_H +#define CKW_SRC_CL_HELPERS_ICLMEMORYOPHELPER_H + +#include "ckw/TensorSampler.h" + +#include "src/Tensor3dMapper.h" +#include "src/TileView.h" + +#include <cstdint> +#include <memory> +#include <string> + +namespace ckw +{ + +// Forward Declarations +class CLTile; +class CLKernelWriter; +class ITensor; +class TensorSampler; +enum class MemoryOperation; + +/** Base class OpenCL memory operation helper classes + * that helps writing code for memory operations like load/store. + */ +class ICLMemoryOpHelper +{ +public: + /** Constructor + * + * @param[in] writer @ref ckw::CLKernelWriter object to write the code + * @param[in] tensor @ref ckw::ITensor object to perform the memory operation on + * @param[in] sampler @ref ckw::TensorSampler object that tells how to sample a tensor + * @param[in] op The memory operation to be done (e.g. Load/Store) + * @param[in] dst The tile to perform the memory operation on + */ + ICLMemoryOpHelper(CLKernelWriter *writer, + ITensor *tensor, + TensorSampler *sampler, + MemoryOperation op, + const TileView<CLTile> &dst) + : _writer(writer), _tensor(tensor), _sampler(sampler), _op(op), _dst(dst) + { + _mapper = std::make_unique<Tensor3dMapper>(tensor, sampler->format()); + _ls_width_full = _dst.width(); + } + + /** Copy constructor */ + ICLMemoryOpHelper(const ICLMemoryOpHelper &) = delete; + + /** Assignment operator overload */ + ICLMemoryOpHelper &operator=(const ICLMemoryOpHelper &) = delete; + + /** Destructor */ + virtual ~ICLMemoryOpHelper() = default; + + /** Initialization method that takes a 3D tensor's x, z dimensions and + * the batch offset as a tile object, and initializes the code inside + * the writer object. + * + * @param[in] x tile object that describes the x-coordinate of the tensor involved + * @param[in] z tile object that describes the z-coordinate of the tensor involved + * @param[in] b tile object that describes the batch offset of the tensor involved + */ + virtual void initialize(const CLTile *x, const CLTile *z, const CLTile *b) = 0; + + /** Method that writes the actual code to the writer that performs the mentioned memory + * operation on the tile initialized. It writes the code for a specific row given in the + * arguments. + * + * @param[in] row_id row id + * @param[in] coord_y y-coordinate as string + */ + virtual void write_row(int32_t row_id, const std::string &coord_y) = 0; + + /** Method that finalizes the code in the writer object. This part is usually for taking + * care of finalizing anything that's been initialized inside @ref IMemoryHelper::initialize() + * such as matching compound statements, checking certain boundary conditions etc. No inputs + * and/or outputs, only the writer object is affected. + */ + virtual void finalize() = 0; + +protected: + CLKernelWriter *_writer{nullptr}; + ITensor *_tensor{nullptr}; + TensorSampler *_sampler{nullptr}; + MemoryOperation _op; + std::unique_ptr<Tensor3dMapper> _mapper{nullptr}; + TileView<CLTile> _dst{}; + int32_t _ls_width_full{0}; + std::string _coord_x{}; + std::string _coord_z{}; + std::string _coord_b{}; +}; +} // namespace ckw + +#endif // CKW_SRC_CL_HELPERS_ICLMEMORYOPHELPER_H diff --git a/compute_kernel_writer/src/types/ConstantData.cpp b/compute_kernel_writer/src/types/ConstantData.cpp new file mode 100644 index 0000000000..6d15eab407 --- /dev/null +++ b/compute_kernel_writer/src/types/ConstantData.cpp @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "ckw/types/ConstantData.h" + +#include <limits> + +namespace ckw +{ +namespace +{ +template <typename T> +typename std::enable_if<std::is_same<T, float>::value, std::string>::type to_str(T value) +{ + std::stringstream ss; + ss << std::scientific << std::setprecision(std::numeric_limits<T>::max_digits10) << value; + return ss.str(); +} + +template <typename T> +typename std::enable_if<!std::is_same<T, float>::value && !std::is_same<T, bool>::value, std::string>::type +to_str(T value) +{ + return std::to_string(value); +} + +template <typename T> +typename std::enable_if<std::is_same<T, bool>::value, std::string>::type to_str(T value) +{ + return std::to_string((int)value); +} +} // namespace + +template <typename T> +ConstantData::ConstantData(std::initializer_list<std::initializer_list<T>> values, DataType data_type) + : _data_type(data_type) +{ + CKW_ASSERT(validate<T>(data_type)); + CKW_ASSERT(values.size() > 0); + + for (auto value_arr : values) + { + // Each row must have the same number of elements + CKW_ASSERT(value_arr.size() == (*values.begin()).size()); + + StringVector vec; + std::transform(value_arr.begin(), value_arr.end(), std::back_inserter(vec), [](T val) { return to_str(val); }); + + _values.push_back(std::move(vec)); + } +} + +template <typename T> +ConstantData::ConstantData(const std::vector<std::vector<T>> &values, DataType data_type) : _data_type(data_type) +{ + CKW_ASSERT(validate<T>(data_type)); + CKW_ASSERT(values.size() > 0); + + for (auto value_arr : values) + { + // Each row must have the same number of elements + CKW_ASSERT(value_arr.size() == (*values.begin()).size()); + + StringVector vec; + std::transform(value_arr.begin(), value_arr.end(), std::back_inserter(vec), [](T val) { return to_str(val); }); + + _values.push_back(std::move(vec)); + } +} + +template <typename T> +bool ConstantData::validate(DataType data_type) +{ + switch (data_type) + { + case DataType::Fp32: + case DataType::Fp16: + return std::is_same<T, float>::value; + case DataType::Bool: + return std::is_same<T, bool>::value; + case DataType::Int32: + case DataType::Int16: + case DataType::Int8: + return std::is_same<T, int32_t>::value; + case DataType::Uint32: + case DataType::Uint16: + case DataType::Uint8: + return std::is_same<T, uint32_t>::value; + default: + CKW_THROW_MSG("Unknown data type!"); + break; + } +} + +// Necessary instantiations for compiler to recognize +template ConstantData::ConstantData(std::initializer_list<std::initializer_list<int32_t>>, DataType); +template ConstantData::ConstantData(std::initializer_list<std::initializer_list<uint32_t>>, DataType); +template ConstantData::ConstantData(std::initializer_list<std::initializer_list<bool>>, DataType); +template ConstantData::ConstantData(std::initializer_list<std::initializer_list<float>>, DataType); +template ConstantData::ConstantData(const std::vector<std::vector<int32_t>> &, DataType); +template ConstantData::ConstantData(const std::vector<std::vector<uint32_t>> &, DataType); +template ConstantData::ConstantData(const std::vector<std::vector<bool>> &, DataType); +template ConstantData::ConstantData(const std::vector<std::vector<float>> &, DataType); + +template bool ConstantData::validate<int32_t>(DataType); +template bool ConstantData::validate<uint32_t>(DataType); +template bool ConstantData::validate<bool>(DataType); +template bool ConstantData::validate<float>(DataType); + +const std::vector<std::vector<std::string>> &ConstantData::values() const +{ + return _values; +} + +DataType ConstantData::data_type() const +{ + return _data_type; +} + +} // namespace ckw diff --git a/compute_kernel_writer/src/types/DataTypeHelpers.cpp b/compute_kernel_writer/src/types/DataTypeHelpers.cpp new file mode 100644 index 0000000000..7f0c33fb72 --- /dev/null +++ b/compute_kernel_writer/src/types/DataTypeHelpers.cpp @@ -0,0 +1,35 @@ +/* +* Copyright (c) 2023 Arm Limited. +* +* SPDX-License-Identifier: MIT +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to +* deal in the Software without restriction, including without limitation the +* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +* sell copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#include "src/types/DataTypeHelpers.h" + +namespace ckw +{ + +bool is_data_type_float(DataType data_type) +{ + return (data_type == DataType::Fp32 || data_type == DataType::Fp16); +} + +} // namespace ckw diff --git a/compute_kernel_writer/src/types/DataTypeHelpers.h b/compute_kernel_writer/src/types/DataTypeHelpers.h new file mode 100644 index 0000000000..b6ec6ccd19 --- /dev/null +++ b/compute_kernel_writer/src/types/DataTypeHelpers.h @@ -0,0 +1,43 @@ +/* +* Copyright (c) 2023 Arm Limited. +* +* SPDX-License-Identifier: MIT +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to +* deal in the Software without restriction, including without limitation the +* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +* sell copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#ifndef CKW_SRC_TYPES_DATATYPEHELPERS_H +#define CKW_SRC_TYPES_DATATYPEHELPERS_H + +#include "ckw/types/DataType.h" + +namespace ckw +{ + +/** Return a value indicating whether the data type is floating-point. + * + * @param[in] data_type The data type to check. + * + * @return Whether the data type is floating-point. + */ +bool is_data_type_float(DataType data_type); + +} // namespace ckw + +#endif // CKW_SRC_TYPES_DATATYPEHELPERS_H diff --git a/compute_kernel_writer/src/types/TensorComponentType.h b/compute_kernel_writer/src/types/TensorComponentType.h new file mode 100644 index 0000000000..03f4f4f5c8 --- /dev/null +++ b/compute_kernel_writer/src/types/TensorComponentType.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef CKW_SRC_TYPES_TENSORCOMPONENTTYPE_H +#define CKW_SRC_TYPES_TENSORCOMPONENTTYPE_H + +#include <cstdint> + +namespace ckw +{ + +/** Compute Kernel Writer tensor component bitmask. + * + * The bitmask can be used to retrieve the info from @ref TensorComponent. + */ +enum class TensorComponentBitmask : uint32_t +{ + OffsetFirstElement = 0x01000000, // For example, OffsetFirstElement in TensorComponent + Stride = 0x02000000, // For example, stride0 in TensorComponent + Dimension = 0x04000000, // For example, Dim0 in TensorComponent + FoldedDimensions = 0x08000000, // For example, Dim0xDim1 in TensorComponent +}; + +/** Mask to retrieve the component index (for example, 1 for stride1, 2 for stride2, or 1 and 2 for Dim1xDim2). + * + * The 4 least significant half-bytes (nibbles) of the @ref TensorComponent are used to retrieve the specific component index. + * TensorComponent = | i7 | i6 | i5 | i4 | i3 | i2 | i1 | i0 |, where i7,...i0 are the nibbles + * of the TensorComponent hexadecimal number. i0, i1, i2 and i3 are reserved to the component index. + * + * In particular: + * + * -# i0: reserved to the first folded dimension component index + * -# i1: reserved to the second folded dimension component index + * -# i2: reserved to the third folded dimension component index + * -# i3: reserved to the fourth folded dimension component index + * + * Therefore, if there are no folded dimensions (dimensions and strides), only i0 is used. + * Instead, if there are two folded dimensions, only i0 and i1 are used. + * + * The component index is stored with the corresponding hexadecimal number + 1, + * hence the component index 0 is represented as 1, while the component index 3 is represented as 4. + */ +enum class TensorComponentIndexBitmask : uint32_t +{ + All = 0x0000ffff, // All nibbles reserved to the tensor component index + Index0 = 0x0000000f, // Folded dimension 0 + Index1 = 0x000000f0, // Folded dimension 1 + Index2 = 0x00000f00, // Folded dimension 2 + Index3 = 0x0000f000 // Folded dimension 3 +}; + +/** The maximum number of folded dimensions. */ +constexpr int tensor_component_index_max_count = 4; + +} // namespace ckw + +#endif // CKW_SRC_TYPES_TENSORCOMPONENTTYPE_H diff --git a/compute_kernel_writer/validation/Validation.cpp b/compute_kernel_writer/validation/Validation.cpp new file mode 100644 index 0000000000..4fbd1eacda --- /dev/null +++ b/compute_kernel_writer/validation/Validation.cpp @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "validation/tests/CLConstantTileTest.hpp" +#include "validation/tests/CLKernelWriterAssignTest.h" +#include "validation/tests/CLKernelWriterBinaryOpTest.h" +#include "validation/tests/CLKernelWriterCastTest.h" +#include "validation/tests/CLKernelWriterCommentTest.h" +#include "validation/tests/CLKernelWriterDeclareConstantTileTest.h" +#include "validation/tests/CLKernelWriterDeclareTensorTest.h" +#include "validation/tests/CLKernelWriterDeclareTileTest.h" +#include "validation/tests/CLKernelWriterForTest.h" +#include "validation/tests/CLKernelWriterGetGlobalIdTest.h" +#include "validation/tests/CLKernelWriterIfTest.h" +#include "validation/tests/CLKernelWriterOpLoadIndirectTest.h" +#include "validation/tests/CLKernelWriterOpLoadStoreTest.h" +#include "validation/tests/CLKernelWriterPrintTest.h" +#include "validation/tests/CLKernelWriterReturnTest.h" +#include "validation/tests/CLKernelWriterSubTileTest.h" +#include "validation/tests/CLKernelWriterTernaryOpTest.h" +#include "validation/tests/CLKernelWriterUnaryExpressionTest.h" +#include "validation/tests/CLTensorArgumentTest.h" +#include "validation/tests/CLTileTest.hpp" +#include "validation/tests/TensorBitMaskTest.h" +#include "validation/tests/UtilsTest.h" + +#include <cstdint> +#include <memory> +#include <vector> + +using namespace ckw; + +/** Main test program + */ +int32_t main() +{ + std::vector<ITest *> tests; + + // Add your test here + const auto test0 = std::make_unique<UtilsTest>(); + const auto test1 = std::make_unique<TensorBitMaskTrueTest>(); + const auto test2 = std::make_unique<TensorBitMaskFalseTest>(); + tests.push_back(test0.get()); + tests.push_back(test1.get()); + tests.push_back(test2.get()); + +#ifdef COMPUTE_KERNEL_WRITER_OPENCL_ENABLED + const auto test3 = std::make_unique<CLTileInternalVariableNamesTest>(); + const auto test4 = std::make_unique<CLTileInternalNumVariablesTest>(); + const auto test5 = std::make_unique<CLTileAccessScalarVariableTest>(); + const auto test6 = std::make_unique<CLTileAccessScalarVariableBroadcastXTest>(); + const auto test7 = std::make_unique<CLTileAccessScalarVariableBroadcastYTest>(); + const auto test8 = std::make_unique<CLTileAccessVectorVariablesTest>(); + const auto test9 = std::make_unique<CLTileAccessSubVectorVariablesTest>(); + const auto test10 = std::make_unique<CLConstantTileInternalValuesTest>(); + const auto test11 = std::make_unique<CLConstantTileAccessScalarVariableBroadcastXTest>(); + const auto test12 = std::make_unique<CLConstantTileAccessScalarVariableBroadcastYTest>(); + const auto test13 = std::make_unique<CLConstantTileAccessVectorVariablesTest>(); + const auto test14 = std::make_unique<CLConstantTileAccessSubVectorVariablesTest>(); +#ifdef COMPUTE_KERNEL_WRITER_DEBUG_ENABLED + const auto test15 = std::make_unique<CLKernelWriterCommentTest>(); +#endif /* COMPUTE_KERNEL_WRITER_DEBUG_ENABLED */ + const auto test16 = std::make_unique<CLKernelWriterDeclareTileTest>(); + const auto test17 = std::make_unique<CLTensorArgumentComponentNamesTest>(); + const auto test18 = std::make_unique<CLTensorArgumentStorageNamesTest>(); + const auto test19 = std::make_unique<CLTensorArgumentComponentValuesTest>(); + const auto test20 = std::make_unique<CLTensorArgumentComponentsUsedPassByValueFalseTest>(); + const auto test21 = std::make_unique<CLTensorArgumentComponentsUsedPassByValueTrueTest>(); + const auto test22 = std::make_unique<CLTensorArgumentStoragesUsedTest>(); + const auto test23 = std::make_unique<CLTensorArgumentComponentsUsedPassByValueTrueDynamicDimTrueTest>(); + const auto test24 = std::make_unique<CLKernelWriterDeclareTensorTest>(); + const auto test25 = std::make_unique<CLKernelWriterOpLoadStoreTest>(); + const auto test26 = std::make_unique<CLKernelWriterAssignTest>(); + const auto test27 = std::make_unique<CLKernelWriterCastTest>(); + const auto test28 = std::make_unique<CLKernelWriterUnaryExpressionTest>(); + const auto test29 = std::make_unique<CLKernelWriterBinaryOpTest>(); + const auto test30 = std::make_unique<CLKernelWriterTernaryOpTest>(); + const auto test31 = std::make_unique<CLKernelWriterDeclareConstantTileTest>(); + const auto test32 = std::make_unique<CLKernelWriterIfTest>(); + const auto test33 = std::make_unique<CLKernelWriterForTest>(); + const auto test34 = std::make_unique<CLKernelWriterReturnTest>(); + const auto test35 = std::make_unique<CLKernelWriterGetGlobalIdTest>(); + const auto test36 = std::make_unique<CLKernelWriterPrintTest>(); + const auto test37 = std::make_unique<CLKernelWriterOpLoadIndirectTest>(); + const auto test38 = std::make_unique<CLKernelWriterSubTileTest>(); + + tests.push_back(test3.get()); + tests.push_back(test4.get()); + tests.push_back(test5.get()); + tests.push_back(test6.get()); + tests.push_back(test7.get()); + tests.push_back(test8.get()); + tests.push_back(test9.get()); + tests.push_back(test10.get()); + tests.push_back(test11.get()); + tests.push_back(test12.get()); + tests.push_back(test13.get()); + tests.push_back(test14.get()); +#ifdef COMPUTE_KERNEL_WRITER_DEBUG_ENABLED + tests.push_back(test15.get()); +#endif /* COMPUTE_KERNEL_WRITER_DEBUG_ENABLED */ + tests.push_back(test16.get()); + tests.push_back(test17.get()); + tests.push_back(test18.get()); + tests.push_back(test19.get()); + tests.push_back(test20.get()); + tests.push_back(test21.get()); + tests.push_back(test22.get()); + tests.push_back(test23.get()); + tests.push_back(test24.get()); + tests.push_back(test25.get()); + tests.push_back(test26.get()); + tests.push_back(test27.get()); + tests.push_back(test28.get()); + tests.push_back(test29.get()); + tests.push_back(test30.get()); + tests.push_back(test31.get()); + tests.push_back(test32.get()); + tests.push_back(test33.get()); + tests.push_back(test34.get()); + tests.push_back(test35.get()); + tests.push_back(test36.get()); + tests.push_back(test37.get()); + tests.push_back(test38.get()); +#endif /* COMPUTE_KERNEL_WRITER_OPENCL_ENABLED */ + + bool all_test_passed = true; + + for(auto &x : tests) + { + std::cout << x->name() << std::endl; + all_test_passed &= x->run(); + } + + if(all_test_passed == true) + { + std::cout << "All tests passed" << std::endl; + } + else + { + throw std::runtime_error("One or more tests failed"); + } + + return 0; +} diff --git a/compute_kernel_writer/validation/tests/CLConstantTileTest.hpp b/compute_kernel_writer/validation/tests/CLConstantTileTest.hpp new file mode 100644 index 0000000000..f10ad10146 --- /dev/null +++ b/compute_kernel_writer/validation/tests/CLConstantTileTest.hpp @@ -0,0 +1,371 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef CKW_TESTS_CLCONSTANTTILETEST_HPP +#define CKW_TESTS_CLCONSTANTTILETEST_HPP + +#include "common/Common.h" +#include "src/Helpers.h" +#include "src/cl/CLHelpers.h" +#include "src/cl/CLTile.h" + +#include <random> +#include <string> +#include <vector> + +namespace ckw +{ +class CLConstantTileInternalValuesTest : public ITest +{ +public: + CLConstantTileInternalValuesTest() + { + _values.push_back({ { "1.2", "3.5" }, + { "4.2", "1.3" } }); + _values.push_back({ { "1.2" } }); + _values.push_back({ { "1.2", "6.9" } }); + } + + bool run() override + { + // The status of this variable can change in VALIDATE_TEST() + bool all_tests_passed = true; + + int32_t test_idx = 0; + for(const auto &test : _values) + { + const CLTile tile(test, DataType::Fp16); + const auto vars = tile.all(); + const int32_t num_vars = vars.size(); + const int32_t width = tile.info().width(); + + for(int32_t y = 0; y < num_vars; ++y) + { + const int32_t col = y % width; + const int32_t row = y / width; + const std::string expected_var_name = "((half)(" + test[row][col] + "))"; + const std::string actual_var_name = vars[y].str; + VALIDATE_TEST(actual_var_name.compare(expected_var_name) == 0, all_tests_passed, test_idx++); + } + } + return all_tests_passed; + } + + std::string name() override + { + return "CLConstantTileInternalValuesTest"; + } + +private: + std::vector<TileContainer> _values{}; +}; + +class CLConstantTileAccessScalarVariableBroadcastXTest : public ITest +{ +public: + const std::string tile_name = "src"; + const int32_t height = 8; + const DataType dt = DataType::Fp16; + + CLConstantTileAccessScalarVariableBroadcastXTest() + { + _width.push_back(1); + _width.push_back(2); + _width.push_back(3); + + _x_coord.push_back(4); + _x_coord.push_back(5); + _x_coord.push_back(6); + + _y_coord.push_back(1); + _y_coord.push_back(3); + _y_coord.push_back(2); + } + + bool run() override + { + VALIDATE_ON_MSG(_width.size() == _y_coord.size(), "The number of widths and y-coords does not match"); + VALIDATE_ON_MSG(_x_coord.size() == _y_coord.size(), "The number of x-coords and y-coords does not match"); + + // The status of this variable can change in VALIDATE_TEST() + bool all_tests_passed = true; + + const size_t num_coords = _x_coord.size(); + + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_real_distribution<> dist(-1, 1); + + int32_t test_idx = 0; + for(size_t i = 0; i < num_coords; ++i) + { + const int32_t width = _width[i]; + const int32_t x_coord = _x_coord[i]; + const int32_t y_coord = _y_coord[i]; + + const int32_t x_coord_clamped = clamp(x_coord, static_cast<int32_t>(0), width - 1); + + TileContainer container = TileContainer(height, std::vector<std::string>(width)); + + for(int32_t row = 0; row < height; ++row) + { + for(int32_t col = 0; col < width; ++col) + { + container[row][col] = std::to_string(dist(gen)); + } + } + + const CLTile tile(container, dt); + + const TileVariable var = tile.scalar(y_coord, x_coord); + + const std::string actual_var_name = var.str; + const std::string expected_var_name = "((half)(" + container[y_coord][x_coord_clamped] + "))"; + + VALIDATE_TEST(actual_var_name.compare(expected_var_name) == 0, all_tests_passed, test_idx++); + } + return all_tests_passed; + } + + std::string name() override + { + return "CLConstantTileAccessScalarVariableBroadcastXTest"; + } + +private: + std::vector<int32_t> _width{}; + std::vector<int32_t> _x_coord{}; + std::vector<int32_t> _y_coord{}; +}; + +class CLConstantTileAccessScalarVariableBroadcastYTest : public ITest +{ +public: + const std::string tile_name = "src"; + const int32_t width = 8; + const DataType dt = DataType::Fp16; + + CLConstantTileAccessScalarVariableBroadcastYTest() + { + _height.push_back(1); + _height.push_back(2); + _height.push_back(3); + + _x_coord.push_back(4); + _x_coord.push_back(5); + _x_coord.push_back(6); + + _y_coord.push_back(3); + _y_coord.push_back(4); + _y_coord.push_back(5); + } + + bool run() override + { + VALIDATE_ON_MSG(_height.size() == _y_coord.size(), "The number of widths and y-coords does not match"); + VALIDATE_ON_MSG(_x_coord.size() == _y_coord.size(), "The number of x-coords and y-coords does not match"); + + // The status of this variable can change in VALIDATE_TEST() + bool all_tests_passed = true; + + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_real_distribution<> dist(-1, 1); + + const size_t num_coords = _x_coord.size(); + + int32_t test_idx = 0; + for(size_t i = 0; i < num_coords; ++i) + { + const int32_t height = _height[i]; + const int32_t x_coord = _x_coord[i]; + const int32_t y_coord = _y_coord[i]; + + const int32_t y_coord_clamped = clamp(y_coord, static_cast<int32_t>(0), height - 1); + + TileContainer container = TileContainer(height, std::vector<std::string>(width)); + + for(int32_t row = 0; row < height; ++row) + { + for(int32_t col = 0; col < width; ++col) + { + container[row][col] = std::to_string(dist(gen)); + } + } + + const CLTile tile(container, dt); + + const TileVariable var = tile.scalar(y_coord, x_coord); + + const std::string actual_var_name = var.str; + const std::string expected_var_name = "((half)(" + container[y_coord_clamped][x_coord] + "))"; + + VALIDATE_TEST(actual_var_name.compare(expected_var_name) == 0, all_tests_passed, test_idx++); + } + return all_tests_passed; + } + + std::string name() override + { + return "CLConstantTileAccessScalarVariableBroadcastYTest"; + } + +private: + std::vector<int32_t> _height{}; + std::vector<int32_t> _x_coord{}; + std::vector<int32_t> _y_coord{}; +}; + +class CLConstantTileAccessVectorVariablesTest : public ITest +{ +public: + const DataType dt = DataType::Fp16; + + CLConstantTileAccessVectorVariablesTest() + { + _values.push_back({ { "1.2", "3.5" }, + { "4.2", "1.3" } }); + _values.push_back({ { "1.2" } }); + // Mix variable names and values + _values.push_back({ { "1.2", "acc", "8.7", "9.3", "ratio", "2.9", "1.7", "0.3" } }); + } + + bool run() override + { + // The status of this variable can change in VALIDATE_TEST() + bool all_tests_passed = true; + + int32_t test_idx = 0; + + for(const auto &test : _values) + { + const CLTile tile(test, dt); + const int32_t width = tile.info().width(); + const int32_t height = tile.info().height(); + + for(int32_t row = 0; row < height; ++row) + { + std::string expected_var_name = "(("; + expected_var_name += cl_get_variable_datatype_as_string(dt, width); + expected_var_name += ")("; + + int32_t col = 0; + for(; col < width - 1; ++col) + { + expected_var_name += test[row][col]; + expected_var_name += ", "; + } + + expected_var_name += test[row][col]; + expected_var_name += "))"; + + const std::string actual_var_name = tile.vector(row).str; + VALIDATE_TEST(actual_var_name.compare(expected_var_name) == 0, all_tests_passed, test_idx++); + } + } + return all_tests_passed; + } + + std::string name() override + { + return "CLConstantTileAccessVectorVariablesTest"; + } + +private: + std::vector<TileContainer> _values{}; +}; + +class CLConstantTileAccessSubVectorVariablesTest : public ITest +{ +public: + const DataType dt = DataType::Fp16; + + CLConstantTileAccessSubVectorVariablesTest() + { + _values.push_back({ { "1.2", "acc", "8.7", "9.3", "ratio", "2.9", "1.7", "0.3" } }); + _subwidths.push_back(1); + _subwidths.push_back(2); + _subwidths.push_back(3); + _subwidths.push_back(4); + _offsets.push_back(1); + _offsets.push_back(3); + _offsets.push_back(4); + } + + bool run() override + { + // The status of this variable can change in VALIDATE_TEST() + bool all_tests_passed = true; + + size_t test_idx = 0; + + for(auto &test : _values) + { + for(auto &col_start : _offsets) + { + for(auto &subwidth : _subwidths) + { + const CLTile tile(test, dt); + const int32_t height = tile.info().height(); + + for(int32_t row = 0; row < height; ++row) + { + std::string expected_var_name = "(("; + expected_var_name += cl_get_variable_datatype_as_string(dt, subwidth); + expected_var_name += ")("; + + int32_t col = col_start; + for(; col < subwidth - 1; ++col) + { + expected_var_name += test[row][col]; + expected_var_name += ", "; + } + + expected_var_name += test[row][col]; + expected_var_name += "))"; + + const std::string actual_var_name = tile.vector(row, col_start, subwidth).str; + VALIDATE_TEST(actual_var_name.compare(expected_var_name) == 0, all_tests_passed, + test_idx++); + } + } + } + } + return all_tests_passed; + } + + std::string name() override + { + return "CLConstantTileAccessSubVectorVariablesTest"; + } + +private: + std::vector<TileContainer> _values{}; + std::vector<int32_t> _subwidths{}; + std::vector<int32_t> _offsets{}; +}; + +} // namespace ckw + +#endif // CKW_TESTS_CLCONSTANTTILETEST_HPP diff --git a/compute_kernel_writer/validation/tests/CLKernelWriterAssignTest.h b/compute_kernel_writer/validation/tests/CLKernelWriterAssignTest.h new file mode 100644 index 0000000000..f32f797a01 --- /dev/null +++ b/compute_kernel_writer/validation/tests/CLKernelWriterAssignTest.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef CKW_VALIDATION_TESTS_CLKERNELWRITERASSIGNTEST_H +#define CKW_VALIDATION_TESTS_CLKERNELWRITERASSIGNTEST_H + +#include "ckw/TileInfo.h" +#include "ckw/types/DataType.h" +#include "src/cl/CLKernelWriter.h" +#include "validation/tests/common/Common.h" +#include "validation/tests/common/KernelWriterInterceptor.h" + +#include <cstdint> +#include <vector> + +namespace ckw +{ + +class CLKernelWriterAssignTest : public ITest +{ +public: + CLKernelWriterAssignTest() + { + _tests.push_back({ 1, 1, 1, 1, DataType::Fp32, "G0__dst = G0__src;\n" }); // Scalar. + + _tests.push_back({ 1, 3, 1, 3, DataType::Fp16, "G0__dst = G0__src;\n" }); // Whole vector. + + _tests.push_back({ 2, 4, 2, 4, DataType::Int8, "G0__dst__0 = G0__src__0;\nG0__dst__1 = G0__src__1;\n" }); // Whole tile. + + _tests.push_back({ 2, 3, 1, 3, DataType::Uint8, "G0__dst__0 = G0__src;\nG0__dst__1 = G0__src;\n" }); // Y-dimension broadcast. + + _tests.push_back({ 2, 4, 2, 1, DataType::Fp32, "G0__dst__0 = (float4)G0__src__0;\nG0__dst__1 = (float4)G0__src__1;\n" }); // X-dimension broadcast. + + _tests.push_back({ 2, 3, 1, 1, DataType::Fp16, "G0__dst__0 = (half3)G0__src;\nG0__dst__1 = (half3)G0__src;\n" }); // X and y dimension broadcast. + } + + bool run() override + { + int32_t test_no = 0; + bool all_tests_passed = true; + + for(const auto &test : _tests) + { + KernelWriterInterceptor<CLKernelWriter> writer; + + auto dst = writer.declare_tile("dst", TileInfo(test.data_type, test.dst_height, test.dst_width)); + auto src = writer.declare_tile("src", TileInfo(test.data_type, test.src_height, test.src_width)); + + writer.start_capture_code(); + + writer.op_assign(dst, src); + + VALIDATE_TEST(writer.check_added_code(test.expected_code), all_tests_passed, test_no++); + } + + return all_tests_passed; + } + + std::string name() override + { + return "CLKernelWriterAssignTest"; + } + +private: + struct TestInfo + { + int32_t dst_height; + int32_t dst_width; + int32_t src_height; + int32_t src_width; + DataType data_type; + std::string expected_code; + }; + + std::vector<TestInfo> _tests{}; +}; + +} // namespace ckw + +#endif // CKW_VALIDATION_TESTS_CLKERNELWRITERASSIGNTEST_H diff --git a/compute_kernel_writer/validation/tests/CLKernelWriterBinaryOpTest.h b/compute_kernel_writer/validation/tests/CLKernelWriterBinaryOpTest.h new file mode 100644 index 0000000000..44a4df1ce1 --- /dev/null +++ b/compute_kernel_writer/validation/tests/CLKernelWriterBinaryOpTest.h @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef CKW_VALIDATION_TESTS_CLKERNELWRITERBINARYOPTEST_H +#define CKW_VALIDATION_TESTS_CLKERNELWRITERBINARYOPTEST_H + +#include "ckw/TileInfo.h" +#include "ckw/types/DataType.h" +#include "src/cl/CLKernelWriter.h" +#include "validation/tests/common/Common.h" +#include "validation/tests/common/KernelWriterInterceptor.h" + +#include <cstdint> +#include <vector> + +namespace ckw +{ + +class CLKernelWriterBinaryOpTest : public ITest +{ +public: + CLKernelWriterBinaryOpTest() + { + // dst_height, dst_width, dst_data_type, lhs_height, lhs_width, rhs_height, rhs_width, src_data_type, op, expected_code + _tests.push_back({ 1, 1, DataType::Fp32, 1, 1, 1, 1, DataType::Fp32, BinaryOp::Add, "G0__dst = G0__lhs + G0__rhs;\n" }); // Scalar. + + _tests.push_back({ 1, 3, DataType::Bool, 1, 3, 1, 3, DataType::Fp16, BinaryOp::Equal, "G0__dst = G0__lhs == G0__rhs;\n" }); // Whole vector. + + _tests.push_back({ 2, 4, DataType::Int8, 2, 4, 2, 4, DataType::Int8, BinaryOp::Min, "G0__dst__0 = min(G0__lhs__0, G0__rhs__0);\nG0__dst__1 = min(G0__lhs__1, G0__rhs__1);\n" }); // Whole tile. + + _tests.push_back({ 2, 3, DataType::Uint8, 1, 3, 2, 3, DataType::Uint8, BinaryOp::BitwiseXOR, "G0__dst__0 = G0__lhs ^ G0__rhs__0;\nG0__dst__1 = G0__lhs ^ G0__rhs__1;\n" }); // LHS y-dimension broadcast. + + _tests.push_back({ 2, 3, DataType::Bool, 2, 3, 1, 3, DataType::Fp32, BinaryOp::Less, "G0__dst__0 = G0__lhs__0 < G0__rhs;\nG0__dst__1 = G0__lhs__1 < G0__rhs;\n" }); // RHS y-dimension broadcast. + + _tests.push_back({ 2, 3, DataType::Fp16, 1, 3, 1, 3, DataType::Fp16, BinaryOp::Max, "G0__dst__0 = fmax(G0__lhs, G0__rhs);\nG0__dst__1 = fmax(G0__lhs, G0__rhs);\n" }); // LHS and RHS y-dimension broadcast. + + _tests.push_back({ 2, 4, DataType::Fp32, 2, 1, 2, 4, DataType::Fp32, BinaryOp::Div, "G0__dst__0 = (float4)G0__lhs__0 / G0__rhs__0;\nG0__dst__1 = (float4)G0__lhs__1 / G0__rhs__1;\n" }); // LHS x-dimension broadcast. + + _tests.push_back({ 2, 4, DataType::Fp16, 2, 4, 2, 1, DataType::Fp16, BinaryOp::Mod, "G0__dst__0 = G0__lhs__0 % (half4)G0__rhs__0;\nG0__dst__1 = G0__lhs__1 % (half4)G0__rhs__1;\n" }); // RHS x-dimension broadcast. + + _tests.push_back({ 2, 4, DataType::Bool, 2, 1, 2, 1, DataType::Fp32, BinaryOp::GreaterEqual, "G0__dst__0 = (float4)G0__lhs__0 >= (float4)G0__rhs__0;\nG0__dst__1 = (float4)G0__lhs__1 >= (float4)G0__rhs__1;\n" }); // LHS and RHS x-dimension broadcast. + + _tests.push_back({ 2, 2, DataType::Fp32, 2, 3, 2, 3, DataType::Fp32, BinaryOp::MatMul_Nt_T, + "G0__dst__0.s0 = fma(G0__lhs__0.s0, G0__rhs__0.s0, G0__dst__0.s0);\n" + "G0__dst__0.s0 = fma(G0__lhs__0.s1, G0__rhs__0.s1, G0__dst__0.s0);\n" + "G0__dst__0.s0 = fma(G0__lhs__0.s2, G0__rhs__0.s2, G0__dst__0.s0);\n" + "G0__dst__0.s1 = fma(G0__lhs__0.s0, G0__rhs__1.s0, G0__dst__0.s1);\n" + "G0__dst__0.s1 = fma(G0__lhs__0.s1, G0__rhs__1.s1, G0__dst__0.s1);\n" + "G0__dst__0.s1 = fma(G0__lhs__0.s2, G0__rhs__1.s2, G0__dst__0.s1);\n" + "G0__dst__1.s0 = fma(G0__lhs__1.s0, G0__rhs__0.s0, G0__dst__1.s0);\n" + "G0__dst__1.s0 = fma(G0__lhs__1.s1, G0__rhs__0.s1, G0__dst__1.s0);\n" + "G0__dst__1.s0 = fma(G0__lhs__1.s2, G0__rhs__0.s2, G0__dst__1.s0);\n" + "G0__dst__1.s1 = fma(G0__lhs__1.s0, G0__rhs__1.s0, G0__dst__1.s1);\n" + "G0__dst__1.s1 = fma(G0__lhs__1.s1, G0__rhs__1.s1, G0__dst__1.s1);\n" + "G0__dst__1.s1 = fma(G0__lhs__1.s2, G0__rhs__1.s2, G0__dst__1.s1);\n" }); + } + + bool run() override + { + int32_t test_no = 0; + bool all_tests_passed = true; + + for(const auto &test : _tests) + { + KernelWriterInterceptor<CLKernelWriter> writer; + + auto dst = writer.declare_tile("dst", TileInfo(test.dst_data_type, test.dst_height, test.dst_width)); + auto lhs = writer.declare_tile("lhs", TileInfo(test.src_data_type, test.lhs_height, test.lhs_width)); + auto rhs = writer.declare_tile("rhs", TileInfo(test.src_data_type, test.rhs_height, test.rhs_width)); + + writer.start_capture_code(); + + writer.op_binary(dst, test.op, lhs, rhs); + + VALIDATE_TEST(writer.check_added_code(test.expected_code), all_tests_passed, test_no++); + } + + return all_tests_passed; + } + + std::string name() override + { + return "CLKernelWriterBinaryOpTest"; + } + +private: + struct TestInfo + { + int32_t dst_height; + int32_t dst_width; + DataType dst_data_type; + int32_t lhs_height; + int32_t lhs_width; + int32_t rhs_height; + int32_t rhs_width; + DataType src_data_type; + BinaryOp op; + std::string expected_code; + }; + + std::vector<TestInfo> _tests{}; +}; + +} // namespace ckw + +#endif // CKW_VALIDATION_TESTS_CLKERNELWRITERBINARYOPTEST_H diff --git a/compute_kernel_writer/validation/tests/CLKernelWriterCastTest.h b/compute_kernel_writer/validation/tests/CLKernelWriterCastTest.h new file mode 100644 index 0000000000..a185cce545 --- /dev/null +++ b/compute_kernel_writer/validation/tests/CLKernelWriterCastTest.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef CKW_VALIDATION_TESTS_CLKERNELWRITERCASTTEST_H +#define CKW_VALIDATION_TESTS_CLKERNELWRITERCASTTEST_H + +#include "ckw/TileInfo.h" +#include "ckw/types/ConvertPolicy.h" +#include "ckw/types/DataType.h" +#include "src/cl/CLKernelWriter.h" +#include "validation/tests/common/Common.h" +#include "validation/tests/common/KernelWriterInterceptor.h" + +#include <cstdint> +#include <vector> + +namespace ckw +{ + +class CLKernelWriterCastTest : public ITest +{ +public: + CLKernelWriterCastTest() + { + _tests.push_back({ 1, 1, DataType::Fp16, 1, 1, DataType::Fp32, ConvertPolicy::None, "G0__dst = convert_half(G0__src);\n" }); // Scalar. + + _tests.push_back({ 1, 3, DataType::Int32, 1, 3, DataType::Fp16, ConvertPolicy::Saturate, "G0__dst = convert_int3_sat(G0__src);\n" }); // Whole vector. + + _tests.push_back({ 2, 4, DataType::Uint16, 2, 4, DataType::Int8, ConvertPolicy::Saturate, "G0__dst__0 = convert_ushort4_sat(G0__src__0);\nG0__dst__1 = convert_ushort4_sat(G0__src__1);\n" }); // Whole tile. + + _tests.push_back({ 2, 3, DataType::Int8, 1, 3, DataType::Uint8, ConvertPolicy::None, "G0__dst__0 = convert_char3(G0__src);\nG0__dst__1 = convert_char3(G0__src);\n" }); // Y-dimension broadcast. + + _tests.push_back({ 2, 4, DataType::Fp16, 2, 1, DataType::Fp32, ConvertPolicy::None, "G0__dst__0 = (half4)convert_half(G0__src__0);\nG0__dst__1 = (half4)convert_half(G0__src__1);\n" }); // X-dimension broadcast. + + _tests.push_back({ 2, 3, DataType::Fp32, 1, 1, DataType::Fp16, ConvertPolicy::None, "G0__dst__0 = (float3)convert_float(G0__src);\nG0__dst__1 = (float3)convert_float(G0__src);\n" }); // X and y dimension broadcast. + } + + bool run() override + { + int32_t test_no = 0; + bool all_tests_passed = true; + + for(const auto &test : _tests) + { + KernelWriterInterceptor<CLKernelWriter> writer; + + auto dst = writer.declare_tile("dst", TileInfo(test.dst_data_type, test.dst_height, test.dst_width)); + auto src = writer.declare_tile("src", TileInfo(test.src_data_type, test.src_height, test.src_width)); + + writer.start_capture_code(); + + writer.op_cast(dst, src, test.policy); + + VALIDATE_TEST(writer.check_added_code(test.expected_code), all_tests_passed, test_no++); + } + + return all_tests_passed; + } + + std::string name() override + { + return "CLKernelWriterCastTest"; + } + +private: + struct TestInfo + { + int32_t dst_height; + int32_t dst_width; + DataType dst_data_type; + int32_t src_height; + int32_t src_width; + DataType src_data_type; + ConvertPolicy policy; + std::string expected_code; + }; + + std::vector<TestInfo> _tests{}; +}; + +} // namespace ckw + +#endif // CKW_VALIDATION_TESTS_CLKERNELWRITERCASTTEST_H diff --git a/compute_kernel_writer/validation/tests/CLKernelWriterCommentTest.h b/compute_kernel_writer/validation/tests/CLKernelWriterCommentTest.h new file mode 100644 index 0000000000..b36c3905ec --- /dev/null +++ b/compute_kernel_writer/validation/tests/CLKernelWriterCommentTest.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef CKW_VALIDATION_TESTS_CLKERNELWRITERCOMMENTTEST_H +#define CKW_VALIDATION_TESTS_CLKERNELWRITERCOMMENTTEST_H + +#include "src/cl/CLKernelWriter.h" +#include "validation/tests/common/Common.h" +#include "validation/tests/common/KernelWriterInterceptor.h" + +namespace ckw +{ + +class CLKernelWriterCommentTest : public ITest +{ +public: + CLKernelWriterCommentTest() + { + } + + bool run() override + { + bool all_tests_passed = true; + + KernelWriterInterceptor<CLKernelWriter> writer; + + writer.op_comment("previous code"); + + writer.start_capture_code(); + + writer.op_comment("code under test 0"); + writer.op_comment("code under test 1"); + +#ifdef COMPUTE_KERNEL_WRITER_DEBUG_ENABLED + constexpr auto expected_code = "// code under test 0\n// code under test 1\n"; +#else // COMPUTE_KERNEL_WRITER_DEBUG_ENABLED + constexpr auto expected_code = ""; +#endif // COMPUTE_KERNEL_WRITER_DEBUG_ENABLED + + VALIDATE_TEST(writer.check_added_code(expected_code), all_tests_passed, 0); + + return all_tests_passed; + } + + std::string name() override + { + return "CLKernelWriterCommentTest"; + } +}; + +} // namespace ckw + +#endif // CKW_VALIDATION_TESTS_CLKERNELWRITERCOMMENTTEST_H diff --git a/compute_kernel_writer/validation/tests/CLKernelWriterDeclareConstantTileTest.h b/compute_kernel_writer/validation/tests/CLKernelWriterDeclareConstantTileTest.h new file mode 100644 index 0000000000..661a8328e8 --- /dev/null +++ b/compute_kernel_writer/validation/tests/CLKernelWriterDeclareConstantTileTest.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef CKW_VALIDATION_TESTS_CLKERNELWRITERDECLARECONSTANTTILETEST_H +#define CKW_VALIDATION_TESTS_CLKERNELWRITERDECLARECONSTANTTILETEST_H + +#include "ckw/TileInfo.h" +#include "ckw/types/ConstantData.h" +#include "ckw/types/DataType.h" +#include "src/cl/CLKernelWriter.h" +#include "validation/tests/common/KernelWriterInterceptor.h" +#include "validation/tests/common/Common.h" + +#include <string> +#include <tuple> +#include <vector> + +namespace ckw +{ +class CLKernelWriterDeclareConstantTileTest : public ITest +{ + using TestConfig = std::tuple<ConstantData, DataType, int32_t, int32_t, std::string>; +public: + CLKernelWriterDeclareConstantTileTest() + { + _configs = { + // ConstantData, DataType, Height, Width + {ConstantData({{1}}, DataType::Int32), DataType::Int32, 1, 1, + "G0__tile = ((int)(1));\n"}, + {ConstantData({{1U}}, DataType::Uint32), DataType::Uint32, 1, 1, + "G0__tile = ((uint)(1));\n"}, + {ConstantData({{1, 2}}, DataType::Int8), DataType::Int8, 1, 2, + "G0__tile = ((char2)(1, 2));\n"}, + {ConstantData({{1, -2}, {-3, 4}}, DataType::Int32), DataType::Int32, 2, 2, + "G0__tile__0 = ((int2)(1, -2));\nG0__tile__1 = ((int2)(-3, 4));\n"}, + {ConstantData({{1.0f, -2.0f}}, DataType::Fp16), DataType::Fp16, 1, 2, + "G0__tile = ((half2)(1.000000000e+00, -2.000000000e+00));\n"}, + {ConstantData({{/* FLT_MAX */ 340282346638528859811704183484516925440.0f, -2.0f, 3.0f}}, DataType::Fp32), DataType::Fp32, 1, 3, + "G0__tile = ((float3)(3.402823466e+38, -2.000000000e+00, 3.000000000e+00));\n"}, + {ConstantData({{1.0f, -1e-20f, 2e-20f, /* FLT_EPS */ 1.1920928955078125e-7f}}, DataType::Fp32), DataType::Fp32, 1, 4, + "G0__tile = ((float4)(1.000000000e+00, -9.999999683e-21, 1.999999937e-20, 1.192092896e-07));\n"}, + {ConstantData({{0.5f, 2.1e-30f, /* FLT_MIN */ 1.175494350822287507969e-38f}}, DataType::Fp32), DataType::Fp32, 1, 3, + "G0__tile = ((float3)(5.000000000e-01, 2.099999969e-30, 1.175494351e-38));\n"}, + {ConstantData({{true}, {false}, {false}}, DataType::Bool), DataType::Bool, 3, 1, + "G0__tile__0 = ((bool)(1));\nG0__tile__1 = ((bool)(0));\nG0__tile__2 = ((bool)(0));\n"} + }; + } + + bool run() override + { + bool all_tests_passed = true; + int test_idx = 0; + + for(TestConfig _config: _configs) + { + KernelWriterInterceptor<CLKernelWriter> writer; + const ConstantData const_data = std::get<0>(_config); + const DataType data_type = std::get<1>(_config); + const size_t height = std::get<2>(_config); + const size_t width = std::get<3>(_config); + const std::string expected_code = std::get<4>(_config); + + TileOperand tile = writer.declare_tile("tile", TileInfo(data_type, height, width)); + writer.start_capture_code(); + TileOperand const_tile = writer.declare_constant_tile(const_data); + writer.op_assign(tile, const_tile); + + VALIDATE_TEST(writer.check_added_code(expected_code), all_tests_passed, test_idx++); + } + + return all_tests_passed; + } + + std::string name() override + { + return "CLKernelWriterDeclareConstantTileTest"; + } + +private: + std::vector<TestConfig> _configs {}; +}; + +} // namespace ckw + +#endif // CKW_VALIDATION_TESTS_CLKERNELWRITERDECLARECONSTANTTILETEST_H diff --git a/compute_kernel_writer/validation/tests/CLKernelWriterDeclareTensorTest.h b/compute_kernel_writer/validation/tests/CLKernelWriterDeclareTensorTest.h new file mode 100644 index 0000000000..855c747f13 --- /dev/null +++ b/compute_kernel_writer/validation/tests/CLKernelWriterDeclareTensorTest.h @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef CKW_VALIDATION_TESTS_CLKERNELWRITERDECLARETENSORTEST_H +#define CKW_VALIDATION_TESTS_CLKERNELWRITERDECLARETENSORTEST_H + +#include "ckw/Error.h" +#include "ckw/Kernel.h" +#include "ckw/KernelArgument.h" +#include "ckw/TensorInfo.h" +#include "ckw/types/TensorComponentType.h" +#include "ckw/types/TensorDataLayout.h" +#include "src/cl/CLKernelWriter.h" +#include "validation/tests/common/Common.h" + +namespace ckw +{ + +class CLKernelWriterDeclareTensorTest : public ITest +{ +public: + CLKernelWriterDeclareTensorTest() + { + } + + std::string name() override + { + return "CLKernelWriterDeclareTensorTest"; + } + + bool run() override + { + auto all_tests_passed = true; + + CLKernelWriter writer; + + auto src = writer.declare_tensor_argument("src", TensorInfo(DataType::Fp32, TensorShape{ 2, 3, 4, 5 }, TensorDataLayout::Nhwc, 0)); + auto dst = writer.declare_tensor_argument("dst", TensorInfo(DataType::Fp32, TensorShape{ 6, 7, 8, 9 }, TensorDataLayout::Nhwc, 1)); + + auto src_dim0 = src.dim0(); + auto src_stride2 = src.stride2(); + auto src_offset_element = src.offset_first_element_in_bytes(); + + auto dst_dim1 = dst.dim0(); + + auto src_dim0_again = src.dim0(); + + CKW_UNUSED(src_dim0, src_stride2, src_offset_element, dst_dim1, src_dim0_again); + + const auto kernel = writer.emit_kernel("test_kernel"); + + const std::string expected_code = + "__kernel void test_kernel\n" + "(\n" + "int G0__src_dim0,\n" + "int G0__src_stride2,\n" + "int G0__src_offset_first_element,\n" + "int G0__dst_dim0\n" + ")\n" + "{\n" + "}\n"; + + std::string actual_code = kernel->source_code(); + + std::size_t pos = actual_code.find("__kernel"); + + if (pos != std::string::npos) + { + // Remove text before "__kernel" + actual_code = actual_code.substr(pos); + } + + int test_id = 0; + VALIDATE_TEST(kernel->arguments().size() == 4, all_tests_passed, test_id++); + test_tensor_component_argument(kernel->arguments()[0], 0, TensorComponentType::Dim0, all_tests_passed, test_id); + test_tensor_component_argument(kernel->arguments()[1], 0, TensorComponentType::Stride2, all_tests_passed, test_id); + test_tensor_component_argument(kernel->arguments()[2], 0, TensorComponentType::OffsetFirstElement, all_tests_passed, test_id); + test_tensor_component_argument(kernel->arguments()[3], 1, TensorComponentType::Dim0, all_tests_passed, test_id); + VALIDATE_TEST(actual_code == expected_code, all_tests_passed, test_id++); + + return all_tests_passed; + } + + void test_tensor_component_argument(const KernelArgument &arg, int32_t tensor_id, TensorComponentType component_type, bool &all_tests_passed, int &test_id) + { + VALIDATE_TEST(arg.type() == KernelArgument::Type::TensorComponent, all_tests_passed, test_id++); + VALIDATE_TEST(arg.id() == tensor_id, all_tests_passed, test_id++); + VALIDATE_TEST(arg.tensor_component_type() == component_type, all_tests_passed, test_id++); + } +}; + +} // namespace ckw + +#endif // CKW_VALIDATION_TESTS_CLKERNELWRITERDECLARETENSORTEST_H diff --git a/compute_kernel_writer/validation/tests/CLKernelWriterDeclareTileTest.h b/compute_kernel_writer/validation/tests/CLKernelWriterDeclareTileTest.h new file mode 100644 index 0000000000..4f728bc1bf --- /dev/null +++ b/compute_kernel_writer/validation/tests/CLKernelWriterDeclareTileTest.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef CKW_VALIDATION_TESTS_CLKERNELWRITER_H +#define CKW_VALIDATION_TESTS_CLKERNELWRITER_H + +#include "ckw/TileInfo.h" +#include "ckw/types/DataType.h" +#include "src/cl/CLKernelWriter.h" +#include "validation/tests/common/KernelWriterInterceptor.h" +#include "validation/tests/common/Common.h" + +#include <vector> + +namespace ckw +{ + +using CLKernelWriterDeclareTileConfig = std::tuple<DataType, int32_t, int32_t, std::string>; + +class CLKernelWriterDeclareTileTest : public ITest +{ +public: + CLKernelWriterDeclareTileTest() + { + _configs = { + {DataType::Fp32, 4, 4, "float4 G0__a_tile"}, + {DataType::Uint8, 4, 1, "uchar G0__a_tile"}, + {DataType::Int8, 4, 2, "char2 G0__a_tile"}, + {DataType::Bool, 9, 3, "bool3 G0__a_tile"}, + {DataType::Fp16, 4, 16, "half16 G0__a_tile"}, + {DataType::Uint32, 1, 8, "uint8 G0__a_tile"}, + {DataType::Uint16, 2, 3, "ushort3 G0__a_tile"}, + }; + } + + bool run() override + { + bool all_tests_passed = true; + int32_t test_idx = 0; + + for(auto _config: _configs) + { + KernelWriterInterceptor<CLKernelWriter> writer; + writer.start_capture_code(); + + const DataType data_type = std::get<0>(_config); + const int32_t height = std::get<1>(_config); + const int32_t width = std::get<2>(_config); + const std::string prefix = std::get<3>(_config); + + // expected output + std::string expected_code = ""; + for(int32_t row = 0; row < height; ++row) + { + expected_code += prefix + ((height > 1) ? std::string("__") + std::to_string(row) : "") + ";\n"; + } + + TileInfo tile_info(data_type, height, width); + writer.declare_tile("a_tile", tile_info); + + VALIDATE_TEST(writer.check_added_code(expected_code), all_tests_passed, test_idx++); + } + + return all_tests_passed; + } + + std::string name() override + { + return "CLKernelWriterDeclareTileTest"; + } + +private: + std::vector<CLKernelWriterDeclareTileConfig> _configs {}; +}; + +} // namespace ckw + +#endif /* CKW_VALIDATION_TESTS_CLKERNELWRITER_H */ diff --git a/compute_kernel_writer/validation/tests/CLKernelWriterForTest.h b/compute_kernel_writer/validation/tests/CLKernelWriterForTest.h new file mode 100644 index 0000000000..beb39966b2 --- /dev/null +++ b/compute_kernel_writer/validation/tests/CLKernelWriterForTest.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef CKW_VALIDATION_TESTS_CLKERNELWRITERFORTEST_H +#define CKW_VALIDATION_TESTS_CLKERNELWRITERFORTEST_H + +#include "ckw/Error.h" +#include "ckw/TileInfo.h" +#include "ckw/types/Operators.h" +#include "src/cl/CLKernelWriter.h" +#include "validation/tests/common/Common.h" +#include "validation/tests/common/KernelWriterInterceptor.h" + +namespace ckw +{ + +class CLKernelWriterForTest : public ITest +{ +public: + CLKernelWriterForTest() + { + } + + bool run() override + { + bool all_tests_passed = true; + + KernelWriterInterceptor<CLKernelWriter> writer; + + auto idx = writer.declare_tile("idx", TileInfo(DataType::Int32, 1, 1)); + auto len = writer.declare_tile("len", TileInfo(DataType::Int32, 1, 1)); + auto addr = writer.declare_tile("addr", TileInfo(DataType::Int32, 1, 1)); + auto esize = writer.declare_tile("esize", TileInfo(DataType::Int32, 1, 1)); + + writer.start_capture_code(); + + writer.op_for_loop( + idx, BinaryOp::Less, len, addr, AssignmentOp::Increment, esize, + [&]() + { + auto tile = writer.declare_tile("tile", TileInfo(DataType::Fp32, 1, 3)); + CKW_UNUSED(tile); + }); + + constexpr auto expected_code = + "for (; G0__idx < G0__len; G0__addr += G0__esize)\n" + "{\n" + "float3 G1__tile;\n" + "}\n"; + + VALIDATE_TEST(writer.check_added_code(expected_code), all_tests_passed, 0); + + return all_tests_passed; + } + + std::string name() override + { + return "CLKernelWriterForTest"; + } +}; + +} // namespace ckw + +#endif // CKW_VALIDATION_TESTS_CLKERNELWRITERFORTEST_H diff --git a/compute_kernel_writer/validation/tests/CLKernelWriterGetGlobalIdTest.h b/compute_kernel_writer/validation/tests/CLKernelWriterGetGlobalIdTest.h new file mode 100644 index 0000000000..fa34b3f5df --- /dev/null +++ b/compute_kernel_writer/validation/tests/CLKernelWriterGetGlobalIdTest.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef CKW_VALIDATION_TESTS_CLKERNELWRITERGETGLOBALIDTEST_H +#define CKW_VALIDATION_TESTS_CLKERNELWRITERGETGLOBALIDTEST_H + +#include "ckw/TileInfo.h" +#include "src/cl/CLKernelWriter.h" +#include "validation/tests/common/Common.h" +#include "validation/tests/common/KernelWriterInterceptor.h" + +namespace ckw +{ + +class CLKernelWriterGetGlobalIdTest : public ITest +{ +public: + CLKernelWriterGetGlobalIdTest() + { + } + + bool run() override + { + bool all_tests_passed = true; + + KernelWriterInterceptor<CLKernelWriter> writer; + + auto gid = writer.declare_tile("gid", TileInfo(DataType::Int32)); + + writer.start_capture_code(); + + writer.op_get_global_id(gid, 0); + writer.op_get_global_id(gid, 1); + writer.op_get_global_id(gid, 2); + + constexpr auto expected_code = "G0__gid = get_global_id(0);\nG0__gid = get_global_id(1);\nG0__gid = get_global_id(2);\n"; + + VALIDATE_TEST(writer.check_added_code(expected_code), all_tests_passed, 0); + + return all_tests_passed; + } + + std::string name() override + { + return "CLKernelWriterGetGlobalIdTest"; + } +}; + +} // namespace ckw + +#endif // CKW_VALIDATION_TESTS_CLKERNELWRITERGETGLOBALIDTEST_H diff --git a/compute_kernel_writer/validation/tests/CLKernelWriterIfTest.h b/compute_kernel_writer/validation/tests/CLKernelWriterIfTest.h new file mode 100644 index 0000000000..3964bd76d4 --- /dev/null +++ b/compute_kernel_writer/validation/tests/CLKernelWriterIfTest.h @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef CKW_VALIDATION_TESTS_CLKERNELWRITERIFTEST_H +#define CKW_VALIDATION_TESTS_CLKERNELWRITERIFTEST_H + +#include "ckw/Error.h" +#include "ckw/TileInfo.h" +#include "src/cl/CLKernelWriter.h" +#include "validation/tests/common/Common.h" +#include "validation/tests/common/KernelWriterInterceptor.h" + +#include <cstdint> + +namespace ckw +{ + +class CLKernelWriterIfTest : public ITest +{ +public: + CLKernelWriterIfTest() + { + } + + bool run() override + { + int32_t test_no = 0; + bool all_tests_passed = true; + + KernelWriterInterceptor<CLKernelWriter> writer; + + auto lhs = writer.declare_tile("lhs", TileInfo(DataType::Fp32, 1, 1)); + auto rhs = writer.declare_tile("rhs", TileInfo(DataType::Fp32, 1, 1)); + + // The first if block. + { + writer.start_capture_code(); + + writer.op_if( + lhs, BinaryOp::Equal, rhs, + [&]() + { + auto tile = writer.declare_tile("tile", TileInfo(DataType::Fp16, 2, 3)); + CKW_UNUSED(tile); + }); + + constexpr auto expected_code = + "if (G0__lhs == G0__rhs)\n" + "{\n" + "half3 G1__tile__0;\n" + "half3 G1__tile__1;\n" + "}\n"; + + VALIDATE_TEST(writer.check_added_code(expected_code), all_tests_passed, test_no++); + } + + // The second if block - The ID space inside the if block should change. + { + writer.start_capture_code(); + + writer.op_if( + lhs, BinaryOp::Equal, rhs, + [&]() + { + auto tile = writer.declare_tile("tile", TileInfo(DataType::Fp16, 2, 3)); + CKW_UNUSED(tile); + }); + + constexpr auto expected_code = + "if (G0__lhs == G0__rhs)\n" + "{\n" + "half3 G2__tile__0;\n" + "half3 G2__tile__1;\n" + "}\n"; + + VALIDATE_TEST(writer.check_added_code(expected_code), all_tests_passed, test_no++); + } + + // The if-else block - The ID space in each block should change. + { + writer.start_capture_code(); + + writer.op_if( + lhs, BinaryOp::Equal, rhs, + [&]() + { + auto tile = writer.declare_tile("tile", TileInfo(DataType::Fp16, 2, 3)); + CKW_UNUSED(tile); + }); + writer.op_else( + [&]() + { + auto tile = writer.declare_tile("tile", TileInfo(DataType::Uint8, 1, 4)); + CKW_UNUSED(tile); + }); + + constexpr auto expected_code = + "if (G0__lhs == G0__rhs)\n" + "{\n" + "half3 G3__tile__0;\n" + "half3 G3__tile__1;\n" + "}\n" + "else\n" + "{\n" + "uchar4 G4__tile;\n" + "}\n"; + + VALIDATE_TEST(writer.check_added_code(expected_code), all_tests_passed, test_no++); + } + + // If-else if block. + { + writer.start_capture_code(); + + writer.op_if( + lhs, BinaryOp::Equal, rhs, + [&]() + { + auto tile = writer.declare_tile("tile", TileInfo(DataType::Fp32, 1, 3)); + CKW_UNUSED(tile); + }); + writer.op_else_if( + lhs, BinaryOp::Less, rhs, + [&]() + { + auto tile = writer.declare_tile("tile", TileInfo(DataType::Int8, 1, 4)); + CKW_UNUSED(tile); + }); + + constexpr auto expected_code = + "if (G0__lhs == G0__rhs)\n" + "{\n" + "float3 G5__tile;\n" + "}\n" + "else if (G0__lhs < G0__rhs)\n" + "{\n" + "char4 G6__tile;\n" + "}\n"; + + VALIDATE_TEST(writer.check_added_code(expected_code), all_tests_passed, test_no++); + } + + return all_tests_passed; + } + + std::string name() override + { + return "CLKernelWriterIfTest"; + } +}; + +} // namespace ckw + +#endif // CKW_VALIDATION_TESTS_CLKERNELWRITERIFTEST_H diff --git a/compute_kernel_writer/validation/tests/CLKernelWriterOpLoadIndirectTest.h b/compute_kernel_writer/validation/tests/CLKernelWriterOpLoadIndirectTest.h new file mode 100644 index 0000000000..dacf3cd435 --- /dev/null +++ b/compute_kernel_writer/validation/tests/CLKernelWriterOpLoadIndirectTest.h @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef CKW_VALIDATION_TESTS_CLKERNELWRITEROPLOADINDIRECTTEST_H +#define CKW_VALIDATION_TESTS_CLKERNELWRITEROPLOADINDIRECTTEST_H + +#include "ckw/TileInfo.h" +#include "ckw/types/DataType.h" +#include "ckw/TensorSampler.h" +#include "ckw/types/MemoryOperation.h" +#include "ckw/types/TensorSamplerTypes.h" +#include "src/cl/CLKernelWriter.h" +#include "validation/tests/common/KernelWriterInterceptor.h" +#include "validation/tests/common/Common.h" + +#include <vector> + +namespace ckw +{ + +class CLKernelWriterOpLoadIndirectTest : public ITest +{ +private: + using AddressModeX = TensorSamplerAddressModeX; + using AddressModeY = TensorSamplerAddressModeY; + using AddressModeZ = TensorSamplerAddressModeZ; + using Format = TensorSamplerFormat; + using Storage = TensorStorageType; + + struct Coordinates + { + Coordinates(std::string x, std::string y, std::string z, std::string batch) + : x(x), y(y), z(z), batch(batch) + { + } + + std::string x; + std::string y; + std::string z; + std::string batch; + }; + + struct SamplerData + { + SamplerData(Format format, AddressModeX mode_x, AddressModeY mode_y, AddressModeZ mode_z) + : format(format), mode_x(mode_x), mode_y(mode_y), mode_z(mode_z) + { + } + + Format format; + AddressModeX mode_x; + AddressModeY mode_y; + AddressModeZ mode_z; + }; + + using CLKernelWriterOpLoadIndirectConfig = std::tuple<TileInfo, TensorStorageType, SamplerData, Coordinates, std::string>; + +public: + CLKernelWriterOpLoadIndirectTest() + { + const std::string fp_2x3_tile = R"_( +G0__tile__0 = vload3(0, (__global float*)(G0__tensor_ptr + (G0__x) * sizeof(float) + (G0__indirect_addr__0) * G0__tensor_stride1 + (G0__z) * G0__tensor_stride2 + (G0__b) * G0__tensor_stride3)); +G0__tile__1 = vload3(0, (__global float*)(G0__tensor_ptr + (G0__x) * sizeof(float) + (G0__indirect_addr__1) * G0__tensor_stride1 + (G0__z) * G0__tensor_stride2 + (G0__b) * G0__tensor_stride3)); +)_"; + + const std::string half_2x4_yz_collapsed_y_clamped_to_border_max_only_image = R"_( +G0__tile__0 = read_imageh(G0__tensor_img2d, CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP | CLK_FILTER_NEAREST, (int2)((G0__x) >> 2, (G0__indirect_addr__0 + (G0__b) * G0__tensor_dim1xdim2 * 1))); +G0__tile__1 = read_imageh(G0__tensor_img2d, CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP | CLK_FILTER_NEAREST, (int2)((G0__x) >> 2, (G0__indirect_addr__1 + (G0__b) * G0__tensor_dim1xdim2 * 1))); +)_"; + + const std::string int_2x4_y_skip_less_than_zero = R"_( +if(G0__indirect_addr__0 >= 0) +{ +G0__tile__0 = vload4(0, (__global int*)(G0__tensor_ptr + (G0__x) * sizeof(int) + (G0__indirect_addr__0) * G0__tensor_stride1 + (G0__z) * G0__tensor_stride2 + (G0__b) * G0__tensor_stride3)); +} +if(G0__indirect_addr__1 >= 0) +{ +G0__tile__1 = vload4(0, (__global int*)(G0__tensor_ptr + (G0__x) * sizeof(int) + (G0__indirect_addr__1) * G0__tensor_stride1 + (G0__z) * G0__tensor_stride2 + (G0__b) * G0__tensor_stride3)); +} +)_"; + + // tensor shape in x-dim is 10 (thus the 8, 2 vloads in if, else blocks respectively) + const std::string uint16_3x8_yz_collapsed_b_eq_0_x_overlapping_min_y_skip_less_than_zero = R"_( +if(G0__x > 0) +{ +if(G0__indirect_addr__0 >= 0) +{ +G0__tile__0 = vload8(0, (__global ushort*)(G0__tensor_ptr + (G0__x) * sizeof(ushort) + (G0__indirect_addr__0) * G0__tensor_stride1 + (G0__0) * G0__tensor_stride3)); +} +if(G0__indirect_addr__1 >= 0) +{ +G0__tile__1 = vload8(0, (__global ushort*)(G0__tensor_ptr + (G0__x) * sizeof(ushort) + (G0__indirect_addr__1) * G0__tensor_stride1 + (G0__0) * G0__tensor_stride3)); +} +if(G0__indirect_addr__2 >= 0) +{ +G0__tile__2 = vload8(0, (__global ushort*)(G0__tensor_ptr + (G0__x) * sizeof(ushort) + (G0__indirect_addr__2) * G0__tensor_stride1 + (G0__0) * G0__tensor_stride3)); +} +} +else +{ +if(G0__indirect_addr__0 >= 0) +{ +G0__tile__0.s01 = vload2(0, (__global ushort*)(G0__tensor_ptr + (G0__x + 0) * sizeof(ushort) + (G0__indirect_addr__0) * G0__tensor_stride1 + (G0__0) * G0__tensor_stride3)); +} +if(G0__indirect_addr__1 >= 0) +{ +G0__tile__1.s01 = vload2(0, (__global ushort*)(G0__tensor_ptr + (G0__x + 0) * sizeof(ushort) + (G0__indirect_addr__1) * G0__tensor_stride1 + (G0__0) * G0__tensor_stride3)); +} +if(G0__indirect_addr__2 >= 0) +{ +G0__tile__2.s01 = vload2(0, (__global ushort*)(G0__tensor_ptr + (G0__x + 0) * sizeof(ushort) + (G0__indirect_addr__2) * G0__tensor_stride1 + (G0__0) * G0__tensor_stride3)); +} +} +)_"; + + // Configs Bundled + _configs = { + { + TileInfo(DataType::Fp32, 2, 3), + TensorStorageType::BufferUint8Ptr, + SamplerData(Format::Dim0_Dim1_Dim2, AddressModeX::None, AddressModeY::None, AddressModeZ::None), + Coordinates("x", "y", "z", "b"), + fp_2x3_tile + }, + { + TileInfo(DataType::Fp16, 2, 4), + TensorStorageType::Texture2dReadOnly, + SamplerData(Format::Dim0_Dim1xDim2_1, AddressModeX::None, AddressModeY::ClampToBorderMaxOnly, AddressModeZ::None), + Coordinates("x", "y", "z", "b"), + half_2x4_yz_collapsed_y_clamped_to_border_max_only_image + }, + { + TileInfo(DataType::Int32, 2, 4), + TensorStorageType::BufferUint8Ptr, + SamplerData(Format::Dim0_Dim1_Dim2, AddressModeX::None, AddressModeY::SkipLessThanZero, AddressModeZ::None), + Coordinates("x", "y", "z", "b"), + int_2x4_y_skip_less_than_zero + }, + { + TileInfo(DataType::Uint16, 3, 8), + TensorStorageType::BufferUint8Ptr, + SamplerData(Format::Dim0_Dim1xDim2_1, AddressModeX::OverlappingMin, AddressModeY::SkipLessThanZero, AddressModeZ::None), + Coordinates("x", "y", "z", "0"), + uint16_3x8_yz_collapsed_b_eq_0_x_overlapping_min_y_skip_less_than_zero + } + }; + } + + bool run() override + { + bool all_tests_passed = true; + int32_t test_idx = 0; + + for(auto _config: _configs) + { + KernelWriterInterceptor<CLKernelWriter> writer; + + const TileInfo tile_info = std::get<0>(_config); + const Storage storage = std::get<1>(_config); + const SamplerData sampler_data = std::get<2>(_config); + const Coordinates coord = std::get<3>(_config); + const std::string expected_code = std::get<4>(_config).substr(1); // ignore initial newline, which was added for convenience + + TileOperand tile_op = writer.declare_tile("tile", TileInfo(tile_info.data_type(), tile_info.height(), tile_info.width())); + TileOperand indirect_addr_op = writer.declare_tile("indirect_addr", TileInfo(DataType::Int32, tile_info.height(), 1)); // (M0, 1) + TileOperand x_op = writer.declare_tile(coord.x, TileInfo(DataType::Int32)); + TileOperand z_op = writer.declare_tile(coord.z, TileInfo(DataType::Int32)); + TileOperand batch_op = writer.declare_tile(coord.batch, TileInfo(DataType::Int32)); + + TensorShape tensor_shape {10, 10, 10, 10}; + TensorInfo tensor_info(tile_info.data_type(), tensor_shape, TensorDataLayout::Nhwc, 0 /* id */); + TensorOperand tensor_op = writer.declare_tensor_argument("tensor", tensor_info); + TensorSampler sampler(storage, sampler_data.format, sampler_data.mode_x, sampler_data.mode_y, sampler_data.mode_z); + + writer.start_capture_code(); + writer.op_load_indirect(tile_op, tensor_op, sampler, x_op, indirect_addr_op, z_op, batch_op); + + VALIDATE_TEST(writer.check_added_code(expected_code), all_tests_passed, test_idx++); + } + + return all_tests_passed; + } + + std::string name() override + { + return "CLKernelWriterOpLoadIndirectTest"; + } + +private: + std::vector<CLKernelWriterOpLoadIndirectConfig> _configs {}; +}; + +} // namespace ckw + +#endif // CKW_VALIDATION_TESTS_CLKERNELWRITEROPLOADINDIRECTTEST_H diff --git a/compute_kernel_writer/validation/tests/CLKernelWriterOpLoadStoreTest.h b/compute_kernel_writer/validation/tests/CLKernelWriterOpLoadStoreTest.h new file mode 100644 index 0000000000..870e80ee9a --- /dev/null +++ b/compute_kernel_writer/validation/tests/CLKernelWriterOpLoadStoreTest.h @@ -0,0 +1,325 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef CKW_VALIDATION_TESTS_CLKERNELWRITEROPLOADSTORETEST_H +#define CKW_VALIDATION_TESTS_CLKERNELWRITEROPLOADSTORETEST_H + +#include "ckw/TileInfo.h" +#include "ckw/types/DataType.h" +#include "src/cl/CLKernelWriter.h" +#include "validation/tests/common/KernelWriterInterceptor.h" +#include "validation/tests/common/Common.h" + +#include "ckw/TensorSampler.h" +#include "ckw/types/MemoryOperation.h" +#include "ckw/types/TensorSamplerTypes.h" + +#include <vector> + +namespace ckw +{ + +class CLKernelWriterOpLoadStoreTest : public ITest +{ +private: + using AddressModeX = TensorSamplerAddressModeX; + using AddressModeY = TensorSamplerAddressModeY; + using AddressModeZ = TensorSamplerAddressModeZ; + using Format = TensorSamplerFormat; + using Storage = TensorStorageType; + + struct Coordinates + { + Coordinates(std::string x, std::string y, std::string z, std::string batch) + : x(x), y(y), z(z), batch(batch) + { + } + + std::string x; + std::string y; + std::string z; + std::string batch; + }; + + struct SamplerData + { + SamplerData(Format format, AddressModeX mode_x, AddressModeY mode_y, AddressModeZ mode_z) + : format(format), mode_x(mode_x), mode_y(mode_y), mode_z(mode_z) + { + } + + Format format; + AddressModeX mode_x; + AddressModeY mode_y; + AddressModeZ mode_z; + }; + + struct Dilations + { + Dilations(std::string dilation_x, std::string dilation_y) + : dilation_x(dilation_x), dilation_y(dilation_y) + { + } + + std::string dilation_x; + std::string dilation_y; + }; + + using CLKernelWriterOpLoadStoreConfig = std::tuple<MemoryOperation, TileInfo, TensorStorageType, SamplerData, Coordinates, Dilations, std::string>; + +public: + CLKernelWriterOpLoadStoreTest() + { + // Cases + const std::string load_fp_2x3_tile = R"_( +G0__tile__0 = vload3(0, (__global float*)(G0__tensor_ptr + (G0__x) * sizeof(float) + (G0__y + 0) * G0__tensor_stride1 + (G0__z) * G0__tensor_stride2 + (G0__b) * G0__tensor_stride3)); +G0__tile__1 = vload3(0, (__global float*)(G0__tensor_ptr + (G0__x) * sizeof(float) + (G0__y + 1) * G0__tensor_stride1 + (G0__z) * G0__tensor_stride2 + (G0__b) * G0__tensor_stride3)); +)_"; + const std::string load_half_2x4_tile_image_clamp_y = R"_( +G0__tile__0 = read_imageh(G0__tensor_img2d, CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP | CLK_FILTER_NEAREST, (int2)((G0__x) >> 2, (G0__y + 0 + (G0__z) * G0__tensor_dim1 + (G0__b) * G0__tensor_dim1 * G0__tensor_dim2))); +G0__tile__1 = read_imageh(G0__tensor_img2d, CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP | CLK_FILTER_NEAREST, (int2)((G0__x) >> 2, (G0__y + 1 + (G0__z) * G0__tensor_dim1 + (G0__b) * G0__tensor_dim1 * G0__tensor_dim2))); +)_"; + const std::string store_fp_2x3_tile = R"_( +vstore3(G0__tile__0, 0, (__global float*)(G0__tensor_ptr + (G0__x) * sizeof(float) + (G0__y + 0) * G0__tensor_stride1 + (G0__b) * G0__tensor_stride3)); +vstore3(G0__tile__1, 0, (__global float*)(G0__tensor_ptr + (G0__x) * sizeof(float) + (G0__y + 1) * G0__tensor_stride1 + (G0__b) * G0__tensor_stride3)); +)_"; + const std::string store_int8_4x4_y_dilation_batch_eq_0 = R"_( +vstore4(G0__tile__0, 0, (__global char*)(G0__tensor_ptr + (((int)(1))) * sizeof(char) + (G0__y + 0 * G0__y_dilation) * G0__tensor_stride1 + (G0__z) * G0__tensor_stride2 + (((int)(0))) * G0__tensor_stride3)); +vstore4(G0__tile__1, 0, (__global char*)(G0__tensor_ptr + (((int)(1))) * sizeof(char) + (G0__y + 1 * G0__y_dilation) * G0__tensor_stride1 + (G0__z) * G0__tensor_stride2 + (((int)(0))) * G0__tensor_stride3)); +vstore4(G0__tile__2, 0, (__global char*)(G0__tensor_ptr + (((int)(1))) * sizeof(char) + (G0__y + 2 * G0__y_dilation) * G0__tensor_stride1 + (G0__z) * G0__tensor_stride2 + (((int)(0))) * G0__tensor_stride3)); +vstore4(G0__tile__3, 0, (__global char*)(G0__tensor_ptr + (((int)(1))) * sizeof(char) + (G0__y + 3 * G0__y_dilation) * G0__tensor_stride1 + (G0__z) * G0__tensor_stride2 + (((int)(0))) * G0__tensor_stride3)); +)_"; + // tensor dimension is 10 + const std::string load_fp_2x3_tile_x_overlapping_min_y_eq_0_batch_eq_1 = R"_( +if(G0__x > 0) +{ +G0__tile__0 = vload3(0, (__global float*)(G0__tensor_ptr + (G0__x) * sizeof(float) + (((int)(0)) + 0) * G0__tensor_stride1 + (G0__z) * G0__tensor_stride2 + (((int)(1))) * G0__tensor_stride3)); +G0__tile__1 = vload3(0, (__global float*)(G0__tensor_ptr + (G0__x) * sizeof(float) + (((int)(0)) + 1) * G0__tensor_stride1 + (G0__z) * G0__tensor_stride2 + (((int)(1))) * G0__tensor_stride3)); +} +else +{ +G0__tile__0.s0 = *((__global float*)(G0__tensor_ptr + (G0__x + 0) * sizeof(float) + (((int)(0)) + 0) * G0__tensor_stride1 + (G0__z) * G0__tensor_stride2 + (((int)(1))) * G0__tensor_stride3)); +G0__tile__1.s0 = *((__global float*)(G0__tensor_ptr + (G0__x + 0) * sizeof(float) + (((int)(0)) + 1) * G0__tensor_stride1 + (G0__z) * G0__tensor_stride2 + (((int)(1))) * G0__tensor_stride3)); +} +)_"; + const std::string store_fp_2x3_tile_x_overlapping_min_y_clamp_to_border_max_only = R"_( +if(G0__x > 0) +{ +if(G0__y + 0 < G0__tensor_dim1) +{ +vstore3(G0__tile__0, 0, (__global float*)(G0__tensor_ptr + (G0__x) * sizeof(float) + (G0__y + 0) * G0__tensor_stride1 + (G0__z) * G0__tensor_stride2 + (G0__b) * G0__tensor_stride3)); +} +else +{ +G0__tile__0 = 0.0f; +} +if(G0__y + 1 < G0__tensor_dim1) +{ +vstore3(G0__tile__1, 0, (__global float*)(G0__tensor_ptr + (G0__x) * sizeof(float) + (G0__y + 1) * G0__tensor_stride1 + (G0__z) * G0__tensor_stride2 + (G0__b) * G0__tensor_stride3)); +} +else +{ +G0__tile__1 = 0.0f; +} +} +else +{ +if(G0__y + 0 < G0__tensor_dim1) +{ +*((__global float*)(G0__tensor_ptr + (G0__x + 0) * sizeof(float) + (G0__y + 0) * G0__tensor_stride1 + (G0__z) * G0__tensor_stride2 + (G0__b) * G0__tensor_stride3)) = G0__tile__0.s0; +} +else +{ +G0__tile__0.s0 = 0.0f; +} +if(G0__y + 1 < G0__tensor_dim1) +{ +*((__global float*)(G0__tensor_ptr + (G0__x + 0) * sizeof(float) + (G0__y + 1) * G0__tensor_stride1 + (G0__z) * G0__tensor_stride2 + (G0__b) * G0__tensor_stride3)) = G0__tile__1.s0; +} +else +{ +G0__tile__1.s0 = 0.0f; +} +} +)_"; + const std::string store_half_2x4_tile_x_image_y_dilation = R"_( +write_imageh(G0__tensor_img2d, (int2)((G0__x) >> 2, (((int)(0)) + 0 * G0__y_dilation + (G0__z) * G0__tensor_dim1 + (((int)(1))) * G0__tensor_dim1 * G0__tensor_dim2)), G0__tile__0); +write_imageh(G0__tensor_img2d, (int2)((G0__x) >> 2, (((int)(0)) + 1 * G0__y_dilation + (G0__z) * G0__tensor_dim1 + (((int)(1))) * G0__tensor_dim1 * G0__tensor_dim2)), G0__tile__1); +)_"; + + // Configs Bundled + _configs = { + // op, tile, storage, sampler, coordinates, dilation, expected + { + MemoryOperation::Load, + TileInfo(DataType::Fp32, 2, 3), + TensorStorageType::BufferUint8Ptr, + SamplerData(Format::Dim0_Dim1_Dim2, AddressModeX::None, AddressModeY::None, AddressModeZ::None), + Coordinates("x", "y", "z", "b"), + Dilations("1", "1"), + load_fp_2x3_tile + }, + { + MemoryOperation::Load, + TileInfo(DataType::Fp16, 2, 4), + TensorStorageType::Texture2dReadOnly, + SamplerData(Format::Dim0_Dim1_Dim2, AddressModeX::None, AddressModeY::ClampToBorderMaxOnly, AddressModeZ::None), + Coordinates("x", "y", "z", "b"), + Dilations("1", "1"), + load_half_2x4_tile_image_clamp_y + }, + { + MemoryOperation::Store, + TileInfo(DataType::Fp32, 2, 3), + TensorStorageType::BufferUint8Ptr, + SamplerData(Format::Dim0_Dim1xDim2_1,AddressModeX::None, AddressModeY::None, AddressModeZ::None), + Coordinates("x", "y", "z", "b"), + Dilations("1", "1"), + store_fp_2x3_tile + }, + { + MemoryOperation::Store, + TileInfo(DataType::Int8, 4, 4), + TensorStorageType::BufferUint8Ptr, + SamplerData(Format::Dim0_Dim1_Dim2, AddressModeX::None, AddressModeY::None, AddressModeZ::None), + Coordinates("1", "y", "z", "0"), + Dilations("1", "y_dilation"), + store_int8_4x4_y_dilation_batch_eq_0 + }, + { + MemoryOperation::Load, + TileInfo(DataType::Fp32, 2, 3), + TensorStorageType::BufferUint8Ptr, + SamplerData(Format::Dim0_Dim1_Dim2, AddressModeX::OverlappingMin, AddressModeY::None, AddressModeZ::None), + Coordinates("x", "0", "z", "1"), + Dilations("1", "1"), + load_fp_2x3_tile_x_overlapping_min_y_eq_0_batch_eq_1 + }, + { + MemoryOperation::Store, + TileInfo(DataType::Fp32, 2, 3), + TensorStorageType::BufferUint8Ptr, + SamplerData(Format::Dim0_Dim1_Dim2, AddressModeX::OverlappingMin, AddressModeY::ClampToBorderMaxOnly, AddressModeZ::None), + Coordinates("x", "y", "z", "b"), + Dilations("1", "1"), + store_fp_2x3_tile_x_overlapping_min_y_clamp_to_border_max_only + }, + { + MemoryOperation::Store, + TileInfo(DataType::Fp16, 2, 4), + TensorStorageType::Texture2dWriteOnly, + SamplerData(Format::Dim0_Dim1_Dim2, AddressModeX::None, AddressModeY::None, AddressModeZ::None), + Coordinates("x", "0", "z", "1"), + Dilations("1", "y_dilation"), + store_half_2x4_tile_x_image_y_dilation + } + }; + } + + TileOperand declare_tile_helper(KernelWriter &writer, std::string tile) + { + if(tile == "0" || tile == "1") + { + return writer.declare_constant_tile(ConstantData({{std::stoi(tile)}}, DataType::Int32)); + } + else + { + return writer.declare_tile(tile, TileInfo(DataType::Int32)); + } + } + + bool run() override + { + bool all_tests_passed = true; + int32_t test_idx = 0; + + for(auto _config: _configs) + { + KernelWriterInterceptor<CLKernelWriter> writer; + + const MemoryOperation op = std::get<0>(_config); + const TileInfo tile_info = std::get<1>(_config); + const Storage storage = std::get<2>(_config); + const SamplerData sampler_data = std::get<3>(_config); + const Coordinates coord = std::get<4>(_config); + const Dilations dilations = std::get<5>(_config); + const std::string expected_code = std::get<6>(_config).substr(1); // ignore initial newline, which was added for convenience + + TileOperand tile_op = writer.declare_tile("tile", tile_info); + TileOperand x_op = declare_tile_helper(writer, coord.x); + TileOperand y_op = declare_tile_helper(writer, coord.y); + TileOperand z_op = declare_tile_helper(writer, coord.z); + TileOperand batch_op = declare_tile_helper(writer, coord.batch); + TileOperand dil_x_op = declare_tile_helper(writer, dilations.dilation_x); + TileOperand dil_y_op = declare_tile_helper(writer, dilations.dilation_y); + + TensorShape tensor_shape {10, 10, 10, 10}; + TensorInfo tensor_info(tile_info.data_type(), tensor_shape, TensorDataLayout::Nhwc, 0 /* id */); + TensorOperand tensor_op = writer.declare_tensor_argument("tensor", tensor_info); + TensorSampler sampler(storage, sampler_data.format, sampler_data.mode_x, sampler_data.mode_y, sampler_data.mode_z); + + const bool no_dilation = (dilations.dilation_x == "1" && dilations.dilation_y == "1"); + + writer.start_capture_code(); + if(op == MemoryOperation::Load) + { + if(no_dilation) + { + writer.op_load(tile_op, tensor_op, sampler, x_op, y_op, z_op, batch_op); + } + else + { + writer.op_load_dilated(tile_op, tensor_op, sampler, x_op, y_op, z_op, batch_op, dil_x_op, dil_y_op); + } + } + else + { + if(no_dilation) + { + writer.op_store(tensor_op, tile_op, sampler, x_op, y_op, z_op, batch_op); + } + else + { + writer.op_store_dilated(tensor_op, tile_op, sampler, x_op, y_op, z_op, batch_op, dil_x_op, dil_y_op); + } + } + + VALIDATE_TEST(writer.check_added_code(expected_code), all_tests_passed, test_idx++); + } + + return all_tests_passed; + } + + std::string name() override + { + return "CLKernelWriterOpLoadStoreTest"; + } + +private: + std::vector<CLKernelWriterOpLoadStoreConfig> _configs {}; +}; + +} // namespace ckw + +#endif // CKW_VALIDATION_TESTS_CLKERNELWRITEROPLOADSTORETEST_H diff --git a/compute_kernel_writer/validation/tests/CLKernelWriterPrintTest.h b/compute_kernel_writer/validation/tests/CLKernelWriterPrintTest.h new file mode 100644 index 0000000000..6229dfb8c0 --- /dev/null +++ b/compute_kernel_writer/validation/tests/CLKernelWriterPrintTest.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef CKW_VALIDATION_TESTS_CLKERNELWRITERPRINT_H +#define CKW_VALIDATION_TESTS_CLKERNELWRITERPRINT_H + +#include "ckw/TileInfo.h" +#include "src/cl/CLKernelWriter.h" +#include "validation/tests/common/Common.h" +#include "validation/tests/common/KernelWriterInterceptor.h" + +namespace ckw +{ + +class CLKernelWriterPrintTest : public ITest +{ +public: + CLKernelWriterPrintTest() + { + } + + bool run() override + { + bool all_tests_passed = true; + + KernelWriterInterceptor<CLKernelWriter> writer; + + const auto tile2x3f16 = writer.declare_tile("tile2x3f16", TileInfo(DataType::Fp16, 2, 3)); + const auto tile1x2i32 = writer.declare_tile("tile1x2i32", TileInfo(DataType::Int32, 1, 2)); + const auto tile2x1s32 = writer.declare_tile("tile2x1s32", TileInfo(DataType::Int32, 2, 1)); + const auto tile1x1u32 = writer.declare_tile("tile1x1u32", TileInfo(DataType::Uint32, 1, 1)); + + writer.start_capture_code(); + + writer.op_print("debug_log", { tile2x3f16, tile1x2i32, tile2x1s32, tile1x1u32 }); + + constexpr auto expected_code = + "printf(\"debug_log\\nG0__tile2x3f16 = [[%v3hg], [%v3hg]]\\nG0__tile1x2i32 = [%v2hli]\\nG0__tile2x1s32 = [%i, %i]\\nG0__tile1x1u32 = %u\\n\", " + "G0__tile2x3f16__0, G0__tile2x3f16__1, G0__tile1x2i32, G0__tile2x1s32__0, G0__tile2x1s32__1, G0__tile1x1u32);\n"; + + VALIDATE_TEST(writer.check_added_code(expected_code), all_tests_passed, 0); + + return all_tests_passed; + } + + std::string name() override + { + return "CLKernelWriterPrintTest"; + } +}; + +} // namespace ckw + +#endif // CKW_VALIDATION_TESTS_CLKERNELWRITERPRINT_H diff --git a/compute_kernel_writer/validation/tests/CLKernelWriterReturnTest.h b/compute_kernel_writer/validation/tests/CLKernelWriterReturnTest.h new file mode 100644 index 0000000000..fc3f2a639b --- /dev/null +++ b/compute_kernel_writer/validation/tests/CLKernelWriterReturnTest.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef CKW_VALIDATION_TESTS_CLKERNELWRITERRETURNTEST_H +#define CKW_VALIDATION_TESTS_CLKERNELWRITERRETURNTEST_H + +#include "src/cl/CLKernelWriter.h" +#include "validation/tests/common/Common.h" +#include "validation/tests/common/KernelWriterInterceptor.h" + +namespace ckw +{ + +class CLKernelWriterReturnTest : public ITest +{ +public: + CLKernelWriterReturnTest() + { + } + + bool run() override + { + bool all_tests_passed = true; + + KernelWriterInterceptor<CLKernelWriter> writer; + + writer.start_capture_code(); + + writer.op_return(); + + constexpr auto expected_code = "return;\n"; + + VALIDATE_TEST(writer.check_added_code(expected_code), all_tests_passed, 0); + + return all_tests_passed; + } + + std::string name() override + { + return "CLKernelWriterReturnTest"; + } +}; + +} // namespace ckw + +#endif // CKW_VALIDATION_TESTS_CLKERNELWRITERRETURNTEST_H diff --git a/compute_kernel_writer/validation/tests/CLKernelWriterSubTileTest.h b/compute_kernel_writer/validation/tests/CLKernelWriterSubTileTest.h new file mode 100644 index 0000000000..ea360b289e --- /dev/null +++ b/compute_kernel_writer/validation/tests/CLKernelWriterSubTileTest.h @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef CKW_VALIDATION_SRC_TESTS_CLKERNELWRITERSUBTILETEST_H +#define CKW_VALIDATION_SRC_TESTS_CLKERNELWRITERSUBTILETEST_H + +#include "ckw/TileInfo.h" +#include "ckw/types/DataType.h" +#include "ckw/types/Operators.h" +#include "src/cl/CLKernelWriter.h" +#include "validation/tests/common/Common.h" +#include "validation/tests/common/KernelWriterInterceptor.h" + +#include <cstdint> +#include <vector> + +namespace ckw +{ + +class CLKernelWriterSubTileTest : public ITest +{ +public: + CLKernelWriterSubTileTest() + { + // These are the definitions of the tiles involving in the writing actions. + // + // Structure: + // * List of tiles: + // - Tile full height. + // - Tile full width. + // - Tile view access type (full tile, vector, scalar). + // - Tile view start row. + // - Tile view start column. + // - The tile name. + + // Vector access. + _tests.push_back( + { { { 1, 4, AccessType::Vector, 0, 0, "{tile_name}" }, // + { 4, 4, AccessType::Vector, 2, 0, "{tile_name}__2" }, + { 1, 4, AccessType::Full, 0, 0, "{tile_name}" }, + { 4, 4, AccessType::Vector, 3, 0, "{tile_name}__3" } } }); + + // Scalar access. + _tests.push_back( + { { { 1, 1, AccessType::Full, 0, 0, "{tile_name}" }, // + { 4, 8, AccessType::Scalar, 2, 4, "{tile_name}__2.s4" }, + { 1, 16, AccessType::ScalarOfVector, 0, 10, "{tile_name}.sA" }, + { 1, 1, AccessType::Scalar, 0, 0, "{tile_name}" } } }); + + // These are the definitions of the writing actions. + // + // Structure: + // * Writing function. + // * Whether this function only works with scalar value. + // * Expected code format. + + _actions.push_back( + { [](CLKernelWriter &writer, const std::vector<TileOperand> &args) + { + writer.op_assign(args.at(0), args.at(1)); + }, + false, + "{op0} = {op1};\n" }); + + _actions.push_back( + { [](CLKernelWriter &writer, const std::vector<TileOperand> &args) + { + writer.op_unary(args.at(0), UnaryOp::Sqrt, args.at(1)); + }, + false, + "{op0} = sqrt({op1});\n" }); + + _actions.push_back( + { [](CLKernelWriter &writer, const std::vector<TileOperand> &args) + { + writer.op_binary(args.at(0), BinaryOp::Add, args.at(1), args.at(2)); + }, + false, + "{op0} = {op1} + {op2};\n" }); + + _actions.push_back( + { [](CLKernelWriter &writer, const std::vector<TileOperand> &args) + { + writer.op_ternary(args.at(0), TernaryOp::Clamp, args.at(1), args.at(2), args.at(3)); + }, + false, + "{op0} = clamp({op1}, {op2}, {op3});\n" }); + + _actions.push_back( + { [](CLKernelWriter &writer, const std::vector<TileOperand> &args) + { + writer.op_if(args.at(0), BinaryOp::Greater, args.at(1), [] {}); + }, + true, + "if ({op0} > {op1})\n{\n}\n" }); + } + + bool run() override + { + bool all_tests_passed = true; + int32_t test_idx = 0; + + KernelWriterInterceptor<CLKernelWriter> writer; + + for(size_t test_no = 0; test_no < _tests.size(); ++test_no) + { + const TestInfo &test = _tests.at(test_no); + + // Declare all the tiles and get the full name of those tile operand. + std::vector<TileOperand> tiles; + std::vector<std::string> expected_tiles_name; + + for(size_t operand_no = 0; operand_no < test.operands.size(); ++operand_no) + { + const TestOperand &operand = test.operands.at(operand_no); + std::string name = "test" + std::to_string(test_no) + "_op" + std::to_string(operand_no); + + const TileOperand full_tile = writer.declare_tile(name, TileInfo(DataType::Fp32, operand.height, operand.width)); + + switch(operand.access_type) + { + case AccessType::Full: + tiles.emplace_back(full_tile); + break; + + case AccessType::Vector: + tiles.emplace_back(full_tile.row(operand.start_row)); + break; + + case AccessType::Scalar: + tiles.emplace_back(full_tile.scalar(operand.start_row, operand.start_col)); + break; + + case AccessType::ScalarOfVector: + tiles.emplace_back(full_tile.row(operand.start_row).scalar(0, operand.start_col)); + break; + + default: + CKW_THROW_MSG("Unsupported access type!"); + } + + expected_tiles_name.push_back("G0__" + name); + } + + // Try each writing action using the newly declared tiles. + for(const TestAction &action : _actions) + { + if(action.scalar_only && // + (test.operands.at(0).access_type != AccessType::Scalar && // + (test.operands.at(0).height != 1 || test.operands.at(0).width != 1))) + { + continue; + } + + writer.start_capture_code(); + + action.write(writer, tiles); + + // The expected code is constructed from the format strings. + std::string expected_code = action.expected_code; + + for(size_t operand_no = 0; operand_no < test.operands.size(); ++operand_no) + { + const TestOperand &operand = test.operands.at(operand_no); + + const std::string op_name = search_and_replace(operand.name, "{tile_name}", expected_tiles_name.at(operand_no)); + expected_code = search_and_replace(expected_code, "{op" + std::to_string(operand_no) + "}", op_name); + } + + VALIDATE_TEST(writer.check_added_code(expected_code), all_tests_passed, test_idx++); + } + } + + return all_tests_passed; + } + + std::string search_and_replace(const std::string &src, const std::string &search, const std::string &replace) + { + std::string result = src; + + size_t idx = 0; + + while(true) + { + idx = result.find(search, idx); + + if(idx == std::string::npos) + { + break; + } + + result = result.replace(idx, search.size(), replace); + } + + return result; + } + + std::string name() override + { + return "CLKernelWriterSubTileTest"; + } + +private: + enum class AccessType + { + Full, + Vector, + Scalar, + ScalarOfVector, + }; + + struct TestOperand + { + int32_t height; + int32_t width; + + AccessType access_type; + int32_t start_row; + int32_t start_col; + + std::string name; + }; + + struct TestInfo + { + std::vector<TestOperand> operands; + }; + + struct TestAction + { + std::function<void(CLKernelWriter &, const std::vector<TileOperand> &)> write; + + bool scalar_only; + std::string expected_code; + }; + + std::vector<TestInfo> _tests{}; + std::vector<TestAction> _actions{}; +}; + +} // namespace ckw + +#endif // CKW_VALIDATION_SRC_TESTS_CLKERNELWRITERSUBTILETEST_H diff --git a/compute_kernel_writer/validation/tests/CLKernelWriterTernaryOpTest.h b/compute_kernel_writer/validation/tests/CLKernelWriterTernaryOpTest.h new file mode 100644 index 0000000000..d25d3e2958 --- /dev/null +++ b/compute_kernel_writer/validation/tests/CLKernelWriterTernaryOpTest.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef CKW_VALIDATION_TESTS_CLKERNELWRITERTERNARYOPTEST_H +#define CKW_VALIDATION_TESTS_CLKERNELWRITERTERNARYOPTEST_H + +#include "ckw/TileInfo.h" +#include "ckw/types/DataType.h" +#include "ckw/types/Operators.h" +#include "src/cl/CLKernelWriter.h" +#include "validation/tests/common/Common.h" +#include "validation/tests/common/KernelWriterInterceptor.h" + +#include <cstdint> +#include <vector> + +namespace ckw +{ + +class CLKernelWriterTernaryOpTest : public ITest +{ +public: + CLKernelWriterTernaryOpTest() + { + // dst_height, dst_width, op0_height, op0_width, op1_height, op1_width, op2_height, op2_width, data_type, op, expected_code + + _tests.push_back({ 1, 1, 1, 1, 1, 1, 1, 1, DataType::Fp32, TernaryOp::Select, "G0__dst = select(G0__op0, G0__op1, G0__op2);\n" }); // Scalar. + + _tests.push_back({ 1, 3, 1, 3, 1, 3, 1, 3, DataType::Fp16, TernaryOp::Clamp, "G0__dst = clamp(G0__op0, G0__op1, G0__op2);\n" }); // Whole vector. + + _tests.push_back({ 2, 4, 2, 4, 2, 4, 2, 4, DataType::Int8, TernaryOp::Select, "G0__dst__0 = select(G0__op0__0, G0__op1__0, G0__op2__0);\nG0__dst__1 = select(G0__op0__1, G0__op1__1, G0__op2__1);\n" }); // Whole tile. + + _tests.push_back({ 2, 3, 1, 3, 2, 3, 2, 3, DataType::Uint8, TernaryOp::Clamp, "G0__dst__0 = clamp(G0__op0, G0__op1__0, G0__op2__0);\nG0__dst__1 = clamp(G0__op0, G0__op1__1, G0__op2__1);\n" }); // 1st operand y-dimension broadcast. + + _tests.push_back({ 2, 3, 2, 3, 2, 1, 2, 3, DataType::Fp32, TernaryOp::Select, "G0__dst__0 = select(G0__op0__0, (float3)G0__op1__0, G0__op2__0);\nG0__dst__1 = select(G0__op0__1, (float3)G0__op1__1, G0__op2__1);\n" }); // 2nd operand x-dimension broadcast. + + _tests.push_back({ 2, 3, 1, 3, 2, 1, 1, 1, DataType::Fp16, TernaryOp::Clamp, "G0__dst__0 = clamp(G0__op0, (half3)G0__op1__0, (half3)G0__op2);\nG0__dst__1 = clamp(G0__op0, (half3)G0__op1__1, (half3)G0__op2);\n" }); // 1st operand y-, 2nd operand x-, 3rd operand x- and y-dimension broadcast. + } + + bool run() override + { + int32_t test_no = 0; + bool all_tests_passed = true; + + for(const auto &test : _tests) + { + KernelWriterInterceptor<CLKernelWriter> writer; + + auto dst = writer.declare_tile("dst", TileInfo(test.data_type, test.dst_height, test.dst_width)); + auto op0 = writer.declare_tile("op0", TileInfo(DataType::Bool, test.op0_height, test.op0_width)); + auto op1 = writer.declare_tile("op1", TileInfo(test.data_type, test.op1_height, test.op1_width)); + auto op2 = writer.declare_tile("op2", TileInfo(test.data_type, test.op2_height, test.op2_width)); + + writer.start_capture_code(); + + writer.op_ternary(dst, test.op, op0, op1, op2); + + VALIDATE_TEST(writer.check_added_code(test.expected_code), all_tests_passed, test_no++); + } + + return all_tests_passed; + } + + std::string name() override + { + return "CLKernelWriterTernaryOpTest"; + } + +private: + struct TestInfo + { + int32_t dst_height; + int32_t dst_width; + int32_t op0_height; + int32_t op0_width; + int32_t op1_height; + int32_t op1_width; + int32_t op2_height; + int32_t op2_width; + DataType data_type; + TernaryOp op; + std::string expected_code; + }; + + std::vector<TestInfo> _tests{}; +}; + +} // namespace ckw + +#endif // CKW_VALIDATION_TESTS_CLKERNELWRITERTERNARYOPTEST_H diff --git a/compute_kernel_writer/validation/tests/CLKernelWriterUnaryExpressionTest.h b/compute_kernel_writer/validation/tests/CLKernelWriterUnaryExpressionTest.h new file mode 100644 index 0000000000..395a2fe817 --- /dev/null +++ b/compute_kernel_writer/validation/tests/CLKernelWriterUnaryExpressionTest.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef CKW_VALIDATION_TESTS_CLKERNELWRITERUNARYEXPRESSIONTEST_H +#define CKW_VALIDATION_TESTS_CLKERNELWRITERUNARYEXPRESSIONTEST_H + +#include "ckw/TileInfo.h" +#include "ckw/types/DataType.h" +#include "ckw/types/Operators.h" +#include "src/cl/CLKernelWriter.h" +#include "validation/tests/common/Common.h" +#include "validation/tests/common/KernelWriterInterceptor.h" + +#include <cstdint> +#include <vector> + +namespace ckw +{ + +class CLKernelWriterUnaryExpressionTest : public ITest +{ +public: + CLKernelWriterUnaryExpressionTest() + { + // dst_height, dst_width, src_height, src_width, data_type, op, expected_code + + _tests.push_back({ 1, 1, 1, 1, DataType::Uint32, UnaryOp::BitwiseNot, "G0__dst = ~G0__src;\n" }); // Scalar. + + _tests.push_back({ 1, 3, 1, 3, DataType::Int16, UnaryOp::LogicalNot, "G0__dst = !G0__src;\n" }); // Whole vector. + + _tests.push_back({ 2, 4, 2, 4, DataType::Int8, UnaryOp::Exp, "G0__dst__0 = exp(G0__src__0);\nG0__dst__1 = exp(G0__src__1);\n" }); // Whole tile. + + _tests.push_back({ 2, 3, 1, 3, DataType::Uint8, UnaryOp::Log, "G0__dst__0 = log(G0__src);\nG0__dst__1 = log(G0__src);\n" }); // Y-dimension broadcast. + + _tests.push_back({ 2, 4, 2, 1, DataType::Uint16, UnaryOp::Sqrt, "G0__dst__0 = (ushort4)sqrt(G0__src__0);\nG0__dst__1 = (ushort4)sqrt(G0__src__1);\n" }); // X-dimension broadcast. + + _tests.push_back({ 2, 3, 1, 1, DataType::Int32, UnaryOp::Round, "G0__dst__0 = (int3)round(G0__src);\nG0__dst__1 = (int3)round(G0__src);\n" }); // X and y dimension broadcast. + } + + bool run() override + { + int32_t test_no = 0; + bool all_tests_passed = true; + + for(const auto &test : _tests) + { + KernelWriterInterceptor<CLKernelWriter> writer; + + auto dst = writer.declare_tile("dst", TileInfo(test.data_type, test.dst_height, test.dst_width)); + auto src = writer.declare_tile("src", TileInfo(test.data_type, test.src_height, test.src_width)); + + writer.start_capture_code(); + + writer.op_unary(dst, test.op, src); + + VALIDATE_TEST(writer.check_added_code(test.expected_code), all_tests_passed, test_no++); + } + + return all_tests_passed; + } + + std::string name() override + { + return "CLKernelWriterUnaryExpressionTest"; + } + +private: + struct TestInfo + { + int32_t dst_height; + int32_t dst_width; + int32_t src_height; + int32_t src_width; + DataType data_type; + UnaryOp op; + std::string expected_code; + }; + + std::vector<TestInfo> _tests{}; +}; + +} // namespace ckw + +#endif // CKW_VALIDATION_TESTS_CLKERNELWRITERUNARYEXPRESSIONTEST_H diff --git a/compute_kernel_writer/validation/tests/CLTensorArgumentTest.h b/compute_kernel_writer/validation/tests/CLTensorArgumentTest.h new file mode 100644 index 0000000000..d3e455cb83 --- /dev/null +++ b/compute_kernel_writer/validation/tests/CLTensorArgumentTest.h @@ -0,0 +1,540 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef CKW_VALIDATION_TESTS_CLTENSORARGUMENTTEST_H +#define CKW_VALIDATION_TESTS_CLTENSORARGUMENTTEST_H + +#include "common/Common.h" +#include "src/cl/CLHelpers.h" +#include "src/cl/CLTensorArgument.h" +#include "src/cl/CLTensorComponent.h" + +#include <string> +#include <vector> + +namespace ckw +{ +class CLTensorArgumentComponentNamesTest : public ITest +{ +public: + const DataType dt = DataType::Fp32; + const TensorShape shape = TensorShape({ { 12, 14, 3, 1, 2 } }); + const std::string tensor_name = "src"; + + CLTensorArgumentComponentNamesTest() + { + _components.push_back(TensorComponentType::Dim0); + _components.push_back(TensorComponentType::Dim1); + _components.push_back(TensorComponentType::Dim2); + _components.push_back(TensorComponentType::Dim3); + _components.push_back(TensorComponentType::Dim4); + _components.push_back(TensorComponentType::Dim1xDim2); + _components.push_back(TensorComponentType::Dim2xDim3); + _components.push_back(TensorComponentType::OffsetFirstElement); + _components.push_back(TensorComponentType::Stride0); + _components.push_back(TensorComponentType::Stride1); + _components.push_back(TensorComponentType::Stride2); + _components.push_back(TensorComponentType::Stride3); + _components.push_back(TensorComponentType::Stride4); + + _expected_vars.push_back("src_dim0"); + _expected_vars.push_back("src_dim1"); + _expected_vars.push_back("src_dim2"); + _expected_vars.push_back("src_dim3"); + _expected_vars.push_back("src_dim4"); + _expected_vars.push_back("src_dim1xdim2"); + _expected_vars.push_back("src_dim2xdim3"); + _expected_vars.push_back("src_offset_first_element"); + _expected_vars.push_back("src_stride0"); + _expected_vars.push_back("src_stride1"); + _expected_vars.push_back("src_stride2"); + _expected_vars.push_back("src_stride3"); + _expected_vars.push_back("src_stride4"); + } + + bool run() override + { + VALIDATE_ON_MSG(_components.size() == _expected_vars.size(), "The number of components and variables does not match"); + + // The status of this variable can change in VALIDATE_TEST() + bool all_tests_passed = true; + + const TensorInfo info(dt, shape, TensorDataLayout::Nhwc, 1); + + const size_t num_tests = _expected_vars.size(); + + int32_t test_idx = 0; + for(size_t i = 0; i < num_tests; ++i) + { + CLTensorArgument arg(tensor_name, info, false /* return_dims_by_value */); + + const std::string expected_var_name = _expected_vars[i]; + const std::string actual_var_name = arg.component(_components[i]).name(); + + VALIDATE_TEST(actual_var_name.compare(expected_var_name) == 0, all_tests_passed, test_idx++); + } + return all_tests_passed; + } + + std::string name() override + { + return "CLTensorArgumentVariableNamesTest"; + } + +private: + std::vector<TensorComponentType> _components{}; + std::vector<std::string> _expected_vars{}; +}; + +class CLTensorArgumentStorageNamesTest : public ITest +{ +public: + const DataType dt = DataType::Fp32; + const TensorShape shape = TensorShape({ { 12, 14, 3, 1, 2 } }); + const std::string tensor_name = "src"; + + CLTensorArgumentStorageNamesTest() + { + _storages.push_back(TensorStorageType::BufferUint8Ptr); + _storages.push_back(TensorStorageType::Texture2dReadOnly); + _storages.push_back(TensorStorageType::Texture2dWriteOnly); + + _expected_vars.push_back("src_ptr"); + _expected_vars.push_back("src_img2d"); + _expected_vars.push_back("src_img2d"); + } + + bool run() override + { + VALIDATE_ON_MSG(_storages.size() == _expected_vars.size(), "The number of storages and variables does not match"); + + // The status of this variable can change in VALIDATE_TEST() + bool all_tests_passed = true; + + const TensorInfo info(dt, shape, TensorDataLayout::Nhwc, 1); + + const size_t num_tests = _expected_vars.size(); + + int32_t test_idx = 0; + for(size_t i = 0; i < num_tests; ++i) + { + CLTensorArgument arg(tensor_name, info, false /* return_dims_by_value */); + + const std::string expected_var_name = _expected_vars[i]; + const std::string actual_var_name = arg.storage(_storages[i]).val; + + VALIDATE_TEST(actual_var_name.compare(expected_var_name) == 0, all_tests_passed, test_idx++); + } + return all_tests_passed; + } + + std::string name() override + { + return "CLTensorArgumentStorageNamesTest"; + } + +private: + std::vector<TensorStorageType> _storages{}; + std::vector<std::string> _expected_vars{}; +}; + +class CLTensorArgumentComponentValuesTest : public ITest +{ +public: + const DataType dt = DataType::Fp32; + const TensorShape shape = TensorShape({ { 12, 14, 3, 1, 2 } }); + const std::string tensor_name = "src"; + + CLTensorArgumentComponentValuesTest() + { + _components.push_back(TensorComponentType::Dim0); + _components.push_back(TensorComponentType::Dim1); + _components.push_back(TensorComponentType::Dim2); + _components.push_back(TensorComponentType::Dim3); + _components.push_back(TensorComponentType::Dim4); + _components.push_back(TensorComponentType::Dim1xDim2); + _components.push_back(TensorComponentType::Dim2xDim3); + + _expected_vals.push_back(std::to_string(shape[0])); + _expected_vals.push_back(std::to_string(shape[1])); + _expected_vals.push_back(std::to_string(shape[2])); + _expected_vals.push_back(std::to_string(shape[3])); + _expected_vals.push_back(std::to_string(shape[4])); + _expected_vals.push_back(std::to_string(shape[1] * shape[2])); + _expected_vals.push_back(std::to_string(shape[2] * shape[3])); + } + + bool run() override + { + VALIDATE_ON_MSG(_components.size() == _expected_vals.size(), "The number of components and values does not match"); + + // The status of this variable can change in VALIDATE_TEST() + bool all_tests_passed = true; + + const TensorInfo info(dt, shape, TensorDataLayout::Nhwc, 1); + + const size_t num_tests = _expected_vals.size(); + + int32_t test_idx = 0; + for(size_t i = 0; i < num_tests; ++i) + { + CLTensorArgument arg(tensor_name, info, true /* return_dims_by_value */); + + const std::string expected_var_val = std::string("((int)(") + _expected_vals[i] + "))"; + const std::string actual_var_val = arg.cl_component(_components[i]).scalar(0, 0).str; + + VALIDATE_TEST(actual_var_val.compare(expected_var_val) == 0, all_tests_passed, test_idx++); + } + return all_tests_passed; + } + + std::string name() override + { + return "CLTensorArgumentComponentValuesTest"; + } + +private: + std::vector<TensorComponentType> _components{}; + std::vector<std::string> _expected_vals{}; +}; + +class CLTensorArgumentComponentsUsedPassByValueFalseTest : public ITest +{ +public: + const DataType dt = DataType::Fp32; + const TensorShape shape = TensorShape({ { 12, 14, 3, 1, 2 } }); + const std::string tensor_name = "src"; + + CLTensorArgumentComponentsUsedPassByValueFalseTest() + { + _components.push_back(TensorComponentType::Dim0); + _components.push_back(TensorComponentType::Dim2); + _components.push_back(TensorComponentType::Dim3); + _components.push_back(TensorComponentType::Dim1xDim2); + _components.push_back(TensorComponentType::OffsetFirstElement); + _components.push_back(TensorComponentType::Stride1); + _components.push_back(TensorComponentType::Stride2); + _components.push_back(TensorComponentType::Stride3); + _components.push_back(TensorComponentType::Dim0); // Repeat the query. The TensorArgument should not create a new variable + _components.push_back(TensorComponentType::Dim2); // Repeat the query. The TensorArgument should not create a new variable + _components.push_back(TensorComponentType::Dim3); // Repeat the query. The TensorArgument should not create a new variable + + _expected_vars.push_back("src_dim0"); + _expected_vars.push_back("src_dim2"); + _expected_vars.push_back("src_dim3"); + _expected_vars.push_back("src_dim1xdim2"); + _expected_vars.push_back("src_offset_first_element"); + _expected_vars.push_back("src_stride1"); + _expected_vars.push_back("src_stride2"); + _expected_vars.push_back("src_stride3"); + } + + bool run() override + { + // The status of this variable can change in VALIDATE_TEST() + bool all_tests_passed = true; + + const TensorInfo info(dt, shape, TensorDataLayout::Nhwc, 1); + + const size_t num_components = _components.size(); + + int32_t test_idx = 0; + + CLTensorArgument arg(tensor_name, info, false /* return_dims_by_value */); + for(size_t i = 0; i < num_components; ++i) + { + arg.component(_components[i]); + } + + const auto actual_vars = arg.components(); + + const size_t num_vars = _expected_vars.size(); + + VALIDATE_ON_MSG(actual_vars.size() == num_vars, "The number of variables must match the number of expected variables"); + + for(size_t i = 0; i < num_vars; ++i) + { + // Validate variable name + const std::string expected_var_name = _expected_vars[i]; + const std::string actual_var_name = actual_vars[i]->tile().name(); + VALIDATE_TEST(actual_var_name.compare(expected_var_name) == 0, all_tests_passed, test_idx++); + + // Validate data type + const DataType expected_var_type = DataType::Int32; + const DataType actual_var_type = actual_vars[i]->tile().info().data_type(); + VALIDATE_TEST(actual_var_type == expected_var_type, all_tests_passed, test_idx++); + + // Validate tile shape + const int32_t actual_var_width = actual_vars[i]->tile().info().width(); + const int32_t actual_var_height = actual_vars[i]->tile().info().height(); + + VALIDATE_TEST(actual_var_height == 1, all_tests_passed, test_idx++); + VALIDATE_TEST(actual_var_width == 1, all_tests_passed, test_idx++); + } + return all_tests_passed; + } + + std::string name() override + { + return "CLTensorArgumentComponentsUsedPassByValueFalseTest"; + } + +private: + std::vector<TensorComponentType> _components{}; + std::vector<std::string> _expected_vars{}; +}; + +class CLTensorArgumentComponentsUsedPassByValueTrueTest : public ITest +{ +public: + const DataType dt = DataType::Fp32; + const TensorShape shape = TensorShape({ { 12, 14, 3, 1, 2 } }); + const std::string tensor_name = "src"; + + CLTensorArgumentComponentsUsedPassByValueTrueTest() + { + _components.push_back(TensorComponentType::Dim0); + _components.push_back(TensorComponentType::Dim2); + _components.push_back(TensorComponentType::Dim3); + _components.push_back(TensorComponentType::Dim1xDim2); + _components.push_back(TensorComponentType::OffsetFirstElement); + _components.push_back(TensorComponentType::Stride1); + _components.push_back(TensorComponentType::Stride2); + _components.push_back(TensorComponentType::Stride3); + _components.push_back(TensorComponentType::OffsetFirstElement); // Repeat the query. The TensorArgument should not create a new variable + _components.push_back(TensorComponentType::Stride1); // Repeat the query. The TensorArgument should not create a new variable + + _expected_vars.push_back("src_offset_first_element"); + _expected_vars.push_back("src_stride1"); + _expected_vars.push_back("src_stride2"); + _expected_vars.push_back("src_stride3"); + } + + bool run() override + { + // The status of this variable can change in VALIDATE_TEST() + bool all_tests_passed = true; + + const TensorInfo info(dt, shape, TensorDataLayout::Nhwc, 1); + + const size_t num_components = _components.size(); + + int32_t test_idx = 0; + + CLTensorArgument arg(tensor_name, info, true /* return_dims_by_value */); + for(size_t i = 0; i < num_components; ++i) + { + arg.component(_components[i]); + } + + const auto actual_vars = arg.components(); + + const size_t num_vars = _expected_vars.size(); + + VALIDATE_ON_MSG(actual_vars.size() == num_vars, "The number of variables must match the number of expected variables"); + + // Since the dimensions are passed by value, we expect only the variables for the strides + for(size_t i = 0; i < num_vars; ++i) + { + // Validate variable name + const std::string expected_var_name = _expected_vars[i]; + const std::string actual_var_name = actual_vars[i]->tile().name(); + VALIDATE_TEST(actual_var_name.compare(expected_var_name) == 0, all_tests_passed, test_idx++); + + // Validate data type + const DataType expected_var_type = DataType::Int32; + const DataType actual_var_type = actual_vars[i]->tile().info().data_type(); + VALIDATE_TEST(actual_var_type == expected_var_type, all_tests_passed, test_idx++); + + // Validate tile shape + const int32_t actual_var_width = actual_vars[i]->tile().info().width(); + const int32_t actual_var_height = actual_vars[i]->tile().info().height(); + + VALIDATE_TEST(actual_var_height == 1, all_tests_passed, test_idx++); + VALIDATE_TEST(actual_var_width == 1, all_tests_passed, test_idx++); + } + return all_tests_passed; + } + + std::string name() override + { + return "CLTensorArgumentComponentsUsedPassByValueTrueTest"; + } + +private: + std::vector<TensorComponentType> _components{}; + std::vector<std::string> _expected_vars{}; +}; + +class CLTensorArgumentStoragesUsedTest : public ITest +{ +public: + const DataType dt = DataType::Fp32; + const TensorShape shape = TensorShape({ { 12, 14, 3, 1, 2 } }); + const std::string tensor_name = "src"; + + CLTensorArgumentStoragesUsedTest() + { + _storages.push_back(TensorStorageType::BufferUint8Ptr); + _storages.push_back(TensorStorageType::Texture2dReadOnly); + _storages.push_back(TensorStorageType::BufferUint8Ptr); // Repeat the query. The TensorArgument should not create a new variable + + _expected_vars.push_back("src_ptr"); + _expected_vars.push_back("src_img2d"); + } + + bool run() override + { + // The status of this variable can change in VALIDATE_TEST() + bool all_tests_passed = true; + + const TensorInfo info(dt, shape, TensorDataLayout::Nhwc, 1); + + const size_t num_storages = _storages.size(); + + int32_t test_idx = 0; + + CLTensorArgument arg(tensor_name, info, true /* return_dims_by_value */); + for(size_t i = 0; i < num_storages; ++i) + { + arg.storage(_storages[i]); + } + + const auto actual_vars = arg.storages(); + + const size_t num_vars = _expected_vars.size(); + + VALIDATE_ON_MSG(actual_vars.size() == num_vars, "The number of variables must match the number of expected variables"); + + for(size_t i = 0; i < num_vars; ++i) + { + // Validate variable name + const std::string expected_var_name = _expected_vars[i]; + const std::string actual_var_name = actual_vars[i].val; + VALIDATE_TEST(actual_var_name.compare(expected_var_name) == 0, all_tests_passed, test_idx++); + + // Validate storage type + const auto expected_var_type = _storages[i]; + const auto actual_var_type = actual_vars[i].type; + VALIDATE_TEST(actual_var_type == expected_var_type, all_tests_passed, test_idx++); + } + return all_tests_passed; + } + + std::string name() override + { + return "CLTensorArgumentStoragesUsedTest"; + } + +private: + std::vector<TensorStorageType> _storages{}; + std::vector<std::string> _expected_vars{}; +}; + +class CLTensorArgumentComponentsUsedPassByValueTrueDynamicDimTrueTest : public ITest +{ +public: + const DataType dt = DataType::Fp32; + const TensorShape shape = TensorShape({ { -1, -1, 3, 1, 2 } }); + const std::string tensor_name = "src"; + + CLTensorArgumentComponentsUsedPassByValueTrueDynamicDimTrueTest() + { + _components.push_back(TensorComponentType::Dim0); + _components.push_back(TensorComponentType::Dim2); + _components.push_back(TensorComponentType::Dim3); + _components.push_back(TensorComponentType::Dim1xDim2); + _components.push_back(TensorComponentType::OffsetFirstElement); + _components.push_back(TensorComponentType::Stride1); + _components.push_back(TensorComponentType::Stride2); + _components.push_back(TensorComponentType::Stride3); + _components.push_back(TensorComponentType::OffsetFirstElement); // Repeat the query. The TensorArgument should not create a new variable + _components.push_back(TensorComponentType::Stride1); // Repeat the query. The TensorArgument should not create a new variable + + _expected_vars.push_back("src_dim0"); + _expected_vars.push_back("src_dim1xdim2"); + _expected_vars.push_back("src_offset_first_element"); + _expected_vars.push_back("src_stride1"); + _expected_vars.push_back("src_stride2"); + _expected_vars.push_back("src_stride3"); + } + + bool run() override + { + // The status of this variable can change in VALIDATE_TEST() + bool all_tests_passed = true; + + const TensorInfo info(dt, shape, TensorDataLayout::Nhwc, 1); + + const size_t num_components = _components.size(); + + int32_t test_idx = 0; + + CLTensorArgument arg(tensor_name, info, true /* return_dims_by_value */); + for(size_t i = 0; i < num_components; ++i) + { + arg.component(_components[i]); + } + + const auto actual_vars = arg.components(); + + const size_t num_vars = _expected_vars.size(); + + VALIDATE_ON_MSG(actual_vars.size() == num_vars, "The number of variables must match the number of expected variables"); + + // Since the dimensions are passed by value, we expect only the variables for the strides + for(size_t i = 0; i < num_vars; ++i) + { + // Validate variable name + const std::string expected_var_name = _expected_vars[i]; + const std::string actual_var_name = actual_vars[i]->tile().name(); + VALIDATE_TEST(actual_var_name.compare(expected_var_name) == 0, all_tests_passed, test_idx++); + + // Validate data type + const DataType expected_var_type = DataType::Int32; + const DataType actual_var_type = actual_vars[i]->tile().info().data_type(); + VALIDATE_TEST(actual_var_type == expected_var_type, all_tests_passed, test_idx++); + + // Validate tile shape + const int32_t actual_var_width = actual_vars[i]->tile().info().width(); + const int32_t actual_var_height = actual_vars[i]->tile().info().height(); + + VALIDATE_TEST(actual_var_height == 1, all_tests_passed, test_idx++); + VALIDATE_TEST(actual_var_width == 1, all_tests_passed, test_idx++); + } + return all_tests_passed; + } + + std::string name() override + { + return "CLTensorArgumentComponentsUsedPassByValueTrueDynamicDimTrueTest"; + } + +private: + std::vector<TensorComponentType> _components{}; + std::vector<std::string> _expected_vars{}; +}; +} // namespace ckw + +#endif // CKW_VALIDATION_TESTS_CLTENSORARGUMENTTEST_H diff --git a/compute_kernel_writer/validation/tests/CLTileTest.hpp b/compute_kernel_writer/validation/tests/CLTileTest.hpp new file mode 100644 index 0000000000..a95a11ace7 --- /dev/null +++ b/compute_kernel_writer/validation/tests/CLTileTest.hpp @@ -0,0 +1,467 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef CKW_VALIDATION_TESTS_CLTILETEST_HPP +#define CKW_VALIDATION_TESTS_CLTILETEST_HPP + +#include "common/Common.h" +#include "src/Helpers.h" +#include "src/cl/CLTile.h" + +#include <string> +#include <vector> + +namespace ckw +{ +class CLTileInternalVariableNamesTest : public ITest +{ +public: + const int32_t width = 4; + const int32_t height = 4; + const DataType dt = DataType::Fp32; + + CLTileInternalVariableNamesTest() + { + _tile_name.push_back("dst"); + _tile_name.push_back("_G0_dst"); + _tile_name.push_back("_SRC"); + } + + bool run() override + { + // The status of this variable can change in VALIDATE_TEST() + bool all_tests_passed = true; + + const TileInfo info(dt, height, width); + + int32_t test_idx = 0; + for(const auto &tile_name : _tile_name) + { + const CLTile tile(tile_name, info); + const auto vars = tile.all(); + + for(int32_t y = 0; y < height; ++y) + { + const std::string expected_var_name = tile_name + "__" + std::to_string(y); + const std::string actual_var_name = vars[y].str; + VALIDATE_TEST(actual_var_name.compare(expected_var_name) == 0, all_tests_passed, test_idx++); + } + } + return all_tests_passed; + } + + std::string name() override + { + return "CLTileInternalVariableNamesTest"; + } + +private: + std::vector<std::string> _tile_name{}; +}; + +class CLTileInternalNumVariablesTest : public ITest +{ +public: + CLTileInternalNumVariablesTest() + { + _width.push_back(4); + _width.push_back(1); + _width.push_back(16); + + _height.push_back(1); + _height.push_back(5); + _height.push_back(3); + } + + bool run() override + { + VALIDATE_ON_MSG(_width.size() == _height.size(), "The number of widths and heights does not match"); + + // The status of this variable can change in VALIDATE_TEST() + bool all_tests_passed = true; + + const size_t num_dims = _width.size(); + + int32_t test_idx = 0; + for(size_t i = 0; i < num_dims; ++i) + { + const int32_t width = _width[i]; + const int32_t height = _height[i]; + const TileInfo info(DataType::Fp32, height, width); + const CLTile tile("src", info); + const auto vars = tile.all(); + const int32_t num_vars = vars.size(); + + // We expect the number of variables to match the heigth of the tile + VALIDATE_TEST(num_vars == height, all_tests_passed, test_idx++); + } + return all_tests_passed; + } + + std::string name() override + { + return "CLTileInternalNumVariablesTest"; + } + +private: + std::vector<int32_t> _width{}; + std::vector<int32_t> _height{}; +}; + +class CLTileAccessScalarVariableTest : public ITest +{ +public: + const std::string tile_name = "src"; + const int32_t width = 16; + const int32_t height = 8; + const DataType dt = DataType::Fp32; + + CLTileAccessScalarVariableTest() + { + _x_coord.push_back(4); + _x_coord.push_back(1); + _x_coord.push_back(15); + _x_coord.push_back(10); + + _y_coord.push_back(1); + _y_coord.push_back(5); + _y_coord.push_back(3); + _y_coord.push_back(4); + } + + bool run() override + { + const TileInfo info(dt, height, width); + const CLTile tile(tile_name, info); + + VALIDATE_ON_MSG(_x_coord.size() == _y_coord.size(), "The number of x-coords and y-coords does not match"); + + // The status of this variable can change in VALIDATE_TEST() + bool all_tests_passed = true; + + const size_t num_coords = _x_coord.size(); + + int32_t test_idx = 0; + for(size_t i = 0; i < num_coords; ++i) + { + const int32_t x_coord = _x_coord[i]; + const int32_t y_coord = _y_coord[i]; + + const TileVariable var = tile.scalar(y_coord, x_coord); + + const std::string actual_var_name = var.str; + std::string expected_var_name = tile_name; + expected_var_name += "__" + std::to_string(y_coord); + expected_var_name += ".s" + dec_to_hex_as_string(x_coord); + + VALIDATE_TEST(actual_var_name.compare(expected_var_name) == 0, all_tests_passed, test_idx++); + } + return all_tests_passed; + } + + std::string name() override + { + return "CLTileAccessScalarVariableTest"; + } + +private: + std::vector<int32_t> _x_coord{}; + std::vector<int32_t> _y_coord{}; +}; + +class CLTileAccessScalarVariableBroadcastXTest : public ITest +{ +public: + const std::string tile_name = "src"; + const int32_t height = 8; + const DataType dt = DataType::Fp32; + + CLTileAccessScalarVariableBroadcastXTest() + { + _width.push_back(1); + _width.push_back(2); + _width.push_back(3); + + _x_coord.push_back(4); + _x_coord.push_back(5); + _x_coord.push_back(6); + + _y_coord.push_back(1); + _y_coord.push_back(3); + _y_coord.push_back(2); + } + + bool run() override + { + VALIDATE_ON_MSG(_width.size() == _y_coord.size(), "The number of widths and y-coords does not match"); + VALIDATE_ON_MSG(_x_coord.size() == _y_coord.size(), "The number of x-coords and y-coords does not match"); + + // The status of this variable can change in VALIDATE_TEST() + bool all_tests_passed = true; + + const size_t num_coords = _x_coord.size(); + + int32_t test_idx = 0; + for(size_t i = 0; i < num_coords; ++i) + { + const int32_t width = _width[i]; + const int32_t x_coord = _x_coord[i]; + const int32_t y_coord = _y_coord[i]; + + const int32_t x_coord_clamped = clamp(x_coord, static_cast<int32_t>(0), width - 1); + + const TileInfo info(dt, height, width); + const CLTile tile(tile_name, info); + + const TileVariable var = tile.scalar(y_coord, x_coord); + + const std::string actual_var_name = var.str; + std::string expected_var_name = tile_name; + expected_var_name += "__" + std::to_string(y_coord); + if(width != 1) + { + expected_var_name += ".s" + dec_to_hex_as_string(x_coord_clamped); + } + + VALIDATE_TEST(actual_var_name.compare(expected_var_name) == 0, all_tests_passed, test_idx++); + } + return all_tests_passed; + } + + std::string name() override + { + return "CLTileAccessScalarVariableBroadcastXTest"; + } + +private: + std::vector<int32_t> _width{}; + std::vector<int32_t> _x_coord{}; + std::vector<int32_t> _y_coord{}; +}; + +class CLTileAccessScalarVariableBroadcastYTest : public ITest +{ +public: + const std::string tile_name = "src"; + const int32_t width = 8; + const DataType dt = DataType::Fp32; + + CLTileAccessScalarVariableBroadcastYTest() + { + _height.push_back(1); + _height.push_back(2); + _height.push_back(3); + + _x_coord.push_back(4); + _x_coord.push_back(5); + _x_coord.push_back(6); + + _y_coord.push_back(3); + _y_coord.push_back(4); + _y_coord.push_back(5); + } + + bool run() override + { + VALIDATE_ON_MSG(_height.size() == _y_coord.size(), "The number of widths and y-coords does not match"); + VALIDATE_ON_MSG(_x_coord.size() == _y_coord.size(), "The number of x-coords and y-coords does not match"); + + // The status of this variable can change in VALIDATE_TEST() + bool all_tests_passed = true; + + const size_t num_coords = _x_coord.size(); + + int32_t test_idx = 0; + for(size_t i = 0; i < num_coords; ++i) + { + const int32_t height = _height[i]; + const int32_t x_coord = _x_coord[i]; + const int32_t y_coord = _y_coord[i]; + + const int32_t y_coord_clamped = clamp(y_coord, static_cast<int32_t>(0), height - 1); + + const TileInfo info(dt, height, width); + const CLTile tile(tile_name, info); + + const TileVariable var = tile.scalar(y_coord, x_coord); + + const std::string actual_var_name = var.str; + std::string expected_var_name = tile_name; + if(height != 1) + { + expected_var_name += "__" + std::to_string(y_coord_clamped); + } + + if(width != 1) + { + expected_var_name += ".s" + dec_to_hex_as_string(x_coord); + } + + VALIDATE_TEST(actual_var_name.compare(expected_var_name) == 0, all_tests_passed, test_idx++); + } + return all_tests_passed; + } + + std::string name() override + { + return "CLTileAccessScalarVariableBroadcastYTest"; + } + +private: + std::vector<int32_t> _height{}; + std::vector<int32_t> _x_coord{}; + std::vector<int32_t> _y_coord{}; +}; + +class CLTileAccessVectorVariablesTest : public ITest +{ +public: + const std::string tile_name = "src"; + const int32_t width = 8; + const DataType dt = DataType::Fp32; + + CLTileAccessVectorVariablesTest() + { + _heights.push_back(1); + _heights.push_back(2); + _heights.push_back(3); + } + + bool run() override + { + // The status of this variable can change in VALIDATE_TEST() + bool all_tests_passed = true; + + int32_t test_idx = 0; + for(const auto &height : _heights) + { + const TileInfo info(dt, height, width); + const CLTile tile(tile_name, info); + + for(int32_t row = 0; row < height; ++row) + { + const TileVariable var = tile.vector(row); + + const std::string actual_var_name = var.str; + std::string expected_var_name = tile_name; + if(height != 1) + { + expected_var_name += "__" + std::to_string(row); + } + + VALIDATE_TEST(actual_var_name.compare(expected_var_name) == 0, all_tests_passed, test_idx++); + } + } + return all_tests_passed; + } + + std::string name() override + { + return "CLTileAccessVectorVariablesTest"; + } + +private: + std::vector<int32_t> _heights{}; +}; + +class CLTileAccessSubVectorVariablesTest : public ITest +{ +public: + const std::string tile_name = "src"; + const int32_t width = 8; + const int32_t height = 3; + const DataType dt = DataType::Fp32; + + CLTileAccessSubVectorVariablesTest() + { + _subwidths.push_back(1); + _subwidths.push_back(2); + _subwidths.push_back(3); + _subwidths.push_back(4); + _offsets.push_back(1); + _offsets.push_back(3); + _offsets.push_back(4); + } + + bool run() override + { + // The status of this variable can change in VALIDATE_TEST() + bool all_tests_passed = true; + + size_t test_idx = 0; + + for(auto &col_start : _offsets) + { + for(const auto &subwidth : _subwidths) + { + const TileInfo info(dt, height, width); + const CLTile tile(tile_name, info); + + for(int32_t row = 0; row < height; ++row) + { + std::string expected_var_name = tile_name; + if(height != 1) + { + expected_var_name += "__" + std::to_string(row); + } + + if(width != 1) + { + expected_var_name += ".s"; + } + + int32_t col = col_start; + for(; col < col_start + subwidth - 1; ++col) + { + if(width != 1) + { + expected_var_name += dec_to_hex_as_string(col); + } + } + + if(width != 1) + { + expected_var_name += dec_to_hex_as_string(col); + } + + const std::string actual_var_name = tile.vector(row, col_start, subwidth).str; + VALIDATE_TEST(actual_var_name.compare(expected_var_name) == 0, all_tests_passed, test_idx++); + } + } + } + return all_tests_passed; + } + + std::string name() override + { + return "CLTileAccessSubVectorVariablesTest"; + } + +private: + std::vector<int32_t> _subwidths{}; + std::vector<int32_t> _offsets{}; +}; +} // namespace ckw + +#endif // CKW_VALIDATION_TESTS_CLTILETEST_HPP diff --git a/compute_kernel_writer/validation/tests/TensorBitMaskTest.h b/compute_kernel_writer/validation/tests/TensorBitMaskTest.h new file mode 100644 index 0000000000..759d926d18 --- /dev/null +++ b/compute_kernel_writer/validation/tests/TensorBitMaskTest.h @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef CKW_TESTS_TENSORBITMASKTEST_H +#define CKW_TESTS_TENSORBITMASKTEST_H + +#include "ckw/TensorInfo.h" +#include "ckw/types/TensorComponentType.h" +#include "common/Common.h" +#include "src/types/TensorComponentType.h" + +#include <vector> + +namespace ckw +{ +class TensorBitMaskTrueTest : public ITest +{ +public: + TensorBitMaskTrueTest() + { + _component.push_back(TensorComponentType::Dim0); + _component.push_back(TensorComponentType::Dim1); + _component.push_back(TensorComponentType::Dim2); + _component.push_back(TensorComponentType::Dim3); + _component.push_back(TensorComponentType::Dim4); + _component.push_back(TensorComponentType::Stride0); + _component.push_back(TensorComponentType::Stride1); + _component.push_back(TensorComponentType::Stride2); + _component.push_back(TensorComponentType::Stride3); + _component.push_back(TensorComponentType::Stride4); + _component.push_back(TensorComponentType::Dim1xDim2); + _component.push_back(TensorComponentType::Dim1xDim2xDim3); + _component.push_back(TensorComponentType::Dim2xDim3); + _component.push_back(TensorComponentType::OffsetFirstElement); + + _bitmask.push_back(TensorComponentBitmask::Dimension); + _bitmask.push_back(TensorComponentBitmask::Dimension); + _bitmask.push_back(TensorComponentBitmask::Dimension); + _bitmask.push_back(TensorComponentBitmask::Dimension); + _bitmask.push_back(TensorComponentBitmask::Dimension); + _bitmask.push_back(TensorComponentBitmask::Stride); + _bitmask.push_back(TensorComponentBitmask::Stride); + _bitmask.push_back(TensorComponentBitmask::Stride); + _bitmask.push_back(TensorComponentBitmask::Stride); + _bitmask.push_back(TensorComponentBitmask::Stride); + _bitmask.push_back(TensorComponentBitmask::FoldedDimensions); + _bitmask.push_back(TensorComponentBitmask::FoldedDimensions); + _bitmask.push_back(TensorComponentBitmask::FoldedDimensions); + _bitmask.push_back(TensorComponentBitmask::OffsetFirstElement); + } + + bool run() override + { + // The status of this variable can change in VALIDATE_TEST() + bool all_tests_passed = true; + + VALIDATE_ON_MSG(_component.size() == _bitmask.size(), + "The number of layouts and components does not match"); + const size_t num_tests = _component.size(); + for(size_t i = 0; i < num_tests; ++i) + { + const TensorComponentType component = _component[i]; + const TensorComponentBitmask bitmask = _bitmask[i]; + const bool out = static_cast<uint32_t>(component) & static_cast<uint32_t>(bitmask); + VALIDATE_TEST(out == true, all_tests_passed, i); + } + return all_tests_passed; + } + + std::string name() override + { + return "TensorBitMaskTrueTest"; + } + +private: + std::vector<TensorComponentType> _component{}; + std::vector<TensorComponentBitmask> _bitmask{}; +}; + +class TensorBitMaskFalseTest : public ITest +{ +public: + TensorBitMaskFalseTest() + { + _component.push_back(TensorComponentType::Dim0); + _component.push_back(TensorComponentType::Dim1); + _component.push_back(TensorComponentType::Dim2); + _component.push_back(TensorComponentType::Dim3); + _component.push_back(TensorComponentType::Dim4); + _component.push_back(TensorComponentType::Dim0); + _component.push_back(TensorComponentType::Dim1); + _component.push_back(TensorComponentType::Dim2); + _component.push_back(TensorComponentType::Dim3); + _component.push_back(TensorComponentType::Dim4); + _component.push_back(TensorComponentType::Dim0); + _component.push_back(TensorComponentType::Dim1); + _component.push_back(TensorComponentType::Dim2); + _component.push_back(TensorComponentType::Dim3); + _component.push_back(TensorComponentType::Dim4); + _component.push_back(TensorComponentType::Stride0); + _component.push_back(TensorComponentType::Stride1); + _component.push_back(TensorComponentType::Stride2); + _component.push_back(TensorComponentType::Stride3); + _component.push_back(TensorComponentType::Stride4); + _component.push_back(TensorComponentType::Stride0); + _component.push_back(TensorComponentType::Stride1); + _component.push_back(TensorComponentType::Stride2); + _component.push_back(TensorComponentType::Stride3); + _component.push_back(TensorComponentType::Stride4); + _component.push_back(TensorComponentType::Stride0); + _component.push_back(TensorComponentType::Stride1); + _component.push_back(TensorComponentType::Stride2); + _component.push_back(TensorComponentType::Stride3); + _component.push_back(TensorComponentType::Stride4); + _component.push_back(TensorComponentType::Dim1xDim2); + _component.push_back(TensorComponentType::Dim1xDim2xDim3); + _component.push_back(TensorComponentType::Dim2xDim3); + _component.push_back(TensorComponentType::Dim1xDim2); + _component.push_back(TensorComponentType::Dim1xDim2xDim3); + _component.push_back(TensorComponentType::Dim2xDim3); + _component.push_back(TensorComponentType::Dim1xDim2); + _component.push_back(TensorComponentType::Dim1xDim2xDim3); + _component.push_back(TensorComponentType::Dim2xDim3); + _component.push_back(TensorComponentType::OffsetFirstElement); + _component.push_back(TensorComponentType::OffsetFirstElement); + _component.push_back(TensorComponentType::OffsetFirstElement); + + _bitmask.push_back(TensorComponentBitmask::Stride); + _bitmask.push_back(TensorComponentBitmask::Stride); + _bitmask.push_back(TensorComponentBitmask::Stride); + _bitmask.push_back(TensorComponentBitmask::Stride); + _bitmask.push_back(TensorComponentBitmask::Stride); + _bitmask.push_back(TensorComponentBitmask::FoldedDimensions); + _bitmask.push_back(TensorComponentBitmask::FoldedDimensions); + _bitmask.push_back(TensorComponentBitmask::FoldedDimensions); + _bitmask.push_back(TensorComponentBitmask::FoldedDimensions); + _bitmask.push_back(TensorComponentBitmask::FoldedDimensions); + _bitmask.push_back(TensorComponentBitmask::OffsetFirstElement); + _bitmask.push_back(TensorComponentBitmask::OffsetFirstElement); + _bitmask.push_back(TensorComponentBitmask::OffsetFirstElement); + _bitmask.push_back(TensorComponentBitmask::OffsetFirstElement); + _bitmask.push_back(TensorComponentBitmask::OffsetFirstElement); + _bitmask.push_back(TensorComponentBitmask::Dimension); + _bitmask.push_back(TensorComponentBitmask::Dimension); + _bitmask.push_back(TensorComponentBitmask::Dimension); + _bitmask.push_back(TensorComponentBitmask::Dimension); + _bitmask.push_back(TensorComponentBitmask::Dimension); + _bitmask.push_back(TensorComponentBitmask::FoldedDimensions); + _bitmask.push_back(TensorComponentBitmask::FoldedDimensions); + _bitmask.push_back(TensorComponentBitmask::FoldedDimensions); + _bitmask.push_back(TensorComponentBitmask::FoldedDimensions); + _bitmask.push_back(TensorComponentBitmask::FoldedDimensions); + _bitmask.push_back(TensorComponentBitmask::OffsetFirstElement); + _bitmask.push_back(TensorComponentBitmask::OffsetFirstElement); + _bitmask.push_back(TensorComponentBitmask::OffsetFirstElement); + _bitmask.push_back(TensorComponentBitmask::OffsetFirstElement); + _bitmask.push_back(TensorComponentBitmask::OffsetFirstElement); + _bitmask.push_back(TensorComponentBitmask::Dimension); + _bitmask.push_back(TensorComponentBitmask::Dimension); + _bitmask.push_back(TensorComponentBitmask::Dimension); + _bitmask.push_back(TensorComponentBitmask::Stride); + _bitmask.push_back(TensorComponentBitmask::Stride); + _bitmask.push_back(TensorComponentBitmask::Stride); + _bitmask.push_back(TensorComponentBitmask::OffsetFirstElement); + _bitmask.push_back(TensorComponentBitmask::OffsetFirstElement); + _bitmask.push_back(TensorComponentBitmask::OffsetFirstElement); + _bitmask.push_back(TensorComponentBitmask::Dimension); + _bitmask.push_back(TensorComponentBitmask::Stride); + _bitmask.push_back(TensorComponentBitmask::FoldedDimensions); + } + + bool run() override + { + // The status of this variable can change in VALIDATE_TEST() + bool all_tests_passed = true; + + VALIDATE_ON_MSG(_component.size() == _bitmask.size(), + "The number of layouts and components does not match"); + const size_t num_tests = _component.size(); + for(size_t i = 0; i < num_tests; ++i) + { + const TensorComponentType component = _component[i]; + const TensorComponentBitmask bitmask = _bitmask[i]; + const bool out = static_cast<uint32_t>(component) & static_cast<uint32_t>(bitmask); + VALIDATE_TEST(out == false, all_tests_passed, i); + } + return all_tests_passed; + } + + std::string name() override + { + return "TensorBitMaskFalseTest"; + } + +private: + std::vector<TensorComponentType> _component{}; + std::vector<TensorComponentBitmask> _bitmask{}; +}; +} // namespace ckw + +#endif // CKW_TESTS_TENSORBITMASKTEST_H diff --git a/compute_kernel_writer/validation/tests/UtilsTest.h b/compute_kernel_writer/validation/tests/UtilsTest.h new file mode 100644 index 0000000000..a335a48f81 --- /dev/null +++ b/compute_kernel_writer/validation/tests/UtilsTest.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef CKW_TESTS_UTILSTEST_H +#define CKW_TESTS_UTILSTEST_H + +#include "ckw/TensorInfo.h" +#include "ckw/types/TensorDataLayout.h" +#include "common/Common.h" +#include "src/TensorUtils.h" + +#include <vector> + +namespace ckw +{ +class UtilsTest : public ITest +{ +public: + UtilsTest() + { + _layout.push_back(TensorDataLayout::Nhwc); + _layout.push_back(TensorDataLayout::Nhwc); + _layout.push_back(TensorDataLayout::Nhwc); + _layout.push_back(TensorDataLayout::Nhwc); + _layout.push_back(TensorDataLayout::Ndhwc); + _layout.push_back(TensorDataLayout::Ndhwc); + _layout.push_back(TensorDataLayout::Ndhwc); + _layout.push_back(TensorDataLayout::Ndhwc); + _layout.push_back(TensorDataLayout::Ndhwc); + + _component.push_back(TensorDataLayoutComponent::N); + _component.push_back(TensorDataLayoutComponent::H); + _component.push_back(TensorDataLayoutComponent::W); + _component.push_back(TensorDataLayoutComponent::C); + _component.push_back(TensorDataLayoutComponent::N); + _component.push_back(TensorDataLayoutComponent::D); + _component.push_back(TensorDataLayoutComponent::H); + _component.push_back(TensorDataLayoutComponent::W); + _component.push_back(TensorDataLayoutComponent::C); + + _expected.push_back(TensorComponentType::Dim3); + _expected.push_back(TensorComponentType::Dim2); + _expected.push_back(TensorComponentType::Dim1); + _expected.push_back(TensorComponentType::Dim0); + _expected.push_back(TensorComponentType::Dim4); + _expected.push_back(TensorComponentType::Dim3); + _expected.push_back(TensorComponentType::Dim2); + _expected.push_back(TensorComponentType::Dim1); + _expected.push_back(TensorComponentType::Dim0); + } + + bool run() override + { + // The status of this variable can change in VALIDATE_TEST() + bool all_tests_passed = true; + + VALIDATE_ON_MSG(_layout.size() == _component.size(), "The number of layouts and components does not match"); + VALIDATE_ON_MSG(_layout.size() == _expected.size(), + "The number of layouts and expected outputs does not match"); + const size_t num_tests = _layout.size(); + for(size_t i = 0; i < num_tests; ++i) + { + const TensorDataLayout layout = _layout[i]; + const TensorDataLayoutComponent component = _component[i]; + const TensorComponentType expected = _expected[i]; + const TensorComponentType out = get_tensor_dimension(layout, component); + VALIDATE_TEST(out == expected, all_tests_passed, i); + } + return all_tests_passed; + } + + std::string name() override + { + return "UtilsTest"; + } + +private: + std::vector<TensorDataLayout> _layout{}; + std::vector<TensorDataLayoutComponent> _component{}; + std::vector<TensorComponentType> _expected{}; +}; +} // namespace ckw + +#endif // CKW_TESTS_UTILSTEST_H diff --git a/compute_kernel_writer/validation/tests/common/Common.h b/compute_kernel_writer/validation/tests/common/Common.h new file mode 100644 index 0000000000..8573c42b88 --- /dev/null +++ b/compute_kernel_writer/validation/tests/common/Common.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef COMPUTE_KERNEL_WRITER_TEST_COMMON_COMMON_H +#define COMPUTE_KERNEL_WRITER_TEST_COMMON_COMMON_H + +#include <cassert> +#include <iostream> +#include <string> + +namespace ckw +{ +#define VALIDATE_ON_MSG(exp, msg) assert(((void)msg, exp)) + +#define VALIDATE_TEST(exp, all_tests_passed, id_test) \ + do \ + { \ + if((exp) == true) \ + { \ + all_tests_passed &= true; \ + const std::string msg = "TEST " + std::to_string((id_test)) + ": [PASSED]"; \ + std::cout << msg << std::endl; \ + } \ + else \ + { \ + all_tests_passed &= false; \ + const std::string msg = "TEST " + std::to_string((id_test)) + ": [FAILED]"; \ + std::cout << msg << std::endl; \ + } \ + } while(false) + +class ITest +{ +public: + virtual ~ITest() = default; + + /** Method to run the test + * + * @return it returns true if all tests passed + */ + virtual bool run() = 0; + + /** Name of the test + * + * @return it returns the name of the test + */ + virtual std::string name() = 0; +}; +} // namespace ckw + +#endif /* COMPUTE_KERNEL_WRITER_TEST_COMMON_COMMON_H */ diff --git a/compute_kernel_writer/validation/tests/common/KernelWriterInterceptor.h b/compute_kernel_writer/validation/tests/common/KernelWriterInterceptor.h new file mode 100644 index 0000000000..89bb76e37f --- /dev/null +++ b/compute_kernel_writer/validation/tests/common/KernelWriterInterceptor.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2023 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef CKW_VALIDATION_TESTS_COMMON_KERNELWRITERINTERCEPTOR_H +#define CKW_VALIDATION_TESTS_COMMON_KERNELWRITERINTERCEPTOR_H + +#include <string> +#include <utility> + +namespace ckw +{ + +/** This class provides the ability to capture only the code changed after a point + * and compares that part of code with the expected value. + * + * It is useful for testing purpose when a particular sequence of instructions is interested + * while the rest of the initialization code is out of scope. + */ +template <typename T> +class KernelWriterInterceptor : public T +{ +public: + template <typename... TArgs> + KernelWriterInterceptor(const TArgs &&...args) + : T(std::forward<TArgs>(args)...) + { + } + + /** Mark this point in the source code as the start position to capture. + * Only source code added after this function is considered when check_add_code is called. + */ + void start_capture_code() + { + _start_code = this->body_source_code(); + } + + /** Compare the source code added after start_capture_code is called the the specified expected code. */ + bool check_added_code(const std::string &expected_added_code) + { + const auto &end_code = this->body_source_code(); + + // Code can only grow over time. + if(end_code.length() < _start_code.length()) + { + return false; + } + + // New code must be added to the source code without changing the already existed code. + if(end_code.substr(0, _start_code.length()) != _start_code) + { + return false; + } + + // The newly added code must match the expected value. + if(end_code.substr(_start_code.length(), end_code.length() - _start_code.length()) != expected_added_code) + { + return false; + } + + return true; + } + +private: + std::string _start_code{}; +}; + +} // namespace ckw + +#endif // CKW_VALIDATION_TESTS_COMMON_KERNELWRITERINTERCEPTOR_H |