Unexpected behaviour wrt/ volatile

Unexpected behaviour wrt/ volatile

Hello,

we're using the Intel C++ compiler and came across some unexpected behaviour when compiling code containing the volatile modifier. We managed to narrow it down to the following demo code (please see inline comments):

// save as demo.cc
// volatile "bug"?

struct counter
{
   // XXX bug seems to only occur when using bitfield
#if defined(NO_BITFIELD)
   unsigned int value_;
#else
   unsigned int value_ : 32;
#endif
};

struct counter_container
{
      struct counter counter_;

      inline int get_counter() volatile
      {
         return counter_.value_;
      }
};

class Demo
{
public:

   counter_container* pCounter_container_;

   int counter(int x)
   {
      // XXX bug wrt/ volatile?!
#if defined(UNEXPECTED)
      int ret = static_cast(pCounter_container_ + x)->get_counter();
#else
      // only this seems to work as expected
      volatile int ret = (pCounter_container_ + x)->get_counter();
#endif
      return ret;
   }

   Demo()
      : pCounter_container_(0)
   {
      return;
   }

   int play()
   {
      // this loop is omitted when compiling with -DUNEXPECTED
      while (counter(0x1234) != 0)
      {
      }

      return 42;
   }
};

int main()
{
   Demo demo;
   return demo.play();
} 

Now use the commands in the following shell script to compile and look at the assembler output:

#!/bin/bash

# volatile "bug"?

# $ icc -V
ICC_REFERENCE='Intel C Intel 64 Compiler Professional for applications running on Intel 64, Version 11.0    Build 20090318 Package ID: l_cproc_p_11.0.083
Copyright (C) 1985-2009 Intel Corporation.  All rights reserved.'

set -e

DEMO="demo.cc"

echo "Reference version:
$ICC_REFERENCE
"
echo "Using version:
$(icc -V 2>&1)
"

read -p 'Press Enter to compile $DEMO (or ^C to quit) ...' -n1 -s
echo
icc -S -fsource-asm -O2 -x c++ "$DEMO" -o "$(basename "$DEMO").ok.s"
icc -S -fsource-asm -O2 -x c++ -DUNEXPECTED "$DEMO" -o "$(basename "$DEMO").bad.s"

if [ -x "$(which vimdiff)" ]
then
  read -p 'Press Enter to start vimdiff (or ^C to quit) ...' -n1 -s
  echo
  vimdiff "$(basename "$DEMO").ok.s" "$(basename "$DEMO").bad.s"
else
  echo "Look at diff $(basename "$DEMO").ok.s $(basename "$DEMO").bad.s!"
fi

We believe that in the "unexpected" case the compiler shouldn't be allowed to optimize/leave out the "volatile" reads to counter_->value_ in the while loop. Is this correct? Or is the version with static_cast not supposed to work at all? Any comments or insights are appreciated!

Regards

Oliver

3 posts / 0 new
Last post
For more complete information about compiler optimizations, see our Optimization Notice.

Hi,

the compiler is correct. As the volatile modifier is only use while casting and is not attached to the variable, this information is gone after the assignement to ret has happened.

Volatile modifiers always need to be attached to the variables declaration to indicate to the compiler that the variable can be changed at any time and that it has to be read from main memory.

Cheers

-michael

I agree that the working version where volatile is attached to the local variable is correct. It seems that the actual storage for the local variable is indeed written repeatedly. But since the right side of the assignment (that is the value that is read from memory) isn't volatile, the compiler would only be required to read it once (it knows it can't change), wouldn't it? That the read is actually performed in each iteration of the loop is not strictly necessary.

I assumed that it is mandatory though if the volatile modifier is (even "temporarily") attached to the memory accessed through (pCounter_container + x). Assuming that access to a volatile object (here: *(pCounter_container + x)) may have side effects unknown to the implementation, the read has to be performed in any case. That "volatile" value is then assigned to the local variable, and thus the compiler should assume that it might change with each assignment and operate accordingly by emitting the while loop.

EDIT: Furthermore the compiler seems to emit correct code (while loop reading volatile storage) in both cases when you compile with -DNO_BITFIELD, and these changes should be independent ...

Leave a Comment

Please sign in to add a comment. Not a member? Join today