Fix predicating instructions before return in loops.

Only the instructions following a return statement were being marked as
affected by it so predication would be applied. But in a loop the instructions
above the return statement are also affected by it.

Bug b/25220690

Change-Id: If2490a6e0b4e9cf8b6e28b33cbbbcec8b4ebfdaa
Reviewed-on: https://swiftshader-review.googlesource.com/5183
Reviewed-by: Alexis Hétu <sugoi@google.com>
Reviewed-by: Nicolas Capens <capn@google.com>
Tested-by: Nicolas Capens <capn@google.com>
diff --git a/src/Shader/Shader.cpp b/src/Shader/Shader.cpp
index d61dcde..a6c5fda 100644
--- a/src/Shader/Shader.cpp
+++ b/src/Shader/Shader.cpp
@@ -1090,14 +1090,14 @@
 		return opcode == OPCODE_BREAK || opcode == OPCODE_BREAKC || opcode == OPCODE_BREAKP;
 	}
 
-	bool Shader::Instruction::isLoopOrSwitch() const
+	bool Shader::Instruction::isLoop() const
 	{
-		return opcode == OPCODE_LOOP || opcode == OPCODE_REP || opcode == OPCODE_WHILE || opcode == OPCODE_SWITCH;
+		return opcode == OPCODE_LOOP || opcode == OPCODE_REP || opcode == OPCODE_WHILE;
 	}
 
-	bool Shader::Instruction::isEndLoopOrSwitch() const
+	bool Shader::Instruction::isEndLoop() const
 	{
-		return opcode == OPCODE_ENDLOOP || opcode == OPCODE_ENDREP || opcode == OPCODE_ENDWHILE || opcode == OPCODE_ENDSWITCH;;
+		return opcode == OPCODE_ENDLOOP || opcode == OPCODE_ENDREP || opcode == OPCODE_ENDWHILE;
 	}
 
 	bool Shader::Instruction::isPredicated() const
@@ -1657,6 +1657,7 @@
 		int breakDepth = 0;
 		int continueDepth = 0;
 		bool leaveReturn = false;
+		unsigned int functionBegin = 0;
 
 		for(unsigned int i = 0; i < instruction.size(); i++)
 		{
@@ -1688,11 +1689,11 @@
 
 			if(breakDepth > 0)
 			{
-				if(instruction[i]->isLoopOrSwitch())   // Nested loop or switch, don't make the end of it disable the break execution mask
+				if(instruction[i]->isLoop() || instruction[i]->opcode == OPCODE_SWITCH)   // Nested loop or switch, don't make the end of it disable the break execution mask
 				{
 					breakDepth++;
 				}
-				else if(instruction[i]->isEndLoopOrSwitch())
+				else if(instruction[i]->isEndLoop() || instruction[i]->opcode == OPCODE_ENDSWITCH)
 				{
 					breakDepth--;
 				}
@@ -1713,11 +1714,11 @@
 
 			if(continueDepth > 0)
 			{
-				if(instruction[i]->isLoopOrSwitch())   // Nested loop or switch, don't make the end of it disable the break execution mask
+				if(instruction[i]->isLoop() || instruction[i]->opcode == OPCODE_SWITCH)   // Nested loop or switch, don't make the end of it disable the break execution mask
 				{
 					continueDepth++;
 				}
-				else if(instruction[i]->isEndLoopOrSwitch())
+				else if(instruction[i]->isEndLoop() || instruction[i]->opcode == OPCODE_ENDSWITCH)
 				{
 					continueDepth--;
 				}
@@ -1734,11 +1735,29 @@
 			if(instruction[i]->opcode == OPCODE_LEAVE)
 			{
 				leaveReturn = true;
+
+				// Mark loop body instructions prior to the return statement
+				for(unsigned int l = functionBegin; l < i; l++)
+				{
+					if(instruction[l]->isLoop())
+					{
+						for(unsigned int r = l + 1; r < i; r++)
+						{
+							instruction[r]->analysisLeave = true;
+						}
+
+						break;
+					}
+				}
 			}
 			else if(instruction[i]->opcode == OPCODE_RET)   // End of the function
 			{
 				leaveReturn = false;
 			}
+			else if(instruction[i]->opcode == OPCODE_LABEL)
+			{
+				functionBegin = i;
+			}
 
 			if(leaveReturn)
 			{
diff --git a/src/Shader/Shader.hpp b/src/Shader/Shader.hpp
index 404452d..f41d514 100644
--- a/src/Shader/Shader.hpp
+++ b/src/Shader/Shader.hpp
@@ -495,8 +495,8 @@
 			bool isBranch() const;
 			bool isCall() const;
 			bool isBreak() const;
-			bool isLoopOrSwitch() const;
-			bool isEndLoopOrSwitch() const;
+			bool isLoop() const;
+			bool isEndLoop() const;
 
 			bool isPredicated() const;