blob: 27f145d035f8914bfc2fbf5a97963cd0f69e77dc [file] [log] [blame]
John Kessenichf04c51b2018-08-03 15:56:12 -06001//
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"
50namespace 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
62namespace spv {
63
John Kessenich31aa3d62018-08-15 13:54:09 -060064// 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.
67void 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 Kessenichafe0c662018-09-10 18:10:51 -0600123 case OpExtInst:
124 switch (inst.getImmediateOperand(1)) {
125#if AMD_EXTENSIONS
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;
137#endif
138 default:
139 break;
140 }
141 break;
John Kessenich31aa3d62018-08-15 13:54:09 -0600142 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 Kessenich228e9642018-08-13 21:37:59 -0600153// Called for each instruction that resides in a block.
John Kessenich31aa3d62018-08-15 13:54:09 -0600154void Builder::postProcess(const Instruction& inst)
John Kessenichf04c51b2018-08-03 15:56:12 -0600155{
156 // Add capabilities based simply on the opcode.
157 switch (inst.getOpCode()) {
158 case OpExtInst:
John Kessenich228e9642018-08-13 21:37:59 -0600159 switch (inst.getImmediateOperand(1)) {
John Kessenichf04c51b2018-08-03 15:56:12 -0600160 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 Kessenich31aa3d62018-08-15 13:54:09 -0600196
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 Kessenichf04c51b2018-08-03 15:56:12 -0600208}
209
210// Called for each instruction in a reachable block.
John Kessenich31aa3d62018-08-15 13:54:09 -0600211void Builder::postProcessReachable(const Instruction& inst)
John Kessenichf04c51b2018-08-03 15:56:12 -0600212{
213 // did have code here, but questionable to do so without deleting the instructions
214}
215
216// comment in header
217void 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