-
Notifications
You must be signed in to change notification settings - Fork 122
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for differentiation of immediate functions #1109
Conversation
bf49849
to
d5c0602
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
clang-tidy made some suggestions
class CladFunction { | ||
public: | ||
using CladFunctionType = F; | ||
using FunctorType = FunctorT; | ||
|
||
private: | ||
CladFunctionType m_Function; | ||
char* m_Code; | ||
const char* m_Code; | ||
FunctorType *m_Functor = nullptr; | ||
bool m_CUDAkernel = false; | ||
|
||
public: | ||
CUDA_HOST_DEVICE CladFunction(CladFunctionType f, const char* code, | ||
FunctorType* functor = nullptr, | ||
bool CUDAkernel = false) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
warning: expected ';' at end of declaration list [clang-diagnostic-error]
bool CUDAkernel = false) | |
bool CUDAkernel = false); |
/// Constructor overload for initializing `m_Functor` when functor | ||
/// is passed by reference. | ||
CUDA_HOST_DEVICE | ||
CladFunction(CladFunctionType f, const char* code, FunctorType& functor) | ||
: CladFunction(f, code, &functor) {}; | ||
|
||
constexpr CUDA_HOST_DEVICE CladFunction(CladFunctionType f, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
warning: constructor does not initialize these fields: m_Code [cppcoreguidelines-pro-type-member-init]
include/clad/Differentiator/Differentiator.h:203:
- const char* m_Code;
+ const char* m_Code{};
return static_cast<return_type_t<F>>(0); | ||
} | ||
|
||
/// Return the string representation for the generated derivative. | ||
const char* getCode() const { | ||
constexpr const char* getCode() const { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
warning: function 'getCode' should be marked [[nodiscard]] [modernize-use-nodiscard]
constexpr const char* getCode() const { | |
[[nodiscard]] constexpr const char* getCode() const { |
template <unsigned... BitMaskedOpts, typename ArgSpec = const char*, | ||
typename F, typename... Args> | ||
return_type_t<F> constexpr differentiate_and_execute(F fn, ArgSpec args = "", | ||
Args&&... args_) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
warning: invalid case style for parameter 'args_' [readability-identifier-naming]
Args&&... args_) { | |
Args&&... args) { |
include/clad/Differentiator/Differentiator.h:474:
- std::forward<Args>(args_)...);
+ std::forward<Args>(args)...);
lib/Differentiator/DiffPlanner.cpp
Outdated
call->setArg(codeArgIdx, newArg); | ||
if (derivedFnArgIdx != -1) { | ||
// Add the "&" operator | ||
auto newUnOp = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
warning: 'auto newUnOp' can be declared as 'auto *newUnOp' [llvm-qualified-auto]
auto newUnOp = | |
auto *newUnOp = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That seems a good suggestion.
d5c0602
to
7476842
Compare
7476842
to
866081e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
clang-tidy made some suggestions
template <unsigned... BitMaskedOpts, typename ArgSpec = const char*, | ||
typename F, typename... Args> | ||
return_type_t<F> constexpr differentiate_and_execute(F fn, ArgSpec args = "", | ||
Args&&... args_) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
warning: invalid case style for parameter 'args_' [readability-identifier-naming]
Args&&... args_) { | |
Args&&... args) { |
include/clad/Differentiator/Differentiator.h:471:
- std::forward<Args>(args_)...);
+ std::forward<Args>(args)...);
3b4cbd3
to
26d7071
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
clang-tidy made some suggestions
CUDA_HOST_DEVICE std::size_t size() const { return m_size; } | ||
CUDA_HOST_DEVICE PUREFUNC T* ptr() const { return m_arr; } | ||
CUDA_HOST_DEVICE PUREFUNC T*& ptr_ref() { return m_arr; } | ||
constexpr CUDA_HOST_DEVICE std::size_t size() const { return m_size; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
warning: function 'size' should be marked [[nodiscard]] [modernize-use-nodiscard]
constexpr CUDA_HOST_DEVICE std::size_t size() const { return m_size; } | |
[[nodiscard]] constexpr CUDA_HOST_DEVICE std::size_t size() const { return m_size; } |
bool CUDAkernel = false) | ||
: m_Function(f), m_Functor(functor), m_CUDAkernel(CUDAkernel) { | ||
#ifndef __CLAD_SO_LOADED | ||
static_assert(false, "clad doesn't appear to be loaded; make sure that " |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
warning: static assertion failed: clad doesn't appear to be loaded; make sure that you pass clad.so to clang. [clang-diagnostic-error]
static_assert(false, "clad doesn't appear to be loaded; make sure that "
^
/// Constructor overload for initializing `m_Functor` when functor | ||
/// is passed by reference. | ||
CUDA_HOST_DEVICE | ||
CladFunction(CladFunctionType f, const char* code, FunctorType& functor) | ||
CUDA_HOST_DEVICE CladFunction(CladFunctionType f, const char* code, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
warning: constructor does not initialize these fields: m_Code [cppcoreguidelines-pro-type-member-init]
include/clad/Differentiator/Differentiator.h:203:
- const char* m_Code;
+ const char* m_Code{};
: CladFunction(f, code, &functor) {}; | ||
|
||
constexpr CUDA_HOST_DEVICE CladFunction(CladFunctionType f, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
warning: constructor does not initialize these fields: m_Code [cppcoreguidelines-pro-type-member-init]
constexpr CUDA_HOST_DEVICE CladFunction(CladFunctionType f,
^
d5954b9
to
c213322
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
clang-tidy made some suggestions
@@ -65,6 +65,9 @@ struct DiffRequest { | |||
/// A flag to enable TBR analysis during reverse-mode differentiation. | |||
bool EnableTBRAnalysis = false; | |||
bool EnableVariedAnalysis = false; | |||
/// A flag specifying whether this differentiation is to be used | |||
/// in immediate contexts. | |||
bool ImmediateMode = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
warning: member variable 'ImmediateMode' has public visibility [cppcoreguidelines-non-private-member-variables-in-classes]
bool ImmediateMode = false;
^
482d0dc
to
fa19625
Compare
fa19625
to
ef992db
Compare
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## master #1109 +/- ##
==========================================
+ Coverage 94.43% 94.44% +0.01%
==========================================
Files 50 50
Lines 8751 8770 +19
==========================================
+ Hits 8264 8283 +19
Misses 487 487
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
clang-tidy made some suggestions
/// Constructor overload for initializing `m_Functor` when functor | ||
/// is passed by reference. | ||
CUDA_HOST_DEVICE | ||
CladFunction(CladFunctionType f, const char* code, FunctorType& functor) | ||
CUDA_HOST_DEVICE CladFunction(CladFunctionType f, const char* code, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
warning: constructor does not initialize these fields: m_Code [cppcoreguidelines-pro-type-member-init]
include/clad/Differentiator/Differentiator.h:202:
- const char* m_Code;
+ const char* m_Code{};
tools/ClangPlugin.cpp
Outdated
for (DiffRequest& request : m_DiffRequestGraph.getNodes()) { | ||
if (!request.ImmediateMode || | ||
(!request.Function->isConstexpr() && | ||
!request.Function->isImmediateFunction())) | ||
continue; | ||
|
||
m_DiffRequestGraph.setCurrentProcessingNode(request); | ||
ProcessDiffRequest(request); | ||
m_DiffRequestGraph.markCurrentNodeProcessed(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for (DiffRequest& request : m_DiffRequestGraph.getNodes()) { | |
if (!request.ImmediateMode || | |
(!request.Function->isConstexpr() && | |
!request.Function->isImmediateFunction())) | |
continue; | |
m_DiffRequestGraph.setCurrentProcessingNode(request); | |
ProcessDiffRequest(request); | |
m_DiffRequestGraph.markCurrentNodeProcessed(); | |
for (DiffRequest& request : m_DiffRequestGraph.getNodes()) { | |
if (request.ImmediateMode && | |
(request.Function->isConstexpr() || | |
request.Function->isImmediateFunction())) { | |
m_DiffRequestGraph.setCurrentProcessingNode(request); | |
ProcessDiffRequest(request); | |
m_DiffRequestGraph.markCurrentNodeProcessed(); | |
} |
I am not sure why we need to iterate over all of the requests, though...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this method HandleTopLevelDeclForClad
we are given the new DeclGroupRef
and then the DiffCollector
adds the new nodes to the differentiation graph, but we don't really have a way to see what is new. I don't think it would be correct to move the processing part inside the collector as that's not it's purpose. Maybe there should be some way for the collector to give references to just the nodes that it added on it's last traversal? Should I do that or do you have something else in mind?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am wondering if we shouldn't check if there is a FunctionDecl
in DGR
and ask it if it were an immediate function?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DGR
is the top level decl, so to find the just the immediate FunctionDecl
s we'd need to traverse all of it again, which doesn't sound like a great idea, when that's what DiffCollector
already does, or am I misunderstanding?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, yes you are right. Please then just invert the condition as suggested and I think we might be good to go.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, I'll do that, but also I think that I'll have some documentation ready by tomorrow, so maybe I can include that in this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed. We need a demo but that can be in a separate PR.
1c243ec
to
28343d5
Compare
28343d5
to
9fdea68
Compare
515d0ba
to
6d1e43e
Compare
The derivatives that Clad generates are valid C++ code, which | ||
could in theory be executed at compile-time (or in an immediate | ||
context as the C++ standard calls it). When a function is differentiated | ||
all attributes, such as `constexpr` and `consteval` are kept, but |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
all attributes, such as `constexpr` and `consteval` are kept, but | |
all specifiers, such as `constexpr` and `consteval` are kept, but |
Usage of Clad's immediate mode | ||
================================================ | ||
|
||
The following code snippet shows how one can request Clad to use the immediate mode for |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We tend to break at 80 columns.
loops, etc. are not yet usable in an immediate context. | ||
|
||
Both `constexpr` and `consteval` are supported as Clad doesn't actually rely on these | ||
specific keywords for it's support, but instead uses clang's API to determine if the functions |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
specific keywords for it's support, but instead uses clang's API to determine if the functions | |
specific keywords for its support, but instead uses clang's API to determine if the functions |
tools/ClangPlugin.cpp
Outdated
for (DiffRequest& request : m_DiffRequestGraph.getNodes()) { | ||
if (request.ImmediateMode && | ||
(request.Function->isConstexpr() || | ||
request.Function->isImmediateFunction())) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any chance to write a test that hits here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems like isImmediateFunction
isn't being hit, because isConstexpr
is checking the ConstexprSpecKind
, which contains information for constexpr, consteval and constinit and even if I remove the isImmediateFunction
check the tests still pass, but I believe that is only the case, because with the current implementation we are guaranteed that clang would've set the ConstexprSpecKind
before we process the FunctionDecl
. So would it be better to keep that assumption and remove the second check or keep it, even though it's not tested currently?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah. Removing the check makes sense.
38f708f
to
1ec232a
Compare
1ec232a
to
c8395b0
Compare
c8395b0
to
27f1892
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lgtm!
No description provided.