[LLVMdev] Unwind behaviour in Clang/LLVM

1,980 views
Skip to first unread message

Renato Golin

unread,
Feb 6, 2014, 8:29:43 AM2/6/14
to Anton Korobeynikov, Eric Christopher, Keith Walker, Jim Grosbach, Clang Dev, LLVM Dev
Folks,

We're having some discussions about the behaviour of exception handling and Dwarf sharing unwind logic, tables, etc. and it seems that the code around it wasn't designed with any particular goal in mind, but evolved (like the EHABI) and now we're seeing the results from it. 

The problems below are assuming C vs. C++, but it actually apply to any possibly-exceptional vs. never-exceptional cases.


1. C vs. C++

We have two unwind flags: nounwind, which flags functions that can't unwind (leaf, nothrow, etc) and uwtable, which forces generation of the table regardless of nounwind. It seems sensible that C++ code with exceptions enabled should generate the tables for all functions, in case they're called by (or call) external functions. In C we don't want any of that.

GCC seems to never emit tables, and G++ always do, even on C code (.c files, no exception or anything), which is very sensible and in line with my reasoning above. Clang, on the other hand, always generates them. I guess it'll have to figure out what to do based on its impressions on what language is being used to produce similar results.

I believe that emitting the tables on anything that could potentially interact with exceptional code makes sense, but that's clearly a front-end decision. To LLVM, nounwind and uwtables should be absolute:

IF (uwtables)
  IF (nownwind)
    CantUnwind
  ELSE
    Unwind Table
ELSE
  do nothing
ENDIF


2. .fnstart/.fnend+friends

Another problem is that the emission of unwinding tables (based on .fnstart/.fnend symbols and others) is conditional *only* to the existence (or not) of an exception handling class being loaded (EHABI, Dwarf). Which means that, we can't disable the EH on a per-function basis.

We'll have to change the way these symbols are emitted, at least when using ARMException, so that we can emit the tables and honour the uwtable on a per-function basis.

Again, this is a requirement for problem 1, but it'd need to be fixed after 3.


3. Unwinding code

Currently, even when no exception handling are needed, the exception code is used to generate Dwarf unwinding directives (CFI) for the debugger.

Both DwarfCFIException and ARMException inherit from DwarfException, and they are called to do the debug info about the stack unwinding, which is (at least) misplaced. The consensus is that this code should be factored out.

The part that is relevant to this thread is that, today, if -arm-disable-ehabi is requested, ARMException will not be used and we won't have a way of generating debug stack directives, which is wrong.

Factoring out this code is a requirement for the unwinding problem (1), since if we disable EH today, we'll disable Dwarf stack unwinding altogether. But we also need a final solution for problem 4 below before we start.


4. Clang EH control

There are a number of Clang/LLVM options to control exception handling:
 * -fno-excetpion (enable/disable EH on C++ mode, off in C mode)
 * -fcxx-exception (no idea, is it objC++ specific? does it control tables in any way?)
 * -funwind-tables (forces uwtable attribute?)
 * -arm-disable-ehabi (ARM specific bogus flag)

Those options are not always completely exclusive, and they damage different parts of the compilation process (as seen recently on the list), so we need a clear consensus on what each option mean (or should mean), and translate it to the back-end (via function attributes). This would considerably simplify the back-end and help us tackle this refactoring.

My main target for this problem is to have the one true option on the front-ends, and rely only on function attributes on the back-end (including tools like llc). For that, I'd love to be left with -fexception only and infer all the rest from the language / conditions of the code.

Ex:

IF (C++ mode)
  IF (-fno-exception)
    no uwtable
    no nounwind
  ELSE
    uwtable
    IF (leaf / nothrow)
      nounwind
    ENDIF
  ENDIF
ELSE
  no uwtable
  no nounwind
ENDIF

My main goal is to get rid of (at least) -arm-disable-ehabi.



Finally,

If you got this far, you're *really* interested in making the exception handling more user/dev friendly in LLVM, so I welcome any critics about the "proposal" above, as well as any explanations of the doubts expressed. I may be wrong about many things, feel free to enlighten me! We may have a lot less work to do, and I'll be happy. ;)

I believe the dependency graph of the solutions are:

4 -> 3 -> 2 -> 1

So, we first should solve the flags problem, than refactor the unwinding code out of EH, make the debug generation use them as well, and then we can start with the EH specific changes. The problem is that 4 will change things considerably, so we might need some scaffolding during the whole process.

Makes sense?

cheers,
--renato

Joerg Sonnenberger

unread,
Feb 6, 2014, 8:59:40 AM2/6/14
to cfe...@cs.uiuc.edu, LLVM Dev
On Thu, Feb 06, 2014 at 01:29:43PM +0000, Renato Golin wrote:
> We have two unwind flags: nounwind, which flags functions that can't unwind
> (leaf, nothrow, etc) and uwtable, which forces generation of the table
> regardless of nounwind. It seems sensible that C++ code with exceptions
> enabled should generate the tables for all functions, in case they're
> called by (or call) external functions. In C we don't want any of that.

This is not true. Even for nounwind, you want to get basic tables so
that backtrace(3) works.

Joerg
_______________________________________________
LLVM Developers mailing list
LLV...@cs.uiuc.edu http://llvm.cs.uiuc.edu
http://lists.cs.uiuc.edu/mailman/listinfo/llvmdev

Renato Golin

unread,
Feb 6, 2014, 9:30:11 AM2/6/14
to Clang Dev, LLVM Dev
On 6 February 2014 13:59, Joerg Sonnenberger <jo...@britannica.bec.de> wrote:
This is not true. Even for nounwind, you want to get basic tables so
that backtrace(3) works.

Hi Joerg,

It's a matter of consensus, I believe. Is it the general consensus that we will *always* want unwind tables to exist? Code size is a clear reason to not want unwind tables at all, but there might not be many more. If the general consensus is that unwind tables are a must, and should only be turned off in special cases, then we just keep emitting them and create (or reuse) a flag to stop it. If not, language / flags decision (-fexception, -g, profiling, etc) should turn them on. However, if every one agrees that, no matter what, we *will* emit unwind tables, than the whole argument is moot, and there is absolutely nothing to "fix" besides removing -arm-disable-ehabi.

cheers,
--renato

Joerg Sonnenberger

unread,
Feb 6, 2014, 9:51:52 AM2/6/14
to llv...@cs.uiuc.edu, Clang Dev
My point is that there are two different questions here:

(1) Should enough unwind data be provided for a basic, read-only stack
walk?

(2) Should landing pads etc. be provided for exception handling.

The second implies the first, but not the other way around. Some
platforms require (1), e.g. AMD64. Debugging in any form is certainly a
lot more difficult without. Binary size (not so much code size itself)
increases a bit, but typically in the 1-2 per cent range. For NetBSD we
are moving to always creating .eh_frame on all platforms as default,
because the troubleshooting help justifies the price.

That said, the option already exists for changing the default:
-funwind-tables. There shouldn't be another ARM specific flag. I can't
say what the best default is.

Renato Golin

unread,
Feb 6, 2014, 10:18:02 AM2/6/14
to LLVM Dev, Clang Dev
Hi Joerg,

You are right, there are three separate issues here: unwinding, debug info and exception handling.

Debug and EH require unwinding, but one doesn't require the other and unwinding is not exclusive to either.


On 6 February 2014 14:51, Joerg Sonnenberger <jo...@britannica.bec.de> wrote:
(1) Should enough unwind data be provided for a basic, read-only stack
walk?

As you said, platforms and systems should define their own defaults, and that's mainly driven by the front-end via flags and triples.

The behaviour of flags should be precise, and different flags should be on/off by default depending on the triple, so build systems can enforce flags if they need to make sure the behaviour will be the same across different platforms.

I can't decide that, but those decisions should stay in the front-end. To the back-end, function attributes should be able to control table emission (or maybe we need flags, I don't know).

 
That said, the option already exists for changing the default:
-funwind-tables. There shouldn't be another ARM specific flag.

So, can I assume that -f{no-}unwind-tables's syntax is to completely enable/disable the unwind tables and add/remove the ELF sections from the objects? If that's so, I can easily remove -arm-disable-ehabi and check on that flag.

thanks,
--renato


Joerg Sonnenberger

unread,
Feb 6, 2014, 10:47:48 AM2/6/14
to llv...@cs.uiuc.edu, Clang Dev
On Thu, Feb 06, 2014 at 03:18:02PM +0000, Renato Golin wrote:
> Debug and EH require unwinding, but one doesn't require the other and
> unwinding is not exclusive to either.

Correct.

> I can't decide that, but those decisions should stay in the front-end. To
> the back-end, function attributes should be able to control table emission
> (or maybe we need flags, I don't know).

I disagree on this. Table emission by itself doesn't involve code
generation and I don't think it makes sense as a per function attribute
either. You either want it for all functions or only when needed (e.g.
exceptions are possible). As such, it makes perfect sense to me as a
global flag.

> > That said, the option already exists for changing the default:
> > -funwind-tables. There shouldn't be another ARM specific flag.
> >
>
> So, can I assume that -f{no-}unwind-tables's syntax is to completely
> enable/disable the unwind tables and add/remove the ELF sections from the
> objects? If that's so, I can easily remove -arm-disable-ehabi and check on
> that flag.

-funwind-tables says that the tables should always be emitted. If there
are no other reasons like exception handling, they will only destructive
stack unwind.

-fno-unwind-tables says that tables should only be emitted, if another
(language specific) reason requires them. So for C++, it would normally
be ignored as exception support is the default. For C code, it would
ensure that no .eh_frame* sections are emitted, even on platforms that
require them for the ABI like AMD64.

Renato Golin

unread,
Feb 6, 2014, 1:42:17 PM2/6/14
to Keith Walker, LLVM Dev, Clang Dev
On 6 February 2014 18:02, Keith Walker <Keith....@arm.com> wrote:
You can make GCC emit unwind tables for C if you use the command line option -funwind-tables.

This could be useful if you are compiling C code which is called by C++ code and which itself calls C++ code and you wish the C++ exception to propagate through the C code.

Yes, we still need the possibility to force unwind tables in C code, and that's what -funwind-tables is there for.

Since -fexception only triggers landing pads and cleanup blocks, we don't need to worry about it, but the -funwind-tables should communicate with the back-end via some clear and unambiguous means.

Maybe function attributes aren't enough, but we need to make sure that they don't clash with the flags, if we do use them.


There is some interaction in that if you are generating a .eh_frame section for exception handling then there is little sense is generating a DWARF .debug_frame section as well;  I would hope most debuggers could use the .eh_frame section if present as its format is so similar to a .debug_frame section.

Right, makes sense.

It seems, then, that we basically need two types of unwinding, and they could live in different places. 

Before your patch, the debugger wasn't able to use the EHABI stack unwinding code. Is that because it was using .push/.save instead of .cfi directives? Or is that because the table generated is not readable by normal debuggers?


What does -arm-disable-ehabi actually mean?

Currently it is effectively "don't generate exception tables".

Yes. It has no effect on the front-end. It should actually be replaced by whatever mechanism -f{no-}unwind-tables is using.

I created a bug (PR18758) and copied you.


Maybe is should it rather be "don't generate EHABI exception tables but generate .eh_frame tables instead"?   Obviously this requires the necessary DWARF like support to generate the information for the .eh_frame tables.

This is where things start getting a little interesting. If we all agree that generating unwind tables is good *in any case*, even on embedded devices like Coretx-M0, than I think we should hard-code .eh_frame generation under any circumstance, and use -funwind-tables to generate EHABI tables.

Clang would then enable it by default depending on the language contract, which is of little importance to the back-end.

But the question I don't have an answer yet is: if we use that flag, what do we do when -fno-unwind-tables clashes with the uwtables function attribute?


 IF(arm_disable_ehabi)
   USE ARMException
 ELSE
   USE DwarfCFIException
 ENDIF

Today, it means:

IF(arm_disable_ehabi)
  nothing at all
ELSE
  USE ARMException
ENDIF

I'm sensing that the consensus is driving towards *always* emitting unwind tables, but I can't just use DwarfCFIException on ARM, it won't work. This is why we need to carefully extract the common interface, not just for the debug x EH problem, but also the Dwarf x EHABI problem.


Hmmm!   I'm very nervous about the backend making decisions based on the language.

No, sorry! That was a front-end decision! I'd die before enquiring the language in the back-end! :D


  IF (-funwind-tables || -fexceptions)

    uwtable
    IF (leaf / nothrow)
      nounwind
    ENDIF
  ELSE
    no uwtable
    no nounwind
  ENDIF

I'd only like to completely remove the unwind tables if the user forcefully selected -fno-unwind-tables, so I wouldn't like Clang to automatically select that, even in C code.


-fexceptions allows compiling exception handling constructs in the source language and enables the generation of unwind tables.
-funwind-tables only enables the generation of unwind tables.

That makes total sense. Right now, I can't see a difference between -fno-unwind-tables and -arm-disable-ehabi.

As far as I could see, -fcxx-exceptions has no relevance to this discussion, so I'll drop it right here.

My questions about the flags were answered, and it's going to be a lot easier than I thought. I'll change the ehabi flag to connect via unwind-tables and will send a patch review. 

Once that's done, we can get some work done on refactoring ARMException, but only after your patch lands upstream.

Thanks!
--renato

Richard Smith

unread,
Feb 6, 2014, 2:21:25 PM2/6/14
to Renato Golin, LLVM Dev, Keith Walker, Clang Dev
On Thu, Feb 6, 2014 at 5:29 AM, Renato Golin <renato...@linaro.org> wrote:
Folks,

We're having some discussions about the behaviour of exception handling and Dwarf sharing unwind logic, tables, etc. and it seems that the code around it wasn't designed with any particular goal in mind, but evolved (like the EHABI) and now we're seeing the results from it. 

The problems below are assuming C vs. C++, but it actually apply to any possibly-exceptional vs. never-exceptional cases.


1. C vs. C++

We have two unwind flags: nounwind, which flags functions that can't unwind (leaf, nothrow, etc) and uwtable, which forces generation of the table regardless of nounwind. It seems sensible that C++ code with exceptions enabled should generate the tables for all functions, in case they're called by (or call) external functions. In C we don't want any of that.

GCC seems to never emit tables, and G++ always do, even on C code (.c files, no exception or anything), which is very sensible and in line with my reasoning above. Clang, on the other hand, always generates them. I guess it'll have to figure out what to do based on its impressions on what language is being used to produce similar results.

I believe that emitting the tables on anything that could potentially interact with exceptional code makes sense, but that's clearly a front-end decision. To LLVM, nounwind and uwtables should be absolute:

IF (uwtables)
  IF (nownwind)
    CantUnwind
  ELSE
    Unwind Table
ELSE
  do nothing
ENDIF

This looks wrong, based on the LangRef:

"uwtable
  This attribute indicates that the ABI being targeted requires that an unwind table entry be produce for this function even if we can show that no exceptions passes by it."

I think it's probably:

if (nounwind)
  can't unwind

if (uwtable || (!nounwind && need uwtable to unwind))
  unwind table


2. .fnstart/.fnend+friends

Another problem is that the emission of unwinding tables (based on .fnstart/.fnend symbols and others) is conditional *only* to the existence (or not) of an exception handling class being loaded (EHABI, Dwarf). Which means that, we can't disable the EH on a per-function basis.

We'll have to change the way these symbols are emitted, at least when using ARMException, so that we can emit the tables and honour the uwtable on a per-function basis.

Again, this is a requirement for problem 1, but it'd need to be fixed after 3.


3. Unwinding code

Currently, even when no exception handling are needed, the exception code is used to generate Dwarf unwinding directives (CFI) for the debugger.

Both DwarfCFIException and ARMException inherit from DwarfException, and they are called to do the debug info about the stack unwinding, which is (at least) misplaced. The consensus is that this code should be factored out.

The part that is relevant to this thread is that, today, if -arm-disable-ehabi is requested, ARMException will not be used and we won't have a way of generating debug stack directives, which is wrong.

Factoring out this code is a requirement for the unwinding problem (1), since if we disable EH today, we'll disable Dwarf stack unwinding altogether. But we also need a final solution for problem 4 below before we start.


4. Clang EH control

There are a number of Clang/LLVM options to control exception handling:
 * -fno-excetpion (enable/disable EH on C++ mode, off in C mode)
 * -fcxx-exception (no idea, is it objC++ specific? does it control tables in any way?)
 * -funwind-tables (forces uwtable attribute?)
 * -arm-disable-ehabi (ARM specific bogus flag)

Those options are not always completely exclusive, and they damage different parts of the compilation process (as seen recently on the list), so we need a clear consensus on what each option mean (or should mean), and translate it to the back-end (via function attributes). This would considerably simplify the back-end and help us tackle this refactoring.

I believe the intent is:
  -fexceptions/-fno-exceptions controls whether we generate code that copes with exceptions passing through it.
  -fcxx-exceptions/-fno-cxx-exceptions controls whether we allow exception constructs in C++ code (throw, catch, try) and whether we validate exception specifications (both during compilation and at runtime).
  -fobjc-exceptions/-fno-objc-exceptions controls whether we allow exception constructs in ObjC code (@throw, @try, @catch).

So:

  -fno-exceptions implies -fno-cxx-exceptions and -fno-objc-exceptions
  -fcxx-exceptions and -fobjc-exceptions require -fexceptions

The (-cc1) frontend defaults to -fno-exceptions -fno-cxx-exceptions -fno-objc-exceptions (I'm not sure why).
The driver defaults to:
  * in ObjC++: -fcxx-exceptions -fobjc-exceptions -fexceptions
  * in ObjC: -fobjc-exceptions -fexceptions
  * in C++: -fcxx-exceptions -fexceptions
  * in C: <nothing>

(Users might want to specify -fexceptions in C, when building code like qsort that might have exceptions thrown across it.)


-funwind-tables appears to be an entirely orthogonal flag, which is by default determined based on the target (with some -f flags to override the default), entirely ignoring the -fexceptions flags and language mode. This does not appear to be a flag that an end-user should touch, under most circumstances, but it's far from clear to me that we're getting the default right here (maybe it should depend on -fexceptions?).


The 'nounwind' attribute is set if -fexceptions is specified, or if we have some other way of knowing the function does not throw. Exception: in -fobjc-exceptions mode, we ask the ObjC runtime whether to set the flag. This looks like a bug to me.

The 'uwtable' attribute is set based on the value we determined for -funwind-tables (either through an explicit flag or from the target).

My main target for this problem is to have the one true option on the front-ends, and rely only on function attributes on the back-end (including tools like llc). For that, I'd love to be left with -fexception only and infer all the rest from the language / conditions of the code.

Ex:

IF (C++ mode)
  IF (-fno-exception)
    no uwtable
    no nounwind
  ELSE
    uwtable
    IF (leaf / nothrow)
      nounwind
    ENDIF
  ENDIF
ELSE
  no uwtable
  no nounwind

This seems wrong -- in C with -fexceptions we do not want nounwind, and sometimes want uwtable (depending on ABI).
 
ENDIF

My main goal is to get rid of (at least) -arm-disable-ehabi.



Finally,

If you got this far, you're *really* interested in making the exception handling more user/dev friendly in LLVM, so I welcome any critics about the "proposal" above, as well as any explanations of the doubts expressed. I may be wrong about many things, feel free to enlighten me! We may have a lot less work to do, and I'll be happy. ;)

I believe the dependency graph of the solutions are:

4 -> 3 -> 2 -> 1

So, we first should solve the flags problem, than refactor the unwinding code out of EH, make the debug generation use them as well, and then we can start with the EH specific changes. The problem is that 4 will change things considerably, so we might need some scaffolding during the whole process.

Makes sense?

cheers,
--renato


Stephen Checkoway

unread,
Feb 6, 2014, 3:01:04 PM2/6/14
to Joerg Sonnenberger, Clang Dev, LLVM Developers Mailing List

On Feb 6, 2014, at 10:47 AM, Joerg Sonnenberger <jo...@britannica.bec.de> wrote:

> On Thu, Feb 06, 2014 at 03:18:02PM +0000, Renato Golin wrote:
>> I can't decide that, but those decisions should stay in the front-end. To
>> the back-end, function attributes should be able to control table emission
>> (or maybe we need flags, I don't know).
>
> I disagree on this. Table emission by itself doesn't involve code
> generation and I don't think it makes sense as a per function attribute
> either. You either want it for all functions or only when needed (e.g.
> exceptions are possible). As such, it makes perfect sense to me as a
> global flag.

Does LTO change this analysis?

--
Stephen Checkoway

Joerg Sonnenberger

unread,
Feb 6, 2014, 3:12:33 PM2/6/14
to LLVM Developers Mailing List, Clang Dev
On Thu, Feb 06, 2014 at 03:01:04PM -0500, Stephen Checkoway wrote:
>
> On Feb 6, 2014, at 10:47 AM, Joerg Sonnenberger <jo...@britannica.bec.de> wrote:
>
> > On Thu, Feb 06, 2014 at 03:18:02PM +0000, Renato Golin wrote:
> >> I can't decide that, but those decisions should stay in the front-end. To
> >> the back-end, function attributes should be able to control table emission
> >> (or maybe we need flags, I don't know).
> >
> > I disagree on this. Table emission by itself doesn't involve code
> > generation and I don't think it makes sense as a per function attribute
> > either. You either want it for all functions or only when needed (e.g.
> > exceptions are possible). As such, it makes perfect sense to me as a
> > global flag.
>
> Does LTO change this analysis?

I don't think so.

Joerg

Renato Golin

unread,
Feb 6, 2014, 4:27:52 PM2/6/14
to Richard Smith, LLVM Dev, Keith Walker, Clang Dev
On 6 February 2014 19:21, Richard Smith <ric...@metafoo.co.uk> wrote:
if (nounwind)
  can't unwind

can't unwind == unwind table + no EH directives + no EH table


if (uwtable || (!nounwind && need uwtable to unwind))
  unwind table

"need unwind table to unwind" is probably true in almost all cases. At least in all where ARMException and DwarfCFIException are concerned, which is the ones we're discussing about.

I'm beginning to think that there is no reason at all to have an uwtable attribute, given that the -funwind-tables is propagated to the back-end AND if we emit the table for one function we should do it for all.


I believe the intent is:
  -fexceptions/-fno-exceptions controls whether we generate code that copes with exceptions passing through it.

Right. Landing pads, cleanups, etc. We'll need those for emitting the EH tables, but not the unwind tables, so for this particular discussion, we don't need to worry.

 
  -fcxx-exceptions/-fno-cxx-exceptions controls whether we allow exception constructs in C++ code (throw, catch, try) and whether we validate exception specifications (both during compilation and at runtime).
  -fobjc-exceptions/-fno-objc-exceptions controls whether we allow exception constructs in ObjC code (@throw, @try, @catch).

Language-specific stuff, not even important to the EH tables (since in the back end we don't care what constructs you use in the language, only the IR basic block structure).


  -fno-exceptions implies -fno-cxx-exceptions and -fno-objc-exceptions
  -fcxx-exceptions and -fobjc-exceptions require -fexceptions

Will -fcxx-exceptions also include -fexceptions? I mean, if the user specify -fcxx-exceptions in C, will that also turn on all the internal flags that -fexception would?

If so, we really don't need to worry at all about them.


-funwind-tables appears to be an entirely orthogonal flag, which is by default determined based on the target (with some -f flags to override the default), entirely ignoring the -fexceptions flags and language mode.

Right. They do clash in the back-end, since one generates EH unwinding and the other might only generate Dwarf unwinding. We need to clear that confusion in the back-end, but I don't think that the front-end should even care on how it gets implemented in the end, as long as it works.


This does not appear to be a flag that an end-user should touch, under most circumstances, but it's far from clear to me that we're getting the default right here (maybe it should depend on -fexceptions?).

I think that both -fexceptions and -g should turn -funwind-tables by default. The third user is the backtrace which doesn't need debug or EH info (just the unwind table), and you need the -funwind-tables IFF your target doesn't have it on by default.

I can only think about GPU targets that won't need any of that, but they're not using Dwarf or EHABI handlers, so we shouldn't concern about that. Embedded CPUs might want them disabled to save space, and for that we should actively disable (-fno-unwind-tables or -Os/z).


The 'nounwind' attribute is set if -fexceptions is specified, or if we have some other way of knowing the function does not throw.

You mean -fno-exceptions, I believe. I think this behaviour is correct.


The 'uwtable' attribute is set based on the value we determined for -funwind-tables (either through an explicit flag or from the target).

Seems reasonable, though unnecessary in most cases.


This seems wrong -- in C with -fexceptions we do not want nounwind, and sometimes want uwtable (depending on ABI).

Yes, I agree, I was wrong. Unwind tables are almost entirely orthogonal to EH and Dwarf.


Thanks everyone for the enlightening responses, I'll have to digest everything again and see if I remember any of it tomorrow... :)

I managed to get a definite step out of this (http://llvm.org/PR18758), and I think we can safely ignore the -fexceptions flags for now.

My revised plan:

0. Get Keith's patch in to have unwinding without EH
1. Connect -funwind-tables with EHABI
2. Abstract the unwind code where both EH and the Dwarf producers can make use of
3. Disable unwind tables if -fno-unwind-tables on ARM
4. Apply CFI unwinding if -funwind-tables AND -fno-exceptions on EHABI

I'm not sure why we're using .save/.push and not CFI with EHABI, but step 2 would be a lot simpler if we could unify both exception handling classes, and step 4 would completely disappear. Anton/Logan?

cheers,
--renato

Richard Smith

unread,
Feb 6, 2014, 7:19:22 PM2/6/14
to Renato Golin, LLVM Dev, Keith Walker, Clang Dev
On Thu, Feb 6, 2014 at 1:27 PM, Renato Golin <renato...@linaro.org> wrote:
On 6 February 2014 19:21, Richard Smith <ric...@metafoo.co.uk> wrote:
if (nounwind)
  can't unwind

can't unwind == unwind table + no EH directives + no EH table


if (uwtable || (!nounwind && need uwtable to unwind))
  unwind table

"need unwind table to unwind" is probably true in almost all cases. At least in all where ARMException and DwarfCFIException are concerned, which is the ones we're discussing about.

I'm beginning to think that there is no reason at all to have an uwtable attribute, given that the -funwind-tables is propagated to the back-end AND if we emit the table for one function we should do it for all.


I believe the intent is:
  -fexceptions/-fno-exceptions controls whether we generate code that copes with exceptions passing through it.

Right. Landing pads, cleanups, etc. We'll need those for emitting the EH tables, but not the unwind tables, so for this particular discussion, we don't need to worry.

 
  -fcxx-exceptions/-fno-cxx-exceptions controls whether we allow exception constructs in C++ code (throw, catch, try) and whether we validate exception specifications (both during compilation and at runtime).
  -fobjc-exceptions/-fno-objc-exceptions controls whether we allow exception constructs in ObjC code (@throw, @try, @catch).

Language-specific stuff, not even important to the EH tables (since in the back end we don't care what constructs you use in the language, only the IR basic block structure).


  -fno-exceptions implies -fno-cxx-exceptions and -fno-objc-exceptions
  -fcxx-exceptions and -fobjc-exceptions require -fexceptions

Will -fcxx-exceptions also include -fexceptions? I mean, if the user specify -fcxx-exceptions in C, will that also turn on all the internal flags that -fexception would?

If so, we really don't need to worry at all about them.

-fcxx-exceptions implies -fexceptions if the input kind is C++ and is ignored if not.
-fobjc-exceptions implies -fexceptions if the input kind is ObjC(++) and is ignored if not.

Which isn't quite what you asked, but I think the conclusion is the same (you don't need to care, the driver does the right thing).
 
-funwind-tables appears to be an entirely orthogonal flag, which is by default determined based on the target (with some -f flags to override the default), entirely ignoring the -fexceptions flags and language mode.

Right. They do clash in the back-end, since one generates EH unwinding and the other might only generate Dwarf unwinding. We need to clear that confusion in the back-end, but I don't think that the front-end should even care on how it gets implemented in the end, as long as it works. 
This does not appear to be a flag that an end-user should touch, under most circumstances, but it's far from clear to me that we're getting the default right here (maybe it should depend on -fexceptions?).

I think that both -fexceptions and -g should turn -funwind-tables by default. The third user is the backtrace which doesn't need debug or EH info (just the unwind table), and you need the -funwind-tables IFF your target doesn't have it on by default.

I can only think about GPU targets that won't need any of that, but they're not using Dwarf or EHABI handlers, so we shouldn't concern about that. Embedded CPUs might want them disabled to save space, and for that we should actively disable (-fno-unwind-tables or -Os/z).

This (-fexceptions and -g imply -funwind-tables) seems like it's probably the right thing for most targets. With SjLj exceptions, -fexceptions probably doesn't need -funwind-tables.

Keith Walker

unread,
Feb 6, 2014, 1:02:03 PM2/6/14
to Renato Golin, Anton Korobeynikov, Eric Christopher, Jim Grosbach, Clang Dev, LLVM Dev
> From: Renato Golin [mailto:renato...@linaro.org]
>
> We're having some discussions about the behaviour of exception handling and Dwarf
> sharing unwind logic, tables, etc. and it seems that the code around it wasn't
> designed with any particular goal in mind, but evolved (like the EHABI) and now
> we're seeing the results from it.
>
> The problems below are assuming C vs. C++, but it actually apply to any possibly-exceptional
> vs. never-exceptional cases.
>
>
> 1. C vs. C++
>
> We have two unwind flags: nounwind, which flags functions that can't unwind (leaf,
> nothrow, etc) and uwtable, which forces generation of the table regardless of
> nounwind. It seems sensible that C++ code with exceptions enabled should generate
> the tables for all functions, in case they're called by (or call) external functions.
> In C we don't want any of that.
>
> GCC seems to never emit tables, and G++ always do, even on C code (.c files,
> no exception or anything), which is very sensible and in line with my reasoning above.
> Clang, on the other hand, always generates them. I guess it'll have to figure out what
> to do based on its impressions on what language is being used to produce similar results.

You can make GCC emit unwind tables for C if you use the command line option -funwind-tables.

This could be useful if you are compiling C code which is called by C++ code and which itself calls C++ code and you wish the C++ exception to propagate through the C code.

> I believe that emitting the tables on anything that could potentially interact
> with exceptional code makes sense, but that's clearly a front-end decision.
> To LLVM, nounwind and uwtables should be absolute:

> IF (uwtables)
> IF (nownwind)
> CantUnwind
> ELSE
> Unwind Table
> ELSE
> do nothing
> ENDIF

This certainly appears to me a sensible sequence of decision.

> 2. .fnstart/.fnend+friends
>
> Another problem is that the emission of unwinding tables (based on
> .fnstart/.fnend symbols and others) is conditional *only* to the existence
> (or not) of an exception handling class being loaded (EHABI, Dwarf). Which
> means that, we can't disable the EH on a per-function basis.
>
> We'll have to change the way these symbols are emitted, at least when using
> ARMException, so that we can emit the tables and honour the uwtable on a per-function basis.
>
> Again, this is a requirement for problem 1, but it'd need to be fixed after 3.
>
>
> 3. Unwinding code
>
> Currently, even when no exception handling are needed, the exception code is
> used to generate Dwarf unwinding directives (CFI) for the debugger.

This is because the 2 are so similar.

> Both DwarfCFIException and ARMException inherit from DwarfException, and they
> are called to do the debug info about the stack unwinding, which is (at least)
> misplaced. The consensus is that this code should be factored out.

There is some interaction in that if you are generating a .eh_frame section for exception handling then there is little sense is generating a DWARF .debug_frame section as well; I would hope most debuggers could use the .eh_frame section if present as its format is so similar to a .debug_frame section.

> The part that is relevant to this thread is that, today, if -arm-disable-ehabi
> is requested, ARMException will not be used and we won't have a way of
> generating debug stack directives, which is wrong.

What does -arm-disable-ehabi actually mean?

Currently it is effectively "don't generate exception tables".

Maybe is should it rather be "don't generate EHABI exception tables but generate .eh_frame tables instead"? Obviously this requires the necessary DWARF like support to generate the information for the .eh_frame tables.

> Factoring out this code is a requirement for the unwinding problem (1), since
> if we disable EH today, we'll disable Dwarf stack unwinding altogether. But we
> also need a final solution for problem 4 below before we start.

I think the solution depends on what we mean when we specify -arm-disable-ehabi ... is it

IF(arm_disable_ehabi)
USE ARMException
ELSE
USE DwarfCFiNoException <---- Just generates DWARF debugging information
ENDIF

Or is it

IF(arm_disable_ehabi)
USE ARMException
ELSE
USE DwarfCFIException
ENDIF

Hmmm! I'm very nervous about the backend making decisions based on the language.

As I mentioned above I also wonder if you do still need to support -funwind-tables for compiling C code through which you wish to allow exceptions to propogate.

In which case the logic is more like ....

IF (-funwind-tables || -fexceptions)
uwtable
IF (leaf / nothrow)
nounwind
ENDIF
ELSE
no uwtable
no nounwind
ENDIF

The main difference I see between -fexceptions and -funwind-tables is:

-fexceptions allows compiling exception handling constructs in the source language and enables the generation of unwind tables.
-funwind-tables only enables the generation of unwind tables.

Keith



Keith


-- IMPORTANT NOTICE: The contents of this email and any attachments are confidential and may also be privileged. If you are not the intended recipient, please notify the sender immediately and do not disclose the contents to any other person, use it for any purpose, or store or copy the information in any medium. Thank you.

ARM Limited, Registered office 110 Fulbourn Road, Cambridge CB1 9NJ, Registered in England & Wales, Company No: 2557590
ARM Holdings plc, Registered office 110 Fulbourn Road, Cambridge CB1 9NJ, Registered in England & Wales, Company No: 2548782

Renato Golin

unread,
Feb 7, 2014, 4:35:17 AM2/7/14
to Richard Smith, LLVM Dev, Keith Walker, Clang Dev
On 7 February 2014 00:19, Richard Smith <ric...@metafoo.co.uk> wrote:
This (-fexceptions and -g imply -funwind-tables) seems like it's probably the right thing for most targets. With SjLj exceptions, -fexceptions probably doesn't need -funwind-tables.

Thanks, I think that's the general consensus, yes.

Do we have such logic in Clang at the moment?

My original point was that the back-end shouldn't try to guess, so front-ends should pass this information down, either via function attributes or flags. Attributes would be better for multi-stage compilation process, but if the default target description is shared between front and back ends, than we might not need this, and flags become the preferred method.

This is another reason to proceed with a separate library for target description (that deals with triples, -m flags, defaults, etc), to be used by both front and back ends, so then we don't have to worry about changing all sides whenever a default changes.

cheers,
--renato

Renato Golin

unread,
Feb 7, 2014, 6:35:36 AM2/7/14
to Richard Smith, LLVM Dev, Keith Walker, Clang Dev
On 7 February 2014 09:35, Renato Golin <renato...@linaro.org> wrote:
Do we have such logic in Clang at the moment?

Let me answer my own question. Right now, both -funwind-tables and -fno-unwind-tables only enable the -muniwind-tables for -cc1, and either lack or presence of that flag in -cc1 has the same behaviour on C++ code: they both generate .eh_frame sections.

The only behaviour I found is that -munwind-tables will add uwtable to functions, which had no effect on x86_64. Is that because x86_64 mandates unwind tables?

If the semantics is that uwtable turns on tables on targets that don't require it (SjLj), but not that it turns off if missing on targets that mandate it, I can't use it to disable them on ARM. To be honest, recently, I'm not even sure I need, or want to disable. 

With Keith's patch to generate Dwarf unwind, the -arm-disable-ehabi flag could possibly be removed clean, and we can go directly to step 2.

Keith, Reid, are you happy with always generating unwind tables on ARM regardless?

cheers,
--renato

Jim Grosbach

unread,
Feb 7, 2014, 12:46:28 PM2/7/14
to Renato Golin, LLVM Dev, Keith Walker, Clang Dev
Just to double check, none of this will change the current behavior on Darwin, right?

-Jim

Renato Golin

unread,
Feb 7, 2014, 1:03:24 PM2/7/14
to Jim Grosbach, LLVM Dev, Keith Walker, Clang Dev
On 7 February 2014 17:46, Jim Grosbach <gros...@apple.com> wrote:
Just to double check, none of this will change the current behavior on Darwin, right?

Right! Unless Darwin starts using EHABI. ;)

--renato

Jim Grosbach

unread,
Feb 7, 2014, 1:20:47 PM2/7/14
to Renato Golin, LLVM Dev, Keith Walker, Clang Dev
Perfect. Thanks!

-Jim

Rafael Espíndola

unread,
Feb 9, 2014, 9:54:24 PM2/9/14
to Renato Golin, LLVM Dev, Keith Walker, Clang Dev
On 6 February 2014 08:29, Renato Golin <renato...@linaro.org> wrote:
> Folks,
>
> We're having some discussions about the behaviour of exception handling and
> Dwarf sharing unwind logic, tables, etc. and it seems that the code around
> it wasn't designed with any particular goal in mind, but evolved (like the
> EHABI) and now we're seeing the results from it.
>
> The problems below are assuming C vs. C++, but it actually apply to any
> possibly-exceptional vs. never-exceptional cases.
>
>
> 1. C vs. C++
>
> We have two unwind flags: nounwind, which flags functions that can't unwind
> (leaf, nothrow, etc) and uwtable, which forces generation of the table
> regardless of nounwind. It seems sensible that C++ code with exceptions
> enabled should generate the tables for all functions, in case they're called
> by (or call) external functions. In C we don't want any of that.

Depends on the ABI. On X86-64 we should produce the tables even with C
according to the ABI.

Cheers,
Rafael

Rafael Espíndola

unread,
Feb 9, 2014, 9:59:52 PM2/9/14
to LLVM Developers Mailing List, Clang Dev
> I disagree on this. Table emission by itself doesn't involve code
> generation and I don't think it makes sense as a per function attribute
> either. You either want it for all functions or only when needed (e.g.
> exceptions are possible). As such, it makes perfect sense to me as a
> global flag.

It has to be an attribute because of LTO. You can LTO a file compiled
with -fasynchronous-unwind-tables and one with
-fno-asynchronous-unwind-tables. That is why we have the uwtable
attribute.

Cheers,
Rafael

Renato Golin

unread,
Feb 10, 2014, 3:44:30 AM2/10/14
to Rafael Espíndola, Clang Dev, LLVM Developers Mailing List
On 10 February 2014 02:59, Rafael Espíndola <rafael.e...@gmail.com> wrote:
> It has to be an attribute because of LTO. You can LTO a file compiled
> with -fasynchronous-unwind-tables and one with
> -fno-asynchronous-unwind-tables. That is why we have the uwtable
> attribute.

Hi Rafael,

I've removed the -arm-disable-ehabi flag already, and I'm planning on
using the uwtable attribute to determine if we need to emit the unwind
tables or not, just like DwarfCFIException. What is the real semantics
of uwtable?

I don't see that attribute too often, but I still see tables being
emitted in x86_64. The rules on what to emit in beginFunction() are a
bit convoluted, and I guess that's because uwtables' semantics is not
definite. I was hoping for something a bit stronger, because we do
lose a lot of information in the back-end, such as what exception
style is has (depending on the target, language, flags).

Is there any definitive explanation on the complete semantics of
uwtables and nounwind? I could be wrong, but from what I can see,
beginFunction() is based on circumstantial behaviour, not explicit
semantics.

cheers,
--renato

Rafael Espíndola

unread,
Feb 10, 2014, 9:44:06 AM2/10/14
to Renato Golin, Clang Dev, LLVM Developers Mailing List
On 10 February 2014 03:44, Renato Golin <renato...@linaro.org> wrote:
> On 10 February 2014 02:59, Rafael Espíndola <rafael.e...@gmail.com> wrote:
>> It has to be an attribute because of LTO. You can LTO a file compiled
>> with -fasynchronous-unwind-tables and one with
>> -fno-asynchronous-unwind-tables. That is why we have the uwtable
>> attribute.
>
> Hi Rafael,
>
> I've removed the -arm-disable-ehabi flag already, and I'm planning on
> using the uwtable attribute to determine if we need to emit the unwind
> tables or not, just like DwarfCFIException. What is the real semantics
> of uwtable?

Just that: unwind tables are output for the function, even if nothing
else seems to say they are necessary.

> I don't see that attribute too often, but I still see tables being
> emitted in x86_64.

Strange. I see it even with simple C testcases:

void bar(void);
void zed(void) { bar();}

produces

define void @zed() #0 ....
attributes #0 = { nounwind uwtable...

>The rules on what to emit in beginFunction() are a
> bit convoluted, and I guess that's because uwtables' semantics is not
> definite. I was hoping for something a bit stronger, because we do
> lose a lot of information in the back-end, such as what exception
> style is has (depending on the target, language, flags).
>
> Is there any definitive explanation on the complete semantics of
> uwtables and nounwind? I could be wrong, but from what I can see,
> beginFunction() is based on circumstantial behaviour, not explicit
> semantics.

I agree that the logic in there seems a bit odd. What is called
shouldEmitMoves should probably include all possible
reasons for printing the unwind tables. We should probably also use
uwtable to guard the emission of other table formats
as you propose.

The semantics uwtable is just "make sure it is possible to unwind past
this function". It doesn't include anything more,
like the ability to run destructors. It is used for ABIs that require
it for use in debuggers and profilers.

I am not too sure about nounwind. I think it is more about using
unwind for control flow in the language. Having it allows us
to prune information used for exception handling.

> cheers,
> --renato

Cheers,
Rafael

Renato Golin

unread,
Feb 11, 2014, 4:40:44 AM2/11/14
to Rafael Espíndola, Clang Dev, LLVM Developers Mailing List
On 10 February 2014 14:44, Rafael Espíndola <rafael.e...@gmail.com> wrote:
> Just that: unwind tables are output for the function, even if nothing
> else seems to say they are necessary.

Right, I shall rely on that behaviour when preparing my patch and we
shall see how it behaves on EHABI land. Maybe shouldEmitMoves will
have to change, but I'm hoping it doesn't.


> The semantics uwtable is just "make sure it is possible to unwind past
> this function". It doesn't include anything more,
> like the ability to run destructors. It is used for ABIs that require
> it for use in debuggers and profilers.

Well, that meaning fails when you have both uwtable and nounwind,
which means: "generate a CantUnwind table".


> I am not too sure about nounwind. I think it is more about using
> unwind for control flow in the language. Having it allows us
> to prune information used for exception handling.

nounwind translates into "doesNotThrow()", which is *also* used on
"needsUnwindTables()" which is used in "needsCFIMoves()".

It's not simple. ;)

cheers,
--renato

Rafael Espíndola

unread,
Feb 11, 2014, 9:12:52 AM2/11/14
to Renato Golin, Clang Dev, LLVM Developers Mailing List
>> The semantics uwtable is just "make sure it is possible to unwind past
>> this function". It doesn't include anything more,
>> like the ability to run destructors. It is used for ABIs that require
>> it for use in debuggers and profilers.
>
> Well, that meaning fails when you have both uwtable and nounwind,
> which means: "generate a CantUnwind table".

Why fails? I would read a "generate a CantUnwind table" as a table
that a debugger or profiler can use, but lacks information to allow an
exception to be handled.

>> I am not too sure about nounwind. I think it is more about using
>> unwind for control flow in the language. Having it allows us
>> to prune information used for exception handling.
>
> nounwind translates into "doesNotThrow()", which is *also* used on
> "needsUnwindTables()" which is used in "needsCFIMoves()".
>
> It's not simple. ;)
>
> cheers,
> --renato

Cheers,
Rafael

Renato Golin

unread,
Feb 11, 2014, 9:20:41 AM2/11/14
to Rafael Espíndola, Clang Dev, LLVM Developers Mailing List
On 11 February 2014 14:12, Rafael Espíndola <rafael.e...@gmail.com> wrote:
> Why fails? I would read a "generate a CantUnwind table" as a table
> that a debugger or profiler can use, but lacks information to allow an
> exception to be handled.

Right, I was assuming CantUnwind was interpreted as is by debug and
profilers. If CantUnwind is *only* blocking EH, than it looks good.

Let Keith's patch go in and I'll have a go trying to merge the two.

Thanks,
--renato

Evgeniy Stepanov

unread,
Feb 13, 2014, 6:35:26 AM2/13/14
to Renato Golin, LLVM Developers Mailing List, Clang Dev
I assume this "CantUnwind table" is not the same as

Unwind table index '.ARM.exidx' at offset 0x818 contains 4 entries:
0x5d4 <main>: 0x1 [cantunwind]

because the latter prevent any unwinding, breaking
debuggers/profilers/sanitizers.

As I understand, the right way to enable basic unwind through any
function (without emitting landing pands etc, unless they are needed
for something else) is -funwind-tables. I'm going to add that in the
driver for all sanitizers then.

Renato Golin

unread,
Feb 13, 2014, 8:39:49 AM2/13/14
to Evgeniy Stepanov, LLVM Developers Mailing List, Clang Dev
On 13 February 2014 11:35, Evgeniy Stepanov <eug...@google.com> wrote:
> Unwind table index '.ARM.exidx' at offset 0x818 contains 4 entries:
> 0x5d4 <main>: 0x1 [cantunwind]

This is exactly what I meant.


> because the latter prevent any unwinding, breaking
> debuggers/profilers/sanitizers.

In that case, my argument stands again.


> As I understand, the right way to enable basic unwind through any
> function (without emitting landing pands etc, unless they are needed
> for something else) is -funwind-tables. I'm going to add that in the
> driver for all sanitizers then.

-funwind-tables are not yet understood by ARM, but it is for Dwarf EH.
I plan on moving ARM to the same style as Dwarf, so you can assume it
is correct.

cheers,

Evgeniy Stepanov

unread,
Feb 13, 2014, 8:47:07 AM2/13/14
to Renato Golin, LLVM Developers Mailing List, Clang Dev
On Thu, Feb 13, 2014 at 5:39 PM, Renato Golin <renato...@linaro.org> wrote:
> On 13 February 2014 11:35, Evgeniy Stepanov <eug...@google.com> wrote:
>> Unwind table index '.ARM.exidx' at offset 0x818 contains 4 entries:
>> 0x5d4 <main>: 0x1 [cantunwind]
>
> This is exactly what I meant.
>
>
>> because the latter prevent any unwinding, breaking
>> debuggers/profilers/sanitizers.
>
> In that case, my argument stands again.
>
>
>> As I understand, the right way to enable basic unwind through any
>> function (without emitting landing pands etc, unless they are needed
>> for something else) is -funwind-tables. I'm going to add that in the
>> driver for all sanitizers then.
>
> -funwind-tables are not yet understood by ARM, but it is for Dwarf EH.
> I plan on moving ARM to the same style as Dwarf, so you can assume it
> is correct.

Hm, I see that -funwind-tables on arm-linux-androideabi target
replaces this "cantunwind" with a proper unwind table.
Hence http://llvm-reviews.chandlerc.com/D2762.

Renato Golin

unread,
Feb 13, 2014, 8:52:50 AM2/13/14
to Evgeniy Stepanov, LLVM Developers Mailing List, Clang Dev
On 13 February 2014 13:47, Evgeniy Stepanov <eug...@google.com> wrote:
> Hm, I see that -funwind-tables on arm-linux-androideabi target
> replaces this "cantunwind" with a proper unwind table.
> Hence http://llvm-reviews.chandlerc.com/D2762.

If Android is using EHABI (I think it is), the default now is to
output full tables all the time, everywhere. This will change to be
the same as x86 soon.

--renato

Evgeniy Stepanov

unread,
Feb 13, 2014, 9:03:03 AM2/13/14
to Renato Golin, LLVM Developers Mailing List, Clang Dev
On Thu, Feb 13, 2014 at 5:52 PM, Renato Golin <renato...@linaro.org> wrote:
> On 13 February 2014 13:47, Evgeniy Stepanov <eug...@google.com> wrote:
>> Hm, I see that -funwind-tables on arm-linux-androideabi target
>> replaces this "cantunwind" with a proper unwind table.
>> Hence http://llvm-reviews.chandlerc.com/D2762.
>
> If Android is using EHABI (I think it is), the default now is to
> output full tables all the time, everywhere. This will change to be
> the same as x86 soon.

It does use EHABI, but at r201326:
$ cat 1.cc
int f() {}
$ ./bin/clang++ -target arm-linux-androideabi 1.cc -c
$ readelf -u 1.o
0x0 <_Z1fv>: 0x1 [cantunwind]
$ ./bin/clang++ -target arm-linux-androideabi 1.cc -c -funwind-tables
$ readelf -u 1.o
0x0 <_Z1fv>: 0x8000b0b0
Compact model 0
0x00 vsp = vsp + 4
0xb0 finish
0xb0 finish

Logan Chien

unread,
Feb 14, 2014, 11:42:04 AM2/14/14
to Evgeniy Stepanov, Clang Dev, LLVM Developers Mailing List
Hi Evgeniy,

I have run in to the some unwind handling table issue recently.  After some investigation, I believe that the correct behavior is to generate [cantunwind] with and without the -funwind-tables.  Thus, I am afraid that -funwind-tables might not be a good solution for your use case.  (i.e. your program might fall in an infinite loop during stack unwinding.)

I will send the details for discussion ASAP.

Sincerely,
Logan

Evgeniy Stepanov

unread,
Feb 15, 2014, 6:38:28 AM2/15/14
to Logan Chien, Clang Dev, LLVM Developers Mailing List
I'd love to hear more details. Are you saying that this infinite loop
is a limitation of EHABI table format, and not something that can be
fixed in the compiler?
Meanwhile, please notice that gcc behavior matches current clang
behavior that I described above. We would not want to create an
incompatibility.

Renato Golin

unread,
Feb 15, 2014, 10:36:54 AM2/15/14
to Evgeniy Stepanov, Clang Dev, LLVM Developers Mailing List
On 15 February 2014 11:38, Evgeniy Stepanov <eugeni....@gmail.com> wrote:
> I'd love to hear more details. Are you saying that this infinite loop
> is a limitation of EHABI table format, and not something that can be
> fixed in the compiler?

I'd find it hard to believe. EHABI has been out for a long time and I
don't remember any intrinsic problem like that.

Remember that our EHABI implementation is *very* new, and that this
problem is when mixing Dwarf unwinding with EHABI unwinding, which we
have just enabled yesterday (by Keith's patch), so "here be dragons".
;)


> Meanwhile, please notice that gcc behavior matches current clang
> behavior that I described above. We would not want to create an
> incompatibility.

Absolutely! We need to be extra careful.

It'd be good if we had some examples compiled with GCC and LLVM
intermixed and throw exceptions from different places, either on the
check-all or the test-suite.

cheers,
--renato

Logan Chien

unread,
Feb 17, 2014, 11:59:04 AM2/17/14
to Renato Golin, Clang Dev, LLVM Developers Mailing List
Hi all,

I feel that there are two problems with the existing infrastructure:

* The nounwind attribute is ambiguous for (1) not throwing exceptions and (2) not performing stack unwinding. I feel that it will be better to separate this in two different attributes

* There is some problem when the function has both uwtable and nounwind. Although, I think it fine to keep the current definition of nounwind, however, the uwtable attribute will be much useless to its user. Besides, even if we wish to keep the current definition, to avoid the undefined behavior, we have to slightly change the code generator to emit [can't unwind] whenever there is a nounwind attribute.

I am writing my thoughts in detail below, please have a look, and feel free to challenge or send me the feedback. Thanks.

Sincerely,
Logan

tl;dr

(HTML version: http://loganchien.github.io/llvm/nounwind.html )

Notations
---------

To make my argument clear, I would like to use a different notation from LLVM IR:

* The no-throw attribute guarantees that the function will not throw any exception object.

* The no-unwind attribute guarantees that the function won't read the prior calling sequence with the stack unwinder.

For simplification, if the function is not tagged with no-throw then it is may-throw. Similarly, if the function is not tagged with no-unwind, then it is may-unwind.

* The ehtable attribute guarantees that the exception handling table, such as the LSDA handler data and (by implementation) the stack unwinding information for exception, will be generated for exception handling. If the table includes stack unwinding infomration, then it is guaranteed that the unwinding information is sufficient enough to go back to previous propagation barrier (landingpad, cleanup filter, or can't unwind.)

* The uwtable attribute guarantees that the unwind table will be generated for stack unwinding for profilers, debuggers, (possibly) exception handling mechansim and etc. This attribute guarantees that the complete stack trace can be obtained as long as the calling sequence does not contain the external function or the function marked with no-unwind.

It is possible for some exception handling implemenation requires uwtable to unwind the stack, it is not necessary to do so. On the other hand, even if the stack unwind information for exception handling is encoded in ehtable, it might be insufficient to implement the stack unwinder. We will come back with this later.

If a function has both no-uwnind and uwtable, then there must be a mechanism (might be an agreement between the compiler and the run-time library) to signal the stack unwinder to stop before passing through the function. Otherwise, the undefined behavior might be happened. Similarly, we need a similar mechanism for the function with both no-throw and ehtable

With these notations, we can do some simple reasoning:

* may-throw implies may-unwind — We have to unwind the stack if the exception is thrown by the function, thus the may-throw attribute will imply may-unwind.

* no-unwind implies no-throw — The contraposition to the previous statement.

Please notice that no-throw does not imply no-unwind. We will come back with this later.


Attribute Properties
--------------------

In this section, I would like to discuss the properties of no-unwind and no-throw, and the rules to infer the attributes if the programmer didn't specified them in function definition. These properties may be used by some optimization passes, such as PruneEH.

First, since no-unwind implies no-throw, we can add no-throw attribute to the functions which have no-unwind attribute.

Second, it is clear that the external functions should be considered as may-throw unless it is explicitly tagged with no-throw. Similarly, the external functions should be may-unwind unless it is explicitly tagged with no-unwind.

For function definition, we can inspect the instructions:

* If the function does not have any call or invoke instruction to a may-unwind callee function and -fasynchronous-unwind-table compiler option isn't given, then we can add the no-unwind attribute to the function.

* If the function does not have any call or invoke instruction to a may-throw callee function, then we can add the no-throw attribute to the function (safe approximation.)

* If we can prove that every exceptions are caught by the landing pad and the function won't re-throw the exception or won't continue the unwinding, then we can add the no-throw to the function attribute.

Please notice that we have to deliberately separate the attribute into no-throw and no-unwind because the landingpad instruction can't give any guarantee on no-unwind attribute.


Problems with Existing LLVM Infrastructure
------------------------------------------

There are two function attributes related with unwinding and exception handling in the existing LLVM infrastructure. Here are the descriptions copied from the LLVM reference manual:

* nounwind — This function attribute indicates that the function never returns with an unwind or exceptional control flow. If the function does unwind, its runtime behavior is undefined.

* uwtable — This attribute indicates that the ABI being targeted requires that an unwind table entry be produce for this function even if we can show that no exceptions passes by it. This is normally the case for the ELF x86-64 abi, but it can be disabled for some compilation units.

From my interpretation, the specification for nounwind guarantees that the function will neither throw an exception nor unwind the stack, i.e. nounwind = no-throw + no-unwind. The specification for uwtable guarantees some unwind table will be generated; however, it does not specify which kind of unwind table should be generated. IIRC, the ARM backend actually implements ehtable, which has only limited capability to unwind the stack.

# Inconsistant Interpretation of nounwind

The things are getting tricky when it comes to the implementation. There is a PruneEH pass, which will try to remove the unnecessary exception handling informantion. For example, the following code:

define void @foo() {
entry:
  ret void
}

will be converted to:

define void @foo() nounwind {
entry:
  ret void
}

Here's a much more complex example:

define void @foo() {

declare void @_Z3barv()

declare i32 @__gxx_personality_v0(...)

define void @_Z3foov() {
entry:
  invoke void @_Z3barv() to label %try.cont unwind label %lpad

lpad:
  %0 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*)
          catch i8* null
  ret void

try.cont:
  ret void
}

is converted to

declare void @_Z3barv()

declare i32 @__gxx_personality_v0(...)

; Function Attrs: nounwind
define void @_Z3foov() #0 {
entry:
  invoke void @_Z3barv()
          to label %try.cont unwind label %lpad

lpad:
  %0 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*)
          catch i8* null
  ret void

try.cont:
  ret void
}

attributes #0 = { nounwind }

Some careful reader might have noticed the problem here. The nounwind attribute is added to @_Z3foov() simply because the landingpad can catch the exception object. However, in my notation, only no-throw can be added to this function, and no-throw does not imply no-unwind. It is possible for @_Z3foov() to unwind the stack. For example, the function @_Z3barv() may call _Unwind_Backtrace() to get the backtrace.

Besides, some optimization might make the situation even worse. AFAIK, most of the stack unwinding implementations rely on the value in the link register or the return address on the stack. However, there is an optimization in LLVM code generation which will not save the link register if the callee function has noreturn attribute [1]. Even though the optimization will only be applied when the callee function has nounwind attribute as well, the problem still occurs because PruneEH will (incorrectly) add nounwind to some function which actually unwinds. Besides, I am in doubt about whether we can apply this optimization when the caller function has the uwtable attribute and requires the unwind table.

# Unwind Table and Can't Unwind

The mixture of uwtable and nounwind will cause another problem.

IMO, it is incorrect to decide whether to emit [can't unwind] with !needsUnwindTableEntry() [2]. The needsUnwindTableEntry() is defined as either the function has uwtable attribute or the function does not have nounwind. Thus, !needsUnwindTableEntry() imples not having uwtable attribute and having nounwind. As the result, the [can't unwind] won't be generated for the following function:

define void @foo() uwtable nounwind {
entry:
  call void @bar()
  ret void
}

The stack unwinder might continue to unwind the stack because there isn't any mark in the unwind table to stop the stack unwinding. And, unfortunately, according to the LLVM reference manual, this will result in undefined behavior. In fact, I did encounter some real example [3] which will fall into an infinite loop during the phase 1 unwinding.
Summary

In conclusion, I would like to suggest that we need to put more efforts to define a precise specification for exception handling and stack unwinding mechanism, so that the optimization passes and the run-time environment can interact with each other without problems.

In summary, IMO, these are the topic have to be discussed:

* Should we separate nounwind into no-throw and no-unwind?
  - What is the impact in terms of the run-time performance?
  - What is the impact on the code size?

* Should we write more implementation details about uwtable in the LLVM reference manual?
  - What is the possible expected behavior when uwtable is used with nounwind

If we have some decision, I am willing to write the patch. :-)


Footnotes
---------

    See <llvm>/lib/CodeGen/VirtRegMap.cpp line 290
    See <llvm>/lib/CodeGen/AsmPrinter/ARMException.cpp line 65
    See <libc++abi>/test/test_vector3.cpp


Rafael Espíndola

unread,
Feb 18, 2014, 8:44:40 AM2/18/14
to Logan Chien, Clang Dev, LLVM Developers Mailing List
> * The uwtable attribute guarantees that the unwind table will be generated
> for stack unwinding for profilers, debuggers, (possibly) exception handling
> mechansim and etc. This attribute guarantees that the complete stack trace
> can be obtained as long as the calling sequence does not contain the
> external function or the function marked with no-unwind.
>
> It is possible for some exception handling implemenation requires uwtable to
> unwind the stack, it is not necessary to do so. On the other hand, even if
> the stack unwind information for exception handling is encoded in ehtable,
> it might be insufficient to implement the stack unwinder. We will come back
> with this later.
>
> If a function has both no-uwnind and uwtable, then there must be a mechanism
> (might be an agreement between the compiler and the run-time library) to
> signal the stack unwinder to stop before passing through the function.
> Otherwise, the undefined behavior might be happened. Similarly, we need a
> similar mechanism for the function with both no-throw and ehtable

I would prefer to keep uwtable with its current meaning: It is
possible to unwind past this function. That has nothing to do with
exception handling, except that some ABIs might choose to use the same
tables for unwinding when an exception is thrown.

So any combination of uwtable and another attribute still gets an
entry in .eh_table and a debugger or profiler can go past it.

Cheers,
Rafael

Rafael Espíndola

unread,
Feb 18, 2014, 8:54:50 AM2/18/14
to Logan Chien, Clang Dev, LLVM Developers Mailing List
> Some careful reader might have noticed the problem here. The nounwind
> attribute is added to @_Z3foov() simply because the landingpad can catch the
> exception object. However, in my notation, only no-throw can be added to
> this function, and no-throw does not imply no-unwind. It is possible for
> @_Z3foov() to unwind the stack. For example, the function @_Z3barv() may
> call _Unwind_Backtrace() to get the backtrace.
>
> Besides, some optimization might make the situation even worse. AFAIK, most
> of the stack unwinding implementations rely on the value in the link
> register or the return address on the stack. However, there is an
> optimization in LLVM code generation which will not save the link register
> if the callee function has noreturn attribute [1].

Can we just call this a bug? I still don't see why we need more than 2
attributes. The semantics being:

* uwtable. We can unwind past this from a debugger or profiler. No
exception handling support guaranteed.
* nounwind. No need to output exception handling support.

In particular, if a function has uwtable, it should be invalid to drop
setting a link register if an ABI requires that for unwinding.

> define void @foo() uwtable nounwind {
> entry:
> call void @bar()
> ret void
> }
>
> The stack unwinder might continue to unwind the stack because there isn't
> any mark in the unwind table to stop the stack unwinding. And,
> unfortunately, according to the LLVM reference manual, this will result in
> undefined behavior.

We probably just have to explain better what is undefined behavior.
Passing an exception through this function is. Calling backtrace is
not.

Cheers,
Rafael

Renato Golin

unread,
Feb 18, 2014, 9:46:21 AM2/18/14
to Rafael Espíndola, Clang Dev, LLVM Developers Mailing List
On 18 February 2014 13:44, Rafael Espíndola <rafael.e...@gmail.com> wrote:
> I would prefer to keep uwtable with its current meaning: It is
> possible to unwind past this function. That has nothing to do with
> exception handling, except that some ABIs might choose to use the same
> tables for unwinding when an exception is thrown.

I agree this is sensible. uwtable has *only* to do with unwinding and
it's the job of the exception handling mechanism to either use the
unwinding semantics or not, based on how the platform expects EH to
work, which may be different across systems. I'm not saying this is
the simplest implementation, but it's the simplest semantics.

Maybe the attribute nounwind should actually be called nothrow?


> So any combination of uwtable and another attribute still gets an
> entry in .eh_table and a debugger or profiler can go past it.

Yes!

Though, Clang seems not to generate -munwind-tables when -g or -pg is
present on ARM. This is probably a bug.

--renato

Rafael Espíndola

unread,
Feb 18, 2014, 9:53:24 AM2/18/14
to Renato Golin, Clang Dev, LLVM Developers Mailing List
> Yes!
>
> Though, Clang seems not to generate -munwind-tables when -g or -pg is
> present on ARM. This is probably a bug.

For -g that might be an issue depending on what ARM needs to do with
uwtable. If it only needs to generate an entry in a table, that is
probably fine. If there are code differences that are needed to make
unwinding work on arm, that probably needs to be attached to a
different option since we really want the generated code with and
without -g to be the same.

Cheers,
Rafael
Reply all
Reply to author
Forward
0 new messages