C++Talk.NET Forum Index C++Talk.NET
C++ language newsgroups
 
Archives   FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

compiler smarts: register variables and catching exceptions

 
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ Language (Moderated)
View previous topic :: View next topic  
Author Message
andrew_nuss@yahoo.com
Guest





PostPosted: Wed Jun 21, 2006 3:02 pm    Post subject: compiler smarts: register variables and catching exceptions Reply with quote



Hi,

I have a long running loop that uses 4 stacks whose pointers are
declared as register
variables and which catches a SpecialException. The question is
whether the
introduction of the catch block will destroy register optimizations,
and force the
compiler to ignore using registers to hold the stack pointers.

---------------------------------------------------------------------------------------------------------------

do {
register int* sp1 = ...;
register int* sp2 = ...;
register int* sp3 = ...;
register int* sp4 = ...;

try {
do {
register int op = ...;
switch (op) {
// all of the cases do brief manipulations of the
various sp1,...,sp4
// some of the cases call functions which throw
SpecialException
}
} while (true);

} catch (SpecialException& e) {
// code to cleanup the values currently "held" by the
sp1,...,sp4
// and then re-enter the outer loop
}

} while (true);

---------------------------------------------------------------------------------------------------------------------

Thanks,
Andy


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
Michael Tiomkin
Guest





PostPosted: Thu Jun 22, 2006 4:00 pm    Post subject: Re: compiler smarts: register variables and catching excepti Reply with quote



andrew_nuss (AT) yahoo (DOT) com wrote:
Quote:
Hi,

I have a long running loop that uses 4 stacks whose pointers are
declared as register
variables and which catches a SpecialException. The question is
whether the
introduction of the catch block will destroy register optimizations,
and force the
compiler to ignore using registers to hold the stack pointers.

First of all, the 'register' keyword is in many cases obsolete, and
usually your compiler will decide without your help what live ranges of
the variables it allocates to registers.
Introduction of the catch block that uses the values of these
variables can prolong their live ranges, changing the compiler
behaviour. For a compiler, the
try
.......1
catch
......2
block is similar to
......1
if (unknown value)
.....2
and the reason is the possibility that an exception wouldn't occur. For
example, if you don't use the 'sp1' value in the try block but do use
it in the catch block, this value might be put somewhere if the catch
block exists, but it would be optimized away without the catch block.
---------------------------------------------------------------------------------------------------------------
Quote:

do {
register int* sp1 = ...;
register int* sp2 = ...;
register int* sp3 = ...;
register int* sp4 = ...;

try {
do {
register int op = ...;
switch (op) {
// all of the cases do brief manipulations of the
various sp1,...,sp4
// some of the cases call functions which throw
SpecialException

Before any function call your compiler will either put a living (used
later) value of a variable to a register that keeps its value through
calls, or save it before a call and restore it sometimes after the
call. In the first case, the called function might save this value
elsewhere (usually on the stack) when it needs this register, and
restore it later, before return or raising an exception.

Quote:
}
} while (true);

} catch (SpecialException& e) {
// code to cleanup the values currently "held" by the
sp1,...,sp4
// and then re-enter the outer loop
}

} while (true);


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
Francis Glassborow
Guest





PostPosted: Fri Jun 23, 2006 5:32 am    Post subject: Re: compiler smarts: register variables and catching excepti Reply with quote



In article <1150827716.899818.134040 (AT) p79g2000cwp (DOT) googlegroups.com>,
"andrew_nuss (AT) yahoo (DOT) com" <andrew_nuss (AT) yahoo (DOT) com> writes
Quote:
Hi,

I have a long running loop that uses 4 stacks whose pointers are
declared as register
variables and which catches a SpecialException. The question is
whether the
introduction of the catch block will destroy register optimizations,
and force the
compiler to ignore using registers to hold the stack pointers.

What register optimisations? Smile You may think that using the register
keyword does something but it actually does nothing useful. Most
compilers simply ignore it and those that don't frequently produce
worse
code as a result. I count the request for five registers. On many
platforms obeying that request would have adverse effects on any
function calls.

The issue has nothing to do with exceptions and a great deal to do with

your expectations as to the effect of using the register keyword.

Quote:

---------------------------------------------------------------------------------------------------------------

do {
register int* sp1 = ...;
register int* sp2 = ...;
register int* sp3 = ...;
register int* sp4 = ...;

try {
do {
register int op = ...;
switch (op) {
// all of the cases do brief manipulations of the
various sp1,...,sp4
// some of the cases call functions which throw
SpecialException
}
} while (true);

} catch (SpecialException& e) {
// code to cleanup the values currently "held" by the
sp1,...,sp4
// and then re-enter the outer loop
}

} while (true);

--
Francis Glassborow ACCU
Author of 'You Can Do It!' and "You Can Program in C++"
see http://www.spellen.org/youcandoit
For project ideas and contributions:
http://www.spellen.org/youcandoit/projects


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
kanze
Guest





PostPosted: Fri Jun 23, 2006 5:34 am    Post subject: Re: compiler smarts: register variables and catching excepti Reply with quote

andrew_nuss (AT) yahoo (DOT) com wrote:

Quote:
I have a long running loop that uses 4 stacks whose pointers
are declared as register variables and which catches a
SpecialException. The question is whether the introduction of
the catch block will destroy register optimizations, and force
the compiler to ignore using registers to hold the stack
pointers.

Almost all modern compilers ignore the register specifier
anyway, because they can do a better job of it than you can.

Anyway, any answer would be implementation dependent. Is the
program fast enough, or not? And if not, does the profiler
indicate that this is where the problem is? If the answer to
both is yes, you *might* look at the generated code, to see what
is really happening. But generally, I wouldn't worry about it.
With or without the register specifier, if the variables are
really intensively used, the compiler will put them in registers
during the loop. Possibly spilling them back to memory before
any throw, or any function which might throw.

--
James Kanze GABI Software
Conseils en informatique orient��e objet/
Beratung in objektorientierter Datenverarbeitung
9 place S��mard, 78210 St.-Cyr-l'��cole,
France, +33 (0)1 30 23 00 34


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
Rob
Guest





PostPosted: Fri Jun 23, 2006 5:38 am    Post subject: Re: compiler smarts: register variables and catching excepti Reply with quote

andrew_nuss (AT) yahoo (DOT) com wrote:

Quote:
I have a long running loop that uses 4 stacks whose pointers are
declared as register
variables and which catches a SpecialException. The question is
whether the
introduction of the catch block will destroy register optimizations,
and force the
compiler to ignore using registers to hold the stack pointers.

It depends on the compiler. Using the register keyword does not give
a guarantee of *any* optimisations, as the compiler is allowed to
ignore the hint. Whether it ignores the hint or not in the presence
of exception handling code presumably depends on how the compiler
implements exception handling.

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
Francis Glassborow
Guest





PostPosted: Fri Jun 23, 2006 5:46 am    Post subject: Re: compiler smarts: register variables and catching excepti Reply with quote

In article <btpnm3-d18.ln1 (AT) satorlaser (DOT) homedns.org>, Ulrich Eckhardt
<eckhardt (AT) satorlaser (DOT) com> writes
Quote:
Just like 'inline' the 'register' keyword is mostly advisory, i.e. a
compiler is free to ignore it, which is what most modern compilers do.
The
point is that their optimiser can usually figure out much better what
data
is accessed so often that it's worth putting it into a register.

FYI, the only guaranteed effect of 'register' is that you can't take
the
address of a variable anymore. The only guaranteed effect of 'inline'
is
that multiple definitions don't lead to linker errors.
Ahem, that is only true in C. You are allowed to take the address of a

register variable in C++, however the normal consequence is that the
compiler then ignores the register keyword entirely.

Actually, in modern implementations 'register' is entirely fluff in
source code and has zero impact on the generated object code.

--
Francis Glassborow ACCU
Author of 'You Can Do It!' and "You Can Program in C++"
see http://www.spellen.org/youcandoit
For project ideas and contributions:
http://www.spellen.org/youcandoit/projects


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
James Dennett
Guest





PostPosted: Fri Jun 23, 2006 3:04 pm    Post subject: Re: compiler smarts: register variables and catching excepti Reply with quote

Ulrich Eckhardt wrote:
Quote:
andrew_nuss (AT) yahoo (DOT) com wrote:
I have a long running loop that uses 4 stacks whose pointers are
declared as register variables and which catches a SpecialException.
The question is whether the introduction of the catch block will
destroy register optimizations, and force the compiler to ignore
using registers to hold the stack pointers.

Just like 'inline' the 'register' keyword is mostly advisory, i.e. a
compiler is free to ignore it, which is what most modern compilers do. The
point is that their optimiser can usually figure out much better what data
is accessed so often that it's worth putting it into a register.

FYI, the only guaranteed effect of 'register' is that you can't take the
address of a variable anymore.

Is that true for C++? I thought it was a C thing only,
and that in C++ taking the address of a 'register' object
is just fine (though likely to inhibit keeping it in a
register).

-- James

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
andrew_nuss@yahoo.com
Guest





PostPosted: Fri Jun 23, 2006 3:08 pm    Post subject: Re: compiler smarts: register variables and catching excepti Reply with quote

I did look at the asm code both before and after adding a catch and
found
that any function with a catch block does not persist any variables in
registers.

Thanks,
Andy

David Abrahams wrote:
Quote:
"andrew_nuss (AT) yahoo (DOT) com" <andrew_nuss (AT) yahoo (DOT) com> writes:

Hi,

I have a long running loop that uses 4 stacks whose pointers are
declared as register variables and which catches a SpecialException.
The question is whether the introduction of the catch block will
destroy register optimizations, and force the compiler to ignore
using registers to hold the stack pointers.

Maybe, but not necessarily. It's 100% implementation-dependent, so
you should look at the generated assembly code carefully (both before
and after you add the catch block ;-> ).


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
andrew_nuss@yahoo.com
Guest





PostPosted: Sat Jun 24, 2006 6:11 am    Post subject: Re: compiler smarts: register variables and catching excepti Reply with quote

I put the register keyword there primarily for effect. Let me be more
explicit.
This is a virtual machine loop, and the four stack pointers is an
exaggeration,
there are only 3 of them, plus an opcode array whose offset doesn't
change.

I'm assuming that the compiler has profiled the switch statement and
seen
that so many of the sp1..sp3 accesses are happening, that it will want
to
use registers for those stacks.

As to the issue of function calls and throws, that happens in only a
few of
the switch cases. Most of the cases are just 2 or 3 statements plus
some inline function calls. However, from the compiler's standpoint,
any of the function calls made in any of the switch cases could throw
specialexception, and unfortunately, I need to catch it, clean up the
stacks, and resume processing in many cases. That means that the
values of sp1..sp3 at the time of the function call that throws are
needed
in the catch block, and my guess is that this is easiest for the
compiler
if those values are held in the frame.

main {
// assume that these are used so frequently that
// compiler would choose to put them in registers on its own
// (aside from catch block issues seen below)
register int* sp1 = ...;
register int* sp2 = ...;
register int* sp3 = ...;
register int* bytecodes = ...;
register int cursor = 0;

do {
try {
do {
int opcode = bytecodes[cursor++];
switch (opcode) {
case DUP1: {
// very important for sp1 to be a register
// because this is a common opcode
// even though its only one case in the switch
// and there's only 2 statements here
int temp = *(sp1-1);
*--sp1 = temp;
break;
}

case CALLNATIVE: {
// call a function which could throw special
exception
// this case does not happen frequently and
does
// not need to be fast.
*--sp2 = MyFunctionWhichCanThrow(*sp1++,
*sp1++);
break;
}
}
} while (true);
} catch (SpecialException& e) {
...
// manipulate sp1..sp3 as seen in first posting
// does the fact that sp1..sp3 are being used in the catch
block
// mean that registers will not be chosen by the compiler
for them???
// otherwise, the compiler would have to unwind not only
the frame
// but also the complete register state. I just cannot
see how a compiler
// could do this.
}
} while (true);
}


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
Michael Tiomkin
Guest





PostPosted: Sun Jun 25, 2006 6:53 pm    Post subject: Re: compiler smarts: register variables and catching excepti Reply with quote

andrew_nuss (AT) yahoo (DOT) com wrote:
Quote:
I put the register keyword there primarily for effect. Let me be more
explicit.
This is a virtual machine loop, and the four stack pointers is an
exaggeration,
there are only 3 of them, plus an opcode array whose offset doesn't
change.

I'm assuming that the compiler has profiled the switch statement and
seen
that so many of the sp1..sp3 accesses are happening, that it will want
to
use registers for those stacks.

The compiler cannot automagically profile your program. I assume that
your compiler can read profiling results and you supplied them.

Quote:
As to the issue of function calls and throws, that happens in only a
few of
the switch cases. Most of the cases are just 2 or 3 statements plus
some inline function calls. However, from the compiler's standpoint,
any of the function calls made in any of the switch cases could throw
specialexception, and unfortunately, I need to catch it, clean up the
stacks, and resume processing in many cases. That means that the
values of sp1..sp3 at the time of the function call that throws are
needed
in the catch block, and my guess is that this is easiest for the
compiler
if those values are held in the frame.

main {
// assume that these are used so frequently that
// compiler would choose to put them in registers on its own
// (aside from catch block issues seen below)
register int* sp1 = ...;
register int* sp2 = ...;
register int* sp3 = ...;
register int* bytecodes = ...;
register int cursor = 0;

do {
try {
do {
int opcode = bytecodes[cursor++];
switch (opcode) {
case DUP1: {
// very important for sp1 to be a register
// because this is a common opcode
// even though its only one case in the switch
// and there's only 2 statements here
int temp = *(sp1-1);
*--sp1 = temp;
break;
}

case CALLNATIVE: {
// call a function which could throw special
exception
// this case does not happen frequently and
does
// not need to be fast.
*--sp2 = MyFunctionWhichCanThrow(*sp1++,
*sp1++);

I think the behaviour is undefined in this case. You'd better assign
*sp1++ twice to temporal vars. Recall that the order of evaluation of
function arguments is not determined in C/C++.

Quote:
break;
}
}
} while (true);
} catch (SpecialException& e) {
...
// manipulate sp1..sp3 as seen in first posting
// does the fact that sp1..sp3 are being used in the catch
block
// mean that registers will not be chosen by the compiler
for them???
// otherwise, the compiler would have to unwind not only
the frame
// but also the complete register state. I just cannot
see how a compiler
// could do this.
}
} while (true);
}

Well, let's assume that the compiler saved these values in memory.
Very fortunately, even if the values were saved, the compiler wouldn't
need to restore them for the DUP1 case: it can use their registers
because no function calls occured between definition of sp1...3 and use
of them in this particular case.
The cost of saving the values is usually very low, because the
processor can immediately proceed to other instructions. I think the
only problem you might have is that your processor doesn't have enough
registers (i86?), and therefore it has to reuse some registers to
compute these 6 vars. Only in this case it will need to restore the
values in the DUP1 case.
Recall that you use 6 long-living int vars in this code: sp1..3,
bytecodes, cursor and opcode. The last of them, 'opcode', would
probably be put in a register because it's used in a switch. On an x86,
some of the others might be saved/restored. On a RISC machine, all the
vars will be assigned different registers. In case that your compiler
knows that DUP1 is the most frequently used part of the switch
statement, it will probably assign sp1 to a register that is not reused
for other vars.

Michael

P.S. Did you see the assembly of this code?


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
kanze
Guest





PostPosted: Tue Jun 27, 2006 4:21 pm    Post subject: Re: compiler smarts: register variables and catching excepti Reply with quote

andrew_nuss (AT) yahoo (DOT) com wrote:

Quote:
I put the register keyword there primarily for effect. Let me
be more explicit. This is a virtual machine loop, and the
four stack pointers is an exaggeration, there are only 3 of
them, plus an opcode array whose offset doesn't change.

I'm assuming that the compiler has profiled the switch
statement and seen that so many of the sp1..sp3 accesses are
happening, that it will want to use registers for those
stacks.

Maybe. Or maybe just some of the time.

Remember, too, that on some architectures (including IA-32), the
number of registers is extremely limited. One of the reasons
the register keyword has become counterproductive is that
compilers have become very good at knowing which variables to
put in registers, when---rather than have the same value
constantly in the same register, it will switch them around
depending on what it is doing at any one particular point in the
function.

Quote:
As to the issue of function calls and throws, that happens in
only a few of the switch cases. Most of the cases are just 2
or 3 statements plus some inline function calls. However,
from the compiler's standpoint, any of the function calls made
in any of the switch cases could throw specialexception, and
unfortunately, I need to catch it, clean up the stacks, and
resume processing in many cases. That means that the values
of sp1..sp3 at the time of the function call that throws are
needed in the catch block, and my guess is that this is
easiest for the compiler if those values are held in the
frame.

It's easiest for the compiler to just keep all variables
(including compiler generated temporaries) in memory. The whole
point of optimization is that the compiler does extra work, to
find a solution which is better than the obvious one.

Quote:
main {
// assume that these are used so frequently that
// compiler would choose to put them in registers on its own
// (aside from catch block issues seen below)
register int* sp1 = ...;
register int* sp2 = ...;
register int* sp3 = ...;
register int* bytecodes = ...;
register int cursor = 0;

On an IA-32, I'd be surprised if you got more than two or three
in a register. Once that stack frame has been set up, the
compiler only has six registers to play with, and even very
simple expressions can require three registers to implement.

On a Sparc, of course, there would be no problem.

Quote:
do {
try {
do {
int opcode = bytecodes[cursor++];
switch (opcode) {
case DUP1: {
// very important for sp1 to be a register
// because this is a common opcode
// even though its only one case in the switch
// and there's only 2 statements here
int temp = *(sp1-1);
*--sp1 = temp;
break;
}

case CALLNATIVE: {
// call a function which could throw special exception
// this case does not happen frequently and does
// not need to be fast.
*--sp2 = MyFunctionWhichCanThrow(*sp1++, *sp1++);

Well, this epxression has undefined behavior. You need to break
out at least one of the auto-increments into a separate
expression.

Beyond that, it also depends on the calling conventions of the
local API: does it save registers, or not? On a Sparc, the
conventions (actually, the underlying hardware) ensures that 16
registers will be saved; the compiler would doubtlessly put the
register variables in those, and not bother spilling to memory
(and of course, the exception handling routine can also access
those registers). I'm less familiar with the conventions used
on modern Intel processors; back in the days of the 8086, the
usual convetion was that all of the six general registers were
volatile, and that it was up to the caller to save anything he
needed. In such cases, the compiler will spill the registers to
memory, exceptions or no.

If the compiler is using some sort of dynamic register
assignment scheme, of course, it will have to ensure that the
values are somewhere the exception handling routine can find
them. Spilling to memory is the easiest solution, but the
compiler could just as well arrange to use a different point of
entry to the catch block, which would move the values from where
ever they happened to be to where ever the catch block expected
them.

Quote:
break;
}
}
} while (true);
} catch (SpecialException& e) {
...
// manipulate sp1..sp3 as seen in first posting
// does the fact that sp1..sp3 are being used in the catch block
// mean that registers will not be chosen by the compiler for them???
// otherwise, the compiler would have to unwind not only the frame
// but also the complete register state. I just cannot see how a compiler
// could do this.

It depends on the hardware, and how exceptions are being
handled. With the standard handling algorithm, the compiler
generates a map which maps return addresses to exception
handling code---and there is absolutely nothing to prevent it
from using different mappings for different call sites, even in
cases where the code ends up in the same user code.

Quote:
}
} while (true);
}

In the end, the only real answer one can give is to profile
different variants, to see what the actual effect is on
performance.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
Display posts from previous:   
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ Language (Moderated) All times are GMT
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2006 phpBB Group
SEO toolkit © 2004-2006 webmedic.