Example #1
0
File: free.c Project: Adpa18/malloc
void    _free(void *ptr)
{
    t_block *block;

    RETURN(!ptr || ptr > (void *)moreSpace(0, true));
    block = GET_BLOCK(ptr);
    RETURN(block->isFree);
    mergeBlocks(&block);
    block->isFree = true;
    if (block == block->parent->lastBlock)
    {
        block->parent->lastBlock = block->prev;
        block->parent->freeSize += B_SIZE(block->size);
        if (!block->parent->next && block->parent->freeSize > PAGE_SIZE)
        {
            if (blocks != block->parent)
            {
                if (block->parent->prev)
                    block->parent->prev->next = block->parent->next;
                brk(block->parent);
            }
        }
    }
    else
    if (block->parent->maxFreeSize < block->size)
        block->parent->maxFreeSize = block->size;
}
    void convertToJump(BasicBlock* block, BasicBlock* targetBlock)
    {
        ASSERT(targetBlock);
        ASSERT(targetBlock->isReachable);
        if (targetBlock->predecessors.size() == 1) {
            m_graph.dethread();
            mergeBlocks(block, targetBlock, noBlocks());
        } else {
            Node* branch = block->terminal();
            ASSERT(branch->op() == Branch || branch->op() == Switch);

            block->replaceTerminal(
                m_graph, SpecNone, Jump, branch->origin, OpInfo(targetBlock));
        }
    }
bool SimplifyControlFlowGraphPass::_mergeBlockIntoPredecessor(ir::IRKernel& k)
{
	bool merged = false;
	
	report(" Merging blocks with predecessors...");
	
	for(ir::ControlFlowGraph::iterator block = k.cfg()->begin();
		block != k.cfg()->end(); )
	{
		if(block == k.cfg()->get_entry_block()) { ++block; continue; }
		if(block == k.cfg()->get_exit_block())  { ++block; continue; }
	
		// Block has a single predecessor
		bool singlePredecessor = block->in_edges.size() == 1;
	
		if(!singlePredecessor) { ++block; continue; }
	
		// Predecessor has single successor
		auto predecessor = block->in_edges.back()->head;
		
		if(predecessor == k.cfg()->get_entry_block()) { ++block; continue; }
		
		bool singleSuccessor = predecessor->out_edges.size() == 1;
		
		if(!singleSuccessor) { ++block; continue; }
		
		report("  " << predecessor->label() << " <- " << block->label());
		
		// Merge the blocks
		mergeBlocks(k, predecessor, block++);
		
		merged = true;
	}
	
	return merged;
}
memPtrSize CHeapManager::mergeIteration(memPtrSize mergeBlock)
{
	if (smallMemSet.size() > 0)
	{
		auto smallLowerBound = smallMemSet.lower_bound(mergeBlock);
		if (smallLowerBound != smallMemSet.end() &&
			*smallLowerBound == mergeBlock)
		{
			smallLowerBound--;
		}

		auto smallUpperBound = smallMemSet.upper_bound(mergeBlock);
		if (smallUpperBound == smallMemSet.end())
		{
			smallUpperBound--;
		}

		if (smallLowerBound != smallMemSet.end() &&
			canMerge(*smallLowerBound, mergeBlock) == LMERGE)
		{
			memPtrSize newBlock = mergeBlocks(*smallLowerBound, mergeBlock);
			smallMemSet.erase(smallLowerBound);
			delFromMemSet(mergeBlock);
			addToMemSet(newBlock);
			return newBlock;
		}

		if (canMerge(mergeBlock, *smallUpperBound) == LMERGE)
		{
			memPtrSize newBlock = mergeBlocks(mergeBlock, *smallUpperBound);
			smallMemSet.erase(smallUpperBound);
			delFromMemSet(mergeBlock);
			addToMemSet(newBlock);
			return newBlock;
		}
	}

	if (mediumMemSet.size() > 0)
	{
		auto mediumLowerBound = mediumMemSet.lower_bound(mergeBlock);
		if (mediumLowerBound != mediumMemSet.end() &&
			*mediumLowerBound == mergeBlock)
		{
			mediumLowerBound--;
		}

		auto mediumUpperBound = mediumMemSet.upper_bound(mergeBlock);
		if (mediumUpperBound == mediumMemSet.end())
		{
			mediumUpperBound--;
		}

		if (mediumLowerBound != mediumMemSet.end() &&
			canMerge(*mediumLowerBound, mergeBlock) == LMERGE)
		{
			memPtrSize newBlock = mergeBlocks(*mediumLowerBound, mergeBlock);
			mediumMemSet.erase(mediumLowerBound);
			delFromMemSet(mergeBlock);
			addToMemSet(newBlock);
			return newBlock;
		}

		if (canMerge(mergeBlock, *mediumUpperBound) == LMERGE)
		{
			memPtrSize newBlock = mergeBlocks(mergeBlock, *mediumUpperBound);
			mediumMemSet.erase(mediumUpperBound);
			delFromMemSet(mergeBlock);
			addToMemSet(newBlock);
			return newBlock;
		}
	}

	if (largeMemSet.size() > 0)
	{
		auto largeLowerBound = largeMemSet.lower_bound(mergeBlock);
		if (largeLowerBound != largeMemSet.end() &&
			*largeLowerBound == mergeBlock)
		{
			largeLowerBound--;
		}

		auto largeUpperBound = largeMemSet.upper_bound(mergeBlock);
		if (largeUpperBound == largeMemSet.end())
		{
			largeUpperBound--;
		}

		if (largeLowerBound != largeMemSet.end() &&
			canMerge(*largeLowerBound, mergeBlock) == LMERGE)
		{
			memPtrSize newBlock = mergeBlocks(*largeLowerBound, mergeBlock);
			largeMemSet.erase(largeLowerBound);
			delFromMemSet(mergeBlock);
			addToMemSet(newBlock);
			return newBlock;
		}

		if (canMerge(mergeBlock, *largeUpperBound) == LMERGE)
		{
			memPtrSize newBlock = mergeBlocks(mergeBlock, *largeUpperBound);
			largeMemSet.erase(largeUpperBound);
			delFromMemSet(mergeBlock);
			addToMemSet(newBlock);
			return newBlock;
		}
	}

	return (std::make_pair((void *)NULL, -1));
}
Example #5
0
bool MergedMiner::mine(std::string address1, std::string wallet1, std::string address2, std::string wallet2, size_t threads) {
  epee::net_utils::http::http_simple_client httpClient1;
  epee::net_utils::http::http_simple_client httpClient2;
  Miner miner;
  BlockTemplate blockTemplate1;
  BlockTemplate blockTemplate2;
  cryptonote::block block1;
  cryptonote::block block2;
  cryptonote::difficulty_type difficulty1;
  cryptonote::difficulty_type difficulty2;
  std::future<bool> request1;
  std::future<bool> request2;
  std::future<bool> mining;
  crypto::hash hash;
  std::chrono::steady_clock::time_point time1;

  uint64_t prefix1;
  cryptonote::account_public_address walletAddress1;
  if (!get_account_address_from_str(prefix1, walletAddress1, wallet1)) {
    std::lock_guard<std::mutex> lock(m_mutex);
    m_messages.push("Failed to parse donor wallet address");
    return false;
  }

  uint64_t prefix2;
  cryptonote::account_public_address walletAddress2;
  if (!address2.empty() && !get_account_address_from_str(prefix2, walletAddress2, wallet2)) {
    std::lock_guard<std::mutex> lock(m_mutex);
    m_messages.push("Failed to parse acceptor wallet address");
    return false;
  }

  m_blockCount = 0;
  for (;;) {
    miner.getHashCount();

    if (m_stopped) {
      if (mining.valid()) {
        miner.stop();
        mining.wait();
      }

      return true;
    }

    request1 = std::async(std::launch::async, getBlockTemplate, &httpClient1, &address1, &wallet1, MERGE_MINING_TAG_RESERVED_SIZE, &blockTemplate1);
    if (!address2.empty()) {
      request2 = std::async(std::launch::async, getBlockTemplate, &httpClient2, &address2, &wallet2, MERGE_MINING_TAG_RESERVED_SIZE, &blockTemplate2);
    }

    if (!request1.get()) {
      std::lock_guard<std::mutex> lock(m_mutex);
      m_messages.push("Failed to get donor block");
      continue;
    }

    if (!address2.empty()) {
      if (!request2.get()) {
        std::lock_guard<std::mutex> lock(m_mutex);
        m_messages.push("Failed to get acceptor block");
        continue;
      }

      if (blockTemplate2.block.major_version != BLOCK_MAJOR_VERSION_2) {
        std::lock_guard<std::mutex> lock(m_mutex);
        m_messages.push("Unsupported block version received from acceptor network, merged mining is not possible");
        if (mining.valid()) {
          miner.stop();
          mining.wait();
        }

        return false;
      }
    }

    if (mining.valid()) {
      miner.stop();
      if (mining.get()) {
        if (cryptonote::check_hash(hash, difficulty1)) {
          if (submitBlock(httpClient1, address1, block1)) {
            std::lock_guard<std::mutex> lock(m_mutex);
            m_messages.push("Submitted donor block");
          } else {
            std::lock_guard<std::mutex> lock(m_mutex);
            m_messages.push("Failed to submit donor block");
          }
        }

        if (!address2.empty() && cryptonote::check_hash(hash, difficulty2)) {
          if (!mergeBlocks(block1, block2)) {
            std::lock_guard<std::mutex> lock(m_mutex);
            m_messages.push("Internal error");
            return false;
          }

          if (submitBlock(httpClient2, address2, block2)) {
            std::lock_guard<std::mutex> lock(m_mutex);
            m_messages.push("Submitted acceptor block");
          } else {
            std::lock_guard<std::mutex> lock(m_mutex);
            m_messages.push("Failed to submit acceptor block");
          }
        }
      }

      std::chrono::steady_clock::time_point time2 = std::chrono::steady_clock::now();
      std::ostringstream stream;
      stream << "Hashrate: " << miner.getHashCount() / std::chrono::duration_cast<std::chrono::duration<double>>(time2 - time1).count();
      std::lock_guard<std::mutex> lock(m_mutex);
      m_messages.push(stream.str());
      time1 = time2;

      miner.start();
    } else {
      time1 = std::chrono::steady_clock::now();
    }

    block1 = blockTemplate1.block;
    difficulty1 = blockTemplate1.difficulty;
    cryptonote::difficulty_type difficulty = difficulty1;
    if (!address2.empty()) {
      block2 = blockTemplate2.block;
      difficulty2 = blockTemplate2.difficulty;
      difficulty = std::min(difficulty, difficulty2);
      if (!fillExtra(block1, block2)) {
        std::lock_guard<std::mutex> lock(m_mutex);
        m_messages.push("Internal error");
        return false;
      }
    }

    mining = std::async(std::launch::async, &Miner::findNonce, &miner, &block1, blockTemplate1.height, difficulty, threads, &hash);
    for (size_t i = 0; i < 50; ++i) {
      if (m_stopped || mining.wait_for(std::chrono::milliseconds(100)) == std::future_status::ready) {
        break;
      }
    }
  }
}
    bool run()
    {
        const bool extremeLogging = false;

        bool outerChanged = false;
        bool innerChanged;
        
        do {
            innerChanged = false;
            for (BlockIndex blockIndex = 0; blockIndex < m_graph.numBlocks(); ++blockIndex) {
                BasicBlock* block = m_graph.block(blockIndex);
                if (!block)
                    continue;
                ASSERT(block->isReachable);
            
                switch (block->last()->op()) {
                case Jump: {
                    // Successor with one predecessor -> merge.
                    if (block->successor(0)->predecessors.size() == 1) {
                        ASSERT(block->successor(0)->predecessors[0] == block);
                        if (extremeLogging)
                            m_graph.dump();
                        m_graph.dethread();
                        mergeBlocks(block, block->successor(0), noBlocks());
                        innerChanged = outerChanged = true;
                        break;
                    }
                
                    // FIXME: Block only has a jump -> remove. This is tricky though because of
                    // liveness. What we really want is to slam in a phantom at the end of the
                    // block, after the terminal. But we can't right now. :-(
                    // Idea: what if I slam the ghosties into my successor? Nope, that's
                    // suboptimal, because if my successor has multiple predecessors then we'll
                    // be keeping alive things on other predecessor edges unnecessarily.
                    // What we really need is the notion of end-of-block ghosties!
                    break;
                }
                
                case Branch: {
                    // Branch on constant -> jettison the not-taken block and merge.
                    if (isKnownDirection(block->cfaBranchDirection)) {
                        bool condition = branchCondition(block->cfaBranchDirection);
                        BasicBlock* targetBlock = block->successorForCondition(condition);
                        BasicBlock* jettisonedBlock = block->successorForCondition(!condition);
                        if (targetBlock->predecessors.size() == 1) {
                            if (extremeLogging)
                                m_graph.dump();
                            m_graph.dethread();
                            mergeBlocks(block, targetBlock, oneBlock(jettisonedBlock));
                        } else {
                            if (extremeLogging)
                                m_graph.dump();
                            m_graph.dethread();
                        
                            ASSERT(block->last()->isTerminal());
                            CodeOrigin boundaryCodeOrigin = block->last()->codeOrigin;
                            block->last()->convertToPhantom();
                            ASSERT(block->last()->refCount() == 1);
                        
                            jettisonBlock(block, jettisonedBlock, boundaryCodeOrigin);
                        
                            block->appendNode(
                                m_graph, SpecNone, Jump, boundaryCodeOrigin,
                                OpInfo(targetBlock));
                        }
                        innerChanged = outerChanged = true;
                        break;
                    }
                    
                    if (block->successor(0) == block->successor(1)) {
                        convertToJump(block, block->successor(0));
                        innerChanged = outerChanged = true;
                        break;
                    }
                    
                    // Branch to same destination -> jump.
                    // FIXME: this will currently not be hit because of the lack of jump-only
                    // block simplification.
                    
                    break;
                }
                    
                case Switch: {
                    SwitchData* data = block->last()->switchData();
                    
                    // Prune out cases that end up jumping to default.
                    for (unsigned i = 0; i < data->cases.size(); ++i) {
                        if (data->cases[i].target == data->fallThrough)
                            data->cases[i--] = data->cases.takeLast();
                    }
                    
                    // If there are no cases other than default then this turns
                    // into a jump.
                    if (data->cases.isEmpty()) {
                        convertToJump(block, data->fallThrough);
                        innerChanged = outerChanged = true;
                        break;
                    }
                    
                    // Switch on constant -> jettison all other targets and merge.
                    if (block->last()->child1()->hasConstant()) {
                        JSValue value = m_graph.valueOfJSConstant(block->last()->child1().node());
                        TriState found = FalseTriState;
                        BasicBlock* targetBlock = 0;
                        for (unsigned i = data->cases.size(); found == FalseTriState && i--;) {
                            found = data->cases[i].value.strictEqual(value);
                            if (found == TrueTriState)
                                targetBlock = data->cases[i].target;
                        }
                        
                        if (found == MixedTriState)
                            break;
                        if (found == FalseTriState)
                            targetBlock = data->fallThrough;
                        ASSERT(targetBlock);
                        
                        Vector<BasicBlock*, 1> jettisonedBlocks;
                        for (unsigned i = block->numSuccessors(); i--;) {
                            BasicBlock* jettisonedBlock = block->successor(i);
                            if (jettisonedBlock != targetBlock)
                                jettisonedBlocks.append(jettisonedBlock);
                        }
                        
                        if (targetBlock->predecessors.size() == 1) {
                            if (extremeLogging)
                                m_graph.dump();
                            m_graph.dethread();
                            
                            mergeBlocks(block, targetBlock, jettisonedBlocks);
                        } else {
                            if (extremeLogging)
                                m_graph.dump();
                            m_graph.dethread();
                            
                            CodeOrigin boundaryCodeOrigin = block->last()->codeOrigin;
                            block->last()->convertToPhantom();
                            for (unsigned i = jettisonedBlocks.size(); i--;)
                                jettisonBlock(block, jettisonedBlocks[i], boundaryCodeOrigin);
                            block->appendNode(
                                m_graph, SpecNone, Jump, boundaryCodeOrigin, OpInfo(targetBlock));
                        }
                        innerChanged = outerChanged = true;
                        break;
                    }
                }
                    
                default:
                    break;
                }
            }
            
            if (innerChanged) {
                // Here's the reason for this pass:
                // Blocks: A, B, C, D, E, F
                // A -> B, C
                // B -> F
                // C -> D, E
                // D -> F
                // E -> F
                //
                // Assume that A's branch is determined to go to B. Then the rest of this phase
                // is smart enough to simplify down to:
                // A -> B
                // B -> F
                // C -> D, E
                // D -> F
                // E -> F
                //
                // We will also merge A and B. But then we don't have any other mechanism to
                // remove D, E as predecessors for F. Worse, the rest of this phase does not
                // know how to fix the Phi functions of F to ensure that they no longer refer
                // to variables in D, E. In general, we need a way to handle Phi simplification
                // upon:
                // 1) Removal of a predecessor due to branch simplification. The branch
                //    simplifier already does that.
                // 2) Invalidation of a predecessor because said predecessor was rendered
                //    unreachable. We do this here.
                //
                // This implies that when a block is unreachable, we must inspect its
                // successors' Phi functions to remove any references from them into the
                // removed block.
                
                m_graph.invalidateCFG();
                m_graph.resetReachability();
                m_graph.killUnreachableBlocks();
            }
            
            if (Options::validateGraphAtEachPhase())
                validate(m_graph);
        } while (innerChanged);
        
        return outerChanged;
    }
    bool run()
    {
        const bool extremeLogging = false;

        bool outerChanged = false;
        bool innerChanged;
        
        do {
            innerChanged = false;
            for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) {
                BasicBlock* block = m_graph.m_blocks[blockIndex].get();
                if (!block)
                    continue;
                ASSERT(block->isReachable);
            
                switch (block->last()->op()) {
                case Jump: {
                    // Successor with one predecessor -> merge.
                    if (m_graph.m_blocks[m_graph.successor(block, 0)]->m_predecessors.size() == 1) {
                        ASSERT(m_graph.m_blocks[m_graph.successor(block, 0)]->m_predecessors[0]
                               == blockIndex);
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
                        dataLogF("CFGSimplify: Jump merge on Block #%u to Block #%u.\n",
                                blockIndex, m_graph.successor(block, 0));
#endif
                        if (extremeLogging)
                            m_graph.dump();
                        m_graph.dethread();
                        mergeBlocks(blockIndex, m_graph.successor(block, 0), NoBlock);
                        innerChanged = outerChanged = true;
                        break;
                    } else {
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
                        dataLogF("Not jump merging on Block #%u to Block #%u because predecessors = ",
                                blockIndex, m_graph.successor(block, 0));
                        for (unsigned i = 0; i < m_graph.m_blocks[m_graph.successor(block, 0)]->m_predecessors.size(); ++i) {
                            if (i)
                                dataLogF(", ");
                            dataLogF("#%u", m_graph.m_blocks[m_graph.successor(block, 0)]->m_predecessors[i]);
                        }
                        dataLogF(".\n");
#endif
                    }
                
                    // FIXME: Block only has a jump -> remove. This is tricky though because of
                    // liveness. What we really want is to slam in a phantom at the end of the
                    // block, after the terminal. But we can't right now. :-(
                    // Idea: what if I slam the ghosties into my successor? Nope, that's
                    // suboptimal, because if my successor has multiple predecessors then we'll
                    // be keeping alive things on other predecessor edges unnecessarily.
                    // What we really need is the notion of end-of-block ghosties!
                    break;
                }
                
                case Branch: {
                    // Branch on constant -> jettison the not-taken block and merge.
                    if (isKnownDirection(block->cfaBranchDirection)) {
                        bool condition = branchCondition(block->cfaBranchDirection);
                        BasicBlock* targetBlock = m_graph.m_blocks[
                            m_graph.successorForCondition(block, condition)].get();
                        if (targetBlock->m_predecessors.size() == 1) {
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
                            dataLogF("CFGSimplify: Known condition (%s) branch merge on Block #%u to Block #%u, jettisoning Block #%u.\n",
                                    condition ? "true" : "false",
                                    blockIndex, m_graph.successorForCondition(block, condition),
                                    m_graph.successorForCondition(block, !condition));
#endif
                            if (extremeLogging)
                                m_graph.dump();
                            m_graph.dethread();
                            mergeBlocks(
                                blockIndex,
                                m_graph.successorForCondition(block, condition),
                                m_graph.successorForCondition(block, !condition));
                        } else {
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
                            dataLogF("CFGSimplify: Known condition (%s) branch->jump conversion on Block #%u to Block #%u, jettisoning Block #%u.\n",
                                    condition ? "true" : "false",
                                    blockIndex, m_graph.successorForCondition(block, condition),
                                    m_graph.successorForCondition(block, !condition));
#endif
                            if (extremeLogging)
                                m_graph.dump();
                            m_graph.dethread();
                            BlockIndex takenBlockIndex = m_graph.successorForCondition(block, condition);
                            BlockIndex notTakenBlockIndex = m_graph.successorForCondition(block, !condition);
                        
                            ASSERT(block->last()->isTerminal());
                            CodeOrigin boundaryCodeOrigin = block->last()->codeOrigin;
                            block->last()->convertToPhantom();
                            ASSERT(block->last()->refCount() == 1);
                        
                            jettisonBlock(blockIndex, notTakenBlockIndex, boundaryCodeOrigin);
                        
                            block->appendNode(
                                m_graph, SpecNone, Jump, boundaryCodeOrigin,
                                OpInfo(takenBlockIndex));
                        }
                        innerChanged = outerChanged = true;
                        break;
                    }
                    
                    if (m_graph.successor(block, 0) == m_graph.successor(block, 1)) {
                        BlockIndex targetBlockIndex = m_graph.successor(block, 0);
                        BasicBlock* targetBlock = m_graph.m_blocks[targetBlockIndex].get();
                        ASSERT(targetBlock);
                        ASSERT(targetBlock->isReachable);
                        if (targetBlock->m_predecessors.size() == 1) {
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
                            dataLogF("CFGSimplify: Branch to same successor merge on Block #%u to Block #%u.\n",
                                    blockIndex, targetBlockIndex);
#endif
                            m_graph.dethread();
                            mergeBlocks(blockIndex, targetBlockIndex, NoBlock);
                        } else {
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
                            dataLogF("CFGSimplify: Branch->jump conversion to same successor on Block #%u to Block #%u.\n",
                                    blockIndex, targetBlockIndex);
#endif
                            Node* branch = block->last();
                            ASSERT(branch->isTerminal());
                            ASSERT(branch->op() == Branch);
                            branch->convertToPhantom();
                            ASSERT(branch->refCount() == 1);
                            
                            block->appendNode(
                                m_graph, SpecNone, Jump, branch->codeOrigin,
                                OpInfo(targetBlockIndex));
                        }
                        innerChanged = outerChanged = true;
                        break;
                    }
                    
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
                    dataLogF("Not branch simplifying on Block #%u because the successors differ and the condition is not known.\n",
                            blockIndex);
#endif
                
                    // Branch to same destination -> jump.
                    // FIXME: this will currently not be hit because of the lack of jump-only
                    // block simplification.
                    
                    break;
                }
                
                default:
                    break;
                }
            }
            
            if (innerChanged) {
                // Here's the reason for this pass:
                // Blocks: A, B, C, D, E, F
                // A -> B, C
                // B -> F
                // C -> D, E
                // D -> F
                // E -> F
                //
                // Assume that A's branch is determined to go to B. Then the rest of this phase
                // is smart enough to simplify down to:
                // A -> B
                // B -> F
                // C -> D, E
                // D -> F
                // E -> F
                //
                // We will also merge A and B. But then we don't have any other mechanism to
                // remove D, E as predecessors for F. Worse, the rest of this phase does not
                // know how to fix the Phi functions of F to ensure that they no longer refer
                // to variables in D, E. In general, we need a way to handle Phi simplification
                // upon:
                // 1) Removal of a predecessor due to branch simplification. The branch
                //    simplifier already does that.
                // 2) Invalidation of a predecessor because said predecessor was rendered
                //    unreachable. We do this here.
                //
                // This implies that when a block is unreachable, we must inspect its
                // successors' Phi functions to remove any references from them into the
                // removed block.
                
                m_graph.resetReachability();

                for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) {
                    BasicBlock* block = m_graph.m_blocks[blockIndex].get();
                    if (!block)
                        continue;
                    if (block->isReachable)
                        continue;
                    
                    killUnreachable(blockIndex);
                }
            }
            
            if (Options::validateGraphAtEachPhase())
                validate(m_graph);
        } while (innerChanged);
        
        return outerChanged;
    }