From d1595dc0c564a6f3e530f2d9a9558b3a0a489ac6 Mon Sep 17 00:00:00 2001 From: "petro.zarytskyi" Date: Tue, 13 Feb 2024 12:53:23 +0200 Subject: [PATCH] Add conversion to void* in the execute function --- docs/userDocs/source/user/CoreConcepts.rst | 2 +- include/clad/Differentiator/Differentiator.h | 23 +- include/clad/Differentiator/FunctionTraits.h | 324 ++++++++++--------- include/clad/Differentiator/NumericalDiff.h | 4 +- lib/Differentiator/ReverseModeVisitor.cpp | 2 +- test/ForwardMode/NotEnoughArgError.C | 2 +- 6 files changed, 193 insertions(+), 164 deletions(-) diff --git a/docs/userDocs/source/user/CoreConcepts.rst b/docs/userDocs/source/user/CoreConcepts.rst index 642822900..87d698461 100644 --- a/docs/userDocs/source/user/CoreConcepts.rst +++ b/docs/userDocs/source/user/CoreConcepts.rst @@ -266,7 +266,7 @@ partial derivative of the function with respect to every input, and as such is used in Clad's reverse mode. The signature of the method is as follows:: template ::type, + typename RetType = typename clad::function_traits::return_type, typename... Args> void central_difference(F f, clad::tape_impl>& _grad, bool printErrors, Args&&... args) { // Similar to the above method, here: diff --git a/include/clad/Differentiator/Differentiator.h b/include/clad/Differentiator/Differentiator.h index a686e35ef..fc1dde14a 100644 --- a/include/clad/Differentiator/Differentiator.h +++ b/include/clad/Differentiator/Differentiator.h @@ -93,35 +93,41 @@ inline CUDA_HOST_DEVICE unsigned int GetLength(const char* code) { /// return f(1.0, 2.0, 0, 0); // for executing non-member functions template ::type = true> CUDA_HOST_DEVICE return_type_t - execute_with_default_args(list, F f, Args&&... args) { + execute_with_default_args(list, F f, list, + Args&&... args) { return f(static_cast(args)..., static_cast(nullptr)...); } template ::type = true> return_type_t execute_with_default_args(list, F f, + list, Args&&... args) { return f(static_cast(args)...); } // for executing member-functions template ::type = true> CUDA_HOST_DEVICE auto execute_with_default_args(list, ReturnType C::*f, Obj&& obj, + list, Args&&... args) -> return_type_t { - return (static_cast(obj).*f)(static_cast(args)..., + return (static_cast(obj).*f)((fArgTypes)(args)..., static_cast(nullptr)...); } template ::type = true> auto execute_with_default_args(list, ReturnType C::*f, Obj&& obj, + list, Args&&... args) -> return_type_t { return (static_cast(obj).*f)(static_cast(args)...); } @@ -238,7 +244,9 @@ inline CUDA_HOST_DEVICE unsigned int GetLength(const char* code) { execute_helper(Fn f, Args&&... args) { // `static_cast` is required here for perfect forwarding. return execute_with_default_args( - DropArgs_t{}, f, static_cast(args)...); + DropArgs_t{}, f, + TakeNFirstArgs_t{}, + static_cast(args)...); } /// Helper functions for executing member derived functions. @@ -256,7 +264,9 @@ inline CUDA_HOST_DEVICE unsigned int GetLength(const char* code) { // `static_cast` is required here for perfect forwarding. return execute_with_default_args( DropArgs_t{}, f, - static_cast(obj), static_cast(args)...); + static_cast(obj), + TakeNFirstArgs_t{}, + static_cast(args)...); } /// If user have not passed object explicitly, then this specialization /// will be used and derived function will be called through the object @@ -270,6 +280,7 @@ inline CUDA_HOST_DEVICE unsigned int GetLength(const char* code) { // `static_cast` is required here for perfect forwarding. return execute_with_default_args( DropArgs_t{}, f, *m_Functor, + TakeNFirstArgs_t{}, static_cast(args)...); } }; diff --git a/include/clad/Differentiator/FunctionTraits.h b/include/clad/Differentiator/FunctionTraits.h index 72cab7438..e8ce9d8ca 100644 --- a/include/clad/Differentiator/FunctionTraits.h +++ b/include/clad/Differentiator/FunctionTraits.h @@ -42,238 +42,290 @@ namespace clad { /// `type` to denote no function type exists. class NoFunction {}; + template struct list {}; // Trait class to deduce return type of function(both member and non-member) at commpile time // Only function pointer types are supported by this trait class template - struct return_type {}; + struct function_traits {}; template - using return_type_t = typename return_type::type; + using return_type_t = typename function_traits::return_type; + template + using argument_types_t = typename function_traits::argument_types; // specializations for non-member functions pointer types template - struct return_type { - using type = ReturnType; + struct function_traits { + using return_type = ReturnType; + using argument_types = list; }; template - struct return_type { - using type = ReturnType; + struct function_traits { + using return_type = ReturnType; + using argument_types = list; }; // specializations for member functions pointer types with no qualifiers template - struct return_type { - using type = ReturnType; + struct function_traits { + using return_type = ReturnType; + using argument_types = list; }; template - struct return_type { - using type = ReturnType; + struct function_traits { + using return_type = ReturnType; + using argument_types = list; }; // specializations for member functions pointer type with only cv-qualifiers template - struct return_type { - using type = ReturnType; + struct function_traits { + using return_type = ReturnType; + using argument_types = list; }; template - struct return_type { - using type = ReturnType; + struct function_traits { + using return_type = ReturnType; + using argument_types = list; }; template - struct return_type { - using type = ReturnType; + struct function_traits { + using return_type = ReturnType; + using argument_types = list; }; template - struct return_type { - using type = ReturnType; + struct function_traits { + using return_type = ReturnType; + using argument_types = list; }; template - struct return_type { - using type = ReturnType; + struct function_traits { + using return_type = ReturnType; + using argument_types = list; }; template - struct return_type { - using type = ReturnType; + struct function_traits { + using return_type = ReturnType; + using argument_types = list; }; // specializations for member functions pointer types with // reference qualifiers and with and without cv-qualifiers template - struct return_type { - using type = ReturnType; + struct function_traits { + using return_type = ReturnType; + using argument_types = list; }; template - struct return_type { - using type = ReturnType; + struct function_traits { + using return_type = ReturnType; + using argument_types = list; }; template - struct return_type { - using type = ReturnType; + struct function_traits { + using return_type = ReturnType; + using argument_types = list; }; template - struct return_type { - using type = ReturnType; + struct function_traits { + using return_type = ReturnType; + using argument_types = list; }; template - struct return_type { - using type = ReturnType; + struct function_traits { + using return_type = ReturnType; + using argument_types = list; }; template - struct return_type { - using type = ReturnType; + struct function_traits { + using return_type = ReturnType; + using argument_types = list; }; template - struct return_type { - using type = ReturnType; + struct function_traits { + using return_type = ReturnType; + using argument_types = list; }; template - struct return_type { - using type = ReturnType; + struct function_traits { + using return_type = ReturnType; + using argument_types = list; }; template - struct return_type { - using type = ReturnType; + struct function_traits { + using return_type = ReturnType; + using argument_types = list; }; template - struct return_type { - using type = ReturnType; + struct function_traits { + using return_type = ReturnType; + using argument_types = list; }; template - struct return_type { - using type = ReturnType; + struct function_traits { + using return_type = ReturnType; + using argument_types = list; }; template - struct return_type { - using type = ReturnType; + struct function_traits { + using return_type = ReturnType; + using argument_types = list; }; template - struct return_type { - using type = ReturnType; + struct function_traits { + using return_type = ReturnType; + using argument_types = list; }; template - struct return_type { - using type = ReturnType; + struct function_traits { + using return_type = ReturnType; + using argument_types = list; }; template - struct return_type { - using type = ReturnType; + struct function_traits { + using return_type = ReturnType; + using argument_types = list; }; template - struct return_type { - using type = ReturnType; + struct function_traits { + using return_type = ReturnType; + using argument_types = list; }; template<> - struct return_type { - using type = void; + struct function_traits { + using return_type = void; + using argument_types = void; }; // specializations for noexcept member functions #if __cpp_noexcept_function_type > 0 template - struct return_type { - using type = ReturnType; + struct function_traits { + using return_type = ReturnType; + using argument_types = list; }; template - struct return_type { - using type = ReturnType; + struct function_traits { + using return_type = ReturnType; + using argument_types = list; }; template - struct return_type { - using type = ReturnType; + struct function_traits { + using return_type = ReturnType; + using argument_types = list; }; template - struct return_type { - using type = ReturnType; + struct function_traits { + using return_type = ReturnType; + using argument_types = list; }; template - struct return_type { - using type = ReturnType; + struct function_traits { + using return_type = ReturnType; + using argument_types = list; }; template - struct return_type { - using type = ReturnType; + struct function_traits { + using return_type = ReturnType; + using argument_types = list; }; template - struct return_type { - using type = ReturnType; + struct function_traits { + using return_type = ReturnType; + using argument_types = list; }; template - struct return_type { - using type = ReturnType; + struct function_traits { + using return_type = ReturnType; + using argument_types = list; }; template - struct return_type { - using type = ReturnType; + struct function_traits { + using return_type = ReturnType; + using argument_types = list; }; template - struct return_type { - using type = ReturnType; + struct function_traits { + using return_type = ReturnType; + using argument_types = list; }; template - struct return_type { - using type = ReturnType; + struct function_traits { + using return_type = ReturnType; + using argument_types = list; }; template - struct return_type { - using type = ReturnType; + struct function_traits { + using return_type = ReturnType; + using argument_types = list; }; template - struct return_type { - using type = ReturnType; + struct function_traits { + using return_type = ReturnType; + using argument_types = list; }; template - struct return_type { - using type = ReturnType; + struct function_traits { + using return_type = ReturnType; + using argument_types = list; }; template - struct return_type { - using type = ReturnType; + struct function_traits { + using return_type = ReturnType; + using argument_types = list; }; template - struct return_type { - using type = ReturnType; + struct function_traits { + using return_type = ReturnType; + using argument_types = list; }; template - struct return_type { - using type = ReturnType; + struct function_traits { + using return_type = ReturnType; + using argument_types = list; }; template - struct return_type { - using type = ReturnType; + struct function_traits { + using return_type = ReturnType; + using argument_types = list; }; template - struct return_type { - using type = ReturnType; + struct function_traits { + using return_type = ReturnType; + using argument_types = list; }; template - struct return_type { - using type = ReturnType; + struct function_traits { + using return_type = ReturnType; + using argument_types = list; }; template - struct return_type { - using type = ReturnType; + struct function_traits { + using return_type = ReturnType; + using argument_types = list; }; template - struct return_type { - using type = ReturnType; + struct function_traits { + using return_type = ReturnType; + using argument_types = list; }; template - struct return_type { - using type = ReturnType; + struct function_traits { + using return_type = ReturnType; + using argument_types = list; }; template - struct return_type { - using type = ReturnType; + using return_type = ReturnType; + using argument_types = list; }; #endif #define REM_CTOR(...) __VA_ARGS__ // Setup for DropArgs - template struct list {}; - struct dummy {}; template struct wrap { @@ -315,60 +367,26 @@ namespace clad { template using Drop_t = decltype(dropUsingIndex(MakeIndexSequence{})); - template struct DropArgs; + template + constexpr auto DropArgs (list) -> Drop_t; // Returns the Args in the function F that come after the Nth arg template - using DropArgs_t = typename DropArgs::type; - - template - struct DropArgs { - using type = Drop_t; - }; - - template - struct DropArgs { - using type = Drop_t; - }; - - /// These macro expansions are used to cover all possible cases of - /// qualifiers in member functions when declaring DropArgs. They need to be - /// read from the bottom to the top. Starting from the use of AddCON, - /// the call to which is used to pass the cases with and without C-style - /// varargs, then as the macro name AddCON says it adds cases of const - /// qualifier. The AddVOL and AddREF macro similarly add cases for volatile - /// qualifier and reference respectively. The AddNOEX adds cases for noexcept - /// qualifier only if it is supported and finally AddSPECS declares the - /// function with all the cases -#define DropArgs_AddSPECS(var, con, vol, ref, noex) \ - template \ - struct DropArgs { \ - using type = Drop_t; \ - }; + using DropArgs_t = decltype(DropArgs(argument_types_t{})); -#if __cpp_noexcept_function_type > 0 -#define DropArgs_AddNOEX(var, con, vol, ref) \ - DropArgs_AddSPECS(var, con, vol, ref, ) \ - DropArgs_AddSPECS(var, con, vol, ref, noexcept) -#else -#define DropArgs_AddNOEX(var, con, vol, ref) \ - DropArgs_AddSPECS(var, con, vol, ref, ) -#endif -#define DropArgs_AddREF(var, con, vol) \ - DropArgs_AddNOEX(var, con, vol, ) DropArgs_AddNOEX(var, con, vol, &) \ - DropArgs_AddNOEX(var, con, vol, &&) + template + constexpr auto TakeNFirstArgs(IndexSequence) -> list>::type...>; -#define DropArgs_AddVOL(var, con) \ - DropArgs_AddREF(var, con, ) DropArgs_AddREF(var, con, volatile) + template + constexpr auto TakeNFirstArgs(list) -> decltype(TakeNFirstArgs(MakeIndexSequence{})); -#define DropArgs_AddCON(var) DropArgs_AddVOL(var, ) DropArgs_AddVOL(var, const) - - DropArgs_AddCON(()) - DropArgs_AddCON((, ...)); // Declares all the specializations + // Returns the first N arguments in the function F + template + using TakeNFirstArgs_t = decltype(TakeNFirstArgs(argument_types_t{}));; template struct OutputParamType { - using type = array_ref::type>; + using type = typename std::remove_pointer::type*; }; template @@ -623,7 +641,7 @@ namespace clad { // HessianDerivedFnTraits specializations for pure function pointer types template struct HessianDerivedFnTraits { - using type = void (*)(Args..., array_ref); + using type = void (*)(Args..., ReturnType*); }; /// These macro expansions are used to cover all possible cases of @@ -638,7 +656,7 @@ namespace clad { #define HessianDerivedFnTraits_AddSPECS(var, cv, vol, ref, noex) \ template \ struct HessianDerivedFnTraits { \ - using type = void (C::*)(Args..., array_ref) cv vol ref noex; \ + using type = void (C::*)(Args..., R*) cv vol ref noex; \ }; #if __cpp_noexcept_function_type > 0 diff --git a/include/clad/Differentiator/NumericalDiff.h b/include/clad/Differentiator/NumericalDiff.h index e8ae560c0..792812be4 100644 --- a/include/clad/Differentiator/NumericalDiff.h +++ b/include/clad/Differentiator/NumericalDiff.h @@ -255,7 +255,7 @@ namespace numerical_diff { /// the input parameter pack. /// \param[in] \c args The arguments to the function to differentiate. template ::type, + typename RetType = typename clad::function_traits::return_type, typename... Args> void central_difference_helper( F f, clad::tape_impl>& _grad, bool printErrors, @@ -339,7 +339,7 @@ namespace numerical_diff { /// diff errors estimates. /// \param[in] \c args The arguments to the function to differentiate. template ::type, + typename RetType = typename clad::function_traits::return_type, typename... Args> void central_difference(F f, clad::tape_impl>& _grad, bool printErrors, Args&&... args) { diff --git a/lib/Differentiator/ReverseModeVisitor.cpp b/lib/Differentiator/ReverseModeVisitor.cpp index 67c48635f..81c69e62b 100644 --- a/lib/Differentiator/ReverseModeVisitor.cpp +++ b/lib/Differentiator/ReverseModeVisitor.cpp @@ -132,7 +132,7 @@ Expr* getArraySizeExpr(const ArrayType* AT, ASTContext& context, // We require each output parameter to be of same type in the overloaded // derived function due to limitations of generating the exact derived // function type at the compile-time (without clad plugin help). - QualType outputParamType = GetCladArrayRefOfType(m_Context.VoidTy); + QualType outputParamType = m_Context.getPointerType(m_Context.VoidTy); llvm::SmallVector paramTypes; diff --git a/test/ForwardMode/NotEnoughArgError.C b/test/ForwardMode/NotEnoughArgError.C index 5517bcafd..3fca2fc06 100644 --- a/test/ForwardMode/NotEnoughArgError.C +++ b/test/ForwardMode/NotEnoughArgError.C @@ -12,7 +12,7 @@ int main () { func1_dx.execute(5); // expected-error@clad/Differentiator/Differentiator.h:* {{too few arguments to function call, expected 2, have 1}} - // expected-note@clad/Differentiator/Differentiator.h:* {{in instantiation of function template specialization 'clad::execute_with_default_args' requested here}} + // expected-note@clad/Differentiator/Differentiator.h:* {{in instantiation of function template specialization 'clad::execute_with_default_args' requested here}} #if __clang_major__ < 16 // expected-note@clad/Differentiator/Differentiator.h:* {{in instantiation of function template specialization 'clad::CladFunction::execute_helper' requested here}} // expected-note@NotEnoughArgError.C:13 {{in instantiation of function template specialization 'clad::CladFunction::execute' requested here}}