Topic: Exceptions Footprint
Author: non-existent@iobox.com ("Sergey P. Derevyago")
Date: Wed, 22 Sep 2004 17:18:46 GMT Raw View
Bob Bell wrote:
> EH table:
>
> PC is in r1 is in r2 is in stack size
> -------------------------------------------
> 1 r1 r2 1
> 2 r1 r2 2
> 3 stack slot 2 r2 2
> 4 r1 r2 1
> 5 r1 r2 2
> 6 r1 stack slot 2 2
> 7 r1 r2 1
>
> (Stack slot 1 is always used for the return address.)
>
> No matter what block of code an exception passes up through, the table
> tells us, explicitly and efficiently, what needs to be done to restore
> execution context and tear down the stack.
>
OK, good.
Now let's consider Itanium architecture: the CPU has 128 general purpose
registers of 64 bits long. Do you really suggest to build 128*8*N bytes
EH-tables for every function?
Sure you don't. That's why an implementation has to disable certain
optimizations and store the number of already created registers at well-known
stack slot.
P.S. Also it seems like our discussion starts trashing, unfortunately. We
really need some input from compiler implementors.
--
With all respect, Sergey. http://ders.angen.net/
mailto : ders at skeptik.net
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: Michael.Karcher@writeme.com (Michael Karcher)
Date: Wed, 22 Sep 2004 17:48:24 GMT Raw View
Sergey P. Derevyago <non-existent@iobox.com> wrote:
>> out from where hN, h(N-1), ..., h1 have been called. So the complexity is
>> already of the order N. Restoring further registers contents (besides PC)
>> during unwinding of course slows the process down, but just by a constant
>> factor of perhaps four.
> Could you elaborate please where the constant 4 comes from? What
> testing under what assumptions and/or conditions has been done?
That's an educated(?) guess. Currently for stack unwinding, you have to
restore (on Intel architecture): EIP (called PC in architecture independent
literature), ESP (the stack pointer) and depending on the frame type used,
also EBP (a stack frame pointer). Restoring the stack pointer is difficult
without having the frame pointer pointing to a frame structure containing
the next frame, as each stack manipulation modifies the stack pointer.
As on this platform (using linux) usually no registers are saved through
function calls at all, except for EBX (used as something like DLL base
address), I just tried to think about how many registers could be needed to
restore (on other platforms like ARM it should be around 8). Compare 8
additional registers to restore with about three already resstored registers
and add the additional overhead of being able to look into other registers,
still keeping in mind that EH lookups also have to be done per frame, I got
to the factor of perhaps(!) four. Probably the easiest way to accomplish the
register restauration is implementing it in machine code, and let the
unwinding code "call" it.
>> As this price has to be paid only in the case of a
>> thrown exception, it is usually unimportant.
> Please refer to the my reply to Bob Bell (Message-ID:
> <414EB1C3.A69394F1@iobox.com>).
I was unable to find any point in that posting considering the
non-exceptional case. The tables may be bloated, but they are not even
accessed (on demand-load-systems not even loaded), until the exception
really happens.
Michael Karcher
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: non-existent@iobox.com ("Sergey P. Derevyago")
Date: Wed, 22 Sep 2004 17:49:02 GMT Raw View
Hyman Rosen wrote:
> > Currently, the text implicitly suggests that the "code" approach uses
> > dynamic datastructures while the readonly tables are enough for the "table"
> > approach. But it's not the case.
>
> It *is* the case. God, this is frustrating. I know you're not a troll, but
> I swear, it feels like I'm talking to one.
>
OK. It seems like we look at the same phrases but (implicitly) disagree on
their meaning. And here is the root of misunderstanding.
Due to some reasons I failed to clearly explain my point of view, sorry. I'd
better stop my participation in this thread.
Thank you for your efforts.
--
With all respect, Sergey. http://ders.angen.net/
mailto : ders at skeptik.net
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: hyrosen@mail.com (Hyman Rosen)
Date: Thu, 23 Sep 2004 15:08:07 GMT Raw View
Sergey P. Derevyago wrote:
> Do you really suggest to build 128*8*N bytes EH-tables for every function?
You don't have to. The encoding suggested was just one approach,
and it illustrates the static nature of the needed information.
Instead, the table can hold pointers to snippets of code that do
the right thing for each value of the PC. The size of that will
be about the same size as the non-exceptional code for doing
returns.
This approach also lends itself to compression - if multiple
sections can all unwind the same way, the snippet of code to
do it can be shared.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: belvis@pacbell.net (Bob Bell)
Date: Thu, 23 Sep 2004 16:33:29 GMT Raw View
non-existent@iobox.com ("Sergey P. Derevyago") wrote in message news:<41514711.91A4AC27@iobox.com>...
> Bob Bell wrote:
> > EH table:
> >
> > PC is in r1 is in r2 is in stack size
> > -------------------------------------------
> > 1 r1 r2 1
> > 2 r1 r2 2
> > 3 stack slot 2 r2 2
> > 4 r1 r2 1
> > 5 r1 r2 2
> > 6 r1 stack slot 2 2
> > 7 r1 r2 1
> >
> > (Stack slot 1 is always used for the return address.)
> >
> > No matter what block of code an exception passes up through, the table
> > tells us, explicitly and efficiently, what needs to be done to restore
> > execution context and tear down the stack.
> >
> OK, good.
> Now let's consider Itanium architecture: the CPU has 128 general purpose
> registers of 64 bits long. Do you really suggest to build 128*8*N bytes
> EH-tables for every function?
Of course not. How does this figure follow from my examples? The size
of the EH tables have nothing to do with the sizes of the registers.
Are you under the mistaken impression that the EH tables contain
copies of register values? If you are, that might account for a large
number of odd statements you have made in this thread, like why you
thought recursion made things difficult for EH tables, or the idea
that EH tables are dynamic. EH tables are not dynamic; nothing is
written into them at run time. They contain nothing more than
instructions for how to unwind the stack from a given PC location
within a function.
Further, there's no reason the tables can't be compacted. For example,
if a given function only disturbs a small number of registers, there's
no reason the tables have to account for all 128 of them. Note that
the EH table example given above is perfectly valid on a register-rich
implementation; since only r1 and r2 are disturbed by the function,
they are the only ones mentioned in the tables. The amount of
information in the tables is dependent on two things:
-- the number of places in a function which require unique unwinding
steps
-- the actual complexity of unwinding at each of those places
Both of these are essentially proportional to the complexity of a
given function, which means that table size will be roughly
proportional to the size of a function. With compaction techniques,
tables can often be made to take up significantly less space than the
function itself.
It's odd that you understand that the code can be subject to any
number of clever optimizations, but you assume that the EH tables
themselves must be implemented as naively and inefficiently as
possible.
> Sure you don't. That's why an implementation has to disable certain
> optimizations and store the number of already created registers at well-known
> stack slot.
"Has to"? Sorry, you still haven't shown that.
Bob
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: stefan_heinzmann@yahoo.com (Stefan Heinzmann)
Date: Thu, 23 Sep 2004 16:50:49 GMT Raw View
Hyman Rosen wrote:
> Sergey P. Derevyago wrote:
> > Do you really suggest to build 128*8*N bytes EH-tables for every
> function?
>
> You don't have to. The encoding suggested was just one approach,
> and it illustrates the static nature of the needed information.
> Instead, the table can hold pointers to snippets of code that do
> the right thing for each value of the PC. The size of that will
> be about the same size as the non-exceptional code for doing
> returns.
>
> This approach also lends itself to compression - if multiple
> sections can all unwind the same way, the snippet of code to
> do it can be shared.
Yet another approach would be some kind of difference encoding. You
store the full EH table at the function entry (and that is probably very
compressible because parameter passing is standardized) and for each
point in the code (PC) where the situation changes you store the
difference in the EH tables. This makes stack unwinding more laborious,
because you have to walk through the table from the beginning of the
function up to the point where the exception occurred, but the table may
well be much smaller. So you've traded EH run-time efficiency for EH
table size.
But the important issue that has been demonstrated umpteen times now
stays the same. No optimization for the normal case needs to be spared,
as the EH tables can always be adjusted appropriately.
Sergey is right when he says that this may inflate the EH tables,
compared to a case where some restraint is used in optimizing the code.
In this sense there is a compromise to be made between size (and
efficiency) of exception handling code/data and the optimization of the
normal execution path. But it seems that the inflates the impact of this
beyond proportion. I have no hard data available, but my feeling is that
the size increase of the EH tables because of aggressive code
optimization is usually fairly minor.
So, Sergey, maybe you can agree that there is no problem with the EH
table approach *in principle*. Whether you like the *size* of the EH
tables or not is a different matter. I don't think you can draw a
general conclusion here. Just about all that you can do is to tinker
with your compiler switches to adjust the balance between size/speed of
the normal code vs. size/speed of exception handling. In most cases I
can think of I would probably use the settings that favor the normal
case, even when that means the EH tables become fairly large, except
maybe in an embedded system where I might have to go for a setting with
the smallest overall memory footprint. The alternatives I have at this
level are (merely) a quality of (compiler) implementation issue.
--
Cheers
Stefan
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: hyrosen@mail.com (Hyman Rosen)
Date: Thu, 23 Sep 2004 18:10:11 GMT Raw View
Bob Bell wrote:
> Of course not. How does this figure follow from my examples?
Your table appeared to have a column for each register, so at
the very least it would take #Regsisters * #PCs space.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: dave@boost-consulting.com (David Abrahams)
Date: Thu, 23 Sep 2004 18:30:04 GMT Raw View
hyrosen@mail.com (Hyman Rosen) writes:
> Sergey P. Derevyago wrote:
> > Do you really suggest to build 128*8*N bytes EH-tables for every function?
>
> You don't have to. The encoding suggested was just one approach,
> and it illustrates the static nature of the needed information.
> Instead, the table can hold pointers to snippets of code that do
> the right thing for each value of the PC. The size of that will
> be about the same size as the non-exceptional code for doing
> returns.
>
> This approach also lends itself to compression - if multiple
> sections can all unwind the same way, the snippet of code to
> do it can be shared.
Also, the variety of possible instructions needed to accomplish
unwinding being much smaller than those of a typical CPU, it's
possible to generate highly compact bytecode rather than regular
machine instructions.
--
Dave Abrahams
Boost Consulting
http://www.boost-consulting.com
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: belvis@pacbell.net (Bob Bell)
Date: Fri, 24 Sep 2004 20:47:35 GMT Raw View
hyrosen@mail.com (Hyman Rosen) wrote in message news:<1095961882.197223@master.nyc.kbcfp.com>...
> Bob Bell wrote:
> > Of course not. How does this figure follow from my examples?
>
> Your table appeared to have a column for each register, so at
> the very least it would take #Regsisters * #PCs space.
I should have been clearer. His statement
> > Now let's consider Itanium architecture: the CPU has 128 general purpose
> > registers of 64 bits long. Do you really suggest to build 128*8*N bytes
> > EH-tables for every function?
seems to indicate that he thought table entry sizes are equal to the
sizes of the registers, but that didn't follow from anything I said.
Bob
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: non-existent@iobox.com ("Sergey P. Derevyago")
Date: Mon, 20 Sep 2004 15:32:06 GMT Raw View
First of all, thank you for the constructive and detailed reply.
> > f->A::A->h->h->h->...->h->some_exception
> > 1 2 3 N
> >
> > During the stack unwinding the runtime needs the R2 value in order to figure
> > out how many A objects have been created in f().
> > 1. This value is stored somewhere between h1 and hN.
> > 2. N in virtually unlimited.
> >
> > That is the reason why EH-enabled implementations have to store the number of
> > already created array members on the well-known stack slot (usually, at the
> > f()'s stack frame).
>
> I'll take a crack at this one.
>
> I'm going to reformat your example a bit and divide the code into
> blocks. I'm even going to add a little, to mutate r2 after each
> instance of it being saved just to make it interesting:
[good explanation snipped]
> It should be obvious that this works because at each boundary between
> function invocations we consult the EH table, which essentially tells
> us to do the same thing that a normal return would do to restore r2.
> It should also be obvious that it doesn't matter how deeply h()
> recurses, nor does it matter which switch cases are taken.
>
> No matter what a normal return would do, table-driven unwinding can do
> the same thing.
>
Also the key paragraph of my posting is:
> > Well, the R2 value can *always* be found somewhere between h1 and hN. But the
> > implementation that really tracks down register values via virtually unlimited
> > inspection of the stack frames would be unacceptably inefficient.
You have illustrated that the R2 value can *always* be found somewhere
between h1 and hN. Sure, and I said this above.
But the point is "the implementation ... would be unacceptably inefficient".
At least two approaches can be suggested:
1. Save the number of already created objects at the f()'s stack frame before
every call to A::A(). The implementation can easily fetch this value during
the stack unwinding.
2. Build bulk EH-tables that allow to restore _all_ the register values at
_any_ execution point that has been stored on the stack before an exception
was thrown.
In the second case the implementation has to restore all the register values
for every stack frame it is inspecting during the stack unwinding. This
cumbersome resource-consuming process is _sometimes_ needed to figure out how
many objects have been created in some function before. I.e. occasionally we
really need a few restored register values but we have to restore all the
registers _every_ time. Also the size of these extended EH-tables must also be
taken into account during the consideration of the performance impact.
IMHO it's to expensive to do stack unwinding this way. That's why I have
said: an implementation has to store the counter at some well-known stack
slot.
BTW do you know some EH-implementation that really builds the tables that can
be used to restore any register value to track the number of already created
objects?
--
With all respect, Sergey. http://ders.angen.net/
mailto : ders at skeptik.net
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: belvis@pacbell.net (Bob Bell)
Date: Tue, 21 Sep 2004 01:10:23 GMT Raw View
non-existent@iobox.com ("Sergey P. Derevyago") wrote in message news:<414EB1C3.A69394F1@iobox.com>...
> First of all, thank you for the constructive and detailed reply.
Your welcome.
> > No matter what a normal return would do, table-driven unwinding can do
> > the same thing.
>
> Also the key paragraph of my posting is:
>
> > > Well, the R2 value can *always* be found somewhere between h1 and hN. But the
> > > implementation that really tracks down register values via virtually unlimited
> > > inspection of the stack frames would be unacceptably inefficient.
>
> You have illustrated that the R2 value can *always* be found somewhere
> between h1 and hN. Sure, and I said this above.
> But the point is "the implementation ... would be unacceptably inefficient".
I'm afraid you're losing me. Implementation of what? The normal return
case or the unwinding case? Unacceptably inefficient compared to what?
I don't see why you consider it inefficient when unwinding has to do
the same thing that a normal return would do, at least as far as
restoring a function's execution context when unwinding "passes
through" a function.
I understood that your original point was that use of EH tables would
not be able to allow unwinding to properly destruct the right number
of objects when exceptions are thrown during array construction.
>From your post dated September 9:
> The point is that PC value is _not_ sufficient to track the number of
> currently created objects during the array construction. This issue is a
> showstopper for "read-only" table-driven EH.
I think this claim has been pretty thoroughly debunked.
> BTW do you know some EH-implementation that really builds the tables that can
> be used to restore any register value to track the number of already created
> objects?
Not off the top of my head. Do you know of any that get it wrong?
Bob
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: "Sergey P. Derevyago" <non-existent@iobox.com>
Date: Mon, 20 Sep 2004 23:24:54 CST Raw View
Michael Karcher wrote:
> The implementation already has to unwind the stack frame by frame to find
> out from where hN, h(N-1), ..., h1 have been called. So the complexity is
> already of the order N. Restoring further registers contents (besides PC)
> during unwinding of course slows the process down, but just by a constant
> factor of perhaps four.
>
Could you elaborate please where the constant 4 comes from? What testing
under what assumptions and/or conditions has been done?
> As this price has to be paid only in the case of a
> thrown exception, it is usually unimportant.
>
Please refer to the my reply to Bob Bell (Message-ID:
<414EB1C3.A69394F1@iobox.com>).
--
With all respect, Sergey. http://ders.angen.net/
mailto : ders at skeptik.net
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: Hyman Rosen <hyrosen@mail.com>
Date: Mon, 20 Sep 2004 23:29:01 CST Raw View
Sergey P. Derevyago wrote:
> You have illustrated that the R2 value can *always* be found somewhere
> between h1 and hN. Sure, and I said this above.
> But the point is "the implementation ... would be unacceptably inefficient".
No, absolutely not. This technique is acceptably efficient.
It does little more work in the EH case than it would do on
a normal return. Unwinding the stack means excatly that -
the code simulates what would happen on a normal return.
On top of that, inefficiency when actually processing an
exception is considered the proper tradeoff for efficiency
in the non-exceptional cases.
So there is no reason to expect that the EH code should
bypass the entire recursive stack of h calls in one fell
swoop to get directly back to the constructor. Instead,
EH patiently unwinds the stack frame by frame.
> At least two approaches can be suggested:
No. The correct approach is the one everyone has been
explaining to you. There is no reason to attempt to be
super-efficient in EH, because exceptions are expected
to be rare are permitted to be handled relatively slowly.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: non-existent@iobox.com ("Sergey P. Derevyago")
Date: Tue, 21 Sep 2004 17:36:55 GMT Raw View
Hyman Rosen wrote:
> > But the point is "the implementation ... would be unacceptably inefficient".
>
> No, absolutely not. This technique is acceptably efficient.
> It does little more work in the EH case than it would do on
> a normal return.
>
I'm talking about highly optimized code. Sure, suboptimal code doesn't
require sophisticated techniques to restore saved registers.
Let's consider the following example:
void f()
{
switch (some_opaque_runtime_condition) {
case 1: { use R1 register
return; }
case 2: { use R2 register
return; }
case 3: { call g();
return; }
}
}
1. A suboptimal implementation might look like:
void suboptimal_f()
{
always allocate stack frame for both R1 and R2 registers
switch (some_opaque_runtime_condition) {
case 1: { save R1 register // R2 slot is unused
use R1 register
restore R1 register
return; }
case 2: { save R2 register // R1 slot is unused
use R2 register
restore R2 register
return; }
case 3: { call g(); // both R1 and R2 slots are unused
return; }
}
}
As you can see, the unused stack slots are the price for the light EH tables.
In this case both R1 and R2 values can easily be restored during the stack
unwinding. And yes: _this_ technique is acceptably efficient.
2. And now let's take a look at an "optimal" implementation:
void optimal_f()
{
switch (some_opaque_runtime_condition) {
case 1: { allocate stack frame only for R1 register
save R1 register // no unused slots
use R1 register
restore R1 register
return; }
case 2: { allocate stack frame only for R2 register
save R2 register // no unused slots
use R2 register
restore R2 register
return; }
case 3: { call g(); // no slots allocated, no unused slots
return; }
}
}
In this case, no unused stack slots are allocated and some really
sophisticated techniques are need to figure out whether some particular
register was saved and _where_.
> Unwinding the stack means excatly that -
> the code simulates what would happen on a normal return.
>
But the prise is the key. Both bulk EH tables and sophisticated EH algorithms
are required to deal with highly optimized code.
In principle, one can tell that the sophisticated algorithm is not the issue.
But the enormous size of required EH tables does really affect the performance
even in the case where exceptions aren't thrown.
P.S. Is there any compiler vendor around? Could you please make some comments
on the issue above?
--
With all respect, Sergey. http://ders.angen.net/
mailto : ders at skeptik.net
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: non-existent@iobox.com ("Sergey P. Derevyago")
Date: Tue, 21 Sep 2004 17:37:05 GMT Raw View
Bob Bell wrote:
> > But the point is "the implementation ... would be unacceptably inefficient".
> >
> I'm afraid you're losing me. Implementation of what? The normal return
> case or the unwinding case?
>
I'm trying to point out that the need for unwinding does affect the normal
return case: some optimizations become inappropriate. That is "the normal
code" can be made more optimal but the additional support for EH will degrade
the cumulative performance of the whole application.
> Unacceptably inefficient compared to what?
>
The resulting performance of the whole application degrades.
> I don't see why you consider it inefficient when unwinding has to do
> the same thing that a normal return would do, at least as far as
> restoring a function's execution context when unwinding "passes
> through" a function.
>
Please refer to the my reply to Hyman Rosen (Message-ID:
<414FF14D.AE9F90E0@iobox.com>).
> I understood that your original point was that use of EH tables would
> not be able to allow unwinding to properly destruct the right number
> of objects when exceptions are thrown during array construction.
>
Not exactly. As for the TR in question:
-----------------------------------8<-----------------------------------
IMHO the description of the "table" approach should be somehow changed
to reflect the fact that implementation has to dynamically maintain certain
datastructures too.
Currently, the text implicitly suggests that the "code" approach uses
dynamic datastructures while the readonly tables are enough for the "table"
approach. But it's not the case.
That's why the wording looks confusing to me.
-----------------------------------8<-----------------------------------
> >From your post dated September 9:
>
> > The point is that PC value is _not_ sufficient to track the number of
> > currently created objects during the array construction. This issue is a
> > showstopper for "read-only" table-driven EH.
>
> I think this claim has been pretty thoroughly debunked.
>
IMHO it's not the case: PC + read-only tables + register values (+ ...) are
required.
It has been shown that the register values can always be restored. Sure.
My point is that in the case of highly optimized code the right way to go is
to save the number of already created objects on the stack. An implementation
has to go this way due to the performance reasons.
> > BTW do you know some EH-implementation that really builds the tables that can
> > be used to restore any register value to track the number of already created
> > objects?
>
> Not off the top of my head. Do you know of any that get it wrong?
>
IMHO some comments from compiler implementors are of great value here...
--
With all respect, Sergey. http://ders.angen.net/
mailto : ders at skeptik.net
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: hyrosen@mail.com (Hyman Rosen)
Date: Tue, 21 Sep 2004 19:44:07 GMT Raw View
===================================== MODERATOR'S COMMENT:
This discussion looks like it's turning from standards-related issues
to issues about implementing C++ exceptions in a production compiler.
This is an interesting and important topic, but probably best suited to
another group.
===================================== END OF MODERATOR'S COMMENT
Sergey P. Derevyago wrote:
> As you can see, the unused stack slots are the price for the light EH tables.
It is clear by now that only you see such a thing.
I don't see this at all. There is no need for unused
statck slots for EH.
> In this case, no unused stack slots are allocated and some really
> sophisticated techniques are need to figure out whether some particular
> register was saved and _where_.
No, these techniques are not sophisticated at all.
Those same sequences of restore instructions that
you've written in each branch are what go into the
EH data. Presumably, although you do not show it,
in between the use and restore in each case there
is a a throw, or a call to a function which throws.
Then you have three different PCs, and each PC is
used to index into EH tables which include the code
needed to restore only the appropriate registers.
> But the prise is the key. Both bulk EH tables and sophisticated EH
> algorithms are required to deal with highly optimized code.
I don't know what you consider bulky and sophisticated;
I consider them relatively lean and straightforward, and
certainly not much larger than the equivalent inline error
handling code would be.
> But the enormous size of required EH tables does really affect the performance
> even in the case where exceptions aren't thrown.
Why do you think they are enormous? They should be roughly
the same size as the code which would be required if you
used error return values and tested for those. Implementations
can place the EH tables and code apart from the mainline code,
so that it need not even be loaded until an exception is thrown.
How does this affect performance?
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: belvis@pacbell.net (Bob Bell)
Date: Wed, 22 Sep 2004 02:06:11 GMT Raw View
non-existent@iobox.com ("Sergey P. Derevyago") wrote in message news:<414FF1E8.62A764C2@iobox.com>...
> Bob Bell wrote:
> > > But the point is "the implementation ... would be unacceptably inefficient".
> > >
> > I'm afraid you're losing me. Implementation of what? The normal return
> > case or the unwinding case?
> >
> I'm trying to point out that the need for unwinding does affect the normal
> return case: some optimizations become inappropriate.
So far you have not shown that. My detailed example showed that each
different calling convention you proposed was easily (and efficiently)
handled by EH tables. Can you propose any other call/return
optimizations that the EH tables wouldn't efficiently deal with?
> > I understood that your original point was that use of EH tables would
> > not be able to allow unwinding to properly destruct the right number
> > of objects when exceptions are thrown during array construction.
> >
> Not exactly. As for the TR in question:
> -----------------------------------8<-----------------------------------
> IMHO the description of the "table" approach should be somehow changed
> to reflect the fact that implementation has to dynamically maintain certain
> datastructures too.
> Currently, the text implicitly suggests that the "code" approach uses
> dynamic datastructures while the readonly tables are enough for the "table"
> approach. But it's not the case.
> That's why the wording looks confusing to me.
> -----------------------------------8<-----------------------------------
I would have thought you'd realize by now that there are no dynamic
data structures with the table approach. If you think there are, could
you please refer to my detailed example a couple posts back and point
out what the dynamic data structures are?
> > >From your post dated September 9:
>
> > > The point is that PC value is _not_ sufficient to track the number of
> > > currently created objects during the array construction. This issue is a
> > > showstopper for "read-only" table-driven EH.
> >
> > I think this claim has been pretty thoroughly debunked.
> >
> IMHO it's not the case: PC + read-only tables + register values (+ ...) are
> required.
> It has been shown that the register values can always be restored. Sure.
> My point is that in the case of highly optimized code the right way to go is
> to save the number of already created objects on the stack. An implementation
> has to go this way due to the performance reasons.
You keep claiming that, but so far you haven't shown an example where
an optimization for the normal return case is precluded. Perhaps if
you expend the effort trying to come up with such an example, you will
be able to convince yourself that these constraints you imagine don't
exist.
> > > BTW do you know some EH-implementation that really builds the tables that can
> > > be used to restore any register value to track the number of already created
> > > objects?
> >
> > Not off the top of my head. Do you know of any that get it wrong?
> >
> IMHO some comments from compiler implementors are of great value here...
I'm sure they would be.
Bob
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: belvis@pacbell.net (Bob Bell)
Date: Wed, 22 Sep 2004 02:06:23 GMT Raw View
non-existent@iobox.com ("Sergey P. Derevyago") wrote in message news:<414FF14D.AE9F90E0@iobox.com>...
> 2. And now let's take a look at an "optimal" implementation:
>
> void optimal_f()
> {
> switch (some_opaque_runtime_condition) {
>
> case 1: { allocate stack frame only for R1 register
> save R1 register // no unused slots
> use R1 register
> restore R1 register
> return; }
>
> case 2: { allocate stack frame only for R2 register
> save R2 register // no unused slots
> use R2 register
> restore R2 register
> return; }
>
> case 3: { call g(); // no slots allocated, no unused slots
> return; }
>
> }
> }
>
> In this case, no unused stack slots are allocated and some really
> sophisticated techniques are need to figure out whether some particular
> register was saved and _where_.
I don't understand why you think it's hard to figure out where a
register is saved.
void optimal_f()
{
1 ------------
switch (some_opaque_runtime_condition) {
case 1:
allocate stack frame only for R1 register
2 ------------
save R1 register // no unused slots
3 ------------
use R1 register
restore R1 register
4 ------------
return;
case 2:
allocate stack frame only for R2 register
5 ------------
save R2 register // no unused slots
6 ------------
use R2 register
restore R2 register
7 ------------
return;
case 3:
call g(); // no slots allocated, no unused slots
return;
}
}
EH table:
PC is in r1 is in r2 is in stack size
-------------------------------------------
1 r1 r2 1
2 r1 r2 2
3 stack slot 2 r2 2
4 r1 r2 1
5 r1 r2 2
6 r1 stack slot 2 2
7 r1 r2 1
(Stack slot 1 is always used for the return address.)
No matter what block of code an exception passes up through, the table
tells us, explicitly and efficiently, what needs to be done to restore
execution context and tear down the stack.
Here's how to unwind from each block of code:
1) grab the PC value from stack slot 1, decrease the stack by 1
2) grab the PC value from stack slot 1, decrease the stack by 2
3) restore r1 from stack slot 2, grab the PC from stack slot 1,
decrease the stack slot by 2
4) grab the PC value from stack slot 1, decrease the stack by 1
5) grab the PC value from stack slot 1, decrease the stack by 2
6) restore r2 from stack slot 2, grab the PC from stack slot 1,
decrease the stack slot by 2
7) grab the PC value from stack slot 1, decrease the stack by 1
So we can see that this example, which was given by you to show a
function which would be difficult and/or inefficient to use with EH
tables, can actually use EH tables just fine, with no changes to the
calling conventions for the normal return case, and an unwinding case
which has similar complexity to a normal return. No "sophisticated
techniques" are required, and no performance problems are
demonstrated.
If you still think there's a problem, perhaps you could construct a
detailed example showing what the problem is, but more variations on
the "function with a switch statement" theme aren't really going to
add anything.
Bob
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: hyrosen@mail.com (Hyman Rosen)
Date: Wed, 22 Sep 2004 16:22:50 GMT Raw View
===================================== MODERATOR'S COMMENT:
This has changed from a discussion about standards-related issues to a
discussion of how to implement C++ exceptions in a production compiler.
That's an interesting and important question, but it's probably best
suited to another newsgroup. (comp.compilers, perhaps?)
===================================== END OF MODERATOR'S COMMENT
Sergey P. Derevyago wrote:
> I'm trying to point out that the need for unwinding does affect the normal
> return case: some optimizations become inappropriate. That is "the normal
> code" can be made more optimal but the additional support for EH will degrade
> the cumulative performance of the whole application.
But you have completely failed to *demonstrate* this. You keep insisting
that it's true, people show you why it's not, and you seem to utterly
ignore them and keep right on insisting. I suggest you take the time to
try and understand why it is that everyone disagrees with you. You may
learn something.
> IMHO the description of the "table" approach should be somehow changed
> to reflect the fact that implementation has to dynamically maintain certain
> datastructures too.
But it *doesn't*. You have been shown this over and over again.
> Currently, the text implicitly suggests that the "code" approach uses
> dynamic datastructures while the readonly tables are enough for the "table"
> approach. But it's not the case.
It *is* the case. God, this is frustrating. I know you're not a troll, but
I swear, it feels like I'm talking to one. The EH tables are completely static
(and may involve snippets of code as well as data). Anything saved in stack slots
is done so as part of the normal course of execution, and is then used by the
stack unwinding code. The code works in the same way that it would if EH were not
present.
> IMHO it's not the case: PC + read-only tables + register values (+ ...) are
> required.
The register values are those that are already present in the non-exceptional case,
and occupy the same positions, and are stacked or not as the non-exceptional code
requires. There is no extra code or lack of optimization in the non-exceptional code
as an expense of the EH code.
> My point is that in the case of highly optimized code the right way to go is
> to save the number of already created objects on the stack. An implementation
> has to go this way due to the performance reasons.
Why? It's certainly free to do so if it truly is optimal, but it just needs
to consider the non-exceptional case. The EH code can retrieve the counts easily
no matter where they are. This is not some extremely clever hack - it's a simple
consequnce that the PC, indexing a static table, gives you all the information you
need to locate the count.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: non-existent@iobox.com ("Sergey P. Derevyago")
Date: Wed, 15 Sep 2004 02:59:58 GMT Raw View
David Abrahams wrote:
> > But without EH, an implementation does _not_ have to store this counter in a
> > well-known place (typically, in the well-known slot of stack frame) every time
> > it calls the constructor:
>
> You have misunderstood the mechanism. There doesn't have to be a
> "well-known slot of stack frame".
>
Yes, it does: stack unwinding requires "well-knowness" while recursion
requires stack.
> The register -- the alternative to
> a stack slot -- will be pushed onto the stack as part of the calling
> convention of the function that constructs the UDT. Or if it isn't,
> it will be pushed later, in the UDT's constructor. Either way, it can
> be recovered using statically-generated exception tables.
>
> It's easy to see that the information *has* to be recoverable, because
> it is needed back in f() for the construction of the UDT, so when
> control returns there it must be back where it belongs.
>
It's the callee's job to save the caller's registers. For example, no-op
callees have no need to save the registers at all.
But the need for stack unwinding substantially changes the picture...
--
With all respect, Sergey. http://ders.angen.net/
mailto : ders at skeptik.net
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: Hyman Rosen <hyrosen@mail.com>
Date: Wed, 15 Sep 2004 16:09:39 CST Raw View
Sergey P. Derevyago wrote:
> Yes, it does: stack unwinding requires "well-knowness" while recursion
> requires stack.
If the program had not thrown an exception and the recursive
call returned, then the program would have "unwound" that call
statck normally, doing whatever is appropriate. That "whatever"
is static, and based on the PC, and can thus be statically
encoded in the EH tables. Then EH stack unwinding does the same
thing. Nothing requires "well-knownness" or hinders optimization.
> It's the callee's job to save the caller's registers. For example, no-op
> callees have no need to save the registers at all.
> But the need for stack unwinding substantially changes the picture...
No it does not. If the callee doesn't need to save a register,
then unwinding, either in the normal or the EH case, doesn't
need to restore it.
You keep making statements that everyone else insists are untrue,
and you have received endless explanations of why they are untrue.
It's time for you to demonstrate your arguments with actual code,
becuase right now, you are only convincing people that you are
incapable of understanding.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: dave@boost-consulting.com (David Abrahams)
Date: Wed, 15 Sep 2004 17:35:48 GMT Raw View
non-existent@iobox.com ("Sergey P. Derevyago") writes:
> David Abrahams wrote:
>> > But without EH, an implementation does _not_ have to store this counter in a
>> > well-known place (typically, in the well-known slot of stack frame) every time
>> > it calls the constructor:
>>
>> You have misunderstood the mechanism. There doesn't have to be a
>> "well-known slot of stack frame".
>>
> Yes, it does: stack unwinding requires "well-knowness" while recursion
> requires stack.
Well, several people have tried hard and patiently to explain to you
why there's no difference. I believe my previous message gave as
close to a informal proof as you're likely to get in any discussion on
this list. You're obviously committed to your point of view that the
rest of the world is wrong about this, so I'm going to stop trying. I
will leave you with this: you've claimed the publication of the
committee, Dietmar, Hyman, and I are all wrong. It seems to me the
burden of proof should be on you to prove it. Until you do, I request
that you stop posting unsubstantiated claims about the committee's
report and its accuracy.
>> The register -- the alternative to
>> a stack slot -- will be pushed onto the stack as part of the calling
>> convention of the function that constructs the UDT. Or if it isn't,
>> it will be pushed later, in the UDT's constructor. Either way, it can
>> be recovered using statically-generated exception tables.
>>
>> It's easy to see that the information *has* to be recoverable, because
>> it is needed back in f() for the construction of the UDT, so when
>> control returns there it must be back where it belongs.
>>
> It's the callee's job to save the caller's registers.
Whose job it is depends on the calling convention of the compiler.
> For example, no-op callees have no need to save the registers at
> all.
Right.
> But the need for stack unwinding substantially changes the
> picture...
Not at all. The information required to recover the counter is not
lost. It can't be lost, since the program has to use it later.
--
Dave Abrahams
Boost Consulting
http://www.boost-consulting.com
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: Bart.van.Ingen.Schenau@ict.nl (Bart van Ingen Schenau)
Date: Thu, 16 Sep 2004 15:10:06 GMT Raw View
On Tue, 14 Sep 2004 15:13:48 GMT, non-existent@iobox.com ("Sergey P.
Derevyago") wrote:
>Hyman Rosen wrote:
>> > But without EH, an implementation does _not_ have to store this counter in a
>> > well-known place (typically, in the well-known slot of stack frame) every time
>> > it calls the constructor:
>>
>> Somehow you seem to keep missing the point. At any place where
>> the array is being constructed, by definition the count is
>> *somewhere*.
>>
> The point is that without EH an implementation is free to place it almost
>everywhere so the certain optimizations become possible.
> While in the presence of EH the number of already constructed objects must be
>at the place where the runtime can found it during the stack unwinding.
And independent of EH, the counter must be at a place where it can be
found after the constructor for the N-th array element finishes, to
see if there is an (N+1)-th element waiting to be constructed.
As the counter has to be available in the non-failure case anyway, the
EH mechanism can just record where the array-construction code keeps
this counter.
Bart v Ingen Schenau
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: non-existent@iobox.com ("Sergey P. Derevyago")
Date: Thu, 16 Sep 2004 15:13:28 GMT Raw View
Hyman Rosen wrote:
> > The point is that without EH an implementation is free to place it almost
> > everywhere so the certain optimizations become possible.
> > While in the presence of EH the number of already constructed objects must be
> > at the place where the runtime can found it during the stack unwinding.
>
> No, you are just not understanding. With EH, the implementation
> can still put the count anywhere it wants to. It is not restricted
> at all.
>
The point is that it's the callee's job to save the caller's registers.
Consider the following code:
void f()
{
A a[5];
g();
}
A non-EH implementation might look like this:
void f()
{
{reserve some space for the 5 objects of A}
register R1={address of the reserved space}
for (register R2=0; R2<5; R2++) {
register R3=R1+R2*sizeof(A);
// point_1
{call A::A() to construct an object at [R3] address}
// point_2
}
The implementation of f() doesn't save R1 and R2 registers, it just expects
to find their values unchanged after the call to A::A(). I.e.
R2@point_1==R2@point_2.
But this approach doesn't work if A::A() can throw exceptions. The runtime
must know R2 value to figure out how many As have been created.
But some implementation of A::A() can choose not to modify R2 so the counter
lives in R2.
The second implementation of A::A() can save R2 on its stack so the counter
lives on the A::A()'s stack frame.
The third implementation of A::A() can _dynamically_ call A::h1() or A::h2().
Some of the A::h-es can leave R2 untouched the others can choose to save it at
any arbitrary memory location...
How the runtime can determine the counter value in the most general case? The
answer is: it keeps its value at the well-known stack slot. It has to.
> The EH tables simply say, for a given PC, where that count
> can be found. The runtime can always find the count during unwinding.
> Really, you've been told this over and over again. I think it's time
> for you to show some code which you think cannot work in the presence
> of EH instead of just arguing abstractly, and then we can show you
> explicitly how you're wrong.
>
OK, please do it.
--
With all respect, Sergey. http://ders.angen.net/
mailto : ders at skeptik.net
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: Michael.Karcher@writeme.com (Michael Karcher)
Date: Thu, 16 Sep 2004 23:10:27 GMT Raw View
"Sergey P. Derevyago" <non-existent@iobox.com> wrote:
> A non-EH implementation might look like this:
> void f()
> {
> {reserve some space for the 5 objects of A}
> register R1={address of the reserved space}
> for (register R2=0; R2<5; R2++) {
> register R3=R1+R2*sizeof(A);
> // point_1
> {call A::A() to construct an object at [R3] address}
> // point_2
> }
> The implementation of f() doesn't save R1 and R2 registers, it just
> expects to find their values unchanged after the call to A::A(). I.e.
> R2@point_1==R2@point_2.
Correct.
> [...] if A::A() can throw exceptions. The runtime must know R2 value to
> figure out how many As have been created.
Yes.
> But some implementation of A::A() can choose not to modify R2 so the
> counter lives in R2.
Yes. Let's call this implementation A1::A1()
> The second implementation of A::A() can save R2 on its stack so the counter
> lives on the A::A()'s stack frame.
Yeah, why not. Let's call this implementation A2::A2()
> The third implementation of A::A() can _dynamically_ call A::h1() or
> A::h2(). Some of the A::h-es can leave R2 untouched the others can choose
> to save it at any arbitrary memory location...
Perfectly possible.
> How the runtime can determine the counter value in the most general case?
If the exception gets thrown from A1::A1(), PC points to A1::A1(). The
exception table for this value of PC tells the runtime to leave R2 alone.
If the exception gets thrown from A2::A2(), PC does not point to A1::A1()
but to A2::A2(), so a different EH table is used. This table tells the
runtime to restore R2 from the stack frame.
Your third implementation won't throw directly, but from A::h1() or A::h2().
Each of these functions has it's own table telling the runtime what to do
with R2.
> The answer is: it keeps its value at the well-known stack slot.
This is one possible answer. Another valid answer is: When the unwinding
process reaches the function block of f(), it already has used the different
EH tables from the matching implementation of A::A(), so at this point, the
count *is* in R2, even if R2 at the moment the exception was thrown did
contain some very different value.
So, the idea is to not describe a place where the count can *always* be
found, this would really have to be a well-known stack place, but just a
place where the count can be found during unwinding the stack. And if
unwinding completed up to the point where the count is needed, the count is
always in the correct register.
> It has to.
No, as explained above.
Michael Karcher
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: non-existent@iobox.com ("Sergey P. Derevyago")
Date: Fri, 17 Sep 2004 06:14:39 GMT Raw View
David Abrahams wrote:
> > But the need for stack unwinding substantially changes the
> > picture...
>
> Not at all. The information required to recover the counter is not
> lost. It can't be lost, since the program has to use it later.
>
"Can't be lost" doesn't mean "can be effectively determined by the runtime
during the stack unwinding".
For example:
1. Function f() creates an array of A.
2. Function f() uses register R2 to track the number of the already created
objects.
3. Generally speaking, the following call chains are possible:
3.1. f() calls A::A() {A::A() doesn't save R2} ...
3.2. f() calls A::A() {A::A() does save R2 and use it in a way it needs}
calls h1() ...
3.3. f() calls A::A() calls h1() calls h2() {h2() does save R2 and uses it
in a way it needs} calls h3() calls A::A() calls h4() {h4() does save R2 and
uses it in a way it needs} calls h5() ...
4. Moreover, at the compile time there is no way to figure out what call chain
will be spawned by A::A().
Having the following recursive call chain
{f}->{A::A}->{h1}->...->{hN}->{exception!}, where the R2 value is placed?
--
With all respect, Sergey. http://ders.angen.net/
mailto : ders at skeptik.net
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: non-existent@iobox.com ("Sergey P. Derevyago")
Date: Fri, 17 Sep 2004 13:58:31 GMT Raw View
Michael Karcher wrote:
> > How the runtime can determine the counter value in the most general case?
> >
> If the exception gets thrown from A1::A1(), PC points to A1::A1(). The
> exception table for this value of PC tells the runtime to leave R2 alone.
>
> If the exception gets thrown from A2::A2(), PC does not point to A1::A1()
> but to A2::A2(), so a different EH table is used. This table tells the
> runtime to restore R2 from the stack frame.
>
> Your third implementation won't throw directly, but from A::h1() or A::h2().
> Each of these functions has it's own table telling the runtime what to do
> with R2.
>
Please consider the following function:
void h()
{
switch (some_opaque_runtime_condition) {
case 1: { don't modify R2;
call h() on some opaque runtime condition;
return; }
case 2: { save R2 on the stack; modify R2;
call h() on some opaque runtime condition;
restore R2; return; }
case 3: { save R2 in R3; modify R2;
call h() on some opaque runtime condition;
restore R2; return; }
case 4: { throw some_exception; }
}
}
and the following call chain:
f->A::A->h->h->h->...->h->some_exception
1 2 3 N
During the stack unwinding the runtime needs the R2 value in order to figure
out how many A objects have been created in f().
1. This value is stored somewhere between h1 and hN.
2. N in virtually unlimited.
That is the reason why EH-enabled implementations have to store the number of
already created array members on the well-known stack slot (usually, at the
f()'s stack frame).
> So, the idea is to not describe a place where the count can *always* be
> found, this would really have to be a well-known stack place, but just a
> place where the count can be found during unwinding the stack.
>
Well, the R2 value can *always* be found somewhere between h1 and hN. But the
implementation that really tracks down register values via virtually unlimited
inspection of the stack frames would be unacceptably inefficient.
--
With all respect, Sergey. http://ders.angen.net/
mailto : ders at skeptik.net
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: dave@boost-consulting.com (David Abrahams)
Date: Sat, 18 Sep 2004 00:02:23 GMT Raw View
non-existent@iobox.com ("Sergey P. Derevyago") writes:
> Please consider the following function:
>
> void h()
> {
> switch (some_opaque_runtime_condition) {
>
> case 1: { don't modify R2;
> call h() on some opaque runtime condition;
> return; }
>
> case 2: { save R2 on the stack; modify R2;
> call h() on some opaque runtime condition;
> restore R2; return; }
>
> case 3: { save R2 in R3; modify R2;
> call h() on some opaque runtime condition;
> restore R2; return; }
>
> case 4: { throw some_exception; }
> }
> }
>
> and the following call chain:
>
> f->A::A->h->h->h->...->h->some_exception
> 1 2 3 N
>
> During the stack unwinding the runtime needs the R2 value in order
> to figure out how many A objects have been created in f().
>
> 1. This value is stored somewhere between h1 and hN.
> 2. N in virtually unlimited.
In each of those different cases, the return address on the stack will
point into a different region of h's code. For each region, the
tables identify how to recover r2. Looking at return addresses is
part of stack unwinding. This is my last post on the topic.
--
Dave Abrahams
Boost Consulting
http://www.boost-consulting.com
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: Michael.Karcher@writeme.com (Michael Karcher)
Date: Sat, 18 Sep 2004 00:02:43 GMT Raw View
"Sergey P. Derevyago" <non-existent@iobox.com> wrote:
>> So, the idea is to not describe a place where the count can *always* be
>> found, this would really have to be a well-known stack place, but just a
>> place where the count can be found during unwinding the stack.
> Well, the R2 value can *always* be found somewhere between h1 and hN. But the
> implementation that really tracks down register values via virtually unlimited
> inspection of the stack frames would be unacceptably inefficient.
The implementation already has to unwind the stack frame by frame to find
out from where hN, h(N-1), ..., h1 have been called. So the complexity is
already of the order N. Restoring further registers contents (besides PC)
during unwinding of course slows the process down, but just by a constant
factor of perhaps four. As this price has to be paid only in the case of a
thrown exception, it is usually unimportant.
Michael Karcher
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: brangdon@cix.co.uk (Dave Harris)
Date: Sat, 18 Sep 2004 00:03:28 GMT Raw View
non-existent@iobox.com ("Sergey P. Derevyago") wrote (abridged):
> The point is that it's the callee's job to save the caller's
> registers.
Yes. And, in effect, it's also the callee's job to restore them. It has to
restore them during a normal return, so it can restore them during stack
unwinding too.
I am wondering if you are thinking of exception handling in terms of a
longjmp that goes directly from the point of throw to the first stack
frame that has some exception-specific work to do, skipping over all the
intermediate stack frames. Then the handler looks around to figure out
what to do. If so, you're right: it has trouble because the information it
needs is in a lower-down stack frame which was thrown away by the longjmp.
However, think instead of exception handling which visits each stack frame
in turn, whether or not there is anything to do there. In each frame it
does the equivalent of a procedure return, restoring all registers to how
they were before that procedure was called. Only local knowledge is needed
to do this. It is the same knowledge needed to return normally (but
probably encoded differently, not as executable instructions). So now when
we reach a stack frame where there is some real unwinding work to do, we
have all the information we need safely restored to the places we expect.
Do you see that this is possible? Unwinding might be slower than with the
longjmp approach, it does lots of unnecessary work, and it might need tons
of static data, but we can live with that.
(If it helps, think of each stack frame as being an object, with its own
state and vtable for responding to abstract messages. Each stack frame is
responsible for unwinding itself. Stack frames don't care what other
stack frames do.)
-- Dave Harris, Nottingham, UK
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: belvis@pacbell.net (Bob Bell)
Date: Sat, 18 Sep 2004 00:05:15 GMT Raw View
non-existent@iobox.com ("Sergey P. Derevyago") wrote in message news:<414ACCCB.6706F044@iobox.com>...
> Please consider the following function:
>
> void h()
> {
> switch (some_opaque_runtime_condition) {
>
> case 1: { don't modify R2;
> call h() on some opaque runtime condition;
> return; }
>
> case 2: { save R2 on the stack; modify R2;
> call h() on some opaque runtime condition;
> restore R2; return; }
>
> case 3: { save R2 in R3; modify R2;
> call h() on some opaque runtime condition;
> restore R2; return; }
>
> case 4: { throw some_exception; }
> }
> }
>
> and the following call chain:
>
> f->A::A->h->h->h->...->h->some_exception
> 1 2 3 N
>
> During the stack unwinding the runtime needs the R2 value in order to figure
> out how many A objects have been created in f().
> 1. This value is stored somewhere between h1 and hN.
> 2. N in virtually unlimited.
>
> That is the reason why EH-enabled implementations have to store the number of
> already created array members on the well-known stack slot (usually, at the
> f()'s stack frame).
I'll take a crack at this one.
I'm going to reformat your example a bit and divide the code into
blocks. I'm even going to add a little, to mutate r2 after each
instance of it being saved just to make it interesting:
void h()
{
1 -----------------
switch (/* ... */) {
case 1:
h();
return;
case 2:
save r2 in stack slot 1
2 -----------------
r2 = random value
h();
restore r2 from stack slot 1
3 -----------------
return;
case 3:
move r2 to r3;
4 -----------------
r2 = r6;
h();
move r3 to r2;
5 -----------------
return;
case 4:
throw something();
}
}
Each block requires a different strategy for restoring r2. Here's what
the EH tables say about each block and r2:
PC is in block r2 is saved in
----------------------------------
1 r2
2 stack slot 1
3 r2
4 r3
5 r2
Here's f:
void f()
{
A a[5];
}
We are supposing that f calls A::A() for each object in the a array,
where it uses r2 as an index to keep track of which one it is
constructing. We are further supposing that h() above is called from
A::A().
Here's the call stack:
f -> A::A -> h -> h -> ... -> h -> throw
The question, as I understand it, is "when control transfers from the
throw point back up to f(), how will the runtime know how many A's
have been constructed?" In other words, how will the correct value of
r2 be restored?
Let's take a particular calling sequence example and see what happens
to R2. Assume that the first call to h falls into the first switch
case, the second call falls into the second case, etc. So our call
stack looks like:
f -> A::A -> h-1 -> h-2 -> h-3 -> h-4 (throw)
Let's look at what happened on the way down from f to the throw point:
f() uses r2 as a loop counter as it calls A::A(). For the sake of
argument, let's say r2 = 2 (we're constructing the third A object).
A::A() executes. Let's assume that it doesn't affect r2 or save it
anywhere. A::A() calls h-1.
h-1 executes the first switch case, which doesn't affect r2 or save it
anywhere, and calls h-2.
h-2 executes the second switch case, which saves r2 in stack slot 1 of
h-2's stack frame, and then sets r2 to a random value. Let's say it
sets it to 0. Then h-2 calls h-3.
h-3 executes the third switch case, which copies r2 to r3, then copies
r6 into r2. Let's say that r6 had the value -1, so now r2 contains -1.
Then h-3 calls h-4.
h-4 executes the fourth switch case, doesn't affect r2 or save it
anywhere, and throws an exception.
Here's what the stack looks like at this point:
f():
slots 1-n: storage for the array of A objects
A::A():
slot 1: PC for return in f()
h-1:
slot 1: <unused>
slot 2: PC for return in A::A()
h-2:
slot 1: 2
slot 2: PC for return in h-1
h-3:
slot 1: <unused>
slot 2: PC for return in h-2
h-4:
slot 1: <unused>
slot 2: PC for return in h-3
Our registers now look like this:
r2: -1
r3: 0
Now let's walk back:
At the throw point, inside h-4, the PC is in block 5 of the EH table.
When we are in block 5, restoring r2 is a no-op, so nothing happens to
r2. We then go up a level to the caller, h-3, by taking the PC value
from this invocation's stack frame..
At the PC location for h-3, we are in block 4 of the EH table. In
block 4, restoring r2 means copying from r3. R3 is 0, so we copy that
to r2; now r2 == 0. Then we go up a level to the caller, h-2, by
taking the PC value from this invocation's stack frame.
At the PC location for h-2, we are in block 2 of the EH table. When
the PC is in block 2, restoring r2 means copying from stack slot 1 of
this invocation's stack frame. Stack slot 1 for h-2 contains the value
2, so now r2 == 2. Up we go again, this time to h-1, by taking the PC
value from this invocation's stack frame.
At the PC location for h-1, we are in block 1 of the EH table. When
the PC is in block 1, restoring r2 is a no-op. Up one more level into
A::A().
In A::A(), restoring r2 is a no-op, so we do nothing. Then we go up
one level to f().
In f(), we now see that r2 has the value it had when we originally
called A::A(): 2, so it again contains the index of the object that we
were constructing, so we know which objects need to be destroyed.
It should be obvious that this works because at each boundary between
function invocations we consult the EH table, which essentially tells
us to do the same thing that a normal return would do to restore r2.
It should also be obvious that it doesn't matter how deeply h()
recurses, nor does it matter which switch cases are taken.
No matter what a normal return would do, table-driven unwinding can do
the same thing.
Bob
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: non-existent@iobox.com ("Sergey P. Derevyago")
Date: Mon, 13 Sep 2004 18:23:56 GMT Raw View
Dietmar Kuehl wrote:
> This statement is correct. However, it is utterly irrelevant , too:
>
No, it is not.
The "2.4.1.2 The Table Approach" section describes the method using the
following sentence:
Typical implementations using this approach will generate read-only tables for
determining the current execution context, locating catch clauses and tracking
objects needing destruction.
The point is that in the presence of arrays of UDT the "...read-only
tables..." subphrase leads to confusion.
> during array construction, the implementation has a count of the
> currently constructed array element available anyway.
>
But without EH, an implementation does _not_ have to store this counter in a
well-known place (typically, in the well-known slot of stack frame) every time
it calls the constructor:
class UDT { /* ... */ };
void f()
{
UDT array[5];
// ...
}
UDT::UDT()
{
if (some_opaque_condition)
f(); // recursion!
// ...
}
> This can be used to destroy the correct number of array elements.
--
With all respect, Sergey. http://ders.angen.net/
mailto : ders at skeptik.net
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: hyrosen@mail.com (Hyman Rosen)
Date: Mon, 13 Sep 2004 19:50:20 GMT Raw View
Sergey P. Derevyago wrote:
> But without EH, an implementation does _not_ have to store this counter in a
> well-known place (typically, in the well-known slot of stack frame) every time
> it calls the constructor:
Somehow you seem to keep missing the point. At any place where
the array is being constructed, by definition the count is
*somewhere*. The EH information simply records where that is,
for each value of the PC where an exception can occur. Recursion
is completely irrelevant, since every recursive invocation will
have the count in the same place for the same PC value. And the
count does *not* have to be in a well-known place. It could be in
a different register or memory location for each array element,
and it still would not matter.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: dave@boost-consulting.com (David Abrahams)
Date: Mon, 13 Sep 2004 19:50:32 GMT Raw View
non-existent@iobox.com ("Sergey P. Derevyago") writes:
>> during array construction, the implementation has a count of the
>> currently constructed array element available anyway.
>>
> But without EH, an implementation does _not_ have to store this counter in a
> well-known place (typically, in the well-known slot of stack frame) every time
> it calls the constructor:
You have misunderstood the mechanism. There doesn't have to be a
"well-known slot of stack frame". The register -- the alternative to
a stack slot -- will be pushed onto the stack as part of the calling
convention of the function that constructs the UDT. Or if it isn't,
it will be pushed later, in the UDT's constructor. Either way, it can
be recovered using statically-generated exception tables.
It's easy to see that the information *has* to be recoverable, because
it is needed back in f() for the construction of the UDT, so when
control returns there it must be back where it belongs. All the
information needed to restore the register at the point of the throw
is in the stack, the current registers, and some statically generated
data known as "object code." Just replace "throw whatever" with
"return whatever-else" and it's easy to see that.
There _are_ cases where a value in a register can be known to be
unneeded at the point of a function call, so the program could avoid
putting it on the stack at all, but this isn't one of those. However,
even in those cases, if you were trying to take the same error
recovery measures in some other way than using exceptions, you'd still
have to recover the information in that register -- no fair comparing
the EH case with the ignore-errors case. This is a simple matter of
information management.
--
Dave Abrahams
Boost Consulting
http://www.boost-consulting.com
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: dietmar_kuehl@yahoo.com (Dietmar Kuehl)
Date: Mon, 13 Sep 2004 20:09:55 GMT Raw View
Sergey P. Derevyago wrote:
> The point is that in the presence of arrays of UDT the "...read-only
> tables..." subphrase leads to confusion.
It doesn't confuse me - and the tables are read-only, even if other
context information is also used. ... and the tables do what the
sentence states: it determines the current execution context. To
proceed correctly, other context information like e.g. the stack
frame, the context of registers, etc. is, of course, also used.
> But without EH, an implementation does _not_ have to store this counter in
> a well-known place (typically, in the well-known slot of stack frame)
> every time it calls the constructor:
Why should this be any different with exceptions? The contract between
the constructor and the call site is no different: upon return from
the constructor, the count has to be stored wherever the implementation
decided to store the count, be it in a register, a stack variable, a
heap location, an external file or whatever. If an exception is thrown,
the tables tell the implementation where the current execution context
is and what needs to be restored. This applies to objects to be
destructed as well as other context information like the arrays count.
--
<mailto:dietmar_kuehl@yahoo.com> <http://www.dietmar-kuehl.de/>
<http://www.contendix.com> - Software Development & Consulting
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: non-existent@iobox.com ("Sergey P. Derevyago")
Date: Tue, 14 Sep 2004 15:13:48 GMT Raw View
Hyman Rosen wrote:
> > But without EH, an implementation does _not_ have to store this counter in a
> > well-known place (typically, in the well-known slot of stack frame) every time
> > it calls the constructor:
>
> Somehow you seem to keep missing the point. At any place where
> the array is being constructed, by definition the count is
> *somewhere*.
>
The point is that without EH an implementation is free to place it almost
everywhere so the certain optimizations become possible.
While in the presence of EH the number of already constructed objects must be
at the place where the runtime can found it during the stack unwinding.
> The EH information simply records where that is,
> for each value of the PC where an exception can occur. Recursion
> is completely irrelevant, since every recursive invocation will
> have the count in the same place for the same PC value.
>
Recursion means that one counter per array per function is not enough.
> And the count does *not* have to be in a well-known place.
>
Yes, it does: the runtime must be able to find it during the stack unwinding.
No unwinding -- no well-known place is needed.
--
With all respect, Sergey. http://ders.angen.net/
mailto : ders at skeptik.net
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: hyrosen@mail.com (Hyman Rosen)
Date: Tue, 14 Sep 2004 18:00:54 GMT Raw View
Sergey P. Derevyago wrote:
> The point is that without EH an implementation is free to place it almost
> everywhere so the certain optimizations become possible.
> While in the presence of EH the number of already constructed objects must be
> at the place where the runtime can found it during the stack unwinding.
No, you are just not understanding. With EH, the implementation
can still put the count anywhere it wants to. It is not restricted
at all. The EH tables simply say, for a given PC, where that count
can be found. The runtime can always find the count during unwinding.
Really, you've been told this over and over again. I think it's time
for you to show some code which you think cannot work in the presence
of EH instead of just arguing abstractly, and then we can show you
explicitly how you're wrong.
> Recursion means that one counter per array per function is not enough.
Any counters which are in the middle of being used but are
temporarily suspended because of a recursive call are saved
*somewhere*. They will be restored by the stack unwinding.
Meanwhile, the single active one will be used to destruct the
partially destructed array.
>>And the count does *not* have to be in a well-known place.
>
> Yes, it does: the runtime must be able to find it during the stack unwinding.
> No unwinding -- no well-known place is needed.
No well-known place is needed at all. No matter how obscure
the place is, the EH tables just say where it is.
I don't know why you ar having such difficulty grasping this
simple fact - if the straight-line non-exception code has
access to the value (and for array construction, it must), then
the EH code has access to the same value.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: non-existent@iobox.com ("Sergey P. Derevyago")
Date: Tue, 14 Sep 2004 18:23:30 GMT Raw View
Dietmar Kuehl wrote:
> > The point is that in the presence of arrays of UDT the "...read-only
> > tables..." subphrase leads to confusion.
>
> It doesn't confuse me - and the tables are read-only, even if other
> context information is also used. ... and the tables do what the
> sentence states: it determines the current execution context. To
> proceed correctly, other context information like e.g. the stack
> frame, the context of registers, etc. is, of course, also used.
>
IMHO the description of the "table" approach should be somehow changed to
reflect the fact that implementation has to dynamically maintain certain
datastructures too.
Currently, the text implicitly suggests that the "code" approach uses dynamic
datastructures while the readonly tables are enough for the "table" approach.
But it's not the case.
That's why the wording looks confusing to me.
--
With all respect, Sergey. http://ders.angen.net/
mailto : ders at skeptik.net
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: hyrosen@mail.com (Hyman Rosen)
Date: Tue, 14 Sep 2004 18:44:44 GMT Raw View
Sergey P. Derevyago wrote:
> IMHO the description of the "table" approach should be somehow changed to
> reflect the fact that implementation has to dynamically maintain certain
> datastructures too.
But these structures aren't maintained for EH, they're just part
of the normal way the code works even without EH.
> Currently, the text implicitly suggests that the "code" approach uses dynamic
> datastructures while the readonly tables are enough for the "table" approach.
> But it's not the case.
It is the case. The tables are raed-only, and they don't require
any dynamic data sructures that aren't already being used by the
non-EH code.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: non-existent@iobox.com ("Sergey P. Derevyago")
Date: Sat, 11 Sep 2004 00:12:11 GMT Raw View
David Abrahams wrote:
> One *could* interpret the fact that several committee members
> responded to your post and refuted your arguments as an indication
> that you were wrong about this,
>
The statement is:
When an unhandled exception occurs during construction of an array of objects
the PC value is not sufficient to determine how many elements that have been
constructed.
Disprove, please.
--
With all respect, Sergey. http://ders.angen.net/
mailto : ders at skeptik.net
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: llewelly.at@xmission.dot.com (llewelly)
Date: Sun, 12 Sep 2004 00:02:58 GMT Raw View
fpilhofe@mc.com (Frank Pilhofer) writes:
[snip]
> Some argue that this alternative must be preserved, to allow targeting
> embedded systems, where exceptions may be considered "too expensive."
> One figure that was quoted was that gcc-compiled code was three times
> as big when exceptions were enabled.
I think three times is an exageration, or an extreme case, but on
platforms where gcc uses a 'shadow stack' or 'setjmp-longjmp'
implementation, I have seen code size substantially increase when
exceptions are enabled.
Regardless of actual performance, there are C++ compilers for
embedded platforms which support almost every other C++ feature,
but don't support exceptions or rtti at all.
I've encountered plenty in the embedded systems community which
will refuse to enable exceptions no matter what numbers are
cited. IMO, if you offer no alternative to exceptions, you are
bound to lose some portion of that community (but not all of it).
But it would be madness to evaluate exception-handling solely on
performance alone; it is a great boon to anyone who needs
non-local error-handling (and that is just about everyone that
uses modularity, or layers of indirection to solve problems), and
it is becoming vital to many C++ styles and idioms. So, if you
don't leverage exception-handling, you're bound to lose some
portion of the C++ community. (But exception-handling isn't the
most important thing in the world; an interface that was both
type-safe and highly flexible would win more C++ aficiandos than
one that wasn't but leveraged exception-handling, I think.)
In the long run, I think preference for exception-handling will
dominate; it's growing, and that's where the future lies. Even
now, a C++ api which does not use exception-handling will seem
dated anywhere outside certian communities like the embedded
community. So if you can't provide both, go with
exception-handling.
>
> Now that number would indeed be of concern, if it was true. The source
> was not able to provide concrete figures, or substantiate the claim.
> Yet, based on his experience in the embedded marketplace, I hesitate
> to dismiss the statement as FUD.
>
> Also, if it were true, it might be a gcc implementation issue. Maybe
> they made some bad choices in implementing exception-enabled code.
For some platforms, they did. gcc has two different exception
handling implementations - one is called setjmp-longjmp, and the
other is called dwarf2-eh or table-driven. setjmp-longjmp has the
advantage of being much easier to port to new platforms, but the
disadvantage of substantial performance and code size
penalties. The alternative dwarf2-eh has almost no performance or
code-size penalties, and only light data size penalties. But it
is much harder to port. So the quality exception implementation
is used on only a few of the many platforms gcc supports.
>
> So I'd like to ask if anybody here had some real numbers, scientific
> (i.e., published) or not -- or academic research on whether and why
> exceptions are "cheap" or not.
The trouble is that the truth is somewhere in the middle - the
technology for implementing exceptions cheaply has been known for
almost a decade (if not longer), but it is more difficult to
implement, and switching to it breaks backwards compatibility(0). So
the technology is out there, but it isn't implemented
everywhere. There are widely used compilers - such as MS VC++
2003, I believe - which have quite expensive implementations of
exception-handling, and others which have quite cheap
implementations.
Whatever research you find is going to be quite sensitive to
environmental considerations, in ways which will surprise you. For
example, if you had two C++ compilers, built from the same source
code, targeting the same archeticture, and differing only in
the target operating system, you would expect the quality of
their exception-handling implementations would not vary much,
right? But gcc 3.3.4 on x86-linux has an implementation of
exception-handling, which is so cheap as to be nearly free (in
terms of performance and code-size cost), while the same gcc
3.3.4 on x86 windows has a frightfully expensive implementation
of exception-handling.
[snip]
> I expect the difference to be small, but would like some numbers to
> substantiate my arguments with.
[snip]
Sorry I don't have any real numbers for you ...
(0) However, exception-handling isn't the source of the myriad gcc
backwards compat issues. It could become one, but for whatever
reason(s), on most platforms, gcc either moved to dwarf2-eh
with gcc 3.0, or stayed with setjmp-longjmp .
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: dietmar_kuehl@yahoo.com (Dietmar Kuehl)
Date: Sun, 12 Sep 2004 16:51:20 GMT Raw View
Sergey P. Derevyago wrote:
> The statement is:
>
> When an unhandled exception occurs during construction of an array of
> objects the PC value is not sufficient to determine how many elements that
> have been constructed.
>
> Disprove, please.
This statement is correct. However, it is utterly irrelevant, too:
during array construction, the implementation has a count of the
currently constructed array element available anyway. This can be
used to destroy the correct number of array elements.
--
<mailto:dietmar_kuehl@yahoo.com> <http://www.dietmar-kuehl.de/>
<http://www.contendix.com> - Software Development & Consulting
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: dave@boost-consulting.com (David Abrahams)
Date: Sun, 12 Sep 2004 20:17:15 GMT Raw View
non-existent@iobox.com ("Sergey P. Derevyago") writes:
> David Abrahams wrote:
>> One *could* interpret the fact that several committee members
>> responded to your post and refuted your arguments as an indication
>> that you were wrong about this,
>>
> The statement is:
>
> When an unhandled exception occurs during construction of an array of objects
> the PC value is not sufficient to determine how many elements that have been
> constructed.
>
> Disprove, please.
Well of course, it's the PC, plus the stack pointer and the contents
of the stack. Already disproved in the thread you cited, the fact
that you won't accept the proof notwithstanding.
--
Dave Abrahams
Boost Consulting
http://www.boost-consulting.com
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: dave@boost-consulting.com (David Abrahams)
Date: Thu, 9 Sep 2004 00:23:46 GMT Raw View
fpilhofe@mc.com (Frank Pilhofer) writes:
> So I'd like to ask if anybody here had some real numbers, scientific
> (i.e., published) or not -- or academic research on whether and why
> exceptions are "cheap" or not.
>
> I guess one test would be to take a large project that currently uses
> exceptions, to modify it to use a different means of error reporting
> between function calls, and then to compile each with different
> compilers. (A more extreme comparison would be against code that
> assert(0)ed in case of failure, i.e., assuming that there would be no
> exceptions.)
>
> I expect the difference to be small, but would like some numbers to
> substantiate my arguments with.
I don't think anyone's done a comprehensive survey. Results will
probably vary widely between compiler implementations. Even different
versions of GCC use different EH strategies. Furthermore, no vendors
that I know of have really gone all out to minimize the size of
generated EH information. It could be made quite a lot smaller (at
some cost in unwinding speed).
--
Dave Abrahams
Boost Consulting
http://www.boost-consulting.com
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: dave@boost-consulting.com (David Abrahams)
Date: Thu, 9 Sep 2004 08:01:33 GMT Raw View
dietmar_kuehl@yahoo.com ("dietmar_kuehl@yahoo.com") writes:
> Frank Pilhofer wrote:
>> I am looking for research on the "footprint" of C++ exceptions, i.e.,
>> the impact of using C++ exceptions on code size and run-time.
>
> You might want to have a look at the performance TR produced by the
> C++ committee:
> <http://www.open-std.org/jtc1/sc22/wg21/docs/PDTR18015.pdf>
>
>> I realize that this is a silly question to ask in this newsgroup.
>
> I don't think so.
>
>> I am involved in the CORBA standardization process at the Object
>> Management Group (OMG). I recently participated in a session where
>> we discussed a possible new C++ language mapping, and tossed around
>> ideas for features, compromises and tradeoffs.
>
> If the OMG embarks on creating a new C++ binding for CORBA, can you
> please let the C++ community know? There is loads of interest in this
> area from the C++ side and I think that it would be both for the
> benefit of CORBA and C++ if the two standardization groups liaise for
> the creation of a new binding.
Yes, please! Thanks for suggesting that, Dietmar!
--
Dave Abrahams
Boost Consulting
http://www.boost-consulting.com
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: non-existent@iobox.com ("Sergey P. Derevyago")
Date: Thu, 9 Sep 2004 15:27:06 GMT Raw View
> You might want to have a look at the performance TR produced by the
> C++ committee:
> <http://www.open-std.org/jtc1/sc22/wg21/docs/PDTR18015.pdf>
>
Unfortunately, the report has missed the "array of UDT" case in its "The
Table Approach" EH-related section. It seems like the committee doesn't listen
to the NG because this issue has already been discussed long ago:
http://groups.google.com/groups?as_umsgid=3D0E5E16.51F109C@iobox.com
The point is that PC value is _not_ sufficient to track the number of
currently created objects during the array construction. This issue is a
showstopper for "read-only" table-driven EH.
--
With all respect, Sergey. http://ders.angen.net/
mailto : ders at skeptik.net
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: fpilhofe@mc.com (Frank Pilhofer)
Date: Thu, 9 Sep 2004 17:47:47 GMT Raw View
dietmar_kuehl@yahoo.com ("dietmar_kuehl@yahoo.com") wrote in message news:<chn39h$kpi@odbk17.prod.google.com>...
> Frank Pilhofer wrote:
> > I am looking for research on the "footprint" of C++ exceptions, i.e.,
> > the impact of using C++ exceptions on code size and run-time.
>
> You might want to have a look at the performance TR produced by the
> C++ committee:
> <http://www.open-std.org/jtc1/sc22/wg21/docs/PDTR18015.pdf>
>
Thank you, Dieter and Geert, for this link. That document is a great
resource, not just for discussing the overhead of exceptions. The only
values it mentions in this area is a "6% speed impact" for the "code"
approach, and a "15% code and data space impact" for the "table"
approach.
I realize that measuring such numbers is not easy, and probably not
many compiler vendors want to disclose their implementation
strategies.
The 15% size overhead is much smaller than the 200% overhead that was
quoted at the meeting, so my question is answered.
>
> If the OMG embarks on creating a new C++ binding for CORBA, can you
> please let the C++ community know? There is loads of interest in this
> area from the C++ side and I think that it would be both for the
> benefit of CORBA and C++ if the two standardization groups liaise for
> the creation of a new binding.
>
I will keep you informed. At the moment, there is no more than
preliminary discussion. It will get more interesting once an RFP
(Request for Proposals), asking for a new mapping, is published.
Updating the language mapping is a hard sell. While the current one is
agreeably far from perfect, and users keep complaining, it works, and
vendors hesitate to replace the result of 10 years of patching the
current one, existing training material, and accumulated experience,
with something new.
So if anything happens, then probably not before well into next year.
If it does, then it will be great to get input from the C++ community.
Thanks,
Frank
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: nesotto@cs.auc.dk ("Thorsten Ottosen")
Date: Thu, 9 Sep 2004 18:47:10 GMT Raw View
""Sergey P. Derevyago"" <non-existent@iobox.com> wrote in message
news:41401620.4E3BF71B@iobox.com...
| > You might want to have a look at the performance TR produced by the
| > C++ committee:
| > <http://www.open-std.org/jtc1/sc22/wg21/docs/PDTR18015.pdf>
| >
| Unfortunately, the report has missed the "array of UDT" case in its "The
| Table Approach" EH-related section. It seems like the committee doesn't
listen
| to the NG because this issue has already been discussed long ago:
| http://groups.google.com/groups?as_umsgid=3D0E5E16.51F109C@iobox.com
|
| The point is that PC value is _not_ sufficient to track the number of
| currently created objects during the array construction. This issue is a
| showstopper for "read-only" table-driven EH.
Or yet another reason not to use built-in arrays in C++.
br
Thorsten
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: dave@boost-consulting.com (David Abrahams)
Date: Thu, 9 Sep 2004 18:47:56 GMT Raw View
non-existent@iobox.com ("Sergey P. Derevyago") writes:
>> You might want to have a look at the performance TR produced by the
>> C++ committee:
>> <http://www.open-std.org/jtc1/sc22/wg21/docs/PDTR18015.pdf>
>>
> Unfortunately, the report has missed the "array of UDT" case in its "The
> Table Approach" EH-related section. It seems like the committee doesn't listen
> to the NG because this issue has already been discussed long ago:
> http://groups.google.com/groups?as_umsgid=3D0E5E16.51F109C@iobox.com
>
> The point is that PC value is _not_ sufficient to track the number of
> currently created objects during the array construction. This issue is a
> showstopper for "read-only" table-driven EH.
One *could* interpret the fact that several committee members
responded to your post and refuted your arguments as an indication
that you were wrong about this, and not that the committee doesn't
listen to the NG.
--
Dave Abrahams
Boost Consulting
http://www.boost-consulting.com
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: fpilhofe@mc.com (Frank Pilhofer)
Date: Tue, 7 Sep 2004 22:31:11 GMT Raw View
Hi,
I am looking for research on the "footprint" of C++ exceptions, i.e.,
the impact of using C++ exceptions on code size and run-time.
I realize that this is a silly question to ask in this newsgroup.
Allow me to put the above question in context.
I am involved in the CORBA standardization process at the Object
Management Group (OMG). I recently participated in a session where
we discussed a possible new C++ language mapping, and tossed around
ideas for features, compromises and tradeoffs.
One subject that was controversial was the usage of C++ exceptions.
The current CORBA C++ language mapping allows for implementations to
use an additional "out" parameter to indicate exceptions, as an
alternative to using "native" exceptions (i.e., throw).
Some argue that this alternative must be preserved, to allow targeting
embedded systems, where exceptions may be considered "too expensive."
One figure that was quoted was that gcc-compiled code was three times
as big when exceptions were enabled.
Now that number would indeed be of concern, if it was true. The source
was not able to provide concrete figures, or substantiate the claim.
Yet, based on his experience in the embedded marketplace, I hesitate
to dismiss the statement as FUD.
Also, if it were true, it might be a gcc implementation issue. Maybe
they made some bad choices in implementing exception-enabled code.
So I'd like to ask if anybody here had some real numbers, scientific
(i.e., published) or not -- or academic research on whether and why
exceptions are "cheap" or not.
I guess one test would be to take a large project that currently uses
exceptions, to modify it to use a different means of error reporting
between function calls, and then to compile each with different
compilers. (A more extreme comparison would be against code that
assert(0)ed in case of failure, i.e., assuming that there would be no
exceptions.)
I expect the difference to be small, but would like some numbers to
substantiate my arguments with.
Thanks,
Frank
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: geert@cs.uu.nl ("G.J. Giezeman")
Date: Wed, 8 Sep 2004 07:57:45 GMT Raw View
Frank Pilhofer wrote:
> Hi,
>
> I am looking for research on the "footprint" of C++ exceptions, i.e.,
> the impact of using C++ exceptions on code size and run-time.
> So I'd like to ask if anybody here had some real numbers, scientific
> (i.e., published) or not -- or academic research on whether and why
> exceptions are "cheap" or not.
The Performance Technical Report, which can be found on
http://www.open-std.org/jtc1/sc22/wg21/
discusses overhead (space and time) of various implementation techniques
of exception handling.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: dietmar_kuehl@yahoo.com ("dietmar_kuehl@yahoo.com")
Date: Wed, 8 Sep 2004 17:55:21 GMT Raw View
Frank Pilhofer wrote:
> I am looking for research on the "footprint" of C++ exceptions, i.e.,
> the impact of using C++ exceptions on code size and run-time.
You might want to have a look at the performance TR produced by the
C++ committee:
<http://www.open-std.org/jtc1/sc22/wg21/docs/PDTR18015.pdf>
> I realize that this is a silly question to ask in this newsgroup.
I don't think so.
> I am involved in the CORBA standardization process at the Object
> Management Group (OMG). I recently participated in a session where
> we discussed a possible new C++ language mapping, and tossed around
> ideas for features, compromises and tradeoffs.
If the OMG embarks on creating a new C++ binding for CORBA, can you
please let the C++ community know? There is loads of interest in this
area from the C++ side and I think that it would be both for the
benefit of CORBA and C++ if the two standardization groups liaise for
the creation of a new binding.
--
<mailto:dietmar_kuehl@yahoo.com> <http://www.dietmar-kuehl.de/>
<http://www.contendix.com> - Software Development & Consulting
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]