blob: d9abd91e67afbbd7b554221a9f94fa3d72cc7efd [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;
123 default:
124 if (basicTypeOp == OpTypeFloat && width == 16)
125 addCapability(CapabilityFloat16);
126 if (basicTypeOp == OpTypeInt && width == 16)
127 addCapability(CapabilityInt16);
128 if (basicTypeOp == OpTypeInt && width == 8)
129 addCapability(CapabilityInt8);
130 break;
131 }
132}
133
John Kessenich228e9642018-08-13 21:37:59 -0600134// Called for each instruction that resides in a block.
John Kessenich31aa3d62018-08-15 13:54:09 -0600135void Builder::postProcess(const Instruction& inst)
John Kessenichf04c51b2018-08-03 15:56:12 -0600136{
137 // Add capabilities based simply on the opcode.
138 switch (inst.getOpCode()) {
139 case OpExtInst:
John Kessenich228e9642018-08-13 21:37:59 -0600140 switch (inst.getImmediateOperand(1)) {
John Kessenichf04c51b2018-08-03 15:56:12 -0600141 case GLSLstd450InterpolateAtCentroid:
142 case GLSLstd450InterpolateAtSample:
143 case GLSLstd450InterpolateAtOffset:
144 addCapability(CapabilityInterpolationFunction);
145 break;
146 default:
147 break;
148 }
149 break;
150 case OpDPdxFine:
151 case OpDPdyFine:
152 case OpFwidthFine:
153 case OpDPdxCoarse:
154 case OpDPdyCoarse:
155 case OpFwidthCoarse:
156 addCapability(CapabilityDerivativeControl);
157 break;
158
159 case OpImageQueryLod:
160 case OpImageQuerySize:
161 case OpImageQuerySizeLod:
162 case OpImageQuerySamples:
163 case OpImageQueryLevels:
164 addCapability(CapabilityImageQuery);
165 break;
166
167#ifdef NV_EXTENSIONS
168 case OpGroupNonUniformPartitionNV:
169 addExtension(E_SPV_NV_shader_subgroup_partitioned);
170 addCapability(CapabilityGroupNonUniformPartitionedNV);
171 break;
172#endif
173
174 default:
175 break;
176 }
John Kessenich31aa3d62018-08-15 13:54:09 -0600177
178 // Checks based on type
179 if (inst.getTypeId() != NoType)
180 postProcessType(inst, inst.getTypeId());
181 for (int op = 0; op < inst.getNumOperands(); ++op) {
182 if (inst.isIdOperand(op)) {
183 // In blocks, these are always result ids, but we are relying on
184 // getTypeId() to return NoType for things like OpLabel.
185 if (getTypeId(inst.getIdOperand(op)) != NoType)
186 postProcessType(inst, getTypeId(inst.getIdOperand(op)));
187 }
188 }
John Kessenichf04c51b2018-08-03 15:56:12 -0600189}
190
191// Called for each instruction in a reachable block.
John Kessenich31aa3d62018-08-15 13:54:09 -0600192void Builder::postProcessReachable(const Instruction& inst)
John Kessenichf04c51b2018-08-03 15:56:12 -0600193{
194 // did have code here, but questionable to do so without deleting the instructions
195}
196
197// comment in header
198void Builder::postProcess()
199{
200 std::unordered_set<const Block*> reachableBlocks;
201 std::unordered_set<Id> unreachableDefinitions;
202 // Collect IDs defined in unreachable blocks. For each function, label the
203 // reachable blocks first. Then for each unreachable block, collect the
204 // result IDs of the instructions in it.
205 for (auto fi = module.getFunctions().cbegin(); fi != module.getFunctions().cend(); fi++) {
206 Function* f = *fi;
207 Block* entry = f->getEntryBlock();
208 inReadableOrder(entry, [&reachableBlocks](const Block* b) { reachableBlocks.insert(b); });
209 for (auto bi = f->getBlocks().cbegin(); bi != f->getBlocks().cend(); bi++) {
210 Block* b = *bi;
211 if (reachableBlocks.count(b) == 0) {
212 for (auto ii = b->getInstructions().cbegin(); ii != b->getInstructions().cend(); ii++)
213 unreachableDefinitions.insert(ii->get()->getResultId());
214 }
215 }
216 }
217
218 // Remove unneeded decorations, for unreachable instructions
219 decorations.erase(std::remove_if(decorations.begin(), decorations.end(),
220 [&unreachableDefinitions](std::unique_ptr<Instruction>& I) -> bool {
221 Id decoration_id = I.get()->getIdOperand(0);
222 return unreachableDefinitions.count(decoration_id) != 0;
223 }),
224 decorations.end());
225
226 // Add per-instruction capabilities, extensions, etc.,
227
228 // process all reachable instructions...
229 for (auto bi = reachableBlocks.cbegin(); bi != reachableBlocks.cend(); ++bi) {
230 const Block* block = *bi;
231 const auto function = [this](const std::unique_ptr<Instruction>& inst) { postProcessReachable(*inst.get()); };
232 std::for_each(block->getInstructions().begin(), block->getInstructions().end(), function);
233 }
234
235 // process all block-contained instructions
236 for (auto fi = module.getFunctions().cbegin(); fi != module.getFunctions().cend(); fi++) {
237 Function* f = *fi;
238 for (auto bi = f->getBlocks().cbegin(); bi != f->getBlocks().cend(); bi++) {
239 Block* b = *bi;
240 for (auto ii = b->getInstructions().cbegin(); ii != b->getInstructions().cend(); ii++)
241 postProcess(*ii->get());
242 }
243 }
244}
245
246}; // end spv namespace