summaryrefslogtreecommitdiff
path: root/src/movesup.c
diff options
context:
space:
mode:
authorScott Gasch <[email protected]>2026-09-16 08:02:21 -0700
committerScott Gasch <[email protected]>2026-09-16 08:02:21 -0700
commit966a0ef4da9cf3911eb3da0533179199808717b4 (patch)
treee09363b574215ef0d8996155a5ab24314db2dd69 /src/movesup.c
parentb91c8453f3fe21c8ccd3568923d8e39141c9c945 (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.c57
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.