[llvm-dev] RFC: Allow readnone and readonly functions to throw exceptions

Daniel Berlin via llvm-dev llvm-dev at lists.llvm.org
Tue Jan 3 11:52:56 PST 2017


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>


More information about the llvm-dev mailing list