volatile int typecast return value. Could this make problem?

Hello experts, I'm a beginner of embedded system development.

I have question.about difference of two code.

at first, suppose there is function like

   int SomeFunction(void) {

      ......

       return (some integer);

}

And using this function return value like these

  • int retval;

       retval = SomeFunction();

       if (retval == {some Variable})  {

         . . .

       }

  • if ( {some Variable} == (volatile int) SomeFunction()) {

          ....

        }

    

former one doesn't make any problem, but when i used like latter one.. some times, retval is not correct and some strange value remains on r0 register...;

I don't know what is problem on latter one exactly...;

Could explain what is the problem of these?

Thank you very much for reading this..... though it is written by pool english...

Parents
  • As a general observation, the compiler can not eliminate a function call without ensuring that it implements all of the side-effects that calling that function at that specific time may have produced.

    As a simple example, the following code compiled without access to the body of func_one() will produce code that calls func_one() twice:

    extern int func_one(void);
    
    void example1(void)
    {
      func_one();
      func_one();
    }
    

    This is the case even if func_one() has no side-effects simply because the compiler has no way of knowing this.

    If, however, the body of func_one() is available, then its side-effects can be inlined and the calls subsequently optimised away, e.g.:

    int func_one(void)
    {
      return 0;
    }
    
    void example2(void)
    {
      func_one();
      func_one();
    }
    

    can compile such that example() makes no calls whatsoever, and just returns, i.e. the compiler can legitimately interpret this as:

    void example2(void)
    {
    }
    

    Conversely, if we replace func_one() with func_two() which does have side-effects, then even if it is inlined the effects must remain, e.g.:

    int func_two(void)
    {
      return *(volatile int*)0xE000E010u;
    }
    
    void example3(void)
    {
      func_two();
      func_two();
    }
    

    Could be implemented by the compiler as:

    void example3(void)
    {
      *(volatile int*)0xE000E010u;
      *(volatile int*)0xE000E010u;
    }
    

    which retains all of the side-effects, but could not be optimised to a single volatile access, or an empty function.

    As such, there appears to be no requirement to need to be able to mark a function as volatile.

    Beyond what is specified in the C standard, some tool chains do provide the opposite feature, i.e. the ability to tell the compiler that the function either has no side-effects and/or that its return value is dependent only on the arguments passed to the function and not on global variables / volatile memory accesses. Significant care must be taken, as these can potentially produce very strange results if used incorrectly, e.g:

    extern int func_two(void) __attribute__((pure));
    
    int example4(void)
    {
      return func_two() + func_two();
    }
    

    Might be expected to perform two separate reads of 0xE000E010, add them together and return the result, however, due to erroneously marking our previous func_two() as "pure", the compiler is free to implement this as:

    int example4(void)
    {
        return func_two() * 2;
    }
    

    i.e., reuse the value from the first call/read, and simply multiply it be two.

    Simon.

Reply
  • As a general observation, the compiler can not eliminate a function call without ensuring that it implements all of the side-effects that calling that function at that specific time may have produced.

    As a simple example, the following code compiled without access to the body of func_one() will produce code that calls func_one() twice:

    extern int func_one(void);
    
    void example1(void)
    {
      func_one();
      func_one();
    }
    

    This is the case even if func_one() has no side-effects simply because the compiler has no way of knowing this.

    If, however, the body of func_one() is available, then its side-effects can be inlined and the calls subsequently optimised away, e.g.:

    int func_one(void)
    {
      return 0;
    }
    
    void example2(void)
    {
      func_one();
      func_one();
    }
    

    can compile such that example() makes no calls whatsoever, and just returns, i.e. the compiler can legitimately interpret this as:

    void example2(void)
    {
    }
    

    Conversely, if we replace func_one() with func_two() which does have side-effects, then even if it is inlined the effects must remain, e.g.:

    int func_two(void)
    {
      return *(volatile int*)0xE000E010u;
    }
    
    void example3(void)
    {
      func_two();
      func_two();
    }
    

    Could be implemented by the compiler as:

    void example3(void)
    {
      *(volatile int*)0xE000E010u;
      *(volatile int*)0xE000E010u;
    }
    

    which retains all of the side-effects, but could not be optimised to a single volatile access, or an empty function.

    As such, there appears to be no requirement to need to be able to mark a function as volatile.

    Beyond what is specified in the C standard, some tool chains do provide the opposite feature, i.e. the ability to tell the compiler that the function either has no side-effects and/or that its return value is dependent only on the arguments passed to the function and not on global variables / volatile memory accesses. Significant care must be taken, as these can potentially produce very strange results if used incorrectly, e.g:

    extern int func_two(void) __attribute__((pure));
    
    int example4(void)
    {
      return func_two() + func_two();
    }
    

    Might be expected to perform two separate reads of 0xE000E010, add them together and return the result, however, due to erroneously marking our previous func_two() as "pure", the compiler is free to implement this as:

    int example4(void)
    {
        return func_two() * 2;
    }
    

    i.e., reuse the value from the first call/read, and simply multiply it be two.

    Simon.

Children