Почему -Wcast-align не предупреждает о приведении из char * в int * на x86?

Я понимаю, что gcc имеет опцию -Wcast-align, которая предупреждает, когда указатель вызывается таким образом, что требуемое выравнивание цели увеличивается.

Здесь моя программа:

char data[10];
int ptr = *((int *)data);

На моей машине требование выравнивания данных равно 1, тогда как значение 8 для ptr.

Почему я не получаю предупреждение?

Может быть, потому что я компилирую его для x86?

Ответ 1

Предупреждение никогда не будет выпущено при компиляции для Linux i386 или x86-64 при использовании стандартных ABI для этих систем. Позвольте мне объяснить вам, почему это так.

Во-первых, посмотрим, что gcc documentation говорит о -Wcast-align:

Предупреждать, когда указатель отображается, так что требуемое выравнивание цель увеличена. Например, предупреждайте, если a char * передается в int * на машинах, где целые числа могут быть доступны только при двух- или четырехбайтовые границы.

Архитектура Intel не требует выравнивания целых чисел при использовании инструкций общего назначения. Цитата из Руководство по базовой архитектуре Intel, глава 4.1.1 Выравнивание слов, двойных слов, четырехъядерных чисел и двойных четырехъядерных слов:

Слова, двойные слова и квадранты не должны быть выровнены в памяти на естественных границах. Естественные границы слов, двойных слов, и quadwords - это четные адреса, адреса равномерно делимые на четыре, и адреса, равномерно делящиеся на восемь, соответственно. Однако для повышения эффективности программ структуры данных (особенно стеки) должны быть выровнены на естественных границах всякий раз возможно.

Выравнивание, следовательно, не является строго необходимым, хотя настоятельно рекомендуется. Однако есть одно исключение из этого правила, которое вы, возможно, имели в виду. Бит 18 регистра EFLAGS известен как бит "Проверка выравнивания", а бит 18 регистра CR0 известен как флаг "Alignment Mask". Когда они оба установлены в 1, любая память обращается к данным, которые не выровнены по своей "естественной границе" (так что 2 байта для слов, 4 байта для двойных слов и т.д.) Приводит к #AC, исключению проверки выравнивания, Если вы хотите узнать об этом подробнее, ознакомьтесь с Руководство по системному программированию Intel.

Однако ни System V ABI для i386, ни System V ABI для x86-64 укажите, что установлен флаг выравнивания в EFLAGS. Фактически, i386 ABI отмечает следующее на стр. 29, глава 3-3 "Интерфейс машины":

Архитектура Intel386 не требует, чтобы весь доступ к данным правильно выровнен. (...) Следовательно, произвольные обращения к данным, такие как может быть или не быть правильно выровнен. Доступ к несогласованным данным будет медленнее, чем доступ к правильно выровненным данным, но в противном случае нет разницы.

Хотя он также рекомендует, чтобы:

Компиляторы должны выделять независимые объекты данных с соответствующими выравнивание.

GCC всегда знает ABI платформы, для которой он компилирует код, и - в случае x86/64 - знает о том, что разрешен доступ к неограниченным данным. Вот почему код, подобный этому, будет скомпилирован без предупреждения о выравнивании (не забывайте о строгих правилах псевдонимов в следующих примерах):

int main(void)
{
    char foo[] = "foobar";
    int bar = *(int*)(foo + 1);
    return 0;
}

Если вы попытаетесь скомпилировать этот код с помощью gcs toolchain для ARM, вы получите предупреждение:

[email protected]:/tmp$ arm-linux-gnueabi-gcc -Wcast-align align.c 
align.c: In function 'main':
align.c:4:13: warning: cast increases required alignment of target type [-Wcast-align]
  int bar = *(int*)(foo + 1);

Это связано с тем, что в ARM обычно избегают неприсоединения доступа. Я не эксперт по ARM, поэтому я действительно не могу сказать ничего более.

Кроме того, обратите внимание, что большая часть того, что я написал, не относится к SSE/AVX.