What case compiler optimize the variable

Thread Starter

Kittu20

Joined Oct 12, 2022
432
Hello Everyone,

I am getting confused when I read about the volatile keyword in a c language. The definition says that the volatile keyword informs the compiler that the value of a variable may change at any time from outside. It also instructs the compiler to read the value of the variable each time before using it. I see this is commonly used for variables that are accessed in an Interrupt Service Routine (ISR) or when reading pin status.

My question is, in a C program, there can be multiple variables, so why does the compiler optimize specific variables and not others? When the program doesn't read the value from the original address, what does it read and where does it read it from?
 

BobTPH

Joined Jun 5, 2013
8,674
Consider a local variable in a function. Assume its address is never taken and stored as a pointer. The compiler knows that variable cannot be altered by any other function, including an interrupt handler. So, when optimizing that function, the compiler knows all places that can alter that variable.

If you take the the address and assign it to a global pointer, then any function call might alter it via the pointer, so the compiler assumes any function call can alter it.
 

nsaspook

Joined Aug 27, 2009
12,830
XC8
https://onlinedocs.microchip.com/pr...tml?GUID-CF91148E-EEFA-4F44-9F63-389808A847A1
The minimal code generator optimizations consist of the following.

  • Whole-program analysis for object allocation into data banks without having to use non-standard keywords or compiler directives.
  • Simplification and folding of constant expressions to simplify expressions.
  • Expression tree optimizations to ensure efficient assembly generation.
  • Propagation of constants is performed where the numerical contents of a variable can be determined. Variables which are not volatile and whose value can be exactly determined are replaced with the numerical value. Uninitialized global variables are assumed to contain zero prior to any assignment to them.
  • Unreachable code is removed. C Statements that cannot be reached are removed before they generate assembly code. This allows subsequent optimizations to be applied at the C level.
The following is a list of more advanced code generation (C-level) optimizations, which simplify C expressions or code produced from C expressions. These are applied across the entire program, not just on a module-by-module basis.

  • Tracking of the current data bank is performed by the compiler as it generates assembly code. This allows the compiler to reduce the number of bank-selection instructions generated.
  • Strength reductions and expression transformations are applied to all expression trees before code is generated. This involves replacing expressions with equivalent, but less costly operations.
  • Unused variables in a program are removed. This applies to all variables. Variables removed will not have memory reserved for them, will not appear in any list or map file, and will not be present in debug information (will not be observable in the debugger). A warning is produced if an unused variable is encountered. Global objects qualified volatile will never be removed (see Volatile Type Qualifier). Taking the address of a variable or referencing its assembly-domain symbol in hand-written assembly code also constitutes use of the variable.
  • Redundant assignments to variables not subsequently used are removed, unless the variable is volatile. The assignment statement is completely removed, as if it was never present in the original source code. No code will be produced for it and you will not be able to set a breakpoint on that line in the debugger.
  • Unused functions in a program are removed. A function is considered unused if it is not called, directly or indirectly, nor has had its address taken. The entire function is removed, as if it was never present in the original source code. No code will be produced for it and you will not be able to set a breakpoint on any line in the function in the debugger. Referencing a function’s assembly-domain symbol in a separate hand-written assembly module will prevent it being removed. The assembly code need only use the symbol in the GLOBAL directive.
  • Unused return expressions in a function are removed. The return value is considered unused if the result of all calls to that function discard the return value. The code associated with calculation of the return value will be removed and the function will be encoded as if its return type was void.
  • Variables assigned a value before being read are not cleared or initialized by the runtime startup code. Only non-auto variables are considered and if they are assigned a value before other code can read their value, they are treated as being __persistent (see )Bank Type Qualifier. All __persistent objects are not cleared by the runtime startup code, so this optimization will speed execution of the program startup.
  • Pointer sizes are optimized to suit the target objects they can access. The compiler tracks all assignments to pointer variables and keeps a list of targets each pointer can access. As the memory space of each target is known, the size and dereference method used can be customized for each pointer.
  • Dereferencing pointers with only target can be replaced with direct access of the target object. This applies to data and function pointers.
 

Thread Starter

Kittu20

Joined Oct 12, 2022
432
Consider a local variable in a function. Assume its address is never taken and stored as a pointer. The compiler knows that variable cannot be altered by any other function, including an interrupt handler. So, when optimizing that function, the compiler knows all places that can alter that variable.
That's correct. When a local variable in a function is not accessed through a pointer or its address isn't taken and stored for later use outside of the function, the compiler can optimize its usage. The compiler knows that the variable is local to the function and cannot be accessed or modified by external code, so it can make certain assumptions and optimizations.

If you take the the address and assign it to a global pointer, then any function call might alter it via the pointer, so the compiler assumes any function call can alter it.
could you please provide a more in-depth explanation on above statement

The value of a variable can be modified through the pointer if we assign the address of the variable to the pointer,
 

BobTPH

Joined Jun 5, 2013
8,674
That's correct. When a local variable in a function is not accessed through a pointer or its address isn't taken and stored for later use outside of the function, the compiler can optimize its usage. The compiler knows that the variable is local to the function and cannot be accessed or modified by external code, so it can make certain assumptions and optimizations.


could you please provide a more in-depth explanation on above statement

The value of a variable can be modified through the pointer if we assign the address of the variable to the pointer,
Right which means it might be modified by any code not included in our analysis.
 

WBahn

Joined Mar 31, 2012
29,874
Hello Everyone,

I am getting confused when I read about the volatile keyword in a c language. The definition says that the volatile keyword informs the compiler that the value of a variable may change at any time from outside. It also instructs the compiler to read the value of the variable each time before using it. I see this is commonly used for variables that are accessed in an Interrupt Service Routine (ISR) or when reading pin status.

My question is, in a C program, there can be multiple variables, so why does the compiler optimize specific variables and not others? When the program doesn't read the value from the original address, what does it read and where does it read it from?
Variables are often placed in a register so that they are quickly accessible. So if you have a variable, say fred, stored at memory location 0x100 that some other process can access and change, but you have a function that copies the value from that variable into a process register and then uses the value in the register whenever the variable fred it used, if the external process changes the value stored at 0x100 your function won't see the change because it is using the value stored in the register, which it has no reason to believe can have a different value than when it copied it into the register because it believes that it have complete visibility and control over when the values in memory can change. The volatile attribute lets the compiler know that this isn't the case for that particular variable, and so the compiler can't make such assumptions.
 

Thread Starter

Kittu20

Joined Oct 12, 2022
432
I get that it's used to signal the compiler that a variable's value might change from outside. But what exactly does "outside" refer to in this context.

Does it specifically mean changes by other functions within the program?
 

WBahn

Joined Mar 31, 2012
29,874
I get that it's used to signal the compiler that a variable's value might change from outside. But what exactly does "outside" refer to in this context.

Does it specifically mean changes by other functions within the program?
It means from any were outside the current function. Often it is when something unrelated to the program can change it. For instance, if your program is reading an input port on the hardware that is reading the state of the lights in a facility, the values that it reads can change independent of the program that is running because someone flips a switch or a light burns out. Letting the compiler know that the variable that the hardware is storing that information in is volatile let's it know that every time it uses that information, it needs to read it fresh and not rely on some copy it placed in some register some time back.
 

xox

Joined Sep 8, 2017
838
I get that it's used to signal the compiler that a variable's value might change from outside. But what exactly does "outside" refer to in this context.


Does it specifically mean changes by other functions within the program?
Normally, the compiler is free to optimize your program to one extent or another based on the (rather loose) assumption that it can keep track of all points where read/write of a given variable can possibly take place.

Of course that isn't always the case. Both multithreaded coroutines and interrupt service routines for example can change the value of a variable in a way that the compiler might not have anticipated. Say it decides to make a copy of the variable and store it in a register for later use when in fact it should have dereferenced a pointer at that point to obtain the current value.

The volatile keyword simply tells the compiler that no optimizations whatsoever should be applied to a particular variable.
 
Top