summaryrefslogtreecommitdiff
path: root/src/chess.h
diff options
context:
space:
mode:
authorScott Gasch <[email protected]>2026-10-14 12:36:24 -0700
committerScott Gasch <[email protected]>2026-10-14 12:36:24 -0700
commitbb2d7a9cce6275fcfb038c8a3ab248ba3cf2c827 (patch)
treefd340778cf077ec53df1b144bf191ee8ae8188ea /src/chess.h
parent2d7d182d4ab518d9f48c5b8cef0c5f664ed9ba3f (diff)
Extract Search()'s per-move kernel into SearchOneMove (searchsup.c)
UNIFY_SEARCH.md step 2: pull the per-move body out of Search()'s move loop (search.c) -- SEE, MakeMove, extension, LMP/LMR eligibility and decision, PVS search with re-search-on-fail-high, UnmakeMove -- into a standalone function, SearchOneMove (searchsup.c), called from Search() in place of the inline block. HelpSearch() (split.c) is deliberately untouched for now; unifying it onto this same kernel is UNIFY_SEARCH.md's later step. Not extracted (stays in Search(), per UNIFY_SEARCH.md's "what should NOT be extracted"): move acquisition/selection, result publication (iAlpha/mvBest bookkeeping, UpdatePV, hash storage), and the split-launch decision. The split-launch check (StartParallelSearch) runs BEFORE this kernel would be called for that move, unaffected. MOVE_KERNEL_RESULT (chess.h) replaces the old goto/continue control flow a called function can't use: SEARCHED (made, searched, unmade; *piScore valid), PRUNED (LMP fired, never made), ILLEGAL (MakeMove refused mv -- previously silent fall-through with no uLegalMoves bump), ABORTED (WE_SHOULD_STOP_SEARCHING fired after the move was already made/searched/unmade -- *piScore is a real partial result). fFullWindow is an explicit parameter, not inferred from the iBestScore==-INFINITY_SCORE sentinel the inline code used -- that sentinel is only safe because HelpSearch() currently never calls this code at all. Once HelpSearch() also calls SearchOneMove (a later step), its own first move off the shared pool would never be this sentinel's "node's actual first move" in the first place, so making the caller state it explicitly removes a trap that wouldn't assert if violated. FORCEINLINE across the search.c/searchsup.c boundary, no new shared header -- matches this project's own existing convention (WouldGiveCheck, generate.c, non-static FORCEINLINE, called from half a dozen .c files, "one of the most called codepaths... speed of the essence") rather than needing search_inline.h as UNIFY_SEARCH.md originally sketched. Verified actual inlining, not just assumed it: `nm`/objdump on the final linked typhoon binary show no SearchOneMove symbol and no call instruction anywhere -- same as WouldGiveCheck/ComputeMoveExtension/ CanWeReduceOrPruneMove, all of which inline away entirely under this build's -flto, while non-inlined functions (Search, HelpSearch) still show up as expected. Confirms the long parameter list costs nothing at runtime (no call/ret/stack-marshaling survives once genuinely inlined). Diagnostic locals crossing the old inline-block boundary resolved per their actual #ifdef gating (checked, not assumed): fIsLeftoverMove/ fMoveIsPruneReduceEligible and the four uThisMove*Bucket values are read only under #ifdef PERF_COUNTERS post-kernel, so they're OUT params gated the same way; fThisMoveIsCountermoveMatch/uCMEvidenceBucket are caller-computed before the kernel and never mutated by it, so plain IN params under MOVE_ORDERING_EVIDENCE_COLLECTION; fThisMoveJointExtraEligible/uThisMoveJointExtraTier never cross the boundary at all (every consumer is inside the kernel), so they became pure kernel-locals, dropped from Search()'s own declarations entirely. Verification: precommit_check.sh passes. Bit-identical node counts (per UNIFY_SEARCH.md's step-2 gate) confirmed at sd10/--cpus 1 against the pre-extraction baseline (head_reference/pre_unification_sd10_cpus1/) across all 191 positions in the three curated suites, zero differences -- a pure refactor, exactly as required at this stage. Co-Authored-By: Claude Sonnet 5 <[email protected]>
Diffstat (limited to 'src/chess.h')
-rwxr-xr-xsrc/chess.h80
1 files changed, 80 insertions, 0 deletions
diff --git a/src/chess.h b/src/chess.h
index ec912a2..a985c16 100755
--- a/src/chess.h
+++ b/src/chess.h
@@ -3878,6 +3878,86 @@ ComputeMoveExtension(IN OUT SEARCHER_THREAD_CONTEXT *ctx,
IN FLAG fIsLeftoverMove,
IN OUT INT *piExtend);
+// SearchOneMove's return value -- what Search()'s move loop (and, once
+// unified, HelpSearch()'s) should do next. An enum instead of the
+// goto/continue control flow the inline code used to use directly,
+// since those targets don't exist inside a called function.
+typedef enum _MOVE_KERNEL_RESULT
+{
+ MOVE_KERNEL_SEARCHED, // move was made, searched, and unmade; *piScore valid
+ MOVE_KERNEL_PRUNED, // late-move-pruned before searching; not made, *piScore invalid
+ MOVE_KERNEL_ILLEGAL, // MakeMove refused mv (left mover's own king in check);
+ // nothing else ran, *piScore invalid, *puLegalMoves unchanged
+ MOVE_KERNEL_ABORTED // search was stopped mid-move; mv WAS made, searched, and
+ // unmade (cleanup already ran) before this was detected;
+ // *piScore is a partial/real result, treat as this move's score
+}
+MOVE_KERNEL_RESULT;
+
+// The per-move kernel hoisted out of Search()'s move loop (search.c) --
+// see UNIFY_SEARCH.md for why and the full accounting of every
+// parameter below. FORCEINLINE + -flto is what makes the long
+// parameter list free: this is the same convention WouldGiveCheck
+// (generate.c) already uses to stay inlined across translation units
+// despite being called from half a dozen different .c files -- once
+// actually inlined there is no call/ret/stack-marshaling left at all,
+// every parameter just becomes an ordinary local in the caller's own
+// inlined expansion. Confirm it still inlines (not just assume) if
+// this is ever moved or its signature grows further.
+MOVE_KERNEL_RESULT FORCEINLINE
+SearchOneMove(IN SEARCHER_THREAD_CONTEXT *ctx,
+ IN MOVE mv,
+ IN ULONG x, // this move's slot in ctx->sMoveStack,
+ // AFTER the caller's own index was
+ // incremented past it (same convention
+ // search.c's inline code used -- every
+ // "this move's own index" use inside
+ // the kernel computes (x - 1) itself)
+ IN FLAG fIsGeneratedMoveStage, // (uStage == TRY_GENERATED_MOVES)
+ // in search.c's own terms -- passed as
+ // a plain FLAG since TRY_HASH_MOVE/
+ // TRY_GENERATED_MOVES are search.c-
+ // private #defines, not visible here.
+ // Needed only for GetMoveSee's index arg
+ IN ULONG uDepth,
+ IN SCORE iAlpha,
+ IN SCORE iBeta,
+ IN SCORE iEval,
+ IN INT iPositionalExtend,
+ IN FLAG fIsLeftoverMove,
+ IN FLAG fThreat,
+ IN FLAG fFullWindow, // TRUE only for the actual first move
+ // tried at this node -- explicit, not
+ // inferred from an iBestScore sentinel
+ // the way search.c's inline code did
+ // (see UNIFY_SEARCH.md on why that
+ // matters once HelpSearch() also calls
+ // this: its first move off the shared
+ // pool is never the node's first move)
+ IN ULONG uRawDepthB, // node-level hoists, loop-invariant --
+ IN ULONG uRawImproveB, // caller computes once, not per-move
+ IN BITBOARD bbMinorAttacksSnapshot[2],
+ IN BITBOARD bbRookAttacksSnapshot[2],
+#ifdef MOVE_ORDERING_EVIDENCE_COLLECTION
+ IN FLAG fThisMoveIsCountermoveMatch,
+ IN ULONG uCMEvidenceBucket,
+#endif
+#ifdef TEST_NULL
+ IN FLAG fNullCalibEligible,
+ IN ULONG uNullCalibBucket,
+ OUT UINT64 *puNullCalibFirstMoveNodeCost,
+#endif
+ IN OUT ULONG *puLegalMoves,
+ OUT SCORE *piScore
+#ifdef PERF_COUNTERS
+ , OUT FLAG *pfMoveIsPruneReduceEligible
+ , OUT ULONG *puThisMoveHangBucket
+ , OUT ULONG *puThisMoveGapBucket
+ , OUT ULONG *puThisMoveNumBucket
+ , OUT ULONG *puThisMoveDepthBucket
+#endif
+ );
+
SCORE
RescoreMovesViaSearch(IN SEARCHER_THREAD_CONTEXT *ctx,
IN ULONG uDepth,