This check emits a warning when memcpy or memset is used and suggest replacing it with a call to std::copy and std::fill respectively.
For the motivation see modernize-use-algorithm.rst
Taken from: https://llvm.org/bugs/show_bug.cgi?id=22209
Differential D22725
[clang-tidy] Add check 'modernize-use-algorithm' JDevlieghere on Jul 23 2016, 2:12 AM. Authored by
Details
This check emits a warning when memcpy or memset is used and suggest replacing it with a call to std::copy and std::fill respectively. For the motivation see modernize-use-algorithm.rst Taken from: https://llvm.org/bugs/show_bug.cgi?id=22209
Diff Detail
Event TimelineComment Actions Please mention this check in docs/ReleaseNotes.rst (in alphabetical order). May be modernize will be better place then misc? Comment Actions
To my knowledge, those type-aware optimizations are for transforming copies involving trivially-copyable types into calls to memcpy(). What other optimizations does this check enable? Comment Actions I might be mistaken, but I understood that a call to std::copy (whether or not specialized for certain types) might get inlined, while a call to memcpy is typically not. Comment Actions Thanks for the contribution. Is it your first check? Some main issues:
'modernize-replace-memcpy' also works, maybe it is even better. Your choice.
Look at DeprecatedHeadersCheck.cpp or PassByValueCheck ( or grep for registerPPCallbacks)
Comment Actions The optimizer can be quite smart about memcpy(). For instance: https://godbolt.org/g/INfHWE If we know of specific cases where std::memcpy() is used, but std::copy() will provide better performance metrics, that would be really interesting to know. Comment Actions So if it is something trivially copyable (just copying bytes) then std::copy would probably call memcpy inside - so it won't be optimization in our case (even if it will get inlined). But it will definitelly make code cleaner and won't be slower, which would be good to mention in docs. Comment Actions
Comment Actions Yes, it is! :-)
Agreed, I moved and renamed the check.
Done
I have included an example in the documentation with the ternary conditional operator. I agree that it's not ideal, but I tried to be defensive. Basically anything with lower precedence than the + operator might cause an issue, and this is quite a lot...
Doing this as we speak!
Comment Actions Cool! hope to see some other cool checks. You could probably use pretty much the same code, and make modernize-use-fill for replacing memset and Maybe the right way would be to have check called 'modernize-use-sequence-algorithm' or just 'modernize-use-algorithm' that would basically do all those stuff. It would be good to not introduce 5 new check that pretty much do the same thing and also the user would want to have them all or none.
So It should not be very hard. You would just have to check if the second or third argument to memcpy is one of [list here] of the things that Anyway good job, hope to see some more checks from you!
When you will be finished just post a diff of changes after running it on llvm on phabricator and add me as subscriber :)
Comment Actions
Comment Actions I kept everything in modernize-use-algorithm as I agree this isn't worth 3 different checks.
You are right. I didn't completely finish the first run on LLVM, but none of the occurrences it had found so far would have caused an issue. I guess that means that omitting the extra parens is a sensible default for now. I will look into excluding problematic situations once I am sure this check is fully functional.
Thanks, I really appreciate the feedback from everyone! :)
It's running again with the changes from the last revision included. It is taking a very long time though but I guess that's my fault for not building in release mode. I'll leave it running for now as I don't want to start over again. Comment Actions hmm It seems that I mislead you, I suck at C api - memmove source and destination can overlap, but std::move can't. So I guess you have to remove the memmove support. Sorry. Comment Actions No problem, I wasn't aware of the issue either, so thanks for letting me know! While running my check on the LLVM repo I ran into another issue: can't do pointer arithmetic when the arguments to memcpy are void pointers. I will look into updating my matchers to exclude this particular case. Comment Actions Yep, developing clang-tidy checks is mostly about trying to find out all the cases, and then finding that there are 3x more corner cases after running it on LLVM :)
Comment Actions It is possible that the call to move with some trivialy copyable object would end up calling memmove, even if the call is wrong. quote from http://en.cppreference.com/w/cpp/algorithm/move
But this is only the special ability of memmove, so we should not introduce stl-implementation dependent bugs Comment Actions Thanks for the reviews everyone! I'm currently still stuck on an issue I discovered when processing LLVM. I asked for help on the mailing list [0] but maybe someone here can help. Basically the issue is that sometimes pointers are passed to memcpy for which the types are not assignable. That's okay for memcpy because it takes its arguments as void pointers, but of course this causes std::copy to complain. I want my pass to warn the user about this issue but skip the substitution, but I don't know how to check for this case... [0] http://lists.llvm.org/pipermail/cfe-dev/2016-July/050105.html Comment Actions
Comment Actions I see you solved the void and conmpatible types problems. Excellent! Can you post a patch with changes after running LLVM? I would wait for Alex or Aaron to accept it.
Comment Actions Addressed comments from Piotr Padlewski LLVM compiles with the latest version of this check, however some tests are failing. I'll investigate the cause of this and update this check if it can be prevented.
Comment Actions Addresses comments from Aaron Ballman @aaron.ballman Thanks for the thorough review! Can you check whether the tests I added address your concerns? Could you also elaborate on the case with the C-function pointer? Unless I explicitly cast it to void* the compiler rejects will reject it as an argument to memcpy. Am I missing a case where this could go wrong? I still added it to the pointer arithmetic check though, just to be sure. Comment Actions Did you manage to see what was wrong in the transformation that you did on LLVM? I have one idea, which should be fixed. Memset takes char as an argument, so if you would fill the array of ints with memset(arr, 0, sizeof(arr)), then it will set all int val = 1 | (1 << 8) | (1 << 16) | (1 << 24) for each elemnt of the array. I don't think tho that any LLVM code depend od this, you usually set everything to zero, or to another value if you operate on char type. So allow transformation and diagnostic if the argument equals 0, or when the type is char. Comment Actions The tests added mostly cover them -- I elaborated on the function pointer case, which I don't *think* will go wrong with this check, but should have a pathological test case for just to be sure.
Comment Actions
I extended the matchers to include ::memcpy and ::memset as well because the check otherwise does not work on my mac because the cstring header that ships with Xcode is implemented as shown below. _LIBCPP_BEGIN_NAMESPACE_STD using ::size_t; using ::memcpy; using ::memset; ... _LIBCPP_END_NAMESPACE_STD This is annoying as I have no way to discriminate functions that have the same signature. Unless there's a better solution, this seems like a reasonable trade-off to me. Of course I'm open to suggestions! I added the test case. The call is indeed replaced, which I guess is fine? Not yet, thought it seemed to be related to std::copy rather than std::fill. I'm still trying to pinpoint the culprit but it's extremely time consuming as I have to recompile LLVM completely in order to run the tests... Comment Actions You should add a similar approach as a test case to ensure we're covering this usage.
Given that you also have to handle the case where C++ code includes string.h (rather than cstring) to call memcpy, I think this is the only approach you can really take. Either you are getting a global declaration or one in the std namespace, and both should be handled when the signatures are correct.
Yes, that's the behavior I would expect.
Comment Actions Removed anonymous namespaces in test file. I was playing around with it but forgot to remove it before making my last diff.
Comment Actions Still working on comment #2 from Alex but wanted to update my diff since it's been a while and I haven't gotten around to looking into it further. So no need to review yet. Comment Actions I'm abandoning this revision because I think this check is getting overly complex. There's still the problem of supporting arguments that can have side effects, and then there's also the unaddressed issue of code possibly using the void* return type of these two functions. Any solution to this would require rather inelegant and complex code that is, in my opinion, simply not warranted by the added value of this check. |
Entries should be sorted alphabetically.