John Kessenich | f04c51b | 2018-08-03 15:56:12 -0600 | [diff] [blame] | 1 | // |
| 2 | // Copyright (C) 2016-2018 Google, Inc. |
| 3 | // |
| 4 | // All rights reserved. |
| 5 | // |
| 6 | // Redistribution and use in source and binary forms, with or without |
| 7 | // modification, are permitted provided that the following conditions |
| 8 | // are met: |
| 9 | // |
| 10 | // Redistributions of source code must retain the above copyright |
| 11 | // notice, this list of conditions and the following disclaimer. |
| 12 | // |
| 13 | // Redistributions in binary form must reproduce the above |
| 14 | // copyright notice, this list of conditions and the following |
| 15 | // disclaimer in the documentation and/or other materials provided |
| 16 | // with the distribution. |
| 17 | // |
| 18 | // Neither the name of 3Dlabs Inc. Ltd. nor the names of its |
| 19 | // contributors may be used to endorse or promote products derived |
| 20 | // from this software without specific prior written permission. |
| 21 | // |
| 22 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 23 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 24 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
| 25 | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
| 26 | // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
| 27 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
| 28 | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| 29 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| 30 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| 31 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
| 32 | // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| 33 | // POSSIBILITY OF SUCH DAMAGE. |
| 34 | |
| 35 | // |
| 36 | // Post-processing for SPIR-V IR, in internal form, not standard binary form. |
| 37 | // |
| 38 | |
| 39 | #include <cassert> |
| 40 | #include <cstdlib> |
| 41 | |
| 42 | #include <unordered_set> |
| 43 | #include <algorithm> |
| 44 | |
| 45 | #include "SpvBuilder.h" |
| 46 | |
| 47 | #include "spirv.hpp" |
| 48 | #include "GlslangToSpv.h" |
| 49 | #include "SpvBuilder.h" |
| 50 | namespace spv { |
| 51 | #include "GLSL.std.450.h" |
| 52 | #include "GLSL.ext.KHR.h" |
| 53 | #include "GLSL.ext.EXT.h" |
| 54 | #ifdef AMD_EXTENSIONS |
| 55 | #include "GLSL.ext.AMD.h" |
| 56 | #endif |
| 57 | #ifdef NV_EXTENSIONS |
| 58 | #include "GLSL.ext.NV.h" |
| 59 | #endif |
| 60 | } |
| 61 | |
| 62 | namespace spv { |
| 63 | |
John Kessenich | 31aa3d6 | 2018-08-15 13:54:09 -0600 | [diff] [blame] | 64 | // Hook to visit each operand type and result type of an instruction. |
| 65 | // Will be called multiple times for one instruction, once for each typed |
| 66 | // operand and the result. |
| 67 | void Builder::postProcessType(const Instruction& inst, Id typeId) |
| 68 | { |
| 69 | // Characterize the type being questioned |
| 70 | Id basicTypeOp = getMostBasicTypeClass(typeId); |
| 71 | int width = 0; |
| 72 | if (basicTypeOp == OpTypeFloat || basicTypeOp == OpTypeInt) |
| 73 | width = getScalarTypeWidth(typeId); |
| 74 | |
| 75 | // Do opcode-specific checks |
| 76 | switch (inst.getOpCode()) { |
| 77 | case OpLoad: |
| 78 | case OpStore: |
| 79 | if (basicTypeOp == OpTypeStruct) { |
| 80 | if (containsType(typeId, OpTypeInt, 8)) |
| 81 | addCapability(CapabilityInt8); |
| 82 | if (containsType(typeId, OpTypeInt, 16)) |
| 83 | addCapability(CapabilityInt16); |
| 84 | if (containsType(typeId, OpTypeFloat, 16)) |
| 85 | addCapability(CapabilityFloat16); |
| 86 | } else { |
| 87 | StorageClass storageClass = getStorageClass(inst.getIdOperand(0)); |
| 88 | if (width == 8) { |
| 89 | switch (storageClass) { |
| 90 | case StorageClassUniform: |
| 91 | case StorageClassStorageBuffer: |
| 92 | case StorageClassPushConstant: |
| 93 | break; |
| 94 | default: |
| 95 | addCapability(CapabilityInt8); |
| 96 | break; |
| 97 | } |
| 98 | } else if (width == 16) { |
| 99 | switch (storageClass) { |
| 100 | case StorageClassUniform: |
| 101 | case StorageClassStorageBuffer: |
| 102 | case StorageClassPushConstant: |
| 103 | case StorageClassInput: |
| 104 | case StorageClassOutput: |
| 105 | break; |
| 106 | default: |
| 107 | if (basicTypeOp == OpTypeInt) |
| 108 | addCapability(CapabilityInt16); |
| 109 | if (basicTypeOp == OpTypeFloat) |
| 110 | addCapability(CapabilityFloat16); |
| 111 | break; |
| 112 | } |
| 113 | } |
| 114 | } |
| 115 | break; |
| 116 | case OpAccessChain: |
| 117 | case OpPtrAccessChain: |
| 118 | case OpCopyObject: |
| 119 | case OpFConvert: |
| 120 | case OpSConvert: |
| 121 | case OpUConvert: |
| 122 | break; |
John Kessenich | afe0c66 | 2018-09-10 18:10:51 -0600 | [diff] [blame] | 123 | case OpExtInst: |
John Kessenich | afe0c66 | 2018-09-10 18:10:51 -0600 | [diff] [blame] | 124 | #if AMD_EXTENSIONS |
Corentin Wallez | 04a2fe9 | 2018-10-29 16:24:00 +0100 | [diff] [blame^] | 125 | switch (inst.getImmediateOperand(1)) { |
John Kessenich | afe0c66 | 2018-09-10 18:10:51 -0600 | [diff] [blame] | 126 | case GLSLstd450Frexp: |
| 127 | case GLSLstd450FrexpStruct: |
| 128 | if (getSpvVersion() < glslang::EShTargetSpv_1_3 && containsType(typeId, OpTypeInt, 16)) |
| 129 | addExtension(spv::E_SPV_AMD_gpu_shader_int16); |
| 130 | break; |
| 131 | case GLSLstd450InterpolateAtCentroid: |
| 132 | case GLSLstd450InterpolateAtSample: |
| 133 | case GLSLstd450InterpolateAtOffset: |
| 134 | if (getSpvVersion() < glslang::EShTargetSpv_1_3 && containsType(typeId, OpTypeFloat, 16)) |
| 135 | addExtension(spv::E_SPV_AMD_gpu_shader_half_float); |
| 136 | break; |
John Kessenich | afe0c66 | 2018-09-10 18:10:51 -0600 | [diff] [blame] | 137 | default: |
| 138 | break; |
| 139 | } |
Corentin Wallez | 04a2fe9 | 2018-10-29 16:24:00 +0100 | [diff] [blame^] | 140 | #endif |
John Kessenich | afe0c66 | 2018-09-10 18:10:51 -0600 | [diff] [blame] | 141 | break; |
John Kessenich | 31aa3d6 | 2018-08-15 13:54:09 -0600 | [diff] [blame] | 142 | default: |
| 143 | if (basicTypeOp == OpTypeFloat && width == 16) |
| 144 | addCapability(CapabilityFloat16); |
| 145 | if (basicTypeOp == OpTypeInt && width == 16) |
| 146 | addCapability(CapabilityInt16); |
| 147 | if (basicTypeOp == OpTypeInt && width == 8) |
| 148 | addCapability(CapabilityInt8); |
| 149 | break; |
| 150 | } |
| 151 | } |
| 152 | |
John Kessenich | 228e964 | 2018-08-13 21:37:59 -0600 | [diff] [blame] | 153 | // Called for each instruction that resides in a block. |
John Kessenich | 31aa3d6 | 2018-08-15 13:54:09 -0600 | [diff] [blame] | 154 | void Builder::postProcess(const Instruction& inst) |
John Kessenich | f04c51b | 2018-08-03 15:56:12 -0600 | [diff] [blame] | 155 | { |
| 156 | // Add capabilities based simply on the opcode. |
| 157 | switch (inst.getOpCode()) { |
| 158 | case OpExtInst: |
John Kessenich | 228e964 | 2018-08-13 21:37:59 -0600 | [diff] [blame] | 159 | switch (inst.getImmediateOperand(1)) { |
John Kessenich | f04c51b | 2018-08-03 15:56:12 -0600 | [diff] [blame] | 160 | case GLSLstd450InterpolateAtCentroid: |
| 161 | case GLSLstd450InterpolateAtSample: |
| 162 | case GLSLstd450InterpolateAtOffset: |
| 163 | addCapability(CapabilityInterpolationFunction); |
| 164 | break; |
| 165 | default: |
| 166 | break; |
| 167 | } |
| 168 | break; |
| 169 | case OpDPdxFine: |
| 170 | case OpDPdyFine: |
| 171 | case OpFwidthFine: |
| 172 | case OpDPdxCoarse: |
| 173 | case OpDPdyCoarse: |
| 174 | case OpFwidthCoarse: |
| 175 | addCapability(CapabilityDerivativeControl); |
| 176 | break; |
| 177 | |
| 178 | case OpImageQueryLod: |
| 179 | case OpImageQuerySize: |
| 180 | case OpImageQuerySizeLod: |
| 181 | case OpImageQuerySamples: |
| 182 | case OpImageQueryLevels: |
| 183 | addCapability(CapabilityImageQuery); |
| 184 | break; |
| 185 | |
| 186 | #ifdef NV_EXTENSIONS |
| 187 | case OpGroupNonUniformPartitionNV: |
| 188 | addExtension(E_SPV_NV_shader_subgroup_partitioned); |
| 189 | addCapability(CapabilityGroupNonUniformPartitionedNV); |
| 190 | break; |
| 191 | #endif |
| 192 | |
| 193 | default: |
| 194 | break; |
| 195 | } |
John Kessenich | 31aa3d6 | 2018-08-15 13:54:09 -0600 | [diff] [blame] | 196 | |
| 197 | // Checks based on type |
| 198 | if (inst.getTypeId() != NoType) |
| 199 | postProcessType(inst, inst.getTypeId()); |
| 200 | for (int op = 0; op < inst.getNumOperands(); ++op) { |
| 201 | if (inst.isIdOperand(op)) { |
| 202 | // In blocks, these are always result ids, but we are relying on |
| 203 | // getTypeId() to return NoType for things like OpLabel. |
| 204 | if (getTypeId(inst.getIdOperand(op)) != NoType) |
| 205 | postProcessType(inst, getTypeId(inst.getIdOperand(op))); |
| 206 | } |
| 207 | } |
John Kessenich | f04c51b | 2018-08-03 15:56:12 -0600 | [diff] [blame] | 208 | } |
| 209 | |
| 210 | // Called for each instruction in a reachable block. |
John Kessenich | 31aa3d6 | 2018-08-15 13:54:09 -0600 | [diff] [blame] | 211 | void Builder::postProcessReachable(const Instruction& inst) |
John Kessenich | f04c51b | 2018-08-03 15:56:12 -0600 | [diff] [blame] | 212 | { |
| 213 | // did have code here, but questionable to do so without deleting the instructions |
| 214 | } |
| 215 | |
| 216 | // comment in header |
| 217 | void Builder::postProcess() |
| 218 | { |
| 219 | std::unordered_set<const Block*> reachableBlocks; |
| 220 | std::unordered_set<Id> unreachableDefinitions; |
| 221 | // Collect IDs defined in unreachable blocks. For each function, label the |
| 222 | // reachable blocks first. Then for each unreachable block, collect the |
| 223 | // result IDs of the instructions in it. |
| 224 | for (auto fi = module.getFunctions().cbegin(); fi != module.getFunctions().cend(); fi++) { |
| 225 | Function* f = *fi; |
| 226 | Block* entry = f->getEntryBlock(); |
| 227 | inReadableOrder(entry, [&reachableBlocks](const Block* b) { reachableBlocks.insert(b); }); |
| 228 | for (auto bi = f->getBlocks().cbegin(); bi != f->getBlocks().cend(); bi++) { |
| 229 | Block* b = *bi; |
| 230 | if (reachableBlocks.count(b) == 0) { |
| 231 | for (auto ii = b->getInstructions().cbegin(); ii != b->getInstructions().cend(); ii++) |
| 232 | unreachableDefinitions.insert(ii->get()->getResultId()); |
| 233 | } |
| 234 | } |
| 235 | } |
| 236 | |
| 237 | // Remove unneeded decorations, for unreachable instructions |
| 238 | decorations.erase(std::remove_if(decorations.begin(), decorations.end(), |
| 239 | [&unreachableDefinitions](std::unique_ptr<Instruction>& I) -> bool { |
| 240 | Id decoration_id = I.get()->getIdOperand(0); |
| 241 | return unreachableDefinitions.count(decoration_id) != 0; |
| 242 | }), |
| 243 | decorations.end()); |
| 244 | |
| 245 | // Add per-instruction capabilities, extensions, etc., |
| 246 | |
| 247 | // process all reachable instructions... |
| 248 | for (auto bi = reachableBlocks.cbegin(); bi != reachableBlocks.cend(); ++bi) { |
| 249 | const Block* block = *bi; |
| 250 | const auto function = [this](const std::unique_ptr<Instruction>& inst) { postProcessReachable(*inst.get()); }; |
| 251 | std::for_each(block->getInstructions().begin(), block->getInstructions().end(), function); |
| 252 | } |
| 253 | |
| 254 | // process all block-contained instructions |
| 255 | for (auto fi = module.getFunctions().cbegin(); fi != module.getFunctions().cend(); fi++) { |
| 256 | Function* f = *fi; |
| 257 | for (auto bi = f->getBlocks().cbegin(); bi != f->getBlocks().cend(); bi++) { |
| 258 | Block* b = *bi; |
| 259 | for (auto ii = b->getInstructions().cbegin(); ii != b->getInstructions().cend(); ii++) |
| 260 | postProcess(*ii->get()); |
| 261 | } |
| 262 | } |
| 263 | } |
| 264 | |
| 265 | }; // end spv namespace |