About static variable

Go To Last Post
72 posts / 0 new

Pages

Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Am I guaranteed that a static variable don't get optimized away or do I need to make it volatile as well?

static unsigned int timercounter=0;
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Declaring a variable 'static' governs it's visibility and how it is allocated. It has two slightly different meanings depending on where you use it:

- In file scope: The variable will only be visible in this file.

- In function scope: The variable will be statically allocated (i.e. will exists even if when the function is not executing), but will only be visible inside the function.

You need 'volatile' as well, which governs how the optimizer treats/generates code that accesses the variable.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ok thanks
This is for a ISR function so we talk about the "old" function static.
Are there any way to avoid using volatile in this case?
Even code like this :

  if (++timercounter==4000){
    timercounter=0;

Gets bigger then using volatile.
(Read the variable twice)
At the moment the compiler make the same code if it's global or static (no surprise) and don't optimize it away (I use -Os) but I want it to work tomorrow as well.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

This is for a ISR function so we talk about the "old" function static.
Are there any way to avoid using volatile in this case?

Are you saying this is a variable shared between the ISR and the main() program? If so you must declare it 'volatile'.

Or are you saying this is a "local variable" in the ISR that you want to "be alive" between ISR activations? If so you need it to be 'static'.

(These cases are exclusive as far as I understand. It makes no sense to declare a function static variable volatile. Unless you are seriously into "aliasing" data.)

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Are you saying this is a variable shared between the ISR and the main() program

No I took it that he meant is as a static local holding state between one ISR() invocation and the next (timer counters possibly being the classic example of a local static?). So, yes, there's no worries about optimisation. The compiler has to write to globals and a static local is really a kind of "hidden" global which just happens to have limited name scope.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok clawson
So what you say is that I don't need to make it volatile.
A global variable can't be optimized away is that correct? (Because that is not what I hear from Johan)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

A global variable can't be optimized away is that correct?

No, that is not correct. It can be optimized away.

But this is not the primary problem that 'volatile' solves.

When a variable is shared between an ISR and the main() line of execution without volatile being used, what is optimized out is accesses to the variable. I.e. you might have:

uint8_t theVariable;

int main(void)
{
   theVariable = PINB;

   while (1)
   {
      PORTD = theVariable;
      PORTC = theVariable;
   }

   return 0;
}

For this, the compiler can not optimize the variable away totally. (It might decide to store it in a register, and not at all in RAM.) The compiler must read the contents from PINB, and somehow see to it that it gets written to PORTD. Sice tha AVR instruction set looks the way it looks, some tepmoprary storage is needed - AVRs can not move data directly from one I/O register to another.

Let's assume that the compiler is ineffective enough that it has this temporary storage in RAM (so the read of PINB will be to a GP register, whose content is then stored at a certain RAM address - and the write to PORTD is a load from that *RAM to a GP register which then gets written to the PORTD register).

Now, for the write to PORTC, the compiler can decide not to do the read again. It knows it has the correct value in the GP register, so it will grab it there.

UNLESS...

There is an ISR somewhere, looking like this:

ISR(whatever)
{
   theVariable = PINA;
}

Now, if this ISR actually is run right between these two lines in main()

      PORTD = theVariable;
      // The interrupt wedges in at this point
      PORTC = theVariable;

then the assumption of the compiler that it can re-use the cached value in the GP register is wrong. The ISR has written a potentially totally different value to the variable.

If the compiler was to take haight for any such possible invalidation of cached copies of data optimization would be totally impossible. Everything would always have to be written to, and everything would always have to be read from, RAM. So we cant do that.

Instead there is a mechanism to tell the compiler that it may not optimize accesses to a certain variable. This the primary reason for 'volatile' to exist in the C language.

In some cases the compiler can decide to throw out a variable completely. Example:

uint8_t theVariable = 42;

int main(void)
{
   PORTD = theVariable;

   while(1);
   return 0;
}

The compiler can translate that to machine code that load an "immediate value" directly from code memory to a GP register, and then write to PORTD. No variable is needed, and the compiler will optimize it out.

The 'volatile' keyword does not directly say to the compiler "do not optimie this variable out". It says "do not optimize accesses to this variable out", and from that it follows that the variable per se can nor be optimized out.

IMO this is an important distinction. It divides people who has understood 'volatile' and the people that has a hazy conception of it (perhaps prefixed with "mis-").

A global variable can very well be optimized away. There are uncountable special circumstances where the compiler might not be able to optimize away a global, e.g. the example above. As a generic statement it is just wrong to say that "a global variable can not be optimized away".

Re Cliffs "The compiler has to write to globals" he has something to teach me. I was under the impression that the compiler can optimize away writes just as well as it can optimize away reads. I'm sure he is correct! I am eager to learn something new!

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

sparrow2 wrote:
ok thanks
This is for a ISR function so we talk about the "old" function static.
Are there any way to avoid using volatile in this case?
Even code like this :

  if (++timercounter==4000){
    timercounter=0;

Gets bigger then using volatile.
(Read the variable twice)
At the moment the compiler make the same code if it's global or static (no surprise) and don't optimize it away (I use -Os) but I want it to work tomorrow as well.

What is the problem?

ISR(SOME_vect)
{
    static uint16_t timercounter;
    if (++timercounter == 4000) {
        timercounter=0;
    }
}

timercounter is only accessible by the ISR(). There is no need for volatile.

If your compiler allows you to waste a global register pair instead of a global memory location, the pre-increment and test will be quicker.

Incidentally, if you are worried about execution time, counting down is easier than counting up. And nesting two uint8_t memory variables is quicker (on average) than doing uint16_t maths.

I thought that you avoided C.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

My understanding is that the compiler's duty in regard
to global variables is limited to ensuring that other
compilation units see the correct values of global variables.
A function call to a user function outside the compiler's knowledge
would require that global variables have their correct values stored.
This suggests that the compiler could eliminate a global variable
if it were told that there was only one compilation unit.
In efect, a global variable would be static.

I suppose if the compiler were passed the call DAG and a cross reference,
it would have the information necessary to optimize more global variable references.

Iluvatar is the better part of Valar.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Put it this way - if (local) static's stopped holding state (always!) then about 50% of all C programs ever written would be completely FUBAR! If you call a function and set a local static to some value then leave that function and return there is no question on earth that the variable (unless its memory space has actually been corrupted) will not hold the last value written when you return. Sure the optimiser might "fiddle" about with the access to it but it has to ensure that when you leave the function the last value written to it IS stored in the memory location allocated to it so it will be available with that value next time you come back. Without this almost every state machine ever implemented would break!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ok so why can this

#include 
	unsigned int i;
int main(void)
{
	volatile int j;
    while(1)
    {
		if (i++>4000)
		{
			i=0;
			j++;
		}
    }
}

optimize i away ?
(what's left is a j++ forever )

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It can't remove i.
j is volatile. So every access to j must be performed.

This ensures that i is accessed too.

I would like to see how you would get any other behaviour.

Untested.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

sparrow2 wrote:
ok so why can this
#include 
	unsigned int i;
int main(void)
{
	volatile int j;
    while(1)
    {
		if (i++>4000)
		{
			i=0;
			j++;
		}
    }
}

optimize i away ?
(what's left is a j++ forever )

i is effectively static.
No other compilation unit is allowed to touch i.

Iluvatar is the better part of Valar.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

david.prentice wrote:
It can't remove i.

If optimization is set to higher than -O1 it does. Test first, talk later. :wink:
#include 

unsigned int i;

int main(void)
{
	volatile int j;
	
	while(1)
	{
		if (i++ > 4000)
		{
			i = 0;
			j++;
		}
	}
}
00000045  PUSH R28		Push register on stack 
00000046  PUSH R29		Push register on stack 
00000047  RCALL PC+0x0001		Relative call subroutine 
00000048  IN R28,0x3D		In from I/O location 
00000049  IN R29,0x3E		In from I/O location 
			j++;
0000004A  LDD R24,Y+1		Load indirect with displacement 
0000004B  LDD R25,Y+2		Load indirect with displacement 
0000004C  ADIW R24,0x01		Add immediate to word 
0000004D  STD Y+2,R25		Store indirect with displacement 
0000004E  STD Y+1,R24		Store indirect with displacement 
0000004F  RJMP PC-0x0005		Relative jump 

EDIT: Using latest and greatest Atmel Studio 6.1 (Version 6.1.2730 - Service Pack 2)

"I may make you feel but I can't make you think" - Jethro Tull - Thick As A Brick

"void transmigratus(void) {transmigratus();} // recursio infinitus" - larryvc

"It's much more practical to rely on the processing powers of the real debugger, i.e. the one between the keyboard and chair." - JW wek3

"When you arise in the morning think of what a privilege it is to be alive: to breathe, to think, to enjoy, to love." -  Marcus Aurelius

Last Edited: Mon. Feb 10, 2014 - 07:33 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

No other compilation unit is allowed to touch i.

You lost me there. Why not? What if another compilation unit contained:

extern int i;
.
.
.
i = PINB;

What in C disallows this scenario?

Quote:
i is effectively static.

How do you reach this conclusion?

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

skeeve wrote:
i is effectively static.
No other compilation unit is allowed to touch i.

i is effectively a global unsigned int. The fact that i is not being assigned to anything makes it an unused variable. The compiler appears to be doing its job of optimization quite correctly in this case. It could do better by also eliminating the code generated for reading in the value of i too. It does omit the code for the increment, compare, and assignment to zero.

"I may make you feel but I can't make you think" - Jethro Tull - Thick As A Brick

"void transmigratus(void) {transmigratus();} // recursio infinitus" - larryvc

"It's much more practical to rely on the processing powers of the real debugger, i.e. the one between the keyboard and chair." - JW wek3

"When you arise in the morning think of what a privilege it is to be alive: to breathe, to think, to enjoy, to love." -  Marcus Aurelius

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

But again this bring me back to the original question
I don't see a difference with a global variable used in main or in ISR (or ?) so all but Johan are wrong I have to make my static variable volatile otherwise it would be legal to optimize it away (don't happen today but perhaps tomorrow).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

larryvc wrote:
david.prentice wrote:
It can't remove i.

If optimization is set to higher than -O1 it does. Test first, talk later. :wink:
#include 

unsigned int i;

int main(void)
{
	volatile int j;
	
	while(1)
	{
		if (i++ > 4000)
		{
			i = 0;
			j++;
		}
	}
}
00000045  PUSH R28		Push register on stack 
00000046  PUSH R29		Push register on stack 
00000047  RCALL PC+0x0001		Relative call subroutine 
00000048  IN R28,0x3D		In from I/O location 
00000049  IN R29,0x3E		In from I/O location 
			j++;
0000004A  LDD R24,Y+1		Load indirect with displacement 
0000004B  LDD R25,Y+2		Load indirect with displacement 
0000004C  ADIW R24,0x01		Add immediate to word 
0000004D  STD Y+2,R25		Store indirect with displacement 
0000004E  STD Y+1,R24		Store indirect with displacement 
0000004F  RJMP PC-0x0005		Relative jump 

EDIT: Using latest and greatest Atmel Studio 6.1 (Version 6.1.2730 - Service Pack 2)

My apologies. The example is too trivial. All that happens is that j is incremented.
It effectively makes no difference whether you had 4000, 10000, ...
At the end of the day, you just get j++

Now try this:

#include 

unsigned int i;

int main(void)
{
	volatile int j;
	
	while(1)
	{
		if (i++ > 4000)
		{
			i = 0;
			j = 0;      // make j do something
		}
                else j++;
	}
}

My earlier ISR() example is very typical of Timer service routines.

David.

@sparrow2,

In a real application, everything works just fine. The compiler can only remove a variable if it makes no difference to the logic. For example, my "i" is pointless. You might just as well control the loop with "j". i.e. 0 .. 4001, 0 .. 4001, ...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

so all but Johan are wrong I have to make my static variable volatile otherwise it would be legal to optimize it away

I think my head is going to explode. :?

Where in the example you quoted:

#include 
   unsigned int i;
int main(void)
{
   volatile int j;
    while(1)
    {
      if (i++>4000)
      {
         i=0;
         j++;
      }
    }
} 

Is there a "static" ??!??!?

You appear to be talking about one concept then demonstrating another? Show your complete test program with a function static variable and show how the optimiser has "got at it" so it is not properly updated so that the next call finds it how it was last left...

(I don't believe you can produce such a thing).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Like Cliff, I am sure that many C programs have state variables in local static.

Certainly, almost every C app will have a regular Timer interrupt and have local static counters to determine which procedure(s) to do at 1ms, 10ms, 500ms, ...

Yes, most apps will be compiled with 'aggressive' -O3 or -Os optimisations.

Yes, CodeVision and ImageCraft are currently more 'benign', you still use the correct semantics e.g. volatile.

As a general rule, if a cup of tea can suggest an efficiency optimisation, GCC has probably already done it.

Since sparrow2 has an excellent knowledge of algorithms, it seems pretty straightforward to me. I bet that "sparrow2 + tea" should produce some dramatic results.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Then I have to get this precise
What is the difference of a global variable and a local static other than the scope?
From my understanding nothing but please tell me if I'm wrong

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

A local static is stored in global memory.
Its scope is only within that function.

A global static is stored in global memory.
Its scope is within that C file.

A regular global is visible to all C files.

I am sure that Wikipedia will explain things better.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So there is no difference other than the scope (at least that is what David tell, and what I think too), but then I don't get why Cliff write this :

Quote:
Is there a "static" ??!??!?

there is a global variable and it should be optimized the same way than local static. (at least from a C perspectiv).

So nobody seem to be able to show me that a local static can't legally be optimized, so I will follow Johan and make it volatile, then I think we all can agree on that the code will work.(but bee big and slow).

But then I still ask the GCC experts are there any __attributes__ etc. that can make the code more efficient and also work in the future.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes, of course a local static can be optimised. Think of a situation when it is declared but unused.

Provide a realistic example.

Volatile and Static are two different concepts. You use when appropriate.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

yes like my main question!

  if (++timercounter==4000){
    timercounter=0;
    tick++;
  } 

placed in a ISR
(timercounter is local static).
GCC don't optimize away today but would it be legal following C
if yes I will need to make it volatile.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

sparrow2 wrote:
GCC don't optimize away today but would it be legal following C
if yes I will need to make it volatile.

If that is your concern then I feel you have nothing to worry about as a static variable has a lifetime that extends across the entire run of the program. If they change the rules regarding static variables they will break many a program. Certainly many of mine.

See: http://en.wikipedia.org/wiki/Sta...

"I may make you feel but I can't make you think" - Jethro Tull - Thick As A Brick

"void transmigratus(void) {transmigratus();} // recursio infinitus" - larryvc

"It's much more practical to rely on the processing powers of the real debugger, i.e. the one between the keyboard and chair." - JW wek3

"When you arise in the morning think of what a privilege it is to be alive: to breathe, to think, to enjoy, to love." -  Marcus Aurelius

Last Edited: Tue. Feb 11, 2014 - 08:34 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

yes like my main question!
Code:
if (++timercounter==4000){
timercounter=0;
tick++;
}

placed in a ISR
(timercounter is local static).


PLEASE, do not present a snippet and some more or less descriptive text. If you want people to discuss code, then show code. If you want people to have opinions on prosaic descriptions which might leave room for vague interpretations then continue as above.

Let's try to get this down to some basics and see who agrees or not:

* Accesses to a 'static' variable can potentially be optimized away.

* This in turn might lead to the variable as such being optimized away.

* Accesses to a 'volatile' variable can never be optimized away.

* 'static' does not control optimization. It controls visibility and allocation.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

JohanEkdahl wrote:
* 'static' does not control optimization. It controls visibility and allocation.
While it does not control optimization it does guarantee that the variable will exist for the lifetime of the program. So why does it get optimized away? And it seems to optimize away in main() and in other functions but not in the ISR.

EDIT: Optimizing away in main() and other functions occurs with -O2 and -O3 but not with -O1 or -Os.

EDIT: Spelling.

"I may make you feel but I can't make you think" - Jethro Tull - Thick As A Brick

"void transmigratus(void) {transmigratus();} // recursio infinitus" - larryvc

"It's much more practical to rely on the processing powers of the real debugger, i.e. the one between the keyboard and chair." - JW wek3

"When you arise in the morning think of what a privilege it is to be alive: to breathe, to think, to enjoy, to love." -  Marcus Aurelius

Last Edited: Tue. Feb 11, 2014 - 09:11 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Larry, you are just as guilty as sparrow2 for unrealistic examples.

Sit down with a practical problem, and think through what is possible to optimise.

A local static has only got scope within its own block. So a Compiler knows exactly what it can do.
A global static has scope throughout that file. The Compiler/Optimiser knows which functions reference the static. It knows that main() is a special function that is always in operation for the lifetime.

So it can analyse the regular call graph. It also knows that ISR()s execute independently.

Your cup of tea may suggest some optimisations / eliminations that are currently not taken. For example, CV and ImageCraft don't implement several legal opportunities. So the general advice is: follow the existing C rules and not rely on current absent behaviour.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

While it does not control optimization it does guarantee that the variable will exist for the lifetime of the program.

I would add "unless it is possible to optimize it away" to that statement, but I might be wrong.

Or re-formulate to: If at all allocated, it will be statically allocated. This will have the effect that it exists throughout the lifetime of the program.

Also keep in mind that (I think I am re-phrasing part of what David said just above):

i) seeing a variable not being optimized away is not a proof for such optimizing away being impossible or "forbidden", and

ii) seeing a variable being optimized away is a proof for such optimization being possible and #allowed" (unless we are looking at a compiler bug).

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Johan
But this is more a principal question, and I was hoping for a yes or no answer (with a reference to a C99 spec x.x.a), and something like but with GCC you can use attribute xxx and it will do what you want.
But here is the code that made me ask in the first place:

//global
volatile char printflag=0;
volatile unsigned int timercounter=0;// only usend in ISR (place#1)
//unsigned int timercounter=0;// place#2

ISR(TIMER0_COMPA_vect){
//static unsigned int timercounter=0;(place#3)
  if (++timercounter==4000){
    timercounter=0;
    printflag=1;
  }
}

The org code use #1 to declare timercounter and it worked but made a clumsy code (I would expect only one read of timercounter in each run not two).
Then I tryed #2 (remove volatile) and then the code became as I wanted, but was not sure if it was safe, and that's why I asked here.
#3 is only a scope change (clean up)

And I know that counting down split into two chars could make a fast code, but this is for me a principal C question, if it was for real speed I would do ASM.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

David, you do not have to lecture me about scope, lifetime, and unrealistic examples, although it was probably time for some payback. :wink: :)

I do concur with what you and Johan are saying. I also agree that in order to guarantee, hopefully, that the variable will not be optimized away is to either declare it volatile or actually use the variable. e.g PORTB = variable;

I would like to know why the same piece of code using the same type of variables in a function, in main(), and in an ISR are not treated equally by the optimizer. Why is the code not being optimized away in the ISR? What is so special about the ISR?

Again this is with the Native GCC compiler in Studio 6.1.

/*
 * sparrow2.c
 */


#include 
#include 

unsigned int a, b, c;

void count(void)
{
    static unsigned int d;
    if (a++ > 4000) {
        a = 0;
        d++;
    }
}

int main(void)
{
    sei();

    static unsigned int e;
    while(1) {
        if (b++ > 4000)	{
            b = 0;
            e++;
        }
        count();
    }
}

ISR(TIMER0_OVF_vect)
{
    static unsigned int f;
    if (c++ > 4000) {
        c = 0;
        f++;
    }
}

"I may make you feel but I can't make you think" - Jethro Tull - Thick As A Brick

"void transmigratus(void) {transmigratus();} // recursio infinitus" - larryvc

"It's much more practical to rely on the processing powers of the real debugger, i.e. the one between the keyboard and chair." - JW wek3

"When you arise in the morning think of what a privilege it is to be alive: to breathe, to think, to enjoy, to love." -  Marcus Aurelius

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

sparrow2 wrote:
Hi Johan
But this is more a principal question, and I was hoping for a yes or no answer (with a reference to a C99 spec x.x.a), and something like but with GCC you can use attribute xxx and it will do what you want.
But here is the code that made me ask in the first place:

//global
volatile char printflag=0;
volatile unsigned int timercounter=0;// only usend in ISR (place#1)
//unsigned int timercounter=0;// place#2

ISR(TIMER0_COMPA_vect){
//static unsigned int timercounter=0;(place#3)
  if (++timercounter==4000){
    timercounter=0;
    printflag=1;
  }
}

The org code use #1 to declare timercounter and it worked but made a clumsy code (I would expect only one read of timercounter in each run not two).
Then I tryed #2 (remove volatile) and then the code became as I wanted, but was not sure if it was safe, and that's why I asked here.
#3 is only a scope change (clean up)

And I know that counting down split into two chars could make a fast code, but this is for me a principal C question, if it was for real speed I would do ASM.

place#3 would be the preferred position. Its only scope is inside the ISR()
It does not need to be volatile.
It can not be eliminated because it controls the volatile printflag.

timercounter will be stored in regular SRAM.

place#2 would be perfectly legal. But why make it visible to other modules?

place#1 would just encourage clumsy code.

I cannot believe that this sequence would ever be time-critical.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

@Larry,

My brain would eliminate d, e, f.
AFIK, C would allow each of these eliminations.

GCC knows that ISR() is an independent thread to main() and other foreground code.
So it is less inclined to be as fierce with ISR()

One day, GCC may pluck up enough courage to eliminate f too.

Untested. I take your word for it that f is still there with GCC.
I guess that CV and ImageCraft may keep d, e, f.

I might try it later.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

david.prentice wrote:
GCC knows that ISR() is an independent thread to main() and other foreground code.
So it is less inclined to be as fierce with ISR()
Sorry David, I may be wrong, but I'm not buying that one as the reason, yet.

"I may make you feel but I can't make you think" - Jethro Tull - Thick As A Brick

"void transmigratus(void) {transmigratus();} // recursio infinitus" - larryvc

"It's much more practical to rely on the processing powers of the real debugger, i.e. the one between the keyboard and chair." - JW wek3

"When you arise in the morning think of what a privilege it is to be alive: to breathe, to think, to enjoy, to love." -  Marcus Aurelius

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

I would like to know why the same piece of code using the same type of variables in a function, in main(), and in an ISR are not treated equally by the optimizer.

It's a missed optimization. The reason could be all sorts of things, including "surrounding code". What registers are already allocated, what special attributes are applied to the function etc..

Quote:
I was hoping for a yes or no answer

The old teacher in me prevents me from giving yes/no answers in most cases. It is my opinion that naswers should help the person asking to really understand and to take responsibility for his own actions.

Else we have "Can I point the barrel to my sinus and pull the trigger?". The yes/no aswer will be "yes".

Again:
'static' is about visibility (the posh CS in me says "scope"), and type of allocation (in this case static allocation which in turn affects lifetime (same posh CS says "extent").

'volatile' is about telling the compiler something it can not deduce - specifically that it may not optimize out accesses.

Re all the other "why is this NOT optimized out" sfluff above: Always code as if the compiler will be as agressive as possible by the C standard (unless you are Bob). Never cry to lout about a missed optimization, and most importantly DO NOT INTERPRET IT AS THAT THAT OPTIMIZATION WILL NEVER BE DONE.

It's the good old "Behave the best, prepare for the worst".

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

* Accesses to a 'static' variable can potentially be optimized away.

Not in reality. If you create a static:

void foo(void) {
 static int n = 12435;
}

then "yes" this could be optimised away because it's never accessed. In fact:

void foo(void) {
 static int n = 12435;
 n++;
}

this could be too. Nothing "uses" the variable.

But as soon as you use the static (and why else would you create/increment/whatever it?) to do something that involves an external input or output. For example:

void foo(void) {
 static int n = 12435;
 n++;
 if (n == 12399) {
   global = 1;
 }
}

or

void foo(void) {
 static int n = 12435;
 n++;
 if (n == 12399) {
   PORTB = 0x55;
 }
}

then there is no way on earth that the optimiser can skip access to 'n'. Each time the function is called the stored 'n' must increment and on the occasion it reaches 12399 something happens. If C didn't guarantee this, as others and I have said above then many C programs would not work as written.

Whether foo() is an ISR() or not makes no difference here.

EDIT: Just built those 4 as foo1..4() and only the first got trodden on:

foo1:
//==> void foo1(void) {
/* prologue: function */
/* frame size = 0 */
/* stack size = 0 */
.L__stack_usage = 0
	ret
foo2:
//==> void foo2(void) {
/* prologue: function */
/* frame size = 0 */
/* stack size = 0 */
.L__stack_usage = 0
//==>     n++;
	lds r24,n.1361
	lds r25,n.1361+1
	adiw r24,1
	sts n.1361+1,r25
	sts n.1361,r24
	ret
foo3:
//==> void foo3(void) {
/* prologue: function */
/* frame size = 0 */
/* stack size = 0 */
.L__stack_usage = 0
//==>     n++;
	lds r24,n.1365
	lds r25,n.1365+1
	adiw r24,1
	sts n.1365+1,r25
	sts n.1365,r24
//==>     if (n == 12399) {
	cpi r24,111
	sbci r25,48
	breq .L6
	ret
.L6:
//==>         global = 1;
	ldi r24,lo8(1)
	ldi r25,0
	sts global+1,r25
	sts global,r24
	ret
foo4:
//==> void foo4(void) {
/* prologue: function */
/* frame size = 0 */
/* stack size = 0 */
.L__stack_usage = 0
//==>     n++;
	lds r24,n.1369
	lds r25,n.1369+1
	adiw r24,1
	sts n.1369+1,r25
	sts n.1369,r24
//==>     if (n == 12399) {
	cpi r24,111
	sbci r25,48
	breq .L9
	ret
.L9:
//==>         PORTB = 0x55;
	ldi r24,lo8(85)
	out 0x5,r24
	ret

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok I guess that my answer lay in how the ISR macro(definition or what ever it is) holds the answer.
It's place in the interrupt.h file but I have to admit that I can't understand it.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well, I have just tried AS4 -Os and AS6 -Os

Neither eliminates d, e, f.

As I said earlier, my brain would eliminate d, e, f.
After all, the code is pointless.

Why would anyone ever write this code ?
One day, an Optimiser will eliminate it.

Of course, you generally access memory-mapped peripherals via a private address.
You declare the address space as volatile to ensure it is accessed properly.
i.e. just like you access the AVR's SFRs.

AS6 -O3 eliminates d, e

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

@clawson
perhaps I stupid but then please explain why i can be removed in this code:

#include  
   unsigned int i; 
int main(void) 
{ 
   volatile int j; 
    while(1) 
    { 
      if (i++>4000) 
      { 
         i=0; 
         j++; 
      } 
    } 
}

The way I read your answer i can't be removed because j is volatile, but it does because the loop make no sense

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

My brain would reduce it to:

#include 

int main(void)
{
   volatile int j;
    while(1)
    {
         j++;
    }
}

And sure enough, AS6 -O3 produces just this.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

sorry I was mixing names
But David you wrote this to my code:

Quote:
place#3 would be the preferred position. Its only scope is inside the ISR()
It does not need to be volatile.
It can not be eliminated because it controls the volatile printflag.

I see no difference!
j and printflags are both volatile!
So please explane, why I don't need volatile in ISR but do when it's in main.

And to your question why make this code. There are way more in my timer interrupt, and it runs @64000Hz so this code set a flag to main to run some slow code at 16Hz.
And that is why I don't want it to be to slow.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
#include 
#include 

//global
volatile char printflag = 0;

void main(void)
{
    static unsigned int timercounter = 0;
    while (1) {
        if (++timercounter == 4000) {
            timercounter = 0;
            printflag = 1;
        }
    }
}

ISR(TIMER0_OVF_vect)
{
    static unsigned int timercounter = 0;
        if (++timercounter == 4000) {
            timercounter = 0;
            printflag = 0;
        }
} 

My brain would say that main() reduces to while (1) printflag = 1;
Sure enough, AS4 -O3 does this.

The ISR() retains its own "timercounter" because it is straight code that can execute independently from main().

The compiler can see that printflag appears in main() and in ISR()

I am sorry, but we really need some more realistic example tests. Or a nice cup of tea.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
I am sorry, but we really need some more realistic example tests

But this is my code in question.(a ISR loopcounter in a 64KHz ISR that make a 16Hz flag nothing fancy but needed)
And now you say that it's ok in an ISR because it's a ISR and printflag are volatile from an other thread or what?
(sorry but for me I don't see a difference in the two loops)
So this really don't make me fell safe without making my static loop counter volatile because it seems that people just are guessing and see what the compiler do today, and no tomorrow.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

unless the ISR definition grantee something, but as I say I don't understand the interrupt.h file . It need at GCC expert and it's not me :(

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

No. It is quite easy to understand. The main() is entirely obvious. i.e. one single while() loop.

The ISR() can occur asynchronously from the main() foreground thread.

Since the ISR counter is static, it must retain its value between ISR() calls.

Do you have tea in Denmark ?
I have yet to see any wrong behaviour. Just imagine you are writing ASM. It it makes sense, you can do it. If it breaks the logic, you should not do it.

Seriously, there are loads of C programs with static counters in timer ISR()s. And they are quite efficient. The biggest overhead is the ISR prologue and epilogue. Using a single Timer ISR() means that adding an extra task is trivial.
After all, a "if (--counter == 0) { counter = interval; do_task(); }" is far less cycles than most prologues.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

My program is about 19Kbyte in size with about 150 byte in the ISR.
I can't change the speed of the prologue but of the code.
My prologue is 18 clk
The if(++counter==4000) is 13 clk.
So not so "far less"
I have a ISR for each 250 clk so I need to know something about the speed of the code.

Add:
and I guess that the --counter==0 only save 1 clk (two sub become a or)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
@clawson
then please explain why i can be removed in this code

Well "i" still exists:

	.section	.text.startup.main,"ax",@progbits
.global	main
	.type	main, @function
main:
//==> {
	push r28
	push r29
	rcall .
	in r28,__SP_L__
	in r29,__SP_H__
/* prologue: function */
/* frame size = 2 */
/* stack size = 4 */
.L__stack_usage = 4
.L2:
//==>             j++;
	ldd r24,Y+1
	ldd r25,Y+2
	adiw r24,1
	std Y+2,r25
	std Y+1,r24
	rjmp .L2
	.size	main, .-main
	.comm	i,2,1

Notice the .comm statement at the end - that creates "i" in the .bss section as the global (non-initialized) definition would dictate. But I guess your question is why is it excluded from the while(1) loop. Perhaps your question should really be "what purpose would "i" serve inside the while(1) if it were there?".

OK let's say i existed as you wrote then what actually happens here? It hits the if() and i gets incremented, it then loops round the while and i gets incremented again. It is 100% inevitable (because it is of int type) that at some stage it will reach 4000. If that happens it will get set back to 0 and j will increment. So all i has really done in this code is to delay (by 4000 times round the loop) how long it is before j gets incremented. But it serves no other purpose. The loop can never exit and it never calls anything that might try to access "i" so nothing outside here will ever care what "i" is set to.

In fact if you hadn't made j volatile then there'd be nothing but an "rjmp" in this loop:

main:
//==> {
/* prologue: function */
/* frame size = 0 */
/* stack size = 0 */
.L__stack_usage = 0
.L2:
	rjmp .L2

But your use of volatile says "you must access j" so the compiler generates the code for that. But it's not interested in using "i" in the loop as it's nothing more than a delay. You might have written all this as:

#include 
   unsigned int i;
int main(void)
{
   volatile int j;
    while(1)
    {
      for(i=0; i<4000; i++);
      j++;
    }
} 

and you would have got the same result:

main:
//==> {
	push r28
	push r29
	rcall .
	in r28,__SP_L__
	in r29,__SP_H__
/* prologue: function */
/* frame size = 2 */
/* stack size = 4 */
.L__stack_usage = 4
.L2:
//==>         j++;
	ldd r24,Y+1
	ldd r25,Y+2
	adiw r24,1
	std Y+2,r25
	std Y+1,r24
//==>     }
	rjmp .L2

The optimizer will remove code that just delays and has no effect on inputs/outputs.

EDIT: oops - missed the fact there was page 3

BTW if "i" actually served some purpose other than to simply delay execution:

void foo (void) {
    if (i == 1234) {
        PORTB ^= 0xFF;
    }
}

int main(void)
{
    volatile int j;
    while(1)
    {
        foo();
        if (i++>4000)
        {
            i=0;
            j++;
        }
    }
}

then the compiler would happily (have to) create code to access it:

	.section	.text.foo,"ax",@progbits
.global	foo
	.type	foo, @function
foo:
//==> void foo (void) {
/* prologue: function */
/* frame size = 0 */
/* stack size = 0 */
.L__stack_usage = 0
//==>     if (i == 1234) {
	lds r24,i
	lds r25,i+1
	cpi r24,-46
	sbci r25,4
	breq .L4
	ret
.L4:
//==>         PORTB ^= 0xFF;
	in r24,0x5
	com r24
	out 0x5,r24
	ret
	.size	foo, .-foo

	.section	.text.startup.main,"ax",@progbits
.global	main
	.type	main, @function
main:
//==> {
	push r28
	push r29
	rcall .
	in r28,__SP_L__
	in r29,__SP_H__
/* prologue: function */
/* frame size = 2 */
/* stack size = 4 */
.L__stack_usage = 4
.L10:
//==>         foo();
	call foo
//==>         if (i++>4000)
	lds r24,i
	lds r25,i+1
	movw r18,r24
	subi r18,-1
	sbci r19,-1
	sts i+1,r19
	sts i,r18
	cpi r24,-95
	sbci r25,15
	brlo .L10
//==>             i=0;
	sts i+1,__zero_reg__
	sts i,__zero_reg__
//==>             j++;
	ldd r24,Y+1
	ldd r25,Y+2
	adiw r24,1
	std Y+2,r25
	std Y+1,r24
	rjmp .L10
	.size	main, .-main
	.comm	i,2,1

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

sparrow2 wrote:
My program is about 19Kbyte in size with about 150 byte in the ISR.
I can't change the speed of the prologue but of the code.
My prologue is 18 clk
The if(++counter==4000) is 13 clk.
So not so "far less"
I have a ISR for each 250 clk so I need to know something about the speed of the code.

Add:
and I guess that the --counter==0 only save 1 clk (two sub become a or)

Well, an 18-cycle prologue probably has a 16-cycle epilogue.
Saving one cycle is beter than nothing.

If your printflag IRQ is only executed every 1M cycles, using a separate IRQ vector would save that 13-cycles completely. You can afford a small latency every 4000 IRQs.

Not only that. Simplifying each ISR() can have a dramatic effect on prologues.

As I said earlier, you have the same design considerations with an ASM program. The advantage of C is that you can use more complex algorithms if they improve efficiency.

Most ISR()s are "short and sweet". So simplicity generally trumps complexity.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

There is a reason for me to run the ISR at 64KHz, so a 2. interrupt would break my timing. (And I don't want to hack nested interrupts).
Like most real time systems I don't care about AVG speed (I have plenty of power), but worst case.
And that is C's main problem a new and better compiler will go for a better AVG, and not worst case :(
And just change one line could change worst case a lot because now the optimizer have found a "better" way to solve the problem.
So you will need some more headroom to make sure that it will work in the future.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

and this:

Quote:
As I said earlier, you have the same design considerations with an ASM program. The advantage of C is that you can use more complex algorithms if they improve efficiency.

Not really because there I would keep a counter like this in a register pair and spend 3 clk in each interrupt on the count and check (either have this in high registers or have a low register with the value of 0xff at all time).
Add:
or 2 clk if ugly but fast :)

And yes I know that I can do similar things in C,
But when I write in C it's because it should be easy to read, especially by others, and not all kind of none standard C adds on.

Pages