From 376a71785da94f0d64cc5729f180c0e84ff2eb28 Mon Sep 17 00:00:00 2001
From: Clark Coleman <clc@zephyr-software.com>
Date: Wed, 25 Nov 2020 10:11:48 -0500
Subject: [PATCH] SPARK: Continue improvements to new
 FindConditionalFollowNode() algorithm.

---
 include/base/SMPBasicBlock.h |   5 +-
 include/base/SMPFunction.h   |   6 +-
 src/base/SMPBasicBlock.cpp   |  54 +++---
 src/base/SMPFunction.cpp     | 353 +++++++++++++++++++++++++++--------
 src/base/SMPProgram.cpp      |   2 +-
 5 files changed, 313 insertions(+), 107 deletions(-)

diff --git a/include/base/SMPBasicBlock.h b/include/base/SMPBasicBlock.h
index 458d4e07..bba1ebec 100644
--- a/include/base/SMPBasicBlock.h
+++ b/include/base/SMPBasicBlock.h
@@ -394,7 +394,10 @@ public:
 	bool AllPredecessorsProcessed(void);
 	void DepthFirstMark(bool MarkIBTBlocks); // Depth-first traversal, mark as processed.
 	void DepthFirstMarkNonBackEdgeSuccessors(void); // Depth-first, but do not follow back edges.
-	bool IsBlockReachableWithoutBackEdges(const int DestBlockNum) const; // path without back edges?
+
+	// NOTE: Call SMPFunction::ResetSCCPVisitedBlocks() before calling SMPBasicBlock::isBlockReachableWithoutBackEdges().
+	bool IsBlockReachableWithoutBackEdges(const int DestBlockNum, bool &HitDeadEnd) const; // path without back edges?
+
 	std::size_t GetNumPredsMinusBackEdges(void) const; // Compute predecessor count, excluding loop-back edges. Loop ID must occur first.
 	STARS_ea_t GetUltimateOperandSource(STARS_ea_t UseAddr, const STARSOpndTypePtr &UseOp, int UseSSANum); // trace through move and Phi defs to some defining operation.
 	bool GetUltimateInitValue(const STARSOpndTypePtr &UseOp, STARS_ea_t InstAddr, int SSANum, bool LocalName, STARSOpndTypePtr &DefMoveOp, STARS_uval_t &InitValue);
diff --git a/include/base/SMPFunction.h b/include/base/SMPFunction.h
index 9c79a723..3b9224d3 100644
--- a/include/base/SMPFunction.h
+++ b/include/base/SMPFunction.h
@@ -609,6 +609,7 @@ public:
 	bool DoesEdgeExitLoop(const int BlockNum1, const int BlockNum2) const; // Does going from BlockNum1 to BlockNum2 exit a loop?
 	void BuildLoopList(int BlockNum, std::list<std::size_t> &LoopList) const; // build list of loop numbers that BlockNum is part of.
 	void BuildLoopBlockList(const size_t LoopNum, std::list<std::size_t> &BlockList); // build list of Block numbers contained in LoopNum.
+	void BuildBlockListFromBitset(const STARSBitSet &CurrBitSet, std::list<std::size_t> &BlockList) const; // each bit set in CurrBitSet is a block # for BlockList
 	void AnalyzeLoopIterations(void); // analyze how many times each loop iterates
 	STARSExpression *CreateMemoryAddressExpr(const STARSOpndTypePtr &MemDefOp, SMPInstr *WriteInst); // create expression for the memory address computation in MemDefOp
 	void AliasAnalysis(void); // Find memory writes with possible aliases
@@ -1035,10 +1036,13 @@ private:
 	int FindConditionalFollowNode(int HeadBlockNum); // Find candidate block # for if-else follow node for HeadBlockNum; return -1 otherwise
 	int FindConditionalFollowNode2(int HeadBlockNum); // Find candidate block # for if-else follow node for HeadBlockNum; return -1 otherwise
 	int FindConditionalFollowNode3(int HeadBlockNum); // Find candidate block # for if-else follow node for HeadBlockNum; return -1 otherwise
+	bool FindNextBlockAfterReachableBlocks(const int ContainingLoopNum, const STARSBitSet &BlocksSeen, int &NextBlockNum); // Get unique successor to BlocksSeen; return false if unable to do so.
 	int TrackConditionalBranchTerminus(int BranchHeadBlockNum, int CurrBlockNum, STARSBitSet &BlocksSeen, int &BlockAlreadySeenCounter); // Track CurrBlockNum until we reach block that BranchHeadBlockNum doesn't dominate, or dead end. Return block num, possibly SMP_BLOCKNUM_COMMON_RETURN.
-	bool FindUnstructuredIfThenElse(const int HeadBlockNum, const int ThenBlockNum, const int ElseBlockNum, const int FollowBlockNum) const; // Do IF and ELSE branches merge before FollowBlockNum?
+	bool FindUnstructuredIfThenElse(const int HeadBlockNum, const int ThenBlockNum, const int ElseBlockNum, const int FollowBlockNum); // Do IF and ELSE branches merge before FollowBlockNum?
+	bool FindUnstructuredBlockList(const std::list<std::size_t> &BlockList, const int CondHeadBlockNum, const int FollowBlockNum); // Do any blocks fail to reach their FollowBlockNum?
 	bool FindMultiLoopContinue(const int BranchBlockNum); // Does COND_BRANCH behave as an unstructured multi-level loop continue statement?
 	bool DetectConditionalReachabilityErrors(const int HeadBlockNum, const int FTBlockNum, const int NFTBlockNum, const STARSBitSet &FTBlocksSeen, const STARSBitSet &NFTBlocksSeen, const STARSBitSet &CommonBlocksSeen);
+	bool ConditionalReachabilityLoopErrorHelper(const int CondHeadBlockNum, const int BlockNum) const; // Back door edge into loop exit targets (for multi-exit loops)?
 	void FindGuardedLoops(void); // Find guarded loops and fill the GuardToLoopMap and LoopToGuardMap
 
 	bool IsSPARKLoopInTranslationStack(void) const; // Are we already translating a SPARK_LOOP when we encounter another loop?
diff --git a/src/base/SMPBasicBlock.cpp b/src/base/SMPBasicBlock.cpp
index 2912fe70..52079071 100644
--- a/src/base/SMPBasicBlock.cpp
+++ b/src/base/SMPBasicBlock.cpp
@@ -384,11 +384,16 @@ void SMPBasicBlock::DepthFirstMarkNonBackEdgeSuccessors(void) {
 	return;
 } // end of SMPBasicBlock::DepthFirstMark()
 
+// NOTE: Call SMPFunction::ResetSCCPVisitedBlocks() before calling SMPBasicBlock::isBlockReachableWithoutBackEdges().
 // Mark as processed, recurse into successors depth-first, do not follow back edges.
-bool SMPBasicBlock::IsBlockReachableWithoutBackEdges(const int DestBlockNum) const {
+bool SMPBasicBlock::IsBlockReachableWithoutBackEdges(const int DestBlockNum, bool &HitDeadEnd) const {
 	bool Found = false;
 	list<SMPBasicBlock *>::const_iterator CurrSucc;
 	int CurrBlockNum = this->GetNumber();
+	if (CurrBlockNum == DestBlockNum) {
+		Found = true;
+		return Found;
+	}
 
 	// See if we can get lucky by finding the block in the DomFrontier.
 	//  This search is more breadth-first; combining breadth-first checks
@@ -400,28 +405,33 @@ bool SMPBasicBlock::IsBlockReachableWithoutBackEdges(const int DestBlockNum) con
 		}
 	} // end for all DomFrontier blocks
 
-	for (CurrSucc = this->GetFirstConstSucc(); !Found && (CurrSucc != this->GetLastConstSucc()); ++CurrSucc) {
-		SMPBasicBlock *SuccBlock = (*CurrSucc);
-		int SuccBlockNum = SuccBlock->GetNumber();
-		if (SuccBlockNum > CurrBlockNum) { // not back edge
-			if (SuccBlockNum == DestBlockNum) {
-				Found = true;
-				SuccBlock->SetSCCPVisited(true);
-			}
-			else if (SuccBlock->IsSCCPVisited()) { // already hit this block on our search
-				break; // return false
-			}
-			else if (SuccBlockNum > DestBlockNum) {
-				// RPO ordering means we can no longer reach DestBlockNum without a back edge.
-				SuccBlock->SetSCCPVisited(true);
-				break; // return false
-			}
-			else {
-				SuccBlock->SetSCCPVisited(true);
-				Found = SuccBlock->IsBlockReachableWithoutBackEdges(DestBlockNum); // recurse, depth-first
+	if (0 == this->GetNumSuccessors()) {
+		HitDeadEnd = true;
+	}
+	else {
+		for (CurrSucc = this->GetFirstConstSucc(); !Found && (CurrSucc != this->GetLastConstSucc()); ++CurrSucc) {
+			SMPBasicBlock *SuccBlock = (*CurrSucc);
+			int SuccBlockNum = SuccBlock->GetNumber();
+			if (SuccBlockNum > CurrBlockNum) { // not back edge
+				if (SuccBlockNum == DestBlockNum) {
+					Found = true;
+					SuccBlock->SetSCCPVisited(true);
+				}
+				else if (SuccBlock->IsSCCPVisited()) { // already hit this block on our search
+					break; // return false
+				}
+				else if (SuccBlockNum > DestBlockNum) {
+					// RPO ordering means we can no longer reach DestBlockNum without a back edge.
+					SuccBlock->SetSCCPVisited(true);
+					break; // return false
+				}
+				else {
+					SuccBlock->SetSCCPVisited(true);
+					Found = SuccBlock->IsBlockReachableWithoutBackEdges(DestBlockNum, HitDeadEnd); // recurse, depth-first
+				}
 			}
-		}
-	} // end for all successor blocks
+		} // end for all successor blocks
+	}
 
 	return Found;
 } // end of SMPBasicBlock::IsBlockReachableWithoutBackEdges()
diff --git a/src/base/SMPFunction.cpp b/src/base/SMPFunction.cpp
index 98841957..2ed179d5 100644
--- a/src/base/SMPFunction.cpp
+++ b/src/base/SMPFunction.cpp
@@ -7394,29 +7394,44 @@ void SMPFunction::ComputeLoopFollowNodesReachability(const std::size_t LoopNum)
 	assert(LoopNum < this->GetNumLoops());
 	assert(LoopNum < this->LoopExitTargets.size());
 	int LoopHeadBlockNum = this->LoopHeadBlockNumbers[LoopNum];
+	bool Unstructured = false;
 	for (int FollowNodeNum : this->LoopExitTargets[LoopNum]) {
 		STARSBitSet Reachable;
 		Reachable.AllocateBits(this->GetNumBlocks());
 		this->MarkReachableBlocks(FollowNodeNum, (int)LoopNum, LoopHeadBlockNum, Reachable);
-		pair<int, STARSBitSet> MapItem(FollowNodeNum, Reachable);
-		this->LoopExitTargetsReachabilitySets[LoopNum].insert(MapItem);
+		if (Reachable.IsAnyBitSet()) {
+			pair<int, STARSBitSet> MapItem(FollowNodeNum, Reachable);
+			this->LoopExitTargetsReachabilitySets[LoopNum].insert(MapItem);
+		}
+		else {
+			Unstructured = true;
+			if (!this->PrintedSPARKUnstructuredMsg) {
+				SMP_msg("ERROR: SPARK: Unstructured due to no loop head dominance of follow node %d from loop %zu ", FollowNodeNum, LoopNum);
+				this->DumpFuncNameAndAddr();
+				this->PrintedSPARKUnstructuredMsg = true;
+			}
+			this->HasStructuredCFG = false;
+			break;
+		}
 	}
 
-	// Find out where the multiple follow nodes converge, if they do. If they all hit separate return blocks,
-	//  we use the code of -2 to signify it.
-	if (this->MultiLoopExitTargetsConvergenceMap[LoopNum].empty()) {
-		// Must have hit return blocks.
-		this->MultiLoopExitTargetsConvergenceMap[LoopNum].insert(SMP_BLOCKNUM_COMMON_RETURN);
-	}
-	else if (1 != this->MultiLoopExitTargetsConvergenceMap[LoopNum].size()) {
-		if (!this->PrintedSPARKUnstructuredMsg) {
-			SMP_msg("ERROR: SPARK: Unstructured due to no convergence of multiple follow nodes from loop %zu ", LoopNum);
-			this->DumpFuncNameAndAddr();
-			this->PrintedSPARKUnstructuredMsg = true;
+	if (!Unstructured) {
+		// Find out where the multiple follow nodes converge, if they do. If they all hit separate return blocks,
+		//  we use the code of -2 to signify it.
+		if (this->MultiLoopExitTargetsConvergenceMap[LoopNum].empty()) {
+			// Must have hit return blocks.
+			this->MultiLoopExitTargetsConvergenceMap[LoopNum].insert(SMP_BLOCKNUM_COMMON_RETURN);
+		}
+		else if (1 != this->MultiLoopExitTargetsConvergenceMap[LoopNum].size()) {
+			if (!this->PrintedSPARKUnstructuredMsg) {
+				SMP_msg("ERROR: SPARK: Unstructured due to no convergence of multiple follow nodes from loop %zu ", LoopNum);
+				this->DumpFuncNameAndAddr();
+				this->PrintedSPARKUnstructuredMsg = true;
+			}
+			this->HasStructuredCFG = false; // multi-exit loop has no single convergence point of follow nodes.
+			// TODO: If the multiple termination blocks all loop back to the same outer loop head block, this
+			//  is structured code and can be translated. !!!!****!!!!
 		}
-		this->HasStructuredCFG = false; // multi-exit loop has no single convergence point of follow nodes.
-		// TODO: If the multiple termination blocks all loop back to the same outer loop head block, this
-		//  is structured code and can be translated. !!!!****!!!!
 	}
 
 	return;
@@ -12130,7 +12145,7 @@ int SMPFunction::FindConditionalFollowNode(int HeadBlockNum) {
 	else {
 		if (BadNew) {
 			SMP_msg("INFO: SPARK: Success on old version, failure on new version of FindConditionalFollowNode at block %d Follow: %d ", HeadBlockNum, OldAlgorithmFollowNode);
-			ReturnFollowBlockNum = OldAlgorithmFollowNode;
+			// ReturnFollowBlockNum = OldAlgorithmFollowNode;
 			this->DumpDotCFG();
 		}
 		else {
@@ -12440,15 +12455,16 @@ int SMPFunction::FindConditionalFollowNode2(int HeadBlockNum) {
 				// Screen out remaining if-then cases by ensuring that neither branch
 				//  can reach the other without following a back edge. It is sufficient
 				//  to check whether the lower RPO-numbered block can reach the other.
+				bool HitDeadEnd = false;
 				if (ThenBlockNum < ElseBlockNum) {
 					SMPBasicBlock *ThenBlock = this->GetBlockByNum(ThenBlockNum);
 					this->ResetSCCPVisitedBlocks(); // SCCP is long since done; reuse its visited flag in each block
-					IfThenElseCase = (!ThenBlock->IsBlockReachableWithoutBackEdges(ElseBlockNum));
+					IfThenElseCase = (!ThenBlock->IsBlockReachableWithoutBackEdges(ElseBlockNum, HitDeadEnd));
 				}
 				else {
 					SMPBasicBlock *ElseBlock = this->GetBlockByNum(ElseBlockNum);
 					this->ResetSCCPVisitedBlocks(); // SCCP is long since done; reuse its visited flag in each block
-					IfThenElseCase = (!ElseBlock->IsBlockReachableWithoutBackEdges(ThenBlockNum));
+					IfThenElseCase = (!ElseBlock->IsBlockReachableWithoutBackEdges(ThenBlockNum, HitDeadEnd));
 				}
 			}
 			if (!IfThenElseCase) {
@@ -12614,6 +12630,11 @@ int SMPFunction::FindConditionalFollowNode3(int HeadBlockNum) {
 	//  1. No intersection of the two bitsets. FollowBlockNum is -2 (SMP_BLOCKNUM_COMMON_RETURN)
 	//     indicating the lack of intersection, with each trace ending in a block with no successors
 	//     (e.g. return instructions or calls to non-returning functions or halt instructions or LOOP_BACKs).
+	//
+	//     If NFTBlockNum is not dominated by HeadBlockNum, but FTBlockNum is dominated by HeadBlockNum,
+	//     then the FTBlockNum branch is part of an IF-THEN. If vice versa, the NFTBlockNum is part of an OddIfThenCase.
+	//     If neither FTBlockNum nor NFTBlockNum is dominated by HeadBlockNum, the code is unstructured.
+	//
 	//     EXCEPTION: Intersection point was about to happen, but it is just out of the region dominated
 	//     by HeadBlockNum, e.g. conditional join point is also a loop follow block for an enclosing loop or
 	//     is a join point for an enclosing conditional. This can be detected by seeing if the end of each
@@ -12658,12 +12679,16 @@ int SMPFunction::FindConditionalFollowNode3(int HeadBlockNum) {
 	CommonBlocksSeen = STARSBitSetIntersection(FTBlocksSeen, NFTBlocksSeen);
 	size_t CommonBlocksCount = CommonBlocksSeen.CountSetBits();
 	int FirstCommonBlockNum = CommonBlocksSeen.FindLowestBitSet();
-	int LastFTReachableBlockNum = FTBlocksSeen.FindHighestBitSet();
-	int LastNFTReachableBlockNum = NFTBlocksSeen.FindHighestBitSet();
-	bool IfThenElseCase = true;
+	bool IntersectionSeen = (0 < CommonBlocksCount);
+	
 	bool FTNotDominated = (!this->DoesBlockDominateBlock(HeadBlockNum, FTBlockNum));
 	bool NFTNotDominated = (!this->DoesBlockDominateBlock(HeadBlockNum, NFTBlockNum));
 	bool DominanceProblem = (FTNotDominated || NFTNotDominated);
+
+	int LastFTReachableBlockNum = FTBlocksSeen.FindHighestBitSet();
+	int LastNFTReachableBlockNum = NFTBlocksSeen.FindHighestBitSet();
+	int LastBlockNumReached = (LastFTReachableBlockNum >= LastNFTReachableBlockNum) ? LastFTReachableBlockNum : LastNFTReachableBlockNum;
+	bool IfThenElseCase = true;
 	bool Case0 = false;
 
 	// Find CFG problems that are obvious just from the reachability bitsets.
@@ -12673,29 +12698,29 @@ int SMPFunction::FindConditionalFollowNode3(int HeadBlockNum) {
 
 	// Based on CommonBlocksCount, FirstCommonBlockNum, FTBlockNum, and NFTBlockNum, we
 	//  can find which case in the comment above applies.
-	if (0 == CommonBlocksCount) { // Case 1 or Case 0.
+	if (!IntersectionSeen) { // Case 1 or Case 0.
 		FollowBlockNum = SMP_BLOCKNUM_COMMON_RETURN; // Case 1
 
 		// Detect the Case 1 exception subcase.
 		int NextFTBlockNum = SMP_BLOCKNUM_UNINIT;
 		int NextNFTBlockNum = SMP_BLOCKNUM_UNINIT;
 		if (0 <= LastFTReachableBlockNum) { // FTBlock was dominated by HeadBlock
-			SMPBasicBlock *LastFTBlockReached = this->GetBlockByNum((size_t)LastFTReachableBlockNum);
-			if (1 == LastFTBlockReached->GetNumSuccessors()) {
-				NextFTBlockNum = (*(LastFTBlockReached->GetFirstConstSucc()))->GetNumber();
+			bool ErrorSeen = this->FindNextBlockAfterReachableBlocks(InnerMostLoopNum, FTBlocksSeen, NextFTBlockNum);
+			if (ErrorSeen) {
+				FollowBlockNum = SMP_BLOCKNUM_UNINIT;
 			}
 		}
 		else { // FTBlock was outside region dominated by HeadBlock
 			NextFTBlockNum = FTBlockNum; // First block outside dominated region
 			IfThenElseCase = false;
 		}
-		if (0 <= LastNFTReachableBlockNum) { // FTBlock was dominated by HeadBlock
-			SMPBasicBlock *LastNFTBlockReached = this->GetBlockByNum((size_t)LastNFTReachableBlockNum);
-			if (1 == LastNFTBlockReached->GetNumSuccessors()) {
-				NextNFTBlockNum = (*(LastNFTBlockReached->GetFirstConstSucc()))->GetNumber();
+		if (0 <= LastNFTReachableBlockNum) { // NFTBlock was dominated by HeadBlock
+			bool ErrorSeen = this->FindNextBlockAfterReachableBlocks(InnerMostLoopNum, NFTBlocksSeen, NextNFTBlockNum);
+			if (ErrorSeen) {
+				FollowBlockNum = SMP_BLOCKNUM_UNINIT;
 			}
 		}
-		else { // FTBlock was outside region dominated by HeadBlock
+		else { // NFTBlock was outside region dominated by HeadBlock
 			NextNFTBlockNum = NFTBlockNum; // First block outside dominated region
 			IfThenElseCase = false;
 		}
@@ -12790,10 +12815,95 @@ int SMPFunction::FindConditionalFollowNode3(int HeadBlockNum) {
 			}
 		}
 	}
+	// All THEN and ELSE blocks should be able to reach the FollowBlockNum.
+	if (0 <= FollowBlockNum) {
+		// Eliminate blocks after the FollowBlockNum from the bitsets.
+		for (int BlockIndex = FollowBlockNum; BlockIndex <= LastBlockNumReached; ++BlockIndex) {
+			FTBlocksSeen.ResetBit((size_t)BlockIndex);
+			NFTBlocksSeen.ResetBit((size_t)BlockIndex);
+		}
+		list<size_t> ReachedBlocksList;
+		this->BuildBlockListFromBitset(FTBlocksSeen, ReachedBlocksList);
+		this->BuildBlockListFromBitset(NFTBlocksSeen, ReachedBlocksList);
+		this->ResetSCCPVisitedBlocks();
+		if (this->FindUnstructuredBlockList(ReachedBlocksList, HeadBlockNum, FollowBlockNum)) {
+			FollowBlockNum = SMP_BLOCKNUM_UNINIT; // error
+		}
+	}
 
 	return FollowBlockNum;
 } // end of SMPFunction::FindConditionalFollowNode3()
 
+// Get unique successor to BlocksSeen; return false if unable to do so.
+bool SMPFunction::FindNextBlockAfterReachableBlocks(const int ContainingLoopNum, const STARSBitSet &BlocksSeen, int &NextBlockNum) {
+	// If we don't encounter any loops, we just go to the end of the BlocksSeen trace and
+	//  get the unique successor to the last block. If we see a loop along the way, we 
+	//  look at the exit targets. All exit targets must be in the BlocksSeen trace. If
+	//  the LoopFollowNode is also in the trace, continue with it and skip over the blocks
+	//  within the loop. If not, then the LoopFollowNode is the NextBlockNum we are searching for.
+	int LowestBlockNum = BlocksSeen.FindLowestBitSet();
+	int HighestBlockNum = BlocksSeen.FindHighestBitSet();
+	int HighestNonLoopBlockNum = 0;
+	bool ErrorSeen = (0 > LowestBlockNum);
+	int BlockNum = LowestBlockNum;
+	bool UsedLoopFollowBlock = false;
+	while (BlockNum <= HighestBlockNum) {
+		size_t BlockIndex = (size_t)BlockNum;
+		int InnermostLoopNum = this->GetInnermostLoopNum(BlockNum);
+		if (BlocksSeen.GetBit(BlockIndex)) {
+			SMPBasicBlock *CurrBlock = this->GetBlockByNum(BlockIndex);
+			if (InnermostLoopNum != ContainingLoopNum) { // Entered into loop within conditional statement
+				if (CurrBlock->IsLoopHeaderBlock()) { // jump to follow block
+					int LoopNum = this->GetLoopNumFromHeaderBlockNum(BlockNum);
+					int LoopFollowBlockNum;
+					bool MultiExitTargetLoop = this->GetLoopFollowBlockNum((size_t)LoopNum, LoopFollowBlockNum);
+					if (0 > LoopFollowBlockNum) {
+						ErrorSeen = true;
+						break;
+					}
+					if (BlocksSeen.GetBit((size_t)LoopFollowBlockNum)) {
+						// Loop and its follow node are contained inside the current conditional branch. Continue
+						//  with the follow node and beyond.
+						BlockNum = LoopFollowBlockNum;
+					}
+					else {
+						// Loop is in current conditional branch, but its follow block is not. If the
+						//  code is structured, it will be the case that the loop follow block is also
+						//  the follow block for the containing conditional statement, and the SPARK Ada
+						//  decompilation will have "end loop;" falling directly into "end if;".
+						NextBlockNum = LoopFollowBlockNum;
+						UsedLoopFollowBlock = true;
+						break;
+					}
+				}
+				else {
+					++BlockNum; // Don't bother with loop blocks other than the header block
+				}
+			}
+			else {
+				HighestNonLoopBlockNum = BlockNum;
+				++BlockNum;
+			}
+		}
+		else {
+			++BlockNum;
+		}
+	} // end while (BlockNum <= HighestBlockNum)
+
+	if (!ErrorSeen) {
+		if (!UsedLoopFollowBlock) {
+			// Still need convergence point.
+			SMPBasicBlock *LastBlock = this->GetBlockByNum((size_t)HighestNonLoopBlockNum);
+			assert(nullptr != LastBlock);
+			if (1 == LastBlock->GetNumSuccessors()) {
+				NextBlockNum = (*(LastBlock->GetFirstConstSucc()))->GetNumber();
+				ErrorSeen = (0 > NextBlockNum);
+			}
+		}
+	}
+
+	return ErrorSeen;
+} // end of SMPFunction::FindNextBlockAfterReachableBlocks()
 
 // Track CurrBlockNum until we reach block that BranchHeadBlockNum doesn't dominate, or dead end. Return block num, possibly SMP_BLOCKNUM_COMMON_RETURN.
 //  A dead end is a block that returns or that calls a non-returning function.
@@ -12947,8 +13057,8 @@ int SMPFunction::TrackConditionalBranchTerminus(int BranchHeadBlockNum, int Curr
 	return TerminusBlockNum;
 } // end of SMPFunction::TrackConditionalBranchTerminus()
 
-// Do IF and ELSE branches merge before FollowBlockNum?
-bool SMPFunction::FindUnstructuredIfThenElse(const int HeadBlockNum, const int ThenBlockNum, const int ElseBlockNum, const int FollowBlockNum) const {
+// Do IF and ELSE branches merge before FollowBlockNum? Do all IF and ELSE blocks reach FollowBlockNum?
+bool SMPFunction::FindUnstructuredIfThenElse(const int HeadBlockNum, const int ThenBlockNum, const int ElseBlockNum, const int FollowBlockNum) {
 	bool UnstructuredClauses = false;
 
 	if ((SMP_BLOCKNUM_UNINIT != ElseBlockNum) && (SMP_BLOCKNUM_UNINIT != ThenBlockNum)) {
@@ -13009,11 +13119,62 @@ bool SMPFunction::FindUnstructuredIfThenElse(const int HeadBlockNum, const int T
 				break;
 			}
 		}
+		if (!UnstructuredClauses && (0 < FollowBlockNum)) {
+			// Every block in the THEN clause should reach the follow block. Ditto for every
+			//  block in the ELSE clause.
+			list<size_t> BlockNumList;
+			this->BuildBlockListFromBitset(ThenBlocksSeen, BlockNumList);
+			this->BuildBlockListFromBitset(ElseBlocksSeen, BlockNumList);
+			UnstructuredClauses = this->FindUnstructuredBlockList(BlockNumList, HeadBlockNum, FollowBlockNum);
+		}
 	}
 
 	return UnstructuredClauses;
 } // end of SMPFunction::FindUnstructuredIfThenElse()
 
+// Do any blocks fail to reach their FollowBlockNum?
+bool SMPFunction::FindUnstructuredBlockList(const list<size_t> &BlockList, const int CondHeadBlockNum, const int FollowBlockNum) {
+	assert(0 < FollowBlockNum);
+	bool Unstructured = false;
+	int InnermostContainingLoopNum = this->GetInnermostLoopNum(CondHeadBlockNum);
+
+	for (size_t BlockNum : BlockList) {
+		SMPBasicBlock *CurrBlock = this->GetBlockByNum(BlockNum);
+		bool HitDeadEnd = false;
+
+		// Every block in a loop can make it back to the header block. If the header
+		//  block can reach FollowBlockNum, then all blocks in the loop can also reach
+		//  FollowBlockNum, but some might have to follow a back edge to the loop header
+		//  block in order to do so. We can save time and avoid special-case following of
+		//  back edges by only testing the header blocks.
+		// NOTE: If the header block is not inside the conditional statement, meaning that the
+		//  conditional statement is contained in the block, then we would ignore all blocks in
+		//  the conditional statement and analyze nothing. We need to separate internal loops from
+		//  containing loops. For internal loops, the header block should reach the conditional
+		//  follow node and other loop blocks can be ignored. For containing loops, we do not ignore
+		//  any blocks; each must reach the conditional follow node.
+		bool LoopDontCareBlock = (this->IsBlockInAnyLoop((int)BlockNum) && (!CurrBlock->IsLoopHeaderBlock()));
+		if (LoopDontCareBlock) {
+			// Don't ignore blocks from the containing loop.
+			int InnermostLoopNum = this->GetInnermostLoopNum((int)BlockNum);
+			LoopDontCareBlock = (InnermostContainingLoopNum != InnermostLoopNum);
+		}
+		if (!LoopDontCareBlock) {
+			this->ResetSCCPVisitedBlocks();
+			if (!CurrBlock->IsBlockReachableWithoutBackEdges(FollowBlockNum, HitDeadEnd)) {
+				if (!HitDeadEnd) {
+					Unstructured = true;
+					break;
+				}
+			}
+		}
+	} // end for all blocks in BlockList
+
+	return Unstructured;
+} // end of SMPFunction::FindUnstructuredBlockList()
+
+
+
 // Does COND_BRANCH behave as an unstructured multi-level loop continue statement?
 bool SMPFunction::FindMultiLoopContinue(const int BranchBlockNum) {
 	assert(0 <= BranchBlockNum);
@@ -13089,74 +13250,96 @@ bool SMPFunction::DetectConditionalReachabilityErrors(const int HeadBlockNum, co
 
 	// All blocks reachable by either FTBlock or NFTBlock should either precede the intersection point or
 	//  should be in CommonBlocksSeen (i.e. RPO block number greater than intersection point RPO number
-	//  should imply that we are merely continuing downward from the intersection point).
+	//  should imply that we are merely continuing downward from the intersection point). This is only true
+	//  for the IfThenElse case, not for an IfThen.
 	int IntersectionBlockNum = CommonBlocksSeen.FindLowestBitSet();
 	bool TracesIntersect = (0 < IntersectionBlockNum);
 	if (TracesIntersect) {
-		for (size_t BlockNum = (size_t)(IntersectionBlockNum + 1); BlockNum < this->GetNumBlocks(); ++BlockNum) {
-			if (FTBlocksSeen.GetBit(BlockNum)) {
-				bool success = NFTBlocksSeen.GetBit(BlockNum);
-				if (!success) {
-					Unstructured = true;
-					break;
+		bool IfThenCase = (IntersectionBlockNum == NFTBlockNum);
+		bool OddIfThenCase = (IntersectionBlockNum == FTBlockNum);
+		bool IfThenElseCase = (!(IfThenCase || OddIfThenCase));
+		if (IfThenElseCase) {
+			for (size_t BlockNum = (size_t)(IntersectionBlockNum + 1); BlockNum < this->GetNumBlocks(); ++BlockNum) {
+				if (FTBlocksSeen.GetBit(BlockNum)) {
+					bool success = NFTBlocksSeen.GetBit(BlockNum);
+					if (!success) {
+						Unstructured = true;
+						break;
+					}
 				}
 			}
 		}
 	}
-	if (!Unstructured) {
-		// Ensure that any loop contained inside the then or else clauses has its exit target
-		//  blocks dominated by the loop head block; a "back door" into a loop exit target block
-		//  that does not come from the loop must be restructured.
-		int LowestBlockSeen = FTBlocksSeen.FindLowestBitSet();
-		int HighestBlockSeen = FTBlocksSeen.FindHighestBitSet();
-		int LimitBlockNum = TracesIntersect ? IntersectionBlockNum : HighestBlockSeen;
-		if (0 <= LowestBlockSeen) {
-			for (int BlockNum = LowestBlockSeen; BlockNum <= LimitBlockNum; ++BlockNum) {
-				if (FTBlocksSeen.GetBit((size_t)BlockNum)) {
-					SMPBasicBlock *CurrBlock = this->GetBlockByNum((size_t)BlockNum);
-					if (CurrBlock->IsLoopHeaderBlock()) {
-						int LoopNum = this->GetLoopNumFromHeaderBlockNum(BlockNum);
-						assert(0 <= LoopNum);
-						for (int ExitTargetBlockNum : this->LoopExitTargets[(size_t)LoopNum]) {
-							if (!this->DoesBlockDominateBlock(HeadBlockNum, ExitTargetBlockNum)) {
-								Unstructured = true;
-								break;
-							}
+	if (0 < this->GetNumLoops()) {
+		if (!Unstructured) {
+			// Ensure that any loop contained inside the then or else clauses has its exit target
+			//  blocks dominated by the loop head block; a "back door" into a loop exit target block
+			//  that does not come from the loop must be restructured.
+			int LowestBlockSeen = FTBlocksSeen.FindLowestBitSet();
+			int HighestBlockSeen = FTBlocksSeen.FindHighestBitSet();
+			int LimitBlockNum = TracesIntersect ? IntersectionBlockNum : HighestBlockSeen;
+			if (0 <= LowestBlockSeen) {
+				for (int BlockNum = LowestBlockSeen; BlockNum <= LimitBlockNum; ++BlockNum) {
+					if (FTBlocksSeen.GetBit((size_t)BlockNum)) {
+						if (this->ConditionalReachabilityLoopErrorHelper(HeadBlockNum, BlockNum)) {
+							Unstructured = true;
+							break;
 						}
 					}
 				}
 			}
 		}
-	}
-	if (!Unstructured) {
-		// Ensure that any loop contained inside the then or else clauses has its exit target
-		//  blocks dominated by the loop head block; a "back door" into a loop exit target block
-		//  that does not come from the loop must be restructured.
-		int LowestBlockSeen = NFTBlocksSeen.FindLowestBitSet();
-		int HighestBlockSeen = NFTBlocksSeen.FindHighestBitSet();
-		int LimitBlockNum = TracesIntersect ? IntersectionBlockNum : HighestBlockSeen;
-		if (0 <= LowestBlockSeen) {
-			for (int BlockNum = LowestBlockSeen; BlockNum <= LimitBlockNum; ++BlockNum) {
-				if (NFTBlocksSeen.GetBit((size_t)BlockNum)) {
-					SMPBasicBlock *CurrBlock = this->GetBlockByNum((size_t)BlockNum);
-					if (CurrBlock->IsLoopHeaderBlock()) {
-						int LoopNum = this->GetLoopNumFromHeaderBlockNum(BlockNum);
-						assert(0 <= LoopNum);
-						for (int ExitTargetBlockNum : this->LoopExitTargets[(size_t)LoopNum]) {
-							if (!this->DoesBlockDominateBlock(HeadBlockNum, ExitTargetBlockNum)) {
-								Unstructured = true;
-								break;
-							}
+		if (!Unstructured) {
+			// Ensure that any loop contained inside the then or else clauses has its exit target
+			//  blocks dominated by the loop head block; a "back door" into a loop exit target block
+			//  that does not come from the loop must be restructured.
+			int LowestBlockSeen = NFTBlocksSeen.FindLowestBitSet();
+			int HighestBlockSeen = NFTBlocksSeen.FindHighestBitSet();
+			int LimitBlockNum = TracesIntersect ? IntersectionBlockNum : HighestBlockSeen;
+			if (0 <= LowestBlockSeen) {
+				for (int BlockNum = LowestBlockSeen; BlockNum <= LimitBlockNum; ++BlockNum) {
+					if (NFTBlocksSeen.GetBit((size_t)BlockNum)) {
+						if (this->ConditionalReachabilityLoopErrorHelper(HeadBlockNum, BlockNum)) {
+							Unstructured = true;
+							break;
 						}
 					}
 				}
 			}
 		}
-	}
+	} // end if we have loops
 
 	return Unstructured;
 } // end of SMPFunction::DetectConditionalReachabilityErrors()
 
+// Back door edge into loop exit targets (for multi-exit loops)?
+bool SMPFunction::ConditionalReachabilityLoopErrorHelper(const int CondHeadBlockNum, const int BlockNum) const {
+	bool Unstructured = false;
+
+	SMPBasicBlock *CurrBlock = this->GetBlockByNum((size_t)BlockNum);
+	if (CurrBlock->IsLoopHeaderBlock()) {
+		int InnerLoopNum = this->GetLoopNumFromHeaderBlockNum(BlockNum);
+		assert(0 <= InnerLoopNum);
+		bool MultiExitTargetInnerLoop = this->DoesLoopHaveMultipleExitTargets(InnerLoopNum);
+		for (int ExitTargetBlockNum : this->LoopExitTargets[(size_t)InnerLoopNum]) {
+			// If we have multiple exit targets, then they should all be contained
+			//  with the ELSE-branch, with their common successor being the loop
+			//  follow node. This loop follow node can be a common follow node for enclosing
+			//  statements, including the current conditional statement and outer 
+			//  conditional statements, in which case the inner HeadBlock does not
+			//  dominate it. However, with multiple exit targets, the exit targets
+			//  themselves must be dominated by the conditional HeadBlockNum in order
+			//  to be a structured CFG.
+			if (MultiExitTargetInnerLoop && (!this->DoesBlockDominateBlock(CondHeadBlockNum, ExitTargetBlockNum))) {
+				Unstructured = true;
+				break;
+			}
+		}
+	}
+
+	return Unstructured;
+} // end of SMPFunction::ConditionalReachabilityLoopErrorHelper()
+
 // Find guarded loops and fill the GuardToLoopMap and LoopToGuardMap
 void SMPFunction::FindGuardedLoops(void) {
 	assert(this->HasStructuredControlFlow());
@@ -15074,13 +15257,19 @@ void SMPFunction::BuildLoopList(int BlockNum, list<std::size_t> &LoopList) const
 // Build list of Block numbers contained in LoopNum.
 void SMPFunction::BuildLoopBlockList(const size_t LoopNum, list<std::size_t> &BlockList) {
 	assert(LoopNum < this->LoopCount);
+	this->BuildBlockListFromBitset(this->FuncBlocksByLoop.at(LoopNum), BlockList);
+	return;
+} // end of SMPFunction::BuildLoopBlockList()
+
+// each bit set in CurrBitSet is a block # for BlockList
+void SMPFunction::BuildBlockListFromBitset(const STARSBitSet &CurrBitSet, list<std::size_t> &BlockList) const {
 	for (size_t BlockIndex = 0; BlockIndex < (size_t)this->BlockCount; ++BlockIndex) {
-		if (this->FuncBlocksByLoop.at(LoopNum).GetBit(BlockIndex)) {
+		if (CurrBitSet.GetBit(BlockIndex)) {
 			BlockList.push_back(BlockIndex);
 		}
 	}
 	return;
-} // end of SMPFunction::BuildLoopBlockList()
+} // end of SMPFunction::BuildBlockListFromBitset()
 
 // Analyze how many times each loop iterates
 void SMPFunction::AnalyzeLoopIterations(void) {
diff --git a/src/base/SMPProgram.cpp b/src/base/SMPProgram.cpp
index b280b464..9089d66d 100644
--- a/src/base/SMPProgram.cpp
+++ b/src/base/SMPProgram.cpp
@@ -1147,7 +1147,7 @@ bool SMPProgram::EmitProgramSPARKAda(void) {
 	for (FuncIter = this->FuncMap.begin(); FuncIter != this->FuncMap.end(); ++FuncIter) {
 		SMPFunction *TempFunc = FuncIter->second;
 		if (TempFunc == nullptr) continue;
-		bool FuncFound = (0x444492 == TempFunc->GetFirstFuncAddr());
+		bool FuncFound = (0x402b90 == TempFunc->GetFirstFuncAddr());
 		if (FuncFound) {
 			TempFunc->Dump();
 			TempFunc->DumpDotCFG();
-- 
GitLab