-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Add use of Clang __attribute__((nonnull)) in Emscripten system headers. #16545
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
7c3eeff to
a6feeea
Compare
|
Is it worth using a macro here in case these headers ever need to be used on a non-clang compiler? (could be it shorter and less visually noisy too). |
|
Makes me wonder if we also want to use |
|
I did actually first do this with a int emscripten_futex_wait(volatile void *addr EMSCRIPTEN_NONNULL, uint32_t val, double maxWaitMilliseconds);vs int emscripten_futex_wait(volatile void *addr __attribute__((nonnull)), uint32_t val, double maxWaitMilliseconds);I found that in IDE (and looks like also on GitHub), using EMSCRIPTEN_NONNULL loses syntax highlighting, so visually it did parse off worse to my eye than directly using Also an indirection can cause developers to need to look up what the macro means, if they don't guess its meaning (or doubt themselves). Finally, our system headers aren't particularly include clean anymore, so I wish not to add to the include chain dependency. Having almost every include file include one more header adds to that load. (not sure how much that matters for compilation unit perf though nowadays.. in the past ~decade+ ago it used to be noticeable, but I might be a dinosaur on this)
I wonder if that should be a use case even. In a distant future if that becomes a thing, we can look into it then? |
My understanding is that restrict enables more optimizations on the implementation side of the function, not outside the function on the caller side (expect than to warn if one does attempt to pass aliasing pointers to the function). Most of these functions are implemented in JS side, so there wouldn't be any optimizations that LLVM would be able to perform there. Skimming all of these declarations, I was not able to find any that would receive two or more pointers of the same type (pointers to different types are already always assumed to not alias by the compiler). |
|
I learn that there are two different types of nonnull annotations in Clang: The first tells the compiler that it can optimize based on the assumption that the pointer will never be null. The second annotation does not affect optimizations, but simply emits diagnostics if a static null is passed. Adjusted the code to use |
That argument makes some sense to me.
There are some cases where include-bloat is an issue, but for things like this example, we already have split up our includes nad have a very-tiny header called
It used to be the case the the emscripten headers we designed to also be running able through gcc and/or msvc but I don't think I've actually don't that in years, so maybe its fine to make them clang-only? (I guess both these new attributes are gcc-compatible though?) |
| uint16_t emscripten_atomic_exchange_u16(void/*uint16_t*/ *addr, uint16_t newVal); | ||
| uint32_t emscripten_atomic_exchange_u32(void/*uint32_t*/ *addr, uint32_t newVal); | ||
| uint64_t emscripten_atomic_exchange_u64(void/*uint64_t*/ *addr, uint64_t newVal); | ||
| uint8_t emscripten_atomic_exchange_u8(void/*uint8_t*/ *addr __attribute__((nonnull)), uint8_t newVal); |
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.
Wouldn't it also be nice to have the compiler warning if a user passes NULL 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.
The compiler will warn if a user passes a null here - that is what __attribute__((nonnull)) will do. But in addition to issuing the warning, the compiler can optimize code inside the function, and at the calls to that function with the expectation that any non-statically-inferrable value passed here will not be a null.
So i.e. if one had silly code like
void foo(const char *x __attribute__((nonnull)))
{
printf("%s\n", x);
}
extern const char *bar(void);
int main()
{
const char *s = bar();
foo(s);
if (!s) // This can be assumed never to be true, since the contract with foo says it can only be called with nonnull pointers. Clang could issue a warning here.
{
// we cannot get here!
printf("s was null!\n");
}
}However now I see that Clang does not actually do this kind of analysis when trying out in practice. :/
To answer the difference between __attribute__((nonnull)) and _Nonnull: if instead the function foo read
void foo(const char *x __attribute__((nonnull)))
{
if (x)
printf("%s\n", x);
}the compiler will issue a diagnostic warning, because if (x) is tautologically true: the contract with x says it will never be null.
So if we have a function that does want to do something different when x actually is null, then we want to just use the weaker _Nonnull semantics:
void foo(const char * _Nonnull x) // issue a warning if x is null, but don't affect codegen
{
if (x)
printf("%s\n", x);
}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 see, from your earlier description had though that one triggered optimization and the triggers warnings, but its seem that the both trigger warning and in addition the the __attribute__ form allows optimization.
This makes sense to me now.
| if (!plce.isActive) { | ||
| printf("Requesting pointer lock..\n"); | ||
| ret = emscripten_request_pointerlock(0, 1); | ||
| ret = emscripten_request_pointerlock("#canvas", 1); |
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.
What was the effect of passing zero here previously?
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.
Under the old semantics 0 meant to use #canvas, but that support was deprecated.
sbc100
left a comment
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 % formatting
|
Updated the PR for formatting. |
sbc100
left a comment
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.
Still lgtm.
In practice, did you notice any new warnings showing up in real world code? i.e. did this reveal any latent bugs in real code?
| @node_pthreads | ||
| def test_emscripten_futexes(self): | ||
| self.set_setting('USE_PTHREADS') | ||
| self.emcc_args += ['-Wno-nonnull'] |
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.
Can we add a comment as to why this warning is being disabled? Is it strictly needed for the test?
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, the test is explicitly checking that passing null returns in EINVAL error, which would trigger a diagnostic in the test.
I recall I started this PR after I saw a bug in Unity's code that would have been caught if these annotations had existed. I do not know this PR revealing any new bugs in Emscripten's codebase itself. |
…s. (emscripten-core#16545) * Add use of Clang __attribute__((nonnull)) in Emscripten system headers. * Fix annotation on emscripten_futex_wake * Remove nonnull on emscripten_fetch_wait * Fix annotation and tests * Add use of _Nonnull attribute * Fix test * Break up long lines in webgl2_ext.h * Add notes about null pointers in emmalloc.h
…s. (emscripten-core#16545) * Add use of Clang __attribute__((nonnull)) in Emscripten system headers. * Fix annotation on emscripten_futex_wake * Remove nonnull on emscripten_fetch_wait * Fix annotation and tests * Add use of _Nonnull attribute * Fix test * Break up long lines in webgl2_ext.h * Add notes about null pointers in emmalloc.h
This allows users to get a compile time warning if they try to pass a statically null pointer value to a system header that is not intended to accept such a value.