[LLVMdev] Does nounwind have semantics?

Duncan Sands baldrick at free.fr
Mon Jul 22 00:13:31 PDT 2013


Hi Eli,

On 22/07/13 03:37, Eli Friedman wrote:
> On Sun, Jul 21, 2013 at 5:56 PM, Andrew Trick <atrick at apple.com> wrote:
>> Does 'nounwind' have semantics that inform optimization passes? It seems to in some cases, but not consistently. For example...
>>
>> int32_t foo(int32_t* ptr) {
>>    int i = 0;
>>    int result;
>>    do {
>>      bar(ptr);
>>      result = *ptr;
>>      bar(ptr);
>>    } while (i++ < *ptr);
>>    return result;
>> }
>>
>> Say we have a front end that declares bar as...
>>
>> declare void @bar(i32*) readonly;
>>
>> So 'bar' is 'readonly' and 'may-unwind'.
>
> It's impossible to write a readonly function which throws a DWARF
> exception.  Given that, I would imagine there are all sorts of bugs
> nobody has ever run into.

very true.  That said, it is a pity not to support other ways of doing
exception handling.  For example, you could declare every function to return
two results, the usual one and an additional i1 result.  If the i1 value
returned is 1 then this means that "an exception is being unwound", and the
caller should jump to the landing pad if there is one; and if there isn't then
it itself should return 1.  This scheme doesn't write memory.  OK, now imagine
implementing this scheme where the additional return value is hidden, only
implicitly present.  You can argue that the function still doesn't write
memory, though I admit you could also argue that the only way we have to model
this extra parameter is to say that the function writes memory.  What is a bit
more problematic is that there is then also an implicit control flow construct
("if exception_value == 1 then return 1; end if") after every call.  If
everything was explicit then the above code
   bar(ptr);
   result = *ptr;
   bar(ptr);
would be
   exc = bar(ptr);
   if (exc) return 1;
   result = *ptr;
   exc = bar(ptr);
   if (exc) return 1;
At this point it is clear that because bar is readonly, the second bar call can
be dropped, giving
   exc = bar(ptr);
   if (exc) return 1;
   result = *ptr;
However it would have been wrong to drop the first bar call.  This is all
obvious when there are no hidden parameters and everything is explicit.  I
can't help feeling that either we should require everything to be explicit
like this, or if it is implicit then probably we should say that bar does
write memory (the invisible return parameter).  But even then the implicit
control flow after the bar call isn't modelled (already the case with the
usual exception handling) which is an endless source of little bugs, though
in practice as people don't raise exceptions much the bugs aren't noticed...

Ciao, Duncan.



More information about the llvm-dev mailing list