Я работаю над мультиплатформенной мультикомпиляторной библиотекой. В библиотеке есть следующий макрос:
#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?
В компиляторе 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.
ffmpeg (написанный на C, а не C ++) решает эту проблему, имея обертка для free
, который обнуляет указатель.
В новом коде они предпочитают av_free(&ptr)
над av_free(ptr)
,
Если есть условие использования после освобождения, компилятор не сможет доказать, что это мертвое хранилище, и устранить его, я полагаю. Это может не работать в C ++, если компилятору разрешено предполагать, что записи в переменные-члены в деструкторе являются мертвыми хранилищами.
Я знаю, что это ничего не доказывает, но вы видели случаи, когда компилятор оптимизировал эти хранилища с нулевым указателем?