blob: a0ee8b299530ab886e4d48890adcc906a76c4424 [file] [log] [blame]
Ben Claytonb4274002019-11-28 11:47:29 +00001// 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"
Ben Claytonfc951cd2019-05-15 17:16:56 +010016#include "SpirvShaderDebug.hpp"
Ben Claytonb4274002019-11-28 11:47:29 +000017
Ben Claytonbc1c0672019-12-17 20:37:37 +000018#include "Reactor/Coroutine.hpp" // rr::Yield
Ben Claytonb4274002019-11-28 11:47:29 +000019
20#include "ShaderCore.hpp"
21
22#include <spirv/unified1/spirv.hpp>
Ben Claytonb4274002019-11-28 11:47:29 +000023
24#include <queue>
25
Ben Clayton0d6791c2020-04-22 21:55:27 +010026#include <fstream>
27#include <iostream>
28
Ben Claytonb4274002019-11-28 11:47:29 +000029namespace sw {
30
Ben Claytonbc1c0672019-12-17 20:37:37 +000031SpirvShader::Block::Block(InsnIterator begin, InsnIterator end)
32 : begin_(begin)
33 , end_(end)
Ben Claytonb4274002019-11-28 11:47:29 +000034{
35 // Default to a Simple, this may change later.
36 kind = Block::Simple;
37
38 // Walk the instructions to find the last two of the block.
39 InsnIterator insns[2];
Nicolas Capens81bc9d92019-12-16 15:05:57 -050040 for(auto insn : *this)
Ben Claytonb4274002019-11-28 11:47:29 +000041 {
42 insns[0] = insns[1];
43 insns[1] = insn;
44 }
45
Nicolas Capens81bc9d92019-12-16 15:05:57 -050046 switch(insns[1].opcode())
Ben Claytonb4274002019-11-28 11:47:29 +000047 {
48 case spv::OpBranch:
49 branchInstruction = insns[1];
50 outs.emplace(Block::ID(branchInstruction.word(1)));
51
Nicolas Capens81bc9d92019-12-16 15:05:57 -050052 switch(insns[0].opcode())
Ben Claytonb4274002019-11-28 11:47:29 +000053 {
54 case spv::OpLoopMerge:
55 kind = Loop;
56 mergeInstruction = insns[0];
57 mergeBlock = Block::ID(mergeInstruction.word(1));
58 continueTarget = Block::ID(mergeInstruction.word(2));
59 break;
60
61 default:
62 kind = Block::Simple;
63 break;
64 }
65 break;
66
67 case spv::OpBranchConditional:
68 branchInstruction = insns[1];
69 outs.emplace(Block::ID(branchInstruction.word(2)));
70 outs.emplace(Block::ID(branchInstruction.word(3)));
71
Nicolas Capens81bc9d92019-12-16 15:05:57 -050072 switch(insns[0].opcode())
Ben Claytonb4274002019-11-28 11:47:29 +000073 {
74 case spv::OpSelectionMerge:
75 kind = StructuredBranchConditional;
76 mergeInstruction = insns[0];
77 mergeBlock = Block::ID(mergeInstruction.word(1));
78 break;
79
80 case spv::OpLoopMerge:
81 kind = Loop;
82 mergeInstruction = insns[0];
83 mergeBlock = Block::ID(mergeInstruction.word(1));
84 continueTarget = Block::ID(mergeInstruction.word(2));
85 break;
86
87 default:
88 kind = UnstructuredBranchConditional;
89 break;
90 }
91 break;
92
93 case spv::OpSwitch:
94 branchInstruction = insns[1];
95 outs.emplace(Block::ID(branchInstruction.word(2)));
Nicolas Capens81bc9d92019-12-16 15:05:57 -050096 for(uint32_t w = 4; w < branchInstruction.wordCount(); w += 2)
Ben Claytonb4274002019-11-28 11:47:29 +000097 {
98 outs.emplace(Block::ID(branchInstruction.word(w)));
99 }
100
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500101 switch(insns[0].opcode())
Ben Claytonb4274002019-11-28 11:47:29 +0000102 {
103 case spv::OpSelectionMerge:
104 kind = StructuredSwitch;
105 mergeInstruction = insns[0];
106 mergeBlock = Block::ID(mergeInstruction.word(1));
107 break;
108
109 default:
110 kind = UnstructuredSwitch;
111 break;
112 }
113 break;
114
115 default:
116 break;
117 }
118}
119
Ben Claytonbc1c0672019-12-17 20:37:37 +0000120void SpirvShader::Function::TraverseReachableBlocks(Block::ID id, SpirvShader::Block::Set &reachable) const
Ben Claytonb4274002019-11-28 11:47:29 +0000121{
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500122 if(reachable.count(id) == 0)
Ben Claytonb4274002019-11-28 11:47:29 +0000123 {
124 reachable.emplace(id);
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500125 for(auto out : getBlock(id).outs)
Ben Claytonb4274002019-11-28 11:47:29 +0000126 {
127 TraverseReachableBlocks(out, reachable);
128 }
129 }
130}
131
132void SpirvShader::Function::AssignBlockFields()
133{
134 Block::Set reachable;
135 TraverseReachableBlocks(entry, reachable);
136
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500137 for(auto &it : blocks)
Ben Claytonb4274002019-11-28 11:47:29 +0000138 {
139 auto &blockId = it.first;
140 auto &block = it.second;
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500141 if(reachable.count(blockId) > 0)
Ben Claytonb4274002019-11-28 11:47:29 +0000142 {
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500143 for(auto &outId : it.second.outs)
Ben Claytonb4274002019-11-28 11:47:29 +0000144 {
145 auto outIt = blocks.find(outId);
146 ASSERT_MSG(outIt != blocks.end(), "Block %d has a non-existent out %d", blockId.value(), outId.value());
147 auto &out = outIt->second;
148 out.ins.emplace(blockId);
149 }
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500150 if(block.kind == Block::Loop)
Ben Claytonb4274002019-11-28 11:47:29 +0000151 {
152 auto mergeIt = blocks.find(block.mergeBlock);
153 ASSERT_MSG(mergeIt != blocks.end(), "Loop block %d has a non-existent merge block %d", blockId.value(), block.mergeBlock.value());
154 mergeIt->second.isLoopMerge = true;
155 }
156 }
157 }
158}
159
160void SpirvShader::Function::ForeachBlockDependency(Block::ID blockId, std::function<void(Block::ID)> f) const
161{
162 auto block = getBlock(blockId);
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500163 for(auto dep : block.ins)
Ben Claytonb4274002019-11-28 11:47:29 +0000164 {
Ben Claytonbc1c0672019-12-17 20:37:37 +0000165 if(block.kind != Block::Loop || // if not a loop...
166 !ExistsPath(blockId, dep, block.mergeBlock)) // or a loop and not a loop back edge
Ben Claytonb4274002019-11-28 11:47:29 +0000167 {
168 f(dep);
169 }
170 }
171}
172
173bool SpirvShader::Function::ExistsPath(Block::ID from, Block::ID to, Block::ID notPassingThrough) const
174{
175 // TODO: Optimize: This can be cached on the block.
176 Block::Set seen;
177 seen.emplace(notPassingThrough);
178
179 std::queue<Block::ID> pending;
180 pending.emplace(from);
181
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500182 while(pending.size() > 0)
Ben Claytonb4274002019-11-28 11:47:29 +0000183 {
184 auto id = pending.front();
185 pending.pop();
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500186 for(auto out : getBlock(id).outs)
Ben Claytonb4274002019-11-28 11:47:29 +0000187 {
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500188 if(seen.count(out) != 0) { continue; }
189 if(out == to) { return true; }
Ben Claytonb4274002019-11-28 11:47:29 +0000190 pending.emplace(out);
191 }
192 seen.emplace(id);
193 }
194
195 return false;
196}
197
198void SpirvShader::EmitState::addOutputActiveLaneMaskEdge(Block::ID to, RValue<SIMD::Int> mask)
199{
200 addActiveLaneMaskEdge(block, to, mask & activeLaneMask());
201}
202
203void SpirvShader::EmitState::addActiveLaneMaskEdge(Block::ID from, Block::ID to, RValue<SIMD::Int> mask)
204{
Ben Claytonbc1c0672019-12-17 20:37:37 +0000205 auto edge = Block::Edge{ from, to };
Ben Claytonb4274002019-11-28 11:47:29 +0000206 auto it = edgeActiveLaneMasks.find(edge);
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500207 if(it == edgeActiveLaneMasks.end())
Ben Claytonb4274002019-11-28 11:47:29 +0000208 {
209 edgeActiveLaneMasks.emplace(edge, mask);
210 }
211 else
212 {
213 auto combined = it->second | mask;
214 edgeActiveLaneMasks.erase(edge);
215 edgeActiveLaneMasks.emplace(edge, combined);
216 }
217}
218
219RValue<SIMD::Int> SpirvShader::GetActiveLaneMaskEdge(EmitState *state, Block::ID from, Block::ID to) const
220{
Ben Claytonbc1c0672019-12-17 20:37:37 +0000221 auto edge = Block::Edge{ from, to };
Ben Claytonb4274002019-11-28 11:47:29 +0000222 auto it = state->edgeActiveLaneMasks.find(edge);
223 ASSERT_MSG(it != state->edgeActiveLaneMasks.end(), "Could not find edge %d -> %d", from.value(), to.value());
224 return it->second;
225}
226
227void SpirvShader::EmitBlocks(Block::ID id, EmitState *state, Block::ID ignore /* = 0 */) const
228{
229 auto oldPending = state->pending;
230 auto &function = getFunction(state->function);
231
232 std::deque<Block::ID> pending;
233 state->pending = &pending;
234 pending.push_front(id);
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500235 while(pending.size() > 0)
Ben Claytonb4274002019-11-28 11:47:29 +0000236 {
237 auto id = pending.front();
238
239 auto const &block = function.getBlock(id);
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500240 if(id == ignore)
Ben Claytonb4274002019-11-28 11:47:29 +0000241 {
242 pending.pop_front();
243 continue;
244 }
245
246 // Ensure all dependency blocks have been generated.
247 auto depsDone = true;
Ben Claytonbc1c0672019-12-17 20:37:37 +0000248 function.ForeachBlockDependency(id, [&](Block::ID dep) {
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500249 if(state->visited.count(dep) == 0)
Ben Claytonb4274002019-11-28 11:47:29 +0000250 {
251 state->pending->push_front(dep);
252 depsDone = false;
253 }
254 });
255
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500256 if(!depsDone)
Ben Claytonb4274002019-11-28 11:47:29 +0000257 {
258 continue;
259 }
260
261 pending.pop_front();
262
263 state->block = id;
264
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500265 switch(block.kind)
Ben Claytonb4274002019-11-28 11:47:29 +0000266 {
267 case Block::Simple:
268 case Block::StructuredBranchConditional:
269 case Block::UnstructuredBranchConditional:
270 case Block::StructuredSwitch:
271 case Block::UnstructuredSwitch:
272 EmitNonLoop(state);
273 break;
274
275 case Block::Loop:
276 EmitLoop(state);
277 break;
278
279 default:
280 UNREACHABLE("Unexpected Block Kind: %d", int(block.kind));
281 }
282 }
283
284 state->pending = oldPending;
285}
286
287void SpirvShader::EmitNonLoop(EmitState *state) const
288{
289 auto &function = getFunction(state->function);
290 auto blockId = state->block;
291 auto block = function.getBlock(blockId);
292
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500293 if(!state->visited.emplace(blockId).second)
Ben Claytonb4274002019-11-28 11:47:29 +0000294 {
Ben Claytonbc1c0672019-12-17 20:37:37 +0000295 return; // Already generated this block.
Ben Claytonb4274002019-11-28 11:47:29 +0000296 }
297
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500298 if(blockId != function.entry)
Ben Claytonb4274002019-11-28 11:47:29 +0000299 {
300 // Set the activeLaneMask.
301 SIMD::Int activeLaneMask(0);
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500302 for(auto in : block.ins)
Ben Claytonb4274002019-11-28 11:47:29 +0000303 {
304 auto inMask = GetActiveLaneMaskEdge(state, in, blockId);
Ben Claytonfc951cd2019-05-15 17:16:56 +0100305 SPIRV_SHADER_DBG("Block {0} -> {1} mask: {2}", in, blockId, inMask);
Ben Claytonb4274002019-11-28 11:47:29 +0000306 activeLaneMask |= inMask;
307 }
Ben Claytonfc951cd2019-05-15 17:16:56 +0100308 SPIRV_SHADER_DBG("Block {0} mask: {1}", blockId, activeLaneMask);
Ben Clayton8a7067d2020-01-08 12:30:06 +0000309 SetActiveLaneMask(activeLaneMask, state);
Ben Claytonb4274002019-11-28 11:47:29 +0000310 }
311
312 EmitInstructions(block.begin(), block.end(), state);
313
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500314 for(auto out : block.outs)
Ben Claytonb4274002019-11-28 11:47:29 +0000315 {
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500316 if(state->visited.count(out) == 0)
Ben Claytonb4274002019-11-28 11:47:29 +0000317 {
318 state->pending->push_back(out);
319 }
320 }
Ben Claytonfc951cd2019-05-15 17:16:56 +0100321
322 SPIRV_SHADER_DBG("Block {0} done", blockId);
Ben Claytonb4274002019-11-28 11:47:29 +0000323}
324
325void SpirvShader::EmitLoop(EmitState *state) const
326{
327 auto &function = getFunction(state->function);
328 auto blockId = state->block;
329 auto &block = function.getBlock(blockId);
330 auto mergeBlockId = block.mergeBlock;
331 auto &mergeBlock = function.getBlock(mergeBlockId);
332
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500333 if(!state->visited.emplace(blockId).second)
Ben Claytonb4274002019-11-28 11:47:29 +0000334 {
Ben Claytonbc1c0672019-12-17 20:37:37 +0000335 return; // Already emitted this loop.
Ben Claytonb4274002019-11-28 11:47:29 +0000336 }
337
Ben Claytonfc951cd2019-05-15 17:16:56 +0100338 SPIRV_SHADER_DBG("*** LOOP HEADER ***");
339
Ben Claytonb4274002019-11-28 11:47:29 +0000340 // Gather all the blocks that make up the loop.
341 std::unordered_set<Block::ID> loopBlocks;
342 loopBlocks.emplace(block.mergeBlock);
343 function.TraverseReachableBlocks(blockId, loopBlocks);
344
345 // incomingBlocks are block ins that are not back-edges.
346 std::unordered_set<Block::ID> incomingBlocks;
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500347 for(auto in : block.ins)
Ben Claytonb4274002019-11-28 11:47:29 +0000348 {
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500349 if(loopBlocks.count(in) == 0)
Ben Claytonb4274002019-11-28 11:47:29 +0000350 {
351 incomingBlocks.emplace(in);
352 }
353 }
354
355 // Emit the loop phi instructions, and initialize them with a value from
356 // the incoming blocks.
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500357 for(auto insn = block.begin(); insn != block.mergeInstruction; insn++)
Ben Claytonb4274002019-11-28 11:47:29 +0000358 {
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500359 if(insn.opcode() == spv::OpPhi)
Ben Claytonb4274002019-11-28 11:47:29 +0000360 {
361 StorePhi(blockId, insn, state, incomingBlocks);
362 }
363 }
364
365 // loopActiveLaneMask is the mask of lanes that are continuing to loop.
366 // This is initialized with the incoming active lane masks.
367 SIMD::Int loopActiveLaneMask = SIMD::Int(0);
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500368 for(auto in : incomingBlocks)
Ben Claytonb4274002019-11-28 11:47:29 +0000369 {
370 loopActiveLaneMask |= GetActiveLaneMaskEdge(state, in, blockId);
371 }
372
373 // mergeActiveLaneMasks contains edge lane masks for the merge block.
374 // This is the union of all edge masks across all iterations of the loop.
375 std::unordered_map<Block::ID, SIMD::Int> mergeActiveLaneMasks;
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500376 for(auto in : function.getBlock(mergeBlockId).ins)
Ben Claytonb4274002019-11-28 11:47:29 +0000377 {
378 mergeActiveLaneMasks.emplace(in, SIMD::Int(0));
379 }
380
381 // Create the loop basic blocks
382 auto headerBasicBlock = Nucleus::createBasicBlock();
383 auto mergeBasicBlock = Nucleus::createBasicBlock();
384
385 // Start emitting code inside the loop.
386 Nucleus::createBr(headerBasicBlock);
387 Nucleus::setInsertBlock(headerBasicBlock);
388
Ben Claytonfc951cd2019-05-15 17:16:56 +0100389 SPIRV_SHADER_DBG("*** LOOP START (mask: {0}) ***", loopActiveLaneMask);
390
Ben Claytonb4274002019-11-28 11:47:29 +0000391 // Load the active lane mask.
Ben Clayton8a7067d2020-01-08 12:30:06 +0000392 SetActiveLaneMask(loopActiveLaneMask, state);
Ben Claytonb4274002019-11-28 11:47:29 +0000393
394 // Emit the non-phi loop header block's instructions.
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500395 for(auto insn = block.begin(); insn != block.end(); insn++)
Ben Claytonb4274002019-11-28 11:47:29 +0000396 {
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500397 if(insn.opcode() == spv::OpPhi)
Ben Claytonb4274002019-11-28 11:47:29 +0000398 {
399 LoadPhi(insn, state);
400 }
401 else
402 {
403 EmitInstruction(insn, state);
404 }
405 }
406
407 // Emit all blocks between the loop header and the merge block, but
408 // don't emit the merge block yet.
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500409 for(auto out : block.outs)
Ben Claytonb4274002019-11-28 11:47:29 +0000410 {
411 EmitBlocks(out, state, mergeBlockId);
412 }
413
414 // Restore current block id after emitting loop blocks.
415 state->block = blockId;
416
417 // Rebuild the loopActiveLaneMask from the loop back edges.
418 loopActiveLaneMask = SIMD::Int(0);
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500419 for(auto in : block.ins)
Ben Claytonb4274002019-11-28 11:47:29 +0000420 {
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500421 if(function.ExistsPath(blockId, in, mergeBlockId))
Ben Claytonb4274002019-11-28 11:47:29 +0000422 {
423 loopActiveLaneMask |= GetActiveLaneMaskEdge(state, in, blockId);
424 }
425 }
426
427 // Add active lanes to the merge lane mask.
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500428 for(auto in : function.getBlock(mergeBlockId).ins)
Ben Claytonb4274002019-11-28 11:47:29 +0000429 {
Ben Claytonbc1c0672019-12-17 20:37:37 +0000430 auto edge = Block::Edge{ in, mergeBlockId };
Ben Claytonb4274002019-11-28 11:47:29 +0000431 auto it = state->edgeActiveLaneMasks.find(edge);
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500432 if(it != state->edgeActiveLaneMasks.end())
Ben Claytonb4274002019-11-28 11:47:29 +0000433 {
434 mergeActiveLaneMasks[in] |= it->second;
435 }
436 }
437
438 // Update loop phi values.
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500439 for(auto insn = block.begin(); insn != block.mergeInstruction; insn++)
Ben Claytonb4274002019-11-28 11:47:29 +0000440 {
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500441 if(insn.opcode() == spv::OpPhi)
Ben Claytonb4274002019-11-28 11:47:29 +0000442 {
443 StorePhi(blockId, insn, state, loopBlocks);
444 }
445 }
446
Ben Claytonfc951cd2019-05-15 17:16:56 +0100447 SPIRV_SHADER_DBG("*** LOOP END (mask: {0}) ***", loopActiveLaneMask);
448
Ben Claytonb4274002019-11-28 11:47:29 +0000449 // Use the [loop -> merge] active lane masks to update the phi values in
450 // the merge block. We need to do this to handle divergent control flow
451 // in the loop.
452 //
453 // Consider the following:
454 //
455 // int phi_source = 0;
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500456 // for(uint i = 0; i < 4; i++)
Ben Claytonb4274002019-11-28 11:47:29 +0000457 // {
458 // phi_source = 0;
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500459 // if(gl_GlobalInvocationID.x % 4 == i) // divergent control flow
Ben Claytonb4274002019-11-28 11:47:29 +0000460 // {
461 // phi_source = 42; // single lane assignment.
462 // break; // activeLaneMask for [loop->merge] is active for a single lane.
463 // }
464 // // -- we are here --
465 // }
466 // // merge block
467 // int phi = phi_source; // OpPhi
468 //
469 // In this example, with each iteration of the loop, phi_source will
470 // only have a single lane assigned. However by 'phi' value in the merge
471 // block needs to be assigned the union of all the per-lane assignments
472 // of phi_source when that lane exited the loop.
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500473 for(auto insn = mergeBlock.begin(); insn != mergeBlock.end(); insn++)
Ben Claytonb4274002019-11-28 11:47:29 +0000474 {
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500475 if(insn.opcode() == spv::OpPhi)
Ben Claytonb4274002019-11-28 11:47:29 +0000476 {
477 StorePhi(mergeBlockId, insn, state, loopBlocks);
478 }
479 }
480
481 // Loop body now done.
482 // If any lanes are still active, jump back to the loop header,
483 // otherwise jump to the merge block.
Nicolas Capensb6e8c3f2020-05-01 23:28:37 -0400484 Nucleus::createCondBr(AnyTrue(loopActiveLaneMask).value(), headerBasicBlock, mergeBasicBlock);
Ben Claytonb4274002019-11-28 11:47:29 +0000485
486 // Continue emitting from the merge block.
487 Nucleus::setInsertBlock(mergeBasicBlock);
488 state->pending->push_back(mergeBlockId);
Ben Clayton9aff7ae2020-04-02 10:12:11 +0100489 for(const auto &it : mergeActiveLaneMasks)
Ben Claytonb4274002019-11-28 11:47:29 +0000490 {
491 state->addActiveLaneMaskEdge(it.first, mergeBlockId, it.second);
492 }
493}
494
495SpirvShader::EmitResult SpirvShader::EmitBranch(InsnIterator insn, EmitState *state) const
496{
497 auto target = Block::ID(insn.word(1));
498 state->addActiveLaneMaskEdge(state->block, target, state->activeLaneMask());
499 return EmitResult::Terminator;
500}
501
502SpirvShader::EmitResult SpirvShader::EmitBranchConditional(InsnIterator insn, EmitState *state) const
503{
504 auto &function = getFunction(state->function);
505 auto block = function.getBlock(state->block);
506 ASSERT(block.branchInstruction == insn);
507
508 auto condId = Object::ID(block.branchInstruction.word(1));
509 auto trueBlockId = Block::ID(block.branchInstruction.word(2));
510 auto falseBlockId = Block::ID(block.branchInstruction.word(3));
511
Nicolas Capense6f65d92020-04-08 21:55:43 -0400512 auto cond = Operand(this, state, condId);
Nicolas Capens2f4b6032020-04-09 02:01:50 -0400513 ASSERT_MSG(getType(getObject(condId)).componentCount == 1, "Condition must be a Boolean type scalar");
Ben Claytonb4274002019-11-28 11:47:29 +0000514
515 // TODO: Optimize for case where all lanes take same path.
516
517 state->addOutputActiveLaneMaskEdge(trueBlockId, cond.Int(0));
518 state->addOutputActiveLaneMaskEdge(falseBlockId, ~cond.Int(0));
519
520 return EmitResult::Terminator;
521}
522
523SpirvShader::EmitResult SpirvShader::EmitSwitch(InsnIterator insn, EmitState *state) const
524{
525 auto &function = getFunction(state->function);
526 auto block = function.getBlock(state->block);
527 ASSERT(block.branchInstruction == insn);
528
529 auto selId = Object::ID(block.branchInstruction.word(1));
530
Nicolas Capense6f65d92020-04-08 21:55:43 -0400531 auto sel = Operand(this, state, selId);
Nicolas Capens2f4b6032020-04-09 02:01:50 -0400532 ASSERT_MSG(sel.componentCount == 1, "Selector must be a scalar");
Ben Claytonfc951cd2019-05-15 17:16:56 +0100533 SPIRV_SHADER_DBG("switch({0})", sel);
Ben Claytonb4274002019-11-28 11:47:29 +0000534
535 auto numCases = (block.branchInstruction.wordCount() - 3) / 2;
536
537 // TODO: Optimize for case where all lanes take same path.
538
539 SIMD::Int defaultLaneMask = state->activeLaneMask();
540
541 // Gather up the case label matches and calculate defaultLaneMask.
542 std::vector<RValue<SIMD::Int>> caseLabelMatches;
543 caseLabelMatches.reserve(numCases);
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500544 for(uint32_t i = 0; i < numCases; i++)
Ben Claytonb4274002019-11-28 11:47:29 +0000545 {
546 auto label = block.branchInstruction.word(i * 2 + 3);
547 auto caseBlockId = Block::ID(block.branchInstruction.word(i * 2 + 4));
548 auto caseLabelMatch = CmpEQ(sel.Int(0), SIMD::Int(label));
Ben Claytonfc951cd2019-05-15 17:16:56 +0100549 SPIRV_SHADER_DBG("case {0}: {1}", label, caseLabelMatch & state->activeLaneMask());
Ben Claytonb4274002019-11-28 11:47:29 +0000550 state->addOutputActiveLaneMaskEdge(caseBlockId, caseLabelMatch);
551 defaultLaneMask &= ~caseLabelMatch;
552 }
553
554 auto defaultBlockId = Block::ID(block.branchInstruction.word(2));
Ben Claytonfc951cd2019-05-15 17:16:56 +0100555 SPIRV_SHADER_DBG("default: {0}", defaultLaneMask);
Ben Claytonb4274002019-11-28 11:47:29 +0000556 state->addOutputActiveLaneMaskEdge(defaultBlockId, defaultLaneMask);
557
558 return EmitResult::Terminator;
559}
560
561SpirvShader::EmitResult SpirvShader::EmitUnreachable(InsnIterator insn, EmitState *state) const
562{
563 // TODO: Log something in this case?
Ben Clayton8a7067d2020-01-08 12:30:06 +0000564 SetActiveLaneMask(SIMD::Int(0), state);
Ben Claytonb4274002019-11-28 11:47:29 +0000565 return EmitResult::Terminator;
566}
567
568SpirvShader::EmitResult SpirvShader::EmitReturn(InsnIterator insn, EmitState *state) const
569{
Ben Clayton8a7067d2020-01-08 12:30:06 +0000570 SetActiveLaneMask(SIMD::Int(0), state);
Ben Claytonb4274002019-11-28 11:47:29 +0000571 return EmitResult::Terminator;
572}
573
574SpirvShader::EmitResult SpirvShader::EmitKill(InsnIterator insn, EmitState *state) const
575{
576 state->routine->killMask |= SignMask(state->activeLaneMask());
Ben Clayton8a7067d2020-01-08 12:30:06 +0000577 SetActiveLaneMask(SIMD::Int(0), state);
Ben Claytonb4274002019-11-28 11:47:29 +0000578 return EmitResult::Terminator;
579}
580
581SpirvShader::EmitResult SpirvShader::EmitFunctionCall(InsnIterator insn, EmitState *state) const
582{
583 auto functionId = Function::ID(insn.word(3));
Ben Claytonbc1c0672019-12-17 20:37:37 +0000584 const auto &functionIt = functions.find(functionId);
Ben Claytonb4274002019-11-28 11:47:29 +0000585 ASSERT(functionIt != functions.end());
Ben Claytonbc1c0672019-12-17 20:37:37 +0000586 auto &function = functionIt->second;
Ben Claytonb4274002019-11-28 11:47:29 +0000587
588 // TODO(b/141246700): Add full support for spv::OpFunctionCall
589 // The only supported function is a single OpKill wrapped in a
590 // function, as a result of the "wrap OpKill" SPIRV-Tools pass
591 ASSERT(function.blocks.size() == 1);
592 spv::Op wrapOpKill[] = { spv::OpLabel, spv::OpKill };
593
Ben Clayton9aff7ae2020-04-02 10:12:11 +0100594 for(const auto &block : function.blocks)
Ben Claytonb4274002019-11-28 11:47:29 +0000595 {
596 int insnNumber = 0;
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500597 for(auto blockInsn : block.second)
Ben Claytonb4274002019-11-28 11:47:29 +0000598 {
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500599 if(insnNumber > 1)
Ben Claytonb4274002019-11-28 11:47:29 +0000600 {
Nicolas Capensdd0e6002020-01-24 01:21:47 -0500601 UNIMPLEMENTED("b/141246700: Function block number of instructions: %d", insnNumber); // FIXME(b/141246700)
Ben Claytonb4274002019-11-28 11:47:29 +0000602 return EmitResult::Continue;
603 }
Nicolas Capens44bd43a2020-01-22 03:07:14 -0500604
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500605 if(blockInsn.opcode() != wrapOpKill[insnNumber++])
Ben Claytonb4274002019-11-28 11:47:29 +0000606 {
Nicolas Capensdd0e6002020-01-24 01:21:47 -0500607 UNIMPLEMENTED("b/141246700: Function block instruction %d : %s", insnNumber - 1, OpcodeName(blockInsn.opcode()).c_str()); // FIXME(b/141246700)
Ben Claytonb4274002019-11-28 11:47:29 +0000608 return EmitResult::Continue;
609 }
Nicolas Capens44bd43a2020-01-22 03:07:14 -0500610
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500611 if(blockInsn.opcode() == spv::OpKill)
Ben Claytonb4274002019-11-28 11:47:29 +0000612 {
613 EmitInstruction(blockInsn, state);
614 }
615 }
616 }
617
618 return EmitResult::Continue;
619}
620
621SpirvShader::EmitResult SpirvShader::EmitControlBarrier(InsnIterator insn, EmitState *state) const
622{
623 auto executionScope = spv::Scope(GetConstScalarInt(insn.word(1)));
624 auto semantics = spv::MemorySemanticsMask(GetConstScalarInt(insn.word(3)));
625 // TODO: We probably want to consider the memory scope here. For now,
626 // just always emit the full fence.
627 Fence(semantics);
628
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500629 switch(executionScope)
Ben Claytonb4274002019-11-28 11:47:29 +0000630 {
Ben Claytonbc1c0672019-12-17 20:37:37 +0000631 case spv::ScopeWorkgroup:
632 Yield(YieldResult::ControlBarrier);
633 break;
634 case spv::ScopeSubgroup:
635 break;
636 default:
637 // See Vulkan 1.1 spec, Appendix A, Validation Rules within a Module.
638 UNREACHABLE("Scope for execution must be limited to Workgroup or Subgroup");
639 break;
Ben Claytonb4274002019-11-28 11:47:29 +0000640 }
641
642 return EmitResult::Continue;
643}
644
645SpirvShader::EmitResult SpirvShader::EmitPhi(InsnIterator insn, EmitState *state) const
646{
647 auto &function = getFunction(state->function);
648 auto currentBlock = function.getBlock(state->block);
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500649 if(!currentBlock.isLoopMerge)
Ben Claytonb4274002019-11-28 11:47:29 +0000650 {
651 // If this is a loop merge block, then don't attempt to update the
652 // phi values from the ins. EmitLoop() has had to take special care
653 // of this phi in order to correctly deal with divergent lanes.
654 StorePhi(state->block, insn, state, currentBlock.ins);
655 }
656 LoadPhi(insn, state);
657 return EmitResult::Continue;
658}
659
660void SpirvShader::LoadPhi(InsnIterator insn, EmitState *state) 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
Nicolas Capensff9f9b52020-04-14 00:46:38 -0400670 auto &dst = state->createIntermediate(objectId, type.componentCount);
671 for(uint32_t i = 0; i < type.componentCount; i++)
Ben Claytonb4274002019-11-28 11:47:29 +0000672 {
673 dst.move(i, storage[i]);
Ben Claytonfc951cd2019-05-15 17:16:56 +0100674 SPIRV_SHADER_DBG("LoadPhi({0}.{1}): {2}", objectId, i, storage[i]);
Ben Claytonb4274002019-11-28 11:47:29 +0000675 }
676}
677
Ben Claytonbc1c0672019-12-17 20:37:37 +0000678void SpirvShader::StorePhi(Block::ID currentBlock, InsnIterator insn, EmitState *state, std::unordered_set<SpirvShader::Block::ID> const &filter) const
Ben Claytonb4274002019-11-28 11:47:29 +0000679{
680 auto typeId = Type::ID(insn.word(1));
681 auto type = getType(typeId);
682 auto objectId = Object::ID(insn.word(2));
683
684 auto storageIt = state->routine->phis.find(objectId);
685 ASSERT(storageIt != state->routine->phis.end());
686 auto &storage = storageIt->second;
687
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500688 for(uint32_t w = 3; w < insn.wordCount(); w += 2)
Ben Claytonb4274002019-11-28 11:47:29 +0000689 {
690 auto varId = Object::ID(insn.word(w + 0));
691 auto blockId = Block::ID(insn.word(w + 1));
692
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500693 if(filter.count(blockId) == 0)
Ben Claytonb4274002019-11-28 11:47:29 +0000694 {
695 continue;
696 }
697
698 auto mask = GetActiveLaneMaskEdge(state, blockId, currentBlock);
Nicolas Capense6f65d92020-04-08 21:55:43 -0400699 auto in = Operand(this, state, varId);
Ben Claytonb4274002019-11-28 11:47:29 +0000700
Nicolas Capensff9f9b52020-04-14 00:46:38 -0400701 for(uint32_t i = 0; i < type.componentCount; i++)
Ben Claytonb4274002019-11-28 11:47:29 +0000702 {
703 storage[i] = As<SIMD::Float>((As<SIMD::Int>(storage[i]) & ~mask) | (in.Int(i) & mask));
Ben Claytonfc951cd2019-05-15 17:16:56 +0100704 SPIRV_SHADER_DBG("StorePhi({0}.{1}): [{2} <- {3}] {4}: {5}, mask: {6}",
705 objectId, i, currentBlock, blockId, varId, in.UInt(i), mask);
Ben Claytonb4274002019-11-28 11:47:29 +0000706 }
707 }
Ben Claytonfc951cd2019-05-15 17:16:56 +0100708
709 for(uint32_t i = 0; i < type.componentCount; i++)
710 {
711 SPIRV_SHADER_DBG("StorePhi({0}.{1}): {2}", objectId, i, As<SIMD::UInt>(storage[i]));
712 }
Ben Claytonb4274002019-11-28 11:47:29 +0000713}
714
715void SpirvShader::Fence(spv::MemorySemanticsMask semantics) const
716{
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500717 if(semantics == spv::MemorySemanticsMaskNone)
Ben Claytonb4274002019-11-28 11:47:29 +0000718 {
Ben Claytonbc1c0672019-12-17 20:37:37 +0000719 return; //no-op
Ben Claytonb4274002019-11-28 11:47:29 +0000720 }
721 rr::Fence(MemoryOrder(semantics));
722}
723
724void SpirvShader::Yield(YieldResult res) const
725{
726 rr::Yield(RValue<Int>(int(res)));
727}
728
Ben Clayton8a7067d2020-01-08 12:30:06 +0000729void SpirvShader::SetActiveLaneMask(RValue<SIMD::Int> mask, EmitState *state) const
730{
Nicolas Capensb6e8c3f2020-05-01 23:28:37 -0400731 state->activeLaneMaskValue = mask.value();
Ben Claytonb0ca2a82020-01-08 13:00:57 +0000732 dbgUpdateActiveLaneMask(mask, state);
Ben Clayton8a7067d2020-01-08 12:30:06 +0000733}
734
Ben Clayton0d6791c2020-04-22 21:55:27 +0100735void SpirvShader::WriteCFGGraphVizDotFile(const char *path) const
736{
737 std::ofstream file(path);
738 file << "digraph D {" << std::endl;
739 for(auto &func : functions)
740 {
741 file << " subgraph cluster_function_" << func.first.value() << " {"
742 << std::endl;
743
744 file << " label = \"function<" << func.first.value() << ">"
745 << (func.first == entryPoint ? " (entry point)" : "")
746 << "\"" << std::endl;
747
748 for(auto &block : func.second.blocks)
749 {
750 file << " block_" << block.first.value() << " ["
751 << "shape=circle "
752 << "label=\"" << block.first.value() << "\""
753 << "]" << std::endl;
754 }
755 file << std::endl;
756 for(auto &block : func.second.blocks)
757 {
758 file << " block_" << block.first.value() << " -> {";
759 bool first = true;
760 for(auto outs : block.second.outs)
761 {
762 if(!first) { file << ", "; }
763 file << "block_" << outs.value();
764 first = false;
765 }
766 file << "}" << std::endl;
767 }
768 file << std::endl;
769 for(auto &block : func.second.blocks)
770 {
771 if(block.second.kind == Block::Loop)
772 {
773 if(block.second.mergeBlock != 0)
774 {
775 file << " block_" << block.first.value() << " -> "
776 << "block_" << block.second.mergeBlock.value()
777 << "[label=\"M\" style=dashed color=blue]"
778 << std::endl;
779 }
780 if(block.second.continueTarget != 0)
781 {
782 file << " block_" << block.first.value() << " -> "
783 << "block_" << block.second.continueTarget.value()
784 << "[label=\"C\" style=dashed color=green]"
785 << std::endl;
786 }
787 }
788 }
789
790 file << " }" << std::endl;
791 }
792
793 for(auto &func : functions)
794 {
795 for(auto &block : func.second.blocks)
796 {
797 for(auto insn : block.second)
798 {
799 if(insn.opcode() == spv::OpFunctionCall)
800 {
801 auto target = getFunction(insn.word(3)).entry;
802 file << " block_" << block.first.value() << " -> "
803 << "block_" << target.value()
804 << "[color=\"#00008050\"]"
805 << std::endl;
806 }
807 }
808 }
809 }
810
811 file << "}" << std::endl;
812}
813
Ben Claytonb4274002019-11-28 11:47:29 +0000814} // namespace sw