diff options
| author | Scott Gasch <[email protected]> | 2026-09-04 01:03:00 -0700 |
|---|---|---|
| committer | Scott Gasch <[email protected]> | 2026-09-04 01:03:00 -0700 |
| commit | 5c8d794782d3be6368dbba613ef129b11d878d97 (patch) | |
| tree | bd43bd7c72aa32a4c99001147416e503142a7590 /src/eval.c | |
| parent | dddcaa09ad12f1972a3128748b5d90d22f9a9326 (diff) | |
Fix passed-pawn bitboard bit-clear bug, LMR gate coupling, inline hot bitboard helpers
- bitboard.c: CoorFromBitBoardRank1ToRank8 cleared the lowest set bit
unconditionally instead of the reported (highest) one, silently
mis-walking doubled-pawn files in eval.c's passed-pawn detection.
- search.c/searchsup.c: move GetLMRReduction's precondition checks from
inside the function to the caller in search.c (pre-existing work),
finishing the split with a matching gate in split.c's HelpSearch --
the parallel-search call site had no gate at all, letting it call
GetLMRReduction unconditionally (including for checking moves),
reachable only under real multithreading (--cpus > 1) and the
intermittent root cause of assertion crashes seen under --cpus 4.
- eval.c: redirect CountBits/CoorFromBitBoardRank8ToRank1/
CoorFromBitBoardRank1ToRank8 to inline compiler-builtin versions
(gated !CROUTINES) instead of the real out-of-line asm calls, on
eval.c's ~20 existing production call sites. CountBits' asm body
isn't O(1) popcnt, it's a Kernighan bit-clearing loop plus call
overhead, paid on every Eval() call.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Jntky4yGUTyQVaGCXms4F2
Diffstat (limited to 'src/eval.c')
| -rwxr-xr-x | src/eval.c | 62 |
1 files changed, 62 insertions, 0 deletions
@@ -21,6 +21,68 @@ Revision History: #include "chess.h" +// +// CountBits and the CoorFromBitBoard{Rank8ToRank1,Rank1ToRank8} +// wrappers (bitboard.c) are real out-of-line asm calls on every one of +// eval.c's ~20 production call sites (passed-pawn detection, bishop- +// pair logic, etc.), hit on every real Eval() call. CountBits' asm +// body (x64.asm) isn't even O(1) popcnt, it's a Kernighan bit-clearing +// loop -- O(popcount) iterations plus call overhead. Redirected via +// #define, the same mechanism chess.h already uses for the OTHER +// direction (#ifdef CROUTINES routes CountBits/FirstBit/LastBit to the +// slow C fallbacks) -- this routes them to fast inline compiler +// builtins instead, for every existing call site below with no further +// edits. Gated on !CROUTINES so the CROUTINES debug/comparison build +// still gets the real (slow, cross-checked) implementations. +// +// _FastCoorFromBitBoardRank1ToRank8 also bakes in the wrong-bit-clear +// fix applied to bitboard.c's real CoorFromBitBoardRank1ToRank8 (that +// function used to clear the LOWEST set bit via `*pbb &= (*pbb - 1)` +// regardless of uLastBit, correct only when a single bit was set) -- +// this version clears the bit it actually just reported, via BBSQUARE. +// +#ifndef CROUTINES +static ULONG INLINE +_FastCountBits(IN BITBOARD bb) +{ + return (ULONG)__builtin_popcountll(bb); +} + +static COOR INLINE +_FastCoorFromBitBoardRank8ToRank1(IN OUT BITBOARD *pbb) +{ + COOR c = ILLEGAL_COOR; + ULONG uBitIndex; + + if (*pbb) + { + uBitIndex = (ULONG)__builtin_ctzll(*pbb); + c = BIT_NUMBER_TO_COOR(uBitIndex); + *pbb &= (*pbb - 1); + } + return c; +} + +static COOR INLINE +_FastCoorFromBitBoardRank1ToRank8(IN OUT BITBOARD *pbb) +{ + COOR c = ILLEGAL_COOR; + ULONG uBitIndex; + + if (*pbb) + { + uBitIndex = (ULONG)(63 - __builtin_clzll(*pbb)); + c = BIT_NUMBER_TO_COOR(uBitIndex); + *pbb &= ~BBSQUARE[uBitIndex]; + } + return c; +} + +#define CountBits _FastCountBits +#define CoorFromBitBoardRank8ToRank1 _FastCoorFromBitBoardRank8ToRank1 +#define CoorFromBitBoardRank1ToRank8 _FastCoorFromBitBoardRank1ToRank8 +#endif // !CROUTINES + // // Bishop-mobility ray-walk outcome categories -- see BMobCaseTable in |
