diff options
| author | Scott Gasch <[email protected]> | 2026-09-16 08:02:21 -0700 |
|---|---|---|
| committer | Scott Gasch <[email protected]> | 2026-09-16 08:02:21 -0700 |
| commit | 966a0ef4da9cf3911eb3da0533179199808717b4 (patch) | |
| tree | e09363b574215ef0d8996155a5ab24314db2dd69 /src/movesup.c | |
| parent | b91c8453f3fe21c8ccd3568923d8e39141c9c945 (diff) | |
Retire cPawns/cNonPawns piece-location lists in favor of bbPawns/bbPieces/cKingSq, and hoist two redundant TotalNonPawnCount POPCNT calls out of per-node hot loops.
POSITION no longer stores cPawns[2][8], cNonPawns[2][16], uPawnCount[2],
uNonPawnCount[2][8], or uWhiteSqBishopCount[2] -- every reader now goes
through PawnMaterial/TotalNonPawnCount/WhiteSqBishopCount/KingPosition
(chess.h), computed from the already-incrementally-maintained bbPawns/
bbPieces/cKingSq bitboards via a single -mpopcnt POPCNT instead of a
stored counter. move.c's make/unmake loses the list-swap-on-removal
bookkeeping entirely. GNUmakefile now passes -mpopcnt so the popcount
builtins actually compile to one instruction instead of clang's ~15-op
SWAR fallback.
Measured a small but real nps regression from the naive swap (search.c's
QSearch move-filter and eval.c's _EvalKing were each recomputing the same
TotalNonPawnCount sum on every candidate move / twice per node); hoisted
both to a single call per node instead. Net result across a same-code
self-play match: ~1541.5 knps candidate vs ~1505.7 knps head_reference.
A third hoist (FindEnprisePiece out of SelectBestWithHistory) was tried
and reverted -- its cache isn't actually loop-invariant within a node
(populated from inside Eval(), which doesn't run before every selection
call), caught by a DEBUG-build ASSERT before it landed.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Diffstat (limited to 'src/movesup.c')
| -rw-r--r-- | src/movesup.c | 57 |
1 files changed, 37 insertions, 20 deletions
diff --git a/src/movesup.c b/src/movesup.c index 82ba10a..4a5f973 100644 --- a/src/movesup.c +++ b/src/movesup.c @@ -42,6 +42,14 @@ Revision History: #undef InCheck #endif +// See eval.c's identical comment: routes this file's (dead-under- +// ISATTACKED_BITBOARD) mailbox IsAttacked's bitboard-scan loop to the +// inlined FastCoorFromBitBoardRank8ToRank1 (chess.h) instead of the +// real out-of-line extern call. +#ifndef CROUTINES +#define CoorFromBitBoardRank8ToRank1 FastCoorFromBitBoardRank8ToRank1 +#endif + // board_representation/MOVEGEN_MIGRATION.md section 6b: bitboard // equivalents of ExposesCheck/FasterExposesCheck/ExposesCheckEp. // ExposesCheck is called from MakeMove (move.c) on essentially every @@ -668,7 +676,7 @@ Return value: **/ { - COOR cKingLoc = pos->cNonPawns[uSide][0]; + COOR cKingLoc = KingPosition(pos, uSide); ASSERT(IS_VALID_COLOR(uSide)); ASSERT(IS_KING(pos->rgSquare[cKingLoc].pPiece)); @@ -697,28 +705,29 @@ Return value: **/ { - ULONG u; PIECE pPiece; COOR cLocation; COOR cBlockIndex; int iIndex; int iDelta; + BITBOARD bbNonPawns; static int iPawnLoc[2] = { -17, +15 }; static PIECE pPawn[2] = { BLACK_PAWN, WHITE_PAWN }; - + ASSERT(IS_ON_BOARD(cTest)); ASSERT(IS_VALID_COLOR(uSide)); - ASSERT((pos->uNonPawnCount[uSide][0] - 1) < 16); - + // - // Begin at the end because the king is the 0th item in the list and - // we want to consider king moves last. - // - for (u = pos->uNonPawnCount[uSide][0] - 1; - u != (ULONG)-1; - u--) + // Dead in this build (ISATTACKED_BITBOARD routes every caller to + // IsAttackedBB instead -- see this file's #undef block above), so + // iteration order here has no measured perf consequence; combining + // king + non-king pieces into one bitboard scan (instead of the + // old cNonPawns[uSide][] list, which deliberately visited the king + // -- always list slot 0 -- last) is simplest and just as correct. + // + bbNonPawns = NON_KING_PIECES_BB(pos, uSide) | COOR_TO_BB(KingPosition(pos, uSide)); + while (IS_ON_BOARD(cLocation = CoorFromBitBoardRank8ToRank1(&bbNonPawns))) { - cLocation = pos->cNonPawns[uSide][u]; ASSERT(IS_ON_BOARD(cLocation)); pPiece = pos->rgSquare[cLocation].pPiece; ASSERT(!IS_EMPTY(pPiece)); @@ -810,7 +819,7 @@ Return value: ASSERT(IS_VALID_COLOR(uSide)); - cKingLoc = pos->cNonPawns[uSide][0]; + cKingLoc = KingPosition(pos, uSide); #ifdef DEBUG pKing = pos->rgSquare[cKingLoc].pPiece; ASSERT(IS_KING(pKing)); @@ -1024,7 +1033,7 @@ Return value: // pieces in the way... but it cannot expose its own king to check // by doing so! // - cKing = pos->cNonPawns[pos->uToMove][0]; + cKing = KingPosition(pos, pos->uToMove); cAttack = ExposesCheck(pos, cFrom, cKing); @@ -1421,6 +1430,20 @@ Return value: ASSERT(u >= ctx->sMoveStack.uBegin[ctx->uPly]); ASSERT(u < uEnd); + // Flee-to-safety: a small, same-tier selection-time nudge (not a + // GOOD_MOVE promotion -- that class was retired, see generate.c's + // RETIRED comment) for a quiet move whose origin square is + // currently en prise. NOT loop-invariant across repeated calls at + // the same node the way TotalNonPawnCount was in search.c/eval.c's + // recent hoists -- RecordEnprisePiece (dynamic.c) only populates + // this ply's cache from inside Eval(), which doesn't necessarily + // run before every single SelectBestWithHistory call at this node, + // so a later call in the same node can legitimately see a fresher + // answer than an earlier one. Recomputed every call (2026-09-16: + // briefly hoisted to the caller, reverted after a DEBUG-build + // ASSERT here caught the mismatch -- see git log). + cEnprise = FindEnprisePiece(ctx, ctx->sPosition.uToMove); + // Continuation history: "did a move like this tend to fail high // right after a move like that?", keyed by (previous move, this // move) at the same [piece][to] coarseness g_HistoryCounters @@ -1436,12 +1459,6 @@ Return value: fHaveContinuation = TRUE; } - // Flee-to-safety: a small, same-tier selection-time nudge (not a - // GOOD_MOVE promotion -- that class was retired, see generate.c's - // RETIRED comment) for a quiet move whose origin square is - // currently en prise. - cEnprise = FindEnprisePiece(ctx, ctx->sPosition.uToMove); - // // Linear search from u..ctx->sMoveStack.uEnd[ctx->uPly] for the // move with the best value. |
