Eliminate EmitResult

The return value of code emission functions was used to determine
whether the terminator of a SPIR-V code block had been reached. This was
not intuitive and entangled a control flow implementation detail with
all unrelated instructions as well.

Arguably one advantage of the old code is that it was impossible to not
add an EmitResult to the implementation of new instructions, while the
new IsTerminator() function returns false in the default case. To
prevent this from causing bugs when left unupdated, the
EmitInstruction() method now handles terminator and non-terminator
instructions in separate switch statements.

Bug: b/247020580
Change-Id: I8ea1fa2250e22990f0f4864d1844c8e378793f1e
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/68968
Kokoro-Result: kokoro <noreply+kokoro@google.com>
Tested-by: Nicolas Capens <nicolascapens@google.com>
Reviewed-by: Alexis Hétu <sugoi@google.com>
diff --git a/src/Pipeline/SpirvShader.cpp b/src/Pipeline/SpirvShader.cpp
index b026e93..22eb654 100644
--- a/src/Pipeline/SpirvShader.cpp
+++ b/src/Pipeline/SpirvShader.cpp
@@ -1877,21 +1877,16 @@
 {
 	for(auto insn = begin; insn != end; insn++)
 	{
-		auto result = EmitInstruction(insn);
-		switch(result)
+		EmitInstruction(insn);
+
+		if(shader.IsTerminator(insn.opcode()))
 		{
-		case EmitResult::Continue:
-			continue;
-		case EmitResult::Terminator:
-			break;
-		default:
-			UNREACHABLE("Unexpected EmitResult %d", int(result));
 			break;
 		}
 	}
 }
 
-EmitState::EmitResult EmitState::EmitInstruction(InsnIterator insn)
+void EmitState::EmitInstruction(InsnIterator insn)
 {
 	auto opcode = insn.opcode();
 
@@ -1908,377 +1903,387 @@
 	}
 #endif  // ENABLE_DBG_MSGS
 
-	switch(opcode)
+	if(shader.IsTerminator(opcode))
 	{
-	case spv::OpTypeVoid:
-	case spv::OpTypeInt:
-	case spv::OpTypeFloat:
-	case spv::OpTypeBool:
-	case spv::OpTypeVector:
-	case spv::OpTypeArray:
-	case spv::OpTypeRuntimeArray:
-	case spv::OpTypeMatrix:
-	case spv::OpTypeStruct:
-	case spv::OpTypePointer:
-	case spv::OpTypeForwardPointer:
-	case spv::OpTypeFunction:
-	case spv::OpTypeImage:
-	case spv::OpTypeSampledImage:
-	case spv::OpTypeSampler:
-	case spv::OpExecutionMode:
-	case spv::OpExecutionModeId:
-	case spv::OpMemoryModel:
-	case spv::OpFunction:
-	case spv::OpFunctionEnd:
-	case spv::OpConstant:
-	case spv::OpConstantNull:
-	case spv::OpConstantTrue:
-	case spv::OpConstantFalse:
-	case spv::OpConstantComposite:
-	case spv::OpSpecConstant:
-	case spv::OpSpecConstantTrue:
-	case spv::OpSpecConstantFalse:
-	case spv::OpSpecConstantComposite:
-	case spv::OpSpecConstantOp:
-	case spv::OpUndef:
-	case spv::OpExtension:
-	case spv::OpCapability:
-	case spv::OpEntryPoint:
-	case spv::OpExtInstImport:
-	case spv::OpDecorate:
-	case spv::OpMemberDecorate:
-	case spv::OpGroupDecorate:
-	case spv::OpGroupMemberDecorate:
-	case spv::OpDecorationGroup:
-	case spv::OpDecorateId:
-	case spv::OpDecorateString:
-	case spv::OpMemberDecorateString:
-	case spv::OpName:
-	case spv::OpMemberName:
-	case spv::OpSource:
-	case spv::OpSourceContinued:
-	case spv::OpSourceExtension:
-	case spv::OpNoLine:
-	case spv::OpModuleProcessed:
-	case spv::OpString:
-		// Nothing to do at emit time. These are either fully handled at analysis time,
-		// or don't require any work at all.
-		return EmitResult::Continue;
+		switch(opcode)
+		{
+		case spv::OpBranch:
+			return EmitBranch(insn);
 
-	case spv::OpLine:
-		return EmitResult::Continue;  // TODO(b/251802301)
+		case spv::OpBranchConditional:
+			return EmitBranchConditional(insn);
 
-	case spv::OpLabel:
-		return EmitResult::Continue;
+		case spv::OpSwitch:
+			return EmitSwitch(insn);
 
-	case spv::OpVariable:
-		return EmitVariable(insn);
+		case spv::OpUnreachable:
+			return EmitUnreachable(insn);
 
-	case spv::OpLoad:
-	case spv::OpAtomicLoad:
-		return EmitLoad(insn);
+		case spv::OpReturn:
+			return EmitReturn(insn);
 
-	case spv::OpStore:
-	case spv::OpAtomicStore:
-		return EmitStore(insn);
+		case spv::OpKill:
+		case spv::OpTerminateInvocation:
+			return EmitTerminateInvocation(insn);
 
-	case spv::OpAtomicIAdd:
-	case spv::OpAtomicISub:
-	case spv::OpAtomicSMin:
-	case spv::OpAtomicSMax:
-	case spv::OpAtomicUMin:
-	case spv::OpAtomicUMax:
-	case spv::OpAtomicAnd:
-	case spv::OpAtomicOr:
-	case spv::OpAtomicXor:
-	case spv::OpAtomicIIncrement:
-	case spv::OpAtomicIDecrement:
-	case spv::OpAtomicExchange:
-		return EmitAtomicOp(insn);
-
-	case spv::OpAtomicCompareExchange:
-		return EmitAtomicCompareExchange(insn);
-
-	case spv::OpAccessChain:
-	case spv::OpInBoundsAccessChain:
-	case spv::OpPtrAccessChain:
-		return EmitAccessChain(insn);
-
-	case spv::OpCompositeConstruct:
-		return EmitCompositeConstruct(insn);
-
-	case spv::OpCompositeInsert:
-		return EmitCompositeInsert(insn);
-
-	case spv::OpCompositeExtract:
-		return EmitCompositeExtract(insn);
-
-	case spv::OpVectorShuffle:
-		return EmitVectorShuffle(insn);
-
-	case spv::OpVectorExtractDynamic:
-		return EmitVectorExtractDynamic(insn);
-
-	case spv::OpVectorInsertDynamic:
-		return EmitVectorInsertDynamic(insn);
-
-	case spv::OpVectorTimesScalar:
-	case spv::OpMatrixTimesScalar:
-		return EmitVectorTimesScalar(insn);
-
-	case spv::OpMatrixTimesVector:
-		return EmitMatrixTimesVector(insn);
-
-	case spv::OpVectorTimesMatrix:
-		return EmitVectorTimesMatrix(insn);
-
-	case spv::OpMatrixTimesMatrix:
-		return EmitMatrixTimesMatrix(insn);
-
-	case spv::OpOuterProduct:
-		return EmitOuterProduct(insn);
-
-	case spv::OpTranspose:
-		return EmitTranspose(insn);
-
-	case spv::OpNot:
-	case spv::OpBitFieldInsert:
-	case spv::OpBitFieldSExtract:
-	case spv::OpBitFieldUExtract:
-	case spv::OpBitReverse:
-	case spv::OpBitCount:
-	case spv::OpSNegate:
-	case spv::OpFNegate:
-	case spv::OpLogicalNot:
-	case spv::OpConvertFToU:
-	case spv::OpConvertFToS:
-	case spv::OpConvertSToF:
-	case spv::OpConvertUToF:
-	case spv::OpBitcast:
-	case spv::OpIsInf:
-	case spv::OpIsNan:
-	case spv::OpDPdx:
-	case spv::OpDPdxCoarse:
-	case spv::OpDPdy:
-	case spv::OpDPdyCoarse:
-	case spv::OpFwidth:
-	case spv::OpFwidthCoarse:
-	case spv::OpDPdxFine:
-	case spv::OpDPdyFine:
-	case spv::OpFwidthFine:
-	case spv::OpQuantizeToF16:
-		return EmitUnaryOp(insn);
-
-	case spv::OpIAdd:
-	case spv::OpISub:
-	case spv::OpIMul:
-	case spv::OpSDiv:
-	case spv::OpUDiv:
-	case spv::OpFAdd:
-	case spv::OpFSub:
-	case spv::OpFMul:
-	case spv::OpFDiv:
-	case spv::OpFMod:
-	case spv::OpFRem:
-	case spv::OpFOrdEqual:
-	case spv::OpFUnordEqual:
-	case spv::OpFOrdNotEqual:
-	case spv::OpFUnordNotEqual:
-	case spv::OpFOrdLessThan:
-	case spv::OpFUnordLessThan:
-	case spv::OpFOrdGreaterThan:
-	case spv::OpFUnordGreaterThan:
-	case spv::OpFOrdLessThanEqual:
-	case spv::OpFUnordLessThanEqual:
-	case spv::OpFOrdGreaterThanEqual:
-	case spv::OpFUnordGreaterThanEqual:
-	case spv::OpSMod:
-	case spv::OpSRem:
-	case spv::OpUMod:
-	case spv::OpIEqual:
-	case spv::OpINotEqual:
-	case spv::OpUGreaterThan:
-	case spv::OpSGreaterThan:
-	case spv::OpUGreaterThanEqual:
-	case spv::OpSGreaterThanEqual:
-	case spv::OpULessThan:
-	case spv::OpSLessThan:
-	case spv::OpULessThanEqual:
-	case spv::OpSLessThanEqual:
-	case spv::OpShiftRightLogical:
-	case spv::OpShiftRightArithmetic:
-	case spv::OpShiftLeftLogical:
-	case spv::OpBitwiseOr:
-	case spv::OpBitwiseXor:
-	case spv::OpBitwiseAnd:
-	case spv::OpLogicalOr:
-	case spv::OpLogicalAnd:
-	case spv::OpLogicalEqual:
-	case spv::OpLogicalNotEqual:
-	case spv::OpUMulExtended:
-	case spv::OpSMulExtended:
-	case spv::OpIAddCarry:
-	case spv::OpISubBorrow:
-		return EmitBinaryOp(insn);
-
-	case spv::OpDot:
-	case spv::OpSDot:
-	case spv::OpUDot:
-	case spv::OpSUDot:
-	case spv::OpSDotAccSat:
-	case spv::OpUDotAccSat:
-	case spv::OpSUDotAccSat:
-		return EmitDot(insn);
-
-	case spv::OpSelect:
-		return EmitSelect(insn);
-
-	case spv::OpExtInst:
-		return EmitExtendedInstruction(insn);
-
-	case spv::OpAny:
-		return EmitAny(insn);
-
-	case spv::OpAll:
-		return EmitAll(insn);
-
-	case spv::OpBranch:
-		return EmitBranch(insn);
-
-	case spv::OpPhi:
-		return EmitPhi(insn);
-
-	case spv::OpSelectionMerge:
-	case spv::OpLoopMerge:
-		return EmitResult::Continue;
-
-	case spv::OpBranchConditional:
-		return EmitBranchConditional(insn);
-
-	case spv::OpSwitch:
-		return EmitSwitch(insn);
-
-	case spv::OpUnreachable:
-		return EmitUnreachable(insn);
-
-	case spv::OpReturn:
-		return EmitReturn(insn);
-
-	case spv::OpFunctionCall:
-		return EmitFunctionCall(insn);
-
-	case spv::OpKill:
-	case spv::OpTerminateInvocation:
-		return EmitTerminateInvocation(insn);
-
-	case spv::OpDemoteToHelperInvocation:
-		return EmitDemoteToHelperInvocation(insn);
-
-	case spv::OpIsHelperInvocationEXT:
-		return EmitIsHelperInvocation(insn);
-
-	case spv::OpImageSampleImplicitLod:
-	case spv::OpImageSampleExplicitLod:
-	case spv::OpImageSampleDrefImplicitLod:
-	case spv::OpImageSampleDrefExplicitLod:
-	case spv::OpImageSampleProjImplicitLod:
-	case spv::OpImageSampleProjExplicitLod:
-	case spv::OpImageSampleProjDrefImplicitLod:
-	case spv::OpImageSampleProjDrefExplicitLod:
-	case spv::OpImageGather:
-	case spv::OpImageDrefGather:
-	case spv::OpImageFetch:
-	case spv::OpImageQueryLod:
-		return EmitImageSample(ImageInstruction(insn, shader, *this));
-
-	case spv::OpImageQuerySizeLod:
-		return EmitImageQuerySizeLod(insn);
-
-	case spv::OpImageQuerySize:
-		return EmitImageQuerySize(insn);
-
-	case spv::OpImageQueryLevels:
-		return EmitImageQueryLevels(insn);
-
-	case spv::OpImageQuerySamples:
-		return EmitImageQuerySamples(insn);
-
-	case spv::OpImageRead:
-		return EmitImageRead(ImageInstruction(insn, shader, *this));
-
-	case spv::OpImageWrite:
-		return EmitImageWrite(ImageInstruction(insn, shader, *this));
-
-	case spv::OpImageTexelPointer:
-		return EmitImageTexelPointer(ImageInstruction(insn, shader, *this));
-
-	case spv::OpSampledImage:
-		return EmitSampledImage(insn);
-
-	case spv::OpImage:
-		return EmitImage(insn);
-
-	case spv::OpCopyObject:
-	case spv::OpCopyLogical:
-		return EmitCopyObject(insn);
-
-	case spv::OpCopyMemory:
-		return EmitCopyMemory(insn);
-
-	case spv::OpControlBarrier:
-		return EmitControlBarrier(insn);
-
-	case spv::OpMemoryBarrier:
-		return EmitMemoryBarrier(insn);
-
-	case spv::OpGroupNonUniformElect:
-	case spv::OpGroupNonUniformAll:
-	case spv::OpGroupNonUniformAny:
-	case spv::OpGroupNonUniformAllEqual:
-	case spv::OpGroupNonUniformBroadcast:
-	case spv::OpGroupNonUniformBroadcastFirst:
-	case spv::OpGroupNonUniformQuadBroadcast:
-	case spv::OpGroupNonUniformQuadSwap:
-	case spv::OpGroupNonUniformBallot:
-	case spv::OpGroupNonUniformInverseBallot:
-	case spv::OpGroupNonUniformBallotBitExtract:
-	case spv::OpGroupNonUniformBallotBitCount:
-	case spv::OpGroupNonUniformBallotFindLSB:
-	case spv::OpGroupNonUniformBallotFindMSB:
-	case spv::OpGroupNonUniformShuffle:
-	case spv::OpGroupNonUniformShuffleXor:
-	case spv::OpGroupNonUniformShuffleUp:
-	case spv::OpGroupNonUniformShuffleDown:
-	case spv::OpGroupNonUniformIAdd:
-	case spv::OpGroupNonUniformFAdd:
-	case spv::OpGroupNonUniformIMul:
-	case spv::OpGroupNonUniformFMul:
-	case spv::OpGroupNonUniformSMin:
-	case spv::OpGroupNonUniformUMin:
-	case spv::OpGroupNonUniformFMin:
-	case spv::OpGroupNonUniformSMax:
-	case spv::OpGroupNonUniformUMax:
-	case spv::OpGroupNonUniformFMax:
-	case spv::OpGroupNonUniformBitwiseAnd:
-	case spv::OpGroupNonUniformBitwiseOr:
-	case spv::OpGroupNonUniformBitwiseXor:
-	case spv::OpGroupNonUniformLogicalAnd:
-	case spv::OpGroupNonUniformLogicalOr:
-	case spv::OpGroupNonUniformLogicalXor:
-		return EmitGroupNonUniform(insn);
-
-	case spv::OpArrayLength:
-		return EmitArrayLength(insn);
-
-	default:
-		UNREACHABLE("%s", shader.OpcodeName(opcode));
-		break;
+		default:
+			UNREACHABLE("Unknown terminal instruction %s", shader.OpcodeName(opcode));
+			break;
+		}
 	}
+	else  // Non-terminal instructions
+	{
+		switch(opcode)
+		{
+		case spv::OpTypeVoid:
+		case spv::OpTypeInt:
+		case spv::OpTypeFloat:
+		case spv::OpTypeBool:
+		case spv::OpTypeVector:
+		case spv::OpTypeArray:
+		case spv::OpTypeRuntimeArray:
+		case spv::OpTypeMatrix:
+		case spv::OpTypeStruct:
+		case spv::OpTypePointer:
+		case spv::OpTypeForwardPointer:
+		case spv::OpTypeFunction:
+		case spv::OpTypeImage:
+		case spv::OpTypeSampledImage:
+		case spv::OpTypeSampler:
+		case spv::OpExecutionMode:
+		case spv::OpExecutionModeId:
+		case spv::OpMemoryModel:
+		case spv::OpFunction:
+		case spv::OpFunctionEnd:
+		case spv::OpConstant:
+		case spv::OpConstantNull:
+		case spv::OpConstantTrue:
+		case spv::OpConstantFalse:
+		case spv::OpConstantComposite:
+		case spv::OpSpecConstant:
+		case spv::OpSpecConstantTrue:
+		case spv::OpSpecConstantFalse:
+		case spv::OpSpecConstantComposite:
+		case spv::OpSpecConstantOp:
+		case spv::OpUndef:
+		case spv::OpExtension:
+		case spv::OpCapability:
+		case spv::OpEntryPoint:
+		case spv::OpExtInstImport:
+		case spv::OpDecorate:
+		case spv::OpMemberDecorate:
+		case spv::OpGroupDecorate:
+		case spv::OpGroupMemberDecorate:
+		case spv::OpDecorationGroup:
+		case spv::OpDecorateId:
+		case spv::OpDecorateString:
+		case spv::OpMemberDecorateString:
+		case spv::OpName:
+		case spv::OpMemberName:
+		case spv::OpSource:
+		case spv::OpSourceContinued:
+		case spv::OpSourceExtension:
+		case spv::OpNoLine:
+		case spv::OpModuleProcessed:
+		case spv::OpString:
+			// Nothing to do at emit time. These are either fully handled at analysis time,
+			// or don't require any work at all.
+			return;
 
-	return EmitResult::Continue;
+		case spv::OpLine:
+			return;  // TODO(b/251802301)
+
+		case spv::OpLabel:
+			return;
+
+		case spv::OpVariable:
+			return EmitVariable(insn);
+
+		case spv::OpLoad:
+		case spv::OpAtomicLoad:
+			return EmitLoad(insn);
+
+		case spv::OpStore:
+		case spv::OpAtomicStore:
+			return EmitStore(insn);
+
+		case spv::OpAtomicIAdd:
+		case spv::OpAtomicISub:
+		case spv::OpAtomicSMin:
+		case spv::OpAtomicSMax:
+		case spv::OpAtomicUMin:
+		case spv::OpAtomicUMax:
+		case spv::OpAtomicAnd:
+		case spv::OpAtomicOr:
+		case spv::OpAtomicXor:
+		case spv::OpAtomicIIncrement:
+		case spv::OpAtomicIDecrement:
+		case spv::OpAtomicExchange:
+			return EmitAtomicOp(insn);
+
+		case spv::OpAtomicCompareExchange:
+			return EmitAtomicCompareExchange(insn);
+
+		case spv::OpAccessChain:
+		case spv::OpInBoundsAccessChain:
+		case spv::OpPtrAccessChain:
+			return EmitAccessChain(insn);
+
+		case spv::OpCompositeConstruct:
+			return EmitCompositeConstruct(insn);
+
+		case spv::OpCompositeInsert:
+			return EmitCompositeInsert(insn);
+
+		case spv::OpCompositeExtract:
+			return EmitCompositeExtract(insn);
+
+		case spv::OpVectorShuffle:
+			return EmitVectorShuffle(insn);
+
+		case spv::OpVectorExtractDynamic:
+			return EmitVectorExtractDynamic(insn);
+
+		case spv::OpVectorInsertDynamic:
+			return EmitVectorInsertDynamic(insn);
+
+		case spv::OpVectorTimesScalar:
+		case spv::OpMatrixTimesScalar:
+			return EmitVectorTimesScalar(insn);
+
+		case spv::OpMatrixTimesVector:
+			return EmitMatrixTimesVector(insn);
+
+		case spv::OpVectorTimesMatrix:
+			return EmitVectorTimesMatrix(insn);
+
+		case spv::OpMatrixTimesMatrix:
+			return EmitMatrixTimesMatrix(insn);
+
+		case spv::OpOuterProduct:
+			return EmitOuterProduct(insn);
+
+		case spv::OpTranspose:
+			return EmitTranspose(insn);
+
+		case spv::OpNot:
+		case spv::OpBitFieldInsert:
+		case spv::OpBitFieldSExtract:
+		case spv::OpBitFieldUExtract:
+		case spv::OpBitReverse:
+		case spv::OpBitCount:
+		case spv::OpSNegate:
+		case spv::OpFNegate:
+		case spv::OpLogicalNot:
+		case spv::OpConvertFToU:
+		case spv::OpConvertFToS:
+		case spv::OpConvertSToF:
+		case spv::OpConvertUToF:
+		case spv::OpBitcast:
+		case spv::OpIsInf:
+		case spv::OpIsNan:
+		case spv::OpDPdx:
+		case spv::OpDPdxCoarse:
+		case spv::OpDPdy:
+		case spv::OpDPdyCoarse:
+		case spv::OpFwidth:
+		case spv::OpFwidthCoarse:
+		case spv::OpDPdxFine:
+		case spv::OpDPdyFine:
+		case spv::OpFwidthFine:
+		case spv::OpQuantizeToF16:
+			return EmitUnaryOp(insn);
+
+		case spv::OpIAdd:
+		case spv::OpISub:
+		case spv::OpIMul:
+		case spv::OpSDiv:
+		case spv::OpUDiv:
+		case spv::OpFAdd:
+		case spv::OpFSub:
+		case spv::OpFMul:
+		case spv::OpFDiv:
+		case spv::OpFMod:
+		case spv::OpFRem:
+		case spv::OpFOrdEqual:
+		case spv::OpFUnordEqual:
+		case spv::OpFOrdNotEqual:
+		case spv::OpFUnordNotEqual:
+		case spv::OpFOrdLessThan:
+		case spv::OpFUnordLessThan:
+		case spv::OpFOrdGreaterThan:
+		case spv::OpFUnordGreaterThan:
+		case spv::OpFOrdLessThanEqual:
+		case spv::OpFUnordLessThanEqual:
+		case spv::OpFOrdGreaterThanEqual:
+		case spv::OpFUnordGreaterThanEqual:
+		case spv::OpSMod:
+		case spv::OpSRem:
+		case spv::OpUMod:
+		case spv::OpIEqual:
+		case spv::OpINotEqual:
+		case spv::OpUGreaterThan:
+		case spv::OpSGreaterThan:
+		case spv::OpUGreaterThanEqual:
+		case spv::OpSGreaterThanEqual:
+		case spv::OpULessThan:
+		case spv::OpSLessThan:
+		case spv::OpULessThanEqual:
+		case spv::OpSLessThanEqual:
+		case spv::OpShiftRightLogical:
+		case spv::OpShiftRightArithmetic:
+		case spv::OpShiftLeftLogical:
+		case spv::OpBitwiseOr:
+		case spv::OpBitwiseXor:
+		case spv::OpBitwiseAnd:
+		case spv::OpLogicalOr:
+		case spv::OpLogicalAnd:
+		case spv::OpLogicalEqual:
+		case spv::OpLogicalNotEqual:
+		case spv::OpUMulExtended:
+		case spv::OpSMulExtended:
+		case spv::OpIAddCarry:
+		case spv::OpISubBorrow:
+			return EmitBinaryOp(insn);
+
+		case spv::OpDot:
+		case spv::OpSDot:
+		case spv::OpUDot:
+		case spv::OpSUDot:
+		case spv::OpSDotAccSat:
+		case spv::OpUDotAccSat:
+		case spv::OpSUDotAccSat:
+			return EmitDot(insn);
+
+		case spv::OpSelect:
+			return EmitSelect(insn);
+
+		case spv::OpExtInst:
+			return EmitExtendedInstruction(insn);
+
+		case spv::OpAny:
+			return EmitAny(insn);
+
+		case spv::OpAll:
+			return EmitAll(insn);
+
+		case spv::OpPhi:
+			return EmitPhi(insn);
+
+		case spv::OpSelectionMerge:
+		case spv::OpLoopMerge:
+			return;
+
+		case spv::OpFunctionCall:
+			return EmitFunctionCall(insn);
+
+		case spv::OpDemoteToHelperInvocation:
+			return EmitDemoteToHelperInvocation(insn);
+
+		case spv::OpIsHelperInvocationEXT:
+			return EmitIsHelperInvocation(insn);
+
+		case spv::OpImageSampleImplicitLod:
+		case spv::OpImageSampleExplicitLod:
+		case spv::OpImageSampleDrefImplicitLod:
+		case spv::OpImageSampleDrefExplicitLod:
+		case spv::OpImageSampleProjImplicitLod:
+		case spv::OpImageSampleProjExplicitLod:
+		case spv::OpImageSampleProjDrefImplicitLod:
+		case spv::OpImageSampleProjDrefExplicitLod:
+		case spv::OpImageGather:
+		case spv::OpImageDrefGather:
+		case spv::OpImageFetch:
+		case spv::OpImageQueryLod:
+			return EmitImageSample(ImageInstruction(insn, shader, *this));
+
+		case spv::OpImageQuerySizeLod:
+			return EmitImageQuerySizeLod(insn);
+
+		case spv::OpImageQuerySize:
+			return EmitImageQuerySize(insn);
+
+		case spv::OpImageQueryLevels:
+			return EmitImageQueryLevels(insn);
+
+		case spv::OpImageQuerySamples:
+			return EmitImageQuerySamples(insn);
+
+		case spv::OpImageRead:
+			return EmitImageRead(ImageInstruction(insn, shader, *this));
+
+		case spv::OpImageWrite:
+			return EmitImageWrite(ImageInstruction(insn, shader, *this));
+
+		case spv::OpImageTexelPointer:
+			return EmitImageTexelPointer(ImageInstruction(insn, shader, *this));
+
+		case spv::OpSampledImage:
+			return EmitSampledImage(insn);
+
+		case spv::OpImage:
+			return EmitImage(insn);
+
+		case spv::OpCopyObject:
+		case spv::OpCopyLogical:
+			return EmitCopyObject(insn);
+
+		case spv::OpCopyMemory:
+			return EmitCopyMemory(insn);
+
+		case spv::OpControlBarrier:
+			return EmitControlBarrier(insn);
+
+		case spv::OpMemoryBarrier:
+			return EmitMemoryBarrier(insn);
+
+		case spv::OpGroupNonUniformElect:
+		case spv::OpGroupNonUniformAll:
+		case spv::OpGroupNonUniformAny:
+		case spv::OpGroupNonUniformAllEqual:
+		case spv::OpGroupNonUniformBroadcast:
+		case spv::OpGroupNonUniformBroadcastFirst:
+		case spv::OpGroupNonUniformQuadBroadcast:
+		case spv::OpGroupNonUniformQuadSwap:
+		case spv::OpGroupNonUniformBallot:
+		case spv::OpGroupNonUniformInverseBallot:
+		case spv::OpGroupNonUniformBallotBitExtract:
+		case spv::OpGroupNonUniformBallotBitCount:
+		case spv::OpGroupNonUniformBallotFindLSB:
+		case spv::OpGroupNonUniformBallotFindMSB:
+		case spv::OpGroupNonUniformShuffle:
+		case spv::OpGroupNonUniformShuffleXor:
+		case spv::OpGroupNonUniformShuffleUp:
+		case spv::OpGroupNonUniformShuffleDown:
+		case spv::OpGroupNonUniformIAdd:
+		case spv::OpGroupNonUniformFAdd:
+		case spv::OpGroupNonUniformIMul:
+		case spv::OpGroupNonUniformFMul:
+		case spv::OpGroupNonUniformSMin:
+		case spv::OpGroupNonUniformUMin:
+		case spv::OpGroupNonUniformFMin:
+		case spv::OpGroupNonUniformSMax:
+		case spv::OpGroupNonUniformUMax:
+		case spv::OpGroupNonUniformFMax:
+		case spv::OpGroupNonUniformBitwiseAnd:
+		case spv::OpGroupNonUniformBitwiseOr:
+		case spv::OpGroupNonUniformBitwiseXor:
+		case spv::OpGroupNonUniformLogicalAnd:
+		case spv::OpGroupNonUniformLogicalOr:
+		case spv::OpGroupNonUniformLogicalXor:
+			return EmitGroupNonUniform(insn);
+
+		case spv::OpArrayLength:
+			return EmitArrayLength(insn);
+
+		default:
+			UNREACHABLE("Unknown non-terminal instruction %s", shader.OpcodeName(opcode));
+			break;
+		}
+	}
 }
 
-EmitState::EmitResult EmitState::EmitAccessChain(InsnIterator insn)
+void EmitState::EmitAccessChain(InsnIterator insn)
 {
 	Type::ID typeId = insn.word(1);
 	Object::ID resultId = insn.word(2);
@@ -2317,11 +2322,9 @@
 		auto ptr = WalkAccessChain(baseId, elementId, Span(insn, indexId, insn.wordCount() - indexId), nonUniform);
 		createPointer(resultId, ptr);
 	}
-
-	return EmitResult::Continue;
 }
 
-EmitState::EmitResult EmitState::EmitCompositeConstruct(InsnIterator insn)
+void EmitState::EmitCompositeConstruct(InsnIterator insn)
 {
 	auto &type = shader.getType(insn.resultTypeId());
 	auto &dst = createIntermediate(insn.resultId(), type.componentCount);
@@ -2339,11 +2342,9 @@
 			dst.move(offset++, srcObjectAccess.Float(j));
 		}
 	}
-
-	return EmitResult::Continue;
 }
 
-EmitState::EmitResult EmitState::EmitCompositeInsert(InsnIterator insn)
+void EmitState::EmitCompositeInsert(InsnIterator insn)
 {
 	Type::ID resultTypeId = insn.word(1);
 	auto &type = shader.getType(resultTypeId);
@@ -2370,11 +2371,9 @@
 	{
 		dst.move(i, srcObjectAccess.Float(i));
 	}
-
-	return EmitResult::Continue;
 }
 
-EmitState::EmitResult EmitState::EmitCompositeExtract(InsnIterator insn)
+void EmitState::EmitCompositeExtract(InsnIterator insn)
 {
 	auto &type = shader.getType(insn.resultTypeId());
 	auto &dst = createIntermediate(insn.resultId(), type.componentCount);
@@ -2387,11 +2386,9 @@
 	{
 		dst.move(i, compositeObjectAccess.Float(firstComponent + i));
 	}
-
-	return EmitResult::Continue;
 }
 
-EmitState::EmitResult EmitState::EmitVectorShuffle(InsnIterator insn)
+void EmitState::EmitVectorShuffle(InsnIterator insn)
 {
 	// Note: number of components in result, first vector, and second vector are all independent.
 	uint32_t resultSize = shader.getType(insn.resultTypeId()).componentCount;
@@ -2417,11 +2414,9 @@
 			result.move(i, secondVector.Float(selector - firstVectorSize));
 		}
 	}
-
-	return EmitResult::Continue;
 }
 
-EmitState::EmitResult EmitState::EmitVectorExtractDynamic(InsnIterator insn)
+void EmitState::EmitVectorExtractDynamic(InsnIterator insn)
 {
 	auto &type = shader.getType(insn.resultTypeId());
 	auto &dst = createIntermediate(insn.resultId(), type.componentCount);
@@ -2438,10 +2433,9 @@
 	}
 
 	dst.move(0, v);
-	return EmitResult::Continue;
 }
 
-EmitState::EmitResult EmitState::EmitVectorInsertDynamic(InsnIterator insn)
+void EmitState::EmitVectorInsertDynamic(InsnIterator insn)
 {
 	auto &type = shader.getType(insn.resultTypeId());
 	auto &dst = createIntermediate(insn.resultId(), type.componentCount);
@@ -2455,10 +2449,9 @@
 		SIMD::UInt mask = CmpEQ(SIMD::UInt(i), index.UInt(0));
 		dst.move(i, (src.UInt(i) & ~mask) | (component.UInt(0) & mask));
 	}
-	return EmitResult::Continue;
 }
 
-EmitState::EmitResult EmitState::EmitSelect(InsnIterator insn)
+void EmitState::EmitSelect(InsnIterator insn)
 {
 	auto &type = shader.getType(insn.resultTypeId());
 	auto result = shader.getObject(insn.resultId());
@@ -2495,11 +2488,9 @@
 		SPIRV_SHADER_DBG("{0}: {1}", insn.word(4), lhs);
 		SPIRV_SHADER_DBG("{0}: {1}", insn.word(5), rhs);
 	}
-
-	return EmitResult::Continue;
 }
 
-EmitState::EmitResult EmitState::EmitAny(InsnIterator insn)
+void EmitState::EmitAny(InsnIterator insn)
 {
 	auto &type = shader.getType(insn.resultTypeId());
 	ASSERT(type.componentCount == 1);
@@ -2515,10 +2506,9 @@
 	}
 
 	dst.move(0, result);
-	return EmitResult::Continue;
 }
 
-EmitState::EmitResult EmitState::EmitAll(InsnIterator insn)
+void EmitState::EmitAll(InsnIterator insn)
 {
 	auto &type = shader.getType(insn.resultTypeId());
 	ASSERT(type.componentCount == 1);
@@ -2534,10 +2524,9 @@
 	}
 
 	dst.move(0, result);
-	return EmitResult::Continue;
 }
 
-EmitState::EmitResult EmitState::EmitAtomicOp(InsnIterator insn)
+void EmitState::EmitAtomicOp(InsnIterator insn)
 {
 	auto &resultType = shader.getType(Type::ID(insn.word(1)));
 	Object::ID resultId = insn.word(2);
@@ -2607,10 +2596,9 @@
 	}
 
 	dst.move(0, result);
-	return EmitResult::Continue;
 }
 
-EmitState::EmitResult EmitState::EmitAtomicCompareExchange(InsnIterator insn)
+void EmitState::EmitAtomicCompareExchange(InsnIterator insn)
 {
 	// Separate from EmitAtomicOp due to different instruction encoding
 	auto &resultType = shader.getType(Type::ID(insn.word(1)));
@@ -2640,10 +2628,9 @@
 	}
 
 	dst.move(0, x);
-	return EmitResult::Continue;
 }
 
-EmitState::EmitResult EmitState::EmitCopyObject(InsnIterator insn)
+void EmitState::EmitCopyObject(InsnIterator insn)
 {
 	auto src = Operand(shader, *this, insn.word(3));
 	if(src.isPointer())
@@ -2663,10 +2650,9 @@
 			dst.move(i, src.Int(i));
 		}
 	}
-	return EmitResult::Continue;
 }
 
-EmitState::EmitResult EmitState::EmitArrayLength(InsnIterator insn)
+void EmitState::EmitArrayLength(InsnIterator insn)
 {
 	auto structPtrId = Object::ID(insn.word(3));
 	auto arrayFieldIdx = insn.word(4);
@@ -2694,11 +2680,9 @@
 	auto arrayLength = arraySizeInBytes / SIMD::Int(arrayDecorations.ArrayStride);
 
 	result.move(0, SIMD::Int(arrayLength));
-
-	return EmitResult::Continue;
 }
 
-EmitState::EmitResult EmitState::EmitExtendedInstruction(InsnIterator insn)
+void EmitState::EmitExtendedInstruction(InsnIterator insn)
 {
 	auto ext = shader.getExtension(insn.word(3));
 	switch(ext.name)
@@ -2713,7 +2697,6 @@
 	default:
 		UNREACHABLE("Unknown Extension::Name<%d>", int(ext.name));
 	}
-	return EmitResult::Continue;
 }
 
 uint32_t SpirvShader::GetConstScalarInt(Object::ID id) const
@@ -2721,6 +2704,7 @@
 	auto &scopeObj = getObject(id);
 	ASSERT(scopeObj.kind == Object::Kind::Constant);
 	ASSERT(getType(scopeObj).componentCount == 1);
+
 	return scopeObj.constantValue[0];
 }
 
diff --git a/src/Pipeline/SpirvShader.hpp b/src/Pipeline/SpirvShader.hpp
index 740f6ba..478e1e7 100644
--- a/src/Pipeline/SpirvShader.hpp
+++ b/src/Pipeline/SpirvShader.hpp
@@ -962,22 +962,23 @@
 	void WriteCFGGraphVizDotFile(const char *path) const;
 
 	// OpcodeName() returns the name of the opcode op.
-	static const char *OpcodeName(spv::Op op);
+	static const char *OpcodeName(spv::Op opcode);
 	static std::memory_order MemoryOrder(spv::MemorySemanticsMask memorySemantics);
 
 	// IsStatement() returns true if the given opcode actually performs
 	// work (as opposed to declaring a type, defining a function start / end,
 	// etc).
-	static bool IsStatement(spv::Op op);
+	static bool IsStatement(spv::Op opcode);
 
 	// HasTypeAndResult() returns true if the given opcode's instruction
 	// has a result type ID and result ID, i.e. defines an Object.
-	static bool HasTypeAndResult(spv::Op op);
+	static bool HasTypeAndResult(spv::Op opcode);
 
 	// Returns 0 when invalid.
 	static VkShaderStageFlagBits executionModelToStage(spv::ExecutionModel model);
 
 	static bool IsExplicitLayout(spv::StorageClass storageClass);
+	static bool IsTerminator(spv::Op opcode);
 };
 
 class EmitState
@@ -1304,66 +1305,59 @@
 		return isSampledImage(id) ? getSampledImage(id) : getPointer(id);
 	}
 
-	// EmitResult is an enumerator of result values from the Emit functions.
-	enum class EmitResult
-	{
-		Continue,    // No termination instructions.
-		Terminator,  // Reached a termination instruction.
-	};
-
-	EmitResult EmitVariable(InsnIterator insn);
-	EmitResult EmitLoad(InsnIterator insn);
-	EmitResult EmitStore(InsnIterator insn);
-	EmitResult EmitAccessChain(InsnIterator insn);
-	EmitResult EmitCompositeConstruct(InsnIterator insn);
-	EmitResult EmitCompositeInsert(InsnIterator insn);
-	EmitResult EmitCompositeExtract(InsnIterator insn);
-	EmitResult EmitVectorShuffle(InsnIterator insn);
-	EmitResult EmitVectorTimesScalar(InsnIterator insn);
-	EmitResult EmitMatrixTimesVector(InsnIterator insn);
-	EmitResult EmitVectorTimesMatrix(InsnIterator insn);
-	EmitResult EmitMatrixTimesMatrix(InsnIterator insn);
-	EmitResult EmitOuterProduct(InsnIterator insn);
-	EmitResult EmitTranspose(InsnIterator insn);
-	EmitResult EmitVectorExtractDynamic(InsnIterator insn);
-	EmitResult EmitVectorInsertDynamic(InsnIterator insn);
-	EmitResult EmitUnaryOp(InsnIterator insn);
-	EmitResult EmitBinaryOp(InsnIterator insn);
-	EmitResult EmitDot(InsnIterator insn);
-	EmitResult EmitSelect(InsnIterator insn);
-	EmitResult EmitExtendedInstruction(InsnIterator insn);
-	EmitResult EmitExtGLSLstd450(InsnIterator insn);
-	EmitResult EmitAny(InsnIterator insn);
-	EmitResult EmitAll(InsnIterator insn);
-	EmitResult EmitBranch(InsnIterator insn);
-	EmitResult EmitBranchConditional(InsnIterator insn);
-	EmitResult EmitSwitch(InsnIterator insn);
-	EmitResult EmitUnreachable(InsnIterator insn);
-	EmitResult EmitReturn(InsnIterator insn);
-	EmitResult EmitTerminateInvocation(InsnIterator insn);
-	EmitResult EmitDemoteToHelperInvocation(InsnIterator insn);
-	EmitResult EmitIsHelperInvocation(InsnIterator insn);
-	EmitResult EmitFunctionCall(InsnIterator insn);
-	EmitResult EmitPhi(InsnIterator insn);
-	EmitResult EmitImageSample(const ImageInstruction &instruction);
-	EmitResult EmitImageQuerySizeLod(InsnIterator insn);
-	EmitResult EmitImageQuerySize(InsnIterator insn);
-	EmitResult EmitImageQueryLevels(InsnIterator insn);
-	EmitResult EmitImageQuerySamples(InsnIterator insn);
-	EmitResult EmitImageRead(const ImageInstruction &instruction);
-	EmitResult EmitImageWrite(const ImageInstruction &instruction);
-	EmitResult EmitImageTexelPointer(const ImageInstruction &instruction);
-	EmitResult EmitAtomicOp(InsnIterator insn);
-	EmitResult EmitAtomicCompareExchange(InsnIterator insn);
-	EmitResult EmitSampledImage(InsnIterator insn);
-	EmitResult EmitImage(InsnIterator insn);
-	EmitResult EmitCopyObject(InsnIterator insn);
-	EmitResult EmitCopyMemory(InsnIterator insn);
-	EmitResult EmitControlBarrier(InsnIterator insn);
-	EmitResult EmitMemoryBarrier(InsnIterator insn);
-	EmitResult EmitGroupNonUniform(InsnIterator insn);
-	EmitResult EmitArrayLength(InsnIterator insn);
-	EmitResult EmitBitcastPointer(Object::ID resultID, Operand &src);
+	void EmitVariable(InsnIterator insn);
+	void EmitLoad(InsnIterator insn);
+	void EmitStore(InsnIterator insn);
+	void EmitAccessChain(InsnIterator insn);
+	void EmitCompositeConstruct(InsnIterator insn);
+	void EmitCompositeInsert(InsnIterator insn);
+	void EmitCompositeExtract(InsnIterator insn);
+	void EmitVectorShuffle(InsnIterator insn);
+	void EmitVectorTimesScalar(InsnIterator insn);
+	void EmitMatrixTimesVector(InsnIterator insn);
+	void EmitVectorTimesMatrix(InsnIterator insn);
+	void EmitMatrixTimesMatrix(InsnIterator insn);
+	void EmitOuterProduct(InsnIterator insn);
+	void EmitTranspose(InsnIterator insn);
+	void EmitVectorExtractDynamic(InsnIterator insn);
+	void EmitVectorInsertDynamic(InsnIterator insn);
+	void EmitUnaryOp(InsnIterator insn);
+	void EmitBinaryOp(InsnIterator insn);
+	void EmitDot(InsnIterator insn);
+	void EmitSelect(InsnIterator insn);
+	void EmitExtendedInstruction(InsnIterator insn);
+	void EmitExtGLSLstd450(InsnIterator insn);
+	void EmitAny(InsnIterator insn);
+	void EmitAll(InsnIterator insn);
+	void EmitBranch(InsnIterator insn);
+	void EmitBranchConditional(InsnIterator insn);
+	void EmitSwitch(InsnIterator insn);
+	void EmitUnreachable(InsnIterator insn);
+	void EmitReturn(InsnIterator insn);
+	void EmitTerminateInvocation(InsnIterator insn);
+	void EmitDemoteToHelperInvocation(InsnIterator insn);
+	void EmitIsHelperInvocation(InsnIterator insn);
+	void EmitFunctionCall(InsnIterator insn);
+	void EmitPhi(InsnIterator insn);
+	void EmitImageSample(const ImageInstruction &instruction);
+	void EmitImageQuerySizeLod(InsnIterator insn);
+	void EmitImageQuerySize(InsnIterator insn);
+	void EmitImageQueryLevels(InsnIterator insn);
+	void EmitImageQuerySamples(InsnIterator insn);
+	void EmitImageRead(const ImageInstruction &instruction);
+	void EmitImageWrite(const ImageInstruction &instruction);
+	void EmitImageTexelPointer(const ImageInstruction &instruction);
+	void EmitAtomicOp(InsnIterator insn);
+	void EmitAtomicCompareExchange(InsnIterator insn);
+	void EmitSampledImage(InsnIterator insn);
+	void EmitImage(InsnIterator insn);
+	void EmitCopyObject(InsnIterator insn);
+	void EmitCopyMemory(InsnIterator insn);
+	void EmitControlBarrier(InsnIterator insn);
+	void EmitMemoryBarrier(InsnIterator insn);
+	void EmitGroupNonUniform(InsnIterator insn);
+	void EmitArrayLength(InsnIterator insn);
+	void EmitBitcastPointer(Object::ID resultID, Operand &src);
 
 	enum InterpolationType
 	{
@@ -1474,7 +1468,7 @@
 	void EmitLoop();
 
 	void EmitInstructions(InsnIterator begin, InsnIterator end);
-	EmitResult EmitInstruction(InsnIterator insn);
+	void EmitInstruction(InsnIterator insn);
 
 	// Helper for implementing OpStore, which doesn't take an InsnIterator so it
 	// can also store independent operands.
diff --git a/src/Pipeline/SpirvShaderArithmetic.cpp b/src/Pipeline/SpirvShaderArithmetic.cpp
index 43d652f..613b0db 100644
--- a/src/Pipeline/SpirvShaderArithmetic.cpp
+++ b/src/Pipeline/SpirvShaderArithmetic.cpp
@@ -23,7 +23,7 @@
 
 namespace sw {
 
-EmitState::EmitResult EmitState::EmitVectorTimesScalar(SpirvShader::InsnIterator insn)
+void EmitState::EmitVectorTimesScalar(SpirvShader::InsnIterator insn)
 {
 	auto &type = shader.getType(insn.resultTypeId());
 	auto &dst = createIntermediate(insn.resultId(), type.componentCount);
@@ -34,11 +34,9 @@
 	{
 		dst.move(i, lhs.Float(i) * rhs.Float(0));
 	}
-
-	return EmitResult::Continue;
 }
 
-EmitState::EmitResult EmitState::EmitState::EmitMatrixTimesVector(SpirvShader::InsnIterator insn)
+void EmitState::EmitState::EmitMatrixTimesVector(SpirvShader::InsnIterator insn)
 {
 	auto &type = shader.getType(insn.resultTypeId());
 	auto &dst = createIntermediate(insn.resultId(), type.componentCount);
@@ -54,11 +52,9 @@
 		}
 		dst.move(i, v);
 	}
-
-	return EmitResult::Continue;
 }
 
-EmitState::EmitResult EmitState::EmitVectorTimesMatrix(SpirvShader::InsnIterator insn)
+void EmitState::EmitVectorTimesMatrix(SpirvShader::InsnIterator insn)
 {
 	auto &type = shader.getType(insn.resultTypeId());
 	auto &dst = createIntermediate(insn.resultId(), type.componentCount);
@@ -74,11 +70,9 @@
 		}
 		dst.move(i, v);
 	}
-
-	return EmitResult::Continue;
 }
 
-EmitState::EmitResult EmitState::EmitMatrixTimesMatrix(SpirvShader::InsnIterator insn)
+void EmitState::EmitMatrixTimesMatrix(SpirvShader::InsnIterator insn)
 {
 	auto &type = shader.getType(insn.resultTypeId());
 	auto &dst = createIntermediate(insn.resultId(), type.componentCount);
@@ -101,11 +95,9 @@
 			dst.move(numRows * col + row, v);
 		}
 	}
-
-	return EmitResult::Continue;
 }
 
-EmitState::EmitResult EmitState::EmitOuterProduct(SpirvShader::InsnIterator insn)
+void EmitState::EmitOuterProduct(SpirvShader::InsnIterator insn)
 {
 	auto &type = shader.getType(insn.resultTypeId());
 	auto &dst = createIntermediate(insn.resultId(), type.componentCount);
@@ -122,11 +114,9 @@
 			dst.move(col * numRows + row, lhs.Float(row) * rhs.Float(col));
 		}
 	}
-
-	return EmitResult::Continue;
 }
 
-EmitState::EmitResult EmitState::EmitState::EmitTranspose(SpirvShader::InsnIterator insn)
+void EmitState::EmitState::EmitTranspose(SpirvShader::InsnIterator insn)
 {
 	auto &type = shader.getType(insn.resultTypeId());
 	auto &dst = createIntermediate(insn.resultId(), type.componentCount);
@@ -142,11 +132,9 @@
 			dst.move(col * numRows + row, mat.Float(row * numCols + col));
 		}
 	}
-
-	return EmitResult::Continue;
 }
 
-EmitState::EmitResult EmitState::EmitBitcastPointer(SpirvShader::Object::ID resultID, Operand &src)
+void EmitState::EmitBitcastPointer(SpirvShader::Object::ID resultID, Operand &src)
 {
 	if(src.isPointer())  // Pointer -> Integer bits
 	{
@@ -184,11 +172,9 @@
 			createPointer(resultID, SIMD::Pointer(src.UInt(0), src.UInt(1)));
 		}
 	}
-
-	return EmitResult::Continue;
 }
 
-EmitState::EmitResult EmitState::EmitUnaryOp(SpirvShader::InsnIterator insn)
+void EmitState::EmitUnaryOp(SpirvShader::InsnIterator insn)
 {
 	auto &type = shader.getType(insn.resultTypeId());
 	auto src = Operand(shader, *this, insn.word(3));
@@ -357,11 +343,9 @@
 			UNREACHABLE("%s", shader.OpcodeName(insn.opcode()));
 		}
 	}
-
-	return EmitResult::Continue;
 }
 
-EmitState::EmitResult EmitState::EmitBinaryOp(SpirvShader::InsnIterator insn)
+void EmitState::EmitBinaryOp(SpirvShader::InsnIterator insn)
 {
 	auto &type = shader.getType(insn.resultTypeId());
 	auto &dst = createIntermediate(insn.resultId(), type.componentCount);
@@ -570,11 +554,9 @@
 	SPIRV_SHADER_DBG("{0}: {1}", insn.word(2), dst);
 	SPIRV_SHADER_DBG("{0}: {1}", insn.word(3), lhs);
 	SPIRV_SHADER_DBG("{0}: {1}", insn.word(4), rhs);
-
-	return EmitResult::Continue;
 }
 
-EmitState::EmitResult EmitState::EmitDot(SpirvShader::InsnIterator insn)
+void EmitState::EmitDot(SpirvShader::InsnIterator insn)
 {
 	auto &type = shader.getType(insn.resultTypeId());
 	ASSERT(type.componentCount == 1);
@@ -624,8 +606,6 @@
 	SPIRV_SHADER_DBG("{0}: {1}", insn.resultId(), dst);
 	SPIRV_SHADER_DBG("{0}: {1}", insn.word(3), lhs);
 	SPIRV_SHADER_DBG("{0}: {1}", insn.word(4), rhs);
-
-	return EmitResult::Continue;
 }
 
 SIMD::Float EmitState::FDot(unsigned numComponents, const Operand &x, const Operand &y)
diff --git a/src/Pipeline/SpirvShaderControlFlow.cpp b/src/Pipeline/SpirvShaderControlFlow.cpp
index dc17a71..bfe93ae 100644
--- a/src/Pipeline/SpirvShaderControlFlow.cpp
+++ b/src/Pipeline/SpirvShaderControlFlow.cpp
@@ -429,6 +429,7 @@
 	{
 		auto edge = Block::Edge{ in, mergeBlockId };
 		auto it = edgeActiveLaneMasks.find(edge);
+
 		if(it != edgeActiveLaneMasks.end())
 		{
 			mergeActiveLaneMasks[in] |= it->second;
@@ -486,20 +487,20 @@
 	// Continue emitting from the merge block.
 	Nucleus::setInsertBlock(mergeBasicBlock);
 	pending->push_back(mergeBlockId);
+
 	for(const auto &it : mergeActiveLaneMasks)
 	{
 		addActiveLaneMaskEdge(it.first, mergeBlockId, it.second);
 	}
 }
 
-EmitState::EmitResult EmitState::EmitBranch(InsnIterator insn)
+void EmitState::EmitBranch(InsnIterator insn)
 {
 	auto target = Block::ID(insn.word(1));
 	addActiveLaneMaskEdge(block, target, activeLaneMask());
-	return EmitResult::Terminator;
 }
 
-EmitState::EmitResult EmitState::EmitBranchConditional(InsnIterator insn)
+void EmitState::EmitBranchConditional(InsnIterator insn)
 {
 	auto &function = shader.getFunction(this->function);
 	auto block = function.getBlock(this->block);
@@ -516,11 +517,9 @@
 
 	addOutputActiveLaneMaskEdge(trueBlockId, cond.Int(0));
 	addOutputActiveLaneMaskEdge(falseBlockId, ~cond.Int(0));
-
-	return EmitResult::Terminator;
 }
 
-EmitState::EmitResult EmitState::EmitSwitch(InsnIterator insn)
+void EmitState::EmitSwitch(InsnIterator insn)
 {
 	auto &function = shader.getFunction(this->function);
 	auto block = function.getBlock(this->block);
@@ -541,6 +540,7 @@
 	// Gather up the case label matches and calculate defaultLaneMask.
 	std::vector<RValue<SIMD::Int>> caseLabelMatches;
 	caseLabelMatches.reserve(numCases);
+
 	for(uint32_t i = 0; i < numCases; i++)
 	{
 		auto label = block.branchInstruction.word(i * 2 + 3);
@@ -554,47 +554,40 @@
 	auto defaultBlockId = Block::ID(block.branchInstruction.word(2));
 	SPIRV_SHADER_DBG("default: {0}", defaultLaneMask);
 	addOutputActiveLaneMaskEdge(defaultBlockId, defaultLaneMask);
-
-	return EmitResult::Terminator;
 }
 
-EmitState::EmitResult EmitState::EmitUnreachable(InsnIterator insn)
+void EmitState::EmitUnreachable(InsnIterator insn)
 {
 	// TODO: Log something in this case?
 	SetActiveLaneMask(SIMD::Int(0));
-	return EmitResult::Terminator;
 }
 
-EmitState::EmitResult EmitState::EmitReturn(InsnIterator insn)
+void EmitState::EmitReturn(InsnIterator insn)
 {
 	SetActiveLaneMask(SIMD::Int(0));
-	return EmitResult::Terminator;
 }
 
-EmitState::EmitResult EmitState::EmitTerminateInvocation(InsnIterator insn)
+void EmitState::EmitTerminateInvocation(InsnIterator insn)
 {
 	routine->discardMask |= SignMask(activeLaneMask());
 	SetActiveLaneMask(SIMD::Int(0));
-	return EmitResult::Terminator;
 }
 
-EmitState::EmitResult EmitState::EmitDemoteToHelperInvocation(InsnIterator insn)
+void EmitState::EmitDemoteToHelperInvocation(InsnIterator insn)
 {
 	routine->helperInvocation |= activeLaneMask();
 	routine->discardMask |= SignMask(activeLaneMask());
 	SetStoresAndAtomicsMask(storesAndAtomicsMask() & ~activeLaneMask());
-	return EmitResult::Continue;
 }
 
-EmitState::EmitResult EmitState::EmitIsHelperInvocation(InsnIterator insn)
+void EmitState::EmitIsHelperInvocation(InsnIterator insn)
 {
 	auto &type = shader.getType(insn.resultTypeId());
 	auto &dst = createIntermediate(insn.resultId(), type.componentCount);
 	dst.move(0, routine->helperInvocation);
-	return EmitResult::Continue;
 }
 
-EmitState::EmitResult EmitState::EmitFunctionCall(InsnIterator insn)
+void EmitState::EmitFunctionCall(InsnIterator insn)
 {
 	auto functionId = SpirvShader::Function::ID(insn.word(3));
 	const auto &functionIt = shader.functions.find(functionId);
@@ -615,13 +608,11 @@
 			if(insnNumber > 1)
 			{
 				UNIMPLEMENTED("b/141246700: Function block number of instructions: %d", insnNumber);  // FIXME(b/141246700)
-				return EmitResult::Continue;
 			}
 
 			if(blockInsn.opcode() != wrapOpKill[insnNumber++])
 			{
 				UNIMPLEMENTED("b/141246700: Function block instruction %d : %s", insnNumber - 1, shader.OpcodeName(blockInsn.opcode()));  // FIXME(b/141246700)
-				return EmitResult::Continue;
 			}
 
 			if(blockInsn.opcode() == spv::OpKill)
@@ -630,11 +621,9 @@
 			}
 		}
 	}
-
-	return EmitResult::Continue;
 }
 
-EmitState::EmitResult EmitState::EmitControlBarrier(InsnIterator insn)
+void EmitState::EmitControlBarrier(InsnIterator insn)
 {
 	auto executionScope = spv::Scope(shader.GetConstScalarInt(insn.word(1)));
 	auto semantics = spv::MemorySemanticsMask(shader.GetConstScalarInt(insn.word(3)));
@@ -654,14 +643,13 @@
 		UNREACHABLE("Scope for execution must be limited to Workgroup or Subgroup");
 		break;
 	}
-
-	return EmitResult::Continue;
 }
 
-EmitState::EmitResult EmitState::EmitPhi(InsnIterator insn)
+void EmitState::EmitPhi(InsnIterator insn)
 {
 	auto &function = shader.getFunction(this->function);
 	auto currentBlock = function.getBlock(block);
+
 	if(!currentBlock.isLoopMerge)
 	{
 		// If this is a loop merge block, then don't attempt to update the
@@ -669,8 +657,8 @@
 		// of this phi in order to correctly deal with divergent lanes.
 		StorePhi(block, insn, currentBlock.ins);
 	}
+
 	LoadPhi(insn);
-	return EmitResult::Continue;
 }
 
 void EmitState::LoadPhi(InsnIterator insn)
@@ -684,6 +672,7 @@
 	const auto &storage = storageIt->second;
 
 	auto &dst = createIntermediate(objectId, type.componentCount);
+
 	for(uint32_t i = 0; i < type.componentCount; i++)
 	{
 		dst.move(i, storage[i]);
diff --git a/src/Pipeline/SpirvShaderGLSLstd450.cpp b/src/Pipeline/SpirvShaderGLSLstd450.cpp
index 1205668..3c70888 100644
--- a/src/Pipeline/SpirvShaderGLSLstd450.cpp
+++ b/src/Pipeline/SpirvShaderGLSLstd450.cpp
@@ -25,7 +25,7 @@
 
 static constexpr float PI = 3.141592653589793f;
 
-EmitState::EmitResult EmitState::EmitExtGLSLstd450(SpirvShader::InsnIterator insn)
+void EmitState::EmitExtGLSLstd450(SpirvShader::InsnIterator insn)
 {
 	auto &type = shader.getType(insn.resultTypeId());
 	auto &dst = createIntermediate(insn.resultId(), type.componentCount);
@@ -955,8 +955,6 @@
 		UNREACHABLE("ExtInst %d", int(extInstIndex));
 		break;
 	}
-
-	return EmitResult::Continue;
 }
 
 static SIMD::Float Interpolate(const SIMD::Float &x, const SIMD::Float &y, const SIMD::Float &rhw,
diff --git a/src/Pipeline/SpirvShaderGroup.cpp b/src/Pipeline/SpirvShaderGroup.cpp
index e298ade..64bc5e4 100644
--- a/src/Pipeline/SpirvShaderGroup.cpp
+++ b/src/Pipeline/SpirvShaderGroup.cpp
@@ -64,7 +64,7 @@
 	}
 }
 
-EmitState::EmitResult EmitState::EmitGroupNonUniform(InsnIterator insn)
+void EmitState::EmitGroupNonUniform(InsnIterator insn)
 {
 	ASSERT(SIMD::Width == 4);  // EmitGroupNonUniform makes many assumptions that the SIMD vector width is 4
 
@@ -505,7 +505,6 @@
 		}
 		break;
 	}
-	return EmitResult::Continue;
 }
 
 }  // namespace sw
diff --git a/src/Pipeline/SpirvShaderImage.cpp b/src/Pipeline/SpirvShaderImage.cpp
index 648929b..5b64307 100644
--- a/src/Pipeline/SpirvShaderImage.cpp
+++ b/src/Pipeline/SpirvShaderImage.cpp
@@ -329,7 +329,7 @@
 	return (operandsIndex != 0) ? insn.word(operandsIndex) : 0;
 }
 
-EmitState::EmitResult EmitState::EmitImageSample(const ImageInstruction &instruction)
+void EmitState::EmitImageSample(const ImageInstruction &instruction)
 {
 	auto &resultType = shader.getType(instruction.resultTypeId);
 	auto &result = createIntermediate(instruction.resultId, resultType.componentCount);
@@ -344,8 +344,6 @@
 	}
 
 	for(auto i = 0u; i < resultType.componentCount; i++) { result.move(i, out[i]); }
-
-	return EmitResult::Continue;
 }
 
 void EmitState::EmitImageSampleUnconditional(Array<SIMD::Float> &out, const ImageInstruction &instruction) const
@@ -502,7 +500,7 @@
 	Call<ImageSampler>(samplerFunction, texture, &in, &out, routine->constants);
 }
 
-EmitState::EmitResult EmitState::EmitImageQuerySizeLod(InsnIterator insn)
+void EmitState::EmitImageQuerySizeLod(InsnIterator insn)
 {
 	auto &resultTy = shader.getType(insn.resultTypeId());
 	auto imageId = Object::ID(insn.word(3));
@@ -510,11 +508,9 @@
 
 	auto &dst = createIntermediate(insn.resultId(), resultTy.componentCount);
 	GetImageDimensions(resultTy, imageId, lodId, dst);
-
-	return EmitResult::Continue;
 }
 
-EmitState::EmitResult EmitState::EmitImageQuerySize(InsnIterator insn)
+void EmitState::EmitImageQuerySize(InsnIterator insn)
 {
 	auto &resultTy = shader.getType(insn.resultTypeId());
 	auto imageId = Object::ID(insn.word(3));
@@ -522,8 +518,6 @@
 
 	auto &dst = createIntermediate(insn.resultId(), resultTy.componentCount);
 	GetImageDimensions(resultTy, imageId, lodId, dst);
-
-	return EmitResult::Continue;
 }
 
 void EmitState::GetImageDimensions(const Type &resultTy, Object::ID imageId, Object::ID lodId, Intermediate &dst) const
@@ -588,7 +582,7 @@
 	}
 }
 
-EmitState::EmitResult EmitState::EmitImageQueryLevels(InsnIterator insn)
+void EmitState::EmitImageQueryLevels(InsnIterator insn)
 {
 	auto &resultTy = shader.getType(insn.resultTypeId());
 	ASSERT(resultTy.componentCount == 1);
@@ -612,11 +606,9 @@
 
 	auto &dst = createIntermediate(insn.resultId(), 1);
 	dst.move(0, SIMD::Int(mipLevels));
-
-	return EmitResult::Continue;
 }
 
-EmitState::EmitResult EmitState::EmitImageQuerySamples(InsnIterator insn)
+void EmitState::EmitImageQuerySamples(InsnIterator insn)
 {
 	auto &resultTy = shader.getType(insn.resultTypeId());
 	ASSERT(resultTy.componentCount == 1);
@@ -647,8 +639,6 @@
 
 	auto &dst = createIntermediate(insn.resultId(), 1);
 	dst.move(0, SIMD::Int(sampleCount));
-
-	return EmitResult::Continue;
 }
 
 EmitState::TexelAddressData EmitState::setupTexelAddressData(SIMD::Int rowPitch, SIMD::Int slicePitch, SIMD::Int samplePitch, ImageInstructionSignature instruction, SIMD::Int coordinate[], SIMD::Int sample, vk::Format imageFormat, const SpirvRoutine *routine)
@@ -830,7 +820,7 @@
 	return SIMD::Pointer(imageBase, imageSizeInBytes, texelData.ptrOffset);
 }
 
-EmitState::EmitResult EmitState::EmitImageRead(const ImageInstruction &instruction)
+void EmitState::EmitImageRead(const ImageInstruction &instruction)
 {
 	auto &resultType = shader.getObjectType(instruction.resultId);
 	auto &image = shader.getObject(instruction.imageId);
@@ -1241,11 +1231,9 @@
 		UNSUPPORTED("VkFormat %d", int(imageFormat));
 		break;
 	}
-
-	return EmitResult::Continue;
 }
 
-EmitState::EmitResult EmitState::EmitImageWrite(const ImageInstruction &instruction)
+void EmitState::EmitImageWrite(const ImageInstruction &instruction)
 {
 	auto &image = shader.getObject(instruction.imageId);
 	auto &imageType = shader.getType(image);
@@ -1317,8 +1305,6 @@
 			}
 		}
 	}
-
-	return EmitResult::Continue;
 }
 
 void EmitState::WriteImage(ImageInstructionSignature instruction, Pointer<Byte> descriptor, const Pointer<SIMD::Int> &coord, const Pointer<SIMD::Int> &texelAndMask, vk::Format imageFormat)
@@ -1551,7 +1537,7 @@
 		UNREACHABLE("texelSize: %d", int(texelSize));
 }
 
-EmitState::EmitResult EmitState::EmitImageTexelPointer(const ImageInstruction &instruction)
+void EmitState::EmitImageTexelPointer(const ImageInstruction &instruction)
 {
 	auto coordinate = Operand(shader, *this, instruction.coordinateId);
 
@@ -1576,11 +1562,9 @@
 	                    : GetNonUniformTexelAddress(instruction, ptr, uvwa, sample, imageFormat, robustness, activeLaneMask(), routine);
 
 	createPointer(instruction.resultId, texelPtr);
-
-	return EmitResult::Continue;
 }
 
-EmitState::EmitResult EmitState::EmitSampledImage(InsnIterator insn)
+void EmitState::EmitSampledImage(InsnIterator insn)
 {
 	Object::ID resultId = insn.word(2);
 	Object::ID imageId = insn.word(3);
@@ -1588,19 +1572,15 @@
 
 	// Create a sampled image, containing both a sampler and an image
 	createSampledImage(resultId, { getPointer(imageId), samplerId });
-
-	return EmitResult::Continue;
 }
 
-EmitState::EmitResult EmitState::EmitImage(InsnIterator insn)
+void EmitState::EmitImage(InsnIterator insn)
 {
 	Object::ID resultId = insn.word(2);
 	Object::ID imageId = insn.word(3);
 
 	// Extract the image from a sampled image.
 	createPointer(resultId, getImage(imageId));
-
-	return EmitResult::Continue;
 }
 
 }  // namespace sw
diff --git a/src/Pipeline/SpirvShaderInstructions.cpp b/src/Pipeline/SpirvShaderInstructions.cpp
index a189258..7675665 100644
--- a/src/Pipeline/SpirvShaderInstructions.cpp
+++ b/src/Pipeline/SpirvShaderInstructions.cpp
@@ -20,15 +20,15 @@
 
 namespace sw {
 
-const char *SpirvShader::OpcodeName(spv::Op op)
+const char *SpirvShader::OpcodeName(spv::Op opcode)
 {
-	return spvOpcodeString(op);
+	return spvOpcodeString(opcode);
 }
 
 // This function is used by the shader debugger to determine whether an instruction is steppable.
-bool SpirvShader::IsStatement(spv::Op op)
+bool SpirvShader::IsStatement(spv::Op opcode)
 {
-	switch(op)
+	switch(opcode)
 	{
 	default:
 		// Most statement-like instructions produce a result which has a type.
@@ -36,7 +36,7 @@
 		{
 			bool hasResult = false;
 			bool hasResultType = false;
-			spv::HasResultAndType(op, &hasResult, &hasResultType);
+			spv::HasResultAndType(opcode, &hasResult, &hasResultType);
 
 			return hasResult && hasResultType;
 		}
@@ -91,4 +91,24 @@
 	}
 }
 
+bool SpirvShader::IsTerminator(spv::Op opcode)
+{
+	switch(opcode)
+	{
+	// Branch instructions
+	case spv::OpBranch:
+	case spv::OpBranchConditional:
+	case spv::OpSwitch:
+	// Function termination instructions
+	case spv::OpReturn:
+	case spv::OpReturnValue:
+	case spv::OpKill:
+	case spv::OpUnreachable:
+	case spv::OpTerminateInvocation:
+		return true;
+	default:
+		return false;
+	}
+}
+
 }  // namespace sw
\ No newline at end of file
diff --git a/src/Pipeline/SpirvShaderMemory.cpp b/src/Pipeline/SpirvShaderMemory.cpp
index ea46052..666afa4 100644
--- a/src/Pipeline/SpirvShaderMemory.cpp
+++ b/src/Pipeline/SpirvShaderMemory.cpp
@@ -23,7 +23,7 @@
 
 namespace sw {
 
-EmitState::EmitResult EmitState::EmitLoad(InsnIterator insn)
+void EmitState::EmitLoad(InsnIterator insn)
 {
 	bool atomic = (insn.opcode() == spv::OpAtomicLoad);
 	Object::ID resultId = insn.word(2);
@@ -43,7 +43,6 @@
 		// Just propagate the pointer.
 		auto &ptr = getPointer(pointerId);
 		createPointer(resultId, ptr);
-		return EmitResult::Continue;
 	}
 
 	if(atomic)
@@ -76,11 +75,9 @@
 
 		SPIRV_SHADER_DBG("Load(atomic: {0}, order: {1}, ptr: {2}, val: {3}, mask: {4})", atomic, int(memoryOrder), ptr, dst, activeLaneMask());
 	}
-
-	return EmitResult::Continue;
 }
 
-EmitState::EmitResult EmitState::EmitStore(InsnIterator insn)
+void EmitState::EmitStore(InsnIterator insn)
 {
 	bool atomic = (insn.opcode() == spv::OpAtomicStore);
 	Object::ID pointerId = insn.word(1);
@@ -97,8 +94,6 @@
 	const auto &value = Operand(shader, *this, objectId);
 
 	Store(pointerId, value, atomic, memoryOrder);
-
-	return EmitResult::Continue;
 }
 
 void EmitState::Store(Object::ID pointerId, const Operand &value, bool atomic, std::memory_order memoryOrder) const
@@ -137,7 +132,7 @@
 	}
 }
 
-EmitState::EmitResult EmitState::EmitVariable(InsnIterator insn)
+void EmitState::EmitVariable(InsnIterator insn)
 {
 	Object::ID resultId = insn.word(2);
 	auto &object = shader.getObject(resultId);
@@ -262,11 +257,9 @@
 			ASSERT_MSG(initializerId == 0, "Vulkan does not permit variables of storage class %d to have initializers", int(objectTy.storageClass));
 		}
 	}
-
-	return EmitResult::Continue;
 }
 
-EmitState::EmitResult EmitState::EmitCopyMemory(InsnIterator insn)
+void EmitState::EmitCopyMemory(InsnIterator insn)
 {
 	Object::ID dstPtrId = insn.word(1);
 	Object::ID srcPtrId = insn.word(2);
@@ -296,16 +289,14 @@
 		auto value = src.Load<SIMD::Float>(robustness, activeLaneMask());
 		dst.Store(value, robustness, activeLaneMask());
 	});
-	return EmitResult::Continue;
 }
 
-EmitState::EmitResult EmitState::EmitMemoryBarrier(InsnIterator insn)
+void EmitState::EmitMemoryBarrier(InsnIterator insn)
 {
 	auto semantics = spv::MemorySemanticsMask(shader.GetConstScalarInt(insn.word(2)));
 	// TODO(b/176819536): We probably want to consider the memory scope here.
 	// For now, just always emit the full fence.
 	Fence(semantics);
-	return EmitResult::Continue;
 }
 
 void SpirvShader::VisitMemoryObjectInner(Type::ID id, Decorations d, uint32_t &index, uint32_t offset, bool resultIsPointer, const MemoryVisitor &f) const