(Заранее извините за то, что мне не удалось свести мою проблему к простому неудачному тестовому кейсу …)
Я столкнулся с проблемами при обновлении до GCC 6.3.0 для создания нашей кодовой базы (соответствующие флаги: -O3 -m32
).
В частности, мое приложение вызывает ошибки в вызове struct ctor из-за оптимизации GCC.
В этом ctor используется GCC movaps
:
movaps %xmm0,0x30a0(%ebx)
movaps
требует операнд быть 16-байтовое выравнивание. Но на данный момент, %ebx
указывает на мой объект, который не обязательно 16-байтовый. Из glibc:
«Адрес блока, возвращаемого malloc или realloc в системах GNU, всегда кратен восьми (или шестнадцати в 64-битных системах)».
Следовательно segfault (когда построен с -O3 -m32
).
Почему GCC полагает, что выделенный объект будет выровнен по 16 байтов? Я что-то неправильно понимаю?
Заметки:
new
оператор-m32 -O2
-m32 -O2 -ftree-slp-vectorize
-m32 -O3 -fno-tree-slp-vectorize
-m32 -O3
Этот другой проект, похоже, столкнулся с похожими проблемами: https://github.com/godotengine/godot/issues/4623
Их расследование указывает на -fvect-cost-model=dynamic
, Расследование на моей кодовой базе скорее указывает на -ftree-slp-vectorize
,
Возможно, у компилятора есть основания полагать, что объект имеет выравнивание ≥ 16 байт. Можно узнать, что компилятор считает выравниванием, используя alignof()
оператор в C ++ 11. GCC имеет расширение __alignof__
это доступно в C и более ранних версиях C ++.
Выравнивание структуры является наивысшим выравниванием всего, что есть в ней, рекурсивно. Там может быть что-то с более высоким выравниванием, чем ожидалось.
В то время как стандарт C ++ 11 гарантирует, что память возвращается new
выравнивается по значению, требуемому «фундаментальным требованием выравнивания» любого объекта, это относится только к стандартным типам и объектам из них. Использование C ++ 11 alignas()
или __attribute__((aligned(x)))
Расширение GCC для запроса более высокого выравнивания может превышать то, что new
обеспечивает.
Решением этого было бы использовать std::aligned_alloc()
(C ++ 11 или более поздняя версия) или posix_memalign()
(Только для POSIX, но < C ++ 11) для выравнивания памяти. Это может быть связано с формой размещения new
оператор для создания объекта в этой памяти или класс перегрузки оператора конкретного класса new
а также delete
,
Других решений пока нет …