Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.c > #15193 > unrolled thread
| Started by | James Harris <james.harris.1@googlemail.com> |
|---|---|
| First post | 2011-12-29 09:52 -0800 |
| Last post | 2012-02-17 22:38 +0000 |
| Articles | 20 on this page of 73 — 20 participants |
Back to article view | Back to comp.lang.c
Exception propagation in C James Harris <james.harris.1@googlemail.com> - 2011-12-29 09:52 -0800
Re: Exception propagation in C jacob navia <jacob@spamsink.net> - 2011-12-29 19:40 +0100
Re: Exception propagation in C BGB <cr88192@hotmail.com> - 2011-12-29 14:03 -0600
Re: Exception propagation in C BGB <cr88192@hotmail.com> - 2011-12-29 13:07 -0600
Re: Exception propagation in C James Harris <james.harris.1@googlemail.com> - 2011-12-30 13:38 -0800
Re: Exception propagation in C BGB <cr88192@hotmail.com> - 2011-12-30 20:24 -0600
Re: Exception propagation in C James Harris <james.harris.1@googlemail.com> - 2012-01-03 02:19 -0800
Re: Exception propagation in C BGB <cr88192@hotmail.com> - 2012-01-03 14:04 -0600
Re: Exception propagation in C James Harris <james.harris.1@googlemail.com> - 2012-01-04 04:26 -0800
Re: Exception propagation in C BGB <cr88192@hotmail.com> - 2012-01-06 14:05 -0600
Re: Exception propagation in C jacob navia <jacob@spamsink.net> - 2012-01-06 23:54 +0100
Re: Exception propagation in C "Ed Coran" <sysdev@new.net> - 2012-01-06 22:24 -0600
Re: Exception propagation in C jacob navia <jacob@spamsink.net> - 2012-01-07 09:15 +0100
Re: Exception propagation in C "Ed Coran" <sysdev@new.net> - 2012-01-08 23:45 -0600
Re: Exception propagation in C James Harris <james.harris.1@googlemail.com> - 2012-01-08 12:52 -0800
Re: Exception propagation in C James Harris <james.harris.1@googlemail.com> - 2012-01-08 14:20 -0800
Re: Exception propagation in C jacob navia <jacob@spamsink.net> - 2012-01-09 00:19 +0100
Re: Exception propagation in C James Harris <james.harris.1@googlemail.com> - 2012-01-08 21:12 -0800
Re: Exception propagation in C Scott Wood <scott@buserror.net> - 2012-01-08 23:41 -0600
Re: Exception propagation in C James Harris <james.harris.1@googlemail.com> - 2012-01-09 11:00 -0800
Re: Exception propagation in C Scott Wood <scott@buserror.net> - 2012-01-10 00:42 -0600
Re: Exception propagation in C jgk@panix.com (Joe keane) - 2012-01-11 07:40 +0000
Re: Exception propagation in C James Harris <james.harris.1@googlemail.com> - 2012-01-11 12:59 -0800
Re: Exception propagation in C "Ed Coran" <sysdev@new.net> - 2012-01-09 00:05 -0600
Re: Exception propagation in C Ian Collins <ian-news@hotmail.com> - 2012-01-09 20:40 +1300
Re: Exception propagation in C "Ed Coran" <sysdev@new.net> - 2012-01-09 04:53 -0600
Re: Exception propagation in C 88888 Dihedral <dihedral88888@googlemail.com> - 2012-01-09 05:49 -0800
Re: Exception propagation in C Scott Wood <scott@buserror.net> - 2012-01-08 17:29 -0600
Re: Exception propagation in C jacob navia <jacob@spamsink.net> - 2012-01-09 00:37 +0100
Re: Exception propagation in C Nomen Nescio <nobody@dizum.com> - 2012-01-09 11:36 +0100
Re: Exception propagation in C James Harris <james.harris.1@googlemail.com> - 2012-01-09 11:08 -0800
Re: Exception propagation in C Scott Wood <scott@buserror.net> - 2012-01-05 01:25 -0600
Re: Exception propagation in C James Harris <james.harris.1@googlemail.com> - 2012-01-05 09:18 -0800
Re: Exception propagation in C "s_dubrovich@yahoo.com" <s_dubrovich@yahoo.com> - 2011-12-30 21:20 -0800
Re: Exception propagation in C "Rod Pemberton" <do_not_have@noavailemail.cmm> - 2011-12-31 06:19 -0500
Re: Exception propagation in C Nick Keighley <nick_keighley_nospam@hotmail.com> - 2012-01-01 10:32 -0800
Re: Exception propagation in C "Rod Pemberton" <do_not_have@noavailemail.cmm> - 2011-12-31 05:53 -0500
Re: Exception propagation in C James Harris <james.harris.1@googlemail.com> - 2012-01-01 16:30 -0800
Re: Exception propagation in C "Rod Pemberton" <do_not_have@noavailemail.cmm> - 2012-01-02 01:50 -0500
Re: Exception propagation in C Scott Wood <scott@buserror.net> - 2012-01-02 02:03 -0600
Re: Exception propagation in C "Rod Pemberton" <do_not_have@noavailemail.cmm> - 2012-01-02 15:59 -0500
Re: Exception propagation in C James Harris <james.harris.1@googlemail.com> - 2012-01-03 04:12 -0800
Re: Exception propagation in C Scott Wood <scott@buserror.net> - 2012-01-04 23:53 -0600
Re: Exception propagation in C "Rod Pemberton" <do_not_have@noavailemail.cmm> - 2012-01-05 06:45 -0500
Re: Exception propagation in C James Harris <james.harris.1@googlemail.com> - 2012-01-03 04:16 -0800
Re: Exception propagation in C Keith Thompson <kst-u@mib.org> - 2012-01-03 11:50 -0800
Re: Exception propagation in C Jens Gustedt <jens.gustedt@loria.fr> - 2011-12-31 13:40 +0100
Re: Exception propagation in C BGB <cr88192@hotmail.com> - 2011-12-31 10:40 -0600
Re: Exception propagation in C Jens Gustedt <jens.gustedt@loria.fr> - 2012-01-02 16:49 +0100
Re: Exception propagation in C BGB <cr88192@hotmail.com> - 2012-01-02 13:02 -0600
Re: Exception propagation in C Dr Nick <3-nospam@temporary-address.org.uk> - 2012-01-02 19:53 +0000
Re: Exception propagation in C ImpalerCore <jadill33@gmail.com> - 2012-01-02 13:10 -0800
Re: Exception propagation in C ImpalerCore <jadill33@gmail.com> - 2012-01-02 12:58 -0800
Re: Exception propagation in C Jens Gustedt <jens.gustedt@loria.fr> - 2012-01-04 18:01 +0100
Re: Exception propagation in C BGB <cr88192@hotmail.com> - 2012-01-06 14:38 -0600
Re: Exception propagation in C "Rod Pemberton" <do_not_have@noavailemail.cmm> - 2012-01-06 21:40 -0500
Re: Exception propagation in C Kaz Kylheku <kaz@kylheku.com> - 2011-12-29 21:02 +0000
Re: Exception propagation in C jacob navia <jacob@spamsink.net> - 2011-12-30 10:38 +0100
Re: Exception propagation in C James Harris <james.harris.1@googlemail.com> - 2011-12-30 14:38 -0800
Re: Exception propagation in C "Rod Pemberton" <do_not_have@noavailemail.cmm> - 2011-12-29 18:37 -0500
Re: Exception propagation in C "BartC" <bc@freeuk.com> - 2011-12-30 01:07 +0000
Re: Exception propagation in C James Harris <james.harris.1@googlemail.com> - 2011-12-30 14:07 -0800
Re: Exception propagation in C Eric Sosman <esosman@ieee-dot-org.invalid> - 2011-12-30 17:53 -0500
Re: Exception propagation in C James Harris <james.harris.1@googlemail.com> - 2012-01-03 03:56 -0800
Re: Exception propagation in C jgk@panix.com (Joe keane) - 2012-01-02 09:49 +0000
Re: Exception propagation in C Kleuske <kleuske@nowhere.net> - 2012-01-02 10:51 +0000
Re: Exception propagation in C "Rod Pemberton" <do_not_have@noavailemail.cmm> - 2012-01-02 16:01 -0500
Re: Exception propagation in C Kleuske <kleuske@nowhere.net> - 2012-01-03 11:14 +0000
Re: Exception propagation in C James Harris <james.harris.1@googlemail.com> - 2012-01-03 03:59 -0800
Re: Exception propagation in C Kleuske <kleuske@nowhere.net> - 2012-01-03 12:09 +0000
Re: Exception propagation in C jgk@panix.com (Joe keane) - 2012-01-03 21:25 +0000
Re: Exception propagation in C James Harris <james.harris.1@googlemail.com> - 2012-02-15 14:24 -0800
Re: Exception propagation in C jgk@panix.com (Joe keane) - 2012-02-17 22:38 +0000
Page 1 of 4 [1] 2 3 4 Next page →
| From | James Harris <james.harris.1@googlemail.com> |
|---|---|
| Date | 2011-12-29 09:52 -0800 |
| Subject | Exception propagation in C |
| Message-ID | <99fbedd4-b37f-4ced-8759-4ba9656259dc@m7g2000vbc.googlegroups.com> |
Below is a suggestion for handling - more accurately, propagating - exceptions in C. It is intended for overall program performance though it goes against a preference of many including me. Can anyone come up with a faster or better way to propagate exceptions? What other ways are there to handle exceptions in C? What do you do? (I tend to detect exceptions where they occur by checking return values but I have never had a good way in C to get them to propagate.) The issue is the desire to have exception propagation using *standard* C. I'm not talking principally about detecting exceptions but of a way for nested functions to signal an exception and have that exception propagate back along the call chain to a function that is set up to handle it. * Option 1. Use setjmp/longjmp. Not too good as, to be compatible, they need to be used in an if or a switch statement IIRC which can be inconvenient. Don't they also need a separate jump buffer for each active setjmp? I have used them in the past but found them awkward. * Option 2. Traditional exception handling. As used in other languages this unwinds the stack of function calls looking for a handler. I cannot think of a way to implement that in C. Furthermore, it might be slow for two reasons: 1) the need to push and pop sufficient context for the exception handler to recognise where a given exception should be caught, 2) the consequent loss of sync between the stack and the CPU's branch prediction for returns. (A return would go back to somewhere other than where it was called from potentially impacting the performance of the rest of the calls on the call stack.) The latter is OK if exceptions are infrequent but is not generally applicable. * Option 3. The function detecting an exception creates an exception object and sets an exception type in a thread-specific variable. Any calling functions, after each call (or group of calls) test that variable. If non-zero they jump to a local exception handler if there is one or jump to a return from the current function if not. This does involve a test and branch for each call but that is very fast (fusible and single cycle) and it avoids the need for exception handling context to be pushed and popped. It would also preserve the branch prediction of calls and returns. Unfortunately option 3 would mean use of goto. I haven't used goto in C code for many years and don't like the idea of starting now but I can't think of anything quicker. In C this could be temp = func(); if (thread_excep) goto exception_handler; x = temp; In x86-32 this should become something like cmp [thread_excep], 0 jne exception_handler mov x, eax If set up to do so the exception handler would handle the exception itself and clear thread_excep. If not or if it decided to propagate the exception it would return to its caller with the thread_excep word still holding the exception value. Or it could alter thread_excep and return. There are subtleties but option 3 is the basic idea. Anyone know of something better? James
[toc] | [next] | [standalone]
| From | jacob navia <jacob@spamsink.net> |
|---|---|
| Date | 2011-12-29 19:40 +0100 |
| Message-ID | <jdic66$17l$1@speranza.aioe.org> |
| In reply to | #15193 |
Le 29/12/11 18:52, James Harris a écrit :
> * Option 2. Traditional exception handling. As used in other languages
> this unwinds the stack of function calls looking for a handler. I
> cannot think of a way to implement that in C.
This can be done in C of course. Look at the exception handling code in
gcc. It implements an abstract machine that gets opcodes from a series
of tables generated by the compiler.
For more information look at the DWARF debugging info specs, where
the stack unwinding machine is described in detail. These are the
specs, not to be confused with the implementation of those specs
by gcc (the only compiler that uses this monster) that are
slightly different.
You should also look at the code of gcc. Specifically look in the
"except.c" file and in the unwind-xx-fde files. There you will
find the implementation
> Furthermore, it might be
> slow for two reasons: 1) the need to push and pop sufficient context
> for the exception handler to recognise where a given exception should
> be caught,
That is the exception handling object. But before you go any further,
think a bit:
SPEED IS NOT IMPORTANT HERE
The program has just crashed, and most often than not this is NOT the
regular path of the application. So, stop worrying about speed now.
2) the consequent loss of sync between the stack and the
> CPU's branch prediction for returns.
So what? The pipeline will be flushed, but that is not important at all
since (I repeat) SPEED IS NOT IMPORTANT HERE.
> (A return would go back to
> somewhere other than where it was called from potentially impacting
> the performance of the rest of the calls on the call stack.) The
> latter is OK if exceptions are infrequent but is not generally
> applicable.
>
Look. This is a quite researched field, and several full
implementations exist. Before starting spewing ideas that
are nothing more than a few hours of thought
1) Read the literature and documentations of the existing
implementations. Do NOT think you will run into the
GREAT idea that all others didn't see.
2) Study the code of gcc and the open implementations of the
exception handling implementations published by Microsoft and
Apple. (Apple is mostly gcc anyway).
3) Choose one model implementation and implement that. See how
it works within your context.
If you are writing an OS forget about C and remember that your OS
should support several languages smoothly, not only just C.
[toc] | [prev] | [next] | [standalone]
| From | BGB <cr88192@hotmail.com> |
|---|---|
| Date | 2011-12-29 14:03 -0600 |
| Message-ID | <jdih2d$7ci$1@news.albasani.net> |
| In reply to | #15196 |
On 12/29/2011 12:40 PM, jacob navia wrote: > Le 29/12/11 18:52, James Harris a écrit : >> * Option 2. Traditional exception handling. As used in other languages >> this unwinds the stack of function calls looking for a handler. I >> cannot think of a way to implement that in C. > > This can be done in C of course. Look at the exception handling code in > gcc. It implements an abstract machine that gets opcodes from a series > of tables generated by the compiler. > > For more information look at the DWARF debugging info specs, where > the stack unwinding machine is described in detail. These are the > specs, not to be confused with the implementation of those specs > by gcc (the only compiler that uses this monster) that are > slightly different. > > You should also look at the code of gcc. Specifically look in the > "except.c" file and in the unwind-xx-fde files. There you will > find the implementation > simpler is, of course, to do it more like Win32 SEH, which basically just captures the state (stack position, registers, ...) at each handler frame (state is captured, linked into a linked-list, and unlinked on return). in this case, passing control to a frame essentially restores the state at this point, and invokes the handler logic. proper frame unwinding (like used with GCC and Win64) does work, and is a little cheaper (when no exceptions are thrown), but is a bit more complex and requires compiler support (debug-info, or, in the case of Win64, specially-formed frame prologues/epilogues and some special info located in a table within a special section). I had written some (never really used) ideas for my own code-generators, namely using a prologue/epilogue system similar to Win64 and having linked annotation metadata (exploiting the use of multi-byte NOP instructions, which can also be used to encode pointers to things). more practically though, I just stuck with a SEH-like system, since then the unwinder code does not have to know anything about how to unwind the stack or similar (or cross language/compiler boundaries), it just saves/restores whatever. >> Furthermore, it might be >> slow for two reasons: 1) the need to push and pop sufficient context >> for the exception handler to recognise where a given exception should >> be caught, > > That is the exception handling object. But before you go any further, > think a bit: > > SPEED IS NOT IMPORTANT HERE > > The program has just crashed, and most often than not this is NOT the > regular path of the application. So, stop worrying about speed now. > yep, agreed. despite some people using exceptions as a control-flow feature, this isn't really their intended or ideal use. > 2) the consequent loss of sync between the stack and the >> CPU's branch prediction for returns. > > So what? The pipeline will be flushed, but that is not important at all > since (I repeat) SPEED IS NOT IMPORTANT HERE. > I think he was expecting something like "all branch predictions for all subsequent returns will fail", which will not actually happen (given how the CPU works). it will flush the pipeline, maybe effecting several instructions and costing several clock cycles, but really?... a mispredicted "if()", a call through a function pointer, or invoking a "switch()" which uses a jump-table, all have the same sorts of costs here. >> (A return would go back to >> somewhere other than where it was called from potentially impacting >> the performance of the rest of the calls on the call stack.) The >> latter is OK if exceptions are infrequent but is not generally >> applicable. >> > > Look. This is a quite researched field, and several full > implementations exist. Before starting spewing ideas that > are nothing more than a few hours of thought > > 1) Read the literature and documentations of the existing > implementations. Do NOT think you will run into the > GREAT idea that all others didn't see. > > 2) Study the code of gcc and the open implementations of the > exception handling implementations published by Microsoft and > Apple. (Apple is mostly gcc anyway). > > 3) Choose one model implementation and implement that. See how > it works within your context. > yep. > If you are writing an OS forget about C and remember that your OS > should support several languages smoothly, not only just C. > this doesn't entirely follow. ideally, one can have an interface which works fairly well for whatever languages work on the system. however, sadly, IRL it is often not so nice: typically language-specific exceptions and OS exceptions are disjoint. say, Win32 SEH vs C++ or Java exceptions, ... typical result is then that they don't really cleanly cross language boundaries or show up in a "sane" manner (things don't necessarily unwind in a sane order, often one system is invisible to another, ...). at least on Win64, IIRC Win64 SEH / MSVC++ / .NET all use the same basic mechanism. typically, this means some amount of ugliness to deal with these cases. luckily, at least, C is sort of a "least common denominator". better would probably be a solid ABI-level mechanism (which applies to C and any other language which is compiled to native code on the OS). but, alas...
[toc] | [prev] | [next] | [standalone]
| From | BGB <cr88192@hotmail.com> |
|---|---|
| Date | 2011-12-29 13:07 -0600 |
| Message-ID | <jdidp0$v8c$1@news.albasani.net> |
| In reply to | #15193 |
On 12/29/2011 11:52 AM, James Harris wrote:
> Below is a suggestion for handling - more accurately, propagating -
> exceptions in C. It is intended for overall program performance though
> it goes against a preference of many including me. Can anyone come up
> with a faster or better way to propagate exceptions? What other ways
> are there to handle exceptions in C? What do you do? (I tend to detect
> exceptions where they occur by checking return values but I have never
> had a good way in C to get them to propagate.)
>
> The issue is the desire to have exception propagation using *standard*
> C. I'm not talking principally about detecting exceptions but of a way
> for nested functions to signal an exception and have that exception
> propagate back along the call chain to a function that is set up to
> handle it.
>
it depends on what exactly one means by "standard".
I will assume here "can work with a typical C compiler on a typical
target without the need/ability to modify said compiler to make it work".
> * Option 1. Use setjmp/longjmp. Not too good as, to be compatible,
> they need to be used in an if or a switch statement IIRC which can be
> inconvenient. Don't they also need a separate jump buffer for each
> active setjmp? I have used them in the past but found them awkward.
>
yes. however, partially useful may be the ideas/workings of
setjmp/longjmp, rather than the specific interface.
main thing it does:
captures call state;
restores call state upon invocation.
one could either wrap setjmp/longjmp, or create something similar, which
implements a nested exception handler.
> * Option 2. Traditional exception handling. As used in other languages
> this unwinds the stack of function calls looking for a handler. I
> cannot think of a way to implement that in C. Furthermore, it might be
> slow for two reasons: 1) the need to push and pop sufficient context
> for the exception handler to recognise where a given exception should
> be caught, 2) the consequent loss of sync between the stack and the
> CPU's branch prediction for returns. (A return would go back to
> somewhere other than where it was called from potentially impacting
> the performance of the rest of the calls on the call stack.) The
> latter is OK if exceptions are infrequent but is not generally
> applicable.
>
IRL, this is not likely to actually make all that big of a difference.
the CPU doesn't do any sort of large-scale code analysis, but only
really tends to predict/pipeline within a fairly small context (the next
several instructions).
after the (probably reasonably minor) cost of the non-local jump/...,
there is unlikely to be any further costs.
as for implementing in C:
one option is, of-course, to fold the try, catch, and finally (if
applicable) blocks into their own functions, and then handle the whole
thing something like:
fooTryCatchFinally(myapp_try, myapp_catch, myapp_finally);
but, this interface is awkward (I have tried it).
also possibly workable:
if(!fooBeginTry())
{
try-block stuff...
fooEndTry(); //unregister handler
}else
{
exception-handling stuff...
}
so, the "fooBeginTry()" registers the exception handler frame (similar
to a "setjmp()"), returning 0 for normal execution, and 1 if an
exception has taken place.
"fooEndTry()", of course, unregisters the exception handler frame (it is
not necessary if an exception is thrown before it is reached).
handling could be something like:
FooExceptionObject *ex;
ex=fooGetExceptionObject();
if(fooExceptionIs(ex, "MyApp_SomeException"))
{
...
}
...
finally stuff...
...
fooThrow(ex); //continue unwinding
//otherwise, one just falls out the end and continues executing
note that almost invariably one will end up either dealing with OS
specific mechanisms or ASM in terms of implementing the mechanism itself
(or faking it with "longjmp" or similar).
> * Option 3. The function detecting an exception creates an exception
> object and sets an exception type in a thread-specific variable. Any
> calling functions, after each call (or group of calls) test that
> variable. If non-zero they jump to a local exception handler if there
> is one or jump to a return from the current function if not. This does
> involve a test and branch for each call but that is very fast (fusible
> and single cycle) and it avoids the need for exception handling
> context to be pushed and popped. It would also preserve the branch
> prediction of calls and returns.
>
TLS variables are not standard C (but are supported by MSVC and GCC).
potentially though one could implement them as status-getter function
calls, which is allowed in standard C.
if(fooGetExceptionStatus())
...
> Unfortunately option 3 would mean use of goto. I haven't used goto in
> C code for many years and don't like the idea of starting now but I
> can't think of anything quicker. In C this could be
>
> temp = func();
> if (thread_excep) goto exception_handler;
> x = temp;
>
> In x86-32 this should become something like
>
> cmp [thread_excep], 0
> jne exception_handler
> mov x, eax
>
don't need goto's.
more so, assuming one had TLS variables, one can't typically access them
in a single instruction.
more typically it is some magic like:
mov ecx, [fs:magicOffset]
mov edx, [__myVariable_index]
mov eax, [ecx+edx*4]
and eax, eax
jnz ...
on some systems, they amount to an internal function call.
> If set up to do so the exception handler would handle the exception
> itself and clear thread_excep. If not or if it decided to propagate
> the exception it would return to its caller with the thread_excep word
> still holding the exception value. Or it could alter thread_excep and
> return.
>
unclear how this would work exactly.
> There are subtleties but option 3 is the basic idea. Anyone know of
> something better?
>
> James
[toc] | [prev] | [next] | [standalone]
| From | James Harris <james.harris.1@googlemail.com> |
|---|---|
| Date | 2011-12-30 13:38 -0800 |
| Message-ID | <541969d7-e4ab-43ba-8cf5-679f18eb3221@m4g2000vbc.googlegroups.com> |
| In reply to | #15200 |
On Dec 29, 7:07 pm, BGB <cr88...@hotmail.com> wrote:
> On 12/29/2011 11:52 AM, James Harris wrote:
...
> > The issue is the desire to have exception propagation using *standard*
> > C. I'm not talking principally about detecting exceptions but of a way
> > for nested functions to signal an exception and have that exception
> > propagate back along the call chain to a function that is set up to
> > handle it.
>
> it depends on what exactly one means by "standard".
> I will assume here "can work with a typical C compiler on a typical
> target without the need/ability to modify said compiler to make it work".
Yes, that's the intention.
...
> > * Option 3. The function detecting an exception creates an exception
> > object and sets an exception type in a thread-specific variable. Any
> > calling functions, after each call (or group of calls) test that
> > variable. If non-zero they jump to a local exception handler if there
> > is one or jump to a return from the current function if not. This does
> > involve a test and branch for each call but that is very fast (fusible
> > and single cycle) and it avoids the need for exception handling
> > context to be pushed and popped. It would also preserve the branch
> > prediction of calls and returns.
>
> TLS variables are not standard C (but are supported by MSVC and GCC).
> potentially though one could implement them as status-getter function
> calls, which is allowed in standard C.
I can't see TLS as C or not C. Wouldn't TLS be a characteristic of the
environment in which the C code runs?
...
> more so, assuming one had TLS variables, one can't typically access them
> in a single instruction.
I was thinking of thread_excep being at a fixed location so it could
literally be checked with one instruction:
cmp [thread_excep], 0
> more typically it is some magic like:
> mov ecx, [fs:magicOffset]
> mov edx, [__myVariable_index]
> mov eax, [ecx+edx*4]
>
> and eax, eax
> jnz ...
>
> on some systems, they amount to an internal function call.
As shown, with static linking
if (thread_excep)
should compile to
cmp [thread_excep], 0
> > If set up to do so the exception handler would handle the exception
> > itself and clear thread_excep. If not or if it decided to propagate
> > the exception it would return to its caller with the thread_excep word
> > still holding the exception value. Or it could alter thread_excep and
> > return.
>
> unclear how this would work exactly.
I'll try to explain what I have in mind. There would be a machine word
(which I've called thread_excep above) which would be initialised to
zero. A non-zero value would be set in it to indicate that an
exception had occurred. I'm not sure how best to use the word but one
option is to use different bits to indicate exception classes: data
exception, index exception, io exception, user exception etc.
In the main body of the code exceptions would be detected by
if (thread_excep) goto handler_2;
Within a function there would be as many handlers as are needed. Say
we wanted to deal with only index-type exceptions in this module. We
could code
handler_2:
if (thread_excep & EXCEP_INDEX) {
/* Deal with an index exception */
thread_excep &= ~EXCEP_INDEX;
/* Delete the exception object */
/* Next step: return, go on to next iteration, whatever */
}
else return 0;
In the else path the value of thread_excep has not been changed so
whatever called this routine will get a chance to handle the
exception(s).
Does that make sense? I'd welcome corrections or improvements.
James
[toc] | [prev] | [next] | [standalone]
| From | BGB <cr88192@hotmail.com> |
|---|---|
| Date | 2011-12-30 20:24 -0600 |
| Message-ID | <jdlrpl$bbr$1@news.albasani.net> |
| In reply to | #15233 |
On 12/30/2011 3:38 PM, James Harris wrote:
> On Dec 29, 7:07 pm, BGB<cr88...@hotmail.com> wrote:
>> On 12/29/2011 11:52 AM, James Harris wrote:
>
> ...
>
>>> The issue is the desire to have exception propagation using *standard*
>>> C. I'm not talking principally about detecting exceptions but of a way
>>> for nested functions to signal an exception and have that exception
>>> propagate back along the call chain to a function that is set up to
>>> handle it.
>>
>> it depends on what exactly one means by "standard".
>> I will assume here "can work with a typical C compiler on a typical
>> target without the need/ability to modify said compiler to make it work".
>
> Yes, that's the intention.
>
> ...
>
>>> * Option 3. The function detecting an exception creates an exception
>>> object and sets an exception type in a thread-specific variable. Any
>>> calling functions, after each call (or group of calls) test that
>>> variable. If non-zero they jump to a local exception handler if there
>>> is one or jump to a return from the current function if not. This does
>>> involve a test and branch for each call but that is very fast (fusible
>>> and single cycle) and it avoids the need for exception handling
>>> context to be pushed and popped. It would also preserve the branch
>>> prediction of calls and returns.
>>
>> TLS variables are not standard C (but are supported by MSVC and GCC).
>> potentially though one could implement them as status-getter function
>> calls, which is allowed in standard C.
>
> I can't see TLS as C or not C. Wouldn't TLS be a characteristic of the
> environment in which the C code runs?
>
> ...
>
TLS is typically stored separately from typical global variables, and is
generally indicated via the use of special modifiers ("__thread" or
"__declspec(thread)", say, in GCC or MSVC), however, doing so is not
allowed in standard C (these are extensions).
typically, the threading library will also provide function-calls to
get/set TLS variables, which are a little more standard.
>> more so, assuming one had TLS variables, one can't typically access them
>> in a single instruction.
>
> I was thinking of thread_excep being at a fixed location so it could
> literally be checked with one instruction:
>
> cmp [thread_excep], 0
>
if it is a global though, it is not a TLS.
otherwise, I guess it would need to be changed around by the scheduler
or something (register variables with scheduler, and it saves/restores
their values on context switches or something?...).
>> more typically it is some magic like:
>> mov ecx, [fs:magicOffset]
>> mov edx, [__myVariable_index]
>> mov eax, [ecx+edx*4]
>>
>> and eax, eax
>> jnz ...
>>
>> on some systems, they amount to an internal function call.
>
> As shown, with static linking
>
> if (thread_excep)
>
> should compile to
>
> cmp [thread_excep], 0
>
except if it were a proper/traditional TLS variable, which are typically
accessed indirectly (on Windows and Linux, there is a special per-thread
structure, generally accessed via a segment register).
I guess, if one is making an OS, other possibilities also exist (special
magic TLS sections?).
say, if one has a section ".tls" section, where the scheduler
automatically saves/restores the contents of anything stored within
these sections. the advantage could be very fast access to TLS variables
at the cost of potentially more expensive context switches.
>>> If set up to do so the exception handler would handle the exception
>>> itself and clear thread_excep. If not or if it decided to propagate
>>> the exception it would return to its caller with the thread_excep word
>>> still holding the exception value. Or it could alter thread_excep and
>>> return.
>>
>> unclear how this would work exactly.
>
> I'll try to explain what I have in mind. There would be a machine word
> (which I've called thread_excep above) which would be initialised to
> zero. A non-zero value would be set in it to indicate that an
> exception had occurred. I'm not sure how best to use the word but one
> option is to use different bits to indicate exception classes: data
> exception, index exception, io exception, user exception etc.
>
FOURCC?...
'IOX ', 'GPF ', 'PAGE', ...
many systems use objects though, where the object will typically hold
information, such as the location, captured register state, ...
> In the main body of the code exceptions would be detected by
>
> if (thread_excep) goto handler_2;
>
> Within a function there would be as many handlers as are needed. Say
> we wanted to deal with only index-type exceptions in this module. We
> could code
>
> handler_2:
> if (thread_excep& EXCEP_INDEX) {
> /* Deal with an index exception */
> thread_excep&= ~EXCEP_INDEX;
> /* Delete the exception object */
> /* Next step: return, go on to next iteration, whatever */
> }
> else return 0;
>
> In the else path the value of thread_excep has not been changed so
> whatever called this routine will get a chance to handle the
> exception(s).
>
> Does that make sense? I'd welcome corrections or improvements.
>
I don't think flags are the ideal strategy here.
flags don't as easily allow user-defined exceptions, and if one has only
a few bits for a user-defined exception class or similar, then it
creates a bit of a burden WRT avoiding clashes.
one "could" potentially use interned strings and magic index numbers
though, say, 10 or 12 bits being plenty to allow a range of named
exception classes (potentially being keyed via a hash-table or similar).
this is actually how my dynamic type-system works (types are identified
by name, but encoded via hash indices).
technically, one can combine indices with FOURCC values though, since if
one restricts the valid FOURCC values to being ASCII printable
characters, there is a large range of non-ASCII values which can be used
as index values (512M in the range of 0x00000000..0x1FFFFFFF, and 2G in
the range 0x80000000..0xFFFFFFFF, and possibly another 16M in the range
0x7F000000..0x7FFFFFFF).
IIRC, I once devised a TLV format based on this strategy, but I don't
remember ever making use of this (many of my TLV formats use a
variable-length encoding more like that used in the Matroska/MKV format).
or such...
[toc] | [prev] | [next] | [standalone]
| From | James Harris <james.harris.1@googlemail.com> |
|---|---|
| Date | 2012-01-03 02:19 -0800 |
| Message-ID | <98257b01-551a-48fe-8474-0e7df1988aa7@f11g2000yql.googlegroups.com> |
| In reply to | #15237 |
On Dec 31 2011, 2:24 am, BGB <cr88...@hotmail.com> wrote: > On 12/30/2011 3:38 PM, James Harris wrote: I've snipped the bits about how TLS is generally accessed. Thanks for explaining that info. It was new to me. ... > > I was thinking of thread_excep being at a fixed location so it could > > literally be checked with one instruction: > > > cmp [thread_excep], 0 > > if it is a global though, it is not a TLS. > > otherwise, I guess it would need to be changed around by the scheduler > or something (register variables with scheduler, and it saves/restores > their values on context switches or something?...). Yes. The task switcher would include code along the lines of push [thread_excep] for the outgoing task and, for the incoming task, pop [thread_excep] Since the word would be checked many times between task switches this is much faster than following a structure off FS or similar. ... > I guess, if one is making an OS, other possibilities also exist (special > magic TLS sections?). > > say, if one has a section ".tls" section, where the scheduler > automatically saves/restores the contents of anything stored within > these sections. the advantage could be very fast access to TLS variables > at the cost of potentially more expensive context switches. As thread_excep would be accessed very frequently it would be changed explicitly on a context switch. That doesn't apply to the bulk of the thread info, though, which might be wanted by the task. Most of that info would be changed simply by updating a pointer. For example, as well as there being thread_excep: resd 1 in a page that the user-mode task can update there would also be thread_data_p: resd 1 in a page which, to the task, was read-only. On a context switch both words would be updated. This keeps thread switching minimal yet allows the user-mode program easy and fast access to the kinds of things it may want info on. For example, if the program wanted its thread id it might find it at thread_data_p->id. Incidentally, these could be wrapped in getter and setter functions but, alternatively, for performance they can be linked to fixed locations. > I don't think flags are the ideal strategy here. > flags don't as easily allow user-defined exceptions, and if one has only > a few bits for a user-defined exception class or similar, then it > creates a bit of a burden WRT avoiding clashes. I suppose it depends on the normal first level of selecting exceptions. The best way might be whatever the handler uses to first distinguish them. Most code I've seen or written is interested, at the top level, in which *type* of exceptions to catch and which to ignore and it generally does that by looking at the exception type: indexerror, valueerror, computationerror etc. That coupled with the fact that multiple exceptions can be outstanding at the same time suggested the use of a bit array but it's not the only option. You mention user-defined exceptions. I planned only one bit for them (as there is an arbitrary number of them). To distinguish one user exception from another would probably require a call to a routine that examines the detail. > one "could" potentially use interned strings and magic index numbers > though, say, 10 or 12 bits being plenty to allow a range of named > exception classes (potentially being keyed via a hash-table or similar). > > this is actually how my dynamic type-system works (types are identified > by name, but encoded via hash indices). I see your point. The problem is that this allows only one exception at a time to be signalled-but-not-yet-handled. Which one? I suppose the most critical one or the first one or the last one could be chosen to be marked in the indicator word. <<END Just on this point, and bringing it back specifically to C, if TLS is hard to obtain or slow to access there are two other possibilities that I don't think have been mentioned yet that spring to mind for use in C. 1. If only running a single thread simply use a global. Job done. 2. Use errno. Set it to zero at the start and check it where appropriate. On a user-detected exception that does not already set errno set it to a value which is outside the normal range (and create or append to the detailed exception object, as before). Using errno does only allow one exception to be indicated at a time but that's the same as what I understand your suggestion to be. It also doesn't work between languages (unless they obtain the address of errno) but I think it could work well for C. END At the end of the day, any scheme could be used. For performance the idea is that the exception-indicating word is either zero or non-zero. If it's non-zero one or more exceptions has/have occurred and must be dealt with. James
[toc] | [prev] | [next] | [standalone]
| From | BGB <cr88192@hotmail.com> |
|---|---|
| Date | 2012-01-03 14:04 -0600 |
| Message-ID | <jdvn0l$bet$1@news.albasani.net> |
| In reply to | #15358 |
On 1/3/2012 4:19 AM, James Harris wrote: > On Dec 31 2011, 2:24 am, BGB<cr88...@hotmail.com> wrote: >> On 12/30/2011 3:38 PM, James Harris wrote: > > I've snipped the bits about how TLS is generally accessed. Thanks for > explaining that info. It was new to me. > > ... > >>> I was thinking of thread_excep being at a fixed location so it could >>> literally be checked with one instruction: >> >>> cmp [thread_excep], 0 >> >> if it is a global though, it is not a TLS. >> >> otherwise, I guess it would need to be changed around by the scheduler >> or something (register variables with scheduler, and it saves/restores >> their values on context switches or something?...). > > Yes. The task switcher would include code along the lines of > > push [thread_excep] > > for the outgoing task and, for the incoming task, > > pop [thread_excep] > > Since the word would be checked many times between task switches this > is much faster than following a structure off FS or similar. > possibly, however the issue then partly becomes: how does the task switcher know where the variable is?... possible options: do it like the BIOS, with every task having a dedicated kernel area which is also accessible to the userspace process; the shared-object or DLL holding the variable is "owned" by the OS/kernel (the kernel then depends on the presence of the library and on the location of the variable within the library). > ... > >> I guess, if one is making an OS, other possibilities also exist (special >> magic TLS sections?). >> >> say, if one has a section ".tls" section, where the scheduler >> automatically saves/restores the contents of anything stored within >> these sections. the advantage could be very fast access to TLS variables >> at the cost of potentially more expensive context switches. > > As thread_excep would be accessed very frequently it would be changed > explicitly on a context switch. That doesn't apply to the bulk of the > thread info, though, which might be wanted by the task. Most of that > info would be changed simply by updating a pointer. For example, as > well as there being > > thread_excep: resd 1 > > in a page that the user-mode task can update there would also be > > thread_data_p: resd 1 > > in a page which, to the task, was read-only. On a context switch both > words would be updated. This keeps thread switching minimal yet allows > the user-mode program easy and fast access to the kinds of things it > may want info on. For example, if the program wanted its thread id it > might find it at thread_data_p->id. > > Incidentally, these could be wrapped in getter and setter functions > but, alternatively, for performance they can be linked to fixed > locations. > FWIW: if one uses PE/COFF DLLs, typically all accesses to imported global variables is indirect (and requires explicit declaration); if one uses ELF, potentially nearly all access to global variables is indirect (the typical strategy is to access them via the GOT except in certain special cases). also, segment overrides are fairly cheap. the cheapest option then would probably be to include whatever magic state directly into the TEB/TIB/whatever, then be like: mov eax, [fs:address] in some cases, accessing a variable this way could in-fact be potentially cheaper than accessing a true global. x86-64 is a little better, since the CPU defaults to relative addressing, but is messed up some by ELF and the SysV/AMD64 ABI by demanding that all access to globals still be via the GOT. the slight advantage though is that one doesn't need dllimport/dllexport annotations to import/export functions and variables, but overall I prefer PE/COFF DLLs more. back when I was developing an OS (so long ago), I also used PE/COFF. a possible idea (for an alternative to both a GOT and the traditional DLL imports, if developing a custom format for binaries, or just tweaking PE/COFF) could be the use of a relocation table for any imports (potentially in a semi-compressed form). the main issue is mostly on x86-64, how to best deal with the 2GB window issue for global variables if using this strategy (besides just requiring all libraries to be in the low 2GB, or using indirect addressing if the compiler and/or linker can't determine if the variable will be within the 2GB window). this would probably be mostly applicable in the case where one is reusing an existing compiler but writing a custom linker, and probably while using a non-ELF object format (such as COFF). possible nifty features one could add in such a case: fat-binaries, partial late-binding (sadly, non-trivial cases would require compiler support), ... >> I don't think flags are the ideal strategy here. >> flags don't as easily allow user-defined exceptions, and if one has only >> a few bits for a user-defined exception class or similar, then it >> creates a bit of a burden WRT avoiding clashes. > > I suppose it depends on the normal first level of selecting > exceptions. The best way might be whatever the handler uses to first > distinguish them. Most code I've seen or written is interested, at the > top level, in which *type* of exceptions to catch and which to ignore > and it generally does that by looking at the exception type: > indexerror, valueerror, computationerror etc. That coupled with the > fact that multiple exceptions can be outstanding at the same time > suggested the use of a bit array but it's not the only option. > > You mention user-defined exceptions. I planned only one bit for them > (as there is an arbitrary number of them). To distinguish one user > exception from another would probably require a call to a routine that > examines the detail. > probably ok for a single app, but is dubious for an OS or general mechanism. >> one "could" potentially use interned strings and magic index numbers >> though, say, 10 or 12 bits being plenty to allow a range of named >> exception classes (potentially being keyed via a hash-table or similar). >> >> this is actually how my dynamic type-system works (types are identified >> by name, but encoded via hash indices). > > I see your point. The problem is that this allows only one exception > at a time to be signalled-but-not-yet-handled. Which one? I suppose > the most critical one or the first one or the last one could be chosen > to be marked in the indicator word. > errm... typical exception mechanisms only allow a single exception at a time. as soon as an exception is thrown, it is handled immediately. no delays or queuing are allowed. if an exception occurs within a handler, this may either throw the new exception (the prior one is forgotten), or simply kill the app. Windows does this: if something goes wrong and an exception can't be thrown in-app, Windows will simply kill the process. if something goes wrong in-kernel, it is a blue-screen / BSOD. likewise for the CPU: if an exception occurs within an interrupt handler, this is a "doublefault" and generally leads to a reboot. an exception is not a status code... > <<END > Just on this point, and bringing it back specifically to C, if TLS is > hard to obtain or slow to access there are two other possibilities > that I don't think have been mentioned yet that spring to mind for use > in C. > it is slow, yes, but typically not enough to care about. for most things people don't worry about a few clock-cycles here and there (generally, it is big algorithm-level stuff that kills performance, and not a lack of sufficient micro-optimization). > 1. If only running a single thread simply use a global. Job done. > except when globals are slower. > 2. Use errno. Set it to zero at the start and check it where > appropriate. On a user-detected exception that does not already set > errno set it to a value which is outside the normal range (and create > or append to the detailed exception object, as before). > FWIW, depending on the C-library, errno may be in-fact a function call wrapped in a macro. > Using errno does only allow one exception to be indicated at a time > but that's the same as what I understand your suggestion to be. It > also doesn't work between languages (unless they obtain the address of > errno) but I think it could work well for C. > END > > At the end of the day, any scheme could be used. For performance the > idea is that the exception-indicating word is either zero or non-zero. > If it's non-zero one or more exceptions has/have occurred and must be > dealt with. > except that "exceptions as a status word" aren't really "exceptions" in the traditional sense. may as well just call them status-codes, and leave the whole mess of exception-handling out of this.
[toc] | [prev] | [next] | [standalone]
| From | James Harris <james.harris.1@googlemail.com> |
|---|---|
| Date | 2012-01-04 04:26 -0800 |
| Message-ID | <5b3c65a3-ccf7-40ce-a161-55e106a93c44@w4g2000vbc.googlegroups.com> |
| In reply to | #15382 |
On Jan 3, 8:04 pm, BGB <cr88...@hotmail.com> wrote:
> On 1/3/2012 4:19 AM, James Harris wrote:
> > On Dec 31 2011, 2:24 am, BGB<cr88...@hotmail.com> wrote:
...
> > Yes. The task switcher would include code along the lines of
>
> > push [thread_excep]
>
> > for the outgoing task and, for the incoming task,
>
> > pop [thread_excep]
>
> > Since the word would be checked many times between task switches this
> > is much faster than following a structure off FS or similar.
>
> possibly, however the issue then partly becomes:
> how does the task switcher know where the variable is?...
Isn't this easy if you have a flat address space: just put the
variable at a fixed location?
> possible options:
> do it like the BIOS, with every task having a dedicated kernel area
> which is also accessible to the userspace process;
> the shared-object or DLL holding the variable is "owned" by the
> OS/kernel (the kernel then depends on the presence of the library and on
> the location of the variable within the library).
...
> FWIW:
> if one uses PE/COFF DLLs, typically all accesses to imported global
> variables is indirect (and requires explicit declaration);
> if one uses ELF, potentially nearly all access to global variables is
> indirect (the typical strategy is to access them via the GOT except in
> certain special cases).
Does this mean that the global, G, in the following would not be
accessed directly?
int G;
void sub() {
while (G > 0) {
G -= 1;
sub();
}
}
int main(int argc, char **argv) {
G = argc;
sub();
return 0;
}
> also, segment overrides are fairly cheap.
True. It obviously depends on the CPU but in my tests, while segment
loading cost a bit, segment overrides were completely free.
> the cheapest option then would probably be to include whatever magic
> state directly into the TEB/TIB/whatever, then be like:
> mov eax, [fs:address]
>
> in some cases, accessing a variable this way could in-fact be
> potentially cheaper than accessing a true global.
As mentioned, I would expect a file-scope global to be accessed
directly - as a machine word at a location that gets filled-in by the
linker. Am I wrong? Are you are thinking of library code?
...
> > You mention user-defined exceptions. I planned only one bit for them
> > (as there is an arbitrary number of them). To distinguish one user
> > exception from another would probably require a call to a routine that
> > examines the detail.
>
> probably ok for a single app, but is dubious for an OS or general mechanism.
Why is this no good? There can be any number of user exception types
so it's not possible to have one bit for each. The best I could think
of was to have one bit to indicate a user exception and functions to
examine as much other detail as needed.
> >> one "could" potentially use interned strings and magic index numbers
> >> though, say, 10 or 12 bits being plenty to allow a range of named
> >> exception classes (potentially being keyed via a hash-table or similar).
>
> >> this is actually how my dynamic type-system works (types are identified
> >> by name, but encoded via hash indices).
>
> > I see your point. The problem is that this allows only one exception
> > at a time to be signalled-but-not-yet-handled. Which one? I suppose
> > the most critical one or the first one or the last one could be chosen
> > to be marked in the indicator word.
>
> errm... typical exception mechanisms only allow a single exception at a
> time. as soon as an exception is thrown, it is handled immediately. no
> delays or queuing are allowed. if an exception occurs within a handler,
> this may either throw the new exception (the prior one is forgotten), or
> simply kill the app.
If only one exception at a time is needed that makes things
significantly easier. But is only one needed? I was thinking of
exceptions caused in exception handlers. For example, the exception
handler divides by the number it first thought of and triggers an
overflow or it checks an array index and finds it out of bounds. What
does the system do?
It could leave the first exception in place - after all that was the
root cause. Or it could replace the exception information with its own
- after all it has an internal fault. Neither seems obviously best. It
might be best to keep both indications until both have been either
resolved or reported ... but it would be easier to handle if only one
was kept.
> Windows does this: if something goes wrong and an exception can't be
> thrown in-app, Windows will simply kill the process.
If an exception "can't be thrown"? What would prevent it being thrown?
> if something goes
> wrong in-kernel, it is a blue-screen / BSOD.
Not very good, though, is it! In reality I think that Windows or any
OS will handle almost all exceptions. A BSOD should only occur on a
hardware failure such as a machine-check exception. (It looks like
Windows does run third-party drivers as privileged this increasing its
vulnerability many-fold.)
> likewise for the CPU:
> if an exception occurs within an interrupt handler, this is a
> "doublefault" and generally leads to a reboot.
>
> an exception is not a status code...
Following your analogy of a CPU you could perhaps think of each
exception condition as an interrupt line that gets asserted until the
condition causing the interrupt has been resolved. I know, as an
analogy it's not perfect. :-(
> > <<END
> > Just on this point, and bringing it back specifically to C, if TLS is
> > hard to obtain or slow to access there are two other possibilities
> > that I don't think have been mentioned yet that spring to mind for use
> > in C.
>
> it is slow, yes, but typically not enough to care about.
> for most things people don't worry about a few clock-cycles here and
> there (generally, it is big algorithm-level stuff that kills
> performance, and not a lack of sufficient micro-optimization).
>
> > 1. If only running a single thread simply use a global. Job done.
>
> except when globals are slower.
I'd like to understand why they would be. I tried compiling the code
above with gcc and it seems to refer to G directly such as in sub()
the compiler generates
mov DWORD PTR G, %eax
> > 2. Use errno. Set it to zero at the start and check it where
> > appropriate. On a user-detected exception that does not already set
> > errno set it to a value which is outside the normal range (and create
> > or append to the detailed exception object, as before).
>
> FWIW, depending on the C-library, errno may be in-fact a function call
> wrapped in a macro.
I know. I found that in some systems there is a call which returns the
address of errno. In that case the compiler should (hopefully)
remember that address for each use in a function.
> > Using errno does only allow one exception to be indicated at a time
> > but that's the same as what I understand your suggestion to be. It
> > also doesn't work between languages (unless they obtain the address of
> > errno) but I think it could work well for C.
> > END
>
> > At the end of the day, any scheme could be used. For performance the
> > idea is that the exception-indicating word is either zero or non-zero.
> > If it's non-zero one or more exceptions has/have occurred and must be
> > dealt with.
>
> except that "exceptions as a status word" aren't really "exceptions" in
> the traditional sense.
>
> may as well just call them status-codes, and leave the whole mess of
> exception-handling out of this.
Don't forget the original intention was speed. The point of a status
word, as you call it, is to allow exception handling (or, non-
handling) quickly. It is not an end in itself but a means of adding
exceptions to C in a way that recognises exceptions in the right
places while having the lowest possible impact on overall performance.
James
[toc] | [prev] | [next] | [standalone]
| From | BGB <cr88192@hotmail.com> |
|---|---|
| Date | 2012-01-06 14:05 -0600 |
| Message-ID | <je7k70$3nf$1@news.albasani.net> |
| In reply to | #15404 |
On 1/4/2012 6:26 AM, James Harris wrote:
> On Jan 3, 8:04 pm, BGB<cr88...@hotmail.com> wrote:
>> On 1/3/2012 4:19 AM, James Harris wrote:
>>> On Dec 31 2011, 2:24 am, BGB<cr88...@hotmail.com> wrote:
>
> ...
>
>>> Yes. The task switcher would include code along the lines of
>>
>>> push [thread_excep]
>>
>>> for the outgoing task and, for the incoming task,
>>
>>> pop [thread_excep]
>>
>>> Since the word would be checked many times between task switches this
>>> is much faster than following a structure off FS or similar.
>>
>> possibly, however the issue then partly becomes:
>> how does the task switcher know where the variable is?...
>
> Isn't this easy if you have a flat address space: just put the
> variable at a fixed location?
>
if the task switcher is part of the OS kernel (it generally is), then it
does not have direct visibility of variables declared in a user-space
binary.
>> possible options:
>> do it like the BIOS, with every task having a dedicated kernel area
>> which is also accessible to the userspace process;
>> the shared-object or DLL holding the variable is "owned" by the
>> OS/kernel (the kernel then depends on the presence of the library and on
>> the location of the variable within the library).
>
> ...
>
>> FWIW:
>> if one uses PE/COFF DLLs, typically all accesses to imported global
>> variables is indirect (and requires explicit declaration);
>> if one uses ELF, potentially nearly all access to global variables is
>> indirect (the typical strategy is to access them via the GOT except in
>> certain special cases).
>
> Does this mean that the global, G, in the following would not be
> accessed directly?
>
> int G;
> void sub() {
> while (G> 0) {
> G -= 1;
> sub();
> }
> }
> int main(int argc, char **argv) {
> G = argc;
> sub();
> return 0;
> }
>
it depends on the target (CPU mode, compilation mode, and object format).
if compiling for PE/COFF (EXE or DLL), it will be accessed directly.
for ELF, in this case it will generally be accessed indirectly via the
GOT (since the compiler will not know at this point where the variable
is declared, a "common" variable could be either within the local ".bss"
segment, or potentially imported from another SO, but this is only known
to the linker).
if it were initialized or static:
static int G;
or:
int G=0;
then the compiler would know that it was defined in the same compilation
unit, in which case:
on 32-bit x86, it will depend on whether or not this is PIC code (which
will generally depend on whether or not this is a Shared-Object).
on x86-64, the CPU has RIP-relative addressing, and so will presumably
use this to access the variable (for both SO's and non-SO binaries).
>> also, segment overrides are fairly cheap.
>
> True. It obviously depends on the CPU but in my tests, while segment
> loading cost a bit, segment overrides were completely free.
>
yep.
>> the cheapest option then would probably be to include whatever magic
>> state directly into the TEB/TIB/whatever, then be like:
>> mov eax, [fs:address]
>>
>> in some cases, accessing a variable this way could in-fact be
>> potentially cheaper than accessing a true global.
>
> As mentioned, I would expect a file-scope global to be accessed
> directly - as a machine word at a location that gets filled-in by the
> linker. Am I wrong? Are you are thinking of library code?
>
> ...
>
I am thinking for the case where an OS scheduler needs access to a
userspace global variable.
the kernel can't easily know about this case, unless something "special"
is done.
>>> You mention user-defined exceptions. I planned only one bit for them
>>> (as there is an arbitrary number of them). To distinguish one user
>>> exception from another would probably require a call to a routine that
>>> examines the detail.
>>
>> probably ok for a single app, but is dubious for an OS or general mechanism.
>
> Why is this no good? There can be any number of user exception types
> so it's not possible to have one bit for each. The best I could think
> of was to have one bit to indicate a user exception and functions to
> examine as much other detail as needed.
>
exceptions don't generally work this way.
yes, "one bit per type for every type" is absurd, but using one bit
per-type for certain exceptions but not others, isn't much better.
>>>> one "could" potentially use interned strings and magic index numbers
>>>> though, say, 10 or 12 bits being plenty to allow a range of named
>>>> exception classes (potentially being keyed via a hash-table or similar).
>>
>>>> this is actually how my dynamic type-system works (types are identified
>>>> by name, but encoded via hash indices).
>>
>>> I see your point. The problem is that this allows only one exception
>>> at a time to be signalled-but-not-yet-handled. Which one? I suppose
>>> the most critical one or the first one or the last one could be chosen
>>> to be marked in the indicator word.
>>
>> errm... typical exception mechanisms only allow a single exception at a
>> time. as soon as an exception is thrown, it is handled immediately. no
>> delays or queuing are allowed. if an exception occurs within a handler,
>> this may either throw the new exception (the prior one is forgotten), or
>> simply kill the app.
>
> If only one exception at a time is needed that makes things
> significantly easier. But is only one needed? I was thinking of
> exceptions caused in exception handlers. For example, the exception
> handler divides by the number it first thought of and triggers an
> overflow or it checks an array index and finds it out of bounds. What
> does the system do?
>
> It could leave the first exception in place - after all that was the
> root cause. Or it could replace the exception information with its own
> - after all it has an internal fault. Neither seems obviously best. It
> might be best to keep both indications until both have been either
> resolved or reported ... but it would be easier to handle if only one
> was kept.
>
most exception handler systems will simply ignore the first and rethrow
the next.
some systems (those with Resumable Exceptions, such as Win32 SEH),
generally nest them sort of like a stack, so (AFAIK) exceptions are
handled in LIFO order provided all are resumable exceptions. if a
non-resumable exception occurs, then it will unwind and presumably there
is no way to handle prior exceptions.
>> Windows does this: if something goes wrong and an exception can't be
>> thrown in-app, Windows will simply kill the process.
>
> If an exception "can't be thrown"? What would prevent it being thrown?
>
stack overflow;
crash within dispatcher;
(Win64) if function prologues/epilogues are missing or illegible;
...
>> if something goes
>> wrong in-kernel, it is a blue-screen / BSOD.
>
> Not very good, though, is it! In reality I think that Windows or any
> OS will handle almost all exceptions. A BSOD should only occur on a
> hardware failure such as a machine-check exception. (It looks like
> Windows does run third-party drivers as privileged this increasing its
> vulnerability many-fold.)
>
nope.
apparently the Windows kernel treats pretty much any CPU exception
within the kernel as a BSOD-causing offense.
this leads to more BSODs, potentially, but the argument is that if
something has gone wrong in the kernel, it is better to try to kill the
system "gracefully" than to continue on in a potentially inconsistent state.
AFAIK, Linux tries a little harder, displaying a register dump and
similar and tries to recover, or failing this does a "kernel panic".
user-space exceptions can be handled more gracefully (raising signals or
invoking exception handlers, ...).
interestingly, the "This program has performed an illegal operation and
must be shut down." message is also an exception handler. if one crashes
the application hard enough, one will not see this, and instead the
process simply/silently dies/disappears.
in the past, this could leave dead/phantom windows (don't redraw, can't
be moved or closed, ...) but I suspect newer versions have addressed
this (any windows owned by the process are automatically destroyed even
if the process dies rather non-gracefully...).
I could be wrong on the details here, mostly speaking from old memories.
>> likewise for the CPU:
>> if an exception occurs within an interrupt handler, this is a
>> "doublefault" and generally leads to a reboot.
>>
>> an exception is not a status code...
>
> Following your analogy of a CPU you could perhaps think of each
> exception condition as an interrupt line that gets asserted until the
> condition causing the interrupt has been resolved. I know, as an
> analogy it's not perfect. :-(
>
flags and similar are used here.
so, normal interrupt occurs:
transfers to handler (and temporarily disables other interrupts);
handler does its thing;
iret (return from interrupt, restores prior CPU state, although GPRs/...
generally need to be manually saved/restored).
if things go bad in the first handler, then a double-fault occurs (a
dedicated special interrupt).
if the DF handler itself fails, the CPU reboots.
AFAIK, older CPUs would essentially just ignore whatever (maskable)
interrupts occurred during the handler, but newer ones will essentially
queue them and handle them in a FIFO order.
my memory gets a bit foggy here, as it has been a long time since I have
messed with this stuff...
>>> <<END
>>> Just on this point, and bringing it back specifically to C, if TLS is
>>> hard to obtain or slow to access there are two other possibilities
>>> that I don't think have been mentioned yet that spring to mind for use
>>> in C.
>>
>> it is slow, yes, but typically not enough to care about.
>> for most things people don't worry about a few clock-cycles here and
>> there (generally, it is big algorithm-level stuff that kills
>> performance, and not a lack of sufficient micro-optimization).
>>
>>> 1. If only running a single thread simply use a global. Job done.
>>
>> except when globals are slower.
>
> I'd like to understand why they would be. I tried compiling the code
> above with gcc and it seems to refer to G directly such as in sub()
> the compiler generates
>
> mov DWORD PTR G, %eax
>
but, with which OS and what compiler settings.
if this was Linux-i386 and "-shared" or "-fpic" was given, one would not
likely see a global being loaded this way.
if it was on Win32, then the above is what one would expect to see.
>>> 2. Use errno. Set it to zero at the start and check it where
>>> appropriate. On a user-detected exception that does not already set
>>> errno set it to a value which is outside the normal range (and create
>>> or append to the detailed exception object, as before).
>>
>> FWIW, depending on the C-library, errno may be in-fact a function call
>> wrapped in a macro.
>
> I know. I found that in some systems there is a call which returns the
> address of errno. In that case the compiler should (hopefully)
> remember that address for each use in a function.
>
this depends...
in most cases, it probably wont, since the compiler can't be sure that
the function wont return a different address each time (though I wont
rule out the possibility of some sort of a "this function will always
return the same value" modifier/attribute).
>>> Using errno does only allow one exception to be indicated at a time
>>> but that's the same as what I understand your suggestion to be. It
>>> also doesn't work between languages (unless they obtain the address of
>>> errno) but I think it could work well for C.
>>> END
>>
>>> At the end of the day, any scheme could be used. For performance the
>>> idea is that the exception-indicating word is either zero or non-zero.
>>> If it's non-zero one or more exceptions has/have occurred and must be
>>> dealt with.
>>
>> except that "exceptions as a status word" aren't really "exceptions" in
>> the traditional sense.
>>
>> may as well just call them status-codes, and leave the whole mess of
>> exception-handling out of this.
>
> Don't forget the original intention was speed. The point of a status
> word, as you call it, is to allow exception handling (or, non-
> handling) quickly. It is not an end in itself but a means of adding
> exceptions to C in a way that recognises exceptions in the right
> places while having the lowest possible impact on overall performance.
>
it is debatable that this is actually faster...
the problem is if the status is checked in each function (possibly
multiple times) then this may infact cost more overall than even
registering/unregistering exception handlers.
handlers capable of reflective unwinding (the GCC/DWARF strategy, Win64
SEH), only have a cost if an exception occurs, and are free otherwise
(apart from debatable "possibly contrivance" in the prologue/epilogue
case WRT Win64 SEH).
in my own (mostly unsused) ABI, I had done something like:
push ebp
mov ebp, esp
push ... ;push-register sequence, like in Win64
sub esp, ... ;space for locals/...
...
lea esp, [ebp-offs]
pop ...
pop ebp
ret
nop [magic_info] ;function metadata (optional)
this preserved a property that is desirable from typical with cdecl, but
is lost with both Win64 and SysV/AMD64: the ability to backtrack simply
using an EBP/RBP chain (with Win64, one has to unwind using awkward
logic, and on SysV/AMD64 one may be SOL apart from using DWARF as the
ABI does not mandate any sort of standardized frame pointer).
in the 32-bit case, my ABI would have been otherwise nearly identical to
cdecl, only differing in the use of special prologues and epilogues.
the 64-case was mostly similar to the 32-bit case and Win64, differing
from Win64 mostly in that the first 4 arguments are always passed on the
stack (and RBP being reserved as the frame pointer).
(gluing between them would generally be little more than a few 'mov'
instructions or similar).
in practice, I had mostly just used the Win64 and SysV/AMD64 ABIs (and a
custom name-mangling scheme). my implementation of the SysV/AMD64 ABI
was technically kind of crappy though, as I didn't bother much with
complex edge cases (passing/returning structs in registers, ...), and
due to technical reasons, arguments were stored to the stack and loaded
into registers just before the call operation.
otherwise, cross-ABI calls would require the use of costly
transfer-thunks (which basically save/restore registers and re-pack
argument lists, so a poorly-implemented SysV/AMD64 was at least a little
faster).
or such...
[toc] | [prev] | [next] | [standalone]
| From | jacob navia <jacob@spamsink.net> |
|---|---|
| Date | 2012-01-06 23:54 +0100 |
| Message-ID | <je7u36$gje$1@speranza.aioe.org> |
| In reply to | #15494 |
Le 06/01/12 21:05, BGB a écrit : > it is debatable that this is actually faster... > > the problem is if the status is checked in each function (possibly > multiple times) then this may infact cost more overall than even > registering/unregistering exception handlers. > > handlers capable of reflective unwinding (the GCC/DWARF strategy, Win64 > SEH), only have a cost if an exception occurs, and are free otherwise > (apart from debatable "possibly contrivance" in the prologue/epilogue > case WRT Win64 SEH). > I was the first to answer Mr Harris post (Dec 29th) He never answered or acknowldged my post even if I pointed him to: <quote from my message from Dec 29th> Look at the exception handling code in gcc. It implements an abstract machine that gets opcodes from a series of tables generated by the compiler. ... snip ... You should also look at the code of gcc. Specifically look in the "except.c" file and in the unwind-xx-fde files. There you will find the implementation <end quote> He ignored my advice and tried to defend his completely crazy schema with some "machine word" global variable. This is surprising since he declared (in another thread) that "gcc" was his "leight weight" compiler that he would use. Either: 1 Mr Harris doesn't *really* know what he is talking about 2 He wants to eliminate the exception handling mechanism of his preferred compiler and figure out a new one AND hack gcc to implement that... Since the second looks like a LOT of development time (I would guess at least 6 months full time) I would rather say that option (1) is more likely, unless of curse, he doesn't KNOW about DWARF/gcc/exception handling what would confirm (1) but make comprehensible why he is proposing (2).
[toc] | [prev] | [next] | [standalone]
| From | "Ed Coran" <sysdev@new.net> |
|---|---|
| Date | 2012-01-06 22:24 -0600 |
| Message-ID | <je8hd6$j50$1@speranza.aioe.org> |
| In reply to | #15502 |
jacob navia wrote: > Le 06/01/12 21:05, BGB a écrit : >> it is debatable that this is actually faster... >> >> the problem is if the status is checked in each function (possibly >> multiple times) then this may infact cost more overall than even >> registering/unregistering exception handlers. >> >> handlers capable of reflective unwinding (the GCC/DWARF strategy, >> Win64 SEH), only have a cost if an exception occurs, and are free >> otherwise (apart from debatable "possibly contrivance" in the >> prologue/epilogue case WRT Win64 SEH). >> > > I was the first to answer Mr Harris post (Dec 29th) And that is significant why? > > He never answered or acknowldged my post even if I pointed him to: Uh oh, you think newsgroup posts are akin to talking to people for real? > > He ignored my advice You are his advisor? I have done a little bit of consulting and know that all that matters in the legal substrate is the contract. So, what are you all up about, Jack? Are you in love with him? Is this beyond "the law"? > Either: > > 1 Mr Harris doesn't *really* know what he is talking about Or he does not accept you as "his master"? Have you considered that you are unimportant to him? > > 2 He wants to eliminate the exception handling mechanism of his > preferred compiler and figure out a new one AND hack gcc to > implement that... And if he solved that problem, your toil and labor would be all for naught? Really now. His disregard for emperialism is, surely, at the highest disregard, I mean we are all the same huh, but he breached that? You seek a "kindred spirit", surely. Surely I'm just here to fuck you up. Ya think?
[toc] | [prev] | [next] | [standalone]
| From | jacob navia <jacob@spamsink.net> |
|---|---|
| Date | 2012-01-07 09:15 +0100 |
| Message-ID | <je8uuc$9nf$1@speranza.aioe.org> |
| In reply to | #15520 |
Le 07/01/12 05:24, Ed Coran a écrit : Mr Coran: [snip personal attacks] Nothing in your message goes to the problem I have been repeating for a while here: Exception handling is a wide field of research that has been studied for years by many bright people with some implementations like gcc's open for study with their source code. The idea of replacing the fruit of all those efforts with something like a global variable that must be constantly POLLED by user code is such a completely NUTS idea that I recommended mr Harris to READ THE LITERATURE about the subject before proposing such a ridiciulous "solution". Your personal attacks do not change anything about this state of affairs and really only display your total lack of arguments. Yes, what else can you say than: <quote> Surely I'm just here to fuck you up <end quote> THAT is the level of your arguemnts. Well, to be able to insult me, Sir, it is first necessary that I give some importantce to what you say. Yours sincerely Jacob Navia
[toc] | [prev] | [next] | [standalone]
| From | "Ed Coran" <sysdev@new.net> |
|---|---|
| Date | 2012-01-08 23:45 -0600 |
| Message-ID | <jedutl$io$1@speranza.aioe.org> |
| In reply to | #15526 |
"jacob navia" <jacob@spamsink.net> wrote in message news:je8uuc$9nf$1@speranza.aioe.org... > Le 07/01/12 05:24, Ed Coran a écrit : > > Mr Coran: > > [snip personal attacks] > > Nothing in your message goes to the problem I have been repeating for a > while here: > > Exception handling is a wide field of research that has been studied > for years by many bright people with some implementations like gcc's > open for study with their source code. Surely by some of those lovely people that gave us C and C++! (See what's wrong with your stance in the above statement now?) > > The idea of replacing the fruit of all those efforts with something > like a global variable that must be constantly POLLED by user code > is such a completely NUTS idea that I recommended mr Harris to READ THE > LITERATURE about the subject before proposing such a ridiciulous > "solution". I must have missed the part where he suggested polling as some kind of ultimate general solution. Indeed, wasn't his OP surveying the landscape of ideas on the subject, and in particular, as a retrofit for C while still being 100% legal C? You wouldn't be contriving something in attempt to seem somehow superior to him, now would you? (OK, noted: no more remote humour when replying to your thread. It's hard to assess whether posters just know literal English or the "richer" language that it can be).
[toc] | [prev] | [next] | [standalone]
| From | James Harris <james.harris.1@googlemail.com> |
|---|---|
| Date | 2012-01-08 12:52 -0800 |
| Message-ID | <b1c38afa-6832-43cb-8579-617a5d46c4c1@a40g2000vbu.googlegroups.com> |
| In reply to | #15502 |
On Jan 6, 10:54 pm, jacob navia <ja...@spamsink.net> wrote: > Le 06/01/12 21:05, BGB a écrit : > > > it is debatable that this is actually faster... > > > the problem is if the status is checked in each function (possibly > > multiple times) then this may infact cost more overall than even > > registering/unregistering exception handlers. > > > handlers capable of reflective unwinding (the GCC/DWARF strategy, Win64 > > SEH), only have a cost if an exception occurs, and are free otherwise > > (apart from debatable "possibly contrivance" in the prologue/epilogue > > case WRT Win64 SEH). > > I was the first to answer Mr Harris post (Dec 29th) > > He never answered or acknowldged my post even if I pointed him to: Just because I didn't reply to you directly don't think I didn't read your post. I did, more than once, so don't be offended. It is not feasible to reply to everyone! James
[toc] | [prev] | [next] | [standalone]
| From | James Harris <james.harris.1@googlemail.com> |
|---|---|
| Date | 2012-01-08 14:20 -0800 |
| Message-ID | <8d5062d8-9470-4383-8427-26563c7cac7a@m20g2000vbf.googlegroups.com> |
| In reply to | #15494 |
On Jan 6, 8:05 pm, BGB <cr88...@hotmail.com> wrote:
...
> >>> Since the word would be checked many times between task switches this
> >>> is much faster than following a structure off FS or similar.
>
> >> possibly, however the issue then partly becomes:
> >> how does the task switcher know where the variable is?...
>
> > Isn't this easy if you have a flat address space: just put the
> > variable at a fixed location?
>
> if the task switcher is part of the OS kernel (it generally is), then it
> does not have direct visibility of variables declared in a user-space
> binary.
The intention is the other way round. Rather than the OS accessing
task space the tasks can access a fixed location in the kernel space.
For example, say the indicator variable is called task_excep and is
actually at location 4100. With a flat address space the linker should
be able to resolve the label to that address shouldn't it? (Though see
my response below to your point about PIC.)
...
> > Does this mean that the global, G, in the following would not be
> > accessed directly?
>
> > int G;
> > void sub() {
> > while (G> 0) {
> > G -= 1;
> > sub();
> > }
> > }
> > int main(int argc, char **argv) {
> > G = argc;
> > sub();
> > return 0;
> > }
>
> it depends on the target (CPU mode, compilation mode, and object format).
>
> if compiling for PE/COFF (EXE or DLL), it will be accessed directly.
>
> for ELF, in this case it will generally be accessed indirectly via the
> GOT (since the compiler will not know at this point where the variable
> is declared, a "common" variable could be either within the local ".bss"
> segment, or potentially imported from another SO, but this is only known
> to the linker).
>
> if it were initialized or static:
> static int G;
> or:
> int G=0;
>
> then the compiler would know that it was defined in the same compilation
> unit, in which case:
> on 32-bit x86, it will depend on whether or not this is PIC code (which
> will generally depend on whether or not this is a Shared-Object).
Thanks for pointing this out. Normal elf32 from GCC and was fine. It
just issued a simple access. When I added the -fpic option you
mentioned below it tried to access it via the GOT. That would be a bit
of a pain, given my desire for speed. That's a pretty cumbersome
mechanism for a variable at a fixed location in the address space but
there's maybe no way to tell the compiler enough that it understands.
For the code, I'm not sure that PIC would be necessary. Do you know
what else it does on GCC, especially as x86 code is generally position-
independent anyway? Branches and jumps are usually relative without
specifying PIC. Is it just for data accesses, for example?
> on x86-64, the CPU has RIP-relative addressing, and so will presumably
> use this to access the variable (for both SO's and non-SO binaries).
The thing is, the program would not necessarily be loaded at the same
address in each address space so the RIP-relative offset to a fixed
location doesn't work. OT but RIP-relative data addressing is bearable
for reading constants but otherwise is a pretty awful addition, IMHO.
None of these things should be a problem, though. The default settings
in C seemed fine.
...
> I am thinking for the case where an OS scheduler needs access to a
> userspace global variable.
>
> the kernel can't easily know about this case, unless something "special"
> is done.
As mentioned, it's the other way round. Each task needs access to a
machine word that belongs to the kernel. I can't see anything special
being needed. Am I missing something? The idea of a symbolic name
rather than an address is that it can be resolved by the link step.
...
> > Why is this no good? There can be any number of user exception types
> > so it's not possible to have one bit for each. The best I could think
> > of was to have one bit to indicate a user exception and functions to
> > examine as much other detail as needed.
>
> exceptions don't generally work this way.
> yes, "one bit per type for every type" is absurd, but using one bit
> per-type for certain exceptions but not others, isn't much better.
OK
...
> >> an exception is not a status code...
>
> > Following your analogy of a CPU you could perhaps think of each
> > exception condition as an interrupt line that gets asserted until the
> > condition causing the interrupt has been resolved. I know, as an
> > analogy it's not perfect. :-(
>
> flags and similar are used here.
>
> so, normal interrupt occurs:
> transfers to handler (and temporarily disables other interrupts);
> handler does its thing;
> iret (return from interrupt, restores prior CPU state, although GPRs/...
> generally need to be manually saved/restored).
>
> if things go bad in the first handler, then a double-fault occurs (a
> dedicated special interrupt).
>
> if the DF handler itself fails, the CPU reboots.
It seems you are thinking principally of the CPU exception interrupts.
I was thinking more of the hardware interrupt lines but it's not too
important. It wasn't a very good analogy anyway.
...
> > I tried compiling the code
> > above with gcc and it seems to refer to G directly such as in sub()
> > the compiler generates
>
> > mov DWORD PTR G, %eax
>
> but, with which OS and what compiler settings.
>
> if this was Linux-i386 and "-shared" or "-fpic" was given, one would not
> likely see a global being loaded this way.
As above, you are right about -fpic. -shared seemed to make no
difference.
> if it was on Win32, then the above is what one would expect to see.
Do you mean that a compiler using Win32's version of position
independent code would access G directly?
...
> > I found that in some systems there is a call which returns the
> > address of errno. In that case the compiler should (hopefully)
> > remember that address for each use in a function.
>
> this depends...
>
> in most cases, it probably wont, since the compiler can't be sure that
> the function wont return a different address each time (though I wont
> rule out the possibility of some sort of a "this function will always
> return the same value" modifier/attribute).
OK. How about at the top of a function,
int *erloc = &errno;
Then after each call,
if (*erloc)
I've not worked this through. Just a thought. As Kirk says, I like to
think there are always options. :-)
...
> > Don't forget the original intention was speed. The point of a status
> > word, as you call it, is to allow exception handling (or, non-
> > handling) quickly. It is not an end in itself but a means of adding
> > exceptions to C in a way that recognises exceptions in the right
> > places while having the lowest possible impact on overall performance.
>
> it is debatable that this is actually faster...
>
> the problem is if the status is checked in each function (possibly
> multiple times) then this may infact cost more overall than even
> registering/unregistering exception handlers.
I don't see your mathematics on this. I can't think of a perfect
analogy but it is similar to taking a C program ending with
return;
and changing it to
return 1;
How many people would complain that that slowed down their code?
Your PIC example would not be so fast, if written in C, where it goes
via the GOT. Position-independent assembly language would not be a
problem. Non-PIC code in either language doesn't see to be a problem
either.
The only thing I could think of which would be ostensibly faster is
just to test the carry flag on return
call subroutine
jc exception
However, that is no good for these reasons
1. Time would still be required to set or clear carry in the
subroutine.
2. C does not support access to the carry flag.
So, overall, it would not be faster and may be slower in some cases,
and could not be written in C.
> handlers capable of reflective unwinding (the GCC/DWARF strategy, Win64
> SEH), only have a cost if an exception occurs, and are free otherwise
> (apart from debatable "possibly contrivance" in the prologue/epilogue
> case WRT Win64 SEH).
>
> in my own (mostly unsused) ABI, I had done something like:
> push ebp
> mov ebp, esp
> push ... ;push-register sequence, like in Win64
> sub esp, ... ;space for locals/...
> ...
> lea esp, [ebp-offs]
> pop ...
> pop ebp
> ret
> nop [magic_info] ;function metadata (optional)
OK. This seems similar to a standard prologue and epilogue.
> this preserved a property that is desirable from typical with cdecl, but
> is lost with both Win64 and SysV/AMD64: the ability to backtrack simply
> using an EBP/RBP chain (with Win64, one has to unwind using awkward
> logic, and on SysV/AMD64 one may be SOL apart from using DWARF as the
> ABI does not mandate any sort of standardized frame pointer).
OK. FWIW if you used a test-on-return as per my proposal you could
build the stack trace on return, even without use of ebp-style stack
frames. That's what I plan to do. Of course, it will only build a
stack trace report between where the exception occurs and where it is
handled.
James
[toc] | [prev] | [next] | [standalone]
| From | jacob navia <jacob@spamsink.net> |
|---|---|
| Date | 2012-01-09 00:19 +0100 |
| Message-ID | <jed8aa$npp$1@speranza.aioe.org> |
| In reply to | #15596 |
Le 08/01/12 23:20, James Harris a écrit :
>
> As mentioned, it's the other way round. Each task needs access to a
> machine word that belongs to the kernel. I can't see anything special
> being needed. Am I missing something?
Yes, you are missing everything:
1) Many people including me have told you that each thread needs an
exception handler, in a multi-threaded environment. It can't be
just a machine word somewhere. It must be thread local storage
2) Accessing that machine word by polling, as you propose can't be done
in one instruction since you need the thread ID to be able to access
the thread local storage.
3) You need exclusive access to that machine word. You do not want other
process to write to it when you are reading. Remember that today's
CPUs have several cores. Having a thread gain exclusive access would
BLOCK all other threads since they are constantly polling that
famous machine word... If a thread gains exclusive access to that
address and then dies without releasing the lock the PROCESS STOPS.
All threads are blocked.
I could go on but what would be the point?
Can you explain why the existing schemas that have been developed
through many years of usage and debugging do not please you?
Why do you feel compelled to reinvent the wheel by proposing a
square wheel?
HINT: Wheels should be round :-)
HINT: Exception handlers are invoked by the OS or a throw in a given
thread and walk the stack seeking a handler using the tables
generated by the compiler
[toc] | [prev] | [next] | [standalone]
| From | James Harris <james.harris.1@googlemail.com> |
|---|---|
| Date | 2012-01-08 21:12 -0800 |
| Message-ID | <7f577b06-8018-4c97-8b38-1aeb60d3c297@k29g2000vbl.googlegroups.com> |
| In reply to | #15597 |
On Jan 8, 11:19 pm, jacob navia <ja...@spamsink.net> wrote: > Le 08/01/12 23:20, James Harris a écrit : > > > > > As mentioned, it's the other way round. Each task needs access to a > > machine word that belongs to the kernel. I can't see anything special > > being needed. Am I missing something? The above relates to the initial option 3 so, sticking to that.... > Yes, you are missing everything: Are you sure it's not that you totally misunderstand the idea? > 1) Many people including me have told you that each thread needs an > exception handler, in a multi-threaded environment. It can't be > just a machine word somewhere. It must be thread local storage This is TLS. I stated that in the initial post. > 2) Accessing that machine word by polling, as you propose can't be done > in one instruction since you need the thread ID to be able to access > the thread local storage. No, for option 3 the word is changed on a thread switch. I have explained this too. Checking back, if you view this discussion as a hierarchy see what is currently the seventh message, a reply I made to BGB. > 3) You need exclusive access to that machine word. You do not want other > process to write to it when you are reading. Remember that today's > CPUs have several cores. Having a thread gain exclusive access would > BLOCK all other threads since they are constantly polling that > famous machine word... If a thread gains exclusive access to that > address and then dies without releasing the lock the PROCESS STOPS. > All threads are blocked. The word is specific to a thread, not to a process, not to a CPU, not to a machine. > I could go on but what would be the point? Agreed. Please don't. :-) James
[toc] | [prev] | [next] | [standalone]
| From | Scott Wood <scott@buserror.net> |
|---|---|
| Date | 2012-01-08 23:41 -0600 |
| Message-ID | <slrnjgkvg2.3qv.scott@snotra.buserror.net> |
| In reply to | #15602 |
On 2012-01-09, James Harris <james.harris.1@googlemail.com> wrote: > On Jan 8, 11:19 pm, jacob navia <ja...@spamsink.net> wrote: >> 1) Many people including me have told you that each thread needs an >> exception handler, in a multi-threaded environment. It can't be >> just a machine word somewhere. It must be thread local storage > > This is TLS. I stated that in the initial post. > >> 2) Accessing that machine word by polling, as you propose can't be done >> in one instruction since you need the thread ID to be able to access >> the thread local storage. > > No, for option 3 the word is changed on a thread switch. I have > explained this too. And it's been explained to you that "changed on a thread switch" doesn't work unless this is CPU-local storage by some other mechanism. More than one thread can be running at the same time -- no switching, no opportunity to swap the magic variable. -Scott
[toc] | [prev] | [next] | [standalone]
| From | James Harris <james.harris.1@googlemail.com> |
|---|---|
| Date | 2012-01-09 11:00 -0800 |
| Message-ID | <e2e3f0bf-70c5-4871-a07c-13bb6df60cb3@t30g2000vbx.googlegroups.com> |
| In reply to | #15603 |
On Jan 9, 5:41 am, Scott Wood <sc...@buserror.net> wrote: ... > >> 2) Accessing that machine word by polling, as you propose can't be done > >> in one instruction since you need the thread ID to be able to access > >> the thread local storage. > > > No, for option 3 the word is changed on a thread switch. I have > > explained this too. > > And it's been explained to you that "changed on a thread switch" doesn't > work unless this is CPU-local storage by some other mechanism. If you mean your comment four days ago, I did reply. I said that I didn't understand what you meant but you said no more. Not what I would call 'explained' to me! :-( > More than > one thread can be running at the same time -- no switching, no opportunity > to swap the magic variable. I've been puzzling over this. The only thing I can think that might cause a problem is multiple threads running in the same address space on different CPUs. Is that what you mean? James
[toc] | [prev] | [next] | [standalone]
Page 1 of 4 [1] 2 3 4 Next page →
Back to top | Article view | comp.lang.c
csiph-web