Daniel Berlin via llvm-dev
2017-Jan-03 19:52 UTC
[llvm-dev] RFC: Allow readnone and readonly functions to throw exceptions
On Tue, Jan 3, 2017 at 10:47 AM, Michael Kuperstein via llvm-dev < llvm-dev at lists.llvm.org> wrote:> > > On Tue, Jan 3, 2017 at 9:59 AM, Sanjoy Das via llvm-dev < > llvm-dev at lists.llvm.org> wrote: > >> Hi Michael, >> >> On Mon, Jan 2, 2017 at 11:49 PM, Michael Kuperstein >> <michael.kuperstein at gmail.com> wrote: >> > This sounds right to me. >> > >> > IIUC, historically, readonly and readnone are meant to model the "pure" >> and >> > "const" GCC attributes. These attributes make pretty strong guarantees: >> > >> > "[a pure] function can be subject to common subexpression elimination >> and >> > loop optimization just as an arithmetic operator would be. These >> functions >> > should be declared with the attribute pure [...] Interesting non-pure >> > functions are functions with infinite loops or those depending on >> volatile >> > memory or other system resource, that may change between two consecutive >> > calls (such as feof in a multithreading environment)." >> > >> > In particular, pure/const imply termination - something that's not >> entirely >> > clear w.r.t readonly. However, apparently, they don't imply nothrow. >> I've >> > actually always thought they *do* imply it - and said so on-list :-) - >> but >> > it looks like GCC itself doesn't interpret them that way. E.g. see John >> > Regher's example here: https://t.co/REzy5m1tT3 >> > So there's at least one use-case for possibly throwing >> readonly/readnone. >> >> One important thing to note then: clang marks const and pure functions >> as nounwind *explicitly*. That needs to be fixed. >> >> https://godbolt.org/g/SMF4C9 >> >> > Hah. So it does. > Eric, this was originally your change. Do I understand GCC's behavior > incorrectly? >No, but I was in the office when Kenny wrote ipa-pure-const, which is the equivalent to llvm's pass to find function attributions, and it mostly wasn't thought about. GCC isn't as consistent as one may think here. /* Non-looping const functions always return normally. Otherwise the call might not return or have side-effects that forbids hoisting possibly trapping expressions before it. */ int flags = gimple_call_flags (stmt); if (!(flags & ECF_CONST) || (flags & ECF_LOOPING_CONST_OR_PURE)) BB_MAY_NOTRETURN (block) = 1; } It also, for example, will do this: double cos (double) __attribute__ ((const)); double sin (double) __attribute__ ((const)); double f(double a) { double b; double c,d; double (*fp) (double) __attribute__ ((const)); /* Partially redundant call */ if (a < 2.0) { fp = sin; c = fp (a); } else { c = 1.0; fp = cos; } d = fp (a); return d + c; } into double cos (double) __attribute__ ((const)); double sin (double) __attribute__ ((const)); double f(double a) { double b; double c,d; double (*fp) (double) __attribute__ ((const)); /* Partially redundant call */ if (a < 2.0) { fp = sin; c = fp (a); } else { c = 1.0; fp = cos; temp = fp(a) } d = phi(c, temp) return d + c; } This only works if the second call to sin is guaranteed not to throw, no? In any case, optimizations check throwing separately from pure/const, but i'm not sure it's well thought out here. Note that GCC also has a notion of possibly infinite looping pure/const as well:) -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20170103/b14132a1/attachment.html>
Sanjoy Das via llvm-dev
2017-Jan-05 04:35 UTC
[llvm-dev] RFC: Allow readnone and readonly functions to throw exceptions
I just realized that there's an annoying corner case to this scheme -- I can't DSE stores across readnone maythrow function calls because the exception handler could read memory. That is, in: try { *a = 10; call void @readnone_mayunwind_fn(); *a = 20; } catch (...) { assert(*a == 10); } I can't DSE the `*a = 10` store. As far as I can tell, the most restrictive memory attribute for a potentially throwing function is readonly. "readnone may-unwind" does not make sense. "readonly may-unwind" is fine because even if the EH handler writes to memory, the code in the normal execution path does not have worry about the memory clobbers. I thought I had this figured out, but now it looks like I gotta think more. :) @Danny: I agree with your assessment of the example; unless the compiler knows that `cos` won't throw (which it may very well know since it is the standard library function, but I don't know GCC internals), the transform is wrong. -- Sanjoy On Tue, Jan 3, 2017 at 11:52 AM, Daniel Berlin <dberlin at dberlin.org> wrote:> > > On Tue, Jan 3, 2017 at 10:47 AM, Michael Kuperstein via llvm-dev > <llvm-dev at lists.llvm.org> wrote: >> >> >> >> On Tue, Jan 3, 2017 at 9:59 AM, Sanjoy Das via llvm-dev >> <llvm-dev at lists.llvm.org> wrote: >>> >>> Hi Michael, >>> >>> On Mon, Jan 2, 2017 at 11:49 PM, Michael Kuperstein >>> <michael.kuperstein at gmail.com> wrote: >>> > This sounds right to me. >>> > >>> > IIUC, historically, readonly and readnone are meant to model the "pure" >>> > and >>> > "const" GCC attributes. These attributes make pretty strong guarantees: >>> > >>> > "[a pure] function can be subject to common subexpression elimination >>> > and >>> > loop optimization just as an arithmetic operator would be. These >>> > functions >>> > should be declared with the attribute pure [...] Interesting non-pure >>> > functions are functions with infinite loops or those depending on >>> > volatile >>> > memory or other system resource, that may change between two >>> > consecutive >>> > calls (such as feof in a multithreading environment)." >>> > >>> > In particular, pure/const imply termination - something that's not >>> > entirely >>> > clear w.r.t readonly. However, apparently, they don't imply nothrow. >>> > I've >>> > actually always thought they *do* imply it - and said so on-list :-) - >>> > but >>> > it looks like GCC itself doesn't interpret them that way. E.g. see John >>> > Regher's example here: https://t.co/REzy5m1tT3 >>> > So there's at least one use-case for possibly throwing >>> > readonly/readnone. >>> >>> One important thing to note then: clang marks const and pure functions >>> as nounwind *explicitly*. That needs to be fixed. >>> >>> https://godbolt.org/g/SMF4C9 >>> >> >> Hah. So it does. >> Eric, this was originally your change. Do I understand GCC's behavior >> incorrectly? > > > No, but I was in the office when Kenny wrote ipa-pure-const, which is the > equivalent to llvm's pass to find function attributions, and it mostly > wasn't thought about. > > GCC isn't as consistent as one may think here. > > /* Non-looping const functions always return normally. > Otherwise the call might not return or have side-effects > that forbids hoisting possibly trapping expressions > before it. */ > int flags = gimple_call_flags (stmt); > if (!(flags & ECF_CONST) > || (flags & ECF_LOOPING_CONST_OR_PURE)) > BB_MAY_NOTRETURN (block) = 1; > } > > It also, for example, will do this: > double cos (double) __attribute__ ((const)); > double sin (double) __attribute__ ((const)); > double f(double a) > { > double b; > double c,d; > double (*fp) (double) __attribute__ ((const)); > /* Partially redundant call */ > if (a < 2.0) > { > fp = sin; > c = fp (a); > } > else > { > c = 1.0; > fp = cos; > } > d = fp (a); > return d + c; > } > > > into > double cos (double) __attribute__ ((const)); > double sin (double) __attribute__ ((const)); > double f(double a) > { > double b; > double c,d; > double (*fp) (double) __attribute__ ((const)); > /* Partially redundant call */ > if (a < 2.0) > { > fp = sin; > c = fp (a); > } > else > { > c = 1.0; > fp = cos; > temp = fp(a) > } > d = phi(c, temp) > return d + c; > } > > This only works if the second call to sin is guaranteed not to throw, no? > > In any case, optimizations check throwing separately from pure/const, but > i'm not sure it's well thought out here. > > Note that GCC also has a notion of possibly infinite looping pure/const as > well:) >
Daniel Berlin via llvm-dev
2017-Jan-05 05:33 UTC
[llvm-dev] RFC: Allow readnone and readonly functions to throw exceptions
On Wed, Jan 4, 2017 at 8:35 PM, Sanjoy Das <sanjoy at playingwithpointers.com> wrote:> I just realized that there's an annoying corner case to this scheme -- > I can't DSE stores across readnone maythrow function calls because the > exception handler could read memory. That is, in: > > try { > *a = 10; > call void @readnone_mayunwind_fn(); > *a = 20; > } catch (...) { > assert(*a == 10); > } > > I can't DSE the `*a = 10` store. > > As far as I can tell, the most restrictive memory attribute for a > potentially throwing function is readonly. "readnone may-unwind" does > not make sense. "readonly may-unwind" is fine because even if the EH > handler writes to memory, the code in the normal execution path does > not have worry about the memory clobbers. > > I thought I had this figured out, but now it looks like I gotta think > more. :) > > @Danny: I agree with your assessment of the example; unless the > compiler knows that `cos` won't throw (which it may very well know > since it is the standard library function, but I don't know GCC > internals), the transform is wrong. > > It does not know that, actually.I would say that GCC pretty much is a grab bag, since i wrote that for the gcc regression testsuite, and it's still tested :) There are like 3 optimization testcases that use nothrow, and john's example happens to just fall into this case: if (stmt_ends_bb_p (stmt) || gimple_has_volatile_ops (stmt) || gimple_has_side_effects (stmt) || stmt_could_throw_p (stmt)) return MOVE_IMPOSSIBLE; Some of the passes check throwing, some of them don't. -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20170104/84c06c28/attachment.html>
Hal Finkel via llvm-dev
2017-Jan-05 14:12 UTC
[llvm-dev] RFC: Allow readnone and readonly functions to throw exceptions
On 01/04/2017 10:35 PM, Sanjoy Das via llvm-dev wrote:> I just realized that there's an annoying corner case to this scheme -- > I can't DSE stores across readnone maythrow function calls because the > exception handler could read memory. That is, in: > > try { > *a = 10; > call void @readnone_mayunwind_fn(); > *a = 20; > } catch (...) { > assert(*a == 10); > } > > I can't DSE the `*a = 10` store. > > As far as I can tell, the most restrictive memory attribute for a > potentially throwing function is readonly. "readnone may-unwind" does > not make sense.Why not? I've not followed this thread in detail, but it seems like you're discussing allowing the modeling of EH schemes that don't access accessible memory. In that case, a may-unwind readnone function is just one that makes its decision about if/what to throw based only on its arguments. -Hal> "readonly may-unwind" is fine because even if the EH > handler writes to memory, the code in the normal execution path does > not have worry about the memory clobbers. > > I thought I had this figured out, but now it looks like I gotta think more. :) > > @Danny: I agree with your assessment of the example; unless the > compiler knows that `cos` won't throw (which it may very well know > since it is the standard library function, but I don't know GCC > internals), the transform is wrong. > > -- Sanjoy > > > On Tue, Jan 3, 2017 at 11:52 AM, Daniel Berlin <dberlin at dberlin.org> wrote: >> >> On Tue, Jan 3, 2017 at 10:47 AM, Michael Kuperstein via llvm-dev >> <llvm-dev at lists.llvm.org> wrote: >>> >>> >>> On Tue, Jan 3, 2017 at 9:59 AM, Sanjoy Das via llvm-dev >>> <llvm-dev at lists.llvm.org> wrote: >>>> Hi Michael, >>>> >>>> On Mon, Jan 2, 2017 at 11:49 PM, Michael Kuperstein >>>> <michael.kuperstein at gmail.com> wrote: >>>>> This sounds right to me. >>>>> >>>>> IIUC, historically, readonly and readnone are meant to model the "pure" >>>>> and >>>>> "const" GCC attributes. These attributes make pretty strong guarantees: >>>>> >>>>> "[a pure] function can be subject to common subexpression elimination >>>>> and >>>>> loop optimization just as an arithmetic operator would be. These >>>>> functions >>>>> should be declared with the attribute pure [...] Interesting non-pure >>>>> functions are functions with infinite loops or those depending on >>>>> volatile >>>>> memory or other system resource, that may change between two >>>>> consecutive >>>>> calls (such as feof in a multithreading environment)." >>>>> >>>>> In particular, pure/const imply termination - something that's not >>>>> entirely >>>>> clear w.r.t readonly. However, apparently, they don't imply nothrow. >>>>> I've >>>>> actually always thought they *do* imply it - and said so on-list :-) - >>>>> but >>>>> it looks like GCC itself doesn't interpret them that way. E.g. see John >>>>> Regher's example here: https://t.co/REzy5m1tT3 >>>>> So there's at least one use-case for possibly throwing >>>>> readonly/readnone. >>>> One important thing to note then: clang marks const and pure functions >>>> as nounwind *explicitly*. That needs to be fixed. >>>> >>>> https://godbolt.org/g/SMF4C9 >>>> >>> Hah. So it does. >>> Eric, this was originally your change. Do I understand GCC's behavior >>> incorrectly? >> >> No, but I was in the office when Kenny wrote ipa-pure-const, which is the >> equivalent to llvm's pass to find function attributions, and it mostly >> wasn't thought about. >> >> GCC isn't as consistent as one may think here. >> >> /* Non-looping const functions always return normally. >> Otherwise the call might not return or have side-effects >> that forbids hoisting possibly trapping expressions >> before it. */ >> int flags = gimple_call_flags (stmt); >> if (!(flags & ECF_CONST) >> || (flags & ECF_LOOPING_CONST_OR_PURE)) >> BB_MAY_NOTRETURN (block) = 1; >> } >> >> It also, for example, will do this: >> double cos (double) __attribute__ ((const)); >> double sin (double) __attribute__ ((const)); >> double f(double a) >> { >> double b; >> double c,d; >> double (*fp) (double) __attribute__ ((const)); >> /* Partially redundant call */ >> if (a < 2.0) >> { >> fp = sin; >> c = fp (a); >> } >> else >> { >> c = 1.0; >> fp = cos; >> } >> d = fp (a); >> return d + c; >> } >> >> >> into >> double cos (double) __attribute__ ((const)); >> double sin (double) __attribute__ ((const)); >> double f(double a) >> { >> double b; >> double c,d; >> double (*fp) (double) __attribute__ ((const)); >> /* Partially redundant call */ >> if (a < 2.0) >> { >> fp = sin; >> c = fp (a); >> } >> else >> { >> c = 1.0; >> fp = cos; >> temp = fp(a) >> } >> d = phi(c, temp) >> return d + c; >> } >> >> This only works if the second call to sin is guaranteed not to throw, no? >> >> In any case, optimizations check throwing separately from pure/const, but >> i'm not sure it's well thought out here. >> >> Note that GCC also has a notion of possibly infinite looping pure/const as >> well:) >> > _______________________________________________ > LLVM Developers mailing list > llvm-dev at lists.llvm.org > http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev-- Hal Finkel Lead, Compiler Technology and Programming Languages Leadership Computing Facility Argonne National Laboratory