From e2d3c163cbdd98de6f0399db358be964d014d313 Mon Sep 17 00:00:00 2001 From: protonspring Date: Tue, 1 Jan 2019 05:35:53 -0700 Subject: [PATCH 1/9] Remove as useless micro-optimization in pawns generation (#1915) The extra condition is used as a shortcut to skip the following 3 assignments: ```C++ Bitboard b1 = shift(pawnsOn7) & enemies; Bitboard b2 = shift(pawnsOn7) & enemies; Bitboard b3 = shift(pawnsOn7) & emptySquares; ``` In case of EVASION with no target on 8th rank (the common case), we end up performing the 3 statements for nothing because b1 = b2 = b3 = 0. But this is just a small micro-optimization and the condition is quite confusing, so just remove it and prefer a readable code instead. STC LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 78020 W: 16978 L: 16967 D: 44075 http://tests.stockfishchess.org/tests/view/5c27b4fe0ebc5902ba135bb0 No functional change. --- src/movegen.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/movegen.cpp b/src/movegen.cpp index 76a27d9f..e7678979 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -96,7 +96,6 @@ namespace { // Compute our parametrized parameters at compile time, named according to // the point of view of white side. constexpr Color Them = (Us == WHITE ? BLACK : WHITE); - constexpr Bitboard TRank8BB = (Us == WHITE ? Rank8BB : Rank1BB); constexpr Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB); constexpr Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB); constexpr Direction Up = (Us == WHITE ? NORTH : SOUTH); @@ -161,7 +160,7 @@ namespace { } // Promotions and underpromotions - if (pawnsOn7 && (Type != EVASIONS || (target & TRank8BB))) + if (pawnsOn7) { if (Type == CAPTURES) emptySquares = ~pos.pieces(); From 7accf07c0b9cb3e2b932da5dd11b7d321745895e Mon Sep 17 00:00:00 2001 From: protonspring Date: Tue, 1 Jan 2019 05:36:56 -0700 Subject: [PATCH 2/9] Remove "Any" predicate filter (#1914) This custom predicate filter creates an unnecessary abstraction layer, but doesn't make the code any more readable. The code is clear enough without it. No functional change. --- src/movepick.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index 883135d4..d8ab68e7 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -31,9 +31,6 @@ namespace { QSEARCH_TT, QCAPTURE_INIT, QCAPTURE, QCHECK_INIT, QCHECK }; - // Helper filter used with select() - const auto Any = [](){ return true; }; - // partial_insertion_sort() sorts moves in descending order up to and including // a given limit. The order of moves smaller than the limit is left unspecified. void partial_insertion_sort(ExtMove* begin, ExtMove* end, int limit) { @@ -225,7 +222,7 @@ top: /* fallthrough */ case BAD_CAPTURE: - return select(Any); + return select([](){ return true; }); case EVASION_INIT: cur = moves; @@ -236,7 +233,7 @@ top: /* fallthrough */ case EVASION: - return select(Any); + return select([](){ return true; }); case PROBCUT: return select([&](){ return pos.see_ge(move, threshold); }); @@ -261,7 +258,7 @@ top: /* fallthrough */ case QCHECK: - return select(Any); + return select([](){ return true; }); } assert(false); From 79c97625a42c131708af953839900aff31102454 Mon Sep 17 00:00:00 2001 From: protonspring Date: Tue, 1 Jan 2019 05:38:09 -0700 Subject: [PATCH 3/9] Remove openFiles in pawns. (#1917) A single popcount in evaluate.cpp replaces all openFiles stuff in pawns. It doesn't seem to affect performance at all. STC LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 28103 W: 6134 L: 6025 D: 15944 http://tests.stockfishchess.org/tests/view/5b7d70a20ebc5902bdbb1999 No functional change. --- src/evaluate.cpp | 3 ++- src/pawns.cpp | 1 - src/pawns.h | 2 -- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 53a54006..0a7d0af9 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -717,7 +717,8 @@ namespace { behind |= (Us == WHITE ? behind >> 16 : behind << 16); int bonus = popcount(safe) + popcount(behind & safe); - int weight = pos.count(Us) - 2 * pe->open_files(); + int weight = pos.count(Us) + - 2 * popcount(pe->semiopenFiles[WHITE] & pe->semiopenFiles[BLACK]); Score score = make_score(bonus * weight * weight / 16, 0); diff --git a/src/pawns.cpp b/src/pawns.cpp index edd670b8..a9fc92b2 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -185,7 +185,6 @@ Entry* probe(const Position& pos) { e->key = key; e->scores[WHITE] = evaluate(pos, e); e->scores[BLACK] = evaluate(pos, e); - e->openFiles = popcount(e->semiopenFiles[WHITE] & e->semiopenFiles[BLACK]); e->asymmetry = popcount( (e->passedPawns[WHITE] | e->passedPawns[BLACK]) | (e->semiopenFiles[WHITE] ^ e->semiopenFiles[BLACK])); diff --git a/src/pawns.h b/src/pawns.h index df220eab..67f966ad 100644 --- a/src/pawns.h +++ b/src/pawns.h @@ -39,7 +39,6 @@ struct Entry { Bitboard pawn_attacks_span(Color c) const { return pawnAttacksSpan[c]; } int weak_unopposed(Color c) const { return weakUnopposed[c]; } int pawn_asymmetry() const { return asymmetry; } - int open_files() const { return openFiles; } int semiopen_file(Color c, File f) const { return semiopenFiles[c] & (1 << f); @@ -73,7 +72,6 @@ struct Entry { int semiopenFiles[COLOR_NB]; int pawnsOnSquares[COLOR_NB][COLOR_NB]; // [color][light/dark squares] int asymmetry; - int openFiles; }; typedef HashTable Table; From eb6d7f537d214c4dc8bde7d4fdc2aaead47dd3c3 Mon Sep 17 00:00:00 2001 From: Marco Costalba Date: Tue, 1 Jan 2019 14:10:26 +0100 Subject: [PATCH 4/9] Assorted trivial cleanups (#1894) To address https://github.com/official-stockfish/Stockfish/issues/1862 No functional change. --- src/bitboard.cpp | 4 ++-- src/endgame.cpp | 5 +++- src/evaluate.cpp | 8 +++---- src/movegen.cpp | 8 +++---- src/movepick.h | 4 ++-- src/pawns.cpp | 2 +- src/position.cpp | 10 ++++---- src/psqt.cpp | 18 +++++++------- src/search.cpp | 62 ++++++++++++++++++++++++------------------------ tests/perft.sh | 2 +- 10 files changed, 63 insertions(+), 60 deletions(-) diff --git a/src/bitboard.cpp b/src/bitboard.cpp index 398c6bf7..8079b783 100644 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@ -184,8 +184,8 @@ namespace { // init_magics() computes all rook and bishop attacks at startup. Magic // bitboards are used to look up attacks of sliding pieces. As a reference see - // chessprogramming.wikispaces.com/Magic+Bitboards. In particular, here we - // use the so called "fancy" approach. + // www.chessprogramming.org/Magic_Bitboards. In particular, here we use the so + // called "fancy" approach. void init_magics(Bitboard table[], Magic magics[], Direction directions[]) { diff --git a/src/endgame.cpp b/src/endgame.cpp index 66ee54d8..835173c4 100644 --- a/src/endgame.cpp +++ b/src/endgame.cpp @@ -131,7 +131,7 @@ Value Endgame::operator()(const Position& pos) const { Square loserKSq = pos.square(weakSide); Square bishopSq = pos.square(strongSide); - // If our Bishop does not attack A1/H8, we flip the enemy king square + // If our Bishop does not attack A1/H8, we flip the enemy king square // to drive to opposite corners (A8/H1). Value result = VALUE_KNOWN_WIN @@ -724,6 +724,9 @@ ScaleFactor Endgame::operator()(const Position& pos) const { template<> ScaleFactor Endgame::operator()(const Position& pos) const { + assert(verify_material(pos, strongSide, KnightValueMg, 1)); + assert(verify_material(pos, weakSide, BishopValueMg, 0)); + Square pawnSq = pos.square(strongSide); Square bishopSq = pos.square(weakSide); Square weakKingSq = pos.square(weakSide); diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 0a7d0af9..725ea49c 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -213,7 +213,7 @@ namespace { // kingRing[color] are the squares adjacent to the king, plus (only for a // king on its first rank) the squares two ranks in front. For instance, // if black's king is on g8, kingRing[BLACK] is f8, h8, f7, g7, h7, f6, g6 - // and h6. It is set to 0 when king safety evaluation is skipped. + // and h6. Bitboard kingRing[COLOR_NB]; // kingAttackersCount[color] is the number of pieces of the given color @@ -505,7 +505,7 @@ namespace { Score score = SCORE_ZERO; // Non-pawn enemies - nonPawnEnemies = pos.pieces(Them) & ~pos.pieces(Them, PAWN); + nonPawnEnemies = pos.pieces(Them) & ~pos.pieces(PAWN); // Squares strongly protected by the enemy, either because they defend the // square with a pawn, or because they defend the square twice and we don't. @@ -717,8 +717,8 @@ namespace { behind |= (Us == WHITE ? behind >> 16 : behind << 16); int bonus = popcount(safe) + popcount(behind & safe); - int weight = pos.count(Us) - - 2 * popcount(pe->semiopenFiles[WHITE] & pe->semiopenFiles[BLACK]); + int weight = pos.count(Us) + - 2 * popcount(pe->semiopenFiles[WHITE] & pe->semiopenFiles[BLACK]); Score score = make_score(bonus * weight * weight / 16, 0); diff --git a/src/movegen.cpp b/src/movegen.cpp index e7678979..c3cff08d 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -28,6 +28,7 @@ namespace { template ExtMove* generate_castling(const Position& pos, ExtMove* moveList) { + constexpr Color Them = (Us == WHITE ? BLACK : WHITE); constexpr CastlingRight Cr = Us | Cs; constexpr bool KingSide = (Cs == KING_SIDE); @@ -39,7 +40,7 @@ namespace { Square kfrom = pos.square(Us); Square rfrom = pos.castling_rook_square(Cr); Square kto = relative_square(Us, KingSide ? SQ_G1 : SQ_C1); - Bitboard enemies = pos.pieces(~Us); + Bitboard enemies = pos.pieces(Them); assert(!pos.checkers()); @@ -53,7 +54,7 @@ namespace { // Because we generate only legal castling moves we need to verify that // when moving the castling rook we do not discover some hidden checker. // For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1. - if (Chess960 && (attacks_bb(kto, pos.pieces() ^ rfrom) & pos.pieces(~Us, ROOK, QUEEN))) + if (Chess960 && (attacks_bb(kto, pos.pieces() ^ rfrom) & pos.pieces(Them, ROOK, QUEEN))) return moveList; Move m = make(kfrom, rfrom); @@ -93,8 +94,7 @@ namespace { template ExtMove* generate_pawn_moves(const Position& pos, ExtMove* moveList, Bitboard target) { - // Compute our parametrized parameters at compile time, named according to - // the point of view of white side. + // Compute some compile time parameters relative to the white side constexpr Color Them = (Us == WHITE ? BLACK : WHITE); constexpr Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB); constexpr Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB); diff --git a/src/movepick.h b/src/movepick.h index 7a55e21c..d9ecba99 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -85,11 +85,11 @@ enum StatsParams { NOT_USED = 0 }; /// ButterflyHistory records how often quiet moves have been successful or /// unsuccessful during the current search, and is used for reduction and move /// ordering decisions. It uses 2 tables (one for each color) indexed by -/// the move's from and to squares, see chessprogramming.wikispaces.com/Butterfly+Boards +/// the move's from and to squares, see www.chessprogramming.org/Butterfly_Boards typedef Stats ButterflyHistory; /// CounterMoveHistory stores counter moves indexed by [piece][to] of the previous -/// move, see chessprogramming.wikispaces.com/Countermove+Heuristic +/// move, see www.chessprogramming.org/Countermove_Heuristic typedef Stats CounterMoveHistory; /// CapturePieceToHistory is addressed by a move's [piece][to][captured piece type] diff --git a/src/pawns.cpp b/src/pawns.cpp index a9fc92b2..c581c8e7 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -115,7 +115,7 @@ namespace { // not attacked more times than defended. if ( !(stoppers ^ lever ^ leverPush) && popcount(support) >= popcount(lever) - 1 - && popcount(phalanx) >= popcount(leverPush)) + && popcount(phalanx) >= popcount(leverPush)) e->passedPawns[Us] |= s; else if ( stoppers == SquareBB[s + Up] diff --git a/src/position.cpp b/src/position.cpp index bd5daa6d..0396eb9f 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -183,7 +183,7 @@ void Position::init() { { std::swap(cuckoo[i], key); std::swap(cuckooMove[i], move); - if (move == 0) // Arrived at empty slot ? + if (move == MOVE_NONE) // Arrived at empty slot? break; i = (i == H1(key)) ? H2(key) : H1(key); // Push victim to alternative slot } @@ -465,16 +465,16 @@ const string Position::fen() const { ss << (sideToMove == WHITE ? " w " : " b "); if (can_castle(WHITE_OO)) - ss << (chess960 ? char('A' + file_of(castling_rook_square(WHITE | KING_SIDE))) : 'K'); + ss << (chess960 ? char('A' + file_of(castling_rook_square(WHITE_OO ))) : 'K'); if (can_castle(WHITE_OOO)) - ss << (chess960 ? char('A' + file_of(castling_rook_square(WHITE | QUEEN_SIDE))) : 'Q'); + ss << (chess960 ? char('A' + file_of(castling_rook_square(WHITE_OOO))) : 'Q'); if (can_castle(BLACK_OO)) - ss << (chess960 ? char('a' + file_of(castling_rook_square(BLACK | KING_SIDE))) : 'k'); + ss << (chess960 ? char('a' + file_of(castling_rook_square(BLACK_OO ))) : 'k'); if (can_castle(BLACK_OOO)) - ss << (chess960 ? char('a' + file_of(castling_rook_square(BLACK | QUEEN_SIDE))) : 'q'); + ss << (chess960 ? char('a' + file_of(castling_rook_square(BLACK_OOO))) : 'q'); if (!can_castle(ANY_CASTLING)) ss << '-'; diff --git a/src/psqt.cpp b/src/psqt.cpp index a4a5e0a0..bc3e5392 100644 --- a/src/psqt.cpp +++ b/src/psqt.cpp @@ -90,15 +90,15 @@ constexpr Score Bonus[][RANK_NB][int(FILE_NB) / 2] = { } }; -constexpr Score PBonus[RANK_NB][FILE_NB] = - { // Pawn - { S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0) }, - { S( 0,-11), S( -3, -4), S( 13, -1), S( 19, -4), S( 16, 17), S( 13, 7), S( 4, 4), S( -4,-13) }, - { S(-16, -8), S(-12, -6), S( 20, -3), S( 21, 0), S( 25,-11), S( 29, 3), S( 0, 0), S(-27, -1) }, - { S(-11, 3), S(-17, 6), S( 11,-10), S( 21, 1), S( 32, -6), S( 19,-11), S( -5, 0), S(-14, -2) }, - { S( 4, 13), S( 6, 7), S( -8, 3), S( 3, -5), S( 8,-15), S( -2, -1), S(-19, 9), S( -5, 13) }, - { S( -5, 25), S(-19, 20), S( 7, 16), S( 8, 12), S( -7, 21), S( -2, 3), S(-10, -4), S(-16, 15) }, - { S(-10, 6), S( 9, -5), S( -7, 16), S(-12, 27), S( -7, 15), S( -8, 11), S( 16, -7), S( -8, 4) } +constexpr Score PBonus[RANK_NB][FILE_NB] = + { // Pawn (asymmetric distribution) + { }, + { S( 0,-11), S( -3,-4), S(13, -1), S( 19, -4), S(16, 17), S(13, 7), S( 4, 4), S( -4,-13) }, + { S(-16, -8), S(-12,-6), S(20, -3), S( 21, 0), S(25,-11), S(29, 3), S( 0, 0), S(-27, -1) }, + { S(-11, 3), S(-17, 6), S(11,-10), S( 21, 1), S(32, -6), S(19,-11), S( -5, 0), S(-14, -2) }, + { S( 4, 13), S( 6, 7), S(-8, 3), S( 3, -5), S( 8,-15), S(-2, -1), S(-19, 9), S( -5, 13) }, + { S( -5, 25), S(-19,20), S( 7, 16), S( 8, 12), S(-7, 21), S(-2, 3), S(-10, -4), S(-16, 15) }, + { S(-10, 6), S( 9,-5), S(-7, 16), S(-12, 27), S(-7, 15), S(-8, 11), S( 16, -7), S( -8, 4) } }; #undef S diff --git a/src/search.cpp b/src/search.cpp index 4a541c5b..fe9b446e 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -113,8 +113,8 @@ namespace { Value value_from_tt(Value v, int ply); void update_pv(Move* pv, Move move, Move* childPv); void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus); - void update_quiet_stats(const Position& pos, Stack* ss, Move move, Move* quiets, int quietsCnt, int bonus); - void update_capture_stats(const Position& pos, Move move, Move* captures, int captureCnt, int bonus); + void update_quiet_stats(const Position& pos, Stack* ss, Move move, Move* quiets, int quietCount, int bonus); + void update_capture_stats(const Position& pos, Move move, Move* captures, int captureCount, int bonus); inline bool gives_check(const Position& pos, Move move) { Color us = pos.side_to_move(); @@ -498,32 +498,32 @@ void Thread::search() { if ( Limits.use_time_management() && !Threads.stop && !Threads.stopOnPonderhit) + { + double fallingEval = (306 + 119 * failedLow + 6 * (mainThread->previousScore - bestValue)) / 581.0; + fallingEval = std::max(0.5, std::min(1.5, fallingEval)); + + // If the bestMove is stable over several iterations, reduce time accordingly + timeReduction = 1.0; + for (int i : {3, 4, 5}) + if (lastBestMoveDepth * i < completedDepth) + timeReduction *= 1.25; + + // Use part of the gained time from a previous stable move for the current move + double bestMoveInstability = 1.0 + mainThread->bestMoveChanges; + bestMoveInstability *= std::pow(mainThread->previousTimeReduction, 0.528) / timeReduction; + + // Stop the search if we have only one legal move, or if available time elapsed + if ( rootMoves.size() == 1 + || Time.elapsed() > Time.optimum() * bestMoveInstability * fallingEval) { - double fallingEval = (306 + 119 * failedLow + 6 * (mainThread->previousScore - bestValue)) / 581.0; - fallingEval = std::max(0.5, std::min(1.5, fallingEval)); - - // If the bestMove is stable over several iterations, reduce time accordingly - timeReduction = 1.0; - for (int i : {3, 4, 5}) - if (lastBestMoveDepth * i < completedDepth) - timeReduction *= 1.25; - - // Use part of the gained time from a previous stable move for the current move - double bestMoveInstability = 1.0 + mainThread->bestMoveChanges; - bestMoveInstability *= std::pow(mainThread->previousTimeReduction, 0.528) / timeReduction; - - // Stop the search if we have only one legal move, or if available time elapsed - if ( rootMoves.size() == 1 - || Time.elapsed() > Time.optimum() * bestMoveInstability * fallingEval) - { - // If we are allowed to ponder do not stop the search now but - // keep pondering until the GUI sends "ponderhit" or "stop". - if (Threads.ponder) - Threads.stopOnPonderhit = true; - else - Threads.stop = true; - } + // If we are allowed to ponder do not stop the search now but + // keep pondering until the GUI sends "ponderhit" or "stop". + if (Threads.ponder) + Threads.stopOnPonderhit = true; + else + Threads.stop = true; } + } } if (!mainThread) @@ -1481,7 +1481,7 @@ moves_loop: // When in check, search starts from here // update_capture_stats() updates move sorting heuristics when a new capture best move is found void update_capture_stats(const Position& pos, Move move, - Move* captures, int captureCnt, int bonus) { + Move* captures, int captureCount, int bonus) { CapturePieceToHistory& captureHistory = pos.this_thread()->captureHistory; Piece moved_piece = pos.moved_piece(move); @@ -1491,7 +1491,7 @@ moves_loop: // When in check, search starts from here captureHistory[moved_piece][to_sq(move)][captured] << bonus; // Decrease all the other played capture moves - for (int i = 0; i < captureCnt; ++i) + for (int i = 0; i < captureCount; ++i) { moved_piece = pos.moved_piece(captures[i]); captured = type_of(pos.piece_on(to_sq(captures[i]))); @@ -1503,7 +1503,7 @@ moves_loop: // When in check, search starts from here // update_quiet_stats() updates move sorting heuristics when a new quiet best move is found void update_quiet_stats(const Position& pos, Stack* ss, Move move, - Move* quiets, int quietsCnt, int bonus) { + Move* quiets, int quietCount, int bonus) { if (ss->killers[0] != move) { @@ -1523,7 +1523,7 @@ moves_loop: // When in check, search starts from here } // Decrease all the other played quiet moves - for (int i = 0; i < quietsCnt; ++i) + for (int i = 0; i < quietCount; ++i) { thisThread->mainHistory[us][from_to(quiets[i])] << -bonus; update_continuation_histories(ss, pos.moved_piece(quiets[i]), to_sq(quiets[i]), -bonus); @@ -1666,7 +1666,7 @@ bool RootMove::extract_ponder_from_tt(Position& pos) { assert(pv.size() == 1); - if (!pv[0]) + if (pv[0] == MOVE_NONE) return false; pos.do_move(pv[0], st); diff --git a/tests/perft.sh b/tests/perft.sh index d4022211..545e750f 100755 --- a/tests/perft.sh +++ b/tests/perft.sh @@ -1,5 +1,5 @@ #!/bin/bash -# verify perft numbers (positions from https://chessprogramming.wikispaces.com/Perft+Results) +# verify perft numbers (positions from www.chessprogramming.org/Perft_Results) error() { From 3c576efa77f431cf3687881b8fd6a728e87ed97d Mon Sep 17 00:00:00 2001 From: Marco Costalba Date: Wed, 2 Jan 2019 08:31:03 +0100 Subject: [PATCH 5/9] Delay castling legality check Delay legality check of castling moves at search time, just before making the move, as is the standard with all the other move types. This should avoid an useless and not trivial legality check when the castling is then not tried later. For instance due to a previous cut-off. The patch is also a big simplification and allows to entirely remove generate_castling() Bench changes due to a different move sequence out of MovePicker. STC: LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 45073 W: 9918 L: 9843 D: 25312 http://tests.stockfishchess.org/tests/view/5c2f176f0ebc596a450bdfb3 LTC: LLR: 3.15 (-2.94,2.94) [-3.00,1.00] Total: 10156 W: 1707 L: 1560 D: 6889 http://tests.stockfishchess.org/tests/view/5c2e7dfd0ebc596a450bcdf4 Verified with perft both in standard and Chess960 cases. Closes https://github.com/official-stockfish/Stockfish/pull/1929 Bench: 3559104 --- src/movegen.cpp | 63 +++++++----------------------------------------- src/position.cpp | 31 +++++++++++++++++++----- src/position.h | 2 +- src/types.h | 4 ++- 4 files changed, 38 insertions(+), 62 deletions(-) diff --git a/src/movegen.cpp b/src/movegen.cpp index c3cff08d..5ed24893 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -25,48 +25,6 @@ namespace { - template - ExtMove* generate_castling(const Position& pos, ExtMove* moveList) { - - constexpr Color Them = (Us == WHITE ? BLACK : WHITE); - constexpr CastlingRight Cr = Us | Cs; - constexpr bool KingSide = (Cs == KING_SIDE); - - if (pos.castling_impeded(Cr) || !pos.can_castle(Cr)) - return moveList; - - // After castling, the rook and king final positions are the same in Chess960 - // as they would be in standard chess. - Square kfrom = pos.square(Us); - Square rfrom = pos.castling_rook_square(Cr); - Square kto = relative_square(Us, KingSide ? SQ_G1 : SQ_C1); - Bitboard enemies = pos.pieces(Them); - - assert(!pos.checkers()); - - const Direction step = Chess960 ? kto > kfrom ? WEST : EAST - : KingSide ? WEST : EAST; - - for (Square s = kto; s != kfrom; s += step) - if (pos.attackers_to(s) & enemies) - return moveList; - - // Because we generate only legal castling moves we need to verify that - // when moving the castling rook we do not discover some hidden checker. - // For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1. - if (Chess960 && (attacks_bb(kto, pos.pieces() ^ rfrom) & pos.pieces(Them, ROOK, QUEEN))) - return moveList; - - Move m = make(kfrom, rfrom); - - if (Checks && !pos.gives_check(m)) - return moveList; - - *moveList++ = m; - return moveList; - } - - template ExtMove* make_promotions(ExtMove* moveList, Square to, Square ksq) { @@ -261,7 +219,9 @@ namespace { template ExtMove* generate_all(const Position& pos, ExtMove* moveList, Bitboard target) { - constexpr bool Checks = Type == QUIET_CHECKS; + constexpr CastlingRight OO = Us | KING_SIDE; + constexpr CastlingRight OOO = Us | QUEEN_SIDE; + constexpr bool Checks = Type == QUIET_CHECKS; // Reduce template instantations moveList = generate_pawn_moves(pos, moveList, target); moveList = generate_moves(pos, moveList, Us, target); @@ -275,19 +235,14 @@ namespace { Bitboard b = pos.attacks_from(ksq) & target; while (b) *moveList++ = make_move(ksq, pop_lsb(&b)); - } - if (Type != CAPTURES && Type != EVASIONS && pos.castling_rights(Us)) - { - if (pos.is_chess960()) + if (Type != CAPTURES && pos.can_castle(CastlingRight(OO | OOO))) { - moveList = generate_castling(pos, moveList); - moveList = generate_castling(pos, moveList); - } - else - { - moveList = generate_castling(pos, moveList); - moveList = generate_castling(pos, moveList); + if (!pos.castling_impeded(OO) && pos.can_castle(OO)) + *moveList++ = make(ksq, pos.castling_rook_square(OO)); + + if (!pos.castling_impeded(OOO) && pos.can_castle(OOO)) + *moveList++ = make(ksq, pos.castling_rook_square(OOO)); } } diff --git a/src/position.cpp b/src/position.cpp index 0396eb9f..21eff88c 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -540,6 +540,7 @@ bool Position::legal(Move m) const { Color us = sideToMove; Square from = from_sq(m); + Square to = to_sq(m); assert(color_of(moved_piece(m)) == us); assert(piece_on(square(us)) == make_piece(us, KING)); @@ -550,7 +551,6 @@ bool Position::legal(Move m) const { if (type_of(m) == ENPASSANT) { Square ksq = square(us); - Square to = to_sq(m); Square capsq = to - pawn_push(us); Bitboard occupied = (pieces() ^ from ^ capsq) | to; @@ -563,16 +563,35 @@ bool Position::legal(Move m) const { && !(attacks_bb(ksq, occupied) & pieces(~us, QUEEN, BISHOP)); } - // If the moving piece is a king, check whether the destination - // square is attacked by the opponent. Castling moves are checked - // for legality during move generation. + // Castling moves generation does not check if the castling path is clear of + // enemy attacks, it is delayed at a later time: now! + if (type_of(m) == CASTLING) + { + // After castling, the rook and king final positions are the same in + // Chess960 as they would be in standard chess. + to = relative_square(us, to > from ? SQ_G1 : SQ_C1); + Direction step = to > from ? WEST : EAST; + + for (Square s = to; s != from; s += step) + if (attackers_to(s) & pieces(~us)) + return false; + + // In case of Chess960, verify that when moving the castling rook we do + // not discover some hidden checker. + // For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1. + return !chess960 + || !(attacks_bb(to, pieces() ^ to_sq(m)) & pieces(~us, ROOK, QUEEN)); + } + + // If the moving piece is a king, check whether the destination square is + // attacked by the opponent. if (type_of(piece_on(from)) == KING) - return type_of(m) == CASTLING || !(attackers_to(to_sq(m)) & pieces(~us)); + return !(attackers_to(to) & pieces(~us)); // A non-king move is legal if and only if it is not pinned or it // is moving along the ray towards or away from the king. return !(blockers_for_king(us) & from) - || aligned(from, to_sq(m), square(us)); + || aligned(from, to, square(us)); } diff --git a/src/position.h b/src/position.h index d94ef185..74173d03 100644 --- a/src/position.h +++ b/src/position.h @@ -265,7 +265,7 @@ inline bool Position::can_castle(CastlingRight cr) const { } inline int Position::castling_rights(Color c) const { - return st->castlingRights & ((WHITE_OO | WHITE_OOO) << (2 * c)); + return st->castlingRights & (c == WHITE ? WHITE_CASTLING : BLACK_CASTLING); } inline bool Position::castling_impeded(CastlingRight cr) const { diff --git a/src/types.h b/src/types.h index c4c2752c..8e27606c 100644 --- a/src/types.h +++ b/src/types.h @@ -141,7 +141,9 @@ enum CastlingRight { WHITE_OOO = WHITE_OO << 1, BLACK_OO = WHITE_OO << 2, BLACK_OOO = WHITE_OO << 3, - ANY_CASTLING = WHITE_OO | WHITE_OOO | BLACK_OO | BLACK_OOO, + WHITE_CASTLING = WHITE_OO | WHITE_OOO, + BLACK_CASTLING = BLACK_OO | BLACK_OOO, + ANY_CASTLING = WHITE_CASTLING | BLACK_CASTLING, CASTLING_RIGHT_NB = 16 }; From bb843a00c1ef381162dc9c2491b5436b6cf5563f Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Thu, 3 Jan 2019 23:56:11 +0100 Subject: [PATCH 6/9] Check tablebase files This addresses partially issue #1911 in that it documents in our Readme the command that users can use to verifying the md5sum of their downloaded tablebase files. Additionally, a quick check of the file size (the size of each tablebase file modulo 64 is 16 as pointed out by @syzygy1) has been implemented at launch time in Stockfish. Closes https://github.com/official-stockfish/Stockfish/pull/1927 and https://github.com/official-stockfish/Stockfish/issues/1911 No functional change. --- Readme.md | 4 +++- src/syzygy/tbprobe.cpp | 34 ++++++++++++++++++++++++++-------- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/Readme.md b/Readme.md index a68ef560..bc058dbc 100644 --- a/Readme.md +++ b/Readme.md @@ -86,7 +86,9 @@ Currently, Stockfish has the following UCI options: Example: `C:\tablebases\wdl345;C:\tablebases\wdl6;D:\tablebases\dtz345;D:\tablebases\dtz6` It is recommended to store .rtbw files on an SSD. There is no loss in storing - the .rtbz files on a regular HD. + the .rtbz files on a regular HD. It is recommended to verify all md5 checksums + of the downloaded tablebase files (`md5sum -c checksum.md5`) as corruption will + lead to engine crashes. * #### SyzygyProbeDepth Minimum remaining search depth for which a position is probed. Set this option diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index 57c7d872..bf6ed421 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -214,14 +214,22 @@ public: return *baseAddress = nullptr, nullptr; fstat(fd, &statbuf); + + if (statbuf.st_size % 64 != 16) + { + std::cerr << "Corrupt tablebase file " << fname << std::endl; + exit(EXIT_FAILURE); + } + *mapping = statbuf.st_size; *baseAddress = mmap(nullptr, statbuf.st_size, PROT_READ, MAP_SHARED, fd, 0); madvise(*baseAddress, statbuf.st_size, MADV_RANDOM); ::close(fd); - if (*baseAddress == MAP_FAILED) { + if (*baseAddress == MAP_FAILED) + { std::cerr << "Could not mmap() " << fname << std::endl; - exit(1); + exit(EXIT_FAILURE); } #else // Note FILE_FLAG_RANDOM_ACCESS is only a hint to Windows and as such may get ignored. @@ -233,21 +241,30 @@ public: DWORD size_high; DWORD size_low = GetFileSize(fd, &size_high); + + if (size_low % 64 != 16) + { + std::cerr << "Corrupt tablebase file " << fname << std::endl; + exit(EXIT_FAILURE); + } + HANDLE mmap = CreateFileMapping(fd, nullptr, PAGE_READONLY, size_high, size_low, nullptr); CloseHandle(fd); - if (!mmap) { + if (!mmap) + { std::cerr << "CreateFileMapping() failed" << std::endl; - exit(1); + exit(EXIT_FAILURE); } *mapping = (uint64_t)mmap; *baseAddress = MapViewOfFile(mmap, FILE_MAP_READ, 0, 0, 0); - if (!*baseAddress) { + if (!*baseAddress) + { std::cerr << "MapViewOfFile() failed, name = " << fname << ", error = " << GetLastError() << std::endl; - exit(1); + exit(EXIT_FAILURE); } #endif uint8_t* data = (uint8_t*)*baseAddress; @@ -255,7 +272,8 @@ public: constexpr uint8_t Magics[][4] = { { 0xD7, 0x66, 0x0C, 0xA5 }, { 0x71, 0xE8, 0x23, 0x5D } }; - if (memcmp(data, Magics[type == WDL], 4)) { + if (memcmp(data, Magics[type == WDL], 4)) + { std::cerr << "Corrupted table in file " << fname << std::endl; unmap(*baseAddress, *mapping); return *baseAddress = nullptr, nullptr; @@ -416,7 +434,7 @@ class TBTables { } } std::cerr << "TB hash table size too low!" << std::endl; - exit(1); + exit(EXIT_FAILURE); } public: From f69106f7bb82f97ba07fb06a31395039d08af9f4 Mon Sep 17 00:00:00 2001 From: Miguel Lahoz Date: Sun, 6 Jan 2019 04:53:21 +0800 Subject: [PATCH 7/9] Introduce Multi-Cut This was inspired after reading about [Multi-Cut](https://www.chessprogramming.org/Multi-Cut). We now do non-singular cut node pruning. The idea is to prune when we have a "backup plan" in case our expected fail high node does not fail high on the ttMove. For singular extensions, we do a search on all other moves but the ttMove. If this fails high on our original beta, this means that both the ttMove, as well as at least one other move was proven to fail high on a lower depth search. We then assume that one of these moves will work on a higher depth and prune. STC: LLR: 2.96 (-2.94,2.94) [0.50,4.50] Total: 72952 W: 16104 L: 15583 D: 41265 http://tests.stockfishchess.org/tests/view/5c3119640ebc596a450c0be5 LTC: LLR: 2.95 (-2.94,2.94) [0.00,3.50] Total: 27103 W: 4564 L: 4314 D: 18225 http://tests.stockfishchess.org/tests/view/5c3184c00ebc596a450c1662 Bench: 3145487 --- src/search.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index fe9b446e..c8e6c8b6 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -943,13 +943,21 @@ moves_loop: // When in check, search starts from here && tte->depth() >= depth - 3 * ONE_PLY && pos.legal(move)) { - Value reducedBeta = std::max(ttValue - 2 * depth / ONE_PLY, -VALUE_MATE); + Value singularBeta = std::max(ttValue - 2 * depth / ONE_PLY, -VALUE_MATE); ss->excludedMove = move; - value = search(pos, ss, reducedBeta - 1, reducedBeta, depth / 2, cutNode); + value = search(pos, ss, singularBeta - 1, singularBeta, depth / 2, cutNode); ss->excludedMove = MOVE_NONE; - if (value < reducedBeta) + if (value < singularBeta) extension = ONE_PLY; + + // Multi-cut pruning + // Our ttMove is assumed to fail high, and now we failed high also on a reduced + // search without the ttMove. So we assume this expected Cut-node is not singular, + // that is multiple moves fail high, and we can prune the whole subtree by returning + // the hard beta bound. + else if (cutNode && singularBeta > beta) + return beta; } else if ( givesCheck // Check extension (~2 Elo) && pos.see_ge(move)) From 70880b8e247c94d0a16a2fb50b41827726e00742 Mon Sep 17 00:00:00 2001 From: MJZ1977 <37274752+MJZ1977@users.noreply.github.com> Date: Wed, 9 Jan 2019 15:05:28 +0100 Subject: [PATCH 8/9] Flag critical search tree in hash table Introducing new concept, saving principal lines into the transposition table to generate a "critical search tree" which we can reuse later for intelligent pruning/extension decisions. For instance in this patch we just reduce reduction for these lines. But a lot of other ideas are possible. To go further : tune some parameters, how to add or remove lines from the critical search tree, how to use these lines in search choices, etc. STC : LLR: 2.94 (-2.94,2.94) [0.50,4.50] Total: 59761 W: 13321 L: 12863 D: 33577 +2.23 ELO http://tests.stockfishchess.org/tests/view/5c34da5d0ebc596a450c53d3 LTC : LLR: 2.96 (-2.94,2.94) [0.00,3.50] Total: 26826 W: 4439 L: 4191 D: 18196 +2.9 ELO http://tests.stockfishchess.org/tests/view/5c35ceb00ebc596a450c65b2 Special thanks to Miguel Lahoz for his help in transposition table in/out. Bench: 3399866 --- src/search.cpp | 26 +++++++++++++++++++------- src/tt.cpp | 12 ++++++------ src/tt.h | 8 +++++--- tests/instrumented.sh | 1 + 4 files changed, 31 insertions(+), 16 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index c8e6c8b6..b7e04fad 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -577,7 +577,7 @@ namespace { Move ttMove, move, excludedMove, bestMove; Depth extension, newDepth; Value bestValue, value, ttValue, eval, maxValue, pureStaticEval; - bool ttHit, inCheck, givesCheck, improving; + bool ttHit, pvHit, inCheck, givesCheck, improving; bool captureOrPromotion, doFullDepthSearch, moveCountPruning, skipQuiets, ttCapture, pvExact; Piece movedPiece; int moveCount, captureCount, quietCount; @@ -643,6 +643,7 @@ namespace { ttValue = ttHit ? value_from_tt(tte->value(), ss->ply) : VALUE_NONE; ttMove = rootNode ? thisThread->rootMoves[thisThread->pvIdx].pv[0] : ttHit ? tte->move() : MOVE_NONE; + pvHit = ttHit ? tte->pv_hit() : false; // At non-PV nodes we check for an early TT cutoff if ( !PvNode @@ -676,6 +677,11 @@ namespace { return ttValue; } + if ( depth > 6 * ONE_PLY + && !excludedMove + && PvNode) + pvHit = true; + // Step 5. Tablebases probe if (!rootNode && TB::Cardinality) { @@ -709,7 +715,7 @@ namespace { if ( b == BOUND_EXACT || (b == BOUND_LOWER ? value >= beta : value <= alpha)) { - tte->save(posKey, value_to_tt(value, ss->ply), b, + tte->save(posKey, value_to_tt(value, ss->ply), pvHit, b, std::min(DEPTH_MAX - ONE_PLY, depth + 6 * ONE_PLY), MOVE_NONE, VALUE_NONE); @@ -760,7 +766,7 @@ namespace { else ss->staticEval = eval = pureStaticEval = -(ss-1)->staticEval + 2 * Eval::Tempo; - tte->save(posKey, VALUE_NONE, BOUND_NONE, DEPTH_NONE, MOVE_NONE, pureStaticEval); + tte->save(posKey, VALUE_NONE, pvHit, BOUND_NONE, DEPTH_NONE, MOVE_NONE, pureStaticEval); } // Step 7. Razoring (~2 Elo) @@ -875,6 +881,7 @@ namespace { tte = TT.probe(posKey, ttHit); ttValue = ttHit ? value_from_tt(tte->value(), ss->ply) : VALUE_NONE; ttMove = ttHit ? tte->move() : MOVE_NONE; + pvHit = ttHit ? tte->pv_hit() : false; } moves_loop: // When in check, search starts from here @@ -1035,6 +1042,10 @@ moves_loop: // When in check, search starts from here { Depth r = reduction(improving, depth, moveCount); + // Decrease reduction if position is or has been on the PV + if (pvHit && !PvNode) + r -= ONE_PLY; + // Decrease reduction if opponent's move count is high (~10 Elo) if ((ss-1)->moveCount > 15) r -= ONE_PLY; @@ -1217,7 +1228,7 @@ moves_loop: // When in check, search starts from here bestValue = std::min(bestValue, maxValue); if (!excludedMove) - tte->save(posKey, value_to_tt(bestValue, ss->ply), + tte->save(posKey, value_to_tt(bestValue, ss->ply), pvHit, bestValue >= beta ? BOUND_LOWER : PvNode && bestMove ? BOUND_EXACT : BOUND_UPPER, depth, bestMove, pureStaticEval); @@ -1247,7 +1258,7 @@ moves_loop: // When in check, search starts from here Move ttMove, move, bestMove; Depth ttDepth; Value bestValue, value, ttValue, futilityValue, futilityBase, oldAlpha; - bool ttHit, inCheck, givesCheck, evasionPrunable; + bool ttHit, pvHit, inCheck, givesCheck, evasionPrunable; int moveCount; if (PvNode) @@ -1281,6 +1292,7 @@ moves_loop: // When in check, search starts from here tte = TT.probe(posKey, ttHit); ttValue = ttHit ? value_from_tt(tte->value(), ss->ply) : VALUE_NONE; ttMove = ttHit ? tte->move() : MOVE_NONE; + pvHit = ttHit ? tte->pv_hit() : false; if ( !PvNode && ttHit @@ -1318,7 +1330,7 @@ moves_loop: // When in check, search starts from here if (bestValue >= beta) { if (!ttHit) - tte->save(posKey, value_to_tt(bestValue, ss->ply), BOUND_LOWER, + tte->save(posKey, value_to_tt(bestValue, ss->ply), pvHit, BOUND_LOWER, DEPTH_NONE, MOVE_NONE, ss->staticEval); return bestValue; @@ -1429,7 +1441,7 @@ moves_loop: // When in check, search starts from here if (inCheck && bestValue == -VALUE_INFINITE) return mated_in(ss->ply); // Plies to mate from the root - tte->save(posKey, value_to_tt(bestValue, ss->ply), + tte->save(posKey, value_to_tt(bestValue, ss->ply), pvHit, bestValue >= beta ? BOUND_LOWER : PvNode && bestValue > oldAlpha ? BOUND_EXACT : BOUND_UPPER, ttDepth, bestMove, ss->staticEval); diff --git a/src/tt.cpp b/src/tt.cpp index 653d081f..d05de916 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -31,7 +31,7 @@ TranspositionTable TT; // Our global transposition table /// TTEntry::save saves a TTEntry -void TTEntry::save(Key k, Value v, Bound b, Depth d, Move m, Value ev) { +void TTEntry::save(Key k, Value v, bool PvNode, Bound b, Depth d, Move m, Value ev) { assert(d / ONE_PLY * ONE_PLY == d); @@ -47,7 +47,7 @@ void TTEntry::save(Key k, Value v, Bound b, Depth d, Move m, Value ev) { key16 = (uint16_t)(k >> 48); value16 = (int16_t)v; eval16 = (int16_t)ev; - genBound8 = (uint8_t)(TT.generation8 | b); + genBound8 = (uint8_t)(TT.generation8 | PvNode << 2 | b); depth8 = (int8_t)(d / ONE_PLY); } } @@ -122,7 +122,7 @@ TTEntry* TranspositionTable::probe(const Key key, bool& found) const { for (int i = 0; i < ClusterSize; ++i) if (!tte[i].key16 || tte[i].key16 == key16) { - tte[i].genBound8 = uint8_t(generation8 | tte[i].bound()); // Refresh + tte[i].genBound8 = uint8_t(generation8 | tte[i].pv_hit() << 2 | tte[i].bound()); // Refresh return found = (bool)tte[i].key16, &tte[i]; } @@ -131,11 +131,11 @@ TTEntry* TranspositionTable::probe(const Key key, bool& found) const { TTEntry* replace = tte; for (int i = 1; i < ClusterSize; ++i) // Due to our packed storage format for generation and its cyclic - // nature we add 259 (256 is the modulus plus 3 to keep the lowest + // nature we add 263 (263 is the modulus plus 7 to keep the lowest // two bound bits from affecting the result) to calculate the entry // age correctly even after generation8 overflows into the next cycle. - if ( replace->depth8 - ((259 + generation8 - replace->genBound8) & 0xFC) * 2 - > tte[i].depth8 - ((259 + generation8 - tte[i].genBound8) & 0xFC) * 2) + if ( replace->depth8 - ((263 + generation8 - replace->genBound8) & 0xF8) + > tte[i].depth8 - ((263 + generation8 - tte[i].genBound8) & 0xF8)) replace = &tte[i]; return found = false, replace; diff --git a/src/tt.h b/src/tt.h index 2cf82f58..8b98dbd5 100644 --- a/src/tt.h +++ b/src/tt.h @@ -30,7 +30,8 @@ /// move 16 bit /// value 16 bit /// eval value 16 bit -/// generation 6 bit +/// generation 5 bit +/// PvNode 1 bit /// bound type 2 bit /// depth 8 bit @@ -40,8 +41,9 @@ struct TTEntry { Value value() const { return (Value)value16; } Value eval() const { return (Value)eval16; } Depth depth() const { return (Depth)(depth8 * int(ONE_PLY)); } + bool pv_hit() const { return (bool)(genBound8 & 0x4); } Bound bound() const { return (Bound)(genBound8 & 0x3); } - void save(Key k, Value v, Bound b, Depth d, Move m, Value ev); + void save(Key k, Value v, bool PvNode, Bound b, Depth d, Move m, Value ev); private: friend class TranspositionTable; @@ -76,7 +78,7 @@ class TranspositionTable { public: ~TranspositionTable() { free(mem); } - void new_search() { generation8 += 4; } // Lower 2 bits are used by Bound + void new_search() { generation8 += 8; } // Lower 3 bits are used by PV flag and Bound TTEntry* probe(const Key key, bool& found) const; int hashfull() const; void resize(size_t mbSize); diff --git a/tests/instrumented.sh b/tests/instrumented.sh index 137d0e4a..eb70466f 100755 --- a/tests/instrumented.sh +++ b/tests/instrumented.sh @@ -45,6 +45,7 @@ race:TTEntry::bound race:TTEntry::save race:TTEntry::value race:TTEntry::eval +race:TTEntry::pv_hit race:TranspositionTable::probe race:TranspositionTable::hashfull From d2acdac1012cfbd8563fbe70157b2adbb05b2002 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Wed, 9 Jan 2019 16:14:34 +0100 Subject: [PATCH 9/9] Small improvements to the CI infrastructure - avoid inlining for the debug testing so that suppressions work - provide more output for triggered errors No functional change. --- .travis.yml | 2 +- tests/instrumented.sh | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index ea5bda1d..c4f68ae5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -63,7 +63,7 @@ script: # # Valgrind # - - export CXXFLAGS=-O1 + - export CXXFLAGS="-O1 -fno-inline" - if [ -x "$(command -v valgrind )" ]; then make clean && make -j2 ARCH=x86-64 debug=yes optimize=no build > /dev/null && ../tests/instrumented.sh --valgrind; fi - if [ -x "$(command -v valgrind )" ]; then ../tests/instrumented.sh --valgrind-thread; fi # diff --git a/tests/instrumented.sh b/tests/instrumented.sh index eb70466f..5156e02f 100755 --- a/tests/instrumented.sh +++ b/tests/instrumented.sh @@ -28,14 +28,14 @@ case $1 in echo "sanitizer-undefined testing started" prefix='!' exeprefix='' - postfix='2>&1 | grep "runtime error:"' + postfix='2>&1 | grep -A50 "runtime error:"' threads="1" ;; --sanitizer-thread) echo "sanitizer-thread testing started" prefix='!' exeprefix='' - postfix='2>&1 | grep "WARNING: ThreadSanitizer:"' + postfix='2>&1 | grep -A50 "WARNING: ThreadSanitizer:"' threads="2" cat << EOF > tsan.supp