How can I prevent GCC optimizing some statements in C?
Asked Answered
L

3

151

In order to make a page dirty (switching on the dirty bit in the page table entry), I touch the first bytes of the page like this:

pageptr[0] = pageptr[0];

But in practice, GCC will ignore the statement by dead store elimination. In order to prevent GCC optimizing it, I rewrite the statement as follows:

volatile int tmp;
tmp = pageptr[0];
pageptr[0] = tmp;

It seems the trick works, but somewhat ugly. I would like to know is there any directives or syntax which has the same effect? And I don't want to use a -O0 flag, since it will bring great performance penalty as well.

Liatrice answered 8/2, 2010 at 5:50 Comment(2)
@Mark -O0 will stop the optimization, but also slows down program performance. I just want to prevent optimization of this code snippet :PLiatrice
I would like to add that in the past, even using -O0 did not prevent dead code "optimization", e.g., when GCC detects some code has no effect, it simply removes it. AFAIK this is a stage even before -O0... But that's just my experienceHodden
T
118

Turning off optimization fixes the problem, but it is unnecessary. A safer alternative is to make it illegal for the compiler to optimize out the store by using the volatile type qualifier.

// Assuming pageptr is unsigned char * already...
unsigned char *pageptr = ...;
((unsigned char volatile *)pageptr)[0] = pageptr[0];

The volatile type qualifier instructs the compiler to be strict about memory stores and loads. One purpose of volatile is to let the compiler know that the memory access has side effects, and therefore must be preserved. In this case, the store has the side effect of causing a page fault, and you want the compiler to preserve the page fault.

This way, the surrounding code can still be optimized, and your code is portable to other compilers which don't understand GCC's #pragma or __attribute__ syntax.

Tressa answered 8/2, 2010 at 5:53 Comment(22)
I would say this is preferable to turning off optimizations. You can still benefit from other optimizations using this method.Shastashastra
Do you really need to do a load from the page, though? Surely just the store would do: *(volatile int *)pageptr = 0;Touchy
You need to modify the program which will make it humanly unreadable.Byandby
@Touchy - your version could cause the contents of the page to change. The OP implies that the page contents should not change.Sedberry
True, I read something into it that wasn't there (of course, the given solution here also would change the page, if the type of pageptr[0] is wider than char).Touchy
@Touchy - missed the int/char issue. @DietrichEpp - it looks like your fix should be *(unsigned char volatile *)pageptr = *(unsigned char *)pageptr;Sedberry
Dietrich Epp's solution is not working under ARM4.1 compiler. Even ZelluX's solution is not working. Alternative method to make this work for ARM4.1 is in ZelluX's solution, make 'temp' a global volatile variable.Klenk
That's pretty bad for said compiler.Lockard
This doesn't work for GCC 6.2.0 with -O2. My code volatile Object* obj = ... and result obj = <optimised out>Polysynthetic
@Shocker: GCC can still optimize out the variable without optimizing out the actual memory access. Those are different issues.Tressa
"One purpose of volatile is to let the compiler know that the memory access has side effects..." - Under GCC that is not true. The GCC folks state the purpose of volatile is for memory mapped hardware. From Ian Lance Taylor's blog on volatile: "In summary, if you are using volatile for anything other than manipulating memory mapped hardware, or for very limited communication between threads, it is very likely that you are making a mistake. Think carefully about what volatile means and about what it does not mean."Ursal
@jww: this usage fits with what is described in that blog post. volatile means that the memory access must occur as written, which is exactly what we want. In other words, we have thought carefully about it, and it means what we think it means.Tressa
then why call it "volatile"...it really means the opposite of "exactly what we want" or "strict"Wellfound
@pf12345678910: One reason why access is strict is because the underlying memory may change depending on when it is read. In other words, it is a “volatile” memory location that may change even when you don’t write to it. Because the variable is volatile, the access must be done in a precise, strict way.Tressa
no I mean volatile means it will evaporate, which is the opposite of strictWellfound
@pf12345678910: The variable is volatile. If you write a value to it, you might read a different value back. The value that you wrote evaporated, because the variable is volatile.Tressa
With nonvolatile memory, when you write a value, you get the same value back afterwards. It sticks around.Tressa
@DietrichEpp: That depends. Some kinds of non-volatile storage may only be written if they hold 0xFF, except when using a special mechanism to reset a large range of storage to 0xFF.Incommensurable
@supercat: The word volatile can mean something different in different contexts. The context here is the C language, so we’re trying to understand what the volatile keyword means here. If you have non-volatile memory of some kind and you want to write to it from C, paradoxically, you need the volatile keyword for that.Tressa
C’s notion of volatile is that loads and stores have some other effect, besides just making it so that you can read the same value back afterwards, during the same program run. If you have EEPROM or something, then one effect of writing a value is that you flip bits to zero, another effect is that you can read the EEPROM later after turning the power off and back on. Hence, volatile, by C standards.Tressa
@DietrichEpp: The lack of a volatile qualfier means that a compiler may assume that anything its generated code writes to a storage location will still be there the next time its generated code reads that location, but a compiler may process source code that stores a value in ways that don't actually create, much less store, the value anywhere, so there might not be anything that could stick around.Incommensurable
@Incommensurable This topic has already been discussed elsewhere, I don’t think this is a good place to try and replicate the discussion. See: https://mcmap.net/q/15361/-why-is-volatile-needed-in-cTressa
L
238

You can use

#pragma GCC push_options
#pragma GCC optimize ("O0")

your code

#pragma GCC pop_options

to disable optimizations since GCC 4.4.

See the GCC documentation if you need more details.

Lorinalorinda answered 8/2, 2010 at 9:29 Comment(3)
It is worth noting however that this only works on entire functions, not on specific statments: gcc.gnu.org/onlinedocs/gcc-4.8.2/gcc/… "Each function that is defined after this point is as if attribute((optimize("STRING"))) was specified for that function.".Curative
Can you do the reverse to optimise a single function only @CiroSantilliOurBigBook.comTouchmenot
@Touchmenot I'd try pragma GCC optimize ("O3"): stackoverflow.com/questions/47222127/…Curative
L
179

Instead of using the new pragmas, you can also use __attribute__((optimize("O0"))) for your needs. This has the advantage of just applying to a single function and not all functions defined in the same file.

Usage example:

void __attribute__((optimize("O0"))) foo(unsigned char data) {
    // unmodifiable compiler code
}
Luciferous answered 28/1, 2012 at 10:16 Comment(3)
What if I am not using a-Oleveloption but I used the individuals options it turns on separetely? (In my case, I can't determine which is the individual optimization option which is breaking the code).Spongioblast
None of the recommendations around volatile work any more. GCC compiler headers cannot handle volatile + O0. While loops are optimized out as -faggressive-loop-optimization is default, only O0 blocks this. For these reasons, O0 and no volatile in the unoptimized function is now the correct answer.Closelipped
To update for C23, you can also use the attribute specifier syntax introduced in the new standard: [[gnu::optimize("O0")]] void foo(). The same goes for C++11 and newer.Stability
T
118

Turning off optimization fixes the problem, but it is unnecessary. A safer alternative is to make it illegal for the compiler to optimize out the store by using the volatile type qualifier.

// Assuming pageptr is unsigned char * already...
unsigned char *pageptr = ...;
((unsigned char volatile *)pageptr)[0] = pageptr[0];

The volatile type qualifier instructs the compiler to be strict about memory stores and loads. One purpose of volatile is to let the compiler know that the memory access has side effects, and therefore must be preserved. In this case, the store has the side effect of causing a page fault, and you want the compiler to preserve the page fault.

This way, the surrounding code can still be optimized, and your code is portable to other compilers which don't understand GCC's #pragma or __attribute__ syntax.

Tressa answered 8/2, 2010 at 5:53 Comment(22)
I would say this is preferable to turning off optimizations. You can still benefit from other optimizations using this method.Shastashastra
Do you really need to do a load from the page, though? Surely just the store would do: *(volatile int *)pageptr = 0;Touchy
You need to modify the program which will make it humanly unreadable.Byandby
@Touchy - your version could cause the contents of the page to change. The OP implies that the page contents should not change.Sedberry
True, I read something into it that wasn't there (of course, the given solution here also would change the page, if the type of pageptr[0] is wider than char).Touchy
@Touchy - missed the int/char issue. @DietrichEpp - it looks like your fix should be *(unsigned char volatile *)pageptr = *(unsigned char *)pageptr;Sedberry
Dietrich Epp's solution is not working under ARM4.1 compiler. Even ZelluX's solution is not working. Alternative method to make this work for ARM4.1 is in ZelluX's solution, make 'temp' a global volatile variable.Klenk
That's pretty bad for said compiler.Lockard
This doesn't work for GCC 6.2.0 with -O2. My code volatile Object* obj = ... and result obj = <optimised out>Polysynthetic
@Shocker: GCC can still optimize out the variable without optimizing out the actual memory access. Those are different issues.Tressa
"One purpose of volatile is to let the compiler know that the memory access has side effects..." - Under GCC that is not true. The GCC folks state the purpose of volatile is for memory mapped hardware. From Ian Lance Taylor's blog on volatile: "In summary, if you are using volatile for anything other than manipulating memory mapped hardware, or for very limited communication between threads, it is very likely that you are making a mistake. Think carefully about what volatile means and about what it does not mean."Ursal
@jww: this usage fits with what is described in that blog post. volatile means that the memory access must occur as written, which is exactly what we want. In other words, we have thought carefully about it, and it means what we think it means.Tressa
then why call it "volatile"...it really means the opposite of "exactly what we want" or "strict"Wellfound
@pf12345678910: One reason why access is strict is because the underlying memory may change depending on when it is read. In other words, it is a “volatile” memory location that may change even when you don’t write to it. Because the variable is volatile, the access must be done in a precise, strict way.Tressa
no I mean volatile means it will evaporate, which is the opposite of strictWellfound
@pf12345678910: The variable is volatile. If you write a value to it, you might read a different value back. The value that you wrote evaporated, because the variable is volatile.Tressa
With nonvolatile memory, when you write a value, you get the same value back afterwards. It sticks around.Tressa
@DietrichEpp: That depends. Some kinds of non-volatile storage may only be written if they hold 0xFF, except when using a special mechanism to reset a large range of storage to 0xFF.Incommensurable
@supercat: The word volatile can mean something different in different contexts. The context here is the C language, so we’re trying to understand what the volatile keyword means here. If you have non-volatile memory of some kind and you want to write to it from C, paradoxically, you need the volatile keyword for that.Tressa
C’s notion of volatile is that loads and stores have some other effect, besides just making it so that you can read the same value back afterwards, during the same program run. If you have EEPROM or something, then one effect of writing a value is that you flip bits to zero, another effect is that you can read the EEPROM later after turning the power off and back on. Hence, volatile, by C standards.Tressa
@DietrichEpp: The lack of a volatile qualfier means that a compiler may assume that anything its generated code writes to a storage location will still be there the next time its generated code reads that location, but a compiler may process source code that stores a value in ways that don't actually create, much less store, the value anywhere, so there might not be anything that could stick around.Incommensurable
@Incommensurable This topic has already been discussed elsewhere, I don’t think this is a good place to try and replicate the discussion. See: https://mcmap.net/q/15361/-why-is-volatile-needed-in-cTressa

© 2022 - 2024 — McMap. All rights reserved.