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(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(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