Укрощает ли барьер памяти оптимизатор Microsoft?

Я работаю над мультиплатформенной мультикомпиляторной библиотекой. В библиотеке есть следующий макрос:

#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

Под GCC приведенный выше код может быть использован для приручения оптимизатора.
Хотя функция называется MEMORY_BARRIER, важной частью является встроенная сборка с маркировкой volatile, Это та часть, которая приручает оптимизатор под GCC, Clang и Intel.

РЕДАКТИРОВАТЬ: Встроенная сборка не приручает оптимизатор на Clang, хотя Clang утверждает, что GCC, определяя __GNUC__, Увидеть Ошибка LLVM 15495 — проход мертвого хранилища игнорирует ассемблерный оператор asm.

Использование макроса является handle учебный класс. handle обеспечивает один уровень и косвенность, и мы пытаемся вызвать разыменование нулевого указателя, чтобы помочь найти ошибки (некоторые ручные отказы). Для достижения нашей цели нам нужно убедиться, что оптимизатор не удаляет мертвый магазин (m_p = NULL;):

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

MEMORY_BARRIER();
}

Я не хочу использовать volatile приведение, потому что (1) я не верю, что это правильное использование классификатора (взятого из взаимодействий с разработчиками Clang и GCC), и (2) он кажется volatile приведение является неопределенным поведением в C ++ (см. Утвержденный способ избежать lvalue броска предупреждений и ошибок?).

Укрощает ли барьер памяти оптимизатор на платформах Microsoft?

2

Решение

В компиляторе GCC вы можете отключить оптимизацию для выбранных функций вручную с помощью директив компилятора, как в примере ниже.

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

Под компилятором VC вы можете отключить оптимизацию для выбранных функций вручную с помощью директив компилятора, как в примере ниже.

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

Может быть, вы можете использовать эти трюки, чтобы получить то, что вы хотите?

К сожалению, я не знаю, как сделать подобный трюк под clang / llvm или Intel Compiler.

2

Другие решения

ffmpeg (написанный на C, а не C ++) решает эту проблему, имея обертка для free, который обнуляет указатель.

В новом коде они предпочитают av_free(&ptr) над av_free(ptr),

Если есть условие использования после освобождения, компилятор не сможет доказать, что это мертвое хранилище, и устранить его, я полагаю. Это может не работать в C ++, если компилятору разрешено предполагать, что записи в переменные-члены в деструкторе являются мертвыми хранилищами.

Я знаю, что это ничего не доказывает, но вы видели случаи, когда компилятор оптимизировал эти хранилища с нулевым указателем?

0