Ответ 1
Я уверен, что это ошибка в clang. Я не вижу поведения undefined в программе (предполагая, что он не превышает пределы пропускной способности реализации) - кроме небольшой проблемы в вызове printf
, который я рассмотрю ниже (и который теперь был рассмотрен в редактирование вопроса). Возможно, что я что-то пропустил, но я так не думаю.
Если я что-то пропустил, я ожидаю, что это будет указано в ближайшее время. Если через несколько дней этот ответ останется без проблем, я буду считать его сильным признаком того, что это действительно ошибка в clang.
ОБНОВЛЕНИЕ: Марк Адлер, оригинальный плакат, сообщил об этом и подтвердил, что это ошибка в pre-3.6.0 clang, исправленная в более поздних версиях. Я бесстыдно украду эту ссылку на отчет об ошибке из своего ответа.
Правильный вывод:
47 bits set (4294967296 loops)
Чтобы рассмотреть некоторые из вещей, которые были указаны (или что я заметил сам):
static uint64_t vec[1 << 26] = {0};
Это большой объект (2 29 байты или половина гигабайта, предполагая CHAR_BIT==8
), но он, по-видимому, не превышает емкость реализации. Если бы это было так, это было бы отклонено. Я не на 100% уверен, что стандарт требует этого, но поскольку программа работает корректно на более низких уровнях оптимизации, мы можем предположить, что объект не слишком велик.
vec[31415927] = 0xb9fe2f2fedf7ebbd
Постоянная 0xb9fe2f2fedf7ebbd
не является проблемой. Его значение находится между 2 63 и 2 64 поэтому оно находится в диапазоне uint64_t
. Тип шестнадцатеричной целочисленной константы достаточно широка, чтобы удерживать ее значение (если оно не превышает ULLONG_MAX
, но это не так).
if (vec[x >> 6] & ((uint64_t)1 << (x & 0x3f)))
Я кратко подумал, что сдвиг влево может быть проблемой, но это не так. Левый операнд имеет тип uint64_t
, а правый операнд находится в диапазоне 0
.. 63
. Левый сдвиг в 64 бита будет иметь поведение undefined, но это не так.
printf("%llu bits set (%llu loops)\n", count, loops);
Обновление связано с вопросом. Я пробовал обновленную версию кода, и я получил те же результаты.
%llu
требуется аргумент типа unsigned long long
; count
и loops
имеют тип uint64_t
. Здесь, в зависимости от реализации, мы могли бы иметь поведение undefined (в моей системе uint64_t
есть typedef для unsigned long
, и я получаю предупреждение). Но это вряд ли вызовет какие-либо реальные проблемы (unsigned long long
и uint64_t
обычно имеют одинаковое представление, даже если они не одного типа), и когда я добавляю отливки, чтобы избежать UB:
printf("%llu bits set (%llu loops)\n",
(unsigned long long)count,
(unsigned long long)loops);
Я получаю такое же поведение. Следующие результаты для программы с добавлениями, добавленными к вызову printf
.
Забастовкa >
Используя gcc 5.2.0 на моей 64-битной системе, я получаю правильный вывод с -O0
, -O1
, -O2
и -O3
с или без -m32
. Время указывает, что gcc не устраняет цикл на любом уровне оптимизации.
Используя clang 3.4 в той же системе, я получаю правильный вывод с -O0
или -O1
, но неверный вывод (0 bits set
) в -O2
или -O3
. Сроки показывают, что цикл исключается при -O2
и -O3
. Когда я компилирую с clang -m32
, вывод корректен (и цикл не устраняется) на всех уровнях оптимизации.
Когда я изменяю объявление loops
на
volatile uint64_t loops = 0;
Я получаю правильный вывод на всех уровнях оптимизации (и цикл не устраняется).
Дальнейшая настройка программы (не показана здесь) показывает, что vec[31415927]
действительно установлен на 0xb9fe2f2fedf7ebbd
, даже если оптимизация вызывает неправильное количество бит.