Helpful ReplyHot!Issue with incrementing and setting a global variable in XC32

Page: < 12 Showing page 2 of 2
Author
Gort2015
Klaatu Barada Nikto
  • Total Posts : 2645
  • Reward points : 0
  • Joined: 2015/04/30 10:49:57
  • Location: 0
  • Status: offline
Re: Issue with incrementing and setting a global variable in XC32 2018/06/13 13:36:18 (permalink)
+2 (2)
That is what I said in post #3, write to a SFR so that the compiler knows that they are in use.

MPLab X playing up, bug in your code? Nevermind, Star Trek:Discovery will be with us soon.
https://www.youtube.com/watch?v=Iu1qa8N2ID0
+ ST:Continues, "What Ships are Made for", Q's back.
#21
moser
Super Member
  • Total Posts : 353
  • Reward points : 0
  • Joined: 2015/06/16 02:53:47
  • Location: 0
  • Status: online
Re: Issue with incrementing and setting a global variable in XC32 2018/06/15 04:09:36 (permalink)
+3 (3)
As others have pointed out, in this case the debugger is probably the problem, why you are observing this.
 
However, as Gort2015 and NKurzman have pointed out, in general you seem to having wrong expectations, about being able to observe a certain state of the variables by debugging. Compilers do optimization. And they are free to do any sequence of instructions as long as the result and side effects are the same. What they choose to execute may be completely different from what you might expect.
 
To give a simple example, if you have x, y, z declared as unsigned int and you have a code fragment like 
x++
y++
z++

then you have to notice, that all those statements are completely independent of each other. Therefore the compiler is free to reorder those. He might do 
z++
x++
y++
or any of the other four sequences. Usually he will not do it in this case, because it is not more efficient, but it is perfectly valid for the compiler to do so.
 
If you now want to set a breakpoint at a place before z++ is executed, you have to be aware that the state, which the variables would have in your code at this breakpoint, might never exist in the instruction sequence which the compiler did choose.
 
Another thing is that the processor does not operate on memory but on registers. So to increment a variable, it loads the value from memory into a register (LW). Then it increments the register (ADDIU). And then it writes back the register back to memory (SW). Now, you have to realize, that if you have two instructions, which manipulate the same variable, the compiler might realize, that it is unnecessary to store the intermediate result back to memory and load it again. Instead he will just continue to use the result from the register. When you debug, the debugger might realize, that this variable is located in a register at certain statements and display its value correctly, but I believe sometimes it just gets confused.
 
The compiler can even combine instructions. For example if you have an instruction like 
x++
// some code here not involving x
x++

The compiler could replace it with 
x+=2
// some code here not involving x

 
Now let's do a final step: The compiler might realize that you increment your x, y and z. But you never use it. No other code does ever check the state of these variable. So, if the compiler decides to remove the increment instructions it does not change the overall result of your program and does not cause any side effects. And because of this your code
unsigned int x = 5;
unsigned int y = 5;
unsigned int z = 5;
 
int main(void)
{
  while(1)
  {
    x++;
    y++;
    z++;
  }
}
is exactly the same as:
unsigned int x = 5;
unsigned int y = 5;
unsigned int z = 5;
 
int main(void)
{
  while(1);
}
regarding the final result.
 
Now, if you look at the assembly, all those things described above did not happen in your case. But what I want to tell you, is that you must be careful with your expectations about debugging. The state, which you prefer to observe might not be observable at all, because the compiler is using a completely different sequence of instructions, than your code, but which will give the same result.
 
 
If you want to make sure a change of variable actually happens in memory at a certain point you only have few ways to do it.
 
One is to use a volatile variable:
volatile unsigned int x = 5;
Changes and reads to such a variable must be done by the compiler and can not be optimized away. And that is why Gort2015's way of writing to a SFR works, because all SFRs are declared volatile. But note, that other statements without volatile still can be reordered with a statement containing a volatile variable. 
 
Another way is to use a memory barrier, or memory fence, like 
__asm__ __volatile__ ("" : : : "memory");

It tells the compiler, there is some assembler code (which is actually empty, but that doesn't matter) about which the compiler should not make assumptions, and this code could read or write any memory. So before this line the processor needs to write every result back into memory and after it, read everything from there.
 
Another way is that if you call a function from another compile unit (.c file) or leave a (not static) function, the compiler needs to write back all variables to memory. But it is dangerous to rely on this effect, because as soon as you increase your compiler optimization (in particular when you turn on link time optimization) this effect is gone.
 
#22
Jim Nickerson
User 452 _
  • Total Posts : 4843
  • Reward points : 0
  • Joined: 2003/11/07 12:35:10
  • Location: San Diego, CA
  • Status: online
Re: Issue with incrementing and setting a global variable in XC32 2018/06/15 05:28:58 (permalink)
0
moser
Another way is to use a memory barrier, or memory fence, like 
__asm__ __volatile__ ("" : : : "memory");

It tells the compiler, there is some assembler code (which is actually empty, but that doesn't matter) about which the compiler should not make assumptions, and this code could read or write any memory. So before this line the processor needs to write every result back into memory and after it, read everything from there.
 

I agree with moser.
As detailed in <xc.h>.
There is the predefined macro
_ehb()
which also inserts the Execution Hazard Barrier instruction.
And I note
_jr_hb()
jump register with hazard barrier.
#23
Page: < 12 Showing page 2 of 2
Jump to:
© 2018 APG vNext Commercial Version 4.5