/** * Chain the retranslation blocks. This method enforces that, for * each region block, all its successor have distinct SrcKeys. */ void RegionDesc::chainRetransBlocks() { jit::vector<Chain> chains; BlockToChainMap block2chain; // 1. Initially assign each region block to its own chain. for (auto b : blocks()) { auto bid = b->id(); auto cid = chains.size(); chains.push_back({cid, {bid}}); block2chain[bid] = cid; } // 2. For each block, if it has 2 successors with the same SrcKey, // then merge the successors' chains into one. for (auto b : blocks()) { auto bid = b->id(); const auto& succSet = succs(bid); for (auto it1 = succSet.begin(); it1 != succSet.end(); it1++) { auto bid1 = *it1; auto cid1 = block2chain[bid1]; for (auto it2 = it1 + 1; it2 != succSet.end(); it2++) { auto bid2 = *it2; auto cid2 = block2chain[bid2]; if (data(bid1).block->start() == data(bid2).block->start()) { mergeChains(chains[cid1], chains[cid2], block2chain); } } } } // 3. Sort each chain. In general, we want to sort each chain in // decreasing order of profile weights. However, note that this // transformation can turn acyclic graphs into cyclic ones (see // example below). Therefore, if JitLoops are disabled, we // instead sort each chain following the original block order, // which prevents loops from being generated if the region was // originally acyclic. // // Here's an example showing how an acyclic CFG can become cyclic // by chaining its retranslation blocks: // // - Region before chaining retranslation blocks, where B2' and B2" // are retranslations starting at the same SrcKey: // B1 -> B2' // B1 -> B2" // B2' -> B3 // B3 -> B2" // // - Region after sorting the chain as B2" -R-> B2': // B1 -> B2" // B2" -R-> B2' // B2' -> B3 // B3 -> B2" // Note the cycle: B2" -R-> B2' -> B3 -> B2". // auto profData = mcg->tx().profData(); auto weight = [&](RegionDesc::BlockId bid) { return hasTransID(bid) ? profData->absTransCounter(getTransID(bid)) : 0; }; auto sortGeneral = [&](RegionDesc::BlockId bid1, RegionDesc::BlockId bid2) { return weight(bid1) > weight(bid2); }; using SortFun = std::function<bool(RegionDesc::BlockId, RegionDesc::BlockId)>; SortFun sortFunc = sortGeneral; hphp_hash_map<RegionDesc::BlockId, uint32_t> origBlockOrder; if (!RuntimeOption::EvalJitLoops) { for (uint32_t i = 0; i < m_blocks.size(); i++) { origBlockOrder[m_blocks[i]->id()] = i; } auto sortAcyclic = [&](RegionDesc::BlockId bid1, RegionDesc::BlockId bid2) { return origBlockOrder[bid1] < origBlockOrder[bid2]; }; sortFunc = sortAcyclic; } TRACE(1, "chainRetransBlocks: computed chains:\n"); for (auto& c : chains) { std::sort(c.blocks.begin(), c.blocks.end(), sortFunc); if (Trace::moduleEnabled(Trace::region, 1) && c.blocks.size() > 0) { FTRACE(1, " -> {} (w={})", c.blocks[0], weight(c.blocks[0])); for (size_t i = 1; i < c.blocks.size(); i++) { FTRACE(1, ", {} (w={})", c.blocks[i], weight(c.blocks[i])); } FTRACE(1, "\n"); } } // 4. Set the nextRetrans blocks according to the computed chains. for (auto& c : chains) { if (c.blocks.size() == 0) continue; for (size_t i = 0; i < c.blocks.size() - 1; i++) { setNextRetrans(c.blocks[i], c.blocks[i + 1]); } } // 5. For each block with multiple successors in the same chain, // only keep the successor that first appears in the chain. for (auto b : blocks()) { auto& succSet = data(b->id()).succs; for (auto s : succSet) { auto& c = chains[block2chain[s]]; auto selectedSucc = findFirstInSet(c, succSet); for (auto other : c.blocks) { if (other == selectedSucc) continue; succSet.erase(other); } } } // 6. Reorder the blocks in the region in topological order (if // region is acyclic), since the previous steps may break it. sortBlocks(); }
/* * Updates the board with a move. Assumes that the move is legal. */ void Board::doMove(Player p, Move m) { if (m == MOVE_PASS) return; int x = getX(m); int y = getY(m); assert(pieces[index(x, y)] == EMPTY); assert(chainID[index(x, y)] == 0); pieces[index(x, y)] = p; zobristKey ^= zobristTable[zobristIndex(p, x, y)]; Player victim = otherPlayer(p); Stone east = pieces[index(x+1, y)]; Stone west = pieces[index(x-1, y)]; Stone north = pieces[index(x, y+1)]; Stone south = pieces[index(x, y-1)]; int connectionCount = (east == p) + (west == p) + (north == p) + (south == p); // If the stone placed is a new chain if (connectionCount == 0) { // Record which chain this square is a part of chainID[index(x, y)] = nextID; // Add this chain to the list of chains Chain *cargo = new Chain(p, nextID); cargo->add(m); cargo->liberties = 0; if (east == EMPTY) cargo->addLiberty(coordToMove(x+1, y)); if (west == EMPTY) cargo->addLiberty(coordToMove(x-1, y)); if (north == EMPTY) cargo->addLiberty(coordToMove(x, y+1)); if (south == EMPTY) cargo->addLiberty(coordToMove(x, y-1)); chainList.add(cargo); nextID++; } // If the stone placed is added to an existing chain else if (connectionCount == 1) { // Find the ID of the chain we are adding this stone to int thisID; if (east == p) thisID = chainID[index(x+1, y)]; else if (west == p) thisID = chainID[index(x-1, y)]; else if (north == p) thisID = chainID[index(x, y+1)]; else thisID = chainID[index(x, y-1)]; chainID[index(x, y)] = thisID; Chain *node = nullptr; searchChainsByID(node, thisID); node->add(m); // The new stone occupies a previous liberty, but adds on however many // liberties it itself has node->removeLiberty(node->findLiberty(m)); updateLiberty(node, x, y); } // If the stone possibly connects two existing chains else { int eastID = (east == p) * chainID[index(x+1, y)]; int westID = (west == p) * chainID[index(x-1, y)]; int northID = (north == p) * chainID[index(x, y+1)]; int southID = (south == p) * chainID[index(x, y-1)]; Chain *node = nullptr; bool added = false; if (eastID) { chainID[index(x, y)] = eastID; searchChainsByID(node, eastID); node->add(m); node->removeLiberty(node->findLiberty(m)); updateLiberty(node, x, y); added = true; } if (westID) { if (added) { // If two stones from the same chain are adjacent, do nothing // If they are from different chains, we need to combine... if (westID != eastID) mergeChains(node, westID, m); } else { chainID[index(x, y)] = westID; searchChainsByID(node, westID); node->add(m); node->removeLiberty(node->findLiberty(m)); updateLiberty(node, x, y); added = true; } } if (northID) { if (added) { if (northID != eastID && northID != westID) mergeChains(node, northID, m); } else { chainID[index(x, y)] = northID; searchChainsByID(node, northID); node->add(m); node->removeLiberty(node->findLiberty(m)); updateLiberty(node, x, y); added = true; } } if (southID) { if (added) { if (southID != eastID && southID != westID && southID != northID) mergeChains(node, southID, m); } else { chainID[index(x, y)] = southID; searchChainsByID(node, southID); node->add(m); node->removeLiberty(node->findLiberty(m)); updateLiberty(node, x, y); added = true; } } } // Update opponent liberties int eastID = (east == victim) * chainID[index(x+1, y)]; int westID = (west == victim) * chainID[index(x-1, y)]; int northID = (north == victim) * chainID[index(x, y+1)]; int southID = (south == victim) * chainID[index(x, y-1)]; if (eastID) { Chain *node = nullptr; int nodeIndex = searchChainsByID(node, eastID); node->removeLiberty(node->findLiberty(m)); if (node->liberties == 0) captureChain(node, nodeIndex); } if (westID && westID != eastID) { Chain *node = nullptr; int nodeIndex = searchChainsByID(node, westID); node->removeLiberty(node->findLiberty(m)); if (node->liberties == 0) captureChain(node, nodeIndex); } if (northID && northID != eastID && northID != westID) { Chain *node = nullptr; int nodeIndex = searchChainsByID(node, northID); node->removeLiberty(node->findLiberty(m)); if (node->liberties == 0) captureChain(node, nodeIndex); } if (southID && southID != eastID && southID != westID && southID != northID) { Chain *node = nullptr; int nodeIndex = searchChainsByID(node, southID); node->removeLiberty(node->findLiberty(m)); if (node->liberties == 0) captureChain(node, nodeIndex); } // Check for a suicide int selfID = chainID[index(x, y)]; Chain *node = nullptr; int nodeIndex = searchChainsByID(node, selfID); if (node->liberties == 0) captureChain(node, nodeIndex); // A debugging check assert(!checkChains()); // Check if p captured any of the other player's stones with move m /* doCaptures<true>(victim, coordToMove(x+1, y)); doCaptures<true>(victim, coordToMove(x-1, y)); doCaptures<true>(victim, coordToMove(x, y+1)); doCaptures<true>(victim, coordToMove(x, y-1)); // Check if p suicided with move m doCaptures<true>(p, coordToMove(x, y)); */ }