[cfe-dev] Unwind, exception handling, debuggers and profilers

Renato Golin renato.golin at linaro.org
Wed Mar 19 06:29:02 PDT 2014


Folks,

I'm sorry for getting at this again, but this will not be the last
discussion on the topic, so let's just get to business. We're about to
merge the last critical patch to make EHABI compatible with other EH
mechanisms in LLVM (D3079), and that has unearthed a few issues with
the function attributes.

Logan's blog post [1] contains a proposal to split unwinding from
exceptional logic which I think we should give it a try. I'm trying to
make it as backward compatible as possible, but the point is to make
the IR authoritative on how and when to emit what kind of unwind
information.

** Unwinding **

AFAIK, there are three ways to unwind the stack:

1. Checking the return information on the stack (LR). This might be
optimized away, so debuggers and profilers can't rely on it. Binaries
with no debug or EH information at all have to resort to this when
back-tracing during a segfault or similar.

2. Dwarf unwinding. This is a carefully constructed stack unwinding
Dwarf information for each frame that debuggers and profilers can use
to gather precise information on the stack, typically stored on
.eh_frame sections.

3. Itanium-compatible exception handling. This is another format of
unwind tables using personality routines and similar unwinding logic
to Dwarf CFI directives, also on .eh_frame. This is why you can use
CFI to build the EH tables, and why debuggers can also use this table
to unwind the stack.

** Exception Handling **

LLVM has four types of EH: Dwarf, ARM, SjLj and Win64. Of them, only
SjLj doesn't need unwind tables, and each of the others, even being
Itanium-compatible, need slightly different logic.

In LLVM, all targets that use DwarfCFIException are using the same
tables as the debugger and profilers would, but ARM and Win64 have
separate unwind logic. This is where the problem begins.

We're left with three EH types:

1. No tables (SjLj)
2. Dwarf tables (DwarfCFI)
3. Specific EH tables (ARM, Win64?)

** Debug & Profiling **

In debug/profile mode (-g, -pg), none of the optimizations that prune
unwind information should be allowed to run. I believe currently this
is informed via the uwtable/nothrow function attributes, but since
their use is controversial, we can reach situations where information
is indeed removed when it shouldn't happen. (see Logan's post).

** Function Attributes Proposal **

I still don't have a clear idea on what do we need, but being overly
conservative, we should plan for every behaviour to be expressed by a
flag, and during the discussion, merge similar behaviour and possibly
remove unused flags along the way.

Today we have uwtable and nothrow, which are interchangeably being
used to mean debug and EH unwinding, which is just wrong. Logan's
proposal is to split uwtable and ehtable, nothrow and nounwind and to
understand which trumps which if combined.

In my view, nounwind has no purpose. I may be wrong, but I can't think
of a case where you want to generate debug unwind tables and NOT want
to unwind a particular function on a purely non-EH context. Supposing
nounwind has meaning, I'm happy with the semantics Logan proposes in
his post.

A function without any of the attributes below should emit *no* tables at all.

The remaining possibilities are:

* uwtable
  - Generated only when -g or -pg are specified
  - Option -fno-unwind-tables loses meaning (unless nounwind has meaning)
  - Generate full EH/Debug tables on all archs

* ehtable
  - Generated for EH only (front-end/arch/lang specific)
  - Could be forced enabled/disabled via -feh-tables/-fno-eh-tables
  - Only emits EH directives on ARM, full debug on others, nothing on SjLj

* nothrow
  - Leaf functions, throw(), languages without EH, etc.
  - On its own, do nothing (no tables are emitted anyway)
  - +uwtable, do nothing (we don't want to break debug)
  - +ehtable, emit CantUnwind (the whole purpose)

* nounwind
  - No idea why, but assuming there is a reason
  - On its own, do nothing (no tables are emitted anyway)
  - +uwtable, emit CantUnwind (given Logan's semantics)
  - +ehtable, emit CantUnwind (given Logan's semantics)

The primary reason for adding the ehtable attribute is to be able to
control the nothrow flag correctly. Other reasons are to limit
emitting tables to archs that support it (ie. no SjLj) and to focus
-fno-eh-table on EH info only, not Debug, so you don't get broken
debug info when you just add "-g" to a build that already has
-fno-eh-tables.

The option -fno-unwind-tables should either be removed (if nounwind
has no meaning apart from EH context), or carefully merged with
-fno-eh-tables. Logan can expand on that.

The reason why uwtable emits both EH and debug is backwards
compatibility. ARM currently emits both directives for binutils' sake,
and others have fused Debug/EH directives anyway. This attribute
should also prevents any optimization related to stack unwinding, even
on recursive or tail calls.

LangRef would have to change, but I don't think old IR would stop working.

Does that sound like a reasonable plan? Anything I haven't mentioned
that needs mentioning? Any conflict that this will generate on any
optimization pass?

cheers,
--renato

[1] http://loganchien.github.io/llvm/nounwind.html



More information about the cfe-dev mailing list