-
Notifications
You must be signed in to change notification settings - Fork 2.6k
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
Fixed macro redefinition warnings when compiling with clang-cl. #256
Conversation
Both clang-cl and Clang/C2 #define _MSC_VER but also have support for __builtin_clz and __builtin_clzll, leading to duplicate macro definition warnings. This change suppresses emulation of clz using _BitScanReverse if the __clang__ macro is defined.
It seemed to "heavy" to mention it in the commit, but clang-cl refers to this http://www.llvm.org/builds/, and clang/c2 refers to this: http://blogs.msdn.com/b/vcblog/archive/2015/12/04/introducing-clang-with-microsoft-codegen-in-vs-2015-update-1.aspx. It's possibly also reasonable to add undef's of |
Great catch, thanks!
Maybe check #if defined(_MSC_VER) && !defined(FMT_BUILTIN_CLZ) instead of #if defined(_MSC_VER) && !defined(__clang__) in case some other compiler decides to do the same (however unlikely it is =)). What do you think? Also it would be nice to have a comment why this is necessary, because it's not obvious that other compilers can define |
Both sound good, I'll get to that soon. There's another "issue" with the emulation, and that's that _BitScanReverse is annotated as only returning success if the return value is non-zero, so VC++'s static analyzer complains about "r" being uninitialized since there's no check for success. The only way that could happen is if 0 is passed in to the clz functions. Since cppformat is careful to never pass in zero I was going to just assert that 'x' isn't zero and suppress the warning, likely with just a pragma. Would you like that to be a separate PR? Do you have any stylistic concerns about it? So clz would switch from this: inline uint32_t clz(uint32_t x) {
unsigned long r = 0;
_BitScanReverse(&r, x);
return 31 - r;
} to this: inline uint32_t clz(uint32_t x) {
unsigned long r = 0;
_BitScanReverse(&r, x);
assert(x != 0);
#pragma warning(suppress: 6102)
return 31 - r;
} |
Same PR is OK, but from https://msdn.microsoft.com/en-us/library/fbxyd7zd.aspx I don't see how |
This ended up being more words than you wanted, sorry. The lines may be off from current code, but:
Line 111 is in clzll's definition. The winnt.h header (but not in intrin.h, so this depends on which files are included before format.h), defines the function prototype as: _Success_(return!=0)
BOOLEAN
_BitScanReverse (
_Out_ DWORD *Index,
_In_ DWORD Mask
); Which tells the static analyzer "*Index is valid only if the return value is non-zero". Since there's no check of the result from _BitScanReverse or _BitScanReverse64, the static analyzer assumes that the function failed and then warns that there is an undefined value usage. From those docs for _BitScanReverse, A similar workaround is seen here: inline uint32_t clz(uint32_t x)
{
unsigned long r = 0;
if (_BitScanReverse(&r, x))
{
return 31 - r;
}
return 32;
} but the codegen retains the branch, even with known values, so the assert that undefined behavior cannot happen and suppress seems like the best for the resulting code even if it's slightly uglier in the source code. |
Thanks for a detailed explanation. My only concern regarding the #pragma warning(push)
...
#pragma warning(pop) but it's a bit messy. Since this warning is triggered by a SAL annotation, can it be fixed using an annotation too, e.g. inline uint32_t clz(_In_range_(1, UINT32_MAX) uint32_t x) or something like that? |
If not, then |
I'm uncertain, in doing some research for that last comment, I came across this bug report, which tries to come up with several ways of annotating the function correctly, but since I'll work on trying to annotate clz and clzll to generate a warning if there's ever a call that is not known to be non-zero but my attempts so far haven't been fruitful. #pragma warning suppress is a shorthand for the push / disable / pop syntax, but only for the line that follows the suppress, so it shouldn't be an issue, unless you feel the push / pop is clearer. From here: https://msdn.microsoft.com/en-us/library/2c8f766e.aspx "Pushes the current state of the pragma on the stack, disables the specified warning for the next line, and then pops the warning stack so that the pragma state is reset." |
Didn't know that. Then let's go with the pragma and don't worry about annotations. |
Both clang-cl and Clang/C2 #define _MSC_VER but also have support for __builtin_clz and __builtin_clzll, leading to duplicate macro definition warnings. Emulation of clz using _BitScanReverse is suppressed if the builtins are already available. Additionally, the value of the output parameter of _BitScanReverse is undefined if the input value is 0, which is avoided by construction, so the code analysis warning for using uninitialized data is now suppressed.
Fixed macro redefinition warnings when compiling with clang-cl.
Merged, thanks! |
I missed one other problem. I don't know how it happened, I thought I committed the fix, but obviously didn't. The issue is this: I'll get another PR soon, sorry about that. It is a bit more conditional code, though. |
No problems, thanks for working on this. |
Both clang-cl and Clang/C2 #define
_MSC_VER
but also have support for__builtin_clz
and__builtin_clzll
, leading to duplicate macro definition warnings. This change suppresses emulation of clz using_BitScanReverse
if the__clang__
macro is defined.