Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Volatile automatic variables and setjmp

134 views
Skip to first unread message

Kenneth Almquist

unread,
Oct 28, 2002, 5:06:43 PM10/28/02
to
In the "volatile optimizations" discussion which occurred a few weeks
back, most of the participants ignored the issue of setjmp. Consider
the code, which was valid C prior to the ANSI standard:

{
int a;

a = 1;
if (setjmp(jbuf) == 0) {
a_function_that_might_call_longjmp();
a++;
another_function_that_might_call_longjmp();
} else {
printf("%d\n", a);
}
}

In order to compile this code, any liveness analysis done by the
compiler must take setjmp into account. The most obvious use for
liveness information is to eliminate computations whose results
are not used. If the set of registers preserved across function
calls is different from the set of registers preserved by longjmp,
then liveness infomation is also needed for register allocation.
In the above example, the variable "a" cannot be assigned to a
register which is not preserved by longjmp.

In order to simplify implementations, the 1989 standard required
automatic variables that are modified between a call to setjmp
and longjmp be declared volatile. The point is that a compiler
probably does not need liveness information when generating code
using volatile variables. The register allocator doesn't need
this information if the compiler places all volatile variables
in memory. So if a compiler does not compute liveness information
correctly for automatic variables which are alive across longjmps,
that will probably only affect the compiled code for programs where
such variables are not volatile. By declaring such programs
nonconforming, the standard relieved compiler writers from the
burden of computing liveness information correctly.

(The preceding paragraph is actually an oversimplification. Even
under the ANSI C standard, compilers need to handle variables
which are live across calls to longjmp but which are not modified
between the call to setjmp and the call to longjmp. Such variables
can be stored in registers which are not preserved by longjmp if
the registers are saved when setjmp is called and restored to the
saved values when longjmp is called. I think that the intended
implementation was the dumb, brute force approach of saving and
restoring all registers whose values were preserved across function
calls but not calls to longjmp. This eliminates the need to use
(potentialy incorrect) liveness information to decide which
registers to save and restore.)


The problem that this creates for compiler writers is that volatile
has two different meanings. It can mean that the programmer wants
to do special, platform specific stuff which breaks the assumptions
that the compiler normally makes about the environment. Or it can
mean that the programmer has written code that will break the
compiler's liveness assumptions if those assumptions are based on
the presumption that automatic variables are never live across
calls to longjmp.

There was a lot of discussion about what the standard requires for
volatile. Since

What constitutes an access to an object that has volatile-qualified
type is implementation-defined.

it seems to me that it meets the letter of the standard for an
implementation to define "access to an object" in such a way that
an automatic variable is never accessed. If this reading of the
standard is correct, then the question is whether defining "access
to an object" in such a fasion is reasonable from a quality of
implementation perspective. And that, in my opinion, depends on
how well the definition matches the intent of the programmer who
wrote the volatile qualifier.

Whenever *I* declare an automatic variable to be volatile, my intent
is to make the code portable to compilers that have incorrect liveness
information. From my perspective, a quality implementation won't
generate inefficient code just because a variable happens to be
live across longjmp. For example, I think that in a good quality
implementation the programmer should be able to take constant
propagation for granted. In the code snippet I gave above, I would
expect a high quality compiler to generate code for the statement
"a++;" that is as efficient as the code it would generate for "a = 2;"
since the compiler can determine that both these statements have the
same effect. And I would hope that the compiler would do this even
if I changed the type of "a" to "volatile int", since a compiler that
generates efficient code only for programs which don't comply with the
C standard is of limited use to someone who wants his code to be both
portable *and* fast.

The earlier thread on this topic makes it clear that there are people
who do want to be able to use the volatile attribute to tell the
compiler to map operations on automatic variables to specific memory
read and write operations. The most plausible example of a reason for
doing this was code which passes the address of an automatic variable
to another thread and then polls the variable periodically to see if
the other thread has changed it. Anther example was debugging. This
is a fairly specialized need. Many compilers now ship with
integerated debuggers, and if the supplied debugger provides the
functionality you need then then difficulty of writing a third party
debugger that will work with the compiler is irrelevant. Furthermore,
the handling of volatile automatic variables strikes me as pretty
tangential to the problem of making it easy for people to write
debuggers.

Take the example of setting a breakpoint on a statement that might
not generate any code. Some approaches that a debugger might use
are

1) Allow the user to set a breakpoint at any statements.
2) Allow the user to set a breakpoint on at some statements. If
the user wants to set a breakpoint at another statement, he
can insert a new statement into the code which does nothing
except allow a breakpoint to be set there. The new statement
should look like:
2a) breakpoint(); /* does nothing */
2b) { static volatile int a = 0; }
2c) { volatile int a = 0; }

In my view, approach 1 is significantly better that any of the variants
of approach 2 because the whole point of a debugger (as I see it) is
to let you debug your program without cluttering it up with lots of
debugging code. Thus in my view the primary quality of implementation
issue is how well the compiler supports approach 1. If the user sets
a breakpoint on a statement and the compiler hasn't generated any code
for that statement, the debugger can set one instruction earlier and
then single step over that instruction. The difficulty of implementing
a debugger that does this can vary greatly depending on the usefulness
of the debugging information produced by the compiler.

The bottom line is that "volatile" really has two distinct meanings
when applied to an automatic variable. My guess is that if you
interpret it as meaning "don't assume that this variable is not alive
across longjmp" when applied to a variable whose address is not taken,
your interpretation will almost always be correct. It is less clear
what to do when the address of the variable is taken; I'd say treat it
like a nonautomatic volatile variable, on the grounds that that is
the interpreation that will cause the least harm when you are wrong.
Taking the address of an automatic variable will suppress the majority
of optimizations anyway. It's safe to say that no matter what you do,
someone will complain.
Kenneth Almquist

Douglas A. Gwyn

unread,
Oct 29, 2002, 1:13:05 AM10/29/02
to
Kenneth Almquist wrote:
> it seems to me that it meets the letter of the standard for an
> implementation to define "access to an object" in such a way that
> an automatic variable is never accessed.

No. I wonder how you missed the requirement that volatile
objects be accessed in accordance with the abstract C
machine model. The implementation-defined aspects of
access pertain to the width (e.g. a byte access might
require simultaneous access to adjacent bytes, depending
on the memory architecture) and other such warts, but
*not* to whether the read/write accesses are done per the
instructions in the source code.

> ... And I would hope that the compiler would do this even
> if I changed the type of "a" to "volatile int", ...

A conforming implementation could do that only if no strictly
conforming program could detect the difference in behavior.

> The bottom line is that "volatile" really has two distinct
> meanings when applied to an automatic variable.

Both "meanings" must be simultaneously satisfied by any
conforming implementation. The setjmp-associated
"meaning" is secondary; the primary meaning is that
accesses are not optimized away.

Kevin Bracey

unread,
Oct 29, 2002, 2:54:54 AM10/29/02
to
In message <apkcdj$g9k$1...@shell.monmouth.com>
k...@sorry.no.email (Kenneth Almquist) wrote:

> The earlier thread on this topic makes it clear that there are people
> who do want to be able to use the volatile attribute to tell the
> compiler to map operations on automatic variables to specific memory
> read and write operations.

I think it's a silly argument.

Firstly, it's up to the compiler how it deals with automatic variables. If it
does not provide any hooks to control where automatic variables end up in
memory, then there is no reason for it to suppose that anyone is capable of
tracking what its doing. Why should it be forced to do loads and stores to
memory, as opposed to updates of a register? As pedants keep pointing out to
newbies, "C doesn't have a stack". Who is to say that an automatic variable
has a memory location? If a compiler did have mechanisms for placing
automatic variables in known locations, thus potentially mapping them to I/O
registers, then it would have to visibly obey volatile access rules to them.
But I bet there aren't many compilers that can do that.

On another point - who's to say that "writing it to memory" makes it more
inspectable than keeping it in a register? If the compiler knows it hasn't
told anyone else where the variable is, anyone trying to monitor or modify it
must be using their own nefarious means that the compiler doesn't know
anything about. Such an omnipotent being could presumably just as easily pick
it out of an arbitrary register as out of an arbitrary memory location.

> The most plausible example of a reason for doing this was code which passes
> the address of an automatic variable to another thread and then polls the
> variable periodically to see if the other thread has changed it.

That is totally plausible, as the compiler now knows that someone else has
access to the variable. Volatile rules must be obeyed there, and it must go
via the addressed location, after the address has been taken.

The only reason there is scope for automatic volatile variables to behave
differently from static ones is that it can be known that no-one else can
access the variable, but that goes out the window when you've handed the
address to someone.

> Furthermore, the handling of volatile automatic variables strikes me as
> pretty tangential to the problem of making it easy for people to write
> debuggers.

Absolutely.

Debuggers provide a back-door into the system that the standard says nothing
about, and my compiler for one has to turn off all sorts of optimisations to
make debugging work (although auto variables are not forced into memory, as
the debugger can inspect and watchpoint register contents). If there's a need
for you to make variables volatile so the debugger can see or modify them,
that's a quality-of-implementation or configuration problem in your debugging
environment, again nothing to do with the standard. A proper debugging
environment will be able to modify any variable.

In practice, it may be that you're compiling with optimisation on as well as
debugging, in which case your tools may document that making a variable
volatile will guarantee it is debugger-accessible. But that's an
implementation choice, and again, how exactly that works is up to your tools
- it's an additional meaning to that of the standard, which says nothing
about debugger accessibility.

--
Kevin Bracey
http://www.bracey-griffith.freeserve.co.uk/

t...@cs.ucr.edu

unread,
Oct 29, 2002, 3:07:18 AM10/29/02
to
Douglas A. Gwyn <DAG...@null.net> wrote:
+ Kenneth Almquist wrote:
+> it seems to me that it meets the letter of the standard for an
+> implementation to define "access to an object" in such a way that
+> an automatic variable is never accessed.

+ No. I wonder how you missed the requirement that volatile
+ objects be accessed in accordance with the abstract C
+ machine model.

I've always wondered what that means? Is there somewhere that the
Standard says that non-volatile variables don't have to be accessed in
accordance with the abstract C machine model?

Tom Payne

Kevin Bracey

unread,
Oct 29, 2002, 4:22:33 AM10/29/02
to
In message <aplfjm$k7p$1...@glue.ucr.edu>
t...@cs.ucr.edu wrote:

Section 5.1.2.3 (Program execution) covers this. It says that an actual
implementation need not evaluate part of an expression if it can deduce that
its value is not needed and no needed side effects are produced. (Accessing
a volatile object counts as a side effect).

Kevin Bracey

unread,
Oct 29, 2002, 4:45:55 AM10/29/02
to
In message <78f2e98c...@bracey-griffith.freeserve.co.uk>
Kevin Bracey <ke...@bracey-griffith.freeserve.co.uk> wrote:

> In message <apkcdj$g9k$1...@shell.monmouth.com>
> k...@sorry.no.email (Kenneth Almquist) wrote:
>
> > The earlier thread on this topic makes it clear that there are people
> > who do want to be able to use the volatile attribute to tell the
> > compiler to map operations on automatic variables to specific memory
> > read and write operations.
>
> I think it's a silly argument.

Here's a slightly more restrained response which I hope will summarise the
positions.

The standard says that "an object that has volatile-qualified type may be
modified in ways unknown to the implementation or have other unknown
side-effects. Therefore any expression referring to such an object shall be
evaluated strictly according to the rules of the abstract machine..."

I think this pretty unambiguously stops the compiler from optimising out
constructs like:

for (volatile int a=0; a<1000; a++)
;

or

void x(void)
{
volatile int y = 500;
y = y * y;
}

I believe that the optimal compilation for those (in ARM code) would be:

MOV a1, #0
L1 CMP a1, #1000
ADDLT a1, a1, #1
BLT L1
MOV pc, lr

x MOV a1, #500
MUL a2, a1, a1 ; y moves from a1 to a2 due to MUL reg clash
MOV pc, lr

This may slightly contradict my position in the other thread. The standard
does say that the expression involving volatile objects must be evaluated,
with all side-effects, so I think you cannot legally eliminate the
multiplication, the loop, or the first comparison.

But some have said that we must go further, and that the volatile variable
must be placed in memory so that "loads and stores" happen. I think that's
definitely overreaching. "Loads and stores" to memory are not in the scope of
the standard.

The standard only says that "the object may be modified in ways unknown to
the implementation or have other unknown side-effects". It doesn't say that
the compiler must actually change where it stores the variable to produce
certain side effects. The standard doesn't make any distinction between
registers and memory (at least in this area, if not ever), so I can't
anything to mandate that a volatile automatic variable should be placed in
memory at all, if its address isn't taken. Who's to say that the "side
effect" of changing y above isn't just that register a1 gets updated, rather
than memory location 0x9000 in the stack getting updated? The fact that the
object is moving from register to register would make it hard to track, but
that's why they invented the & operator, which would force the compiler to
put it in a fixed memory location instead.

Now, I think that my compiler actually does put volatile variables in memory,
but that's only as the simplest way of guaranteeing complete evaluation - it
has an internal "opcode" it uses for volatile memory accesses that prevents
the peepholer etc optimising them away. If it placed the volatile objects in
registers as shown in the examples above, it wouldn't be able to stop itself
from optimising the machine code away.

Thomas R. Truscott

unread,
Oct 29, 2002, 10:00:34 AM10/29/02
to
> In the "volatile optimizations" discussion which occurred a few weeks
> back, most of the participants ignored the issue of setjmp.

My 2c:
The C standard should deprecate the volatile/setjmp special case.
That is, auto variables should retain their value
whether or not they are volatile.
Many compilers are already setjmp-aware
and do not require that the autos be volatile.

Speaking of less-mentioned uses of volatile, here are two more:

1) To ensure floating-point values are rounded to 'double' on IA-32.
The Intel x86 has 64-bit 'double', but 80-bit floating point registers,
which results in various unpleasant quirks.
By declaring "volatile double x;" one can ensure that
assignments to x will result in a rounded 64-bit value.

2) To ensure that a floating-point operation occurs.
The IA-32 and (32-bit) PA-RISC architectures have the curious
feature that a floating-point exception is not detected until
the *next* floating-point operation.
This causes problems if, when the exception is finally taken,
there is a longjmp() to a function which has long since returned :-)
One can ensure timely detection of the exception
by forcing a floating-point operation:
_x_ = 0.0; /* volatile double _x_; */

Tom Truscott

t...@cs.ucr.edu

unread,
Oct 29, 2002, 9:47:05 AM10/29/02
to
Kevin Bracey <ke...@bracey-griffith.freeserve.co.uk> wrote:
+ In message <aplfjm$k7p$1...@glue.ucr.edu>
+ t...@cs.ucr.edu wrote:

+> Douglas A. Gwyn <DAG...@null.net> wrote:
+> + Kenneth Almquist wrote:
+> +> it seems to me that it meets the letter of the standard for an
+> +> implementation to define "access to an object" in such a way that
^^^^^
volatile

Right?

+> +> an automatic variable is never accessed.
^^^
volatile

Right?

+>
+> + No. I wonder how you missed the requirement that volatile
+> + objects be accessed in accordance with the abstract C
+> + machine model.
+>
+> I've always wondered what that means? Is there somewhere that the
+> Standard says that non-volatile variables don't have to be accessed in
+> accordance with the abstract C machine model?

+ Section 5.1.2.3 (Program execution) covers this. It says that an actual
+ implementation need not evaluate part of an expression if it can deduce that
+ its value is not needed and no needed side effects are produced. (Accessing
+ a volatile object counts as a side effect).

Hmmmmm. I agree that "accessing" a volatile variable is a "needed
side effect". But, the hypothesis s that the implementation has
defined "access to a volatile object" in such a way that a volatile
variable is never "accessed"?

Tom Payne

eri...@lksejb.lks.agilent.com

unread,
Oct 29, 2002, 3:24:34 PM10/29/02
to
Kevin Bracey <ke...@bracey-griffith.freeserve.co.uk> writes:

> The only reason there is scope for automatic volatile variables to behave
> differently from static ones is that it can be known that no-one else can
> access the variable, but that goes out the window when you've handed the
> address to someone.

I've seen this argument before, but I don't buy it. You're saying
that the compiler "knows" that nobody can access the variable, but the
whole point of volatile is to tell the compiler that it really can't
know that.

Turn the question around: what benefit is there in having the compiler
optimize away accesses to automatic volatile variables?

--
Eric Backus
R&D Design Engineer
Agilent Technologies, Inc.
425-335-2495 Tel

Kevin Bracey

unread,
Oct 29, 2002, 5:14:21 PM10/29/02
to
In message <uy98hf...@lksejb.lks.agilent.com>
eri...@lksejb.lks.agilent.com wrote:

> Kevin Bracey <ke...@bracey-griffith.freeserve.co.uk> writes:
>
> > The only reason there is scope for automatic volatile variables to behave
> > differently from static ones is that it can be known that no-one else can
> > access the variable, but that goes out the window when you've handed the
> > address to someone.
>
> I've seen this argument before, but I don't buy it. You're saying
> that the compiler "knows" that nobody can access the variable, but the
> whole point of volatile is to tell the compiler that it really can't
> know that.

My second post came part-way round to this. I agree that the compiler can't
know that, so it has to perform all the necessary operations on the variable,
completed at sequence point. But it can still put it in a register, as long
as it does all the necessary operations to the register in order - it's the
person trying to access the variable's problem to find it if it wants to
modify it.

> Turn the question around: what benefit is there in having the compiler
> optimize away accesses to automatic volatile variables?

The original poster in this thread highlighted one reason - if you're using
volatile for its auxiliary meaning of "preserved across longjmp".

Douglas A. Gwyn

unread,
Oct 29, 2002, 7:33:41 PM10/29/02
to
t...@cs.ucr.edu wrote:
> Hmmmmm. I agree that "accessing" a volatile variable is a "needed
> side effect". But, the hypothesis s that the implementation has
> defined "access to a volatile object" in such a way that a volatile
> variable is never "accessed"?

The hypothesis is wrong. Object access is specified in the
C standard. What the implementation gets to do is describe
characteristics of that access such as I explained previously,
not redefine terms of the standard.

James Kuyper Jr.

unread,
Oct 29, 2002, 8:11:48 PM10/29/02
to

That may be what's intended, but all that the standard actually says is
that it defines "access" to mean "(execution-time action) to read or
modify the value of an object", and then says that "what constitutes an

access to an object that has volatile-qualified type is

implementation-defined". It doesn't contain any list like the one you've
given, that restricts the ways that an implementation may re-define what
"access" means. That list cannot be deduced from the actual words of the
standard.

t...@cs.ucr.edu

unread,
Oct 29, 2002, 10:04:46 PM10/29/02
to
James Kuyper Jr. <kuy...@wizard.net> wrote:
+ Douglas A. Gwyn wrote:
+> t...@cs.ucr.edu wrote:
+>
+>> Hmmmmm. I agree that "accessing" a volatile variable is a "needed
+>> side effect". But, the hypothesis s that the implementation has
+>> defined "access to a volatile object" in such a way that a volatile
+>> variable is never "accessed"?
+>
+>
+> The hypothesis is wrong. Object access is specified in the
+> C standard. What the implementation gets to do is describe
+> characteristics of that access such as I explained previously,
+> not redefine terms of the standard.

+ That may be what's intended, but all that the standard actually says is
+ that it defines "access" to mean "(execution-time action) to read or
+ modify the value of an object", and then says that "what constitutes an
+ access to an object that has volatile-qualified type is
+ implementation-defined". It doesn't contain any list like the one you've
+ given, that restricts the ways that an implementation may re-define what
+ "access" means. That list cannot be deduced from the actual words of the
+ standard.

Exactly. At the most literal level, the Standard seems to be
contradicting itself. Presumably the more specific clause

What constitutes an access to an object that has volatile-qualified
type is implementation defined

superedes the more general clause

access [means] (execution-time action) to read or modify the value
of an object

but it's hard to imagine that the committee really meant to give the
implementer all that much discretion in this matter. Still, I don't
see exactly where the line should be drawn.

Tom Payne

Douglas A. Gwyn

unread,
Oct 29, 2002, 11:36:21 PM10/29/02
to
James Kuyper Jr. wrote:
> It doesn't contain any list like the one you've given, that
> restricts the ways that an implementation may re-define what
> "access" means.

It doesn't permit the implementation to "re-define" anything!
The *constitution* of an access shall be specified by the
implementation, not the *existence* of an access, which is
the read or write action as you cited.

Douglas A. Gwyn

unread,
Oct 29, 2002, 11:38:14 PM10/29/02
to
t...@cs.ucr.edu wrote:
> At the most literal level, the Standard seems to be
> contradicting itself. Presumably the more specific clause
> What constitutes an access to an object that has volatile-qualified
> type is implementation defined
> superedes the more general clause
> access [means] (execution-time action) to read or modify the value
> of an object

What part of "no, it doesn't" don't you understand?

> ... Still, I don't see exactly where the line should be drawn.

I explained that already. Try understanding what I said
instead of arguing with it.

t...@cs.ucr.edu

unread,
Oct 30, 2002, 12:29:11 AM10/30/02
to
Douglas A. Gwyn <DAG...@null.net> wrote:
+ t...@cs.ucr.edu wrote:
+> At the most literal level, the Standard seems to be
+> contradicting itself. Presumably the more specific clause
+> What constitutes an access to an object that has volatile-qualified
+> type is implementation defined
+> superedes the more general clause
+> access [means] (execution-time action) to read or modify the value
+> of an object

+ What part of "no, it doesn't" don't you understand?

We have a situation where two statements from the Standard contradict
each other -- either

(1) reads and writes are accesses (to objects of volatile-qualified types),

or

(2) what constitutes an access to an object of volatile-quaified type is
implementation defined.

It can't be both ways.

[...]
+ Try understanding what I said instead of arguing with it.

I'm simply pointing out a contradiction in the Standard. It's your
perogative to ignore one aspect or the other of that contradiction and
argue that you have access to the one true interpretation. Thus far,
you've offered nothing but heightened rhetoric to support your claims.

Tom Payne

James Kuyper Jr.

unread,
Oct 30, 2002, 6:48:37 AM10/30/02
to

You've made a distinction between constitution and existence, but I'm
afraid that I don't see how you reach the conclusions you've reached
about what that distinction means. The english language alone doesn't
seem sufficient to support your distinction.

Alexander Terekhov

unread,
Oct 30, 2002, 10:02:44 AM10/30/02
to

<constitution>

SIGABRT is raised on an attempt to access a volatile object of
automatic storage duration by this "Let's-Get-Rid-Of-Volatiles-
And-Jumps"(tm) implementation.

</constitution>

Is this OK?

regards,
alexander.

--
http://groups.google.com/groups?selm=3D6CA96B.682148CB%40web.de
(In the next round, please: - deprecate *jmp stuff ...)
http://groups.google.com/groups?selm=3DA7FFA2.515FF640%40web.de
(Forget C/C++ volatiles, Momchil. Java/RTJ folks did it "right")

t...@cs.ucr.edu

unread,
Oct 30, 2002, 10:44:29 AM10/30/02
to
Douglas A. Gwyn <DAG...@null.net> wrote:
+ James Kuyper Jr. wrote:
+> It doesn't contain any list like the one you've given, that
+ > restricts the ways that an implementation may re-define what
+> "access" means.

+ It doesn't permit the implementation to "re-define" anything!
+ The *constitution* of an access shall be specified by the
+ implementation, not the *existence* of an access, which is
+ the read or write action as you cited.

From Encarta World English:

contittute [...] transitive verb1. be: to be, amount to, or have the
status of a particular thing *This letter does not constitute an offer
of employment.

If that which "constitutes" an access cannot occur, then no accesses
will "be" (i.e., "exist").

Tom Payne

lawrenc...@eds.com

unread,
Oct 30, 2002, 12:19:21 PM10/30/02
to
Thomas R. Truscott <t...@news.cs.duke.edu> wrote:
>
> 1) To ensure floating-point values are rounded to 'double' on IA-32.
> The Intel x86 has 64-bit 'double', but 80-bit floating point registers,
> which results in various unpleasant quirks.
> By declaring "volatile double x;" one can ensure that
> assignments to x will result in a rounded 64-bit value.

You don't need volatile to do that (except with buggy compilers). The
standard requires that any conversion to double (which includes
assignment to a double) remove any excess range and/or precision.

-Larry Jones

I won't eat any cereal that doesn't turn the milk purple. -- Calvin

Douglas A. Gwyn

unread,
Oct 31, 2002, 4:08:08 PM10/31/02
to
t...@cs.ucr.edu wrote:
> I'm simply pointing out a contradiction in the Standard.

Obviously the standard was not written with intentional
contradictions. Therefore, to understand the intent,
you must *give up* the notion that there is a contradiction.
We didn't say that the implementation gets to redefine what
an access is. That's your own misinterpretation. It only
allows (actually, requires) the implementation to specify the
constitution of object access, i.e. components thereof. The
superclass "access" must be taken as existing context or it
would make no sense. I.e., our spec is not analogous to the
following (which indeed makes no sense):
The product must apply ink to three freems per second;
a "freem" is a piece of paper with a letterhead, or
an embossed envelope.
...
The supplier can redefine what a "freem" is.

We give a model, say that "volatile" qualification requires
direct mapping onto that model, and say further that the
implementation must document the details of that mapping.
(The standard itself does not specify those details because
it can't; it has to accommodate a wide variety of target
architectures.)

Douglas A. Gwyn

unread,
Oct 31, 2002, 4:10:10 PM10/31/02
to
Alexander Terekhov wrote:
> SIGABRT is raised on an attempt to access ...
> Is this OK?

Not in general, provided that the program conformed to
the implementation-documented requirements for that access.

t...@cs.ucr.edu

unread,
Oct 31, 2002, 6:38:42 PM10/31/02
to
Douglas A. Gwyn <DAG...@null.net> wrote:
+ t...@cs.ucr.edu wrote:
+> I'm simply pointing out a contradiction in the Standard.
+
+ Obviously the standard was not written with intentional
+ contradictions. Therefore, to understand the intent,
+ you must *give up* the notion that there is a contradiction.

Most contradictions are unintentional. ;-)

+ We didn't say that the implementation gets to redefine what
+ an access is. That's your own misinterpretation. It only
+ allows (actually, requires) the implementation to specify the
+ constitution of object access, i.e. components thereof.

+ The superclass "access" must be taken as existing context or
+ it would make no sense.
[...]
+ We give a model, say that "volatile" qualification requires
+ direct mapping onto that model, and say further that the
+ implementation must document the details of that mapping.
+ (The standard itself does not specify those details because
+ it can't; it has to accommodate a wide variety of target
+ architectures.)

Ah so! Finally I get it. Thanks.

Tom Payne

Kenneth Almquist

unread,
Nov 14, 2002, 4:24:41 PM11/14/02
to
t...@news.cs.duke.edu (Thomas R. Truscott) wrote:
> [[Kenneth Almquist write:]

>> In the "volatile optimizations" discussion which occurred a few weeks
>> back, most of the participants ignored the issue of setjmp.
>
> My 2c:
> The C standard should deprecate the volatile/setjmp special case.
> That is, auto variables should retain their value
> whether or not they are volatile.
> Many compilers are already setjmp-aware
> and do not require that the autos be volatile.

I agree.

I believe that when the 1989 standard was written, there was good
reason to do something about setjmp, because too many existing
implementations did not get it right. The problem is that when
longjmp is called, some of the registers in the target function
have probably been saved on the stack. Some implementations punted
on the problem of locating the saved values on the stack, and instead
restored the registers to the values they had at the time setjmp was
called. The standard could have made these implementations legal by
specifying that:

the values of objects with register storage class that are local
to the function containing the invocation of the corresponding
setjmp macro and have been changed between the setjmp invocation
and longjmp call are indeterminate.

Rather than trying to codify existing practice, the committee came
up with the rule involving volatile. There are at least two problems
with this foray into language design:

1) It creates problems for optimizers. The absence of a register
designation on a variable doesn't limit optimization; compilers
are allowed to store variables in registers even in the absence
of a register specifier. In contrast, performing certain
types of optimizations on variables with the volatile qualifier
requires an extremely strained, if not downright wrong,
interpretation of the standard.

2) If the address of a volatile qualified variable taken, a type
cast is required to cast away the volatile qualifier from
the pointer value. In contrast, no type cast is needed
when taking the address of a variable which has a non-register
storage class.

At the time the C89 standard was written, there still were compilers
for memory limited environments, where it would be impractical to
scan an entire subroutine to see if it contained a call to setjmp
before beginning code generation. That is no longer the case, so
there is now a third consideration:

3) Computers can track variable usage more reliably than people.
Requiring the programmer to figure out which variables need to
be designated as volatile creates an additional opportunity
for software bugs.

No other language that I am aware of requires the programmer to
assist the compiler in this fashion. For example, in C++ one can
write:

{
int a; // variable "a" is not flagged as special in any way

a = 1;
try {
a_function_that_might_raise_an_exception();
a++;
another_function_that_might_raise_an_exception();
} catch(an_exception) {
std::printf("%d\n", a);
}
}


> 2) To ensure that a floating-point operation occurs.
> The IA-32 and (32-bit) PA-RISC architectures have the curious
> feature that a floating-point exception is not detected until
> the *next* floating-point operation.
> This causes problems if, when the exception is finally taken,
> there is a longjmp() to a function which has long since returned :-)
> One can ensure timely detection of the exception
> by forcing a floating-point operation:
> _x_ = 0.0; /* volatile double _x_; */

OK, but this solution is compiler specific. For example, gcc for
the IA-32 will generate two integer moves, rather than a floating
instruction, for the above assignment. This is why I have been
concentrating on the use of volatile with setjmp; any other use of
volatile is going to be nonportable in any event.
Kenneth Almquist

Douglas A. Gwyn

unread,
Nov 14, 2002, 5:58:03 PM11/14/02
to
Kenneth Almquist wrote:
> I believe that when the 1989 standard was written, there was good
> reason to do something about setjmp, because too many existing
> implementations did not get it right. The problem is that when
> longjmp is called, some of the registers in the target function
> have probably been saved on the stack.

No, that's not the issue behind the current Standard wording.
Quite the opposite: some of the auto variables may have been
cached in registers.

> Some implementations punted
> on the problem of locating the saved values on the stack, ...

The history of setjmp/longjmp includes its predecessor setexit/reset
from 6th Edition Unix. Over the years many people tried to "do the
right thing" and unwind the stack, restore registers, what have you.
The current standard specification for setjmp/longjmp is the result
of many years of evolution and does exactly what we want.

> Rather than trying to codify existing practice, the committee came
> up with the rule involving volatile.

Existing practice was definitely broken.

> There are at least two problems with this foray into language design:
> 1) It creates problems for optimizers. The absence of a register
> designation on a variable doesn't limit optimization; compilers
> are allowed to store variables in registers even in the absence
> of a register specifier. In contrast, performing certain
> types of optimizations on variables with the volatile qualifier
> requires an extremely strained, if not downright wrong,
> interpretation of the standard.

You're arguing exactly backward. The reason the standard spec
is good is precisely because volatile already ensures the right
behavior. Your suggestion to use register storage class is a
step backward to one of the experiments that wasn't satisfactory.

> 2) If the address of a volatile qualified variable taken, a type
> cast is required to cast away the volatile qualifier from
> the pointer value.

There is no need to do that, except for some localized gain in
speed. Without making use of volatile qualification, you cannot
solve the caching problem, at least not in any acceptable way.

> No other language that I am aware of requires the programmer to
> assist the compiler in this fashion.

No other language you are aware of has the same requirements.
setjmp/longjmp is more primitive and thus harder to ensure
reasonable operation for than a structured exception facility.

t...@cs.ucr.edu

unread,
Nov 15, 2002, 1:48:41 AM11/15/02
to
Douglas A. Gwyn <DAG...@null.net> wrote:
+ Kenneth Almquist wrote:
+> I believe that when the 1989 standard was written, there was good
+> reason to do something about setjmp, because too many existing
+> implementations did not get it right. The problem is that when
+> longjmp is called, some of the registers in the target function
+> have probably been saved on the stack.
+
+ No, that's not the issue behind the current Standard wording.
+ Quite the opposite: some of the auto variables may have been
+ cached in registers.

When the current values are cached in (known) registers at the call to
longjmp(), there should be no problem in getting them to have that
value after the call to longjmp(). I got the impression that the
serious problems occur when the compiler uses a callee-save protocol
that is optimized where the callee saves as few registers as
necessary.

I got that impression from a 12/15/98 posting in this group by
Dennis Ritchie, who wrote:
>
> Suppose we have main(...) with
> int a;
> if (setjmp(...)) { /* what's the state here? */}
> else {
> /* lots of manipulation of values of
> variables like a and even machine status */
> f(); /* where f() calls g() calls h() calls i()*/
> }

> In the scenario, i() does a longjmp.
[...]
> The other practical implication and difficulty is that the
> local variables visible in main (after the longjmp) may well
> be strewn throughout the stack as main calls f calls g calls h
> calls i, depending on details of the calling sequence. For
> example, main's 'a' may be spilled from a register to the
> stack only somewhere in the middle of the sequence of calls.

> The runtime implementation of longjmp thus must (in general)
> be prepared to scrounge back through the stack and find where
> the various bits were secreted.

> The C89 use of "volatile" in this context was sort of a hint
> to help this out, but isn't much use in the context of modern
> optimizing compilers; it applied only to things explicitly
> also declared as "register". Today local (auto) things
> tend to be put into registers automatically by the compiler.

Tom Payne


Christian Bau

unread,
Nov 15, 2002, 3:39:17 AM11/15/02
to
In article <ar25c9$f2t$1...@glue.ucr.edu>, t...@cs.ucr.edu wrote:

> When the current values are cached in (known) registers at the call to
> longjmp(), there should be no problem in getting them to have that

******* I guess you mean setjmp.

> value after the call to longjmp(). I got the impression that the
> serious problems occur when the compiler uses a callee-save protocol
> that is optimized where the callee saves as few registers as
> necessary.

The only problem is that if a variable is cached in a register, setjmp
saves the register, a new value is assigned to the variable and later
longjmp is called, then the new value assigned to the variable will be
lost and the value at the time of setjmp will be restored. Getting the
original value back is easy, not losing changes is difficult.

Which is why the standard allows implementations to lose changed values
of non-volatile variables; it is not nice but it makes it a lot easier
for implementations to be standard conforming :-)

Douglas A. Gwyn

unread,
Nov 15, 2002, 6:00:50 AM11/15/02
to
t...@cs.ucr.edu wrote:
>>For
>>example, main's 'a' may be spilled from a register to the
>>stack only somewhere in the middle of the sequence of calls.

Yes, but note that 'a' was declared as a "stack" (auto)
variable all along. As I said it was its caching in a
register that was the real problem.

Douglas A. Gwyn

unread,
Nov 15, 2002, 6:03:15 AM11/15/02
to
Christian Bau wrote:
> Which is why the standard allows implementations to lose changed
> values of non-volatile variables; it is not nice but it makes it
> a lot easier for implementations to be standard conforming :-)

Another way of putting it is that the alternative is more likely
to force a big performance hit for conformance, which is clearly
undesirable.

James Kuyper

unread,
Nov 15, 2002, 11:06:40 AM11/15/02
to
Kenneth Almquist wrote:
>
> t...@news.cs.duke.edu (Thomas R. Truscott) wrote:
...

> > My 2c:
> > The C standard should deprecate the volatile/setjmp special case.
> > That is, auto variables should retain their value
> > whether or not they are volatile.
> > Many compilers are already setjmp-aware
> > and do not require that the autos be volatile.
>
> I agree.
...

> Rather than trying to codify existing practice, the committee came
> up with the rule involving volatile. There are at least two problems
> with this foray into language design:
>
> 1) It creates problems for optimizers. The absence of a register

But the proposed change would spread those same problems to apply to all
variables, not just those declared volatile.

...


> No other language that I am aware of requires the programmer to
> assist the compiler in this fashion. For example, in C++ one can

C++ has volatile too, with almost (but not quite) the same meaning as in
C.

> write:
>
> {
> int a; // variable "a" is not flagged as special in any way
>
> a = 1;
> try {
> a_function_that_might_raise_an_exception();
> a++;
> another_function_that_might_raise_an_exception();
> } catch(an_exception) {
> std::printf("%d\n", a);
> }
> }

C++ exceptions are a very different animal from the kinds of
interruptions of program flow that 'volatile' is intended to help with.

t...@cs.ucr.edu

unread,
Nov 15, 2002, 6:05:44 PM11/15/02
to
Christian Bau <christ...@freeserve.co.uk> wrote:
+ In article <ar25c9$f2t$1...@glue.ucr.edu>, t...@cs.ucr.edu wrote:
+
+> When the current values are cached in (known) registers at the call to
+> longjmp(), there should be no problem in getting them to have that
+ ******* I guess you mean setjmp.
+
+> value after the call to longjmp(). I got the impression that the
+> serious problems occur when the compiler uses a callee-save protocol
+> that is optimized where the callee saves as few registers as
+> necessary.
+
+ The only problem is that if a variable is cached in a register, setjmp
+ saves the register, a new value is assigned to the variable and later
+ longjmp is called, then the new value assigned to the variable will be
+ lost and the value at the time of setjmp will be restored.

AFAIK, it is not required that setjmp save all register and that
longjmp restore them.

+ Getting the original value back is easy,

Agreed.

+ not losing changes is difficult.

At function calls within a function that contains a call to setjmp,
the compiler treats all local automatics as though they had
volatile-qualified types, i.e., at function calls, the compiler
generates code to spill those local automatics to their home locations
(in the current activation record).

+ Which is why the standard allows implementations to lose changed values
+ of non-volatile variables; it is not nice but it makes it a lot easier
+ for implementations to be standard conforming :-)

IMHO, saving registers at function calls and retrieving them at
longjmps isn't particularly difficult. Am I missing something here?

Tom Payne

Kenneth Almquist

unread,
Nov 28, 2002, 7:38:18 PM11/28/02
to
"Douglas A. Gwyn" <DAG...@null.net> wrote:
> Kenneth Almquist wrote:
>> I believe that when the 1989 standard was written, there was good
>> reason to do something about setjmp, because too many existing
>> implementations did not get it right. The problem is that when
>> longjmp is called, some of the registers in the target function
>> have probably been saved on the stack.
>
> No, that's not the issue behind the current Standard wording.
> Quite the opposite: some of the auto variables may have been
> cached in registers.

Perhaps we have a terminology problem here. When I talk about a
variable being cached in a register I mean that the compiler is
doing the same type of thing that a hardware memory cache does.
The compiler takes a variable which has been allocated in memory
and temporarily keeps its value in a register in order to decrease
the number of accesses to main memory. It's common practice for
a compiler to attempt to assign an automatic variable to a register
if the address of the variable is not taken, and let the register
allocator force the variable out to memory if there aren't enough
registers. Thus the variables targeted for caching are non-automatic
variable and automatic variables whose addresses are taken.[1] Most
likely a compiler isn't going to attempt to cache these sorts of
variables across procedure calls. And if a compiler does attempt
to identify cases where caching across procedure calls is safe, it
would be easy enough to suppress these attempts when the current
procedure contains a call to setjmp.

Modern compilers read an entire function definition before generating
code for it. This approach was not practical for compilers designed
to operate in a limited memory environment, where there may not be
enough memory to hold the entire function definition in memory. When
assigning a variable to a register, there are three things that a
compiler would like to know, but cannot know without analyzing the
entire function:

1) Is the address of the variable taken? C specifies that the
address of a variable with the register storage class cannot
be taken. For non-register variables, the compiler doesn't
know whether the address is taken, and must assume the worst.

2) Is the variable live across a function call? If so, the
variable can only be assigned to a register which is preserved
by function calls. The C language doesn't contain any features
to allow the programmer to tell the compiler the answer to this
question, so the compiler must assume the worst.

3) Is the variable live across a longjmp? If so, the value of the
variable will be lost unless the variable is assigned to a
registers which is preserved by longjmp.

For the benefit of compilers in this class, it made sense to have
some language feature which allow the compiler to not "assume the
worst" answer to question 3. The "register" keyword could have
been used for this, as I suggested in my earlier article.

There is no longer any need to compile in memory limited environments,
so the problems facing such compilers are only of historic interest.
Modern compilers can determine the answers to all three of the questions
listed above by examining the function body.

>> Some implementations punted
>> on the problem of locating the saved values on the stack, ...
>
> The history of setjmp/longjmp includes its predecessor setexit/reset
> from 6th Edition Unix. Over the years many people tried to "do the
> right thing" and unwind the stack, restore registers, what have you.
> The current standard specification for setjmp/longjmp is the result
> of many years of evolution and does exactly what we want.

The evolution from "reset" to the two argument version of longjmp took
place on the PDP-11, and as far as I know all versions were implemented
correctly on that platform. The current specification is the invention
of the standard committee. I don't want to be seen as putting down the
committee or the work it did on the standard when taken as a whole, but
talking about "many years of evolution" doesn't change this history.

>> There are at least two problems with this foray into language design:
>> 1) It creates problems for optimizers. The absence of a register
>> designation on a variable doesn't limit optimization; compilers
>> are allowed to store variables in registers even in the absence
>> of a register specifier. In contrast, performing certain
>> types of optimizations on variables with the volatile qualifier
>> requires an extremely strained, if not downright wrong,
>> interpretation of the standard.
>
> You're arguing exactly backward. The reason the standard spec
> is good is precisely because volatile already ensures the right
> behavior.

I'm arguing from the premise that proper role of a language standard
is not to mandate a particular implementation in order to ensure the
right behavior, but rather to specify what the "right behavior" is and
leave it to the implementor to decide how to implement that behavior.

> Your suggestion to use register storage class is a
> step backward to one of the experiments that wasn't satisfactory.

Unsatisfactory how?

>> 2) If the address of a volatile qualified variable taken, a type
>> cast is required to cast away the volatile qualifier from
>> the pointer value.
>
> There is no need to do that, except for some localized gain in
> speed. Without making use of volatile qualification, you cannot
> solve the caching problem, at least not in any acceptable way.

I was mistaken here. I was thinking about the casts in code like the
following:

void
execute_file(FILE *fp) {
volatile char line[512];

if (setjmp(syntax_error))
printf("Invalid input: %s", line);
while (fgets((char *) line, sizeof line, fp))
parse_and_execute((char *) line);
}

This code should be portable in practice, but it breaks the prohibition
against accessing a volatile variable through non-volatile-qualified
pointers. To comply with the standard, we have to copy the text of
the line, and we can't use strcpy to perform the copy:

void
execute_file(FILE *fp) {
char line[512], *p;
volatile char vline[512], *vp;

if (setjmp(syntax_error)) {
p = line, vp = vline;
while ((*p++ = *vp++) != '\0');
printf("Invalid input: %s", line);
}
while (fgets(line, sizeof line, fp))
p = line, vp = vline;
while ((*vp++ = *p++) != '\0');
parse_and_execute(line);
}
}

>> No other language that I am aware of requires the programmer to
>> assist the compiler in this fashion.
>
> No other language you are aware of has the same requirements.
> setjmp/longjmp is more primitive and thus harder to ensure
> reasonable operation for than a structured exception facility.

Both setjmp/longjmp and a structured exception facility like the one
in Ada implement a nonlocal transfer of control. The difference
is that longjmp transfers control to a location specified by the
programmer whereas raising an exception transfers control to an
exception handler located by the run time system. The exception
mechanism is less general in the sense that the target of the transfer
is more constrained, but I'm at a loss to see how this difference
makes handling the transfer of control any easier to implement.

In any case, C is not the only language to be designed before
exceptions became the preferred way of providing non-local transfer
of control. For example:

C construct PL/1 equivalent
----------- ---------------

jmp_buf target; DECLARE TARGET LABEL;

if (setjmp(target)) TARGET = L1;
...; IF 0 THEN
L1: ...;

longjmp(target, 1); GOTO TARGET;

If anything, the PL/1 constructs are a bit harder to deal with because
the assignment to TARGET could be far away from the definition of L1.
Yet the compiler is expected to figure out which optimizations are safe
without any help from the programmer.
Kenneth Almquist


[1] If a variable is live across one or more function calls, the
register allocator might force it to memory because of a lack
of registers, but registers that are not preserved across
function calls might still be idle. Therefore, a compiler might
cache the variable using a register which is not preserved
across function calls. Obviously, this doesn't require the
compiler to take any special action to deal with longjmp since
the variable will not be cached across a function call.

Douglas A. Gwyn

unread,
Nov 29, 2002, 2:02:34 AM11/29/02
to
Kenneth Almquist wrote:
> There is no longer any need to compile in memory limited environments,
> so the problems facing such compilers are only of historic interest.

That's not literally true. There are still PDP-11 Unix users.
However, it is true that the resource model for C99 was a 512KB
compilation host, so your argument has some merit. But requiring
such global optimization, rather than merely allowing it, would
be a substantial change and a hard item to sell to the standards
committee. It also doesn't really tackle many of the issues
somewhat related to use of volatile qualification, which have
been recently discussed to some extent here and elsewhere.
I would rather see a comprehensive solution to these issues
than a significant new implementation requirement that addresses
only one issue.

> The evolution from "reset" to the two argument version of longjmp took
> place on the PDP-11, and as far as I know all versions were implemented
> correctly on that platform.

Absolutely not. I've seen both stack unwinding and non stack
unwinding intermediate implementations, also code generated that
used "one past the top" of the stack for scratch storage that
caused problems when jumping across frames. It is true that a
worse botch occurred on VAX-11 implementations.

> I'm arguing from the premise that proper role of a language standard
> is not to mandate a particular implementation in order to ensure the
> right behavior, but rather to specify what the "right behavior" is and
> leave it to the implementor to decide how to implement that behavior.

You're thinking in terms of constraining the implementor. But we
needed volatile qualification anyway for other purposes. Having
it available, the setjmp spec constrains the *programmer* to use
that feature in a context where otherwise "getting it right" would
significantly increase the burden on implementations.

> To comply with the standard, we have to copy the text of
> the line, and we can't use strcpy to perform the copy:

Yes, as I have noted previously the current standard is overly
restrictive in such contexts. A good DR could get that fixed.

t...@cs.ucr.edu

unread,
Nov 29, 2002, 4:12:35 AM11/29/02
to
Douglas A. Gwyn <DAG...@null.net> wrote:
+ Kenneth Almquist wrote:
+> There is no longer any need to compile in memory limited environments,
+> so the problems facing such compilers are only of historic interest.
+
+ That's not literally true. There are still PDP-11 Unix users.
+ However, it is true that the resource model for C99 was a 512KB
+ compilation host, so your argument has some merit. But requiring
+ such global optimization, rather than merely allowing it, would
+ be a substantial change and a hard item to sell to the standards
+ committee. It also doesn't really tackle many of the issues
+ somewhat related to use of volatile qualification, which have
+ been recently discussed to some extent here and elsewhere.
+ I would rather see a comprehensive solution to these issues
+ than a significant new implementation requirement that addresses
+ only one issue.
+
+> The evolution from "reset" to the two argument version of longjmp took
+> place on the PDP-11, and as far as I know all versions were implemented
+> correctly on that platform.
+
+ Absolutely not. I've seen both stack unwinding and non stack
+ unwinding intermediate implementations, also code generated that
+ used "one past the top" of the stack for scratch storage that
+ caused problems when jumping across frames. It is true that a
+ worse botch occurred on VAX-11 implementations.
+
+> I'm arguing from the premise that proper role of a language standard
+> is not to mandate a particular implementation in order to ensure the
+> right behavior, but rather to specify what the "right behavior" is and
+> leave it to the implementor to decide how to implement that behavior.
+
+ You're thinking in terms of constraining the implementor. But we
+ needed volatile qualification anyway for other purposes. Having
+ it available, the setjmp spec constrains the *programmer* to use
+ that feature in a context where otherwise "getting it right" would
+ significantly increase the burden on implementations.

What burden? When compiling a function containing an invocation of
setjmp:

- At function calls, generate code to store all local automatics to
known locations in the current stack frame.

- At the post-longjmp return from setjmp, generate code to restore
all local automatics from the current stack frame.

Perhaps the concern is overhead. Then only store and restore the
local automatics that are live at the post-longjmp exit from setjmp.
This suggested implementation will involve much less overhead than the
current solution of giving those variables volatile-qualified types.

Tom Payne


Alexander Terekhov

unread,
Nov 29, 2002, 7:05:26 AM11/29/02
to

Kenneth Almquist was thinking about:
: ....
: void

: execute_file(FILE *fp) {
: volatile char line[512];
:
: if (setjmp(syntax_error))
: printf("Invalid input: %s", line);
: while (fgets((char *) line, sizeof line, fp))
: parse_and_execute((char *) line);
: }
: ....

t...@cs.ucr.edu wrote:
[...]


> What burden? When compiling a function containing an invocation of

> setjmp: ...

Nah, when compiling a function containing an "invocation" of *try*:

< see "LANDING PADS", "COMPENSATION CODE", "PLACING LANDING PAD IN
``COLD'' CODE", "COMPRESSING TABLES", etc. >

http://www.computer.org/concurrency/pd2000/p4072abs.htm
(C++ Exception Handling, Christophe de Dinechin,
IEEE Concurrency October-December 2000 (Vol. 8, No. 4)

And as for brain-dead/archaic setjmp... well:

<quote>

....
int i;
try {
for (i = 0; i < 100; i++)bar(i);
} catch (OutOfMemory) {
/* Report out-of-memory condition*/
cerr << "Out of memory for i=" << i << endl;
}
....

- The setjmp function must be called at the beginning of every try
block, and the list of jmp_buf must be maintained.

- A linked list of objects on the stack must be maintained at all
times and kept in a consistent state with respect to the list of
jmp_buf.

- All variables that are stored in registers and declared outside
the try block must be restored to their initial value when
longjmp is invoked. For instance, the value of i in the catch
block in main must be the same value as when bar was called. This
can be achieved either by spilling all variables to memory before
calling setjmp or by having setjmp itself save all registers. Both
options are expensive on architectures with large register files
such as RISC processors.

This impact exists even if no exception is ever thrown, because the
calls to setjmp and the management of the object stack must be done
each time a try block is entered or exited.

</quote>

regards,
alexander.

t...@cs.ucr.edu

unread,
Nov 29, 2002, 1:42:05 PM11/29/02
to
Alexander Terekhov <tere...@web.de> wrote:
[...]
+ http://www.computer.org/concurrency/pd2000/p4072abs.htm
+ (C++ Exception Handling, Christophe de Dinechin,
+ IEEE Concurrency October-December 2000 (Vol. 8, No. 4)
+
+ And as for brain-dead/archaic setjmp... well:
+
+ <quote>
+
+ ....
+ int i;
+ try {
+ for (i = 0; i < 100; i++)bar(i);
+ } catch (OutOfMemory) {
+ /* Report out-of-memory condition*/
+ cerr << "Out of memory for i=" << i << endl;
+ }
+ ....
+
+ - The setjmp function must be called at the beginning of every try
+ block, and the list of jmp_buf must be maintained.
+
+ - A linked list of objects on the stack must be maintained at all
+ times and kept in a consistent state with respect to the list of
+ jmp_buf.
+
+ - All variables that are stored in registers and declared outside
+ the try block must be restored to their initial value when
+ longjmp is invoked. For instance, the value of i in the catch
+ block in main must be the same value as when bar was called. This
+ can be achieved either by spilling all variables to memory before
+ calling setjmp or by having setjmp itself save all registers. Both
+ options are expensive on architectures with large register files
+ such as RISC processors.
+
+ This impact exists even if no exception is ever thrown, because the
+ calls to setjmp and the management of the object stack must be done
+ each time a try block is entered or exited.
+
+ </quote>

After a call to longjmp, variables should have their value as of the
call to longjmp. The problem is to AVOID ROLLING BACK the values of
an implementation-dependend subset of objects to their value as of the
call to setjmp, namely, those objects whose values are cached in
registers as of the call to setjmp. IIUC, point #3 above is a recipe
for achieving what we seek to avoid!! ;-)

Tom Payne

Witless

unread,
Nov 29, 2002, 8:03:50 PM11/29/02
to
"Douglas A. Gwyn" wrote:

Is there a burden other than saving and restoring the register set? If so,
cannot the calling environment handle the save and reload of enregistered
variables?

t...@cs.ucr.edu

unread,
Nov 30, 2002, 8:59:20 AM11/30/02
to
Witless <wit...@attbi.com> wrote:
+ "Douglas A. Gwyn" wrote:
[...]
+> You're thinking in terms of constraining the implementor. But we
+> needed volatile qualification anyway for other purposes. Having
+> it available, the setjmp spec constrains the *programmer* to use
+> that feature in a context where otherwise "getting it right" would
+> significantly increase the burden on implementations.
+
+ Is there a burden other than saving and restoring the register set? If so,
+ cannot the calling environment handle the save and reload of enregistered
+ variables?

You're right as far as I can see. According to the 1989 C Standard:

All accessible objects have values as of the time longjmp was
called, except that the values of objects of automatic storage
duration that are local to the function containing the invocation
of the corresponding setjmp macro that do not have
volatile-qualified type and have been changed between the setjmp
invocation and longjmp call are indeterminate. [C89 7.6.2.1]

Were the burden placed on the implementor, he/she would need to worry
about those "objects of automatic storage duration that are local to


the function containing the invocation of the corresponding setjmp

macro that do not have volatile-qualified type and have been changed
between the setjmp invocation and longjmp call", especially the ones
that are live at the post-longjmp continuation from the invocation of
setjmp.

Option #1: Treat all worrisome objects as though they were declared
volatile. (How much of a burden is that?)

Option #2: When compiling a function that contains an invocation
of setjmp:
- At each function call that is reachable from the initial return
from an invocation of setjmp, store all enregistered objects
(at least, the worrisome ones).
- After a post-longjmp return from an invocation of setjmp, treat
all enregistered values as stale, i.e., reload them before their
next use.

Tom Payne

Alexander Terekhov

unread,
Nov 30, 2002, 9:35:11 AM11/30/02
to

My understanding is that "save all registers"/"initial value" aside,
point #3 is basically about: < "spilling all variables to memory" >

" - At function calls, generate code to store all local automatics to
known locations in the current stack frame.

- At the post-longjmp return from setjmp, generate code to restore
all local automatics from the current stack frame. "

with respect to "i"-like used variables (see above).

regards,
alexander.

Witless

unread,
Nov 30, 2002, 10:04:23 AM11/30/02
to
t...@cs.ucr.edu wrote:

For the programmer it is a large burden. It extends the influence of the
set/longjmp constraints throughout the entire function enclosing the setjmp. For
instance, elsewhere within that function one might find a tight loop frequently
accessing a local auto variable. The performance impact of silently changing
that variable to volatile might be unacceptable.

>
>
> Option #2: When compiling a function that contains an invocation
> of setjmp:
> - At each function call that is reachable from the initial return
> from an invocation of setjmp, store all enregistered objects
> (at least, the worrisome ones).
> - After a post-longjmp return from an invocation of setjmp, treat
> all enregistered values as stale, i.e., reload them before their
> next use.

This is by far the superior choice because it limits the impact of the
set/longjmp constraints to the immediate vicinity of the potentially affected
function calls.

But the distinction between local auto variables and local static variables
escapes me. Either might be enregistered by the optimizer, and thus the register
image of the variable would be subject to corruption by longjmp. How does the
implementor handle local statics and why can't that handling mechanism be applied
to local auto variables?


Christian Bau

unread,
Nov 30, 2002, 10:28:26 AM11/30/02
to
In article <3DE8D527...@attbi.com>, Witless <wit...@attbi.com>
wrote:

> But the distinction between local auto variables and local static variables
> escapes me. Either might be enregistered by the optimizer, and thus the
> register
> image of the variable would be subject to corruption by longjmp. How does
> the
> implementor handle local statics and why can't that handling mechanism be
> applied
> to local auto variables?

If your program modifies an extern, non-local static or static variable,
the compiler will make sure that the value is stored in its memory
location before any function call. The only situation where it doesn't
have to do this is if the compiler can prove that the called function
and anything called from it cannot access the variable. Remember that a
local static variable can be accessed by a recursive call to the
function containing it, so it will be rare that a compiler can prove
that access is impossible.

So any not-extremely-optimising compiler will store local static values
into memory before any function call. An extremely-optimising compiler
that checks that the called function cannot access the local static will
have to check that the called function cannot call longjmp anyway, no
matter whether you call setjmp in your function or not: Say function X
has a local static variable and decides to keep its value in a register
during a call to function Y. If one of the functions that called X has
called setjmp, and the function Y calls longjmp, then control will
transfer to the point of setjmp and the value will never be stored in
the local static variable. Which is wrong because that value must be
there in case your function X is called again.

So in practice keeping an extern, static or local static variable in a
register throughout a function call is a very difficult optimisation
anyway.

t...@cs.ucr.edu

unread,
Nov 30, 2002, 11:28:15 AM11/30/02
to
Witless <wit...@attbi.com> wrote:
+ t...@cs.ucr.edu wrote:
+
+> Witless <wit...@attbi.com> wrote:
+> + "Douglas A. Gwyn" wrote:
+> [...]
+> +> You're thinking in terms of constraining the implementor. But we
+> +> needed volatile qualification anyway for other purposes. Having
+> +> it available, the setjmp spec constrains the *programmer* to use
+> +> that feature in a context where otherwise "getting it right" would
+> +> significantly increase the burden on implementations.

+> +
+> + Is there a burden other than saving and restoring the register set? If so,
+> + cannot the calling environment handle the save and reload of enregistered
+> + variables?
+>
+> You're right as far as I can see. According to the 1989 C Standard:
+>
+> All accessible objects have values as of the time longjmp was
+> called, except that the values of objects of automatic storage
+> duration that are local to the function containing the invocation
+> of the corresponding setjmp macro that do not have
+> volatile-qualified type and have been changed between the setjmp
+> invocation and longjmp call are indeterminate. [C89 7.6.2.1]
+>
+> Were the burden placed on the implementor, he/she would need to worry
+> about those "objects of automatic storage duration that are local to
+> the function containing the invocation of the corresponding setjmp
+> macro that do not have volatile-qualified type and have been changed
+> between the setjmp invocation and longjmp call", especially the ones
+> that are live at the post-longjmp continuation from the invocation of
+> setjmp.
+>
+> Option #1: Treat all worrisome objects as though they were declared
+> volatile. (How much of a burden is that?)
+
+ For the programmer it is a large burden. It extends the influence of the
+ set/longjmp constraints throughout the entire function enclosing the setjmp. For
+ instance, elsewhere within that function one might find a tight loop frequently
+ accessing a local auto variable. The performance impact of silently changing
+ that variable to volatile might be unacceptable.

But, this is exactly the burden that the status quo already imposes.
Per the standard, following a longjmp, worrisome objects have
"indeterminate" value, in which case accessing them yields undefined
behavior. So the programmer must volatile-qualify the type of any
such object that is live at the post-longjmp return from setjmp.

I agree, however, that Option #2 is better and not all that much work.

Tom Payne

Alexander Terekhov

unread,
Nov 30, 2002, 4:35:22 PM11/30/02
to

t...@cs.ucr.edu wrote:
[...]

> But, this is exactly the burden that the status quo already imposes.

Yeah. Exactly!

regards,
alexander.

--
"Status quo, you know, that is Latin for ``the mess we're in.''"
-- Ronald Reagan

t...@cs.ucr.edu

unread,
Dec 1, 2002, 1:05:28 AM12/1/02
to
Alexander Terekhov <tere...@web.de> wrote:
+ t...@cs.ucr.edu wrote:
+> Alexander Terekhov <tere...@web.de> wrote:
+> [...]
+> + http://www.computer.org/concurrency/pd2000/p4072abs.htm
+> + (C++ Exception Handling, Christophe de Dinechin,
+> + IEEE Concurrency October-December 2000 (Vol. 8, No. 4)
[...]
+> + <quote>
[... point #3 ...]

+> + - All variables that are stored in registers and declared outside
+> + the try block must be restored to their initial value when
+> + longjmp is invoked. For instance, the value of i in the catch
+> + block in main must be the same value as when bar was called. This
+> + can be achieved either by spilling all variables to memory before
+> + calling setjmp or by having setjmp itself save all registers.
[...]
+> + </quote>
+>
+> After a call to longjmp, variables should have their value as of the
+> call to longjmp. The problem is to AVOID ROLLING BACK the values of
+> an implementation-dependend subset of objects to their value as of the
+> call to setjmp, namely, those objects whose values are cached in
+> registers as of the call to setjmp. IIUC, point #3 above is a recipe
+> for achieving what we seek to avoid!! ;-)
+
+ My understanding is that "save all registers"/"initial value" aside,
+ point #3 is basically about: < "spilling all variables to memory" >

Right. But point #3 would have us "[spill] all variables to memory
before setjmp or [have] setjmp itself save all registers". In either
case, the saved values will be the pre-setjmp values of those objects.
However, following a longjmp, we want objects to have their *current*
values (as of the call to longjmp). That's a horse with an entirely
different set of feathers.

In fact, the status quo almost mandates point #3 --- witness:

All accessible objects have values as of the time longjmp was

called, except that the values of objects of automatic storage
duration that are local to the function containing the invocation
of the corresponding setjmp macro that do not have
volatile-qualified type and have been changed between the setjmp
invocation and longjmp call are indeterminate. [C89 7.6.2.1]

If point #3 weren't already implemented, how would objects of
automatic storage duration that are local to the function containing
the invocation of the corresponding setjmp macro that do not have
volatile-qualified type and have NOT been changed between the setjmp
invoctaion and the longjmp call manage to "have values as of the time
longjmp was called"? If those values aren't saved at the call to
setjmp, those values would almost surely be lost when the
corresponding registers are spilled at subeequent function calls.

The problem is to ssve the post-setjmp modifications to those values,

Tom Payne

David Thompson

unread,
Dec 2, 2002, 1:19:57 AM12/2/02
to
Kenneth Almquist <k...@sorry.no.email> wrote :
...

> In any case, C is not the only language to be designed before
> exceptions became the preferred way of providing non-local transfer
> of control. For example:
>
> C construct PL/1 equivalent
> ----------- ---------------
>
> jmp_buf target; DECLARE TARGET LABEL;
>
> if (setjmp(target)) TARGET = L1;
> ...; IF 0 THEN
> L1: ...;
>
> longjmp(target, 1); GOTO TARGET;
>
> If anything, the PL/1 constructs are a bit harder to deal with because
> the assignment to TARGET could be far away from the definition of L1.

A PL/I label variable is indeed a very close match to C jmp_buf;
each can be "assigned" anywhere it is accessible (in scope, or
passed by reference*), but good practice** is usually to do so only
at or soon after its definition, and also used = jumped to anywhere
it is accessible (and valid). * OK, C refuses to call it by-reference
but you know what I mean. ** Except of course to people who reject
_all_ exceptions, goto, or other nonstructured control flow.

But PL/I also had ON-conditions/handlers which are much closer to
modern (Ada or C++) exceptions* and at least by the time _I_ saw C
(6ed ~1975) were being taught as the preferred though not required
way to handle exceptions, and certainly used as for builtin/ISA ones.
And LISP had throw/catch (closer yet) even before 1970 IINM.
* Except, as with most of PL/I, being more general = flexible =
confusing = dangerous. <G>

--
- David.Thompson 1 now at worldnet.att.net


Alexander Terekhov

unread,
Dec 2, 2002, 6:05:21 AM12/2/02
to

t...@cs.ucr.edu wrote:
[...]

> The problem is to ssve the post-setjmp modifications to those values,

Yes, and IMO the example/commentary I've quoted clearly states it:

"...For instance, the value of i in the catch block in main must be the
same value as when bar was called. ..."

regards,
alexander.

t...@cs.ucr.edu

unread,
Dec 2, 2002, 10:18:48 AM12/2/02
to
Alexander Terekhov <tere...@web.de> wrote:
+
+ t...@cs.ucr.edu wrote:
+ [...]
+> The problem is to ssve the post-setjmp modifications to those values,
+
+ Yes, and IMO the example/commentary I've quoted clearly states it:
+
+ "...For instance, the value of i in the catch block in main must be the
+ same value as when bar was called. ..."

Good point. I got hung up on the line that follows that one:

This can be achieved either by spilling all variables to memory
before calling setjmp or by having setjmp itself save all registers.

But this line is probably a typo (or a thinko), since restoring i to
"the same value as when bar was called" visibly requires that we spill
i before calling bar, rather than "before calling setjmp".

Tom Payne

0 new messages