SpirvShader: Move control flow handling to new cpp file

Bug: b/145336353
Change-Id: I1b78cd03247e64f06de77da07b7f08bbc25e326f
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/38811
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
Reviewed-by: Alexis Hétu <sugoi@google.com>
Tested-by: Ben Clayton <bclayton@google.com>
diff --git a/src/Pipeline/BUILD.gn b/src/Pipeline/BUILD.gn
index 62f28cb..39283ed 100644
--- a/src/Pipeline/BUILD.gn
+++ b/src/Pipeline/BUILD.gn
@@ -40,6 +40,7 @@
     "ShaderCore.cpp",
     "SpirvShader_dbg.cpp",
     "SpirvShader.cpp",
+    "SpirvShaderControlFlow.cpp",
     "SpirvShaderGLSLstd450.cpp",
     "SpirvShaderSampling.cpp",
     "VertexProgram.cpp",
diff --git a/src/Pipeline/SpirvShader.cpp b/src/Pipeline/SpirvShader.cpp
index ec916b5..115e57b 100644
--- a/src/Pipeline/SpirvShader.cpp
+++ b/src/Pipeline/SpirvShader.cpp
@@ -15,7 +15,6 @@
 #include "SpirvShader.hpp"
 
 #include "SamplerCore.hpp"
-#include "Reactor/Coroutine.hpp"
 #include "System/Math.hpp"
 #include "Vulkan/VkBuffer.hpp"
 #include "Vulkan/VkBufferView.hpp"
@@ -1748,67 +1747,6 @@
 		EmitBlocks(getFunction(entryPoint).entry, &state);
 	}
 
-	void SpirvShader::EmitBlocks(Block::ID id, EmitState *state, Block::ID ignore /* = 0 */) const
-	{
-		auto oldPending = state->pending;
-		auto &function = getFunction(state->function);
-
-		std::deque<Block::ID> pending;
-		state->pending = &pending;
-		pending.push_front(id);
-		while (pending.size() > 0)
-		{
-			auto id = pending.front();
-
-			auto const &block = function.getBlock(id);
-			if (id == ignore)
-			{
-				pending.pop_front();
-				continue;
-			}
-
-			// Ensure all dependency blocks have been generated.
-			auto depsDone = true;
-			function.ForeachBlockDependency(id, [&](Block::ID dep)
-			{
-				if (state->visited.count(dep) == 0)
-				{
-					state->pending->push_front(dep);
-					depsDone = false;
-				}
-			});
-
-			if (!depsDone)
-			{
-				continue;
-			}
-
-			pending.pop_front();
-
-			state->block = id;
-
-			switch (block.kind)
-			{
-				case Block::Simple:
-				case Block::StructuredBranchConditional:
-				case Block::UnstructuredBranchConditional:
-				case Block::StructuredSwitch:
-				case Block::UnstructuredSwitch:
-					EmitNonLoop(state);
-					break;
-
-				case Block::Loop:
-					EmitLoop(state);
-					break;
-
-				default:
-					UNREACHABLE("Unexpected Block Kind: %d", int(block.kind));
-			}
-		}
-
-		state->pending = oldPending;
-	}
-
 	void SpirvShader::EmitInstructions(InsnIterator begin, InsnIterator end, EmitState *state) const
 	{
 		for (auto insn = begin; insn != end; insn++)
@@ -1827,204 +1765,6 @@
 		}
 	}
 
-	void SpirvShader::EmitNonLoop(EmitState *state) const
-	{
-		auto &function = getFunction(state->function);
-		auto blockId = state->block;
-		auto block = function.getBlock(blockId);
-
-		if (!state->visited.emplace(blockId).second)
-		{
-			return; // Already generated this block.
-		}
-
-		if (blockId != function.entry)
-		{
-			// Set the activeLaneMask.
-			SIMD::Int activeLaneMask(0);
-			for (auto in : block.ins)
-			{
-				auto inMask = GetActiveLaneMaskEdge(state, in, blockId);
-				activeLaneMask |= inMask;
-			}
-			state->setActiveLaneMask(activeLaneMask);
-		}
-
-		EmitInstructions(block.begin(), block.end(), state);
-
-		for (auto out : block.outs)
-		{
-			if (state->visited.count(out) == 0)
-			{
-				state->pending->push_back(out);
-			}
-		}
-	}
-
-	void SpirvShader::EmitLoop(EmitState *state) const
-	{
-		auto &function = getFunction(state->function);
-		auto blockId = state->block;
-		auto &block = function.getBlock(blockId);
-		auto mergeBlockId = block.mergeBlock;
-		auto &mergeBlock = function.getBlock(mergeBlockId);
-
-		if (!state->visited.emplace(blockId).second)
-		{
-			return; // Already emitted this loop.
-		}
-
-		// Gather all the blocks that make up the loop.
-		std::unordered_set<Block::ID> loopBlocks;
-		loopBlocks.emplace(block.mergeBlock);
-		function.TraverseReachableBlocks(blockId, loopBlocks);
-
-		// incomingBlocks are block ins that are not back-edges.
-		std::unordered_set<Block::ID> incomingBlocks;
-		for (auto in : block.ins)
-		{
-			if (loopBlocks.count(in) == 0)
-			{
-				incomingBlocks.emplace(in);
-			}
-		}
-
-		// Emit the loop phi instructions, and initialize them with a value from
-		// the incoming blocks.
-		for (auto insn = block.begin(); insn != block.mergeInstruction; insn++)
-		{
-			if (insn.opcode() == spv::OpPhi)
-			{
-				StorePhi(blockId, insn, state, incomingBlocks);
-			}
-		}
-
-		// loopActiveLaneMask is the mask of lanes that are continuing to loop.
-		// This is initialized with the incoming active lane masks.
-		SIMD::Int loopActiveLaneMask = SIMD::Int(0);
-		for (auto in : incomingBlocks)
-		{
-			loopActiveLaneMask |= GetActiveLaneMaskEdge(state, in, blockId);
-		}
-
-		// mergeActiveLaneMasks contains edge lane masks for the merge block.
-		// This is the union of all edge masks across all iterations of the loop.
-		std::unordered_map<Block::ID, SIMD::Int> mergeActiveLaneMasks;
-		for (auto in : function.getBlock(mergeBlockId).ins)
-		{
-			mergeActiveLaneMasks.emplace(in, SIMD::Int(0));
-		}
-
-		// Create the loop basic blocks
-		auto headerBasicBlock = Nucleus::createBasicBlock();
-		auto mergeBasicBlock = Nucleus::createBasicBlock();
-
-		// Start emitting code inside the loop.
-		Nucleus::createBr(headerBasicBlock);
-		Nucleus::setInsertBlock(headerBasicBlock);
-
-		// Load the active lane mask.
-		state->setActiveLaneMask(loopActiveLaneMask);
-
-		// Emit the non-phi loop header block's instructions.
-		for (auto insn = block.begin(); insn != block.end(); insn++)
-		{
-			if (insn.opcode() == spv::OpPhi)
-			{
-				LoadPhi(insn, state);
-			}
-			else
-			{
-				EmitInstruction(insn, state);
-			}
-		}
-
-		// Emit all blocks between the loop header and the merge block, but
-		// don't emit the merge block yet.
-		for (auto out : block.outs)
-		{
-			EmitBlocks(out, state, mergeBlockId);
-		}
-
-		// Restore current block id after emitting loop blocks.
-		state->block = blockId;
-
-		// Rebuild the loopActiveLaneMask from the loop back edges.
-		loopActiveLaneMask = SIMD::Int(0);
-		for (auto in : block.ins)
-		{
-			if (function.ExistsPath(blockId, in, mergeBlockId))
-			{
-				loopActiveLaneMask |= GetActiveLaneMaskEdge(state, in, blockId);
-			}
-		}
-
-		// Add active lanes to the merge lane mask.
-		for (auto in : function.getBlock(mergeBlockId).ins)
-		{
-			auto edge = Block::Edge{in, mergeBlockId};
-			auto it = state->edgeActiveLaneMasks.find(edge);
-			if (it != state->edgeActiveLaneMasks.end())
-			{
-				mergeActiveLaneMasks[in] |= it->second;
-			}
-		}
-
-		// Update loop phi values.
-		for (auto insn = block.begin(); insn != block.mergeInstruction; insn++)
-		{
-			if (insn.opcode() == spv::OpPhi)
-			{
-				StorePhi(blockId, insn, state, loopBlocks);
-			}
-		}
-
-		// Use the [loop -> merge] active lane masks to update the phi values in
-		// the merge block. We need to do this to handle divergent control flow
-		// in the loop.
-		//
-		// Consider the following:
-		//
-		//     int phi_source = 0;
-		//     for (uint i = 0; i < 4; i++)
-		//     {
-		//         phi_source = 0;
-		//         if (gl_GlobalInvocationID.x % 4 == i) // divergent control flow
-		//         {
-		//             phi_source = 42; // single lane assignment.
-		//             break; // activeLaneMask for [loop->merge] is active for a single lane.
-		//         }
-		//         // -- we are here --
-		//     }
-		//     // merge block
-		//     int phi = phi_source; // OpPhi
-		//
-		// In this example, with each iteration of the loop, phi_source will
-		// only have a single lane assigned. However by 'phi' value in the merge
-		// block needs to be assigned the union of all the per-lane assignments
-		// of phi_source when that lane exited the loop.
-		for (auto insn = mergeBlock.begin(); insn != mergeBlock.end(); insn++)
-		{
-			if (insn.opcode() == spv::OpPhi)
-			{
-				StorePhi(mergeBlockId, insn, state, loopBlocks);
-			}
-		}
-
-		// Loop body now done.
-		// If any lanes are still active, jump back to the loop header,
-		// otherwise jump to the merge block.
-		Nucleus::createCondBr(AnyTrue(loopActiveLaneMask).value, headerBasicBlock, mergeBasicBlock);
-
-		// Continue emitting from the merge block.
-		Nucleus::setInsertBlock(mergeBasicBlock);
-		state->pending->push_back(mergeBlockId);
-		for (auto it : mergeActiveLaneMasks)
-		{
-			state->addActiveLaneMaskEdge(it.first, mergeBlockId, it.second);
-		}
-	}
-
 	SpirvShader::EmitResult SpirvShader::EmitInstruction(InsnIterator insn, EmitState *state) const
 	{
 		auto opcode = insn.opcode();
@@ -3419,189 +3159,6 @@
 		return EmitResult::Continue;
 	}
 
-	SpirvShader::EmitResult SpirvShader::EmitBranch(InsnIterator insn, EmitState *state) const
-	{
-		auto target = Block::ID(insn.word(1));
-		state->addActiveLaneMaskEdge(state->block, target, state->activeLaneMask());
-		return EmitResult::Terminator;
-	}
-
-	SpirvShader::EmitResult SpirvShader::EmitBranchConditional(InsnIterator insn, EmitState *state) const
-	{
-		auto &function = getFunction(state->function);
-		auto block = function.getBlock(state->block);
-		ASSERT(block.branchInstruction == insn);
-
-		auto condId = Object::ID(block.branchInstruction.word(1));
-		auto trueBlockId = Block::ID(block.branchInstruction.word(2));
-		auto falseBlockId = Block::ID(block.branchInstruction.word(3));
-
-		auto cond = GenericValue(this, state, condId);
-		ASSERT_MSG(getType(cond.type).sizeInComponents == 1, "Condition must be a Boolean type scalar");
-
-		// TODO: Optimize for case where all lanes take same path.
-
-		state->addOutputActiveLaneMaskEdge(trueBlockId, cond.Int(0));
-		state->addOutputActiveLaneMaskEdge(falseBlockId, ~cond.Int(0));
-
-		return EmitResult::Terminator;
-	}
-
-	SpirvShader::EmitResult SpirvShader::EmitSwitch(InsnIterator insn, EmitState *state) const
-	{
-		auto &function = getFunction(state->function);
-		auto block = function.getBlock(state->block);
-		ASSERT(block.branchInstruction == insn);
-
-		auto selId = Object::ID(block.branchInstruction.word(1));
-
-		auto sel = GenericValue(this, state, selId);
-		ASSERT_MSG(getType(sel.type).sizeInComponents == 1, "Selector must be a scalar");
-
-		auto numCases = (block.branchInstruction.wordCount() - 3) / 2;
-
-		// TODO: Optimize for case where all lanes take same path.
-
-		SIMD::Int defaultLaneMask = state->activeLaneMask();
-
-		// 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);
-			auto caseBlockId = Block::ID(block.branchInstruction.word(i * 2 + 4));
-			auto caseLabelMatch = CmpEQ(sel.Int(0), SIMD::Int(label));
-			state->addOutputActiveLaneMaskEdge(caseBlockId, caseLabelMatch);
-			defaultLaneMask &= ~caseLabelMatch;
-		}
-
-		auto defaultBlockId = Block::ID(block.branchInstruction.word(2));
-		state->addOutputActiveLaneMaskEdge(defaultBlockId, defaultLaneMask);
-
-		return EmitResult::Terminator;
-	}
-
-	SpirvShader::EmitResult SpirvShader::EmitUnreachable(InsnIterator insn, EmitState *state) const
-	{
-		// TODO: Log something in this case?
-		state->setActiveLaneMask(SIMD::Int(0));
-		return EmitResult::Terminator;
-	}
-
-	SpirvShader::EmitResult SpirvShader::EmitReturn(InsnIterator insn, EmitState *state) const
-	{
-		state->setActiveLaneMask(SIMD::Int(0));
-		return EmitResult::Terminator;
-	}
-
-	SpirvShader::EmitResult SpirvShader::EmitKill(InsnIterator insn, EmitState *state) const
-	{
-		state->routine->killMask |= SignMask(state->activeLaneMask());
-		state->setActiveLaneMask(SIMD::Int(0));
-		return EmitResult::Terminator;
-	}
-
-	SpirvShader::EmitResult SpirvShader::EmitFunctionCall(InsnIterator insn, EmitState *state) const
-	{
-		auto functionId = Function::ID(insn.word(3));
-		const auto& functionIt = functions.find(functionId);
-		ASSERT(functionIt != functions.end());
-		auto& function = functionIt->second;
-
-		// TODO(b/141246700): Add full support for spv::OpFunctionCall
-		// The only supported function is a single OpKill wrapped in a
-		// function, as a result of the "wrap OpKill" SPIRV-Tools pass
-		ASSERT(function.blocks.size() == 1);
-		spv::Op wrapOpKill[] = { spv::OpLabel, spv::OpKill };
-
-		for (auto block : function.blocks)
-		{
-			int insnNumber = 0;
-			for (auto blockInsn : block.second)
-			{
-				if (insnNumber > 1)
-				{
-					UNIMPLEMENTED("Function block number of instructions: %d", insnNumber);
-					return EmitResult::Continue;
-				}
-				if (blockInsn.opcode() != wrapOpKill[insnNumber++])
-				{
-					UNIMPLEMENTED("Function block instruction %d : %s", insnNumber - 1, OpcodeName(blockInsn.opcode()).c_str());
-					return EmitResult::Continue;
-				}
-				if (blockInsn.opcode() == spv::OpKill)
-				{
-					EmitInstruction(blockInsn, state);
-				}
-			}
-		}
-
-		return EmitResult::Continue;
-	}
-
-	SpirvShader::EmitResult SpirvShader::EmitPhi(InsnIterator insn, EmitState *state) const
-	{
-		auto &function = getFunction(state->function);
-		auto currentBlock = function.getBlock(state->block);
-		if (!currentBlock.isLoopMerge)
-		{
-			// If this is a loop merge block, then don't attempt to update the
-			// phi values from the ins. EmitLoop() has had to take special care
-			// of this phi in order to correctly deal with divergent lanes.
-			StorePhi(state->block, insn, state, currentBlock.ins);
-		}
-		LoadPhi(insn, state);
-		return EmitResult::Continue;
-	}
-
-	void SpirvShader::LoadPhi(InsnIterator insn, EmitState *state) const
-	{
-		auto typeId = Type::ID(insn.word(1));
-		auto type = getType(typeId);
-		auto objectId = Object::ID(insn.word(2));
-
-		auto storageIt = state->routine->phis.find(objectId);
-		ASSERT(storageIt != state->routine->phis.end());
-		auto &storage = storageIt->second;
-
-		auto &dst = state->createIntermediate(objectId, type.sizeInComponents);
-		for(uint32_t i = 0; i < type.sizeInComponents; i++)
-		{
-			dst.move(i, storage[i]);
-		}
-	}
-
-	void SpirvShader::StorePhi(Block::ID currentBlock, InsnIterator insn, EmitState *state, std::unordered_set<SpirvShader::Block::ID> const& filter) const
-	{
-		auto typeId = Type::ID(insn.word(1));
-		auto type = getType(typeId);
-		auto objectId = Object::ID(insn.word(2));
-
-		auto storageIt = state->routine->phis.find(objectId);
-		ASSERT(storageIt != state->routine->phis.end());
-		auto &storage = storageIt->second;
-
-		for (uint32_t w = 3; w < insn.wordCount(); w += 2)
-		{
-			auto varId = Object::ID(insn.word(w + 0));
-			auto blockId = Block::ID(insn.word(w + 1));
-
-			if (filter.count(blockId) == 0)
-			{
-				continue;
-			}
-
-			auto mask = GetActiveLaneMaskEdge(state, blockId, currentBlock);
-			auto in = GenericValue(this, state, varId);
-
-			for (uint32_t i = 0; i < type.sizeInComponents; i++)
-			{
-				storage[i] = As<SIMD::Float>((As<SIMD::Int>(storage[i]) & ~mask) | (in.Int(i) & mask));
-			}
-		}
-	}
-
 	SpirvShader::EmitResult SpirvShader::EmitImageSampleImplicitLod(Variant variant, InsnIterator insn, EmitState *state) const
 	{
 		return EmitImageSample({variant, Implicit}, insn, state);
@@ -4073,11 +3630,6 @@
 		return ptr;
 	}
 
-	void SpirvShader::Yield(YieldResult res) const
-	{
-		rr::Yield(RValue<Int>(int(res)));
-	}
-
 	SpirvShader::EmitResult SpirvShader::EmitImageRead(InsnIterator insn, EmitState *state) const
 	{
 		auto &resultType = getType(Type::ID(insn.word(1)));
@@ -4702,30 +4254,6 @@
 		return EmitResult::Continue;
 	}
 
-	SpirvShader::EmitResult SpirvShader::EmitControlBarrier(InsnIterator insn, EmitState *state) const
-	{
-		auto executionScope = spv::Scope(GetConstScalarInt(insn.word(1)));
-		auto semantics = spv::MemorySemanticsMask(GetConstScalarInt(insn.word(3)));
-		// TODO: We probably want to consider the memory scope here. For now,
-		// just always emit the full fence.
-		Fence(semantics);
-
-		switch (executionScope)
-		{
-		case spv::ScopeWorkgroup:
-			Yield(YieldResult::ControlBarrier);
-			break;
-		case spv::ScopeSubgroup:
-			break;
-		default:
-			// See Vulkan 1.1 spec, Appendix A, Validation Rules within a Module.
-			UNREACHABLE("Scope for execution must be limited to Workgroup or Subgroup");
-			break;
-		}
-
-		return EmitResult::Continue;
-	}
-
 	SpirvShader::EmitResult SpirvShader::EmitMemoryBarrier(InsnIterator insn, EmitState *state) const
 	{
 		auto semantics = spv::MemorySemanticsMask(GetConstScalarInt(insn.word(2)));
@@ -4735,15 +4263,6 @@
 		return EmitResult::Continue;
 	}
 
-	void SpirvShader::Fence(spv::MemorySemanticsMask semantics) const
-	{
-		if (semantics == spv::MemorySemanticsMaskNone)
-		{
-			return; //no-op
-		}
-		rr::Fence(MemoryOrder(semantics));
-	}
-
 	SpirvShader::EmitResult SpirvShader::EmitGroupNonUniform(InsnIterator insn, EmitState *state) const
 	{
 		static_assert(SIMD::Width == 4, "EmitGroupNonUniform makes many assumptions that the SIMD vector width is 4");
@@ -5359,200 +4878,6 @@
 		routine->phis.clear();
 	}
 
-	SpirvShader::Block::Block(InsnIterator begin, InsnIterator end) : begin_(begin), end_(end)
-	{
-		// Default to a Simple, this may change later.
-		kind = Block::Simple;
-
-		// Walk the instructions to find the last two of the block.
-		InsnIterator insns[2];
-		for (auto insn : *this)
-		{
-			insns[0] = insns[1];
-			insns[1] = insn;
-		}
-
-		switch (insns[1].opcode())
-		{
-			case spv::OpBranch:
-				branchInstruction = insns[1];
-				outs.emplace(Block::ID(branchInstruction.word(1)));
-
-				switch (insns[0].opcode())
-				{
-					case spv::OpLoopMerge:
-						kind = Loop;
-						mergeInstruction = insns[0];
-						mergeBlock = Block::ID(mergeInstruction.word(1));
-						continueTarget = Block::ID(mergeInstruction.word(2));
-						break;
-
-					default:
-						kind = Block::Simple;
-						break;
-				}
-				break;
-
-			case spv::OpBranchConditional:
-				branchInstruction = insns[1];
-				outs.emplace(Block::ID(branchInstruction.word(2)));
-				outs.emplace(Block::ID(branchInstruction.word(3)));
-
-				switch (insns[0].opcode())
-				{
-					case spv::OpSelectionMerge:
-						kind = StructuredBranchConditional;
-						mergeInstruction = insns[0];
-						mergeBlock = Block::ID(mergeInstruction.word(1));
-						break;
-
-					case spv::OpLoopMerge:
-						kind = Loop;
-						mergeInstruction = insns[0];
-						mergeBlock = Block::ID(mergeInstruction.word(1));
-						continueTarget = Block::ID(mergeInstruction.word(2));
-						break;
-
-					default:
-						kind = UnstructuredBranchConditional;
-						break;
-				}
-				break;
-
-			case spv::OpSwitch:
-				branchInstruction = insns[1];
-				outs.emplace(Block::ID(branchInstruction.word(2)));
-				for (uint32_t w = 4; w < branchInstruction.wordCount(); w += 2)
-				{
-					outs.emplace(Block::ID(branchInstruction.word(w)));
-				}
-
-				switch (insns[0].opcode())
-				{
-					case spv::OpSelectionMerge:
-						kind = StructuredSwitch;
-						mergeInstruction = insns[0];
-						mergeBlock = Block::ID(mergeInstruction.word(1));
-						break;
-
-					default:
-						kind = UnstructuredSwitch;
-						break;
-				}
-				break;
-
-			default:
-				break;
-		}
-	}
-
-	void SpirvShader::Function::TraverseReachableBlocks(Block::ID id, SpirvShader::Block::Set& reachable) const
-	{
-		if (reachable.count(id) == 0)
-		{
-			reachable.emplace(id);
-			for (auto out : getBlock(id).outs)
-			{
-				TraverseReachableBlocks(out, reachable);
-			}
-		}
-	}
-
-	void SpirvShader::Function::AssignBlockFields()
-	{
-		Block::Set reachable;
-		TraverseReachableBlocks(entry, reachable);
-
-		for (auto &it : blocks)
-		{
-			auto &blockId = it.first;
-			auto &block = it.second;
-			if (reachable.count(blockId) > 0)
-			{
-				for (auto &outId : it.second.outs)
-				{
-					auto outIt = blocks.find(outId);
-					ASSERT_MSG(outIt != blocks.end(), "Block %d has a non-existent out %d", blockId.value(), outId.value());
-					auto &out = outIt->second;
-					out.ins.emplace(blockId);
-				}
-				if (block.kind == Block::Loop)
-				{
-					auto mergeIt = blocks.find(block.mergeBlock);
-					ASSERT_MSG(mergeIt != blocks.end(), "Loop block %d has a non-existent merge block %d", blockId.value(), block.mergeBlock.value());
-					mergeIt->second.isLoopMerge = true;
-				}
-			}
-		}
-	}
-
-	void SpirvShader::Function::ForeachBlockDependency(Block::ID blockId, std::function<void(Block::ID)> f) const
-	{
-		auto block = getBlock(blockId);
-		for (auto dep : block.ins)
-		{
-			if (block.kind != Block::Loop ||                 // if not a loop...
-				!ExistsPath(blockId, dep, block.mergeBlock)) // or a loop and not a loop back edge
-			{
-				f(dep);
-			}
-		}
-	}
-
-	bool SpirvShader::Function::ExistsPath(Block::ID from, Block::ID to, Block::ID notPassingThrough) const
-	{
-		// TODO: Optimize: This can be cached on the block.
-		Block::Set seen;
-		seen.emplace(notPassingThrough);
-
-		std::queue<Block::ID> pending;
-		pending.emplace(from);
-
-		while (pending.size() > 0)
-		{
-			auto id = pending.front();
-			pending.pop();
-			for (auto out : getBlock(id).outs)
-			{
-				if (seen.count(out) != 0) { continue; }
-				if (out == to) { return true; }
-				pending.emplace(out);
-			}
-			seen.emplace(id);
-		}
-
-		return false;
-	}
-
-	void SpirvShader::EmitState::addOutputActiveLaneMaskEdge(Block::ID to, RValue<SIMD::Int> mask)
-	{
-		addActiveLaneMaskEdge(block, to, mask & activeLaneMask());
-	}
-
-	void SpirvShader::EmitState::addActiveLaneMaskEdge(Block::ID from, Block::ID to, RValue<SIMD::Int> mask)
-	{
-		auto edge = Block::Edge{from, to};
-		auto it = edgeActiveLaneMasks.find(edge);
-		if (it == edgeActiveLaneMasks.end())
-		{
-			edgeActiveLaneMasks.emplace(edge, mask);
-		}
-		else
-		{
-			auto combined = it->second | mask;
-			edgeActiveLaneMasks.erase(edge);
-			edgeActiveLaneMasks.emplace(edge, combined);
-		}
-	}
-
-	RValue<SIMD::Int> SpirvShader::GetActiveLaneMaskEdge(EmitState *state, Block::ID from, Block::ID to) const
-	{
-		auto edge = Block::Edge{from, to};
-		auto it = state->edgeActiveLaneMasks.find(edge);
-		ASSERT_MSG(it != state->edgeActiveLaneMasks.end(), "Could not find edge %d -> %d", from.value(), to.value());
-		return it->second;
-	}
-
 	VkShaderStageFlagBits SpirvShader::executionModelToStage(spv::ExecutionModel model)
 	{
 		switch (model)
diff --git a/src/Pipeline/SpirvShaderControlFlow.cpp b/src/Pipeline/SpirvShaderControlFlow.cpp
new file mode 100644
index 0000000..dff64cb
--- /dev/null
+++ b/src/Pipeline/SpirvShaderControlFlow.cpp
@@ -0,0 +1,702 @@
+// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "SpirvShader.hpp"
+
+#include "Reactor/Coroutine.hpp" // rr::Yield
+
+#include "ShaderCore.hpp"
+
+#include <spirv/unified1/spirv.hpp>
+#include <spirv/unified1/GLSL.std.450.h>
+
+#include <queue>
+
+namespace sw {
+
+SpirvShader::Block::Block(InsnIterator begin, InsnIterator end) : begin_(begin), end_(end)
+{
+	// Default to a Simple, this may change later.
+	kind = Block::Simple;
+
+	// Walk the instructions to find the last two of the block.
+	InsnIterator insns[2];
+	for (auto insn : *this)
+	{
+		insns[0] = insns[1];
+		insns[1] = insn;
+	}
+
+	switch (insns[1].opcode())
+	{
+		case spv::OpBranch:
+			branchInstruction = insns[1];
+			outs.emplace(Block::ID(branchInstruction.word(1)));
+
+			switch (insns[0].opcode())
+			{
+				case spv::OpLoopMerge:
+					kind = Loop;
+					mergeInstruction = insns[0];
+					mergeBlock = Block::ID(mergeInstruction.word(1));
+					continueTarget = Block::ID(mergeInstruction.word(2));
+					break;
+
+				default:
+					kind = Block::Simple;
+					break;
+			}
+			break;
+
+		case spv::OpBranchConditional:
+			branchInstruction = insns[1];
+			outs.emplace(Block::ID(branchInstruction.word(2)));
+			outs.emplace(Block::ID(branchInstruction.word(3)));
+
+			switch (insns[0].opcode())
+			{
+				case spv::OpSelectionMerge:
+					kind = StructuredBranchConditional;
+					mergeInstruction = insns[0];
+					mergeBlock = Block::ID(mergeInstruction.word(1));
+					break;
+
+				case spv::OpLoopMerge:
+					kind = Loop;
+					mergeInstruction = insns[0];
+					mergeBlock = Block::ID(mergeInstruction.word(1));
+					continueTarget = Block::ID(mergeInstruction.word(2));
+					break;
+
+				default:
+					kind = UnstructuredBranchConditional;
+					break;
+			}
+			break;
+
+		case spv::OpSwitch:
+			branchInstruction = insns[1];
+			outs.emplace(Block::ID(branchInstruction.word(2)));
+			for (uint32_t w = 4; w < branchInstruction.wordCount(); w += 2)
+			{
+				outs.emplace(Block::ID(branchInstruction.word(w)));
+			}
+
+			switch (insns[0].opcode())
+			{
+				case spv::OpSelectionMerge:
+					kind = StructuredSwitch;
+					mergeInstruction = insns[0];
+					mergeBlock = Block::ID(mergeInstruction.word(1));
+					break;
+
+				default:
+					kind = UnstructuredSwitch;
+					break;
+			}
+			break;
+
+		default:
+			break;
+	}
+}
+
+void SpirvShader::Function::TraverseReachableBlocks(Block::ID id, SpirvShader::Block::Set& reachable) const
+{
+	if (reachable.count(id) == 0)
+	{
+		reachable.emplace(id);
+		for (auto out : getBlock(id).outs)
+		{
+			TraverseReachableBlocks(out, reachable);
+		}
+	}
+}
+
+void SpirvShader::Function::AssignBlockFields()
+{
+	Block::Set reachable;
+	TraverseReachableBlocks(entry, reachable);
+
+	for (auto &it : blocks)
+	{
+		auto &blockId = it.first;
+		auto &block = it.second;
+		if (reachable.count(blockId) > 0)
+		{
+			for (auto &outId : it.second.outs)
+			{
+				auto outIt = blocks.find(outId);
+				ASSERT_MSG(outIt != blocks.end(), "Block %d has a non-existent out %d", blockId.value(), outId.value());
+				auto &out = outIt->second;
+				out.ins.emplace(blockId);
+			}
+			if (block.kind == Block::Loop)
+			{
+				auto mergeIt = blocks.find(block.mergeBlock);
+				ASSERT_MSG(mergeIt != blocks.end(), "Loop block %d has a non-existent merge block %d", blockId.value(), block.mergeBlock.value());
+				mergeIt->second.isLoopMerge = true;
+			}
+		}
+	}
+}
+
+void SpirvShader::Function::ForeachBlockDependency(Block::ID blockId, std::function<void(Block::ID)> f) const
+{
+	auto block = getBlock(blockId);
+	for (auto dep : block.ins)
+	{
+		if (block.kind != Block::Loop ||                 // if not a loop...
+			!ExistsPath(blockId, dep, block.mergeBlock)) // or a loop and not a loop back edge
+		{
+			f(dep);
+		}
+	}
+}
+
+bool SpirvShader::Function::ExistsPath(Block::ID from, Block::ID to, Block::ID notPassingThrough) const
+{
+	// TODO: Optimize: This can be cached on the block.
+	Block::Set seen;
+	seen.emplace(notPassingThrough);
+
+	std::queue<Block::ID> pending;
+	pending.emplace(from);
+
+	while (pending.size() > 0)
+	{
+		auto id = pending.front();
+		pending.pop();
+		for (auto out : getBlock(id).outs)
+		{
+			if (seen.count(out) != 0) { continue; }
+			if (out == to) { return true; }
+			pending.emplace(out);
+		}
+		seen.emplace(id);
+	}
+
+	return false;
+}
+
+void SpirvShader::EmitState::addOutputActiveLaneMaskEdge(Block::ID to, RValue<SIMD::Int> mask)
+{
+	addActiveLaneMaskEdge(block, to, mask & activeLaneMask());
+}
+
+void SpirvShader::EmitState::addActiveLaneMaskEdge(Block::ID from, Block::ID to, RValue<SIMD::Int> mask)
+{
+	auto edge = Block::Edge{from, to};
+	auto it = edgeActiveLaneMasks.find(edge);
+	if (it == edgeActiveLaneMasks.end())
+	{
+		edgeActiveLaneMasks.emplace(edge, mask);
+	}
+	else
+	{
+		auto combined = it->second | mask;
+		edgeActiveLaneMasks.erase(edge);
+		edgeActiveLaneMasks.emplace(edge, combined);
+	}
+}
+
+RValue<SIMD::Int> SpirvShader::GetActiveLaneMaskEdge(EmitState *state, Block::ID from, Block::ID to) const
+{
+	auto edge = Block::Edge{from, to};
+	auto it = state->edgeActiveLaneMasks.find(edge);
+	ASSERT_MSG(it != state->edgeActiveLaneMasks.end(), "Could not find edge %d -> %d", from.value(), to.value());
+	return it->second;
+}
+
+void SpirvShader::EmitBlocks(Block::ID id, EmitState *state, Block::ID ignore /* = 0 */) const
+{
+	auto oldPending = state->pending;
+	auto &function = getFunction(state->function);
+
+	std::deque<Block::ID> pending;
+	state->pending = &pending;
+	pending.push_front(id);
+	while (pending.size() > 0)
+	{
+		auto id = pending.front();
+
+		auto const &block = function.getBlock(id);
+		if (id == ignore)
+		{
+			pending.pop_front();
+			continue;
+		}
+
+		// Ensure all dependency blocks have been generated.
+		auto depsDone = true;
+		function.ForeachBlockDependency(id, [&](Block::ID dep)
+		{
+			if (state->visited.count(dep) == 0)
+			{
+				state->pending->push_front(dep);
+				depsDone = false;
+			}
+		});
+
+		if (!depsDone)
+		{
+			continue;
+		}
+
+		pending.pop_front();
+
+		state->block = id;
+
+		switch (block.kind)
+		{
+			case Block::Simple:
+			case Block::StructuredBranchConditional:
+			case Block::UnstructuredBranchConditional:
+			case Block::StructuredSwitch:
+			case Block::UnstructuredSwitch:
+				EmitNonLoop(state);
+				break;
+
+			case Block::Loop:
+				EmitLoop(state);
+				break;
+
+			default:
+				UNREACHABLE("Unexpected Block Kind: %d", int(block.kind));
+		}
+	}
+
+	state->pending = oldPending;
+}
+
+void SpirvShader::EmitNonLoop(EmitState *state) const
+{
+	auto &function = getFunction(state->function);
+	auto blockId = state->block;
+	auto block = function.getBlock(blockId);
+
+	if (!state->visited.emplace(blockId).second)
+	{
+		return; // Already generated this block.
+	}
+
+	if (blockId != function.entry)
+	{
+		// Set the activeLaneMask.
+		SIMD::Int activeLaneMask(0);
+		for (auto in : block.ins)
+		{
+			auto inMask = GetActiveLaneMaskEdge(state, in, blockId);
+			activeLaneMask |= inMask;
+		}
+		state->setActiveLaneMask(activeLaneMask);
+	}
+
+	EmitInstructions(block.begin(), block.end(), state);
+
+	for (auto out : block.outs)
+	{
+		if (state->visited.count(out) == 0)
+		{
+			state->pending->push_back(out);
+		}
+	}
+}
+
+void SpirvShader::EmitLoop(EmitState *state) const
+{
+	auto &function = getFunction(state->function);
+	auto blockId = state->block;
+	auto &block = function.getBlock(blockId);
+	auto mergeBlockId = block.mergeBlock;
+	auto &mergeBlock = function.getBlock(mergeBlockId);
+
+	if (!state->visited.emplace(blockId).second)
+	{
+		return; // Already emitted this loop.
+	}
+
+	// Gather all the blocks that make up the loop.
+	std::unordered_set<Block::ID> loopBlocks;
+	loopBlocks.emplace(block.mergeBlock);
+	function.TraverseReachableBlocks(blockId, loopBlocks);
+
+	// incomingBlocks are block ins that are not back-edges.
+	std::unordered_set<Block::ID> incomingBlocks;
+	for (auto in : block.ins)
+	{
+		if (loopBlocks.count(in) == 0)
+		{
+			incomingBlocks.emplace(in);
+		}
+	}
+
+	// Emit the loop phi instructions, and initialize them with a value from
+	// the incoming blocks.
+	for (auto insn = block.begin(); insn != block.mergeInstruction; insn++)
+	{
+		if (insn.opcode() == spv::OpPhi)
+		{
+			StorePhi(blockId, insn, state, incomingBlocks);
+		}
+	}
+
+	// loopActiveLaneMask is the mask of lanes that are continuing to loop.
+	// This is initialized with the incoming active lane masks.
+	SIMD::Int loopActiveLaneMask = SIMD::Int(0);
+	for (auto in : incomingBlocks)
+	{
+		loopActiveLaneMask |= GetActiveLaneMaskEdge(state, in, blockId);
+	}
+
+	// mergeActiveLaneMasks contains edge lane masks for the merge block.
+	// This is the union of all edge masks across all iterations of the loop.
+	std::unordered_map<Block::ID, SIMD::Int> mergeActiveLaneMasks;
+	for (auto in : function.getBlock(mergeBlockId).ins)
+	{
+		mergeActiveLaneMasks.emplace(in, SIMD::Int(0));
+	}
+
+	// Create the loop basic blocks
+	auto headerBasicBlock = Nucleus::createBasicBlock();
+	auto mergeBasicBlock = Nucleus::createBasicBlock();
+
+	// Start emitting code inside the loop.
+	Nucleus::createBr(headerBasicBlock);
+	Nucleus::setInsertBlock(headerBasicBlock);
+
+	// Load the active lane mask.
+	state->setActiveLaneMask(loopActiveLaneMask);
+
+	// Emit the non-phi loop header block's instructions.
+	for (auto insn = block.begin(); insn != block.end(); insn++)
+	{
+		if (insn.opcode() == spv::OpPhi)
+		{
+			LoadPhi(insn, state);
+		}
+		else
+		{
+			EmitInstruction(insn, state);
+		}
+	}
+
+	// Emit all blocks between the loop header and the merge block, but
+	// don't emit the merge block yet.
+	for (auto out : block.outs)
+	{
+		EmitBlocks(out, state, mergeBlockId);
+	}
+
+	// Restore current block id after emitting loop blocks.
+	state->block = blockId;
+
+	// Rebuild the loopActiveLaneMask from the loop back edges.
+	loopActiveLaneMask = SIMD::Int(0);
+	for (auto in : block.ins)
+	{
+		if (function.ExistsPath(blockId, in, mergeBlockId))
+		{
+			loopActiveLaneMask |= GetActiveLaneMaskEdge(state, in, blockId);
+		}
+	}
+
+	// Add active lanes to the merge lane mask.
+	for (auto in : function.getBlock(mergeBlockId).ins)
+	{
+		auto edge = Block::Edge{in, mergeBlockId};
+		auto it = state->edgeActiveLaneMasks.find(edge);
+		if (it != state->edgeActiveLaneMasks.end())
+		{
+			mergeActiveLaneMasks[in] |= it->second;
+		}
+	}
+
+	// Update loop phi values.
+	for (auto insn = block.begin(); insn != block.mergeInstruction; insn++)
+	{
+		if (insn.opcode() == spv::OpPhi)
+		{
+			StorePhi(blockId, insn, state, loopBlocks);
+		}
+	}
+
+	// Use the [loop -> merge] active lane masks to update the phi values in
+	// the merge block. We need to do this to handle divergent control flow
+	// in the loop.
+	//
+	// Consider the following:
+	//
+	//     int phi_source = 0;
+	//     for (uint i = 0; i < 4; i++)
+	//     {
+	//         phi_source = 0;
+	//         if (gl_GlobalInvocationID.x % 4 == i) // divergent control flow
+	//         {
+	//             phi_source = 42; // single lane assignment.
+	//             break; // activeLaneMask for [loop->merge] is active for a single lane.
+	//         }
+	//         // -- we are here --
+	//     }
+	//     // merge block
+	//     int phi = phi_source; // OpPhi
+	//
+	// In this example, with each iteration of the loop, phi_source will
+	// only have a single lane assigned. However by 'phi' value in the merge
+	// block needs to be assigned the union of all the per-lane assignments
+	// of phi_source when that lane exited the loop.
+	for (auto insn = mergeBlock.begin(); insn != mergeBlock.end(); insn++)
+	{
+		if (insn.opcode() == spv::OpPhi)
+		{
+			StorePhi(mergeBlockId, insn, state, loopBlocks);
+		}
+	}
+
+	// Loop body now done.
+	// If any lanes are still active, jump back to the loop header,
+	// otherwise jump to the merge block.
+	Nucleus::createCondBr(AnyTrue(loopActiveLaneMask).value, headerBasicBlock, mergeBasicBlock);
+
+	// Continue emitting from the merge block.
+	Nucleus::setInsertBlock(mergeBasicBlock);
+	state->pending->push_back(mergeBlockId);
+	for (auto it : mergeActiveLaneMasks)
+	{
+		state->addActiveLaneMaskEdge(it.first, mergeBlockId, it.second);
+	}
+}
+
+SpirvShader::EmitResult SpirvShader::EmitBranch(InsnIterator insn, EmitState *state) const
+{
+	auto target = Block::ID(insn.word(1));
+	state->addActiveLaneMaskEdge(state->block, target, state->activeLaneMask());
+	return EmitResult::Terminator;
+}
+
+SpirvShader::EmitResult SpirvShader::EmitBranchConditional(InsnIterator insn, EmitState *state) const
+{
+	auto &function = getFunction(state->function);
+	auto block = function.getBlock(state->block);
+	ASSERT(block.branchInstruction == insn);
+
+	auto condId = Object::ID(block.branchInstruction.word(1));
+	auto trueBlockId = Block::ID(block.branchInstruction.word(2));
+	auto falseBlockId = Block::ID(block.branchInstruction.word(3));
+
+	auto cond = GenericValue(this, state, condId);
+	ASSERT_MSG(getType(cond.type).sizeInComponents == 1, "Condition must be a Boolean type scalar");
+
+	// TODO: Optimize for case where all lanes take same path.
+
+	state->addOutputActiveLaneMaskEdge(trueBlockId, cond.Int(0));
+	state->addOutputActiveLaneMaskEdge(falseBlockId, ~cond.Int(0));
+
+	return EmitResult::Terminator;
+}
+
+SpirvShader::EmitResult SpirvShader::EmitSwitch(InsnIterator insn, EmitState *state) const
+{
+	auto &function = getFunction(state->function);
+	auto block = function.getBlock(state->block);
+	ASSERT(block.branchInstruction == insn);
+
+	auto selId = Object::ID(block.branchInstruction.word(1));
+
+	auto sel = GenericValue(this, state, selId);
+	ASSERT_MSG(getType(sel.type).sizeInComponents == 1, "Selector must be a scalar");
+
+	auto numCases = (block.branchInstruction.wordCount() - 3) / 2;
+
+	// TODO: Optimize for case where all lanes take same path.
+
+	SIMD::Int defaultLaneMask = state->activeLaneMask();
+
+	// 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);
+		auto caseBlockId = Block::ID(block.branchInstruction.word(i * 2 + 4));
+		auto caseLabelMatch = CmpEQ(sel.Int(0), SIMD::Int(label));
+		state->addOutputActiveLaneMaskEdge(caseBlockId, caseLabelMatch);
+		defaultLaneMask &= ~caseLabelMatch;
+	}
+
+	auto defaultBlockId = Block::ID(block.branchInstruction.word(2));
+	state->addOutputActiveLaneMaskEdge(defaultBlockId, defaultLaneMask);
+
+	return EmitResult::Terminator;
+}
+
+SpirvShader::EmitResult SpirvShader::EmitUnreachable(InsnIterator insn, EmitState *state) const
+{
+	// TODO: Log something in this case?
+	state->setActiveLaneMask(SIMD::Int(0));
+	return EmitResult::Terminator;
+}
+
+SpirvShader::EmitResult SpirvShader::EmitReturn(InsnIterator insn, EmitState *state) const
+{
+	state->setActiveLaneMask(SIMD::Int(0));
+	return EmitResult::Terminator;
+}
+
+SpirvShader::EmitResult SpirvShader::EmitKill(InsnIterator insn, EmitState *state) const
+{
+	state->routine->killMask |= SignMask(state->activeLaneMask());
+	state->setActiveLaneMask(SIMD::Int(0));
+	return EmitResult::Terminator;
+}
+
+SpirvShader::EmitResult SpirvShader::EmitFunctionCall(InsnIterator insn, EmitState *state) const
+{
+	auto functionId = Function::ID(insn.word(3));
+	const auto& functionIt = functions.find(functionId);
+	ASSERT(functionIt != functions.end());
+	auto& function = functionIt->second;
+
+	// TODO(b/141246700): Add full support for spv::OpFunctionCall
+	// The only supported function is a single OpKill wrapped in a
+	// function, as a result of the "wrap OpKill" SPIRV-Tools pass
+	ASSERT(function.blocks.size() == 1);
+	spv::Op wrapOpKill[] = { spv::OpLabel, spv::OpKill };
+
+	for (auto block : function.blocks)
+	{
+		int insnNumber = 0;
+		for (auto blockInsn : block.second)
+		{
+			if (insnNumber > 1)
+			{
+				UNIMPLEMENTED("Function block number of instructions: %d", insnNumber);
+				return EmitResult::Continue;
+			}
+			if (blockInsn.opcode() != wrapOpKill[insnNumber++])
+			{
+				UNIMPLEMENTED("Function block instruction %d : %s", insnNumber - 1, OpcodeName(blockInsn.opcode()).c_str());
+				return EmitResult::Continue;
+			}
+			if (blockInsn.opcode() == spv::OpKill)
+			{
+				EmitInstruction(blockInsn, state);
+			}
+		}
+	}
+
+	return EmitResult::Continue;
+}
+
+SpirvShader::EmitResult SpirvShader::EmitControlBarrier(InsnIterator insn, EmitState *state) const
+{
+	auto executionScope = spv::Scope(GetConstScalarInt(insn.word(1)));
+	auto semantics = spv::MemorySemanticsMask(GetConstScalarInt(insn.word(3)));
+	// TODO: We probably want to consider the memory scope here. For now,
+	// just always emit the full fence.
+	Fence(semantics);
+
+	switch (executionScope)
+	{
+	case spv::ScopeWorkgroup:
+		Yield(YieldResult::ControlBarrier);
+		break;
+	case spv::ScopeSubgroup:
+		break;
+	default:
+		// See Vulkan 1.1 spec, Appendix A, Validation Rules within a Module.
+		UNREACHABLE("Scope for execution must be limited to Workgroup or Subgroup");
+		break;
+	}
+
+	return EmitResult::Continue;
+}
+
+SpirvShader::EmitResult SpirvShader::EmitPhi(InsnIterator insn, EmitState *state) const
+{
+	auto &function = getFunction(state->function);
+	auto currentBlock = function.getBlock(state->block);
+	if (!currentBlock.isLoopMerge)
+	{
+		// If this is a loop merge block, then don't attempt to update the
+		// phi values from the ins. EmitLoop() has had to take special care
+		// of this phi in order to correctly deal with divergent lanes.
+		StorePhi(state->block, insn, state, currentBlock.ins);
+	}
+	LoadPhi(insn, state);
+	return EmitResult::Continue;
+}
+
+void SpirvShader::LoadPhi(InsnIterator insn, EmitState *state) const
+{
+	auto typeId = Type::ID(insn.word(1));
+	auto type = getType(typeId);
+	auto objectId = Object::ID(insn.word(2));
+
+	auto storageIt = state->routine->phis.find(objectId);
+	ASSERT(storageIt != state->routine->phis.end());
+	auto &storage = storageIt->second;
+
+	auto &dst = state->createIntermediate(objectId, type.sizeInComponents);
+	for(uint32_t i = 0; i < type.sizeInComponents; i++)
+	{
+		dst.move(i, storage[i]);
+	}
+}
+
+void SpirvShader::StorePhi(Block::ID currentBlock, InsnIterator insn, EmitState *state, std::unordered_set<SpirvShader::Block::ID> const& filter) const
+{
+	auto typeId = Type::ID(insn.word(1));
+	auto type = getType(typeId);
+	auto objectId = Object::ID(insn.word(2));
+
+	auto storageIt = state->routine->phis.find(objectId);
+	ASSERT(storageIt != state->routine->phis.end());
+	auto &storage = storageIt->second;
+
+	for (uint32_t w = 3; w < insn.wordCount(); w += 2)
+	{
+		auto varId = Object::ID(insn.word(w + 0));
+		auto blockId = Block::ID(insn.word(w + 1));
+
+		if (filter.count(blockId) == 0)
+		{
+			continue;
+		}
+
+		auto mask = GetActiveLaneMaskEdge(state, blockId, currentBlock);
+		auto in = GenericValue(this, state, varId);
+
+		for (uint32_t i = 0; i < type.sizeInComponents; i++)
+		{
+			storage[i] = As<SIMD::Float>((As<SIMD::Int>(storage[i]) & ~mask) | (in.Int(i) & mask));
+		}
+	}
+}
+
+void SpirvShader::Fence(spv::MemorySemanticsMask semantics) const
+{
+	if (semantics == spv::MemorySemanticsMaskNone)
+	{
+		return; //no-op
+	}
+	rr::Fence(MemoryOrder(semantics));
+}
+
+void SpirvShader::Yield(YieldResult res) const
+{
+	rr::Yield(RValue<Int>(int(res)));
+}
+
+}  // namespace sw
\ No newline at end of file
diff --git a/src/Vulkan/vulkan.vcxproj b/src/Vulkan/vulkan.vcxproj
index 3b6c30e..e77b208 100644
--- a/src/Vulkan/vulkan.vcxproj
+++ b/src/Vulkan/vulkan.vcxproj
@@ -165,6 +165,7 @@
     <ClCompile Include="..\Pipeline\SetupRoutine.cpp" />

     <ClCompile Include="..\Pipeline\ShaderCore.cpp" />

     <ClCompile Include="..\Pipeline\SpirvShader.cpp" />

+    <ClCompile Include="..\Pipeline\SpirvShaderControlFlow.cpp" />

     <ClCompile Include="..\Pipeline\SpirvShaderGLSLstd450.cpp" />

     <ClCompile Include="..\Pipeline\SpirvShaderSampling.cpp" />

     <ClCompile Include="..\Pipeline\SpirvShader_dbg.cpp" />

diff --git a/src/Vulkan/vulkan.vcxproj.filters b/src/Vulkan/vulkan.vcxproj.filters
index 7a0e176..e8e3c09 100644
--- a/src/Vulkan/vulkan.vcxproj.filters
+++ b/src/Vulkan/vulkan.vcxproj.filters
@@ -246,6 +246,9 @@
     <ClCompile Include="..\Pipeline\SpirvShader.cpp">

       <Filter>Source Files\Pipeline</Filter>

     </ClCompile>

+    <ClCompile Include="..\Pipeline\SpirvShaderControlFlow.cpp">

+      <Filter>Source Files\Pipeline</Filter>

+    </ClCompile>

     <ClCompile Include="..\Pipeline\SpirvShaderGLSLstd450.cpp">

       <Filter>Source Files\Pipeline</Filter>

     </ClCompile>