Optimizing Away

J

jaysome

Suppose I have something like this on a free-standing
environment (where void main(void) is documented and well-defined):

static int G_num_loops = 0;

void main (void)
{
/* set up interrupts */
/* ... */
for ( ; ; )
{
G_num_loops++;
}
}

Further suppose that I have the (non-standard) means to "read" the value
of G_num_loops via a link map and other techniques :)

Is there anything in the C standard that allows the compiler to
optimize away and NOT allocate storage for G_num_loops and thus prevent me
from "viewing" its value? If so, does defining G_num_loops as volatile
prevent this? (or put in a more standard kind of way, does the C standard
require the statement:

G_num_loops++;

to be executed.

My compiler seems to be optimizing that statement away when defined as
static and not volatile. So instead, I change the definition to have
external linkage:

int G_num_loops = 0;

and it works for my purpose.

The only *potential* problem is that if I define variables with the
same name (mostly arrays) with external linkage in another translation
unit (more likely the name would be G_debug_buf), then the linker issues
an error about multiply defined symbols.

I can deal with that--I've learned to "work" with the compiler and
linker and just name such variables with prefixes that are specific to the
module, e.g., G_fir_debug_buf or G_serial_debug_buf. Maybe I'm just facing
the reality of embedded debugging, but I'd like to hear the opinions of
others who have similar experience in this area.

BTW, the compiler I'm working with does not conform to the C standard in
that it does not initialize the following file scope definition to 0:

static int G_num_loops;

I have to explicitly initialize it to 0:

static int G_num_loops = 0;

Thanks
 
R

Richard Bos

jaysome said:
Suppose I have something like this on a free-standing
environment (where void main(void) is documented and well-defined):

static int G_num_loops = 0;

void main (void)
{
/* set up interrupts */
/* ... */
for ( ; ; )
{
G_num_loops++;
}
}

Further suppose that I have the (non-standard) means to "read" the value
of G_num_loops via a link map and other techniques :)

Is there anything in the C standard that allows the compiler to
optimize away and NOT allocate storage for G_num_loops and thus prevent me
from "viewing" its value?
Yes.

If so, does defining G_num_loops as volatile prevent this?

Keeping in mind that volatile is intentionally under-specified, yes, it
should.
(or put in a more standard kind of way, does the C standard
require the statement:

G_num_loops++;

to be executed.

No. It requires the program to behave _as if_ it were executed, but if
there's nothing in the program itself which can detect whether it is, or
even detect whether G_num_loops exists at all, it's allowed to leave it
out and only pretend to have increment an int. That you might also be
able to read its value using means outside the program itself is not
within the scope of the S Standard, and an implementation is allowed to
ignore it.

Making G_num_loops volatile should, as you expect, stop this. All
accesses of a volatile object must be performed exactly as the Standard
specifies, with no optimisation performed on it. The catch is that "What
constitutes an access ... is implementation-defined".
An implementation could theoretically define an access as any reading of
a volatile object for the purposes of assigning it to another object, or
writing a value in it from another object. Since G_num_loops++ involves
only its own previous and next value, this would not constitute an
access under such an implementation. That implementation would, however,
be perverse - strictly within the bounds of the Standard, but perverse -
and I wouldn't take it into account in practice.

Richard
 
I

insik.park

jaysome said:
Suppose I have something like this on a free-standing
environment (where void main(void) is documented and well-defined):

static int G_num_loops = 0;

void main (void)
{
/* set up interrupts */
/* ... */
for ( ; ; )
{
G_num_loops++;
}
}

Further suppose that I have the (non-standard) means to "read" the value
of G_num_loops via a link map and other techniques :)

Is there anything in the C standard that allows the compiler to
optimize away and NOT allocate storage for G_num_loops and thus prevent me
from "viewing" its value? If so, does defining G_num_loops as volatile
prevent this? (or put in a more standard kind of way, does the C standard
require the statement:

G_num_loops++;

to be executed.

My compiler seems to be optimizing that statement away when defined as
static and not volatile. So instead, I change the definition to have
external linkage:

int G_num_loops = 0;

and it works for my purpose.

The only *potential* problem is that if I define variables with the
same name (mostly arrays) with external linkage in another translation
unit (more likely the name would be G_debug_buf), then the linker issues
an error about multiply defined symbols.

I can deal with that--I've learned to "work" with the compiler and
linker and just name such variables with prefixes that are specific to the
module, e.g., G_fir_debug_buf or G_serial_debug_buf. Maybe I'm just facing
the reality of embedded debugging, but I'd like to hear the opinions of
others who have similar experience in this area.

BTW, the compiler I'm working with does not conform to the C standard in
that it does not initialize the following file scope definition to 0:

static int G_num_loops;

I have to explicitly initialize it to 0:

static int G_num_loops = 0;

Thanks

Defining G_num_loops as volatile will prevent it from being optimized
out. In embedded applications, you should use the volatile keyword to
describe any variables that you are mapping to a hardware register.
 
C

cassius

Defining G_num_loops as volatile will prevent it from being optimized
out. In embedded applications, you should use the volatile keyword to
describe any variables that you are mapping to a hardware register.

Right. In addition to hardware registers and memory mapped devices,
other situations require the use of volatile:

1) Variables shared between threads.
2) Variables shared between threads and Interrupt Service routines.
3) Local variables in functions were setjmp has been called . This is
an exotic and used in few situations, but worth to mention.
 
C

CBFalconer

jaysome said:
Suppose I have something like this on a free-standing
environment (where void main(void) is documented and well-defined):

static int G_num_loops = 0;

void main (void)

Even if it is defined for your peculiar system, why use it? All
you are doing is creating another needless incompatibility.
 
T

Tor Rustad

jaysome said:
static int G_num_loops = 0;

void main (void)
{
/* set up interrupts */
/* ... */
for ( ; ; )
{
G_num_loops++;
}
}


In case you are accessing G_num_loops from an
interrupt handler, I would use:

volatile static sig_atomic_t G_num_loops;

volatile will stop the compiler from optimizing away your

G_num_loops++;
 
K

Keith Thompson

CBFalconer said:
Even if it is defined for your peculiar system, why use it? All
you are doing is creating another needless incompatibility.

A freestanding implementation is free to say that "void main(void)"
is legal and "int main(void)" isn't. The requirement
that an implementation must accept "int main(void)" and
"int main(int argc, char *argv[])" applies *only* to hosted
environments.
 
J

jaysome

Even if it is defined for your peculiar system, why use it? All
you are doing is creating another needless incompatibility.

Because if I define main() with a return type of int, and I have
something like this:

int main(void)
{

for ( ; ; )
{
/* do something */
}

return 0;
}

then some compilers and PC-lint complain about the "return 0;"
statement being unreachable.

Using void main(void) is appropriate for most embedded applications
that I can think of. Furthermore, most embedded applications are
"incompatible" (which I assume implies "portable") by there very
nature, so "creating another needless incompatibility" is really a non
sequitur.

I prefer to use void main(void) in embedded applications, where it
makes sense, because my main() will never return, and that's how it's
designed to work.

Sometimes it makes sense to shed your pedantic sunglasses when dealing
with the real world.

Best regards
 
J

jaysome

In case you are accessing G_num_loops from an
interrupt handler, I would use:

volatile static sig_atomic_t G_num_loops;

volatile will stop the compiler from optimizing away your

G_num_loops++;

Tor:

I think you are correct. Until reading your post I'd never heard of
the type sig_atomic_t.

Unfortunately, my compiler does not define the sig_atomic_t type. All
it has is a comment in <stdint.h>:

/* sig_atomic_t not defined */

Thanks
 
C

CBFalconer

jaysome said:
.... snip ...

I think you are correct. Until reading your post I'd never heard
of the type sig_atomic_t.

Unfortunately, my compiler does not define the sig_atomic_t type.
All it has is a comment in <stdint.h>:

/* sig_atomic_t not defined */

It should be defined in <signal.h>
 
T

Tor Rustad

jaysome said:
Tor:

I think you are correct. Until reading your post I'd never heard of
the type sig_atomic_t.

OK, sig_atomic_t was defined in C89
Unfortunately, my compiler does not define the sig_atomic_t type. All
it has is a comment in <stdint.h>:

/* sig_atomic_t not defined */

Did you check in <signal.h>?
 
K

Keith Thompson

jaysome said:
Because if I define main() with a return type of int, and I have
something like this:

int main(void)
{

for ( ; ; )
{
/* do something */
}

return 0;
}

then some compilers and PC-lint complain about the "return 0;"
statement being unreachable.

Using void main(void) is appropriate for most embedded applications
that I can think of. Furthermore, most embedded applications are
"incompatible" (which I assume implies "portable") by there very
nature, so "creating another needless incompatibility" is really a non
sequitur.

I prefer to use void main(void) in embedded applications, where it
makes sense, because my main() will never return, and that's how it's
designed to work.

Sure, that makes sense *if* your implementation documents
"void main(void)" as valid declaration for main.

If it doesn't, you're risking undefined behavior. Personally, I'd
rather put up with a warning message than take that risk. YMMV.
 
T

Tor Rustad

CBFalconer said:
Even if it is defined for your peculiar system, why use it? All
you are doing is creating another needless incompatibility.

In a freestanding environment, the type (and name!) of
startup function, is implementation-defined.

So what (in)compatibility are you talking about?
 
T

Tor Rustad

Keith said:
[...]

Sure, that makes sense *if* your implementation documents
"void main(void)" as valid declaration for main.

If it doesn't, you're risking undefined behavior. Personally, I'd
rather put up with a warning message than take that risk. YMMV.

What exactly made you beleave OP's implementation might
not document "void main(void)"?

:)
 
K

Keith Thompson

Tor Rustad said:
Keith said:
jaysome said:
jaysome wrote:

Suppose I have something like this on a free-standing
environment (where void main(void) is documented and well-defined)
[...]

Sure, that makes sense *if* your implementation documents
"void main(void)" as valid declaration for main.

If it doesn't, you're risking undefined behavior. Personally, I'd
rather put up with a warning message than take that risk. YMMV.

What exactly made you beleave OP's implementation might
not document "void main(void)"?

:)

I believe that it *might* not because I don't know one way or the
other. For the same reason, I believe that it might.

(I see the smiley, but I don't get the joke.)
 
C

Christopher Benson-Manica

(From the OP:)
I believe that it *might* not because I don't know one way or the
other. For the same reason, I believe that it might.

OP clearly stated that void main(void) was documented and well-defined
for his freestanding implementation, leaving little room for doubt.
 
K

Keith Thompson

Christopher Benson-Manica said:
OP clearly stated that void main(void) was documented and well-defined
for his freestanding implementation, leaving little room for doubt.

There's plenty of room for doubt for those of us who don't read
carefully enough. (*sigh*)
 
M

Mark McIntyre

Because if I define main() with a return type of int, and I have
something like this:

(example of a main that never returns)
then some compilers and PC-lint complain about the "return 0;"
statement being unreachable.

"When I drive too fast, the oil light comes on.
So I disconnected the bulb, now it no longer bothers me."
Using void main(void) is appropriate for most embedded applications
that I can think of.

Indeed, and this is why the rule is specifically for hosted
implementations. Furthermore embedded implementations document this as
an extension.

--
Mark McIntyre

"Debugging is twice as hard as writing the code in the first place.
Therefore, if you write the code as cleverly as possible, you are,
by definition, not smart enough to debug it."
--Brian Kernighan
 
D

Default User

Keith said:
There's plenty of room for doubt for those of us who don't read
carefully enough. (*sigh*)


We gotta start reading the messages now? Damn.




Brian
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,057
Latest member
KetoBeezACVGummies

Latest Threads

Top