2
votes

I'm working on a multi-platform, multi-compiler library. The library has the following macro:

#if defined(_MSC_VER) && (_MSC_VER >= 1400)
# pragma intrinsic(_ReadWriteBarrier)
# define MEMORY_BARRIER() _ReadWriteBarrier()
#elif ...
#elif defined(__GNUC__)
# define MEMORY_BARRIER() __asm__ __volatile__ ("" ::: "memory")
#else
# define MEMORY_BARRIER()
#endif

Under GCC, the code above can be used to tame the optimizer. Though the function is called MEMORY_BARRIER, the important part is the inline assembly marked volatile. That's the part that tames the optimizer under GCC, Clang and Intel.

EDIT: The inline assembly does not tame the optimizer on Clang, even though Clang claims to be GCC by defining __GNUC__. See LLVM Bug 15495 - dead store pass ignores memory clobbering asm statement.

The use of the macro is a handle class. The handle provides one level and indirection, and we are trying to induce a NULL pointer dereference to help locate bugs (some hand waiving). To achieve our goal, we need to ensure the optimizer does not remove the dead store (m_p = NULL;):

template <class T> handle<T>::~handle()
{
    delete m_p;
    m_p = NULL;

    MEMORY_BARRIER();
}

I don't want to use a volatile cast because (1) I don't believe its the correct use of the qualifier (taken from interactions with the Clang and GCC devs), and (2) it appears the volatile cast is undefined behavior in C++ (see Approved way to avoid lvalue cast warnings and errors?).

Does a memory barrier tame the optimizer on Microsoft platforms?

2
As an alternative solution, if you want to set m_p to zero with no concern about it being optimized away, you can use SecureZeroMemory() when Windows is the target. For other targets you can perhaps call some other API or an assembly function? - Michael Burr
@Michael - yes, agreed. If I could re-use the memory barrier code, then it tidies things up nicely by hiding the implementation specific details behaind the macro (and without the need for more macros and the _Pragma, and all the conditionals that accompany it). - jww

2 Answers

2
votes

Under GCC compiler you can turn off optimization for selected functions manually with compiler directives like in the example below.

#pragma GCC push_options
#pragma GCC optimize ("O0")
static inline void MEMORY_BARRIER() {
    // your code
}
#pragma GCC pop_options

Under VC compiler you can turn off optimization for selected functions manually with compiler directives like in the example below.

#pragma optimize( "", off )
static inline void MEMORY_BARRIER() {
    // your code
}
#pragma optimize( "", on ) 

Maybe you can use this tricks to get what you want?

Unfortunately I do not now how to do similar trick under clang/llvm or Intel Compiler.

0
votes

ffmpeg (written in C, not C++) addresses this problem by having a wrapper for free, which zeroes the pointer.

In new code they prefer av_free(&ptr) over av_free(ptr).

If there is a use-after-free condition, the compiler won't be able to prove it's a dead store and eliminate it, I guess. This might not work in C++, if the compiler is allowed to assume that writes to member variables in a destructor are dead stores.

I know it wouldn't prove anything, but have you seen cases where the compiler optimizes away these pointer-zeroing stores?