From 1b2f22146c02233a41c3ed2bf17a86a0474049db Mon Sep 17 00:00:00 2001
From: Clark Coleman <clc@zephyr-software.com>
Date: Sun, 4 Oct 2020 20:15:12 -0400
Subject: [PATCH] SPARK: Translate fall-through block after conditional tail
 call.

---
 include/base/SMPFunction.h |  3 +-
 src/base/SMPFunction.cpp   | 96 +++++++++++++++++++++++++++++++++++---
 2 files changed, 92 insertions(+), 7 deletions(-)

diff --git a/include/base/SMPFunction.h b/include/base/SMPFunction.h
index 525a6521..e1387ed1 100644
--- a/include/base/SMPFunction.h
+++ b/include/base/SMPFunction.h
@@ -600,7 +600,7 @@ public:
 	bool IsBlockInLoop(int BlockNum, std::size_t LoopNum) const; // Is block (with block # BlockNum) inside loop # LoopNum?
 	bool AreBlocksInSameLoops(const int BlockNum1, const int BlockNum2) const;
 	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); // build list of loop numbers that BlockNum is part of.
+	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 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
@@ -1017,6 +1017,7 @@ private:
 	int FindConditionalFollowNode(int HeadBlockNum); // Find candidate block # for if-else follow node for HeadBlockNum; return -1 otherwise
 	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 FollowBlockNum) const; // Do IF and ELSE branches merge before FollowBlockNum?
+	bool FindMultiLoopContinue(const int BranchBlockNum) const; // Does COND_BRANCH behave as an unstructured multi-level loop continue statement?
 	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/SMPFunction.cpp b/src/base/SMPFunction.cpp
index 342a3df4..8642815e 100644
--- a/src/base/SMPFunction.cpp
+++ b/src/base/SMPFunction.cpp
@@ -593,11 +593,13 @@ bool SMPFunction::ComputeInOutRegs(bool InheritPass, bool &WritesMem, bool &Call
 	bool Changed = false;
 	bool MemoryInput = false;
 	bool MemoryOutput = false;
-	bool VerboseOutput = global_stars_interface->VerboseLoopsMode();
+	bool VerboseOutput = global_stars_interface->VerboseSPARKMode();
 	CallChainNonReturning = false;
 
 	if (InheritPass) {
-		SMP_msg("INFO: InheritPass for ComputeInOutRegs(), function %s at %llx\n", this->GetFuncName(), (uint64_t) this->GetFirstFuncAddr());
+		if (VerboseOutput) {
+			SMP_msg("INFO: InheritPass for ComputeInOutRegs(), function %s at %llx\n", this->GetFuncName(), (uint64_t) this->GetFirstFuncAddr());
+		}
 		// Look at all callees and union their bitsets in.
 		size_t OldInputBitCount = this->InputRegs.count();
 		size_t OldOutputBitCount = this->OutputRegs.count();
@@ -11567,7 +11569,7 @@ bool SMPFunction::AnalyzeConditionalStatements(void) {
 		SMPitype FlowType = LastInst->GetDataFlowType();
 		if (FlowType == COND_BRANCH) {
 			ControlFlowType LastCFType = this->GetControlFlowType(LastAddr);
-			if (FALL_THROUGH != LastCFType) { // already part of some loop or switch structure
+			if (FALL_THROUGH != LastCFType) { // already part of some compound conditional, loop or switch structure
 				continue;
 			}
 			int HeadBlockNum = CurrBlock->GetNumber();
@@ -11743,6 +11745,11 @@ int SMPFunction::FindConditionalFollowNode(int HeadBlockNum) {
 		return FollowBlockNum;
 	}
 
+	// Screen out branches that go outside their current loop in an unstructured way.
+	if (this->FindMultiLoopContinue(HeadBlockNum)) {
+		return FollowBlockNum;
+	}
+
 	// A well-structured candidate would be the biggest RPO-number block that has >= 2 predecessors
 	//  and has HeadBlockNum as its IDom. 
 	// EXCEPTION #1: Return and loop-back instructions interrupt control flow. See below.
@@ -12277,6 +12284,67 @@ bool SMPFunction::FindUnstructuredIfThenElse(const int HeadBlockNum, const int F
 	return UnstructuredClauses;
 } // end of SMPFunction::FindUnstructuredIfThenElse()
 
+// Does COND_BRANCH behave as an unstructured multi-level loop continue statement?
+bool SMPFunction::FindMultiLoopContinue(const int BranchBlockNum) const {
+	assert(0 <= BranchBlockNum);
+	bool Unstructured = false;
+	if (!this->IsBlockInAnyLoop(BranchBlockNum))
+		return Unstructured;
+
+	int InnerMostLoopNum = this->GetInnermostLoopNum(BranchBlockNum);
+	SMPBasicBlock *BranchBlock = this->GetBlockByNum((size_t)BranchBlockNum);
+	assert(0 <= InnerMostLoopNum);
+
+	// Each successor of the COND_BRANCH at the end of BranchBlock
+	//  must be in one of the following three categories:
+	//  A) Inside the innermost loop containing BranchBlock.
+	//  B) The follow block for the innermost loop containing BranchBlock.
+	//  C) The header block for an outer loop that has the same follow block as the
+	//      innermost loop.
+	//
+	// The SPARK Ada translations of these cases are straightforward:
+	//  A) if-then-else within the innermost loop.
+	//  B) exit when (condition) or exit when (inverted condition).
+	//  C) if (inverted condition) then ... endif; endloop; endloop;
+	//
+	// Note that category C precludes having any code inside the endif-endloop-endloop series.
+	//  This condition is guaranteed by having the same follow nodes; a follow node comes
+	//  right after an endloop, so if an inner loop has the same follow node as an outer loop,
+	//  their endloop statements are consecutive.
+	//
+	// An example of a COND_BRANCH inside a loop that is not in categories A or B but is within
+	//  category C is seen at the end of basic block #5 in the func at 0x4031b0 in bzip2.psexe.
+	// An example that fails all three category tests, and is thus an unstructured multi-level
+	//  loop continue statement, is seen at basic block #8 in the func at 0x437bf3 in busybox.psexe.
+
+	int InnerLoopFollowBlockNum = this->LoopFollowNodes[(size_t)InnerMostLoopNum];
+	for (list<SMPBasicBlock *>::const_iterator SuccIter = BranchBlock->GetFirstConstSucc(); SuccIter != BranchBlock->GetLastConstSucc(); ++SuccIter) {
+		SMPBasicBlock *SuccBlock = (*SuccIter);
+		int SuccBlockNum = SuccBlock->GetNumber();
+		if (!this->IsBlockInLoop(SuccBlockNum, (size_t)InnerMostLoopNum)) {
+			// Failed category A test.
+			if (SuccBlockNum != InnerLoopFollowBlockNum) {
+				// Failed category B test. Needs to be header block for outer loop.
+				int OuterLoopFollowBlockNum = SMP_BLOCKNUM_UNINIT;
+				if (SuccBlock->IsLoopHeaderBlock()) {
+					int OuterLoopNum = this->GetLoopNumFromHeaderBlockNum(SuccBlockNum);
+					assert(0 <= OuterLoopNum);
+					OuterLoopFollowBlockNum = this->LoopFollowNodes[(size_t)OuterLoopNum];
+				}
+				if (OuterLoopFollowBlockNum != InnerLoopFollowBlockNum) {
+					// Failed category C test.
+					Unstructured = true;
+					SMP_msg("ERROR: SPARK: Unstructured due to multi-level loop continue statement at %llx in %s\n",
+						(uint64_t)BranchBlock->GetLastAddr(), this->GetFuncName());
+					break;
+				}
+			}
+		}
+	} // end for all successors to BranchBlock
+
+	return Unstructured;
+} // end of SMPFunction::FindMultiLoopContinue()
+
 // Find guarded loops and fill the GuardToLoopMap and LoopToGuardMap
 void SMPFunction::FindGuardedLoops(void) {
 	assert(this->HasStructuredControlFlow());
@@ -14135,7 +14203,7 @@ bool SMPFunction::DoesEdgeExitLoop(const int BlockNum1, const int BlockNum2) con
 } // end of SMPFunction::DoesEdgeExitLoop()
 
 // build list of loop numbers that BlockNum is part of.
-void SMPFunction::BuildLoopList(int BlockNum, list<std::size_t> &LoopList) {
+void SMPFunction::BuildLoopList(int BlockNum, list<std::size_t> &LoopList) const {
 	std::size_t LoopIndex;
 	assert((BlockNum >= 0) && (BlockNum < this->BlockCount));
 	for (LoopIndex = 0; LoopIndex < this->LoopCount; ++LoopIndex) {
@@ -16883,6 +16951,7 @@ void SMPFunction::MarkSpecialNumericErrorCases(void) {
 				else {
 					SMP_msg("INFO: Overlapping loops non-single-expr unstructured CFG case at block %zu in %s\n", BlockIndex, this->GetFuncName());
 				}
+				break; // no point in continuing if we cannot translate to SPARK Ada
 			}
 
 			// Find all conditional and unconditional jumps within the function.
@@ -16897,6 +16966,11 @@ void SMPFunction::MarkSpecialNumericErrorCases(void) {
 						break; // no point in continuing if we cannot translate to SPARK Ada
 					}
 
+					if (this->FindMultiLoopContinue((int) BlockIndex)) {
+						this->HasStructuredCFG = false;
+						break; // no point in continuing if we cannot translate to SPARK Ada
+					}
+
 					int FollowBlockNum;
 					if (!InvertedExit) {
 						this->SetControlFlowType(InstAddr, LOOP_EXIT);
@@ -22512,7 +22586,10 @@ void SMPFunction::EmitSPARKAdaForBlock(int CurrBlockNum, int FollowBlockNum, FIL
 				else if (FlowType == RETURN) {
 					LastInst->EmitSPARKAda(SPARKBodyFile);
 					// Handle possible tail call case
-					;
+					if ((FALL_THROUGH == LastCFType) && (1 == CurrBlock->GetNumSuccessors())) {
+						assert(LastInst->IsCondTailCall());
+						ResumeBlockNum = (*(CurrBlock->GetFirstConstSucc()))->GetNumber();
+					}
 				}
 				else if ((FlowType == CALL) || (FlowType == INDIR_CALL)) {
 					LastInst->EmitSPARKAda(SPARKBodyFile);
@@ -22894,8 +22971,15 @@ void SMPFunction::EmitSPARKAdaForConditional(int HeaderBlockNum, int FollowBlock
 		//  that guards the loop could be separated from the loop header by a block of
 		//  straight-line (fallthrough-only) code, or not. We need to detect this case.
 
-		SMPBasicBlock *FallThroughBlock = *(HeaderBlock->GetFallThroughSucc()); // !!!!****!!!! Could there be an inverted guard loop case?
+		SMPBasicBlock *FallThroughBlock = *(HeaderBlock->GetFallThroughSucc());
 		int FallThroughBlockNum = FallThroughBlock->GetNumber();
+		bool InvertedCase = LastInst->IsOddIfThenCase();
+		if (InvertedCase) {
+			// Guard jumps to the THEN case.
+			assert(FallThroughBlockNum == FollowBlockNum);
+			FallThroughBlock = *(HeaderBlock->GetCondNonFallThroughSucc());
+			FallThroughBlockNum = FallThroughBlock->GetNumber();
+		}
 		STARS_ea_t LoopAddr = GuardIter->second;
 		bool SeparatedCase = (!FallThroughBlock->IsLoopHeaderBlock());
 		SMPBasicBlock *LoopHeadBlock = this->GetBlockFromInstAddr(LoopAddr);
-- 
GitLab