-
Notifications
You must be signed in to change notification settings - Fork 1.5k
findtoken.h: make sure code is being matchcompiled #6641
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
Conversation
Clang 18 This should help with https://trac.cppcheck.net/ticket/12271. Detecting match calls in headers is tracked in https://trac.cppcheck.net/ticket/11541. This is obviously extremely horrible and needs to be done differently. Because templates are involved I could not come up with something better for now - but because of the sizable impact I published it anyways. |
The example from https://trac.cppcheck.net/ticket/10765#comment:4. Clang 17 |
@pfultz2 Care to have a look at this? Thanks! |
The example from https://trac.cppcheck.net/ticket/10765#comment:4. Clang 17 This makes a lot of difference and we really need to figure out how to get this change in properly. |
lib/findtoken.h
Outdated
} | ||
if (Token::Match(tok, "if|for|while (") && Token::simpleMatch(tok->linkAt(1), ") {")) { | ||
//if (Token::Match(tok, "if|for|while (") && Token::simpleMatch(tok->linkAt(1), ") {")) { | ||
if (findTokensSkipDeadCodeImplMatch_1(tok)) { |
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.
Why cant the match compiler be updated to automatically generate this? This seems very problematic.
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.
Seems obvious in hindsight. But thinking about it I think there might be problems.
That would cause the matchcompiler.h
include to spill which seems undesired. Same with the match functions. The latter will probably cause conflicts in other matchcompiled files which include this.
Can check thought if if it would work if we also process header files. Would be a better approach - albeit I would prefer if it only were temporary.
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 would cause the matchcompiler.h include to spill which seems undesired
What do you mean spill? There is a header guard.
Same with the match functions. The latter will probably cause conflicts in other matchcompiled files which include this.
I thought each function is appended a unique number.
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 would cause the matchcompiler.h include to spill which seems undesired
What do you mean spill? There is a header guard.
I mean that it will be includes in all files including this even when not necessary. It is just not clean.
Same with the match functions. The latter will probably cause conflicts in other matchcompiled files which include this.
I thought each function is appended a unique number.
I am not sure if that is unique globally or locally. The first is unnecessary since we only generate functions within source files. But will immediately come up if I try to include the headers.
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 just remembered the main issue.
The files need to be matchcompiled thus you need to store the modified files and build those. But if you do that with headers you also need to inject an additional header path in release builds and you two headers of the same name which might also cause issues.
So it is not just include the *.h
in the matchcompiler.
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 can move this header to another directory thats not in the include path. For matchcompiler builds we can add the include path to the directory where these files are dumped, and without matchcompiler we can just add the include path to the original header.
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 have no "other directories". We are using a flat structure and introducing an additional folder (for a single file mind you) will have a ripple effect. This needs to be addressed in a different way for each build system and I have the feeling that would not work at all for the Visual Studio release build we perform in the CI.
I tried introducing support for subfolders for the sources in the matchcompiler at some point. It was a pain, very hacky and I never got it to work fully so I abandoned it. I did that when I proposed the splitting of valueflow.cpp
.
On a side note. I just realized that the matchcompiler is not integrated at all in the Visual Studio project file we are providing so people using that to build their local binary will have a much slower executable. Another reason to dump these files and go with CMake all the way.
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 have no "other directories". We are using a flat structure and introducing an additional folder (for a single file mind you) will have a ripple effect. This needs to be addressed in a different way for each build system and I have the feeling that would not work at all for the Visual Studio release build we perform in the CI.
We can always create a directory. Then we can get it running using cmake. For the other build systems, its just a matter of adding an include path. There wont be a perf regression for these other build systems because they are not using the matchcompiler on header files in the first place. Users of these build systems can provide the support to improve it. We shouldn't make it a barrier for improving code quality.
Also, it is a single file, but this will make it easier to add more header-only files in the future.
Another reason to dump these files and go with CMake all the way.
I agree. CMake already has a lot of enhancements already over the other builds such as parallel test running, and this will be another additional enhancement.
042c118
to
aa64d95
Compare
I do not want the changes to go in this way but I am marking this for review so I hopefully get some more feedback on how to resolve this. |
lib/findtoken.h
Outdated
return result; | ||
} | ||
|
||
bool findTokensSkipDeadCodeImplMatch_1(const Token* tok); |
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 think it's unfortunate to separate the match patterns from the code. It can lead to bugs. Is it possible to move the whole function implementation to the cpp file..
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 think it's unfortunate to separate the match patterns from the code. It can lead to bugs.
Yeah - not my intention at all to get this code in. As I wrote I want to get help on how to get this in properly.
Is it possible to move the whole function implementation to the cpp file..
Since templates are involved that is problematic. I failed in my attempts so far.
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.
hmm.. I haven't tried to take this the whole way but I have the idea we could do something like this. Move the findTokensSkipDeadCodeImpl
template to the findtoken.cpp file.
In findtoken.h:
std::vector<Token*> findTokensSkipDeadCodeCpp(const Library& library,
Token* start,
const Token* end,
const std::function<bool(const Token*)>& pred,
const std::function<std::vector<MathLib::bigint>(const Token*)>& evaluate);
std::vector<const Token*> findTokensSkipDeadCodeCpp(const Library& library,
const Token* start,
const Token* end,
const std::function<bool(const Token*)>& pred,
const std::function<std::vector<MathLib::bigint>(const Token*)>& evaluate);
template<class T, class Predicate, class Evaluate, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
std::vector<T*> findTokensSkipDeadCode(const Library& library,
T* start,
const Token* end,
const Predicate& pred,
const Evaluate& evaluate)
{
std::function<bool(const Token*)> predFunc = pred;
std::function<std::vector<MathLib::bigint>(const Token*)> evaluateFunc = evaluate;
return findTokensSkipDeadCodeCpp(library, start, end, predFunc, evaluateFunc);
}
The findTokensSkipDeadCodeCpp
are defined in the findtoken.cpp and invokes the findTokensSkipDeadCodeImpl
.
The code does get more messy as there are "Cpp" intermediate 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.
I have a solution which builds. It is a bit messier than the hack in terms of total changes. I will give it a spin and update the PR later.
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.
Done. It is even a tad faster than the previous hack. Will post profiling numbers later.
8a467bc
to
4eb26f1
Compare
Clang 19 The example from https://trac.cppcheck.net/ticket/10765#comment:4. Clang 19 callgrind in the CI |
4eb26f1
to
2b557f5
Compare
nice speedups! I am curious.. do you have any theory why there is speedup it just looks like a refactoring to me? |
The |
ah yes! 👍 |
@pfultz2 does this look okay to you? |
lib/findtoken.h
Outdated
const std::function<bool(const Token *)> &pred, | ||
const std::function<bool(T *)>& found, | ||
const std::function<std::vector<MathLib::bigint>(const Token *)> &evaluate, | ||
bool skipUnevaluated) = delete; |
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.
Why does this need to be deleted?
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.
Because we are only using specializations. If this is not deleted the compiler will complain about it missing.
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, I see. Using templates makes no sense anymore everything related to it can just be dropped.
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.
Done.
lib/findtoken.h
Outdated
const std::function<std::vector<MathLib::bigint>(const Token *)> &evaluate, | ||
bool skipUnevaluated) = delete; | ||
|
||
template<> |
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.
This line can be removed.
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.
This and the others are template specializations. I thought those needed to be indicated as templates as well.
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.
Done.
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.
Yea its simpler to treat it as function overloading.
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.
Too many trees ...
lib/findtoken.h
Outdated
const std::function<std::vector<MathLib::bigint>(const Token *)> &evaluate, | ||
bool skipUnevaluated); | ||
|
||
template<> |
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.
This line can be removed.
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.
Done.
lib/findtoken.h
Outdated
std::function<bool(T*)> f = [&](T* tok) { | ||
result.push_back(tok); | ||
return 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.
Why cant this be defined inline?
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.
Ask the compiler. It cannot convert the lambda to std::function
if it is inline.
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 file included from /home/user/CLionProjects/cppcheck/lib/valueflow.cpp:87:
/home/user/CLionProjects/cppcheck/lib/findtoken.h:114:11: error: no matching function for call to 'findTokensSkipDeadCodeImpl'
114 | (void)internal::findTokensSkipDeadCodeImpl(
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/user/CLionProjects/cppcheck/lib/findtoken.h:131:12: note: in instantiation of function template specialization 'findTokensSkipDeadCode<Token, (lambda at /home/user/CLionProjects/cppcheck/lib/valueflow.cpp:5826:67), std::vector<long long> (*)(const Token *), void>' requested here
131 | return findTokensSkipDeadCode(library, start, end, pred, &evaluateKnownValues);
| ^
/home/user/CLionProjects/cppcheck/lib/valueflow.cpp:5826:12: note: in instantiation of function template specialization 'findTokensSkipDeadCode<Token, (lambda at /home/user/CLionProjects/cppcheck/lib/valueflow.cpp:5826:67), void>' requested here
5826 | return findTokensSkipDeadCode(library, start, scope->bodyEnd, [&](const Token* tok) {
| ^
/home/user/CLionProjects/cppcheck/lib/findtoken.h:79:10: note: candidate template ignored: could not match 'std::function<bool (T *)>' against '(lambda at /home/user/CLionProjects/cppcheck/lib/findtoken.h:119:9)'
79 | bool findTokensSkipDeadCodeImpl(const Library &library,
| ^
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 - without the template stuff that now works again.
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.
Done.
lib/findtoken.cpp
Outdated
#include "token.h" | ||
|
||
template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )> | ||
static bool findTokensSkipDeadCodeImpl2(const Library& library, |
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.
How about we name this findTokensSkipDeadCodeImplGeneric
instead?
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. I forgot to give this a proper name.
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.
Done.
lib/findtoken.cpp
Outdated
} | ||
|
||
namespace internal { | ||
template<> |
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.
This line can be removed.
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.
Done.
lib/findtoken.cpp
Outdated
return findTokensSkipDeadCodeImpl2(library, start, end, pred, found, evaluate, skipUnevaluated); | ||
} | ||
|
||
template<> |
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.
This line can be removed.
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.
Done.
lib/astutils.cpp
Outdated
const Token* operator()(const Token* start, const Token* end, F f) const | ||
{ | ||
return findToken(start, end, f); | ||
return findToken(start, end, std::move(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.
The predicate is taken as const ref, so this will cause an extra copy.
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.
Good catch. Leftover from some wrongly applied changes.
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 wonder why clang-tidy did not report this. Possibly because it is not yet a std::function
and still a function pointer. Something to look into.
Thanks. I think I addressed all of it. |
No description provided.