-1

Code for an interrupt service handler:

volatile unsigned char x = 0;
void interruptHandler() __attribute__ ((signal));
void interruptHandler() {
    f();
    g();
}

Calls:

void f() { x ++; } // could be more complex, could also be in a different file
void g() { x ++; } // as `f()`, this is just a very simple example

Because x is a volatile variable, it is read and written every time it is used. The body of the interrupt handler compiles to (avr-gcc -g -c -Wa,-alh -mmcu=atmega328p -Ofast file.c):

lds r24,x
subi r24,lo8(-(1))
sts x,r24
lds r24,x
subi r24,lo8(-(1))
sts x,r24

Now I can manually inline the functions and employ a temporary variable:

unsigned char y = x;
y ++;
y ++;
x = y;

Or I can just write:

x += 2;

Both examples compile to the much more efficient:

lds r24,x
subi r24,lo8(-(2))
sts x,r24

Is it possible to tell avr-gcc to optimize access to volatile variables inside of interruptHandler, i.e. to do my manual optimization automatically?

After all, while interruptHandler is running, global interrupts are disabled, and it is impossible for x to change. I prefer not having to hand optimize code, thereby possibly creating duplicate code (if f() and g() are needed elsewhere) and introducing errors.

feklee
  • 7,555
  • 9
  • 54
  • 72
  • 1
    I'm probably missing something here, but if x doesn't change, why aren't you doing `x += 2`? – Mat May 30 '18 at 11:43
  • 1
    What is ugly and confusing about calculating the new value of `x` in the temporary variable `y`? Just add a comment which explains that you are using the temporary variable `y` to prevent multiple reads of the volatile variable `x`. – kkrambo May 30 '18 at 12:32
  • I was in a similar situation. I have a timer interrupt routine which increments a global tick count, declared as volatile. That variable was also used inside the ISR for other purposes. The compiler issued inefficient code until I introduced a local copy of the tick count. I don't know whether an union with a volatile and a non-volatile version of the variable would work, but would render the code unreadable. Use a local copy and let the compiler do the optimization work. – user5329483 May 30 '18 at 16:29
  • 1
    the compiler is doing what you told it to do in an optimized way. the two examples you have are not the same nor can they be interpreted the same by the compiler. if you want such an optimization you need to do it yourself as you have demonstrated, not something the compiler can do based on the language you are using. if you want volatile you get volatile (hopefully) if you dont want volatile behavior then dont declare it volatile, solve it some other way. – old_timer May 30 '18 at 17:28
  • @kkrambo I slightly increased complexity in the example. Even if the code inside the interrupt handler compiles to a few dozen instructions, it may be spread across different functions for readability. In this case, using temporary variables is hard. And, besides this is something that I prefer the compiler to do by itself. – feklee May 31 '18 at 09:11
  • @Mat Edited now, to make the example a bit closer to real-world issues. – feklee May 31 '18 at 09:27
  • How could the compiler know that is should emit code for non-volatile reads/writes to `x` when it is compiling `f` or `g`? – Mat May 31 '18 at 11:16
  • @Mat With `-Ofast`, the compiler is inlining `f()` and `g()`. If those functions are in separate files, the linker can do that job, given `-flto` has been specified (LTO = *Link Time Optimization*). That much I understand. If the linker can — or could be modified to — emit code for “non-volatile reads/writes”, I don’t know. *Maybe someone can discuss that in an answer?* – feklee May 31 '18 at 11:57

2 Answers2

1

Is it possible to tell avr-gcc to optimize access to volatile variables inside of interruptHandler, i.e. to do my manual optimization automatically?

No, that is not possible in the C language.

After all, while interruptHandler is running, global interrupts are disabled

The compiler does not know this - and you could simply put an sei into the handler to turn them back on.

Also note that hardware registers are declared volatile, too. Some of these - like the UART data register - have side effects even when read. The compiler must not remove any reads or writes for these.

Turbo J
  • 7,563
  • 1
  • 23
  • 43
  • Please note that I was asking in particular about `avr-gcc`, which does have function attributes and command line options that do enable non-standard compliant optimizations. So I am hoping that I can tell the compiler explicitly: *At my own risk, I hereby instruct you to ignore the `volatile` keyword for variable accesses in `interruptHandler()`!* – feklee Jun 13 '18 at 14:03
  • Hardware register access must be done exactly as stated - especially in interrupt handlers - and these would no longer work for you, as the compiler would "optimize" these, too. Changing `f()` and `g()` functions is *much* simpler to implement than your proposed solution. – Turbo J Jun 14 '18 at 09:00
0

If you declare a variable to be volatile, then all accesses to it are volatile - the compiler will read and write it exactly as many times as the source code says, without combining them or doing similar optimisations.

So if you want combining optimisations, declare the variable without the "volatile" - then you will get what you need inside the interrupt code.

And then from outside the interrupt code, you can force volatile accesses using something like this macro:

#define volatileAccess(v) *((volatile typeof((v)) *) &(v))

Use "volatileAccess(x)" rather than "x" outside the interrupt code.

Just don't forget that "volatile" does not mean "atomic" !

David
  • 132
  • 3