Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 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" |
Ben Clayton | fc951cd | 2019-05-15 17:16:56 +0100 | [diff] [blame] | 16 | #include "SpirvShaderDebug.hpp" |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 17 | |
Ben Clayton | bc1c067 | 2019-12-17 20:37:37 +0000 | [diff] [blame] | 18 | #include "Reactor/Coroutine.hpp" // rr::Yield |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 19 | |
| 20 | #include "ShaderCore.hpp" |
| 21 | |
| 22 | #include <spirv/unified1/spirv.hpp> |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 23 | |
| 24 | #include <queue> |
| 25 | |
Ben Clayton | 0d6791c | 2020-04-22 21:55:27 +0100 | [diff] [blame] | 26 | #include <fstream> |
| 27 | #include <iostream> |
| 28 | |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 29 | namespace sw { |
| 30 | |
Ben Clayton | bc1c067 | 2019-12-17 20:37:37 +0000 | [diff] [blame] | 31 | SpirvShader::Block::Block(InsnIterator begin, InsnIterator end) |
| 32 | : begin_(begin) |
| 33 | , end_(end) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 34 | { |
| 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 Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 40 | for(auto insn : *this) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 41 | { |
| 42 | insns[0] = insns[1]; |
| 43 | insns[1] = insn; |
| 44 | } |
| 45 | |
Nicolas Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 46 | switch(insns[1].opcode()) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 47 | { |
| 48 | case spv::OpBranch: |
| 49 | branchInstruction = insns[1]; |
| 50 | outs.emplace(Block::ID(branchInstruction.word(1))); |
| 51 | |
Nicolas Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 52 | switch(insns[0].opcode()) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 53 | { |
| 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 Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 72 | switch(insns[0].opcode()) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 73 | { |
| 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 Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 96 | for(uint32_t w = 4; w < branchInstruction.wordCount(); w += 2) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 97 | { |
| 98 | outs.emplace(Block::ID(branchInstruction.word(w))); |
| 99 | } |
| 100 | |
Nicolas Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 101 | switch(insns[0].opcode()) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 102 | { |
| 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 Clayton | bc1c067 | 2019-12-17 20:37:37 +0000 | [diff] [blame] | 120 | void SpirvShader::Function::TraverseReachableBlocks(Block::ID id, SpirvShader::Block::Set &reachable) const |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 121 | { |
Nicolas Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 122 | if(reachable.count(id) == 0) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 123 | { |
| 124 | reachable.emplace(id); |
Nicolas Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 125 | for(auto out : getBlock(id).outs) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 126 | { |
| 127 | TraverseReachableBlocks(out, reachable); |
| 128 | } |
| 129 | } |
| 130 | } |
| 131 | |
| 132 | void SpirvShader::Function::AssignBlockFields() |
| 133 | { |
| 134 | Block::Set reachable; |
| 135 | TraverseReachableBlocks(entry, reachable); |
| 136 | |
Nicolas Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 137 | for(auto &it : blocks) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 138 | { |
| 139 | auto &blockId = it.first; |
| 140 | auto &block = it.second; |
Nicolas Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 141 | if(reachable.count(blockId) > 0) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 142 | { |
Nicolas Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 143 | for(auto &outId : it.second.outs) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 144 | { |
| 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 Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 150 | if(block.kind == Block::Loop) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 151 | { |
| 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 | |
| 160 | void SpirvShader::Function::ForeachBlockDependency(Block::ID blockId, std::function<void(Block::ID)> f) const |
| 161 | { |
| 162 | auto block = getBlock(blockId); |
Nicolas Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 163 | for(auto dep : block.ins) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 164 | { |
Ben Clayton | bc1c067 | 2019-12-17 20:37:37 +0000 | [diff] [blame] | 165 | 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 Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 167 | { |
| 168 | f(dep); |
| 169 | } |
| 170 | } |
| 171 | } |
| 172 | |
| 173 | bool 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 Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 182 | while(pending.size() > 0) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 183 | { |
| 184 | auto id = pending.front(); |
| 185 | pending.pop(); |
Nicolas Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 186 | for(auto out : getBlock(id).outs) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 187 | { |
Nicolas Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 188 | if(seen.count(out) != 0) { continue; } |
| 189 | if(out == to) { return true; } |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 190 | pending.emplace(out); |
| 191 | } |
| 192 | seen.emplace(id); |
| 193 | } |
| 194 | |
| 195 | return false; |
| 196 | } |
| 197 | |
| 198 | void SpirvShader::EmitState::addOutputActiveLaneMaskEdge(Block::ID to, RValue<SIMD::Int> mask) |
| 199 | { |
| 200 | addActiveLaneMaskEdge(block, to, mask & activeLaneMask()); |
| 201 | } |
| 202 | |
| 203 | void SpirvShader::EmitState::addActiveLaneMaskEdge(Block::ID from, Block::ID to, RValue<SIMD::Int> mask) |
| 204 | { |
Ben Clayton | bc1c067 | 2019-12-17 20:37:37 +0000 | [diff] [blame] | 205 | auto edge = Block::Edge{ from, to }; |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 206 | auto it = edgeActiveLaneMasks.find(edge); |
Nicolas Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 207 | if(it == edgeActiveLaneMasks.end()) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 208 | { |
| 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 | |
| 219 | RValue<SIMD::Int> SpirvShader::GetActiveLaneMaskEdge(EmitState *state, Block::ID from, Block::ID to) const |
| 220 | { |
Ben Clayton | bc1c067 | 2019-12-17 20:37:37 +0000 | [diff] [blame] | 221 | auto edge = Block::Edge{ from, to }; |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 222 | 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 | |
| 227 | void 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 Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 235 | while(pending.size() > 0) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 236 | { |
| 237 | auto id = pending.front(); |
| 238 | |
| 239 | auto const &block = function.getBlock(id); |
Nicolas Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 240 | if(id == ignore) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 241 | { |
| 242 | pending.pop_front(); |
| 243 | continue; |
| 244 | } |
| 245 | |
| 246 | // Ensure all dependency blocks have been generated. |
| 247 | auto depsDone = true; |
Ben Clayton | bc1c067 | 2019-12-17 20:37:37 +0000 | [diff] [blame] | 248 | function.ForeachBlockDependency(id, [&](Block::ID dep) { |
Nicolas Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 249 | if(state->visited.count(dep) == 0) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 250 | { |
| 251 | state->pending->push_front(dep); |
| 252 | depsDone = false; |
| 253 | } |
| 254 | }); |
| 255 | |
Nicolas Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 256 | if(!depsDone) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 257 | { |
| 258 | continue; |
| 259 | } |
| 260 | |
| 261 | pending.pop_front(); |
| 262 | |
| 263 | state->block = id; |
| 264 | |
Nicolas Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 265 | switch(block.kind) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 266 | { |
| 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 | |
| 287 | void 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 Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 293 | if(!state->visited.emplace(blockId).second) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 294 | { |
Ben Clayton | bc1c067 | 2019-12-17 20:37:37 +0000 | [diff] [blame] | 295 | return; // Already generated this block. |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 296 | } |
| 297 | |
Nicolas Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 298 | if(blockId != function.entry) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 299 | { |
| 300 | // Set the activeLaneMask. |
| 301 | SIMD::Int activeLaneMask(0); |
Nicolas Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 302 | for(auto in : block.ins) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 303 | { |
| 304 | auto inMask = GetActiveLaneMaskEdge(state, in, blockId); |
Ben Clayton | fc951cd | 2019-05-15 17:16:56 +0100 | [diff] [blame] | 305 | SPIRV_SHADER_DBG("Block {0} -> {1} mask: {2}", in, blockId, inMask); |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 306 | activeLaneMask |= inMask; |
| 307 | } |
Ben Clayton | fc951cd | 2019-05-15 17:16:56 +0100 | [diff] [blame] | 308 | SPIRV_SHADER_DBG("Block {0} mask: {1}", blockId, activeLaneMask); |
Ben Clayton | 8a7067d | 2020-01-08 12:30:06 +0000 | [diff] [blame] | 309 | SetActiveLaneMask(activeLaneMask, state); |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 310 | } |
| 311 | |
| 312 | EmitInstructions(block.begin(), block.end(), state); |
| 313 | |
Nicolas Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 314 | for(auto out : block.outs) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 315 | { |
Nicolas Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 316 | if(state->visited.count(out) == 0) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 317 | { |
| 318 | state->pending->push_back(out); |
| 319 | } |
| 320 | } |
Ben Clayton | fc951cd | 2019-05-15 17:16:56 +0100 | [diff] [blame] | 321 | |
| 322 | SPIRV_SHADER_DBG("Block {0} done", blockId); |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 323 | } |
| 324 | |
| 325 | void 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 Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 333 | if(!state->visited.emplace(blockId).second) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 334 | { |
Ben Clayton | bc1c067 | 2019-12-17 20:37:37 +0000 | [diff] [blame] | 335 | return; // Already emitted this loop. |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 336 | } |
| 337 | |
Ben Clayton | fc951cd | 2019-05-15 17:16:56 +0100 | [diff] [blame] | 338 | SPIRV_SHADER_DBG("*** LOOP HEADER ***"); |
| 339 | |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 340 | // 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 Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 347 | for(auto in : block.ins) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 348 | { |
Nicolas Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 349 | if(loopBlocks.count(in) == 0) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 350 | { |
| 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 Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 357 | for(auto insn = block.begin(); insn != block.mergeInstruction; insn++) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 358 | { |
Nicolas Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 359 | if(insn.opcode() == spv::OpPhi) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 360 | { |
| 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 Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 368 | for(auto in : incomingBlocks) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 369 | { |
| 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 Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 376 | for(auto in : function.getBlock(mergeBlockId).ins) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 377 | { |
| 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 Clayton | fc951cd | 2019-05-15 17:16:56 +0100 | [diff] [blame] | 389 | SPIRV_SHADER_DBG("*** LOOP START (mask: {0}) ***", loopActiveLaneMask); |
| 390 | |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 391 | // Load the active lane mask. |
Ben Clayton | 8a7067d | 2020-01-08 12:30:06 +0000 | [diff] [blame] | 392 | SetActiveLaneMask(loopActiveLaneMask, state); |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 393 | |
| 394 | // Emit the non-phi loop header block's instructions. |
Nicolas Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 395 | for(auto insn = block.begin(); insn != block.end(); insn++) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 396 | { |
Nicolas Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 397 | if(insn.opcode() == spv::OpPhi) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 398 | { |
| 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 Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 409 | for(auto out : block.outs) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 410 | { |
| 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 Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 419 | for(auto in : block.ins) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 420 | { |
Nicolas Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 421 | if(function.ExistsPath(blockId, in, mergeBlockId)) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 422 | { |
| 423 | loopActiveLaneMask |= GetActiveLaneMaskEdge(state, in, blockId); |
| 424 | } |
| 425 | } |
| 426 | |
| 427 | // Add active lanes to the merge lane mask. |
Nicolas Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 428 | for(auto in : function.getBlock(mergeBlockId).ins) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 429 | { |
Ben Clayton | bc1c067 | 2019-12-17 20:37:37 +0000 | [diff] [blame] | 430 | auto edge = Block::Edge{ in, mergeBlockId }; |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 431 | auto it = state->edgeActiveLaneMasks.find(edge); |
Nicolas Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 432 | if(it != state->edgeActiveLaneMasks.end()) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 433 | { |
| 434 | mergeActiveLaneMasks[in] |= it->second; |
| 435 | } |
| 436 | } |
| 437 | |
| 438 | // Update loop phi values. |
Nicolas Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 439 | for(auto insn = block.begin(); insn != block.mergeInstruction; insn++) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 440 | { |
Nicolas Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 441 | if(insn.opcode() == spv::OpPhi) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 442 | { |
| 443 | StorePhi(blockId, insn, state, loopBlocks); |
| 444 | } |
| 445 | } |
| 446 | |
Ben Clayton | fc951cd | 2019-05-15 17:16:56 +0100 | [diff] [blame] | 447 | SPIRV_SHADER_DBG("*** LOOP END (mask: {0}) ***", loopActiveLaneMask); |
| 448 | |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 449 | // 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 Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 456 | // for(uint i = 0; i < 4; i++) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 457 | // { |
| 458 | // phi_source = 0; |
Nicolas Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 459 | // if(gl_GlobalInvocationID.x % 4 == i) // divergent control flow |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 460 | // { |
| 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 Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 473 | for(auto insn = mergeBlock.begin(); insn != mergeBlock.end(); insn++) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 474 | { |
Nicolas Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 475 | if(insn.opcode() == spv::OpPhi) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 476 | { |
| 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 Capens | b6e8c3f | 2020-05-01 23:28:37 -0400 | [diff] [blame] | 484 | Nucleus::createCondBr(AnyTrue(loopActiveLaneMask).value(), headerBasicBlock, mergeBasicBlock); |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 485 | |
| 486 | // Continue emitting from the merge block. |
| 487 | Nucleus::setInsertBlock(mergeBasicBlock); |
| 488 | state->pending->push_back(mergeBlockId); |
Ben Clayton | 9aff7ae | 2020-04-02 10:12:11 +0100 | [diff] [blame] | 489 | for(const auto &it : mergeActiveLaneMasks) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 490 | { |
| 491 | state->addActiveLaneMaskEdge(it.first, mergeBlockId, it.second); |
| 492 | } |
| 493 | } |
| 494 | |
| 495 | SpirvShader::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 | |
| 502 | SpirvShader::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 Capens | e6f65d9 | 2020-04-08 21:55:43 -0400 | [diff] [blame] | 512 | auto cond = Operand(this, state, condId); |
Nicolas Capens | 2f4b603 | 2020-04-09 02:01:50 -0400 | [diff] [blame] | 513 | ASSERT_MSG(getType(getObject(condId)).componentCount == 1, "Condition must be a Boolean type scalar"); |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 514 | |
| 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 | |
| 523 | SpirvShader::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 Capens | e6f65d9 | 2020-04-08 21:55:43 -0400 | [diff] [blame] | 531 | auto sel = Operand(this, state, selId); |
Nicolas Capens | 2f4b603 | 2020-04-09 02:01:50 -0400 | [diff] [blame] | 532 | ASSERT_MSG(sel.componentCount == 1, "Selector must be a scalar"); |
Ben Clayton | fc951cd | 2019-05-15 17:16:56 +0100 | [diff] [blame] | 533 | SPIRV_SHADER_DBG("switch({0})", sel); |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 534 | |
| 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 Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 544 | for(uint32_t i = 0; i < numCases; i++) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 545 | { |
| 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 Clayton | fc951cd | 2019-05-15 17:16:56 +0100 | [diff] [blame] | 549 | SPIRV_SHADER_DBG("case {0}: {1}", label, caseLabelMatch & state->activeLaneMask()); |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 550 | state->addOutputActiveLaneMaskEdge(caseBlockId, caseLabelMatch); |
| 551 | defaultLaneMask &= ~caseLabelMatch; |
| 552 | } |
| 553 | |
| 554 | auto defaultBlockId = Block::ID(block.branchInstruction.word(2)); |
Ben Clayton | fc951cd | 2019-05-15 17:16:56 +0100 | [diff] [blame] | 555 | SPIRV_SHADER_DBG("default: {0}", defaultLaneMask); |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 556 | state->addOutputActiveLaneMaskEdge(defaultBlockId, defaultLaneMask); |
| 557 | |
| 558 | return EmitResult::Terminator; |
| 559 | } |
| 560 | |
| 561 | SpirvShader::EmitResult SpirvShader::EmitUnreachable(InsnIterator insn, EmitState *state) const |
| 562 | { |
| 563 | // TODO: Log something in this case? |
Ben Clayton | 8a7067d | 2020-01-08 12:30:06 +0000 | [diff] [blame] | 564 | SetActiveLaneMask(SIMD::Int(0), state); |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 565 | return EmitResult::Terminator; |
| 566 | } |
| 567 | |
| 568 | SpirvShader::EmitResult SpirvShader::EmitReturn(InsnIterator insn, EmitState *state) const |
| 569 | { |
Ben Clayton | 8a7067d | 2020-01-08 12:30:06 +0000 | [diff] [blame] | 570 | SetActiveLaneMask(SIMD::Int(0), state); |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 571 | return EmitResult::Terminator; |
| 572 | } |
| 573 | |
| 574 | SpirvShader::EmitResult SpirvShader::EmitKill(InsnIterator insn, EmitState *state) const |
| 575 | { |
| 576 | state->routine->killMask |= SignMask(state->activeLaneMask()); |
Ben Clayton | 8a7067d | 2020-01-08 12:30:06 +0000 | [diff] [blame] | 577 | SetActiveLaneMask(SIMD::Int(0), state); |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 578 | return EmitResult::Terminator; |
| 579 | } |
| 580 | |
| 581 | SpirvShader::EmitResult SpirvShader::EmitFunctionCall(InsnIterator insn, EmitState *state) const |
| 582 | { |
| 583 | auto functionId = Function::ID(insn.word(3)); |
Ben Clayton | bc1c067 | 2019-12-17 20:37:37 +0000 | [diff] [blame] | 584 | const auto &functionIt = functions.find(functionId); |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 585 | ASSERT(functionIt != functions.end()); |
Ben Clayton | bc1c067 | 2019-12-17 20:37:37 +0000 | [diff] [blame] | 586 | auto &function = functionIt->second; |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 587 | |
| 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 Clayton | 9aff7ae | 2020-04-02 10:12:11 +0100 | [diff] [blame] | 594 | for(const auto &block : function.blocks) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 595 | { |
| 596 | int insnNumber = 0; |
Nicolas Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 597 | for(auto blockInsn : block.second) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 598 | { |
Nicolas Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 599 | if(insnNumber > 1) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 600 | { |
Nicolas Capens | dd0e600 | 2020-01-24 01:21:47 -0500 | [diff] [blame] | 601 | UNIMPLEMENTED("b/141246700: Function block number of instructions: %d", insnNumber); // FIXME(b/141246700) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 602 | return EmitResult::Continue; |
| 603 | } |
Nicolas Capens | 44bd43a | 2020-01-22 03:07:14 -0500 | [diff] [blame] | 604 | |
Nicolas Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 605 | if(blockInsn.opcode() != wrapOpKill[insnNumber++]) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 606 | { |
Nicolas Capens | dd0e600 | 2020-01-24 01:21:47 -0500 | [diff] [blame] | 607 | UNIMPLEMENTED("b/141246700: Function block instruction %d : %s", insnNumber - 1, OpcodeName(blockInsn.opcode()).c_str()); // FIXME(b/141246700) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 608 | return EmitResult::Continue; |
| 609 | } |
Nicolas Capens | 44bd43a | 2020-01-22 03:07:14 -0500 | [diff] [blame] | 610 | |
Nicolas Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 611 | if(blockInsn.opcode() == spv::OpKill) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 612 | { |
| 613 | EmitInstruction(blockInsn, state); |
| 614 | } |
| 615 | } |
| 616 | } |
| 617 | |
| 618 | return EmitResult::Continue; |
| 619 | } |
| 620 | |
| 621 | SpirvShader::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 Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 629 | switch(executionScope) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 630 | { |
Ben Clayton | bc1c067 | 2019-12-17 20:37:37 +0000 | [diff] [blame] | 631 | 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 Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 640 | } |
| 641 | |
| 642 | return EmitResult::Continue; |
| 643 | } |
| 644 | |
| 645 | SpirvShader::EmitResult SpirvShader::EmitPhi(InsnIterator insn, EmitState *state) const |
| 646 | { |
| 647 | auto &function = getFunction(state->function); |
| 648 | auto currentBlock = function.getBlock(state->block); |
Nicolas Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 649 | if(!currentBlock.isLoopMerge) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 650 | { |
| 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 | |
| 660 | void 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 Capens | ff9f9b5 | 2020-04-14 00:46:38 -0400 | [diff] [blame] | 670 | auto &dst = state->createIntermediate(objectId, type.componentCount); |
| 671 | for(uint32_t i = 0; i < type.componentCount; i++) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 672 | { |
| 673 | dst.move(i, storage[i]); |
Ben Clayton | fc951cd | 2019-05-15 17:16:56 +0100 | [diff] [blame] | 674 | SPIRV_SHADER_DBG("LoadPhi({0}.{1}): {2}", objectId, i, storage[i]); |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 675 | } |
| 676 | } |
| 677 | |
Ben Clayton | bc1c067 | 2019-12-17 20:37:37 +0000 | [diff] [blame] | 678 | void SpirvShader::StorePhi(Block::ID currentBlock, InsnIterator insn, EmitState *state, std::unordered_set<SpirvShader::Block::ID> const &filter) const |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 679 | { |
| 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 Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 688 | for(uint32_t w = 3; w < insn.wordCount(); w += 2) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 689 | { |
| 690 | auto varId = Object::ID(insn.word(w + 0)); |
| 691 | auto blockId = Block::ID(insn.word(w + 1)); |
| 692 | |
Nicolas Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 693 | if(filter.count(blockId) == 0) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 694 | { |
| 695 | continue; |
| 696 | } |
| 697 | |
| 698 | auto mask = GetActiveLaneMaskEdge(state, blockId, currentBlock); |
Nicolas Capens | e6f65d9 | 2020-04-08 21:55:43 -0400 | [diff] [blame] | 699 | auto in = Operand(this, state, varId); |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 700 | |
Nicolas Capens | ff9f9b5 | 2020-04-14 00:46:38 -0400 | [diff] [blame] | 701 | for(uint32_t i = 0; i < type.componentCount; i++) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 702 | { |
| 703 | storage[i] = As<SIMD::Float>((As<SIMD::Int>(storage[i]) & ~mask) | (in.Int(i) & mask)); |
Ben Clayton | fc951cd | 2019-05-15 17:16:56 +0100 | [diff] [blame] | 704 | SPIRV_SHADER_DBG("StorePhi({0}.{1}): [{2} <- {3}] {4}: {5}, mask: {6}", |
| 705 | objectId, i, currentBlock, blockId, varId, in.UInt(i), mask); |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 706 | } |
| 707 | } |
Ben Clayton | fc951cd | 2019-05-15 17:16:56 +0100 | [diff] [blame] | 708 | |
| 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 Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 713 | } |
| 714 | |
| 715 | void SpirvShader::Fence(spv::MemorySemanticsMask semantics) const |
| 716 | { |
Nicolas Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 717 | if(semantics == spv::MemorySemanticsMaskNone) |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 718 | { |
Ben Clayton | bc1c067 | 2019-12-17 20:37:37 +0000 | [diff] [blame] | 719 | return; //no-op |
Ben Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 720 | } |
| 721 | rr::Fence(MemoryOrder(semantics)); |
| 722 | } |
| 723 | |
| 724 | void SpirvShader::Yield(YieldResult res) const |
| 725 | { |
| 726 | rr::Yield(RValue<Int>(int(res))); |
| 727 | } |
| 728 | |
Ben Clayton | 8a7067d | 2020-01-08 12:30:06 +0000 | [diff] [blame] | 729 | void SpirvShader::SetActiveLaneMask(RValue<SIMD::Int> mask, EmitState *state) const |
| 730 | { |
Nicolas Capens | b6e8c3f | 2020-05-01 23:28:37 -0400 | [diff] [blame] | 731 | state->activeLaneMaskValue = mask.value(); |
Ben Clayton | b0ca2a8 | 2020-01-08 13:00:57 +0000 | [diff] [blame] | 732 | dbgUpdateActiveLaneMask(mask, state); |
Ben Clayton | 8a7067d | 2020-01-08 12:30:06 +0000 | [diff] [blame] | 733 | } |
| 734 | |
Ben Clayton | 0d6791c | 2020-04-22 21:55:27 +0100 | [diff] [blame] | 735 | void 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 Clayton | b427400 | 2019-11-28 11:47:29 +0000 | [diff] [blame] | 814 | } // namespace sw |