diff --git a/CMakeLists.txt b/CMakeLists.txt index 2496c559d..823899c26 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -296,6 +296,7 @@ if (CLAD_INCLUDE_DOCS) endif() if (NOT CLAD_BUILD_STATIC_ONLY) + add_subdirectory(unittests) add_subdirectory(test) add_subdirectory(demos/ErrorEstimation/CustomModel) add_subdirectory(demos/ErrorEstimation/PrintModel) diff --git a/cmake/modules/AddCladBenchmark.cmake b/cmake/modules/AddCladBenchmark.cmake index 17983d0aa..aae26a223 100644 --- a/cmake/modules/AddCladBenchmark.cmake +++ b/cmake/modules/AddCladBenchmark.cmake @@ -10,31 +10,88 @@ string(REPLACE "/" "" CURRENT_REPO_COMMIT ${CURRENT_REPO_COMMIT}) set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${CMAKE_SOURCE_DIR}/.git/HEAD") - # Change the default compiler to the clang which we run clad upon. set(CMAKE_CXX_COMPILER ${LLVM_TOOLS_BINARY_DIR}/clang) -#---------------------------------------------------------------------------- -# function CB_ADD_GBENCHMARK( source1 source2... LIBRARIES libs) -#---------------------------------------------------------------------------- -function(CB_ADD_GBENCHMARK benchmark) - cmake_parse_arguments(ARG "" "LABEL" "DEPENDS;LIBRARIES" ${ARGN}) - set(source_files ${ARG_UNPARSED_ARGUMENTS}) - - add_executable(${benchmark} ${source_files}) +#------------------------------------------------------------------------------- +# function ENABLE_CLAD_FOR_EXECUTABLE( +# DEPENDS dependencies... +# A list of targets that the executable depends on. +# LIBRARIES libraries... +# A list of libraries to be linked in. Defaults to stdc++ pthread m. +# ) +#------------------------------------------------------------------------------- +function(ENABLE_CLAD_FOR_EXECUTABLE executable) + if (NOT TARGET ${executable}) + message(FATAL_ERROR "'${executable}' is not a valid target.") + endif() # Add the clad plugin - target_compile_options(${benchmark} PUBLIC -fplugin=$) + target_compile_options(${executable} PUBLIC -fplugin=$) # Debugging. Emitting the derivatives' source code. - #target_compile_options(${benchmark} PUBLIC "SHELL:-Xclang -plugin-arg-clad" + #target_compile_options(${executable} PUBLIC "SHELL:-Xclang -plugin-arg-clad" # "SHELL: -Xclang -fdump-derived-fn") # Debugging. Emit llvm IR. - #target_compile_options(${benchmark} PUBLIC -S -emit-llvm) + #target_compile_options(${executable} PUBLIC -S -emit-llvm) # Debugging. Optimization misses. - #target_compile_options(${benchmark} PUBLIC "SHELL:-Xclang -Rpass-missed=.*inline.*") + #target_compile_options(${executable} PUBLIC "SHELL:-Xclang -Rpass-missed=.*inline.*") + + # Clad requires us to link against these libraries. + target_link_libraries(${executable} PUBLIC stdc++ pthread m) + + target_include_directories(${executable} PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) + set_property(TARGET ${executable} PROPERTY RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + + add_dependencies(${executable} clad) + # If clad.so changes we don't need to relink but to rebuild the source files. + # $ does not work for OBJECT_DEPENDS. + set (CLAD_SO_PATH "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/clad${CMAKE_SHARED_LIBRARY_SUFFIX}") + set_source_files_properties(${source_files} PROPERTY OBJECT_DEPENDS ${CLAD_SO_PATH}) + + # Add dependencies to executable + if(ARG_DEPENDS) + add_dependencies(${executable} ${ARG_DEPENDS}) + endif(ARG_DEPENDS) + + +endfunction(ENABLE_CLAD_FOR_EXECUTABLE) + +#------------------------------------------------------------------------------- +# function ADD_CLAD_EXECUTABLE( sources... +# DEPENDS dependencies... +# A list of targets that the executable depends on. +# LIBRARIES libraries... +# A list of libraries to be linked in. Defaults to stdc++ pthread m. +# ) +#------------------------------------------------------------------------------- +function(ADD_CLAD_EXECUTABLE executable) + cmake_parse_arguments(ARG "" "DEPENDS;LIBRARIES" "" ${ARGN}) + + set(source_files ${ARG_UNPARSED_ARGUMENTS}) + + add_executable(${executable} ${source_files}) + + ENABLE_CLAD_FOR_EXECUTABLE(${executable} ${ARGN}) + +endfunction(ADD_CLAD_EXECUTABLE) + +#------------------------------------------------------------------------------- +# function CB_ADD_GBENCHMARK( sources +# LABEL +# A label that classifies how much time a benchmark is expected to take. +# Short benchmarks time out at 1200 seconds, long at 2400. +# DEPENDS dependencies... +# A list of targets that the executable depends on. +# LIBRARIES libraries... +# A list of libraries to be linked in. Defaults to stdc++ pthread m. +# ) +#------------------------------------------------------------------------------- +function(CB_ADD_GBENCHMARK benchmark) + cmake_parse_arguments(ARG "" "LABEL" "" ${ARGN}) + ADD_CLAD_EXECUTABLE(${benchmark} ${ARG_UNPARSED_ARGUMENTS}) # Optimize the produced code. target_compile_options(${benchmark} PUBLIC -O3) @@ -42,23 +99,14 @@ function(CB_ADD_GBENCHMARK benchmark) # Turn off numerical diff fallback. target_compile_definitions(${benchmark} PUBLIC CLAD_NO_NUM_DIFF) - # Clad requires us to link against these libraries. - target_link_libraries(${benchmark} PUBLIC stdc++ pthread m) - - target_include_directories(${benchmark} PUBLIC ${CMAKE_CURRENT_BINARY_DIR} ${GBENCHMARK_INCLUDE_DIR}) - set_property(TARGET ${benchmark} PROPERTY RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + # Add GBenchmark includes + target_include_directories(${executable} PUBLIC ${GBENCHMARK_INCLUDE_DIR}) target_link_libraries(${benchmark} PUBLIC ${ARG_LIBRARIES} gbenchmark) if (NOT APPLE) target_link_libraries(${benchmark} PUBLIC rt) endif() - add_dependencies(${benchmark} clad) - # If clad.so changes we don't need to relink but to rebuild the source files. - # $ does not work for OBJECT_DEPENDS. - set (CLAD_SO_PATH "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/clad${CMAKE_SHARED_LIBRARY_SUFFIX}") - set_source_files_properties(${source_files} PROPERTY OBJECT_DEPENDS ${CLAD_SO_PATH}) - set (TIMEOUT_VALUE 1200) set (LABEL "short") if (ARG_LABEL AND "${ARG_LABEL}" STREQUAL "long") @@ -66,11 +114,6 @@ function(CB_ADD_GBENCHMARK benchmark) set (LABEL "long") endif() - # Add dependencies to benchmark - if(ARG_DEPENDS) - add_dependencies(${benchmark} ${ARG_DEPENDS}) - endif() - # Add benchmark as a CTest add_test(NAME clad-${benchmark} COMMAND ${benchmark} --benchmark_out_format=json @@ -82,3 +125,27 @@ function(CB_ADD_GBENCHMARK benchmark) RUN_SERIAL TRUE DEPENDS ${benchmark}) endfunction(CB_ADD_GBENCHMARK) + +#------------------------------------------------------------------------------- +# function ADD_CLAD_GTEST( sources +# DEPENDS dependencies... +# A list of targets that the executable depends on. +# LIBRARIES libraries... +# A list of libraries to be linked in. Defaults to stdc++ pthread m. +# ) +#------------------------------------------------------------------------------- +function(ADD_CLAD_GTEST gtest) + ADD_CLAD_EXECUTABLE(${gtest} ${ARGN}) + + set(gtest_libs gtest gtest_main) + # Clang prior than clang13 (I think) merges both gmock into gtest. + if (TARGET gmock) + list(APPEND gtest_libs gmock gmock_main) + endif() + + target_link_libraries(${gtest} PUBLIC ${ARG_LIBRARIES} ${gtest_libs}) + if (NOT APPLE) + #target_link_libraries(${gtest} PUBLIC rt) + endif() + +endfunction(ADD_CLAD_GTEST) diff --git a/include/clad/Differentiator/CladConfig.h b/include/clad/Differentiator/CladConfig.h index 8ed22421b..4ad838a31 100644 --- a/include/clad/Differentiator/CladConfig.h +++ b/include/clad/Differentiator/CladConfig.h @@ -54,16 +54,10 @@ constexpr unsigned GetBitmaskedOpts(unsigned const first, Opts... opts) { // Define trap function that is a CUDA compatible replacement for // exit(int code) function #ifdef __CUDACC__ -__device__ void trap(int code) { - asm("trap;"); -} -__host__ void trap(int code) { - exit(code); -} +inline __device__ void trap(int code) { asm("trap;"); } +inline __host__ void trap(int code) { exit(code); } #else -void trap(int code) { - exit(code); -} +inline void trap(int code) { exit(code); } #endif #ifdef __CUDACC__ diff --git a/include/clad/Differentiator/Differentiator.h b/include/clad/Differentiator/Differentiator.h index 49797a7ba..5468d6773 100644 --- a/include/clad/Differentiator/Differentiator.h +++ b/include/clad/Differentiator/Differentiator.h @@ -27,20 +27,20 @@ template struct ValueAndAdjoint { }; /// \returns the size of a c-style string - CUDA_HOST_DEVICE unsigned int GetLength(const char* code) { - unsigned int count; - const char* code_copy = code; - #ifdef __CUDACC__ - count = 0; - while (*code_copy != '\0') { - count++; - code_copy++; - } - #else - count = strlen(code_copy); - #endif - return count; +inline CUDA_HOST_DEVICE unsigned int GetLength(const char* code) { + unsigned int count; + const char* code_copy = code; +#ifdef __CUDACC__ + count = 0; + while (*code_copy != '\0') { + count++; + code_copy++; } +#else + count = strlen(code_copy); +#endif + return count; +} /// Tape type used for storing values in reverse-mode AD inside loops. template diff --git a/include/clad/Differentiator/NumericalDiff.h b/include/clad/Differentiator/NumericalDiff.h index 5f090d793..e8ae560c0 100644 --- a/include/clad/Differentiator/NumericalDiff.h +++ b/include/clad/Differentiator/NumericalDiff.h @@ -73,7 +73,10 @@ namespace numerical_diff { /// A buffer manager to request for buffer space /// while forwarding reference/pointer args to the target function. - ManageBufferSpace bufferManager; + inline ManageBufferSpace& getBufferManager() { + static ManageBufferSpace bufMan; + return bufMan; + } /// The precision to do the numerical differentiation calculations in. using precision = double; @@ -87,7 +90,7 @@ namespace numerical_diff { /// \param[in] \c h The h to make representable. /// /// \returns A value of h that does not result in catastrohic cancellation. - precision make_h_representable(precision x, precision h) { + inline precision make_h_representable(precision x, precision h) { precision xph = x + h; precision dx = xph - x; @@ -104,7 +107,7 @@ namespace numerical_diff { /// \param[in] \c arg The input argument to adjust and get h for. /// /// \returns A calculated and adjusted h value. - precision get_h(precision arg) { + inline precision get_h(precision arg) { // First get a suitable h value, we do all of this in elevated precision // (default double). Maximum error in h = eps^4/5 precision h = std::pow(11.25 * std::numeric_limits::epsilon(), @@ -126,8 +129,8 @@ namespace numerical_diff { /// belongs. /// \param[in] \c arrPos The position of the array element /// (-1 if parameter is scalar) to which the error belongs. - void printError(precision derivError, precision evalError, unsigned paramPos, - int arrPos = -1) { + inline void printError(precision derivError, precision evalError, + unsigned paramPos, int arrPos = -1) { if (arrPos != -1) printf("\nError Report for parameter at position %d and index %d:\n", paramPos, arrPos); @@ -227,7 +230,7 @@ namespace numerical_diff { // this is required to make sure that we are retuning a deep copy // that is valid throughout the scope of the central_diff function. // Temp is system owned. - T* temp = bufferManager.make_buffer_space(n); + T* temp = getBufferManager().make_buffer_space(n); // deepcopy for (std::size_t j = 0; j < n; j++) { temp[j] = arg[j]; @@ -318,7 +321,7 @@ namespace numerical_diff { // five-point stencil formula = (4f[x+h, x-h] - f[x+2h, x-2h])/3 _grad[i][j] = 4.0 * xf1 / 3.0 - xf2 / 3.0; - bufferManager.free_buffer(); + getBufferManager().free_buffer(); } } } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 823109517..a37a418ad 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -29,6 +29,13 @@ configure_lit_site_cfg( ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg ) +configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/Unit/lit.site.cfg.py.in + ${CMAKE_CURRENT_BINARY_DIR}/Unit/lit.site.cfg.py + MAIN_CONFIG + ${CMAKE_CURRENT_SOURCE_DIR}/Unit/lit.cfg.py + ) + option(CLANG_TEST_USE_VG "Run Clang tests under Valgrind" OFF) if(CLANG_TEST_USE_VG) set(CLANG_TEST_EXTRA_ARGS ${CLANG_TEST_EXTRA_ARGS} "--vg") @@ -44,6 +51,12 @@ set(CLAD_TEST_PARAMS clad_site_config=${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg ) +# Unit tests +list(APPEND CLAD_TEST_DEPS CladUnitTests) +list(APPEND CLAD_TEST_PARAMS + clad_site_config=${CMAKE_CURRENT_BINARY_DIR}/Unit/lit.site.cfg + ) + add_custom_target(clad-test-depends DEPENDS ${CLAD_TEST_DEPS}) set_target_properties(clad-test-depends PROPERTIES FOLDER "Clad tests") diff --git a/unittests/Basic/CMakeLists.txt b/unittests/Basic/CMakeLists.txt new file mode 100644 index 000000000..48da2fd0b --- /dev/null +++ b/unittests/Basic/CMakeLists.txt @@ -0,0 +1,4 @@ +add_clad_unittest(DifferentiatorHTests + DifferentiatorH.cpp + MultiIncludeDifferentiatorH.cpp + ) diff --git a/unittests/Basic/DifferentiatorH.cpp b/unittests/Basic/DifferentiatorH.cpp new file mode 100644 index 000000000..34157c910 --- /dev/null +++ b/unittests/Basic/DifferentiatorH.cpp @@ -0,0 +1,8 @@ +#include "clad/Differentiator/Differentiator.h" + +#include "gtest/gtest.h" + +TEST(DifferentiatorH, GetLength) { + EXPECT_TRUE(clad::GetLength("") == 0); + EXPECT_TRUE(clad::GetLength("abc") == 3); +} diff --git a/unittests/Basic/MultiIncludeDifferentiatorH.cpp b/unittests/Basic/MultiIncludeDifferentiatorH.cpp new file mode 100644 index 000000000..d6e5c5179 --- /dev/null +++ b/unittests/Basic/MultiIncludeDifferentiatorH.cpp @@ -0,0 +1 @@ +#include "clad/Differentiator/Differentiator.h" diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt new file mode 100644 index 000000000..5213b4611 --- /dev/null +++ b/unittests/CMakeLists.txt @@ -0,0 +1,35 @@ +add_custom_target(CladUnitTests) +set_target_properties(CladUnitTests PROPERTIES FOLDER "Clang tests") + +# LLVM builds (not installed llvm) provides gtest. +if (NOT TARGET gtest) + include(CladGoogleTest) + include(AddCladBenchmark) +endif() + +# add_clad_unittest(test_dirname file1.cpp file2.cpp) +# +# Will compile the list of files together and link against clad. +# Produces a binary named 'basename(test_dirname)'. +function(add_clad_unittest test_dirname) + add_unittest(CladUnitTests ${test_dirname} ${ARGN}) + + # Remove the llvm_gtest_* coming from add_unittest. + get_target_property(GTEST_LINKED_LIBS ${test_dirname} LINK_LIBRARIES) + list(REMOVE_ITEM GTEST_LINKED_LIBS llvm_gtest_main llvm_gtest) + set_property(TARGET ${test_dirname} PROPERTY LINK_LIBRARIES ${GTEST_LINKED_LIBS}) + + enable_clad_for_executable(${test_dirname}) + #target_include_directories(${test_dirname}) + set(gtest_libs gtest gtest_main) + # Clang prior than clang13 (I think) merges both gmock into gtest. + if (TARGET gmock) + list(APPEND gtest_libs gmock gmock_main) + endif() + + target_link_libraries(${test_dirname} PUBLIC ${gtest_libs}) + +endfunction() + +add_subdirectory(Basic) +