1 // Copyright 2019 The SwiftShader Authors. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //    http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "SpirvShader.hpp"
16 
17 #include "Reactor/Coroutine.hpp"  // rr::Yield
18 
19 #include "ShaderCore.hpp"
20 
21 #include <spirv/unified1/spirv.hpp>
22 
23 #include <queue>
24 
25 namespace sw {
26 
Block(InsnIterator begin,InsnIterator end)27 SpirvShader::Block::Block(InsnIterator begin, InsnIterator end)
28     : begin_(begin)
29     , end_(end)
30 {
31 	// Default to a Simple, this may change later.
32 	kind = Block::Simple;
33 
34 	// Walk the instructions to find the last two of the block.
35 	InsnIterator insns[2];
36 	for(auto insn : *this)
37 	{
38 		insns[0] = insns[1];
39 		insns[1] = insn;
40 	}
41 
42 	switch(insns[1].opcode())
43 	{
44 		case spv::OpBranch:
45 			branchInstruction = insns[1];
46 			outs.emplace(Block::ID(branchInstruction.word(1)));
47 
48 			switch(insns[0].opcode())
49 			{
50 				case spv::OpLoopMerge:
51 					kind = Loop;
52 					mergeInstruction = insns[0];
53 					mergeBlock = Block::ID(mergeInstruction.word(1));
54 					continueTarget = Block::ID(mergeInstruction.word(2));
55 					break;
56 
57 				default:
58 					kind = Block::Simple;
59 					break;
60 			}
61 			break;
62 
63 		case spv::OpBranchConditional:
64 			branchInstruction = insns[1];
65 			outs.emplace(Block::ID(branchInstruction.word(2)));
66 			outs.emplace(Block::ID(branchInstruction.word(3)));
67 
68 			switch(insns[0].opcode())
69 			{
70 				case spv::OpSelectionMerge:
71 					kind = StructuredBranchConditional;
72 					mergeInstruction = insns[0];
73 					mergeBlock = Block::ID(mergeInstruction.word(1));
74 					break;
75 
76 				case spv::OpLoopMerge:
77 					kind = Loop;
78 					mergeInstruction = insns[0];
79 					mergeBlock = Block::ID(mergeInstruction.word(1));
80 					continueTarget = Block::ID(mergeInstruction.word(2));
81 					break;
82 
83 				default:
84 					kind = UnstructuredBranchConditional;
85 					break;
86 			}
87 			break;
88 
89 		case spv::OpSwitch:
90 			branchInstruction = insns[1];
91 			outs.emplace(Block::ID(branchInstruction.word(2)));
92 			for(uint32_t w = 4; w < branchInstruction.wordCount(); w += 2)
93 			{
94 				outs.emplace(Block::ID(branchInstruction.word(w)));
95 			}
96 
97 			switch(insns[0].opcode())
98 			{
99 				case spv::OpSelectionMerge:
100 					kind = StructuredSwitch;
101 					mergeInstruction = insns[0];
102 					mergeBlock = Block::ID(mergeInstruction.word(1));
103 					break;
104 
105 				default:
106 					kind = UnstructuredSwitch;
107 					break;
108 			}
109 			break;
110 
111 		default:
112 			break;
113 	}
114 }
115 
TraverseReachableBlocks(Block::ID id,SpirvShader::Block::Set & reachable) const116 void SpirvShader::Function::TraverseReachableBlocks(Block::ID id, SpirvShader::Block::Set &reachable) const
117 {
118 	if(reachable.count(id) == 0)
119 	{
120 		reachable.emplace(id);
121 		for(auto out : getBlock(id).outs)
122 		{
123 			TraverseReachableBlocks(out, reachable);
124 		}
125 	}
126 }
127 
AssignBlockFields()128 void SpirvShader::Function::AssignBlockFields()
129 {
130 	Block::Set reachable;
131 	TraverseReachableBlocks(entry, reachable);
132 
133 	for(auto &it : blocks)
134 	{
135 		auto &blockId = it.first;
136 		auto &block = it.second;
137 		if(reachable.count(blockId) > 0)
138 		{
139 			for(auto &outId : it.second.outs)
140 			{
141 				auto outIt = blocks.find(outId);
142 				ASSERT_MSG(outIt != blocks.end(), "Block %d has a non-existent out %d", blockId.value(), outId.value());
143 				auto &out = outIt->second;
144 				out.ins.emplace(blockId);
145 			}
146 			if(block.kind == Block::Loop)
147 			{
148 				auto mergeIt = blocks.find(block.mergeBlock);
149 				ASSERT_MSG(mergeIt != blocks.end(), "Loop block %d has a non-existent merge block %d", blockId.value(), block.mergeBlock.value());
150 				mergeIt->second.isLoopMerge = true;
151 			}
152 		}
153 	}
154 }
155 
ForeachBlockDependency(Block::ID blockId,std::function<void (Block::ID)> f) const156 void SpirvShader::Function::ForeachBlockDependency(Block::ID blockId, std::function<void(Block::ID)> f) const
157 {
158 	auto block = getBlock(blockId);
159 	for(auto dep : block.ins)
160 	{
161 		if(block.kind != Block::Loop ||                  // if not a loop...
162 		   !ExistsPath(blockId, dep, block.mergeBlock))  // or a loop and not a loop back edge
163 		{
164 			f(dep);
165 		}
166 	}
167 }
168 
ExistsPath(Block::ID from,Block::ID to,Block::ID notPassingThrough) const169 bool SpirvShader::Function::ExistsPath(Block::ID from, Block::ID to, Block::ID notPassingThrough) const
170 {
171 	// TODO: Optimize: This can be cached on the block.
172 	Block::Set seen;
173 	seen.emplace(notPassingThrough);
174 
175 	std::queue<Block::ID> pending;
176 	pending.emplace(from);
177 
178 	while(pending.size() > 0)
179 	{
180 		auto id = pending.front();
181 		pending.pop();
182 		for(auto out : getBlock(id).outs)
183 		{
184 			if(seen.count(out) != 0) { continue; }
185 			if(out == to) { return true; }
186 			pending.emplace(out);
187 		}
188 		seen.emplace(id);
189 	}
190 
191 	return false;
192 }
193 
addOutputActiveLaneMaskEdge(Block::ID to,RValue<SIMD::Int> mask)194 void SpirvShader::EmitState::addOutputActiveLaneMaskEdge(Block::ID to, RValue<SIMD::Int> mask)
195 {
196 	addActiveLaneMaskEdge(block, to, mask & activeLaneMask());
197 }
198 
addActiveLaneMaskEdge(Block::ID from,Block::ID to,RValue<SIMD::Int> mask)199 void SpirvShader::EmitState::addActiveLaneMaskEdge(Block::ID from, Block::ID to, RValue<SIMD::Int> mask)
200 {
201 	auto edge = Block::Edge{ from, to };
202 	auto it = edgeActiveLaneMasks.find(edge);
203 	if(it == edgeActiveLaneMasks.end())
204 	{
205 		edgeActiveLaneMasks.emplace(edge, mask);
206 	}
207 	else
208 	{
209 		auto combined = it->second | mask;
210 		edgeActiveLaneMasks.erase(edge);
211 		edgeActiveLaneMasks.emplace(edge, combined);
212 	}
213 }
214 
GetActiveLaneMaskEdge(EmitState * state,Block::ID from,Block::ID to) const215 RValue<SIMD::Int> SpirvShader::GetActiveLaneMaskEdge(EmitState *state, Block::ID from, Block::ID to) const
216 {
217 	auto edge = Block::Edge{ from, to };
218 	auto it = state->edgeActiveLaneMasks.find(edge);
219 	ASSERT_MSG(it != state->edgeActiveLaneMasks.end(), "Could not find edge %d -> %d", from.value(), to.value());
220 	return it->second;
221 }
222 
EmitBlocks(Block::ID id,EmitState * state,Block::ID ignore) const223 void SpirvShader::EmitBlocks(Block::ID id, EmitState *state, Block::ID ignore /* = 0 */) const
224 {
225 	auto oldPending = state->pending;
226 	auto &function = getFunction(state->function);
227 
228 	std::deque<Block::ID> pending;
229 	state->pending = &pending;
230 	pending.push_front(id);
231 	while(pending.size() > 0)
232 	{
233 		auto id = pending.front();
234 
235 		auto const &block = function.getBlock(id);
236 		if(id == ignore)
237 		{
238 			pending.pop_front();
239 			continue;
240 		}
241 
242 		// Ensure all dependency blocks have been generated.
243 		auto depsDone = true;
244 		function.ForeachBlockDependency(id, [&](Block::ID dep) {
245 			if(state->visited.count(dep) == 0)
246 			{
247 				state->pending->push_front(dep);
248 				depsDone = false;
249 			}
250 		});
251 
252 		if(!depsDone)
253 		{
254 			continue;
255 		}
256 
257 		pending.pop_front();
258 
259 		state->block = id;
260 
261 		switch(block.kind)
262 		{
263 			case Block::Simple:
264 			case Block::StructuredBranchConditional:
265 			case Block::UnstructuredBranchConditional:
266 			case Block::StructuredSwitch:
267 			case Block::UnstructuredSwitch:
268 				EmitNonLoop(state);
269 				break;
270 
271 			case Block::Loop:
272 				EmitLoop(state);
273 				break;
274 
275 			default:
276 				UNREACHABLE("Unexpected Block Kind: %d", int(block.kind));
277 		}
278 	}
279 
280 	state->pending = oldPending;
281 }
282 
EmitNonLoop(EmitState * state) const283 void SpirvShader::EmitNonLoop(EmitState *state) const
284 {
285 	auto &function = getFunction(state->function);
286 	auto blockId = state->block;
287 	auto block = function.getBlock(blockId);
288 
289 	if(!state->visited.emplace(blockId).second)
290 	{
291 		return;  // Already generated this block.
292 	}
293 
294 	if(blockId != function.entry)
295 	{
296 		// Set the activeLaneMask.
297 		SIMD::Int activeLaneMask(0);
298 		for(auto in : block.ins)
299 		{
300 			auto inMask = GetActiveLaneMaskEdge(state, in, blockId);
301 			activeLaneMask |= inMask;
302 		}
303 		SetActiveLaneMask(activeLaneMask, state);
304 	}
305 
306 	EmitInstructions(block.begin(), block.end(), state);
307 
308 	for(auto out : block.outs)
309 	{
310 		if(state->visited.count(out) == 0)
311 		{
312 			state->pending->push_back(out);
313 		}
314 	}
315 }
316 
EmitLoop(EmitState * state) const317 void SpirvShader::EmitLoop(EmitState *state) const
318 {
319 	auto &function = getFunction(state->function);
320 	auto blockId = state->block;
321 	auto &block = function.getBlock(blockId);
322 	auto mergeBlockId = block.mergeBlock;
323 	auto &mergeBlock = function.getBlock(mergeBlockId);
324 
325 	if(!state->visited.emplace(blockId).second)
326 	{
327 		return;  // Already emitted this loop.
328 	}
329 
330 	// Gather all the blocks that make up the loop.
331 	std::unordered_set<Block::ID> loopBlocks;
332 	loopBlocks.emplace(block.mergeBlock);
333 	function.TraverseReachableBlocks(blockId, loopBlocks);
334 
335 	// incomingBlocks are block ins that are not back-edges.
336 	std::unordered_set<Block::ID> incomingBlocks;
337 	for(auto in : block.ins)
338 	{
339 		if(loopBlocks.count(in) == 0)
340 		{
341 			incomingBlocks.emplace(in);
342 		}
343 	}
344 
345 	// Emit the loop phi instructions, and initialize them with a value from
346 	// the incoming blocks.
347 	for(auto insn = block.begin(); insn != block.mergeInstruction; insn++)
348 	{
349 		if(insn.opcode() == spv::OpPhi)
350 		{
351 			StorePhi(blockId, insn, state, incomingBlocks);
352 		}
353 	}
354 
355 	// loopActiveLaneMask is the mask of lanes that are continuing to loop.
356 	// This is initialized with the incoming active lane masks.
357 	SIMD::Int loopActiveLaneMask = SIMD::Int(0);
358 	for(auto in : incomingBlocks)
359 	{
360 		loopActiveLaneMask |= GetActiveLaneMaskEdge(state, in, blockId);
361 	}
362 
363 	// mergeActiveLaneMasks contains edge lane masks for the merge block.
364 	// This is the union of all edge masks across all iterations of the loop.
365 	std::unordered_map<Block::ID, SIMD::Int> mergeActiveLaneMasks;
366 	for(auto in : function.getBlock(mergeBlockId).ins)
367 	{
368 		mergeActiveLaneMasks.emplace(in, SIMD::Int(0));
369 	}
370 
371 	// Create the loop basic blocks
372 	auto headerBasicBlock = Nucleus::createBasicBlock();
373 	auto mergeBasicBlock = Nucleus::createBasicBlock();
374 
375 	// Start emitting code inside the loop.
376 	Nucleus::createBr(headerBasicBlock);
377 	Nucleus::setInsertBlock(headerBasicBlock);
378 
379 	// Load the active lane mask.
380 	SetActiveLaneMask(loopActiveLaneMask, state);
381 
382 	// Emit the non-phi loop header block's instructions.
383 	for(auto insn = block.begin(); insn != block.end(); insn++)
384 	{
385 		if(insn.opcode() == spv::OpPhi)
386 		{
387 			LoadPhi(insn, state);
388 		}
389 		else
390 		{
391 			EmitInstruction(insn, state);
392 		}
393 	}
394 
395 	// Emit all blocks between the loop header and the merge block, but
396 	// don't emit the merge block yet.
397 	for(auto out : block.outs)
398 	{
399 		EmitBlocks(out, state, mergeBlockId);
400 	}
401 
402 	// Restore current block id after emitting loop blocks.
403 	state->block = blockId;
404 
405 	// Rebuild the loopActiveLaneMask from the loop back edges.
406 	loopActiveLaneMask = SIMD::Int(0);
407 	for(auto in : block.ins)
408 	{
409 		if(function.ExistsPath(blockId, in, mergeBlockId))
410 		{
411 			loopActiveLaneMask |= GetActiveLaneMaskEdge(state, in, blockId);
412 		}
413 	}
414 
415 	// Add active lanes to the merge lane mask.
416 	for(auto in : function.getBlock(mergeBlockId).ins)
417 	{
418 		auto edge = Block::Edge{ in, mergeBlockId };
419 		auto it = state->edgeActiveLaneMasks.find(edge);
420 		if(it != state->edgeActiveLaneMasks.end())
421 		{
422 			mergeActiveLaneMasks[in] |= it->second;
423 		}
424 	}
425 
426 	// Update loop phi values.
427 	for(auto insn = block.begin(); insn != block.mergeInstruction; insn++)
428 	{
429 		if(insn.opcode() == spv::OpPhi)
430 		{
431 			StorePhi(blockId, insn, state, loopBlocks);
432 		}
433 	}
434 
435 	// Use the [loop -> merge] active lane masks to update the phi values in
436 	// the merge block. We need to do this to handle divergent control flow
437 	// in the loop.
438 	//
439 	// Consider the following:
440 	//
441 	//     int phi_source = 0;
442 	//     for(uint i = 0; i < 4; i++)
443 	//     {
444 	//         phi_source = 0;
445 	//         if(gl_GlobalInvocationID.x % 4 == i) // divergent control flow
446 	//         {
447 	//             phi_source = 42; // single lane assignment.
448 	//             break; // activeLaneMask for [loop->merge] is active for a single lane.
449 	//         }
450 	//         // -- we are here --
451 	//     }
452 	//     // merge block
453 	//     int phi = phi_source; // OpPhi
454 	//
455 	// In this example, with each iteration of the loop, phi_source will
456 	// only have a single lane assigned. However by 'phi' value in the merge
457 	// block needs to be assigned the union of all the per-lane assignments
458 	// of phi_source when that lane exited the loop.
459 	for(auto insn = mergeBlock.begin(); insn != mergeBlock.end(); insn++)
460 	{
461 		if(insn.opcode() == spv::OpPhi)
462 		{
463 			StorePhi(mergeBlockId, insn, state, loopBlocks);
464 		}
465 	}
466 
467 	// Loop body now done.
468 	// If any lanes are still active, jump back to the loop header,
469 	// otherwise jump to the merge block.
470 	Nucleus::createCondBr(AnyTrue(loopActiveLaneMask).value, headerBasicBlock, mergeBasicBlock);
471 
472 	// Continue emitting from the merge block.
473 	Nucleus::setInsertBlock(mergeBasicBlock);
474 	state->pending->push_back(mergeBlockId);
475 	for(const auto &it : mergeActiveLaneMasks)
476 	{
477 		state->addActiveLaneMaskEdge(it.first, mergeBlockId, it.second);
478 	}
479 }
480 
EmitBranch(InsnIterator insn,EmitState * state) const481 SpirvShader::EmitResult SpirvShader::EmitBranch(InsnIterator insn, EmitState *state) const
482 {
483 	auto target = Block::ID(insn.word(1));
484 	state->addActiveLaneMaskEdge(state->block, target, state->activeLaneMask());
485 	return EmitResult::Terminator;
486 }
487 
EmitBranchConditional(InsnIterator insn,EmitState * state) const488 SpirvShader::EmitResult SpirvShader::EmitBranchConditional(InsnIterator insn, EmitState *state) const
489 {
490 	auto &function = getFunction(state->function);
491 	auto block = function.getBlock(state->block);
492 	ASSERT(block.branchInstruction == insn);
493 
494 	auto condId = Object::ID(block.branchInstruction.word(1));
495 	auto trueBlockId = Block::ID(block.branchInstruction.word(2));
496 	auto falseBlockId = Block::ID(block.branchInstruction.word(3));
497 
498 	auto cond = GenericValue(this, state, condId);
499 	ASSERT_MSG(getType(cond.type).sizeInComponents == 1, "Condition must be a Boolean type scalar");
500 
501 	// TODO: Optimize for case where all lanes take same path.
502 
503 	state->addOutputActiveLaneMaskEdge(trueBlockId, cond.Int(0));
504 	state->addOutputActiveLaneMaskEdge(falseBlockId, ~cond.Int(0));
505 
506 	return EmitResult::Terminator;
507 }
508 
EmitSwitch(InsnIterator insn,EmitState * state) const509 SpirvShader::EmitResult SpirvShader::EmitSwitch(InsnIterator insn, EmitState *state) const
510 {
511 	auto &function = getFunction(state->function);
512 	auto block = function.getBlock(state->block);
513 	ASSERT(block.branchInstruction == insn);
514 
515 	auto selId = Object::ID(block.branchInstruction.word(1));
516 
517 	auto sel = GenericValue(this, state, selId);
518 	ASSERT_MSG(getType(sel.type).sizeInComponents == 1, "Selector must be a scalar");
519 
520 	auto numCases = (block.branchInstruction.wordCount() - 3) / 2;
521 
522 	// TODO: Optimize for case where all lanes take same path.
523 
524 	SIMD::Int defaultLaneMask = state->activeLaneMask();
525 
526 	// Gather up the case label matches and calculate defaultLaneMask.
527 	std::vector<RValue<SIMD::Int>> caseLabelMatches;
528 	caseLabelMatches.reserve(numCases);
529 	for(uint32_t i = 0; i < numCases; i++)
530 	{
531 		auto label = block.branchInstruction.word(i * 2 + 3);
532 		auto caseBlockId = Block::ID(block.branchInstruction.word(i * 2 + 4));
533 		auto caseLabelMatch = CmpEQ(sel.Int(0), SIMD::Int(label));
534 		state->addOutputActiveLaneMaskEdge(caseBlockId, caseLabelMatch);
535 		defaultLaneMask &= ~caseLabelMatch;
536 	}
537 
538 	auto defaultBlockId = Block::ID(block.branchInstruction.word(2));
539 	state->addOutputActiveLaneMaskEdge(defaultBlockId, defaultLaneMask);
540 
541 	return EmitResult::Terminator;
542 }
543 
EmitUnreachable(InsnIterator insn,EmitState * state) const544 SpirvShader::EmitResult SpirvShader::EmitUnreachable(InsnIterator insn, EmitState *state) const
545 {
546 	// TODO: Log something in this case?
547 	SetActiveLaneMask(SIMD::Int(0), state);
548 	return EmitResult::Terminator;
549 }
550 
EmitReturn(InsnIterator insn,EmitState * state) const551 SpirvShader::EmitResult SpirvShader::EmitReturn(InsnIterator insn, EmitState *state) const
552 {
553 	SetActiveLaneMask(SIMD::Int(0), state);
554 	return EmitResult::Terminator;
555 }
556 
EmitKill(InsnIterator insn,EmitState * state) const557 SpirvShader::EmitResult SpirvShader::EmitKill(InsnIterator insn, EmitState *state) const
558 {
559 	state->routine->killMask |= SignMask(state->activeLaneMask());
560 	SetActiveLaneMask(SIMD::Int(0), state);
561 	return EmitResult::Terminator;
562 }
563 
EmitFunctionCall(InsnIterator insn,EmitState * state) const564 SpirvShader::EmitResult SpirvShader::EmitFunctionCall(InsnIterator insn, EmitState *state) const
565 {
566 	auto functionId = Function::ID(insn.word(3));
567 	const auto &functionIt = functions.find(functionId);
568 	ASSERT(functionIt != functions.end());
569 	auto &function = functionIt->second;
570 
571 	// TODO(b/141246700): Add full support for spv::OpFunctionCall
572 	// The only supported function is a single OpKill wrapped in a
573 	// function, as a result of the "wrap OpKill" SPIRV-Tools pass
574 	ASSERT(function.blocks.size() == 1);
575 	spv::Op wrapOpKill[] = { spv::OpLabel, spv::OpKill };
576 
577 	for(const auto &block : function.blocks)
578 	{
579 		int insnNumber = 0;
580 		for(auto blockInsn : block.second)
581 		{
582 			if(insnNumber > 1)
583 			{
584 				UNIMPLEMENTED("b/141246700: Function block number of instructions: %d", insnNumber);  // FIXME(b/141246700)
585 				return EmitResult::Continue;
586 			}
587 
588 			if(blockInsn.opcode() != wrapOpKill[insnNumber++])
589 			{
590 				UNIMPLEMENTED("b/141246700: Function block instruction %d : %s", insnNumber - 1, OpcodeName(blockInsn.opcode()).c_str());  // FIXME(b/141246700)
591 				return EmitResult::Continue;
592 			}
593 
594 			if(blockInsn.opcode() == spv::OpKill)
595 			{
596 				EmitInstruction(blockInsn, state);
597 			}
598 		}
599 	}
600 
601 	return EmitResult::Continue;
602 }
603 
EmitControlBarrier(InsnIterator insn,EmitState * state) const604 SpirvShader::EmitResult SpirvShader::EmitControlBarrier(InsnIterator insn, EmitState *state) const
605 {
606 	auto executionScope = spv::Scope(GetConstScalarInt(insn.word(1)));
607 	auto semantics = spv::MemorySemanticsMask(GetConstScalarInt(insn.word(3)));
608 	// TODO: We probably want to consider the memory scope here. For now,
609 	// just always emit the full fence.
610 	Fence(semantics);
611 
612 	switch(executionScope)
613 	{
614 		case spv::ScopeWorkgroup:
615 			Yield(YieldResult::ControlBarrier);
616 			break;
617 		case spv::ScopeSubgroup:
618 			break;
619 		default:
620 			// See Vulkan 1.1 spec, Appendix A, Validation Rules within a Module.
621 			UNREACHABLE("Scope for execution must be limited to Workgroup or Subgroup");
622 			break;
623 	}
624 
625 	return EmitResult::Continue;
626 }
627 
EmitPhi(InsnIterator insn,EmitState * state) const628 SpirvShader::EmitResult SpirvShader::EmitPhi(InsnIterator insn, EmitState *state) const
629 {
630 	auto &function = getFunction(state->function);
631 	auto currentBlock = function.getBlock(state->block);
632 	if(!currentBlock.isLoopMerge)
633 	{
634 		// If this is a loop merge block, then don't attempt to update the
635 		// phi values from the ins. EmitLoop() has had to take special care
636 		// of this phi in order to correctly deal with divergent lanes.
637 		StorePhi(state->block, insn, state, currentBlock.ins);
638 	}
639 	LoadPhi(insn, state);
640 	return EmitResult::Continue;
641 }
642 
LoadPhi(InsnIterator insn,EmitState * state) const643 void SpirvShader::LoadPhi(InsnIterator insn, EmitState *state) const
644 {
645 	auto typeId = Type::ID(insn.word(1));
646 	auto type = getType(typeId);
647 	auto objectId = Object::ID(insn.word(2));
648 
649 	auto storageIt = state->routine->phis.find(objectId);
650 	ASSERT(storageIt != state->routine->phis.end());
651 	auto &storage = storageIt->second;
652 
653 	auto &dst = state->createIntermediate(objectId, type.sizeInComponents);
654 	for(uint32_t i = 0; i < type.sizeInComponents; i++)
655 	{
656 		dst.move(i, storage[i]);
657 	}
658 }
659 
StorePhi(Block::ID currentBlock,InsnIterator insn,EmitState * state,std::unordered_set<SpirvShader::Block::ID> const & filter) const660 void SpirvShader::StorePhi(Block::ID currentBlock, InsnIterator insn, EmitState *state, std::unordered_set<SpirvShader::Block::ID> const &filter) const
661 {
662 	auto typeId = Type::ID(insn.word(1));
663 	auto type = getType(typeId);
664 	auto objectId = Object::ID(insn.word(2));
665 
666 	auto storageIt = state->routine->phis.find(objectId);
667 	ASSERT(storageIt != state->routine->phis.end());
668 	auto &storage = storageIt->second;
669 
670 	for(uint32_t w = 3; w < insn.wordCount(); w += 2)
671 	{
672 		auto varId = Object::ID(insn.word(w + 0));
673 		auto blockId = Block::ID(insn.word(w + 1));
674 
675 		if(filter.count(blockId) == 0)
676 		{
677 			continue;
678 		}
679 
680 		auto mask = GetActiveLaneMaskEdge(state, blockId, currentBlock);
681 		auto in = GenericValue(this, state, varId);
682 
683 		for(uint32_t i = 0; i < type.sizeInComponents; i++)
684 		{
685 			storage[i] = As<SIMD::Float>((As<SIMD::Int>(storage[i]) & ~mask) | (in.Int(i) & mask));
686 		}
687 	}
688 }
689 
Fence(spv::MemorySemanticsMask semantics) const690 void SpirvShader::Fence(spv::MemorySemanticsMask semantics) const
691 {
692 	if(semantics == spv::MemorySemanticsMaskNone)
693 	{
694 		return;  //no-op
695 	}
696 	rr::Fence(MemoryOrder(semantics));
697 }
698 
Yield(YieldResult res) const699 void SpirvShader::Yield(YieldResult res) const
700 {
701 	rr::Yield(RValue<Int>(int(res)));
702 }
703 
SetActiveLaneMask(RValue<SIMD::Int> mask,EmitState * state) const704 void SpirvShader::SetActiveLaneMask(RValue<SIMD::Int> mask, EmitState *state) const
705 {
706 	state->activeLaneMaskValue = mask.value;
707 	dbgUpdateActiveLaneMask(mask, state);
708 }
709 
710 }  // namespace sw