HooverChessUtils_PgnReader 0.9.0
Loading...
Searching...
No Matches
bitboard-attacks-x86-avx512f.h
Go to the documentation of this file.
1// Hoover Chess Utilities / PGN reader
2// Copyright (C) 2025 Sami Kiminki
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <https://www.gnu.org/licenses/>.
16
17#ifndef HOOVER_CHESS_UTILS__PGN_READER__BITBOARD_ATTACKS_X86_AVX512F_H_INCLUDED
18#define HOOVER_CHESS_UTILS__PGN_READER__BITBOARD_ATTACKS_X86_AVX512F_H_INCLUDED
19
21
22#include <immintrin.h>
23#include <array>
24#include <cstdint>
25
27{
28
30{
31public:
32
33 alignas(__m128i) static constexpr std::array<std::uint64_t, 2U> ctAttackingPawnMasks {
34 static_cast<std::uint64_t>(~SquareSet::column(0)), // captures to left
35 static_cast<std::uint64_t>(~SquareSet::column(7)), // captures to right
36 };
37
38 alignas(__m128i) static constexpr std::array<std::int64_t, 4U> ctAttackingPawnRotateLefts {
39 -9, // captures to left, black pawn
40 -7, // captures to right, black pawn
41 +7, // captures to left, white pawn
42 +9, // captures to right, white pawn
43 };
44
45 // the order of knights:
46 // -2, +1 (two left, one up)
47 // -1, +2
48 // +1, +2
49 // +2, +1
50 // -2, -1
51 // -1, -2
52 // +1, -2
53 // +2, -1
54 alignas(__m512i) static constexpr std::array<std::uint64_t, 8U> ctAttackingKnightMasks {
55 static_cast<std::uint64_t>( ~(SquareSet::column(0U) | SquareSet::column(1U) | SquareSet::row(7U) )),
56 static_cast<std::uint64_t>(~(SquareSet::column(0U) | SquareSet::row(7U) | SquareSet::row(6U) )),
57 static_cast<std::uint64_t>(~(SquareSet::column(7U) | SquareSet::row(7U) | SquareSet::row(6U) )),
58 static_cast<std::uint64_t>(~(SquareSet::column(7U) | SquareSet::column(6U) | SquareSet::row(7U) )),
59 static_cast<std::uint64_t>(~(SquareSet::column(0U) | SquareSet::column(1U) | SquareSet::row(0U) )),
60 static_cast<std::uint64_t>(~(SquareSet::column(0U) | SquareSet::row(0U) | SquareSet::row(1U) )),
61 static_cast<std::uint64_t>(~(SquareSet::column(7U) | SquareSet::row(0U) | SquareSet::row(1U) )),
62 static_cast<std::uint64_t>(~(SquareSet::column(7U) | SquareSet::column(6U) | SquareSet::row(0U) )),
63 };
64
65 alignas(__m512i) static constexpr std::array<std::int64_t, 8U> ctAttackingKnightRotateLefts {
66 -2 + 8 ,
67 -1 + 16,
68 +1 + 16,
69 +2 + 8 ,
70 -2 - 8 ,
71 -1 - 16,
72 +1 - 16,
73 +2 - 8 ,
74 };
75
76 // the order of sliders:
77 // - bishop advancing top-left
78 // - bishop advancing top-right
79 // - bishop advancing bottom-left
80 // - bishop advancing bottom-right
81 // - rook advancing top
82 // - rook bishop advancing bottom
83 // - rook advancing left
84 // - rook advancing right
85 alignas(__m512i) static constexpr std::array<std::uint64_t, 8U> ctAttackingSliderMasks {
86 static_cast<std::uint64_t>(~(SquareSet::row(7U) | SquareSet::column(0U))),
87 static_cast<std::uint64_t>(~(SquareSet::row(7U) | SquareSet::column(7U))),
88 static_cast<std::uint64_t>(~(SquareSet::row(0U) | SquareSet::column(0U))),
89 static_cast<std::uint64_t>(~(SquareSet::row(0U) | SquareSet::column(7U))),
90 static_cast<std::uint64_t>(~SquareSet::row(7U)),
91 static_cast<std::uint64_t>(~SquareSet::row(0U)),
92 static_cast<std::uint64_t>(~SquareSet::column(0U)),
93 static_cast<std::uint64_t>(~SquareSet::column(7U)),
94 };
95
96 alignas(__m512i) static constexpr std::array<std::int64_t, 8U> ctAttackingSliderRotateLefts {
97 +7,
98 +9,
99 -9,
100 -7,
101 +8,
102 -8,
103 -1,
104 +1
105 };
106
107#if HAVE_X86_AVX512VL
108 static inline __m128i getAttackedSquaresByPawns128(const SquareSet pawns, Color oppPawnColor) noexcept
109 {
110 static_assert(static_cast<ColorUnderlyingType>(Color::WHITE) == 0);
111 static_assert(static_cast<ColorUnderlyingType>(Color::BLACK) == 8);
112
113 const std::uint8_t *rotateData { std::bit_cast<const std::uint8_t *>(ctAttackingPawnRotateLefts.data()) };
114
115 const __m128i rotateLefts { _mm_load_epi64(rotateData + 2U * static_cast<std::size_t>(oppPawnColor)) };
116 const __m128i attackingPawnMasks { _mm_load_epi64(ctAttackingPawnMasks.data()) };
117
118 __m128i attackingPawns { _mm_set1_epi64x(static_cast<std::uint64_t>(pawns)) };
119 attackingPawns &= attackingPawnMasks;
120 return _mm_rolv_epi64(attackingPawns, rotateLefts);
121 }
122#else
123 static inline __m512i getAttackedSquaresByPawns512(const SquareSet pawns, Color oppPawnColor) noexcept
124 {
125 static_assert(static_cast<ColorUnderlyingType>(Color::WHITE) == 0);
126 static_assert(static_cast<ColorUnderlyingType>(Color::BLACK) == 8);
127
128 const std::uint8_t *rotateData { std::bit_cast<const std::uint8_t *>(ctAttackingPawnRotateLefts.data()) };
129
130 const __m128i rotateLefts { _mm_load_si128(std::bit_cast<const __m128i *>(rotateData + 2U * static_cast<std::size_t>(oppPawnColor))) };
131 const __m128i attackingPawnMasks { _mm_load_si128(std::bit_cast<const __m128i *>(ctAttackingPawnMasks.data())) };
132
133 __m128i attackingPawns { _mm_set1_epi64x(static_cast<std::uint64_t>(pawns)) };
134 attackingPawns &= attackingPawnMasks;
135 return _mm512_rolv_epi64(_mm512_zextsi128_si512(attackingPawns), _mm512_zextsi128_si512(rotateLefts));
136 }
137#endif
138
139 static inline __m512i getAttackedSquaresByKnights(const SquareSet knights) noexcept
140 {
141 const __m512i rotateLefts = _mm512_load_epi64(ctAttackingKnightRotateLefts.data());
142 const __m512i attackingKnightMasks = _mm512_load_epi64(ctAttackingKnightMasks.data());
143
144 __m512i attackingKnights { _mm512_set1_epi64(static_cast<std::uint64_t>(knights)) };
145 attackingKnights &= attackingKnightMasks;
146 return _mm512_rolv_epi64(attackingKnights, rotateLefts);
147 }
148
149 static inline __m512i getAttackedSquaresBySliders(__m512i attacks, const SquareSet bishops, const SquareSet rooks, const SquareSet occupancyMask) noexcept
150 {
151 if ((bishops | rooks) == SquareSet { })
152 return attacks;
153
154 const __m512i rotateLefts = _mm512_load_epi64(ctAttackingSliderRotateLefts.data());
155 const __m512i attackingSliderMasks = _mm512_load_epi64(ctAttackingSliderMasks.data());
156
157 __m512i attackingSliders { };
158
159 attackingSliders =
160 _mm512_mask_set1_epi64(
161 _mm512_set1_epi64(static_cast<std::uint64_t>(bishops)),
162 0xF0,
163 static_cast<std::uint64_t>(rooks));
164
165 const __m512i occupancyMasksNegated = _mm512_set1_epi64(static_cast<std::uint64_t>(~occupancyMask));
166
167 attackingSliders &= attackingSliderMasks;
168
169 __mmask8 exitCond;
170
171 do
172 {
173 attackingSliders = _mm512_rolv_epi64(attackingSliders, rotateLefts);
174 attacks |= attackingSliders;
175 attackingSliders = attackingSliders & occupancyMasksNegated;
176 attackingSliders &= attackingSliderMasks;
177
178 attackingSliders = _mm512_rolv_epi64(attackingSliders, rotateLefts);
179 attacks |= attackingSliders;
180 attackingSliders = attackingSliders & occupancyMasksNegated;
181
182 exitCond = _mm512_test_epi64_mask(attackingSliders, attackingSliderMasks);
183
184 attackingSliders &= attackingSliderMasks;
185 }
186 while (exitCond != 0U);
187
188 return attacks;
189 }
190
192 SquareSet occupancyMask,
193 SquareSet pawns,
194 SquareSet knights,
195 SquareSet bishops,
196 SquareSet rooks,
197 Square king,
198 Color turn) noexcept
199 {
200 // add knight attacks
201 __m512i attacks {
202#if !HAVE_X86_AVX512VL
203 getAttackedSquaresByPawns512(pawns, turn) |
204#endif
206
207 // add slider attacks
208 attacks = getAttackedSquaresBySliders(attacks, bishops, rooks, occupancyMask);
209
210 // reduce 8x 64b attack tables to 4x 64b
211 const __m256i attacks256 {
212 _mm512_extracti64x4_epi64(attacks, 1U) |
213 _mm512_castsi512_si256(attacks) };
214
215 // reduce 4x 64b attack tables to 2x 64b & add pawn attacks
216 const __m128i attacks128 {
217#if HAVE_X86_AVX512VL
218 getAttackedSquaresByPawns128(pawns, turn) |
219#endif
220 _mm256_extracti128_si256(attacks256, 1U) |
221 _mm256_castsi256_si128(attacks256) };
222
223 // reduce 2x 64 to 64b
224 const std::uint64_t attacks64 =
225 _mm_cvtsi128_si64(
226 _mm_bsrli_si128(attacks128, 8U) |
227 attacks128);
228
229 // ... and add king attacks
230 return
231 SquareSet { attacks64 } |
233 }
234
235#if 0 // note: this is quite a lot slower than the regular PEXT/PDEP implementation
236 static inline void determineSliderCheckersAndPins(
237 SquareSet occupancyMask,
238 SquareSet turnColorMask,
239 SquareSet bishops,
240 SquareSet rooks,
241 SquareSet epCapturable,
242 Square kingSq,
243 SquareSet &out_checkers,
244 SquareSet &out_pinnedPieces)
245 {
246 // rays expanding outwards from king towards a checker or possibly pinned piece
247 __m512i kingRays { _mm512_set1_epi64(static_cast<std::uint64_t>(SquareSet { kingSq })) };
248
249 __m512i oppSliders {
250 _mm512_set_epi64(
251 static_cast<std::uint64_t>(rooks &~ turnColorMask),
252 static_cast<std::uint64_t>(rooks &~ turnColorMask),
253 static_cast<std::uint64_t>(rooks &~ turnColorMask),
254 static_cast<std::uint64_t>(rooks &~ turnColorMask),
255 static_cast<std::uint64_t>(bishops &~ turnColorMask),
256 static_cast<std::uint64_t>(bishops &~ turnColorMask),
257 static_cast<std::uint64_t>(bishops &~ turnColorMask),
258 static_cast<std::uint64_t>(bishops &~ turnColorMask)) };
259
260 // Traces from potentially pinned pieces outwards. Used to commit the pinned piece in
261 // case the trace hits an x-ray attacker
262 __m512i xrays { };
263
264 // pinned pieces
265 __m512i potentiallyPinnedPieces { };
266 __m512i pinnedPieces { };
267
268 const __m512i occupancyMasks { _mm512_set1_epi64(static_cast<std::uint64_t>(occupancyMask)) };
269 __m512i checkers { };
270 const __m512i pinnables {
271 _mm512_set_epi64(
272 static_cast<std::uint64_t>(turnColorMask),
273 static_cast<std::uint64_t>(turnColorMask),
274 static_cast<std::uint64_t>(turnColorMask),
275 static_cast<std::uint64_t>(turnColorMask),
276 static_cast<std::uint64_t>(turnColorMask | epCapturable),
277 static_cast<std::uint64_t>(turnColorMask | epCapturable),
278 static_cast<std::uint64_t>(turnColorMask | epCapturable),
279 static_cast<std::uint64_t>(turnColorMask | epCapturable)) };
280
281 const __m512i rotateLefts = _mm512_load_epi64(ctAttackingSliderRotateLefts.data());
282 const __m512i attackingSliderMasks = _mm512_load_epi64(ctAttackingSliderMasks.data());
283
284 // Filter out king rays about to go out of board
285 kingRays &= attackingSliderMasks;
286
287 while (true)
288 {
289 // expand king rays by a square. These are guaranteed to be on the board after expansion
290 kingRays = _mm512_rolv_epi64(kingRays, rotateLefts);
291
292 // add checkers if king rays meet opponent's sliders
293 checkers |= kingRays & oppSliders;
294
295 // king ray becomes an x-ray if it meets a pinnable piece
296 xrays |= kingRays & pinnables;
297
298 // filter out king rays that cannot expand anymore
299 kingRays &= attackingSliderMasks;
300
301 // terminate king rays on any piece
302 kingRays &= ~occupancyMasks;
303
304 // viable king rays left?
305 if (_mm512_test_epi64_mask(kingRays, kingRays) == 0U)
306 break;
307 }
308
309 potentiallyPinnedPieces = xrays; // potentially pinned, will resolve later
310
311 while (true)
312 {
313 // filter out x-rays that are about to go oob
314 xrays &= attackingSliderMasks;
315
316 // viable x-rays left?
317 if (_mm512_test_epi64_mask(xrays, xrays) == 0U)
318 break;
319
320 // expand x-rays
321 xrays = _mm512_rolv_epi64(xrays, rotateLefts);
322
323 // if an x-ray hits an opponent slider, that slider is a pinner
324 const __mmask8 commitPinnedPieces { _mm512_test_epi64_mask(xrays, oppSliders) };
325 pinnedPieces |= _mm512_maskz_mov_epi64(commitPinnedPieces, potentiallyPinnedPieces);
326
327 // x-rays terminate on any piece
328 xrays &= ~occupancyMasks;
329 }
330
331 out_checkers = SquareSet { static_cast<std::uint64_t>(_mm512_reduce_or_epi64(checkers)) };
332 out_pinnedPieces = SquareSet { static_cast<std::uint64_t>(_mm512_reduce_or_epi64(pinnedPieces)) };
333 }
334#endif
335
336};
337
338}
339
340#endif
Definition bitboard-attacks-x86-avx512f.h:30
static constexpr std::array< std::int64_t, 8U > ctAttackingSliderRotateLefts
Definition bitboard-attacks-x86-avx512f.h:96
static SquareSet determineAttackedSquares(SquareSet occupancyMask, SquareSet pawns, SquareSet knights, SquareSet bishops, SquareSet rooks, Square king, Color turn) noexcept
Definition bitboard-attacks-x86-avx512f.h:191
static __m512i getAttackedSquaresByKnights(const SquareSet knights) noexcept
Definition bitboard-attacks-x86-avx512f.h:139
static __m512i getAttackedSquaresBySliders(__m512i attacks, const SquareSet bishops, const SquareSet rooks, const SquareSet occupancyMask) noexcept
Definition bitboard-attacks-x86-avx512f.h:149
static constexpr std::array< std::int64_t, 4U > ctAttackingPawnRotateLefts
Definition bitboard-attacks-x86-avx512f.h:38
static constexpr std::array< std::uint64_t, 8U > ctAttackingKnightMasks
Definition bitboard-attacks-x86-avx512f.h:54
static constexpr std::array< std::uint64_t, 2U > ctAttackingPawnMasks
Definition bitboard-attacks-x86-avx512f.h:33
static __m512i getAttackedSquaresByPawns512(const SquareSet pawns, Color oppPawnColor) noexcept
Definition bitboard-attacks-x86-avx512f.h:123
static constexpr std::array< std::uint64_t, 8U > ctAttackingSliderMasks
Definition bitboard-attacks-x86-avx512f.h:85
static constexpr std::array< std::int64_t, 8U > ctAttackingKnightRotateLefts
Definition bitboard-attacks-x86-avx512f.h:65
static SquareSet getKingAttackMask(Square sq) noexcept
Definition bitboard-attacks-portable.h:266
Set of squares. Implemented using a bit-mask.
Definition chessboard-types-squareset.h:35
static constexpr SquareSet column(RowColumn col) noexcept
Returns a set of squares in column number col.
Definition chessboard-types-squareset.h:471
static constexpr SquareSet row(RowColumn row) noexcept
Returns a set of squares in row number row.
Definition chessboard-types-squareset.h:485
std::uint_fast8_t ColorUnderlyingType
Underlying type of Color
Definition chessboard-types.h:47
Color
Color of a piece or side to move.
Definition chessboard-types.h:194
Square
Named square.
Definition chessboard-types.h:122
@ BLACK
Black piece or black side to move.
@ WHITE
White piece or white side to move.
Definition chessboard-types-squareset.h:30