Bitboardクラスの演算子オーバーロードで_mm_andnot_si128()を使う

and演算とnot演算が実装されたBitboardクラスのコードを以下に示す。このクラスを使うと、b0 & ~b1 のように書くことは可能になる。しかし、_mm_andnot_si128()を使った高速化ができないという欠点がある。

class Bitboard {
    __m128i m128i_;

public:
    Bitboard operator~() const {
        __m128i temp;
        const __m128i mask = _mm_cmpeq_epi64(temp, temp);
        return Bitboard{ _mm_xor_si128(m128i_, mask) };
    }
    Bitboard operator&(const Bitboard& b) const {
        return Bitboard{ _mm_and_si128(m128i_, b.m128i_) };
    }

    uint64_t operator[](const size_t i) const {
        return m128i_.m128i_u64[i];
    }
    constexpr explicit Bitboard(const __m128i& i) : m128i_(i) {}
};

そこで、内部にNot構造体を持つことにする。not演算子があってもすぐに否定演算をすると決めつけず、判断を保留してand演算子に出会えば_mm_andnot_si128()が使われる(要するに使えるところでは_mm_andnot_si128()を使うようにコンパイルしてくれる)。

class Bitboard {
    __m128i m128i_;

    struct Not {
        const __m128i not_m128i_;

        //~b0 & b1
        Bitboard operator&(const Bitboard& b) const {
            return Bitboard{ _mm_andnot_si128(not_m128i_, b.m128i_) };
        }
        //通常のnot
        operator Bitboard() const {
            __m128i temp;
            const __m128i mask = _mm_cmpeq_epi64(temp, temp);
            return Bitboard{ _mm_xor_si128(not_m128i_, mask) };
        }
        explicit Not(const Bitboard& b) : not_m128i_(b.m128i_) {}
    };

public:
    //ここでは計算せずNot構造体を返す
    Bitboard::Not operator~() const {
        return Bitboard::Not{ *this };
    }
    //b0 & ~b1
    Bitboard operator&(const Bitboard::Not& not_b) const {
        return Bitboard{ _mm_andnot_si128(not_b.not_m128i_, m128i_) };
    }
    Bitboard operator&(const Bitboard& b) const {
        return Bitboard{ _mm_and_si128(m128i_, b.m128i_) };
    }

    uint64_t operator[](const size_t i) const {
        return m128i_.m128i_u64[i];
    }
    constexpr explicit Bitboard(const __m128i& i) : m128i_(i) {}
};

//気軽に下のように書けてとても便利!
Bitboard attackBB = attacks_bb(C, from, P, pos.occupied_) & ~pos.byColorBB_[C];//_mm_andnot_si128()が使われる