Mehdi Amini via llvm-dev
2017-Feb-09 01:45 UTC
[llvm-dev] help me understand how nounwind attribute on functions works?
> On Feb 8, 2017, at 12:58 PM, Reid Kleckner via llvm-dev <llvm-dev at lists.llvm.org> wrote: > > I think this behavior is intended to allow better LTO between C and C++. Consider this kind of code: > > // foo.h > extern "C" int doThing(bool canThrow); > // foo.cpp > int doThing(bool canThrow) { > ... > if (hadError) { > if (canThrow) throw MyException; else return -1; > } > } > // bar.c > #include "foo.h" > void f() { > doThing(false); // don't declare doThing as nounwind > } > // baz.cpp > ... > doThing(true); > > Basically, compiling a C declaration for a function is not an assertion that it never throws an exception. However, *defining* a function in a C TU implies that it will not throw an exception.What isn’t clear to me still is : why shouldn't this be transitive? In the example you’re showing, for a caller of f() in bar, what is the advantage of knowing that f() is nounwind if it an exception can still be thrown? What does it allow? — Mehdi> > On Sun, Feb 5, 2017 at 8:32 AM, Andrew Kelley via llvm-dev <llvm-dev at lists.llvm.org <mailto:llvm-dev at lists.llvm.org>> wrote: > from http://llvm.org/docs/LangRef.html <http://llvm.org/docs/LangRef.html>: > > nounwind > This function attribute indicates that the function never raises an exception. If the function does raise an exception, its runtime behavior is undefined. However, functions marked nounwind may still trap or generate asynchronous exceptions. Exception handling schemes that are recognized by LLVM to handle asynchronous exceptions, such as SEH, will still provide their implementation defined semantics. > > Some things I noticed by looking at various C test programs with clang -S -emit-llvm: > > * If a function definition is provided, it gets nounwind, even if it calls a non-nounwind function > * If a function is external (no definition provided), it does not get nounwind > > What I don't understand is: > * A non-nounwind function that calls a nounwind function, wouldn't the ability for an exception to be thrown transfer? I thought if you don't catch an exception, it bubbles up to the caller and so on. > * More importantly, if compiling in C mode, why doesn't every function have nounwind? I thought that C does not have exceptions, so calling a function and having it throw an exception would be undefined behavior anyway. Wouldn't that mean every function should be nounwind? > > Regards, > Andrew > > _______________________________________________ > LLVM Developers mailing list > llvm-dev at lists.llvm.org <mailto:llvm-dev at lists.llvm.org> > http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev <http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev> > > > _______________________________________________ > LLVM Developers mailing list > llvm-dev at lists.llvm.org > http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev-------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20170208/d36ea63e/attachment.html>
Reid Kleckner via llvm-dev
2017-Feb-09 16:41 UTC
[llvm-dev] help me understand how nounwind attribute on functions works?
On Wed, Feb 8, 2017 at 5:45 PM, Mehdi Amini <mehdi.amini at apple.com> wrote:> > What isn’t clear to me still is : why shouldn't this be transitive? > In the example you’re showing, for a caller of f() in bar, what is the > advantage of knowing that f() is nounwind if it an exception can still be > thrown? What does it allow? >We know an exception cannot unwind out of f. An exception can be thrown inside something that f calls, but it must be caught before it unwinds beyond f. -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20170209/03268144/attachment.html>
David Chisnall via llvm-dev
2017-Feb-09 17:12 UTC
[llvm-dev] help me understand how nounwind attribute on functions works?
On 9 Feb 2017, at 08:41, Reid Kleckner via llvm-dev <llvm-dev at lists.llvm.org> wrote:> > On Wed, Feb 8, 2017 at 5:45 PM, Mehdi Amini <mehdi.amini at apple.com> wrote: > What isn’t clear to me still is : why shouldn't this be transitive? > In the example you’re showing, for a caller of f() in bar, what is the advantage of knowing that f() is nounwind if it an exception can still be thrown? What does it allow? > > We know an exception cannot unwind out of f. An exception can be thrown inside something that f calls, but it must be caught before it unwinds beyond f.Why? With the x86-64 ABI, for example, f is required to emit async unwind tables. It has a personality function that knows how to handle cleanups, but there are none here, so the generic unwinder will happily unwind through the code. The only ways to prevent exceptions from being propagated are to explicitly catch them, or to use something like a C++ exception specifier so that the personality function will explicitly block exception propagation. In this example, given that doThing is not marked as noexcept, we will emit unwind tables that explicitly allow unwinding through f; however, when the unwinder tries to unwind into the next stack frame it will find that there is no unwind record. Depending on how lazy we are in defining the PC ranges in the unwind table, this will either cause stack corruption or a run-time abort from well-formed code. David
Sean Silva via llvm-dev
2017-Feb-09 21:00 UTC
[llvm-dev] help me understand how nounwind attribute on functions works?
On Feb 8, 2017 5:46 PM, "Mehdi Amini via llvm-dev" <llvm-dev at lists.llvm.org> wrote: On Feb 8, 2017, at 12:58 PM, Reid Kleckner via llvm-dev < llvm-dev at lists.llvm.org> wrote: I think this behavior is intended to allow better LTO between C and C++. Consider this kind of code: // foo.h extern "C" int doThing(bool canThrow); // foo.cpp int doThing(bool canThrow) { ... if (hadError) { if (canThrow) throw MyException; else return -1; } } // bar.c #include "foo.h" void f() { doThing(false); // don't declare doThing as nounwind } // baz.cpp ... doThing(true); Basically, compiling a C declaration for a function is not an assertion that it never throws an exception. However, *defining* a function in a C TU implies that it will not throw an exception. What isn’t clear to me still is : why shouldn't this be transitive? In the example you’re showing, for a caller of f() in bar, what is the advantage of knowing that f() is nounwind if it an exception can still be thrown? What does it allow? Given the semantics, aren't we allowed to infer it transitively? I.e. if a callee of a nounwind function does dynamically unwind through the nounwind caller, then it is UB. -- Sean Silva — Mehdi On Sun, Feb 5, 2017 at 8:32 AM, Andrew Kelley via llvm-dev < llvm-dev at lists.llvm.org> wrote:> from http://llvm.org/docs/LangRef.html: > > nounwind >> This function attribute indicates that the function never raises an >> exception. If the function does raise an exception, its runtime behavior is >> undefined. However, functions marked nounwind may still trap or generate >> asynchronous exceptions. Exception handling schemes that are recognized by >> LLVM to handle asynchronous exceptions, such as SEH, will still provide >> their implementation defined semantics. > > > Some things I noticed by looking at various C test programs with clang -S > -emit-llvm: > > * If a function definition is provided, it gets nounwind, even if it > calls a non-nounwind function > * If a function is external (no definition provided), it does not get > nounwind > > What I don't understand is: > * A non-nounwind function that calls a nounwind function, wouldn't the > ability for an exception to be thrown transfer? I thought if you don't > catch an exception, it bubbles up to the caller and so on. > * More importantly, if compiling in C mode, why doesn't every function > have nounwind? I thought that C does not have exceptions, so calling a > function and having it throw an exception would be undefined behavior > anyway. Wouldn't that mean every function should be nounwind? > > Regards, > Andrew > > _______________________________________________ > LLVM Developers mailing list > llvm-dev at lists.llvm.org > http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev > >_______________________________________________ LLVM Developers mailing list llvm-dev at lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev _______________________________________________ LLVM Developers mailing list llvm-dev at lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20170209/a6cd78d1/attachment.html>
Sanjoy Das via llvm-dev
2017-Feb-10 02:16 UTC
[llvm-dev] help me understand how nounwind attribute on functions works?
On Thu, Feb 9, 2017 at 8:41 AM, Reid Kleckner via llvm-dev <llvm-dev at lists.llvm.org> wrote:> On Wed, Feb 8, 2017 at 5:45 PM, Mehdi Amini <mehdi.amini at apple.com> wrote: >> >> What isn’t clear to me still is : why shouldn't this be transitive? >> In the example you’re showing, for a caller of f() in bar, what is the >> advantage of knowing that f() is nounwind if it an exception can still be >> thrown? What does it allow? > > > We know an exception cannot unwind out of f. An exception can be thrown > inside something that f calls, but it must be caught before it unwinds > beyond f.So perhaps a viable rule is that every CallInst in a nounwind function can be marked as nounwind (even though the callee for said CallInst can't be)? -- Sanjoy