Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to force an unused memory read in C that won't be optimized away?

Microcontrollers often require a register to be read to clear certain status conditions. Is there a portable way in C to ensure that a read is not optimized away if the data is not used? Is it sufficient that the pointer to the memory mapped register is declared as volatile? In other words, would the following always work on standard compliant compilers?

void func(void)
{
   volatile unsigned int *REGISTER = (volatile unsigned int *) 0x12345678;

   *REGISTER;
}

I understand that dealing with functionality like this runs into compiler-dependent issues. So, my definition of portable is a bit loose in this case. I just mean that it would work as widely as possible with the most popular toolchains.

like image 966
Judge Maygarden Avatar asked Sep 07 '25 09:09

Judge Maygarden


2 Answers

People argue quite strenuously about exactly what volatile means. I think most people agree that the construct you show was intended to do what you want, but there is no general agreement that the language in the C standard actually guarantees it as of C99. (The situation may have been improved in C2011; I haven't read that yet.)

A nonstandard, but fairly widely supported by embedded compilers, alternative that may be more likely to work is

void func(void)
{
  asm volatile ("" : : "r" (*(unsigned int *)0x12345678));
}

(The 'volatile' here appies to the 'asm' and means 'this may not be deleted even though it has no output operands. It is not necessary to put it on the pointer as well.)

The major remaining drawback of this construct is that you still have no guarantee that the compiler will generate a one-instruction memory read. With C2011, using _Atomic unsigned int might be sufficient, but in the absence of that feature, you pretty much have to write a real (nonempty) assembly insert yourself if you need that guarantee.

EDIT: Another wrinkle occurred to me this morning. If reading from the memory location has the side-effect of changing the value at that memory location, you need

void func(void)
{
  unsigned int *ptr = (unsigned int *)0x12345678;
  asm volatile ("" : "=m" (*ptr) : "r" (*ptr));
}

to prevent mis-optimization of other reads from that location. (To be 100% clear, this change will not change the assembly language generated for func itself, but may affect optimization of surrounding code, particularly if func is inlined.)

like image 126
zwol Avatar answered Sep 09 '25 02:09

zwol


Yes, the C standard guarantees that code accessing a volatile variable will not be optimized away.

C11 5.1.2.3/2

"Accessing a volatile object, " ... "are all side effects"

C11 5.1.2.3/4

"An actual implementation need not evaluate part of an expression if it can deduce that its value is not used and that no needed side effects are produced (including any caused by calling a function or accessing a volatile object)."

C11 5.1.2.3/6

"The least requirements on a conforming implementation are:

— Accesses to volatile objects are evaluated strictly according to the rules of the abstract machine."

like image 25
Lundin Avatar answered Sep 09 '25 01:09

Lundin