MSVC + Union + SSE = некорректный скомпилированный код

Рейтинг: 3Ответов: 0Опубликовано: 23.08.2023

Наблюдаю странное поведение компилятора MSVC. Подозреваю, что я неправильно использую union и нарушаю выравнивание. Компилирую 64х битным MSVC командой cl /std:c++20 /O2 /Z7 algo.cpp.

Имеется структура Board размера 40 байт и функция print_board. При компиляции с флагом /O2 проблемный участок кода (помечен комментом) превращается в набор SSE инструкций, а запуск программы приводит к выводу мусора (idx считается неверно и выходит за границу массива). Проблема пропадает в билде без /O2. Так же она пропадает, если развернуть цикл как написано в закомменченных строчках (работает корректно даже с /O2, причём компилятор генерирует вообще другой код вроде бы без SSE и инлайнит всю функцию).

#include <cstdio>

using Bitboard = unsigned long long;
struct Board {
    union {
        struct {
            Bitboard a,b,c,d;
        };
        Bitboard bbs[4];
    };
    Bitboard occ;
};

void print_board(const Board &b) {
    constexpr char dict[] = ".ABCD";
    for (int i = 0; i < 8; ++i) {
        printf("%d ", i);
        for (int j = 0; j < 8; ++j) {
            int idx = 0, off = i*8+j;
            // баг проявляется здесь
            for (int k = 1; k <= 4; ++k)
                idx += k * ((b.bbs[k-1] >> off) & 1);
            /* idx = 1 * ((b.bbs[0] >> off) & 1) */
            /*     + 2 * ((b.bbs[1] >> off) & 1) */
            /*     + 3 * ((b.bbs[2] >> off) & 1) */
            /*     + 4 * ((b.bbs[3] >> off) & 1); */

            printf("%c", dict[idx]);
        }
        printf("\n");
    }

    printf("  01234567\n");
}

int main() {
    // Сгенерировать случайную доску с фиксированным seed. 
    Board b; 
    b.bbs[0] = 9078816779534945ull;
    b.bbs[1] = 576465150354178056ull;
    b.bbs[2] = 2523335239976686852ull;
    b.bbs[3] = 15337864866599151762ull;
    b.occ = 18446744073709551615ull;
    /* Board b = make_random_board(); */
    print_board(b);
}

Ожидаемый вывод, получаемый без оптимизаций или с оптимизациями, но с развёрнутым циклом.

0 ADCBDAAD
1 CACDCDBB
2 CDDCDABD
3 ADCDCDAA
4 DADCDADD
5 ADBDCCAC
6 DDCDDADD
7 CCDBDCDD
  01234567

Вывод с флагом /O2 и инструкциями SSE в дизассемблере.

0   ♠ ÄD Ä
1    Ä√♠
2 ♠Ä♠♠ÄD√Ä
3  ♠♠√♠♠
4 DADCDADD
5 ADBDCCAC
6 DDCDDADD
7 CCDBDCDD
  01234567

UPD
Дополнил код до полностью воспроизводимого примера.

Ответы

Ответов пока нет.