diff --git a/catch/hipTestMain/config/config_amd_linux b/catch/hipTestMain/config/config_amd_linux index d8a69ab302..5c4cce0510 100644 --- a/catch/hipTestMain/config/config_amd_linux +++ b/catch/hipTestMain/config/config_amd_linux @@ -251,6 +251,12 @@ "Unit_hipGraphicsUnregisterResource_Negative_Parameters", "SWDEV-443760: This test fails when device memory is used for kernel args", "Unit_hipGraphExecMemcpyNodeSetParams1D_Negative", + "=== Below tests fail in external CI for PR https://github.com/ROCm-Developer-Tools/hip-tests/pull/356 ===", + "Unit_Device_Complex_Unary_Negative_Parameters_RTC", + "Unit_Device_Complex_Binary_Negative_Parameters_RTC", + "Unit_Device_Complex_hipCfma_Negative_Parameters_RTC", + "Unit_Device_make_Complex_Negative_Parameters_RTC", + "Unit_Device_Complex_Cast_Negative_Parameters_RTC", #endif #if defined VEGA20 "=== SWDEV-419112 Below tests fail in stress test on 29/08/23 ===", @@ -303,7 +309,6 @@ "Unit_hipFreeMipmappedArray_Negative_DoubleFree", "Unit_hipFreeMipmappedArrayMultiTArray - char", "Unit_hipFreeMipmappedArrayMultiTArray - int", - "Unit_Printf_Negative", "Unit_Multi_Grid_Group_Getters_Positive_Basic", "Unit_Multi_Grid_Group_Getters_Positive_Base_Type", "Unit_Multi_Grid_Group_Getters_Positive_Non_Member_Functions", @@ -340,16 +345,6 @@ "Unit_Warp_Ballot_Positive_Basic", "Unit_Warp_Vote_Any_Positive_Basic", "Unit_Warp_Vote_All_Positive_Basic", - "Unit_Device_memcpy_Negative", - "Unit_Device_memset_Negative", - "Unit_Device_Complex_make_Negative", - "Unit_Device_Complex_Cast_Negative", - "Unit_Device_Complex_Unary_float_Negative", - "Unit_Device_Complex_Unary_double_Negative", - "Unit_Device_Complex_Binary_float_Negative", - "Unit_Device_Complex_Binary_double_Negative", - "Unit_Device_Complex_hipCfma_Negative", - "Unit_Device__hip_hc_8pk_Negative", #endif #if defined MI2XX "Unit_hipStreamPerThread_DeviceReset_1", @@ -437,7 +432,6 @@ "Unit_hipFreeMipmappedArray_Negative_DoubleFree", "Unit_hipFreeMipmappedArrayMultiTArray - char", "Unit_hipFreeMipmappedArrayMultiTArray - int", - "Unit_Printf_Negative", "Unit_Multi_Grid_Group_Getters_Positive_Basic", "Unit_Multi_Grid_Group_Getters_Positive_Base_Type", "Unit_Multi_Grid_Group_Getters_Positive_Non_Member_Functions", @@ -474,16 +468,6 @@ "Unit_Warp_Ballot_Positive_Basic", "Unit_Warp_Vote_Any_Positive_Basic", "Unit_Warp_Vote_All_Positive_Basic", - "Unit_Device_memcpy_Negative", - "Unit_Device_memset_Negative", - "Unit_Device_Complex_make_Negative", - "Unit_Device_Complex_Cast_Negative", - "Unit_Device_Complex_Unary_float_Negative", - "Unit_Device_Complex_Unary_double_Negative", - "Unit_Device_Complex_Binary_float_Negative", - "Unit_Device_Complex_Binary_double_Negative", - "Unit_Device_Complex_hipCfma_Negative", - "Unit_Device__hip_hc_8pk_Negative", "=== SWDEV-439298: Below test failing in CQE staging ===", "Unit_hipCGMultiGridGroupType_Barrier", #endif @@ -520,7 +504,6 @@ "Unit_hipFreeMipmappedArray_Negative_DoubleFree", "Unit_hipFreeMipmappedArrayMultiTArray - char", "Unit_hipFreeMipmappedArrayMultiTArray - int", - "Unit_Printf_Negative", "Unit_Multi_Grid_Group_Getters_Positive_Basic", "Unit_Multi_Grid_Group_Getters_Positive_Base_Type", "Unit_Multi_Grid_Group_Getters_Positive_Non_Member_Functions", @@ -533,16 +516,6 @@ "Unit_Coalesced_Group_Shfl_Positive_Basic - unsigned long long", "Unit_Coalesced_Group_Shfl_Positive_Basic - float", "Unit_Coalesced_Group_Shfl_Positive_Basic - double", - "Unit_Device_memcpy_Negative", - "Unit_Device_memset_Negative", - "Unit_Device_Complex_make_Negative", - "Unit_Device_Complex_Cast_Negative", - "Unit_Device_Complex_Unary_float_Negative", - "Unit_Device_Complex_Unary_double_Negative", - "Unit_Device_Complex_Binary_float_Negative", - "Unit_Device_Complex_Binary_double_Negative", - "Unit_Device_Complex_hipCfma_Negative", - "Unit_Device__hip_hc_8pk_Negative", #endif #if defined NAVI3X "=== Below tests soft hang in stress test on 13/09/23 ===", diff --git a/catch/hipTestMain/config/config_amd_windows b/catch/hipTestMain/config/config_amd_windows index e289ed4c3c..9b2639734a 100644 --- a/catch/hipTestMain/config/config_amd_windows +++ b/catch/hipTestMain/config/config_amd_windows @@ -286,6 +286,14 @@ "Unit_atomicExch_system_Positive_Host_And_Peer_GPUs - unsigned long long", "Unit_atomicExch_system_Positive_Host_And_Peer_GPUs - float", "Unit_atomicExch_system_Positive_Host_And_Peer_GPUs - double", + "=== SWDEV-435667: Below tests failing randomly in stress test on 08/12/23 ===", + "Unit_hipMemPoolSetAccess_Negative_Parameters", + "Unit_hipMallocMipmappedArray_Negative_Parameters", + "Unit_hipFreeMipmappedArray_Negative_Parameters", + "Unit_hipGetMipmappedArrayLevel_Negative_Parameters", + "Unit_hipMipmappedArrayCreate_Negative_Parameters", + "Unit_hipMipmappedArrayDestroy_Negative_Parameters", + "Unit_hipMipmappedArrayGetLevel_Negative_Parameters", "SWDEV-438524: Below tests taking long time to run in stress test on 15/12/23 ===", "Unit_Coalesced_Group_Shfl_Up_Positive_Basic - int", "Unit_Coalesced_Group_Shfl_Up_Positive_Basic - unsigned int", @@ -314,17 +322,6 @@ "SWDEV-438524: Below tests causing TDR & machine down in stress test on 15/12/23 ===", "Unit_hipExtModuleLaunchKernel_Functional", "Unit_hipExtLaunchKernelGGL_Functional", - "SWDEV-438524:Below tests failed in stress test on 15/12/23 ===", - "Unit_Device_memcpy_Negative", - "Unit_Device_memset_Negative", - "Unit_Device_Complex_make_Negative", - "Unit_Device_Complex_Cast_Negative", - "Unit_Device_Complex_Unary_float_Negative", - "Unit_Device_Complex_Unary_double_Negative", - "Unit_Device_Complex_Binary_float_Negative", - "Unit_Device_Complex_Binary_double_Negative", - "Unit_Device_Complex_hipCfma_Negative", - "Unit_Device__hip_hc_8pk_Negative", "SWDEV-413997: VMM test still failing in windows", "Unit_hipMemSetAccess_ChangeAccessProp", "SWDEV-444265: This test fails in Phoenix", @@ -340,6 +337,25 @@ "Unit_hipMemSetAccess_Vmm2VMMMemCpy", "SWDEV-444031: This test fails in Navi32 MGPU", "Unit_hipMemSetAccess_Multithreaded", + "=== Below test fails in external CI for PR https://github.com/ROCm-Developer-Tools/hip-tests/pull/274 ===", + "Unit_Printf_flags_Sanity_Positive", + "Unit_Printf_length_Sanity_Positive", + "=== Below tests fail in external CI for PR https://github.com/ROCm-Developer-Tools/hip-tests/pull/356 ===", + "Unit_Device_Complex_Unary_Negative_Parameters_RTC", + "Unit_Device_Complex_Binary_Negative_Parameters_RTC", + "Unit_Device_Complex_hipCfma_Negative_Parameters_RTC", + "Unit_Device_make_Complex_Negative_Parameters_RTC", + "Unit_Device_Complex_Cast_Negative_Parameters_RTC", + "=== Below tests are failing PSDB ===", + "Unit_hipGraphMem_Alloc_Free_NodeGetParams_Functional_3", + "Unit_hipGraphAddMemAllocNode_Positive_FreeInGraph", + "Unit_hipFreeAsync_Negative_Parameters", + "Unit_hipMallocMipmappedArray_DiffSizes", + "Unit_hipMallocMipmappedArray_MultiThread", + "Unit_hipMallocMipmappedArray_Negative_InvalidFlags", + "Unit_hipGetMipmappedArrayLevel_Negative", + "Unit_hipFreeMipmappedArray_Negative_DoubleFree", + "Unit_hipFreeMipmappedArrayMultiTArray - int", #endif "End of json" ] diff --git a/catch/hipTestMain/config/config_nvidia_linux.json b/catch/hipTestMain/config/config_nvidia_linux.json index b0bf56894d..f5d75df1b1 100644 --- a/catch/hipTestMain/config/config_nvidia_linux.json +++ b/catch/hipTestMain/config/config_nvidia_linux.json @@ -19,7 +19,7 @@ "Unit_ChannelDescriptor_Positive_Basic_4D - ulong4", "Unit_ChannelDescriptor_Positive_Basic_4D - long4", "=== Below test fails in external CI for PR https://github.com/ROCm-Developer-Tools/hip-tests/pull/38 ===", - "Unit_hipFreeAsync_negative", + "Unit_hipFreeAsync_Negative_Parameters", "=== Below test fails in external CI for PR https://github.com/ROCm-Developer-Tools/hip-tests/pull/92 ===", "Unit_hipGetTexObjectResourceDesc_positive", "Unit_hipGetTexObjectResourceDesc_Negative_Parameters", @@ -52,6 +52,24 @@ "Unit_atomicExch_system_Positive_Host_And_GPU - int", "Unit_atomicExch_system_Positive_Host_And_GPU - unsigned int", "Unit_atomicExch_system_Positive_Host_And_GPU - unsigned long long", - "Unit_atomicExch_system_Positive_Host_And_GPU - float" + "Unit_atomicExch_system_Positive_Host_And_GPU - float", + "=== Below tests fail in external CI for PR https://github.com/ROCm-Developer-Tools/hip-tests/pull/356 ===", + "Unit_Device_Complex_Unary_Negative_Parameters_RTC", + "Unit_Device_Complex_Binary_Negative_Parameters_RTC", + "Unit_Device_Complex_hipCfma_Negative_Parameters_RTC", + "Unit_Device_make_Complex_Negative_Parameters_RTC", + "Unit_Device_Complex_Cast_Negative_Parameters_RTC", + "=== Below 2 tests are disabled due to defect EXSWHTEC-342 ===", + "Unit_hipDeviceSetLimit_Negative_Parameters", + "Unit_hipDeviceGetLimit_Negative_Parameters", + "=== Below tests are failing PSDB ===", + "Unit_hipGraphMem_Alloc_Free_NodeGetParams_Functional_3", + "Unit_hipMemPoolSetAccess_Negative_Parameters", + "Unit_hipMallocMipmappedArray_Negative_NumLevels", + "Unit_hipFreeMipmappedArray_Negative_Nullptr", + "Unit_hipFreeMipmappedArrayMultiTArray - int", + "Unit_hipFreeMipmappedArray_Negative_Parameters", + "Unit_hipCreateSurfaceObject_Negative_Parameters", + "Unit_hipDestroySurfaceObject_Negative_Parameters" ] } diff --git a/catch/hipTestMain/config/config_nvidia_windows.json b/catch/hipTestMain/config/config_nvidia_windows.json index 5f35a92780..4bd8c6dde7 100644 --- a/catch/hipTestMain/config/config_nvidia_windows.json +++ b/catch/hipTestMain/config/config_nvidia_windows.json @@ -15,6 +15,15 @@ "Unit_ChannelDescriptor_Positive_Basic_3D - ulong3", "Unit_ChannelDescriptor_Positive_Basic_3D - long3", "Unit_ChannelDescriptor_Positive_Basic_4D - ulong4", - "Unit_ChannelDescriptor_Positive_Basic_4D - long4" + "Unit_ChannelDescriptor_Positive_Basic_4D - long4", + "=== Below tests fail in external CI for PR https://github.com/ROCm-Developer-Tools/hip-tests/pull/356 ===", + "Unit_Device_Complex_Unary_Negative_Parameters_RTC", + "Unit_Device_Complex_Binary_Negative_Parameters_RTC", + "Unit_Device_Complex_hipCfma_Negative_Parameters_RTC", + "Unit_Device_make_Complex_Negative_Parameters_RTC", + "Unit_Device_Complex_Cast_Negative_Parameters_RTC", + "=== Below 2 tests are disabled due to defect EXSWHTEC-342 ===", + "Unit_hipDeviceSetLimit_Negative_Parameters", + "Unit_hipDeviceGetLimit_Negative_Parameters" ] } diff --git a/catch/hipTestMain/main.cc b/catch/hipTestMain/main.cc index 74096c4af4..109b0593fc 100644 --- a/catch/hipTestMain/main.cc +++ b/catch/hipTestMain/main.cc @@ -17,7 +17,7 @@ int main(int argc, char** argv) { using namespace Catch::clara; // clang-format off - auto cli = session.cli() + auto cli = session.cli() | Opt(cmd_options.iterations, "iterations") ["-I"]["--iterations"] ("Number of iterations used for performance tests (default: 1000)") @@ -32,6 +32,9 @@ int main(int argc, char** argv) { ("Show progress bar when running performance tests") | Opt(cmd_options.cg_extended_run, "cg_extened_run") ["-E"]["--cg-extended-run"] + ("TODO: Description goes here") + | Opt(cmd_options.cg_iterations, "cg_iterations") + ["-C"]["--cg-iterations"] ("Number of iterations used for cooperative groups sync tests (default: 5)") ; // clang-format on diff --git a/catch/include/cmd_options.hh b/catch/include/cmd_options.hh index 99e565491b..6caf7a0f48 100644 --- a/catch/include/cmd_options.hh +++ b/catch/include/cmd_options.hh @@ -26,8 +26,9 @@ struct CmdOptions { int iterations = 10; int warmups = 100; int cg_extended_run = 5; + int cg_iterations = 5; bool no_display = false; bool progress = false; }; -extern CmdOptions cmd_options; \ No newline at end of file +extern CmdOptions cmd_options; diff --git a/catch/include/cpu_grid.h b/catch/include/cpu_grid.h index 6f0b7edbca..8f7b37214f 100644 --- a/catch/include/cpu_grid.h +++ b/catch/include/cpu_grid.h @@ -78,6 +78,32 @@ struct CPUGrid { unsigned int thread_count_; }; +struct CPUMultiGrid { + CPUMultiGrid(const unsigned int num_grids, const dim3 grid_dims[], const dim3 block_dims[]) { + thread_count_ = 0; + grid_count_ = num_grids; + grids_.reserve(grid_count_); + for (int i = 0; i < grid_count_; i++) { + grids_.emplace_back(grid_dims[i], block_dims[i]); + thread_count_ += grids_[i].thread_count_; + } + } + + inline unsigned int thread0_rank_in_multi_grid(const unsigned int grid_rank) const { + unsigned int multi_grid_thread_rank_0 = 0; + unsigned int multi_grid_thread_count = 0; + for (int i = 0; i <= grid_rank; i++) { + multi_grid_thread_rank_0 = multi_grid_thread_count; + multi_grid_thread_count += grids_[i].thread_count_; + } + return multi_grid_thread_rank_0; + } + + std::vector grids_; + unsigned int grid_count_; + unsigned int thread_count_; +}; + /* Generate dimensions for 1D, 2D and 3D blocks of threads */ inline dim3 GenerateThreadDimensions() { hipDeviceProp_t props; diff --git a/catch/include/hip_test_defgroups.hh b/catch/include/hip_test_defgroups.hh index c34fc1202f..fdf289e8c5 100644 --- a/catch/include/hip_test_defgroups.hh +++ b/catch/include/hip_test_defgroups.hh @@ -144,6 +144,28 @@ THE SOFTWARE. * @} */ + /** + * @defgroup StreamOTest Ordered Memory Allocator + * @{ + * This section describes the tests for Stream Ordered Memory Allocator functions of HIP runtime + * API. + * @} + */ + +/** + * @defgroup StreamOTest Ordered Memory Allocator + * @{ + * This section describes the tests for Stream Ordered Memory Allocator functions of HIP runtime + * API. + */ + +/** + * @defgroup StreamOTest Ordered Memory Allocator + * @{ + * This section describes the tests for Stream Ordered Memory Allocator functions of HIP runtime + * API. + */ + /** * @defgroup StreamTest Stream Management * @{ @@ -171,3 +193,17 @@ THE SOFTWARE. * This section describes the various Printf use case Scenarios. * @} */ + +/** + * @defgroup SurfaceTest Surface Management + * @{ + * This section describes tests for the surface management functions of HIP runtime API. + * @} + */ + + /** + * @defgroup ComplexTest Complex type + * @{ + * This section describes tests for the Complex type functions. + * @} + */ diff --git a/catch/include/resource_guards.hh b/catch/include/resource_guards.hh index 3ec6aabf8d..5f8f2cbee4 100644 --- a/catch/include/resource_guards.hh +++ b/catch/include/resource_guards.hh @@ -102,23 +102,25 @@ template class LinearAllocGuard { ~LinearAllocGuard() { // No Catch macros, don't want to possibly throw in the destructor - switch (allocation_type_) { - case LinearAllocs::noAlloc: - break; - case LinearAllocs::malloc: - free(ptr_); - break; - case LinearAllocs::mallocAndRegister: - // Cast to void to suppress nodiscard warnings - static_cast(hipHostUnregister(host_ptr_)); - free(host_ptr_); - break; - case LinearAllocs::hipHostMalloc: - static_cast(hipHostFree(ptr_)); - break; - case LinearAllocs::hipMalloc: - case LinearAllocs::hipMallocManaged: - static_cast(hipFree(ptr_)); + if (ptr_ != nullptr) { + switch (allocation_type_) { + case LinearAllocs::noAlloc: + break; + case LinearAllocs::malloc: + free(ptr_); + break; + case LinearAllocs::mallocAndRegister: + // Cast to void to suppress nodiscard warnings + static_cast(hipHostUnregister(host_ptr_)); + free(host_ptr_); + break; + case LinearAllocs::hipHostMalloc: + static_cast(hipHostFree(ptr_)); + break; + case LinearAllocs::hipMalloc: + case LinearAllocs::hipMallocManaged: + static_cast(hipFree(ptr_)); + } } } @@ -287,7 +289,7 @@ class StreamGuard { } ~StreamGuard() { - if (stream_type_ == Streams::created) { + if (stream_type_ == Streams::created && stream_ != nullptr) { static_cast(hipStreamDestroy(stream_)); } } @@ -346,3 +348,45 @@ class StreamsGuard { private: std::vector streams_; }; + +enum class MemPools { dev_default, created }; + +class MemPoolGuard { + public: + MemPoolGuard(const MemPools mempool_type, int device, + hipMemAllocationHandleType handle_type = hipMemHandleTypeNone) + : mempool_type_{mempool_type}, device_{device}, handle_type_{handle_type} { + switch (mempool_type_) { + case MemPools::dev_default: + HIP_CHECK(hipDeviceGetDefaultMemPool(&mempool_, device_)); + break; + case MemPools::created: + hipMemPoolProps pool_props; + pool_props.allocType = hipMemAllocationTypePinned; + pool_props.handleTypes = handle_type_; + pool_props.location.type = hipMemLocationTypeDevice; + pool_props.location.id = device_; + pool_props.win32SecurityAttributes = nullptr; + memset(pool_props.reserved, 0, sizeof(pool_props.reserved)); + + HIP_CHECK(hipMemPoolCreate(&mempool_, &pool_props)); + } + } + + MemPoolGuard(const MemPoolGuard&) = delete; + MemPoolGuard(MemPoolGuard&&) = delete; + + ~MemPoolGuard() { + if (mempool_type_ == MemPools::created) { + static_cast(hipMemPoolDestroy(mempool_)); + } + } + + hipMemPool_t mempool() const { return mempool_; } + + private: + const MemPools mempool_type_; + int device_; + hipMemAllocationHandleType handle_type_; + hipMemPool_t mempool_; +}; diff --git a/catch/unit/CMakeLists.txt b/catch/unit/CMakeLists.txt index 7d44709d1d..8fac51f5c6 100644 --- a/catch/unit/CMakeLists.txt +++ b/catch/unit/CMakeLists.txt @@ -35,8 +35,9 @@ add_subdirectory(multiThread) add_subdirectory(compiler) add_subdirectory(errorHandling) add_subdirectory(cooperativeGrps) -add_subdirectory(context) add_subdirectory(warp) +add_subdirectory(context) +add_subdirectory(device_memory) add_subdirectory(dynamicLoading) add_subdirectory(g++) add_subdirectory(module) @@ -44,12 +45,14 @@ add_subdirectory(channelDescriptor) add_subdirectory(executionControl) add_subdirectory(vector_types) add_subdirectory(atomics) +add_subdirectory(complex) add_subdirectory(p2p) add_subdirectory(gcc) if(HIP_PLATFORM STREQUAL "amd") add_subdirectory(callback) -add_subdirectory(clock) +#add_subdirectory(clock) +add_subdirectory(hip_specific) # Vulkan interop APIs currently undefined for Nvidia add_subdirectory(vulkan_interop) add_subdirectory(gl_interop) # Disabled on NVIDIA due to defect - EXSWHTEC-246 diff --git a/catch/unit/compileAndCaptureOutput.py b/catch/unit/compileAndCaptureOutput.py index c06feb5858..a8a7fb506a 100644 --- a/catch/unit/compileAndCaptureOutput.py +++ b/catch/unit/compileAndCaptureOutput.py @@ -80,7 +80,7 @@ if __name__ == '__main__': CompileAndCapture.platform = sys.argv[2] except IndexError: CompileAndCapture.platform = None - + try: CompileAndCapture.hip_path = sys.argv[3] except IndexError: @@ -95,7 +95,7 @@ if __name__ == '__main__': CompileAndCapture.expected_error_count = int(sys.argv[5]) except IndexError: CompileAndCapture.expected_error_count = 0 - + try: CompileAndCapture.expected_warning_count = int(sys.argv[6]) except IndexError: diff --git a/catch/unit/complex/CMakeLists.txt b/catch/unit/complex/CMakeLists.txt new file mode 100644 index 0000000000..7ca235b176 --- /dev/null +++ b/catch/unit/complex/CMakeLists.txt @@ -0,0 +1,73 @@ +# Copyright (c) 2023 Advanced Micro Devices, Inc. All Rights Reserved. +# +# 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. + +# Common Tests - Test independent of all platforms + +set(TEST_SRC + complex.cc +) + +if(HIP_PLATFORM MATCHES "nvidia") + set(LINKER_LIBS nvrtc) +elseif(HIP_PLATFORM MATCHES "amd") + set(LINKER_LIBS hiprtc) +endif() + +hip_add_exe_to_target(NAME ComplexTest + TEST_SRC ${TEST_SRC} + TEST_TARGET_NAME build_tests + LINKER_LIBS ${LINKER_LIBS}) + +# These tests fail in PSDB +#add_test(NAME Unit_Device_Complex_make_Negative +# COMMAND python3 ${CMAKE_CURRENT_SOURCE_DIR}/../compileAndCaptureOutput.py +# ${CMAKE_CURRENT_SOURCE_DIR} ${HIP_PLATFORM} ${HIP_PATH} +# complex_make_negative_kernels.cc 54) +# +#add_test(NAME Unit_Device_Complex_Cast_Negative +# COMMAND python3 ${CMAKE_CURRENT_SOURCE_DIR}/../compileAndCaptureOutput.py +# ${CMAKE_CURRENT_SOURCE_DIR} ${HIP_PLATFORM} ${HIP_PATH} +# complex_cast_negative_kernels.cc 28) +# +#add_test(NAME Unit_Device_Complex_Unary_float_Negative +# COMMAND python3 ${CMAKE_CURRENT_SOURCE_DIR}/../compileAndCaptureOutput.py +# ${CMAKE_CURRENT_SOURCE_DIR} ${HIP_PLATFORM} ${HIP_PATH} +# complex_negative_kernels_1Arg_float.cc 62) +# +#add_test(NAME Unit_Device_Complex_Unary_double_Negative +# COMMAND python3 ${CMAKE_CURRENT_SOURCE_DIR}/../compileAndCaptureOutput.py +# ${CMAKE_CURRENT_SOURCE_DIR} ${HIP_PLATFORM} ${HIP_PATH} +# complex_negative_kernels_1Arg_double.cc 62) +# +#add_test(NAME Unit_Device_Complex_Binary_float_Negative +# COMMAND python3 ${CMAKE_CURRENT_SOURCE_DIR}/../compileAndCaptureOutput.py +# ${CMAKE_CURRENT_SOURCE_DIR} ${HIP_PLATFORM} ${HIP_PATH} +# complex_negative_kernels_2Arg_float.cc 88) +# +#add_test(NAME Unit_Device_Complex_Binary_double_Negative +# COMMAND python3 ${CMAKE_CURRENT_SOURCE_DIR}/../compileAndCaptureOutput.py +# ${CMAKE_CURRENT_SOURCE_DIR} ${HIP_PLATFORM} ${HIP_PATH} +# complex_negative_kernels_2Arg_double.cc 88) +# +#add_test(NAME Unit_Device_Complex_hipCfma_Negative +# COMMAND python3 ${CMAKE_CURRENT_SOURCE_DIR}/../compileAndCaptureOutput.py +# ${CMAKE_CURRENT_SOURCE_DIR} ${HIP_PLATFORM} ${HIP_PATH} +# complex_negative_kernels_3Arg.cc 60) + diff --git a/catch/unit/complex/complex.cc b/catch/unit/complex/complex.cc new file mode 100644 index 0000000000..11b42eb0e5 --- /dev/null +++ b/catch/unit/complex/complex.cc @@ -0,0 +1,479 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. +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 WARRANNTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNNESS 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 INN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR INN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include "complex_function_common.hh" +#include "complex_cast_negative_kernels_rtc.hh" +#include "complex_make_negative_kernels_rtc.hh" +#include "complex_negative_kernels_1Arg_rtc.hh" +#include "complex_negative_kernels_2Arg_rtc.hh" +#include "complex_negative_kernels_3Arg_rtc.hh" + +/** + * @addtogroup complex complex + * @{ + * @ingroup ComplexTest + * Contains unit tests for complex type functions + */ + + +/** + * Test Description + * ------------------------ + * - Test that checks unary complex functions on device for reduced set of input values. The + * results are compared against manually calculated ones: + * -# hipConj, hipConjf + * -# hipCreal, hipCrealf + * -# hipCimag, hipCimagf + * -# hipCabs, hipCabsf + * -# hipCsqabs, hipCsqabsf + * + * Test source + * ------------------------ + * - unit/complex/complex.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEMPLATE_TEST_CASE("Unit_Device_Complex_Unary_Device_Sanity_Positive", "", hipFloatComplex, + hipDoubleComplex) { + decltype(TestType().x) input_r = GENERATE(-4.75, 0, 1.75); + decltype(TestType().x) input_i = GENERATE(-4.75, 0, 1.75); + + TestType input_val = MakeComplexType(input_r, input_i); + for (const auto function : + {ComplexFunction::kConj, ComplexFunction::kReal, ComplexFunction::kImag, + ComplexFunction::kAbs, ComplexFunction::kSqabs}) { + DYNAMIC_SECTION("function: " << to_string(function)) { + ComplexFunctionUnaryDeviceTest(function, input_val); + } + } +} + +/** + * Test Description + * ------------------------ + * - Test that checks unary complex functions on host for reduced set of input values. The + * results are compared against manually calculated ones: + * -# hipConj, hipConjf + * -# hipCreal, hipCrealf + * -# hipCimag, hipCimagf + * -# hipCabs, hipCabsf + * -# hipCsqabs, hipCsqabsf + * + * Test source + * ------------------------ + * - unit/complex/complex.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEMPLATE_TEST_CASE("Unit_Device_Complex_Unary_Host_Sanity_Positive", "", hipFloatComplex, + hipDoubleComplex) { + decltype(TestType().x) input_r = GENERATE(-4.75, 0, 1.75); + decltype(TestType().x) input_i = GENERATE(-4.75, 0, 1.75); + + TestType input_val = MakeComplexType(input_r, input_i); + for (const auto function : + {ComplexFunction::kConj, ComplexFunction::kReal, ComplexFunction::kImag, + ComplexFunction::kAbs, ComplexFunction::kSqabs}) { + DYNAMIC_SECTION("function: " << to_string(function)) { + ComplexFunctionUnaryHostTest(function, input_val); + } + } +} + +/** + * Test Description + * ------------------------ + * - RTCs kernels that pass argument of invalid type for unary complex functions. + * + * Test source + * ------------------------ + * - unit/complex/complex.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_Device_Complex_Unary_Negative_Parameters_RTC") { + ComplexTypeRTCWrapper<28>(kComplexConj); + ComplexTypeRTCWrapper<24>(kComplexReal); + ComplexTypeRTCWrapper<24>(kComplexImag); + ComplexTypeRTCWrapper<24>(kComplexAbs); + ComplexTypeRTCWrapper<24>(kComplexSqabs); +} + +/** + * Test Description + * ------------------------ + * - Test that checks binary complex functions on device for reduced set of input values. The + * results are compared against manually calculated ones: + * -# hipCadd, hipCaddf + * -# hipCsub, hipCsubf + * -# hipCmul, hipCmulf + * -# hipCdiv, hipCdivf + * + * Test source + * ------------------------ + * - unit/complex/complex.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEMPLATE_TEST_CASE("Unit_Device_Complex_Binary_Device_Sanity_Positive", "", hipFloatComplex, + hipDoubleComplex) { + decltype(TestType().x) input1_r = GENERATE(-4.75, 0, 1.75); + decltype(TestType().x) input1_i = GENERATE(-4.75, 0, 1.75); + decltype(TestType().x) input2_r = GENERATE(-4.75, 0, 1.75); + decltype(TestType().x) input2_i = GENERATE(-4.75, 0, 1.75); + + TestType input_val1 = MakeComplexType(input1_r, input1_i); + TestType input_val2 = MakeComplexType(input2_r, input2_i); + for (const auto function : {ComplexFunction::kAdd, ComplexFunction::kSub, ComplexFunction::kMul, + ComplexFunction::kDiv}) { + DYNAMIC_SECTION("function: " << to_string(function)) { + ComplexFunctionBinaryDeviceTest(function, input_val1, input_val2); + } + } +} + +/** + * Test Description + * ------------------------ + * - Test that checks binary complex functions on host for reduced set of input values. The + * results are compared against manually calculated ones: + * -# hipCadd, hipCaddf + * -# hipCsub, hipCsubf + * -# hipCmul, hipCmulf + * -# hipCdiv, hipCdivf + * + * Test source + * ------------------------ + * - unit/complex/complex.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEMPLATE_TEST_CASE("Unit_Device_Complex_Binary_Host_Sanity_Positive", "", hipFloatComplex, + hipDoubleComplex) { + decltype(TestType().x) input1_r = GENERATE(-4.75, 0, 1.75); + decltype(TestType().x) input1_i = GENERATE(-4.75, 0, 1.75); + decltype(TestType().x) input2_r = GENERATE(-4.75, 0, 1.75); + decltype(TestType().x) input2_i = GENERATE(-4.75, 0, 1.75); + + TestType input_val1 = MakeComplexType(input1_r, input1_i); + TestType input_val2 = MakeComplexType(input2_r, input2_i); + for (const auto function : {ComplexFunction::kAdd, ComplexFunction::kSub, ComplexFunction::kMul, + ComplexFunction::kDiv}) { + DYNAMIC_SECTION("function: " << to_string(function)) { + ComplexFunctionBinaryHostTest(function, input_val1, input_val2); + } + } +} + +/** + * Test Description + * ------------------------ + * - RTCs kernels that pass argument of invalid type for binary complex functions. + * + * Test source + * ------------------------ + * - unit/complex/complex.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_Device_Complex_Binary_Negative_Parameters_RTC") { + ComplexTypeRTCWrapper<44>(kComplexAdd); + ComplexTypeRTCWrapper<44>(kComplexSub); + ComplexTypeRTCWrapper<44>(kComplexMul); + ComplexTypeRTCWrapper<44>(kComplexDiv); +} + +/** + * Test Description + * ------------------------ + * - Test that checks hipCfma/hipCfmaf complex functions on device for reduced set of input + * values. The results are compared against manually calculated ones. + * + * Test source + * ------------------------ + * - unit/complex/complex.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEMPLATE_TEST_CASE("Unit_Device_Complex_hipCfma_Device_Sanity_Positive", "", hipFloatComplex, + hipDoubleComplex) { + decltype(TestType().x) input1_r = GENERATE(-4.75, 0, 1.75); + decltype(TestType().x) input1_i = GENERATE(-4.75, 0, 1.75); + decltype(TestType().x) input2_r = GENERATE(-4.75, 0, 1.75); + decltype(TestType().x) input2_i = GENERATE(-4.75, 0, 1.75); + decltype(TestType().x) input3_r = GENERATE(-4.75, 0, 1.75); + decltype(TestType().x) input3_i = GENERATE(-4.75, 0, 1.75); + + TestType input_val1 = MakeComplexType(input1_r, input1_i); + TestType input_val2 = MakeComplexType(input2_r, input2_i); + TestType input_val3 = MakeComplexType(input3_r, input3_i); + + ComplexFunctionTernaryDeviceTest(ComplexFunction::kFma, input_val1, input_val2, input_val3); +} + +/** + * Test Description + * ------------------------ + * - Test that checks hipCfma/hipCfmaf complex functions on host for reduced set of input + * values. The results are compared against manually calculated ones. + * + * Test source + * ------------------------ + * - unit/complex/complex.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEMPLATE_TEST_CASE("Unit_Device_Complex_hipCfma_Host_Sanity_Positive", "", hipFloatComplex, + hipDoubleComplex) { + decltype(TestType().x) input1_r = GENERATE(-4.75, 0, 1.75); + decltype(TestType().x) input1_i = GENERATE(-4.75, 0, 1.75); + decltype(TestType().x) input2_r = GENERATE(-4.75, 0, 1.75); + decltype(TestType().x) input2_i = GENERATE(-4.75, 0, 1.75); + decltype(TestType().x) input3_r = GENERATE(-4.75, 0, 1.75); + decltype(TestType().x) input3_i = GENERATE(-4.75, 0, 1.75); + + TestType input_val1 = MakeComplexType(input1_r, input1_i); + TestType input_val2 = MakeComplexType(input2_r, input2_i); + TestType input_val3 = MakeComplexType(input3_r, input3_i); + + ComplexFunctionTernaryHostTest(ComplexFunction::kFma, input_val1, input_val2, input_val3); +} + +/** + * Test Description + * ------------------------ + * - RTCs kernels that pass argument of invalid type for hipCfma/hipCfmaf complex function. + * + * Test source + * ------------------------ + * - unit/complex/complex.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_Device_Complex_hipCfma_Negative_Parameters_RTC") { + ComplexTypeRTCWrapper<60>(kComplexFma); +} + +/** + * Test Description + * ------------------------ + * - Test that checks make_hipFloatComplex/make_hipDoubleComplex functions on device for reduced + * set of input values. The results are compared against manually calculated ones. + * + * Test source + * ------------------------ + * - unit/complex/complex.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEMPLATE_TEST_CASE("Unit_Device_make_Complex_Device_Positive", "", hipFloatComplex, + hipDoubleComplex) { + decltype(TestType().x) input_r = GENERATE(-0.25, 0, 0.25); + decltype(TestType().x) input_i = GENERATE(-1.75, 0, 1.75); + + LinearAllocGuard result_d(LinearAllocs::hipMalloc, sizeof(TestType)); + LinearAllocGuard result_h(LinearAllocs::hipHostMalloc, sizeof(TestType)); + + MakeComplexTypeKernel<<<1, 1>>>(result_d.ptr(), input_r, input_i); + HIP_CHECK(hipMemcpy(result_h.ptr(), result_d.ptr(), sizeof(TestType), hipMemcpyDeviceToHost)); + HIP_CHECK(hipDeviceSynchronize()); + + REQUIRE(result_h.ptr()[0].x == input_r); + REQUIRE(result_h.ptr()[0].y == input_i); +} + +/** + * Test Description + * ------------------------ + * - Test that checks make_hipFloatComplex/make_hipDoubleComplex functions on device for reduced + * set of input values. The results are compared against manually calculated ones. + * + * Test source + * ------------------------ + * - unit/complex/complex.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEMPLATE_TEST_CASE("Unit_Device_make_Complex_Host_Positive", "", hipFloatComplex, + hipDoubleComplex) { + decltype(TestType().x) input_r = GENERATE(-0.25, 0, 0.25); + decltype(TestType().x) input_i = GENERATE(-1.75, 0, 1.75); + + TestType result = MakeComplexType(input_r, input_i); + + REQUIRE(result.x == input_r); + REQUIRE(result.y == input_i); +} + +#if HT_AMD // EXSWHTEC-321 +/** + * Test Description + * ------------------------ + * - Test that checks make_hipComplex functions on device for reduced set of input values. The + * results are compared against manually calculated ones. + * + * Test source + * ------------------------ + * - unit/complex/complex.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_Device_make_hipComplex_Device_Positive") { + float input_r = GENERATE(-0.25, 0, 0.25); + float input_i = GENERATE(-1.75, 0, 1.75); + + LinearAllocGuard result_d(LinearAllocs::hipMalloc, sizeof(hipComplex)); + LinearAllocGuard result_h(LinearAllocs::hipHostMalloc, sizeof(hipComplex)); + + MakeHipComplexTypeKernel<<<1, 1>>>(result_d.ptr(), input_r, input_i); + HIP_CHECK(hipMemcpy(result_h.ptr(), result_d.ptr(), sizeof(hipComplex), hipMemcpyDeviceToHost)); + HIP_CHECK(hipDeviceSynchronize()); + + REQUIRE(result_h.ptr()[0].x == input_r); + REQUIRE(result_h.ptr()[0].y == input_i); +} + +/** + * Test Description + * ------------------------ + * - Test that checks make_hipComplex functions on host for reduced set of input values. The + * results are compared against manually calculated ones. + * + * Test source + * ------------------------ + * - unit/complex/complex.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_Device_make_hipComplex_Host_Positive") { + float input_r = GENERATE(-0.25, 0, 0.25); + float input_i = GENERATE(-1.75, 0, 1.75); + + hipComplex result = make_hipComplex(input_r, input_i); + + REQUIRE(result.x == input_r); + REQUIRE(result.y == input_i); +} +#endif + +/** + * Test Description + * ------------------------ + * - RTCs kernels that pass argument of invalid type for make complex functions. + * + * Test source + * ------------------------ + * - unit/complex/complex.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_Device_make_Complex_Negative_Parameters_RTC") { + ComplexTypeRTCWrapper<18>(kMakeHipComplex); + ComplexTypeRTCWrapper<18>(kMakeHipFloatComplex); + ComplexTypeRTCWrapper<18>(kMakeHipDoubleComplex); +} + +/** + * Test Description + * ------------------------ + * - Test that checks hipComplexDoubleToFloat/hipComplexFloatToDouble functions on device for + * reduced set of input values. The results are compared against manually calculated ones. + * + * Test source + * ------------------------ + * - unit/complex/complex.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEMPLATE_TEST_CASE("Unit_Device_Complex_Cast_Device_Sanity_Positive", "", hipFloatComplex, + hipDoubleComplex) { + decltype(TestType().x) input_r = GENERATE(-0.25, 0, 0.25); + decltype(TestType().x) input_i = GENERATE(-1.75, 0, 1.75); + TestType input = MakeComplexType(input_r, input_i); + + LinearAllocGuard> result_d{LinearAllocs::hipMalloc, + sizeof(CastType_t)}; + LinearAllocGuard> result_h{LinearAllocs::hipHostMalloc, + sizeof(CastType_t)}; + + CastComplexTypeKernel<<<1, 1>>>(result_d.ptr(), input); + HIP_CHECK(hipMemcpy(result_h.ptr(), result_d.ptr(), sizeof(CastType_t), + hipMemcpyDeviceToHost)); + HIP_CHECK(hipDeviceSynchronize()); + + REQUIRE(result_h.ptr()[0].x == static_cast().x)>(input_r)); + REQUIRE(result_h.ptr()[0].y == static_cast().x)>(input_i)); +} + +/** + * Test Description + * ------------------------ + * - Test that checks hipComplexDoubleToFloat/hipComplexFloatToDouble functions on host for + * reduced set of input values. The results are compared against manually calculated ones. + * + * Test source + * ------------------------ + * - unit/complex/complex.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEMPLATE_TEST_CASE("Unit_Device_Complex_Cast_Host_Sanity_Positive", "", hipFloatComplex, + hipDoubleComplex) { + decltype(TestType().x) input_r = GENERATE(-0.25, 0, 0.25); + decltype(TestType().x) input_i = GENERATE(-1.75, 0, 1.75); + TestType input = MakeComplexType(input_r, input_i); + + CastType_t result = CastComplexType>(input); + + REQUIRE(result.x == static_cast().x)>(input_r)); + REQUIRE(result.y == static_cast().x)>(input_i)); +} + +/** + * Test Description + * ------------------------ + * - RTCs kernels that pass argument of invalid type for complex cast functions. + * + * Test source + * ------------------------ + * - unit/complex/complex.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_Device_Complex_Cast_Negative_Parameters_RTC") { + ComplexTypeRTCWrapper<14>(kComplexDoubleToFloat); + ComplexTypeRTCWrapper<14>(kComplexFloatToDouble); +} diff --git a/catch/unit/complex/complex_basic_common.hh b/catch/unit/complex/complex_basic_common.hh new file mode 100644 index 0000000000..f0e07d6cc4 --- /dev/null +++ b/catch/unit/complex/complex_basic_common.hh @@ -0,0 +1,106 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. +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 WARRANNTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNNESS 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 INN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR INN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +#pragma once + +#include +#include +#include + +template +__host__ __device__ T MakeComplexType(decltype(T().x) input_val1, decltype(T().x) input_val2) { + if constexpr (std::is_same_v) { + return make_hipFloatComplex(input_val1, input_val2); + } else { + return make_hipDoubleComplex(input_val1, input_val2); + } +} + +template +__global__ void MakeComplexTypeKernel(T* const output_val, decltype(T().x) const input_val1, + decltype(T().x) const input_val2) { + *output_val = MakeComplexType(input_val1, input_val2); +} +#if HT_AMD // EXSWHTEC-321 +__global__ void MakeHipComplexTypeKernel(hipComplex* const output_val, float const input_val1, + float const input_val2) { + *output_val = make_hipComplex(input_val1, input_val2); +} +#endif +template struct CastType {}; + +template <> struct CastType { + using type = hipDoubleComplex; +}; + +template <> struct CastType { + using type = hipFloatComplex; +}; + +template using CastType_t = typename CastType::type; + +template __device__ __host__ T1 CastComplexType(T2 const input_val) { + if constexpr (std::is_same_v) { + return hipComplexDoubleToFloat(input_val); + } else if constexpr (std::is_same_v) { + return hipComplexFloatToDouble(input_val); + } +} + +template +__global__ void CastComplexTypeKernel(T1* const output_val, T2 const input_val) { + *output_val = CastComplexType(input_val); +} + +template void CompareValues(T actual_val, T ref_val, double margin) { + if (!std::isnan(ref_val)) { + REQUIRE_THAT(actual_val, Catch::WithinAbs(ref_val, margin)); + } +} + +template void ComplexTypeRTCWrapper(const char* program_source) { + hiprtcProgram program{}; + HIPRTC_CHECK(hiprtcCreateProgram(&program, program_source, "complex_type_kernels.cc", 0, nullptr, + nullptr)); + +#if HT_AMD + std::string args = std::string("-ferror-limit=100"); + const char* options[] = {args.c_str()}; + hiprtcResult result{hiprtcCompileProgram(program, 1, options)}; +#else + hiprtcResult result{hiprtcCompileProgram(program, 0, nullptr)}; +#endif + + size_t log_size{}; + HIPRTC_CHECK(hiprtcGetProgramLogSize(program, &log_size)); + std::string log(log_size, ' '); + HIPRTC_CHECK(hiprtcGetProgramLog(program, log.data())); + int error_count{0}; + + std::string error_message{"error:"}; + + size_t npos_e = log.find(error_message, 0); + while (npos_e != std::string::npos) { + ++error_count; + npos_e = log.find(error_message, npos_e + 1); + } + + HIPRTC_CHECK(hiprtcDestroyProgram(&program)); + HIPRTC_CHECK_ERROR(result, HIPRTC_ERROR_COMPILATION); + REQUIRE(error_count == expected_errors_num); +} diff --git a/catch/unit/complex/complex_cast_negative_kernels.cc b/catch/unit/complex/complex_cast_negative_kernels.cc new file mode 100644 index 0000000000..23b0700205 --- /dev/null +++ b/catch/unit/complex/complex_cast_negative_kernels.cc @@ -0,0 +1,113 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. +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 WARRANNTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNNESS 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 INN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR INN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include +#include + +class Dummy { + public: + __device__ Dummy() {} + __device__ ~Dummy() {} +}; + +__global__ void hipComplexDoubleToFloat_kernel_v1(hipFloatComplex* result, hipDoubleComplex* x) { + *result = hipComplexDoubleToFloat(x); +} +__global__ void hipComplexDoubleToFloat_kernel_v2(hipFloatComplex* result, hipFloatComplex x) { + *result = hipComplexDoubleToFloat(x); +} +__global__ void hipComplexDoubleToFloat_kernel_v3(hipFloatComplex* result, double x) { + *result = hipComplexDoubleToFloat(x); +} +__global__ void hipComplexDoubleToFloat_kernel_v4(hipFloatComplex* result, Dummy x) { + *result = hipComplexDoubleToFloat(x); +} +__global__ void hipComplexDoubleToFloat_kernel_v5(float* result, hipDoubleComplex x) { + *result = hipComplexDoubleToFloat(x); +} +__global__ void hipComplexDoubleToFloat_kernel_v6(hipDoubleComplex* result, hipDoubleComplex x) { + *result = hipComplexDoubleToFloat(x); +} +__global__ void hipComplexDoubleToFloat_kernel_v7(Dummy* result, hipDoubleComplex x) { + *result = hipComplexDoubleToFloat(x); +} +void hipComplexDoubleToFloat_v1(hipFloatComplex* result, hipDoubleComplex* x) { + *result = hipComplexDoubleToFloat(x); +} +void hipComplexDoubleToFloat_v2(hipFloatComplex* result, hipFloatComplex x) { + *result = hipComplexDoubleToFloat(x); +} +void hipComplexDoubleToFloat_v3(hipFloatComplex* result, double x) { + *result = hipComplexDoubleToFloat(x); +} +void hipComplexDoubleToFloat_v4(hipFloatComplex* result, Dummy x) { + *result = hipComplexDoubleToFloat(x); +} +void hipComplexDoubleToFloat_v5(float* result, hipDoubleComplex x) { + *result = hipComplexDoubleToFloat(x); +} +void hipComplexDoubleToFloat_v6(hipDoubleComplex* result, hipDoubleComplex x) { + *result = hipComplexDoubleToFloat(x); +} +void hipComplexDoubleToFloat_v7(Dummy* result, hipDoubleComplex x) { + *result = hipComplexDoubleToFloat(x); +} + +__global__ void hipComplexFloatToDouble_kernel_v1(hipDoubleComplex* result, hipFloatComplex* x) { + *result = hipComplexFloatToDouble(x); +} +__global__ void hipComplexFloatToDouble_kernel_v2(hipDoubleComplex* result, hipDoubleComplex x) { + *result = hipComplexFloatToDouble(x); +} +__global__ void hipComplexFloatToDouble_kernel_v3(hipDoubleComplex* result, float x) { + *result = hipComplexFloatToDouble(x); +} +__global__ void hipComplexFloatToDouble_kernel_v4(hipDoubleComplex* result, Dummy x) { + *result = hipComplexFloatToDouble(x); +} +__global__ void hipComplexFloatToDouble_kernel_v5(double* result, hipFloatComplex x) { + *result = hipComplexFloatToDouble(x); +} +__global__ void hipComplexFloatToDouble_kernel_v6(hipFloatComplex* result, hipFloatComplex x) { + *result = hipComplexFloatToDouble(x); +} +__global__ void hipComplexFloatToDouble_kernel_v7(Dummy* result, hipFloatComplex x) { + *result = hipComplexFloatToDouble(x); +} +void hipComplexFloatToDouble_v1(hipDoubleComplex* result, hipFloatComplex* x) { + *result = hipComplexFloatToDouble(x); +} +void hipComplexFloatToDouble_v2(hipDoubleComplex* result, hipDoubleComplex x) { + *result = hipComplexFloatToDouble(x); +} +void hipComplexFloatToDouble_v3(hipDoubleComplex* result, float x) { + *result = hipComplexFloatToDouble(x); +} +void hipComplexFloatToDouble_v4(hipDoubleComplex* result, Dummy x) { + *result = hipComplexFloatToDouble(x); +} +void hipComplexFloatToDouble_v5(double* result, hipFloatComplex x) { + *result = hipComplexFloatToDouble(x); +} +void hipComplexFloatToDouble_v6(hipFloatComplex* result, hipFloatComplex x) { + *result = hipComplexFloatToDouble(x); +} +void hipComplexFloatToDouble_v7(Dummy* result, hipFloatComplex x) { + *result = hipComplexFloatToDouble(x); +} \ No newline at end of file diff --git a/catch/unit/complex/complex_cast_negative_kernels_rtc.hh b/catch/unit/complex/complex_cast_negative_kernels_rtc.hh new file mode 100644 index 0000000000..c2ef7bef8d --- /dev/null +++ b/catch/unit/complex/complex_cast_negative_kernels_rtc.hh @@ -0,0 +1,121 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. +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 WARRANNTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNNESS 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 INN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR INN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#pragma once + +static constexpr auto kComplexDoubleToFloat{R"( + class Dummy { + public: + __device__ Dummy() {} + __device__ ~Dummy() {} + }; + __global__ void hipComplexDoubleToFloat_kernel_v1(hipFloatComplex* result, hipDoubleComplex* x) { + *result = hipComplexDoubleToFloat(x); + } + __global__ void hipComplexDoubleToFloat_kernel_v2(hipFloatComplex* result, hipFloatComplex x) { + *result = hipComplexDoubleToFloat(x); + } + __global__ void hipComplexDoubleToFloat_kernel_v3(hipFloatComplex* result, double x) { + *result = hipComplexDoubleToFloat(x); + } + __global__ void hipComplexDoubleToFloat_kernel_v4(hipFloatComplex* result, Dummy x) { + *result = hipComplexDoubleToFloat(x); + } + __global__ void hipComplexDoubleToFloat_kernel_v5(float* result, hipDoubleComplex x) { + *result = hipComplexDoubleToFloat(x); + } + __global__ void hipComplexDoubleToFloat_kernel_v6(hipDoubleComplex* result, hipDoubleComplex x) { + *result = hipComplexDoubleToFloat(x); + } + __global__ void hipComplexDoubleToFloat_kernel_v7(Dummy* result, hipDoubleComplex x) { + *result = hipComplexDoubleToFloat(x); + } + void hipComplexDoubleToFloat_v1(hipFloatComplex* result, hipDoubleComplex* x) { + *result = hipComplexDoubleToFloat(x); + } + void hipComplexDoubleToFloat_v2(hipFloatComplex* result, hipFloatComplex x) { + *result = hipComplexDoubleToFloat(x); + } + void hipComplexDoubleToFloat_v3(hipFloatComplex* result, double x) { + *result = hipComplexDoubleToFloat(x); + } + void hipComplexDoubleToFloat_v4(hipFloatComplex* result, Dummy x) { + *result = hipComplexDoubleToFloat(x); + } + void hipComplexDoubleToFloat_v5(float* result, hipDoubleComplex x) { + *result = hipComplexDoubleToFloat(x); + } + void hipComplexDoubleToFloat_v6(hipDoubleComplex* result, hipDoubleComplex x) { + *result = hipComplexDoubleToFloat(x); + } + void hipComplexDoubleToFloat_v7(Dummy* result, hipDoubleComplex x) { + *result = hipComplexDoubleToFloat(x); + } + +)"}; + +static constexpr auto kComplexFloatToDouble{R"( + class Dummy { + public: + __device__ Dummy() {} + __device__ ~Dummy() {} + }; + __global__ void hipComplexFloatToDouble_kernel_v1(hipDoubleComplex* result, hipFloatComplex* x) { + *result = hipComplexFloatToDouble(x); + } + __global__ void hipComplexFloatToDouble_kernel_v2(hipDoubleComplex* result, hipDoubleComplex x) { + *result = hipComplexFloatToDouble(x); + } + __global__ void hipComplexFloatToDouble_kernel_v3(hipDoubleComplex* result, float x) { + *result = hipComplexFloatToDouble(x); + } + __global__ void hipComplexFloatToDouble_kernel_v4(hipDoubleComplex* result, Dummy x) { + *result = hipComplexFloatToDouble(x); + } +__global__ void hipComplexFloatToDouble_kernel_v5(double* result, hipFloatComplex x) { + *result = hipComplexFloatToDouble(x); +} + __global__ void hipComplexFloatToDouble_kernel_v6(hipFloatComplex* result, hipFloatComplex x) { + *result = hipComplexFloatToDouble(x); + } + __global__ void hipComplexFloatToDouble_kernel_v7(Dummy* result, hipFloatComplex x) { + *result = hipComplexFloatToDouble(x); + } + void hipComplexFloatToDouble_v1(hipDoubleComplex* result, hipFloatComplex* x) { + *result = hipComplexFloatToDouble(x); + } + void hipComplexFloatToDouble_v2(hipDoubleComplex* result, hipDoubleComplex x) { + *result = hipComplexFloatToDouble(x); + } + void hipComplexFloatToDouble_v3(hipDoubleComplex* result, float x) { + *result = hipComplexFloatToDouble(x); + } + void hipComplexFloatToDouble_v4(hipDoubleComplex* result, Dummy x) { + *result = hipComplexFloatToDouble(x); + } + void hipComplexFloatToDouble_v5(double* result, hipFloatComplex x) { + *result = hipComplexFloatToDouble(x); + } + void hipComplexFloatToDouble_v6(hipFloatComplex* result, hipFloatComplex x) { + *result = hipComplexFloatToDouble(x); + } + void hipComplexFloatToDouble_v7(Dummy* result, hipFloatComplex x) { + *result = hipComplexFloatToDouble(x); + } +)"}; \ No newline at end of file diff --git a/catch/unit/complex/complex_function_common.hh b/catch/unit/complex/complex_function_common.hh new file mode 100644 index 0000000000..2cecfc9b2d --- /dev/null +++ b/catch/unit/complex/complex_function_common.hh @@ -0,0 +1,311 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. +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 WARRANNTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNNESS 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 INN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR INN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +#pragma once + +#include "complex_basic_common.hh" + +enum class ComplexFunction { kReal, kImag, kConj, kAdd, kSub, kMul, kDiv, kAbs, kSqabs, kFma }; + +inline std::string to_string(ComplexFunction function) { + switch (function) { + case ComplexFunction::kReal: + return "real"; + case ComplexFunction::kImag: + return "imaginary"; + case ComplexFunction::kConj: + return "conjugate "; + case ComplexFunction::kAdd: + return "addition"; + case ComplexFunction::kSub: + return "subtract"; + case ComplexFunction::kMul: + return "multiply"; + case ComplexFunction::kDiv: + return "divide"; + case ComplexFunction::kAbs: + return "absolute"; + case ComplexFunction::kSqabs: + return "square absolute"; + case ComplexFunction::kFma: + return "fused multiply"; + default: + return "Unknown"; + } +} + +// Function that validates complex functions with complex type result +template +void ValidateComplexResultFunction(ComplexFunction function, T input_val1, T input_val2, + T input_val3, T actual_val) { + decltype(T().x) ref_val_r; + decltype(T().x) ref_val_i; + double margin = 0; + + switch (function) { + case ComplexFunction::kAdd: { + ref_val_r = input_val1.x + input_val2.x; + ref_val_i = input_val1.y + input_val2.y; + break; + } + case ComplexFunction::kSub: { + ref_val_r = input_val1.x - input_val2.x; + ref_val_i = input_val1.y - input_val2.y; + break; + } + case ComplexFunction::kMul: { + ref_val_r = input_val1.x * input_val2.x - input_val1.y * input_val2.y; + ref_val_i = input_val1.y * input_val2.x + input_val1.x * input_val2.y; + break; + } + case ComplexFunction::kDiv: { + decltype(T().x) sqabs = input_val2.x * input_val2.x + input_val2.y * input_val2.y; + ref_val_r = (input_val1.x * input_val2.x + input_val1.y * input_val2.y) / sqabs; + ref_val_i = (input_val1.y * input_val2.x - input_val1.x * input_val2.y) / sqabs; +#if HT_NVIDIA + // Nvidia implementation uses scaling to guard against intermediate underflow and overflow + margin = 0.000001; +#endif + break; + } + case ComplexFunction::kConj: { + ref_val_r = input_val1.x; + ref_val_i = -input_val1.y; + break; + } + case ComplexFunction::kFma: { + ref_val_r = (input_val1.x * input_val2.x) + input_val3.x; + ref_val_i = (input_val2.x * input_val1.y) + input_val3.y; + + ref_val_r = -(input_val1.y * input_val2.y) + ref_val_r; + ref_val_i = (input_val1.x * input_val2.y) + ref_val_i; + break; + } + default: { + ref_val_r = input_val1.x; + ref_val_i = input_val1.y; + break; + } + } + + CompareValues(actual_val.x, ref_val_r, margin); + CompareValues(actual_val.y, ref_val_i, margin); +} + +// Function that validates complex functions with scalar type result +template +void ValidateScalarResultFunction(ComplexFunction function, T input_val, + decltype(T().x) actual_val) { + decltype(T().x) ref_val; + + switch (function) { + case ComplexFunction::kReal: { + ref_val = input_val.x; + break; + } + case ComplexFunction::kImag: { + ref_val = input_val.y; + break; + } + case ComplexFunction::kAbs: { + decltype(T().x) sqabs = input_val.x * input_val.x + input_val.y * input_val.y; + ref_val = std::sqrt(sqabs); + break; + } + case ComplexFunction::kSqabs: { + ref_val = input_val.x * input_val.x + input_val.y * input_val.y; + break; + } + default: { + ref_val = input_val.x; + break; + } + } + + CompareValues(actual_val, ref_val, 0); +} + +// Function that performs complex functions with complex type result on host/device +template +__device__ __host__ void PerformComplexResultFunction(ComplexFunction function, T* output_val, + T input_val1, T input_val2, T input_val3) { + if (function == ComplexFunction::kAdd) { + if constexpr (std::is_same_v) { + *output_val = hipCaddf(input_val1, input_val2); + } else if constexpr (std::is_same_v) { + *output_val = hipCadd(input_val1, input_val2); + } + } else if (function == ComplexFunction::kSub) { + if constexpr (std::is_same_v) { + *output_val = hipCsubf(input_val1, input_val2); + } else if constexpr (std::is_same_v) { + *output_val = hipCsub(input_val1, input_val2); + } + } else if (function == ComplexFunction::kMul) { + if constexpr (std::is_same_v) { + *output_val = hipCmulf(input_val1, input_val2); + } else if constexpr (std::is_same_v) { + *output_val = hipCmul(input_val1, input_val2); + } + } else if (function == ComplexFunction::kDiv) { + if constexpr (std::is_same_v) { + *output_val = hipCdivf(input_val1, input_val2); + } else if constexpr (std::is_same_v) { + *output_val = hipCdiv(input_val1, input_val2); + } + } else if (function == ComplexFunction::kConj) { + if constexpr (std::is_same_v) { + *output_val = hipConjf(input_val1); + } else if constexpr (std::is_same_v) { + *output_val = hipConj(input_val1); + } + } else if (function == ComplexFunction::kFma) { + if constexpr (std::is_same_v) { + *output_val = hipCfmaf(input_val1, input_val2, input_val3); + } else if constexpr (std::is_same_v) { + *output_val = hipCfma(input_val1, input_val2, input_val3); + } + } else { + *output_val = input_val1; + } +} + +// Function that performs complex functions with scalar type result on host/device +template +__device__ __host__ void PerformScalarResultFunction(ComplexFunction function, + decltype(T().x)* output_val, T input_val) { + if (function == ComplexFunction::kReal) { + if constexpr (std::is_same_v) { + *output_val = hipCrealf(input_val); + } else if constexpr (std::is_same_v) { + *output_val = hipCreal(input_val); + } + } else if (function == ComplexFunction::kImag) { + if constexpr (std::is_same_v) { + *output_val = hipCimagf(input_val); + } else if constexpr (std::is_same_v) { + *output_val = hipCimag(input_val); + } + } else if (function == ComplexFunction::kAbs) { + if constexpr (std::is_same_v) { + *output_val = hipCabsf(input_val); + } else if constexpr (std::is_same_v) { + *output_val = hipCabs(input_val); + } + } else if (function == ComplexFunction::kSqabs) { + if constexpr (std::is_same_v) { + *output_val = hipCsqabsf(input_val); + } else if constexpr (std::is_same_v) { + *output_val = hipCsqabs(input_val); + } + } else { + *output_val = input_val.x; + } +} + +// Kernel that calls device function which performs complex functions with complex type result +template +__global__ void ComplexResultKernel(ComplexFunction function, T* output_val, T input_val1, + T input_val2, T input_val3) { + PerformComplexResultFunction(function, output_val, input_val1, input_val2, input_val3); +} + +// Kernel that calls device function which performs complex functions with scalar type result +template +__global__ void ScalarResultKernel(ComplexFunction function, decltype(T().x)* output_val, + T input_val) { + PerformScalarResultFunction(function, output_val, input_val); +} + +// Wrapper function for testing complex functions with one input parameter on device +template void ComplexFunctionUnaryDeviceTest(ComplexFunction function, T input_val) { + if (function == ComplexFunction::kConj) { + LinearAllocGuard result_d{LinearAllocs::hipMalloc, sizeof(T)}; + LinearAllocGuard result_h{LinearAllocs::hipHostMalloc, sizeof(T)}; + + ComplexResultKernel<<<1, 1>>>(function, result_d.ptr(), input_val, input_val, input_val); + HIP_CHECK(hipMemcpy(result_h.ptr(), result_d.ptr(), sizeof(T), hipMemcpyDeviceToHost)); + HIP_CHECK(hipDeviceSynchronize()); + ValidateComplexResultFunction(function, input_val, input_val, input_val, result_h.ptr()[0]); + } else { + LinearAllocGuard result_d{LinearAllocs::hipMalloc, sizeof(decltype(T().x))}; + LinearAllocGuard result_h{LinearAllocs::hipHostMalloc, + sizeof(decltype(T().x))}; + + ScalarResultKernel<<<1, 1>>>(function, result_d.ptr(), input_val); + HIP_CHECK( + hipMemcpy(result_h.ptr(), result_d.ptr(), sizeof(decltype(T().x)), hipMemcpyDeviceToHost)); + HIP_CHECK(hipDeviceSynchronize()); + ValidateScalarResultFunction(function, input_val, result_h.ptr()[0]); + } +} + +// Wrapper function for testing complex functions with one input parameter on host +template void ComplexFunctionUnaryHostTest(ComplexFunction function, T input_val) { + if (function == ComplexFunction::kConj) { + T result; + PerformComplexResultFunction(function, &result, input_val, input_val, input_val); + ValidateComplexResultFunction(function, input_val, input_val, input_val, result); + } else { + decltype(T().x) result; + PerformScalarResultFunction(function, &result, input_val); + ValidateScalarResultFunction(function, input_val, result); + } +} + +// Wrapper function for testing complex functions with two input parameters on device +template +void ComplexFunctionBinaryDeviceTest(ComplexFunction function, T input_val1, T input_val2) { + LinearAllocGuard result_d{LinearAllocs::hipMalloc, sizeof(T)}; + LinearAllocGuard result_h{LinearAllocs::hipHostMalloc, sizeof(T)}; + + ComplexResultKernel<<<1, 1>>>(function, result_d.ptr(), input_val1, input_val2, input_val2); + HIP_CHECK(hipMemcpy(result_h.ptr(), result_d.ptr(), sizeof(T), hipMemcpyDeviceToHost)); + HIP_CHECK(hipDeviceSynchronize()); + ValidateComplexResultFunction(function, input_val1, input_val2, input_val2, result_h.ptr()[0]); +} + +// Wrapper function for testing complex functions with two input parameters on host +template +void ComplexFunctionBinaryHostTest(ComplexFunction function, T input_val1, T input_val2) { + T result; + PerformComplexResultFunction(function, &result, input_val1, input_val2, input_val2); + ValidateComplexResultFunction(function, input_val1, input_val2, input_val2, result); +} + +// Wrapper function for testing complex functions with three input parameters on device +template +void ComplexFunctionTernaryDeviceTest(ComplexFunction function, T input_val1, T input_val2, + T input_val3) { + LinearAllocGuard result_d{LinearAllocs::hipMalloc, sizeof(T)}; + LinearAllocGuard result_h{LinearAllocs::hipHostMalloc, sizeof(T)}; + + ComplexResultKernel<<<1, 1>>>(function, result_d.ptr(), input_val1, input_val2, input_val3); + HIP_CHECK(hipMemcpy(result_h.ptr(), result_d.ptr(), sizeof(T), hipMemcpyDeviceToHost)); + HIP_CHECK(hipDeviceSynchronize()); + ValidateComplexResultFunction(function, input_val1, input_val2, input_val3, result_h.ptr()[0]); +} + +// Wrapper function for testing complex functions with three input parameters on host +template +void ComplexFunctionTernaryHostTest(ComplexFunction function, T input_val1, T input_val2, + T input_val3) { + T result; + PerformComplexResultFunction(function, &result, input_val1, input_val2, input_val3); + ValidateComplexResultFunction(function, input_val1, input_val2, input_val3, result); +} \ No newline at end of file diff --git a/catch/unit/complex/complex_make_negative_kernels.cc b/catch/unit/complex/complex_make_negative_kernels.cc new file mode 100644 index 0000000000..e18943f6f3 --- /dev/null +++ b/catch/unit/complex/complex_make_negative_kernels.cc @@ -0,0 +1,121 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. +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 WARRANNTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNNESS 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 INN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR INN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include +#include + +class Dummy { + public: + __device__ Dummy() {} + __device__ ~Dummy() {} +}; + +#define NEGATIVE_SHELL_MAKE_FLOAT(T, func_name) \ + __global__ void func_name##_kernel_v1(T* result, float* x, float y) { \ + *result = func_name(x, y); \ + } \ + __global__ void func_name##_kernel_v2(T* result, float x, float* y) { \ + *result = func_name(x, y); \ + } \ + __global__ void func_name##_kernel_v3(T* result, T x, float y) { *result = func_name(x, y); } \ + __global__ void func_name##_kernel_v4(T* result, float x, T y) { *result = func_name(x, y); } \ + __global__ void func_name##_kernel_v5(T* result, Dummy x, float y) { \ + *result = func_name(x, y); \ + } \ + __global__ void func_name##_kernel_v6(T* result, float x, Dummy y) { \ + *result = func_name(x, y); \ + } \ + __global__ void func_name##_kernel_v7(float* result, float x, float y) { \ + *result = func_name(x, y); \ + } \ + __global__ void func_name##_kernel_v8(hipDoubleComplex* result, float x, float y) { \ + *result = func_name(x, y); \ + } \ + __global__ void func_name##_kernel_v9(Dummy* result, float x, float y) { \ + *result = func_name(x, y); \ + } \ + void func_name##_v1(T* result, float* x, float y) { *result = func_name(x, y); } \ + void func_name##_v2(T* result, float x, float* y) { *result = func_name(x, y); } \ + void func_name##_v3(T* result, T x, float y) { *result = func_name(x, y); } \ + void func_name##_v4(T* result, float x, T y) { *result = func_name(x, y); } \ + void func_name##_v5(T* result, Dummy x, float y) { *result = func_name(x, y); } \ + void func_name##_v6(T* result, float x, Dummy y) { *result = func_name(x, y); } \ + void func_name##_v7(float* result, float x, float y) { *result = func_name(x, y); } \ + void func_name##_v8(hipDoubleComplex* result, float x, float y) { *result = func_name(x, y); } \ + void func_name##_v9(Dummy* result, float x, float y) { *result = func_name(x, y); } + +__global__ void make_hipDoubleComplex_kernel_v1(hipDoubleComplex* result, double* x, double y) { + *result = make_hipDoubleComplex(x, y); +} +__global__ void make_hipDoubleComplex_kernel_v2(hipDoubleComplex* result, double x, double* y) { + *result = make_hipDoubleComplex(x, y); +} +__global__ void make_hipDoubleComplex_kernel_v3(hipDoubleComplex* result, hipDoubleComplex x, + double y) { + *result = make_hipDoubleComplex(x, y); +} +__global__ void make_hipDoubleComplex_kernel_v4(hipDoubleComplex* result, double x, + hipDoubleComplex y) { + *result = make_hipDoubleComplex(x, y); +} +__global__ void make_hipDoubleComplex_kernel_v5(hipDoubleComplex* result, Dummy x, double y) { + *result = make_hipDoubleComplex(x, y); +} +__global__ void make_hipDoubleComplex_kernel_v6(hipDoubleComplex* result, double x, Dummy y) { + *result = make_hipDoubleComplex(x, y); +} +__global__ void make_hipDoubleComplex_kernel_v7(double* result, double x, double y) { + *result = make_hipDoubleComplex(x, y); +} +__global__ void make_hipDoubleComplex_kernel_v8(hipFloatComplex* result, double x, double y) { + *result = make_hipDoubleComplex(x, y); +} +__global__ void make_hipDoubleComplex_kernel_v9(Dummy* result, double x, double y) { + *result = make_hipDoubleComplex(x, y); +} +void make_hipDoubleComplex_v1(hipDoubleComplex* result, double* x, double y) { + *result = make_hipDoubleComplex(x, y); +} +void make_hipDoubleComplex_v2(hipDoubleComplex* result, double x, double* y) { + *result = make_hipDoubleComplex(x, y); +} +void make_hipDoubleComplex_v3(hipDoubleComplex* result, hipDoubleComplex x, double y) { + *result = make_hipDoubleComplex(x, y); +} +void make_hipDoubleComplex_v4(hipDoubleComplex* result, double x, hipDoubleComplex y) { + *result = make_hipDoubleComplex(x, y); +} +void make_hipDoubleComplex_v5(hipDoubleComplex* result, Dummy x, double y) { + *result = make_hipDoubleComplex(x, y); +} +void make_hipDoubleComplex_v6(hipDoubleComplex* result, double x, Dummy y) { + *result = make_hipDoubleComplex(x, y); +} +void make_hipDoubleComplex_v7(float* result, double x, double y) { + *result = make_hipDoubleComplex(x, y); +} +void make_hipDoubleComplex_v8(hipFloatComplex* result, double x, double y) { + *result = make_hipDoubleComplex(x, y); +} +void make_hipDoubleComplex_v9(Dummy* result, double x, double y) { + *result = make_hipDoubleComplex(x, y); +} + +NEGATIVE_SHELL_MAKE_FLOAT(hipFloatComplex, make_hipFloatComplex) +NEGATIVE_SHELL_MAKE_FLOAT(hipComplex, make_hipComplex) \ No newline at end of file diff --git a/catch/unit/complex/complex_make_negative_kernels_rtc.hh b/catch/unit/complex/complex_make_negative_kernels_rtc.hh new file mode 100644 index 0000000000..c643d02c09 --- /dev/null +++ b/catch/unit/complex/complex_make_negative_kernels_rtc.hh @@ -0,0 +1,198 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. +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 WARRANNTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNNESS 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 INN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR INN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#pragma once + +static constexpr auto kMakeHipComplex{R"( + class Dummy { + public: + __device__ Dummy() {} + __device__ ~Dummy() {} + }; + __global__ void make_hipComplex_kernel_v1(hipComplex* result, float* x, float y) { + *result = make_hipComplex(x, y); + } + __global__ void make_hipComplex_kernel_v2(hipComplex* result, float x, float* y) { + *result = make_hipComplex(x, y); + } + __global__ void make_hipComplex_kernel_v3(hipComplex* result, hipComplex x, float y) { + *result = make_hipComplex(x, y); + } + __global__ void make_hipComplex_kernel_v4(hipComplex* result, float x, hipComplex y) { + *result = make_hipComplex(x, y); + } + __global__ void make_hipComplex_kernel_v5(hipComplex* result, Dummy x, float y) { + *result = make_hipComplex(x, y); + } + __global__ void make_hipComplex_kernel_v6(hipComplex* result, float x, Dummy y) { + *result = make_hipComplex(x, y); + } + __global__ void make_hipComplex_kernel_v7(float* result, float x, float y) { + *result = make_hipComplex(x, y); + } + __global__ void make_hipComplex_kernel_v8(hipDoubleComplex* result, float x, float y) { + *result = make_hipComplex(x, y); + } + __global__ void make_hipComplex_kernel_v9(Dummy* result, float x, float y) { + *result = make_hipComplex(x, y); + } + void make_hipComplex_v1(hipComplex* result, float* x, float y) { *result = make_hipComplex(x, y); } + void make_hipComplex_v2(hipComplex* result, float x, float* y) { *result = make_hipComplex(x, y); } + void make_hipComplex_v3(hipComplex* result, hipComplex x, float y) { + *result = make_hipComplex(x, y); + } + void make_hipComplex_v4(hipComplex* result, float x, hipComplex y) { + *result = make_hipComplex(x, y); + } + void make_hipComplex_v5(hipComplex* result, Dummy x, float y) { *result = make_hipComplex(x, y); } + void make_hipComplex_v6(hipComplex* result, float x, Dummy y) { *result = make_hipComplex(x, y); } + void make_hipComplex_v7(float* result, float x, float y) { *result = make_hipComplex(x, y); } + void make_hipComplex_v8(hipDoubleComplex* result, float x, float y) { + *result = make_hipComplex(x, y); + } + void make_hipComplex_v9(Dummy* result, float x, float y) { *result = make_hipComplex(x, y); } +)"}; + +static constexpr auto kMakeHipFloatComplex{R"( + class Dummy { + public: + __device__ Dummy() {} + __device__ ~Dummy() {} + }; + __global__ void make_hipFloatComplex_kernel_v1(hipFloatComplex* result, float* x, float y) { + *result = make_hipFloatComplex(x, y); + } + __global__ void make_hipFloatComplex_kernel_v2(hipFloatComplex* result, float x, float* y) { + *result = make_hipFloatComplex(x, y); + } + __global__ void make_hipFloatComplex_kernel_v3(hipFloatComplex* result, hipFloatComplex x, + float y) { + *result = make_hipFloatComplex(x, y); + } + __global__ void make_hipFloatComplex_kernel_v4(hipFloatComplex* result, float x, + hipFloatComplex y) { + *result = make_hipFloatComplex(x, y); + } + __global__ void make_hipFloatComplex_kernel_v5(hipFloatComplex* result, Dummy x, float y) { + *result = make_hipFloatComplex(x, y); + } + __global__ void make_hipFloatComplex_kernel_v6(hipFloatComplex* result, float x, Dummy y) { + *result = make_hipFloatComplex(x, y); + } + __global__ void make_hipFloatComplex_kernel_v7(float* result, float x, float y) { + *result = make_hipFloatComplex(x, y); + } + __global__ void make_hipFloatComplex_kernel_v8(hipDoubleComplex* result, float x, float y) { + *result = make_hipFloatComplex(x, y); + } + __global__ void make_hipFloatComplex_kernel_v9(Dummy* result, float x, float y) { + *result = make_hipFloatComplex(x, y); + } + void make_hipFloatComplex_v1(hipFloatComplex* result, float* x, float y) { + *result = make_hipFloatComplex(x, y); + } + void make_hipFloatComplex_v2(hipFloatComplex* result, float x, float* y) { + *result = make_hipFloatComplex(x, y); + } + void make_hipFloatComplex_v3(hipFloatComplex* result, hipFloatComplex x, float y) { + *result = make_hipFloatComplex(x, y); + } + void make_hipFloatComplex_v4(hipFloatComplex* result, float x, hipFloatComplex y) { + *result = make_hipFloatComplex(x, y); + } + void make_hipFloatComplex_v5(hipFloatComplex* result, Dummy x, float y) { + *result = make_hipFloatComplex(x, y); + } + void make_hipFloatComplex_v6(hipFloatComplex* result, float x, Dummy y) { + *result = make_hipFloatComplex(x, y); + } + void make_hipFloatComplex_v7(float* result, float x, float y) { + *result = make_hipFloatComplex(x, y); + } + void make_hipFloatComplex_v8(hipDoubleComplex* result, float x, float y) { + *result = make_hipFloatComplex(x, y); + } + void make_hipFloatComplex_v9(Dummy* result, float x, float y) { + *result = make_hipFloatComplex(x, y); + } +)"}; + +static constexpr auto kMakeHipDoubleComplex{R"( + class Dummy { + public: + __device__ Dummy() {} + __device__ ~Dummy() {} + }; + __global__ void make_hipDoubleComplex_kernel_v1(hipDoubleComplex* result, double* x, double y) { + *result = make_hipDoubleComplex(x, y); + } + __global__ void make_hipDoubleComplex_kernel_v2(hipDoubleComplex* result, double x, double* y) { + *result = make_hipDoubleComplex(x, y); + } + __global__ void make_hipDoubleComplex_kernel_v3(hipDoubleComplex* result, hipDoubleComplex x, + double y) { + *result = make_hipDoubleComplex(x, y); + } + __global__ void make_hipDoubleComplex_kernel_v4(hipDoubleComplex* result, double x, + hipDoubleComplex y) { + *result = make_hipDoubleComplex(x, y); + } + __global__ void make_hipDoubleComplex_kernel_v5(hipDoubleComplex* result, Dummy x, double y) { + *result = make_hipDoubleComplex(x, y); + } + __global__ void make_hipDoubleComplex_kernel_v6(hipDoubleComplex* result, double x, Dummy y) { + *result = make_hipDoubleComplex(x, y); + } + __global__ void make_hipDoubleComplex_kernel_v7(double* result, double x, double y) { + *result = make_hipDoubleComplex(x, y); + } + __global__ void make_hipDoubleComplex_kernel_v8(hipFloatComplex* result, double x, double y) { + *result = make_hipDoubleComplex(x, y); + } + __global__ void make_hipDoubleComplex_kernel_v9(Dummy* result, double x, double y) { + *result = make_hipDoubleComplex(x, y); + } + void make_hipDoubleComplex_v1(hipDoubleComplex* result, double* x, double y) { + *result = make_hipDoubleComplex(x, y); + } + void make_hipDoubleComplex_v2(hipDoubleComplex* result, double x, double* y) { + *result = make_hipDoubleComplex(x, y); + } + void make_hipDoubleComplex_v3(hipDoubleComplex* result, hipDoubleComplex x, double y) { + *result = make_hipDoubleComplex(x, y); + } + void make_hipDoubleComplex_v4(hipDoubleComplex* result, double x, hipDoubleComplex y) { + *result = make_hipDoubleComplex(x, y); + } + void make_hipDoubleComplex_v5(hipDoubleComplex* result, Dummy x, double y) { + *result = make_hipDoubleComplex(x, y); + } + void make_hipDoubleComplex_v6(hipDoubleComplex* result, double x, Dummy y) { + *result = make_hipDoubleComplex(x, y); + } + void make_hipDoubleComplex_v7(float* result, double x, double y) { + *result = make_hipDoubleComplex(x, y); + } + void make_hipDoubleComplex_v8(hipFloatComplex* result, double x, double y) { + *result = make_hipDoubleComplex(x, y); + } + void make_hipDoubleComplex_v9(Dummy* result, double x, double y) { + *result = make_hipDoubleComplex(x, y); + } +)"}; diff --git a/catch/unit/complex/complex_negative_kernels_1Arg_double.cc b/catch/unit/complex/complex_negative_kernels_1Arg_double.cc new file mode 100644 index 0000000000..31ff6ce5b8 --- /dev/null +++ b/catch/unit/complex/complex_negative_kernels_1Arg_double.cc @@ -0,0 +1,75 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. +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 WARRANNTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNNESS 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 INN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR INN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include +#include + +class Dummy { + public: + __device__ Dummy() {} + __device__ ~Dummy() {} +}; + +#define NEGATIVE_SHELL_ONE_ARG_DOUBLE(func_name) \ + __global__ void func_name##_kernel_v1(double* result, hipDoubleComplex* x) { \ + *result = func_name(x); \ + } \ + __global__ void func_name##_kernel_v2(double* result, hipFloatComplex x) { \ + *result = func_name(x); \ + } \ + __global__ void func_name##_kernel_v3(double* result, double x) { *result = func_name(x); } \ + __global__ void func_name##_kernel_v4(double* result, Dummy x) { *result = func_name(x); } \ + __global__ void func_name##_kernel_v5(hipDoubleComplex* result, hipDoubleComplex x) { \ + *result = func_name(x); \ + } \ + __global__ void func_name##_kernel_v6(Dummy* result, hipDoubleComplex x) { \ + *result = func_name(x); \ + } \ + void func_name##_v1(double* result, hipDoubleComplex* x) { *result = func_name(x); } \ + void func_name##_v2(double* result, hipFloatComplex x) { *result = func_name(x); } \ + void func_name##_v3(double* result, double x) { *result = func_name(x); } \ + void func_name##_v4(double* result, Dummy x) { *result = func_name(x); } \ + void func_name##_v5(hipDoubleComplex* result, hipDoubleComplex x) { *result = func_name(x); } \ + void func_name##_v6(Dummy* result, hipDoubleComplex x) { *result = func_name(x); } + +__global__ void hipConj_kernel_v1(hipDoubleComplex* result, hipDoubleComplex* x) { + *result = hipConj(x); +} +__global__ void hipConj_kernel_v2(hipDoubleComplex* result, hipFloatComplex x) { + *result = hipConj(x); +} +__global__ void hipConj_kernel_v3(hipDoubleComplex* result, double x) { *result = hipConj(x); } +__global__ void hipConj_kernel_v4(hipDoubleComplex* result, Dummy x) { *result = hipConj(x); } +__global__ void hipConj_kernel_v5(double* result, hipDoubleComplex x) { *result = hipConj(x); } +__global__ void hipConj_kernel_v6(hipFloatComplex* result, hipDoubleComplex x) { + *result = hipConj(x); +} +__global__ void hipConj_kernel_v7(Dummy* result, hipDoubleComplex x) { *result = hipConj(x); } +void hipConj_v1(hipDoubleComplex* result, hipDoubleComplex* x) { *result = hipConj(x); } +void hipConj_v2(hipDoubleComplex* result, hipFloatComplex x) { *result = hipConj(x); } +void hipConj_v3(hipDoubleComplex* result, double x) { *result = hipConj(x); } +void hipConj_v4(hipDoubleComplex* result, Dummy x) { *result = hipConj(x); } +void hipConj_v5(double* result, hipDoubleComplex x) { *result = hipConj(x); } +void hipConj_v6(hipFloatComplex* result, hipDoubleComplex x) { *result = hipConj(x); } +void hipConj_v7(Dummy* result, hipDoubleComplex x) { *result = hipConj(x); } + +NEGATIVE_SHELL_ONE_ARG_DOUBLE(hipCreal) +NEGATIVE_SHELL_ONE_ARG_DOUBLE(hipCimag) +NEGATIVE_SHELL_ONE_ARG_DOUBLE(hipCabs) +NEGATIVE_SHELL_ONE_ARG_DOUBLE(hipCsqabs) diff --git a/catch/unit/complex/complex_negative_kernels_1Arg_float.cc b/catch/unit/complex/complex_negative_kernels_1Arg_float.cc new file mode 100644 index 0000000000..381ea35c0b --- /dev/null +++ b/catch/unit/complex/complex_negative_kernels_1Arg_float.cc @@ -0,0 +1,75 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. +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 WARRANNTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNNESS 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 INN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR INN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include +#include + +class Dummy { + public: + __device__ Dummy() {} + __device__ ~Dummy() {} +}; + +#define NEGATIVE_SHELL_ONE_ARG_FLOAT(func_name) \ + __global__ void func_name##_kernel_v1(float* result, hipFloatComplex* x) { \ + *result = func_name(x); \ + } \ + __global__ void func_name##_kernel_v2(float* result, hipDoubleComplex x) { \ + *result = func_name(x); \ + } \ + __global__ void func_name##_kernel_v3(float* result, float x) { *result = func_name(x); } \ + __global__ void func_name##_kernel_v4(float* result, Dummy x) { *result = func_name(x); } \ + __global__ void func_name##_kernel_v5(hipFloatComplex* result, hipFloatComplex x) { \ + *result = func_name(x); \ + } \ + __global__ void func_name##_kernel_v6(Dummy* result, hipFloatComplex x) { \ + *result = func_name(x); \ + } \ + void func_name##_v1(float* result, hipFloatComplex* x) { *result = func_name(x); } \ + void func_name##_v2(float* result, hipDoubleComplex x) { *result = func_name(x); } \ + void func_name##_v3(float* result, float x) { *result = func_name(x); } \ + void func_name##_v4(float* result, Dummy x) { *result = func_name(x); } \ + void func_name##_v5(hipFloatComplex* result, hipFloatComplex x) { *result = func_name(x); } \ + void func_name##_v6(Dummy* result, hipFloatComplex x) { *result = func_name(x); } + +__global__ void hipConjf_kernel_v1(hipFloatComplex* result, hipFloatComplex* x) { + *result = hipConjf(x); +} +__global__ void hipConjf_kernel_v2(hipFloatComplex* result, hipDoubleComplex x) { + *result = hipConjf(x); +} +__global__ void hipConjf_kernel_v3(hipFloatComplex* result, float x) { *result = hipConjf(x); } +__global__ void hipConjf_kernel_v4(hipFloatComplex* result, Dummy x) { *result = hipConjf(x); } +__global__ void hipConjf_kernel_v5(float* result, hipFloatComplex x) { *result = hipConjf(x); } +__global__ void hipConjf_kernel_v6(hipDoubleComplex* result, hipFloatComplex x) { + *result = hipConjf(x); +} +__global__ void hipConjf_kernel_v7(Dummy* result, hipFloatComplex x) { *result = hipConjf(x); } +void hipConjf_v1(hipFloatComplex* result, hipFloatComplex* x) { *result = hipConjf(x); } +void hipConjf_v2(hipFloatComplex* result, hipDoubleComplex x) { *result = hipConjf(x); } +void hipConjf_v3(hipFloatComplex* result, float x) { *result = hipConjf(x); } +void hipConjf_v4(hipFloatComplex* result, Dummy x) { *result = hipConjf(x); } +void hipConjf_v5(float* result, hipFloatComplex x) { *result = hipConjf(x); } +void hipConjf_v6(hipDoubleComplex* result, hipFloatComplex x) { *result = hipConjf(x); } +void hipConjf_v7(Dummy* result, hipFloatComplex x) { *result = hipConjf(x); } + +NEGATIVE_SHELL_ONE_ARG_FLOAT(hipCrealf) +NEGATIVE_SHELL_ONE_ARG_FLOAT(hipCimagf) +NEGATIVE_SHELL_ONE_ARG_FLOAT(hipCabsf) +NEGATIVE_SHELL_ONE_ARG_FLOAT(hipCsqabsf) diff --git a/catch/unit/complex/complex_negative_kernels_1Arg_rtc.hh b/catch/unit/complex/complex_negative_kernels_1Arg_rtc.hh new file mode 100644 index 0000000000..ad631a3701 --- /dev/null +++ b/catch/unit/complex/complex_negative_kernels_1Arg_rtc.hh @@ -0,0 +1,207 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. +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 WARRANNTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNNESS 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 INN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR INN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#pragma once + +static constexpr auto kComplexConj{R"( + __global__ void hipConjf_kernel_v1(hipFloatComplex* result, hipFloatComplex* x) { + *result = hipConjf(x); + } + __global__ void hipConjf_kernel_v2(hipFloatComplex* result, hipDoubleComplex x) { + *result = hipConjf(x); + } + __global__ void hipConjf_kernel_v3(hipFloatComplex* result, float x) { *result = hipConjf(x); } + __global__ void hipConjf_kernel_v4(hipFloatComplex* result, Dummy x) { *result = hipConjf(x); } + __global__ void hipConjf_kernel_v5(float* result, hipFloatComplex x) { *result = hipConjf(x); } + __global__ void hipConjf_kernel_v6(hipDoubleComplex* result, hipFloatComplex x) { + *result = hipConjf(x); + } + __global__ void hipConjf_kernel_v7(Dummy* result, hipFloatComplex x) { *result = hipConjf(x); } + __global__ void hipConj_kernel_v1(hipDoubleComplex* result, hipDoubleComplex* x) { + *result = hipConj(x); + } + __global__ void hipConj_kernel_v2(hipDoubleComplex* result, hipFloatComplex x) { + *result = hipConj(x); + } + __global__ void hipConj_kernel_v3(hipDoubleComplex* result, double x) { *result = hipConj(x); } + __global__ void hipConj_kernel_v4(hipDoubleComplex* result, Dummy x) { *result = hipConj(x); } + __global__ void hipConj_kernel_v5(double* result, hipDoubleComplex x) { *result = hipConj(x); } + __global__ void hipConj_kernel_v6(hipFloatComplex* result, hipDoubleComplex x) { + *result = hipConj(x); + } + __global__ void hipConj_kernel_v7(Dummy* result, hipDoubleComplex x) { *result = hipConj(x); } + void hipConjf_v1(hipFloatComplex* result, hipFloatComplex* x) { *result = hipConjf(x); } + void hipConjf_v2(hipFloatComplex* result, hipDoubleComplex x) { *result = hipConjf(x); } + void hipConjf_v3(hipFloatComplex* result, float x) { *result = hipConjf(x); } + void hipConjf_v4(hipFloatComplex* result, Dummy x) { *result = hipConjf(x); } + void hipConjf_v5(float* result, hipFloatComplex x) { *result = hipConjf(x); } + void hipConjf_v6(hipDoubleComplex* result, hipFloatComplex x) { *result = hipConjf(x); } + void hipConjf_v7(Dummy* result, hipFloatComplex x) { *result = hipConjf(x); } + void hipConj_v1(hipDoubleComplex* result, hipDoubleComplex* x) { *result = hipConj(x); } + void hipConj_v2(hipDoubleComplex* result, hipFloatComplex x) { *result = hipConj(x); } + void hipConj_v3(hipDoubleComplex* result, double x) { *result = hipConj(x); } + void hipConj_v4(hipDoubleComplex* result, Dummy x) { *result = hipConj(x); } + void hipConj_v5(double* result, hipDoubleComplex x) { *result = hipConj(x); } + void hipConj_v6(hipFloatComplex* result, hipDoubleComplex x) { *result = hipConj(x); } + void hipConj_v7(Dummy* result, hipDoubleComplex x) { *result = hipConj(x); } +)"}; + +static constexpr auto kComplexReal{R"( + class Dummy { + public: + __device__ Dummy() {} + __device__ ~Dummy() {} + }; + __global__ void hipCrealf_kernel_v1(float* result, hipFloatComplex* x) { *result = hipCrealf(x); } + __global__ void hipCrealf_kernel_v2(float* result, hipDoubleComplex x) { *result = hipCrealf(x); } + __global__ void hipCrealf_kernel_v3(float* result, float x) { *result = hipCrealf(x); } + __global__ void hipCrealf_kernel_v4(float* result, Dummy x) { *result = hipCrealf(x); } + __global__ void hipCrealf_kernel_v5(hipFloatComplex* result, hipFloatComplex x) { + *result = hipCrealf(x); + } + __global__ void hipCrealf_kernel_v6(Dummy* result, hipFloatComplex x) { *result = hipCrealf(x); } + __global__ void hipCreal_kernel_v1(double* result, hipDoubleComplex* x) { *result = hipCreal(x); } + __global__ void hipCreal_kernel_v2(double* result, hipFloatComplex x) { *result = hipCreal(x); } + __global__ void hipCreal_kernel_v3(double* result, double x) { *result = hipCreal(x); } + __global__ void hipCreal_kernel_v4(double* result, Dummy x) { *result = hipCreal(x); } + __global__ void hipCreal_kernel_v5(hipDoubleComplex* result, hipDoubleComplex x) { + *result = hipCreal(x); + } + __global__ void hipCreal_kernel_v6(Dummy* result, hipDoubleComplex x) { *result = hipCreal(x); } + void hipCrealf_v1(float* result, hipFloatComplex* x) { *result = hipCrealf(x); } + void hipCrealf_v2(float* result, hipDoubleComplex x) { *result = hipCrealf(x); } + void hipCrealf_v3(float* result, float x) { *result = hipCrealf(x); } + void hipCrealf_v4(float* result, Dummy x) { *result = hipCrealf(x); } + void hipCrealf_v5(hipFloatComplex* result, hipFloatComplex x) { *result = hipCrealf(x); } + void hipCrealf_v6(Dummy* result, hipFloatComplex x) { *result = hipCrealf(x); } + void hipCreal_v1(double* result, hipDoubleComplex* x) { *result = hipCreal(x); } + void hipCreal_v2(double* result, hipFloatComplex x) { *result = hipCreal(x); } + void hipCreal_v3(double* result, double x) { *result = hipCreal(x); } + void hipCreal_v4(double* result, Dummy x) { *result = hipCreal(x); } + void hipCreal_v5(hipDoubleComplex* result, hipDoubleComplex x) { *result = hipCreal(x); } + void hipCreal_v6(Dummy* result, hipDoubleComplex x) { *result = hipCreal(x); } +)"}; + +static constexpr auto kComplexImag{R"( + class Dummy { + public: + __device__ Dummy() {} + __device__ ~Dummy() {} + }; + __global__ void hipCimagf_kernel_v1(float* result, hipFloatComplex* x) { *result = hipCimagf(x); } + __global__ void hipCimagf_kernel_v2(float* result, hipDoubleComplex x) { *result = hipCimagf(x); } + __global__ void hipCimagf_kernel_v3(float* result, float x) { *result = hipCimagf(x); } + __global__ void hipCimagf_kernel_v4(float* result, Dummy x) { *result = hipCimagf(x); } + __global__ void hipCimagf_kernel_v5(hipFloatComplex* result, hipFloatComplex x) { + *result = hipCimagf(x); + } + __global__ void hipCimagf_kernel_v6(Dummy* result, hipFloatComplex x) { *result = hipCimagf(x); } + __global__ void hipCimag_kernel_v1(double* result, hipDoubleComplex* x) { *result = hipCimag(x); } + __global__ void hipCimag_kernel_v2(double* result, hipFloatComplex x) { *result = hipCimag(x); } + __global__ void hipCimag_kernel_v3(double* result, double x) { *result = hipCimag(x); } + __global__ void hipCimag_kernel_v4(double* result, Dummy x) { *result = hipCimag(x); } + __global__ void hipCimag_kernel_v5(hipDoubleComplex* result, hipDoubleComplex x) { + *result = hipCimag(x); + } + __global__ void hipCimag_kernel_v6(Dummy* result, hipDoubleComplex x) { *result = hipCimag(x); } + void hipCimagf_v1(float* result, hipFloatComplex* x) { *result = hipCimagf(x); } + void hipCimagf_v2(float* result, hipDoubleComplex x) { *result = hipCimagf(x); } + void hipCimagf_v3(float* result, float x) { *result = hipCimagf(x); } + void hipCimagf_v4(float* result, Dummy x) { *result = hipCimagf(x); } + void hipCimagf_v5(hipFloatComplex* result, hipFloatComplex x) { *result = hipCimagf(x); } + void hipCimagf_v6(Dummy* result, hipFloatComplex x) { *result = hipCimagf(x); } + void hipCimag_v1(double* result, hipDoubleComplex* x) { *result = hipCimag(x); } + void hipCimag_v2(double* result, hipFloatComplex x) { *result = hipCimag(x); } + void hipCimag_v3(double* result, double x) { *result = hipCimag(x); } + void hipCimag_v4(double* result, Dummy x) { *result = hipCimag(x); } + void hipCimag_v5(hipDoubleComplex* result, hipDoubleComplex x) { *result = hipCimag(x); } + void hipCimag_v6(Dummy* result, hipDoubleComplex x) { *result = hipCimag(x); } +)"}; + +static constexpr auto kComplexAbs{R"( + class Dummy { + public: + __device__ Dummy() {} + __device__ ~Dummy() {} + }; + __global__ void hipCabsf_kernel_v1(float* result, hipFloatComplex* x) { *result = hipCabsf(x); } + __global__ void hipCabsf_kernel_v2(float* result, hipDoubleComplex x) { *result = hipCabsf(x); } + __global__ void hipCabsf_kernel_v3(float* result, float x) { *result = hipCabsf(x); } + __global__ void hipCabsf_kernel_v4(float* result, Dummy x) { *result = hipCabsf(x); } + __global__ void hipCabsf_kernel_v5(hipFloatComplex* result, hipFloatComplex x) { + *result = hipCabsf(x); + } + __global__ void hipCabsf_kernel_v6(Dummy* result, hipFloatComplex x) { *result = hipCabsf(x); } + __global__ void hipCabs_kernel_v1(double* result, hipDoubleComplex* x) { *result = hipCabs(x); } + __global__ void hipCabs_kernel_v2(double* result, hipFloatComplex x) { *result = hipCabs(x); } + __global__ void hipCabs_kernel_v3(double* result, double x) { *result = v(x); } + __global__ void hipCabs_kernel_v4(double* result, Dummy x) { *result = hipCabs(x); } + __global__ void hipCabs_kernel_v5(hipDoubleComplex* result, hipDoubleComplex x) { + *result = hipCabs(x); + } + __global__ void hipCabs_kernel_v6(Dummy* result, hipDoubleComplex x) { *result = hipCabs(x); } + void hipCabsf_v1(float* result, hipFloatComplex* x) { *result = hipCabsf(x); } + void hipCabsf_v2(float* result, hipDoubleComplex x) { *result = hipCabsf(x); } + void hipCabsf_v3(float* result, float x) { *result = hipCabsf(x); } + void hipCabsf_v4(float* result, Dummy x) { *result = hipCabsf(x); } + void hipCabsf_v5(hipFloatComplex* result, hipFloatComplex x) { *result = hipCabsf(x); } + void hipCabsf_v6(Dummy* result, hipFloatComplex x) { *result = hipCabsf(x); } + void hipCabs_v1(double* result, hipDoubleComplex* x) { *result = hipCabs(x); } + void hipCabs_v2(double* result, hipFloatComplex x) { *result = hipCabs(x); } + void hipCabs_v3(double* result, double x) { *result = hipCabs(x); } + void hipCabs_v4(double* result, Dummy x) { *result = hipCabs(x); } + void hipCabs_v5(hipDoubleComplex* result, hipDoubleComplex x) { *result = hipCabs(x); } + void hipCabs_v6(Dummy* result, hipDoubleComplex x) { *result = hipCabs(x); } +)"}; + +static constexpr auto kComplexSqabs{R"( + class Dummy { + public: + __device__ Dummy() {} + __device__ ~Dummy() {} + }; + __global__ void hipCsqabsf_kernel_v1(float* result, hipFloatComplex* x) { *result = hipCsqabsf(x); } + __global__ void hipCsqabsf_kernel_v2(float* result, hipDoubleComplex x) { *result = hipCsqabsf(x); } + __global__ void hipCsqabsf_kernel_v3(float* result, float x) { *result = hipCsqabsf(x); } + __global__ void hipCsqabsf_kernel_v4(float* result, Dummy x) { *result = hipCsqabsf(x); } + __global__ void hipCsqabsf_kernel_v5(hipFloatComplex* result, hipFloatComplex x) { + *result = hipCsqabsf(x); + } + __global__ void hipCsqabsf_kernel_v6(Dummy* result, hipFloatComplex x) { *result = hipCsqabs(x); } + __global__ void hipCsqabs_kernel_v1(double* result, hipDoubleComplex* x) { *result = hipCsqabs(x); } + __global__ void hipCsqabs_kernel_v2(double* result, hipFloatComplex x) { *result = hipCsqabs(x); } + __global__ void hipCsqabs_kernel_v3(double* result, double x) { *result = hipCsqabs(x); } + __global__ void hipCsqabs_kernel_v4(double* result, Dummy x) { *result = hipCsqabs(x); } + __global__ void hipCsqabs_kernel_v5(hipDoubleComplex* result, hipDoubleComplex x) { + *result = hipCsqabs(x); + } + __global__ void hipCsqabs_kernel_v6(Dummy* result, hipDoubleComplex x) { *result = hipCsqabs(x); } + void hipCsqabsf_v1(float* result, hipFloatComplex* x) { *result = hipCsqabsf(x); } + void hipCsqabsf_v2(float* result, hipDoubleComplex x) { *result = hipCsqabsf(x); } + void hipCsqabsf_v3(float* result, float x) { *result = hipCsqabsf(x); } + void hipCsqabsf_v4(float* result, Dummy x) { *result = hipCsqabsf(x); } + void hipCsqabsf_v5(hipFloatComplex* result, hipFloatComplex x) { *result = hipCsqabsf(x); } + void hipCsqabsf_v6(Dummy* result, hipFloatComplex x) { *result = hipCsqabsf(x); } + void hipCsqabs_v1(double* result, hipDoubleComplex* x) { *result = hipCsqabs(x); } + void hipCsqabs_v2(double* result, hipFloatComplex x) { *result = hipCsqabs(x); } + void hipCsqabs_v3(double* result, double x) { *result = hipCsqabs(x); } + void hipCsqabs_v4(double* result, Dummy x) { *result = hipCsqabs(x); } + void hipCsqabs_v5(hipDoubleComplex* result, hipDoubleComplex x) { *result = hipCsqabs(x); } + void hipCsqabs_v6(Dummy* result, hipDoubleComplex x) { *result = hipCsqabs(x); } +)"}; diff --git a/catch/unit/complex/complex_negative_kernels_2Arg_double.cc b/catch/unit/complex/complex_negative_kernels_2Arg_double.cc new file mode 100644 index 0000000000..c3e29c812c --- /dev/null +++ b/catch/unit/complex/complex_negative_kernels_2Arg_double.cc @@ -0,0 +1,105 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. +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 WARRANNTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNNESS 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 INN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR INN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include +#include + +class Dummy { + public: + __device__ Dummy() {} + __device__ ~Dummy() {} +}; + +#define NEGATIVE_SHELL_TWO_ARG_DOUBLE(func_name) \ + __global__ void func_name##_kernel_v1(hipDoubleComplex* result, hipDoubleComplex* x, \ + hipDoubleComplex y) { \ + *result = func_name(x, y); \ + } \ + __global__ void func_name##_kernel_v2(hipDoubleComplex* result, hipDoubleComplex x, \ + hipDoubleComplex* y) { \ + *result = func_name(x, y); \ + } \ + __global__ void func_name##_kernel_v3(hipDoubleComplex* result, double x, hipDoubleComplex y) { \ + *result = func_name(x, y); \ + } \ + __global__ void func_name##_kernel_v4(hipDoubleComplex* result, hipDoubleComplex x, double y) { \ + *result = func_name(x, y); \ + } \ + __global__ void func_name##_kernel_v5(hipDoubleComplex* result, hipFloatComplex x, \ + hipDoubleComplex y) { \ + *result = func_name(x, y); \ + } \ + __global__ void func_name##_kernel_v6(hipDoubleComplex* result, hipDoubleComplex x, \ + hipFloatComplex y) { \ + *result = func_name(x, y); \ + } \ + __global__ void func_name##_kernel_v7(hipDoubleComplex* result, Dummy x, hipDoubleComplex y) { \ + *result = func_name(x, y); \ + } \ + __global__ void func_name##_kernel_v8(hipDoubleComplex* result, hipDoubleComplex x, Dummy y) { \ + *result = func_name(x, y); \ + } \ + __global__ void func_name##_kernel_v9(double* result, hipDoubleComplex x, hipDoubleComplex y) { \ + *result = func_name(x, y); \ + } \ + __global__ void func_name##_kernel_v10(hipFloatComplex* result, hipDoubleComplex x, \ + hipDoubleComplex y) { \ + *result = func_name(x, y); \ + } \ + __global__ void func_name##_kernel_v11(Dummy* result, hipDoubleComplex x, hipDoubleComplex y) { \ + *result = func_name(x, y); \ + } \ + void func_name##_v1(hipDoubleComplex* result, hipDoubleComplex* x, hipDoubleComplex y) { \ + *result = func_name(x, y); \ + } \ + void func_name##_v2(hipDoubleComplex* result, hipDoubleComplex x, hipDoubleComplex* y) { \ + *result = func_name(x, y); \ + } \ + void func_name##_v3(hipDoubleComplex* result, double x, hipDoubleComplex y) { \ + *result = func_name(x, y); \ + } \ + void func_name##_v4(hipDoubleComplex* result, hipDoubleComplex x, double y) { \ + *result = func_name(x, y); \ + } \ + void func_name##_v5(hipDoubleComplex* result, hipFloatComplex x, hipDoubleComplex y) { \ + *result = func_name(x, y); \ + } \ + void func_name##_v6(hipDoubleComplex* result, hipDoubleComplex x, hipFloatComplex y) { \ + *result = func_name(x, y); \ + } \ + void func_name##_v7(hipDoubleComplex* result, Dummy x, hipDoubleComplex y) { \ + *result = func_name(x, y); \ + } \ + void func_name##_v8(hipDoubleComplex* result, hipDoubleComplex x, Dummy y) { \ + *result = func_name(x, y); \ + } \ + void func_name##_v9(double* result, hipDoubleComplex x, hipDoubleComplex y) { \ + *result = func_name(x, y); \ + } \ + void func_name##_v10(hipFloatComplex* result, hipDoubleComplex x, hipDoubleComplex y) { \ + *result = func_name(x, y); \ + } \ + void func_name##_v11(Dummy* result, hipDoubleComplex x, hipDoubleComplex y) { \ + *result = func_name(x, y); \ + } + +NEGATIVE_SHELL_TWO_ARG_DOUBLE(hipCadd) +NEGATIVE_SHELL_TWO_ARG_DOUBLE(hipCsub) +NEGATIVE_SHELL_TWO_ARG_DOUBLE(hipCmul) +NEGATIVE_SHELL_TWO_ARG_DOUBLE(hipCdiv) \ No newline at end of file diff --git a/catch/unit/complex/complex_negative_kernels_2Arg_float.cc b/catch/unit/complex/complex_negative_kernels_2Arg_float.cc new file mode 100644 index 0000000000..5979155aef --- /dev/null +++ b/catch/unit/complex/complex_negative_kernels_2Arg_float.cc @@ -0,0 +1,105 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. +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 WARRANNTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNNESS 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 INN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR INN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include +#include + +class Dummy { + public: + __device__ Dummy() {} + __device__ ~Dummy() {} +}; + +#define NEGATIVE_SHELL_TWO_ARG_FLOAT(func_name) \ + __global__ void func_name##_kernel_v1(hipFloatComplex* result, hipFloatComplex* x, \ + hipFloatComplex y) { \ + *result = func_name(x, y); \ + } \ + __global__ void func_name##_kernel_v2(hipFloatComplex* result, hipFloatComplex x, \ + hipFloatComplex* y) { \ + *result = func_name(x, y); \ + } \ + __global__ void func_name##_kernel_v3(hipFloatComplex* result, float x, hipFloatComplex y) { \ + *result = func_name(x, y); \ + } \ + __global__ void func_name##_kernel_v4(hipFloatComplex* result, hipFloatComplex x, float y) { \ + *result = func_name(x, y); \ + } \ + __global__ void func_name##_kernel_v5(hipFloatComplex* result, hipDoubleComplex x, \ + hipFloatComplex y) { \ + *result = func_name(x, y); \ + } \ + __global__ void func_name##_kernel_v6(hipFloatComplex* result, hipFloatComplex x, \ + hipDoubleComplex y) { \ + *result = func_name(x, y); \ + } \ + __global__ void func_name##_kernel_v7(hipFloatComplex* result, Dummy x, hipFloatComplex y) { \ + *result = func_name(x, y); \ + } \ + __global__ void func_name##_kernel_v8(hipFloatComplex* result, hipFloatComplex x, Dummy y) { \ + *result = func_name(x, y); \ + } \ + __global__ void func_name##_kernel_v9(float* result, hipFloatComplex x, hipFloatComplex y) { \ + *result = func_name(x, y); \ + } \ + __global__ void func_name##_kernel_v10(hipDoubleComplex* result, hipFloatComplex x, \ + hipFloatComplex y) { \ + *result = func_name(x, y); \ + } \ + __global__ void func_name##_kernel_v11(Dummy* result, hipFloatComplex x, hipFloatComplex y) { \ + *result = func_name(x, y); \ + } \ + void func_name##_v1(hipFloatComplex* result, hipFloatComplex* x, hipFloatComplex y) { \ + *result = func_name(x, y); \ + } \ + void func_name##_v2(hipFloatComplex* result, hipFloatComplex x, hipFloatComplex* y) { \ + *result = func_name(x, y); \ + } \ + void func_name##_v3(hipFloatComplex* result, float x, hipFloatComplex y) { \ + *result = func_name(x, y); \ + } \ + void func_name##_v4(hipFloatComplex* result, hipFloatComplex x, float y) { \ + *result = func_name(x, y); \ + } \ + void func_name##_v5(hipFloatComplex* result, hipDoubleComplex x, hipFloatComplex y) { \ + *result = func_name(x, y); \ + } \ + void func_name##_v6(hipFloatComplex* result, hipFloatComplex x, hipDoubleComplex y) { \ + *result = func_name(x, y); \ + } \ + void func_name##_v7(hipFloatComplex* result, Dummy x, hipFloatComplex y) { \ + *result = func_name(x, y); \ + } \ + void func_name##_v8(hipFloatComplex* result, hipFloatComplex x, Dummy y) { \ + *result = func_name(x, y); \ + } \ + void func_name##_v9(float* result, hipFloatComplex x, hipFloatComplex y) { \ + *result = func_name(x, y); \ + } \ + void func_name##_v10(hipDoubleComplex* result, hipFloatComplex x, hipFloatComplex y) { \ + *result = func_name(x, y); \ + } \ + void func_name##_v11(Dummy* result, hipFloatComplex x, hipFloatComplex y) { \ + *result = func_name(x, y); \ + } + +NEGATIVE_SHELL_TWO_ARG_FLOAT(hipCaddf) +NEGATIVE_SHELL_TWO_ARG_FLOAT(hipCsubf) +NEGATIVE_SHELL_TWO_ARG_FLOAT(hipCmulf) +NEGATIVE_SHELL_TWO_ARG_FLOAT(hipCdivf) \ No newline at end of file diff --git a/catch/unit/complex/complex_negative_kernels_2Arg_rtc.hh b/catch/unit/complex/complex_negative_kernels_2Arg_rtc.hh new file mode 100644 index 0000000000..f3576a1ec8 --- /dev/null +++ b/catch/unit/complex/complex_negative_kernels_2Arg_rtc.hh @@ -0,0 +1,620 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. +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 WARRANNTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNNESS 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 INN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR INN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#pragma once + +static constexpr auto kComplexAdd{R"( + class Dummy { + public: + __device__ Dummy() {} + __device__ ~Dummy() {} + }; + __global__ void hipCaddf_kernel_v1(hipFloatComplex* result, hipFloatComplex* x, + hipFloatComplex y) { + *result = hipCaddf(x, y); + } + __global__ void hipCaddf_kernel_v2(hipFloatComplex* result, hipFloatComplex x, + hipFloatComplex* y) { + *result = hipCaddf(x, y); + } + __global__ void hipCaddf_kernel_v3(hipFloatComplex* result, float x, hipFloatComplex y) { + *result = hipCaddf(x, y); + } + __global__ void hipCaddf_kernel_v4(hipFloatComplex* result, hipFloatComplex x, float y) { + *result = hipCaddf(x, y); + } + __global__ void hipCaddf_kernel_v5(hipFloatComplex* result, hipDoubleComplex x, + hipFloatComplex y) { + *result = hipCaddf(x, y); + } + __global__ void hipCaddf_kernel_v6(hipFloatComplex* result, hipFloatComplex x, + hipDoubleComplex y) { + *result = hipCaddf(x, y); + } + __global__ void hipCaddf_kernel_v7(hipFloatComplex* result, Dummy x, hipFloatComplex y) { + *result = hipCaddf(x, y); + } + __global__ void hipCaddf_kernel_v8(hipFloatComplex* result, hipFloatComplex x, Dummy y) { + *result = hipCaddf(x, y); + } + __global__ void hipCaddf_kernel_v9(float* result, hipFloatComplex x, hipFloatComplex y) { + *result = hipCaddf(x, y); + } + __global__ void hipCaddf_kernel_v10(hipDoubleComplex* result, hipFloatComplex x, + hipFloatComplex y) { + *result = hipCaddf(x, y); + } + __global__ void hipCaddf_kernel_v11(Dummy* result, hipFloatComplex x, hipFloatComplex y) { + *result = hipCaddf(x, y); + } + __global__ void hipCadd_kernel_v1(hipDoubleComplex* result, hipDoubleComplex* x, + hipDoubleComplex y) { + *result = hipCadd(x, y); + } + __global__ void hipCadd_kernel_v2(hipDoubleComplex* result, hipDoubleComplex x, + hipDoubleComplex* y) { + *result = hipCadd(x, y); + } + __global__ void hipCadd_kernel_v3(hipDoubleComplex* result, double x, hipDoubleComplex y) { + *result = hipCadd(x, y); + } + __global__ void hipCadd_kernel_v4(hipDoubleComplex* result, hipDoubleComplex x, double y) { + *result = hipCadd(x, y); + } + __global__ void hipCadd_kernel_v5(hipDoubleComplex* result, hipFloatComplex x, + hipDoubleComplex y) { + *result = hipCadd(x, y); + } + __global__ void hipCadd_kernel_v6(hipDoubleComplex* result, hipDoubleComplex x, + hipFloatComplex y) { + *result = hipCadd(x, y); + } + __global__ void hipCadd_kernel_v7(hipDoubleComplex* result, Dummy x, hipDoubleComplex y) { + *result = hipCadd(x, y); + } + __global__ void hipCadd_kernel_v8(hipDoubleComplex* result, hipDoubleComplex x, Dummy y) { + *result = hipCadd(x, y); + } + __global__ void hipCadd_kernel_v9(double* result, hipDoubleComplex x, hipDoubleComplex y) { + *result = hipCadd(x, y); + } + __global__ void hipCadd_kernel_v10(hipFloatComplex* result, hipDoubleComplex x, + hipDoubleComplex y) { + *result = hipCadd(x, y); + } + __global__ void hipCadd_kernel_v11(Dummy* result, hipDoubleComplex x, hipDoubleComplex y) { + *result = hipCadd(x, y); + } + void hipCaddf_v1(hipFloatComplex* result, hipFloatComplex* x, hipFloatComplex y) { + *result = hipCaddf(x, y); + } + void hipCaddf_v2(hipFloatComplex* result, hipFloatComplex x, hipFloatComplex* y) { + *result = hipCaddf(x, y); + } + void hipCaddf_v3(hipFloatComplex* result, float x, hipFloatComplex y) { + *result = hipCaddf(x, y); + } + void hipCaddf_v4(hipFloatComplex* result, hipFloatComplex x, float y) { + *result = hipCaddf(x, y); + } + void hipCaddf_v5(hipFloatComplex* result, hipDoubleComplex x, hipFloatComplex y) { + *result = hipCaddf(x, y); + } + void hipCaddf_v6(hipFloatComplex* result, hipFloatComplex x, hipDoubleComplex y) { + *result = hipCaddf(x, y); + } + void hipCaddf_v7(hipFloatComplex* result, Dummy x, hipFloatComplex y) { + *result = hipCaddf(x, y); + } + void hipCaddf_v8(hipFloatComplex* result, hipFloatComplex x, Dummy y) { + *result = hipCaddf(x, y); + } + void hipCaddf_v9(float* result, hipFloatComplex x, hipFloatComplex y) { + *result = hipCaddf(x, y); + } + void hipCaddf_v10(hipDoubleComplex* result, hipFloatComplex x, hipFloatComplex y) { + *result = hipCaddf(x, y); + } + void hipCaddf_v11(Dummy* result, hipFloatComplex x, hipFloatComplex y) { + *result = hipCaddf(x, y); + } + void hipCadd_v1(hipDoubleComplex* result, hipDoubleComplex* x, hipDoubleComplex y) { + *result = hipCadd(x, y); + } + void hipCadd_v2(hipDoubleComplex* result, hipDoubleComplex x, hipDoubleComplex* y) { + *result = hipCadd(x, y); + } + void hipCadd_v3(hipDoubleComplex* result, double x, hipDoubleComplex y) { + *result = hipCadd(x, y); + } + void hipCadd_v4(hipDoubleComplex* result, hipDoubleComplex x, double y) { + *result = hipCadd(x, y); + } + void hipCadd_v5(hipDoubleComplex* result, hipFloatComplex x, hipDoubleComplex y) { + *result = hipCadd(x, y); + } + void hipCadd_v6(hipDoubleComplex* result, hipDoubleComplex x, hipFloatComplex y) { + *result = hipCadd(x, y); + } + void hipCadd_v7(hipDoubleComplex* result, Dummy x, hipDoubleComplex y) { + *result = hipCadd(x, y); + } + void hipCadd_v8(hipDoubleComplex* result, hipDoubleComplex x, Dummy y) { + *result = hipCadd(x, y); + } + void hipCadd_v9(double* result, hipDoubleComplex x, hipDoubleComplex y) { + *result = hipCadd(x, y); + } + void hipCadd_v10(hipFloatComplex* result, hipDoubleComplex x, hipDoubleComplex y) { + *result = hipCadd(x, y); + } + void hipCadd_v11(Dummy* result, hipDoubleComplex x, hipDoubleComplex y) { + *result = hipCadd(x, y); + } +)"}; + +static constexpr auto kComplexSub{R"( + class Dummy { + public: + __device__ Dummy() {} + __device__ ~Dummy() {} + }; + __global__ void hipCsubf_kernel_v1(hipFloatComplex* result, hipFloatComplex* x, + hipFloatComplex y) { + *result = hipCsubf(x, y); + } + __global__ void hipCsubf_kernel_v2(hipFloatComplex* result, hipFloatComplex x, + hipFloatComplex* y) { + *result = hipCsubf(x, y); + } + __global__ void hipCsubf_kernel_v3(hipFloatComplex* result, float x, hipFloatComplex y) { + *result = hipCsubf(x, y); + } + __global__ void hipCsubf_kernel_v4(hipFloatComplex* result, hipFloatComplex x, float y) { + *result = hipCsubf(x, y); + } + __global__ void hipCsubf_kernel_v5(hipFloatComplex* result, hipDoubleComplex x, + hipFloatComplex y) { + *result = hipCsubf(x, y); + } + __global__ void hipCsubf_kernel_v6(hipFloatComplex* result, hipFloatComplex x, + hipDoubleComplex y) { + *result = hipCsubf(x, y); + } + __global__ void hipCsubf_kernel_v7(hipFloatComplex* result, Dummy x, hipFloatComplex y) { + *result = hipCsubf(x, y); + } + __global__ void hipCsubf_kernel_v8(hipFloatComplex* result, hipFloatComplex x, Dummy y) { + *result = hipCsubf(x, y); + } + __global__ void hipCsubf_kernel_v9(float* result, hipFloatComplex x, hipFloatComplex y) { + *result = hipCsubf(x, y); + } + __global__ void hipCsubf_kernel_v10(hipDoubleComplex* result, hipFloatComplex x, + hipFloatComplex y) { + *result = hipCsubf(x, y); + } + __global__ void hipCsubf_kernel_v11(Dummy* result, hipFloatComplex x, hipFloatComplex y) { + *result = hipCsubf(x, y); + } + __global__ void hipCsub_kernel_v1(hipDoubleComplex* result, hipDoubleComplex* x, + hipDoubleComplex y) { + *result = hipCsub(x, y); + } + __global__ void hipCsub_kernel_v2(hipDoubleComplex* result, hipDoubleComplex x, + hipDoubleComplex* y) { + *result = hipCsub(x, y); + } + __global__ void hipCsub_kernel_v3(hipDoubleComplex* result, double x, hipDoubleComplex y) { + *result = hipCsub(x, y); + } + __global__ void hipCsub_kernel_v4(hipDoubleComplex* result, hipDoubleComplex x, double y) { + *result = hipCsub(x, y); + } + __global__ void hipCsub_kernel_v5(hipDoubleComplex* result, hipFloatComplex x, + hipDoubleComplex y) { + *result = hipCsub(x, y); + } + __global__ void hipCsub_kernel_v6(hipDoubleComplex* result, hipDoubleComplex x, + hipFloatComplex y) { + *result = hipCsub(x, y); + } + __global__ void hipCsub_kernel_v7(hipDoubleComplex* result, Dummy x, hipDoubleComplex y) { + *result = hipCsub(x, y); + } + __global__ void hipCsub_kernel_v8(hipDoubleComplex* result, hipDoubleComplex x, Dummy y) { + *result = hipCsub(x, y); + } + __global__ void hipCsub_kernel_v9(double* result, hipDoubleComplex x, hipDoubleComplex y) { + *result = hipCsub(x, y); + } + __global__ void hipCsub_kernel_v10(hipFloatComplex* result, hipDoubleComplex x, + hipDoubleComplex y) { + *result = hipCsub(x, y); + } + __global__ void hipCsub_kernel_v11(Dummy* result, hipDoubleComplex x, hipDoubleComplex y) { + *result = hipCsub(x, y); + } + void hipCsubf_v1(hipFloatComplex* result, hipFloatComplex* x, hipFloatComplex y) { + *result = hipCsubf(x, y); + } + void hipCsubf_v2(hipFloatComplex* result, hipFloatComplex x, hipFloatComplex* y) { + *result = hipCsubf(x, y); + } + void hipCsubf_v3(hipFloatComplex* result, float x, hipFloatComplex y) { + *result = hipCsubf(x, y); + } + void hipCsubf_v4(hipFloatComplex* result, hipFloatComplex x, float y) { + *result = hipCsubf(x, y); + } + void hipCsubf_v5(hipFloatComplex* result, hipDoubleComplex x, hipFloatComplex y) { + *result = hipCsubf(x, y); + } + void hipCsubf_v6(hipFloatComplex* result, hipFloatComplex x, hipDoubleComplex y) { + *result = hipCsubf(x, y); + } + void hipCsubf_v7(hipFloatComplex* result, Dummy x, hipFloatComplex y) { + *result = hipCsubf(x, y); + } + void hipCsubf_v8(hipFloatComplex* result, hipFloatComplex x, Dummy y) { + *result = hipCsubf(x, y); + } + void hipCaddf_v9(float* result, hipFloatComplex x, hipFloatComplex y) { + *result = hipCaddf(x, y); + } + void hipCsubf_v10(hipDoubleComplex* result, hipFloatComplex x, hipFloatComplex y) { + *result = hipCsubf(x, y); + } + void hipCsubf_v11(Dummy* result, hipFloatComplex x, hipFloatComplex y) { + *result = hipCsubf(x, y); + } + void hipCsub_v1(hipDoubleComplex* result, hipDoubleComplex* x, hipDoubleComplex y) { + *result = hipCsub(x, y); + } + void hipCsub_v2(hipDoubleComplex* result, hipDoubleComplex x, hipDoubleComplex* y) { + *result = hipCsub(x, y); + } + void hipCsub_v3(hipDoubleComplex* result, double x, hipDoubleComplex y) { + *result = hipCsub(x, y); + } + void hipCsub_v4(hipDoubleComplex* result, hipDoubleComplex x, double y) { + *result = hipCsub(x, y); + } + void hipCsub_v5(hipDoubleComplex* result, hipFloatComplex x, hipDoubleComplex y) { + *result = hipCsub(x, y); + } + void hipCsub_v6(hipDoubleComplex* result, hipDoubleComplex x, hipFloatComplex y) { + *result = hipCsub(x, y); + } + void hipCsub_v7(hipDoubleComplex* result, Dummy x, hipDoubleComplex y) { + *result = hipCsub(x, y); + } + void hipCsub_v8(hipDoubleComplex* result, hipDoubleComplex x, Dummy y) { + *result = hipCsub(x, y); + } + void hipCsub_v9(double* result, hipDoubleComplex x, hipDoubleComplex y) { + *result = hipCsub(x, y); + } + void hipCsub_v10(hipFloatComplex* result, hipDoubleComplex x, hipDoubleComplex y) { + *result = hipCsub(x, y); + } + void hipCsub_v11(Dummy* result, hipDoubleComplex x, hipDoubleComplex y) { + *result = hipCsub(x, y); + } +)"}; + +static constexpr auto kComplexMul{R"( + class Dummy { + public: + __device__ Dummy() {} + __device__ ~Dummy() {} + }; + __global__ void hipCmulf_kernel_v1(hipFloatComplex* result, hipFloatComplex* x, + hipFloatComplex y) { + *result = hipCmulf(x, y); + } + __global__ void hipCmulf_kernel_v2(hipFloatComplex* result, hipFloatComplex x, + hipFloatComplex* y) { + *result = hipCmulf(x, y); + } + __global__ void hipCmulf_kernel_v3(hipFloatComplex* result, float x, hipFloatComplex y) { + *result = hipCmulf(x, y); + } + __global__ void hipCmulf_kernel_v4(hipFloatComplex* result, hipFloatComplex x, float y) { + *result = hipCmulf(x, y); + } + __global__ void hipCmulf_kernel_v5(hipFloatComplex* result, hipDoubleComplex x, + hipFloatComplex y) { + *result = hipCmulf(x, y); + } + __global__ void hipCmulf_kernel_v6(hipFloatComplex* result, hipFloatComplex x, + hipDoubleComplex y) { + *result = hipCmulf(x, y); + } + __global__ void hipCmulf_kernel_v7(hipFloatComplex* result, Dummy x, hipFloatComplex y) { + *result = hipCmulf(x, y); + } + __global__ void hipCmulf_kernel_v8(hipFloatComplex* result, hipFloatComplex x, Dummy y) { + *result = hipCmulf(x, y); + } + __global__ void hipCmulf_kernel_v9(float* result, hipFloatComplex x, hipFloatComplex y) { + *result = hipCmulf(x, y); + } + __global__ void hipCmulf_kernel_v10(hipDoubleComplex* result, hipFloatComplex x, + hipFloatComplex y) { + *result = hipCmulf(x, y); + } + __global__ void hipCmulf_kernel_v11(Dummy* result, hipFloatComplex x, hipFloatComplex y) { + *result = hipCmulf(x, y); + } + __global__ void hipCmul_kernel_v1(hipDoubleComplex* result, hipDoubleComplex* x, + hipDoubleComplex y) { + *result = hipCmul(x, y); + } + __global__ void hipCmul_kernel_v2(hipDoubleComplex* result, hipDoubleComplex x, + hipDoubleComplex* y) { + *result = hipCmul(x, y); + } + __global__ void hipCmul_kernel_v3(hipDoubleComplex* result, double x, hipDoubleComplex y) { + *result = hipCmul(x, y); + } + __global__ void hipCmul_kernel_v4(hipDoubleComplex* result, hipDoubleComplex x, double y) { + *result = hipCmul(x, y); + } + __global__ void hipCmul_kernel_v5(hipDoubleComplex* result, hipFloatComplex x, + hipDoubleComplex y) { + *result = hipCmul(x, y); + } + __global__ void hipCmul_kernel_v6(hipDoubleComplex* result, hipDoubleComplex x, + hipFloatComplex y) { + *result = hipCmul(x, y); + } + __global__ void hipCmul_kernel_v7(hipDoubleComplex* result, Dummy x, hipDoubleComplex y) { + *result = hipCmul(x, y); + } + __global__ void hipCmul_kernel_v8(hipDoubleComplex* result, hipDoubleComplex x, Dummy y) { + *result = hipCmul(x, y); + } + __global__ void hipCmul_kernel_v9(double* result, hipDoubleComplex x, hipDoubleComplex y) { + *result = hipCmul(x, y); + } + __global__ void hipCmul_kernel_v10(hipFloatComplex* result, hipDoubleComplex x, + hipDoubleComplex y) { + *result = hipCmul(x, y); + } + __global__ void hipCmul_kernel_v11(Dummy* result, hipDoubleComplex x, hipDoubleComplex y) { + *result = hipCmul(x, y); + } + void hipCmulf_v1(hipFloatComplex* result, hipFloatComplex* x, hipFloatComplex y) { + *result = hipCmulf(x, y); + } + void hipCmulf_v2(hipFloatComplex* result, hipFloatComplex x, hipFloatComplex* y) { + *result = hipCmulf(x, y); + } + void hipCmulf_v3(hipFloatComplex* result, float x, hipFloatComplex y) { + *result = hipCmulf(x, y); + } + void hipCmulf_v4(hipFloatComplex* result, hipFloatComplex x, float y) { + *result = hipCmulf(x, y); + } + void hipCmulf_v5(hipFloatComplex* result, hipDoubleComplex x, hipFloatComplex y) { + *result = hipCmulf(x, y); + } + void hipCmulf_v6(hipFloatComplex* result, hipFloatComplex x, hipDoubleComplex y) { + *result = hipCmulf(x, y); + } + void hipCmulf_v7(hipFloatComplex* result, Dummy x, hipFloatComplex y) { + *result = hipCmulf(x, y); + } + void hipCmulf_v8(hipFloatComplex* result, hipFloatComplex x, Dummy y) { + *result = hipCmulf(x, y); + } + void hipCmulf_v9(float* result, hipFloatComplex x, hipFloatComplex y) { + *result = hipCmulf(x, y); + } + void hipCmulf_v10(hipDoubleComplex* result, hipFloatComplex x, hipFloatComplex y) { + *result = hipCmulf(x, y); + } + void hipCmulf_v11(Dummy* result, hipFloatComplex x, hipFloatComplex y) { + *result = hipCmulf(x, y); + } + void hipCmul_v1(hipDoubleComplex* result, hipDoubleComplex* x, hipDoubleComplex y) { + *result = hipCmul(x, y); + } + void hipCmul_v2(hipDoubleComplex* result, hipDoubleComplex x, hipDoubleComplex* y) { + *result = hipCmul(x, y); + } + void hipCmul_v3(hipDoubleComplex* result, double x, hipDoubleComplex y) { + *result = hipCmul(x, y); + } + void hipCmul_v4(hipDoubleComplex* result, hipDoubleComplex x, double y) { + *result = hipCmul(x, y); + } + void hipCmul_v5(hipDoubleComplex* result, hipFloatComplex x, hipDoubleComplex y) { + *result = hipCmul(x, y); + } + void hipCmul_v6(hipDoubleComplex* result, hipDoubleComplex x, hipFloatComplex y) { + *result = hipCmul(x, y); + } + void hipCmul_v7(hipDoubleComplex* result, Dummy x, hipDoubleComplex y) { + *result = hipCmul(x, y); + } + void hipCmul_v8(hipDoubleComplex* result, hipDoubleComplex x, Dummy y) { + *result = hipCmul(x, y); + } + void hipCmul_v9(double* result, hipDoubleComplex x, hipDoubleComplex y) { + *result = hipCmul(x, y); + } + void hipCmul_v10(hipFloatComplex* result, hipDoubleComplex x, hipDoubleComplex y) { + *result = hipCmul(x, y); + } + void hipCmul_v11(Dummy* result, hipDoubleComplex x, hipDoubleComplex y) { + *result = hipCmul(x, y); + } +)"}; + +static constexpr auto kComplexDiv{R"( + class Dummy { + public: + __device__ Dummy() {} + __device__ ~Dummy() {} + }; + __global__ void hipCdivf_kernel_v1(hipFloatComplex* result, hipFloatComplex* x, + hipFloatComplex y) { + *result = hipCdivf(x, y); + } + __global__ void hipCdivf_kernel_v2(hipFloatComplex* result, hipFloatComplex x, + hipFloatComplex* y) { + *result = hipCdivf(x, y); + } + __global__ void hipCdivf_kernel_v3(hipFloatComplex* result, float x, hipFloatComplex y) { + *result = hipCdivf(x, y); + } + __global__ void hipCdivf_kernel_v4(hipFloatComplex* result, hipFloatComplex x, float y) { + *result = hipCdivf(x, y); + } + __global__ void hipCdivf_kernel_v5(hipFloatComplex* result, hipDoubleComplex x, + hipFloatComplex y) { + *result = hipCdivf(x, y); + } + __global__ void hipCdivf_kernel_v6(hipFloatComplex* result, hipFloatComplex x, + hipDoubleComplex y) { + *result = hipCdivf(x, y); + } + __global__ void hipCdivf_kernel_v7(hipFloatComplex* result, Dummy x, hipFloatComplex y) { + *result = hipCdivf(x, y); + } + __global__ void hipCdivf_kernel_v8(hipFloatComplex* result, hipFloatComplex x, Dummy y) { + *result = hipCdivf(x, y); + } + __global__ void hipCdivf_kernel_v9(float* result, hipFloatComplex x, hipFloatComplex y) { + *result = hipCdivf(x, y); + } + __global__ void hipCdivf_kernel_v10(hipDoubleComplex* result, hipFloatComplex x, + hipFloatComplex y) { + *result = hipCdivf(x, y); + } + __global__ void hipCdivf_kernel_v11(Dummy* result, hipFloatComplex x, hipFloatComplex y) { + *result = hipCdivf(x, y); + } + __global__ void hipCdiv_kernel_v1(hipDoubleComplex* result, hipDoubleComplex* x, + hipDoubleComplex y) { + *result = hipCdiv(x, y); + } + __global__ void hipCdiv_kernel_v2(hipDoubleComplex* result, hipDoubleComplex x, + hipDoubleComplex* y) { + *result = hipCdiv(x, y); + } + __global__ void hipCdiv_kernel_v3(hipDoubleComplex* result, double x, hipDoubleComplex y) { + *result = hipCdiv(x, y); + } + __global__ void hipCdiv_kernel_v4(hipDoubleComplex* result, hipDoubleComplex x, double y) { + *result = hipCdiv(x, y); + } + __global__ void hipCdiv_kernel_v5(hipDoubleComplex* result, hipFloatComplex x, + hipDoubleComplex y) { + *result = hipCdiv(x, y); + } + __global__ void hipCdiv_kernel_v6(hipDoubleComplex* result, hipDoubleComplex x, + hipFloatComplex y) { + *result = hipCdiv(x, y); + } + __global__ void hipCdiv_kernel_v7(hipDoubleComplex* result, Dummy x, hipDoubleComplex y) { + *result = hipCdiv(x, y); + } + __global__ void hipCdiv_kernel_v8(hipDoubleComplex* result, hipDoubleComplex x, Dummy y) { + *result = hipCdiv(x, y); + } + __global__ void hipCdiv_kernel_v9(double* result, hipDoubleComplex x, hipDoubleComplex y) { + *result = hipCdiv(x, y); + } + __global__ void hipCdiv_kernel_v10(hipFloatComplex* result, hipDoubleComplex x, + hipDoubleComplex y) { + *result = hipCdiv(x, y); + } + __global__ void hipCdiv_kernel_v11(Dummy* result, hipDoubleComplex x, hipDoubleComplex y) { + *result = hipCdiv(x, y); + } + void hipCdivf_v1(hipFloatComplex* result, hipFloatComplex* x, hipFloatComplex y) { + *result = hipCdivf(x, y); + } + void hipCdivf_v2(hipFloatComplex* result, hipFloatComplex x, hipFloatComplex* y) { + *result = hipCdivf(x, y); + } + void hipCdivf_v3(hipFloatComplex* result, float x, hipFloatComplex y) { + *result = hipCdivf(x, y); + } + void hipCdivf_v4(hipFloatComplex* result, hipFloatComplex x, float y) { + *result = hipCdivf(x, y); + } + void hipCdivf_v5(hipFloatComplex* result, hipDoubleComplex x, hipFloatComplex y) { + *result = hipCdivf(x, y); + } + void hipCdivf_v6(hipFloatComplex* result, hipFloatComplex x, hipDoubleComplex y) { + *result = hipCdivf(x, y); + } + void hipCdivf_v7(hipFloatComplex* result, Dummy x, hipFloatComplex y) { + *result = hipCdivf(x, y); + } + void hipCdivf_v8(hipFloatComplex* result, hipFloatComplex x, Dummy y) { + *result = hipCdivf(x, y); + } + void hipCdivf_v9(float* result, hipFloatComplex x, hipFloatComplex y) { + *result = hipCdivf(x, y); + } + void hipCdivf_v10(hipDoubleComplex* result, hipFloatComplex x, hipFloatComplex y) { + *result = hipCdivf(x, y); + } + void hipCdivf_v11(Dummy* result, hipFloatComplex x, hipFloatComplex y) { + *result = hipCdivf(x, y); + } + void hipCdiv_v1(hipDoubleComplex* result, hipDoubleComplex* x, hipDoubleComplex y) { + *result = hipCdiv(x, y); + } + void hipCdiv_v2(hipDoubleComplex* result, hipDoubleComplex x, hipDoubleComplex* y) { + *result = hipCdiv(x, y); + } + void hipCdiv_v3(hipDoubleComplex* result, double x, hipDoubleComplex y) { + *result = hipCdiv(x, y); + } + void hipCdiv_v4(hipDoubleComplex* result, hipDoubleComplex x, double y) { + *result = hipCdiv(x, y); + } + void hipCdiv_v5(hipDoubleComplex* result, hipFloatComplex x, hipDoubleComplex y) { + *result = hipCdiv(x, y); + } + void hipCdiv_v6(hipDoubleComplex* result, hipDoubleComplex x, hipFloatComplex y) { + *result = hipCdiv(x, y); + } + void hipCdiv_v7(hipDoubleComplex* result, Dummy x, hipDoubleComplex y) { + *result = hipCdiv(x, y); + } + void hipCdiv_v8(hipDoubleComplex* result, hipDoubleComplex x, Dummy y) { + *result = hipCdiv(x, y); + } + void hipCdiv_v9(double* result, hipDoubleComplex x, hipDoubleComplex y) { + *result = hipCdiv(x, y); + } + void hipCdiv_v10(hipFloatComplex* result, hipDoubleComplex x, hipDoubleComplex y) { + *result = hipCdiv(x, y); + } + void hipCdiv_v11(Dummy* result, hipDoubleComplex x, hipDoubleComplex y) { + *result = hipCdiv(x, y); + } +)"}; diff --git a/catch/unit/complex/complex_negative_kernels_3Arg.cc b/catch/unit/complex/complex_negative_kernels_3Arg.cc new file mode 100644 index 0000000000..ce26e50af0 --- /dev/null +++ b/catch/unit/complex/complex_negative_kernels_3Arg.cc @@ -0,0 +1,247 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. +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 WARRANNTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNNESS 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 INN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR INN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include +#include + +class Dummy { + public: + __device__ Dummy() {} + __device__ ~Dummy() {} +}; + +__global__ void hipCfmaf_kernel_v1(hipComplex* result, hipFloatComplex* x, hipFloatComplex y, + hipFloatComplex z) { + *result = hipCfmaf(x, y, z); +} +__global__ void hipCfmaf_kernel_v2(hipComplex* result, hipFloatComplex x, hipFloatComplex* y, + hipFloatComplex z) { + *result = hipCfmaf(x, y, z); +} +__global__ void hipCfmaf_kernel_v3(hipComplex* result, hipFloatComplex x, hipFloatComplex y, + hipFloatComplex* z) { + *result = hipCfmaf(x, y, z); +} +__global__ void hipCfmaf_kernel_v4(hipComplex* result, float x, hipFloatComplex y, + hipFloatComplex z) { + *result = hipCfmaf(x, y, z); +} +__global__ void hipCfmaf_kernel_v5(hipComplex* result, hipFloatComplex x, float y, + hipFloatComplex z) { + *result = hipCfmaf(x, y, z); +} +__global__ void hipCfmaf_kernel_v6(hipComplex* result, hipFloatComplex x, hipFloatComplex y, + float z) { + *result = hipCfmaf(x, y, z); +} +__global__ void hipCfmaf_kernel_v7(hipComplex* result, hipDoubleComplex x, hipFloatComplex y, + hipFloatComplex z) { + *result = hipCfmaf(x, y, z); +} +__global__ void hipCfmaf_kernel_v8(hipComplex* result, hipFloatComplex x, hipDoubleComplex y, + hipFloatComplex z) { + *result = hipCfmaf(x, y, z); +} +__global__ void hipCfmaf_kernel_v9(hipComplex* result, hipFloatComplex x, hipFloatComplex y, + hipDoubleComplex z) { + *result = hipCfmaf(x, y, z); +} +__global__ void hipCfmaf_kernel_v10(hipComplex* result, Dummy x, hipFloatComplex y, + hipFloatComplex z) { + *result = hipCfmaf(x, y, z); +} +__global__ void hipCfmaf_kernel_v11(hipComplex* result, hipFloatComplex x, Dummy y, + hipFloatComplex z) { + *result = hipCfmaf(x, y, z); +} +__global__ void hipCfmaf_kernel_v12(hipComplex* result, hipFloatComplex x, hipFloatComplex y, + Dummy z) { + *result = hipCfmaf(x, y, z); +} +__global__ void hipCfmaf_kernel_v13(float* result, hipFloatComplex x, hipFloatComplex y, + hipFloatComplex z) { + *result = hipCfmaf(x, y, z); +} +__global__ void hipCfmaf_kernel_v14(hipDoubleComplex* result, hipFloatComplex x, hipFloatComplex y, + hipFloatComplex z) { + *result = hipCfmaf(x, y, z); +} +__global__ void hipCfmaf_kernel_v15(Dummy* result, hipFloatComplex x, hipFloatComplex y, + hipFloatComplex z) { + *result = hipCfmaf(x, y, z); +} +void hipCfmaf_v1(hipComplex* result, hipFloatComplex* x, hipFloatComplex y, hipFloatComplex z) { + *result = hipCfmaf(x, y, z); +} +void hipCfmaf_v2(hipComplex* result, hipFloatComplex x, hipFloatComplex* y, hipFloatComplex z) { + *result = hipCfmaf(x, y, z); +} +void hipCfmaf_v3(hipComplex* result, hipFloatComplex x, hipFloatComplex y, hipFloatComplex* z) { + *result = hipCfmaf(x, y, z); +} +void hipCfmaf_v4(hipComplex* result, float x, hipFloatComplex y, hipFloatComplex z) { + *result = hipCfmaf(x, y, z); +} +void hipCfmaf_v5(hipComplex* result, hipFloatComplex x, float y, hipFloatComplex z) { + *result = hipCfmaf(x, y, z); +} +void hipCfmaf_v6(hipComplex* result, hipFloatComplex x, hipFloatComplex y, float z) { + *result = hipCfmaf(x, y, z); +} +void hipCfmaf_v7(hipComplex* result, hipDoubleComplex x, hipFloatComplex y, hipFloatComplex z) { + *result = hipCfmaf(x, y, z); +} +void hipCfmaf_v8(hipComplex* result, hipFloatComplex x, hipDoubleComplex y, hipFloatComplex z) { + *result = hipCfmaf(x, y, z); +} +void hipCfmaf_v9(hipComplex* result, hipFloatComplex x, hipFloatComplex y, hipDoubleComplex z) { + *result = hipCfmaf(x, y, z); +} +void hipCfmaf_v10(hipComplex* result, Dummy x, hipFloatComplex y, hipFloatComplex z) { + *result = hipCfmaf(x, y, z); +} +void hipCfmaf_v11(hipComplex* result, hipFloatComplex x, Dummy y, hipFloatComplex z) { + *result = hipCfmaf(x, y, z); +} +void hipCfmaf_v12(hipComplex* result, hipFloatComplex x, hipFloatComplex y, Dummy z) { + *result = hipCfmaf(x, y, z); +} +void hipCfmaf_v13(float* result, hipFloatComplex x, hipFloatComplex y, hipFloatComplex z) { + *result = hipCfmaf(x, y, z); +} +void hipCfmaf_v14(hipDoubleComplex* result, hipFloatComplex x, hipFloatComplex y, + hipFloatComplex z) { + *result = hipCfmaf(x, y, z); +} +void hipCfmaf_v15(Dummy* result, hipFloatComplex x, hipFloatComplex y, hipFloatComplex z) { + *result = hipCfmaf(x, y, z); +} + +__global__ void hipCfma_kernel_v1(hipDoubleComplex* result, hipDoubleComplex* x, hipDoubleComplex y, + hipDoubleComplex z) { + *result = hipCfma(x, y, z); +} +__global__ void hipCfma_kernel_v2(hipDoubleComplex* result, hipDoubleComplex x, hipDoubleComplex* y, + hipDoubleComplex z) { + *result = hipCfma(x, y, z); +} +__global__ void hipCfma_kernel_v3(hipDoubleComplex* result, hipDoubleComplex x, hipDoubleComplex y, + hipDoubleComplex* z) { + *result = hipCfma(x, y, z); +} +__global__ void hipCfma_kernel_v4(hipDoubleComplex* result, double x, hipDoubleComplex y, + hipDoubleComplex z) { + *result = hipCfma(x, y, z); +} +__global__ void hipCfma_kernel_v5(hipDoubleComplex* result, hipDoubleComplex x, double y, + hipDoubleComplex z) { + *result = hipCfma(x, y, z); +} +__global__ void hipCfma_kernel_v6(hipDoubleComplex* result, hipDoubleComplex x, hipDoubleComplex y, + double z) { + *result = hipCfma(x, y, z); +} +__global__ void hipCfma_kernel_v7(hipDoubleComplex* result, hipFloatComplex x, hipDoubleComplex y, + hipDoubleComplex z) { + *result = hipCfma(x, y, z); +} +__global__ void hipCfma_kernel_v8(hipDoubleComplex* result, hipDoubleComplex x, hipFloatComplex y, + hipDoubleComplex z) { + *result = hipCfma(x, y, z); +} +__global__ void hipCfma_kernel_v9(hipDoubleComplex* result, hipDoubleComplex x, hipDoubleComplex y, + hipFloatComplex z) { + *result = hipCfma(x, y, z); +} +__global__ void hipCfma_kernel_v10(hipDoubleComplex* result, Dummy x, hipDoubleComplex y, + hipDoubleComplex z) { + *result = hipCfma(x, y, z); +} +__global__ void hipCfma_kernel_v11(hipDoubleComplex* result, hipDoubleComplex x, Dummy y, + hipDoubleComplex z) { + *result = hipCfma(x, y, z); +} +__global__ void hipCfma_kernel_v12(hipDoubleComplex* result, hipDoubleComplex x, hipDoubleComplex y, + Dummy z) { + *result = hipCfma(x, y, z); +} +__global__ void hipCfma_kernel_v13(double* result, hipDoubleComplex x, hipDoubleComplex y, + hipDoubleComplex z) { + *result = hipCfma(x, y, z); +} +__global__ void hipCfma_kernel_v14(hipFloatComplex* result, hipDoubleComplex x, hipDoubleComplex y, + hipDoubleComplex z) { + *result = hipCfma(x, y, z); +} +__global__ void hipCfma_kernel_v15(Dummy* result, hipDoubleComplex x, hipDoubleComplex y, + hipDoubleComplex z) { + *result = hipCfma(x, y, z); +} +void hipCfma_v1(hipDoubleComplex* result, hipDoubleComplex* x, hipDoubleComplex y, + hipDoubleComplex z) { + *result = hipCfma(x, y, z); +} +void hipCfma_v2(hipDoubleComplex* result, hipDoubleComplex x, hipDoubleComplex* y, + hipDoubleComplex z) { + *result = hipCfma(x, y, z); +} +void hipCfma_v3(hipDoubleComplex* result, hipDoubleComplex x, hipDoubleComplex y, + hipDoubleComplex* z) { + *result = hipCfma(x, y, z); +} +void hipCfma_v4(hipDoubleComplex* result, double x, hipDoubleComplex y, hipDoubleComplex z) { + *result = hipCfma(x, y, z); +} +void hipCfma_v5(hipDoubleComplex* result, hipDoubleComplex x, double y, hipDoubleComplex z) { + *result = hipCfma(x, y, z); +} +void hipCfma_v6(hipDoubleComplex* result, hipDoubleComplex x, hipDoubleComplex y, double z) { + *result = hipCfma(x, y, z); +} +void hipCfma_v7(hipDoubleComplex* result, hipFloatComplex x, hipDoubleComplex y, + hipDoubleComplex z) { + *result = hipCfma(x, y, z); +} +void hipCfma_v8(hipDoubleComplex* result, hipDoubleComplex x, hipFloatComplex y, + hipDoubleComplex z) { + *result = hipCfma(x, y, z); +} +void hipCfma_v9(hipDoubleComplex* result, hipDoubleComplex x, hipDoubleComplex y, + hipFloatComplex z) { + *result = hipCfma(x, y, z); +} +void hipCfma_v10(hipDoubleComplex* result, Dummy x, hipDoubleComplex y, hipDoubleComplex z) { + *result = hipCfma(x, y, z); +} +void hipCfma_v11(hipDoubleComplex* result, hipDoubleComplex x, Dummy y, hipDoubleComplex z) { + *result = hipCfma(x, y, z); +} +void hipCfma_v12(hipDoubleComplex* result, hipDoubleComplex x, hipDoubleComplex y, Dummy z) { + *result = hipCfma(x, y, z); +} +void hipCfma_v13(double* result, hipDoubleComplex x, hipDoubleComplex y, hipDoubleComplex z) { + *result = hipCfma(x, y, z); +} +void hipCfma_v14(hipFloatComplex* result, hipDoubleComplex x, hipDoubleComplex y, + hipDoubleComplex z) { + *result = hipCfma(x, y, z); +} +void hipCfma_v15(Dummy* result, hipDoubleComplex x, hipDoubleComplex y, hipDoubleComplex z) { + *result = hipCfma(x, y, z); +} \ No newline at end of file diff --git a/catch/unit/complex/complex_negative_kernels_3Arg_rtc.hh b/catch/unit/complex/complex_negative_kernels_3Arg_rtc.hh new file mode 100644 index 0000000000..caf12a31c8 --- /dev/null +++ b/catch/unit/complex/complex_negative_kernels_3Arg_rtc.hh @@ -0,0 +1,246 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. +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 WARRANNTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNNESS 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 INN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR INN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#pragma once + +static constexpr auto kComplexFma{R"( + class Dummy { + public: + __device__ Dummy() {} + __device__ ~Dummy() {} + }; + __global__ void hipCfmaf_kernel_v1(hipComplex* result, hipFloatComplex* x, hipFloatComplex y, + hipFloatComplex z) { + *result = hipCfmaf(x, y, z); + } + __global__ void hipCfmaf_kernel_v2(hipComplex* result, hipFloatComplex x, hipFloatComplex* y, + hipFloatComplex z) { + *result = hipCfmaf(x, y, z); + } + __global__ void hipCfmaf_kernel_v3(hipComplex* result, hipFloatComplex x, hipFloatComplex y, + hipFloatComplex* z) { + *result = hipCfmaf(x, y, z); + } + __global__ void hipCfmaf_kernel_v4(hipComplex* result, float x, hipFloatComplex y, + hipFloatComplex z) { + *result = hipCfmaf(x, y, z); + } + __global__ void hipCfmaf_kernel_v5(hipComplex* result, hipFloatComplex x, float y, + hipFloatComplex z) { + *result = hipCfmaf(x, y, z); + } + __global__ void hipCfmaf_kernel_v6(hipComplex* result, hipFloatComplex x, hipFloatComplex y, + float z) { + *result = hipCfmaf(x, y, z); + } + __global__ void hipCfmaf_kernel_v7(hipComplex* result, hipDoubleComplex x, hipFloatComplex y, + hipFloatComplex z) { + *result = hipCfmaf(x, y, z); + } + __global__ void hipCfmaf_kernel_v8(hipComplex* result, hipFloatComplex x, hipDoubleComplex y, + hipFloatComplex z) { + *result = hipCfmaf(x, y, z); + } + __global__ void hipCfmaf_kernel_v9(hipComplex* result, hipFloatComplex x, hipFloatComplex y, + hipDoubleComplex z) { + *result = hipCfmaf(x, y, z); + } + __global__ void hipCfmaf_kernel_v10(hipComplex* result, Dummy x, hipFloatComplex y, + hipFloatComplex z) { + *result = hipCfmaf(x, y, z); + } + __global__ void hipCfmaf_kernel_v11(hipComplex* result, hipFloatComplex x, Dummy y, + hipFloatComplex z) { + *result = hipCfmaf(x, y, z); + } + __global__ void hipCfmaf_kernel_v12(hipComplex* result, hipFloatComplex x, hipFloatComplex y, + Dummy z) { + *result = hipCfmaf(x, y, z); + } + __global__ void hipCfmaf_kernel_v13(float* result, hipFloatComplex x, hipFloatComplex y, + hipFloatComplex z) { + *result = hipCfmaf(x, y, z); + } + __global__ void hipCfmaf_kernel_v14(hipDoubleComplex* result, hipFloatComplex x, hipFloatComplex y, + hipFloatComplex z) { + *result = hipCfmaf(x, y, z); + } + __global__ void hipCfmaf_kernel_v15(Dummy* result, hipFloatComplex x, hipFloatComplex y, + hipFloatComplex z) { + *result = hipCfmaf(x, y, z); + } + __global__ void hipCfma_kernel_v1(hipDoubleComplex* result, hipDoubleComplex* x, hipDoubleComplex y, + hipDoubleComplex z) { + *result = hipCfma(x, y, z); + } + __global__ void hipCfma_kernel_v2(hipDoubleComplex* result, hipDoubleComplex x, hipDoubleComplex* y, + hipDoubleComplex z) { + *result = hipCfma(x, y, z); + } + __global__ void hipCfma_kernel_v3(hipDoubleComplex* result, hipDoubleComplex x, hipDoubleComplex y, + hipDoubleComplex* z) { + *result = hipCfma(x, y, z); + } + __global__ void hipCfma_kernel_v4(hipDoubleComplex* result, double x, hipDoubleComplex y, + hipDoubleComplex z) { + *result = hipCfma(x, y, z); + } + __global__ void hipCfma_kernel_v5(hipDoubleComplex* result, hipDoubleComplex x, double y, + hipDoubleComplex z) { + *result = hipCfma(x, y, z); + } + __global__ void hipCfma_kernel_v6(hipDoubleComplex* result, hipDoubleComplex x, hipDoubleComplex y, + double z) { + *result = hipCfma(x, y, z); + } + __global__ void hipCfma_kernel_v7(hipDoubleComplex* result, hipFloatComplex x, hipDoubleComplex y, + hipDoubleComplex z) { + *result = hipCfma(x, y, z); + } + __global__ void hipCfma_kernel_v8(hipDoubleComplex* result, hipDoubleComplex x, hipFloatComplex y, + hipDoubleComplex z) { + *result = hipCfma(x, y, z); + } + __global__ void hipCfma_kernel_v9(hipDoubleComplex* result, hipDoubleComplex x, hipDoubleComplex y, + hipFloatComplex z) { + *result = hipCfma(x, y, z); + } + __global__ void hipCfma_kernel_v10(hipDoubleComplex* result, Dummy x, hipDoubleComplex y, + hipDoubleComplex z) { + *result = hipCfma(x, y, z); + } + __global__ void hipCfma_kernel_v11(hipDoubleComplex* result, hipDoubleComplex x, Dummy y, + hipDoubleComplex z) { + *result = hipCfma(x, y, z); + } + __global__ void hipCfma_kernel_v12(hipDoubleComplex* result, hipDoubleComplex x, hipDoubleComplex y, + Dummy z) { + *result = hipCfma(x, y, z); + } + __global__ void hipCfma_kernel_v13(double* result, hipDoubleComplex x, hipDoubleComplex y, + hipDoubleComplex z) { + *result = hipCfma(x, y, z); + } + __global__ void hipCfma_kernel_v14(hipFloatComplex* result, hipDoubleComplex x, hipDoubleComplex y, + hipDoubleComplex z) { + *result = hipCfma(x, y, z); + } + __global__ void hipCfma_kernel_v15(Dummy* result, hipDoubleComplex x, hipDoubleComplex y, + hipDoubleComplex z) { + *result = hipCfma(x, y, z); + } + void hipCfmaf_v1(hipComplex* result, hipFloatComplex* x, hipFloatComplex y, hipFloatComplex z) { + *result = hipCfmaf(x, y, z); + } + void hipCfmaf_v2(hipComplex* result, hipFloatComplex x, hipFloatComplex* y, hipFloatComplex z) { + *result = hipCfmaf(x, y, z); + } + void hipCfmaf_v3(hipComplex* result, hipFloatComplex x, hipFloatComplex y, hipFloatComplex* z) { + *result = hipCfmaf(x, y, z); + } + void hipCfmaf_v4(hipComplex* result, float x, hipFloatComplex y, hipFloatComplex z) { + *result = hipCfmaf(x, y, z); + } + void hipCfmaf_v5(hipComplex* result, hipFloatComplex x, float y, hipFloatComplex z) { + *result = hipCfmaf(x, y, z); + } + void hipCfmaf_v6(hipComplex* result, hipFloatComplex x, hipFloatComplex y, float z) { + *result = hipCfmaf(x, y, z); + } + void hipCfmaf_v7(hipComplex* result, hipDoubleComplex x, hipFloatComplex y, hipFloatComplex z) { + *result = hipCfmaf(x, y, z); + } + void hipCfmaf_v8(hipComplex* result, hipFloatComplex x, hipDoubleComplex y, hipFloatComplex z) { + *result = hipCfmaf(x, y, z); + } + void hipCfmaf_v9(hipComplex* result, hipFloatComplex x, hipFloatComplex y, hipDoubleComplex z) { + *result = hipCfmaf(x, y, z); + } + void hipCfmaf_v10(hipComplex* result, Dummy x, hipFloatComplex y, hipFloatComplex z) { + *result = hipCfmaf(x, y, z); + } + void hipCfmaf_v11(hipComplex* result, hipFloatComplex x, Dummy y, hipFloatComplex z) { + *result = hipCfmaf(x, y, z); + } + void hipCfmaf_v12(hipComplex* result, hipFloatComplex x, hipFloatComplex y, Dummy z) { + *result = hipCfmaf(x, y, z); + } + void hipCfmaf_v13(float* result, hipFloatComplex x, hipFloatComplex y, hipFloatComplex z) { + *result = hipCfmaf(x, y, z); + } + void hipCfmaf_v14(hipDoubleComplex* result, hipFloatComplex x, hipFloatComplex y, + hipFloatComplex z) { + *result = hipCfmaf(x, y, z); + } + void hipCfmaf_v15(Dummy* result, hipFloatComplex x, hipFloatComplex y, hipFloatComplex z) { + *result = hipCfmaf(x, y, z); + } + void hipCfma_v1(hipDoubleComplex* result, hipDoubleComplex* x, hipDoubleComplex y, + hipDoubleComplex z) { + *result = hipCfma(x, y, z); + } + void hipCfma_v2(hipDoubleComplex* result, hipDoubleComplex x, hipDoubleComplex* y, + hipDoubleComplex z) { + *result = hipCfma(x, y, z); + } + void hipCfma_v3(hipDoubleComplex* result, hipDoubleComplex x, hipDoubleComplex y, + hipDoubleComplex* z) { + *result = hipCfma(x, y, z); + } + void hipCfma_v4(hipDoubleComplex* result, double x, hipDoubleComplex y, hipDoubleComplex z) { + *result = hipCfma(x, y, z); + } + void hipCfma_v5(hipDoubleComplex* result, hipDoubleComplex x, double y, hipDoubleComplex z) { + *result = hipCfma(x, y, z); + } + void hipCfma_v6(hipDoubleComplex* result, hipDoubleComplex x, hipDoubleComplex y, double z) { + *result = hipCfma(x, y, z); + } + void hipCfma_v7(hipDoubleComplex* result, hipFloatComplex x, hipDoubleComplex y, + hipDoubleComplex z) { + *result = hipCfma(x, y, z); + } + void hipCfma_v8(hipDoubleComplex* result, hipDoubleComplex x, hipFloatComplex y, + hipDoubleComplex z) { + *result = hipCfma(x, y, z); + } + void hipCfma_v9(hipDoubleComplex* result, hipDoubleComplex x, hipDoubleComplex y, + hipFloatComplex z) { + *result = hipCfma(x, y, z); + } + void hipCfma_v10(hipDoubleComplex* result, Dummy x, hipDoubleComplex y, hipDoubleComplex z) { + *result = hipCfma(x, y, z); + } + void hipCfma_v11(hipDoubleComplex* result, hipDoubleComplex x, Dummy y, hipDoubleComplex z) { + *result = hipCfma(x, y, z); + } + void hipCfma_v12(hipDoubleComplex* result, hipDoubleComplex x, hipDoubleComplex y, Dummy z) { + *result = hipCfma(x, y, z); + } + void hipCfma_v13(double* result, hipDoubleComplex x, hipDoubleComplex y, hipDoubleComplex z) { + *result = hipCfma(x, y, z); + } + void hipCfma_v14(hipFloatComplex* result, hipDoubleComplex x, hipDoubleComplex y, + hipDoubleComplex z) { + *result = hipCfma(x, y, z); + } + void hipCfma_v15(Dummy* result, hipDoubleComplex x, hipDoubleComplex y, hipDoubleComplex z) { + *result = hipCfma(x, y, z); + } +)"}; diff --git a/catch/unit/cooperativeGrps/CMakeLists.txt b/catch/unit/cooperativeGrps/CMakeLists.txt index 1090a68382..8bf1ce439b 100644 --- a/catch/unit/cooperativeGrps/CMakeLists.txt +++ b/catch/unit/cooperativeGrps/CMakeLists.txt @@ -8,14 +8,18 @@ set(TEST_SRC hipCGCoalescedGroups_old.cc hipLaunchCooperativeKernel_old.cc hipLaunchCooperativeKernelMultiDevice_old.cc + multi_grid_group.cc + coalesced_groups_shfl_down_old.cc + coalesced_groups_shfl_up_old.cc + hipCGCoalescedGroups_old.cc + coalesced_group.cc grid_group.cc - coalesced_groups_shfl_down.cc - coalesced_groups_shfl_up.cc coalesced_tiled_groups_metagrp.cc ) if(HIP_PLATFORM STREQUAL "nvidia") set_source_files_properties(hipCGMultiGridGroupType_old.cc PROPERTIES COMPILE_FLAGS "-D_CG_ABI_EXPERIMENTAL -rdc=true -gencode arch=compute_60,code=sm_60 -gencode arch=compute_70,code=sm_70 -gencode arch=compute_80,code=sm_80") set_source_files_properties(hipLaunchCooperativeKernelMultiDevice_old.cc PROPERTIES COMPILE_FLAGS "-D_CG_ABI_EXPERIMENTAL -rdc=true -gencode arch=compute_60,code=sm_60 -gencode arch=compute_70,code=sm_70 -gencode arch=compute_80,code=sm_80") + set_source_files_properties(multi_grid_group.cc PROPERTIES COMPILE_FLAGS "-D_CG_ABI_EXPERIMENTAL -rdc=true -gencode arch=compute_60,code=sm_60 -gencode arch=compute_70,code=sm_70 -gencode arch=compute_80,code=sm_80") hip_add_exe_to_target(NAME coopGrpTest TEST_SRC ${TEST_SRC} TEST_TARGET_NAME build_tests diff --git a/catch/unit/cooperativeGrps/coalesced_group.cc b/catch/unit/cooperativeGrps/coalesced_group.cc new file mode 100644 index 0000000000..3c5f3c9c62 --- /dev/null +++ b/catch/unit/cooperativeGrps/coalesced_group.cc @@ -0,0 +1,760 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. +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 "cooperative_groups_common.hh" + +#include +#include +#include + +/** + * @addtogroup coalesced_group coalesced_group + * @{ + * @ingroup DeviceLanguageTest + * Contains unit tests for all coalesced_group basic APIs + */ + +namespace cg = cooperative_groups; + +template +static __global__ void coalesced_group_size_getter(unsigned int* sizes, uint64_t active_mask) { + const cg::thread_block_tile tile = + cg::tiled_partition(cg::this_thread_block()); + if (active_mask & (static_cast(1) << tile.thread_rank())) { + BaseType active = cg::coalesced_threads(); + sizes[thread_rank_in_grid()] = active.size(); + } +} + +template +static __global__ void coalesced_group_thread_rank_getter(unsigned int* thread_ranks, + uint64_t active_mask) { + const cg::thread_block_tile tile = + cg::tiled_partition(cg::this_thread_block()); + if (active_mask & (static_cast(1) << tile.thread_rank())) { + BaseType active = cg::coalesced_threads(); + thread_ranks[thread_rank_in_grid()] = active.thread_rank(); + } +} + +template +static __global__ void coalesced_group_non_member_size_getter(unsigned int* sizes, + uint64_t active_mask) { + const cg::thread_block_tile tile = + cg::tiled_partition(cg::this_thread_block()); + if (active_mask & (static_cast(1) << tile.thread_rank())) { + cg::coalesced_group active = cg::coalesced_threads(); + sizes[thread_rank_in_grid()] = cg::group_size(active); + } +} + +template +static __global__ void coalesced_group_non_member_thread_rank_getter(unsigned int* thread_ranks, + uint64_t active_mask) { + const cg::thread_block_tile tile = + cg::tiled_partition(cg::this_thread_block()); + if (active_mask & (static_cast(1) << tile.thread_rank())) { + cg::coalesced_group active = cg::coalesced_threads(); + thread_ranks[thread_rank_in_grid()] = cg::thread_rank(active); + } +} + +static unsigned int get_active_thread_count(uint64_t active_mask, unsigned int partition_size) { + unsigned int active_thread_count = 0; + for (int i = 0; i < partition_size; i++) { + if (active_mask & (static_cast(1) << i)) active_thread_count++; + } + return active_thread_count; +} + +static uint64_t get_active_mask(unsigned int test_case) { + uint64_t active_mask = 0; + switch (test_case) { + case 0: // 1st thread + active_mask = 1; + break; + case 1: // last thread + active_mask = static_cast(1) << (kWarpSize - 1); + break; + case 2: // all threads + active_mask = 0xFFFFFFFFFFFFFFFF; + break; + case 3: // every second thread + active_mask = 0xAAAAAAAAAAAAAAAA; + break; + default: // random + static std::mt19937_64 mt(test_case); + std::uniform_int_distribution dist(0, std::numeric_limits::max()); + active_mask = dist(mt); + } + return active_mask; +} + +/** + * Test Description + * ------------------------ + * - Launches kernels that write the return values of size and thread_rank member + * functions of coalesced groups, created according to the generated mask, to an output array that + * is validated on the host side. The kernels are run sequentially, reusing the output array, to + * avoid running out of device memory for large kernel launches + * Test source + * ------------------------ + * - unit/cooperativeGrps/coalesced_group.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_Coalesced_Group_Getters_Positive_Basic") { + int device; + hipDeviceProp_t device_properties; + HIP_CHECK(hipGetDevice(&device)); + HIP_CHECK(hipGetDeviceProperties(&device_properties, device)); + + if (!device_properties.cooperativeLaunch) { + HipTest::HIP_SKIP_TEST("Device doesn't support cooperative launch!"); + return; + } + + const auto blocks = GenerateBlockDimensionsForShuffle(); + const auto threads = GenerateThreadDimensionsForShuffle(); + auto test_case = GENERATE(range(0, 4)); + uint64_t active_mask = get_active_mask(test_case); + INFO("Grid dimensions: x " << blocks.x << ", y " << blocks.y << ", z " << blocks.z); + INFO("Block dimensions: x " << threads.x << ", y " << threads.y << ", z " << threads.z); + INFO("Coalesced group mask: " << active_mask); + const CPUGrid grid(blocks, threads); + + LinearAllocGuard uint_arr_dev(LinearAllocs::hipMalloc, + grid.thread_count_ * sizeof(unsigned int)); + LinearAllocGuard uint_arr(LinearAllocs::hipHostMalloc, + grid.thread_count_ * sizeof(unsigned int)); + HIP_CHECK(hipMemset(uint_arr_dev.ptr(), 0, grid.thread_count_ * sizeof(unsigned int))); + + // Launch Kernel + coalesced_group_size_getter<<>>(uint_arr_dev.ptr(), active_mask); + + HIP_CHECK(hipMemcpy(uint_arr.ptr(), uint_arr_dev.ptr(), + grid.thread_count_ * sizeof(*uint_arr.ptr()), hipMemcpyDeviceToHost)); + HIP_CHECK(hipMemset(uint_arr_dev.ptr(), 0, grid.thread_count_ * sizeof(unsigned int))); + HIP_CHECK(hipDeviceSynchronize()); + coalesced_group_thread_rank_getter + <<>>(uint_arr_dev.ptr(), active_mask); + + // Verify coalesced_group.size() values + unsigned int coalesced_size = 0; + const auto partitions_in_block = (grid.threads_in_block_count_ + kWarpSize - 1) / kWarpSize; + for (int i = 0; i < grid.thread_count_; i++) { + const auto rank_in_block = grid.thread_rank_in_block(i).value(); + const int rank_in_partition = rank_in_block % kWarpSize; + // If the number of threads in a block is not a multiple of warp size, the + // last warp will have inactive threads and coalesced group size must be recalculated + if (rank_in_block == (partitions_in_block - 1) * kWarpSize) { + unsigned int partition_size = + grid.threads_in_block_count_ - (partitions_in_block - 1) * kWarpSize; + coalesced_size = get_active_thread_count(active_mask, partition_size); + } else if (rank_in_block == 0) { + coalesced_size = get_active_thread_count(active_mask, kWarpSize); + } + if (active_mask & (static_cast(1) << rank_in_partition)) { + if (uint_arr.ptr()[i] != coalesced_size) { + REQUIRE(uint_arr.ptr()[i] == coalesced_size); + } + } + } + + HIP_CHECK(hipMemcpy(uint_arr.ptr(), uint_arr_dev.ptr(), + grid.thread_count_ * sizeof(*uint_arr.ptr()), hipMemcpyDeviceToHost)); + HIP_CHECK(hipDeviceSynchronize()); + + // Verify coalesced_group.thread_rank() values + unsigned int coalesced_rank = 0; + for (int i = 0; i < grid.thread_count_; i++) { + const auto rank_in_block = grid.thread_rank_in_block(i).value(); + const int rank_in_partition = rank_in_block % kWarpSize; + if (rank_in_partition == 0) coalesced_rank = 0; + + if (active_mask & (static_cast(1) << rank_in_partition)) { + if (uint_arr.ptr()[i] != coalesced_rank) { + REQUIRE(uint_arr.ptr()[i] == coalesced_rank); + } + coalesced_rank++; + } + } +} + +/** + * Test Description + * ------------------------ + * - Launches kernels that write the return values of size and thread_rank member functions to an + * output array that is validated on the host side, while treating the coalesced group, created + * according to the generated mask, as a thread group. The kernels are run sequentially, reusing the + * output array, to avoid running out of device memory for large kernel launches + * Test source + * ------------------------ + * - unit/cooperativeGrps/coalesced_group.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_Coalesced_Group_Getters_Via_Base_Type_Positive_Basic") { + int device; + hipDeviceProp_t device_properties; + HIP_CHECK(hipGetDevice(&device)); + HIP_CHECK(hipGetDeviceProperties(&device_properties, device)); + + if (!device_properties.cooperativeLaunch) { + HipTest::HIP_SKIP_TEST("Device doesn't support cooperative launch!"); + return; + } + + const auto blocks = GenerateBlockDimensionsForShuffle(); + const auto threads = GenerateThreadDimensionsForShuffle(); + auto test_case = GENERATE(range(0, 4)); + uint64_t active_mask = get_active_mask(test_case); + INFO("Grid dimensions: x " << blocks.x << ", y " << blocks.y << ", z " << blocks.z); + INFO("Block dimensions: x " << threads.x << ", y " << threads.y << ", z " << threads.z); + INFO("Coalesced group mask: " << active_mask); + + const CPUGrid grid(blocks, threads); + + LinearAllocGuard uint_arr_dev(LinearAllocs::hipMalloc, + grid.thread_count_ * sizeof(unsigned int)); + LinearAllocGuard uint_arr(LinearAllocs::hipHostMalloc, + grid.thread_count_ * sizeof(unsigned int)); + HIP_CHECK(hipMemset(uint_arr_dev.ptr(), 0, grid.thread_count_ * sizeof(unsigned int))); + + // Launch Kernel + coalesced_group_size_getter + <<>>(uint_arr_dev.ptr(), active_mask); + + HIP_CHECK(hipMemcpy(uint_arr.ptr(), uint_arr_dev.ptr(), + grid.thread_count_ * sizeof(*uint_arr.ptr()), hipMemcpyDeviceToHost)); + HIP_CHECK(hipMemset(uint_arr_dev.ptr(), 0, grid.thread_count_ * sizeof(unsigned int))); + HIP_CHECK(hipDeviceSynchronize()); + coalesced_group_thread_rank_getter + <<>>(uint_arr_dev.ptr(), active_mask); + + // Verify coalesced_group.size() values + unsigned int coalesced_size = 0; + const auto partitions_in_block = (grid.threads_in_block_count_ + kWarpSize - 1) / kWarpSize; + for (int i = 0; i < grid.thread_count_; i++) { + const auto rank_in_block = grid.thread_rank_in_block(i).value(); + const int rank_in_partition = rank_in_block % kWarpSize; + // If the number of threads in a block is not a multiple of warp size, the + // last warp will have inactive threads and coalesced group size must be recalculated + if (rank_in_block == (partitions_in_block - 1) * kWarpSize) { + unsigned int partition_size = + grid.threads_in_block_count_ - (partitions_in_block - 1) * kWarpSize; + coalesced_size = get_active_thread_count(active_mask, partition_size); + } else if (rank_in_block == 0) { + coalesced_size = get_active_thread_count(active_mask, kWarpSize); + } + if (active_mask & (static_cast(1) << rank_in_partition)) { + if (uint_arr.ptr()[i] != coalesced_size) { + REQUIRE(uint_arr.ptr()[i] == coalesced_size); + } + } + } + + HIP_CHECK(hipMemcpy(uint_arr.ptr(), uint_arr_dev.ptr(), + grid.thread_count_ * sizeof(*uint_arr.ptr()), hipMemcpyDeviceToHost)); + HIP_CHECK(hipDeviceSynchronize()); + + // Verify coalesced_group.thread_rank() values + unsigned int coalesced_rank = 0; + for (int i = 0; i < grid.thread_count_; i++) { + const auto rank_in_block = grid.thread_rank_in_block(i).value(); + const int rank_in_partition = rank_in_block % kWarpSize; + if (rank_in_partition == 0) coalesced_rank = 0; + + if (active_mask & (static_cast(1) << rank_in_partition)) { + if (uint_arr.ptr()[i] != coalesced_rank) { + REQUIRE(uint_arr.ptr()[i] == coalesced_rank); + } + coalesced_rank++; + } + } +} + +/** + * Test Description + * ------------------------ + * - Launches kernels that write the return values of size and thread_rank non-member functions + * of coalesced groups, created according to the generated mask, to an output array that is + * validated on the host side. The kernels are run sequentially, reusing the output array, to avoid + * running out of device memory for large kernel launches. + * Test source + * ------------------------ + * - unit/cooperativeGrps/coalesced_group.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_Coalesced_Group_Getters_Via_Non_Member_Functions_Positive_Basic") { + int device; + hipDeviceProp_t device_properties; + HIP_CHECK(hipGetDevice(&device)); + HIP_CHECK(hipGetDeviceProperties(&device_properties, device)); + + if (!device_properties.cooperativeLaunch) { + HipTest::HIP_SKIP_TEST("Device doesn't support cooperative launch!"); + return; + } + + const auto blocks = GenerateBlockDimensionsForShuffle(); + const auto threads = GenerateThreadDimensionsForShuffle(); + auto test_case = GENERATE(range(0, 4)); + uint64_t active_mask = get_active_mask(test_case); + INFO("Grid dimensions: x " << blocks.x << ", y " << blocks.y << ", z " << blocks.z); + INFO("Block dimensions: x " << threads.x << ", y " << threads.y << ", z " << threads.z); + INFO("Coalesced group mask: " << active_mask); + + const CPUGrid grid(blocks, threads); + + LinearAllocGuard uint_arr_dev(LinearAllocs::hipMalloc, + grid.thread_count_ * sizeof(unsigned int)); + LinearAllocGuard uint_arr(LinearAllocs::hipHostMalloc, + grid.thread_count_ * sizeof(unsigned int)); + HIP_CHECK(hipMemset(uint_arr_dev.ptr(), 0, grid.thread_count_ * sizeof(unsigned int))); + + // Launch Kernel + coalesced_group_non_member_size_getter + <<>>(uint_arr_dev.ptr(), active_mask); + + HIP_CHECK(hipMemcpy(uint_arr.ptr(), uint_arr_dev.ptr(), + grid.thread_count_ * sizeof(*uint_arr.ptr()), hipMemcpyDeviceToHost)); + HIP_CHECK(hipMemset(uint_arr_dev.ptr(), 0, grid.thread_count_ * sizeof(unsigned int))); + HIP_CHECK(hipDeviceSynchronize()); + coalesced_group_non_member_thread_rank_getter + <<>>(uint_arr_dev.ptr(), active_mask); + + // Verify coalesced_group.size() values + unsigned int coalesced_size = 0; + const auto partitions_in_block = (grid.threads_in_block_count_ + kWarpSize - 1) / kWarpSize; + for (int i = 0; i < grid.thread_count_; i++) { + const auto rank_in_block = grid.thread_rank_in_block(i).value(); + const int rank_in_partition = rank_in_block % kWarpSize; + // If the number of threads in a block is not a multiple of warp size, the + // last warp will have inactive threads and coalesced group size must be recalculated + if (rank_in_block == (partitions_in_block - 1) * kWarpSize) { + unsigned int partition_size = + grid.threads_in_block_count_ - (partitions_in_block - 1) * kWarpSize; + coalesced_size = get_active_thread_count(active_mask, partition_size); + } else if (rank_in_block == 0) { + coalesced_size = get_active_thread_count(active_mask, kWarpSize); + } + if (active_mask & (static_cast(1) << rank_in_partition)) { + if (uint_arr.ptr()[i] != coalesced_size) { + REQUIRE(uint_arr.ptr()[i] == coalesced_size); + } + } + } + + HIP_CHECK(hipMemcpy(uint_arr.ptr(), uint_arr_dev.ptr(), + grid.thread_count_ * sizeof(*uint_arr.ptr()), hipMemcpyDeviceToHost)); + HIP_CHECK(hipDeviceSynchronize()); + + // Verify coalesced_group.thread_rank() values + unsigned int coalesced_rank = 0; + for (int i = 0; i < grid.thread_count_; i++) { + const auto rank_in_block = grid.thread_rank_in_block(i).value(); + const int rank_in_partition = rank_in_block % kWarpSize; + if (rank_in_partition == 0) coalesced_rank = 0; + + if (active_mask & (static_cast(1) << rank_in_partition)) { + if (uint_arr.ptr()[i] != coalesced_rank) { + REQUIRE(uint_arr.ptr()[i] == coalesced_rank); + } + coalesced_rank++; + } + } +} + +template +__global__ void coalesced_group_shfl_up(T* const out, const unsigned int delta, + const uint64_t active_mask) { + const cg::thread_block_tile tile = + cg::tiled_partition(cg::this_thread_block()); + if (active_mask & (static_cast(1) << tile.thread_rank())) { + cg::coalesced_group active = cg::coalesced_threads(); + T var = static_cast(active.thread_rank()); + out[thread_rank_in_grid()] = active.shfl_up(var, delta); + } +} + +template void CoalescedGroupShflUpTestImpl() { + const auto blocks = GenerateBlockDimensionsForShuffle(); + const auto threads = GenerateThreadDimensionsForShuffle(); + auto test_case = GENERATE(range(0, 4)); + uint64_t active_mask = get_active_mask(test_case); + INFO("Grid dimensions: x " << blocks.x << ", y " << blocks.y << ", z " << blocks.z); + INFO("Block dimensions: x " << threads.x << ", y " << threads.y << ", z " << threads.z); + INFO("Coalesced group mask: " << active_mask); + unsigned int active_thread_count = get_active_thread_count(active_mask, kWarpSize); + + auto delta = GENERATE(range(static_cast(0), kWarpSize)); + delta = delta % active_thread_count; + INFO("Delta: " << delta); + CPUGrid grid(blocks, threads); + + const auto alloc_size = grid.thread_count_ * sizeof(T); + LinearAllocGuard arr_dev(LinearAllocs::hipMalloc, alloc_size); + LinearAllocGuard arr(LinearAllocs::hipHostMalloc, alloc_size); + + coalesced_group_shfl_up<<>>(arr_dev.ptr(), delta, active_mask); + HIP_CHECK(hipMemcpy(arr.ptr(), arr_dev.ptr(), alloc_size, hipMemcpyDeviceToHost)); + HIP_CHECK(hipDeviceSynchronize()); + + unsigned int coalesced_rank = 0; + for (int i = 0; i < grid.thread_count_; i++) { + const auto rank_in_block = grid.thread_rank_in_block(i).value(); + const int rank_in_partition = rank_in_block % kWarpSize; + if (rank_in_partition == 0) coalesced_rank = 0; + if (active_mask & (static_cast(1) << rank_in_partition)) { + int target = coalesced_rank - delta; + target = target < 0 ? coalesced_rank : target; + if (arr.ptr()[i] != target) { + REQUIRE(arr.ptr()[i] == target); + } + coalesced_rank++; + } + } +} + + +/** + * Test Description + * ------------------------ + * - Validates the shuffle up behavior of coalesced group, created according to the generated + * mask, for various delta values + * Test source + * ------------------------ + * - unit/cooperativeGrps/coalesced_group.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEMPLATE_TEST_CASE("Unit_Coalesced_Group_Shfl_Up_Positive_Basic", "", int, unsigned int, long, + unsigned long, long long, unsigned long long, float, double) { + CoalescedGroupShflUpTestImpl(); +} + +template +__global__ void coalesced_group_shfl_down(T* const out, const unsigned int delta, + const uint64_t active_mask) { + const cg::thread_block_tile tile = + cg::tiled_partition(cg::this_thread_block()); + if (active_mask & (static_cast(1) << tile.thread_rank())) { + cg::coalesced_group active = cg::coalesced_threads(); + T var = static_cast(active.thread_rank()); + out[thread_rank_in_grid()] = active.shfl_down(var, delta); + } +} + +template void CoalescedGroupShflDownTest() { + const auto blocks = GenerateBlockDimensionsForShuffle(); + const auto threads = GenerateThreadDimensionsForShuffle(); + auto test_case = GENERATE(range(0, 4)); + uint64_t active_mask = get_active_mask(test_case); + INFO("Grid dimensions: x " << blocks.x << ", y " << blocks.y << ", z " << blocks.z); + INFO("Block dimensions: x " << threads.x << ", y " << threads.y << ", z " << threads.z); + INFO("Coalesced group mask: " << active_mask); + unsigned int active_thread_count = get_active_thread_count(active_mask, kWarpSize); + + auto delta = GENERATE(range(static_cast(0), kWarpSize)); + delta = delta % active_thread_count; + INFO("Delta: " << delta); + CPUGrid grid(blocks, threads); + + const auto alloc_size = grid.thread_count_ * sizeof(T); + LinearAllocGuard arr_dev(LinearAllocs::hipMalloc, alloc_size); + LinearAllocGuard arr(LinearAllocs::hipHostMalloc, alloc_size); + + coalesced_group_shfl_down<<>>(arr_dev.ptr(), delta, active_mask); + HIP_CHECK(hipMemcpy(arr.ptr(), arr_dev.ptr(), alloc_size, hipMemcpyDeviceToHost)); + HIP_CHECK(hipDeviceSynchronize()); + + unsigned int coalesced_rank = 0; + unsigned int coalesced_size = 0; + const auto partitions_in_block = (grid.threads_in_block_count_ + kWarpSize - 1) / kWarpSize; + for (int i = 0; i < grid.thread_count_; i++) { + const auto rank_in_block = grid.thread_rank_in_block(i).value(); + const int rank_in_partition = rank_in_block % kWarpSize; + if (rank_in_partition == 0) coalesced_rank = 0; + // If the number of threads in a block is not a multiple of warp size, the + // last warp will have inactive threads and coalesced group size must be recalculated + if (rank_in_block == (partitions_in_block - 1) * kWarpSize) { + unsigned int partition_size = + grid.threads_in_block_count_ - (partitions_in_block - 1) * kWarpSize; + coalesced_size = get_active_thread_count(active_mask, partition_size); + } else if (rank_in_block == 0) { + coalesced_size = get_active_thread_count(active_mask, kWarpSize); + } + if (active_mask & (static_cast(1) << rank_in_partition)) { + int target = coalesced_rank + delta; + target = target < coalesced_size ? target : coalesced_rank; + if (arr.ptr()[i] != target) { + REQUIRE(arr.ptr()[i] == target); + } + coalesced_rank++; + } + } +} + +/** + * Test Description + * ------------------------ + * - Validates the shuffle down behavior of coalesced group, created according to the generated + * mask, for various delta values + * Test source + * ------------------------ + * - unit/cooperativeGrps/coalesced_group.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEMPLATE_TEST_CASE("Unit_Coalesced_Group_Shfl_Down_Positive_Basic", "", int, unsigned int, long, + unsigned long, long long, unsigned long long, float, double) { + CoalescedGroupShflDownTest(); +} + +template +__global__ void coalesced_group_shfl(T* const out, uint8_t* target_lanes, + const uint64_t active_mask) { + const cg::thread_block_tile tile = + cg::tiled_partition(cg::this_thread_block()); + if (active_mask & (static_cast(1) << tile.thread_rank())) { + cg::coalesced_group active = cg::coalesced_threads(); + T var = static_cast(active.thread_rank()); + out[thread_rank_in_grid()] = active.shfl(var, target_lanes[active.thread_rank()]); + ; + } +} + +template void CoalescedGroupShflTest() { + const auto blocks = GenerateBlockDimensionsForShuffle(); + const auto threads = GenerateThreadDimensionsForShuffle(); + auto test_case = GENERATE(range(0, 4)); + uint64_t active_mask = get_active_mask(test_case); + INFO("Grid dimensions: x " << blocks.x << ", y " << blocks.y << ", z " << blocks.z); + INFO("Block dimensions: x " << threads.x << ", y " << threads.y << ", z " << threads.z); + INFO("Coalesced group mask: " << active_mask); + unsigned int active_thread_count = get_active_thread_count(active_mask, kWarpSize); + CPUGrid grid(blocks, threads); + + const auto alloc_size = grid.thread_count_ * sizeof(T); + LinearAllocGuard arr_dev(LinearAllocs::hipMalloc, alloc_size); + LinearAllocGuard arr(LinearAllocs::hipHostMalloc, alloc_size); + + LinearAllocGuard target_lanes_dev(LinearAllocs::hipMalloc, + active_thread_count * sizeof(uint8_t)); + LinearAllocGuard target_lanes(LinearAllocs::hipHostMalloc, + active_thread_count * sizeof(uint8_t)); + // Generate a couple different combinations for target lanes + for (auto i = 0u; i < active_thread_count; ++i) { + target_lanes.ptr()[i] = active_thread_count - 1 - i; + } + + HIP_CHECK(hipMemcpy(target_lanes_dev.ptr(), target_lanes.ptr(), + active_thread_count * sizeof(uint8_t), hipMemcpyHostToDevice)); + coalesced_group_shfl + <<>>(arr_dev.ptr(), target_lanes_dev.ptr(), active_mask); + HIP_CHECK(hipMemcpy(arr.ptr(), arr_dev.ptr(), alloc_size, hipMemcpyDeviceToHost)); + HIP_CHECK(hipDeviceSynchronize()); + + + unsigned int coalesced_rank = 0; + unsigned int coalesced_size = 0; + const auto partitions_in_block = (grid.threads_in_block_count_ + kWarpSize - 1) / kWarpSize; + for (int i = 0; i < grid.thread_count_; i++) { + const auto rank_in_block = grid.thread_rank_in_block(i).value(); + const int rank_in_partition = rank_in_block % kWarpSize; + if (rank_in_partition == 0) coalesced_rank = 0; + // If the number of threads in a block is not a multiple of warp size, the + // last warp will have inactive threads and coalesced group size must be recalculated + if (rank_in_block == (partitions_in_block - 1) * kWarpSize) { + unsigned int partition_size = + grid.threads_in_block_count_ - (partitions_in_block - 1) * kWarpSize; + coalesced_size = get_active_thread_count(active_mask, partition_size); + } else if (rank_in_block == 0) { + coalesced_size = get_active_thread_count(active_mask, kWarpSize); + } + if (active_mask & (static_cast(1) << rank_in_partition)) { + auto target = target_lanes.ptr()[coalesced_rank]; + if (target >= coalesced_size) target = 0; + if (arr.ptr()[i] != target) { + REQUIRE(arr.ptr()[i] == target); + } + coalesced_rank++; + } + } +} + +/** + * Test Description + * ------------------------ + * - Validates the shuffle behavior of of coalesced group, created according to the generated + * mask, for generated shuffle target lanes + * Test source + * ------------------------ + * - unit/cooperativeGrps/coalesced_group.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEMPLATE_TEST_CASE("Unit_Coalesced_Group_Shfl_Positive_Basic", "", int, unsigned int, long, + unsigned long, long long, unsigned long long, float, double) { + CoalescedGroupShflTest(); +} + +static inline std::mt19937& GetRandomGenerator() { + static std::mt19937 mt(11); + return mt; +} + +template static inline T GenerateRandomInteger(const T min, const T max) { + std::uniform_int_distribution dist(min, max); + return dist(GetRandomGenerator()); +} + +template +__global__ void coalesced_group_sync_check(T* global_data, unsigned int* wait_modifiers, + const uint64_t active_mask) { + extern __shared__ uint8_t shared_data[]; + T* const data = use_global ? global_data : reinterpret_cast(shared_data); + const auto tid = cg::this_grid().thread_rank(); + const auto block = cg::this_thread_block(); + const cg::thread_block_tile partition = cg::tiled_partition(block); + + const auto data_idx = [&block](unsigned int i) { return use_global ? i : (i % block.size()); }; + + const auto partition_rank = block.thread_rank() / partition.size(); + + const auto block_base_idx = tid / block.size() * block.size(); + const auto tile_base_idx = block_base_idx + partition_rank * partition.size(); + const auto wait_modifier = wait_modifiers[tid]; + + if (active_mask & (static_cast(1) << partition.thread_rank())) { + cg::coalesced_group active = cg::coalesced_threads(); + busy_wait(wait_modifier); + data[data_idx(tid)] = active.thread_rank(); + active.sync(); + bool valid = true; + for (auto i = 0; i < active.size(); ++i) { + const auto expected = (active.thread_rank() + i) % active.size(); + unsigned int active_count = 0; + int offset = -1; + while (active_count <= expected) { + offset++; + if (active_mask & (static_cast(1) << offset)) active_count++; + } + + if (!(valid &= (data[data_idx(tile_base_idx + offset)] == expected))) { + break; + } + } + active.sync(); + data[data_idx(tid)] = valid; + + if constexpr (!use_global) { + global_data[tid] = data[data_idx(tid)]; + } + } +} + +template void CoalescedGroupSyncTest() { + const auto randomized_run_count = GENERATE(range(0, cmd_options.cg_iterations)); + const auto blocks = GenerateBlockDimensionsForShuffle(); + const auto threads = GenerateThreadDimensionsForShuffle(); + auto test_case = GENERATE(range(0, 4)); + uint64_t active_mask = get_active_mask(test_case); + INFO("Grid dimensions: x " << blocks.x << ", y " << blocks.y << ", z " << blocks.z); + INFO("Block dimensions: x " << threads.x << ", y " << threads.y << ", z " << threads.z); + INFO("Coalesced group mask: " << active_mask); + CPUGrid grid(blocks, threads); + + const auto alloc_size = grid.thread_count_ * sizeof(T); + const auto alloc_size_per_block = alloc_size / grid.block_count_; + + int max_shared_mem_per_block = 0; + HIP_CHECK(hipDeviceGetAttribute(&max_shared_mem_per_block, + hipDeviceAttributeMaxSharedMemoryPerBlock, 0)); + if (!global_memory && (max_shared_mem_per_block < alloc_size_per_block)) { + return; + } + + LinearAllocGuard arr_dev(LinearAllocs::hipMalloc, alloc_size); + LinearAllocGuard arr(LinearAllocs::hipHostMalloc, alloc_size); + LinearAllocGuard wait_modifiers_dev(LinearAllocs::hipMalloc, + grid.thread_count_ * sizeof(unsigned int)); + LinearAllocGuard wait_modifiers(LinearAllocs::hipHostMalloc, + grid.thread_count_ * sizeof(unsigned int)); + if (randomized_run_count != 0) { + std::generate(wait_modifiers.ptr(), wait_modifiers.ptr() + grid.thread_count_, + [] { return GenerateRandomInteger(0u, 1500u); }); + } else { + std::fill_n(wait_modifiers.ptr(), grid.thread_count_, 0u); + } + + const auto shared_memory_size = global_memory ? 0u : alloc_size_per_block; + HIP_CHECK(hipMemcpy(wait_modifiers_dev.ptr(), wait_modifiers.ptr(), + grid.thread_count_ * sizeof(unsigned int), hipMemcpyHostToDevice)); + + coalesced_group_sync_check<<>>( + arr_dev.ptr(), wait_modifiers_dev.ptr(), active_mask); + HIP_CHECK(hipGetLastError()); + + HIP_CHECK(hipMemcpy(arr.ptr(), arr_dev.ptr(), alloc_size, hipMemcpyDeviceToHost)); + HIP_CHECK(hipDeviceSynchronize()); + + for (int i = 0; i < grid.thread_count_; i++) { + const auto rank_in_block = grid.thread_rank_in_block(i).value(); + const int rank_in_partition = rank_in_block % kWarpSize; + if (active_mask & (static_cast(1) << rank_in_partition)) { + if (arr.ptr()[i] != 1) { + REQUIRE(arr.ptr()[i] == 1); + } + } + } +} + +/** + * Test Description + * ------------------------ + * - Launches a kernel where blocks are devided into coalesced groups and every thread writes its + * intra-tile rank into an array slot determined by its grid-wide linear index. The array is either + * in global or dynamic shared memory based on a compile time switch, and the test is run for arrays + * of 1, 2, and 4 byte elements. Before the write each thread executes a busy wait loop for a random + * amount of clock cycles, the amount being read from an input array. After the write a sync for + * active threads is performed and each thread validates that it can read the expected values that + * other active threads within the same coalesced group have written to their respective array + * slots. Each thread begins the validation from a given offset from its own index. For the first + * run of the test, all the offsets are zero, so memory reads should be coalesced as adjacent + * threads read from adjacent memory locations. On subsequent runs the offsets are randomized for + * each thread, leading to non-coalesced reads and cache thrashing. + * Test source + * ------------------------ + * - unit/cooperativeGrps/coalesced_group.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEMPLATE_TEST_CASE("Unit_Coalesced_Group_Sync_Positive_Basic", "", uint8_t, uint16_t, uint32_t) { + SECTION("Global memory") { CoalescedGroupSyncTest(); } + SECTION("Shared memory") { CoalescedGroupSyncTest(); } +} \ No newline at end of file diff --git a/catch/unit/cooperativeGrps/coalesced_groups_shfl_down.cc b/catch/unit/cooperativeGrps/coalesced_groups_shfl_down_old.cc similarity index 100% rename from catch/unit/cooperativeGrps/coalesced_groups_shfl_down.cc rename to catch/unit/cooperativeGrps/coalesced_groups_shfl_down_old.cc diff --git a/catch/unit/cooperativeGrps/coalesced_groups_shfl_up.cc b/catch/unit/cooperativeGrps/coalesced_groups_shfl_up_old.cc similarity index 100% rename from catch/unit/cooperativeGrps/coalesced_groups_shfl_up.cc rename to catch/unit/cooperativeGrps/coalesced_groups_shfl_up_old.cc diff --git a/catch/unit/cooperativeGrps/cooperative_groups_common.hh b/catch/unit/cooperativeGrps/cooperative_groups_common.hh index b0c8598581..25922c5eb2 100644 --- a/catch/unit/cooperativeGrps/cooperative_groups_common.hh +++ b/catch/unit/cooperativeGrps/cooperative_groups_common.hh @@ -28,10 +28,9 @@ constexpr size_t kWarpSize = 32; #else constexpr size_t kWarpSize = 64; #endif +constexpr int kMaxGPUs = 8; } // namespace -constexpr int MaxGPUs = 8; - __device__ inline unsigned int thread_rank_in_grid() { const auto block_size = blockDim.x * blockDim.y * blockDim.z; const auto block_rank_in_grid = (blockIdx.z * gridDim.y + blockIdx.y) * gridDim.x + blockIdx.x; diff --git a/catch/unit/cooperativeGrps/multi_grid_group.cc b/catch/unit/cooperativeGrps/multi_grid_group.cc new file mode 100644 index 0000000000..a92af4e10a --- /dev/null +++ b/catch/unit/cooperativeGrps/multi_grid_group.cc @@ -0,0 +1,652 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. +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 "cooperative_groups_common.hh" + +#include +#include +#include + +/** + * @addtogroup multi_grid_group multi_grid_group + * @{ + * @ingroup DeviceLanguageTest + * Contains unit tests for all multi_grid_group APIs + */ + +namespace cg = cooperative_groups; + +template +static __global__ void multi_grid_group_size_getter(unsigned int* sizes) { + const BaseType group = cg::this_multi_grid(); + sizes[thread_rank_in_grid()] = group.size(); +} + +template +static __global__ void multi_grid_group_thread_rank_getter(unsigned int* thread_ranks) { + const BaseType group = cg::this_multi_grid(); + thread_ranks[thread_rank_in_grid()] = group.thread_rank(); +} + +template +static __global__ void multi_grid_group_is_valid_getter(unsigned int* is_valid_flags) { + const BaseType group = cg::this_multi_grid(); + is_valid_flags[thread_rank_in_grid()] = static_cast(group.is_valid()); +} + +static __global__ void multi_grid_group_num_grids_getter(unsigned int* num_grids) { + num_grids[thread_rank_in_grid()] = cg::this_multi_grid().num_grids(); +} + +static __global__ void multi_grid_group_grid_rank_getter(unsigned int* grid_ranks) { + grid_ranks[thread_rank_in_grid()] = cg::this_multi_grid().grid_rank(); +} + +static __global__ void multi_grid_group_non_member_size_getter(unsigned int* sizes) { + sizes[thread_rank_in_grid()] = cg::group_size(cg::this_multi_grid()); +} + +static __global__ void multi_grid_group_non_member_thread_rank_getter(unsigned int* thread_ranks) { + thread_ranks[thread_rank_in_grid()] = cg::thread_rank(cg::this_multi_grid()); +} + +static __global__ void sync_kernel(unsigned int* atomic_val, unsigned int* global_array, + unsigned int* array, uint32_t loops) { + cooperative_groups::grid_group grid = cooperative_groups::this_grid(); + cooperative_groups::multi_grid_group mgrid = cooperative_groups::this_multi_grid(); + unsigned rank = grid.thread_rank(); + unsigned global_rank = mgrid.thread_rank(); + + int offset = (blockIdx.z * gridDim.y + blockIdx.y) * gridDim.x + blockIdx.x; + for (int i = 0; i < loops; i++) { + // Make the last thread run way behind everyone else. + // If the sync below fails, then the other threads may hit the + // atomicInc instruction many times before the last thread ever gets to it. + // If the sync works, then it will likely contain "total number of blocks"*i + if (rank == (grid.size() - 1)) { + busy_wait(100000); + } + if (threadIdx.x == blockDim.x - 1 && threadIdx.y == blockDim.y - 1 && + threadIdx.z == blockDim.z - 1) { + array[offset] = atomicInc(atomic_val, UINT_MAX); + } + grid.sync(); + + // Make the last thread in the entire multi-grid run way behind + // everyone else. + if (global_rank == (mgrid.size() - 1)) { + busy_wait(100000); + } + // During even iterations, add into your own array entry + // During odd iterations, add into next array entry + unsigned grid_rank = mgrid.grid_rank(); + unsigned inter_gpu_offset = (grid_rank + 1) % mgrid.num_grids(); + if (rank == (grid.size() - 1)) { + if (i % 2 == 0) { + global_array[grid_rank] += 2; + } else { + global_array[inter_gpu_offset] *= 2; + } + } + mgrid.sync(); + offset += gridDim.x * gridDim.y * gridDim.z; + } +} + +static void get_multi_grid_dims(dim3& grid_dim, dim3& block_dim, unsigned int device, + unsigned int test_case) { + hipDeviceProp_t props; + HIP_CHECK(hipSetDevice(device)) + HIP_CHECK(hipGetDeviceProperties(&props, 0)); + int sm = props.multiProcessorCount; + std::vector block_dim_values = {dim3(1, 1, 1), + dim3(props.maxThreadsDim[0], 1, 1), + dim3(1, props.maxThreadsDim[1], 1), + dim3(1, 1, props.maxThreadsDim[2]), + dim3(16, 8, 8), + dim3(32, 32, 1), + dim3(64, 8, 2), + dim3(16, 16, 3), + dim3(kWarpSize - 1, 3, 3), + dim3(kWarpSize + 1, 3, 3)}; + std::vector grid_dim_values = {dim3(1, 1, 1), + dim3(static_cast(0.5 * sm), 1, 3), + dim3(4, static_cast(0.5 * sm), 1), + dim3(1, 1, static_cast(0.5 * sm)), + dim3(sm, 2, 1), + dim3(2, sm, 1), + dim3(1, sm, 2), + dim3(3, 3, 3)}; + + if (test_case < 10) { + grid_dim = grid_dim_values[test_case % grid_dim_values.size()]; + block_dim = block_dim_values[test_case % block_dim_values.size()]; + } else { + grid_dim = grid_dim_values[(test_case + device) % grid_dim_values.size()]; + block_dim = block_dim_values[(test_case + device) % block_dim_values.size()]; + } +} + +/** + * Test Description + * ------------------------ + * - Launches kernels that write the return values of size, thread_rank, grid_rank, num_grids and + * is_valid member functions to an output array that is validated on the host side. The kernels are + * run sequentially, reusing the output array, to avoid running out of device memory for large + * kernel launches. + * Test source + * ------------------------ + * - unit/cooperativeGrps/multi_grid_group.c + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + * - Devices support cooperative multi device launch + */ +TEST_CASE("Unit_Multi_Grid_Group_Getters_Positive_Basic") { + int num_devices = 0; + HIP_CHECK(hipGetDeviceCount(&num_devices)); + num_devices = min(num_devices, kMaxGPUs); + + std::vector device_properties(num_devices); + for (int i = 0; i < num_devices; i++) { + HIP_CHECK(hipGetDeviceProperties(&device_properties[i], i)); + if (!device_properties[i].cooperativeMultiDeviceLaunch) { + HipTest::HIP_SKIP_TEST("Device doesn't support cooperative launch!"); + return; + } + } + const auto test_case = GENERATE(range(0, 20)); + std::vector grid_dims(num_devices); + std::vector block_dims(num_devices); + for (int i = 0; i < num_devices; i++) { + get_multi_grid_dims(grid_dims[i], block_dims[i], i, test_case); + if (!CheckDimensions(i, multi_grid_group_size_getter, grid_dims[i], + block_dims[i])) + return; + INFO("Grid dimensions dev " << i << " : x " << grid_dims[i].x << ", y " << grid_dims[i].y + << ", z " << grid_dims[i].z); + INFO("Block dimensions dev " << i << " : x " << block_dims[i].x << ", y " << block_dims[i].y + << ", z " << block_dims[i].z); + } + + CPUMultiGrid multi_grid(num_devices, grid_dims.data(), block_dims.data()); + + std::vector streams; + std::vector> uint_arr_dev; + std::vector> uint_arr; + std::vector uint_arr_dev_ptr(num_devices); + for (int i = 0; i < num_devices; i++) { + HIP_CHECK(hipSetDevice(i)); + HIP_CHECK(hipDeviceSynchronize()); + streams.emplace_back(Streams::created); + + uint_arr_dev.emplace_back(LinearAllocs::hipMalloc, + multi_grid.grids_[i].thread_count_ * sizeof(unsigned int)); + uint_arr_dev_ptr[i] = uint_arr_dev[i].ptr(); + uint_arr.emplace_back(LinearAllocs::hipHostMalloc, + multi_grid.grids_[i].thread_count_ * sizeof(unsigned int)); + } + + // Launch Kernel + std::vector launchParamsList(num_devices); + std::vector args(num_devices); + for (int i = 0; i < num_devices; i++) { + args[i] = &uint_arr_dev_ptr[i]; + + launchParamsList[i].func = + reinterpret_cast(multi_grid_group_size_getter); + launchParamsList[i].gridDim = grid_dims[i]; + launchParamsList[i].blockDim = block_dims[i]; + launchParamsList[i].sharedMem = 0; + launchParamsList[i].stream = streams[i].stream(); + launchParamsList[i].args = &args[i]; + } + HIP_CHECK(hipLaunchCooperativeKernelMultiDevice(launchParamsList.data(), num_devices, 0)); + for (int i = 0; i < num_devices; i++) { + HIP_CHECK(hipSetDevice(i)); + HIP_CHECK(hipMemcpy(uint_arr[i].ptr(), uint_arr_dev[i].ptr(), + multi_grid.grids_[i].thread_count_ * sizeof(*uint_arr[i].ptr()), + hipMemcpyDeviceToHost)); + HIP_CHECK(hipDeviceSynchronize()); + + launchParamsList[i].func = + reinterpret_cast(multi_grid_group_thread_rank_getter); + } + HIP_CHECK(hipLaunchCooperativeKernelMultiDevice(launchParamsList.data(), num_devices, 0)); + + for (int i = 0; i < num_devices; i++) { + HIP_CHECK(hipSetDevice(i)); + // Verify multi_grid_group.size() values + ArrayAllOf(uint_arr[i].ptr(), multi_grid.grids_[i].thread_count_, + [size = multi_grid.thread_count_](uint32_t) { return size; }); + HIP_CHECK(hipMemcpy(uint_arr[i].ptr(), uint_arr_dev[i].ptr(), + multi_grid.grids_[i].thread_count_ * sizeof(*uint_arr[i].ptr()), + hipMemcpyDeviceToHost)); + HIP_CHECK(hipDeviceSynchronize()); + + launchParamsList[i].func = reinterpret_cast(multi_grid_group_grid_rank_getter); + } + HIP_CHECK(hipLaunchCooperativeKernelMultiDevice(launchParamsList.data(), num_devices, 0)); + + for (int i = 0; i < num_devices; i++) { + HIP_CHECK(hipSetDevice(i)); + // Verify multi_grid_group.thread_rank() values + const auto multi_grid_thread0_rank = multi_grid.thread0_rank_in_multi_grid(i); + ArrayAllOf(uint_arr[i].ptr(), multi_grid.grids_[i].thread_count_, + [rank_0 = multi_grid_thread0_rank](uint32_t j) { return rank_0 + j; }); + HIP_CHECK(hipMemcpy(uint_arr[i].ptr(), uint_arr_dev[i].ptr(), + multi_grid.grids_[i].thread_count_ * sizeof(*uint_arr[i].ptr()), + hipMemcpyDeviceToHost)); + HIP_CHECK(hipDeviceSynchronize()); + + launchParamsList[i].func = reinterpret_cast(multi_grid_group_num_grids_getter); + } + HIP_CHECK(hipLaunchCooperativeKernelMultiDevice(launchParamsList.data(), num_devices, 0)); + + for (int i = 0; i < num_devices; i++) { + HIP_CHECK(hipSetDevice(i)); + // Verify multi_grid_group.grid_rank() values + ArrayFindIfNot(uint_arr[i].ptr(), static_cast(i), + multi_grid.grids_[i].thread_count_); + + HIP_CHECK(hipMemcpy(uint_arr[i].ptr(), uint_arr_dev[i].ptr(), + multi_grid.grids_[i].thread_count_ * sizeof(*uint_arr[i].ptr()), + hipMemcpyDeviceToHost)); + HIP_CHECK(hipDeviceSynchronize()); + + launchParamsList[i].func = + reinterpret_cast(multi_grid_group_is_valid_getter); + } + HIP_CHECK(hipLaunchCooperativeKernelMultiDevice(launchParamsList.data(), num_devices, 0)); + + for (int i = 0; i < num_devices; i++) { + HIP_CHECK(hipSetDevice(i)); + // Verify multi_grid_group.num_grids() values + ArrayFindIfNot(uint_arr[i].ptr(), static_cast(num_devices), + multi_grid.grids_[i].thread_count_); + + HIP_CHECK(hipMemcpy(uint_arr[i].ptr(), uint_arr_dev[i].ptr(), + multi_grid.grids_[i].thread_count_ * sizeof(*uint_arr[i].ptr()), + hipMemcpyDeviceToHost)); + HIP_CHECK(hipDeviceSynchronize()); + + // Verify multi_grid_group.is_valid() values + ArrayFindIfNot(uint_arr[i].ptr(), 1U, multi_grid.grids_[i].thread_count_); + } +} + +/** + * Test Description + * ------------------------ + * - Launches kernels that write the return values of size, thread_rank and is_valid member + * functions to an output array that is validated on the host side, while treating the + * multi_grid_group as a thread group. The kernels are run sequentially, reusing the output array, + * to avoid running out of device memory for large kernel launches. + * Test source + * ------------------------ + * - unit/cooperativeGrps/multi_grid_group.c + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + * - Devices support cooperative multi device launch + */ +TEST_CASE("Unit_Multi_Grid_Group_Getters_Positive_Base_Type") { + int num_devices = 0; + HIP_CHECK(hipGetDeviceCount(&num_devices)); + num_devices = min(num_devices, kMaxGPUs); + std::vector device_properties(num_devices); + for (int i = 0; i < num_devices; i++) { + HIP_CHECK(hipGetDeviceProperties(&device_properties[i], i)); + if (!device_properties[i].cooperativeMultiDeviceLaunch) { + HipTest::HIP_SKIP_TEST("Device doesn't support cooperative launch!"); + return; + } + } + + const auto test_case = GENERATE(range(0, 20)); + std::vector grid_dims(num_devices); + std::vector block_dims(num_devices); + for (int i = 0; i < num_devices; i++) { + get_multi_grid_dims(grid_dims[i], block_dims[i], i, test_case); + if (!CheckDimensions(i, multi_grid_group_size_getter, grid_dims[i], + block_dims[i])) + return; + INFO("Grid dimensions dev " << i << " : x " << grid_dims[i].x << ", y " << grid_dims[i].y + << ", z " << grid_dims[i].z); + INFO("Block dimensions dev " << i << " : x " << block_dims[i].x << ", y " << block_dims[i].y + << ", z " << block_dims[i].z); + } + + CPUMultiGrid multi_grid(num_devices, grid_dims.data(), block_dims.data()); + + std::vector streams; + std::vector> uint_arr_dev; + std::vector> uint_arr; + std::vector uint_arr_dev_ptr(num_devices); + for (int i = 0; i < num_devices; i++) { + HIP_CHECK(hipSetDevice(i)); + HIP_CHECK(hipDeviceSynchronize()); + streams.emplace_back(Streams::created); + + uint_arr_dev.emplace_back(LinearAllocs::hipMalloc, + multi_grid.grids_[i].thread_count_ * sizeof(unsigned int)); + uint_arr_dev_ptr[i] = uint_arr_dev[i].ptr(); + uint_arr.emplace_back(LinearAllocs::hipHostMalloc, + multi_grid.grids_[i].thread_count_ * sizeof(unsigned int)); + } + + // Launch Kernel + std::vector launchParamsList(num_devices); + std::vector args(num_devices); + for (int i = 0; i < num_devices; i++) { + args[i] = &uint_arr_dev_ptr[i]; + + launchParamsList[i].func = + reinterpret_cast(multi_grid_group_size_getter); + launchParamsList[i].gridDim = grid_dims[i]; + launchParamsList[i].blockDim = block_dims[i]; + launchParamsList[i].sharedMem = 0; + launchParamsList[i].stream = streams[i].stream(); + launchParamsList[i].args = &args[i]; + } + HIP_CHECK(hipLaunchCooperativeKernelMultiDevice(launchParamsList.data(), num_devices, 0)); + + for (int i = 0; i < num_devices; i++) { + HIP_CHECK(hipSetDevice(i)); + HIP_CHECK(hipMemcpy(uint_arr[i].ptr(), uint_arr_dev[i].ptr(), + multi_grid.grids_[i].thread_count_ * sizeof(*uint_arr[i].ptr()), + hipMemcpyDeviceToHost)); + HIP_CHECK(hipDeviceSynchronize()); + + launchParamsList[i].func = + reinterpret_cast(multi_grid_group_thread_rank_getter); + } + HIP_CHECK(hipLaunchCooperativeKernelMultiDevice(launchParamsList.data(), num_devices, 0)); + + for (int i = 0; i < num_devices; i++) { + HIP_CHECK(hipSetDevice(i)); + // Verify multi_grid_group.size() values + ArrayFindIfNot(uint_arr[i].ptr(), multi_grid.thread_count_, multi_grid.grids_[i].thread_count_); + HIP_CHECK(hipMemcpy(uint_arr[i].ptr(), uint_arr_dev[i].ptr(), + multi_grid.grids_[i].thread_count_ * sizeof(*uint_arr[i].ptr()), + hipMemcpyDeviceToHost)); + HIP_CHECK(hipDeviceSynchronize()); +#if HT_AMD + launchParamsList[i].func = + reinterpret_cast(multi_grid_group_is_valid_getter); +#else + launchParamsList[i].func = + reinterpret_cast(multi_grid_group_is_valid_getter); +#endif + } + HIP_CHECK(hipLaunchCooperativeKernelMultiDevice(launchParamsList.data(), num_devices, 0)); + + for (int i = 0; i < num_devices; i++) { + HIP_CHECK(hipSetDevice(i)); + // Verify multi_grid_group.thread_rank() values + const auto multi_grid_thread0_rank = multi_grid.thread0_rank_in_multi_grid(i); + ArrayAllOf(uint_arr[i].ptr(), multi_grid.grids_[i].thread_count_, + [rank_0 = multi_grid_thread0_rank](uint32_t j) { return rank_0 + j; }); + HIP_CHECK(hipMemcpy(uint_arr[i].ptr(), uint_arr_dev[i].ptr(), + multi_grid.grids_[i].thread_count_ * sizeof(*uint_arr[i].ptr()), + hipMemcpyDeviceToHost)); + HIP_CHECK(hipDeviceSynchronize()); + + // Verify multi_grid_group.is_valid() values + ArrayFindIfNot(uint_arr[i].ptr(), 1U, multi_grid.grids_[i].thread_count_); + } +} + +/** + * Test Description + * ------------------------ + * - Launches kernels that write the return values of size and thread_rank non-member functions + * to an output array that is validated on the host side. The kernels are run sequentially, reusing + * the output array, to avoid running out of device memory for large kernel launches. + * Test source + * ------------------------ + * - unit/cooperativeGrps/multi_grid_group.c + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + * - Devices support cooperative multi device launch + */ +TEST_CASE("Unit_Multi_Grid_Group_Getters_Positive_Non_Member_Functions") { + int num_devices = 0; + HIP_CHECK(hipGetDeviceCount(&num_devices)); + num_devices = min(num_devices, kMaxGPUs); + + std::vector device_properties(num_devices); + for (int i = 0; i < num_devices; i++) { + HIP_CHECK(hipGetDeviceProperties(&device_properties[i], i)); + if (!device_properties[i].cooperativeMultiDeviceLaunch) { + HipTest::HIP_SKIP_TEST("Device doesn't support cooperative launch!"); + return; + } + } + const auto test_case = GENERATE(range(0, 20)); + std::vector grid_dims(num_devices); + std::vector block_dims(num_devices); + for (int i = 0; i < num_devices; i++) { + get_multi_grid_dims(grid_dims[i], block_dims[i], i, test_case); + if (!CheckDimensions(i, multi_grid_group_size_getter, grid_dims[i], + block_dims[i])) + return; + INFO("Grid dimensions dev " << i << " : x " << grid_dims[i].x << ", y " << grid_dims[i].y + << ", z " << grid_dims[i].z); + INFO("Block dimensions dev " << i << " : x " << block_dims[i].x << ", y " << block_dims[i].y + << ", z " << block_dims[i].z); + } + + CPUMultiGrid multi_grid(num_devices, grid_dims.data(), block_dims.data()); + + std::vector streams; + std::vector> uint_arr_dev; + std::vector> uint_arr; + std::vector uint_arr_dev_ptr(num_devices); + for (int i = 0; i < num_devices; i++) { + HIP_CHECK(hipSetDevice(i)); + HIP_CHECK(hipDeviceSynchronize()); + streams.emplace_back(Streams::created); + + uint_arr_dev.emplace_back(LinearAllocs::hipMalloc, + multi_grid.grids_[i].thread_count_ * sizeof(unsigned int)); + uint_arr_dev_ptr[i] = uint_arr_dev[i].ptr(); + uint_arr.emplace_back(LinearAllocs::hipHostMalloc, + multi_grid.grids_[i].thread_count_ * sizeof(unsigned int)); + } + + // Launch Kernel + std::vector launchParamsList(num_devices); + std::vector args(num_devices); + for (int i = 0; i < num_devices; i++) { + args[i] = &uint_arr_dev_ptr[i]; + + launchParamsList[i].func = reinterpret_cast(multi_grid_group_non_member_size_getter); + launchParamsList[i].gridDim = grid_dims[i]; + launchParamsList[i].blockDim = block_dims[i]; + launchParamsList[i].sharedMem = 0; + launchParamsList[i].stream = streams[i].stream(); + launchParamsList[i].args = &args[i]; + } + HIP_CHECK(hipLaunchCooperativeKernelMultiDevice(launchParamsList.data(), num_devices, 0)); + + for (int i = 0; i < num_devices; i++) { + HIP_CHECK(hipSetDevice(i)); + HIP_CHECK(hipMemcpy(uint_arr[i].ptr(), uint_arr_dev[i].ptr(), + multi_grid.grids_[i].thread_count_ * sizeof(*uint_arr[i].ptr()), + hipMemcpyDeviceToHost)); + HIP_CHECK(hipDeviceSynchronize()); + + launchParamsList[i].func = + reinterpret_cast(multi_grid_group_non_member_thread_rank_getter); + } + HIP_CHECK(hipLaunchCooperativeKernelMultiDevice(launchParamsList.data(), num_devices, 0)); + + for (int i = 0; i < num_devices; i++) { + HIP_CHECK(hipSetDevice(i)); + // Verify multi_grid_group.size() values + ArrayFindIfNot(uint_arr[i].ptr(), multi_grid.thread_count_, multi_grid.grids_[i].thread_count_); + HIP_CHECK(hipMemcpy(uint_arr[i].ptr(), uint_arr_dev[i].ptr(), + multi_grid.grids_[i].thread_count_ * sizeof(*uint_arr[i].ptr()), + hipMemcpyDeviceToHost)); + HIP_CHECK(hipDeviceSynchronize()); + // Verify multi_grid_group.thread_rank() values + const auto multi_grid_thread0_rank = multi_grid.thread0_rank_in_multi_grid(i); + ArrayAllOf(uint_arr[i].ptr(), multi_grid.grids_[i].thread_count_, + [rank_0 = multi_grid_thread0_rank](uint32_t j) { return rank_0 + j; }); + } +} + +/** + * Test Description + * ------------------------ + * - Launches a kernel to multiple gpus which tests sync of separate grids and sync of the entire + * multi grid. The last thread in a block in a grid atomically increments a global variable within a + * work loop. The value returned from this atomic increment entirely depends on the order the + * threads arrive at the atomic instruction. Each thread then stores the result in the global array + * based on its block id. A wait loop is inserted into the last thread so that it runs behind all + * other threads. If the grid sync doesn't work, the other threads will increment the atomic + * variable many times before the last thread gets to it and it will read a very large value. If the + * grid sync works, each thread will increment the variable once per loop iteration and the last + * thread will contain total number of blocks * loop iteration. In the end of the work loop, a value + * is added into grid's own global array entry during even iterations and during odd iterations, a + * value of the next grid is multiplied. A wait loop is inserted into the last thread in the entire + * multi-grid so that it runs behind all the other threads. If the multi grid sync doesn't work the + * two global array entries will end up being out of sync, because the intermingling of adds and + * multiplies will not be aligned between the devices. + * Test source + * ------------------------ + * - unit/cooperativeGrps/multi_grid_group.c + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + * - Devices support cooperative multi device launch + */ +TEST_CASE("Unit_Multi_Grid_Group_Positive_Sync") { + int num_devices = 0; + HIP_CHECK(hipGetDeviceCount(&num_devices)); + num_devices = min(num_devices, kMaxGPUs); + + std::vector device_properties(num_devices); + for (int i = 0; i < num_devices; i++) { + HIP_CHECK(hipGetDeviceProperties(&device_properties[i], i)); + if (!device_properties[i].cooperativeMultiDeviceLaunch) { + HipTest::HIP_SKIP_TEST("Device doesn't support cooperative launch!"); + return; + } + } + auto loops = GENERATE(2, 4, 8, 16); + const auto test_case = GENERATE(range(0, 20)); + std::vector grid_dims(num_devices); + std::vector block_dims(num_devices); + for (int i = 0; i < num_devices; i++) { + get_multi_grid_dims(grid_dims[i], block_dims[i], i, test_case); + if (!CheckDimensions(i, sync_kernel, grid_dims[i], block_dims[i])) return; + INFO("Grid dimensions dev " << i << " : x " << grid_dims[i].x << ", y " << grid_dims[i].y + << ", z " << grid_dims[i].z); + INFO("Block dimensions dev " << i << " : x " << block_dims[i].x << ", y " << block_dims[i].y + << ", z " << block_dims[i].z); + } + + CPUMultiGrid multi_grid(num_devices, grid_dims.data(), block_dims.data()); + + std::vector streams; + std::vector> uint_arr_dev; + std::vector> uint_arr; + std::vector> atomic_val; + std::vector uint_arr_dev_ptr(num_devices); + std::vector atomic_val_ptr(num_devices); + for (int i = 0; i < num_devices; i++) { + HIP_CHECK(hipSetDevice(i)); + HIP_CHECK(hipDeviceSynchronize()); + streams.emplace_back(Streams::created); + + // Allocate grid sync arrays + unsigned int array_len = multi_grid.grids_[i].block_count_ * loops; + uint_arr_dev.emplace_back(LinearAllocs::hipMalloc, array_len * sizeof(unsigned int)); + uint_arr_dev_ptr[i] = uint_arr_dev[i].ptr(); + uint_arr.emplace_back(LinearAllocs::hipHostMalloc, array_len * sizeof(unsigned int)); + + atomic_val.emplace_back(LinearAllocs::hipMalloc, sizeof(unsigned int)); + HIP_CHECK(hipMemset(atomic_val[i].ptr(), 0, sizeof(unsigned int))); + atomic_val_ptr[i] = atomic_val[i].ptr(); + } + // Allocate multi_grid sync array + LinearAllocGuard global_arr(LinearAllocs::hipHostMalloc, + num_devices * sizeof(unsigned int)); + HIP_CHECK(hipMemset(global_arr.ptr(), 0, num_devices * sizeof(unsigned int))); + unsigned int* global_arr_ptr = global_arr.ptr(); + + std::vector> dev_params(num_devices, std::vector(4, nullptr)); + std::vector md_params(num_devices); + for (int i = 0; i < num_devices; i++) { + dev_params[i][0] = reinterpret_cast(&atomic_val_ptr[i]); + dev_params[i][1] = reinterpret_cast(&global_arr_ptr); + dev_params[i][2] = reinterpret_cast(&uint_arr_dev_ptr[i]); + dev_params[i][3] = reinterpret_cast(&loops); + + md_params[i].func = reinterpret_cast(sync_kernel); + md_params[i].gridDim = grid_dims[i]; + md_params[i].blockDim = block_dims[i]; + md_params[i].sharedMem = 0; + md_params[i].stream = streams[i].stream(); + md_params[i].args = dev_params[i].data(); + } + + // Launch Kernel + HIP_CHECK(hipLaunchCooperativeKernelMultiDevice(md_params.data(), num_devices, 0)); + HIP_CHECK(hipDeviceSynchronize()); + + // Read back the grid sync buffer to host + for (int i = 0; i < num_devices; i++) { + HIP_CHECK(hipSetDevice(i)); + unsigned int array_len = multi_grid.grids_[i].block_count_ * loops; + HIP_CHECK(hipMemcpy(uint_arr[i].ptr(), uint_arr_dev[i].ptr(), array_len * sizeof(unsigned int), + hipMemcpyDeviceToHost)); + } + + HIP_CHECK(hipDeviceSynchronize()); + + // Verify grid sync host array values + for (int i = 0; i < num_devices; i++) { + unsigned int max_in_this_loop = 0; + for (unsigned int j = 0; j < loops; j++) { + max_in_this_loop += multi_grid.grids_[i].block_count_; + unsigned int k = 0; + for (k = 0; k < multi_grid.grids_[i].block_count_ - 1; k++) { + REQUIRE(uint_arr[i].ptr()[j * multi_grid.grids_[i].block_count_ + k] < max_in_this_loop); + } + REQUIRE(uint_arr[i].ptr()[j * multi_grid.grids_[i].block_count_ + k] == max_in_this_loop - 1); + } + } + + // Verify multi_grid sync array values + const auto f = [loops](unsigned int) -> unsigned int { + unsigned int desired_val = 0; + for (int j = 0; j < loops; j++) { + if (j % 2 == 0) { + desired_val += 2; + } else { + desired_val *= 2; + } + } + return desired_val; + }; + ArrayAllOf(global_arr.ptr(), num_devices, f); +} \ No newline at end of file diff --git a/catch/unit/device/CMakeLists.txt b/catch/unit/device/CMakeLists.txt index 8a14c06860..d7de547fe6 100644 --- a/catch/unit/device/CMakeLists.txt +++ b/catch/unit/device/CMakeLists.txt @@ -3,7 +3,7 @@ set(TEST_SRC hipChooseDevice.cc hipDeviceComputeCapability.cc hipDeviceGetByPCIBusId.cc - hipDeviceGetLimit.cc + hipDeviceGetLimit_old.cc hipDeviceGetName.cc hipDeviceGetPCIBusId.cc hipDeviceSetGetCacheConfig.cc @@ -20,7 +20,8 @@ set(TEST_SRC hipDeviceCanAccessPeer.cc hipDeviceEnableDisablePeerAccess.cc hipExtGetLinkTypeAndHopCount.cc - hipDeviceSetLimit.cc + hipDeviceSetLimit_old.cc + hipDeviceSetGetLimit.cc hipDeviceSetGetSharedMemConfig.cc hipDeviceReset.cc hipDeviceSetGetMemPool.cc diff --git a/catch/unit/device/hipDeviceGetLimit.cc b/catch/unit/device/hipDeviceGetLimit_old.cc similarity index 100% rename from catch/unit/device/hipDeviceGetLimit.cc rename to catch/unit/device/hipDeviceGetLimit_old.cc diff --git a/catch/unit/device/hipDeviceSetGetLimit.cc b/catch/unit/device/hipDeviceSetGetLimit.cc new file mode 100644 index 0000000000..87b417d942 --- /dev/null +++ b/catch/unit/device/hipDeviceSetGetLimit.cc @@ -0,0 +1,190 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. + +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 WARRANNTY OF ANY KIND, EXPRESS OR +IMPLIED, INNCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANNY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER INN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR INN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include + +/** + * @addtogroup hipDeviceSetLimit hipDeviceSetLimit + * @{ + * @ingroup DeviceTest + * `hipDeviceSetLimit(enum hipLimit_t limit, size_t value)` - + * Set Resource limits of current device. + */ + +void DeviceSetLimitTest(hipLimit_t limit) { + size_t old_val; + HIP_CHECK(hipDeviceGetLimit(&old_val, limit)); + REQUIRE(old_val != 0); + + HIP_CHECK(hipDeviceSetLimit(limit, old_val + 8)); + + size_t new_val; + HIP_CHECK(hipDeviceGetLimit(&new_val, limit)); + REQUIRE(new_val >= old_val + 8); +} + +/** + * Test Description + * ------------------------ + * - Basic set-get test for `hipLimitStackSize`. + * Test source + * ------------------------ + * - unit/device/hipDeviceSetGetLimit.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.3 + */ +TEST_CASE("Unit_hipDeviceSetLimit_Positive_StackSize") { DeviceSetLimitTest(hipLimitStackSize); } + +#if HT_NVIDIA + +__device__ __managed__ bool stop = false; + +/** + * Test Description + * ------------------------ + * - Basic set-get test for `hipLimitPrintfFifoSize`. + * Test source + * ------------------------ + * - unit/device/hipDeviceSetGetLimit.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.3 + */ +TEST_CASE("Unit_hipDeviceSetLimit_Positive_PrintfFifoSize") { + DeviceSetLimitTest(hipLimitPrintfFifoSize); +} + +__global__ void PrintfKernel() { + while (!stop) printf(""); +} + +/** + * Test Description + * ------------------------ + * - Tests scenario where we try to set `hipLimitPrintfFifoSize` while a kernel that calls + * `printf()` is running. + * Test source + * ------------------------ + * - unit/device/hipDeviceSetGetLimit.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.3 + */ +TEST_CASE("Unit_hipDeviceSetLimit_Negative_PrintfFifoSize") { + PrintfKernel<<<1, 1>>>(); + HIP_CHECK_ERROR(hipDeviceSetLimit(hipLimitPrintfFifoSize, 1024), hipErrorInvalidValue); + stop = true; + HIP_CHECK(hipDeviceSynchronize()); + stop = false; +} + +/** + * Test Description + * ------------------------ + * - Basic set-get test for `hipLimitMallocHeapSize`. + * Test source + * ------------------------ + * - unit/device/hipDeviceSetGetLimit.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.3 + */ +TEST_CASE("Unit_hipDeviceSetLimit_Positive_MallocHeapSize") { + DeviceSetLimitTest(hipLimitMallocHeapSize); +} + +__global__ void MallocKernel() { + while (!stop) free(malloc(1)); +} + +/** + * Test Description + * ------------------------ + * - Tests scenario where we try to set `hipLimitMallocHeapSize` while a kernel that calls + * `malloc()` and `free()` is running. + * Test source + * ------------------------ + * - unit/device/hipDeviceSetGetLimit.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.3 + */ +TEST_CASE("Unit_hipDeviceSetLimit_Negative_MallocHeapSize") { + MallocKernel<<<1, 1>>>(); + HIP_CHECK_ERROR(hipDeviceSetLimit(hipLimitMallocHeapSize, 1024), hipErrorInvalidValue); + stop = true; + HIP_CHECK(hipDeviceSynchronize()); + stop = false; +} + +#endif + +/** + * Test Description + * ------------------------ + * - Negative parameters test for `hipDeviceSetLimit`. + * Test source + * ------------------------ + * - unit/device/hipDeviceSetGetLimit.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.3 + */ +TEST_CASE("Unit_hipDeviceSetLimit_Negative_Parameters") { + HIP_CHECK_ERROR(hipDeviceSetLimit(static_cast(-1), 1024), hipErrorUnsupportedLimit); +} + +/** + * End doxygen group hipDeviceSetLimit. + * @} + */ + +/** + * @addtogroup hipDeviceGetLimit hipDeviceGetLimit + * @{ + * @ingroup DeviceTest + * `hipDeviceGetLimit(size_t* pValue, enum hipLimit_t limit)` - + * Get Resource limits of current device. + */ + +/** + * Test Description + * ------------------------ + * - Negative parameters test for `hipDeviceGetLimit`. + * Test source + * ------------------------ + * - unit/device/hipDeviceSetGetLimit.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_hipDeviceGetLimit_Negative_Parameters") { + SECTION("nullptr") { + HIP_CHECK_ERROR(hipDeviceGetLimit(nullptr, hipLimitStackSize), hipErrorInvalidValue); + } + + SECTION("unsupported limit") { + size_t val; + HIP_CHECK_ERROR(hipDeviceGetLimit(&val, static_cast(-1)), hipErrorUnsupportedLimit); + } +} \ No newline at end of file diff --git a/catch/unit/device/hipDeviceSetLimit.cc b/catch/unit/device/hipDeviceSetLimit_old.cc similarity index 100% rename from catch/unit/device/hipDeviceSetLimit.cc rename to catch/unit/device/hipDeviceSetLimit_old.cc diff --git a/catch/unit/device_memory/CMakeLists.txt b/catch/unit/device_memory/CMakeLists.txt new file mode 100644 index 0000000000..984062576d --- /dev/null +++ b/catch/unit/device_memory/CMakeLists.txt @@ -0,0 +1,58 @@ +# Copyright (c) 2022 Advanced Micro Devices, Inc. All Rights Reserved. +# +# 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. + +# Common Tests - Test independent of all platforms + +set(TEST_SRC + memcpy.cc + memset.cc +) + +if(HIP_PLATFORM MATCHES "nvidia") + set(LINKER_LIBS nvrtc) +elseif(HIP_PLATFORM MATCHES "amd") + set(LINKER_LIBS hiprtc) +endif() + +if(HIP_PLATFORM MATCHES "amd") + hip_add_exe_to_target(NAME DeviceMemoryTest + TEST_SRC ${TEST_SRC} + TEST_TARGET_NAME build_tests + LINKER_LIBS ${LINKER_LIBS} + PROPERTY CXX_STANDARD 17) +elseif (HIP_PLATFORM MATCHES "nvidia") + hip_add_exe_to_target(NAME DeviceMemoryTest + TEST_SRC ${TEST_SRC} + TEST_TARGET_NAME build_tests + LINKER_LIBS ${LINKER_LIBS} + COMPILE_OPTIONS -std=c++17) +endif() + +# This test fails in PSDB +#add_test(NAME Unit_Device_memcpy_Negative +# COMMAND python3 ${CMAKE_CURRENT_SOURCE_DIR}/../compileAndCaptureOutput.py +# ${CMAKE_CURRENT_SOURCE_DIR} ${HIP_PLATFORM} ${HIP_PATH} +# memcpy_negative_kernels.cc 4) + +# This test fails in PSDB +#add_test(NAME Unit_Device_memset_Negative +# COMMAND python3 ${CMAKE_CURRENT_SOURCE_DIR}/../compileAndCaptureOutput.py +# ${CMAKE_CURRENT_SOURCE_DIR} ${HIP_PLATFORM} ${HIP_PATH} +# memset_negative_kernels.cc 4) diff --git a/catch/unit/device_memory/memcpy.cc b/catch/unit/device_memory/memcpy.cc new file mode 100644 index 0000000000..efb3bea567 --- /dev/null +++ b/catch/unit/device_memory/memcpy.cc @@ -0,0 +1,249 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. +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 "memcpy_negative_kernels_rtc.hh" + +#include +#include +#include + +#include + +/** + * @addtogroup memcpy memcpy + * @{ + * @ingroup DeviceLanguageTest + * `memcpy(void* dst, const void* src, size_t size)` - + * copies device accessible data inside a kernel + */ + +template using kernel_sig = void (*)(T*, T*, const size_t); + +template +__global__ void memcpy_at_once_kernel(T* dst, T* src, const size_t alloc_size) { + memcpy(dst, src, alloc_size); +} + +template __global__ void memcpy_one_by_one_kernel(T* dst, T* src, const size_t N) { + const auto tid = cooperative_groups::this_grid().thread_rank(); + const auto stride = cooperative_groups::this_grid().size(); + + for (auto i = tid; i < N; i += stride) { + memcpy(dst + tid, src + tid, sizeof(T)); + } +} + +template void MemcpyDeviceToDeviceCommon(kernel_sig memcpy_kernel) { + const auto allocation_size = GENERATE(kPageSize / 2, kPageSize, kPageSize * 2); + const auto element_count = allocation_size / sizeof(T); + + LinearAllocGuard input(LinearAllocs::hipHostMalloc, allocation_size); + LinearAllocGuard result(LinearAllocs::hipHostMalloc, allocation_size); + LinearAllocGuard src_allocation(LinearAllocs::hipMalloc, allocation_size); + LinearAllocGuard dst_allocation(LinearAllocs::hipMalloc, allocation_size); + + /* fill input data */ + for (auto i = 0; i < element_count; i++) { + input.host_ptr()[i] = static_cast(i); + } + + /* Copy input data to device memory */ + HIP_CHECK( + hipMemcpy(src_allocation.ptr(), input.host_ptr(), allocation_size, hipMemcpyHostToDevice)); + + /* Launch appropriate kernel*/ + if (memcpy_kernel == &memcpy_at_once_kernel) { + memcpy_at_once_kernel<<<1, 1>>>(dst_allocation.ptr(), src_allocation.ptr(), allocation_size); + } else { + constexpr auto thread_count = 1024; + const auto block_count = element_count / thread_count + 1; + memcpy_one_by_one_kernel + <<>>(dst_allocation.ptr(), src_allocation.ptr(), element_count); + } + + /* Copy filled device memory to result */ + HIP_CHECK( + hipMemcpy(result.host_ptr(), dst_allocation.ptr(), allocation_size, hipMemcpyDeviceToHost)); + + ArrayMismatch(input.host_ptr(), result.host_ptr(), element_count); +} + +template void MemcpyPinnedToDeviceCommon(kernel_sig memcpy_kernel) { + const auto allocation_size = GENERATE(kPageSize / 2, kPageSize, kPageSize * 2); + const auto element_count = allocation_size / sizeof(T); + + LinearAllocGuard input(LinearAllocs::hipHostMalloc, allocation_size); + LinearAllocGuard result(LinearAllocs::hipHostMalloc, allocation_size); + LinearAllocGuard dst_allocation(LinearAllocs::hipMalloc, allocation_size); + + /* fill input data */ + for (auto i = 0; i < element_count; i++) { + input.host_ptr()[i] = static_cast(i); + } + + /* Launch appropriate kernel*/ + if (memcpy_kernel == &memcpy_at_once_kernel) { + memcpy_at_once_kernel<<<1, 1>>>(dst_allocation.ptr(), input.host_ptr(), allocation_size); + } else { + constexpr auto thread_count = 1024; + const auto block_count = element_count / thread_count + 1; + memcpy_one_by_one_kernel + <<>>(dst_allocation.ptr(), input.host_ptr(), element_count); + } + + /* Copy filled device memory to result */ + HIP_CHECK( + hipMemcpy(result.host_ptr(), dst_allocation.ptr(), allocation_size, hipMemcpyDeviceToHost)); + + ArrayMismatch(input.host_ptr(), result.host_ptr(), element_count); +} + +template void MemcpyDeviceToPinnedCommon(kernel_sig memcpy_kernel) { + const auto allocation_size = GENERATE(kPageSize / 2, kPageSize, kPageSize * 2); + const auto element_count = allocation_size / sizeof(T); + + LinearAllocGuard input(LinearAllocs::hipHostMalloc, allocation_size); + LinearAllocGuard result(LinearAllocs::hipHostMalloc, allocation_size); + LinearAllocGuard src_allocation(LinearAllocs::hipMalloc, allocation_size); + + /* fill input data */ + for (auto i = 0; i < element_count; i++) { + input.host_ptr()[i] = static_cast(i); + } + + /* Copy input data to device memory */ + HIP_CHECK( + hipMemcpy(src_allocation.ptr(), input.host_ptr(), allocation_size, hipMemcpyHostToDevice)); + + /* Launch appropriate kernel*/ + if (memcpy_kernel == &memcpy_at_once_kernel) { + memcpy_at_once_kernel<<<1, 1>>>(result.host_ptr(), src_allocation.ptr(), allocation_size); + } else { + constexpr auto thread_count = 1024; + const auto block_count = element_count / thread_count + 1; + memcpy_one_by_one_kernel + <<>>(result.host_ptr(), src_allocation.ptr(), element_count); + } + + HIP_CHECK(hipStreamSynchronize(nullptr)); + + ArrayMismatch(input.host_ptr(), result.host_ptr(), element_count); +} + +template void MemcpyPinnedToPinnedCommon(kernel_sig memcpy_kernel) { + const auto allocation_size = GENERATE(kPageSize / 2, kPageSize, kPageSize * 2); + const auto element_count = allocation_size / sizeof(T); + + LinearAllocGuard input(LinearAllocs::hipHostMalloc, allocation_size); + LinearAllocGuard result(LinearAllocs::hipHostMalloc, allocation_size); + + /* fill input data */ + for (auto i = 0; i < element_count; i++) { + input.host_ptr()[i] = static_cast(i); + } + + /* Launch appropriate kernel*/ + if (memcpy_kernel == &memcpy_at_once_kernel) { + memcpy_at_once_kernel<<<1, 1>>>(result.host_ptr(), input.host_ptr(), allocation_size); + } else { + constexpr auto thread_count = 1024; + const auto block_count = element_count / thread_count + 1; + memcpy_one_by_one_kernel + <<>>(result.host_ptr(), input.host_ptr(), element_count); + } + + HIP_CHECK(hipStreamSynchronize(nullptr)); + + ArrayMismatch(input.host_ptr(), result.host_ptr(), element_count); +} + +template void DeviceMemcpyCommon(kernel_sig memcpy_kernel) { + SECTION("Device to Device memory") { MemcpyDeviceToDeviceCommon(memcpy_kernel); } + + SECTION("Pinned to Device memory") { MemcpyPinnedToDeviceCommon(memcpy_kernel); } + + SECTION("Device to Pinned memory") { MemcpyDeviceToPinnedCommon(memcpy_kernel); } + + SECTION("Pinned to Pinned memory") { MemcpyPinnedToPinnedCommon(memcpy_kernel); } +} + + +/** + * Test Description + * ------------------------ + * - Verifies basic test cases for copying device/pinned memory inside a kernel using various data + * types and memory sizes: + * -# Copies whole memory buffer in one thread + * -# Copies memory buffer elements one by one in multiple threads/blocks + * Test source + * ------------------------ + * - unit/device_memory/memcpy.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEMPLATE_TEST_CASE("Unit_Device_memcpy_Positive", "", char, int, unsigned int, long, unsigned long, + long long, unsigned long long, float, double) { + SECTION("Memcpy whole buffer in one thread") { + DeviceMemcpyCommon(memcpy_at_once_kernel); + } + SECTION("Memcpy buffer in multiple threads/blocks") { + DeviceMemcpyCommon(memcpy_one_by_one_kernel); + } +} + +/** + * Test Description + * ------------------------ + * - RTCs kernels that pass combinations of arguments of invalid types for memcpy + * Test source + * ------------------------ + * - unit/device_memory/memcpy.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_Device_memcpy_Negative_Parameters_RTC") { + hiprtcProgram program{}; + + const auto program_source = kMemcpyParam; + + HIPRTC_CHECK( + hiprtcCreateProgram(&program, program_source, "memcpy_negative.cc", 0, nullptr, nullptr)); + hiprtcResult result{hiprtcCompileProgram(program, 0, nullptr)}; + + // Get the compile log and count compiler error messages + size_t log_size{}; + HIPRTC_CHECK(hiprtcGetProgramLogSize(program, &log_size)); + std::string log(log_size, ' '); + HIPRTC_CHECK(hiprtcGetProgramLog(program, log.data())); + int error_count{0}; + + int expected_error_count{4}; + std::string error_message{"error:"}; + + size_t n_pos = log.find(error_message, 0); + while (n_pos != std::string::npos) { + ++error_count; + n_pos = log.find(error_message, n_pos + 1); + } + + HIPRTC_CHECK(hiprtcDestroyProgram(&program)); + HIPRTC_CHECK_ERROR(result, HIPRTC_ERROR_COMPILATION); + REQUIRE(error_count == expected_error_count); +} diff --git a/catch/unit/device_memory/memcpy_negative_kernels.cc b/catch/unit/device_memory/memcpy_negative_kernels.cc new file mode 100644 index 0000000000..84621dca8b --- /dev/null +++ b/catch/unit/device_memory/memcpy_negative_kernels.cc @@ -0,0 +1,31 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. +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 + +struct Dummy { + __device__ Dummy() {} + __device__ ~Dummy() {} +}; + +/*void* memcpy(void* dst, const void* src, size_t size)*/ +__global__ void memcpy_n1(int* dst, const int src, size_t size) { memcpy(dst, src, size); } +__global__ void memcpy_n2(int dst, const int* src, size_t size) { memcpy(dst, src, size); } +__global__ void memcpy_n3(int* dst, const int* src, size_t* size) { memcpy(dst, src, size); } +__global__ void memcpy_n4(int* dst, const int* src, Dummy size) { memcpy(dst, src, size); } \ No newline at end of file diff --git a/catch/unit/device_memory/memcpy_negative_kernels_rtc.hh b/catch/unit/device_memory/memcpy_negative_kernels_rtc.hh new file mode 100644 index 0000000000..ac86844ece --- /dev/null +++ b/catch/unit/device_memory/memcpy_negative_kernels_rtc.hh @@ -0,0 +1,32 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. +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. +*/ + +#pragma once + +static constexpr auto kMemcpyParam{ + R"( + struct Dummy { + __device__ Dummy() {} + __device__ ~Dummy() {} + }; + __global__ void memcpy_n1(int* dst, const int src, size_t size) { memcpy(dst, src, size); } + __global__ void memcpy_n2(int dst, const int* src, size_t size) { memcpy(dst, src, size); } + __global__ void memcpy_n3(int* dst, const int* src, size_t* size) { memcpy(dst, src, size); } + __global__ void memcpy_n8(int* dst, const int* src, Dummy size) { memcpy(dst, src, size); } + )"}; \ No newline at end of file diff --git a/catch/unit/device_memory/memset.cc b/catch/unit/device_memory/memset.cc new file mode 100644 index 0000000000..902a45111e --- /dev/null +++ b/catch/unit/device_memory/memset.cc @@ -0,0 +1,172 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. +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 "memset_negative_kernels_rtc.hh" + +#include +#include +#include + +#include + +/** + * @addtogroup memset memset + * @{ + * @ingroup DeviceLanguageTest + * `memset(void* ptr, int val, size_t size)` - + * sets device accessible data inside a kernel + */ + +template using kernel_sig = void (*)(T*, int, const size_t); + +template +__global__ void memset_at_once_kernel(T* dst, int value, const size_t alloc_size) { + memset(dst, value, alloc_size); +} + +template __global__ void memset_one_by_one_kernel(T* dst, int value, const size_t N) { + const auto tid = cooperative_groups::this_grid().thread_rank(); + const auto stride = cooperative_groups::this_grid().size(); + + for (auto i = tid; i < N; i += stride) { + memset(dst + tid, value, sizeof(T)); + } +} + +template void MemsetDeviceCommon(kernel_sig memset_kernel) { + const auto allocation_size = GENERATE(kPageSize / 2, kPageSize, kPageSize * 2); + const auto element_count = allocation_size / sizeof(T); + + LinearAllocGuard reference(LinearAllocs::hipHostMalloc, allocation_size); + LinearAllocGuard result(LinearAllocs::hipHostMalloc, allocation_size); + LinearAllocGuard dst_allocation(LinearAllocs::hipMalloc, allocation_size); + + constexpr auto thread_count = 1024; + const auto block_count = element_count / thread_count + 1; + constexpr auto expected_value = 42; + + memset(reference.host_ptr(), expected_value, allocation_size); + + if (memset_kernel == &memset_at_once_kernel) { + memset_at_once_kernel<<<1, 1>>>(dst_allocation.ptr(), expected_value, allocation_size); + } else { + memset_one_by_one_kernel + <<>>(dst_allocation.ptr(), expected_value, element_count); + } + + HIP_CHECK( + hipMemcpy(result.host_ptr(), dst_allocation.ptr(), allocation_size, hipMemcpyDeviceToHost)); + + ArrayMismatch(reference.host_ptr(), result.host_ptr(), element_count); +} + +template void MemsetPinnedCommon(kernel_sig memset_kernel) { + const auto allocation_size = GENERATE(kPageSize / 2, kPageSize, kPageSize * 2); + const auto element_count = allocation_size / sizeof(T); + + LinearAllocGuard reference(LinearAllocs::hipHostMalloc, allocation_size); + LinearAllocGuard result(LinearAllocs::hipHostMalloc, allocation_size); + + constexpr auto thread_count = 1024; + const auto block_count = element_count / thread_count + 1; + constexpr auto expected_value = 42; + + memset(reference.host_ptr(), expected_value, allocation_size); + + if (memset_kernel == &memset_at_once_kernel) { + memset_at_once_kernel<<<1, 1>>>(result.host_ptr(), expected_value, allocation_size); + } else { + memset_one_by_one_kernel + <<>>(result.host_ptr(), expected_value, element_count); + } + + HIP_CHECK(hipStreamSynchronize(nullptr)); + + ArrayMismatch(reference.host_ptr(), result.host_ptr(), element_count); +} + +template void DeviceMemsetCommon(kernel_sig memset_kernel) { + SECTION("Set Device memory") { MemsetDeviceCommon(memset_kernel); } + + SECTION("Set Pinned memory") { MemsetPinnedCommon(memset_kernel); } +} + +/** + * Test Description + * ------------------------ + * - Verifies basic test cases for setting device/pinned memory inside a kernel using various data + * types and memory sizes: + * -# Set whole memory buffer in one thread + * -# Set memory buffer elements one by one in multiple threads/blocks + * Test source + * ------------------------ + * - unit/device_memory/memset.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEMPLATE_TEST_CASE("Unit_Device_memset_Positive", "", char, int, unsigned int, long, unsigned long, + long long, unsigned long long, float, double) { + SECTION("Memset whole buffer in one thread") { + DeviceMemsetCommon(memset_at_once_kernel); + } + SECTION("Memset buffer in multiple threads/blocks") { + DeviceMemsetCommon(memset_one_by_one_kernel); + } +} + +/** + * Test Description + * ------------------------ + * - RTCs kernels that pass combinations of arguments of invalid types for memset + * Test source + * ------------------------ + * - unit/device_memory/memset.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_Device_memset_Negative_Parameters_RTC") { + hiprtcProgram program{}; + + const auto program_source = kMemsetParam; + + HIPRTC_CHECK( + hiprtcCreateProgram(&program, program_source, "memset_negative.cc", 0, nullptr, nullptr)); + hiprtcResult result{hiprtcCompileProgram(program, 0, nullptr)}; + + // Get the compile log and count compiler error messages + size_t log_size{}; + HIPRTC_CHECK(hiprtcGetProgramLogSize(program, &log_size)); + std::string log(log_size, ' '); + HIPRTC_CHECK(hiprtcGetProgramLog(program, log.data())); + int error_count{0}; + + int expected_error_count{4}; + std::string error_message{"error:"}; + + size_t n_pos = log.find(error_message, 0); + while (n_pos != std::string::npos) { + ++error_count; + n_pos = log.find(error_message, n_pos + 1); + } + + HIPRTC_CHECK(hiprtcDestroyProgram(&program)); + HIPRTC_CHECK_ERROR(result, HIPRTC_ERROR_COMPILATION); + REQUIRE(error_count == expected_error_count); +} \ No newline at end of file diff --git a/catch/unit/device_memory/memset_negative_kernels.cc b/catch/unit/device_memory/memset_negative_kernels.cc new file mode 100644 index 0000000000..e6d1c19e04 --- /dev/null +++ b/catch/unit/device_memory/memset_negative_kernels.cc @@ -0,0 +1,31 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. +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 + +struct Dummy { + __device__ Dummy() {} + __device__ ~Dummy() {} +}; + +/*void* memset(void* ptr, int val, size_t size)*/ +__global__ void memset_n1(int* ptr, int* val, size_t size) { memset(ptr, val, size); } +__global__ void memset_n2(int ptr, int val, size_t size) { memset(ptr, val, size); } +__global__ void memset_n3(int* ptr, int val, size_t* size) { memset(ptr, val, size); } +__global__ void memset_n4(int* ptr, int val, Dummy size) { memset(ptr, val, size); } \ No newline at end of file diff --git a/catch/unit/device_memory/memset_negative_kernels_rtc.hh b/catch/unit/device_memory/memset_negative_kernels_rtc.hh new file mode 100644 index 0000000000..43d3c513b0 --- /dev/null +++ b/catch/unit/device_memory/memset_negative_kernels_rtc.hh @@ -0,0 +1,32 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. +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. +*/ + +#pragma once + +static constexpr auto kMemsetParam{ + R"( + struct Dummy { + __device__ Dummy() {} + __device__ ~Dummy() {} + }; + __global__ void memset_n1(int* ptr, int* val, size_t size) { memset(ptr, val, size); } + __global__ void memset_n2(int ptr, int val, size_t size) { memset(ptr, val, size); } + __global__ void memset_n3(int* ptr, int val, size_t* size) { memset(ptr, val, size); } + __global__ void memset_n4(int* ptr, int val, Dummy size) { memset(ptr, val, size); } + )"}; \ No newline at end of file diff --git a/catch/unit/gl_interop/hipGraphicsSubResourceGetMappedArray.cc b/catch/unit/gl_interop/hipGraphicsSubResourceGetMappedArray.cc index cbd165a1ae..1a48e782b2 100644 --- a/catch/unit/gl_interop/hipGraphicsSubResourceGetMappedArray.cc +++ b/catch/unit/gl_interop/hipGraphicsSubResourceGetMappedArray.cc @@ -38,7 +38,7 @@ TEST_CASE("Unit_hipGraphicsSubResourceGetMappedArray_Positive_Basic") { HIP_CHECK(hipGraphicsMapResources(1, &tex_resource, 0)); - hipArray* image_devptr = nullptr; + hipArray_t image_devptr = nullptr; HIP_CHECK(hipGraphicsSubResourceGetMappedArray(&image_devptr, tex_resource, 0, 0)); REQUIRE(image_devptr != nullptr); @@ -60,7 +60,7 @@ TEST_CASE("Unit_hipGraphicsSubResourceGetMappedArray_Negative_Parameters") { HIP_CHECK(hipGraphicsMapResources(1, &tex_resource, 0)); - hipArray* image_devptr = nullptr; + hipArray_t image_devptr = nullptr; SECTION("array == nullptr") { HIP_CHECK(hipGraphicsSubResourceGetMappedArray(nullptr, tex_resource, 0, 0)); diff --git a/catch/unit/graph/CMakeLists.txt b/catch/unit/graph/CMakeLists.txt index d9a529fa1b..e1ee3f1907 100644 --- a/catch/unit/graph/CMakeLists.txt +++ b/catch/unit/graph/CMakeLists.txt @@ -141,6 +141,8 @@ set(TEST_SRC hipGraphKernelNodeSetAttribute.cc hipGraphMemAllocNodeGetParams.cc hipDrvGraphAddMemcpyNode.cc + hipGraphAddMemAllocNode.cc + hipGraphAddMemFreeNode.cc ) add_custom_target(add_Kernel.code COMMAND ${CMAKE_CXX_COMPILER} --genco ${OFFLOAD_ARCH_STR} ${CMAKE_CURRENT_SOURCE_DIR}/add_Kernel.cpp -o ${CMAKE_CURRENT_BINARY_DIR}/../graph/add_Kernel.code -I${HIP_PATH}/include/ -I${CMAKE_CURRENT_SOURCE_DIR}/../../include --rocm-path=${ROCM_PATH}) diff --git a/catch/unit/graph/hipGraphAddMemAllocNode.cc b/catch/unit/graph/hipGraphAddMemAllocNode.cc new file mode 100644 index 0000000000..4c99e25473 --- /dev/null +++ b/catch/unit/graph/hipGraphAddMemAllocNode.cc @@ -0,0 +1,458 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. + +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 +#include +#include + +/** + * @addtogroup hipGraphAddMemAllocNode hipGraphAddMemAllocNode + * @{ + * @ingroup GraphTest + * `hipGraphAddMemAllocNode (hipGraphNode_t *pGraphNode, hipGraph_t graph, const hipGraphNode_t + * *pDependencies, size_t numDependencies, hipMemAllocNodeParams *pNodeParams)` - + * Creates a memory allocation node and adds it to a graph. + */ + +static constexpr auto element_count{512 * 1024 * 1024}; + +__global__ void validateGPU(int* const vec, const int value, size_t N, unsigned int* mismatch) { + int idx = blockIdx.x * blockDim.x + threadIdx.x; + + if (idx < N) { + if (vec[idx] != value) { + atomicAdd(mismatch, 1); + } + } +} + +/** + * Test Description + * ------------------------ + * - Test to verify hipGraphAddMemAllocNode behavior with invalid arguments: + * -# Null graph node + * -# Null graph node + * -# Invalid numDependencies for null list of dependencies + * -# Invalid numDependencies and valid list for dependencies + * -# Null alloc params + * -# Invalid poolProps alloc type + * -# Invalid poolProps location type + * -# Invalid poolProps location id + * -# Bytesize is max size_t + * -# Invalid accessDescCount + * Test source + * ------------------------ + * - /unit/graph/hipGraphAddMemAllocNode.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 6.0 + */ +TEST_CASE("Unit_hipGraphAddMemAllocNode_Negative_Params") { + constexpr size_t N = 1024; + hipGraph_t graph; + hipGraphNode_t alloc_node; + std::vector dependencies; + + HIP_CHECK(hipGraphCreate(&graph, 0)); + + int num_dev = 0; + HIP_CHECK(hipGetDeviceCount(&num_dev)); + + hipMemAccessDesc desc; + memset(&desc, 0, sizeof(hipMemAccessDesc)); + desc.location.type = hipMemLocationTypeDevice; + desc.location.id = 0; + desc.flags = hipMemAccessFlagsProtReadWrite; + + hipMemAllocNodeParams alloc_param; + memset(&alloc_param, 0, sizeof(alloc_param)); + alloc_param.bytesize = N; + alloc_param.poolProps.allocType = hipMemAllocationTypePinned; + alloc_param.poolProps.location.id = 0; + alloc_param.poolProps.location.type = hipMemLocationTypeDevice; + alloc_param.accessDescs = &desc; + alloc_param.accessDescCount = 1; + + SECTION("Passing nullptr to graph node") { + HIP_CHECK_ERROR(hipGraphAddMemAllocNode(nullptr, graph, nullptr, 0, &alloc_param), + hipErrorInvalidValue); + } + + SECTION("Passing nullptr to graph") { + HIP_CHECK_ERROR(hipGraphAddMemAllocNode(&alloc_node, nullptr, nullptr, 0, &alloc_param), + hipErrorInvalidValue); + } + + SECTION("Pass invalid numDependencies") { + HIP_CHECK_ERROR(hipGraphAddMemAllocNode(&alloc_node, graph, nullptr, 11, &alloc_param), + hipErrorInvalidValue); + } + + SECTION("Pass invalid numDependencies and valid list for dependencies") { + HIP_CHECK(hipGraphAddMemAllocNode(&alloc_node, graph, nullptr, 0, &alloc_param)); + dependencies.push_back(alloc_node); + HIP_CHECK_ERROR(hipGraphAddMemAllocNode(&alloc_node, graph, dependencies.data(), + dependencies.size() + 1, &alloc_param), + hipErrorInvalidValue); + } + + SECTION("Passing nullptr to alloc params") { + HIP_CHECK_ERROR(hipGraphAddMemAllocNode(&alloc_node, graph, nullptr, 0, nullptr), + hipErrorInvalidValue); + } + + SECTION("Passing invalid poolProps alloc type") { + alloc_param.poolProps.allocType = hipMemAllocationTypeInvalid; + HIP_CHECK_ERROR(hipGraphAddMemAllocNode(&alloc_node, graph, nullptr, 0, &alloc_param), + hipErrorInvalidValue); + alloc_param.poolProps.allocType = hipMemAllocationTypePinned; + } + + SECTION("Passing invalid poolProps location type") { + alloc_param.poolProps.location.type = hipMemLocationTypeInvalid; + HIP_CHECK_ERROR(hipGraphAddMemAllocNode(&alloc_node, graph, nullptr, 0, &alloc_param), + hipErrorInvalidValue); + alloc_param.poolProps.location.type = hipMemLocationTypeDevice; + } + + SECTION("Passing invalid poolProps location id") { + alloc_param.poolProps.location.id = num_dev; + HIP_CHECK_ERROR(hipGraphAddMemAllocNode(&alloc_node, graph, nullptr, 0, &alloc_param), + hipErrorInvalidValue); + alloc_param.poolProps.location.id = 0; + } + +#if HT_NVIDIA //EXSWHTEC-353 + SECTION("Passing max size_t bytesize") { + alloc_param.bytesize = std::numeric_limits::max(); + HIP_CHECK_ERROR(hipGraphAddMemAllocNode(&alloc_node, graph, nullptr, 0, &alloc_param), + hipErrorOutOfMemory); + alloc_param.bytesize = N; + } + + SECTION("Passing invalid accessDescCount") { + alloc_param.accessDescCount = num_dev + 1; + HIP_CHECK_ERROR(hipGraphAddMemAllocNode(&alloc_node, graph, nullptr, 0, &alloc_param), + hipErrorInvalidValue); + alloc_param.accessDescCount = 0; + } +#endif + + HIP_CHECK(hipGraphDestroy(graph)); +} + +/** + * Test Description + * ------------------------ + * - Test to verify hipGraphAddMemAllocNode unsupported behavior: + * -# More than one instantiation of the graph exist at the same time + * -# Clone graph with mem alloc node + * -# Use graph with mem alloc node in a child node + * -# Delete edge of the graph with mem alloc node + * Test source + * ------------------------ + * - /unit/graph/hipGraphAddMemAllocNode.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 6.0 + */ +TEST_CASE("Unit_hipGraphAddMemAllocNode_Negative_NotSupported") { + constexpr size_t N = 1024; + hipGraph_t graph; + hipGraphNode_t alloc_node; + + HIP_CHECK(hipGraphCreate(&graph, 0)); + + hipMemAllocNodeParams alloc_param; + memset(&alloc_param, 0, sizeof(alloc_param)); + alloc_param.bytesize = N; + alloc_param.poolProps.allocType = hipMemAllocationTypePinned; + alloc_param.poolProps.location.id = 0; + alloc_param.poolProps.location.type = hipMemLocationTypeDevice; + + HIP_CHECK(hipGraphAddMemAllocNode(&alloc_node, graph, nullptr, 0, &alloc_param)); + + SECTION("More than one instantation of the graph exists") { + hipGraphExec_t graph_exec1, graph_exec2; + HIP_CHECK(hipGraphInstantiate(&graph_exec1, graph, nullptr, nullptr, 0)); + HIP_CHECK_ERROR(hipGraphInstantiate(&graph_exec2, graph, nullptr, nullptr, 0), + hipErrorNotSupported); + HIP_CHECK(hipGraphExecDestroy(graph_exec1)); + } + +#if HT_NVIDIA //EXSWHTEC-353 + SECTION("Clone graph with mem alloc node") { + hipGraph_t cloned_graph; + HIP_CHECK_ERROR(hipGraphClone(&cloned_graph, graph), hipErrorNotSupported); + } + + SECTION("Use graph in a child node") { + hipGraph_t parent_graph; + HIP_CHECK(hipGraphCreate(&parent_graph, 0)); + hipGraphNode_t child_graph_node; + HIP_CHECK_ERROR(hipGraphAddChildGraphNode(&child_graph_node, parent_graph, nullptr, 0, graph), + hipErrorNotSupported); + HIP_CHECK(hipGraphDestroy(parent_graph)); + } + + SECTION("Delete edge of the graph") { + hipGraphNode_t empty_node; + HIP_CHECK(hipGraphAddEmptyNode(&empty_node, graph, &alloc_node, 1)); + HIP_CHECK_ERROR(hipGraphRemoveDependencies(graph, &alloc_node, &empty_node, 1), + hipErrorNotSupported); + } +#endif + + HIP_CHECK(hipGraphDestroy(graph)); +} + +/* Create graph with memory nodes that copies memset data to host array */ +static void createGraph(hipGraphExec_t* graph_exec, int* A_h, int fill_value, + int** device_alloc = nullptr) { + constexpr size_t num_bytes = element_count * sizeof(int); + + hipGraph_t graph; + HIP_CHECK(hipGraphCreate(&graph, 0)); + + hipGraphNode_t alloc_node; + hipMemAllocNodeParams alloc_param; + memset(&alloc_param, 0, sizeof(alloc_param)); + alloc_param.bytesize = num_bytes; + alloc_param.poolProps.allocType = hipMemAllocationTypePinned; + alloc_param.poolProps.location.id = 0; + alloc_param.poolProps.location.type = hipMemLocationTypeDevice; + + HIP_CHECK(hipGraphAddMemAllocNode(&alloc_node, graph, nullptr, 0, &alloc_param)); + REQUIRE(alloc_param.dptr != nullptr); + int* A_d = reinterpret_cast(alloc_param.dptr); + + hipGraphNode_t memset_node; + hipMemsetParams memset_params{}; + memset(&memset_params, 0, sizeof(memset_params)); + memset_params.dst = reinterpret_cast(A_d); + memset_params.value = fill_value; + memset_params.pitch = 0; + memset_params.elementSize = sizeof(int); + memset_params.width = element_count; + memset_params.height = 1; + HIP_CHECK(hipGraphAddMemsetNode(&memset_node, graph, &alloc_node, 1, &memset_params)); + + hipGraphNode_t memcpy_node; + HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpy_node, graph, &memset_node, 1, A_h, A_d, num_bytes, + hipMemcpyDeviceToHost)); + + if (device_alloc == nullptr) { + hipGraphNode_t free_node; + HIP_CHECK(hipGraphAddMemFreeNode(&free_node, graph, &memcpy_node, 1, (void*)A_d)); + } else { + *device_alloc = A_d; + } + + // Instantiate graph + HIP_CHECK(hipGraphInstantiate(graph_exec, graph, nullptr, nullptr, 0)); + + HIP_CHECK(hipGraphDestroy(graph)); +} + +static void createFreeGraph(hipGraphExec_t* graph_exec, int* device_alloc) { + hipGraph_t graph; + hipGraphNode_t free_node; + HIP_CHECK(hipGraphCreate(&graph, 0)); + + HIP_CHECK(hipGraphAddMemFreeNode(&free_node, graph, nullptr, 0, (void*)device_alloc)); + + // Instantiate graph + HIP_CHECK(hipGraphInstantiate(graph_exec, graph, nullptr, nullptr, 0)); + + HIP_CHECK(hipGraphDestroy(graph)); +} + +/** + * Test Description + * ------------------------ + * - Test to verify hipGraphAddMemAllocNode allocates memory correctly and graph behaves as + * expected when free node is added to the same graph. + * Test source + * ------------------------ + * - /unit/graph/hipGraphAddMemAllocNode.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 6.0 + */ +TEST_CASE("Unit_hipGraphAddMemAllocNode_Positive_FreeInGraph") { + hipGraphExec_t graph_exec; + + LinearAllocGuard host_alloc = + LinearAllocGuard(LinearAllocs::malloc, element_count * sizeof(int)); + + StreamGuard stream_guard(Streams::created); + hipStream_t stream = stream_guard.stream(); + + constexpr int fill_value = 11; + createGraph(&graph_exec, host_alloc.ptr(), fill_value); + HIP_CHECK(hipGraphLaunch(graph_exec, stream)); + HIP_CHECK(hipStreamSynchronize(stream)); + + ArrayFindIfNot(host_alloc.host_ptr(), fill_value, element_count); + + HIP_CHECK(hipGraphExecDestroy(graph_exec)); +} + +/** + * Test Description + * ------------------------ + * - Test to verify hipGraphAddMemAllocNode allocates memory correctly, graph behaves as expected + * and allocated memory can can be accessed by outside the graph before memory is freed outside the + * stream. + * Test source + * ------------------------ + * - /unit/graph/hipGraphAddMemAllocNode.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 6.0 + */ +TEST_CASE("Unit_hipGraphAddMemAllocNode_Positive_FreeOutsideStream") { + hipGraphExec_t graph_exec; + + LinearAllocGuard host_alloc(LinearAllocs::malloc, element_count * sizeof(int)); + LinearAllocGuard mismatch_count_h = + LinearAllocGuard(LinearAllocs::malloc, sizeof(unsigned int)); + LinearAllocGuard mismatch_count_d = + LinearAllocGuard(LinearAllocs::hipMalloc, sizeof(unsigned int)); + HIP_CHECK(hipMemset(mismatch_count_d.ptr(), 0, sizeof(unsigned int))); + int* dev_p; + + StreamGuard stream_guard(Streams::created); + hipStream_t stream = stream_guard.stream(); + + constexpr auto thread_count = 1024; + const auto block_count = element_count / thread_count + 1; + constexpr int fill_value = 12; + + createGraph(&graph_exec, host_alloc.ptr(), fill_value, &dev_p); + HIP_CHECK(hipGraphLaunch(graph_exec, stream)); + validateGPU<<>>(dev_p, fill_value, element_count, + mismatch_count_d.ptr()); + // Since hipFree is synchronous, the stream must synchronize before freeing dev_p + HIP_CHECK(hipStreamSynchronize(stream)); + HIP_CHECK(hipFree(dev_p)); + + HIP_CHECK(hipMemcpy(mismatch_count_h.host_ptr(), mismatch_count_d.ptr(), sizeof(unsigned int), + hipMemcpyDeviceToHost)); + REQUIRE(mismatch_count_h.host_ptr()[0] == 0); + ArrayFindIfNot(host_alloc.host_ptr(), fill_value, element_count); + + HIP_CHECK(hipGraphExecDestroy(graph_exec)); +} + +/** + * Test Description + * ------------------------ + * - Test to verify hipGraphAddMemAllocNode allocates memory correctly, graph behaves as expected + * and allocated memory can can be accessed by outside the graph before memory is freed. + * Test source + * ------------------------ + * - /unit/graph/hipGraphAddMemAllocNode.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 6.0 + */ +TEST_CASE("Unit_hipGraphAddMemAllocNode_Positive_FreeOutsideGraph") { + hipGraphExec_t graph_exec; + + LinearAllocGuard host_alloc(LinearAllocs::malloc, element_count * sizeof(int)); + LinearAllocGuard mismatch_count_h = + LinearAllocGuard(LinearAllocs::malloc, sizeof(unsigned int)); + LinearAllocGuard mismatch_count_d = + LinearAllocGuard(LinearAllocs::hipMalloc, sizeof(unsigned int)); + HIP_CHECK(hipMemset(mismatch_count_d.ptr(), 0, sizeof(unsigned int))); + int* dev_p; + + StreamGuard stream_guard(Streams::created); + hipStream_t stream = stream_guard.stream(); + + constexpr auto thread_count = 1024; + const auto block_count = element_count / thread_count + 1; + constexpr int fill_value = 13; + + createGraph(&graph_exec, host_alloc.ptr(), fill_value, &dev_p); + HIP_CHECK(hipGraphLaunch(graph_exec, stream)); + validateGPU<<>>(dev_p, fill_value, element_count, + mismatch_count_d.ptr()); + HIP_CHECK(hipFreeAsync(dev_p, stream)); + HIP_CHECK(hipStreamSynchronize(stream)); + + HIP_CHECK(hipMemcpy(mismatch_count_h.host_ptr(), mismatch_count_d.ptr(), sizeof(unsigned int), + hipMemcpyDeviceToHost)); + REQUIRE(mismatch_count_h.host_ptr()[0] == 0); + ArrayFindIfNot(host_alloc.host_ptr(), fill_value, element_count); + + HIP_CHECK(hipGraphExecDestroy(graph_exec)); +} + +/** + * Test Description + * ------------------------ + * - Test to verify hipGraphAddMemAllocNode allocates memory correctly, graph behaves as expected + * and allocated memory can can be accessed by outside the graph before memory is freed in a + * different graph. + * Test source + * ------------------------ + * - /unit/graph/hipGraphAddMemAllocNode.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 6.0 + */ +TEST_CASE("Unit_hipGraphAddMemAllocNode_Positive_FreeSeparateGraph") { + hipGraphExec_t graph_exec1, graph_exec2; + + LinearAllocGuard host_alloc(LinearAllocs::malloc, element_count * sizeof(int)); + LinearAllocGuard mismatch_count_h = + LinearAllocGuard(LinearAllocs::malloc, sizeof(unsigned int)); + LinearAllocGuard mismatch_count_d = + LinearAllocGuard(LinearAllocs::hipMalloc, sizeof(unsigned int)); + HIP_CHECK(hipMemset(mismatch_count_d.ptr(), 0, sizeof(unsigned int))); + int* dev_p; + + StreamGuard stream_guard(Streams::created); + hipStream_t stream = stream_guard.stream(); + + constexpr auto thread_count = 1024; + const auto block_count = element_count / thread_count + 1; + constexpr int fill_value = 13; + + createGraph(&graph_exec1, host_alloc.ptr(), fill_value, &dev_p); + createFreeGraph(&graph_exec2, dev_p); + HIP_CHECK(hipGraphLaunch(graph_exec1, stream)); + validateGPU<<>>(dev_p, fill_value, element_count, + mismatch_count_d.ptr()); + HIP_CHECK(hipGraphLaunch(graph_exec2, stream)); + HIP_CHECK(hipStreamSynchronize(stream)); + + HIP_CHECK(hipMemcpy(mismatch_count_h.host_ptr(), mismatch_count_d.ptr(), sizeof(unsigned int), + hipMemcpyDeviceToHost)); + REQUIRE(mismatch_count_h.host_ptr()[0] == 0); + ArrayFindIfNot(host_alloc.host_ptr(), fill_value, element_count); + + HIP_CHECK(hipGraphExecDestroy(graph_exec1)); + HIP_CHECK(hipGraphExecDestroy(graph_exec2)); +} diff --git a/catch/unit/graph/hipGraphAddMemFreeNode.cc b/catch/unit/graph/hipGraphAddMemFreeNode.cc new file mode 100644 index 0000000000..fc023bff34 --- /dev/null +++ b/catch/unit/graph/hipGraphAddMemFreeNode.cc @@ -0,0 +1,192 @@ +/* +Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved. +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 +#include + +/** + * @addtogroup hipGraphAddMemFreeNode hipGraphAddMemFreeNode + * @{ + * @ingroup GraphTest + * `hipGraphAddMemFreeNode (hipGraphNode_t *pGraphNode, hipGraph_t graph, const hipGraphNode_t + * *pDependencies, size_t numDependencies, void *dev_ptr)` - + * Creates a memory free node and adds it to a graph. + */ + + +/** + * Test Description + * ------------------------ + * - Test to verify hipGraphAddMemFreeNode behavior with invalid arguments: + * -# Null graph node + * -# Null graph + * -# Invalid numDependencies for null list of dependencies + * -# Invalid numDependencies and valid list for dependencies + * -# Null dev_ptr + * -# Invalid dev_ptr address + * -# dev_ptr not allocated with alloc node + * -# Allocation is freed twice in the same graph + * Test source + * ------------------------ + * - /unit/graph/hipGraphAddMemFreeNode.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 6.0 + */ +TEST_CASE("Unit_hipGraphAddMemFreeNode_Negative_Params") { + constexpr size_t N = 1024; + hipGraph_t graph; + hipGraphNode_t alloc_node, free_node; + std::vector dependencies; + + HIP_CHECK(hipGraphCreate(&graph, 0)); + + hipMemAllocNodeParams alloc_param; + memset(&alloc_param, 0, sizeof(alloc_param)); + alloc_param.bytesize = N; + alloc_param.poolProps.allocType = hipMemAllocationTypePinned; + alloc_param.poolProps.location.id = 0; + alloc_param.poolProps.location.type = hipMemLocationTypeDevice; + + HIP_CHECK(hipGraphAddMemAllocNode(&alloc_node, graph, nullptr, 0, &alloc_param)); + REQUIRE(alloc_param.dptr != nullptr); + int* A_d = reinterpret_cast(alloc_param.dptr); + + SECTION("Passing nullptr to graph node") { + HIP_CHECK_ERROR(hipGraphAddMemFreeNode(nullptr, graph, &alloc_node, 1, (void*)A_d), + hipErrorInvalidValue); + } + + SECTION("Passing nullptr to graph") { + HIP_CHECK_ERROR(hipGraphAddMemFreeNode(&free_node, nullptr, &alloc_node, 1, (void*)A_d), + hipErrorInvalidValue); + } + + SECTION("Pass invalid numDependencies") { + HIP_CHECK_ERROR(hipGraphAddMemFreeNode(&free_node, graph, nullptr, 5, (void*)A_d), + hipErrorInvalidValue); + } + + SECTION("Pass invalid numDependencies and valid list for dependencies") { + dependencies.push_back(alloc_node); + HIP_CHECK_ERROR(hipGraphAddMemFreeNode(&free_node, graph, dependencies.data(), + dependencies.size() + 1, (void*)A_d), + hipErrorInvalidValue); + } + + SECTION("Passing nullptr to dev_ptr") { + HIP_CHECK_ERROR(hipGraphAddMemFreeNode(&alloc_node, graph, &alloc_node, 1, nullptr), + hipErrorInvalidValue); + } + + SECTION("Passing invalid address to dev_ptr") { + int value; + HIP_CHECK_ERROR(hipGraphAddMemFreeNode(&alloc_node, graph, &alloc_node, 1, &value), + hipErrorInvalidValue); + } + +#if HT_NVIDIA // EXSWHTEC-352 + SECTION("Passing address not allocated with alloc node to dev_ptr") { + LinearAllocGuard dev_alloc = + LinearAllocGuard(LinearAllocs::hipMalloc, N * sizeof(int)); + HIP_CHECK_ERROR(hipGraphAddMemFreeNode(&alloc_node, graph, &alloc_node, 1, dev_alloc.ptr()), + hipErrorInvalidValue); + } + + SECTION("Free allocation twice in the same graph") { + HIP_CHECK(hipGraphAddMemFreeNode(&alloc_node, graph, &alloc_node, 1, (void*)A_d)); + HIP_CHECK_ERROR(hipGraphAddMemFreeNode(&alloc_node, graph, &alloc_node, 1, (void*)A_d), + hipErrorInvalidValue); + } +#endif + + HIP_CHECK(hipGraphDestroy(graph)); +} + +/** + * Test Description + * ------------------------ + * - Test to verify hipGraphAddMemFreeNode unsupported behavior: + * -# More than one instantiation of the graph exist at the same time + * -# Clone graph with mem free node + * -# Use graph with mem free node in a child node + * -# Delete edge of the graph with mem free node + * Test source + * ------------------------ + * - /unit/graph/hipGraphAddMemFreeNode.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 6.0 + */ +TEST_CASE("Unit_hipGraphAddMemFreeNode_Negative_NotSupported") { + constexpr size_t N = 1024; + hipGraph_t graph1, graph2; + hipGraphNode_t alloc_node, free_node; + + HIP_CHECK(hipGraphCreate(&graph1, 0)); + HIP_CHECK(hipGraphCreate(&graph2, 0)); + + hipMemAllocNodeParams alloc_param; + memset(&alloc_param, 0, sizeof(alloc_param)); + alloc_param.bytesize = N; + alloc_param.poolProps.allocType = hipMemAllocationTypePinned; + alloc_param.poolProps.location.id = 0; + alloc_param.poolProps.location.type = hipMemLocationTypeDevice; + + HIP_CHECK(hipGraphAddMemAllocNode(&alloc_node, graph1, nullptr, 0, &alloc_param)); + REQUIRE(alloc_param.dptr != nullptr); + int* A_d = reinterpret_cast(alloc_param.dptr); + + HIP_CHECK(hipGraphAddMemFreeNode(&free_node, graph2, nullptr, 0, (void*)A_d)); + + SECTION("More than one instantation of the graph exists") { + hipGraphExec_t graph_exec1, graph_exec2; + HIP_CHECK(hipGraphInstantiate(&graph_exec1, graph2, nullptr, nullptr, 0)); + HIP_CHECK_ERROR(hipGraphInstantiate(&graph_exec2, graph2, nullptr, nullptr, 0), + hipErrorNotSupported); + HIP_CHECK(hipGraphExecDestroy(graph_exec1)); + } + +#if HT_NVIDIA // EXSWHTEC-352 + SECTION("Clone graph with mem free node") { + hipGraph_t cloned_graph; + HIP_CHECK_ERROR(hipGraphClone(&cloned_graph, graph2), hipErrorNotSupported); + } + + SECTION("Use graph in a child node") { + hipGraph_t parent_graph; + HIP_CHECK(hipGraphCreate(&parent_graph, 0)); + hipGraphNode_t child_graph_node; + HIP_CHECK_ERROR(hipGraphAddChildGraphNode(&child_graph_node, parent_graph, nullptr, 0, graph2), + hipErrorNotSupported); + HIP_CHECK(hipGraphDestroy(parent_graph)); + } + + SECTION("Delete edge of the graph") { + hipGraphNode_t empty_node; + HIP_CHECK(hipGraphAddEmptyNode(&empty_node, graph2, &free_node, 1)); + HIP_CHECK_ERROR(hipGraphRemoveDependencies(graph2, &free_node, &empty_node, 1), + hipErrorNotSupported); + } +#endif + + HIP_CHECK(hipGraphDestroy(graph1)); + HIP_CHECK(hipGraphDestroy(graph2)); +} diff --git a/catch/unit/graph/hipGraphMemAllocNodeGetParams.cc b/catch/unit/graph/hipGraphMemAllocNodeGetParams.cc index b8e6e42661..99e341a77d 100644 --- a/catch/unit/graph/hipGraphMemAllocNodeGetParams.cc +++ b/catch/unit/graph/hipGraphMemAllocNodeGetParams.cc @@ -18,47 +18,44 @@ THE SOFTWARE. */ /** -* @addtogroup hipGraphMemAllocNodeGetParams hipGraphMemAllocNodeGetParams -* @{ -* @ingroup GraphTest -* `hipGraphMemAllocNodeGetParams(hipGraphNode_t node, hipMemAllocNodeParams* params_out)` -* Returns a memory alloc node's parameters. -* `hipGraphMemFreeNodeGetParams(hipGraphNode_t node, void* dptr_out)` - -* Returns a memory free node's parameters. -*/ + * @addtogroup hipGraphMemAllocNodeGetParams hipGraphMemAllocNodeGetParams + * @{ + * @ingroup GraphTest + * `hipGraphMemAllocNodeGetParams(hipGraphNode_t node, hipMemAllocNodeParams* params_out)` + * Returns a memory alloc node's parameters. + * `hipGraphMemFreeNodeGetParams(hipGraphNode_t node, void* dptr_out)` - + * Returns a memory free node's parameters. + */ #include #include #include +#include +#include /** -* Test Description -* ------------------------ -*  - Functional Test for API - hipGraphMemAllocNodeGetParams -* Create a graph and add a node with hipGraphAddMemAllocNode -* and hipGraphAddMemFreeNode and launch it. -* 1) Get alloc node by calling hipGraphMemAllocNodeGetParams and Validate. -* 2) Get Free Node ptr by calling hipGraphMemFreeNodeGetParams and Validate. -* 3) Check for multiple devices case. -* 4) Allocate multiple alloc node and validate by calling its get param. -* Test source -* ------------------------ -*  - /unit/graph/hipGraphMemAllocNodeGetParams.cc -* Test requirements -* ------------------------ -*  - HIP_VERSION >= 6.0 -*/ + * Test Description + * ------------------------ + *  - Functional Test for API - hipGraphMemAllocNodeGetParams + * Create a graph and add a node with hipGraphAddMemAllocNode + * and hipGraphAddMemFreeNode and launch it. + * 1) Get alloc node by calling hipGraphMemAllocNodeGetParams and Validate. + * 2) Get Free Node ptr by calling hipGraphMemFreeNodeGetParams and Validate. + * 3) Check for multiple devices case. + * 4) Allocate multiple alloc node and validate by calling its get param. + * Test source + * ------------------------ + *  - /unit/graph/hipGraphMemAllocNodeGetParams.cc + * Test requirements + * ------------------------ + *  - HIP_VERSION >= 6.0 + */ -static bool validateAllocParam(hipMemAllocNodeParams in, - hipMemAllocNodeParams out) { - if (in.bytesize != out.bytesize) - return false; - if (in.poolProps.allocType != out.poolProps.allocType) - return false; - if (in.poolProps.location.id != out.poolProps.location.id) - return false; - if (in.poolProps.location.type != out.poolProps.location.type) - return false; +static bool validateAllocParam(hipMemAllocNodeParams in, hipMemAllocNodeParams out) { + if (in.bytesize != out.bytesize) return false; + if (in.poolProps.allocType != out.poolProps.allocType) return false; + if (in.poolProps.location.id != out.poolProps.location.id) return false; + if (in.poolProps.location.type != out.poolProps.location.type) return false; return true; } @@ -85,7 +82,7 @@ static void hipGraphMemAllocNodeGetParams_Functional(unsigned deviceId = 0) { params_in.poolProps.location.type = hipMemLocationTypeDevice; HIP_CHECK(hipGraphAddMemAllocNode(&allocNodeA, graph, NULL, 0, ¶ms_in)); - int *A_d = reinterpret_cast(params_in.dptr); + int* A_d = reinterpret_cast(params_in.dptr); REQUIRE(A_d != nullptr); HIP_CHECK(hipGraphAddMemFreeNode(&freeNodeA, graph, &allocNodeA, 1, A_d)); @@ -105,21 +102,21 @@ static void hipGraphMemAllocNodeGetParams_Functional(unsigned deviceId = 0) { } /** -* Test Description -* ------------------------ -*  - Functional Test for API - hipGraphMemAllocNodeGetParams -* Create a graph and add a node with hipGraphAddMemAllocNode -* and hipGraphAddMemFreeNode and launch it. -* 1) Get alloc node by calling hipGraphMemAllocNodeGetParams and Validate it. -* 2) Get Free node ptr by calling hipGraphMemFreeNodeGetParams and Validate it. -* 3) Check for multiple devices case. -* Test source -* ------------------------ -*  - /unit/graph/hipGraphMemAllocNodeGetParams.cc -* Test requirements -* ------------------------ -*  - HIP_VERSION >= 6.0 -*/ + * Test Description + * ------------------------ + *  - Functional Test for API - hipGraphMemAllocNodeGetParams + * Create a graph and add a node with hipGraphAddMemAllocNode + * and hipGraphAddMemFreeNode and launch it. + * 1) Get alloc node by calling hipGraphMemAllocNodeGetParams and Validate it. + * 2) Get Free node ptr by calling hipGraphMemFreeNodeGetParams and Validate it. + * 3) Check for multiple devices case. + * Test source + * ------------------------ + *  - /unit/graph/hipGraphMemAllocNodeGetParams.cc + * Test requirements + * ------------------------ + *  - HIP_VERSION >= 6.0 + */ TEST_CASE("Unit_hipGraphMem_Alloc_Free_NodeGetParams_Functional") { hipGraphMemAllocNodeGetParams_Functional(); @@ -130,7 +127,7 @@ TEST_CASE("Unit_hipGraphMem_Alloc_Free_NodeGetParams_Functional_MultiDevice") { HIP_CHECK(hipGetDeviceCount(&numDevices)); if (numDevices > 0) { - for ( int i = 0; i < numDevices; ++i ) { + for (int i = 0; i < numDevices; ++i) { hipGraphMemAllocNodeGetParams_Functional(i); } } else { @@ -139,19 +136,19 @@ TEST_CASE("Unit_hipGraphMem_Alloc_Free_NodeGetParams_Functional_MultiDevice") { } /** -* Test Description -* ------------------------ -*  - Functional Test for API - hipGraphMemAllocNodeGetParams -* Create a graph and add multiple node with hipGraphAddMemAllocNode -* and hipGraphAddMemFreeNode and launch it. -* 1) Allocate multiple alloc node and validate by calling its get param. -* Test source -* ------------------------ -*  - /unit/graph/hipGraphMemAllocNodeGetParams.cc -* Test requirements -* ------------------------ -*  - HIP_VERSION >= 6.0 -*/ + * Test Description + * ------------------------ + *  - Functional Test for API - hipGraphMemAllocNodeGetParams + * Create a graph and add multiple node with hipGraphAddMemAllocNode + * and hipGraphAddMemFreeNode and launch it. + * 1) Allocate multiple alloc node and validate by calling its get param. + * Test source + * ------------------------ + *  - /unit/graph/hipGraphMemAllocNodeGetParams.cc + * Test requirements + * ------------------------ + *  - HIP_VERSION >= 6.0 + */ TEST_CASE("Unit_hipGraphMem_Alloc_Free_NodeGetParams_Functional_2") { constexpr size_t N = 1024 * 1024; @@ -173,8 +170,7 @@ TEST_CASE("Unit_hipGraphMem_Alloc_Free_NodeGetParams_Functional_2") { int *A_d, *B_d, *C_d; int *A_h, *B_h, *C_h; - HipTest::initArrays(nullptr, nullptr, nullptr, - &A_h, &B_h, &C_h, N, false); + HipTest::initArrays(nullptr, nullptr, nullptr, &A_h, &B_h, &C_h, N, false); HIP_CHECK(hipGraphCreate(&graph, 0)); HIP_CHECK(hipStreamCreate(&stream)); @@ -187,49 +183,46 @@ TEST_CASE("Unit_hipGraphMem_Alloc_Free_NodeGetParams_Functional_2") { HIP_CHECK(hipGraphAddMemAllocNode(&allocNodeA, graph, NULL, 0, ¶ms_in)); REQUIRE(params_in.dptr != nullptr); - A_d = reinterpret_cast(params_in.dptr); - HIP_CHECK(hipGraphAddMemAllocNode(&allocNodeB, graph, - &allocNodeA, 1, ¶ms_in)); + A_d = reinterpret_cast(params_in.dptr); + HIP_CHECK(hipGraphAddMemAllocNode(&allocNodeB, graph, &allocNodeA, 1, ¶ms_in)); REQUIRE(params_in.dptr != nullptr); - B_d = reinterpret_cast(params_in.dptr); - HIP_CHECK(hipGraphAddMemAllocNode(&allocNodeC, graph, - &allocNodeB, 1, ¶ms_in)); + B_d = reinterpret_cast(params_in.dptr); + HIP_CHECK(hipGraphAddMemAllocNode(&allocNodeC, graph, &allocNodeB, 1, ¶ms_in)); REQUIRE(params_in.dptr != nullptr); - C_d = reinterpret_cast(params_in.dptr); + C_d = reinterpret_cast(params_in.dptr); // Check shows that A_d, B_d & C_d DON'T share any virtual address each other REQUIRE(A_d != B_d); REQUIRE(B_d != C_d); REQUIRE(A_d != C_d); - HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyH2D_A, graph, &allocNodeC, 1, A_d, - A_h, Nbytes, hipMemcpyHostToDevice)); - HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyH2D_B, graph, &allocNodeC, 1, B_d, - B_h, Nbytes, hipMemcpyHostToDevice)); + HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyH2D_A, graph, &allocNodeC, 1, A_d, A_h, Nbytes, + hipMemcpyHostToDevice)); + HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyH2D_B, graph, &allocNodeC, 1, B_d, B_h, Nbytes, + hipMemcpyHostToDevice)); - void* kernelArgs[] = {&A_d, &B_d, &C_d, reinterpret_cast(&NElem)}; - kernelNodeParams.func = reinterpret_cast(HipTest::vectorADD); + void* kernelArgs[] = {&A_d, &B_d, &C_d, reinterpret_cast(&NElem)}; + kernelNodeParams.func = reinterpret_cast(HipTest::vectorADD); kernelNodeParams.gridDim = dim3(blocks); kernelNodeParams.blockDim = dim3(threadsPerBlock); kernelNodeParams.sharedMemBytes = 0; kernelNodeParams.kernelParams = reinterpret_cast(kernelArgs); kernelNodeParams.extra = nullptr; - HIP_CHECK(hipGraphAddKernelNode(&kernel_vecAdd, graph, nullptr, 0, - &kernelNodeParams)); + HIP_CHECK(hipGraphAddKernelNode(&kernel_vecAdd, graph, nullptr, 0, &kernelNodeParams)); // Create dependencies HIP_CHECK(hipGraphAddDependencies(graph, &memcpyH2D_A, &kernel_vecAdd, 1)); HIP_CHECK(hipGraphAddDependencies(graph, &memcpyH2D_B, &kernel_vecAdd, 1)); - HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyD2H_C, graph, &kernel_vecAdd, 1, - C_h, C_d, Nbytes, hipMemcpyDeviceToHost)); + HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyD2H_C, graph, &kernel_vecAdd, 1, C_h, C_d, Nbytes, + hipMemcpyDeviceToHost)); - HIP_CHECK(hipGraphAddMemFreeNode(&freeNodeA, graph, &memcpyD2H_C, - 1, reinterpret_cast(A_d))); - HIP_CHECK(hipGraphAddMemFreeNode(&freeNodeB, graph, &memcpyD2H_C, - 1, reinterpret_cast(B_d))); - HIP_CHECK(hipGraphAddMemFreeNode(&freeNodeC, graph, &memcpyD2H_C, - 1, reinterpret_cast(C_d))); + HIP_CHECK( + hipGraphAddMemFreeNode(&freeNodeA, graph, &memcpyD2H_C, 1, reinterpret_cast(A_d))); + HIP_CHECK( + hipGraphAddMemFreeNode(&freeNodeB, graph, &memcpyD2H_C, 1, reinterpret_cast(B_d))); + HIP_CHECK( + hipGraphAddMemFreeNode(&freeNodeC, graph, &memcpyD2H_C, 1, reinterpret_cast(C_d))); HIP_CHECK(hipGraphMemAllocNodeGetParams(allocNodeA, ¶ms_out)); REQUIRE(true == validateAllocParam(params_in, params_out)); @@ -239,12 +232,9 @@ TEST_CASE("Unit_hipGraphMem_Alloc_Free_NodeGetParams_Functional_2") { REQUIRE(true == validateAllocParam(params_in, params_out)); int temp[] = {0}; - HIP_CHECK(hipGraphMemFreeNodeGetParams(freeNodeA, - reinterpret_cast(temp))); - HIP_CHECK(hipGraphMemFreeNodeGetParams(freeNodeB, - reinterpret_cast(temp))); - HIP_CHECK(hipGraphMemFreeNodeGetParams(freeNodeC, - reinterpret_cast(temp))); + HIP_CHECK(hipGraphMemFreeNodeGetParams(freeNodeA, reinterpret_cast(temp))); + HIP_CHECK(hipGraphMemFreeNodeGetParams(freeNodeB, reinterpret_cast(temp))); + HIP_CHECK(hipGraphMemFreeNodeGetParams(freeNodeC, reinterpret_cast(temp))); HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); HIP_CHECK(hipGraphLaunch(graphExec, stream)); @@ -261,27 +251,111 @@ TEST_CASE("Unit_hipGraphMem_Alloc_Free_NodeGetParams_Functional_2") { } /** -* Test Description -* ------------------------ -*  - Negative Test for API - hipGraphMemAllocNodeGetParams -* 1) Pass MemAllocNode as nullptr -* 2) Pass MemAllocNode as empty node -* 3) Pass params_out as nullptr -* 4) Pass MemFreeNode inplace of MemAllocNode in 1st arguments -* - Negative Test for API - hipGraphMemFreeNodeGetParams -* 1) Pass MemFreeNode as nullptr -* 2) Pass MemFreeNode as empty node -* 3) Pass free pointer as nullptr -* 4) Pass free pointer as invalid pointer -* 5) Pass MemAllocNode inplace of MemFreeNode in 1st arguments -* Test source -* ------------------------ -*  - /unit/graph/hipGraphMemAllocNodeGetParams.cc -* Test requirements -* ------------------------ -*  - HIP_VERSION >= 6.0 -*/ + * Test Description + * ------------------------ + * - Functional Test for API - hipGraphMemAllocNodeGetParams. Create a graph and add a node with + * hipGraphAddMemAllocNode and hipGraphAddMemFreeNode and launch it. Check both pool props and + * access descriptor. + * Test source + * ------------------------ + * - /unit/graph/hipGraphMemAllocNodeGetParams.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 6.0 + */ +TEST_CASE("Unit_hipGraphMem_Alloc_Free_NodeGetParams_Functional_3") { + constexpr auto element_count{512 * 1024 * 1024}; + constexpr size_t num_bytes = element_count * sizeof(int); + hipGraphExec_t graph_exec; + hipGraph_t graph; + + LinearAllocGuard A_h = + LinearAllocGuard(LinearAllocs::malloc, element_count * sizeof(int)); + + StreamGuard stream_guard(Streams::created); + hipStream_t stream = stream_guard.stream(); + HIP_CHECK(hipGraphCreate(&graph, 0)); + + hipMemAccessDesc desc; + memset(&desc, 0, sizeof(hipMemAccessDesc)); + desc.location.type = hipMemLocationTypeDevice; + desc.location.id = 0; + desc.flags = hipMemAccessFlagsProtReadWrite; + + hipGraphNode_t alloc_node; + hipMemAllocNodeParams alloc_param; + memset(&alloc_param, 0, sizeof(alloc_param)); + alloc_param.bytesize = num_bytes; + alloc_param.poolProps.allocType = hipMemAllocationTypePinned; + alloc_param.poolProps.location.id = 0; + alloc_param.poolProps.location.type = hipMemLocationTypeDevice; + alloc_param.accessDescs = &desc; + alloc_param.accessDescCount = 1; + + HIP_CHECK(hipGraphAddMemAllocNode(&alloc_node, graph, nullptr, 0, &alloc_param)); + REQUIRE(alloc_param.dptr != nullptr); + int* A_d = reinterpret_cast(alloc_param.dptr); + + hipMemAllocNodeParams get_alloc_params; + HIP_CHECK(hipGraphMemAllocNodeGetParams(alloc_node, &get_alloc_params)); + REQUIRE(memcmp(&alloc_param, &get_alloc_params, sizeof(hipMemAllocNodeParams)) == 0); + + constexpr int fill_value = 11; + hipGraphNode_t memset_node; + hipMemsetParams memset_params{}; + memset(&memset_params, 0, sizeof(memset_params)); + memset_params.dst = reinterpret_cast(A_d); + memset_params.value = fill_value; + memset_params.pitch = 0; + memset_params.elementSize = sizeof(int); + memset_params.width = element_count; + memset_params.height = 1; + HIP_CHECK(hipGraphAddMemsetNode(&memset_node, graph, &alloc_node, 1, &memset_params)); + + hipGraphNode_t memcpy_node; + HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpy_node, graph, &memset_node, 1, A_h.host_ptr(), A_d, + num_bytes, hipMemcpyDeviceToHost)); + + hipGraphNode_t free_node; + HIP_CHECK(hipGraphAddMemFreeNode(&free_node, graph, &memcpy_node, 1, (void*)A_d)); + + void* dptr_out; + HIP_CHECK(hipGraphMemFreeNodeGetParams(free_node, &dptr_out)); + REQUIRE(A_d == static_cast(dptr_out)); + + // Instantiate graph + HIP_CHECK(hipGraphInstantiate(&graph_exec, graph, nullptr, nullptr, 0)); + HIP_CHECK(hipGraphLaunch(graph_exec, stream)); + HIP_CHECK(hipStreamSynchronize(stream)); + + ArrayFindIfNot(A_h.host_ptr(), fill_value, element_count); + + HIP_CHECK(hipGraphExecDestroy(graph_exec)); + HIP_CHECK(hipGraphDestroy(graph)); +} + +/** + * Test Description + * ------------------------ + *  - Negative Test for API - hipGraphMemAllocNodeGetParams + * 1) Pass MemAllocNode as nullptr + * 2) Pass MemAllocNode as empty node + * 3) Pass params_out as nullptr + * 4) Pass MemFreeNode inplace of MemAllocNode in 1st arguments + * - Negative Test for API - hipGraphMemFreeNodeGetParams + * 1) Pass MemFreeNode as nullptr + * 2) Pass MemFreeNode as empty node + * 3) Pass free pointer as nullptr + * 4) Pass free pointer as invalid pointer + * 5) Pass MemAllocNode inplace of MemFreeNode in 1st arguments + * Test source + * ------------------------ + *  - /unit/graph/hipGraphMemAllocNodeGetParams.cc + * Test requirements + * ------------------------ + *  - HIP_VERSION >= 6.0 + */ TEST_CASE("Unit_hipGraphMem_Alloc_Free_NodeGetParams_Negative") { hipError_t ret; constexpr size_t N = 1024 * 1024; @@ -303,7 +377,7 @@ TEST_CASE("Unit_hipGraphMem_Alloc_Free_NodeGetParams_Negative") { params_in.poolProps.location.type = hipMemLocationTypeDevice; HIP_CHECK(hipGraphAddMemAllocNode(&allocNodeA, graph, NULL, 0, ¶ms_in)); - int *A_d = reinterpret_cast(params_in.dptr); + int* A_d = reinterpret_cast(params_in.dptr); REQUIRE(A_d != nullptr); HIP_CHECK(hipGraphAddMemFreeNode(&freeNodeA, graph, &allocNodeA, 1, A_d)); @@ -328,14 +402,12 @@ TEST_CASE("Unit_hipGraphMem_Alloc_Free_NodeGetParams_Negative") { int temp[] = {0}; SECTION("Pass MemFreeNode as nullptr") { - ret = hipGraphMemFreeNodeGetParams(nullptr, - reinterpret_cast(temp)); + ret = hipGraphMemFreeNodeGetParams(nullptr, reinterpret_cast(temp)); REQUIRE(hipErrorInvalidValue == ret); } SECTION("Pass MemFreeNode as empty node") { hipGraphNode_t freeNode_empty{}; - ret = hipGraphMemFreeNodeGetParams(freeNode_empty, - reinterpret_cast(temp)); + ret = hipGraphMemFreeNodeGetParams(freeNode_empty, reinterpret_cast(temp)); REQUIRE(hipErrorInvalidValue == ret); } SECTION("Pass free pointer as nullptr") { @@ -343,8 +415,7 @@ TEST_CASE("Unit_hipGraphMem_Alloc_Free_NodeGetParams_Negative") { REQUIRE(hipErrorInvalidValue == ret); } SECTION("Pass MemAllocNode inplace of MemFreeNode in 1st arguments") { - ret = hipGraphMemFreeNodeGetParams(allocNodeA, - reinterpret_cast(temp)); + ret = hipGraphMemFreeNodeGetParams(allocNodeA, reinterpret_cast(temp)); REQUIRE(hipErrorInvalidValue == ret); } diff --git a/catch/unit/hip_specific/CMakeLists.txt b/catch/unit/hip_specific/CMakeLists.txt new file mode 100644 index 0000000000..cb0601e430 --- /dev/null +++ b/catch/unit/hip_specific/CMakeLists.txt @@ -0,0 +1,37 @@ +# Copyright (c) 2022 Advanced Micro Devices, Inc. All Rights Reserved. +# +# 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. + +# Common Tests - Test independent of all platforms + +set(TEST_SRC + hip_hc_8pk.cc +) + +hip_add_exe_to_target(NAME HipSpecificTest + TEST_SRC ${TEST_SRC} + TEST_TARGET_NAME build_tests + LINKER_LIBS hiprtc + PROPERTY CXX_STANDARD 17) + +# This test fails in PSDB +#add_test(NAME Unit_Device__hip_hc_8pk_Negative +# COMMAND python3 ${CMAKE_CURRENT_SOURCE_DIR}/../compileAndCaptureOutput.py +# ${CMAKE_CURRENT_SOURCE_DIR} ${HIP_PLATFORM} ${HIP_PATH} +# hip_hc_8pk_negative_kernels.cc 90) diff --git a/catch/unit/hip_specific/hip_hc_8pk.cc b/catch/unit/hip_specific/hip_hc_8pk.cc new file mode 100644 index 0000000000..a3d996fee4 --- /dev/null +++ b/catch/unit/hip_specific/hip_hc_8pk.cc @@ -0,0 +1,172 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. +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 "hip_hc_8pk_negative_kernels_rtc.hh" + +#include +#include + + +/** + * @addtogroup hip_hc_8pk hip_hc_8pk + * @{ + * @ingroup DeviceLanguageTest + */ + +__global__ void __hip_hc_add8pk_kernel(char4* out, char4 in1, char4 in2) { + out[0] = __hip_hc_add8pk(in1, in2); +} + +/** + * Test Description + * ------------------------ + * - Sanity test for `__hip_hc_add8pk(in1, in2)`. + * + * Test source + * ------------------------ + * - unit/hip_specific/hip_hc_8pk.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_Device__hip_hc_add8pk_Sanity_Positive") { + const char input1[] = {-0x70, -0x50, -0x30, -0x0f, 0x0, 0x01, 0x10, 0x20, 0x70, 0x7f}; + const char input2[] = {-0x05, -0x11, -0x20, -0x03, 0x0, 0x30, 0x05, 0x33, 0x0f, 0x7a}; + const char reference[] = {-0x75, -0x61, -0x50, -0x12, 0x0, 0x31, 0x15, 0x53, 0x7f, -0x07}; + LinearAllocGuard out(LinearAllocs::hipMallocManaged, sizeof(char4)); + + for (int i = 0; i < 10; ++i) { + __hip_hc_add8pk_kernel<<<1, 1>>>(out.ptr(), make_char4(0, 0, 0, input1[i]), + make_char4(0, 0, 0, input2[i])); + HIP_CHECK(hipDeviceSynchronize()); + REQUIRE(out.ptr()[0].x == 0); + REQUIRE(out.ptr()[0].y == 0); + REQUIRE(out.ptr()[0].z == 0); + REQUIRE(out.ptr()[0].w == reference[i]); + } +} + +__global__ void __hip_hc_sub8pk_kernel(char4* out, char4 in1, char4 in2) { + out[0] = __hip_hc_sub8pk(in1, in2); +} + +/** + * Test Description + * ------------------------ + * - Sanity test for `__hip_hc_sub8pk(in1, in2)`. + * + * Test source + * ------------------------ + * - unit/hip_specific/hip_hc_8pk.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_Device__hip_hc_sub8pk_Sanity_Positive") { + const char input1[] = {-0x70, -0x50, -0x30, -0x0f, 0x0, 0x30, 0x10, 0x33, 0x70, 0x7a}; + const char input2[] = {-0x05, -0x11, -0x20, -0x03, 0x0, 0x01, 0x05, 0x20, 0x0f, 0x7f}; + const char reference[] = {-0x6b, -0x3f, -0x10, -0x0c, 0x0, 0x2f, 0x0b, 0x13, 0x61, -0x05}; + LinearAllocGuard out(LinearAllocs::hipMallocManaged, sizeof(char4)); + + for (int i = 0; i < 10; ++i) { + __hip_hc_sub8pk_kernel<<<1, 1>>>(out.ptr(), make_char4(0, 0, 0, input1[i]), + make_char4(0, 0, 0, input2[i])); + HIP_CHECK(hipDeviceSynchronize()); + REQUIRE(out.ptr()[0].x == 0); + REQUIRE(out.ptr()[0].y == 0); + REQUIRE(out.ptr()[0].z == 0); + REQUIRE(out.ptr()[0].w == reference[i]); + } +} + +__global__ void __hip_hc_mul8pk_kernel(char4* out, char4 in1, char4 in2) { + out[0] = __hip_hc_mul8pk(in1, in2); +} + +/** + * Test Description + * ------------------------ + * - Sanity test for `__hip_hc_mul8pk(in1, in2)`. + * + * Test source + * ------------------------ + * - unit/hip_specific/hip_hc_8pk.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_Device__hip_hc_mul8pk_Sanity_Positive") { + const char input1[] = {-0x70, -0x50, -0x30, -0x0f, 0x0, 0x01, 0x10, 0x20, 0x70, 0x7f}; + const char input2[] = {0x05, -0x11, 0x22, -0x03, 0x0, 0x30, 0x05, 0x33, 0x0f, 0x7a}; + const char reference[] = {-0x30, 0x50, -0x60, 0x2d, 0x0, 0x30, 0x50, 0x60, -0x70, -0x7a}; + LinearAllocGuard out(LinearAllocs::hipMallocManaged, sizeof(char4)); + + for (int i = 0; i < 10; ++i) { + __hip_hc_mul8pk_kernel<<<1, 1>>>(out.ptr(), make_char4(0, 0, 0, input1[i]), + make_char4(0, 0, 0, input2[i])); + HIP_CHECK(hipDeviceSynchronize()); + REQUIRE(out.ptr()[0].x == 0); + REQUIRE(out.ptr()[0].y == 0); + REQUIRE(out.ptr()[0].z == 0); + REQUIRE(out.ptr()[0].w == reference[i]); + } +} + +/** + * Test Description + * ------------------------ + * - RTCs kernels that pass combinations of arguments of invalid types for + * __hip_hc_8pk + * Test source + * ------------------------ + * - unit/hip_specific/hip_hc_8pk.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_Device__hip_hc_8pk_Negative_Parameters_RTC") { + hiprtcProgram program{}; + + const auto program_source = GENERATE(kHipHcAdd8pkBasic, kHipHcAdd8pkVector, kHipHcSub8pkBasic, + kHipHcSub8pkVector, kHipHcMul8pkBasic, kHipHcMul8pkVector); + + HIPRTC_CHECK( + hiprtcCreateProgram(&program, program_source, "hip_hc_8pk_negative.cc", 0, nullptr, nullptr)); + hiprtcResult result{hiprtcCompileProgram(program, 0, nullptr)}; + + // Get the compile log and count compiler error messages + size_t log_size{}; + HIPRTC_CHECK(hiprtcGetProgramLogSize(program, &log_size)); + std::string log(log_size, ' '); + HIPRTC_CHECK(hiprtcGetProgramLog(program, log.data())); + int error_count{0}; + + int expected_error_count{15}; + std::string error_message{"error:"}; + + size_t n_pos = log.find(error_message, 0); + while (n_pos != std::string::npos) { + ++error_count; + n_pos = log.find(error_message, n_pos + 1); + } + + HIPRTC_CHECK(hiprtcDestroyProgram(&program)); + HIPRTC_CHECK_ERROR(result, HIPRTC_ERROR_COMPILATION); + REQUIRE(error_count == expected_error_count); +} \ No newline at end of file diff --git a/catch/unit/hip_specific/hip_hc_8pk_negative_kernels.cc b/catch/unit/hip_specific/hip_hc_8pk_negative_kernels.cc new file mode 100644 index 0000000000..7b8195d29d --- /dev/null +++ b/catch/unit/hip_specific/hip_hc_8pk_negative_kernels.cc @@ -0,0 +1,121 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. +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 + +struct Dummy { + __device__ Dummy() {} + __device__ ~Dummy() {} +}; + +#define NEGATIVE_KERNELS_SHELL(func_name) \ +__global__ void func_name##_char_n1(char4* out, char in1, char4 in2) { \ + *out = func_name(in1, in2); \ +} \ +__global__ void func_name##_char_n2(char4* out, char4 in1, char in2) { \ + *out = func_name(in1, in2); \ +} \ +__global__ void func_name##_char_n3(char* out, char4 in1, char4 in2) { \ + *out = func_name(in1, in2); \ +} \ +__global__ void func_name##_int_n1(char4* out, int in1, char4 in2) { \ + *out = func_name(in1, in2); \ +} \ +__global__ void func_name##_int_n2(char4* out, char4 in1, int in2) { \ + *out = func_name(in1, in2); \ +} \ +__global__ void func_name##_int_n3(int* out, char4 in1, char4 in2) { \ + *out = func_name(in1, in2); \ +} \ +__global__ void func_name##_long_n1(char4* out, long in1, char4 in2) { \ + *out = func_name(in1, in2); \ +} \ +__global__ void func_name##_long_n2(char4* out, char4 in1, long in2) { \ + *out = func_name(in1, in2); \ +} \ +__global__ void func_name##_long_n3(long* out, char4 in1, char4 in2) { \ + *out = func_name(in1, in2); \ +} \ +__global__ void func_name##_float_n1(char4* out, float in1, char4 in2) { \ + *out = func_name(in1, in2); \ +} \ +__global__ void func_name##_float_n2(char4* out, char4 in1, float in2) { \ + *out = func_name(in1, in2); \ +} \ +__global__ void func_name##_float_n3(float* out, char4 in1, char4 in2) { \ + *out = func_name(in1, in2); \ +} \ +__global__ void func_name##_char4_n1(char4* out, char4 in1, char4 in2) { \ + out = func_name(in1, in2); \ +} \ +__global__ void func_name##_char4_n2(char4* out, char4 in1, char4 in2) { \ + *out = func_name(&in1, in2); \ +} \ +__global__ void func_name##_char4_n3(char4* out, char4 in1, char4 in2) { \ + *out = func_name(in1, &in2); \ +} \ +__global__ void func_name##_char2_n1(char4* out, char2 in1, char4 in2) { \ + *out = func_name(in1, in2); \ +} \ +__global__ void func_name##_char2_n2(char4* out, char4 in1, char2 in2) { \ + *out = func_name(in1, in2); \ +} \ +__global__ void func_name##_char2_n3(char2* out, char4 in1, char4 in2) { \ + *out = func_name(in1, in2); \ +} \ +__global__ void func_name##_int4_n1(char4* out, int4 in1, char4 in2) { \ + *out = func_name(in1, in2); \ +} \ +__global__ void func_name##_int4_n2(char4* out, char4 in1, int4 in2) { \ + *out = func_name(in1, in2); \ +} \ +__global__ void func_name##_int4_n3(int4* out, char4 in1, char4 in2) { \ + *out = func_name(in1, in2); \ +} \ +__global__ void func_name##_long4_n1(char4* out, long4 in1, char4 in2) { \ + *out = func_name(in1, in2); \ +} \ +__global__ void func_name##_long4_n2(char4* out, char4 in1, long4 in2) { \ + *out = func_name(in1, in2); \ +} \ +__global__ void func_name##_long4_n3(long4* out, char4 in1, char4 in2) { \ + *out = func_name(in1, in2); \ +} \ +__global__ void func_name##_float4_n1(char4* out, float4 in1, char4 in2) { \ + *out = func_name(in1, in2); \ +} \ +__global__ void func_name##_float4_n2(char4* out, char4 in1, float4 in2) { \ + *out = func_name(in1, in2); \ +} \ +__global__ void func_name##_float4_n3(float4* out, char4 in1, char4 in2) { \ + *out = func_name(in1, in2); \ +} \ +__global__ void func_name##_dummy_n1(char4* out, Dummy in1, char4 in2) { \ + *out = func_name(in1, in2); \ +} \ +__global__ void func_name##_dummy_n2(char4* out, char4 in1, Dummy in2) { \ + *out = func_name(in1, in2); \ +} \ +__global__ void func_name##_dummy_n3(Dummy* out, char4 in1, char4 in2) { \ + *out = func_name(in1, in2); \ +} + +NEGATIVE_KERNELS_SHELL(__hip_hc_add8pk) +NEGATIVE_KERNELS_SHELL(__hip_hc_sub8pk) +NEGATIVE_KERNELS_SHELL(__hip_hc_mul8pk) \ No newline at end of file diff --git a/catch/unit/hip_specific/hip_hc_8pk_negative_kernels_rtc.hh b/catch/unit/hip_specific/hip_hc_8pk_negative_kernels_rtc.hh new file mode 100644 index 0000000000..a2e89d8802 --- /dev/null +++ b/catch/unit/hip_specific/hip_hc_8pk_negative_kernels_rtc.hh @@ -0,0 +1,149 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. +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. +*/ + +#pragma once + +static constexpr auto kHipHcAdd8pkBasic{ + R"( + struct Dummy { + __device__ Dummy() {} + __device__ ~Dummy() {} + }; + + __global__ void hip_hc_add8pk_char_n1(char4* out, char in1, char4 in2) { *out = __hip_hc_add8pk(in1, in2); } + __global__ void hip_hc_add8pk_char_n2(char4* out, char4 in1, char in2) { *out = __hip_hc_add8pk(in1, in2); } + __global__ void hip_hc_add8pk_char_n3(char* out, char4 in1, char4 in2) { *out = __hip_hc_add8pk(in1, in2); } + __global__ void hip_hc_add8pk_int_n1(char4* out, int in1, char4 in2) { *out = __hip_hc_add8pk(in1, in2); } + __global__ void hip_hc_add8pk_int_n2(char4* out, char4 in1, int in2) { *out = __hip_hc_add8pk(in1, in2); } + __global__ void hip_hc_add8pk_int_n3(int* out, char4 in1, char4 in2) { *out = __hip_hc_add8pk(in1, in2); } + __global__ void hip_hc_add8pk_long_n1(char4* out, long in1, char4 in2) { *out = __hip_hc_add8pk(in1, in2); } + __global__ void hip_hc_add8pk_long_n2(char4* out, char4 in1, long in2) { *out = __hip_hc_add8pk(in1, in2); } + __global__ void hip_hc_add8pk_long_n3(long* out, char4 in1, char4 in2) { *out = __hip_hc_add8pk(in1, in2); } + __global__ void hip_hc_add8pk_float_n1(char4* out, float in1, char4 in2) { *out = __hip_hc_add8pk(in1, in2); } + __global__ void hip_hc_add8pk_float_n2(char4* out, char4 in1, float in2) { *out = __hip_hc_add8pk(in1, in2); } + __global__ void hip_hc_add8pk_float_n3(float* out, char4 in1, char4 in2) { *out = __hip_hc_add8pk(in1, in2); } + __global__ void hip_hc_add8pk_dummy_n1(char4* out, Dummy in1, char4 in2) { *out = __hip_hc_add8pk(in1, in2); } + __global__ void hip_hc_add8pk_dummy_n2(char4* out, char4 in1, Dummy in2) { *out = __hip_hc_add8pk(in1, in2); } + __global__ void hip_hc_add8pk_dummy_n3(Dummy* out, char4 in1, char4 in2) { *out = __hip_hc_add8pk(in1, in2); } + )"}; + +static constexpr auto kHipHcAdd8pkVector{ + R"( + __global__ void hip_hc_add8pk_char4_n1(char4* out, char4 in1, char4 in2) { out = __hip_hc_add8pk(in1, in2); } + __global__ void hip_hc_add8pk_char4_n2(char4* out, char4 in1, char4 in2) { *out = __hip_hc_add8pk(&in1, in2); } + __global__ void hip_hc_add8pk_char4_n3(char4* out, char4 in1, char4 in2) { *out = __hip_hc_add8pk(in1, &in2); } + __global__ void hip_hc_add8pk_char2_n1(char4* out, char2 in1, char4 in2) { *out = __hip_hc_add8pk(in1, in2); } + __global__ void hip_hc_add8pk_char2_n2(char4* out, char4 in1, char2 in2) { *out = __hip_hc_add8pk(in1, in2); } + __global__ void hip_hc_add8pk_char2_n3(char2* out, char4 in1, char4 in2) { *out = __hip_hc_add8pk(in1, in2); } + __global__ void hip_hc_add8pk_int4_n1(char4* out, int4 in1, char4 in2) { *out = __hip_hc_add8pk(in1, in2); } + __global__ void hip_hc_add8pk_int4_n2(char4* out, char4 in1, int4 in2) { *out = __hip_hc_add8pk(in1, in2); } + __global__ void hip_hc_add8pk_int4_n3(int4* out, char4 in1, char4 in2) { *out = __hip_hc_add8pk(in1, in2); } + __global__ void hip_hc_add8pk_long4_n1(char4* out, long4 in1, char4 in2) { *out = __hip_hc_add8pk(in1, in2); } + __global__ void hip_hc_add8pk_long4_n2(char4* out, char4 in1, long4 in2) { *out = __hip_hc_add8pk(in1, in2); } + __global__ void hip_hc_add8pk_long4_n3(long4* out, char4 in1, char4 in2) { *out = __hip_hc_add8pk(in1, in2); } + __global__ void hip_hc_add8pk_float4_n1(char4* out, float4 in1, char4 in2) { *out = __hip_hc_add8pk(in1, in2); } + __global__ void hip_hc_add8pk_float4_n2(char4* out, char4 in1, float4 in2) { *out = __hip_hc_add8pk(in1, in2); } + __global__ void hip_hc_add8pk_float4_n3(float4* out, char4 in1, char4 in2) { *out = __hip_hc_add8pk(in1, in2); } + )"}; + +static constexpr auto kHipHcSub8pkBasic{ + R"( + struct Dummy { + __device__ Dummy() {} + __device__ ~Dummy() {} + }; + + __global__ void hip_hc_sub8pk_char_n1(char4* out, char in1, char4 in2) { *out = __hip_hc_sub8pk(in1, in2); } + __global__ void hip_hc_sub8pk_char_n2(char4* out, char4 in1, char in2) { *out = __hip_hc_sub8pk(in1, in2); } + __global__ void hip_hc_sub8pk_char_n3(char* out, char4 in1, char4 in2) { *out = __hip_hc_sub8pk(in1, in2); } + __global__ void hip_hc_sub8pk_int_n1(char4* out, int in1, char4 in2) { *out = __hip_hc_sub8pk(in1, in2); } + __global__ void hip_hc_sub8pk_int_n2(char4* out, char4 in1, int in2) { *out = __hip_hc_sub8pk(in1, in2); } + __global__ void hip_hc_sub8pk_int_n3(int* out, char4 in1, char4 in2) { *out = __hip_hc_sub8pk(in1, in2); } + __global__ void hip_hc_sub8pk_long_n1(char4* out, long in1, char4 in2) { *out = __hip_hc_sub8pk(in1, in2); } + __global__ void hip_hc_sub8pk_long_n2(char4* out, char4 in1, long in2) { *out = __hip_hc_sub8pk(in1, in2); } + __global__ void hip_hc_sub8pk_long_n3(long* out, char4 in1, char4 in2) { *out = __hip_hc_sub8pk(in1, in2); } + __global__ void hip_hc_sub8pk_float_n1(char4* out, float in1, char4 in2) { *out = __hip_hc_sub8pk(in1, in2); } + __global__ void hip_hc_sub8pk_float_n2(char4* out, char4 in1, float in2) { *out = __hip_hc_sub8pk(in1, in2); } + __global__ void hip_hc_sub8pk_float_n3(float* out, char4 in1, char4 in2) { *out = __hip_hc_sub8pk(in1, in2); } + __global__ void hip_hc_sub8pk_dummy_n1(char4* out, Dummy in1, char4 in2) { *out = __hip_hc_sub8pk(in1, in2); } + __global__ void hip_hc_sub8pk_dummy_n2(char4* out, char4 in1, Dummy in2) { *out = __hip_hc_sub8pk(in1, in2); } + __global__ void hip_hc_sub8pk_dummy_n3(Dummy* out, char4 in1, char4 in2) { *out = __hip_hc_sub8pk(in1, in2); } + )"}; + +static constexpr auto kHipHcSub8pkVector{ + R"( + __global__ void hip_hc_sub8pk_char4_n1(char4* out, char4 in1, char4 in2) { out = __hip_hc_sub8pk(in1, in2); } + __global__ void hip_hc_sub8pk_char4_n2(char4* out, char4 in1, char4 in2) { *out = __hip_hc_sub8pk(&in1, in2); } + __global__ void hip_hc_sub8pk_char4_n3(char4* out, char4 in1, char4 in2) { *out = __hip_hc_sub8pk(in1, &in2); } + __global__ void hip_hc_sub8pk_char2_n1(char4* out, char2 in1, char4 in2) { *out = __hip_hc_sub8pk(in1, in2); } + __global__ void hip_hc_sub8pk_char2_n2(char4* out, char4 in1, char2 in2) { *out = __hip_hc_sub8pk(in1, in2); } + __global__ void hip_hc_sub8pk_char2_n3(char2* out, char4 in1, char4 in2) { *out = __hip_hc_sub8pk(in1, in2); } + __global__ void hip_hc_sub8pk_int4_n1(char4* out, int4 in1, char4 in2) { *out = __hip_hc_sub8pk(in1, in2); } + __global__ void hip_hc_sub8pk_int4_n2(char4* out, char4 in1, int4 in2) { *out = __hip_hc_sub8pk(in1, in2); } + __global__ void hip_hc_sub8pk_int4_n3(int4* out, char4 in1, char4 in2) { *out = __hip_hc_sub8pk(in1, in2); } + __global__ void hip_hc_sub8pk_long4_n1(char4* out, long4 in1, char4 in2) { *out = __hip_hc_sub8pk(in1, in2); } + __global__ void hip_hc_sub8pk_long4_n2(char4* out, char4 in1, long4 in2) { *out = __hip_hc_sub8pk(in1, in2); } + __global__ void hip_hc_sub8pk_long4_n3(long4* out, char4 in1, char4 in2) { *out = __hip_hc_sub8pk(in1, in2); } + __global__ void hip_hc_sub8pk_float4_n1(char4* out, float4 in1, char4 in2) { *out = __hip_hc_sub8pk(in1, in2); } + __global__ void hip_hc_sub8pk_float4_n2(char4* out, char4 in1, float4 in2) { *out = __hip_hc_sub8pk(in1, in2); } + __global__ void hip_hc_sub8pk_float4_n3(float4* out, char4 in1, char4 in2) { *out = __hip_hc_sub8pk(in1, in2); } + )"}; + +static constexpr auto kHipHcMul8pkBasic{ + R"( + struct Dummy { + __device__ Dummy() {} + __device__ ~Dummy() {} + }; + + __global__ void hip_hc_mul8pk_char_n1(char4* out, char in1, char4 in2) { *out = __hip_hc_mul8pk(in1, in2); } + __global__ void hip_hc_mul8pk_char_n2(char4* out, char4 in1, char in2) { *out = __hip_hc_mul8pk(in1, in2); } + __global__ void hip_hc_mul8pk_char_n3(char* out, char4 in1, char4 in2) { *out = __hip_hc_mul8pk(in1, in2); } + __global__ void hip_hc_mul8pk_int_n1(char4* out, int in1, char4 in2) { *out = __hip_hc_mul8pk(in1, in2); } + __global__ void hip_hc_mul8pk_int_n2(char4* out, char4 in1, int in2) { *out = __hip_hc_mul8pk(in1, in2); } + __global__ void hip_hc_mul8pk_int_n3(int* out, char4 in1, char4 in2) { *out = __hip_hc_mul8pk(in1, in2); } + __global__ void hip_hc_mul8pk_long_n1(char4* out, long in1, char4 in2) { *out = __hip_hc_mul8pk(in1, in2); } + __global__ void hip_hc_mul8pk_long_n2(char4* out, char4 in1, long in2) { *out = __hip_hc_mul8pk(in1, in2); } + __global__ void hip_hc_mul8pk_long_n3(long* out, char4 in1, char4 in2) { *out = __hip_hc_mul8pk(in1, in2); } + __global__ void hip_hc_mul8pk_float_n1(char4* out, float in1, char4 in2) { *out = __hip_hc_mul8pk(in1, in2); } + __global__ void hip_hc_mul8pk_float_n2(char4* out, char4 in1, float in2) { *out = __hip_hc_mul8pk(in1, in2); } + __global__ void hip_hc_mul8pk_float_n3(float* out, char4 in1, char4 in2) { *out = __hip_hc_mul8pk(in1, in2); } + __global__ void hip_hc_mul8pk_dummy_n1(char4* out, Dummy in1, char4 in2) { *out = __hip_hc_mul8pk(in1, in2); } + __global__ void hip_hc_mul8pk_dummy_n2(char4* out, char4 in1, Dummy in2) { *out = __hip_hc_mul8pk(in1, in2); } + __global__ void hip_hc_mul8pk_dummy_n3(Dummy* out, char4 in1, char4 in2) { *out = __hip_hc_mul8pk(in1, in2); } + )"}; + +static constexpr auto kHipHcMul8pkVector{ + R"( + __global__ void hip_hc_mul8pk_char4_n1(char4* out, char4 in1, char4 in2) { out = __hip_hc_mul8pk(in1, in2); } + __global__ void hip_hc_mul8pk_char4_n2(char4* out, char4 in1, char4 in2) { *out = __hip_hc_mul8pk(&in1, in2); } + __global__ void hip_hc_mul8pk_char4_n3(char4* out, char4 in1, char4 in2) { *out = __hip_hc_mul8pk(in1, &in2); } + __global__ void hip_hc_mul8pk_char2_n1(char4* out, char2 in1, char4 in2) { *out = __hip_hc_mul8pk(in1, in2); } + __global__ void hip_hc_mul8pk_char2_n2(char4* out, char4 in1, char2 in2) { *out = __hip_hc_mul8pk(in1, in2); } + __global__ void hip_hc_mul8pk_char2_n3(char2* out, char4 in1, char4 in2) { *out = __hip_hc_mul8pk(in1, in2); } + __global__ void hip_hc_mul8pk_int4_n1(char4* out, int4 in1, char4 in2) { *out = __hip_hc_mul8pk(in1, in2); } + __global__ void hip_hc_mul8pk_int4_n2(char4* out, char4 in1, int4 in2) { *out = __hip_hc_mul8pk(in1, in2); } + __global__ void hip_hc_mul8pk_int4_n3(int4* out, char4 in1, char4 in2) { *out = __hip_hc_mul8pk(in1, in2); } + __global__ void hip_hc_mul8pk_long4_n1(char4* out, long4 in1, char4 in2) { *out = __hip_hc_mul8pk(in1, in2); } + __global__ void hip_hc_mul8pk_long4_n2(char4* out, char4 in1, long4 in2) { *out = __hip_hc_mul8pk(in1, in2); } + __global__ void hip_hc_mul8pk_long4_n3(long4* out, char4 in1, char4 in2) { *out = __hip_hc_mul8pk(in1, in2); } + __global__ void hip_hc_mul8pk_float4_n1(char4* out, float4 in1, char4 in2) { *out = __hip_hc_mul8pk(in1, in2); } + __global__ void hip_hc_mul8pk_float4_n2(char4* out, char4 in1, float4 in2) { *out = __hip_hc_mul8pk(in1, in2); } + __global__ void hip_hc_mul8pk_float4_n3(float4* out, char4 in1, char4 in2) { *out = __hip_hc_mul8pk(in1, in2); } + )"}; \ No newline at end of file diff --git a/catch/unit/memory/CMakeLists.txt b/catch/unit/memory/CMakeLists.txt index 143da641de..09e7277aac 100644 --- a/catch/unit/memory/CMakeLists.txt +++ b/catch/unit/memory/CMakeLists.txt @@ -107,6 +107,12 @@ set(TEST_SRC hipMemcpyFromSymbol.cc hipPtrGetAttribute.cc hipMemPoolApi.cc + hipMemPoolSetGetAccess.cc + hipMemPoolSetGetAttribute.cc + hipMemPoolCreate.cc + hipMemPoolDestroy.cc + hipMemPoolTrimTo.cc + hipMallocFromPoolAsync.cc hipMemcpyPeer.cc hipMemcpyPeer_old.cc hipMemcpyPeerAsync.cc @@ -151,7 +157,9 @@ set(TEST_SRC hipStreamAttachMemAsync.cc hipMemRangeGetAttributes_old.cc hipMemGetAddressRange.cc - hipArrayGetDescriptor.cc) + hipArrayGetDescriptor.cc + hipMallocMipmappedArray.cc + hipFreeMipmappedArray.cc) set(NOT_FOR_MI200_AND_ABOVE_TEST hipMallocArray.cc hipArrayCreate.cc) # tests not for MI200+ set(MI200_AND_ABOVE_TARGETS gfx90a gfx940 gfx941 gfx942) diff --git a/catch/unit/memory/hipFreeAsync.cc b/catch/unit/memory/hipFreeAsync.cc index c7fcd0b179..eb8c5b50de 100644 --- a/catch/unit/memory/hipFreeAsync.cc +++ b/catch/unit/memory/hipFreeAsync.cc @@ -21,24 +21,59 @@ THE SOFTWARE. */ #include +#include +#include -TEST_CASE("Unit_hipFreeAsync_negative") { - HIP_CHECK(hipSetDevice(0)); - void* p = nullptr; - hipStream_t stream{nullptr}; - HIP_CHECK(hipStreamCreate(&stream)); +/** + * @addtogroup hipFreeAsync hipFreeAsync + * @{ + * @ingroup StreamOTest + * `hipFreeAsync(void* dev_ptr, hipStream_t stream)` + * - Frees memory with stream ordered semantics + */ - SECTION("dev_ptr is nullptr") { REQUIRE(hipFreeAsync(nullptr, stream) != hipSuccess); } - SECTION("invalid stream handle") { - HIP_CHECK(hipMallocAsync(static_cast(&p), 100, stream)); - HIP_CHECK(hipStreamSynchronize(stream)); - hipError_t error = hipFreeAsync(p, reinterpret_cast(-1)); - HIP_CHECK(hipFreeAsync(p, stream)); - HIP_CHECK(hipStreamSynchronize(stream)); - REQUIRE(error != hipSuccess); +/** + * Test Description + * ------------------------ + * - Test to verify hipFreeAsync behavior with invalid arguments: + * -# Nullptr dev_ptr + * -# Invalid stream handle + * -# Double hipFreeAsync + * + * Test source + * ------------------------ + * - /unit/memory/hipFreeAsync.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 6.0 + */ +TEST_CASE("Unit_hipFreeAsync_Negative_Parameters") { + int device_id = 0; + HIP_CHECK(hipSetDevice(device_id)); + + int* p = nullptr; + size_t alloc_size = 1024; + StreamGuard stream(Streams::created); + + SECTION("dev_ptr is nullptr") { + HIP_CHECK_ERROR(hipFreeAsync(nullptr, stream.stream()), hipErrorInvalidValue); } - HIP_CHECK(hipStreamSynchronize(stream)); - HIP_CHECK(hipStreamDestroy(stream)); + SECTION("Invalid stream handle") { + HIP_CHECK(hipMallocAsync(reinterpret_cast(&p), alloc_size, stream.stream())); + HIP_CHECK(hipStreamSynchronize(stream.stream())); + HIP_CHECK_ERROR(hipFreeAsync(p, reinterpret_cast(-1)), hipErrorInvalidHandle); + HIP_CHECK(hipFreeAsync(reinterpret_cast(p), stream.stream())); + HIP_CHECK(hipStreamSynchronize(stream.stream())); + } + + SECTION("Double free") { + HIP_CHECK(hipMallocAsync(reinterpret_cast(&p), alloc_size, stream.stream())); + HIP_CHECK(hipStreamSynchronize(stream.stream())); + HIP_CHECK(hipFreeAsync(reinterpret_cast(p), stream.stream())); + HIP_CHECK(hipStreamSynchronize(stream.stream())); + HIP_CHECK_ERROR(hipFreeAsync(reinterpret_cast(p), stream.stream()), + hipErrorInvalidValue); + } } diff --git a/catch/unit/memory/hipFreeMipmappedArray.cc b/catch/unit/memory/hipFreeMipmappedArray.cc new file mode 100644 index 0000000000..df6c4faee0 --- /dev/null +++ b/catch/unit/memory/hipFreeMipmappedArray.cc @@ -0,0 +1,126 @@ +/* +Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved. + +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 +#include "hipArrayCommon.hh" +#include "utils.hh" + +/* + * hipFreeMipmappedArray API test scenarios + * 1. Check that hipFreeMipmappedArray implicitly synchronises the device. + * 2. Perform multiple allocations and then call hipFreeMipmappedArray on each pointer concurrently (from unique + * threads) for different memory types and different allocation sizes. + * 3. Pass nullptr as argument and check that correct error code is returned. + * 4. Call hipFreeMipmappedArray twice on the same pointer and check that the implementation handles the second + * call correctly. + */ + +TEMPLATE_TEST_CASE("Unit_hipFreeMipmappedArrayImplicitSyncArray", "", char, float) { + hipMipmappedArray_t arrayPtr{}; + hipExtent extent{}; + hipChannelFormatDesc desc = hipCreateChannelDesc(); + +#if HT_AMD + const unsigned int flags = hipArrayDefault; +#else + const unsigned int flags = GENERATE(hipArrayDefault, hipArraySurfaceLoadStore); +#endif + + extent.width = GENERATE(64, 256, 1024); + extent.height = GENERATE(64, 256, 1024); + extent.depth = GENERATE(0, 64, 256, 1024); + + const unsigned int numLevels = GENERATE(1, 5, 7); + + HIP_CHECK(hipMallocMipmappedArray(&arrayPtr, &desc, extent, numLevels, flags)); + + LaunchDelayKernel(std::chrono::milliseconds{50}, nullptr); + // make sure device is busy + HIP_CHECK_ERROR(hipStreamQuery(nullptr), hipErrorNotReady); + HIP_CHECK(hipFreeMipmappedArray(arrayPtr)); + HIP_CHECK(hipStreamQuery(nullptr)); +} + +TEST_CASE("Unit_hipFreeMipmappedArray_Negative_Nullptr") { + HIP_CHECK_ERROR(hipFreeMipmappedArray(nullptr), hipErrorInvalidValue); +} + +TEST_CASE("Unit_hipFreeMipmappedArray_Negative_DoubleFree") { + hipMipmappedArray_t arrayPtr{}; + hipExtent extent{}; + hipChannelFormatDesc desc = hipCreateChannelDesc(); + +#if HT_AMD + const unsigned int flags = hipArrayDefault; +#else + const unsigned int flags = GENERATE(hipArrayDefault, hipArraySurfaceLoadStore); +#endif + + extent.width = GENERATE(64, 512, 1024); + extent.height = GENERATE(64, 512, 1024); + extent.depth = GENERATE(0, 64, 512, 1024); + + const unsigned int numLevels = GENERATE(1, 5, 7); + + HIP_CHECK(hipMallocMipmappedArray(&arrayPtr, &desc, extent, numLevels, flags)); + + HIP_CHECK(hipFreeMipmappedArray(arrayPtr)); + HIP_CHECK_ERROR(hipFreeMipmappedArray(arrayPtr), hipErrorContextIsDestroyed); +} + +TEMPLATE_TEST_CASE("Unit_hipFreeMipmappedArrayMultiTArray", "", char, int) { + constexpr size_t numAllocs = 10; + std::vector threads; + std::vector ptrs(numAllocs); + hipExtent extent{}; + hipChannelFormatDesc desc = hipCreateChannelDesc(); + + const unsigned int numLevels = GENERATE(1, 5, 7); + +#if HT_AMD + const unsigned int flags = hipArrayDefault; +#else + const unsigned int flags = GENERATE(hipArrayDefault, hipArraySurfaceLoadStore); +#endif + + extent.width = GENERATE(64, 256, 1024); + extent.height = GENERATE(64, 256, 1024); + extent.depth = GENERATE(0, 64, 256, 1024); + + for (auto& ptr : ptrs) { + HIP_CHECK(hipMallocMipmappedArray(&ptr, &desc, extent, numLevels, flags)); + } + + for (auto ptr : ptrs) { + threads.emplace_back(([ptr] { + HIP_CHECK_THREAD(hipFreeMipmappedArray(ptr)); + HIP_CHECK_THREAD(hipStreamQuery(nullptr)); + })); + } + + for (auto& t : threads) { + t.join(); + } + + HIP_CHECK_THREAD_FINALIZE(); +} diff --git a/catch/unit/memory/hipMalloc3D.cc b/catch/unit/memory/hipMalloc3D.cc index b5f12bade9..d3edad5a2f 100644 --- a/catch/unit/memory/hipMalloc3D.cc +++ b/catch/unit/memory/hipMalloc3D.cc @@ -69,7 +69,18 @@ TEST_CASE("Unit_hipMalloc3D_Basic") { size_t height{SMALL_SIZE}, depth{SMALL_SIZE}; hipPitchedPtr devPitchedPtr; hipExtent extent = make_hipExtent(width, height, depth); + size_t tot, avail, ptot, pavail; + HIP_CHECK(hipMemGetInfo(&pavail, &ptot)); + REQUIRE(hipMalloc3D(&devPitchedPtr, extent) == hipSuccess); + HIPCHECK(hipFree(devPitchedPtr.ptr)); + + HIP_CHECK(hipMemGetInfo(&avail, &tot)); + + if (pavail != avail) { + WARN("Memory leak of hipMalloc3D API in multithreaded scenario"); + REQUIRE(false); + } } /* diff --git a/catch/unit/memory/hipMallocAsync.cc b/catch/unit/memory/hipMallocAsync.cc index db74c99459..f9e1d8aa8a 100644 --- a/catch/unit/memory/hipMallocAsync.cc +++ b/catch/unit/memory/hipMallocAsync.cc @@ -17,31 +17,122 @@ THE SOFTWARE. */ -#include -#include +#include "mempool_common.hh" #include -TEST_CASE("Unit_hipMallocAsync_negative") { - HIP_CHECK(hipSetDevice(0)); +#pragma clang diagnostic ignored "-Wunused-parameter" - void* p = nullptr; - size_t max_size = std::numeric_limits::max(); - hipStream_t stream{nullptr}; - HIP_CHECK(hipStreamCreate(&stream)); +/** + * @addtogroup hipMallocAsync hipMallocAsync + * @{ + * @ingroup StreamOTest + * `hipMallocAsync(void** dev_ptr, size_t size, hipStream_t stream)` + * - Allocates memory with stream ordered semantics + */ - - SECTION("Device pointer is null") { REQUIRE(hipMallocAsync(nullptr, 100, stream) != hipSuccess); } - - SECTION("stream is invalid") { - REQUIRE(hipMallocAsync(static_cast(&p), 100, reinterpret_cast(-1)) != - hipSuccess); - } - - SECTION("out of memory") { - REQUIRE(hipMallocAsync(static_cast(&p), max_size, stream) != hipSuccess); - } - - HIP_CHECK(hipStreamSynchronize(stream)); - HIP_CHECK(hipStreamDestroy(stream)); +/** + * Test Description + * ------------------------ + * - Basic test to verify proper allocation and stream ordering of hipMallocAsync when one + * memory allocation is performed. + * Test source + * ------------------------ + * - /unit/memory/hipMallocAsync.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 6.0 + */ +TEST_CASE("Unit_hipMallocAsync_Basic_OneAlloc") { + MallocMemPoolAsync_OneAlloc( + [](void** dev_ptr, size_t size, hipMemPool_t mem_pool, hipStream_t stream) { + return hipMallocAsync(dev_ptr, size, stream); + }, + MemPools::dev_default); +} + +/** + * Test Description + * ------------------------ + * - Basic test to verify proper allocation and stream ordering of hipMallocAsync when two + * memory allocations are performed. + * Test source + * ------------------------ + * - /unit/memory/hipMallocAsync.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 6.0 + */ +TEST_CASE("Unit_hipMallocAsync_Basic_TwoAllocs") { + MallocMemPoolAsync_TwoAllocs( + [](void** dev_ptr, size_t size, hipMemPool_t mem_pool, hipStream_t stream) { + return hipMallocAsync(dev_ptr, size, stream); + }, + MemPools::dev_default); +} + +/** + * Test Description + * ------------------------ + * - Basic test to verify that memory allocated with hipMallocAsync can be properly reused. + * Test source + * ------------------------ + * - /unit/memory/hipMallocAsync.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 6.0 + */ +TEST_CASE("Unit_hipMallocAsync_Basic_Reuse") { + MallocMemPoolAsync_Reuse([](void** dev_ptr, size_t size, hipMemPool_t mem_pool, + hipStream_t stream) { return hipMallocAsync(dev_ptr, size, stream); }, + MemPools::dev_default); +} + + +/** + * Test Description + * ------------------------ + * - Test to verify hipMallocAsync behavior with invalid arguments: + * -# Nullptr dev_ptr + * -# Invalid stream handle + * -# Size is max size_t + * + * Test source + * ------------------------ + * - /unit/memory/hipMallocAsync.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 6.0 + */ +TEST_CASE("Unit_hipMallocAsync_Negative_Parameters") { + int device_id = 0; + HIP_CHECK(hipSetDevice(device_id)); + + int mem_pool_support = 0; + HIP_CHECK(hipDeviceGetAttribute(&mem_pool_support, hipDeviceAttributeMemoryPoolsSupported, 0)); + if (!mem_pool_support) { + SUCCEED("Runtime doesn't support Memory Pool. Skip the test case."); + return; + } + + int* p = nullptr; + size_t max_size = std::numeric_limits::max(); + size_t alloc_size = 1024; + MemPoolGuard mempool(MemPools::dev_default, device_id); + StreamGuard stream(Streams::created); + + SECTION("dev_ptr is nullptr") { + HIP_CHECK_ERROR(hipMallocAsync(nullptr, alloc_size, stream.stream()), hipErrorInvalidValue); + } + + SECTION("invalid stream handle") { + HIP_CHECK_ERROR( + hipMallocAsync(reinterpret_cast(&p), alloc_size, reinterpret_cast(-1)), + hipErrorInvalidHandle); + } + + SECTION("Size is max size_t") { + HIP_CHECK_ERROR(hipMallocAsync(reinterpret_cast(&p), max_size, stream.stream()), + hipErrorOutOfMemory); + } } diff --git a/catch/unit/memory/hipMallocFromPoolAsync.cc b/catch/unit/memory/hipMallocFromPoolAsync.cc new file mode 100644 index 0000000000..408e43374a --- /dev/null +++ b/catch/unit/memory/hipMallocFromPoolAsync.cc @@ -0,0 +1,149 @@ +/* + Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. + 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 WARRANNTY OF ANY KIND, EXPRESS OR + IMPLIED, INNCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANNY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER INN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR INN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + */ + +#include "mempool_common.hh" + +#include + +/** + * @addtogroup hipMallocFromPoolAsync hipMallocFromPoolAsync + * @{ + * @ingroup StreamOTest + * `hipMallocFromPoolAsync(void** dev_ptr, size_t size, hipMemPool_t mem_pool, hipStream_t stream)` + * - Allocates memory from a specified pool with stream ordered semantics + */ + + +/** + * Test Description + * ------------------------ + * - Basic test to verify proper allocation and stream ordering of hipMallocFromPoolAsync when one + * memory allocation is performed. + * Test source + * ------------------------ + * - /unit/memory/hipMallocFromPoolAsync.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 6.0 + */ +TEST_CASE("Unit_hipMallocFromPoolAsync_Basic_OneAlloc") { + MallocMemPoolAsync_OneAlloc( + [](void** dev_ptr, size_t size, hipMemPool_t mem_pool, hipStream_t stream) { + return hipMallocFromPoolAsync(dev_ptr, size, mem_pool, stream); + }, + MemPools::created); +} + + +/** + * Test Description + * ------------------------ + * - Basic test to verify proper allocation and stream ordering of hipMallocFromPoolAsync when two + * memory allocations are performed. + * Test source + * ------------------------ + * - /unit/memory/hipMallocFromPoolAsync.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 6.0 + */ +TEST_CASE("Unit_hipMallocFromPoolAsync_Basic_TwoAllocs") { + MallocMemPoolAsync_TwoAllocs( + [](void** dev_ptr, size_t size, hipMemPool_t mem_pool, hipStream_t stream) { + return hipMallocFromPoolAsync(dev_ptr, size, mem_pool, stream); + }, + MemPools::created); +} + +/** + * Test Description + * ------------------------ + * - Basic test to verify that memory allocated with hipMallocFromPoolAsync can be properly reused. + * Test source + * ------------------------ + * - /unit/memory/hipMallocFromPoolAsync.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 6.0 + */ +TEST_CASE("Unit_hipMallocFromPoolAsync_Basic_Reuse") { + MallocMemPoolAsync_Reuse( + [](void** dev_ptr, size_t size, hipMemPool_t mem_pool, hipStream_t stream) { + return hipMallocFromPoolAsync(dev_ptr, size, mem_pool, stream); + }, + MemPools::created); +} + +/** + * Test Description + * ------------------------ + * - Test to verify hipMallocFromPoolAsync behavior with invalid arguments: + * -# Nullptr dev_ptr + * -# Nullptr mem_pool + * -# Invalid stream handle + * -# Size is max size_t + * + * Test source + * ------------------------ + * - /unit/memory/hipMallocFromPoolAsync.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 6.0 + */ +TEST_CASE("Unit_hipMallocFromPoolAsync_Negative_Parameters") { + int device_id = 0; + HIP_CHECK(hipSetDevice(device_id)); + + int mem_pool_support = 0; + HIP_CHECK(hipDeviceGetAttribute(&mem_pool_support, hipDeviceAttributeMemoryPoolsSupported, 0)); + if (!mem_pool_support) { + SUCCEED("Runtime doesn't support Memory Pool. Skip the test case."); + return; + } + + void* p = nullptr; + size_t max_size = std::numeric_limits::max(); + size_t alloc_size = 1024; + MemPoolGuard mempool(MemPools::created, device_id); + StreamGuard stream(Streams::created); + + SECTION("dev_ptr is nullptr") { + HIP_CHECK_ERROR(hipMallocFromPoolAsync(nullptr, alloc_size, mempool.mempool(), stream.stream()), + hipErrorInvalidValue); + } + + SECTION("Mempool not created") { + hipMemPool_t dummy_mem_pool = nullptr; + HIP_CHECK_ERROR(hipMallocFromPoolAsync(static_cast(&p), alloc_size, dummy_mem_pool, + stream.stream()), + hipErrorInvalidValue); + } + + SECTION("Invalid stream handle") { + HIP_CHECK_ERROR(hipMallocFromPoolAsync(static_cast(&p), alloc_size, mempool.mempool(), + reinterpret_cast(-1)), + hipErrorInvalidHandle); + } + + SECTION("Size is max size_t") { + HIP_CHECK_ERROR(hipMallocFromPoolAsync(static_cast(&p), max_size, mempool.mempool(), + stream.stream()), + hipErrorOutOfMemory); + } +} diff --git a/catch/unit/memory/hipMallocMipmappedArray.cc b/catch/unit/memory/hipMallocMipmappedArray.cc new file mode 100644 index 0000000000..43da90a3ee --- /dev/null +++ b/catch/unit/memory/hipMallocMipmappedArray.cc @@ -0,0 +1,417 @@ +/* +Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved. +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. +*/ + +/* +hipMallocMipmappedArray API test scenarios +1. Basic Functionality +2. Negative Scenarios +3. Allocating Small and big chunk data +4. Multithreaded scenario +*/ + +#include +#include +#include "hipArrayCommon.hh" + +static constexpr auto ARRAY_SIZE{4}; +static constexpr auto BIG_ARRAY_SIZE{100}; +static constexpr auto ARRAY_LOOP{100}; + + +/* + * This API verifies memory allocations for small and + * bigger chunks of data. + * Two scenarios are verified in this API + * 1. SmallArray: Allocates ARRAY_SIZE in a loop and + * releases the memory and verifies the meminfo. + * 2. BigArray: Allocates BIG_ARRAY_SIZE in a loop and + * releases the memory and verifies the meminfo + * + * In both cases, the memory info before allocation and + * after releasing the memory should be the same + * + */ +static void MallocMipmappedArray_DiffSizes(int gpu) { + HIP_CHECK_THREAD(hipSetDevice(gpu)); + // Use of GENERATE in thead function causes random failures with multithread condition. + std::vector runs{ARRAY_SIZE, BIG_ARRAY_SIZE}; + for (const auto& size : runs) { + auto numLevelsLimit = floor(log2(size)); + for (unsigned int numLevels = 0; numLevels < numLevelsLimit; numLevels++) { + size_t width{size}, height{size}, depth{size}; + hipChannelFormatDesc channelDesc = hipCreateChannelDesc(); + std::array arr; + size_t pavail, avail, total; + HIP_CHECK_THREAD(hipMemGetInfo(&pavail, &total)); + + for (int i = 0; i < ARRAY_LOOP; i++) { + HIP_CHECK_THREAD(hipMallocMipmappedArray(&arr[i], &channelDesc, + make_hipExtent(width, height, depth), + (1 + numLevels), hipArrayDefault)); + } + for (int i = 0; i < ARRAY_LOOP; i++) { + HIP_CHECK_THREAD(hipFreeMipmappedArray(arr[i])); + } + + HIP_CHECK_THREAD(hipMemGetInfo(&avail, &total)); + REQUIRE_THREAD(pavail == avail); + } + } +} + +TEST_CASE("Unit_hipMallocMipmappedArray_DiffSizes") { + MallocMipmappedArray_DiffSizes(0); + HIP_CHECK_THREAD_FINALIZE(); +} + +/* +This testcase verifies the hipMallocMipmappedArray API in multithreaded +scenario by launching threads in parallel on multiple GPUs +and verifies the hipMallocMipmappedArray API with small and big chunks data +*/ +TEST_CASE("Unit_hipMallocMipmappedArray_MultiThread") { + std::vector threadlist; + int devCnt = 0; + devCnt = HipTest::getDeviceCount(); + for (int i = 0; i < devCnt; i++) { + threadlist.push_back(std::thread(MallocMipmappedArray_DiffSizes, i)); + } + + for (auto& t : threadlist) { + t.join(); + } + HIP_CHECK_THREAD_FINALIZE(); +} + +namespace { +void checkMipmappedArrayIsExpected(hipArray_t level_array, + const hipChannelFormatDesc& expected_desc, + const hipExtent& expected_extent, + const unsigned int expected_flags) { +// hipArrayGetInfo doesn't currently exist (EXSWCPHIPT-87) +#if HT_AMD + std::ignore = level_array; + std::ignore = expected_desc; + std::ignore = expected_extent; + std::ignore = expected_flags; +#else + cudaChannelFormatDesc queried_desc; + cudaExtent queried_extent; + unsigned int queried_flags; + + cudaArrayGetInfo(&queried_desc, &queried_extent, &queried_flags, level_array); + + REQUIRE(expected_desc.x == queried_desc.x); + REQUIRE(expected_desc.y == queried_desc.y); + REQUIRE(expected_desc.z == queried_desc.z); + REQUIRE(expected_desc.f == queried_desc.f); + + REQUIRE(expected_extent.width == queried_extent.width); + REQUIRE(expected_extent.height == queried_extent.height); + REQUIRE(expected_extent.depth == queried_extent.depth); + + REQUIRE(expected_flags == queried_flags); +#endif +} +} // namespace + +TEMPLATE_TEST_CASE("Unit_hipMallocMipmappedArray_happy", "", char, uint2, int4, short4, float) { + hipMipmappedArray_t array; + const auto desc = hipCreateChannelDesc(); +#if HT_AMD + const unsigned int flags = hipArrayDefault; +#else + const unsigned int flags = + GENERATE(hipArrayDefault, hipArraySurfaceLoadStore, hipArrayTextureGather); +#endif + constexpr size_t size = 64; + const unsigned int numLevels = GENERATE(1, 3, 5, 7); + + std::vector extents; + extents.reserve(3); + extents.push_back({size, size, 0}); // 2D array + if (flags != hipArrayTextureGather) { + extents.push_back({size, 0, 0}); // 1D array + extents.push_back({size, size, size}); // 3D array + }; + + for (const auto extent : extents) { + CAPTURE(flags, extent.width, extent.height, extent.depth); + + HIP_CHECK(hipMallocMipmappedArray(&array, &desc, extent, numLevels, flags)); + hipArray_t hipArray = nullptr; + HIP_CHECK(hipGetMipmappedArrayLevel(&hipArray, array, 0)); + checkMipmappedArrayIsExpected(hipArray, desc, extent, flags); + HIP_CHECK(hipFreeMipmappedArray(array)); + } +} + +#if HT_AMD +constexpr std::array validFlags{hipArrayDefault}; +#else +constexpr std::array validFlags{ + hipArrayDefault, + hipArrayDefault | hipArraySurfaceLoadStore, + hipArrayLayered, + hipArrayLayered | hipArraySurfaceLoadStore, + hipArrayCubemap, + hipArrayCubemap | hipArrayLayered, + hipArrayCubemap | hipArraySurfaceLoadStore, + hipArrayCubemap | hipArrayLayered | hipArraySurfaceLoadStore, + hipArrayTextureGather}; +#endif + +hipExtent makeMipmappedExtent(unsigned int flag, size_t s) { + if (flag == hipArrayTextureGather) { + return make_hipExtent(s, s, 0); + } + return make_hipExtent(s, s, s); +} + +// Providing the array pointer as nullptr should return an error +TEST_CASE("Unit_hipMallocMipmappedArray_Negative_NullArrayPtr") { + hipChannelFormatDesc desc = hipCreateChannelDesc(); + unsigned int numLevels = 1; + constexpr size_t s = 6; + + const auto flag = GENERATE(from_range(std::begin(validFlags), std::end(validFlags))); + HIP_CHECK_ERROR( + hipMallocMipmappedArray(nullptr, &desc, makeMipmappedExtent(flag, s), numLevels, flag), + hipErrorInvalidValue); +} + +// Providing the description pointer as nullptr should return an error +TEST_CASE("Unit_hipMallocMipmappedArray_Negative_NullDescPtr") { + constexpr size_t s = 6; // 6 to keep cubemap happy + unsigned int numLevels = 1; + hipMipmappedArray_t array; + + const auto flag = GENERATE(from_range(std::begin(validFlags), std::end(validFlags))); + + HIP_CHECK_ERROR( + hipMallocMipmappedArray(&array, nullptr, makeMipmappedExtent(flag, s), numLevels, flag), + hipErrorInvalidValue); +} + +// Zero width arrays are not allowed +TEST_CASE("Unit_hipMallocMipmappedArray_Negative_ZeroWidth") { + constexpr size_t s = 6; // 6 to keep cubemap happy + unsigned int numLevels = 1; + hipMipmappedArray_t array; + hipChannelFormatDesc desc = hipCreateChannelDesc(); + + const auto flag = GENERATE(from_range(std::begin(validFlags), std::end(validFlags))); + + HIP_CHECK_ERROR(hipMallocMipmappedArray(&array, &desc, make_hipExtent(0, s, s), numLevels, flag), + hipErrorInvalidValue); +} + +// Zero height arrays are only allowed for 1D arrays and layered arrays +TEST_CASE("Unit_hipMallocMipmappedArray_Negative_ZeroHeight") { + constexpr size_t s = 6; // 6 to keep cubemap happy + unsigned int numLevels = 1; + hipMipmappedArray_t array; + hipChannelFormatDesc desc = hipCreateChannelDesc(); + std::array exceptions{hipArrayLayered, + hipArrayLayered | hipArraySurfaceLoadStore}; + + const auto flag = GENERATE(from_range(std::begin(validFlags), std::end(validFlags))); + + if (std::find(std::begin(exceptions), std::end(exceptions), flag) == std::end(exceptions)) { + // flag is not in list of exceptions + HIP_CHECK_ERROR( + hipMallocMipmappedArray(&array, &desc, make_hipExtent(s, 0, s), numLevels, flag), + hipErrorInvalidValue); + } +} + +TEST_CASE("Unit_hipMallocMipmappedArray_Negative_InvalidFlags") { + constexpr size_t s = 6; // 6 to keep cubemap happy + unsigned int numLevels = 1; + hipMipmappedArray_t array; + hipChannelFormatDesc desc = hipCreateChannelDesc(); + +#if HT_AMD + const unsigned int flag = 0xDEADBEEF; +#else + const unsigned int flag = + GENERATE(0xDEADBEEF, hipArrayTextureGather | hipArraySurfaceLoadStore, + hipArrayTextureGather | hipArrayCubemap, + hipArrayTextureGather | hipArraySurfaceLoadStore | hipArrayCubemap); +#endif + + CAPTURE(flag); + + REQUIRE(std::find(std::begin(validFlags), std::end(validFlags), flag) == std::end(validFlags)); + + HIP_CHECK_ERROR( + hipMallocMipmappedArray(&array, &desc, makeMipmappedExtent(flag, s), numLevels, flag), + hipErrorInvalidValue); +} + +void testInvalidDescriptionMipmapped(hipChannelFormatDesc desc) { + constexpr size_t s = 6; // 6 to keep cubemap happy + unsigned int numLevels = 1; + hipMipmappedArray_t array; + +#if HT_NVIDIA + hipError_t expectedError = hipErrorUnknown; +#else + hipError_t expectedError = hipErrorInvalidValue; +#endif + + const auto flag = GENERATE(from_range(std::begin(validFlags), std::end(validFlags))); + HIP_CHECK_ERROR( + hipMallocMipmappedArray(&array, &desc, makeMipmappedExtent(flag, s), numLevels, flag), + expectedError); +} + +TEST_CASE("Unit_hipMallocMipmappedArray_Negative_InvalidFormat") { + hipChannelFormatDesc desc = hipCreateChannelDesc(); + desc.f = GENERATE(hipChannelFormatKindNone, 0xBEEF); + testInvalidDescriptionMipmapped(desc); +} + +TEST_CASE("Unit_hipMallocMipmappedArray_Negative_BadChannelLayout") { + const int bits = GENERATE(8, 16, 32); + const hipChannelFormatKind formatKind = + GENERATE(hipChannelFormatKindSigned, hipChannelFormatKindUnsigned, hipChannelFormatKindFloat); + if (bits == 8 && formatKind == hipChannelFormatKindFloat) return; + + + hipChannelFormatDesc desc = GENERATE_COPY(hipCreateChannelDesc(bits, bits, bits, 0, formatKind), + hipCreateChannelDesc(0, bits, bits, 0, formatKind), + hipCreateChannelDesc(0, bits, bits, bits, formatKind), + hipCreateChannelDesc(bits, 0, bits, 0, formatKind), + hipCreateChannelDesc(bits, bits, 0, bits, formatKind), + hipCreateChannelDesc(0, 0, bits, 0, formatKind), + hipCreateChannelDesc(0, 0, bits, bits, formatKind)); + + INFO("kind: " << channelFormatString(formatKind)); + INFO("x: " << desc.x << ", y: " << desc.y << ", z: " << desc.z << ", w: " << desc.w); + + testInvalidDescriptionMipmapped(desc); +} + +TEST_CASE("Unit_hipMallocMipmappedArray_Negative_8BitFloat") { + hipChannelFormatDesc desc = GENERATE(hipCreateChannelDesc(8, 0, 0, 0, hipChannelFormatKindFloat), + hipCreateChannelDesc(8, 8, 0, 0, hipChannelFormatKindFloat), + hipCreateChannelDesc(8, 8, 8, 8, hipChannelFormatKindFloat)); + + testInvalidDescriptionMipmapped(desc); +} + +TEST_CASE("Unit_hipMallocMipmappedArray_Negative_DifferentChannelSizes") { + const int bitsX = GENERATE(8, 16, 32); + const int bitsY = GENERATE(8, 16, 32); + const int bitsZ = GENERATE(8, 16, 32); + const int bitsW = GENERATE(8, 16, 32); + if (bitsX == bitsY && bitsY == bitsZ && bitsZ == bitsW) return; // skip when they are equal + + const hipChannelFormatKind channelFormat = + GENERATE(hipChannelFormatKindSigned, hipChannelFormatKindUnsigned, hipChannelFormatKindFloat); + + if (channelFormat == hipChannelFormatKindFloat && + (bitsX == 8 || bitsY == 8 || bitsZ == 8 || bitsW == 8)) + return; // 8 bit floats aren't allowed + + hipChannelFormatDesc desc = hipCreateChannelDesc(bitsX, bitsY, bitsZ, bitsW, channelFormat); + + INFO("format: " << channelFormatString(channelFormat) << ", x bits: " << bitsX + << ", y bits: " << bitsY << ", z bits: " << bitsZ << ", w bits: " << bitsW); + + + testInvalidDescriptionMipmapped(desc); +} + +TEST_CASE("Unit_hipMallocMipmappedArray_Negative_BadChannelSize") { + const int badBits = GENERATE(-1, 0, 10, 100); + const hipChannelFormatKind formatKind = + GENERATE(hipChannelFormatKindSigned, hipChannelFormatKindUnsigned, hipChannelFormatKindFloat); + hipChannelFormatDesc desc = hipCreateChannelDesc(badBits, badBits, badBits, badBits, formatKind); + + INFO("Number of bits: " << badBits); + + testInvalidDescriptionMipmapped(desc); +} + + +// hipMallocMipmappedArray should handle the max numeric value gracefully. +TEST_CASE("Unit_hipMallocMipmappedArray_Negative_NumericLimit") { + hipMipmappedArray_t arrayPtr; + unsigned int numLevels = 1; + hipChannelFormatDesc desc = hipCreateChannelDesc(); + + size_t size = std::numeric_limits::max(); + const auto flag = GENERATE(from_range(std::begin(validFlags), std::end(validFlags))); + HIP_CHECK_ERROR( + hipMallocMipmappedArray(&arrayPtr, &desc, makeMipmappedExtent(flag, size), numLevels, flag), + hipErrorInvalidValue); +} + +// texture gather arrays are only allowed to be 2D +TEMPLATE_TEST_CASE("Unit_hipMallocMipmappedArray_Negative_Non2DTextureGather", "", char, uchar2, + float2) { +#if HT_AMD + HipTest::HIP_SKIP_TEST("Texture Gather arrays not supported using AMD backend"); + return; +#endif + hipMipmappedArray_t array; + unsigned int numLevels = 1; + const auto desc = hipCreateChannelDesc(); + + constexpr unsigned int flags = hipArrayTextureGather; + constexpr size_t size = 64; + const hipExtent extent = GENERATE(make_hipExtent(size, 0, 0), make_hipExtent(size, size, size)); + + HIP_CHECK_ERROR(hipMallocMipmappedArray(&array, &desc, extent, numLevels, flags), + hipErrorInvalidValue); +} + +TEST_CASE("Unit_hipMallocMipmappedArray_Negative_NumLevels") { + hipMipmappedArray_t array; + constexpr size_t size = 6; + unsigned int numLevels = floor(log2(size)) + 2; + hipChannelFormatDesc desc = hipCreateChannelDesc(); + + const auto flag = GENERATE(from_range(std::begin(validFlags), std::end(validFlags))); + HIP_CHECK_ERROR( + hipMallocMipmappedArray(&array, &desc, makeMipmappedExtent(flag, size), numLevels, flag), + hipErrorInvalidValue); +} + +TEST_CASE("Unit_hipGetMipmappedArrayLevel_Negative") { + constexpr size_t s = 6; + unsigned int numLevels = 1; + hipMipmappedArray_t array; + hipArray_t level_array; + + hipChannelFormatDesc desc = hipCreateChannelDesc(); + + HIP_CHECK( + hipMallocMipmappedArray(&array, &desc, make_hipExtent(s, s, s), numLevels, hipArrayDefault)); + SECTION("Level is invalid") { + HIP_CHECK_ERROR(hipGetMipmappedArrayLevel(&level_array, array, 3), hipErrorInvalidValue); + } + SECTION("Mipmapped array is nullptr") { + HIP_CHECK_ERROR(hipGetMipmappedArrayLevel(&level_array, nullptr, 1), + hipErrorInvalidResourceHandle); + } + HIP_CHECK(hipFreeMipmappedArray(array)); +} diff --git a/catch/unit/memory/hipMemPoolCreate.cc b/catch/unit/memory/hipMemPoolCreate.cc new file mode 100644 index 0000000000..57a3db7f45 --- /dev/null +++ b/catch/unit/memory/hipMemPoolCreate.cc @@ -0,0 +1,93 @@ +/* + Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. + 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 WARRANNTY OF ANY KIND, EXPRESS OR + IMPLIED, INNCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANNY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER INN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR INN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + */ + +#include + +/** + * @addtogroup hipMemPoolCreate hipMemPoolCreate + * @{ + * @ingroup StreamOTest + * `hipMemPoolCreate(hipMemPool_t* mem_pool, const hipMemPoolProps* pool_props)` - + * Creates a memory pool and returns the handle in mem pool + */ + +/** + * Test Description + * ------------------------ + * - Test to verify hipMemPoolCreate behavior with invalid arguments: + * -# Nullptr mem_pool + * -# Nullptr props + * -# Invalid props alloc type + * -# Invalid props location type + * -# Invalid props location id + * + * Test source + * ------------------------ + * - /unit/memory/hipMemPoolCreate.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 6.0 + */ +TEST_CASE("Unit_hipMemPoolCreate_Negative_Parameter") { + int mem_pool_support = 0; + HIP_CHECK(hipDeviceGetAttribute(&mem_pool_support, hipDeviceAttributeMemoryPoolsSupported, 0)); + if (!mem_pool_support) { + SUCCEED("Runtime doesn't support Memory Pool. Skip the test case."); + return; + } + + int num_dev = 0; + HIP_CHECK(hipGetDeviceCount(&num_dev)); + + hipMemPoolProps pool_props; + pool_props.allocType = hipMemAllocationTypePinned; + pool_props.handleTypes = hipMemHandleTypeNone; + pool_props.location.type = hipMemLocationTypeDevice; + pool_props.location.id = 0; + pool_props.win32SecurityAttributes = nullptr; + memset(pool_props.reserved, 0, sizeof(pool_props.reserved)); + + hipMemPool_t mem_pool = nullptr; + + SECTION("Passing nullptr to mem_pool") { + HIP_CHECK_ERROR(hipMemPoolCreate(nullptr, &pool_props), hipErrorInvalidValue); + } + + SECTION("Passing nullptr to props") { + HIP_CHECK_ERROR(hipMemPoolCreate(&mem_pool, nullptr), hipErrorInvalidValue); + } + + SECTION("Passing invalid props alloc type") { + pool_props.allocType = hipMemAllocationTypeInvalid; + HIP_CHECK_ERROR(hipMemPoolCreate(&mem_pool, &pool_props), hipErrorInvalidValue); + pool_props.allocType = hipMemAllocationTypePinned; + } + + SECTION("Passing invalid props location type") { + pool_props.location.type = hipMemLocationTypeInvalid; + HIP_CHECK_ERROR(hipMemPoolCreate(&mem_pool, &pool_props), hipErrorInvalidValue); + pool_props.location.type = hipMemLocationTypeDevice; + } + + SECTION("Passing invalid props location id") { + pool_props.location.id = num_dev; + HIP_CHECK_ERROR(hipMemPoolCreate(&mem_pool, &pool_props), hipErrorInvalidValue); + pool_props.location.id = 0; + } +} diff --git a/catch/unit/memory/hipMemPoolDestroy.cc b/catch/unit/memory/hipMemPoolDestroy.cc new file mode 100644 index 0000000000..f76059dbbc --- /dev/null +++ b/catch/unit/memory/hipMemPoolDestroy.cc @@ -0,0 +1,71 @@ +/* + Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. + 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 WARRANNTY OF ANY KIND, EXPRESS OR + IMPLIED, INNCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANNY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER INN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR INN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + */ + +#include "mempool_common.hh" + +/** + * @addtogroup hipMemPoolDestroy hipMemPoolDestroy + * @{ + * @ingroup StreamOTest + * `hipMemPoolDestroy(hipMemPool_t mem_pool)` - + * Destroys the specified memory pool + */ + +/** + * Test Description + * ------------------------ + * - Test to verify hipMemPoolCreate behavior with invalid arguments: + * -# Nullptr mem_pool + * -# Double hipMemPoolDestroy + * -# Attempt to destroy default mempool + * + * Test source + * ------------------------ + * - /unit/memory/hipMemPoolDestroy.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 6.0 + */ +TEST_CASE("Unit_hipMemPoolDestroy_Negative_Parameter") { + int mem_pool_support = 0; + HIP_CHECK(hipDeviceGetAttribute(&mem_pool_support, hipDeviceAttributeMemoryPoolsSupported, 0)); + if (!mem_pool_support) { + SUCCEED("Runtime doesn't support Memory Pool. Skip the test case."); + return; + } + + hipMemPool_t mem_pool = nullptr; + + SECTION("Passing nullptr to mempool") { + HIP_CHECK_ERROR(hipMemPoolDestroy(nullptr), hipErrorInvalidValue); + } + + SECTION("Double hipMemPoolDestroy") { + HIP_CHECK(hipMemPoolCreate(&mem_pool, &kPoolProps)); + HIP_CHECK(hipMemPoolDestroy(mem_pool)); + HIP_CHECK_ERROR(hipMemPoolDestroy(mem_pool), hipErrorInvalidValue); + } + + SECTION("Attempt to destroy default mempool") { + hipMemPool_t default_mem_pool = nullptr; + int device = 0; + HIP_CHECK(hipDeviceGetDefaultMemPool(&default_mem_pool, device)); + HIP_CHECK_ERROR(hipMemPoolDestroy(default_mem_pool), hipErrorInvalidValue); + } +} diff --git a/catch/unit/memory/hipMemPoolSetGetAccess.cc b/catch/unit/memory/hipMemPoolSetGetAccess.cc new file mode 100644 index 0000000000..ce42aff87f --- /dev/null +++ b/catch/unit/memory/hipMemPoolSetGetAccess.cc @@ -0,0 +1,361 @@ +/* + Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. + 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 WARRANNTY OF ANY KIND, EXPRESS OR + IMPLIED, INNCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANNY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER INN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR INN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + */ + +#include +#include +#include + +/** + * @addtogroup hipMemPoolSetAccess hipMemPoolSetAccess + * @{ + * @ingroup StreamOTest + * `hipMemPoolSetAccess(hipMemPool_t mem_pool, const hipMemAccessDesc* desc_list, size_t count)` + * - Controls visibility of the specified pool between devices + */ + +__global__ void copyP2PAndScale(int* dst, const int* src, size_t N) { + int idx = blockIdx.x * blockDim.x + threadIdx.x; + + if (idx < N) { + // scale & store src vector. + dst[idx] = 2 * src[idx]; + } +} + +static void MemPoolSetGetAccess(const MemPools mempool_type, int src_device, int dst_device, + hipMemAccessFlags access_flags) { + MemPoolGuard mempool(mempool_type, src_device); + + hipMemAccessDesc desc; + memset(&desc, 0, sizeof(hipMemAccessDesc)); + desc.location.type = hipMemLocationTypeDevice; + desc.location.id = dst_device; + desc.flags = access_flags; + HIP_CHECK(hipMemPoolSetAccess(mempool.mempool(), &desc, 1)); + + hipMemAccessFlags flags = hipMemAccessFlagsProtNone; + HIP_CHECK(hipMemPoolGetAccess(&flags, mempool.mempool(), &desc.location)); + REQUIRE(flags == access_flags); +} + +/** + * Test Description + * ------------------------ + * - Basic test to verify hipMemPoolSetAccess/hipMemPoolGetAccess on a single device. + * Test source + * ------------------------ + * - /unit/memory/hipMemPoolSetGetAccess.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 6.0 + */ +TEST_CASE("Unit_hipMemPoolSetGetAccess_Positive_Basic") { + const auto device = GENERATE(range(0, HipTest::getDeviceCount())); + + int mem_pool_support = 0; + HIP_CHECK( + hipDeviceGetAttribute(&mem_pool_support, hipDeviceAttributeMemoryPoolsSupported, device)); + if (!mem_pool_support) { + SUCCEED("Runtime doesn't support Memory Pool. Skip the test case."); + return; + } + + const auto mempool_type = GENERATE(MemPools::dev_default, MemPools::created); + + MemPoolSetGetAccess(mempool_type, device, device, hipMemAccessFlagsProtReadWrite); +} + +int CheckP2PMemPoolSupport(int src_device, int dst_device) { + int mem_pool_support = 0; + HIP_CHECK( + hipDeviceGetAttribute(&mem_pool_support, hipDeviceAttributeMemoryPoolsSupported, src_device)); + if (mem_pool_support) { + HIP_CHECK(hipDeviceGetAttribute(&mem_pool_support, hipDeviceAttributeMemoryPoolsSupported, + dst_device)); + } + return mem_pool_support; +} + +/** + * Test Description + * ------------------------ + * - Basic test to verify hipMemPoolSetAccess/hipMemPoolGetAccess on multiple devices. + * Test source + * ------------------------ + * - /unit/memory/hipMemPoolSetGetAccess.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 6.0 + */ +TEST_CASE("Unit_hipMemPoolSetGetAccess_Positive_MultipleGPU") { + const auto device_count = HipTest::getDeviceCount(); + if (device_count < 2) { + HipTest::HIP_SKIP_TEST("Skipping because devices < 2"); + return; + } + const auto src_device = GENERATE(range(0, HipTest::getDeviceCount())); + const auto dst_device = GENERATE(range(0, HipTest::getDeviceCount())); + INFO("Src device: " << src_device << ", Dst device: " << dst_device); + + int mem_pool_support = CheckP2PMemPoolSupport(src_device, dst_device); + if (!mem_pool_support) { + SUCCEED("Runtime doesn't support Memory Pool. Skip the test case."); + return; + } + + const auto mempool_type = GENERATE(MemPools::dev_default, MemPools::created); + const auto access_flag = GENERATE(hipMemAccessFlagsProtNone, hipMemAccessFlagsProtRead, + hipMemAccessFlagsProtReadWrite); + + int can_access_peer = 0; + HIP_CHECK(hipSetDevice(src_device)); + HIP_CHECK(hipDeviceCanAccessPeer(&can_access_peer, src_device, dst_device)); + if (can_access_peer) { + MemPoolSetGetAccess(mempool_type, src_device, dst_device, access_flag); + } +} + +void MemPoolSetGetAccess_P2P(const MemPools mempool_type) { + const auto src_device = GENERATE(range(0, HipTest::getDeviceCount())); + const auto dst_device = GENERATE(range(0, HipTest::getDeviceCount())); + INFO("Src device: " << src_device << ", Dst device: " << dst_device); + + const auto allocation_size = GENERATE(kPageSize / 2, kPageSize, kPageSize * 2); + + int mem_pool_support = CheckP2PMemPoolSupport(src_device, dst_device); + if (!mem_pool_support) { + SUCCEED("Runtime doesn't support Memory Pool. Skip the test case."); + return; + } + + int *alloc_mem1, *alloc_mem2; + int can_access_peer = 0; + HIP_CHECK(hipSetDevice(src_device)); + HIP_CHECK(hipDeviceCanAccessPeer(&can_access_peer, src_device, dst_device)); + if (can_access_peer) { + hipEvent_t waitOnStream1; + + LinearAllocGuard host_alloc(LinearAllocs::malloc, allocation_size); + HIP_CHECK(hipEventCreate(&waitOnStream1)) + StreamGuard stream1(Streams::withFlags, hipStreamNonBlocking); + // Get/create mempool for src_device + MemPoolGuard mempool(mempool_type, src_device); + + // Allocate memory in a stream from the pool set above + if (mempool_type == MemPools::dev_default) { + HIP_CHECK( + hipMallocAsync(reinterpret_cast(&alloc_mem1), allocation_size, stream1.stream())); + } else { + HIP_CHECK(hipMallocFromPoolAsync(reinterpret_cast(&alloc_mem1), allocation_size, + mempool.mempool(), stream1.stream())); + } + + const auto element_count = allocation_size / sizeof(int); + constexpr auto thread_count = 1024; + const auto block_count = element_count / thread_count + 1; + constexpr int expected_value = 15; + VectorSet<<>>(alloc_mem1, expected_value, + element_count); + HIP_CHECK(hipEventRecord(waitOnStream1, stream1.stream())); + + HIP_CHECK(hipSetDevice(dst_device)); + StreamGuard stream2(Streams::withFlags, hipStreamNonBlocking); + + // Allocate memory in dst device + HIP_CHECK( + hipMallocAsync(reinterpret_cast(&alloc_mem2), allocation_size, stream2.stream())); + + // Setup peer mappings for dst device + hipMemAccessDesc desc; + memset(&desc, 0, sizeof(hipMemAccessDesc)); + desc.location.type = hipMemLocationTypeDevice; + desc.location.id = dst_device; + desc.flags = hipMemAccessFlagsProtReadWrite; + HIP_CHECK(hipMemPoolSetAccess(mempool.mempool(), &desc, 1)); + + hipMemAccessFlags flags = hipMemAccessFlagsProtNone; + HIP_CHECK(hipMemPoolGetAccess(&flags, mempool.mempool(), &desc.location)); + REQUIRE(flags == hipMemAccessFlagsProtReadWrite); + + HIP_CHECK(hipStreamWaitEvent(stream2.stream(), waitOnStream1, 0)); + copyP2PAndScale<<>>(alloc_mem2, alloc_mem1, + element_count); + + HIP_CHECK(hipMemcpyAsync(host_alloc.host_ptr(), alloc_mem2, allocation_size, + hipMemcpyDeviceToHost, stream2.stream())); + HIP_CHECK(hipFreeAsync(alloc_mem1, stream2.stream())); + HIP_CHECK(hipFreeAsync(alloc_mem2, stream2.stream())); + HIP_CHECK(hipStreamSynchronize(stream2.stream())); + + ArrayFindIfNot(host_alloc.host_ptr(), 2 * expected_value, element_count); + } +} + +/** + * Test Description + * ------------------------ + * - Basic test to verify peer-to-peer access of stream ordered memory with hipMemPoolSetAccess. + * Test source + * ------------------------ + * - /unit/memory/hipMemPoolSetGetAccess.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 6.0 + */ +TEST_CASE("Unit_hipMemPoolSetGetAccess_Positive_P2P") { + const auto device_count = HipTest::getDeviceCount(); + if (device_count < 2) { + HipTest::HIP_SKIP_TEST("Skipping because devices < 2"); + return; + } + + SECTION("Default MemPool") { MemPoolSetGetAccess_P2P(MemPools::dev_default); } + + SECTION("Created MemPool") { MemPoolSetGetAccess_P2P(MemPools::created); } +} + +/** + * Test Description + * ------------------------ + * - Test to verify hipMemPoolSetAccess behavior with invalid arguments: + * -# Nullptr mem_pool + * -# Desc is nullptr and count is > 0 + * -# Count > num_device + * -# Invalid desc location type + * -# Invalid desc location id + * -# Revoking access to own memory pool + * + * Test source + * ------------------------ + * - /unit/memory/hipMemPoolSetGetAccess.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 6.0 + */ +TEST_CASE("Unit_hipMemPoolSetAccess_Negative_Parameters") { + int device_id = 0; + HIP_CHECK(hipSetDevice(device_id)); + + MemPoolGuard mempool(MemPools::dev_default, device_id); + + int num_dev = 0; + HIP_CHECK(hipGetDeviceCount(&num_dev)); + + hipMemAccessDesc desc; + memset(&desc, 0, sizeof(hipMemAccessDesc)); + desc.location.type = hipMemLocationTypeDevice; + desc.location.id = device_id; + desc.flags = hipMemAccessFlagsProtReadWrite; + + SECTION("Mempool is nullptr") { + HIP_CHECK_ERROR(hipMemPoolSetAccess(nullptr, &desc, 1), hipErrorInvalidValue); + } +#if HT_AMD + SECTION("Desc is nullptr and count is > 0") { + HIP_CHECK_ERROR(hipMemPoolSetAccess(mempool.mempool(), nullptr, 1), hipErrorInvalidValue); + } +#endif + SECTION("Count > num_device") { + HIP_CHECK_ERROR(hipMemPoolSetAccess(mempool.mempool(), &desc, (num_dev + 1)), + hipErrorNotSupported); + } + + SECTION("Passing invalid desc location type") { + desc.location.type = hipMemLocationTypeInvalid; + HIP_CHECK_ERROR(hipMemPoolSetAccess(mempool.mempool(), &desc, 1), hipErrorNotSupported); + desc.location.type = hipMemLocationTypeDevice; + } + + SECTION("Passing invalid desc location id") { + desc.location.id = num_dev; + HIP_CHECK_ERROR(hipMemPoolSetAccess(mempool.mempool(), &desc, 1), hipErrorInvalidDevice); + desc.location.id = device_id; + } + + SECTION("Revoking access to own memory pool") { + desc.flags = hipMemAccessFlagsProtNone; + HIP_CHECK_ERROR(hipMemPoolSetAccess(mempool.mempool(), &desc, 1), hipErrorInvalidDevice); + desc.flags = hipMemAccessFlagsProtReadWrite; + } +} + +/** + * End doxygen group hipMemPoolSetAccess. + * @} + */ + +/** + * @addtogroup hipMemPoolGetAccess hipMemPoolGetAccess + * @{ + * @ingroup StreamOTest + * `hipMemPoolGetAccess(hipMemAccessFlags* flags, hipMemPool_t mem_pool, hipMemLocation* location)` + * - Returns the accessibility of a pool from a device + */ + +/** + * Test Description + * ------------------------ + * - Test to verify hipMemPoolGetAccess behavior with invalid arguments: + * -# Nullptr mem_pool + * -# Flags is nullptr + * -# Invalid location type + * -# Invalid location id + * + * Test source + * ------------------------ + * - /unit/memory/hipMemPoolSetGetAccess.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 6.0 + */ +TEST_CASE("Unit_hipMemPoolGetAccess_Negative_Parameters") { + int device_id = 0; + HIP_CHECK(hipSetDevice(device_id)); + + MemPoolGuard mempool(MemPools::dev_default, device_id); + + int num_dev = 0; + HIP_CHECK(hipGetDeviceCount(&num_dev)); + + hipMemAccessFlags flags = hipMemAccessFlagsProtNone; + hipMemLocation location = {hipMemLocationTypeDevice, device_id}; + + SECTION("Mempool is nullptr") { + HIP_CHECK_ERROR(hipMemPoolGetAccess(&flags, nullptr, &location), hipErrorInvalidValue); + } +#if HT_AMD + SECTION("Flags is nullptr") { + HIP_CHECK_ERROR(hipMemPoolGetAccess(nullptr, mempool.mempool(), &location), + hipErrorInvalidValue); + } +#endif + SECTION("Passing invalid location type") { + location.type = hipMemLocationTypeInvalid; + HIP_CHECK_ERROR(hipMemPoolGetAccess(&flags, mempool.mempool(), &location), + hipErrorInvalidValue); + location.type = hipMemLocationTypeDevice; + } + + SECTION("Passing invalid location id") { + location.id = num_dev; + HIP_CHECK_ERROR(hipMemPoolGetAccess(&flags, mempool.mempool(), &location), + hipErrorInvalidValue); + location.id = device_id; + } +} diff --git a/catch/unit/memory/hipMemPoolSetGetAttribute.cc b/catch/unit/memory/hipMemPoolSetGetAttribute.cc new file mode 100644 index 0000000000..6e0781afce --- /dev/null +++ b/catch/unit/memory/hipMemPoolSetGetAttribute.cc @@ -0,0 +1,590 @@ +/* + Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. + 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 WARRANNTY OF ANY KIND, EXPRESS OR + IMPLIED, INNCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANNY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER INN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR INN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + */ +#include "mempool_common.hh" + +#include +#include + +/** + * @addtogroup hipMemPoolSetAttribute hipMemPoolSetAttribute + * @{ + * @ingroup StreamOTest + * `hipMemPoolSetAttribute(hipMemPool_t mem_pool, hipMemPoolAttr attr, void* value)` + * - Sets attributes of a memory pool + */ + +template +static void MemPoolSetGetAttribute(const hipMemPool_t mempool, const hipMemPoolAttr attr, + T& set_value) { + T get_value = 100; + HIP_CHECK(hipMemPoolSetAttribute(mempool, attr, &set_value)); + HIP_CHECK(hipMemPoolGetAttribute(mempool, attr, &get_value)); + REQUIRE(get_value == set_value); +} + + +/** + * Test Description + * ------------------------ + * - Basic test to verify that default attribute values are correct. + * Test source + * ------------------------ + * - /unit/memory/hipMemPoolSetGetAttribute.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 6.0 + */ +TEST_CASE("Unit_hipMemPoolSetGetAttribute_Positive_Default") { + const auto device = GENERATE(range(0, HipTest::getDeviceCount())); + + int mem_pool_support = 0; + HIP_CHECK( + hipDeviceGetAttribute(&mem_pool_support, hipDeviceAttributeMemoryPoolsSupported, device)); + if (!mem_pool_support) { + SUCCEED("Runtime doesn't support Memory Pool. Skip the test case."); + return; + } + + const auto mempool_type = GENERATE(MemPools::dev_default, MemPools::created); + MemPoolGuard mempool(mempool_type, device); + + const auto attr_type = + GENERATE(hipMemPoolReuseFollowEventDependencies, hipMemPoolReuseAllowOpportunistic, + hipMemPoolReuseAllowInternalDependencies); + + // Check default value + int def_value = 0; + HIP_CHECK(hipMemPoolGetAttribute(mempool.mempool(), attr_type, &def_value)); + REQUIRE(def_value == 1); + + // Check if attribute can be disabled + int set_value = 0; + MemPoolSetGetAttribute(mempool.mempool(), attr_type, set_value); +} + +/** + * Test Description + * ------------------------ + * - Basic test to verify hipMemPoolSetAttribute/hipMemPoolGetAttribute functionality. + * Test source + * ------------------------ + * - /unit/memory/hipMemPoolSetGetAttribute.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 6.0 + */ +TEST_CASE("Unit_hipMemPoolSetGetAttribute_Positive_MemBasic") { + const auto device = GENERATE(range(0, HipTest::getDeviceCount())); + + int mem_pool_support = 0; + HIP_CHECK( + hipDeviceGetAttribute(&mem_pool_support, hipDeviceAttributeMemoryPoolsSupported, device)); + if (!mem_pool_support) { + SUCCEED("Runtime doesn't support Memory Pool. Skip the test case."); + return; + } + + const auto mempool_type = GENERATE(MemPools::dev_default, MemPools::created); + MemPoolGuard mempool(mempool_type, device); + + // Check hipMemPoolAttrReleaseThreshold default value + hipMemPoolAttr attr = hipMemPoolAttrReleaseThreshold; + std::uint64_t value64 = 100; + HIP_CHECK(hipMemPoolGetAttribute(mempool.mempool(), attr, &value64)); + REQUIRE(value64 == 0); + + // Check setting hipMemPoolAttrReleaseThreshold to a value + std::uint64_t set_value64 = kPageSize; + MemPoolSetGetAttribute(mempool.mempool(), hipMemPoolAttrReleaseThreshold, set_value64); + + // Check reset of hipMemPoolAttrReservedMemHigh and hipMemPoolAttrUsedMemHigh + set_value64 = 0; + MemPoolSetGetAttribute(mempool.mempool(), hipMemPoolAttrReservedMemHigh, set_value64); + MemPoolSetGetAttribute(mempool.mempool(), hipMemPoolAttrUsedMemHigh, set_value64); +} + +/** + * Test Description + * ------------------------ + * - Basic test to verify correct behavior of the Opportunistic attribute. + * Test source + * ------------------------ + * - /unit/memory/hipMemPoolSetGetAttribute.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 6.0 + */ +TEST_CASE("Unit_hipMemPoolSetAttribute_Opportunistic") { + int device_id = 0; + HIP_CHECK(hipSetDevice(device_id)); + + int mem_pool_support = 0; + HIP_CHECK(hipDeviceGetAttribute(&mem_pool_support, hipDeviceAttributeMemoryPoolsSupported, 0)); + if (!mem_pool_support) { + SUCCEED("Runtime doesn't support Memory Pool. Skip the test case."); + return; + } + + MemPoolGuard mempool(MemPools::created, device_id); + + hipMemPoolAttr attr; + int blocks = 2; + int clk_rate; + if (IsGfx11()) { + HIPCHECK(hipDeviceGetAttribute(&clk_rate, hipDeviceAttributeWallClockRate, 0)); + } else { + HIPCHECK(hipDeviceGetAttribute(&clk_rate, hipDeviceAttributeClockRate, 0)); + } + + int *alloc_mem1, *alloc_mem2, *alloc_mem3; + + // Create 2 async non-blocking streams + StreamGuard stream1(Streams::withFlags, hipStreamNonBlocking); + StreamGuard stream2(Streams::withFlags, hipStreamNonBlocking); + + size_t allocation_size = kPageSize; + HIP_CHECK(hipMallocFromPoolAsync(reinterpret_cast(&alloc_mem3), allocation_size, + mempool.mempool(), stream1.stream())); + int value = 0; + + SECTION("Disallow Opportunistic - No Reuse") { + allocation_size = kPageSize * kPageSize * 2; + HIP_CHECK(hipMallocFromPoolAsync(reinterpret_cast(&alloc_mem1), allocation_size, + mempool.mempool(), stream1.stream())); + + // Disable all default pool states + attr = hipMemPoolReuseFollowEventDependencies; + HIP_CHECK(hipMemPoolSetAttribute(mempool.mempool(), attr, &value)); + attr = hipMemPoolReuseAllowOpportunistic; + HIP_CHECK(hipMemPoolSetAttribute(mempool.mempool(), attr, &value)); + attr = hipMemPoolReuseAllowInternalDependencies; + HIP_CHECK(hipMemPoolSetAttribute(mempool.mempool(), attr, &value)); + + // Run kernel for 500 ms in the first stream + if (IsGfx11()) { + kernel_500ms_gfx11<<<32, blocks, 0, stream1.stream()>>>(alloc_mem1, clk_rate); + } else { + kernel_500ms<<<32, blocks, 0, stream1.stream()>>>(alloc_mem1, clk_rate); + } + + // Not a real free, since kernel isn't done + HIP_CHECK(hipFreeAsync(reinterpret_cast(alloc_mem1), stream1.stream())); + + // Sleep for 1 second GPU should be idle by now + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + + // Allocate memory for the second stream + HIP_CHECK(hipMallocFromPoolAsync(reinterpret_cast(&alloc_mem2), allocation_size, + mempool.mempool(), stream2.stream())); + // Without Opportunistic state runtime must allocate another buffer + REQUIRE(alloc_mem1 != alloc_mem2); + + // Run kernel with the new memory in the second stream + if (IsGfx11()) { + kernel_500ms_gfx11<<<32, blocks, 0, stream2.stream()>>>(alloc_mem2, clk_rate); + } else { + kernel_500ms<<<32, blocks, 0, stream2.stream()>>>(alloc_mem2, clk_rate); + } + + HIP_CHECK(hipStreamSynchronize(stream1.stream())); + HIP_CHECK(hipStreamSynchronize(stream2.stream())); + + HIP_CHECK(hipFreeAsync(reinterpret_cast(alloc_mem2), stream2.stream())); + } + + SECTION("Disallow Opportunistic - Reuse") { + allocation_size = kPageSize * kPageSize * 2; + HIP_CHECK(hipMallocFromPoolAsync(reinterpret_cast(&alloc_mem1), allocation_size, + mempool.mempool(), stream1.stream())); + + // Disable all default pool states + attr = hipMemPoolReuseFollowEventDependencies; + HIP_CHECK(hipMemPoolSetAttribute(mempool.mempool(), attr, &value)); + attr = hipMemPoolReuseAllowOpportunistic; + HIP_CHECK(hipMemPoolSetAttribute(mempool.mempool(), attr, &value)); + attr = hipMemPoolReuseAllowInternalDependencies; + HIP_CHECK(hipMemPoolSetAttribute(mempool.mempool(), attr, &value)); + + // Run kernel for 500 ms in the first stream + if (IsGfx11()) { + kernel_500ms_gfx11<<<32, blocks, 0, stream1.stream()>>>(alloc_mem1, clk_rate); + } else { + kernel_500ms<<<32, blocks, 0, stream1.stream()>>>(alloc_mem1, clk_rate); + } + + // Not a real free, since kernel isn't done + HIP_CHECK(hipFreeAsync(reinterpret_cast(alloc_mem1), stream1.stream())); + + // Sleep for 1 second GPU should be idle by now + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + + // Allocate memory for the second stream + HIP_CHECK(hipMallocFromPoolAsync(reinterpret_cast(&alloc_mem2), allocation_size, + mempool.mempool(), stream1.stream())); + // Without Opportunistic state runtime must allocate another buffer + REQUIRE(alloc_mem1 == alloc_mem2); + + // Run kernel with the new memory in the second stream + if (IsGfx11()) { + kernel_500ms_gfx11<<<32, blocks, 0, stream1.stream()>>>(alloc_mem2, clk_rate); + } else { + kernel_500ms<<<32, blocks, 0, stream1.stream()>>>(alloc_mem2, clk_rate); + } + + HIP_CHECK(hipStreamSynchronize(stream1.stream())); + + HIP_CHECK(hipFreeAsync(reinterpret_cast(alloc_mem2), stream1.stream())); + } + + SECTION("Allow Opportunistic - Reuse") { + allocation_size = kPageSize * kPageSize * 2; + HIP_CHECK(hipMallocFromPoolAsync(reinterpret_cast(&alloc_mem1), allocation_size, + mempool.mempool(), stream1.stream())); + + value = 1; + attr = hipMemPoolReuseAllowOpportunistic; + // Enable Opportunistic + HIP_CHECK(hipMemPoolSetAttribute(mempool.mempool(), attr, &value)); + + // Run kernel for 500 ms in the first stream + if (IsGfx11()) { + HIP_CHECK(hipDeviceGetAttribute(&clk_rate, hipDeviceAttributeWallClockRate, 0)); + kernel_500ms_gfx11<<<32, blocks, 0, stream1.stream()>>>(alloc_mem1, clk_rate); + } else { + HIP_CHECK(hipDeviceGetAttribute(&clk_rate, hipDeviceAttributeClockRate, 0)); + kernel_500ms<<<32, blocks, 0, stream1.stream()>>>(alloc_mem1, clk_rate); + } + + // Not a real free, since kernel isn't done + HIP_CHECK(hipFreeAsync(reinterpret_cast(alloc_mem1), stream1.stream())); + + // Sleep for 1 second GPU should be idle by now + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + + // Allocate memory for the second stream + HIP_CHECK(hipMallocFromPoolAsync(reinterpret_cast(&alloc_mem2), allocation_size, + mempool.mempool(), stream2.stream())); + // With Opportunistic state runtime will reuse freed buffer A + REQUIRE(alloc_mem1 == alloc_mem2); + + // Run kernel with the new memory in the second stream + if (IsGfx11()) { + kernel_500ms_gfx11<<<32, blocks, 0, stream2.stream()>>>(alloc_mem2, clk_rate); + } else { + kernel_500ms<<<32, blocks, 0, stream2.stream()>>>(alloc_mem2, clk_rate); + } + + HIP_CHECK(hipStreamSynchronize(stream1.stream())); + HIP_CHECK(hipStreamSynchronize(stream2.stream())); + + HIP_CHECK(hipFreeAsync(reinterpret_cast(alloc_mem2), stream2.stream())); + } + + SECTION("Allow Opportunistic - No Reuse") { + allocation_size = kPageSize * kPageSize * 2; + HIP_CHECK(hipMallocFromPoolAsync(reinterpret_cast(&alloc_mem1), allocation_size, + mempool.mempool(), stream1.stream())); + + value = 1; + attr = hipMemPoolReuseAllowOpportunistic; + // Enable Opportunistic + HIP_CHECK(hipMemPoolSetAttribute(mempool.mempool(), attr, &value)); + + // Run kernel for 500 ms in the first stream + + if (IsGfx11()) { + kernel_500ms_gfx11<<<32, blocks, 0, stream1.stream()>>>(alloc_mem1, clk_rate); + } else { + kernel_500ms<<<32, blocks, 0, stream1.stream()>>>(alloc_mem1, clk_rate); + } + + // Not a real free, since kernel isn't done + HIP_CHECK(hipFreeAsync(reinterpret_cast(alloc_mem1), stream1.stream())); + + // Allocate memory for the second stream + HIP_CHECK(hipMallocFromPoolAsync(reinterpret_cast(&alloc_mem2), allocation_size, + mempool.mempool(), stream2.stream())); + // With Opportunistic state runtime can't reuse freed buffer A, because it's still busy with the + // kernel + REQUIRE(alloc_mem1 != alloc_mem2); + + // Run kernel with the new memory in the second stream + if (IsGfx11()) { + kernel_500ms_gfx11<<<32, blocks, 0, stream2.stream()>>>(alloc_mem2, clk_rate); + } else { + kernel_500ms<<<32, blocks, 0, stream2.stream()>>>(alloc_mem2, clk_rate); + } + + HIP_CHECK(hipStreamSynchronize(stream1.stream())); + HIP_CHECK(hipStreamSynchronize(stream2.stream())); + + HIP_CHECK(hipFreeAsync(reinterpret_cast(alloc_mem2), stream2.stream())); + } + + HIP_CHECK(hipFreeAsync(reinterpret_cast(alloc_mem3), stream1.stream())); +} + +/** + * Test Description + * ------------------------ + * - Basic test to verify correct behavior of the EventDependencies attribute. + * Test source + * ------------------------ + * - /unit/memory/hipMemPoolSetGetAttribute.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 6.0 + */ +TEST_CASE("Unit_hipMemPoolSetAttribute_EventDependencies") { + int device_id = 0; + HIP_CHECK(hipSetDevice(device_id)); + + int mem_pool_support = 0; + HIP_CHECK(hipDeviceGetAttribute(&mem_pool_support, hipDeviceAttributeMemoryPoolsSupported, 0)); + if (!mem_pool_support) { + SUCCEED("Runtime doesn't support Memory Pool. Skip the test case."); + return; + } + + MemPoolGuard mempool(MemPools::created, device_id); + + hipMemPoolAttr attr; + int blocks = 2; + int clk_rate; + if (IsGfx11()) { + HIPCHECK(hipDeviceGetAttribute(&clk_rate, hipDeviceAttributeWallClockRate, 0)); + } else { + HIPCHECK(hipDeviceGetAttribute(&clk_rate, hipDeviceAttributeClockRate, 0)); + } + + int *alloc_mem1, *alloc_mem2, *alloc_mem3; + + // Create 2 async non-blocking streams + StreamGuard stream1(Streams::withFlags, hipStreamNonBlocking); + StreamGuard stream2(Streams::withFlags, hipStreamNonBlocking); + + hipEvent_t event; + HIP_CHECK(hipEventCreate(&event)); + + size_t allocation_size = kPageSize; + HIP_CHECK(hipMallocFromPoolAsync(reinterpret_cast(&alloc_mem3), allocation_size, + mempool.mempool(), stream1.stream())); + int value = 0; + + SECTION("Allow Event Dependencies - Reuse") { + allocation_size = kPageSize * kPageSize * 2; + HIP_CHECK(hipMallocFromPoolAsync(reinterpret_cast(&alloc_mem1), allocation_size, + mempool.mempool(), stream1.stream())); + + value = 1; + attr = hipMemPoolReuseFollowEventDependencies; + // Enable Opportunistic + HIP_CHECK(hipMemPoolSetAttribute(mempool.mempool(), attr, &value)); + + // Run kernel for 500 ms in the first stream + if (IsGfx11()) { + HIP_CHECK(hipDeviceGetAttribute(&clk_rate, hipDeviceAttributeWallClockRate, 0)); + kernel_500ms_gfx11<<<32, blocks, 0, stream1.stream()>>>(alloc_mem1, clk_rate); + } else { + HIP_CHECK(hipDeviceGetAttribute(&clk_rate, hipDeviceAttributeClockRate, 0)); + kernel_500ms<<<32, blocks, 0, stream1.stream()>>>(alloc_mem1, clk_rate); + } + + // Not a real free, since kernel isn't done + HIP_CHECK(hipFreeAsync(reinterpret_cast(alloc_mem1), stream1.stream())); + + HIP_CHECK(hipEventRecord(event, stream1.stream())); + HIP_CHECK(hipStreamWaitEvent(stream2.stream(), event, 0)); + + // Allocate memory for the second stream + HIP_CHECK(hipMallocFromPoolAsync(reinterpret_cast(&alloc_mem2), allocation_size, + mempool.mempool(), stream2.stream())); + // With Opportunistic state runtime will reuse freed buffer A + REQUIRE(alloc_mem1 == alloc_mem2); + + // Run kernel with the new memory in the second stream + if (IsGfx11()) { + kernel_500ms_gfx11<<<32, blocks, 0, stream2.stream()>>>(alloc_mem2, clk_rate); + } else { + kernel_500ms<<<32, blocks, 0, stream2.stream()>>>(alloc_mem2, clk_rate); + } + + HIP_CHECK(hipStreamSynchronize(stream1.stream())); + HIP_CHECK(hipStreamSynchronize(stream2.stream())); + + HIP_CHECK(hipFreeAsync(reinterpret_cast(alloc_mem2), stream2.stream())); + } + + SECTION("Disallow Event Dependencies - No Reuse") { + allocation_size = kPageSize * kPageSize * 2; + HIP_CHECK(hipMallocFromPoolAsync(reinterpret_cast(&alloc_mem1), allocation_size, + mempool.mempool(), stream1.stream())); + + value = 0; + attr = hipMemPoolReuseFollowEventDependencies; + // Enable Opportunistic + HIP_CHECK(hipMemPoolSetAttribute(mempool.mempool(), attr, &value)); + + // Run kernel for 500 ms in the first stream + + if (IsGfx11()) { + kernel_500ms_gfx11<<<32, blocks, 0, stream1.stream()>>>(alloc_mem1, clk_rate); + } else { + kernel_500ms<<<32, blocks, 0, stream1.stream()>>>(alloc_mem1, clk_rate); + } + + // Not a real free, since kernel isn't done + HIP_CHECK(hipFreeAsync(reinterpret_cast(alloc_mem1), stream1.stream())); + + HIP_CHECK(hipEventRecord(event, stream1.stream())); + HIP_CHECK(hipStreamWaitEvent(stream2.stream(), event, 0)); + + // Allocate memory for the second stream + HIP_CHECK(hipMallocFromPoolAsync(reinterpret_cast(&alloc_mem2), allocation_size, + mempool.mempool(), stream2.stream())); + // With Opportunistic state runtime can't reuse freed buffer A, because it's still busy with the + // kernel + REQUIRE(alloc_mem1 != alloc_mem2); + + // Run kernel with the new memory in the second stream + if (IsGfx11()) { + kernel_500ms_gfx11<<<32, blocks, 0, stream2.stream()>>>(alloc_mem2, clk_rate); + } else { + kernel_500ms<<<32, blocks, 0, stream2.stream()>>>(alloc_mem2, clk_rate); + } + + HIP_CHECK(hipStreamSynchronize(stream1.stream())); + HIP_CHECK(hipStreamSynchronize(stream2.stream())); + + HIP_CHECK(hipFreeAsync(reinterpret_cast(alloc_mem2), stream2.stream())); + } + + HIP_CHECK(hipFreeAsync(reinterpret_cast(alloc_mem3), stream1.stream())); + HIP_CHECK(hipEventDestroy(event)); +} + +/** + * Test Description + * ------------------------ + * - Test to verify hipMemPoolSetAttribute behavior with invalid arguments: + * -# Nullptr mem_pool + * -# Attribute value is not valid + * -# Nullptr value + * -# hipMemPoolAttrReservedMemHigh set to non-zero + * -# IhipMemPoolAttrUsedMemHigh set to non-zero + * + * Test source + * ------------------------ + * - /unit/memory/hipMemPoolSetGetAttribute.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 6.0 + */ +TEST_CASE("Unit_hipMemPoolSetAttribute_Negative_Parameters") { + int device_id = 0; + HIP_CHECK(hipSetDevice(device_id)); + + MemPoolGuard mempool(MemPools::dev_default, device_id); + + hipMemPoolAttr attr = hipMemPoolReuseFollowEventDependencies; + int set_value = 0; + std::uint64_t set_value64 = 0; + + SECTION("Mempool is nullptr") { + HIP_CHECK_ERROR(hipMemPoolSetAttribute(nullptr, attr, &set_value), hipErrorInvalidValue); + } + + SECTION("Attribute value is not valid") { + HIP_CHECK_ERROR( + hipMemPoolSetAttribute(mempool.mempool(), static_cast(0x9), &set_value), + hipErrorInvalidValue); + } +#if HT_AMD + SECTION("Set values is nullptr") { + HIP_CHECK_ERROR(hipMemPoolSetAttribute(mempool.mempool(), attr, nullptr), hipErrorInvalidValue); + } +#endif + + SECTION("Set hipMemPoolAttrReservedMemHigh to non-zero") { + hipMemPoolAttr attr = hipMemPoolAttrReservedMemHigh; + set_value64 = 1; + HIP_CHECK_ERROR((hipMemPoolSetAttribute(mempool.mempool(), attr, &set_value64)), + hipErrorInvalidValue); + } + + SECTION("Set hipMemPoolAttrUsedMemHigh to non-zero") { + hipMemPoolAttr attr = hipMemPoolAttrUsedMemHigh; + set_value64 = 1; + HIP_CHECK_ERROR((hipMemPoolSetAttribute(mempool.mempool(), attr, &set_value64)), + hipErrorInvalidValue); + } +} + +/** + * End doxygen group hipMemPoolSetAttribute. + * @} + */ + +/** + * @addtogroup hipMemPoolGetAttribute hipMemPoolGetAttribute + * @{ + * @ingroup StreamOTest + * `hipMemPoolGetAttribute(hipMemPool_t mem_pool, hipMemPoolAttr attr, void* value)` + * - Gets attributes of a memory pool + */ + +/** + * Test Description + * ------------------------ + * - Test to verify hipMemPoolGetAttribute behavior with invalid arguments: + * -# Nullptr mem_pool + * -# Attribute value is not valid + * -# Nullptr value + * + * Test source + * ------------------------ + * - /unit/memory/hipMemPoolSetGetAttribute.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 6.0 + */ +TEST_CASE("Unit_hipMemPoolGetAttribute_Negative_Parameters") { + int device_id = 0; + HIP_CHECK(hipSetDevice(device_id)); + + MemPoolGuard mempool(MemPools::dev_default, device_id); + + + hipMemPoolAttr attr = hipMemPoolReuseFollowEventDependencies; + int get_value = 0; + + SECTION("Mempool is nullptr") { + HIP_CHECK_ERROR(hipMemPoolGetAttribute(nullptr, attr, &get_value), hipErrorInvalidValue); + } + + SECTION("Attribute value is not valid") { + HIP_CHECK_ERROR( + hipMemPoolGetAttribute(mempool.mempool(), static_cast(0x9), &get_value), + hipErrorInvalidValue); + } + + SECTION("Get values is nullptr") { + HIP_CHECK_ERROR(hipMemPoolGetAttribute(mempool.mempool(), attr, nullptr), hipErrorInvalidValue); + } +} diff --git a/catch/unit/memory/hipMemPoolTrimTo.cc b/catch/unit/memory/hipMemPoolTrimTo.cc new file mode 100644 index 0000000000..6decc1c11f --- /dev/null +++ b/catch/unit/memory/hipMemPoolTrimTo.cc @@ -0,0 +1,165 @@ +/* + Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. + 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 WARRANNTY OF ANY KIND, EXPRESS OR + IMPLIED, INNCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANNY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER INN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR INN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + */ + + +#include "mempool_common.hh" + +#include +#include + +/** + * @addtogroup hipMemPoolTrimTo hipMemPoolTrimTo + * @{ + * @ingroup StreamOTest + * `hipMemPoolTrimTo(hipMemPool_t mem_pool, size_t min_bytes_to_hold)` - + * Releases freed memory back to the OS + */ + + +/** + * Test Description + * ------------------------ + * - Test to verify hipMemPoolTrimTo behavior with invalid arguments: + * -# Nullptr mem_pool + * + * Test source + * ------------------------ + * - /unit/memory/hipMemPoolTrimTo.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 6.0 + */ +TEST_CASE("Unit_hipMemPoolTrimTo_Negative_Parameter") { + int device_id = 0; + HIP_CHECK(hipSetDevice(device_id)); + + int mem_pool_support = 0; + HIP_CHECK(hipDeviceGetAttribute(&mem_pool_support, hipDeviceAttributeMemoryPoolsSupported, 0)); + if (!mem_pool_support) { + SUCCEED("Runtime doesn't support Memory Pool. Skip the test case."); + return; + } + + size_t trim_size = 1024; + + SECTION("Passing nullptr to mem_pool") { + HIP_CHECK_ERROR(hipMemPoolTrimTo(nullptr, trim_size), hipErrorInvalidValue); + } +} + + +/** + * Test Description + * ------------------------ + * - Basic test to verify hipMemPoolTrimTo releases memory correctly to the OS. + * Test source + * ------------------------ + * - /unit/memory/hipMemPoolTrimTo.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 6.0 + */ +TEST_CASE("Unit_hipMemPoolTrimTo_Positive_Basic") { + int device_id = 0; + HIP_CHECK(hipSetDevice(device_id)); + + int mem_pool_support = 0; + HIP_CHECK(hipDeviceGetAttribute(&mem_pool_support, hipDeviceAttributeMemoryPoolsSupported, 0)); + if (!mem_pool_support) { + SUCCEED("Runtime doesn't support Memory Pool. Skip the test case."); + return; + } + + const size_t allocation_size1 = kPageSize * kPageSize * 2; + const size_t allocation_size2 = kPageSize / 2; + MemPoolGuard mempool(MemPools::created, device_id); + + int* alloc_mem1; + int* alloc_mem2; + StreamGuard stream(Streams::created); + + HIP_CHECK(hipMallocFromPoolAsync(reinterpret_cast(&alloc_mem1), allocation_size1, + mempool.mempool(), stream.stream())); + HIP_CHECK(hipMallocFromPoolAsync(reinterpret_cast(&alloc_mem2), allocation_size2, + mempool.mempool(), stream.stream())); + + int blocks = 2; + int clk_rate; + if (IsGfx11()) { + HIP_CHECK(hipDeviceGetAttribute(&clk_rate, hipDeviceAttributeWallClockRate, 0)); + kernel_500ms_gfx11<<<32, blocks, 0, stream.stream()>>>(alloc_mem1, clk_rate); + } else { + HIP_CHECK(hipDeviceGetAttribute(&clk_rate, hipDeviceAttributeClockRate, 0)); + + kernel_500ms<<<32, blocks, 0, stream.stream()>>>(alloc_mem1, clk_rate); + } + + hipMemPoolAttr attr; + attr = hipMemPoolAttrReleaseThreshold; + // The pool must hold 128MB + std::uint64_t threshold = 128 * 1024 * 1024; + HIP_CHECK(hipMemPoolSetAttribute(mempool.mempool(), attr, &threshold)); + + // Not a real free, since kernel isn't done + HIP_CHECK(hipFreeAsync(reinterpret_cast(alloc_mem1), stream.stream())); + + // Get reserved memory before trim + attr = hipMemPoolAttrReservedMemCurrent; + std::uint64_t res_before_trim = 0; + HIP_CHECK(hipMemPoolGetAttribute(mempool.mempool(), attr, &res_before_trim)); + + size_t min_bytes_to_hold = allocation_size2; + HIP_CHECK(hipMemPoolTrimTo(mempool.mempool(), min_bytes_to_hold)); + + std::uint64_t res_after_trim = 0; + HIP_CHECK(hipMemPoolGetAttribute(mempool.mempool(), attr, &res_after_trim)); + // Trim must be a nop because execution isn't done + REQUIRE(res_before_trim == res_after_trim); + + HIP_CHECK(hipStreamSynchronize(stream.stream())); + + std::uint64_t res_after_sync = 0; + HIP_CHECK(hipMemPoolGetAttribute(mempool.mempool(), attr, &res_after_sync)); + // Since hipMemPoolAttrReleaseThreshold is 128 MB sync does nothing to the freed memory + REQUIRE(res_after_trim == res_after_sync); + + HIP_CHECK(hipMemPoolTrimTo(mempool.mempool(), min_bytes_to_hold)); + + HIP_CHECK(hipMemPoolGetAttribute(mempool.mempool(), attr, &res_after_trim)); + // Validate memory after real trim. The pool must hold less memory than before + REQUIRE(res_after_trim < res_after_sync); + + attr = hipMemPoolAttrReleaseThreshold; + std::uint64_t value64 = 0; + HIP_CHECK(hipMemPoolGetAttribute(mempool.mempool(), attr, &value64)); + // Make sure the threshold query works + REQUIRE(threshold == value64); + + attr = hipMemPoolAttrUsedMemCurrent; + HIP_CHECK(hipMemPoolGetAttribute(mempool.mempool(), attr, &value64)); + // Make sure the current usage query works - just small buffer left + REQUIRE(allocation_size2 == value64); + + attr = hipMemPoolAttrUsedMemHigh; + HIP_CHECK(hipMemPoolGetAttribute(mempool.mempool(), attr, &value64)); + // Make sure the high watermark usage works - the both buffers must be reported + REQUIRE((allocation_size1 + allocation_size2) == value64); + + HIP_CHECK(hipFreeAsync(reinterpret_cast(alloc_mem2), stream.stream())); +} diff --git a/catch/unit/memory/mempool_common.hh b/catch/unit/memory/mempool_common.hh new file mode 100644 index 0000000000..9bd95f0511 --- /dev/null +++ b/catch/unit/memory/mempool_common.hh @@ -0,0 +1,292 @@ +/* + Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. + 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 WARRANNTY OF ANY KIND, EXPRESS OR + IMPLIED, INNCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANNY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER INN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR INN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + */ +#pragma once + +#include +#include +#include + +namespace { +constexpr hipMemPoolProps kPoolProps = { + hipMemAllocationTypePinned, hipMemHandleTypeNone, {hipMemLocationTypeDevice, 0}, nullptr, {0}}; + +constexpr auto wait_ms = 500; +} // anonymous namespace + + +template __global__ void kernel_500ms(T* host_res, int clk_rate) { + int tid = threadIdx.x + blockIdx.x * blockDim.x; + host_res[tid] = tid + 1; + __threadfence_system(); + // expecting that the data is getting flushed to host here! + uint64_t start = clock64() / clk_rate, cur; + if (clk_rate > 1) { + do { + cur = clock64() / clk_rate - start; + } while (cur < wait_ms); + } else { + do { + cur = clock64() / start; + } while (cur < wait_ms); + } +} + +template __global__ void kernel_500ms_gfx11(T* host_res, int clk_rate) { +#if HT_AMD + int tid = threadIdx.x + blockIdx.x * blockDim.x; + host_res[tid] = tid + 1; + __threadfence_system(); + // expecting that the data is getting flushed to host here! + uint64_t start = wall_clock64() / clk_rate, cur; + if (clk_rate > 1) { + do { + cur = wall_clock64() / clk_rate - start; + } while (cur < wait_ms); + } else { + do { + cur = wall_clock64() / start; + } while (cur < wait_ms); + } +#endif +} + +template void MallocMemPoolAsync_OneAlloc(F malloc_func, const MemPools mempool_type) { + int device_id = 0; + HIP_CHECK(hipSetDevice(device_id)); + + int mem_pool_support = 0; + HIP_CHECK(hipDeviceGetAttribute(&mem_pool_support, hipDeviceAttributeMemoryPoolsSupported, 0)); + if (!mem_pool_support) { + SUCCEED("Runtime doesn't support Memory Pool. Skip the test case."); + return; + } + + const auto allocation_size = GENERATE(kPageSize / 2, kPageSize, kPageSize * 2); + LinearAllocGuard host_alloc(LinearAllocs::hipHostMalloc, allocation_size); + MemPoolGuard mempool(mempool_type, device_id); + + int* alloc_mem; + StreamGuard stream(Streams::created); + + HIP_CHECK(malloc_func(reinterpret_cast(&alloc_mem), allocation_size, mempool.mempool(), + stream.stream())); + + int blocks = 1024; + int clk_rate; + hipMemPoolAttr attr; + if (IsGfx11()) { + HIP_CHECK(hipDeviceGetAttribute(&clk_rate, hipDeviceAttributeWallClockRate, 0)); + kernel_500ms_gfx11<<<32, blocks, 0, stream.stream()>>>(alloc_mem, clk_rate); + } else { + HIP_CHECK(hipDeviceGetAttribute(&clk_rate, hipDeviceAttributeClockRate, 0)); + + kernel_500ms<<<32, blocks, 0, stream.stream()>>>(alloc_mem, clk_rate); + } + + const auto element_count = allocation_size / sizeof(int); + constexpr auto thread_count = 1024; + const auto block_count = element_count / thread_count + 1; + constexpr int expected_value = 17; + VectorSet<<>>(alloc_mem, expected_value, + element_count); + + HIP_CHECK(hipMemcpyAsync(host_alloc.host_ptr(), alloc_mem, allocation_size, hipMemcpyDeviceToHost, + stream.stream())); + + HIP_CHECK(hipFreeAsync(reinterpret_cast(alloc_mem), stream.stream())); + + attr = hipMemPoolAttrReservedMemCurrent; + std::uint64_t res_before_sync = 0; + HIP_CHECK(hipMemPoolGetAttribute(mempool.mempool(), attr, &res_before_sync)); + HIP_CHECK(hipStreamSynchronize(stream.stream())); + + std::uint64_t res_after_sync = 0; + HIP_CHECK(hipMemPoolGetAttribute(mempool.mempool(), attr, &res_after_sync)); + // Sync must release memory to OS + REQUIRE(res_after_sync <= res_before_sync); + + std::uint64_t used_mem = 10; + attr = hipMemPoolAttrUsedMemCurrent; + HIP_CHECK(hipMemPoolGetAttribute(mempool.mempool(), attr, &used_mem)); + REQUIRE(0 == used_mem); + + ArrayFindIfNot(host_alloc.host_ptr(), expected_value, element_count); +} + +template +void MallocMemPoolAsync_TwoAllocs(F malloc_func, const MemPools mempool_type) { + int device_id = 0; + HIP_CHECK(hipSetDevice(device_id)); + + int mem_pool_support = 0; + HIP_CHECK(hipDeviceGetAttribute(&mem_pool_support, hipDeviceAttributeMemoryPoolsSupported, 0)); + if (!mem_pool_support) { + SUCCEED("Runtime doesn't support Memory Pool. Skip the test case."); + return; + } + + const auto allocation_size = GENERATE(kPageSize / 2, kPageSize, kPageSize * 2); + LinearAllocGuard host_alloc(LinearAllocs::hipHostMalloc, allocation_size); + MemPoolGuard mempool(mempool_type, device_id); + + int* alloc_mem1; + int* alloc_mem2; + StreamGuard stream(Streams::created); + + HIP_CHECK(malloc_func(reinterpret_cast(&alloc_mem1), allocation_size, mempool.mempool(), + stream.stream())); + HIP_CHECK(malloc_func(reinterpret_cast(&alloc_mem2), allocation_size, mempool.mempool(), + stream.stream())); + + int blocks = 1024; + int clk_rate; + hipMemPoolAttr attr; + if (IsGfx11()) { + HIP_CHECK(hipDeviceGetAttribute(&clk_rate, hipDeviceAttributeWallClockRate, 0)); + kernel_500ms_gfx11<<<32, blocks, 0, stream.stream()>>>(alloc_mem1, clk_rate); + } else { + HIP_CHECK(hipDeviceGetAttribute(&clk_rate, hipDeviceAttributeClockRate, 0)); + + kernel_500ms<<<32, blocks, 0, stream.stream()>>>(alloc_mem1, clk_rate); + } + + const auto element_count = allocation_size / sizeof(int); + constexpr auto thread_count = 1024; + const auto block_count = element_count / thread_count + 1; + constexpr int expected_value = 17; + VectorSet<<>>(alloc_mem1, expected_value, + element_count); + HIP_CHECK(hipGetLastError()); + + HIP_CHECK(hipMemcpyAsync(alloc_mem2, alloc_mem1, allocation_size, hipMemcpyDeviceToDevice, + stream.stream())); + + HIP_CHECK(hipMemcpyAsync(host_alloc.host_ptr(), alloc_mem2, allocation_size, + hipMemcpyDeviceToHost, stream.stream())); + + HIP_CHECK(hipFreeAsync(reinterpret_cast(alloc_mem1), stream.stream())); + + attr = hipMemPoolAttrReservedMemCurrent; + std::uint64_t res_before_sync = 0; + HIP_CHECK(hipMemPoolGetAttribute(mempool.mempool(), attr, &res_before_sync)); + HIP_CHECK(hipStreamSynchronize(stream.stream())); + + std::uint64_t res_after_sync = 0; + HIP_CHECK(hipMemPoolGetAttribute(mempool.mempool(), attr, &res_after_sync)); + // Sync must release memory to OS + REQUIRE(res_after_sync <= res_before_sync); + + std::uint64_t used_mem = 0; + attr = hipMemPoolAttrUsedMemCurrent; + HIP_CHECK(hipMemPoolGetAttribute(mempool.mempool(), attr, &used_mem)); + // Make sure the current usage query works - just second buffer is left + REQUIRE(allocation_size == used_mem); + + attr = hipMemPoolAttrUsedMemHigh; + HIP_CHECK(hipMemPoolGetAttribute(mempool.mempool(), attr, &used_mem)); + // Make sure the high watermark usage works - both buffers must be reported + REQUIRE((2 * allocation_size) == used_mem); + + HIP_CHECK(hipFreeAsync(reinterpret_cast(alloc_mem2), stream.stream())); + HIP_CHECK(hipStreamSynchronize(stream.stream())); + + attr = hipMemPoolAttrUsedMemCurrent; + HIP_CHECK(hipMemPoolGetAttribute(mempool.mempool(), attr, &used_mem)); + // Make sure the current usage query works - none of the buffers are used + REQUIRE(0 == used_mem); + + ArrayFindIfNot(host_alloc.host_ptr(), expected_value, element_count); +} + +template void MallocMemPoolAsync_Reuse(F malloc_func, const MemPools mempool_type) { + int device_id = 0; + HIP_CHECK(hipSetDevice(device_id)); + + int mem_pool_support = 0; + HIP_CHECK(hipDeviceGetAttribute(&mem_pool_support, hipDeviceAttributeMemoryPoolsSupported, 0)); + if (!mem_pool_support) { + SUCCEED("Runtime doesn't support Memory Pool. Skip the test case."); + return; + } + + MemPoolGuard mempool(mempool_type, device_id); + + int *alloc_mem1, *alloc_mem2, *alloc_mem3; + StreamGuard stream(Streams::created); + + size_t allocation_size1 = kPageSize * kPageSize * 2; + HIP_CHECK(malloc_func(reinterpret_cast(&alloc_mem1), allocation_size1, mempool.mempool(), + stream.stream())); + + size_t allocation_size2 = kPageSize; + HIP_CHECK(malloc_func(reinterpret_cast(&alloc_mem3), allocation_size2, mempool.mempool(), + stream.stream())); + + int blocks = 2; + int clk_rate; + + if (IsGfx11()) { + HIP_CHECK(hipDeviceGetAttribute(&clk_rate, hipDeviceAttributeWallClockRate, 0)); + kernel_500ms_gfx11<<<32, blocks, 0, stream.stream()>>>(alloc_mem1, clk_rate); + } else { + HIP_CHECK(hipDeviceGetAttribute(&clk_rate, hipDeviceAttributeClockRate, 0)); + + kernel_500ms<<<32, blocks, 0, stream.stream()>>>(alloc_mem1, clk_rate); + } + + hipMemPoolAttr attr; + // Not a real free, since kernel isn't done + HIP_CHECK(hipFreeAsync(reinterpret_cast(alloc_mem1), stream.stream())); + + HIP_CHECK(malloc_func(reinterpret_cast(&alloc_mem2), allocation_size1, mempool.mempool(), + stream.stream())); + // Runtime must reuse the pointer + REQUIRE(alloc_mem1 == alloc_mem2); + + // Make a sync before the second kernel launch to make sure memory B isn't gone + HIP_CHECK(hipStreamSynchronize(stream.stream())); + + // Second kernel launch with new memory + if (IsGfx11()) { + kernel_500ms_gfx11<<<32, blocks, 0, stream.stream()>>>(alloc_mem2, clk_rate); + } else { + kernel_500ms<<<32, blocks, 0, stream.stream()>>>(alloc_mem2, clk_rate); + } + + HIP_CHECK(hipStreamSynchronize(stream.stream())); + + attr = hipMemPoolAttrUsedMemCurrent; + std::uint64_t value64 = 0; + HIP_CHECK(hipMemPoolGetAttribute(mempool.mempool(), attr, &value64)); + // Make sure the current usage reports the both buffers + REQUIRE((allocation_size1 + allocation_size2) == value64); + + attr = hipMemPoolAttrUsedMemHigh; + HIP_CHECK(hipMemPoolGetAttribute(mempool.mempool(), attr, &value64)); + // Make sure the high watermark usage works - the both buffers must be reported + REQUIRE((allocation_size1 + allocation_size2) == value64); + + HIP_CHECK(hipFreeAsync(reinterpret_cast(alloc_mem2), stream.stream())); + attr = hipMemPoolAttrUsedMemCurrent; + HIP_CHECK(hipMemPoolGetAttribute(mempool.mempool(), attr, &value64)); + // Make sure the current usage reports just one buffer, because the above free doesn't hold memory + REQUIRE(allocation_size2 == value64); + + HIP_CHECK(hipFreeAsync(reinterpret_cast(alloc_mem3), stream.stream())); +} diff --git a/catch/unit/printf/CMakeLists.txt b/catch/unit/printf/CMakeLists.txt index 17e4b87876..992326b356 100644 --- a/catch/unit/printf/CMakeLists.txt +++ b/catch/unit/printf/CMakeLists.txt @@ -1,13 +1,23 @@ # AMD Tests set(TEST_SRC printfFlags.cc + printfLength.cc printfSpecifiers.cc printfFlagsNonHost.cc printfSpecifiersNonHost.cc printfHost.cc ) -set(AMD_TEST_SRC - printfNonHost.cc) + +if(HIP_PLATFORM MATCHES "nvidia") + set(LINKER_LIBS nvrtc) +elseif(HIP_PLATFORM MATCHES "amd") + set(LINKER_LIBS hiprtc) +endif() + +if(UNIX) + set(AMD_TEST_SRC + printfNonHost.cc) +endif() if(HIP_PLATFORM MATCHES "amd") set(TEST_SRC ${TEST_SRC} ${AMD_TEST_SRC}) @@ -20,24 +30,34 @@ if(HIP_PLATFORM MATCHES "amd") endif() if(HIP_PLATFORM MATCHES "amd") - hip_add_exe_to_target(NAME printfTests + hip_add_exe_to_target(NAME PrintfTest TEST_SRC ${TEST_SRC} TEST_TARGET_NAME build_tests + LINKER_LIBS ${LINKER_LIBS} PROPERTY CXX_STANDARD 17) elseif (HIP_PLATFORM MATCHES "nvidia") - hip_add_exe_to_target(NAME printfTests + hip_add_exe_to_target(NAME PrintfTest TEST_SRC ${TEST_SRC} TEST_TARGET_NAME build_tests + LINKER_LIBS ${LINKER_LIBS} COMPILE_OPTIONS -std=c++17) endif() +# This test fails in PSDB +#add_test(NAME Unit_Printf_Negative +# COMMAND python3 ${CMAKE_CURRENT_SOURCE_DIR}/../compileAndCaptureOutput.py +# ${CMAKE_CURRENT_SOURCE_DIR} ${HIP_PLATFORM} ${HIP_PATH} +# printf_negative_kernels.cc 11) + # Standalone exes add_executable(printfFlags_exe EXCLUDE_FROM_ALL printfFlags_exe.cc) +add_executable(printfLength_exe EXCLUDE_FROM_ALL printfLength_exe.cc) add_executable(printfSpecifiers_exe EXCLUDE_FROM_ALL printfSpecifiers_exe.cc) add_executable(printfFlagsNonHost_exe EXCLUDE_FROM_ALL printfFlagsNonHost_exe.cc) add_executable(printfSpecifiersNonHost_exe EXCLUDE_FROM_ALL printfSpecifiersNonHost_exe.cc) add_dependencies(build_tests printfFlags_exe) +add_dependencies(build_tests printfLength_exe) add_dependencies(build_tests printfSpecifiers_exe) add_dependencies(build_tests printfFlagsNonHost_exe) add_dependencies(build_tests printfSpecifiersNonHost_exe) diff --git a/catch/unit/printf/printfFlags.cc b/catch/unit/printf/printfFlags.cc index 8fa5181ba2..58efa857b3 100644 --- a/catch/unit/printf/printfFlags.cc +++ b/catch/unit/printf/printfFlags.cc @@ -1,5 +1,5 @@ /* -Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved. +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -23,7 +23,28 @@ THE SOFTWARE. #include #include -TEST_CASE("Unit_printf_flags") { +/** +* @addtogroup printf printf +* @{ +* @ingroup PrintfTest +* `int printf()` - +* Method to print the content on output device. +*/ + + +/** + * Test Description + * ------------------------ + * - Sanity test for `printf(format, ...)` to check all format specifier flags. + * + * Test source + * ------------------------ + * - unit/printf/printfFlags.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_Printf_flags_Sanity_Positive") { std::string reference(R"here(00000042 -0000042 00000042 @@ -33,8 +54,19 @@ TEST_CASE("Unit_printf_flags") { +0000042 xyzzy -42 + 42 00000042 00000042 +052 +0x2a +0X2A +42.000000 +4.200000e+01 +4.200000E+01 +42.0000 +42.0000 +0x1.5p+5 +0X1.5P+5 )here"); hip::SpawnProc proc("printfFlags_exe", true); diff --git a/catch/unit/printf/printfFlagsNonHost.cc b/catch/unit/printf/printfFlagsNonHost.cc index cea2ea6072..75f5ef7c35 100644 --- a/catch/unit/printf/printfFlagsNonHost.cc +++ b/catch/unit/printf/printfFlagsNonHost.cc @@ -21,7 +21,7 @@ THE SOFTWARE. #include /** -* @addtogroup printf +* @addtogroup printf printf * @{ * @ingroup PrintfTest * `int printf()` - diff --git a/catch/unit/printf/printfFlags_exe.cc b/catch/unit/printf/printfFlags_exe.cc index 6ce9691b17..957b2415cf 100644 --- a/catch/unit/printf/printfFlags_exe.cc +++ b/catch/unit/printf/printfFlags_exe.cc @@ -1,5 +1,5 @@ /* -Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved. +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -20,7 +20,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include +#include __global__ void test_kernel() { printf("%08d\n", 42); @@ -32,8 +32,23 @@ __global__ void test_kernel() { printf("%+08d\n", 42); printf("%-8s\n", "xyzzy"); printf("% i\n", -42); + printf("% i\n", 42); printf("%-16.8d\n", 42); printf("%16.8d\n", 42); + printf("%#o\n", 42); + printf("%#x\n", 42); + printf("%#X\n", 42); +#if HT_AMD + printf("%#F\n", 42.); +#else + printf("%#f\n", 42.); +#endif + printf("%#e\n", 42.); + printf("%#E\n", 42.); + printf("%#g\n", 42.); + printf("%#G\n", 42.); + printf("%#a\n", 42.); + printf("%#A\n", 42.); } int main() { diff --git a/catch/unit/printf/printfHost.cc b/catch/unit/printf/printfHost.cc index bd226d3fd4..3456d63d21 100644 --- a/catch/unit/printf/printfHost.cc +++ b/catch/unit/printf/printfHost.cc @@ -26,7 +26,7 @@ __global__ void run_printf(int *count) { *count = printf("Hello World"); } /** -* @addtogroup printf +* @addtogroup printf printf * @{ * @ingroup PrintfTest * `int printf()` - diff --git a/catch/unit/printf/printfLength.cc b/catch/unit/printf/printfLength.cc new file mode 100644 index 0000000000..7fce68bac3 --- /dev/null +++ b/catch/unit/printf/printfLength.cc @@ -0,0 +1,87 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. + +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 +#include + +/** +* @addtogroup printf printf +* @{ +* @ingroup PrintfTest +* `int printf()` - +* Method to print the content on output device. +*/ + +/** + * Test Description + * ------------------------ + * - Sanity test for `printf(format, ...)` to check all format specifier length sub-specifiers. + * + * Test source + * ------------------------ + * - unit/printf/printfLength.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_Printf_length_Sanity_Positive") { +#if HT_NVIDIA + std::string reference(R"here(-42 -42 +-42 -42 +-42 -42 +42 52 +42 52 +42 52 +2a 2A +2a 2A +2a 2A +123.456000 +x +)here"); +#else + std::string reference(R"here(-42 -42 +-42 -42 +-42 -42 +42 52 +42 52 +42 52 +2a 2A +2a 2A +2a 2A +123.456000 +x +123.456000 +-42 -42 +-42 -42 +-42 -42 +0 0 +42 52 +42 52 +42 52 +0 0 +)here"); +#endif + + hip::SpawnProc proc("printfLength_exe", true); + REQUIRE(0 == proc.run()); + REQUIRE(proc.getOutput() == reference); +} diff --git a/catch/unit/printf/printfLength_exe.cc b/catch/unit/printf/printfLength_exe.cc new file mode 100644 index 0000000000..d114a4a28b --- /dev/null +++ b/catch/unit/printf/printfLength_exe.cc @@ -0,0 +1,62 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. + +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 + +#if defined(_WIN32) +#if defined(_WIN64) +typedef __int64 ssize_t; +#else // !_WIN64 +typedef __int32 ssize_t; +#endif // !_WIN64 +#endif /*_WIN32*/ + +__global__ void test_kernel() { + printf("%hd %hi\n", short(-42), short(-42)); + printf("%ld %li\n", -42l, -42l); + printf("%lld %lli\n", -42ll, -42ll); + printf("%hu %ho\n", ushort(42), ushort(42)); + printf("%lu %lo\n", 42l, 42l); + printf("%llu %llo\n", 42ll, 42ll); + printf("%hx %hX\n", ushort(42), ushort(42)); + printf("%lx %lX\n", 42l, 42l); + printf("%llx %llX\n", 42ll, 42ll); + printf("%lf\n", 123.456); + printf("%lc\n", wint_t ('x')); +#if HT_AMD + const char* N = nullptr; + printf("%lF\n", 123.456); + printf("%hhd %hhi\n", char(-42), char(-42)); + printf("%jd %ji\n", intmax_t(-42l), intmax_t(-42l)); + printf("%zd %zi\n", ssize_t(-42l), ssize_t(-42l)); + printf("%td %ti\n", (ptrdiff_t)N, (ptrdiff_t)N); + printf("%hhu %hho\n", static_cast(42), static_cast(42)); + printf("%ju %jo\n", uintmax_t(42l), uintmax_t(42l)); + printf("%zu %zo\n", size_t(42l), size_t(42l)); + printf("%tu %to\n", (ptrdiff_t)N, (ptrdiff_t)N); +#endif +} + +int main() { + test_kernel<<<1, 1>>>(); + static_cast(hipDeviceSynchronize()); +} \ No newline at end of file diff --git a/catch/unit/printf/printfNonHost.cc b/catch/unit/printf/printfNonHost.cc index dee16c512f..36bd8e35f6 100644 --- a/catch/unit/printf/printfNonHost.cc +++ b/catch/unit/printf/printfNonHost.cc @@ -45,7 +45,7 @@ __global__ void kernel_printf_thread(int *count) { } /** -* @addtogroup printf +* @addtogroup printf printf * @{ * @ingroup PrintfTest * `int printf()` - diff --git a/catch/unit/printf/printfSpecifiers.cc b/catch/unit/printf/printfSpecifiers.cc index 0f366f8c58..1c76c6f1dc 100644 --- a/catch/unit/printf/printfSpecifiers.cc +++ b/catch/unit/printf/printfSpecifiers.cc @@ -1,5 +1,5 @@ /* -Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved. +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -20,11 +20,34 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include "printf_negative_kernels_rtc.hh" + #include #include -TEST_CASE("Unit_printf_specifier") { -#ifdef __HIP_PLATFORM_NVIDIA__ +/** +* @addtogroup printf printf +* @{ +* @ingroup PrintfTest +* `int printf()` - +* Method to print the content on output device. +*/ + +/** + * Test Description + * ------------------------ + * - Sanity test for `printf(format, ...)` to check all format specifier specifier characters and + * precision/width sub-specifiers. + * + * Test source + * ------------------------ + * - unit/printf/printfSpecifier.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_Printf_specifier_Sanity_Positive") { +#if HT_NVIDIA std::string reference(R"here(xyzzy % hello % world @@ -33,13 +56,19 @@ hello % world %cxyzzy sep -42 +-42 42 +52 +2a +2A 123.456000 -123.456000 -1.234560e+02 1.234560E+02 123.456 -123.456 +0x1.edd2f1a9fbe77p+6 +-0X1.EDD2F1A9FBE77P+6 x (null) (nil) @@ -54,13 +83,19 @@ hello % world %cxyzzy sep -42 +-42 42 +52 +2a +2A 123.456000 -123.456000 -1.234560e+02 1.234560E+02 123.456 -123.456 +0x1.edd2f1a9fbe77p+6 +-0X1.EDD2F1A9FBE77P+6 x (nil) @@ -75,13 +110,19 @@ hello % world %cxyzzy sep -42 +-42 42 +52 +2a +2A 123.456000 -123.456000 -1.234560e+02 1.234560E+02 123.456 -123.456 +0x1.edd2f1a9fbe77p+6 +-0X1.EDD2F1A9FBE77P+6 x 0000000000000000 @@ -93,3 +134,44 @@ x REQUIRE(0 == proc.run()); REQUIRE(proc.getOutput() == reference); } + +/** + * Test Description + * ------------------------ + * - RTCs kernels that pass combinations of arguments of invalid types for printf + * Test source + * ------------------------ + * - unit/printf/printfSpecifiers.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_Printf_Negative_Parameters_RTC") { + hiprtcProgram program{}; + + const auto program_source = kPrintfParam; + + HIPRTC_CHECK( + hiprtcCreateProgram(&program, program_source, "printf_negative.cc", 0, nullptr, nullptr)); + hiprtcResult result{hiprtcCompileProgram(program, 0, nullptr)}; + + // Get the compile log and count compiler error messages + size_t log_size{}; + HIPRTC_CHECK(hiprtcGetProgramLogSize(program, &log_size)); + std::string log(log_size, ' '); + HIPRTC_CHECK(hiprtcGetProgramLog(program, log.data())); + int error_count{0}; + + int expected_error_count{11}; + std::string error_message{"error:"}; + + size_t n_pos = log.find(error_message, 0); + while (n_pos != std::string::npos) { + ++error_count; + n_pos = log.find(error_message, n_pos + 1); + } + + HIPRTC_CHECK(hiprtcDestroyProgram(&program)); + HIPRTC_CHECK_ERROR(result, HIPRTC_ERROR_COMPILATION); + REQUIRE(error_count == expected_error_count); +} diff --git a/catch/unit/printf/printfSpecifiers_exe.cc b/catch/unit/printf/printfSpecifiers_exe.cc index 5dcabdc4d4..07e571d98b 100644 --- a/catch/unit/printf/printfSpecifiers_exe.cc +++ b/catch/unit/printf/printfSpecifiers_exe.cc @@ -1,5 +1,5 @@ /* -Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved. +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -20,7 +20,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include +#include __global__ void test_kernel() { const char* N = nullptr; @@ -36,9 +36,13 @@ __global__ void test_kernel() { printf("%%c%s\n", "xyzzy"); printf("%c%c%c\n", 's', 'e', 'p'); printf("%d\n", -42); + printf("%i\n", -42); printf("%u\n", 42); + printf("%o\n", 42); + printf("%x\n", 42); + printf("%X\n", 42); printf("%f\n", 123.456); -#ifdef __HIP_PLATFORM_AMD__ +#if HT_AMD printf("%F\n", -123.456); #else printf("%f\n", -123.456); @@ -47,10 +51,12 @@ __global__ void test_kernel() { printf("%E\n", 123.456); printf("%g\n", 123.456); printf("%G\n", -123.456); + printf("%a\n", 123.456); + printf("%A\n", -123.456); printf("%c\n", 'x'); printf("%s\n", N); printf("%p\n", (void *)N); -#ifdef __HIP_PLATFORM_AMD__ +#if HT_AMD printf("%.*f %*.*s %p\n", 8, 3.14159, 8, 5, s, (void*)0xf01dab1eca55e77e); #else // In Cuda, printf doesn't support %.*, %*.* diff --git a/catch/unit/printf/printf_negative_kernels.cc b/catch/unit/printf/printf_negative_kernels.cc new file mode 100644 index 0000000000..0fe89ec216 --- /dev/null +++ b/catch/unit/printf/printf_negative_kernels.cc @@ -0,0 +1,41 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. + +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 + +struct Dummy { + __device__ Dummy() {} + __device__ ~Dummy() {} +}; + +/*int printf(T*)*/ +__global__ void printf_n1(int* p) { printf(p); } +__global__ void printf_n2(unsigned int* p) { printf(p); } +__global__ void printf_n3(short* p) { printf(p); } +__global__ void printf_n4(long* p) { printf(p); } +__global__ void printf_n5(unsigned long* p) { printf(p); } +__global__ void printf_n6(long long* p) { printf(p); } +__global__ void printf_n7(unsigned long long* p) { printf(p); } +__global__ void printf_n8(float* p) { printf(p); } +__global__ void printf_n9(double* p) { printf(p); } +__global__ void printf_n10(long double* p) { printf(p); } +__global__ void printf_n11(Dummy* p) { printf(p); } \ No newline at end of file diff --git a/catch/unit/printf/printf_negative_kernels_rtc.hh b/catch/unit/printf/printf_negative_kernels_rtc.hh new file mode 100644 index 0000000000..a264e00f9d --- /dev/null +++ b/catch/unit/printf/printf_negative_kernels_rtc.hh @@ -0,0 +1,42 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. + +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. +*/ + +#pragma once + +static constexpr auto kPrintfParam{ + R"( + struct Dummy { + __device__ Dummy() {} + __device__ ~Dummy() {} + }; + __global__ void printf_n1(int* p) { printf(p); } + __global__ void printf_n2(unsigned int* p) { printf(p); } + __global__ void printf_n3(short* p) { printf(p); } + __global__ void printf_n4(long* p) { printf(p); } + __global__ void printf_n5(unsigned long* p) { printf(p); } + __global__ void printf_n6(long long* p) { printf(p); } + __global__ void printf_n7(unsigned long long* p) { printf(p); } + __global__ void printf_n8(float* p) { printf(p); } + __global__ void printf_n9(double* p) { printf(p); } + __global__ void printf_n10(long double* p) { printf(p); } + __global__ void printf_n11(Dummy* p) { printf(p); } + )"}; \ No newline at end of file diff --git a/catch/unit/surface/CMakeLists.txt b/catch/unit/surface/CMakeLists.txt index 4bc01e0794..d2afb5c702 100644 --- a/catch/unit/surface/CMakeLists.txt +++ b/catch/unit/surface/CMakeLists.txt @@ -23,6 +23,8 @@ set(TEST_SRC hipSurfaceObj1D.cc hipSurfaceObj2D.cc hipSurfaceObj3D.cc + hipCreateSurfaceObject.cc + hipDestroySurfaceObject.cc ) hip_add_exe_to_target(NAME SurfaceTest diff --git a/catch/unit/surface/hipCreateSurfaceObject.cc b/catch/unit/surface/hipCreateSurfaceObject.cc new file mode 100644 index 0000000000..7fcb341385 --- /dev/null +++ b/catch/unit/surface/hipCreateSurfaceObject.cc @@ -0,0 +1,85 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. + +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 + +/** + * @addtogroup hipCreateSurfaceObject hipCreateSurfaceObject + * @{ + * @ingroup SurfaceTest + */ + +/** + * Test Description + * ------------------------ + * - Negative parameters test for `hipCreateSurfaceObject`. + * Test source + * ------------------------ + * - unit/texture/hipCreateSurfaceObject.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.7 + */ +TEST_CASE("Unit_hipCreateSurfaceObject_Negative_Parameters") { + hipArray_t array; + hipChannelFormatDesc desc = hipCreateChannelDesc(); + + HIP_CHECK(hipMallocArray(&array, &desc, 64, 0, hipArraySurfaceLoadStore)); + + hipSurfaceObject_t surf; + + hipResourceDesc resc = {}; + resc.resType = hipResourceTypeArray; + resc.res.array.array = array; + + SECTION("pSurfObject is nullptr") { + HIP_CHECK_ERROR(hipCreateSurfaceObject(nullptr, &resc), hipErrorInvalidValue); + } + + SECTION("pResDesc is nullptr") { + HIP_CHECK_ERROR(hipCreateSurfaceObject(&surf, nullptr), hipErrorInvalidValue); + } + + SECTION("invalid resource type") { + resc.resType = hipResourceTypeLinear; + HIP_CHECK_ERROR(hipCreateSurfaceObject(&surf, &resc), hipErrorInvalidValue); + } + +#if HT_NVIDIA // DIsalbed due to defect EXSWHTEC-366 + SECTION("array handle is nullptr") { + resc.res.array.array = nullptr; + HIP_CHECK_ERROR(hipCreateSurfaceObject(&surf, &resc), hipErrorInvalidHandle); + } +#endif + +#if HT_NVIDIA // Disalbed due to defect EXSWHTEC-367 + SECTION("freed array handle") { + hipArray_t invalid_array; + HIP_CHECK(hipMallocArray(&invalid_array, &desc, 64, 0, hipArraySurfaceLoadStore)); + HIP_CHECK(hipFreeArray(invalid_array)); + resc.res.array.array = invalid_array; + HIP_CHECK_ERROR(hipCreateSurfaceObject(&surf, &resc), hipErrorContextIsDestroyed); + } +#endif + + HIP_CHECK(hipFreeArray(array)); +} \ No newline at end of file diff --git a/catch/unit/surface/hipDestroySurfaceObject.cc b/catch/unit/surface/hipDestroySurfaceObject.cc new file mode 100644 index 0000000000..dc67a24e2a --- /dev/null +++ b/catch/unit/surface/hipDestroySurfaceObject.cc @@ -0,0 +1,66 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. + +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 + +/** + * @addtogroup hipDestroySurfaceObject hipDestroySurfaceObject + * @{ + * @ingroup SurfaceTest + */ + +/** + * Test Description + * ------------------------ + * - Negative parameters test for `hipDestroySurfaceObject`. + * Test source + * ------------------------ + * - unit/texture/hipDestroySurfaceObject.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.7 + */ +TEST_CASE("Unit_hipDestroySurfaceObject_Negative_Parameters") { + SECTION("surfObject is NULL") { + HIP_CHECK(hipDestroySurfaceObject(static_cast(0))); + } + + SECTION("double free") { + hipArray_t array; + hipChannelFormatDesc desc = hipCreateChannelDesc(); + + HIP_CHECK(hipMallocArray(&array, &desc, 64, 0, hipArraySurfaceLoadStore)); + + hipSurfaceObject_t surf; + + hipResourceDesc resc = {}; + resc.resType = hipResourceTypeArray; + resc.res.array.array = array; + + HIP_CHECK(hipCreateSurfaceObject(&surf, &resc)); + + HIP_CHECK(hipDestroySurfaceObject(surf)); + HIP_CHECK_ERROR(hipDestroySurfaceObject(surf), hipErrorInvalidValue); + + HIP_CHECK(hipFreeArray(array)); + } +} \ No newline at end of file diff --git a/catch/unit/texture/CMakeLists.txt b/catch/unit/texture/CMakeLists.txt index 0b18379c7b..fe4c0a8d1d 100644 --- a/catch/unit/texture/CMakeLists.txt +++ b/catch/unit/texture/CMakeLists.txt @@ -45,6 +45,12 @@ set(TEST_SRC hipTexObjectTests.cc hipTextureObjectTests.cc hipBindTextureToMipmappedArray.cc + hipMallocMipmappedArray.cc + hipFreeMipmappedArray.cc + hipGetMipmappedArrayLevel.cc + hipMipmappedArrayCreate.cc + hipMipmappedArrayDestroy.cc + hipMipmappedArrayGetLevel.cc ) if(WIN32) diff --git a/catch/unit/texture/fixed_point.hh b/catch/unit/texture/fixed_point.hh new file mode 100644 index 0000000000..ba95c42c44 --- /dev/null +++ b/catch/unit/texture/fixed_point.hh @@ -0,0 +1,73 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. + +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. +*/ + +#pragma once + +#include + +template class FixedPoint { + public: + FixedPoint() = default; + FixedPoint(float x) { fixed_point_ = static_cast(roundf(x * (1 << fractional_bits))); } + + operator float() const { + return (static_cast(fixed_point_) / static_cast(1 << fractional_bits)); + } + + FixedPoint operator+(FixedPoint other) const { + FixedPoint res; + res.fixed_point_ = fixed_point_ + other.fixed_point_; + return res; + } + + FixedPoint operator-(FixedPoint other) const { + FixedPoint res; + res.fixed_point_ = fixed_point_ - other.fixed_point_; + return res; + } + + FixedPoint operator*(FixedPoint other) const { + constexpr auto K = 1 << (fractional_bits - 1); + + FixedPoint res; + int32_t temp; + + temp = static_cast(fixed_point_) * static_cast(other.fixed_point_); + temp += K; + + res.fixed_point_ = Sat16(temp >> fractional_bits); + + return res; + } + + private: + int16_t fixed_point_; + + int16_t Sat16(int32_t x) const { + if (x > 0x7FFF) + return 0x7FFF; + else if (x < -0x8000) + return -0x8000; + else + return (int16_t)x; + } +}; \ No newline at end of file diff --git a/catch/unit/texture/hipFreeMipmappedArray.cc b/catch/unit/texture/hipFreeMipmappedArray.cc new file mode 100644 index 0000000000..c0ca080d26 --- /dev/null +++ b/catch/unit/texture/hipFreeMipmappedArray.cc @@ -0,0 +1,60 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. + +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 + +/** + * @addtogroup hipFreeMipmappedArray hipFreeMipmappedArray + * @{ + * @ingroup TextureTest + */ + +/** + * Test Description + * ------------------------ + * - Negative parameters test for `hipFreeMipmappedArray`. + * Test source + * ------------------------ + * - unit/texture/hipFreeMipmappedArray.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.7 + */ +TEST_CASE("Unit_hipFreeMipmappedArray_Negative_Parameters") { + CHECK_IMAGE_SUPPORT; + + SECTION("array is nullptr") { + HIP_CHECK_ERROR(hipFreeMipmappedArray(nullptr), hipErrorInvalidValue); + } + + SECTION("double free") { + hipMipmappedArray_t array; + hipChannelFormatDesc desc = hipCreateChannelDesc(); + hipExtent extent = make_hipExtent(4, 4, 6); + unsigned int levels = 4; + + HIP_CHECK(hipMallocMipmappedArray(&array, &desc, extent, levels, 0)); + + HIP_CHECK(hipFreeMipmappedArray(array)); + HIP_CHECK_ERROR(hipFreeMipmappedArray(array), hipErrorContextIsDestroyed); + } +} \ No newline at end of file diff --git a/catch/unit/texture/hipGetMipmappedArrayLevel.cc b/catch/unit/texture/hipGetMipmappedArrayLevel.cc new file mode 100644 index 0000000000..0740c06d04 --- /dev/null +++ b/catch/unit/texture/hipGetMipmappedArrayLevel.cc @@ -0,0 +1,67 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. + +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 + +/** + * @addtogroup hipGetMipmappedArrayLevel hipGetMipmappedArrayLevel + * @{ + * @ingroup TextureTest + */ + +/** + * Test Description + * ------------------------ + * - Negative parameters test for `hipGetMipmappedArrayLevel`. + * Test source + * ------------------------ + * - unit/texture/hipGetMipmappedArrayLevel.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.7 + */ +TEST_CASE("Unit_hipGetMipmappedArrayLevel_Negative_Parameters") { + CHECK_IMAGE_SUPPORT; + + hipMipmappedArray_t array; + hipChannelFormatDesc desc = hipCreateChannelDesc(); + hipExtent extent = make_hipExtent(4, 4, 6); + unsigned int levels = 4; + + HIP_CHECK(hipMallocMipmappedArray(&array, &desc, extent, levels, 0)); + + hipArray_t levelArray; + + SECTION("levelArray is nullptr") { + HIP_CHECK_ERROR(hipGetMipmappedArrayLevel(nullptr, array, 2), hipErrorInvalidValue); + } + + SECTION("mipmappedArray is nullptr") { + HIP_CHECK_ERROR(hipGetMipmappedArrayLevel(&levelArray, nullptr, 2), hipErrorInvalidHandle); + } + + SECTION("level index is greater than number of levels") { + HIP_CHECK_ERROR(hipGetMipmappedArrayLevel(&levelArray, array, 4), hipErrorInvalidValue); + } + + HIP_CHECK(hipFreeMipmappedArray(array)); +} \ No newline at end of file diff --git a/catch/unit/texture/hipMallocMipmappedArray.cc b/catch/unit/texture/hipMallocMipmappedArray.cc new file mode 100644 index 0000000000..364f481465 --- /dev/null +++ b/catch/unit/texture/hipMallocMipmappedArray.cc @@ -0,0 +1,117 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. + +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 + +/** + * @addtogroup hipMallocMipmappedArray hipMallocMipmappedArray + * @{ + * @ingroup TextureTest + */ + +/** + * Test Description + * ------------------------ + * - Negative parameters test for `hipMallocMipmappedArray`. + * Test source + * ------------------------ + * - unit/texture/hipMallocMipmappedArray.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.7 + */ +TEST_CASE("Unit_hipMallocMipmappedArray_Negative_Parameters") { + CHECK_IMAGE_SUPPORT; + + hipMipmappedArray_t array; + hipChannelFormatDesc desc = hipCreateChannelDesc(); + hipExtent extent = make_hipExtent(4, 4, 6); + unsigned int levels = 4; + + SECTION("mipmappedArray is nullptr") { + HIP_CHECK_ERROR(hipMallocMipmappedArray(nullptr, &desc, extent, levels, 0), + hipErrorInvalidValue); + } + + SECTION("desc is nullptr") { + HIP_CHECK_ERROR(hipMallocMipmappedArray(&array, nullptr, extent, levels, 0), + hipErrorInvalidValue); + } + + SECTION("extent is zero") { + extent = {}; + HIP_CHECK_ERROR(hipMallocMipmappedArray(&array, &desc, extent, levels, 0), + hipErrorInvalidValue); + } + + SECTION("invalid flags") { + HIP_CHECK_ERROR( + hipMallocMipmappedArray(&array, &desc, extent, levels, static_cast(-1)), + hipErrorInvalidValue); + } + + SECTION("hipArrayCubemap && depth != height") { + extent.height = 5; + HIP_CHECK_ERROR(hipMallocMipmappedArray(&array, &desc, extent, levels, hipArrayCubemap), + hipErrorInvalidValue); + } + + SECTION("hipArrayCubemap && depth != 6") { + extent.depth = 12; + HIP_CHECK_ERROR(hipMallocMipmappedArray(&array, &desc, extent, levels, hipArrayCubemap), + hipErrorInvalidValue); + } + + SECTION("hipArrayCubemap && hipArrayLayered && depth is not a multiple of 6") { + extent.depth = 13; + HIP_CHECK_ERROR( + hipMallocMipmappedArray(&array, &desc, extent, levels, hipArrayCubemap | hipArrayLayered), + hipErrorInvalidValue); + } + + SECTION("hipArrayTextureGather && 1D array") { + extent.height = 0; + extent.depth = 0; + HIP_CHECK_ERROR(hipMallocMipmappedArray(&array, &desc, extent, levels, hipArrayTextureGather), + hipErrorInvalidValue); + } + + SECTION("hipArrayTextureGather && 3D array") { + HIP_CHECK_ERROR(hipMallocMipmappedArray(&array, &desc, extent, levels, hipArrayTextureGather), + hipErrorInvalidValue); + } + +#if HT_NVIDIA // Disabled due to defect EXSWHTEC-365 + SECTION("hipArraySparse && 1D array") { + extent.height = 0; + extent.depth = 0; + HIP_CHECK_ERROR(hipMallocMipmappedArray(&array, &desc, extent, levels, cudaArraySparse), + hipErrorInvalidValue); + } + + SECTION("hipArraySparse && cubemap array") { + HIP_CHECK_ERROR( + hipMallocMipmappedArray(&array, &desc, extent, levels, hipArrayCubemap | cudaArraySparse), + hipErrorInvalidValue); + } +#endif +} \ No newline at end of file diff --git a/catch/unit/texture/hipMipmappedArrayCreate.cc b/catch/unit/texture/hipMipmappedArrayCreate.cc new file mode 100644 index 0000000000..33f80a1a9a --- /dev/null +++ b/catch/unit/texture/hipMipmappedArrayCreate.cc @@ -0,0 +1,118 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. + +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 +#include + +/** + * @addtogroup hipMipmappedArrayCreate hipMipmappedArrayCreate + * @{ + * @ingroup TextureTest + */ + +/** + * Test Description + * ------------------------ + * - Negative parameters test for `hipMipmappedArrayCreate`. + * Test source + * ------------------------ + * - unit/texture/hipMipmappedArrayCreate.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.7 + */ +TEST_CASE("Unit_hipMipmappedArrayCreate_Negative_Parameters") { + CHECK_IMAGE_SUPPORT; + + hipmipmappedArray array; + + HIP_ARRAY3D_DESCRIPTOR desc = {}; + using vec_info = vector_info; + desc.Format = vec_info::format; + desc.NumChannels = vec_info::size; + desc.Width = 4; + desc.Height = 4; + desc.Depth = 6; + desc.Flags = 0; + + unsigned int levels = 4; + + HIP_CHECK(hipFree(0)); + + SECTION("mipmappedArray is nullptr") { + HIP_CHECK_ERROR(hipMipmappedArrayCreate(nullptr, &desc, levels), hipErrorInvalidValue); + } + + SECTION("desc is nullptr") { + HIP_CHECK_ERROR(hipMipmappedArrayCreate(&array, nullptr, levels), hipErrorInvalidValue); + } + + SECTION("extent is zero") { + desc.Width = 0; + desc.Height = 0; + desc.Depth = 0; + HIP_CHECK_ERROR(hipMipmappedArrayCreate(&array, &desc, levels), hipErrorInvalidValue); + } + + SECTION("invalid flags") { + desc.Flags = static_cast(-1); + HIP_CHECK_ERROR(hipMipmappedArrayCreate(&array, &desc, levels), hipErrorInvalidValue); + } + + SECTION("hipArrayCubemap && depth != 6") { + desc.Depth = 5; + desc.Flags = hipArrayCubemap; + HIP_CHECK_ERROR(hipMipmappedArrayCreate(&array, &desc, levels), hipErrorInvalidValue); + } + + SECTION("hipArrayCubemap && hipArrayLayered && depth is not a multiple of 6") { + desc.Depth = 13; + desc.Flags = hipArrayCubemap | hipArrayLayered; + HIP_CHECK_ERROR(hipMipmappedArrayCreate(&array, &desc, levels), hipErrorInvalidValue); + } + + SECTION("hipArrayTextureGather && 1D array") { + desc.Height = 0; + desc.Depth = 0; + desc.Flags = hipArrayTextureGather; + HIP_CHECK_ERROR(hipMipmappedArrayCreate(&array, &desc, levels), hipErrorInvalidValue); + } + + SECTION("hipArrayTextureGather && 3D array") { + desc.Flags = hipArrayTextureGather; + HIP_CHECK_ERROR(hipMipmappedArrayCreate(&array, &desc, levels), hipErrorInvalidValue); + } + +#if HT_NVIDIA // Disabled due to defect EXSWHTEC-365 + SECTION("hipArraySparse && 1D array") { + desc.Height = 0; + desc.Depth = 0; + desc.Flags = cudaArraySparse; + HIP_CHECK_ERROR(hipMipmappedArrayCreate(&array, &desc, levels), hipErrorInvalidValue); + } + + SECTION("hipArraySparse && cubemap array") { + desc.Flags = hipArrayCubemap | cudaArraySparse; + HIP_CHECK_ERROR(hipMipmappedArrayCreate(&array, &desc, levels), hipErrorInvalidValue); + } +#endif +} \ No newline at end of file diff --git a/catch/unit/texture/hipMipmappedArrayDestroy.cc b/catch/unit/texture/hipMipmappedArrayDestroy.cc new file mode 100644 index 0000000000..3706cc48ff --- /dev/null +++ b/catch/unit/texture/hipMipmappedArrayDestroy.cc @@ -0,0 +1,71 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. + +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 +#include + +/** + * @addtogroup hipMipmappedArrayDestroy hipMipmappedArrayDestroy + * @{ + * @ingroup TextureTest + */ + +/** + * Test Description + * ------------------------ + * - Negative parameters test for `hipMipmappedArrayDestroy`. + * Test source + * ------------------------ + * - unit/texture/hipMipmappedArrayDestroy.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.7 + */ +TEST_CASE("Unit_hipMipmappedArrayDestroy_Negative_Parameters") { + CHECK_IMAGE_SUPPORT; + + HIP_CHECK(hipFree(0)); + + SECTION("array is nullptr") { + HIP_CHECK_ERROR(hipMipmappedArrayDestroy(nullptr), hipErrorInvalidHandle); + } + + SECTION("double free") { + hipmipmappedArray array; + + HIP_ARRAY3D_DESCRIPTOR desc = {}; + using vec_info = vector_info; + desc.Format = vec_info::format; + desc.NumChannels = vec_info::size; + desc.Width = 4; + desc.Height = 4; + desc.Depth = 6; + desc.Flags = 0; + + unsigned int levels = 4; + + HIP_CHECK(hipMipmappedArrayCreate(&array, &desc, levels)); + + HIP_CHECK(hipMipmappedArrayDestroy(array)); + HIP_CHECK_ERROR(hipMipmappedArrayDestroy(array), hipErrorContextIsDestroyed); + } +} \ No newline at end of file diff --git a/catch/unit/texture/hipMipmappedArrayGetLevel.cc b/catch/unit/texture/hipMipmappedArrayGetLevel.cc new file mode 100644 index 0000000000..dacd07c7b5 --- /dev/null +++ b/catch/unit/texture/hipMipmappedArrayGetLevel.cc @@ -0,0 +1,77 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. + +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 +#include + +/** + * @addtogroup hipMipmappedArrayGetLevel hipMipmappedArrayGetLevel + * @{ + * @ingroup TextureTest + */ + +/** + * Test Description + * ------------------------ + * - Negative parameters test for `hipMipmappedArrayGetLevel`. + * Test source + * ------------------------ + * - unit/texture/hipMipmappedArrayGetLevel.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.7 + */ +TEST_CASE("Unit_hipMipmappedArrayGetLevel_Negative_Parameters") { + CHECK_IMAGE_SUPPORT; + + hipmipmappedArray array; + + HIP_ARRAY3D_DESCRIPTOR desc = {}; + using vec_info = vector_info; + desc.Format = vec_info::format; + desc.NumChannels = vec_info::size; + desc.Width = 4; + desc.Height = 4; + desc.Depth = 6; + desc.Flags = 0; + + unsigned int levels = 4; + + HIP_CHECK(hipFree(0)); + HIP_CHECK(hipMipmappedArrayCreate(&array, &desc, levels)); + + hipArray_t levelArray; + + SECTION("levelArray is nullptr") { + HIP_CHECK_ERROR(hipMipmappedArrayGetLevel(nullptr, array, 2), hipErrorInvalidValue); + } + + SECTION("mipmappedArray is nullptr") { + HIP_CHECK_ERROR(hipMipmappedArrayGetLevel(&levelArray, nullptr, 2), hipErrorInvalidHandle); + } + + SECTION("level index is greater than number of levels") { + HIP_CHECK_ERROR(hipMipmappedArrayGetLevel(&levelArray, array, 4), hipErrorInvalidValue); + } + + HIP_CHECK(hipMipmappedArrayDestroy(array)); +} \ No newline at end of file diff --git a/catch/unit/texture/kernels.hh b/catch/unit/texture/kernels.hh new file mode 100644 index 0000000000..056dd3f6e0 --- /dev/null +++ b/catch/unit/texture/kernels.hh @@ -0,0 +1,107 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. + +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. +*/ + +#pragma once + +#include +#include + +namespace cg = cooperative_groups; + +__host__ __device__ inline float GetCoordinate(size_t iteration, size_t N, size_t dim, + size_t num_subdivisions, bool normalized_coords) { + float x = (static_cast(iteration) - N / 2) / num_subdivisions; + return normalized_coords ? x / dim : x; +} + +template +__global__ void tex1DKernel(TexelType* const out, size_t N, hipTextureObject_t tex_obj, + size_t width, size_t num_subdivisions, bool normalized_coords) { + const auto tid = cg::this_grid().thread_rank(); + if (tid >= N) return; + + float x = GetCoordinate(tid, N, width, num_subdivisions, normalized_coords); + out[tid] = tex1D(tex_obj, x); +} + +template +__global__ void tex2DKernel(TexelType* const out, size_t N_x, size_t N_y, + hipTextureObject_t tex_obj, size_t width, size_t height, + size_t num_subdivisions, bool normalized_coords) { + const auto tid_x = blockIdx.x * blockDim.x + threadIdx.x; + if (tid_x >= N_x) return; + + const auto tid_y = blockIdx.y * blockDim.y + threadIdx.y; + if (tid_y >= N_y) return; + + float x = GetCoordinate(tid_x, N_x, width, num_subdivisions, normalized_coords); + float y = GetCoordinate(tid_y, N_y, height, num_subdivisions, normalized_coords); + + out[tid_y * N_x + tid_x] = tex2D(tex_obj, x, y); +} + +template +__global__ void tex3DKernel(TexelType* const out, size_t N_x, size_t N_y, size_t N_z, + hipTextureObject_t tex_obj, size_t width, size_t height, size_t depth, + size_t num_subdivisions, bool normalized_coords) { + const auto tid_x = blockIdx.x * blockDim.x + threadIdx.x; + if (tid_x >= N_x) return; + + const auto tid_y = blockIdx.y * blockDim.y + threadIdx.y; + if (tid_y >= N_y) return; + + const auto tid_z = blockIdx.z * blockDim.z + threadIdx.z; + if (tid_z >= N_z) return; + + float x = GetCoordinate(tid_x, N_x, width, num_subdivisions, normalized_coords); + float y = GetCoordinate(tid_y, N_y, height, num_subdivisions, normalized_coords); + float z = GetCoordinate(tid_z, N_z, depth, num_subdivisions, normalized_coords); + + out[tid_z * N_x * N_y + tid_y * N_x + tid_x] = tex3D(tex_obj, x, y, z); +} + +template +__global__ void tex1DLayeredKernel(TexelType* const out, size_t N, hipTextureObject_t tex_obj, + size_t width, size_t num_subdivisions, bool normalized_coords, + size_t layer) { + const auto tid = cg::this_grid().thread_rank(); + if (tid >= N) return; + + float x = GetCoordinate(tid, N, width, num_subdivisions, normalized_coords); + out[tid] = tex1DLayered(tex_obj, x, layer); +} + +template +__global__ void tex2DLayeredKernel(TexelType* const out, size_t N_x, size_t N_y, + hipTextureObject_t tex_obj, size_t width, size_t height, + size_t num_subdivisions, bool normalized_coords, size_t layer) { + const auto tid_x = blockIdx.x * blockDim.x + threadIdx.x; + if (tid_x >= N_x) return; + + const auto tid_y = blockIdx.y * blockDim.y + threadIdx.y; + if (tid_y >= N_y) return; + + float x = GetCoordinate(tid_x, N_x, width, num_subdivisions, normalized_coords); + float y = GetCoordinate(tid_y, N_y, height, num_subdivisions, normalized_coords); + + out[tid_y * N_x + tid_x] = tex2DLayered(tex_obj, x, y, layer); +} \ No newline at end of file diff --git a/catch/unit/texture/test_fixture.hh b/catch/unit/texture/test_fixture.hh new file mode 100644 index 0000000000..47ac8b73bc --- /dev/null +++ b/catch/unit/texture/test_fixture.hh @@ -0,0 +1,150 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. + +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. +*/ + +#pragma once + +#include +#include + +#include "texture_reference.hh" +#include "utils.hh" +#include "vec4.hh" + +template struct TextureTestParams { + hipExtent extent; + size_t layers; + size_t num_subdivisions; + hipTextureDesc tex_desc; + + size_t Size() const { + return extent.width * (extent.height ?: 1) * (extent.depth ?: 1) * (layers ?: 1); + } + + size_t NumItersX() const { return 3 * extent.width * num_subdivisions * 2 + 1; } + + size_t NumItersY() const { return 3 * extent.height * num_subdivisions * 2 + 1; } + + size_t NumItersZ() const { return 3 * extent.depth * num_subdivisions * 2 + 1; } + + size_t NumIters() const { return NumItersX() * NumItersY() * NumItersZ(); } + + size_t Width() const { return extent.width; } + + size_t Height() const { return extent.height; } + + size_t Depth() const { return extent.depth; } + + hipExtent LayeredExtent() const { + return Layered() ? make_hipExtent(Width(), Height(), layers) : extent; + } + + bool Layered() const { return layers > 1; } + + void GenerateTextureDesc(decltype(hipReadModeElementType) read_mode = hipReadModeElementType) { + constexpr bool is_floating_point = std::is_floating_point_v; + + memset(&tex_desc, 0, sizeof(tex_desc)); + tex_desc.readMode = read_mode; + + tex_desc.filterMode = hipFilterModePoint; + if (is_floating_point || tex_desc.readMode == hipReadModeNormalizedFloat) { + tex_desc.filterMode = GENERATE(hipFilterModePoint, hipFilterModeLinear); + } + + tex_desc.normalizedCoords = GENERATE(false, true); + + auto address_mode_x = hipAddressModeClamp; + auto address_mode_y = address_mode_x; + auto address_mode_z = address_mode_y; + + if (tex_desc.normalizedCoords) { + address_mode_x = GENERATE(hipAddressModeClamp, hipAddressModeBorder, hipAddressModeWrap, + hipAddressModeMirror); + if (extent.height) + address_mode_y = GENERATE(hipAddressModeClamp, hipAddressModeBorder, hipAddressModeWrap, + hipAddressModeMirror); + if (extent.depth) + address_mode_z = GENERATE(hipAddressModeClamp, hipAddressModeBorder, hipAddressModeWrap, + hipAddressModeMirror); + } else { + address_mode_x = GENERATE(hipAddressModeClamp, hipAddressModeBorder); + if (extent.height) address_mode_y = GENERATE(hipAddressModeClamp, hipAddressModeBorder); + if (extent.depth) address_mode_z = GENERATE(hipAddressModeClamp, hipAddressModeBorder); + } + + tex_desc.addressMode[0] = address_mode_x; + if (extent.height) tex_desc.addressMode[1] = address_mode_y; + if (extent.depth) tex_desc.addressMode[2] = address_mode_z; + } +}; + +template struct TextureTestFixture { + using VecType = vec4; + using OutType = std::conditional_t, VecType>; + + TextureTestParams params; + hipResourceDesc res_desc; + + LinearAllocGuard host_alloc; + TextureReference tex_h; + ArrayAllocGuard tex_alloc_d; + TextureGuard tex; + LinearAllocGuard out_alloc_d; + std::vector out_alloc_h; + + TextureTestFixture(const TextureTestParams& p) + : params{p}, + host_alloc{LinearAllocs::hipHostMalloc, sizeof(VecType) * params.Size()}, + tex_h{host_alloc.ptr(), params.extent, params.layers}, + tex_alloc_d{params.LayeredExtent(), params.Layered() ? hipArrayLayered : 0u}, + tex{ResDesc(), ¶ms.tex_desc}, + out_alloc_d{LinearAllocs::hipMalloc, sizeof(OutType) * params.NumIters()}, + out_alloc_h(params.NumIters()) {} + + hipResourceDesc* ResDesc() { + constexpr int test_value_offset = 7; + for (auto i = 0u; i < params.Size(); ++i) { + SetVec4(host_alloc.ptr()[i], i + test_value_offset); + } + + hipMemcpy3DParms memcpy_params = {}; + memcpy_params.dstArray = tex_alloc_d.ptr(); + memcpy_params.extent = params.LayeredExtent(); + memcpy_params.extent.height = memcpy_params.extent.height ?: 1; + memcpy_params.extent.depth = memcpy_params.extent.depth ?: 1; + memcpy_params.srcPtr = make_hipPitchedPtr(tex_h.ptr(0), sizeof(VecType) * params.Width(), + params.Width(), params.Height() ?: 1); + memcpy_params.kind = hipMemcpyHostToDevice; + HIP_CHECK(hipMemcpy3D(&memcpy_params)); + + memset(&res_desc, 0, sizeof(res_desc)); + res_desc.resType = hipResourceTypeArray; + res_desc.res.array.array = tex_alloc_d.ptr(); + return &res_desc; + } + + void LoadOutput() { + HIP_CHECK(hipMemcpy(out_alloc_h.data(), out_alloc_d.ptr(), sizeof(OutType) * params.NumIters(), + hipMemcpyDeviceToHost)); + HIP_CHECK(hipDeviceSynchronize()); + } +}; \ No newline at end of file diff --git a/catch/unit/texture/texture_reference.hh b/catch/unit/texture/texture_reference.hh new file mode 100644 index 0000000000..d2ee9159e0 --- /dev/null +++ b/catch/unit/texture/texture_reference.hh @@ -0,0 +1,253 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. + +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. +*/ + +#pragma once + +#include + +#include "fixed_point.hh" + +template class TextureReference { + public: + TextureReference(TexelType* alloc, hipExtent extent, size_t layers) + : alloc_{alloc}, extent_{extent}, layers_{layers} {} + + TexelType Tex1D(float x, const hipTextureDesc& tex_desc) const { + return Tex1DLayered(x, 0, tex_desc); + } + + TexelType Tex2D(float x, float y, const hipTextureDesc& tex_desc) const { + return Tex2DLayered(x, y, 0, tex_desc); + } + + TexelType Tex3D(float x, float y, float z, const hipTextureDesc& tex_desc) const { + x = tex_desc.normalizedCoords ? x * extent_.width : x; + y = tex_desc.normalizedCoords ? y * extent_.height : y; + z = tex_desc.normalizedCoords ? z * extent_.depth : z; + if (tex_desc.filterMode == hipFilterModePoint) { + return Sample(floorf(x), floorf(y), floorf(z), tex_desc.addressMode); + } else if (tex_desc.filterMode == hipFilterModeLinear) { + return LinearFiltering(x, y, z, tex_desc.addressMode); + } else { + throw std::invalid_argument("Invalid hipFilterMode value"); + } + } + + TexelType Tex1DLayered(float x, int layer, const hipTextureDesc& tex_desc) const { + x = tex_desc.normalizedCoords ? x * extent_.width : x; + if (tex_desc.filterMode == hipFilterModePoint) { + return Sample(floorf(x), layer, tex_desc.addressMode); + } else if (tex_desc.filterMode == hipFilterModeLinear) { + return LinearFiltering(x, layer, tex_desc.addressMode); + } else { + throw std::invalid_argument("Invalid hipFilterMode value"); + } + } + + TexelType Tex2DLayered(float x, float y, int layer, const hipTextureDesc& tex_desc) const { + x = tex_desc.normalizedCoords ? x * extent_.width : x; + y = tex_desc.normalizedCoords ? y * extent_.height : y; + if (tex_desc.filterMode == hipFilterModePoint) { + return Sample(floorf(x), floorf(y), layer, tex_desc.addressMode); + } else if (tex_desc.filterMode == hipFilterModeLinear) { + return LinearFiltering(x, y, layer, tex_desc.addressMode); + } else { + throw std::invalid_argument("Invalid hipFilterMode value"); + } + } + + TexelType* ptr(size_t layer) const { + return alloc_ + layer * extent_.width * (extent_.height ?: 1); + } + + size_t width() const { return extent_.width; } + + size_t height() const { return extent_.height; } + + size_t depth() const { return extent_.depth; } + + private: + TexelType* const alloc_; + const hipExtent extent_; + const size_t layers_; + + template TexelType Vec4Sum(T arg) const { return Vec4Add(arg, Zero()); } + + template TexelType Vec4Sum(T arg, Ts... args) const { + return Vec4Add(arg, Vec4Sum(args...)); + } + + TexelType Zero() const { + TexelType ret; + memset(&ret, 0, sizeof(ret)); + return ret; + } + + float ApplyAddressMode(float coord, size_t dim, hipTextureAddressMode address_mode) const { + switch (address_mode) { + case hipAddressModeClamp: + return ApplyClamp(coord, dim); + case hipAddressModeBorder: + if (CheckBorder(coord, dim)) { + return std::numeric_limits::quiet_NaN(); + } + case hipAddressModeWrap: + return ApplyWrap(coord, dim); + case hipAddressModeMirror: + return ApplyMirror(coord, dim); + default: + throw std::invalid_argument("Invalid hipAddressMode value"); + } + } + + TexelType Sample(float x, int layer, const hipTextureAddressMode* address_mode) const { + x = ApplyAddressMode(x, extent_.width, address_mode[0]); + + if (std::isnan(x)) { + return Zero(); + } + + return ptr(layer)[static_cast(x)]; + } + + TexelType Sample(float x, float y, int layer, const hipTextureAddressMode* address_mode) const { + x = ApplyAddressMode(x, extent_.width, address_mode[0]); + y = ApplyAddressMode(y, extent_.height, address_mode[1]); + + if (std::isnan(x) || std::isnan(y)) { + return Zero(); + } + + return ptr(layer)[static_cast(y) * extent_.width + static_cast(x)]; + } + + TexelType Sample(float x, float y, float z, const hipTextureAddressMode* address_mode) const { + x = ApplyAddressMode(x, extent_.width, address_mode[0]); + y = ApplyAddressMode(y, extent_.height, address_mode[1]); + z = ApplyAddressMode(z, extent_.depth, address_mode[2]); + + if (std::isnan(x) || std::isnan(y) || std::isnan(z)) { + return Zero(); + } + + return ptr(0)[static_cast(z) * extent_.width * extent_.height + + static_cast(y) * extent_.width + static_cast(x)]; + } + + TexelType LinearFiltering(float x, int layer, const hipTextureAddressMode* address_mode) const { + const auto [i, alpha] = GetLinearFilteringParams(x); + + const auto T_i0 = Sample(i, layer, address_mode); + const auto T_i1 = Sample(i + 1.0f, layer, address_mode); + + const auto term_i0 = Vec4Scale((1.0f - alpha), T_i0); + const auto term_i1 = Vec4Scale(alpha, T_i1); + + return Vec4Sum(term_i0, term_i1); + } + + TexelType LinearFiltering(float x, float y, int layer, + const hipTextureAddressMode* address_mode) const { + const auto [i, alpha] = GetLinearFilteringParams(x); + const auto [j, beta] = GetLinearFilteringParams(y); + + const auto T_i0j0 = Sample(i, j, layer, address_mode); + const auto T_i1j0 = Sample(i + 1.0f, j, layer, address_mode); + const auto T_i0j1 = Sample(i, j + 1.0f, layer, address_mode); + const auto T_i1j1 = Sample(i + 1.0f, j + 1.0f, layer, address_mode); + + const auto term_i0j0 = Vec4Scale((1.0f - alpha) * (1.0f - beta), T_i0j0); + const auto term_i1j0 = Vec4Scale(alpha * (1.0f - beta), T_i1j0); + const auto term_i0j1 = Vec4Scale((1.0f - alpha) * beta, T_i0j1); + const auto term_i1j1 = Vec4Scale(alpha * beta, T_i1j1); + + return Vec4Sum(term_i0j0, term_i1j0, term_i0j1, term_i1j1); + } + + TexelType LinearFiltering(float x, float y, float z, + const hipTextureAddressMode* address_mode) const { + const auto [i, alpha] = GetLinearFilteringParams(x); + const auto [j, beta] = GetLinearFilteringParams(y); + const auto [k, gamma] = GetLinearFilteringParams(z); + + const auto T_i0j0k0 = Sample(i, j, k, address_mode); + const auto T_i1j0k0 = Sample(i + 1.0f, j, k, address_mode); + const auto T_i0j1k0 = Sample(i, j + 1.0f, k, address_mode); + const auto T_i1j1k0 = Sample(i + 1.0f, j + 1.0f, k, address_mode); + const auto T_i0j0k1 = Sample(i, j, k + 1.0f, address_mode); + const auto T_i1j0k1 = Sample(i + 1.0f, j, k + 1.0f, address_mode); + const auto T_i0j1k1 = Sample(i, j + 1.0f, k + 1.0f, address_mode); + const auto T_i1j1k1 = Sample(i + 1.0f, j + 1.0f, k + 1.0f, address_mode); + + const auto term_i0j0k0 = Vec4Scale((1.0f - alpha) * (1.0f - beta) * (1.0f - gamma), T_i0j0k0); + const auto term_i1j0k0 = Vec4Scale(alpha * (1.0f - beta) * (1.0f - gamma), T_i1j0k0); + const auto term_i0j1k0 = Vec4Scale((1.0f - alpha) * beta * (1.0f - gamma), T_i0j1k0); + const auto term_i1j1k0 = Vec4Scale(alpha * beta * (1.0f - gamma), T_i1j1k0); + const auto term_i0j0k1 = Vec4Scale((1.0f - alpha) * (1.0f - beta) * gamma, T_i0j0k1); + const auto term_i1j0k1 = Vec4Scale(alpha * (1.0f - beta) * gamma, T_i1j0k1); + const auto term_i0j1k1 = Vec4Scale((1.0f - alpha) * beta * gamma, T_i0j1k1); + const auto term_i1j1k1 = Vec4Scale(alpha * beta * gamma, T_i1j1k1); + + return Vec4Sum(term_i0j0k0, term_i1j0k0, term_i0j1k0, term_i1j1k0, term_i0j0k1, term_i1j0k1, + term_i0j1k1, term_i1j1k1); + } + + float ApplyClamp(float coord, size_t dim) const { + return max(min(coord, static_cast(dim - 1)), 0.0f); + } + + bool CheckBorder(float coord, size_t dim) const { return coord > dim - 1 || coord < 0.0f; } + + float ApplyWrap(float coord, size_t dim) const { + coord /= dim; + coord = coord - floorf(coord); + coord *= dim; + + return coord; + } + + float ApplyMirror(float coord, size_t dim) const { + coord /= dim; + const float frac = coord - floor(coord); + const bool is_reversing = static_cast(floorf(coord)) % 2; + coord = is_reversing ? 1.0f - frac : frac; + coord *= dim; + coord -= (coord == truncf(coord)) * is_reversing; + + return coord; + } + + template float FloatToNBitFractional(float x) const { + constexpr size_t mult = 1 << N; + const auto x_trunc = std::trunc(x); + const auto x_frac = std::round((x - x_trunc) * mult) / mult; + return x_trunc + x_frac; + } + + std::tuple GetLinearFilteringParams(float coord) const { + const auto coordB = FloatToNBitFractional<8>(coord - 0.5f); + const auto index = floorf(coordB); + const auto coeff = coordB - index; + + return {index, coeff}; + } +}; diff --git a/catch/unit/texture/utils.hh b/catch/unit/texture/utils.hh new file mode 100644 index 0000000000..90e51d48c8 --- /dev/null +++ b/catch/unit/texture/utils.hh @@ -0,0 +1,85 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. + +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. +*/ + +#pragma once + +#include +#include + +#include + +class TextureGuard { + public: + TextureGuard(hipResourceDesc* res_desc, hipTextureDesc* tex_desc) { + HIP_CHECK(hipCreateTextureObject(&tex_obj_, res_desc, tex_desc, nullptr)); + } + + ~TextureGuard() { static_cast(hipDestroyTextureObject(tex_obj_)); } + + TextureGuard(const TextureGuard&) = delete; + TextureGuard& operator=(const TextureGuard&) = delete; + + hipTextureObject_t object() const { return tex_obj_; } + + private: + hipTextureObject_t tex_obj_ = 0; +}; + +template std::enable_if_t, float> NormalizeInteger(const T x) { + // On the GPU, -1.0 will be returned both for the minimum value of a signed type and its + // successor e.g. for char, -1.0 will be returned for both -128 and -127. + auto xf = std::abs(static_cast(x)); + xf = std::min(xf, std::numeric_limits::max()); + return std::copysign(xf / std::numeric_limits::max(), x); +} + +inline std::tuple GetLaunchConfig(size_t max_num_threads, size_t num_iters) { + auto num_threads = std::min(max_num_threads, num_iters); + auto num_blocks = (num_iters + num_threads - 1) / num_threads; + return {num_threads, num_blocks}; +} + +inline std::string AddressModeToString(decltype(hipAddressModeClamp) address_mode) { + switch (address_mode) { + case hipAddressModeClamp: + return "hipAddressModeClamp"; + case hipAddressModeBorder: + return "hipAddressModeBorder"; + case hipAddressModeWrap: + return "hipAddressModeWrap"; + case hipAddressModeMirror: + return "hipAddressModeMirror"; + default: + throw std::invalid_argument("Invalid hipAddressMode value"); + } +} + +inline std::string FilteringModeToString(decltype(hipFilterModePoint) filter_mode) { + switch (filter_mode) { + case hipFilterModePoint: + return "hipFilterModePoint"; + case hipFilterModeLinear: + return "hipFilterModeLinear"; + default: + throw std::invalid_argument("Invalid hipFilterMode value"); + } +} \ No newline at end of file diff --git a/catch/unit/texture/vec4.hh b/catch/unit/texture/vec4.hh new file mode 100644 index 0000000000..db4b073908 --- /dev/null +++ b/catch/unit/texture/vec4.hh @@ -0,0 +1,106 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. + +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. +*/ + +#pragma once + +#include + +template struct vec4_struct { using type = void; }; + +#define DEFINE_VEC4_OVERLOAD(base_type, vec_type) \ + template <> struct vec4_struct { using type = vec_type; } + +DEFINE_VEC4_OVERLOAD(char, char4); +DEFINE_VEC4_OVERLOAD(short, short4); +DEFINE_VEC4_OVERLOAD(int, int4); +DEFINE_VEC4_OVERLOAD(long, long4); +DEFINE_VEC4_OVERLOAD(long long, longlong4); + +DEFINE_VEC4_OVERLOAD(unsigned char, uchar4); +DEFINE_VEC4_OVERLOAD(unsigned short, ushort4); +DEFINE_VEC4_OVERLOAD(unsigned int, uint4); +DEFINE_VEC4_OVERLOAD(unsigned long, ulong4); +DEFINE_VEC4_OVERLOAD(unsigned long long, ulonglong4); + +DEFINE_VEC4_OVERLOAD(float, float4); +DEFINE_VEC4_OVERLOAD(double, float4); + +template using vec4 = typename vec4_struct::type; + +template inline void SetVec4(vec4& vec, const T val) { + vec.x = val; + vec.y = val; + vec.z = val; + vec.w = val; +} + +template +inline void SetVec4(vec4& vec, const T x, const T y, const T z, const T w) { + vec.x = x; + vec.y = y; + vec.z = z; + vec.w = w; +} + +template inline auto MakeVec4(const T val) { + vec4 vec; + SetVec4(vec, val); + + return vec; +} + +template inline void MakeVec4(const T x, const T y, const T z, const T w) { + vec4 vec; + SetVec4(vec, x, y, z, w); + + return vec; +} + +template inline auto Vec4Map(const vec4& vec, F f) { + vec4 ret; + ret.x = f(vec.x); + ret.y = f(vec.y); + ret.z = f(vec.z); + ret.w = f(vec.w); + + return ret; +} + +template inline __host__ __device__ auto Vec4Scale(float s, const T& vec) { + T ret; + ret.x = s * vec.x; + ret.y = s * vec.y; + ret.z = s * vec.z; + ret.w = s * vec.w; + + return ret; +} + +template inline __host__ __device__ auto Vec4Add(const T& vec1, const T& vec2) { + T ret; + ret.x = vec1.x + vec2.x; + ret.y = vec1.y + vec2.y; + ret.z = vec1.z + vec2.z; + ret.w = vec1.w + vec2.w; + + return ret; +} \ No newline at end of file diff --git a/catch/unit/warp/CMakeLists.txt b/catch/unit/warp/CMakeLists.txt index cd2ec5226b..87bb5b0ab1 100644 --- a/catch/unit/warp/CMakeLists.txt +++ b/catch/unit/warp/CMakeLists.txt @@ -2,6 +2,11 @@ set(TEST_SRC warp_shfl_xor.cc warp_shfl.cc + warp_shfl_up.cc + warp_shfl_down.cc + warp_ballot.cc + warp_any.cc + warp_all.cc ) hip_add_exe_to_target(NAME WarpTest diff --git a/catch/unit/warp/warp_all.cc b/catch/unit/warp/warp_all.cc new file mode 100644 index 0000000000..40c129609d --- /dev/null +++ b/catch/unit/warp/warp_all.cc @@ -0,0 +1,128 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. +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 "warp_vote_common.hh" + +#include + +/** + * @addtogroup all all + * @{ + * @ingroup DeviceLanguageTest + * `int __all(int predicate)` - + * Contains unit test for warp vote all function + */ + +namespace cg = cooperative_groups; + +static bool check_if_all(uint64_t predicate_mask, uint64_t active_mask, size_t partition_size) { + if (!(predicate_mask & active_mask)) return false; + for (int i = 0; i < partition_size; i++) { + if (active_mask & (static_cast(1) << i)) { + if (!(predicate_mask & (static_cast(1) << i))) return false; + } + } + return true; +} + +__global__ void kernel_all(int* const out, const uint64_t* const active_masks, uint64_t predicate) { + if (deactivate_thread(active_masks)) { + return; + } + + const auto grid = cg::this_grid(); + const auto warp = cg::tiled_partition(cg::this_thread_block(), warpSize); + + out[grid.thread_rank()] = __all((predicate & (static_cast(1) << warp.thread_rank()))); +} + +class WarpAll : public WarpVoteTest { + public: + void launch_kernel(int* const arr_dev, const uint64_t* const active_masks) { + auto test_case = GENERATE(range(0, 5)); + predicate_mask_ = get_predicate_mask(test_case, this->warp_size_); + INFO("Predicate mask: " << predicate_mask_); + kernel_all<<grid_.grid_dim_, this->grid_.block_dim_>>>(arr_dev, active_masks, + predicate_mask_); + } + + void validate(const int* const arr) { + ArrayAllOf(arr, this->grid_.thread_count_, [this](unsigned int i) -> std::optional { + const auto rank_in_block = this->grid_.thread_rank_in_block(i).value(); + const auto rank_in_warp = rank_in_block % this->warp_size_; + const auto warp_idx = this->warps_in_block_ * (i / this->grid_.threads_in_block_count_) + + rank_in_block / this->warp_size_; + const auto block_rank = warp_idx / this->warps_in_block_; + const std::bitset active_mask(this->active_masks_[warp_idx]); + + auto partition_size = this->warp_size_; + // If the number of threads in a block is not a multiple of warp size, the + // last warp will have inactive threads and partition size must be recalculated + if (warp_idx == this->warps_in_block_ * (block_rank + 1) - 1) { + partition_size = + this->grid_.threads_in_block_count_ - (this->warps_in_block_ - 1) * this->warp_size_; + } + + if (!active_mask.test(rank_in_warp)) + return std::nullopt; + else { + // Active predicate mask must be calculated as partition can be smaller than warp_size + auto active_predicate = get_active_predicate(predicate_mask_, partition_size); + return check_if_all(active_predicate, this->active_masks_[warp_idx], partition_size); + } + }); + } + + private: + uint64_t predicate_mask_; +}; + +/** + * Test Description + * ------------------------ + * - Validates the warp vote all function behavior. Threads are deactivated based on the passed + * active mask. The predicate for each thread is determined according to the generated predicate + * mask. + * Test source + * ------------------------ + * - unit/warp/warp_all.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + * - Device supports warp vote + */ +TEST_CASE("Unit_Warp_Vote_All_Positive_Basic") { + int device; + hipDeviceProp_t device_properties; + HIP_CHECK(hipGetDevice(&device)); + HIP_CHECK(hipGetDeviceProperties(&device_properties, device)); + + if (!device_properties.arch.hasWarpVote) { + HipTest::HIP_SKIP_TEST("Device doesn't support Warp Vote!"); + return; + } + + SECTION("Warp Vote All with specified active mask") { + WarpAll().run(false); + } + + SECTION("Warp Vote All with random active mask") { + WarpAll().run(true); + } +} \ No newline at end of file diff --git a/catch/unit/warp/warp_any.cc b/catch/unit/warp/warp_any.cc new file mode 100644 index 0000000000..91dadbb12c --- /dev/null +++ b/catch/unit/warp/warp_any.cc @@ -0,0 +1,119 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. +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 "warp_vote_common.hh" + +#include + +/** + * @addtogroup any any + * @{ + * @ingroup DeviceLanguageTest + * `int __any(int predicate)` - + * Contains unit test for warp vote any function + */ + +namespace cg = cooperative_groups; + + +__global__ void kernel_any(int* const out, const uint64_t* const active_masks, uint64_t predicate) { + if (deactivate_thread(active_masks)) { + return; + } + + const auto grid = cg::this_grid(); + const auto warp = cg::tiled_partition(cg::this_thread_block(), warpSize); + + out[grid.thread_rank()] = __any((predicate & (static_cast(1) << warp.thread_rank()))); +} + +class WarpAny : public WarpVoteTest { + public: + void launch_kernel(int* const arr_dev, const uint64_t* const active_masks) { + auto test_case = GENERATE(range(0, 5)); + predicate_mask_ = get_predicate_mask(test_case, this->warp_size_); + INFO("Predicate mask: " << predicate_mask_); + kernel_any<<grid_.grid_dim_, this->grid_.block_dim_>>>(arr_dev, active_masks, + predicate_mask_); + } + + void validate(const int* const arr) { + ArrayAllOf(arr, this->grid_.thread_count_, [this](unsigned int i) -> std::optional { + const auto rank_in_block = this->grid_.thread_rank_in_block(i).value(); + const auto rank_in_warp = rank_in_block % this->warp_size_; + const auto warp_idx = this->warps_in_block_ * (i / this->grid_.threads_in_block_count_) + + rank_in_block / this->warp_size_; + const auto block_rank = warp_idx / this->warps_in_block_; + const std::bitset active_mask(this->active_masks_[warp_idx]); + + auto partition_size = this->warp_size_; + // If the number of threads in a block is not a multiple of warp size, the + // last warp will have inactive threads and partition size must be recalculated + if (warp_idx == this->warps_in_block_ * (block_rank + 1) - 1) { + partition_size = + this->grid_.threads_in_block_count_ - (this->warps_in_block_ - 1) * this->warp_size_; + } + + if (!active_mask.test(rank_in_warp)) + return std::nullopt; + else { + // Active predicate mask must be calculated as partition can be smaller than warp_size + auto active_predicate = get_active_predicate(predicate_mask_, partition_size); + return ((active_predicate & this->active_masks_[warp_idx]) != 0); + } + }); + } + + private: + uint64_t predicate_mask_; +}; + +/** + * Test Description + * ------------------------ + * - Validates the warp vote any function behavior. Threads are deactivated based on the passed + * active mask. The predicate for each thread is determined according to the generated predicate + * mask. + * Test source + * ------------------------ + * - unit/warp/warp_any.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + * - Device supports warp vote + */ +TEST_CASE("Unit_Warp_Vote_Any_Positive_Basic") { + int device; + hipDeviceProp_t device_properties; + HIP_CHECK(hipGetDevice(&device)); + HIP_CHECK(hipGetDeviceProperties(&device_properties, device)); + + if (!device_properties.arch.hasWarpVote) { + HipTest::HIP_SKIP_TEST("Device doesn't support Warp Vote!"); + return; + } + + SECTION("Warp Vote Any with specified active mask") { + WarpAny().run(false); + } + + SECTION("Warp Vote Any with random active mask") { + WarpAny().run(true); + } +} \ No newline at end of file diff --git a/catch/unit/warp/warp_ballot.cc b/catch/unit/warp/warp_ballot.cc new file mode 100644 index 0000000000..6365d67fb0 --- /dev/null +++ b/catch/unit/warp/warp_ballot.cc @@ -0,0 +1,120 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. +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 "warp_vote_common.hh" + +#include + +/** + * @addtogroup ballot ballot + * @{ + * @ingroup DeviceLanguageTest + * `unsigned long long int __ballot(int predicate)` - + * Contains unit test for warp ballot function + */ + +namespace cg = cooperative_groups; + +__global__ void kernel_ballot(uint64_t* const out, const uint64_t* const active_masks, + uint64_t predicate) { + if (deactivate_thread(active_masks)) { + return; + } + + const auto grid = cg::this_grid(); + const auto warp = cg::tiled_partition(cg::this_thread_block(), warpSize); + + out[grid.thread_rank()] = + __ballot((predicate & (static_cast(1) << warp.thread_rank()))); +} + +class WarpBallot : public WarpVoteTest { + public: + void launch_kernel(uint64_t* const arr_dev, const uint64_t* const active_masks) { + auto test_case = GENERATE(range(0, 5)); + predicate_mask_ = get_predicate_mask(test_case, this->warp_size_); + INFO("Predicate mask: " << predicate_mask_); + kernel_ballot<<grid_.grid_dim_, this->grid_.block_dim_>>>(arr_dev, active_masks, + predicate_mask_); + } + + void validate(const uint64_t* const arr) { + ArrayAllOf(arr, this->grid_.thread_count_, [this](unsigned int i) -> std::optional { + const auto rank_in_block = this->grid_.thread_rank_in_block(i).value(); + const auto rank_in_warp = rank_in_block % this->warp_size_; + const auto warp_idx = this->warps_in_block_ * (i / this->grid_.threads_in_block_count_) + + rank_in_block / this->warp_size_; + const auto block_rank = warp_idx / this->warps_in_block_; + const std::bitset active_mask(this->active_masks_[warp_idx]); + + auto partition_size = this->warp_size_; + // If the number of threads in a block is not a multiple of warp size, the + // last warp will have inactive threads and partition size must be recalculated + if (warp_idx == this->warps_in_block_ * (block_rank + 1) - 1) { + partition_size = + this->grid_.threads_in_block_count_ - (this->warps_in_block_ - 1) * this->warp_size_; + } + + if (!active_mask.test(rank_in_warp)) + return std::nullopt; + else { + // Active predicate mask must be calculated as partition can be smaller than warp_size + auto active_predicate = get_active_predicate(predicate_mask_, partition_size); + return (active_predicate & this->active_masks_[warp_idx]); + } + }); + } + + private: + uint64_t predicate_mask_; +}; + +/** + * Test Description + * ------------------------ + * - Validates the warp ballot function behavior. Threads are deactivated based on the passed + * active mask. The predicate for each thread is determined according to the generated predicate + * mask. + * Test source + * ------------------------ + * - unit/warp/warp_ballot.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + * - Device supports warp ballot + */ +TEST_CASE("Unit_Warp_Ballot_Positive_Basic") { + int device; + hipDeviceProp_t device_properties; + HIP_CHECK(hipGetDevice(&device)); + HIP_CHECK(hipGetDeviceProperties(&device_properties, device)); + + if (!device_properties.arch.hasWarpBallot) { + HipTest::HIP_SKIP_TEST("Device doesn't support Warp Ballot!"); + return; + } + + SECTION("Warp Ballot with specified active mask") { + WarpBallot().run(false); + } + + SECTION("Warp Ballot with random active mask") { + WarpBallot().run(true); + } +} \ No newline at end of file diff --git a/catch/unit/warp/warp_shfl_down.cc b/catch/unit/warp/warp_shfl_down.cc new file mode 100644 index 0000000000..d42a19c7d8 --- /dev/null +++ b/catch/unit/warp/warp_shfl_down.cc @@ -0,0 +1,121 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. +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 "warp_shfl_common.hh" + +#include + +/** + * @addtogroup shfl_down shfl_down + * @{ + * @ingroup DeviceLanguageTest + * `T __shfl_down(T var, unsigned int lane_delta, int width = warpSize)` - + * Contains unit test for warp shfl_down function + */ + +namespace cg = cooperative_groups; + +template +__global__ void shfl_down(T* const out, const T* const in, const uint64_t* const active_masks, + const unsigned int* const deltas, const int width) { + if (deactivate_thread(active_masks)) { + return; + } + + const auto grid = cg::this_grid(); + const auto block = cg::this_thread_block(); + T var = in[grid.thread_rank()]; + out[grid.thread_rank()] = __shfl_down(var, deltas[block.thread_rank() % width], width); +} + +template class WarpShflDown : public WarpShflTest, T> { + public: + void launch_kernel(T* const arr_dev, T* const input_dev, const uint64_t* const active_masks) { + width_ = generate_width(this->warp_size_); + INFO("Width: " << width_); + const auto alloc_size = width_ * sizeof(unsigned int); + LinearAllocGuard deltas_dev(LinearAllocs::hipMalloc, alloc_size); + deltas_.resize(width_); + std::generate(deltas_.begin(), deltas_.end(), + [this] { return GenerateRandomInteger(0u, static_cast(width_)); }); + HIP_CHECK(hipMemcpy(deltas_dev.ptr(), deltas_.data(), alloc_size, hipMemcpyHostToDevice)); + shfl_down<<grid_.grid_dim_, this->grid_.block_dim_>>>(arr_dev, input_dev, active_masks, + deltas_dev.ptr(), width_); + } + + void validate(const T* const arr, const T* const input) { + ArrayAllOf(arr, this->grid_.thread_count_, [this, &input](unsigned int i) -> std::optional { + const int rank_in_block = this->grid_.thread_rank_in_block(i).value(); + const auto rank_in_warp = rank_in_block % this->warp_size_; + const auto rank_in_partition = rank_in_block % width_; + const auto mask_idx = this->warps_in_block_ * (i / this->grid_.threads_in_block_count_) + + rank_in_block / this->warp_size_; + const unsigned int delta = deltas_[rank_in_partition] % width_; + const std::bitset active_mask(this->active_masks_[mask_idx]); + + const int target = rank_in_block % width_ + delta; + if (!active_mask.test(rank_in_warp) || + (target < width_ && !active_mask.test(rank_in_warp + delta)) || + (target < width_ && rank_in_block + delta >= this->grid_.threads_in_block_count_)) { + return std::nullopt; + } + + return (target >= width_ ? input[i] : input[i + delta]); + }); + }; + + private: + std::vector deltas_; + int width_; +}; + +/** + * Test Description + * ------------------------ + * - Validates the warp shuffle down behavior for all valid width sizes {2, 4, 8, 16, 32, + * 64(if supported)} for generated delta values. The threads are deactivated based on the + * passed active mask. The test is run for all overloads of shfl_down. + * Test source + * ------------------------ + * - unit/warp/warp_shfl_down.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + * - Device supports warp shuffle + */ +TEMPLATE_TEST_CASE("Unit_Warp_Shfl_Down_Positive_Basic", "", int, unsigned int, long, unsigned long, + long long, unsigned long long, float, double) { + int device; + hipDeviceProp_t device_properties; + HIP_CHECK(hipGetDevice(&device)); + HIP_CHECK(hipGetDeviceProperties(&device_properties, device)); + + if (!device_properties.arch.hasWarpShuffle) { + HipTest::HIP_SKIP_TEST("Device doesn't support Warp Shuffle!"); + return; + } + + SECTION("Shfl Down with specified active mask and input values") { + WarpShflDown().run(false); + } + + SECTION("Shfl Down with random active mask and input values") { + WarpShflDown().run(true); + } +} \ No newline at end of file diff --git a/catch/unit/warp/warp_shfl_up.cc b/catch/unit/warp/warp_shfl_up.cc new file mode 100644 index 0000000000..201289f363 --- /dev/null +++ b/catch/unit/warp/warp_shfl_up.cc @@ -0,0 +1,120 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. +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 "warp_shfl_common.hh" + +#include + +/** + * @addtogroup shfl_up shfl_up + * @{ + * @ingroup DeviceLanguageTest + * `T __shfl_up(T var, unsigned int lane_delta, int width = warpSize)` - + * Contains unit test for warp shfl_up function + */ + +namespace cg = cooperative_groups; + +template +__global__ void shfl_up(T* const out, const T* const in, const uint64_t* const active_masks, + const unsigned int* const deltas, const int width) { + if (deactivate_thread(active_masks)) { + return; + } + + const auto grid = cg::this_grid(); + const auto block = cg::this_thread_block(); + T var = in[grid.thread_rank()]; + out[grid.thread_rank()] = __shfl_up(var, deltas[block.thread_rank() % width], width); +} + +template class WarpShflUp : public WarpShflTest, T> { + public: + void launch_kernel(T* const arr_dev, T* const input_dev, const uint64_t* const active_masks) { + width_ = generate_width(this->warp_size_); + INFO("Width: " << width_); + const auto alloc_size = width_ * sizeof(unsigned int); + LinearAllocGuard deltas_dev(LinearAllocs::hipMalloc, alloc_size); + deltas_.resize(width_); + std::generate(deltas_.begin(), deltas_.end(), + [this] { return GenerateRandomInteger(0u, static_cast(width_)); }); + HIP_CHECK(hipMemcpy(deltas_dev.ptr(), deltas_.data(), alloc_size, hipMemcpyHostToDevice)); + shfl_up<<grid_.grid_dim_, this->grid_.block_dim_>>>(arr_dev, input_dev, active_masks, + deltas_dev.ptr(), width_); + } + + void validate(const T* const arr, const T* const input) { + ArrayAllOf(arr, this->grid_.thread_count_, [this, &input](unsigned int i) -> std::optional { + const auto rank_in_block = this->grid_.thread_rank_in_block(i).value(); + const auto rank_in_warp = rank_in_block % this->warp_size_; + const auto rank_in_partition = rank_in_block % width_; + const auto mask_idx = this->warps_in_block_ * (i / this->grid_.threads_in_block_count_) + + rank_in_block / this->warp_size_; + const unsigned int delta = deltas_[rank_in_partition] % width_; + const std::bitset active_mask(this->active_masks_[mask_idx]); + + const int target = rank_in_block % width_ - delta; + if (!active_mask.test(rank_in_warp) || + (target >= 0 && !active_mask.test(rank_in_warp - delta))) { + return std::nullopt; + } + + return (target < 0 ? input[i] : input[i - delta]); + }); + }; + + private: + std::vector deltas_; + int width_; +}; + +/** + * Test Description + * ------------------------ + * - Validates the warp shuffle up behavior for all valid width sizes {2, 4, 8, 16, 32, + * 64(if supported)} for generated delta values. The threads are deactivated based on the + * passed active mask. The test is run for all overloads of shfl_up. + * Test source + * ------------------------ + * - unit/warp/warp_shfl_up.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + * - Device supports warp shuffle + */ +TEMPLATE_TEST_CASE("Unit_Warp_Shfl_Up_Positive_Basic", "", int, unsigned int, long, unsigned long, + long long, unsigned long long, float, double) { + int device; + hipDeviceProp_t device_properties; + HIP_CHECK(hipGetDevice(&device)); + HIP_CHECK(hipGetDeviceProperties(&device_properties, device)); + + if (!device_properties.arch.hasWarpShuffle) { + HipTest::HIP_SKIP_TEST("Device doesn't support Warp Shuffle!"); + return; + } + + SECTION("Shfl Up with specified active mask and input values") { + WarpShflUp().run(false); + } + + SECTION("Shfl Down with random active mask and input values") { + WarpShflUp().run(true); + } +} \ No newline at end of file diff --git a/catch/unit/warp/warp_vote_common.hh b/catch/unit/warp/warp_vote_common.hh new file mode 100644 index 0000000000..36809b7507 --- /dev/null +++ b/catch/unit/warp/warp_vote_common.hh @@ -0,0 +1,117 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. +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. +*/ + +#pragma once + +#include "warp_common.hh" + +#include +#include +#include + +inline uint64_t get_predicate_mask(unsigned int test_case, unsigned int warp_size) { + uint64_t predicate_mask = 0; + switch (test_case) { + case 0: // no thread + predicate_mask = 0; + break; + case 1: // 1st thread + predicate_mask = 1; + break; + case 2: // last thread + predicate_mask = static_cast(1) << (warp_size - 1); + break; + case 3: // all threads + predicate_mask = 0xFFFFFFFFFFFFFFFF; + break; + default: // random + static std::mt19937_64 mt(test_case); + std::uniform_int_distribution dist(0, std::numeric_limits::max()); + predicate_mask = dist(mt); + } + return predicate_mask; +} + +inline uint64_t get_active_predicate(uint64_t predicate, size_t partition_size) { + uint64_t active_predicate = predicate; + for (int i = partition_size; i < 64; i++) { + active_predicate &= ~(static_cast(1) << i); + } + return active_predicate; +} + +template class WarpVoteTest { + public: + WarpVoteTest() : warp_size_{get_warp_size()} {} + + void run(bool random = false) { + const auto blocks = GenerateBlockDimensionsForShuffle(); + INFO("Grid dimensions: x " << blocks.x << ", y " << blocks.y << ", z " << blocks.z); + const auto threads = GenerateThreadDimensionsForShuffle(); + INFO("Block dimensions: x " << threads.x << ", y " << threads.y << ", z " << threads.z); + grid_ = CPUGrid(blocks, threads); + + const auto alloc_size = grid_.thread_count_ * sizeof(T); + LinearAllocGuard arr_dev(LinearAllocs::hipMalloc, alloc_size); + LinearAllocGuard arr(LinearAllocs::hipHostMalloc, alloc_size); + HIP_CHECK(hipMemset(arr_dev.ptr(), 0, alloc_size)); + + warps_in_block_ = (grid_.threads_in_block_count_ + warp_size_ - 1) / warp_size_; + const auto warps_in_grid = warps_in_block_ * grid_.block_count_; + LinearAllocGuard active_masks_dev(LinearAllocs::hipMalloc, + warps_in_grid * sizeof(uint64_t)); + active_masks_.resize(warps_in_grid); + + if (random) { + std::generate(active_masks_.begin(), active_masks_.end(), [] { + return GenerateRandomInteger(0ul, std::numeric_limits().max()); + }); + } else { + unsigned long long int i = 0; + std::generate(active_masks_.begin(), active_masks_.end(), + [this, &i]() { return get_active_mask(i++, warp_size_); }); + } + + HIP_CHECK(hipMemcpy(active_masks_dev.ptr(), active_masks_.data(), + warps_in_grid * sizeof(uint64_t), hipMemcpyHostToDevice)); + cast_to_derived().launch_kernel(arr_dev.ptr(), active_masks_dev.ptr()); + HIP_CHECK(hipGetLastError()); + HIP_CHECK(hipMemcpy(arr.ptr(), arr_dev.ptr(), alloc_size, hipMemcpyDeviceToHost)); + HIP_CHECK(hipDeviceSynchronize()); + + cast_to_derived().validate(arr.ptr()); + } + + private: + int get_warp_size() const { + int current_dev = -1; + HIP_CHECK(hipGetDevice(¤t_dev)); + int warp_size = 0u; + HIP_CHECK(hipDeviceGetAttribute(&warp_size, hipDeviceAttributeWarpSize, 0)); + return warp_size; + } + + Derived& cast_to_derived() { return reinterpret_cast(*this); } + + protected: + const int warp_size_; + CPUGrid grid_; + unsigned int warps_in_block_; + std::vector active_masks_; +}; \ No newline at end of file