Assert on unimplemented instructions

Replace the runtime `warning` with something that will actually crash
us, and add stub implementations of OpLabel and OpReturn to allow
trivial shaders to work.

There's little point in continuing beyond an unimplemented instruction
-- if you get lucky, you hit a crash on use of the instruction's result.
If you get unlucky, you wander off into undefined behavior.

Change-Id: I3b222ea74446754276096c81fc3669c311872316
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/26088
Tested-by: Chris Forbes <chrisforbes@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
diff --git a/src/Pipeline/SpirvShader.cpp b/src/Pipeline/SpirvShader.cpp
index ed2beee..77d3393 100644
--- a/src/Pipeline/SpirvShader.cpp
+++ b/src/Pipeline/SpirvShader.cpp
@@ -31,6 +31,13 @@
 		// - There is exactly one entrypoint in the module, and it's the one we want
 		// - The only input/output OpVariables present are those used by the entrypoint
 
+		// TODO: Add real support for control flow. For now, track whether we've seen
+		// a label or a return already (if so, the shader does things we will mishandle).
+		// We expect there to be one of each in a simple shader -- the first and last instruction
+		// of the entrypoint function.
+		bool seenLabel = false;
+		bool seenReturn = false;
+
 		for (auto insn : *this)
 		{
 			switch (insn.opcode())
@@ -101,6 +108,18 @@
 				break;
 			}
 
+			case spv::OpLabel:
+				if (seenLabel)
+					UNIMPLEMENTED("Shader contains multiple labels, has control flow");
+				seenLabel = true;
+				break;
+
+			case spv::OpReturn:
+				if (seenReturn)
+					UNIMPLEMENTED("Shader contains multiple returns, has control flow");
+				seenReturn = true;
+				break;
+
 			case spv::OpTypeVoid:
 			case spv::OpTypeBool:
 			case spv::OpTypeInt:
@@ -278,7 +297,6 @@
 			}
 
 			case spv::OpStore:
-			case spv::OpReturn:
 				// Don't need to do anything during analysis pass
 				break;
 
@@ -287,8 +305,7 @@
 				break;
 
 			default:
-				printf("Warning: ignored opcode %s\n", OpcodeName(insn.opcode()).c_str());
-				break;    // This is OK, these passes are intentionally partial
+				UNIMPLEMENTED(OpcodeName(insn.opcode()).c_str());
 			}
 		}
 	}
@@ -875,6 +892,14 @@
 				// or don't require any work at all.
 				break;
 
+			case spv::OpLabel:
+			case spv::OpReturn:
+				// TODO: when we do control flow, will need to do some work here.
+				// Until then, there is nothing to do -- we expect there to be an initial OpLabel
+				// in the entrypoint function, for which we do nothing; and a final OpReturn at the
+				// end of the entrypoint function, for which we do nothing.
+				break;
+
 			case spv::OpVariable:
 				EmitVariable(insn, routine);
 				break;
@@ -956,7 +981,7 @@
 				break;
 
 			default:
-				printf("emit: ignoring opcode %s\n", OpcodeName(insn.opcode()).c_str());
+				UNIMPLEMENTED(OpcodeName(insn.opcode()).c_str());
 				break;
 			}
 		}